summaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2021-11-22 19:25:48 -0500
committerMatt A. Tobin <email@mattatobin.com>2021-11-22 19:25:48 -0500
commitd177d011148982c3d4640584ff88421384ada749 (patch)
tree6ff90e2275c85e6fdb854c07c3f9c271d5f2ebcb /system
parent96d206e2eb803f71b02ae5a8ff62730d2f053c09 (diff)
downloadaura-central-d177d011148982c3d4640584ff88421384ada749.tar.gz
Issue %3005 - Move gfx/ to system/graphics
Diffstat (limited to 'system')
-rw-r--r--system/graphics/2d/2D.h1524
-rw-r--r--system/graphics/2d/AutoHelpersWin.h86
-rw-r--r--system/graphics/2d/BaseCoord.h110
-rw-r--r--system/graphics/2d/BaseMargin.h152
-rw-r--r--system/graphics/2d/BasePoint.h121
-rw-r--r--system/graphics/2d/BasePoint3D.h122
-rw-r--r--system/graphics/2d/BasePoint4D.h131
-rw-r--r--system/graphics/2d/BaseRect.h586
-rw-r--r--system/graphics/2d/BaseSize.h100
-rw-r--r--system/graphics/2d/BezierUtils.cpp339
-rw-r--r--system/graphics/2d/BezierUtils.h185
-rw-r--r--system/graphics/2d/BigEndianInts.h79
-rw-r--r--system/graphics/2d/Blur.cpp769
-rw-r--r--system/graphics/2d/Blur.h185
-rw-r--r--system/graphics/2d/BlurLS3.cpp588
-rw-r--r--system/graphics/2d/BlurNEON.cpp288
-rw-r--r--system/graphics/2d/BlurSSE2.cpp315
-rw-r--r--system/graphics/2d/BorrowedContext.h217
-rw-r--r--system/graphics/2d/CGTextDrawing.h144
-rw-r--r--system/graphics/2d/Coord.h151
-rw-r--r--system/graphics/2d/CriticalSection.h80
-rw-r--r--system/graphics/2d/DataSourceSurface.cpp21
-rw-r--r--system/graphics/2d/DataSourceSurfaceWrapper.h39
-rw-r--r--system/graphics/2d/DataSurfaceHelpers.cpp374
-rw-r--r--system/graphics/2d/DataSurfaceHelpers.h147
-rw-r--r--system/graphics/2d/DrawCommand.h594
-rw-r--r--system/graphics/2d/DrawEventRecorder.cpp117
-rw-r--r--system/graphics/2d/DrawEventRecorder.h148
-rw-r--r--system/graphics/2d/DrawTarget.cpp56
-rw-r--r--system/graphics/2d/DrawTargetCairo.cpp2357
-rw-r--r--system/graphics/2d/DrawTargetCairo.h262
-rw-r--r--system/graphics/2d/DrawTargetCapture.cpp201
-rw-r--r--system/graphics/2d/DrawTargetCapture.h168
-rw-r--r--system/graphics/2d/DrawTargetD2D1.cpp1932
-rw-r--r--system/graphics/2d/DrawTargetD2D1.h297
-rw-r--r--system/graphics/2d/DrawTargetDual.cpp223
-rw-r--r--system/graphics/2d/DrawTargetDual.h178
-rw-r--r--system/graphics/2d/DrawTargetRecording.cpp736
-rw-r--r--system/graphics/2d/DrawTargetRecording.h336
-rw-r--r--system/graphics/2d/DrawTargetSkia.cpp2149
-rw-r--r--system/graphics/2d/DrawTargetSkia.h194
-rw-r--r--system/graphics/2d/DrawTargetTiled.cpp337
-rw-r--r--system/graphics/2d/DrawTargetTiled.h221
-rw-r--r--system/graphics/2d/DrawingJob.cpp120
-rw-r--r--system/graphics/2d/DrawingJob.h158
-rw-r--r--system/graphics/2d/ExtendInputEffectD2D1.cpp204
-rw-r--r--system/graphics/2d/ExtendInputEffectD2D1.h88
-rw-r--r--system/graphics/2d/Factory.cpp955
-rw-r--r--system/graphics/2d/FilterNodeD2D1.cpp1102
-rw-r--r--system/graphics/2d/FilterNodeD2D1.h133
-rw-r--r--system/graphics/2d/FilterNodeSoftware.cpp3689
-rw-r--r--system/graphics/2d/FilterNodeSoftware.h724
-rw-r--r--system/graphics/2d/FilterProcessing.cpp262
-rw-r--r--system/graphics/2d/FilterProcessing.h141
-rw-r--r--system/graphics/2d/FilterProcessingSIMD-inl.h1081
-rw-r--r--system/graphics/2d/FilterProcessingSSE2.cpp112
-rw-r--r--system/graphics/2d/FilterProcessingScalar.cpp244
-rw-r--r--system/graphics/2d/Filters.h512
-rw-r--r--system/graphics/2d/GenericRefCounted.h132
-rw-r--r--system/graphics/2d/GradientStopsD2D.h40
-rw-r--r--system/graphics/2d/Helpers.h97
-rw-r--r--system/graphics/2d/HelpersCairo.h352
-rw-r--r--system/graphics/2d/HelpersD2D.h967
-rw-r--r--system/graphics/2d/HelpersSkia.h396
-rw-r--r--system/graphics/2d/HelpersWinFonts.h61
-rw-r--r--system/graphics/2d/ImageScaling.cpp251
-rw-r--r--system/graphics/2d/ImageScaling.h76
-rw-r--r--system/graphics/2d/ImageScalingSSE2.cpp330
-rw-r--r--system/graphics/2d/IterableArena.h193
-rw-r--r--system/graphics/2d/JobScheduler.cpp288
-rw-r--r--system/graphics/2d/JobScheduler.h257
-rw-r--r--system/graphics/2d/JobScheduler_posix.cpp192
-rw-r--r--system/graphics/2d/JobScheduler_posix.h142
-rw-r--r--system/graphics/2d/JobScheduler_win32.cpp148
-rw-r--r--system/graphics/2d/JobScheduler_win32.h99
-rw-r--r--system/graphics/2d/Logging.h691
-rw-r--r--system/graphics/2d/LoggingConstants.h30
-rw-r--r--system/graphics/2d/MMIHelpers.h196
-rw-r--r--system/graphics/2d/Matrix.cpp134
-rw-r--r--system/graphics/2d/Matrix.h1690
-rw-r--r--system/graphics/2d/MatrixFwd.h26
-rw-r--r--system/graphics/2d/NativeFontResourceDWrite.cpp287
-rw-r--r--system/graphics/2d/NativeFontResourceDWrite.h56
-rw-r--r--system/graphics/2d/NativeFontResourceGDI.cpp62
-rw-r--r--system/graphics/2d/NativeFontResourceGDI.h54
-rw-r--r--system/graphics/2d/NumericTools.h43
-rw-r--r--system/graphics/2d/Path.cpp550
-rw-r--r--system/graphics/2d/PathAnalysis.h57
-rw-r--r--system/graphics/2d/PathCairo.cpp331
-rw-r--r--system/graphics/2d/PathCairo.h95
-rw-r--r--system/graphics/2d/PathD2D.cpp523
-rw-r--r--system/graphics/2d/PathD2D.h117
-rw-r--r--system/graphics/2d/PathHelpers.cpp277
-rw-r--r--system/graphics/2d/PathHelpers.h424
-rw-r--r--system/graphics/2d/PathRecording.cpp120
-rw-r--r--system/graphics/2d/PathRecording.h142
-rw-r--r--system/graphics/2d/PathSkia.cpp237
-rw-r--r--system/graphics/2d/PathSkia.h92
-rw-r--r--system/graphics/2d/PatternHelpers.h137
-rw-r--r--system/graphics/2d/Point.h343
-rw-r--r--system/graphics/2d/Polygon.h295
-rw-r--r--system/graphics/2d/Quaternion.cpp57
-rw-r--r--system/graphics/2d/Quaternion.h111
-rw-r--r--system/graphics/2d/RadialGradientEffectD2D1.cpp398
-rw-r--r--system/graphics/2d/RadialGradientEffectD2D1.h101
-rw-r--r--system/graphics/2d/RecordedEvent.cpp1884
-rw-r--r--system/graphics/2d/RecordedEvent.h1281
-rw-r--r--system/graphics/2d/RecordingTypes.h41
-rw-r--r--system/graphics/2d/Rect.h334
-rw-r--r--system/graphics/2d/SFNTData.cpp250
-rw-r--r--system/graphics/2d/SFNTData.h94
-rw-r--r--system/graphics/2d/SFNTNameTable.cpp260
-rw-r--r--system/graphics/2d/SFNTNameTable.h67
-rw-r--r--system/graphics/2d/SIMD.h1180
-rw-r--r--system/graphics/2d/SSEHelpers.h17
-rw-r--r--system/graphics/2d/SVGTurbulenceRenderer-inl.h360
-rw-r--r--system/graphics/2d/Scale.cpp44
-rw-r--r--system/graphics/2d/Scale.h36
-rw-r--r--system/graphics/2d/ScaleFactor.h85
-rw-r--r--system/graphics/2d/ScaleFactors2D.h135
-rw-r--r--system/graphics/2d/ScaledFontBase.cpp266
-rw-r--r--system/graphics/2d/ScaledFontBase.h72
-rw-r--r--system/graphics/2d/ScaledFontCairo.cpp38
-rw-r--r--system/graphics/2d/ScaledFontCairo.h31
-rw-r--r--system/graphics/2d/ScaledFontDWrite.cpp295
-rw-r--r--system/graphics/2d/ScaledFontDWrite.h84
-rw-r--r--system/graphics/2d/ScaledFontFontconfig.cpp47
-rw-r--r--system/graphics/2d/ScaledFontFontconfig.h37
-rw-r--r--system/graphics/2d/ScaledFontWin.cpp103
-rw-r--r--system/graphics/2d/ScaledFontWin.h49
-rw-r--r--system/graphics/2d/ShadersD2D.fx746
-rw-r--r--system/graphics/2d/ShadersD2D.h16012
-rw-r--r--system/graphics/2d/ShadersD2D1.h1419
-rw-r--r--system/graphics/2d/ShadersD2D1.hlsl117
-rw-r--r--system/graphics/2d/SourceSurfaceCairo.cpp164
-rw-r--r--system/graphics/2d/SourceSurfaceCairo.h70
-rw-r--r--system/graphics/2d/SourceSurfaceD2D1.cpp241
-rw-r--r--system/graphics/2d/SourceSurfaceD2D1.h95
-rw-r--r--system/graphics/2d/SourceSurfaceDual.h47
-rw-r--r--system/graphics/2d/SourceSurfaceRawData.cpp81
-rw-r--r--system/graphics/2d/SourceSurfaceRawData.h160
-rw-r--r--system/graphics/2d/SourceSurfaceSkia.cpp191
-rw-r--r--system/graphics/2d/SourceSurfaceSkia.h65
-rw-r--r--system/graphics/2d/StackArray.h30
-rw-r--r--system/graphics/2d/Tools.h246
-rw-r--r--system/graphics/2d/Triangle.h66
-rw-r--r--system/graphics/2d/Types.h397
-rw-r--r--system/graphics/2d/UserData.h128
-rw-r--r--system/graphics/2d/convolver.cpp562
-rw-r--r--system/graphics/2d/convolver.h201
-rw-r--r--system/graphics/2d/convolverLS3.cpp927
-rw-r--r--system/graphics/2d/convolverLS3.h75
-rw-r--r--system/graphics/2d/convolverSSE2.cpp471
-rw-r--r--system/graphics/2d/convolverSSE2.h68
-rw-r--r--system/graphics/2d/genshaders.sh10
-rw-r--r--system/graphics/2d/gfx2d.sln29
-rw-r--r--system/graphics/2d/gfx2d.vcxproj139
-rw-r--r--system/graphics/2d/image_operations.cpp385
-rw-r--r--system/graphics/2d/image_operations.h285
-rw-r--r--system/graphics/2d/moz.build206
-rw-r--r--system/graphics/2d/ssse3-scaler.c575
-rw-r--r--system/graphics/2d/ssse3-scaler.h24
-rw-r--r--system/graphics/2d/u16string.h23
-rw-r--r--system/graphics/2d/unittest/Main.cpp56
-rw-r--r--system/graphics/2d/unittest/SanityChecks.cpp19
-rw-r--r--system/graphics/2d/unittest/SanityChecks.h16
-rw-r--r--system/graphics/2d/unittest/TestBase.cpp48
-rw-r--r--system/graphics/2d/unittest/TestBase.h54
-rw-r--r--system/graphics/2d/unittest/TestBugs.cpp86
-rw-r--r--system/graphics/2d/unittest/TestBugs.h18
-rw-r--r--system/graphics/2d/unittest/TestCairo.cpp96
-rw-r--r--system/graphics/2d/unittest/TestDrawTargetBase.cpp109
-rw-r--r--system/graphics/2d/unittest/TestDrawTargetBase.h38
-rw-r--r--system/graphics/2d/unittest/TestDrawTargetD2D.cpp23
-rw-r--r--system/graphics/2d/unittest/TestDrawTargetD2D.h19
-rw-r--r--system/graphics/2d/unittest/TestPoint.cpp46
-rw-r--r--system/graphics/2d/unittest/TestPoint.h17
-rw-r--r--system/graphics/2d/unittest/TestScaling.cpp249
-rw-r--r--system/graphics/2d/unittest/TestScaling.h22
-rw-r--r--system/graphics/2d/unittest/unittest.vcxproj94
-rw-r--r--system/graphics/config/gfxConfig.cpp288
-rw-r--r--system/graphics/config/gfxConfig.h225
-rw-r--r--system/graphics/config/gfxFallback.h29
-rw-r--r--system/graphics/config/gfxFeature.cpp312
-rw-r--r--system/graphics/config/gfxFeature.h134
-rw-r--r--system/graphics/config/gfxVarReceiver.h24
-rw-r--r--system/graphics/config/gfxVars.cpp124
-rw-r--r--system/graphics/config/gfxVars.h148
-rw-r--r--system/graphics/config/moz.build25
-rw-r--r--system/graphics/doc/AsyncPanZoom-HighLevel.pngbin0 -> 67837 bytes
-rw-r--r--system/graphics/doc/AsyncPanZoom.md299
-rw-r--r--system/graphics/doc/B2GInputFlow.svg349
-rw-r--r--system/graphics/doc/GraphicsOverview.md83
-rw-r--r--system/graphics/doc/LayersHistory.md60
-rw-r--r--system/graphics/doc/MainPage.md21
-rw-r--r--system/graphics/doc/MozSurface.md124
-rw-r--r--system/graphics/doc/SharedMozSurface.md147
-rw-r--r--system/graphics/doc/Silk.md246
-rw-r--r--system/graphics/doc/silkArchitecture.pngbin0 -> 221047 bytes
-rw-r--r--system/graphics/docs/index.rst9
-rw-r--r--system/graphics/gl/DecomposeIntoNoRepeatTriangles.cpp172
-rw-r--r--system/graphics/gl/DecomposeIntoNoRepeatTriangles.h76
-rw-r--r--system/graphics/gl/EGLUtils.cpp114
-rw-r--r--system/graphics/gl/EGLUtils.h60
-rw-r--r--system/graphics/gl/GLBlitHelper.cpp803
-rw-r--r--system/graphics/gl/GLBlitHelper.h159
-rw-r--r--system/graphics/gl/GLConsts.h5951
-rw-r--r--system/graphics/gl/GLContext.cpp2982
-rw-r--r--system/graphics/gl/GLContext.h3728
-rw-r--r--system/graphics/gl/GLContextEGL.h143
-rw-r--r--system/graphics/gl/GLContextFeatures.cpp940
-rw-r--r--system/graphics/gl/GLContextGLX.h101
-rw-r--r--system/graphics/gl/GLContextProvider.h78
-rw-r--r--system/graphics/gl/GLContextProviderEGL.cpp961
-rw-r--r--system/graphics/gl/GLContextProviderGLX.cpp1435
-rw-r--r--system/graphics/gl/GLContextProviderImpl.h122
-rw-r--r--system/graphics/gl/GLContextProviderNull.cpp60
-rw-r--r--system/graphics/gl/GLContextProviderWGL.cpp652
-rw-r--r--system/graphics/gl/GLContextSymbols.h705
-rw-r--r--system/graphics/gl/GLContextTypes.cpp14
-rw-r--r--system/graphics/gl/GLContextTypes.h65
-rw-r--r--system/graphics/gl/GLContextWGL.h79
-rw-r--r--system/graphics/gl/GLDebugUtils.cpp59
-rw-r--r--system/graphics/gl/GLDebugUtils.h19
-rw-r--r--system/graphics/gl/GLDefs.h82
-rw-r--r--system/graphics/gl/GLLibraryEGL.cpp694
-rw-r--r--system/graphics/gl/GLLibraryEGL.h658
-rw-r--r--system/graphics/gl/GLLibraryLoader.cpp117
-rw-r--r--system/graphics/gl/GLLibraryLoader.h63
-rw-r--r--system/graphics/gl/GLParseRegistryXML.py216
-rw-r--r--system/graphics/gl/GLReadTexImageHelper.cpp658
-rw-r--r--system/graphics/gl/GLReadTexImageHelper.h88
-rw-r--r--system/graphics/gl/GLScreenBuffer.cpp1040
-rw-r--r--system/graphics/gl/GLScreenBuffer.h302
-rw-r--r--system/graphics/gl/GLTextureImage.cpp521
-rw-r--r--system/graphics/gl/GLTextureImage.h306
-rw-r--r--system/graphics/gl/GLTypes.h94
-rw-r--r--system/graphics/gl/GLUploadHelpers.cpp592
-rw-r--r--system/graphics/gl/GLUploadHelpers.h84
-rw-r--r--system/graphics/gl/GLXLibrary.h274
-rw-r--r--system/graphics/gl/GfxTexturesReporter.cpp73
-rw-r--r--system/graphics/gl/GfxTexturesReporter.h104
-rw-r--r--system/graphics/gl/HeapCopyOfStackArray.h46
-rw-r--r--system/graphics/gl/ScopedGLHelpers.cpp540
-rw-r--r--system/graphics/gl/ScopedGLHelpers.h368
-rw-r--r--system/graphics/gl/SharedSurface.cpp621
-rw-r--r--system/graphics/gl/SharedSurface.h333
-rw-r--r--system/graphics/gl/SharedSurfaceANGLE.cpp355
-rw-r--r--system/graphics/gl/SharedSurfaceANGLE.h101
-rw-r--r--system/graphics/gl/SharedSurfaceD3D11Interop.cpp427
-rw-r--r--system/graphics/gl/SharedSurfaceD3D11Interop.h101
-rw-r--r--system/graphics/gl/SharedSurfaceEGL.cpp191
-rw-r--r--system/graphics/gl/SharedSurfaceEGL.h118
-rw-r--r--system/graphics/gl/SharedSurfaceGL.cpp192
-rw-r--r--system/graphics/gl/SharedSurfaceGL.h167
-rw-r--r--system/graphics/gl/SharedSurfaceGLX.cpp144
-rw-r--r--system/graphics/gl/SharedSurfaceGLX.h68
-rw-r--r--system/graphics/gl/SkiaGLGlue.cpp327
-rw-r--r--system/graphics/gl/SkiaGLGlue.h64
-rw-r--r--system/graphics/gl/SurfaceTypes.cpp44
-rw-r--r--system/graphics/gl/SurfaceTypes.h98
-rw-r--r--system/graphics/gl/TextureGarbageBin.cpp44
-rw-r--r--system/graphics/gl/TextureGarbageBin.h46
-rw-r--r--system/graphics/gl/TextureImageEGL.cpp251
-rw-r--r--system/graphics/gl/TextureImageEGL.h90
-rw-r--r--system/graphics/gl/WGLLibrary.h131
-rw-r--r--system/graphics/gl/moz.build127
-rw-r--r--system/graphics/ipc/CompositorSession.cpp40
-rw-r--r--system/graphics/ipc/CompositorSession.h91
-rw-r--r--system/graphics/ipc/CompositorWidgetVsyncObserver.cpp34
-rw-r--r--system/graphics/ipc/CompositorWidgetVsyncObserver.h36
-rw-r--r--system/graphics/ipc/D3DMessageUtils.cpp80
-rw-r--r--system/graphics/ipc/D3DMessageUtils.h47
-rw-r--r--system/graphics/ipc/GPUChild.cpp165
-rw-r--r--system/graphics/ipc/GPUChild.h51
-rw-r--r--system/graphics/ipc/GPUParent.cpp358
-rw-r--r--system/graphics/ipc/GPUParent.h59
-rw-r--r--system/graphics/ipc/GPUProcessHost.cpp248
-rw-r--r--system/graphics/ipc/GPUProcessHost.h143
-rw-r--r--system/graphics/ipc/GPUProcessImpl.cpp38
-rw-r--r--system/graphics/ipc/GPUProcessImpl.h43
-rw-r--r--system/graphics/ipc/GPUProcessListener.h26
-rw-r--r--system/graphics/ipc/GPUProcessManager.cpp787
-rw-r--r--system/graphics/ipc/GPUProcessManager.h228
-rw-r--r--system/graphics/ipc/GfxMessageUtils.h1272
-rw-r--r--system/graphics/ipc/GraphicsMessages.ipdlh83
-rw-r--r--system/graphics/ipc/InProcessCompositorSession.cpp88
-rw-r--r--system/graphics/ipc/InProcessCompositorSession.h49
-rw-r--r--system/graphics/ipc/PGPU.ipdl95
-rw-r--r--system/graphics/ipc/PVsyncBridge.ipdl22
-rw-r--r--system/graphics/ipc/RemoteCompositorSession.cpp113
-rw-r--r--system/graphics/ipc/RemoteCompositorSession.h46
-rw-r--r--system/graphics/ipc/SharedDIB.cpp77
-rw-r--r--system/graphics/ipc/SharedDIB.h50
-rw-r--r--system/graphics/ipc/SharedDIBSurface.cpp63
-rw-r--r--system/graphics/ipc/SharedDIBSurface.h62
-rw-r--r--system/graphics/ipc/SharedDIBWin.cpp139
-rw-r--r--system/graphics/ipc/SharedDIBWin.h56
-rw-r--r--system/graphics/ipc/VsyncBridgeChild.cpp155
-rw-r--r--system/graphics/ipc/VsyncBridgeChild.h56
-rw-r--r--system/graphics/ipc/VsyncBridgeParent.cpp89
-rw-r--r--system/graphics/ipc/VsyncBridgeParent.h41
-rw-r--r--system/graphics/ipc/VsyncIOThreadHolder.cpp45
-rw-r--r--system/graphics/ipc/VsyncIOThreadHolder.h36
-rw-r--r--system/graphics/ipc/moz.build80
-rw-r--r--system/graphics/layers/AsyncCanvasRenderer.cpp290
-rw-r--r--system/graphics/layers/AsyncCanvasRenderer.h168
-rw-r--r--system/graphics/layers/AtomicRefCountedWithFinalize.h202
-rw-r--r--system/graphics/layers/AxisPhysicsMSDModel.cpp92
-rw-r--r--system/graphics/layers/AxisPhysicsMSDModel.h85
-rw-r--r--system/graphics/layers/AxisPhysicsModel.cpp120
-rw-r--r--system/graphics/layers/AxisPhysicsModel.h130
-rw-r--r--system/graphics/layers/BSPTree.cpp107
-rw-r--r--system/graphics/layers/BSPTree.h106
-rw-r--r--system/graphics/layers/BufferTexture.cpp594
-rw-r--r--system/graphics/layers/BufferTexture.h99
-rw-r--r--system/graphics/layers/BufferUnrotate.cpp64
-rw-r--r--system/graphics/layers/BufferUnrotate.h14
-rw-r--r--system/graphics/layers/Compositor.cpp596
-rw-r--r--system/graphics/layers/Compositor.h724
-rw-r--r--system/graphics/layers/CompositorTypes.h249
-rw-r--r--system/graphics/layers/CopyableCanvasLayer.cpp102
-rw-r--r--system/graphics/layers/CopyableCanvasLayer.h67
-rw-r--r--system/graphics/layers/D3D11ShareHandleImage.cpp163
-rw-r--r--system/graphics/layers/D3D11ShareHandleImage.h72
-rw-r--r--system/graphics/layers/D3D9SurfaceImage.cpp304
-rw-r--r--system/graphics/layers/D3D9SurfaceImage.h134
-rw-r--r--system/graphics/layers/DirectedGraph.h116
-rw-r--r--system/graphics/layers/Effects.cpp67
-rw-r--r--system/graphics/layers/Effects.h331
-rw-r--r--system/graphics/layers/FrameMetrics.cpp23
-rw-r--r--system/graphics/layers/FrameMetrics.h1112
-rw-r--r--system/graphics/layers/GLImages.cpp106
-rw-r--r--system/graphics/layers/GLImages.h65
-rw-r--r--system/graphics/layers/GPUVideoImage.h71
-rw-r--r--system/graphics/layers/IMFYCbCrImage.cpp212
-rw-r--r--system/graphics/layers/IMFYCbCrImage.h37
-rw-r--r--system/graphics/layers/IPDLActor.h62
-rw-r--r--system/graphics/layers/ImageContainer.cpp796
-rw-r--r--system/graphics/layers/ImageContainer.h892
-rw-r--r--system/graphics/layers/ImageDataSerializer.cpp296
-rw-r--r--system/graphics/layers/ImageDataSerializer.h92
-rw-r--r--system/graphics/layers/ImageLayers.cpp64
-rw-r--r--system/graphics/layers/ImageLayers.h94
-rw-r--r--system/graphics/layers/ImageTypes.h127
-rw-r--r--system/graphics/layers/LayerMetricsWrapper.h516
-rw-r--r--system/graphics/layers/LayerScope.cpp1833
-rw-r--r--system/graphics/layers/LayerScope.h68
-rw-r--r--system/graphics/layers/LayerSorter.cpp361
-rw-r--r--system/graphics/layers/LayerSorter.h21
-rw-r--r--system/graphics/layers/LayerTreeInvalidation.cpp679
-rw-r--r--system/graphics/layers/LayerTreeInvalidation.h77
-rw-r--r--system/graphics/layers/LayerUserData.h27
-rw-r--r--system/graphics/layers/Layers.cpp2498
-rw-r--r--system/graphics/layers/Layers.h2619
-rw-r--r--system/graphics/layers/LayersLogging.cpp410
-rw-r--r--system/graphics/layers/LayersLogging.h267
-rw-r--r--system/graphics/layers/LayersTypes.cpp29
-rw-r--r--system/graphics/layers/LayersTypes.h250
-rw-r--r--system/graphics/layers/MacIOSurfaceHelpers.cpp176
-rw-r--r--system/graphics/layers/MacIOSurfaceHelpers.h28
-rw-r--r--system/graphics/layers/MacIOSurfaceImage.cpp36
-rw-r--r--system/graphics/layers/MacIOSurfaceImage.h48
-rw-r--r--system/graphics/layers/PersistentBufferProvider.cpp445
-rw-r--r--system/graphics/layers/PersistentBufferProvider.h187
-rw-r--r--system/graphics/layers/ReadbackLayer.h204
-rw-r--r--system/graphics/layers/ReadbackProcessor.cpp186
-rw-r--r--system/graphics/layers/ReadbackProcessor.h80
-rw-r--r--system/graphics/layers/RenderTrace.cpp82
-rw-r--r--system/graphics/layers/RenderTrace.h75
-rw-r--r--system/graphics/layers/RotatedBuffer.cpp786
-rw-r--r--system/graphics/layers/RotatedBuffer.h430
-rw-r--r--system/graphics/layers/TextureDIB.cpp507
-rw-r--r--system/graphics/layers/TextureDIB.h132
-rw-r--r--system/graphics/layers/TextureWrapperImage.cpp58
-rw-r--r--system/graphics/layers/TextureWrapperImage.h37
-rw-r--r--system/graphics/layers/TiledLayerBuffer.h220
-rw-r--r--system/graphics/layers/TransactionIdAllocator.h66
-rw-r--r--system/graphics/layers/TreeTraversal.h280
-rw-r--r--system/graphics/layers/apz/public/CompositorController.h32
-rw-r--r--system/graphics/layers/apz/public/GeckoContentController.h178
-rw-r--r--system/graphics/layers/apz/public/IAPZCTreeManager.cpp163
-rw-r--r--system/graphics/layers/apz/public/IAPZCTreeManager.h222
-rw-r--r--system/graphics/layers/apz/public/MetricsSharingController.h39
-rw-r--r--system/graphics/layers/apz/src/APZCTreeManager.cpp2113
-rw-r--r--system/graphics/layers/apz/src/APZCTreeManager.h531
-rw-r--r--system/graphics/layers/apz/src/APZUtils.h82
-rw-r--r--system/graphics/layers/apz/src/AsyncDragMetrics.h64
-rw-r--r--system/graphics/layers/apz/src/AsyncPanZoomAnimation.h79
-rw-r--r--system/graphics/layers/apz/src/AsyncPanZoomController.cpp3986
-rw-r--r--system/graphics/layers/apz/src/AsyncPanZoomController.h1223
-rw-r--r--system/graphics/layers/apz/src/Axis.cpp680
-rw-r--r--system/graphics/layers/apz/src/Axis.h335
-rw-r--r--system/graphics/layers/apz/src/CheckerboardEvent.cpp229
-rw-r--r--system/graphics/layers/apz/src/CheckerboardEvent.h220
-rw-r--r--system/graphics/layers/apz/src/DragTracker.cpp70
-rw-r--r--system/graphics/layers/apz/src/DragTracker.h39
-rw-r--r--system/graphics/layers/apz/src/GenericFlingAnimation.h206
-rw-r--r--system/graphics/layers/apz/src/GestureEventListener.cpp552
-rw-r--r--system/graphics/layers/apz/src/GestureEventListener.h251
-rw-r--r--system/graphics/layers/apz/src/HitTestingTreeNode.cpp335
-rw-r--r--system/graphics/layers/apz/src/HitTestingTreeNode.h165
-rw-r--r--system/graphics/layers/apz/src/InputBlockState.cpp864
-rw-r--r--system/graphics/layers/apz/src/InputBlockState.h482
-rw-r--r--system/graphics/layers/apz/src/InputQueue.cpp730
-rw-r--r--system/graphics/layers/apz/src/InputQueue.h217
-rw-r--r--system/graphics/layers/apz/src/Overscroll.h136
-rw-r--r--system/graphics/layers/apz/src/OverscrollHandoffState.cpp174
-rw-r--r--system/graphics/layers/apz/src/OverscrollHandoffState.h158
-rw-r--r--system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.cpp56
-rw-r--r--system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.h58
-rw-r--r--system/graphics/layers/apz/src/QueuedInput.cpp53
-rw-r--r--system/graphics/layers/apz/src/QueuedInput.h57
-rw-r--r--system/graphics/layers/apz/src/TouchCounter.cpp50
-rw-r--r--system/graphics/layers/apz/src/TouchCounter.h33
-rw-r--r--system/graphics/layers/apz/src/WheelScrollAnimation.cpp118
-rw-r--r--system/graphics/layers/apz/src/WheelScrollAnimation.h50
-rw-r--r--system/graphics/layers/apz/test/gtest/APZCBasicTester.h119
-rw-r--r--system/graphics/layers/apz/test/gtest/APZCTreeManagerTester.h193
-rw-r--r--system/graphics/layers/apz/test/gtest/APZTestCommon.h608
-rw-r--r--system/graphics/layers/apz/test/gtest/InputUtils.h296
-rw-r--r--system/graphics/layers/apz/test/gtest/TestBasic.cpp355
-rw-r--r--system/graphics/layers/apz/test/gtest/TestEventRegions.cpp271
-rw-r--r--system/graphics/layers/apz/test/gtest/TestGestureDetector.cpp637
-rw-r--r--system/graphics/layers/apz/test/gtest/TestHitTesting.cpp577
-rw-r--r--system/graphics/layers/apz/test/gtest/TestInputQueue.cpp43
-rw-r--r--system/graphics/layers/apz/test/gtest/TestPanning.cpp127
-rw-r--r--system/graphics/layers/apz/test/gtest/TestPinching.cpp293
-rw-r--r--system/graphics/layers/apz/test/gtest/TestScrollHandoff.cpp520
-rw-r--r--system/graphics/layers/apz/test/gtest/TestSnapping.cpp63
-rw-r--r--system/graphics/layers/apz/test/gtest/TestTreeManager.cpp111
-rw-r--r--system/graphics/layers/apz/test/gtest/moz.build32
-rw-r--r--system/graphics/layers/apz/test/mochitest/apz_test_native_event_utils.js261
-rw-r--r--system/graphics/layers/apz/test/mochitest/apz_test_utils.js403
-rw-r--r--system/graphics/layers/apz/test/mochitest/chrome.ini9
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_basic_pan.html38
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1151663.html83
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1162771.html104
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1271432.html574
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1280013.html74
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1285070.html44
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug1299195.html47
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_bug982141.html149
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_click.html41
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_div_pan.html44
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_drag_click.html43
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_drag_scroll.html603
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_iframe1.html14
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_iframe2.html14
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_iframe_pan.html41
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_long_tap.html81
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html46
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html47
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html62
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_scrollto_tap.html61
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_subframe_style.css15
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_tall.html504
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_tap.html32
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_tap_fullzoom.html33
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_tap_passive.html64
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_touch_action.html115
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_touch_action_complex.html143
-rw-r--r--system/graphics/layers/apz/test/mochitest/helper_touch_action_regions.html246
-rw-r--r--system/graphics/layers/apz/test/mochitest/mochitest.ini67
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1151663.html38
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1151667.html65
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1253683.html59
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1277814.html106
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1304689-2.html131
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug1304689.html135
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_bug982141.html38
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_frame_reconstruction.html218
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_group_mouseevents.html35
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_group_pointerevents.html31
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_group_touchevents.html104
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_group_wheelevents.html41
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_group_zoom.html44
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_interrupted_reflow.html719
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_layerization.html214
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html541
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html49
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html117
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_smoothness.html77
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html114
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_wheel_scroll.html106
-rw-r--r--system/graphics/layers/apz/test/mochitest/test_wheel_transactions.html137
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-ref.html9
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html10
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html14
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-h.html13
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-ref.html9
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html10
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html14
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-v.html13
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html9
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html10
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html14
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh.html13
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html9
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1.html14
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html9
-rw-r--r--system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2.html14
-rw-r--r--system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html27
-rw-r--r--system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html53
-rw-r--r--system/graphics/layers/apz/test/reftest/initial-scale-1-ref.html10
-rw-r--r--system/graphics/layers/apz/test/reftest/initial-scale-1.html10
-rw-r--r--system/graphics/layers/apz/test/reftest/reftest-stylo.list20
-rw-r--r--system/graphics/layers/apz/test/reftest/reftest.list19
-rw-r--r--system/graphics/layers/apz/testutil/APZTestData.cpp66
-rw-r--r--system/graphics/layers/apz/testutil/APZTestData.h168
-rw-r--r--system/graphics/layers/apz/util/APZCCallbackHelper.cpp931
-rw-r--r--system/graphics/layers/apz/util/APZCCallbackHelper.h209
-rw-r--r--system/graphics/layers/apz/util/APZEventState.cpp509
-rw-r--r--system/graphics/layers/apz/util/APZEventState.h103
-rw-r--r--system/graphics/layers/apz/util/APZThreadUtils.cpp79
-rw-r--r--system/graphics/layers/apz/util/APZThreadUtils.h104
-rw-r--r--system/graphics/layers/apz/util/ActiveElementManager.cpp237
-rw-r--r--system/graphics/layers/apz/util/ActiveElementManager.h104
-rw-r--r--system/graphics/layers/apz/util/CheckerboardReportService.cpp228
-rw-r--r--system/graphics/layers/apz/util/CheckerboardReportService.h144
-rw-r--r--system/graphics/layers/apz/util/ChromeProcessController.cpp276
-rw-r--r--system/graphics/layers/apz/util/ChromeProcessController.h83
-rw-r--r--system/graphics/layers/apz/util/ContentProcessController.cpp206
-rw-r--r--system/graphics/layers/apz/util/ContentProcessController.h90
-rw-r--r--system/graphics/layers/apz/util/DoubleTapToZoom.cpp178
-rw-r--r--system/graphics/layers/apz/util/DoubleTapToZoom.h29
-rw-r--r--system/graphics/layers/apz/util/InputAPZContext.cpp69
-rw-r--r--system/graphics/layers/apz/util/InputAPZContext.h50
-rw-r--r--system/graphics/layers/apz/util/ScrollInputMethods.h61
-rw-r--r--system/graphics/layers/apz/util/ScrollLinkedEffectDetector.cpp49
-rw-r--r--system/graphics/layers/apz/util/ScrollLinkedEffectDetector.h43
-rw-r--r--system/graphics/layers/apz/util/TouchActionHelper.cpp96
-rw-r--r--system/graphics/layers/apz/util/TouchActionHelper.h42
-rw-r--r--system/graphics/layers/basic/AutoMaskData.h60
-rw-r--r--system/graphics/layers/basic/BasicCanvasLayer.cpp141
-rw-r--r--system/graphics/layers/basic/BasicCanvasLayer.h51
-rw-r--r--system/graphics/layers/basic/BasicColorLayer.cpp83
-rw-r--r--system/graphics/layers/basic/BasicCompositor.cpp996
-rw-r--r--system/graphics/layers/basic/BasicCompositor.h162
-rw-r--r--system/graphics/layers/basic/BasicContainerLayer.cpp172
-rw-r--r--system/graphics/layers/basic/BasicContainerLayer.h106
-rw-r--r--system/graphics/layers/basic/BasicImageLayer.cpp119
-rw-r--r--system/graphics/layers/basic/BasicImages.cpp185
-rw-r--r--system/graphics/layers/basic/BasicImplData.h132
-rw-r--r--system/graphics/layers/basic/BasicLayerManager.cpp991
-rw-r--r--system/graphics/layers/basic/BasicLayers.h217
-rw-r--r--system/graphics/layers/basic/BasicLayersImpl.cpp211
-rw-r--r--system/graphics/layers/basic/BasicLayersImpl.h151
-rw-r--r--system/graphics/layers/basic/BasicPaintedLayer.cpp243
-rw-r--r--system/graphics/layers/basic/BasicPaintedLayer.h133
-rw-r--r--system/graphics/layers/basic/TextureClientX11.cpp156
-rw-r--r--system/graphics/layers/basic/TextureClientX11.h57
-rw-r--r--system/graphics/layers/basic/TextureHostBasic.cpp23
-rw-r--r--system/graphics/layers/basic/TextureHostBasic.h34
-rw-r--r--system/graphics/layers/basic/X11BasicCompositor.cpp136
-rw-r--r--system/graphics/layers/basic/X11BasicCompositor.h65
-rw-r--r--system/graphics/layers/basic/X11TextureSourceBasic.cpp67
-rw-r--r--system/graphics/layers/basic/X11TextureSourceBasic.h54
-rw-r--r--system/graphics/layers/client/CanvasClient.cpp531
-rw-r--r--system/graphics/layers/client/CanvasClient.h216
-rw-r--r--system/graphics/layers/client/ClientCanvasLayer.cpp264
-rw-r--r--system/graphics/layers/client/ClientCanvasLayer.h116
-rw-r--r--system/graphics/layers/client/ClientColorLayer.cpp79
-rw-r--r--system/graphics/layers/client/ClientContainerLayer.cpp36
-rw-r--r--system/graphics/layers/client/ClientContainerLayer.h189
-rw-r--r--system/graphics/layers/client/ClientImageLayer.cpp170
-rw-r--r--system/graphics/layers/client/ClientLayerManager.cpp901
-rw-r--r--system/graphics/layers/client/ClientLayerManager.h422
-rw-r--r--system/graphics/layers/client/ClientPaintedLayer.cpp173
-rw-r--r--system/graphics/layers/client/ClientPaintedLayer.h127
-rw-r--r--system/graphics/layers/client/ClientReadbackLayer.h34
-rw-r--r--system/graphics/layers/client/ClientTiledPaintedLayer.cpp608
-rw-r--r--system/graphics/layers/client/ClientTiledPaintedLayer.h153
-rw-r--r--system/graphics/layers/client/CompositableChild.cpp119
-rw-r--r--system/graphics/layers/client/CompositableChild.h91
-rw-r--r--system/graphics/layers/client/CompositableClient.cpp273
-rw-r--r--system/graphics/layers/client/CompositableClient.h209
-rw-r--r--system/graphics/layers/client/ContentClient.cpp681
-rw-r--r--system/graphics/layers/client/ContentClient.h412
-rw-r--r--system/graphics/layers/client/GPUVideoTextureClient.cpp72
-rw-r--r--system/graphics/layers/client/GPUVideoTextureClient.h56
-rw-r--r--system/graphics/layers/client/ImageClient.cpp293
-rw-r--r--system/graphics/layers/client/ImageClient.h138
-rw-r--r--system/graphics/layers/client/SingleTiledContentClient.cpp263
-rw-r--r--system/graphics/layers/client/SingleTiledContentClient.h135
-rw-r--r--system/graphics/layers/client/TextureClient.cpp1686
-rw-r--r--system/graphics/layers/client/TextureClient.h826
-rw-r--r--system/graphics/layers/client/TextureClientPool.cpp339
-rw-r--r--system/graphics/layers/client/TextureClientPool.h180
-rw-r--r--system/graphics/layers/client/TextureClientRecycleAllocator.cpp279
-rw-r--r--system/graphics/layers/client/TextureClientRecycleAllocator.h140
-rw-r--r--system/graphics/layers/client/TextureClientSharedSurface.cpp96
-rw-r--r--system/graphics/layers/client/TextureClientSharedSurface.h77
-rw-r--r--system/graphics/layers/client/TiledContentClient.cpp1405
-rw-r--r--system/graphics/layers/client/TiledContentClient.h545
-rw-r--r--system/graphics/layers/composite/AsyncCompositionManager.cpp1391
-rw-r--r--system/graphics/layers/composite/AsyncCompositionManager.h275
-rw-r--r--system/graphics/layers/composite/CanvasLayerComposite.cpp159
-rw-r--r--system/graphics/layers/composite/CanvasLayerComposite.h81
-rw-r--r--system/graphics/layers/composite/ColorLayerComposite.cpp47
-rw-r--r--system/graphics/layers/composite/ColorLayerComposite.h65
-rw-r--r--system/graphics/layers/composite/CompositableHost.cpp292
-rw-r--r--system/graphics/layers/composite/CompositableHost.h316
-rw-r--r--system/graphics/layers/composite/ContainerLayerComposite.cpp676
-rw-r--r--system/graphics/layers/composite/ContainerLayerComposite.h191
-rw-r--r--system/graphics/layers/composite/ContentHost.cpp487
-rw-r--r--system/graphics/layers/composite/ContentHost.h228
-rw-r--r--system/graphics/layers/composite/FPSCounter.cpp465
-rw-r--r--system/graphics/layers/composite/FPSCounter.h114
-rw-r--r--system/graphics/layers/composite/FontData.h13
-rw-r--r--system/graphics/layers/composite/FrameUniformityData.cpp152
-rw-r--r--system/graphics/layers/composite/FrameUniformityData.h73
-rw-r--r--system/graphics/layers/composite/GPUVideoTextureHost.cpp90
-rw-r--r--system/graphics/layers/composite/GPUVideoTextureHost.h53
-rw-r--r--system/graphics/layers/composite/ImageHost.cpp739
-rw-r--r--system/graphics/layers/composite/ImageHost.h201
-rw-r--r--system/graphics/layers/composite/ImageLayerComposite.cpp241
-rw-r--r--system/graphics/layers/composite/ImageLayerComposite.h79
-rw-r--r--system/graphics/layers/composite/LayerManagerComposite.cpp1210
-rw-r--r--system/graphics/layers/composite/LayerManagerComposite.h684
-rw-r--r--system/graphics/layers/composite/PaintCounter.cpp79
-rw-r--r--system/graphics/layers/composite/PaintCounter.h49
-rw-r--r--system/graphics/layers/composite/PaintedLayerComposite.cpp199
-rw-r--r--system/graphics/layers/composite/PaintedLayerComposite.h94
-rw-r--r--system/graphics/layers/composite/TextRenderer.cpp173
-rw-r--r--system/graphics/layers/composite/TextRenderer.h50
-rw-r--r--system/graphics/layers/composite/TextureHost.cpp1176
-rw-r--r--system/graphics/layers/composite/TextureHost.h898
-rw-r--r--system/graphics/layers/composite/TiledContentHost.cpp643
-rw-r--r--system/graphics/layers/composite/TiledContentHost.h292
-rw-r--r--system/graphics/layers/composite/X11TextureHost.cpp112
-rw-r--r--system/graphics/layers/composite/X11TextureHost.h72
-rw-r--r--system/graphics/layers/composite/qrcode_table.h259
-rw-r--r--system/graphics/layers/d3d11/BlendShaderConstants.h59
-rw-r--r--system/graphics/layers/d3d11/BlendingHelpers.hlslh184
-rw-r--r--system/graphics/layers/d3d11/CompositorD3D11.cpp1577
-rw-r--r--system/graphics/layers/d3d11/CompositorD3D11.h208
-rw-r--r--system/graphics/layers/d3d11/CompositorD3D11.hlsl421
-rw-r--r--system/graphics/layers/d3d11/CompositorD3D11Shaders.h9434
-rw-r--r--system/graphics/layers/d3d11/ReadbackManagerD3D11.cpp160
-rw-r--r--system/graphics/layers/d3d11/ReadbackManagerD3D11.h65
-rw-r--r--system/graphics/layers/d3d11/TextureD3D11.cpp1286
-rw-r--r--system/graphics/layers/d3d11/TextureD3D11.h455
-rw-r--r--system/graphics/layers/d3d11/genshaders.sh50
-rw-r--r--system/graphics/layers/ipc/APZCTreeManagerChild.cpp265
-rw-r--r--system/graphics/layers/ipc/APZCTreeManagerChild.h110
-rw-r--r--system/graphics/layers/ipc/APZCTreeManagerParent.cpp312
-rw-r--r--system/graphics/layers/ipc/APZCTreeManagerParent.h150
-rw-r--r--system/graphics/layers/ipc/APZChild.cpp97
-rw-r--r--system/graphics/layers/ipc/APZChild.h54
-rw-r--r--system/graphics/layers/ipc/CompositableForwarder.cpp27
-rw-r--r--system/graphics/layers/ipc/CompositableForwarder.h126
-rw-r--r--system/graphics/layers/ipc/CompositableTransactionParent.cpp245
-rw-r--r--system/graphics/layers/ipc/CompositableTransactionParent.h55
-rw-r--r--system/graphics/layers/ipc/CompositorBench.cpp345
-rw-r--r--system/graphics/layers/ipc/CompositorBench.h30
-rw-r--r--system/graphics/layers/ipc/CompositorBridgeChild.cpp1149
-rw-r--r--system/graphics/layers/ipc/CompositorBridgeChild.h332
-rw-r--r--system/graphics/layers/ipc/CompositorBridgeParent.cpp2378
-rw-r--r--system/graphics/layers/ipc/CompositorBridgeParent.h687
-rw-r--r--system/graphics/layers/ipc/CompositorThread.cpp148
-rw-r--r--system/graphics/layers/ipc/CompositorThread.h67
-rw-r--r--system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.cpp571
-rw-r--r--system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.h177
-rw-r--r--system/graphics/layers/ipc/GonkNativeHandle.cpp80
-rw-r--r--system/graphics/layers/ipc/GonkNativeHandle.h24
-rw-r--r--system/graphics/layers/ipc/GonkNativeHandleUtils.cpp93
-rw-r--r--system/graphics/layers/ipc/GonkNativeHandleUtils.h26
-rw-r--r--system/graphics/layers/ipc/ISurfaceAllocator.cpp235
-rw-r--r--system/graphics/layers/ipc/ISurfaceAllocator.h318
-rw-r--r--system/graphics/layers/ipc/ImageBridgeChild.cpp1225
-rw-r--r--system/graphics/layers/ipc/ImageBridgeChild.h400
-rw-r--r--system/graphics/layers/ipc/ImageBridgeParent.cpp448
-rw-r--r--system/graphics/layers/ipc/ImageBridgeParent.h155
-rw-r--r--system/graphics/layers/ipc/ImageContainerChild.cpp70
-rw-r--r--system/graphics/layers/ipc/ImageContainerChild.h61
-rw-r--r--system/graphics/layers/ipc/ImageContainerParent.cpp31
-rw-r--r--system/graphics/layers/ipc/ImageContainerParent.h37
-rw-r--r--system/graphics/layers/ipc/KnowsCompositor.h89
-rw-r--r--system/graphics/layers/ipc/LayerAnimationUtils.cpp44
-rw-r--r--system/graphics/layers/ipc/LayerAnimationUtils.h29
-rw-r--r--system/graphics/layers/ipc/LayerTransactionChild.cpp78
-rw-r--r--system/graphics/layers/ipc/LayerTransactionChild.h90
-rw-r--r--system/graphics/layers/ipc/LayerTransactionParent.cpp1098
-rw-r--r--system/graphics/layers/ipc/LayerTransactionParent.h240
-rw-r--r--system/graphics/layers/ipc/LayerTreeOwnerTracker.cpp82
-rw-r--r--system/graphics/layers/ipc/LayerTreeOwnerTracker.h70
-rw-r--r--system/graphics/layers/ipc/LayersMessages.ipdlh499
-rw-r--r--system/graphics/layers/ipc/LayersSurfaces.ipdlh136
-rw-r--r--system/graphics/layers/ipc/PAPZ.ipdl71
-rw-r--r--system/graphics/layers/ipc/PAPZCTreeManager.ipdl139
-rw-r--r--system/graphics/layers/ipc/PCompositable.ipdl28
-rw-r--r--system/graphics/layers/ipc/PCompositorBridge.ipdl237
-rw-r--r--system/graphics/layers/ipc/PImageBridge.ipdl70
-rw-r--r--system/graphics/layers/ipc/PImageContainer.ipdl33
-rw-r--r--system/graphics/layers/ipc/PLayer.ipdl39
-rw-r--r--system/graphics/layers/ipc/PLayerTransaction.ipdl133
-rw-r--r--system/graphics/layers/ipc/PTexture.ipdl45
-rw-r--r--system/graphics/layers/ipc/PVideoBridge.ipdl31
-rw-r--r--system/graphics/layers/ipc/RemoteContentController.cpp264
-rw-r--r--system/graphics/layers/ipc/RemoteContentController.h96
-rw-r--r--system/graphics/layers/ipc/ShadowLayerChild.cpp45
-rw-r--r--system/graphics/layers/ipc/ShadowLayerChild.h39
-rw-r--r--system/graphics/layers/ipc/ShadowLayerParent.cpp138
-rw-r--r--system/graphics/layers/ipc/ShadowLayerParent.h58
-rw-r--r--system/graphics/layers/ipc/ShadowLayerUtils.h46
-rw-r--r--system/graphics/layers/ipc/ShadowLayerUtilsX11.cpp153
-rw-r--r--system/graphics/layers/ipc/ShadowLayerUtilsX11.h85
-rw-r--r--system/graphics/layers/ipc/ShadowLayers.cpp1044
-rw-r--r--system/graphics/layers/ipc/ShadowLayers.h465
-rw-r--r--system/graphics/layers/ipc/SharedPlanarYCbCrImage.cpp246
-rw-r--r--system/graphics/layers/ipc/SharedPlanarYCbCrImage.h60
-rw-r--r--system/graphics/layers/ipc/SharedRGBImage.cpp113
-rw-r--r--system/graphics/layers/ipc/SharedRGBImage.h59
-rw-r--r--system/graphics/layers/ipc/SynchronousTask.h65
-rw-r--r--system/graphics/layers/ipc/TextureForwarder.h78
-rw-r--r--system/graphics/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h91
-rw-r--r--system/graphics/layers/ipc/VideoBridgeChild.cpp122
-rw-r--r--system/graphics/layers/ipc/VideoBridgeChild.h75
-rw-r--r--system/graphics/layers/ipc/VideoBridgeParent.cpp123
-rw-r--r--system/graphics/layers/ipc/VideoBridgeParent.h73
-rw-r--r--system/graphics/layers/layerviewer/hide.pngbin0 -> 3079 bytes
-rw-r--r--system/graphics/layers/layerviewer/index.html47
-rw-r--r--system/graphics/layers/layerviewer/layerTreeView.js885
-rw-r--r--system/graphics/layers/layerviewer/noise.pngbin0 -> 2118 bytes
-rw-r--r--system/graphics/layers/layerviewer/show.pngbin0 -> 3187 bytes
-rw-r--r--system/graphics/layers/layerviewer/tree.css36
-rw-r--r--system/graphics/layers/moz.build400
-rw-r--r--system/graphics/layers/opengl/Composer2D.h73
-rw-r--r--system/graphics/layers/opengl/CompositingRenderTargetOGL.cpp120
-rw-r--r--system/graphics/layers/opengl/CompositingRenderTargetOGL.h195
-rw-r--r--system/graphics/layers/opengl/CompositorOGL.cpp1890
-rw-r--r--system/graphics/layers/opengl/CompositorOGL.h502
-rw-r--r--system/graphics/layers/opengl/EGLImageHelpers.cpp40
-rw-r--r--system/graphics/layers/opengl/EGLImageHelpers.h26
-rw-r--r--system/graphics/layers/opengl/GLBlitTextureImageHelper.cpp279
-rw-r--r--system/graphics/layers/opengl/GLBlitTextureImageHelper.h70
-rw-r--r--system/graphics/layers/opengl/OGLShaderProgram.cpp974
-rw-r--r--system/graphics/layers/opengl/OGLShaderProgram.h621
-rw-r--r--system/graphics/layers/opengl/TextureClientOGL.cpp77
-rw-r--r--system/graphics/layers/opengl/TextureClientOGL.h53
-rw-r--r--system/graphics/layers/opengl/TextureHostOGL.cpp561
-rw-r--r--system/graphics/layers/opengl/TextureHostOGL.h438
-rw-r--r--system/graphics/layers/opengl/TexturePoolOGL.cpp123
-rw-r--r--system/graphics/layers/opengl/TexturePoolOGL.h40
-rw-r--r--system/graphics/layers/opengl/X11TextureSourceOGL.cpp114
-rw-r--r--system/graphics/layers/opengl/X11TextureSourceOGL.h65
-rw-r--r--system/graphics/layers/protobuf/LayerScopePacket.pb.cc6802
-rw-r--r--system/graphics/layers/protobuf/LayerScopePacket.pb.h5779
-rw-r--r--system/graphics/layers/protobuf/LayerScopePacket.proto218
-rw-r--r--system/graphics/moz.build21
-rw-r--r--system/graphics/src/AppUnits.h15
-rw-r--r--system/graphics/src/ArrayView.h50
-rw-r--r--system/graphics/src/DriverCrashGuard.cpp565
-rw-r--r--system/graphics/src/DriverCrashGuard.h170
-rw-r--r--system/graphics/src/FilterSupport.cpp2185
-rw-r--r--system/graphics/src/FilterSupport.h479
-rw-r--r--system/graphics/src/PingPongRegion.h63
-rw-r--r--system/graphics/src/RegionBuilder.h32
-rw-r--r--system/graphics/src/TiledRegion.cpp369
-rw-r--r--system/graphics/src/TiledRegion.h207
-rw-r--r--system/graphics/src/X11UndefineNone.h22
-rw-r--r--system/graphics/src/X11Util.cpp94
-rw-r--r--system/graphics/src/X11Util.h148
-rw-r--r--system/graphics/src/gfxTelemetry.cpp60
-rw-r--r--system/graphics/src/gfxTelemetry.h64
-rw-r--r--system/graphics/src/moz.build87
-rw-r--r--system/graphics/src/nsBoundingMetrics.h87
-rw-r--r--system/graphics/src/nsColor.cpp361
-rw-r--r--system/graphics/src/nsColor.h123
-rw-r--r--system/graphics/src/nsColorNameList.h181
-rw-r--r--system/graphics/src/nsColorNames.h16
-rw-r--r--system/graphics/src/nsCoord.h443
-rw-r--r--system/graphics/src/nsDeviceContext.cpp681
-rw-r--r--system/graphics/src/nsDeviceContext.h308
-rw-r--r--system/graphics/src/nsFont.cpp297
-rw-r--r--system/graphics/src/nsFont.h147
-rw-r--r--system/graphics/src/nsFontMetrics.cpp445
-rw-r--r--system/graphics/src/nsFontMetrics.h268
-rw-r--r--system/graphics/src/nsGfxCIID.h21
-rw-r--r--system/graphics/src/nsIFontEnumerator.idl61
-rw-r--r--system/graphics/src/nsIScriptableRegion.idl176
-rw-r--r--system/graphics/src/nsITheme.h206
-rw-r--r--system/graphics/src/nsMargin.h26
-rw-r--r--system/graphics/src/nsPoint.h106
-rw-r--r--system/graphics/src/nsRect.cpp62
-rw-r--r--system/graphics/src/nsRect.h324
-rw-r--r--system/graphics/src/nsRegion.cpp1147
-rw-r--r--system/graphics/src/nsRegion.h867
-rw-r--r--system/graphics/src/nsRegionFwd.h26
-rw-r--r--system/graphics/src/nsRenderingContext.h41
-rw-r--r--system/graphics/src/nsScriptableRegion.cpp159
-rw-r--r--system/graphics/src/nsScriptableRegion.h28
-rw-r--r--system/graphics/src/nsSize.h70
-rw-r--r--system/graphics/src/nsThebesFontEnumerator.cpp129
-rw-r--r--system/graphics/src/nsThebesFontEnumerator.h24
-rw-r--r--system/graphics/src/nsThebesGfxFactory.cpp63
-rw-r--r--system/graphics/src/nsThemeConstants.h296
-rw-r--r--system/graphics/src/nsTransform2D.cpp23
-rw-r--r--system/graphics/src/nsTransform2D.h86
-rw-r--r--system/graphics/tests/browser/browser.ini4
-rw-r--r--system/graphics/tests/browser/browser_windowless_troubleshoot_crash.js45
-rw-r--r--system/graphics/tests/crashtests/1034403-1.html8
-rw-r--r--system/graphics/tests/crashtests/1134549-1.svg14
-rw-r--r--system/graphics/tests/crashtests/1205900.html20
-rw-r--r--system/graphics/tests/crashtests/1216832-1.html13
-rw-r--r--system/graphics/tests/crashtests/1225125-1.html11
-rw-r--r--system/graphics/tests/crashtests/122875-1.html1
-rw-r--r--system/graphics/tests/crashtests/1308394.html23
-rw-r--r--system/graphics/tests/crashtests/1317403-1.html18
-rw-r--r--system/graphics/tests/crashtests/1325159-1.html35
-rw-r--r--system/graphics/tests/crashtests/156882-1.html205
-rw-r--r--system/graphics/tests/crashtests/157320-1.html11
-rw-r--r--system/graphics/tests/crashtests/199379-1.html10
-rw-r--r--system/graphics/tests/crashtests/206561-1.html8
-rw-r--r--system/graphics/tests/crashtests/248518-1.html7
-rw-r--r--system/graphics/tests/crashtests/306649-1.xml1
-rw-r--r--system/graphics/tests/crashtests/306902-1.xml14
-rw-r--r--system/graphics/tests/crashtests/333861-1.html18
-rw-r--r--system/graphics/tests/crashtests/334735-1.html11
-rw-r--r--system/graphics/tests/crashtests/345576-1.html6
-rw-r--r--system/graphics/tests/crashtests/345629-1.html7
-rw-r--r--system/graphics/tests/crashtests/348462-1.html11
-rw-r--r--system/graphics/tests/crashtests/348462-2.html13
-rw-r--r--system/graphics/tests/crashtests/366643.html7
-rw-r--r--system/graphics/tests/crashtests/369688-1.html19
-rw-r--r--system/graphics/tests/crashtests/369947-1.html11
-rw-r--r--system/graphics/tests/crashtests/372094-1.xhtml45
-rw-r--r--system/graphics/tests/crashtests/376627-1.html3
-rw-r--r--system/graphics/tests/crashtests/377231-1.html1
-rw-r--r--system/graphics/tests/crashtests/377232-1.xhtml5
-rw-r--r--system/graphics/tests/crashtests/377461-1.xhtml16
-rw-r--r--system/graphics/tests/crashtests/383473-1.html8
-rw-r--r--system/graphics/tests/crashtests/383872-1.svg19
-rw-r--r--system/graphics/tests/crashtests/385228-1.svg22
-rw-r--r--system/graphics/tests/crashtests/385228-2.svg20
-rw-r--r--system/graphics/tests/crashtests/385289-1.xhtml30
-rw-r--r--system/graphics/tests/crashtests/385417-1.html1
-rw-r--r--system/graphics/tests/crashtests/385417-2.html10
-rw-r--r--system/graphics/tests/crashtests/385423-1.html17
-rw-r--r--system/graphics/tests/crashtests/385423-2.html17
-rw-r--r--system/graphics/tests/crashtests/385719-1.html1
-rw-r--r--system/graphics/tests/crashtests/389326-1-inner.xhtml29
-rw-r--r--system/graphics/tests/crashtests/389326-1.html9
-rw-r--r--system/graphics/tests/crashtests/390476.html13
-rw-r--r--system/graphics/tests/crashtests/393746-1.xhtml14
-rw-r--r--system/graphics/tests/crashtests/393749-1.html18
-rw-r--r--system/graphics/tests/crashtests/393822-1.html32
-rw-r--r--system/graphics/tests/crashtests/394246-1.html16
-rw-r--r--system/graphics/tests/crashtests/394246-2.html23
-rw-r--r--system/graphics/tests/crashtests/394384-1.html26
-rw-r--r--system/graphics/tests/crashtests/394751.xhtml3
-rw-r--r--system/graphics/tests/crashtests/395335-1.xhtml20
-rw-r--r--system/graphics/tests/crashtests/395458-1.html5
-rw-r--r--system/graphics/tests/crashtests/396321-1.svg5
-rw-r--r--system/graphics/tests/crashtests/398042-1.xhtml13
-rw-r--r--system/graphics/tests/crashtests/398042-2.xhtml13
-rw-r--r--system/graphics/tests/crashtests/402307-1.html10
-rw-r--r--system/graphics/tests/crashtests/403464-1.html134
-rw-r--r--system/graphics/tests/crashtests/404112-1.html10
-rw-r--r--system/graphics/tests/crashtests/404112-2.html8
-rw-r--r--system/graphics/tests/crashtests/405268-1.xhtml20
-rw-r--r--system/graphics/tests/crashtests/407761-1.html8
-rw-r--r--system/graphics/tests/crashtests/407842.html18
-rw-r--r--system/graphics/tests/crashtests/408754-1.html13
-rw-r--r--system/graphics/tests/crashtests/410728-1.xml14
-rw-r--r--system/graphics/tests/crashtests/416637-1.html5
-rw-r--r--system/graphics/tests/crashtests/419095-1.html20
-rw-r--r--system/graphics/tests/crashtests/419255-1.html4
-rw-r--r--system/graphics/tests/crashtests/420945-1.html4
-rw-r--r--system/graphics/tests/crashtests/420962-1.html4
-rw-r--r--system/graphics/tests/crashtests/421393-1.html14
-rw-r--r--system/graphics/tests/crashtests/421813-1.html4
-rw-r--r--system/graphics/tests/crashtests/423110-1.xhtml1
-rw-r--r--system/graphics/tests/crashtests/423270-1.html5
-rw-r--r--system/graphics/tests/crashtests/428633.html5
-rw-r--r--system/graphics/tests/crashtests/429899-1.html1
-rw-r--r--system/graphics/tests/crashtests/441360.html39
-rw-r--r--system/graphics/tests/crashtests/441360_data.gifbin0 -> 3016 bytes
-rw-r--r--system/graphics/tests/crashtests/445711.html11
-rw-r--r--system/graphics/tests/crashtests/463307-1.html5
-rw-r--r--system/graphics/tests/crashtests/467703-1.xhtml1
-rw-r--r--system/graphics/tests/crashtests/467873-1.html8
-rw-r--r--system/graphics/tests/crashtests/470418-1.html5
-rw-r--r--system/graphics/tests/crashtests/474410-1.html16
-rw-r--r--system/graphics/tests/crashtests/483120-1.xhtml23
-rw-r--r--system/graphics/tests/crashtests/483120-2.xhtml22
-rw-r--r--system/graphics/tests/crashtests/487549-1.html23
-rw-r--r--system/graphics/tests/crashtests/487549-bad_kern_table.ttfbin0 -> 18404 bytes
-rw-r--r--system/graphics/tests/crashtests/487724-1.html23
-rw-r--r--system/graphics/tests/crashtests/490777-1.html9
-rw-r--r--system/graphics/tests/crashtests/516512-1.html5
-rw-r--r--system/graphics/tests/crashtests/532726-1.html5
-rw-r--r--system/graphics/tests/crashtests/538065-1.html14
-rw-r--r--system/graphics/tests/crashtests/546870-1.html8
-rw-r--r--system/graphics/tests/crashtests/557348-1.html1
-rw-r--r--system/graphics/tests/crashtests/563740-1.html2
-rw-r--r--system/graphics/tests/crashtests/580100-1.html7
-rw-r--r--system/graphics/tests/crashtests/580100-bad_hhea_table.ttfbin0 -> 36109 bytes
-rw-r--r--system/graphics/tests/crashtests/580212-1.html7
-rw-r--r--system/graphics/tests/crashtests/580212-bad_loca_table.ttfbin0 -> 36109 bytes
-rw-r--r--system/graphics/tests/crashtests/580233-1.html7
-rw-r--r--system/graphics/tests/crashtests/580233-bad_gpos_table.ttfbin0 -> 173500 bytes
-rw-r--r--system/graphics/tests/crashtests/580719-1.html18
-rw-r--r--system/graphics/tests/crashtests/580719-bad_head_table.ttfbin0 -> 173520 bytes
-rw-r--r--system/graphics/tests/crashtests/593526.html1
-rw-r--r--system/graphics/tests/crashtests/593526.xul5
-rw-r--r--system/graphics/tests/crashtests/594654-1.xhtml5
-rw-r--r--system/graphics/tests/crashtests/595042-1.html1
-rw-r--r--system/graphics/tests/crashtests/595727-1.html23
-rw-r--r--system/graphics/tests/crashtests/624198.xhtml1
-rw-r--r--system/graphics/tests/crashtests/633322-1.html1
-rw-r--r--system/graphics/tests/crashtests/633453-1.html10
-rw-r--r--system/graphics/tests/crashtests/662467-1.html2
-rw-r--r--system/graphics/tests/crashtests/665218.html8
-rw-r--r--system/graphics/tests/crashtests/675550-1.html24
-rw-r--r--system/graphics/tests/crashtests/686190-1.html18
-rw-r--r--system/graphics/tests/crashtests/691581-1.html6
-rw-r--r--system/graphics/tests/crashtests/693143-1.html44
-rw-r--r--system/graphics/tests/crashtests/696936-1.html2
-rw-r--r--system/graphics/tests/crashtests/699563-1.html2
-rw-r--r--system/graphics/tests/crashtests/710149-1.html19
-rw-r--r--system/graphics/tests/crashtests/766452-1.html6
-rw-r--r--system/graphics/tests/crashtests/766452-2.html6
-rw-r--r--system/graphics/tests/crashtests/768079-1.html4
-rw-r--r--system/graphics/tests/crashtests/783041-1.html63
-rw-r--r--system/graphics/tests/crashtests/783041-2.html63
-rw-r--r--system/graphics/tests/crashtests/783041-3.html71
-rw-r--r--system/graphics/tests/crashtests/783041-4.html82
-rw-r--r--system/graphics/tests/crashtests/798853.html3
-rw-r--r--system/graphics/tests/crashtests/805760-1.html22
-rw-r--r--system/graphics/tests/crashtests/805760.ttfbin0 -> 61656 bytes
-rw-r--r--system/graphics/tests/crashtests/815489.html17
-rw-r--r--system/graphics/tests/crashtests/836225-1.html19
-rw-r--r--system/graphics/tests/crashtests/839745-1.html20
-rw-r--r--system/graphics/tests/crashtests/856784-1.html11
-rw-r--r--system/graphics/tests/crashtests/893572-1.html11
-rw-r--r--system/graphics/tests/crashtests/893572-2.html30
-rw-r--r--system/graphics/tests/crashtests/893572-3.html44
-rw-r--r--system/graphics/tests/crashtests/893572-4.html38
-rw-r--r--system/graphics/tests/crashtests/914457-1.html9
-rw-r--r--system/graphics/tests/crashtests/944579.html1
-rw-r--r--system/graphics/tests/crashtests/944579.pngbin0 -> 3452 bytes
-rw-r--r--system/graphics/tests/crashtests/944579.svg26
-rw-r--r--system/graphics/tests/crashtests/950000.html40
-rw-r--r--system/graphics/tests/crashtests/PigLatin_Plane15.ttfbin0 -> 57236 bytes
-rw-r--r--system/graphics/tests/crashtests/Prototype.ttfbin0 -> 29592 bytes
-rw-r--r--system/graphics/tests/crashtests/balinese-letter-spacing.html2
-rw-r--r--system/graphics/tests/crashtests/crashtests.list135
-rw-r--r--system/graphics/tests/gtest/PolygonTestUtils.cpp171
-rw-r--r--system/graphics/tests/gtest/PolygonTestUtils.h39
-rw-r--r--system/graphics/tests/gtest/TestArena.cpp188
-rw-r--r--system/graphics/tests/gtest/TestArrayView.cpp18
-rw-r--r--system/graphics/tests/gtest/TestBSPTree.cpp959
-rw-r--r--system/graphics/tests/gtest/TestBufferRotation.cpp151
-rw-r--r--system/graphics/tests/gtest/TestColorNames.cpp100
-rw-r--r--system/graphics/tests/gtest/TestCompositor.cpp284
-rw-r--r--system/graphics/tests/gtest/TestGfxPrefs.cpp82
-rw-r--r--system/graphics/tests/gtest/TestGfxWidgets.cpp86
-rw-r--r--system/graphics/tests/gtest/TestJobScheduler.cpp245
-rw-r--r--system/graphics/tests/gtest/TestLayers.cpp488
-rw-r--r--system/graphics/tests/gtest/TestLayers.h52
-rw-r--r--system/graphics/tests/gtest/TestMatrix.cpp60
-rw-r--r--system/graphics/tests/gtest/TestMoz2D.cpp37
-rw-r--r--system/graphics/tests/gtest/TestPolygon.cpp143
-rw-r--r--system/graphics/tests/gtest/TestQcms.cpp168
-rw-r--r--system/graphics/tests/gtest/TestRect.cpp450
-rw-r--r--system/graphics/tests/gtest/TestRegion.cpp837
-rw-r--r--system/graphics/tests/gtest/TestSkipChars.cpp165
-rw-r--r--system/graphics/tests/gtest/TestTextures.cpp300
-rw-r--r--system/graphics/tests/gtest/TestTiledLayerBuffer.cpp64
-rw-r--r--system/graphics/tests/gtest/TestTreeTraversal.cpp2225
-rw-r--r--system/graphics/tests/gtest/TestVsync.cpp205
-rw-r--r--system/graphics/tests/gtest/gfxFontSelectionTest.cpp320
-rw-r--r--system/graphics/tests/gtest/gfxFontSelectionTests.h210
-rw-r--r--system/graphics/tests/gtest/gfxSurfaceRefCountTest.cpp151
-rw-r--r--system/graphics/tests/gtest/gfxTextRunPerfTest.cpp126
-rw-r--r--system/graphics/tests/gtest/moz.build59
-rw-r--r--system/graphics/tests/gtest/per-word-runs.h58528
-rw-r--r--system/graphics/tests/mochitest/mochitest.ini11
-rw-r--r--system/graphics/tests/mochitest/test_acceleration.html111
-rw-r--r--system/graphics/tests/mochitest/test_bug509244.html47
-rw-r--r--system/graphics/tests/mochitest/test_bug513439.html37
-rw-r--r--system/graphics/tests/mochitest/test_font_whitelist.html85
-rw-r--r--system/graphics/tests/mochitest/test_overdraw.html23
-rw-r--r--system/graphics/tests/moz.build8
-rw-r--r--system/graphics/tests/reftest/1086723-ref.html27
-rw-r--r--system/graphics/tests/reftest/1086723.html27
-rw-r--r--system/graphics/tests/reftest/1131264-1.svg17
-rw-r--r--system/graphics/tests/reftest/1143303-1.svg26
-rw-r--r--system/graphics/tests/reftest/1149923-ref.html28
-rw-r--r--system/graphics/tests/reftest/1149923.html29
-rw-r--r--system/graphics/tests/reftest/468496-1-ref.html32
-rw-r--r--system/graphics/tests/reftest/468496-1.html51
-rw-r--r--system/graphics/tests/reftest/611498-1.html19
-rw-r--r--system/graphics/tests/reftest/611498-ref.html6
-rw-r--r--system/graphics/tests/reftest/709477-1-ref.html47
-rw-r--r--system/graphics/tests/reftest/709477-1.html69
-rw-r--r--system/graphics/tests/reftest/853889-1-ref.html12
-rw-r--r--system/graphics/tests/reftest/853889-1.html19
-rw-r--r--system/graphics/tests/reftest/blacktrans.pngbin0 -> 105 bytes
-rw-r--r--system/graphics/tests/reftest/bwinton.jpgbin0 -> 1110 bytes
-rw-r--r--system/graphics/tests/reftest/pass.svg8
-rw-r--r--system/graphics/tests/reftest/reftest-stylo.list12
-rw-r--r--system/graphics/tests/reftest/reftest.list9
-rw-r--r--system/graphics/tests/unit/test_nsIScriptableRegion.js10
-rw-r--r--system/graphics/tests/unit/xpcshell.ini5
-rw-r--r--system/graphics/thebes/CJKCompatSVS.cpp1039
-rw-r--r--system/graphics/thebes/ContextStateTracker.cpp125
-rw-r--r--system/graphics/thebes/ContextStateTracker.h84
-rw-r--r--system/graphics/thebes/D3D11Checks.cpp412
-rw-r--r--system/graphics/thebes/D3D11Checks.h30
-rw-r--r--system/graphics/thebes/DeviceManagerDx.cpp867
-rw-r--r--system/graphics/thebes/DeviceManagerDx.h153
-rw-r--r--system/graphics/thebes/DrawMode.h26
-rw-r--r--system/graphics/thebes/PrintTarget.cpp159
-rw-r--r--system/graphics/thebes/PrintTarget.h160
-rw-r--r--system/graphics/thebes/PrintTargetPDF.cpp85
-rw-r--r--system/graphics/thebes/PrintTargetPDF.h41
-rw-r--r--system/graphics/thebes/PrintTargetPS.cpp110
-rw-r--r--system/graphics/thebes/PrintTargetPS.h54
-rw-r--r--system/graphics/thebes/PrintTargetRecording.cpp116
-rw-r--r--system/graphics/thebes/PrintTargetRecording.h43
-rw-r--r--system/graphics/thebes/PrintTargetThebes.cpp126
-rw-r--r--system/graphics/thebes/PrintTargetThebes.h57
-rw-r--r--system/graphics/thebes/PrintTargetWindows.cpp116
-rw-r--r--system/graphics/thebes/PrintTargetWindows.h43
-rw-r--r--system/graphics/thebes/RoundedRect.h43
-rw-r--r--system/graphics/thebes/SoftwareVsyncSource.cpp150
-rw-r--r--system/graphics/thebes/SoftwareVsyncSource.h62
-rw-r--r--system/graphics/thebes/VsyncSource.cpp153
-rw-r--r--system/graphics/thebes/VsyncSource.h84
-rw-r--r--system/graphics/thebes/cairo-xlib-utils.h119
-rw-r--r--system/graphics/thebes/d3dkmtQueryStatistics.h168
-rw-r--r--system/graphics/thebes/genLanguageTagList.pl86
-rw-r--r--system/graphics/thebes/genTables.py22
-rw-r--r--system/graphics/thebes/gencjkcisvs.py77
-rw-r--r--system/graphics/thebes/gfx2DGlue.h130
-rw-r--r--system/graphics/thebes/gfxASurface.cpp611
-rw-r--r--system/graphics/thebes/gfxASurface.h204
-rw-r--r--system/graphics/thebes/gfxAlphaRecovery.cpp53
-rw-r--r--system/graphics/thebes/gfxAlphaRecovery.h111
-rw-r--r--system/graphics/thebes/gfxAlphaRecoverySSE2.cpp235
-rw-r--r--system/graphics/thebes/gfxBaseSharedMemorySurface.cpp11
-rw-r--r--system/graphics/thebes/gfxBaseSharedMemorySurface.h199
-rw-r--r--system/graphics/thebes/gfxBlur.cpp1088
-rw-r--r--system/graphics/thebes/gfxBlur.h194
-rw-r--r--system/graphics/thebes/gfxColor.h83
-rw-r--r--system/graphics/thebes/gfxContext.cpp1256
-rw-r--r--system/graphics/thebes/gfxContext.h694
-rw-r--r--system/graphics/thebes/gfxDWriteCommon.cpp182
-rw-r--r--system/graphics/thebes/gfxDWriteCommon.h153
-rw-r--r--system/graphics/thebes/gfxDWriteFontList.cpp1813
-rw-r--r--system/graphics/thebes/gfxDWriteFontList.h441
-rw-r--r--system/graphics/thebes/gfxDWriteFonts.cpp709
-rw-r--r--system/graphics/thebes/gfxDWriteFonts.h107
-rw-r--r--system/graphics/thebes/gfxDrawable.cpp254
-rw-r--r--system/graphics/thebes/gfxDrawable.h182
-rw-r--r--system/graphics/thebes/gfxEnv.h121
-rw-r--r--system/graphics/thebes/gfxFT2FontBase.cpp217
-rw-r--r--system/graphics/thebes/gfxFT2FontBase.h45
-rw-r--r--system/graphics/thebes/gfxFT2FontList.cpp1598
-rw-r--r--system/graphics/thebes/gfxFT2FontList.h201
-rw-r--r--system/graphics/thebes/gfxFT2Fonts.cpp227
-rw-r--r--system/graphics/thebes/gfxFT2Fonts.h84
-rw-r--r--system/graphics/thebes/gfxFT2Utils.cpp394
-rw-r--r--system/graphics/thebes/gfxFT2Utils.h94
-rw-r--r--system/graphics/thebes/gfxFailure.h25
-rw-r--r--system/graphics/thebes/gfxFcPlatformFontList.cpp1877
-rw-r--r--system/graphics/thebes/gfxFcPlatformFontList.h330
-rw-r--r--system/graphics/thebes/gfxFont-Impl.h83
-rw-r--r--system/graphics/thebes/gfxFont.cpp3958
-rw-r--r--system/graphics/thebes/gfxFont.h2223
-rw-r--r--system/graphics/thebes/gfxFontConstants.h238
-rw-r--r--system/graphics/thebes/gfxFontEntry.cpp1831
-rw-r--r--system/graphics/thebes/gfxFontEntry.h777
-rw-r--r--system/graphics/thebes/gfxFontFamilyList.h365
-rw-r--r--system/graphics/thebes/gfxFontFeatures.cpp81
-rw-r--r--system/graphics/thebes/gfxFontFeatures.h126
-rw-r--r--system/graphics/thebes/gfxFontInfoLoader.cpp285
-rw-r--r--system/graphics/thebes/gfxFontInfoLoader.h258
-rw-r--r--system/graphics/thebes/gfxFontMissingGlyphs.cpp280
-rw-r--r--system/graphics/thebes/gfxFontMissingGlyphs.h55
-rw-r--r--system/graphics/thebes/gfxFontPrefLangList.h36
-rw-r--r--system/graphics/thebes/gfxFontTest.cpp8
-rw-r--r--system/graphics/thebes/gfxFontTest.h84
-rw-r--r--system/graphics/thebes/gfxFontUtils.cpp1789
-rw-r--r--system/graphics/thebes/gfxFontUtils.h1027
-rw-r--r--system/graphics/thebes/gfxFontconfigFonts.cpp2255
-rw-r--r--system/graphics/thebes/gfxFontconfigFonts.h124
-rw-r--r--system/graphics/thebes/gfxFontconfigUtils.cpp1088
-rw-r--r--system/graphics/thebes/gfxFontconfigUtils.h328
-rw-r--r--system/graphics/thebes/gfxGDIFont.cpp563
-rw-r--r--system/graphics/thebes/gfxGDIFont.h108
-rw-r--r--system/graphics/thebes/gfxGDIFontList.cpp1168
-rw-r--r--system/graphics/thebes/gfxGDIFontList.h351
-rw-r--r--system/graphics/thebes/gfxGdkNativeRenderer.cpp69
-rw-r--r--system/graphics/thebes/gfxGdkNativeRenderer.h88
-rw-r--r--system/graphics/thebes/gfxGlyphExtents.cpp154
-rw-r--r--system/graphics/thebes/gfxGlyphExtents.h151
-rw-r--r--system/graphics/thebes/gfxGradientCache.cpp237
-rw-r--r--system/graphics/thebes/gfxGradientCache.h36
-rw-r--r--system/graphics/thebes/gfxGraphiteShaper.cpp438
-rw-r--r--system/graphics/thebes/gfxGraphiteShaper.h59
-rw-r--r--system/graphics/thebes/gfxHarfBuzzShaper.cpp1786
-rw-r--r--system/graphics/thebes/gfxHarfBuzzShaper.h192
-rw-r--r--system/graphics/thebes/gfxImageSurface.cpp379
-rw-r--r--system/graphics/thebes/gfxImageSurface.h188
-rw-r--r--system/graphics/thebes/gfxLanguageTagList.cpp7876
-rw-r--r--system/graphics/thebes/gfxLineSegment.h77
-rw-r--r--system/graphics/thebes/gfxMathTable.cpp211
-rw-r--r--system/graphics/thebes/gfxMathTable.h155
-rw-r--r--system/graphics/thebes/gfxMatrix.cpp191
-rw-r--r--system/graphics/thebes/gfxMatrix.h322
-rw-r--r--system/graphics/thebes/gfxPattern.cpp219
-rw-r--r--system/graphics/thebes/gfxPattern.h77
-rw-r--r--system/graphics/thebes/gfxPlatform.cpp2477
-rw-r--r--system/graphics/thebes/gfxPlatform.h844
-rw-r--r--system/graphics/thebes/gfxPlatformFontList.cpp1699
-rw-r--r--system/graphics/thebes/gfxPlatformFontList.h482
-rw-r--r--system/graphics/thebes/gfxPlatformGtk.cpp899
-rw-r--r--system/graphics/thebes/gfxPlatformGtk.h163
-rw-r--r--system/graphics/thebes/gfxPoint.h70
-rw-r--r--system/graphics/thebes/gfxPrefs.cpp267
-rw-r--r--system/graphics/thebes/gfxPrefs.h671
-rw-r--r--system/graphics/thebes/gfxQuad.h51
-rw-r--r--system/graphics/thebes/gfxQuaternion.h93
-rw-r--r--system/graphics/thebes/gfxRect.cpp96
-rw-r--r--system/graphics/thebes/gfxRect.h148
-rw-r--r--system/graphics/thebes/gfxSVGGlyphs.cpp502
-rw-r--r--system/graphics/thebes/gfxSVGGlyphs.h242
-rw-r--r--system/graphics/thebes/gfxScriptItemizer.cpp231
-rw-r--r--system/graphics/thebes/gfxScriptItemizer.h102
-rw-r--r--system/graphics/thebes/gfxSharedImageSurface.h24
-rw-r--r--system/graphics/thebes/gfxSkipChars.cpp154
-rw-r--r--system/graphics/thebes/gfxSkipChars.h308
-rw-r--r--system/graphics/thebes/gfxTextRun.cpp3248
-rw-r--r--system/graphics/thebes/gfxTextRun.h1236
-rw-r--r--system/graphics/thebes/gfxTypes.h82
-rw-r--r--system/graphics/thebes/gfxUserFontSet.cpp1415
-rw-r--r--system/graphics/thebes/gfxUserFontSet.h716
-rw-r--r--system/graphics/thebes/gfxUtils.cpp1485
-rw-r--r--system/graphics/thebes/gfxUtils.h325
-rw-r--r--system/graphics/thebes/gfxWindowsNativeDrawing.cpp320
-rw-r--r--system/graphics/thebes/gfxWindowsNativeDrawing.h116
-rw-r--r--system/graphics/thebes/gfxWindowsPlatform.cpp1983
-rw-r--r--system/graphics/thebes/gfxWindowsPlatform.h266
-rw-r--r--system/graphics/thebes/gfxWindowsSurface.cpp161
-rw-r--r--system/graphics/thebes/gfxWindowsSurface.h58
-rw-r--r--system/graphics/thebes/gfxXlibNativeRenderer.cpp620
-rw-r--r--system/graphics/thebes/gfxXlibNativeRenderer.h105
-rw-r--r--system/graphics/thebes/gfxXlibSurface.cpp614
-rw-r--r--system/graphics/thebes/gfxXlibSurface.h122
-rw-r--r--system/graphics/thebes/moz.build219
-rw-r--r--system/graphics/thebes/nsUnicodeRange.cpp419
-rw-r--r--system/graphics/thebes/nsUnicodeRange.h91
-rw-r--r--system/moz.build3
-rw-r--r--system/toolkit.mozbuild1
1158 files changed, 400448 insertions, 2 deletions
diff --git a/system/graphics/2d/2D.h b/system/graphics/2d/2D.h
new file mode 100644
index 000000000..89de5630c
--- /dev/null
+++ b/system/graphics/2d/2D.h
@@ -0,0 +1,1524 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_2D_H
+#define _MOZILLA_GFX_2D_H
+
+#include "Types.h"
+#include "Point.h"
+#include "Rect.h"
+#include "Matrix.h"
+#include "Quaternion.h"
+#include "UserData.h"
+
+// GenericRefCountedBase allows us to hold on to refcounted objects of any type
+// (contrary to RefCounted<T> which requires knowing the type T) and, in particular,
+// without having a dependency on that type. This is used for DrawTargetSkia
+// to be able to hold on to a GLContext.
+#include "mozilla/GenericRefCounted.h"
+
+// This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
+// outparams using the &-operator. But it will have to do as there's no easy
+// solution.
+#include "mozilla/RefPtr.h"
+
+#include "mozilla/DebugOnly.h"
+
+#ifdef MOZ_ENABLE_FREETYPE
+#include <string>
+#endif
+
+#include "gfxPrefs.h"
+
+struct _cairo_surface;
+typedef _cairo_surface cairo_surface_t;
+
+struct _cairo_scaled_font;
+typedef _cairo_scaled_font cairo_scaled_font_t;
+
+struct _FcPattern;
+typedef _FcPattern FcPattern;
+
+struct ID3D11Texture2D;
+struct ID3D11Device;
+struct ID2D1Device;
+struct IDWriteFactory;
+struct IDWriteRenderingParams;
+struct IDWriteFontFace;
+
+class GrContext;
+class SkCanvas;
+struct gfxFontStyle;
+
+struct CGContext;
+typedef struct CGContext *CGContextRef;
+
+namespace mozilla {
+
+namespace gfx {
+
+class SourceSurface;
+class DataSourceSurface;
+class DrawTarget;
+class DrawEventRecorder;
+class FilterNode;
+class LogForwarder;
+
+struct NativeSurface {
+ NativeSurfaceType mType;
+ SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ void *mSurface;
+};
+
+struct NativeFont {
+ NativeFontType mType;
+ void *mFont;
+};
+
+/**
+ * This structure is used to send draw options that are universal to all drawing
+ * operations.
+ */
+struct DrawOptions {
+ /// For constructor parameter description, see member data documentation.
+ explicit DrawOptions(Float aAlpha = 1.0f,
+ CompositionOp aCompositionOp = CompositionOp::OP_OVER,
+ AntialiasMode aAntialiasMode = AntialiasMode::DEFAULT)
+ : mAlpha(aAlpha)
+ , mCompositionOp(aCompositionOp)
+ , mAntialiasMode(aAntialiasMode)
+ {}
+
+ Float mAlpha; /**< Alpha value by which the mask generated by this
+ operation is multiplied. */
+ CompositionOp mCompositionOp; /**< The operator that indicates how the source and
+ destination patterns are blended. */
+ AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing
+ operation. */
+};
+
+/**
+ * This structure is used to send stroke options that are used in stroking
+ * operations.
+ */
+struct StrokeOptions {
+ /// For constructor parameter description, see member data documentation.
+ explicit StrokeOptions(Float aLineWidth = 1.0f,
+ JoinStyle aLineJoin = JoinStyle::MITER_OR_BEVEL,
+ CapStyle aLineCap = CapStyle::BUTT,
+ Float aMiterLimit = 10.0f,
+ size_t aDashLength = 0,
+ const Float* aDashPattern = 0,
+ Float aDashOffset = 0.f)
+ : mLineWidth(aLineWidth)
+ , mMiterLimit(aMiterLimit)
+ , mDashPattern(aDashLength > 0 ? aDashPattern : 0)
+ , mDashLength(aDashLength)
+ , mDashOffset(aDashOffset)
+ , mLineJoin(aLineJoin)
+ , mLineCap(aLineCap)
+ {
+ MOZ_ASSERT(aDashLength == 0 || aDashPattern);
+ }
+
+ Float mLineWidth; //!< Width of the stroke in userspace.
+ Float mMiterLimit; //!< Miter limit in units of linewidth
+ const Float* mDashPattern; /**< Series of on/off userspace lengths defining dash.
+ Owned by the caller; must live at least as long as
+ this StrokeOptions.
+ mDashPattern != null <=> mDashLength > 0. */
+ size_t mDashLength; //!< Number of on/off lengths in mDashPattern.
+ Float mDashOffset; /**< Userspace offset within mDashPattern at which
+ stroking begins. */
+ JoinStyle mLineJoin; //!< Join style used for joining lines.
+ CapStyle mLineCap; //!< Cap style used for capping lines.
+};
+
+/**
+ * This structure supplies additional options for calls to DrawSurface.
+ */
+struct DrawSurfaceOptions {
+ /// For constructor parameter description, see member data documentation.
+ explicit DrawSurfaceOptions(SamplingFilter aSamplingFilter = SamplingFilter::LINEAR,
+ SamplingBounds aSamplingBounds = SamplingBounds::UNBOUNDED)
+ : mSamplingFilter(aSamplingFilter)
+ , mSamplingBounds(aSamplingBounds)
+ { }
+
+ SamplingFilter mSamplingFilter; /**< SamplingFilter used when resampling source surface
+ region to the destination region. */
+ SamplingBounds mSamplingBounds; /**< This indicates whether the implementation is
+ allowed to sample pixels outside the source
+ rectangle as specified in DrawSurface on
+ the surface. */
+
+};
+
+/**
+ * This class is used to store gradient stops, it can only be used with a
+ * matching DrawTarget. Not adhering to this condition will make a draw call
+ * fail.
+ */
+class GradientStops : public RefCounted<GradientStops>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops)
+ virtual ~GradientStops() {}
+
+ virtual BackendType GetBackendType() const = 0;
+ virtual bool IsValid() const { return true; }
+
+protected:
+ GradientStops() {}
+};
+
+/**
+ * This is the base class for 'patterns'. Patterns describe the pixels used as
+ * the source for a masked composition operation that is done by the different
+ * drawing commands. These objects are not backend specific, however for
+ * example the gradient stops on a gradient pattern can be backend specific.
+ */
+class Pattern
+{
+public:
+ virtual ~Pattern() {}
+
+ virtual PatternType GetType() const = 0;
+
+protected:
+ Pattern() {}
+};
+
+class ColorPattern : public Pattern
+{
+public:
+ // Explicit because consumers should generally use ToDeviceColor when
+ // creating a ColorPattern.
+ explicit ColorPattern(const Color &aColor)
+ : mColor(aColor)
+ {}
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::COLOR;
+ }
+
+ Color mColor;
+};
+
+/**
+ * This class is used for Linear Gradient Patterns, the gradient stops are
+ * stored in a separate object and are backend dependent. This class itself
+ * may be used on the stack.
+ */
+class LinearGradientPattern : public Pattern
+{
+public:
+ /// For constructor parameter description, see member data documentation.
+ LinearGradientPattern(const Point &aBegin,
+ const Point &aEnd,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix())
+ : mBegin(aBegin)
+ , mEnd(aEnd)
+ , mStops(aStops)
+ , mMatrix(aMatrix)
+ {
+ }
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::LINEAR_GRADIENT;
+ }
+
+ Point mBegin; //!< Start of the linear gradient
+ Point mEnd; /**< End of the linear gradient - NOTE: In the case
+ of a zero length gradient it will act as the
+ color of the last stop. */
+ RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this
+ should match the backend type of the draw
+ target this pattern will be used with. */
+ Matrix mMatrix; /**< A matrix that transforms the pattern into
+ user space */
+};
+
+/**
+ * This class is used for Radial Gradient Patterns, the gradient stops are
+ * stored in a separate object and are backend dependent. This class itself
+ * may be used on the stack.
+ */
+class RadialGradientPattern : public Pattern
+{
+public:
+ /// For constructor parameter description, see member data documentation.
+ RadialGradientPattern(const Point &aCenter1,
+ const Point &aCenter2,
+ Float aRadius1,
+ Float aRadius2,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix())
+ : mCenter1(aCenter1)
+ , mCenter2(aCenter2)
+ , mRadius1(aRadius1)
+ , mRadius2(aRadius2)
+ , mStops(aStops)
+ , mMatrix(aMatrix)
+ {
+ }
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::RADIAL_GRADIENT;
+ }
+
+ Point mCenter1; //!< Center of the inner (focal) circle.
+ Point mCenter2; //!< Center of the outer circle.
+ Float mRadius1; //!< Radius of the inner (focal) circle.
+ Float mRadius2; //!< Radius of the outer circle.
+ RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this
+ should match the backend type of the draw target
+ this pattern will be used with. */
+ Matrix mMatrix; //!< A matrix that transforms the pattern into user space
+};
+
+/**
+ * This class is used for Surface Patterns, they wrap a surface and a
+ * repetition mode for the surface. This may be used on the stack.
+ */
+class SurfacePattern : public Pattern
+{
+public:
+ /// For constructor parameter description, see member data documentation.
+ SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode,
+ const Matrix &aMatrix = Matrix(),
+ SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
+ const IntRect &aSamplingRect = IntRect())
+ : mSurface(aSourceSurface)
+ , mExtendMode(aExtendMode)
+ , mSamplingFilter(aSamplingFilter)
+ , mMatrix(aMatrix)
+ , mSamplingRect(aSamplingRect)
+ {}
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::SURFACE;
+ }
+
+ RefPtr<SourceSurface> mSurface; //!< Surface to use for drawing
+ ExtendMode mExtendMode; /**< This determines how the image is extended
+ outside the bounds of the image */
+ SamplingFilter mSamplingFilter; //!< Resampling filter for resampling the image.
+ Matrix mMatrix; //!< Transforms the pattern into user space
+
+ IntRect mSamplingRect; /**< Rect that must not be sampled outside of,
+ or an empty rect if none has been specified. */
+};
+
+class StoredPattern;
+class DrawTargetCaptureImpl;
+
+/**
+ * This is the base class for source surfaces. These objects are surfaces
+ * which may be used as a source in a SurfacePattern or a DrawSurface call.
+ * They cannot be drawn to directly.
+ *
+ * Although SourceSurface has thread-safe refcount, some SourceSurface cannot
+ * be used on random threads at the same time. Only DataSourceSurface can be
+ * used on random threads now. This will be fixed in the future. Eventually
+ * all SourceSurface should be thread-safe.
+ */
+class SourceSurface : public external::AtomicRefCounted<SourceSurface>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface)
+ virtual ~SourceSurface() {}
+
+ virtual SurfaceType GetType() const = 0;
+ virtual IntSize GetSize() const = 0;
+ virtual SurfaceFormat GetFormat() const = 0;
+
+ /** This returns false if some event has made this source surface invalid for
+ * usage with current DrawTargets. For example in the case of Direct2D this
+ * could return false if we have switched devices since this surface was
+ * created.
+ */
+ virtual bool IsValid() const { return true; }
+
+ /**
+ * This function will get a DataSourceSurface for this surface, a
+ * DataSourceSurface's data can be accessed directly.
+ */
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() = 0;
+
+ /** Tries to get this SourceSurface's native surface. This will fail if aType
+ * is not the type of this SourceSurface's native surface.
+ */
+ virtual void *GetNativeSurface(NativeSurfaceType aType) {
+ return nullptr;
+ }
+
+ void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+ mUserData.Add(key, userData, destroy);
+ }
+ void *GetUserData(UserDataKey *key) {
+ return mUserData.Get(key);
+ }
+
+protected:
+ friend class DrawTargetCaptureImpl;
+ friend class StoredPattern;
+
+ // This is for internal use, it ensures the SourceSurface's data remains
+ // valid during the lifetime of the SourceSurface.
+ // @todo XXX - We need something better here :(. But we may be able to get rid
+ // of CreateWrappingDataSourceSurface in the future.
+ virtual void GuaranteePersistance() {}
+
+ UserData mUserData;
+};
+
+class DataSourceSurface : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurface, override)
+ DataSourceSurface()
+ : mIsMapped(false)
+ {
+ }
+
+#ifdef DEBUG
+ virtual ~DataSourceSurface()
+ {
+ MOZ_ASSERT(!mIsMapped, "Someone forgot to call Unmap()");
+ }
+#endif
+
+ struct MappedSurface {
+ uint8_t *mData;
+ int32_t mStride;
+ };
+
+ enum MapType {
+ READ,
+ WRITE,
+ READ_WRITE
+ };
+
+ /**
+ * This is a scoped version of Map(). Map() is called in the constructor and
+ * Unmap() in the destructor. Use this for automatic unmapping of your data
+ * surfaces.
+ *
+ * Use IsMapped() to verify whether Map() succeeded or not.
+ */
+ class ScopedMap {
+ public:
+ explicit ScopedMap(DataSourceSurface* aSurface, MapType aType)
+ : mSurface(aSurface)
+ , mIsMapped(aSurface->Map(aType, &mMap)) {}
+
+ virtual ~ScopedMap()
+ {
+ if (mIsMapped) {
+ mSurface->Unmap();
+ }
+ }
+
+ uint8_t* GetData() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return mMap.mData;
+ }
+
+ int32_t GetStride() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return mMap.mStride;
+ }
+
+ const MappedSurface* GetMappedSurface() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return &mMap;
+ }
+
+ bool IsMapped() const { return mIsMapped; }
+
+ private:
+ RefPtr<DataSourceSurface> mSurface;
+ MappedSurface mMap;
+ bool mIsMapped;
+ };
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ /** @deprecated
+ * Get the raw bitmap data of the surface.
+ * Can return null if there was OOM allocating surface data.
+ */
+ virtual uint8_t *GetData() = 0;
+
+ /** @deprecated
+ * Stride of the surface, distance in bytes between the start of the image
+ * data belonging to row y and row y+1. This may be negative.
+ * Can return 0 if there was OOM allocating surface data.
+ */
+ virtual int32_t Stride() = 0;
+
+ /**
+ * The caller is responsible for ensuring aMappedSurface is not null.
+ */
+ virtual bool Map(MapType, MappedSurface *aMappedSurface)
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ mIsMapped = !!aMappedSurface->mData;
+ return mIsMapped;
+ }
+
+ virtual void Unmap()
+ {
+ MOZ_ASSERT(mIsMapped);
+ mIsMapped = false;
+ }
+
+ /**
+ * Returns a DataSourceSurface with the same data as this one, but
+ * guaranteed to have surface->GetType() == SurfaceType::DATA.
+ *
+ * The returning surface might be null, because of OOM or gfx device reset.
+ * The caller needs to do null-check before using it.
+ */
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() override;
+
+protected:
+ bool mIsMapped;
+};
+
+/** This is an abstract object that accepts path segments. */
+class PathSink : public RefCounted<PathSink>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSink)
+ virtual ~PathSink() {}
+
+ /** Move the current point in the path, any figure currently being drawn will
+ * be considered closed during fill operations, however when stroking the
+ * closing line segment will not be drawn.
+ */
+ virtual void MoveTo(const Point &aPoint) = 0;
+ /** Add a linesegment to the current figure */
+ virtual void LineTo(const Point &aPoint) = 0;
+ /** Add a cubic bezier curve to the current figure */
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3) = 0;
+ /** Add a quadratic bezier curve to the current figure */
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2) = 0;
+ /** Close the current figure, this will essentially generate a line segment
+ * from the current point to the starting point for the current figure
+ */
+ virtual void Close() = 0;
+ /** Add an arc to the current figure */
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false) = 0;
+ /** Point the current subpath is at - or where the next subpath will start
+ * if there is no active subpath.
+ */
+ virtual Point CurrentPoint() const = 0;
+};
+
+class PathBuilder;
+class FlattenedPath;
+
+/** The path class is used to create (sets of) figures of any shape that can be
+ * filled or stroked to a DrawTarget
+ */
+class Path : public RefCounted<Path>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path)
+ virtual ~Path();
+
+ virtual BackendType GetBackendType() const = 0;
+
+ /** This returns a PathBuilder object that contains a copy of the contents of
+ * this path and is still writable.
+ */
+ inline already_AddRefed<PathBuilder> CopyToBuilder() const {
+ return CopyToBuilder(GetFillRule());
+ }
+ inline already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform) const {
+ return TransformedCopyToBuilder(aTransform, GetFillRule());
+ }
+ /** This returns a PathBuilder object that contains a copy of the contents of
+ * this path, converted to use the specified FillRule, and still writable.
+ */
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const = 0;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const = 0;
+
+ /** This function checks if a point lies within a path. It allows passing a
+ * transform that will transform the path to the coordinate space in which
+ * aPoint is given.
+ */
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const = 0;
+
+
+ /** This function checks if a point lies within the stroke of a path using the
+ * specified strokeoptions. It allows passing a transform that will transform
+ * the path to the coordinate space in which aPoint is given.
+ */
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const = 0;
+
+ /** This functions gets the bounds of this path. These bounds are not
+ * guaranteed to be tight. A transform may be specified that gives the bounds
+ * after application of the transform.
+ */
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const = 0;
+
+ /** This function gets the bounds of the stroke of this path using the
+ * specified strokeoptions. These bounds are not guaranteed to be tight.
+ * A transform may be specified that gives the bounds after application of
+ * the transform.
+ */
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const = 0;
+
+ /** Take the contents of this path and stream it to another sink, this works
+ * regardless of the backend that might be used for the destination sink.
+ */
+ virtual void StreamToSink(PathSink *aSink) const = 0;
+
+ /** This gets the fillrule this path's builder was created with. This is not
+ * mutable.
+ */
+ virtual FillRule GetFillRule() const = 0;
+
+ virtual Float ComputeLength();
+
+ virtual Point ComputePointAtLength(Float aLength,
+ Point* aTangent = nullptr);
+
+protected:
+ Path();
+ void EnsureFlattenedPath();
+
+ RefPtr<FlattenedPath> mFlattenedPath;
+};
+
+/** The PathBuilder class allows path creation. Once finish is called on the
+ * pathbuilder it may no longer be written to.
+ */
+class PathBuilder : public PathSink
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilder)
+ /** Finish writing to the path and return a Path object that can be used for
+ * drawing. Future use of the builder results in a crash!
+ */
+ virtual already_AddRefed<Path> Finish() = 0;
+
+ virtual BackendType GetBackendType() const = 0;
+};
+
+struct Glyph
+{
+ uint32_t mIndex;
+ Point mPosition;
+};
+
+/** This class functions as a glyph buffer that can be drawn to a DrawTarget.
+ * @todo XXX - This should probably contain the guts of gfxTextRun in the future as
+ * roc suggested. But for now it's a simple container for a glyph vector.
+ */
+struct GlyphBuffer
+{
+ const Glyph *mGlyphs; //!< A pointer to a buffer of glyphs. Managed by the caller.
+ uint32_t mNumGlyphs; //!< Number of glyphs mGlyphs points to.
+};
+
+struct GlyphMetrics
+{
+ // Horizontal distance from the origin to the leftmost side of the bounding
+ // box of the drawn glyph. This can be negative!
+ Float mXBearing;
+ // Horizontal distance from the origin of this glyph to the origin of the
+ // next glyph.
+ Float mXAdvance;
+ // Vertical distance from the origin to the topmost side of the bounding box
+ // of the drawn glyph.
+ Float mYBearing;
+ // Vertical distance from the origin of this glyph to the origin of the next
+ // glyph, this is used when drawing vertically and will typically be 0.
+ Float mYAdvance;
+ // Width of the glyph's black box.
+ Float mWidth;
+ // Height of the glyph's black box.
+ Float mHeight;
+};
+
+/** This class is an abstraction of a backend/platform specific font object
+ * at a particular size. It is passed into text drawing calls to describe
+ * the font used for the drawing call.
+ */
+class ScaledFont : public RefCounted<ScaledFont>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
+ virtual ~ScaledFont() {}
+
+ typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void *aBaton);
+ typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength, void* aBaton);
+ typedef void (*FontDescriptorOutput)(const uint8_t *aData, uint32_t aLength, Float aFontSize, void *aBaton);
+
+ virtual FontType GetType() const = 0;
+ virtual AntialiasMode GetDefaultAAMode() {
+ if (gfxPrefs::DisableAllTextAA()) {
+ return AntialiasMode::NONE;
+ }
+
+ return AntialiasMode::DEFAULT;
+ }
+
+ /** This allows getting a path that describes the outline of a set of glyphs.
+ * A target is passed in so that the guarantee is made the returned path
+ * can be used with any DrawTarget that has the same backend as the one
+ * passed in.
+ */
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
+
+ /** This copies the path describing the glyphs into a PathBuilder. We use this
+ * API rather than a generic API to append paths because it allows easier
+ * implementation in some backends, and more efficient implementation in
+ * others.
+ */
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint = nullptr) = 0;
+
+ /* This gets the metrics of a set of glyphs for the current font face.
+ */
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) = 0;
+
+ virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
+
+ virtual bool GetFontInstanceData(FontInstanceDataOutput, void *) { return false; }
+
+ virtual bool GetFontDescriptor(FontDescriptorOutput, void *) { return false; }
+
+ void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+ mUserData.Add(key, userData, destroy);
+ }
+ void *GetUserData(UserDataKey *key) {
+ return mUserData.Get(key);
+ }
+
+protected:
+ ScaledFont() {}
+
+ UserData mUserData;
+};
+
+/**
+ * Derived classes hold a native font resource from which to create
+ * ScaledFonts.
+ */
+class NativeFontResource : public RefCounted<NativeFontResource>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResource)
+
+ /**
+ * Creates a ScaledFont using the font corresponding to the index and
+ * the given glyph size.
+ *
+ * @param aIndex index for the font within the resource.
+ * @param aGlyphSize the size of ScaledFont required.
+ * @param aInstanceData pointer to read-only buffer of any available instance data.
+ * @param aInstanceDataLength the size of the instance data.
+ * @return an already_addrefed ScaledFont, containing nullptr if failed.
+ */
+ virtual already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) = 0;
+
+ virtual ~NativeFontResource() {};
+};
+
+/** This class is designed to allow passing additional glyph rendering
+ * parameters to the glyph drawing functions. This is an empty wrapper class
+ * merely used to allow holding on to and passing around platform specific
+ * parameters. This is because different platforms have unique rendering
+ * parameters.
+ */
+class GlyphRenderingOptions : public RefCounted<GlyphRenderingOptions>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptions)
+ virtual ~GlyphRenderingOptions() {}
+
+ virtual FontType GetType() const = 0;
+
+protected:
+ GlyphRenderingOptions() {}
+};
+
+class DrawTargetCapture;
+
+/** This is the main class used for all the drawing. It is created through the
+ * factory and accepts drawing commands. The results of drawing to a target
+ * may be used either through a Snapshot or by flushing the target and directly
+ * accessing the backing store a DrawTarget was created with.
+ */
+class DrawTarget : public RefCounted<DrawTarget>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)
+ DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
+ virtual ~DrawTarget() {}
+
+ virtual bool IsValid() const { return true; };
+ virtual DrawTargetType GetType() const = 0;
+
+ virtual BackendType GetBackendType() const = 0;
+
+ virtual bool IsRecording() const { return false; }
+
+ /**
+ * Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
+ * Multiple calls to Snapshot() without any drawing operations in between will
+ * normally return the same SourceSurface object.
+ */
+ virtual already_AddRefed<SourceSurface> Snapshot() = 0;
+ virtual IntSize GetSize() = 0;
+
+ /**
+ * If possible returns the bits to this DrawTarget for direct manipulation. While
+ * the bits is locked any modifications to this DrawTarget is forbidden.
+ * Release takes the original data pointer for safety.
+ */
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin = nullptr) { return false; }
+ virtual void ReleaseBits(uint8_t* aData) {}
+
+ /** Ensure that the DrawTarget backend has flushed all drawing operations to
+ * this draw target. This must be called before using the backing surface of
+ * this draw target outside of GFX 2D code.
+ */
+ virtual void Flush() = 0;
+
+ /**
+ * Realize a DrawTargetCapture onto the draw target.
+ *
+ * @param aSource Capture DrawTarget to draw
+ * @param aTransform Transform to apply when replaying commands
+ */
+ virtual void DrawCapturedDT(DrawTargetCapture *aCaptureDT,
+ const Matrix& aTransform);
+
+ /**
+ * Draw a surface to the draw target. Possibly doing partial drawing or
+ * applying scaling. No sampling happens outside the source.
+ *
+ * @param aSurface Source surface to draw
+ * @param aDest Destination rectangle that this drawing operation should draw to
+ * @param aSource Source rectangle in aSurface coordinates, this area of aSurface
+ * will be stretched to the size of aDest.
+ * @param aOptions General draw options that are applied to the operation
+ * @param aSurfOptions DrawSurface options that are applied
+ */
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Draw the output of a FilterNode to the DrawTarget.
+ *
+ * @param aNode FilterNode to draw
+ * @param aSourceRect Source rectangle in FilterNode space to draw
+ * @param aDestPoint Destination point on the DrawTarget to draw the
+ * SourceRectangle of the filter output to
+ */
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Blend a surface to the draw target with a shadow. The shadow is drawn as a
+ * gaussian blur using a specified sigma. The shadow is clipped to the size
+ * of the input surface, so the input surface should contain a transparent
+ * border the size of the approximate coverage of the blur (3 * aSigma).
+ * NOTE: This function works in device space!
+ *
+ * @param aSurface Source surface to draw.
+ * @param aDest Destination point that this drawing operation should draw to.
+ * @param aColor Color of the drawn shadow
+ * @param aOffset Offset of the shadow
+ * @param aSigma Sigma used for the guassian filter kernel
+ * @param aOperator Composition operator used
+ */
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) = 0;
+
+ /**
+ * Clear a rectangle on the draw target to transparent black. This will
+ * respect the clipping region and transform.
+ *
+ * @param aRect Rectangle to clear
+ */
+ virtual void ClearRect(const Rect &aRect) = 0;
+
+ /**
+ * This is essentially a 'memcpy' between two surfaces. It moves a pixel
+ * aligned area from the source surface unscaled directly onto the
+ * drawtarget. This ignores both transform and clip.
+ *
+ * @param aSurface Surface to copy from
+ * @param aSourceRect Source rectangle to be copied
+ * @param aDest Destination point to copy the surface to
+ */
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) = 0;
+
+ /** @see CopySurface
+ * Same as CopySurface, except uses itself as the source.
+ *
+ * Some backends may be able to optimize this better
+ * than just taking a snapshot and using CopySurface.
+ */
+ virtual void CopyRect(const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+ {
+ RefPtr<SourceSurface> source = Snapshot();
+ CopySurface(source, aSourceRect, aDestination);
+ }
+
+ /**
+ * Fill a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * @param aRect Rectangle that forms the mask of this filling operation
+ * @param aPattern Pattern that forms the source of this filling operation
+ * @param aOptions Options that are applied to this operation
+ */
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Stroke a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * @param aRect Rectangle that forms the mask of this stroking operation
+ * @param aPattern Pattern that forms the source of this stroking operation
+ * @param aOptions Options that are applied to this operation
+ */
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Stroke a line on the DrawTarget with a certain source pattern.
+ *
+ * @param aStart Starting point of the line
+ * @param aEnd End point of the line
+ * @param aPattern Pattern that forms the source of this stroking operation
+ * @param aOptions Options that are applied to this operation
+ */
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Stroke a path on the draw target with a certain source pattern.
+ *
+ * @param aPath Path that is to be stroked
+ * @param aPattern Pattern that should be used for the stroke
+ * @param aStrokeOptions Stroke options used for this operation
+ * @param aOptions Draw options used for this operation
+ */
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Fill a path on the draw target with a certain source pattern.
+ *
+ * @param aPath Path that is to be filled
+ * @param aPattern Pattern that should be used for the fill
+ * @param aOptions Draw options used for this operation
+ */
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Fill a series of clyphs on the draw target with a certain source pattern.
+ */
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) = 0;
+
+ /**
+ * This takes a source pattern and a mask, and composites the source pattern
+ * onto the destination surface using the alpha channel of the mask pattern
+ * as a mask for the operation.
+ *
+ * @param aSource Source pattern
+ * @param aMask Mask pattern
+ * @param aOptions Drawing options
+ */
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * This takes a source pattern and a mask, and composites the source pattern
+ * onto the destination surface using the alpha channel of the mask source.
+ * The operation is bound by the extents of the mask.
+ *
+ * @param aSource Source pattern
+ * @param aMask Mask surface
+ * @param aOffset a transformed offset that the surface is masked at
+ * @param aOptions Drawing options
+ */
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Draw aSurface using the 3D transform aMatrix. The DrawTarget's transform
+ * and clip are applied after the 3D transform.
+ *
+ * If the transform fails (i.e. because aMatrix is singular), false is returned and nothing is drawn.
+ */
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix);
+
+ /**
+ * Push a clip to the DrawTarget.
+ *
+ * @param aPath The path to clip to
+ */
+ virtual void PushClip(const Path *aPath) = 0;
+
+ /**
+ * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
+ * is specified in user space.
+ *
+ * @param aRect The rect to clip to
+ */
+ virtual void PushClipRect(const Rect &aRect) = 0;
+
+ /**
+ * Push a clip region specifed by the union of axis-aligned rectangular
+ * clips to the DrawTarget. These rectangles are specified in device space.
+ * This must be balanced by a corresponding call to PopClip within a layer.
+ *
+ * @param aRects The rects to clip to
+ * @param aCount The number of rectangles
+ */
+ virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount);
+
+ /** Pop a clip from the DrawTarget. A pop without a corresponding push will
+ * be ignored.
+ */
+ virtual void PopClip() = 0;
+
+ /**
+ * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
+ * drawing will be redirected to, this is used for example to support group
+ * opacity or the masking of groups. Clips must be balanced within a layer,
+ * i.e. between a matching PushLayer/PopLayer pair there must be as many
+ * PushClip(Rect) calls as there are PopClip calls.
+ *
+ * @param aOpaque Whether the layer will be opaque
+ * @param aOpacity Opacity of the layer
+ * @param aMask Mask applied to the layer
+ * @param aMaskTransform Transform applied to the layer mask
+ * @param aBounds Optional bounds in device space to which the layer is
+ * limited in size.
+ * @param aCopyBackground Whether to copy the background into the layer, this
+ * is only supported when aOpaque is true.
+ */
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) { MOZ_CRASH("GFX: PushLayer"); }
+
+ /**
+ * This balances a call to PushLayer and proceeds to blend the layer back
+ * onto the background. This blend will blend the temporary surface back
+ * onto the target in device space using POINT sampling and operator over.
+ */
+ virtual void PopLayer() { MOZ_CRASH("GFX: PopLayer"); }
+
+ /**
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * existing bitmap data in memory.
+ *
+ * The SourceSurface does not take ownership of aData, and may be freed at any time.
+ */
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const = 0;
+
+ /**
+ * Create a SourceSurface optimized for use with this DrawTarget from an
+ * arbitrary SourceSurface type supported by this backend. This may return
+ * aSourceSurface or some other existing surface.
+ */
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const = 0;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const {
+ return OptimizeSourceSurface(aSurface);
+ }
+
+ /**
+ * Create a SourceSurface for a type of NativeSurface. This may fail if the
+ * draw target does not know how to deal with the type of NativeSurface passed
+ * in. If this succeeds, the SourceSurface takes the ownersip of the NativeSurface.
+ */
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const = 0;
+
+ /**
+ * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
+
+ /**
+ * Create a DrawTarget that captures the drawing commands and can be replayed
+ * onto a compatible DrawTarget afterwards.
+ *
+ * @param aSize Size of the area this DT will capture.
+ */
+ virtual already_AddRefed<DrawTargetCapture> CreateCaptureDT(const IntSize& aSize);
+
+ /**
+ * Create a draw target optimized for drawing a shadow.
+ *
+ * Note that aSigma is the blur radius that must be used when we draw the
+ * shadow. Also note that this doesn't affect the size of the allocated
+ * surface, the caller is still responsible for including the shadow area in
+ * its size.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const
+ {
+ return CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ /**
+ * Create a path builder with the specified fillmode.
+ *
+ * We need the fill mode up front because of Direct2D.
+ * ID2D1SimplifiedGeometrySink requires the fill mode
+ * to be set before calling BeginFigure().
+ */
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
+
+ /**
+ * Create a GradientStops object that holds information about a set of
+ * gradient stops, this object is required for linear or radial gradient
+ * patterns to represent the color stops in the gradient.
+ *
+ * @param aStops An array of gradient stops
+ * @param aNumStops Number of stops in the array aStops
+ * @param aExtendNone This describes how to extend the stop color outside of the
+ * gradient area.
+ */
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const = 0;
+
+ /**
+ * Create a FilterNode object that can be used to apply a filter to various
+ * inputs.
+ *
+ * @param aType Type of filter node to be created.
+ */
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) = 0;
+
+ Matrix GetTransform() const { return mTransform; }
+
+ /*
+ * Get the metrics of a glyph, including any additional spacing that is taken
+ * during rasterization to this backends (for example because of antialiasing
+ * filters.
+ *
+ * aScaledFont The scaled font used when drawing.
+ * aGlyphIndices An array of indices for the glyphs whose the metrics are wanted
+ * aNumGlyphs The amount of elements in aGlyphIndices
+ * aGlyphMetrics The glyph metrics
+ */
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+ {
+ aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs, aGlyphMetrics);
+ }
+
+ /**
+ * Set a transform on the surface, this transform is applied at drawing time
+ * to both the mask and source of the operation.
+ *
+ * Performance note: For some backends it is expensive to change the current
+ * transform (because transforms affect a lot of the parts of the pipeline,
+ * so new transform change can result in a pipeline flush). To get around
+ * this, DrawTarget implementations buffer transform changes and try to only
+ * set the current transform on the backend when required. That tracking has
+ * its own performance impact though, and ideally callers would be smart
+ * enough not to require it. At a future date this method may stop this
+ * doing transform buffering so, if you're a consumer, please try to be smart
+ * about calling this method as little as possible. For example, instead of
+ * concatenating a translation onto the current transform then calling
+ * FillRect, try to integrate the translation into FillRect's aRect
+ * argument's x/y offset.
+ */
+ virtual void SetTransform(const Matrix &aTransform)
+ { mTransform = aTransform; mTransformDirty = true; }
+
+ inline void ConcatTransform(const Matrix &aTransform)
+ { SetTransform(aTransform * Matrix(GetTransform())); }
+
+ SurfaceFormat GetFormat() const { return mFormat; }
+
+ /** Tries to get a native surface for a DrawTarget, this may fail if the
+ * draw target cannot convert to this surface type.
+ */
+ virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
+
+ virtual bool IsDualDrawTarget() const { return false; }
+ virtual bool IsTiledDrawTarget() const { return false; }
+ virtual bool SupportsRegionClipping() const { return true; }
+
+ void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+ mUserData.Add(key, userData, destroy);
+ }
+ void *GetUserData(UserDataKey *key) const {
+ return mUserData.Get(key);
+ }
+ void *RemoveUserData(UserDataKey *key) {
+ return mUserData.Remove(key);
+ }
+
+ /** Within this rectangle all pixels will be opaque by the time the result of
+ * this DrawTarget is first used for drawing. Either by the underlying surface
+ * being used as an input to external drawing, or Snapshot() being called.
+ * This rectangle is specified in device space.
+ */
+ void SetOpaqueRect(const IntRect &aRect) {
+ mOpaqueRect = aRect;
+ }
+
+ const IntRect &GetOpaqueRect() const {
+ return mOpaqueRect;
+ }
+
+ virtual bool IsCurrentGroupOpaque() {
+ return GetFormat() == SurfaceFormat::B8G8R8X8;
+ }
+
+ virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
+ mPermitSubpixelAA = aPermitSubpixelAA;
+ }
+
+ bool GetPermitSubpixelAA() {
+ return mPermitSubpixelAA;
+ }
+
+ /**
+ * Ensures that no snapshot is still pointing to this DrawTarget's surface data.
+ *
+ * This can be useful if the DrawTarget is wrapped around data that it does not
+ * own, and for some reason the owner of the data has to make it temporarily
+ * unavailable without the DrawTarget knowing about it.
+ * This can cause costly surface copies, so it should not be used without a
+ * a good reason.
+ */
+ virtual void DetachAllSnapshots() = 0;
+
+#ifdef USE_SKIA_GPU
+ virtual bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+ {
+ MOZ_CRASH("GFX: InitWithGrContext");
+ }
+#endif
+
+protected:
+ UserData mUserData;
+ Matrix mTransform;
+ IntRect mOpaqueRect;
+ bool mTransformDirty : 1;
+ bool mPermitSubpixelAA : 1;
+
+ SurfaceFormat mFormat;
+};
+
+class DrawTargetCapture : public DrawTarget
+{
+};
+
+class DrawEventRecorder : public RefCounted<DrawEventRecorder>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
+ virtual ~DrawEventRecorder() { }
+};
+
+struct Tile
+{
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mTileOrigin;
+};
+
+struct TileSet
+{
+ Tile* mTiles;
+ size_t mTileCount;
+};
+
+struct Config {
+ LogForwarder* mLogForwarder;
+ int32_t mMaxTextureSize;
+ int32_t mMaxAllocSize;
+
+ Config()
+ : mLogForwarder(nullptr)
+ , mMaxTextureSize(8192)
+ , mMaxAllocSize(52000000)
+ {}
+};
+
+class GFX2D_API Factory
+{
+public:
+ static void Init(const Config& aConfig);
+ static void ShutDown();
+
+ static bool HasSSE2();
+
+ /**
+ * Returns false if any of the following are true:
+ *
+ * - the width/height of |sz| are less than or equal to zero
+ * - the width/height of |sz| are greater than |limit|
+ * - the number of bytes that need to be allocated for the surface is too
+ * big to fit in an int32_t, or bigger than |allocLimit|, if specifed
+ *
+ * To calculate the number of bytes that need to be allocated for the surface
+ * this function makes the conservative assumption that there need to be
+ * 4 bytes-per-pixel, and the stride alignment is 16 bytes.
+ *
+ * The reason for using int32_t rather than uint32_t is again to be
+ * conservative; some code has in the past and may in the future use signed
+ * integers to store buffer lengths etc.
+ */
+ static bool CheckSurfaceSize(const IntSize &sz,
+ int32_t limit = 0,
+ int32_t allocLimit = 0);
+
+ /**
+ * Make sure that the given buffer size doesn't exceed the allocation limit.
+ */
+ static bool CheckBufferSize(int32_t bufSize);
+
+ /** Make sure the given dimension satisfies the CheckSurfaceSize and is
+ * within 8k limit. The 8k value is chosen a bit randomly.
+ */
+ static bool ReasonableSurfaceSize(const IntSize &aSize);
+
+ static bool AllowedSurfaceSize(const IntSize &aSize);
+
+ static already_AddRefed<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
+
+ static already_AddRefed<SourceSurface> CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat);
+
+ static already_AddRefed<DrawTarget>
+ CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
+
+ static already_AddRefed<DrawTarget>
+ CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT);
+
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
+
+#ifdef MOZ_WIDGET_GTK
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+#endif
+
+ /**
+ * This creates a NativeFontResource from TrueType data.
+ *
+ * @param aData Pointer to the data
+ * @param aSize Size of the TrueType data
+ * @param aType Type of NativeFontResource that should be created.
+ * @return a NativeFontResource of nullptr if failed.
+ */
+ static already_AddRefed<NativeFontResource>
+ CreateNativeFontResource(uint8_t *aData, uint32_t aSize, FontType aType);
+
+ /**
+ * This creates a scaled font with an associated cairo_scaled_font_t, and
+ * must be used when using the Cairo backend. The NativeFont and
+ * cairo_scaled_font_t* parameters must correspond to the same font.
+ */
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontWithCairo(const NativeFont &aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont);
+
+ /**
+ * This creates a simple data source surface for a certain size. It allocates
+ * new memory for the surface. This memory is freed when the surface is
+ * destroyed. The caller is responsible for handing the case where nullptr
+ * is returned. The surface is not zeroed unless requested.
+ */
+ static already_AddRefed<DataSourceSurface>
+ CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat, bool aZero = false);
+
+ /**
+ * This creates a simple data source surface for a certain size with a
+ * specific stride, which must be large enough to fit all pixels.
+ * It allocates new memory for the surface. This memory is freed when
+ * the surface is destroyed. The caller is responsible for handling the case
+ * where nullptr is returned. The surface is not zeroed unless requested.
+ */
+ static already_AddRefed<DataSourceSurface>
+ CreateDataSourceSurfaceWithStride(const IntSize &aSize, SurfaceFormat aFormat, int32_t aStride, bool aZero = false);
+
+ typedef void (*SourceSurfaceDeallocator)(void* aClosure);
+
+ /**
+ * This creates a simple data source surface for some existing data. It will
+ * wrap this data and the data for this source surface.
+ *
+ * We can provide a custom destroying function for |aData|. This will be
+ * called in the surface dtor using |aDeallocator| and the |aClosure|. If
+ * there are errors during construction(return a nullptr surface), the caller
+ * is responsible for the deallocation.
+ *
+ * If there is no destroying function, the caller is responsible for
+ * deallocating the aData memory only after destruction of this
+ * DataSourceSurface.
+ */
+ static already_AddRefed<DataSourceSurface>
+ CreateWrappingDataSourceSurface(uint8_t *aData,
+ int32_t aStride,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ SourceSurfaceDeallocator aDeallocator = nullptr,
+ void* aClosure = nullptr);
+
+ static void
+ CopyDataSourceSurface(DataSourceSurface* aSource,
+ DataSourceSurface* aDest);
+
+
+ static already_AddRefed<DrawEventRecorder>
+ CreateEventRecorderForFile(const char *aFilename);
+
+ static void SetGlobalEventRecorder(DrawEventRecorder *aRecorder);
+
+ static uint32_t GetMaxSurfaceSize(BackendType aType);
+
+ static LogForwarder* GetLogForwarder() { return sConfig ? sConfig->mLogForwarder : nullptr; }
+
+private:
+ static Config* sConfig;
+public:
+
+#ifdef USE_SKIA_GPU
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+#endif
+
+ static void PurgeAllCaches();
+
+ static already_AddRefed<DrawTarget>
+ CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB);
+
+ /*
+ * This creates a new tiled DrawTarget. When a tiled drawtarget is used the
+ * drawing is distributed over number of tiles which may each hold an
+ * individual offset. The tiles in the set must each have the same backend
+ * and format.
+ */
+ static already_AddRefed<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
+
+ static bool DoesBackendSupportDataDrawtarget(BackendType aType);
+
+#ifdef USE_SKIA
+ static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas);
+#endif
+
+#ifdef WIN32
+ static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
+
+ /*
+ * Attempts to create and install a D2D1 device from the supplied Direct3D11 device.
+ * Returns true on success, or false on failure and leaves the D2D1/Direct3D11 devices unset.
+ */
+ static bool SetDirect3D11Device(ID3D11Device *aDevice);
+ static bool SetDWriteFactory(IDWriteFactory *aFactory);
+ static ID3D11Device *GetDirect3D11Device();
+ static ID2D1Device *GetD2D1Device();
+ static IDWriteFactory *GetDWriteFactory();
+ static bool SupportsD2D1();
+
+ static already_AddRefed<GlyphRenderingOptions>
+ CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
+
+ static uint64_t GetD2DVRAMUsageDrawTarget();
+ static uint64_t GetD2DVRAMUsageSourceSurface();
+ static void D2DCleanup();
+
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
+ const gfxFontStyle* aStyle,
+ Float aSize,
+ bool aUseEmbeddedBitmap,
+ bool aForceGDIMode);
+
+private:
+ static ID2D1Device *mD2D1Device;
+ static ID3D11Device *mD3D11Device;
+ static IDWriteFactory *mDWriteFactory;
+#endif
+
+ static DrawEventRecorder *mRecorder;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_2D_H
diff --git a/system/graphics/2d/AutoHelpersWin.h b/system/graphics/2d/AutoHelpersWin.h
new file mode 100644
index 000000000..744d0d500
--- /dev/null
+++ b/system/graphics/2d/AutoHelpersWin.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_AutoHelpersWin_h
+#define mozilla_gfx_AutoHelpersWin_h
+
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+// Get the global device context, and auto-release it on destruction.
+class AutoDC
+{
+public:
+ AutoDC() {
+ mDC = ::GetDC(nullptr);
+ }
+
+ ~AutoDC() {
+ ::ReleaseDC(nullptr, mDC);
+ }
+
+ HDC GetDC() {
+ return mDC;
+ }
+
+private:
+ HDC mDC;
+};
+
+// Select a font into the given DC, and auto-restore.
+class AutoSelectFont
+{
+public:
+ AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
+ : mOwnsFont(false)
+ {
+ mFont = ::CreateFontIndirectW(aLogFont);
+ if (mFont) {
+ mOwnsFont = true;
+ mDC = aDC;
+ mOldFont = (HFONT)::SelectObject(aDC, mFont);
+ } else {
+ mOldFont = nullptr;
+ }
+ }
+
+ AutoSelectFont(HDC aDC, HFONT aFont)
+ : mOwnsFont(false)
+ {
+ mDC = aDC;
+ mFont = aFont;
+ mOldFont = (HFONT)::SelectObject(aDC, aFont);
+ }
+
+ ~AutoSelectFont() {
+ if (mOldFont) {
+ ::SelectObject(mDC, mOldFont);
+ if (mOwnsFont) {
+ ::DeleteObject(mFont);
+ }
+ }
+ }
+
+ bool IsValid() const {
+ return mFont != nullptr;
+ }
+
+ HFONT GetFont() const {
+ return mFont;
+ }
+
+private:
+ HDC mDC;
+ HFONT mFont;
+ HFONT mOldFont;
+ bool mOwnsFont;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_AutoHelpersWin_h
diff --git a/system/graphics/2d/BaseCoord.h b/system/graphics/2d/BaseCoord.h
new file mode 100644
index 000000000..61caf9889
--- /dev/null
+++ b/system/graphics/2d/BaseCoord.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASECOORD_H_
+#define MOZILLA_GFX_BASECOORD_H_
+
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BaseCoord {
+ T value;
+
+ // Constructors
+ constexpr BaseCoord() : value(0) {}
+ explicit constexpr BaseCoord(T aValue) : value(aValue) {}
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ operator T() const { return value; }
+
+ friend bool operator==(Sub aA, Sub aB) {
+ return aA.value == aB.value;
+ }
+ friend bool operator!=(Sub aA, Sub aB) {
+ return aA.value != aB.value;
+ }
+
+ friend Sub operator+(Sub aA, Sub aB) {
+ return Sub(aA.value + aB.value);
+ }
+ friend Sub operator-(Sub aA, Sub aB) {
+ return Sub(aA.value - aB.value);
+ }
+ friend Sub operator*(Sub aCoord, T aScale) {
+ return Sub(aCoord.value * aScale);
+ }
+ friend Sub operator*(T aScale, Sub aCoord) {
+ return Sub(aScale * aCoord.value);
+ }
+ friend Sub operator/(Sub aCoord, T aScale) {
+ return Sub(aCoord.value / aScale);
+ }
+ // 'scale / coord' is intentionally omitted because it doesn't make sense.
+
+ Sub& operator+=(Sub aCoord) {
+ value += aCoord.value;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(Sub aCoord) {
+ value -= aCoord.value;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator*=(T aScale) {
+ value *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator/=(T aScale) {
+ value /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ // Since BaseCoord is implicitly convertible to its value type T, we need
+ // mixed-type operator overloads to avoid ambiguities at mixed-type call
+ // sites. As we transition more of our code to strongly-typed classes, we
+ // may be able to remove some or all of these overloads.
+ friend bool operator==(Sub aA, T aB) {
+ return aA.value == aB;
+ }
+ friend bool operator==(T aA, Sub aB) {
+ return aA == aB.value;
+ }
+ friend bool operator!=(Sub aA, T aB) {
+ return aA.value != aB;
+ }
+ friend bool operator!=(T aA, Sub aB) {
+ return aA != aB.value;
+ }
+ friend T operator+(Sub aA, T aB) {
+ return aA.value + aB;
+ }
+ friend T operator+(T aA, Sub aB) {
+ return aA + aB.value;
+ }
+ friend T operator-(Sub aA, T aB) {
+ return aA.value - aB;
+ }
+ friend T operator-(T aA, Sub aB) {
+ return aA - aB.value;
+ }
+
+ Sub operator-() const {
+ return Sub(-value);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASECOORD_H_ */
diff --git a/system/graphics/2d/BaseMargin.h b/system/graphics/2d/BaseMargin.h
new file mode 100644
index 000000000..76f0a5296
--- /dev/null
+++ b/system/graphics/2d/BaseMargin.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASEMARGIN_H_
+#define MOZILLA_GFX_BASEMARGIN_H_
+
+#include <ostream>
+
+#include "Types.h"
+
+namespace mozilla {
+
+/**
+ * Sides represents a set of physical sides.
+ */
+struct Sides final {
+ Sides() : mBits(0) {}
+ explicit Sides(SideBits aSideBits)
+ {
+ MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
+ mBits = aSideBits;
+ }
+ bool IsEmpty() const { return mBits == 0; }
+ bool Top() const { return (mBits & eSideBitsTop) != 0; }
+ bool Right() const { return (mBits & eSideBitsRight) != 0; }
+ bool Bottom() const { return (mBits & eSideBitsBottom) != 0; }
+ bool Left() const { return (mBits & eSideBitsLeft) != 0; }
+ bool Contains(SideBits aSideBits) const
+ {
+ MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
+ return (mBits & aSideBits) == aSideBits;
+ }
+ Sides operator|(Sides aOther) const
+ {
+ return Sides(SideBits(mBits | aOther.mBits));
+ }
+ Sides operator|(SideBits aSideBits) const
+ {
+ return *this | Sides(aSideBits);
+ }
+ Sides& operator|=(Sides aOther)
+ {
+ mBits |= aOther.mBits;
+ return *this;
+ }
+ Sides& operator|=(SideBits aSideBits)
+ {
+ return *this |= Sides(aSideBits);
+ }
+ bool operator==(Sides aOther) const
+ {
+ return mBits == aOther.mBits;
+ }
+ bool operator!=(Sides aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+private:
+ uint8_t mBits;
+};
+
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass.
+ */
+template <class T, class Sub>
+struct BaseMargin {
+ typedef mozilla::Side SideT; // because we have a method named Side
+
+ // Do not change the layout of these members; the Side() methods below
+ // depend on this order.
+ T top, right, bottom, left;
+
+ // Constructors
+ BaseMargin() : top(0), right(0), bottom(0), left(0) {}
+ BaseMargin(T aTop, T aRight, T aBottom, T aLeft) :
+ top(aTop), right(aRight), bottom(aBottom), left(aLeft) {}
+
+ void SizeTo(T aTop, T aRight, T aBottom, T aLeft)
+ {
+ top = aTop; right = aRight; bottom = aBottom; left = aLeft;
+ }
+
+ T LeftRight() const { return left + right; }
+ T TopBottom() const { return top + bottom; }
+
+ T& Side(SideT aSide) {
+ // This is ugly!
+ return *(&top + int(aSide));
+ }
+ T Side(SideT aSide) const {
+ // This is ugly!
+ return *(&top + int(aSide));
+ }
+
+ void ApplySkipSides(Sides aSkipSides)
+ {
+ if (aSkipSides.Top()) {
+ top = 0;
+ }
+ if (aSkipSides.Right()) {
+ right = 0;
+ }
+ if (aSkipSides.Bottom()) {
+ bottom = 0;
+ }
+ if (aSkipSides.Left()) {
+ left = 0;
+ }
+ }
+
+ // Overloaded operators. Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+ bool operator==(const Sub& aMargin) const {
+ return top == aMargin.top && right == aMargin.right &&
+ bottom == aMargin.bottom && left == aMargin.left;
+ }
+ bool operator!=(const Sub& aMargin) const {
+ return !(*this == aMargin);
+ }
+ Sub operator+(const Sub& aMargin) const {
+ return Sub(top + aMargin.top, right + aMargin.right,
+ bottom + aMargin.bottom, left + aMargin.left);
+ }
+ Sub operator-(const Sub& aMargin) const {
+ return Sub(top - aMargin.top, right - aMargin.right,
+ bottom - aMargin.bottom, left - aMargin.left);
+ }
+ Sub& operator+=(const Sub& aMargin) {
+ top += aMargin.top;
+ right += aMargin.right;
+ bottom += aMargin.bottom;
+ left += aMargin.left;
+ return *static_cast<Sub*>(this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const BaseMargin& aMargin) {
+ return aStream << '(' << aMargin.top << ',' << aMargin.right << ','
+ << aMargin.bottom << ',' << aMargin.left << ')';
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASEMARGIN_H_ */
diff --git a/system/graphics/2d/BasePoint.h b/system/graphics/2d/BasePoint.h
new file mode 100644
index 000000000..8e4c5fc56
--- /dev/null
+++ b/system/graphics/2d/BasePoint.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASEPOINT_H_
+#define MOZILLA_GFX_BASEPOINT_H_
+
+#include <cmath>
+#include <ostream>
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub, class Coord = T>
+struct BasePoint {
+ union {
+ struct {
+ T x, y;
+ };
+ T components[2];
+ };
+
+ // Constructors
+ constexpr BasePoint() : x(0), y(0) {}
+ constexpr BasePoint(Coord aX, Coord aY) : x(aX), y(aY) {}
+
+ void MoveTo(T aX, T aY) { x = aX; y = aY; }
+ void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y;
+ }
+
+ Coord Length() const {
+ return hypot(x, y);
+ }
+
+ T LengthSquare() const {
+ return x * x + y * y;
+ }
+
+ // Round() is *not* rounding to nearest integer if the values are negative.
+ // They are always rounding as floor(n + 0.5).
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
+ Sub& Round() {
+ x = Coord(floor(T(x) + T(0.5)));
+ y = Coord(floor(T(y) + T(0.5)));
+ return *static_cast<Sub*>(this);
+ }
+
+ // "Finite" means not inf and not NaN
+ bool IsFinite() const
+ {
+ typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
+ return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y)));
+ return true;
+ }
+
+ void Clamp(T aMaxAbsValue)
+ {
+ x = std::max(std::min(x, aMaxAbsValue), -aMaxAbsValue);
+ y = std::max(std::min(y, aMaxAbsValue), -aMaxAbsValue);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const BasePoint<T, Sub, Coord>& aPoint) {
+ return stream << '(' << aPoint.x << ',' << aPoint.y << ')';
+ }
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASEPOINT_H_ */
diff --git a/system/graphics/2d/BasePoint3D.h b/system/graphics/2d/BasePoint3D.h
new file mode 100644
index 000000000..41e387576
--- /dev/null
+++ b/system/graphics/2d/BasePoint3D.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_BASEPOINT3D_H_
+#define MOZILLA_BASEPOINT3D_H_
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BasePoint3D {
+ union {
+ struct {
+ T x, y, z;
+ };
+ T components[3];
+ };
+
+ // Constructors
+ BasePoint3D() : x(0), y(0), z(0) {}
+ BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
+
+ void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
+ void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ T& operator[](int aIndex) {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
+ return *((&x)+aIndex);
+ }
+
+ const T& operator[](int aIndex) const {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
+ return *((&x)+aIndex);
+ }
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y && z == aPoint.z;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y || z != aPoint.z;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ z += aPoint.z;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ z -= aPoint.z;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale, z * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale, z / aScale);
+ }
+
+ Sub& operator*=(T aScale) {
+ x *= aScale;
+ y *= aScale;
+ z *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub& operator/=(T aScale) {
+ x /= aScale;
+ y /= aScale;
+ z /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y, -z);
+ }
+
+ Sub CrossProduct(const Sub& aPoint) const {
+ return Sub(y * aPoint.z - aPoint.y * z,
+ z * aPoint.x - aPoint.z * x,
+ x * aPoint.y - aPoint.x * y);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y + z * aPoint.z;
+ }
+
+ T Length() const {
+ return sqrt(x*x + y*y + z*z);
+ }
+
+ // Invalid for points with distance from origin of 0.
+ void Normalize() {
+ *this /= Length();
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_BASEPOINT3D_H_ */
diff --git a/system/graphics/2d/BasePoint4D.h b/system/graphics/2d/BasePoint4D.h
new file mode 100644
index 000000000..3f4d71011
--- /dev/null
+++ b/system/graphics/2d/BasePoint4D.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_BASEPOINT4D_H_
+#define MOZILLA_BASEPOINT4D_H_
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BasePoint4D {
+ union {
+ struct {
+ T x, y, z, w;
+ };
+ T components[4];
+ };
+
+ // Constructors
+ BasePoint4D() : x(0), y(0), z(0), w(0) {}
+ BasePoint4D(T aX, T aY, T aZ, T aW) : x(aX), y(aY), z(aZ), w(aW) {}
+
+ void MoveTo(T aX, T aY, T aZ, T aW) { x = aX; y = aY; z = aZ; w = aW; }
+ void MoveBy(T aDx, T aDy, T aDz, T aDw) { x += aDx; y += aDy; z += aDz; w += aDw; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y &&
+ z == aPoint.z && w == aPoint.w;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y ||
+ z != aPoint.z || w != aPoint.w;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z, w + aPoint.w);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z, w - aPoint.w);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ z += aPoint.z;
+ w += aPoint.w;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ z -= aPoint.z;
+ w -= aPoint.w;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale, z * aScale, w * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale, z / aScale, w / aScale);
+ }
+
+ Sub& operator*=(T aScale) {
+ x *= aScale;
+ y *= aScale;
+ z *= aScale;
+ w *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub& operator/=(T aScale) {
+ x /= aScale;
+ y /= aScale;
+ z /= aScale;
+ w /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y, -z, -w);
+ }
+
+ T& operator[](int aIndex) {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ return *((&x)+aIndex);
+ }
+
+ const T& operator[](int aIndex) const {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ return *((&x)+aIndex);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y + z * aPoint.z + w * aPoint.w;
+ }
+
+ // Ignores the 4th component!
+ Sub CrossProduct(const Sub& aPoint) const {
+ return Sub(y * aPoint.z - aPoint.y * z,
+ z * aPoint.x - aPoint.z * x,
+ x * aPoint.y - aPoint.x * y,
+ 0);
+ }
+
+ T Length() const {
+ return sqrt(x*x + y*y + z*z + w*w);
+ }
+
+ void Normalize() {
+ *this /= Length();
+ }
+
+ bool HasPositiveWCoord() { return w > 0; }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_BASEPOINT4D_H_ */
diff --git a/system/graphics/2d/BaseRect.h b/system/graphics/2d/BaseRect.h
new file mode 100644
index 000000000..57d01ba09
--- /dev/null
+++ b/system/graphics/2d/BaseRect.h
@@ -0,0 +1,586 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASERECT_H_
+#define MOZILLA_GFX_BASERECT_H_
+
+#include <algorithm>
+#include <cmath>
+#include <ostream>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+#include "Types.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Rectangles have two interpretations: a set of (zero-size) points,
+ * and a rectangular area of the plane. Most rectangle operations behave
+ * the same no matter what interpretation is being used, but some operations
+ * differ:
+ * -- Equality tests behave differently. When a rectangle represents an area,
+ * all zero-width and zero-height rectangles are equal to each other since they
+ * represent the empty area. But when a rectangle represents a set of
+ * mathematical points, zero-width and zero-height rectangles can be unequal.
+ * -- The union operation can behave differently. When rectangles represent
+ * areas, taking the union of a zero-width or zero-height rectangle with
+ * another rectangle can just ignore the empty rectangle. But when rectangles
+ * represent sets of mathematical points, we may need to extend the latter
+ * rectangle to include the points of a zero-width or zero-height rectangle.
+ *
+ * To ensure that these interpretations are explicitly disambiguated, we
+ * deny access to the == and != operators and require use of IsEqualEdges and
+ * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
+ * methods.
+ *
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass.
+ */
+template <class T, class Sub, class Point, class SizeT, class MarginT>
+struct BaseRect {
+ T x, y, width, height;
+
+ // Constructors
+ BaseRect() : x(0), y(0), width(0), height(0) {}
+ BaseRect(const Point& aOrigin, const SizeT &aSize) :
+ x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
+ {
+ }
+ BaseRect(T aX, T aY, T aWidth, T aHeight) :
+ x(aX), y(aY), width(aWidth), height(aHeight)
+ {
+ }
+
+ // Emptiness. An empty rect is one that has no area, i.e. its height or width
+ // is <= 0
+ bool IsEmpty() const { return height <= 0 || width <= 0; }
+ void SetEmpty() { width = height = 0; }
+
+ // "Finite" means not inf and not NaN
+ bool IsFinite() const
+ {
+ typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
+ return (mozilla::IsFinite(FloatType(x)) &&
+ mozilla::IsFinite(FloatType(y)) &&
+ mozilla::IsFinite(FloatType(width)) &&
+ mozilla::IsFinite(FloatType(height)));
+ }
+
+ // Returns true if this rectangle contains the interior of aRect. Always
+ // returns true if aRect is empty, and always returns false is aRect is
+ // nonempty but this rect is empty.
+ bool Contains(const Sub& aRect) const
+ {
+ return aRect.IsEmpty() ||
+ (x <= aRect.x && aRect.XMost() <= XMost() &&
+ y <= aRect.y && aRect.YMost() <= YMost());
+ }
+ // Returns true if this rectangle contains the point. Points are considered
+ // in the rectangle if they are on the left or top edge, but outside if they
+ // are on the right or bottom edge.
+ bool Contains(T aX, T aY) const
+ {
+ return x <= aX && aX < XMost() &&
+ y <= aY && aY < YMost();
+ }
+ // Returns true if this rectangle contains the point. Points are considered
+ // in the rectangle if they are on the left or top edge, but outside if they
+ // are on the right or bottom edge.
+ bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
+
+ // Intersection. Returns TRUE if the receiver's area has non-empty
+ // intersection with aRect's area, and FALSE otherwise.
+ // Always returns false if aRect is empty or 'this' is empty.
+ bool Intersects(const Sub& aRect) const
+ {
+ return !IsEmpty() && !aRect.IsEmpty() &&
+ x < aRect.XMost() && aRect.x < XMost() &&
+ y < aRect.YMost() && aRect.y < YMost();
+ }
+ // Returns the rectangle containing the intersection of the points
+ // (including edges) of *this and aRect. If there are no points in that
+ // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
+ // of *this and aRect.
+ MOZ_MUST_USE Sub Intersect(const Sub& aRect) const
+ {
+ Sub result;
+ result.x = std::max<T>(x, aRect.x);
+ result.y = std::max<T>(y, aRect.y);
+ result.width = std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
+ result.height = std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
+ if (result.width < 0 || result.height < 0) {
+ result.SizeTo(0, 0);
+ }
+ return result;
+ }
+ // Sets *this to be the rectangle containing the intersection of the points
+ // (including edges) of *this and aRect. If there are no points in that
+ // intersection, sets *this to be an empty rectangle with x/y set to the std::max
+ // of the x/y of *this and aRect.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
+ {
+ *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
+ return !IsEmpty();
+ }
+
+ // Returns the smallest rectangle that contains both the area of both
+ // this and aRect2.
+ // Thus, empty input rectangles are ignored.
+ // If both rectangles are empty, returns this.
+ // WARNING! This is not safe against overflow, prefer using SafeUnion instead
+ // when dealing with int-based rects.
+ MOZ_MUST_USE Sub Union(const Sub& aRect) const
+ {
+ if (IsEmpty()) {
+ return aRect;
+ } else if (aRect.IsEmpty()) {
+ return *static_cast<const Sub*>(this);
+ } else {
+ return UnionEdges(aRect);
+ }
+ }
+ // Returns the smallest rectangle that contains both the points (including
+ // edges) of both aRect1 and aRect2.
+ // Thus, empty input rectangles are allowed to affect the result.
+ // WARNING! This is not safe against overflow, prefer using SafeUnionEdges
+ // instead when dealing with int-based rects.
+ MOZ_MUST_USE Sub UnionEdges(const Sub& aRect) const
+ {
+ Sub result;
+ result.x = std::min(x, aRect.x);
+ result.y = std::min(y, aRect.y);
+ result.width = std::max(XMost(), aRect.XMost()) - result.x;
+ result.height = std::max(YMost(), aRect.YMost()) - result.y;
+ return result;
+ }
+ // Computes the smallest rectangle that contains both the area of both
+ // aRect1 and aRect2, and fills 'this' with the result.
+ // Thus, empty input rectangles are ignored.
+ // If both rectangles are empty, sets 'this' to aRect2.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ void UnionRect(const Sub& aRect1, const Sub& aRect2)
+ {
+ *static_cast<Sub*>(this) = aRect1.Union(aRect2);
+ }
+
+ // Computes the smallest rectangle that contains both the points (including
+ // edges) of both aRect1 and aRect2.
+ // Thus, empty input rectangles are allowed to affect the result.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
+ {
+ *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
+ }
+
+ // Expands the rect to include the point
+ void ExpandToEnclose(const Point& aPoint)
+ {
+ if (aPoint.x < x) {
+ width = XMost() - aPoint.x;
+ x = aPoint.x;
+ } else if (aPoint.x > XMost()) {
+ width = aPoint.x - x;
+ }
+ if (aPoint.y < y) {
+ height = YMost() - aPoint.y;
+ y = aPoint.y;
+ } else if (aPoint.y > YMost()) {
+ height = aPoint.y - y;
+ }
+ }
+
+ void SetRect(T aX, T aY, T aWidth, T aHeight)
+ {
+ x = aX; y = aY; width = aWidth; height = aHeight;
+ }
+ void SetRect(const Point& aPt, const SizeT& aSize)
+ {
+ SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
+ }
+ void MoveTo(T aX, T aY) { x = aX; y = aY; }
+ void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
+ void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
+ void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
+ void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
+ void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
+
+ void Inflate(T aD) { Inflate(aD, aD); }
+ void Inflate(T aDx, T aDy)
+ {
+ x -= aDx;
+ y -= aDy;
+ width += 2 * aDx;
+ height += 2 * aDy;
+ }
+ void Inflate(const MarginT& aMargin)
+ {
+ x -= aMargin.left;
+ y -= aMargin.top;
+ width += aMargin.LeftRight();
+ height += aMargin.TopBottom();
+ }
+ void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
+
+ void Deflate(T aD) { Deflate(aD, aD); }
+ void Deflate(T aDx, T aDy)
+ {
+ x += aDx;
+ y += aDy;
+ width = std::max(T(0), width - 2 * aDx);
+ height = std::max(T(0), height - 2 * aDy);
+ }
+ void Deflate(const MarginT& aMargin)
+ {
+ x += aMargin.left;
+ y += aMargin.top;
+ width = std::max(T(0), width - aMargin.LeftRight());
+ height = std::max(T(0), height - aMargin.TopBottom());
+ }
+ void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
+
+ // Return true if the rectangles contain the same set of points, including
+ // points on the edges.
+ // Use when we care about the exact x/y/width/height values being
+ // equal (i.e. we care about differences in empty rectangles).
+ bool IsEqualEdges(const Sub& aRect) const
+ {
+ return x == aRect.x && y == aRect.y &&
+ width == aRect.width && height == aRect.height;
+ }
+ // Return true if the rectangles contain the same area of the plane.
+ // Use when we do not care about differences in empty rectangles.
+ bool IsEqualInterior(const Sub& aRect) const
+ {
+ return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
+ }
+
+ friend Sub operator+(Sub aSub, const Point& aPoint)
+ {
+ aSub += aPoint;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const Point& aPoint)
+ {
+ aSub -= aPoint;
+ return aSub;
+ }
+ friend Sub operator+(Sub aSub, const SizeT& aSize)
+ {
+ aSub += aSize;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const SizeT& aSize)
+ {
+ aSub -= aSize;
+ return aSub;
+ }
+ Sub& operator+=(const Point& aPoint)
+ {
+ MoveBy(aPoint);
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Point& aPoint)
+ {
+ MoveBy(-aPoint);
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator+=(const SizeT& aSize)
+ {
+ width += aSize.width;
+ height += aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const SizeT& aSize)
+ {
+ width -= aSize.width;
+ height -= aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+ // Find difference as a Margin
+ MarginT operator-(const Sub& aRect) const
+ {
+ return MarginT(aRect.y - y,
+ XMost() - aRect.XMost(),
+ YMost() - aRect.YMost(),
+ aRect.x - x);
+ }
+
+ // Helpers for accessing the vertices
+ Point TopLeft() const { return Point(x, y); }
+ Point TopRight() const { return Point(XMost(), y); }
+ Point BottomLeft() const { return Point(x, YMost()); }
+ Point BottomRight() const { return Point(XMost(), YMost()); }
+ Point AtCorner(int aCorner) const {
+ switch (aCorner) {
+ case RectCorner::TopLeft: return TopLeft();
+ case RectCorner::TopRight: return TopRight();
+ case RectCorner::BottomRight: return BottomRight();
+ case RectCorner::BottomLeft: return BottomLeft();
+ }
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+ Point CCWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopLeft();
+ case NS_SIDE_RIGHT: return TopRight();
+ case NS_SIDE_BOTTOM: return BottomRight();
+ case NS_SIDE_LEFT: return BottomLeft();
+ }
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+ Point CWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopRight();
+ case NS_SIDE_RIGHT: return BottomRight();
+ case NS_SIDE_BOTTOM: return BottomLeft();
+ case NS_SIDE_LEFT: return TopLeft();
+ }
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+ Point Center() const { return Point(x, y) + Point(width, height)/2; }
+ SizeT Size() const { return SizeT(width, height); }
+
+ T Area() const { return width * height; }
+
+ // Helper methods for computing the extents
+ T X() const { return x; }
+ T Y() const { return y; }
+ T Width() const { return width; }
+ T Height() const { return height; }
+ T XMost() const { return x + width; }
+ T YMost() const { return y + height; }
+
+ // Get the coordinate of the edge on the given side.
+ T Edge(mozilla::Side aSide) const
+ {
+ switch (aSide) {
+ case NS_SIDE_TOP: return Y();
+ case NS_SIDE_RIGHT: return XMost();
+ case NS_SIDE_BOTTOM: return YMost();
+ case NS_SIDE_LEFT: return X();
+ }
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+
+ // Moves one edge of the rect without moving the opposite edge.
+ void SetLeftEdge(T aX) {
+ MOZ_ASSERT(aX <= XMost());
+ width = XMost() - aX;
+ x = aX;
+ }
+ void SetRightEdge(T aXMost) {
+ MOZ_ASSERT(aXMost >= x);
+ width = aXMost - x;
+ }
+ void SetTopEdge(T aY) {
+ MOZ_ASSERT(aY <= YMost());
+ height = YMost() - aY;
+ y = aY;
+ }
+ void SetBottomEdge(T aYMost) {
+ MOZ_ASSERT(aYMost >= y);
+ height = aYMost - y;
+ }
+
+ // Round the rectangle edges to integer coordinates, such that the rounded
+ // rectangle has the same set of pixel centers as the original rectangle.
+ // Edges at offset 0.5 round up.
+ // Suitable for most places where integral device coordinates
+ // are needed, but note that any translation should be applied first to
+ // avoid pixel rounding errors.
+ // Note that this is *not* rounding to nearest integer if the values are negative.
+ // They are always rounding as floor(n + 0.5).
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
+ // If you need similar method which is using NS_round(), you should create
+ // new |RoundAwayFromZero()| method.
+ void Round()
+ {
+ T x0 = static_cast<T>(floor(T(X()) + 0.5));
+ T y0 = static_cast<T>(floor(T(Y()) + 0.5));
+ T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
+ T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Snap the rectangle edges to integer coordinates, such that the
+ // original rectangle contains the resulting rectangle.
+ void RoundIn()
+ {
+ T x0 = static_cast<T>(ceil(T(X())));
+ T y0 = static_cast<T>(ceil(T(Y())));
+ T x1 = static_cast<T>(floor(T(XMost())));
+ T y1 = static_cast<T>(floor(T(YMost())));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Snap the rectangle edges to integer coordinates, such that the
+ // resulting rectangle contains the original rectangle.
+ void RoundOut()
+ {
+ T x0 = static_cast<T>(floor(T(X())));
+ T y0 = static_cast<T>(floor(T(Y())));
+ T x1 = static_cast<T>(ceil(T(XMost())));
+ T y1 = static_cast<T>(ceil(T(YMost())));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Scale 'this' by aScale without doing any rounding.
+ void Scale(T aScale) { Scale(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, without doing any rounding.
+ void Scale(T aXScale, T aYScale)
+ {
+ T right = XMost() * aXScale;
+ T bottom = YMost() * aYScale;
+ x = x * aXScale;
+ y = y * aYScale;
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by aScale, converting coordinates to integers so that the result is
+ // the smallest integer-coordinate rectangle containing the unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
+ // that the result is the smallest integer-coordinate rectangle containing the
+ // unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleRoundOut(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(ceil(double(XMost()) * aXScale));
+ T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
+ x = static_cast<T>(floor(double(x) * aXScale));
+ y = static_cast<T>(floor(double(y) * aYScale));
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by aScale, converting coordinates to integers so that the result is
+ // the largest integer-coordinate rectangle contained by the unrounded result.
+ void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
+ // that the result is the largest integer-coordinate rectangle contained by the
+ // unrounded result.
+ void ScaleRoundIn(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(floor(double(XMost()) * aXScale));
+ T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
+ x = static_cast<T>(ceil(double(x) * aXScale));
+ y = static_cast<T>(ceil(double(y) * aYScale));
+ width = std::max<T>(0, right - x);
+ height = std::max<T>(0, bottom - y);
+ }
+ // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
+ // the smallest integer-coordinate rectangle containing the unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
+ // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
+ // that the result is the smallest integer-coordinate rectangle containing the
+ // unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleInverseRoundOut(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(ceil(double(XMost()) / aXScale));
+ T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
+ x = static_cast<T>(floor(double(x) / aXScale));
+ y = static_cast<T>(floor(double(y) / aYScale));
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
+ // the largest integer-coordinate rectangle contained by the unrounded result.
+ void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
+ // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
+ // that the result is the largest integer-coordinate rectangle contained by the
+ // unrounded result.
+ void ScaleInverseRoundIn(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(floor(double(XMost()) / aXScale));
+ T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
+ x = static_cast<T>(ceil(double(x) / aXScale));
+ y = static_cast<T>(ceil(double(y) / aYScale));
+ width = std::max<T>(0, right - x);
+ height = std::max<T>(0, bottom - y);
+ }
+
+ /**
+ * Clamp aPoint to this rectangle. It is allowed to end up on any
+ * edge of the rectangle.
+ */
+ MOZ_MUST_USE Point ClampPoint(const Point& aPoint) const
+ {
+ return Point(std::max(x, std::min(XMost(), aPoint.x)),
+ std::max(y, std::min(YMost(), aPoint.y)));
+ }
+
+ /**
+ * Translate this rectangle to be inside aRect. If it doesn't fit inside
+ * aRect then the dimensions that don't fit will be shrunk so that they
+ * do fit. The resulting rect is returned.
+ */
+ MOZ_MUST_USE Sub MoveInsideAndClamp(const Sub& aRect) const
+ {
+ Sub rect(std::max(aRect.x, x),
+ std::max(aRect.y, y),
+ std::min(aRect.width, width),
+ std::min(aRect.height, height));
+ rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
+ rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
+ return rect;
+ }
+
+ // Returns the largest rectangle that can be represented with 32-bit
+ // signed integers, centered around a point at 0,0. As BaseRect's represent
+ // the dimensions as a top-left point with a width and height, the width
+ // and height will be the largest positive 32-bit value. The top-left
+ // position coordinate is divided by two to center the rectangle around a
+ // point at 0,0.
+ static Sub MaxIntRect()
+ {
+ return Sub(
+ static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+ static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+ static_cast<T>(std::numeric_limits<int32_t>::max()),
+ static_cast<T>(std::numeric_limits<int32_t>::max())
+ );
+ };
+
+ friend std::ostream& operator<<(std::ostream& stream,
+ const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
+ return stream << '(' << aRect.x << ',' << aRect.y << ','
+ << aRect.width << ',' << aRect.height << ')';
+ }
+
+private:
+ // Do not use the default operator== or operator!= !
+ // Use IsEqualEdges or IsEqualInterior explicitly.
+ bool operator==(const Sub& aRect) const { return false; }
+ bool operator!=(const Sub& aRect) const { return false; }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASERECT_H_ */
diff --git a/system/graphics/2d/BaseSize.h b/system/graphics/2d/BaseSize.h
new file mode 100644
index 000000000..7bcccc629
--- /dev/null
+++ b/system/graphics/2d/BaseSize.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASESIZE_H_
+#define MOZILLA_GFX_BASESIZE_H_
+
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BaseSize {
+ union {
+ struct {
+ T width, height;
+ };
+ T components[2];
+ };
+
+ // Constructors
+ constexpr BaseSize() : width(0), height(0) {}
+ constexpr BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
+
+ void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
+
+ bool IsEmpty() const {
+ return width <= 0 || height <= 0;
+ }
+
+ bool IsSquare() const {
+ return width == height;
+ }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aSize) const {
+ return width == aSize.width && height == aSize.height;
+ }
+ bool operator!=(const Sub& aSize) const {
+ return width != aSize.width || height != aSize.height;
+ }
+ bool operator<=(const Sub& aSize) const {
+ return width <= aSize.width && height <= aSize.height;
+ }
+ bool operator<(const Sub& aSize) const {
+ return *this <= aSize && *this != aSize;
+ }
+
+ Sub operator+(const Sub& aSize) const {
+ return Sub(width + aSize.width, height + aSize.height);
+ }
+ Sub operator-(const Sub& aSize) const {
+ return Sub(width - aSize.width, height - aSize.height);
+ }
+ Sub& operator+=(const Sub& aSize) {
+ width += aSize.width;
+ height += aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aSize) {
+ width -= aSize.width;
+ height -= aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(width * aScale, height * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(width / aScale, height / aScale);
+ }
+ friend Sub operator*(T aScale, const Sub& aSize) {
+ return Sub(aScale * aSize.width, aScale * aSize.height);
+ }
+ void Scale(T aXScale, T aYScale) {
+ width *= aXScale;
+ height *= aYScale;
+ }
+
+ Sub operator*(const Sub& aSize) const {
+ return Sub(width * aSize.width, height * aSize.height);
+ }
+ Sub operator/(const Sub& aSize) const {
+ return Sub(width / aSize.width, height / aSize.height);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASESIZE_H_ */
diff --git a/system/graphics/2d/BezierUtils.cpp b/system/graphics/2d/BezierUtils.cpp
new file mode 100644
index 000000000..292c6356b
--- /dev/null
+++ b/system/graphics/2d/BezierUtils.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BezierUtils.h"
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+Point
+GetBezierPoint(const Bezier& aBezier, Float t)
+{
+ Float s = 1.0f - t;
+
+ return Point(
+ aBezier.mPoints[0].x * s * s * s +
+ 3.0f * aBezier.mPoints[1].x * t * s * s +
+ 3.0f * aBezier.mPoints[2].x * t * t * s +
+ aBezier.mPoints[3].x * t * t * t,
+ aBezier.mPoints[0].y * s * s * s +
+ 3.0f * aBezier.mPoints[1].y * t * s * s +
+ 3.0f * aBezier.mPoints[2].y * t * t * s +
+ aBezier.mPoints[3].y * t * t * t
+ );
+}
+
+Point
+GetBezierDifferential(const Bezier& aBezier, Float t)
+{
+ // Return P'(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ -3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
+ 2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
+ -3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
+ 2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
+ );
+}
+
+Point
+GetBezierDifferential2(const Bezier& aBezier, Float t)
+{
+ // Return P''(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ 6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
+ (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
+ 6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
+ (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
+ );
+}
+
+Float
+GetBezierLength(const Bezier& aBezier, Float a, Float b)
+{
+ if (a < 0.5f && b > 0.5f) {
+ // To increase the accuracy, split into two parts.
+ return GetBezierLength(aBezier, a, 0.5f) +
+ GetBezierLength(aBezier, 0.5f, b);
+ }
+
+ // Calculate length of simple bezier curve with Simpson's rule.
+ // _
+ // / b
+ // length = | |P'(x)| dx
+ // _/ a
+ //
+ // b - a a + b
+ // = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
+ // 6 2
+
+ Float fa = GetBezierDifferential(aBezier, a).Length();
+ Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
+ Float fb = GetBezierDifferential(aBezier, b).Length();
+
+ return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
+}
+
+static void
+SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[0] = aBezier.mPoints[0];
+
+ aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+
+ aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
+ tmp1 = tmp1 * s + tmp2 * t;
+
+ aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
+}
+
+static void
+SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[3] = aBezier.mPoints[3];
+
+ aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+
+ aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
+ tmp1 = tmp2 * s + tmp1 * t;
+
+ aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
+}
+
+void
+GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
+{
+ Bezier tmp;
+ SplitBezierB(&tmp, aBezier, t1);
+
+ Float range = 1.0f - t1;
+ if (range == 0.0f) {
+ *aSubBezier = tmp;
+ } else {
+ SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
+ }
+}
+
+static Point
+BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float* aT)
+{
+ // Find a nearest point on bezier curve with Binary search.
+ // Called from FindBezierNearestPoint.
+
+ Float lower = 0.0f;
+ Float upper = 1.0f;
+ Float t;
+
+ Point P, lastP;
+ const size_t MAX_LOOP = 32;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ const Float DIFF = 0.0001f;
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ P = GetBezierPoint(aBezier, t);
+
+ // Check if it converged.
+ if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+
+ Float distSquare = (P - aTarget).LengthSquare();
+ if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ lower = t;
+ } else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ upper = t;
+ } else {
+ break;
+ }
+
+ lastP = P;
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+Point
+FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT)
+{
+ // Find a nearest point on bezier curve with Newton's method.
+ // It converges within 4 iterations in most cases.
+ //
+ // f(t_n)
+ // t_{n+1} = t_n - ---------
+ // f'(t_n)
+ //
+ // d 2
+ // f(t) = ---- | P(t) - aTarget |
+ // dt
+
+ Float t = aInitialT;
+ Point P;
+ Point lastP = GetBezierPoint(aBezier, t);
+
+ const size_t MAX_LOOP = 4;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ for (size_t i = 0; i <= MAX_LOOP; i++) {
+ Point dP = GetBezierDifferential(aBezier, t);
+ Point ddP = GetBezierDifferential2(aBezier, t);
+ Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
+ Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
+ aTarget.DotProduct(ddP));
+ t = t - f / df;
+ P = GetBezierPoint(aBezier, t);
+ if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+ lastP = P;
+
+ if (i == MAX_LOOP) {
+ // If aInitialT is too bad, it won't converge in a few iterations,
+ // fallback to binary search.
+ return BisectBezierNearestPoint(aBezier, aTarget, aT);
+ }
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+void
+GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint, const Size& aCornerSize)
+{
+ // Calculate bezier control points for elliptic arc.
+
+ const Float signsList[4][2] = {
+ { +1.0f, +1.0f },
+ { -1.0f, +1.0f },
+ { -1.0f, -1.0f },
+ { +1.0f, -1.0f }
+ };
+ const Float (& signs)[2] = signsList[aCorner];
+
+ aBezier->mPoints[0] = aCornerPoint;
+ aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
+
+ aBezier->mPoints[1] = aBezier->mPoints[0];
+ aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
+
+ aBezier->mPoints[3] = aCornerPoint;
+ aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
+
+ aBezier->mPoints[2] = aBezier->mPoints[3];
+ aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
+}
+
+Float
+GetQuarterEllipticArcLength(Float a, Float b)
+{
+ // Calculate the approximate length of a quarter elliptic arc formed by radii
+ // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // p = PI | (a + b) + ------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // = PI | (a + b) + -------------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * S |
+ // = PI | A + -------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * A + sqrt(4 * A - 3 * S ) _|
+ //
+ // where A = a + b, S = a - b
+
+ Float A = a + b, S = a - b;
+ Float A2 = A * A, S2 = S * S;
+ Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
+ return p / 4.0f;
+}
+
+Float
+CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin, Float width, Float height)
+{
+ // Solve following equations with n and return smaller n.
+ //
+ // / (x, y) = P + n * normal
+ // |
+ // < _ _ 2 _ _ 2
+ // | | x - origin.x | | y - origin.y |
+ // | | ------------ | + | ------------ | = 1
+ // \ |_ width _| |_ height _|
+
+ Float a = (P.x - origin.x) / width;
+ Float b = normal.x / width;
+ Float c = (P.y - origin.y) / height;
+ Float d = normal.y / height;
+
+ Float A = b * b + d * d;
+ Float B = a * b + c * d;
+ Float C = a * a + c * c - 1;
+
+ Float S = sqrt(B * B - A * C);
+
+ Float n1 = (- B + S) / A;
+ Float n2 = (- B - S) / A;
+
+ MOZ_ASSERT(n1 >= 0);
+ MOZ_ASSERT(n2 >= 0);
+
+ return n1 < n2 ? n1 : n2;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/BezierUtils.h b/system/graphics/2d/BezierUtils.h
new file mode 100644
index 000000000..7871f0284
--- /dev/null
+++ b/system/graphics/2d/BezierUtils.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BezierUtils_h_
+#define mozilla_BezierUtils_h_
+
+#include "mozilla/gfx/2D.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Control points for bezier curve
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __--
+// _--
+// /
+// /
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] +
+struct Bezier {
+ Point mPoints[4];
+};
+
+// Calculate a point or it's differential of a bezier curve formed by
+// aBezier and parameter t.
+//
+// GetBezierPoint = P(t)
+// GetBezierDifferential = P'(t)
+// GetBezierDifferential2 = P''(t)
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __-- P(1)
+// _--
+// +
+// / P(t)
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] + P(0)
+Point GetBezierPoint(const Bezier& aBezier, Float t);
+Point GetBezierDifferential(const Bezier& aBezier, Float t);
+Point GetBezierDifferential2(const Bezier& aBezier, Float t);
+
+// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
+Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
+
+// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
+// stores control points for [t1,t2] to aSubBezier.
+//
+// ___---+
+// __+- P(1)
+// _-- P(t2)
+// -
+// / <-- aSubBezier
+// |
+// |
+// +
+// | P(t1)
+// |
+// |
+// |
+// |
+// + P(0)
+void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
+ Float t1, Float t2);
+
+// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
+// aInitialT is a hint to find the parameter t for the nearest point.
+// If aT is non-null, parameter for the nearest point is stored to *aT.
+// This function expects a bezier curve to be an approximation of elliptic arc.
+// Otherwise it will return wrong point.
+//
+// aTarget
+// + ___---+
+// __--
+// _--
+// +
+// / nearest point = P(t = *aT)
+// |
+// |
+// |
+// + P(aInitialT)
+// |
+// |
+// |
+// |
+// +
+Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT=nullptr);
+
+// Calculate control points for a bezier curve that is an approximation of
+// an elliptic arc.
+//
+// aCornerSize.width
+// |<----------------->|
+// | |
+// aCornerPoint| mPoints[2] |
+// -------------+-------+-----___---+ mPoints[3]
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// aCornerSize.height | mPoints[1] + |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v mPoints[0] |
+// -------------+
+void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint,
+ const Size& aCornerSize);
+
+// Calculate the approximate length of a quarter elliptic arc formed by radii
+// (a, b).
+//
+// a
+// |<----------------->|
+// | |
+// ---+-------------___---+
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// b | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// ---+
+Float GetQuarterEllipticArcLength(Float a, Float b);
+
+// Calculate the distance between an elliptic arc formed by (origin, width,
+// height), and a point P, along a line formed by |P + n * normal|.
+// P should be outside of the ellipse, and the line should cross with the
+// ellipse twice at n > 0 points.
+//
+// width
+// |<----------------->|
+// origin | |
+// -----------+-------------___---+
+// ^ normal | __--
+// | P +->__ | _--
+// | --__ -
+// | | --+
+// height | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// -----------+
+Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin,
+ Float width, Float height);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* mozilla_BezierUtils_h_ */
diff --git a/system/graphics/2d/BigEndianInts.h b/system/graphics/2d/BigEndianInts.h
new file mode 100644
index 000000000..e14696e6e
--- /dev/null
+++ b/system/graphics/2d/BigEndianInts.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BigEndianInts_h
+#define mozilla_BigEndianInts_h
+
+#include "mozilla/EndianUtils.h"
+
+namespace mozilla {
+
+#pragma pack(push, 1)
+
+struct BigEndianUint16
+{
+#ifdef __SUNPRO_CC
+ BigEndianUint16& operator=(const uint16_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT BigEndianUint16(const uint16_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+
+ operator uint16_t() const
+ {
+ return NativeEndian::swapFromBigEndian(value);
+ }
+
+ friend inline bool
+ operator==(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+ {
+ return lhs.value == rhs.value;
+ }
+
+ friend inline bool
+ operator!=(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+private:
+ uint16_t value;
+};
+
+struct BigEndianUint32
+{
+#ifdef __SUNPRO_CC
+ BigEndianUint32& operator=(const uint32_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT BigEndianUint32(const uint32_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+
+ operator uint32_t() const
+ {
+ return NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ uint32_t value;
+};
+
+#pragma pack(pop)
+
+} // mozilla
+
+#endif // mozilla_BigEndianInts_h
diff --git a/system/graphics/2d/Blur.cpp b/system/graphics/2d/Blur.cpp
new file mode 100644
index 000000000..6a38b6666
--- /dev/null
+++ b/system/graphics/2d/Blur.cpp
@@ -0,0 +1,769 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Blur.h"
+
+#include <algorithm>
+#include <math.h>
+#include <string.h>
+
+#include "mozilla/CheckedInt.h"
+
+#include "2D.h"
+#include "DataSurfaceHelpers.h"
+#include "Tools.h"
+
+#ifdef BUILD_ARM_NEON
+#include "mozilla/arm.h"
+#endif
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Box blur involves looking at one pixel, and setting its value to the average
+ * of its neighbouring pixels.
+ * @param aInput The input buffer.
+ * @param aOutput The output buffer.
+ * @param aLeftLobe The number of pixels to blend on the left.
+ * @param aRightLobe The number of pixels to blend on the right.
+ * @param aWidth The number of columns in the buffers.
+ * @param aRows The number of rows in the buffers.
+ * @param aSkipRect An area to skip blurring in.
+ * XXX shouldn't we pass stride in separately here?
+ */
+static void
+BoxBlurHorizontal(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aWidth,
+ int32_t aRows,
+ const IntRect& aSkipRect)
+{
+ MOZ_ASSERT(aWidth > 0);
+
+ int32_t boxSize = aLeftLobe + aRightLobe + 1;
+ bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
+ aWidth <= aSkipRect.XMost();
+ if (boxSize == 1) {
+ memcpy(aOutput, aInput, aWidth*aRows);
+ return;
+ }
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ for (int32_t y = 0; y < aRows; y++) {
+ // Check whether the skip rect intersects this row. If the skip
+ // rect covers the whole surface in this row, we can avoid
+ // this row entirely (and any others along the skip rect).
+ bool inSkipRectY = y >= aSkipRect.y &&
+ y < aSkipRect.YMost();
+ if (inSkipRectY && skipRectCoversWholeRow) {
+ y = aSkipRect.YMost() - 1;
+ continue;
+ }
+
+ uint32_t alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = i - aLeftLobe;
+ // See assertion above; if aWidth is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aWidth - 1);
+ alphaSum += aInput[aWidth * y + pos];
+ }
+ for (int32_t x = 0; x < aWidth; x++) {
+ // Check whether we are within the skip rect. If so, go
+ // to the next point outside the skip rect.
+ if (inSkipRectY && x >= aSkipRect.x &&
+ x < aSkipRect.XMost()) {
+ x = aSkipRect.XMost();
+ if (x >= aWidth)
+ break;
+
+ // Recalculate the neighbouring alpha values for
+ // our new point on the surface.
+ alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = x + i - aLeftLobe;
+ // See assertion above; if aWidth is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aWidth - 1);
+ alphaSum += aInput[aWidth * y + pos];
+ }
+ }
+ int32_t tmp = x - aLeftLobe;
+ int32_t last = max(tmp, 0);
+ int32_t next = min(tmp + boxSize, aWidth - 1);
+
+ aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
+
+ alphaSum += aInput[aWidth * y + next] -
+ aInput[aWidth * y + last];
+ }
+ }
+}
+
+/**
+ * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
+ * left and right.
+ * XXX shouldn't we pass stride in separately here?
+ */
+static void
+BoxBlurVertical(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ int32_t aWidth,
+ int32_t aRows,
+ const IntRect& aSkipRect)
+{
+ MOZ_ASSERT(aRows > 0);
+
+ int32_t boxSize = aTopLobe + aBottomLobe + 1;
+ bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
+ aRows <= aSkipRect.YMost();
+ if (boxSize == 1) {
+ memcpy(aOutput, aInput, aWidth*aRows);
+ return;
+ }
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ for (int32_t x = 0; x < aWidth; x++) {
+ bool inSkipRectX = x >= aSkipRect.x &&
+ x < aSkipRect.XMost();
+ if (inSkipRectX && skipRectCoversWholeColumn) {
+ x = aSkipRect.XMost() - 1;
+ continue;
+ }
+
+ uint32_t alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = i - aTopLobe;
+ // See assertion above; if aRows is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aRows - 1);
+ alphaSum += aInput[aWidth * pos + x];
+ }
+ for (int32_t y = 0; y < aRows; y++) {
+ if (inSkipRectX && y >= aSkipRect.y &&
+ y < aSkipRect.YMost()) {
+ y = aSkipRect.YMost();
+ if (y >= aRows)
+ break;
+
+ alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = y + i - aTopLobe;
+ // See assertion above; if aRows is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aRows - 1);
+ alphaSum += aInput[aWidth * pos + x];
+ }
+ }
+ int32_t tmp = y - aTopLobe;
+ int32_t last = max(tmp, 0);
+ int32_t next = min(tmp + boxSize, aRows - 1);
+
+ aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
+
+ alphaSum += aInput[aWidth * next + x] -
+ aInput[aWidth * last + x];
+ }
+ }
+}
+
+static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
+{
+ int32_t major, minor, final;
+
+ /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
+ * some notes about approximating the Gaussian blur with box-blurs.
+ * The comments below are in the terminology of that page.
+ */
+ int32_t z = aRadius / 3;
+ switch (aRadius % 3) {
+ case 0:
+ // aRadius = z*3; choose d = 2*z + 1
+ major = minor = final = z;
+ break;
+ case 1:
+ // aRadius = z*3 + 1
+ // This is a tricky case since there is no value of d which will
+ // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
+ // for some integer k, then the radius will be 3*k. If d is even,
+ // i.e. d=2*k, then the radius will be 3*k - 1.
+ // So we have to choose values that don't match the standard
+ // algorithm.
+ major = z + 1;
+ minor = final = z;
+ break;
+ case 2:
+ // aRadius = z*3 + 2; choose d = 2*z + 2
+ major = final = z + 1;
+ minor = z;
+ break;
+ default:
+ // Mathematical impossibility!
+ MOZ_ASSERT(false);
+ major = minor = final = 0;
+ }
+ MOZ_ASSERT(major + minor + final == aRadius);
+
+ aLobes[0][0] = major;
+ aLobes[0][1] = minor;
+ aLobes[1][0] = minor;
+ aLobes[1][1] = major;
+ aLobes[2][0] = final;
+ aLobes[2][1] = final;
+}
+
+static void
+SpreadHorizontal(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aRadius,
+ int32_t aWidth,
+ int32_t aRows,
+ int32_t aStride,
+ const IntRect& aSkipRect)
+{
+ if (aRadius == 0) {
+ memcpy(aOutput, aInput, aStride * aRows);
+ return;
+ }
+
+ bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
+ aWidth <= aSkipRect.XMost();
+ for (int32_t y = 0; y < aRows; y++) {
+ // Check whether the skip rect intersects this row. If the skip
+ // rect covers the whole surface in this row, we can avoid
+ // this row entirely (and any others along the skip rect).
+ bool inSkipRectY = y >= aSkipRect.y &&
+ y < aSkipRect.YMost();
+ if (inSkipRectY && skipRectCoversWholeRow) {
+ y = aSkipRect.YMost() - 1;
+ continue;
+ }
+
+ for (int32_t x = 0; x < aWidth; x++) {
+ // Check whether we are within the skip rect. If so, go
+ // to the next point outside the skip rect.
+ if (inSkipRectY && x >= aSkipRect.x &&
+ x < aSkipRect.XMost()) {
+ x = aSkipRect.XMost();
+ if (x >= aWidth)
+ break;
+ }
+
+ int32_t sMin = max(x - aRadius, 0);
+ int32_t sMax = min(x + aRadius, aWidth - 1);
+ int32_t v = 0;
+ for (int32_t s = sMin; s <= sMax; ++s) {
+ v = max<int32_t>(v, aInput[aStride * y + s]);
+ }
+ aOutput[aStride * y + x] = v;
+ }
+ }
+}
+
+static void
+SpreadVertical(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aRadius,
+ int32_t aWidth,
+ int32_t aRows,
+ int32_t aStride,
+ const IntRect& aSkipRect)
+{
+ if (aRadius == 0) {
+ memcpy(aOutput, aInput, aStride * aRows);
+ return;
+ }
+
+ bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
+ aRows <= aSkipRect.YMost();
+ for (int32_t x = 0; x < aWidth; x++) {
+ bool inSkipRectX = x >= aSkipRect.x &&
+ x < aSkipRect.XMost();
+ if (inSkipRectX && skipRectCoversWholeColumn) {
+ x = aSkipRect.XMost() - 1;
+ continue;
+ }
+
+ for (int32_t y = 0; y < aRows; y++) {
+ // Check whether we are within the skip rect. If so, go
+ // to the next point outside the skip rect.
+ if (inSkipRectX && y >= aSkipRect.y &&
+ y < aSkipRect.YMost()) {
+ y = aSkipRect.YMost();
+ if (y >= aRows)
+ break;
+ }
+
+ int32_t sMin = max(y - aRadius, 0);
+ int32_t sMax = min(y + aRadius, aRows - 1);
+ int32_t v = 0;
+ for (int32_t s = sMin; s <= sMax; ++s) {
+ v = max<int32_t>(v, aInput[aStride * s + x]);
+ }
+ aOutput[aStride * y + x] = v;
+ }
+ }
+}
+
+CheckedInt<int32_t>
+AlphaBoxBlur::RoundUpToMultipleOf4(int32_t aVal)
+{
+ CheckedInt<int32_t> val(aVal);
+
+ val += 3;
+ val /= 4;
+ val *= 4;
+
+ return val;
+}
+
+AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
+ const IntSize& aSpreadRadius,
+ const IntSize& aBlurRadius,
+ const Rect* aDirtyRect,
+ const Rect* aSkipRect)
+ : mSpreadRadius(aSpreadRadius),
+ mBlurRadius(aBlurRadius),
+ mSurfaceAllocationSize(0)
+{
+ Rect rect(aRect);
+ rect.Inflate(Size(aBlurRadius + aSpreadRadius));
+ rect.RoundOut();
+
+ if (aDirtyRect) {
+ // If we get passed a dirty rect from layout, we can minimize the
+ // shadow size and make painting faster.
+ mHasDirtyRect = true;
+ mDirtyRect = *aDirtyRect;
+ Rect requiredBlurArea = mDirtyRect.Intersect(rect);
+ requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
+ rect = requiredBlurArea.Intersect(rect);
+ } else {
+ mHasDirtyRect = false;
+ }
+
+ mRect = IntRect(int32_t(rect.x), int32_t(rect.y),
+ int32_t(rect.width), int32_t(rect.height));
+ if (mRect.IsEmpty()) {
+ return;
+ }
+
+ if (aSkipRect) {
+ // If we get passed a skip rect, we can lower the amount of
+ // blurring/spreading we need to do. We convert it to IntRect to avoid
+ // expensive int<->float conversions if we were to use Rect instead.
+ Rect skipRect = *aSkipRect;
+ skipRect.RoundIn();
+ skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
+ mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y),
+ int32_t(skipRect.width), int32_t(skipRect.height));
+
+ mSkipRect = mSkipRect.Intersect(mRect);
+ if (mSkipRect.IsEqualInterior(mRect))
+ return;
+
+ mSkipRect -= mRect.TopLeft();
+ } else {
+ mSkipRect = IntRect(0, 0, 0, 0);
+ }
+
+ CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
+ if (stride.isValid()) {
+ mStride = stride.value();
+
+ // We need to leave room for an additional 3 bytes for a potential overrun
+ // in our blurring code.
+ size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3);
+ if (size != 0) {
+ mSurfaceAllocationSize = size;
+ }
+ }
+}
+
+AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
+ int32_t aStride,
+ float aSigmaX,
+ float aSigmaY)
+ : mRect(int32_t(aRect.x), int32_t(aRect.y),
+ int32_t(aRect.width), int32_t(aRect.height)),
+ mSpreadRadius(),
+ mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))),
+ mStride(aStride),
+ mSurfaceAllocationSize(0)
+{
+ IntRect intRect;
+ if (aRect.ToIntRect(&intRect)) {
+ size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
+ if (minDataSize != 0) {
+ mSurfaceAllocationSize = minDataSize;
+ }
+ }
+}
+
+
+AlphaBoxBlur::~AlphaBoxBlur()
+{
+}
+
+IntSize
+AlphaBoxBlur::GetSize()
+{
+ IntSize size(mRect.width, mRect.height);
+ return size;
+}
+
+int32_t
+AlphaBoxBlur::GetStride()
+{
+ return mStride;
+}
+
+IntRect
+AlphaBoxBlur::GetRect()
+{
+ return mRect;
+}
+
+Rect*
+AlphaBoxBlur::GetDirtyRect()
+{
+ if (mHasDirtyRect) {
+ return &mDirtyRect;
+ }
+
+ return nullptr;
+}
+
+size_t
+AlphaBoxBlur::GetSurfaceAllocationSize() const
+{
+ return mSurfaceAllocationSize;
+}
+
+void
+AlphaBoxBlur::Blur(uint8_t* aData)
+{
+ if (!aData) {
+ return;
+ }
+
+ // no need to do all this if not blurring or spreading
+ if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
+ int32_t stride = GetStride();
+
+ IntSize size = GetSize();
+
+ if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
+ // No need to use CheckedInt here - we have validated it in the constructor.
+ size_t szB = stride * size.height;
+ unsigned char* tmpData = new (std::nothrow) uint8_t[szB];
+
+ if (!tmpData) {
+ return;
+ }
+
+ memset(tmpData, 0, szB);
+
+ SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
+ SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
+
+ delete [] tmpData;
+ }
+
+ int32_t horizontalLobes[3][2];
+ ComputeLobes(mBlurRadius.width, horizontalLobes);
+ int32_t verticalLobes[3][2];
+ ComputeLobes(mBlurRadius.height, verticalLobes);
+
+ // We want to allow for some extra space on the left for alignment reasons.
+ int32_t maxLeftLobe = RoundUpToMultipleOf4(horizontalLobes[0][0] + 1).value();
+
+ IntSize integralImageSize(size.width + maxLeftLobe + horizontalLobes[1][1],
+ size.height + verticalLobes[0][0] + verticalLobes[1][1] + 1);
+
+ if ((integralImageSize.width * integralImageSize.height) > (1 << 24)) {
+ // Fallback to old blurring code when the surface is so large it may
+ // overflow our integral image!
+
+ // No need to use CheckedInt here - we have validated it in the constructor.
+ size_t szB = stride * size.height;
+ uint8_t* tmpData = new (std::nothrow) uint8_t[szB];
+ if (!tmpData) {
+ return;
+ }
+
+ memset(tmpData, 0, szB);
+
+ uint8_t* a = aData;
+ uint8_t* b = tmpData;
+ if (mBlurRadius.width > 0) {
+ BoxBlurHorizontal(a, b, horizontalLobes[0][0], horizontalLobes[0][1], stride, GetSize().height, mSkipRect);
+ BoxBlurHorizontal(b, a, horizontalLobes[1][0], horizontalLobes[1][1], stride, GetSize().height, mSkipRect);
+ BoxBlurHorizontal(a, b, horizontalLobes[2][0], horizontalLobes[2][1], stride, GetSize().height, mSkipRect);
+ } else {
+ a = tmpData;
+ b = aData;
+ }
+ // The result is in 'b' here.
+ if (mBlurRadius.height > 0) {
+ BoxBlurVertical(b, a, verticalLobes[0][0], verticalLobes[0][1], stride, GetSize().height, mSkipRect);
+ BoxBlurVertical(a, b, verticalLobes[1][0], verticalLobes[1][1], stride, GetSize().height, mSkipRect);
+ BoxBlurVertical(b, a, verticalLobes[2][0], verticalLobes[2][1], stride, GetSize().height, mSkipRect);
+ } else {
+ a = b;
+ }
+ // The result is in 'a' here.
+ if (a == tmpData) {
+ memcpy(aData, tmpData, szB);
+ }
+ delete [] tmpData;
+ } else {
+ size_t integralImageStride = GetAlignedStride<16>(integralImageSize.width, 4);
+ if (integralImageStride == 0) {
+ return;
+ }
+
+ // We need to leave room for an additional 12 bytes for a maximum overrun
+ // of 3 pixels in the blurring code.
+ size_t bufLen = BufferSizeFromStrideAndHeight(integralImageStride, integralImageSize.height, 12);
+ if (bufLen == 0) {
+ return;
+ }
+ // bufLen is a byte count, but here we want a multiple of 32-bit ints, so
+ // we divide by 4.
+ AlignedArray<uint32_t> integralImage((bufLen / 4) + ((bufLen % 4) ? 1 : 0));
+
+ if (!integralImage) {
+ return;
+ }
+
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+ } else
+#endif
+#ifdef BUILD_ARM_NEON
+ if (mozilla::supports_neon()) {
+ BoxBlur_NEON(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_NEON(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_NEON(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+ } else
+#endif
+ {
+#ifdef _MIPS_ARCH_LOONGSON3A
+ BoxBlur_LS3(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_LS3(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_LS3(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+#else
+ BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_C(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_C(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+#endif
+ }
+ }
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralRow(uint32_t *aDest, const uint8_t *aSource, uint32_t *aPreviousRow,
+ const uint32_t &aSourceWidth, const uint32_t &aLeftInflation, const uint32_t &aRightInflation)
+{
+ uint32_t currentRowSum = 0;
+ uint32_t pixel = aSource[0];
+ for (uint32_t x = 0; x < aLeftInflation; x++) {
+ currentRowSum += pixel;
+ *aDest++ = currentRowSum + *aPreviousRow++;
+ }
+ for (uint32_t x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x += 4) {
+ uint32_t alphaValues = *(uint32_t*)(aSource + (x - aLeftInflation));
+#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN || defined __BIG_ENDIAN__
+ currentRowSum += (alphaValues >> 24) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += (alphaValues >> 16) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += (alphaValues >> 8) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+#else
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+#endif
+ }
+ pixel = aSource[aSourceWidth - 1];
+ for (uint32_t x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += pixel;
+ *aDest++ = currentRowSum + *aPreviousRow++;
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_C(int32_t aLeftInflation, int32_t aRightInflation,
+ int32_t aTopInflation, int32_t aBottomInflation,
+ uint32_t *aIntegralImage, size_t aIntegralImageStride,
+ uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
+{
+ uint32_t stride32bit = aIntegralImageStride / 4;
+
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ memset(aIntegralImage, 0, aIntegralImageStride);
+
+ GenerateIntegralRow(aIntegralImage, aSource, aIntegralImage,
+ aSize.width, aLeftInflation, aRightInflation);
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource, aIntegralImage + (y - 1) * stride32bit,
+ aSize.width, aLeftInflation, aRightInflation);
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + aSourceStride * (y - aTopInflation),
+ aIntegralImage + (y - 1) * stride32bit, aSize.width, aLeftInflation, aRightInflation);
+ }
+
+ if (aBottomInflation) {
+ for (int y = (aSize.height + aTopInflation); y < integralImageSize.height; y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + ((aSize.height - 1) * aSourceStride),
+ aIntegralImage + (y - 1) * stride32bit,
+ aSize.width, aLeftInflation, aRightInflation);
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ uint32_t *aIntegralImage,
+ size_t aIntegralImageStride)
+{
+ IntSize size = GetSize();
+
+ MOZ_ASSERT(size.width > 0);
+
+ // Our 'left' or 'top' lobe will include the current pixel. i.e. when
+ // looking at an integral image the value of a pixel at 'x,y' is calculated
+ // using the value of the integral image values above/below that.
+ aLeftLobe++;
+ aTopLobe++;
+ int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
+
+ MOZ_ASSERT(boxSize > 0);
+
+ if (boxSize == 1) {
+ return;
+ }
+
+ int32_t stride32bit = aIntegralImageStride / 4;
+
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_C(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+
+ // Storing these locally makes this about 30% faster! Presumably the compiler
+ // can't be sure we're not altering the member variables in this loop.
+ IntRect skipRect = mSkipRect;
+ uint8_t *data = aData;
+ int32_t stride = mStride;
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * stride32bit - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * stride32bit + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * stride32bit + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * stride32bit - aLeftLobe);
+
+ for (int32_t x = 0; x < size.width; x++) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 1;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+ int32_t topLeft = topLeftBase[x];
+ int32_t topRight = topRightBase[x];
+ int32_t bottomRight = bottomRightBase[x];
+ int32_t bottomLeft = bottomLeftBase[x];
+
+ uint32_t value = bottomRight - topRight - bottomLeft;
+ value += topLeft;
+
+ data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32;
+ }
+ }
+}
+
+/**
+ * Compute the box blur size (which we're calling the blur radius) from
+ * the standard deviation.
+ *
+ * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
+ * approximating a Gaussian using box blurs. This yields quite a good
+ * approximation for a Gaussian. Then we multiply this by 1.5 since our
+ * code wants the radius of the entire triple-box-blur kernel instead of
+ * the diameter of an individual box blur. For more details, see:
+ * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
+ */
+static const Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt(2 * M_PI) / 4) * 1.5);
+
+IntSize
+AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
+{
+ IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
+ static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
+
+ return size;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Blur.h b/system/graphics/2d/Blur.h
new file mode 100644
index 000000000..6fffeb239
--- /dev/null
+++ b/system/graphics/2d/Blur.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BLUR_H_
+#define MOZILLA_GFX_BLUR_H_
+
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/CheckedInt.h"
+
+namespace mozilla {
+namespace gfx {
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4251 )
+#endif
+
+/**
+ * Implementation of a triple box blur approximation of a Gaussian blur.
+ *
+ * A Gaussian blur is good for blurring because, when done independently
+ * in the horizontal and vertical directions, it matches the result that
+ * would be obtained using a different (rotated) set of axes. A triple
+ * box blur is a very close approximation of a Gaussian.
+ *
+ * This is a "service" class; the constructors set up all the information
+ * based on the values and compute the minimum size for an 8-bit alpha
+ * channel context.
+ * The callers are responsible for creating and managing the backing surface
+ * and passing the pointer to the data to the Blur() method. This class does
+ * not retain the pointer to the data outside of the Blur() call.
+ *
+ * A spread N makes each output pixel the maximum value of all source
+ * pixels within a square of side length 2N+1 centered on the output pixel.
+ */
+class GFX2D_API AlphaBoxBlur
+{
+public:
+
+ /** Constructs a box blur and computes the backing surface size.
+ *
+ * @param aRect The coordinates of the surface to create in device units.
+ *
+ * @param aBlurRadius The blur radius in pixels. This is the radius of the
+ * entire (triple) kernel function. Each individual box blur has radius
+ * approximately 1/3 this value, or diameter approximately 2/3 this value.
+ * This parameter should nearly always be computed using CalculateBlurRadius,
+ * below.
+ *
+ * @param aDirtyRect A pointer to a dirty rect, measured in device units, if
+ * available. This will be used for optimizing the blur operation. It is
+ * safe to pass nullptr here.
+ *
+ * @param aSkipRect A pointer to a rect, measured in device units, that
+ * represents an area where blurring is unnecessary and shouldn't be done for
+ * speed reasons. It is safe to pass nullptr here.
+ */
+ AlphaBoxBlur(const Rect& aRect,
+ const IntSize& aSpreadRadius,
+ const IntSize& aBlurRadius,
+ const Rect* aDirtyRect,
+ const Rect* aSkipRect);
+
+ AlphaBoxBlur(const Rect& aRect,
+ int32_t aStride,
+ float aSigmaX,
+ float aSigmaY);
+
+ ~AlphaBoxBlur();
+
+ /**
+ * Return the size, in pixels, of the 8-bit alpha surface we'd use.
+ */
+ IntSize GetSize();
+
+ /**
+ * Return the stride, in bytes, of the 8-bit alpha surface we'd use.
+ */
+ int32_t GetStride();
+
+ /**
+ * Returns the device-space rectangle the 8-bit alpha surface covers.
+ */
+ IntRect GetRect();
+
+ /**
+ * Return a pointer to a dirty rect, as passed in to the constructor, or nullptr
+ * if none was passed in.
+ */
+ Rect* GetDirtyRect();
+
+ /**
+ * Return the minimum buffer size that should be given to Blur() method. If
+ * zero, the class is not properly setup for blurring. Note that this
+ * includes the extra three bytes on top of the stride*width, where something
+ * like gfxImageSurface::GetDataSize() would report without it, even if it
+ * happens to have the extra bytes.
+ */
+ size_t GetSurfaceAllocationSize() const;
+
+ /**
+ * Perform the blur in-place on the surface backed by specified 8-bit
+ * alpha surface data. The size must be at least that returned by
+ * GetSurfaceAllocationSize() or bad things will happen.
+ */
+ void Blur(uint8_t* aData);
+
+ /**
+ * Calculates a blur radius that, when used with box blur, approximates a
+ * Gaussian blur with the given standard deviation. The result of this
+ * function should be used as the aBlurRadius parameter to AlphaBoxBlur's
+ * constructor, above.
+ */
+ static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
+
+private:
+
+ void BoxBlur_C(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+ void BoxBlur_SSE2(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+#ifdef BUILD_ARM_NEON
+ void BoxBlur_NEON(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+#endif
+#ifdef _MIPS_ARCH_LOONGSON3A
+ void BoxBlur_LS3(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+#endif
+
+ static CheckedInt<int32_t> RoundUpToMultipleOf4(int32_t aVal);
+
+ /**
+ * A rect indicating the area where blurring is unnecessary, and the blur
+ * algorithm should skip over it.
+ */
+ IntRect mSkipRect;
+
+ /**
+ * The device-space rectangle the the backing 8-bit alpha surface covers.
+ */
+ IntRect mRect;
+
+ /**
+ * A copy of the dirty rect passed to the constructor. This will only be valid if
+ * mHasDirtyRect is true.
+ */
+ Rect mDirtyRect;
+
+ /**
+ * The spread radius, in pixels.
+ */
+ IntSize mSpreadRadius;
+
+ /**
+ * The blur radius, in pixels.
+ */
+ IntSize mBlurRadius;
+
+ /**
+ * The stride of the data passed to Blur()
+ */
+ int32_t mStride;
+
+ /**
+ * The minimum size of the buffer needed for the Blur() operation.
+ */
+ size_t mSurfaceAllocationSize;
+
+ /**
+ * Whether mDirtyRect contains valid data.
+ */
+ bool mHasDirtyRect;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BLUR_H_ */
diff --git a/system/graphics/2d/BlurLS3.cpp b/system/graphics/2d/BlurLS3.cpp
new file mode 100644
index 000000000..20c28b37e
--- /dev/null
+++ b/system/graphics/2d/BlurLS3.cpp
@@ -0,0 +1,588 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Blur.h"
+
+#include <string.h>
+
+#ifdef _MIPS_ARCH_LOONGSON3A
+
+#include "MMIHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+typedef struct { double l; double h; } __m128i;
+
+MOZ_ALWAYS_INLINE
+__m128i loadUnaligned128(__m128i *p)
+{
+ __m128i v;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[vh], 0xf(%[p]) \n\t"
+ "gsldrc1 %[vh], 0x8(%[p]) \n\t"
+ "gsldlc1 %[vl], 0x7(%[p]) \n\t"
+ "gsldrc1 %[vl], 0x0(%[p]) \n\t"
+ ".set pop \n\t"
+ :[vh]"=f"(v.h), [vl]"=f"(v.l)
+ :[p]"r"(p)
+ :"memory"
+ );
+
+ return v;
+}
+
+MOZ_ALWAYS_INLINE
+__m128i Divide(__m128i aValues, __m128i aDivisor)
+{
+ uint64_t tmp;
+ double srl32;
+ __m128i mask, ra, p4321, t1, t2;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 0x80000000 \n\t"
+ "mtc1 %[tmp], %[ral] \n\t"
+ "xor %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[rah], %[ral] \n\t"
+ "li %[tmp], 0xffffffff \n\t"
+ "mthc1 %[tmp], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ ".set pop \n\t"
+ :[rah]"=f"(ra.h), [ral]"=f"(ra.l),
+ [maskh]"=f"(mask.h), [maskl]"=f"(mask.l),
+ [tmp]"=&r"(tmp)
+ );
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, 32 \n\t"
+ "mtc1 %[tmp], %[srl32] \n\t"
+ _mm_pmuluw(t1, av, ad)
+ _mm_psrld(t2, av, srl32)
+ _mm_pmuluw(t2, t2, ad)
+ // Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
+ // result is rounded.
+ _mm_paddd(t1, t1, ra)
+ _mm_psrld(t1, t1, srl32)
+ _mm_paddd(t2, t2, ra)
+ _mm_and(t2, t2, mask)
+ _mm_or(p4321, t1, t2)
+ ".set pop \n\t"
+ :[p4321h]"=&f"(p4321.h), [p4321l]"=&f"(p4321.l),
+ [t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
+ [t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l),
+ [srl32]"=&f"(srl32), [tmp]"=&r"(tmp)
+ :[rah]"f"(ra.h), [ral]"f"(ra.l),
+ [maskh]"f"(mask.h), [maskl]"f"(mask.l),
+ [avh]"f"(aValues.h), [avl]"f"(aValues.l),
+ [adh]"f"(aDivisor.h), [adl]"f"(aDivisor.l)
+ );
+
+ return p4321;
+}
+
+MOZ_ALWAYS_INLINE
+__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
+ const __m128i& aBottomRight, const __m128i& aBottomLeft,
+ const __m128i& aDivisor)
+{
+ __m128i values;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_psubw(val, abr, atr)
+ _mm_psubw(val, val, abl)
+ _mm_paddw(val, val, atl)
+ ".set pop \n\t"
+ :[valh]"=&f"(values.h), [vall]"=&f"(values.l)
+ :[abrh]"f"(aBottomRight.h), [abrl]"f"(aBottomRight.l),
+ [atrh]"f"(aTopRight.h), [atrl]"f"(aTopRight.l),
+ [ablh]"f"(aBottomLeft.h), [abll]"f"(aBottomLeft.l),
+ [atlh]"f"(aTopLeft.h), [atll]"f"(aTopLeft.l)
+ );
+
+ return Divide(values, aDivisor);
+}
+
+MOZ_ALWAYS_INLINE
+void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
+ int32_t aSourceWidth, int32_t aLeftInflation,
+ int32_t aRightInflation)
+{
+ int32_t currentRowSum = 0;
+
+ for (int x = 0; x < aLeftInflation; x++) {
+ currentRowSum += aSource[0];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
+ currentRowSum += aSource[(x - aLeftInflation)];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += aSource[aSourceWidth - 1];
+ aDest[x] = currentRowSum;
+ }
+}
+
+// This function calculates an integral of four pixels stored in the 4
+// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
+// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
+// much testing.
+MOZ_ALWAYS_INLINE
+__m128i AccumulatePixelSums(__m128i aPixels)
+{
+ uint64_t tr;
+ double tmp, s4, s64;
+ __m128i sumPixels, currentPixels, zero;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(z, z, z)
+ "li %[tr], 64 \n\t"
+ "mtc1 %[tr], %[s64] \n\t"
+ "li %[tr], 32 \n\t"
+ "mtc1 %[tr], %[s4] \n\t"
+ _mm_psllq(cp, ap, s4, s64, t)
+ _mm_paddw(sp, ap, cp)
+ _mm_punpckldq(cp, z, sp)
+ _mm_paddw(sp, sp, cp)
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
+ [cph]"=&f"(currentPixels.h), [cpl]"=&f"(currentPixels.l),
+ [zh]"=&f"(zero.h), [zl]"=&f"(zero.l),
+ [s4]"=&f"(s4), [s64]"=&f"(s64), [t]"=&f"(tmp), [tr]"=&r"(tr)
+ :[aph]"f"(aPixels.h), [apl]"f"(aPixels.l)
+ );
+
+ return sumPixels;
+}
+
+MOZ_ALWAYS_INLINE
+void GenerateIntegralImage_LS3(int32_t aLeftInflation, int32_t aRightInflation,
+ int32_t aTopInflation, int32_t aBottomInflation,
+ uint32_t *aIntegralImage, size_t aIntegralImageStride,
+ uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
+{
+ MOZ_ASSERT(!(aLeftInflation & 3));
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intFirstRow = aIntegralImage;
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ __m128i firstRow, previousRow;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gslqc1 %[frh], %[frl], (%[fr]) \n\t"
+ "gslqc1 %[prh], %[prl], (%[pr]) \n\t"
+ _mm_paddw(fr, fr, pr)
+ "gssqc1 %[frh], %[frl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[frh]"=&f"(firstRow.h), [frl]"=&f"(firstRow.l),
+ [prh]"=&f"(previousRow.h), [prl]"=&f"(previousRow.l)
+ :[fr]"r"(intFirstRow + x), [pr]"r"(intPrevRow + x),
+ [r]"r"(intRow + x)
+ :"memory"
+ );
+ }
+ }
+
+ uint64_t tmp;
+ double s44, see;
+ __m128i zero;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 0xee \n\t"
+ "mtc1 %[tmp], %[see] \n\t"
+ "li %[tmp], 0x44 \n\t"
+ "mtc1 %[tmp], %[s44] \n\t"
+ _mm_xor(zero, zero, zero)
+ ".set pop \n\t"
+ :[tmp]"=&r"(tmp), [s44]"=f"(s44), [see]"=f"(see),
+ [zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
+ );
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ __m128i currentRowSum;
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
+ uint32_t pixel = sourceRow[0];
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(cr, cr, cr)
+ ".set pop \n\t"
+ :[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
+ );
+ for (int x = 0; x < aLeftInflation; x += 4) {
+ __m128i sumPixels, t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ "pshufh %[sph], %[spl], %[s44] \n\t"
+ "pshufh %[spl], %[spl], %[s44] \n\t"
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l)
+ :[pix]"r"(pixel), [s44]"f"(s44)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "pshufh %[crh], %[sph], %[see] \n\t"
+ "pshufh %[crl], %[sph], %[see] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
+ :"memory"
+ );
+ }
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
+ __m128i sumPixels, t;
+
+ // It's important to shuffle here. When we exit this loop currentRowSum
+ // has to be set to sumPixels, so that the following loop can get the
+ // correct pixel for the currentRowSum. The highest order pixel in
+ // currentRowSum could've originated from accumulation in the stride.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "pshufh %[crl], %[crh], %[see] \n\t"
+ "pshufh %[crh], %[crh], %[see] \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ _mm_punpcklbh(sp, sp, zero)
+ _mm_punpcklhw(sp, sp, zero)
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[pix]"r"(pixels), [see]"f"(see),
+ [zeroh]"f"(zero.h), [zerol]"f"(zero.l)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "mov.d %[crh], %[sph] \n\t"
+ "mov.d %[crl], %[spl] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x)
+ :"memory"
+ );
+ }
+
+ pixel = sourceRow[aSize.width - 1];
+ int x = (aSize.width + aLeftInflation);
+ if ((aSize.width & 3)) {
+ // Deal with unaligned portion. Get the correct pixel from currentRowSum,
+ // see explanation above.
+ uint32_t intCurrentRowSum = ((uint32_t*)&currentRowSum)[(aSize.width % 4) - 1];
+ for (; x < integralImageSize.width; x++) {
+ // We could be unaligned here!
+ if (!(x & 3)) {
+ // aligned!
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[cr], %[crl] \n\t"
+ "punpcklwd %[crl], %[crl], %[crl] \n\t"
+ "mov.d %[crh], %[crl] \n\t"
+ ".set pop \n\t"
+ :[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
+ :[cr]"r"(intCurrentRowSum)
+ );
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "pshufh %[crl], %[crh], %[see] \n\t"
+ "pshufh %[crh], %[crh], %[see] \n\t"
+ ".set pop \n\t"
+ :[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[see]"f"(see)
+ );
+ }
+ for (; x < integralImageSize.width; x += 4) {
+ __m128i sumPixels, t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ ".set pop \n\t"
+ :[sph]"=f"(sumPixels.h), [spl]"=f"(sumPixels.l)
+ :[pix]"r"(pixel)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "pshufh %[crh], %[sph], %[see] \n\t"
+ "pshufh %[crl], %[sph], %[see] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
+ :"memory"
+ );
+ }
+ }
+
+ if (aBottomInflation) {
+ // Store the last valid row of our source image in the last row of
+ // our integral image. This will be overwritten with the correct values
+ // in the upcoming loop.
+ LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
+ aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
+
+
+ for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
+ __m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
+ __m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
+ __m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ __m128i t1, t2;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gslqc1 %[t1h], %[t1l], (%[lr]) \n\t"
+ "gslqc1 %[t2h], %[t2l], (%[pr]) \n\t"
+ _mm_paddw(t1, t1, t2)
+ "gssqc1 %[t1h], %[t1l], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
+ [t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l)
+ :[r]"r"(intRow + (x / 4)),
+ [lr]"r"(intLastRow + (x / 4)),
+ [pr]"r"(intPrevRow + (x / 4))
+ :"memory"
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_LS3(uint8_t* aData,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ uint32_t *aIntegralImage,
+ size_t aIntegralImageStride)
+{
+ IntSize size = GetSize();
+
+ MOZ_ASSERT(size.height > 0);
+
+ // Our 'left' or 'top' lobe will include the current pixel. i.e. when
+ // looking at an integral image the value of a pixel at 'x,y' is calculated
+ // using the value of the integral image values above/below that.
+ aLeftLobe++;
+ aTopLobe++;
+ int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
+
+ MOZ_ASSERT(boxSize > 0);
+
+ if (boxSize == 1) {
+ return;
+ }
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_LS3(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ __m128i divisor, zero;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[rec], %[divl] \n\t"
+ "punpcklwd %[divl], %[divl], %[divl] \n\t"
+ "mov.d %[divh], %[divl] \n\t"
+ _mm_xor(zero, zero, zero)
+ ".set pop \n\t"
+ :[divh]"=f"(divisor.h), [divl]"=f"(divisor.l),
+ [zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
+ :[rec]"r"(reciprocal)
+ );
+
+ // This points to the start of the rectangle within the IntegralImage that overlaps
+ // the surface being blurred.
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+
+ IntRect skipRect = mSkipRect;
+ int32_t stride = mStride;
+ uint8_t *data = aData;
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+
+ int32_t x = 0;
+ // Process 16 pixels at a time for as long as possible.
+ for (; x <= size.width - 16; x += 16) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 16;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ __m128i topLeft;
+ __m128i topRight;
+ __m128i bottomRight;
+ __m128i bottomLeft;
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+ __m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
+ __m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
+ __m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
+ __m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ double t;
+ __m128i final;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_packsswh(r3, r3, r4, t)
+ _mm_packsswh(f, r1, r2, t)
+ _mm_packushb(f, f, r3, t)
+ "gssdlc1 %[fh], 0xf(%[d]) \n\t"
+ "gssdrc1 %[fh], 0x8(%[d]) \n\t"
+ "gssdlc1 %[fl], 0x7(%[d]) \n\t"
+ "gssdrc1 %[fl], 0x0(%[d]) \n\t"
+ ".set pop \n\t"
+ :[fh]"=&f"(final.h), [fl]"=&f"(final.l),
+ [r3h]"+f"(result3.h), [r3l]"+f"(result3.l),
+ [t]"=&f"(t)
+ :[r1h]"f"(result1.h), [r1l]"f"(result1.l),
+ [r2h]"f"(result2.h), [r2l]"f"(result2.l),
+ [r4h]"f"(result4.h), [r4l]"f"(result4.l),
+ [d]"r"(data + stride * y + x)
+ :"memory"
+ );
+ }
+
+ // Process the remaining pixels 4 bytes at a time.
+ for (; x < size.width; x += 4) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 4;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+ __m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ __m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ __m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ __m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+
+ __m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ double t;
+ __m128i final;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_packsswh(f, r, zero, t)
+ _mm_packushb(f, f, zero, t)
+ "swc1 %[fl], (%[d]) \n\t"
+ ".set pop \n\t"
+ :[fh]"=&f"(final.h), [fl]"=&f"(final.l),
+ [t]"=&f"(t)
+ :[d]"r"(data + stride * y + x),
+ [rh]"f"(result.h), [rl]"f"(result.l),
+ [zeroh]"f"(zero.h), [zerol]"f"(zero.l)
+ :"memory"
+ );
+ }
+ }
+
+}
+
+}
+}
+
+#endif /* _MIPS_ARCH_LOONGSON3A */
diff --git a/system/graphics/2d/BlurNEON.cpp b/system/graphics/2d/BlurNEON.cpp
new file mode 100644
index 000000000..978b3cdc0
--- /dev/null
+++ b/system/graphics/2d/BlurNEON.cpp
@@ -0,0 +1,288 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Blur.h"
+#include <arm_neon.h>
+
+namespace mozilla {
+namespace gfx {
+
+MOZ_ALWAYS_INLINE
+uint16x4_t Divide(uint32x4_t aValues, uint32x2_t aDivisor)
+{
+ uint64x2_t roundingAddition = vdupq_n_u64(int64_t(1) << 31);
+ uint64x2_t multiplied21 = vmull_u32(vget_low_u32(aValues), aDivisor);
+ uint64x2_t multiplied43 = vmull_u32(vget_high_u32(aValues), aDivisor);
+ return vqmovn_u32(vcombine_u32(vshrn_n_u64(vaddq_u64(multiplied21, roundingAddition), 32),
+ vshrn_n_u64(vaddq_u64(multiplied43, roundingAddition), 32)));
+}
+
+MOZ_ALWAYS_INLINE
+uint16x4_t BlurFourPixels(const uint32x4_t& aTopLeft, const uint32x4_t& aTopRight,
+ const uint32x4_t& aBottomRight, const uint32x4_t& aBottomLeft,
+ const uint32x2_t& aDivisor)
+{
+ uint32x4_t values = vaddq_u32(vsubq_u32(vsubq_u32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
+ return Divide(values, aDivisor);
+}
+
+MOZ_ALWAYS_INLINE
+void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
+ int32_t aSourceWidth, int32_t aLeftInflation,
+ int32_t aRightInflation)
+{
+ int32_t currentRowSum = 0;
+
+ for (int x = 0; x < aLeftInflation; x++) {
+ currentRowSum += aSource[0];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
+ currentRowSum += aSource[(x - aLeftInflation)];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += aSource[aSourceWidth - 1];
+ aDest[x] = currentRowSum;
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_NEON(int32_t aLeftInflation, int32_t aRightInflation,
+ int32_t aTopInflation, int32_t aBottomInflation,
+ uint32_t *aIntegralImage, size_t aIntegralImageStride,
+ uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
+{
+ MOZ_ASSERT(!(aLeftInflation & 3));
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intFirstRow = aIntegralImage;
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ uint32x4_t firstRow = vld1q_u32(intFirstRow + x);
+ uint32x4_t previousRow = vld1q_u32(intPrevRow + x);
+ vst1q_u32(intRow + x, vaddq_u32(firstRow, previousRow));
+ }
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ uint32x4_t currentRowSum = vdupq_n_u32(0);
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
+
+ uint32_t pixel = sourceRow[0];
+ for (int x = 0; x < aLeftInflation; x += 4) {
+ uint32_t temp[4];
+ temp[0] = pixel;
+ temp[1] = temp[0] + pixel;
+ temp[2] = temp[1] + pixel;
+ temp[3] = temp[2] + pixel;
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ // It's important to shuffle here. When we exit this loop currentRowSum
+ // has to be set to sumPixels, so that the following loop can get the
+ // correct pixel for the currentRowSum. The highest order pixel in
+ // currentRowSum could've originated from accumulation in the stride.
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
+
+ uint32_t temp[4];
+ temp[0] = *(sourceRow + (x - aLeftInflation));
+ temp[1] = temp[0] + *(sourceRow + (x - aLeftInflation) + 1);
+ temp[2] = temp[1] + *(sourceRow + (x - aLeftInflation) + 2);
+ temp[3] = temp[2] + *(sourceRow + (x - aLeftInflation) + 3);
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = sumPixels;
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+
+ pixel = sourceRow[aSize.width - 1];
+ int x = (aSize.width + aLeftInflation);
+ if ((aSize.width & 3)) {
+ // Deal with unaligned portion. Get the correct pixel from currentRowSum,
+ // see explanation above.
+ uint32_t intCurrentRowSum = ((uint32_t*)&currentRowSum)[(aSize.width % 4) - 1];
+ for (; x < integralImageSize.width; x++) {
+ // We could be unaligned here!
+ if (!(x & 3)) {
+ // aligned!
+ currentRowSum = vdupq_n_u32(intCurrentRowSum);
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
+ }
+
+ for (; x < integralImageSize.width; x += 4) {
+ uint32_t temp[4];
+ temp[0] = pixel;
+ temp[1] = temp[0] + pixel;
+ temp[2] = temp[1] + pixel;
+ temp[3] = temp[2] + pixel;
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+ }
+
+ if (aBottomInflation) {
+ // Store the last valid row of our source image in the last row of
+ // our integral image. This will be overwritten with the correct values
+ // in the upcoming loop.
+ LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
+ aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intLastRow = aIntegralImage + (integralImageSize.height - 1) * stride32bit;
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ vst1q_u32(intRow + x,
+ vaddq_u32(vld1q_u32(intLastRow + x),
+ vld1q_u32(intPrevRow + x)));
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_NEON(uint8_t* aData,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ uint32_t *aIntegralImage,
+ size_t aIntegralImageStride)
+{
+ IntSize size = GetSize();
+
+ MOZ_ASSERT(size.height > 0);
+
+ // Our 'left' or 'top' lobe will include the current pixel. i.e. when
+ // looking at an integral image the value of a pixel at 'x,y' is calculated
+ // using the value of the integral image values above/below that.
+ aLeftLobe++;
+ aTopLobe++;
+ int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
+
+ MOZ_ASSERT(boxSize > 0);
+
+ if (boxSize == 1) {
+ return;
+ }
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_NEON(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ uint32x2_t divisor = vdup_n_u32(reciprocal);
+
+ // This points to the start of the rectangle within the IntegralImage that overlaps
+ // the surface being blurred.
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+ IntRect skipRect = mSkipRect;
+ int32_t stride = mStride;
+ uint8_t *data = aData;
+
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+
+ int32_t x = 0;
+ // Process 16 pixels at a time for as long as possible.
+ for (; x <= size.width - 16; x += 16) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 16;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ uint32x4_t topLeft;
+ uint32x4_t topRight;
+ uint32x4_t bottomRight;
+ uint32x4_t bottomLeft;
+ topLeft = vld1q_u32(topLeftBase + x);
+ topRight = vld1q_u32(topRightBase + x);
+ bottomRight = vld1q_u32(bottomRightBase + x);
+ bottomLeft = vld1q_u32(bottomLeftBase + x);
+ uint16x4_t result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 4);
+ topRight = vld1q_u32(topRightBase + x + 4);
+ bottomRight = vld1q_u32(bottomRightBase + x + 4);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 4);
+ uint16x4_t result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 8);
+ topRight = vld1q_u32(topRightBase + x + 8);
+ bottomRight = vld1q_u32(bottomRightBase + x + 8);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 8);
+ uint16x4_t result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 12);
+ topRight = vld1q_u32(topRightBase + x + 12);
+ bottomRight = vld1q_u32(bottomRightBase + x + 12);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 12);
+ uint16x4_t result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ uint8x8_t combine1 = vqmovn_u16(vcombine_u16(result1, result2));
+ uint8x8_t combine2 = vqmovn_u16(vcombine_u16(result3, result4));
+ uint8x16_t final = vcombine_u8(combine1, combine2);
+ vst1q_u8(data + stride * y + x, final);
+ }
+
+ // Process the remaining pixels 4 bytes at a time.
+ for (; x < size.width; x += 4) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 4;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ uint32x4_t topLeft = vld1q_u32(topLeftBase + x);
+ uint32x4_t topRight = vld1q_u32(topRightBase + x);
+ uint32x4_t bottomRight = vld1q_u32(bottomRightBase + x);
+ uint32x4_t bottomLeft = vld1q_u32(bottomLeftBase + x);
+ uint16x4_t result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+ uint32x2_t final = vreinterpret_u32_u8(vmovn_u16(vcombine_u16(result, vdup_n_u16(0))));
+ *(uint32_t*)(data + stride * y + x) = vget_lane_u32(final, 0);
+ }
+ }
+}
+
+}
+}
+
diff --git a/system/graphics/2d/BlurSSE2.cpp b/system/graphics/2d/BlurSSE2.cpp
new file mode 100644
index 000000000..d652325b9
--- /dev/null
+++ b/system/graphics/2d/BlurSSE2.cpp
@@ -0,0 +1,315 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Blur.h"
+
+#include "SSEHelpers.h"
+
+#include <string.h>
+
+namespace mozilla {
+namespace gfx {
+
+MOZ_ALWAYS_INLINE
+__m128i Divide(__m128i aValues, __m128i aDivisor)
+{
+ const __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff);
+ static const union {
+ int64_t i64[2];
+ __m128i m;
+ } roundingAddition = { { int64_t(1) << 31, int64_t(1) << 31 } };
+
+ __m128i multiplied31 = _mm_mul_epu32(aValues, aDivisor);
+ __m128i multiplied42 = _mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor);
+
+ // Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
+ // result is rounded.
+ __m128i p_3_1 = _mm_srli_epi64(_mm_add_epi64(multiplied31, roundingAddition.m), 32);
+ __m128i p4_2_ = _mm_and_si128(_mm_add_epi64(multiplied42, roundingAddition.m), mask);
+ __m128i p4321 = _mm_or_si128(p_3_1, p4_2_);
+ return p4321;
+}
+
+MOZ_ALWAYS_INLINE
+__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
+ const __m128i& aBottomRight, const __m128i& aBottomLeft,
+ const __m128i& aDivisor)
+{
+ __m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
+ return Divide(values, aDivisor);
+}
+
+MOZ_ALWAYS_INLINE
+void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
+ int32_t aSourceWidth, int32_t aLeftInflation,
+ int32_t aRightInflation)
+{
+ int32_t currentRowSum = 0;
+
+ for (int x = 0; x < aLeftInflation; x++) {
+ currentRowSum += aSource[0];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
+ currentRowSum += aSource[(x - aLeftInflation)];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += aSource[aSourceWidth - 1];
+ aDest[x] = currentRowSum;
+ }
+}
+
+// This function calculates an integral of four pixels stored in the 4
+// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
+// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
+// much testing.
+MOZ_ALWAYS_INLINE
+__m128i AccumulatePixelSums(__m128i aPixels)
+{
+ __m128i sumPixels = aPixels;
+ __m128i currentPixels = _mm_slli_si128(aPixels, 4);
+ sumPixels = _mm_add_epi32(sumPixels, currentPixels);
+ currentPixels = _mm_unpacklo_epi64(_mm_setzero_si128(), sumPixels);
+
+ return _mm_add_epi32(sumPixels, currentPixels);
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation,
+ int32_t aTopInflation, int32_t aBottomInflation,
+ uint32_t *aIntegralImage, size_t aIntegralImageStride,
+ uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
+{
+ MOZ_ASSERT(!(aLeftInflation & 3));
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intFirstRow = aIntegralImage;
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ __m128i firstRow = _mm_load_si128((__m128i*)(intFirstRow + x));
+ __m128i previousRow = _mm_load_si128((__m128i*)(intPrevRow + x));
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(firstRow, previousRow));
+ }
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ __m128i currentRowSum = _mm_setzero_si128();
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
+
+ uint32_t pixel = sourceRow[0];
+ for (int x = 0; x < aLeftInflation; x += 4) {
+ __m128i sumPixels = AccumulatePixelSums(_mm_shuffle_epi32(_mm_set1_epi32(pixel), _MM_SHUFFLE(0, 0, 0, 0)));
+
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
+ }
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
+
+ // It's important to shuffle here. When we exit this loop currentRowSum
+ // has to be set to sumPixels, so that the following loop can get the
+ // correct pixel for the currentRowSum. The highest order pixel in
+ // currentRowSum could've originated from accumulation in the stride.
+ currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
+
+ __m128i sumPixels = AccumulatePixelSums(_mm_unpacklo_epi16(_mm_unpacklo_epi8( _mm_set1_epi32(pixels), _mm_setzero_si128()), _mm_setzero_si128()));
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = sumPixels;
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
+ }
+
+ pixel = sourceRow[aSize.width - 1];
+ int x = (aSize.width + aLeftInflation);
+ if ((aSize.width & 3)) {
+ // Deal with unaligned portion. Get the correct pixel from currentRowSum,
+ // see explanation above.
+ uint32_t intCurrentRowSum = ((uint32_t*)&currentRowSum)[(aSize.width % 4) - 1];
+ for (; x < integralImageSize.width; x++) {
+ // We could be unaligned here!
+ if (!(x & 3)) {
+ // aligned!
+ currentRowSum = _mm_set1_epi32(intCurrentRowSum);
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+ for (; x < integralImageSize.width; x += 4) {
+ __m128i sumPixels = AccumulatePixelSums(_mm_set1_epi32(pixel));
+
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
+ }
+ }
+
+ if (aBottomInflation) {
+ // Store the last valid row of our source image in the last row of
+ // our integral image. This will be overwritten with the correct values
+ // in the upcoming loop.
+ LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
+ aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
+
+
+ for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
+ __m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
+ __m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
+ __m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ _mm_store_si128(intRow + (x / 4),
+ _mm_add_epi32(_mm_load_si128(intLastRow + (x / 4)),
+ _mm_load_si128(intPrevRow + (x / 4))));
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ uint32_t *aIntegralImage,
+ size_t aIntegralImageStride)
+{
+ IntSize size = GetSize();
+
+ MOZ_ASSERT(size.height > 0);
+
+ // Our 'left' or 'top' lobe will include the current pixel. i.e. when
+ // looking at an integral image the value of a pixel at 'x,y' is calculated
+ // using the value of the integral image values above/below that.
+ aLeftLobe++;
+ aTopLobe++;
+ int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
+
+ MOZ_ASSERT(boxSize > 0);
+
+ if (boxSize == 1) {
+ return;
+ }
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_SSE2(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ __m128i divisor = _mm_set1_epi32(reciprocal);
+
+ // This points to the start of the rectangle within the IntegralImage that overlaps
+ // the surface being blurred.
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+
+ IntRect skipRect = mSkipRect;
+ int32_t stride = mStride;
+ uint8_t *data = aData;
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+
+ int32_t x = 0;
+ // Process 16 pixels at a time for as long as possible.
+ for (; x <= size.width - 16; x += 16) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 16;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ __m128i topLeft;
+ __m128i topRight;
+ __m128i bottomRight;
+ __m128i bottomLeft;
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+ __m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
+ __m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
+ __m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
+ __m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ __m128i final = _mm_packus_epi16(_mm_packs_epi32(result1, result2), _mm_packs_epi32(result3, result4));
+
+ _mm_storeu_si128((__m128i*)(data + stride * y + x), final);
+ }
+
+ // Process the remaining pixels 4 bytes at a time.
+ for (; x < size.width; x += 4) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 4;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+ __m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ __m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ __m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ __m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+
+ __m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+ __m128i final = _mm_packus_epi16(_mm_packs_epi32(result, _mm_setzero_si128()), _mm_setzero_si128());
+
+ *(uint32_t*)(data + stride * y + x) = _mm_cvtsi128_si32(final);
+ }
+ }
+
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/BorrowedContext.h b/system/graphics/2d/BorrowedContext.h
new file mode 100644
index 000000000..edb923b1e
--- /dev/null
+++ b/system/graphics/2d/BorrowedContext.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_BORROWED_CONTEXT_H
+#define _MOZILLA_GFX_BORROWED_CONTEXT_H
+
+#include "2D.h"
+
+#ifdef MOZ_X11
+#include <X11/extensions/Xrender.h>
+#include <X11/Xlib.h>
+#include "X11UndefineNone.h"
+#endif
+
+struct _cairo;
+typedef struct _cairo cairo_t;
+
+namespace mozilla {
+
+namespace gfx {
+
+/* This is a helper class that let's you borrow a cairo_t from a
+ * DrawTargetCairo. This is used for drawing themed widgets.
+ *
+ * Callers should check the cr member after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the context is borrowed. */
+class BorrowedCairoContext
+{
+public:
+ BorrowedCairoContext()
+ : mCairo(nullptr)
+ , mDT(nullptr)
+ { }
+
+ explicit BorrowedCairoContext(DrawTarget *aDT)
+ : mDT(aDT)
+ {
+ mCairo = BorrowCairoContextFromDrawTarget(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ cairo_t *Init(DrawTarget *aDT)
+ {
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ return mCairo = BorrowCairoContextFromDrawTarget(aDT);
+ }
+
+ // The caller needs to call Finish if cr is non-null when
+ // they are done with the context. This is currently explicit
+ // instead of happening implicitly in the destructor to make
+ // what's happening in the caller more clear. It also
+ // let's you resume using the DrawTarget in the same scope.
+ void Finish()
+ {
+ if (mCairo) {
+ ReturnCairoContextToDrawTarget(mDT, mCairo);
+ mCairo = nullptr;
+ }
+ }
+
+ ~BorrowedCairoContext() {
+ MOZ_ASSERT(!mCairo);
+ }
+
+ cairo_t *mCairo;
+private:
+ static cairo_t* BorrowCairoContextFromDrawTarget(DrawTarget *aDT);
+ static void ReturnCairoContextToDrawTarget(DrawTarget *aDT, cairo_t *aCairo);
+ DrawTarget *mDT;
+};
+
+#ifdef MOZ_X11
+/* This is a helper class that let's you borrow an Xlib drawable from
+ * a DrawTarget. This is used for drawing themed widgets.
+ *
+ * Callers should check the Xlib drawable after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the drawable is borrowed. */
+class BorrowedXlibDrawable
+{
+public:
+ BorrowedXlibDrawable()
+ : mDT(nullptr),
+ mDisplay(nullptr),
+ mDrawable(X11None),
+ mScreen(nullptr),
+ mVisual(nullptr),
+ mXRenderFormat(nullptr)
+ {}
+
+ explicit BorrowedXlibDrawable(DrawTarget *aDT)
+ : mDT(nullptr),
+ mDisplay(nullptr),
+ mDrawable(X11None),
+ mScreen(nullptr),
+ mVisual(nullptr),
+ mXRenderFormat(nullptr)
+ {
+ Init(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ bool Init(DrawTarget *aDT);
+
+ // The caller needs to call Finish if drawable is non-zero when
+ // they are done with the context. This is currently explicit
+ // instead of happening implicitly in the destructor to make
+ // what's happening in the caller more clear. It also
+ // let's you resume using the DrawTarget in the same scope.
+ void Finish();
+
+ ~BorrowedXlibDrawable() {
+ MOZ_ASSERT(!mDrawable);
+ }
+
+ Display *GetDisplay() const { return mDisplay; }
+ Drawable GetDrawable() const { return mDrawable; }
+ Screen *GetScreen() const { return mScreen; }
+ Visual *GetVisual() const { return mVisual; }
+ IntSize GetSize() const { return mSize; }
+ Point GetOffset() const { return mOffset; }
+
+ XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; }
+
+private:
+ DrawTarget *mDT;
+ Display *mDisplay;
+ Drawable mDrawable;
+ Screen *mScreen;
+ Visual *mVisual;
+ XRenderPictFormat *mXRenderFormat;
+ IntSize mSize;
+ Point mOffset;
+};
+#endif
+
+#ifdef XP_DARWIN
+/* This is a helper class that let's you borrow a CGContextRef from a
+ * DrawTargetCG. This is used for drawing themed widgets.
+ *
+ * Callers should check the cg member after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the context is borrowed. */
+class BorrowedCGContext
+{
+public:
+ BorrowedCGContext()
+ : cg(nullptr)
+ , mDT(nullptr)
+ { }
+
+ explicit BorrowedCGContext(DrawTarget *aDT)
+ : mDT(aDT)
+ {
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ cg = BorrowCGContextFromDrawTarget(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ CGContextRef Init(DrawTarget *aDT)
+ {
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ cg = BorrowCGContextFromDrawTarget(aDT);
+ return cg;
+ }
+
+ // The caller needs to call Finish if cg is non-null when
+ // they are done with the context. This is currently explicit
+ // instead of happening implicitly in the destructor to make
+ // what's happening in the caller more clear. It also
+ // let's you resume using the DrawTarget in the same scope.
+ void Finish()
+ {
+ if (cg) {
+ ReturnCGContextToDrawTarget(mDT, cg);
+ cg = nullptr;
+ }
+ }
+
+ ~BorrowedCGContext() {
+ MOZ_ASSERT(!cg);
+ }
+
+ CGContextRef cg;
+private:
+#ifdef USE_SKIA
+ static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT);
+ static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg);
+#else
+ static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT) {
+ MOZ_CRASH("Not supported without Skia");
+ }
+
+ static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg) {
+ MOZ_CRASH("not supported without Skia");
+ }
+#endif
+ DrawTarget *mDT;
+};
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_BORROWED_CONTEXT_H
diff --git a/system/graphics/2d/CGTextDrawing.h b/system/graphics/2d/CGTextDrawing.h
new file mode 100644
index 000000000..b9e3b374a
--- /dev/null
+++ b/system/graphics/2d/CGTextDrawing.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
+#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
+
+#include <ApplicationServices/ApplicationServices.h>
+#include "nsDebug.h"
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "PathCG.h"
+#include <dlfcn.h>
+
+// This is used when we explicitly need CG to draw text to support things such
+// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
+// are really only to enable Skia to support drawing text in those situations.
+
+namespace mozilla {
+namespace gfx {
+
+typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
+
+static CGContextSetFontSmoothingBackgroundColorFunc
+GetCGContextSetFontSmoothingBackgroundColorFunc()
+{
+ static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
+ static bool lookedUpFunc = false;
+ if (!lookedUpFunc) {
+ func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
+ RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
+ lookedUpFunc = true;
+ }
+ return func;
+}
+
+static CGColorRef
+ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
+{
+ CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+ return CGColorCreate(aColorSpace, components);
+}
+
+static bool
+SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
+ const GlyphRenderingOptions* aRenderingOptions)
+{
+ if (aRenderingOptions) {
+ Color fontSmoothingBackgroundColor =
+ static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+ if (fontSmoothingBackgroundColor.a > 0) {
+ CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
+ GetCGContextSetFontSmoothingBackgroundColorFunc();
+ if (setFontSmoothingBGColorFunc) {
+ CGColorRef color = ColorToCGColor(aColorSpace, fontSmoothingBackgroundColor);
+ setFontSmoothingBGColorFunc(aCGContext, color);
+ CGColorRelease(color);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// Font rendering with a non-transparent font smoothing background color
+// can leave pixels in our buffer where the rgb components exceed the alpha
+// component. When this happens we need to clean up the data afterwards.
+// The purpose of this is probably the following: Correct compositing of
+// subpixel anti-aliased fonts on transparent backgrounds requires
+// different alpha values per RGB component. Usually, premultiplied color
+// values are derived by multiplying all components with the same per-pixel
+// alpha value. However, if you multiply each component with a *different*
+// alpha, and set the alpha component of the pixel to, say, the average
+// of the alpha values that you used during the premultiplication of the
+// RGB components, you can trick OVER compositing into doing a simplified
+// form of component alpha compositing. (You just need to make sure to
+// clamp the components of the result pixel to [0,255] afterwards.)
+static void
+EnsureValidPremultipliedData(CGContextRef aContext,
+ CGRect aTextBounds = CGRectInfinite)
+{
+ if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
+ CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
+ return;
+ }
+
+ uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
+ CGRect bitmapBounds = CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
+ int stride = CGBitmapContextGetBytesPerRow(aContext);
+
+ CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
+ int startX = bounds.origin.x;
+ int endX = startX + bounds.size.width;
+ MOZ_ASSERT(endX <= bitmapBounds.size.width);
+
+
+ // CGRect assume that our origin is the bottom left.
+ // The data assumes that the origin is the top left.
+ // Have to switch the Y axis so that our coordinates are correct
+ int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
+ int endY = startY + bounds.size.height;
+ MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));
+
+ for (int y = startY; y < endY; y++) {
+ for (int x = startX; x < endX; x++) {
+ int i = y * stride + x * 4;
+ uint8_t a = bitmapData[i + 3];
+
+ bitmapData[i + 0] = std::min(a, bitmapData[i+0]);
+ bitmapData[i + 1] = std::min(a, bitmapData[i+1]);
+ bitmapData[i + 2] = std::min(a, bitmapData[i+2]);
+ }
+ }
+}
+
+static CGRect
+ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
+{
+ CGFloat x1, x2, y1, y2;
+ if (count < 1)
+ return CGRectZero;
+
+ x1 = bboxes[0].origin.x + positions[0].x;
+ x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
+ y1 = bboxes[0].origin.y + positions[0].y;
+ y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
+
+ // accumulate max and minimum coordinates
+ for (int i = 1; i < count; i++) {
+ x1 = std::min(x1, bboxes[i].origin.x + positions[i].x);
+ y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
+ x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
+ y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
+ }
+
+ CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
+ return extents;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/Coord.h b/system/graphics/2d/Coord.h
new file mode 100644
index 000000000..0d2cdd6a6
--- /dev/null
+++ b/system/graphics/2d/Coord.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COORD_H_
+#define MOZILLA_GFX_COORD_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/TypeTraits.h" // For IsSame
+#include "Types.h"
+#include "BaseCoord.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+template <class units> struct IntCoordTyped;
+template <class units, class F = Float> struct CoordTyped;
+
+// CommonType<coord, primitive> is a metafunction that returns the type of the
+// result of an arithmetic operation on the underlying type of a strongly-typed
+// coordinate type 'coord', and a primitive type 'primitive'. C++ rules for
+// arithmetic conversions are designed to avoid losing information - for
+// example, the result of adding an int and a float is a float - and we want
+// the same behaviour when mixing our coordinate types with primitive types.
+// We get C++ to compute the desired result type using 'decltype'.
+
+template <class coord, class primitive>
+struct CommonType;
+
+template <class units, class primitive>
+struct CommonType<IntCoordTyped<units>, primitive> {
+ typedef decltype(int32_t() + primitive()) type;
+};
+
+template <class units, class F, class primitive>
+struct CommonType<CoordTyped<units, F>, primitive> {
+ typedef decltype(F() + primitive()) type;
+};
+
+// This is a base class that provides mixed-type operator overloads between
+// a strongly-typed Coord and a primitive value. It is needed to avoid
+// ambiguities at mixed-type call sites, because Coord classes are implicitly
+// convertible to their underlying value type. As we transition more of our code
+// to strongly-typed classes, we may be able to remove some or all of these
+// overloads.
+
+template <bool B, class coord, class primitive>
+struct CoordOperatorsHelper {
+ // Using SFINAE (Substitution Failure Is Not An Error) to suppress redundant
+ // operators
+};
+
+template <class coord, class primitive>
+struct CoordOperatorsHelper<true, coord, primitive> {
+ friend bool operator==(coord aA, primitive aB) {
+ return aA.value == aB;
+ }
+ friend bool operator==(primitive aA, coord aB) {
+ return aA == aB.value;
+ }
+ friend bool operator!=(coord aA, primitive aB) {
+ return aA.value != aB;
+ }
+ friend bool operator!=(primitive aA, coord aB) {
+ return aA != aB.value;
+ }
+
+ typedef typename CommonType<coord, primitive>::type result_type;
+
+ friend result_type operator+(coord aA, primitive aB) {
+ return aA.value + aB;
+ }
+ friend result_type operator+(primitive aA, coord aB) {
+ return aA + aB.value;
+ }
+ friend result_type operator-(coord aA, primitive aB) {
+ return aA.value - aB;
+ }
+ friend result_type operator-(primitive aA, coord aB) {
+ return aA - aB.value;
+ }
+ friend result_type operator*(coord aCoord, primitive aScale) {
+ return aCoord.value * aScale;
+ }
+ friend result_type operator*(primitive aScale, coord aCoord) {
+ return aScale * aCoord.value;
+ }
+ friend result_type operator/(coord aCoord, primitive aScale) {
+ return aCoord.value / aScale;
+ }
+ // 'scale / coord' is intentionally omitted because it doesn't make sense.
+};
+
+// Note: 'IntCoordTyped<units>' and 'CoordTyped<units>' do not derive from
+// 'units' to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61959.
+
+template<class units>
+struct IntCoordTyped :
+ public BaseCoord< int32_t, IntCoordTyped<units> >,
+ public CoordOperatorsHelper< true, IntCoordTyped<units>, float >,
+ public CoordOperatorsHelper< true, IntCoordTyped<units>, double > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< int32_t, IntCoordTyped<units> > Super;
+
+ constexpr IntCoordTyped() : Super() {}
+ constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {}
+};
+
+template<class units, class F>
+struct CoordTyped :
+ public BaseCoord< F, CoordTyped<units, F> >,
+ public CoordOperatorsHelper< !IsSame<F, int32_t>::value, CoordTyped<units, F>, int32_t >,
+ public CoordOperatorsHelper< !IsSame<F, uint32_t>::value, CoordTyped<units, F>, uint32_t >,
+ public CoordOperatorsHelper< !IsSame<F, double>::value, CoordTyped<units, F>, double >,
+ public CoordOperatorsHelper< !IsSame<F, float>::value, CoordTyped<units, F>, float > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< F, CoordTyped<units, F> > Super;
+
+ constexpr CoordTyped() : Super() {}
+ constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {}
+ explicit constexpr CoordTyped(const IntCoordTyped<units>& aCoord) : Super(F(aCoord.value)) {}
+
+ void Round() {
+ this->value = floor(this->value + 0.5);
+ }
+ void Truncate() {
+ this->value = int32_t(this->value);
+ }
+
+ IntCoordTyped<units> Rounded() const {
+ return IntCoordTyped<units>(int32_t(floor(this->value + 0.5)));
+ }
+ IntCoordTyped<units> Truncated() const {
+ return IntCoordTyped<units>(int32_t(this->value));
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COORD_H_ */
diff --git a/system/graphics/2d/CriticalSection.h b/system/graphics/2d/CriticalSection.h
new file mode 100644
index 000000000..d1eb69abc
--- /dev/null
+++ b/system/graphics/2d/CriticalSection.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_CRITICALSECTION_H_
+#define MOZILLA_GFX_CRITICALSECTION_H_
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#include "mozilla/DebugOnly.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+#ifdef WIN32
+
+class CriticalSection {
+public:
+ CriticalSection() { ::InitializeCriticalSection(&mCriticalSection); }
+
+ ~CriticalSection() { ::DeleteCriticalSection(&mCriticalSection); }
+
+ void Enter() { ::EnterCriticalSection(&mCriticalSection); }
+
+ void Leave() { ::LeaveCriticalSection(&mCriticalSection); }
+
+protected:
+ CRITICAL_SECTION mCriticalSection;
+};
+
+#else
+// posix
+
+class PosixCondvar;
+class CriticalSection {
+public:
+ CriticalSection() {
+ DebugOnly<int> err = pthread_mutex_init(&mMutex, nullptr);
+ MOZ_ASSERT(!err);
+ }
+
+ ~CriticalSection() {
+ DebugOnly<int> err = pthread_mutex_destroy(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Enter() {
+ DebugOnly<int> err = pthread_mutex_lock(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Leave() {
+ DebugOnly<int> err = pthread_mutex_unlock(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+protected:
+ pthread_mutex_t mMutex;
+ friend class PosixCondVar;
+};
+
+#endif
+
+/// RAII helper.
+struct CriticalSectionAutoEnter {
+ explicit CriticalSectionAutoEnter(CriticalSection* aSection) : mSection(aSection) { mSection->Enter(); }
+ ~CriticalSectionAutoEnter() { mSection->Leave(); }
+protected:
+ CriticalSection* mSection;
+};
+
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/2d/DataSourceSurface.cpp b/system/graphics/2d/DataSourceSurface.cpp
new file mode 100644
index 000000000..75d843506
--- /dev/null
+++ b/system/graphics/2d/DataSourceSurface.cpp
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include "DataSourceSurfaceWrapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<DataSourceSurface>
+DataSourceSurface::GetDataSurface()
+{
+ RefPtr<DataSourceSurface> surface =
+ (GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this);
+ return surface.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DataSourceSurfaceWrapper.h b/system/graphics/2d/DataSourceSurfaceWrapper.h
new file mode 100644
index 000000000..d1112b57c
--- /dev/null
+++ b/system/graphics/2d/DataSourceSurfaceWrapper.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_
+#define MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Wraps a DataSourceSurface and forwards all methods except for GetType(),
+// from which it always returns SurfaceType::DATA.
+class DataSourceSurfaceWrapper : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceWrapper, override)
+ explicit DataSourceSurfaceWrapper(DataSourceSurface *aSurface)
+ : mSurface(aSurface)
+ {}
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+
+ virtual uint8_t *GetData() override { return mSurface->GetData(); }
+ virtual int32_t Stride() override { return mSurface->Stride(); }
+ virtual IntSize GetSize() const override { return mSurface->GetSize(); }
+ virtual SurfaceFormat GetFormat() const override { return mSurface->GetFormat(); }
+ virtual bool IsValid() const override { return mSurface->IsValid(); }
+
+private:
+ RefPtr<DataSourceSurface> mSurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ */
diff --git a/system/graphics/2d/DataSurfaceHelpers.cpp b/system/graphics/2d/DataSurfaceHelpers.cpp
new file mode 100644
index 000000000..99dfe063a
--- /dev/null
+++ b/system/graphics/2d/DataSurfaceHelpers.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+
+#include "2D.h"
+#include "DataSurfaceHelpers.h"
+#include "Logging.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/PodOperations.h"
+#include "Tools.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceFromData(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ const uint8_t* aData,
+ int32_t aDataStride)
+{
+ RefPtr<DataSourceSurface> srcSurface =
+ Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
+ aDataStride,
+ aSize,
+ aFormat);
+ RefPtr<DataSourceSurface> destSurface =
+ Factory::CreateDataSourceSurface(aSize, aFormat, false);
+
+ if (!srcSurface || !destSurface) {
+ return nullptr;
+ }
+
+ if (CopyRect(srcSurface,
+ destSurface,
+ IntRect(IntPoint(), srcSurface->GetSize()),
+ IntPoint())) {
+ return destSurface.forget();
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ const uint8_t* aData,
+ int32_t aDataStride)
+{
+ RefPtr<DataSourceSurface> srcSurface =
+ Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
+ aDataStride,
+ aSize,
+ aFormat);
+ RefPtr<DataSourceSurface> destSurface =
+ Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, aStride, false);
+
+ if (!srcSurface || !destSurface) {
+ return nullptr;
+ }
+
+ if (CopyRect(srcSurface,
+ destSurface,
+ IntRect(IntPoint(), srcSurface->GetSize()),
+ IntPoint())) {
+ return destSurface.forget();
+ }
+
+ return nullptr;
+}
+
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface,
+ const DataSourceSurface::MappedSurface* aMap,
+ IntPoint aPoint)
+{
+ if (!SurfaceContainsPoint(aSurface, aPoint)) {
+ MOZ_CRASH("GFX: sample position needs to be inside surface!");
+ }
+
+ MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
+ "surface size overflows - this should have been prevented when the surface was created");
+
+ uint8_t* data = aMap->mData + aPoint.y * aMap->mStride +
+ aPoint.x * BytesPerPixel(aSurface->GetFormat());
+
+ if (data < aMap->mData) {
+ MOZ_CRASH("GFX: out-of-range data access");
+ }
+
+ return data;
+}
+
+// This check is safe against integer overflow.
+bool
+SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
+{
+ IntSize size = aSurface->GetSize();
+ return aPoint.x >= 0 && aPoint.x < size.width &&
+ aPoint.y >= 0 && aPoint.y < size.height;
+}
+
+void
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride)
+{
+ int height = aSize.height, width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+#ifdef IS_BIG_ENDIAN
+ aData[column] = 0xFF;
+#else
+ aData[column + 3] = 0xFF;
+#endif
+ }
+ aData += aStride;
+ }
+}
+
+void
+CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
+ int32_t aSrcStride, int32_t aBytesPerPixel)
+{
+ MOZ_ASSERT(aBytesPerPixel > 0,
+ "Negative stride for aDst not currently supported");
+ MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0,
+ "How did we end up with a surface with such a big buffer?");
+
+ int packedStride = aSrcSize.width * aBytesPerPixel;
+
+ if (aSrcStride == packedStride) {
+ // aSrc is already packed, so we can copy with a single memcpy.
+ memcpy(aDst, aSrc, packedStride * aSrcSize.height);
+ } else {
+ // memcpy one row at a time.
+ for (int row = 0; row < aSrcSize.height; ++row) {
+ memcpy(aDst, aSrc, packedStride);
+ aSrc += aSrcStride;
+ aDst += packedStride;
+ }
+ }
+}
+
+void
+CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst,
+ IntSize aSrcSize, int32_t aSrcStride)
+{
+ int packedStride = aSrcSize.width * 3;
+
+ uint8_t* srcPx = aSrc;
+ uint8_t* dstPx = aDst;
+
+ for (int row = 0; row < aSrcSize.height; ++row) {
+ for (int col = 0; col < aSrcSize.width; ++col) {
+ dstPx[0] = srcPx[0];
+ dstPx[1] = srcPx[1];
+ dstPx[2] = srcPx[2];
+ // srcPx[3] (unused or alpha component) dropped on floor
+ srcPx += 4;
+ dstPx += 3;
+ }
+ srcPx = aSrc += aSrcStride;
+ dstPx = aDst += packedStride;
+ }
+}
+
+UniquePtr<uint8_t[]>
+SurfaceToPackedBGRA(DataSourceSurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetFormat();
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ IntSize size = aSurface->GetSize();
+
+ UniquePtr<uint8_t[]> imageBuffer(
+ new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]);
+ if (!imageBuffer) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ return nullptr;
+ }
+
+ CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size,
+ map.mStride, 4 * sizeof(uint8_t));
+
+ aSurface->Unmap();
+
+ if (format == SurfaceFormat::B8G8R8X8) {
+ // Convert BGRX to BGRA by setting a to 255.
+ ConvertBGRXToBGRA(imageBuffer.get(), size, size.width * sizeof(uint32_t));
+ }
+
+ return imageBuffer;
+}
+
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetFormat();
+ MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
+
+ if (format != SurfaceFormat::B8G8R8X8) {
+ // To support B8G8R8A8 we'd need to un-pre-multiply alpha
+ return nullptr;
+ }
+
+ IntSize size = aSurface->GetSize();
+
+ uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)];
+ if (!imageBuffer) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ delete [] imageBuffer;
+ return nullptr;
+ }
+
+ CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size,
+ map.mStride);
+
+ aSurface->Unmap();
+
+ return imageBuffer;
+}
+
+void
+ClearDataSourceSurface(DataSourceSurface *aSurface)
+{
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
+ MOZ_ASSERT(false, "Failed to map DataSourceSurface");
+ return;
+ }
+
+ // We avoid writing into the gaps between the rows here since we can't be
+ // sure that some drivers don't use those bytes.
+
+ uint32_t width = aSurface->GetSize().width;
+ uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat());
+ uint8_t* row = map.mData;
+ // converting to size_t here because otherwise the temporaries can overflow
+ // and we can end up with |end| being a bad address!
+ uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height);
+
+ while (row != end) {
+ memset(row, 0, bytesPerRow);
+ row += map.mStride;
+ }
+
+ aSurface->Unmap();
+}
+
+size_t
+BufferSizeFromStrideAndHeight(int32_t aStride,
+ int32_t aHeight,
+ int32_t aExtraBytes)
+{
+ if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) {
+ return 0;
+ }
+
+ // We limit the length returned to values that can be represented by int32_t
+ // because we don't want to allocate buffers any bigger than that. This
+ // allows for a buffer size of over 2 GiB which is already rediculously
+ // large and will make the process janky. (Note the choice of the signed type
+ // is deliberate because we specifically don't want the returned value to
+ // overflow if someone stores the buffer length in an int32_t variable.)
+
+ CheckedInt32 requiredBytes =
+ CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes);
+ if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
+ gfxWarning() << "Buffer size too big; returning zero " << aStride << ", " << aHeight << ", " << aExtraBytes;
+ return 0;
+ }
+ return requiredBytes.value();
+}
+
+size_t
+BufferSizeFromDimensions(int32_t aWidth,
+ int32_t aHeight,
+ int32_t aDepth,
+ int32_t aExtraBytes)
+{
+ if (MOZ_UNLIKELY(aHeight <= 0) ||
+ MOZ_UNLIKELY(aWidth <= 0) ||
+ MOZ_UNLIKELY(aDepth <= 0)) {
+ return 0;
+ }
+
+ // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
+
+ CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes);
+ if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
+ gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes;
+ return 0;
+ }
+ return requiredBytes.value();
+}
+
+/**
+ * aSrcRect: Rect relative to the aSrc surface
+ * aDestPoint: Point inside aDest surface
+ */
+bool
+CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
+ IntRect aSrcRect, IntPoint aDestPoint)
+{
+ if (aSrcRect.Overflows() ||
+ IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
+ MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
+ }
+
+ MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
+ "GFX: different surface formats");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
+ "GFX: source rect too big for source surface");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
+ "GFX: dest surface too small");
+
+ if (aSrcRect.IsEmpty()) {
+ return false;
+ }
+
+ DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) {
+ return false;
+ }
+
+ uint8_t* sourceData = DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft());
+ uint32_t sourceStride = srcMap.GetStride();
+ uint8_t* destData = DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint);
+ uint32_t destStride = destMap.GetStride();
+
+ if (BytesPerPixel(aSrc->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aSrcRect.height; y++) {
+ PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
+ sourceData += sourceStride;
+ destData += destStride;
+ }
+ } else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aSrcRect.height; y++) {
+ PodCopy(destData, sourceData, aSrcRect.width);
+ sourceData += sourceStride;
+ destData += destStride;
+ }
+ }
+
+ return true;
+}
+
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource)
+{
+ RefPtr<DataSourceSurface> copy =
+ Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat(), true);
+ if (copy) {
+ CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
+ }
+ return copy.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DataSurfaceHelpers.h b/system/graphics/2d/DataSurfaceHelpers.h
new file mode 100644
index 000000000..b0fdf2983
--- /dev/null
+++ b/system/graphics/2d/DataSurfaceHelpers.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_DATASURFACEHELPERS_H
+#define _MOZILLA_GFX_DATASURFACEHELPERS_H
+
+#include "2D.h"
+
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Create a DataSourceSurface and init the surface with the |aData|. The stride
+ * of this source surface might be different from the input data's |aDataStride|.
+ * System will try to use the optimal one.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceFromData(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ const uint8_t* aData,
+ int32_t aDataStride);
+
+/**
+ * Similar to CreateDataSourceSurfaceFromData(), but could setup the stride for
+ * this surface.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ const uint8_t* aData,
+ int32_t aDataStride);
+
+void
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
+
+/**
+ * Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
+ * and aBytesPerPixel give the size, stride and bytes per pixel for aSrc's
+ * surface. Callers are responsible for making sure that aDst is big enough to
+ * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
+ */
+void
+CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
+ int32_t aSrcStride, int32_t aBytesPerPixel);
+
+/**
+ * Convert aSurface to a packed buffer in BGRA format.
+ */
+UniquePtr<uint8_t[]>
+SurfaceToPackedBGRA(DataSourceSurface *aSurface);
+
+/**
+ * Convert aSurface to a packed buffer in BGR format. The pixel data is
+ * returned in a buffer allocated with new uint8_t[]. The caller then has
+ * ownership of the buffer and is responsible for delete[]'ing it.
+ *
+ * This function is currently only intended for use with surfaces of format
+ * SurfaceFormat::B8G8R8X8 since the X components of the pixel data (if any)
+ * are simply dropped (no attempt is made to un-pre-multiply alpha from the
+ * color components).
+ */
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface);
+
+/**
+ * Clears all the bytes in a DataSourceSurface's data array to zero (so to
+ * transparent black for SurfaceFormat::B8G8R8A8, for example).
+ * Note that DataSourceSurfaces can be initialized to zero, which is
+ * more efficient than zeroing the surface after initialization.
+ */
+void
+ClearDataSourceSurface(DataSourceSurface *aSurface);
+
+/**
+ * Multiplies aStride and aHeight and makes sure the result is limited to
+ * something sane. To keep things consistent, this should always be used
+ * wherever we allocate a buffer based on surface stride and height.
+ *
+ * @param aExtra Optional argument to specify an additional number of trailing
+ * bytes (useful for creating intermediate surfaces for filters, for
+ * example).
+ *
+ * @return The result of the multiplication if it is acceptable, or else zero.
+ */
+size_t
+BufferSizeFromStrideAndHeight(int32_t aStride,
+ int32_t aHeight,
+ int32_t aExtraBytes = 0);
+
+/**
+ * Multiplies aWidth, aHeight, aDepth and makes sure the result is limited to
+ * something sane. To keep things consistent, this should always be used
+ * wherever we allocate a buffer based on surface dimensions.
+ *
+ * @param aExtra Optional argument to specify an additional number of trailing
+ * bytes (useful for creating intermediate surfaces for filters, for
+ * example).
+ *
+ * @return The result of the multiplication if it is acceptable, or else zero.
+ */
+size_t
+BufferSizeFromDimensions(int32_t aWidth,
+ int32_t aHeight,
+ int32_t aDepth,
+ int32_t aExtraBytes = 0);
+/**
+ * Copy aSrcRect from aSrc to aDest starting at aDestPoint.
+ * @returns false if the copy is not successful or the aSrc's size is empty.
+ */
+bool
+CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
+ IntRect aSrcRect, IntPoint aDestPoint);
+
+/**
+ * Create a non aliasing copy of aSource. This creates a new DataSourceSurface
+ * using the factory and copies the bits.
+ *
+ * @return a dss allocated by Factory that contains a copy a aSource.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource);
+
+/**
+ * Return the byte at aPoint.
+ */
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface,
+ const DataSourceSurface::MappedSurface* aMap,
+ IntPoint aPoint);
+
+/**
+ * Check if aPoint is contained by the surface.
+ *
+ * @returns true if and only if aPoint is inside the surface.
+ */
+bool
+SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_DATASURFACEHELPERS_H
diff --git a/system/graphics/2d/DrawCommand.h b/system/graphics/2d/DrawCommand.h
new file mode 100644
index 000000000..eb415c70a
--- /dev/null
+++ b/system/graphics/2d/DrawCommand.h
@@ -0,0 +1,594 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWCOMMAND_H_
+#define MOZILLA_GFX_DRAWCOMMAND_H_
+
+#include <math.h>
+
+#include "2D.h"
+#include "Filters.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+enum class CommandType : int8_t {
+ DRAWSURFACE = 0,
+ DRAWFILTER,
+ DRAWSURFACEWITHSHADOW,
+ CLEARRECT,
+ COPYSURFACE,
+ COPYRECT,
+ FILLRECT,
+ STROKERECT,
+ STROKELINE,
+ STROKE,
+ FILL,
+ FILLGLYPHS,
+ MASK,
+ MASKSURFACE,
+ PUSHCLIP,
+ PUSHCLIPRECT,
+ POPCLIP,
+ SETTRANSFORM,
+ FLUSH
+};
+
+class DrawingCommand
+{
+public:
+ virtual ~DrawingCommand() {}
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
+
+ virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
+
+protected:
+ explicit DrawingCommand(CommandType aType)
+ : mType(aType)
+ {
+ }
+
+ CommandType GetType() { return mType; }
+
+private:
+ CommandType mType;
+};
+
+class StoredPattern
+{
+public:
+ explicit StoredPattern(const Pattern& aPattern)
+ {
+ Assign(aPattern);
+ }
+
+ void Assign(const Pattern& aPattern)
+ {
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ new (mColor)ColorPattern(*static_cast<const ColorPattern*>(&aPattern));
+ return;
+ case PatternType::SURFACE:
+ {
+ SurfacePattern* surfPat = new (mSurface)SurfacePattern(*static_cast<const SurfacePattern*>(&aPattern));
+ surfPat->mSurface->GuaranteePersistance();
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ new (mLinear)LinearGradientPattern(*static_cast<const LinearGradientPattern*>(&aPattern));
+ return;
+ case PatternType::RADIAL_GRADIENT:
+ new (mRadial)RadialGradientPattern(*static_cast<const RadialGradientPattern*>(&aPattern));
+ return;
+ }
+ }
+
+ ~StoredPattern()
+ {
+ reinterpret_cast<Pattern*>(mPattern)->~Pattern();
+ }
+
+ operator Pattern&()
+ {
+ return *reinterpret_cast<Pattern*>(mPattern);
+ }
+
+ operator const Pattern&() const
+ {
+ return *reinterpret_cast<const Pattern*>(mPattern);
+ }
+
+ StoredPattern(const StoredPattern& aPattern)
+ {
+ Assign(aPattern);
+ }
+
+private:
+ StoredPattern operator=(const StoredPattern& aOther)
+ {
+ // Block this so that we notice if someone's doing excessive assigning.
+ return *this;
+ }
+
+ union {
+ char mPattern[sizeof(Pattern)];
+ char mColor[sizeof(ColorPattern)];
+ char mLinear[sizeof(LinearGradientPattern)];
+ char mRadial[sizeof(RadialGradientPattern)];
+ char mSurface[sizeof(SurfacePattern)];
+ };
+};
+
+class DrawSurfaceCommand : public DrawingCommand
+{
+public:
+ DrawSurfaceCommand(SourceSurface *aSurface, const Rect& aDest,
+ const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::DRAWSURFACE)
+ , mSurface(aSurface), mDest(aDest)
+ , mSource(aSource), mSurfOptions(aSurfOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions);
+ }
+
+private:
+ RefPtr<SourceSurface> mSurface;
+ Rect mDest;
+ Rect mSource;
+ DrawSurfaceOptions mSurfOptions;
+ DrawOptions mOptions;
+};
+
+class DrawFilterCommand : public DrawingCommand
+{
+public:
+ DrawFilterCommand(FilterNode* aFilter, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::DRAWSURFACE)
+ , mFilter(aFilter), mSourceRect(aSourceRect)
+ , mDestPoint(aDestPoint), mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
+ }
+
+private:
+ RefPtr<FilterNode> mFilter;
+ Rect mSourceRect;
+ Point mDestPoint;
+ DrawOptions mOptions;
+};
+
+class ClearRectCommand : public DrawingCommand
+{
+public:
+ explicit ClearRectCommand(const Rect& aRect)
+ : DrawingCommand(CommandType::CLEARRECT)
+ , mRect(aRect)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->ClearRect(mRect);
+ }
+
+private:
+ Rect mRect;
+};
+
+class CopySurfaceCommand : public DrawingCommand
+{
+public:
+ CopySurfaceCommand(SourceSurface* aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint& aDestination)
+ : DrawingCommand(CommandType::COPYSURFACE)
+ , mSurface(aSurface)
+ , mSourceRect(aSourceRect)
+ , mDestination(aDestination)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform) const
+ {
+ MOZ_ASSERT(!aTransform || !aTransform->HasNonIntegerTranslation());
+ Point dest(Float(mDestination.x), Float(mDestination.y));
+ if (aTransform) {
+ dest = aTransform->TransformPoint(dest);
+ }
+ aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y)));
+ }
+
+private:
+ RefPtr<SourceSurface> mSurface;
+ IntRect mSourceRect;
+ IntPoint mDestination;
+};
+
+class FillRectCommand : public DrawingCommand
+{
+public:
+ FillRectCommand(const Rect& aRect,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::FILLRECT)
+ , mRect(aRect)
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->FillRect(mRect, mPattern, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = aTransform.TransformBounds(mRect);
+ return true;
+ }
+
+private:
+ Rect mRect;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+};
+
+class StrokeRectCommand : public DrawingCommand
+{
+public:
+ StrokeRectCommand(const Rect& aRect,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKERECT)
+ , mRect(aRect)
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ if (aStrokeOptions.mDashLength) {
+ mDashes.resize(aStrokeOptions.mDashLength);
+ mStrokeOptions.mDashPattern = &mDashes.front();
+ memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
+ }
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ Rect mRect;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+ std::vector<Float> mDashes;
+};
+
+class StrokeLineCommand : public DrawingCommand
+{
+public:
+ StrokeLineCommand(const Point& aStart,
+ const Point& aEnd,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKELINE)
+ , mStart(aStart)
+ , mEnd(aEnd)
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ Point mStart;
+ Point mEnd;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class FillCommand : public DrawingCommand
+{
+public:
+ FillCommand(const Path* aPath,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::FILL)
+ , mPath(const_cast<Path*>(aPath))
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Fill(mPath, mPattern, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = mPath->GetBounds(aTransform);
+ return true;
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+};
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.707106781186547524400844362104849039
+#endif
+
+// The logic for this comes from _cairo_stroke_style_max_distance_from_path
+static Rect
+PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions,
+ const Rect &aRect,
+ const Matrix &aTransform)
+{
+ double styleExpansionFactor = 0.5f;
+
+ if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
+ styleExpansionFactor = M_SQRT1_2;
+ }
+
+ if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
+ styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
+ styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
+ }
+
+ styleExpansionFactor *= aStrokeOptions.mLineWidth;
+
+ double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
+ double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
+
+ Rect result = aRect;
+ result.Inflate(dx, dy);
+ return result;
+}
+
+class StrokeCommand : public DrawingCommand
+{
+public:
+ StrokeCommand(const Path* aPath,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKE)
+ , mPath(const_cast<Path*>(aPath))
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ if (aStrokeOptions.mDashLength) {
+ mDashes.resize(aStrokeOptions.mDashLength);
+ mStrokeOptions.mDashPattern = &mDashes.front();
+ memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
+ }
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = PathExtentsToMaxStrokeExtents(mStrokeOptions, mPath->GetBounds(aTransform), aTransform);
+ return true;
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+ std::vector<Float> mDashes;
+};
+
+class FillGlyphsCommand : public DrawingCommand
+{
+public:
+ FillGlyphsCommand(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions)
+ : DrawingCommand(CommandType::FILLGLYPHS)
+ , mFont(aFont)
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ , mRenderingOptions(const_cast<GlyphRenderingOptions*>(aRenderingOptions))
+ {
+ mGlyphs.resize(aBuffer.mNumGlyphs);
+ memcpy(&mGlyphs.front(), aBuffer.mGlyphs, sizeof(Glyph) * aBuffer.mNumGlyphs);
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ GlyphBuffer buf;
+ buf.mNumGlyphs = mGlyphs.size();
+ buf.mGlyphs = &mGlyphs.front();
+ aDT->FillGlyphs(mFont, buf, mPattern, mOptions, mRenderingOptions);
+ }
+
+private:
+ RefPtr<ScaledFont> mFont;
+ std::vector<Glyph> mGlyphs;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+ RefPtr<GlyphRenderingOptions> mRenderingOptions;
+};
+
+class MaskCommand : public DrawingCommand
+{
+public:
+ MaskCommand(const Pattern& aSource,
+ const Pattern& aMask,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::MASK)
+ , mSource(aSource)
+ , mMask(aMask)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Mask(mSource, mMask, mOptions);
+ }
+
+private:
+ StoredPattern mSource;
+ StoredPattern mMask;
+ DrawOptions mOptions;
+};
+
+class MaskSurfaceCommand : public DrawingCommand
+{
+public:
+ MaskSurfaceCommand(const Pattern& aSource,
+ const SourceSurface* aMask,
+ const Point& aOffset,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::MASKSURFACE)
+ , mSource(aSource)
+ , mMask(const_cast<SourceSurface*>(aMask))
+ , mOffset(aOffset)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->MaskSurface(mSource, mMask, mOffset, mOptions);
+ }
+
+private:
+ StoredPattern mSource;
+ RefPtr<SourceSurface> mMask;
+ Point mOffset;
+ DrawOptions mOptions;
+};
+
+class PushClipCommand : public DrawingCommand
+{
+public:
+ explicit PushClipCommand(const Path* aPath)
+ : DrawingCommand(CommandType::PUSHCLIP)
+ , mPath(const_cast<Path*>(aPath))
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->PushClip(mPath);
+ }
+
+private:
+ RefPtr<Path> mPath;
+};
+
+class PushClipRectCommand : public DrawingCommand
+{
+public:
+ explicit PushClipRectCommand(const Rect& aRect)
+ : DrawingCommand(CommandType::PUSHCLIPRECT)
+ , mRect(aRect)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->PushClipRect(mRect);
+ }
+
+private:
+ Rect mRect;
+};
+
+class PopClipCommand : public DrawingCommand
+{
+public:
+ PopClipCommand()
+ : DrawingCommand(CommandType::POPCLIP)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->PopClip();
+ }
+};
+
+class SetTransformCommand : public DrawingCommand
+{
+public:
+ explicit SetTransformCommand(const Matrix& aTransform)
+ : DrawingCommand(CommandType::SETTRANSFORM)
+ , mTransform(aTransform)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aMatrix) const
+ {
+ if (aMatrix) {
+ aDT->SetTransform(mTransform * (*aMatrix));
+ } else {
+ aDT->SetTransform(mTransform);
+ }
+ }
+
+private:
+ Matrix mTransform;
+};
+
+class FlushCommand : public DrawingCommand
+{
+public:
+ explicit FlushCommand()
+ : DrawingCommand(CommandType::FLUSH)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Flush();
+ }
+};
+
+} // namespace gfx
+
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */
diff --git a/system/graphics/2d/DrawEventRecorder.cpp b/system/graphics/2d/DrawEventRecorder.cpp
new file mode 100644
index 000000000..0e20b8b5a
--- /dev/null
+++ b/system/graphics/2d/DrawEventRecorder.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawEventRecorder.h"
+#include "PathRecording.h"
+#include "RecordingTypes.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+DrawEventRecorderPrivate::DrawEventRecorderPrivate(std::ostream *aStream)
+ : mOutputStream(aStream)
+{
+}
+
+void
+DrawEventRecorderPrivate::WriteHeader()
+{
+ WriteElement(*mOutputStream, kMagicInt);
+ WriteElement(*mOutputStream, kMajorRevision);
+ WriteElement(*mOutputStream, kMinorRevision);
+}
+
+void
+DrawEventRecorderPrivate::RecordEvent(const RecordedEvent &aEvent)
+{
+ WriteElement(*mOutputStream, aEvent.mType);
+
+ aEvent.RecordToStream(*mOutputStream);
+
+ Flush();
+}
+
+DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename)
+ : DrawEventRecorderPrivate(nullptr)
+ , mOutputFile(aFilename, ofstream::binary)
+{
+ mOutputStream = &mOutputFile;
+
+ WriteHeader();
+}
+
+DrawEventRecorderFile::~DrawEventRecorderFile()
+{
+ mOutputFile.close();
+}
+
+void
+DrawEventRecorderFile::Flush()
+{
+ mOutputFile.flush();
+}
+
+bool
+DrawEventRecorderFile::IsOpen()
+{
+ return mOutputFile.is_open();
+}
+
+void
+DrawEventRecorderFile::OpenNew(const char *aFilename)
+{
+ MOZ_ASSERT(!mOutputFile.is_open());
+
+ mOutputFile.open(aFilename, ofstream::binary);
+ WriteHeader();
+}
+
+void
+DrawEventRecorderFile::Close()
+{
+ MOZ_ASSERT(mOutputFile.is_open());
+
+ mOutputFile.close();
+}
+
+DrawEventRecorderMemory::DrawEventRecorderMemory()
+ : DrawEventRecorderPrivate(nullptr)
+{
+ mOutputStream = &mMemoryStream;
+
+ WriteHeader();
+}
+
+void
+DrawEventRecorderMemory::Flush()
+{
+ mOutputStream->flush();
+}
+
+size_t
+DrawEventRecorderMemory::RecordingSize()
+{
+ return mMemoryStream.tellp();
+}
+
+bool
+DrawEventRecorderMemory::CopyRecording(char* aBuffer, size_t aBufferLen)
+{
+ return !!mMemoryStream.read(aBuffer, aBufferLen);
+}
+
+void
+DrawEventRecorderMemory::WipeRecording()
+{
+ mMemoryStream.str(std::string());
+ mMemoryStream.clear();
+
+ WriteHeader();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawEventRecorder.h b/system/graphics/2d/DrawEventRecorder.h
new file mode 100644
index 000000000..a789379d2
--- /dev/null
+++ b/system/graphics/2d/DrawEventRecorder.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWEVENTRECORDER_H_
+#define MOZILLA_GFX_DRAWEVENTRECORDER_H_
+
+#include "2D.h"
+#include "RecordedEvent.h"
+#include <ostream>
+#include <fstream>
+
+#if defined(_MSC_VER)
+#include <unordered_set>
+#else
+#include <set>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class PathRecording;
+
+class DrawEventRecorderPrivate : public DrawEventRecorder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
+ explicit DrawEventRecorderPrivate(std::ostream *aStream);
+ virtual ~DrawEventRecorderPrivate() { }
+
+ void WriteHeader();
+
+ void RecordEvent(const RecordedEvent &aEvent);
+ void WritePath(const PathRecording *aPath);
+
+ void AddStoredObject(const ReferencePtr aObject) {
+ mStoredObjects.insert(aObject);
+ }
+
+ void RemoveStoredObject(const ReferencePtr aObject) {
+ mStoredObjects.erase(aObject);
+ }
+
+ bool HasStoredObject(const ReferencePtr aObject) {
+ return mStoredObjects.find(aObject) != mStoredObjects.end();
+ }
+
+ void AddStoredFontData(const uint64_t aFontDataKey) {
+ mStoredFontData.insert(aFontDataKey);
+ }
+
+ bool HasStoredFontData(const uint64_t aFontDataKey) {
+ return mStoredFontData.find(aFontDataKey) != mStoredFontData.end();
+ }
+
+protected:
+ std::ostream *mOutputStream;
+
+ virtual void Flush() = 0;
+
+#if defined(_MSC_VER)
+ typedef std::unordered_set<const void*> ObjectSet;
+ typedef std::unordered_set<uint64_t> Uint64Set;
+#else
+ typedef std::set<const void*> ObjectSet;
+ typedef std::set<uint64_t> Uint64Set;
+#endif
+
+ ObjectSet mStoredObjects;
+ Uint64Set mStoredFontData;
+};
+
+class DrawEventRecorderFile : public DrawEventRecorderPrivate
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile)
+ explicit DrawEventRecorderFile(const char *aFilename);
+ ~DrawEventRecorderFile();
+
+ /**
+ * Returns whether a recording file is currently open.
+ */
+ bool IsOpen();
+
+ /**
+ * Opens new file with the provided name. The recorder does NOT forget which
+ * objects it has recorded. This can be used with Close, so that a recording
+ * can be processed in chunks. The file must not already be open.
+ */
+ void OpenNew(const char *aFilename);
+
+ /**
+ * Closes the file so that it can be processed. The recorder does NOT forget
+ * which objects it has recorded. This can be used with OpenNew, so that a
+ * recording can be processed in chunks. The file must be open.
+ */
+ void Close();
+
+private:
+ virtual void Flush();
+
+ std::ofstream mOutputFile;
+};
+
+class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
+
+ /**
+ * Constructs a DrawEventRecorder that stores the recording in memory.
+ */
+ DrawEventRecorderMemory();
+
+ /**
+ * @return the current size of the recording (in chars).
+ */
+ size_t RecordingSize();
+
+ /**
+ * Copies at most aBufferLen chars of the recording into aBuffer.
+ *
+ * @param aBuffer buffer to receive the recording chars
+ * @param aBufferLen length of aBuffer
+ * @return true if copied successfully
+ */
+ bool CopyRecording(char* aBuffer, size_t aBufferLen);
+
+ /**
+ * Wipes the internal recording buffer, but the recorder does NOT forget which
+ * objects it has recorded. This can be used so that a recording can be copied
+ * and processed in chunks, releasing memory as it goes.
+ */
+ void WipeRecording();
+
+private:
+ ~DrawEventRecorderMemory() {};
+
+ void Flush() final;
+
+ std::stringstream mMemoryStream;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
diff --git a/system/graphics/2d/DrawTarget.cpp b/system/graphics/2d/DrawTarget.cpp
new file mode 100644
index 000000000..72e070dc3
--- /dev/null
+++ b/system/graphics/2d/DrawTarget.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include "Logging.h"
+#include "PathHelpers.h"
+
+#include "DrawTargetCapture.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<DrawTargetCapture>
+DrawTarget::CreateCaptureDT(const IntSize& aSize)
+{
+ RefPtr<DrawTargetCaptureImpl> dt = new DrawTargetCaptureImpl();
+
+ if (!dt->Init(aSize, this)) {
+ gfxWarning() << "Failed to initialize Capture DrawTarget!";
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+void
+DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT,
+ const Matrix& aTransform)
+{
+ if (aTransform.HasNonIntegerTranslation()) {
+ gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!";
+ return;
+ }
+ static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform);
+}
+
+void
+DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ Matrix oldTransform = GetTransform();
+ SetTransform(Matrix());
+
+ RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
+ for (uint32_t i = 0; i < aCount; i++) {
+ AppendRectToPath(pathBuilder, Rect(aRects[i]));
+ }
+ RefPtr<Path> path = pathBuilder->Finish();
+ PushClip(path);
+
+ SetTransform(oldTransform);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetCairo.cpp b/system/graphics/2d/DrawTargetCairo.cpp
new file mode 100644
index 000000000..13283ab4c
--- /dev/null
+++ b/system/graphics/2d/DrawTargetCairo.cpp
@@ -0,0 +1,2357 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetCairo.h"
+
+#include "SourceSurfaceCairo.h"
+#include "PathCairo.h"
+#include "HelpersCairo.h"
+#include "ScaledFontBase.h"
+#include "BorrowedContext.h"
+#include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+#include "cairo.h"
+#include "cairo-tee.h"
+#include <string.h>
+
+#include "Blur.h"
+#include "Logging.h"
+#include "Tools.h"
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+#endif
+
+#ifdef CAIRO_HAS_WIN32_SURFACE
+#include "cairo-win32.h"
+#endif
+
+#define PIXMAN_DONT_DEFINE_STDINT
+#include "pixman.h"
+
+#include <algorithm>
+
+// 2^23
+#define CAIRO_COORD_MAX (Float(0x7fffff))
+
+namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
+
+namespace gfx {
+
+cairo_surface_t *DrawTargetCairo::mDummySurface;
+
+namespace {
+
+// An RAII class to prepare to draw a context and optional path. Saves and
+// restores the context on construction/destruction.
+class AutoPrepareForDrawing
+{
+public:
+ AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
+ : mCtx(ctx)
+ {
+ dt->PrepareForDrawing(ctx);
+ cairo_save(mCtx);
+ MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
+ }
+
+ AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
+ : mCtx(ctx)
+ {
+ dt->PrepareForDrawing(ctx, path);
+ cairo_save(mCtx);
+ MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
+ }
+
+ ~AutoPrepareForDrawing()
+ {
+ cairo_restore(mCtx);
+ cairo_status_t status = cairo_status(mCtx);
+ if (status) {
+ gfxWarning() << "DrawTargetCairo context in error state: " << cairo_status_to_string(status) << "(" << status << ")";
+ }
+ }
+
+private:
+#ifdef DEBUG
+ Matrix GetTransform()
+ {
+ cairo_matrix_t mat;
+ cairo_get_matrix(mCtx, &mat);
+ return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
+ }
+#endif
+
+ cairo_t* mCtx;
+};
+
+/* Clamp r to (0,0) (2^23,2^23)
+ * these are to be device coordinates.
+ *
+ * Returns false if the rectangle is completely out of bounds,
+ * true otherwise.
+ *
+ * This function assumes that it will be called with a rectangle being
+ * drawn into a surface with an identity transformation matrix; that
+ * is, anything above or to the left of (0,0) will be offscreen.
+ *
+ * First it checks if the rectangle is entirely beyond
+ * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
+ * false is returned.
+ *
+ * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
+ * and adjusts the width and height appropriately. For example, a
+ * rectangle from (0,-5) with dimensions (5,10) will become a
+ * rectangle from (0,0) with dimensions (5,5).
+ *
+ * If after negative x/y adjustment to 0, either the width or height
+ * is negative, then the rectangle is completely offscreen, and
+ * nothing is drawn -- false is returned.
+ *
+ * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
+ * the width and height are clamped such x+width or y+height are equal
+ * to CAIRO_COORD_MAX, and true is returned.
+ */
+static bool
+ConditionRect(Rect& r) {
+ // if either x or y is way out of bounds;
+ // note that we don't handle negative w/h here
+ if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX)
+ return false;
+
+ if (r.X() < 0.f) {
+ r.width += r.X();
+ if (r.width < 0.f)
+ return false;
+ r.x = 0.f;
+ }
+
+ if (r.XMost() > CAIRO_COORD_MAX) {
+ r.width = CAIRO_COORD_MAX - r.X();
+ }
+
+ if (r.Y() < 0.f) {
+ r.height += r.Y();
+ if (r.Height() < 0.f)
+ return false;
+
+ r.y = 0.f;
+ }
+
+ if (r.YMost() > CAIRO_COORD_MAX) {
+ r.height = CAIRO_COORD_MAX - r.Y();
+ }
+ return true;
+}
+
+} // end anonymous namespace
+
+static bool
+SupportsSelfCopy(cairo_surface_t* surface)
+{
+ switch (cairo_surface_get_type(surface))
+ {
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ case CAIRO_SURFACE_TYPE_WIN32:
+ case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+static bool
+PatternIsCompatible(const Pattern& aPattern)
+{
+ switch (aPattern.GetType())
+ {
+ case PatternType::LINEAR_GRADIENT:
+ {
+ const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
+ }
+ default:
+ return true;
+ }
+}
+
+static cairo_user_data_key_t surfaceDataKey;
+
+void
+ReleaseData(void* aData)
+{
+ DataSourceSurface *data = static_cast<DataSourceSurface*>(aData);
+ data->Unmap();
+ data->Release();
+}
+
+cairo_surface_t*
+CopyToImageSurface(unsigned char *aData,
+ const IntRect &aRect,
+ int32_t aStride,
+ SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aData);
+
+ cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat),
+ aRect.width,
+ aRect.height);
+ // In certain scenarios, requesting larger than 8k image fails. Bug 803568
+ // covers the details of how to run into it, but the full detailed
+ // investigation hasn't been done to determine the underlying cause. We
+ // will just handle the failure to allocate the surface to avoid a crash.
+ if (cairo_surface_status(surf)) {
+ gfxWarning() << "Invalid surface DTC " << cairo_surface_status(surf);
+ return nullptr;
+ }
+
+ unsigned char* surfData = cairo_image_surface_get_data(surf);
+ int surfStride = cairo_image_surface_get_stride(surf);
+ int32_t pixelWidth = BytesPerPixel(aFormat);
+
+ unsigned char* source = aData +
+ aRect.y * aStride +
+ aRect.x * pixelWidth;
+
+ MOZ_ASSERT(aStride >= aRect.width * pixelWidth);
+ for (int32_t y = 0; y < aRect.height; ++y) {
+ memcpy(surfData + y * surfStride,
+ source + y * aStride,
+ aRect.width * pixelWidth);
+ }
+ cairo_surface_mark_dirty(surf);
+ return surf;
+}
+
+/**
+ * If aSurface can be represented as a surface of type
+ * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does
+ * not add a reference.
+ */
+cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface)
+{
+ if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ return aSurface;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ } else if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_WIN32) {
+ return cairo_win32_surface_get_image(aSurface);
+#endif
+ }
+
+ return nullptr;
+}
+
+cairo_surface_t* CreateSubImageForData(unsigned char* aData,
+ const IntRect& aRect,
+ int aStride,
+ SurfaceFormat aFormat)
+{
+ if (!aData) {
+ gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
+ return nullptr;
+ }
+ unsigned char *data = aData +
+ aRect.y * aStride +
+ aRect.x * BytesPerPixel(aFormat);
+
+ cairo_surface_t *image =
+ cairo_image_surface_create_for_data(data,
+ GfxFormatToCairoFormat(aFormat),
+ aRect.width,
+ aRect.height,
+ aStride);
+ cairo_surface_set_device_offset(image, -aRect.x, -aRect.y);
+ return image;
+}
+
+/**
+ * Returns a referenced cairo_surface_t representing the
+ * sub-image specified by aSubImage.
+ */
+cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface,
+ const IntRect& aSubImage,
+ SurfaceFormat aFormat)
+{
+ // No need to worry about retaining a reference to the original
+ // surface since the only caller of this function guarantees
+ // that aSurface will stay alive as long as the result
+
+ cairo_surface_t* image = GetAsImageSurface(aSurface);
+ if (image) {
+ image = CreateSubImageForData(cairo_image_surface_get_data(image),
+ aSubImage,
+ cairo_image_surface_get_stride(image),
+ aFormat);
+ return image;
+ }
+
+ cairo_surface_t* similar =
+ cairo_surface_create_similar(aSurface,
+ cairo_surface_get_content(aSurface),
+ aSubImage.width, aSubImage.height);
+
+ cairo_t* ctx = cairo_create(similar);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(ctx, aSurface, -aSubImage.x, -aSubImage.y);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ cairo_surface_set_device_offset(similar, -aSubImage.x, -aSubImage.y);
+ return similar;
+}
+
+/**
+ * Returns cairo surface for the given SourceSurface.
+ * If possible, it will use the cairo_surface associated with aSurface,
+ * otherwise, it will create a new cairo_surface.
+ * In either case, the caller must call cairo_surface_destroy on the
+ * result when it is done with it.
+ */
+cairo_surface_t*
+GetCairoSurfaceForSourceSurface(SourceSurface *aSurface,
+ bool aExistingOnly = false,
+ const IntRect& aSubImage = IntRect())
+{
+ if (!aSurface) {
+ return nullptr;
+ }
+
+ IntRect subimage = IntRect(IntPoint(), aSurface->GetSize());
+ if (!aSubImage.IsEmpty()) {
+ MOZ_ASSERT(!aExistingOnly);
+ MOZ_ASSERT(subimage.Contains(aSubImage));
+ subimage = aSubImage;
+ }
+
+ if (aSurface->GetType() == SurfaceType::CAIRO) {
+ cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
+ if (aSubImage.IsEmpty()) {
+ cairo_surface_reference(surf);
+ } else {
+ surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
+ }
+ return surf;
+ }
+
+ if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) {
+ cairo_surface_t* surf =
+ static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface();
+ if (aSubImage.IsEmpty()) {
+ cairo_surface_reference(surf);
+ } else {
+ surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
+ }
+ return surf;
+ }
+
+ if (aExistingOnly) {
+ return nullptr;
+ }
+
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+ if (!data) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!data->Map(DataSourceSurface::READ, &map)) {
+ return nullptr;
+ }
+
+ cairo_surface_t* surf =
+ CreateSubImageForData(map.mData, subimage,
+ map.mStride, data->GetFormat());
+
+ // In certain scenarios, requesting larger than 8k image fails. Bug 803568
+ // covers the details of how to run into it, but the full detailed
+ // investigation hasn't been done to determine the underlying cause. We
+ // will just handle the failure to allocate the surface to avoid a crash.
+ if (!surf || cairo_surface_status(surf)) {
+ if (surf && (cairo_surface_status(surf) == CAIRO_STATUS_INVALID_STRIDE)) {
+ // If we failed because of an invalid stride then copy into
+ // a new surface with a stride that cairo chooses. No need to
+ // set user data since we're not dependent on the original
+ // data.
+ cairo_surface_t* result =
+ CopyToImageSurface(map.mData,
+ subimage,
+ map.mStride,
+ data->GetFormat());
+ data->Unmap();
+ return result;
+ }
+ data->Unmap();
+ return nullptr;
+ }
+
+ cairo_surface_set_user_data(surf,
+ &surfaceDataKey,
+ data.forget().take(),
+ ReleaseData);
+ return surf;
+}
+
+// An RAII class to temporarily clear any device offset set
+// on a surface. Note that this does not take a reference to the
+// surface.
+class AutoClearDeviceOffset
+{
+public:
+ explicit AutoClearDeviceOffset(SourceSurface* aSurface)
+ : mSurface(nullptr)
+ , mX(0)
+ , mY(0)
+ {
+ Init(aSurface);
+ }
+
+ explicit AutoClearDeviceOffset(const Pattern& aPattern)
+ : mSurface(nullptr)
+ {
+ if (aPattern.GetType() == PatternType::SURFACE) {
+ const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
+ Init(pattern.mSurface);
+ }
+ }
+
+ ~AutoClearDeviceOffset()
+ {
+ if (mSurface) {
+ cairo_surface_set_device_offset(mSurface, mX, mY);
+ }
+ }
+
+private:
+ void Init(SourceSurface* aSurface)
+ {
+ cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true);
+ if (surface) {
+ Init(surface);
+ cairo_surface_destroy(surface);
+ }
+ }
+
+ void Init(cairo_surface_t *aSurface)
+ {
+ mSurface = aSurface;
+ cairo_surface_get_device_offset(mSurface, &mX, &mY);
+ cairo_surface_set_device_offset(mSurface, 0, 0);
+ }
+
+ cairo_surface_t* mSurface;
+ double mX;
+ double mY;
+};
+
+static inline void
+CairoPatternAddGradientStop(cairo_pattern_t* aPattern,
+ const GradientStop &aStop,
+ Float aNudge = 0)
+{
+ cairo_pattern_add_color_stop_rgba(aPattern, aStop.offset + aNudge,
+ aStop.color.r, aStop.color.g, aStop.color.b,
+ aStop.color.a);
+
+}
+
+// Never returns nullptr. As such, you must always pass in Cairo-compatible
+// patterns, most notably gradients with a GradientStopCairo.
+// The pattern returned must have cairo_pattern_destroy() called on it by the
+// caller.
+// As the cairo_pattern_t returned may depend on the Pattern passed in, the
+// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
+// Pattern passed in.
+static cairo_pattern_t*
+GfxPatternToCairoPattern(const Pattern& aPattern,
+ Float aAlpha,
+ const Matrix& aTransform)
+{
+ cairo_pattern_t* pat;
+ const Matrix* matrix = nullptr;
+
+ switch (aPattern.GetType())
+ {
+ case PatternType::COLOR:
+ {
+ Color color = static_cast<const ColorPattern&>(aPattern).mColor;
+ pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
+ break;
+ }
+
+ case PatternType::SURFACE:
+ {
+ const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface,
+ false,
+ pattern.mSamplingRect);
+ if (!surf)
+ return nullptr;
+
+ pat = cairo_pattern_create_for_surface(surf);
+
+ matrix = &pattern.mMatrix;
+
+ cairo_pattern_set_filter(pat, GfxSamplingFilterToCairoFilter(pattern.mSamplingFilter));
+ cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
+
+ cairo_surface_destroy(surf);
+ break;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
+
+ pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
+ pattern.mEnd.x, pattern.mEnd.y);
+
+ MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
+ GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
+ cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
+
+ matrix = &pattern.mMatrix;
+
+ const std::vector<GradientStop>& stops = cairoStops->GetStops();
+ for (size_t i = 0; i < stops.size(); ++i) {
+ CairoPatternAddGradientStop(pat, stops[i]);
+ }
+
+ break;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
+
+ pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
+ pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
+
+ MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
+ GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
+ cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
+
+ matrix = &pattern.mMatrix;
+
+ const std::vector<GradientStop>& stops = cairoStops->GetStops();
+ for (size_t i = 0; i < stops.size(); ++i) {
+ CairoPatternAddGradientStop(pat, stops[i]);
+ }
+
+ break;
+ }
+ default:
+ {
+ // We should support all pattern types!
+ MOZ_ASSERT(false);
+ }
+ }
+
+ // The pattern matrix is a matrix that transforms the pattern into user
+ // space. Cairo takes a matrix that converts from user space to pattern
+ // space. Cairo therefore needs the inverse.
+ if (matrix) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(*matrix, mat);
+ cairo_matrix_invert(&mat);
+ cairo_pattern_set_matrix(pat, &mat);
+ }
+
+ return pat;
+}
+
+static bool
+NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
+{
+ // We pre-multiply colours' alpha by the global alpha, so we don't need to
+ // use an intermediate surface for them.
+ if (aPattern.GetType() == PatternType::COLOR)
+ return false;
+
+ if (aOptions.mAlpha == 1.0)
+ return false;
+
+ return true;
+}
+
+DrawTargetCairo::DrawTargetCairo()
+ : mContext(nullptr)
+ , mSurface(nullptr)
+ , mTransformSingular(false)
+ , mLockedBits(nullptr)
+ , mFontOptions(nullptr)
+{
+}
+
+DrawTargetCairo::~DrawTargetCairo()
+{
+ cairo_destroy(mContext);
+ if (mSurface) {
+ cairo_surface_destroy(mSurface);
+ mSurface = nullptr;
+ }
+ if (mFontOptions) {
+ cairo_font_options_destroy(mFontOptions);
+ mFontOptions = nullptr;
+ }
+ MOZ_ASSERT(!mLockedBits);
+}
+
+bool
+DrawTargetCairo::IsValid() const
+{
+ return mSurface && !cairo_surface_status(mSurface) &&
+ mContext && !cairo_surface_status(cairo_get_group_target(mContext));
+}
+
+DrawTargetType
+DrawTargetCairo::GetType() const
+{
+ if (mContext) {
+ cairo_surface_type_t type = cairo_surface_get_type(mSurface);
+ if (type == CAIRO_SURFACE_TYPE_TEE) {
+ type = cairo_surface_get_type(cairo_tee_surface_index(mSurface, 0));
+ MOZ_ASSERT(type != CAIRO_SURFACE_TYPE_TEE, "C'mon!");
+ MOZ_ASSERT(type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)),
+ "What should we do here?");
+ }
+ switch (type) {
+ case CAIRO_SURFACE_TYPE_PDF:
+ case CAIRO_SURFACE_TYPE_PS:
+ case CAIRO_SURFACE_TYPE_SVG:
+ case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
+ case CAIRO_SURFACE_TYPE_XML:
+ return DrawTargetType::VECTOR;
+
+ case CAIRO_SURFACE_TYPE_VG:
+ case CAIRO_SURFACE_TYPE_GL:
+ case CAIRO_SURFACE_TYPE_GLITZ:
+ case CAIRO_SURFACE_TYPE_QUARTZ:
+ case CAIRO_SURFACE_TYPE_DIRECTFB:
+ return DrawTargetType::HARDWARE_RASTER;
+
+ case CAIRO_SURFACE_TYPE_SKIA:
+ case CAIRO_SURFACE_TYPE_QT:
+ MOZ_FALLTHROUGH_ASSERT("Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER");
+ case CAIRO_SURFACE_TYPE_IMAGE:
+ case CAIRO_SURFACE_TYPE_XLIB:
+ case CAIRO_SURFACE_TYPE_XCB:
+ case CAIRO_SURFACE_TYPE_WIN32:
+ case CAIRO_SURFACE_TYPE_BEOS:
+ case CAIRO_SURFACE_TYPE_OS2:
+ case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
+ case CAIRO_SURFACE_TYPE_SCRIPT:
+ case CAIRO_SURFACE_TYPE_RECORDING:
+ case CAIRO_SURFACE_TYPE_DRM:
+ case CAIRO_SURFACE_TYPE_SUBSURFACE:
+ case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
+ return DrawTargetType::SOFTWARE_RASTER;
+ default:
+ MOZ_CRASH("GFX: Unsupported cairo surface type");
+ }
+ }
+ MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
+ return DrawTargetType::SOFTWARE_RASTER;
+}
+
+IntSize
+DrawTargetCairo::GetSize()
+{
+ return mSize;
+}
+
+SurfaceFormat
+GfxFormatForCairoSurface(cairo_surface_t* surface)
+{
+ cairo_surface_type_t type = cairo_surface_get_type(surface);
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+ return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
+ }
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ // xlib is currently the only Cairo backend that creates 16bpp surfaces
+ if (type == CAIRO_SURFACE_TYPE_XLIB &&
+ cairo_xlib_surface_get_depth(surface) == 16) {
+ return SurfaceFormat::R5G6B5_UINT16;
+ }
+#endif
+ return CairoContentToGfxFormat(cairo_surface_get_content(surface));
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::Snapshot()
+{
+ if (!IsValid()) {
+ gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface " << cairo_surface_status(mSurface);
+ return nullptr;
+ }
+ if (mSnapshot) {
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+ }
+
+ IntSize size = GetSize();
+
+ mSnapshot = new SourceSurfaceCairo(mSurface,
+ size,
+ GfxFormatForCairoSurface(mSurface),
+ this);
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+}
+
+bool
+DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin)
+{
+ cairo_surface_t* target = cairo_get_group_target(mContext);
+ cairo_surface_t* surf = target;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+ cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+ if (imgsurf) {
+ surf = imgsurf;
+ }
+ }
+#endif
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE &&
+ cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) {
+ PointDouble offset;
+ cairo_surface_get_device_offset(target, &offset.x, &offset.y);
+ // verify the device offset can be converted to integers suitable for a bounds rect
+ IntPoint origin(int32_t(-offset.x), int32_t(-offset.y));
+ if (-PointDouble(origin) != offset ||
+ (!aOrigin && origin != IntPoint())) {
+ return false;
+ }
+
+ WillChange();
+ Flush();
+
+ mLockedBits = cairo_image_surface_get_data(surf);
+ *aData = mLockedBits;
+ *aSize = IntSize(cairo_image_surface_get_width(surf),
+ cairo_image_surface_get_height(surf));
+ *aStride = cairo_image_surface_get_stride(surf);
+ *aFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(surf));
+ if (aOrigin) {
+ *aOrigin = origin;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void
+DrawTargetCairo::ReleaseBits(uint8_t* aData)
+{
+ MOZ_ASSERT(mLockedBits == aData);
+ mLockedBits = nullptr;
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+ cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+ if (imgsurf) {
+ cairo_surface_mark_dirty(imgsurf);
+ }
+ }
+#endif
+ cairo_surface_mark_dirty(surf);
+}
+
+void
+DrawTargetCairo::Flush()
+{
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+ cairo_surface_flush(surf);
+}
+
+void
+DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */)
+{
+ WillChange(aPath);
+}
+
+cairo_surface_t*
+DrawTargetCairo::GetDummySurface()
+{
+ if (mDummySurface) {
+ return mDummySurface;
+ }
+
+ mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+ return mDummySurface;
+}
+
+static void
+PaintWithAlpha(cairo_t* aContext, const DrawOptions& aOptions)
+{
+ if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE) {
+ // Cairo treats the source operator like a lerp when alpha is < 1.
+ // Approximate the desired operator by: out = 0; out += src*alpha;
+ if (aOptions.mAlpha == 1) {
+ cairo_set_operator(aContext, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(aContext);
+ } else {
+ cairo_set_operator(aContext, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(aContext);
+ cairo_set_operator(aContext, CAIRO_OPERATOR_ADD);
+ cairo_paint_with_alpha(aContext, aOptions.mAlpha);
+ }
+ } else {
+ cairo_set_operator(aContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+ cairo_paint_with_alpha(aContext, aOptions.mAlpha);
+ }
+}
+
+void
+DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular || aDest.IsEmpty()) {
+ return;
+ }
+
+ if (!IsValid() || !aSurface) {
+ gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
+
+ float sx = aSource.Width() / aDest.Width();
+ float sy = aSource.Height() / aDest.Height();
+
+ cairo_matrix_t src_mat;
+ cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y());
+ cairo_matrix_scale(&src_mat, sx, sy);
+
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
+ if (!surf) {
+ gfxWarning() << "Failed to create cairo surface for DrawTargetCairo::DrawSurface";
+ return;
+ }
+ cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
+ cairo_surface_destroy(surf);
+
+ cairo_pattern_set_matrix(pat, &src_mat);
+ cairo_pattern_set_filter(pat, GfxSamplingFilterToCairoFilter(aSurfOptions.mSamplingFilter));
+ cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ // If the destination rect covers the entire clipped area, then unbounded and bounded
+ // operations are identical, and we don't need to push a group.
+ bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
+ !aDest.Contains(GetUserSpaceClip());
+
+ cairo_translate(mContext, aDest.X(), aDest.Y());
+
+ if (needsGroup) {
+ cairo_push_group(mContext);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
+ cairo_set_source(mContext, pat);
+ cairo_fill(mContext);
+ cairo_pop_group_to_source(mContext);
+ } else {
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
+ cairo_clip(mContext);
+ cairo_set_source(mContext, pat);
+ }
+
+ PaintWithAlpha(mContext, aOptions);
+
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
+}
+
+void
+DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ if (aSurface->GetType() != SurfaceType::CAIRO) {
+ return;
+ }
+
+ AutoClearDeviceOffset clear(aSurface);
+
+ Float width = Float(aSurface->GetSize().width);
+ Float height = Float(aSurface->GetSize().height);
+
+ SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
+ cairo_surface_t* sourcesurf = source->GetSurface();
+ cairo_surface_t* blursurf;
+ cairo_surface_t* surf;
+
+ // We only use the A8 surface for blurred shadows. Unblurred shadows can just
+ // use the RGBA surface directly.
+ if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
+ blursurf = cairo_tee_surface_index(sourcesurf, 0);
+ surf = cairo_tee_surface_index(sourcesurf, 1);
+
+ MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
+ Rect extents(0, 0, width, height);
+ AlphaBoxBlur blur(extents,
+ cairo_image_surface_get_stride(blursurf),
+ aSigma, aSigma);
+ blur.Blur(cairo_image_surface_get_data(blursurf));
+ } else {
+ blursurf = sourcesurf;
+ surf = sourcesurf;
+ }
+
+ WillChange();
+ ClearSurfaceForUnboundedSource(aOperator);
+
+ cairo_save(mContext);
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
+ cairo_identity_matrix(mContext);
+ cairo_translate(mContext, aDest.x, aDest.y);
+
+ if (IsOperatorBoundByMask(aOperator)){
+ cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
+ cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+
+ // Now that the shadow has been drawn, we can draw the surface on top.
+ cairo_set_source_surface(mContext, surf, 0, 0);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, width, height);
+ cairo_fill(mContext);
+ } else {
+ cairo_push_group(mContext);
+ cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
+ cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+
+ // Now that the shadow has been drawn, we can draw the surface on top.
+ cairo_set_source_surface(mContext, surf, 0, 0);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, width, height);
+ cairo_fill(mContext);
+ cairo_pop_group_to_source(mContext);
+ cairo_paint(mContext);
+ }
+
+ cairo_restore(mContext);
+}
+
+void
+DrawTargetCairo::DrawPattern(const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions,
+ DrawPatternType aDrawType,
+ bool aPathBoundsClip)
+{
+ if (!PatternIsCompatible(aPattern)) {
+ return;
+ }
+
+ AutoClearDeviceOffset clear(aPattern);
+
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
+ if (!pat) {
+ return;
+ }
+ if (cairo_pattern_status(pat)) {
+ cairo_pattern_destroy(pat);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
+ cairo_set_source(mContext, pat);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ if (NeedIntermediateSurface(aPattern, aOptions) ||
+ (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) {
+ cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // Don't want operators to be applied twice
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+ if (aDrawType == DRAW_STROKE) {
+ SetCairoStrokeOptions(mContext, aStrokeOptions);
+ cairo_stroke_preserve(mContext);
+ } else {
+ cairo_fill_preserve(mContext);
+ }
+
+ cairo_pop_group_to_source(mContext);
+
+ // Now draw the content using the desired operator
+ PaintWithAlpha(mContext, aOptions);
+ } else {
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+
+ if (aDrawType == DRAW_STROKE) {
+ SetCairoStrokeOptions(mContext, aStrokeOptions);
+ cairo_stroke_preserve(mContext);
+ } else {
+ cairo_fill_preserve(mContext);
+ }
+ }
+
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ bool restoreTransform = false;
+ Matrix mat;
+ Rect r = aRect;
+
+ /* Clamp coordinates to work around a design bug in cairo */
+ if (r.width > CAIRO_COORD_MAX ||
+ r.height > CAIRO_COORD_MAX ||
+ r.x < -CAIRO_COORD_MAX ||
+ r.x > CAIRO_COORD_MAX ||
+ r.y < -CAIRO_COORD_MAX ||
+ r.y > CAIRO_COORD_MAX)
+ {
+ if (!mat.IsRectilinear()) {
+ gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect "
+ "with non-rectilinear transform";
+ }
+
+ mat = GetTransform();
+ r = mat.TransformBounds(r);
+
+ if (!ConditionRect(r)) {
+ gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with "
+ "out-of-bounds Rect";
+ return;
+ }
+
+ restoreTransform = true;
+ SetTransform(Matrix());
+ }
+
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, r.x, r.y, r.Width(), r.Height());
+
+ bool pathBoundsClip = false;
+
+ if (r.Contains(GetUserSpaceClip())) {
+ pathBoundsClip = true;
+ }
+
+ DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip);
+
+ if (restoreTransform) {
+ SetTransform(mat);
+ }
+}
+
+void
+DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
+ const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (cairo_surface_status(aSurface)) {
+ gfxWarning() << "Invalid surface" << cairo_surface_status(aSurface);
+ return;
+ }
+
+ cairo_identity_matrix(mContext);
+
+ cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y);
+ cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
+
+ cairo_reset_clip(mContext);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
+ cairo_fill(mContext);
+}
+
+void
+DrawTargetCairo::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
+
+ if (!aSurface) {
+ gfxWarning() << "Unsupported surface type specified";
+ return;
+ }
+
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
+ if (!surf) {
+ gfxWarning() << "Unsupported surface type specified";
+ return;
+ }
+
+ CopySurfaceInternal(surf, aSource, aDest);
+ cairo_surface_destroy(surf);
+}
+
+void
+DrawTargetCairo::CopyRect(const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ IntRect source = aSource;
+ cairo_surface_t* surf = mSurface;
+
+ if (!SupportsSelfCopy(mSurface) &&
+ aDest.y >= aSource.y &&
+ aDest.y < aSource.YMost()) {
+ cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(GetFormat()),
+ aSource.width, aSource.height);
+ cairo_t* ctx = cairo_create(similar);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ source.x = 0;
+ source.y = 0;
+ surf = similar;
+ }
+
+ CopySurfaceInternal(surf, source, aDest);
+
+ if (surf != mSurface) {
+ cairo_surface_destroy(surf);
+ }
+}
+
+void
+DrawTargetCairo::ClearRect(const Rect& aRect)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ if (!mContext || aRect.Width() < 0 || aRect.Height() < 0 ||
+ !IsFinite(aRect.X()) || !IsFinite(aRect.Width()) ||
+ !IsFinite(aRect.Y()) || !IsFinite(aRect.Height())) {
+ gfxCriticalNote << "ClearRect with invalid argument " << gfx::hexa(mContext) << " with " << aRect.Width() << "x" << aRect.Height() << " [" << aRect.X() << ", " << aRect.Y() << "]";
+ }
+
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
+ cairo_new_path(mContext);
+ cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
+ cairo_rectangle(mContext, aRect.X(), aRect.Y(),
+ aRect.Width(), aRect.Height());
+ cairo_fill(mContext);
+}
+
+void
+DrawTargetCairo::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ cairo_new_path(mContext);
+ cairo_move_to(mContext, aStart.x, aStart.y);
+ cairo_line_to(mContext, aEnd.x, aEnd.y);
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext, aPath);
+
+ if (aPath->GetBackendType() != BackendType::CAIRO)
+ return;
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+ path->SetPathOnContext(mContext);
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext, aPath);
+
+ if (aPath->GetBackendType() != BackendType::CAIRO)
+ return;
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+ path->SetPathOnContext(mContext);
+
+ DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
+}
+
+bool
+DrawTargetCairo::IsCurrentGroupOpaque()
+{
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+
+ if (!surf) {
+ return false;
+ }
+
+ return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR;
+}
+
+void
+DrawTargetCairo::SetFontOptions()
+{
+ // This will attempt to detect if the currently set scaled font on the
+ // context has enabled subpixel AA. If it is not permitted, then it will
+ // downgrade to grayscale AA.
+ // This only currently works effectively for the cairo-ft backend relative
+ // to system defaults, as only cairo-ft reflect system defaults in the scaled
+ // font state. However, this will work for cairo-ft on both tree Cairo and
+ // system Cairo.
+ // Other backends leave the CAIRO_ANTIALIAS_DEFAULT setting untouched while
+ // potentially interpreting it as subpixel or even other types of AA that
+ // can't be safely equivocated with grayscale AA. For this reason we don't
+ // try to also detect and modify the default AA setting, only explicit
+ // subpixel AA. These other backends must instead rely on tree Cairo's
+ // cairo_surface_set_subpixel_antialiasing extension.
+
+ // If allowing subpixel AA, then leave Cairo's default AA state.
+ if (mPermitSubpixelAA) {
+ return;
+ }
+
+ if (!mFontOptions) {
+ mFontOptions = cairo_font_options_create();
+ if (!mFontOptions) {
+ gfxWarning() << "Failed allocating Cairo font options";
+ return;
+ }
+ }
+
+ // If the current font requests subpixel AA, force it to gray since we don't
+ // allow subpixel AA.
+ cairo_get_font_options(mContext, mFontOptions);
+ cairo_antialias_t antialias = cairo_font_options_get_antialias(mFontOptions);
+ if (antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+ cairo_font_options_set_antialias(mFontOptions, CAIRO_ANTIALIAS_GRAY);
+ cairo_set_font_options(mContext, mFontOptions);
+ }
+}
+
+void
+DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
+{
+ DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
+#ifdef MOZ_TREE_CAIRO
+ cairo_surface_set_subpixel_antialiasing(cairo_get_group_target(mContext),
+ aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+#endif
+}
+
+void
+DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions*)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ if (!IsValid()) {
+ gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ return;
+ }
+
+ if (!aFont) {
+ gfxDevCrash(LogReason::InvalidFont) << "Invalid scaled font";
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aPattern);
+
+ ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont);
+ cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
+
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
+ if (!pat)
+ return;
+
+ cairo_set_source(mContext, pat);
+ cairo_pattern_destroy(pat);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ // Override any font-specific options as necessary.
+ SetFontOptions();
+
+ // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
+ // execute millions of times in short periods, so we want to avoid heap
+ // allocation whenever possible. So we use an inline vector capacity of 1024
+ // bytes (the maximum allowed by mozilla::Vector), which gives an inline
+ // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
+ // allocation in ~99% of cases.
+ Vector<cairo_glyph_t, 1024 / sizeof(cairo_glyph_t)> glyphs;
+ if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs)) {
+ gfxDevCrash(LogReason::GlyphAllocFailedCairo) << "glyphs allocation failed";
+ return;
+ }
+ for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
+ glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
+ glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
+ glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
+
+ if (cairo_surface_status(cairo_get_group_target(mContext))) {
+ gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ }
+}
+
+void
+DrawTargetCairo::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clearSource(aSource);
+ AutoClearDeviceOffset clearMask(aMask);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
+ if (!source) {
+ return;
+ }
+
+ cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha, GetTransform());
+ if (!mask) {
+ cairo_pattern_destroy(source);
+ return;
+ }
+
+ if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
+ cairo_pattern_destroy(source);
+ cairo_pattern_destroy(mask);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
+ cairo_set_source(mContext, source);
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+ cairo_mask(mContext, mask);
+
+ cairo_pattern_destroy(mask);
+ cairo_pattern_destroy(source);
+}
+
+void
+DrawTargetCairo::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clearSource(aSource);
+ AutoClearDeviceOffset clearMask(aMask);
+
+ if (!PatternIsCompatible(aSource)) {
+ return;
+ }
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
+ if (!pat) {
+ return;
+ }
+
+ if (cairo_pattern_status(pat)) {
+ cairo_pattern_destroy(pat);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
+ cairo_set_source(mContext, pat);
+
+ if (NeedIntermediateSurface(aSource, aOptions)) {
+ cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // Don't want operators to be applied twice
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+ // Now draw the content using the desired operator
+ cairo_paint_with_alpha(mContext, aOptions.mAlpha);
+
+ cairo_pop_group_to_source(mContext);
+ }
+
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
+ if (!surf) {
+ cairo_pattern_destroy(pat);
+ return;
+ }
+ cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf);
+ cairo_matrix_t matrix;
+
+ cairo_matrix_init_translate (&matrix, -aOffset.x, -aOffset.y);
+ cairo_pattern_set_matrix (mask, &matrix);
+
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+
+ cairo_mask(mContext, mask);
+
+ cairo_surface_destroy(surf);
+ cairo_pattern_destroy(mask);
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::CAIRO) {
+ return;
+ }
+
+ WillChange(aPath);
+ cairo_save(mContext);
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+
+ if (mTransformSingular) {
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, 0, 0);
+ } else {
+ path->SetPathOnContext(mContext);
+ }
+ cairo_clip_preserve(mContext);
+}
+
+void
+DrawTargetCairo::PushClipRect(const Rect& aRect)
+{
+ WillChange();
+ cairo_save(mContext);
+
+ cairo_new_path(mContext);
+ if (mTransformSingular) {
+ cairo_rectangle(mContext, 0, 0, 0, 0);
+ } else {
+ cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+ }
+ cairo_clip_preserve(mContext);
+}
+
+void
+DrawTargetCairo::PopClip()
+{
+ // save/restore does not affect the path, so no need to call WillChange()
+
+ // cairo_restore will restore the transform too and we don't want to do that
+ // so we'll save it now and restore it after the cairo_restore
+ cairo_matrix_t mat;
+ cairo_get_matrix(mContext, &mat);
+
+ cairo_restore(mContext);
+
+ cairo_set_matrix(mContext, &mat);
+}
+
+void
+DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ if (mFormat == SurfaceFormat::A8) {
+ content = CAIRO_CONTENT_ALPHA;
+ } else if (aOpaque) {
+ content = CAIRO_CONTENT_COLOR;
+ }
+
+ if (aCopyBackground) {
+ cairo_surface_t* source = cairo_get_group_target(mContext);
+ cairo_push_group_with_content(mContext, content);
+ cairo_surface_t* dest = cairo_get_group_target(mContext);
+ cairo_t* ctx = cairo_create(dest);
+ cairo_set_source_surface(ctx, source, 0, 0);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+ } else {
+ cairo_push_group_with_content(mContext, content);
+ }
+
+ PushedLayer layer(aOpacity, mPermitSubpixelAA);
+
+ if (aMask) {
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
+ if (surf) {
+ layer.mMaskPattern = cairo_pattern_create_for_surface(surf);
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(aMaskTransform, mat);
+ cairo_matrix_invert(&mat);
+ cairo_pattern_set_matrix(layer.mMaskPattern, &mat);
+ cairo_surface_destroy(surf);
+ } else {
+ gfxCriticalError() << "Failed to get cairo surface for mask surface!";
+ }
+ }
+
+ mPushedLayers.push_back(layer);
+
+ SetPermitSubpixelAA(aOpaque);
+}
+
+void
+DrawTargetCairo::PopLayer()
+{
+ MOZ_ASSERT(mPushedLayers.size());
+
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+ cairo_pop_group_to_source(mContext);
+
+ PushedLayer layer = mPushedLayers.back();
+ mPushedLayers.pop_back();
+
+ if (!layer.mMaskPattern) {
+ cairo_paint_with_alpha(mContext, layer.mOpacity);
+ } else {
+ if (layer.mOpacity != Float(1.0)) {
+ cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // Now draw the content using the desired operator
+ cairo_paint_with_alpha(mContext, layer.mOpacity);
+
+ cairo_pop_group_to_source(mContext);
+ }
+ cairo_mask(mContext, layer.mMaskPattern);
+ }
+
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mTransform, mat);
+ cairo_set_matrix(mContext, &mat);
+
+ cairo_pattern_destroy(layer.mMaskPattern);
+ SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA);
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
+{
+ return MakeAndAddRef<PathBuilderCairo>(aFillRule);
+}
+
+void
+DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
+{
+ if (aOperator != CompositionOp::OP_SOURCE)
+ return;
+ cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
+ // It doesn't really matter what the source is here, since Paint
+ // isn't bounded by the source and the mask covers the entire clip
+ // region.
+ cairo_paint(mContext);
+}
+
+
+already_AddRefed<GradientStops>
+DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
+ ExtendMode aExtendMode) const
+{
+ return MakeAndAddRef<GradientStopsCairo>(aStops, aNumStops, aExtendMode);
+}
+
+already_AddRefed<FilterNode>
+DrawTargetCairo::CreateFilter(FilterType aType)
+{
+ return FilterNodeSoftware::Create(aType);
+}
+
+void
+DrawTargetCairo::GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ cairo_glyph_t glyph;
+ cairo_text_extents_t extents;
+ glyph.index = aGlyphIndices[i];
+ glyph.x = 0;
+ glyph.y = 0;
+ cairo_glyph_extents(mContext, &glyph, 1, &extents);
+
+ aGlyphMetrics[i].mXBearing = extents.x_bearing;
+ aGlyphMetrics[i].mXAdvance = extents.x_advance;
+ aGlyphMetrics[i].mYBearing = extents.y_bearing;
+ aGlyphMetrics[i].mYAdvance = extents.y_advance;
+ aGlyphMetrics[i].mWidth = extents.width;
+ aGlyphMetrics[i].mHeight = extents.height;
+ }
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ if (!aData) {
+ gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
+ return nullptr;
+ }
+
+ cairo_surface_t* surf = CopyToImageSurface(aData, IntRect(IntPoint(), aSize),
+ aStride, aFormat);
+ if (!surf) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
+ cairo_surface_destroy(surf);
+
+ return source_surf.forget();
+}
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+ DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+ ~DestroyPixmapClosure() {
+ XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
+ }
+ Drawable mPixmap;
+ Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+ delete static_cast<DestroyPixmapClosure*>(data);
+}
+#endif
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ RefPtr<SourceSurface> surface(aSurface);
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ cairo_surface_type_t ctype = cairo_surface_get_type(mSurface);
+ if (aSurface->GetType() == SurfaceType::CAIRO &&
+ cairo_surface_get_type(
+ static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface()) == ctype) {
+ return surface.forget();
+ }
+
+ if (ctype != CAIRO_SURFACE_TYPE_XLIB) {
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ if (!size.width || !size.height) {
+ return surface.forget();
+ }
+
+ // Although the dimension parameters in the xCreatePixmapReq wire protocol are
+ // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+ // either dimension cannot be represented by a 16-bit *signed* integer.
+ #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+ if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ return surface.forget();
+ }
+
+ SurfaceFormat format = aSurface->GetFormat();
+ Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+ Display *dpy = DisplayOfScreen(screen);
+ XRenderPictFormat* xrenderFormat = nullptr;
+ switch (format) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+ break;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+ break;
+ case SurfaceFormat::A8:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+ break;
+ default:
+ return surface.forget();
+ }
+ if (!xrenderFormat) {
+ return surface.forget();
+ }
+
+ Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+ size.width, size.height,
+ xrenderFormat->depth);
+ if (!pixmap) {
+ return surface.forget();
+ }
+
+ auto closure = MakeUnique<DestroyPixmapClosure>(pixmap, screen);
+
+ ScopedCairoSurface csurf(
+ cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
+ screen, xrenderFormat,
+ size.width, size.height));
+ if (!csurf || cairo_surface_status(csurf)) {
+ return surface.forget();
+ }
+
+ cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+ closure.release(), DestroyPixmap);
+
+ RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+ if (!dt->Init(csurf, size, &format)) {
+ return surface.forget();
+ }
+
+ dt->CopySurface(aSurface,
+ IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ dt->Flush();
+
+ surface = new SourceSurfaceCairo(csurf, size, format);
+#endif
+
+ return surface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+ return nullptr;
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ if (cairo_surface_status(cairo_get_group_target(mContext))) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->Init(aSize, aFormat)) {
+ return target.forget();
+ }
+ }
+
+ cairo_surface_t* similar;
+ switch (cairo_surface_get_type(mSurface)) {
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ case CAIRO_SURFACE_TYPE_WIN32:
+ similar = cairo_win32_surface_create_with_dib(
+ GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
+ break;
+#endif
+ default:
+ similar = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(aFormat),
+ aSize.width, aSize.height);
+ break;
+ }
+
+ if (!cairo_surface_status(similar)) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(similar, aSize)) {
+ return target.forget();
+ }
+ }
+
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar) << cairo_surface_status(cairo_get_group_target(mContext)) << " format " << (int)aFormat;
+ cairo_surface_destroy(similar);
+
+ return nullptr;
+}
+
+bool
+DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ if (cairo_surface_status(aSurface)) {
+ gfxCriticalNote
+ << "Attempt to create DrawTarget for invalid surface. "
+ << aSize << " Cairo Status: " << cairo_surface_status(aSurface);
+ cairo_surface_destroy(aSurface);
+ return false;
+ }
+
+ mContext = cairo_create(aSurface);
+ mSurface = aSurface;
+ mSize = aSize;
+ mFormat = aFormat ? *aFormat : GfxFormatForCairoSurface(aSurface);
+
+ // Cairo image surface have a bug where they will allocate a mask surface (for clipping)
+ // the size of the clip extents, and don't take the surface extents into account.
+ // Add a manual clip to the surface extents to prevent this.
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, mSize.width, mSize.height);
+ cairo_clip(mContext);
+
+ if (mFormat == SurfaceFormat::A8R8G8B8_UINT32 ||
+ mFormat == SurfaceFormat::R8G8B8A8) {
+ SetPermitSubpixelAA(false);
+ } else {
+ SetPermitSubpixelAA(true);
+ }
+
+ return true;
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const
+{
+ cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
+ GfxFormatToCairoContent(aFormat),
+ aSize.width, aSize.height);
+
+ if (cairo_surface_status(similar)) {
+ return nullptr;
+ }
+
+ // If we don't have a blur then we can use the RGBA mask and keep all the
+ // operations in graphics memory.
+ if (aSigma == 0.0F) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(similar, aSize)) {
+ return target.forget();
+ } else {
+ return nullptr;
+ }
+ }
+
+ cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
+ aSize.width,
+ aSize.height);
+
+ if (cairo_surface_status(blursurf)) {
+ return nullptr;
+ }
+
+ cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
+ cairo_surface_destroy(blursurf);
+ if (cairo_surface_status(tee)) {
+ cairo_surface_destroy(similar);
+ return nullptr;
+ }
+
+ cairo_tee_surface_add(tee, similar);
+ cairo_surface_destroy(similar);
+
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(tee, aSize)) {
+ return target.forget();
+ }
+ return nullptr;
+}
+
+static inline pixman_format_code_t
+GfxFormatToPixmanFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return PIXMAN_a8r8g8b8;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return PIXMAN_x8r8g8b8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return PIXMAN_r5g6b5;
+ case SurfaceFormat::A8:
+ return PIXMAN_a8;
+ default:
+ // Allow both BGRA and ARGB formats to be passed through unmodified,
+ // even though even though we are actually rendering to A8R8G8B8_UINT32.
+ if (aFormat == SurfaceFormat::B8G8R8A8 ||
+ aFormat == SurfaceFormat::A8R8G8B8) {
+ return PIXMAN_a8r8g8b8;
+ }
+ return (pixman_format_code_t)0;
+ }
+}
+
+static inline bool
+GfxMatrixToPixmanTransform(const Matrix4x4 &aMatrix, pixman_transform* aResult)
+{
+ pixman_f_transform fTransform = {{
+ { aMatrix._11, aMatrix._21, aMatrix._41 },
+ { aMatrix._12, aMatrix._22, aMatrix._42 },
+ { aMatrix._14, aMatrix._24, aMatrix._44 }
+ }};
+ return pixman_transform_from_pixman_f_transform(aResult, &fTransform);
+}
+
+#ifndef USE_SKIA
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ // Composite the 3D transform with the DT's transform.
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ // Transform the surface bounds and clip to this DT.
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ // Offset the matrix by the transformed origin.
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ // Invert the matrix into a pattern matrix for pixman.
+ if (!fullMat.Invert()) {
+ return false;
+ }
+ pixman_transform xform;
+ if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+ return false;
+ }
+
+ // Read in the source data.
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+ pixman_format_code_t srcFormat = GfxFormatToPixmanFormat(srcSurf->GetFormat());
+ if (!srcFormat) {
+ return false;
+ }
+ DataSourceSurface::ScopedMap srcMap(srcSurf, DataSourceSurface::READ);
+ if (!srcMap.IsMapped()) {
+ return false;
+ }
+
+ // Set up an intermediate destination surface only the size of the transformed bounds.
+ // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+ RefPtr<DataSourceSurface> dstSurf =
+ Factory::CreateDataSourceSurface(xformBounds.Size(),
+ srcFormat == PIXMAN_a8r8g8b8 ?
+ srcSurf->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32);
+ if (!dstSurf) {
+ return false;
+ }
+
+ // Wrap the surfaces in pixman images and do the transform.
+ pixman_image_t* dst =
+ pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ xformBounds.width, xformBounds.height,
+ (uint32_t*)dstSurf->GetData(), dstSurf->Stride());
+ if (!dst) {
+ return false;
+ }
+ pixman_image_t* src =
+ pixman_image_create_bits(srcFormat,
+ srcSurf->GetSize().width, srcSurf->GetSize().height,
+ (uint32_t*)srcMap.GetData(), srcMap.GetStride());
+ if (!src) {
+ pixman_image_unref(dst);
+ return false;
+ }
+
+ pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, nullptr, 0);
+ pixman_image_set_transform(src, &xform);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, nullptr, dst,
+ 0, 0, 0, 0, 0, 0,
+ xformBounds.width, xformBounds.height);
+
+ pixman_image_unref(dst);
+ pixman_image_unref(src);
+
+ // Temporarily reset the DT's transform, since it has already been composed above.
+ Matrix origTransform = mTransform;
+ SetTransform(Matrix());
+
+ // Draw the transformed surface within the transformed bounds.
+ DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+ SetTransform(origTransform);
+
+ return true;
+}
+#endif
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static bool gXRenderInitialized = false;
+static bool gXRenderHasTransform = false;
+
+static bool
+SupportsXRender(cairo_surface_t* surface)
+{
+ if (!surface ||
+ cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_XLIB ||
+ !cairo_xlib_surface_get_xrender_format(surface)) {
+ return false;
+ }
+
+ if (gXRenderInitialized) {
+ return true;
+ }
+ gXRenderInitialized = true;
+
+ cairo_device_t* device = cairo_surface_get_device(surface);
+ if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+ return false;
+ }
+
+ Display* display = cairo_xlib_surface_get_display(surface);
+ int major, minor;
+ if (XRenderQueryVersion(display, &major, &minor)) {
+ if (major > 0 || (major == 0 && minor >= 6)) {
+ gXRenderHasTransform = true;
+ }
+ }
+
+ cairo_device_release(device);
+
+ return true;
+}
+#endif
+
+bool
+DrawTargetCairo::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+#if CAIRO_HAS_XLIB_SURFACE
+ cairo_surface_t* srcSurf =
+ aSurface->GetType() == SurfaceType::CAIRO ?
+ static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface() : nullptr;
+ if (!SupportsXRender(srcSurf) || !gXRenderHasTransform) {
+ return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+ }
+
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ if (!fullMat.Invert()) {
+ return false;
+ }
+ pixman_transform xform;
+ if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+ return false;
+ }
+
+ cairo_surface_t* xformSurf =
+ cairo_surface_create_similar(srcSurf, CAIRO_CONTENT_COLOR_ALPHA,
+ xformBounds.width, xformBounds.height);
+ if (!SupportsXRender(xformSurf)) {
+ cairo_surface_destroy(xformSurf);
+ return false;
+ }
+ cairo_device_t* device = cairo_surface_get_device(xformSurf);
+ if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(xformSurf);
+ return false;
+ }
+
+ Display* display = cairo_xlib_surface_get_display(xformSurf);
+
+ Picture srcPict = XRenderCreatePicture(display,
+ cairo_xlib_surface_get_drawable(srcSurf),
+ cairo_xlib_surface_get_xrender_format(srcSurf),
+ 0, nullptr);
+ XRenderSetPictureFilter(display, srcPict, FilterBilinear, nullptr, 0);
+ XRenderSetPictureTransform(display, srcPict, (XTransform*)&xform);
+
+ Picture dstPict = XRenderCreatePicture(display,
+ cairo_xlib_surface_get_drawable(xformSurf),
+ cairo_xlib_surface_get_xrender_format(xformSurf),
+ 0, nullptr);
+
+ XRenderComposite(display, PictOpSrc,
+ srcPict, X11None, dstPict,
+ 0, 0, 0, 0, 0, 0,
+ xformBounds.width, xformBounds.height);
+
+ XRenderFreePicture(display, srcPict);
+ XRenderFreePicture(display, dstPict);
+
+ cairo_device_release(device);
+ cairo_surface_mark_dirty(xformSurf);
+
+ AutoPrepareForDrawing(this, mContext);
+
+ cairo_identity_matrix(mContext);
+
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_DEFAULT);
+ cairo_set_source_surface(mContext, xformSurf, xformBounds.x, xformBounds.y);
+
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, xformBounds.x, xformBounds.y, xformBounds.width, xformBounds.height);
+ cairo_fill(mContext);
+
+ cairo_surface_destroy(xformSurf);
+
+ return true;
+#else
+ return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+#endif
+}
+
+bool
+DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ cairo_surface_reference(aSurface);
+ return InitAlreadyReferenced(aSurface, aSize, aFormat);
+}
+
+bool
+DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ cairo_surface_t *surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
+ return InitAlreadyReferenced(surf, aSize);
+}
+
+bool
+DrawTargetCairo::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
+{
+ cairo_surface_t* surf =
+ cairo_image_surface_create_for_data(aData,
+ GfxFormatToCairoFormat(aFormat),
+ aSize.width,
+ aSize.height,
+ aStride);
+ return InitAlreadyReferenced(surf, aSize);
+}
+
+void *
+DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
+{
+ if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
+ return mContext;
+ }
+
+ return nullptr;
+}
+
+void
+DrawTargetCairo::MarkSnapshotIndependent()
+{
+ if (mSnapshot) {
+ if (mSnapshot->refCount() > 1) {
+ // We only need to worry about snapshots that someone else knows about
+ mSnapshot->DrawTargetWillChange();
+ }
+ mSnapshot = nullptr;
+ }
+}
+
+void
+DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
+{
+ MarkSnapshotIndependent();
+ MOZ_ASSERT(!mLockedBits);
+}
+
+void
+DrawTargetCairo::SetTransform(const Matrix& aTransform)
+{
+ DrawTarget::SetTransform(aTransform);
+
+ mTransformSingular = aTransform.IsSingular();
+ if (!mTransformSingular) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mTransform, mat);
+ cairo_set_matrix(mContext, &mat);
+ }
+}
+
+Rect
+DrawTargetCairo::GetUserSpaceClip()
+{
+ double clipX1, clipY1, clipX2, clipY2;
+ cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2);
+ return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats
+}
+
+cairo_t*
+BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
+{
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return nullptr;
+ }
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+
+ cairoDT->WillChange();
+
+ // save the state to make it easier for callers to avoid mucking with things
+ cairo_save(cairoDT->mContext);
+
+ // Neuter the DrawTarget while the context is being borrowed
+ cairo_t* cairo = cairoDT->mContext;
+ cairoDT->mContext = nullptr;
+
+ return cairo;
+}
+
+void
+BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
+ cairo_t* aCairo)
+{
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return;
+ }
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+
+ cairo_restore(aCairo);
+ cairoDT->mContext = aCairo;
+}
+
+#ifdef MOZ_X11
+bool
+BorrowedXlibDrawable::Init(DrawTarget* aDT)
+{
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ mDrawable = X11None;
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return false;
+ }
+
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+ cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext);
+ if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_XLIB) {
+ return false;
+ }
+ cairo_surface_flush(surf);
+
+ cairoDT->WillChange();
+
+ mDisplay = cairo_xlib_surface_get_display(surf);
+ mDrawable = cairo_xlib_surface_get_drawable(surf);
+ mScreen = cairo_xlib_surface_get_screen(surf);
+ mVisual = cairo_xlib_surface_get_visual(surf);
+ mXRenderFormat = cairo_xlib_surface_get_xrender_format(surf);
+ mSize.width = cairo_xlib_surface_get_width(surf);
+ mSize.height = cairo_xlib_surface_get_height(surf);
+
+ double x = 0, y = 0;
+ cairo_surface_get_device_offset(surf, &x, &y);
+ mOffset = Point(x, y);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+void
+BorrowedXlibDrawable::Finish()
+{
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(mDT);
+ cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext);
+ cairo_surface_mark_dirty(surf);
+ if (mDrawable) {
+ mDrawable = X11None;
+ }
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetCairo.h b/system/graphics/2d/DrawTargetCairo.h
new file mode 100644
index 000000000..bd0415b06
--- /dev/null
+++ b/system/graphics/2d/DrawTargetCairo.h
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
+#define _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include "PathCairo.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceCairo;
+
+class GradientStopsCairo : public GradientStops
+{
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCairo)
+ GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops,
+ ExtendMode aExtendMode)
+ : mExtendMode(aExtendMode)
+ {
+ for (uint32_t i = 0; i < aNumStops; ++i) {
+ mStops.push_back(aStops[i]);
+ }
+ }
+
+ virtual ~GradientStopsCairo() {}
+
+ const std::vector<GradientStop>& GetStops() const
+ {
+ return mStops;
+ }
+
+ ExtendMode GetExtendMode() const
+ {
+ return mExtendMode;
+ }
+
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
+
+ private:
+ std::vector<GradientStop> mStops;
+ ExtendMode mExtendMode;
+};
+
+class DrawTargetCairo final : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
+ friend class BorrowedCairoContext;
+ friend class BorrowedXlibDrawable;
+
+ DrawTargetCairo();
+ virtual ~DrawTargetCairo();
+
+ virtual bool IsValid() const override;
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override;
+
+ virtual bool IsCurrentGroupOpaque() override;
+
+ virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
+
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin = nullptr) override;
+ virtual void ReleaseBits(uint8_t* aData) override;
+
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override;
+
+ virtual void ClearRect(const Rect &aRect) override;
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+ virtual void CopyRect(const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix) override;
+
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const override;
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
+
+ bool Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
+ bool Init(const IntSize& aSize, SurfaceFormat aFormat);
+ bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
+
+ virtual void SetTransform(const Matrix& aTransform) override;
+
+ virtual void DetachAllSnapshots() override { MarkSnapshotIndependent(); }
+
+ // Call to set up aContext for drawing (with the current transform, etc).
+ // Pass the path you're going to be using if you have one.
+ // Implicitly calls WillChange(aPath).
+ void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
+
+ static cairo_surface_t *GetDummySurface();
+
+ // Cairo hardcodes this as its maximum surface size.
+ static size_t GetMaxSurfaceSize() {
+ return 32767;
+ }
+
+private: // methods
+ // Init cairo surface without doing a cairo_surface_reference() call.
+ bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
+ enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
+ void DrawPattern(const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions,
+ DrawPatternType aDrawType,
+ bool aPathBoundsClip = false);
+
+ void CopySurfaceInternal(cairo_surface_t* aSurface,
+ const IntRect& aSource,
+ const IntPoint& aDest);
+
+ Rect GetUserSpaceClip();
+
+ // Call before you make any changes to the backing surface with which this
+ // context is associated. Pass the path you're going to be using if you have
+ // one.
+ void WillChange(const Path* aPath = nullptr);
+
+ // Call if there is any reason to disassociate the snapshot from this draw
+ // target; for example, because we're going to be destroyed.
+ void MarkSnapshotIndependent();
+
+ // If the current operator is "source" then clear the destination before we
+ // draw into it, to simulate the effect of an unbounded source operator.
+ void ClearSurfaceForUnboundedSource(const CompositionOp &aOperator);
+
+ // Set the Cairo context font options according to the current draw target
+ // font state.
+ void SetFontOptions();
+
+private: // data
+ cairo_t* mContext;
+ cairo_surface_t* mSurface;
+ IntSize mSize;
+ bool mTransformSingular;
+
+ uint8_t* mLockedBits;
+
+ cairo_font_options_t* mFontOptions;
+
+ struct PushedLayer
+ {
+ PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA)
+ : mOpacity(aOpacity)
+ , mMaskPattern(nullptr)
+ , mWasPermittingSubpixelAA(aWasPermittingSubpixelAA)
+ {}
+ Float mOpacity;
+ cairo_pattern_t* mMaskPattern;
+ bool mWasPermittingSubpixelAA;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+
+ // The latest snapshot of this surface. This needs to be told when this
+ // target is modified. We keep it alive as a cache.
+ RefPtr<SourceSurfaceCairo> mSnapshot;
+ static cairo_surface_t *mDummySurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
diff --git a/system/graphics/2d/DrawTargetCapture.cpp b/system/graphics/2d/DrawTargetCapture.cpp
new file mode 100644
index 000000000..f0584c427
--- /dev/null
+++ b/system/graphics/2d/DrawTargetCapture.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetCapture.h"
+#include "DrawCommand.h"
+
+namespace mozilla {
+namespace gfx {
+
+
+DrawTargetCaptureImpl::~DrawTargetCaptureImpl()
+{
+ uint8_t* start = &mDrawCommandStorage.front();
+
+ uint8_t* current = start;
+
+ while (current < start + mDrawCommandStorage.size()) {
+ reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->~DrawingCommand();
+ current += *(uint32_t*)current;
+ }
+}
+
+bool
+DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT)
+{
+ if (!aRefDT) {
+ return false;
+ }
+
+ mRefDT = aRefDT;
+
+ mSize = aSize;
+ return true;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCaptureImpl::Snapshot()
+{
+ RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat());
+
+ ReplayToDrawTarget(dt, Matrix());
+
+ return dt->Snapshot();
+}
+
+void
+DrawTargetCaptureImpl::DetachAllSnapshots()
+{}
+
+#define AppendCommand(arg) new (AppendToCommandList<arg>()) arg
+
+void
+DrawTargetCaptureImpl::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ aSurface->GuaranteePersistance();
+ AppendCommand(DrawSurfaceCommand)(aSurface, aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ // @todo XXX - this won't work properly long term yet due to filternodes not
+ // being immutable.
+ AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::ClearRect(const Rect &aRect)
+{
+ AppendCommand(ClearRectCommand)(aRect);
+}
+
+void
+DrawTargetCaptureImpl::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ aMask->GuaranteePersistance();
+ AppendCommand(MaskSurfaceCommand)(aSource, aMask, aOffset, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::CopySurface(SourceSurface* aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint& aDestination)
+{
+ aSurface->GuaranteePersistance();
+ AppendCommand(CopySurfaceCommand)(aSurface, aSourceRect, aDestination);
+}
+
+void
+DrawTargetCaptureImpl::FillRect(const Rect& aRect,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(FillRectCommand)(aRect, aPattern, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::StrokeRect(const Rect& aRect,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeRectCommand)(aRect, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::StrokeLine(const Point& aStart,
+ const Point& aEnd,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeLineCommand)(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::Stroke(const Path* aPath,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeCommand)(aPath, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::Fill(const Path* aPath,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(FillCommand)(aPath, aPattern, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::FillGlyphs(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions)
+{
+ AppendCommand(FillGlyphsCommand)(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetCaptureImpl::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ AppendCommand(MaskCommand)(aSource, aMask, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::PushClip(const Path* aPath)
+{
+ AppendCommand(PushClipCommand)(aPath);
+}
+
+void
+DrawTargetCaptureImpl::PushClipRect(const Rect& aRect)
+{
+ AppendCommand(PushClipRectCommand)(aRect);
+}
+
+void
+DrawTargetCaptureImpl::PopClip()
+{
+ AppendCommand(PopClipCommand)();
+}
+
+void
+DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform)
+{
+ AppendCommand(SetTransformCommand)(aTransform);
+}
+
+void
+DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform)
+{
+ uint8_t* start = &mDrawCommandStorage.front();
+
+ uint8_t* current = start;
+
+ while (current < start + mDrawCommandStorage.size()) {
+ reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, &aTransform);
+ current += *(uint32_t*)current;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetCapture.h b/system/graphics/2d/DrawTargetCapture.h
new file mode 100644
index 000000000..a60e07b56
--- /dev/null
+++ b/system/graphics/2d/DrawTargetCapture.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETCAPTURE_H_
+#define MOZILLA_GFX_DRAWTARGETCAPTURE_H_
+
+#include "2D.h"
+#include <vector>
+
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawingCommand;
+
+class DrawTargetCaptureImpl : public DrawTargetCapture
+{
+public:
+ DrawTargetCaptureImpl()
+ {}
+
+ bool Init(const IntSize& aSize, DrawTarget* aRefDT);
+
+ virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); }
+ virtual DrawTargetType GetType() const { return mRefDT->GetType(); }
+
+ virtual already_AddRefed<SourceSurface> Snapshot();
+
+ virtual void DetachAllSnapshots();
+
+ virtual IntSize GetSize() { return mSize; }
+
+ virtual void Flush() {}
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions);
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) { /* Not implemented */ }
+
+ virtual void ClearRect(const Rect &aRect);
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions());
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination);
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr);
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void PushClip(const Path *aPath);
+ virtual void PushClipRect(const Rect &aRect);
+ virtual void PopClip();
+
+ virtual void SetTransform(const Matrix &aTransform);
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+ {
+ return mRefDT->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+ {
+ return mRefDT->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
+ {
+ return mRefDT->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const
+ {
+ return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType)
+ {
+ return mRefDT->CreateFilter(aType);
+ }
+
+ void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);
+
+protected:
+ ~DrawTargetCaptureImpl();
+
+private:
+
+ // This storage system was used to minimize the amount of heap allocations
+ // that are required while recording. It should be noted there's no
+ // guarantees on the alignments of DrawingCommands allocated in this array.
+ template<typename T>
+ T* AppendToCommandList()
+ {
+ size_t oldSize = mDrawCommandStorage.size();
+ mDrawCommandStorage.resize(mDrawCommandStorage.size() + sizeof(T) + sizeof(uint32_t));
+ uint8_t* nextDrawLocation = &mDrawCommandStorage.front() + oldSize;
+ *(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t);
+ return reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t));
+ }
+ RefPtr<DrawTarget> mRefDT;
+
+ IntSize mSize;
+
+ std::vector<uint8_t> mDrawCommandStorage;
+};
+
+} // namespace gfx
+
+} // namespace mozilla
+
+
+#endif /* MOZILLA_GFX_DRAWTARGETCAPTURE_H_ */
diff --git a/system/graphics/2d/DrawTargetD2D1.cpp b/system/graphics/2d/DrawTargetD2D1.cpp
new file mode 100644
index 000000000..a2e854107
--- /dev/null
+++ b/system/graphics/2d/DrawTargetD2D1.cpp
@@ -0,0 +1,1932 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <initguid.h>
+#include "DrawTargetD2D1.h"
+#include "FilterNodeSoftware.h"
+#include "GradientStopsD2D.h"
+#include "SourceSurfaceD2D1.h"
+#include "RadialGradientEffectD2D1.h"
+
+#include "HelpersD2D.h"
+#include "FilterNodeD2D1.h"
+#include "ExtendInputEffectD2D1.h"
+#include "Tools.h"
+#include "nsWindowsHelpers.h"
+
+using namespace std;
+
+// decltype is not usable for overloaded functions.
+typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType,
+ REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ void **factory
+);
+
+namespace mozilla {
+namespace gfx {
+
+uint64_t DrawTargetD2D1::mVRAMUsageDT;
+uint64_t DrawTargetD2D1::mVRAMUsageSS;
+IDWriteFactory *DrawTargetD2D1::mDWriteFactory;
+ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
+
+ID2D1Factory1 *D2DFactory1()
+{
+ return DrawTargetD2D1::factory();
+}
+
+DrawTargetD2D1::DrawTargetD2D1()
+ : mPushedLayers(1)
+ , mUsedCommandListsSincePurge(0)
+ , mDidComplexBlendWithListInList(false)
+{
+}
+
+DrawTargetD2D1::~DrawTargetD2D1()
+{
+ PopAllClips();
+
+ if (mSnapshot) {
+ // We may hold the only reference. MarkIndependent will clear mSnapshot;
+ // keep the snapshot object alive so it doesn't get destroyed while
+ // MarkIndependent is running.
+ RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
+ // mSnapshot can be treated as independent of this DrawTarget since we know
+ // this DrawTarget won't change again.
+ deathGrip->MarkIndependent();
+ // mSnapshot will be cleared now.
+ }
+
+ if (mDC) {
+ // The only way mDC can be null is if Init failed, but it can happen and the
+ // destructor is the only place where we need to check for it since the
+ // DrawTarget will destroyed right after Init fails.
+ mDC->EndDraw();
+ }
+
+ // Targets depending on us can break that dependency, since we're obviously not going to
+ // be modified in the future.
+ for (auto iter = mDependentTargets.begin();
+ iter != mDependentTargets.end(); iter++) {
+ (*iter)->mDependingOnTargets.erase(this);
+ }
+ // Our dependencies on other targets no longer matter.
+ for (TargetSet::iterator iter = mDependingOnTargets.begin();
+ iter != mDependingOnTargets.end(); iter++) {
+ (*iter)->mDependentTargets.erase(this);
+ }
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::Snapshot()
+{
+ if (mSnapshot) {
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+ }
+ PopAllClips();
+
+ Flush();
+
+ mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
+
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+}
+
+// Command lists are kept around by device contexts until EndDraw is called,
+// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
+// are expensive though, especially relatively when little work is done, so
+// we try to reduce the amount of times we execute these purges.
+static const uint32_t kPushedLayersBeforePurge = 25;
+
+void
+DrawTargetD2D1::Flush()
+{
+ if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+ mPushedLayers.size() == 1) {
+ // It's important to pop all clips as otherwise layers can forget about
+ // their clip when doing an EndDraw. When we have layers pushed we cannot
+ // easily pop all underlying clips to delay the purge until we have no
+ // layers pushed.
+ PopAllClips();
+ mUsedCommandListsSincePurge = 0;
+ mDC->EndDraw();
+ mDC->BeginDraw();
+ } else {
+ mDC->Flush();
+ }
+
+ // We no longer depend on any target.
+ for (TargetSet::iterator iter = mDependingOnTargets.begin();
+ iter != mDependingOnTargets.end(); iter++) {
+ (*iter)->mDependentTargets.erase(this);
+ }
+ mDependingOnTargets.clear();
+}
+
+void
+DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+
+ D2D1_RECT_F samplingBounds;
+
+ if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) {
+ samplingBounds = D2DRect(aSource);
+ } else {
+ samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height));
+ }
+
+ Float xScale = aDest.width / aSource.width;
+ Float yScale = aDest.height / aSource.height;
+
+ RefPtr<ID2D1ImageBrush> brush;
+
+ // Here we scale the source pattern up to the size and position where we want
+ // it to be.
+ Matrix transform;
+ transform.PreTranslate(aDest.x - aSource.x * xScale, aDest.y - aSource.y * yScale);
+ transform.PreScale(xScale, yScale);
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, transform, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << *this << ": Unable to get D2D image for surface.";
+ return;
+ }
+
+ RefPtr<ID2D1Bitmap> bitmap;
+ if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ // If this is called with a DataSourceSurface it might do a partial upload
+ // that our DrawBitmap call doesn't support.
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ }
+
+ if (bitmap && aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
+ mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha,
+ D2DFilter(aSurfOptions.mSamplingFilter), D2DRect(aSource));
+ } else {
+ // This has issues ignoring the alpha channel on windows 7 with images marked opaque.
+ MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
+
+ // Bug 1275478 - D2D1 cannot draw A8 surface correctly.
+ MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::A8);
+
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2DInterpolationMode(aSurfOptions.mSamplingFilter)),
+ D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
+ getter_AddRefs(brush));
+ mDC->FillRectangle(D2DRect(aDest), brush);
+ }
+
+ FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+}
+
+void
+DrawTargetD2D1::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
+ gfxWarning() << *this << ": Incompatible filter passed to DrawFilter.";
+ return;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
+ node->WillDraw(this);
+
+ mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
+
+ FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+}
+
+void
+DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ MarkChanged();
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ Matrix mat;
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Couldn't get image for surface.";
+ return;
+ }
+
+ if (!mat.IsIdentity()) {
+ gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces.";
+ return;
+ }
+
+ // Step 1, create the shadow effect.
+ RefPtr<ID2D1Effect> shadowEffect;
+ HRESULT hr = mDC->CreateEffect(CLSID_D2D1Shadow, getter_AddRefs(shadowEffect));
+ if (FAILED(hr) || !shadowEffect) {
+ gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr);
+ return;
+ }
+ shadowEffect->SetInput(0, image);
+ shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
+ D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
+ shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
+
+ D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
+ mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+
+ D2D1_POINT_2F imgPoint = D2DPoint(aDest);
+ mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+}
+
+void
+DrawTargetD2D1::ClearRect(const Rect &aRect)
+{
+ MarkChanged();
+
+ PopAllClips();
+
+ PushClipRect(aRect);
+
+ if (mTransformDirty ||
+ !mTransform.IsIdentity()) {
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+ }
+
+ D2D1_RECT_F clipRect;
+ bool isPixelAligned;
+ if (mTransform.IsRectilinear() &&
+ GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
+ mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->Clear();
+ mDC->PopAxisAlignedClip();
+
+ PopClip();
+ return;
+ }
+
+ RefPtr<ID2D1CommandList> list;
+ mUsedCommandListsSincePurge++;
+ mDC->CreateCommandList(getter_AddRefs(list));
+ mDC->SetTarget(list);
+
+ IntRect addClipRect;
+ RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
+ mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->FillGeometry(geom, brush);
+ mDC->PopAxisAlignedClip();
+
+ mDC->SetTarget(CurrentTarget());
+ list->Close();
+
+ mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
+
+ PopClip();
+
+ return;
+}
+
+void
+DrawTargetD2D1::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+
+ RefPtr<ID2D1Bitmap> bitmap;
+
+ Matrix mat = Matrix::Translation(aOffset);
+ RefPtr<ID2D1Image> image = GetImageForSurface(aMask, mat, ExtendMode::CLAMP, nullptr);
+
+ MOZ_ASSERT(!mat.HasNonTranslation());
+ aOffset.x = mat._31;
+ aOffset.y = mat._32;
+
+ if (!image) {
+ gfxWarning() << "Failed to get image for surface.";
+ return;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, aSource);
+
+ // FillOpacityMask only works if the antialias mode is MODE_ALIASED
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (!bitmap) {
+ gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
+ return;
+ }
+
+ IntSize size = IntSize::Truncate(bitmap->GetSize().width, bitmap->GetSize().height);
+
+ Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
+
+ Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
+ mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
+
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aSource);
+}
+
+void
+DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ MarkChanged();
+
+ PopAllClips();
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ Matrix mat = Matrix::Translation(aDestination.x - aSourceRect.x, aDestination.y - aSourceRect.y);
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Couldn't get image for surface.";
+ return;
+ }
+
+ if (mat.HasNonIntegerTranslation()) {
+ gfxDebug() << *this << ": At this point scaled partial uploads are not supported for CopySurface.";
+ return;
+ }
+
+ IntRect sourceRect = aSourceRect;
+ sourceRect.x += (aDestination.x - aSourceRect.x) - mat._31;
+ sourceRect.width -= (aDestination.x - aSourceRect.x) - mat._31;
+ sourceRect.y += (aDestination.y - aSourceRect.y) - mat._32;
+ sourceRect.height -= (aDestination.y - aSourceRect.y) - mat._32;
+
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+ if (bitmap && mFormat == SurfaceFormat::A8) {
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
+ D2D1::BrushProperties(), getter_AddRefs(brush));
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ Rect srcRect(Float(sourceRect.x), Float(sourceRect.y),
+ Float(aSourceRect.width), Float(aSourceRect.height));
+
+ Rect dstRect(Float(aDestination.x), Float(aDestination.y),
+ Float(aSourceRect.width), Float(aSourceRect.height));
+
+ if (bitmap) {
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
+ D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
+ D2DRect(srcRect));
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
+ D2DRect(srcRect), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
+ D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+}
+
+void
+DrawTargetD2D1::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ mDC->FillRectangle(D2DRect(aRect), brush);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
+ return;
+ }
+ const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ if (!aPath || aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
+ return;
+ }
+ const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+
+ mDC->FillGeometry(d2dPath->mGeometry, brush);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ if (aFont->GetType() != FontType::DWRITE) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
+ return;
+ }
+
+ ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
+
+ IDWriteRenderingParams *params = nullptr;
+ if (aRenderingOptions) {
+ if (aRenderingOptions->GetType() != FontType::DWRITE) {
+ gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
+ // This should never happen.
+ MOZ_ASSERT(false);
+ } else {
+ params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams;
+ }
+ }
+
+ AntialiasMode aaMode = font->GetDefaultAAMode();
+
+ if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
+ aaMode = aOptions.mAntialiasMode;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ bool forceClearType = false;
+ if (!CurrentLayer().mIsOpaque && mPermitSubpixelAA &&
+ aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) {
+ forceClearType = true;
+ }
+
+
+ D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+
+ switch (aaMode) {
+ case AntialiasMode::NONE:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+ break;
+ case AntialiasMode::GRAY:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ break;
+ case AntialiasMode::SUBPIXEL:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+ break;
+ default:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+ }
+
+ if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
+ !CurrentLayer().mIsOpaque && !forceClearType) {
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ }
+
+ mDC->SetTextAntialiasMode(d2dAAMode);
+
+ if (params != mTextRenderingParams) {
+ mDC->SetTextRenderingParams(params);
+ mTextRenderingParams = params;
+ }
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+
+ AutoDWriteGlyphRun autoRun;
+ DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
+
+ bool needsRepushedLayers = false;
+ if (forceClearType) {
+ D2D1_RECT_F rect;
+ bool isAligned;
+ needsRepushedLayers = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ // If we have a complex clip in our stack and we have a transparent
+ // background, and subpixel AA is permitted, we need to repush our layer
+ // stack limited by the glyph run bounds initializing our layers for
+ // subpixel AA.
+ if (needsRepushedLayers) {
+ mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+ DWRITE_MEASURING_MODE_NATURAL, &rect);
+ rect.left = std::floor(rect.left);
+ rect.right = std::ceil(rect.right);
+ rect.top = std::floor(rect.top);
+ rect.bottom = std::ceil(rect.bottom);
+
+ PopAllClips();
+
+ if (!mTransform.IsRectilinear()) {
+ // We must limit the pixels we touch to the -user space- bounds of
+ // the glyphs being drawn. In order not to get transparent pixels
+ // copied up in our pushed layer stack.
+ D2D1_RECT_F userRect;
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+ DWRITE_MEASURING_MODE_NATURAL, &userRect);
+
+ RefPtr<ID2D1PathGeometry> path;
+ factory()->CreatePathGeometry(getter_AddRefs(path));
+ RefPtr<ID2D1GeometrySink> sink;
+ path->Open(getter_AddRefs(sink));
+ AddRectToSink(sink, userRect);
+ sink->Close();
+
+ mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
+ D2DMatrix(mTransform), 1.0f, nullptr,
+ D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
+ D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
+ }
+
+ PushClipsToDC(mDC, true, rect);
+ mDC->SetTransform(D2DMatrix(mTransform));
+ }
+ }
+
+ if (brush) {
+ mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
+ }
+
+ if (needsRepushedLayers) {
+ PopClipsFromDC(mDC);
+
+ if (!mTransform.IsRectilinear()) {
+ mDC->PopLayer();
+ }
+ }
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aSource);
+
+ RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
+ RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f);
+ mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0f, mask),
+ nullptr);
+
+ Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
+ Matrix mat = mTransform;
+ mat.Invert();
+
+ mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
+
+ mDC->PopLayer();
+
+ FinalizeDrawing(aOptions.mCompositionOp, aSource);
+}
+
+void
+DrawTargetD2D1::PushClipGeometry(ID2D1Geometry* aGeometry,
+ const D2D1_MATRIX_3X2_F& aTransform,
+ bool aPixelAligned)
+{
+ mCurrentClippedGeometry = nullptr;
+
+ PushedClip clip;
+ clip.mGeometry = aGeometry;
+ clip.mTransform = aTransform;
+ clip.mIsPixelAligned = aPixelAligned;
+
+ aGeometry->GetBounds(aTransform, &clip.mBounds);
+
+ CurrentLayer().mPushedClips.push_back(clip);
+
+ // The transform of clips is relative to the world matrix, since we use the total
+ // transform for the clips, make the world matrix identity.
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (CurrentLayer().mClipsArePushed) {
+ PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
+ }
+}
+
+void
+DrawTargetD2D1::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
+ return;
+ }
+
+ RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
+
+ PushClipGeometry(pathD2D->GetGeometry(), D2DMatrix(mTransform));
+}
+
+void
+DrawTargetD2D1::PushClipRect(const Rect &aRect)
+{
+ if (!mTransform.IsRectilinear()) {
+ // Whoops, this isn't a rectangle in device space, Direct2D will not deal
+ // with this transform the way we want it to.
+ // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
+ RefPtr<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect));
+ return PushClipGeometry(geom, D2DMatrix(mTransform));
+ }
+
+ mCurrentClippedGeometry = nullptr;
+
+ PushedClip clip;
+ Rect rect = mTransform.TransformBounds(aRect);
+ IntRect intRect;
+ clip.mIsPixelAligned = rect.ToIntRect(&intRect);
+
+ // Do not store the transform, just store the device space rectangle directly.
+ clip.mBounds = D2DRect(rect);
+
+ CurrentLayer().mPushedClips.push_back(clip);
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (CurrentLayer().mClipsArePushed) {
+ mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+}
+
+void
+DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ // Build a path for the union of the rects.
+ RefPtr<ID2D1PathGeometry> path;
+ factory()->CreatePathGeometry(getter_AddRefs(path));
+ RefPtr<ID2D1GeometrySink> sink;
+ path->Open(getter_AddRefs(sink));
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ for (uint32_t i = 0; i < aCount; i++) {
+ const IntRect& rect = aRects[i];
+ sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
+ D2D1_POINT_2F lines[3] = { D2DPoint(rect.TopRight()), D2DPoint(rect.BottomRight()), D2DPoint(rect.BottomLeft()) };
+ sink->AddLines(lines, 3);
+ sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ }
+ sink->Close();
+
+ // The path is in device-space, so there is no transform needed,
+ // and all rects are pixel aligned.
+ PushClipGeometry(path, D2D1::IdentityMatrix(), true);
+}
+
+void
+DrawTargetD2D1::PopClip()
+{
+ mCurrentClippedGeometry = nullptr;
+ if (CurrentLayer().mPushedClips.empty()) {
+ gfxDevCrash(LogReason::UnbalancedClipStack) << "DrawTargetD2D1::PopClip: No clip to pop.";
+ return;
+ }
+
+ if (CurrentLayer().mClipsArePushed) {
+ if (CurrentLayer().mPushedClips.back().mGeometry) {
+ mDC->PopLayer();
+ } else {
+ mDC->PopAxisAlignedClip();
+ }
+ }
+ CurrentLayer().mPushedClips.pop_back();
+}
+
+void
+DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (aOpaque) {
+ options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+ }
+ if (aCopyBackground) {
+ options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ RefPtr<ID2D1BitmapBrush> mask;
+
+ Matrix maskTransform = aMaskTransform;
+
+ RefPtr<ID2D1PathGeometry> clip;
+ if (aMask) {
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
+
+ // The mask is given in user space. Our layer will apply it in device space.
+ maskTransform = maskTransform * mTransform;
+
+ if (image) {
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+ mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
+ MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
+
+ factory()->CreatePathGeometry(getter_AddRefs(clip));
+ RefPtr<ID2D1GeometrySink> sink;
+ clip->Open(getter_AddRefs(sink));
+ AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
+ sink->Close();
+ } else {
+ gfxCriticalError() << "Failed to get image for mask surface!";
+ }
+ }
+
+ PushAllClips();
+
+ mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr);
+ PushedLayer pushedLayer;
+ pushedLayer.mClipsArePushed = false;
+ pushedLayer.mIsOpaque = aOpaque;
+ pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
+ mPermitSubpixelAA = aOpaque;
+
+ mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
+ mPushedLayers.push_back(pushedLayer);
+
+ mDC->SetTarget(CurrentTarget());
+
+ mUsedCommandListsSincePurge++;
+}
+
+void
+DrawTargetD2D1::PopLayer()
+{
+ MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
+
+ RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+ mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
+
+ mPushedLayers.pop_back();
+ mDC->SetTarget(CurrentTarget());
+
+ list->Close();
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ DCCommandSink sink(mDC);
+ list->Stream(&sink);
+
+ mDC->PopLayer();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<ID2D1Bitmap1> bitmap;
+
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride,
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)),
+ getter_AddRefs(bitmap));
+
+ if (FAILED(hr) || !bitmap) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
+ return nullptr;
+ }
+
+ return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, aFormat, aSize);
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
+
+ if (!dt->Init(aSize, aFormat)) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
+{
+ RefPtr<ID2D1PathGeometry> path;
+ HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1GeometrySink> sink;
+ hr = path->Open(getter_AddRefs(sink));
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aFillRule == FillRule::FILL_WINDING) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ }
+
+ return MakeAndAddRef<PathBuilderD2D>(sink, path, aFillRule, BackendType::DIRECT2D1_1);
+}
+
+already_AddRefed<GradientStops>
+DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
+{
+ if (aNumStops == 0) {
+ gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops.";
+ return nullptr;
+ }
+
+ D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
+
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ stops[i].position = rawStops[i].offset;
+ stops[i].color = D2DColor(rawStops[i].color);
+ }
+
+ RefPtr<ID2D1GradientStopCollection> stopCollection;
+
+ HRESULT hr =
+ mDC->CreateGradientStopCollection(stops, aNumStops,
+ D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH),
+ getter_AddRefs(stopCollection));
+ delete [] stops;
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<GradientStopsD2D>(stopCollection, Factory::GetDirect3D11Device());
+}
+
+already_AddRefed<FilterNode>
+DrawTargetD2D1::CreateFilter(FilterType aType)
+{
+ return FilterNodeD2D1::Create(mDC, aType);
+}
+
+void
+DrawTargetD2D1::GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ MOZ_ASSERT(aScaledFont->GetType() == FontType::DWRITE);
+
+ aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs, aGlyphMetrics);
+
+ // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
+ // account for antialiasing.
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth += 2.0f;
+ aGlyphMetrics[i].mXBearing -= 1.0f;
+ }
+ }
+}
+
+bool
+DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
+ return false;
+ }
+
+ hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ aTexture->QueryInterface(__uuidof(IDXGISurface),
+ (void**)((IDXGISurface**)getter_AddRefs(dxgiSurface)));
+ if (!dxgiSurface) {
+ gfxCriticalError() <<"[D2D1.1] Failed to obtain a DXGI surface.";
+ return false;
+ }
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(aFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ hr = mDC->CreateBitmapFromDxgiSurface(dxgiSurface, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ mFormat = aFormat;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+ mSize.width = desc.Width;
+ mSize.height = desc.Height;
+
+ // This single solid color brush system is not very 'threadsafe', however,
+ // issueing multiple drawing commands simultaneously to a single drawtarget
+ // from multiple threads is unexpected since there's no way to guarantee
+ // ordering in that situation anyway.
+ hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I1).";
+ return false;
+ }
+
+ mDC->SetTarget(CurrentTarget());
+
+ mDC->BeginDraw();
+
+ CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
+ return true;
+}
+
+bool
+DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
+ return false;
+ }
+
+ hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) ||
+ mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) {
+ // This is 'ok', so don't assert
+ gfxCriticalNote << "[D2D1.1] Attempt to use unsupported surface size " << aSize;
+ return false;
+ }
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(aFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ mDC->SetTarget(CurrentTarget());
+
+ hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
+ return false;
+ }
+
+ mDC->BeginDraw();
+
+ CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
+ mDC->Clear();
+
+ mFormat = aFormat;
+ mSize = aSize;
+
+ return true;
+}
+
+/**
+ * Private helpers.
+ */
+uint32_t
+DrawTargetD2D1::GetByteSize() const
+{
+ return mSize.width * mSize.height * BytesPerPixel(mFormat);
+}
+
+ID2D1Factory1*
+DrawTargetD2D1::factory()
+{
+ if (mFactory) {
+ return mFactory;
+ }
+
+ RefPtr<ID2D1Factory> factory;
+ D2D1CreateFactoryFunc createD2DFactory;
+ HMODULE d2dModule = LoadLibraryW(L"d2d1.dll");
+ createD2DFactory = (D2D1CreateFactoryFunc)
+ GetProcAddress(d2dModule, "D2D1CreateFactory");
+
+ if (!createD2DFactory) {
+ gfxWarning() << "Failed to locate D2D1CreateFactory function.";
+ return nullptr;
+ }
+
+ D2D1_FACTORY_OPTIONS options;
+#ifdef _DEBUG
+ options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
+#else
+ options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+#endif
+ //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
+
+ HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory),
+ &options,
+ getter_AddRefs(factory));
+
+ if (FAILED(hr) || !factory) {
+ gfxCriticalNote << "Failed to create a D2D1 content device: " << hexa(hr);
+ return nullptr;
+ }
+
+ hr = factory->QueryInterface((ID2D1Factory1**)&mFactory);
+ if (FAILED(hr) || !mFactory) {
+ return nullptr;
+ }
+
+ ExtendInputEffectD2D1::Register(mFactory);
+ RadialGradientEffectD2D1::Register(mFactory);
+
+ return mFactory;
+}
+
+IDWriteFactory*
+DrawTargetD2D1::GetDWriteFactory()
+{
+ if (mDWriteFactory) {
+ return mDWriteFactory;
+ }
+
+ decltype(DWriteCreateFactory)* createDWriteFactory;
+ HMODULE dwriteModule = LoadLibrarySystem32(L"dwrite.dll");
+ createDWriteFactory = (decltype(DWriteCreateFactory)*)
+ GetProcAddress(dwriteModule, "DWriteCreateFactory");
+
+ if (!createDWriteFactory) {
+ gfxWarning() << "Failed to locate DWriteCreateFactory function.";
+ return nullptr;
+ }
+
+ HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(&mDWriteFactory));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create DWrite Factory.";
+ }
+
+ return mDWriteFactory;
+}
+
+void
+DrawTargetD2D1::CleanupD2D()
+{
+ if (mFactory) {
+ RadialGradientEffectD2D1::Unregister(mFactory);
+ ExtendInputEffectD2D1::Unregister(mFactory);
+ mFactory->Release();
+ mFactory = nullptr;
+ }
+}
+
+void
+DrawTargetD2D1::MarkChanged()
+{
+ if (mSnapshot) {
+ if (mSnapshot->hasOneRef()) {
+ // Just destroy it, since no-one else knows about it.
+ mSnapshot = nullptr;
+ } else {
+ mSnapshot->DrawTargetWillChange();
+ // The snapshot will no longer depend on this target.
+ MOZ_ASSERT(!mSnapshot);
+ }
+ }
+ if (mDependentTargets.size()) {
+ // Copy mDependentTargets since the Flush()es below will modify it.
+ TargetSet tmpTargets = mDependentTargets;
+ for (TargetSet::iterator iter = tmpTargets.begin();
+ iter != tmpTargets.end(); iter++) {
+ (*iter)->Flush();
+ }
+ // The Flush() should have broken all dependencies on this target.
+ MOZ_ASSERT(!mDependentTargets.size());
+ }
+}
+
+bool
+DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,
+ const Pattern& aPattern,
+ bool aClipIsComplex)
+{
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+ return patternSupported && !CurrentLayer().mIsOpaque && D2DSupportsCompositeMode(aOp) &&
+ IsOperatorBoundByMask(aOp) && aClipIsComplex;
+}
+
+void
+DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ MarkChanged();
+
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+ if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
+ // It's important to do this before FlushTransformToDC! As this will cause
+ // the transform to become dirty.
+ PushAllClips();
+
+ FlushTransformToDC();
+
+ if (aOp != CompositionOp::OP_OVER)
+ mDC->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp));
+
+ return;
+ }
+
+ HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
+ mDC->SetTarget(mCommandList);
+ mUsedCommandListsSincePurge++;
+
+ // This is where we should have a valid command list. If we don't, something is
+ // wrong, and it's likely an OOM.
+ if (!mCommandList) {
+ gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D2D1.1 command list on creation " << mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
+ }
+
+ D2D1_RECT_F rect;
+ bool isAligned;
+ bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+ PushClipsToDC(mDC);
+ }
+
+ FlushTransformToDC();
+}
+
+void
+DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+ if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
+ if (aOp != CompositionOp::OP_OVER)
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ D2D1_RECT_F rect;
+ bool isAligned;
+ bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+ PopClipsFromDC(mDC);
+ }
+
+ mDC->SetTarget(CurrentTarget());
+ if (!mCommandList) {
+ gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D21.1 command list on finalize";
+ return;
+ }
+ mCommandList->Close();
+
+ RefPtr<ID2D1CommandList> source = mCommandList;
+ mCommandList = nullptr;
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (patternSupported) {
+ if (D2DSupportsCompositeMode(aOp)) {
+ RefPtr<ID2D1Image> tmpImage;
+ if (clipIsComplex) {
+ PopAllClips();
+ if (!IsOperatorBoundByMask(aOp)) {
+ tmpImage = GetImageForLayerContent();
+ }
+ }
+ mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
+
+ if (tmpImage) {
+ RefPtr<ID2D1ImageBrush> brush;
+ RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
+ mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)),
+ getter_AddRefs(brush));
+
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->FillGeometry(inverseGeom, brush);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ }
+ return;
+ }
+
+ RefPtr<ID2D1Effect> blendEffect;
+ HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
+
+ if (FAILED(hr) || !blendEffect) {
+ gfxWarning() << "Failed to create blend effect!";
+ return;
+ }
+
+ // We don't need to preserve the current content of this layer as the output
+ // of the blend effect should completely replace it.
+ RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(false);
+ if (!tmpImage) {
+ return;
+ }
+
+ blendEffect->SetInput(0, tmpImage);
+ blendEffect->SetInput(1, source);
+ blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
+
+ mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+
+ // This may seem a little counter intuitive. If this is false, we go through the regular
+ // codepaths and set it to true. When this was true, GetImageForLayerContent will return
+ // a bitmap for the current command list and we will no longer have a complex blend
+ // with a list for tmpImage. Therefore we can set it to false again.
+ mDidComplexBlendWithListInList = !mDidComplexBlendWithListInList;
+
+ return;
+ }
+
+ const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
+ if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
+ // Draw nothing!
+ return;
+ }
+
+ if (!pat->mStops) {
+ // Draw nothing because of no color stops
+ return;
+ }
+
+ RefPtr<ID2D1Effect> radialGradientEffect;
+
+ HRESULT hr = mDC->CreateEffect(CLSID_RadialGradientEffect, getter_AddRefs(radialGradientEffect));
+ if (FAILED(hr) || !radialGradientEffect) {
+ gfxWarning() << "Failed to create radial gradient effect. Code: " << hexa(hr);
+ return;
+ }
+
+ radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION,
+ static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection);
+ radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y));
+ radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y));
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1);
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
+ radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform));
+ radialGradientEffect->SetInput(0, source);
+
+ mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
+}
+
+void
+DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
+{
+ if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) {
+ aSource->mDrawTarget->mDependentTargets.insert(this);
+ mDependingOnTargets.insert(aSource->mDrawTarget);
+ }
+}
+
+static D2D1_RECT_F
+IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
+{
+ D2D1_RECT_F result;
+ result.left = max(aRect1.left, aRect2.left);
+ result.top = max(aRect1.top, aRect2.top);
+ result.right = min(aRect1.right, aRect2.right);
+ result.bottom = min(aRect1.bottom, aRect2.bottom);
+
+ result.right = max(result.right, result.left);
+ result.bottom = max(result.bottom, result.top);
+
+ return result;
+}
+
+bool
+DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
+{
+ if (!CurrentLayer().mPushedClips.size()) {
+ return false;
+ }
+
+ aIsPixelAligned = true;
+ aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
+ for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
+ if (iter->mGeometry) {
+ return false;
+ }
+ aClipRect = IntersectRect(aClipRect, iter->mBounds);
+ if (!iter->mIsPixelAligned) {
+ aIsPixelAligned = false;
+ }
+ }
+ return true;
+}
+
+already_AddRefed<ID2D1Image>
+DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent)
+{
+ PopAllClips();
+
+ if (!CurrentLayer().mCurrentList) {
+ RefPtr<ID2D1Bitmap> tmpBitmap;
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+ // If it's a recreate target error, return and handle it elsewhere.
+ if (hr == D2DERR_RECREATE_TARGET) {
+ mDC->Flush();
+ return nullptr;
+ }
+ // For now, crash in other scenarios; this should happen because tmpBitmap is
+ // null and CopyFromBitmap call below dereferences it.
+ }
+ mDC->Flush();
+
+ tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+ return tmpBitmap.forget();
+ } else {
+ RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+ mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
+ mDC->SetTarget(CurrentTarget());
+ list->Close();
+
+ RefPtr<ID2D1Bitmap1> tmpBitmap;
+ if (mDidComplexBlendWithListInList) {
+ D2D1_BITMAP_PROPERTIES1 props =
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_PREMULTIPLIED));
+ mDC->CreateBitmap(mBitmap->GetPixelSize(), nullptr, 0, &props, getter_AddRefs(tmpBitmap));
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mDC->SetTarget(tmpBitmap);
+ mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+ mDC->SetTarget(CurrentTarget());
+ }
+
+ DCCommandSink sink(mDC);
+
+ if (aShouldPreserveContent) {
+ list->Stream(&sink);
+ PushAllClips();
+ }
+
+ if (mDidComplexBlendWithListInList) {
+ return tmpBitmap.forget();
+ }
+
+ return list.forget();
+ }
+}
+
+already_AddRefed<ID2D1Geometry>
+DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
+{
+ if (mCurrentClippedGeometry) {
+ *aClipBounds = mCurrentClipBounds;
+ RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
+ return clippedGeometry.forget();
+ }
+
+ MOZ_ASSERT(CurrentLayer().mPushedClips.size());
+
+ mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
+
+ // if pathGeom is null then pathRect represents the path.
+ RefPtr<ID2D1Geometry> pathGeom;
+ D2D1_RECT_F pathRect;
+ bool pathRectIsAxisAligned = false;
+ auto iter = CurrentLayer().mPushedClips.begin();
+
+ if (iter->mGeometry) {
+ pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
+ } else {
+ pathRect = iter->mBounds;
+ pathRectIsAxisAligned = iter->mIsPixelAligned;
+ }
+
+ iter++;
+ for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
+ // Do nothing but add it to the current clip bounds.
+ if (!iter->mGeometry && iter->mIsPixelAligned) {
+ mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+ IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
+ int32_t(iter->mBounds.right - iter->mBounds.left),
+ int32_t(iter->mBounds.bottom - iter->mBounds.top)));
+ continue;
+ }
+
+ if (!pathGeom) {
+ if (pathRectIsAxisAligned) {
+ mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+ IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
+ int32_t(pathRect.right - pathRect.left),
+ int32_t(pathRect.bottom - pathRect.top)));
+ }
+ if (iter->mGeometry) {
+ // See if pathRect needs to go into the path geometry.
+ if (!pathRectIsAxisAligned) {
+ pathGeom = ConvertRectToGeometry(pathRect);
+ } else {
+ pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
+ }
+ } else {
+ pathRect = IntersectRect(pathRect, iter->mBounds);
+ pathRectIsAxisAligned = false;
+ continue;
+ }
+ }
+
+ RefPtr<ID2D1PathGeometry> newGeom;
+ factory()->CreatePathGeometry(getter_AddRefs(newGeom));
+
+ RefPtr<ID2D1GeometrySink> currentSink;
+ newGeom->Open(getter_AddRefs(currentSink));
+
+ if (iter->mGeometry) {
+ pathGeom->CombineWithGeometry(iter->mGeometry, D2D1_COMBINE_MODE_INTERSECT,
+ iter->mTransform, currentSink);
+ } else {
+ RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
+ pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
+ D2D1::IdentityMatrix(), currentSink);
+ }
+
+ currentSink->Close();
+
+ pathGeom = newGeom.forget();
+ }
+
+ // For now we need mCurrentClippedGeometry to always be non-nullptr. This
+ // method might seem a little strange but it is just fine, if pathGeom is
+ // nullptr pathRect will always still contain 1 clip unaccounted for
+ // regardless of mCurrentClipBounds.
+ if (!pathGeom) {
+ pathGeom = ConvertRectToGeometry(pathRect);
+ }
+ mCurrentClippedGeometry = pathGeom.forget();
+ *aClipBounds = mCurrentClipBounds;
+ RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
+ return clippedGeometry.forget();
+}
+
+already_AddRefed<ID2D1Geometry>
+DrawTargetD2D1::GetInverseClippedGeometry()
+{
+ IntRect bounds;
+ RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
+ RefPtr<ID2D1RectangleGeometry> rectGeom;
+ RefPtr<ID2D1PathGeometry> inverseGeom;
+
+ factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), getter_AddRefs(rectGeom));
+ factory()->CreatePathGeometry(getter_AddRefs(inverseGeom));
+ RefPtr<ID2D1GeometrySink> sink;
+ inverseGeom->Open(getter_AddRefs(sink));
+ rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
+ sink->Close();
+
+ return inverseGeom.forget();
+}
+
+void
+DrawTargetD2D1::PopAllClips()
+{
+ if (CurrentLayer().mClipsArePushed) {
+ PopClipsFromDC(mDC);
+
+ CurrentLayer().mClipsArePushed = false;
+ }
+}
+
+void
+DrawTargetD2D1::PushAllClips()
+{
+ if (!CurrentLayer().mClipsArePushed) {
+ PushClipsToDC(mDC);
+
+ CurrentLayer().mClipsArePushed = true;
+ }
+}
+
+void
+DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
+{
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
+ if (iter->mGeometry) {
+ PushD2DLayer(aDC, iter->mGeometry, iter->mTransform, iter->mIsPixelAligned, aForceIgnoreAlpha, aMaxRect);
+ } else {
+ mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+ }
+}
+
+void
+DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
+{
+ for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
+ if (CurrentLayer().mPushedClips[i].mGeometry) {
+ aDC->PopLayer();
+ } else {
+ aDC->PopAxisAlignedClip();
+ }
+ }
+}
+
+already_AddRefed<ID2D1Brush>
+DrawTargetD2D1::CreateTransparentBlackBrush()
+{
+ return GetSolidColorBrush(D2D1::ColorF(0, 0));
+}
+
+already_AddRefed<ID2D1SolidColorBrush>
+DrawTargetD2D1::GetSolidColorBrush(const D2D_COLOR_F& aColor)
+{
+ RefPtr<ID2D1SolidColorBrush> brush = mSolidColorBrush;
+ brush->SetColor(aColor);
+ return brush.forget();
+}
+
+already_AddRefed<ID2D1Brush>
+DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
+{
+ if (!IsPatternSupportedByD2D(aPattern)) {
+ return GetSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f));
+ }
+
+ if (aPattern.GetType() == PatternType::COLOR) {
+ Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
+ return GetSolidColorBrush(D2D1::ColorF(color.r, color.g, color.b, color.a * aAlpha));
+ }
+ if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
+ RefPtr<ID2D1LinearGradientBrush> gradBrush;
+ const LinearGradientPattern *pat =
+ static_cast<const LinearGradientPattern*>(&aPattern);
+
+ GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
+
+ if (!stops) {
+ gfxDebug() << "No stops specified for gradient pattern.";
+ return CreateTransparentBlackBrush();
+ }
+
+ if (pat->mBegin == pat->mEnd) {
+ uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
+ vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
+ stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
+ d2dStops.back().color.a *= aAlpha;
+ return GetSolidColorBrush(d2dStops.back().color);
+ }
+
+ mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
+ D2DPoint(pat->mEnd)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
+ stops->mStopCollection,
+ getter_AddRefs(gradBrush));
+
+ if (!gradBrush) {
+ gfxWarning() << "Couldn't create gradient brush.";
+ return CreateTransparentBlackBrush();
+ }
+
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
+ RefPtr<ID2D1RadialGradientBrush> gradBrush;
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aPattern);
+
+ GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
+
+ if (!stops) {
+ gfxDebug() << "No stops specified for gradient pattern.";
+ return CreateTransparentBlackBrush();
+ }
+
+ // This will not be a complex radial gradient brush.
+ mDC->CreateRadialGradientBrush(
+ D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2),
+ D2DPoint(pat->mCenter1 - pat->mCenter2),
+ pat->mRadius2, pat->mRadius2),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
+ stops->mStopCollection,
+ getter_AddRefs(gradBrush));
+
+ if (!gradBrush) {
+ gfxWarning() << "Couldn't create gradient brush.";
+ return CreateTransparentBlackBrush();
+ }
+
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::SURFACE) {
+ const SurfacePattern *pat =
+ static_cast<const SurfacePattern*>(&aPattern);
+
+ if (!pat->mSurface) {
+ gfxDebug() << "No source surface specified for surface pattern";
+ return CreateTransparentBlackBrush();
+ }
+
+ D2D1_RECT_F samplingBounds;
+ Matrix mat = pat->mMatrix;
+
+ MOZ_ASSERT(pat->mSurface->IsValid());
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
+
+ if (pat->mSurface->GetFormat() == SurfaceFormat::A8) {
+ // See bug 1251431, at least FillOpacityMask does not appear to allow a source bitmapbrush
+ // with source format A8. This creates a BGRA surface with the same alpha values that
+ // the A8 surface has.
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (bitmap) {
+ RefPtr<ID2D1Image> oldTarget;
+ RefPtr<ID2D1Bitmap1> tmpBitmap;
+ mDC->CreateBitmap(D2D1::SizeU(pat->mSurface->GetSize().width, pat->mSurface->GetSize().height), nullptr, 0,
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
+ getter_AddRefs(tmpBitmap));
+ mDC->GetTarget(getter_AddRefs(oldTarget));
+ mDC->SetTarget(tmpBitmap);
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
+ mDC->FillOpacityMask(bitmap, brush);
+ mDC->SetTarget(oldTarget);
+ image = tmpBitmap;
+ }
+ }
+
+ if (!image) {
+ return CreateTransparentBlackBrush();
+ }
+
+ if (pat->mSamplingRect.IsEmpty()) {
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (bitmap) {
+ /**
+ * Create the brush with the proper repeat modes.
+ */
+ RefPtr<ID2D1BitmapBrush> bitmapBrush;
+ D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
+ D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
+
+ mDC->CreateBitmapBrush(bitmap,
+ D2D1::BitmapBrushProperties(xRepeat, yRepeat,
+ D2DFilter(pat->mSamplingFilter)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
+ getter_AddRefs(bitmapBrush));
+ if (!bitmapBrush) {
+ gfxWarning() << "Couldn't create bitmap brush!";
+ return CreateTransparentBlackBrush();
+ }
+ return bitmapBrush.forget();
+ }
+ }
+
+ RefPtr<ID2D1ImageBrush> imageBrush;
+ if (pat->mSamplingRect.IsEmpty()) {
+ samplingBounds = D2D1::RectF(0, 0,
+ Float(pat->mSurface->GetSize().width),
+ Float(pat->mSurface->GetSize().height));
+ } else if (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ samplingBounds = D2DRect(pat->mSamplingRect);
+ mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
+ } else {
+ // We will do a partial upload of the sampling restricted area from GetImageForSurface.
+ samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height);
+ }
+
+ D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
+ D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
+
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ xRepeat,
+ yRepeat,
+ D2DInterpolationMode(pat->mSamplingFilter)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
+ getter_AddRefs(imageBrush));
+
+ if (!imageBrush) {
+ gfxWarning() << "Couldn't create image brush!";
+ return CreateTransparentBlackBrush();
+ }
+
+ return imageBrush.forget();
+ }
+
+ gfxWarning() << "Invalid pattern type detected.";
+ return CreateTransparentBlackBrush();
+}
+
+already_AddRefed<ID2D1Image>
+DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
+ ExtendMode aExtendMode, const IntRect* aSourceRect)
+{
+ RefPtr<ID2D1Image> image;
+
+ switch (aSurface->GetType()) {
+ case SurfaceType::D2D1_1_IMAGE:
+ {
+ SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
+ image = surf->GetImage();
+ AddDependencyOnSource(surf);
+ }
+ break;
+ default:
+ {
+ RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+ if (!dataSurf) {
+ gfxWarning() << "Invalid surface type.";
+ return nullptr;
+ }
+ return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode,
+ aSourceTransform, mDC, aSourceRect);
+ }
+ break;
+ }
+
+ return image.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const
+{
+ if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ RefPtr<ID2D1Bitmap1> bitmap;
+ {
+ DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!map.IsMapped())) {
+ return nullptr;
+ }
+
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.GetData(), map.GetStride(),
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())),
+ getter_AddRefs(bitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(data->GetSize()))) << "[D2D1.1] 4CreateBitmap failure " << data->GetSize() << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
+ }
+ }
+
+ if (!bitmap) {
+ return data.forget();
+ }
+
+ return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
+}
+
+void
+DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+ bool aPixelAligned, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
+{
+ D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
+ options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ D2D1_ANTIALIAS_MODE antialias =
+ aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+
+ mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
+ 1.0, nullptr, options), nullptr);
+}
+
+}
+}
diff --git a/system/graphics/2d/DrawTargetD2D1.h b/system/graphics/2d/DrawTargetD2D1.h
new file mode 100644
index 000000000..624fb58cc
--- /dev/null
+++ b/system/graphics/2d/DrawTargetD2D1.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETD2D1_H_
+#define MOZILLA_GFX_DRAWTARGETD2D1_H_
+
+#include "2D.h"
+#include <d3d11.h>
+#include <d2d1_1.h>
+#include "PathD2D.h"
+#include "HelpersD2D.h"
+
+#include <vector>
+#include <sstream>
+
+#include <unordered_set>
+
+struct IDWriteFactory;
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceD2D1;
+
+const int32_t kLayerCacheSize1 = 5;
+
+class DrawTargetD2D1 : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1, override)
+ DrawTargetD2D1();
+ virtual ~DrawTargetD2D1();
+
+ virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
+ virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override { return mSize; }
+
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override;
+ virtual void ClearRect(const Rect &aRect) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
+
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { return nullptr; }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+
+ virtual bool SupportsRegionClipping() const override { return false; }
+ virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
+
+ virtual void DetachAllSnapshots() override { MarkChanged(); }
+
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
+
+ bool Init(const IntSize &aSize, SurfaceFormat aFormat);
+ bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
+ uint32_t GetByteSize() const;
+
+ // This function will get an image for a surface, it may adjust the source
+ // transform for any transformation of the resulting image relative to the
+ // oritingal SourceSurface.
+ already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
+ ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr);
+
+ already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) {
+ Matrix mat;
+ return GetImageForSurface(aSurface, mat, aExtendMode, nullptr);
+ }
+
+ static ID2D1Factory1 *factory();
+ static void CleanupD2D();
+ static IDWriteFactory *GetDWriteFactory();
+
+ operator std::string() const {
+ std::stringstream stream;
+ stream << "DrawTargetD2D 1.1 (" << this << ")";
+ return stream.str();
+ }
+
+ static uint32_t GetMaxSurfaceSize() {
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ }
+
+ static uint64_t mVRAMUsageDT;
+ static uint64_t mVRAMUsageSS;
+
+private:
+ friend class SourceSurfaceD2D1;
+
+ typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
+
+ // This function will mark the surface as changing, and make sure any
+ // copy-on-write snapshots are notified.
+ void MarkChanged();
+ bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
+ void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
+ void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
+ void FlushTransformToDC() {
+ if (mTransformDirty) {
+ mDC->SetTransform(D2DMatrix(mTransform));
+ mTransformDirty = false;
+ }
+ }
+ void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
+
+ // Must be called with all clips popped and an identity matrix set.
+ already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true);
+
+ ID2D1Image* CurrentTarget()
+ {
+ if (CurrentLayer().mCurrentList) {
+ return CurrentLayer().mCurrentList;
+ }
+ return mBitmap;
+ }
+
+ // This returns the clipped geometry, in addition it returns aClipBounds which
+ // represents the intersection of all pixel-aligned rectangular clips that
+ // are currently set. The returned clipped geometry must be clipped by these
+ // bounds to correctly reflect the total clip. This is in device space and
+ // only for clips applied to the -current layer-.
+ already_AddRefed<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
+
+ already_AddRefed<ID2D1Geometry> GetInverseClippedGeometry();
+
+ // This gives the device space clip rect applied to the -current layer-.
+ bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
+
+ void PopAllClips();
+ void PushAllClips();
+ void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
+ void PopClipsFromDC(ID2D1DeviceContext *aDC);
+
+ already_AddRefed<ID2D1Brush> CreateTransparentBlackBrush();
+ already_AddRefed<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
+ already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
+
+ void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
+
+ void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+ bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
+ const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
+
+ IntSize mSize;
+
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
+ // This is only valid if mCurrentClippedGeometry is non-null. And will
+ // only be the intersection of all pixel-aligned retangular clips. This is in
+ // device space.
+ IntRect mCurrentClipBounds;
+ mutable RefPtr<ID2D1DeviceContext> mDC;
+ RefPtr<ID2D1Bitmap1> mBitmap;
+ RefPtr<ID2D1CommandList> mCommandList;
+
+ RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;
+
+ // We store this to prevent excessive SetTextRenderingParams calls.
+ RefPtr<IDWriteRenderingParams> mTextRenderingParams;
+
+ // List of pushed clips.
+ struct PushedClip
+ {
+ D2D1_RECT_F mBounds;
+ // If mGeometry is non-null, the mTransform member will be used.
+ D2D1_MATRIX_3X2_F mTransform;
+ RefPtr<ID2D1Geometry> mGeometry;
+ // Indicates if mBounds, and when non-null, mGeometry with mTransform
+ // applied, are pixel-aligned.
+ bool mIsPixelAligned;
+ };
+
+ // List of pushed layers.
+ struct PushedLayer
+ {
+ PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {}
+
+ std::vector<PushedClip> mPushedClips;
+ RefPtr<ID2D1CommandList> mCurrentList;
+ // True if the current clip stack is pushed to the CurrentTarget().
+ bool mClipsArePushed;
+ bool mIsOpaque;
+ bool mOldPermitSubpixelAA;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+ PushedLayer& CurrentLayer()
+ {
+ return mPushedLayers.back();
+ }
+
+ // The latest snapshot of this surface. This needs to be told when this
+ // target is modified. We keep it alive as a cache.
+ RefPtr<SourceSurfaceD2D1> mSnapshot;
+ // A list of targets we need to flush when we're modified.
+ TargetSet mDependentTargets;
+ // A list of targets which have this object in their mDependentTargets set
+ TargetSet mDependingOnTargets;
+
+ uint32_t mUsedCommandListsSincePurge;
+ // When a BlendEffect has been drawn to a command list, and that command list is
+ // subsequently used -again- as an input to a blend effect for a command list,
+ // this causes an infinite recursion inside D2D as it tries to resolve the bounds.
+ // If we resolve the current command list before this happens
+ // we can avoid the subsequent hang. (See bug 1293586)
+ bool mDidComplexBlendWithListInList;
+
+ static ID2D1Factory1 *mFactory;
+ static IDWriteFactory *mDWriteFactory;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
diff --git a/system/graphics/2d/DrawTargetDual.cpp b/system/graphics/2d/DrawTargetDual.cpp
new file mode 100644
index 000000000..87714f123
--- /dev/null
+++ b/system/graphics/2d/DrawTargetDual.cpp
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetDual.h"
+#include "Tools.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DualSurface
+{
+public:
+ inline explicit DualSurface(SourceSurface *aSurface)
+ {
+ if (!aSurface) {
+ mA = mB = nullptr;
+ return;
+ }
+
+ if (aSurface->GetType() != SurfaceType::DUAL_DT) {
+ mA = mB = aSurface;
+ return;
+ }
+
+ SourceSurfaceDual *ssDual =
+ static_cast<SourceSurfaceDual*>(aSurface);
+ mA = ssDual->mA;
+ mB = ssDual->mB;
+ }
+
+ SourceSurface *mA;
+ SourceSurface *mB;
+};
+
+/* This only needs to split patterns up for SurfacePatterns. Only in that
+ * case can we be dealing with a 'dual' source (SourceSurfaceDual) and do
+ * we need to pass separate patterns into our destination DrawTargets.
+ */
+class DualPattern
+{
+public:
+ inline explicit DualPattern(const Pattern &aPattern)
+ : mPatternsInitialized(false)
+ {
+ if (aPattern.GetType() != PatternType::SURFACE) {
+ mA = mB = &aPattern;
+ return;
+ }
+
+ const SurfacePattern *surfPat =
+ static_cast<const SurfacePattern*>(&aPattern);
+
+ if (surfPat->mSurface->GetType() != SurfaceType::DUAL_DT) {
+ mA = mB = &aPattern;
+ return;
+ }
+
+ const SourceSurfaceDual *ssDual =
+ static_cast<const SourceSurfaceDual*>(surfPat->mSurface.get());
+ mA = new (mSurfPatA.addr()) SurfacePattern(ssDual->mA, surfPat->mExtendMode,
+ surfPat->mMatrix,
+ surfPat->mSamplingFilter);
+ mB = new (mSurfPatB.addr()) SurfacePattern(ssDual->mB, surfPat->mExtendMode,
+ surfPat->mMatrix,
+ surfPat->mSamplingFilter);
+ mPatternsInitialized = true;
+ }
+
+ inline ~DualPattern()
+ {
+ if (mPatternsInitialized) {
+ mA->~Pattern();
+ mB->~Pattern();
+ }
+ }
+
+ ClassStorage<SurfacePattern> mSurfPatA;
+ ClassStorage<SurfacePattern> mSurfPatB;
+
+ const Pattern *mA;
+ const Pattern *mB;
+
+ bool mPatternsInitialized;
+};
+
+void
+DrawTargetDual::DetachAllSnapshots()
+{
+ mA->DetachAllSnapshots();
+ mB->DetachAllSnapshots();
+}
+
+void
+DrawTargetDual::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions)
+{
+ DualSurface surface(aSurface);
+ mA->DrawSurface(surface.mA, aDest, aSource, aSurfOptions, aOptions);
+ mB->DrawSurface(surface.mB, aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetDual::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp)
+{
+ DualSurface surface(aSurface);
+ mA->DrawSurfaceWithShadow(surface.mA, aDest, aColor, aOffset, aSigma, aOp);
+ mB->DrawSurfaceWithShadow(surface.mB, aDest, aColor, aOffset, aSigma, aOp);
+}
+
+void
+DrawTargetDual::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ DualPattern source(aSource);
+ DualSurface mask(aMask);
+ mA->MaskSurface(*source.mA, mask.mA, aOffset, aOptions);
+ mB->MaskSurface(*source.mB, mask.mB, aOffset, aOptions);
+}
+
+void
+DrawTargetDual::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ DualSurface surface(aSurface);
+ mA->CopySurface(surface.mA, aSourceRect, aDestination);
+ mB->CopySurface(surface.mB, aSourceRect, aDestination);
+}
+
+void
+DrawTargetDual::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->FillRect(aRect, *pattern.mA, aOptions);
+ mB->FillRect(aRect, *pattern.mB, aOptions);
+}
+
+void
+DrawTargetDual::StrokeRect(const Rect &aRect, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->StrokeRect(aRect, *pattern.mA, aStrokeOptions, aOptions);
+ mB->StrokeRect(aRect, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->StrokeLine(aStart, aEnd, *pattern.mA, aStrokeOptions, aOptions);
+ mB->StrokeLine(aStart, aEnd, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::Stroke(const Path *aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->Stroke(aPath, *pattern.mA, aStrokeOptions, aOptions);
+ mB->Stroke(aPath, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->Fill(aPath, *pattern.mA, aOptions);
+ mB->Fill(aPath, *pattern.mB, aOptions);
+}
+
+void
+DrawTargetDual::FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
+ const Pattern &aPattern, const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->FillGlyphs(aScaledFont, aBuffer, *pattern.mA, aOptions, aRenderingOptions);
+ mB->FillGlyphs(aScaledFont, aBuffer, *pattern.mB, aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetDual::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
+{
+ DualPattern source(aSource);
+ DualPattern mask(aMask);
+ mA->Mask(*source.mA, *mask.mA, aOptions);
+ mB->Mask(*source.mB, *mask.mB, aOptions);
+}
+
+void
+DrawTargetDual::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ DualSurface mask(aMask);
+ mA->PushLayer(aOpaque, aOpacity, mask.mA, aMaskTransform, aBounds, aCopyBackground);
+ mB->PushLayer(aOpaque, aOpacity, mask.mB, aMaskTransform, aBounds, aCopyBackground);
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTarget> dtA = mA->CreateSimilarDrawTarget(aSize, aFormat);
+ RefPtr<DrawTarget> dtB = mB->CreateSimilarDrawTarget(aSize, aFormat);
+
+ if (!dtA || !dtB) {
+ gfxWarning() << "Failure to allocate a similar DrawTargetDual. Size: " << aSize;
+ return nullptr;
+ }
+
+ return MakeAndAddRef<DrawTargetDual>(dtA, dtB);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetDual.h b/system/graphics/2d/DrawTargetDual.h
new file mode 100644
index 000000000..190346e44
--- /dev/null
+++ b/system/graphics/2d/DrawTargetDual.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETDUAL_H_
+#define MOZILLA_GFX_DRAWTARGETDUAL_H_
+
+#include <vector>
+#include <sstream>
+
+#include "SourceSurfaceDual.h"
+
+#include "2D.h"
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define FORWARD_FUNCTION(funcName) \
+ virtual void funcName() override { mA->funcName(); mB->funcName(); }
+#define FORWARD_FUNCTION1(funcName, var1Type, var1Name) \
+ virtual void funcName(var1Type var1Name) override { mA->funcName(var1Name); mB->funcName(var1Name); }
+
+/* This is a special type of DrawTarget. It duplicates all drawing calls
+ * accross two drawtargets. An exception to this is when a snapshot of another
+ * dual DrawTarget is used as the source for any surface data. In this case
+ * the snapshot of the first source DrawTarget is used as a source for the call
+ * to the first destination DrawTarget (mA) and the snapshot of the second
+ * source DrawTarget is used at the source for the second destination
+ * DrawTarget (mB). This class facilitates black-background/white-background
+ * drawing for per-component alpha extraction for backends which do not support
+ * native component alpha.
+ */
+class DrawTargetDual : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override)
+ DrawTargetDual(DrawTarget *aA, DrawTarget *aB)
+ : mA(aA)
+ , mB(aB)
+ {
+ mFormat = aA->GetFormat();
+ }
+
+ virtual DrawTargetType GetType() const override { return mA->GetType(); }
+ virtual BackendType GetBackendType() const override { return mA->GetBackendType(); }
+ virtual already_AddRefed<SourceSurface> Snapshot() override {
+ return MakeAndAddRef<SourceSurfaceDual>(mA, mB);
+ }
+ virtual IntSize GetSize() override { return mA->GetSize(); }
+
+ virtual void DetachAllSnapshots() override;
+
+ FORWARD_FUNCTION(Flush)
+ FORWARD_FUNCTION1(PushClip, const Path *, aPath)
+ FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect)
+ FORWARD_FUNCTION(PopClip)
+ FORWARD_FUNCTION(PopLayer)
+ FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
+
+ virtual void SetTransform(const Matrix &aTransform) override {
+ mTransform = aTransform;
+ mA->SetTransform(aTransform);
+ mB->SetTransform(aTransform);
+ }
+
+ virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect & aSource,
+ const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) override;
+
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override
+ {
+ mA->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
+ mB->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
+ }
+
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp) override;
+
+ virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) override;
+
+ virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
+
+ virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
+
+ virtual void Stroke(const Path *aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
+
+ virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override;
+
+ virtual void FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
+ const Pattern &aPattern, const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions) override;
+
+ virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override;
+
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override
+ {
+ return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
+ {
+ return mA->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
+ {
+ return mA->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
+ {
+ return mA->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
+ {
+ return mA->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
+ {
+ return mA->CreateFilter(aType);
+ }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override
+ {
+ return nullptr;
+ }
+
+ virtual bool IsDualDrawTarget() const override
+ {
+ return true;
+ }
+
+ virtual bool IsCurrentGroupOpaque() override { return mA->IsCurrentGroupOpaque(); }
+
+private:
+ RefPtr<DrawTarget> mA;
+ RefPtr<DrawTarget> mB;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWTARGETDUAL_H_ */
diff --git a/system/graphics/2d/DrawTargetRecording.cpp b/system/graphics/2d/DrawTargetRecording.cpp
new file mode 100644
index 000000000..0bd1577ec
--- /dev/null
+++ b/system/graphics/2d/DrawTargetRecording.cpp
@@ -0,0 +1,736 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetRecording.h"
+#include "PathRecording.h"
+#include <stdio.h>
+
+#include "Logging.h"
+#include "Tools.h"
+#include "Filters.h"
+#include "mozilla/UniquePtr.h"
+#include "RecordingTypes.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct RecordingSourceSurfaceUserData
+{
+ void *refPtr;
+ RefPtr<DrawEventRecorderPrivate> recorder;
+};
+
+void RecordingSourceSurfaceUserDataFunc(void *aUserData)
+{
+ RecordingSourceSurfaceUserData *userData =
+ static_cast<RecordingSourceSurfaceUserData*>(aUserData);
+
+ userData->recorder->RemoveStoredObject(userData->refPtr);
+ userData->recorder->RecordEvent(
+ RecordedSourceSurfaceDestruction(userData->refPtr));
+
+ delete userData;
+}
+
+static void
+StoreSourceSurface(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
+ DataSourceSurface *aDataSurf, const char *reason)
+{
+ if (!aDataSurf) {
+ gfxWarning() << "Recording failed to record SourceSurface for " << reason;
+ // Insert a bogus source surface.
+ int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat());
+ UniquePtr<uint8_t[]> sourceData(new uint8_t[stride * aSurface->GetSize().height]());
+ aRecorder->RecordEvent(
+ RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride,
+ aSurface->GetSize(), aSurface->GetFormat()));
+ } else {
+ DataSourceSurface::ScopedMap map(aDataSurf, DataSourceSurface::READ);
+ aRecorder->RecordEvent(
+ RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(),
+ aDataSurf->GetSize(), aDataSurf->GetFormat()));
+ }
+}
+
+static void
+EnsureSurfaceStored(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
+ const char *reason)
+{
+ if (aRecorder->HasStoredObject(aSurface)) {
+ return;
+ }
+
+ RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+ StoreSourceSurface(aRecorder, aSurface, dataSurf, reason);
+ aRecorder->AddStoredObject(aSurface);
+
+ RecordingSourceSurfaceUserData *userData = new RecordingSourceSurfaceUserData;
+ userData->refPtr = aSurface;
+ userData->recorder = aRecorder;
+ aSurface->AddUserData(reinterpret_cast<UserDataKey*>(aRecorder),
+ userData, &RecordingSourceSurfaceUserDataFunc);
+ return;
+}
+
+class SourceSurfaceRecording : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording)
+ SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder)
+ : mFinalSurface(aFinalSurface), mRecorder(aRecorder)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~SourceSurfaceRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this));
+ }
+
+ virtual SurfaceType GetType() const { return SurfaceType::RECORDING; }
+ virtual IntSize GetSize() const { return mFinalSurface->GetSize(); }
+ virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); }
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() { return mFinalSurface->GetDataSurface(); }
+
+ RefPtr<SourceSurface> mFinalSurface;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+class GradientStopsRecording : public GradientStops
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording)
+ GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder)
+ : mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~GradientStopsRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedGradientStopsDestruction(this));
+ }
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+
+ RefPtr<GradientStops> mFinalGradientStops;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+static SourceSurface *
+GetSourceSurface(SourceSurface *aSurface)
+{
+ if (aSurface->GetType() != SurfaceType::RECORDING) {
+ return aSurface;
+ }
+
+ return static_cast<SourceSurfaceRecording*>(aSurface)->mFinalSurface;
+}
+
+static GradientStops *
+GetGradientStops(GradientStops *aStops)
+{
+ if (aStops->GetBackendType() != BackendType::RECORDING) {
+ return aStops;
+ }
+
+ return static_cast<GradientStopsRecording*>(aStops)->mFinalGradientStops;
+}
+
+class FilterNodeRecording : public FilterNode
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override)
+ using FilterNode::SetAttribute;
+
+ FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder)
+ : mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~FilterNodeRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedFilterNodeDestruction(this));
+ }
+
+ static FilterNode*
+ GetFilterNode(FilterNode* aNode)
+ {
+ if (aNode->GetBackendType() != FILTER_BACKEND_RECORDING) {
+ gfxWarning() << "Non recording filter node used with recording DrawTarget!";
+ return aNode;
+ }
+
+ return static_cast<FilterNodeRecording*>(aNode)->mFinalFilterNode;
+ }
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
+ {
+ EnsureSurfaceStored(mRecorder, aSurface, "SetInput");
+
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface));
+ mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface));
+ }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(aFilter));
+
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter));
+ mFinalFilterNode->SetInput(aIndex, GetFilterNode(aFilter));
+ }
+
+
+#define FORWARD_SET_ATTRIBUTE(type, argtype) \
+ virtual void SetAttribute(uint32_t aIndex, type aValue) override { \
+ mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aValue, RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \
+ mFinalFilterNode->SetAttribute(aIndex, aValue); \
+ }
+
+ FORWARD_SET_ATTRIBUTE(bool, BOOL);
+ FORWARD_SET_ATTRIBUTE(uint32_t, UINT32);
+ FORWARD_SET_ATTRIBUTE(Float, FLOAT);
+ FORWARD_SET_ATTRIBUTE(const Size&, SIZE);
+ FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE);
+ FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT);
+ FORWARD_SET_ATTRIBUTE(const Rect&, RECT);
+ FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT);
+ FORWARD_SET_ATTRIBUTE(const Point&, POINT);
+ FORWARD_SET_ATTRIBUTE(const Matrix&, MATRIX);
+ FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4);
+ FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D);
+ FORWARD_SET_ATTRIBUTE(const Color&, COLOR);
+
+#undef FORWARD_SET_ATTRIBUTE
+
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override {
+ mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize));
+ mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize);
+ }
+
+ virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; }
+
+ RefPtr<FilterNode> mFinalFilterNode;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+struct AdjustedPattern
+{
+ explicit AdjustedPattern(const Pattern &aPattern)
+ : mPattern(nullptr)
+ {
+ mOrigPattern = const_cast<Pattern*>(&aPattern);
+ }
+
+ ~AdjustedPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ operator Pattern*()
+ {
+ switch(mOrigPattern->GetType()) {
+ case PatternType::COLOR:
+ return mOrigPattern;
+ case PatternType::SURFACE:
+ {
+ SurfacePattern *surfPat = static_cast<SurfacePattern*>(mOrigPattern);
+ mPattern =
+ new (mSurfPat) SurfacePattern(GetSourceSurface(surfPat->mSurface),
+ surfPat->mExtendMode, surfPat->mMatrix,
+ surfPat->mSamplingFilter,
+ surfPat->mSamplingRect);
+ return mPattern;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPattern *linGradPat = static_cast<LinearGradientPattern*>(mOrigPattern);
+ mPattern =
+ new (mLinGradPat) LinearGradientPattern(linGradPat->mBegin, linGradPat->mEnd,
+ GetGradientStops(linGradPat->mStops),
+ linGradPat->mMatrix);
+ return mPattern;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPattern *radGradPat = static_cast<RadialGradientPattern*>(mOrigPattern);
+ mPattern =
+ new (mRadGradPat) RadialGradientPattern(radGradPat->mCenter1, radGradPat->mCenter2,
+ radGradPat->mRadius1, radGradPat->mRadius2,
+ GetGradientStops(radGradPat->mStops),
+ radGradPat->mMatrix);
+ return mPattern;
+ }
+ default:
+ return new (mColPat) ColorPattern(Color());
+ }
+
+ return mPattern;
+ }
+
+ union {
+ char mColPat[sizeof(ColorPattern)];
+ char mLinGradPat[sizeof(LinearGradientPattern)];
+ char mRadGradPat[sizeof(RadialGradientPattern)];
+ char mSurfPat[sizeof(SurfacePattern)];
+ };
+
+ Pattern *mOrigPattern;
+ Pattern *mPattern;
+};
+
+DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData)
+ : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder))
+ , mFinalDT(aDT)
+{
+ RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr;
+ mRecorder->RecordEvent(RecordedDrawTargetCreation(this,
+ mFinalDT->GetBackendType(),
+ mFinalDT->GetSize(),
+ mFinalDT->GetFormat(),
+ aHasData, snapshot));
+ mFormat = mFinalDT->GetFormat();
+}
+
+DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT,
+ DrawTarget *aSimilarDT)
+ : mRecorder(aDT->mRecorder)
+ , mFinalDT(aSimilarDT)
+{
+ mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this,
+ mFinalDT->GetSize(),
+ mFinalDT->GetFormat()));
+ mFormat = mFinalDT->GetFormat();
+}
+
+DrawTargetRecording::~DrawTargetRecording()
+{
+ mRecorder->RecordEvent(RecordedDrawTargetDestruction(this));
+}
+
+void
+DrawTargetRecording::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedFillRect(this, aRect, aPattern, aOptions));
+ mFinalDT->FillRect(aRect, *AdjustedPattern(aPattern), aOptions);
+}
+
+void
+DrawTargetRecording::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStrokeRect(this, aRect, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->StrokeRect(aRect, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetRecording::StrokeLine(const Point &aBegin,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStrokeLine(this, aBegin, aEnd, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->StrokeLine(aBegin, aEnd, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetRecording::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions));
+ mFinalDT->Fill(pathRecording->mPath, *AdjustedPattern(aPattern), aOptions);
+}
+
+struct RecordingFontUserData
+{
+ void *refPtr;
+ RefPtr<DrawEventRecorderPrivate> recorder;
+};
+
+void RecordingFontUserDataDestroyFunc(void *aUserData)
+{
+ RecordingFontUserData *userData =
+ static_cast<RecordingFontUserData*>(aUserData);
+
+ userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr));
+
+ delete userData;
+}
+
+void
+DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) {
+ RecordedFontData fontData(aFont);
+ RecordedFontDetails fontDetails;
+ if (fontData.GetFontDetails(fontDetails)) {
+ // Try to serialise the whole font, just in case this is a web font that
+ // is not present on the system.
+ if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
+ mRecorder->RecordEvent(fontData);
+ mRecorder->AddStoredFontData(fontDetails.fontDataKey);
+ }
+ mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, fontDetails));
+ } else {
+ // If that fails, record just the font description and try to load it from
+ // the system on the other side.
+ RecordedFontDescriptor fontDesc(aFont);
+ if (fontDesc.IsValid()) {
+ mRecorder->RecordEvent(fontDesc);
+ } else {
+ gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise ScaledFont";
+ }
+ }
+ RecordingFontUserData *userData = new RecordingFontUserData;
+ userData->refPtr = aFont;
+ userData->recorder = mRecorder;
+ aFont->AddUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()), userData,
+ &RecordingFontUserDataDestroyFunc);
+ }
+
+ mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
+ mFinalDT->FillGlyphs(aFont, aBuffer, *AdjustedPattern(aPattern), aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetRecording::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aSource);
+ EnsurePatternDependenciesStored(aMask);
+
+ mRecorder->RecordEvent(RecordedMask(this, aSource, aMask, aOptions));
+ mFinalDT->Mask(*AdjustedPattern(aSource), *AdjustedPattern(aMask), aOptions);
+}
+
+void
+DrawTargetRecording::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aSource);
+ EnsureSurfaceStored(mRecorder, aMask, "MaskSurface");
+
+ mRecorder->RecordEvent(RecordedMaskSurface(this, aSource, aMask, aOffset, aOptions));
+ mFinalDT->MaskSurface(*AdjustedPattern(aSource), GetSourceSurface(aMask), aOffset, aOptions);
+}
+
+void
+DrawTargetRecording::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStroke(this, pathRecording, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->Stroke(pathRecording->mPath, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::Snapshot()
+{
+ RefPtr<SourceSurface> surf = mFinalDT->Snapshot();
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ mRecorder->RecordEvent(RecordedSnapshot(retSurf, this));
+
+ return retSurf.forget();
+}
+
+void
+DrawTargetRecording::DetachAllSnapshots()
+{
+ mFinalDT->DetachAllSnapshots();
+}
+
+void
+DrawTargetRecording::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "DrawSurface");
+
+ mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, aSurfOptions, aOptions));
+ mFinalDT->DrawSurface(GetSourceSurface(aSurface), aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOp)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "DrawSurfaceWithShadow");
+
+ mRecorder->RecordEvent(RecordedDrawSurfaceWithShadow(this, aSurface, aDest, aColor, aOffset, aSigma, aOp));
+ mFinalDT->DrawSurfaceWithShadow(GetSourceSurface(aSurface), aDest, aColor, aOffset, aSigma, aOp);
+}
+
+void
+DrawTargetRecording::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ MOZ_ASSERT(mRecorder->HasStoredObject(aNode));
+
+ mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions));
+ mFinalDT->DrawFilter(FilterNodeRecording::GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions);
+}
+
+already_AddRefed<FilterNode>
+DrawTargetRecording::CreateFilter(FilterType aType)
+{
+ RefPtr<FilterNode> node = mFinalDT->CreateFilter(aType);
+
+ RefPtr<FilterNode> retNode = new FilterNodeRecording(node, mRecorder);
+
+ mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType));
+
+ return retNode.forget();
+}
+
+void
+DrawTargetRecording::ClearRect(const Rect &aRect)
+{
+ mRecorder->RecordEvent(RecordedClearRect(this, aRect));
+ mFinalDT->ClearRect(aRect);
+}
+
+void
+DrawTargetRecording::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "CopySurface");
+
+ mRecorder->RecordEvent(RecordedCopySurface(this, aSurface, aSourceRect, aDestination));
+ mFinalDT->CopySurface(GetSourceSurface(aSurface), aSourceRect, aDestination);
+}
+
+void
+DrawTargetRecording::PushClip(const Path *aPath)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+
+ mRecorder->RecordEvent(RecordedPushClip(this, pathRecording));
+ mFinalDT->PushClip(pathRecording->mPath);
+}
+
+void
+DrawTargetRecording::PushClipRect(const Rect &aRect)
+{
+ mRecorder->RecordEvent(RecordedPushClipRect(this, aRect));
+ mFinalDT->PushClipRect(aRect);
+}
+
+void
+DrawTargetRecording::PopClip()
+{
+ mRecorder->RecordEvent(RecordedPopClip(this));
+ mFinalDT->PopClip();
+}
+
+void
+DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds, bool aCopyBackground)
+{
+ if (aMask) {
+ EnsureSurfaceStored(mRecorder, aMask, "PushLayer");
+ }
+
+ mRecorder->RecordEvent(RecordedPushLayer(this, aOpaque, aOpacity, aMask,
+ aMaskTransform, aBounds,
+ aCopyBackground));
+ mFinalDT->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
+ aCopyBackground);
+}
+
+void
+DrawTargetRecording::PopLayer()
+{
+ mRecorder->RecordEvent(RecordedPopLayer(this));
+ mFinalDT->PopLayer();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat));
+
+ return retSurf.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->OptimizeSourceSurface(aSurface);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
+
+ if (!dataSurf) {
+ // Let's try get it off the original surface.
+ dataSurf = aSurface->GetDataSurface();
+ }
+
+ StoreSourceSurface(mRecorder, retSurf, dataSurf, "OptimizeSourceSurface");
+
+ return retSurf.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromNativeSurface(aSurface);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
+ StoreSourceSurface(mRecorder, retSurf, dataSurf, "CreateSourceSurfaceFromNativeSurface");
+
+ return retSurf.forget();
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTarget> similarDT =
+ mFinalDT->CreateSimilarDrawTarget(aSize, aFormat);
+ if (!similarDT) {
+ return nullptr;
+ }
+
+ similarDT = new DrawTargetRecording(this, similarDT);
+ return similarDT.forget();
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
+ return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
+}
+
+already_AddRefed<GradientStops>
+DrawTargetRecording::CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode) const
+{
+ RefPtr<GradientStops> stops = mFinalDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
+
+ RefPtr<GradientStops> retStops = new GradientStopsRecording(stops, mRecorder);
+
+ mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode));
+
+ return retStops.forget();
+}
+
+void
+DrawTargetRecording::SetTransform(const Matrix &aTransform)
+{
+ mRecorder->RecordEvent(RecordedSetTransform(this, aTransform));
+ DrawTarget::SetTransform(aTransform);
+ mFinalDT->SetTransform(aTransform);
+}
+
+already_AddRefed<PathRecording>
+DrawTargetRecording::EnsurePathStored(const Path *aPath)
+{
+ RefPtr<PathRecording> pathRecording;
+ if (aPath->GetBackendType() == BackendType::RECORDING) {
+ pathRecording = const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath));
+ if (mRecorder->HasStoredObject(aPath)) {
+ return pathRecording.forget();
+ }
+ } else {
+ MOZ_ASSERT(!mRecorder->HasStoredObject(aPath));
+ FillRule fillRule = aPath->GetFillRule();
+ RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(fillRule);
+ RefPtr<PathBuilderRecording> builderRecording =
+ new PathBuilderRecording(builder, fillRule);
+ aPath->StreamToSink(builderRecording);
+ pathRecording = builderRecording->Finish().downcast<PathRecording>();
+ }
+
+ mRecorder->RecordEvent(RecordedPathCreation(pathRecording));
+ mRecorder->AddStoredObject(pathRecording);
+ pathRecording->mStoredRecorders.push_back(mRecorder);
+
+ return pathRecording.forget();
+}
+
+void
+DrawTargetRecording::EnsurePatternDependenciesStored(const Pattern &aPattern)
+{
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ // No dependencies here.
+ return;
+ case PatternType::LINEAR_GRADIENT:
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const LinearGradientPattern*>(&aPattern)->mStops));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const RadialGradientPattern*>(&aPattern)->mStops));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ const SurfacePattern *pat = static_cast<const SurfacePattern*>(&aPattern);
+ EnsureSurfaceStored(mRecorder, pat->mSurface, "EnsurePatternDependenciesStored");
+ return;
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetRecording.h b/system/graphics/2d/DrawTargetRecording.h
new file mode 100644
index 000000000..58daec5ca
--- /dev/null
+++ b/system/graphics/2d/DrawTargetRecording.h
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETRECORDING_H_
+#define MOZILLA_GFX_DRAWTARGETRECORDING_H_
+
+#include "2D.h"
+#include "DrawEventRecorder.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetRecording : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetRecording, override)
+ DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false);
+
+ ~DrawTargetRecording();
+
+ virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); }
+ virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); }
+ virtual bool IsRecording() const override { return true; }
+
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+
+ virtual void DetachAllSnapshots() override;
+
+ virtual IntSize GetSize() override { return mFinalDT->GetSize(); }
+
+ /* Ensure that the DrawTarget backend has flushed all drawing operations to
+ * this draw target. This must be called before using the backing surface of
+ * this draw target outside of GFX 2D code.
+ */
+ virtual void Flush() override { mFinalDT->Flush(); }
+
+ /*
+ * Draw a surface to the draw target. Possibly doing partial drawing or
+ * applying scaling. No sampling happens outside the source.
+ *
+ * aSurface Source surface to draw
+ * aDest Destination rectangle that this drawing operation should draw to
+ * aSource Source rectangle in aSurface coordinates, this area of aSurface
+ * will be stretched to the size of aDest.
+ * aOptions General draw options that are applied to the operation
+ * aSurfOptions DrawSurface options that are applied
+ */
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Blend a surface to the draw target with a shadow. The shadow is drawn as a
+ * gaussian blur using a specified sigma. The shadow is clipped to the size
+ * of the input surface, so the input surface should contain a transparent
+ * border the size of the approximate coverage of the blur (3 * aSigma).
+ * NOTE: This function works in device space!
+ *
+ * aSurface Source surface to draw.
+ * aDest Destination point that this drawing operation should draw to.
+ * aColor Color of the drawn shadow
+ * aOffset Offset of the shadow
+ * aSigma Sigma used for the guassian filter kernel
+ * aOperator Composition operator used
+ */
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override;
+
+ /*
+ * Clear a rectangle on the draw target to transparent black. This will
+ * respect the clipping region and transform.
+ *
+ * aRect Rectangle to clear
+ */
+ virtual void ClearRect(const Rect &aRect) override;
+
+ /*
+ * This is essentially a 'memcpy' between two surfaces. It moves a pixel
+ * aligned area from the source surface unscaled directly onto the
+ * drawtarget. This ignores both transform and clip.
+ *
+ * aSurface Surface to copy from
+ * aSourceRect Source rectangle to be copied
+ * aDest Destination point to copy the surface to
+ */
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ /*
+ * Fill a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * aRect Rectangle that forms the mask of this filling operation
+ * aPattern Pattern that forms the source of this filling operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Stroke a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * aRect Rectangle that forms the mask of this stroking operation
+ * aPattern Pattern that forms the source of this stroking operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Stroke a line on the DrawTarget with a certain source pattern.
+ *
+ * aStart Starting point of the line
+ * aEnd End point of the line
+ * aPattern Pattern that forms the source of this stroking operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Stroke a path on the draw target with a certain source pattern.
+ *
+ * aPath Path that is to be stroked
+ * aPattern Pattern that should be used for the stroke
+ * aStrokeOptions Stroke options used for this operation
+ * aOptions Draw options used for this operation
+ */
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Fill a path on the draw target with a certain source pattern.
+ *
+ * aPath Path that is to be filled
+ * aPattern Pattern that should be used for the fill
+ * aOptions Draw options used for this operation
+ */
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Fill a series of clyphs on the draw target with a certain source pattern.
+ */
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+
+ /*
+ * This takes a source pattern and a mask, and composites the source pattern
+ * onto the destination surface using the alpha channel of the mask pattern
+ * as a mask for the operation.
+ *
+ * aSource Source pattern
+ * aMask Mask pattern
+ * aOptions Drawing options
+ */
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Push a clip to the DrawTarget.
+ *
+ * aPath The path to clip to
+ */
+ virtual void PushClip(const Path *aPath) override;
+
+ /*
+ * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
+ * is specified in user space.
+ *
+ * aRect The rect to clip to
+ */
+ virtual void PushClipRect(const Rect &aRect) override;
+
+ /* Pop a clip from the DrawTarget. A pop without a corresponding push will
+ * be ignored.
+ */
+ virtual void PopClip() override;
+
+ /**
+ * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
+ * drawing will be redirected to, this is used for example to support group
+ * opacity or the masking of groups. Clips must be balanced within a layer,
+ * i.e. between a matching PushLayer/PopLayer pair there must be as many
+ * PushClip(Rect) calls as there are PopClip calls.
+ *
+ * @param aOpaque Whether the layer will be opaque
+ * @param aOpacity Opacity of the layer
+ * @param aMask Mask applied to the layer
+ * @param aMaskTransform Transform applied to the layer mask
+ * @param aBounds Optional bounds in device space to which the layer is
+ * limited in size.
+ * @param aCopyBackground Whether to copy the background into the layer, this
+ * is only supported when aOpaque is true.
+ */
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+
+ /**
+ * This balances a call to PushLayer and proceeds to blend the layer back
+ * onto the background. This blend will blend the temporary surface back
+ * onto the target in device space using POINT sampling and operator over.
+ */
+ virtual void PopLayer() override;
+
+ /*
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * existing bitmap data in memory.
+ *
+ * The SourceSurface does not take ownership of aData, and may be freed at any time.
+ */
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+
+ /*
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * an arbitrary other SourceSurface. This may return aSourceSurface or some
+ * other existing surface.
+ */
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+
+ /*
+ * Create a SourceSurface for a type of NativeSurface. This may fail if the
+ * draw target does not know how to deal with the type of NativeSurface passed
+ * in.
+ */
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+
+ /*
+ * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ /*
+ * Create a path builder with the specified fillmode.
+ *
+ * We need the fill mode up front because of Direct2D.
+ * ID2D1SimplifiedGeometrySink requires the fill mode
+ * to be set before calling BeginFigure().
+ */
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ /*
+ * Create a GradientStops object that holds information about a set of
+ * gradient stops, this object is required for linear or radial gradient
+ * patterns to represent the color stops in the gradient.
+ *
+ * aStops An array of gradient stops
+ * aNumStops Number of stops in the array aStops
+ * aExtendNone This describes how to extend the stop color outside of the
+ * gradient area.
+ */
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+
+ /*
+ * Set a transform on the surface, this transform is applied at drawing time
+ * to both the mask and source of the operation.
+ */
+ virtual void SetTransform(const Matrix &aTransform) override;
+
+ /* Tries to get a native surface for a DrawTarget, this may fail if the
+ * draw target cannot convert to this surface type.
+ */
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override { return mFinalDT->GetNativeSurface(aType); }
+
+ virtual bool IsCurrentGroupOpaque() override {
+ return mFinalDT->IsCurrentGroupOpaque();
+ }
+
+private:
+ /**
+ * Used for creating a DrawTargetRecording for a CreateSimilarDrawTarget call.
+ * We have to call CreateSimilarDrawTarget on mFinalDT up front and pass it in
+ * as it can fail.
+ *
+ * @param aDT DrawTargetRecording on which CreateSimilarDrawTarget was called
+ * @param aSimilarDT Similar DrawTarget created from aDT.mFinalDT.
+ */
+ DrawTargetRecording(const DrawTargetRecording *aDT,
+ DrawTarget *aSimilarDT);
+
+ Path *GetPathForPathRecording(const Path *aPath) const;
+ already_AddRefed<PathRecording> EnsurePathStored(const Path *aPath);
+ void EnsurePatternDependenciesStored(const Pattern &aPattern);
+
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+ RefPtr<DrawTarget> mFinalDT;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWTARGETRECORDING_H_ */
diff --git a/system/graphics/2d/DrawTargetSkia.cpp b/system/graphics/2d/DrawTargetSkia.cpp
new file mode 100644
index 000000000..7e71e54cc
--- /dev/null
+++ b/system/graphics/2d/DrawTargetSkia.cpp
@@ -0,0 +1,2149 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetSkia.h"
+#include "SourceSurfaceSkia.h"
+#include "ScaledFontBase.h"
+#include "ScaledFontCairo.h"
+#include "skia/include/core/SkBitmapDevice.h"
+#include "FilterNodeSoftware.h"
+#include "HelpersSkia.h"
+
+#include "mozilla/ArrayUtils.h"
+
+#include "skia/include/core/SkSurface.h"
+#include "skia/include/core/SkTypeface.h"
+#include "skia/include/effects/SkGradientShader.h"
+#include "skia/include/core/SkColorFilter.h"
+#include "skia/include/effects/SkBlurImageFilter.h"
+#include "skia/include/effects/SkLayerRasterizer.h"
+#include "skia/src/core/SkSpecialImage.h"
+#include "Blur.h"
+#include "Logging.h"
+#include "Tools.h"
+#include "DataSurfaceHelpers.h"
+#include <algorithm>
+
+#ifdef USE_SKIA_GPU
+#include "GLDefs.h"
+#include "skia/include/gpu/SkGr.h"
+#include "skia/include/gpu/GrContext.h"
+#include "skia/include/gpu/GrDrawContext.h"
+#include "skia/include/gpu/gl/GrGLInterface.h"
+#include "skia/src/image/SkImage_Gpu.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "BorrowedContext.h"
+#include <ApplicationServices/ApplicationServices.h>
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "CGTextDrawing.h"
+#endif
+
+#ifdef XP_WIN
+#include "ScaledFontDWrite.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class GradientStopsSkia : public GradientStops
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia)
+ GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops, ExtendMode aExtendMode)
+ : mCount(aNumStops)
+ , mExtendMode(aExtendMode)
+ {
+ if (mCount == 0) {
+ return;
+ }
+
+ // Skia gradients always require a stop at 0.0 and 1.0, insert these if
+ // we don't have them.
+ uint32_t shift = 0;
+ if (aStops[0].offset != 0) {
+ mCount++;
+ shift = 1;
+ }
+ if (aStops[aNumStops-1].offset != 1) {
+ mCount++;
+ }
+ mColors.resize(mCount);
+ mPositions.resize(mCount);
+ if (aStops[0].offset != 0) {
+ mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
+ mPositions[0] = 0;
+ }
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0);
+ mPositions[i + shift] = SkFloatToScalar(aStops[i].offset);
+ }
+ if (aStops[aNumStops-1].offset != 1) {
+ mColors[mCount-1] = ColorToSkColor(aStops[aNumStops-1].color, 1.0);
+ mPositions[mCount-1] = SK_Scalar1;
+ }
+ }
+
+ BackendType GetBackendType() const { return BackendType::SKIA; }
+
+ std::vector<SkColor> mColors;
+ std::vector<SkScalar> mPositions;
+ int mCount;
+ ExtendMode mExtendMode;
+};
+
+/**
+ * When constructing a temporary SkImage via GetSkImageForSurface, we may also
+ * have to construct a temporary DataSourceSurface, which must live as long as
+ * the SkImage. We attach this temporary surface to the image's pixelref, so
+ * that it can be released once the pixelref is freed.
+ */
+static void
+ReleaseTemporarySurface(const void* aPixels, void* aContext)
+{
+ DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
+ if (surf) {
+ surf->Release();
+ }
+}
+
+#ifdef IS_BIG_ENDIAN
+static const int kARGBAlphaOffset = 0;
+#else
+static const int kARGBAlphaOffset = 3;
+#endif
+
+static void
+WriteRGBXFormat(uint8_t* aData, const IntSize &aSize,
+ const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return;
+ }
+
+ int height = aSize.height;
+ int width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+ aData[column + kARGBAlphaOffset] = 0xFF;
+ }
+ aData += aStride;
+ }
+
+ return;
+}
+
+#ifdef DEBUG
+static bool
+VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return true;
+ }
+ // We should've initialized the data to be opaque already
+ // On debug builds, verify that this is actually true.
+ int height = aSize.height;
+ int width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+ if (aData[column + kARGBAlphaOffset] != 0xFF) {
+ gfxCriticalError() << "RGBX pixel at (" << column << "," << row << ") in "
+ << width << "x" << height << " surface is not opaque: "
+ << int(aData[column]) << ","
+ << int(aData[column+1]) << ","
+ << int(aData[column+2]) << ","
+ << int(aData[column+3]);
+ }
+ }
+ aData += aStride;
+ }
+
+ return true;
+}
+
+// Since checking every pixel is expensive, this only checks the four corners and center
+// of a surface that their alpha value is 0xFF.
+static bool
+VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return true;
+ }
+
+ int height = aSize.height;
+ int width = aSize.width;
+ const int pixelSize = 4;
+ const int strideDiff = aStride - (width * pixelSize);
+ MOZ_ASSERT(width * pixelSize <= aStride);
+
+ const int topLeft = 0;
+ const int topRight = width * pixelSize - pixelSize;
+ const int bottomRight = aStride * height - strideDiff - pixelSize;
+ const int bottomLeft = aStride * height - aStride;
+
+ // Lastly the center pixel
+ int middleRowHeight = height / 2;
+ int middleRowWidth = (width / 2) * pixelSize;
+ const int middle = aStride * middleRowHeight + middleRowWidth;
+
+ const int offsets[] = { topLeft, topRight, bottomRight, bottomLeft, middle };
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(offsets); i++) {
+ int offset = offsets[i];
+ if (aData[offset + kARGBAlphaOffset] != 0xFF) {
+ int row = offset / aStride;
+ int column = (offset % aStride) / pixelSize;
+ gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row << ") in "
+ << width << "x" << height << " surface is not opaque: "
+ << int(aData[offset]) << ","
+ << int(aData[offset+1]) << ","
+ << int(aData[offset+2]) << ","
+ << int(aData[offset+3]);
+ }
+ }
+
+ return true;
+}
+#endif
+
+static sk_sp<SkImage>
+GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
+{
+ if (!aSurface) {
+ gfxDebug() << "Creating null Skia image from null SourceSurface";
+ return nullptr;
+ }
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
+ }
+
+ DataSourceSurface* surf = aSurface->GetDataSurface().take();
+ if (!surf) {
+ gfxWarning() << "Failed getting DataSourceSurface for Skia image";
+ return nullptr;
+ }
+
+ SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
+ surf->GetData(), surf->Stride());
+ sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, ReleaseTemporarySurface, surf);
+ if (!image) {
+ ReleaseTemporarySurface(nullptr, surf);
+ gfxDebug() << "Failed making Skia raster image for temporary surface";
+ }
+
+ // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque white.
+ MOZ_ASSERT(VerifyRGBXCorners(surf->GetData(), surf->GetSize(),
+ surf->Stride(), surf->GetFormat()));
+ return image;
+}
+
+DrawTargetSkia::DrawTargetSkia()
+ : mSnapshot(nullptr)
+#ifdef MOZ_WIDGET_COCOA
+ , mCG(nullptr)
+ , mColorSpace(nullptr)
+ , mCanvasData(nullptr)
+ , mCGSize(0, 0)
+#endif
+{
+}
+
+DrawTargetSkia::~DrawTargetSkia()
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (mCG) {
+ CGContextRelease(mCG);
+ mCG = nullptr;
+ }
+
+ if (mColorSpace) {
+ CGColorSpaceRelease(mColorSpace);
+ mColorSpace = nullptr;
+ }
+#endif
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::Snapshot()
+{
+ RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
+ if (mSurface && !snapshot) {
+ snapshot = new SourceSurfaceSkia();
+ sk_sp<SkImage> image;
+ // If the surface is raster, making a snapshot may trigger a pixel copy.
+ // Instead, try to directly make a raster image referencing the surface pixels.
+ SkPixmap pixmap;
+ if (mSurface->peekPixels(&pixmap)) {
+ image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
+ } else {
+ image = mSurface->makeImageSnapshot(SkBudgeted::kNo);
+ }
+ if (!snapshot->InitFromImage(image, mFormat, this)) {
+ return nullptr;
+ }
+ mSnapshot = snapshot;
+ }
+
+ return snapshot.forget();
+}
+
+bool
+DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin)
+{
+ // Ensure the layer is at the origin if required.
+ SkIPoint origin = mCanvas->getTopDevice()->getOrigin();
+ if (!aOrigin && !origin.isZero()) {
+ return false;
+ }
+
+ /* Test if the canvas' device has accessible pixels first, as actually
+ * accessing the pixels may trigger side-effects, even if it fails.
+ */
+ if (!mCanvas->peekPixels(nullptr)) {
+ return false;
+ }
+
+ SkImageInfo info;
+ size_t rowBytes;
+ void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes);
+ if (!pixels) {
+ return false;
+ }
+
+ MarkChanged();
+
+ *aData = reinterpret_cast<uint8_t*>(pixels);
+ *aSize = IntSize(info.width(), info.height());
+ *aStride = int32_t(rowBytes);
+ *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
+ if (aOrigin) {
+ *aOrigin = IntPoint(origin.x(), origin.y());
+ }
+ return true;
+}
+
+void
+DrawTargetSkia::ReleaseBits(uint8_t* aData)
+{
+}
+
+static void
+ReleaseImage(const void* aPixels, void* aContext)
+{
+ SkImage* image = static_cast<SkImage*>(aContext);
+ SkSafeUnref(image);
+}
+
+static sk_sp<SkImage>
+ExtractSubset(sk_sp<SkImage> aImage, const IntRect& aRect)
+{
+ SkIRect subsetRect = IntRectToSkIRect(aRect);
+ if (aImage->bounds() == subsetRect) {
+ return aImage;
+ }
+ // makeSubset is slow, so prefer to use SkPixmap::extractSubset where possible.
+ SkPixmap pixmap, subsetPixmap;
+ if (aImage->peekPixels(&pixmap) &&
+ pixmap.extractSubset(&subsetPixmap, subsetRect)) {
+ // Release the original image reference so only the subset image keeps it alive.
+ return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage, aImage.release());
+ }
+ return aImage->makeSubset(subsetRect);
+}
+
+static inline bool
+SkImageIsMask(const sk_sp<SkImage>& aImage)
+{
+ SkPixmap pixmap;
+ if (aImage->peekPixels(&pixmap)) {
+ return pixmap.colorType() == kAlpha_8_SkColorType;
+#ifdef USE_SKIA_GPU
+ } else if (GrTexture* tex = aImage->getTexture()) {
+ return GrPixelConfigIsAlphaOnly(tex->config());
+#endif
+ } else {
+ return false;
+ }
+}
+
+static bool
+ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
+{
+ SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
+ SkBitmap bitmap;
+ if (!bitmap.tryAllocPixels(info, SkAlign4(info.minRowBytes())) ||
+ !aImage->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)) {
+ gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
+ return false;
+ }
+
+ *aResultBitmap = bitmap;
+ return true;
+}
+
+static sk_sp<SkImage>
+ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
+{
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
+ if (!image) {
+ return nullptr;
+ }
+ if (SkImageIsMask(image)) {
+ return image;
+ }
+
+ SkBitmap bitmap;
+ if (!ExtractAlphaBitmap(image, &bitmap)) {
+ return nullptr;
+ }
+
+ // Mark the bitmap immutable so that it will be shared rather than copied.
+ bitmap.setImmutable();
+ return SkImage::MakeFromBitmap(bitmap);
+}
+
+static void
+SetPaintPattern(SkPaint& aPaint,
+ const Pattern& aPattern,
+ Maybe<MutexAutoLock>& aLock,
+ Float aAlpha = 1.0,
+ Point aOffset = Point(0, 0))
+{
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR: {
+ Color color = static_cast<const ColorPattern&>(aPattern).mColor;
+ aPaint.setColor(ColorToSkColor(color, aAlpha));
+ break;
+ }
+ case PatternType::LINEAR_GRADIENT: {
+ const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
+ GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
+ if (!stops || stops->mCount < 2 ||
+ !pat.mBegin.IsFinite() || !pat.mEnd.IsFinite()) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ } else {
+ SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
+ SkPoint points[2];
+ points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y));
+ points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y));
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+ sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
+ mode, 0, &mat);
+ aPaint.setShader(shader);
+ }
+ break;
+ }
+ case PatternType::RADIAL_GRADIENT: {
+ const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
+ GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
+ if (!stops || stops->mCount < 2 ||
+ !pat.mCenter1.IsFinite() || !IsFinite(pat.mRadius1) ||
+ !pat.mCenter2.IsFinite() || !IsFinite(pat.mRadius2)) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ } else {
+ SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
+ SkPoint points[2];
+ points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y));
+ points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y));
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+ sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
+ SkFloatToScalar(pat.mRadius1),
+ points[1],
+ SkFloatToScalar(pat.mRadius2),
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
+ mode, 0, &mat);
+ aPaint.setShader(shader);
+ }
+ break;
+ }
+ case PatternType::SURFACE: {
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
+ if (!image) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ break;
+ }
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+
+ if (!pat.mSamplingRect.IsEmpty()) {
+ image = ExtractSubset(image, pat.mSamplingRect);
+ mat.preTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y);
+ }
+
+ SkShader::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
+ SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
+
+ aPaint.setShader(image->makeShader(xTileMode, yTileMode, &mat));
+
+ if (pat.mSamplingFilter == SamplingFilter::POINT) {
+ aPaint.setFilterQuality(kNone_SkFilterQuality);
+ }
+ break;
+ }
+ }
+}
+
+static inline Rect
+GetClipBounds(SkCanvas *aCanvas)
+{
+ // Use a manually transformed getClipDeviceBounds instead of
+ // getClipBounds because getClipBounds inflates the the bounds
+ // by a pixel in each direction to compensate for antialiasing.
+ SkIRect deviceBounds;
+ if (!aCanvas->getClipDeviceBounds(&deviceBounds)) {
+ return Rect();
+ }
+ SkMatrix inverseCTM;
+ if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
+ return Rect();
+ }
+ SkRect localBounds;
+ inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
+ return SkRectToRect(localBounds);
+}
+
+struct AutoPaintSetup {
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr, Point aOffset = Point(0, 0))
+ : mNeedsRestore(false), mAlpha(1.0)
+ {
+ Init(aCanvas, aOptions, aMaskBounds, false);
+ SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
+ }
+
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
+ : mNeedsRestore(false), mAlpha(1.0)
+ {
+ Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
+ }
+
+ ~AutoPaintSetup()
+ {
+ if (mNeedsRestore) {
+ mCanvas->restore();
+ }
+ }
+
+ void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds, bool aForceGroup)
+ {
+ mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
+ mCanvas = aCanvas;
+
+ //TODO: Can we set greyscale somehow?
+ if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
+ mPaint.setAntiAlias(true);
+ } else {
+ mPaint.setAntiAlias(false);
+ }
+
+ bool needsGroup = aForceGroup ||
+ (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
+ (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
+
+ // TODO: We could skip the temporary for operator_source and just
+ // clear the clip rect. The other operators would be harder
+ // but could be worth it to skip pushing a group.
+ if (needsGroup) {
+ mPaint.setBlendMode(SkBlendMode::kSrcOver);
+ SkPaint temp;
+ temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
+ temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
+ //TODO: Get a rect here
+ mCanvas->saveLayer(nullptr, &temp);
+ mNeedsRestore = true;
+ } else {
+ mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
+ mAlpha = aOptions.mAlpha;
+ }
+ mPaint.setFilterQuality(kLow_SkFilterQuality);
+ }
+
+ // TODO: Maybe add an operator overload to access this easier?
+ SkPaint mPaint;
+ bool mNeedsRestore;
+ SkCanvas* mCanvas;
+ Maybe<MutexAutoLock> mLock;
+ Float mAlpha;
+};
+
+void
+DrawTargetSkia::Flush()
+{
+ mCanvas->flush();
+}
+
+void
+DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ if (aSource.IsEmpty()) {
+ return;
+ }
+
+ MarkChanged();
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
+ if (!image) {
+ return;
+ }
+
+ SkRect destRect = RectToSkRect(aDest);
+ SkRect sourceRect = RectToSkRect(aSource);
+ bool forceGroup = SkImageIsMask(image) &&
+ aOptions.mCompositionOp != CompositionOp::OP_OVER;
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest, forceGroup);
+ if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
+ paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
+ }
+
+ mCanvas->drawImageRect(image, sourceRect, destRect, &paint.mPaint);
+}
+
+DrawTargetType
+DrawTargetSkia::GetType() const
+{
+#ifdef USE_SKIA_GPU
+ if (mGrContext) {
+ return DrawTargetType::HARDWARE_RASTER;
+ }
+#endif
+ return DrawTargetType::SOFTWARE_RASTER;
+}
+
+void
+DrawTargetSkia::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
+}
+
+void
+DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ if (aSurface->GetSize().IsEmpty()) {
+ return;
+ }
+
+ MarkChanged();
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
+ if (!image) {
+ return;
+ }
+
+ mCanvas->save();
+ mCanvas->resetMatrix();
+
+ SkPaint paint;
+ paint.setBlendMode(GfxOpToSkiaOp(aOperator));
+
+ // bug 1201272
+ // We can't use the SkDropShadowImageFilter here because it applies the xfer
+ // mode first to render the bitmap to a temporary layer, and then implicitly
+ // uses src-over to composite the resulting shadow.
+ // The canvas spec, however, states that the composite op must be used to
+ // composite the resulting shadow, so we must instead use a SkBlurImageFilter
+ // to blur the image ourselves.
+
+ SkPaint shadowPaint;
+ shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
+
+ auto shadowDest = IntPoint::Round(aDest + aOffset);
+
+ SkBitmap blurMask;
+ if (!UsingSkiaGPU() &&
+ ExtractAlphaBitmap(image, &blurMask)) {
+ // Prefer using our own box blur instead of Skia's when we're
+ // not using the GPU. It currently performs much better than
+ // SkBlurImageFilter or SkBlurMaskFilter on the CPU.
+ AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
+ int32_t(blurMask.rowBytes()),
+ aSigma, aSigma);
+ blurMask.lockPixels();
+ blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
+ blurMask.unlockPixels();
+ blurMask.notifyPixelsChanged();
+
+ shadowPaint.setColor(ColorToSkColor(aColor, 1.0f));
+
+ mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint);
+ } else {
+ sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(aSigma, aSigma, nullptr));
+ sk_sp<SkColorFilter> colorFilter(
+ SkColorFilter::MakeModeFilter(ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
+
+ shadowPaint.setImageFilter(blurFilter);
+ shadowPaint.setColorFilter(colorFilter);
+
+ mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
+ }
+
+ // Composite the original image after the shadow
+ auto dest = IntPoint::Round(aDest);
+ mCanvas->drawImage(image, dest.x, dest.y, &paint);
+
+ mCanvas->restore();
+}
+
+void
+DrawTargetSkia::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ // The sprite blitting path in Skia can be faster than the shader blitter for
+ // operators other than source (or source-over with opaque surface). So, when
+ // possible/beneficial, route to DrawSurface which will use the sprite blitter.
+ if (aPattern.GetType() == PatternType::SURFACE &&
+ aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ // Verify there is a valid surface and a pattern matrix without skew.
+ if (pat.mSurface &&
+ (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
+ GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) != kOpaque_SkAlphaType) &&
+ !pat.mMatrix.HasNonAxisAlignedTransform()) {
+ // Bound the sampling to smaller of the bounds or the sampling rect.
+ IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
+ if (!pat.mSamplingRect.IsEmpty()) {
+ srcRect = srcRect.Intersect(pat.mSamplingRect);
+ }
+ // Transform the destination rectangle by the inverse of the pattern
+ // matrix so that it is in pattern space like the source rectangle.
+ Rect patRect = aRect - pat.mMatrix.GetTranslation();
+ patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
+ // Verify the pattern rectangle will not tile or clamp.
+ if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
+ // The pattern is a surface with an axis-aligned source rectangle
+ // fitting entirely in its bounds, so just treat it as a DrawSurface.
+ DrawSurface(pat.mSurface, aRect, patRect,
+ DrawSurfaceOptions(pat.mSamplingFilter),
+ aOptions);
+ return;
+ }
+ }
+ }
+
+ MarkChanged();
+ SkRect rect = RectToSkRect(aRect);
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern, &aRect);
+
+ mCanvas->drawRect(rect, paint.mPaint);
+}
+
+void
+DrawTargetSkia::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ MOZ_ASSERT(aPath, "Null path");
+ if (aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ if (!skiaPath->GetPath().isFinite()) {
+ return;
+ }
+
+ mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
+}
+
+void
+DrawTargetSkia::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint);
+}
+
+void
+DrawTargetSkia::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
+ SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
+ paint.mPaint);
+}
+
+void
+DrawTargetSkia::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+
+ if (!skiaPath->GetPath().isFinite()) {
+ return;
+ }
+
+ mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
+}
+
+bool
+DrawTargetSkia::ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode)
+{
+ // For non-opaque surfaces, only allow subpixel AA if explicitly permitted.
+ if (!IsOpaque(mFormat) && !mPermitSubpixelAA) {
+ return false;
+ }
+
+ if (aAntialiasMode == AntialiasMode::DEFAULT) {
+ switch (aFontType) {
+ case FontType::MAC:
+ case FontType::GDI:
+ case FontType::DWRITE:
+ case FontType::FONTCONFIG:
+ return true;
+ default:
+ // TODO: Figure out what to do for the other platforms.
+ return false;
+ }
+ }
+ return (aAntialiasMode == AntialiasMode::SUBPIXEL);
+}
+
+#ifdef MOZ_WIDGET_COCOA
+class CGClipApply : public SkCanvas::ClipVisitor {
+public:
+ explicit CGClipApply(CGContextRef aCGContext)
+ : mCG(aCGContext) {}
+ void clipRect(const SkRect& aRect, SkCanvas::ClipOp op, bool antialias) override {
+ CGRect rect = CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height());
+ CGContextClipToRect(mCG, rect);
+ }
+
+ void clipRRect(const SkRRect& rrect, SkCanvas::ClipOp op, bool antialias) override {
+ SkPath path;
+ path.addRRect(rrect);
+ clipPath(path, op, antialias);
+ }
+
+ void clipPath(const SkPath& aPath, SkCanvas::ClipOp, bool antialias) override {
+ SkPath::Iter iter(aPath, true);
+ SkPoint source[4];
+ SkPath::Verb verb;
+ RefPtr<PathBuilderCG> pathBuilder =
+ new PathBuilderCG(GetFillRule(aPath.getFillType()));
+
+ while ((verb = iter.next(source)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ {
+ SkPoint dest = source[0];
+ pathBuilder->MoveTo(Point(dest.fX, dest.fY));
+ break;
+ }
+ case SkPath::kLine_Verb:
+ {
+ // The first point should be the end point of whatever
+ // verb we got to get here.
+ SkPoint second = source[1];
+ pathBuilder->LineTo(Point(second.fX, second.fY));
+ break;
+ }
+ case SkPath::kQuad_Verb:
+ {
+ SkPoint second = source[1];
+ SkPoint third = source[2];
+
+ pathBuilder->QuadraticBezierTo(Point(second.fX, second.fY),
+ Point(third.fX, third.fY));
+ break;
+ }
+ case SkPath::kCubic_Verb:
+ {
+ SkPoint second = source[1];
+ SkPoint third = source[2];
+ SkPoint fourth = source[2];
+
+ pathBuilder->BezierTo(Point(second.fX, second.fY),
+ Point(third.fX, third.fY),
+ Point(fourth.fX, fourth.fY));
+ break;
+ }
+ case SkPath::kClose_Verb:
+ {
+ pathBuilder->Close();
+ break;
+ }
+ default:
+ {
+ SkDEBUGFAIL("unknown verb");
+ break;
+ }
+ } // end switch
+ } // end while
+
+ RefPtr<Path> path = pathBuilder->Finish();
+ PathCG* cgPath = static_cast<PathCG*>(path.get());
+
+ // Weirdly, CoreGraphics clips empty paths as all shown
+ // but empty rects as all clipped. We detect this situation and
+ // workaround it appropriately
+ if (CGPathIsEmpty(cgPath->GetPath())) {
+ CGContextClipToRect(mCG, CGRectZero);
+ return;
+ }
+
+ CGContextBeginPath(mCG);
+ CGContextAddPath(mCG, cgPath->GetPath());
+
+ if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) {
+ CGContextEOClip(mCG);
+ } else {
+ CGContextClip(mCG);
+ }
+ }
+
+private:
+ CGContextRef mCG;
+};
+
+static inline CGAffineTransform
+GfxMatrixToCGAffineTransform(const Matrix &m)
+{
+ CGAffineTransform t;
+ t.a = m._11;
+ t.b = m._12;
+ t.c = m._21;
+ t.d = m._22;
+ t.tx = m._31;
+ t.ty = m._32;
+ return t;
+}
+
+/***
+ * We have to do a lot of work to draw glyphs with CG because
+ * CG assumes that the origin of rects are in the bottom left
+ * while every other DrawTarget assumes the top left is the origin.
+ * This means we have to transform the CGContext to have rects
+ * actually be applied in top left fashion. We do this by:
+ *
+ * 1) Translating the context up by the height of the canvas
+ * 2) Flipping the context by the Y axis so it's upside down.
+ *
+ * These two transforms put the origin in the top left.
+ * Transforms are better understood thinking about them from right to left order (mathematically).
+ *
+ * Consider a point we want to draw at (0, 10) in normal cartesian planes with
+ * a box of (100, 100). in CG terms, this would be at (0, 10).
+ * Positive Y values point up.
+ * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
+ * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in DrawTarget
+ * terms should end up at (0, 90). How does this work with the current transforms?
+ *
+ * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian coordinates
+ * of (0, 10). The first flip of the Y axis puts the point now at (0, -10);
+ * Next, we translate the context up by the size of the canvas (Positive Y values go up in CG
+ * coordinates but down in our draw target coordinates). Since our canvas size is (100, 100),
+ * the resulting coordinate becomes (0, 90), which is what we expect from our DrawTarget code.
+ * These two transforms put the CG context equal to what every other DrawTarget expects.
+ *
+ * Next, we need two more transforms for actual text. IF we left the transforms as is,
+ * the text would be drawn upside down, so we need another flip of the Y axis
+ * to draw the text right side up. However, with only the flip, the text would be drawn
+ * in the wrong place. Thus we also have to invert the Y position of the glyphs to get them
+ * in the right place.
+ *
+ * Thus we have the following transforms:
+ * 1) Translation of the context up
+ * 2) Flipping the context around the Y axis
+ * 3) Flipping the context around the Y axis
+ * 4) Inverting the Y position of each glyph
+ *
+ * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
+ * of DrawTargetSkia between (2) and (3).
+ *
+ * Consider the example letter P, drawn at (0, 20) in CG coordinates in a (100, 100) rect.
+ * Again, going right to left of the transforms. We'd get:
+ *
+ * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
+ * 2) The letter P upside down (b) at (0, 20) due to the second flip
+ * 3) The letter P right side up at (0, -20) due to the first flip
+ * 4) The letter P right side up at (0, 80) due to the translation
+ *
+ * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top left.
+ */
+static bool
+SetupCGContext(DrawTargetSkia* aDT,
+ CGContextRef aCGContext,
+ sk_sp<SkCanvas> aCanvas)
+{
+ // DrawTarget expects the origin to be at the top left, but CG
+ // expects it to be at the bottom left. Transform to set the origin to
+ // the top left. Have to set this before we do anything else.
+ // This is transform (1) up top
+ CGContextTranslateCTM(aCGContext, 0, aDT->GetSize().height);
+
+ // Transform (2) from the comments.
+ CGContextScaleCTM(aCGContext, 1, -1);
+
+ // Want to apply clips BEFORE the transform since the transform
+ // will apply to the clips we apply.
+ // CGClipApply applies clips in device space, so it would be a mistake
+ // to transform these clips.
+ CGClipApply clipApply(aCGContext);
+ aCanvas->replayClips(&clipApply);
+
+ CGContextConcatCTM(aCGContext, GfxMatrixToCGAffineTransform(aDT->GetTransform()));
+ return true;
+}
+
+static bool
+SetupCGGlyphs(CGContextRef aCGContext,
+ const GlyphBuffer& aBuffer,
+ Vector<CGGlyph,32>& aGlyphs,
+ Vector<CGPoint,32>& aPositions)
+{
+ // Flip again so we draw text in right side up. Transform (3) from the top
+ CGContextScaleCTM(aCGContext, 1, -1);
+
+ if (!aGlyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
+ !aPositions.resizeUninitialized(aBuffer.mNumGlyphs)) {
+ gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
+ return false;
+ }
+
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ aGlyphs[i] = aBuffer.mGlyphs[i].mIndex;
+
+ // Flip the y coordinates so that text ends up in the right spot after the (3) flip
+ // Inversion from (4) in the comments.
+ aPositions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
+ -aBuffer.mGlyphs[i].mPosition.y);
+ }
+
+ return true;
+}
+// End long comment about transforms. SetupCGContext and SetupCGGlyphs should stay
+// next to each other.
+
+// The context returned from this method will have the origin
+// in the top left and will hvae applied all the neccessary clips
+// and transforms to the CGContext. See the comment above
+// SetupCGContext.
+CGContextRef
+DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
+{
+ int32_t stride;
+ SurfaceFormat format;
+ IntSize size;
+
+ uint8_t* aSurfaceData = nullptr;
+ if (!LockBits(&aSurfaceData, &size, &stride, &format)) {
+ NS_WARNING("Could not lock skia bits to wrap CG around");
+ return nullptr;
+ }
+
+ if ((aSurfaceData == mCanvasData) && mCG && (mCGSize == size)) {
+ // If our canvas data still points to the same data,
+ // we can reuse the CG Context
+ CGContextSaveGState(mCG);
+ CGContextSetAlpha(mCG, aOptions.mAlpha);
+ SetupCGContext(this, mCG, mCanvas);
+ return mCG;
+ }
+
+ if (!mColorSpace) {
+ mColorSpace = (format == SurfaceFormat::A8) ?
+ CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
+ }
+
+ if (mCG) {
+ // Release the old CG context since it's no longer valid.
+ CGContextRelease(mCG);
+ }
+
+ mCanvasData = aSurfaceData;
+ mCGSize = size;
+
+ uint32_t bitmapInfo = (format == SurfaceFormat::A8) ?
+ kCGImageAlphaOnly :
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+
+ mCG = CGBitmapContextCreateWithData(mCanvasData,
+ mCGSize.width,
+ mCGSize.height,
+ 8, /* bits per component */
+ stride,
+ mColorSpace,
+ bitmapInfo,
+ NULL, /* Callback when released */
+ NULL);
+ if (!mCG) {
+ ReleaseBits(mCanvasData);
+ NS_WARNING("Could not create bitmap around skia data\n");
+ return nullptr;
+ }
+
+ CGContextSetAlpha(mCG, aOptions.mAlpha);
+ CGContextSetShouldAntialias(mCG, aOptions.mAntialiasMode != AntialiasMode::NONE);
+ CGContextSetShouldSmoothFonts(mCG, true);
+ CGContextSetTextDrawingMode(mCG, kCGTextFill);
+ CGContextSaveGState(mCG);
+ SetupCGContext(this, mCG, mCanvas);
+ return mCG;
+}
+
+void
+DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext)
+{
+ MOZ_ASSERT(aCGContext == mCG);
+ ReleaseBits(mCanvasData);
+ CGContextRestoreGState(aCGContext);
+}
+
+CGContextRef
+BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
+{
+ DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
+ return skiaDT->BorrowCGContext(DrawOptions());
+}
+
+void
+BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
+{
+ DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
+ skiaDT->ReturnCGContext(cg);
+ return;
+}
+
+static void
+SetFontColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace, const Pattern& aPattern)
+{
+ const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
+ CGColorRef textColor = ColorToCGColor(aColorSpace, color);
+ CGContextSetFillColorWithColor(aCGContext, textColor);
+ CGColorRelease(textColor);
+}
+
+/***
+ * We need this to support subpixel AA text on OS X in two cases:
+ * text in DrawTargets that are not opaque and text over vibrant backgrounds.
+ * Skia normally doesn't support subpixel AA text on transparent backgrounds.
+ * To get around this, we have to wrap the Skia bytes with a CGContext and ask
+ * CG to draw the text.
+ * In vibrancy cases, we have to use a private API,
+ * CGContextSetFontSmoothingBackgroundColor, which sets the expected
+ * background color the text will draw onto so that CG can render the text
+ * properly. After that, we have to go back and fixup the pixels
+ * such that their alpha values are correct.
+ */
+bool
+DrawTargetSkia::FillGlyphsWithCG(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ MOZ_ASSERT(aFont->GetType() == FontType::MAC);
+ MOZ_ASSERT(aPattern.GetType() == PatternType::COLOR);
+
+ CGContextRef cgContext = BorrowCGContext(aOptions);
+ if (!cgContext) {
+ return false;
+ }
+
+ Vector<CGGlyph,32> glyphs;
+ Vector<CGPoint,32> positions;
+ if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
+ ReturnCGContext(cgContext);
+ return false;
+ }
+
+ SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
+ SetFontColor(cgContext, mColorSpace, aPattern);
+
+ ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
+ if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
+ ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
+ positions.begin(),
+ aBuffer.mNumGlyphs, cgContext);
+ } else {
+ CGContextSetFont(cgContext, macFont->mFont);
+ CGContextSetFontSize(cgContext, macFont->mSize);
+ CGContextShowGlyphsAtPositions(cgContext, glyphs.begin(), positions.begin(),
+ aBuffer.mNumGlyphs);
+ }
+
+ // Calculate the area of the text we just drew
+ CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
+ CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
+ glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
+ CGRect extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
+ delete[] bboxes;
+
+ CGAffineTransform cgTransform = CGContextGetCTM(cgContext);
+ extents = CGRectApplyAffineTransform(extents, cgTransform);
+
+ // Have to round it out to ensure we fully cover all pixels
+ Rect rect(extents.origin.x, extents.origin.y, extents.size.width, extents.size.height);
+ rect.RoundOut();
+ extents = CGRectMake(rect.x, rect.y, rect.width, rect.height);
+
+ EnsureValidPremultipliedData(cgContext, extents);
+
+ ReturnCGContext(cgContext);
+ return true;
+}
+
+static bool
+HasFontSmoothingBackgroundColor(const GlyphRenderingOptions* aRenderingOptions)
+{
+ // This should generally only be true if we have a popup context menu
+ if (aRenderingOptions && aRenderingOptions->GetType() == FontType::MAC) {
+ Color fontSmoothingBackgroundColor =
+ static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+ return fontSmoothingBackgroundColor.a > 0;
+ }
+
+ return false;
+}
+
+static bool
+ShouldUseCGToFillGlyphs(const GlyphRenderingOptions* aOptions, const Pattern& aPattern)
+{
+ return HasFontSmoothingBackgroundColor(aOptions) &&
+ aPattern.GetType() == PatternType::COLOR;
+}
+
+#endif
+
+static bool
+CanDrawFont(ScaledFont* aFont)
+{
+ switch (aFont->GetType()) {
+ case FontType::SKIA:
+ case FontType::CAIRO:
+ case FontType::FONTCONFIG:
+ case FontType::MAC:
+ case FontType::GDI:
+ case FontType::DWRITE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ if (!CanDrawFont(aFont)) {
+ return;
+ }
+
+ MarkChanged();
+
+#ifdef MOZ_WIDGET_COCOA
+ if (ShouldUseCGToFillGlyphs(aRenderingOptions, aPattern)) {
+ if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions, aRenderingOptions)) {
+ return;
+ }
+ }
+#endif
+
+ ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
+ SkTypeface* typeface = skiaFont->GetSkTypeface();
+ if (!typeface) {
+ return;
+ }
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ AntialiasMode aaMode = aFont->GetDefaultAAMode();
+ if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
+ aaMode = aOptions.mAntialiasMode;
+ }
+ bool aaEnabled = aaMode != AntialiasMode::NONE;
+
+ paint.mPaint.setAntiAlias(aaEnabled);
+ paint.mPaint.setTypeface(sk_ref_sp(typeface));
+ paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
+ paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aaMode);
+ paint.mPaint.setLCDRenderText(shouldLCDRenderText);
+
+ bool useSubpixelText = true;
+
+ switch (aFont->GetType()) {
+ case FontType::SKIA:
+ case FontType::CAIRO:
+ case FontType::FONTCONFIG:
+ // SkFontHost_cairo does not support subpixel text positioning,
+ // so only enable it for other font hosts.
+ useSubpixelText = false;
+ break;
+ case FontType::MAC:
+ if (aaMode == AntialiasMode::GRAY) {
+ // Normally, Skia enables LCD FontSmoothing which creates thicker fonts
+ // and also enables subpixel AA. CoreGraphics without font smoothing
+ // explicitly creates thinner fonts and grayscale AA.
+ // CoreGraphics doesn't support a configuration that produces thicker
+ // fonts with grayscale AA as LCD Font Smoothing enables or disables both.
+ // However, Skia supports it by enabling font smoothing (producing subpixel AA)
+ // and converts it to grayscale AA. Since Skia doesn't support subpixel AA on
+ // transparent backgrounds, we still want font smoothing for the thicker fonts,
+ // even if it is grayscale AA.
+ //
+ // With explicit Grayscale AA (from -moz-osx-font-smoothing:grayscale),
+ // we want to have grayscale AA with no smoothing at all. This means
+ // disabling the LCD font smoothing behaviour.
+ // To accomplish this we have to explicitly disable hinting,
+ // and disable LCDRenderText.
+ paint.mPaint.setHinting(SkPaint::kNo_Hinting);
+ }
+ break;
+ case FontType::GDI:
+ {
+ if (!shouldLCDRenderText && aaEnabled) {
+ // If we have non LCD GDI text, render the fonts as cleartype and convert them
+ // to grayscale. This seems to be what Chrome and IE are doing on Windows 7.
+ // This also applies if cleartype is disabled system wide.
+ paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag);
+ }
+ break;
+ }
+#ifdef XP_WIN
+ case FontType::DWRITE:
+ {
+ ScaledFontDWrite* dwriteFont = static_cast<ScaledFontDWrite*>(aFont);
+ paint.mPaint.setEmbeddedBitmapText(dwriteFont->UseEmbeddedBitmaps());
+
+ if (dwriteFont->ForceGDIMode()) {
+ paint.mPaint.setEmbeddedBitmapText(true);
+ useSubpixelText = false;
+ }
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ paint.mPaint.setSubpixelText(useSubpixelText);
+
+ std::vector<uint16_t> indices;
+ std::vector<SkPoint> offsets;
+ indices.resize(aBuffer.mNumGlyphs);
+ offsets.resize(aBuffer.mNumGlyphs);
+
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ indices[i] = aBuffer.mGlyphs[i].mIndex;
+ offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
+ offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
+ }
+
+ mCanvas->drawPosText(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), paint.mPaint);
+}
+
+void
+DrawTargetSkia::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
+
+ Maybe<MutexAutoLock> lock;
+ SkPaint maskPaint;
+ SetPaintPattern(maskPaint, aMask, lock);
+
+ SkLayerRasterizer::Builder builder;
+ builder.addLayer(maskPaint);
+ sk_sp<SkLayerRasterizer> raster(builder.detach());
+ paint.mPaint.setRasterizer(raster);
+
+ mCanvas->drawPaint(paint.mPaint);
+}
+
+void
+DrawTargetSkia::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
+ if (!alphaMask) {
+ gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
+ return;
+ }
+
+ mCanvas->drawImage(alphaMask, aOffset.x, aOffset.y, &paint.mPaint);
+}
+
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ // Composite the 3D transform with the DT's transform.
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ if (fullMat.IsSingular()) {
+ return false;
+ }
+ // Transform the surface bounds and clip to this DT.
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ // Offset the matrix by the transformed origin.
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+
+ // Read in the source data.
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
+ if (!srcImage) {
+ return true;
+ }
+
+ // Set up an intermediate destination surface only the size of the transformed bounds.
+ // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+ RefPtr<DataSourceSurface> dstSurf =
+ Factory::CreateDataSourceSurface(xformBounds.Size(),
+ !srcImage->isOpaque() ?
+ aSurface->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32,
+ true);
+ if (!dstSurf) {
+ return false;
+ }
+ sk_sp<SkCanvas> dstCanvas(
+ SkCanvas::NewRasterDirect(
+ SkImageInfo::Make(xformBounds.width, xformBounds.height,
+ GfxFormatToSkiaColorType(dstSurf->GetFormat()),
+ kPremul_SkAlphaType),
+ dstSurf->GetData(), dstSurf->Stride()));
+ if (!dstCanvas) {
+ return false;
+ }
+
+ // Do the transform.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ paint.setBlendMode(SkBlendMode::kSrc);
+
+ SkMatrix xform;
+ GfxMatrixToSkiaMatrix(fullMat, xform);
+ dstCanvas->setMatrix(xform);
+
+ dstCanvas->drawImage(srcImage, 0, 0, &paint);
+ dstCanvas->flush();
+
+ // Temporarily reset the DT's transform, since it has already been composed above.
+ Matrix origTransform = mTransform;
+ SetTransform(Matrix());
+
+ // Draw the transformed surface within the transformed bounds.
+ DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+ SetTransform(origTransform);
+
+ return true;
+}
+
+bool
+DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ if (aMatrix.IsSingular()) {
+ return false;
+ }
+
+ MarkChanged();
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
+ if (!image) {
+ return true;
+ }
+
+ mCanvas->save();
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterQuality(kLow_SkFilterQuality);
+
+ SkMatrix xform;
+ GfxMatrixToSkiaMatrix(aMatrix, xform);
+ mCanvas->concat(xform);
+
+ mCanvas->drawImage(image, 0, 0, &paint);
+
+ mCanvas->restore();
+
+ return true;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
+
+ if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
+ gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize;
+ return nullptr;
+ }
+
+ return newSurf.forget();
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ // Try to create a GPU draw target first if we're currently using the GPU.
+ // Mark the DT as cached so that shadow DTs, extracted subrects, and similar can be reused.
+ if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
+ return target.forget();
+ }
+ // Otherwise, just fall back to a software draw target.
+ }
+#endif
+
+#ifdef DEBUG
+ if (!IsBackedByPixels(mCanvas.get())) {
+ // If our canvas is backed by vector storage such as PDF then we want to
+ // create a new DrawTarget with similar storage to avoid losing fidelity
+ // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
+ // back onto us since a raster will be drawn instead of vector commands).
+ NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
+ }
+#endif
+
+ if (!target->Init(aSize, aFormat)) {
+ return nullptr;
+ }
+ return target.forget();
+}
+
+bool
+DrawTargetSkia::UsingSkiaGPU() const
+{
+#ifdef USE_SKIA_GPU
+ return !!mGrContext;
+#else
+ return false;
+#endif
+}
+
+#ifdef USE_SKIA_GPU
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
+{
+ // Check if the underlying SkImage already has an associated GrTexture.
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
+ if (!image || image->isTextureBacked()) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // Upload the SkImage to a GrTexture otherwise.
+ sk_sp<SkImage> texture = image->makeTextureImage(mGrContext.get());
+ if (texture) {
+ // Create a new SourceSurfaceSkia whose SkImage contains the GrTexture.
+ RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+ if (surface->InitFromImage(texture, aSurface->GetFormat())) {
+ return surface.forget();
+ }
+ }
+
+ // The data was too big to fit in a GrTexture.
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ // It is already a Skia source surface, so just reuse it as-is.
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // Wrap it in a Skia source surface so that can do tiled uploads on-demand.
+ RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+ surface->InitFromImage(image);
+ return surface.forget();
+}
+#endif
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ return OptimizeGPUSourceSurface(aSurface);
+ }
+#endif
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
+
+ // For plugins, GDI can sometimes just write 0 to the alpha channel
+ // even for RGBX formats. In this case, we have to manually write
+ // the alpha channel to make Skia happy with RGBX and in case GDI
+ // writes some bad data. Luckily, this only happens on plugins.
+ WriteRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
+ dataSurface->Stride(), dataSurface->GetFormat());
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ return OptimizeGPUSourceSurface(aSurface);
+ }
+#endif
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // If we're not using skia-gl then drawing doesn't require any
+ // uploading, so any data surface is fine. Call GetDataSurface
+ // to trigger any required readback so that it only happens
+ // once.
+ RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
+ MOZ_ASSERT(VerifyRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
+ dataSurface->Stride(), dataSurface->GetFormat()));
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
+ // Wrap the OpenGL texture id in a Skia texture handle.
+ GrBackendTextureDesc texDesc;
+ texDesc.fWidth = aSurface.mSize.width;
+ texDesc.fHeight = aSurface.mSize.height;
+ texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ texDesc.fConfig = GfxFormatToGrConfig(aSurface.mFormat);
+
+ GrGLTextureInfo texInfo;
+ texInfo.fTarget = LOCAL_GL_TEXTURE_2D;
+ texInfo.fID = (GrGLuint)(uintptr_t)aSurface.mSurface;
+ texDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&texInfo);
+
+ sk_sp<SkImage> texture =
+ SkImage::MakeFromAdoptedTexture(mGrContext.get(), texDesc,
+ GfxFormatToSkiaAlphaType(aSurface.mFormat));
+ RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
+ if (texture && newSurf->InitFromImage(texture, aSurface.mFormat)) {
+ return newSurf.forget();
+ }
+ return nullptr;
+ }
+#endif
+
+ return nullptr;
+}
+
+void
+DrawTargetSkia::CopySurface(SourceSurface *aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint &aDestination)
+{
+ MarkChanged();
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
+ if (!image) {
+ return;
+ }
+
+ mCanvas->save();
+ mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x), SkIntToScalar(aDestination.y)));
+ mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.width, aSourceRect.height), kReplace_SkClipOp);
+
+ SkPaint paint;
+ if (!image->isOpaque()) {
+ // Keep the xfermode as SOURCE_OVER for opaque bitmaps
+ // http://code.google.com/p/skia/issues/detail?id=628
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ // drawImage with A8 images ends up doing a mask operation
+ // so we need to clear before
+ if (SkImageIsMask(image)) {
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ }
+ mCanvas->drawImage(image, -SkIntToScalar(aSourceRect.x), -SkIntToScalar(aSourceRect.y), &paint);
+ mCanvas->restore();
+}
+
+bool
+DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
+{
+ if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
+ return false;
+ }
+
+ // we need to have surfaces that have a stride aligned to 4 for interop with cairo
+ SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
+ size_t stride = SkAlign4(info.minRowBytes());
+ mSurface = SkSurface::MakeRaster(info, stride, nullptr);
+ if (!mSurface) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+
+ if (info.isOpaque()) {
+ mCanvas->clear(SK_ColorBLACK);
+ }
+ return true;
+}
+
+bool
+DrawTargetSkia::Init(SkCanvas* aCanvas)
+{
+ mCanvas = sk_ref_sp(aCanvas);
+
+ SkImageInfo imageInfo = mCanvas->imageInfo();
+
+ // If the canvas is backed by pixels we clear it to be on the safe side. If
+ // it's not (for example, for PDF output) we don't.
+ if (IsBackedByPixels(mCanvas.get())) {
+ SkColor clearColor = imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+ mCanvas->clear(clearColor);
+ }
+
+ SkISize size = mCanvas->getBaseLayerSize();
+ mSize.width = size.width();
+ mSize.height = size.height();
+ mFormat = SkiaColorTypeToGfxFormat(imageInfo.colorType(),
+ imageInfo.alphaType());
+ return true;
+}
+
+#ifdef USE_SKIA_GPU
+/** Indicating a DT should be cached means that space will be reserved in Skia's cache
+ * for the render target at creation time, with any unused resources exceeding the cache
+ * limits being purged. When the DT is freed, it will then be guaranteed to be kept around
+ * for subsequent allocations until it gets incidentally purged.
+ *
+ * If it is not marked as cached, no space will be purged to make room for the render
+ * target in the cache. When the DT is freed, If there is space within the resource limits
+ * it may be added to the cache, otherwise it will be freed immediately if the cache is
+ * already full.
+ *
+ * If you want to ensure that the resources will be kept around for reuse, it is better
+ * to mark them as cached. Such resources should be short-lived to ensure they don't
+ * permanently tie up cache resource limits. Long-lived resources should generally be
+ * left as uncached.
+ *
+ * In neither case will cache resource limits affect whether the resource allocation
+ * succeeds. The amount of in-use GPU resources is allowed to exceed the size of the cache.
+ * Thus, only hard GPU out-of-memory conditions will cause resource allocation to fail.
+ */
+bool
+DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aCached)
+{
+ MOZ_ASSERT(aGrContext, "null GrContext");
+
+ if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
+ return false;
+ }
+
+ // Create a GPU rendertarget/texture using the supplied GrContext.
+ // NewRenderTarget also implicitly clears the underlying texture on creation.
+ mSurface =
+ SkSurface::MakeRenderTarget(aGrContext,
+ SkBudgeted(aCached),
+ MakeSkiaImageInfo(aSize, aFormat));
+ if (!mSurface) {
+ return false;
+ }
+
+ mGrContext = sk_ref_sp(aGrContext);
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+ return true;
+}
+
+#endif
+
+bool
+DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized)
+{
+ MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) ||
+ aUninitialized || VerifyRGBXFormat(aData, aSize, aStride, aFormat));
+
+ mSurface = SkSurface::MakeRasterDirect(MakeSkiaImageInfo(aSize, aFormat), aData, aStride);
+ if (!mSurface) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+ return true;
+}
+
+void
+DrawTargetSkia::SetTransform(const Matrix& aTransform)
+{
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(aTransform, mat);
+ mCanvas->setMatrix(mat);
+ mTransform = aTransform;
+}
+
+void*
+DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
+{
+#ifdef USE_SKIA_GPU
+ if (aType == NativeSurfaceType::OPENGL_TEXTURE && mSurface) {
+ GrBackendObject handle = mSurface->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess);
+ if (handle) {
+ return (void*)(uintptr_t)reinterpret_cast<GrGLTextureInfo *>(handle)->fID;
+ }
+ }
+#endif
+ return nullptr;
+}
+
+
+already_AddRefed<PathBuilder>
+DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const
+{
+ return MakeAndAddRef<PathBuilderSkia>(aFillRule);
+}
+
+void
+DrawTargetSkia::ClearRect(const Rect &aRect)
+{
+ MarkChanged();
+ mCanvas->save();
+ mCanvas->clipRect(RectToSkRect(aRect), kIntersect_SkClipOp, true);
+ SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+ mCanvas->clear(clearColor);
+ mCanvas->restore();
+}
+
+void
+DrawTargetSkia::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+ mCanvas->save();
+ mCanvas->clipPath(skiaPath->GetPath(), kIntersect_SkClipOp, true);
+}
+
+void
+DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ // Build a region by unioning all the rects together.
+ SkRegion region;
+ for (uint32_t i = 0; i < aCount; i++) {
+ region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
+ }
+
+ // Clip with the resulting region. clipRegion does not transform
+ // this region by the current transform, unlike the other SkCanvas
+ // clip methods, so it is just passed through in device-space.
+ mCanvas->save();
+ mCanvas->clipRegion(region, kIntersect_SkClipOp);
+}
+
+void
+DrawTargetSkia::PushClipRect(const Rect& aRect)
+{
+ SkRect rect = RectToSkRect(aRect);
+
+ mCanvas->save();
+ mCanvas->clipRect(rect, kIntersect_SkClipOp, true);
+}
+
+void
+DrawTargetSkia::PopClip()
+{
+ mCanvas->restore();
+}
+
+// Image filter that just passes the source through to the result unmodified.
+class CopyLayerImageFilter : public SkImageFilter
+{
+public:
+ CopyLayerImageFilter()
+ : SkImageFilter(nullptr, 0, nullptr)
+ {}
+
+ virtual sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
+ const Context& ctx,
+ SkIPoint* offset) const override {
+ offset->set(0, 0);
+ return sk_ref_sp(source);
+ }
+
+ SK_TO_STRING_OVERRIDE()
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CopyLayerImageFilter)
+};
+
+sk_sp<SkFlattenable>
+CopyLayerImageFilter::CreateProc(SkReadBuffer& buffer)
+{
+ SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
+ return sk_make_sp<CopyLayerImageFilter>();
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void
+CopyLayerImageFilter::toString(SkString* str) const
+{
+ str->append("CopyLayerImageFilter: ()");
+}
+#endif
+
+void
+DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ PushedLayer layer(GetPermitSubpixelAA(), aOpaque, aOpacity, aMask, aMaskTransform);
+ mPushedLayers.push_back(layer);
+
+ SkPaint paint;
+
+ // If we have a mask, set the opacity to 0 so that SkCanvas::restore skips
+ // implicitly drawing the layer so that we can properly mask it in PopLayer.
+ paint.setAlpha(aMask ? 0 : ColorFloatToByte(aOpacity));
+
+ SkRect bounds = IntRectToSkRect(aBounds);
+
+ sk_sp<SkImageFilter> backdrop(aCopyBackground ? new CopyLayerImageFilter : nullptr);
+
+ SkCanvas::SaveLayerRec saveRec(aBounds.IsEmpty() ? nullptr : &bounds,
+ &paint,
+ backdrop.get(),
+ aOpaque ? SkCanvas::kIsOpaque_SaveLayerFlag : 0);
+
+ mCanvas->saveLayer(saveRec);
+
+ SetPermitSubpixelAA(aOpaque);
+
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRelease(mCG);
+ mCG = nullptr;
+#endif
+}
+
+void
+DrawTargetSkia::PopLayer()
+{
+ MarkChanged();
+
+ MOZ_ASSERT(mPushedLayers.size());
+ const PushedLayer& layer = mPushedLayers.back();
+
+ if (layer.mMask) {
+ // If we have a mask, take a reference to the top layer's device so that
+ // we can mask it ourselves. This assumes we forced SkCanvas::restore to
+ // skip implicitly drawing the layer.
+ sk_sp<SkBaseDevice> layerDevice = sk_ref_sp(mCanvas->getTopDevice());
+ SkIRect layerBounds = layerDevice->getGlobalBounds();
+ sk_sp<SkImage> layerImage;
+ SkPixmap layerPixmap;
+ if (layerDevice->peekPixels(&layerPixmap)) {
+ layerImage = SkImage::MakeFromRaster(layerPixmap, nullptr, nullptr);
+#ifdef USE_SKIA_GPU
+ } else if (GrDrawContext* drawCtx = mCanvas->internal_private_accessTopLayerDrawContext()) {
+ drawCtx->prepareForExternalIO();
+ if (GrTexture* tex = drawCtx->accessRenderTarget()->asTexture()) {
+ layerImage = sk_make_sp<SkImage_Gpu>(layerBounds.width(), layerBounds.height(),
+ kNeedNewImageUniqueID,
+ layerDevice->imageInfo().alphaType(),
+ tex, nullptr, SkBudgeted::kNo);
+ }
+#endif
+ }
+
+ // Restore the background with the layer's device left alive.
+ mCanvas->restore();
+
+ SkPaint paint;
+ paint.setAlpha(ColorFloatToByte(layer.mOpacity));
+
+ SkMatrix maskMat, layerMat;
+ // Get the total transform affecting the mask, considering its pattern
+ // transform and the current canvas transform.
+ GfxMatrixToSkiaMatrix(layer.mMaskTransform, maskMat);
+ maskMat.postConcat(mCanvas->getTotalMatrix());
+ if (!maskMat.invert(&layerMat)) {
+ gfxDebug() << *this << ": PopLayer() failed to invert mask transform";
+ } else {
+ // The layer should not be affected by the current canvas transform,
+ // even though the mask is. So first we use the inverse of the transform
+ // affecting the mask, then add back on the layer's origin.
+ layerMat.preTranslate(layerBounds.x(), layerBounds.y());
+
+ if (layerImage) {
+ paint.setShader(layerImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &layerMat));
+ } else {
+ paint.setColor(SK_ColorTRANSPARENT);
+ }
+
+ Maybe<MutexAutoLock> lock;
+ sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
+ if (!alphaMask) {
+ gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
+ } else {
+ mCanvas->save();
+
+ // The layer may be smaller than the canvas size, so make sure drawing is
+ // clipped to within the bounds of the layer.
+ mCanvas->resetMatrix();
+ mCanvas->clipRect(SkRect::Make(layerBounds));
+
+ mCanvas->setMatrix(maskMat);
+ mCanvas->drawImage(alphaMask, 0, 0, &paint);
+
+ mCanvas->restore();
+ }
+ }
+ } else {
+ mCanvas->restore();
+ }
+
+ SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
+
+ mPushedLayers.pop_back();
+
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRelease(mCG);
+ mCG = nullptr;
+#endif
+}
+
+already_AddRefed<GradientStops>
+DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
+{
+ std::vector<GradientStop> stops;
+ stops.resize(aNumStops);
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ stops[i] = aStops[i];
+ }
+ std::stable_sort(stops.begin(), stops.end());
+
+ return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
+}
+
+already_AddRefed<FilterNode>
+DrawTargetSkia::CreateFilter(FilterType aType)
+{
+ return FilterNodeSoftware::Create(aType);
+}
+
+void
+DrawTargetSkia::MarkChanged()
+{
+ if (mSnapshot) {
+ mSnapshot->DrawTargetWillChange();
+ mSnapshot = nullptr;
+
+ // Handle copying of any image snapshots bound to the surface.
+ if (mSurface) {
+ mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+ }
+ }
+}
+
+void
+DrawTargetSkia::SnapshotDestroyed()
+{
+ mSnapshot = nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetSkia.h b/system/graphics/2d/DrawTargetSkia.h
new file mode 100644
index 000000000..9c7dedf1f
--- /dev/null
+++ b/system/graphics/2d/DrawTargetSkia.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_SOURCESURFACESKIA_H
+#define _MOZILLA_GFX_SOURCESURFACESKIA_H
+
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/core/SkSurface.h"
+
+#include "2D.h"
+#include "HelpersSkia.h"
+#include "Rect.h"
+#include "PathSkia.h"
+#include <sstream>
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceSkia;
+
+class DrawTargetSkia : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override)
+ DrawTargetSkia();
+ virtual ~DrawTargetSkia();
+
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override { return mSize; }
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin = nullptr) override;
+ virtual void ReleaseBits(uint8_t* aData) override;
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override;
+ virtual void ClearRect(const Rect &aRect) override;
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect& aRect) override;
+ virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+ virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+ virtual void SetTransform(const Matrix &aTransform) override;
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
+ virtual void DetachAllSnapshots() override { MarkChanged(); }
+
+ bool Init(const IntSize &aSize, SurfaceFormat aFormat);
+ bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+ bool Init(SkCanvas* aCanvas);
+
+#ifdef USE_SKIA_GPU
+ bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aCached);
+ virtual bool
+ InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat) override {
+ return InitWithGrContext(aGrContext, aSize, aFormat, false);
+ }
+
+ already_AddRefed<SourceSurface> OptimizeGPUSourceSurface(SourceSurface *aSurface) const;
+#endif
+
+ // Skia assumes that texture sizes fit in 16-bit signed integers.
+ static size_t GetMaxSurfaceSize() {
+ return 32767;
+ }
+
+ operator std::string() const {
+ std::stringstream stream;
+ stream << "DrawTargetSkia(" << this << ")";
+ return stream.str();
+ }
+
+private:
+ friend class SourceSurfaceSkia;
+ void SnapshotDestroyed();
+
+ void MarkChanged();
+
+ bool ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode);
+
+ bool UsingSkiaGPU() const;
+
+ struct PushedLayer
+ {
+ PushedLayer(bool aOldPermitSubpixelAA,
+ bool aOpaque,
+ Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform)
+ : mOldPermitSubpixelAA(aOldPermitSubpixelAA),
+ mOpaque(aOpaque),
+ mOpacity(aOpacity),
+ mMask(aMask),
+ mMaskTransform(aMaskTransform)
+ {}
+ bool mOldPermitSubpixelAA;
+ bool mOpaque;
+ Float mOpacity;
+ RefPtr<SourceSurface> mMask;
+ Matrix mMaskTransform;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+
+#ifdef USE_SKIA_GPU
+ sk_sp<GrContext> mGrContext;
+#endif
+
+ IntSize mSize;
+ sk_sp<SkSurface> mSurface;
+ sk_sp<SkCanvas> mCanvas;
+ SourceSurfaceSkia* mSnapshot;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_SOURCESURFACESKIA_H
diff --git a/system/graphics/2d/DrawTargetTiled.cpp b/system/graphics/2d/DrawTargetTiled.cpp
new file mode 100644
index 000000000..fd7465408
--- /dev/null
+++ b/system/graphics/2d/DrawTargetTiled.cpp
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetTiled.h"
+#include "Logging.h"
+#include "PathHelpers.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DrawTargetTiled::DrawTargetTiled()
+{
+}
+
+bool
+DrawTargetTiled::Init(const TileSet& aTiles)
+{
+ if (!aTiles.mTileCount) {
+ return false;
+ }
+
+ mTiles.reserve(aTiles.mTileCount);
+ for (size_t i = 0; i < aTiles.mTileCount; ++i) {
+ mTiles.push_back(TileInternal(aTiles.mTiles[i]));
+ if (!aTiles.mTiles[i].mDrawTarget) {
+ return false;
+ }
+ if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
+ mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
+ return false;
+ }
+ uint32_t newXMost = max(mRect.XMost(),
+ mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
+ uint32_t newYMost = max(mRect.YMost(),
+ mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
+ mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
+ mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
+ mRect.width = newXMost - mRect.x;
+ mRect.height = newYMost - mRect.y;
+ mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y));
+ }
+ mFormat = mTiles[0].mDrawTarget->GetFormat();
+ return true;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetTiled::Snapshot()
+{
+ return MakeAndAddRef<SnapshotTiled>(mTiles, mRect);
+}
+
+void
+DrawTargetTiled::DetachAllSnapshots()
+{}
+
+// Skip the mClippedOut check since this is only used for Flush() which
+// should happen even if we're clipped.
+#define TILED_COMMAND(command) \
+ void \
+ DrawTargetTiled::command() \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ mTiles[i].mDrawTarget->command(); \
+ } \
+ }
+#define TILED_COMMAND1(command, type1) \
+ void \
+ DrawTargetTiled::command(type1 arg1) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1); \
+ } \
+ }
+#define TILED_COMMAND3(command, type1, type2, type3) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
+ } \
+ }
+#define TILED_COMMAND4(command, type1, type2, type3, type4) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
+ } \
+ }
+#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
+ } \
+ }
+
+TILED_COMMAND(Flush)
+TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
+TILED_COMMAND1(ClearRect, const Rect&)
+TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
+TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
+TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
+
+void
+DrawTargetTiled::PushClip(const Path* aPath)
+{
+ mClippedOutTilesStack.push_back(std::vector<uint32_t>());
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+
+ Rect deviceRect = aPath->GetBounds(mTransform);
+
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->PushClip(aPath);
+ } else {
+ mTiles[i].mClippedOut = true;
+ clippedTiles.push_back(i);
+ }
+ }
+ }
+}
+
+void
+DrawTargetTiled::PushClipRect(const Rect& aRect)
+{
+ mClippedOutTilesStack.push_back(std::vector<uint32_t>());
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->PushClipRect(aRect);
+ } else {
+ mTiles[i].mClippedOut = true;
+ clippedTiles.push_back(i);
+ }
+ }
+ }
+}
+
+void
+DrawTargetTiled::PopClip()
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ mTiles[i].mDrawTarget->PopClip();
+ }
+ }
+
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+ for (size_t i = 0; i < clippedTiles.size(); i++) {
+ mTiles[clippedTiles[i]].mClippedOut = false;
+ }
+
+ mClippedOutTilesStack.pop_back();
+}
+
+void
+DrawTargetTiled::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ IntPoint tileOrigin = mTiles[i].mTileOrigin;
+ IntSize tileSize = mTiles[i].mDrawTarget->GetSize();
+ if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) {
+ continue;
+ }
+ // CopySurface ignores the transform, account for that here.
+ mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin);
+ }
+}
+
+void
+DrawTargetTiled::SetTransform(const Matrix& aTransform)
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ Matrix mat = aTransform;
+ mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
+ mTiles[i].mDrawTarget->SetTransform(mat);
+ }
+ DrawTarget::SetTransform(aTransform);
+}
+
+void
+DrawTargetTiled::DrawSurface(SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfaceOptions, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aDest);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->DrawSurface(aSurface, aDest, aSource, aSurfaceOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ // Approximate the stroke extents, since Path::GetStrokeExtents can be slow
+ Rect deviceRect = aPath->GetBounds(mTransform);
+ deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->Stroke(aPath, aPattern, aStrokeOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+ Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform);
+ Rect outerRect = deviceRect;
+ outerRect.Inflate(strokeMargin);
+ Rect innerRect;
+ if (mTransform.IsRectilinear()) {
+ // If rects are mapped to rects, we can compute the inner rect
+ // of the stroked rect.
+ innerRect = deviceRect;
+ innerRect.Deflate(strokeMargin);
+ }
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (mTiles[i].mClippedOut) {
+ continue;
+ }
+ Rect tileRect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height);
+ if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) {
+ mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size()));
+ Rect deviceRect = mTransform.TransformBounds(lineBounds);
+ deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = aPath->GetBounds(mTransform);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+ // intermediate surface, that would require tweaking the code in here a little though.
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ IntRect bounds = aBounds;
+ bounds.MoveBy(-mTiles[i].mTileOrigin);
+ mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds);
+ }
+}
+
+void
+DrawTargetTiled::PopLayer()
+{
+ // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+ // intermediate surface, that would require tweaking the code in here a little though.
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ mTiles[i].mDrawTarget->PopLayer();
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/DrawTargetTiled.h b/system/graphics/2d/DrawTargetTiled.h
new file mode 100644
index 000000000..23e8318a3
--- /dev/null
+++ b/system/graphics/2d/DrawTargetTiled.h
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETTILED_H_
+#define MOZILLA_GFX_DRAWTARGETTILED_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include "Logging.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct TileInternal : public Tile {
+ TileInternal()
+ : mClippedOut(false)
+ {}
+
+ explicit TileInternal(const Tile& aOther)
+ : Tile(aOther)
+ , mClippedOut(false)
+ {}
+
+ bool mClippedOut;
+};
+
+
+class DrawTargetTiled : public DrawTarget
+{
+public:
+ DrawTargetTiled();
+
+ bool Init(const TileSet& mTiles);
+
+ virtual bool IsTiledDrawTarget() const override { return true; }
+
+ virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); }
+ virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual void DetachAllSnapshots() override;
+ virtual IntSize GetSize() override {
+ MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
+ return IntSize(mRect.XMost(), mRect.YMost());
+ }
+
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override { /* Not implemented */ MOZ_CRASH("GFX: DrawSurfaceWithShadow"); }
+
+ virtual void ClearRect(const Rect &aRect) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+
+ virtual void SetTransform(const Matrix &aTransform) override;
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
+ {
+ return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
+ {
+ return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
+ {
+ return mTiles[0].mDrawTarget->CreateFilter(aType);
+ }
+
+private:
+ std::vector<TileInternal> mTiles;
+ std::vector<std::vector<uint32_t> > mClippedOutTilesStack;
+ IntRect mRect;
+};
+
+class SnapshotTiled : public SourceSurface
+{
+public:
+ SnapshotTiled(const std::vector<TileInternal>& aTiles, const IntRect& aRect)
+ : mRect(aRect)
+ {
+ for (size_t i = 0; i < aTiles.size(); i++) {
+ mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot());
+ mOrigins.push_back(aTiles[i].mTileOrigin);
+ }
+ }
+
+ virtual SurfaceType GetType() const { return SurfaceType::TILED; }
+ virtual IntSize GetSize() const {
+ MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
+ return IntSize(mRect.XMost(), mRect.YMost());
+ }
+ virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); }
+
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface()
+ {
+ RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat());
+
+ DataSourceSurface::MappedSurface mappedSurf;
+ if (!surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf)) {
+ gfxCriticalError() << "DrawTargetTiled::GetDataSurface failed to map surface";
+ return nullptr;
+ }
+
+ {
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData,
+ GetSize(), mappedSurf.mStride, GetFormat());
+
+ if (!dt) {
+ gfxWarning() << "DrawTargetTiled::GetDataSurface failed in CreateDrawTargetForData";
+ surf->Unmap();
+ return nullptr;
+ }
+ for (size_t i = 0; i < mSnapshots.size(); i++) {
+ RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface();
+ dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]);
+ }
+ }
+ surf->Unmap();
+
+ return surf.forget();
+ }
+
+ std::vector<RefPtr<SourceSurface>> mSnapshots;
+ std::vector<IntPoint> mOrigins;
+ IntRect mRect;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/DrawingJob.cpp b/system/graphics/2d/DrawingJob.cpp
new file mode 100644
index 000000000..728e330f4
--- /dev/null
+++ b/system/graphics/2d/DrawingJob.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawingJob.h"
+#include "JobScheduler.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+DrawingJobBuilder::DrawingJobBuilder()
+{}
+
+DrawingJobBuilder::~DrawingJobBuilder()
+{
+ MOZ_ASSERT(!mDrawTarget);
+}
+
+void
+DrawingJob::Clear()
+{
+ mCommandBuffer = nullptr;
+ mCursor = 0;
+}
+
+void
+DrawingJobBuilder::BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart)
+{
+ MOZ_ASSERT(mCommandOffsets.empty());
+ MOZ_ASSERT(aTarget);
+ mDrawTarget = aTarget;
+ mOffset = aOffset;
+ mStart = aStart;
+}
+
+DrawingJob*
+DrawingJobBuilder::EndDrawingJob(CommandBuffer* aCmdBuffer,
+ SyncObject* aCompletion,
+ WorkerThread* aPinToWorker)
+{
+ MOZ_ASSERT(mDrawTarget);
+ DrawingJob* task = new DrawingJob(mDrawTarget, mOffset, mStart, aCompletion, aPinToWorker);
+ task->mCommandBuffer = aCmdBuffer;
+ task->mCommandOffsets = Move(mCommandOffsets);
+
+ mDrawTarget = nullptr;
+ mOffset = IntPoint();
+ mStart = nullptr;
+
+ return task;
+}
+
+DrawingJob::DrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart, SyncObject* aCompletion,
+ WorkerThread* aPinToWorker)
+: Job(aStart, aCompletion, aPinToWorker)
+, mCommandBuffer(nullptr)
+, mCursor(0)
+, mDrawTarget(aTarget)
+, mOffset(aOffset)
+{
+ mCommandOffsets.reserve(64);
+}
+
+JobStatus
+DrawingJob::Run()
+{
+ while (mCursor < mCommandOffsets.size()) {
+
+ const DrawingCommand* cmd = mCommandBuffer->GetDrawingCommand(mCommandOffsets[mCursor]);
+
+ if (!cmd) {
+ return JobStatus::Error;
+ }
+
+ cmd->ExecuteOnDT(mDrawTarget);
+
+ ++mCursor;
+ }
+
+ return JobStatus::Complete;
+}
+
+DrawingJob::~DrawingJob()
+{
+ Clear();
+}
+
+const DrawingCommand*
+CommandBuffer::GetDrawingCommand(ptrdiff_t aId)
+{
+ return static_cast<DrawingCommand*>(mStorage.GetStorage(aId));
+}
+
+CommandBuffer::~CommandBuffer()
+{
+ mStorage.ForEach([](void* item){
+ static_cast<DrawingCommand*>(item)->~DrawingCommand();
+ });
+ mStorage.Clear();
+}
+
+void
+CommandBufferBuilder::BeginCommandBuffer(size_t aBufferSize)
+{
+ MOZ_ASSERT(!mCommands);
+ mCommands = new CommandBuffer(aBufferSize);
+}
+
+already_AddRefed<CommandBuffer>
+CommandBufferBuilder::EndCommandBuffer()
+{
+ return mCommands.forget();
+}
+
+} // namespace
+} // namespace
diff --git a/system/graphics/2d/DrawingJob.h b/system/graphics/2d/DrawingJob.h
new file mode 100644
index 000000000..a384dabda
--- /dev/null
+++ b/system/graphics/2d/DrawingJob.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMMANDBUFFER_H_
+#define MOZILLA_GFX_COMMANDBUFFER_H_
+
+#include <stdint.h>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/JobScheduler.h"
+#include "mozilla/gfx/IterableArena.h"
+#include "mozilla/RefCounted.h"
+#include "DrawCommand.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawingCommand;
+class PrintCommand;
+class SignalCommand;
+class DrawingJob;
+class WaitCommand;
+
+class SyncObject;
+class MultiThreadedJobQueue;
+
+class DrawTarget;
+
+class DrawingJobBuilder;
+class CommandBufferBuilder;
+
+/// Contains a sequence of immutable drawing commands that are typically used by
+/// several DrawingJobs.
+///
+/// CommandBuffer objects are built using CommandBufferBuilder.
+class CommandBuffer : public external::AtomicRefCounted<CommandBuffer>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(CommandBuffer)
+
+ ~CommandBuffer();
+
+ const DrawingCommand* GetDrawingCommand(ptrdiff_t aId);
+
+protected:
+ explicit CommandBuffer(size_t aSize = 256)
+ : mStorage(IterableArena::GROWABLE, aSize)
+ {}
+
+ IterableArena mStorage;
+ friend class CommandBufferBuilder;
+};
+
+/// Generates CommandBuffer objects.
+///
+/// The builder is a separate object to ensure that commands are not added to a
+/// submitted CommandBuffer.
+class CommandBufferBuilder
+{
+public:
+ void BeginCommandBuffer(size_t aBufferSize = 256);
+
+ already_AddRefed<CommandBuffer> EndCommandBuffer();
+
+ /// Build the CommandBuffer, command after command.
+ /// This must be used between BeginCommandBuffer and EndCommandBuffer.
+ template<typename T, typename... Args>
+ ptrdiff_t AddCommand(Args&&... aArgs)
+ {
+ static_assert(IsBaseOf<DrawingCommand, T>::value,
+ "T must derive from DrawingCommand");
+ return mCommands->mStorage.Alloc<T>(Forward<Args>(aArgs)...);
+ }
+
+ bool HasCommands() const { return !!mCommands; }
+
+protected:
+ RefPtr<CommandBuffer> mCommands;
+};
+
+/// Stores multiple commands to be executed sequencially.
+class DrawingJob : public Job {
+public:
+ ~DrawingJob();
+
+ virtual JobStatus Run() override;
+
+protected:
+ DrawingJob(DrawTarget* aTarget,
+ IntPoint aOffset,
+ SyncObject* aStart,
+ SyncObject* aCompletion,
+ WorkerThread* aPinToWorker = nullptr);
+
+ /// Runs the tasks's destructors and resets the buffer.
+ void Clear();
+
+ std::vector<ptrdiff_t> mCommandOffsets;
+ RefPtr<CommandBuffer> mCommandBuffer;
+ uint32_t mCursor;
+
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mOffset;
+
+ friend class DrawingJobBuilder;
+};
+
+/// Generates DrawingJob objects.
+///
+/// The builder is a separate object to ensure that commands are not added to a
+/// submitted DrawingJob.
+class DrawingJobBuilder {
+public:
+ DrawingJobBuilder();
+
+ ~DrawingJobBuilder();
+
+ /// Allocates a DrawingJob.
+ ///
+ /// call this method before starting to add commands.
+ void BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart = nullptr);
+
+ /// Build the DrawingJob, command after command.
+ /// This must be used between BeginDrawingJob and EndDrawingJob.
+ void AddCommand(ptrdiff_t offset)
+ {
+ mCommandOffsets.push_back(offset);
+ }
+
+ /// Finalizes and returns the drawing task.
+ ///
+ /// If aCompletion is not null, the sync object will be signaled after the
+ /// task buffer is destroyed (and after the destructor of the tasks have run).
+ /// In most cases this means after the completion of all tasks in the task buffer,
+ /// but also when the task buffer is destroyed due to an error.
+ DrawingJob* EndDrawingJob(CommandBuffer* aCmdBuffer,
+ SyncObject* aCompletion = nullptr,
+ WorkerThread* aPinToWorker = nullptr);
+
+ /// Returns true between BeginDrawingJob and EndDrawingJob, false otherwise.
+ bool HasDrawingJob() const { return !!mDrawTarget; }
+
+protected:
+ std::vector<ptrdiff_t> mCommandOffsets;
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mOffset;
+ RefPtr<SyncObject> mStart;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/2d/ExtendInputEffectD2D1.cpp b/system/graphics/2d/ExtendInputEffectD2D1.cpp
new file mode 100644
index 000000000..d48de53c3
--- /dev/null
+++ b/system/graphics/2d/ExtendInputEffectD2D1.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ExtendInputEffectD2D1.h"
+
+#include "Logging.h"
+
+#include "ShadersD2D1.h"
+#include "HelpersD2D.h"
+
+#include <vector>
+
+#define TEXTW(x) L##x
+#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
+
+static const PCWSTR kXmlDescription =
+ XML(
+ <?xml version='1.0'?>
+ <Effect>
+ <!-- System Properties -->
+ <Property name='DisplayName' type='string' value='ExtendInputEffect'/>
+ <Property name='Author' type='string' value='Mozilla'/>
+ <Property name='Category' type='string' value='Utility Effects'/>
+ <Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
+ <Inputs>
+ <Input name='InputEffect'/>
+ </Inputs>
+ <Property name='OutputRect' type='vector4'>
+ <Property name='DisplayName' type='string' value='Output Rect'/>
+ </Property>
+ </Effect>
+ );
+
+namespace mozilla {
+namespace gfx {
+
+ExtendInputEffectD2D1::ExtendInputEffectD2D1()
+ : mRefCount(0)
+ , mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX))
+{
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
+{
+ HRESULT hr;
+ hr = pTransformGraph->SetSingleTransformNode(this);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
+{
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
+{
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP_(ULONG)
+ExtendInputEffectD2D1::AddRef()
+{
+ return ++mRefCount;
+}
+
+IFACEMETHODIMP_(ULONG)
+ExtendInputEffectD2D1::Release()
+{
+ if (!--mRefCount) {
+ delete this;
+ return 0;
+ }
+ return mRefCount;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
+{
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
+ } else if (aIID == IID_ID2D1EffectImpl) {
+ *aPtr = static_cast<ID2D1EffectImpl*>(this);
+ } else if (aIID == IID_ID2D1DrawTransform) {
+ *aPtr = static_cast<ID2D1DrawTransform*>(this);
+ } else if (aIID == IID_ID2D1Transform) {
+ *aPtr = static_cast<ID2D1Transform*>(this);
+ } else if (aIID == IID_ID2D1TransformNode) {
+ *aPtr = static_cast<ID2D1TransformNode*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+
+ static_cast<IUnknown*>(*aPtr)->AddRef();
+ return S_OK;
+}
+
+static D2D1_RECT_L
+ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect)
+{
+ // Clamp values to LONG range. We can't use std::min/max here because we want
+ // the comparison to operate on a type that's different from the type of the
+ // result.
+ return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
+ aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
+ aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
+ aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w));
+}
+
+static D2D1_RECT_L
+IntersectRect(const D2D1_RECT_L& aRect1, const D2D1_RECT_L& aRect2)
+{
+ return D2D1::RectL(std::max(aRect1.left, aRect2.left),
+ std::max(aRect1.top, aRect2.top),
+ std::min(aRect1.right, aRect2.right),
+ std::min(aRect1.bottom, aRect2.bottom));
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect)
+{
+ // This transform only accepts one input, so there will only be one input rect.
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ // Set the output rect to the specified rect. This is the whole purpose of this effect.
+ *pOutputRect = ConvertFloatToLongRect(mOutputRect);
+ *pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const
+{
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ *pInputRects = *pOutputRect;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const
+{
+ MOZ_ASSERT(inputIndex == 0);
+
+ *pInvalidOutputRect = invalidInputRect;
+ return S_OK;
+}
+
+HRESULT
+ExtendInputEffectD2D1::Register(ID2D1Factory1 *aFactory)
+{
+ D2D1_PROPERTY_BINDING bindings[] = {
+ D2D1_VALUE_TYPE_BINDING(L"OutputRect", &ExtendInputEffectD2D1::SetOutputRect, &ExtendInputEffectD2D1::GetOutputRect),
+ };
+ HRESULT hr = aFactory->RegisterEffectFromString(CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to register extend input effect.";
+ }
+ return hr;
+}
+
+void
+ExtendInputEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+ aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
+}
+
+HRESULT __stdcall
+ExtendInputEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
+{
+ *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
+ (*aEffectImpl)->AddRef();
+
+ return S_OK;
+}
+
+}
+}
diff --git a/system/graphics/2d/ExtendInputEffectD2D1.h b/system/graphics/2d/ExtendInputEffectD2D1.h
new file mode 100644
index 000000000..724c6a71e
--- /dev/null
+++ b/system/graphics/2d/ExtendInputEffectD2D1.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
+#define MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
+
+#include <d2d1_1.h>
+#include <d2d1effectauthor.h>
+#include <d2d1effecthelpers.h>
+
+#include "2D.h"
+#include "mozilla/Attributes.h"
+
+// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
+DEFINE_GUID(CLSID_ExtendInputEffect,
+0x5fb55c7c, 0xd795, 0x4ba3, 0xa9, 0x5c, 0x22, 0x82, 0x5d, 0x0c, 0x4d, 0xf7);
+
+namespace mozilla {
+namespace gfx {
+
+enum {
+ EXTENDINPUT_PROP_OUTPUT_RECT = 0
+};
+
+// An effect type that passes through its input unchanged but sets the effect's
+// output rect to a specified rect. Unlike the built-in Crop effect, the
+// ExtendInput effect can extend the input rect, and not just make it smaller.
+// The added margins are filled with transparent black.
+// Some effects have different output depending on their input effect's output
+// rect, for example the Border effect (which repeats the edges of its input
+// effect's output rect) or the component transfer and color matrix effects
+// (which can transform transparent pixels into non-transparent ones, but only
+// inside their input effect's output rect).
+class ExtendInputEffectD2D1 final : public ID2D1EffectImpl
+ , public ID2D1DrawTransform
+{
+public:
+ // ID2D1EffectImpl
+ IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph);
+ IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
+ IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph);
+
+ // IUnknown
+ IFACEMETHODIMP_(ULONG) AddRef();
+ IFACEMETHODIMP_(ULONG) Release();
+ IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput);
+
+ // ID2D1Transform
+ IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect);
+ IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const;
+ IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const;
+
+ // ID2D1TransformNode
+ IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
+
+ // ID2D1DrawTransform
+ IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo) { return S_OK; }
+
+ static HRESULT Register(ID2D1Factory1* aFactory);
+ static void Unregister(ID2D1Factory1* aFactory);
+ static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
+
+ HRESULT SetOutputRect(D2D1_VECTOR_4F aOutputRect)
+ { mOutputRect = aOutputRect; return S_OK; }
+ D2D1_VECTOR_4F GetOutputRect() const { return mOutputRect; }
+
+private:
+ ExtendInputEffectD2D1();
+
+ uint32_t mRefCount;
+ D2D1_VECTOR_4F mOutputRect;
+};
+
+}
+}
+#undef SIMPLE_PROP
+
+#endif
diff --git a/system/graphics/2d/Factory.cpp b/system/graphics/2d/Factory.cpp
new file mode 100644
index 000000000..5bec8e9ad
--- /dev/null
+++ b/system/graphics/2d/Factory.cpp
@@ -0,0 +1,955 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+
+#ifdef USE_CAIRO
+#include "DrawTargetCairo.h"
+#include "ScaledFontCairo.h"
+#include "SourceSurfaceCairo.h"
+#endif
+
+#ifdef USE_SKIA
+#include "DrawTargetSkia.h"
+#include "ScaledFontBase.h"
+#ifdef MOZ_ENABLE_FREETYPE
+#define USE_SKIA_FREETYPE
+#include "ScaledFontCairo.h"
+#endif
+#endif
+
+#if defined(WIN32)
+#include "ScaledFontWin.h"
+#include "NativeFontResourceGDI.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include "ScaledFontFontconfig.h"
+#endif
+
+#ifdef WIN32
+#include "DrawTargetD2D1.h"
+#include "ScaledFontDWrite.h"
+#include "NativeFontResourceDWrite.h"
+#include <d3d10_1.h>
+#include "HelpersD2D.h"
+#endif
+
+#include "DrawTargetDual.h"
+#include "DrawTargetTiled.h"
+#include "DrawTargetRecording.h"
+
+#include "SourceSurfaceRawData.h"
+
+#include "DrawEventRecorder.h"
+
+#include "Logging.h"
+
+#include "mozilla/CheckedInt.h"
+
+#if defined(MOZ_LOGGING)
+GFX2D_API mozilla::LogModule*
+GetGFX2DLog()
+{
+ static mozilla::LazyLogModule sLog("gfx2d");
+ return sLog;
+}
+#endif
+
+// The following code was largely taken from xpcom/glue/SSE.cpp and
+// made a little simpler.
+enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
+
+#ifdef HAVE_CPUID_H
+
+#if !(defined(__SSE2__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
+#include <cpuid.h>
+
+static inline bool
+HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
+{
+ unsigned int regs[4];
+ return __get_cpuid(level, &regs[0], &regs[1], &regs[2], &regs[3]) &&
+ (regs[reg] & bit);
+}
+#endif
+
+#define HAVE_CPU_DETECTION
+#else
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+// MSVC 2005 or later supports __cpuid by intrin.h
+#include <intrin.h>
+
+#define HAVE_CPU_DETECTION
+#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
+
+// Define a function identical to MSVC function.
+#ifdef __i386
+static void
+__cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %esi, %ebx\n"
+ "cpuid\n"
+ "movl %eax, (%edi)\n"
+ "movl %ebx, 4(%edi)\n"
+ "movl %ecx, 8(%edi)\n"
+ "movl %edx, 12(%edi)\n"
+ "xchg %esi, %ebx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %edi
+ : "%ecx", "%edx", "%esi"
+ );
+}
+#else
+static void
+__cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %rsi, %rbx\n"
+ "cpuid\n"
+ "movl %eax, (%rdi)\n"
+ "movl %ebx, 4(%rdi)\n"
+ "movl %ecx, 8(%rdi)\n"
+ "movl %edx, 12(%rdi)\n"
+ "xchg %rsi, %rbx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %rdi
+ : "%ecx", "%edx", "%rsi"
+ );
+}
+
+#define HAVE_CPU_DETECTION
+#endif
+#endif
+
+#ifdef HAVE_CPU_DETECTION
+static inline bool
+HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
+{
+ // Check that the level in question is supported.
+ volatile int regs[4];
+ __cpuid((int *)regs, level & 0x80000000u);
+ if (unsigned(regs[0]) < level)
+ return false;
+ __cpuid((int *)regs, level);
+ return !!(unsigned(regs[reg]) & bit);
+}
+#endif
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+// In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
+int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
+
+#ifdef WIN32
+ID3D11Device *Factory::mD3D11Device = nullptr;
+ID2D1Device *Factory::mD2D1Device = nullptr;
+IDWriteFactory *Factory::mDWriteFactory = nullptr;
+#endif
+
+DrawEventRecorder *Factory::mRecorder;
+
+mozilla::gfx::Config* Factory::sConfig = nullptr;
+
+void
+Factory::Init(const Config& aConfig)
+{
+ MOZ_ASSERT(!sConfig);
+ sConfig = new Config(aConfig);
+
+ // Make sure we don't completely break rendering because of a typo in the
+ // pref or whatnot.
+ const int32_t kMinAllocPref = 10000000;
+ const int32_t kMinSizePref = 2048;
+ if (sConfig->mMaxAllocSize < kMinAllocPref) {
+ sConfig->mMaxAllocSize = kMinAllocPref;
+ }
+ if (sConfig->mMaxTextureSize < kMinSizePref) {
+ sConfig->mMaxTextureSize = kMinSizePref;
+ }
+}
+
+void
+Factory::ShutDown()
+{
+ if (sConfig) {
+ delete sConfig->mLogForwarder;
+ delete sConfig;
+ sConfig = nullptr;
+ }
+}
+
+bool
+Factory::HasSSE2()
+{
+#if defined(__SSE2__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+ // gcc with -msse2 (default on OSX and x86-64)
+ // cl.exe with -arch:SSE2 (default on x64 compiler)
+ return true;
+#elif defined(HAVE_CPU_DETECTION)
+ static enum {
+ UNINITIALIZED,
+ NO_SSE2,
+ HAS_SSE2
+ } sDetectionState = UNINITIALIZED;
+
+ if (sDetectionState == UNINITIALIZED) {
+ sDetectionState = HasCPUIDBit(1u, edx, (1u<<26)) ? HAS_SSE2 : NO_SSE2;
+ }
+ return sDetectionState == HAS_SSE2;
+#else
+ return false;
+#endif
+}
+
+// If the size is "reasonable", we want gfxCriticalError to assert, so
+// this is the option set up for it.
+inline int LoggerOptionsBasedOnSize(const IntSize& aSize)
+{
+ return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize));
+}
+
+bool
+Factory::ReasonableSurfaceSize(const IntSize &aSize)
+{
+ return Factory::CheckSurfaceSize(aSize, 8192);
+}
+
+bool
+Factory::AllowedSurfaceSize(const IntSize &aSize)
+{
+ if (sConfig) {
+ return Factory::CheckSurfaceSize(aSize,
+ sConfig->mMaxTextureSize,
+ sConfig->mMaxAllocSize);
+ }
+
+ return CheckSurfaceSize(aSize);
+}
+
+bool
+Factory::CheckBufferSize(int32_t bufSize)
+{
+ return !sConfig || bufSize < sConfig->mMaxAllocSize;
+}
+
+bool
+Factory::CheckSurfaceSize(const IntSize &sz,
+ int32_t extentLimit,
+ int32_t allocLimit)
+{
+ if (sz.width <= 0 || sz.height <= 0) {
+ gfxDebug() << "Surface width or height <= 0!";
+ return false;
+ }
+
+ // reject images with sides bigger than limit
+ if (extentLimit && (sz.width > extentLimit || sz.height > extentLimit)) {
+ gfxDebug() << "Surface size too large (exceeds extent limit)!";
+ return false;
+ }
+
+ // assuming 4 bytes per pixel, make sure the allocation size
+ // doesn't overflow a int32_t either
+ CheckedInt<int32_t> stride = GetAlignedStride<16>(sz.width, 4);
+ if (!stride.isValid() || stride.value() == 0) {
+ gfxDebug() << "Surface size too large (stride overflows int32_t)!";
+ return false;
+ }
+
+ CheckedInt<int32_t> numBytes = stride * sz.height;
+ if (!numBytes.isValid()) {
+ gfxDebug() << "Surface size too large (allocation size would overflow int32_t)!";
+ return false;
+ }
+
+ if (allocLimit && allocLimit < numBytes.value()) {
+ gfxDebug() << "Surface size too large (exceeds allocation limit)!";
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat)
+{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (CDT) " << aSize;
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> retVal;
+ switch (aBackend) {
+#ifdef WIN32
+ case BackendType::DIRECT2D1_1:
+ {
+ RefPtr<DrawTargetD2D1> newTarget;
+ newTarget = new DrawTargetD2D1();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ {
+ RefPtr<DrawTargetSkia> newTarget;
+ newTarget = new DrawTargetSkia();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+#ifdef USE_CAIRO
+ case BackendType::CAIRO:
+ {
+ RefPtr<DrawTargetCairo> newTarget;
+ newTarget = new DrawTargetCairo();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+ default:
+ return nullptr;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal);
+ }
+
+ if (!retVal) {
+ // Failed
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
+ }
+
+ return retVal.forget();
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT)
+{
+ return MakeAndAddRef<DrawTargetRecording>(aRecorder, aDT);
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetForData(BackendType aBackend,
+ unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ bool aUninitialized)
+{
+ MOZ_ASSERT(aData);
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DTD) " << aSize;
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> retVal;
+
+ switch (aBackend) {
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ {
+ RefPtr<DrawTargetSkia> newTarget;
+ newTarget = new DrawTargetSkia();
+ if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+#ifdef USE_CAIRO
+ case BackendType::CAIRO:
+ {
+ RefPtr<DrawTargetCairo> newTarget;
+ newTarget = new DrawTargetCairo();
+ if (newTarget->Init(aData, aSize, aStride, aFormat)) {
+ retVal = newTarget.forget();
+ }
+ break;
+ }
+#endif
+ default:
+ gfxCriticalNote << "Invalid draw target type specified: " << (int)aBackend;
+ return nullptr;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
+ }
+
+ if (!retVal) {
+ gfxCriticalNote << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize << ", Data: " << hexa((void *)aData) << ", Stride: " << aStride;
+ }
+
+ return retVal.forget();
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateTiledDrawTarget(const TileSet& aTileSet)
+{
+ RefPtr<DrawTargetTiled> dt = new DrawTargetTiled();
+
+ if (!dt->Init(aTileSet)) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+bool
+Factory::DoesBackendSupportDataDrawtarget(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::DIRECT2D:
+ case BackendType::DIRECT2D1_1:
+ case BackendType::RECORDING:
+ case BackendType::NONE:
+ case BackendType::BACKEND_LAST:
+ return false;
+ case BackendType::CAIRO:
+ case BackendType::SKIA:
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+Factory::GetMaxSurfaceSize(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::CAIRO:
+ return DrawTargetCairo::GetMaxSurfaceSize();
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ return DrawTargetSkia::GetMaxSurfaceSize();
+#endif
+#ifdef WIN32
+ case BackendType::DIRECT2D1_1:
+ return DrawTargetD2D1::GetMaxSurfaceSize();
+#endif
+ default:
+ return 0;
+ }
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
+{
+ switch (aNativeFont.mType) {
+#ifdef WIN32
+ case NativeFontType::DWRITE_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
+ }
+#if defined(USE_CAIRO) || defined(USE_SKIA)
+ case NativeFontType::GDI_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
+ }
+#endif
+#endif
+#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
+ case NativeFontType::CAIRO_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
+ }
+#endif
+ default:
+ gfxWarning() << "Invalid native font type specified.";
+ return nullptr;
+ }
+}
+
+already_AddRefed<NativeFontResource>
+Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize,
+ FontType aType)
+{
+ switch (aType) {
+#ifdef WIN32
+ case FontType::DWRITE:
+ {
+ return NativeFontResourceDWrite::Create(aData, aSize,
+ /* aNeedsCairo = */ false);
+ }
+#endif
+ case FontType::CAIRO:
+#ifdef USE_SKIA
+ case FontType::SKIA:
+#endif
+ {
+#ifdef WIN32
+ if (GetDWriteFactory()) {
+ return NativeFontResourceDWrite::Create(aData, aSize,
+ /* aNeedsCairo = */ true);
+ } else {
+ return NativeFontResourceGDI::Create(aData, aSize,
+ /* aNeedsCairo = */ true);
+ }
+#else
+ gfxWarning() << "Unable to create cairo scaled font from truetype data";
+ return nullptr;
+#endif
+ }
+ default:
+ gfxWarning() << "Unable to create requested font resource from truetype data";
+ return nullptr;
+ }
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
+{
+#ifdef USE_CAIRO
+ // In theory, we could pull the NativeFont out of the cairo_scaled_font_t*,
+ // but that would require a lot of code that would be otherwise repeated in
+ // various backends.
+ // Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
+ RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
+ static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
+ return font.forget();
+#else
+ return nullptr;
+#endif
+}
+
+#ifdef MOZ_WIDGET_GTK
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
+{
+ return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aSize);
+}
+#endif
+
+already_AddRefed<DrawTarget>
+Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB)
+{
+ MOZ_ASSERT(targetA && targetB);
+
+ RefPtr<DrawTarget> newTarget =
+ new DrawTargetDual(targetA, targetB);
+
+ RefPtr<DrawTarget> retVal = newTarget;
+
+ if (mRecorder) {
+ retVal = new DrawTargetRecording(mRecorder, retVal);
+ }
+
+ return retVal.forget();
+}
+
+
+#ifdef WIN32
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aTexture);
+
+ RefPtr<DrawTargetD2D1> newTarget;
+
+ newTarget = new DrawTargetD2D1();
+ if (newTarget->Init(aTexture, aFormat)) {
+ RefPtr<DrawTarget> retVal = newTarget;
+
+ if (mRecorder) {
+ retVal = new DrawTargetRecording(mRecorder, retVal, true);
+ }
+
+ return retVal.forget();
+ }
+
+ gfxWarning() << "Failed to create draw target for D3D11 texture.";
+
+ // Failed
+ return nullptr;
+}
+
+bool
+Factory::SetDWriteFactory(IDWriteFactory *aFactory)
+{
+ mDWriteFactory = aFactory;
+ return true;
+}
+
+bool
+Factory::SetDirect3D11Device(ID3D11Device *aDevice)
+{
+ mD3D11Device = aDevice;
+
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+
+ if (!aDevice) {
+ return true;
+ }
+
+ RefPtr<ID2D1Factory1> factory = D2DFactory1();
+
+ RefPtr<IDXGIDevice> device;
+ aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device));
+ HRESULT hr = factory->CreateDevice(device, &mD2D1Device);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr);
+
+ mD3D11Device = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+ID3D11Device*
+Factory::GetDirect3D11Device()
+{
+ return mD3D11Device;
+}
+
+ID2D1Device*
+Factory::GetD2D1Device()
+{
+ return mD2D1Device;
+}
+
+IDWriteFactory*
+Factory::GetDWriteFactory()
+{
+ return mDWriteFactory;
+}
+
+bool
+Factory::SupportsD2D1()
+{
+ return !!D2DFactory1();
+}
+
+already_AddRefed<GlyphRenderingOptions>
+Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams)
+{
+ return MakeAndAddRef<GlyphRenderingOptionsDWrite>(aParams);
+}
+
+uint64_t
+Factory::GetD2DVRAMUsageDrawTarget()
+{
+ return DrawTargetD2D1::mVRAMUsageDT;
+}
+
+uint64_t
+Factory::GetD2DVRAMUsageSourceSurface()
+{
+ return DrawTargetD2D1::mVRAMUsageSS;
+}
+
+void
+Factory::D2DCleanup()
+{
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+ DrawTargetD2D1::CleanupD2D();
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
+ const gfxFontStyle* aStyle,
+ float aSize,
+ bool aUseEmbeddedBitmap,
+ bool aForceGDIMode)
+{
+ return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aSize,
+ aUseEmbeddedBitmap, aForceGDIMode,
+ aStyle);
+}
+
+#endif // XP_WIN
+
+#ifdef USE_SKIA_GPU
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
+ if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
+ return nullptr;
+ }
+ return newTarget.forget();
+}
+
+#endif // USE_SKIA_GPU
+
+#ifdef USE_SKIA
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas)
+{
+ RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia();
+ if (!newTarget->Init(aCanvas)) {
+ return nullptr;
+ }
+ return newTarget.forget();
+}
+#endif
+
+void
+Factory::PurgeAllCaches()
+{
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxWarning() << "Allowing surface with invalid size (Cairo) " << aSize;
+ }
+
+ RefPtr<DrawTarget> retVal;
+
+#ifdef USE_CAIRO
+ RefPtr<DrawTargetCairo> newTarget = new DrawTargetCairo();
+
+ if (newTarget->Init(aSurface, aSize, aFormat)) {
+ retVal = newTarget;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
+ }
+#endif
+ return retVal.forget();
+}
+
+already_AddRefed<SourceSurface>
+Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
+{
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxWarning() << "Can't create a SourceSurface without a valid size";
+ return nullptr;
+ }
+
+#ifdef USE_CAIRO
+ return MakeAndAddRef<SourceSurfaceCairo>(aSurface, aSize, aFormat);
+#else
+ return nullptr;
+#endif
+}
+
+already_AddRefed<DataSourceSurface>
+Factory::CreateWrappingDataSourceSurface(uint8_t *aData,
+ int32_t aStride,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ SourceSurfaceDeallocator aDeallocator /* = nullptr */,
+ void* aClosure /* = nullptr */)
+{
+ // Just check for negative/zero size instead of the full AllowedSurfaceSize() - since
+ // the data is already allocated we do not need to check for a possible overflow - it
+ // already worked.
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ return nullptr;
+ }
+ if (!aDeallocator && aClosure) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aData);
+
+ RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
+ newSurf->InitWrappingData(aData, aSize, aStride, aFormat, aDeallocator, aClosure);
+
+ return newSurf.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+Factory::CreateDataSourceSurface(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aZero)
+{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DSS) " << aSize;
+ return nullptr;
+ }
+
+ // Skia doesn't support RGBX, so memset RGBX to 0xFF
+ bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+ uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->Init(aSize, aFormat, clearSurface, clearValue)) {
+ return newSurf.forget();
+ }
+
+ gfxWarning() << "CreateDataSourceSurface failed in init";
+ return nullptr;
+}
+
+already_AddRefed<DataSourceSurface>
+Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ bool aZero)
+{
+ if (!AllowedSurfaceSize(aSize) ||
+ aStride < aSize.width * BytesPerPixel(aFormat)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat;
+ return nullptr;
+ }
+
+ // Skia doesn't support RGBX, so memset RGBX to 0xFF
+ bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+ uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->Init(aSize, aFormat, clearSurface, clearValue, aStride)) {
+ return newSurf.forget();
+ }
+
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero;
+ return nullptr;
+}
+
+static uint16_t
+PackRGB565(uint8_t r, uint8_t g, uint8_t b)
+{
+ uint16_t pixel = ((r << 11) & 0xf800) |
+ ((g << 5) & 0x07e0) |
+ ((b ) & 0x001f);
+
+ return pixel;
+}
+
+void
+Factory::CopyDataSourceSurface(DataSourceSurface* aSource,
+ DataSourceSurface* aDest)
+{
+ // Don't worry too much about speed.
+ MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
+ MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
+ MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+ aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
+
+ const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
+ const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
+ const bool needsSwap02 = isSrcBGR != isDestBGR;
+
+ const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
+ const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
+ const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
+
+ const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
+
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
+ !aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+ MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
+ return;
+ }
+
+ MOZ_ASSERT(srcMap.mStride >= 0);
+ MOZ_ASSERT(destMap.mStride >= 0);
+
+ const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
+ const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
+ const size_t srcRowHole = srcMap.mStride - srcRowBytes;
+
+ const size_t destBPP = BytesPerPixel(aDest->GetFormat());
+ const size_t destRowBytes = aDest->GetSize().width * destBPP;
+ const size_t destRowHole = destMap.mStride - destRowBytes;
+
+ uint8_t* srcRow = srcMap.mData;
+ uint8_t* destRow = destMap.mData;
+ const size_t rows = aSource->GetSize().height;
+ for (size_t i = 0; i < rows; i++) {
+ const uint8_t* srcRowEnd = srcRow + srcRowBytes;
+
+ while (srcRow != srcRowEnd) {
+ uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
+ uint8_t d1 = srcRow[1];
+ uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
+ uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
+
+ if (needsConvertTo16Bits) {
+ *(uint16_t*)destRow = PackRGB565(d0, d1, d2);
+ } else {
+ destRow[0] = d0;
+ destRow[1] = d1;
+ destRow[2] = d2;
+ destRow[3] = d3;
+ }
+ srcRow += srcBPP;
+ destRow += destBPP;
+ }
+
+ srcRow += srcRowHole;
+ destRow += destRowHole;
+ }
+
+ aSource->Unmap();
+ aDest->Unmap();
+}
+
+already_AddRefed<DrawEventRecorder>
+Factory::CreateEventRecorderForFile(const char *aFilename)
+{
+ return MakeAndAddRef<DrawEventRecorderFile>(aFilename);
+}
+
+void
+Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder)
+{
+ mRecorder = aRecorder;
+}
+
+// static
+void
+CriticalLogger::OutputMessage(const std::string &aString,
+ int aLevel, bool aNoNewline)
+{
+ if (Factory::GetLogForwarder()) {
+ Factory::GetLogForwarder()->Log(aString);
+ }
+
+ BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
+}
+
+void
+CriticalLogger::CrashAction(LogReason aReason)
+{
+ if (Factory::GetLogForwarder()) {
+ Factory::GetLogForwarder()->CrashAction(aReason);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/FilterNodeD2D1.cpp b/system/graphics/2d/FilterNodeD2D1.cpp
new file mode 100644
index 000000000..9f4ded23c
--- /dev/null
+++ b/system/graphics/2d/FilterNodeD2D1.cpp
@@ -0,0 +1,1102 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilterNodeD2D1.h"
+
+#include "Logging.h"
+
+#include "SourceSurfaceD2D1.h"
+#include "DrawTargetD2D1.h"
+#include "ExtendInputEffectD2D1.h"
+
+namespace mozilla {
+namespace gfx {
+
+D2D1_COLORMATRIX_ALPHA_MODE D2DAlphaMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case ALPHA_MODE_PREMULTIPLIED:
+ return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
+ case ALPHA_MODE_STRAIGHT:
+ return D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT;
+ default:
+ MOZ_CRASH("GFX: Unknown enum value D2DAlphaMode!");
+ }
+
+ return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
+}
+
+D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case SamplingFilter::LINEAR:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case SamplingFilter::POINT:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ MOZ_CRASH("GFX: Unknown enum value D2DAffineTIM!");
+ }
+
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+}
+
+D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case BLEND_MODE_DARKEN:
+ return D2D1_BLEND_MODE_DARKEN;
+ case BLEND_MODE_LIGHTEN:
+ return D2D1_BLEND_MODE_LIGHTEN;
+ case BLEND_MODE_MULTIPLY:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ case BLEND_MODE_SCREEN:
+ return D2D1_BLEND_MODE_SCREEN;
+ case BLEND_MODE_OVERLAY:
+ return D2D1_BLEND_MODE_OVERLAY;
+ case BLEND_MODE_COLOR_DODGE:
+ return D2D1_BLEND_MODE_COLOR_DODGE;
+ case BLEND_MODE_COLOR_BURN:
+ return D2D1_BLEND_MODE_COLOR_BURN;
+ case BLEND_MODE_HARD_LIGHT:
+ return D2D1_BLEND_MODE_HARD_LIGHT;
+ case BLEND_MODE_SOFT_LIGHT:
+ return D2D1_BLEND_MODE_SOFT_LIGHT;
+ case BLEND_MODE_DIFFERENCE:
+ return D2D1_BLEND_MODE_DIFFERENCE;
+ case BLEND_MODE_EXCLUSION:
+ return D2D1_BLEND_MODE_EXCLUSION;
+ case BLEND_MODE_HUE:
+ return D2D1_BLEND_MODE_HUE;
+ case BLEND_MODE_SATURATION:
+ return D2D1_BLEND_MODE_SATURATION;
+ case BLEND_MODE_COLOR:
+ return D2D1_BLEND_MODE_COLOR;
+ case BLEND_MODE_LUMINOSITY:
+ return D2D1_BLEND_MODE_LUMINOSITY;
+
+ default:
+ MOZ_CRASH("GFX: Unknown enum value D2DBlendMode!");
+ }
+
+ return D2D1_BLEND_MODE_DARKEN;
+}
+
+D2D1_MORPHOLOGY_MODE D2DMorphologyMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case MORPHOLOGY_OPERATOR_DILATE:
+ return D2D1_MORPHOLOGY_MODE_DILATE;
+ case MORPHOLOGY_OPERATOR_ERODE:
+ return D2D1_MORPHOLOGY_MODE_ERODE;
+ }
+
+ MOZ_CRASH("GFX: Unknown enum value D2DMorphologyMode!");
+ return D2D1_MORPHOLOGY_MODE_DILATE;
+}
+
+D2D1_TURBULENCE_NOISE D2DTurbulenceNoise(uint32_t aMode)
+{
+ switch (aMode) {
+ case TURBULENCE_TYPE_FRACTAL_NOISE:
+ return D2D1_TURBULENCE_NOISE_FRACTAL_SUM;
+ case TURBULENCE_TYPE_TURBULENCE:
+ return D2D1_TURBULENCE_NOISE_TURBULENCE;
+ }
+
+ MOZ_CRASH("GFX: Unknown enum value D2DTurbulenceNoise!");
+ return D2D1_TURBULENCE_NOISE_TURBULENCE;
+}
+
+D2D1_COMPOSITE_MODE D2DFilterCompositionMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case COMPOSITE_OPERATOR_OVER:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ case COMPOSITE_OPERATOR_IN:
+ return D2D1_COMPOSITE_MODE_SOURCE_IN;
+ case COMPOSITE_OPERATOR_OUT:
+ return D2D1_COMPOSITE_MODE_SOURCE_OUT;
+ case COMPOSITE_OPERATOR_ATOP:
+ return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+ case COMPOSITE_OPERATOR_XOR:
+ return D2D1_COMPOSITE_MODE_XOR;
+ }
+
+ MOZ_CRASH("GFX: Unknown enum value D2DFilterCompositionMode!");
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+}
+
+D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode)
+{
+ switch (aMode) {
+ case COLOR_CHANNEL_R:
+ return D2D1_CHANNEL_SELECTOR_R;
+ case COLOR_CHANNEL_G:
+ return D2D1_CHANNEL_SELECTOR_G;
+ case COLOR_CHANNEL_B:
+ return D2D1_CHANNEL_SELECTOR_B;
+ case COLOR_CHANNEL_A:
+ return D2D1_CHANNEL_SELECTOR_A;
+ }
+
+ MOZ_CRASH("GFX: Unknown enum value D2DChannelSelector!");
+ return D2D1_CHANNEL_SELECTOR_R;
+}
+
+already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
+{
+ if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
+ gfxDevCrash(LogReason::FilterNodeD2D1Target) << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget() << " " << (int)aDT->IsDualDrawTarget();
+ return nullptr;
+ }
+ switch (aDT->GetBackendType()) {
+ case BackendType::DIRECT2D1_1:
+ return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
+ default:
+ gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType();
+ return nullptr;
+ }
+}
+
+uint32_t ConvertValue(FilterType aType, uint32_t aAttribute, uint32_t aValue)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ if (aAttribute == ATT_COLOR_MATRIX_ALPHA_MODE) {
+ aValue = D2DAlphaMode(aValue);
+ }
+ break;
+ case FilterType::TRANSFORM:
+ if (aAttribute == ATT_TRANSFORM_FILTER) {
+ aValue = D2DAffineTransformInterpolationMode(SamplingFilter(aValue));
+ }
+ break;
+ case FilterType::BLEND:
+ if (aAttribute == ATT_BLEND_BLENDMODE) {
+ aValue = D2DBlendMode(aValue);
+ }
+ break;
+ case FilterType::MORPHOLOGY:
+ if (aAttribute == ATT_MORPHOLOGY_OPERATOR) {
+ aValue = D2DMorphologyMode(aValue);
+ }
+ break;
+ case FilterType::DISPLACEMENT_MAP:
+ if (aAttribute == ATT_DISPLACEMENT_MAP_X_CHANNEL ||
+ aAttribute == ATT_DISPLACEMENT_MAP_Y_CHANNEL) {
+ aValue = D2DChannelSelector(aValue);
+ }
+ break;
+ case FilterType::TURBULENCE:
+ if (aAttribute == ATT_TURBULENCE_TYPE) {
+ aValue = D2DTurbulenceNoise(aValue);
+ }
+ break;
+ case FilterType::COMPOSITE:
+ if (aAttribute == ATT_COMPOSITE_OPERATOR) {
+ aValue = D2DFilterCompositionMode(aValue);
+ }
+ break;
+ }
+
+ return aValue;
+}
+
+void ConvertValue(FilterType aType, uint32_t aAttribute, IntSize &aValue)
+{
+ switch (aType) {
+ case FilterType::MORPHOLOGY:
+ if (aAttribute == ATT_MORPHOLOGY_RADII) {
+ aValue.width *= 2;
+ aValue.width += 1;
+ aValue.height *= 2;
+ aValue.height += 1;
+ }
+ break;
+ }
+}
+
+UINT32
+GetD2D1InputForInput(FilterType aType, uint32_t aIndex)
+{
+ return aIndex;
+}
+
+#define CONVERT_PROP(moz2dname, d2dname) \
+ case ATT_##moz2dname: \
+ return D2D1_##d2dname
+
+UINT32
+GetD2D1PropForAttribute(FilterType aType, uint32_t aIndex)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ switch (aIndex) {
+ CONVERT_PROP(COLOR_MATRIX_MATRIX, COLORMATRIX_PROP_COLOR_MATRIX);
+ CONVERT_PROP(COLOR_MATRIX_ALPHA_MODE, COLORMATRIX_PROP_ALPHA_MODE);
+ }
+ break;
+ case FilterType::TRANSFORM:
+ switch (aIndex) {
+ CONVERT_PROP(TRANSFORM_MATRIX, 2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX);
+ CONVERT_PROP(TRANSFORM_FILTER, 2DAFFINETRANSFORM_PROP_INTERPOLATION_MODE);
+ }
+ case FilterType::BLEND:
+ switch (aIndex) {
+ CONVERT_PROP(BLEND_BLENDMODE, BLEND_PROP_MODE);
+ }
+ break;
+ case FilterType::MORPHOLOGY:
+ switch (aIndex) {
+ CONVERT_PROP(MORPHOLOGY_OPERATOR, MORPHOLOGY_PROP_MODE);
+ }
+ break;
+ case FilterType::FLOOD:
+ switch (aIndex) {
+ CONVERT_PROP(FLOOD_COLOR, FLOOD_PROP_COLOR);
+ }
+ break;
+ case FilterType::TILE:
+ switch (aIndex) {
+ CONVERT_PROP(TILE_SOURCE_RECT, TILE_PROP_RECT);
+ }
+ break;
+ case FilterType::TABLE_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_R, TABLETRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_G, TABLETRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_B, TABLETRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_A, TABLETRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_R, TABLETRANSFER_PROP_RED_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_G, TABLETRANSFER_PROP_GREEN_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_B, TABLETRANSFER_PROP_BLUE_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_A, TABLETRANSFER_PROP_ALPHA_TABLE);
+ }
+ break;
+ case FilterType::DISCRETE_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_R, DISCRETETRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_G, DISCRETETRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_B, DISCRETETRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_A, DISCRETETRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_R, DISCRETETRANSFER_PROP_RED_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_G, DISCRETETRANSFER_PROP_GREEN_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_B, DISCRETETRANSFER_PROP_BLUE_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_A, DISCRETETRANSFER_PROP_ALPHA_TABLE);
+ }
+ break;
+ case FilterType::LINEAR_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_R, LINEARTRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_G, LINEARTRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_B, LINEARTRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_A, LINEARTRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_R, LINEARTRANSFER_PROP_RED_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_G, LINEARTRANSFER_PROP_GREEN_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_B, LINEARTRANSFER_PROP_BLUE_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_A, LINEARTRANSFER_PROP_ALPHA_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_R, LINEARTRANSFER_PROP_RED_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_G, LINEARTRANSFER_PROP_GREEN_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_B, LINEARTRANSFER_PROP_BLUE_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_A, LINEARTRANSFER_PROP_ALPHA_SLOPE);
+ }
+ break;
+ case FilterType::GAMMA_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_R, GAMMATRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_G, GAMMATRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_B, GAMMATRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_A, GAMMATRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_R, GAMMATRANSFER_PROP_RED_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_G, GAMMATRANSFER_PROP_GREEN_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_B, GAMMATRANSFER_PROP_BLUE_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_A, GAMMATRANSFER_PROP_ALPHA_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_R, GAMMATRANSFER_PROP_RED_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_G, GAMMATRANSFER_PROP_GREEN_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_B, GAMMATRANSFER_PROP_BLUE_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_A, GAMMATRANSFER_PROP_ALPHA_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_R, GAMMATRANSFER_PROP_RED_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_G, GAMMATRANSFER_PROP_GREEN_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_B, GAMMATRANSFER_PROP_BLUE_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_A, GAMMATRANSFER_PROP_ALPHA_OFFSET);
+ }
+ break;
+ case FilterType::CONVOLVE_MATRIX:
+ switch (aIndex) {
+ CONVERT_PROP(CONVOLVE_MATRIX_BIAS, CONVOLVEMATRIX_PROP_BIAS);
+ CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_MATRIX, CONVOLVEMATRIX_PROP_KERNEL_MATRIX);
+ CONVERT_PROP(CONVOLVE_MATRIX_DIVISOR, CONVOLVEMATRIX_PROP_DIVISOR);
+ CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, CONVOLVEMATRIX_PROP_KERNEL_UNIT_LENGTH);
+ CONVERT_PROP(CONVOLVE_MATRIX_PRESERVE_ALPHA, CONVOLVEMATRIX_PROP_PRESERVE_ALPHA);
+ }
+ case FilterType::DISPLACEMENT_MAP:
+ switch (aIndex) {
+ CONVERT_PROP(DISPLACEMENT_MAP_SCALE, DISPLACEMENTMAP_PROP_SCALE);
+ CONVERT_PROP(DISPLACEMENT_MAP_X_CHANNEL, DISPLACEMENTMAP_PROP_X_CHANNEL_SELECT);
+ CONVERT_PROP(DISPLACEMENT_MAP_Y_CHANNEL, DISPLACEMENTMAP_PROP_Y_CHANNEL_SELECT);
+ }
+ break;
+ case FilterType::TURBULENCE:
+ switch (aIndex) {
+ CONVERT_PROP(TURBULENCE_BASE_FREQUENCY, TURBULENCE_PROP_BASE_FREQUENCY);
+ CONVERT_PROP(TURBULENCE_NUM_OCTAVES, TURBULENCE_PROP_NUM_OCTAVES);
+ CONVERT_PROP(TURBULENCE_SEED, TURBULENCE_PROP_SEED);
+ CONVERT_PROP(TURBULENCE_STITCHABLE, TURBULENCE_PROP_STITCHABLE);
+ CONVERT_PROP(TURBULENCE_TYPE, TURBULENCE_PROP_NOISE);
+ }
+ break;
+ case FilterType::ARITHMETIC_COMBINE:
+ switch (aIndex) {
+ CONVERT_PROP(ARITHMETIC_COMBINE_COEFFICIENTS, ARITHMETICCOMPOSITE_PROP_COEFFICIENTS);
+ }
+ break;
+ case FilterType::COMPOSITE:
+ switch (aIndex) {
+ CONVERT_PROP(COMPOSITE_OPERATOR, COMPOSITE_PROP_MODE);
+ }
+ break;
+ case FilterType::GAUSSIAN_BLUR:
+ switch (aIndex) {
+ CONVERT_PROP(GAUSSIAN_BLUR_STD_DEVIATION, GAUSSIANBLUR_PROP_STANDARD_DEVIATION);
+ }
+ break;
+ case FilterType::DIRECTIONAL_BLUR:
+ switch (aIndex) {
+ CONVERT_PROP(DIRECTIONAL_BLUR_STD_DEVIATION, DIRECTIONALBLUR_PROP_STANDARD_DEVIATION);
+ CONVERT_PROP(DIRECTIONAL_BLUR_DIRECTION, DIRECTIONALBLUR_PROP_ANGLE);
+ }
+ break;
+ case FilterType::POINT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(POINT_DIFFUSE_DIFFUSE_CONSTANT, POINTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(POINT_DIFFUSE_POSITION, POINTDIFFUSE_PROP_LIGHT_POSITION);
+ CONVERT_PROP(POINT_DIFFUSE_COLOR, POINTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(POINT_DIFFUSE_SURFACE_SCALE, POINTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(POINT_DIFFUSE_KERNEL_UNIT_LENGTH, POINTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::SPOT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(SPOT_DIFFUSE_DIFFUSE_CONSTANT, SPOTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(SPOT_DIFFUSE_POINTS_AT, SPOTDIFFUSE_PROP_POINTS_AT);
+ CONVERT_PROP(SPOT_DIFFUSE_FOCUS, SPOTDIFFUSE_PROP_FOCUS);
+ CONVERT_PROP(SPOT_DIFFUSE_LIMITING_CONE_ANGLE, SPOTDIFFUSE_PROP_LIMITING_CONE_ANGLE);
+ CONVERT_PROP(SPOT_DIFFUSE_POSITION, SPOTDIFFUSE_PROP_LIGHT_POSITION);
+ CONVERT_PROP(SPOT_DIFFUSE_COLOR, SPOTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(SPOT_DIFFUSE_SURFACE_SCALE, SPOTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(SPOT_DIFFUSE_KERNEL_UNIT_LENGTH, SPOTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::DISTANT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(DISTANT_DIFFUSE_DIFFUSE_CONSTANT, DISTANTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(DISTANT_DIFFUSE_AZIMUTH, DISTANTDIFFUSE_PROP_AZIMUTH);
+ CONVERT_PROP(DISTANT_DIFFUSE_ELEVATION, DISTANTDIFFUSE_PROP_ELEVATION);
+ CONVERT_PROP(DISTANT_DIFFUSE_COLOR, DISTANTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(DISTANT_DIFFUSE_SURFACE_SCALE, DISTANTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH, DISTANTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::POINT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(POINT_SPECULAR_SPECULAR_CONSTANT, POINTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(POINT_SPECULAR_SPECULAR_EXPONENT, POINTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(POINT_SPECULAR_POSITION, POINTSPECULAR_PROP_LIGHT_POSITION);
+ CONVERT_PROP(POINT_SPECULAR_COLOR, POINTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(POINT_SPECULAR_SURFACE_SCALE, POINTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(POINT_SPECULAR_KERNEL_UNIT_LENGTH, POINTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::SPOT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(SPOT_SPECULAR_SPECULAR_CONSTANT, SPOTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(SPOT_SPECULAR_SPECULAR_EXPONENT, SPOTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(SPOT_SPECULAR_POINTS_AT, SPOTSPECULAR_PROP_POINTS_AT);
+ CONVERT_PROP(SPOT_SPECULAR_FOCUS, SPOTSPECULAR_PROP_FOCUS);
+ CONVERT_PROP(SPOT_SPECULAR_LIMITING_CONE_ANGLE, SPOTSPECULAR_PROP_LIMITING_CONE_ANGLE);
+ CONVERT_PROP(SPOT_SPECULAR_POSITION, SPOTSPECULAR_PROP_LIGHT_POSITION);
+ CONVERT_PROP(SPOT_SPECULAR_COLOR, SPOTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(SPOT_SPECULAR_SURFACE_SCALE, SPOTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(SPOT_SPECULAR_KERNEL_UNIT_LENGTH, SPOTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::DISTANT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_CONSTANT, DISTANTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_EXPONENT, DISTANTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(DISTANT_SPECULAR_AZIMUTH, DISTANTSPECULAR_PROP_AZIMUTH);
+ CONVERT_PROP(DISTANT_SPECULAR_ELEVATION, DISTANTSPECULAR_PROP_ELEVATION);
+ CONVERT_PROP(DISTANT_SPECULAR_COLOR, DISTANTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(DISTANT_SPECULAR_SURFACE_SCALE, DISTANTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(DISTANT_SPECULAR_KERNEL_UNIT_LENGTH, DISTANTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::CROP:
+ switch (aIndex) {
+ CONVERT_PROP(CROP_RECT, CROP_PROP_RECT);
+ }
+ break;
+ }
+
+ return UINT32_MAX;
+}
+
+bool
+GetD2D1PropsForIntSize(FilterType aType, uint32_t aIndex, UINT32 *aPropWidth, UINT32 *aPropHeight)
+{
+ switch (aType) {
+ case FilterType::MORPHOLOGY:
+ if (aIndex == ATT_MORPHOLOGY_RADII) {
+ *aPropWidth = D2D1_MORPHOLOGY_PROP_WIDTH;
+ *aPropHeight = D2D1_MORPHOLOGY_PROP_HEIGHT;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static inline REFCLSID GetCLDIDForFilterType(FilterType aType)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ return CLSID_D2D1ColorMatrix;
+ case FilterType::TRANSFORM:
+ return CLSID_D2D12DAffineTransform;
+ case FilterType::BLEND:
+ return CLSID_D2D1Blend;
+ case FilterType::MORPHOLOGY:
+ return CLSID_D2D1Morphology;
+ case FilterType::FLOOD:
+ return CLSID_D2D1Flood;
+ case FilterType::TILE:
+ return CLSID_D2D1Tile;
+ case FilterType::TABLE_TRANSFER:
+ return CLSID_D2D1TableTransfer;
+ case FilterType::LINEAR_TRANSFER:
+ return CLSID_D2D1LinearTransfer;
+ case FilterType::DISCRETE_TRANSFER:
+ return CLSID_D2D1DiscreteTransfer;
+ case FilterType::GAMMA_TRANSFER:
+ return CLSID_D2D1GammaTransfer;
+ case FilterType::DISPLACEMENT_MAP:
+ return CLSID_D2D1DisplacementMap;
+ case FilterType::TURBULENCE:
+ return CLSID_D2D1Turbulence;
+ case FilterType::ARITHMETIC_COMBINE:
+ return CLSID_D2D1ArithmeticComposite;
+ case FilterType::COMPOSITE:
+ return CLSID_D2D1Composite;
+ case FilterType::GAUSSIAN_BLUR:
+ return CLSID_D2D1GaussianBlur;
+ case FilterType::DIRECTIONAL_BLUR:
+ return CLSID_D2D1DirectionalBlur;
+ case FilterType::POINT_DIFFUSE:
+ return CLSID_D2D1PointDiffuse;
+ case FilterType::POINT_SPECULAR:
+ return CLSID_D2D1PointSpecular;
+ case FilterType::SPOT_DIFFUSE:
+ return CLSID_D2D1SpotDiffuse;
+ case FilterType::SPOT_SPECULAR:
+ return CLSID_D2D1SpotSpecular;
+ case FilterType::DISTANT_DIFFUSE:
+ return CLSID_D2D1DistantDiffuse;
+ case FilterType::DISTANT_SPECULAR:
+ return CLSID_D2D1DistantSpecular;
+ case FilterType::CROP:
+ return CLSID_D2D1Crop;
+ case FilterType::PREMULTIPLY:
+ return CLSID_D2D1Premultiply;
+ case FilterType::UNPREMULTIPLY:
+ return CLSID_D2D1UnPremultiply;
+ }
+ return GUID_NULL;
+}
+
+static bool
+IsTransferFilterType(FilterType aType)
+{
+ switch (aType) {
+ case FilterType::LINEAR_TRANSFER:
+ case FilterType::GAMMA_TRANSFER:
+ case FilterType::TABLE_TRANSFER:
+ case FilterType::DISCRETE_TRANSFER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+HasUnboundedOutputRegion(FilterType aType)
+{
+ if (IsTransferFilterType(aType)) {
+ return true;
+ }
+
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ case FilterType::POINT_DIFFUSE:
+ case FilterType::SPOT_DIFFUSE:
+ case FilterType::DISTANT_DIFFUSE:
+ case FilterType::POINT_SPECULAR:
+ case FilterType::SPOT_SPECULAR:
+ case FilterType::DISTANT_SPECULAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* static */
+already_AddRefed<FilterNode>
+FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
+{
+ if (aType == FilterType::CONVOLVE_MATRIX) {
+ return MakeAndAddRef<FilterNodeConvolveD2D1>(aDC);
+ }
+
+ RefPtr<ID2D1Effect> effect;
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(GetCLDIDForFilterType(aType), getter_AddRefs(effect));
+
+ if (FAILED(hr) || !effect) {
+ gfxCriticalErrorOnce() << "Failed to create effect for FilterType: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aType == FilterType::ARITHMETIC_COMBINE) {
+ effect->SetValue(D2D1_ARITHMETICCOMPOSITE_PROP_CLAMP_OUTPUT, TRUE);
+ }
+
+ RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType);
+
+ if (HasUnboundedOutputRegion(aType)) {
+ // These filters can produce non-transparent output from transparent
+ // input pixels, and we want them to have an unbounded output region.
+ filter = new FilterNodeExtendInputAdapterD2D1(aDC, filter, aType);
+ }
+
+ if (IsTransferFilterType(aType)) {
+ // Component transfer filters should appear to apply on unpremultiplied
+ // colors, but the D2D1 effects apply on premultiplied colors.
+ filter = new FilterNodePremultiplyAdapterD2D1(aDC, filter, aType);
+ }
+
+ return filter.forget();
+}
+
+void
+FilterNodeD2D1::InitUnmappedProperties()
+{
+ switch (mType) {
+ case FilterType::TRANSFORM:
+ mEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+FilterNodeD2D1::SetInput(uint32_t aIndex, SourceSurface *aSurface)
+{
+ UINT32 input = GetD2D1InputForInput(mType, aIndex);
+ ID2D1Effect* effect = InputEffect();
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ if (mType == FilterType::COMPOSITE) {
+ UINT32 inputCount = effect->GetInputCount();
+
+ if (aIndex == inputCount - 1 && aSurface == nullptr) {
+ effect->SetInputCount(inputCount - 1);
+ } else if (aIndex >= inputCount && aSurface) {
+ effect->SetInputCount(aIndex + 1);
+ }
+ }
+
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ mInputSurfaces.resize(effect->GetInputCount());
+ mInputFilters.resize(effect->GetInputCount());
+
+ // In order to convert aSurface into an ID2D1Image, we need to know what
+ // DrawTarget we paint into. However, the same FilterNode object can be
+ // used on different DrawTargets, so we need to hold on to the SourceSurface
+ // objects and delay the conversion until we're actually painted and know
+ // our target DrawTarget.
+ // The conversion happens in WillDraw().
+
+ mInputSurfaces[input] = aSurface;
+ mInputFilters[input] = nullptr;
+
+ // Clear the existing image from the effect.
+ effect->SetInput(input, nullptr);
+}
+
+void
+FilterNodeD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ UINT32 input = GetD2D1InputForInput(mType, aIndex);
+ ID2D1Effect* effect = InputEffect();
+
+ if (mType == FilterType::COMPOSITE) {
+ UINT32 inputCount = effect->GetInputCount();
+
+ if (aIndex == inputCount - 1 && aFilter == nullptr) {
+ effect->SetInputCount(inputCount - 1);
+ } else if (aIndex >= inputCount && aFilter) {
+ effect->SetInputCount(aIndex + 1);
+ }
+ }
+
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
+ gfxWarning() << "Unknown input FilterNode set on effect.";
+ MOZ_ASSERT(0);
+ return;
+ }
+
+ FilterNodeD2D1* filter = static_cast<FilterNodeD2D1*>(aFilter);
+
+ mInputSurfaces.resize(effect->GetInputCount());
+ mInputFilters.resize(effect->GetInputCount());
+
+ // We hold on to the FilterNode object so that we can call WillDraw() on it.
+ mInputSurfaces[input] = nullptr;
+ mInputFilters[input] = filter;
+
+ if (filter) {
+ effect->SetInputEffect(input, filter->OutputEffect());
+ }
+}
+
+void
+FilterNodeD2D1::WillDraw(DrawTarget *aDT)
+{
+ // Convert input SourceSurfaces into ID2D1Images and set them on the effect.
+ for (size_t inputIndex = 0; inputIndex < mInputSurfaces.size(); inputIndex++) {
+ if (mInputSurfaces[inputIndex]) {
+ ID2D1Effect* effect = InputEffect();
+ RefPtr<ID2D1Image> image = GetImageForSourceSurface(aDT, mInputSurfaces[inputIndex]);
+ effect->SetInput(inputIndex, image);
+ }
+ }
+
+ // Call WillDraw() on our input filters.
+ for (std::vector<RefPtr<FilterNodeD2D1>>::iterator it = mInputFilters.begin();
+ it != mInputFilters.end(); it++) {
+ if (*it) {
+ (*it)->WillDraw(aDT);
+ }
+ }
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ if (mType == FilterType::TURBULENCE && aIndex == ATT_TURBULENCE_BASE_FREQUENCY) {
+ mEffect->SetValue(input, D2D1::Vector2F(FLOAT(aValue), FLOAT(aValue)));
+ return;
+ } else if (mType == FilterType::DIRECTIONAL_BLUR && aIndex == ATT_DIRECTIONAL_BLUR_DIRECTION) {
+ mEffect->SetValue(input, aValue == BLUR_DIRECTION_X ? 0 : 90.0f);
+ return;
+ }
+
+ mEffect->SetValue(input, ConvertValue(mType, aIndex, aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, aValue);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DPoint(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DMatrix5x4(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point3D &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DVector3D(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Size &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2D1::Vector2F(aValue.width, aValue.height));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue)
+{
+ UINT32 widthProp, heightProp;
+
+ if (!GetD2D1PropsForIntSize(mType, aIndex, &widthProp, &heightProp)) {
+ return;
+ }
+
+ IntSize value = aValue;
+ ConvertValue(mType, aIndex, value);
+
+ mEffect->SetValue(widthProp, (UINT)value.width);
+ mEffect->SetValue(heightProp, (UINT)value.height);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Color &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ switch (mType) {
+ case FilterType::POINT_DIFFUSE:
+ case FilterType::SPOT_DIFFUSE:
+ case FilterType::DISTANT_DIFFUSE:
+ case FilterType::POINT_SPECULAR:
+ case FilterType::SPOT_SPECULAR:
+ case FilterType::DISTANT_SPECULAR:
+ mEffect->SetValue(input, D2D1::Vector3F(aValue.r, aValue.g, aValue.b));
+ break;
+ default:
+ mEffect->SetValue(input, D2D1::Vector4F(aValue.r * aValue.a, aValue.g * aValue.a, aValue.b * aValue.a, aValue.a));
+ }
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Rect &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DRect(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue)
+{
+ if (mType == FilterType::TURBULENCE) {
+ MOZ_ASSERT(aIndex == ATT_TURBULENCE_RECT);
+
+ mEffect->SetValue(D2D1_TURBULENCE_PROP_OFFSET, D2D1::Vector2F(Float(aValue.x), Float(aValue.y)));
+ mEffect->SetValue(D2D1_TURBULENCE_PROP_SIZE, D2D1::Vector2F(Float(aValue.width), Float(aValue.height)));
+ return;
+ }
+
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2D1::RectF(Float(aValue.x), Float(aValue.y),
+ Float(aValue.XMost()), Float(aValue.YMost())));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, bool aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, (BOOL)aValue);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, (BYTE*)aValues, sizeof(Float) * aSize);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DPoint(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DMatrix(aMatrix));
+}
+
+FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
+ : FilterNodeD2D1(nullptr, FilterType::CONVOLVE_MATRIX)
+ , mEdgeMode(EDGE_MODE_DUPLICATE)
+{
+ // Correctly handling the interaction of edge mode and source rect is a bit
+ // tricky with D2D1 effects. We want the edge mode to only apply outside of
+ // the source rect (as specified by the ATT_CONVOLVE_MATRIX_SOURCE_RECT
+ // attribute). So if our input surface or filter is smaller than the source
+ // rect, we need to add transparency around it until we reach the edges of
+ // the source rect, and only then do any repeating or edge duplicating.
+ // Unfortunately, the border effect does not have a source rect attribute -
+ // it only looks at the output rect of its input filter or surface. So we use
+ // our custom ExtendInput effect to adjust the output rect of our input.
+ // All of this is only necessary when our edge mode is not EDGE_MODE_NONE, so
+ // we update the filter chain dynamically in UpdateChain().
+
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_D2D1ConvolveMatrix, getter_AddRefs(mEffect));
+
+ if (FAILED(hr) || !mEffect) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE, D2D1_BORDER_MODE_SOFT);
+
+ hr = aDC->CreateEffect(CLSID_ExtendInputEffect, getter_AddRefs(mExtendInputEffect));
+
+ if (FAILED(hr) || !mExtendInputEffect) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1Border, getter_AddRefs(mBorderEffect));
+
+ if (FAILED(hr) || !mBorderEffect) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ mBorderEffect->SetInputEffect(0, mExtendInputEffect.get());
+
+ UpdateChain();
+ UpdateSourceRect();
+}
+
+void
+FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ FilterNodeD2D1::SetInput(aIndex, aFilter);
+
+ UpdateChain();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_EDGE_MODE) {
+ return FilterNodeD2D1::SetAttribute(aIndex, aValue);
+ }
+
+ mEdgeMode = (ConvolveMatrixEdgeMode)aValue;
+
+ UpdateChain();
+}
+
+ID2D1Effect*
+FilterNodeConvolveD2D1::InputEffect()
+{
+ return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mExtendInputEffect.get();
+}
+
+void
+FilterNodeConvolveD2D1::UpdateChain()
+{
+ // The shape of the filter graph:
+ //
+ // EDGE_MODE_NONE:
+ // input --> convolvematrix
+ //
+ // EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP:
+ // input --> extendinput --> border --> convolvematrix
+ //
+ // mEffect is convolvematrix.
+
+ if (mEdgeMode != EDGE_MODE_NONE) {
+ mEffect->SetInputEffect(0, mBorderEffect.get());
+ }
+
+ RefPtr<ID2D1Effect> inputEffect;
+ if (mInputFilters.size() > 0 && mInputFilters[0]) {
+ inputEffect = mInputFilters[0]->OutputEffect();
+ }
+ InputEffect()->SetInputEffect(0, inputEffect);
+
+ if (mEdgeMode == EDGE_MODE_DUPLICATE) {
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_CLAMP);
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_CLAMP);
+ } else if (mEdgeMode == EDGE_MODE_WRAP) {
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP);
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP);
+ }
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_KERNEL_SIZE) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mKernelSize = aValue;
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_X, aValue.width);
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_Y, aValue.height);
+
+ UpdateOffset();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_TARGET) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mTarget = aValue;
+
+ UpdateOffset();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_SOURCE_RECT) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mSourceRect = aValue;
+
+ UpdateSourceRect();
+}
+
+void
+FilterNodeConvolveD2D1::UpdateOffset()
+{
+ D2D1_VECTOR_2F vector =
+ D2D1::Vector2F((Float(mKernelSize.width) - 1.0f) / 2.0f - Float(mTarget.x),
+ (Float(mKernelSize.height) - 1.0f) / 2.0f - Float(mTarget.y));
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_OFFSET, vector);
+}
+
+void
+FilterNodeConvolveD2D1::UpdateSourceRect()
+{
+ mExtendInputEffect->SetValue(EXTENDINPUT_PROP_OUTPUT_RECT,
+ D2D1::Vector4F(Float(mSourceRect.x), Float(mSourceRect.y),
+ Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
+}
+
+FilterNodeExtendInputAdapterD2D1::FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC,
+ FilterNodeD2D1 *aFilterNode, FilterType aType)
+ : FilterNodeD2D1(aFilterNode->MainEffect(), aType)
+ , mWrappedFilterNode(aFilterNode)
+{
+ // We have an mEffect that looks at the bounds of the input effect, and we
+ // want mEffect to regard its input as unbounded. So we take the input,
+ // pipe it through an ExtendInput effect (which has an infinite output rect
+ // by default), and feed the resulting unbounded composition into mEffect.
+
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_ExtendInputEffect, getter_AddRefs(mExtendInputEffect));
+
+ if (FAILED(hr) || !mExtendInputEffect) {
+ gfxWarning() << "Failed to create extend input effect for filter: " << hexa(hr);
+ return;
+ }
+
+ aFilterNode->InputEffect()->SetInputEffect(0, mExtendInputEffect.get());
+}
+
+FilterNodePremultiplyAdapterD2D1::FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC,
+ FilterNodeD2D1 *aFilterNode, FilterType aType)
+ : FilterNodeD2D1(aFilterNode->MainEffect(), aType)
+{
+ // D2D1 component transfer effects do strange things when it comes to
+ // premultiplication.
+ // For our purposes we only need the transfer filters to apply straight to
+ // unpremultiplied source channels and output unpremultiplied results.
+ // However, the D2D1 effects are designed differently: They can apply to both
+ // premultiplied and unpremultiplied inputs, and they always premultiply
+ // their result - at least in those color channels that have not been
+ // disabled.
+ // In order to determine whether the input needs to be unpremultiplied as
+ // part of the transfer, the effect consults the alpha mode metadata of the
+ // input surface or the input effect. We don't have such a concept in Moz2D,
+ // and giving Moz2D users different results based on something that cannot be
+ // influenced through Moz2D APIs seems like a bad idea.
+ // We solve this by applying a premultiply effect to the input before feeding
+ // it into the transfer effect. The premultiply effect always premultiplies
+ // regardless of any alpha mode metadata on inputs, and it always marks its
+ // output as premultiplied so that the transfer effect will unpremultiply
+ // consistently. Feeding always-premultiplied input into the transfer effect
+ // also avoids another problem that would appear when individual color
+ // channels disable the transfer: In that case, the disabled channels would
+ // pass through unchanged in their unpremultiplied form and the other
+ // channels would be premultiplied, giving a mixed result.
+ // But since we now ensure that the input is premultiplied, disabled channels
+ // will pass premultiplied values through to the result, which is consistent
+ // with the enabled channels.
+ // We also add an unpremultiply effect that postprocesses the result of the
+ // transfer effect because getting unpremultiplied results from the transfer
+ // filters is part of the FilterNode API.
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_D2D1Premultiply, getter_AddRefs(mPrePremultiplyEffect));
+
+ if (FAILED(hr) || !mPrePremultiplyEffect) {
+ gfxWarning() << "Failed to create ComponentTransfer filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1UnPremultiply, getter_AddRefs(mPostUnpremultiplyEffect));
+
+ if (FAILED(hr) || !mPostUnpremultiplyEffect) {
+ gfxWarning() << "Failed to create ComponentTransfer filter!";
+ return;
+ }
+
+ aFilterNode->InputEffect()->SetInputEffect(0, mPrePremultiplyEffect.get());
+ mPostUnpremultiplyEffect->SetInputEffect(0, aFilterNode->OutputEffect());
+}
+
+}
+}
diff --git a/system/graphics/2d/FilterNodeD2D1.h b/system/graphics/2d/FilterNodeD2D1.h
new file mode 100644
index 000000000..8c4c6df30
--- /dev/null
+++ b/system/graphics/2d/FilterNodeD2D1.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_FILTERNODED2D1_H_
+#define MOZILLA_GFX_FILTERNODED2D1_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include <vector>
+#include <d2d1_1.h>
+#include <cguid.h>
+
+namespace mozilla {
+namespace gfx {
+
+class FilterNodeD2D1 : public FilterNode
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeD2D1)
+ static already_AddRefed<FilterNode> Create(ID2D1DeviceContext *aDC, FilterType aType);
+
+ FilterNodeD2D1(ID2D1Effect *aEffect, FilterType aType)
+ : mEffect(aEffect)
+ , mType(aType)
+ {
+ InitUnmappedProperties();
+ }
+
+ virtual FilterBackend GetBackendType() { return FILTER_BACKEND_DIRECT2D1_1; }
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface);
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter);
+
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue);
+ virtual void SetAttribute(uint32_t aIndex, Float aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Point &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Size &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Color &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Rect &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
+ virtual void SetAttribute(uint32_t aIndex, bool aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize);
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue);
+
+ // Called by DrawTarget before it draws our OutputEffect, and recursively
+ // by the filter nodes that have this filter as one of their inputs. This
+ // gives us a chance to convert any input surfaces to the target format for
+ // the DrawTarget that we will draw to.
+ virtual void WillDraw(DrawTarget *aDT);
+
+ virtual ID2D1Effect* MainEffect() { return mEffect.get(); }
+ virtual ID2D1Effect* InputEffect() { return mEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() { return mEffect.get(); }
+
+protected:
+ friend class DrawTargetD2D1;
+ friend class DrawTargetD2D;
+ friend class FilterNodeConvolveD2D1;
+
+ void InitUnmappedProperties();
+
+ RefPtr<ID2D1Effect> mEffect;
+ std::vector<RefPtr<FilterNodeD2D1>> mInputFilters;
+ std::vector<RefPtr<SourceSurface>> mInputSurfaces;
+ FilterType mType;
+};
+
+class FilterNodeConvolveD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveD2D1, override)
+ FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC);
+
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
+
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue) override;
+
+ virtual ID2D1Effect* InputEffect() override;
+
+private:
+ void UpdateChain();
+ void UpdateOffset();
+ void UpdateSourceRect();
+
+ RefPtr<ID2D1Effect> mExtendInputEffect;
+ RefPtr<ID2D1Effect> mBorderEffect;
+ ConvolveMatrixEdgeMode mEdgeMode;
+ IntPoint mTarget;
+ IntSize mKernelSize;
+ IntRect mSourceRect;
+};
+
+class FilterNodeExtendInputAdapterD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeExtendInputAdapterD2D1, override)
+ FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
+
+ virtual ID2D1Effect* InputEffect() override { return mExtendInputEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() override { return mWrappedFilterNode->OutputEffect(); }
+
+private:
+ RefPtr<FilterNodeD2D1> mWrappedFilterNode;
+ RefPtr<ID2D1Effect> mExtendInputEffect;
+};
+
+class FilterNodePremultiplyAdapterD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplyAdapterD2D1, override)
+ FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
+
+ virtual ID2D1Effect* InputEffect() override { return mPrePremultiplyEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() override { return mPostUnpremultiplyEffect.get(); }
+
+private:
+ RefPtr<ID2D1Effect> mPrePremultiplyEffect;
+ RefPtr<ID2D1Effect> mPostUnpremultiplyEffect;
+};
+
+}
+}
+
+#endif
diff --git a/system/graphics/2d/FilterNodeSoftware.cpp b/system/graphics/2d/FilterNodeSoftware.cpp
new file mode 100644
index 000000000..169694069
--- /dev/null
+++ b/system/graphics/2d/FilterNodeSoftware.cpp
@@ -0,0 +1,3689 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <cmath>
+#include "DataSurfaceHelpers.h"
+#include "FilterNodeSoftware.h"
+#include "2D.h"
+#include "Tools.h"
+#include "Blur.h"
+#include <map>
+#include "FilterProcessing.h"
+#include "Logging.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/DebugOnly.h"
+
+// #define DEBUG_DUMP_SURFACES
+
+#ifdef DEBUG_DUMP_SURFACES
+#include "gfxUtils.h" // not part of Moz2D
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+namespace {
+
+/**
+ * This class provides a way to get a pow() results in constant-time. It works
+ * by caching 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
+ * 0 and 1 and a fixed exponent.
+ **/
+class PowCache
+{
+public:
+ PowCache()
+ : mNumPowTablePreSquares(-1)
+ {
+ }
+
+ void CacheForExponent(Float aExponent)
+ {
+ // Since we are in the world where we only care about
+ // input and results in [0,1], there is no point in
+ // dealing with non-positive exponents.
+ if (aExponent <= 0) {
+ mNumPowTablePreSquares = -1;
+ return;
+ }
+ int numPreSquares = 0;
+ while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
+ numPreSquares++;
+ }
+ mNumPowTablePreSquares = numPreSquares;
+ for (size_t i = 0; i < sCacheSize; i++) {
+ // sCacheSize is chosen in such a way that a takes values
+ // from 0.0 to 1.0 inclusive.
+ Float a = i / Float(1 << sCacheIndexPrecisionBits);
+ MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
+
+ for (int j = 0; j < mNumPowTablePreSquares; j++) {
+ a = sqrt(a);
+ }
+ uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
+ MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
+
+ mPowTable[i] = cachedInt;
+ }
+ }
+
+ // Only call Pow() if HasPowerTable() would return true, to avoid complicating
+ // this code and having it just return (1 << sOutputIntPrecisionBits))
+ uint16_t Pow(uint16_t aBase)
+ {
+ MOZ_ASSERT(HasPowerTable());
+ // Results should be similar to what the following code would produce:
+ // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
+ // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
+
+ MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
+
+ uint32_t a = aBase;
+ for (int j = 0; j < mNumPowTablePreSquares; j++) {
+ a = a * a >> sInputIntPrecisionBits;
+ }
+ uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
+ MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
+ return mPowTable[i];
+ }
+
+ static const int sInputIntPrecisionBits = 15;
+ static const int sOutputIntPrecisionBits = 15;
+ static const int sCacheIndexPrecisionBits = 7;
+
+ inline bool HasPowerTable() const
+ {
+ return mNumPowTablePreSquares >= 0;
+ }
+
+private:
+ static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
+
+ int mNumPowTablePreSquares;
+ uint16_t mPowTable[sCacheSize];
+};
+
+class PointLightSoftware
+{
+public:
+ bool SetAttribute(uint32_t aIndex, Float) { return false; }
+ bool SetAttribute(uint32_t aIndex, const Point3D &);
+ void Prepare() {}
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Point3D mPosition;
+};
+
+class SpotLightSoftware
+{
+public:
+ SpotLightSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ bool SetAttribute(uint32_t aIndex, const Point3D &);
+ void Prepare();
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Point3D mPosition;
+ Point3D mPointsAt;
+ Point3D mVectorFromFocusPointToLight;
+ Float mSpecularFocus;
+ Float mLimitingConeAngle;
+ Float mLimitingConeCos;
+ PowCache mPowCache;
+};
+
+class DistantLightSoftware
+{
+public:
+ DistantLightSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
+ void Prepare();
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Float mAzimuth;
+ Float mElevation;
+ Point3D mVectorToLight;
+};
+
+class DiffuseLightingSoftware
+{
+public:
+ DiffuseLightingSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ void Prepare() {}
+ uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
+ uint32_t aColor);
+
+private:
+ Float mDiffuseConstant;
+};
+
+class SpecularLightingSoftware
+{
+public:
+ SpecularLightingSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ void Prepare();
+ uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
+ uint32_t aColor);
+
+private:
+ Float mSpecularConstant;
+ Float mSpecularExponent;
+ uint32_t mSpecularConstantInt;
+ PowCache mPowCache;
+};
+
+} // unnamed namespace
+
+// from xpcom/ds/nsMathUtils.h
+static int32_t
+NS_lround(double x)
+{
+ return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
+}
+
+already_AddRefed<DataSourceSurface>
+CloneAligned(DataSourceSurface* aSource)
+{
+ return CreateDataSourceSurfaceByCloning(aSource);
+}
+
+static void
+FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
+ "aPixelPos needs to be inside the surface");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if(MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+ uint8_t* sourcePixelData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
+ int bpp = BytesPerPixel(aSurface->GetFormat());
+
+ // Fill the first row by hand.
+ if (bpp == 4) {
+ uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
+ for (int32_t x = 0; x < aFillRect.width; x++) {
+ *((uint32_t*)data + x) = sourcePixel;
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ uint8_t sourcePixel = *sourcePixelData;
+ memset(data, sourcePixel, aFillRect.width);
+ }
+
+ // Copy the first row into the other rows.
+ for (int32_t y = 1; y < aFillRect.height; y++) {
+ PodCopy(data + y * surfMap.GetStride(), data, aFillRect.width * bpp);
+ }
+}
+
+static void
+FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
+ const IntRect &aFillRect,
+ const IntRect &aSampleRect)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(!aSampleRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+ "aSampleRect needs to be completely inside the surface");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
+ if (BytesPerPixel(aSurface->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
+ data += surfMap.GetStride();
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ PodCopy(data, sampleData, aFillRect.width);
+ data += surfMap.GetStride();
+ }
+ }
+}
+
+static void
+FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
+ const IntRect &aFillRect,
+ const IntRect &aSampleRect)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(!aSampleRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+ "aSampleRect needs to be completely inside the surface");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
+ if (BytesPerPixel(aSurface->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ int32_t sampleColor = *((uint32_t*)sampleData);
+ for (int32_t x = 0; x < aFillRect.width; x++) {
+ *((uint32_t*)data + x) = sampleColor;
+ }
+ data += surfMap.GetStride();
+ sampleData += surfMap.GetStride();
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ uint8_t sampleColor = *sampleData;
+ memset(data, sampleColor, aFillRect.width);
+ data += surfMap.GetStride();
+ sampleData += surfMap.GetStride();
+ }
+ }
+}
+
+static void
+DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
+{
+ MOZ_ASSERT(!aFromRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
+ "aFromRect needs to be completely inside the surface");
+
+ IntSize size = aSurface->GetSize();
+ IntRect fill;
+ IntRect sampleRect;
+ for (int32_t ix = 0; ix < 3; ix++) {
+ switch (ix) {
+ case 0:
+ fill.x = 0;
+ fill.width = aFromRect.x;
+ sampleRect.x = fill.XMost();
+ sampleRect.width = 1;
+ break;
+ case 1:
+ fill.x = aFromRect.x;
+ fill.width = aFromRect.width;
+ sampleRect.x = fill.x;
+ sampleRect.width = fill.width;
+ break;
+ case 2:
+ fill.x = aFromRect.XMost();
+ fill.width = size.width - fill.x;
+ sampleRect.x = fill.x - 1;
+ sampleRect.width = 1;
+ break;
+ }
+ if (fill.width <= 0) {
+ continue;
+ }
+ bool xIsMiddle = (ix == 1);
+ for (int32_t iy = 0; iy < 3; iy++) {
+ switch (iy) {
+ case 0:
+ fill.y = 0;
+ fill.height = aFromRect.y;
+ sampleRect.y = fill.YMost();
+ sampleRect.height = 1;
+ break;
+ case 1:
+ fill.y = aFromRect.y;
+ fill.height = aFromRect.height;
+ sampleRect.y = fill.y;
+ sampleRect.height = fill.height;
+ break;
+ case 2:
+ fill.y = aFromRect.YMost();
+ fill.height = size.height - fill.y;
+ sampleRect.y = fill.y - 1;
+ sampleRect.height = 1;
+ break;
+ }
+ if (fill.height <= 0) {
+ continue;
+ }
+ bool yIsMiddle = (iy == 1);
+ if (!xIsMiddle && !yIsMiddle) {
+ // Corner
+ FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
+ }
+ if (xIsMiddle && !yIsMiddle) {
+ // Top middle or bottom middle
+ FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
+ }
+ if (!xIsMiddle && yIsMiddle) {
+ // Left middle or right middle
+ FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
+ }
+ }
+ }
+}
+
+static IntPoint
+TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
+{
+ return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)),
+ int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height)));
+}
+
+static void
+TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
+{
+ IntRect sourceRect(aOffset, aSource->GetSize());
+ IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
+ IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
+ IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
+
+ for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
+ for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
+ IntPoint destPoint(sourceRect.x + ix * sourceRect.width,
+ sourceRect.y + iy * sourceRect.height);
+ IntRect destRect(destPoint, sourceRect.Size());
+ destRect = destRect.Intersect(targetRect);
+ IntRect srcRect = destRect - destPoint;
+ CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
+ }
+ }
+}
+
+static already_AddRefed<DataSourceSurface>
+GetDataSurfaceInRect(SourceSurface *aSurface,
+ const IntRect &aSurfaceRect,
+ const IntRect &aDestRect,
+ ConvolveMatrixEdgeMode aEdgeMode)
+{
+ MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
+
+ if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
+ // We can't rely on the intersection calculations below to make sense when
+ // XMost() or YMost() overflow. Bail out.
+ return nullptr;
+ }
+
+ IntRect sourceRect = aSurfaceRect;
+
+ if (sourceRect.IsEqualEdges(aDestRect)) {
+ return aSurface ? aSurface->GetDataSurface() : nullptr;
+ }
+
+ IntRect intersect = sourceRect.Intersect(aDestRect);
+ IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
+ IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
+ SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ if (!aSurface) {
+ return target.forget();
+ }
+
+ RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
+ MOZ_ASSERT(dataSource);
+
+ if (aEdgeMode == EDGE_MODE_WRAP) {
+ TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
+ return target.forget();
+ }
+
+ CopyRect(dataSource, target, intersectInSourceSpace,
+ intersectInDestSpace.TopLeft());
+
+ if (aEdgeMode == EDGE_MODE_DUPLICATE) {
+ DuplicateEdges(target, intersectInDestSpace);
+ }
+
+ return target.forget();
+}
+
+/* static */ already_AddRefed<FilterNode>
+FilterNodeSoftware::Create(FilterType aType)
+{
+ RefPtr<FilterNodeSoftware> filter;
+ switch (aType) {
+ case FilterType::BLEND:
+ filter = new FilterNodeBlendSoftware();
+ break;
+ case FilterType::TRANSFORM:
+ filter = new FilterNodeTransformSoftware();
+ break;
+ case FilterType::MORPHOLOGY:
+ filter = new FilterNodeMorphologySoftware();
+ break;
+ case FilterType::COLOR_MATRIX:
+ filter = new FilterNodeColorMatrixSoftware();
+ break;
+ case FilterType::FLOOD:
+ filter = new FilterNodeFloodSoftware();
+ break;
+ case FilterType::TILE:
+ filter = new FilterNodeTileSoftware();
+ break;
+ case FilterType::TABLE_TRANSFER:
+ filter = new FilterNodeTableTransferSoftware();
+ break;
+ case FilterType::DISCRETE_TRANSFER:
+ filter = new FilterNodeDiscreteTransferSoftware();
+ break;
+ case FilterType::LINEAR_TRANSFER:
+ filter = new FilterNodeLinearTransferSoftware();
+ break;
+ case FilterType::GAMMA_TRANSFER:
+ filter = new FilterNodeGammaTransferSoftware();
+ break;
+ case FilterType::CONVOLVE_MATRIX:
+ filter = new FilterNodeConvolveMatrixSoftware();
+ break;
+ case FilterType::DISPLACEMENT_MAP:
+ filter = new FilterNodeDisplacementMapSoftware();
+ break;
+ case FilterType::TURBULENCE:
+ filter = new FilterNodeTurbulenceSoftware();
+ break;
+ case FilterType::ARITHMETIC_COMBINE:
+ filter = new FilterNodeArithmeticCombineSoftware();
+ break;
+ case FilterType::COMPOSITE:
+ filter = new FilterNodeCompositeSoftware();
+ break;
+ case FilterType::GAUSSIAN_BLUR:
+ filter = new FilterNodeGaussianBlurSoftware();
+ break;
+ case FilterType::DIRECTIONAL_BLUR:
+ filter = new FilterNodeDirectionalBlurSoftware();
+ break;
+ case FilterType::CROP:
+ filter = new FilterNodeCropSoftware();
+ break;
+ case FilterType::PREMULTIPLY:
+ filter = new FilterNodePremultiplySoftware();
+ break;
+ case FilterType::UNPREMULTIPLY:
+ filter = new FilterNodeUnpremultiplySoftware();
+ break;
+ case FilterType::POINT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
+ break;
+ case FilterType::POINT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
+ break;
+ case FilterType::SPOT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
+ break;
+ case FilterType::SPOT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
+ break;
+ case FilterType::DISTANT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
+ break;
+ case FilterType::DISTANT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
+ break;
+ }
+ return filter.forget();
+}
+
+void
+FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
+#endif
+
+ Rect renderRect = aSourceRect;
+ renderRect.RoundOut();
+ IntRect renderIntRect;
+ if (!renderRect.ToIntRect(&renderIntRect)) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("render rect overflowed, not painting anything\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+ IntRect outputRect = GetOutputRectInRect(renderIntRect);
+ if (outputRect.Overflows()) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output rect overflowed, not painting anything\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+ RefPtr<DataSourceSurface> result;
+ if (!outputRect.IsEmpty()) {
+ result = GetOutput(outputRect);
+ }
+
+ if (!result) {
+ // Null results are allowed and treated as transparent. Don't draw anything.
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output returned null\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output from %s:\n", GetName());
+ printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n");
+ printf("</pre>\n");
+#endif
+
+ Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
+ Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
+ Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
+ if (result->GetFormat() == SurfaceFormat::A8) {
+ // Interpret the result as having implicitly black color channels.
+ aDrawTarget->PushClipRect(renderedDestRect);
+ aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
+ result,
+ Point(outputRect.TopLeft()) + sourceToDestOffset,
+ aOptions);
+ aDrawTarget->PopClip();
+ } else {
+ aDrawTarget->DrawSurface(result, renderedDestRect,
+ renderedSourceRect - Point(outputRect.TopLeft()),
+ DrawSurfaceOptions(), aOptions);
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeSoftware::GetOutput(const IntRect &aRect)
+{
+ MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
+
+ if (aRect.Overflows()) {
+ return nullptr;
+ }
+
+ if (!mCachedRect.Contains(aRect)) {
+ RequestRect(aRect);
+ mCachedOutput = Render(mRequestedRect);
+ if (!mCachedOutput) {
+ mCachedRect = IntRect();
+ mRequestedRect = IntRect();
+ return nullptr;
+ }
+ mCachedRect = mRequestedRect;
+ mRequestedRect = IntRect();
+ } else {
+ MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
+ }
+ return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
+}
+
+void
+FilterNodeSoftware::RequestRect(const IntRect &aRect)
+{
+ if (mRequestedRect.Contains(aRect)) {
+ // Bail out now. Otherwise pathological filters can spend time exponential
+ // in the number of primitives, e.g. if each primitive takes the
+ // previous primitive as its two inputs.
+ return;
+ }
+ mRequestedRect = mRequestedRect.Union(aRect);
+ RequestFromInputsForRect(aRect);
+}
+
+void
+FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
+{
+ if (aRect.Overflows()) {
+ return;
+ }
+
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
+ return;
+ }
+ if (mInputSurfaces[inputIndex]) {
+ return;
+ }
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ filter->RequestRect(filter->GetOutputRectInRect(aRect));
+}
+
+SurfaceFormat
+FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
+ FormatHint aFormatHint)
+{
+ if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
+ return SurfaceFormat::A8;
+ }
+ return SurfaceFormat::B8G8R8A8;
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
+ const IntRect& aRect,
+ FormatHint aFormatHint,
+ ConvolveMatrixEdgeMode aEdgeMode,
+ const IntRect *aTransparencyPaddedSourceRect)
+{
+ if (aRect.Overflows()) {
+ return nullptr;
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
+ aRect.x, aRect.y, aRect.width, aRect.height);
+#endif
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ gfxDevCrash(LogReason::FilterInputData) << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
+ return nullptr;
+ }
+
+ if (aRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurface> surface;
+ IntRect surfaceRect;
+
+ if (mInputSurfaces[inputIndex]) {
+ // Input from input surface
+ surface = mInputSurfaces[inputIndex];
+#ifdef DEBUG_DUMP_SURFACES
+ printf("input from input surface:\n");
+#endif
+ surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
+ } else {
+ // Input from input filter
+#ifdef DEBUG_DUMP_SURFACES
+ printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
+#endif
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
+ if (!inputFilterOutput.IsEmpty()) {
+ surface = filter->GetOutput(inputFilterOutput);
+ }
+#ifdef DEBUG_DUMP_SURFACES
+ printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
+#endif
+ surfaceRect = inputFilterOutput;
+ MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
+ }
+
+ if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("wrong input format</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ if (!surfaceRect.IsEmpty() && !surface) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf(" -- no input --</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
+ IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
+ surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
+ surfaceRect = srcRect;
+ }
+
+ RefPtr<DataSourceSurface> result =
+ GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
+
+ if (result) {
+ // TODO: This isn't safe since we don't have a guarantee
+ // that future Maps will have the same stride
+ DataSourceSurface::MappedSurface map;
+ if (result->Map(DataSourceSurface::READ, &map)) {
+ // Unmap immediately since CloneAligned hasn't been updated
+ // to use the Map API yet. We can still read the stride/data
+ // values as long as we don't try to dereference them.
+ result->Unmap();
+ if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
+ reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
+ // Align unaligned surface.
+ result = CloneAligned(result);
+ }
+ } else {
+ result = nullptr;
+ }
+ }
+
+
+ if (!result) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf(" -- no input --</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ SurfaceFormat currentFormat = result->GetFormat();
+ if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
+ currentFormat != SurfaceFormat::B8G8R8A8) {
+ result = FilterProcessing::ConvertToB8G8R8A8(result);
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>");
+#endif
+
+ MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
+
+ return result.forget();
+}
+
+IntRect
+FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
+ const IntRect &aInRect)
+{
+ if (aInRect.Overflows()) {
+ return IntRect();
+ }
+
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ gfxDevCrash(LogReason::FilterInputRect) << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
+ return IntRect();
+ }
+ if (mInputSurfaces[inputIndex]) {
+ return aInRect.Intersect(IntRect(IntPoint(0, 0),
+ mInputSurfaces[inputIndex]->GetSize()));
+ }
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ return filter->GetOutputRectInRect(aInRect);
+}
+
+size_t
+FilterNodeSoftware::NumberOfSetInputs()
+{
+ return std::max(mInputSurfaces.size(), mInputFilters.size());
+}
+
+void
+FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
+{
+ MOZ_ASSERT(aListener, "null listener");
+ mInvalidationListeners.push_back(aListener);
+}
+
+void
+FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
+{
+ MOZ_ASSERT(aListener, "null listener");
+ std::vector<FilterInvalidationListener*>::iterator it =
+ std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
+ mInvalidationListeners.erase(it);
+}
+
+void
+FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
+{
+ Invalidate();
+}
+
+void
+FilterNodeSoftware::Invalidate()
+{
+ mCachedOutput = nullptr;
+ mCachedRect = IntRect();
+ for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
+ it != mInvalidationListeners.end(); it++) {
+ (*it)->FilterInvalidated(this);
+ }
+}
+
+FilterNodeSoftware::~FilterNodeSoftware()
+{
+ MOZ_ASSERT(!mInvalidationListeners.size(),
+ "All invalidation listeners should have unsubscribed themselves by now!");
+
+ for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
+ it != mInputFilters.end(); it++) {
+ if (*it) {
+ (*it)->RemoveInvalidationListener(this);
+ }
+ }
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
+ MOZ_ASSERT(false, "can only take software filters as inputs");
+ return;
+ }
+ SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
+{
+ SetInput(aIndex, aSurface, nullptr);
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
+ SourceSurface *aSurface,
+ FilterNodeSoftware *aFilter)
+{
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0) {
+ gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
+ return;
+ }
+ if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
+ mInputSurfaces.resize(inputIndex + 1);
+ mInputFilters.resize(inputIndex + 1);
+ }
+ mInputSurfaces[inputIndex] = aSurface;
+ if (mInputFilters[inputIndex]) {
+ mInputFilters[inputIndex]->RemoveInvalidationListener(this);
+ }
+ if (aFilter) {
+ aFilter->AddInvalidationListener(this);
+ }
+ mInputFilters[inputIndex] = aFilter;
+ if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
+ mInputSurfaces.resize(inputIndex);
+ mInputFilters.resize(inputIndex);
+ }
+ Invalidate();
+}
+
+FilterNodeBlendSoftware::FilterNodeBlendSoftware()
+ : mBlendMode(BLEND_MODE_MULTIPLY)
+{}
+
+int32_t
+FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_BLEND_IN: return 0;
+ case IN_BLEND_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
+{
+ MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
+ mBlendMode = static_cast<BlendMode>(aBlendMode);
+ Invalidate();
+}
+
+static CompositionOp ToBlendOp(BlendMode aOp)
+{
+ switch (aOp) {
+ case BLEND_MODE_MULTIPLY:
+ return CompositionOp::OP_MULTIPLY;
+ case BLEND_MODE_SCREEN:
+ return CompositionOp::OP_SCREEN;
+ case BLEND_MODE_OVERLAY:
+ return CompositionOp::OP_OVERLAY;
+ case BLEND_MODE_DARKEN:
+ return CompositionOp::OP_DARKEN;
+ case BLEND_MODE_LIGHTEN:
+ return CompositionOp::OP_LIGHTEN;
+ case BLEND_MODE_COLOR_DODGE:
+ return CompositionOp::OP_COLOR_DODGE;
+ case BLEND_MODE_COLOR_BURN:
+ return CompositionOp::OP_COLOR_BURN;
+ case BLEND_MODE_HARD_LIGHT:
+ return CompositionOp::OP_HARD_LIGHT;
+ case BLEND_MODE_SOFT_LIGHT:
+ return CompositionOp::OP_SOFT_LIGHT;
+ case BLEND_MODE_DIFFERENCE:
+ return CompositionOp::OP_DIFFERENCE;
+ case BLEND_MODE_EXCLUSION:
+ return CompositionOp::OP_EXCLUSION;
+ case BLEND_MODE_HUE:
+ return CompositionOp::OP_HUE;
+ case BLEND_MODE_SATURATION:
+ return CompositionOp::OP_SATURATION;
+ case BLEND_MODE_COLOR:
+ return CompositionOp::OP_COLOR;
+ case BLEND_MODE_LUMINOSITY:
+ return CompositionOp::OP_LUMINOSITY;
+ default:
+ return CompositionOp::OP_OVER;
+ }
+
+ return CompositionOp::OP_OVER;
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeBlendSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input1 =
+ GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> input2 =
+ GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
+
+ // Null inputs need to be treated as transparent.
+
+ // First case: both are transparent.
+ if (!input1 && !input2) {
+ // Then the result is transparent, too.
+ return nullptr;
+ }
+
+ // Second case: one of them is transparent. Return the non-transparent one.
+ if (!input1 || !input2) {
+ return input1 ? input1.forget() : input2.forget();
+ }
+
+ // Third case: both are non-transparent.
+ // Apply normal filtering.
+ RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
+ if (target != nullptr) {
+ return target.forget();
+ }
+
+ IntSize size = input1->GetSize();
+ target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
+
+ // This needs to stay in scope until the draw target has been flushed.
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ targetMap.GetData(),
+ target->GetSize(),
+ targetMap.GetStride(),
+ target->GetFormat());
+
+ if (!dt) {
+ gfxWarning() << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
+ return nullptr;
+ }
+
+ Rect r(0, 0, size.width, size.height);
+ dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
+ dt->Flush();
+ return target.forget();
+}
+
+void
+FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_BLEND_IN, aRect);
+ RequestInputRect(IN_BLEND_IN2, aRect);
+}
+
+IntRect
+FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
+ GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
+}
+
+FilterNodeTransformSoftware::FilterNodeTransformSoftware()
+ : mSamplingFilter(SamplingFilter::GOOD)
+{}
+
+int32_t
+FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TRANSFORM_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
+{
+ MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
+ mSamplingFilter = static_cast<SamplingFilter>(aFilter);
+ Invalidate();
+}
+
+void
+FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
+{
+ MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
+ mMatrix = aMatrix;
+ Invalidate();
+}
+
+IntRect
+FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
+{
+ if (aRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Matrix inverted(mMatrix);
+ if (!inverted.Invert()) {
+ return IntRect();
+ }
+
+ Rect neededRect = inverted.TransformBounds(Rect(aRect));
+ neededRect.RoundOut();
+ IntRect neededIntRect;
+ if (!neededRect.ToIntRect(&neededIntRect)) {
+ return IntRect();
+ }
+ return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeTransformSoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = SourceRectForOutputRect(aRect);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
+ Matrix::Translation(-aRect.x, -aRect.y);
+ if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
+ return input.forget();
+ }
+
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
+
+ if (!surf) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface mapping;
+ if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
+ gfxCriticalError() << "FilterNodeTransformSoftware::Render failed to map surface";
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ mapping.mData,
+ surf->GetSize(),
+ mapping.mStride,
+ surf->GetFormat());
+ if (!dt) {
+ gfxWarning() << "FilterNodeTransformSoftware::Render failed in CreateDrawTargetForData";
+ return nullptr;
+ }
+
+ Rect r(0, 0, srcRect.width, srcRect.height);
+ dt->SetTransform(transform);
+ dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
+
+ dt->Flush();
+ surf->Unmap();
+ return surf.forget();
+}
+
+void
+FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
+}
+
+IntRect
+FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRect = SourceRectForOutputRect(aRect);
+ if (srcRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
+ outRect.RoundOut();
+ IntRect outIntRect;
+ if (!outRect.ToIntRect(&outIntRect)) {
+ return IntRect();
+ }
+ return outIntRect.Intersect(aRect);
+}
+
+FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
+ : mOperator(MORPHOLOGY_OPERATOR_ERODE)
+{}
+
+int32_t
+FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_MORPHOLOGY_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
+ const IntSize &aRadii)
+{
+ MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
+ mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
+ mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
+ Invalidate();
+}
+
+void
+FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aOperator)
+{
+ MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
+ mOperator = static_cast<MorphologyOperator>(aOperator);
+ Invalidate();
+}
+
+static already_AddRefed<DataSourceSurface>
+ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
+ const IntRect& aDestRect, int32_t rx, int32_t ry,
+ MorphologyOperator aOperator)
+{
+ IntRect srcRect = aSourceRect - aDestRect.TopLeft();
+ IntRect destRect = aDestRect - aDestRect.TopLeft();
+ IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height);
+#ifdef DEBUG
+ IntMargin margin = srcRect - destRect;
+ MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
+ margin.bottom >= ry && margin.left >= rx, "insufficient margin");
+#endif
+
+ RefPtr<DataSourceSurface> tmp;
+ if (rx == 0) {
+ tmp = aInput;
+ } else {
+ tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!tmp)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
+ return nullptr;
+ }
+ uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
+ destRect.TopLeft() - srcRect.TopLeft());
+ uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
+ destRect.TopLeft() - tmpRect.TopLeft());
+
+ FilterProcessing::ApplyMorphologyHorizontal(
+ sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect, rx, aOperator);
+ }
+
+ RefPtr<DataSourceSurface> dest;
+ if (ry == 0) {
+ dest = tmp;
+ } else {
+ dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!dest)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
+ return nullptr;
+ }
+ int32_t tmpStride = tmpMap.GetStride();
+ uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft());
+
+ int32_t destStride = destMap.GetStride();
+ uint8_t* destData = destMap.GetData();
+
+ FilterProcessing::ApplyMorphologyVertical(
+ tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
+ }
+
+ return dest.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeMorphologySoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(mRadii);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
+ if (!input) {
+ return nullptr;
+ }
+
+ int32_t rx = mRadii.width;
+ int32_t ry = mRadii.height;
+
+ if (rx == 0 && ry == 0) {
+ return input.forget();
+ }
+
+ return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
+}
+
+void
+FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(mRadii);
+ RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
+}
+
+IntRect
+FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect inflatedSourceRect = aRect;
+ inflatedSourceRect.Inflate(mRadii);
+ IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
+ if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
+ inputRect.Deflate(mRadii);
+ } else {
+ inputRect.Inflate(mRadii);
+ }
+ return inputRect.Intersect(aRect);
+}
+
+int32_t
+FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_COLOR_MATRIX_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const Matrix5x4 &aMatrix)
+{
+ MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
+ mMatrix = aMatrix;
+ Invalidate();
+}
+
+void
+FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aAlphaMode)
+{
+ MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
+ mAlphaMode = (AlphaMode)aAlphaMode;
+ Invalidate();
+}
+
+static already_AddRefed<DataSourceSurface>
+Premultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ RefPtr<DataSourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = inputMap.GetData();
+ int32_t inputStride = inputMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ FilterProcessing::DoPremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+static already_AddRefed<DataSourceSurface>
+Unpremultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ RefPtr<DataSourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = inputMap.GetData();
+ int32_t inputStride = inputMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ FilterProcessing::DoUnpremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
+ if (!input) {
+ return nullptr;
+ }
+
+ if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
+ input = Unpremultiply(input);
+ }
+
+ RefPtr<DataSourceSurface> result =
+ FilterProcessing::ApplyColorMatrix(input, mMatrix);
+
+ if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
+ result = Premultiply(result);
+ }
+
+ return result.forget();
+}
+
+void
+FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
+}
+
+IntRect
+FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mMatrix._54 > 0.0f) {
+ return aRect;
+ }
+ return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
+}
+
+void
+FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
+{
+ MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
+ mColor = aColor;
+ Invalidate();
+}
+
+static uint32_t
+ColorToBGRA(const Color& aColor)
+{
+ union {
+ uint32_t color;
+ uint8_t components[4];
+ };
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
+ return color;
+}
+
+static SurfaceFormat
+FormatForColor(Color aColor)
+{
+ if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
+ return SurfaceFormat::A8;
+ }
+ return SurfaceFormat::B8G8R8A8;
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeFloodSoftware::Render(const IntRect& aRect)
+{
+ SurfaceFormat format = FormatForColor(mColor);
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), format);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* targetData = targetMap.GetData();
+ int32_t stride = targetMap.GetStride();
+
+ if (format == SurfaceFormat::B8G8R8A8) {
+ uint32_t color = ColorToBGRA(mColor);
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ *((uint32_t*)targetData + x) = color;
+ }
+ PodZero(&targetData[aRect.width * 4], stride - aRect.width * 4);
+ targetData += stride;
+ }
+ } else if (format == SurfaceFormat::A8) {
+ uint8_t alpha = NS_lround(mColor.a * 255.0f);
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ targetData[x] = alpha;
+ }
+ PodZero(&targetData[aRect.width], stride - aRect.width);
+ targetData += stride;
+ }
+ } else {
+ gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
+ return nullptr;
+ }
+
+ return target.forget();
+}
+
+// Override GetOutput to get around caching. Rendering simple floods is
+// comparatively fast.
+already_AddRefed<DataSourceSurface>
+FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
+{
+ return Render(aRect);
+}
+
+IntRect
+FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mColor.a == 0.0f) {
+ return IntRect();
+ }
+ return aRect;
+}
+
+int32_t
+FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TILE_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
+ const IntRect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
+ mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
+ int32_t(aSourceRect.width), int32_t(aSourceRect.height));
+ Invalidate();
+}
+
+namespace {
+struct CompareIntRects
+{
+ bool operator()(const IntRect& a, const IntRect& b) const
+ {
+ if (a.x != b.x) {
+ return a.x < b.x;
+ }
+ if (a.y != b.y) {
+ return a.y < b.y;
+ }
+ if (a.width != b.width) {
+ return a.width < b.width;
+ }
+ return a.height < b.height;
+ }
+};
+
+} // namespace
+
+already_AddRefed<DataSourceSurface>
+FilterNodeTileSoftware::Render(const IntRect& aRect)
+{
+ if (mSourceRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ if (mSourceRect.Contains(aRect)) {
+ return GetInputDataSourceSurface(IN_TILE_IN, aRect);
+ }
+
+ RefPtr<DataSourceSurface> target;
+
+ typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
+ InputMap inputs;
+
+ IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
+ IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
+ for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
+ for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
+ IntPoint sourceToDestOffset(ix * mSourceRect.width,
+ iy * mSourceRect.height);
+ IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
+ IntRect srcRect = destRect - sourceToDestOffset;
+ if (srcRect.IsEmpty()) {
+ continue;
+ }
+
+ RefPtr<DataSourceSurface> input;
+ InputMap::iterator it = inputs.find(srcRect);
+ if (it == inputs.end()) {
+ input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
+ inputs[srcRect] = input;
+ } else {
+ input = it->second;
+ }
+ if (!input) {
+ return nullptr;
+ }
+ if (!target) {
+ // We delay creating the target until now because we want to use the
+ // same format as our input filter, and we do not actually know the
+ // input format before we call GetInputDataSourceSurface.
+ target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+ }
+
+ if (input->GetFormat() != target->GetFormat()) {
+ // Different rectangles of the input can have different formats. If
+ // that happens, just convert everything to B8G8R8A8.
+ target = FilterProcessing::ConvertToB8G8R8A8(target);
+ input = FilterProcessing::ConvertToB8G8R8A8(input);
+ if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
+ return nullptr;
+ }
+ }
+
+ CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
+ }
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ // Do not request anything.
+ // Source rects for the tile filter can be discontinuous with large gaps
+ // between them. Requesting those from our input filter might cause it to
+ // render the whole bounding box of all of them, which would be wasteful.
+}
+
+IntRect
+FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return aRect;
+}
+
+FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
+ : mDisableR(true)
+ , mDisableG(true)
+ , mDisableB(true)
+ , mDisableA(true)
+{}
+
+void
+FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
+ bool aDisable)
+{
+ switch (aIndex) {
+ case ATT_TRANSFER_DISABLE_R:
+ mDisableR = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_G:
+ mDisableG = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_B:
+ mDisableB = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_A:
+ mDisableA = aDisable;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
+ uint8_t aTables[4][256],
+ bool aDisabled)
+{
+ if (aDisabled) {
+ static uint8_t sIdentityLookupTable[256];
+ static bool sInitializedIdentityLookupTable = false;
+ if (!sInitializedIdentityLookupTable) {
+ for (int32_t i = 0; i < 256; i++) {
+ sIdentityLookupTable[i] = i;
+ }
+ sInitializedIdentityLookupTable = true;
+ }
+ memcpy(aTables[aComponent], sIdentityLookupTable, 256);
+ } else {
+ FillLookupTable(aComponent, aTables[aComponent]);
+ }
+}
+
+template<uint32_t BytesPerPixel>
+static void TransferComponents(DataSourceSurface* aInput,
+ DataSourceSurface* aTarget,
+ const uint8_t aLookupTables[BytesPerPixel][256])
+{
+ MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
+ IntSize size = aInput->GetSize();
+
+ DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
+ uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
+ for (uint32_t i = 0; i < BytesPerPixel; i++) {
+ targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
+ }
+ }
+
+ // Zero padding to keep valgrind happy.
+ PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
+ targetStride - size.width * BytesPerPixel);
+ }
+}
+
+bool
+IsAllZero(uint8_t aLookupTable[256])
+{
+ for (int32_t i = 0; i < 256; i++) {
+ if (aLookupTable[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
+{
+ if (mDisableR && mDisableG && mDisableB && mDisableA) {
+ return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
+ }
+
+ uint8_t lookupTables[4][256];
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
+
+ bool needColorChannels =
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
+
+ FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
+ if (!input) {
+ return nullptr;
+ }
+
+ if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
+ bool colorChannelsBecomeBlack =
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
+
+ if (colorChannelsBecomeBlack) {
+ input = FilterProcessing::ExtractAlpha(input);
+ }
+ }
+
+ SurfaceFormat format = input->GetFormat();
+ if (format == SurfaceFormat::A8 && mDisableA) {
+ return input.forget();
+ }
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), format);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ if (format == SurfaceFormat::A8) {
+ TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
+ } else {
+ TransferComponents<4>(input, target, lookupTables);
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_TRANSFER_IN, aRect);
+}
+
+IntRect
+FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mDisableA) {
+ return GetInputRectInRect(IN_TRANSFER_IN, aRect);
+ }
+ return aRect;
+}
+
+int32_t
+FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TRANSFER_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ std::vector<Float> table(aFloat, aFloat + aSize);
+ switch (aIndex) {
+ case ATT_TABLE_TRANSFER_TABLE_R:
+ mTableR = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_G:
+ mTableG = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_B:
+ mTableB = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_A:
+ mTableA = table;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mTableR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mTableG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mTableB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mTableA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
+ uint8_t aTable[256])
+{
+ uint32_t tvLength = aTableValues.size();
+ if (tvLength < 2) {
+ return;
+ }
+
+ for (size_t i = 0; i < 256; i++) {
+ uint32_t k = (i * (tvLength - 1)) / 255;
+ Float v1 = aTableValues[k];
+ Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
+ int32_t val =
+ int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+void
+FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ std::vector<Float> discrete(aFloat, aFloat + aSize);
+ switch (aIndex) {
+ case ATT_DISCRETE_TRANSFER_TABLE_R:
+ mTableR = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_G:
+ mTableG = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_B:
+ mTableB = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_A:
+ mTableA = discrete;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mTableR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mTableG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mTableB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mTableA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
+ uint8_t aTable[256])
+{
+ uint32_t tvLength = aTableValues.size();
+ if (tvLength < 1) {
+ return;
+ }
+
+ for (size_t i = 0; i < 256; i++) {
+ uint32_t k = (i * tvLength) / 255;
+ k = std::min(k, tvLength - 1);
+ Float v = aTableValues[k];
+ int32_t val = NS_lround(255 * v);
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
+ : mSlopeR(0)
+ , mSlopeG(0)
+ , mSlopeB(0)
+ , mSlopeA(0)
+ , mInterceptR(0)
+ , mInterceptG(0)
+ , mInterceptB(0)
+ , mInterceptA(0)
+{}
+
+void
+FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
+ Float aValue)
+{
+ switch (aIndex) {
+ case ATT_LINEAR_TRANSFER_SLOPE_R:
+ mSlopeR = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_R:
+ mInterceptR = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_G:
+ mSlopeG = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_G:
+ mInterceptG = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_B:
+ mSlopeB = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_B:
+ mInterceptB = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_A:
+ mSlopeA = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_A:
+ mInterceptA = aValue;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
+ Float aIntercept,
+ uint8_t aTable[256])
+{
+ for (size_t i = 0; i < 256; i++) {
+ int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
+ : mAmplitudeR(0)
+ , mAmplitudeG(0)
+ , mAmplitudeB(0)
+ , mAmplitudeA(0)
+ , mExponentR(0)
+ , mExponentG(0)
+ , mExponentB(0)
+ , mExponentA(0)
+{}
+
+void
+FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
+ Float aValue)
+{
+ switch (aIndex) {
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
+ mAmplitudeR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_R:
+ mExponentR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_R:
+ mOffsetR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
+ mAmplitudeG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_G:
+ mExponentG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_G:
+ mOffsetG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
+ mAmplitudeB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_B:
+ mExponentB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_B:
+ mOffsetB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
+ mAmplitudeA = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_A:
+ mExponentA = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_A:
+ mOffsetA = aValue;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
+ Float aExponent,
+ Float aOffset,
+ uint8_t aTable[256])
+{
+ for (size_t i = 0; i < 256; i++) {
+ int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
+ : mDivisor(0)
+ , mBias(0)
+ , mEdgeMode(EDGE_MODE_DUPLICATE)
+ , mPreserveAlpha(false)
+{}
+
+int32_t
+FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_CONVOLVE_MATRIX_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntSize &aKernelSize)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
+ mKernelSize = aKernelSize;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const Float *aMatrix,
+ uint32_t aSize)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
+ mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_CONVOLVE_MATRIX_DIVISOR:
+ mDivisor = aValue;
+ break;
+ case ATT_CONVOLVE_MATRIX_BIAS:
+ mBias = aValue;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
+{
+ switch (aIndex) {
+ case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
+ mKernelUnitLength = aKernelUnitLength;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntPoint &aTarget)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
+ mTarget = aTarget;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntRect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
+ mSourceRect = aSourceRect;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aEdgeMode)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
+ mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ bool aPreserveAlpha)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
+ mPreserveAlpha = aPreserveAlpha;
+ Invalidate();
+}
+
+#ifdef DEBUG
+static bool sColorSamplingAccessControlEnabled = false;
+static uint8_t* sColorSamplingAccessControlStart = nullptr;
+static uint8_t* sColorSamplingAccessControlEnd = nullptr;
+
+struct DebugOnlyAutoColorSamplingAccessControl
+{
+ explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
+ {
+ sColorSamplingAccessControlStart = aSurface->GetData();
+ sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
+ aSurface->Stride() * aSurface->GetSize().height;
+ sColorSamplingAccessControlEnabled = true;
+ }
+
+ ~DebugOnlyAutoColorSamplingAccessControl()
+ {
+ sColorSamplingAccessControlEnabled = false;
+ }
+};
+
+static inline void
+DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
+{
+ if (sColorSamplingAccessControlEnabled) {
+ MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
+ MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
+ }
+}
+#else
+typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
+#define DebugOnlyCheckColorSamplingAccess(address)
+#endif
+
+static inline uint8_t
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
+{
+ DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
+ return aData[y * aStride + bpp * x + c];
+}
+
+static inline int32_t
+ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
+{
+ DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
+ return *(uint32_t*)(aData + y * aStride + 4 * x);
+}
+
+// Accepts fractional x & y and does bilinear interpolation.
+// Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
+static inline uint8_t
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
+{
+ const uint32_t f = 256;
+ const int32_t lx = floor(x);
+ const int32_t ly = floor(y);
+ const int32_t tux = uint32_t((x - lx) * f);
+ const int32_t tlx = f - tux;
+ const int32_t tuy = uint32_t((y - ly) * f);
+ const int32_t tly = f - tuy;
+ const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c);
+ const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c);
+ const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c);
+ const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
+ return ((cll * tlx + cul * tux) * tly +
+ (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
+}
+
+static int32_t
+ClampToNonZero(int32_t a)
+{
+ return a * (a >= 0);
+}
+
+template<typename CoordType>
+static void
+ConvolvePixel(const uint8_t *aSourceData,
+ uint8_t *aTargetData,
+ int32_t aWidth, int32_t aHeight,
+ int32_t aSourceStride, int32_t aTargetStride,
+ int32_t aX, int32_t aY,
+ const int32_t *aKernel,
+ int32_t aBias, int32_t shiftL, int32_t shiftR,
+ bool aPreserveAlpha,
+ int32_t aOrderX, int32_t aOrderY,
+ int32_t aTargetX, int32_t aTargetY,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ int32_t sum[4] = {0, 0, 0, 0};
+ int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A };
+ int32_t channels = aPreserveAlpha ? 3 : 4;
+ int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
+
+ for (int32_t y = 0; y < aOrderY; y++) {
+ CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
+ for (int32_t x = 0; x < aOrderX; x++) {
+ CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
+ for (int32_t i = 0; i < channels; i++) {
+ sum[i] += aKernel[aOrderX * y + x] *
+ ColorComponentAtPoint(aSourceData, aSourceStride,
+ sampleX, sampleY, 4, offsets[i]);
+ }
+ }
+ }
+ for (int32_t i = 0; i < channels; i++) {
+ int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
+ aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
+ (clamped + roundingAddition) << shiftR >> shiftL;
+ }
+ if (aPreserveAlpha) {
+ aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
+ aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
+{
+ if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
+ mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
+ return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
+ }
+ return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
+}
+
+static std::vector<Float>
+ReversedVector(const std::vector<Float> &aVector)
+{
+ size_t length = aVector.size();
+ std::vector<Float> result(length, 0);
+ for (size_t i = 0; i < length; i++) {
+ result[length - 1 - i] = aVector[i];
+ }
+ return result;
+}
+
+static std::vector<Float>
+ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
+{
+ size_t length = aVector.size();
+ std::vector<Float> result(length, 0);
+ for (size_t i = 0; i < length; i++) {
+ result[i] = aVector[i] / aDivisor;
+ }
+ return result;
+}
+
+static Float
+MaxVectorSum(const std::vector<Float> &aVector)
+{
+ Float sum = 0;
+ size_t length = aVector.size();
+ for (size_t i = 0; i < length; i++) {
+ if (aVector[i] > 0) {
+ sum += aVector[i];
+ }
+ }
+ return sum;
+}
+
+// Returns shiftL and shiftR in such a way that
+// a << shiftL >> shiftR is roughly a * aFloat.
+static void
+TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
+{
+ aShiftL = 0;
+ aShiftR = 0;
+ if (aDouble <= 0) {
+ MOZ_CRASH("GFX: TranslateDoubleToShifts");
+ }
+ if (aDouble < 1) {
+ while (1 << (aShiftR + 1) < 1 / aDouble) {
+ aShiftR++;
+ }
+ } else {
+ while (1 << (aShiftL + 1) < aDouble) {
+ aShiftL++;
+ }
+ }
+}
+
+template<typename CoordType>
+already_AddRefed<DataSourceSurface>
+FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
+ mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
+ !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
+ mDivisor == 0) {
+ return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
+ }
+
+ IntRect srcRect = InflatedSourceRect(aRect);
+
+ // Inflate the source rect by another pixel because the bilinear filtering in
+ // ColorComponentAtPoint may want to access the margins.
+ srcRect.Inflate(1);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ DebugOnlyAutoColorSamplingAccessControl accessControl(input);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+ DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ // Why exactly are we reversing the kernel?
+ std::vector<Float> kernel = ReversedVector(mKernelMatrix);
+ kernel = ScaledVector(kernel, mDivisor);
+ Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
+ MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
+ maxResultAbs = std::max(maxResultAbs, 1.0f);
+
+ double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
+ MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
+ int32_t shiftL, shiftR;
+ TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
+ double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
+ MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
+
+ int32_t* intKernel = new int32_t[kernel.size()];
+ for (size_t i = 0; i < kernel.size(); i++) {
+ intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
+ }
+ int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
+
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ ConvolvePixel(sourceData, targetData,
+ aRect.width, aRect.height, sourceStride, targetStride,
+ x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
+ mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
+ aKernelUnitLengthX, aKernelUnitLengthY);
+ }
+ }
+ delete[] intKernel;
+
+ return target.forget();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
+{
+ if (aDestRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ IntMargin margin;
+ margin.left = ceil(mTarget.x * mKernelUnitLength.width);
+ margin.top = ceil(mTarget.y * mKernelUnitLength.height);
+ margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
+ margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
+
+ IntRect srcRect = aDestRect;
+ srcRect.Inflate(margin);
+ return srcRect;
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
+{
+ if (aSourceRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ IntMargin margin;
+ margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
+ margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
+ margin.right = ceil(mTarget.x * mKernelUnitLength.width);
+ margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
+
+ IntRect destRect = aSourceRect;
+ destRect.Inflate(margin);
+ return destRect;
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
+ return InflatedDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
+ : mScale(0.0f)
+ , mChannelX(COLOR_CHANNEL_R)
+ , mChannelY(COLOR_CHANNEL_G)
+{}
+
+int32_t
+FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_DISPLACEMENT_MAP_IN: return 0;
+ case IN_DISPLACEMENT_MAP_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
+ Float aScale)
+{
+ MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
+ mScale = aScale;
+ Invalidate();
+}
+
+void
+FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ switch (aIndex) {
+ case ATT_DISPLACEMENT_MAP_X_CHANNEL:
+ mChannelX = static_cast<ColorChannel>(aValue);
+ break;
+ case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
+ mChannelY = static_cast<ColorChannel>(aValue);
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = InflatedSourceOrDestRect(aRect);
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> map =
+ GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!(input && map && target))) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+ DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && targetMap.IsMapped()))) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, inputMap.GetMappedSurface(), offset);
+ int32_t sourceStride = inputMap.GetStride();
+ uint8_t* mapData = mapMap.GetData();
+ int32_t mapStride = mapMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ static const ptrdiff_t channelMap[4] = {
+ B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A };
+ uint16_t xChannel = channelMap[mChannelX];
+ uint16_t yChannel = channelMap[mChannelY];
+
+ float scaleOver255 = mScale / 255.0f;
+ float scaleAdjustment = -0.5f * mScale;
+
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ uint32_t mapIndex = y * mapStride + 4 * x;
+ uint32_t targIndex = y * targetStride + 4 * x;
+ int32_t sourceX = x +
+ scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
+ int32_t sourceY = y +
+ scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
+ *(uint32_t*)(targetData + targIndex) =
+ ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
+ }
+
+ // Keep valgrind happy.
+ PodZero(&targetData[y * targetStride + 4 * aRect.width], targetStride - 4 * aRect.width);
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
+ RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
+}
+
+IntRect
+FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
+{
+ IntRect sourceOrDestRect = aDestOrSourceRect;
+ sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
+ return sourceOrDestRect;
+}
+
+IntRect
+FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceOrDestRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
+ return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
+ : mNumOctaves(0)
+ , mSeed(0)
+ , mStitchable(false)
+ , mType(TURBULENCE_TYPE_TURBULENCE)
+{}
+
+int32_t
+FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ return -1;
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_BASE_FREQUENCY:
+ mBaseFrequency = aBaseFrequency;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ break;
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_RECT:
+ mRenderRect = aRect;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ break;
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
+{
+ MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
+ mStitchable = aStitchable;
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_NUM_OCTAVES:
+ mNumOctaves = aValue;
+ break;
+ case ATT_TURBULENCE_SEED:
+ mSeed = aValue;
+ break;
+ case ATT_TURBULENCE_TYPE:
+ mType = static_cast<TurbulenceType>(aValue);
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ break;
+ }
+ Invalidate();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
+{
+ return FilterProcessing::RenderTurbulence(
+ aRect.Size(), aRect.TopLeft(), mBaseFrequency,
+ mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
+}
+
+IntRect
+FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return aRect.Intersect(mRenderRect);
+}
+
+FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
+ : mK1(0), mK2(0), mK3(0), mK4(0)
+{
+}
+
+int32_t
+FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_ARITHMETIC_COMBINE_IN: return 0;
+ case IN_ARITHMETIC_COMBINE_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
+ MOZ_RELEASE_ASSERT(aSize == 4);
+
+ mK1 = aFloat[0];
+ mK2 = aFloat[1];
+ mK3 = aFloat[2];
+ mK4 = aFloat[3];
+
+ Invalidate();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input1 =
+ GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> input2 =
+ GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
+ if (!input1 && !input2) {
+ return nullptr;
+ }
+
+ // If one input is null, treat it as transparent by adjusting the factors.
+ Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
+ if (!input1) {
+ k1 = 0.0f;
+ k2 = 0.0f;
+ input1 = input2;
+ }
+
+ if (!input2) {
+ k1 = 0.0f;
+ k3 = 0.0f;
+ input2 = input1;
+ }
+
+ return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
+}
+
+void
+FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
+ RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
+}
+
+IntRect
+FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mK4 > 0.0f) {
+ return aRect;
+ }
+ IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
+ IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
+ IntRect result;
+ if (mK1 > 0.0f) {
+ result = rectFrom1.Intersect(rectFrom2);
+ }
+ if (mK2 > 0.0f) {
+ result = result.Union(rectFrom1);
+ }
+ if (mK3 > 0.0f) {
+ result = result.Union(rectFrom2);
+ }
+ return result;
+}
+
+FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
+ : mOperator(COMPOSITE_OPERATOR_OVER)
+{}
+
+int32_t
+FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ return aInputEnumIndex - IN_COMPOSITE_IN_START;
+}
+
+void
+FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
+{
+ MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
+ mOperator = static_cast<CompositeOperator>(aCompositeOperator);
+ Invalidate();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeCompositeSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> start =
+ GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> dest =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
+ if (MOZ2D_WARN_IF(!dest)) {
+ return nullptr;
+ }
+
+ if (start) {
+ CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
+ }
+
+ for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
+ if (input) {
+ FilterProcessing::ApplyComposition(input, dest, mOperator);
+ } else {
+ // We need to treat input as transparent. Depending on the composite
+ // operator, different things happen to dest.
+ switch (mOperator) {
+ case COMPOSITE_OPERATOR_OVER:
+ case COMPOSITE_OPERATOR_ATOP:
+ case COMPOSITE_OPERATOR_XOR:
+ // dest is unchanged.
+ break;
+ case COMPOSITE_OPERATOR_OUT:
+ // dest is now transparent, but it can become non-transparent again
+ // when compositing additional inputs.
+ ClearDataSourceSurface(dest);
+ break;
+ case COMPOSITE_OPERATOR_IN:
+ // Transparency always wins. We're completely transparent now and
+ // no additional input can get rid of that transparency.
+ return nullptr;
+ }
+ }
+ }
+ return dest.forget();
+}
+
+void
+FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
+ }
+}
+
+IntRect
+FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect rect;
+ for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
+ if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
+ rect = rect.Intersect(inputRect);
+ } else {
+ rect = rect.Union(inputRect);
+ }
+ }
+ return rect;
+}
+
+int32_t
+FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_GAUSSIAN_BLUR_IN: return 0;
+ default: return -1;
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
+{
+ Size sigmaXY = StdDeviationXY();
+ IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
+
+ if (d.width == 0 && d.height == 0) {
+ return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
+ }
+
+ IntRect srcRect = InflatedSourceOrDestRect(aRect);
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
+ if (!input) {
+ return nullptr;
+ }
+
+ RefPtr<DataSourceSurface> target;
+ Rect r(0, 0, srcRect.width, srcRect.height);
+
+ if (input->GetFormat() == SurfaceFormat::A8) {
+ target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+ CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
+
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+ AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(targetMap.GetData());
+ } else {
+ RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
+ FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
+ if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
+ return nullptr;
+ }
+ {
+ DataSourceSurface::ScopedMap channel0Map(channel0, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel1Map(channel1, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel2Map(channel2, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel3Map(channel3, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return nullptr;
+ }
+
+ AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(channel0Map.GetData());
+ blur.Blur(channel1Map.GetData());
+ blur.Blur(channel2Map.GetData());
+ blur.Blur(channel3Map.GetData());
+ }
+ target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
+ }
+
+ return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
+}
+
+void
+FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
+}
+
+IntRect
+FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
+{
+ Size sigmaXY = StdDeviationXY();
+ IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
+ IntRect srcRect = aDestRect;
+ srcRect.Inflate(d);
+ return srcRect;
+}
+
+IntRect
+FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceOrDestRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
+ return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
+ : mStdDeviation(0)
+{}
+
+static float
+ClampStdDeviation(float aStdDeviation)
+{
+ // Cap software blur radius for performance reasons.
+ return std::min(std::max(0.0f, aStdDeviation), 100.0f);
+}
+
+void
+FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
+ float aStdDeviation)
+{
+ switch (aIndex) {
+ case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
+ mStdDeviation = ClampStdDeviation(aStdDeviation);
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+Size
+FilterNodeGaussianBlurSoftware::StdDeviationXY()
+{
+ return Size(mStdDeviation, mStdDeviation);
+}
+
+FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
+ : mBlurDirection(BLUR_DIRECTION_X)
+{}
+
+void
+FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
+ Float aStdDeviation)
+{
+ switch (aIndex) {
+ case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
+ mStdDeviation = ClampStdDeviation(aStdDeviation);
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aBlurDirection)
+{
+ switch (aIndex) {
+ case ATT_DIRECTIONAL_BLUR_DIRECTION:
+ mBlurDirection = (BlurDirection)aBlurDirection;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+Size
+FilterNodeDirectionalBlurSoftware::StdDeviationXY()
+{
+ float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
+ float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
+ return Size(sigmaX, sigmaY);
+}
+
+int32_t
+FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_CROP_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
+ const Rect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_CROP_RECT);
+ Rect srcRect = aSourceRect;
+ srcRect.Round();
+ if (!srcRect.ToIntRect(&mCropRect)) {
+ mCropRect = IntRect();
+ }
+ Invalidate();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeCropSoftware::Render(const IntRect& aRect)
+{
+ return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
+}
+
+void
+FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
+}
+
+IntRect
+FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
+}
+
+int32_t
+FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_PREMULTIPLY_IN: return 0;
+ default: return -1;
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodePremultiplySoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
+ return input ? Premultiply(input) : nullptr;
+}
+
+void
+FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_PREMULTIPLY_IN, aRect);
+}
+
+IntRect
+FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
+}
+
+int32_t
+FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_UNPREMULTIPLY_IN: return 0;
+ default: return -1;
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
+ return input ? Unpremultiply(input) : nullptr;
+}
+
+void
+FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
+}
+
+IntRect
+FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
+}
+
+bool
+PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ switch (aIndex) {
+ case ATT_POINT_LIGHT_POSITION:
+ mPosition = aPoint;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+SpotLightSoftware::SpotLightSoftware()
+ : mSpecularFocus(0)
+ , mLimitingConeAngle(0)
+ , mLimitingConeCos(1)
+{
+}
+
+bool
+SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ switch (aIndex) {
+ case ATT_SPOT_LIGHT_POSITION:
+ mPosition = aPoint;
+ break;
+ case ATT_SPOT_LIGHT_POINTS_AT:
+ mPointsAt = aPoint;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool
+SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
+ mLimitingConeAngle = aValue;
+ break;
+ case ATT_SPOT_LIGHT_FOCUS:
+ mSpecularFocus = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+DistantLightSoftware::DistantLightSoftware()
+ : mAzimuth(0)
+ , mElevation(0)
+{
+}
+
+bool
+DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_DISTANT_LIGHT_AZIMUTH:
+ mAzimuth = aValue;
+ break;
+ case ATT_DISTANT_LIGHT_ELEVATION:
+ mElevation = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static inline Point3D Normalized(const Point3D &vec) {
+ Point3D copy(vec);
+ copy.Normalize();
+ return copy;
+}
+
+template<typename LightType, typename LightingType>
+FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
+ : mSurfaceScale(0)
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ , mTypeName(aTypeName)
+#endif
+{}
+
+template<typename LightType, typename LightingType>
+int32_t
+FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_LIGHTING_IN: return 0;
+ default: return -1;
+ }
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ if (mLight.SetAttribute(aIndex, aPoint)) {
+ Invalidate();
+ return;
+ }
+ MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ if (mLight.SetAttribute(aIndex, aValue) ||
+ mLighting.SetAttribute(aIndex, aValue)) {
+ Invalidate();
+ return;
+ }
+ switch (aIndex) {
+ case ATT_LIGHTING_SURFACE_SCALE:
+ mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
+ }
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
+{
+ switch (aIndex) {
+ case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
+ mKernelUnitLength = aKernelUnitLength;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
+ }
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
+{
+ MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
+ mColor = aColor;
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+IntRect
+FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
+{
+ return aRect;
+}
+
+Point3D
+PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return Normalized(mPosition - aTargetPoint);
+}
+
+uint32_t
+PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ return aLightColor;
+}
+
+void
+SpotLightSoftware::Prepare()
+{
+ mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
+ mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
+ mPowCache.CacheForExponent(mSpecularFocus);
+}
+
+Point3D
+SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return Normalized(mPosition - aTargetPoint);
+}
+
+uint32_t
+SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ union {
+ uint32_t color;
+ uint8_t colorC[4];
+ };
+
+ Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
+ if (!mPowCache.HasPowerTable()) {
+ dot *= (dot >= mLimitingConeCos);
+ color = aLightColor;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
+ } else {
+ color = aLightColor;
+ uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
+ uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
+ MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ }
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
+ return color;
+}
+
+void
+DistantLightSoftware::Prepare()
+{
+ const double radPerDeg = M_PI / 180.0;
+ mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
+ mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
+ mVectorToLight.z = sin(mElevation * radPerDeg);
+}
+
+Point3D
+DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return mVectorToLight;
+}
+
+uint32_t
+DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ return aLightColor;
+}
+
+template<typename CoordType>
+static Point3D
+GenerateNormal(const uint8_t *data, int32_t stride,
+ int32_t x, int32_t y, float surfaceScale,
+ CoordType dx, CoordType dy)
+{
+ const uint8_t *index = data + y * stride + x;
+
+ CoordType zero = 0;
+
+ // See this for source of constants:
+ // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
+ int16_t normalX =
+ -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
+ -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
+ 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
+ -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+
+ int16_t normalY =
+ -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
+ -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
+ -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
+ 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+
+ Point3D normal;
+ normal.x = -surfaceScale * normalX / 4.0f;
+ normal.y = -surfaceScale * normalY / 4.0f;
+ normal.z = 255;
+ return Normalized(normal);
+}
+
+template<typename LightType, typename LightingType>
+already_AddRefed<DataSourceSurface>
+FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
+{
+ if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
+ mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
+ return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
+ }
+ return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(ceil(mKernelUnitLength.width),
+ ceil(mKernelUnitLength.height));
+ RequestInputRect(IN_LIGHTING_IN, srcRect);
+}
+
+template<typename LightType, typename LightingType> template<typename CoordType>
+already_AddRefed<DataSourceSurface>
+FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ MOZ_ASSERT(aKernelUnitLengthX > 0, "aKernelUnitLengthX can be a negative or zero value");
+ MOZ_ASSERT(aKernelUnitLengthY > 0, "aKernelUnitLengthY can be a negative or zero value");
+
+ IntRect srcRect = aRect;
+ IntSize size = aRect.Size();
+ srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
+ ceil(float(aKernelUnitLengthY)));
+
+ // Inflate the source rect by another pixel because the bilinear filtering in
+ // ColorComponentAtPoint may want to access the margins.
+ srcRect.Inflate(1);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
+ EDGE_MODE_NONE);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ if (input->GetFormat() != SurfaceFormat::A8) {
+ input = FilterProcessing::ExtractAlpha(input);
+ }
+
+ DebugOnlyAutoColorSamplingAccessControl accessControl(input);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+
+ DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ uint32_t lightColor = ColorToBGRA(mColor);
+ mLight.Prepare();
+ mLighting.Prepare();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + x;
+ int32_t targetIndex = y * targetStride + 4 * x;
+
+ Point3D normal = GenerateNormal(sourceData, sourceStride,
+ x, y, mSurfaceScale,
+ aKernelUnitLengthX, aKernelUnitLengthY);
+
+ IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
+ Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
+ Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
+ Point3D rayDir = mLight.GetVectorToLight(pt);
+ uint32_t color = mLight.GetColor(lightColor, rayDir);
+
+ *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
+ }
+
+ // Zero padding to keep valgrind happy.
+ PodZero(&targetData[y * targetStride + 4 * size.width], targetStride - 4 * size.width);
+ }
+
+ return target.forget();
+}
+
+DiffuseLightingSoftware::DiffuseLightingSoftware()
+ : mDiffuseConstant(0)
+{
+}
+
+bool
+DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
+ mDiffuseConstant = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+uint32_t
+DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
+ const Point3D &aVectorToLight,
+ uint32_t aColor)
+{
+ Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
+ Float diffuseNL = mDiffuseConstant * dotNL;
+
+ union {
+ uint32_t bgra;
+ uint8_t components[4];
+ } color = { aColor };
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
+ return color.bgra;
+}
+
+SpecularLightingSoftware::SpecularLightingSoftware()
+ : mSpecularConstant(0)
+ , mSpecularExponent(0)
+ , mSpecularConstantInt(0)
+{
+}
+
+bool
+SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
+ mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
+ break;
+ case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
+ mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void
+SpecularLightingSoftware::Prepare()
+{
+ mPowCache.CacheForExponent(mSpecularExponent);
+ mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
+}
+
+uint32_t
+SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
+ const Point3D &aVectorToLight,
+ uint32_t aColor)
+{
+ Point3D vectorToEye(0, 0, 1);
+ Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
+ Float dotNH = aNormal.DotProduct(halfwayVector);
+ uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
+ // The exponent for specular is in [1,128] range, so we don't need to check and
+ // optimize for the "default power table" scenario here.
+ MOZ_ASSERT(mPowCache.HasPowerTable());
+ uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
+
+ union {
+ uint32_t bgra;
+ uint8_t components[4];
+ } color = { aColor };
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
+
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
+ umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
+ umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
+ return color.bgra;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/FilterNodeSoftware.h b/system/graphics/2d/FilterNodeSoftware.h
new file mode 100644
index 000000000..7ad27ec0f
--- /dev/null
+++ b/system/graphics/2d/FilterNodeSoftware.h
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_FILTERNODESOFTWARE_H_
+#define _MOZILLA_GFX_FILTERNODESOFTWARE_H_
+
+#include "Filters.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class DataSourceSurface;
+class DrawTarget;
+struct DrawOptions;
+class FilterNodeSoftware;
+
+/**
+ * Can be attached to FilterNodeSoftware instances using
+ * AddInvalidationListener. FilterInvalidated is called whenever the output of
+ * the observed filter may have changed; that is, whenever cached GetOutput()
+ * results (and results derived from them) need to discarded.
+ */
+class FilterInvalidationListener
+{
+public:
+ virtual void FilterInvalidated(FilterNodeSoftware* aFilter) = 0;
+};
+
+/**
+ * This is the base class for the software (i.e. pure CPU, non-accelerated)
+ * FilterNode implementation. The software implementation is backend-agnostic,
+ * so it can be used as a fallback for all DrawTarget implementations.
+ */
+class FilterNodeSoftware : public FilterNode,
+ public FilterInvalidationListener
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeSoftware, override)
+ virtual ~FilterNodeSoftware();
+
+ // Factory method, intended to be called from DrawTarget*::CreateFilter.
+ static already_AddRefed<FilterNode> Create(FilterType aType);
+
+ // Draw the filter, intended to be called by DrawTarget*::DrawFilter.
+ void Draw(DrawTarget* aDrawTarget, const Rect &aSourceRect,
+ const Point &aDestPoint, const DrawOptions &aOptions);
+
+ virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_SOFTWARE; }
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override;
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
+
+ virtual const char* GetName() { return "Unknown"; }
+
+ virtual void AddInvalidationListener(FilterInvalidationListener* aListener);
+ virtual void RemoveInvalidationListener(FilterInvalidationListener* aListener);
+
+ // FilterInvalidationListener implementation
+ virtual void FilterInvalidated(FilterNodeSoftware* aFilter) override;
+
+protected:
+
+ // The following methods are intended to be overriden by subclasses.
+
+ /**
+ * Translates a *FilterInputs enum value into an index for the
+ * mInputFilters / mInputSurfaces arrays. Returns -1 for invalid inputs.
+ * If somebody calls SetInput(enumValue, input) with an enumValue for which
+ * InputIndex(enumValue) is -1, we abort.
+ */
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) { return -1; }
+
+ /**
+ * Every filter node has an output rect, which can also be infinite. The
+ * output rect can depend on the values of any set attributes and on the
+ * output rects of any input filters or surfaces.
+ * This method returns the intersection of the filter's output rect with
+ * aInRect. Filters with unconstrained output always return aInRect.
+ */
+ virtual IntRect GetOutputRectInRect(const IntRect& aInRect) = 0;
+
+ /**
+ * Return a surface with the rendered output which is of size aRect.Size().
+ * aRect is required to be a subrect of this filter's output rect; in other
+ * words, aRect == GetOutputRectInRect(aRect) must always be true.
+ * May return nullptr in error conditions or for an empty aRect.
+ * Implementations are not required to allocate a new surface and may even
+ * pass through input surfaces unchanged.
+ * Callers need to treat the returned surface as immutable.
+ */
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) = 0;
+
+ /**
+ * Call RequestRect (see below) on any input filters with the desired input
+ * rect, so that the input filter knows what to cache the next time it
+ * renders.
+ */
+ virtual void RequestFromInputsForRect(const IntRect &aRect) {}
+
+ /**
+ * This method provides a caching default implementation but can be overriden
+ * by subclasses that don't want to cache their output. Those classes should
+ * call Render(aRect) directly from here.
+ */
+ virtual already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect);
+
+ // The following methods are non-virtual helper methods.
+
+ /**
+ * Format hints for GetInputDataSourceSurface. Some callers of
+ * GetInputDataSourceSurface can handle both B8G8R8A8 and A8 surfaces, these
+ * should pass CAN_HANDLE_A8 in order to avoid unnecessary conversions.
+ * Callers that can only handle B8G8R8A8 surfaces pass NEED_COLOR_CHANNELS.
+ */
+ enum FormatHint {
+ CAN_HANDLE_A8,
+ NEED_COLOR_CHANNELS
+ };
+
+ /**
+ * Returns SurfaceFormat::B8G8R8A8 or SurfaceFormat::A8, depending on the current surface
+ * format and the format hint.
+ */
+ SurfaceFormat DesiredFormat(SurfaceFormat aCurrentFormat,
+ FormatHint aFormatHint);
+
+ /**
+ * Intended to be called by FilterNodeSoftware::Render implementations.
+ * Returns a surface of size aRect.Size() or nullptr in error conditions. The
+ * returned surface contains the output of the specified input filter or
+ * input surface in aRect. If aRect extends beyond the input filter's output
+ * rect (or the input surface's dimensions), the remaining area is filled
+ * according to aEdgeMode: The default, EDGE_MODE_NONE, simply pads with
+ * transparent black.
+ * If non-null, the returned surface is guaranteed to be of SurfaceFormat::A8 or
+ * SurfaceFormat::B8G8R8A8. If aFormatHint is NEED_COLOR_CHANNELS, the returned
+ * surface is guaranteed to be of SurfaceFormat::B8G8R8A8 always.
+ * Each pixel row of the returned surface is guaranteed to be 16-byte aligned.
+ */
+ already_AddRefed<DataSourceSurface>
+ GetInputDataSourceSurface(uint32_t aInputEnumIndex, const IntRect& aRect,
+ FormatHint aFormatHint = CAN_HANDLE_A8,
+ ConvolveMatrixEdgeMode aEdgeMode = EDGE_MODE_NONE,
+ const IntRect *aTransparencyPaddedSourceRect = nullptr);
+
+ /**
+ * Returns the intersection of the input filter's or surface's output rect
+ * with aInRect.
+ */
+ IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect);
+
+ /**
+ * Calls RequestRect on the specified input, if it's a filter.
+ */
+ void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect);
+
+ /**
+ * Returns the number of set input filters or surfaces. Needed for filters
+ * which can have an arbitrary number of inputs.
+ */
+ size_t NumberOfSetInputs();
+
+ /**
+ * Discard the cached surface that was stored in the GetOutput default
+ * implementation. Needs to be called whenever attributes or inputs are set
+ * that might change the result of a Render() call.
+ */
+ void Invalidate();
+
+ /**
+ * Called in order to let this filter know what to cache during the next
+ * GetOutput call. Expected to call RequestRect on this filter's input
+ * filters.
+ */
+ void RequestRect(const IntRect &aRect);
+
+ /**
+ * Set input filter and clear input surface for this input index, or set
+ * input surface and clear input filter. One of aSurface and aFilter should
+ * be null.
+ */
+ void SetInput(uint32_t aIndex, SourceSurface *aSurface,
+ FilterNodeSoftware *aFilter);
+
+protected:
+ /**
+ * mInputSurfaces / mInputFilters: For each input index, either a surface or
+ * a filter is set, and the other is null.
+ */
+ std::vector<RefPtr<SourceSurface> > mInputSurfaces;
+ std::vector<RefPtr<FilterNodeSoftware> > mInputFilters;
+
+ /**
+ * Weak pointers to our invalidation listeners, i.e. to those filters who
+ * have this filter as an input. Invalidation listeners are required to
+ * unsubscribe themselves from us when they let go of their reference to us.
+ * This ensures that the pointers in this array are never stale.
+ */
+ std::vector<FilterInvalidationListener*> mInvalidationListeners;
+
+ /**
+ * Stores the rect which we want to render and cache on the next call to
+ * GetOutput.
+ */
+ IntRect mRequestedRect;
+
+ /**
+ * Stores our cached output.
+ */
+ IntRect mCachedRect;
+ RefPtr<DataSourceSurface> mCachedOutput;
+};
+
+// Subclasses for specific filters.
+
+class FilterNodeTransformSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override)
+ FilterNodeTransformSoftware();
+ virtual const char* GetName() override { return "Transform"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override;
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+ IntRect SourceRectForOutputRect(const IntRect &aRect);
+
+private:
+ Matrix mMatrix;
+ SamplingFilter mSamplingFilter;
+};
+
+class FilterNodeBlendSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlendSoftware, override)
+ FilterNodeBlendSoftware();
+ virtual const char* GetName() override { return "Blend"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ BlendMode mBlendMode;
+};
+
+class FilterNodeMorphologySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeMorphologySoftware, override)
+ FilterNodeMorphologySoftware();
+ virtual const char* GetName() override { return "Morphology"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntSize mRadii;
+ MorphologyOperator mOperator;
+};
+
+class FilterNodeColorMatrixSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeColorMatrixSoftware, override)
+ virtual const char* GetName() override { return "ColorMatrix"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ Matrix5x4 mMatrix;
+ AlphaMode mAlphaMode;
+};
+
+class FilterNodeFloodSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeFloodSoftware, override)
+ virtual const char* GetName() override { return "Flood"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Color &aColor) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect) override;
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+
+private:
+ Color mColor;
+};
+
+class FilterNodeTileSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTileSoftware, override)
+ virtual const char* GetName() override { return "Tile"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect mSourceRect;
+};
+
+/**
+ * Baseclass for the four different component transfer filters.
+ */
+class FilterNodeComponentTransferSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferSoftware, override)
+ FilterNodeComponentTransferSoftware();
+
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, bool aDisable) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+ virtual void GenerateLookupTable(ptrdiff_t aComponent, uint8_t aTables[4][256],
+ bool aDisabled);
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
+
+ bool mDisableR;
+ bool mDisableG;
+ bool mDisableB;
+ bool mDisableA;
+};
+
+class FilterNodeTableTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTableTransferSoftware, override)
+ virtual const char* GetName() override { return "TableTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
+
+ std::vector<Float> mTableR;
+ std::vector<Float> mTableG;
+ std::vector<Float> mTableB;
+ std::vector<Float> mTableA;
+};
+
+class FilterNodeDiscreteTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDiscreteTransferSoftware, override)
+ virtual const char* GetName() override { return "DiscreteTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
+
+ std::vector<Float> mTableR;
+ std::vector<Float> mTableG;
+ std::vector<Float> mTableB;
+ std::vector<Float> mTableA;
+};
+
+class FilterNodeLinearTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeLinearTransformSoftware, override)
+ FilterNodeLinearTransferSoftware();
+ virtual const char* GetName() override { return "LinearTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
+
+ Float mSlopeR;
+ Float mSlopeG;
+ Float mSlopeB;
+ Float mSlopeA;
+ Float mInterceptR;
+ Float mInterceptG;
+ Float mInterceptB;
+ Float mInterceptA;
+};
+
+class FilterNodeGammaTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGammaTransferSoftware, override)
+ FilterNodeGammaTransferSoftware();
+ virtual const char* GetName() override { return "GammaTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset, uint8_t aTable[256]);
+
+ Float mAmplitudeR;
+ Float mAmplitudeG;
+ Float mAmplitudeB;
+ Float mAmplitudeA;
+ Float mExponentR;
+ Float mExponentG;
+ Float mExponentB;
+ Float mExponentA;
+ Float mOffsetR;
+ Float mOffsetG;
+ Float mOffsetB;
+ Float mOffsetA;
+};
+
+class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveMatrixSoftware, override)
+ FilterNodeConvolveMatrixSoftware();
+ virtual const char* GetName() override { return "ConvolveMatrix"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aKernelSize) override;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aMatrix, uint32_t aSize) override;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override;
+ virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ template<typename CoordType>
+ already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY);
+
+ IntRect InflatedSourceRect(const IntRect &aDestRect);
+ IntRect InflatedDestRect(const IntRect &aSourceRect);
+
+ IntSize mKernelSize;
+ std::vector<Float> mKernelMatrix;
+ Float mDivisor;
+ Float mBias;
+ IntPoint mTarget;
+ IntRect mSourceRect;
+ ConvolveMatrixEdgeMode mEdgeMode;
+ Size mKernelUnitLength;
+ bool mPreserveAlpha;
+};
+
+class FilterNodeDisplacementMapSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDisplacementMapSoftware, override)
+ FilterNodeDisplacementMapSoftware();
+ virtual const char* GetName() override { return "DisplacementMap"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aScale) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect);
+
+ Float mScale;
+ ColorChannel mChannelX;
+ ColorChannel mChannelY;
+};
+
+class FilterNodeTurbulenceSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTurbulenceSoftware, override)
+ FilterNodeTurbulenceSoftware();
+ virtual const char* GetName() override { return "Turbulence"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Size &aSize) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aRenderRect) override;
+ virtual void SetAttribute(uint32_t aIndex, bool aStitchable) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+
+private:
+ IntRect mRenderRect;
+ Size mBaseFrequency;
+ uint32_t mNumOctaves;
+ uint32_t mSeed;
+ bool mStitchable;
+ TurbulenceType mType;
+};
+
+class FilterNodeArithmeticCombineSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeArithmeticCombineSoftware, override)
+ FilterNodeArithmeticCombineSoftware();
+ virtual const char* GetName() override { return "ArithmeticCombine"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ Float mK1;
+ Float mK2;
+ Float mK3;
+ Float mK4;
+};
+
+class FilterNodeCompositeSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCompositeSoftware, override)
+ FilterNodeCompositeSoftware();
+ virtual const char* GetName() override { return "Composite"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ CompositeOperator mOperator;
+};
+
+// Base class for FilterNodeGaussianBlurSoftware and
+// FilterNodeDirectionalBlurSoftware.
+class FilterNodeBlurXYSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override)
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ IntRect InflatedSourceOrDestRect(const IntRect &aDestRect);
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+ // Implemented by subclasses.
+ virtual Size StdDeviationXY() = 0;
+};
+
+class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGaussianBlurSoftware, override)
+ FilterNodeGaussianBlurSoftware();
+ virtual const char* GetName() override { return "GaussianBlur"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
+
+protected:
+ virtual Size StdDeviationXY() override;
+
+private:
+ Float mStdDeviation;
+};
+
+class FilterNodeDirectionalBlurSoftware : public FilterNodeBlurXYSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDirectionalBlurSoftware, override)
+ FilterNodeDirectionalBlurSoftware();
+ virtual const char* GetName() override { return "DirectionalBlur"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aBlurDirection) override;
+
+protected:
+ virtual Size StdDeviationXY() override;
+
+private:
+ Float mStdDeviation;
+ BlurDirection mBlurDirection;
+};
+
+class FilterNodeCropSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCropSoftware, override)
+ virtual const char* GetName() override { return "Crop"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect mCropRect;
+};
+
+class FilterNodePremultiplySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware, override)
+ virtual const char* GetName() override { return "Premultiply"; }
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+};
+
+class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware, override)
+ virtual const char* GetName() override { return "Unpremultiply"; }
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+};
+
+template<typename LightType, typename LightingType>
+class FilterNodeLightingSoftware : public FilterNodeSoftware
+{
+public:
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ // Helpers for refcounted
+ virtual const char* typeName() const override { return mTypeName; }
+ virtual size_t typeSize() const override { return sizeof(*this); }
+#endif
+ explicit FilterNodeLightingSoftware(const char* aTypeName);
+ virtual const char* GetName() override { return "Lighting"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float) override;
+ virtual void SetAttribute(uint32_t aIndex, const Size &) override;
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &) override;
+ virtual void SetAttribute(uint32_t aIndex, const Color &) override;
+
+protected:
+ virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ template<typename CoordType>
+ already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY);
+
+ LightType mLight;
+ LightingType mLighting;
+ Float mSurfaceScale;
+ Size mKernelUnitLength;
+ Color mColor;
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ const char* mTypeName;
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_FILTERNODESOFTWARE_H_
diff --git a/system/graphics/2d/FilterProcessing.cpp b/system/graphics/2d/FilterProcessing.cpp
new file mode 100644
index 000000000..da734c01a
--- /dev/null
+++ b/system/graphics/2d/FilterProcessing.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilterProcessing.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ExtractAlpha(DataSourceSurface* aSource)
+{
+ IntSize size = aSource->GetSize();
+ RefPtr<DataSourceSurface> alpha = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!alpha)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap alphaMap(alpha, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !alphaMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* alphaData = alphaMap.GetData();
+ int32_t alphaStride = alphaMap.GetStride();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ExtractAlpha_SSE2(size, sourceData, sourceStride, alphaData, alphaStride);
+#endif
+ } else {
+ ExtractAlpha_Scalar(size, sourceData, sourceStride, alphaData, alphaStride);
+ }
+
+ return alpha.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8(SourceSurface* aSurface)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ConvertToB8G8R8A8_SSE2(aSurface);
+#endif
+ }
+ return ConvertToB8G8R8A8_Scalar(aSurface);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyBlending_SSE2(aInput1, aInput2, aBlendMode);
+#endif
+ }
+ return nullptr;
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyMorphologyHorizontal_SSE2(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+#endif
+ } else {
+ ApplyMorphologyHorizontal_Scalar(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyMorphologyVertical_SSE2(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+#endif
+ } else {
+ ApplyMorphologyVertical_Scalar(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyColorMatrix_SSE2(aInput, aMatrix);
+#endif
+ }
+ return ApplyColorMatrix_Scalar(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyComposition_SSE2(aSource, aDest, aOperator);
+#endif
+ } else {
+ ApplyComposition_Scalar(aSource, aDest, aOperator);
+ }
+}
+
+void
+FilterProcessing::SeparateColorChannels(DataSourceSurface* aSource,
+ RefPtr<DataSourceSurface>& aChannel0,
+ RefPtr<DataSourceSurface>& aChannel1,
+ RefPtr<DataSourceSurface>& aChannel2,
+ RefPtr<DataSourceSurface>& aChannel3)
+{
+ IntSize size = aSource->GetSize();
+ aChannel0 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel1 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel2 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel3 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!(aChannel0 && aChannel1 && aChannel2 && aChannel3))) {
+ return;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() &&
+ channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return;
+ }
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* channel0Data = channel0Map.GetData();
+ uint8_t* channel1Data = channel1Map.GetData();
+ uint8_t* channel2Data = channel2Map.GetData();
+ uint8_t* channel3Data = channel3Map.GetData();
+ int32_t channelStride = channel0Map.GetStride();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ SeparateColorChannels_SSE2(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+#endif
+ } else {
+ SeparateColorChannels_Scalar(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
+ DataSourceSurface* aChannel2, DataSourceSurface* aChannel3)
+{
+ IntSize size = aChannel0->GetSize();
+ RefPtr<DataSourceSurface> result =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!result)) {
+ return nullptr;
+ }
+ DataSourceSurface::ScopedMap resultMap(result, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!(resultMap.IsMapped() &&
+ channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return nullptr;
+ }
+ int32_t resultStride = resultMap.GetStride();
+ uint8_t* resultData = resultMap.GetData();
+ int32_t channelStride = channel0Map.GetStride();
+ uint8_t* channel0Data = channel0Map.GetData();
+ uint8_t* channel1Data = channel1Map.GetData();
+ uint8_t* channel2Data = channel2Map.GetData();
+ uint8_t* channel3Data = channel3Map.GetData();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ CombineColorChannels_SSE2(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+#endif
+ } else {
+ CombineColorChannels_Scalar(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+ }
+
+ return result.forget();
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ DoPremultiplicationCalculation_SSE2(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+#endif
+ } else {
+ DoPremultiplicationCalculation_Scalar(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+ }
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ DoUnpremultiplicationCalculation_SSE2(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+#endif
+ } else {
+ DoUnpremultiplicationCalculation_Scalar(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return RenderTurbulence_SSE2(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+#endif
+ }
+ return RenderTurbulence_Scalar(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyArithmeticCombine_SSE2(aInput1, aInput2, aK1, aK2, aK3, aK4);
+#endif
+ }
+ return ApplyArithmeticCombine_Scalar(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/FilterProcessing.h b/system/graphics/2d/FilterProcessing.h
new file mode 100644
index 000000000..802d791a0
--- /dev/null
+++ b/system/graphics/2d/FilterProcessing.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_FILTERPROCESSING_H_
+#define _MOZILLA_GFX_FILTERPROCESSING_H_
+
+#include "2D.h"
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 0;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 1;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 2;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 3;
+
+class FilterProcessing
+{
+public:
+
+ // Fast approximate division by 255. It has the property that
+ // for all 0 <= v <= 255*255, FastDivideBy255(v) == v/255.
+ // But it only uses two adds and two shifts instead of an
+ // integer division (which is expensive on many processors).
+ template<class B, class A>
+ static B FastDivideBy255(A v)
+ {
+ return ((v << 8) + v + 255) >> 16;
+ }
+
+ static already_AddRefed<DataSourceSurface> ExtractAlpha(DataSourceSurface* aSource);
+ static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8(SourceSurface* aSurface);
+ static already_AddRefed<DataSourceSurface> ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
+ static void ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static already_AddRefed<DataSourceSurface> ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+ static void SeparateColorChannels(DataSourceSurface* aSource,
+ RefPtr<DataSourceSurface>& aChannel0,
+ RefPtr<DataSourceSurface>& aChannel1,
+ RefPtr<DataSourceSurface>& aChannel2,
+ RefPtr<DataSourceSurface>& aChannel3);
+ static already_AddRefed<DataSourceSurface>
+ CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
+ DataSourceSurface* aChannel2, DataSourceSurface* aChannel3);
+ static void DoPremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static already_AddRefed<DataSourceSurface>
+ RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static already_AddRefed<DataSourceSurface>
+ ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+
+protected:
+ static void ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
+ static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface);
+ static void ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static already_AddRefed<DataSourceSurface> ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+
+ static void SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
+ static void CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
+ static void DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static already_AddRefed<DataSourceSurface>
+ RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static already_AddRefed<DataSourceSurface>
+ ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+
+#ifdef USE_SSE2
+ static void ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
+ static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface);
+ static already_AddRefed<DataSourceSurface> ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
+ static void ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static already_AddRefed<DataSourceSurface> ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+ static void SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
+ static void CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
+ static void DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static already_AddRefed<DataSourceSurface>
+ RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static already_AddRefed<DataSourceSurface>
+ ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+#endif
+};
+
+// Constant-time max and min functions for unsigned arguments
+static inline unsigned
+umax(unsigned a, unsigned b)
+{
+ return a - ((a - b) & -(a < b));
+}
+
+static inline unsigned
+umin(unsigned a, unsigned b)
+{
+ return a - ((a - b) & -(a > b));
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_FILTERPROCESSING_H_
diff --git a/system/graphics/2d/FilterProcessingSIMD-inl.h b/system/graphics/2d/FilterProcessingSIMD-inl.h
new file mode 100644
index 000000000..3d21a4751
--- /dev/null
+++ b/system/graphics/2d/FilterProcessingSIMD-inl.h
@@ -0,0 +1,1081 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilterProcessing.h"
+
+#include "SIMD.h"
+#include "SVGTurbulenceRenderer-inl.h"
+
+namespace mozilla {
+namespace gfx {
+
+template<typename u8x16_t>
+inline already_AddRefed<DataSourceSurface>
+ConvertToB8G8R8A8_SIMD(SourceSurface* aSurface)
+{
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> input = aSurface->GetDataSurface();
+ RefPtr<DataSourceSurface> output =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ uint8_t *inputData = input->GetData();
+ uint8_t *outputData = output->GetData();
+ int32_t inputStride = input->Stride();
+ int32_t outputStride = output->Stride();
+ switch (input->GetFormat()) {
+ case SurfaceFormat::B8G8R8A8:
+ output = input;
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 0] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 2] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = 255;
+ }
+ }
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 2] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 0] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = inputData[inputIndex + 3];
+ }
+ }
+ break;
+ case SurfaceFormat::R8G8B8X8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 2] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 0] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = 255;
+ }
+ }
+ break;
+ case SurfaceFormat::A8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ int32_t inputIndex = y * inputStride + x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ u8x16_t p1To16 = simd::Load8<u8x16_t>(&inputData[inputIndex]);
+ // Turn AAAAAAAAAAAAAAAA into four chunks of 000A000A000A000A by
+ // interleaving with 0000000000000000 twice.
+ u8x16_t zero = simd::FromZero8<u8x16_t>();
+ u8x16_t p1To8 = simd::InterleaveLo8(zero, p1To16);
+ u8x16_t p9To16 = simd::InterleaveHi8(zero, p1To16);
+ u8x16_t p1To4 = simd::InterleaveLo8(zero, p1To8);
+ u8x16_t p5To8 = simd::InterleaveHi8(zero, p1To8);
+ u8x16_t p9To12 = simd::InterleaveLo8(zero, p9To16);
+ u8x16_t p13To16 = simd::InterleaveHi8(zero, p9To16);
+ simd::Store8(&outputData[outputIndex], p1To4);
+ if ((x + 4) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 4], p5To8);
+ }
+ if ((x + 8) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 8], p9To12);
+ }
+ if ((x + 12) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 12], p13To16);
+ }
+ }
+ }
+ break;
+ default:
+ output = nullptr;
+ break;
+ }
+ return output.forget();
+}
+
+template<typename u8x16_t>
+inline void
+ExtractAlpha_SIMD(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ // Turn up to four chunks of BGRABGRABGRABGRA into one chunk of AAAAAAAAAAAAAAAA.
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * alphaStride + x;
+
+ u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>();
+
+ bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ if (4 * (x + 4) < sourceStride) {
+ bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]);
+ }
+ if (4 * (x + 8) < sourceStride) {
+ bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]);
+ }
+ if (4 * (x + 12) < sourceStride) {
+ bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]);
+ }
+
+ u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+
+ simd::Store8(&alphaData[targetIndex], aaaaaaaaaaaaaaaa);
+ }
+ }
+}
+
+// This function calculates the result color values for four pixels, but for
+// only two color channels - either b & r or g & a. However, the a result will
+// not be used.
+// source and dest each contain 8 values, either bbbb gggg or rrrr aaaa.
+// sourceAlpha and destAlpha are of the form aaaa aaaa, where each aaaa is the
+// alpha of all four pixels (and both aaaa's are the same).
+// blendendComponent1 and blendedComponent2 are the out parameters.
+template<typename i16x8_t, typename i32x4_t, uint32_t aBlendMode>
+inline void
+BlendTwoComponentsOfFourPixels(i16x8_t source, i16x8_t sourceAlpha,
+ i16x8_t dest, const i16x8_t& destAlpha,
+ i32x4_t& blendedComponent1, i32x4_t& blendedComponent2)
+{
+ i16x8_t x255 = simd::FromI16<i16x8_t>(255);
+
+ switch (aBlendMode) {
+
+ case BLEND_MODE_MULTIPLY:
+ {
+ // val = ((255 - destAlpha) * source + (255 - sourceAlpha + source) * dest);
+ i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlphaPlusSource = simd::Add16(twoFiftyFiveMinusSourceAlpha, source);
+
+ i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest);
+ i16x8_t leftFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource);
+ blendedComponent1 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest1, leftFactor1);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest);
+ i16x8_t leftFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource);
+ blendedComponent2 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest2, leftFactor2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ case BLEND_MODE_SCREEN:
+ {
+ // val = 255 * (source + dest) + (0 - dest) * source;
+ i16x8_t sourcePlusDest = simd::Add16(source, dest);
+ i16x8_t zeroMinusDest = simd::Sub16(simd::FromI16<i16x8_t>(0), dest);
+
+ i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest1 = simd::InterleaveLo16(x255, zeroMinusDest);
+ i16x8_t sourcePlusDestInterleavedWithSource1 = simd::InterleaveLo16(sourcePlusDest, source);
+ blendedComponent1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest1, sourcePlusDestInterleavedWithSource1);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest2 = simd::InterleaveHi16(x255, zeroMinusDest);
+ i16x8_t sourcePlusDestInterleavedWithSource2 = simd::InterleaveHi16(sourcePlusDest, source);
+ blendedComponent2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest2, sourcePlusDestInterleavedWithSource2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ case BLEND_MODE_DARKEN:
+ case BLEND_MODE_LIGHTEN:
+ {
+ // Darken:
+ // val = min((255 - destAlpha) * source + 255 * dest,
+ // 255 * source + (255 - sourceAlpha) * dest);
+ //
+ // Lighten:
+ // val = max((255 - destAlpha) * source + 255 * dest,
+ // 255 * source + (255 - sourceAlpha) * dest);
+
+ i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, x255);
+ i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1 = simd::InterleaveLo16(x255, twoFiftyFiveMinusSourceAlpha);
+ i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest);
+ i32x4_t product1_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1, sourceInterleavedWithDest1);
+ i32x4_t product1_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1, sourceInterleavedWithDest1);
+ blendedComponent1 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product1_1, product1_2) : simd::Max32(product1_1, product1_2);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, x255);
+ i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2 = simd::InterleaveHi16(x255, twoFiftyFiveMinusSourceAlpha);
+ i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest);
+ i32x4_t product2_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2, sourceInterleavedWithDest2);
+ i32x4_t product2_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2, sourceInterleavedWithDest2);
+ blendedComponent2 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product2_1, product2_2) : simd::Max32(product2_1, product2_2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ }
+}
+
+// The alpha channel is subject to a different calculation than the RGB
+// channels, and this calculation is the same for all blend modes:
+// resultAlpha * 255 = 255 * 255 - (255 - sourceAlpha) * (255 - destAlpha)
+template<typename i16x8_t, typename i32x4_t>
+inline i32x4_t
+BlendAlphaOfFourPixels(i16x8_t s_rrrraaaa1234, i16x8_t d_rrrraaaa1234)
+{
+ // We're using MulAdd16x8x2To32x4, so we need to interleave our factors
+ // appropriately. The calculation is rewritten as follows:
+ // resultAlpha[0] * 255 = 255 * 255 - (255 - sourceAlpha[0]) * (255 - destAlpha[0])
+ // = 255 * 255 + (255 - sourceAlpha[0]) * (destAlpha[0] - 255)
+ // = (255 - 0) * (510 - 255) + (255 - sourceAlpha[0]) * (destAlpha[0] - 255)
+ // = MulAdd(255 - IntLv(0, sourceAlpha), IntLv(510, destAlpha) - 255)[0]
+ i16x8_t zeroInterleavedWithSourceAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(0), s_rrrraaaa1234);
+ i16x8_t fiveTenInterleavedWithDestAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(510), d_rrrraaaa1234);
+ i16x8_t f1 = simd::Sub16(simd::FromI16<i16x8_t>(255), zeroInterleavedWithSourceAlpha);
+ i16x8_t f2 = simd::Sub16(fiveTenInterleavedWithDestAlpha, simd::FromI16<i16x8_t>(255));
+ return simd::FastDivideBy255(simd::MulAdd16x8x2To32x4(f1, f2));
+}
+
+template<typename u8x16_t, typename i16x8_t>
+inline void
+UnpackAndShuffleComponents(u8x16_t bgrabgrabgrabgra1234,
+ i16x8_t& bbbbgggg1234, i16x8_t& rrrraaaa1234)
+{
+ // bgrabgrabgrabgra1234 -> bbbbgggg1234, rrrraaaa1234
+ i16x8_t bgrabgra12 = simd::UnpackLo8x8ToI16x8(bgrabgrabgrabgra1234);
+ i16x8_t bgrabgra34 = simd::UnpackHi8x8ToI16x8(bgrabgrabgrabgra1234);
+ i16x8_t bbggrraa13 = simd::InterleaveLo16(bgrabgra12, bgrabgra34);
+ i16x8_t bbggrraa24 = simd::InterleaveHi16(bgrabgra12, bgrabgra34);
+ bbbbgggg1234 = simd::InterleaveLo16(bbggrraa13, bbggrraa24);
+ rrrraaaa1234 = simd::InterleaveHi16(bbggrraa13, bbggrraa24);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+inline u8x16_t
+ShuffleAndPackComponents(i32x4_t bbbb1234, i32x4_t gggg1234,
+ i32x4_t rrrr1234, const i32x4_t& aaaa1234)
+{
+ // bbbb1234, gggg1234, rrrr1234, aaaa1234 -> bgrabgrabgrabgra1234
+ i16x8_t bbbbgggg1234 = simd::PackAndSaturate32To16(bbbb1234, gggg1234);
+ i16x8_t rrrraaaa1234 = simd::PackAndSaturate32To16(rrrr1234, aaaa1234);
+ i16x8_t brbrbrbr1234 = simd::InterleaveLo16(bbbbgggg1234, rrrraaaa1234);
+ i16x8_t gagagaga1234 = simd::InterleaveHi16(bbbbgggg1234, rrrraaaa1234);
+ i16x8_t bgrabgra12 = simd::InterleaveLo16(brbrbrbr1234, gagagaga1234);
+ i16x8_t bgrabgra34 = simd::InterleaveHi16(brbrbrbr1234, gagagaga1234);
+ return simd::PackAndSaturate16To8(bgrabgra12, bgrabgra34);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t, BlendMode mode>
+inline already_AddRefed<DataSourceSurface>
+ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2)
+{
+ IntSize size = aInput1->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* source1Data = aInput1->GetData();
+ uint8_t* source2Data = aInput2->GetData();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+ int32_t source1Stride = aInput1->Stride();
+ int32_t source2Stride = aInput2->Stride();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ int32_t targetIndex = y * targetStride + 4 * x;
+ int32_t source1Index = y * source1Stride + 4 * x;
+ int32_t source2Index = y * source2Stride + 4 * x;
+
+ u8x16_t s1234 = simd::Load8<u8x16_t>(&source2Data[source2Index]);
+ u8x16_t d1234 = simd::Load8<u8x16_t>(&source1Data[source1Index]);
+
+ // The blending calculation for the RGB channels all need access to the
+ // alpha channel of their pixel, and the alpha calculation is different,
+ // so it makes sense to separate by channel.
+
+ i16x8_t s_bbbbgggg1234, s_rrrraaaa1234;
+ i16x8_t d_bbbbgggg1234, d_rrrraaaa1234;
+ UnpackAndShuffleComponents(s1234, s_bbbbgggg1234, s_rrrraaaa1234);
+ UnpackAndShuffleComponents(d1234, d_bbbbgggg1234, d_rrrraaaa1234);
+ i16x8_t s_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(s_rrrraaaa1234);
+ i16x8_t d_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(d_rrrraaaa1234);
+
+ // We only use blendedB, blendedG and blendedR.
+ i32x4_t blendedB, blendedG, blendedR, blendedA;
+ BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_bbbbgggg1234, s_aaaaaaaa1234, d_bbbbgggg1234, d_aaaaaaaa1234, blendedB, blendedG);
+ BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_rrrraaaa1234, s_aaaaaaaa1234, d_rrrraaaa1234, d_aaaaaaaa1234, blendedR, blendedA);
+
+ // Throw away blendedA and overwrite it with the correct blended alpha.
+ blendedA = BlendAlphaOfFourPixels<i16x8_t,i32x4_t>(s_rrrraaaa1234, d_rrrraaaa1234);
+
+ u8x16_t result1234 = ShuffleAndPackComponents<i32x4_t,i16x8_t,u8x16_t>(blendedB, blendedG, blendedR, blendedA);
+ simd::Store8(&targetData[targetIndex], result1234);
+ }
+ }
+
+ return target.forget();
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static already_AddRefed<DataSourceSurface>
+ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ switch (aBlendMode) {
+ case BLEND_MODE_MULTIPLY:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_MULTIPLY>(aInput1, aInput2);
+ case BLEND_MODE_SCREEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_SCREEN>(aInput1, aInput2);
+ case BLEND_MODE_DARKEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_DARKEN>(aInput1, aInput2);
+ case BLEND_MODE_LIGHTEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_LIGHTEN>(aInput1, aInput2);
+ default:
+ return nullptr;
+ }
+}
+
+template<MorphologyOperator Operator, typename u8x16_t>
+static u8x16_t
+Morph8(u8x16_t a, u8x16_t b)
+{
+ return Operator == MORPHOLOGY_OPERATOR_ERODE ?
+ simd::Min8(a, b) : simd::Max8(a, b);
+}
+
+// Set every pixel to the per-component minimum or maximum of the pixels around
+// it that are up to aRadius pixels away from it (horizontally).
+template<MorphologyOperator op, typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(op == MORPHOLOGY_OPERATOR_ERODE ||
+ op == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t kernelSize = aRadius + 1 + aRadius;
+ MOZ_ASSERT(kernelSize >= 3, "don't call this with aRadius <= 0");
+ MOZ_ASSERT(kernelSize % 4 == 1 || kernelSize % 4 == 3);
+ int32_t completeKernelSizeForFourPixels = kernelSize + 3;
+ MOZ_ASSERT(completeKernelSizeForFourPixels % 4 == 0 ||
+ completeKernelSizeForFourPixels % 4 == 2);
+
+ // aSourceData[-aRadius] and aDestData[0] are both aligned to 16 bytes, just
+ // the way we need them to be.
+
+ IntRect sourceRect = aDestRect;
+ sourceRect.Inflate(aRadius, 0);
+
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) {
+ int32_t kernelStartX = aDestRect.x - aRadius;
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4, kernelStartX += 4) {
+ // We process four pixels (16 color values) at a time.
+ // aSourceData[0] points to the pixel located at aDestRect.TopLeft();
+ // source values can be read beyond that because the source is extended
+ // by aRadius pixels.
+
+ int32_t sourceIndex = y * aSourceStride + 4 * kernelStartX;
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ u8x16_t m1234 = p1234;
+
+ for (int32_t i = 4; i < completeKernelSizeForFourPixels; i += 4) {
+ u8x16_t p5678 = (kernelStartX + i < sourceRect.XMost()) ?
+ simd::Load8<u8x16_t>(&aSourceData[sourceIndex + 4 * i]) :
+ simd::FromZero8<u8x16_t>();
+ u8x16_t p2345 = simd::Rotate8<4>(p1234, p5678);
+ u8x16_t p3456 = simd::Rotate8<8>(p1234, p5678);
+ m1234 = Morph8<op,u8x16_t>(m1234, p2345);
+ m1234 = Morph8<op,u8x16_t>(m1234, p3456);
+ if (i + 2 < completeKernelSizeForFourPixels) {
+ u8x16_t p4567 = simd::Rotate8<12>(p1234, p5678);
+ m1234 = Morph8<op,u8x16_t>(m1234, p4567);
+ m1234 = Morph8<op,u8x16_t>(m1234, p5678);
+ }
+ p1234 = p5678;
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ simd::Store8(&aDestData[destIndex], m1234);
+ }
+ }
+}
+
+template<typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+// Set every pixel to the per-component minimum or maximum of the pixels around
+// it that are up to aRadius pixels away from it (vertically).
+template<MorphologyOperator op, typename i16x8_t, typename u8x16_t>
+static void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(op == MORPHOLOGY_OPERATOR_ERODE ||
+ op == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t startY = aDestRect.y - aRadius;
+ int32_t endY = aDestRect.y + aRadius;
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) {
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4) {
+ int32_t sourceIndex = startY * aSourceStride + 4 * x;
+ u8x16_t u = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ sourceIndex += aSourceStride;
+ for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) {
+ u8x16_t u2 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ u = Morph8<op,u8x16_t>(u, u2);
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ simd::Store8(&aDestData[destIndex], u);
+ }
+ }
+}
+
+template<typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+template<typename i32x4_t, typename i16x8_t>
+static i32x4_t
+ColorMatrixMultiply(i16x8_t p, i16x8_t rows_bg, i16x8_t rows_ra, const i32x4_t& bias)
+{
+ // int16_t p[8] == { b, g, r, a, b, g, r, a }.
+ // int16_t rows_bg[8] == { bB, bG, bR, bA, gB, gG, gR, gA }.
+ // int16_t rows_ra[8] == { rB, rG, rR, rA, aB, aG, aR, aA }.
+ // int32_t bias[4] == { _B, _G, _R, _A }.
+
+ i32x4_t sum = bias;
+
+ // int16_t bg[8] = { b, g, b, g, b, g, b, g };
+ i16x8_t bg = simd::ShuffleHi16<1,0,1,0>(simd::ShuffleLo16<1,0,1,0>(p));
+ // int32_t prodsum_bg[4] = { b * bB + g * gB, b * bG + g * gG, b * bR + g * gR, b * bA + g * gA }
+ i32x4_t prodsum_bg = simd::MulAdd16x8x2To32x4(bg, rows_bg);
+ sum = simd::Add32(sum, prodsum_bg);
+
+ // uint16_t ra[8] = { r, a, r, a, r, a, r, a };
+ i16x8_t ra = simd::ShuffleHi16<3,2,3,2>(simd::ShuffleLo16<3,2,3,2>(p));
+ // int32_t prodsum_ra[4] = { r * rB + a * aB, r * rG + a * aG, r * rR + a * aR, r * rA + a * aA }
+ i32x4_t prodsum_ra = simd::MulAdd16x8x2To32x4(ra, rows_ra);
+ sum = simd::Add32(sum, prodsum_ra);
+
+ // int32_t sum[4] == { b * bB + g * gB + r * rB + a * aB + _B, ... }.
+ return sum;
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static already_AddRefed<DataSourceSurface>
+ApplyColorMatrix_SIMD(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ IntSize size = aInput->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = aInput->GetData();
+ uint8_t* targetData = target->GetData();
+ int32_t sourceStride = aInput->Stride();
+ int32_t targetStride = target->Stride();
+
+ const int16_t factor = 128;
+ const Float floatElementMax = INT16_MAX / factor; // 255
+ MOZ_ASSERT((floatElementMax * factor) <= INT16_MAX, "badly chosen float-to-int scale");
+
+ const Float *floats = &aMatrix._11;
+
+ ptrdiff_t componentOffsets[4] = {
+ B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A
+ };
+
+ // We store the color matrix in rows_bgra in the following format:
+ // { bB, bG, bR, bA, gB, gG, gR, gA }.
+ // { bB, gB, bG, gG, bR, gR, bA, gA }
+ // The way this is interleaved allows us to use the intrinsic _mm_madd_epi16
+ // which works especially well for our use case.
+ int16_t rows_bgra[2][8];
+ for (size_t rowIndex = 0; rowIndex < 4; rowIndex++) {
+ for (size_t colIndex = 0; colIndex < 4; colIndex++) {
+ const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex];
+ Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -floatElementMax), floatElementMax);
+ int16_t scaledIntMatrixElement = int16_t(clampedFloatMatrixElement * factor + 0.5);
+ int8_t bg_or_ra = componentOffsets[rowIndex] / 2;
+ int8_t g_or_a = componentOffsets[rowIndex] % 2;
+ int8_t B_or_G_or_R_or_A = componentOffsets[colIndex];
+ rows_bgra[bg_or_ra][B_or_G_or_R_or_A * 2 + g_or_a] = scaledIntMatrixElement;
+ }
+ }
+
+ int32_t rowBias[4];
+ Float biasMax = (INT32_MAX - 4 * 255 * INT16_MAX) / (factor * 255);
+ for (size_t colIndex = 0; colIndex < 4; colIndex++) {
+ size_t rowIndex = 4;
+ const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex];
+ Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -biasMax), biasMax);
+ int32_t scaledIntMatrixElement = int32_t(clampedFloatMatrixElement * factor * 255 + 0.5);
+ rowBias[componentOffsets[colIndex]] = scaledIntMatrixElement;
+ }
+
+ i16x8_t row_bg_v = simd::FromI16<i16x8_t>(
+ rows_bgra[0][0], rows_bgra[0][1], rows_bgra[0][2], rows_bgra[0][3],
+ rows_bgra[0][4], rows_bgra[0][5], rows_bgra[0][6], rows_bgra[0][7]);
+
+ i16x8_t row_ra_v = simd::FromI16<i16x8_t>(
+ rows_bgra[1][0], rows_bgra[1][1], rows_bgra[1][2], rows_bgra[1][3],
+ rows_bgra[1][4], rows_bgra[1][5], rows_bgra[1][6], rows_bgra[1][7]);
+
+ i32x4_t rowsBias_v =
+ simd::From32<i32x4_t>(rowBias[0], rowBias[1], rowBias[2], rowBias[3]);
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ MOZ_ASSERT(sourceStride >= 4 * (x + 4), "need to be able to read 4 pixels at this position");
+ MOZ_ASSERT(targetStride >= 4 * (x + 4), "need to be able to write 4 pixels at this position");
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * targetStride + 4 * x;
+
+ // We load 4 pixels, unpack them, process them 1 pixel at a time, and
+ // finally pack and store the 4 result pixels.
+
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+
+ // Splat needed to get each pixel twice into i16x8
+ i16x8_t p11 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<0>(p1234));
+ i16x8_t p22 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<1>(p1234));
+ i16x8_t p33 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<2>(p1234));
+ i16x8_t p44 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<3>(p1234));
+
+ i32x4_t result_p1 = ColorMatrixMultiply(p11, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p2 = ColorMatrixMultiply(p22, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p3 = ColorMatrixMultiply(p33, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p4 = ColorMatrixMultiply(p44, row_bg_v, row_ra_v, rowsBias_v);
+
+ static_assert(factor == 1 << 7, "Please adapt the calculation in the lines below for a different factor.");
+ u8x16_t result_p1234 = simd::PackAndSaturate32To8(simd::ShiftRight32<7>(result_p1),
+ simd::ShiftRight32<7>(result_p2),
+ simd::ShiftRight32<7>(result_p3),
+ simd::ShiftRight32<7>(result_p4));
+ simd::Store8(&targetData[targetIndex], result_p1234);
+ }
+ }
+
+ return target.forget();
+}
+
+// source / dest: bgra bgra
+// sourceAlpha / destAlpha: aaaa aaaa
+// result: bgra bgra
+template<typename i32x4_t, typename u16x8_t, uint32_t aCompositeOperator>
+static inline u16x8_t
+CompositeTwoPixels(u16x8_t source, u16x8_t sourceAlpha, u16x8_t dest, const u16x8_t& destAlpha)
+{
+ u16x8_t x255 = simd::FromU16<u16x8_t>(255);
+
+ switch (aCompositeOperator) {
+
+ case COMPOSITE_OPERATOR_OVER:
+ {
+ // val = dest * (255 - sourceAlpha) + source * 255;
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, x255);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, x255);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ case COMPOSITE_OPERATOR_IN:
+ {
+ // val = source * destAlpha;
+ return simd::FastDivideBy255_16(simd::Mul16(source, destAlpha));
+ }
+
+ case COMPOSITE_OPERATOR_OUT:
+ {
+ // val = source * (255 - destAlpha);
+ u16x8_t prod = simd::Mul16(source, simd::Sub16(x255, destAlpha));
+ return simd::FastDivideBy255_16(prod);
+ }
+
+ case COMPOSITE_OPERATOR_ATOP:
+ {
+ // val = dest * (255 - sourceAlpha) + source * destAlpha;
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, destAlpha);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, destAlpha);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ case COMPOSITE_OPERATOR_XOR:
+ {
+ // val = dest * (255 - sourceAlpha) + source * (255 - destAlpha);
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+ u16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha,
+ twoFiftyFiveMinusDestAlpha);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha,
+ twoFiftyFiveMinusDestAlpha);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ default:
+ return simd::FromU16<u16x8_t>(0);
+
+ }
+}
+
+template<typename i32x4_t, typename u16x8_t, typename u8x16_t, uint32_t op>
+static void
+ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest)
+{
+ IntSize size = aDest->GetSize();
+
+ uint8_t* sourceData = aSource->GetData();
+ uint8_t* destData = aDest->GetData();
+ uint32_t sourceStride = aSource->Stride();
+ uint32_t destStride = aDest->Stride();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ uint32_t sourceIndex = y * sourceStride + 4 * x;
+ uint32_t destIndex = y * destStride + 4 * x;
+
+ u8x16_t s1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ u8x16_t d1234 = simd::Load8<u8x16_t>(&destData[destIndex]);
+
+ u16x8_t s12 = simd::UnpackLo8x8ToU16x8(s1234);
+ u16x8_t d12 = simd::UnpackLo8x8ToU16x8(d1234);
+ u16x8_t sa12 = simd::Splat16<3,3>(s12);
+ u16x8_t da12 = simd::Splat16<3,3>(d12);
+ u16x8_t result12 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s12, sa12, d12, da12);
+
+ u16x8_t s34 = simd::UnpackHi8x8ToU16x8(s1234);
+ u16x8_t d34 = simd::UnpackHi8x8ToU16x8(d1234);
+ u16x8_t sa34 = simd::Splat16<3,3>(s34);
+ u16x8_t da34 = simd::Splat16<3,3>(d34);
+ u16x8_t result34 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s34, sa34, d34, da34);
+
+ u8x16_t result1234 = simd::PackAndSaturate16To8(result12, result34);
+ simd::Store8(&destData[destIndex], result1234);
+ }
+ }
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static void
+ApplyComposition_SIMD(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ switch (aOperator) {
+ case COMPOSITE_OPERATOR_OVER:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OVER>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_IN:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_IN>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_OUT:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OUT>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_ATOP:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_ATOP>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_XOR:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_XOR>(aSource, aDest);
+ break;
+ default:
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+}
+
+template<typename u8x16_t>
+static void
+SeparateColorChannels_SIMD(const IntSize &size, uint8_t* sourceData, int32_t sourceStride,
+ uint8_t* channel0Data, uint8_t* channel1Data,
+ uint8_t* channel2Data, uint8_t* channel3Data,
+ int32_t channelStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * channelStride + x;
+
+ u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>();
+
+ bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ if (4 * (x + 4) < sourceStride) {
+ bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]);
+ }
+ if (4 * (x + 8) < sourceStride) {
+ bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]);
+ }
+ if (4 * (x + 12) < sourceStride) {
+ bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]);
+ }
+
+ u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbbbbbgggggggg1 = simd::InterleaveLo8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t bbbbbbbbgggggggg2 = simd::InterleaveLo8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t bbbbbbbbbbbbbbbb = simd::InterleaveLo8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2);
+ u8x16_t gggggggggggggggg = simd::InterleaveHi8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2);
+ u8x16_t rrrrrrrrrrrrrrrr = simd::InterleaveLo8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+
+ simd::Store8(&channel0Data[targetIndex], bbbbbbbbbbbbbbbb);
+ simd::Store8(&channel1Data[targetIndex], gggggggggggggggg);
+ simd::Store8(&channel2Data[targetIndex], rrrrrrrrrrrrrrrr);
+ simd::Store8(&channel3Data[targetIndex], aaaaaaaaaaaaaaaa);
+ }
+ }
+}
+
+template<typename u8x16_t>
+static void
+CombineColorChannels_SIMD(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ int32_t resultIndex = y * resultStride + 4 * x;
+ int32_t channelIndex = y * channelStride + x;
+
+ u8x16_t bbbbbbbbbbbbbbbb = simd::Load8<u8x16_t>(&channel0Data[channelIndex]);
+ u8x16_t gggggggggggggggg = simd::Load8<u8x16_t>(&channel1Data[channelIndex]);
+ u8x16_t rrrrrrrrrrrrrrrr = simd::Load8<u8x16_t>(&channel2Data[channelIndex]);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::Load8<u8x16_t>(&channel3Data[channelIndex]);
+
+ u8x16_t brbrbrbrbrbrbrbr1 = simd::InterleaveLo8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr);
+ u8x16_t brbrbrbrbrbrbrbr2 = simd::InterleaveHi8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr);
+ u8x16_t gagagagagagagaga1 = simd::InterleaveLo8(gggggggggggggggg, aaaaaaaaaaaaaaaa);
+ u8x16_t gagagagagagagaga2 = simd::InterleaveHi8(gggggggggggggggg, aaaaaaaaaaaaaaaa);
+
+ u8x16_t bgrabgrabgrabgra1 = simd::InterleaveLo8(brbrbrbrbrbrbrbr1, gagagagagagagaga1);
+ u8x16_t bgrabgrabgrabgra2 = simd::InterleaveHi8(brbrbrbrbrbrbrbr1, gagagagagagagaga1);
+ u8x16_t bgrabgrabgrabgra3 = simd::InterleaveLo8(brbrbrbrbrbrbrbr2, gagagagagagagaga2);
+ u8x16_t bgrabgrabgrabgra4 = simd::InterleaveHi8(brbrbrbrbrbrbrbr2, gagagagagagagaga2);
+
+ simd::Store8(&resultData[resultIndex], bgrabgrabgrabgra1);
+ if (4 * (x + 4) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 4 * 4], bgrabgrabgrabgra2);
+ }
+ if (4 * (x + 8) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 8 * 4], bgrabgrabgrabgra3);
+ }
+ if (4 * (x + 12) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 12 * 4], bgrabgrabgrabgra4);
+ }
+ }
+ }
+}
+
+
+template<typename i32x4_t, typename u16x8_t, typename u8x16_t>
+static void
+DoPremultiplicationCalculation_SIMD(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ const u8x16_t alphaMask = simd::From8<u8x16_t>(0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff);
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]);
+ u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234);
+ u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234);
+
+ // Multiply all components with alpha.
+ p12 = simd::Mul16(p12, simd::Splat16<3,3>(p12));
+ p34 = simd::Mul16(p34, simd::Splat16<3,3>(p34));
+
+ // Divide by 255 and pack.
+ u8x16_t result = simd::PackAndSaturate16To8(simd::FastDivideBy255_16(p12),
+ simd::FastDivideBy255_16(p34));
+
+ // Get the original alpha channel value back from p1234.
+ result = simd::Pick(alphaMask, result, p1234);
+
+ simd::Store8(&aTargetData[targetIndex], result);
+ }
+ }
+}
+
+// We use a table of precomputed factors for unpremultiplying.
+// We want to compute round(r / (alpha / 255.0f)) for arbitrary values of
+// r and alpha in constant time. This table of factors has the property that
+// (r * sAlphaFactors[alpha] + 128) >> 8 roughly gives the result we want (with
+// a maximum deviation of 1).
+//
+// sAlphaFactors[alpha] == round(255.0 * (1 << 8) / alpha)
+//
+// This table has been created using the python code
+// ", ".join("%d" % (round(255.0 * 256 / alpha) if alpha > 0 else 0) for alpha in range(256))
+static const uint16_t sAlphaFactors[256] = {
+ 0, 65280, 32640, 21760, 16320, 13056, 10880, 9326, 8160, 7253, 6528, 5935,
+ 5440, 5022, 4663, 4352, 4080, 3840, 3627, 3436, 3264, 3109, 2967, 2838, 2720,
+ 2611, 2511, 2418, 2331, 2251, 2176, 2106, 2040, 1978, 1920, 1865, 1813, 1764,
+ 1718, 1674, 1632, 1592, 1554, 1518, 1484, 1451, 1419, 1389, 1360, 1332, 1306,
+ 1280, 1255, 1232, 1209, 1187, 1166, 1145, 1126, 1106, 1088, 1070, 1053, 1036,
+ 1020, 1004, 989, 974, 960, 946, 933, 919, 907, 894, 882, 870, 859, 848, 837,
+ 826, 816, 806, 796, 787, 777, 768, 759, 750, 742, 733, 725, 717, 710, 702,
+ 694, 687, 680, 673, 666, 659, 653, 646, 640, 634, 628, 622, 616, 610, 604,
+ 599, 593, 588, 583, 578, 573, 568, 563, 558, 553, 549, 544, 540, 535, 531,
+ 526, 522, 518, 514, 510, 506, 502, 498, 495, 491, 487, 484, 480, 476, 473,
+ 470, 466, 463, 460, 457, 453, 450, 447, 444, 441, 438, 435, 432, 429, 427,
+ 424, 421, 418, 416, 413, 411, 408, 405, 403, 400, 398, 396, 393, 391, 389,
+ 386, 384, 382, 380, 377, 375, 373, 371, 369, 367, 365, 363, 361, 359, 357,
+ 355, 353, 351, 349, 347, 345, 344, 342, 340, 338, 336, 335, 333, 331, 330,
+ 328, 326, 325, 323, 322, 320, 318, 317, 315, 314, 312, 311, 309, 308, 306,
+ 305, 304, 302, 301, 299, 298, 297, 295, 294, 293, 291, 290, 289, 288, 286,
+ 285, 284, 283, 281, 280, 279, 278, 277, 275, 274, 273, 272, 271, 270, 269,
+ 268, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256
+};
+
+template<typename u16x8_t, typename u8x16_t>
+static void
+DoUnpremultiplicationCalculation_SIMD(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ union {
+ u8x16_t p1234;
+ uint8_t u8[4][4];
+ };
+ p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]);
+
+ // Prepare the alpha factors.
+ uint16_t aF1 = sAlphaFactors[u8[0][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF2 = sAlphaFactors[u8[1][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF3 = sAlphaFactors[u8[2][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF4 = sAlphaFactors[u8[3][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ u16x8_t aF12 = simd::FromU16<u16x8_t>(aF1, aF1, aF1, 1 << 8, aF2, aF2, aF2, 1 << 8);
+ u16x8_t aF34 = simd::FromU16<u16x8_t>(aF3, aF3, aF3, 1 << 8, aF4, aF4, aF4, 1 << 8);
+
+ u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234);
+ u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234);
+
+ // Multiply with the alpha factors, add 128 for rounding, and shift right by 8 bits.
+ p12 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p12, aF12), simd::FromU16<u16x8_t>(128)));
+ p34 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p34, aF34), simd::FromU16<u16x8_t>(128)));
+
+ u8x16_t result = simd::PackAndSaturate16To8(p12, p34);
+ simd::Store8(&aTargetData[targetIndex], result);
+ }
+ }
+}
+
+template<typename f32x4_t, typename i32x4_t, typename u8x16_t>
+static already_AddRefed<DataSourceSurface>
+RenderTurbulence_SIMD(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+#define RETURN_TURBULENCE(Type, Stitch) \
+ SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t> \
+ renderer(aBaseFrequency, aSeed, aNumOctaves, aTileRect); \
+ return renderer.Render(aSize, aOffset);
+
+ switch (aType) {
+ case TURBULENCE_TYPE_TURBULENCE:
+ {
+ if (aStitch) {
+ RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, true);
+ }
+ RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, false);
+ }
+ case TURBULENCE_TYPE_FRACTAL_NOISE:
+ {
+ if (aStitch) {
+ RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, true);
+ }
+ RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, false);
+ }
+ }
+ return nullptr;
+#undef RETURN_TURBULENCE
+}
+
+// k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4
+template<typename i32x4_t, typename i16x8_t>
+static MOZ_ALWAYS_INLINE i16x8_t
+ArithmeticCombineTwoPixels(i16x8_t in1, i16x8_t in2,
+ const i16x8_t &k1And4, const i16x8_t &k2And3)
+{
+ // Calculate input product: inProd = (in1 * in2) / 255.
+ i32x4_t inProd_1, inProd_2;
+ simd::Mul16x4x2x2To32x4x2(in1, in2, inProd_1, inProd_2);
+ i16x8_t inProd = simd::PackAndSaturate32To16(simd::FastDivideBy255(inProd_1), simd::FastDivideBy255(inProd_2));
+
+ // Calculate k1 * ((in1 * in2) / 255) + (k4/128) * 128
+ i16x8_t oneTwentyEight = simd::FromI16<i16x8_t>(128);
+ i16x8_t inProd1AndOneTwentyEight = simd::InterleaveLo16(inProd, oneTwentyEight);
+ i16x8_t inProd2AndOneTwentyEight = simd::InterleaveHi16(inProd, oneTwentyEight);
+ i32x4_t inProdTimesK1PlusK4_1 = simd::MulAdd16x8x2To32x4(k1And4, inProd1AndOneTwentyEight);
+ i32x4_t inProdTimesK1PlusK4_2 = simd::MulAdd16x8x2To32x4(k1And4, inProd2AndOneTwentyEight);
+
+ // Calculate k2 * in1 + k3 * in2
+ i16x8_t in12_1 = simd::InterleaveLo16(in1, in2);
+ i16x8_t in12_2 = simd::InterleaveHi16(in1, in2);
+ i32x4_t inTimesK2K3_1 = simd::MulAdd16x8x2To32x4(k2And3, in12_1);
+ i32x4_t inTimesK2K3_2 = simd::MulAdd16x8x2To32x4(k2And3, in12_2);
+
+ // Sum everything up and truncate the fractional part.
+ i32x4_t result_1 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_1, inTimesK2K3_1));
+ i32x4_t result_2 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_2, inTimesK2K3_2));
+ return simd::PackAndSaturate32To16(result_1, result_2);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static already_AddRefed<DataSourceSurface>
+ApplyArithmeticCombine_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ IntSize size = aInput1->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* source1Data = aInput1->GetData();
+ uint8_t* source2Data = aInput2->GetData();
+ uint8_t* targetData = target->GetData();
+ uint32_t source1Stride = aInput1->Stride();
+ uint32_t source2Stride = aInput2->Stride();
+ uint32_t targetStride = target->Stride();
+
+ // The arithmetic combine filter does the following calculation:
+ // result = k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4
+ //
+ // Or, with in1/2 integers between 0 and 255:
+ // result = (k1 * in1 * in2) / 255 + k2 * in1 + k3 * in2 + k4 * 255
+ //
+ // We want the whole calculation to happen in integer, with 16-bit factors.
+ // So we convert our factors to fixed-point with precision 1.8.7.
+ // K4 is premultiplied with 255, and it will be multiplied with 128 later
+ // during the actual calculation, because premultiplying it with 255 * 128
+ // would overflow int16.
+
+ i16x8_t k1 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK1, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k2 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK2, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k3 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK3, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k4 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK4, -128.0f), 128.0f) * 255 + 0.5f)));
+
+ i16x8_t k1And4 = simd::InterleaveLo16(k1, k4);
+ i16x8_t k2And3 = simd::InterleaveLo16(k2, k3);
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ uint32_t source1Index = y * source1Stride + 4 * x;
+ uint32_t source2Index = y * source2Stride + 4 * x;
+ uint32_t targetIndex = y * targetStride + 4 * x;
+
+ // Load and unpack.
+ u8x16_t in1 = simd::Load8<u8x16_t>(&source1Data[source1Index]);
+ u8x16_t in2 = simd::Load8<u8x16_t>(&source2Data[source2Index]);
+ i16x8_t in1_12 = simd::UnpackLo8x8ToI16x8(in1);
+ i16x8_t in1_34 = simd::UnpackHi8x8ToI16x8(in1);
+ i16x8_t in2_12 = simd::UnpackLo8x8ToI16x8(in2);
+ i16x8_t in2_34 = simd::UnpackHi8x8ToI16x8(in2);
+
+ // Multiply and add.
+ i16x8_t result_12 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_12, in2_12, k1And4, k2And3);
+ i16x8_t result_34 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_34, in2_34, k1And4, k2And3);
+
+ // Pack and store.
+ simd::Store8(&targetData[targetIndex], simd::PackAndSaturate16To8(result_12, result_34));
+ }
+ }
+
+ return target.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/FilterProcessingSSE2.cpp b/system/graphics/2d/FilterProcessingSSE2.cpp
new file mode 100644
index 000000000..6f14fc3ef
--- /dev/null
+++ b/system/graphics/2d/FilterProcessingSSE2.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define SIMD_COMPILE_SSE2
+
+#include "FilterProcessingSIMD-inl.h"
+
+#ifndef USE_SSE2
+static_assert(false, "If this file is built, FilterProcessing.h should know about it!");
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+void
+FilterProcessing::ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ ExtractAlpha_SIMD<__m128i>(size, sourceData, sourceStride, alphaData, alphaStride);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface)
+{
+ return ConvertToB8G8R8A8_SIMD<__m128i>(aSurface);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ return ApplyBlending_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aBlendMode);
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ ApplyMorphologyHorizontal_SIMD<__m128i,__m128i>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ ApplyMorphologyVertical_SIMD<__m128i,__m128i>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ return ApplyColorMatrix_SIMD<__m128i,__m128i,__m128i>(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ return ApplyComposition_SIMD<__m128i,__m128i,__m128i>(aSource, aDest, aOperator);
+}
+
+void
+FilterProcessing::SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
+{
+ SeparateColorChannels_SIMD<__m128i>(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+}
+
+void
+FilterProcessing::CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ CombineColorChannels_SIMD<__m128i>(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ DoPremultiplicationCalculation_SIMD<__m128i,__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation_SSE2(
+ const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ DoUnpremultiplicationCalculation_SIMD<__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ return RenderTurbulence_SIMD<__m128,__m128i,__m128i>(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ return ApplyArithmeticCombine_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/FilterProcessingScalar.cpp b/system/graphics/2d/FilterProcessingScalar.cpp
new file mode 100644
index 000000000..9e88c563e
--- /dev/null
+++ b/system/graphics/2d/FilterProcessingScalar.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define FILTER_PROCESSING_SCALAR
+
+#include "FilterProcessingSIMD-inl.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+void
+FilterProcessing::ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * alphaStride + x;
+ alphaData[targetIndex] = sourceData[sourceIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ }
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface)
+{
+ return ConvertToB8G8R8A8_SIMD<simd::Scalaru8x16_t>(aSurface);
+}
+
+template<MorphologyOperator Operator>
+static void
+ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
+ Operator == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) {
+ int32_t startX = aDestRect.x - aRadius;
+ int32_t endX = aDestRect.x + aRadius;
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++, startX++, endX++) {
+ int32_t sourceIndex = y * aSourceStride + 4 * startX;
+ uint8_t u[4];
+ for (size_t i = 0; i < 4; i++) {
+ u[i] = aSourceData[sourceIndex + i];
+ }
+ sourceIndex += 4;
+ for (int32_t ix = startX + 1; ix <= endX; ix++, sourceIndex += 4) {
+ for (size_t i = 0; i < 4; i++) {
+ if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
+ u[i] = umin(u[i], aSourceData[sourceIndex + i]);
+ } else {
+ u[i] = umax(u[i], aSourceData[sourceIndex + i]);
+ }
+ }
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ for (size_t i = 0; i < 4; i++) {
+ aDestData[destIndex+i] = u[i];
+ }
+ }
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+template<MorphologyOperator Operator>
+static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
+ Operator == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t startY = aDestRect.y - aRadius;
+ int32_t endY = aDestRect.y + aRadius;
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) {
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++) {
+ int32_t sourceIndex = startY * aSourceStride + 4 * x;
+ uint8_t u[4];
+ for (size_t i = 0; i < 4; i++) {
+ u[i] = aSourceData[sourceIndex + i];
+ }
+ sourceIndex += aSourceStride;
+ for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) {
+ for (size_t i = 0; i < 4; i++) {
+ if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
+ u[i] = umin(u[i], aSourceData[sourceIndex + i]);
+ } else {
+ u[i] = umax(u[i], aSourceData[sourceIndex + i]);
+ }
+ }
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ for (size_t i = 0; i < 4; i++) {
+ aDestData[destIndex+i] = u[i];
+ }
+ }
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ return ApplyColorMatrix_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ return ApplyComposition_SIMD<simd::Scalari32x4_t,simd::Scalaru16x8_t,simd::Scalaru8x16_t>(aSource, aDest, aOperator);
+}
+
+void
+FilterProcessing::SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * channelStride + x;
+ channel0Data[targetIndex] = sourceData[sourceIndex];
+ channel1Data[targetIndex] = sourceData[sourceIndex+1];
+ channel2Data[targetIndex] = sourceData[sourceIndex+2];
+ channel3Data[targetIndex] = sourceData[sourceIndex+3];
+ }
+ }
+}
+
+void
+FilterProcessing::CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t resultIndex = y * resultStride + 4 * x;
+ int32_t channelIndex = y * channelStride + x;
+ resultData[resultIndex] = channel0Data[channelIndex];
+ resultData[resultIndex+1] = channel1Data[channelIndex];
+ resultData[resultIndex+2] = channel2Data[channelIndex];
+ resultData[resultIndex+3] = channel3Data[channelIndex];
+ }
+ }
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x++) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
+ }
+ }
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation_Scalar(
+ const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x++) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ uint16_t alphaFactor = sAlphaFactors[alpha];
+ // inputColor * alphaFactor + 128 is guaranteed to fit into uint16_t
+ // because the input is premultiplied and thus inputColor <= inputAlpha.
+ // The maximum value this can attain is 65520 (which is less than 65535)
+ // for color == alpha == 244:
+ // 244 * sAlphaFactors[244] + 128 == 244 * 268 + 128 == 65520
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
+ }
+ }
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ return RenderTurbulence_SIMD<simd::Scalarf32x4_t,simd::Scalari32x4_t,simd::Scalaru8x16_t>(
+ aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ return ApplyArithmeticCombine_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Filters.h b/system/graphics/2d/Filters.h
new file mode 100644
index 000000000..12eadcebe
--- /dev/null
+++ b/system/graphics/2d/Filters.h
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_FILTERS_H_
+#define MOZILLA_GFX_FILTERS_H_
+
+#include "Types.h"
+#include "mozilla/RefPtr.h"
+
+#include "Point.h"
+#include "Matrix.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurface;
+
+enum FilterBackend {
+ FILTER_BACKEND_SOFTWARE = 0,
+ FILTER_BACKEND_DIRECT2D1_1,
+ FILTER_BACKEND_RECORDING
+};
+
+enum TransformFilterAtts
+{
+ ATT_TRANSFORM_MATRIX = 0, // Matrix
+ ATT_TRANSFORM_FILTER // Filter
+};
+
+enum TransformFilterInputs
+{
+ IN_TRANSFORM_IN = 0
+};
+
+enum BlendFilterAtts
+{
+ ATT_BLEND_BLENDMODE = 0 // uint32_t
+};
+
+enum BlendMode
+{
+ BLEND_MODE_MULTIPLY = 0,
+ BLEND_MODE_SCREEN,
+ BLEND_MODE_DARKEN,
+ BLEND_MODE_LIGHTEN,
+ BLEND_MODE_OVERLAY,
+ BLEND_MODE_COLOR_DODGE,
+ BLEND_MODE_COLOR_BURN,
+ BLEND_MODE_HARD_LIGHT,
+ BLEND_MODE_SOFT_LIGHT,
+ BLEND_MODE_DIFFERENCE,
+ BLEND_MODE_EXCLUSION,
+ BLEND_MODE_HUE,
+ BLEND_MODE_SATURATION,
+ BLEND_MODE_COLOR,
+ BLEND_MODE_LUMINOSITY
+};
+
+enum BlendFilterInputs
+{
+ IN_BLEND_IN = 0,
+ IN_BLEND_IN2
+};
+
+enum MorphologyFilterAtts
+{
+ ATT_MORPHOLOGY_RADII = 0, // IntSize
+ ATT_MORPHOLOGY_OPERATOR // MorphologyOperator
+};
+
+enum MorphologyOperator
+{
+ MORPHOLOGY_OPERATOR_ERODE = 0,
+ MORPHOLOGY_OPERATOR_DILATE
+};
+
+enum MorphologyFilterInputs
+{
+ IN_MORPHOLOGY_IN = 0
+};
+
+enum AlphaMode
+{
+ ALPHA_MODE_PREMULTIPLIED = 0,
+ ALPHA_MODE_STRAIGHT
+};
+
+enum ColorMatrixFilterAtts
+{
+ ATT_COLOR_MATRIX_MATRIX = 0, // Matrix5x4
+ ATT_COLOR_MATRIX_ALPHA_MODE // AlphaMode
+};
+
+enum ColorMatrixFilterInputs
+{
+ IN_COLOR_MATRIX_IN = 0
+};
+
+enum FloodFilterAtts
+{
+ ATT_FLOOD_COLOR = 0 // Color
+};
+
+enum FloodFilterInputs
+{
+ IN_FLOOD_IN = 0
+};
+
+enum TileFilterAtts
+{
+ ATT_TILE_SOURCE_RECT = 0 // IntRect
+};
+
+enum TileFilterInputs
+{
+ IN_TILE_IN = 0
+};
+
+enum TransferAtts
+{
+ ATT_TRANSFER_DISABLE_R = 0, // bool
+ ATT_TRANSFER_DISABLE_G, // bool
+ ATT_TRANSFER_DISABLE_B, // bool
+ ATT_TRANSFER_DISABLE_A // bool
+};
+
+enum TransferInputs
+{
+ IN_TRANSFER_IN = 0
+};
+
+enum TableTransferAtts
+{
+ ATT_TABLE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_TABLE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_TABLE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_TABLE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_TABLE_TRANSFER_TABLE_R, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_G, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_B, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_A // Float[]
+};
+
+enum TableTransferInputs
+{
+ IN_TABLE_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum DiscreteTransferAtts
+{
+ ATT_DISCRETE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_DISCRETE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_DISCRETE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_DISCRETE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_DISCRETE_TRANSFER_TABLE_R, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_G, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_B, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_A // Float[]
+};
+
+enum DiscreteTransferInputs
+{
+ IN_DISCRETE_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum LinearTransferAtts
+{
+ ATT_LINEAR_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_LINEAR_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_LINEAR_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_LINEAR_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_LINEAR_TRANSFER_SLOPE_R, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_G, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_B, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_A, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_R, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_G, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_B, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_A // Float
+};
+
+enum LinearTransferInputs
+{
+ IN_LINEAR_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum GammaTransferAtts
+{
+ ATT_GAMMA_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_GAMMA_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_GAMMA_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_GAMMA_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_R, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_G, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_B, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_A, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_R, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_G, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_B, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_A, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_R, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_G, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_B, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_A // Float
+};
+
+enum GammaTransferInputs
+{
+ IN_GAMMA_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum ConvolveMatrixAtts
+{
+ ATT_CONVOLVE_MATRIX_KERNEL_SIZE = 0, // IntSize
+ ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, // Float[]
+ ATT_CONVOLVE_MATRIX_DIVISOR, // Float
+ ATT_CONVOLVE_MATRIX_BIAS, // Float
+ ATT_CONVOLVE_MATRIX_TARGET, // IntPoint
+ ATT_CONVOLVE_MATRIX_SOURCE_RECT, // IntRect
+ ATT_CONVOLVE_MATRIX_EDGE_MODE, // ConvolveMatrixEdgeMode
+ ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, // Size
+ ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA, // bool
+};
+
+enum ConvolveMatrixEdgeMode
+{
+ EDGE_MODE_DUPLICATE = 0,
+ EDGE_MODE_WRAP,
+ EDGE_MODE_NONE
+};
+
+enum ConvolveMatrixInputs
+{
+ IN_CONVOLVE_MATRIX_IN = 0
+};
+
+enum DisplacementMapAtts
+{
+ ATT_DISPLACEMENT_MAP_SCALE = 0, // Float
+ ATT_DISPLACEMENT_MAP_X_CHANNEL, // ColorChannel
+ ATT_DISPLACEMENT_MAP_Y_CHANNEL // ColorChannel
+};
+
+enum ColorChannel
+{
+ COLOR_CHANNEL_R = 0,
+ COLOR_CHANNEL_G,
+ COLOR_CHANNEL_B,
+ COLOR_CHANNEL_A
+};
+
+enum DisplacementMapInputs
+{
+ IN_DISPLACEMENT_MAP_IN = 0,
+ IN_DISPLACEMENT_MAP_IN2
+};
+
+enum TurbulenceAtts
+{
+ ATT_TURBULENCE_BASE_FREQUENCY = 0, // Size
+ ATT_TURBULENCE_NUM_OCTAVES, // uint32_t
+ ATT_TURBULENCE_SEED, // uint32_t
+ ATT_TURBULENCE_STITCHABLE, // bool
+ ATT_TURBULENCE_TYPE, // TurbulenceType
+ ATT_TURBULENCE_RECT // IntRect
+};
+
+enum TurbulenceType
+{
+ TURBULENCE_TYPE_TURBULENCE = 0,
+ TURBULENCE_TYPE_FRACTAL_NOISE
+};
+
+enum ArithmeticCombineAtts
+{
+ ATT_ARITHMETIC_COMBINE_COEFFICIENTS = 0 // Float[4]
+};
+
+enum ArithmeticCombineInputs
+{
+ IN_ARITHMETIC_COMBINE_IN = 0,
+ IN_ARITHMETIC_COMBINE_IN2
+};
+
+enum CompositeAtts
+{
+ ATT_COMPOSITE_OPERATOR = 0 // CompositeOperator
+};
+
+enum CompositeOperator
+{
+ COMPOSITE_OPERATOR_OVER = 0,
+ COMPOSITE_OPERATOR_IN,
+ COMPOSITE_OPERATOR_OUT,
+ COMPOSITE_OPERATOR_ATOP,
+ COMPOSITE_OPERATOR_XOR
+};
+
+enum CompositeInputs
+{
+ // arbitrary number of inputs
+ IN_COMPOSITE_IN_START = 0
+};
+
+enum GaussianBlurAtts
+{
+ ATT_GAUSSIAN_BLUR_STD_DEVIATION = 0 // Float
+};
+
+enum GaussianBlurInputs
+{
+ IN_GAUSSIAN_BLUR_IN = 0
+};
+
+enum DirectionalBlurAtts
+{
+ ATT_DIRECTIONAL_BLUR_STD_DEVIATION = 0, // Float
+ ATT_DIRECTIONAL_BLUR_DIRECTION // BlurDirection
+};
+
+enum BlurDirection
+{
+ BLUR_DIRECTION_X = 0,
+ BLUR_DIRECTION_Y
+};
+
+enum DirectionalBlurInputs
+{
+ IN_DIRECTIONAL_BLUR_IN = 0
+};
+
+enum LightingAtts
+{
+ ATT_POINT_LIGHT_POSITION = 0, // Point3D
+
+ ATT_SPOT_LIGHT_POSITION, // Point3D
+ ATT_SPOT_LIGHT_POINTS_AT, // Point3D
+ ATT_SPOT_LIGHT_FOCUS, // Float
+ ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, // Float
+
+ ATT_DISTANT_LIGHT_AZIMUTH, // Float
+ ATT_DISTANT_LIGHT_ELEVATION, // Float
+
+ ATT_LIGHTING_COLOR, // Color
+ ATT_LIGHTING_SURFACE_SCALE, // Float
+ ATT_LIGHTING_KERNEL_UNIT_LENGTH, // Size
+
+ ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT, // Float
+
+ ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, // Float
+ ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT // Float
+};
+
+enum LightingInputs
+{
+ IN_LIGHTING_IN = 0
+};
+
+enum PointDiffuseAtts
+{
+ ATT_POINT_DIFFUSE_POSITION = ATT_POINT_LIGHT_POSITION,
+ ATT_POINT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_POINT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_POINT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_POINT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum PointDiffuseInputs
+{
+ IN_POINT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum SpotDiffuseAtts
+{
+ ATT_SPOT_DIFFUSE_POSITION = ATT_SPOT_LIGHT_POSITION,
+ ATT_SPOT_DIFFUSE_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
+ ATT_SPOT_DIFFUSE_FOCUS = ATT_SPOT_LIGHT_FOCUS,
+ ATT_SPOT_DIFFUSE_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ ATT_SPOT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_SPOT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_SPOT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_SPOT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum SpotDiffuseInputs
+{
+ IN_SPOT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum DistantDiffuseAtts
+{
+ ATT_DISTANT_DIFFUSE_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
+ ATT_DISTANT_DIFFUSE_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
+ ATT_DISTANT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_DISTANT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_DISTANT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum DistantDiffuseInputs
+{
+ IN_DISTANT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum PointSpecularAtts
+{
+ ATT_POINT_SPECULAR_POSITION = ATT_POINT_LIGHT_POSITION,
+ ATT_POINT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_POINT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_POINT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_POINT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_POINT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum PointSpecularInputs
+{
+ IN_POINT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum SpotSpecularAtts
+{
+ ATT_SPOT_SPECULAR_POSITION = ATT_SPOT_LIGHT_POSITION,
+ ATT_SPOT_SPECULAR_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
+ ATT_SPOT_SPECULAR_FOCUS = ATT_SPOT_LIGHT_FOCUS,
+ ATT_SPOT_SPECULAR_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ ATT_SPOT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_SPOT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_SPOT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_SPOT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_SPOT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum SpotSpecularInputs
+{
+ IN_SPOT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum DistantSpecularAtts
+{
+ ATT_DISTANT_SPECULAR_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
+ ATT_DISTANT_SPECULAR_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
+ ATT_DISTANT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_DISTANT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_DISTANT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_DISTANT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_DISTANT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum DistantSpecularInputs
+{
+ IN_DISTANT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum CropAtts
+{
+ ATT_CROP_RECT = 0 // Rect
+};
+
+enum CropInputs
+{
+ IN_CROP_IN = 0
+};
+
+enum PremultiplyInputs
+{
+ IN_PREMULTIPLY_IN = 0
+};
+
+enum UnpremultiplyInputs
+{
+ IN_UNPREMULTIPLY_IN = 0
+};
+
+class FilterNode : public RefCounted<FilterNode>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNode)
+ virtual ~FilterNode() {}
+
+ virtual FilterBackend GetBackendType() = 0;
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { MOZ_CRASH("GFX: FilterNode"); }
+
+ virtual void SetAttribute(uint32_t aIndex, bool) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, uint32_t) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, Float) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Size &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Rect &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Point &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Color &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) { MOZ_CRASH("GFX: FilterNode"); }
+
+protected:
+ friend class Factory;
+
+ FilterNode() {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/GenericRefCounted.h b/system/graphics/2d/GenericRefCounted.h
new file mode 100644
index 000000000..bee792b4d
--- /dev/null
+++ b/system/graphics/2d/GenericRefCounted.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This header provides virtual, non-templated alternatives to MFBT's RefCounted<T>.
+// It intentionally uses MFBT coding style with the intention of moving there
+// should there be other use cases for it.
+
+#ifndef MOZILLA_GENERICREFCOUNTED_H_
+#define MOZILLA_GENERICREFCOUNTED_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
+
+namespace mozilla {
+
+/**
+ * Common base class for GenericRefCounted and GenericAtomicRefCounted.
+ *
+ * Having this shared base class, common to both the atomic and non-atomic
+ * cases, allows to have RefPtr's that don't care about whether the
+ * objects they're managing have atomic refcounts or not.
+ */
+class GenericRefCountedBase
+{
+ protected:
+ virtual ~GenericRefCountedBase() {};
+
+ public:
+ // AddRef() and Release() method names are for compatibility with nsRefPtr.
+ virtual void AddRef() = 0;
+
+ virtual void Release() = 0;
+
+ // ref() and deref() method names are for compatibility with wtf::RefPtr.
+ // No virtual keywords here: if a subclass wants to override the refcounting
+ // mechanism, it is welcome to do so by overriding AddRef() and Release().
+ void ref() { AddRef(); }
+ void deref() { Release(); }
+
+#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
+ virtual const char* typeName() const = 0;
+ virtual size_t typeSize() const = 0;
+#endif
+};
+
+namespace detail {
+
+template<RefCountAtomicity Atomicity>
+class GenericRefCounted : public GenericRefCountedBase
+{
+ protected:
+ GenericRefCounted() : refCnt(0) { }
+
+ virtual ~GenericRefCounted() {
+ MOZ_ASSERT(refCnt == detail::DEAD);
+ }
+
+ public:
+ virtual void AddRef() override {
+ // Note: this method must be thread safe for GenericAtomicRefCounted.
+ MOZ_ASSERT(int32_t(refCnt) >= 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+ ++refCnt;
+#else
+ const char* type = typeName();
+ uint32_t size = typeSize();
+ const void* ptr = this;
+ MozRefCountType cnt = ++refCnt;
+ detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
+#endif
+ }
+
+ virtual void Release() override {
+ // Note: this method must be thread safe for GenericAtomicRefCounted.
+ MOZ_ASSERT(int32_t(refCnt) > 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+ MozRefCountType cnt = --refCnt;
+#else
+ const char* type = typeName();
+ const void* ptr = this;
+ MozRefCountType cnt = --refCnt;
+ // Note: it's not safe to touch |this| after decrementing the refcount,
+ // except for below.
+ detail::RefCountLogger::logRelease(ptr, cnt, type);
+#endif
+ if (0 == cnt) {
+ // Because we have atomically decremented the refcount above, only
+ // one thread can get a 0 count here, so as long as we can assume that
+ // everything else in the system is accessing this object through
+ // RefPtrs, it's safe to access |this| here.
+#ifdef DEBUG
+ refCnt = detail::DEAD;
+#endif
+ delete this;
+ }
+ }
+
+ MozRefCountType refCount() const { return refCnt; }
+ bool hasOneRef() const {
+ MOZ_ASSERT(refCnt > 0);
+ return refCnt == 1;
+ }
+
+ private:
+ typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
+};
+
+} // namespace detail
+
+/**
+ * This reference-counting base class is virtual instead of
+ * being templated, which is useful in cases where one needs
+ * genericity at binary code level, but comes at the cost
+ * of a moderate performance and size overhead, like anything virtual.
+ */
+class GenericRefCounted : public detail::GenericRefCounted<detail::NonAtomicRefCount>
+{
+};
+
+/**
+ * GenericAtomicRefCounted is like GenericRefCounted, with an atomically updated
+ * reference counter.
+ */
+class GenericAtomicRefCounted : public detail::GenericRefCounted<detail::AtomicRefCount>
+{
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/GradientStopsD2D.h b/system/graphics/2d/GradientStopsD2D.h
new file mode 100644
index 000000000..de6e448de
--- /dev/null
+++ b/system/graphics/2d/GradientStopsD2D.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GRADIENTSTOPSD2D_H_
+#define MOZILLA_GFX_GRADIENTSTOPSD2D_H_
+
+#include "2D.h"
+
+#include <d2d1.h>
+
+namespace mozilla {
+namespace gfx {
+
+class GradientStopsD2D : public GradientStops
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D)
+ GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice)
+ : mStopCollection(aStopCollection)
+ , mDevice(aDevice)
+ {}
+
+ virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
+
+ virtual bool IsValid() const final{ return mDevice == Factory::GetDirect3D11Device(); }
+
+private:
+ friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
+
+ mutable RefPtr<ID2D1GradientStopCollection> mStopCollection;
+ RefPtr<ID3D11Device> mDevice;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_GRADIENTSTOPSD2D_H_ */
diff --git a/system/graphics/2d/Helpers.h b/system/graphics/2d/Helpers.h
new file mode 100644
index 000000000..11c7eec5d
--- /dev/null
+++ b/system/graphics/2d/Helpers.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_2D_HELPERS_H_
+#define MOZILLA_GFX_2D_HELPERS_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class AutoRestoreTransform
+{
+ public:
+ AutoRestoreTransform()
+ {
+ }
+
+ explicit AutoRestoreTransform(DrawTarget *aTarget)
+ : mDrawTarget(aTarget),
+ mOldTransform(aTarget->GetTransform())
+ {
+ }
+
+ void Init(DrawTarget *aTarget)
+ {
+ MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget);
+ if (!mDrawTarget) {
+ mDrawTarget = aTarget;
+ mOldTransform = aTarget->GetTransform();
+ }
+ }
+
+ ~AutoRestoreTransform()
+ {
+ if (mDrawTarget) {
+ mDrawTarget->SetTransform(mOldTransform);
+ }
+ }
+
+ private:
+ RefPtr<DrawTarget> mDrawTarget;
+ Matrix mOldTransform;
+};
+
+class AutoPopClips
+{
+public:
+ explicit AutoPopClips(DrawTarget *aTarget)
+ : mDrawTarget(aTarget)
+ , mPushCount(0)
+ {
+ MOZ_ASSERT(mDrawTarget);
+ }
+
+ ~AutoPopClips()
+ {
+ PopAll();
+ }
+
+ void PushClip(const Path *aPath)
+ {
+ mDrawTarget->PushClip(aPath);
+ ++mPushCount;
+ }
+
+ void PushClipRect(const Rect &aRect)
+ {
+ mDrawTarget->PushClipRect(aRect);
+ ++mPushCount;
+ }
+
+ void PopClip()
+ {
+ MOZ_ASSERT(mPushCount > 0);
+ mDrawTarget->PopClip();
+ --mPushCount;
+ }
+
+ void PopAll()
+ {
+ while (mPushCount-- > 0) {
+ mDrawTarget->PopClip();
+ }
+ }
+
+private:
+ RefPtr<DrawTarget> mDrawTarget;
+ int32_t mPushCount;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_2D_HELPERS_H_
diff --git a/system/graphics/2d/HelpersCairo.h b/system/graphics/2d/HelpersCairo.h
new file mode 100644
index 000000000..bbcce274e
--- /dev/null
+++ b/system/graphics/2d/HelpersCairo.h
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_HELPERSCAIRO_H_
+#define MOZILLA_GFX_HELPERSCAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+static inline cairo_operator_t
+GfxOpToCairoOp(CompositionOp op)
+{
+ switch (op)
+ {
+ case CompositionOp::OP_OVER:
+ return CAIRO_OPERATOR_OVER;
+ case CompositionOp::OP_ADD:
+ return CAIRO_OPERATOR_ADD;
+ case CompositionOp::OP_ATOP:
+ return CAIRO_OPERATOR_ATOP;
+ case CompositionOp::OP_OUT:
+ return CAIRO_OPERATOR_OUT;
+ case CompositionOp::OP_IN:
+ return CAIRO_OPERATOR_IN;
+ case CompositionOp::OP_SOURCE:
+ return CAIRO_OPERATOR_SOURCE;
+ case CompositionOp::OP_DEST_IN:
+ return CAIRO_OPERATOR_DEST_IN;
+ case CompositionOp::OP_DEST_OUT:
+ return CAIRO_OPERATOR_DEST_OUT;
+ case CompositionOp::OP_DEST_OVER:
+ return CAIRO_OPERATOR_DEST_OVER;
+ case CompositionOp::OP_DEST_ATOP:
+ return CAIRO_OPERATOR_DEST_ATOP;
+ case CompositionOp::OP_XOR:
+ return CAIRO_OPERATOR_XOR;
+ case CompositionOp::OP_MULTIPLY:
+ return CAIRO_OPERATOR_MULTIPLY;
+ case CompositionOp::OP_SCREEN:
+ return CAIRO_OPERATOR_SCREEN;
+ case CompositionOp::OP_OVERLAY:
+ return CAIRO_OPERATOR_OVERLAY;
+ case CompositionOp::OP_DARKEN:
+ return CAIRO_OPERATOR_DARKEN;
+ case CompositionOp::OP_LIGHTEN:
+ return CAIRO_OPERATOR_LIGHTEN;
+ case CompositionOp::OP_COLOR_DODGE:
+ return CAIRO_OPERATOR_COLOR_DODGE;
+ case CompositionOp::OP_COLOR_BURN:
+ return CAIRO_OPERATOR_COLOR_BURN;
+ case CompositionOp::OP_HARD_LIGHT:
+ return CAIRO_OPERATOR_HARD_LIGHT;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return CAIRO_OPERATOR_SOFT_LIGHT;
+ case CompositionOp::OP_DIFFERENCE:
+ return CAIRO_OPERATOR_DIFFERENCE;
+ case CompositionOp::OP_EXCLUSION:
+ return CAIRO_OPERATOR_EXCLUSION;
+ case CompositionOp::OP_HUE:
+ return CAIRO_OPERATOR_HSL_HUE;
+ case CompositionOp::OP_SATURATION:
+ return CAIRO_OPERATOR_HSL_SATURATION;
+ case CompositionOp::OP_COLOR:
+ return CAIRO_OPERATOR_HSL_COLOR;
+ case CompositionOp::OP_LUMINOSITY:
+ return CAIRO_OPERATOR_HSL_LUMINOSITY;
+ case CompositionOp::OP_COUNT:
+ break;
+ }
+
+ return CAIRO_OPERATOR_OVER;
+}
+
+static inline cairo_antialias_t
+GfxAntialiasToCairoAntialias(AntialiasMode antialias)
+{
+ switch (antialias)
+ {
+ case AntialiasMode::NONE:
+ return CAIRO_ANTIALIAS_NONE;
+ case AntialiasMode::GRAY:
+ return CAIRO_ANTIALIAS_GRAY;
+ case AntialiasMode::SUBPIXEL:
+ return CAIRO_ANTIALIAS_SUBPIXEL;
+ default:
+ return CAIRO_ANTIALIAS_DEFAULT;
+ }
+}
+
+static inline AntialiasMode
+CairoAntialiasToGfxAntialias(cairo_antialias_t aAntialias)
+{
+ switch(aAntialias) {
+ case CAIRO_ANTIALIAS_NONE:
+ return AntialiasMode::NONE;
+ case CAIRO_ANTIALIAS_GRAY:
+ return AntialiasMode::GRAY;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ return AntialiasMode::SUBPIXEL;
+ default:
+ return AntialiasMode::DEFAULT;
+ }
+}
+
+static inline cairo_filter_t
+GfxSamplingFilterToCairoFilter(SamplingFilter filter)
+{
+ switch (filter)
+ {
+ case SamplingFilter::GOOD:
+ return CAIRO_FILTER_GOOD;
+ case SamplingFilter::LINEAR:
+ return CAIRO_FILTER_BILINEAR;
+ case SamplingFilter::POINT:
+ return CAIRO_FILTER_NEAREST;
+ default:
+ MOZ_CRASH("GFX: bad Cairo filter");
+ }
+
+ return CAIRO_FILTER_BILINEAR;
+}
+
+static inline cairo_extend_t
+GfxExtendToCairoExtend(ExtendMode extend)
+{
+ switch (extend)
+ {
+ case ExtendMode::CLAMP:
+ return CAIRO_EXTEND_PAD;
+ // Cairo doesn't support tiling in only 1 direction,
+ // So we have to fallback and tile in both.
+ case ExtendMode::REPEAT_X:
+ case ExtendMode::REPEAT_Y:
+ case ExtendMode::REPEAT:
+ return CAIRO_EXTEND_REPEAT;
+ case ExtendMode::REFLECT:
+ return CAIRO_EXTEND_REFLECT;
+ }
+
+ return CAIRO_EXTEND_PAD;
+}
+
+static inline cairo_format_t
+GfxFormatToCairoFormat(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return CAIRO_FORMAT_ARGB32;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return CAIRO_FORMAT_RGB24;
+ case SurfaceFormat::A8:
+ return CAIRO_FORMAT_A8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return CAIRO_FORMAT_RGB16_565;
+ default:
+ gfxCriticalError() << "Unknown image format " << (int)format;
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+
+static inline cairo_content_t
+GfxFormatToCairoContent(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ case SurfaceFormat::R5G6B5_UINT16: //fall through
+ return CAIRO_CONTENT_COLOR;
+ case SurfaceFormat::A8:
+ return CAIRO_CONTENT_ALPHA;
+ default:
+ gfxCriticalError() << "Unknown image content format " << (int)format;
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ }
+}
+
+static inline cairo_line_join_t
+GfxLineJoinToCairoLineJoin(JoinStyle style)
+{
+ switch (style)
+ {
+ case JoinStyle::BEVEL:
+ return CAIRO_LINE_JOIN_BEVEL;
+ case JoinStyle::ROUND:
+ return CAIRO_LINE_JOIN_ROUND;
+ case JoinStyle::MITER:
+ return CAIRO_LINE_JOIN_MITER;
+ case JoinStyle::MITER_OR_BEVEL:
+ return CAIRO_LINE_JOIN_MITER;
+ }
+
+ return CAIRO_LINE_JOIN_MITER;
+}
+
+static inline cairo_line_cap_t
+GfxLineCapToCairoLineCap(CapStyle style)
+{
+ switch (style)
+ {
+ case CapStyle::BUTT:
+ return CAIRO_LINE_CAP_BUTT;
+ case CapStyle::ROUND:
+ return CAIRO_LINE_CAP_ROUND;
+ case CapStyle::SQUARE:
+ return CAIRO_LINE_CAP_SQUARE;
+ }
+
+ return CAIRO_LINE_CAP_BUTT;
+}
+
+static inline SurfaceFormat
+CairoContentToGfxFormat(cairo_content_t content)
+{
+ switch (content)
+ {
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ case CAIRO_CONTENT_COLOR:
+ // BEWARE! format may be 565
+ return SurfaceFormat::X8R8G8B8_UINT32;
+ case CAIRO_CONTENT_ALPHA:
+ return SurfaceFormat::A8;
+ }
+
+ return SurfaceFormat::B8G8R8A8;
+}
+
+static inline SurfaceFormat
+CairoFormatToGfxFormat(cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ case CAIRO_FORMAT_RGB24:
+ return SurfaceFormat::X8R8G8B8_UINT32;
+ case CAIRO_FORMAT_A8:
+ return SurfaceFormat::A8;
+ case CAIRO_FORMAT_RGB16_565:
+ return SurfaceFormat::R5G6B5_UINT16;
+ default:
+ gfxCriticalError() << "Unknown cairo format " << format;
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+static inline FontHinting
+CairoHintingToGfxHinting(cairo_hint_style_t aHintStyle)
+{
+ switch (aHintStyle) {
+ case CAIRO_HINT_STYLE_NONE:
+ return FontHinting::NONE;
+ case CAIRO_HINT_STYLE_SLIGHT:
+ return FontHinting::LIGHT;
+ case CAIRO_HINT_STYLE_MEDIUM:
+ return FontHinting::NORMAL;
+ case CAIRO_HINT_STYLE_FULL:
+ return FontHinting::FULL;
+ default:
+ return FontHinting::NORMAL;
+ }
+}
+
+SurfaceFormat GfxFormatForCairoSurface(cairo_surface_t* surface);
+
+static inline void
+GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
+{
+ cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
+}
+
+static inline void
+SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions)
+{
+ cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth);
+
+ cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit);
+
+ if (aStrokeOptions.mDashPattern) {
+ // Convert array of floats to array of doubles
+ std::vector<double> dashes(aStrokeOptions.mDashLength);
+ bool nonZero = false;
+ for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) {
+ if (aStrokeOptions.mDashPattern[i] != 0) {
+ nonZero = true;
+ }
+ dashes[i] = aStrokeOptions.mDashPattern[i];
+ }
+ // Avoid all-zero patterns that would trigger the CAIRO_STATUS_INVALID_DASH context error state.
+ if (nonZero) {
+ cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength,
+ aStrokeOptions.mDashOffset);
+ }
+ }
+
+ cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin));
+
+ cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap));
+}
+
+static inline cairo_fill_rule_t
+GfxFillRuleToCairoFillRule(FillRule rule)
+{
+ switch (rule)
+ {
+ case FillRule::FILL_WINDING:
+ return CAIRO_FILL_RULE_WINDING;
+ case FillRule::FILL_EVEN_ODD:
+ return CAIRO_FILL_RULE_EVEN_ODD;
+ }
+
+ return CAIRO_FILL_RULE_WINDING;
+}
+
+// RAII class for temporarily changing the cairo matrix transform. It will use
+// the given matrix transform while it is in scope. When it goes out of scope
+// it will put the cairo context back the way it was.
+
+class CairoTempMatrix
+{
+public:
+ CairoTempMatrix(cairo_t* aCtx, const Matrix& aMatrix)
+ : mCtx(aCtx)
+ {
+ cairo_get_matrix(aCtx, &mSaveMatrix);
+ cairo_matrix_t matrix;
+ GfxMatrixToCairoMatrix(aMatrix, matrix);
+ cairo_set_matrix(aCtx, &matrix);
+ }
+
+ ~CairoTempMatrix()
+ {
+ cairo_set_matrix(mCtx, &mSaveMatrix);
+ }
+
+private:
+ cairo_t* mCtx;
+ cairo_matrix_t mSaveMatrix;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_HELPERSCAIRO_H_ */
diff --git a/system/graphics/2d/HelpersD2D.h b/system/graphics/2d/HelpersD2D.h
new file mode 100644
index 000000000..48aec2cb5
--- /dev/null
+++ b/system/graphics/2d/HelpersD2D.h
@@ -0,0 +1,967 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_HELPERSD2D_H_
+#define MOZILLA_GFX_HELPERSD2D_H_
+
+#include <d2d1_1.h>
+
+#include <vector>
+
+#include <dwrite.h>
+#include <versionhelpers.h>
+#include "2D.h"
+#include "Logging.h"
+#include "Tools.h"
+#include "ImageScaling.h"
+
+#include "ScaledFontDWrite.h"
+
+#undef min
+#undef max
+
+namespace mozilla {
+namespace gfx {
+
+ID2D1Factory1* D2DFactory1();
+static ID2D1Factory* D2DFactory() { return D2DFactory1(); }
+
+static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
+{
+ return D2D1::Point2F(aPoint.x, aPoint.y);
+}
+
+static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize)
+{
+ return D2D1::SizeU(aSize.width, aSize.height);
+}
+
+template <typename T>
+static inline D2D1_RECT_F D2DRect(const T &aRect)
+{
+ return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
+}
+
+static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode, Axis aAxis)
+{
+ D2D1_EXTEND_MODE extend;
+ switch (aExtendMode) {
+ case ExtendMode::REPEAT:
+ extend = D2D1_EXTEND_MODE_WRAP;
+ break;
+ case ExtendMode::REPEAT_X:
+ {
+ extend = aAxis == Axis::X_AXIS
+ ? D2D1_EXTEND_MODE_WRAP
+ : D2D1_EXTEND_MODE_CLAMP;
+ break;
+ }
+ case ExtendMode::REPEAT_Y:
+ {
+ extend = aAxis == Axis::Y_AXIS
+ ? D2D1_EXTEND_MODE_WRAP
+ : D2D1_EXTEND_MODE_CLAMP;
+ break;
+ }
+ case ExtendMode::REFLECT:
+ extend = D2D1_EXTEND_MODE_MIRROR;
+ break;
+ default:
+ extend = D2D1_EXTEND_MODE_CLAMP;
+ }
+
+ return extend;
+}
+
+static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::POINT:
+ return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
+ }
+}
+
+static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::POINT:
+ return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ return D2D1_INTERPOLATION_MODE_LINEAR;
+ }
+}
+
+static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix)
+{
+ return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14,
+ aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24,
+ aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34,
+ aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44,
+ aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54);
+}
+
+static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint)
+{
+ return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z);
+}
+
+static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
+{
+ switch (aMode) {
+ case AntialiasMode::NONE:
+ return D2D1_ANTIALIAS_MODE_ALIASED;
+ default:
+ return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+ }
+}
+
+static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform)
+{
+ return D2D1::Matrix3x2F(aTransform._11, aTransform._12,
+ aTransform._21, aTransform._22,
+ aTransform._31, aTransform._32);
+}
+
+static inline D2D1_COLOR_F D2DColor(const Color &aColor)
+{
+ return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a);
+}
+
+static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize)
+{
+ return IntSize(aSize.width, aSize.height);
+}
+
+static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat)
+{
+ switch(aFormat.format) {
+ case DXGI_FORMAT_A8_UNORM:
+ case DXGI_FORMAT_R8_UNORM:
+ return SurfaceFormat::A8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) {
+ return SurfaceFormat::B8G8R8X8;
+ } else {
+ return SurfaceFormat::B8G8R8A8;
+ }
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+static inline Rect ToRect(const D2D1_RECT_F &aRect)
+{
+ return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top);
+}
+
+static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform)
+{
+ return Matrix(aTransform._11, aTransform._12,
+ aTransform._21, aTransform._22,
+ aTransform._31, aTransform._32);
+}
+
+static inline Point ToPoint(const D2D1_POINT_2F &aPoint)
+{
+ return Point(aPoint.x, aPoint.y);
+}
+
+static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::B8G8R8X8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::A8:
+ return DXGI_FORMAT_A8_UNORM;
+ default:
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ return D2D1_ALPHA_MODE_IGNORE;
+ default:
+ return D2D1_ALPHA_MODE_PREMULTIPLIED;
+ }
+}
+
+static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
+{
+ return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat));
+}
+
+static inline bool D2DSupportsCompositeMode(CompositionOp aOp)
+{
+ switch(aOp) {
+ case CompositionOp::OP_OVER:
+ case CompositionOp::OP_ADD:
+ case CompositionOp::OP_ATOP:
+ case CompositionOp::OP_OUT:
+ case CompositionOp::OP_IN:
+ case CompositionOp::OP_SOURCE:
+ case CompositionOp::OP_DEST_IN:
+ case CompositionOp::OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OVER:
+ case CompositionOp::OP_DEST_ATOP:
+ case CompositionOp::OP_XOR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
+{
+ switch(aOp) {
+ case CompositionOp::OP_OVER:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ case CompositionOp::OP_ADD:
+ return D2D1_COMPOSITE_MODE_PLUS;
+ case CompositionOp::OP_ATOP:
+ return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+ case CompositionOp::OP_OUT:
+ return D2D1_COMPOSITE_MODE_SOURCE_OUT;
+ case CompositionOp::OP_IN:
+ return D2D1_COMPOSITE_MODE_SOURCE_IN;
+ case CompositionOp::OP_SOURCE:
+ return D2D1_COMPOSITE_MODE_SOURCE_COPY;
+ case CompositionOp::OP_DEST_IN:
+ return D2D1_COMPOSITE_MODE_DESTINATION_IN;
+ case CompositionOp::OP_DEST_OUT:
+ return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
+ case CompositionOp::OP_DEST_OVER:
+ return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
+ case CompositionOp::OP_DEST_ATOP:
+ return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
+ case CompositionOp::OP_XOR:
+ return D2D1_COMPOSITE_MODE_XOR;
+ default:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ }
+}
+
+static inline D2D1_BLEND_MODE D2DBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_MULTIPLY:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ case CompositionOp::OP_SCREEN:
+ return D2D1_BLEND_MODE_SCREEN;
+ case CompositionOp::OP_OVERLAY:
+ return D2D1_BLEND_MODE_OVERLAY;
+ case CompositionOp::OP_DARKEN:
+ return D2D1_BLEND_MODE_DARKEN;
+ case CompositionOp::OP_LIGHTEN:
+ return D2D1_BLEND_MODE_LIGHTEN;
+ case CompositionOp::OP_COLOR_DODGE:
+ return D2D1_BLEND_MODE_COLOR_DODGE;
+ case CompositionOp::OP_COLOR_BURN:
+ return D2D1_BLEND_MODE_COLOR_BURN;
+ case CompositionOp::OP_HARD_LIGHT:
+ return D2D1_BLEND_MODE_HARD_LIGHT;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return D2D1_BLEND_MODE_SOFT_LIGHT;
+ case CompositionOp::OP_DIFFERENCE:
+ return D2D1_BLEND_MODE_DIFFERENCE;
+ case CompositionOp::OP_EXCLUSION:
+ return D2D1_BLEND_MODE_EXCLUSION;
+ case CompositionOp::OP_HUE:
+ return D2D1_BLEND_MODE_HUE;
+ case CompositionOp::OP_SATURATION:
+ return D2D1_BLEND_MODE_SATURATION;
+ case CompositionOp::OP_COLOR:
+ return D2D1_BLEND_MODE_COLOR;
+ case CompositionOp::OP_LUMINOSITY:
+ return D2D1_BLEND_MODE_LUMINOSITY;
+ default:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ }
+}
+
+static inline bool D2DSupportsPrimitiveBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_OVER:
+// case CompositionOp::OP_SOURCE:
+ return true;
+// case CompositionOp::OP_DARKEN:
+ case CompositionOp::OP_ADD:
+ return IsWindows8Point1OrGreater();
+ default:
+ return false;
+ }
+}
+
+static inline D2D1_PRIMITIVE_BLEND D2DPrimitiveBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_OVER:
+ return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
+ // D2D1_PRIMITIVE_BLEND_COPY should leave pixels out of the source's
+ // bounds unchanged, but doesn't- breaking unbounded ops.
+ // D2D1_PRIMITIVE_BLEND_MIN doesn't quite work like darken either, as it
+ // accounts for the source alpha.
+ //
+ // case CompositionOp::OP_SOURCE:
+ // return D2D1_PRIMITIVE_BLEND_COPY;
+ // case CompositionOp::OP_DARKEN:
+ // return D2D1_PRIMITIVE_BLEND_MIN;
+ case CompositionOp::OP_ADD:
+ return D2D1_PRIMITIVE_BLEND_ADD;
+ default:
+ return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
+ }
+}
+
+static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
+{
+ if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
+ return true;
+ }
+
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aPattern);
+
+ if (pat->mRadius1 != 0) {
+ return false;
+ }
+
+ Point diff = pat->mCenter2 - pat->mCenter1;
+
+ if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) {
+ // Inner point lies outside the circle.
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * This structure is used to pass rectangles to our shader constant. We can use
+ * this for passing rectangular areas to SetVertexShaderConstant. In the format
+ * of a 4 component float(x,y,width,height). Our vertex shader can then use
+ * this to construct rectangular positions from the 0,0-1,1 quad that we source
+ * it with.
+ */
+struct ShaderConstantRectD3D10
+{
+ float mX, mY, mWidth, mHeight;
+ ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight)
+ : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
+ { }
+
+ // For easy passing to SetVertexShaderConstantF.
+ operator float* () { return &mX; }
+};
+
+static inline DWRITE_MATRIX
+DWriteMatrixFromMatrix(Matrix &aMatrix)
+{
+ DWRITE_MATRIX mat;
+ mat.m11 = aMatrix._11;
+ mat.m12 = aMatrix._12;
+ mat.m21 = aMatrix._21;
+ mat.m22 = aMatrix._22;
+ mat.dx = aMatrix._31;
+ mat.dy = aMatrix._32;
+ return mat;
+}
+
+class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
+{
+ static const unsigned kNumAutoGlyphs = 256;
+
+public:
+ AutoDWriteGlyphRun() {
+ glyphCount = 0;
+ }
+
+ ~AutoDWriteGlyphRun() {
+ if (glyphCount > kNumAutoGlyphs) {
+ delete[] glyphIndices;
+ delete[] glyphAdvances;
+ delete[] glyphOffsets;
+ }
+ }
+
+ void allocate(unsigned aNumGlyphs) {
+ glyphCount = aNumGlyphs;
+ if (aNumGlyphs <= kNumAutoGlyphs) {
+ glyphIndices = &mAutoIndices[0];
+ glyphAdvances = &mAutoAdvances[0];
+ glyphOffsets = &mAutoOffsets[0];
+ } else {
+ glyphIndices = new UINT16[aNumGlyphs];
+ glyphAdvances = new FLOAT[aNumGlyphs];
+ glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
+ }
+ }
+
+private:
+ DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
+ FLOAT mAutoAdvances[kNumAutoGlyphs];
+ UINT16 mAutoIndices[kNumAutoGlyphs];
+};
+
+static inline void
+DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run)
+{
+ run->allocate(aGlyphs.mNumGlyphs);
+
+ FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
+ UINT16 *indices = const_cast<UINT16*>(run->glyphIndices);
+ DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
+
+ memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs);
+ for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) {
+ indices[i] = aGlyphs.mGlyphs[i].mIndex;
+ offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x;
+ offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y;
+ }
+
+ run->bidiLevel = 0;
+ run->fontFace = aFont->mFontFace;
+ run->fontEmSize = aFont->GetSize();
+ run->glyphCount = aGlyphs.mNumGlyphs;
+ run->isSideways = FALSE;
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+ConvertRectToGeometry(const D2D1_RECT_F& aRect)
+{
+ RefPtr<ID2D1RectangleGeometry> rectGeom;
+ D2DFactory()->CreateRectangleGeometry(&aRect, getter_AddRefs(rectGeom));
+ return rectGeom.forget();
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+{
+ RefPtr<ID2D1PathGeometry> tmpGeometry;
+ D2DFactory()->CreatePathGeometry(getter_AddRefs(tmpGeometry));
+ RefPtr<ID2D1GeometrySink> currentSink;
+ tmpGeometry->Open(getter_AddRefs(currentSink));
+ aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ aTransform, currentSink);
+ currentSink->Close();
+ return tmpGeometry.forget();
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
+{
+ RefPtr<ID2D1PathGeometry> pathGeom;
+ D2DFactory()->CreatePathGeometry(getter_AddRefs(pathGeom));
+ RefPtr<ID2D1GeometrySink> sink;
+ pathGeom->Open(getter_AddRefs(sink));
+ aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
+ sink->Close();
+
+ return pathGeom.forget();
+}
+
+static inline already_AddRefed<ID2D1StrokeStyle>
+CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
+{
+ RefPtr<ID2D1StrokeStyle> style;
+
+ D2D1_CAP_STYLE capStyle;
+ D2D1_LINE_JOIN joinStyle;
+
+ switch (aStrokeOptions.mLineCap) {
+ case CapStyle::BUTT:
+ capStyle = D2D1_CAP_STYLE_FLAT;
+ break;
+ case CapStyle::ROUND:
+ capStyle = D2D1_CAP_STYLE_ROUND;
+ break;
+ case CapStyle::SQUARE:
+ capStyle = D2D1_CAP_STYLE_SQUARE;
+ break;
+ }
+
+ switch (aStrokeOptions.mLineJoin) {
+ case JoinStyle::MITER:
+ joinStyle = D2D1_LINE_JOIN_MITER;
+ break;
+ case JoinStyle::MITER_OR_BEVEL:
+ joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
+ break;
+ case JoinStyle::ROUND:
+ joinStyle = D2D1_LINE_JOIN_ROUND;
+ break;
+ case JoinStyle::BEVEL:
+ joinStyle = D2D1_LINE_JOIN_BEVEL;
+ break;
+ }
+
+
+ HRESULT hr;
+ // We need to check mDashLength in addition to mDashPattern here since if
+ // mDashPattern is set but mDashLength is zero then the stroke will fail to
+ // paint.
+ if (aStrokeOptions.mDashLength > 0 && aStrokeOptions.mDashPattern) {
+ typedef std::vector<Float> FloatVector;
+ // D2D "helpfully" multiplies the dash pattern by the line width.
+ // That's not what cairo does, or is what <canvas>'s dash wants.
+ // So fix the multiplication in advance.
+ Float lineWidth = aStrokeOptions.mLineWidth;
+ FloatVector dash(aStrokeOptions.mDashPattern,
+ aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength);
+ for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) {
+ *it /= lineWidth;
+ }
+
+ hr = D2DFactory()->CreateStrokeStyle(
+ D2D1::StrokeStyleProperties(capStyle, capStyle,
+ capStyle, joinStyle,
+ aStrokeOptions.mMiterLimit,
+ D2D1_DASH_STYLE_CUSTOM,
+ aStrokeOptions.mDashOffset / lineWidth),
+ &dash[0], // data() is not C++98, although it's in recent gcc
+ // and VC10's STL
+ dash.size(),
+ getter_AddRefs(style));
+ } else {
+ hr = D2DFactory()->CreateStrokeStyle(
+ D2D1::StrokeStyleProperties(capStyle, capStyle,
+ capStyle, joinStyle,
+ aStrokeOptions.mMiterLimit),
+ nullptr, 0, getter_AddRefs(style));
+ }
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create Direct2D stroke style.";
+ }
+
+ return style.forget();
+}
+
+// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
+// uploads the minimum requirement and possibly downscales. It adjusts the
+// input Matrix to compensate.
+static inline already_AddRefed<ID2D1Bitmap>
+CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
+ const IntSize &aDestinationSize, ExtendMode aExtendMode,
+ Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
+ const IntRect* aSourceRect = nullptr)
+{
+ RefPtr<ID2D1Bitmap> bitmap;
+
+ // This is where things get complicated. The source surface was
+ // created for a surface that was too large to fit in a texture.
+ // We'll need to figure out if we can work with a partial upload
+ // or downsample in software.
+
+ Matrix transform = aDestinationTransform;
+ Matrix invTransform = transform = aSourceTransform * transform;
+ if (!invTransform.Invert()) {
+ // Singular transform, nothing to be drawn.
+ return nullptr;
+ }
+
+ Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height));
+
+ // Calculate the rectangle of the source mapped to our surface.
+ rect = invTransform.TransformBounds(rect);
+ rect.RoundOut();
+
+ IntSize size = aSurface->GetSize();
+
+ Rect uploadRect(0, 0, Float(size.width), Float(size.height));
+ if (aSourceRect) {
+ uploadRect = Rect(aSourceRect->x, aSourceRect->y, aSourceRect->width, aSourceRect->height);
+ }
+
+ // Limit the uploadRect as much as possible without supporting discontiguous uploads
+ //
+ // region we will paint from
+ // uploadRect
+ // .---------------. .---------------. resulting uploadRect
+ // | |rect | |
+ // | .---------. .----. .----. .---------------.
+ // | | | ----> | | | | ----> | |
+ // | '---------' '----' '----' '---------------'
+ // '---------------' '---------------'
+ //
+ //
+
+ if (uploadRect.Contains(rect)) {
+ // Extend mode is irrelevant, the displayed rect is completely contained
+ // by the source bitmap.
+ uploadRect = rect;
+ } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
+ // Calculate the rectangle on the source bitmap that touches our
+ // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
+ // correct behaviour in this case.
+ uploadRect = uploadRect.Intersect(rect);
+
+ // We now proceed to check if we can limit at least one dimension of the
+ // upload rect safely without looking at extend mode.
+ } else if (rect.x >= 0 && rect.XMost() < size.width) {
+ uploadRect.x = rect.x;
+ uploadRect.width = rect.width;
+ } else if (rect.y >= 0 && rect.YMost() < size.height) {
+ uploadRect.y = rect.y;
+ uploadRect.height = rect.height;
+ }
+
+ if (uploadRect.IsEmpty()) {
+ // Nothing to be drawn.
+ return nullptr;
+ }
+
+ if (uploadRect.width <= aRT->GetMaximumBitmapSize() &&
+ uploadRect.height <= aRT->GetMaximumBitmapSize()) {
+ {
+ // Scope to auto-Unmap() |mapping|.
+ DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ // A partial upload will suffice.
+ aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
+ mapping.GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * mapping.GetStride(),
+ mapping.GetStride(),
+ D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
+ getter_AddRefs(bitmap));
+ }
+
+ aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y);
+
+ return bitmap.forget();
+ } else {
+ int Bpp = BytesPerPixel(aSurface->GetFormat());
+
+ if (Bpp != 4) {
+ // This shouldn't actually happen in practice!
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ {
+ // Scope to auto-Unmap() |mapping|.
+ DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+ ImageHalfScaler scaler(mapping.GetData(), mapping.GetStride(), size);
+
+ // Calculate the maximum width/height of the image post transform.
+ Point topRight = transform.TransformPoint(Point(Float(size.width), 0));
+ Point topLeft = transform.TransformPoint(Point(0, 0));
+ Point bottomRight = transform.TransformPoint(Point(Float(size.width), Float(size.height)));
+ Point bottomLeft = transform.TransformPoint(Point(0, Float(size.height)));
+
+ IntSize scaleSize;
+
+ scaleSize.width = int32_t(std::max(Distance(topRight, topLeft),
+ Distance(bottomRight, bottomLeft)));
+ scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight),
+ Distance(topLeft, bottomLeft)));
+
+ if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) {
+ // Ok, in this case we'd really want a downscale of a part of the bitmap,
+ // perhaps we can do this later but for simplicity let's do something
+ // different here and assume it's good enough, this should be rare!
+ scaleSize.width = 4095;
+ }
+ if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) {
+ scaleSize.height = 4095;
+ }
+
+ scaler.ScaleForSize(scaleSize);
+
+ IntSize newSize = scaler.GetSize();
+
+ if (newSize.IsEmpty()) {
+ return nullptr;
+ }
+
+ aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
+ scaler.GetScaledData(), scaler.GetStride(),
+ D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
+ getter_AddRefs(bitmap));
+
+ aSourceTransform.PreScale(Float(size.width) / newSize.width,
+ Float(size.height) / newSize.height);
+ }
+ return bitmap.forget();
+ }
+}
+
+static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect)
+{
+ aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED);
+ aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top));
+ aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom));
+ aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom));
+ aSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+}
+
+class DCCommandSink : public ID2D1CommandSink
+{
+public:
+ DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+ {
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else if (aIID == IID_ID2D1CommandSink) {
+ *aPtr = static_cast<ID2D1CommandSink*>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ STDMETHODIMP BeginDraw()
+ {
+ // We don't want to do anything here!
+ return S_OK;
+ }
+ STDMETHODIMP EndDraw()
+ {
+ // We don't want to do anything here!
+ return S_OK;
+ }
+
+ STDMETHODIMP SetAntialiasMode(
+ D2D1_ANTIALIAS_MODE antialiasMode
+ )
+ {
+ mCtx->SetAntialiasMode(antialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2)
+ {
+ mCtx->SetTags(tag1, tag2);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode)
+ {
+ mCtx->SetTextAntialiasMode(textAntialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams)
+ {
+ mCtx->SetTextRenderingParams(textRenderingParams);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform)
+ {
+ mCtx->SetTransform(transform);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend)
+ {
+ mCtx->SetPrimitiveBlend(primitiveBlend);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode)
+ {
+ mCtx->SetUnitMode(unitMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color)
+ {
+ mCtx->Clear(color);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGlyphRun(
+ D2D1_POINT_2F baselineOrigin,
+ _In_ CONST DWRITE_GLYPH_RUN *glyphRun,
+ _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription,
+ _In_ ID2D1Brush *foregroundBrush,
+ DWRITE_MEASURING_MODE measuringMode
+ )
+ {
+ mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription,
+ foregroundBrush, measuringMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawLine(
+ D2D1_POINT_2F point0,
+ D2D1_POINT_2F point1,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGeometry(
+ _In_ ID2D1Geometry *geometry,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawRectangle(
+ _In_ CONST D2D1_RECT_F *rect,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawBitmap(
+ _In_ ID2D1Bitmap *bitmap,
+ _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+ FLOAT opacity,
+ D2D1_INTERPOLATION_MODE interpolationMode,
+ _In_opt_ CONST D2D1_RECT_F *sourceRectangle,
+ _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform
+ )
+ {
+ mCtx->DrawBitmap(bitmap, destinationRectangle, opacity,
+ interpolationMode, sourceRectangle,
+ perspectiveTransform);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawImage(
+ _In_ ID2D1Image *image,
+ _In_opt_ CONST D2D1_POINT_2F *targetOffset,
+ _In_opt_ CONST D2D1_RECT_F *imageRectangle,
+ D2D1_INTERPOLATION_MODE interpolationMode,
+ D2D1_COMPOSITE_MODE compositeMode
+ )
+ {
+ mCtx->DrawImage(image, targetOffset, imageRectangle,
+ interpolationMode, compositeMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGdiMetafile(
+ _In_ ID2D1GdiMetafile *gdiMetafile,
+ _In_opt_ CONST D2D1_POINT_2F *targetOffset
+ )
+ {
+ mCtx->DrawGdiMetafile(gdiMetafile, targetOffset);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillMesh(
+ _In_ ID2D1Mesh *mesh,
+ _In_ ID2D1Brush *brush
+ )
+ {
+ mCtx->FillMesh(mesh, brush);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillOpacityMask(
+ _In_ ID2D1Bitmap *opacityMask,
+ _In_ ID2D1Brush *brush,
+ _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+ _In_opt_ CONST D2D1_RECT_F *sourceRectangle
+ )
+ {
+ mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle,
+ sourceRectangle);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillGeometry(
+ _In_ ID2D1Geometry *geometry,
+ _In_ ID2D1Brush *brush,
+ _In_opt_ ID2D1Brush *opacityBrush
+ )
+ {
+ mCtx->FillGeometry(geometry, brush, opacityBrush);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillRectangle(
+ _In_ CONST D2D1_RECT_F *rect,
+ _In_ ID2D1Brush *brush
+ )
+ {
+ mCtx->FillRectangle(rect, brush);
+ return S_OK;
+ }
+
+ STDMETHODIMP PushAxisAlignedClip(
+ _In_ CONST D2D1_RECT_F *clipRect,
+ D2D1_ANTIALIAS_MODE antialiasMode
+ )
+ {
+ mCtx->PushAxisAlignedClip(clipRect, antialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP PushLayer(
+ _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1,
+ _In_opt_ ID2D1Layer *layer
+ )
+ {
+ mCtx->PushLayer(layerParameters1, layer);
+ return S_OK;
+ }
+
+ STDMETHODIMP PopAxisAlignedClip()
+ {
+ mCtx->PopAxisAlignedClip();
+ return S_OK;
+ }
+
+ STDMETHODIMP PopLayer()
+ {
+ mCtx->PopLayer();
+ return S_OK;
+ }
+
+ ID2D1DeviceContext* mCtx;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_HELPERSD2D_H_ */
diff --git a/system/graphics/2d/HelpersSkia.h b/system/graphics/2d/HelpersSkia.h
new file mode 100644
index 000000000..95c67ad05
--- /dev/null
+++ b/system/graphics/2d/HelpersSkia.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_HELPERSSKIA_H_
+#define MOZILLA_GFX_HELPERSSKIA_H_
+
+#include "2D.h"
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/effects/SkDashPathEffect.h"
+#include "skia/include/core/SkShader.h"
+#ifdef USE_SKIA_GPU
+#include "skia/include/gpu/GrTypes.h"
+#endif
+#include "mozilla/Assertions.h"
+#include <vector>
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace gfx {
+
+static inline SkColorType
+GfxFormatToSkiaColorType(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8A8:
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::B8G8R8X8:
+ // We probably need to do something here.
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kRGB_565_SkColorType;
+ case SurfaceFormat::A8:
+ return kAlpha_8_SkColorType;
+ default:
+ return kRGBA_8888_SkColorType;
+ }
+}
+
+static inline SurfaceFormat
+SkiaColorTypeToGfxFormat(SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType)
+{
+ switch (aColorType)
+ {
+ case kBGRA_8888_SkColorType:
+ return aAlphaType == kOpaque_SkAlphaType ?
+ SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
+ case kRGB_565_SkColorType:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case kAlpha_8_SkColorType:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+static inline SkAlphaType
+GfxFormatToSkiaAlphaType(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kOpaque_SkAlphaType;
+ default:
+ return kPremul_SkAlphaType;
+ }
+}
+
+static inline SkImageInfo
+MakeSkiaImageInfo(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ return SkImageInfo::Make(aSize.width, aSize.height,
+ GfxFormatToSkiaColorType(aFormat),
+ GfxFormatToSkiaAlphaType(aFormat));
+}
+
+#ifdef USE_SKIA_GPU
+static inline GrPixelConfig
+GfxFormatToGrConfig(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8A8:
+ return kBGRA_8888_GrPixelConfig;
+ case SurfaceFormat::B8G8R8X8:
+ // We probably need to do something here.
+ return kBGRA_8888_GrPixelConfig;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kRGB_565_GrPixelConfig;
+ case SurfaceFormat::A8:
+ return kAlpha_8_GrPixelConfig;
+ default:
+ return kRGBA_8888_GrPixelConfig;
+ }
+
+}
+#endif
+static inline void
+GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval)
+{
+ retval.setAll(SkFloatToScalar(mat._11), SkFloatToScalar(mat._21), SkFloatToScalar(mat._31),
+ SkFloatToScalar(mat._12), SkFloatToScalar(mat._22), SkFloatToScalar(mat._32),
+ 0, 0, SK_Scalar1);
+}
+
+static inline void
+GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix, SkMatrix& aResult)
+{
+ aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21), SkFloatToScalar(aMatrix._41),
+ SkFloatToScalar(aMatrix._12), SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42),
+ SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24), SkFloatToScalar(aMatrix._44));
+}
+
+static inline SkPaint::Cap
+CapStyleToSkiaCap(CapStyle aCap)
+{
+ switch (aCap)
+ {
+ case CapStyle::BUTT:
+ return SkPaint::kButt_Cap;
+ case CapStyle::ROUND:
+ return SkPaint::kRound_Cap;
+ case CapStyle::SQUARE:
+ return SkPaint::kSquare_Cap;
+ }
+ return SkPaint::kDefault_Cap;
+}
+
+static inline SkPaint::Join
+JoinStyleToSkiaJoin(JoinStyle aJoin)
+{
+ switch (aJoin)
+ {
+ case JoinStyle::BEVEL:
+ return SkPaint::kBevel_Join;
+ case JoinStyle::ROUND:
+ return SkPaint::kRound_Join;
+ case JoinStyle::MITER:
+ case JoinStyle::MITER_OR_BEVEL:
+ return SkPaint::kMiter_Join;
+ }
+ return SkPaint::kDefault_Join;
+}
+
+static inline bool
+StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
+{
+ // Skia renders 0 width strokes with a width of 1 (and in black),
+ // so we should just skip the draw call entirely.
+ // Skia does not handle non-finite line widths.
+ if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
+ return false;
+ }
+ aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
+ aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
+ aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
+ aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
+
+ if (aOptions.mDashLength > 0) {
+ // Skia only supports dash arrays that are multiples of 2.
+ uint32_t dashCount;
+
+ if (aOptions.mDashLength % 2 == 0) {
+ dashCount = aOptions.mDashLength;
+ } else {
+ dashCount = aOptions.mDashLength * 2;
+ }
+
+ std::vector<SkScalar> pattern;
+ pattern.resize(dashCount);
+
+ for (uint32_t i = 0; i < dashCount; i++) {
+ pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]);
+ }
+
+ sk_sp<SkPathEffect> dash = SkDashPathEffect::Make(&pattern.front(),
+ dashCount,
+ SkFloatToScalar(aOptions.mDashOffset));
+ aPaint.setPathEffect(dash);
+ }
+
+ aPaint.setStyle(SkPaint::kStroke_Style);
+ return true;
+}
+
+static inline SkBlendMode
+GfxOpToSkiaOp(CompositionOp op)
+{
+ switch (op)
+ {
+ case CompositionOp::OP_OVER:
+ return SkBlendMode::kSrcOver;
+ case CompositionOp::OP_ADD:
+ return SkBlendMode::kPlus;
+ case CompositionOp::OP_ATOP:
+ return SkBlendMode::kSrcATop;
+ case CompositionOp::OP_OUT:
+ return SkBlendMode::kSrcOut;
+ case CompositionOp::OP_IN:
+ return SkBlendMode::kSrcIn;
+ case CompositionOp::OP_SOURCE:
+ return SkBlendMode::kSrc;
+ case CompositionOp::OP_DEST_IN:
+ return SkBlendMode::kDstIn;
+ case CompositionOp::OP_DEST_OUT:
+ return SkBlendMode::kDstOut;
+ case CompositionOp::OP_DEST_OVER:
+ return SkBlendMode::kDstOver;
+ case CompositionOp::OP_DEST_ATOP:
+ return SkBlendMode::kDstATop;
+ case CompositionOp::OP_XOR:
+ return SkBlendMode::kXor;
+ case CompositionOp::OP_MULTIPLY:
+ return SkBlendMode::kMultiply;
+ case CompositionOp::OP_SCREEN:
+ return SkBlendMode::kScreen;
+ case CompositionOp::OP_OVERLAY:
+ return SkBlendMode::kOverlay;
+ case CompositionOp::OP_DARKEN:
+ return SkBlendMode::kDarken;
+ case CompositionOp::OP_LIGHTEN:
+ return SkBlendMode::kLighten;
+ case CompositionOp::OP_COLOR_DODGE:
+ return SkBlendMode::kColorDodge;
+ case CompositionOp::OP_COLOR_BURN:
+ return SkBlendMode::kColorBurn;
+ case CompositionOp::OP_HARD_LIGHT:
+ return SkBlendMode::kHardLight;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return SkBlendMode::kSoftLight;
+ case CompositionOp::OP_DIFFERENCE:
+ return SkBlendMode::kDifference;
+ case CompositionOp::OP_EXCLUSION:
+ return SkBlendMode::kExclusion;
+ case CompositionOp::OP_HUE:
+ return SkBlendMode::kHue;
+ case CompositionOp::OP_SATURATION:
+ return SkBlendMode::kSaturation;
+ case CompositionOp::OP_COLOR:
+ return SkBlendMode::kColor;
+ case CompositionOp::OP_LUMINOSITY:
+ return SkBlendMode::kLuminosity;
+ default:
+ return SkBlendMode::kSrcOver;
+ }
+}
+
+/* There's quite a bit of inconsistency about
+ * whether float colors should be rounded with .5f.
+ * We choose to do it to match cairo which also
+ * happens to match the Direct3D specs */
+static inline U8CPU ColorFloatToByte(Float color)
+{
+ //XXX: do a better job converting to int
+ return U8CPU(color*255.f + .5f);
+};
+
+static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
+{
+ return SkColorSetARGB(ColorFloatToByte(color.a*aAlpha), ColorFloatToByte(color.r),
+ ColorFloatToByte(color.g), ColorFloatToByte(color.b));
+}
+
+static inline SkPoint
+PointToSkPoint(const Point &aPoint)
+{
+ return SkPoint::Make(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+}
+
+static inline SkRect
+RectToSkRect(const Rect& aRect)
+{
+ return SkRect::MakeXYWH(SkFloatToScalar(aRect.x), SkFloatToScalar(aRect.y),
+ SkFloatToScalar(aRect.width), SkFloatToScalar(aRect.height));
+}
+
+static inline SkRect
+IntRectToSkRect(const IntRect& aRect)
+{
+ return SkRect::MakeXYWH(SkIntToScalar(aRect.x), SkIntToScalar(aRect.y),
+ SkIntToScalar(aRect.width), SkIntToScalar(aRect.height));
+}
+
+static inline SkIRect
+RectToSkIRect(const Rect& aRect)
+{
+ return SkIRect::MakeXYWH(int32_t(aRect.x), int32_t(aRect.y),
+ int32_t(aRect.width), int32_t(aRect.height));
+}
+
+static inline SkIRect
+IntRectToSkIRect(const IntRect& aRect)
+{
+ return SkIRect::MakeXYWH(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+static inline Point
+SkPointToPoint(const SkPoint &aPoint)
+{
+ return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y()));
+}
+
+static inline Rect
+SkRectToRect(const SkRect &aRect)
+{
+ return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()),
+ SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height()));
+}
+
+static inline SkShader::TileMode
+ExtendModeToTileMode(ExtendMode aMode, Axis aAxis)
+{
+ switch (aMode)
+ {
+ case ExtendMode::CLAMP:
+ return SkShader::kClamp_TileMode;
+ case ExtendMode::REPEAT:
+ return SkShader::kRepeat_TileMode;
+ case ExtendMode::REFLECT:
+ return SkShader::kMirror_TileMode;
+ case ExtendMode::REPEAT_X:
+ {
+ return aAxis == Axis::X_AXIS
+ ? SkShader::kRepeat_TileMode
+ : SkShader::kClamp_TileMode;
+ }
+ case ExtendMode::REPEAT_Y:
+ {
+ return aAxis == Axis::Y_AXIS
+ ? SkShader::kRepeat_TileMode
+ : SkShader::kClamp_TileMode;
+ }
+ }
+ return SkShader::kClamp_TileMode;
+}
+
+static inline SkPaint::Hinting
+GfxHintingToSkiaHinting(FontHinting aHinting)
+{
+ switch (aHinting) {
+ case FontHinting::NONE:
+ return SkPaint::kNo_Hinting;
+ case FontHinting::LIGHT:
+ return SkPaint::kSlight_Hinting;
+ case FontHinting::NORMAL:
+ return SkPaint::kNormal_Hinting;
+ case FontHinting::FULL:
+ return SkPaint::kFull_Hinting;
+ }
+ return SkPaint::kNormal_Hinting;
+}
+
+static inline FillRule GetFillRule(SkPath::FillType aFillType)
+{
+ switch (aFillType)
+ {
+ case SkPath::kWinding_FillType:
+ return FillRule::FILL_WINDING;
+ case SkPath::kEvenOdd_FillType:
+ return FillRule::FILL_EVEN_ODD;
+ case SkPath::kInverseWinding_FillType:
+ case SkPath::kInverseEvenOdd_FillType:
+ default:
+ NS_WARNING("Unsupported fill type\n");
+ break;
+ }
+
+ return FillRule::FILL_EVEN_ODD;
+}
+
+/**
+ * Returns true if the canvas is backed by pixels. Returns false if the canvas
+ * wraps an SkPDFDocument, for example.
+ *
+ * Note: It is not clear whether the test used to implement this function may
+ * result in it returning false in some circumstances even when the canvas
+ * _is_ pixel backed. In other words maybe it is possible for such a canvas to
+ * have kUnknown_SkPixelGeometry?
+ */
+static inline bool IsBackedByPixels(const SkCanvas* aCanvas)
+{
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ if (!aCanvas->getProps(&props) ||
+ props.pixelGeometry() == kUnknown_SkPixelGeometry) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_HELPERSSKIA_H_ */
diff --git a/system/graphics/2d/HelpersWinFonts.h b/system/graphics/2d/HelpersWinFonts.h
new file mode 100644
index 000000000..cd84070d4
--- /dev/null
+++ b/system/graphics/2d/HelpersWinFonts.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace gfx {
+
+// Cleartype can be dynamically enabled/disabled, so we have to check it
+// everytime we want to render some text.
+static BYTE
+GetSystemTextQuality()
+{
+ BOOL font_smoothing;
+ UINT smoothing_type;
+
+ if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
+ return DEFAULT_QUALITY;
+ }
+
+ if (font_smoothing) {
+ if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE,
+ 0, &smoothing_type, 0)) {
+ return DEFAULT_QUALITY;
+ }
+
+ if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
+ return CLEARTYPE_QUALITY;
+ }
+
+ return ANTIALIASED_QUALITY;
+ }
+
+ return DEFAULT_QUALITY;
+}
+
+static AntialiasMode
+GetSystemDefaultAAMode()
+{
+ AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
+ if (gfxPrefs::DisableAllTextAA()) {
+ return AntialiasMode::NONE;
+ }
+
+ switch (GetSystemTextQuality()) {
+ case CLEARTYPE_QUALITY:
+ defaultMode = AntialiasMode::SUBPIXEL;
+ break;
+ case ANTIALIASED_QUALITY:
+ defaultMode = AntialiasMode::GRAY;
+ break;
+ case DEFAULT_QUALITY:
+ defaultMode = AntialiasMode::NONE;
+ break;
+ }
+
+ return defaultMode;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/ImageScaling.cpp b/system/graphics/2d/ImageScaling.cpp
new file mode 100644
index 000000000..190b7a7b9
--- /dev/null
+++ b/system/graphics/2d/ImageScaling.cpp
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageScaling.h"
+#include "2D.h"
+#include "DataSurfaceHelpers.h"
+
+#include <math.h>
+#include <algorithm>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ // Prepare half-adder work
+ uint32_t sum = a ^ b ^ c;
+ uint32_t carry = (a & b) | (a & c) | (b & c);
+
+ // Before shifting, mask lower order bits of each byte to avoid underflow.
+ uint32_t mask = 0xfefefefe;
+
+ // Add d to sum and divide by 2.
+ sum = (((sum ^ d) & mask) >> 1) + (sum & d);
+
+ // Sum is now shifted into place relative to carry, add them together.
+ return (((sum ^ carry) & mask) >> 1) + (sum & carry);
+}
+
+inline uint32_t Avg2(uint32_t a, uint32_t b)
+{
+ // Prepare half-adder work
+ uint32_t sum = a ^ b;
+ uint32_t carry = (a & b);
+
+ // Before shifting, mask lower order bits of each byte to avoid underflow.
+ uint32_t mask = 0xfefefefe;
+
+ // Add d to sum and divide by 2.
+ return ((sum & mask) >> 1) + carry;
+}
+
+void
+ImageHalfScaler::ScaleForSize(const IntSize &aSize)
+{
+ uint32_t horizontalDownscales = 0;
+ uint32_t verticalDownscales = 0;
+
+ IntSize scaleSize = mOrigSize;
+ while ((scaleSize.height / 2) > aSize.height) {
+ verticalDownscales++;
+ scaleSize.height /= 2;
+ }
+
+ while ((scaleSize.width / 2) > aSize.width) {
+ horizontalDownscales++;
+ scaleSize.width /= 2;
+ }
+
+ if (scaleSize == mOrigSize) {
+ return;
+ }
+
+ delete [] mDataStorage;
+
+ IntSize internalSurfSize;
+ internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
+ internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
+
+ size_t bufLen = 0;
+ mStride = GetAlignedStride<16>(internalSurfSize.width, 4);
+ if (mStride > 0) {
+ // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
+ // should add tools for this, see bug 751696.
+ bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
+ }
+
+ if (bufLen == 0) {
+ mSize.SizeTo(0, 0);
+ mDataStorage = nullptr;
+ return;
+ }
+ mDataStorage = new uint8_t[bufLen];
+
+ if (uintptr_t(mDataStorage) % 16) {
+ // Our storage does not start at a 16-byte boundary. Make sure mData does!
+ mData = (uint8_t*)(uintptr_t(mDataStorage) +
+ (16 - (uintptr_t(mDataStorage) % 16)));
+ } else {
+ mData = mDataStorage;
+ }
+
+ mSize = scaleSize;
+
+ /* The surface we sample from might not be even sized, if it's not we will
+ * ignore the last row/column. This means we lose some data but it keeps the
+ * code very simple. There's also no perfect answer that provides a better
+ * solution.
+ */
+ IntSize currentSampledSize = mOrigSize;
+ uint32_t currentSampledStride = mOrigStride;
+ uint8_t *currentSampledData = mOrigData;
+
+ while (verticalDownscales && horizontalDownscales) {
+ if (currentSampledSize.width % 2) {
+ currentSampledSize.width -= 1;
+ }
+ if (currentSampledSize.height % 2) {
+ currentSampledSize.height -= 1;
+ }
+
+ HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ verticalDownscales--;
+ horizontalDownscales--;
+ currentSampledSize.width /= 2;
+ currentSampledSize.height /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+
+ while (verticalDownscales) {
+ if (currentSampledSize.height % 2) {
+ currentSampledSize.height -= 1;
+ }
+
+ HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ verticalDownscales--;
+ currentSampledSize.height /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+
+
+ while (horizontalDownscales) {
+ if (currentSampledSize.width % 2) {
+ currentSampledSize.width -= 1;
+ }
+
+ HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ horizontalDownscales--;
+ currentSampledSize.width /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+}
+
+void
+ImageHalfScaler::HalfImage2D(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x += 2) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ *storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
+ *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x++) {
+ uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+ uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
+
+ *storage++ = Avg2(*upperRow, *lowerRow);
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y++) {
+ uint32_t *storage = (uint32_t*)(aDest + y * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x+= 2) {
+ uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+
+ *storage++ = Avg2(*pixels, *(pixels + 1));
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/ImageScaling.h b/system/graphics/2d/ImageScaling.h
new file mode 100644
index 000000000..56173b644
--- /dev/null
+++ b/system/graphics/2d/ImageScaling.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_IMAGESCALING_H
+#define _MOZILLA_GFX_IMAGESCALING_H
+
+#include "Types.h"
+
+#include <vector>
+#include "Point.h"
+
+namespace mozilla {
+namespace gfx {
+
+class ImageHalfScaler
+{
+public:
+ ImageHalfScaler(uint8_t *aData, int32_t aStride, const IntSize &aSize)
+ : mOrigData(aData), mOrigStride(aStride), mOrigSize(aSize)
+ , mDataStorage(nullptr)
+ {
+ }
+
+ ~ImageHalfScaler()
+ {
+ delete [] mDataStorage;
+ }
+
+ void ScaleForSize(const IntSize &aSize);
+
+ uint8_t *GetScaledData() const { return mData; }
+ IntSize GetSize() const { return mSize; }
+ uint32_t GetStride() const { return mStride; }
+
+private:
+ void HalfImage2D(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ // This is our SSE2 scaling function. Our destination must always be 16-byte
+ // aligned and use a 16-byte aligned stride.
+ void HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ void HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ uint8_t *mOrigData;
+ int32_t mOrigStride;
+ IntSize mOrigSize;
+
+ uint8_t *mDataStorage;
+ // Guaranteed 16-byte aligned
+ uint8_t *mData;
+ IntSize mSize;
+ // Guaranteed 16-byte aligned
+ uint32_t mStride;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/ImageScalingSSE2.cpp b/system/graphics/2d/ImageScalingSSE2.cpp
new file mode 100644
index 000000000..1f4d4778e
--- /dev/null
+++ b/system/graphics/2d/ImageScalingSSE2.cpp
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageScaling.h"
+#include "mozilla/Attributes.h"
+
+#include "SSEHelpers.h"
+
+/* The functions below use the following system for averaging 4 pixels:
+ *
+ * The first observation is that a half-adder is implemented as follows:
+ * R = S + 2C or in the case of a and b (a ^ b) + ((a & b) << 1);
+ *
+ * This can be trivially extended to three pixels by observaring that when
+ * doing (a ^ b ^ c) as the sum, the carry is simply the bitwise-or of the
+ * carries of the individual numbers, since the sum of 3 bits can only ever
+ * have a carry of one.
+ *
+ * We then observe that the average is then ((carry << 1) + sum) >> 1, or,
+ * assuming eliminating overflows and underflows, carry + (sum >> 1).
+ *
+ * We now average our existing sum with the fourth number, so we get:
+ * sum2 = (sum + d) >> 1 or (sum >> 1) + (d >> 1).
+ *
+ * We now observe that our sum has been moved into place relative to the
+ * carry, so we can now average with the carry to get the final 4 input
+ * average: avg = (sum2 + carry) >> 1;
+ *
+ * Or to reverse the proof:
+ * avg = ((sum >> 1) + carry + d >> 1) >> 1
+ * avg = ((a + b + c) >> 1 + d >> 1) >> 1
+ * avg = ((a + b + c + d) >> 2)
+ *
+ * An additional fact used in the SSE versions is the concept that we can
+ * trivially convert a rounded average to a truncated average:
+ *
+ * We have:
+ * f(a, b) = (a + b + 1) >> 1
+ *
+ * And want:
+ * g(a, b) = (a + b) >> 1
+ *
+ * Observe:
+ * ~f(~a, ~b) == ~((~a + ~b + 1) >> 1)
+ * == ~((-a - 1 + -b - 1 + 1) >> 1)
+ * == ~((-a - 1 + -b) >> 1)
+ * == ~((-(a + b) - 1) >> 1)
+ * == ~((~(a + b)) >> 1)
+ * == (a + b) >> 1
+ * == g(a, b)
+ */
+
+MOZ_ALWAYS_INLINE __m128i _mm_not_si128(__m128i arg)
+{
+ __m128i minusone = _mm_set1_epi32(0xffffffff);
+ return _mm_xor_si128(arg, minusone);
+}
+
+/* We have to pass pointers here, MSVC does not allow passing more than 3
+ * __m128i arguments on the stack. And it does not allow 16-byte aligned
+ * stack variables. This inlines properly on MSVC 2010. It does -not- inline
+ * with just the inline directive.
+ */
+MOZ_ALWAYS_INLINE __m128i avg_sse2_8x2(__m128i *a, __m128i *b, __m128i *c, __m128i *d)
+{
+#define shuf1 _MM_SHUFFLE(2, 0, 2, 0)
+#define shuf2 _MM_SHUFFLE(3, 1, 3, 1)
+
+// This cannot be an inline function as the __Imm argument to _mm_shuffle_ps
+// needs to be a compile time constant.
+#define shuffle_si128(arga, argb, imm) \
+ _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps((arga)), _mm_castsi128_ps((argb)), (imm)));
+
+ __m128i t = shuffle_si128(*a, *b, shuf1);
+ *b = shuffle_si128(*a, *b, shuf2);
+ *a = t;
+ t = shuffle_si128(*c, *d, shuf1);
+ *d = shuffle_si128(*c, *d, shuf2);
+ *c = t;
+
+#undef shuf1
+#undef shuf2
+#undef shuffle_si128
+
+ __m128i sum = _mm_xor_si128(*a, _mm_xor_si128(*b, *c));
+
+ __m128i carry = _mm_or_si128(_mm_and_si128(*a, *b), _mm_or_si128(_mm_and_si128(*a, *c), _mm_and_si128(*b, *c)));
+
+ sum = _mm_avg_epu8(_mm_not_si128(sum), _mm_not_si128(*d));
+
+ return _mm_not_si128(_mm_avg_epu8(sum, _mm_not_si128(carry)));
+}
+
+MOZ_ALWAYS_INLINE __m128i avg_sse2_4x2_4x1(__m128i a, __m128i b)
+{
+ return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
+}
+
+MOZ_ALWAYS_INLINE __m128i avg_sse2_8x1_4x1(__m128i a, __m128i b)
+{
+ __m128i t = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(3, 1, 3, 1)));
+ b = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(2, 0, 2, 0)));
+ a = t;
+
+ return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
+}
+
+MOZ_ALWAYS_INLINE uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ uint32_t sum = a ^ b ^ c;
+ uint32_t carry = (a & b) | (a & c) | (b & c);
+
+ uint32_t mask = 0xfefefefe;
+
+ // Not having a byte based average instruction means we should mask to avoid
+ // underflow.
+ sum = (((sum ^ d) & mask) >> 1) + (sum & d);
+
+ return (((sum ^ carry) & mask) >> 1) + (sum & carry);
+}
+
+// Simple 2 pixel average version of the function above.
+MOZ_ALWAYS_INLINE uint32_t Avg2(uint32_t a, uint32_t b)
+{
+ uint32_t sum = a ^ b;
+ uint32_t carry = (a & b);
+
+ uint32_t mask = 0xfefefefe;
+
+ return ((sum & mask) >> 1) + carry;
+}
+
+namespace mozilla {
+namespace gfx {
+
+void
+ImageHalfScaler::HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ const int Bpp = 4;
+
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ __m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
+ !(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = _mm_load_si128(upperRow);
+ __m128i b = _mm_load_si128(upperRow + 1);
+ __m128i c = _mm_load_si128(lowerRow);
+ __m128i d = _mm_load_si128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = _mm_load_si128(upperRow);
+ __m128i b = _mm_load_si128(upperRow + 1);
+ __m128i c = loadUnaligned128(lowerRow);
+ __m128i d = loadUnaligned128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)upperRow + 1);
+ __m128i c = _mm_load_si128((__m128i*)lowerRow);
+ __m128i d = _mm_load_si128((__m128i*)lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = loadUnaligned128(upperRow);
+ __m128i b = loadUnaligned128(upperRow + 1);
+ __m128i c = loadUnaligned128(lowerRow);
+ __m128i d = loadUnaligned128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle. We use a 2x2 'simd' implementation for this.
+ //
+ // Potentially we only have to do this in the last row since overflowing
+ // 8 pixels in an earlier row would appear to be harmless as it doesn't
+ // touch invalid memory. Even when reading and writing to the same surface.
+ // in practice we only do this when doing an additional downscale pass, and
+ // in this situation we have unused stride to write into harmlessly.
+ // I do not believe the additional code complexity would be worth it though.
+ for (; x < aSourceSize.width; x += 2) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * Bpp);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * Bpp);
+
+ *unalignedStorage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
+ *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ __m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
+ !(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = _mm_load_si128((__m128i*)upperRow);
+ __m128i b = _mm_load_si128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ // This line doesn't align well.
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = _mm_load_si128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = _mm_load_si128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle.
+ //
+ // Similar overflow considerations are valid as in the previous function.
+ for (; x < aSourceSize.width; x++) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ *unalignedStorage++ = Avg2(*(uint32_t*)upperRow, *(uint32_t*)lowerRow);
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y++) {
+ __m128i *storage = (__m128i*)(aDest + (y * aDestStride));
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
+
+ __m128i a = _mm_load_si128(pixels);
+ __m128i b = _mm_load_si128(pixels + 1);
+
+ *storage++ = avg_sse2_8x1_4x1(a, b);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
+
+ __m128i a = loadUnaligned128(pixels);
+ __m128i b = loadUnaligned128(pixels + 1);
+
+ *storage++ = avg_sse2_8x1_4x1(a, b);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle.
+ //
+ // Similar overflow considerations are valid as in the previous function.
+ for (; x < aSourceSize.width; x += 2) {
+ uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+
+ *unalignedStorage++ = Avg2(*pixels, *(pixels + 1));
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/IterableArena.h b/system/graphics/2d/IterableArena.h
new file mode 100644
index 000000000..a444c9a38
--- /dev/null
+++ b/system/graphics/2d/IterableArena.h
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_ITERABLEARENA_H_
+#define MOZILLA_GFX_ITERABLEARENA_H_
+
+#include "mozilla/Move.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/gfx/Logging.h"
+
+#include <string.h>
+#include <vector>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace mozilla {
+namespace gfx {
+
+/// A simple pool allocator for plain data structures.
+///
+/// Beware that the pool will not attempt to run the destructors. It is the
+/// responsibility of the user of this class to either use objects with no
+/// destructor or to manually call the allocated objects destructors.
+/// If the pool is growable, its allocated objects must be safely moveable in
+/// in memory (through memcpy).
+class IterableArena {
+protected:
+ struct Header
+ {
+ size_t mBlocSize;
+ };
+public:
+ enum ArenaType {
+ FIXED_SIZE,
+ GROWABLE
+ };
+
+ IterableArena(ArenaType aType, size_t aStorageSize)
+ : mSize(aStorageSize)
+ , mCursor(0)
+ , mIsGrowable(aType == GROWABLE)
+ {
+ if (mSize == 0) {
+ mSize = 128;
+ }
+
+ mStorage = (uint8_t*)malloc(mSize);
+ if (mStorage == nullptr) {
+ gfxCriticalError() << "Not enough Memory allocate a memory pool of size " << aStorageSize;
+ MOZ_CRASH("GFX: Out of memory IterableArena");
+ }
+ }
+
+ ~IterableArena()
+ {
+ free(mStorage);
+ }
+
+ /// Constructs a new item in the pool and returns a positive offset in case of
+ /// success.
+ ///
+ /// The offset never changes even if the storage is reallocated, so users
+ /// of this class should prefer storing offsets rather than direct pointers
+ /// to the allocated objects.
+ /// Alloc can cause the storage to be reallocated if the pool was initialized
+ /// with IterableArena::GROWABLE.
+ /// If for any reason the pool fails to allocate enough space for the new item
+ /// Alloc returns a negative offset and the object's constructor is not called.
+ template<typename T, typename... Args>
+ ptrdiff_t
+ Alloc(Args&&... aArgs)
+ {
+ void* storage = nullptr;
+ auto offset = AllocRaw(sizeof(T), &storage);
+ if (offset < 0) {
+ return offset;
+ }
+ new (storage) T(Forward<Args>(aArgs)...);
+ return offset;
+ }
+
+ ptrdiff_t AllocRaw(size_t aSize, void** aOutPtr = nullptr)
+ {
+ const size_t blocSize = AlignedSize(sizeof(Header) + aSize);
+
+ if (AlignedSize(mCursor + blocSize) > mSize) {
+ if (!mIsGrowable) {
+ return -1;
+ }
+
+ size_t newSize = mSize * 2;
+ while (AlignedSize(mCursor + blocSize) > newSize) {
+ newSize *= 2;
+ }
+
+ uint8_t* newStorage = (uint8_t*)realloc(mStorage, newSize);
+ if (!newStorage) {
+ gfxCriticalError() << "Not enough Memory to grow the memory pool, size: " << newSize;
+ return -1;
+ }
+
+ mStorage = newStorage;
+ mSize = newSize;
+ }
+ ptrdiff_t offset = mCursor;
+ GetHeader(offset)->mBlocSize = blocSize;
+ mCursor += blocSize;
+ if (aOutPtr) {
+ *aOutPtr = GetStorage(offset);
+ }
+ return offset;
+ }
+
+ /// Get access to an allocated item at a given offset (only use offsets returned
+ /// by Alloc or AllocRaw).
+ ///
+ /// If the pool is growable, the returned pointer is only valid temporarily. The
+ /// underlying storage can be reallocated in Alloc or AllocRaw, so do not keep
+ /// these pointers around and store the offset instead.
+ void* GetStorage(ptrdiff_t offset = 0)
+ {
+ MOZ_ASSERT(offset >= 0);
+ MOZ_ASSERT(offset < mCursor);
+ return offset >= 0 ? mStorage + offset + sizeof(Header) : nullptr;
+ }
+
+ /// Clears the storage without running any destructor and without deallocating it.
+ void Clear()
+ {
+ mCursor = 0;
+ }
+
+ /// Iterate over the elements allocated in this pool.
+ ///
+ /// Takes a lambda or function object accepting a void* as parameter.
+ template<typename Func>
+ void ForEach(Func cb)
+ {
+ Iterator it;
+ while (void* ptr = it.Next(this)) {
+ cb(ptr);
+ }
+ }
+
+ /// A simple iterator over an arena.
+ class Iterator {
+ public:
+ Iterator()
+ : mCursor(0)
+ {}
+
+ void* Next(IterableArena* aArena)
+ {
+ if (mCursor >= aArena->mCursor) {
+ return nullptr;
+ }
+ void* result = aArena->GetStorage(mCursor);
+ const size_t blocSize = aArena->GetHeader(mCursor)->mBlocSize;
+ MOZ_ASSERT(blocSize != 0);
+ mCursor += blocSize;
+ return result;
+ }
+
+ private:
+ ptrdiff_t mCursor;
+ };
+
+protected:
+ Header* GetHeader(ptrdiff_t offset)
+ {
+ return (Header*) (mStorage + offset);
+ }
+
+ size_t AlignedSize(size_t aSize) const
+ {
+ const size_t alignment = sizeof(uintptr_t);
+ return aSize + (alignment - (aSize % alignment)) % alignment;
+ }
+
+ uint8_t* mStorage;
+ uint32_t mSize;
+ ptrdiff_t mCursor;
+ bool mIsGrowable;
+
+ friend class Iterator;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/2d/JobScheduler.cpp b/system/graphics/2d/JobScheduler.cpp
new file mode 100644
index 000000000..2c687cde0
--- /dev/null
+++ b/system/graphics/2d/JobScheduler.cpp
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "JobScheduler.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+JobScheduler* JobScheduler::sSingleton = nullptr;
+
+bool JobScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
+{
+ MOZ_ASSERT(!sSingleton);
+ MOZ_ASSERT(aNumThreads >= aNumQueues);
+
+ sSingleton = new JobScheduler();
+ sSingleton->mNextQueue = 0;
+
+ for (uint32_t i = 0; i < aNumQueues; ++i) {
+ sSingleton->mDrawingQueues.push_back(new MultiThreadedJobQueue());
+ }
+
+ for (uint32_t i = 0; i < aNumThreads; ++i) {
+ sSingleton->mWorkerThreads.push_back(WorkerThread::Create(sSingleton->mDrawingQueues[i%aNumQueues]));
+ }
+ return true;
+}
+
+void JobScheduler::ShutDown()
+{
+ MOZ_ASSERT(IsEnabled());
+ if (!IsEnabled()) {
+ return;
+ }
+
+ for (auto queue : sSingleton->mDrawingQueues) {
+ queue->ShutDown();
+ delete queue;
+ }
+
+ for (WorkerThread* thread : sSingleton->mWorkerThreads) {
+ // this will block until the thread is joined.
+ delete thread;
+ }
+
+ sSingleton->mWorkerThreads.clear();
+ delete sSingleton;
+ sSingleton = nullptr;
+}
+
+JobStatus
+JobScheduler::ProcessJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ auto status = aJob->Run();
+ if (status == JobStatus::Error || status == JobStatus::Complete) {
+ delete aJob;
+ }
+ return status;
+}
+
+void
+JobScheduler::SubmitJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ RefPtr<SyncObject> start = aJob->GetStartSync();
+ if (start && start->Register(aJob)) {
+ // The Job buffer starts with a non-signaled sync object, it
+ // is now registered in the list of task buffers waiting on the
+ // sync object, so we should not place it in the queue.
+ return;
+ }
+
+ GetQueueForJob(aJob)->SubmitJob(aJob);
+}
+
+void
+JobScheduler::Join(SyncObject* aCompletion)
+{
+ RefPtr<EventObject> waitForCompletion = new EventObject();
+ JobScheduler::SubmitJob(new SetEventJob(waitForCompletion, aCompletion));
+ waitForCompletion->Wait();
+}
+
+MultiThreadedJobQueue*
+JobScheduler::GetQueueForJob(Job* aJob)
+{
+ return aJob->IsPinnedToAThread() ? aJob->GetWorkerThread()->GetJobQueue()
+ : GetDrawingQueue();
+}
+
+Job::Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread)
+: mNextWaitingJob(nullptr)
+, mStartSync(aStart)
+, mCompletionSync(aCompletion)
+, mPinToThread(aThread)
+{
+ if (mStartSync) {
+ mStartSync->AddSubsequent(this);
+ }
+ if (mCompletionSync) {
+ mCompletionSync->AddPrerequisite(this);
+ }
+}
+
+Job::~Job()
+{
+ if (mCompletionSync) {
+ //printf(" -- Job %p dtor completion %p\n", this, mCompletionSync);
+ mCompletionSync->Signal();
+ mCompletionSync = nullptr;
+ }
+}
+
+JobStatus
+SetEventJob::Run()
+{
+ mEvent->Set();
+ return JobStatus::Complete;
+}
+
+SetEventJob::SetEventJob(EventObject* aEvent,
+ SyncObject* aStart, SyncObject* aCompletion,
+ WorkerThread* aWorker)
+: Job(aStart, aCompletion, aWorker)
+, mEvent(aEvent)
+{}
+
+SetEventJob::~SetEventJob()
+{}
+
+SyncObject::SyncObject(uint32_t aNumPrerequisites)
+: mSignals(aNumPrerequisites)
+, mFirstWaitingJob(nullptr)
+#ifdef DEBUG
+, mNumPrerequisites(aNumPrerequisites)
+, mAddedPrerequisites(0)
+#endif
+{}
+
+SyncObject::~SyncObject()
+{
+ MOZ_ASSERT(mFirstWaitingJob == nullptr);
+}
+
+bool
+SyncObject::Register(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+
+ // For now, ensure that when we schedule the first subsequent, we have already
+ // created all of the prerequisites. This is an arbitrary restriction because
+ // we specify the number of prerequisites in the constructor, but in the typical
+ // scenario, if the assertion FreezePrerequisite blows up here it probably means
+ // we got the initial nmber of prerequisites wrong. We can decide to remove
+ // this restriction if needed.
+ FreezePrerequisites();
+
+ int32_t signals = mSignals;
+
+ if (signals > 0) {
+ AddWaitingJob(aJob);
+ // Since Register and Signal can be called concurrently, it can happen that
+ // reading mSignals in Register happens before decrementing mSignals in Signal,
+ // but SubmitWaitingJobs happens before AddWaitingJob. This ordering means
+ // the SyncObject ends up in the signaled state with a task sitting in the
+ // waiting list. To prevent that we check mSignals a second time and submit
+ // again if signals reached zero in the mean time.
+ // We do this instead of holding a mutex around mSignals+mJobs to reduce
+ // lock contention.
+ int32_t signals2 = mSignals;
+ if (signals2 == 0) {
+ SubmitWaitingJobs();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void
+SyncObject::Signal()
+{
+ int32_t signals = --mSignals;
+ MOZ_ASSERT(signals >= 0);
+
+ if (signals == 0) {
+ SubmitWaitingJobs();
+ }
+}
+
+void
+SyncObject::AddWaitingJob(Job* aJob)
+{
+ // Push (using atomics) the task into the list of waiting tasks.
+ for (;;) {
+ Job* first = mFirstWaitingJob;
+ aJob->mNextWaitingJob = first;
+ if (mFirstWaitingJob.compareExchange(first, aJob)) {
+ break;
+ }
+ }
+}
+
+void SyncObject::SubmitWaitingJobs()
+{
+ // Scheduling the tasks can cause code that modifies <this>'s reference
+ // count to run concurrently, and cause the caller of this function to
+ // be owned by another thread. We need to make sure the reference count
+ // does not reach 0 on another thread before the end of this method, so
+ // hold a strong ref to prevent that!
+ RefPtr<SyncObject> kungFuDeathGrip(this);
+
+ // First atomically swap mFirstWaitingJob and waitingJobs...
+ Job* waitingJobs = nullptr;
+ for (;;) {
+ waitingJobs = mFirstWaitingJob;
+ if (mFirstWaitingJob.compareExchange(waitingJobs, nullptr)) {
+ break;
+ }
+ }
+
+ // ... and submit all of the waiting tasks in waitingJob now that they belong
+ // to this thread.
+ while (waitingJobs) {
+ Job* next = waitingJobs->mNextWaitingJob;
+ waitingJobs->mNextWaitingJob = nullptr;
+ JobScheduler::GetQueueForJob(waitingJobs)->SubmitJob(waitingJobs);
+ waitingJobs = next;
+ }
+}
+
+bool
+SyncObject::IsSignaled()
+{
+ return mSignals == 0;
+}
+
+void
+SyncObject::FreezePrerequisites()
+{
+ MOZ_ASSERT(mAddedPrerequisites == mNumPrerequisites);
+}
+
+void
+SyncObject::AddPrerequisite(Job* aJob)
+{
+ MOZ_ASSERT(++mAddedPrerequisites <= mNumPrerequisites);
+}
+
+void
+SyncObject::AddSubsequent(Job* aJob)
+{
+}
+
+WorkerThread::WorkerThread(MultiThreadedJobQueue* aJobQueue)
+: mQueue(aJobQueue)
+{
+ aJobQueue->RegisterThread();
+}
+
+void
+WorkerThread::Run()
+{
+ SetName("gfx worker");
+
+ for (;;) {
+ Job* commands = nullptr;
+ if (!mQueue->WaitForJob(commands)) {
+ mQueue->UnregisterThread();
+ return;
+ }
+
+ JobStatus status = JobScheduler::ProcessJob(commands);
+
+ if (status == JobStatus::Error) {
+ // Don't try to handle errors for now, but that's open to discussions.
+ // I expect errors to be mostly OOM issues.
+ gfxDevCrash(LogReason::JobStatusError) << "Invalid job status " << (int)status;
+ }
+ }
+}
+
+} //namespace
+} //namespace
diff --git a/system/graphics/2d/JobScheduler.h b/system/graphics/2d/JobScheduler.h
new file mode 100644
index 000000000..483842904
--- /dev/null
+++ b/system/graphics/2d/JobScheduler.h
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TASKSCHEDULER_H_
+#define MOZILLA_GFX_TASKSCHEDULER_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/RefCounted.h"
+
+#ifdef WIN32
+#include "mozilla/gfx/JobScheduler_win32.h"
+#else
+#include "mozilla/gfx/JobScheduler_posix.h"
+#endif
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class MultiThreadedJobQueue;
+class SyncObject;
+class WorkerThread;
+
+class JobScheduler {
+public:
+ /// Return one of the queues that the drawing worker threads pull from, chosen
+ /// pseudo-randomly.
+ static MultiThreadedJobQueue* GetDrawingQueue()
+ {
+ return sSingleton->mDrawingQueues[
+ sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size()
+ ];
+ }
+
+ /// Return one of the queues that the drawing worker threads pull from with a
+ /// hash to choose the queue.
+ ///
+ /// Calling this function several times with the same hash will yield the same queue.
+ static MultiThreadedJobQueue* GetDrawingQueue(uint32_t aHash)
+ {
+ return sSingleton->mDrawingQueues[
+ aHash % sSingleton->mDrawingQueues.size()
+ ];
+ }
+
+ /// Return the task queue associated to the worker the task is pinned to if
+ /// the task is pinned to a worker, or a random queue.
+ static MultiThreadedJobQueue* GetQueueForJob(Job* aJob);
+
+ /// Initialize the task scheduler with aNumThreads worker threads for drawing
+ /// and aNumQueues task queues.
+ ///
+ /// The number of threads must be superior or equal to the number of queues
+ /// (since for now a worker thread only pulls from one queue).
+ static bool Init(uint32_t aNumThreads, uint32_t aNumQueues);
+
+ /// Shut the scheduler down.
+ ///
+ /// This will block until worker threads are joined and deleted.
+ static void ShutDown();
+
+ /// Returns true if there is a successfully initialized JobScheduler singleton.
+ static bool IsEnabled() { return !!sSingleton; }
+
+ /// Submit a task buffer to its associated queue.
+ ///
+ /// The caller looses ownership of the task buffer.
+ static void SubmitJob(Job* aJobs);
+
+ /// Convenience function to block the current thread until a given SyncObject
+ /// is in the signaled state.
+ ///
+ /// The current thread will first try to steal jobs before blocking.
+ static void Join(SyncObject* aCompletionSync);
+
+ /// Process commands until the command buffer needs to block on a sync object,
+ /// completes, yields, or encounters an error.
+ ///
+ /// Can be used on any thread. Worker threads basically loop over this, but the
+ /// main thread can also dequeue pending task buffers and process them alongside
+ /// the worker threads if it is about to block until completion anyway.
+ ///
+ /// The caller looses ownership of the task buffer.
+ static JobStatus ProcessJob(Job* aJobs);
+
+protected:
+ static JobScheduler* sSingleton;
+
+ // queues of Job that are ready to be processed
+ std::vector<MultiThreadedJobQueue*> mDrawingQueues;
+ std::vector<WorkerThread*> mWorkerThreads;
+ Atomic<uint32_t> mNextQueue;
+};
+
+/// Jobs are not reference-counted because they don't have shared ownership.
+/// The ownership of tasks can change when they are passed to certain methods
+/// of JobScheduler and SyncObject. See the docuumentaion of these classes.
+class Job {
+public:
+ Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread = nullptr);
+
+ virtual ~Job();
+
+ virtual JobStatus Run() = 0;
+
+ /// For use in JobScheduler::SubmitJob. Don't use it anywhere else.
+ //already_AddRefed<SyncObject> GetAndResetStartSync();
+ SyncObject* GetStartSync() { return mStartSync; }
+
+ bool IsPinnedToAThread() const { return !!mPinToThread; }
+
+ WorkerThread* GetWorkerThread() { return mPinToThread; }
+
+protected:
+ // An intrusive linked list of tasks waiting for a sync object to enter the
+ // signaled state. When the task is not waiting for a sync object, mNextWaitingJob
+ // should be null. This is only accessed from the thread that owns the task.
+ Job* mNextWaitingJob;
+
+ RefPtr<SyncObject> mStartSync;
+ RefPtr<SyncObject> mCompletionSync;
+ WorkerThread* mPinToThread;
+
+ friend class SyncObject;
+};
+
+class EventObject;
+
+/// This task will set an EventObject.
+///
+/// Typically used as the final task, so that the main thread can block on the
+/// corresponfing EventObject until all of the tasks are processed.
+class SetEventJob : public Job
+{
+public:
+ explicit SetEventJob(EventObject* aEvent,
+ SyncObject* aStart, SyncObject* aCompletion = nullptr,
+ WorkerThread* aPinToWorker = nullptr);
+
+ ~SetEventJob();
+
+ JobStatus Run() override;
+
+ EventObject* GetEvent() { return mEvent; }
+
+protected:
+ RefPtr<EventObject> mEvent;
+};
+
+/// A synchronization object that can be used to express dependencies and ordering between
+/// tasks.
+///
+/// Jobs can register to SyncObjects in order to asynchronously wait for a signal.
+/// In practice, Job objects usually start with a sync object (startSyc) and end
+/// with another one (completionSync).
+/// a Job never gets processed before its startSync is in the signaled state, and
+/// signals its completionSync as soon as it finishes. This is how dependencies
+/// between tasks is expressed.
+class SyncObject final : public external::AtomicRefCounted<SyncObject> {
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(SyncObject)
+
+ /// Create a synchronization object.
+ ///
+ /// aNumPrerequisites represents the number of times the object must be signaled
+ /// before actually entering the signaled state (in other words, it means the
+ /// number of dependencies of this sync object).
+ ///
+ /// Explicitly specifying the number of prerequisites when creating sync objects
+ /// makes it easy to start scheduling some of the prerequisite tasks while
+ /// creating the others, which is how we typically use the task scheduler.
+ /// Automatically determining the number of prerequisites using Job's constructor
+ /// brings the risk that the sync object enters the signaled state while we
+ /// are still adding prerequisites which is hard to fix without using muteces.
+ explicit SyncObject(uint32_t aNumPrerequisites = 1);
+
+ ~SyncObject();
+
+ /// Attempt to register a task.
+ ///
+ /// If the sync object is already in the signaled state, the buffer is *not*
+ /// registered and the sync object does not take ownership of the task.
+ /// If the object is not yet in the signaled state, it takes ownership of
+ /// the task and places it in a list of pending tasks.
+ /// Pending tasks will not be processed by the worker thread.
+ /// When the SyncObject reaches the signaled state, it places the pending
+ /// tasks back in the available buffer queue, so that they can be
+ /// scheduled again.
+ ///
+ /// Returns true if the SyncOject is not already in the signaled state.
+ /// This means that if this method returns true, the SyncObject has taken
+ /// ownership of the Job.
+ bool Register(Job* aJob);
+
+ /// Signal the SyncObject.
+ ///
+ /// This decrements an internal counter. The sync object reaches the signaled
+ /// state when the counter gets to zero.
+ void Signal();
+
+ /// Returns true if mSignals is equal to zero. In other words, returns true
+ /// if all prerequisite tasks have already signaled the sync object.
+ bool IsSignaled();
+
+ /// Asserts that the number of added prerequisites is equal to the number
+ /// specified in the constructor (does nothin in release builds).
+ void FreezePrerequisites();
+
+private:
+ // Called by Job's constructor
+ void AddSubsequent(Job* aJob);
+ void AddPrerequisite(Job* aJob);
+
+ void AddWaitingJob(Job* aJob);
+
+ void SubmitWaitingJobs();
+
+ Atomic<int32_t> mSignals;
+ Atomic<Job*> mFirstWaitingJob;
+
+#ifdef DEBUG
+ uint32_t mNumPrerequisites;
+ Atomic<uint32_t> mAddedPrerequisites;
+#endif
+
+ friend class Job;
+ friend class JobScheduler;
+};
+
+/// Base class for worker threads.
+class WorkerThread
+{
+public:
+ static WorkerThread* Create(MultiThreadedJobQueue* aJobQueue);
+
+ virtual ~WorkerThread() {}
+
+ void Run();
+
+ MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
+
+protected:
+ explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
+
+ virtual void SetName(const char* aName) {}
+
+ MultiThreadedJobQueue* mQueue;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/2d/JobScheduler_posix.cpp b/system/graphics/2d/JobScheduler_posix.cpp
new file mode 100644
index 000000000..5446df611
--- /dev/null
+++ b/system/graphics/2d/JobScheduler_posix.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "JobScheduler.h"
+#include "mozilla/gfx/Logging.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+void* ThreadCallback(void* threadData);
+
+class WorkerThreadPosix : public WorkerThread {
+public:
+ explicit WorkerThreadPosix(MultiThreadedJobQueue* aJobQueue)
+ : WorkerThread(aJobQueue)
+ {
+ pthread_create(&mThread, nullptr, ThreadCallback, static_cast<WorkerThread*>(this));
+ }
+
+ ~WorkerThreadPosix()
+ {
+ pthread_join(mThread, nullptr);
+ }
+
+ virtual void SetName(const char*) override
+ {
+// XXX - temporarily disabled, see bug 1209039
+//
+// // Call this from the thread itself because of Mac.
+//#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+// pthread_set_name_np(mThread, aName);
+//#elif defined(__NetBSD__)
+// pthread_setname_np(mThread, "%s", (void*)aName);
+//#else
+// pthread_setname_np(mThread, aName);
+//#endif
+ }
+
+protected:
+ pthread_t mThread;
+};
+
+void* ThreadCallback(void* threadData)
+{
+ WorkerThread* thread = static_cast<WorkerThread*>(threadData);
+ thread->Run();
+ return nullptr;
+}
+
+WorkerThread*
+WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
+{
+ return new WorkerThreadPosix(aJobQueue);
+}
+
+MultiThreadedJobQueue::MultiThreadedJobQueue()
+: mThreadsCount(0)
+, mShuttingDown(false)
+{}
+
+MultiThreadedJobQueue::~MultiThreadedJobQueue()
+{
+ MOZ_ASSERT(mJobs.empty());
+}
+
+bool
+MultiThreadedJobQueue::WaitForJob(Job*& aOutJob)
+{
+ return PopJob(aOutJob, BLOCKING);
+}
+
+bool
+MultiThreadedJobQueue::PopJob(Job*& aOutJobs, AccessType aAccess)
+{
+ for (;;) {
+ CriticalSectionAutoEnter lock(&mMutex);
+
+ while (aAccess == BLOCKING && !mShuttingDown && mJobs.empty()) {
+ mAvailableCondvar.Wait(&mMutex);
+ }
+
+ if (mShuttingDown) {
+ return false;
+ }
+
+ if (mJobs.empty()) {
+ if (aAccess == NON_BLOCKING) {
+ return false;
+ }
+ continue;
+ }
+
+ Job* task = mJobs.front();
+ MOZ_ASSERT(task);
+
+ mJobs.pop_front();
+
+ aOutJobs = task;
+ return true;
+ }
+}
+
+void
+MultiThreadedJobQueue::SubmitJob(Job* aJobs)
+{
+ MOZ_ASSERT(aJobs);
+ CriticalSectionAutoEnter lock(&mMutex);
+ mJobs.push_back(aJobs);
+ mAvailableCondvar.Broadcast();
+}
+
+size_t
+MultiThreadedJobQueue::NumJobs()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mJobs.size();
+}
+
+bool
+MultiThreadedJobQueue::IsEmpty()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mJobs.empty();
+}
+
+void
+MultiThreadedJobQueue::ShutDown()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ mShuttingDown = true;
+ while (mThreadsCount) {
+ mAvailableCondvar.Broadcast();
+ mShutdownCondvar.Wait(&mMutex);
+ }
+}
+
+void
+MultiThreadedJobQueue::RegisterThread()
+{
+ mThreadsCount += 1;
+}
+
+void
+MultiThreadedJobQueue::UnregisterThread()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ mThreadsCount -= 1;
+ if (mThreadsCount == 0) {
+ mShutdownCondvar.Broadcast();
+ }
+}
+
+EventObject::EventObject()
+: mIsSet(false)
+{}
+
+EventObject::~EventObject()
+{}
+
+bool
+EventObject::Peak()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mIsSet;
+}
+
+void
+EventObject::Set()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ if (!mIsSet) {
+ mIsSet = true;
+ mCond.Broadcast();
+ }
+}
+
+void
+EventObject::Wait()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ if (mIsSet) {
+ return;
+ }
+ mCond.Wait(&mMutex);
+}
+
+} // namespce
+} // namespce
diff --git a/system/graphics/2d/JobScheduler_posix.h b/system/graphics/2d/JobScheduler_posix.h
new file mode 100644
index 000000000..cc1bef84e
--- /dev/null
+++ b/system/graphics/2d/JobScheduler_posix.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WIN32
+#ifndef MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
+#define MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
+
+#include <string>
+#include <vector>
+#include <list>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
+
+namespace mozilla {
+namespace gfx {
+
+class Job;
+class PosixCondVar;
+class WorkerThread;
+
+// posix platforms only!
+class PosixCondVar {
+public:
+ PosixCondVar() {
+ DebugOnly<int> err = pthread_cond_init(&mCond, nullptr);
+ MOZ_ASSERT(!err);
+ }
+
+ ~PosixCondVar() {
+ DebugOnly<int> err = pthread_cond_destroy(&mCond);
+ MOZ_ASSERT(!err);
+ }
+
+ void Wait(CriticalSection* aMutex) {
+ DebugOnly<int> err = pthread_cond_wait(&mCond, &aMutex->mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Broadcast() {
+ DebugOnly<int> err = pthread_cond_broadcast(&mCond);
+ MOZ_ASSERT(!err);
+ }
+
+protected:
+ pthread_cond_t mCond;
+};
+
+
+/// A simple and naive multithreaded task queue
+///
+/// The public interface of this class must remain identical to its equivalent
+/// in JobScheduler_win32.h
+class MultiThreadedJobQueue {
+public:
+ enum AccessType {
+ BLOCKING,
+ NON_BLOCKING
+ };
+
+ // Producer thread
+ MultiThreadedJobQueue();
+
+ // Producer thread
+ ~MultiThreadedJobQueue();
+
+ // Worker threads
+ bool WaitForJob(Job*& aOutJob);
+
+ // Any thread
+ bool PopJob(Job*& aOutJob, AccessType aAccess);
+
+ // Any threads
+ void SubmitJob(Job* aJob);
+
+ // Producer thread
+ void ShutDown();
+
+ // Any thread
+ size_t NumJobs();
+
+ // Any thread
+ bool IsEmpty();
+
+ // Producer thread
+ void RegisterThread();
+
+ // Worker threads
+ void UnregisterThread();
+
+protected:
+
+ std::list<Job*> mJobs;
+ CriticalSection mMutex;
+ PosixCondVar mAvailableCondvar;
+ PosixCondVar mShutdownCondvar;
+ int32_t mThreadsCount;
+ bool mShuttingDown;
+
+ friend class WorkerThread;
+};
+
+/// An object that a thread can synchronously wait on.
+/// Usually set by a SetEventJob.
+class EventObject : public external::AtomicRefCounted<EventObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
+
+ EventObject();
+
+ ~EventObject();
+
+ /// Synchronously wait until the event is set.
+ void Wait();
+
+ /// Return true if the event is set, without blocking.
+ bool Peak();
+
+ /// Set the event.
+ void Set();
+
+protected:
+ CriticalSection mMutex;
+ PosixCondVar mCond;
+ bool mIsSet;
+};
+
+} // namespace
+} // namespace
+
+#include "JobScheduler.h"
+
+#endif
+#endif
diff --git a/system/graphics/2d/JobScheduler_win32.cpp b/system/graphics/2d/JobScheduler_win32.cpp
new file mode 100644
index 000000000..989965adc
--- /dev/null
+++ b/system/graphics/2d/JobScheduler_win32.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "JobScheduler.h"
+#include "mozilla/gfx/Logging.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DWORD __stdcall ThreadCallback(void* threadData);
+
+class WorkerThreadWin32 : public WorkerThread {
+public:
+ explicit WorkerThreadWin32(MultiThreadedJobQueue* aJobQueue)
+ : WorkerThread(aJobQueue)
+ {
+ mThread = ::CreateThread(nullptr, 0, ThreadCallback, static_cast<WorkerThread*>(this), 0, nullptr);
+ }
+
+ ~WorkerThreadWin32()
+ {
+ ::WaitForSingleObject(mThread, INFINITE);
+ ::CloseHandle(mThread);
+ }
+
+protected:
+ HANDLE mThread;
+};
+
+DWORD __stdcall ThreadCallback(void* threadData)
+{
+ WorkerThread* thread = static_cast<WorkerThread*>(threadData);
+ thread->Run();
+ return 0;
+}
+
+WorkerThread*
+WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
+{
+ return new WorkerThreadWin32(aJobQueue);
+}
+
+bool
+MultiThreadedJobQueue::PopJob(Job*& aOutJob, AccessType aAccess)
+{
+ for (;;) {
+ while (aAccess == BLOCKING && mJobs.empty()) {
+ {
+ CriticalSectionAutoEnter lock(&mSection);
+ if (mShuttingDown) {
+ return false;
+ }
+ }
+
+ HANDLE handles[] = { mAvailableEvent, mShutdownEvent };
+ ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ }
+
+ CriticalSectionAutoEnter lock(&mSection);
+
+ if (mShuttingDown) {
+ return false;
+ }
+
+ if (mJobs.empty()) {
+ if (aAccess == NON_BLOCKING) {
+ return false;
+ }
+ continue;
+ }
+
+ Job* task = mJobs.front();
+ MOZ_ASSERT(task);
+
+ mJobs.pop_front();
+
+ if (mJobs.empty()) {
+ ::ResetEvent(mAvailableEvent);
+ }
+
+ aOutJob = task;
+ return true;
+ }
+}
+
+void
+MultiThreadedJobQueue::SubmitJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ CriticalSectionAutoEnter lock(&mSection);
+ mJobs.push_back(aJob);
+ ::SetEvent(mAvailableEvent);
+}
+
+void
+MultiThreadedJobQueue::ShutDown()
+{
+ {
+ CriticalSectionAutoEnter lock(&mSection);
+ mShuttingDown = true;
+ }
+ while (mThreadsCount) {
+ ::SetEvent(mAvailableEvent);
+ ::WaitForSingleObject(mShutdownEvent, INFINITE);
+ }
+}
+
+size_t
+MultiThreadedJobQueue::NumJobs()
+{
+ CriticalSectionAutoEnter lock(&mSection);
+ return mJobs.size();
+}
+
+bool
+MultiThreadedJobQueue::IsEmpty()
+{
+ CriticalSectionAutoEnter lock(&mSection);
+ return mJobs.empty();
+}
+
+void
+MultiThreadedJobQueue::RegisterThread()
+{
+ mThreadsCount += 1;
+}
+
+void
+MultiThreadedJobQueue::UnregisterThread()
+{
+ mSection.Enter();
+ mThreadsCount -= 1;
+ bool finishShutdown = mThreadsCount == 0;
+ mSection.Leave();
+
+ if (finishShutdown) {
+ // Can't touch mSection or any other member from now on because this object
+ // may get deleted on the main thread after mShutdownEvent is set.
+ ::SetEvent(mShutdownEvent);
+ }
+}
+
+} // namespace
+} // namespace
diff --git a/system/graphics/2d/JobScheduler_win32.h b/system/graphics/2d/JobScheduler_win32.h
new file mode 100644
index 000000000..73ccbd4ed
--- /dev/null
+++ b/system/graphics/2d/JobScheduler_win32.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef WIN32
+#ifndef MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
+#define MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
+
+#include <windows.h>
+#include <list>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
+
+namespace mozilla {
+namespace gfx {
+
+class WorkerThread;
+class Job;
+
+// The public interface of this class must remain identical to its equivalent
+// in JobScheduler_posix.h
+class MultiThreadedJobQueue {
+public:
+ enum AccessType {
+ BLOCKING,
+ NON_BLOCKING
+ };
+
+ MultiThreadedJobQueue()
+ : mThreadsCount(0)
+ , mShuttingDown(false)
+ {
+ mAvailableEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ mShutdownEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ }
+
+ ~MultiThreadedJobQueue()
+ {
+ ::CloseHandle(mAvailableEvent);
+ ::CloseHandle(mShutdownEvent);
+ }
+
+ bool WaitForJob(Job*& aOutJob) { return PopJob(aOutJob, BLOCKING); }
+
+ bool PopJob(Job*& aOutJob, AccessType aAccess);
+
+ void SubmitJob(Job* aJob);
+
+ void ShutDown();
+
+ size_t NumJobs();
+
+ bool IsEmpty();
+
+ void RegisterThread();
+
+ void UnregisterThread();
+
+protected:
+ std::list<Job*> mJobs;
+ CriticalSection mSection;
+ HANDLE mAvailableEvent;
+ HANDLE mShutdownEvent;
+ int32_t mThreadsCount;
+ bool mShuttingDown;
+
+ friend class WorkerThread;
+};
+
+
+// The public interface of this class must remain identical to its equivalent
+// in JobScheduler_posix.h
+class EventObject : public external::AtomicRefCounted<EventObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
+
+ EventObject() { mEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr); }
+
+ ~EventObject() { ::CloseHandle(mEvent); }
+
+ void Wait() { ::WaitForSingleObject(mEvent, INFINITE); }
+
+ bool Peak() { return ::WaitForSingleObject(mEvent, 0) == WAIT_OBJECT_0; }
+
+ void Set() { ::SetEvent(mEvent); }
+protected:
+ // TODO: it's expensive to create events so we should try to reuse them
+ HANDLE mEvent;
+};
+
+} // namespace
+} // namespace
+
+#endif
+#endif
diff --git a/system/graphics/2d/Logging.h b/system/graphics/2d/Logging.h
new file mode 100644
index 000000000..4c81fc7a8
--- /dev/null
+++ b/system/graphics/2d/Logging.h
@@ -0,0 +1,691 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_LOGGING_H_
+#define MOZILLA_GFX_LOGGING_H_
+
+#include <string>
+#include <sstream>
+#include <stdio.h>
+#include <vector>
+
+#ifdef MOZ_LOGGING
+#include "mozilla/Logging.h"
+#endif
+#include "mozilla/Tuple.h"
+
+#include "Point.h"
+#include "BaseRect.h"
+#include "Matrix.h"
+#include "LoggingConstants.h"
+
+#if defined(MOZ_LOGGING)
+extern GFX2D_API mozilla::LogModule* GetGFX2DLog();
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+#if defined(MOZ_LOGGING)
+inline mozilla::LogLevel PRLogLevelForLevel(int aLevel) {
+ switch (aLevel) {
+ case LOG_CRITICAL:
+ return LogLevel::Error;
+ case LOG_WARNING:
+ return LogLevel::Warning;
+ case LOG_DEBUG:
+ return LogLevel::Debug;
+ case LOG_DEBUG_PRLOG:
+ return LogLevel::Debug;
+ case LOG_EVERYTHING:
+ return LogLevel::Error;
+ }
+ return LogLevel::Debug;
+}
+#endif
+
+class LoggingPrefs
+{
+public:
+ // Used to choose the level of logging we get. The higher the number,
+ // the more logging we get. Value of zero will give you no logging,
+ // 1 just errors, 2 adds warnings and 3 or 4 add debug logging.
+ // In addition to setting the value to 4, you will need to set the
+ // environment variable MOZ_LOG to gfx:4. See mozilla/Logging.h for details.
+ static int32_t sGfxLogLevel;
+};
+
+/// Graphics logging is available in both debug and release builds and is
+/// controlled with a gfx.logging.level preference. If not set, the default
+/// for the preference is 5 in the debug builds, 1 in the release builds.
+///
+/// gfxDebug only works in the debug builds, and is used for information
+/// level messages, helping with debugging. In addition to only working
+/// in the debug builds, the value of the above preference of 3 or higher
+/// is required.
+///
+/// gfxWarning messages are available in both debug and release builds,
+/// on by default in the debug builds, and off by default in the release builds.
+/// Setting the preference gfx.logging.level to a value of 2 or higher will
+/// show the warnings.
+///
+/// gfxCriticalError is available in debug and release builds by default.
+/// It is only unavailable if gfx.logging.level is set to 0 (or less.)
+/// It outputs the message to stderr or equivalent, like gfxWarning.
+/// In the event of a crash, the crash report is annotated with first and
+/// the last few of these errors, under the key GraphicsCriticalError.
+/// The total number of errors stored in the crash report is controlled
+/// by preference gfx.logging.crash.length.
+///
+/// On platforms that support MOZ_LOGGING, the story is slightly more involved.
+/// In that case, unless gfx.logging.level is set to 4 or higher, the output
+/// is further controlled by the "gfx2d" logging module. However, in the case
+/// where such module would disable the output, in all but gfxDebug cases,
+/// we will still send a printf.
+
+// The range is due to the values set in Histograms.json
+enum class LogReason : int {
+ MustBeMoreThanThis = -1,
+ // Start. Do not insert, always add at end. If you remove items,
+ // make sure the other items retain their values.
+ D3D11InvalidCallDeviceRemoved = 0,
+ D3D11InvalidCall,
+ D3DLockTimeout,
+ D3D10FinalizeFrame,
+ D3D11FinalizeFrame,
+ D3D10SyncLock,
+ D3D11SyncLock,
+ D2D1NoWriteMap,
+ JobStatusError,
+ FilterInputError,
+ FilterInputData, // 10
+ FilterInputRect,
+ FilterInputSet,
+ FilterInputFormat,
+ FilterNodeD2D1Target,
+ FilterNodeD2D1Backend,
+ SourceSurfaceIncompatible,
+ GlyphAllocFailedCairo,
+ GlyphAllocFailedCG,
+ InvalidRect,
+ CannotDraw3D, // 20
+ IncompatibleBasicTexturedEffect,
+ InvalidFont,
+ PAllocTextureBackendMismatch,
+ GetFontFileDataFailed,
+ MessageChannelCloseFailure,
+ MessageChannelInvalidHandle,
+ TextureAliveAfterShutdown,
+ InvalidContext,
+ InvalidCommandList,
+ AsyncTransactionTimeout, // 30
+ TextureCreation,
+ InvalidCacheSurface,
+ AlphaWithBasicClient,
+ UnbalancedClipStack,
+ ProcessingError,
+ NativeFontResourceNotFound,
+ // End
+ MustBeLessThanThis = 101,
+};
+
+struct BasicLogger
+{
+ // For efficiency, this method exists and copies the logic of the
+ // OutputMessage below. If making any changes here, also make it
+ // in the appropriate places in that method.
+ static bool ShouldOutputMessage(int aLevel) {
+ if (LoggingPrefs::sGfxLogLevel >= aLevel) {
+#if defined(MOZ_LOGGING)
+ if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ return true;
+ } else
+#endif
+ if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Only for really critical errors.
+ static void CrashAction(LogReason aReason) {}
+
+ static void OutputMessage(const std::string &aString,
+ int aLevel,
+ bool aNoNewline) {
+ // This behavior (the higher the preference, the more we log)
+ // is consistent with what prlog does in general. Note that if prlog
+ // is in the build, but disabled, we will printf if the preferences
+ // requires us to log something (see sGfxLogLevel for the special
+ // treatment of LOG_DEBUG and LOG_DEBUG_PRLOG)
+ //
+ // If making any logic changes to this method, you should probably
+ // make the corresponding change in the ShouldOutputMessage method
+ // above.
+ if (LoggingPrefs::sGfxLogLevel >= aLevel) {
+#if defined(MOZ_LOGGING)
+ if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ } else
+#endif
+ if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ }
+ }
+ }
+};
+
+struct CriticalLogger {
+ static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
+ static void CrashAction(LogReason aReason);
+};
+
+// The int is the index of the Log call; if the number of logs exceeds some preset
+// capacity we may not get all of them, so the indices help figure out which
+// ones we did save. The double is expected to be the "TimeDuration",
+// time in seconds since the process creation.
+typedef mozilla::Tuple<int32_t,std::string,double> LoggingRecordEntry;
+
+// Implement this interface and init the Factory with an instance to
+// forward critical logs.
+typedef std::vector<LoggingRecordEntry> LoggingRecord;
+class LogForwarder {
+public:
+ virtual ~LogForwarder() {}
+ virtual void Log(const std::string &aString) = 0;
+ virtual void CrashAction(LogReason aReason) = 0;
+ virtual bool UpdateStringsVector(const std::string& aString) = 0;
+
+ // Provide a copy of the logs to the caller.
+ virtual LoggingRecord LoggingRecordCopy() = 0;
+};
+
+class NoLog
+{
+public:
+ NoLog() {}
+ ~NoLog() {}
+
+ // No-op
+ MOZ_IMPLICIT NoLog(const NoLog&) {}
+
+ template<typename T>
+ NoLog &operator <<(const T &aLogText) { return *this; }
+};
+
+enum class LogOptions : int {
+ NoNewline = 0x01,
+ AutoPrefix = 0x02,
+ AssertOnCall = 0x04,
+ CrashAction = 0x08,
+};
+
+template<typename T>
+struct Hexa {
+ explicit Hexa(T aVal) : mVal(aVal) {}
+ T mVal;
+};
+template<typename T>
+Hexa<T> hexa(T val) { return Hexa<T>(val); }
+
+template<int L, typename Logger = BasicLogger>
+class Log
+{
+public:
+ // The default is to have the prefix, have the new line, and for critical
+ // logs assert on each call.
+ static int DefaultOptions(bool aWithAssert = true) {
+ return (int(LogOptions::AutoPrefix) |
+ (aWithAssert ? int(LogOptions::AssertOnCall) : 0));
+ }
+
+ // Note that we're calling BasicLogger::ShouldOutputMessage, rather than
+ // Logger::ShouldOutputMessage. Since we currently don't have a different
+ // version of that method for different loggers, this is OK. Once we do,
+ // change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
+ explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL),
+ LogReason aReason = LogReason::MustBeMoreThanThis)
+ : mOptions(0)
+ , mLogIt(false)
+ {
+ Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason);
+ }
+
+ ~Log() {
+ Flush();
+ }
+
+ void Flush() {
+ if (MOZ_LIKELY(!LogIt())) return;
+
+ std::string str = mMessage.str();
+ if (!str.empty()) {
+ WriteLog(str);
+ }
+ mMessage.str("");
+ }
+
+ Log &operator <<(char aChar) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aChar;
+ }
+ return *this;
+ }
+ Log &operator <<(const std::string &aLogText) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLogText;
+ }
+ return *this;
+ }
+ Log &operator <<(const char aStr[]) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << static_cast<const char*>(aStr);
+ }
+ return *this;
+ }
+ Log &operator <<(bool aBool) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << (aBool ? "true" : "false");
+ }
+ return *this;
+ }
+ Log &operator <<(int aInt) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aInt;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned int aInt) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aInt;
+ }
+ return *this;
+ }
+ Log &operator <<(long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(long long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned long long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(Float aFloat) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aFloat;
+ }
+ return *this;
+ }
+ Log &operator <<(double aDouble) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aDouble;
+ }
+ return *this;
+ }
+ template <typename T, typename Sub, typename Coord>
+ Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Point" << aPoint;
+ }
+ return *this;
+ }
+ template <typename T, typename Sub>
+ Log &operator <<(const BaseSize<T, Sub>& aSize) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Size(" << aSize.width << "," << aSize.height << ")";
+ }
+ return *this;
+ }
+ template <typename T, typename Sub, typename Point, typename SizeT, typename Margin>
+ Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Rect" << aRect;
+ }
+ return *this;
+ }
+ Log &operator<<(const Matrix& aMatrix) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")";
+ }
+ return *this;
+ }
+ template<typename T>
+ Log &operator<<(Hexa<T> aHex) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << std::showbase << std::hex
+ << aHex.mVal
+ << std::noshowbase << std::dec;
+ }
+ return *this;
+ }
+
+ Log& operator<<(SurfaceFormat aFormat) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ switch(aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ mMessage << "SurfaceFormat::B8G8R8A8";
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ mMessage << "SurfaceFormat::B8G8R8X8";
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ mMessage << "SurfaceFormat::R8G8B8A8";
+ break;
+ case SurfaceFormat::R8G8B8X8:
+ mMessage << "SurfaceFormat::R8G8B8X8";
+ break;
+ case SurfaceFormat::R5G6B5_UINT16:
+ mMessage << "SurfaceFormat::R5G6B5_UINT16";
+ break;
+ case SurfaceFormat::A8:
+ mMessage << "SurfaceFormat::A8";
+ break;
+ case SurfaceFormat::YUV:
+ mMessage << "SurfaceFormat::YUV";
+ break;
+ case SurfaceFormat::UNKNOWN:
+ mMessage << "SurfaceFormat::UNKNOWN";
+ break;
+ default:
+ mMessage << "Invalid SurfaceFormat (" << (int)aFormat << ")";
+ break;
+ }
+ }
+ return *this;
+ }
+
+ Log& operator<<(SurfaceType aType) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ switch(aType) {
+ case SurfaceType::DATA:
+ mMessage << "SurfaceType::DATA";
+ break;
+ case SurfaceType::D2D1_BITMAP:
+ mMessage << "SurfaceType::D2D1_BITMAP";
+ break;
+ case SurfaceType::D2D1_DRAWTARGET:
+ mMessage << "SurfaceType::D2D1_DRAWTARGET";
+ break;
+ case SurfaceType::CAIRO:
+ mMessage << "SurfaceType::CAIRO";
+ break;
+ case SurfaceType::CAIRO_IMAGE:
+ mMessage << "SurfaceType::CAIRO_IMAGE";
+ break;
+ case SurfaceType::COREGRAPHICS_IMAGE:
+ mMessage << "SurfaceType::COREGRAPHICS_IMAGE";
+ break;
+ case SurfaceType::COREGRAPHICS_CGCONTEXT:
+ mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT";
+ break;
+ case SurfaceType::SKIA:
+ mMessage << "SurfaceType::SKIA";
+ break;
+ case SurfaceType::DUAL_DT:
+ mMessage << "SurfaceType::DUAL_DT";
+ break;
+ case SurfaceType::D2D1_1_IMAGE:
+ mMessage << "SurfaceType::D2D1_1_IMAGE";
+ break;
+ case SurfaceType::RECORDING:
+ mMessage << "SurfaceType::RECORDING";
+ break;
+ case SurfaceType::TILED:
+ mMessage << "SurfaceType::TILED";
+ break;
+ default:
+ mMessage << "Invalid SurfaceType (" << (int)aType << ")";
+ break;
+ }
+ }
+ return *this;
+ }
+
+ inline bool LogIt() const { return mLogIt; }
+ inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
+ inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
+ inline bool ValidReason() const { return (int)mReason > (int)LogReason::MustBeMoreThanThis && (int)mReason < (int)LogReason::MustBeLessThanThis; }
+
+ // We do not want this version to do any work, and stringstream can't be
+ // copied anyway. It does come in handy for the "Once" macro defined below.
+ MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); }
+
+private:
+ // Initialization common to two constructors
+ void Init(int aOptions, bool aLogIt, LogReason aReason) {
+ mOptions = aOptions;
+ mReason = aReason;
+ mLogIt = aLogIt;
+ if (mLogIt) {
+ if (AutoPrefix()) {
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ mMessage << "[GFX" << L;
+ } else {
+ mMessage << "[GFX" << L << "-";
+ }
+ }
+ if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
+ mMessage << " " << (int)mReason;
+ }
+ if (AutoPrefix()) {
+ mMessage << "]: ";
+ }
+ }
+ }
+
+ void WriteLog(const std::string &aString) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ Logger::OutputMessage(aString, L, NoNewline());
+ // Assert if required. We don't have a three parameter MOZ_ASSERT
+ // so use the underlying functions instead (see bug 1281702):
+#ifdef DEBUG
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__);
+ MOZ_CRASH("GFX: An assert from the graphics logger");
+ }
+#endif
+ if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
+ Logger::CrashAction(mReason);
+ }
+ }
+ }
+
+ std::stringstream mMessage;
+ int mOptions;
+ LogReason mReason;
+ bool mLogIt;
+};
+
+typedef Log<LOG_DEBUG> DebugLog;
+typedef Log<LOG_WARNING> WarningLog;
+typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
+
+// Macro to glue names to get us less chance of name clashing.
+#if defined GFX_LOGGING_GLUE1 || defined GFX_LOGGING_GLUE
+#error "Clash of the macro GFX_LOGGING_GLUE1 or GFX_LOGGING_GLUE"
+#endif
+#define GFX_LOGGING_GLUE1(x, y) x##y
+#define GFX_LOGGING_GLUE(x, y) GFX_LOGGING_GLUE1(x, y)
+
+// This log goes into crash reports, use with care.
+#define gfxCriticalError mozilla::gfx::CriticalLog
+#define gfxCriticalErrorOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalError
+
+// This is a shortcut for errors we want logged in crash reports/about support
+// but we do not want asserting. These are available in all builds, so it is
+// not worth trying to do magic to avoid matching the syntax of gfxCriticalError.
+// So, this one is used as
+// gfxCriticalNote << "Something to report and not assert";
+// while the critical error is
+// gfxCriticalError() << "Something to report and assert";
+#define gfxCriticalNote gfxCriticalError(gfxCriticalError::DefaultOptions(false))
+#define gfxCriticalNoteOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalNote
+
+// The "once" versions will only trigger the first time through. You can do this:
+// gfxCriticalErrorOnce() << "This message only shows up once;
+// instead of the usual:
+// static bool firstTime = true;
+// if (firstTime) {
+// firstTime = false;
+// gfxCriticalError() << "This message only shows up once;
+// }
+#if defined(DEBUG)
+#define gfxDebug mozilla::gfx::DebugLog
+#define gfxDebugOnce static gfxDebug GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxDebug
+#else
+#define gfxDebug if (1) ; else mozilla::gfx::NoLog
+#define gfxDebugOnce if (1) ; else mozilla::gfx::NoLog
+#endif
+
+// Have gfxWarning available (behind a runtime preference)
+#define gfxWarning mozilla::gfx::WarningLog
+#define gfxWarningOnce static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxWarning
+
+// You should create a (new) enum in the LogReason and use it for the reason
+// parameter to ensure uniqueness.
+#define gfxDevCrash(reason) gfxCriticalError(int(gfx::LogOptions::AutoPrefix) | int(gfx::LogOptions::AssertOnCall) | int(gfx::LogOptions::CrashAction), (reason))
+
+// See nsDebug.h and the NS_WARN_IF macro
+
+#ifdef __cplusplus
+ // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds
+inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ gfxCriticalError() << aExpr << " at " << aFile << ":" << aLine;
+ }
+ return aCondition;
+}
+#define MOZ2D_ERROR_IF(condition) \
+ MOZ2D_error_if_impl(condition, #condition, __FILE__, __LINE__)
+
+#ifdef DEBUG
+inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ gfxWarning() << aExpr << " at " << aFile << ":" << aLine;
+ }
+ return aCondition;
+}
+#define MOZ2D_WARN_IF(condition) \
+ MOZ2D_warn_if_impl(condition, #condition, __FILE__, __LINE__)
+#else
+#define MOZ2D_WARN_IF(condition) (bool)(condition)
+#endif
+#endif
+
+const int INDENT_PER_LEVEL = 2;
+
+class TreeLog
+{
+public:
+ explicit TreeLog(const std::string& aPrefix = "")
+ : mLog(int(LogOptions::NoNewline)),
+ mPrefix(aPrefix),
+ mDepth(0),
+ mStartOfLine(true),
+ mConditionedOnPref(false),
+ mPrefFunction(nullptr) {}
+
+ template <typename T>
+ TreeLog& operator<<(const T& aObject) {
+ if (mConditionedOnPref && !mPrefFunction()) {
+ return *this;
+ }
+ if (mStartOfLine) {
+ mLog << '[' << mPrefix << "] " << std::string(mDepth * INDENT_PER_LEVEL, ' ');
+ mStartOfLine = false;
+ }
+ mLog << aObject;
+ if (EndsInNewline(aObject)) {
+ // Don't indent right here as the user may change the indent
+ // between now and the first output to the next line.
+ mLog.Flush();
+ mStartOfLine = true;
+ }
+ return *this;
+ }
+
+ void IncreaseIndent() { ++mDepth; }
+ void DecreaseIndent() {
+ MOZ_ASSERT(mDepth > 0);
+ --mDepth;
+ }
+
+ void ConditionOnPrefFunction(bool(*aPrefFunction)()) {
+ mConditionedOnPref = true;
+ mPrefFunction = aPrefFunction;
+ }
+private:
+ Log<LOG_DEBUG> mLog;
+ std::string mPrefix;
+ uint32_t mDepth;
+ bool mStartOfLine;
+ bool mConditionedOnPref;
+ bool (*mPrefFunction)();
+
+ template <typename T>
+ static bool EndsInNewline(const T& aObject) {
+ return false;
+ }
+
+ static bool EndsInNewline(const std::string& aString) {
+ return !aString.empty() && aString[aString.length() - 1] == '\n';
+ }
+
+ static bool EndsInNewline(char aChar) {
+ return aChar == '\n';
+ }
+
+ static bool EndsInNewline(const char* aString) {
+ return EndsInNewline(std::string(aString));
+ }
+};
+
+class TreeAutoIndent
+{
+public:
+ explicit TreeAutoIndent(TreeLog& aTreeLog) : mTreeLog(aTreeLog) {
+ mTreeLog.IncreaseIndent();
+ }
+
+ TreeAutoIndent(const TreeAutoIndent& aTreeAutoIndent) :
+ TreeAutoIndent(aTreeAutoIndent.mTreeLog) {
+ mTreeLog.IncreaseIndent();
+ }
+
+ TreeAutoIndent& operator=(const TreeAutoIndent& aTreeAutoIndent) = delete;
+
+ ~TreeAutoIndent() {
+ mTreeLog.DecreaseIndent();
+ }
+private:
+ TreeLog& mTreeLog;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_LOGGING_H_ */
diff --git a/system/graphics/2d/LoggingConstants.h b/system/graphics/2d/LoggingConstants.h
new file mode 100644
index 000000000..08eb2014d
--- /dev/null
+++ b/system/graphics/2d/LoggingConstants.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_LOGGING_CONSTANTS_H_
+#define MOZILLA_GFX_LOGGING_CONSTANTS_H_
+
+namespace mozilla {
+namespace gfx {
+
+// Attempting to be consistent with prlog values, but that isn't critical
+// (and note that 5 has a special meaning - see the description
+// with LoggingPrefs::sGfxLogLevel)
+const int LOG_CRITICAL = 1;
+const int LOG_WARNING = 2;
+const int LOG_DEBUG = 3;
+const int LOG_DEBUG_PRLOG = 4;
+const int LOG_EVERYTHING = 5; // This needs to be the highest value
+
+#if defined(DEBUG)
+const int LOG_DEFAULT = LOG_EVERYTHING;
+#else
+const int LOG_DEFAULT = LOG_CRITICAL;
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_LOGGING_CONSTANTS_H_ */
diff --git a/system/graphics/2d/MMIHelpers.h b/system/graphics/2d/MMIHelpers.h
new file mode 100644
index 000000000..2a9b16a9d
--- /dev/null
+++ b/system/graphics/2d/MMIHelpers.h
@@ -0,0 +1,196 @@
+/*
+ ============================================================================
+ Name : MMIHelpers.h
+ Author : Heiher <r@hev.cc>
+ Version : 0.0.1
+ Copyright : Copyright (c) 2015 everyone.
+ Description : The helpers for x86 SSE to Loongson MMI.
+ ============================================================================
+ */
+
+#ifndef __MMI_HELPERS_H__
+#define __MMI_HELPERS_H__
+
+#define __mm_packxxxx(_f, _D, _d, _s, _t) \
+ #_f" %["#_t"], %["#_d"h], %["#_s"h] \n\t" \
+ #_f" %["#_D"l], %["#_d"l], %["#_s"l] \n\t" \
+ "punpckhwd %["#_D"h], %["#_D"l], %["#_t"] \n\t" \
+ "punpcklwd %["#_D"l], %["#_D"l], %["#_t"] \n\t"
+
+#define _mm_or(_D, _d, _s) \
+ "or %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "or %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+#define _mm_xor(_D, _d, _s) \
+ "xor %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "xor %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+#define _mm_and(_D, _d, _s) \
+ "and %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "and %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pandn */
+#define _mm_pandn(_D, _d, _s) \
+ "pandn %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pandn %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pshuflw */
+#define _mm_pshuflh(_D, _d, _s) \
+ "mov.d %["#_D"h], %["#_d"h] \n\t" \
+ "pshufh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psllw (bits) */
+#define _mm_psllh(_D, _d, _s) \
+ "psllh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psllh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: pslld (bits) */
+#define _mm_psllw(_D, _d, _s) \
+ "psllw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psllw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psllq (bits) */
+#define _mm_pslld(_D, _d, _s) \
+ "dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: pslldq (bytes) */
+#define _mm_psllq(_D, _d, _s, _s64, _tf) \
+ "subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
+ "dsrl %["#_tf"], %["#_d"l], %["#_tf"] \n\t" \
+ "dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
+ "or %["#_D"h], %["#_D"h], %["#_tf"] \n\t"
+
+/* SSE: psrlw (bits) */
+#define _mm_psrlh(_D, _d, _s) \
+ "psrlh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psrlh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrld (bits) */
+#define _mm_psrlw(_D, _d, _s) \
+ "psrlw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psrlw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrlq (bits) */
+#define _mm_psrld(_D, _d, _s) \
+ "dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrldq (bytes) */
+#define _mm_psrlq(_D, _d, _s, _s64, _tf) \
+ "subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
+ "dsll %["#_tf"], %["#_d"h], %["#_tf"] \n\t" \
+ "dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
+ "or %["#_D"l], %["#_D"l], %["#_tf"] \n\t"
+
+/* SSE: psrad */
+#define _mm_psraw(_D, _d, _s) \
+ "psraw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psraw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: paddb */
+#define _mm_paddb(_D, _d, _s) \
+ "paddb %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddb %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddw */
+#define _mm_paddh(_D, _d, _s) \
+ "paddh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddd */
+#define _mm_paddw(_D, _d, _s) \
+ "paddw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddq */
+#define _mm_paddd(_D, _d, _s) \
+ "dadd %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "dadd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: psubw */
+#define _mm_psubh(_D, _d, _s) \
+ "psubh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "psubh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: psubd */
+#define _mm_psubw(_D, _d, _s) \
+ "psubw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "psubw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmaxub */
+#define _mm_pmaxub(_D, _d, _s) \
+ "pmaxub %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmaxub %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmullw */
+#define _mm_pmullh(_D, _d, _s) \
+ "pmullh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmullh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmulhw */
+#define _mm_pmulhh(_D, _d, _s) \
+ "pmulhh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmulhh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmuludq */
+#define _mm_pmuluw(_D, _d, _s) \
+ "pmuluw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmuluw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: packsswb */
+#define _mm_packsshb(_D, _d, _s, _t) \
+ __mm_packxxxx(packsshb, _D, _d, _s, _t)
+
+/* SSE: packssdw */
+#define _mm_packsswh(_D, _d, _s, _t) \
+ __mm_packxxxx(packsswh, _D, _d, _s, _t)
+
+/* SSE: packuswb */
+#define _mm_packushb(_D, _d, _s, _t) \
+ __mm_packxxxx(packushb, _D, _d, _s, _t)
+
+/* SSE: punpcklbw */
+#define _mm_punpcklbh(_D, _d, _s) \
+ "punpckhbh %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklbh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpcklwd */
+#define _mm_punpcklhw(_D, _d, _s) \
+ "punpckhhw %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklhw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpckldq */
+#define _mm_punpcklwd(_D, _d, _s) \
+ "punpckhwd %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklwd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpcklqdq */
+#define _mm_punpckldq(_D, _d, _s) \
+ "mov.d %["#_D"h], %["#_s"l] \n\t" \
+ "mov.d %["#_D"l], %["#_d"l] \n\t"
+
+/* SSE: punpckhbw */
+#define _mm_punpckhbh(_D, _d, _s) \
+ "punpcklbh %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhbh %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhwd */
+#define _mm_punpckhhw(_D, _d, _s) \
+ "punpcklhw %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhhw %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhdq */
+#define _mm_punpckhwd(_D, _d, _s) \
+ "punpcklwd %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhwd %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhqdq */
+#define _mm_punpckhdq(_D, _d, _s) \
+ "mov.d %["#_D"l], %["#_d"h] \n\t" \
+ "mov.d %["#_D"h], %["#_s"h] \n\t"
+
+#endif /* __MMI_HELPERS_H__ */
+
diff --git a/system/graphics/2d/Matrix.cpp b/system/graphics/2d/Matrix.cpp
new file mode 100644
index 000000000..b3fce00ba
--- /dev/null
+++ b/system/graphics/2d/Matrix.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Matrix.h"
+#include "Quaternion.h"
+#include "Tools.h"
+#include <algorithm>
+#include <ostream>
+#include <math.h>
+#include <float.h> // for FLT_EPSILON
+
+#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
+
+using namespace std;
+
+
+namespace mozilla {
+namespace gfx {
+
+/* Force small values to zero. We do this to avoid having sin(360deg)
+ * evaluate to a tiny but nonzero value.
+ */
+double
+FlushToZero(double aVal)
+{
+ // XXX Is double precision really necessary here
+ if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
+ return 0.0f;
+ } else {
+ return aVal;
+ }
+}
+
+/* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is
+ * undefined or very large, SafeTangent returns a manageably large value
+ * of the correct sign.
+ */
+double
+SafeTangent(double aTheta)
+{
+ // XXX Is double precision really necessary here
+ const double kEpsilon = 0.0001;
+
+ /* tan(theta) = sin(theta)/cos(theta); problems arise when
+ * cos(theta) is too close to zero. Limit cos(theta) to the
+ * range [-1, -epsilon] U [epsilon, 1].
+ */
+
+ double sinTheta = sin(aTheta);
+ double cosTheta = cos(aTheta);
+
+ if (cosTheta >= 0 && cosTheta < kEpsilon) {
+ cosTheta = kEpsilon;
+ } else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
+ cosTheta = -kEpsilon;
+ }
+ return FlushToZero(sinTheta / cosTheta);
+}
+
+std::ostream&
+operator<<(std::ostream& aStream, const Matrix& aMatrix)
+{
+ return aStream << "[ " << aMatrix._11
+ << " " << aMatrix._12
+ << "; " << aMatrix._21
+ << " " << aMatrix._22
+ << "; " << aMatrix._31
+ << " " << aMatrix._32
+ << "; ]";
+}
+
+Matrix
+Matrix::Rotation(Float aAngle)
+{
+ Matrix newMatrix;
+
+ Float s = sinf(aAngle);
+ Float c = cosf(aAngle);
+
+ newMatrix._11 = c;
+ newMatrix._12 = s;
+ newMatrix._21 = -s;
+ newMatrix._22 = c;
+
+ return newMatrix;
+}
+
+Rect
+Matrix::TransformBounds(const Rect &aRect) const
+{
+ int i;
+ Point quad[4];
+ Float min_x, max_x;
+ Float min_y, max_y;
+
+ quad[0] = TransformPoint(aRect.TopLeft());
+ quad[1] = TransformPoint(aRect.TopRight());
+ quad[2] = TransformPoint(aRect.BottomLeft());
+ quad[3] = TransformPoint(aRect.BottomRight());
+
+ min_x = max_x = quad[0].x;
+ min_y = max_y = quad[0].y;
+
+ for (i = 1; i < 4; i++) {
+ if (quad[i].x < min_x)
+ min_x = quad[i].x;
+ if (quad[i].x > max_x)
+ max_x = quad[i].x;
+
+ if (quad[i].y < min_y)
+ min_y = quad[i].y;
+ if (quad[i].y > max_y)
+ max_y = quad[i].y;
+ }
+
+ return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+Matrix&
+Matrix::NudgeToIntegers()
+{
+ NudgeToInteger(&_11);
+ NudgeToInteger(&_12);
+ NudgeToInteger(&_21);
+ NudgeToInteger(&_22);
+ NudgeToInteger(&_31);
+ NudgeToInteger(&_32);
+ return *this;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Matrix.h b/system/graphics/2d/Matrix.h
new file mode 100644
index 000000000..d6835c8e6
--- /dev/null
+++ b/system/graphics/2d/Matrix.h
@@ -0,0 +1,1690 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_MATRIX_H_
+#define MOZILLA_GFX_MATRIX_H_
+
+#include "Types.h"
+#include "Triangle.h"
+#include "Rect.h"
+#include "Point.h"
+#include "Quaternion.h"
+#include <iosfwd>
+#include <math.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FloatingPoint.h"
+
+namespace mozilla {
+namespace gfx {
+
+static bool FuzzyEqual(Float aV1, Float aV2) {
+ // XXX - Check if fabs does the smart thing and just negates the sign bit.
+ return fabs(aV2 - aV1) < 1e-6;
+}
+
+class Matrix
+{
+public:
+ Matrix()
+ : _11(1.0f), _12(0)
+ , _21(0), _22(1.0f)
+ , _31(0), _32(0)
+ {}
+ Matrix(Float a11, Float a12, Float a21, Float a22, Float a31, Float a32)
+ : _11(a11), _12(a12)
+ , _21(a21), _22(a22)
+ , _31(a31), _32(a32)
+ {}
+ union {
+ struct {
+ Float _11, _12;
+ Float _21, _22;
+ Float _31, _32;
+ };
+ Float components[6];
+ };
+
+ MOZ_ALWAYS_INLINE Matrix Copy() const
+ {
+ return Matrix(*this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Matrix& aMatrix);
+
+ Point TransformPoint(const Point &aPoint) const
+ {
+ Point retPoint;
+
+ retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31;
+ retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32;
+
+ return retPoint;
+ }
+
+ Size TransformSize(const Size &aSize) const
+ {
+ Size retSize;
+
+ retSize.width = aSize.width * _11 + aSize.height * _21;
+ retSize.height = aSize.width * _12 + aSize.height * _22;
+
+ return retSize;
+ }
+
+ GFX2D_API Rect TransformBounds(const Rect& rect) const;
+
+ static Matrix Translation(Float aX, Float aY)
+ {
+ return Matrix(1.0f, 0.0f, 0.0f, 1.0f, aX, aY);
+ }
+
+ static Matrix Translation(Point aPoint)
+ {
+ return Translation(aPoint.x, aPoint.y);
+ }
+
+ /**
+ * Apply a translation to this matrix.
+ *
+ * The "Pre" in this method's name means that the translation is applied
+ * -before- this matrix's existing transformation. That is, any vector that
+ * is multiplied by the resulting matrix will first be translated, then be
+ * transformed by the original transform.
+ *
+ * Calling this method will result in this matrix having the same value as
+ * the result of:
+ *
+ * Matrix::Translation(x, y) * this
+ *
+ * (Note that in performance critical code multiplying by the result of a
+ * Translation()/Scaling() call is not recommended since that results in a
+ * full matrix multiply involving 12 floating-point multiplications. Calling
+ * this method would be preferred since it only involves four floating-point
+ * multiplications.)
+ */
+ Matrix &PreTranslate(Float aX, Float aY)
+ {
+ _31 += _11 * aX + _21 * aY;
+ _32 += _12 * aX + _22 * aY;
+
+ return *this;
+ }
+
+ Matrix &PreTranslate(const Point &aPoint)
+ {
+ return PreTranslate(aPoint.x, aPoint.y);
+ }
+
+ /**
+ * Similar to PreTranslate, but the translation is applied -after- this
+ * matrix's existing transformation instead of before it.
+ *
+ * This method is generally less used than PreTranslate since typically code
+ * want to adjust an existing user space to device space matrix to create a
+ * transform to device space from a -new- user space (translated from the
+ * previous user space). In that case consumers will need to use the Pre*
+ * variants of the matrix methods rather than using the Post* methods, since
+ * the Post* methods add a transform to the device space end of the
+ * transformation.
+ */
+ Matrix &PostTranslate(Float aX, Float aY)
+ {
+ _31 += aX;
+ _32 += aY;
+ return *this;
+ }
+
+ Matrix &PostTranslate(const Point &aPoint)
+ {
+ return PostTranslate(aPoint.x, aPoint.y);
+ }
+
+ static Matrix Scaling(Float aScaleX, Float aScaleY)
+ {
+ return Matrix(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f);
+ }
+
+ /**
+ * Similar to PreTranslate, but applies a scale instead of a translation.
+ */
+ Matrix &PreScale(Float aX, Float aY)
+ {
+ _11 *= aX;
+ _12 *= aX;
+ _21 *= aY;
+ _22 *= aY;
+
+ return *this;
+ }
+
+ /**
+ * Similar to PostTranslate, but applies a scale instead of a translation.
+ */
+ Matrix &PostScale(Float aScaleX, Float aScaleY)
+ {
+ _11 *= aScaleX;
+ _12 *= aScaleY;
+ _21 *= aScaleX;
+ _22 *= aScaleY;
+ _31 *= aScaleX;
+ _32 *= aScaleY;
+
+ return *this;
+ }
+
+ GFX2D_API static Matrix Rotation(Float aAngle);
+
+ /**
+ * Similar to PreTranslate, but applies a rotation instead of a translation.
+ */
+ Matrix &PreRotate(Float aAngle)
+ {
+ return *this = Matrix::Rotation(aAngle) * *this;
+ }
+
+ bool Invert()
+ {
+ // Compute co-factors.
+ Float A = _22;
+ Float B = -_21;
+ Float C = _21 * _32 - _22 * _31;
+ Float D = -_12;
+ Float E = _11;
+ Float F = _31 * _12 - _11 * _32;
+
+ Float det = Determinant();
+
+ if (!det) {
+ return false;
+ }
+
+ Float inv_det = 1 / det;
+
+ _11 = inv_det * A;
+ _12 = inv_det * D;
+ _21 = inv_det * B;
+ _22 = inv_det * E;
+ _31 = inv_det * C;
+ _32 = inv_det * F;
+
+ return true;
+ }
+
+ Matrix Inverse() const
+ {
+ Matrix clone = *this;
+ DebugOnly<bool> inverted = clone.Invert();
+ MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+ return clone;
+ }
+
+ Float Determinant() const
+ {
+ return _11 * _22 - _12 * _21;
+ }
+
+ Matrix operator*(const Matrix &aMatrix) const
+ {
+ Matrix resultMatrix;
+
+ resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21;
+ resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
+ resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
+ resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
+ resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31;
+ resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32;
+
+ return resultMatrix;
+ }
+
+ Matrix& operator*=(const Matrix &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ /**
+ * Multiplies in the opposite order to operator=*.
+ */
+ Matrix &PreMultiply(const Matrix &aMatrix)
+ {
+ *this = aMatrix * *this;
+ return *this;
+ }
+
+ /* Returns true if the other matrix is fuzzy-equal to this matrix.
+ * Note that this isn't a cheap comparison!
+ */
+ bool operator==(const Matrix& other) const
+ {
+ return FuzzyEqual(_11, other._11) && FuzzyEqual(_12, other._12) &&
+ FuzzyEqual(_21, other._21) && FuzzyEqual(_22, other._22) &&
+ FuzzyEqual(_31, other._31) && FuzzyEqual(_32, other._32);
+ }
+
+ bool operator!=(const Matrix& other) const
+ {
+ return !(*this == other);
+ }
+
+ bool ExactlyEquals(const Matrix& o) const
+ {
+ return _11 == o._11 && _12 == o._12 &&
+ _21 == o._21 && _22 == o._22 &&
+ _31 == o._31 && _32 == o._32;
+ }
+
+ /* Verifies that the matrix contains no Infs or NaNs. */
+ bool IsFinite() const
+ {
+ return mozilla::IsFinite(_11) && mozilla::IsFinite(_12) &&
+ mozilla::IsFinite(_21) && mozilla::IsFinite(_22) &&
+ mozilla::IsFinite(_31) && mozilla::IsFinite(_32);
+ }
+
+ /* Returns true if the matrix is a rectilinear transformation (i.e.
+ * grid-aligned rectangles are transformed to grid-aligned rectangles)
+ */
+ bool IsRectilinear() const {
+ if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
+ return true;
+ } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ bool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !FuzzyEqual(_31, floor(_31 + Float(0.5))) ||
+ !FuzzyEqual(_32, floor(_32 + Float(0.5)));
+ }
+
+ /**
+ * Returns true if the matrix only has an integer translation.
+ */
+ bool HasOnlyIntegerTranslation() const {
+ return !HasNonIntegerTranslation();
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a straight translation.
+ */
+ bool HasNonTranslation() const {
+ return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
+ !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or a -1 y scale (y axis flip)
+ */
+ bool HasNonTranslationOrFlip() const {
+ return !FuzzyEqual(_11, 1.0) ||
+ (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) ||
+ !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /* Returns true if the matrix is an identity matrix.
+ */
+ bool IsIdentity() const
+ {
+ return _11 == 1.0f && _12 == 0.0f &&
+ _21 == 0.0f && _22 == 1.0f &&
+ _31 == 0.0f && _32 == 0.0f;
+ }
+
+ /* Returns true if the matrix is singular.
+ */
+ bool IsSingular() const
+ {
+ Float det = Determinant();
+ return !mozilla::IsFinite(det) || det == 0;
+ }
+
+ GFX2D_API Matrix &NudgeToIntegers();
+
+ bool IsTranslation() const
+ {
+ return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
+ FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
+ }
+
+ static bool FuzzyIsInteger(Float aValue)
+ {
+ return FuzzyEqual(aValue, floorf(aValue + 0.5f));
+ }
+
+ bool IsIntegerTranslation() const
+ {
+ return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
+ }
+
+ bool IsAllIntegers() const
+ {
+ return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) &&
+ FuzzyIsInteger(_21) && FuzzyIsInteger(_22) &&
+ FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
+ }
+
+ Point GetTranslation() const {
+ return Point(_31, _32);
+ }
+
+ /**
+ * Returns true if matrix is multiple of 90 degrees rotation with flipping,
+ * scaling and translation.
+ */
+ bool PreservesAxisAlignedRectangles() const {
+ return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0))
+ || (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or scale; this is, if there is
+ * rotation.
+ */
+ bool HasNonAxisAlignedTransform() const {
+ return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix has negative scaling (i.e. flip).
+ */
+ bool HasNegativeScaling() const {
+ return (_11 < 0.0) || (_22 < 0.0);
+ }
+};
+
+// Helper functions used by Matrix4x4Typed defined in Matrix.cpp
+double
+SafeTangent(double aTheta);
+double
+FlushToZero(double aVal);
+
+template<class Units, class F>
+Point4DTyped<Units, F>
+ComputePerspectivePlaneIntercept(const Point4DTyped<Units, F>& aFirst,
+ const Point4DTyped<Units, F>& aSecond)
+{
+ // This function will always return a point with a w value of 0.
+ // The X, Y, and Z components will point towards an infinite vanishing
+ // point.
+
+ // We want to interpolate aFirst and aSecond to find the point intersecting
+ // with the w=0 plane.
+
+ // Since we know what we want the w component to be, we can rearrange the
+ // interpolation equation and solve for t.
+ float t = -aFirst.w / (aSecond.w - aFirst.w);
+
+ // Use t to find the remainder of the components
+ return aFirst + (aSecond - aFirst) * t;
+}
+
+
+template <typename SourceUnits, typename TargetUnits>
+class Matrix4x4Typed
+{
+public:
+ typedef PointTyped<SourceUnits> SourcePoint;
+ typedef PointTyped<TargetUnits> TargetPoint;
+ typedef Point3DTyped<SourceUnits> SourcePoint3D;
+ typedef Point3DTyped<TargetUnits> TargetPoint3D;
+ typedef Point4DTyped<SourceUnits> SourcePoint4D;
+ typedef Point4DTyped<TargetUnits> TargetPoint4D;
+ typedef RectTyped<SourceUnits> SourceRect;
+ typedef RectTyped<TargetUnits> TargetRect;
+
+ Matrix4x4Typed()
+ : _11(1.0f), _12(0.0f), _13(0.0f), _14(0.0f)
+ , _21(0.0f), _22(1.0f), _23(0.0f), _24(0.0f)
+ , _31(0.0f), _32(0.0f), _33(1.0f), _34(0.0f)
+ , _41(0.0f), _42(0.0f), _43(0.0f), _44(1.0f)
+ {}
+
+ Matrix4x4Typed(Float a11, Float a12, Float a13, Float a14,
+ Float a21, Float a22, Float a23, Float a24,
+ Float a31, Float a32, Float a33, Float a34,
+ Float a41, Float a42, Float a43, Float a44)
+ : _11(a11), _12(a12), _13(a13), _14(a14)
+ , _21(a21), _22(a22), _23(a23), _24(a24)
+ , _31(a31), _32(a32), _33(a33), _34(a34)
+ , _41(a41), _42(a42), _43(a43), _44(a44)
+ {}
+
+ explicit Matrix4x4Typed(const Float aArray[16])
+ {
+ memcpy(components, aArray, sizeof(components));
+ }
+
+ Matrix4x4Typed(const Matrix4x4Typed& aOther)
+ {
+ memcpy(this, &aOther, sizeof(*this));
+ }
+
+ union {
+ struct {
+ Float _11, _12, _13, _14;
+ Float _21, _22, _23, _24;
+ Float _31, _32, _33, _34;
+ Float _41, _42, _43, _44;
+ };
+ Float components[16];
+ };
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Matrix4x4Typed& aMatrix)
+ {
+ const Float *f = &aMatrix._11;
+ aStream << "[ " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ]" << std::endl;
+ return aStream;
+ }
+
+ Point4D& operator[](int aIndex)
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return *reinterpret_cast<Point4D*>((&_11)+4*aIndex);
+ }
+ const Point4D& operator[](int aIndex) const
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return *reinterpret_cast<const Point4D*>((&_11)+4*aIndex);
+ }
+
+ /**
+ * Returns true if the matrix is isomorphic to a 2D affine transformation.
+ */
+ bool Is2D() const
+ {
+ if (_13 != 0.0f || _14 != 0.0f ||
+ _23 != 0.0f || _24 != 0.0f ||
+ _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
+ _43 != 0.0f || _44 != 1.0f) {
+ return false;
+ }
+ return true;
+ }
+
+ bool Is2D(Matrix* aMatrix) const {
+ if (!Is2D()) {
+ return false;
+ }
+ if (aMatrix) {
+ aMatrix->_11 = _11;
+ aMatrix->_12 = _12;
+ aMatrix->_21 = _21;
+ aMatrix->_22 = _22;
+ aMatrix->_31 = _41;
+ aMatrix->_32 = _42;
+ }
+ return true;
+ }
+
+ Matrix As2D() const
+ {
+ MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
+
+ return Matrix(_11, _12, _21, _22, _41, _42);
+ }
+
+ bool CanDraw2D(Matrix* aMatrix = nullptr) const {
+ if (_14 != 0.0f ||
+ _24 != 0.0f ||
+ _44 != 1.0f) {
+ return false;
+ }
+ if (aMatrix) {
+ aMatrix->_11 = _11;
+ aMatrix->_12 = _12;
+ aMatrix->_21 = _21;
+ aMatrix->_22 = _22;
+ aMatrix->_31 = _41;
+ aMatrix->_32 = _42;
+ }
+ return true;
+ }
+
+ Matrix4x4Typed& ProjectTo2D() {
+ _31 = 0.0f;
+ _32 = 0.0f;
+ _13 = 0.0f;
+ _23 = 0.0f;
+ _33 = 1.0f;
+ _43 = 0.0f;
+ _34 = 0.0f;
+ // Some matrices, such as those derived from perspective transforms,
+ // can modify _44 from 1, while leaving the rest of the fourth column
+ // (_14, _24) at 0. In this case, after resetting the third row and
+ // third column above, the value of _44 functions only to scale the
+ // coordinate transform divide by W. The matrix can be converted to
+ // a true 2D matrix by normalizing out the scaling effect of _44 on
+ // the remaining components ahead of time.
+ if (_14 == 0.0f && _24 == 0.0f &&
+ _44 != 1.0f && _44 != 0.0f) {
+ Float scale = 1.0f / _44;
+ _11 *= scale;
+ _12 *= scale;
+ _21 *= scale;
+ _22 *= scale;
+ _41 *= scale;
+ _42 *= scale;
+ _44 = 1.0f;
+ }
+ return *this;
+ }
+
+ template<class F>
+ Point4DTyped<TargetUnits, F>
+ ProjectPoint(const PointTyped<SourceUnits, F>& aPoint) const {
+ // Find a value for z that will transform to 0.
+
+ // The transformed value of z is computed as:
+ // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
+
+ // Solving for z when z' = 0 gives us:
+ F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
+
+ // Compute the transformed point
+ return this->TransformPoint(Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1));
+ }
+
+ template<class F>
+ RectTyped<TargetUnits, F>
+ ProjectRectBounds(const RectTyped<SourceUnits, F>& aRect, const RectTyped<TargetUnits, F>& aClip) const
+ {
+ // This function must never return std::numeric_limits<Float>::max() or any
+ // other arbitrary large value in place of inifinity. This often occurs when
+ // aRect is an inversed projection matrix or when aRect is transformed to be
+ // partly behind and in front of the camera (w=0 plane in homogenous
+ // coordinates) - See Bug 1035611
+
+ // Some call-sites will call RoundGfxRectToAppRect which clips both the
+ // extents and dimensions of the rect to be bounded by nscoord_MAX.
+ // If we return a Rect that, when converted to nscoords, has a width or height
+ // greater than nscoord_MAX, RoundGfxRectToAppRect will clip the overflow
+ // off both the min and max end of the rect after clipping the extents of the
+ // rect, resulting in a translation of the rect towards the infinite end.
+
+ // The bounds returned by ProjectRectBounds are expected to be clipped only on
+ // the edges beyond the bounds of the coordinate system; otherwise, the
+ // clipped bounding box would be smaller than the correct one and result
+ // bugs such as incorrect culling (eg. Bug 1073056)
+
+ // To address this without requiring all code to work in homogenous
+ // coordinates or interpret infinite values correctly, a specialized
+ // clipping function is integrated into ProjectRectBounds.
+
+ // Callers should pass an aClip value that represents the extents to clip
+ // the result to, in the same coordinate system as aRect.
+ Point4DTyped<TargetUnits, F> points[4];
+
+ points[0] = ProjectPoint(aRect.TopLeft());
+ points[1] = ProjectPoint(aRect.TopRight());
+ points[2] = ProjectPoint(aRect.BottomRight());
+ points[3] = ProjectPoint(aRect.BottomLeft());
+
+ F min_x = std::numeric_limits<F>::max();
+ F min_y = std::numeric_limits<F>::max();
+ F max_x = -std::numeric_limits<F>::max();
+ F max_y = -std::numeric_limits<F>::max();
+
+ for (int i=0; i<4; i++) {
+ // Only use points that exist above the w=0 plane
+ if (points[i].HasPositiveWCoord()) {
+ PointTyped<TargetUnits, F> point2d = aClip.ClampPoint(points[i].As2DPoint());
+ min_x = std::min<F>(point2d.x, min_x);
+ max_x = std::max<F>(point2d.x, max_x);
+ min_y = std::min<F>(point2d.y, min_y);
+ max_y = std::max<F>(point2d.y, max_y);
+ }
+
+ int next = (i == 3) ? 0 : i + 1;
+ if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) {
+ // If the line between two points crosses the w=0 plane, then interpolate
+ // to find the point of intersection with the w=0 plane and use that
+ // instead.
+ Point4DTyped<TargetUnits, F> intercept =
+ ComputePerspectivePlaneIntercept(points[i], points[next]);
+ // Since intercept.w will always be 0 here, we interpret x,y,z as a
+ // direction towards an infinite vanishing point.
+ if (intercept.x < 0.0f) {
+ min_x = aClip.x;
+ } else if (intercept.x > 0.0f) {
+ max_x = aClip.XMost();
+ }
+ if (intercept.y < 0.0f) {
+ min_y = aClip.y;
+ } else if (intercept.y > 0.0f) {
+ max_y = aClip.YMost();
+ }
+ }
+ }
+
+ if (max_x < min_x || max_y < min_y) {
+ return RectTyped<TargetUnits, F>(0, 0, 0, 0);
+ }
+
+ return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ /**
+ * TransformAndClipBounds transforms aRect as a bounding box, while clipping
+ * the transformed bounds to the extents of aClip.
+ */
+ template<class F>
+ RectTyped<TargetUnits, F> TransformAndClipBounds(const RectTyped<SourceUnits, F>& aRect,
+ const RectTyped<TargetUnits, F>& aClip) const
+ {
+ PointTyped<UnknownUnits, F> verts[kTransformAndClipRectMaxVerts];
+ size_t vertCount = TransformAndClipRect(aRect, aClip, verts);
+
+ F min_x = std::numeric_limits<F>::max();
+ F min_y = std::numeric_limits<F>::max();
+ F max_x = -std::numeric_limits<F>::max();
+ F max_y = -std::numeric_limits<F>::max();
+ for (size_t i=0; i < vertCount; i++) {
+ min_x = std::min(min_x, verts[i].x);
+ max_x = std::max(max_x, verts[i].x);
+ min_y = std::min(min_y, verts[i].y);
+ max_y = std::max(max_y, verts[i].y);
+ }
+
+ if (max_x < min_x || max_y < min_y) {
+ return RectTyped<TargetUnits, F>(0, 0, 0, 0);
+ }
+
+ return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ template<class F>
+ RectTyped<TargetUnits, F> TransformAndClipBounds(const TriangleTyped<SourceUnits, F>& aTriangle,
+ const RectTyped<TargetUnits, F>& aClip) const
+ {
+ return TransformAndClipBounds(aTriangle.BoundingBox(), aClip);
+ }
+
+ /**
+ * TransformAndClipRect projects a rectangle and clips against view frustum
+ * clipping planes in homogenous space so that its projected vertices are
+ * constrained within the 2d rectangle passed in aClip.
+ * The resulting vertices are populated in aVerts. aVerts must be
+ * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points.
+ * The vertex count is returned by TransformAndClipRect. It is possible to
+ * emit fewer than 3 vertices, indicating that aRect will not be visible
+ * within aClip.
+ */
+ template<class F>
+ size_t TransformAndClipRect(const RectTyped<SourceUnits, F>& aRect,
+ const RectTyped<TargetUnits, F>& aClip,
+ PointTyped<TargetUnits, F>* aVerts) const
+ {
+ // Initialize a double-buffered array of points in homogenous space with
+ // the input rectangle, aRect.
+ Point4DTyped<UnknownUnits, F> points[2][kTransformAndClipRectMaxVerts];
+ Point4DTyped<UnknownUnits, F>* dstPointStart = points[0];
+ Point4DTyped<UnknownUnits, F>* dstPoint = dstPointStart;
+
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.x, aRect.y, 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.y, 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.YMost(), 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.x, aRect.YMost(), 0, 1));
+
+ // View frustum clipping planes are described as normals originating from
+ // the 0,0,0,0 origin.
+ Point4DTyped<UnknownUnits, F> planeNormals[4];
+ planeNormals[0] = Point4DTyped<UnknownUnits, F>(1.0, 0.0, 0.0, -aClip.x);
+ planeNormals[1] = Point4DTyped<UnknownUnits, F>(-1.0, 0.0, 0.0, aClip.XMost());
+ planeNormals[2] = Point4DTyped<UnknownUnits, F>(0.0, 1.0, 0.0, -aClip.y);
+ planeNormals[3] = Point4DTyped<UnknownUnits, F>(0.0, -1.0, 0.0, aClip.YMost());
+
+ // Iterate through each clipping plane and clip the polygon.
+ // For each clipping plane, we intersect the plane with all polygon edges.
+ // Each pass can increase or decrease the number of points that make up the
+ // current clipped polygon. We double buffer that set of points, alternating
+ // between points[0] and points[1].
+ for (int plane=0; plane < 4; plane++) {
+ planeNormals[plane].Normalize();
+ Point4DTyped<UnknownUnits, F>* srcPoint = dstPointStart;
+ Point4DTyped<UnknownUnits, F>* srcPointEnd = dstPoint;
+
+ dstPointStart = points[~plane & 1];
+ dstPoint = dstPointStart;
+
+ // Iterate over the polygon edges. In each iteration the current edge is
+ // the edge from prevPoint to srcPoint. If the two end points lie on
+ // different sides of the plane, we have an intersection. Otherwise, the
+ // edge is either completely "inside" the half-space created by the
+ // clipping plane, and we add srcPoint, or it is completely "outside", and
+ // we discard srcPoint.
+ // We may create duplicated points in the polygon. We keep those around
+ // until all clipping is done and then filter out duplicates at the end.
+ Point4DTyped<UnknownUnits, F>* prevPoint = srcPointEnd - 1;
+ F prevDot = planeNormals[plane].DotProduct(*prevPoint);
+ while (srcPoint < srcPointEnd && ((dstPoint - dstPointStart) < kTransformAndClipRectMaxVerts)) {
+ F nextDot = planeNormals[plane].DotProduct(*srcPoint);
+
+ if ((nextDot >= 0.0) != (prevDot >= 0.0)) {
+ // An intersection with the clipping plane has been detected.
+ // Interpolate to find the intersecting point and emit it.
+ F t = -prevDot / (nextDot - prevDot);
+ *dstPoint++ = *srcPoint * t + *prevPoint * (1.0 - t);
+ }
+
+ if (nextDot >= 0.0) {
+ // Emit any source points that are on the positive side of the
+ // clipping plane.
+ *dstPoint++ = *srcPoint;
+ }
+
+ prevPoint = srcPoint++;
+ prevDot = nextDot;
+ }
+
+ if (dstPoint == dstPointStart) {
+ // No polygon points were produced, so the polygon has been
+ // completely clipped away by the current clipping plane. Exit.
+ break;
+ }
+ }
+
+ Point4DTyped<UnknownUnits, F>* srcPoint = dstPointStart;
+ Point4DTyped<UnknownUnits, F>* srcPointEnd = dstPoint;
+ size_t vertCount = 0;
+ while (srcPoint < srcPointEnd) {
+ PointTyped<TargetUnits, F> p;
+ if (srcPoint->w == 0.0) {
+ // If a point lies on the intersection of the clipping planes at
+ // (0,0,0,0), we must avoid a division by zero w component.
+ p = PointTyped<TargetUnits, F>(0.0, 0.0);
+ } else {
+ p = srcPoint->As2DPoint();
+ }
+ // Emit only unique points
+ if (vertCount == 0 || p != aVerts[vertCount - 1]) {
+ aVerts[vertCount++] = p;
+ }
+ srcPoint++;
+ }
+
+ return vertCount;
+ }
+
+ static const int kTransformAndClipRectMaxVerts = 32;
+
+ static Matrix4x4Typed From2D(const Matrix &aMatrix) {
+ Matrix4x4Typed matrix;
+ matrix._11 = aMatrix._11;
+ matrix._12 = aMatrix._12;
+ matrix._21 = aMatrix._21;
+ matrix._22 = aMatrix._22;
+ matrix._41 = aMatrix._31;
+ matrix._42 = aMatrix._32;
+ return matrix;
+ }
+
+ bool Is2DIntegerTranslation() const
+ {
+ return Is2D() && As2D().IsIntegerTranslation();
+ }
+
+ TargetPoint4D TransposeTransform4D(const SourcePoint4D& aPoint) const
+ {
+ Float x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14;
+ Float y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24;
+ Float z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34;
+ Float w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44;
+
+ return TargetPoint4D(x, y, z, w);
+ }
+
+ template<class F>
+ Point4DTyped<TargetUnits, F> TransformPoint(const Point4DTyped<SourceUnits, F>& aPoint) const
+ {
+ Point4DTyped<TargetUnits, F> retPoint;
+
+ retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41;
+ retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42;
+ retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43;
+ retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44;
+
+ return retPoint;
+ }
+
+ template<class F>
+ Point3DTyped<TargetUnits, F> TransformPoint(const Point3DTyped<SourceUnits, F>& aPoint) const
+ {
+ Point3DTyped<TargetUnits, F> result;
+ result.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41;
+ result.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42;
+ result.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43;
+
+ result /= (aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44);
+
+ return result;
+ }
+
+ template<class F>
+ PointTyped<TargetUnits, F> TransformPoint(const PointTyped<SourceUnits, F> &aPoint) const
+ {
+ Point4DTyped<SourceUnits, F> temp(aPoint.x, aPoint.y, 0, 1);
+ return TransformPoint(temp).As2DPoint();
+ }
+
+ template<class F>
+ GFX2D_API RectTyped<TargetUnits, F> TransformBounds(const RectTyped<SourceUnits, F>& aRect) const
+ {
+ Point4DTyped<TargetUnits, F> verts[4];
+ verts[0] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.x, aRect.y, 0.0, 1.0));
+ verts[1] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.XMost(), aRect.y, 0.0, 1.0));
+ verts[2] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.XMost(), aRect.YMost(), 0.0, 1.0));
+ verts[3] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.x, aRect.YMost(), 0.0, 1.0));
+
+ PointTyped<TargetUnits, F> quad[4];
+ F min_x, max_x;
+ F min_y, max_y;
+
+ quad[0] = TransformPoint(aRect.TopLeft());
+ quad[1] = TransformPoint(aRect.TopRight());
+ quad[2] = TransformPoint(aRect.BottomLeft());
+ quad[3] = TransformPoint(aRect.BottomRight());
+
+ min_x = max_x = quad[0].x;
+ min_y = max_y = quad[0].y;
+
+ for (int i = 1; i < 4; i++) {
+ if (quad[i].x < min_x) {
+ min_x = quad[i].x;
+ }
+ if (quad[i].x > max_x) {
+ max_x = quad[i].x;
+ }
+
+ if (quad[i].y < min_y) {
+ min_y = quad[i].y;
+ }
+ if (quad[i].y > max_y) {
+ max_y = quad[i].y;
+ }
+ }
+
+ return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ static Matrix4x4Typed Translation(Float aX, Float aY, Float aZ)
+ {
+ return Matrix4x4Typed(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ aX, aY, aZ, 1.0f);
+ }
+
+ static Matrix4x4Typed Translation(const TargetPoint3D& aP)
+ {
+ return Translation(aP.x, aP.y, aP.z);
+ }
+
+ static Matrix4x4Typed Translation(const TargetPoint& aP)
+ {
+ return Translation(aP.x, aP.y, 0);
+ }
+
+ /**
+ * Apply a translation to this matrix.
+ *
+ * The "Pre" in this method's name means that the translation is applied
+ * -before- this matrix's existing transformation. That is, any vector that
+ * is multiplied by the resulting matrix will first be translated, then be
+ * transformed by the original transform.
+ *
+ * Calling this method will result in this matrix having the same value as
+ * the result of:
+ *
+ * Matrix4x4::Translation(x, y) * this
+ *
+ * (Note that in performance critical code multiplying by the result of a
+ * Translation()/Scaling() call is not recommended since that results in a
+ * full matrix multiply involving 64 floating-point multiplications. Calling
+ * this method would be preferred since it only involves 12 floating-point
+ * multiplications.)
+ */
+ Matrix4x4Typed &PreTranslate(Float aX, Float aY, Float aZ)
+ {
+ _41 += aX * _11 + aY * _21 + aZ * _31;
+ _42 += aX * _12 + aY * _22 + aZ * _32;
+ _43 += aX * _13 + aY * _23 + aZ * _33;
+ _44 += aX * _14 + aY * _24 + aZ * _34;
+
+ return *this;
+ }
+
+ Matrix4x4Typed &PreTranslate(const Point3D& aPoint) {
+ return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ /**
+ * Similar to PreTranslate, but the translation is applied -after- this
+ * matrix's existing transformation instead of before it.
+ *
+ * This method is generally less used than PreTranslate since typically code
+ * wants to adjust an existing user space to device space matrix to create a
+ * transform to device space from a -new- user space (translated from the
+ * previous user space). In that case consumers will need to use the Pre*
+ * variants of the matrix methods rather than using the Post* methods, since
+ * the Post* methods add a transform to the device space end of the
+ * transformation.
+ */
+ Matrix4x4Typed &PostTranslate(Float aX, Float aY, Float aZ)
+ {
+ _11 += _14 * aX;
+ _21 += _24 * aX;
+ _31 += _34 * aX;
+ _41 += _44 * aX;
+ _12 += _14 * aY;
+ _22 += _24 * aY;
+ _32 += _34 * aY;
+ _42 += _44 * aY;
+ _13 += _14 * aZ;
+ _23 += _24 * aZ;
+ _33 += _34 * aZ;
+ _43 += _44 * aZ;
+
+ return *this;
+ }
+
+ Matrix4x4Typed &PostTranslate(const TargetPoint3D& aPoint) {
+ return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ Matrix4x4Typed &PostTranslate(const TargetPoint& aPoint) {
+ return PostTranslate(aPoint.x, aPoint.y, 0);
+ }
+
+ static Matrix4x4Typed Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
+ {
+ return Matrix4x4Typed(aScaleX, 0.0f, 0.0f, 0.0f,
+ 0.0f, aScaleY, 0.0f, 0.0f,
+ 0.0f, 0.0f, aScaleZ, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+
+ /**
+ * Similar to PreTranslate, but applies a scale instead of a translation.
+ */
+ Matrix4x4Typed &PreScale(Float aX, Float aY, Float aZ)
+ {
+ _11 *= aX;
+ _12 *= aX;
+ _13 *= aX;
+ _14 *= aX;
+ _21 *= aY;
+ _22 *= aY;
+ _23 *= aY;
+ _24 *= aY;
+ _31 *= aZ;
+ _32 *= aZ;
+ _33 *= aZ;
+ _34 *= aZ;
+
+ return *this;
+ }
+
+ /**
+ * Similar to PostTranslate, but applies a scale instead of a translation.
+ */
+ Matrix4x4Typed &PostScale(Float aScaleX, Float aScaleY, Float aScaleZ)
+ {
+ _11 *= aScaleX;
+ _21 *= aScaleX;
+ _31 *= aScaleX;
+ _41 *= aScaleX;
+ _12 *= aScaleY;
+ _22 *= aScaleY;
+ _32 *= aScaleY;
+ _42 *= aScaleY;
+ _13 *= aScaleZ;
+ _23 *= aScaleZ;
+ _33 *= aScaleZ;
+ _43 *= aScaleZ;
+
+ return *this;
+ }
+
+ void SkewXY(Float aSkew)
+ {
+ (*this)[1] += (*this)[0] * aSkew;
+ }
+
+ void SkewXZ(Float aSkew)
+ {
+ (*this)[2] += (*this)[0] * aSkew;
+ }
+
+ void SkewYZ(Float aSkew)
+ {
+ (*this)[2] += (*this)[1] * aSkew;
+ }
+
+ Matrix4x4Typed &ChangeBasis(const Point3D& aOrigin)
+ {
+ return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
+ }
+
+ Matrix4x4Typed &ChangeBasis(Float aX, Float aY, Float aZ)
+ {
+ // Translate to the origin before applying this matrix
+ PreTranslate(-aX, -aY, -aZ);
+
+ // Translate back into position after applying this matrix
+ PostTranslate(aX, aY, aZ);
+
+ return *this;
+ }
+
+ Matrix4x4Typed& Transpose() {
+ std::swap(_12, _21);
+ std::swap(_13, _31);
+ std::swap(_14, _41);
+
+ std::swap(_23, _32);
+ std::swap(_24, _42);
+
+ std::swap(_34, _43);
+
+ return *this;
+ }
+
+ bool operator==(const Matrix4x4Typed& o) const
+ {
+ // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
+ return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
+ _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
+ _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
+ _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44;
+ }
+
+ bool operator!=(const Matrix4x4Typed& o) const
+ {
+ return !((*this) == o);
+ }
+
+ template <typename NewTargetUnits>
+ Matrix4x4Typed<SourceUnits, NewTargetUnits> operator*(const Matrix4x4Typed<TargetUnits, NewTargetUnits> &aMatrix) const
+ {
+ Matrix4x4Typed<SourceUnits, NewTargetUnits> matrix;
+
+ matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41;
+ matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41;
+ matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41;
+ matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41;
+ matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42;
+ matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42;
+ matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + _34 * aMatrix._42;
+ matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + _44 * aMatrix._42;
+ matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + _14 * aMatrix._43;
+ matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + _24 * aMatrix._43;
+ matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + _34 * aMatrix._43;
+ matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + _44 * aMatrix._43;
+ matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + _14 * aMatrix._44;
+ matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + _24 * aMatrix._44;
+ matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + _34 * aMatrix._44;
+ matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + _44 * aMatrix._44;
+
+ return matrix;
+ }
+
+ Matrix4x4Typed& operator*=(const Matrix4x4Typed<TargetUnits, TargetUnits> &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ /* Returns true if the matrix is an identity matrix.
+ */
+ bool IsIdentity() const
+ {
+ return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
+ _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
+ _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
+ _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f;
+ }
+
+ bool IsSingular() const
+ {
+ return Determinant() == 0.0;
+ }
+
+ Float Determinant() const
+ {
+ return _14 * _23 * _32 * _41
+ - _13 * _24 * _32 * _41
+ - _14 * _22 * _33 * _41
+ + _12 * _24 * _33 * _41
+ + _13 * _22 * _34 * _41
+ - _12 * _23 * _34 * _41
+ - _14 * _23 * _31 * _42
+ + _13 * _24 * _31 * _42
+ + _14 * _21 * _33 * _42
+ - _11 * _24 * _33 * _42
+ - _13 * _21 * _34 * _42
+ + _11 * _23 * _34 * _42
+ + _14 * _22 * _31 * _43
+ - _12 * _24 * _31 * _43
+ - _14 * _21 * _32 * _43
+ + _11 * _24 * _32 * _43
+ + _12 * _21 * _34 * _43
+ - _11 * _22 * _34 * _43
+ - _13 * _22 * _31 * _44
+ + _12 * _23 * _31 * _44
+ + _13 * _21 * _32 * _44
+ - _11 * _23 * _32 * _44
+ - _12 * _21 * _33 * _44
+ + _11 * _22 * _33 * _44;
+ }
+
+ // Invert() is not unit-correct. Prefer Inverse() where possible.
+ bool Invert()
+ {
+ Float det = Determinant();
+ if (!det) {
+ return false;
+ }
+
+ Matrix4x4Typed<SourceUnits, TargetUnits> result;
+ result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 - _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44;
+ result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 + _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44;
+ result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 - _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44;
+ result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 + _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34;
+ result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 + _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44;
+ result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 - _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44;
+ result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 + _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44;
+ result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 - _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34;
+ result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 - _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44;
+ result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 + _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44;
+ result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44;
+ result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 + _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34;
+ result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 + _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43;
+ result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 - _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43;
+ result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 + _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43;
+ result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33;
+
+ result._11 /= det;
+ result._12 /= det;
+ result._13 /= det;
+ result._14 /= det;
+ result._21 /= det;
+ result._22 /= det;
+ result._23 /= det;
+ result._24 /= det;
+ result._31 /= det;
+ result._32 /= det;
+ result._33 /= det;
+ result._34 /= det;
+ result._41 /= det;
+ result._42 /= det;
+ result._43 /= det;
+ result._44 /= det;
+ *this = result;
+
+ return true;
+ }
+
+ Matrix4x4Typed<TargetUnits, SourceUnits> Inverse() const
+ {
+ typedef Matrix4x4Typed<TargetUnits, SourceUnits> InvertedMatrix;
+ InvertedMatrix clone = InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
+ DebugOnly<bool> inverted = clone.Invert();
+ MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+ return clone;
+ }
+
+ void Normalize()
+ {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ (*this)[i][j] /= (*this)[3][3];
+ }
+ }
+ }
+
+ bool FuzzyEqual(const Matrix4x4Typed& o) const
+ {
+ return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) &&
+ gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) &&
+ gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
+ gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
+ gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
+ gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
+ gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) &&
+ gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44);
+ }
+
+ bool FuzzyEqualsMultiplicative(const Matrix4x4Typed& o) const
+ {
+ return ::mozilla::FuzzyEqualsMultiplicative(_11, o._11) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_12, o._12) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_13, o._13) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_14, o._14) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_21, o._21) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_22, o._22) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_23, o._23) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_24, o._24) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_31, o._31) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_32, o._32) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_33, o._33) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_34, o._34) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_41, o._41) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_42, o._42) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_43, o._43) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_44, o._44);
+ }
+
+ bool IsBackfaceVisible() const
+ {
+ // Inverse()._33 < 0;
+ Float det = Determinant();
+ Float __33 = _12*_24*_41 - _14*_22*_41 +
+ _14*_21*_42 - _11*_24*_42 -
+ _12*_21*_44 + _11*_22*_44;
+ return (__33 * det) < 0;
+ }
+
+ Matrix4x4Typed &NudgeToIntegersFixedEpsilon()
+ {
+ NudgeToInteger(&_11);
+ NudgeToInteger(&_12);
+ NudgeToInteger(&_13);
+ NudgeToInteger(&_14);
+ NudgeToInteger(&_21);
+ NudgeToInteger(&_22);
+ NudgeToInteger(&_23);
+ NudgeToInteger(&_24);
+ NudgeToInteger(&_31);
+ NudgeToInteger(&_32);
+ NudgeToInteger(&_33);
+ NudgeToInteger(&_34);
+ static const float error = 1e-5f;
+ NudgeToInteger(&_41, error);
+ NudgeToInteger(&_42, error);
+ NudgeToInteger(&_43, error);
+ NudgeToInteger(&_44, error);
+ return *this;
+ }
+
+ Point4D TransposedVector(int aIndex) const
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return Point4D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex));
+ }
+
+ void SetTransposedVector(int aIndex, Point4D &aVector)
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ *((&_11)+aIndex) = aVector.x;
+ *((&_21)+aIndex) = aVector.y;
+ *((&_31)+aIndex) = aVector.z;
+ *((&_41)+aIndex) = aVector.w;
+ }
+
+ // Sets this matrix to a rotation matrix given by aQuat.
+ // This quaternion *MUST* be normalized!
+ // Implemented in Quaternion.cpp
+ void SetRotationFromQuaternion(const Quaternion& q)
+ {
+ const Float x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
+ const Float xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
+ const Float yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
+ const Float wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
+
+ _11 = 1.0f - (yy + zz);
+ _21 = xy + wz;
+ _31 = xz - wy;
+ _41 = 0.0f;
+
+ _12 = xy - wz;
+ _22 = 1.0f - (xx + zz);
+ _32 = yz + wx;
+ _42 = 0.0f;
+
+ _13 = xz + wy;
+ _23 = yz - wx;
+ _33 = 1.0f - (xx + yy);
+ _43 = 0.0f;
+
+ _14 = _42 = _43 = 0.0f;
+ _44 = 1.0f;
+ }
+
+ // Set all the members of the matrix to NaN
+ void SetNAN()
+ {
+ _11 = UnspecifiedNaN<Float>();
+ _21 = UnspecifiedNaN<Float>();
+ _31 = UnspecifiedNaN<Float>();
+ _41 = UnspecifiedNaN<Float>();
+ _12 = UnspecifiedNaN<Float>();
+ _22 = UnspecifiedNaN<Float>();
+ _32 = UnspecifiedNaN<Float>();
+ _42 = UnspecifiedNaN<Float>();
+ _13 = UnspecifiedNaN<Float>();
+ _23 = UnspecifiedNaN<Float>();
+ _33 = UnspecifiedNaN<Float>();
+ _43 = UnspecifiedNaN<Float>();
+ _14 = UnspecifiedNaN<Float>();
+ _24 = UnspecifiedNaN<Float>();
+ _34 = UnspecifiedNaN<Float>();
+ _44 = UnspecifiedNaN<Float>();
+ }
+
+ void SkewXY(double aXSkew, double aYSkew)
+ {
+ // XXX Is double precision really necessary here
+ float tanX = SafeTangent(aXSkew);
+ float tanY = SafeTangent(aYSkew);
+ float temp;
+
+ temp = _11;
+ _11 += tanY * _21;
+ _21 += tanX * temp;
+
+ temp = _12;
+ _12 += tanY * _22;
+ _22 += tanX * temp;
+
+ temp = _13;
+ _13 += tanY * _23;
+ _23 += tanX * temp;
+
+ temp = _14;
+ _14 += tanY * _24;
+ _24 += tanX * temp;
+ }
+
+ void RotateX(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _21;
+ _21 = cosTheta * _21 + sinTheta * _31;
+ _31 = -sinTheta * temp + cosTheta * _31;
+
+ temp = _22;
+ _22 = cosTheta * _22 + sinTheta * _32;
+ _32 = -sinTheta * temp + cosTheta * _32;
+
+ temp = _23;
+ _23 = cosTheta * _23 + sinTheta * _33;
+ _33 = -sinTheta * temp + cosTheta * _33;
+
+ temp = _24;
+ _24 = cosTheta * _24 + sinTheta * _34;
+ _34 = -sinTheta * temp + cosTheta * _34;
+ }
+
+ void RotateY(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _11;
+ _11 = cosTheta * _11 + -sinTheta * _31;
+ _31 = sinTheta * temp + cosTheta * _31;
+
+ temp = _12;
+ _12 = cosTheta * _12 + -sinTheta * _32;
+ _32 = sinTheta * temp + cosTheta * _32;
+
+ temp = _13;
+ _13 = cosTheta * _13 + -sinTheta * _33;
+ _33 = sinTheta * temp + cosTheta * _33;
+
+ temp = _14;
+ _14 = cosTheta * _14 + -sinTheta * _34;
+ _34 = sinTheta * temp + cosTheta * _34;
+ }
+
+ void RotateZ(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _11;
+ _11 = cosTheta * _11 + sinTheta * _21;
+ _21 = -sinTheta * temp + cosTheta * _21;
+
+ temp = _12;
+ _12 = cosTheta * _12 + sinTheta * _22;
+ _22 = -sinTheta * temp + cosTheta * _22;
+
+ temp = _13;
+ _13 = cosTheta * _13 + sinTheta * _23;
+ _23 = -sinTheta * temp + cosTheta * _23;
+
+ temp = _14;
+ _14 = cosTheta * _14 + sinTheta * _24;
+ _24 = -sinTheta * temp + cosTheta * _24;
+ }
+
+ // Sets this matrix to a rotation matrix about a
+ // vector [x,y,z] by angle theta. The vector is normalized
+ // to a unit vector.
+ // https://www.w3.org/TR/css3-3d-transforms/#Rotate3dDefined
+ void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta)
+ {
+ Point3D vector(aX, aY, aZ);
+ if (!vector.Length()) {
+ return;
+ }
+ vector.Normalize();
+
+ double x = vector.x;
+ double y = vector.y;
+ double z = vector.z;
+
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ // sin(aTheta / 2) * cos(aTheta / 2)
+ double sc = sinTheta / 2;
+ // pow(sin(aTheta / 2), 2)
+ double sq = (1 - cosTheta) / 2;
+
+ _11 = 1 - 2 * (y * y + z * z) * sq;
+ _12 = 2 * (x * y * sq + z * sc);
+ _13 = 2 * (x * z * sq - y * sc);
+ _14 = 0.0f;
+ _21 = 2 * (x * y * sq - z * sc);
+ _22 = 1 - 2 * (x * x + z * z) * sq;
+ _23 = 2 * (y * z * sq + x * sc);
+ _24 = 0.0f;
+ _31 = 2 * (x * z * sq + y * sc);
+ _32 = 2 * (y * z * sq - x * sc);
+ _33 = 1 - 2 * (x * x + y * y) * sq;
+ _34 = 0.0f;
+ _41 = 0.0f;
+ _42 = 0.0f;
+ _43 = 0.0f;
+ _44 = 1.0f;
+ }
+
+ void Perspective(float aDepth)
+ {
+ MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
+ _31 += -1.0/aDepth * _41;
+ _32 += -1.0/aDepth * _42;
+ _33 += -1.0/aDepth * _43;
+ _34 += -1.0/aDepth * _44;
+ }
+
+ Point3D GetNormalVector() const
+ {
+ // Define a plane in transformed space as the transformations
+ // of 3 points on the z=0 screen plane.
+ Point3D a = TransformPoint(Point3D(0, 0, 0));
+ Point3D b = TransformPoint(Point3D(0, 1, 0));
+ Point3D c = TransformPoint(Point3D(1, 0, 0));
+
+ // Convert to two vectors on the surface of the plane.
+ Point3D ab = b - a;
+ Point3D ac = c - a;
+
+ return ac.CrossProduct(ab);
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a straight translation.
+ */
+ bool HasNonTranslation() const {
+ return !gfx::FuzzyEqual(_11, 1.0) || !gfx::FuzzyEqual(_22, 1.0) ||
+ !gfx::FuzzyEqual(_12, 0.0) || !gfx::FuzzyEqual(_21, 0.0) ||
+ !gfx::FuzzyEqual(_13, 0.0) || !gfx::FuzzyEqual(_23, 0.0) ||
+ !gfx::FuzzyEqual(_31, 0.0) || !gfx::FuzzyEqual(_32, 0.0) ||
+ !gfx::FuzzyEqual(_33, 1.0);
+ }
+
+ /**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ bool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !gfx::FuzzyEqual(_41, floor(_41 + 0.5)) ||
+ !gfx::FuzzyEqual(_42, floor(_42 + 0.5)) ||
+ !gfx::FuzzyEqual(_43, floor(_43 + 0.5));
+ }
+
+ /**
+ * Return true if the matrix is with perspective (w).
+ */
+ bool HasPerspectiveComponent() const {
+ return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1;
+ }
+
+ /**
+ * Convert between typed and untyped matrices.
+ */
+ Matrix4x4 ToUnknownMatrix() const {
+ return Matrix4x4{_11, _12, _13, _14,
+ _21, _22, _23, _24,
+ _31, _32, _33, _34,
+ _41, _42, _43, _44};
+ }
+ static Matrix4x4Typed FromUnknownMatrix(const Matrix4x4& aUnknown) {
+ return Matrix4x4Typed{aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14,
+ aUnknown._21, aUnknown._22, aUnknown._23, aUnknown._24,
+ aUnknown._31, aUnknown._32, aUnknown._33, aUnknown._34,
+ aUnknown._41, aUnknown._42, aUnknown._43, aUnknown._44};
+ }
+};
+
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+
+class Matrix5x4
+{
+public:
+ Matrix5x4()
+ : _11(1.0f), _12(0), _13(0), _14(0)
+ , _21(0), _22(1.0f), _23(0), _24(0)
+ , _31(0), _32(0), _33(1.0f), _34(0)
+ , _41(0), _42(0), _43(0), _44(1.0f)
+ , _51(0), _52(0), _53(0), _54(0)
+ {}
+ Matrix5x4(Float a11, Float a12, Float a13, Float a14,
+ Float a21, Float a22, Float a23, Float a24,
+ Float a31, Float a32, Float a33, Float a34,
+ Float a41, Float a42, Float a43, Float a44,
+ Float a51, Float a52, Float a53, Float a54)
+ : _11(a11), _12(a12), _13(a13), _14(a14)
+ , _21(a21), _22(a22), _23(a23), _24(a24)
+ , _31(a31), _32(a32), _33(a33), _34(a34)
+ , _41(a41), _42(a42), _43(a43), _44(a44)
+ , _51(a51), _52(a52), _53(a53), _54(a54)
+ {}
+
+ bool operator==(const Matrix5x4 &o) const
+ {
+ return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
+ _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
+ _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
+ _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44 &&
+ _51 == o._51 && _52 == o._52 && _53 == o._53 && _54 == o._54;
+ }
+
+ bool operator!=(const Matrix5x4 &aMatrix) const
+ {
+ return !(*this == aMatrix);
+ }
+
+ Matrix5x4 operator*(const Matrix5x4 &aMatrix) const
+ {
+ Matrix5x4 resultMatrix;
+
+ resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21 + this->_13 * aMatrix._31 + this->_14 * aMatrix._41;
+ resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22 + this->_13 * aMatrix._32 + this->_14 * aMatrix._42;
+ resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23 + this->_13 * aMatrix._33 + this->_14 * aMatrix._43;
+ resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24 + this->_13 * aMatrix._34 + this->_14 * aMatrix._44;
+ resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21 + this->_23 * aMatrix._31 + this->_24 * aMatrix._41;
+ resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22 + this->_23 * aMatrix._32 + this->_24 * aMatrix._42;
+ resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23 + this->_23 * aMatrix._33 + this->_24 * aMatrix._43;
+ resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24 + this->_23 * aMatrix._34 + this->_24 * aMatrix._44;
+ resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + this->_33 * aMatrix._31 + this->_34 * aMatrix._41;
+ resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + this->_33 * aMatrix._32 + this->_34 * aMatrix._42;
+ resultMatrix._33 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + this->_33 * aMatrix._33 + this->_34 * aMatrix._43;
+ resultMatrix._34 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + this->_33 * aMatrix._34 + this->_34 * aMatrix._44;
+ resultMatrix._41 = this->_41 * aMatrix._11 + this->_42 * aMatrix._21 + this->_43 * aMatrix._31 + this->_44 * aMatrix._41;
+ resultMatrix._42 = this->_41 * aMatrix._12 + this->_42 * aMatrix._22 + this->_43 * aMatrix._32 + this->_44 * aMatrix._42;
+ resultMatrix._43 = this->_41 * aMatrix._13 + this->_42 * aMatrix._23 + this->_43 * aMatrix._33 + this->_44 * aMatrix._43;
+ resultMatrix._44 = this->_41 * aMatrix._14 + this->_42 * aMatrix._24 + this->_43 * aMatrix._34 + this->_44 * aMatrix._44;
+ resultMatrix._51 = this->_51 * aMatrix._11 + this->_52 * aMatrix._21 + this->_53 * aMatrix._31 + this->_54 * aMatrix._41 + aMatrix._51;
+ resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 + this->_53 * aMatrix._32 + this->_54 * aMatrix._42 + aMatrix._52;
+ resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 + this->_53 * aMatrix._33 + this->_54 * aMatrix._43 + aMatrix._53;
+ resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 + this->_53 * aMatrix._34 + this->_54 * aMatrix._44 + aMatrix._54;
+
+ return resultMatrix;
+ }
+
+ Matrix5x4& operator*=(const Matrix5x4 &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ union {
+ struct {
+ Float _11, _12, _13, _14;
+ Float _21, _22, _23, _24;
+ Float _31, _32, _33, _34;
+ Float _41, _42, _43, _44;
+ Float _51, _52, _53, _54;
+ };
+ Float components[20];
+ };
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_MATRIX_H_ */
diff --git a/system/graphics/2d/MatrixFwd.h b/system/graphics/2d/MatrixFwd.h
new file mode 100644
index 000000000..ec62368b3
--- /dev/null
+++ b/system/graphics/2d/MatrixFwd.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef MOZILLA_GFX_MATRIX_FWD_H_
+#define MOZILLA_GFX_MATRIX_FWD_H_
+
+
+// Forward declare enough things to define the typedef |Matrix4x4|.
+
+namespace mozilla {
+namespace gfx {
+
+struct UnknownUnits;
+
+template<class SourceUnits, class TargetUnits>
+class Matrix4x4Typed;
+
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/NativeFontResourceDWrite.cpp b/system/graphics/2d/NativeFontResourceDWrite.cpp
new file mode 100644
index 000000000..53f2c9f79
--- /dev/null
+++ b/system/graphics/2d/NativeFontResourceDWrite.cpp
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NativeFontResourceDWrite.h"
+
+#include <unordered_map>
+
+#include "DrawTargetD2D1.h"
+#include "Logging.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace gfx {
+
+static Atomic<uint64_t> sNextFontFileKey;
+static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
+
+class DWriteFontFileLoader : public IDWriteFontFileLoader
+{
+public:
+ DWriteFontFileLoader()
+ {
+ }
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileLoader)) {
+ *ppObject = static_cast<IDWriteFontFileLoader*>(this);
+ return S_OK;
+ } else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ return 1;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ return 1;
+ }
+
+ // IDWriteFontFileLoader methods
+ /**
+ * Important! Note the key here has to be a uint64_t that will have been
+ * generated by incrementing sNextFontFileKey.
+ */
+ virtual HRESULT STDMETHODCALLTYPE
+ CreateStreamFromKey(void const* fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ OUT IDWriteFontFileStream** fontFileStream);
+
+ /**
+ * Gets the singleton loader instance. Note that when using this font
+ * loader, the key must be a uint64_t that has been generated by incrementing
+ * sNextFontFileKey.
+ * Also note that this is _not_ threadsafe.
+ */
+ static IDWriteFontFileLoader* Instance()
+ {
+ if (!mInstance) {
+ mInstance = new DWriteFontFileLoader();
+ DrawTargetD2D1::GetDWriteFactory()->
+ RegisterFontFileLoader(mInstance);
+ }
+ return mInstance;
+ }
+
+private:
+ static IDWriteFontFileLoader* mInstance;
+};
+
+class DWriteFontFileStream : public IDWriteFontFileStream
+{
+public:
+ /**
+ * Used by the FontFileLoader to create a new font stream,
+ * this font stream is created from data in memory. The memory
+ * passed may be released after object creation, it will be
+ * copied internally.
+ *
+ * @param aData Font data
+ */
+ DWriteFontFileStream(uint8_t *aData, uint32_t aSize, uint64_t aFontFileKey);
+ ~DWriteFontFileStream();
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileStream)) {
+ *ppObject = static_cast<IDWriteFontFileStream*>(this);
+ return S_OK;
+ } else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ ++mRefCnt;
+ return mRefCnt;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ --mRefCnt;
+ if (mRefCnt == 0) {
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ // IDWriteFontFileStream methods
+ virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ OUT void** fragmentContext);
+
+ virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+
+ virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
+
+ virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
+
+private:
+ std::vector<uint8_t> mData;
+ uint32_t mRefCnt;
+ uint64_t mFontFileKey;
+};
+
+IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ IDWriteFontFileStream **fontFileStream)
+{
+ if (!fontFileReferenceKey || !fontFileStream) {
+ return E_POINTER;
+ }
+
+ uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
+ auto found = sFontFileStreams.find(fontFileKey);
+ if (found == sFontFileStreams.end()) {
+ *fontFileStream = nullptr;
+ return E_FAIL;
+ }
+
+ found->second->AddRef();
+ *fontFileStream = found->second;
+ return S_OK;
+}
+
+DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize,
+ uint64_t aFontFileKey)
+ : mRefCnt(0)
+ , mFontFileKey(aFontFileKey)
+{
+ mData.resize(aSize);
+ memcpy(&mData.front(), aData, aSize);
+}
+
+DWriteFontFileStream::~DWriteFontFileStream()
+{
+ sFontFileStreams.erase(mFontFileKey);
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::GetFileSize(UINT64 *fileSize)
+{
+ *fileSize = mData.size();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ void **fragmentContext)
+{
+ // We are required to do bounds checking.
+ if (fileOffset + fragmentSize > mData.size()) {
+ return E_FAIL;
+ }
+
+ // truncate the 64 bit fileOffset to size_t sized index into mData
+ size_t index = static_cast<size_t>(fileOffset);
+
+ // We should be alive for the duration of this.
+ *fragmentStart = &mData[index];
+ *fragmentContext = nullptr;
+ return S_OK;
+}
+
+void STDMETHODCALLTYPE
+DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
+{
+}
+
+/* static */
+already_AddRefed<NativeFontResourceDWrite>
+NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength,
+ bool aNeedsCairo)
+{
+ IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
+ if (!factory) {
+ gfxWarning() << "Failed to get DWrite Factory.";
+ return nullptr;
+ }
+
+ uint64_t fontFileKey = sNextFontFileKey++;
+ RefPtr<IDWriteFontFileStream> ffsRef =
+ new DWriteFontFileStream(aFontData, aDataLength, fontFileKey);
+ sFontFileStreams[fontFileKey] = ffsRef;
+
+ RefPtr<IDWriteFontFile> fontFile;
+ HRESULT hr =
+ factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey),
+ DWriteFontFileLoader::Instance(),
+ getter_AddRefs(fontFile));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to load font file from data!";
+ return nullptr;
+ }
+
+ BOOL isSupported;
+ DWRITE_FONT_FILE_TYPE fileType;
+ DWRITE_FONT_FACE_TYPE faceType;
+ UINT32 numberOfFaces;
+ hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces);
+ if (FAILED(hr) || !isSupported) {
+ gfxWarning() << "Font file is not supported.";
+ return nullptr;
+ }
+
+ RefPtr<NativeFontResourceDWrite> fontResource =
+ new NativeFontResourceDWrite(factory, fontFile.forget(), faceType,
+ numberOfFaces, aNeedsCairo);
+ return fontResource.forget();
+}
+
+already_AddRefed<ScaledFont>
+NativeFontResourceDWrite::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
+{
+ if (aIndex >= mNumberOfFaces) {
+ gfxWarning() << "Font face index is too high for font resource.";
+ return nullptr;
+ }
+
+ IDWriteFontFile *fontFile = mFontFile;
+ RefPtr<IDWriteFontFace> fontFace;
+ if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
+ DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(fontFace)))) {
+ gfxWarning() << "Failed to create font face from font file data.";
+ return nullptr;
+ }
+
+ RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize);
+ if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
+ gfxWarning() << "Unable to create cairo scaled font DWrite font.";
+ return nullptr;
+ }
+
+ return scaledFont.forget();
+}
+
+} // gfx
+} // mozilla
diff --git a/system/graphics/2d/NativeFontResourceDWrite.h b/system/graphics/2d/NativeFontResourceDWrite.h
new file mode 100644
index 000000000..ff1aafa1e
--- /dev/null
+++ b/system/graphics/2d/NativeFontResourceDWrite.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_NativeFontResourceDWrite_h
+#define mozilla_gfx_NativeFontResourceDWrite_h
+
+#include <dwrite.h>
+
+#include "2D.h"
+#include "mozilla/AlreadyAddRefed.h"
+
+namespace mozilla {
+namespace gfx {
+
+class NativeFontResourceDWrite final : public NativeFontResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceDWrite)
+ /**
+ * Creates a NativeFontResourceDWrite if data is valid. Note aFontData will be
+ * copied if required and so can be released after calling.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length of data.
+ * @param aNeedsCairo whether the ScaledFont created needs a cairo scaled font
+ * @return Referenced NativeFontResourceDWrite or nullptr if invalid.
+ */
+ static already_AddRefed<NativeFontResourceDWrite>
+ Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
+
+ already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
+
+private:
+ NativeFontResourceDWrite(IDWriteFactory *aFactory,
+ already_AddRefed<IDWriteFontFile> aFontFile,
+ DWRITE_FONT_FACE_TYPE aFaceType,
+ uint32_t aNumberOfFaces, bool aNeedsCairo)
+ : mFactory(aFactory), mFontFile(aFontFile), mFaceType(aFaceType)
+ , mNumberOfFaces(aNumberOfFaces), mNeedsCairo(aNeedsCairo)
+ {}
+
+ IDWriteFactory *mFactory;
+ RefPtr<IDWriteFontFile> mFontFile;
+ DWRITE_FONT_FACE_TYPE mFaceType;
+ uint32_t mNumberOfFaces;
+ bool mNeedsCairo;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_NativeFontResourceDWrite_h
diff --git a/system/graphics/2d/NativeFontResourceGDI.cpp b/system/graphics/2d/NativeFontResourceGDI.cpp
new file mode 100644
index 000000000..8a1e42dd7
--- /dev/null
+++ b/system/graphics/2d/NativeFontResourceGDI.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NativeFontResourceGDI.h"
+
+#include "Logging.h"
+#include "mozilla/RefPtr.h"
+#include "ScaledFontWin.h"
+
+namespace mozilla {
+namespace gfx {
+
+/* static */
+already_AddRefed<NativeFontResourceGDI>
+NativeFontResourceGDI::Create(uint8_t *aFontData, uint32_t aDataLength,
+ bool aNeedsCairo)
+{
+ DWORD numberOfFontsAdded;
+ HANDLE fontResourceHandle = ::AddFontMemResourceEx(aFontData, aDataLength,
+ 0, &numberOfFontsAdded);
+ if (!fontResourceHandle) {
+ gfxWarning() << "Failed to add memory font resource.";
+ return nullptr;
+ }
+
+ RefPtr<NativeFontResourceGDI> fontResouce =
+ new NativeFontResourceGDI(fontResourceHandle, aNeedsCairo);
+
+ return fontResouce.forget();
+}
+
+NativeFontResourceGDI::~NativeFontResourceGDI()
+{
+ ::RemoveFontMemResourceEx(mFontResourceHandle);
+}
+
+already_AddRefed<ScaledFont>
+NativeFontResourceGDI::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
+{
+ if (aInstanceDataLength < sizeof(LOGFONT)) {
+ gfxWarning() << "GDI scaled font instance data is truncated.";
+ return nullptr;
+ }
+
+ const LOGFONT* logFont = reinterpret_cast<const LOGFONT*>(aInstanceData);
+
+ // Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
+ // are safe to pass this reference.
+ RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
+ if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
+ gfxWarning() << "Unable to create cairo scaled font GDI font.";
+ return nullptr;
+ }
+
+ return scaledFont.forget();
+}
+
+} // gfx
+} // mozilla
diff --git a/system/graphics/2d/NativeFontResourceGDI.h b/system/graphics/2d/NativeFontResourceGDI.h
new file mode 100644
index 000000000..48932a2af
--- /dev/null
+++ b/system/graphics/2d/NativeFontResourceGDI.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_NativeFontResourceGDI_h
+#define mozilla_gfx_NativeFontResourceGDI_h
+
+#include <windows.h>
+
+#include "2D.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+namespace gfx {
+
+class NativeFontResourceGDI final : public NativeFontResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceGDI)
+ /**
+ * Creates a NativeFontResourceGDI if data is valid. Note aFontData will be
+ * copied if required and so can be released after calling.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length of data.
+ * @param aNeedsCairo whether the ScaledFont created need a cairo scaled font
+ * @return Referenced NativeFontResourceGDI or nullptr if invalid.
+ */
+ static already_AddRefed<NativeFontResourceGDI>
+ Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
+
+ ~NativeFontResourceGDI();
+
+ already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
+
+private:
+ NativeFontResourceGDI(HANDLE aFontResourceHandle,
+ bool aNeedsCairo)
+ : mFontResourceHandle(aFontResourceHandle)
+ , mNeedsCairo(aNeedsCairo)
+ {}
+
+ HANDLE mFontResourceHandle;
+ bool mNeedsCairo;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_NativeFontResourceGDI_h
diff --git a/system/graphics/2d/NumericTools.h b/system/graphics/2d/NumericTools.h
new file mode 100644
index 000000000..03aa7a8e2
--- /dev/null
+++ b/system/graphics/2d/NumericTools.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_NUMERICTOOLS_H_
+#define MOZILLA_GFX_NUMERICTOOLS_H_
+
+namespace mozilla {
+
+// XXX - Move these into mfbt/MathAlgorithms.h?
+
+// Returns the largest multiple of aMultiplied that's <= x.
+// Same as int32_t(floor(double(x) / aMultiplier)) * aMultiplier,
+// but faster.
+inline int32_t
+RoundDownToMultiple(int32_t x, int32_t aMultiplier)
+{
+ // We don't use float division + floor because that's hard for the compiler
+ // to optimize.
+ int mod = x % aMultiplier;
+ if (x > 0) {
+ return x - mod;
+ }
+ return mod ? x - aMultiplier - mod : x;
+}
+
+// Returns the smallest multiple of aMultiplied that's >= x.
+// Same as int32_t(ceil(double(x) / aMultiplier)) * aMultiplier,
+// but faster.
+inline int32_t
+RoundUpToMultiple(int32_t x, int32_t aMultiplier)
+{
+ int mod = x % aMultiplier;
+ if (x > 0) {
+ return mod ? x + aMultiplier - mod : x;
+ }
+ return x - mod;
+}
+
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_NUMERICTOOLS_H_ */
diff --git a/system/graphics/2d/Path.cpp b/system/graphics/2d/Path.cpp
new file mode 100644
index 000000000..f863e12ec
--- /dev/null
+++ b/system/graphics/2d/Path.cpp
@@ -0,0 +1,550 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include "PathAnalysis.h"
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+static double CubicRoot(double aValue) {
+ if (aValue < 0.0) {
+ return -CubicRoot(-aValue);
+ }
+ else {
+ return pow(aValue, 1.0 / 3.0);
+ }
+}
+
+struct PointD : public BasePoint<double, PointD> {
+ typedef BasePoint<double, PointD> Super;
+
+ PointD() : Super() {}
+ PointD(double aX, double aY) : Super(aX, aY) {}
+ MOZ_IMPLICIT PointD(const Point& aPoint) : Super(aPoint.x, aPoint.y) {}
+
+ Point ToPoint() const {
+ return Point(static_cast<Float>(x), static_cast<Float>(y));
+ }
+};
+
+struct BezierControlPoints
+{
+ BezierControlPoints() {}
+ BezierControlPoints(const PointD &aCP1, const PointD &aCP2,
+ const PointD &aCP3, const PointD &aCP4)
+ : mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4)
+ {
+ }
+
+ PointD mCP1, mCP2, mCP3, mCP4;
+};
+
+void
+FlattenBezier(const BezierControlPoints &aPoints,
+ PathSink *aSink, double aTolerance);
+
+
+Path::Path()
+{
+}
+
+Path::~Path()
+{
+}
+
+Float
+Path::ComputeLength()
+{
+ EnsureFlattenedPath();
+ return mFlattenedPath->ComputeLength();
+}
+
+Point
+Path::ComputePointAtLength(Float aLength, Point* aTangent)
+{
+ EnsureFlattenedPath();
+ return mFlattenedPath->ComputePointAtLength(aLength, aTangent);
+}
+
+void
+Path::EnsureFlattenedPath()
+{
+ if (!mFlattenedPath) {
+ mFlattenedPath = new FlattenedPath();
+ StreamToSink(mFlattenedPath);
+ }
+}
+
+// This is the maximum deviation we allow (with an additional ~20% margin of
+// error) of the approximation from the actual Bezier curve.
+const Float kFlatteningTolerance = 0.0001f;
+
+void
+FlattenedPath::MoveTo(const Point &aPoint)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlatPathOp op;
+ op.mType = FlatPathOp::OP_MOVETO;
+ op.mPoint = aPoint;
+ mPathOps.push_back(op);
+
+ mLastMove = aPoint;
+}
+
+void
+FlattenedPath::LineTo(const Point &aPoint)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlatPathOp op;
+ op.mType = FlatPathOp::OP_LINETO;
+ op.mPoint = aPoint;
+ mPathOps.push_back(op);
+}
+
+void
+FlattenedPath::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlattenBezier(BezierControlPoints(CurrentPoint(), aCP1, aCP2, aCP3), this, kFlatteningTolerance);
+}
+
+void
+FlattenedPath::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ // We need to elevate the degree of this quadratic B�zier to cubic, so we're
+ // going to add an intermediate control point, and recompute control point 1.
+ // The first and last control points remain the same.
+ // This formula can be found on http://fontforge.sourceforge.net/bezier.html
+ Point CP0 = CurrentPoint();
+ Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
+ Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
+ Point CP3 = aCP2;
+
+ BezierTo(CP1, CP2, CP3);
+}
+
+void
+FlattenedPath::Close()
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ LineTo(mLastMove);
+}
+
+void
+FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise)
+{
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Float
+FlattenedPath::ComputeLength()
+{
+ if (!mCalculatedLength) {
+ Point currentPoint;
+
+ for (uint32_t i = 0; i < mPathOps.size(); i++) {
+ if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
+ currentPoint = mPathOps[i].mPoint;
+ } else {
+ mCachedLength += Distance(currentPoint, mPathOps[i].mPoint);
+ currentPoint = mPathOps[i].mPoint;
+ }
+ }
+
+ mCalculatedLength = true;
+ }
+
+ return mCachedLength;
+}
+
+Point
+FlattenedPath::ComputePointAtLength(Float aLength, Point *aTangent)
+{
+ // We track the last point that -wasn't- in the same place as the current
+ // point so if we pass the edge of the path with a bunch of zero length
+ // paths we still get the correct tangent vector.
+ Point lastPointSinceMove;
+ Point currentPoint;
+ for (uint32_t i = 0; i < mPathOps.size(); i++) {
+ if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
+ if (Distance(currentPoint, mPathOps[i].mPoint)) {
+ lastPointSinceMove = currentPoint;
+ }
+ currentPoint = mPathOps[i].mPoint;
+ } else {
+ Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
+
+ if (segmentLength) {
+ lastPointSinceMove = currentPoint;
+ if (segmentLength > aLength) {
+ Point currentVector = mPathOps[i].mPoint - currentPoint;
+ Point tangent = currentVector / segmentLength;
+ if (aTangent) {
+ *aTangent = tangent;
+ }
+ return currentPoint + tangent * aLength;
+ }
+ }
+
+ aLength -= segmentLength;
+ currentPoint = mPathOps[i].mPoint;
+ }
+ }
+
+ Point currentVector = currentPoint - lastPointSinceMove;
+ if (aTangent) {
+ if (hypotf(currentVector.x, currentVector.y)) {
+ *aTangent = currentVector / hypotf(currentVector.x, currentVector.y);
+ } else {
+ *aTangent = Point();
+ }
+ }
+ return currentPoint;
+}
+
+// This function explicitly permits aControlPoints to refer to the same object
+// as either of the other arguments.
+static void
+SplitBezier(const BezierControlPoints &aControlPoints,
+ BezierControlPoints *aFirstSegmentControlPoints,
+ BezierControlPoints *aSecondSegmentControlPoints,
+ double t)
+{
+ MOZ_ASSERT(aSecondSegmentControlPoints);
+
+ *aSecondSegmentControlPoints = aControlPoints;
+
+ PointD cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t;
+ PointD cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t;
+ PointD cp1aa = cp1a + (cp2a - cp1a) * t;
+ PointD cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t;
+ PointD cp2aa = cp2a + (cp3a - cp2a) * t;
+ PointD cp1aaa = cp1aa + (cp2aa - cp1aa) * t;
+ aSecondSegmentControlPoints->mCP4 = aControlPoints.mCP4;
+
+ if(aFirstSegmentControlPoints) {
+ aFirstSegmentControlPoints->mCP1 = aControlPoints.mCP1;
+ aFirstSegmentControlPoints->mCP2 = cp1a;
+ aFirstSegmentControlPoints->mCP3 = cp1aa;
+ aFirstSegmentControlPoints->mCP4 = cp1aaa;
+ }
+ aSecondSegmentControlPoints->mCP1 = cp1aaa;
+ aSecondSegmentControlPoints->mCP2 = cp2aa;
+ aSecondSegmentControlPoints->mCP3 = cp3a;
+}
+
+static void
+FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints,
+ PathSink *aSink,
+ double aTolerance)
+{
+ /* The algorithm implemented here is based on:
+ * http://cis.usouthal.edu/~hain/general/Publications/Bezier/Bezier%20Offset%20Curves.pdf
+ *
+ * The basic premise is that for a small t the third order term in the
+ * equation of a cubic bezier curve is insignificantly small. This can
+ * then be approximated by a quadratic equation for which the maximum
+ * difference from a linear approximation can be much more easily determined.
+ */
+ BezierControlPoints currentCP = aControlPoints;
+
+ double t = 0;
+ while (t < 1.0) {
+ PointD cp21 = currentCP.mCP2 - currentCP.mCP1;
+ PointD cp31 = currentCP.mCP3 - currentCP.mCP1;
+
+ /* To remove divisions and check for divide-by-zero, this is optimized from:
+ * Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
+ * t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
+ */
+ double cp21x31 = cp31.x * cp21.y - cp31.y * cp21.x;
+ double h = hypot(cp21.x, cp21.y);
+ if (cp21x31 * h == 0) {
+ break;
+ }
+
+ double s3inv = h / cp21x31;
+ t = 2 * sqrt(aTolerance * std::abs(s3inv) / 3.);
+ if (t >= 1.0) {
+ break;
+ }
+
+ SplitBezier(currentCP, nullptr, &currentCP, t);
+
+ aSink->LineTo(currentCP.mCP1.ToPoint());
+ }
+
+ aSink->LineTo(currentCP.mCP4.ToPoint());
+}
+
+static inline void
+FindInflectionApproximationRange(BezierControlPoints aControlPoints,
+ double *aMin, double *aMax, double aT,
+ double aTolerance)
+{
+ SplitBezier(aControlPoints, nullptr, &aControlPoints, aT);
+
+ PointD cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
+ PointD cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
+
+ if (cp21.x == 0. && cp21.y == 0.) {
+ // In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y.
+
+ // Use the absolute value so that Min and Max will correspond with the
+ // minimum and maximum of the range.
+ *aMin = aT - CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
+ *aMax = aT + CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
+ return;
+ }
+
+ double s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypot(cp21.x, cp21.y);
+
+ if (s3 == 0) {
+ // This means within the precision we have it can be approximated
+ // infinitely by a linear segment. Deal with this by specifying the
+ // approximation range as extending beyond the entire curve.
+ *aMin = -1.0;
+ *aMax = 2.0;
+ return;
+ }
+
+ double tf = CubicRoot(std::abs(aTolerance / s3));
+
+ *aMin = aT - tf * (1 - aT);
+ *aMax = aT + tf * (1 - aT);
+}
+
+/* Find the inflection points of a bezier curve. Will return false if the
+ * curve is degenerate in such a way that it is best approximated by a straight
+ * line.
+ *
+ * The below algorithm was written by Jeff Muizelaar <jmuizelaar@mozilla.com>, explanation follows:
+ *
+ * The lower inflection point is returned in aT1, the higher one in aT2. In the
+ * case of a single inflection point this will be in aT1.
+ *
+ * The method is inspired by the algorithm in "analysis of in?ection points for planar cubic bezier curve"
+ *
+ * Here are some differences between this algorithm and versions discussed elsewhere in the literature:
+ *
+ * zhang et. al compute a0, d0 and e0 incrementally using the follow formula:
+ *
+ * Point a0 = CP2 - CP1
+ * Point a1 = CP3 - CP2
+ * Point a2 = CP4 - CP1
+ *
+ * Point d0 = a1 - a0
+ * Point d1 = a2 - a1
+
+ * Point e0 = d1 - d0
+ *
+ * this avoids any multiplications and may or may not be faster than the approach take below.
+ *
+ * "fast, precise flattening of cubic bezier path and ofset curves" by hain et. al
+ * Point a = CP1 + 3 * CP2 - 3 * CP3 + CP4
+ * Point b = 3 * CP1 - 6 * CP2 + 3 * CP3
+ * Point c = -3 * CP1 + 3 * CP2
+ * Point d = CP1
+ * the a, b, c, d can be expressed in terms of a0, d0 and e0 defined above as:
+ * c = 3 * a0
+ * b = 3 * d0
+ * a = e0
+ *
+ *
+ * a = 3a = a.y * b.x - a.x * b.y
+ * b = 3b = a.y * c.x - a.x * c.y
+ * c = 9c = b.y * c.x - b.x * c.y
+ *
+ * The additional multiples of 3 cancel each other out as show below:
+ *
+ * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
+ * x = (-3 * b + sqrt(3 * b * 3 * b - 4 * a * 3 * 9 * c / 3)) / (2 * 3 * a)
+ * x = 3 * (-b + sqrt(b * b - 4 * a * c)) / (2 * 3 * a)
+ * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
+ *
+ * I haven't looked into whether the formulation of the quadratic formula in
+ * hain has any numerical advantages over the one used below.
+ */
+static inline void
+FindInflectionPoints(const BezierControlPoints &aControlPoints,
+ double *aT1, double *aT2, uint32_t *aCount)
+{
+ // Find inflection points.
+ // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
+ // of this approach.
+ PointD A = aControlPoints.mCP2 - aControlPoints.mCP1;
+ PointD B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1;
+ PointD C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1;
+
+ double a = B.x * C.y - B.y * C.x;
+ double b = A.x * C.y - A.y * C.x;
+ double c = A.x * B.y - A.y * B.x;
+
+ if (a == 0) {
+ // Not a quadratic equation.
+ if (b == 0) {
+ // Instead of a linear acceleration change we have a constant
+ // acceleration change. This means the equation has no solution
+ // and there are no inflection points, unless the constant is 0.
+ // In that case the curve is a straight line, essentially that means
+ // the easiest way to deal with is is by saying there's an inflection
+ // point at t == 0. The inflection point approximation range found will
+ // automatically extend into infinity.
+ if (c == 0) {
+ *aCount = 1;
+ *aT1 = 0;
+ return;
+ }
+ *aCount = 0;
+ return;
+ }
+ *aT1 = -c / b;
+ *aCount = 1;
+ return;
+ } else {
+ double discriminant = b * b - 4 * a * c;
+
+ if (discriminant < 0) {
+ // No inflection points.
+ *aCount = 0;
+ } else if (discriminant == 0) {
+ *aCount = 1;
+ *aT1 = -b / (2 * a);
+ } else {
+ /* Use the following formula for computing the roots:
+ *
+ * q = -1/2 * (b + sign(b) * sqrt(b^2 - 4ac))
+ * t1 = q / a
+ * t2 = c / q
+ */
+ double q = sqrt(discriminant);
+ if (b < 0) {
+ q = b - q;
+ } else {
+ q = b + q;
+ }
+ q *= -1./2;
+
+ *aT1 = q / a;
+ *aT2 = c / q;
+ if (*aT1 > *aT2) {
+ std::swap(*aT1, *aT2);
+ }
+ *aCount = 2;
+ }
+ }
+
+ return;
+}
+
+void
+FlattenBezier(const BezierControlPoints &aControlPoints,
+ PathSink *aSink, double aTolerance)
+{
+ double t1;
+ double t2;
+ uint32_t count;
+
+ FindInflectionPoints(aControlPoints, &t1, &t2, &count);
+
+ // Check that at least one of the inflection points is inside [0..1]
+ if (count == 0 || ((t1 < 0.0 || t1 >= 1.0) && (count == 1 || (t2 < 0.0 || t2 >= 1.0))) ) {
+ FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
+ return;
+ }
+
+ double t1min = t1, t1max = t1, t2min = t2, t2max = t2;
+
+ BezierControlPoints remainingCP = aControlPoints;
+
+ // For both inflection points, calulate the range where they can be linearly
+ // approximated if they are positioned within [0,1]
+ if (count > 0 && t1 >= 0 && t1 < 1.0) {
+ FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
+ }
+ if (count > 1 && t2 >= 0 && t2 < 1.0) {
+ FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
+ }
+ BezierControlPoints nextCPs = aControlPoints;
+ BezierControlPoints prevCPs;
+
+ // Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
+ // segments.
+ if (count == 1 && t1min <= 0 && t1max >= 1.0) {
+ // The whole range can be approximated by a line segment.
+ aSink->LineTo(aControlPoints.mCP4.ToPoint());
+ return;
+ }
+
+ if (t1min > 0) {
+ // Flatten the Bezier up until the first inflection point's approximation
+ // point.
+ SplitBezier(aControlPoints, &prevCPs,
+ &remainingCP, t1min);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ }
+ if (t1max >= 0 && t1max < 1.0 && (count == 1 || t2min > t1max)) {
+ // The second inflection point's approximation range begins after the end
+ // of the first, approximate the first inflection point by a line and
+ // subsequently flatten up until the end or the next inflection point.
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+
+ aSink->LineTo(nextCPs.mCP1.ToPoint());
+
+ if (count == 1 || (count > 1 && t2min >= 1.0)) {
+ // No more inflection points to deal with, flatten the rest of the curve.
+ FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
+ }
+ } else if (count > 1 && t2min > 1.0) {
+ // We've already concluded t2min <= t1max, so if this is true the
+ // approximation range for the first inflection point runs past the
+ // end of the curve, draw a line to the end and we're done.
+ aSink->LineTo(aControlPoints.mCP4.ToPoint());
+ return;
+ }
+
+ if (count > 1 && t2min < 1.0 && t2max > 0) {
+ if (t2min > 0 && t2min < t1max) {
+ // In this case the t2 approximation range starts inside the t1
+ // approximation range.
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+ aSink->LineTo(nextCPs.mCP1.ToPoint());
+ } else if (t2min > 0 && t1max > 0) {
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+
+ // Find a control points describing the portion of the curve between t1max and t2min.
+ double t2mina = (t2min - t1max) / (1 - t1max);
+ SplitBezier(nextCPs, &prevCPs, &nextCPs, t2mina);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ } else if (t2min > 0) {
+ // We have nothing interesting before t2min, find that bit and flatten it.
+ SplitBezier(aControlPoints, &prevCPs, &nextCPs, t2min);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ }
+ if (t2max < 1.0) {
+ // Flatten the portion of the curve after t2max
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t2max);
+
+ // Draw a line to the start, this is the approximation between t2min and
+ // t2max.
+ aSink->LineTo(nextCPs.mCP1.ToPoint());
+ FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
+ } else {
+ // Our approximation range extends beyond the end of the curve.
+ aSink->LineTo(aControlPoints.mCP4.ToPoint());
+ return;
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathAnalysis.h b/system/graphics/2d/PathAnalysis.h
new file mode 100644
index 000000000..5a38ed161
--- /dev/null
+++ b/system/graphics/2d/PathAnalysis.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct FlatPathOp
+{
+ enum OpType {
+ OP_MOVETO,
+ OP_LINETO,
+ };
+
+ OpType mType;
+ Point mPoint;
+};
+
+class FlattenedPath : public PathSink
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath)
+ FlattenedPath() : mCachedLength(0)
+ , mCalculatedLength(false)
+ {
+ }
+
+ virtual void MoveTo(const Point &aPoint);
+ virtual void LineTo(const Point &aPoint);
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ virtual void Close();
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false);
+
+ virtual Point CurrentPoint() const { return mPathOps.empty() ? Point() : mPathOps[mPathOps.size() - 1].mPoint; }
+
+ Float ComputeLength();
+ Point ComputePointAtLength(Float aLength, Point *aTangent);
+
+private:
+ Float mCachedLength;
+ bool mCalculatedLength;
+ Point mLastMove;
+
+ std::vector<FlatPathOp> mPathOps;
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathCairo.cpp b/system/graphics/2d/PathCairo.cpp
new file mode 100644
index 000000000..45bc201cb
--- /dev/null
+++ b/system/graphics/2d/PathCairo.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PathCairo.h"
+#include <math.h>
+#include "DrawTargetCairo.h"
+#include "Logging.h"
+#include "PathHelpers.h"
+#include "HelpersCairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
+ : mFillRule(aFillRule)
+{
+}
+
+void
+PathBuilderCairo::MoveTo(const Point &aPoint)
+{
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_MOVE_TO;
+ data.header.length = 2;
+ mPathData.push_back(data);
+ data.point.x = aPoint.x;
+ data.point.y = aPoint.y;
+ mPathData.push_back(data);
+
+ mBeginPoint = mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderCairo::LineTo(const Point &aPoint)
+{
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_LINE_TO;
+ data.header.length = 2;
+ mPathData.push_back(data);
+ data.point.x = aPoint.x;
+ data.point.y = aPoint.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderCairo::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CURVE_TO;
+ data.header.length = 4;
+ mPathData.push_back(data);
+ data.point.x = aCP1.x;
+ data.point.y = aCP1.y;
+ mPathData.push_back(data);
+ data.point.x = aCP2.x;
+ data.point.y = aCP2.y;
+ mPathData.push_back(data);
+ data.point.x = aCP3.x;
+ data.point.y = aCP3.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aCP3;
+}
+
+void
+PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ // We need to elevate the degree of this quadratic Bézier to cubic, so we're
+ // going to add an intermediate control point, and recompute control point 1.
+ // The first and last control points remain the same.
+ // This formula can be found on http://fontforge.sourceforge.net/bezier.html
+ Point CP0 = CurrentPoint();
+ Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
+ Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
+ Point CP3 = aCP2;
+
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CURVE_TO;
+ data.header.length = 4;
+ mPathData.push_back(data);
+ data.point.x = CP1.x;
+ data.point.y = CP1.y;
+ mPathData.push_back(data);
+ data.point.x = CP2.x;
+ data.point.y = CP2.y;
+ mPathData.push_back(data);
+ data.point.x = CP3.x;
+ data.point.y = CP3.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aCP2;
+}
+
+void
+PathBuilderCairo::Close()
+{
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CLOSE_PATH;
+ data.header.length = 1;
+ mPathData.push_back(data);
+
+ mCurrentPoint = mBeginPoint;
+}
+
+void
+PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise)
+{
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Point
+PathBuilderCairo::CurrentPoint() const
+{
+ return mCurrentPoint;
+}
+
+already_AddRefed<Path>
+PathBuilderCairo::Finish()
+{
+ return MakeAndAddRef<PathCairo>(mFillRule, mPathData, mCurrentPoint);
+}
+
+PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
+ : mFillRule(aFillRule)
+ , mContainingContext(nullptr)
+ , mCurrentPoint(aCurrentPoint)
+{
+ mPathData.swap(aPathData);
+}
+
+PathCairo::PathCairo(cairo_t *aContext)
+ : mFillRule(FillRule::FILL_WINDING)
+ , mContainingContext(nullptr)
+{
+ cairo_path_t *path = cairo_copy_path(aContext);
+
+ // XXX - mCurrentPoint is not properly set here, the same is true for the
+ // D2D Path code, we never require current point when hitting this codepath
+ // but this should be fixed.
+ for (int i = 0; i < path->num_data; i++) {
+ mPathData.push_back(path->data[i]);
+ }
+
+ cairo_path_destroy(path);
+}
+
+PathCairo::~PathCairo()
+{
+ if (mContainingContext) {
+ cairo_destroy(mContainingContext);
+ }
+}
+
+already_AddRefed<PathBuilder>
+PathCairo::CopyToBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ builder->mPathData = mPathData;
+ builder->mCurrentPoint = mCurrentPoint;
+
+ return builder.forget();
+}
+
+already_AddRefed<PathBuilder>
+PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ AppendPathToBuilder(builder, &aTransform);
+ builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
+
+ return builder.forget();
+}
+
+bool
+PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformed = inverse.TransformPoint(aPoint);
+
+ EnsureContainingContext(aTransform);
+
+ return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
+}
+
+bool
+PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformed = inverse.TransformPoint(aPoint);
+
+ EnsureContainingContext(aTransform);
+
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+ return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
+}
+
+Rect
+PathCairo::GetBounds(const Matrix &aTransform) const
+{
+ EnsureContainingContext(aTransform);
+
+ double x1, y1, x2, y2;
+
+ cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
+ Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
+ return aTransform.TransformBounds(bounds);
+}
+
+Rect
+PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ EnsureContainingContext(aTransform);
+
+ double x1, y1, x2, y2;
+
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+ cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
+ Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
+ return aTransform.TransformBounds(bounds);
+}
+
+void
+PathCairo::StreamToSink(PathSink *aSink) const
+{
+ for (size_t i = 0; i < mPathData.size(); i++) {
+ switch (mPathData[i].header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ i++;
+ aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y));
+ break;
+ case CAIRO_PATH_LINE_TO:
+ i++;
+ aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y));
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y),
+ Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y),
+ Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y));
+ i += 3;
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ aSink->Close();
+ break;
+ default:
+ // Corrupt path data!
+ MOZ_ASSERT(false);
+ }
+ }
+}
+
+void
+PathCairo::EnsureContainingContext(const Matrix &aTransform) const
+{
+ if (mContainingContext) {
+ if (mContainingTransform.ExactlyEquals(aTransform)) {
+ return;
+ }
+ } else {
+ mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
+ }
+
+ mContainingTransform = aTransform;
+
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mContainingTransform, mat);
+ cairo_set_matrix(mContainingContext, &mat);
+
+ SetPathOnContext(mContainingContext);
+}
+
+void
+PathCairo::SetPathOnContext(cairo_t *aContext) const
+{
+ // Needs the correct fill rule set.
+ cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+
+ cairo_new_path(aContext);
+
+ if (mPathData.size()) {
+ cairo_path_t path;
+ path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
+ path.num_data = mPathData.size();
+ path.status = CAIRO_STATUS_SUCCESS;
+ cairo_append_path(aContext, &path);
+ }
+}
+
+void
+PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
+{
+ if (aTransform) {
+ size_t i = 0;
+ while (i < mPathData.size()) {
+ uint32_t pointCount = mPathData[i].header.length - 1;
+ aBuilder->mPathData.push_back(mPathData[i]);
+ i++;
+ for (uint32_t c = 0; c < pointCount; c++) {
+ cairo_path_data_t data;
+ Point newPoint = aTransform->TransformPoint(Point(mPathData[i].point.x, mPathData[i].point.y));
+ data.point.x = newPoint.x;
+ data.point.y = newPoint.y;
+ aBuilder->mPathData.push_back(data);
+ i++;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < mPathData.size(); i++) {
+ aBuilder->mPathData.push_back(mPathData[i]);
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathCairo.h b/system/graphics/2d/PathCairo.h
new file mode 100644
index 000000000..5addfb220
--- /dev/null
+++ b/system/graphics/2d/PathCairo.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PATH_CAIRO_H_
+#define MOZILLA_GFX_PATH_CAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class PathCairo;
+
+class PathBuilderCairo : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCairo)
+ explicit PathBuilderCairo(FillRule aFillRule);
+
+ virtual void MoveTo(const Point &aPoint);
+ virtual void LineTo(const Point &aPoint);
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ virtual void Close();
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false);
+ virtual Point CurrentPoint() const;
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
+
+private: // data
+ friend class PathCairo;
+
+ FillRule mFillRule;
+ std::vector<cairo_path_data_t> mPathData;
+ // It's easiest to track this here, parsing the path data to find the current
+ // point is a little tricky.
+ Point mCurrentPoint;
+ Point mBeginPoint;
+};
+
+class PathCairo : public Path
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCairo)
+ PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
+ explicit PathCairo(cairo_t *aContext);
+ ~PathCairo();
+
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const;
+
+ virtual void StreamToSink(PathSink *aSink) const;
+
+ virtual FillRule GetFillRule() const { return mFillRule; }
+
+ void SetPathOnContext(cairo_t *aContext) const;
+
+ void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
+private:
+ void EnsureContainingContext(const Matrix &aTransform) const;
+
+ FillRule mFillRule;
+ std::vector<cairo_path_data_t> mPathData;
+ mutable cairo_t *mContainingContext;
+ mutable Matrix mContainingTransform;
+ Point mCurrentPoint;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATH_CAIRO_H_ */
diff --git a/system/graphics/2d/PathD2D.cpp b/system/graphics/2d/PathD2D.cpp
new file mode 100644
index 000000000..b10e456e1
--- /dev/null
+++ b/system/graphics/2d/PathD2D.cpp
@@ -0,0 +1,523 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PathD2D.h"
+#include "HelpersD2D.h"
+#include <math.h>
+#include "DrawTargetD2D1.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+// This class exists as a wrapper for ID2D1SimplifiedGeometry sink, it allows
+// a geometry to be duplicated into a geometry sink, while removing the final
+// figure end and thus allowing a figure that was implicitly closed to be
+// continued.
+class OpeningGeometrySink : public ID2D1SimplifiedGeometrySink
+{
+public:
+ OpeningGeometrySink(ID2D1SimplifiedGeometrySink *aSink)
+ : mSink(aSink)
+ , mNeedsFigureEnded(false)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+ {
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
+ *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // We ignore SetFillMode, the copier will decide.
+ STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
+ { EnsureFigureEnded(); return; }
+ STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
+ { EnsureFigureEnded(); return mSink->BeginFigure(aPoint, aBegin); }
+ STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
+ { EnsureFigureEnded(); return mSink->AddLines(aLines, aCount); }
+ STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
+ { EnsureFigureEnded(); return mSink->AddBeziers(aSegments, aCount); }
+ STDMETHOD(Close)()
+ { /* Should never be called! */ return S_OK; }
+ STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
+ { return mSink->SetSegmentFlags(aFlags); }
+
+ // This function is special - it's the reason this class exists.
+ // It needs to intercept the very last endfigure. So that a user can
+ // continue writing to this sink as if they never stopped.
+ STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
+ {
+ if (aEnd == D2D1_FIGURE_END_CLOSED) {
+ return mSink->EndFigure(aEnd);
+ } else {
+ mNeedsFigureEnded = true;
+ }
+ }
+private:
+ void EnsureFigureEnded()
+ {
+ if (mNeedsFigureEnded) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ mNeedsFigureEnded = false;
+ }
+ }
+
+ ID2D1SimplifiedGeometrySink *mSink;
+ bool mNeedsFigureEnded;
+};
+
+class MOZ_STACK_CLASS AutoRestoreFP
+{
+public:
+ AutoRestoreFP()
+ {
+ // save the current floating point control word
+ _controlfp_s(&savedFPSetting, 0, 0);
+ UINT unused;
+ // set the floating point control word to its default value
+ _controlfp_s(&unused, _CW_DEFAULT, MCW_PC);
+ }
+ ~AutoRestoreFP()
+ {
+ UINT unused;
+ // restore the saved floating point control word
+ _controlfp_s(&unused, savedFPSetting, MCW_PC);
+ }
+private:
+ UINT savedFPSetting;
+};
+
+// Note that overrides of ID2D1SimplifiedGeometrySink methods in this class may
+// get called from D2D with nonstandard floating point settings (see comments in
+// bug 1134549) - use AutoRestoreFP to reset the floating point control word to
+// what we expect
+class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink
+{
+public:
+ StreamingGeometrySink(PathSink *aSink)
+ : mSink(aSink)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+ {
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
+ *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // We ignore SetFillMode, this depends on the destination sink.
+ STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
+ { return; }
+ STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ mSink->MoveTo(ToPoint(aPoint));
+ }
+ STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); }
+ }
+ STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ for (UINT i = 0; i < aCount; i++) {
+ mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3));
+ }
+ }
+ STDMETHOD(Close)()
+ { /* Should never be called! */ return S_OK; }
+ STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
+ { /* Should never be called! */ }
+
+ STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ if (aEnd == D2D1_FIGURE_END_CLOSED) {
+ return mSink->Close();
+ }
+ }
+private:
+
+ PathSink *mSink;
+};
+
+PathBuilderD2D::~PathBuilderD2D()
+{
+}
+
+void
+PathBuilderD2D::MoveTo(const Point &aPoint)
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ mFigureActive = false;
+ }
+ EnsureActive(aPoint);
+ mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderD2D::LineTo(const Point &aPoint)
+{
+ EnsureActive(aPoint);
+ mSink->AddLine(D2DPoint(aPoint));
+
+ mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderD2D::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+ {
+ EnsureActive(aCP1);
+ mSink->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1),
+ D2DPoint(aCP2),
+ D2DPoint(aCP3)));
+
+ mCurrentPoint = aCP3;
+}
+
+void
+PathBuilderD2D::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ EnsureActive(aCP1);
+ mSink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1),
+ D2DPoint(aCP2)));
+
+ mCurrentPoint = aCP2;
+}
+
+void
+PathBuilderD2D::Close()
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+
+ mFigureActive = false;
+
+ EnsureActive(mBeginPoint);
+ }
+}
+
+void
+PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
+ Float aEndAngle, bool aAntiClockwise)
+{
+ MOZ_ASSERT(aRadius >= 0);
+
+ if (aAntiClockwise && aStartAngle < aEndAngle) {
+ // D2D does things a little differently, and draws the arc by specifying an
+ // beginning and an end point. This means the circle will be the wrong way
+ // around if the start angle is smaller than the end angle. It might seem
+ // tempting to invert aAntiClockwise but that would change the sweeping
+ // direction of the arc so instead we exchange start/begin.
+ Float oldStart = aStartAngle;
+ aStartAngle = aEndAngle;
+ aEndAngle = oldStart;
+ }
+
+ // XXX - Workaround for now, D2D does not appear to do the desired thing when
+ // the angle sweeps a complete circle.
+ bool fullCircle = false;
+ if (aEndAngle - aStartAngle >= 2 * M_PI) {
+ fullCircle = true;
+ aEndAngle = Float(aStartAngle + M_PI * 1.9999);
+ } else if (aStartAngle - aEndAngle >= 2 * M_PI) {
+ fullCircle = true;
+ aStartAngle = Float(aEndAngle + M_PI * 1.9999);
+ }
+
+ Point startPoint;
+ startPoint.x = aOrigin.x + aRadius * cos(aStartAngle);
+ startPoint.y = aOrigin.y + aRadius * sin(aStartAngle);
+
+ if (!mFigureActive) {
+ EnsureActive(startPoint);
+ } else {
+ mSink->AddLine(D2DPoint(startPoint));
+ }
+
+ Point endPoint;
+ endPoint.x = aOrigin.x + aRadius * cosf(aEndAngle);
+ endPoint.y = aOrigin.y + aRadius * sinf(aEndAngle);
+
+ D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
+ D2D1_SWEEP_DIRECTION direction =
+ aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
+ D2D1_SWEEP_DIRECTION_CLOCKWISE;
+
+ // if startPoint and endPoint of our circle are too close there are D2D issues
+ // with drawing the circle as a single arc
+ const Float kEpsilon = 1e-5f;
+ if (!fullCircle ||
+ (std::abs(startPoint.x - endPoint.x) +
+ std::abs(startPoint.y - endPoint.y) > kEpsilon)) {
+
+ if (aAntiClockwise) {
+ if (aStartAngle - aEndAngle > M_PI) {
+ arcSize = D2D1_ARC_SIZE_LARGE;
+ }
+ } else {
+ if (aEndAngle - aStartAngle > M_PI) {
+ arcSize = D2D1_ARC_SIZE_LARGE;
+ }
+ }
+
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+ }
+ else {
+ // our first workaround attempt didn't work, so instead draw the circle as
+ // two half-circles
+ Float midAngle = aEndAngle > aStartAngle ?
+ Float(aStartAngle + M_PI) : Float(aEndAngle + M_PI);
+ Point midPoint;
+ midPoint.x = aOrigin.x + aRadius * cosf(midAngle);
+ midPoint.y = aOrigin.y + aRadius * sinf(midAngle);
+
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(midPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+
+ // if the adjusted endPoint computed above is used here and endPoint !=
+ // startPoint then this half of the circle won't render...
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(startPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+ }
+
+ mCurrentPoint = endPoint;
+}
+
+Point
+PathBuilderD2D::CurrentPoint() const
+{
+ return mCurrentPoint;
+}
+
+void
+PathBuilderD2D::EnsureActive(const Point &aPoint)
+{
+ if (!mFigureActive) {
+ mSink->BeginFigure(D2DPoint(aPoint), D2D1_FIGURE_BEGIN_FILLED);
+ mBeginPoint = aPoint;
+ mFigureActive = true;
+ }
+}
+
+already_AddRefed<Path>
+PathBuilderD2D::Finish()
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ }
+
+ HRESULT hr = mSink->Close();
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to close PathSink. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType);
+}
+
+already_AddRefed<PathBuilder>
+PathD2D::CopyToBuilder(FillRule aFillRule) const
+{
+ return TransformedCopyToBuilder(Matrix(), aFillRule);
+}
+
+already_AddRefed<PathBuilder>
+PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<ID2D1PathGeometry> path;
+ HRESULT hr = DrawTargetD2D1::factory()->CreatePathGeometry(getter_AddRefs(path));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1GeometrySink> sink;
+ hr = path->Open(getter_AddRefs(sink));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to open Geometry for writing. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aFillRule == FillRule::FILL_WINDING) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ }
+
+ if (mEndedActive) {
+ OpeningGeometrySink wrapSink(sink);
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2DMatrix(aTransform),
+ &wrapSink);
+ } else {
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2DMatrix(aTransform),
+ sink);
+ }
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to simplify PathGeometry to tranformed copy. Code: " << hexa(hr) << " Active: " << mEndedActive;
+ return nullptr;
+ }
+
+ RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType);
+
+ pathBuilder->mCurrentPoint = aTransform.TransformPoint(mEndPoint);
+
+ if (mEndedActive) {
+ pathBuilder->mFigureActive = true;
+ }
+
+ return pathBuilder.forget();
+}
+
+void
+PathD2D::StreamToSink(PathSink *aSink) const
+{
+ HRESULT hr;
+
+ StreamingGeometrySink sink(aSink);
+
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2D1::IdentityMatrix(), &sink);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to stream D2D path to sink. Code: " << hexa(hr);
+ return;
+ }
+}
+
+bool
+PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+ BOOL result;
+
+ HRESULT hr = mGeometry->FillContainsPoint(D2DPoint(aPoint), D2DMatrix(aTransform), 0.001f, &result);
+
+ if (FAILED(hr)) {
+ // Log
+ return false;
+ }
+
+ return !!result;
+}
+
+bool
+PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ BOOL result;
+
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+ HRESULT hr = mGeometry->StrokeContainsPoint(D2DPoint(aPoint),
+ aStrokeOptions.mLineWidth,
+ strokeStyle,
+ D2DMatrix(aTransform),
+ &result);
+
+ if (FAILED(hr)) {
+ // Log
+ return false;
+ }
+
+ return !!result;
+}
+
+Rect
+PathD2D::GetBounds(const Matrix &aTransform) const
+{
+ D2D1_RECT_F d2dBounds;
+
+ HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
+
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
+ }
+
+ return bounds;
+}
+
+Rect
+PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ D2D1_RECT_F d2dBounds;
+
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+ HRESULT hr =
+ mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
+ D2DMatrix(aTransform), &d2dBounds);
+
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
+ }
+
+ return bounds;
+}
+
+}
+}
diff --git a/system/graphics/2d/PathD2D.h b/system/graphics/2d/PathD2D.h
new file mode 100644
index 000000000..0fb550b4a
--- /dev/null
+++ b/system/graphics/2d/PathD2D.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PATHD2D_H_
+#define MOZILLA_GFX_PATHD2D_H_
+
+#include <d2d1.h>
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class PathD2D;
+
+class PathBuilderD2D : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderD2D)
+ PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule, BackendType aBackendType)
+ : mSink(aSink)
+ , mGeometry(aGeom)
+ , mFigureActive(false)
+ , mFillRule(aFillRule)
+ , mBackendType(aBackendType)
+ {
+ }
+ virtual ~PathBuilderD2D();
+
+ virtual void MoveTo(const Point &aPoint);
+ virtual void LineTo(const Point &aPoint);
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ virtual void Close();
+ virtual void Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
+ Float aEndAngle, bool aAntiClockwise = false);
+ virtual Point CurrentPoint() const;
+
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return mBackendType; }
+
+ ID2D1GeometrySink *GetSink() { return mSink; }
+
+ bool IsFigureActive() const { return mFigureActive; }
+
+private:
+ friend class PathD2D;
+
+ void EnsureActive(const Point &aPoint);
+
+ RefPtr<ID2D1GeometrySink> mSink;
+ RefPtr<ID2D1PathGeometry> mGeometry;
+
+ bool mFigureActive;
+ Point mCurrentPoint;
+ Point mBeginPoint;
+ FillRule mFillRule;
+ BackendType mBackendType;
+};
+
+class PathD2D : public Path
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D)
+ PathD2D(ID2D1PathGeometry *aGeometry, bool aEndedActive,
+ const Point &aEndPoint, FillRule aFillRule, BackendType aBackendType)
+ : mGeometry(aGeometry)
+ , mEndedActive(aEndedActive)
+ , mEndPoint(aEndPoint)
+ , mFillRule(aFillRule)
+ , mBackendType(aBackendType)
+ {}
+
+ virtual BackendType GetBackendType() const { return mBackendType; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const;
+
+ virtual void StreamToSink(PathSink *aSink) const;
+
+ virtual FillRule GetFillRule() const { return mFillRule; }
+
+ ID2D1Geometry *GetGeometry() { return mGeometry; }
+
+private:
+ friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
+
+ mutable RefPtr<ID2D1PathGeometry> mGeometry;
+ bool mEndedActive;
+ Point mEndPoint;
+ FillRule mFillRule;
+ BackendType mBackendType;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_PATHD2D_H_ */
diff --git a/system/graphics/2d/PathHelpers.cpp b/system/graphics/2d/PathHelpers.cpp
new file mode 100644
index 000000000..49c344b42
--- /dev/null
+++ b/system/graphics/2d/PathHelpers.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+UserDataKey sDisablePixelSnapping;
+
+void
+AppendRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ bool aDrawClockwise)
+{
+ if (aDrawClockwise) {
+ aPathBuilder->MoveTo(aRect.TopLeft());
+ aPathBuilder->LineTo(aRect.TopRight());
+ aPathBuilder->LineTo(aRect.BottomRight());
+ aPathBuilder->LineTo(aRect.BottomLeft());
+ } else {
+ aPathBuilder->MoveTo(aRect.TopRight());
+ aPathBuilder->LineTo(aRect.TopLeft());
+ aPathBuilder->LineTo(aRect.BottomLeft());
+ aPathBuilder->LineTo(aRect.BottomRight());
+ }
+ aPathBuilder->Close();
+}
+
+void
+AppendRoundedRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise)
+{
+ // For CW drawing, this looks like:
+ //
+ // ...******0** 1 C
+ // ****
+ // *** 2
+ // **
+ // *
+ // *
+ // 3
+ // *
+ // *
+ //
+ // Where 0, 1, 2, 3 are the control points of the Bezier curve for
+ // the corner, and C is the actual corner point.
+ //
+ // At the start of the loop, the current point is assumed to be
+ // the point adjacent to the top left corner on the top
+ // horizontal. Note that corner indices start at the top left and
+ // continue clockwise, whereas in our loop i = 0 refers to the top
+ // right corner.
+ //
+ // When going CCW, the control points are swapped, and the first
+ // corner that's drawn is the top left (along with the top segment).
+ //
+ // There is considerable latitude in how one chooses the four
+ // control points for a Bezier curve approximation to an ellipse.
+ // For the overall path to be continuous and show no corner at the
+ // endpoints of the arc, points 0 and 3 must be at the ends of the
+ // straight segments of the rectangle; points 0, 1, and C must be
+ // collinear; and points 3, 2, and C must also be collinear. This
+ // leaves only two free parameters: the ratio of the line segments
+ // 01 and 0C, and the ratio of the line segments 32 and 3C. See
+ // the following papers for extensive discussion of how to choose
+ // these ratios:
+ //
+ // Dokken, Tor, et al. "Good approximation of circles by
+ // curvature-continuous Bezier curves." Computer-Aided
+ // Geometric Design 7(1990) 33--41.
+ // Goldapp, Michael. "Approximation of circular arcs by cubic
+ // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
+ // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
+ // quadratic, or cubic Bezier curves."
+ // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
+ //
+ // We follow the approach in section 2 of Goldapp (least-error,
+ // Hermite-type approximation) and make both ratios equal to
+ //
+ // 2 2 + n - sqrt(2n + 28)
+ // alpha = - * ---------------------
+ // 3 n - 4
+ //
+ // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
+ //
+ // This is the result of Goldapp's equation (10b) when the angle
+ // swept out by the arc is pi/2, and the parameter "a-bar" is the
+ // expression given immediately below equation (21).
+ //
+ // Using this value, the maximum radial error for a circle, as a
+ // fraction of the radius, is on the order of 0.2 x 10^-3.
+ // Neither Dokken nor Goldapp discusses error for a general
+ // ellipse; Maisonobe does, but his choice of control points
+ // follows different constraints, and Goldapp's expression for
+ // 'alpha' gives much smaller radial error, even for very flat
+ // ellipses, than Maisonobe's equivalent.
+ //
+ // For the various corners and for each axis, the sign of this
+ // constant changes, or it might be 0 -- it's multiplied by the
+ // appropriate multiplier from the list before using.
+
+ const Float alpha = Float(0.55191497064665766025);
+
+ typedef struct { Float a, b; } twoFloats;
+
+ twoFloats cwCornerMults[4] = { { -1, 0 }, // cc == clockwise
+ { 0, -1 },
+ { +1, 0 },
+ { 0, +1 } };
+ twoFloats ccwCornerMults[4] = { { +1, 0 }, // ccw == counter-clockwise
+ { 0, -1 },
+ { -1, 0 },
+ { 0, +1 } };
+
+ twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults;
+
+ Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(),
+ aRect.BottomRight(), aRect.BottomLeft() };
+
+ Point pc, p0, p1, p2, p3;
+
+ if (aDrawClockwise) {
+ aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width,
+ aRect.Y()));
+ } else {
+ aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width,
+ aRect.Y()));
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
+ int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4);
+
+ // i+2 and i+3 respectively. These are used to index into the corner
+ // multiplier table, and were deduced by calculating out the long form
+ // of each corner and finding a pattern in the signs and values.
+ int i2 = (i+2) % 4;
+ int i3 = (i+3) % 4;
+
+ pc = cornerCoords[c];
+
+ if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) {
+ p0.x = pc.x + cornerMults[i].a * aRadii[c].width;
+ p0.y = pc.y + cornerMults[i].b * aRadii[c].height;
+
+ p3.x = pc.x + cornerMults[i3].a * aRadii[c].width;
+ p3.y = pc.y + cornerMults[i3].b * aRadii[c].height;
+
+ p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width;
+ p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height;
+
+ p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width;
+ p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height;
+
+ aPathBuilder->LineTo(p0);
+ aPathBuilder->BezierTo(p1, p2, p3);
+ } else {
+ aPathBuilder->LineTo(pc);
+ }
+ }
+
+ aPathBuilder->Close();
+}
+
+void
+AppendEllipseToPath(PathBuilder* aPathBuilder,
+ const Point& aCenter,
+ const Size& aDimensions)
+{
+ Size halfDim = aDimensions / 2.f;
+ Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
+ RectCornerRadii radii(halfDim.width, halfDim.height);
+
+ AppendRoundedRectToPath(aPathBuilder, rect, radii);
+}
+
+bool
+SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget,
+ Float aLineWidth)
+{
+ Matrix mat = aDrawTarget.GetTransform();
+ if (mat.HasNonTranslation()) {
+ return false;
+ }
+ if (aP1.x != aP2.x && aP1.y != aP2.y) {
+ return false; // not a horizontal or vertical line
+ }
+ Point p1 = aP1 + mat.GetTranslation(); // into device space
+ Point p2 = aP2 + mat.GetTranslation();
+ p1.Round();
+ p2.Round();
+ p1 -= mat.GetTranslation(); // back into user space
+ p2 -= mat.GetTranslation();
+
+ aP1 = p1;
+ aP2 = p2;
+
+ bool lineWidthIsOdd = (int(aLineWidth) % 2) == 1;
+ if (lineWidthIsOdd) {
+ if (aP1.x == aP2.x) {
+ // snap vertical line, adding 0.5 to align it to be mid-pixel:
+ aP1 += Point(0.5, 0);
+ aP2 += Point(0.5, 0);
+ } else {
+ // snap horizontal line, adding 0.5 to align it to be mid-pixel:
+ aP1 += Point(0, 0.5);
+ aP2 += Point(0, 0.5);
+ }
+ }
+ return true;
+}
+
+void
+StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (aRect.IsEmpty()) {
+ return;
+ }
+
+ Point p1 = aRect.TopLeft();
+ Point p2 = aRect.BottomLeft();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.BottomLeft();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopLeft();
+ p2 = aRect.TopRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopRight();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+}
+
+// The logic for this comes from _cairo_stroke_style_max_distance_from_path
+Margin
+MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
+ const Matrix& aTransform)
+{
+ double styleExpansionFactor = 0.5f;
+
+ if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
+ styleExpansionFactor = M_SQRT1_2;
+ }
+
+ if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
+ styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
+ styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
+ }
+
+ styleExpansionFactor *= aStrokeOptions.mLineWidth;
+
+ double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
+ double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
+ return Margin(dy, dx, dy, dx);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathHelpers.h b/system/graphics/2d/PathHelpers.h
new file mode 100644
index 000000000..553a886b9
--- /dev/null
+++ b/system/graphics/2d/PathHelpers.h
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PATHHELPERS_H_
+#define MOZILLA_GFX_PATHHELPERS_H_
+
+#include "2D.h"
+#include "UserData.h"
+
+#include <cmath>
+
+namespace mozilla {
+namespace gfx {
+
+// Kappa constant for 90-degree angle
+const Float kKappaFactor = 0.55191497064665766025f;
+
+// Calculate kappa constant for partial curve. The sign of angle in the
+// tangent will actually ensure this is negative for a counter clockwise
+// sweep, so changing signs later isn't needed.
+inline Float ComputeKappaFactor(Float aAngle)
+{
+ return (4.0f / 3.0f) * tanf(aAngle / 4.0f);
+}
+
+/**
+ * Draws a partial arc <= 90 degrees given exact start and end points.
+ * Assumes that it is continuing from an already specified start point.
+ */
+template <typename T>
+inline void PartialArcToBezier(T* aSink,
+ const Point& aStartOffset, const Point& aEndOffset,
+ const Matrix& aTransform,
+ Float aKappaFactor = kKappaFactor)
+{
+ Point cp1 =
+ aStartOffset + Point(-aStartOffset.y, aStartOffset.x) * aKappaFactor;
+
+ Point cp2 =
+ aEndOffset + Point(aEndOffset.y, -aEndOffset.x) * aKappaFactor;
+
+ aSink->BezierTo(aTransform.TransformPoint(cp1),
+ aTransform.TransformPoint(cp2),
+ aTransform.TransformPoint(aEndOffset));
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ * Specialized version avoiding kappa calculation.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+ const Point& aOrigin, const Size& aRadius,
+ const Point& aStartPoint, const Point& aEndPoint,
+ Float aKappaFactor = kKappaFactor)
+{
+ aSink->LineTo(aStartPoint);
+ if (!aRadius.IsEmpty()) {
+ Float kappaX = aKappaFactor * aRadius.width / aRadius.height;
+ Float kappaY = aKappaFactor * aRadius.height / aRadius.width;
+ Point startOffset = aStartPoint - aOrigin;
+ Point endOffset = aEndPoint - aOrigin;
+ aSink->BezierTo(aStartPoint + Point(-startOffset.y * kappaX, startOffset.x * kappaY),
+ aEndPoint + Point(endOffset.y * kappaX, -endOffset.x * kappaY),
+ aEndPoint);
+ } else if (aEndPoint != aStartPoint) {
+ aSink->LineTo(aEndPoint);
+ }
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+ const Point& aOrigin, const Size& aRadius,
+ const Point& aStartPoint, const Point& aEndPoint,
+ Float aStartAngle, Float aEndAngle)
+{
+ AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
+ ComputeKappaFactor(aEndAngle - aStartAngle));
+}
+
+template <typename T>
+void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
+ float aStartAngle, float aEndAngle, bool aAntiClockwise,
+ float aRotation = 0.0f)
+{
+ Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
+
+ // Calculate the total arc we're going to sweep.
+ Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
+
+ // Clockwise we always sweep from the smaller to the larger angle, ccw
+ // it's vice versa.
+ if (arcSweepLeft < 0) {
+ // Rerverse sweep is modulo'd into range rather than clamped.
+ arcSweepLeft = Float(2.0f * M_PI) + fmodf(arcSweepLeft, Float(2.0f * M_PI));
+ // Recalculate the start angle to land closer to end angle.
+ aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
+ } else if (arcSweepLeft > Float(2.0f * M_PI)) {
+ // Sweeping more than 2 * pi is a full circle.
+ arcSweepLeft = Float(2.0f * M_PI);
+ }
+
+ Float currentStartAngle = aStartAngle;
+ Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle));
+ Matrix transform = Matrix::Scaling(aRadius.width, aRadius.height);
+ if (aRotation != 0.0f) {
+ transform *= Matrix::Rotation(aRotation);
+ }
+ transform.PostTranslate(aOrigin);
+ aSink->LineTo(transform.TransformPoint(currentStartOffset));
+
+ while (arcSweepLeft > 0) {
+ Float currentEndAngle =
+ currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection;
+ Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle));
+
+ PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform,
+ ComputeKappaFactor(currentEndAngle - currentStartAngle));
+
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ arcSweepLeft -= Float(M_PI / 2.0f);
+ currentStartAngle = currentEndAngle;
+ currentStartOffset = currentEndOffset;
+ }
+}
+
+/* This is basically the ArcToBezier with the parameters for drawing a circle
+ * inlined which vastly simplifies it and avoids a bunch of transcedental function
+ * calls which should make it faster. */
+template <typename T>
+void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
+{
+ Matrix transform(aRadius.width, 0, 0, aRadius.height, aOrigin.x, aOrigin.y);
+ Point currentStartOffset(1, 0);
+
+ aSink->LineTo(transform.TransformPoint(currentStartOffset));
+
+ for (int i = 0; i < 4; i++) {
+ // cos(x+pi/2) == -sin(x)
+ // sin(x+pi/2) == cos(x)
+ Point currentEndOffset(-currentStartOffset.y, currentStartOffset.x);
+
+ PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform);
+
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ currentStartOffset = currentEndOffset;
+ }
+}
+
+/**
+ * Appends a path represending a rectangle to the path being built by
+ * aPathBuilder.
+ *
+ * aRect The rectangle to append.
+ * aDrawClockwise If set to true, the path will start at the left of the top
+ * left edge and draw clockwise. If set to false the path will
+ * start at the right of the top left edge and draw counter-
+ * clockwise.
+ */
+GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ bool aDrawClockwise = true);
+
+inline already_AddRefed<Path> MakePathForRect(const DrawTarget& aDrawTarget,
+ const Rect& aRect,
+ bool aDrawClockwise = true)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendRectToPath(builder, aRect, aDrawClockwise);
+ return builder->Finish();
+}
+
+struct RectCornerRadii {
+ Size radii[RectCorner::Count];
+
+ RectCornerRadii() {}
+
+ explicit RectCornerRadii(Float radius) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].SizeTo(radius, radius);
+ }
+ }
+
+ explicit RectCornerRadii(Float radiusX, Float radiusY) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].SizeTo(radiusX, radiusY);
+ }
+ }
+
+ RectCornerRadii(Float tl, Float tr, Float br, Float bl) {
+ radii[RectCorner::TopLeft].SizeTo(tl, tl);
+ radii[RectCorner::TopRight].SizeTo(tr, tr);
+ radii[RectCorner::BottomRight].SizeTo(br, br);
+ radii[RectCorner::BottomLeft].SizeTo(bl, bl);
+ }
+
+ RectCornerRadii(const Size& tl, const Size& tr,
+ const Size& br, const Size& bl) {
+ radii[RectCorner::TopLeft] = tl;
+ radii[RectCorner::TopRight] = tr;
+ radii[RectCorner::BottomRight] = br;
+ radii[RectCorner::BottomLeft] = bl;
+ }
+
+ const Size& operator[](size_t aCorner) const {
+ return radii[aCorner];
+ }
+
+ Size& operator[](size_t aCorner) {
+ return radii[aCorner];
+ }
+
+ bool operator==(const RectCornerRadii& aOther) const {
+ for (size_t i = 0; i < RectCorner::Count; i++) {
+ if (radii[i] != aOther.radii[i]) return false;
+ }
+ return true;
+ }
+
+ void Scale(Float aXScale, Float aYScale) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].Scale(aXScale, aYScale);
+ }
+ }
+
+ const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
+ Size& TopLeft() { return radii[RectCorner::TopLeft]; }
+
+ const Size TopRight() const { return radii[RectCorner::TopRight]; }
+ Size& TopRight() { return radii[RectCorner::TopRight]; }
+
+ const Size BottomRight() const { return radii[RectCorner::BottomRight]; }
+ Size& BottomRight() { return radii[RectCorner::BottomRight]; }
+
+ const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; }
+ Size& BottomLeft() { return radii[RectCorner::BottomLeft]; }
+};
+
+/**
+ * Appends a path represending a rounded rectangle to the path being built by
+ * aPathBuilder.
+ *
+ * aRect The rectangle to append.
+ * aCornerRadii Contains the radii of the top-left, top-right, bottom-right
+ * and bottom-left corners, in that order.
+ * aDrawClockwise If set to true, the path will start at the left of the top
+ * left edge and draw clockwise. If set to false the path will
+ * start at the right of the top left edge and draw counter-
+ * clockwise.
+ */
+GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise = true);
+
+inline already_AddRefed<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise = true)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise);
+ return builder->Finish();
+}
+
+/**
+ * Appends a path represending an ellipse to the path being built by
+ * aPathBuilder.
+ *
+ * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
+ * from aCenter, and aDimensions.height / 2.0 in the vertical direction.
+ */
+GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder,
+ const Point& aCenter,
+ const Size& aDimensions);
+
+inline already_AddRefed<Path> MakePathForEllipse(const DrawTarget& aDrawTarget,
+ const Point& aCenter,
+ const Size& aDimensions)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendEllipseToPath(builder, aCenter, aDimensions);
+ return builder->Finish();
+}
+
+/**
+ * If aDrawTarget's transform only contains a translation, and if this line is
+ * a horizontal or vertical line, this function will snap the line's vertices
+ * to align with the device pixel grid so that stroking the line with a one
+ * pixel wide stroke will result in a crisp line that is not antialiased over
+ * two pixels across its width.
+ *
+ * @return Returns true if this function snaps aRect's vertices, else returns
+ * false.
+ */
+GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget,
+ Float aLineWidth);
+
+/**
+ * This function paints each edge of aRect separately, snapping the edges using
+ * SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths
+ * helps ensure not only that the stroke spans a single row of device pixels if
+ * possible, but also that the ends of stroke dashes start and end on device
+ * pixels too.
+ */
+GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
+ DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions);
+
+/**
+ * Return the margin, in device space, by which a stroke can extend beyond the
+ * rendered shape.
+ * @param aStrokeOptions The stroke options that the stroke is drawn with.
+ * @param aTransform The user space to device space transform.
+ * @return The stroke margin.
+ */
+GFX2D_API Margin MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
+ const Matrix& aTransform);
+
+extern UserDataKey sDisablePixelSnapping;
+
+/**
+ * If aDrawTarget's transform only contains a translation or, if
+ * aAllowScaleOr90DegreeRotate is true, and/or a scale/90 degree rotation, this
+ * function will convert aRect to device space and snap it to device pixels.
+ * This function returns true if aRect is modified, otherwise it returns false.
+ *
+ * Note that the snapping is such that filling the rect using a DrawTarget
+ * which has the identity matrix as its transform will result in crisp edges.
+ * (That is, aRect will have integer values, aligning its edges between pixel
+ * boundaries.) If on the other hand you stroking the rect with an odd valued
+ * stroke width then the edges of the stroke will be antialiased (assuming an
+ * AntialiasMode that does antialiasing).
+ *
+ * Empty snaps are those which result in a rectangle of 0 area. If they are
+ * disallowed, an axis is left unsnapped if the rounding process results in a
+ * length of 0.
+ */
+inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aAllowScaleOr90DegreeRotate = false,
+ bool aAllowEmptySnaps = true)
+{
+ if (aDrawTarget.GetUserData(&sDisablePixelSnapping)) {
+ return false;
+ }
+
+ Matrix mat = aDrawTarget.GetTransform();
+
+ const Float epsilon = 0.0000001f;
+#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
+ if (!aAllowScaleOr90DegreeRotate &&
+ (!WITHIN_E(mat._11, 1.f) || !WITHIN_E(mat._22, 1.f) ||
+ !WITHIN_E(mat._12, 0.f) || !WITHIN_E(mat._21, 0.f))) {
+ // We have non-translation, but only translation is allowed.
+ return false;
+ }
+#undef WITHIN_E
+
+ Point p1 = mat.TransformPoint(aRect.TopLeft());
+ Point p2 = mat.TransformPoint(aRect.TopRight());
+ Point p3 = mat.TransformPoint(aRect.BottomRight());
+
+ // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
+ // two opposite corners define the entire rectangle. So check if
+ // the axis-aligned rectangle with opposite corners p1 and p3
+ // define an axis-aligned rectangle whose other corners are p2 and p4.
+ // We actually only need to check one of p2 and p4, since an affine
+ // transform maps parallelograms to parallelograms.
+ if (p2 == Point(p1.x, p3.y) || p2 == Point(p3.x, p1.y)) {
+ Point p1r = p1;
+ Point p3r = p3;
+ p1r.Round();
+ p3r.Round();
+ if (aAllowEmptySnaps || p1r.x != p3r.x) {
+ p1.x = p1r.x;
+ p3.x = p3r.x;
+ }
+ if (aAllowEmptySnaps || p1r.y != p3r.y) {
+ p1.y = p1r.y;
+ p3.y = p3r.y;
+ }
+
+ aRect.MoveTo(Point(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
+ aRect.SizeTo(Size(std::max(p1.x, p3.x) - aRect.X(),
+ std::max(p1.y, p3.y) - aRect.Y()));
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * This function has the same behavior as UserToDevicePixelSnapped except that
+ * aRect is not transformed to device space.
+ */
+inline bool MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aAllowScaleOr90DegreeRotate = false,
+ bool aAllowEmptySnaps = true)
+{
+ if (UserToDevicePixelSnapped(aRect, aDrawTarget,
+ aAllowScaleOr90DegreeRotate, aAllowEmptySnaps)) {
+ // Since UserToDevicePixelSnapped returned true we know there is no
+ // rotation/skew in 'mat', so we can just use TransformBounds() here.
+ Matrix mat = aDrawTarget.GetTransform();
+ mat.Invert();
+ aRect = mat.TransformBounds(aRect);
+ return true;
+ }
+ return false;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATHHELPERS_H_ */
diff --git a/system/graphics/2d/PathRecording.cpp b/system/graphics/2d/PathRecording.cpp
new file mode 100644
index 000000000..5884a0de9
--- /dev/null
+++ b/system/graphics/2d/PathRecording.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PathRecording.h"
+#include "DrawEventRecorder.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+void
+PathBuilderRecording::MoveTo(const Point &aPoint)
+{
+ PathOp op;
+ op.mType = PathOp::OP_MOVETO;
+ op.mP1 = aPoint;
+ mPathOps.push_back(op);
+ mPathBuilder->MoveTo(aPoint);
+}
+
+void
+PathBuilderRecording::LineTo(const Point &aPoint)
+{
+ PathOp op;
+ op.mType = PathOp::OP_LINETO;
+ op.mP1 = aPoint;
+ mPathOps.push_back(op);
+ mPathBuilder->LineTo(aPoint);
+}
+
+void
+PathBuilderRecording::BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3)
+{
+ PathOp op;
+ op.mType = PathOp::OP_BEZIERTO;
+ op.mP1 = aCP1;
+ op.mP2 = aCP2;
+ op.mP3 = aCP3;
+ mPathOps.push_back(op);
+ mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
+}
+
+void
+PathBuilderRecording::QuadraticBezierTo(const Point &aCP1, const Point &aCP2)
+{
+ PathOp op;
+ op.mType = PathOp::OP_QUADRATICBEZIERTO;
+ op.mP1 = aCP1;
+ op.mP2 = aCP2;
+ mPathOps.push_back(op);
+ mPathBuilder->QuadraticBezierTo(aCP1, aCP2);
+}
+
+void
+PathBuilderRecording::Close()
+{
+ PathOp op;
+ op.mType = PathOp::OP_CLOSE;
+ mPathOps.push_back(op);
+ mPathBuilder->Close();
+}
+
+Point
+PathBuilderRecording::CurrentPoint() const
+{
+ return mPathBuilder->CurrentPoint();
+}
+
+already_AddRefed<Path>
+PathBuilderRecording::Finish()
+{
+ RefPtr<Path> path = mPathBuilder->Finish();
+ return MakeAndAddRef<PathRecording>(path, mPathOps, mFillRule);
+}
+
+PathRecording::~PathRecording()
+{
+ for (size_t i = 0; i < mStoredRecorders.size(); i++) {
+ mStoredRecorders[i]->RemoveStoredObject(this);
+ mStoredRecorders[i]->RecordEvent(RecordedPathDestruction(this));
+ }
+}
+
+already_AddRefed<PathBuilder>
+PathRecording::CopyToBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> pathBuilder = mPath->CopyToBuilder(aFillRule);
+ RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule);
+ recording->mPathOps = mPathOps;
+ return recording.forget();
+}
+
+already_AddRefed<PathBuilder>
+PathRecording::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> pathBuilder = mPath->TransformedCopyToBuilder(aTransform, aFillRule);
+ RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule);
+ typedef std::vector<PathOp> pathOpVec;
+ for (pathOpVec::const_iterator iter = mPathOps.begin(); iter != mPathOps.end(); iter++) {
+ PathOp newPathOp;
+ newPathOp.mType = iter->mType;
+ if (sPointCount[newPathOp.mType] >= 1) {
+ newPathOp.mP1 = aTransform.TransformPoint(iter->mP1);
+ }
+ if (sPointCount[newPathOp.mType] >= 2) {
+ newPathOp.mP2 = aTransform.TransformPoint(iter->mP2);
+ }
+ if (sPointCount[newPathOp.mType] >= 3) {
+ newPathOp.mP3 = aTransform.TransformPoint(iter->mP3);
+ }
+ recording->mPathOps.push_back(newPathOp);
+ }
+ return recording.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathRecording.h b/system/graphics/2d/PathRecording.h
new file mode 100644
index 000000000..7faaa716f
--- /dev/null
+++ b/system/graphics/2d/PathRecording.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PATHRECORDING_H_
+#define MOZILLA_GFX_PATHRECORDING_H_
+
+#include "2D.h"
+#include <vector>
+#include <ostream>
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct PathOp
+{
+ enum OpType {
+ OP_MOVETO = 0,
+ OP_LINETO,
+ OP_BEZIERTO,
+ OP_QUADRATICBEZIERTO,
+ OP_CLOSE
+ };
+
+ OpType mType;
+ Point mP1;
+ Point mP2;
+ Point mP3;
+};
+
+const int32_t sPointCount[] = { 1, 1, 3, 2, 0, 0 };
+
+class PathRecording;
+class DrawEventRecorderPrivate;
+
+class PathBuilderRecording : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderRecording)
+ PathBuilderRecording(PathBuilder *aBuilder, FillRule aFillRule)
+ : mPathBuilder(aBuilder), mFillRule(aFillRule)
+ {
+ }
+
+ /* Move the current point in the path, any figure currently being drawn will
+ * be considered closed during fill operations, however when stroking the
+ * closing line segment will not be drawn.
+ */
+ virtual void MoveTo(const Point &aPoint);
+ /* Add a linesegment to the current figure */
+ virtual void LineTo(const Point &aPoint);
+ /* Add a cubic bezier curve to the current figure */
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ /* Add a quadratic bezier curve to the current figure */
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ /* Close the current figure, this will essentially generate a line segment
+ * from the current point to the starting point for the current figure
+ */
+ virtual void Close();
+
+ /* Add an arc to the current figure */
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise) {
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
+ aAntiClockwise);
+ }
+
+ /* Point the current subpath is at - or where the next subpath will start
+ * if there is no active subpath.
+ */
+ virtual Point CurrentPoint() const;
+
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+
+private:
+ friend class PathRecording;
+
+ RefPtr<PathBuilder> mPathBuilder;
+ FillRule mFillRule;
+ std::vector<PathOp> mPathOps;
+};
+
+class PathRecording : public Path
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathRecording)
+ PathRecording(Path *aPath, const std::vector<PathOp> aOps, FillRule aFillRule)
+ : mPath(aPath), mPathOps(aOps), mFillRule(aFillRule)
+ {
+ }
+
+ ~PathRecording();
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+ { return mPath->ContainsPoint(aPoint, aTransform); }
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+ { return mPath->StrokeContainsPoint(aStrokeOptions, aPoint, aTransform); }
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const
+ { return mPath->GetBounds(aTransform); }
+
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const
+ { return mPath->GetStrokedBounds(aStrokeOptions, aTransform); }
+
+ virtual void StreamToSink(PathSink *aSink) const { mPath->StreamToSink(aSink); }
+
+ virtual FillRule GetFillRule() const { return mFillRule; }
+
+ void StorePath(std::ostream &aStream) const;
+ static void ReadPathToBuilder(std::istream &aStream, PathBuilder *aBuilder);
+
+private:
+ friend class DrawTargetRecording;
+ friend class RecordedPathCreation;
+
+ RefPtr<Path> mPath;
+ std::vector<PathOp> mPathOps;
+ FillRule mFillRule;
+
+ // Event recorders that have this path in their event stream.
+ std::vector<RefPtr<DrawEventRecorderPrivate>> mStoredRecorders;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATHRECORDING_H_ */
diff --git a/system/graphics/2d/PathSkia.cpp b/system/graphics/2d/PathSkia.cpp
new file mode 100644
index 000000000..44329bb76
--- /dev/null
+++ b/system/graphics/2d/PathSkia.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PathSkia.h"
+#include <math.h>
+#include "DrawTargetSkia.h"
+#include "Logging.h"
+#include "HelpersSkia.h"
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+PathBuilderSkia::PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule)
+ : mPath(aPath)
+{
+ SkMatrix matrix;
+ GfxMatrixToSkiaMatrix(aTransform, matrix);
+ mPath.transform(matrix);
+ SetFillRule(aFillRule);
+}
+
+PathBuilderSkia::PathBuilderSkia(FillRule aFillRule)
+{
+ SetFillRule(aFillRule);
+}
+
+void
+PathBuilderSkia::SetFillRule(FillRule aFillRule)
+{
+ mFillRule = aFillRule;
+ if (mFillRule == FillRule::FILL_WINDING) {
+ mPath.setFillType(SkPath::kWinding_FillType);
+ } else {
+ mPath.setFillType(SkPath::kEvenOdd_FillType);
+ }
+}
+
+void
+PathBuilderSkia::MoveTo(const Point &aPoint)
+{
+ mPath.moveTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+}
+
+void
+PathBuilderSkia::LineTo(const Point &aPoint)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aPoint);
+ } else {
+ mPath.lineTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+ }
+}
+
+void
+PathBuilderSkia::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aCP1);
+ }
+ mPath.cubicTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
+ SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y),
+ SkFloatToScalar(aCP3.x), SkFloatToScalar(aCP3.y));
+}
+
+void
+PathBuilderSkia::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aCP1);
+ }
+ mPath.quadTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
+ SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y));
+}
+
+void
+PathBuilderSkia::Close()
+{
+ mPath.close();
+}
+
+void
+PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise)
+{
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Point
+PathBuilderSkia::CurrentPoint() const
+{
+ int pointCount = mPath.countPoints();
+ if (!pointCount) {
+ return Point(0, 0);
+ }
+ SkPoint point = mPath.getPoint(pointCount - 1);
+ return Point(SkScalarToFloat(point.fX), SkScalarToFloat(point.fY));
+}
+
+already_AddRefed<Path>
+PathBuilderSkia::Finish()
+{
+ return MakeAndAddRef<PathSkia>(mPath, mFillRule);
+}
+
+void
+PathBuilderSkia::AppendPath(const SkPath &aPath)
+{
+ mPath.addPath(aPath);
+}
+
+already_AddRefed<PathBuilder>
+PathSkia::CopyToBuilder(FillRule aFillRule) const
+{
+ return TransformedCopyToBuilder(Matrix(), aFillRule);
+}
+
+already_AddRefed<PathBuilder>
+PathSkia::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ return MakeAndAddRef<PathBuilderSkia>(aTransform, mPath, aFillRule);
+}
+
+static bool
+SkPathContainsPoint(const SkPath& aPath, const Point& aPoint, const Matrix& aTransform)
+{
+ Matrix inverse = aTransform;
+ if (!inverse.Invert()) {
+ return false;
+ }
+
+ SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
+ return aPath.contains(point.fX, point.fY);
+}
+
+bool
+PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return false;
+ }
+
+ return SkPathContainsPoint(mPath, aPoint, aTransform);
+}
+
+bool
+PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return false;
+ }
+
+ SkPaint paint;
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return false;
+ }
+
+ SkPath strokePath;
+ paint.getFillPath(mPath, &strokePath);
+
+ return SkPathContainsPoint(strokePath, aPoint, aTransform);
+}
+
+Rect
+PathSkia::GetBounds(const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return Rect();
+ }
+
+ Rect bounds = SkRectToRect(mPath.getBounds());
+ return aTransform.TransformBounds(bounds);
+}
+
+Rect
+PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return Rect();
+ }
+
+ SkPaint paint;
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return Rect();
+ }
+
+ SkPath result;
+ paint.getFillPath(mPath, &result);
+
+ Rect bounds = SkRectToRect(result.getBounds());
+ return aTransform.TransformBounds(bounds);
+}
+
+void
+PathSkia::StreamToSink(PathSink *aSink) const
+{
+ SkPath::RawIter iter(mPath);
+
+ SkPoint points[4];
+ SkPath::Verb currentVerb;
+ while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
+ switch (currentVerb) {
+ case SkPath::kMove_Verb:
+ aSink->MoveTo(SkPointToPoint(points[0]));
+ break;
+ case SkPath::kLine_Verb:
+ aSink->LineTo(SkPointToPoint(points[1]));
+ break;
+ case SkPath::kCubic_Verb:
+ aSink->BezierTo(SkPointToPoint(points[1]),
+ SkPointToPoint(points[2]),
+ SkPointToPoint(points[3]));
+ break;
+ case SkPath::kQuad_Verb:
+ aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
+ SkPointToPoint(points[2]));
+ break;
+ case SkPath::kClose_Verb:
+ aSink->Close();
+ break;
+ default:
+ MOZ_ASSERT(false);
+ // Unexpected verb found in path!
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/PathSkia.h b/system/graphics/2d/PathSkia.h
new file mode 100644
index 000000000..aec06bb2e
--- /dev/null
+++ b/system/graphics/2d/PathSkia.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PATH_SKIA_H_
+#define MOZILLA_GFX_PATH_SKIA_H_
+
+#include "2D.h"
+#include "skia/include/core/SkPath.h"
+
+namespace mozilla {
+namespace gfx {
+
+class PathSkia;
+
+class PathBuilderSkia : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderSkia)
+ PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule);
+ explicit PathBuilderSkia(FillRule aFillRule);
+
+ virtual void MoveTo(const Point &aPoint);
+ virtual void LineTo(const Point &aPoint);
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ virtual void Close();
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false);
+ virtual Point CurrentPoint() const;
+ virtual already_AddRefed<Path> Finish();
+
+ void AppendPath(const SkPath &aPath);
+
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
+private:
+
+ void SetFillRule(FillRule aFillRule);
+
+ SkPath mPath;
+ FillRule mFillRule;
+};
+
+class PathSkia : public Path
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSkia)
+ PathSkia(SkPath& aPath, FillRule aFillRule)
+ : mFillRule(aFillRule)
+ {
+ mPath.swap(aPath);
+ }
+
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const;
+
+ virtual void StreamToSink(PathSink *aSink) const;
+
+ virtual FillRule GetFillRule() const { return mFillRule; }
+
+ const SkPath& GetPath() const { return mPath; }
+
+private:
+ friend class DrawTargetSkia;
+
+ SkPath mPath;
+ FillRule mFillRule;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATH_SKIA_H_ */
diff --git a/system/graphics/2d/PatternHelpers.h b/system/graphics/2d/PatternHelpers.h
new file mode 100644
index 000000000..763b30a6f
--- /dev/null
+++ b/system/graphics/2d/PatternHelpers.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_PATTERNHELPERS_H
+#define _MOZILLA_GFX_PATTERNHELPERS_H
+
+#include "mozilla/Alignment.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * This class is used to allow general pattern creation functions to return
+ * any type of pattern via an out-paramater without allocating a pattern
+ * instance on the free-store (an instance of this class being created on the
+ * stack before passing it in to the creation function). Without this class
+ * writing pattern creation functions would be a pain since Pattern objects are
+ * not reference counted, making lifetime management of instances created on
+ * the free-store and returned from a creation function hazardous. Besides
+ * that, in the case that ColorPattern's are expected to be common, it is
+ * particularly desirable to avoid the overhead of allocating on the
+ * free-store.
+ */
+class GeneralPattern
+{
+public:
+ explicit GeneralPattern()
+ : mPattern(nullptr)
+ {}
+
+ GeneralPattern(const GeneralPattern& aOther)
+ : mPattern(nullptr)
+ {}
+
+ ~GeneralPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ Pattern* Init(const Pattern& aPattern) {
+ MOZ_ASSERT(!mPattern);
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ mPattern = new (mColorPattern.addr())
+ ColorPattern(static_cast<const ColorPattern&>(aPattern));
+ break;
+ case PatternType::LINEAR_GRADIENT:
+ mPattern = new (mLinearGradientPattern.addr())
+ LinearGradientPattern(static_cast<const LinearGradientPattern&>(aPattern));
+ break;
+ case PatternType::RADIAL_GRADIENT:
+ mPattern = new (mRadialGradientPattern.addr())
+ RadialGradientPattern(static_cast<const RadialGradientPattern&>(aPattern));
+ break;
+ case PatternType::SURFACE:
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(static_cast<const SurfacePattern&>(aPattern));
+ break;
+ default:
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown pattern type");
+ }
+ return mPattern;
+ }
+
+ ColorPattern* InitColorPattern(const Color &aColor) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mColorPattern.addr()) ColorPattern(aColor);
+ return mColorPattern.addr();
+ }
+
+ LinearGradientPattern* InitLinearGradientPattern(const Point &aBegin,
+ const Point &aEnd,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mLinearGradientPattern.addr())
+ LinearGradientPattern(aBegin, aEnd, aStops, aMatrix);
+ return mLinearGradientPattern.addr();
+ }
+
+ RadialGradientPattern* InitRadialGradientPattern(const Point &aCenter1,
+ const Point &aCenter2,
+ Float aRadius1,
+ Float aRadius2,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mRadialGradientPattern.addr())
+ RadialGradientPattern(aCenter1, aCenter2, aRadius1, aRadius2, aStops, aMatrix);
+ return mRadialGradientPattern.addr();
+ }
+
+ SurfacePattern* InitSurfacePattern(SourceSurface *aSourceSurface,
+ ExtendMode aExtendMode,
+ const Matrix &aMatrix = Matrix(),
+ SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
+ const IntRect &aSamplingRect = IntRect()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(aSourceSurface, aExtendMode, aMatrix, aSamplingFilter, aSamplingRect);
+ return mSurfacePattern.addr();
+ }
+
+ Pattern* GetPattern() {
+ return mPattern;
+ }
+
+ const Pattern* GetPattern() const {
+ return mPattern;
+ }
+
+ operator Pattern&() {
+ if (!mPattern) {
+ MOZ_CRASH("GFX: GeneralPattern not initialized");
+ }
+ return *mPattern;
+ }
+
+private:
+ union {
+ AlignedStorage2<ColorPattern> mColorPattern;
+ AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
+ AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
+ AlignedStorage2<SurfacePattern> mSurfacePattern;
+ };
+ Pattern *mPattern;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_PATTERNHELPERS_H
+
diff --git a/system/graphics/2d/Point.h b/system/graphics/2d/Point.h
new file mode 100644
index 000000000..f66967622
--- /dev/null
+++ b/system/graphics/2d/Point.h
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_POINT_H_
+#define MOZILLA_GFX_POINT_H_
+
+#include "mozilla/Attributes.h"
+#include "Types.h"
+#include "Coord.h"
+#include "BaseCoord.h"
+#include "BasePoint.h"
+#include "BasePoint3D.h"
+#include "BasePoint4D.h"
+#include "BaseSize.h"
+#include "mozilla/TypeTraits.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+// This should only be used by the typedefs below.
+struct UnknownUnits {};
+
+} // namespace gfx
+
+template<> struct IsPixel<gfx::UnknownUnits> : TrueType {};
+
+namespace gfx {
+
+/// Use this for parameters of functions to allow implicit conversions to
+/// integer types but not floating point types.
+/// We use this wrapper to prevent IntSize and IntPoint's constructors to
+/// take foating point values as parameters, and not require their constructors
+/// to have implementations for each permutation of integer types.
+template<typename T>
+struct IntParam {
+ constexpr MOZ_IMPLICIT IntParam(char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long long val) : value(val) {}
+ template<typename Unit>
+ constexpr MOZ_IMPLICIT IntParam(IntCoordTyped<Unit> val) : value(val) {}
+
+ // Disable the evil ones!
+ MOZ_IMPLICIT IntParam(float val) = delete;
+ MOZ_IMPLICIT IntParam(double val) = delete;
+
+ T value;
+};
+
+template<class units, class> struct PointTyped;
+template<class units, class> struct SizeTyped;
+
+template<class units>
+struct IntPointTyped :
+ public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef IntParam<int32_t> ToInt;
+ typedef IntCoordTyped<units> Coord;
+ typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super;
+
+ constexpr IntPointTyped() : Super() {}
+ constexpr IntPointTyped(ToInt aX, ToInt aY) : Super(Coord(aX.value), Coord(aY.value)) {}
+
+ static IntPointTyped<units> Round(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX + 0.5)), int32_t(floorf(aY + 0.5)));
+ }
+
+ static IntPointTyped<units> Ceil(float aX, float aY) {
+ return IntPointTyped(int32_t(ceil(aX)), int32_t(ceil(aY)));
+ }
+
+ static IntPointTyped<units> Floor(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX)), int32_t(floorf(aY)));
+ }
+
+ static IntPointTyped<units> Truncate(float aX, float aY) {
+ return IntPointTyped(int32_t(aX), int32_t(aY));
+ }
+
+ static IntPointTyped<units> Round(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Ceil(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Floor(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Truncate(const PointTyped<units, float>& aPoint);
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static IntPointTyped<units> FromUnknownPoint(const IntPointTyped<UnknownUnits>& aPoint) {
+ return IntPointTyped<units>(aPoint.x, aPoint.y);
+ }
+
+ IntPointTyped<UnknownUnits> ToUnknownPoint() const {
+ return IntPointTyped<UnknownUnits>(this->x, this->y);
+ }
+};
+typedef IntPointTyped<UnknownUnits> IntPoint;
+
+template<class units, class F = Float>
+struct PointTyped :
+ public BasePoint< F, PointTyped<units, F>, CoordTyped<units, F> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef CoordTyped<units, F> Coord;
+ typedef BasePoint< F, PointTyped<units, F>, CoordTyped<units, F> > Super;
+
+ constexpr PointTyped() : Super() {}
+ constexpr PointTyped(F aX, F aY) : Super(Coord(aX), Coord(aY)) {}
+ // The mixed-type constructors (Float, Coord) and (Coord, Float) are needed to
+ // avoid ambiguities because Coord is implicitly convertible to Float.
+ constexpr PointTyped(F aX, Coord aY) : Super(Coord(aX), aY) {}
+ constexpr PointTyped(Coord aX, F aY) : Super(aX, Coord(aY)) {}
+ constexpr PointTyped(Coord aX, Coord aY) : Super(aX.value, aY.value) {}
+ constexpr MOZ_IMPLICIT PointTyped(const IntPointTyped<units>& point) : Super(F(point.x), F(point.y)) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static PointTyped<units, F> FromUnknownPoint(const PointTyped<UnknownUnits, F>& aPoint) {
+ return PointTyped<units, F>(aPoint.x, aPoint.y);
+ }
+
+ PointTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return PointTyped<UnknownUnits, F>(this->x, this->y);
+ }
+};
+typedef PointTyped<UnknownUnits> Point;
+typedef PointTyped<UnknownUnits, double> PointDouble;
+
+template<class units>
+IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
+ return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
+}
+
+template<class units>
+IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
+ return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
+}
+
+template<class units, class F = Float>
+struct Point3DTyped :
+ public BasePoint3D< F, Point3DTyped<units, F> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint3D< F, Point3DTyped<units, F> > Super;
+
+ Point3DTyped() : Super() {}
+ Point3DTyped(F aX, F aY, F aZ) : Super(aX, aY, aZ) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static Point3DTyped<units, F> FromUnknownPoint(const Point3DTyped<UnknownUnits, F>& aPoint) {
+ return Point3DTyped<units, F>(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ Point3DTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return Point3DTyped<UnknownUnits, F>(this->x, this->y, this->z);
+ }
+};
+typedef Point3DTyped<UnknownUnits> Point3D;
+typedef Point3DTyped<UnknownUnits, double> PointDouble3D;
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Round(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Round(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Ceil(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Ceil(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Floor(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Floor(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Truncate(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Truncate(aPoint.x, aPoint.y);
+}
+
+template<class units, class F = Float>
+struct Point4DTyped :
+ public BasePoint4D< F, Point4DTyped<units, F> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint4D< F, Point4DTyped<units, F> > Super;
+
+ Point4DTyped() : Super() {}
+ Point4DTyped(F aX, F aY, F aZ, F aW) : Super(aX, aY, aZ, aW) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static Point4DTyped<units, F> FromUnknownPoint(const Point4DTyped<UnknownUnits, F>& aPoint) {
+ return Point4DTyped<units, F>(aPoint.x, aPoint.y, aPoint.z, aPoint.w);
+ }
+
+ Point4DTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return Point4DTyped<UnknownUnits, F>(this->x, this->y, this->z, this->w);
+ }
+
+ PointTyped<units, F> As2DPoint() {
+ return PointTyped<units, F>(this->x / this->w, this->y / this->w);
+ }
+};
+typedef Point4DTyped<UnknownUnits> Point4D;
+typedef Point4DTyped<UnknownUnits, double> PointDouble4D;
+
+template<class units>
+struct IntSizeTyped :
+ public BaseSize< int32_t, IntSizeTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef IntParam<int32_t> ToInt;
+ typedef BaseSize< int32_t, IntSizeTyped<units> > Super;
+
+ constexpr IntSizeTyped() : Super() {}
+ constexpr IntSizeTyped(ToInt aWidth, ToInt aHeight) : Super(aWidth.value, aHeight.value) {}
+
+ static IntSizeTyped<units> Round(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth + 0.5)), int32_t(floorf(aHeight + 0.5)));
+ }
+
+ static IntSizeTyped<units> Truncate(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(aWidth), int32_t(aHeight));
+ }
+
+ static IntSizeTyped<units> Ceil(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(ceil(aWidth)), int32_t(ceil(aHeight)));
+ }
+
+ static IntSizeTyped<units> Floor(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth)), int32_t(floorf(aHeight)));
+ }
+
+ static IntSizeTyped<units> Round(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Ceil(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Floor(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Truncate(const SizeTyped<units, float>& aSize);
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static IntSizeTyped<units> FromUnknownSize(const IntSizeTyped<UnknownUnits>& aSize) {
+ return IntSizeTyped<units>(aSize.width, aSize.height);
+ }
+
+ IntSizeTyped<UnknownUnits> ToUnknownSize() const {
+ return IntSizeTyped<UnknownUnits>(this->width, this->height);
+ }
+};
+typedef IntSizeTyped<UnknownUnits> IntSize;
+
+template<class units, class F = Float>
+struct SizeTyped :
+ public BaseSize< F, SizeTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseSize< F, SizeTyped<units, F> > Super;
+
+ constexpr SizeTyped() : Super() {}
+ constexpr SizeTyped(F aWidth, F aHeight) : Super(aWidth, aHeight) {}
+ explicit SizeTyped(const IntSizeTyped<units>& size) :
+ Super(F(size.width), F(size.height)) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static SizeTyped<units, F> FromUnknownSize(const SizeTyped<UnknownUnits, F>& aSize) {
+ return SizeTyped<units, F>(aSize.width, aSize.height);
+ }
+
+ SizeTyped<UnknownUnits, F> ToUnknownSize() const {
+ return SizeTyped<UnknownUnits, F>(this->width, this->height);
+ }
+};
+typedef SizeTyped<UnknownUnits> Size;
+typedef SizeTyped<UnknownUnits, double> SizeDouble;
+
+template<class units>
+IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
+ return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)),
+ int32_t(floorf(aSize.height + 0.5f)));
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Round(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Round(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Ceil(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Ceil(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Floor(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Floor(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Truncate(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Truncate(aSize.width, aSize.height);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_POINT_H_ */
diff --git a/system/graphics/2d/Polygon.h b/system/graphics/2d/Polygon.h
new file mode 100644
index 000000000..e1738684c
--- /dev/null
+++ b/system/graphics/2d/Polygon.h
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_POLYGON_H
+#define MOZILLA_GFX_POLYGON_H
+
+#include "Matrix.h"
+#include "mozilla/Move.h"
+#include "nsTArray.h"
+#include "Point.h"
+#include "Triangle.h"
+
+#include <initializer_list>
+
+namespace mozilla {
+namespace gfx {
+
+// Polygon3DTyped stores the points of a convex planar polygon.
+template<class Units>
+class Polygon3DTyped {
+public:
+ Polygon3DTyped() {}
+
+ explicit Polygon3DTyped(const std::initializer_list<Point3DTyped<Units>>& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(aPoints)
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ explicit Polygon3DTyped(nsTArray<Point3DTyped<Units>>&& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(Move(aPoints))
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ explicit Polygon3DTyped(const nsTArray<Point3DTyped<Units>>& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(aPoints)
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ RectTyped<Units> BoundingBox() const
+ {
+ float minX, maxX, minY, maxY;
+ minX = maxX = mPoints[0].x;
+ minY = maxY = mPoints[0].y;
+
+ for (const Point3DTyped<Units>& point : mPoints) {
+ minX = std::min(point.x, minX);
+ maxX = std::max(point.x, maxX);
+
+ minY = std::min(point.y, minY);
+ maxY = std::max(point.y, maxY);
+ }
+
+ return RectTyped<Units>(minX, minY, maxX - minX, maxY - minY);
+ }
+
+ nsTArray<float>
+ CalculateDotProducts(const Polygon3DTyped<Units>& aPlane,
+ size_t& aPos, size_t& aNeg) const
+ {
+ // Point classification might produce incorrect results due to numerical
+ // inaccuracies. Using an epsilon value makes the splitting plane "thicker".
+ const float epsilon = 0.05f;
+
+ MOZ_ASSERT(!aPlane.GetPoints().IsEmpty());
+ const Point3DTyped<Units>& planeNormal = aPlane.GetNormal();
+ const Point3DTyped<Units>& planePoint = aPlane[0];
+
+ aPos = aNeg = 0;
+ nsTArray<float> dotProducts;
+ for (const Point3DTyped<Units>& point : mPoints) {
+ float dot = (point - planePoint).DotProduct(planeNormal);
+
+ if (dot > epsilon) {
+ aPos++;
+ } else if (dot < -epsilon) {
+ aNeg++;
+ } else {
+ // The point is within the thick plane.
+ dot = 0.0f;
+ }
+
+ dotProducts.AppendElement(dot);
+ }
+
+ return dotProducts;
+ }
+
+ // Clips the polygon against the given 2D rectangle.
+ Polygon3DTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
+ {
+ Polygon3DTyped<Units> polygon(mPoints, mNormal);
+
+ // Left edge
+ ClipPolygonWithEdge(polygon, aRect.BottomLeft(), aRect.TopLeft());
+
+ // Bottom edge
+ ClipPolygonWithEdge(polygon, aRect.BottomRight(), aRect.BottomLeft());
+
+ // Right edge
+ ClipPolygonWithEdge(polygon, aRect.TopRight(), aRect.BottomRight());
+
+ // Top edge
+ ClipPolygonWithEdge(polygon, aRect.TopLeft(), aRect.TopRight());
+
+ return polygon;
+ }
+
+ const Point3DTyped<Units>& GetNormal() const
+ {
+ return mNormal;
+ }
+
+ const nsTArray<Point3DTyped<Units>>& GetPoints() const
+ {
+ return mPoints;
+ }
+
+ const Point3DTyped<Units>& operator[](size_t aIndex) const
+ {
+ MOZ_ASSERT(mPoints.Length() > aIndex);
+ return mPoints[aIndex];
+ }
+
+ void SplitPolygon(const Polygon3DTyped<Units>& aSplittingPlane,
+ const nsTArray<float>& aDots,
+ nsTArray<Point3DTyped<Units>>& aBackPoints,
+ nsTArray<Point3DTyped<Units>>& aFrontPoints) const
+ {
+ static const auto Sign = [](const float& f) {
+ if (f > 0.0f) return 1;
+ if (f < 0.0f) return -1;
+ return 0;
+ };
+
+ const Point3DTyped<Units>& normal = aSplittingPlane.GetNormal();
+ const size_t pointCount = mPoints.Length();
+
+ for (size_t i = 0; i < pointCount; ++i) {
+ size_t j = (i + 1) % pointCount;
+
+ const Point3DTyped<Units>& a = mPoints[i];
+ const Point3DTyped<Units>& b = mPoints[j];
+ const float dotA = aDots[i];
+ const float dotB = aDots[j];
+
+ // The point is in front of or on the plane.
+ if (dotA >= 0) {
+ aFrontPoints.AppendElement(a);
+ }
+
+ // The point is behind or on the plane.
+ if (dotA <= 0) {
+ aBackPoints.AppendElement(a);
+ }
+
+ // If the sign of the dot products changes between two consecutive
+ // vertices, then the plane intersects with the polygon edge.
+ // The case where the polygon edge is within the plane is handled above.
+ if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
+ // Calculate the line segment and plane intersection point.
+ const Point3DTyped<Units> ab = b - a;
+ const float dotAB = ab.DotProduct(normal);
+ const float t = -dotA / dotAB;
+ const Point3DTyped<Units> p = a + (ab * t);
+
+ // Add the intersection point to both polygons.
+ aBackPoints.AppendElement(p);
+ aFrontPoints.AppendElement(p);
+ }
+ }
+ }
+
+ nsTArray<TriangleTyped<Units>> ToTriangles() const
+ {
+ nsTArray<TriangleTyped<Units>> triangles;
+
+ if (mPoints.Length() < 3) {
+ return triangles;
+ }
+
+ for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
+ TriangleTyped<Units> triangle(Point(mPoints[0].x, mPoints[0].y),
+ Point(mPoints[i].x, mPoints[i].y),
+ Point(mPoints[i+1].x, mPoints[i+1].y));
+ triangles.AppendElement(Move(triangle));
+ }
+
+ return triangles;
+ }
+
+ void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ TransformPoints(aTransform);
+ mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
+ }
+
+ void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ TransformPoints(aTransform);
+
+ // Normal vectors should be transformed using inverse transpose.
+ mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
+ }
+
+private:
+ void ClipPolygonWithEdge(Polygon3DTyped<Units>& aPolygon,
+ const PointTyped<Units>& aFirst,
+ const PointTyped<Units>& aSecond) const
+ {
+ const Point3DTyped<Units> a(aFirst.x, aFirst.y, 0.0f);
+ const Point3DTyped<Units> b(aSecond.x, aSecond.y, 0.0f);
+ const Point3DTyped<Units> normal(b.y - a.y, a.x - b.x, 0.0f);
+ Polygon3DTyped<Units> plane({a, b}, normal);
+
+ size_t pos, neg;
+ nsTArray<float> dots = aPolygon.CalculateDotProducts(plane, pos, neg);
+
+ nsTArray<Point3DTyped<Units>> backPoints, frontPoints;
+ aPolygon.SplitPolygon(plane, dots, backPoints, frontPoints);
+
+ // Only use the points that are behind the clipping plane.
+ aPolygon = Polygon3DTyped<Units>(Move(backPoints), aPolygon.GetNormal());
+ }
+
+#ifdef DEBUG
+ void EnsurePlanarPolygon() const
+ {
+ if (mPoints.Length() <= 3) {
+ // Polygons with three or less points are guaranteed to be planar.
+ return;
+ }
+
+ // This normal calculation method works only for planar polygons.
+ // The resulting normal vector will point towards the viewer when the
+ // polygon has a counter-clockwise winding order from the perspective
+ // of the viewer.
+ Point3DTyped<Units> normal;
+
+ for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
+ normal +=
+ (mPoints[i] - mPoints[0]).CrossProduct(mPoints[i + 1] - mPoints[0]);
+ }
+
+ // Ensure that at least one component is greater than zero.
+ // This avoids division by zero when normalizing the vector.
+ bool hasNonZeroComponent = std::abs(normal.x) > 0.0f ||
+ std::abs(normal.y) > 0.0f ||
+ std::abs(normal.z) > 0.0f;
+ MOZ_ASSERT(hasNonZeroComponent);
+
+ normal.Normalize();
+
+ // Ensure that the polygon is planar.
+ // http://mathworld.wolfram.com/Point-PlaneDistance.html
+ const float epsilon = 0.01f;
+ for (const Point3DTyped<Units>& point : mPoints) {
+ float d = normal.DotProduct(point - mPoints[0]);
+ MOZ_ASSERT(std::abs(d) < epsilon);
+ }
+ }
+#endif
+ void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ for (Point3DTyped<Units>& point : mPoints) {
+ point = aTransform.TransformPoint(point);
+ }
+ }
+
+ Point3DTyped<Units> mNormal;
+ nsTArray<Point3DTyped<Units>> mPoints;
+};
+
+typedef Polygon3DTyped<UnknownUnits> Polygon3D;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_POLYGON_H */
diff --git a/system/graphics/2d/Quaternion.cpp b/system/graphics/2d/Quaternion.cpp
new file mode 100644
index 000000000..63842d827
--- /dev/null
+++ b/system/graphics/2d/Quaternion.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Quaternion.h"
+#include "Matrix.h"
+#include "Tools.h"
+#include <algorithm>
+#include <ostream>
+#include <math.h>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+std::ostream&
+operator<<(std::ostream& aStream, const Quaternion& aQuat)
+{
+ return aStream << "< " << aQuat.x << " " << aQuat.y << " " << aQuat.z << " " << aQuat.w << ">";
+}
+
+void
+Quaternion::SetFromRotationMatrix(const Matrix4x4& m)
+{
+ // see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+ const Float trace = m._11 + m._22 + m._33;
+ if (trace > 0.0) {
+ const Float s = 0.5f / sqrt(trace + 1.0f);
+ w = 0.25f / s;
+ x = (m._32 - m._23) * s;
+ y = (m._13 - m._31) * s;
+ z = (m._21 - m._12) * s;
+ } else if (m._11 > m._22 && m._11 > m._33) {
+ const Float s = 2.0f * sqrt(1.0f + m._11 - m._22 - m._33);
+ w = (m._32 - m._23) / s;
+ x = 0.25f * s;
+ y = (m._12 + m._21) / s;
+ z = (m._13 + m._31) / s;
+ } else if (m._22 > m._33) {
+ const Float s = 2.0 * sqrt(1.0f + m._22 - m._11 - m._33);
+ w = (m._13 - m._31) / s;
+ x = (m._12 + m._21) / s;
+ y = 0.25f * s;
+ z = (m._23 + m._32) / s;
+ } else {
+ const Float s = 2.0 * sqrt(1.0f + m._33 - m._11 - m._22);
+ w = (m._21 - m._12) / s;
+ x = (m._13 + m._31) / s;
+ y = (m._23 + m._32) / s;
+ z = 0.25f * s;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Quaternion.h b/system/graphics/2d/Quaternion.h
new file mode 100644
index 000000000..f14d11348
--- /dev/null
+++ b/system/graphics/2d/Quaternion.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_QUATERNION_H_
+#define MOZILLA_GFX_QUATERNION_H_
+
+#include "Types.h"
+#include <math.h>
+#include <ostream>
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/MatrixFwd.h"
+#include "mozilla/gfx/Point.h"
+
+namespace mozilla {
+namespace gfx {
+
+class Quaternion
+{
+public:
+ Quaternion()
+ : x(0.0f), y(0.0f), z(0.0f), w(1.0f)
+ {}
+
+ Quaternion(Float aX, Float aY, Float aZ, Float aW)
+ : x(aX), y(aY), z(aZ), w(aW)
+ {}
+
+
+ Quaternion(const Quaternion& aOther)
+ {
+ memcpy(this, &aOther, sizeof(*this));
+ }
+
+ Float x, y, z, w;
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Quaternion& aQuat);
+
+ void Set(Float aX, Float aY, Float aZ, Float aW)
+ {
+ x = aX; y = aY; z = aZ; w = aW;
+ }
+
+ // Assumes upper 3x3 of aMatrix is a pure rotation matrix (no scaling)
+ void SetFromRotationMatrix(const Matrix4x4& aMatrix);
+
+ // result = this * aQuat
+ Quaternion operator*(const Quaternion &aQuat) const
+ {
+ Quaternion o;
+ const Float bx = aQuat.x, by = aQuat.y, bz = aQuat.z, bw = aQuat.w;
+
+ o.x = x*bw + w*bx + y*bz - z*by;
+ o.y = y*bw + w*by + z*bx - x*bz;
+ o.z = z*bw + w*bz + x*by - y*bx;
+ o.w = w*bw - x*bx - y*by - z*bz;
+ return o;
+ }
+
+ Quaternion& operator*=(const Quaternion &aQuat)
+ {
+ *this = *this * aQuat;
+ return *this;
+ }
+
+ Float Length() const
+ {
+ return sqrt(x*x + y*y + z*z + w*w);
+ }
+
+ Quaternion& Conjugate()
+ {
+ x *= -1.f; y *= -1.f; z *= -1.f;
+ return *this;
+ }
+
+ Quaternion& Normalize()
+ {
+ Float l = Length();
+ if (l) {
+ l = 1.0f / l;
+ x *= l; y *= l; z *= l; w *= l;
+ } else {
+ x = y = z = 0.f;
+ w = 1.f;
+ }
+ return *this;
+ }
+
+ Quaternion& Invert()
+ {
+ return Conjugate().Normalize();
+ }
+
+ Point3D RotatePoint(const Point3D& aPoint) {
+ Float uvx = Float(2.0) * (y*aPoint.z - z*aPoint.y);
+ Float uvy = Float(2.0) * (z*aPoint.x - x*aPoint.z);
+ Float uvz = Float(2.0) * (x*aPoint.y - y*aPoint.x);
+
+ return Point3D(aPoint.x + w*uvx + y*uvz - z*uvy,
+ aPoint.y + w*uvy + z*uvx - x*uvz,
+ aPoint.z + w*uvz + x*uvy - y*uvx);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/RadialGradientEffectD2D1.cpp b/system/graphics/2d/RadialGradientEffectD2D1.cpp
new file mode 100644
index 000000000..8f929d8e9
--- /dev/null
+++ b/system/graphics/2d/RadialGradientEffectD2D1.cpp
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RadialGradientEffectD2D1.h"
+
+#include "Logging.h"
+
+#include "ShadersD2D1.h"
+#include "HelpersD2D.h"
+
+#include <vector>
+
+#define TEXTW(x) L##x
+#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
+
+static const PCWSTR kXmlDescription =
+ XML(
+ <?xml version='1.0'?>
+ <Effect>
+ <!-- System Properties -->
+ <Property name='DisplayName' type='string' value='RadialGradientEffect'/>
+ <Property name='Author' type='string' value='Mozilla'/>
+ <Property name='Category' type='string' value='Pattern effects'/>
+ <Property name='Description' type='string' value='This effect is used to render radial gradients in a manner compliant with the 2D Canvas specification.'/>
+ <Inputs>
+ <Input name='Geometry'/>
+ </Inputs>
+ <Property name='StopCollection' type='iunknown'>
+ <Property name='DisplayName' type='string' value='Gradient stop collection'/>
+ </Property>
+ <Property name='Center1' type='vector2'>
+ <Property name='DisplayName' type='string' value='Inner circle center'/>
+ </Property>
+ <Property name='Center2' type='vector2'>
+ <Property name='DisplayName' type='string' value='Outer circle center'/>
+ </Property>
+ <Property name='Radius1' type='float'>
+ <Property name='DisplayName' type='string' value='Inner circle radius'/>
+ </Property>
+ <Property name='Radius2' type='float'>
+ <Property name='DisplayName' type='string' value='Outer circle radius'/>
+ </Property>
+ <Property name='Transform' type='matrix3x2'>
+ <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
+ </Property>
+
+ </Effect>
+ );
+
+// {FB947CDA-718E-40CC-AE7B-D255830D7D14}
+static const GUID GUID_SampleRadialGradientPS =
+ {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
+// {2C468128-6546-453C-8E25-F2DF0DE10A0F}
+static const GUID GUID_SampleRadialGradientA0PS =
+ {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
+
+namespace mozilla {
+namespace gfx {
+
+RadialGradientEffectD2D1::RadialGradientEffectD2D1()
+ : mRefCount(0)
+ , mCenter1(D2D1::Vector2F(0, 0))
+ , mCenter2(D2D1::Vector2F(0, 0))
+ , mRadius1(0)
+ , mRadius2(0)
+ , mTransform(D2D1::IdentityMatrix())
+
+{
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
+{
+ HRESULT hr;
+
+ hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pTransformGraph->SetSingleTransformNode(this);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ mEffectContext = pContextInternal;
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
+{
+ if (changeType == D2D1_CHANGE_TYPE_NONE) {
+ return S_OK;
+ }
+
+ // We'll need to inverse transform our pixel, precompute inverse here.
+ Matrix mat = ToMatrix(mTransform);
+ if (!mat.Invert()) {
+ // Singular
+ return S_OK;
+ }
+
+ if (!mStopCollection) {
+ return S_OK;
+ }
+
+ D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter1.y);
+ float dr = mRadius2 - mRadius1;
+ float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
+
+ HRESULT hr;
+
+ if (A == 0) {
+ hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS);
+ } else {
+ hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS);
+ }
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
+ hr = mDrawInfo->SetResourceTexture(1, tex);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ struct PSConstantBuffer
+ {
+ float diff[3];
+ float padding;
+ float center1[2];
+ float A;
+ float radius1;
+ float sq_radius1;
+ float repeat_correct;
+ float allow_odd;
+ float padding2[1];
+ float transform[8];
+ };
+
+ PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0.0f,
+ { mCenter1.x, mCenter1.y },
+ A, mRadius1, mRadius1 * mRadius1,
+ mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
+ mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
+ { 0.0f }, { mat._11, mat._21, mat._31, 0.0f,
+ mat._12, mat._22, mat._32, 0.0f } };
+
+ hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
+{
+ return pGraph->SetSingleTransformNode(this);
+}
+
+IFACEMETHODIMP_(ULONG)
+RadialGradientEffectD2D1::AddRef()
+{
+ return ++mRefCount;
+}
+
+IFACEMETHODIMP_(ULONG)
+RadialGradientEffectD2D1::Release()
+{
+ if (!--mRefCount) {
+ delete this;
+ return 0;
+ }
+ return mRefCount;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
+{
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
+ } else if (aIID == IID_ID2D1EffectImpl) {
+ *aPtr = static_cast<ID2D1EffectImpl*>(this);
+ } else if (aIID == IID_ID2D1DrawTransform) {
+ *aPtr = static_cast<ID2D1DrawTransform*>(this);
+ } else if (aIID == IID_ID2D1Transform) {
+ *aPtr = static_cast<ID2D1Transform*>(this);
+ } else if (aIID == IID_ID2D1TransformNode) {
+ *aPtr = static_cast<ID2D1TransformNode*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+
+ static_cast<IUnknown*>(*aPtr)->AddRef();
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect)
+{
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ *pOutputRect = *pInputRects;
+ *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const
+{
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ *pInputRects = *pOutputRect;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const
+{
+ MOZ_ASSERT(inputIndex == 0);
+
+ *pInvalidOutputRect = invalidInputRect;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo)
+{
+ mDrawInfo = pDrawInfo;
+ return S_OK;
+}
+
+HRESULT
+RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
+{
+ D2D1_PROPERTY_BINDING bindings[] = {
+ D2D1_VALUE_TYPE_BINDING(L"StopCollection", &RadialGradientEffectD2D1::SetStopCollection,
+ &RadialGradientEffectD2D1::GetStopCollection),
+ D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1, &RadialGradientEffectD2D1::GetCenter1),
+ D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2, &RadialGradientEffectD2D1::GetCenter2),
+ D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1, &RadialGradientEffectD2D1::GetRadius1),
+ D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2, &RadialGradientEffectD2D1::GetRadius2),
+ D2D1_VALUE_TYPE_BINDING(L"Transform", &RadialGradientEffectD2D1::SetTransform, &RadialGradientEffectD2D1::GetTransform)
+ };
+ HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to register radial gradient effect.";
+ }
+ return hr;
+}
+
+void
+RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+ aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
+}
+
+HRESULT __stdcall
+RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
+{
+ *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
+ (*aEffectImpl)->AddRef();
+
+ return S_OK;
+}
+
+HRESULT
+RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection)
+{
+ if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)getter_AddRefs(mStopCollection)))) {
+ return S_OK;
+ }
+
+ return E_INVALIDARG;
+}
+
+already_AddRefed<ID2D1ResourceTexture>
+RadialGradientEffectD2D1::CreateGradientTexture()
+{
+ std::vector<D2D1_GRADIENT_STOP> rawStops;
+ rawStops.resize(mStopCollection->GetGradientStopCount());
+ mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
+
+ std::vector<unsigned char> textureData;
+ textureData.resize(4096 * 4);
+ unsigned char *texData = &textureData.front();
+
+ float prevColorPos = 0;
+ float nextColorPos = 1.0f;
+ D2D1_COLOR_F prevColor = rawStops[0].color;
+ D2D1_COLOR_F nextColor = prevColor;
+
+ if (rawStops.size() >= 2) {
+ nextColor = rawStops[1].color;
+ nextColorPos = rawStops[1].position;
+ }
+
+ uint32_t stopPosition = 2;
+
+ // Not the most optimized way but this will do for now.
+ for (int i = 0; i < 4096; i++) {
+ // The 4095 seems a little counter intuitive, but we want the gradient
+ // color at offset 0 at the first pixel, and at offset 1.0f at the last
+ // pixel.
+ float pos = float(i) / 4095;
+
+ while (pos > nextColorPos) {
+ prevColor = nextColor;
+ prevColorPos = nextColorPos;
+ if (rawStops.size() > stopPosition) {
+ nextColor = rawStops[stopPosition].color;
+ nextColorPos = rawStops[stopPosition++].position;
+ } else {
+ nextColorPos = 1.0f;
+ }
+ }
+
+ float interp;
+
+ if (nextColorPos != prevColorPos) {
+ interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
+ } else {
+ interp = 0;
+ }
+
+ Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
+ prevColor.g + (nextColor.g - prevColor.g) * interp,
+ prevColor.b + (nextColor.b - prevColor.b) * interp,
+ prevColor.a + (nextColor.a - prevColor.a) * interp);
+
+ // Note D2D expects RGBA here!!
+ texData[i * 4] = (char)(255.0f * newColor.r);
+ texData[i * 4 + 1] = (char)(255.0f * newColor.g);
+ texData[i * 4 + 2] = (char)(255.0f * newColor.b);
+ texData[i * 4 + 3] = (char)(255.0f * newColor.a);
+ }
+
+ RefPtr<ID2D1ResourceTexture> tex;
+
+ UINT32 width = 4096;
+ UINT32 stride = 4096 * 4;
+ D2D1_RESOURCE_TEXTURE_PROPERTIES props;
+ // Older shader models do not support 1D textures. So just use a width x 1 texture.
+ props.dimensions = 2;
+ UINT32 dims[] = { width, 1 };
+ props.extents = dims;
+ props.channelDepth = D2D1_CHANNEL_DEPTH_4;
+ props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
+ props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
+ D2D1_EXTEND_MODE extendMode[] = { mStopCollection->GetExtendMode(), mStopCollection->GetExtendMode() };
+ props.extendModes = extendMode;
+
+ HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, getter_AddRefs(tex));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create resource texture: " << hexa(hr);
+ }
+
+ return tex.forget();
+}
+
+}
+}
diff --git a/system/graphics/2d/RadialGradientEffectD2D1.h b/system/graphics/2d/RadialGradientEffectD2D1.h
new file mode 100644
index 000000000..baa02aafb
--- /dev/null
+++ b/system/graphics/2d/RadialGradientEffectD2D1.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RADIALGRADIENTEFFECTD2D1_H_
+#define MOZILLA_GFX_RADIALGRADIENTEFFECTD2D1_H_
+
+#include <d2d1_1.h>
+#include <d2d1effectauthor.h>
+#include <d2d1effecthelpers.h>
+
+#include "2D.h"
+#include "mozilla/Attributes.h"
+
+// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
+DEFINE_GUID(CLSID_RadialGradientEffect,
+0x97143dc6, 0xcbc4, 0x4dd4, 0xa8, 0xba, 0x13, 0x34, 0x2b, 0xb, 0xa4, 0x6d);
+
+// Macro to keep our class nice and clean.
+#define SIMPLE_PROP(type, name) \
+public: \
+ HRESULT Set##name(type a##name) \
+ { m##name = a##name; return S_OK; } \
+ type Get##name() const { return m##name; } \
+private: \
+ type m##name;
+
+namespace mozilla {
+namespace gfx {
+
+enum {
+ RADIAL_PROP_STOP_COLLECTION = 0,
+ RADIAL_PROP_CENTER_1,
+ RADIAL_PROP_CENTER_2,
+ RADIAL_PROP_RADIUS_1,
+ RADIAL_PROP_RADIUS_2,
+ RADIAL_PROP_TRANSFORM
+};
+
+class RadialGradientEffectD2D1 final : public ID2D1EffectImpl
+ , public ID2D1DrawTransform
+{
+public:
+ // ID2D1EffectImpl
+ IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph);
+ IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
+ IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph);
+
+ // IUnknown
+ IFACEMETHODIMP_(ULONG) AddRef();
+ IFACEMETHODIMP_(ULONG) Release();
+ IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput);
+
+ // ID2D1Transform
+ IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect);
+ IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const;
+ IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const;
+
+ // ID2D1TransformNode
+ IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
+
+ // ID2D1DrawTransform
+ IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo);
+
+ static HRESULT Register(ID2D1Factory1* aFactory);
+ static void Unregister(ID2D1Factory1* aFactory);
+ static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
+
+ HRESULT SetStopCollection(IUnknown *aStopCollection);
+ IUnknown *GetStopCollection() const { return mStopCollection; }
+
+private:
+ already_AddRefed<ID2D1ResourceTexture> CreateGradientTexture();
+
+ RadialGradientEffectD2D1();
+
+ uint32_t mRefCount;
+ RefPtr<ID2D1GradientStopCollection> mStopCollection;
+ RefPtr<ID2D1EffectContext> mEffectContext;
+ RefPtr<ID2D1DrawInfo> mDrawInfo;
+ SIMPLE_PROP(D2D1_VECTOR_2F, Center1);
+ SIMPLE_PROP(D2D1_VECTOR_2F, Center2);
+ SIMPLE_PROP(FLOAT, Radius1);
+ SIMPLE_PROP(FLOAT, Radius2);
+ SIMPLE_PROP(D2D_MATRIX_3X2_F, Transform);
+};
+
+}
+}
+#undef SIMPLE_PROP
+
+#endif
diff --git a/system/graphics/2d/RecordedEvent.cpp b/system/graphics/2d/RecordedEvent.cpp
new file mode 100644
index 000000000..e6be60b59
--- /dev/null
+++ b/system/graphics/2d/RecordedEvent.cpp
@@ -0,0 +1,1884 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RecordedEvent.h"
+
+#include "PathRecording.h"
+#include "RecordingTypes.h"
+#include "DrawEventRecorder.h"
+#include "Tools.h"
+#include "Filters.h"
+#include "Logging.h"
+#include "ScaledFontBase.h"
+#include "SFNTData.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+static std::string NameFromBackend(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::NONE:
+ return "None";
+ case BackendType::DIRECT2D:
+ return "Direct2D";
+ default:
+ return "Unknown";
+ }
+}
+
+already_AddRefed<DrawTarget>
+Translator::CreateDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ RefPtr<DrawTarget> newDT =
+ GetReferenceDrawTarget()->CreateSimilarDrawTarget(aSize, aFormat);
+ AddDrawTarget(aRefPtr, newDT);
+ return newDT.forget();
+}
+
+#define LOAD_EVENT_TYPE(_typeenum, _class) \
+ case _typeenum: return new _class(aStream)
+
+RecordedEvent *
+RecordedEvent::LoadEventFromStream(std::istream &aStream, EventType aType)
+{
+ switch (aType) {
+ LOAD_EVENT_TYPE(DRAWTARGETCREATION, RecordedDrawTargetCreation);
+ LOAD_EVENT_TYPE(DRAWTARGETDESTRUCTION, RecordedDrawTargetDestruction);
+ LOAD_EVENT_TYPE(FILLRECT, RecordedFillRect);
+ LOAD_EVENT_TYPE(STROKERECT, RecordedStrokeRect);
+ LOAD_EVENT_TYPE(STROKELINE, RecordedStrokeLine);
+ LOAD_EVENT_TYPE(CLEARRECT, RecordedClearRect);
+ LOAD_EVENT_TYPE(COPYSURFACE, RecordedCopySurface);
+ LOAD_EVENT_TYPE(SETTRANSFORM, RecordedSetTransform);
+ LOAD_EVENT_TYPE(PUSHCLIPRECT, RecordedPushClipRect);
+ LOAD_EVENT_TYPE(PUSHCLIP, RecordedPushClip);
+ LOAD_EVENT_TYPE(POPCLIP, RecordedPopClip);
+ LOAD_EVENT_TYPE(FILL, RecordedFill);
+ LOAD_EVENT_TYPE(FILLGLYPHS, RecordedFillGlyphs);
+ LOAD_EVENT_TYPE(MASK, RecordedMask);
+ LOAD_EVENT_TYPE(STROKE, RecordedStroke);
+ LOAD_EVENT_TYPE(DRAWSURFACE, RecordedDrawSurface);
+ LOAD_EVENT_TYPE(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow);
+ LOAD_EVENT_TYPE(DRAWFILTER, RecordedDrawFilter);
+ LOAD_EVENT_TYPE(PATHCREATION, RecordedPathCreation);
+ LOAD_EVENT_TYPE(PATHDESTRUCTION, RecordedPathDestruction);
+ LOAD_EVENT_TYPE(SOURCESURFACECREATION, RecordedSourceSurfaceCreation);
+ LOAD_EVENT_TYPE(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction);
+ LOAD_EVENT_TYPE(FILTERNODECREATION, RecordedFilterNodeCreation);
+ LOAD_EVENT_TYPE(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction);
+ LOAD_EVENT_TYPE(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation);
+ LOAD_EVENT_TYPE(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction);
+ LOAD_EVENT_TYPE(SNAPSHOT, RecordedSnapshot);
+ LOAD_EVENT_TYPE(SCALEDFONTCREATION, RecordedScaledFontCreation);
+ LOAD_EVENT_TYPE(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction);
+ LOAD_EVENT_TYPE(MASKSURFACE, RecordedMaskSurface);
+ LOAD_EVENT_TYPE(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute);
+ LOAD_EVENT_TYPE(FILTERNODESETINPUT, RecordedFilterNodeSetInput);
+ LOAD_EVENT_TYPE(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget);
+ LOAD_EVENT_TYPE(FONTDATA, RecordedFontData);
+ LOAD_EVENT_TYPE(FONTDESC, RecordedFontDescriptor);
+ LOAD_EVENT_TYPE(PUSHLAYER, RecordedPushLayer);
+ LOAD_EVENT_TYPE(POPLAYER, RecordedPopLayer);
+ default:
+ return nullptr;
+ }
+}
+
+string
+RecordedEvent::GetEventName(EventType aType)
+{
+ switch (aType) {
+ case DRAWTARGETCREATION:
+ return "DrawTarget Creation";
+ case DRAWTARGETDESTRUCTION:
+ return "DrawTarget Destruction";
+ case FILLRECT:
+ return "FillRect";
+ case STROKERECT:
+ return "StrokeRect";
+ case STROKELINE:
+ return "StrokeLine";
+ case CLEARRECT:
+ return "ClearRect";
+ case COPYSURFACE:
+ return "CopySurface";
+ case SETTRANSFORM:
+ return "SetTransform";
+ case PUSHCLIP:
+ return "PushClip";
+ case PUSHCLIPRECT:
+ return "PushClipRect";
+ case POPCLIP:
+ return "PopClip";
+ case FILL:
+ return "Fill";
+ case FILLGLYPHS:
+ return "FillGlyphs";
+ case MASK:
+ return "Mask";
+ case STROKE:
+ return "Stroke";
+ case DRAWSURFACE:
+ return "DrawSurface";
+ case DRAWSURFACEWITHSHADOW:
+ return "DrawSurfaceWithShadow";
+ case DRAWFILTER:
+ return "DrawFilter";
+ case PATHCREATION:
+ return "PathCreation";
+ case PATHDESTRUCTION:
+ return "PathDestruction";
+ case SOURCESURFACECREATION:
+ return "SourceSurfaceCreation";
+ case SOURCESURFACEDESTRUCTION:
+ return "SourceSurfaceDestruction";
+ case FILTERNODECREATION:
+ return "FilterNodeCreation";
+ case FILTERNODEDESTRUCTION:
+ return "FilterNodeDestruction";
+ case GRADIENTSTOPSCREATION:
+ return "GradientStopsCreation";
+ case GRADIENTSTOPSDESTRUCTION:
+ return "GradientStopsDestruction";
+ case SNAPSHOT:
+ return "Snapshot";
+ case SCALEDFONTCREATION:
+ return "ScaledFontCreation";
+ case SCALEDFONTDESTRUCTION:
+ return "ScaledFontDestruction";
+ case MASKSURFACE:
+ return "MaskSurface";
+ case FILTERNODESETATTRIBUTE:
+ return "SetAttribute";
+ case FILTERNODESETINPUT:
+ return "SetInput";
+ case CREATESIMILARDRAWTARGET:
+ return "CreateSimilarDrawTarget";
+ case FONTDATA:
+ return "FontData";
+ case FONTDESC:
+ return "FontDescriptor";
+ case PUSHLAYER:
+ return "PushLayer";
+ case POPLAYER:
+ return "PopLayer";
+ default:
+ return "Unknown";
+ }
+}
+
+void
+RecordedEvent::RecordPatternData(std::ostream &aStream, const PatternStorage &aPattern) const
+{
+ WriteElement(aStream, aPattern.mType);
+
+ switch (aPattern.mType) {
+ case PatternType::COLOR:
+ {
+ WriteElement(aStream, *reinterpret_cast<const ColorPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ WriteElement(aStream, *reinterpret_cast<const LinearGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ WriteElement(aStream, *reinterpret_cast<const RadialGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ WriteElement(aStream, *reinterpret_cast<const SurfacePatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+void
+RecordedEvent::ReadPatternData(std::istream &aStream, PatternStorage &aPattern) const
+{
+ ReadElement(aStream, aPattern.mType);
+
+ switch (aPattern.mType) {
+ case PatternType::COLOR:
+ {
+ ReadElement(aStream, *reinterpret_cast<ColorPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ ReadElement(aStream, *reinterpret_cast<LinearGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ ReadElement(aStream, *reinterpret_cast<RadialGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ ReadElement(aStream, *reinterpret_cast<SurfacePatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+void
+RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource) const
+{
+ aDestination.mType = aSource.GetType();
+
+ switch (aSource.GetType()) {
+ case PatternType::COLOR:
+ {
+ reinterpret_cast<ColorPatternStorage*>(&aDestination.mStorage)->mColor =
+ static_cast<const ColorPattern*>(&aSource)->mColor;
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPatternStorage *store =
+ reinterpret_cast<LinearGradientPatternStorage*>(&aDestination.mStorage);
+ const LinearGradientPattern *pat =
+ static_cast<const LinearGradientPattern*>(&aSource);
+ store->mBegin = pat->mBegin;
+ store->mEnd = pat->mEnd;
+ store->mMatrix = pat->mMatrix;
+ store->mStops = pat->mStops.get();
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPatternStorage *store =
+ reinterpret_cast<RadialGradientPatternStorage*>(&aDestination.mStorage);
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aSource);
+ store->mCenter1 = pat->mCenter1;
+ store->mCenter2 = pat->mCenter2;
+ store->mRadius1 = pat->mRadius1;
+ store->mRadius2 = pat->mRadius2;
+ store->mMatrix = pat->mMatrix;
+ store->mStops = pat->mStops.get();
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ SurfacePatternStorage *store =
+ reinterpret_cast<SurfacePatternStorage*>(&aDestination.mStorage);
+ const SurfacePattern *pat =
+ static_cast<const SurfacePattern*>(&aSource);
+ store->mExtend = pat->mExtendMode;
+ store->mSamplingFilter = pat->mSamplingFilter;
+ store->mMatrix = pat->mMatrix;
+ store->mSurface = pat->mSurface;
+ store->mSamplingRect = pat->mSamplingRect;
+ return;
+ }
+ }
+}
+
+void
+RecordedEvent::RecordStrokeOptions(std::ostream &aStream, const StrokeOptions &aStrokeOptions) const
+{
+ JoinStyle joinStyle = aStrokeOptions.mLineJoin;
+ CapStyle capStyle = aStrokeOptions.mLineCap;
+
+ WriteElement(aStream, uint64_t(aStrokeOptions.mDashLength));
+ WriteElement(aStream, aStrokeOptions.mDashOffset);
+ WriteElement(aStream, aStrokeOptions.mLineWidth);
+ WriteElement(aStream, aStrokeOptions.mMiterLimit);
+ WriteElement(aStream, joinStyle);
+ WriteElement(aStream, capStyle);
+
+ if (!aStrokeOptions.mDashPattern) {
+ return;
+ }
+
+ aStream.write((char*)aStrokeOptions.mDashPattern, sizeof(Float) * aStrokeOptions.mDashLength);
+}
+
+void
+RecordedEvent::ReadStrokeOptions(std::istream &aStream, StrokeOptions &aStrokeOptions)
+{
+ uint64_t dashLength;
+ JoinStyle joinStyle;
+ CapStyle capStyle;
+
+ ReadElement(aStream, dashLength);
+ ReadElement(aStream, aStrokeOptions.mDashOffset);
+ ReadElement(aStream, aStrokeOptions.mLineWidth);
+ ReadElement(aStream, aStrokeOptions.mMiterLimit);
+ ReadElement(aStream, joinStyle);
+ ReadElement(aStream, capStyle);
+ // On 32 bit we truncate the value of dashLength.
+ // See also bug 811850 for history.
+ aStrokeOptions.mDashLength = size_t(dashLength);
+ aStrokeOptions.mLineJoin = joinStyle;
+ aStrokeOptions.mLineCap = capStyle;
+
+ if (!aStrokeOptions.mDashLength) {
+ return;
+ }
+
+ mDashPatternStorage.resize(aStrokeOptions.mDashLength);
+ aStrokeOptions.mDashPattern = &mDashPatternStorage.front();
+ aStream.read((char*)aStrokeOptions.mDashPattern, sizeof(Float) * aStrokeOptions.mDashLength);
+}
+
+void
+RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const
+{
+ switch (aStorage.mType) {
+ case PatternType::COLOR:
+ {
+ const Color color = reinterpret_cast<const ColorPatternStorage*>(&aStorage.mStorage)->mColor;
+ aOutput << "Color: (" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << ")";
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ const LinearGradientPatternStorage *store =
+ reinterpret_cast<const LinearGradientPatternStorage*>(&aStorage.mStorage);
+
+ aOutput << "LinearGradient (" << store->mBegin.x << ", " << store->mBegin.y <<
+ ") - (" << store->mEnd.x << ", " << store->mEnd.y << ") Stops: " << store->mStops;
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ const RadialGradientPatternStorage *store =
+ reinterpret_cast<const RadialGradientPatternStorage*>(&aStorage.mStorage);
+ aOutput << "RadialGradient (Center 1: (" << store->mCenter1.x << ", " <<
+ store->mCenter2.y << ") Radius 2: " << store->mRadius2;
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ const SurfacePatternStorage *store =
+ reinterpret_cast<const SurfacePatternStorage*>(&aStorage.mStorage);
+ aOutput << "Surface (0x" << store->mSurface << ")";
+ return;
+ }
+ }
+}
+
+RecordedDrawingEvent::RecordedDrawingEvent(EventType aType, std::istream &aStream)
+ : RecordedEvent(aType)
+{
+ ReadElement(aStream, mDT);
+}
+
+void
+RecordedDrawingEvent::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mDT);
+}
+
+ReferencePtr
+RecordedDrawingEvent::GetObjectRef() const
+{
+ return mDT;
+}
+
+bool
+RecordedDrawTargetCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<DrawTarget> newDT =
+ aTranslator->CreateDrawTarget(mRefPtr, mSize, mFormat);
+
+ // If we couldn't create a DrawTarget this will probably cause us to crash
+ // with nullptr later in the playback, so return false to abort.
+ if (!newDT) {
+ return false;
+ }
+
+ if (mHasExistingData) {
+ Rect dataRect(0, 0, mExistingData->GetSize().width, mExistingData->GetSize().height);
+ newDT->DrawSurface(mExistingData, dataRect, dataRect);
+ }
+
+ return true;
+}
+
+void
+RecordedDrawTargetCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mBackendType);
+ WriteElement(aStream, mSize);
+ WriteElement(aStream, mFormat);
+ WriteElement(aStream, mHasExistingData);
+
+ if (mHasExistingData) {
+ MOZ_ASSERT(mExistingData);
+ MOZ_ASSERT(mExistingData->GetSize() == mSize);
+ RefPtr<DataSourceSurface> dataSurf = mExistingData->GetDataSurface();
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.write((const char*)dataSurf->GetData() + y * dataSurf->Stride(),
+ BytesPerPixel(mFormat) * mSize.width);
+ }
+ }
+}
+
+RecordedDrawTargetCreation::RecordedDrawTargetCreation(istream &aStream)
+ : RecordedEvent(DRAWTARGETCREATION)
+ , mExistingData(nullptr)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mBackendType);
+ ReadElement(aStream, mSize);
+ ReadElement(aStream, mFormat);
+ ReadElement(aStream, mHasExistingData);
+
+ if (mHasExistingData) {
+ RefPtr<DataSourceSurface> dataSurf = Factory::CreateDataSourceSurface(mSize, mFormat);
+ if (!dataSurf) {
+ gfxWarning() << "RecordedDrawTargetCreation had to reset mHasExistingData";
+ mHasExistingData = false;
+ return;
+ }
+
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.read((char*)dataSurf->GetData() + y * dataSurf->Stride(),
+ BytesPerPixel(mFormat) * mSize.width);
+ }
+ mExistingData = dataSurf;
+ }
+}
+
+void
+RecordedDrawTargetCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] DrawTarget Creation (Type: " << NameFromBackend(mBackendType) << ", Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+
+bool
+RecordedDrawTargetDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveDrawTarget(mRefPtr);
+ return true;
+}
+
+void
+RecordedDrawTargetDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedDrawTargetDestruction::RecordedDrawTargetDestruction(istream &aStream)
+ : RecordedEvent(DRAWTARGETDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedDrawTargetDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] DrawTarget Destruction";
+}
+
+bool
+RecordedCreateSimilarDrawTarget::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<DrawTarget> newDT =
+ aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(mSize, mFormat);
+
+ // If we couldn't create a DrawTarget this will probably cause us to crash
+ // with nullptr later in the playback, so return false to abort.
+ if (!newDT) {
+ return false;
+ }
+
+ aTranslator->AddDrawTarget(mRefPtr, newDT);
+ return true;
+}
+
+void
+RecordedCreateSimilarDrawTarget::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mSize);
+ WriteElement(aStream, mFormat);
+}
+
+RecordedCreateSimilarDrawTarget::RecordedCreateSimilarDrawTarget(istream &aStream)
+ : RecordedEvent(CREATESIMILARDRAWTARGET)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mSize);
+ ReadElement(aStream, mFormat);
+}
+
+void
+RecordedCreateSimilarDrawTarget::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] CreateSimilarDrawTarget (Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+struct GenericPattern
+{
+ GenericPattern(const PatternStorage &aStorage, Translator *aTranslator)
+ : mPattern(nullptr), mTranslator(aTranslator)
+ {
+ mStorage = const_cast<PatternStorage*>(&aStorage);
+ }
+
+ ~GenericPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ operator Pattern*()
+ {
+ switch(mStorage->mType) {
+ case PatternType::COLOR:
+ return new (mColPat) ColorPattern(reinterpret_cast<ColorPatternStorage*>(&mStorage->mStorage)->mColor);
+ case PatternType::SURFACE:
+ {
+ SurfacePatternStorage *storage = reinterpret_cast<SurfacePatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mSurfPat) SurfacePattern(mTranslator->LookupSourceSurface(storage->mSurface),
+ storage->mExtend, storage->mMatrix,
+ storage->mSamplingFilter,
+ storage->mSamplingRect);
+ return mPattern;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPatternStorage *storage = reinterpret_cast<LinearGradientPatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mLinGradPat) LinearGradientPattern(storage->mBegin, storage->mEnd,
+ mTranslator->LookupGradientStops(storage->mStops),
+ storage->mMatrix);
+ return mPattern;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPatternStorage *storage = reinterpret_cast<RadialGradientPatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mRadGradPat) RadialGradientPattern(storage->mCenter1, storage->mCenter2,
+ storage->mRadius1, storage->mRadius2,
+ mTranslator->LookupGradientStops(storage->mStops),
+ storage->mMatrix);
+ return mPattern;
+ }
+ default:
+ return new (mColPat) ColorPattern(Color());
+ }
+
+ return mPattern;
+ }
+
+ union {
+ char mColPat[sizeof(ColorPattern)];
+ char mLinGradPat[sizeof(LinearGradientPattern)];
+ char mRadGradPat[sizeof(RadialGradientPattern)];
+ char mSurfPat[sizeof(SurfacePattern)];
+ };
+
+ PatternStorage *mStorage;
+ Pattern *mPattern;
+ Translator *mTranslator;
+};
+
+bool
+RecordedFillRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->FillRect(mRect, *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+void
+RecordedFillRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+}
+
+RecordedFillRect::RecordedFillRect(istream &aStream)
+ : RecordedDrawingEvent(FILLRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+}
+
+void
+RecordedFillRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] FillRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedStrokeRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->StrokeRect(mRect, *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStrokeRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStrokeRect::RecordedStrokeRect(istream &aStream)
+ : RecordedDrawingEvent(STROKERECT, aStream)
+{
+ ReadElement(aStream, mRect);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStrokeRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] StrokeRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height
+ << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedStrokeLine::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->StrokeLine(mBegin, mEnd, *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStrokeLine::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mBegin);
+ WriteElement(aStream, mEnd);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStrokeLine::RecordedStrokeLine(istream &aStream)
+ : RecordedDrawingEvent(STROKELINE, aStream)
+{
+ ReadElement(aStream, mBegin);
+ ReadElement(aStream, mEnd);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStrokeLine::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] StrokeLine (" << mBegin.x << ", " << mBegin.y << " - " << mEnd.x << ", " << mEnd.y
+ << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedFill::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Fill(aTranslator->LookupPath(mPath), *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+RecordedFill::RecordedFill(istream &aStream)
+ : RecordedDrawingEvent(FILL, aStream)
+{
+ ReadElement(aStream, mPath);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+}
+
+void
+RecordedFill::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+}
+
+void
+RecordedFill::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Fill (" << mPath << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+RecordedFillGlyphs::~RecordedFillGlyphs()
+{
+ delete [] mGlyphs;
+}
+
+bool
+RecordedFillGlyphs::PlayEvent(Translator *aTranslator) const
+{
+ GlyphBuffer buffer;
+ buffer.mGlyphs = mGlyphs;
+ buffer.mNumGlyphs = mNumGlyphs;
+ aTranslator->LookupDrawTarget(mDT)->FillGlyphs(aTranslator->LookupScaledFont(mScaledFont), buffer, *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+RecordedFillGlyphs::RecordedFillGlyphs(istream &aStream)
+ : RecordedDrawingEvent(FILLGLYPHS, aStream)
+{
+ ReadElement(aStream, mScaledFont);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadElement(aStream, mNumGlyphs);
+ mGlyphs = new Glyph[mNumGlyphs];
+ aStream.read((char*)mGlyphs, sizeof(Glyph) * mNumGlyphs);
+}
+
+void
+RecordedFillGlyphs::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mScaledFont);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ WriteElement(aStream, mNumGlyphs);
+ aStream.write((char*)mGlyphs, sizeof(Glyph) * mNumGlyphs);
+}
+
+void
+RecordedFillGlyphs::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] FillGlyphs (" << mScaledFont << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedMask::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Mask(*GenericPattern(mSource, aTranslator), *GenericPattern(mMask, aTranslator), mOptions);
+ return true;
+}
+
+RecordedMask::RecordedMask(istream &aStream)
+ : RecordedDrawingEvent(MASK, aStream)
+{
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mSource);
+ ReadPatternData(aStream, mMask);
+}
+
+void
+RecordedMask::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mSource);
+ RecordPatternData(aStream, mMask);
+}
+
+void
+RecordedMask::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Mask (Source: ";
+ OutputSimplePatternInfo(mSource, aStringStream);
+ aStringStream << " Mask: ";
+ OutputSimplePatternInfo(mMask, aStringStream);
+}
+
+bool
+RecordedStroke::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Stroke(aTranslator->LookupPath(mPath), *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStroke::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStroke::RecordedStroke(istream &aStream)
+ : RecordedDrawingEvent(STROKE, aStream)
+{
+ ReadElement(aStream, mPath);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStroke::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Stroke ("<< mPath << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedClearRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->ClearRect(mRect);
+ return true;
+}
+
+void
+RecordedClearRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+}
+
+RecordedClearRect::RecordedClearRect(istream &aStream)
+ : RecordedDrawingEvent(CLEARRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+}
+
+void
+RecordedClearRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT<< "] ClearRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+}
+
+bool
+RecordedCopySurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->CopySurface(aTranslator->LookupSourceSurface(mSourceSurface),
+ mSourceRect, mDest);
+ return true;
+}
+
+void
+RecordedCopySurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mSourceSurface);
+ WriteElement(aStream, mSourceRect);
+ WriteElement(aStream, mDest);
+}
+
+RecordedCopySurface::RecordedCopySurface(istream &aStream)
+ : RecordedDrawingEvent(COPYSURFACE, aStream)
+{
+ ReadElement(aStream, mSourceSurface);
+ ReadElement(aStream, mSourceRect);
+ ReadElement(aStream, mDest);
+}
+
+void
+RecordedCopySurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT<< "] CopySurface (" << mSourceSurface << ")";
+}
+
+bool
+RecordedPushClip::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PushClip(aTranslator->LookupPath(mPath));
+ return true;
+}
+
+void
+RecordedPushClip::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+}
+
+RecordedPushClip::RecordedPushClip(istream &aStream)
+ : RecordedDrawingEvent(PUSHCLIP, aStream)
+{
+ ReadElement(aStream, mPath);
+}
+
+void
+RecordedPushClip::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushClip (" << mPath << ") ";
+}
+
+bool
+RecordedPushClipRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PushClipRect(mRect);
+ return true;
+}
+
+void
+RecordedPushClipRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+}
+
+RecordedPushClipRect::RecordedPushClipRect(istream &aStream)
+ : RecordedDrawingEvent(PUSHCLIPRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+}
+
+void
+RecordedPushClipRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushClipRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+}
+
+bool
+RecordedPopClip::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PopClip();
+ return true;
+}
+
+void
+RecordedPopClip::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+}
+
+RecordedPopClip::RecordedPopClip(istream &aStream)
+ : RecordedDrawingEvent(POPCLIP, aStream)
+{
+}
+
+void
+RecordedPopClip::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PopClip";
+}
+
+bool
+RecordedPushLayer::PlayEvent(Translator *aTranslator) const
+{
+ SourceSurface* mask = mMask ? aTranslator->LookupSourceSurface(mMask)
+ : nullptr;
+ aTranslator->LookupDrawTarget(mDT)->
+ PushLayer(mOpaque, mOpacity, mask, mMaskTransform, mBounds, mCopyBackground);
+ return true;
+}
+
+void
+RecordedPushLayer::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mOpaque);
+ WriteElement(aStream, mOpacity);
+ WriteElement(aStream, mMask);
+ WriteElement(aStream, mMaskTransform);
+ WriteElement(aStream, mBounds);
+ WriteElement(aStream, mCopyBackground);
+}
+
+RecordedPushLayer::RecordedPushLayer(istream &aStream)
+ : RecordedDrawingEvent(PUSHLAYER, aStream)
+{
+ ReadElement(aStream, mOpaque);
+ ReadElement(aStream, mOpacity);
+ ReadElement(aStream, mMask);
+ ReadElement(aStream, mMaskTransform);
+ ReadElement(aStream, mBounds);
+ ReadElement(aStream, mCopyBackground);
+}
+
+void
+RecordedPushLayer::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushPLayer (Opaque=" << mOpaque <<
+ ", Opacity=" << mOpacity << ", Mask Ref=" << mMask << ") ";
+}
+
+bool
+RecordedPopLayer::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PopLayer();
+ return true;
+}
+
+void
+RecordedPopLayer::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+}
+
+RecordedPopLayer::RecordedPopLayer(istream &aStream)
+ : RecordedDrawingEvent(POPLAYER, aStream)
+{
+}
+
+void
+RecordedPopLayer::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PopLayer";
+}
+
+bool
+RecordedSetTransform::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->SetTransform(mTransform);
+ return true;
+}
+
+void
+RecordedSetTransform::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mTransform);
+}
+
+RecordedSetTransform::RecordedSetTransform(istream &aStream)
+ : RecordedDrawingEvent(SETTRANSFORM, aStream)
+{
+ ReadElement(aStream, mTransform);
+}
+
+void
+RecordedSetTransform::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] SetTransform [ " << mTransform._11 << " " << mTransform._12 << " ; " <<
+ mTransform._21 << " " << mTransform._22 << " ; " << mTransform._31 << " " << mTransform._32 << " ]";
+}
+
+bool
+RecordedDrawSurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawSurface(aTranslator->LookupSourceSurface(mRefSource), mDest, mSource,
+ mDSOptions, mOptions);
+ return true;
+}
+
+void
+RecordedDrawSurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRefSource);
+ WriteElement(aStream, mDest);
+ WriteElement(aStream, mSource);
+ WriteElement(aStream, mDSOptions);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedDrawSurface::RecordedDrawSurface(istream &aStream)
+ : RecordedDrawingEvent(DRAWSURFACE, aStream)
+{
+ ReadElement(aStream, mRefSource);
+ ReadElement(aStream, mDest);
+ ReadElement(aStream, mSource);
+ ReadElement(aStream, mDSOptions);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedDrawSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawSurface (" << mRefSource << ")";
+}
+
+bool
+RecordedDrawFilter::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawFilter(aTranslator->LookupFilterNode(mNode), mSourceRect,
+ mDestPoint, mOptions);
+ return true;
+}
+
+void
+RecordedDrawFilter::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mSourceRect);
+ WriteElement(aStream, mDestPoint);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedDrawFilter::RecordedDrawFilter(istream &aStream)
+ : RecordedDrawingEvent(DRAWFILTER, aStream)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mSourceRect);
+ ReadElement(aStream, mDestPoint);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedDrawFilter::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawFilter (" << mNode << ")";
+}
+
+bool
+RecordedDrawSurfaceWithShadow::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawSurfaceWithShadow(aTranslator->LookupSourceSurface(mRefSource),
+ mDest, mColor, mOffset, mSigma, mOp);
+ return true;
+}
+
+void
+RecordedDrawSurfaceWithShadow::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRefSource);
+ WriteElement(aStream, mDest);
+ WriteElement(aStream, mColor);
+ WriteElement(aStream, mOffset);
+ WriteElement(aStream, mSigma);
+ WriteElement(aStream, mOp);
+}
+
+RecordedDrawSurfaceWithShadow::RecordedDrawSurfaceWithShadow(istream &aStream)
+ : RecordedDrawingEvent(DRAWSURFACEWITHSHADOW, aStream)
+{
+ ReadElement(aStream, mRefSource);
+ ReadElement(aStream, mDest);
+ ReadElement(aStream, mColor);
+ ReadElement(aStream, mOffset);
+ ReadElement(aStream, mSigma);
+ ReadElement(aStream, mOp);
+}
+
+void
+RecordedDrawSurfaceWithShadow::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawSurfaceWithShadow (" << mRefSource << ") Color: (" <<
+ mColor.r << ", " << mColor.g << ", " << mColor.b << ", " << mColor.a << ")";
+}
+
+RecordedPathCreation::RecordedPathCreation(PathRecording *aPath)
+ : RecordedEvent(PATHCREATION), mRefPtr(aPath), mFillRule(aPath->mFillRule), mPathOps(aPath->mPathOps)
+{
+}
+
+RecordedPathCreation::~RecordedPathCreation()
+{
+}
+
+bool
+RecordedPathCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<PathBuilder> builder =
+ aTranslator->GetReferenceDrawTarget()->CreatePathBuilder(mFillRule);
+
+ for (size_t i = 0; i < mPathOps.size(); i++) {
+ const PathOp &op = mPathOps[i];
+ switch (op.mType) {
+ case PathOp::OP_MOVETO:
+ builder->MoveTo(op.mP1);
+ break;
+ case PathOp::OP_LINETO:
+ builder->LineTo(op.mP1);
+ break;
+ case PathOp::OP_BEZIERTO:
+ builder->BezierTo(op.mP1, op.mP2, op.mP3);
+ break;
+ case PathOp::OP_QUADRATICBEZIERTO:
+ builder->QuadraticBezierTo(op.mP1, op.mP2);
+ break;
+ case PathOp::OP_CLOSE:
+ builder->Close();
+ break;
+ }
+ }
+
+ RefPtr<Path> path = builder->Finish();
+ aTranslator->AddPath(mRefPtr, path);
+ return true;
+}
+
+void
+RecordedPathCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, uint64_t(mPathOps.size()));
+ WriteElement(aStream, mFillRule);
+ typedef std::vector<PathOp> pathOpVec;
+ for (pathOpVec::const_iterator iter = mPathOps.begin(); iter != mPathOps.end(); iter++) {
+ WriteElement(aStream, iter->mType);
+ if (sPointCount[iter->mType] >= 1) {
+ WriteElement(aStream, iter->mP1);
+ }
+ if (sPointCount[iter->mType] >= 2) {
+ WriteElement(aStream, iter->mP2);
+ }
+ if (sPointCount[iter->mType] >= 3) {
+ WriteElement(aStream, iter->mP3);
+ }
+ }
+
+}
+
+RecordedPathCreation::RecordedPathCreation(istream &aStream)
+ : RecordedEvent(PATHCREATION)
+{
+ uint64_t size;
+
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, size);
+ ReadElement(aStream, mFillRule);
+
+ for (uint64_t i = 0; i < size; i++) {
+ PathOp newPathOp;
+ ReadElement(aStream, newPathOp.mType);
+ if (sPointCount[newPathOp.mType] >= 1) {
+ ReadElement(aStream, newPathOp.mP1);
+ }
+ if (sPointCount[newPathOp.mType] >= 2) {
+ ReadElement(aStream, newPathOp.mP2);
+ }
+ if (sPointCount[newPathOp.mType] >= 3) {
+ ReadElement(aStream, newPathOp.mP3);
+ }
+
+ mPathOps.push_back(newPathOp);
+ }
+
+}
+
+void
+RecordedPathCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Path created (OpCount: " << mPathOps.size() << ")";
+}
+bool
+RecordedPathDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemovePath(mRefPtr);
+ return true;
+}
+
+void
+RecordedPathDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedPathDestruction::RecordedPathDestruction(istream &aStream)
+ : RecordedEvent(PATHDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedPathDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Path Destroyed";
+}
+
+RecordedSourceSurfaceCreation::~RecordedSourceSurfaceCreation()
+{
+ if (mDataOwned) {
+ delete [] mData;
+ }
+}
+
+bool
+RecordedSourceSurfaceCreation::PlayEvent(Translator *aTranslator) const
+{
+ if (!mData) {
+ return false;
+ }
+
+ RefPtr<SourceSurface> src = aTranslator->GetReferenceDrawTarget()->
+ CreateSourceSurfaceFromData(mData, mSize, mSize.width * BytesPerPixel(mFormat), mFormat);
+ aTranslator->AddSourceSurface(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedSourceSurfaceCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mSize);
+ WriteElement(aStream, mFormat);
+ MOZ_ASSERT(mData);
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.write((const char*)mData + y * mStride, BytesPerPixel(mFormat) * mSize.width);
+ }
+}
+
+RecordedSourceSurfaceCreation::RecordedSourceSurfaceCreation(istream &aStream)
+ : RecordedEvent(SOURCESURFACECREATION), mDataOwned(true)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mSize);
+ ReadElement(aStream, mFormat);
+ mData = (uint8_t*)new (fallible) char[mSize.width * mSize.height * BytesPerPixel(mFormat)];
+ if (!mData) {
+ gfxWarning() << "RecordedSourceSurfaceCreation failed to allocate data";
+ } else {
+ aStream.read((char*)mData, mSize.width * mSize.height * BytesPerPixel(mFormat));
+ }
+}
+
+void
+RecordedSourceSurfaceCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] SourceSurface created (Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+bool
+RecordedSourceSurfaceDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveSourceSurface(mRefPtr);
+ return true;
+}
+
+void
+RecordedSourceSurfaceDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedSourceSurfaceDestruction::RecordedSourceSurfaceDestruction(istream &aStream)
+ : RecordedEvent(SOURCESURFACEDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedSourceSurfaceDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] SourceSurface Destroyed";
+}
+
+RecordedFilterNodeCreation::~RecordedFilterNodeCreation()
+{
+}
+
+bool
+RecordedFilterNodeCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<FilterNode> node = aTranslator->GetReferenceDrawTarget()->
+ CreateFilter(mType);
+ aTranslator->AddFilterNode(mRefPtr, node);
+ return true;
+}
+
+void
+RecordedFilterNodeCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mType);
+}
+
+RecordedFilterNodeCreation::RecordedFilterNodeCreation(istream &aStream)
+ : RecordedEvent(FILTERNODECREATION)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mType);
+}
+
+void
+RecordedFilterNodeCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] FilterNode created (Type: " << int(mType) << ")";
+}
+
+bool
+RecordedFilterNodeDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveFilterNode(mRefPtr);
+ return true;
+}
+
+void
+RecordedFilterNodeDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedFilterNodeDestruction::RecordedFilterNodeDestruction(istream &aStream)
+ : RecordedEvent(FILTERNODEDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedFilterNodeDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] FilterNode Destroyed";
+}
+
+RecordedGradientStopsCreation::~RecordedGradientStopsCreation()
+{
+ if (mDataOwned) {
+ delete [] mStops;
+ }
+}
+
+bool
+RecordedGradientStopsCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<GradientStops> src = aTranslator->GetReferenceDrawTarget()->
+ CreateGradientStops(mStops, mNumStops, mExtendMode);
+ aTranslator->AddGradientStops(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedGradientStopsCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mExtendMode);
+ WriteElement(aStream, mNumStops);
+ aStream.write((const char*)mStops, mNumStops * sizeof(GradientStop));
+}
+
+RecordedGradientStopsCreation::RecordedGradientStopsCreation(istream &aStream)
+ : RecordedEvent(GRADIENTSTOPSCREATION), mDataOwned(true)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mExtendMode);
+ ReadElement(aStream, mNumStops);
+ mStops = new GradientStop[mNumStops];
+
+ aStream.read((char*)mStops, mNumStops * sizeof(GradientStop));
+}
+
+void
+RecordedGradientStopsCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] GradientStops created (Stops: " << mNumStops << ")";
+}
+
+bool
+RecordedGradientStopsDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveGradientStops(mRefPtr);
+ return true;
+}
+
+void
+RecordedGradientStopsDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedGradientStopsDestruction::RecordedGradientStopsDestruction(istream &aStream)
+ : RecordedEvent(GRADIENTSTOPSDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedGradientStopsDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] GradientStops Destroyed";
+}
+
+bool
+RecordedSnapshot::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<SourceSurface> src = aTranslator->LookupDrawTarget(mDT)->Snapshot();
+ aTranslator->AddSourceSurface(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedSnapshot::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mDT);
+}
+
+RecordedSnapshot::RecordedSnapshot(istream &aStream)
+ : RecordedEvent(SNAPSHOT)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mDT);
+}
+
+void
+RecordedSnapshot::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Snapshot Created (DT: " << mDT << ")";
+}
+
+RecordedFontData::~RecordedFontData()
+{
+ delete[] mData;
+}
+
+bool
+RecordedFontData::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<NativeFontResource> fontResource =
+ Factory::CreateNativeFontResource(mData, mFontDetails.size,
+ aTranslator->GetDesiredFontType());
+ if (!fontResource) {
+ return false;
+ }
+
+ aTranslator->AddNativeFontResource(mFontDetails.fontDataKey, fontResource);
+ return true;
+}
+
+void
+RecordedFontData::RecordToStream(std::ostream &aStream) const
+{
+ MOZ_ASSERT(mGetFontFileDataSucceeded);
+
+ WriteElement(aStream, mFontDetails.fontDataKey);
+ WriteElement(aStream, mFontDetails.size);
+ aStream.write((const char*)mData, mFontDetails.size);
+}
+
+void
+RecordedFontData::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "Font Data of size " << mFontDetails.size;
+}
+
+void
+RecordedFontData::SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize)
+{
+ mData = new uint8_t[aSize];
+ memcpy(mData, aData, aSize);
+ mFontDetails.fontDataKey = SFNTData::GetUniqueKey(aData, aSize);
+ mFontDetails.size = aSize;
+ mFontDetails.index = aIndex;
+ mFontDetails.glyphSize = aGlyphSize;
+}
+
+bool
+RecordedFontData::GetFontDetails(RecordedFontDetails& fontDetails)
+{
+ if (!mGetFontFileDataSucceeded) {
+ return false;
+ }
+
+ fontDetails.fontDataKey = mFontDetails.fontDataKey;
+ fontDetails.size = mFontDetails.size;
+ fontDetails.glyphSize = mFontDetails.glyphSize;
+ fontDetails.index = mFontDetails.index;
+ return true;
+}
+
+RecordedFontData::RecordedFontData(istream &aStream)
+ : RecordedEvent(FONTDATA)
+{
+ ReadElement(aStream, mFontDetails.fontDataKey);
+ ReadElement(aStream, mFontDetails.size);
+ mData = new uint8_t[mFontDetails.size];
+ aStream.read((char*)mData, mFontDetails.size);
+}
+
+RecordedFontDescriptor::~RecordedFontDescriptor()
+{
+}
+
+bool
+RecordedFontDescriptor::PlayEvent(Translator *aTranslator) const
+{
+ MOZ_ASSERT(mType == FontType::GDI);
+
+ NativeFont nativeFont;
+ nativeFont.mType = (NativeFontType)mType;
+ nativeFont.mFont = (void*)&mData[0];
+
+ RefPtr<ScaledFont> font =
+ Factory::CreateScaledFontForNativeFont(nativeFont, mFontSize);
+
+#ifdef USE_CAIRO_SCALED_FONT
+ static_cast<ScaledFontBase*>(font.get())->PopulateCairoScaledFont();
+#endif
+
+ aTranslator->AddScaledFont(mRefPtr, font);
+ return true;
+}
+
+void
+RecordedFontDescriptor::RecordToStream(std::ostream &aStream) const
+{
+ MOZ_ASSERT(mHasDesc);
+ WriteElement(aStream, mType);
+ WriteElement(aStream, mFontSize);
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, (size_t)mData.size());
+ aStream.write((char*)&mData[0], mData.size());
+}
+
+void
+RecordedFontDescriptor::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Font Descriptor";
+}
+
+void
+RecordedFontDescriptor::SetFontDescriptor(const uint8_t* aData, uint32_t aSize, Float aFontSize)
+{
+ mData.assign(aData, aData + aSize);
+ mFontSize = aFontSize;
+}
+
+RecordedFontDescriptor::RecordedFontDescriptor(istream &aStream)
+ : RecordedEvent(FONTDATA)
+{
+ ReadElement(aStream, mType);
+ ReadElement(aStream, mFontSize);
+ ReadElement(aStream, mRefPtr);
+
+ size_t size;
+ ReadElement(aStream, size);
+ mData.resize(size);
+ aStream.read((char*)&mData[0], size);
+}
+
+bool
+RecordedScaledFontCreation::PlayEvent(Translator *aTranslator) const
+{
+ NativeFontResource *fontResource = aTranslator->LookupNativeFontResource(mFontDataKey);
+ if (!fontResource) {
+ gfxDevCrash(LogReason::NativeFontResourceNotFound) <<
+ "NativeFontResource lookup failed for key |" << hexa(mFontDataKey) << "|.";
+ return false;
+ }
+
+ RefPtr<ScaledFont> scaledFont =
+ fontResource->CreateScaledFont(mIndex, mGlyphSize, mInstanceData.data(), mInstanceData.size());
+ aTranslator->AddScaledFont(mRefPtr, scaledFont);
+ return true;
+}
+
+void
+RecordedScaledFontCreation::RecordToStream(std::ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mFontDataKey);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mGlyphSize);
+ WriteElement(aStream, (size_t)mInstanceData.size());
+ aStream.write((char*)mInstanceData.data(), mInstanceData.size());
+}
+
+void
+RecordedScaledFontCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] ScaledFont Created";
+}
+
+void
+RecordedScaledFontCreation::SetFontInstanceData(const uint8_t *aData, uint32_t aSize)
+{
+ mInstanceData.assign(aData, aData + aSize);
+}
+
+RecordedScaledFontCreation::RecordedScaledFontCreation(istream &aStream)
+ : RecordedEvent(SCALEDFONTCREATION)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mFontDataKey);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mGlyphSize);
+
+ size_t size;
+ ReadElement(aStream, size);
+ mInstanceData.resize(size);
+ aStream.read((char*)mInstanceData.data(), size);
+}
+
+bool
+RecordedScaledFontDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveScaledFont(mRefPtr);
+ return true;
+}
+
+void
+RecordedScaledFontDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedScaledFontDestruction::RecordedScaledFontDestruction(istream &aStream)
+ : RecordedEvent(SCALEDFONTDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedScaledFontDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] ScaledFont Destroyed";
+}
+
+bool
+RecordedMaskSurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ MaskSurface(*GenericPattern(mPattern, aTranslator),
+ aTranslator->LookupSourceSurface(mRefMask),
+ mOffset, mOptions);
+ return true;
+}
+
+void
+RecordedMaskSurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ RecordPatternData(aStream, mPattern);
+ WriteElement(aStream, mRefMask);
+ WriteElement(aStream, mOffset);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedMaskSurface::RecordedMaskSurface(istream &aStream)
+ : RecordedDrawingEvent(MASKSURFACE, aStream)
+{
+ ReadPatternData(aStream, mPattern);
+ ReadElement(aStream, mRefMask);
+ ReadElement(aStream, mOffset);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedMaskSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] MaskSurface (" << mRefMask << ") Offset: (" << mOffset.x << "x" << mOffset.y << ") Pattern: ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+template<typename T>
+void
+ReplaySetAttribute(FilterNode *aNode, uint32_t aIndex, T aValue)
+{
+ aNode->SetAttribute(aIndex, aValue);
+}
+
+bool
+RecordedFilterNodeSetAttribute::PlayEvent(Translator *aTranslator) const
+{
+#define REPLAY_SET_ATTRIBUTE(type, argtype) \
+ case ARGTYPE_##argtype: \
+ ReplaySetAttribute(aTranslator->LookupFilterNode(mNode), mIndex, *(type*)&mPayload.front()); \
+ break
+
+ switch (mArgType) {
+ REPLAY_SET_ATTRIBUTE(bool, BOOL);
+ REPLAY_SET_ATTRIBUTE(uint32_t, UINT32);
+ REPLAY_SET_ATTRIBUTE(Float, FLOAT);
+ REPLAY_SET_ATTRIBUTE(Size, SIZE);
+ REPLAY_SET_ATTRIBUTE(IntSize, INTSIZE);
+ REPLAY_SET_ATTRIBUTE(IntPoint, INTPOINT);
+ REPLAY_SET_ATTRIBUTE(Rect, RECT);
+ REPLAY_SET_ATTRIBUTE(IntRect, INTRECT);
+ REPLAY_SET_ATTRIBUTE(Point, POINT);
+ REPLAY_SET_ATTRIBUTE(Matrix, MATRIX);
+ REPLAY_SET_ATTRIBUTE(Matrix5x4, MATRIX5X4);
+ REPLAY_SET_ATTRIBUTE(Point3D, POINT3D);
+ REPLAY_SET_ATTRIBUTE(Color, COLOR);
+ case ARGTYPE_FLOAT_ARRAY:
+ aTranslator->LookupFilterNode(mNode)->SetAttribute(
+ mIndex,
+ reinterpret_cast<const Float*>(&mPayload.front()),
+ mPayload.size() / sizeof(Float));
+ break;
+ }
+
+ return true;
+}
+
+void
+RecordedFilterNodeSetAttribute::RecordToStream(ostream &aStream) const
+{
+ RecordedEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mArgType);
+ WriteElement(aStream, uint64_t(mPayload.size()));
+ aStream.write((const char*)&mPayload.front(), mPayload.size());
+}
+
+RecordedFilterNodeSetAttribute::RecordedFilterNodeSetAttribute(istream &aStream)
+ : RecordedEvent(FILTERNODESETATTRIBUTE)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mArgType);
+ uint64_t size;
+ ReadElement(aStream, size);
+ mPayload.resize(size_t(size));
+ aStream.read((char*)&mPayload.front(), size);
+}
+
+void
+RecordedFilterNodeSetAttribute::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ")";
+}
+
+bool
+RecordedFilterNodeSetInput::PlayEvent(Translator *aTranslator) const
+{
+ if (mInputFilter) {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupFilterNode(mInputFilter));
+ } else {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupSourceSurface(mInputSurface));
+ }
+
+ return true;
+}
+
+void
+RecordedFilterNodeSetInput::RecordToStream(ostream &aStream) const
+{
+ RecordedEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mInputFilter);
+ WriteElement(aStream, mInputSurface);
+}
+
+RecordedFilterNodeSetInput::RecordedFilterNodeSetInput(istream &aStream)
+ : RecordedEvent(FILTERNODESETINPUT)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mInputFilter);
+ ReadElement(aStream, mInputSurface);
+}
+
+void
+RecordedFilterNodeSetInput::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ", ";
+
+ if (mInputFilter) {
+ aStringStream << "Filter: " << mInputFilter;
+ } else {
+ aStringStream << "Surface: " << mInputSurface;
+ }
+
+ aStringStream << ")";
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/RecordedEvent.h b/system/graphics/2d/RecordedEvent.h
new file mode 100644
index 000000000..bf660ba24
--- /dev/null
+++ b/system/graphics/2d/RecordedEvent.h
@@ -0,0 +1,1281 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RECORDEDEVENT_H_
+#define MOZILLA_GFX_RECORDEDEVENT_H_
+
+#include "2D.h"
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct PathOp;
+class PathRecording;
+
+const uint32_t kMagicInt = 0xc001feed;
+
+// A change in major revision means a change in event binary format, causing
+// loss of backwards compatibility. Old streams will not work in a player
+// using a newer major revision. And new streams will not work in a player
+// using an older major revision.
+const uint16_t kMajorRevision = 6;
+// A change in minor revision means additions of new events. New streams will
+// not play in older players.
+const uint16_t kMinorRevision = 0;
+
+struct ReferencePtr
+{
+ ReferencePtr()
+ : mLongPtr(0)
+ {}
+
+ MOZ_IMPLICIT ReferencePtr(const void* aLongPtr)
+ : mLongPtr(uint64_t(aLongPtr))
+ {}
+
+ template <typename T>
+ MOZ_IMPLICIT ReferencePtr(const RefPtr<T>& aPtr)
+ : mLongPtr(uint64_t(aPtr.get()))
+ {}
+
+ ReferencePtr &operator =(const void* aLongPtr) {
+ mLongPtr = uint64_t(aLongPtr);
+ return *this;
+ }
+
+ template <typename T>
+ ReferencePtr &operator =(const RefPtr<T>& aPtr) {
+ mLongPtr = uint64_t(aPtr.get());
+ return *this;
+ }
+
+ operator void*() const {
+ return (void*)mLongPtr;
+ }
+
+ uint64_t mLongPtr;
+};
+
+struct RecordedFontDetails
+{
+ uint64_t fontDataKey;
+ uint32_t size;
+ uint32_t index;
+ Float glyphSize;
+};
+
+// Used by the Azure drawing debugger (player2d)
+inline std::string StringFromPtr(ReferencePtr aPtr)
+{
+ std::stringstream stream;
+ stream << aPtr;
+ return stream.str();
+}
+
+class Translator
+{
+public:
+ virtual ~Translator() {}
+
+ virtual DrawTarget *LookupDrawTarget(ReferencePtr aRefPtr) = 0;
+ virtual Path *LookupPath(ReferencePtr aRefPtr) = 0;
+ virtual SourceSurface *LookupSourceSurface(ReferencePtr aRefPtr) = 0;
+ virtual FilterNode *LookupFilterNode(ReferencePtr aRefPtr) = 0;
+ virtual GradientStops *LookupGradientStops(ReferencePtr aRefPtr) = 0;
+ virtual ScaledFont *LookupScaledFont(ReferencePtr aRefPtr) = 0;
+ virtual NativeFontResource *LookupNativeFontResource(uint64_t aKey) = 0;
+ virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0;
+ virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0;
+ virtual void AddPath(ReferencePtr aRefPtr, Path *aPath) = 0;
+ virtual void RemovePath(ReferencePtr aRefPtr) = 0;
+ virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aPath) = 0;
+ virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0;
+ virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr, FilterNode *aSurface) = 0;
+ virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0;
+ virtual void AddGradientStops(ReferencePtr aRefPtr, GradientStops *aPath) = 0;
+ virtual void RemoveGradientStops(ReferencePtr aRefPtr) = 0;
+ virtual void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) = 0;
+ virtual void RemoveScaledFont(ReferencePtr aRefPtr) = 0;
+ virtual void AddNativeFontResource(uint64_t aKey,
+ NativeFontResource *aNativeFontResource) = 0;
+
+ virtual already_AddRefed<DrawTarget> CreateDrawTarget(ReferencePtr aRefPtr,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+ virtual DrawTarget *GetReferenceDrawTarget() = 0;
+ virtual FontType GetDesiredFontType() = 0;
+};
+
+struct ColorPatternStorage
+{
+ Color mColor;
+};
+
+struct LinearGradientPatternStorage
+{
+ Point mBegin;
+ Point mEnd;
+ ReferencePtr mStops;
+ Matrix mMatrix;
+};
+
+struct RadialGradientPatternStorage
+{
+ Point mCenter1;
+ Point mCenter2;
+ Float mRadius1;
+ Float mRadius2;
+ ReferencePtr mStops;
+ Matrix mMatrix;
+};
+
+struct SurfacePatternStorage
+{
+ ExtendMode mExtend;
+ SamplingFilter mSamplingFilter;
+ ReferencePtr mSurface;
+ Matrix mMatrix;
+ IntRect mSamplingRect;
+};
+
+struct PatternStorage
+{
+ PatternType mType;
+ union {
+ char *mStorage;
+ char mColor[sizeof(ColorPatternStorage)];
+ char mLinear[sizeof(LinearGradientPatternStorage)];
+ char mRadial[sizeof(RadialGradientPatternStorage)];
+ char mSurface[sizeof(SurfacePatternStorage)];
+ };
+};
+
+class RecordedEvent {
+public:
+ enum EventType {
+ DRAWTARGETCREATION = 0,
+ DRAWTARGETDESTRUCTION,
+ FILLRECT,
+ STROKERECT,
+ STROKELINE,
+ CLEARRECT,
+ COPYSURFACE,
+ SETTRANSFORM,
+ PUSHCLIP,
+ PUSHCLIPRECT,
+ POPCLIP,
+ FILL,
+ FILLGLYPHS,
+ MASK,
+ STROKE,
+ DRAWSURFACE,
+ DRAWSURFACEWITHSHADOW,
+ PATHCREATION,
+ PATHDESTRUCTION,
+ SOURCESURFACECREATION,
+ SOURCESURFACEDESTRUCTION,
+ GRADIENTSTOPSCREATION,
+ GRADIENTSTOPSDESTRUCTION,
+ SNAPSHOT,
+ SCALEDFONTCREATION,
+ SCALEDFONTDESTRUCTION,
+ MASKSURFACE,
+ FILTERNODECREATION,
+ FILTERNODEDESTRUCTION,
+ DRAWFILTER,
+ FILTERNODESETATTRIBUTE,
+ FILTERNODESETINPUT,
+ CREATESIMILARDRAWTARGET,
+ FONTDATA,
+ FONTDESC,
+ PUSHLAYER,
+ POPLAYER,
+ };
+ static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1;
+
+ virtual ~RecordedEvent() {}
+
+ static std::string GetEventName(EventType aType);
+
+ /**
+ * Play back this event using the translator. Note that derived classes should
+ * only return false when there is a fatal error, as it will probably mean the
+ * translation will abort.
+ * @param aTranslator Translator to be used for retrieving other referenced
+ * objects and making playback decisions.
+ * @return true unless a fatal problem has occurred and playback should abort.
+ */
+ virtual bool PlayEvent(Translator *aTranslator) const { return true; }
+
+ virtual void RecordToStream(std::ostream &aStream) const {}
+
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const { }
+
+ void RecordPatternData(std::ostream &aStream, const PatternStorage &aPatternStorage) const;
+ void ReadPatternData(std::istream &aStream, PatternStorage &aPatternStorage) const;
+ void StorePattern(PatternStorage &aDestination, const Pattern &aSource) const;
+ void RecordStrokeOptions(std::ostream &aStream, const StrokeOptions &aStrokeOptions) const;
+ void ReadStrokeOptions(std::istream &aStream, StrokeOptions &aStrokeOptions);
+
+ virtual std::string GetName() const = 0;
+
+ virtual ReferencePtr GetObjectRef() const = 0;
+
+ virtual ReferencePtr GetDestinedDT() { return nullptr; }
+
+ void OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const;
+
+ static RecordedEvent *LoadEventFromStream(std::istream &aStream, EventType aType);
+
+ EventType GetType() { return (EventType)mType; }
+protected:
+ friend class DrawEventRecorderPrivate;
+
+ MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType)
+ {}
+
+ int32_t mType;
+ std::vector<Float> mDashPatternStorage;
+};
+
+class RecordedDrawingEvent : public RecordedEvent
+{
+public:
+ virtual ReferencePtr GetDestinedDT() { return mDT; }
+
+protected:
+ RecordedDrawingEvent(EventType aType, DrawTarget *aTarget)
+ : RecordedEvent(aType), mDT(aTarget)
+ {
+ }
+
+ RecordedDrawingEvent(EventType aType, std::istream &aStream);
+ virtual void RecordToStream(std::ostream &aStream) const;
+
+ virtual ReferencePtr GetObjectRef() const;
+
+ ReferencePtr mDT;
+};
+
+class RecordedDrawTargetCreation : public RecordedEvent {
+public:
+ RecordedDrawTargetCreation(ReferencePtr aRefPtr, BackendType aType, const IntSize &aSize, SurfaceFormat aFormat,
+ bool aHasExistingData = false, SourceSurface *aExistingData = nullptr)
+ : RecordedEvent(DRAWTARGETCREATION), mRefPtr(aRefPtr), mBackendType(aType), mSize(aSize), mFormat(aFormat)
+ , mHasExistingData(aHasExistingData), mExistingData(aExistingData)
+ {}
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawTarget Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+ BackendType mBackendType;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ bool mHasExistingData;
+ RefPtr<SourceSurface> mExistingData;
+
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawTargetCreation(std::istream &aStream);
+};
+
+class RecordedDrawTargetDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(DRAWTARGETDESTRUCTION), mRefPtr(aRefPtr)
+ {}
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawTarget Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+
+ BackendType mBackendType;
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(std::istream &aStream);
+};
+
+class RecordedCreateSimilarDrawTarget : public RecordedEvent
+{
+public:
+ RecordedCreateSimilarDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
+ SurfaceFormat aFormat)
+ : RecordedEvent(CREATESIMILARDRAWTARGET)
+ , mRefPtr(aRefPtr) , mSize(aSize), mFormat(aFormat)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "CreateSimilarDrawTarget"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedCreateSimilarDrawTarget(std::istream &aStream);
+};
+
+class RecordedFillRect : public RecordedDrawingEvent {
+public:
+ RecordedFillRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(FILLRECT, aDT), mRect(aRect), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FillRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFillRect(std::istream &aStream);
+
+ Rect mRect;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+};
+
+class RecordedStrokeRect : public RecordedDrawingEvent {
+public:
+ RecordedStrokeRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKERECT, aDT), mRect(aRect),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "StrokeRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStrokeRect(std::istream &aStream);
+
+ Rect mRect;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedStrokeLine : public RecordedDrawingEvent {
+public:
+ RecordedStrokeLine(DrawTarget *aDT, const Point &aBegin, const Point &aEnd,
+ const Pattern &aPattern, const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKELINE, aDT), mBegin(aBegin), mEnd(aEnd),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "StrokeLine"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStrokeLine(std::istream &aStream);
+
+ Point mBegin;
+ Point mEnd;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedFill : public RecordedDrawingEvent {
+public:
+ RecordedFill(DrawTarget *aDT, ReferencePtr aPath, const Pattern &aPattern, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(FILL, aDT), mPath(aPath), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Fill"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFill(std::istream &aStream);
+
+ ReferencePtr mPath;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+};
+
+class RecordedFillGlyphs : public RecordedDrawingEvent {
+public:
+ RecordedFillGlyphs(DrawTarget *aDT, ReferencePtr aScaledFont, const Pattern &aPattern, const DrawOptions &aOptions,
+ const Glyph *aGlyphs, uint32_t aNumGlyphs)
+ : RecordedDrawingEvent(FILLGLYPHS, aDT), mScaledFont(aScaledFont), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ mNumGlyphs = aNumGlyphs;
+ mGlyphs = new Glyph[aNumGlyphs];
+ memcpy(mGlyphs, aGlyphs, sizeof(Glyph) * aNumGlyphs);
+ }
+ virtual ~RecordedFillGlyphs();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FillGlyphs"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFillGlyphs(std::istream &aStream);
+
+ ReferencePtr mScaledFont;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+ Glyph *mGlyphs;
+ uint32_t mNumGlyphs;
+};
+
+class RecordedMask : public RecordedDrawingEvent {
+public:
+ RecordedMask(DrawTarget *aDT, const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(MASK, aDT), mOptions(aOptions)
+ {
+ StorePattern(mSource, aSource);
+ StorePattern(mMask, aMask);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Mask"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedMask(std::istream &aStream);
+
+ PatternStorage mSource;
+ PatternStorage mMask;
+ DrawOptions mOptions;
+};
+
+class RecordedStroke : public RecordedDrawingEvent {
+public:
+ RecordedStroke(DrawTarget *aDT, ReferencePtr aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKE, aDT), mPath(aPath),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Stroke"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStroke(std::istream &aStream);
+
+ ReferencePtr mPath;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedClearRect : public RecordedDrawingEvent {
+public:
+ RecordedClearRect(DrawTarget *aDT, const Rect &aRect)
+ : RecordedDrawingEvent(CLEARRECT, aDT), mRect(aRect)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ClearRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedClearRect(std::istream &aStream);
+
+ Rect mRect;
+};
+
+class RecordedCopySurface : public RecordedDrawingEvent {
+public:
+ RecordedCopySurface(DrawTarget *aDT, ReferencePtr aSourceSurface,
+ const IntRect &aSourceRect, const IntPoint &aDest)
+ : RecordedDrawingEvent(COPYSURFACE, aDT), mSourceSurface(aSourceSurface),
+ mSourceRect(aSourceRect), mDest(aDest)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "CopySurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedCopySurface(std::istream &aStream);
+
+ ReferencePtr mSourceSurface;
+ IntRect mSourceRect;
+ IntPoint mDest;
+};
+
+class RecordedPushClip : public RecordedDrawingEvent {
+public:
+ RecordedPushClip(DrawTarget *aDT, ReferencePtr aPath)
+ : RecordedDrawingEvent(PUSHCLIP, aDT), mPath(aPath)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushClip"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushClip(std::istream &aStream);
+
+ ReferencePtr mPath;
+};
+
+class RecordedPushClipRect : public RecordedDrawingEvent {
+public:
+ RecordedPushClipRect(DrawTarget *aDT, const Rect &aRect)
+ : RecordedDrawingEvent(PUSHCLIPRECT, aDT), mRect(aRect)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushClipRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushClipRect(std::istream &aStream);
+
+ Rect mRect;
+};
+
+class RecordedPopClip : public RecordedDrawingEvent {
+public:
+ MOZ_IMPLICIT RecordedPopClip(DrawTarget *aDT)
+ : RecordedDrawingEvent(POPCLIP, aDT)
+ {}
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PopClip"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPopClip(std::istream &aStream);
+};
+
+class RecordedPushLayer : public RecordedDrawingEvent {
+public:
+ RecordedPushLayer(DrawTarget* aDT, bool aOpaque, Float aOpacity,
+ SourceSurface* aMask, const Matrix& aMaskTransform,
+ const IntRect& aBounds, bool aCopyBackground)
+ : RecordedDrawingEvent(PUSHLAYER, aDT), mOpaque(aOpaque)
+ , mOpacity(aOpacity), mMask(aMask), mMaskTransform(aMaskTransform)
+ , mBounds(aBounds), mCopyBackground(aCopyBackground)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushLayer"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushLayer(std::istream &aStream);
+
+ bool mOpaque;
+ Float mOpacity;
+ ReferencePtr mMask;
+ Matrix mMaskTransform;
+ IntRect mBounds;
+ bool mCopyBackground;
+};
+
+class RecordedPopLayer : public RecordedDrawingEvent {
+public:
+ MOZ_IMPLICIT RecordedPopLayer(DrawTarget* aDT)
+ : RecordedDrawingEvent(POPLAYER, aDT)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PopLayer"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPopLayer(std::istream &aStream);
+};
+
+class RecordedSetTransform : public RecordedDrawingEvent {
+public:
+ RecordedSetTransform(DrawTarget *aDT, const Matrix &aTransform)
+ : RecordedDrawingEvent(SETTRANSFORM, aDT), mTransform(aTransform)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetTransform"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedSetTransform(std::istream &aStream);
+
+ Matrix mTransform;
+};
+
+class RecordedDrawSurface : public RecordedDrawingEvent {
+public:
+ RecordedDrawSurface(DrawTarget *aDT, ReferencePtr aRefSource, const Rect &aDest,
+ const Rect &aSource, const DrawSurfaceOptions &aDSOptions,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(DRAWSURFACE, aDT), mRefSource(aRefSource), mDest(aDest)
+ , mSource(aSource), mDSOptions(aDSOptions), mOptions(aOptions)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawSurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawSurface(std::istream &aStream);
+
+ ReferencePtr mRefSource;
+ Rect mDest;
+ Rect mSource;
+ DrawSurfaceOptions mDSOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedDrawSurfaceWithShadow : public RecordedDrawingEvent {
+public:
+ RecordedDrawSurfaceWithShadow(DrawTarget *aDT, ReferencePtr aRefSource, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp)
+ : RecordedDrawingEvent(DRAWSURFACEWITHSHADOW, aDT), mRefSource(aRefSource), mDest(aDest)
+ , mColor(aColor), mOffset(aOffset), mSigma(aSigma), mOp(aOp)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawSurfaceWithShadow"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawSurfaceWithShadow(std::istream &aStream);
+
+ ReferencePtr mRefSource;
+ Point mDest;
+ Color mColor;
+ Point mOffset;
+ Float mSigma;
+ CompositionOp mOp;
+};
+
+class RecordedDrawFilter : public RecordedDrawingEvent {
+public:
+ RecordedDrawFilter(DrawTarget *aDT, ReferencePtr aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(DRAWFILTER, aDT), mNode(aNode), mSourceRect(aSourceRect)
+ , mDestPoint(aDestPoint), mOptions(aOptions)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawFilter"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawFilter(std::istream &aStream);
+
+ ReferencePtr mNode;
+ Rect mSourceRect;
+ Point mDestPoint;
+ DrawOptions mOptions;
+};
+
+class RecordedPathCreation : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedPathCreation(PathRecording *aPath);
+ ~RecordedPathCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Path Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ FillRule mFillRule;
+ std::vector<PathOp> mPathOps;
+
+ MOZ_IMPLICIT RecordedPathCreation(std::istream &aStream);
+};
+
+class RecordedPathDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedPathDestruction(PathRecording *aPath)
+ : RecordedEvent(PATHDESTRUCTION), mRefPtr(aPath)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Path Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedPathDestruction(std::istream &aStream);
+};
+
+class RecordedSourceSurfaceCreation : public RecordedEvent {
+public:
+ RecordedSourceSurfaceCreation(ReferencePtr aRefPtr, uint8_t *aData, int32_t aStride,
+ const IntSize &aSize, SurfaceFormat aFormat)
+ : RecordedEvent(SOURCESURFACECREATION), mRefPtr(aRefPtr), mData(aData)
+ , mStride(aStride), mSize(aSize), mFormat(aFormat), mDataOwned(false)
+ {
+ }
+
+ ~RecordedSourceSurfaceCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SourceSurface Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ uint8_t *mData;
+ int32_t mStride;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ bool mDataOwned;
+
+ MOZ_IMPLICIT RecordedSourceSurfaceCreation(std::istream &aStream);
+};
+
+class RecordedSourceSurfaceDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(SOURCESURFACEDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SourceSurface Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(std::istream &aStream);
+};
+
+class RecordedFilterNodeCreation : public RecordedEvent {
+public:
+ RecordedFilterNodeCreation(ReferencePtr aRefPtr, FilterType aType)
+ : RecordedEvent(FILTERNODECREATION), mRefPtr(aRefPtr), mType(aType)
+ {
+ }
+
+ ~RecordedFilterNodeCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FilterNode Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ FilterType mType;
+
+ MOZ_IMPLICIT RecordedFilterNodeCreation(std::istream &aStream);
+};
+
+class RecordedFilterNodeDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedFilterNodeDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(FILTERNODEDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FilterNode Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedFilterNodeDestruction(std::istream &aStream);
+};
+
+class RecordedGradientStopsCreation : public RecordedEvent {
+public:
+ RecordedGradientStopsCreation(ReferencePtr aRefPtr, GradientStop *aStops,
+ uint32_t aNumStops, ExtendMode aExtendMode)
+ : RecordedEvent(GRADIENTSTOPSCREATION), mRefPtr(aRefPtr), mStops(aStops)
+ , mNumStops(aNumStops), mExtendMode(aExtendMode), mDataOwned(false)
+ {
+ }
+
+ ~RecordedGradientStopsCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "GradientStops Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ GradientStop *mStops;
+ uint32_t mNumStops;
+ ExtendMode mExtendMode;
+ bool mDataOwned;
+
+ MOZ_IMPLICIT RecordedGradientStopsCreation(std::istream &aStream);
+};
+
+class RecordedGradientStopsDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(GRADIENTSTOPSDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "GradientStops Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(std::istream &aStream);
+};
+
+class RecordedSnapshot : public RecordedEvent {
+public:
+ RecordedSnapshot(ReferencePtr aRefPtr, DrawTarget *aDT)
+ : RecordedEvent(SNAPSHOT), mRefPtr(aRefPtr), mDT(aDT)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Snapshot"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ ReferencePtr mDT;
+
+ MOZ_IMPLICIT RecordedSnapshot(std::istream &aStream);
+};
+
+class RecordedFontData : public RecordedEvent {
+public:
+
+ static void FontDataProc(const uint8_t *aData, uint32_t aSize,
+ uint32_t aIndex, Float aGlyphSize, void* aBaton)
+ {
+ auto recordedFontData = static_cast<RecordedFontData*>(aBaton);
+ recordedFontData->SetFontData(aData, aSize, aIndex, aGlyphSize);
+ }
+
+ explicit RecordedFontData(ScaledFont *aScaledFont)
+ : RecordedEvent(FONTDATA), mData(nullptr)
+ {
+ mGetFontFileDataSucceeded = aScaledFont->GetFontFileData(&FontDataProc, this);
+ }
+
+ ~RecordedFontData();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Font Data"; }
+ virtual ReferencePtr GetObjectRef() const { return nullptr; };
+
+ void SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex,
+ Float aGlyphSize);
+
+ bool GetFontDetails(RecordedFontDetails& fontDetails);
+
+private:
+ friend class RecordedEvent;
+
+ uint8_t *mData;
+ RecordedFontDetails mFontDetails;
+
+ bool mGetFontFileDataSucceeded = false;
+
+ MOZ_IMPLICIT RecordedFontData(std::istream &aStream);
+};
+
+class RecordedFontDescriptor : public RecordedEvent {
+public:
+
+ static void FontDescCb(const uint8_t *aData, uint32_t aSize,
+ Float aFontSize, void* aBaton)
+ {
+ auto recordedFontDesc = static_cast<RecordedFontDescriptor*>(aBaton);
+ recordedFontDesc->SetFontDescriptor(aData, aSize, aFontSize);
+ }
+
+ explicit RecordedFontDescriptor(ScaledFont* aScaledFont)
+ : RecordedEvent(FONTDESC)
+ , mType(aScaledFont->GetType())
+ , mRefPtr(aScaledFont)
+ {
+ mHasDesc = aScaledFont->GetFontDescriptor(FontDescCb, this);
+ }
+
+ ~RecordedFontDescriptor();
+
+ bool IsValid() const { return mHasDesc; }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Font Desc"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+private:
+ friend class RecordedEvent;
+
+ void SetFontDescriptor(const uint8_t* aData, uint32_t aSize, Float aFontSize);
+
+ bool mHasDesc;
+
+ FontType mType;
+ Float mFontSize;
+ std::vector<uint8_t> mData;
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedFontDescriptor(std::istream &aStream);
+};
+
+class RecordedScaledFontCreation : public RecordedEvent {
+public:
+
+ static void FontInstanceDataProc(const uint8_t* aData, uint32_t aSize, void* aBaton)
+ {
+ auto recordedScaledFontCreation = static_cast<RecordedScaledFontCreation*>(aBaton);
+ recordedScaledFontCreation->SetFontInstanceData(aData, aSize);
+ }
+
+ RecordedScaledFontCreation(ScaledFont* aScaledFont,
+ RecordedFontDetails aFontDetails)
+ : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aScaledFont)
+ , mFontDataKey(aFontDetails.fontDataKey)
+ , mGlyphSize(aFontDetails.glyphSize) , mIndex(aFontDetails.index)
+ {
+ aScaledFont->GetFontInstanceData(FontInstanceDataProc, this);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ScaledFont Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ void SetFontInstanceData(const uint8_t *aData, uint32_t aSize);
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ uint64_t mFontDataKey;
+ Float mGlyphSize;
+ uint32_t mIndex;
+ std::vector<uint8_t> mInstanceData;
+
+ MOZ_IMPLICIT RecordedScaledFontCreation(std::istream &aStream);
+};
+
+class RecordedScaledFontDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedScaledFontDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(SCALEDFONTDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ScaledFont Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedScaledFontDestruction(std::istream &aStream);
+};
+
+class RecordedMaskSurface : public RecordedDrawingEvent {
+public:
+ RecordedMaskSurface(DrawTarget *aDT, const Pattern &aPattern, ReferencePtr aRefMask,
+ const Point &aOffset, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(MASKSURFACE, aDT), mRefMask(aRefMask), mOffset(aOffset)
+ , mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "MaskSurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedMaskSurface(std::istream &aStream);
+
+ PatternStorage mPattern;
+ ReferencePtr mRefMask;
+ Point mOffset;
+ DrawOptions mOptions;
+};
+
+class RecordedFilterNodeSetAttribute : public RecordedEvent
+{
+public:
+ enum ArgType {
+ ARGTYPE_UINT32,
+ ARGTYPE_BOOL,
+ ARGTYPE_FLOAT,
+ ARGTYPE_SIZE,
+ ARGTYPE_INTSIZE,
+ ARGTYPE_INTPOINT,
+ ARGTYPE_RECT,
+ ARGTYPE_INTRECT,
+ ARGTYPE_POINT,
+ ARGTYPE_MATRIX,
+ ARGTYPE_MATRIX5X4,
+ ARGTYPE_POINT3D,
+ ARGTYPE_COLOR,
+ ARGTYPE_FLOAT_ARRAY
+ };
+
+ template<typename T>
+ RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, T aArgument, ArgType aArgType)
+ : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(aArgType)
+ {
+ mPayload.resize(sizeof(T));
+ memcpy(&mPayload.front(), &aArgument, sizeof(T));
+ }
+
+ RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, const Float *aFloat, uint32_t aSize)
+ : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(ARGTYPE_FLOAT_ARRAY)
+ {
+ mPayload.resize(sizeof(Float) * aSize);
+ memcpy(&mPayload.front(), aFloat, sizeof(Float) * aSize);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetAttribute"; }
+
+ virtual ReferencePtr GetObjectRef() const { return mNode; }
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mNode;
+
+ uint32_t mIndex;
+ ArgType mArgType;
+ std::vector<uint8_t> mPayload;
+
+ MOZ_IMPLICIT RecordedFilterNodeSetAttribute(std::istream &aStream);
+};
+
+class RecordedFilterNodeSetInput : public RecordedEvent
+{
+public:
+ RecordedFilterNodeSetInput(FilterNode* aNode, uint32_t aIndex, FilterNode* aInputNode)
+ : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex)
+ , mInputFilter(aInputNode), mInputSurface(nullptr)
+ {
+ }
+
+ RecordedFilterNodeSetInput(FilterNode *aNode, uint32_t aIndex, SourceSurface *aInputSurface)
+ : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex)
+ , mInputFilter(nullptr), mInputSurface(aInputSurface)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetInput"; }
+
+ virtual ReferencePtr GetObjectRef() const { return mNode; }
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mNode;
+ uint32_t mIndex;
+ ReferencePtr mInputFilter;
+ ReferencePtr mInputSurface;
+
+ MOZ_IMPLICIT RecordedFilterNodeSetInput(std::istream &aStream);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/2d/RecordingTypes.h b/system/graphics/2d/RecordingTypes.h
new file mode 100644
index 000000000..93fc67a68
--- /dev/null
+++ b/system/graphics/2d/RecordingTypes.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RECORDINGTYPES_H_
+#define MOZILLA_GFX_RECORDINGTYPES_H_
+
+#include <ostream>
+
+namespace mozilla {
+namespace gfx {
+
+template<class T>
+struct ElementStreamFormat
+{
+ static void Write(std::ostream &aStream, const T &aElement)
+ {
+ aStream.write(reinterpret_cast<const char*>(&aElement), sizeof(T));
+ }
+ static void Read(std::istream &aStream, T &aElement)
+ {
+ aStream.read(reinterpret_cast<char *>(&aElement), sizeof(T));
+ }
+};
+
+template<class T>
+void WriteElement(std::ostream &aStream, const T &aElement)
+{
+ ElementStreamFormat<T>::Write(aStream, aElement);
+}
+template<class T>
+void ReadElement(std::istream &aStream, T &aElement)
+{
+ ElementStreamFormat<T>::Read(aStream, aElement);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_RECORDINGTYPES_H_ */
diff --git a/system/graphics/2d/Rect.h b/system/graphics/2d/Rect.h
new file mode 100644
index 000000000..942cb200d
--- /dev/null
+++ b/system/graphics/2d/Rect.h
@@ -0,0 +1,334 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RECT_H_
+#define MOZILLA_GFX_RECT_H_
+
+#include "BaseRect.h"
+#include "BaseMargin.h"
+#include "NumericTools.h"
+#include "Point.h"
+#include "Tools.h"
+#include "mozilla/Maybe.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+template<class units, class F> struct RectTyped;
+
+template<class units>
+struct IntMarginTyped:
+ public BaseMargin<int32_t, IntMarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseMargin<int32_t, IntMarginTyped<units> > Super;
+
+ IntMarginTyped() : Super() {}
+ IntMarginTyped(int32_t aTop, int32_t aRight, int32_t aBottom, int32_t aLeft) :
+ Super(aTop, aRight, aBottom, aLeft) {}
+
+ // XXX When all of the code is ported, the following functions to convert
+ // to and from unknown types should be removed.
+
+ static IntMarginTyped<units> FromUnknownMargin(const IntMarginTyped<UnknownUnits>& aMargin) {
+ return IntMarginTyped<units>(aMargin.top, aMargin.right,
+ aMargin.bottom, aMargin.left);
+ }
+
+ IntMarginTyped<UnknownUnits> ToUnknownMargin() const {
+ return IntMarginTyped<UnknownUnits>(this->top, this->right,
+ this->bottom, this->left);
+ }
+};
+typedef IntMarginTyped<UnknownUnits> IntMargin;
+
+template<class units, class F = Float>
+struct MarginTyped:
+ public BaseMargin<F, MarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseMargin<F, MarginTyped<units, F> > Super;
+
+ MarginTyped() : Super() {}
+ MarginTyped(F aTop, F aRight, F aBottom, F aLeft) :
+ Super(aTop, aRight, aBottom, aLeft) {}
+ explicit MarginTyped(const IntMarginTyped<units>& aMargin) :
+ Super(F(aMargin.top), F(aMargin.right),
+ F(aMargin.bottom), F(aMargin.left)) {}
+};
+typedef MarginTyped<UnknownUnits> Margin;
+typedef MarginTyped<UnknownUnits, double> MarginDouble;
+
+template<class units>
+IntMarginTyped<units> RoundedToInt(const MarginTyped<units>& aMargin)
+{
+ return IntMarginTyped<units>(int32_t(floorf(aMargin.top + 0.5f)),
+ int32_t(floorf(aMargin.right + 0.5f)),
+ int32_t(floorf(aMargin.bottom + 0.5f)),
+ int32_t(floorf(aMargin.left + 0.5f)));
+}
+
+template<class units>
+struct IntRectTyped :
+ public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> > Super;
+ typedef IntRectTyped<units> Self;
+ typedef IntParam<int32_t> ToInt;
+
+ IntRectTyped() : Super() {}
+ IntRectTyped(const IntPointTyped<units>& aPos, const IntSizeTyped<units>& aSize) :
+ Super(aPos, aSize) {}
+
+ IntRectTyped(ToInt aX, ToInt aY, ToInt aWidth, ToInt aHeight) :
+ Super(aX.value, aY.value, aWidth.value, aHeight.value) {}
+
+ static IntRectTyped<units> RoundIn(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::RoundIn(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> RoundOut(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::RoundOut(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> Round(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::Round(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> Truncate(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>(IntPointTyped<units>::Truncate(aX, aY),
+ IntSizeTyped<units>::Truncate(aW, aH));
+ }
+
+ static IntRectTyped<units> RoundIn(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.RoundIn();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> RoundOut(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.RoundOut();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> Round(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.Round();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> Truncate(const RectTyped<units, float>& aRect) {
+ return IntRectTyped::Truncate(aRect.x, aRect.y, aRect.width, aRect.height);
+ }
+
+ // Rounding isn't meaningful on an integer rectangle.
+ void Round() {}
+ void RoundIn() {}
+ void RoundOut() {}
+
+ // XXX When all of the code is ported, the following functions to convert
+ // to and from unknown types should be removed.
+
+ static IntRectTyped<units> FromUnknownRect(const IntRectTyped<UnknownUnits>& rect) {
+ return IntRectTyped<units>(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ IntRectTyped<UnknownUnits> ToUnknownRect() const {
+ return IntRectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
+ }
+
+ bool Overflows() const {
+ CheckedInt<int32_t> xMost = this->x;
+ xMost += this->width;
+ CheckedInt<int32_t> yMost = this->y;
+ yMost += this->height;
+ return !xMost.isValid() || !yMost.isValid();
+ }
+
+ // Same as Union(), but in the cases where aRect is non-empty, the union is
+ // done while guarding against overflow. If an overflow is detected, Nothing
+ // is returned.
+ MOZ_MUST_USE Maybe<Self> SafeUnion(const Self& aRect) const
+ {
+ if (this->IsEmpty()) {
+ return aRect.Overflows() ? Nothing() : Some(aRect);
+ } else if (aRect.IsEmpty()) {
+ return Some(*static_cast<const Self*>(this));
+ } else {
+ return this->SafeUnionEdges(aRect);
+ }
+ }
+
+ // Same as UnionEdges, but guards against overflow. If an overflow is detected,
+ // Nothing is returned.
+ MOZ_MUST_USE Maybe<Self> SafeUnionEdges(const Self& aRect) const
+ {
+ if (this->Overflows() || aRect.Overflows()) {
+ return Nothing();
+ }
+ // If neither |this| nor |aRect| overflow, then their XMost/YMost values
+ // should be safe to use.
+ CheckedInt<int32_t> newX = std::min(this->x, aRect.x);
+ CheckedInt<int32_t> newY = std::min(this->y, aRect.y);
+ CheckedInt<int32_t> newXMost = std::max(this->XMost(), aRect.XMost());
+ CheckedInt<int32_t> newYMost = std::max(this->YMost(), aRect.YMost());
+ CheckedInt<int32_t> newW = newXMost - newX;
+ CheckedInt<int32_t> newH = newYMost - newY;
+ if (!newW.isValid() || !newH.isValid()) {
+ return Nothing();
+ }
+ return Some(Self(newX.value(), newY.value(), newW.value(), newH.value()));
+ }
+
+ // This is here only to keep IPDL-generated code happy. DO NOT USE.
+ bool operator==(const IntRectTyped<units>& aRect) const
+ {
+ return IntRectTyped<units>::IsEqualEdges(aRect);
+ }
+
+ void InflateToMultiple(const IntSizeTyped<units>& aTileSize)
+ {
+ if (this->IsEmpty()) {
+ return;
+ }
+
+ int32_t yMost = this->YMost();
+ int32_t xMost = this->XMost();
+
+ this->x = mozilla::RoundDownToMultiple(this->x, aTileSize.width);
+ this->y = mozilla::RoundDownToMultiple(this->y, aTileSize.height);
+ xMost = mozilla::RoundUpToMultiple(xMost, aTileSize.width);
+ yMost = mozilla::RoundUpToMultiple(yMost, aTileSize.height);
+
+ this->width = xMost - this->x;
+ this->height = yMost - this->y;
+ }
+
+};
+typedef IntRectTyped<UnknownUnits> IntRect;
+
+template<class units, class F = Float>
+struct RectTyped :
+ public BaseRect<F, RectTyped<units, F>, PointTyped<units, F>, SizeTyped<units, F>, MarginTyped<units, F> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseRect<F, RectTyped<units, F>, PointTyped<units, F>, SizeTyped<units, F>, MarginTyped<units, F> > Super;
+
+ RectTyped() : Super() {}
+ RectTyped(const PointTyped<units, F>& aPos, const SizeTyped<units, F>& aSize) :
+ Super(aPos, aSize) {}
+ RectTyped(F _x, F _y, F _width, F _height) :
+ Super(_x, _y, _width, _height) {}
+ explicit RectTyped(const IntRectTyped<units>& rect) :
+ Super(F(rect.x), F(rect.y),
+ F(rect.width), F(rect.height)) {}
+
+ void NudgeToIntegers()
+ {
+ NudgeToInteger(&(this->x));
+ NudgeToInteger(&(this->y));
+ NudgeToInteger(&(this->width));
+ NudgeToInteger(&(this->height));
+ }
+
+ bool ToIntRect(IntRectTyped<units> *aOut) const
+ {
+ *aOut = IntRectTyped<units>(int32_t(this->X()), int32_t(this->Y()),
+ int32_t(this->Width()), int32_t(this->Height()));
+ return RectTyped<units, F>(F(aOut->x), F(aOut->y),
+ F(aOut->width), F(aOut->height))
+ .IsEqualEdges(*this);
+ }
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static RectTyped<units, F> FromUnknownRect(const RectTyped<UnknownUnits, F>& rect) {
+ return RectTyped<units, F>(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ RectTyped<UnknownUnits, F> ToUnknownRect() const {
+ return RectTyped<UnknownUnits, F>(this->x, this->y, this->width, this->height);
+ }
+
+ // This is here only to keep IPDL-generated code happy. DO NOT USE.
+ bool operator==(const RectTyped<units, F>& aRect) const
+ {
+ return RectTyped<units, F>::IsEqualEdges(aRect);
+ }
+};
+typedef RectTyped<UnknownUnits> Rect;
+typedef RectTyped<UnknownUnits, double> RectDouble;
+
+template<class units>
+IntRectTyped<units> RoundedToInt(const RectTyped<units>& aRect)
+{
+ RectTyped<units> copy(aRect);
+ copy.Round();
+ return IntRectTyped<units>(int32_t(copy.x),
+ int32_t(copy.y),
+ int32_t(copy.width),
+ int32_t(copy.height));
+}
+
+template<class units>
+IntRectTyped<units> RoundedIn(const RectTyped<units>& aRect)
+{
+ return IntRectTyped<units>::RoundIn(aRect);
+}
+
+template<class units>
+IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect)
+{
+ return IntRectTyped<units>::RoundOut(aRect);
+}
+
+template<class units>
+IntRectTyped<units> TruncatedToInt(const RectTyped<units>& aRect) {
+ return IntRectTyped<units>::Truncate(aRect);
+}
+
+template<class units>
+RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
+{
+ return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+// Convenience function for intersecting two rectangles wrapped in Maybes.
+template <typename T>
+Maybe<T>
+IntersectMaybeRects(const Maybe<T>& a, const Maybe<T>& b)
+{
+ if (!a) {
+ return b;
+ } else if (!b) {
+ return a;
+ } else {
+ return Some(a->Intersect(*b));
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_RECT_H_ */
diff --git a/system/graphics/2d/SFNTData.cpp b/system/graphics/2d/SFNTData.cpp
new file mode 100644
index 000000000..57757f939
--- /dev/null
+++ b/system/graphics/2d/SFNTData.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SFNTData.h"
+
+#include <algorithm>
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "mozilla/HashFunctions.h"
+#include "SFNTNameTable.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+
+#pragma pack(push, 1)
+
+struct TTCHeader
+{
+ BigEndianUint32 ttcTag; // Always 'ttcf'
+ BigEndianUint32 version; // Fixed, 0x00010000
+ BigEndianUint32 numFonts;
+};
+
+struct OffsetTable
+{
+ BigEndianUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0.
+ BigEndianUint16 numTables;
+ BigEndianUint16 searchRange; // (Maximum power of 2 <= numTables) x 16.
+ BigEndianUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
+ BigEndianUint16 rangeShift; // NumTables x 16-searchRange.
+};
+
+struct TableDirEntry
+{
+ BigEndianUint32 tag; // 4 -byte identifier.
+ BigEndianUint32 checkSum; // CheckSum for this table.
+ BigEndianUint32 offset; // Offset from beginning of TrueType font file.
+ BigEndianUint32 length; // Length of this table.
+
+ friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag)
+ {
+ return lhs.tag < aTag;
+ }
+};
+
+#pragma pack(pop)
+
+class SFNTData::Font
+{
+public:
+ Font(const OffsetTable *aOffsetTable, const uint8_t *aFontData,
+ uint32_t aDataLength)
+ : mFontData(aFontData)
+ , mFirstDirEntry(reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1))
+ , mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables)
+ , mDataLength(aDataLength)
+ {
+ }
+
+ bool GetU16FullName(mozilla::u16string& aU16FullName)
+ {
+ const TableDirEntry* dirEntry =
+ GetDirEntry(TRUETYPE_TAG('n', 'a', 'm', 'e'));
+ if (!dirEntry) {
+ gfxWarning() << "Name table entry not found.";
+ return false;
+ }
+
+ UniquePtr<SFNTNameTable> nameTable =
+ SFNTNameTable::Create((mFontData + dirEntry->offset), dirEntry->length);
+ if (!nameTable) {
+ return false;
+ }
+
+ return nameTable->GetU16FullName(aU16FullName);
+ }
+
+private:
+
+ const TableDirEntry*
+ GetDirEntry(const uint32_t aTag)
+ {
+ const TableDirEntry* foundDirEntry =
+ std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
+
+ if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
+ gfxWarning() << "Font data does not contain tag.";
+ return nullptr;
+ }
+
+ if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
+ gfxWarning() << "Font data too short to contain table.";
+ return nullptr;
+ }
+
+ return foundDirEntry;
+ }
+
+ const uint8_t *mFontData;
+ const TableDirEntry *mFirstDirEntry;
+ const TableDirEntry *mEndOfDirEntries;
+ uint32_t mDataLength;
+};
+
+/* static */
+UniquePtr<SFNTData>
+SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
+{
+ MOZ_ASSERT(aFontData);
+
+ // Check to see if this is a font collection.
+ if (aDataLength < sizeof(TTCHeader)) {
+ gfxWarning() << "Font data too short.";
+ return nullptr;
+ }
+
+ const TTCHeader *ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
+ if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
+ uint32_t numFonts = ttcHeader->numFonts;
+ if (aDataLength < sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
+ gfxWarning() << "Font data too short to contain full TTC Header.";
+ return nullptr;
+ }
+
+ UniquePtr<SFNTData> sfntData(new SFNTData);
+ const BigEndianUint32* offset =
+ reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
+ const BigEndianUint32* endOfOffsets = offset + numFonts;
+ while (offset != endOfOffsets) {
+ if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
+ return nullptr;
+ }
+ ++offset;
+ }
+
+ return Move(sfntData);
+ }
+
+ UniquePtr<SFNTData> sfntData(new SFNTData);
+ if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
+ return nullptr;
+ }
+
+ return Move(sfntData);
+}
+
+/* static */
+uint64_t
+SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength)
+{
+ uint64_t hash;
+ UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
+ mozilla::u16string firstName;
+ if (sfntData && sfntData->GetU16FullName(0, firstName)) {
+ hash = HashString(firstName.c_str(), firstName.length());
+ } else {
+ gfxWarning() << "Failed to get name from font data hashing whole font.";
+ hash = HashString(aFontData, aDataLength);
+ }
+
+ return hash << 32 | aDataLength;;
+}
+
+SFNTData::~SFNTData()
+{
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ delete mFonts[i];
+ }
+}
+
+bool
+SFNTData::GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName)
+{
+ if (aIndex >= mFonts.length()) {
+ gfxWarning() << "aIndex to font data too high.";
+ return false;
+ }
+
+ return mFonts[aIndex]->GetU16FullName(aU16FullName);
+}
+
+bool
+SFNTData::GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames)
+{
+ bool fontFound = false;
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ mozilla::u16string name;
+ if (mFonts[i]->GetU16FullName(name)) {
+ fontFound = true;
+ }
+ if (!aU16FullNames.append(Move(name))) {
+ return false;
+ }
+ }
+
+ return fontFound;
+}
+
+bool
+SFNTData::GetIndexForU16Name(const mozilla::u16string& aU16FullName,
+ uint32_t* aIndex, size_t aTruncatedLen)
+{
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ mozilla::u16string name;
+ if (!mFonts[i]->GetU16FullName(name)) {
+ continue;
+ }
+
+ if (aTruncatedLen) {
+ MOZ_ASSERT(aU16FullName.length() <= aTruncatedLen);
+ name = name.substr(0, aTruncatedLen);
+ }
+
+ if (name == aU16FullName) {
+ *aIndex = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+ uint32_t aOffset)
+{
+ uint32_t remainingLength = aDataLength - aOffset;
+ if (remainingLength < sizeof(OffsetTable)) {
+ gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
+ return false;
+ }
+
+ const OffsetTable *offsetTable =
+ reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
+ if (remainingLength <
+ sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
+ gfxWarning() << "Font data too short to contain tables.";
+ return false;
+ }
+
+ return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
+}
+
+} // gfx
+} // mozilla
diff --git a/system/graphics/2d/SFNTData.h b/system/graphics/2d/SFNTData.h
new file mode 100644
index 000000000..eed2dd2d7
--- /dev/null
+++ b/system/graphics/2d/SFNTData.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_SFNTData_h
+#define mozilla_gfx_SFNTData_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SFNTData final
+{
+public:
+
+ /**
+ * Creates an SFNTData if the header is a format that we understand and
+ * aDataLength is sufficient for the length information in the header data.
+ * Note that the data is NOT copied, so must exist the SFNTData's lifetime.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length
+ * @return UniquePtr to a SFNTData or nullptr if the header is invalid.
+ */
+ static UniquePtr<SFNTData> Create(const uint8_t *aFontData,
+ uint32_t aDataLength);
+
+ /**
+ * Creates a unique key for the given font data.
+ *
+ * @param aFontData the SFNT data
+ * @param aDataLength length
+ * @return unique key to be used for caching
+ */
+ static uint64_t GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength);
+
+ ~SFNTData();
+
+ /**
+ * Gets the full name from the name table of the font corresponding to the
+ * index. If the full name string is not present it will use the family space
+ * concatenated with the style.
+ * This will only read names that are already UTF16.
+ *
+ * @param aFontData SFNT data.
+ * @param aDataLength length of aFontData.
+ * @param aU16FullName string to be populated with the full name.
+ * @return true if the full name is successfully read.
+ */
+ bool GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName);
+
+ /**
+ * Populate a Vector with the first UTF16 full name from each name table of
+ * the fonts. If the full name string is not present it will use the family
+ * space concatenated with the style.
+ * This will only read names that are already UTF16.
+ *
+ * @param aU16FullNames the Vector to be populated.
+ * @return true if at least one name found otherwise false.
+ */
+ bool GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames);
+
+ /**
+ * Returns the index for the first UTF16 name matching aU16FullName.
+ *
+ * @param aU16FullName full name to find.
+ * @param aIndex out param for the index if found.
+ * @param aTruncatedLen length to truncate the compared font name to.
+ * @return true if the full name is successfully read.
+ */
+ bool GetIndexForU16Name(const mozilla::u16string& aU16FullName, uint32_t* aIndex,
+ size_t aTruncatedLen = 0);
+
+private:
+
+ SFNTData() {}
+
+ bool AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+ uint32_t aOffset);
+
+ // Internal representation of single font in font file.
+ class Font;
+
+ Vector<Font*> mFonts;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTData_h
diff --git a/system/graphics/2d/SFNTNameTable.cpp b/system/graphics/2d/SFNTNameTable.cpp
new file mode 100644
index 000000000..e7da2305c
--- /dev/null
+++ b/system/graphics/2d/SFNTNameTable.cpp
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SFNTNameTable.h"
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const BigEndianUint16 FORMAT_0 = 0;
+
+static const BigEndianUint16 NAME_ID_FAMILY = 1;
+static const BigEndianUint16 NAME_ID_STYLE = 2;
+static const BigEndianUint16 NAME_ID_FULL = 4;
+
+static const BigEndianUint16 PLATFORM_ID_UNICODE = 0;
+static const BigEndianUint16 PLATFORM_ID_MAC = 1;
+static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3;
+
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
+
+static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0;
+
+static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
+
+static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
+
+#pragma pack(push, 1)
+
+// Name table has a header, followed by name records, followed by string data.
+struct NameHeader
+{
+ BigEndianUint16 format; // Format selector (=0).
+ BigEndianUint16 count; // Number of name records.
+ BigEndianUint16 stringOffset; // Offset to string storage from start of table.
+};
+
+struct NameRecord
+{
+ BigEndianUint16 platformID;
+ BigEndianUint16 encodingID; // Platform-specific encoding ID
+ BigEndianUint16 languageID;
+ BigEndianUint16 nameID;
+ BigEndianUint16 length; // String length in bytes.
+ BigEndianUint16 offset; // String offset from start of storage in bytes.
+};
+
+#pragma pack(pop)
+
+enum ENameDecoder : int
+{
+ eNameDecoderUTF16,
+ eNameDecoderNone
+};
+
+/* static */
+UniquePtr<SFNTNameTable>
+SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength)
+{
+ MOZ_ASSERT(aNameData);
+
+ if (aDataLength < sizeof(NameHeader)) {
+ gfxWarning() << "Name data too short to contain NameHeader.";
+ return nullptr;
+ }
+
+ const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
+ if (nameHeader->format != FORMAT_0) {
+ gfxWarning() << "Only Name Table Format 0 is supported.";
+ return nullptr;
+ }
+
+ uint16_t stringOffset = nameHeader->stringOffset;
+
+ if (stringOffset !=
+ sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) {
+ gfxWarning() << "Name table string offset is incorrect.";
+ return nullptr;
+ }
+
+ if (aDataLength < stringOffset) {
+ gfxWarning() << "Name data too short to contain name records.";
+ return nullptr;
+ }
+
+ return UniquePtr<SFNTNameTable>(
+ new SFNTNameTable(nameHeader, aNameData, aDataLength));
+}
+
+SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader,
+ const uint8_t *aNameData, uint32_t aDataLength)
+ : mFirstRecord(reinterpret_cast<const NameRecord*>(aNameData
+ + sizeof(NameHeader)))
+ , mEndOfRecords(mFirstRecord + aNameHeader->count)
+ , mStringData(aNameData + aNameHeader->stringOffset)
+ , mStringDataLength(aDataLength - aNameHeader->stringOffset)
+{
+ MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
+}
+
+static bool
+IsUTF16Encoding(const NameRecord *aNameRecord)
+{
+ if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP ||
+ aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) {
+ return true;
+ }
+
+ if (aNameRecord->platformID == PLATFORM_ID_UNICODE) {
+ return true;
+ }
+
+ return false;
+}
+
+static NameRecordMatchers*
+CreateCanonicalMatchers(const BigEndianUint16& aNameID)
+{
+ // For Windows, we return only Microsoft platform name record
+ // matchers. On Mac, we return matchers for both Microsoft platform
+ // records and Mac platform records.
+ NameRecordMatchers *matchers = new NameRecordMatchers();
+
+ // First, look for the English name (this will normally succeed).
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
+ aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ IsUTF16Encoding(aNameRecord)) {
+ return eNameDecoderUTF16;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+
+ // Second, look for all languages.
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ IsUTF16Encoding(aNameRecord)) {
+ return eNameDecoderUTF16;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+
+ return matchers;
+}
+
+static const NameRecordMatchers&
+FullNameMatchers()
+{
+ static const NameRecordMatchers *sFullNameMatchers =
+ CreateCanonicalMatchers(NAME_ID_FULL);
+ return *sFullNameMatchers;
+}
+
+static const NameRecordMatchers&
+FamilyMatchers()
+{
+ static const NameRecordMatchers *sFamilyMatchers =
+ CreateCanonicalMatchers(NAME_ID_FAMILY);
+ return *sFamilyMatchers;
+}
+
+static const NameRecordMatchers&
+StyleMatchers()
+{
+ static const NameRecordMatchers *sStyleMatchers =
+ CreateCanonicalMatchers(NAME_ID_STYLE);
+ return *sStyleMatchers;
+}
+
+bool
+SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName)
+{
+ if (ReadU16Name(FullNameMatchers(), aU16FullName)) {
+ return true;
+ }
+
+ // If the full name record doesn't exist create the name from the family space
+ // concatenated with the style.
+ mozilla::u16string familyName;
+ if (!ReadU16Name(FamilyMatchers(), familyName)) {
+ return false;
+ }
+
+ mozilla::u16string styleName;
+ if (!ReadU16Name(StyleMatchers(), styleName)) {
+ return false;
+ }
+
+ aU16FullName.assign(Move(familyName));
+ aU16FullName.append(u" ");
+ aU16FullName.append(styleName);
+ return true;
+}
+
+bool
+SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
+ mozilla::u16string& aU16Name)
+{
+ MOZ_ASSERT(!aMatchers.empty());
+
+ for (size_t i = 0; i < aMatchers.length(); ++i) {
+ const NameRecord* record = mFirstRecord;
+ while (record != mEndOfRecords) {
+ switch (aMatchers[i](record)) {
+ case eNameDecoderUTF16:
+ return ReadU16NameFromU16Record(record, aU16Name);
+ case eNameDecoderNone:
+ break;
+ default:
+ MOZ_CRASH("Invalid matcher encoding type");
+ break;
+ }
+ ++record;
+ }
+ }
+
+ return false;
+}
+
+bool
+SFNTNameTable::ReadU16NameFromU16Record(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name)
+{
+ uint32_t offset = aNameRecord->offset;
+ uint32_t length = aNameRecord->length;
+ if (mStringDataLength < offset + length) {
+ gfxWarning() << "Name data too short to contain name string.";
+ return false;
+ }
+
+ const uint8_t *startOfName = mStringData + offset;
+ size_t actualLength = length / sizeof(char16_t);
+ UniquePtr<char16_t[]> nameData(new char16_t[actualLength]);
+ NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName,
+ actualLength);
+
+ aU16Name.assign(nameData.get(), actualLength);
+ return true;
+}
+
+} // gfx
+} // mozilla
diff --git a/system/graphics/2d/SFNTNameTable.h b/system/graphics/2d/SFNTNameTable.h
new file mode 100644
index 000000000..3fb1ee0bc
--- /dev/null
+++ b/system/graphics/2d/SFNTNameTable.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_SFNTNameTable_h
+#define mozilla_gfx_SFNTNameTable_h
+
+#include "mozilla/Function.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct NameHeader;
+struct NameRecord;
+enum ENameDecoder : int;
+
+typedef Vector<function<ENameDecoder(const NameRecord*)>> NameRecordMatchers;
+
+class SFNTNameTable final
+{
+public:
+
+ /**
+ * Creates a SFNTNameTable if the header data is valid. Note that the data is
+ * NOT copied, so must exist for the lifetime of the table.
+ *
+ * @param aNameData the Name Table data.
+ * @param aDataLength length
+ * @return UniquePtr to a SFNTNameTable or nullptr if the header is invalid.
+ */
+ static UniquePtr<SFNTNameTable> Create(const uint8_t *aNameData,
+ uint32_t aDataLength);
+
+ /**
+ * Gets the full name from the name table. If the full name string is not
+ * present it will use the family space concatenated with the style.
+ * This will only read names that are already UTF16 or Mac OS Roman.
+ *
+ * @param aU16FullName string to be populated with the full name.
+ * @return true if the full name is successfully read.
+ */
+ bool GetU16FullName(mozilla::u16string& aU16FullName);
+
+private:
+
+ SFNTNameTable(const NameHeader *aNameHeader, const uint8_t *aNameData,
+ uint32_t aDataLength);
+
+ bool ReadU16Name(const NameRecordMatchers& aMatchers, mozilla::u16string& aU16Name);
+
+ bool ReadU16NameFromU16Record(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name);
+
+ const NameRecord *mFirstRecord;
+ const NameRecord *mEndOfRecords;
+ const uint8_t *mStringData;
+ const uint32_t mStringDataLength;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTNameTable_h
diff --git a/system/graphics/2d/SIMD.h b/system/graphics/2d/SIMD.h
new file mode 100644
index 000000000..6bf53a38e
--- /dev/null
+++ b/system/graphics/2d/SIMD.h
@@ -0,0 +1,1180 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_SIMD_H_
+#define _MOZILLA_GFX_SIMD_H_
+
+/**
+ * Consumers of this file need to #define SIMD_COMPILE_SSE2 before including it
+ * if they want access to the SSE2 functions.
+ */
+
+#ifdef SIMD_COMPILE_SSE2
+#include <xmmintrin.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+namespace simd {
+
+template<typename u8x16_t>
+u8x16_t Load8(const uint8_t* aSource);
+
+template<typename u8x16_t>
+u8x16_t From8(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p);
+
+template<typename u8x16_t>
+u8x16_t FromZero8();
+
+template<typename i16x8_t>
+i16x8_t FromI16(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h);
+
+template<typename u16x8_t>
+u16x8_t FromU16(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h);
+
+template<typename i16x8_t>
+i16x8_t FromI16(int16_t a);
+
+template<typename u16x8_t>
+u16x8_t FromU16(uint16_t a);
+
+template<typename i32x4_t>
+i32x4_t From32(int32_t a, int32_t b, int32_t c, int32_t d);
+
+template<typename i32x4_t>
+i32x4_t From32(int32_t a);
+
+template<typename f32x4_t>
+f32x4_t FromF32(float a, float b, float c, float d);
+
+template<typename f32x4_t>
+f32x4_t FromF32(float a);
+
+// All SIMD backends overload these functions for their SIMD types:
+
+#if 0
+
+// Store 16 bytes to a 16-byte aligned address
+void Store8(uint8_t* aTarget, u8x16_t aM);
+
+// Fixed shifts
+template<int32_t aNumberOfBits> i16x8_t ShiftRight16(i16x8_t aM);
+template<int32_t aNumberOfBits> i32x4_t ShiftRight32(i32x4_t aM);
+
+i16x8_t Add16(i16x8_t aM1, i16x8_t aM2);
+i32x4_t Add32(i32x4_t aM1, i32x4_t aM2);
+i16x8_t Sub16(i16x8_t aM1, i16x8_t aM2);
+i32x4_t Sub32(i32x4_t aM1, i32x4_t aM2);
+u8x16_t Min8(u8x16_t aM1, iu8x16_t aM2);
+u8x16_t Max8(u8x16_t aM1, iu8x16_t aM2);
+i32x4_t Min32(i32x4_t aM1, i32x4_t aM2);
+i32x4_t Max32(i32x4_t aM1, i32x4_t aM2);
+
+// Truncating i16 -> i16 multiplication
+i16x8_t Mul16(i16x8_t aM1, i16x8_t aM2);
+
+// Long multiplication i16 -> i32
+// aFactorsA1B1 = (a1[4] b1[4])
+// aFactorsA2B2 = (a2[4] b2[4])
+// aProductA = a1 * a2, aProductB = b1 * b2
+void Mul16x4x2x2To32x4x2(i16x8_t aFactorsA1B1, i16x8_t aFactorsA2B2,
+ i32x4_t& aProductA, i32x4_t& aProductB);
+
+// Long multiplication + pairwise addition i16 -> i32
+// See the scalar implementation for specifics.
+i32x4_t MulAdd16x8x2To32x4(i16x8_t aFactorsA, i16x8_t aFactorsB);
+i32x4_t MulAdd16x8x2To32x4(u16x8_t aFactorsA, u16x8_t aFactorsB);
+
+// Set all four 32-bit components to the value of the component at aIndex.
+template<int8_t aIndex>
+i32x4_t Splat32(i32x4_t aM);
+
+// Interpret the input as four 32-bit values, apply Splat32<aIndex> on them,
+// re-interpret the result as sixteen 8-bit values.
+template<int8_t aIndex>
+u8x16_t Splat32On8(u8x16_t aM);
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i32x4 Shuffle32(i32x4 aM);
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleLo16(i16x8 aM);
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleHi16(i16x8 aM);
+
+u8x16_t InterleaveLo8(u8x16_t m1, u8x16_t m2);
+u8x16_t InterleaveHi8(u8x16_t m1, u8x16_t m2);
+i16x8_t InterleaveLo16(i16x8_t m1, i16x8_t m2);
+i16x8_t InterleaveHi16(i16x8_t m1, i16x8_t m2);
+i32x4_t InterleaveLo32(i32x4_t m1, i32x4_t m2);
+
+i16x8_t UnpackLo8x8ToI16x8(u8x16_t m);
+i16x8_t UnpackHi8x8ToI16x8(u8x16_t m);
+u16x8_t UnpackLo8x8ToU16x8(u8x16_t m);
+u16x8_t UnpackHi8x8ToU16x8(u8x16_t m);
+
+i16x8_t PackAndSaturate32To16(i32x4_t m1, i32x4_t m2);
+u8x16_t PackAndSaturate16To8(i16x8_t m1, i16x8_t m2);
+u8x16_t PackAndSaturate32To8(i32x4_t m1, i32x4_t m2, i32x4_t m3, const i32x4_t& m4);
+
+i32x4 FastDivideBy255(i32x4 m);
+i16x8 FastDivideBy255_16(i16x8 m);
+
+#endif
+
+// Scalar
+
+struct Scalaru8x16_t {
+ uint8_t u8[16];
+};
+
+union Scalari16x8_t {
+ int16_t i16[8];
+ uint16_t u16[8];
+};
+
+typedef Scalari16x8_t Scalaru16x8_t;
+
+struct Scalari32x4_t {
+ int32_t i32[4];
+};
+
+struct Scalarf32x4_t {
+ float f32[4];
+};
+
+template<>
+inline Scalaru8x16_t
+Load8<Scalaru8x16_t>(const uint8_t* aSource)
+{
+ return *(Scalaru8x16_t*)aSource;
+}
+
+inline void Store8(uint8_t* aTarget, Scalaru8x16_t aM)
+{
+ *(Scalaru8x16_t*)aTarget = aM;
+}
+
+template<>
+inline Scalaru8x16_t From8<Scalaru8x16_t>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+{
+ Scalaru8x16_t _m;
+ _m.u8[0] = a;
+ _m.u8[1] = b;
+ _m.u8[2] = c;
+ _m.u8[3] = d;
+ _m.u8[4] = e;
+ _m.u8[5] = f;
+ _m.u8[6] = g;
+ _m.u8[7] = h;
+ _m.u8[8+0] = i;
+ _m.u8[8+1] = j;
+ _m.u8[8+2] = k;
+ _m.u8[8+3] = l;
+ _m.u8[8+4] = m;
+ _m.u8[8+5] = n;
+ _m.u8[8+6] = o;
+ _m.u8[8+7] = p;
+ return _m;
+}
+
+template<>
+inline Scalaru8x16_t FromZero8<Scalaru8x16_t>()
+{
+ return From8<Scalaru8x16_t>(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+}
+
+template<>
+inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h)
+{
+ Scalari16x8_t m;
+ m.i16[0] = a;
+ m.i16[1] = b;
+ m.i16[2] = c;
+ m.i16[3] = d;
+ m.i16[4] = e;
+ m.i16[5] = f;
+ m.i16[6] = g;
+ m.i16[7] = h;
+ return m;
+}
+
+template<>
+inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
+{
+ Scalaru16x8_t m;
+ m.u16[0] = a;
+ m.u16[1] = b;
+ m.u16[2] = c;
+ m.u16[3] = d;
+ m.u16[4] = e;
+ m.u16[5] = f;
+ m.u16[6] = g;
+ m.u16[7] = h;
+ return m;
+}
+
+template<>
+inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a)
+{
+ return FromI16<Scalari16x8_t>(a, a, a, a, a, a, a, a);
+}
+
+template<>
+inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a)
+{
+ return FromU16<Scalaru16x8_t>(a, a, a, a, a, a, a, a);
+}
+
+template<>
+inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a, int32_t b, int32_t c, int32_t d)
+{
+ Scalari32x4_t m;
+ m.i32[0] = a;
+ m.i32[1] = b;
+ m.i32[2] = c;
+ m.i32[3] = d;
+ return m;
+}
+
+template<>
+inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a, float b, float c, float d)
+{
+ Scalarf32x4_t m;
+ m.f32[0] = a;
+ m.f32[1] = b;
+ m.f32[2] = c;
+ m.f32[3] = d;
+ return m;
+}
+
+template<>
+inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a)
+{
+ return FromF32<Scalarf32x4_t>(a, a, a, a);
+}
+
+template<>
+inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a)
+{
+ return From32<Scalari32x4_t>(a, a, a, a);
+}
+
+template<int32_t aNumberOfBits>
+inline Scalari16x8_t ShiftRight16(Scalari16x8_t aM)
+{
+ return FromI16<Scalari16x8_t>(uint16_t(aM.i16[0]) >> aNumberOfBits, uint16_t(aM.i16[1]) >> aNumberOfBits,
+ uint16_t(aM.i16[2]) >> aNumberOfBits, uint16_t(aM.i16[3]) >> aNumberOfBits,
+ uint16_t(aM.i16[4]) >> aNumberOfBits, uint16_t(aM.i16[5]) >> aNumberOfBits,
+ uint16_t(aM.i16[6]) >> aNumberOfBits, uint16_t(aM.i16[7]) >> aNumberOfBits);
+}
+
+template<int32_t aNumberOfBits>
+inline Scalari32x4_t ShiftRight32(Scalari32x4_t aM)
+{
+ return From32<Scalari32x4_t>(aM.i32[0] >> aNumberOfBits, aM.i32[1] >> aNumberOfBits,
+ aM.i32[2] >> aNumberOfBits, aM.i32[3] >> aNumberOfBits);
+}
+
+inline Scalaru16x8_t Add16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(aM1.u16[0] + aM2.u16[0], aM1.u16[1] + aM2.u16[1],
+ aM1.u16[2] + aM2.u16[2], aM1.u16[3] + aM2.u16[3],
+ aM1.u16[4] + aM2.u16[4], aM1.u16[5] + aM2.u16[5],
+ aM1.u16[6] + aM2.u16[6], aM1.u16[7] + aM2.u16[7]);
+}
+
+inline Scalari32x4_t Add32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(aM1.i32[0] + aM2.i32[0], aM1.i32[1] + aM2.i32[1],
+ aM1.i32[2] + aM2.i32[2], aM1.i32[3] + aM2.i32[3]);
+}
+
+inline Scalaru16x8_t Sub16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(aM1.u16[0] - aM2.u16[0], aM1.u16[1] - aM2.u16[1],
+ aM1.u16[2] - aM2.u16[2], aM1.u16[3] - aM2.u16[3],
+ aM1.u16[4] - aM2.u16[4], aM1.u16[5] - aM2.u16[5],
+ aM1.u16[6] - aM2.u16[6], aM1.u16[7] - aM2.u16[7]);
+}
+
+inline Scalari32x4_t Sub32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(aM1.i32[0] - aM2.i32[0], aM1.i32[1] - aM2.i32[1],
+ aM1.i32[2] - aM2.i32[2], aM1.i32[3] - aM2.i32[3]);
+}
+
+inline int32_t
+umin(int32_t a, int32_t b)
+{
+ return a - ((a - b) & -(a > b));
+}
+
+inline int32_t
+umax(int32_t a, int32_t b)
+{
+ return a - ((a - b) & -(a < b));
+}
+
+inline Scalaru8x16_t Min8(Scalaru8x16_t aM1, Scalaru8x16_t aM2)
+{
+ return From8<Scalaru8x16_t>(umin(aM1.u8[0], aM2.u8[0]), umin(aM1.u8[1], aM2.u8[1]),
+ umin(aM1.u8[2], aM2.u8[2]), umin(aM1.u8[3], aM2.u8[3]),
+ umin(aM1.u8[4], aM2.u8[4]), umin(aM1.u8[5], aM2.u8[5]),
+ umin(aM1.u8[6], aM2.u8[6]), umin(aM1.u8[7], aM2.u8[7]),
+ umin(aM1.u8[8+0], aM2.u8[8+0]), umin(aM1.u8[8+1], aM2.u8[8+1]),
+ umin(aM1.u8[8+2], aM2.u8[8+2]), umin(aM1.u8[8+3], aM2.u8[8+3]),
+ umin(aM1.u8[8+4], aM2.u8[8+4]), umin(aM1.u8[8+5], aM2.u8[8+5]),
+ umin(aM1.u8[8+6], aM2.u8[8+6]), umin(aM1.u8[8+7], aM2.u8[8+7]));
+}
+
+inline Scalaru8x16_t Max8(Scalaru8x16_t aM1, Scalaru8x16_t aM2)
+{
+ return From8<Scalaru8x16_t>(umax(aM1.u8[0], aM2.u8[0]), umax(aM1.u8[1], aM2.u8[1]),
+ umax(aM1.u8[2], aM2.u8[2]), umax(aM1.u8[3], aM2.u8[3]),
+ umax(aM1.u8[4], aM2.u8[4]), umax(aM1.u8[5], aM2.u8[5]),
+ umax(aM1.u8[6], aM2.u8[6]), umax(aM1.u8[7], aM2.u8[7]),
+ umax(aM1.u8[8+0], aM2.u8[8+0]), umax(aM1.u8[8+1], aM2.u8[8+1]),
+ umax(aM1.u8[8+2], aM2.u8[8+2]), umax(aM1.u8[8+3], aM2.u8[8+3]),
+ umax(aM1.u8[8+4], aM2.u8[8+4]), umax(aM1.u8[8+5], aM2.u8[8+5]),
+ umax(aM1.u8[8+6], aM2.u8[8+6]), umax(aM1.u8[8+7], aM2.u8[8+7]));
+}
+
+inline Scalari32x4_t Min32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(umin(aM1.i32[0], aM2.i32[0]), umin(aM1.i32[1], aM2.i32[1]),
+ umin(aM1.i32[2], aM2.i32[2]), umin(aM1.i32[3], aM2.i32[3]));
+}
+
+inline Scalari32x4_t Max32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(umax(aM1.i32[0], aM2.i32[0]), umax(aM1.i32[1], aM2.i32[1]),
+ umax(aM1.i32[2], aM2.i32[2]), umax(aM1.i32[3], aM2.i32[3]));
+}
+
+inline Scalaru16x8_t Mul16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(uint16_t(int32_t(aM1.u16[0]) * int32_t(aM2.u16[0])), uint16_t(int32_t(aM1.u16[1]) * int32_t(aM2.u16[1])),
+ uint16_t(int32_t(aM1.u16[2]) * int32_t(aM2.u16[2])), uint16_t(int32_t(aM1.u16[3]) * int32_t(aM2.u16[3])),
+ uint16_t(int32_t(aM1.u16[4]) * int32_t(aM2.u16[4])), uint16_t(int32_t(aM1.u16[5]) * int32_t(aM2.u16[5])),
+ uint16_t(int32_t(aM1.u16[6]) * int32_t(aM2.u16[6])), uint16_t(int32_t(aM1.u16[7]) * int32_t(aM2.u16[7])));
+}
+
+inline void Mul16x4x2x2To32x4x2(Scalari16x8_t aFactorsA1B1,
+ Scalari16x8_t aFactorsA2B2,
+ Scalari32x4_t& aProductA,
+ Scalari32x4_t& aProductB)
+{
+ aProductA = From32<Scalari32x4_t>(aFactorsA1B1.i16[0] * aFactorsA2B2.i16[0],
+ aFactorsA1B1.i16[1] * aFactorsA2B2.i16[1],
+ aFactorsA1B1.i16[2] * aFactorsA2B2.i16[2],
+ aFactorsA1B1.i16[3] * aFactorsA2B2.i16[3]);
+ aProductB = From32<Scalari32x4_t>(aFactorsA1B1.i16[4] * aFactorsA2B2.i16[4],
+ aFactorsA1B1.i16[5] * aFactorsA2B2.i16[5],
+ aFactorsA1B1.i16[6] * aFactorsA2B2.i16[6],
+ aFactorsA1B1.i16[7] * aFactorsA2B2.i16[7]);
+}
+
+inline Scalari32x4_t MulAdd16x8x2To32x4(Scalari16x8_t aFactorsA,
+ Scalari16x8_t aFactorsB)
+{
+ return From32<Scalari32x4_t>(aFactorsA.i16[0] * aFactorsB.i16[0] + aFactorsA.i16[1] * aFactorsB.i16[1],
+ aFactorsA.i16[2] * aFactorsB.i16[2] + aFactorsA.i16[3] * aFactorsB.i16[3],
+ aFactorsA.i16[4] * aFactorsB.i16[4] + aFactorsA.i16[5] * aFactorsB.i16[5],
+ aFactorsA.i16[6] * aFactorsB.i16[6] + aFactorsA.i16[7] * aFactorsB.i16[7]);
+}
+
+template<int8_t aIndex>
+inline void AssertIndex()
+{
+ static_assert(aIndex == 0 || aIndex == 1 || aIndex == 2 || aIndex == 3,
+ "Invalid splat index");
+}
+
+template<int8_t aIndex>
+inline Scalari32x4_t Splat32(Scalari32x4_t aM)
+{
+ AssertIndex<aIndex>();
+ return From32<Scalari32x4_t>(aM.i32[aIndex], aM.i32[aIndex],
+ aM.i32[aIndex], aM.i32[aIndex]);
+}
+
+template<int8_t i>
+inline Scalaru8x16_t Splat32On8(Scalaru8x16_t aM)
+{
+ AssertIndex<i>();
+ return From8<Scalaru8x16_t>(aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3]);
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari32x4_t Shuffle32(Scalari32x4_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari32x4_t m = aM;
+ m.i32[0] = aM.i32[i3];
+ m.i32[1] = aM.i32[i2];
+ m.i32[2] = aM.i32[i1];
+ m.i32[3] = aM.i32[i0];
+ return m;
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari16x8_t ShuffleLo16(Scalari16x8_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari16x8_t m = aM;
+ m.i16[0] = aM.i16[i3];
+ m.i16[1] = aM.i16[i2];
+ m.i16[2] = aM.i16[i1];
+ m.i16[3] = aM.i16[i0];
+ return m;
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari16x8_t ShuffleHi16(Scalari16x8_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari16x8_t m = aM;
+ m.i16[4 + 0] = aM.i16[4 + i3];
+ m.i16[4 + 1] = aM.i16[4 + i2];
+ m.i16[4 + 2] = aM.i16[4 + i1];
+ m.i16[4 + 3] = aM.i16[4 + i0];
+ return m;
+}
+
+template<int8_t aIndexLo, int8_t aIndexHi>
+inline Scalaru16x8_t Splat16(Scalaru16x8_t aM)
+{
+ AssertIndex<aIndexLo>();
+ AssertIndex<aIndexHi>();
+ Scalaru16x8_t m;
+ int16_t chosenValueLo = aM.u16[aIndexLo];
+ m.u16[0] = chosenValueLo;
+ m.u16[1] = chosenValueLo;
+ m.u16[2] = chosenValueLo;
+ m.u16[3] = chosenValueLo;
+ int16_t chosenValueHi = aM.u16[4 + aIndexHi];
+ m.u16[4] = chosenValueHi;
+ m.u16[5] = chosenValueHi;
+ m.u16[6] = chosenValueHi;
+ m.u16[7] = chosenValueHi;
+ return m;
+}
+
+inline Scalaru8x16_t
+InterleaveLo8(Scalaru8x16_t m1, Scalaru8x16_t m2)
+{
+ return From8<Scalaru8x16_t>(m1.u8[0], m2.u8[0], m1.u8[1], m2.u8[1],
+ m1.u8[2], m2.u8[2], m1.u8[3], m2.u8[3],
+ m1.u8[4], m2.u8[4], m1.u8[5], m2.u8[5],
+ m1.u8[6], m2.u8[6], m1.u8[7], m2.u8[7]);
+}
+
+inline Scalaru8x16_t
+InterleaveHi8(Scalaru8x16_t m1, Scalaru8x16_t m2)
+{
+ return From8<Scalaru8x16_t>(m1.u8[8+0], m2.u8[8+0], m1.u8[8+1], m2.u8[8+1],
+ m1.u8[8+2], m2.u8[8+2], m1.u8[8+3], m2.u8[8+3],
+ m1.u8[8+4], m2.u8[8+4], m1.u8[8+5], m2.u8[8+5],
+ m1.u8[8+6], m2.u8[8+6], m1.u8[8+7], m2.u8[8+7]);
+}
+
+inline Scalaru16x8_t
+InterleaveLo16(Scalaru16x8_t m1, Scalaru16x8_t m2)
+{
+ return FromU16<Scalaru16x8_t>(m1.u16[0], m2.u16[0], m1.u16[1], m2.u16[1],
+ m1.u16[2], m2.u16[2], m1.u16[3], m2.u16[3]);
+}
+
+inline Scalaru16x8_t
+InterleaveHi16(Scalaru16x8_t m1, Scalaru16x8_t m2)
+{
+ return FromU16<Scalaru16x8_t>(m1.u16[4], m2.u16[4], m1.u16[5], m2.u16[5],
+ m1.u16[6], m2.u16[6], m1.u16[7], m2.u16[7]);
+}
+
+inline Scalari32x4_t
+InterleaveLo32(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ return From32<Scalari32x4_t>(m1.i32[0], m2.i32[0], m1.i32[1], m2.i32[1]);
+}
+
+inline Scalari16x8_t
+UnpackLo8x8ToI16x8(Scalaru8x16_t aM)
+{
+ Scalari16x8_t m;
+ m.i16[0] = aM.u8[0];
+ m.i16[1] = aM.u8[1];
+ m.i16[2] = aM.u8[2];
+ m.i16[3] = aM.u8[3];
+ m.i16[4] = aM.u8[4];
+ m.i16[5] = aM.u8[5];
+ m.i16[6] = aM.u8[6];
+ m.i16[7] = aM.u8[7];
+ return m;
+}
+
+inline Scalari16x8_t
+UnpackHi8x8ToI16x8(Scalaru8x16_t aM)
+{
+ Scalari16x8_t m;
+ m.i16[0] = aM.u8[8+0];
+ m.i16[1] = aM.u8[8+1];
+ m.i16[2] = aM.u8[8+2];
+ m.i16[3] = aM.u8[8+3];
+ m.i16[4] = aM.u8[8+4];
+ m.i16[5] = aM.u8[8+5];
+ m.i16[6] = aM.u8[8+6];
+ m.i16[7] = aM.u8[8+7];
+ return m;
+}
+
+inline Scalaru16x8_t
+UnpackLo8x8ToU16x8(Scalaru8x16_t aM)
+{
+ return FromU16<Scalaru16x8_t>(uint16_t(aM.u8[0]), uint16_t(aM.u8[1]), uint16_t(aM.u8[2]), uint16_t(aM.u8[3]),
+ uint16_t(aM.u8[4]), uint16_t(aM.u8[5]), uint16_t(aM.u8[6]), uint16_t(aM.u8[7]));
+}
+
+inline Scalaru16x8_t
+UnpackHi8x8ToU16x8(Scalaru8x16_t aM)
+{
+ return FromU16<Scalaru16x8_t>(aM.u8[8+0], aM.u8[8+1], aM.u8[8+2], aM.u8[8+3],
+ aM.u8[8+4], aM.u8[8+5], aM.u8[8+6], aM.u8[8+7]);
+}
+
+template<uint8_t aNumBytes>
+inline Scalaru8x16_t
+Rotate8(Scalaru8x16_t a1234, Scalaru8x16_t a5678)
+{
+ Scalaru8x16_t m;
+ for (uint8_t i = 0; i < 16; i++) {
+ uint8_t sourceByte = i + aNumBytes;
+ m.u8[i] = sourceByte < 16 ? a1234.u8[sourceByte] : a5678.u8[sourceByte - 16];
+ }
+ return m;
+}
+
+template<typename T>
+inline int16_t
+SaturateTo16(T a)
+{
+ return int16_t(a >= INT16_MIN ? (a <= INT16_MAX ? a : INT16_MAX) : INT16_MIN);
+}
+
+inline Scalari16x8_t
+PackAndSaturate32To16(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ Scalari16x8_t m;
+ m.i16[0] = SaturateTo16(m1.i32[0]);
+ m.i16[1] = SaturateTo16(m1.i32[1]);
+ m.i16[2] = SaturateTo16(m1.i32[2]);
+ m.i16[3] = SaturateTo16(m1.i32[3]);
+ m.i16[4] = SaturateTo16(m2.i32[0]);
+ m.i16[5] = SaturateTo16(m2.i32[1]);
+ m.i16[6] = SaturateTo16(m2.i32[2]);
+ m.i16[7] = SaturateTo16(m2.i32[3]);
+ return m;
+}
+
+template<typename T>
+inline uint16_t
+SaturateToU16(T a)
+{
+ return uint16_t(umin(a & -(a >= 0), INT16_MAX));
+}
+
+inline Scalaru16x8_t
+PackAndSaturate32ToU16(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ Scalaru16x8_t m;
+ m.u16[0] = SaturateToU16(m1.i32[0]);
+ m.u16[1] = SaturateToU16(m1.i32[1]);
+ m.u16[2] = SaturateToU16(m1.i32[2]);
+ m.u16[3] = SaturateToU16(m1.i32[3]);
+ m.u16[4] = SaturateToU16(m2.i32[0]);
+ m.u16[5] = SaturateToU16(m2.i32[1]);
+ m.u16[6] = SaturateToU16(m2.i32[2]);
+ m.u16[7] = SaturateToU16(m2.i32[3]);
+ return m;
+}
+
+template<typename T>
+inline uint8_t
+SaturateTo8(T a)
+{
+ return uint8_t(umin(a & -(a >= 0), 255));
+}
+
+inline Scalaru8x16_t
+PackAndSaturate32To8(Scalari32x4_t m1, Scalari32x4_t m2, Scalari32x4_t m3, const Scalari32x4_t& m4)
+{
+ Scalaru8x16_t m;
+ m.u8[0] = SaturateTo8(m1.i32[0]);
+ m.u8[1] = SaturateTo8(m1.i32[1]);
+ m.u8[2] = SaturateTo8(m1.i32[2]);
+ m.u8[3] = SaturateTo8(m1.i32[3]);
+ m.u8[4] = SaturateTo8(m2.i32[0]);
+ m.u8[5] = SaturateTo8(m2.i32[1]);
+ m.u8[6] = SaturateTo8(m2.i32[2]);
+ m.u8[7] = SaturateTo8(m2.i32[3]);
+ m.u8[8] = SaturateTo8(m3.i32[0]);
+ m.u8[9] = SaturateTo8(m3.i32[1]);
+ m.u8[10] = SaturateTo8(m3.i32[2]);
+ m.u8[11] = SaturateTo8(m3.i32[3]);
+ m.u8[12] = SaturateTo8(m4.i32[0]);
+ m.u8[13] = SaturateTo8(m4.i32[1]);
+ m.u8[14] = SaturateTo8(m4.i32[2]);
+ m.u8[15] = SaturateTo8(m4.i32[3]);
+ return m;
+}
+
+inline Scalaru8x16_t
+PackAndSaturate16To8(Scalari16x8_t m1, Scalari16x8_t m2)
+{
+ Scalaru8x16_t m;
+ m.u8[0] = SaturateTo8(m1.i16[0]);
+ m.u8[1] = SaturateTo8(m1.i16[1]);
+ m.u8[2] = SaturateTo8(m1.i16[2]);
+ m.u8[3] = SaturateTo8(m1.i16[3]);
+ m.u8[4] = SaturateTo8(m1.i16[4]);
+ m.u8[5] = SaturateTo8(m1.i16[5]);
+ m.u8[6] = SaturateTo8(m1.i16[6]);
+ m.u8[7] = SaturateTo8(m1.i16[7]);
+ m.u8[8] = SaturateTo8(m2.i16[0]);
+ m.u8[9] = SaturateTo8(m2.i16[1]);
+ m.u8[10] = SaturateTo8(m2.i16[2]);
+ m.u8[11] = SaturateTo8(m2.i16[3]);
+ m.u8[12] = SaturateTo8(m2.i16[4]);
+ m.u8[13] = SaturateTo8(m2.i16[5]);
+ m.u8[14] = SaturateTo8(m2.i16[6]);
+ m.u8[15] = SaturateTo8(m2.i16[7]);
+ return m;
+}
+
+// Fast approximate division by 255. It has the property that
+// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
+// But it only uses two adds and two shifts instead of an
+// integer division (which is expensive on many processors).
+//
+// equivalent to v/255
+template<class B, class A>
+inline B FastDivideBy255(A v)
+{
+ return ((v << 8) + v + 255) >> 16;
+}
+
+inline Scalaru16x8_t
+FastDivideBy255_16(Scalaru16x8_t m)
+{
+ return FromU16<Scalaru16x8_t>(FastDivideBy255<uint16_t>(int32_t(m.u16[0])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[1])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[2])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[3])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[4])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[5])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[6])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[7])));
+}
+
+inline Scalari32x4_t
+FastDivideBy255(Scalari32x4_t m)
+{
+ return From32<Scalari32x4_t>(FastDivideBy255<int32_t>(m.i32[0]),
+ FastDivideBy255<int32_t>(m.i32[1]),
+ FastDivideBy255<int32_t>(m.i32[2]),
+ FastDivideBy255<int32_t>(m.i32[3]));
+}
+
+inline Scalaru8x16_t
+Pick(Scalaru8x16_t mask, Scalaru8x16_t a, Scalaru8x16_t b)
+{
+ return From8<Scalaru8x16_t>((a.u8[0] & (~mask.u8[0])) | (b.u8[0] & mask.u8[0]),
+ (a.u8[1] & (~mask.u8[1])) | (b.u8[1] & mask.u8[1]),
+ (a.u8[2] & (~mask.u8[2])) | (b.u8[2] & mask.u8[2]),
+ (a.u8[3] & (~mask.u8[3])) | (b.u8[3] & mask.u8[3]),
+ (a.u8[4] & (~mask.u8[4])) | (b.u8[4] & mask.u8[4]),
+ (a.u8[5] & (~mask.u8[5])) | (b.u8[5] & mask.u8[5]),
+ (a.u8[6] & (~mask.u8[6])) | (b.u8[6] & mask.u8[6]),
+ (a.u8[7] & (~mask.u8[7])) | (b.u8[7] & mask.u8[7]),
+ (a.u8[8+0] & (~mask.u8[8+0])) | (b.u8[8+0] & mask.u8[8+0]),
+ (a.u8[8+1] & (~mask.u8[8+1])) | (b.u8[8+1] & mask.u8[8+1]),
+ (a.u8[8+2] & (~mask.u8[8+2])) | (b.u8[8+2] & mask.u8[8+2]),
+ (a.u8[8+3] & (~mask.u8[8+3])) | (b.u8[8+3] & mask.u8[8+3]),
+ (a.u8[8+4] & (~mask.u8[8+4])) | (b.u8[8+4] & mask.u8[8+4]),
+ (a.u8[8+5] & (~mask.u8[8+5])) | (b.u8[8+5] & mask.u8[8+5]),
+ (a.u8[8+6] & (~mask.u8[8+6])) | (b.u8[8+6] & mask.u8[8+6]),
+ (a.u8[8+7] & (~mask.u8[8+7])) | (b.u8[8+7] & mask.u8[8+7]));
+}
+
+inline Scalari32x4_t
+Pick(Scalari32x4_t mask, Scalari32x4_t a, Scalari32x4_t b)
+{
+ return From32<Scalari32x4_t>((a.i32[0] & (~mask.i32[0])) | (b.i32[0] & mask.i32[0]),
+ (a.i32[1] & (~mask.i32[1])) | (b.i32[1] & mask.i32[1]),
+ (a.i32[2] & (~mask.i32[2])) | (b.i32[2] & mask.i32[2]),
+ (a.i32[3] & (~mask.i32[3])) | (b.i32[3] & mask.i32[3]));
+}
+
+inline Scalarf32x4_t MixF32(Scalarf32x4_t a, Scalarf32x4_t b, float t)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] + (b.f32[0] - a.f32[0]) * t,
+ a.f32[1] + (b.f32[1] - a.f32[1]) * t,
+ a.f32[2] + (b.f32[2] - a.f32[2]) * t,
+ a.f32[3] + (b.f32[3] - a.f32[3]) * t);
+}
+
+inline Scalarf32x4_t WSumF32(Scalarf32x4_t a, Scalarf32x4_t b, float wa, float wb)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] * wa + b.f32[0] * wb,
+ a.f32[1] * wa + b.f32[1] * wb,
+ a.f32[2] * wa + b.f32[2] * wb,
+ a.f32[3] * wa + b.f32[3] * wb);
+}
+
+inline Scalarf32x4_t AbsF32(Scalarf32x4_t a)
+{
+ return FromF32<Scalarf32x4_t>(fabs(a.f32[0]),
+ fabs(a.f32[1]),
+ fabs(a.f32[2]),
+ fabs(a.f32[3]));
+}
+
+inline Scalarf32x4_t AddF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] + b.f32[0],
+ a.f32[1] + b.f32[1],
+ a.f32[2] + b.f32[2],
+ a.f32[3] + b.f32[3]);
+}
+
+inline Scalarf32x4_t MulF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] * b.f32[0],
+ a.f32[1] * b.f32[1],
+ a.f32[2] * b.f32[2],
+ a.f32[3] * b.f32[3]);
+}
+
+inline Scalarf32x4_t DivF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] / b.f32[0],
+ a.f32[1] / b.f32[1],
+ a.f32[2] / b.f32[2],
+ a.f32[3] / b.f32[3]);
+}
+
+template<uint8_t aIndex>
+inline Scalarf32x4_t SplatF32(Scalarf32x4_t m)
+{
+ AssertIndex<aIndex>();
+ return FromF32<Scalarf32x4_t>(m.f32[aIndex],
+ m.f32[aIndex],
+ m.f32[aIndex],
+ m.f32[aIndex]);
+}
+
+inline Scalari32x4_t F32ToI32(Scalarf32x4_t m)
+{
+ return From32<Scalari32x4_t>(int32_t(floor(m.f32[0] + 0.5f)),
+ int32_t(floor(m.f32[1] + 0.5f)),
+ int32_t(floor(m.f32[2] + 0.5f)),
+ int32_t(floor(m.f32[3] + 0.5f)));
+}
+
+#ifdef SIMD_COMPILE_SSE2
+
+// SSE2
+
+template<>
+inline __m128i
+Load8<__m128i>(const uint8_t* aSource)
+{
+ return _mm_load_si128((const __m128i*)aSource);
+}
+
+inline void Store8(uint8_t* aTarget, __m128i aM)
+{
+ _mm_store_si128((__m128i*)aTarget, aM);
+}
+
+template<>
+inline __m128i FromZero8<__m128i>()
+{
+ return _mm_setzero_si128();
+}
+
+template<>
+inline __m128i From8<__m128i>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+{
+ return _mm_setr_epi16((b << 8) + a, (d << 8) + c, (e << 8) + f, (h << 8) + g,
+ (j << 8) + i, (l << 8) + k, (m << 8) + n, (p << 8) + o);
+}
+
+template<>
+inline __m128i FromI16<__m128i>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h)
+{
+ return _mm_setr_epi16(a, b, c, d, e, f, g, h);
+}
+
+template<>
+inline __m128i FromU16<__m128i>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
+{
+ return _mm_setr_epi16(a, b, c, d, e, f, g, h);
+}
+
+template<>
+inline __m128i FromI16<__m128i>(int16_t a)
+{
+ return _mm_set1_epi16(a);
+}
+
+template<>
+inline __m128i FromU16<__m128i>(uint16_t a)
+{
+ return _mm_set1_epi16((int16_t)a);
+}
+
+template<>
+inline __m128i From32<__m128i>(int32_t a, int32_t b, int32_t c, int32_t d)
+{
+ return _mm_setr_epi32(a, b, c, d);
+}
+
+template<>
+inline __m128i From32<__m128i>(int32_t a)
+{
+ return _mm_set1_epi32(a);
+}
+
+template<>
+inline __m128 FromF32<__m128>(float a, float b, float c, float d)
+{
+ return _mm_setr_ps(a, b, c, d);
+}
+
+template<>
+inline __m128 FromF32<__m128>(float a)
+{
+ return _mm_set1_ps(a);
+}
+
+template<int32_t aNumberOfBits>
+inline __m128i ShiftRight16(__m128i aM)
+{
+ return _mm_srli_epi16(aM, aNumberOfBits);
+}
+
+template<int32_t aNumberOfBits>
+inline __m128i ShiftRight32(__m128i aM)
+{
+ return _mm_srai_epi32(aM, aNumberOfBits);
+}
+
+inline __m128i Add16(__m128i aM1, __m128i aM2)
+{
+ return _mm_add_epi16(aM1, aM2);
+}
+
+inline __m128i Add32(__m128i aM1, __m128i aM2)
+{
+ return _mm_add_epi32(aM1, aM2);
+}
+
+inline __m128i Sub16(__m128i aM1, __m128i aM2)
+{
+ return _mm_sub_epi16(aM1, aM2);
+}
+
+inline __m128i Sub32(__m128i aM1, __m128i aM2)
+{
+ return _mm_sub_epi32(aM1, aM2);
+}
+
+inline __m128i Min8(__m128i aM1, __m128i aM2)
+{
+ return _mm_min_epu8(aM1, aM2);
+}
+
+inline __m128i Max8(__m128i aM1, __m128i aM2)
+{
+ return _mm_max_epu8(aM1, aM2);
+}
+
+inline __m128i Min32(__m128i aM1, __m128i aM2)
+{
+ __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2);
+ __m128i m1_greater_than_m2 = _mm_cmpgt_epi32(aM1, aM2);
+ return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m1_greater_than_m2));
+}
+
+inline __m128i Max32(__m128i aM1, __m128i aM2)
+{
+ __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2);
+ __m128i m2_greater_than_m1 = _mm_cmpgt_epi32(aM2, aM1);
+ return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m2_greater_than_m1));
+}
+
+inline __m128i Mul16(__m128i aM1, __m128i aM2)
+{
+ return _mm_mullo_epi16(aM1, aM2);
+}
+
+inline __m128i MulU16(__m128i aM1, __m128i aM2)
+{
+ return _mm_mullo_epi16(aM1, aM2);
+}
+
+inline void Mul16x4x2x2To32x4x2(__m128i aFactorsA1B1,
+ __m128i aFactorsA2B2,
+ __m128i& aProductA,
+ __m128i& aProductB)
+{
+ __m128i prodAB_lo = _mm_mullo_epi16(aFactorsA1B1, aFactorsA2B2);
+ __m128i prodAB_hi = _mm_mulhi_epi16(aFactorsA1B1, aFactorsA2B2);
+ aProductA = _mm_unpacklo_epi16(prodAB_lo, prodAB_hi);
+ aProductB = _mm_unpackhi_epi16(prodAB_lo, prodAB_hi);
+}
+
+inline __m128i MulAdd16x8x2To32x4(__m128i aFactorsA,
+ __m128i aFactorsB)
+{
+ return _mm_madd_epi16(aFactorsA, aFactorsB);
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i Shuffle32(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shuffle_epi32(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i ShuffleLo16(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shufflelo_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i ShuffleHi16(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shufflehi_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t aIndex>
+inline __m128i Splat32(__m128i aM)
+{
+ return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM);
+}
+
+template<int8_t aIndex>
+inline __m128i Splat32On8(__m128i aM)
+{
+ return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM);
+}
+
+template<int8_t aIndexLo, int8_t aIndexHi>
+inline __m128i Splat16(__m128i aM)
+{
+ AssertIndex<aIndexLo>();
+ AssertIndex<aIndexHi>();
+ return ShuffleHi16<aIndexHi,aIndexHi,aIndexHi,aIndexHi>(
+ ShuffleLo16<aIndexLo,aIndexLo,aIndexLo,aIndexLo>(aM));
+}
+
+inline __m128i
+UnpackLo8x8ToI16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpacklo_epi8(m, zero);
+}
+
+inline __m128i
+UnpackHi8x8ToI16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpackhi_epi8(m, zero);
+}
+
+inline __m128i
+UnpackLo8x8ToU16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpacklo_epi8(m, zero);
+}
+
+inline __m128i
+UnpackHi8x8ToU16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpackhi_epi8(m, zero);
+}
+
+inline __m128i
+InterleaveLo8(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi8(m1, m2);
+}
+
+inline __m128i
+InterleaveHi8(__m128i m1, __m128i m2)
+{
+ return _mm_unpackhi_epi8(m1, m2);
+}
+
+inline __m128i
+InterleaveLo16(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi16(m1, m2);
+}
+
+inline __m128i
+InterleaveHi16(__m128i m1, __m128i m2)
+{
+ return _mm_unpackhi_epi16(m1, m2);
+}
+
+inline __m128i
+InterleaveLo32(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi32(m1, m2);
+}
+
+template<uint8_t aNumBytes>
+inline __m128i
+Rotate8(__m128i a1234, __m128i a5678)
+{
+ return _mm_or_si128(_mm_srli_si128(a1234, aNumBytes), _mm_slli_si128(a5678, 16 - aNumBytes));
+}
+
+inline __m128i
+PackAndSaturate32To16(__m128i m1, __m128i m2)
+{
+ return _mm_packs_epi32(m1, m2);
+}
+
+inline __m128i
+PackAndSaturate32ToU16(__m128i m1, __m128i m2)
+{
+ return _mm_packs_epi32(m1, m2);
+}
+
+inline __m128i
+PackAndSaturate32To8(__m128i m1, __m128i m2, __m128i m3, const __m128i& m4)
+{
+ // Pack into 8 16bit signed integers (saturating).
+ __m128i m12 = _mm_packs_epi32(m1, m2);
+ __m128i m34 = _mm_packs_epi32(m3, m4);
+
+ // Pack into 16 8bit unsigned integers (saturating).
+ return _mm_packus_epi16(m12, m34);
+}
+
+inline __m128i
+PackAndSaturate16To8(__m128i m1, __m128i m2)
+{
+ // Pack into 16 8bit unsigned integers (saturating).
+ return _mm_packus_epi16(m1, m2);
+}
+
+inline __m128i
+FastDivideBy255(__m128i m)
+{
+ // v = m << 8
+ __m128i v = _mm_slli_epi32(m, 8);
+ // v = v + (m + (255,255,255,255))
+ v = _mm_add_epi32(v, _mm_add_epi32(m, _mm_set1_epi32(255)));
+ // v = v >> 16
+ return _mm_srai_epi32(v, 16);
+}
+
+inline __m128i
+FastDivideBy255_16(__m128i m)
+{
+ __m128i zero = _mm_set1_epi16(0);
+ __m128i lo = _mm_unpacklo_epi16(m, zero);
+ __m128i hi = _mm_unpackhi_epi16(m, zero);
+ return _mm_packs_epi32(FastDivideBy255(lo), FastDivideBy255(hi));
+}
+
+inline __m128i
+Pick(__m128i mask, __m128i a, __m128i b)
+{
+ return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b));
+}
+
+inline __m128 MixF32(__m128 a, __m128 b, float t)
+{
+ return _mm_add_ps(a, _mm_mul_ps(_mm_sub_ps(b, a), _mm_set1_ps(t)));
+}
+
+inline __m128 WSumF32(__m128 a, __m128 b, float wa, float wb)
+{
+ return _mm_add_ps(_mm_mul_ps(a, _mm_set1_ps(wa)), _mm_mul_ps(b, _mm_set1_ps(wb)));
+}
+
+inline __m128 AbsF32(__m128 a)
+{
+ return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), a), a);
+}
+
+inline __m128 AddF32(__m128 a, __m128 b)
+{
+ return _mm_add_ps(a, b);
+}
+
+inline __m128 MulF32(__m128 a, __m128 b)
+{
+ return _mm_mul_ps(a, b);
+}
+
+inline __m128 DivF32(__m128 a, __m128 b)
+{
+ return _mm_div_ps(a, b);
+}
+
+template<uint8_t aIndex>
+inline __m128 SplatF32(__m128 m)
+{
+ AssertIndex<aIndex>();
+ return _mm_shuffle_ps(m, m, _MM_SHUFFLE(aIndex, aIndex, aIndex, aIndex));
+}
+
+inline __m128i F32ToI32(__m128 m)
+{
+ return _mm_cvtps_epi32(m);
+}
+
+#endif // SIMD_COMPILE_SSE2
+
+} // namespace simd
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_SIMD_H_
diff --git a/system/graphics/2d/SSEHelpers.h b/system/graphics/2d/SSEHelpers.h
new file mode 100644
index 000000000..61b53d86e
--- /dev/null
+++ b/system/graphics/2d/SSEHelpers.h
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+/* Before Nehalem _mm_loadu_si128 could be very slow, this trick is a little
+ * faster. Once enough people are on architectures where _mm_loadu_si128 is
+ * fast we can migrate to it.
+ */
+MOZ_ALWAYS_INLINE __m128i loadUnaligned128(const __m128i *aSource)
+{
+ // Yes! We use uninitialized memory here, we'll overwrite it though!
+ __m128 res = _mm_loadl_pi(_mm_set1_ps(0), (const __m64*)aSource);
+ return _mm_castps_si128(_mm_loadh_pi(res, ((const __m64*)(aSource)) + 1));
+}
diff --git a/system/graphics/2d/SVGTurbulenceRenderer-inl.h b/system/graphics/2d/SVGTurbulenceRenderer-inl.h
new file mode 100644
index 000000000..7b18903e8
--- /dev/null
+++ b/system/graphics/2d/SVGTurbulenceRenderer-inl.h
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include "Filters.h"
+#include "SIMD.h"
+
+namespace mozilla {
+namespace gfx {
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+class SVGTurbulenceRenderer
+{
+public:
+ SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect &aTileRect);
+
+ already_AddRefed<DataSourceSurface> Render(const IntSize &aSize, const Point &aOffset) const;
+
+private:
+ /* The turbulence calculation code is an adapted version of what
+ appears in the SVG 1.1 specification:
+ http://www.w3.org/TR/SVG11/filters.html#feTurbulence
+ */
+
+ struct StitchInfo {
+ int32_t width; // How much to subtract to wrap for stitching.
+ int32_t height;
+ int32_t wrapX; // Minimum value to wrap.
+ int32_t wrapY;
+ };
+
+ const static int sBSize = 0x100;
+ const static int sBM = 0xff;
+ void InitFromSeed(int32_t aSeed);
+ void AdjustBaseFrequencyForStitch(const Rect &aTileRect);
+ IntPoint AdjustForStitch(IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const;
+ StitchInfo CreateStitchInfo(const Rect &aTileRect) const;
+ f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const;
+ i32x4_t Turbulence(const Point &aPoint) const;
+ Point EquivalentNonNegativeOffset(const Point &aOffset) const;
+
+ Size mBaseFrequency;
+ int32_t mNumOctaves;
+ StitchInfo mStitchInfo;
+ bool mStitchable;
+ TurbulenceType mType;
+ uint8_t mLatticeSelector[sBSize];
+ f32x4_t mGradient[sBSize][2];
+};
+
+namespace {
+
+struct RandomNumberSource
+{
+ explicit RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {}
+ int32_t Next() { mLast = Random(mLast); return mLast; }
+
+private:
+ static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */
+ static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */
+ static const int32_t RAND_Q = 127773; /* m / a */
+ static const int32_t RAND_R = 2836; /* m % a */
+
+ /* Produces results in the range [1, 2**31 - 2].
+ Algorithm is: r = (a * r) mod m
+ where a = 16807 and m = 2**31 - 1 = 2147483647
+ See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
+ To test: the algorithm should produce the result 1043618065
+ as the 10,000th generated number if the original seed is 1.
+ */
+
+ static int32_t
+ SetupSeed(int32_t aSeed) {
+ if (aSeed <= 0)
+ aSeed = -(aSeed % (RAND_M - 1)) + 1;
+ if (aSeed > RAND_M - 1)
+ aSeed = RAND_M - 1;
+ return aSeed;
+ }
+
+ static int32_t
+ Random(int32_t aSeed)
+ {
+ int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q);
+ if (result <= 0)
+ result += RAND_M;
+ return result;
+ }
+
+ int32_t mLast;
+};
+
+} // unnamed namespace
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect &aTileRect)
+ : mBaseFrequency(aBaseFrequency)
+ , mNumOctaves(aNumOctaves)
+{
+ InitFromSeed(aSeed);
+ if (Stitch) {
+ AdjustBaseFrequencyForStitch(aTileRect);
+ mStitchInfo = CreateStitchInfo(aTileRect);
+ }
+}
+
+template<typename T>
+static void
+Swap(T& a, T& b) {
+ T c = a;
+ a = b;
+ b = c;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+void
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::InitFromSeed(int32_t aSeed)
+{
+ RandomNumberSource rand(aSeed);
+
+ float gradient[4][sBSize][2];
+ for (int32_t k = 0; k < 4; k++) {
+ for (int32_t i = 0; i < sBSize; i++) {
+ float a, b;
+ do {
+ a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ } while (a == 0 && b == 0);
+ float s = sqrt(a * a + b * b);
+ gradient[k][i][0] = a / s;
+ gradient[k][i][1] = b / s;
+ }
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ mLatticeSelector[i] = i;
+ }
+ for (int32_t i1 = sBSize - 1; i1 > 0; i1--) {
+ int32_t i2 = rand.Next() % sBSize;
+ Swap(mLatticeSelector[i1], mLatticeSelector[i2]);
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ // Contrary to the code in the spec, we build the first lattice selector
+ // lookup into mGradient so that we don't need to do it again for every
+ // pixel.
+ // We also change the order of the gradient indexing so that we can process
+ // all four color channels at the same time.
+ uint8_t j = mLatticeSelector[i];
+ mGradient[i][0] = simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0],
+ gradient[0][j][0], gradient[3][j][0]);
+ mGradient[i][1] = simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1],
+ gradient[0][j][1], gradient[3][j][1]);
+ }
+}
+
+// Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer
+// and as close to aLength * aFreq as possible.
+static inline float
+AdjustForLength(float aFreq, float aLength)
+{
+ float lowFreq = floor(aLength * aFreq) / aLength;
+ float hiFreq = ceil(aLength * aFreq) / aLength;
+ if (aFreq / lowFreq < hiFreq / aFreq) {
+ return lowFreq;
+ }
+ return hiFreq;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+void
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustBaseFrequencyForStitch(const Rect &aTileRect)
+{
+ mBaseFrequency = Size(AdjustForLength(mBaseFrequency.width, aTileRect.width),
+ AdjustForLength(mBaseFrequency.height, aTileRect.height));
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+typename SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::StitchInfo
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::CreateStitchInfo(const Rect &aTileRect) const
+{
+ StitchInfo stitch;
+ stitch.width = int32_t(floorf(aTileRect.width * mBaseFrequency.width + 0.5f));
+ stitch.height = int32_t(floorf(aTileRect.height * mBaseFrequency.height + 0.5f));
+ stitch.wrapX = int32_t(aTileRect.x * mBaseFrequency.width) + stitch.width;
+ stitch.wrapY = int32_t(aTileRect.y * mBaseFrequency.height) + stitch.height;
+ return stitch;
+}
+
+static MOZ_ALWAYS_INLINE Float
+SCurve(Float t)
+{
+ return t * t * (3 - 2 * t);
+}
+
+static MOZ_ALWAYS_INLINE Point
+SCurve(Point t)
+{
+ return Point(SCurve(t.x), SCurve(t.y));
+}
+
+template<typename f32x4_t>
+static MOZ_ALWAYS_INLINE f32x4_t
+BiMix(const f32x4_t& aa, const f32x4_t& ab,
+ const f32x4_t& ba, const f32x4_t& bb, Point s)
+{
+ return simd::MixF32(simd::MixF32(aa, ab, s.x),
+ simd::MixF32(ba, bb, s.x), s.y);
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+IntPoint
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustForStitch(IntPoint aLatticePoint,
+ const StitchInfo& aStitchInfo) const
+{
+ if (Stitch) {
+ if (aLatticePoint.x >= aStitchInfo.wrapX) {
+ aLatticePoint.x -= aStitchInfo.width;
+ }
+ if (aLatticePoint.y >= aStitchInfo.wrapY) {
+ aLatticePoint.y -= aStitchInfo.height;
+ }
+ }
+ return aLatticePoint;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+f32x4_t
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Noise2(Point aVec, const StitchInfo& aStitchInfo) const
+{
+ // aVec is guaranteed to be non-negative, so casting to int32_t always
+ // rounds towards negative infinity.
+ IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y));
+ Point r = aVec - topLeftLatticePoint; // fractional offset
+
+ IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo);
+ IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo);
+
+ uint8_t i = mLatticeSelector[b0.x & sBM];
+ uint8_t j = mLatticeSelector[b1.x & sBM];
+
+ const f32x4_t* qua = mGradient[(i + b0.y) & sBM];
+ const f32x4_t* qub = mGradient[(i + b1.y) & sBM];
+ const f32x4_t* qva = mGradient[(j + b0.y) & sBM];
+ const f32x4_t* qvb = mGradient[(j + b1.y) & sBM];
+
+ return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y),
+ simd::WSumF32(qva[0], qva[1], r.x - 1.f, r.y),
+ simd::WSumF32(qub[0], qub[1], r.x, r.y - 1.f),
+ simd::WSumF32(qvb[0], qvb[1], r.x - 1.f, r.y - 1.f),
+ SCurve(r));
+}
+
+template<typename f32x4_t, typename i32x4_t, typename u8x16_t>
+static inline i32x4_t
+ColorToBGRA(f32x4_t aUnscaledUnpreFloat)
+{
+ // Color is an unpremultiplied float vector where 1.0f means white. We will
+ // convert it into an integer vector where 255 means white.
+ f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat);
+ f32x4_t scaledUnpreFloat = simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255));
+ i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat);
+
+ // Multiply all channels with alpha.
+ i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha));
+
+ // Use the premultiplied color channels and the unpremultiplied alpha channel.
+ i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1);
+ return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt);
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+i32x4_t
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Turbulence(const Point &aPoint) const
+{
+ StitchInfo stitchInfo = mStitchInfo;
+ f32x4_t sum = simd::FromF32<f32x4_t>(0);
+ Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height);
+ f32x4_t ratio = simd::FromF32<f32x4_t>(1);
+
+ for (int octave = 0; octave < mNumOctaves; octave++) {
+ f32x4_t thisOctave = Noise2(vec, stitchInfo);
+ if (Type == TURBULENCE_TYPE_TURBULENCE) {
+ thisOctave = simd::AbsF32(thisOctave);
+ }
+ sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio));
+ vec = vec * 2;
+ ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2));
+
+ if (Stitch) {
+ stitchInfo.width *= 2;
+ stitchInfo.wrapX *= 2;
+ stitchInfo.height *= 2;
+ stitchInfo.wrapY *= 2;
+ }
+ }
+
+ if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) {
+ sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)), simd::FromF32<f32x4_t>(2));
+ }
+ return ColorToBGRA<f32x4_t,i32x4_t,u8x16_t>(sum);
+}
+
+static inline Float
+MakeNonNegative(Float aValue, Float aIncrementSize)
+{
+ if (aValue >= 0) {
+ return aValue;
+ }
+ return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+Point
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::EquivalentNonNegativeOffset(const Point &aOffset) const
+{
+ Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height) :
+ Size(sBSize, sBSize);
+ Size repeatingSize(basePeriod.width / mBaseFrequency.width,
+ basePeriod.height / mBaseFrequency.height);
+ return Point(MakeNonNegative(aOffset.x, repeatingSize.width),
+ MakeNonNegative(aOffset.y, repeatingSize.height));
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+already_AddRefed<DataSourceSurface>
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Render(const IntSize &aSize, const Point &aOffset) const
+{
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* targetData = target->GetData();
+ uint32_t stride = target->Stride();
+
+ Point startOffset = EquivalentNonNegativeOffset(aOffset);
+
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t targIndex = y * stride + x * 4;
+ i32x4_t a = Turbulence(startOffset + Point(x, y));
+ i32x4_t b = Turbulence(startOffset + Point(x + 1, y));
+ i32x4_t c = Turbulence(startOffset + Point(x + 2, y));
+ i32x4_t d = Turbulence(startOffset + Point(x + 3, y));
+ u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d);
+ simd::Store8(&targetData[targIndex], result1234);
+ }
+ }
+
+ return target.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Scale.cpp b/system/graphics/2d/Scale.cpp
new file mode 100644
index 000000000..f1fec03d0
--- /dev/null
+++ b/system/graphics/2d/Scale.cpp
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Scale.h"
+
+#ifdef USE_SKIA
+#include "HelpersSkia.h"
+#include "skia/include/core/SkBitmap.h"
+#include "image_operations.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
+ uint8_t* dstData, int32_t dstWidth, int32_t dstHeight, int32_t dstStride,
+ SurfaceFormat format)
+{
+#ifdef USE_SKIA
+ SkBitmap imgSrc;
+ imgSrc.installPixels(MakeSkiaImageInfo(IntSize(srcWidth, srcHeight), format),
+ srcData, srcStride);
+
+ // Rescaler is compatible with 32 bpp only. Convert to RGB32 if needed.
+ if (imgSrc.colorType() != kBGRA_8888_SkColorType) {
+ imgSrc.copyTo(&imgSrc, kBGRA_8888_SkColorType);
+ }
+
+ // This returns an SkBitmap backed by dstData; since it also wrote to dstData,
+ // we don't need to look at that SkBitmap.
+ SkBitmap result = skia::ImageOperations::Resize(imgSrc,
+ skia::ImageOperations::RESIZE_BEST,
+ dstWidth, dstHeight,
+ dstData);
+
+ return !result.isNull();
+#else
+ return false;
+#endif
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/Scale.h b/system/graphics/2d/Scale.h
new file mode 100644
index 000000000..75465da38
--- /dev/null
+++ b/system/graphics/2d/Scale.h
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALE_H_
+#define MOZILLA_GFX_SCALE_H_
+
+#include "Types.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Scale an image using a high-quality filter.
+ *
+ * Synchronously scales an image and writes the output to the destination in
+ * 32-bit format. The destination must be pre-allocated by the caller.
+ *
+ * Returns true if scaling was successful, and false otherwise. Currently, this
+ * function is implemented using Skia. If Skia is not enabled when building,
+ * calling this function will always return false.
+ *
+ * IMPLEMTATION NOTES:
+ * This API is not currently easily hardware acceleratable. A better API might
+ * take a SourceSurface and return a SourceSurface; the Direct2D backend, for
+ * example, could simply set a status bit on a copy of the image, and use
+ * Direct2D's high-quality scaler at draw time.
+ */
+GFX2D_API bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
+ uint8_t* dstData, int32_t dstWidth, int32_t dstHeight, int32_t dstStride,
+ SurfaceFormat format);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BLUR_H_ */
diff --git a/system/graphics/2d/ScaleFactor.h b/system/graphics/2d/ScaleFactor.h
new file mode 100644
index 000000000..acf5bb739
--- /dev/null
+++ b/system/graphics/2d/ScaleFactor.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEFACTOR_H_
+#define MOZILLA_GFX_SCALEFACTOR_H_
+
+#include "mozilla/Attributes.h"
+
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace gfx {
+
+/*
+ * This class represents a scaling factor between two different pixel unit
+ * systems. This is effectively a type-safe float, intended to be used in
+ * combination with the known-type instances of gfx::Point, gfx::Rect, etc.
+ *
+ * This class is meant to be used in cases where a single scale applies to
+ * both the x and y axes. For cases where two diferent scales apply, use
+ * ScaleFactors2D.
+ */
+template<class src, class dst>
+struct ScaleFactor {
+ float scale;
+
+ constexpr ScaleFactor() : scale(1.0) {}
+ constexpr ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {}
+ explicit constexpr ScaleFactor(float aScale) : scale(aScale) {}
+
+ ScaleFactor<dst, src> Inverse() {
+ return ScaleFactor<dst, src>(1 / scale);
+ }
+
+ bool operator==(const ScaleFactor<src, dst>& aOther) const {
+ return scale == aOther.scale;
+ }
+
+ bool operator!=(const ScaleFactor<src, dst>& aOther) const {
+ return !(*this == aOther);
+ }
+
+ bool operator<(const ScaleFactor<src, dst>& aOther) const {
+ return scale < aOther.scale;
+ }
+
+ bool operator<=(const ScaleFactor<src, dst>& aOther) const {
+ return scale <= aOther.scale;
+ }
+
+ bool operator>(const ScaleFactor<src, dst>& aOther) const {
+ return scale > aOther.scale;
+ }
+
+ bool operator>=(const ScaleFactor<src, dst>& aOther) const {
+ return scale >= aOther.scale;
+ }
+
+ template<class other>
+ ScaleFactor<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
+ return ScaleFactor<other, dst>(scale / aOther.scale);
+ }
+
+ template<class other>
+ ScaleFactor<src, other> operator/(const ScaleFactor<other, dst>& aOther) const {
+ return ScaleFactor<src, other>(scale / aOther.scale);
+ }
+
+ template<class other>
+ ScaleFactor<src, other> operator*(const ScaleFactor<dst, other>& aOther) const {
+ return ScaleFactor<src, other>(scale * aOther.scale);
+ }
+
+ template<class other>
+ ScaleFactor<other, dst> operator*(const ScaleFactor<other, src>& aOther) const {
+ return ScaleFactor<other, dst>(scale * aOther.scale);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEFACTOR_H_ */
diff --git a/system/graphics/2d/ScaleFactors2D.h b/system/graphics/2d/ScaleFactors2D.h
new file mode 100644
index 000000000..6de32d7f2
--- /dev/null
+++ b/system/graphics/2d/ScaleFactors2D.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEFACTORS2D_H_
+#define MOZILLA_GFX_SCALEFACTORS2D_H_
+
+#include <ostream>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/gfx/ScaleFactor.h"
+
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace gfx {
+
+/*
+ * This class is like ScaleFactor, but allows different scales on the x and
+ * y axes.
+ */
+template<class src, class dst>
+struct ScaleFactors2D {
+ float xScale;
+ float yScale;
+
+ constexpr ScaleFactors2D() : xScale(1.0), yScale(1.0) {}
+ constexpr ScaleFactors2D(const ScaleFactors2D<src, dst>& aCopy)
+ : xScale(aCopy.xScale), yScale(aCopy.yScale) {}
+ constexpr ScaleFactors2D(float aXScale, float aYScale)
+ : xScale(aXScale), yScale(aYScale) {}
+ // Layout code often uses gfxSize to represent a pair of x/y scales.
+ explicit constexpr ScaleFactors2D(const gfxSize& aSize)
+ : xScale(aSize.width), yScale(aSize.height) {}
+
+ // "Upgrade" from a ScaleFactor.
+ // This is deliberately 'explicit' so that the treatment of a single scale
+ // number as both the x- and y-scale in a context where they are allowed to
+ // be different, is more visible.
+ explicit constexpr ScaleFactors2D(const ScaleFactor<src, dst>& aScale)
+ : xScale(aScale.scale), yScale(aScale.scale) {}
+
+ bool AreScalesSame() const {
+ return FuzzyEqualsMultiplicative(xScale, yScale);
+ }
+
+ // Convert to a ScaleFactor. Asserts that the scales are, in fact, equal.
+ ScaleFactor<src, dst> ToScaleFactor() const {
+ MOZ_ASSERT(AreScalesSame());
+ return ScaleFactor<src, dst>(xScale);
+ }
+
+ bool operator==(const ScaleFactors2D<src, dst>& aOther) const {
+ return xScale == aOther.xScale && yScale == aOther.yScale;
+ }
+
+ bool operator!=(const ScaleFactors2D<src, dst>& aOther) const {
+ return !(*this == aOther);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const ScaleFactors2D<src, dst>& aScale) {
+ if (aScale.AreScalesSame()) {
+ return aStream << aScale.xScale;
+ } else {
+ return aStream << '(' << aScale.xScale << ',' << aScale.yScale << ')';
+ }
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator/(const ScaleFactors2D<src, other>& aOther) const {
+ return ScaleFactors2D<other, dst>(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator/(const ScaleFactors2D<other, dst>& aOther) const {
+ return ScaleFactors2D<src, other>(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator*(const ScaleFactors2D<dst, other>& aOther) const {
+ return ScaleFactors2D<src, other>(xScale * aOther.xScale, yScale * aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator*(const ScaleFactors2D<other, src>& aOther) const {
+ return ScaleFactors2D<other, dst>(xScale * aOther.xScale, yScale * aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator*(const ScaleFactor<dst, other>& aOther) const {
+ return *this * ScaleFactors2D<dst, other>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator*(const ScaleFactor<other, src>& aOther) const {
+ return *this * ScaleFactors2D<other, src>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator/(const ScaleFactor<other, dst>& aOther) const {
+ return *this / ScaleFactors2D<other, dst>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
+ return *this / ScaleFactors2D<src, other>(aOther);
+ }
+
+ template<class other>
+ friend ScaleFactors2D<other, dst> operator*(const ScaleFactor<other, src>& aA,
+ const ScaleFactors2D<src, dst>& aB) {
+ return ScaleFactors2D<other, src>(aA) * aB;
+ }
+
+ template<class other>
+ friend ScaleFactors2D<other, src> operator/(const ScaleFactor<other, dst>& aA,
+ const ScaleFactors2D<src, dst>& aB) {
+ return ScaleFactors2D<other, src>(aA) / aB;
+ }
+
+ // Divide two scales of the same units, yielding a scale with no units,
+ // represented as a gfxSize. This can mean e.g. the cahnge in a particular
+ // scale from one frame to the next.
+ gfxSize operator/(const ScaleFactors2D& aOther) const {
+ return gfxSize(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEFACTORS2D_H_ */
diff --git a/system/graphics/2d/ScaledFontBase.cpp b/system/graphics/2d/ScaledFontBase.cpp
new file mode 100644
index 000000000..ca9b2a188
--- /dev/null
+++ b/system/graphics/2d/ScaledFontBase.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScaledFontBase.h"
+
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#endif
+
+#ifdef USE_CAIRO
+#include "PathCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
+#endif
+
+#include <vector>
+#include <cmath>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+ScaledFontBase::~ScaledFontBase()
+{
+#ifdef USE_SKIA
+ SkSafeUnref(mTypeface);
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_scaled_font_destroy(mScaledFont);
+#endif
+}
+
+ScaledFontBase::ScaledFontBase(Float aSize)
+ : mSize(aSize)
+{
+#ifdef USE_SKIA
+ mTypeface = nullptr;
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ mScaledFont = nullptr;
+#endif
+}
+
+#ifdef USE_CAIRO_SCALED_FONT
+bool
+ScaledFontBase::PopulateCairoScaledFont()
+{
+ cairo_font_face_t* cairoFontFace = GetCairoFontFace();
+ if (!cairoFontFace) {
+ return false;
+ }
+
+ cairo_matrix_t sizeMatrix;
+ cairo_matrix_t identityMatrix;
+
+ cairo_matrix_init_scale(&sizeMatrix, mSize, mSize);
+ cairo_matrix_init_identity(&identityMatrix);
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ mScaledFont = cairo_scaled_font_create(cairoFontFace, &sizeMatrix,
+ &identityMatrix, fontOptions);
+
+ cairo_font_options_destroy(fontOptions);
+ cairo_font_face_destroy(cairoFontFace);
+
+ return (cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS);
+}
+#endif
+
+#ifdef USE_SKIA
+SkPath
+ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer)
+{
+ SkTypeface *typeFace = GetSkTypeface();
+ MOZ_ASSERT(typeFace);
+
+ SkPaint paint;
+ paint.setTypeface(sk_ref_sp(typeFace));
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setTextSize(SkFloatToScalar(mSize));
+
+ std::vector<uint16_t> indices;
+ std::vector<SkPoint> offsets;
+ indices.resize(aBuffer.mNumGlyphs);
+ offsets.resize(aBuffer.mNumGlyphs);
+
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ indices[i] = aBuffer.mGlyphs[i].mIndex;
+ offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
+ offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
+ }
+
+ SkPath path;
+ paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
+ return path;
+}
+#endif
+
+already_AddRefed<Path>
+ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+#ifdef USE_SKIA
+ if (aTarget->GetBackendType() == BackendType::SKIA) {
+ SkPath path = GetSkiaPathForGlyphs(aBuffer);
+ return MakeAndAddRef<PathSkia>(path, FillRule::FILL_WINDING);
+ }
+#endif
+#ifdef USE_CAIRO
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
+ MOZ_ASSERT(mScaledFont);
+
+ DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
+ cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+
+ bool isNewContext = !ctx;
+ if (!ctx) {
+ ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
+ cairo_set_matrix(ctx, &mat);
+ }
+
+ cairo_set_scaled_font(ctx, mScaledFont);
+
+ // Convert our GlyphBuffer into an array of Cairo glyphs.
+ std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
+ for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
+ glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
+ glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
+ glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ cairo_new_path(ctx);
+
+ cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+ RefPtr<PathCairo> newPath = new PathCairo(ctx);
+ if (isNewContext) {
+ cairo_destroy(ctx);
+ }
+
+ return newPath.forget();
+ }
+#endif
+ return nullptr;
+}
+
+void
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
+{
+ BackendType backendType = aBuilder->GetBackendType();
+#ifdef USE_SKIA
+ if (backendType == BackendType::SKIA) {
+ PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder);
+ builder->AppendPath(GetSkiaPathForGlyphs(aBuffer));
+ return;
+ }
+#endif
+#ifdef USE_CAIRO
+ if (backendType == BackendType::CAIRO) {
+ MOZ_ASSERT(mScaledFont);
+
+ PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
+ cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+
+ if (aTransformHint) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(*aTransformHint, mat);
+ cairo_set_matrix(ctx, &mat);
+ }
+
+ // Convert our GlyphBuffer into an array of Cairo glyphs.
+ std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
+ for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
+ glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
+ glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
+ glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ cairo_set_scaled_font(ctx, mScaledFont);
+ cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+ RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
+ cairo_destroy(ctx);
+
+ cairoPath->AppendPathToBuilder(builder);
+ return;
+ }
+#endif
+}
+
+void
+ScaledFontBase::GetGlyphDesignMetrics(const uint16_t* aGlyphs, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+#ifdef USE_CAIRO_SCALED_FONT
+ if (mScaledFont) {
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ cairo_glyph_t glyph;
+ cairo_text_extents_t extents;
+ glyph.index = aGlyphs[i];
+ glyph.x = 0;
+ glyph.y = 0;
+
+ cairo_scaled_font_glyph_extents(mScaledFont, &glyph, 1, &extents);
+
+ aGlyphMetrics[i].mXBearing = extents.x_bearing;
+ aGlyphMetrics[i].mXAdvance = extents.x_advance;
+ aGlyphMetrics[i].mYBearing = extents.y_bearing;
+ aGlyphMetrics[i].mYAdvance = extents.y_advance;
+ aGlyphMetrics[i].mWidth = extents.width;
+ aGlyphMetrics[i].mHeight = extents.height;
+
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_scaled_font_get_font_options(mScaledFont, options);
+
+ if (cairo_font_options_get_antialias(options) != CAIRO_ANTIALIAS_NONE) {
+ if (cairo_scaled_font_get_type(mScaledFont) == CAIRO_FONT_TYPE_WIN32) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth -= 3.0f;
+ aGlyphMetrics[i].mXBearing += 1.0f;
+ }
+ }
+#if defined(MOZ2D_HAS_MOZ_CAIRO) && defined(CAIRO_HAS_DWRITE_FONT)
+ else if (cairo_scaled_font_get_type(mScaledFont) == CAIRO_FONT_TYPE_DWRITE) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth -= 2.0f;
+ aGlyphMetrics[i].mXBearing += 1.0f;
+ }
+ }
+#endif
+ }
+ cairo_font_options_destroy(options);
+ }
+
+ }
+#endif
+
+ // Don't know how to get the glyph metrics...
+ MOZ_CRASH("The specific backend type is not supported for GetGlyphDesignMetrics.");
+}
+
+
+#ifdef USE_CAIRO_SCALED_FONT
+void
+ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
+{
+ MOZ_ASSERT(!mScaledFont);
+
+ if (font == mScaledFont)
+ return;
+
+ if (mScaledFont)
+ cairo_scaled_font_destroy(mScaledFont);
+
+ mScaledFont = font;
+ cairo_scaled_font_reference(mScaledFont);
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/ScaledFontBase.h b/system/graphics/2d/ScaledFontBase.h
new file mode 100644
index 000000000..e4bb4f2f8
--- /dev/null
+++ b/system/graphics/2d/ScaledFontBase.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEDFONTBASE_H_
+#define MOZILLA_GFX_SCALEDFONTBASE_H_
+
+#include "2D.h"
+
+// Skia uses cairo_scaled_font_t as the internal font type in ScaledFont
+#if defined(USE_SKIA) || defined(USE_CAIRO)
+#define USE_CAIRO_SCALED_FONT
+#endif
+
+#ifdef USE_SKIA
+#include "skia/include/core/SkPath.h"
+#include "skia/include/core/SkTypeface.h"
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontBase : public ScaledFont
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase)
+ explicit ScaledFontBase(Float aSize);
+ virtual ~ScaledFontBase();
+
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
+
+ float GetSize() { return mSize; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface() { return mTypeface; }
+#endif
+
+ // Not true, but required to instantiate a ScaledFontBase.
+ virtual FontType GetType() const { return FontType::SKIA; }
+
+#ifdef USE_CAIRO_SCALED_FONT
+ bool PopulateCairoScaledFont();
+ cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
+ void SetCairoScaledFont(cairo_scaled_font_t* font);
+#endif
+
+protected:
+ friend class DrawTargetSkia;
+#ifdef USE_SKIA
+ SkTypeface* mTypeface;
+ SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ // Overridders should ensure the cairo_font_face_t has been addrefed.
+ virtual cairo_font_face_t* GetCairoFontFace() { return nullptr; }
+ cairo_scaled_font_t* mScaledFont;
+#endif
+ Float mSize;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEDFONTBASE_H_ */
diff --git a/system/graphics/2d/ScaledFontCairo.cpp b/system/graphics/2d/ScaledFontCairo.cpp
new file mode 100644
index 000000000..b8b52145e
--- /dev/null
+++ b/system/graphics/2d/ScaledFontCairo.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScaledFontCairo.h"
+#include "Logging.h"
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+#include "skia/include/ports/SkTypeface_cairo.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+// On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
+// an SkFontHost implementation that allows Skia to render using this.
+// This is mainly because FT_Face is not good for sharing between libraries, which
+// is a requirement when we consider runtime switchable backends and so on
+ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize)
+ : ScaledFontBase(aSize)
+{
+ SetCairoScaledFont(aScaledFont);
+}
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+SkTypeface* ScaledFontCairo::GetSkTypeface()
+{
+ if (!mTypeface) {
+ mTypeface = SkCreateTypefaceFromCairoFTFont(mScaledFont);
+ }
+
+ return mTypeface;
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/ScaledFontCairo.h b/system/graphics/2d/ScaledFontCairo.h
new file mode 100644
index 000000000..7862fa1a0
--- /dev/null
+++ b/system/graphics/2d/ScaledFontCairo.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEDFONTCAIRO_H_
+#define MOZILLA_GFX_SCALEDFONTCAIRO_H_
+
+#include "ScaledFontBase.h"
+
+#include "cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontCairo : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontCairo)
+
+ ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize);
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+ virtual SkTypeface* GetSkTypeface();
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEDFONTCAIRO_H_ */
diff --git a/system/graphics/2d/ScaledFontDWrite.cpp b/system/graphics/2d/ScaledFontDWrite.cpp
new file mode 100644
index 000000000..cf173f849
--- /dev/null
+++ b/system/graphics/2d/ScaledFontDWrite.cpp
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetD2D1.h"
+#include "ScaledFontDWrite.h"
+#include "PathD2D.h"
+#include "gfxFont.h"
+
+using namespace std;
+
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#include "skia/include/core/SkPath.h"
+#include "skia/include/ports/SkTypeface_win.h"
+#endif
+
+#include <vector>
+
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
+#include "HelpersWinFonts.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GASP_TAG 0x70736167
+#define GASP_DOGRAY 0x2
+
+static inline unsigned short
+readShort(const char *aBuf)
+{
+ return (*aBuf << 8) | *(aBuf + 1);
+}
+
+static bool
+DoGrayscale(IDWriteFontFace *aDWFace, Float ppem)
+{
+ void *tableContext;
+ char *tableData;
+ UINT32 tableSize;
+ BOOL exists;
+ aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists);
+
+ if (exists) {
+ if (tableSize < 4) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return true;
+ }
+ struct gaspRange {
+ unsigned short maxPPEM; // Stored big-endian
+ unsigned short behavior; // Stored big-endian
+ };
+ unsigned short numRanges = readShort(tableData + 2);
+ if (tableSize < (UINT)4 + numRanges * 4) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return true;
+ }
+ gaspRange *ranges = (gaspRange *)(tableData + 4);
+ for (int i = 0; i < numRanges; i++) {
+ if (readShort((char*)&ranges[i].maxPPEM) > ppem) {
+ if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return false;
+ }
+ break;
+ }
+ }
+ aDWFace->ReleaseFontTable(tableContext);
+ }
+ return true;
+}
+
+static inline DWRITE_FONT_STRETCH
+DWriteFontStretchFromStretch(int16_t aStretch)
+{
+ switch (aStretch) {
+ case NS_FONT_STRETCH_ULTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
+ case NS_FONT_STRETCH_EXTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
+ case NS_FONT_STRETCH_CONDENSED:
+ return DWRITE_FONT_STRETCH_CONDENSED;
+ case NS_FONT_STRETCH_SEMI_CONDENSED:
+ return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
+ case NS_FONT_STRETCH_NORMAL:
+ return DWRITE_FONT_STRETCH_NORMAL;
+ case NS_FONT_STRETCH_SEMI_EXPANDED:
+ return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
+ case NS_FONT_STRETCH_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXPANDED;
+ case NS_FONT_STRETCH_EXTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
+ case NS_FONT_STRETCH_ULTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
+ default:
+ return DWRITE_FONT_STRETCH_UNDEFINED;
+ }
+}
+
+ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize,
+ bool aUseEmbeddedBitmap, bool aForceGDIMode,
+ const gfxFontStyle* aStyle)
+ : ScaledFontBase(aSize)
+ , mFontFace(aFontFace)
+ , mUseEmbeddedBitmap(aUseEmbeddedBitmap)
+ , mForceGDIMode(aForceGDIMode)
+{
+#ifdef USE_SKIA
+ mStyle = SkFontStyle(aStyle->weight,
+ DWriteFontStretchFromStretch(aStyle->stretch),
+ aStyle->style == NS_FONT_STYLE_NORMAL ?
+ SkFontStyle::kUpright_Slant : SkFontStyle::kItalic_Slant);
+#endif
+}
+
+already_AddRefed<Path>
+ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+ if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) {
+ return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
+ }
+
+ RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
+
+ PathBuilderD2D *pathBuilderD2D =
+ static_cast<PathBuilderD2D*>(pathBuilder.get());
+
+ CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
+
+ return pathBuilder->Finish();
+}
+
+
+#ifdef USE_SKIA
+SkTypeface*
+ScaledFontDWrite::GetSkTypeface()
+{
+ if (!mTypeface) {
+ IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
+ if (!factory) {
+ return nullptr;
+ }
+
+ mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode);
+ }
+ return mTypeface;
+}
+#endif
+
+void
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
+{
+ BackendType backendType = aBuilder->GetBackendType();
+ if (backendType != BackendType::DIRECT2D && backendType != BackendType::DIRECT2D1_1) {
+ ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aTransformHint);
+ return;
+ }
+
+ PathBuilderD2D *pathBuilderD2D =
+ static_cast<PathBuilderD2D*>(aBuilder);
+
+ if (pathBuilderD2D->IsFigureActive()) {
+ gfxCriticalNote << "Attempting to copy glyphs to PathBuilderD2D with active figure.";
+ }
+
+ CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
+}
+
+void
+ScaledFontDWrite::GetGlyphDesignMetrics(const uint16_t* aGlyphs, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ DWRITE_FONT_METRICS fontMetrics;
+ mFontFace->GetMetrics(&fontMetrics);
+
+ vector<DWRITE_GLYPH_METRICS> metrics(aNumGlyphs);
+ mFontFace->GetDesignGlyphMetrics(aGlyphs, aNumGlyphs, &metrics.front());
+
+ Float designUnitCorrection = 1.f / fontMetrics.designUnitsPerEm;
+
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ aGlyphMetrics[i].mXBearing = metrics[i].leftSideBearing * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mXAdvance = metrics[i].advanceWidth * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mYBearing = metrics[i].topSideBearing * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mYAdvance = metrics[i].advanceHeight * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mWidth = (metrics[i].advanceHeight - metrics[i].topSideBearing - metrics[i].bottomSideBearing) *
+ designUnitCorrection * mSize;
+ aGlyphMetrics[i].mHeight = (metrics[i].topSideBearing - metrics[i].verticalOriginY) * designUnitCorrection * mSize;
+ }
+}
+
+void
+ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
+{
+ std::vector<UINT16> indices;
+ std::vector<FLOAT> advances;
+ std::vector<DWRITE_GLYPH_OFFSET> offsets;
+ indices.resize(aBuffer.mNumGlyphs);
+ advances.resize(aBuffer.mNumGlyphs);
+ offsets.resize(aBuffer.mNumGlyphs);
+
+ memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ indices[i] = aBuffer.mGlyphs[i].mIndex;
+ offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
+ offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ HRESULT hr =
+ mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
+ &offsets.front(), aBuffer.mNumGlyphs,
+ FALSE, FALSE, aSink);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to copy glyphs to geometry sink. Code: " << hexa(hr);
+ }
+}
+
+bool
+ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
+{
+ UINT32 fileCount = 0;
+ mFontFace->GetFiles(&fileCount, nullptr);
+
+ if (fileCount > 1) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ RefPtr<IDWriteFontFile> file;
+ mFontFace->GetFiles(&fileCount, getter_AddRefs(file));
+
+ const void *referenceKey;
+ UINT32 refKeySize;
+ // XXX - This can currently crash for webfonts, as when we get the reference
+ // key out of the file, that can be an invalid reference key for the loader
+ // we use it with. The fix to this is not obvious but it will probably
+ // have to happen inside thebes.
+ file->GetReferenceKey(&referenceKey, &refKeySize);
+
+ RefPtr<IDWriteFontFileLoader> loader;
+ file->GetLoader(getter_AddRefs(loader));
+
+ RefPtr<IDWriteFontFileStream> stream;
+ loader->CreateStreamFromKey(referenceKey, refKeySize, getter_AddRefs(stream));
+
+ UINT64 fileSize64;
+ stream->GetFileSize(&fileSize64);
+ if (fileSize64 > UINT32_MAX) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ uint32_t fileSize = static_cast<uint32_t>(fileSize64);
+ const void *fragmentStart;
+ void *context;
+ stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context);
+
+ aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton);
+
+ stream->ReleaseFileFragment(context);
+
+ return true;
+}
+
+AntialiasMode
+ScaledFontDWrite::GetDefaultAAMode()
+{
+ AntialiasMode defaultMode = GetSystemDefaultAAMode();
+
+ if (defaultMode == AntialiasMode::GRAY) {
+ if (!DoGrayscale(mFontFace, mSize)) {
+ defaultMode = AntialiasMode::NONE;
+ }
+ }
+ return defaultMode;
+}
+
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontDWrite::GetCairoFontFace()
+{
+ if (!mFontFace) {
+ return nullptr;
+ }
+
+ return cairo_dwrite_font_face_create_for_dwrite_fontface(nullptr, mFontFace);
+}
+#endif
+
+}
+}
diff --git a/system/graphics/2d/ScaledFontDWrite.h b/system/graphics/2d/ScaledFontDWrite.h
new file mode 100644
index 000000000..fc9a26c42
--- /dev/null
+++ b/system/graphics/2d/ScaledFontDWrite.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEDFONTDWRITE_H_
+#define MOZILLA_GFX_SCALEDFONTDWRITE_H_
+
+#include <dwrite.h>
+#include "ScaledFontBase.h"
+
+struct ID2D1GeometrySink;
+struct gfxFontStyle;
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontDWrite final : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
+ ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
+ : ScaledFontBase(aSize)
+ , mFontFace(aFont)
+ , mUseEmbeddedBitmap(false)
+ , mForceGDIMode(false)
+ {}
+
+ ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap,
+ bool aForceGDIMode, const gfxFontStyle* aStyle);
+
+ virtual FontType GetType() const { return FontType::DWRITE; }
+
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+
+ void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
+
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
+
+ virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
+
+ virtual AntialiasMode GetDefaultAAMode() override;
+
+ bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
+ bool ForceGDIMode() { return mForceGDIMode; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+ SkFontStyle mStyle;
+#endif
+
+ RefPtr<IDWriteFontFace> mFontFace;
+ bool mUseEmbeddedBitmap;
+ bool mForceGDIMode;
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_font_face_t* GetCairoFontFace() override;
+#endif
+};
+
+class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsDWrite)
+ GlyphRenderingOptionsDWrite(IDWriteRenderingParams *aParams)
+ : mParams(aParams)
+ {
+ }
+
+ virtual FontType GetType() const { return FontType::DWRITE; }
+
+private:
+ friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
+
+ RefPtr<IDWriteRenderingParams> mParams;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTDWRITE_H_ */
diff --git a/system/graphics/2d/ScaledFontFontconfig.cpp b/system/graphics/2d/ScaledFontFontconfig.cpp
new file mode 100644
index 000000000..d4751f86d
--- /dev/null
+++ b/system/graphics/2d/ScaledFontFontconfig.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScaledFontFontconfig.h"
+#include "Logging.h"
+
+#ifdef USE_SKIA
+#include "skia/include/ports/SkTypeface_cairo.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+// On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
+// an SkFontHost implementation that allows Skia to render using this.
+// This is mainly because FT_Face is not good for sharing between libraries, which
+// is a requirement when we consider runtime switchable backends and so on
+ScaledFontFontconfig::ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont,
+ FcPattern* aPattern,
+ Float aSize)
+ : ScaledFontBase(aSize),
+ mPattern(aPattern)
+{
+ SetCairoScaledFont(aScaledFont);
+ FcPatternReference(aPattern);
+}
+
+ScaledFontFontconfig::~ScaledFontFontconfig()
+{
+ FcPatternDestroy(mPattern);
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontFontconfig::GetSkTypeface()
+{
+ if (!mTypeface) {
+ mTypeface = SkCreateTypefaceFromCairoFTFontWithFontconfig(mScaledFont, mPattern);
+ }
+
+ return mTypeface;
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/ScaledFontFontconfig.h b/system/graphics/2d/ScaledFontFontconfig.h
new file mode 100644
index 000000000..4d4e8217d
--- /dev/null
+++ b/system/graphics/2d/ScaledFontFontconfig.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_
+#define MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_
+
+#include "ScaledFontBase.h"
+
+#include <fontconfig/fontconfig.h>
+#include <cairo.h>
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontFontconfig : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontFontconfig)
+ ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+ ~ScaledFontFontconfig();
+
+ virtual FontType GetType() const { return FontType::FONTCONFIG; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+#endif
+
+private:
+ FcPattern* mPattern;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_ */
diff --git a/system/graphics/2d/ScaledFontWin.cpp b/system/graphics/2d/ScaledFontWin.cpp
new file mode 100644
index 000000000..2ebae21e5
--- /dev/null
+++ b/system/graphics/2d/ScaledFontWin.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScaledFontWin.h"
+
+#include "AutoHelpersWin.h"
+#include "Logging.h"
+#include "nsString.h"
+
+#ifdef USE_SKIA
+#include "skia/include/ports/SkTypeface_win.h"
+#endif
+
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
+#include "HelpersWinFonts.h"
+
+namespace mozilla {
+namespace gfx {
+
+ScaledFontWin::ScaledFontWin(const LOGFONT* aFont, Float aSize)
+ : ScaledFontBase(aSize)
+ , mLogFont(*aFont)
+{
+}
+
+bool
+ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
+{
+ AutoDC dc;
+ AutoSelectFont font(dc.GetDC(), &mLogFont);
+
+ // Check for a font collection first.
+ uint32_t table = 0x66637474; // 'ttcf'
+ uint32_t tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
+ if (tableSize == GDI_ERROR) {
+ // Try as if just a single font.
+ table = 0;
+ tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
+ if (tableSize == GDI_ERROR) {
+ return false;
+ }
+ }
+
+ UniquePtr<uint8_t[]> fontData(new uint8_t[tableSize]);
+
+ uint32_t sizeGot =
+ ::GetFontData(dc.GetDC(), table, 0, fontData.get(), tableSize);
+ if (sizeGot != tableSize) {
+ return false;
+ }
+
+ aDataCallback(fontData.get(), tableSize, 0, mSize, aBaton);
+ return true;
+}
+
+bool
+ScaledFontWin::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
+{
+ aCb(reinterpret_cast<uint8_t*>(&mLogFont), sizeof(mLogFont), aBaton);
+ return true;
+}
+
+bool
+ScaledFontWin::GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton)
+{
+ aCb(reinterpret_cast<uint8_t*>(&mLogFont), sizeof(mLogFont), mSize, aBaton);
+ return true;
+}
+
+AntialiasMode
+ScaledFontWin::GetDefaultAAMode()
+{
+ return GetSystemDefaultAAMode();
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontWin::GetSkTypeface()
+{
+ if (!mTypeface) {
+ mTypeface = SkCreateTypefaceFromLOGFONT(mLogFont);
+ }
+ return mTypeface;
+}
+#endif
+
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontWin::GetCairoFontFace()
+{
+ if (mLogFont.lfFaceName[0] == 0) {
+ return nullptr;
+ }
+ return cairo_win32_font_face_create_for_logfontw(&mLogFont);
+}
+#endif
+
+}
+}
diff --git a/system/graphics/2d/ScaledFontWin.h b/system/graphics/2d/ScaledFontWin.h
new file mode 100644
index 000000000..c07b263d7
--- /dev/null
+++ b/system/graphics/2d/ScaledFontWin.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SCALEDFONTWIN_H_
+#define MOZILLA_GFX_SCALEDFONTWIN_H_
+
+#include "ScaledFontBase.h"
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontWin : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
+ ScaledFontWin(const LOGFONT* aFont, Float aSize);
+
+ virtual FontType GetType() const { return FontType::GDI; }
+
+ bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) override;
+
+ bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
+
+ virtual bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override;
+ virtual AntialiasMode GetDefaultAAMode() override;
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+#endif
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_font_face_t* GetCairoFontFace() override;
+#endif
+
+private:
+#ifdef USE_SKIA
+ friend class DrawTargetSkia;
+#endif
+ LOGFONT mLogFont;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTWIN_H_ */
diff --git a/system/graphics/2d/ShadersD2D.fx b/system/graphics/2d/ShadersD2D.fx
new file mode 100644
index 000000000..df4ced845
--- /dev/null
+++ b/system/graphics/2d/ShadersD2D.fx
@@ -0,0 +1,746 @@
+// We store vertex coordinates and the quad shape in a constant buffer, this is
+// easy to update and allows us to use a single call to set the x, y, w, h of
+// the quad.
+// The QuadDesc and TexCoords both work as follows:
+// The x component is the quad left point, the y component is the top point
+// the z component is the width, and the w component is the height. The quad
+// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f }
+// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right
+// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture
+// space <0, 1.0f> left to right and top to bottom. The input vertices of the
+// shader stage always form a rectangle from {0, 0} - {1, 1}
+cbuffer cb0
+{
+ float4 QuadDesc;
+ float4 TexCoords;
+ float4 MaskTexCoords;
+ float4 TextColor;
+}
+
+cbuffer cb1
+{
+ float4 BlurOffsetsH[3];
+ float4 BlurOffsetsV[3];
+ float4 BlurWeights[3];
+ float4 ShadowColor;
+}
+
+cbuffer cb2
+{
+ float3x3 DeviceSpaceToUserSpace;
+ float2 dimensions;
+ // Precalculate as much as we can!
+ float3 diff;
+ float2 center1;
+ float A;
+ float radius1;
+ float sq_radius1;
+}
+
+struct VS_OUTPUT
+{
+ float4 Position : SV_Position;
+ float2 TexCoord : TEXCOORD0;
+ float2 MaskTexCoord : TEXCOORD1;
+};
+
+struct VS_RADIAL_OUTPUT
+{
+ float4 Position : SV_Position;
+ float2 MaskTexCoord : TEXCOORD0;
+ float2 PixelCoord : TEXCOORD1;
+};
+
+struct PS_TEXT_OUTPUT
+{
+ float4 color;
+ float4 alpha;
+};
+
+Texture2D tex;
+Texture2D bcktex;
+Texture2D mask;
+uint blendop;
+
+sampler sSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sBckSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = bcktex;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sWrapSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Wrap;
+ AddressV = Wrap;
+};
+
+sampler sMirrorSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Mirror;
+ AddressV = Mirror;
+};
+
+sampler sMaskSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = mask;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sShadowSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Border;
+ AddressV = Border;
+ BorderColor = float4(0, 0, 0, 0);
+};
+
+RasterizerState TextureRast
+{
+ ScissorEnable = True;
+ CullMode = None;
+};
+
+BlendState ShadowBlendH
+{
+ BlendEnable[0] = False;
+ RenderTargetWriteMask[0] = 0xF;
+};
+
+BlendState ShadowBlendV
+{
+ BlendEnable[0] = True;
+ SrcBlend = One;
+ DestBlend = Inv_Src_Alpha;
+ BlendOp = Add;
+ SrcBlendAlpha = One;
+ DestBlendAlpha = Inv_Src_Alpha;
+ BlendOpAlpha = Add;
+ RenderTargetWriteMask[0] = 0xF;
+};
+
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = FALSE;
+ BlendEnable[0] = TRUE;
+ SrcBlend = Src1_Color;
+ DestBlend = Inv_Src1_Color;
+ BlendOp = Add;
+ SrcBlendAlpha = Src1_Alpha;
+ DestBlendAlpha = Inv_Src1_Alpha;
+ BlendOpAlpha = Add;
+ RenderTargetWriteMask[0] = 0x0F; // All
+};
+
+VS_OUTPUT SampleTextureVS(float3 pos : POSITION)
+{
+ VS_OUTPUT Output;
+ Output.Position.w = 1.0f;
+ Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
+ Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
+ Output.Position.z = 0;
+ Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x;
+ Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y;
+ Output.MaskTexCoord.x = pos.x * MaskTexCoords.z + MaskTexCoords.x;
+ Output.MaskTexCoord.y = pos.y * MaskTexCoords.w + MaskTexCoords.y;
+ return Output;
+}
+
+VS_RADIAL_OUTPUT SampleRadialVS(float3 pos : POSITION)
+{
+ VS_RADIAL_OUTPUT Output;
+ Output.Position.w = 1.0f;
+ Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
+ Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
+ Output.Position.z = 0;
+ Output.MaskTexCoord.x = pos.x * MaskTexCoords.z + MaskTexCoords.x;
+ Output.MaskTexCoord.y = pos.y * MaskTexCoords.w + MaskTexCoords.y;
+
+ // For the radial gradient pixel shader we need to pass in the pixel's
+ // coordinates in user space for the color to be correctly determined.
+
+ Output.PixelCoord.x = ((Output.Position.x + 1.0f) / 2.0f) * dimensions.x;
+ Output.PixelCoord.y = ((1.0f - Output.Position.y) / 2.0f) * dimensions.y;
+ Output.PixelCoord.xy = mul(float3(Output.PixelCoord.x, Output.PixelCoord.y, 1.0f), DeviceSpaceToUserSpace).xy;
+ return Output;
+}
+
+float Screen(float a, float b)
+{
+ return 1 - ((1 - a)*(1 - b));
+}
+
+static float RedLuminance = 0.3f;
+static float GreenLuminance = 0.59f;
+static float BlueLuminance = 0.11f;
+
+float Lum(float3 C)
+{
+ return RedLuminance * C.r + GreenLuminance * C.g + BlueLuminance * C.b;
+}
+
+float3 ClipColor(float3 C)
+{
+ float L = Lum(C);
+ float n = min(min(C.r, C.g), C.b);
+ float x = max(max(C.r, C.g), C.b);
+
+ if(n < 0)
+ C = L + (((C - L) * L) / (L - n));
+
+ if(x > 1)
+ C = L + ((C - L) * (1 - L) / (x - L));
+
+ return C;
+}
+
+float3 SetLum(float3 C, float l)
+{
+ float d = l - Lum(C);
+ C = C + d;
+ return ClipColor(C);
+}
+
+float Sat(float3 C)
+{
+ return max(C.r, max(C.g, C.b)) - min(C.r, min(C.g, C.b));
+}
+
+void SetSatComponents(inout float minComp, inout float midComp, inout float maxComp, in float satVal)
+{
+ midComp -= minComp;
+ maxComp -= minComp;
+ minComp = 0.0;
+ if (maxComp > 0.0)
+ {
+ midComp *= satVal/maxComp;
+ maxComp = satVal;
+ }
+}
+
+float3 SetSat(float3 color, in float satVal)
+{
+ if (color.x <= color.y) {
+ if (color.y <= color.z) {
+ // x <= y <= z
+ SetSatComponents(color.x, color.y, color.z, satVal);
+ }
+ else {
+ if (color.x <= color.z) {
+ // x <= z <= y
+ SetSatComponents(color.x, color.z, color.y, satVal);
+ }
+ else {
+ // z <= x <= y
+ SetSatComponents(color.z, color.x, color.y, satVal);
+ }
+ }
+ }
+ else {
+ if (color.x <= color.z) {
+ // y <= x <= z
+ SetSatComponents(color.y, color.x, color.z, satVal);
+ }
+ else {
+ if (color.y <= color.z) {
+ // y <= z <= x
+ SetSatComponents(color.y, color.z, color.x, satVal);
+ }
+ else {
+ // z <= y <= x
+ SetSatComponents(color.z, color.y, color.x, satVal);
+ }
+ }
+ }
+
+ return color;
+}
+
+float4 SampleBlendTextureSeparablePS_1( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 1) { // multiply
+ retval.rgb = output.rgb * background.rgb;
+ } else if(blendop == 2) {
+ retval.rgb = output.rgb + background.rgb - output.rgb * background.rgb;
+ } else if(blendop == 3) {
+ if(background.r <= 0.5)
+ retval.r = 2*background.r * output.r;
+ else
+ retval.r = Screen(output.r, 2 * background.r - 1);
+ if(background.g <= 0.5)
+ retval.g = 2 * background.g * output.g;
+ else
+ retval.g = Screen(output.g, 2 * background.g - 1);
+ if(background.b <= 0.5)
+ retval.b = 2 * background.b * output.b;
+ else
+ retval.b = Screen(output.b, 2 * background.b - 1);
+ } else if(blendop == 4) {
+ retval.rgb = min(output.rgb, background.rgb);
+ } else if(blendop == 5) {
+ retval.rgb = max(output.rgb, background.rgb);
+ } else {
+ if(background.r == 0)
+ retval.r = 0;
+ else
+ if(output.r == 1)
+ retval.r = 1;
+ else
+ retval.r = min(1, background.r / (1 - output.r));
+ if(background.g == 0)
+ retval.g = 0;
+ else
+ if(output.g == 1)
+ retval.g = 1;
+ else
+ retval.g = min(1, background.g / (1 - output.g));
+ if(background.b == 0)
+ retval.b = 0;
+ else
+ if(output.b == 1)
+ retval.b = 1;
+ else
+ retval.b = min(1, background.b / (1 - output.b));
+ }
+
+ output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
+ return output;
+}
+
+float4 SampleBlendTextureSeparablePS_2( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 7) {
+ if(background.r == 1)
+ retval.r = 1;
+ else
+ if(output.r == 0)
+ retval.r = 0;
+ else
+ retval.r = 1 - min(1, (1 - background.r) / output.r);
+ if(background.g == 1)
+ retval.g = 1;
+ else
+ if(output.g == 0)
+ retval.g = 0;
+ else
+ retval.g = 1 - min(1, (1 - background.g) / output.g);
+ if(background.b == 1)
+ retval.b = 1;
+ else
+ if(output.b == 0)
+ retval.b = 0;
+ else
+ retval.b = 1 - min(1, (1 - background.b) / output.b);
+ } else if(blendop == 8) {
+ if(output.r <= 0.5)
+ retval.r = 2 * output.r * background.r;
+ else
+ retval.r = Screen(background.r, 2 * output.r -1);
+ if(output.g <= 0.5)
+ retval.g = 2 * output.g * background.g;
+ else
+ retval.g = Screen(background.g, 2 * output.g -1);
+ if(output.b <= 0.5)
+ retval.b = 2 * output.b * background.b;
+ else
+ retval.b = Screen(background.b, 2 * output.b -1);
+ } else if(blendop == 9){
+ float D;
+ if(background.r <= 0.25)
+ D = ((16 * background.r - 12) * background.r + 4) * background.r;
+ else
+ D = sqrt(background.r);
+ if(output.r <= 0.5)
+ retval.r = background.r - (1 - 2 * output.r) * background.r * (1 - background.r);
+ else
+ retval.r = background.r + (2 * output.r - 1) * (D - background.r);
+
+ if(background.g <= 0.25)
+ D = ((16 * background.g - 12) * background.g + 4) * background.g;
+ else
+ D = sqrt(background.g);
+ if(output.g <= 0.5)
+ retval.g = background.g - (1 - 2 * output.g) * background.g * (1 - background.g);
+ else
+ retval.g = background.g + (2 * output.g - 1) * (D - background.g);
+
+ if(background.b <= 0.25)
+ D = ((16 * background.b - 12) * background.b + 4) * background.b;
+ else
+ D = sqrt(background.b);
+
+ if(output.b <= 0.5)
+ retval.b = background.b - (1 - 2 * output.b) * background.b * (1 - background.b);
+ else
+ retval.b = background.b + (2 * output.b - 1) * (D - background.b);
+ } else if(blendop == 10) {
+ retval.rgb = abs(output.rgb - background.rgb);
+ } else {
+ retval.rgb = output.rgb + background.rgb - 2 * output.rgb * background.rgb;
+ }
+
+ output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
+ return output;
+}
+
+float4 SampleBlendTextureNonSeparablePS( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 12) {
+ retval.rgb = SetLum(SetSat(output.rgb, Sat(background.rgb)), Lum(background.rgb));
+ } else if(blendop == 13) {
+ retval.rgb = SetLum(SetSat(background.rgb, Sat(output.rgb)), Lum(background.rgb));
+ } else if(blendop == 14) {
+ retval.rgb = SetLum(output.rgb, Lum(background.rgb));
+ } else {
+ retval.rgb = SetLum(background.rgb, Lum(output.rgb));
+ }
+
+ output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
+ return output;
+}
+
+
+float4 SampleTexturePS( VS_OUTPUT In) : SV_Target
+{
+ return tex.Sample(sSampler, In.TexCoord);
+}
+
+float4 SampleMaskTexturePS( VS_OUTPUT In) : SV_Target
+{
+ return tex.Sample(sSampler, In.TexCoord) * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+}
+
+float4 SampleRadialGradientPS(VS_RADIAL_OUTPUT In, uniform sampler aSampler) : SV_Target
+{
+ // Radial gradient painting is defined as the set of circles whose centers
+ // are described by C(t) = (C2 - C1) * t + C1; with radii
+ // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
+ // quadratic equation that arises when calculating t for pixel (x, y).
+ //
+ // A more extensive derrivation can be found in the pixman radial gradient
+ // code.
+
+ float2 p = In.PixelCoord;
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - sq_radius1;
+
+ float det = pow(B, 2) - A * C;
+
+ if (det < 0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float sqrt_det = sqrt(abs(det));
+
+ float2 t = (B + float2(sqrt_det, -sqrt_det)) / A;
+
+ float2 isValid = step(float2(-radius1, -radius1), t * diff.z);
+
+ if (max(isValid.x, isValid.y) <= 0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float upper_t = lerp(t.y, t.x, isValid.x);
+
+ float4 output = tex.Sample(aSampler, float2(upper_t, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+ return output;
+};
+
+float4 SampleRadialGradientA0PS( VS_RADIAL_OUTPUT In, uniform sampler aSampler ) : SV_Target
+{
+ // This simpler shader is used for the degenerate case where A is 0,
+ // i.e. we're actually solving a linear equation.
+
+ float2 p = In.PixelCoord;
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);
+
+ float t = 0.5 * C / B;
+
+ if (-radius1 >= t * diff.z) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float4 output = tex.Sample(aSampler, float2(t, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+ return output;
+};
+
+float4 SampleShadowHPS( VS_OUTPUT In) : SV_Target
+{
+ float outputStrength = 0;
+
+ outputStrength += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].x, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].y, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].z, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].w, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].x, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].y, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].z, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].w, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[2].x, In.TexCoord.y)).a;
+
+ return ShadowColor * outputStrength;
+};
+
+float4 SampleShadowVPS( VS_OUTPUT In) : SV_Target
+{
+ float4 outputColor = float4(0, 0, 0, 0);
+
+ outputColor += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].x));
+ outputColor += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].y));
+ outputColor += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].z));
+ outputColor += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].w));
+ outputColor += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].x));
+ outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
+ outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
+ outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
+ outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
+
+ return outputColor;
+};
+
+float4 SampleMaskShadowVPS( VS_OUTPUT In) : SV_Target
+{
+ float4 outputColor = float4(0, 0, 0, 0);
+
+ outputColor += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].x));
+ outputColor += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].y));
+ outputColor += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].z));
+ outputColor += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].w));
+ outputColor += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].x));
+ outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
+ outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
+ outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
+ outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
+
+ return outputColor * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+};
+
+PS_TEXT_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target
+{
+ PS_TEXT_OUTPUT output;
+ output.color = float4(TextColor.r, TextColor.g, TextColor.b, 1.0);
+ output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a;
+ return output;
+};
+
+PS_TEXT_OUTPUT SampleTextTexturePSMasked( VS_OUTPUT In) : SV_Target
+{
+ PS_TEXT_OUTPUT output;
+
+ float maskValue = mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+
+ output.color = float4(TextColor.r, TextColor.g, TextColor.b, 1.0);
+ output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a * maskValue;
+
+ return output;
+};
+
+technique10 SampleTexture
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
+ }
+}
+
+technique10 SampleTextureForSeparableBlending_1
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureSeparablePS_1()));
+ }
+}
+
+technique10 SampleTextureForSeparableBlending_2
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureSeparablePS_2()));
+ }
+}
+
+technique10 SampleTextureForNonSeparableBlending
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureNonSeparablePS()));
+ }
+}
+
+technique10 SampleRadialGradient
+{
+ pass APos
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sSampler )));
+ }
+ pass A0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sSampler )));
+ }
+ pass APosWrap
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sWrapSampler )));
+ }
+ pass A0Wrap
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sWrapSampler )));
+ }
+ pass APosMirror
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sMirrorSampler )));
+ }
+ pass A0Mirror
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sMirrorSampler )));
+ }
+}
+
+technique10 SampleMaskedTexture
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskTexturePS()));
+ }
+}
+
+technique10 SampleTextureWithShadow
+{
+ // Horizontal pass
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendH, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleShadowHPS()));
+ }
+ // Vertical pass
+ pass P1
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleShadowVPS()));
+ }
+ // Vertical pass - used when using a mask
+ pass P2
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskShadowVPS()));
+ }
+}
+
+technique10 SampleTextTexture
+{
+ pass Unmasked
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS()));
+ }
+ pass Masked
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePSMasked()));
+ }
+}
+
diff --git a/system/graphics/2d/ShadersD2D.h b/system/graphics/2d/ShadersD2D.h
new file mode 100644
index 000000000..9cc65a09b
--- /dev/null
+++ b/system/graphics/2d/ShadersD2D.h
@@ -0,0 +1,16012 @@
+#if 0
+//
+// FX Version: fx_4_0
+// Child effect (requires effect pool): false
+//
+// 4 local buffer(s)
+//
+cbuffer $Globals
+{
+ uint blendop; // Offset: 0, size: 4
+}
+
+cbuffer cb0
+{
+ float4 QuadDesc; // Offset: 0, size: 16
+ float4 TexCoords; // Offset: 16, size: 16
+ float4 MaskTexCoords; // Offset: 32, size: 16
+ float4 TextColor; // Offset: 48, size: 16
+}
+
+cbuffer cb1
+{
+ float4 BlurOffsetsH[3]; // Offset: 0, size: 48
+ float4 BlurOffsetsV[3]; // Offset: 48, size: 48
+ float4 BlurWeights[3]; // Offset: 96, size: 48
+ float4 ShadowColor; // Offset: 144, size: 16
+}
+
+cbuffer cb2
+{
+ float3x3 DeviceSpaceToUserSpace; // Offset: 0, size: 44
+ float2 dimensions; // Offset: 48, size: 8
+ float3 diff; // Offset: 64, size: 12
+ float2 center1; // Offset: 80, size: 8
+ float A; // Offset: 88, size: 4
+ float radius1; // Offset: 92, size: 4
+ float sq_radius1; // Offset: 96, size: 4
+}
+
+//
+// 13 local object(s)
+//
+Texture2D tex;
+Texture2D bcktex;
+Texture2D mask;
+SamplerState sSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sBckSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = bcktex;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sWrapSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(WRAP /* 1 */);
+ AddressV = uint(WRAP /* 1 */);
+};
+SamplerState sMirrorSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(MIRROR /* 2 */);
+ AddressV = uint(MIRROR /* 2 */);
+};
+SamplerState sMaskSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = mask;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sShadowSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(BORDER /* 4 */);
+ AddressV = uint(BORDER /* 4 */);
+ BorderColor = float4(0, 0, 0, 0);
+};
+RasterizerState TextureRast
+{
+ ScissorEnable = bool(TRUE /* 1 */);
+ CullMode = uint(NONE /* 1 */);
+};
+BlendState ShadowBlendH
+{
+ BlendEnable[0] = bool(FALSE /* 0 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+BlendState ShadowBlendV
+{
+ BlendEnable[0] = bool(TRUE /* 1 */);
+ SrcBlend[0] = uint(ONE /* 2 */);
+ DestBlend[0] = uint(INV_SRC_ALPHA /* 6 */);
+ BlendOp[0] = uint(ADD /* 1 */);
+ SrcBlendAlpha[0] = uint(ONE /* 2 */);
+ DestBlendAlpha[0] = uint(INV_SRC_ALPHA /* 6 */);
+ BlendOpAlpha[0] = uint(ADD /* 1 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = bool(FALSE /* 0 */);
+ BlendEnable[0] = bool(TRUE /* 1 */);
+ SrcBlend[0] = uint(SRC1_COLOR /* 16 */);
+ DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */);
+ BlendOp[0] = uint(ADD /* 1 */);
+ SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */);
+ DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */);
+ BlendOpAlpha[0] = uint(ADD /* 1 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+
+//
+// 8 technique(s)
+//
+technique10 SampleTexture
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ texld r0, t0, s0
+ mov oC0, r0
+
+ // approximately 2 instruction slots used (1 texture, 1 arithmetic)
+ ps_4_0
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ sample o0.xyzw, v1.xyxx, t0.xyzw, s0
+ ret
+ // Approximately 2 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForSeparableBlending_1
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -1, -2, -3, -4
+ def c2, 1, 0, 0.5, -2
+ def c3, -5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.w, c0.x
+ add r0.x, r0.w, c3.x
+ mul r0.x, r0.x, r0.x
+ texld r1, t0, s1
+ texld r2, t0, s0
+ rcp r0.y, r2.w
+ mad r3.xyz, r2, r0.y, -c2.x
+ mul r3.xyz, r3, r3
+ mad r4.xyz, r2, -r0.y, c2.x
+ rcp r3.w, r4.x
+ rcp r4.w, r1.w
+ mul r5.xyz, r1, r4.w
+ mad r1.xyz, r1, -r4.w, c2.z
+ mul r3.w, r3.w, r5.x
+ min r4.w, r3.w, c2.x
+ cmp r4.w, -r3.x, c2.x, r4.w
+ mul r6.xyz, r5, r5
+ cmp r7.x, -r6.x, c2.y, r4.w
+ rcp r4.w, r4.y
+ mul r4.w, r4.w, r5.y
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.y, c2.x, r5.w
+ cmp r7.y, -r6.y, c2.y, r4.w
+ rcp r4.w, r4.z
+ mul r4.w, r4.w, r5.z
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.z, c2.x, r5.w
+ cmp r7.z, -r6.z, c2.y, r4.w
+ mul r3.xyz, r0.y, r2
+ mad r6.xyz, r2, r0.y, r5
+ mad r6.xyz, r3, -r5, r6
+ max r8.xyz, r3, r5
+ cmp r0.xyz, -r0.x, r8, r7
+ add r7, r0.w, c1
+ mul r7, r7, r7
+ min r8.xyz, r5, r3
+ cmp r0.xyz, -r7.w, r8, r0
+ mad r8.xyz, r5, -c2.w, -c2.x
+ add r8.xyz, -r8, c2.x
+ mad r4.xyz, r4, -r8, c2.x
+ add r8.xyz, r5, r5
+ mul r5.xyz, r5, r3
+ mul r8.xyz, r3, r8
+ cmp r1.xyz, r1, r8, r4
+ cmp r0.xyz, -r7.z, r1, r0
+ cmp r0.xyz, -r7.y, r6, r0
+ cmp r0.xyz, -r7.x, r5, r0
+ lrp r4.xyz, r1.w, r0, r3
+ mul r4.w, r1.w, r1.w
+ cmp r4.w, -r4.w, c2.x, c2.y
+ mul r0.xyz, r2.w, r4
+ mul r0.w, r2.w, r2.w
+ cmp r0.w, -r0.w, c2.x, c2.y
+ add r0.w, r4.w, r0.w
+ cmp r2.xyz, -r0.w, r0, r2
+ mov oC0, r2
+
+ // approximately 56 instruction slots used (2 texture, 54 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 7
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(1)
+ if_nz r2.x
+ mul r2.xyz, r0.xyzx, r1.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(2)
+ if_nz r2.w
+ add r3.xyz, r0.xyzx, r1.xyzx
+ mad r2.xyz, -r0.xyzx, r1.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(3)
+ if_nz r2.w
+ ge r3.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r1.xyzx
+ add r4.xyz, r1.xyzx, r1.xyzx
+ mul r4.xyz, r0.xyzx, r4.xyzx
+ mad r5.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r6.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r6.xyzx, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r2.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(4)
+ if_nz r2.w
+ min r2.xyz, r0.xyzx, r1.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(5)
+ if_nz r2.w
+ max r2.xyz, r0.xyzx, r1.xyzx
+ else
+ eq r3.xyz, r1.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ eq r4.xyz, r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r1.xyz, r1.xyzx, r5.xyzx
+ min r1.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r1.xyz, r4.xyzx, l(1.000000,1.000000,1.000000,0), r1.xyzx
+ movc r2.xyz, r3.xyzx, l(0,0,0,0), r1.xyzx
+ endif
+ endif
+ endif
+ endif
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 57 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForSeparableBlending_2
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -7, -8, -9, -10
+ def c2, 1, 0, -1, 0.25
+ def c3, 0.5, 2, -1, 4
+ def c4, 16, -12, 2, 1
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.w, c0.x
+ add r0, r0.w, c1
+ mul r0, r0, r0
+ texld r1, t0, s0
+ texld r2, t0, s1
+ rcp r3.w, r2.w
+ mad r3.xy, r2.yzzw, -r3.w, c2.w
+ mul r4.xyz, r2, r3.w
+ mad r5.xyz, r4, c4.x, c4.y
+ mad r5.xyz, r5, r4, c3.w
+ mul r5.xyz, r4, r5
+ rsq r4.w, r4.y
+ rcp r4.w, r4.w
+ cmp r4.w, r3.x, r5.y, r4.w
+ mad r4.w, r2.y, -r3.w, r4.w
+ rcp r3.x, r1.w
+ mul r6.xyz, r1, r3.x
+ mad r7.xyz, r6, c3.y, c3.z
+ mad r4.w, r7.y, r4.w, r4.y
+ mad r8.xyz, r1, -r3.x, c3.x
+ mad r9, r2.xyzx, -r3.w, c2.xxxw
+ mad r10.xyz, r6, -c4.z, c4.w
+ mul r10.xyz, r4, r10
+ mad r10.xyz, r10, -r9, r4
+ cmp r11.y, r8.y, r10.y, r4.w
+ rsq r4.w, r4.z
+ rcp r4.w, r4.w
+ cmp r4.w, r3.y, r5.z, r4.w
+ mad r4.w, r2.z, -r3.w, r4.w
+ mad r4.w, r7.z, r4.w, r4.z
+ cmp r11.z, r8.z, r10.z, r4.w
+ rsq r4.w, r4.x
+ rcp r4.w, r4.w
+ cmp r4.w, r9.w, r5.x, r4.w
+ mad r4.w, r2.x, -r3.w, r4.w
+ mad r2.xyz, r2, r3.w, c2.z
+ mul r2.xyz, r2, r2
+ mad r4.w, r7.x, r4.w, r4.x
+ add r3.yzw, -r7.xxyz, c2.x
+ mad r3.yzw, r9.xxyz, -r3, c2.x
+ cmp r11.x, r8.x, r10.x, r4.w
+ mad r5.xyz, r1, r3.x, -r4
+ mad r7.xyz, r1, r3.x, r4
+ abs r5.xyz, r5
+ mul r10.xyz, r4, r6
+ mad r7.xyz, r10, -c3.y, r7
+ cmp r5.xyz, -r0.w, r5, r7
+ cmp r5.xyz, -r0.z, r11, r5
+ add r7.xyz, r6, r6
+ mul r4.xyz, r4, r7
+ cmp r3.xyz, r8, r4, r3.yzww
+ cmp r0.yzw, -r0.y, r3.xxyz, r5.xxyz
+ rcp r6.w, r6.x
+ mad r6.w, r9.x, -r6.w, c2.x
+ max r3.x, r6.w, c2.y
+ mul r3.yzw, r6.xxyz, r6.xxyz
+ cmp r6.w, -r3.y, c2.y, r3.x
+ cmp r4.x, -r2.x, c2.x, r6.w
+ rcp r4.w, r6.y
+ mad r4.w, r9.y, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.z, c2.y, r6.w
+ cmp r4.y, -r2.y, c2.x, r4.w
+ rcp r4.w, r6.z
+ mad r4.w, r9.z, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.w, c2.y, r6.w
+ cmp r4.z, -r2.z, c2.x, r4.w
+ cmp r0.xyz, -r0.x, r4, r0.yzww
+ lrp r3.xyz, r2.w, r0, r6
+ mul r3.w, r2.w, r2.w
+ cmp r3.w, -r3.w, c2.x, c2.y
+ mul r0.xyz, r1.w, r3
+ mul r0.w, r1.w, r1.w
+ cmp r0.w, -r0.w, c2.x, c2.y
+ add r0.w, r3.w, r0.w
+ cmp r1.xyz, -r0.w, r0, r1
+ mov oC0, r1
+
+ // approximately 78 instruction slots used (2 texture, 76 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 7
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(7)
+ if_nz r2.x
+ eq r2.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ eq r3.xyz, r0.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ add r4.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r4.xyz, r4.xyzx, r0.xyzx
+ min r4.xyz, r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r4.xyz, -r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r3.xyz, r3.xyzx, l(0,0,0,0), r4.xyzx
+ movc r2.xyz, r2.xyzx, l(1.000000,1.000000,1.000000,0), r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(8)
+ if_nz r2.w
+ ge r3.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r0.xyzx
+ add r4.xyz, r0.xyzx, r0.xyzx
+ mul r4.xyz, r1.xyzx, r4.xyzx
+ mad r5.xyz, r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r6.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r6.xyzx, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r2.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(9)
+ if_nz r2.w
+ ge r3.xyz, l(0.250000, 0.250000, 0.250000, 0.000000), r1.xyzx
+ mad r4.xyz, r1.xyzx, l(16.000000, 16.000000, 16.000000, 0.000000), l(-12.000000, -12.000000, -12.000000, 0.000000)
+ mad r4.xyz, r4.xyzx, r1.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000)
+ mul r4.xyz, r1.xyzx, r4.xyzx
+ sqrt r5.xyz, r1.xyzx
+ movc r3.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ ge r4.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r0.xyzx
+ mad r5.xyz, -r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(1.000000, 1.000000, 1.000000, 0.000000)
+ mul r5.xyz, r1.xyzx, r5.xyzx
+ add r6.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r5.xyzx, r6.xyzx, r1.xyzx
+ mad r6.xyz, r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r3.xyz, -r1.xyzx, r3.xyzx
+ mad r3.xyz, r6.xyzx, r3.xyzx, r1.xyzx
+ movc r2.xyz, r4.xyzx, r5.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(10)
+ add r3.xyz, r0.xyzx, -r1.xyzx
+ add r4.xyz, r0.xyzx, r1.xyzx
+ mul r1.xyz, r0.xyzx, r1.xyzx
+ mad r1.xyz, -r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r4.xyzx
+ movc r2.xyz, r2.wwww, |r3.xyzx|, r1.xyzx
+ endif
+ endif
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 66 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForNonSeparableBlending
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -12, -13, -14, 0
+ def c2, 1, 0, 0, 0
+ def c3, 0.300000012, 0.589999974, 0.109999999, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.y, c2.y
+ mov r1.y, c2.y
+ mov r2.z, c2.y
+ texld r3, t0, s1
+ texld r4, t0, s0
+ rcp r0.w, r4.w
+ mul r5.xyz, r0.w, r4
+ mad r6.xy, r4.yxzw, r0.w, -r5.zyzw
+ cmp r7.xy, r6.x, r5.yzzw, r5.zyzw
+ max r1.w, r5.x, r7.x
+ min r2.w, r7.y, r5.x
+ add r7.w, r1.w, -r2.w
+ rcp r1.w, r3.w
+ mul r8.xyz, r1.w, r3
+ mad r9.xy, r3.x, r1.w, -r8.zyzw
+ rcp r2.w, r9.y
+ mul r2.w, r2.w, r7.w
+ mad r10, r3.zyyz, r1.w, -r8.xxzy
+ mul r7.y, r2.w, r10.w
+ mov r9.zw, r10
+ cmp r1.xz, -r9.y, r9.yyww, r7.wyyw
+ rcp r2.w, r9.x
+ mul r2.w, r2.w, r7.w
+ mul r7.x, r2.w, r9.z
+ cmp r2.xy, -r9.x, r9.xzzw, r7.wxzw
+ cmp r1.xyz, r9.w, r1, r2
+ rcp r5.w, r9.w
+ mul r5.w, r5.w, r7.w
+ mul r7.z, r5.w, r9.y
+ cmp r0.xz, -r10.w, r9.yyww, r7.zyww
+ cmp r0.xyz, r10.x, r0, r1
+ mov r1.x, c2.y
+ mov r2.x, c2.y
+ mov r11.z, c2.y
+ rcp r2.w, r9.z
+ mul r2.w, r2.w, r7.w
+ mul r7.x, r2.w, r9.x
+ cmp r11.xy, -r10.z, r9.xzzw, r7.xwzw
+ rcp r2.w, r10.y
+ mul r2.w, r2.w, r7.w
+ mul r7.y, r2.w, r10.x
+ cmp r2.yz, -r10.y, r10.xyxw, r7.xwyw
+ cmp r2.xyz, r10.x, r2, r11
+ rcp r2.w, r10.x
+ mul r2.w, r2.w, r7.w
+ mul r7.z, r2.w, r10.y
+ cmp r1.yz, -r10.x, r10.xyxw, r7.xzww
+ cmp r1.xyz, r9.w, r1, r2
+ cmp r0.xyz, r10.y, r1, r0
+ cmp r1.xy, r9.z, r8.yzzw, r8.zyzw
+ dp3 r5.w, r0, c3
+ dp3 r1.z, r8, c3
+ add r5.w, -r5.w, r1.z
+ add r0.xyz, r0, r5.w
+ add r5.w, -r0.y, r0.x
+ cmp r2.xy, r5.w, r0.yxzw, r0
+ min r5.w, r0.z, r2.x
+ max r7.x, r2.y, r0.z
+ dp3 r2.x, r0, c3
+ add r2.y, -r5.w, r2.x
+ rcp r2.y, r2.y
+ add r7.yzw, r0.xxyz, -r2.x
+ mul r7.yzw, r2.x, r7
+ mad r2.yzw, r7, r2.y, r2.x
+ cmp r0.xyz, r5.w, r0, r2.yzww
+ add r2.yzw, -r2.x, r0.xxyz
+ add r5.w, -r2.x, c2.x
+ mul r2.yzw, r2, r5.w
+ add r5.w, -r2.x, r7.x
+ add r7.x, -r7.x, c2.x
+ rcp r5.w, r5.w
+ mad r2.xyz, r2.yzww, r5.w, r2.x
+ cmp r0.xyz, r7.x, r0, r2
+ dp3 r5.w, r5, c3
+ add r2.x, r1.z, -r5.w
+ add r5.w, -r1.z, r5.w
+ mad r2.yzw, r3.xxyz, r1.w, r5.w
+ mad r3.xyz, r4, r0.w, r2.x
+ mad r7, r4.zyzx, r0.w, -r5.xxyz
+ add r0.w, -r3.y, r3.x
+ cmp r8.yz, r0.w, r3.xyxw, r3.xxyw
+ min r0.w, r3.z, r8.y
+ max r1.w, r8.z, r3.z
+ dp3 r5.w, r3, c3
+ add r2.x, -r0.w, r5.w
+ rcp r2.x, r2.x
+ add r8.yzw, r3.xxyz, -r5.w
+ mul r8.yzw, r5.w, r8
+ mad r8.yzw, r8, r2.x, r5.w
+ cmp r3.xyz, r0.w, r3, r8.yzww
+ add r8.yzw, -r5.w, r3.xxyz
+ add r0.w, -r5.w, c2.x
+ mul r8.yzw, r0.w, r8
+ add r0.w, r1.w, -r5.w
+ add r1.w, -r1.w, c2.x
+ rcp r0.w, r0.w
+ mad r8.yzw, r8, r0.w, r5.w
+ cmp r3.xyz, r1.w, r3, r8.yzww
+ add r0.w, -r2.z, r2.y
+ cmp r8.yz, r0.w, r2.xzyw, r2
+ min r0.w, r2.w, r8.y
+ max r1.w, r8.z, r2.w
+ dp3 r5.w, r2.yzww, c3
+ add r2.x, -r0.w, r5.w
+ rcp r2.x, r2.x
+ add r8.yzw, r2, -r5.w
+ mul r8.yzw, r5.w, r8
+ mad r8.yzw, r8, r2.x, r5.w
+ cmp r2.xyz, r0.w, r2.yzww, r8.yzww
+ add r8.yzw, -r5.w, r2.xxyz
+ add r0.w, -r5.w, c2.x
+ mul r8.yzw, r0.w, r8
+ add r0.w, r1.w, -r5.w
+ add r1.w, -r1.w, c2.x
+ rcp r0.w, r0.w
+ mad r8.yzw, r8, r0.w, r5.w
+ cmp r2.xyz, r1.w, r2, r8.yzww
+ mov r0.w, c0.x
+ add r8.yzw, r0.w, c1.xxyz
+ mul r8.yzw, r8, r8
+ cmp r2.xyz, -r8.w, r3, r2
+ cmp r0.xyz, -r8.z, r0, r2
+ mov r2.y, c2.y
+ mov r3.y, c2.y
+ mov r9.z, c2.y
+ max r0.w, r8.x, r1.x
+ min r2.w, r1.y, r8.x
+ add r10.w, r0.w, -r2.w
+ rcp r0.w, r7.w
+ mul r0.w, r0.w, r10.w
+ mul r10.x, r0.w, r6.x
+ mov r6.zw, r7.xywz
+ cmp r9.xy, -r7.w, r6.zxzw, r10.wxzw
+ rcp r0.w, r6.y
+ mul r0.w, r0.w, r10.w
+ mul r10.y, r0.w, r7.z
+ cmp r3.xz, -r6.y, r6.yyww, r10.wyyw
+ cmp r1.xyw, r7.z, r3.xyzz, r9.xyzz
+ rcp r0.w, r7.z
+ mul r0.w, r0.w, r10.w
+ mul r10.z, r0.w, r6.y
+ cmp r2.xz, -r7.z, r6.yyww, r10.zyww
+ cmp r1.xyw, r7.x, r2.xyzz, r1
+ mov r2.x, c2.y
+ mov r3.z, c2.y
+ rcp r0.w, r6.x
+ mul r0.w, r0.w, r10.w
+ mul r10.x, r0.w, r7.w
+ cmp r3.xy, -r6.x, r6.zxzw, r10.xwzw
+ rcp r0.w, r7.y
+ mul r0.w, r0.w, r10.w
+ mul r10.y, r0.w, r7.x
+ cmp r2.yz, -r7.y, r7.xyxw, r10.xwyw
+ cmp r2.xyz, r7.x, r2, r3
+ mov r3.x, c2.y
+ rcp r0.w, r7.x
+ mul r0.w, r0.w, r10.w
+ mul r10.z, r0.w, r7.y
+ cmp r3.yz, -r7.x, r7.xyxw, r10.xzww
+ cmp r2.xyz, r7.z, r3, r2
+ cmp r1.xyw, r7.y, r2.xyzz, r1
+ dp3 r0.w, r1.xyww, c3
+ add r0.w, -r0.w, r1.z
+ add r1.xyz, r0.w, r1.xyww
+ add r0.w, -r1.y, r1.x
+ cmp r2.xy, r0.w, r1.yxzw, r1
+ min r0.w, r1.z, r2.x
+ max r5.w, r2.y, r1.z
+ dp3 r1.w, r1, c3
+ add r2.xyz, -r1.w, r1
+ mul r2.xyz, r1.w, r2
+ add r2.w, -r0.w, r1.w
+ rcp r2.w, r2.w
+ mad r2.xyz, r2, r2.w, r1.w
+ cmp r1.xyz, r0.w, r1, r2
+ add r2.xyz, -r1.w, r1
+ add r0.w, -r1.w, c2.x
+ mul r2.xyz, r0.w, r2
+ add r0.w, -r1.w, r5.w
+ add r2.w, -r5.w, c2.x
+ rcp r0.w, r0.w
+ mad r2.xyz, r2, r0.w, r1.w
+ cmp r1.xyz, r2.w, r1, r2
+ cmp r0.xyz, -r8.y, r1, r0
+ lrp r1.xyz, r3.w, r0, r5
+ mul r1.w, r3.w, r3.w
+ cmp r1.w, -r1.w, c2.x, c2.y
+ mul r0.xyz, r4.w, r1
+ mul r0.w, r4.w, r4.w
+ cmp r0.w, -r0.w, c2.x, c2.y
+ add r0.w, r1.w, r0.w
+ cmp r4.xyz, -r0.w, r0, r4
+ mov oC0, r4
+
+ // approximately 193 instruction slots used (2 texture, 191 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 9
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(12)
+ if_nz r2.x
+ max r2.x, r1.z, r1.y
+ max r2.x, r1.x, r2.x
+ min r2.y, r1.z, r1.y
+ min r2.y, r1.x, r2.y
+ add r2.w, -r2.y, r2.x
+ ge r3.x, r0.y, r0.x
+ if_nz r3.x
+ add r3.xyzw, -r0.xxzz, r0.yzxy
+ lt r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r3.yxwy
+ div r5.xyz, r2.wwww, r3.yxwy
+ mul r2.xyz, r3.xyzx, r5.xyzx
+ movc r5.yz, r4.xxxx, r2.xxwx, r3.xxyx
+ ge r4.xw, r0.zzzz, r0.yyyx
+ movc r6.yz, r4.yyyy, r2.wwyw, r3.xxyx
+ movc r3.xy, r4.zzzz, r2.zwzz, r3.zwzz
+ mov r6.x, l(0)
+ mov r3.z, l(0)
+ movc r3.xyz, r4.wwww, r6.xyzx, r3.xyzx
+ mov r5.x, l(0)
+ movc r3.xyz, r4.xxxx, r5.xyzx, r3.xyzx
+ else
+ add r4.xyzw, -r0.yyzz, r0.xzyx
+ lt r5.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r4.yxwy
+ div r6.xyz, r2.wwww, r4.yxwy
+ mul r2.xyz, r4.xyzx, r6.xyzx
+ movc r6.xz, r5.xxxx, r2.xxwx, r4.xxyx
+ ge r5.xw, r0.zzzz, r0.xxxy
+ movc r7.xz, r5.yyyy, r2.wwyw, r4.xxyx
+ movc r2.xy, r5.zzzz, r2.wzww, r4.wzww
+ mov r7.y, l(0)
+ mov r2.z, l(0)
+ movc r2.xyz, r5.wwww, r7.xyzx, r2.xyzx
+ mov r6.y, l(0)
+ movc r3.xyz, r5.xxxx, r6.xyzx, r2.xyzx
+ endif
+ dp3 r2.x, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r2.y, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.x, -r2.y, r2.x
+ add r2.xyz, r2.xxxx, r3.xyzx
+ dp3 r2.w, r2.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.x, r2.y, r2.x
+ min r3.x, r2.z, r3.x
+ max r3.y, r2.y, r2.x
+ max r3.y, r2.z, r3.y
+ lt r3.z, r3.x, l(0.000000)
+ add r4.xyz, -r2.wwww, r2.xyzx
+ mul r4.xyz, r2.wwww, r4.xyzx
+ add r3.x, r2.w, -r3.x
+ div r4.xyz, r4.xyzx, r3.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.zzzz, r4.xyzx, r2.xyzx
+ lt r3.x, l(1.000000), r3.y
+ add r4.xyz, -r2.wwww, r2.xyzx
+ add r3.z, -r2.w, l(1.000000)
+ mul r4.xyz, r3.zzzz, r4.xyzx
+ add r3.y, -r2.w, r3.y
+ div r3.yzw, r4.xxyz, r3.yyyy
+ add r3.yzw, r2.wwww, r3.yyzw
+ movc r2.xyz, r3.xxxx, r3.yzwy, r2.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(13)
+ if_nz r2.w
+ max r2.w, r0.z, r0.y
+ max r2.w, r0.x, r2.w
+ min r3.x, r0.z, r0.y
+ min r3.x, r0.x, r3.x
+ add r3.w, r2.w, -r3.x
+ ge r2.w, r1.y, r1.x
+ if_nz r2.w
+ add r4.xyzw, -r1.xxzz, r1.yzxy
+ lt r5.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r4.yxwy
+ div r6.xyz, r3.wwww, r4.yxwy
+ mul r3.xyz, r4.xyzx, r6.xyzx
+ movc r6.yz, r5.xxxx, r3.xxwx, r4.xxyx
+ ge r5.xw, r1.zzzz, r1.yyyx
+ movc r7.yz, r5.yyyy, r3.wwyw, r4.xxyx
+ movc r4.xy, r5.zzzz, r3.zwzz, r4.zwzz
+ mov r7.x, l(0)
+ mov r4.z, l(0)
+ movc r4.xyz, r5.wwww, r7.xyzx, r4.xyzx
+ mov r6.x, l(0)
+ movc r4.xyz, r5.xxxx, r6.xyzx, r4.xyzx
+ else
+ add r5.xyzw, -r1.yyzz, r1.xzyx
+ lt r6.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r5.yxwy
+ div r7.xyz, r3.wwww, r5.yxwy
+ mul r3.xyz, r5.xyzx, r7.xyzx
+ movc r7.xz, r6.xxxx, r3.xxwx, r5.xxyx
+ ge r6.xw, r1.zzzz, r1.xxxy
+ movc r8.xz, r6.yyyy, r3.wwyw, r5.xxyx
+ movc r3.xy, r6.zzzz, r3.wzww, r5.wzww
+ mov r8.y, l(0)
+ mov r3.z, l(0)
+ movc r3.xyz, r6.wwww, r8.xyzx, r3.xyzx
+ mov r7.y, l(0)
+ movc r4.xyz, r6.xxxx, r7.xyzx, r3.xyzx
+ endif
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r4.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r3.xyz, r2.wwww, r4.xyzx
+ dp3 r2.w, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.w, r3.y, r3.x
+ min r3.w, r3.z, r3.w
+ max r4.x, r3.y, r3.x
+ max r4.x, r3.z, r4.x
+ lt r4.y, r3.w, l(0.000000)
+ add r5.xyz, -r2.wwww, r3.xyzx
+ mul r5.xyz, r2.wwww, r5.xyzx
+ add r3.w, r2.w, -r3.w
+ div r5.xyz, r5.xyzx, r3.wwww
+ add r5.xyz, r2.wwww, r5.xyzx
+ movc r3.xyz, r4.yyyy, r5.xyzx, r3.xyzx
+ lt r3.w, l(1.000000), r4.x
+ add r4.yzw, -r2.wwww, r3.xxyz
+ add r5.x, -r2.w, l(1.000000)
+ mul r4.yzw, r4.yyzw, r5.xxxx
+ add r4.x, -r2.w, r4.x
+ div r4.xyz, r4.yzwy, r4.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.wwww, r4.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(14)
+ if_nz r2.w
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r0.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r3.xyz, r0.xyzx, r2.wwww
+ dp3 r2.w, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.w, r3.y, r3.x
+ min r3.w, r3.z, r3.w
+ max r4.x, r3.y, r3.x
+ max r4.x, r3.z, r4.x
+ lt r4.y, r3.w, l(0.000000)
+ add r5.xyz, -r2.wwww, r3.xyzx
+ mul r5.xyz, r2.wwww, r5.xyzx
+ add r3.w, r2.w, -r3.w
+ div r5.xyz, r5.xyzx, r3.wwww
+ add r5.xyz, r2.wwww, r5.xyzx
+ movc r3.xyz, r4.yyyy, r5.xyzx, r3.xyzx
+ lt r3.w, l(1.000000), r4.x
+ add r4.yzw, -r2.wwww, r3.xxyz
+ add r5.x, -r2.w, l(1.000000)
+ mul r4.yzw, r4.yyzw, r5.xxxx
+ add r4.x, -r2.w, r4.x
+ div r4.xyz, r4.yzwy, r4.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.wwww, r4.xyzx, r3.xyzx
+ else
+ dp3 r2.w, r0.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r1.xyz, r1.xyzx, r2.wwww
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.x, r1.y, r1.x
+ min r3.x, r1.z, r3.x
+ max r3.y, r1.y, r1.x
+ max r3.y, r1.z, r3.y
+ lt r3.z, r3.x, l(0.000000)
+ add r4.xyz, r1.xyzx, -r2.wwww
+ mul r4.xyz, r2.wwww, r4.xyzx
+ add r3.x, r2.w, -r3.x
+ div r4.xyz, r4.xyzx, r3.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r1.xyz, r3.zzzz, r4.xyzx, r1.xyzx
+ lt r3.x, l(1.000000), r3.y
+ add r4.xyz, -r2.wwww, r1.xyzx
+ add r3.z, -r2.w, l(1.000000)
+ mul r4.xyz, r3.zzzz, r4.xyzx
+ add r3.y, -r2.w, r3.y
+ div r3.yzw, r4.xxyz, r3.yyyy
+ add r3.yzw, r2.wwww, r3.yyzw
+ movc r2.xyz, r3.xxxx, r3.yzwy, r1.xyzx
+ endif
+ endif
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 195 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleRadialGradient
+{
+ pass APos
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ mul r0.xzw, r0, r1.x
+ mov r1.w, c1.w
+ mad r1.xyz, r0.xzww, c0.z, r1.w
+ cmp r2.x, r1.x, r0.x, r0.w
+ cmp r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.x
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ rcp r0.x, r0.x
+ mul r0.x, r0.x, r0.w
+ mov r0.y, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+ pass APosWrap
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sWrapSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ mul r0.xzw, r0, r1.x
+ mov r1.w, c1.w
+ mad r1.xyz, r0.xzww, c0.z, r1.w
+ cmp r2.x, r1.x, r0.x, r0.w
+ cmp r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0Wrap
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sWrapSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.x
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ rcp r0.x, r0.x
+ mul r0.x, r0.x, r0.w
+ mov r0.y, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+ pass APosMirror
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMirrorSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ mul r0.xzw, r0, r1.x
+ mov r1.w, c1.w
+ mad r1.xyz, r0.xzww, c0.z, r1.w
+ cmp r2.x, r1.x, r0.x, r0.w
+ cmp r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0Mirror
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMirrorSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.x
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ rcp r0.x, r0.x
+ mul r0.x, r0.x, r0.w
+ mov r0.y, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleMaskedTexture
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.xy, t0.wzzw
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+ // approximately 5 instruction slots used (2 texture, 3 arithmetic)
+ ps_4_0
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s1
+ mul o0.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 4 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureWithShadow
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendH;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48 [unused]
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sShadowSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ // c3 cb0 6 4 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ add r0.x, t0.x, c0.y
+ mov r0.y, t0.y
+ add r1.x, t0.x, c0.x
+ mov r1.y, t0.y
+ texld r0, r0, s0
+ texld r1, r1, s0
+ mul r0.x, r0.w, c3.y
+ mad r0.x, c3.x, r1.w, r0.x
+ add r1.x, t0.x, c0.z
+ mov r1.y, t0.y
+ add r2.x, t0.x, c0.w
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c3.z, r1.w, r0.x
+ mad r0.x, c3.w, r2.w, r0.x
+ add r1.x, t0.x, c1.x
+ mov r1.y, t0.y
+ add r2.x, t0.x, c1.y
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c4.x, r1.w, r0.x
+ mad r0.x, c4.y, r2.w, r0.x
+ add r1.x, t0.x, c1.z
+ mov r1.y, t0.y
+ add r2.x, t0.x, c1.w
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c4.z, r1.w, r0.x
+ mad r0.x, c4.w, r2.w, r0.x
+ add r1.x, t0.x, c2.x
+ mov r1.y, t0.y
+ texld r1, r1, s0
+ mad r0.x, c5.x, r1.w, r0.x
+ mul r0, r0.x, c6
+ mov oC0, r0
+
+ // approximately 38 instruction slots used (9 texture, 29 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[10], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 4
+ add r0.xyzw, v1.xxxx, cb0[0].zxwy
+ mov r1.xz, r0.yywy
+ mov r1.yw, v1.yyyy
+ sample r2.xyzw, r1.zwzz, t0.xyzw, s0
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mul r1.x, r2.w, cb0[6].y
+ mad r1.x, cb0[6].x, r1.w, r1.x
+ mov r0.yw, v1.yyyy
+ sample r2.xyzw, r0.xyxx, t0.xyzw, s0
+ sample r0.xyzw, r0.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[6].z, r2.w, r1.x
+ mad r0.x, cb0[6].w, r0.w, r0.x
+ add r1.xyzw, v1.xxxx, cb0[1].zxwy
+ mov r2.xz, r1.yywy
+ mov r2.yw, v1.yyyy
+ sample r3.xyzw, r2.xyxx, t0.xyzw, s0
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[7].x, r3.w, r0.x
+ mad r0.x, cb0[7].y, r2.w, r0.x
+ mov r1.yw, v1.yyyy
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[7].z, r2.w, r0.x
+ mad r0.x, cb0[7].w, r1.w, r0.x
+ add r1.x, v1.x, cb0[2].x
+ mov r1.y, v1.y
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mad r0.x, cb0[8].x, r1.w, r0.x
+ mul o0.xyzw, r0.xxxx, cb0[9].xyzw
+ ret
+ // Approximately 30 instruction slots used
+
+ };
+ }
+
+ pass P1
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendV;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48 [unused]
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sShadowSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 6 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ add r0.y, t0.y, c0.y
+ mov r0.x, t0.x
+ add r1.y, t0.y, c0.x
+ mov r1.x, t0.x
+ texld r0, r0, s0
+ texld r1, r1, s0
+ mul r0, r0, c3.y
+ mad r0, c3.x, r1, r0
+ add r1.y, t0.y, c0.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c0.w
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c3.z, r1, r0
+ mad r0, c3.w, r2, r0
+ add r1.y, t0.y, c1.x
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.y
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c4.x, r1, r0
+ mad r0, c4.y, r2, r0
+ add r1.y, t0.y, c1.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.w
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c4.z, r1, r0
+ mad r0, c4.w, r2, r0
+ add r1.y, t0.y, c2.x
+ mov r1.x, t0.x
+ texld r1, r1, s0
+ mad r0, c5.x, r1, r0
+ mov oC0, r0
+
+ // approximately 37 instruction slots used (9 texture, 28 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[9], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 4
+ mov r0.xz, v1.xxxx
+ add r1.xyzw, v1.yyyy, cb0[3].xzyw
+ mov r0.yw, r1.xxxz
+ sample r2.xyzw, r0.zwzz, t0.xyzw, s0
+ sample r0.xyzw, r0.xyxx, t0.xyzw, s0
+ mul r2.xyzw, r2.xyzw, cb0[6].yyyy
+ mad r0.xyzw, cb0[6].xxxx, r0.xyzw, r2.xyzw
+ mov r1.xz, v1.xxxx
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[6].zzzz, r2.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[6].wwww, r1.xyzw, r0.xyzw
+ mov r1.xz, v1.xxxx
+ add r2.xyzw, v1.yyyy, cb0[4].xzyw
+ mov r1.yw, r2.xxxz
+ sample r3.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[7].xxxx, r3.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].yyyy, r1.xyzw, r0.xyzw
+ mov r2.xz, v1.xxxx
+ sample r1.xyzw, r2.xyxx, t0.xyzw, s0
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[7].zzzz, r1.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].wwww, r2.xyzw, r0.xyzw
+ add r1.y, v1.y, cb0[5].x
+ mov r1.x, v1.x
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mad o0.xyzw, cb0[8].xxxx, r1.xyzw, r0.xyzw
+ ret
+ // Approximately 29 instruction slots used
+
+ };
+ }
+
+ pass P2
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendV;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48 [unused]
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMaskSampler sampler NA NA 0 1
+ // sShadowSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 6 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t1
+ // s1 s1 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.y, t0.y, c0.y
+ mov r0.x, t0.x
+ add r1.y, t0.y, c0.x
+ mov r1.x, t0.x
+ texld r0, r0, s1
+ texld r1, r1, s1
+ mul r0, r0, c3.y
+ mad r0, c3.x, r1, r0
+ add r1.y, t0.y, c0.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c0.w
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c3.z, r1, r0
+ mad r0, c3.w, r2, r0
+ add r1.y, t0.y, c1.x
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.y
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c4.x, r1, r0
+ mad r0, c4.y, r2, r0
+ add r1.y, t0.y, c1.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.w
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c4.z, r1, r0
+ mad r0, c4.w, r2, r0
+ add r1.y, t0.y, c2.x
+ mov r1.x, t0.x
+ mov r2.xy, t0.wzzw
+ texld r1, r1, s1
+ texld r2, r2, s0
+ mad r0, c5.x, r1, r0
+ mul r0, r2.w, r0
+ mov oC0, r0
+
+ // approximately 40 instruction slots used (10 texture, 30 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[9], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 4
+ mov r0.xz, v1.xxxx
+ add r1.xyzw, v1.yyyy, cb0[3].xzyw
+ mov r0.yw, r1.xxxz
+ sample r2.xyzw, r0.zwzz, t0.xyzw, s1
+ sample r0.xyzw, r0.xyxx, t0.xyzw, s1
+ mul r2.xyzw, r2.xyzw, cb0[6].yyyy
+ mad r0.xyzw, cb0[6].xxxx, r0.xyzw, r2.xyzw
+ mov r1.xz, v1.xxxx
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s1
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[6].zzzz, r2.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[6].wwww, r1.xyzw, r0.xyzw
+ mov r1.xz, v1.xxxx
+ add r2.xyzw, v1.yyyy, cb0[4].xzyw
+ mov r1.yw, r2.xxxz
+ sample r3.xyzw, r1.xyxx, t0.xyzw, s1
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[7].xxxx, r3.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].yyyy, r1.xyzw, r0.xyzw
+ mov r2.xz, v1.xxxx
+ sample r1.xyzw, r2.xyxx, t0.xyzw, s1
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[7].zzzz, r1.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].wwww, r2.xyzw, r0.xyzw
+ add r1.y, v1.y, cb0[5].x
+ mov r1.x, v1.x
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s1
+ mad r0.xyzw, cb0[8].xxxx, r1.xyzw, r0.xyzw
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s0
+ mul o0.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 31 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextTexture
+{
+ pass Unmasked
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(0, 0, 0, 0);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = bTextBlend;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16 [unused]
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16 [unused]
+ // float4 TextColor; // Offset: 48 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ // SV_Target 1 xyzw 1 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ mov r0.xyz, c0
+ mad r0, r0.xyzx, c1.xxxy, c1.yyyx
+ mov oC0, r0
+ texld r0, t0, s0
+ mul r0, r0.zyxy, c0.w
+ mov oC1, r0
+
+ // approximately 6 instruction slots used (1 texture, 5 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[4], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_output o1.xyzw
+ dcl_temps 1
+ mov o0.xyz, cb0[3].xyzx
+ mov o0.w, l(1.000000)
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ mul o1.xyzw, r0.zyxy, cb0[3].wwww
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ }
+
+ pass Masked
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(0, 0, 0, 0);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = bTextBlend;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16 [unused]
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16 [unused]
+ // float4 TextColor; // Offset: 48 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ // SV_Target 1 xyzw 1 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.xyz, c0
+ mad r0, r0.xyzx, c1.xxxy, c1.yyyx
+ mov oC0, r0
+ mov r0.xy, t0.wzzw
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1, r1.zyxy, c0.w
+ mul r0, r0.w, r1
+ mov oC1, r0
+
+ // approximately 9 instruction slots used (2 texture, 7 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[4], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_output o1.xyzw
+ dcl_temps 2
+ mov o0.xyz, cb0[3].xyzx
+ mov o0.w, l(1.000000)
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r0.xyzw, r0.zyxy, cb0[3].wwww
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s1
+ mul o1.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 7 instruction slots used
+
+ };
+ }
+
+}
+
+#endif
+
+const BYTE d2deffect[] =
+{
+ 68, 88, 66, 67, 116, 210,
+ 237, 43, 26, 169, 147, 99,
+ 62, 90, 128, 241, 238, 193,
+ 236, 181, 1, 0, 0, 0,
+ 242, 19, 1, 0, 1, 0,
+ 0, 0, 36, 0, 0, 0,
+ 70, 88, 49, 48, 198, 19,
+ 1, 0, 1, 16, 255, 254,
+ 4, 0, 0, 0, 16, 0,
+ 0, 0, 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 62, 7,
+ 1, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 32, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 36, 71,
+ 108, 111, 98, 97, 108, 115,
+ 0, 117, 105, 110, 116, 0,
+ 13, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 25, 9, 0, 0, 98, 108,
+ 101, 110, 100, 111, 112, 0,
+ 99, 98, 48, 0, 102, 108,
+ 111, 97, 116, 52, 0, 58,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 10,
+ 33, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 99, 98,
+ 49, 0, 58, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 48, 0,
+ 0, 0, 10, 33, 0, 0,
+ 66, 108, 117, 114, 79, 102,
+ 102, 115, 101, 116, 115, 72,
+ 0, 66, 108, 117, 114, 79,
+ 102, 102, 115, 101, 116, 115,
+ 86, 0, 66, 108, 117, 114,
+ 87, 101, 105, 103, 104, 116,
+ 115, 0, 83, 104, 97, 100,
+ 111, 119, 67, 111, 108, 111,
+ 114, 0, 99, 98, 50, 0,
+ 102, 108, 111, 97, 116, 51,
+ 120, 51, 0, 222, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 48, 0, 0, 0, 36,
+ 0, 0, 0, 11, 91, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 102, 108, 111, 97, 116, 50,
+ 0, 26, 1, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 0, 0, 0, 16,
+ 0, 0, 0, 8, 0, 0,
+ 0, 10, 17, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 102, 108,
+ 111, 97, 116, 51, 0, 72,
+ 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 0, 16, 0, 0,
+ 0, 12, 0, 0, 0, 10,
+ 25, 0, 0, 100, 105, 102,
+ 102, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 102, 108,
+ 111, 97, 116, 0, 120, 1,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 16, 0, 0, 0,
+ 4, 0, 0, 0, 9, 9,
+ 0, 0, 65, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 84,
+ 101, 120, 116, 117, 114, 101,
+ 50, 68, 0, 175, 1, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 116, 101, 120, 0, 98,
+ 99, 107, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 83,
+ 97, 109, 112, 108, 101, 114,
+ 83, 116, 97, 116, 101, 0,
+ 229, 1, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 21, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 21, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 115, 66, 99, 107, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 21, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 115, 87, 114, 97, 112,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 21, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 115, 77, 105, 114,
+ 114, 111, 114, 83, 97, 109,
+ 112, 108, 101, 114, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 21, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 21, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 115, 83, 104, 97, 100, 111,
+ 119, 83, 97, 109, 112, 108,
+ 101, 114, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 21,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 4,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 4,
+ 0, 0, 0, 4, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 82, 97, 115,
+ 116, 101, 114, 105, 122, 101,
+ 114, 83, 116, 97, 116, 101,
+ 0, 87, 3, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 84,
+ 101, 120, 116, 117, 114, 101,
+ 82, 97, 115, 116, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 0, 0, 0, 66,
+ 108, 101, 110, 100, 83, 116,
+ 97, 116, 101, 0, 167, 3,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 83, 104, 97, 100,
+ 111, 119, 66, 108, 101, 110,
+ 100, 72, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 15,
+ 0, 0, 0, 83, 104, 97,
+ 100, 111, 119, 66, 108, 101,
+ 110, 100, 86, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 0, 0, 0, 98, 84,
+ 101, 120, 116, 66, 108, 101,
+ 110, 100, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 17,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 18,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 19,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 15,
+ 0, 0, 0, 83, 97, 109,
+ 112, 108, 101, 84, 101, 120,
+ 116, 117, 114, 101, 0, 80,
+ 48, 0, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 248, 0, 0, 0, 244, 1,
+ 0, 0, 112, 2, 0, 0,
+ 160, 3, 0, 0, 212, 3,
+ 0, 0, 65, 111, 110, 57,
+ 184, 0, 0, 0, 184, 0,
+ 0, 0, 0, 2, 254, 255,
+ 132, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 48, 0, 0, 0, 36, 0,
+ 1, 0, 48, 0, 0, 0,
+ 0, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 2, 0, 238, 160, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 12, 224, 0, 0,
+ 20, 144, 3, 0, 180, 160,
+ 3, 0, 20, 160, 4, 0,
+ 0, 4, 0, 0, 3, 128,
+ 0, 0, 228, 144, 1, 0,
+ 238, 160, 1, 0, 228, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 192, 0, 0, 228, 128,
+ 0, 0, 228, 160, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 4, 0, 68, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 244, 0, 0, 0, 64, 0,
+ 1, 0, 61, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 0, 0, 0, 0, 103, 0,
+ 0, 4, 242, 32, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 32, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 194, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 11,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 6, 20, 16, 0,
+ 0, 0, 0, 0, 166, 142,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 3, 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, 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,
+ 82, 68, 69, 70, 40, 1,
+ 0, 0, 1, 0, 0, 0,
+ 64, 0, 0, 0, 1, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 254, 255, 0, 1,
+ 0, 0, 246, 0, 0, 0,
+ 60, 0, 0, 0, 0, 0,
+ 0, 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, 99, 98, 48, 0,
+ 60, 0, 0, 0, 4, 0,
+ 0, 0, 88, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 184, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 212, 0, 0, 0, 16, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 222, 0, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 236, 0, 0, 0, 48, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 81, 117, 97, 100, 68, 101,
+ 115, 99, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 77, 97, 115, 107,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 84, 101,
+ 120, 116, 67, 111, 108, 111,
+ 114, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 232, 4, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 212, 2, 0, 0,
+ 68, 88, 66, 67, 17, 106,
+ 69, 218, 119, 68, 79, 85,
+ 211, 176, 27, 183, 77, 210,
+ 131, 41, 1, 0, 0, 0,
+ 212, 2, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 164, 0, 0, 0, 16, 1,
+ 0, 0, 140, 1, 0, 0,
+ 48, 2, 0, 0, 160, 2,
+ 0, 0, 65, 111, 110, 57,
+ 100, 0, 0, 0, 100, 0,
+ 0, 0, 0, 2, 255, 255,
+ 60, 0, 0, 0, 40, 0,
+ 0, 0, 0, 0, 40, 0,
+ 0, 0, 40, 0, 0, 0,
+ 40, 0, 1, 0, 36, 0,
+ 0, 0, 40, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 100, 0, 0, 0, 64, 0,
+ 0, 0, 25, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 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,
+ 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,
+ 82, 68, 69, 70, 156, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 105, 0, 0, 0,
+ 92, 0, 0, 0, 3, 0,
+ 0, 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, 101, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 101, 120, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 68, 9,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 70, 111, 114, 83, 101,
+ 112, 97, 114, 97, 98, 108,
+ 101, 66, 108, 101, 110, 100,
+ 105, 110, 103, 95, 49, 0,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 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,
+ 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, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
+ 0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 72, 12,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 13, 0, 0, 68, 88,
+ 66, 67, 193, 65, 249, 15,
+ 188, 209, 36, 123, 179, 111,
+ 3, 63, 40, 10, 7, 98,
+ 1, 0, 0, 0, 72, 13,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 172, 4,
+ 0, 0, 188, 10, 0, 0,
+ 56, 11, 0, 0, 164, 12,
+ 0, 0, 20, 13, 0, 0,
+ 65, 111, 110, 57, 108, 4,
+ 0, 0, 108, 4, 0, 0,
+ 0, 2, 255, 255, 52, 4,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 192,
+ 0, 0, 64, 192, 0, 0,
+ 128, 192, 81, 0, 0, 5,
+ 2, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 192, 81, 0, 0, 5,
+ 3, 0, 15, 160, 0, 0,
+ 160, 192, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 0, 160, 2, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 3, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 0, 128, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 2, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 3, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 128, 2, 0,
+ 0, 161, 5, 0, 0, 3,
+ 3, 0, 7, 128, 3, 0,
+ 228, 128, 3, 0, 228, 128,
+ 4, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 129, 2, 0,
+ 0, 160, 6, 0, 0, 2,
+ 3, 0, 8, 128, 4, 0,
+ 0, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 5, 0, 7, 128, 1, 0,
+ 228, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 4, 0, 255, 129, 2, 0,
+ 170, 160, 5, 0, 0, 3,
+ 3, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 128,
+ 10, 0, 0, 3, 4, 0,
+ 8, 128, 3, 0, 255, 128,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 0, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 6, 0,
+ 7, 128, 5, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 7, 0, 1, 128,
+ 6, 0, 0, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
+ 5, 0, 0, 3, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 85, 128, 10, 0,
+ 0, 3, 5, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 85, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 2, 128,
+ 6, 0, 85, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 170, 128,
+ 5, 0, 0, 3, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 170, 128, 10, 0,
+ 0, 3, 5, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 170, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 4, 128,
+ 6, 0, 170, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 3, 0,
+ 7, 128, 0, 0, 85, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 4, 6, 0, 7, 128,
+ 2, 0, 228, 128, 0, 0,
+ 85, 128, 5, 0, 228, 128,
+ 4, 0, 0, 4, 6, 0,
+ 7, 128, 3, 0, 228, 128,
+ 5, 0, 228, 129, 6, 0,
+ 228, 128, 11, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 5, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 0, 129,
+ 8, 0, 228, 128, 7, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 15, 128, 0, 0,
+ 255, 128, 1, 0, 228, 160,
+ 5, 0, 0, 3, 7, 0,
+ 15, 128, 7, 0, 228, 128,
+ 7, 0, 228, 128, 10, 0,
+ 0, 3, 8, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 255, 129, 8, 0, 228, 128,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 8, 0, 7, 128,
+ 5, 0, 228, 128, 2, 0,
+ 255, 161, 2, 0, 0, 161,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 8, 0, 228, 129,
+ 2, 0, 0, 160, 4, 0,
+ 0, 4, 4, 0, 7, 128,
+ 4, 0, 228, 128, 8, 0,
+ 228, 129, 2, 0, 0, 160,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 5, 0, 228, 128,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 5, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
+ 228, 128, 5, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 8, 0, 228, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 8, 0, 228, 128, 4, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 170, 129, 1, 0, 228, 128,
+ 0, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 85, 129, 6, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 7, 0, 0, 129,
+ 5, 0, 228, 128, 0, 0,
+ 228, 128, 18, 0, 0, 4,
+ 4, 0, 7, 128, 1, 0,
+ 255, 128, 0, 0, 228, 128,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 8, 128,
+ 1, 0, 255, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 4, 0, 8, 128, 4, 0,
+ 255, 129, 2, 0, 0, 160,
+ 2, 0, 85, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 2, 0, 255, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 8, 128, 2, 0,
+ 255, 128, 2, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 255, 129,
+ 2, 0, 0, 160, 2, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 8, 128, 4, 0,
+ 255, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 0, 0, 255, 129,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 2, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 8, 6,
+ 0, 0, 64, 0, 0, 0,
+ 130, 1, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 7, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 24, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 60, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 1, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 2, 0, 0, 0,
+ 31, 0, 4, 3, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 18, 0, 0, 1,
+ 32, 0, 0, 8, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 31, 0, 4, 3,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 29, 0, 0, 10,
+ 114, 0, 16, 0, 3, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 50, 0, 0, 15,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 128, 191, 0, 0,
+ 0, 0, 0, 0, 0, 11,
+ 114, 0, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 5, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 50, 0, 0, 13, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 5, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 4, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 51, 0,
+ 0, 7, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 18, 0, 0, 1,
+ 32, 0, 0, 8, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 5, 0,
+ 0, 0, 31, 0, 4, 3,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 52, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 18, 0, 0, 1, 24, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
+ 51, 0, 0, 10, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 21, 0, 0, 1, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 0, 0, 0, 8, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 57, 0, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 25, 0,
+ 0, 0, 5, 0, 0, 0,
+ 1, 0, 0, 0, 7, 0,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 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, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 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, 197, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 213, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 164, 16, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 83, 101, 112, 97,
+ 114, 97, 98, 108, 101, 66,
+ 108, 101, 110, 100, 105, 110,
+ 103, 95, 50, 0, 68, 4,
+ 0, 0, 68, 88, 66, 67,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 248, 0, 0, 0,
+ 244, 1, 0, 0, 112, 2,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
+ 110, 57, 184, 0, 0, 0,
+ 184, 0, 0, 0, 0, 2,
+ 254, 255, 132, 0, 0, 0,
+ 52, 0, 0, 0, 1, 0,
+ 36, 0, 0, 0, 48, 0,
+ 0, 0, 48, 0, 0, 0,
+ 36, 0, 1, 0, 48, 0,
+ 0, 0, 0, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 5, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 224, 0, 0,
+ 228, 144, 2, 0, 238, 160,
+ 2, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 12, 224,
+ 0, 0, 20, 144, 3, 0,
+ 180, 160, 3, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 1, 0, 238, 160, 1, 0,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 4, 0, 68, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 244, 0, 0, 0,
+ 64, 0, 1, 0, 61, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 194, 32, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 194, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 50, 0, 0, 11,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 194, 32, 16, 0,
+ 1, 0, 0, 0, 6, 20,
+ 16, 0, 0, 0, 0, 0,
+ 166, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 132, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 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, 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, 82, 68, 69, 70,
+ 40, 1, 0, 0, 1, 0,
+ 0, 0, 64, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 246, 0,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 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, 99, 98,
+ 48, 0, 60, 0, 0, 0,
+ 4, 0, 0, 0, 88, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 222, 0, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 236, 0, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 28, 30, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 88, 17,
+ 0, 0, 68, 88, 66, 67,
+ 62, 116, 36, 238, 73, 63,
+ 158, 95, 222, 192, 91, 113,
+ 112, 55, 55, 145, 1, 0,
+ 0, 0, 88, 17, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 88, 6, 0, 0,
+ 204, 14, 0, 0, 72, 15,
+ 0, 0, 180, 16, 0, 0,
+ 36, 17, 0, 0, 65, 111,
+ 110, 57, 24, 6, 0, 0,
+ 24, 6, 0, 0, 0, 2,
+ 255, 255, 224, 5, 0, 0,
+ 56, 0, 0, 0, 1, 0,
+ 44, 0, 0, 0, 56, 0,
+ 0, 0, 56, 0, 2, 0,
+ 36, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 224, 192,
+ 0, 0, 0, 193, 0, 0,
+ 16, 193, 0, 0, 32, 193,
+ 81, 0, 0, 5, 2, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 128, 191, 0, 0, 128, 62,
+ 81, 0, 0, 5, 3, 0,
+ 15, 160, 0, 0, 0, 63,
+ 0, 0, 0, 64, 0, 0,
+ 128, 191, 0, 0, 128, 64,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 65,
+ 0, 0, 64, 193, 0, 0,
+ 0, 64, 0, 0, 128, 63,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 0, 160,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 6, 0,
+ 0, 2, 3, 0, 8, 128,
+ 2, 0, 255, 128, 4, 0,
+ 0, 4, 3, 0, 3, 128,
+ 2, 0, 233, 128, 3, 0,
+ 255, 129, 2, 0, 255, 160,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 3, 0, 255, 128, 4, 0,
+ 0, 4, 5, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 0, 160, 4, 0, 85, 160,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 5, 0, 228, 128,
+ 4, 0, 228, 128, 3, 0,
+ 255, 160, 5, 0, 0, 3,
+ 5, 0, 7, 128, 4, 0,
+ 228, 128, 5, 0, 228, 128,
+ 7, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 8, 128, 3, 0, 0, 128,
+ 5, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 0, 4,
+ 4, 0, 8, 128, 2, 0,
+ 85, 128, 3, 0, 255, 129,
+ 4, 0, 255, 128, 6, 0,
+ 0, 2, 3, 0, 1, 128,
+ 1, 0, 255, 128, 5, 0,
+ 0, 3, 6, 0, 7, 128,
+ 1, 0, 228, 128, 3, 0,
+ 0, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 3, 0, 85, 160,
+ 3, 0, 170, 160, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 7, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 85, 128,
+ 4, 0, 0, 4, 8, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 129, 3, 0,
+ 0, 160, 4, 0, 0, 4,
+ 9, 0, 15, 128, 2, 0,
+ 36, 128, 3, 0, 255, 129,
+ 2, 0, 192, 160, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 6, 0, 228, 128, 4, 0,
+ 170, 161, 4, 0, 255, 160,
+ 5, 0, 0, 3, 10, 0,
+ 7, 128, 4, 0, 228, 128,
+ 10, 0, 228, 128, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 10, 0, 228, 128, 9, 0,
+ 228, 129, 4, 0, 228, 128,
+ 88, 0, 0, 4, 11, 0,
+ 2, 128, 8, 0, 85, 128,
+ 10, 0, 85, 128, 4, 0,
+ 255, 128, 7, 0, 0, 2,
+ 4, 0, 8, 128, 4, 0,
+ 170, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 4, 0,
+ 255, 128, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 85, 128, 5, 0, 170, 128,
+ 4, 0, 255, 128, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 2, 0, 170, 128, 3, 0,
+ 255, 129, 4, 0, 255, 128,
+ 4, 0, 0, 4, 4, 0,
+ 8, 128, 7, 0, 170, 128,
+ 4, 0, 255, 128, 4, 0,
+ 170, 128, 88, 0, 0, 4,
+ 11, 0, 4, 128, 8, 0,
+ 170, 128, 10, 0, 170, 128,
+ 4, 0, 255, 128, 7, 0,
+ 0, 2, 4, 0, 8, 128,
+ 4, 0, 0, 128, 6, 0,
+ 0, 2, 4, 0, 8, 128,
+ 4, 0, 255, 128, 88, 0,
+ 0, 4, 4, 0, 8, 128,
+ 9, 0, 255, 128, 5, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 4, 0,
+ 8, 128, 2, 0, 0, 128,
+ 3, 0, 255, 129, 4, 0,
+ 255, 128, 4, 0, 0, 4,
+ 2, 0, 7, 128, 2, 0,
+ 228, 128, 3, 0, 255, 128,
+ 2, 0, 170, 160, 5, 0,
+ 0, 3, 2, 0, 7, 128,
+ 2, 0, 228, 128, 2, 0,
+ 228, 128, 4, 0, 0, 4,
+ 4, 0, 8, 128, 7, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 128, 2, 0,
+ 0, 3, 3, 0, 14, 128,
+ 7, 0, 144, 129, 2, 0,
+ 0, 160, 4, 0, 0, 4,
+ 3, 0, 14, 128, 9, 0,
+ 144, 128, 3, 0, 228, 129,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 11, 0, 1, 128,
+ 8, 0, 0, 128, 10, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 128, 4, 0,
+ 228, 129, 4, 0, 0, 4,
+ 7, 0, 7, 128, 1, 0,
+ 228, 128, 3, 0, 0, 128,
+ 4, 0, 228, 128, 35, 0,
+ 0, 2, 5, 0, 7, 128,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 10, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 228, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 10, 0,
+ 228, 128, 3, 0, 85, 161,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 5, 0, 7, 128,
+ 0, 0, 255, 129, 5, 0,
+ 228, 128, 7, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 7, 128, 0, 0, 170, 129,
+ 11, 0, 228, 128, 5, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 6, 0, 228, 128,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 4, 0, 228, 128,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 3, 0, 7, 128,
+ 8, 0, 228, 128, 4, 0,
+ 228, 128, 3, 0, 249, 128,
+ 88, 0, 0, 4, 0, 0,
+ 14, 128, 0, 0, 85, 129,
+ 3, 0, 144, 128, 5, 0,
+ 144, 128, 6, 0, 0, 2,
+ 6, 0, 8, 128, 6, 0,
+ 0, 128, 4, 0, 0, 4,
+ 6, 0, 8, 128, 9, 0,
+ 0, 128, 6, 0, 255, 129,
+ 2, 0, 0, 160, 11, 0,
+ 0, 3, 3, 0, 1, 128,
+ 6, 0, 255, 128, 2, 0,
+ 85, 160, 5, 0, 0, 3,
+ 3, 0, 14, 128, 6, 0,
+ 144, 128, 6, 0, 144, 128,
+ 88, 0, 0, 4, 6, 0,
+ 8, 128, 3, 0, 85, 129,
+ 2, 0, 85, 160, 3, 0,
+ 0, 128, 88, 0, 0, 4,
+ 4, 0, 1, 128, 2, 0,
+ 0, 129, 2, 0, 0, 160,
+ 6, 0, 255, 128, 6, 0,
+ 0, 2, 4, 0, 8, 128,
+ 6, 0, 85, 128, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 9, 0, 85, 128, 4, 0,
+ 255, 129, 2, 0, 0, 160,
+ 11, 0, 0, 3, 6, 0,
+ 8, 128, 4, 0, 255, 128,
+ 2, 0, 85, 160, 88, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 170, 129, 2, 0,
+ 85, 160, 6, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 2, 128, 2, 0, 85, 129,
+ 2, 0, 0, 160, 4, 0,
+ 255, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 6, 0,
+ 170, 128, 4, 0, 0, 4,
+ 4, 0, 8, 128, 9, 0,
+ 170, 128, 4, 0, 255, 129,
+ 2, 0, 0, 160, 11, 0,
+ 0, 3, 6, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
+ 85, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 85, 160,
+ 6, 0, 255, 128, 88, 0,
+ 0, 4, 4, 0, 4, 128,
+ 2, 0, 170, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 0, 129,
+ 4, 0, 228, 128, 0, 0,
+ 249, 128, 18, 0, 0, 4,
+ 3, 0, 7, 128, 2, 0,
+ 255, 128, 0, 0, 228, 128,
+ 6, 0, 228, 128, 5, 0,
+ 0, 3, 3, 0, 8, 128,
+ 2, 0, 255, 128, 2, 0,
+ 255, 128, 88, 0, 0, 4,
+ 3, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 0, 160,
+ 2, 0, 85, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 1, 0, 255, 128, 3, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 8, 128, 1, 0,
+ 255, 128, 1, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 255, 129,
+ 2, 0, 0, 160, 2, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 8, 128, 3, 0,
+ 255, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 255, 129,
+ 0, 0, 228, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 1, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 108, 8,
+ 0, 0, 64, 0, 0, 0,
+ 27, 2, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 7, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 24, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 60, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 7, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 24, 0, 0, 10, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 51, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 11,
+ 114, 0, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 4, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 8, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 50, 0, 0, 13,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 9, 0, 0, 0,
+ 31, 0, 4, 3, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 29, 0, 0, 10, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 65, 0, 0, 128, 65,
+ 0, 0, 128, 65, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 64, 193, 0, 0,
+ 64, 193, 0, 0, 64, 193,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 64, 0, 0,
+ 128, 64, 0, 0, 128, 64,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 75, 0, 0, 5,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 16,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 50, 0, 0, 10, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 13, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 55, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 128, 129, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 21, 0, 0, 1, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 0, 0, 0, 8, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 66, 0, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 38, 0,
+ 0, 0, 4, 0, 0, 0,
+ 1, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 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, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 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, 197, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 213, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 120, 34, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 78, 111, 110, 83,
+ 101, 112, 97, 114, 97, 98,
+ 108, 101, 66, 108, 101, 110,
+ 100, 105, 110, 103, 0, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
+ 0, 0, 0, 48, 0, 0,
+ 0, 36, 0, 1, 0, 48,
+ 0, 0, 0, 0, 0, 3,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
+ 0, 0, 0, 70, 16, 16,
+ 0, 0, 0, 0, 0, 230,
+ 138, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
+ 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, 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, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
+ 0, 0, 0, 0, 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, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 1, 52, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 216,
+ 37, 0, 0, 68, 88, 66,
+ 67, 205, 124, 125, 227, 208,
+ 119, 203, 250, 120, 38, 135,
+ 194, 158, 189, 85, 176, 1,
+ 0, 0, 0, 216, 37, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 72, 13, 0,
+ 0, 76, 35, 0, 0, 200,
+ 35, 0, 0, 52, 37, 0,
+ 0, 164, 37, 0, 0, 65,
+ 111, 110, 57, 8, 13, 0,
+ 0, 8, 13, 0, 0, 0,
+ 2, 255, 255, 208, 12, 0,
+ 0, 56, 0, 0, 0, 1,
+ 0, 44, 0, 0, 0, 56,
+ 0, 0, 0, 56, 0, 2,
+ 0, 36, 0, 0, 0, 56,
+ 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 1, 2, 255,
+ 255, 81, 0, 0, 5, 1,
+ 0, 15, 160, 0, 0, 64,
+ 193, 0, 0, 80, 193, 0,
+ 0, 96, 193, 0, 0, 0,
+ 0, 81, 0, 0, 5, 2,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 3,
+ 0, 15, 160, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 31, 0, 0, 2, 0,
+ 0, 0, 128, 0, 0, 15,
+ 176, 31, 0, 0, 2, 0,
+ 0, 0, 144, 0, 8, 15,
+ 160, 31, 0, 0, 2, 0,
+ 0, 0, 144, 1, 8, 15,
+ 160, 1, 0, 0, 2, 0,
+ 0, 2, 128, 2, 0, 85,
+ 160, 1, 0, 0, 2, 1,
+ 0, 2, 128, 2, 0, 85,
+ 160, 1, 0, 0, 2, 2,
+ 0, 4, 128, 2, 0, 85,
+ 160, 66, 0, 0, 3, 3,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 4, 0, 15,
+ 128, 0, 0, 228, 176, 0,
+ 8, 228, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 4,
+ 0, 255, 128, 5, 0, 0,
+ 3, 5, 0, 7, 128, 0,
+ 0, 255, 128, 4, 0, 228,
+ 128, 4, 0, 0, 4, 6,
+ 0, 3, 128, 4, 0, 225,
+ 128, 0, 0, 255, 128, 5,
+ 0, 230, 129, 88, 0, 0,
+ 4, 7, 0, 3, 128, 6,
+ 0, 0, 128, 5, 0, 233,
+ 128, 5, 0, 230, 128, 11,
+ 0, 0, 3, 1, 0, 8,
+ 128, 5, 0, 0, 128, 7,
+ 0, 0, 128, 10, 0, 0,
+ 3, 2, 0, 8, 128, 7,
+ 0, 85, 128, 5, 0, 0,
+ 128, 2, 0, 0, 3, 7,
+ 0, 8, 128, 1, 0, 255,
+ 128, 2, 0, 255, 129, 6,
+ 0, 0, 2, 1, 0, 8,
+ 128, 3, 0, 255, 128, 5,
+ 0, 0, 3, 8, 0, 7,
+ 128, 1, 0, 255, 128, 3,
+ 0, 228, 128, 4, 0, 0,
+ 4, 9, 0, 3, 128, 3,
+ 0, 0, 128, 1, 0, 255,
+ 128, 8, 0, 230, 129, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 85, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 4, 0, 0,
+ 4, 10, 0, 15, 128, 3,
+ 0, 150, 128, 1, 0, 255,
+ 128, 8, 0, 96, 129, 5,
+ 0, 0, 3, 7, 0, 2,
+ 128, 2, 0, 255, 128, 10,
+ 0, 255, 128, 1, 0, 0,
+ 2, 9, 0, 12, 128, 10,
+ 0, 228, 128, 88, 0, 0,
+ 4, 1, 0, 5, 128, 9,
+ 0, 85, 129, 9, 0, 245,
+ 128, 7, 0, 215, 128, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 0, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 255, 128, 9, 0, 170,
+ 128, 88, 0, 0, 4, 2,
+ 0, 3, 128, 9, 0, 0,
+ 129, 9, 0, 232, 128, 7,
+ 0, 227, 128, 88, 0, 0,
+ 4, 1, 0, 7, 128, 9,
+ 0, 255, 128, 1, 0, 228,
+ 128, 2, 0, 228, 128, 6,
+ 0, 0, 2, 5, 0, 8,
+ 128, 9, 0, 255, 128, 5,
+ 0, 0, 3, 5, 0, 8,
+ 128, 5, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 4, 128, 5,
+ 0, 255, 128, 9, 0, 85,
+ 128, 88, 0, 0, 4, 0,
+ 0, 5, 128, 10, 0, 255,
+ 129, 9, 0, 245, 128, 7,
+ 0, 246, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 10,
+ 0, 0, 128, 0, 0, 228,
+ 128, 1, 0, 228, 128, 1,
+ 0, 0, 2, 1, 0, 1,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 2, 0, 1,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 11, 0, 4,
+ 128, 2, 0, 85, 160, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 170, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 255, 128, 9, 0, 0,
+ 128, 88, 0, 0, 4, 11,
+ 0, 3, 128, 10, 0, 170,
+ 129, 9, 0, 232, 128, 7,
+ 0, 236, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 10,
+ 0, 85, 128, 5, 0, 0,
+ 3, 2, 0, 8, 128, 2,
+ 0, 255, 128, 7, 0, 255,
+ 128, 5, 0, 0, 3, 7,
+ 0, 2, 128, 2, 0, 255,
+ 128, 10, 0, 0, 128, 88,
+ 0, 0, 4, 2, 0, 6,
+ 128, 10, 0, 85, 129, 10,
+ 0, 196, 128, 7, 0, 220,
+ 128, 88, 0, 0, 4, 2,
+ 0, 7, 128, 10, 0, 0,
+ 128, 2, 0, 228, 128, 11,
+ 0, 228, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 10,
+ 0, 0, 128, 5, 0, 0,
+ 3, 2, 0, 8, 128, 2,
+ 0, 255, 128, 7, 0, 255,
+ 128, 5, 0, 0, 3, 7,
+ 0, 4, 128, 2, 0, 255,
+ 128, 10, 0, 85, 128, 88,
+ 0, 0, 4, 1, 0, 6,
+ 128, 10, 0, 0, 129, 10,
+ 0, 196, 128, 7, 0, 248,
+ 128, 88, 0, 0, 4, 1,
+ 0, 7, 128, 9, 0, 255,
+ 128, 1, 0, 228, 128, 2,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 10,
+ 0, 85, 128, 1, 0, 228,
+ 128, 0, 0, 228, 128, 88,
+ 0, 0, 4, 1, 0, 3,
+ 128, 9, 0, 170, 128, 8,
+ 0, 233, 128, 8, 0, 230,
+ 128, 8, 0, 0, 3, 5,
+ 0, 8, 128, 0, 0, 228,
+ 128, 3, 0, 228, 160, 8,
+ 0, 0, 3, 1, 0, 4,
+ 128, 8, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 5, 0, 8, 128, 5,
+ 0, 255, 129, 1, 0, 170,
+ 128, 2, 0, 0, 3, 0,
+ 0, 7, 128, 0, 0, 228,
+ 128, 5, 0, 255, 128, 2,
+ 0, 0, 3, 5, 0, 8,
+ 128, 0, 0, 85, 129, 0,
+ 0, 0, 128, 88, 0, 0,
+ 4, 2, 0, 3, 128, 5,
+ 0, 255, 128, 0, 0, 225,
+ 128, 0, 0, 228, 128, 10,
+ 0, 0, 3, 5, 0, 8,
+ 128, 0, 0, 170, 128, 2,
+ 0, 0, 128, 11, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 85, 128, 0, 0, 170,
+ 128, 8, 0, 0, 3, 2,
+ 0, 1, 128, 0, 0, 228,
+ 128, 3, 0, 228, 160, 2,
+ 0, 0, 3, 2, 0, 2,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 128, 6, 0, 0,
+ 2, 2, 0, 2, 128, 2,
+ 0, 85, 128, 2, 0, 0,
+ 3, 7, 0, 14, 128, 0,
+ 0, 144, 128, 2, 0, 0,
+ 129, 5, 0, 0, 3, 7,
+ 0, 14, 128, 2, 0, 0,
+ 128, 7, 0, 228, 128, 4,
+ 0, 0, 4, 2, 0, 14,
+ 128, 7, 0, 228, 128, 2,
+ 0, 85, 128, 2, 0, 0,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 5, 0, 255,
+ 128, 0, 0, 228, 128, 2,
+ 0, 249, 128, 2, 0, 0,
+ 3, 2, 0, 14, 128, 2,
+ 0, 0, 129, 0, 0, 144,
+ 128, 2, 0, 0, 3, 5,
+ 0, 8, 128, 2, 0, 0,
+ 129, 2, 0, 0, 160, 5,
+ 0, 0, 3, 2, 0, 14,
+ 128, 2, 0, 228, 128, 5,
+ 0, 255, 128, 2, 0, 0,
+ 3, 5, 0, 8, 128, 2,
+ 0, 0, 129, 7, 0, 0,
+ 128, 2, 0, 0, 3, 7,
+ 0, 1, 128, 7, 0, 0,
+ 129, 2, 0, 0, 160, 6,
+ 0, 0, 2, 5, 0, 8,
+ 128, 5, 0, 255, 128, 4,
+ 0, 0, 4, 2, 0, 7,
+ 128, 2, 0, 249, 128, 5,
+ 0, 255, 128, 2, 0, 0,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 7, 0, 0,
+ 128, 0, 0, 228, 128, 2,
+ 0, 228, 128, 8, 0, 0,
+ 3, 5, 0, 8, 128, 5,
+ 0, 228, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 2,
+ 0, 1, 128, 1, 0, 170,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 3, 5, 0, 8,
+ 128, 1, 0, 170, 129, 5,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 14, 128, 3,
+ 0, 144, 128, 1, 0, 255,
+ 128, 5, 0, 255, 128, 4,
+ 0, 0, 4, 3, 0, 7,
+ 128, 4, 0, 228, 128, 0,
+ 0, 255, 128, 2, 0, 0,
+ 128, 4, 0, 0, 4, 7,
+ 0, 15, 128, 4, 0, 38,
+ 128, 0, 0, 255, 128, 5,
+ 0, 144, 129, 2, 0, 0,
+ 3, 0, 0, 8, 128, 3,
+ 0, 85, 129, 3, 0, 0,
+ 128, 88, 0, 0, 4, 8,
+ 0, 6, 128, 0, 0, 255,
+ 128, 3, 0, 196, 128, 3,
+ 0, 208, 128, 10, 0, 0,
+ 3, 0, 0, 8, 128, 3,
+ 0, 170, 128, 8, 0, 85,
+ 128, 11, 0, 0, 3, 1,
+ 0, 8, 128, 8, 0, 170,
+ 128, 3, 0, 170, 128, 8,
+ 0, 0, 3, 5, 0, 8,
+ 128, 3, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 2, 0, 1, 128, 0,
+ 0, 255, 129, 5, 0, 255,
+ 128, 6, 0, 0, 2, 2,
+ 0, 1, 128, 2, 0, 0,
+ 128, 2, 0, 0, 3, 8,
+ 0, 14, 128, 3, 0, 144,
+ 128, 5, 0, 255, 129, 5,
+ 0, 0, 3, 8, 0, 14,
+ 128, 5, 0, 255, 128, 8,
+ 0, 228, 128, 4, 0, 0,
+ 4, 8, 0, 14, 128, 8,
+ 0, 228, 128, 2, 0, 0,
+ 128, 5, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 7,
+ 128, 0, 0, 255, 128, 3,
+ 0, 228, 128, 8, 0, 249,
+ 128, 2, 0, 0, 3, 8,
+ 0, 14, 128, 5, 0, 255,
+ 129, 3, 0, 144, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 160, 5, 0, 0,
+ 3, 8, 0, 14, 128, 0,
+ 0, 255, 128, 8, 0, 228,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 1, 0, 255,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 3, 1, 0, 8,
+ 128, 1, 0, 255, 129, 2,
+ 0, 0, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 0,
+ 0, 255, 128, 4, 0, 0,
+ 4, 8, 0, 14, 128, 8,
+ 0, 228, 128, 0, 0, 255,
+ 128, 5, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 7,
+ 128, 1, 0, 255, 128, 3,
+ 0, 228, 128, 8, 0, 249,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 2, 0, 170,
+ 129, 2, 0, 85, 128, 88,
+ 0, 0, 4, 8, 0, 6,
+ 128, 0, 0, 255, 128, 2,
+ 0, 216, 128, 2, 0, 228,
+ 128, 10, 0, 0, 3, 0,
+ 0, 8, 128, 2, 0, 255,
+ 128, 8, 0, 85, 128, 11,
+ 0, 0, 3, 1, 0, 8,
+ 128, 8, 0, 170, 128, 2,
+ 0, 255, 128, 8, 0, 0,
+ 3, 5, 0, 8, 128, 2,
+ 0, 249, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 2,
+ 0, 1, 128, 0, 0, 255,
+ 129, 5, 0, 255, 128, 6,
+ 0, 0, 2, 2, 0, 1,
+ 128, 2, 0, 0, 128, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 2, 0, 228, 128, 5,
+ 0, 255, 129, 5, 0, 0,
+ 3, 8, 0, 14, 128, 5,
+ 0, 255, 128, 8, 0, 228,
+ 128, 4, 0, 0, 4, 8,
+ 0, 14, 128, 8, 0, 228,
+ 128, 2, 0, 0, 128, 5,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 7, 128, 0,
+ 0, 255, 128, 2, 0, 249,
+ 128, 8, 0, 249, 128, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 5, 0, 255, 129, 2,
+ 0, 144, 128, 2, 0, 0,
+ 3, 0, 0, 8, 128, 5,
+ 0, 255, 129, 2, 0, 0,
+ 160, 5, 0, 0, 3, 8,
+ 0, 14, 128, 0, 0, 255,
+ 128, 8, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 1, 0, 255, 128, 5,
+ 0, 255, 129, 2, 0, 0,
+ 3, 1, 0, 8, 128, 1,
+ 0, 255, 129, 2, 0, 0,
+ 160, 6, 0, 0, 2, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 4, 0, 0, 4, 8,
+ 0, 14, 128, 8, 0, 228,
+ 128, 0, 0, 255, 128, 5,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 7, 128, 1,
+ 0, 255, 128, 2, 0, 228,
+ 128, 8, 0, 249, 128, 1,
+ 0, 0, 2, 0, 0, 8,
+ 128, 0, 0, 0, 160, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 0, 0, 255, 128, 1,
+ 0, 144, 160, 5, 0, 0,
+ 3, 8, 0, 14, 128, 8,
+ 0, 228, 128, 8, 0, 228,
+ 128, 88, 0, 0, 4, 2,
+ 0, 7, 128, 8, 0, 255,
+ 129, 3, 0, 228, 128, 2,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 8,
+ 0, 170, 129, 0, 0, 228,
+ 128, 2, 0, 228, 128, 1,
+ 0, 0, 2, 2, 0, 2,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 3, 0, 2,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 9, 0, 4,
+ 128, 2, 0, 85, 160, 11,
+ 0, 0, 3, 0, 0, 8,
+ 128, 8, 0, 0, 128, 1,
+ 0, 0, 128, 10, 0, 0,
+ 3, 2, 0, 8, 128, 1,
+ 0, 85, 128, 8, 0, 0,
+ 128, 2, 0, 0, 3, 10,
+ 0, 8, 128, 0, 0, 255,
+ 128, 2, 0, 255, 129, 6,
+ 0, 0, 2, 0, 0, 8,
+ 128, 7, 0, 255, 128, 5,
+ 0, 0, 3, 0, 0, 8,
+ 128, 0, 0, 255, 128, 10,
+ 0, 255, 128, 5, 0, 0,
+ 3, 10, 0, 1, 128, 0,
+ 0, 255, 128, 6, 0, 0,
+ 128, 1, 0, 0, 2, 6,
+ 0, 12, 128, 7, 0, 180,
+ 128, 88, 0, 0, 4, 9,
+ 0, 3, 128, 7, 0, 255,
+ 129, 6, 0, 226, 128, 10,
+ 0, 227, 128, 6, 0, 0,
+ 2, 0, 0, 8, 128, 6,
+ 0, 85, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 2, 128, 0, 0, 255,
+ 128, 7, 0, 170, 128, 88,
+ 0, 0, 4, 3, 0, 5,
+ 128, 6, 0, 85, 129, 6,
+ 0, 245, 128, 10, 0, 215,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 170,
+ 128, 3, 0, 164, 128, 9,
+ 0, 164, 128, 6, 0, 0,
+ 2, 0, 0, 8, 128, 7,
+ 0, 170, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 4, 128, 0, 0, 255,
+ 128, 6, 0, 85, 128, 88,
+ 0, 0, 4, 2, 0, 5,
+ 128, 7, 0, 170, 129, 6,
+ 0, 245, 128, 10, 0, 246,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 0,
+ 128, 2, 0, 164, 128, 1,
+ 0, 228, 128, 1, 0, 0,
+ 2, 2, 0, 1, 128, 2,
+ 0, 85, 160, 1, 0, 0,
+ 2, 3, 0, 4, 128, 2,
+ 0, 85, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 6,
+ 0, 0, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 1, 128, 0, 0, 255,
+ 128, 7, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 3,
+ 128, 6, 0, 0, 129, 6,
+ 0, 226, 128, 10, 0, 236,
+ 128, 6, 0, 0, 2, 0,
+ 0, 8, 128, 7, 0, 85,
+ 128, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 10, 0, 255, 128, 5,
+ 0, 0, 3, 10, 0, 2,
+ 128, 0, 0, 255, 128, 7,
+ 0, 0, 128, 88, 0, 0,
+ 4, 2, 0, 6, 128, 7,
+ 0, 85, 129, 7, 0, 196,
+ 128, 10, 0, 220, 128, 88,
+ 0, 0, 4, 2, 0, 7,
+ 128, 7, 0, 0, 128, 2,
+ 0, 228, 128, 3, 0, 228,
+ 128, 1, 0, 0, 2, 3,
+ 0, 1, 128, 2, 0, 85,
+ 160, 6, 0, 0, 2, 0,
+ 0, 8, 128, 7, 0, 0,
+ 128, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 10, 0, 255, 128, 5,
+ 0, 0, 3, 10, 0, 4,
+ 128, 0, 0, 255, 128, 7,
+ 0, 85, 128, 88, 0, 0,
+ 4, 3, 0, 6, 128, 7,
+ 0, 0, 129, 7, 0, 196,
+ 128, 10, 0, 248, 128, 88,
+ 0, 0, 4, 2, 0, 7,
+ 128, 7, 0, 170, 128, 3,
+ 0, 228, 128, 2, 0, 228,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 85,
+ 128, 2, 0, 164, 128, 1,
+ 0, 228, 128, 8, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 244, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 129, 1, 0, 170, 128, 2,
+ 0, 0, 3, 1, 0, 7,
+ 128, 0, 0, 255, 128, 1,
+ 0, 244, 128, 2, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 85, 129, 1, 0, 0,
+ 128, 88, 0, 0, 4, 2,
+ 0, 3, 128, 0, 0, 255,
+ 128, 1, 0, 225, 128, 1,
+ 0, 228, 128, 10, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 170, 128, 2, 0, 0,
+ 128, 11, 0, 0, 3, 5,
+ 0, 8, 128, 2, 0, 85,
+ 128, 1, 0, 170, 128, 8,
+ 0, 0, 3, 1, 0, 8,
+ 128, 1, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 2, 0, 7, 128, 1,
+ 0, 255, 129, 1, 0, 228,
+ 128, 5, 0, 0, 3, 2,
+ 0, 7, 128, 1, 0, 255,
+ 128, 2, 0, 228, 128, 2,
+ 0, 0, 3, 2, 0, 8,
+ 128, 0, 0, 255, 129, 1,
+ 0, 255, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 2,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 7, 128, 2,
+ 0, 228, 128, 2, 0, 255,
+ 128, 1, 0, 255, 128, 88,
+ 0, 0, 4, 1, 0, 7,
+ 128, 0, 0, 255, 128, 1,
+ 0, 228, 128, 2, 0, 228,
+ 128, 2, 0, 0, 3, 2,
+ 0, 7, 128, 1, 0, 255,
+ 129, 1, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 1, 0, 255, 129, 2,
+ 0, 0, 160, 5, 0, 0,
+ 3, 2, 0, 7, 128, 0,
+ 0, 255, 128, 2, 0, 228,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 1, 0, 255,
+ 129, 5, 0, 255, 128, 2,
+ 0, 0, 3, 2, 0, 8,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 0,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 7, 128, 2,
+ 0, 228, 128, 0, 0, 255,
+ 128, 1, 0, 255, 128, 88,
+ 0, 0, 4, 1, 0, 7,
+ 128, 2, 0, 255, 128, 1,
+ 0, 228, 128, 2, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 8, 0, 85,
+ 129, 1, 0, 228, 128, 0,
+ 0, 228, 128, 18, 0, 0,
+ 4, 1, 0, 7, 128, 3,
+ 0, 255, 128, 0, 0, 228,
+ 128, 5, 0, 228, 128, 5,
+ 0, 0, 3, 1, 0, 8,
+ 128, 3, 0, 255, 128, 3,
+ 0, 255, 128, 88, 0, 0,
+ 4, 1, 0, 8, 128, 1,
+ 0, 255, 129, 2, 0, 0,
+ 160, 2, 0, 85, 160, 5,
+ 0, 0, 3, 0, 0, 7,
+ 128, 4, 0, 255, 128, 1,
+ 0, 228, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 4,
+ 0, 255, 128, 4, 0, 255,
+ 128, 88, 0, 0, 4, 0,
+ 0, 8, 128, 0, 0, 255,
+ 129, 2, 0, 0, 160, 2,
+ 0, 85, 160, 2, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 255, 128, 0, 0, 255,
+ 128, 88, 0, 0, 4, 4,
+ 0, 7, 128, 0, 0, 255,
+ 129, 0, 0, 228, 128, 4,
+ 0, 228, 128, 1, 0, 0,
+ 2, 0, 8, 15, 128, 4,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 252,
+ 21, 0, 0, 64, 0, 0,
+ 0, 127, 5, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 0,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 1,
+ 0, 0, 0, 88, 24, 0,
+ 4, 0, 112, 16, 0, 0,
+ 0, 0, 0, 85, 85, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 1, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 104,
+ 0, 0, 2, 9, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 1,
+ 0, 0, 0, 70, 126, 16,
+ 0, 0, 0, 0, 0, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 16, 16, 0, 1,
+ 0, 0, 0, 70, 126, 16,
+ 0, 1, 0, 0, 0, 0,
+ 96, 16, 0, 1, 0, 0,
+ 0, 24, 0, 0, 7, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 58, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 7, 34, 0, 16,
+ 0, 2, 0, 0, 0, 58,
+ 0, 16, 0, 1, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 60, 0, 0,
+ 7, 18, 0, 16, 0, 2,
+ 0, 0, 0, 26, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 31, 0, 4, 3, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 54, 0, 0, 5, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 14, 16, 0, 0,
+ 0, 0, 0, 62, 0, 0,
+ 1, 21, 0, 0, 1, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 2, 16, 0, 0, 0, 0,
+ 0, 246, 15, 16, 0, 0,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 246,
+ 15, 16, 0, 1, 0, 0,
+ 0, 32, 0, 0, 8, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 128, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 12,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 2,
+ 0, 0, 0, 52, 0, 0,
+ 7, 18, 0, 16, 0, 2,
+ 0, 0, 0, 42, 0, 16,
+ 0, 1, 0, 0, 0, 26,
+ 0, 16, 0, 1, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 2, 0, 0, 0, 51,
+ 0, 0, 7, 34, 0, 16,
+ 0, 2, 0, 0, 0, 42,
+ 0, 16, 0, 1, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 51, 0, 0,
+ 7, 34, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 26,
+ 0, 16, 0, 2, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 26, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 2,
+ 0, 0, 0, 29, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 31, 0, 4, 3, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 242,
+ 0, 16, 0, 3, 0, 0,
+ 0, 6, 10, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 150, 4, 16, 0, 0,
+ 0, 0, 0, 49, 0, 0,
+ 10, 114, 0, 16, 0, 4,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 22,
+ 7, 16, 0, 3, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 22, 7, 16,
+ 0, 3, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 55, 0, 0,
+ 9, 98, 0, 16, 0, 5,
+ 0, 0, 0, 6, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 3, 16, 0, 2, 0, 0,
+ 0, 6, 1, 16, 0, 3,
+ 0, 0, 0, 29, 0, 0,
+ 7, 146, 0, 16, 0, 4,
+ 0, 0, 0, 166, 10, 16,
+ 0, 0, 0, 0, 0, 86,
+ 1, 16, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 98,
+ 0, 16, 0, 6, 0, 0,
+ 0, 86, 5, 16, 0, 4,
+ 0, 0, 0, 246, 13, 16,
+ 0, 2, 0, 0, 0, 6,
+ 1, 16, 0, 3, 0, 0,
+ 0, 55, 0, 0, 9, 50,
+ 0, 16, 0, 3, 0, 0,
+ 0, 166, 10, 16, 0, 4,
+ 0, 0, 0, 230, 10, 16,
+ 0, 2, 0, 0, 0, 230,
+ 10, 16, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 18,
+ 0, 16, 0, 6, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 66, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 246,
+ 15, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 54,
+ 0, 0, 5, 18, 0, 16,
+ 0, 5, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 6, 0, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 18, 0, 0, 1, 0,
+ 0, 0, 8, 242, 0, 16,
+ 0, 4, 0, 0, 0, 86,
+ 10, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 134,
+ 1, 16, 0, 0, 0, 0,
+ 0, 49, 0, 0, 10, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 7, 16,
+ 0, 4, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 6, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 22, 7, 16, 0, 4,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 6, 0, 0,
+ 0, 55, 0, 0, 9, 82,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 0, 16, 0, 5,
+ 0, 0, 0, 6, 3, 16,
+ 0, 2, 0, 0, 0, 6,
+ 1, 16, 0, 4, 0, 0,
+ 0, 29, 0, 0, 7, 146,
+ 0, 16, 0, 5, 0, 0,
+ 0, 166, 10, 16, 0, 0,
+ 0, 0, 0, 6, 4, 16,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 82, 0, 16,
+ 0, 7, 0, 0, 0, 86,
+ 5, 16, 0, 5, 0, 0,
+ 0, 246, 13, 16, 0, 2,
+ 0, 0, 0, 6, 1, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 50, 0, 16,
+ 0, 2, 0, 0, 0, 166,
+ 10, 16, 0, 5, 0, 0,
+ 0, 182, 15, 16, 0, 2,
+ 0, 0, 0, 182, 15, 16,
+ 0, 4, 0, 0, 0, 54,
+ 0, 0, 5, 34, 0, 16,
+ 0, 7, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 54, 0, 0,
+ 5, 34, 0, 16, 0, 6,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 21,
+ 0, 0, 1, 16, 0, 0,
+ 10, 18, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 16, 0, 0, 10, 34,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 2, 0, 0, 0, 26,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 6, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 16,
+ 0, 0, 10, 130, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 51, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 42, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 52,
+ 0, 0, 7, 34, 0, 16,
+ 0, 3, 0, 0, 0, 26,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 2,
+ 0, 0, 0, 52, 0, 0,
+ 7, 34, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 2, 0, 0, 0, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 49, 0, 0, 7, 66,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 56, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 3, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 166, 10, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 49, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 8, 66, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 166,
+ 10, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 34, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 226, 0, 16,
+ 0, 3, 0, 0, 0, 6,
+ 9, 16, 0, 4, 0, 0,
+ 0, 86, 5, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 226, 0, 16, 0, 3,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 86,
+ 14, 16, 0, 3, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 6, 0, 16, 0, 3,
+ 0, 0, 0, 150, 7, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 18, 0, 0, 1, 32,
+ 0, 0, 8, 130, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 128, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 13, 0, 0,
+ 0, 31, 0, 4, 3, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 52, 0, 0, 7, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 0, 0, 0, 0, 0, 52,
+ 0, 0, 7, 130, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 51, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 26,
+ 0, 16, 0, 0, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 130, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 3, 0, 0,
+ 0, 29, 0, 0, 7, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 31,
+ 0, 4, 3, 58, 0, 16,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 8, 242, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 10, 16, 128, 65, 0, 0,
+ 0, 1, 0, 0, 0, 150,
+ 4, 16, 0, 1, 0, 0,
+ 0, 49, 0, 0, 10, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 7, 16,
+ 0, 4, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 6, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 22, 7, 16, 0, 4,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 6, 0, 0,
+ 0, 55, 0, 0, 9, 98,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 0, 16, 0, 5,
+ 0, 0, 0, 6, 3, 16,
+ 0, 3, 0, 0, 0, 6,
+ 1, 16, 0, 4, 0, 0,
+ 0, 29, 0, 0, 7, 146,
+ 0, 16, 0, 5, 0, 0,
+ 0, 166, 10, 16, 0, 1,
+ 0, 0, 0, 86, 1, 16,
+ 0, 1, 0, 0, 0, 55,
+ 0, 0, 9, 98, 0, 16,
+ 0, 7, 0, 0, 0, 86,
+ 5, 16, 0, 5, 0, 0,
+ 0, 246, 13, 16, 0, 3,
+ 0, 0, 0, 6, 1, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 50, 0, 16,
+ 0, 4, 0, 0, 0, 166,
+ 10, 16, 0, 5, 0, 0,
+ 0, 230, 10, 16, 0, 3,
+ 0, 0, 0, 230, 10, 16,
+ 0, 4, 0, 0, 0, 54,
+ 0, 0, 5, 18, 0, 16,
+ 0, 7, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 4, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 54, 0, 0,
+ 5, 18, 0, 16, 0, 6,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 18,
+ 0, 0, 1, 0, 0, 0,
+ 8, 242, 0, 16, 0, 5,
+ 0, 0, 0, 86, 10, 16,
+ 128, 65, 0, 0, 0, 1,
+ 0, 0, 0, 134, 1, 16,
+ 0, 1, 0, 0, 0, 49,
+ 0, 0, 10, 114, 0, 16,
+ 0, 6, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 22, 7, 16, 0, 5,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 7,
+ 0, 0, 0, 246, 15, 16,
+ 0, 3, 0, 0, 0, 22,
+ 7, 16, 0, 5, 0, 0,
+ 0, 56, 0, 0, 7, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 7, 0, 0, 0, 55,
+ 0, 0, 9, 82, 0, 16,
+ 0, 7, 0, 0, 0, 6,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 3, 16, 0, 3,
+ 0, 0, 0, 6, 1, 16,
+ 0, 5, 0, 0, 0, 29,
+ 0, 0, 7, 146, 0, 16,
+ 0, 6, 0, 0, 0, 166,
+ 10, 16, 0, 1, 0, 0,
+ 0, 6, 4, 16, 0, 1,
+ 0, 0, 0, 55, 0, 0,
+ 9, 82, 0, 16, 0, 8,
+ 0, 0, 0, 86, 5, 16,
+ 0, 6, 0, 0, 0, 246,
+ 13, 16, 0, 3, 0, 0,
+ 0, 6, 1, 16, 0, 5,
+ 0, 0, 0, 55, 0, 0,
+ 9, 50, 0, 16, 0, 3,
+ 0, 0, 0, 166, 10, 16,
+ 0, 6, 0, 0, 0, 182,
+ 15, 16, 0, 3, 0, 0,
+ 0, 182, 15, 16, 0, 5,
+ 0, 0, 0, 54, 0, 0,
+ 5, 34, 0, 16, 0, 8,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 246, 15, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 8, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 34,
+ 0, 16, 0, 7, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 6, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 21, 0, 0,
+ 1, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 16,
+ 0, 0, 10, 18, 0, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 3, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 16, 0, 0,
+ 10, 130, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 51, 0, 0, 7, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 51,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 42,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 0, 3,
+ 0, 0, 0, 52, 0, 0,
+ 7, 18, 0, 16, 0, 4,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 42, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 4, 0, 0, 0, 49,
+ 0, 0, 7, 34, 0, 16,
+ 0, 4, 0, 0, 0, 58,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 5, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 86, 5, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 49, 0, 0, 7, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 10, 0, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 8, 226, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 6,
+ 9, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 18,
+ 0, 16, 0, 5, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 56, 0, 0,
+ 7, 226, 0, 16, 0, 4,
+ 0, 0, 0, 86, 14, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 0, 0, 0, 8, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 4,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 150, 7, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 4, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 2, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 18,
+ 0, 0, 1, 32, 0, 0,
+ 8, 130, 0, 16, 0, 2,
+ 0, 0, 0, 10, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 14, 0, 0, 0, 31,
+ 0, 4, 3, 58, 0, 16,
+ 0, 2, 0, 0, 0, 16,
+ 0, 0, 10, 130, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 16, 0, 0,
+ 10, 18, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 128, 65, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 51,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 51, 0, 0,
+ 7, 130, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 3, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 52,
+ 0, 0, 7, 18, 0, 16,
+ 0, 4, 0, 0, 0, 42,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 4,
+ 0, 0, 0, 49, 0, 0,
+ 7, 34, 0, 16, 0, 4,
+ 0, 0, 0, 58, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 3,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 86,
+ 5, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 49,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 10, 0, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 226, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 6, 9, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 5, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 56, 0, 0, 7, 226,
+ 0, 16, 0, 4, 0, 0,
+ 0, 86, 14, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 4, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 4, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 150, 7, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 18, 0, 0,
+ 1, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 16,
+ 0, 0, 10, 18, 0, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 1, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 16, 0, 0,
+ 10, 130, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 51,
+ 0, 0, 7, 18, 0, 16,
+ 0, 3, 0, 0, 0, 42,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 52, 0, 0,
+ 7, 34, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 0, 1, 0, 0,
+ 0, 52, 0, 0, 7, 34,
+ 0, 16, 0, 3, 0, 0,
+ 0, 42, 0, 16, 0, 1,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 49,
+ 0, 0, 7, 66, 0, 16,
+ 0, 3, 0, 0, 0, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 114, 0, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 18, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 6, 0, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 1, 0, 0,
+ 0, 166, 10, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 49, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 26, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 0, 0, 0, 8, 66,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 56, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 166, 10, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 0, 0, 0, 8, 34,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 14, 0, 0,
+ 7, 226, 0, 16, 0, 3,
+ 0, 0, 0, 6, 9, 16,
+ 0, 4, 0, 0, 0, 86,
+ 5, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 7, 226,
+ 0, 16, 0, 3, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 86, 14, 16,
+ 0, 3, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 2, 0, 0, 0, 6,
+ 0, 16, 0, 3, 0, 0,
+ 0, 150, 7, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 21,
+ 0, 0, 1, 21, 0, 0,
+ 1, 21, 0, 0, 1, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 1, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 56, 0, 0, 7, 226,
+ 0, 16, 0, 1, 0, 0,
+ 0, 246, 15, 16, 0, 1,
+ 0, 0, 0, 6, 9, 16,
+ 0, 2, 0, 0, 0, 50,
+ 0, 0, 9, 114, 0, 16,
+ 0, 0, 0, 0, 0, 6,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 150, 7, 16,
+ 0, 1, 0, 0, 0, 56,
+ 0, 0, 7, 114, 32, 16,
+ 0, 0, 0, 0, 0, 246,
+ 15, 16, 0, 0, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 130, 32, 16, 0, 0,
+ 0, 0, 0, 58, 0, 16,
+ 0, 0, 0, 0, 0, 62,
+ 0, 0, 1, 83, 84, 65,
+ 84, 116, 0, 0, 0, 195,
+ 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 7, 0, 0,
+ 0, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 14, 0, 0,
+ 0, 28, 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, 82, 68, 69, 70, 100,
+ 1, 0, 0, 1, 0, 0,
+ 0, 232, 0, 0, 0, 5,
+ 0, 0, 0, 28, 0, 0,
+ 0, 0, 4, 255, 255, 0,
+ 1, 0, 0, 48, 1, 0,
+ 0, 188, 0, 0, 0, 3,
+ 0, 0, 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, 197, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 209,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 213, 0, 0, 0, 2,
+ 0, 0, 0, 5, 0, 0,
+ 0, 4, 0, 0, 0, 255,
+ 255, 255, 255, 1, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 0, 0, 0, 220, 0, 0,
+ 0, 0, 0, 0, 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, 115,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 115, 66, 99, 107,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 98, 99, 107, 116, 101, 120,
+ 0, 36, 71, 108, 111, 98,
+ 97, 108, 115, 0, 171, 171,
+ 171, 220, 0, 0, 0, 1,
+ 0, 0, 0, 0, 1, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 24, 1, 0, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 32,
+ 1, 0, 0, 0, 0, 0,
+ 0, 98, 108, 101, 110, 100,
+ 111, 112, 0, 0, 0, 19,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 73,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 3, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 0, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 79, 83, 71,
+ 78, 44, 0, 0, 0, 1,
+ 0, 0, 0, 8, 0, 0,
+ 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 15, 0, 0,
+ 0, 83, 86, 95, 84, 97,
+ 114, 103, 101, 116, 0, 171,
+ 171, 93, 56, 0, 0, 0,
+ 0, 0, 0, 83, 97, 109,
+ 112, 108, 101, 82, 97, 100,
+ 105, 97, 108, 71, 114, 97,
+ 100, 105, 101, 110, 116, 0,
+ 65, 80, 111, 115, 0, 44,
+ 7, 0, 0, 68, 88, 66,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
+ 0, 0, 0, 44, 7, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 148, 1, 0,
+ 0, 104, 3, 0, 0, 228,
+ 3, 0, 0, 136, 6, 0,
+ 0, 188, 6, 0, 0, 65,
+ 111, 110, 57, 84, 1, 0,
+ 0, 84, 1, 0, 0, 0,
+ 2, 254, 255, 252, 0, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 36, 0, 0, 0, 84,
+ 0, 0, 0, 84, 0, 0,
+ 0, 36, 0, 1, 0, 84,
+ 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 3, 0, 0, 0, 0,
+ 0, 1, 0, 3, 0, 1,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 6, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 3,
+ 128, 0, 0, 228, 144, 1,
+ 0, 238, 160, 1, 0, 228,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 0,
+ 128, 6, 0, 0, 160, 5,
+ 0, 0, 3, 0, 0, 4,
+ 128, 0, 0, 170, 128, 5,
+ 0, 0, 160, 5, 0, 0,
+ 3, 1, 0, 1, 128, 0,
+ 0, 170, 128, 6, 0, 85,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 85,
+ 129, 6, 0, 0, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 5, 0, 0,
+ 3, 0, 0, 1, 128, 0,
+ 0, 170, 128, 5, 0, 85,
+ 160, 5, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 0,
+ 128, 6, 0, 85, 160, 1,
+ 0, 0, 2, 1, 0, 4,
+ 128, 6, 0, 0, 160, 8,
+ 0, 0, 3, 0, 0, 8,
+ 224, 1, 0, 228, 128, 3,
+ 0, 228, 160, 8, 0, 0,
+ 3, 0, 0, 4, 224, 1,
+ 0, 228, 128, 4, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 6, 0, 36,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 204, 1, 0,
+ 0, 64, 0, 1, 0, 115,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 1, 0, 0,
+ 0, 4, 0, 0, 0, 95,
+ 0, 0, 3, 50, 16, 16,
+ 0, 0, 0, 0, 0, 103,
+ 0, 0, 4, 242, 32, 16,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 101, 0, 0,
+ 3, 50, 32, 16, 0, 1,
+ 0, 0, 0, 101, 0, 0,
+ 3, 194, 32, 16, 0, 1,
+ 0, 0, 0, 104, 0, 0,
+ 2, 2, 0, 0, 0, 54,
+ 0, 0, 8, 194, 32, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 50, 0, 0, 11, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 50, 32, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 8, 34, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 56, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 63, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 1, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 16, 0, 0, 8, 66,
+ 32, 16, 0, 1, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 70, 130, 32,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0,
+ 8, 130, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 12, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 8,
+ 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, 3,
+ 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, 82, 68, 69,
+ 70, 156, 2, 0, 0, 2,
+ 0, 0, 0, 100, 0, 0,
+ 0, 2, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 103,
+ 2, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 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, 96,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 99, 98, 48, 0, 99,
+ 98, 50, 0, 92, 0, 0,
+ 0, 4, 0, 0, 0, 148,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 7, 0, 0, 0, 52,
+ 1, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 244, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 16, 1, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 26, 1, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 40, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 220, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 40,
+ 2, 0, 0, 0, 0, 0,
+ 0, 56, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 64, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 84, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 92, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 100, 105, 102,
+ 102, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 101, 110,
+ 116, 101, 114, 49, 0, 65,
+ 0, 171, 171, 0, 0, 3,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 114, 97, 100, 105, 117,
+ 115, 49, 0, 115, 113, 95,
+ 114, 97, 100, 105, 117, 115,
+ 49, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 171, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 91, 94, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 224,
+ 9, 0, 0, 68, 88, 66,
+ 67, 76, 106, 34, 250, 169,
+ 50, 124, 43, 130, 255, 198,
+ 178, 126, 127, 40, 188, 1,
+ 0, 0, 0, 224, 9, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 128, 2, 0,
+ 0, 88, 6, 0, 0, 212,
+ 6, 0, 0, 60, 9, 0,
+ 0, 172, 9, 0, 0, 65,
+ 111, 110, 57, 64, 2, 0,
+ 0, 64, 2, 0, 0, 0,
+ 2, 255, 255, 8, 2, 0,
+ 0, 56, 0, 0, 0, 1,
+ 0, 44, 0, 0, 0, 56,
+ 0, 0, 0, 56, 0, 2,
+ 0, 36, 0, 0, 0, 56,
+ 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 0, 0, 4,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 255,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 4,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 128, 191, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 31, 0, 0, 2, 0,
+ 0, 0, 128, 0, 0, 15,
+ 176, 31, 0, 0, 2, 0,
+ 0, 0, 144, 0, 8, 15,
+ 160, 31, 0, 0, 2, 0,
+ 0, 0, 144, 1, 8, 15,
+ 160, 2, 0, 0, 3, 0,
+ 0, 3, 128, 0, 0, 235,
+ 176, 1, 0, 228, 161, 90,
+ 0, 0, 4, 0, 0, 8,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 161, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 1, 0, 170, 160, 1,
+ 0, 0, 2, 0, 0, 4,
+ 128, 1, 0, 255, 160, 8,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 2, 128, 0,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 129, 35,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 85, 128, 7,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 170, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 0, 0, 170, 128, 1,
+ 0, 0, 2, 1, 0, 6,
+ 128, 1, 0, 0, 129, 2,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 0, 128, 1,
+ 0, 148, 128, 6, 0, 0,
+ 2, 1, 0, 1, 128, 1,
+ 0, 170, 160, 5, 0, 0,
+ 3, 0, 0, 13, 128, 0,
+ 0, 228, 128, 1, 0, 0,
+ 128, 1, 0, 0, 2, 1,
+ 0, 8, 128, 1, 0, 255,
+ 160, 4, 0, 0, 4, 1,
+ 0, 7, 128, 0, 0, 248,
+ 128, 0, 0, 170, 160, 1,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 1, 128, 1,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 128, 88,
+ 0, 0, 4, 0, 0, 13,
+ 128, 1, 0, 148, 128, 4,
+ 0, 68, 160, 4, 0, 230,
+ 160, 1, 0, 0, 2, 2,
+ 0, 2, 128, 3, 0, 0,
+ 160, 66, 0, 0, 3, 1,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 2, 0, 15,
+ 128, 2, 0, 228, 128, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 2, 0, 7, 128, 2,
+ 0, 255, 128, 2, 0, 228,
+ 128, 5, 0, 0, 3, 1,
+ 0, 15, 128, 1, 0, 255,
+ 128, 2, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 0, 0, 255, 128, 0,
+ 0, 0, 128, 88, 0, 0,
+ 4, 0, 0, 1, 128, 0,
+ 0, 255, 128, 0, 0, 0,
+ 128, 0, 0, 170, 128, 88,
+ 0, 0, 4, 1, 0, 15,
+ 128, 0, 0, 0, 129, 4,
+ 0, 170, 160, 1, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 15, 128, 0, 0, 85,
+ 128, 1, 0, 228, 128, 4,
+ 0, 170, 160, 1, 0, 0,
+ 2, 0, 8, 15, 128, 0,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 208,
+ 3, 0, 0, 64, 0, 0,
+ 0, 244, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 0,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 1,
+ 0, 0, 0, 88, 24, 0,
+ 4, 0, 112, 16, 0, 0,
+ 0, 0, 0, 85, 85, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 1, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 98,
+ 16, 0, 3, 194, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 104,
+ 0, 0, 2, 3, 0, 0,
+ 0, 0, 0, 0, 9, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 230, 26, 16, 0, 1,
+ 0, 0, 0, 70, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 54, 0, 0, 6, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 128, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 16, 0, 0, 8, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 70, 130, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 15, 0, 0,
+ 7, 18, 0, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 8, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 42, 128, 32,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 50, 0, 0,
+ 10, 18, 0, 16, 0, 0,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 49, 0, 0, 7, 34,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 75,
+ 0, 0, 6, 18, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 129, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 6, 34, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 7, 82, 0, 16,
+ 0, 0, 0, 0, 0, 166,
+ 10, 16, 0, 0, 0, 0,
+ 0, 6, 1, 16, 0, 1,
+ 0, 0, 0, 14, 0, 0,
+ 8, 82, 0, 16, 0, 0,
+ 0, 0, 0, 6, 2, 16,
+ 0, 0, 0, 0, 0, 166,
+ 138, 32, 0, 0, 0, 0,
+ 0, 5, 0, 0, 0, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 1, 0, 0, 0, 134,
+ 0, 16, 0, 0, 0, 0,
+ 0, 166, 138, 32, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 29, 0, 0, 9, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 246, 143, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 1, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 50, 0, 0, 9, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 34,
+ 0, 16, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 63, 69, 0, 0,
+ 9, 242, 0, 16, 0, 2,
+ 0, 0, 0, 70, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 26, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 52, 0, 0, 7, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 29,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 56, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 1, 0, 0,
+ 0, 70, 126, 16, 0, 1,
+ 0, 0, 0, 0, 96, 16,
+ 0, 1, 0, 0, 0, 56,
+ 0, 0, 7, 242, 32, 16,
+ 0, 0, 0, 0, 0, 246,
+ 15, 16, 0, 0, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 33, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 19, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 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, 82,
+ 68, 69, 70, 96, 2, 0,
+ 0, 1, 0, 0, 0, 224,
+ 0, 0, 0, 5, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 255, 255, 0, 1, 0,
+ 0, 43, 2, 0, 0, 188,
+ 0, 0, 0, 3, 0, 0,
+ 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, 197, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 210, 0, 0,
+ 0, 2, 0, 0, 0, 5,
+ 0, 0, 0, 4, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 214,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 1, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 219, 0, 0, 0, 0,
+ 0, 0, 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, 115, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 115, 77, 97, 115, 107, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 101, 120, 0, 109,
+ 97, 115, 107, 0, 99, 98,
+ 50, 0, 171, 219, 0, 0,
+ 0, 7, 0, 0, 0, 248,
+ 0, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 160, 1, 0,
+ 0, 0, 0, 0, 0, 44,
+ 0, 0, 0, 0, 0, 0,
+ 0, 184, 1, 0, 0, 0,
+ 0, 0, 0, 200, 1, 0,
+ 0, 48, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0,
+ 0, 212, 1, 0, 0, 0,
+ 0, 0, 0, 228, 1, 0,
+ 0, 64, 0, 0, 0, 12,
+ 0, 0, 0, 2, 0, 0,
+ 0, 236, 1, 0, 0, 0,
+ 0, 0, 0, 252, 1, 0,
+ 0, 80, 0, 0, 0, 8,
+ 0, 0, 0, 2, 0, 0,
+ 0, 212, 1, 0, 0, 0,
+ 0, 0, 0, 4, 2, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 24, 2, 0,
+ 0, 92, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 32, 2, 0,
+ 0, 96, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 68, 101, 118,
+ 105, 99, 101, 83, 112, 97,
+ 99, 101, 84, 111, 85, 115,
+ 101, 114, 83, 112, 97, 99,
+ 101, 0, 171, 3, 0, 3,
+ 0, 3, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 100, 105, 109, 101, 110,
+ 115, 105, 111, 110, 115, 0,
+ 171, 1, 0, 3, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 102, 102, 0, 171, 171,
+ 171, 1, 0, 3, 0, 1,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 101, 110, 116, 101, 114, 49,
+ 0, 65, 0, 171, 171, 0,
+ 0, 3, 0, 1, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 114, 97, 100,
+ 105, 117, 115, 49, 0, 115,
+ 113, 95, 114, 97, 100, 105,
+ 117, 115, 49, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
+ 78, 104, 0, 0, 0, 3,
+ 0, 0, 0, 8, 0, 0,
+ 0, 80, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 15, 0, 0,
+ 0, 92, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 3, 3, 0,
+ 0, 92, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 12, 12, 0,
+ 0, 83, 86, 95, 80, 111,
+ 115, 105, 116, 105, 111, 110,
+ 0, 84, 69, 88, 67, 79,
+ 79, 82, 68, 0, 171, 171,
+ 171, 79, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 83,
+ 86, 95, 84, 97, 114, 103,
+ 101, 116, 0, 171, 171, 159,
+ 101, 0, 0, 0, 0, 0,
+ 0, 65, 48, 0, 44, 7,
+ 0, 0, 68, 88, 66, 67,
+ 172, 27, 205, 113, 176, 254,
+ 27, 44, 22, 107, 179, 112,
+ 127, 38, 148, 161, 1, 0,
+ 0, 0, 44, 7, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 148, 1, 0, 0,
+ 104, 3, 0, 0, 228, 3,
+ 0, 0, 136, 6, 0, 0,
+ 188, 6, 0, 0, 65, 111,
+ 110, 57, 84, 1, 0, 0,
+ 84, 1, 0, 0, 0, 2,
+ 254, 255, 252, 0, 0, 0,
+ 88, 0, 0, 0, 4, 0,
+ 36, 0, 0, 0, 84, 0,
+ 0, 0, 84, 0, 0, 0,
+ 36, 0, 1, 0, 84, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 2, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 0, 0,
+ 1, 0, 3, 0, 1, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 6, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 5, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 224, 0, 0,
+ 228, 144, 2, 0, 238, 160,
+ 2, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 3, 128,
+ 0, 0, 228, 144, 1, 0,
+ 238, 160, 1, 0, 228, 160,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 0, 128,
+ 6, 0, 0, 160, 5, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 160, 5, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 6, 0, 85, 160,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 85, 129,
+ 6, 0, 0, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 170, 128, 5, 0, 85, 160,
+ 5, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 0, 128,
+ 6, 0, 85, 160, 1, 0,
+ 0, 2, 1, 0, 4, 128,
+ 6, 0, 0, 160, 8, 0,
+ 0, 3, 0, 0, 8, 224,
+ 1, 0, 228, 128, 3, 0,
+ 228, 160, 8, 0, 0, 3,
+ 0, 0, 4, 224, 1, 0,
+ 228, 128, 4, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 6, 0, 36, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 204, 1, 0, 0,
+ 64, 0, 1, 0, 115, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 1, 0, 0, 0,
+ 4, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 0, 0, 0, 0, 103, 0,
+ 0, 4, 242, 32, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 54, 0,
+ 0, 8, 194, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 50, 0, 0, 11, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 50, 32, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 8,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 26, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 128, 32, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 1, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 16, 0, 0, 8, 66, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 130, 32, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 8,
+ 130, 32, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 8, 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, 3, 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, 82, 68, 69, 70,
+ 156, 2, 0, 0, 2, 0,
+ 0, 0, 100, 0, 0, 0,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 103, 2,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 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, 96, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 98, 48, 0, 99, 98,
+ 50, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 148, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 7, 0, 0, 0, 52, 1,
+ 0, 0, 112, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 244, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 16, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 26, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 40, 1, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 171, 171,
+ 220, 1, 0, 0, 0, 0,
+ 0, 0, 44, 0, 0, 0,
+ 2, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 48, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 64, 0,
+ 0, 0, 12, 0, 0, 0,
+ 0, 0, 0, 0, 40, 2,
+ 0, 0, 0, 0, 0, 0,
+ 56, 2, 0, 0, 80, 0,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 88, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 84, 2, 0, 0, 92, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 96, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 68, 101, 118, 105, 99, 101,
+ 83, 112, 97, 99, 101, 84,
+ 111, 85, 115, 101, 114, 83,
+ 112, 97, 99, 101, 0, 171,
+ 3, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 100, 105,
+ 109, 101, 110, 115, 105, 111,
+ 110, 115, 0, 171, 1, 0,
+ 3, 0, 1, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 102, 102,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 65, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 114, 97, 100, 105, 117, 115,
+ 49, 0, 115, 113, 95, 114,
+ 97, 100, 105, 117, 115, 49,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 171,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 142, 111, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 192, 7,
+ 0, 0, 68, 88, 66, 67,
+ 73, 174, 125, 52, 147, 212,
+ 172, 159, 223, 39, 1, 144,
+ 137, 10, 201, 206, 1, 0,
+ 0, 0, 192, 7, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 196, 1, 0, 0,
+ 56, 4, 0, 0, 180, 4,
+ 0, 0, 28, 7, 0, 0,
+ 140, 7, 0, 0, 65, 111,
+ 110, 57, 132, 1, 0, 0,
+ 132, 1, 0, 0, 0, 2,
+ 255, 255, 76, 1, 0, 0,
+ 56, 0, 0, 0, 1, 0,
+ 44, 0, 0, 0, 56, 0,
+ 0, 0, 56, 0, 2, 0,
+ 36, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, 0, 4, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 2, 0,
+ 15, 160, 0, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 1, 0, 255, 160, 2, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 235, 176, 1, 0,
+ 228, 161, 90, 0, 0, 4,
+ 0, 0, 8, 128, 0, 0,
+ 228, 128, 0, 0, 228, 128,
+ 0, 0, 255, 129, 5, 0,
+ 0, 3, 0, 0, 8, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 4, 128, 1, 0,
+ 255, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 255, 128, 1, 0,
+ 0, 2, 0, 0, 2, 128,
+ 2, 0, 0, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 128, 0, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 170, 161, 0, 0,
+ 255, 129, 5, 0, 0, 3,
+ 2, 0, 7, 128, 2, 0,
+ 255, 128, 2, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 255, 128,
+ 2, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 15, 128,
+ 0, 0, 0, 128, 2, 0,
+ 85, 160, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 108, 2, 0, 0,
+ 64, 0, 0, 0, 155, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 194, 16, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 0, 0,
+ 0, 9, 50, 0, 16, 0,
+ 0, 0, 0, 0, 230, 26,
+ 16, 0, 1, 0, 0, 0,
+ 70, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 54, 0,
+ 0, 6, 66, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 16, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 14, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 29, 0, 0, 9,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 42, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 21, 0,
+ 0, 1, 56, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 19, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 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,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 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,
+ 82, 68, 69, 70, 96, 2,
+ 0, 0, 1, 0, 0, 0,
+ 224, 0, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 43, 2, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 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, 197, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 210, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 214, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 219, 0, 0, 0,
+ 0, 0, 0, 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, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 115, 77, 97, 115, 107,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 99,
+ 98, 50, 0, 171, 219, 0,
+ 0, 0, 7, 0, 0, 0,
+ 248, 0, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 160, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 184, 1, 0, 0,
+ 0, 0, 0, 0, 200, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 212, 1, 0, 0,
+ 0, 0, 0, 0, 228, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 236, 1, 0, 0,
+ 0, 0, 0, 0, 252, 1,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 212, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 24, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 210, 118, 0, 0, 0, 0,
+ 0, 0, 65, 80, 111, 115,
+ 87, 114, 97, 112, 0, 44,
+ 7, 0, 0, 68, 88, 66,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
+ 0, 0, 0, 44, 7, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 148, 1, 0,
+ 0, 104, 3, 0, 0, 228,
+ 3, 0, 0, 136, 6, 0,
+ 0, 188, 6, 0, 0, 65,
+ 111, 110, 57, 84, 1, 0,
+ 0, 84, 1, 0, 0, 0,
+ 2, 254, 255, 252, 0, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 36, 0, 0, 0, 84,
+ 0, 0, 0, 84, 0, 0,
+ 0, 36, 0, 1, 0, 84,
+ 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 3, 0, 0, 0, 0,
+ 0, 1, 0, 3, 0, 1,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 6, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 3,
+ 128, 0, 0, 228, 144, 1,
+ 0, 238, 160, 1, 0, 228,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 0,
+ 128, 6, 0, 0, 160, 5,
+ 0, 0, 3, 0, 0, 4,
+ 128, 0, 0, 170, 128, 5,
+ 0, 0, 160, 5, 0, 0,
+ 3, 1, 0, 1, 128, 0,
+ 0, 170, 128, 6, 0, 85,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 85,
+ 129, 6, 0, 0, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 5, 0, 0,
+ 3, 0, 0, 1, 128, 0,
+ 0, 170, 128, 5, 0, 85,
+ 160, 5, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 0,
+ 128, 6, 0, 85, 160, 1,
+ 0, 0, 2, 1, 0, 4,
+ 128, 6, 0, 0, 160, 8,
+ 0, 0, 3, 0, 0, 8,
+ 224, 1, 0, 228, 128, 3,
+ 0, 228, 160, 8, 0, 0,
+ 3, 0, 0, 4, 224, 1,
+ 0, 228, 128, 4, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 6, 0, 36,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 204, 1, 0,
+ 0, 64, 0, 1, 0, 115,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 1, 0, 0,
+ 0, 4, 0, 0, 0, 95,
+ 0, 0, 3, 50, 16, 16,
+ 0, 0, 0, 0, 0, 103,
+ 0, 0, 4, 242, 32, 16,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 101, 0, 0,
+ 3, 50, 32, 16, 0, 1,
+ 0, 0, 0, 101, 0, 0,
+ 3, 194, 32, 16, 0, 1,
+ 0, 0, 0, 104, 0, 0,
+ 2, 2, 0, 0, 0, 54,
+ 0, 0, 8, 194, 32, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 50, 0, 0, 11, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 50, 32, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 8, 34, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 56, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 63, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 1, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 16, 0, 0, 8, 66,
+ 32, 16, 0, 1, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 70, 130, 32,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0,
+ 8, 130, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 12, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 8,
+ 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, 3,
+ 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, 82, 68, 69,
+ 70, 156, 2, 0, 0, 2,
+ 0, 0, 0, 100, 0, 0,
+ 0, 2, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 103,
+ 2, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 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, 96,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 99, 98, 48, 0, 99,
+ 98, 50, 0, 92, 0, 0,
+ 0, 4, 0, 0, 0, 148,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 7, 0, 0, 0, 52,
+ 1, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 244, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 16, 1, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 26, 1, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 40, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 220, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 40,
+ 2, 0, 0, 0, 0, 0,
+ 0, 56, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 64, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 84, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 92, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 100, 105, 102,
+ 102, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 101, 110,
+ 116, 101, 114, 49, 0, 65,
+ 0, 171, 171, 0, 0, 3,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 114, 97, 100, 105, 117,
+ 115, 49, 0, 115, 113, 95,
+ 114, 97, 100, 105, 117, 115,
+ 49, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 171, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 167, 126, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 228,
+ 9, 0, 0, 68, 88, 66,
+ 67, 193, 68, 83, 4, 120,
+ 206, 206, 65, 213, 56, 189,
+ 186, 120, 85, 235, 59, 1,
+ 0, 0, 0, 228, 9, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 128, 2, 0,
+ 0, 88, 6, 0, 0, 212,
+ 6, 0, 0, 64, 9, 0,
+ 0, 176, 9, 0, 0, 65,
+ 111, 110, 57, 64, 2, 0,
+ 0, 64, 2, 0, 0, 0,
+ 2, 255, 255, 8, 2, 0,
+ 0, 56, 0, 0, 0, 1,
+ 0, 44, 0, 0, 0, 56,
+ 0, 0, 0, 56, 0, 2,
+ 0, 36, 0, 0, 0, 56,
+ 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 0, 0, 4,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 255,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 4,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 128, 191, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 31, 0, 0, 2, 0,
+ 0, 0, 128, 0, 0, 15,
+ 176, 31, 0, 0, 2, 0,
+ 0, 0, 144, 0, 8, 15,
+ 160, 31, 0, 0, 2, 0,
+ 0, 0, 144, 1, 8, 15,
+ 160, 2, 0, 0, 3, 0,
+ 0, 3, 128, 0, 0, 235,
+ 176, 1, 0, 228, 161, 90,
+ 0, 0, 4, 0, 0, 8,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 161, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 1, 0, 170, 160, 1,
+ 0, 0, 2, 0, 0, 4,
+ 128, 1, 0, 255, 160, 8,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 2, 128, 0,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 129, 35,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 85, 128, 7,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 170, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 0, 0, 170, 128, 1,
+ 0, 0, 2, 1, 0, 6,
+ 128, 1, 0, 0, 129, 2,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 0, 128, 1,
+ 0, 148, 128, 6, 0, 0,
+ 2, 1, 0, 1, 128, 1,
+ 0, 170, 160, 5, 0, 0,
+ 3, 0, 0, 13, 128, 0,
+ 0, 228, 128, 1, 0, 0,
+ 128, 1, 0, 0, 2, 1,
+ 0, 8, 128, 1, 0, 255,
+ 160, 4, 0, 0, 4, 1,
+ 0, 7, 128, 0, 0, 248,
+ 128, 0, 0, 170, 160, 1,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 1, 128, 1,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 128, 88,
+ 0, 0, 4, 0, 0, 13,
+ 128, 1, 0, 148, 128, 4,
+ 0, 68, 160, 4, 0, 230,
+ 160, 1, 0, 0, 2, 2,
+ 0, 2, 128, 3, 0, 0,
+ 160, 66, 0, 0, 3, 1,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 2, 0, 15,
+ 128, 2, 0, 228, 128, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 2, 0, 7, 128, 2,
+ 0, 255, 128, 2, 0, 228,
+ 128, 5, 0, 0, 3, 1,
+ 0, 15, 128, 1, 0, 255,
+ 128, 2, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 0, 0, 255, 128, 0,
+ 0, 0, 128, 88, 0, 0,
+ 4, 0, 0, 1, 128, 0,
+ 0, 255, 128, 0, 0, 0,
+ 128, 0, 0, 170, 128, 88,
+ 0, 0, 4, 1, 0, 15,
+ 128, 0, 0, 0, 129, 4,
+ 0, 170, 160, 1, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 15, 128, 0, 0, 85,
+ 128, 1, 0, 228, 128, 4,
+ 0, 170, 160, 1, 0, 0,
+ 2, 0, 8, 15, 128, 0,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 208,
+ 3, 0, 0, 64, 0, 0,
+ 0, 244, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 0,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 1,
+ 0, 0, 0, 88, 24, 0,
+ 4, 0, 112, 16, 0, 0,
+ 0, 0, 0, 85, 85, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 1, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 98,
+ 16, 0, 3, 194, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 104,
+ 0, 0, 2, 3, 0, 0,
+ 0, 0, 0, 0, 9, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 230, 26, 16, 0, 1,
+ 0, 0, 0, 70, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 54, 0, 0, 6, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 128, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 16, 0, 0, 8, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 70, 130, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 15, 0, 0,
+ 7, 18, 0, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 8, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 42, 128, 32,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 50, 0, 0,
+ 10, 18, 0, 16, 0, 0,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 49, 0, 0, 7, 34,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 75,
+ 0, 0, 6, 18, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 129, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 6, 34, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 7, 82, 0, 16,
+ 0, 0, 0, 0, 0, 166,
+ 10, 16, 0, 0, 0, 0,
+ 0, 6, 1, 16, 0, 1,
+ 0, 0, 0, 14, 0, 0,
+ 8, 82, 0, 16, 0, 0,
+ 0, 0, 0, 6, 2, 16,
+ 0, 0, 0, 0, 0, 166,
+ 138, 32, 0, 0, 0, 0,
+ 0, 5, 0, 0, 0, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 1, 0, 0, 0, 134,
+ 0, 16, 0, 0, 0, 0,
+ 0, 166, 138, 32, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 29, 0, 0, 9, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 246, 143, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 1, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 50, 0, 0, 9, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 34,
+ 0, 16, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 63, 69, 0, 0,
+ 9, 242, 0, 16, 0, 2,
+ 0, 0, 0, 70, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 26, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 52, 0, 0, 7, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 29,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 56, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 1, 0, 0,
+ 0, 70, 126, 16, 0, 1,
+ 0, 0, 0, 0, 96, 16,
+ 0, 1, 0, 0, 0, 56,
+ 0, 0, 7, 242, 32, 16,
+ 0, 0, 0, 0, 0, 246,
+ 15, 16, 0, 0, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 33, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 19, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 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, 82,
+ 68, 69, 70, 100, 2, 0,
+ 0, 1, 0, 0, 0, 228,
+ 0, 0, 0, 5, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 255, 255, 0, 1, 0,
+ 0, 47, 2, 0, 0, 188,
+ 0, 0, 0, 3, 0, 0,
+ 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, 201, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 214, 0, 0,
+ 0, 2, 0, 0, 0, 5,
+ 0, 0, 0, 4, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 218,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 1, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 223, 0, 0, 0, 0,
+ 0, 0, 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, 115, 87, 114,
+ 97, 112, 83, 97, 109, 112,
+ 108, 101, 114, 0, 115, 77,
+ 97, 115, 107, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 101, 120, 0, 109, 97, 115,
+ 107, 0, 99, 98, 50, 0,
+ 171, 223, 0, 0, 0, 7,
+ 0, 0, 0, 252, 0, 0,
+ 0, 112, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 164, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 0, 0, 0, 0, 188,
+ 1, 0, 0, 0, 0, 0,
+ 0, 204, 1, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 216,
+ 1, 0, 0, 0, 0, 0,
+ 0, 232, 1, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 240,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 216,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 28, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 36, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 100, 105, 102,
+ 102, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 101, 110,
+ 116, 101, 114, 49, 0, 65,
+ 0, 171, 171, 0, 0, 3,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 114, 97, 100, 105, 117,
+ 115, 49, 0, 115, 113, 95,
+ 114, 97, 100, 105, 117, 115,
+ 49, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 171, 73, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 3, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 12, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 79,
+ 83, 71, 78, 44, 0, 0,
+ 0, 1, 0, 0, 0, 8,
+ 0, 0, 0, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 0, 171, 171, 235, 133, 0,
+ 0, 0, 0, 0, 0, 65,
+ 48, 87, 114, 97, 112, 0,
+ 44, 7, 0, 0, 68, 88,
+ 66, 67, 172, 27, 205, 113,
+ 176, 254, 27, 44, 22, 107,
+ 179, 112, 127, 38, 148, 161,
+ 1, 0, 0, 0, 44, 7,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 148, 1,
+ 0, 0, 104, 3, 0, 0,
+ 228, 3, 0, 0, 136, 6,
+ 0, 0, 188, 6, 0, 0,
+ 65, 111, 110, 57, 84, 1,
+ 0, 0, 84, 1, 0, 0,
+ 0, 2, 254, 255, 252, 0,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 36, 0, 0, 0,
+ 84, 0, 0, 0, 84, 0,
+ 0, 0, 36, 0, 1, 0,
+ 84, 0, 0, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 3, 0, 0, 0,
+ 0, 0, 1, 0, 3, 0,
+ 1, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 6, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 1, 0, 238, 160, 1, 0,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 4, 128, 0, 0,
+ 0, 128, 6, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 170, 128,
+ 5, 0, 0, 160, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 0, 0, 170, 128, 6, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 4, 128, 0, 0,
+ 85, 129, 6, 0, 0, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 192, 0, 0, 228, 128,
+ 0, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 170, 128, 5, 0,
+ 85, 160, 5, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 0, 128, 6, 0, 85, 160,
+ 1, 0, 0, 2, 1, 0,
+ 4, 128, 6, 0, 0, 160,
+ 8, 0, 0, 3, 0, 0,
+ 8, 224, 1, 0, 228, 128,
+ 3, 0, 228, 160, 8, 0,
+ 0, 3, 0, 0, 4, 224,
+ 1, 0, 228, 128, 4, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 6, 0,
+ 36, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 204, 1,
+ 0, 0, 64, 0, 1, 0,
+ 115, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 1, 0,
+ 0, 0, 4, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 194, 32, 16, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 54, 0, 0, 8, 194, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 50, 0, 0, 11,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 16, 0, 0, 8,
+ 66, 32, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 8, 130, 32, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 12, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 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,
+ 3, 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, 82, 68,
+ 69, 70, 156, 2, 0, 0,
+ 2, 0, 0, 0, 100, 0,
+ 0, 0, 2, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 103, 2, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 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,
+ 96, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 99, 98, 48, 0,
+ 99, 98, 50, 0, 92, 0,
+ 0, 0, 4, 0, 0, 0,
+ 148, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 96, 0,
+ 0, 0, 7, 0, 0, 0,
+ 52, 1, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 26, 1,
+ 0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 40, 1,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 171, 171, 220, 1, 0, 0,
+ 0, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 244, 1, 0, 0, 0, 0,
+ 0, 0, 4, 2, 0, 0,
+ 48, 0, 0, 0, 8, 0,
+ 0, 0, 2, 0, 0, 0,
+ 16, 2, 0, 0, 0, 0,
+ 0, 0, 32, 2, 0, 0,
+ 64, 0, 0, 0, 12, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 80, 0, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 0, 0,
+ 0, 0, 64, 2, 0, 0,
+ 88, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 84, 2, 0, 0,
+ 92, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 96, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 68, 101, 118, 105,
+ 99, 101, 83, 112, 97, 99,
+ 101, 84, 111, 85, 115, 101,
+ 114, 83, 112, 97, 99, 101,
+ 0, 171, 3, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 109, 101, 110, 115,
+ 105, 111, 110, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 100, 105,
+ 102, 102, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 99, 101,
+ 110, 116, 101, 114, 49, 0,
+ 65, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 114, 97, 100, 105,
+ 117, 115, 49, 0, 115, 113,
+ 95, 114, 97, 100, 105, 117,
+ 115, 49, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
+ 171, 171, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 226, 143,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 196, 7, 0, 0, 68, 88,
+ 66, 67, 223, 174, 80, 104,
+ 241, 52, 44, 173, 100, 134,
+ 52, 219, 15, 210, 214, 245,
+ 1, 0, 0, 0, 196, 7,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 196, 1,
+ 0, 0, 56, 4, 0, 0,
+ 180, 4, 0, 0, 32, 7,
+ 0, 0, 144, 7, 0, 0,
+ 65, 111, 110, 57, 132, 1,
+ 0, 0, 132, 1, 0, 0,
+ 0, 2, 255, 255, 76, 1,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 4, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 2, 0, 15, 160, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 5, 0, 0, 3,
+ 0, 0, 8, 128, 1, 0,
+ 255, 160, 1, 0, 255, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 128, 0, 0, 235, 176,
+ 1, 0, 228, 161, 90, 0,
+ 0, 4, 0, 0, 8, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 0, 0, 255, 129,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 2, 0, 0, 160, 1, 0,
+ 0, 2, 0, 0, 4, 128,
+ 1, 0, 255, 160, 8, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 6, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 0, 0, 255, 128,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 2, 0, 0, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 128, 0, 8,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 255, 160, 4, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 0, 0, 170, 161,
+ 0, 0, 255, 129, 5, 0,
+ 0, 3, 2, 0, 7, 128,
+ 2, 0, 255, 128, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 255, 128, 2, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 0, 128,
+ 2, 0, 85, 160, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 108, 2,
+ 0, 0, 64, 0, 0, 0,
+ 155, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 54, 0, 0, 6, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 15, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 14, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 29, 0,
+ 0, 9, 66, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 34, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 63,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 31, 0, 4, 3, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 19, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 10, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 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, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 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, 82, 68, 69, 70,
+ 100, 2, 0, 0, 1, 0,
+ 0, 0, 228, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 47, 2,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 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, 201, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 214, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 218, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 223, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 87, 114, 97, 112, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 115, 77, 97, 115, 107,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 99,
+ 98, 50, 0, 171, 223, 0,
+ 0, 0, 7, 0, 0, 0,
+ 252, 0, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 188, 1, 0, 0,
+ 0, 0, 0, 0, 204, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 232, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 240, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 8, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 36, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 38, 151, 0, 0, 0, 0,
+ 0, 0, 65, 80, 111, 115,
+ 77, 105, 114, 114, 111, 114,
+ 0, 44, 7, 0, 0, 68,
+ 88, 66, 67, 172, 27, 205,
+ 113, 176, 254, 27, 44, 22,
+ 107, 179, 112, 127, 38, 148,
+ 161, 1, 0, 0, 0, 44,
+ 7, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 148,
+ 1, 0, 0, 104, 3, 0,
+ 0, 228, 3, 0, 0, 136,
+ 6, 0, 0, 188, 6, 0,
+ 0, 65, 111, 110, 57, 84,
+ 1, 0, 0, 84, 1, 0,
+ 0, 0, 2, 254, 255, 252,
+ 0, 0, 0, 88, 0, 0,
+ 0, 4, 0, 36, 0, 0,
+ 0, 84, 0, 0, 0, 84,
+ 0, 0, 0, 36, 0, 1,
+ 0, 84, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 1, 0, 2, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 3, 0, 0,
+ 0, 0, 0, 1, 0, 3,
+ 0, 1, 0, 5, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 254, 255, 81,
+ 0, 0, 5, 6, 0, 15,
+ 160, 0, 0, 128, 63, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31,
+ 0, 0, 2, 5, 0, 0,
+ 128, 0, 0, 15, 144, 4,
+ 0, 0, 4, 0, 0, 3,
+ 224, 0, 0, 228, 144, 2,
+ 0, 238, 160, 2, 0, 228,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 4, 128, 0,
+ 0, 0, 128, 6, 0, 0,
+ 160, 5, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 170,
+ 128, 5, 0, 0, 160, 5,
+ 0, 0, 3, 1, 0, 1,
+ 128, 0, 0, 170, 128, 6,
+ 0, 85, 160, 2, 0, 0,
+ 3, 0, 0, 4, 128, 0,
+ 0, 85, 129, 6, 0, 0,
+ 160, 2, 0, 0, 3, 0,
+ 0, 3, 192, 0, 0, 228,
+ 128, 0, 0, 228, 160, 5,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 170, 128, 5,
+ 0, 85, 160, 5, 0, 0,
+ 3, 1, 0, 2, 128, 0,
+ 0, 0, 128, 6, 0, 85,
+ 160, 1, 0, 0, 2, 1,
+ 0, 4, 128, 6, 0, 0,
+ 160, 8, 0, 0, 3, 0,
+ 0, 8, 224, 1, 0, 228,
+ 128, 3, 0, 228, 160, 8,
+ 0, 0, 3, 0, 0, 4,
+ 224, 1, 0, 228, 128, 4,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 6,
+ 0, 36, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 204,
+ 1, 0, 0, 64, 0, 1,
+ 0, 115, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 1,
+ 0, 0, 0, 4, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 104,
+ 0, 0, 2, 2, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 0, 16, 0, 0,
+ 0, 0, 0, 70, 16, 16,
+ 0, 0, 0, 0, 0, 230,
+ 138, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 70,
+ 128, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 0, 8, 34, 0, 16,
+ 0, 0, 0, 0, 0, 26,
+ 0, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 56, 0, 0, 8, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 56, 0, 0,
+ 10, 50, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 63, 0, 0, 0, 63, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 1, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 16, 0, 0,
+ 8, 66, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 8, 130, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 70, 130, 32, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 1, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 8, 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, 3, 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, 82,
+ 68, 69, 70, 156, 2, 0,
+ 0, 2, 0, 0, 0, 100,
+ 0, 0, 0, 2, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 254, 255, 0, 1, 0,
+ 0, 103, 2, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 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, 96, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 99, 98, 48,
+ 0, 99, 98, 50, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 148, 0, 0, 0, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 0, 7, 0, 0,
+ 0, 52, 1, 0, 0, 112,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 244,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 16,
+ 1, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 26,
+ 1, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 40,
+ 1, 0, 0, 48, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 81,
+ 117, 97, 100, 68, 101, 115,
+ 99, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 77, 97, 115, 107, 84,
+ 101, 120, 67, 111, 111, 114,
+ 100, 115, 0, 84, 101, 120,
+ 116, 67, 111, 108, 111, 114,
+ 0, 171, 171, 220, 1, 0,
+ 0, 0, 0, 0, 0, 44,
+ 0, 0, 0, 2, 0, 0,
+ 0, 244, 1, 0, 0, 0,
+ 0, 0, 0, 4, 2, 0,
+ 0, 48, 0, 0, 0, 8,
+ 0, 0, 0, 2, 0, 0,
+ 0, 16, 2, 0, 0, 0,
+ 0, 0, 0, 32, 2, 0,
+ 0, 64, 0, 0, 0, 12,
+ 0, 0, 0, 0, 0, 0,
+ 0, 40, 2, 0, 0, 0,
+ 0, 0, 0, 56, 2, 0,
+ 0, 80, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 2, 0, 0, 0,
+ 0, 0, 0, 64, 2, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 84, 2, 0,
+ 0, 92, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 92, 2, 0,
+ 0, 96, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 68, 101, 118,
+ 105, 99, 101, 83, 112, 97,
+ 99, 101, 84, 111, 85, 115,
+ 101, 114, 83, 112, 97, 99,
+ 101, 0, 171, 3, 0, 3,
+ 0, 3, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 100, 105, 109, 101, 110,
+ 115, 105, 111, 110, 115, 0,
+ 171, 1, 0, 3, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 102, 102, 0, 171, 171,
+ 171, 1, 0, 3, 0, 1,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 101, 110, 116, 101, 114, 49,
+ 0, 65, 0, 171, 171, 0,
+ 0, 3, 0, 1, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 114, 97, 100,
+ 105, 117, 115, 49, 0, 115,
+ 113, 95, 114, 97, 100, 105,
+ 117, 115, 49, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
+ 78, 44, 0, 0, 0, 1,
+ 0, 0, 0, 8, 0, 0,
+ 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 7, 3, 0,
+ 0, 80, 79, 83, 73, 84,
+ 73, 79, 78, 0, 171, 171,
+ 171, 79, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 12, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 3, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 1,
+ 159, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 232, 9, 0, 0, 68,
+ 88, 66, 67, 48, 133, 157,
+ 76, 135, 209, 82, 153, 49,
+ 138, 172, 57, 31, 63, 161,
+ 231, 1, 0, 0, 0, 232,
+ 9, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 128,
+ 2, 0, 0, 88, 6, 0,
+ 0, 212, 6, 0, 0, 68,
+ 9, 0, 0, 180, 9, 0,
+ 0, 65, 111, 110, 57, 64,
+ 2, 0, 0, 64, 2, 0,
+ 0, 0, 2, 255, 255, 8,
+ 2, 0, 0, 56, 0, 0,
+ 0, 1, 0, 44, 0, 0,
+ 0, 56, 0, 0, 0, 56,
+ 0, 2, 0, 36, 0, 0,
+ 0, 56, 0, 0, 0, 0,
+ 0, 1, 1, 1, 0, 0,
+ 0, 4, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 81, 0, 0,
+ 5, 3, 0, 15, 160, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 128,
+ 191, 0, 0, 0, 0, 0,
+ 0, 0, 128, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 15, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 31, 0, 0,
+ 2, 0, 0, 0, 144, 1,
+ 8, 15, 160, 2, 0, 0,
+ 3, 0, 0, 3, 128, 0,
+ 0, 235, 176, 1, 0, 228,
+ 161, 90, 0, 0, 4, 0,
+ 0, 8, 128, 0, 0, 228,
+ 128, 0, 0, 228, 128, 2,
+ 0, 0, 161, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 1, 0, 170,
+ 160, 1, 0, 0, 2, 0,
+ 0, 4, 128, 1, 0, 255,
+ 160, 8, 0, 0, 3, 0,
+ 0, 1, 128, 0, 0, 228,
+ 128, 0, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 2,
+ 128, 0, 0, 0, 128, 0,
+ 0, 0, 128, 0, 0, 255,
+ 129, 35, 0, 0, 2, 0,
+ 0, 4, 128, 0, 0, 85,
+ 128, 7, 0, 0, 2, 0,
+ 0, 4, 128, 0, 0, 170,
+ 128, 6, 0, 0, 2, 1,
+ 0, 1, 128, 0, 0, 170,
+ 128, 1, 0, 0, 2, 1,
+ 0, 6, 128, 1, 0, 0,
+ 129, 2, 0, 0, 3, 0,
+ 0, 13, 128, 0, 0, 0,
+ 128, 1, 0, 148, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 1, 0, 170, 160, 5,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 228, 128, 1,
+ 0, 0, 128, 1, 0, 0,
+ 2, 1, 0, 8, 128, 1,
+ 0, 255, 160, 4, 0, 0,
+ 4, 1, 0, 7, 128, 0,
+ 0, 248, 128, 0, 0, 170,
+ 160, 1, 0, 255, 128, 88,
+ 0, 0, 4, 2, 0, 1,
+ 128, 1, 0, 0, 128, 0,
+ 0, 0, 128, 0, 0, 255,
+ 128, 88, 0, 0, 4, 0,
+ 0, 13, 128, 1, 0, 148,
+ 128, 4, 0, 68, 160, 4,
+ 0, 230, 160, 1, 0, 0,
+ 2, 2, 0, 2, 128, 3,
+ 0, 0, 160, 66, 0, 0,
+ 3, 1, 0, 15, 128, 0,
+ 0, 228, 176, 1, 8, 228,
+ 160, 66, 0, 0, 3, 2,
+ 0, 15, 128, 2, 0, 228,
+ 128, 0, 8, 228, 160, 5,
+ 0, 0, 3, 2, 0, 7,
+ 128, 2, 0, 255, 128, 2,
+ 0, 228, 128, 5, 0, 0,
+ 3, 1, 0, 15, 128, 1,
+ 0, 255, 128, 2, 0, 228,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 0, 0, 0, 128, 88,
+ 0, 0, 4, 0, 0, 1,
+ 128, 0, 0, 255, 128, 0,
+ 0, 0, 128, 0, 0, 170,
+ 128, 88, 0, 0, 4, 1,
+ 0, 15, 128, 0, 0, 0,
+ 129, 4, 0, 170, 160, 1,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 15, 128, 0,
+ 0, 85, 128, 1, 0, 228,
+ 128, 4, 0, 170, 160, 1,
+ 0, 0, 2, 0, 8, 15,
+ 128, 0, 0, 228, 128, 255,
+ 255, 0, 0, 83, 72, 68,
+ 82, 208, 3, 0, 0, 64,
+ 0, 0, 0, 244, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 0, 0, 0,
+ 0, 7, 0, 0, 0, 90,
+ 0, 0, 3, 0, 96, 16,
+ 0, 0, 0, 0, 0, 90,
+ 0, 0, 3, 0, 96, 16,
+ 0, 1, 0, 0, 0, 88,
+ 24, 0, 4, 0, 112, 16,
+ 0, 0, 0, 0, 0, 85,
+ 85, 0, 0, 88, 24, 0,
+ 4, 0, 112, 16, 0, 1,
+ 0, 0, 0, 85, 85, 0,
+ 0, 98, 16, 0, 3, 50,
+ 16, 16, 0, 1, 0, 0,
+ 0, 98, 16, 0, 3, 194,
+ 16, 16, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 104, 0, 0, 2, 3,
+ 0, 0, 0, 0, 0, 0,
+ 9, 50, 0, 16, 0, 0,
+ 0, 0, 0, 230, 26, 16,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 54, 0, 0,
+ 6, 66, 0, 16, 0, 0,
+ 0, 0, 0, 58, 128, 32,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 16, 0, 0,
+ 8, 66, 0, 16, 0, 0,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 70,
+ 130, 32, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 15,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 9, 18, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 128, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 0, 56, 0, 0,
+ 8, 18, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 128, 32, 0, 0, 0, 0,
+ 0, 5, 0, 0, 0, 50,
+ 0, 0, 10, 18, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 49, 0, 0,
+ 7, 34, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 75, 0, 0, 6, 18,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 0, 16, 128, 129,
+ 0, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 6, 34,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 7, 82,
+ 0, 16, 0, 0, 0, 0,
+ 0, 166, 10, 16, 0, 0,
+ 0, 0, 0, 6, 1, 16,
+ 0, 1, 0, 0, 0, 14,
+ 0, 0, 8, 82, 0, 16,
+ 0, 0, 0, 0, 0, 6,
+ 2, 16, 0, 0, 0, 0,
+ 0, 166, 138, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 56, 0, 0, 8, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 134, 0, 16, 0, 0,
+ 0, 0, 0, 166, 138, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 29, 0, 0,
+ 9, 50, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 1, 0, 0, 0, 246,
+ 143, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 1, 0, 0,
+ 10, 50, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 1, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 128,
+ 63, 0, 0, 128, 63, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 42, 0, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 50, 0, 0,
+ 9, 18, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 34, 0, 16, 0, 2,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 63, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 126, 16, 0, 0,
+ 0, 0, 0, 0, 96, 16,
+ 0, 0, 0, 0, 0, 31,
+ 0, 4, 3, 26, 0, 16,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 8, 242, 32, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 62, 0, 0, 1, 21,
+ 0, 0, 1, 52, 0, 0,
+ 7, 18, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 0, 1, 0, 0,
+ 0, 29, 0, 0, 7, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 31,
+ 0, 4, 3, 10, 0, 16,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 8, 242, 32, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 62, 0, 0, 1, 21,
+ 0, 0, 1, 56, 0, 0,
+ 7, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 1,
+ 0, 0, 0, 70, 126, 16,
+ 0, 1, 0, 0, 0, 0,
+ 96, 16, 0, 1, 0, 0,
+ 0, 56, 0, 0, 7, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 246, 15, 16, 0, 0,
+ 0, 0, 0, 70, 14, 16,
+ 0, 2, 0, 0, 0, 62,
+ 0, 0, 1, 83, 84, 65,
+ 84, 116, 0, 0, 0, 33,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 19, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 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, 82, 68, 69, 70, 104,
+ 2, 0, 0, 1, 0, 0,
+ 0, 232, 0, 0, 0, 5,
+ 0, 0, 0, 28, 0, 0,
+ 0, 0, 4, 255, 255, 0,
+ 1, 0, 0, 51, 2, 0,
+ 0, 188, 0, 0, 0, 3,
+ 0, 0, 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, 203, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 216,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 220, 0, 0, 0, 2,
+ 0, 0, 0, 5, 0, 0,
+ 0, 4, 0, 0, 0, 255,
+ 255, 255, 255, 1, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 0, 0, 0, 225, 0, 0,
+ 0, 0, 0, 0, 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, 115,
+ 77, 105, 114, 114, 111, 114,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 115, 77, 97, 115,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 109, 97, 115, 107, 0,
+ 99, 98, 50, 0, 171, 171,
+ 171, 225, 0, 0, 0, 7,
+ 0, 0, 0, 0, 1, 0,
+ 0, 112, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 168, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 1, 0, 0, 0, 0, 0,
+ 0, 208, 1, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 220,
+ 1, 0, 0, 0, 0, 0,
+ 0, 236, 1, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 220,
+ 1, 0, 0, 0, 0, 0,
+ 0, 12, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 40, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 100, 105, 102,
+ 102, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 101, 110,
+ 116, 101, 114, 49, 0, 65,
+ 0, 171, 171, 0, 0, 3,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 114, 97, 100, 105, 117,
+ 115, 49, 0, 115, 113, 95,
+ 114, 97, 100, 105, 117, 115,
+ 49, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 171, 73, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 3, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 12, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 79,
+ 83, 71, 78, 44, 0, 0,
+ 0, 1, 0, 0, 0, 8,
+ 0, 0, 0, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 0, 171, 171, 69, 166, 0,
+ 0, 0, 0, 0, 0, 65,
+ 48, 77, 105, 114, 114, 111,
+ 114, 0, 44, 7, 0, 0,
+ 68, 88, 66, 67, 172, 27,
+ 205, 113, 176, 254, 27, 44,
+ 22, 107, 179, 112, 127, 38,
+ 148, 161, 1, 0, 0, 0,
+ 44, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 148, 1, 0, 0, 104, 3,
+ 0, 0, 228, 3, 0, 0,
+ 136, 6, 0, 0, 188, 6,
+ 0, 0, 65, 111, 110, 57,
+ 84, 1, 0, 0, 84, 1,
+ 0, 0, 0, 2, 254, 255,
+ 252, 0, 0, 0, 88, 0,
+ 0, 0, 4, 0, 36, 0,
+ 0, 0, 84, 0, 0, 0,
+ 84, 0, 0, 0, 36, 0,
+ 1, 0, 84, 0, 0, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 1, 0, 2, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 3, 0,
+ 0, 0, 0, 0, 1, 0,
+ 3, 0, 1, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 6, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 2, 0, 238, 160, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 0, 128, 6, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 4, 128, 0, 0,
+ 170, 128, 5, 0, 0, 160,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 170, 128,
+ 6, 0, 85, 160, 2, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 85, 129, 6, 0,
+ 0, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 170, 128,
+ 5, 0, 85, 160, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 0, 128, 6, 0,
+ 85, 160, 1, 0, 0, 2,
+ 1, 0, 4, 128, 6, 0,
+ 0, 160, 8, 0, 0, 3,
+ 0, 0, 8, 224, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 8, 0, 0, 3, 0, 0,
+ 4, 224, 1, 0, 228, 128,
+ 4, 0, 228, 160, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 6, 0, 36, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 204, 1, 0, 0, 64, 0,
+ 1, 0, 115, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 1, 0, 0, 0, 4, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 50, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 8, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 8,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 50, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 16, 0,
+ 0, 8, 66, 32, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 8, 130, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 130, 32, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 12, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 8, 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, 3, 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,
+ 82, 68, 69, 70, 156, 2,
+ 0, 0, 2, 0, 0, 0,
+ 100, 0, 0, 0, 2, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 254, 255, 0, 1,
+ 0, 0, 103, 2, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 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, 96, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 99, 98,
+ 48, 0, 99, 98, 50, 0,
+ 92, 0, 0, 0, 4, 0,
+ 0, 0, 148, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 7, 0,
+ 0, 0, 52, 1, 0, 0,
+ 112, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 244, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 26, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 40, 1, 0, 0, 48, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 81, 117, 97, 100, 68, 101,
+ 115, 99, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 77, 97, 115, 107,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 84, 101,
+ 120, 116, 67, 111, 108, 111,
+ 114, 0, 171, 171, 220, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 2, 0,
+ 0, 0, 244, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 0, 0,
+ 0, 0, 40, 2, 0, 0,
+ 0, 0, 0, 0, 56, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 84, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 66, 176, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 200, 7, 0, 0,
+ 68, 88, 66, 67, 238, 212,
+ 160, 43, 129, 11, 44, 225,
+ 62, 162, 102, 35, 9, 220,
+ 80, 177, 1, 0, 0, 0,
+ 200, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 196, 1, 0, 0, 56, 4,
+ 0, 0, 180, 4, 0, 0,
+ 36, 7, 0, 0, 148, 7,
+ 0, 0, 65, 111, 110, 57,
+ 132, 1, 0, 0, 132, 1,
+ 0, 0, 0, 2, 255, 255,
+ 76, 1, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0,
+ 0, 0, 4, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 2, 0, 15, 160,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 5, 0,
+ 0, 3, 0, 0, 8, 128,
+ 1, 0, 255, 160, 1, 0,
+ 255, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 235, 176, 1, 0, 228, 161,
+ 90, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 228, 128,
+ 0, 0, 228, 128, 0, 0,
+ 255, 129, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 2, 0, 0, 160,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 1, 0, 255, 160,
+ 8, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 6, 0,
+ 0, 2, 0, 0, 1, 128,
+ 0, 0, 0, 128, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 0, 0,
+ 255, 128, 1, 0, 0, 2,
+ 0, 0, 2, 128, 2, 0,
+ 0, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 255, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 0, 128, 0, 0,
+ 170, 161, 0, 0, 255, 129,
+ 5, 0, 0, 3, 2, 0,
+ 7, 128, 2, 0, 255, 128,
+ 2, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 255, 128, 2, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 2, 0, 85, 160,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 108, 2, 0, 0, 64, 0,
+ 0, 0, 155, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 1, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 1, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 98, 16, 0, 3, 194, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 0, 0, 0, 9,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 230, 26, 16, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 54, 0, 0, 6,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 16, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 15, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 63,
+ 14, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 29, 0, 0, 9, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 31, 0, 4, 3,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 21, 0, 0, 1,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 19, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 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, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 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, 82, 68,
+ 69, 70, 104, 2, 0, 0,
+ 1, 0, 0, 0, 232, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 51, 2, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 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,
+ 203, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 216, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 225, 0, 0, 0, 0, 0,
+ 0, 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, 115, 77, 105, 114,
+ 114, 111, 114, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 99, 98, 50,
+ 0, 171, 171, 171, 225, 0,
+ 0, 0, 7, 0, 0, 0,
+ 0, 1, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 192, 1, 0, 0,
+ 0, 0, 0, 0, 208, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 244, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 40, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 134, 183, 0, 0, 0, 0,
+ 0, 0, 83, 97, 109, 112,
+ 108, 101, 77, 97, 115, 107,
+ 101, 100, 84, 101, 120, 116,
+ 117, 114, 101, 0, 68, 4,
+ 0, 0, 68, 88, 66, 67,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 248, 0, 0, 0,
+ 244, 1, 0, 0, 112, 2,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
+ 110, 57, 184, 0, 0, 0,
+ 184, 0, 0, 0, 0, 2,
+ 254, 255, 132, 0, 0, 0,
+ 52, 0, 0, 0, 1, 0,
+ 36, 0, 0, 0, 48, 0,
+ 0, 0, 48, 0, 0, 0,
+ 36, 0, 1, 0, 48, 0,
+ 0, 0, 0, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 5, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 224, 0, 0,
+ 228, 144, 2, 0, 238, 160,
+ 2, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 12, 224,
+ 0, 0, 20, 144, 3, 0,
+ 180, 160, 3, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 1, 0, 238, 160, 1, 0,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 4, 0, 68, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 244, 0, 0, 0,
+ 64, 0, 1, 0, 61, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 194, 32, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 194, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 50, 0, 0, 11,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 194, 32, 16, 0,
+ 1, 0, 0, 0, 6, 20,
+ 16, 0, 0, 0, 0, 0,
+ 166, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 132, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 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, 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, 82, 68, 69, 70,
+ 40, 1, 0, 0, 1, 0,
+ 0, 0, 64, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 246, 0,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 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, 99, 98,
+ 48, 0, 60, 0, 0, 0,
+ 4, 0, 0, 0, 88, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 222, 0, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 236, 0, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 110, 191, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 212, 3,
+ 0, 0, 68, 88, 66, 67,
+ 98, 136, 224, 212, 103, 235,
+ 205, 77, 125, 241, 101, 150,
+ 199, 56, 208, 85, 1, 0,
+ 0, 0, 212, 3, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 224, 0, 0, 0,
+ 188, 1, 0, 0, 56, 2,
+ 0, 0, 48, 3, 0, 0,
+ 160, 3, 0, 0, 65, 111,
+ 110, 57, 160, 0, 0, 0,
+ 160, 0, 0, 0, 0, 2,
+ 255, 255, 116, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 44, 0,
+ 0, 0, 44, 0, 2, 0,
+ 36, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 212, 0, 0, 0,
+ 64, 0, 0, 0, 53, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 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,
+ 2, 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,
+ 82, 68, 69, 70, 240, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 187, 0, 0, 0,
+ 156, 0, 0, 0, 3, 0,
+ 0, 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, 165, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 178, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 182, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
+ 171, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 12, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 202, 195,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 87, 105, 116, 104, 83,
+ 104, 97, 100, 111, 119, 0,
+ 4, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 128, 63,
+ 1, 0, 0, 0, 0, 0,
+ 128, 63, 1, 0, 0, 0,
+ 0, 0, 128, 63, 1, 0,
+ 0, 0, 0, 0, 128, 63,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 255, 255, 255, 255,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 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,
+ 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, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
+ 0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 242, 199,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 9, 0, 0, 68, 88,
+ 66, 67, 128, 131, 241, 85,
+ 199, 21, 192, 89, 55, 255,
+ 82, 94, 121, 175, 16, 184,
+ 1, 0, 0, 0, 232, 9,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 2,
+ 0, 0, 8, 7, 0, 0,
+ 132, 7, 0, 0, 68, 9,
+ 0, 0, 180, 9, 0, 0,
+ 65, 111, 110, 57, 184, 2,
+ 0, 0, 184, 2, 0, 0,
+ 0, 2, 255, 255, 120, 2,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 40, 0, 0, 0,
+ 64, 0, 0, 0, 64, 0,
+ 1, 0, 36, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 4, 0,
+ 3, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 176, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 3, 0, 85, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 0, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 170, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 2, 0, 0, 3, 2, 0,
+ 1, 128, 0, 0, 0, 176,
+ 0, 0, 255, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 0, 0, 85, 176, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 0, 8, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 170, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 4, 0, 0, 4,
+ 0, 0, 1, 128, 3, 0,
+ 255, 160, 2, 0, 255, 128,
+ 0, 0, 0, 128, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 0, 0, 0, 176, 1, 0,
+ 0, 160, 1, 0, 0, 2,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 3,
+ 2, 0, 1, 128, 0, 0,
+ 0, 176, 1, 0, 85, 160,
+ 1, 0, 0, 2, 2, 0,
+ 2, 128, 0, 0, 85, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 128, 0, 8,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 1, 128, 4, 0,
+ 0, 160, 1, 0, 255, 128,
+ 0, 0, 0, 128, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 4, 0, 85, 160, 2, 0,
+ 255, 128, 0, 0, 0, 128,
+ 2, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 1, 0, 170, 160, 1, 0,
+ 0, 2, 1, 0, 2, 128,
+ 0, 0, 85, 176, 2, 0,
+ 0, 3, 2, 0, 1, 128,
+ 0, 0, 0, 176, 1, 0,
+ 255, 160, 1, 0, 0, 2,
+ 2, 0, 2, 128, 0, 0,
+ 85, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 2, 0, 228, 128,
+ 0, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 4, 0, 170, 160, 1, 0,
+ 255, 128, 0, 0, 0, 128,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 4, 0, 255, 160,
+ 2, 0, 255, 128, 0, 0,
+ 0, 128, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 2, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 0, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 5, 0, 0, 160, 1, 0,
+ 255, 128, 0, 0, 0, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 0, 128,
+ 6, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 8, 4, 0, 0, 64, 0,
+ 0, 0, 2, 1, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 16,
+ 16, 0, 1, 0, 0, 0,
+ 38, 135, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 7, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 230, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 0, 0, 0, 0, 86, 21,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 10, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 1, 0, 0, 0, 38, 135,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 82, 0, 16, 0,
+ 2, 0, 0, 0, 86, 7,
+ 16, 0, 1, 0, 0, 0,
+ 54, 0, 0, 5, 162, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 70, 0, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 230, 10, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 18, 0, 16, 0, 1, 0,
+ 0, 0, 10, 16, 16, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 30, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 13, 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, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 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, 82, 68, 69, 70,
+ 184, 1, 0, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 132, 1,
+ 0, 0, 124, 0, 0, 0,
+ 3, 0, 0, 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, 139, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 143, 0, 0, 0, 0, 0,
+ 0, 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, 115, 83, 104, 97,
+ 100, 111, 119, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 101, 120, 0, 99, 98, 49,
+ 0, 171, 143, 0, 0, 0,
+ 4, 0, 0, 0, 172, 0,
+ 0, 0, 160, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 12, 1, 0, 0,
+ 0, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 44, 1, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 1, 0, 0, 0, 0,
+ 0, 0, 76, 1, 0, 0,
+ 96, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 88, 1, 0, 0, 0, 0,
+ 0, 0, 104, 1, 0, 0,
+ 144, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 116, 1, 0, 0, 0, 0,
+ 0, 0, 66, 108, 117, 114,
+ 79, 102, 102, 115, 101, 116,
+ 115, 72, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 66, 108,
+ 117, 114, 79, 102, 102, 115,
+ 101, 116, 115, 86, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 66, 108, 117, 114, 87, 101,
+ 105, 103, 104, 116, 115, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 83, 104,
+ 97, 100, 111, 119, 67, 111,
+ 108, 111, 114, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 78, 204, 0, 0,
+ 0, 0, 0, 0, 80, 49,
+ 0, 4, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 128,
+ 63, 1, 0, 0, 0, 0,
+ 0, 128, 63, 1, 0, 0,
+ 0, 0, 0, 128, 63, 1,
+ 0, 0, 0, 0, 0, 128,
+ 63, 1, 0, 0, 0, 3,
+ 0, 0, 0, 255, 255, 255,
+ 255, 68, 4, 0, 0, 68,
+ 88, 66, 67, 77, 85, 167,
+ 240, 56, 56, 155, 78, 125,
+ 96, 49, 253, 103, 100, 22,
+ 62, 1, 0, 0, 0, 68,
+ 4, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 248,
+ 0, 0, 0, 244, 1, 0,
+ 0, 112, 2, 0, 0, 160,
+ 3, 0, 0, 212, 3, 0,
+ 0, 65, 111, 110, 57, 184,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 2, 254, 255, 132,
+ 0, 0, 0, 52, 0, 0,
+ 0, 1, 0, 36, 0, 0,
+ 0, 48, 0, 0, 0, 48,
+ 0, 0, 0, 36, 0, 1,
+ 0, 48, 0, 0, 0, 0,
+ 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 254, 255, 81,
+ 0, 0, 5, 4, 0, 15,
+ 160, 0, 0, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31,
+ 0, 0, 2, 5, 0, 0,
+ 128, 0, 0, 15, 144, 4,
+ 0, 0, 4, 0, 0, 3,
+ 224, 0, 0, 228, 144, 2,
+ 0, 238, 160, 2, 0, 228,
+ 160, 4, 0, 0, 4, 0,
+ 0, 12, 224, 0, 0, 20,
+ 144, 3, 0, 180, 160, 3,
+ 0, 20, 160, 4, 0, 0,
+ 4, 0, 0, 3, 128, 0,
+ 0, 228, 144, 1, 0, 238,
+ 160, 1, 0, 228, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 4,
+ 0, 68, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 244,
+ 0, 0, 0, 64, 0, 1,
+ 0, 61, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 95, 0, 0,
+ 3, 50, 16, 16, 0, 0,
+ 0, 0, 0, 103, 0, 0,
+ 4, 242, 32, 16, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 50,
+ 32, 16, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 194, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 63, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 6, 20, 16, 0, 0,
+ 0, 0, 0, 166, 142, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 6, 132, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 5, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 3, 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, 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, 82,
+ 68, 69, 70, 40, 1, 0,
+ 0, 1, 0, 0, 0, 64,
+ 0, 0, 0, 1, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 254, 255, 0, 1, 0,
+ 0, 246, 0, 0, 0, 60,
+ 0, 0, 0, 0, 0, 0,
+ 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, 99, 98, 48, 0, 60,
+ 0, 0, 0, 4, 0, 0,
+ 0, 88, 0, 0, 0, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 184,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 212,
+ 0, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 222,
+ 0, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 236,
+ 0, 0, 0, 48, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 81,
+ 117, 97, 100, 68, 101, 115,
+ 99, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 77, 97, 115, 107, 84,
+ 101, 120, 67, 111, 111, 114,
+ 100, 115, 0, 84, 101, 120,
+ 116, 67, 111, 108, 111, 114,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 73, 83, 71,
+ 78, 44, 0, 0, 0, 1,
+ 0, 0, 0, 8, 0, 0,
+ 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 7, 3, 0,
+ 0, 80, 79, 83, 73, 84,
+ 73, 79, 78, 0, 171, 171,
+ 171, 79, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 12, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 3, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 117,
+ 214, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 172, 9, 0, 0, 68,
+ 88, 66, 67, 67, 47, 1,
+ 244, 0, 102, 246, 41, 38,
+ 220, 84, 204, 156, 139, 96,
+ 25, 1, 0, 0, 0, 172,
+ 9, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 220,
+ 2, 0, 0, 204, 6, 0,
+ 0, 72, 7, 0, 0, 8,
+ 9, 0, 0, 120, 9, 0,
+ 0, 65, 111, 110, 57, 156,
+ 2, 0, 0, 156, 2, 0,
+ 0, 0, 2, 255, 255, 104,
+ 2, 0, 0, 52, 0, 0,
+ 0, 1, 0, 40, 0, 0,
+ 0, 52, 0, 0, 0, 52,
+ 0, 1, 0, 36, 0, 0,
+ 0, 52, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 6,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 255, 255, 31,
+ 0, 0, 2, 0, 0, 0,
+ 128, 0, 0, 15, 176, 31,
+ 0, 0, 2, 0, 0, 0,
+ 144, 0, 8, 15, 160, 2,
+ 0, 0, 3, 0, 0, 2,
+ 128, 0, 0, 85, 176, 0,
+ 0, 85, 160, 1, 0, 0,
+ 2, 0, 0, 1, 128, 0,
+ 0, 0, 176, 2, 0, 0,
+ 3, 1, 0, 2, 128, 0,
+ 0, 85, 176, 0, 0, 0,
+ 160, 1, 0, 0, 2, 1,
+ 0, 1, 128, 0, 0, 0,
+ 176, 66, 0, 0, 3, 0,
+ 0, 15, 128, 0, 0, 228,
+ 128, 0, 8, 228, 160, 66,
+ 0, 0, 3, 1, 0, 15,
+ 128, 1, 0, 228, 128, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 228, 128, 3, 0, 85,
+ 160, 4, 0, 0, 4, 0,
+ 0, 15, 128, 3, 0, 0,
+ 160, 1, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 3, 1, 0, 2, 128, 0,
+ 0, 85, 176, 0, 0, 170,
+ 160, 1, 0, 0, 2, 1,
+ 0, 1, 128, 0, 0, 0,
+ 176, 2, 0, 0, 3, 2,
+ 0, 2, 128, 0, 0, 85,
+ 176, 0, 0, 255, 160, 1,
+ 0, 0, 2, 2, 0, 1,
+ 128, 0, 0, 0, 176, 66,
+ 0, 0, 3, 1, 0, 15,
+ 128, 1, 0, 228, 128, 0,
+ 8, 228, 160, 66, 0, 0,
+ 3, 2, 0, 15, 128, 2,
+ 0, 228, 128, 0, 8, 228,
+ 160, 4, 0, 0, 4, 0,
+ 0, 15, 128, 3, 0, 170,
+ 160, 1, 0, 228, 128, 0,
+ 0, 228, 128, 4, 0, 0,
+ 4, 0, 0, 15, 128, 3,
+ 0, 255, 160, 2, 0, 228,
+ 128, 0, 0, 228, 128, 2,
+ 0, 0, 3, 1, 0, 2,
+ 128, 0, 0, 85, 176, 1,
+ 0, 0, 160, 1, 0, 0,
+ 2, 1, 0, 1, 128, 0,
+ 0, 0, 176, 2, 0, 0,
+ 3, 2, 0, 2, 128, 0,
+ 0, 85, 176, 1, 0, 85,
+ 160, 1, 0, 0, 2, 2,
+ 0, 1, 128, 0, 0, 0,
+ 176, 66, 0, 0, 3, 1,
+ 0, 15, 128, 1, 0, 228,
+ 128, 0, 8, 228, 160, 66,
+ 0, 0, 3, 2, 0, 15,
+ 128, 2, 0, 228, 128, 0,
+ 8, 228, 160, 4, 0, 0,
+ 4, 0, 0, 15, 128, 4,
+ 0, 0, 160, 1, 0, 228,
+ 128, 0, 0, 228, 128, 4,
+ 0, 0, 4, 0, 0, 15,
+ 128, 4, 0, 85, 160, 2,
+ 0, 228, 128, 0, 0, 228,
+ 128, 2, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 85,
+ 176, 1, 0, 170, 160, 1,
+ 0, 0, 2, 1, 0, 1,
+ 128, 0, 0, 0, 176, 2,
+ 0, 0, 3, 2, 0, 2,
+ 128, 0, 0, 85, 176, 1,
+ 0, 255, 160, 1, 0, 0,
+ 2, 2, 0, 1, 128, 0,
+ 0, 0, 176, 66, 0, 0,
+ 3, 1, 0, 15, 128, 1,
+ 0, 228, 128, 0, 8, 228,
+ 160, 66, 0, 0, 3, 2,
+ 0, 15, 128, 2, 0, 228,
+ 128, 0, 8, 228, 160, 4,
+ 0, 0, 4, 0, 0, 15,
+ 128, 4, 0, 170, 160, 1,
+ 0, 228, 128, 0, 0, 228,
+ 128, 4, 0, 0, 4, 0,
+ 0, 15, 128, 4, 0, 255,
+ 160, 2, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 3, 1, 0, 2, 128, 0,
+ 0, 85, 176, 2, 0, 0,
+ 160, 1, 0, 0, 2, 1,
+ 0, 1, 128, 0, 0, 0,
+ 176, 66, 0, 0, 3, 1,
+ 0, 15, 128, 1, 0, 228,
+ 128, 0, 8, 228, 160, 4,
+ 0, 0, 4, 0, 0, 15,
+ 128, 5, 0, 0, 160, 1,
+ 0, 228, 128, 0, 0, 228,
+ 128, 1, 0, 0, 2, 0,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 232, 3, 0,
+ 0, 64, 0, 0, 0, 250,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 9, 0, 0,
+ 0, 90, 0, 0, 3, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 0, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 104,
+ 0, 0, 2, 4, 0, 0,
+ 0, 54, 0, 0, 5, 82,
+ 0, 16, 0, 0, 0, 0,
+ 0, 6, 16, 16, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 8, 242, 0, 16, 0, 1,
+ 0, 0, 0, 86, 21, 16,
+ 0, 1, 0, 0, 0, 134,
+ 141, 32, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 54,
+ 0, 0, 5, 162, 0, 16,
+ 0, 0, 0, 0, 0, 6,
+ 8, 16, 0, 1, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 2, 0, 0,
+ 0, 230, 10, 16, 0, 0,
+ 0, 0, 0, 70, 126, 16,
+ 0, 0, 0, 0, 0, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 70, 126, 16,
+ 0, 0, 0, 0, 0, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 56, 0, 0, 8, 242,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 0, 0, 0, 86, 133, 32,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 0, 50, 0, 0,
+ 10, 242, 0, 16, 0, 0,
+ 0, 0, 0, 6, 128, 32,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 0, 70, 14, 16,
+ 0, 0, 0, 0, 0, 70,
+ 14, 16, 0, 2, 0, 0,
+ 0, 54, 0, 0, 5, 82,
+ 0, 16, 0, 1, 0, 0,
+ 0, 6, 16, 16, 0, 1,
+ 0, 0, 0, 69, 0, 0,
+ 9, 242, 0, 16, 0, 2,
+ 0, 0, 0, 70, 0, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 69, 0, 0,
+ 9, 242, 0, 16, 0, 1,
+ 0, 0, 0, 230, 10, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 50, 0, 0,
+ 10, 242, 0, 16, 0, 0,
+ 0, 0, 0, 166, 138, 32,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 0, 70, 14, 16,
+ 0, 2, 0, 0, 0, 70,
+ 14, 16, 0, 0, 0, 0,
+ 0, 50, 0, 0, 10, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 246, 143, 32, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 70, 14, 16, 0, 1,
+ 0, 0, 0, 70, 14, 16,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 82, 0, 16,
+ 0, 1, 0, 0, 0, 6,
+ 16, 16, 0, 1, 0, 0,
+ 0, 0, 0, 0, 8, 242,
+ 0, 16, 0, 2, 0, 0,
+ 0, 86, 21, 16, 0, 1,
+ 0, 0, 0, 134, 141, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 54, 0, 0,
+ 5, 162, 0, 16, 0, 1,
+ 0, 0, 0, 6, 8, 16,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 3, 0, 0, 0, 70,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 126, 16, 0, 0,
+ 0, 0, 0, 0, 96, 16,
+ 0, 0, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 1, 0, 0, 0, 230,
+ 10, 16, 0, 1, 0, 0,
+ 0, 70, 126, 16, 0, 0,
+ 0, 0, 0, 0, 96, 16,
+ 0, 0, 0, 0, 0, 50,
+ 0, 0, 10, 242, 0, 16,
+ 0, 0, 0, 0, 0, 6,
+ 128, 32, 0, 0, 0, 0,
+ 0, 7, 0, 0, 0, 70,
+ 14, 16, 0, 3, 0, 0,
+ 0, 70, 14, 16, 0, 0,
+ 0, 0, 0, 50, 0, 0,
+ 10, 242, 0, 16, 0, 0,
+ 0, 0, 0, 86, 133, 32,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 70, 14, 16,
+ 0, 1, 0, 0, 0, 70,
+ 14, 16, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 82,
+ 0, 16, 0, 2, 0, 0,
+ 0, 6, 16, 16, 0, 1,
+ 0, 0, 0, 69, 0, 0,
+ 9, 242, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 69, 0, 0,
+ 9, 242, 0, 16, 0, 2,
+ 0, 0, 0, 230, 10, 16,
+ 0, 2, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 50, 0, 0,
+ 10, 242, 0, 16, 0, 0,
+ 0, 0, 0, 166, 138, 32,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 70, 14, 16,
+ 0, 1, 0, 0, 0, 70,
+ 14, 16, 0, 0, 0, 0,
+ 0, 50, 0, 0, 10, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 246, 143, 32, 0, 0,
+ 0, 0, 0, 7, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 0, 0, 0, 70, 14, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 34, 0, 16,
+ 0, 1, 0, 0, 0, 26,
+ 16, 16, 0, 1, 0, 0,
+ 0, 10, 128, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 54, 0, 0, 5, 18,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 16, 16, 0, 1,
+ 0, 0, 0, 69, 0, 0,
+ 9, 242, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 50, 0, 0,
+ 10, 242, 32, 16, 0, 0,
+ 0, 0, 0, 6, 128, 32,
+ 0, 0, 0, 0, 0, 8,
+ 0, 0, 0, 70, 14, 16,
+ 0, 1, 0, 0, 0, 70,
+ 14, 16, 0, 0, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 29, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 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, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 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, 82, 68, 69,
+ 70, 184, 1, 0, 0, 1,
+ 0, 0, 0, 148, 0, 0,
+ 0, 3, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 255,
+ 255, 0, 1, 0, 0, 132,
+ 1, 0, 0, 124, 0, 0,
+ 0, 3, 0, 0, 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, 139,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 143, 0, 0, 0, 0,
+ 0, 0, 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, 115, 83, 104,
+ 97, 100, 111, 119, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 99, 98,
+ 49, 0, 171, 143, 0, 0,
+ 0, 4, 0, 0, 0, 172,
+ 0, 0, 0, 160, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 12, 1, 0,
+ 0, 0, 0, 0, 0, 48,
+ 0, 0, 0, 0, 0, 0,
+ 0, 28, 1, 0, 0, 0,
+ 0, 0, 0, 44, 1, 0,
+ 0, 48, 0, 0, 0, 48,
+ 0, 0, 0, 2, 0, 0,
+ 0, 60, 1, 0, 0, 0,
+ 0, 0, 0, 76, 1, 0,
+ 0, 96, 0, 0, 0, 48,
+ 0, 0, 0, 2, 0, 0,
+ 0, 88, 1, 0, 0, 0,
+ 0, 0, 0, 104, 1, 0,
+ 0, 144, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 116, 1, 0, 0, 0,
+ 0, 0, 0, 66, 108, 117,
+ 114, 79, 102, 102, 115, 101,
+ 116, 115, 72, 0, 171, 171,
+ 171, 1, 0, 3, 0, 1,
+ 0, 4, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 66,
+ 108, 117, 114, 79, 102, 102,
+ 115, 101, 116, 115, 86, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 66, 108, 117, 114, 87,
+ 101, 105, 103, 104, 116, 115,
+ 0, 1, 0, 3, 0, 1,
+ 0, 4, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 83,
+ 104, 97, 100, 111, 119, 67,
+ 111, 108, 111, 114, 0, 1,
+ 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
+ 171, 73, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 3, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 79,
+ 83, 71, 78, 44, 0, 0,
+ 0, 1, 0, 0, 0, 8,
+ 0, 0, 0, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 0, 171, 171, 209, 218, 0,
+ 0, 0, 0, 0, 0, 80,
+ 50, 0, 4, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 128, 63, 1, 0, 0, 0,
+ 0, 0, 128, 63, 1, 0,
+ 0, 0, 0, 0, 128, 63,
+ 1, 0, 0, 0, 0, 0,
+ 128, 63, 1, 0, 0, 0,
+ 3, 0, 0, 0, 255, 255,
+ 255, 255, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 248, 0, 0, 0, 244, 1,
+ 0, 0, 112, 2, 0, 0,
+ 160, 3, 0, 0, 212, 3,
+ 0, 0, 65, 111, 110, 57,
+ 184, 0, 0, 0, 184, 0,
+ 0, 0, 0, 2, 254, 255,
+ 132, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 48, 0, 0, 0, 36, 0,
+ 1, 0, 48, 0, 0, 0,
+ 0, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 2, 0, 238, 160, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 12, 224, 0, 0,
+ 20, 144, 3, 0, 180, 160,
+ 3, 0, 20, 160, 4, 0,
+ 0, 4, 0, 0, 3, 128,
+ 0, 0, 228, 144, 1, 0,
+ 238, 160, 1, 0, 228, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 192, 0, 0, 228, 128,
+ 0, 0, 228, 160, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 4, 0, 68, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 244, 0, 0, 0, 64, 0,
+ 1, 0, 61, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 0, 0, 0, 0, 103, 0,
+ 0, 4, 242, 32, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 32, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 194, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 11,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 6, 20, 16, 0,
+ 0, 0, 0, 0, 166, 142,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 3, 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, 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,
+ 82, 68, 69, 70, 40, 1,
+ 0, 0, 1, 0, 0, 0,
+ 64, 0, 0, 0, 1, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 254, 255, 0, 1,
+ 0, 0, 246, 0, 0, 0,
+ 60, 0, 0, 0, 0, 0,
+ 0, 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, 99, 98, 48, 0,
+ 60, 0, 0, 0, 4, 0,
+ 0, 0, 88, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 184, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 212, 0, 0, 0, 16, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 222, 0, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 236, 0, 0, 0, 48, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 81, 117, 97, 100, 68, 101,
+ 115, 99, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 77, 97, 115, 107,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 84, 101,
+ 120, 116, 67, 111, 108, 111,
+ 114, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 188, 228, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 164, 10, 0, 0,
+ 68, 88, 66, 67, 70, 166,
+ 174, 156, 153, 145, 163, 116,
+ 127, 37, 205, 162, 136, 116,
+ 62, 222, 1, 0, 0, 0,
+ 164, 10, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 24, 3, 0, 0, 112, 7,
+ 0, 0, 236, 7, 0, 0,
+ 0, 10, 0, 0, 112, 10,
+ 0, 0, 65, 111, 110, 57,
+ 216, 2, 0, 0, 216, 2,
+ 0, 0, 0, 2, 255, 255,
+ 160, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 1, 0,
+ 0, 0, 0, 1, 1, 0,
+ 0, 0, 3, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 2, 128,
+ 0, 0, 85, 176, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 176, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 3, 0, 85, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 3, 0, 0, 160,
+ 1, 0, 228, 128, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 0, 0, 170, 160,
+ 1, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 2, 0, 0, 3, 2, 0,
+ 2, 128, 0, 0, 85, 176,
+ 0, 0, 255, 160, 1, 0,
+ 0, 2, 2, 0, 1, 128,
+ 0, 0, 0, 176, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 8, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 3, 0, 170, 160,
+ 1, 0, 228, 128, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 3, 0,
+ 255, 160, 2, 0, 228, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 85, 176, 1, 0,
+ 0, 160, 1, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 2, 0, 0, 3,
+ 2, 0, 2, 128, 0, 0,
+ 85, 176, 1, 0, 85, 160,
+ 1, 0, 0, 2, 2, 0,
+ 1, 128, 0, 0, 0, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 128, 1, 8,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 4, 0,
+ 0, 160, 1, 0, 228, 128,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 4, 0, 85, 160, 2, 0,
+ 228, 128, 0, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 1, 0, 170, 160, 1, 0,
+ 0, 2, 1, 0, 1, 128,
+ 0, 0, 0, 176, 2, 0,
+ 0, 3, 2, 0, 2, 128,
+ 0, 0, 85, 176, 1, 0,
+ 255, 160, 1, 0, 0, 2,
+ 2, 0, 1, 128, 0, 0,
+ 0, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 2, 0, 228, 128,
+ 1, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 4, 0, 170, 160, 1, 0,
+ 228, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 4, 0, 255, 160,
+ 2, 0, 228, 128, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 1, 0, 0, 2, 2, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 128, 0, 8,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 5, 0,
+ 0, 160, 1, 0, 228, 128,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 2, 0, 255, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 80, 4,
+ 0, 0, 64, 0, 0, 0,
+ 20, 1, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 9, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 134, 141,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 0, 0, 0, 0, 6, 8,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 230, 10, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 230, 10, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 82, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 1, 0,
+ 0, 0, 134, 141, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 6, 8, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
+ 16, 0, 1, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 54, 0, 0, 5, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 8, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 31, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 13, 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,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 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,
+ 82, 68, 69, 70, 12, 2,
+ 0, 0, 1, 0, 0, 0,
+ 232, 0, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 216, 1, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 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, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 216, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 220, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 225, 0, 0, 0,
+ 0, 0, 0, 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, 115, 77,
+ 97, 115, 107, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 83, 104, 97, 100, 111, 119,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 99,
+ 98, 49, 0, 171, 171, 171,
+ 225, 0, 0, 0, 4, 0,
+ 0, 0, 0, 1, 0, 0,
+ 160, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 0, 0, 112, 1,
+ 0, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 48, 0,
+ 0, 0, 48, 0, 0, 0,
+ 2, 0, 0, 0, 144, 1,
+ 0, 0, 0, 0, 0, 0,
+ 160, 1, 0, 0, 96, 0,
+ 0, 0, 48, 0, 0, 0,
+ 2, 0, 0, 0, 172, 1,
+ 0, 0, 0, 0, 0, 0,
+ 188, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 66, 108, 117, 114, 79, 102,
+ 102, 115, 101, 116, 115, 72,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 66, 108, 117, 114,
+ 79, 102, 102, 115, 101, 116,
+ 115, 86, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 66, 108,
+ 117, 114, 87, 101, 105, 103,
+ 104, 116, 115, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 83, 104, 97, 100,
+ 111, 119, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 24, 233, 0, 0, 0, 0,
+ 0, 0, 83, 97, 109, 112,
+ 108, 101, 84, 101, 120, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 85, 110, 109, 97,
+ 115, 107, 101, 100, 0, 4,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 255, 255, 255, 255, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
+ 0, 0, 0, 48, 0, 0,
+ 0, 36, 0, 1, 0, 48,
+ 0, 0, 0, 0, 0, 3,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
+ 0, 0, 0, 70, 16, 16,
+ 0, 0, 0, 0, 0, 230,
+ 138, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
+ 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, 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, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
+ 0, 0, 0, 0, 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, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 19, 244, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 152,
+ 4, 0, 0, 68, 88, 66,
+ 67, 227, 84, 48, 176, 142,
+ 231, 109, 63, 97, 30, 1,
+ 57, 105, 137, 178, 120, 1,
+ 0, 0, 0, 152, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 4, 1, 0,
+ 0, 224, 1, 0, 0, 92,
+ 2, 0, 0, 220, 3, 0,
+ 0, 76, 4, 0, 0, 65,
+ 111, 110, 57, 196, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 2, 255, 255, 144, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 40, 0, 0, 0, 52,
+ 0, 0, 0, 52, 0, 1,
+ 0, 36, 0, 0, 0, 52,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 81, 0, 0,
+ 5, 1, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 15, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 1, 0, 0,
+ 2, 0, 0, 7, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 15, 128, 0,
+ 0, 36, 128, 1, 0, 64,
+ 160, 1, 0, 21, 160, 1,
+ 0, 0, 2, 0, 8, 15,
+ 128, 0, 0, 228, 128, 66,
+ 0, 0, 3, 0, 0, 15,
+ 128, 0, 0, 228, 176, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 70, 128, 0, 0, 255,
+ 160, 1, 0, 0, 2, 1,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 212, 0, 0,
+ 0, 64, 0, 0, 0, 53,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 90, 0, 0, 3, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 0, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 1, 0, 0, 0, 104,
+ 0, 0, 2, 1, 0, 0,
+ 0, 54, 0, 0, 6, 114,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 130, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 130,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 69, 0, 0,
+ 9, 242, 0, 16, 0, 0,
+ 0, 0, 0, 70, 16, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 56, 0, 0,
+ 8, 242, 32, 16, 0, 1,
+ 0, 0, 0, 102, 4, 16,
+ 0, 0, 0, 0, 0, 246,
+ 143, 32, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 62,
+ 0, 0, 1, 83, 84, 65,
+ 84, 116, 0, 0, 0, 5,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 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, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 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, 82, 68, 69, 70, 120,
+ 1, 0, 0, 1, 0, 0,
+ 0, 144, 0, 0, 0, 3,
+ 0, 0, 0, 28, 0, 0,
+ 0, 0, 4, 255, 255, 0,
+ 1, 0, 0, 70, 1, 0,
+ 0, 124, 0, 0, 0, 3,
+ 0, 0, 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, 133, 0, 0,
+ 0, 2, 0, 0, 0, 5,
+ 0, 0, 0, 4, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 137,
+ 0, 0, 0, 0, 0, 0,
+ 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, 115, 83, 97, 109, 112,
+ 108, 101, 114, 0, 116, 101,
+ 120, 0, 99, 98, 48, 0,
+ 171, 171, 171, 137, 0, 0,
+ 0, 4, 0, 0, 0, 168,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 1, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 36, 1, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 46, 1, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 60, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 104,
+ 0, 0, 0, 3, 0, 0,
+ 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 92,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 3, 0, 0, 92,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 83,
+ 86, 95, 80, 111, 115, 105,
+ 116, 105, 111, 110, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 171, 79,
+ 83, 71, 78, 68, 0, 0,
+ 0, 2, 0, 0, 0, 8,
+ 0, 0, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 56, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 0, 171, 171, 111, 248, 0,
+ 0, 0, 0, 0, 0, 77,
+ 97, 115, 107, 101, 100, 0,
+ 4, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 255, 255, 255, 255,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 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,
+ 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, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
+ 0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 74, 253,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 140, 5, 0, 0, 68, 88,
+ 66, 67, 233, 167, 4, 110,
+ 60, 182, 197, 16, 114, 252,
+ 67, 184, 217, 172, 169, 241,
+ 1, 0, 0, 0, 140, 5,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 64, 1,
+ 0, 0, 132, 2, 0, 0,
+ 0, 3, 0, 0, 208, 4,
+ 0, 0, 64, 5, 0, 0,
+ 65, 111, 110, 57, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 255, 255, 200, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 7, 128, 0, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 36, 128, 1, 0, 64, 160,
+ 1, 0, 21, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 3, 128,
+ 0, 0, 235, 176, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 1, 8, 228, 160,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 70, 128,
+ 0, 0, 255, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 60, 1,
+ 0, 0, 64, 0, 0, 0,
+ 79, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 54, 0, 0, 6, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 102, 4, 16, 0,
+ 0, 0, 0, 0, 246, 143,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 26,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 7, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 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, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 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, 82, 68,
+ 69, 70, 200, 1, 0, 0,
+ 1, 0, 0, 0, 224, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 150, 1, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 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,
+ 197, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 210, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 214, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 219, 0, 0, 0, 0, 0,
+ 0, 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, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 99, 98, 48,
+ 0, 171, 219, 0, 0, 0,
+ 4, 0, 0, 0, 248, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 88, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 116, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 126, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 140, 1, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 84, 101, 120, 67, 111, 111,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 12, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 166, 1, 1, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 46, 0,
+ 0, 0, 18, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 93, 0, 0, 0,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 0, 0, 0, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 112, 0,
+ 0, 0, 65, 0, 0, 0,
+ 0, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 126, 0, 0, 0,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 160, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 168, 0, 0, 0, 140, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 181, 0,
+ 0, 0, 140, 0, 0, 0,
+ 0, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 194, 0, 0, 0,
+ 140, 0, 0, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 206, 0, 0, 0, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 218, 0,
+ 0, 0, 112, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 3, 1,
+ 0, 0, 231, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 61, 1, 0, 0,
+ 33, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 107, 1, 0, 0, 79, 1,
+ 0, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 112, 1,
+ 0, 0, 33, 1, 0, 0,
+ 0, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 154, 1, 0, 0,
+ 126, 1, 0, 0, 0, 0,
+ 0, 0, 88, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 156, 1, 0, 0, 126, 1,
+ 0, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 126, 1, 0, 0,
+ 0, 0, 0, 0, 96, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 213, 1, 0, 0,
+ 185, 1, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 217, 1,
+ 0, 0, 185, 1, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 224, 1, 0, 0, 185, 1,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 14, 2, 0, 0,
+ 242, 1, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 4, 0, 0, 0, 45, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 23, 2,
+ 0, 0, 55, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 213, 1, 0, 0,
+ 46, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 35, 2, 0, 0, 47, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 47, 2,
+ 0, 0, 0, 0, 0, 0,
+ 59, 2, 0, 0, 242, 1,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 45, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 71, 2, 0, 0,
+ 55, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 217, 1, 0, 0, 46, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 83, 2,
+ 0, 0, 47, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 95, 2, 0, 0,
+ 0, 0, 0, 0, 107, 2,
+ 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 4, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 120, 2, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 213, 1,
+ 0, 0, 46, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 132, 2, 0, 0,
+ 47, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 144, 2, 0, 0, 0, 0,
+ 0, 0, 156, 2, 0, 0,
+ 242, 1, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 4, 0, 0, 0, 45, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 171, 2,
+ 0, 0, 55, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 213, 1, 0, 0,
+ 46, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 183, 2, 0, 0, 47, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 195, 2,
+ 0, 0, 0, 0, 0, 0,
+ 207, 2, 0, 0, 242, 1,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 45, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 220, 2, 0, 0,
+ 55, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 224, 1, 0, 0, 46, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 232, 2,
+ 0, 0, 47, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 244, 2, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 5, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 3, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 213, 1,
+ 0, 0, 46, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 27, 3, 0, 0,
+ 47, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 39, 3, 0, 0, 52, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 51, 3,
+ 0, 0, 0, 0, 0, 0,
+ 131, 3, 0, 0, 103, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 2, 0,
+ 0, 0, 19, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 143, 3, 0, 0,
+ 13, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 155, 3, 0, 0, 0, 0,
+ 0, 0, 206, 3, 0, 0,
+ 178, 3, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 2, 0, 0, 0, 37, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 219, 3,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 231, 3, 0, 0,
+ 0, 0, 0, 0, 243, 3,
+ 0, 0, 178, 3, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 8, 0, 0, 0,
+ 37, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 4, 0, 0, 38, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 4,
+ 0, 0, 39, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 24, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 36, 4, 0, 0, 41, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 48, 4,
+ 0, 0, 42, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 60, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 72, 4, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 84, 4,
+ 0, 0, 0, 0, 0, 0,
+ 96, 4, 0, 0, 178, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 9, 0,
+ 0, 0, 36, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 107, 4, 0, 0,
+ 37, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 119, 4, 0, 0, 38, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 131, 4,
+ 0, 0, 39, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 143, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 155, 4, 0, 0, 41, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 167, 4,
+ 0, 0, 42, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 179, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 191, 4, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 203, 4,
+ 0, 0, 0, 0, 0, 0,
+ 215, 4, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 229, 4, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 48, 9,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 56, 9, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 28, 12, 0, 0, 36, 12,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 229, 4,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 131, 3,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 144, 16, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 152, 16, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 240, 29,
+ 0, 0, 248, 29, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 229, 4, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 131, 3, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 100, 34, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 108, 34,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 212, 51, 0, 0,
+ 220, 51, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 229, 4, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 73, 56,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 81, 56, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 57, 94, 0, 0, 65, 94,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 86, 94,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 131, 3,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 139, 101, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 147, 101, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 131, 111,
+ 0, 0, 139, 111, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 131, 3, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 190, 118, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 198, 118,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 150, 126, 0, 0,
+ 158, 126, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 215, 133,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 223, 133, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 211, 143, 0, 0, 219, 143,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 131, 3,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 18, 151, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 151, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 238, 158,
+ 0, 0, 246, 158, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 131, 3, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 49, 166, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 57, 166,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 49, 176, 0, 0,
+ 57, 176, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 114, 183,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 122, 183, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 82, 191, 0, 0, 90, 191,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 229, 4,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 131, 3,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 182, 195, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 190, 195, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 162, 199,
+ 0, 0, 170, 199, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 229, 4, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 131, 3, 0, 0,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 194, 199, 0, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 230, 199,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 206, 3, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 58, 204, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 66, 204,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 58, 214, 0, 0,
+ 66, 214, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 10, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 69, 214,
+ 0, 0, 11, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 105, 214, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 243, 3, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 189, 218,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 197, 218, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 129, 228, 0, 0, 137, 228,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 131, 3,
+ 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 140, 228, 0, 0,
+ 11, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 176, 228, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 243, 3,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 4, 233, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 233, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 192, 243,
+ 0, 0, 200, 243, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 218, 243, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 131, 3, 0, 0,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 227, 243, 0, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 7, 244,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 96, 4, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 91, 248, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 99, 248,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 11, 253, 0, 0,
+ 19, 253, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 10, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 253,
+ 0, 0, 11, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 62, 253, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 96, 4, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 146, 1,
+ 1, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 154, 1, 1, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 54, 7, 1, 0
+};
diff --git a/system/graphics/2d/ShadersD2D1.h b/system/graphics/2d/ShadersD2D1.h
new file mode 100644
index 000000000..2d004a000
--- /dev/null
+++ b/system/graphics/2d/ShadersD2D1.h
@@ -0,0 +1,1419 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+//
+//
+// Buffer Definitions:
+//
+// cbuffer constants
+// {
+//
+// float3 diff; // Offset: 0 Size: 12
+// float2 center1; // Offset: 16 Size: 8
+// float A; // Offset: 24 Size: 4
+// float radius1; // Offset: 28 Size: 4
+// float sq_radius1; // Offset: 32 Size: 4
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
+// float3x2 transform; // Offset: 48 Size: 28
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// InputSampler sampler NA NA 0 1
+// GradientSampler sampler NA NA 1 1
+// InputTexture texture float4 2d 0 1
+// GradientTexture texture float4 2d 1 1
+// constants cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_POSITION 0 xyzw 0 POS float
+// SCENE_POSITION 0 xyzw 1 NONE float xy
+// TEXCOORD 0 xyzw 2 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s1 t1
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c5, 0.5, 1, 0, 0
+ def c6, 1, -1, 0, -0
+ dcl t0
+ dcl t1
+ dcl_2d s0
+ dcl_2d s1
+ dp2add r0.x, t0, c3, c3.z
+ dp2add r0.y, t0, c4, c4.z
+ add r0.xy, r0, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ cmp r0.y, r0.y, c5.y, c5.z
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ mul r0.xzw, r0, r1.x
+ mov r1.w, c1.w
+ mad r1.xyz, r0.xzww, c0.z, r1.w
+ cmp r1.w, r1.x, r0.x, r0.w
+ cmp r0.xzw, r1.xyyz, c6.xyxy, c6.zyzw
+ frc r1.x, r1.w
+ add r1.x, -r1.x, r1.w
+ mul r1.y, r1.x, c5.x
+ abs r1.y, r1.y
+ frc r1.y, r1.y
+ cmp r1.y, r1.x, r1.y, -r1.y
+ add r1.x, -r1.x, r1.w
+ add r1.y, r1.y, r1.y
+ abs r1.y, r1.y
+ mul r1.y, r1.y, c2.z
+ frc r1.z, -r1.w
+ lrp r2.w, r1.y, r1.z, r1.x
+ lrp r3.x, c2.y, r2.w, r1.w
+ mov r3.y, c5.x
+ texld r1, t1, s0
+ texld r2, r3, s1
+ mul r2.xyz, r2.w, r2
+ mul r1, r1, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ mul r1, r0.x, r1
+ mul r0, r0.y, r1
+ mov oC0, r0
+
+// approximately 46 instruction slots used (2 texture, 44 arithmetic)
+ps_4_0
+dcl_constantbuffer cb0[5], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_sampler s1, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xy
+dcl_output o0.xyzw
+dcl_temps 3
+dp2 r0.x, v1.xyxx, cb0[3].xyxx
+add r0.x, r0.x, cb0[3].z
+dp2 r0.z, v1.xyxx, cb0[4].xyxx
+add r0.y, r0.z, cb0[4].z
+add r0.xy, r0.xyxx, -cb0[1].xyxx
+dp2 r0.w, r0.xyxx, r0.xyxx
+add r0.w, r0.w, -cb0[2].x
+mul r0.w, r0.w, cb0[1].z
+mov r0.z, cb0[1].w
+dp3 r0.x, r0.xyzx, cb0[0].xyzx
+mad r0.y, r0.x, r0.x, -r0.w
+sqrt r1.x, |r0.y|
+ge r0.y, r0.y, l(0.000000)
+and r0.y, r0.y, l(0x3f800000)
+mov r1.y, -r1.x
+add r0.xz, r0.xxxx, r1.xxyx
+div r0.xz, r0.xxzx, cb0[1].zzzz
+add r0.w, -r0.z, r0.x
+mul r1.xy, r0.xzxx, cb0[0].zzzz
+ge r1.xy, r1.xyxx, -cb0[1].wwww
+and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+mad r0.x, r1.x, r0.w, r0.z
+max r0.z, r1.y, r1.x
+ge r0.z, l(0.000000), r0.z
+movc r0.z, r0.z, l(-0.000000), l(1.000000)
+round_pi r0.w, r0.x
+add r0.w, -r0.x, r0.w
+round_ni r1.x, r0.x
+mul r1.y, r1.x, l(0.500000)
+add r1.x, r0.x, -r1.x
+ge r1.z, r1.y, -r1.y
+frc r1.y, |r1.y|
+movc r1.y, r1.z, r1.y, -r1.y
+add r1.y, r1.y, r1.y
+mul r1.z, |r1.y|, cb0[2].z
+mad r1.y, -|r1.y|, cb0[2].z, l(1.000000)
+mul r0.w, r0.w, r1.z
+mad r0.w, r1.x, r1.y, r0.w
+mul r0.w, r0.w, cb0[2].y
+add r1.x, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r1.x, r0.w
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
+mul r1.xyz, r1.wwww, r1.xyzx
+sample r2.xyzw, v2.xyxx, t0.xyzw, s0
+mul r1.xyzw, r1.xyzw, r2.xyzw
+mul r1.xyzw, r0.zzzz, r1.xyzw
+mul o0.xyzw, r0.yyyy, r1.xyzw
+ret
+// Approximately 49 instruction slots used
+#endif
+
+const BYTE SampleRadialGradientPS[] =
+{
+ 68, 88, 66, 67, 20, 173,
+ 189, 124, 239, 6, 22, 67,
+ 226, 55, 243, 56, 30, 182,
+ 172, 36, 1, 0, 0, 0,
+ 180, 13, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 136, 3, 0, 0, 232, 9,
+ 0, 0, 100, 10, 0, 0,
+ 4, 13, 0, 0, 128, 13,
+ 0, 0, 65, 111, 110, 57,
+ 72, 3, 0, 0, 72, 3,
+ 0, 0, 0, 2, 255, 255,
+ 16, 3, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 5, 0, 15, 160,
+ 0, 0, 0, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 0,
+ 0, 5, 6, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 128, 191, 0, 0, 0, 0,
+ 0, 0, 0, 128, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 90, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
+ 90, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 228, 176,
+ 4, 0, 228, 160, 4, 0,
+ 170, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 1, 0, 228, 161,
+ 90, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 228, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 161, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 1, 0, 170, 160,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 1, 0, 255, 160,
+ 8, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 2, 128,
+ 0, 0, 0, 128, 0, 0,
+ 0, 128, 0, 0, 255, 129,
+ 35, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 85, 128,
+ 88, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 85, 128,
+ 5, 0, 85, 160, 5, 0,
+ 170, 160, 7, 0, 0, 2,
+ 0, 0, 4, 128, 0, 0,
+ 170, 128, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 1, 0, 0, 2,
+ 1, 0, 6, 128, 1, 0,
+ 0, 129, 2, 0, 0, 3,
+ 0, 0, 13, 128, 0, 0,
+ 0, 128, 1, 0, 148, 128,
+ 6, 0, 0, 2, 1, 0,
+ 1, 128, 1, 0, 170, 160,
+ 5, 0, 0, 3, 0, 0,
+ 13, 128, 0, 0, 228, 128,
+ 1, 0, 0, 128, 1, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 160, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 0, 0, 248, 128, 0, 0,
+ 170, 160, 1, 0, 255, 128,
+ 88, 0, 0, 4, 1, 0,
+ 8, 128, 1, 0, 0, 128,
+ 0, 0, 0, 128, 0, 0,
+ 255, 128, 88, 0, 0, 4,
+ 0, 0, 13, 128, 1, 0,
+ 148, 128, 6, 0, 68, 160,
+ 6, 0, 230, 160, 19, 0,
+ 0, 2, 1, 0, 1, 128,
+ 1, 0, 255, 128, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 1, 0, 0, 129, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 1, 0, 2, 128, 1, 0,
+ 0, 128, 5, 0, 0, 160,
+ 35, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 19, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 88, 0, 0, 4, 1, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 85, 128, 1, 0,
+ 85, 129, 2, 0, 0, 3,
+ 1, 0, 1, 128, 1, 0,
+ 0, 129, 1, 0, 255, 128,
+ 2, 0, 0, 3, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 1, 0, 85, 128, 35, 0,
+ 0, 2, 1, 0, 2, 128,
+ 1, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 1, 0, 85, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 1, 0, 4, 128, 1, 0,
+ 255, 129, 18, 0, 0, 4,
+ 2, 0, 8, 128, 1, 0,
+ 85, 128, 1, 0, 170, 128,
+ 1, 0, 0, 128, 18, 0,
+ 0, 4, 3, 0, 1, 128,
+ 2, 0, 85, 160, 2, 0,
+ 255, 128, 1, 0, 255, 128,
+ 1, 0, 0, 2, 3, 0,
+ 2, 128, 5, 0, 0, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 3, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 7, 128, 2, 0,
+ 255, 128, 2, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 8, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 88, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 0, 0, 0, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 85, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 88, 6, 0, 0,
+ 64, 0, 0, 0, 150, 1,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 15, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 15, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 54, 0, 0, 6, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 16, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 75, 0, 0, 6, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 129, 0,
+ 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 54, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 82, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 6, 1,
+ 16, 0, 1, 0, 0, 0,
+ 14, 0, 0, 8, 82, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 2, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 50, 0, 16, 0,
+ 1, 0, 0, 0, 134, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 9, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 246, 143, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 10, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 52, 0,
+ 0, 7, 66, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 29, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 128, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 66, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 29, 0, 0, 8, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 129, 0, 0, 0, 1, 0,
+ 0, 0, 55, 0, 0, 10,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 9, 66, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 129, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 193, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 130, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 49, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 40, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 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, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 2, 0, 0, 0,
+ 3, 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, 82, 68, 69, 70,
+ 152, 2, 0, 0, 1, 0,
+ 0, 0, 0, 1, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 100, 2,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 201, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 217, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 230, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 246, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 73, 110, 112, 117, 116, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 71, 114, 97, 100, 105,
+ 101, 110, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 73,
+ 110, 112, 117, 116, 84, 101,
+ 120, 116, 117, 114, 101, 0,
+ 71, 114, 97, 100, 105, 101,
+ 110, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 99, 111,
+ 110, 115, 116, 97, 110, 116,
+ 115, 0, 246, 0, 0, 0,
+ 8, 0, 0, 0, 24, 1,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 12, 0,
+ 0, 0, 2, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 240, 1, 0, 0,
+ 16, 0, 0, 0, 8, 0,
+ 0, 0, 2, 0, 0, 0,
+ 248, 1, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 24, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 28, 2, 0, 0,
+ 28, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 36, 2, 0, 0,
+ 32, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 47, 2, 0, 0,
+ 36, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 62, 2, 0, 0,
+ 40, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 72, 2, 0, 0,
+ 48, 0, 0, 0, 28, 0,
+ 0, 0, 2, 0, 0, 0,
+ 84, 2, 0, 0, 0, 0,
+ 0, 0, 100, 105, 102, 102,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 1, 0,
+ 3, 0, 1, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 114,
+ 101, 112, 101, 97, 116, 95,
+ 99, 111, 114, 114, 101, 99,
+ 116, 0, 97, 108, 108, 111,
+ 119, 95, 111, 100, 100, 0,
+ 116, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 171, 171,
+ 3, 0, 3, 0, 3, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 73, 83, 71, 78,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 15, 3, 0, 0,
+ 107, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 3, 0, 0,
+ 83, 86, 95, 80, 79, 83,
+ 73, 84, 73, 79, 78, 0,
+ 83, 67, 69, 78, 69, 95,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+//
+//
+// Buffer Definitions:
+//
+// cbuffer constants
+// {
+//
+// float3 diff; // Offset: 0 Size: 12
+// float2 center1; // Offset: 16 Size: 8
+// float A; // Offset: 24 Size: 4 [unused]
+// float radius1; // Offset: 28 Size: 4
+// float sq_radius1; // Offset: 32 Size: 4 [unused]
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
+// float3x2 transform; // Offset: 48 Size: 28
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// InputSampler sampler NA NA 0 1
+// GradientSampler sampler NA NA 1 1
+// InputTexture texture float4 2d 0 1
+// GradientTexture texture float4 2d 1 1
+// constants cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_POSITION 0 xyzw 0 POS float
+// SCENE_POSITION 0 xyzw 1 NONE float xy
+// TEXCOORD 0 xyzw 2 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s1 t1
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c5, 0.5, -0, 1, 0
+ dcl t0
+ dcl t1
+ dcl_2d s0
+ dcl_2d s1
+ dp2add r0.x, t0, c3, c3.z
+ dp2add r0.y, t0, c4, c4.z
+ add r0.xy, r0, -c1
+ mul r0.w, c1.w, c1.w
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c5.x
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ rcp r0.x, r0.x
+ mul r0.y, r0.x, r0.w
+ frc r0.z, r0.y
+ add r0.z, -r0.z, r0.y
+ mul r1.w, r0.z, c5.x
+ abs r1.x, r1.w
+ frc r1.x, r1.x
+ cmp r1.x, r0.z, r1.x, -r1.x
+ mad r0.x, r0.w, r0.x, -r0.z
+ add r0.z, r1.x, r1.x
+ abs r0.z, r0.z
+ mul r0.z, r0.z, c2.z
+ frc r0.w, -r0.y
+ lrp r1.x, r0.z, r0.w, r0.x
+ lrp r2.x, c2.y, r1.x, r0.y
+ mov r0.w, c1.w
+ mad r0.x, r0.y, -c0.z, -r0.w
+ cmp r0.x, r0.x, c5.y, c5.z
+ mov r2.y, c5.x
+ texld r1, t1, s0
+ texld r2, r2, s1
+ mul r2.xyz, r2.w, r2
+ mul r1, r1, r2
+ mul r0, r0.x, r1
+ mov oC0, r0
+
+// approximately 36 instruction slots used (2 texture, 34 arithmetic)
+ps_4_0
+dcl_constantbuffer cb0[5], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_sampler s1, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xy
+dcl_output o0.xyzw
+dcl_temps 3
+dp2 r0.x, v1.xyxx, cb0[3].xyxx
+add r0.x, r0.x, cb0[3].z
+dp2 r0.z, v1.xyxx, cb0[4].xyxx
+add r0.y, r0.z, cb0[4].z
+add r0.xy, r0.xyxx, -cb0[1].xyxx
+dp2 r0.w, r0.xyxx, r0.xyxx
+mad r0.w, -cb0[1].w, cb0[1].w, r0.w
+mul r0.w, r0.w, l(0.500000)
+mov r0.z, cb0[1].w
+dp3 r0.x, r0.xyzx, cb0[0].xyzx
+div r0.x, r0.w, r0.x
+round_pi r0.y, r0.x
+round_ni r0.z, r0.x
+mul r0.w, r0.z, l(0.500000)
+add r0.yz, -r0.xxzx, r0.yyxy
+ge r1.x, r0.w, -r0.w
+frc r0.w, |r0.w|
+movc r0.w, r1.x, r0.w, -r0.w
+add r0.w, r0.w, r0.w
+mul r1.x, |r0.w|, cb0[2].z
+mad r0.w, -|r0.w|, cb0[2].z, l(1.000000)
+mul r0.y, r0.y, r1.x
+mad r0.y, r0.z, r0.w, r0.y
+mul r0.y, r0.y, cb0[2].y
+add r0.z, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r0.z, r0.y
+mul r0.x, r0.x, cb0[0].z
+ge r0.x, -cb0[1].w, r0.x
+movc r0.x, r0.x, l(-0.000000), l(1.000000)
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
+mul r1.xyz, r1.wwww, r1.xyzx
+sample r2.xyzw, v2.xyxx, t0.xyzw, s0
+mul r1.xyzw, r1.xyzw, r2.xyzw
+mul o0.xyzw, r0.xxxx, r1.xyzw
+ret
+// Approximately 36 instruction slots used
+#endif
+
+const BYTE SampleRadialGradientA0PS[] =
+{
+ 68, 88, 66, 67, 47, 105,
+ 118, 126, 8, 122, 228, 233,
+ 56, 98, 50, 148, 135, 10,
+ 63, 196, 1, 0, 0, 0,
+ 120, 11, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 212, 2, 0, 0, 172, 7,
+ 0, 0, 40, 8, 0, 0,
+ 200, 10, 0, 0, 68, 11,
+ 0, 0, 65, 111, 110, 57,
+ 148, 2, 0, 0, 148, 2,
+ 0, 0, 0, 2, 255, 255,
+ 92, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 5, 0, 15, 160,
+ 0, 0, 0, 63, 0, 0,
+ 0, 128, 0, 0, 128, 63,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 90, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
+ 90, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 228, 176,
+ 4, 0, 228, 160, 4, 0,
+ 170, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 1, 0, 228, 161,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 1, 0, 255, 160, 90, 0,
+ 0, 4, 0, 0, 8, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 0, 0, 255, 129,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 5, 0, 0, 160, 1, 0,
+ 0, 2, 0, 0, 4, 128,
+ 1, 0, 255, 160, 8, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 6, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 0, 3,
+ 0, 0, 2, 128, 0, 0,
+ 0, 128, 0, 0, 255, 128,
+ 19, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 85, 128,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 170, 129,
+ 0, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 160, 35, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 255, 128, 19, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 1, 0, 0, 128,
+ 1, 0, 0, 129, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 0, 0, 170, 129,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 1, 0, 0, 128,
+ 1, 0, 0, 128, 35, 0,
+ 0, 2, 0, 0, 4, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 170, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 85, 129, 18, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 0, 0, 255, 128,
+ 0, 0, 0, 128, 18, 0,
+ 0, 4, 2, 0, 1, 128,
+ 2, 0, 85, 160, 1, 0,
+ 0, 128, 0, 0, 85, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 85, 128,
+ 0, 0, 170, 161, 0, 0,
+ 255, 129, 88, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 85, 160,
+ 5, 0, 170, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 5, 0, 0, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 8, 228, 160,
+ 5, 0, 0, 3, 2, 0,
+ 7, 128, 2, 0, 255, 128,
+ 2, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 208, 4, 0, 0,
+ 64, 0, 0, 0, 52, 1,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 15, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 15, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 54, 0, 0, 6,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 16, 0, 0, 8,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 14, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 66, 0, 0, 5,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 66, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 98, 0, 16, 0,
+ 0, 0, 0, 0, 6, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 86, 4,
+ 16, 0, 0, 0, 0, 0,
+ 29, 0, 0, 8, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 26, 0, 0, 6,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 129, 0, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 10,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 9, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 128, 129, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 128, 193, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 128,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 54, 0, 0, 5,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 36, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 29, 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,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 3, 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,
+ 82, 68, 69, 70, 152, 2,
+ 0, 0, 1, 0, 0, 0,
+ 0, 1, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 100, 2, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 217, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 230, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 246, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 73, 110,
+ 112, 117, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 71,
+ 114, 97, 100, 105, 101, 110,
+ 116, 83, 97, 109, 112, 108,
+ 101, 114, 0, 73, 110, 112,
+ 117, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 71, 114,
+ 97, 100, 105, 101, 110, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 99, 111, 110, 115,
+ 116, 97, 110, 116, 115, 0,
+ 246, 0, 0, 0, 8, 0,
+ 0, 0, 24, 1, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 0, 0,
+ 0, 0, 12, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
+ 0, 0, 0, 0, 0, 0,
+ 240, 1, 0, 0, 16, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 248, 1,
+ 0, 0, 0, 0, 0, 0,
+ 8, 2, 0, 0, 24, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 28, 2, 0, 0, 28, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 36, 2, 0, 0, 32, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 47, 2, 0, 0, 36, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 62, 2, 0, 0, 40, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 48, 0,
+ 0, 0, 28, 0, 0, 0,
+ 2, 0, 0, 0, 84, 2,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 65, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 114, 97, 100, 105,
+ 117, 115, 49, 0, 115, 113,
+ 95, 114, 97, 100, 105, 117,
+ 115, 49, 0, 114, 101, 112,
+ 101, 97, 116, 95, 99, 111,
+ 114, 114, 101, 99, 116, 0,
+ 97, 108, 108, 111, 119, 95,
+ 111, 100, 100, 0, 116, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 171, 171, 3, 0,
+ 3, 0, 3, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 116, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 3, 0, 0, 107, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 3, 0, 0, 83, 86,
+ 95, 80, 79, 83, 73, 84,
+ 73, 79, 78, 0, 83, 67,
+ 69, 78, 69, 95, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 84, 69, 88, 67, 79,
+ 79, 82, 68, 0, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
diff --git a/system/graphics/2d/ShadersD2D1.hlsl b/system/graphics/2d/ShadersD2D1.hlsl
new file mode 100644
index 000000000..42337afc2
--- /dev/null
+++ b/system/graphics/2d/ShadersD2D1.hlsl
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Texture2D InputTexture : register(t0);
+SamplerState InputSampler : register(s0);
+Texture2D GradientTexture : register(t1);
+SamplerState GradientSampler : register(s1);
+
+cbuffer constants : register(b0)
+{
+ // Precalculate as much as we can!
+ float3 diff : packoffset(c0.x);
+ float2 center1 : packoffset(c1.x);
+ float A : packoffset(c1.z);
+ float radius1 : packoffset(c1.w);
+ float sq_radius1 : packoffset(c2.x);
+
+ // The next two values are used for a hack to compensate for an apparent
+ // bug in D2D where the GradientSampler SamplerState doesn't get the
+ // correct addressing modes.
+ float repeat_correct : packoffset(c2.y);
+ float allow_odd : packoffset(c2.z);
+
+ float3x2 transform : packoffset(c3.x);
+}
+
+float4 SampleRadialGradientPS(
+ float4 clipSpaceOutput : SV_POSITION,
+ float4 sceneSpaceOutput : SCENE_POSITION,
+ float4 texelSpaceInput0 : TEXCOORD0
+ ) : SV_Target
+{
+ // Radial gradient painting is defined as the set of circles whose centers
+ // are described by C(t) = (C2 - C1) * t + C1; with radii
+ // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
+ // quadratic equation that arises when calculating t for pixel (x, y).
+ //
+ // A more extensive derrivation can be found in the pixman radial gradient
+ // code.
+
+ float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
+ sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - sq_radius1;
+
+ float det = pow(B, 2) - A * C;
+
+ float sqrt_det = sqrt(abs(det));
+
+ float2 t = (B + float2(sqrt_det, -sqrt_det)) / A;
+
+ float2 isValid = step(float2(-radius1, -radius1), t * diff.z);
+
+ float upper_t = lerp(t.y, t.x, isValid.x);
+
+ // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
+ float oddeven = abs(fmod(floor(upper_t), 2)) * allow_odd;
+
+ // Now let's calculate even or odd addressing in a branchless manner.
+ float upper_t_repeated = ((upper_t - floor(upper_t)) * (1.0f - oddeven)) + ((ceil(upper_t) - upper_t) * oddeven);
+
+ float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t * (1.0f - repeat_correct) + upper_t_repeated * repeat_correct, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);
+
+ // In order to compile for PS_4_0_level_9_3 we need to be branchless.
+ // This is essentially returning nothing, i.e. bailing early if:
+ // det < 0 || max(isValid.x, isValid.y) <= 0
+ return output * abs(step(max(isValid.x, isValid.y), 0) - 1.0f) * step(0, det);
+};
+
+float4 SampleRadialGradientA0PS(
+ float4 clipSpaceOutput : SV_POSITION,
+ float4 sceneSpaceOutput : SCENE_POSITION,
+ float4 texelSpaceInput0 : TEXCOORD0
+ ) : SV_Target
+{
+ // This simpler shader is used for the degenerate case where A is 0,
+ // i.e. we're actually solving a linear equation.
+
+ float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
+ sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);
+
+ float t = 0.5 * C / B;
+
+ // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
+ float oddeven = abs(fmod(floor(t), 2)) * allow_odd;
+
+ // Now let's calculate even or odd addressing in a branchless manner.
+ float t_repeated = ((t - floor(t)) * (1.0f - oddeven)) + ((ceil(t) - t) * oddeven);
+
+ float4 output = GradientTexture.Sample(GradientSampler, float2(t * (1.0f - repeat_correct) + t_repeated * repeat_correct, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);
+
+ // In order to compile for PS_4_0_level_9_3 we need to be branchless.
+ // This is essentially returning nothing, i.e. bailing early if:
+ // -radius1 >= t * diff.z
+ return output * abs(step(t * diff.z, -radius1) - 1.0f);
+};
+
diff --git a/system/graphics/2d/SourceSurfaceCairo.cpp b/system/graphics/2d/SourceSurfaceCairo.cpp
new file mode 100644
index 000000000..ba8498b84
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceCairo.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SourceSurfaceCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
+#include "DataSourceSurfaceWrapper.h"
+
+#include "cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+static SurfaceFormat
+CairoFormatToSurfaceFormat(cairo_format_t format)
+{
+ switch (format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ return SurfaceFormat::B8G8R8A8;
+ case CAIRO_FORMAT_RGB24:
+ return SurfaceFormat::B8G8R8X8;
+ case CAIRO_FORMAT_RGB16_565:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case CAIRO_FORMAT_A8:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+SourceSurfaceCairo::SourceSurfaceCairo(cairo_surface_t* aSurface,
+ const IntSize& aSize,
+ const SurfaceFormat& aFormat,
+ DrawTargetCairo* aDrawTarget /* = nullptr */)
+ : mSize(aSize)
+ , mFormat(aFormat)
+ , mSurface(aSurface)
+ , mDrawTarget(aDrawTarget)
+{
+ cairo_surface_reference(mSurface);
+}
+
+SourceSurfaceCairo::~SourceSurfaceCairo()
+{
+ cairo_surface_destroy(mSurface);
+}
+
+IntSize
+SourceSurfaceCairo::GetSize() const
+{
+ return mSize;
+}
+
+SurfaceFormat
+SourceSurfaceCairo::GetFormat() const
+{
+ return mFormat;
+}
+
+already_AddRefed<DataSourceSurface>
+SourceSurfaceCairo::GetDataSurface()
+{
+ RefPtr<DataSourceSurface> dataSurf;
+
+ if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ dataSurf = new DataSourceSurfaceCairo(mSurface);
+ } else {
+ cairo_surface_t* imageSurf = cairo_image_surface_create(GfxFormatToCairoFormat(mFormat),
+ mSize.width, mSize.height);
+
+ // Fill the new image surface with the contents of our surface.
+ cairo_t* ctx = cairo_create(imageSurf);
+ cairo_set_source_surface(ctx, mSurface, 0, 0);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ dataSurf = new DataSourceSurfaceCairo(imageSurf);
+ cairo_surface_destroy(imageSurf);
+ }
+
+ // We also need to make sure that the returned surface has
+ // surface->GetType() == SurfaceType::DATA.
+ return MakeAndAddRef<DataSourceSurfaceWrapper>(dataSurf);
+}
+
+cairo_surface_t*
+SourceSurfaceCairo::GetSurface() const
+{
+ return mSurface;
+}
+
+void
+SourceSurfaceCairo::DrawTargetWillChange()
+{
+ if (mDrawTarget) {
+ mDrawTarget = nullptr;
+
+ // We're about to lose our version of the surface, so make a copy of it.
+ cairo_surface_t* surface = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(mFormat),
+ mSize.width, mSize.height);
+ cairo_t* ctx = cairo_create(surface);
+ cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
+ cairo_set_source(ctx, pat);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+ cairo_pattern_destroy(pat);
+
+ // Swap in this new surface.
+ cairo_surface_destroy(mSurface);
+ mSurface = surface;
+ }
+}
+
+DataSourceSurfaceCairo::DataSourceSurfaceCairo(cairo_surface_t* imageSurf)
+ : mImageSurface(imageSurf)
+{
+ cairo_surface_reference(mImageSurface);
+}
+
+DataSourceSurfaceCairo::~DataSourceSurfaceCairo()
+{
+ cairo_surface_destroy(mImageSurface);
+}
+
+unsigned char *
+DataSourceSurfaceCairo::GetData()
+{
+ return cairo_image_surface_get_data(mImageSurface);
+}
+
+int32_t
+DataSourceSurfaceCairo::Stride()
+{
+ return cairo_image_surface_get_stride(mImageSurface);
+}
+
+IntSize
+DataSourceSurfaceCairo::GetSize() const
+{
+ IntSize size;
+ size.width = cairo_image_surface_get_width(mImageSurface);
+ size.height = cairo_image_surface_get_height(mImageSurface);
+
+ return size;
+}
+
+SurfaceFormat
+DataSourceSurfaceCairo::GetFormat() const
+{
+ return CairoFormatToSurfaceFormat(cairo_image_surface_get_format(mImageSurface));
+}
+
+cairo_surface_t*
+DataSourceSurfaceCairo::GetSurface() const
+{
+ return mImageSurface;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/SourceSurfaceCairo.h b/system/graphics/2d/SourceSurfaceCairo.h
new file mode 100644
index 000000000..428c75957
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceCairo.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
+#define _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetCairo;
+
+class SourceSurfaceCairo : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCairo)
+ // Create a SourceSurfaceCairo. The surface will not be copied, but simply
+ // referenced.
+ // If aDrawTarget is non-nullptr, it is assumed that this is a snapshot source
+ // surface, and we'll call DrawTargetCairo::RemoveSnapshot(this) on it when
+ // we're destroyed.
+ SourceSurfaceCairo(cairo_surface_t* aSurface, const IntSize& aSize,
+ const SurfaceFormat& aFormat,
+ DrawTargetCairo* aDrawTarget = nullptr);
+ virtual ~SourceSurfaceCairo();
+
+ virtual SurfaceType GetType() const { return SurfaceType::CAIRO; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface();
+
+ cairo_surface_t* GetSurface() const;
+
+private: // methods
+ friend class DrawTargetCairo;
+ void DrawTargetWillChange();
+
+private: // data
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ cairo_surface_t* mSurface;
+ DrawTargetCairo* mDrawTarget;
+};
+
+class DataSourceSurfaceCairo : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCairo)
+ explicit DataSourceSurfaceCairo(cairo_surface_t* imageSurf);
+ virtual ~DataSourceSurfaceCairo();
+ virtual unsigned char *GetData();
+ virtual int32_t Stride();
+
+ virtual SurfaceType GetType() const { return SurfaceType::CAIRO_IMAGE; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+
+ cairo_surface_t* GetSurface() const;
+
+private:
+ cairo_surface_t* mImageSurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
diff --git a/system/graphics/2d/SourceSurfaceD2D1.cpp b/system/graphics/2d/SourceSurfaceD2D1.cpp
new file mode 100644
index 000000000..92b2e4d1d
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceD2D1.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SourceSurfaceD2D1.h"
+#include "DrawTargetD2D1.h"
+#include "Tools.h"
+
+namespace mozilla {
+namespace gfx {
+
+SourceSurfaceD2D1::SourceSurfaceD2D1(ID2D1Image *aImage, ID2D1DeviceContext *aDC,
+ SurfaceFormat aFormat, const IntSize &aSize,
+ DrawTargetD2D1 *aDT)
+ : mImage(aImage)
+ , mDC(aDC)
+ , mDevice(Factory::GetD2D1Device())
+ , mDrawTarget(aDT)
+{
+ aImage->QueryInterface((ID2D1Bitmap1**)getter_AddRefs(mRealizedBitmap));
+
+ mFormat = aFormat;
+ mSize = aSize;
+}
+
+SourceSurfaceD2D1::~SourceSurfaceD2D1()
+{
+}
+
+bool
+SourceSurfaceD2D1::IsValid() const
+{
+ return mDevice == Factory::GetD2D1Device();
+}
+
+already_AddRefed<DataSourceSurface>
+SourceSurfaceD2D1::GetDataSurface()
+{
+ HRESULT hr;
+
+ if (!EnsureRealizedBitmap()) {
+ gfxCriticalError() << "Failed to realize a bitmap, device " << hexa(mDevice);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1Bitmap1> softwareBitmap;
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_CANNOT_DRAW |
+ D2D1_BITMAP_OPTIONS_CPU_READ;
+ hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(softwareBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to create software bitmap: " << mSize << " Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ D2D1_POINT_2U point = D2D1::Point2U(0, 0);
+ D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
+
+ hr = softwareBitmap->CopyFromBitmap(&point, mRealizedBitmap, &rect);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to readback into software bitmap. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<DataSourceSurfaceD2D1>(softwareBitmap, mFormat);
+}
+
+bool
+SourceSurfaceD2D1::EnsureRealizedBitmap()
+{
+ if (mRealizedBitmap) {
+ return true;
+ }
+
+ // Why aren't we using mDevice here or anywhere else?
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<ID2D1DeviceContext> dc;
+ device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, getter_AddRefs(dc));
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ dc->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(mRealizedBitmap));
+
+ dc->SetTarget(mRealizedBitmap);
+
+ dc->BeginDraw();
+ dc->DrawImage(mImage);
+ dc->EndDraw();
+
+ return true;
+}
+
+void
+SourceSurfaceD2D1::DrawTargetWillChange()
+{
+ // At this point in time this should always be true here.
+ MOZ_ASSERT(mRealizedBitmap);
+
+ RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap;
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(mRealizedBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to create bitmap to make DrawTarget copy. Size: " << mSize << " Code: " << hexa(hr);
+ MarkIndependent();
+ return;
+ }
+
+ D2D1_POINT_2U point = D2D1::Point2U(0, 0);
+ D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
+ mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect);
+ mImage = mRealizedBitmap;
+
+ DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat);
+
+ // We now no longer depend on the source surface content remaining the same.
+ MarkIndependent();
+}
+
+void
+SourceSurfaceD2D1::MarkIndependent()
+{
+ if (mDrawTarget) {
+ MOZ_ASSERT(mDrawTarget->mSnapshot == this);
+ mDrawTarget->mSnapshot = nullptr;
+ mDrawTarget = nullptr;
+ }
+}
+
+DataSourceSurfaceD2D1::DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat)
+ : mBitmap(aMappableBitmap)
+ , mFormat(aFormat)
+ , mMapped(false)
+{
+}
+
+DataSourceSurfaceD2D1::~DataSourceSurfaceD2D1()
+{
+ if (mMapped) {
+ mBitmap->Unmap();
+ }
+}
+
+IntSize
+DataSourceSurfaceD2D1::GetSize() const
+{
+ D2D1_SIZE_F size = mBitmap->GetSize();
+
+ return IntSize(int32_t(size.width), int32_t(size.height));
+}
+
+uint8_t*
+DataSourceSurfaceD2D1::GetData()
+{
+ EnsureMapped();
+
+ return mMap.bits;
+}
+
+bool
+DataSourceSurfaceD2D1::Map(MapType aMapType, MappedSurface *aMappedSurface)
+{
+ // DataSourceSurfaces used with the new Map API should not be used with GetData!!
+ MOZ_ASSERT(!mMapped);
+ MOZ_ASSERT(!mIsMapped);
+
+ D2D1_MAP_OPTIONS options;
+ if (aMapType == MapType::READ) {
+ options = D2D1_MAP_OPTIONS_READ;
+ } else {
+ gfxWarning() << "Attempt to map D2D1 DrawTarget for writing.";
+ return false;
+ }
+
+ D2D1_MAPPED_RECT map;
+ if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) {
+ gfxCriticalError() << "Failed to map bitmap (M).";
+ return false;
+ }
+ aMappedSurface->mData = map.bits;
+ aMappedSurface->mStride = map.pitch;
+
+ mIsMapped = !!aMappedSurface->mData;
+ return mIsMapped;
+}
+
+void
+DataSourceSurfaceD2D1::Unmap()
+{
+ MOZ_ASSERT(mIsMapped);
+
+ mIsMapped = false;
+ mBitmap->Unmap();
+}
+
+int32_t
+DataSourceSurfaceD2D1::Stride()
+{
+ EnsureMapped();
+
+ return mMap.pitch;
+}
+
+void
+DataSourceSurfaceD2D1::EnsureMapped()
+{
+ // Do not use GetData() after having used Map!
+ MOZ_ASSERT(!mIsMapped);
+ if (mMapped) {
+ return;
+ }
+ if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &mMap))) {
+ gfxCriticalError() << "Failed to map bitmap (EM).";
+ return;
+ }
+ mMapped = true;
+}
+
+}
+}
diff --git a/system/graphics/2d/SourceSurfaceD2D1.h b/system/graphics/2d/SourceSurfaceD2D1.h
new file mode 100644
index 000000000..5b7cedf4c
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceD2D1.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SOURCESURFACED2D1_H_
+#define MOZILLA_GFX_SOURCESURFACED2D1_H_
+
+#include "2D.h"
+#include "HelpersD2D.h"
+#include <vector>
+#include <d3d11.h>
+#include <d2d1_1.h>
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetD2D1;
+
+class SourceSurfaceD2D1 : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D1)
+ SourceSurfaceD2D1(ID2D1Image* aImage, ID2D1DeviceContext *aDC,
+ SurfaceFormat aFormat, const IntSize &aSize,
+ DrawTargetD2D1 *aDT = nullptr);
+ ~SourceSurfaceD2D1();
+
+ virtual SurfaceType GetType() const { return SurfaceType::D2D1_1_IMAGE; }
+ virtual IntSize GetSize() const { return mSize; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+ virtual bool IsValid() const;
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface();
+
+ ID2D1Image *GetImage() { return mImage; }
+
+ void EnsureIndependent() { if (!mDrawTarget) return; DrawTargetWillChange(); }
+
+private:
+ friend class DrawTargetD2D1;
+
+ bool EnsureRealizedBitmap();
+
+ // This function is called by the draw target this texture belongs to when
+ // it is about to be changed. The texture will be required to make a copy
+ // of itself when this happens.
+ void DrawTargetWillChange();
+
+ // This will mark the surface as no longer depending on its drawtarget,
+ // this may happen on destruction or copying.
+ void MarkIndependent();
+
+ RefPtr<ID2D1Image> mImage;
+ // This may be null if we were created for a non-bitmap image and have not
+ // had a reason yet to realize ourselves.
+ RefPtr<ID2D1Bitmap1> mRealizedBitmap;
+ RefPtr<ID2D1DeviceContext> mDC;
+ // Keep this around to verify whether out image is still valid in the future.
+ RefPtr<ID2D1Device> mDevice;
+
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ DrawTargetD2D1* mDrawTarget;
+};
+
+class DataSourceSurfaceD2D1 : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D1)
+ DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat);
+ ~DataSourceSurfaceD2D1();
+
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+ virtual bool IsValid() const { return !!mBitmap; }
+ virtual uint8_t *GetData();
+ virtual int32_t Stride();
+ virtual bool Map(MapType, MappedSurface *aMappedSurface);
+ virtual void Unmap();
+
+private:
+ friend class SourceSurfaceD2DTarget;
+ void EnsureMapped();
+
+ mutable RefPtr<ID2D1Bitmap1> mBitmap;
+ SurfaceFormat mFormat;
+ D2D1_MAPPED_RECT mMap;
+ bool mMapped;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SOURCESURFACED2D2TARGET_H_ */
diff --git a/system/graphics/2d/SourceSurfaceDual.h b/system/graphics/2d/SourceSurfaceDual.h
new file mode 100644
index 000000000..df81b87f2
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceDual.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SOURCESURFACEDUAL_H_
+#define MOZILLA_GFX_SOURCESURFACEDUAL_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DualSurface;
+class DualPattern;
+
+class SourceSurfaceDual : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceDual)
+ SourceSurfaceDual(DrawTarget *aDTA, DrawTarget *aDTB)
+ : mA(aDTA->Snapshot())
+ , mB(aDTB->Snapshot())
+ { }
+
+ virtual SurfaceType GetType() const { return SurfaceType::DUAL_DT; }
+ virtual IntSize GetSize() const { return mA->GetSize(); }
+ virtual SurfaceFormat GetFormat() const { return mA->GetFormat(); }
+
+ // This is implemented for debugging purposes only (used by dumping
+ // client-side textures for paint dumps), for which we don't care about
+ // component alpha, so we just use the first of the two surfaces.
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() {
+ return mA->GetDataSurface();
+ }
+private:
+ friend class DualSurface;
+ friend class DualPattern;
+
+ RefPtr<SourceSurface> mA;
+ RefPtr<SourceSurface> mB;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACEDUAL_H_ */
diff --git a/system/graphics/2d/SourceSurfaceRawData.cpp b/system/graphics/2d/SourceSurfaceRawData.cpp
new file mode 100644
index 000000000..9d95e9900
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceRawData.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SourceSurfaceRawData.h"
+
+#include "DataSurfaceHelpers.h"
+#include "Logging.h"
+#include "mozilla/Types.h" // for decltype
+
+namespace mozilla {
+namespace gfx {
+
+void
+SourceSurfaceRawData::InitWrappingData(uint8_t *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ Factory::SourceSurfaceDeallocator aDeallocator,
+ void* aClosure)
+{
+ mRawData = aData;
+ mSize = aSize;
+ mStride = aStride;
+ mFormat = aFormat;
+
+ if (aDeallocator) {
+ mOwnData = true;
+ }
+ mDeallocator = aDeallocator;
+ mClosure = aClosure;
+}
+
+void
+SourceSurfaceRawData::GuaranteePersistance()
+{
+ if (mOwnData) {
+ return;
+ }
+
+ uint8_t* oldData = mRawData;
+ mRawData = new uint8_t[mStride * mSize.height];
+
+ memcpy(mRawData, oldData, mStride * mSize.height);
+ mOwnData = true;
+}
+
+bool
+SourceSurfaceAlignedRawData::Init(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aClearMem,
+ uint8_t aClearValue,
+ int32_t aStride)
+{
+ mFormat = aFormat;
+ mStride = aStride ? aStride : GetAlignedStride<16>(aSize.width, BytesPerPixel(aFormat));
+
+ size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
+ if (bufLen > 0) {
+ bool zeroMem = aClearMem && !aClearValue;
+ static_assert(sizeof(decltype(mArray[0])) == 1,
+ "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
+
+ // AlignedArray uses cmalloc to zero mem for a fast path.
+ mArray.Realloc(/* actually an object count */ bufLen, zeroMem);
+ mSize = aSize;
+
+ if (mArray && aClearMem && aClearValue) {
+ memset(mArray, aClearValue, mStride * aSize.height);
+ }
+ } else {
+ mArray.Dealloc();
+ mSize.SizeTo(0, 0);
+ }
+
+ return mArray != nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/SourceSurfaceRawData.h b/system/graphics/2d/SourceSurfaceRawData.h
new file mode 100644
index 000000000..f4c707198
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceRawData.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SOURCESURFACERAWDATA_H_
+#define MOZILLA_GFX_SOURCESURFACERAWDATA_H_
+
+#include "2D.h"
+#include "Tools.h"
+#include "mozilla/Atomics.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceRawData : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceRawData, override)
+
+ SourceSurfaceRawData()
+ : mRawData(0)
+ , mStride(0)
+ , mFormat(SurfaceFormat::UNKNOWN)
+ , mMapCount(0)
+ , mOwnData(false)
+ , mDeallocator(nullptr)
+ , mClosure(nullptr)
+ {
+ }
+
+ virtual ~SourceSurfaceRawData()
+ {
+ if (mDeallocator) {
+ mDeallocator(mClosure);
+ } else if (mOwnData) {
+ // The buffer is created from GuaranteePersistance().
+ delete [] mRawData;
+ }
+
+ MOZ_ASSERT(mMapCount == 0);
+ }
+
+ virtual uint8_t *GetData() override { return mRawData; }
+ virtual int32_t Stride() override { return mStride; }
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const override { return mSize; }
+ virtual SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual void GuaranteePersistance() override;
+
+ // Althought Map (and Moz2D in general) isn't normally threadsafe,
+ // we want to allow it for SourceSurfaceRawData since it should
+ // always be fine (for reading at least).
+ //
+ // This is the same as the base class implementation except using
+ // mMapCount instead of mIsMapped since that breaks for multithread.
+ //
+ // Once mfbt supports Monitors we should implement proper read/write
+ // locking to prevent write races.
+ virtual bool Map(MapType, MappedSurface *aMappedSurface) override
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ bool success = !!aMappedSurface->mData;
+ if (success) {
+ mMapCount++;
+ }
+ return success;
+ }
+
+ virtual void Unmap() override
+ {
+ mMapCount--;
+ MOZ_ASSERT(mMapCount >= 0);
+ }
+
+private:
+ friend class Factory;
+
+ // If we have a custom deallocator, the |aData| will be released using the
+ // custom deallocator and |aClosure| in dtor. The assumption is that the
+ // caller will check for valid size and stride before making this call.
+ void InitWrappingData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ Factory::SourceSurfaceDeallocator aDeallocator,
+ void* aClosure);
+
+ uint8_t *mRawData;
+ int32_t mStride;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ Atomic<int32_t> mMapCount;
+
+ bool mOwnData;
+ Factory::SourceSurfaceDeallocator mDeallocator;
+ void* mClosure;
+};
+
+class SourceSurfaceAlignedRawData : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceAlignedRawData, override)
+ SourceSurfaceAlignedRawData()
+ : mStride(0)
+ , mFormat(SurfaceFormat::UNKNOWN)
+ , mMapCount(0)
+ {}
+ ~SourceSurfaceAlignedRawData()
+ {
+ MOZ_ASSERT(mMapCount == 0);
+ }
+
+ virtual uint8_t* GetData() override { return mArray; }
+ virtual int32_t Stride() override { return mStride; }
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const override { return mSize; }
+ virtual SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual bool Map(MapType, MappedSurface *aMappedSurface) override
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ bool success = !!aMappedSurface->mData;
+ if (success) {
+ mMapCount++;
+ }
+ return success;
+ }
+
+ virtual void Unmap() override
+ {
+ mMapCount--;
+ MOZ_ASSERT(mMapCount >= 0);
+ }
+
+private:
+ friend class Factory;
+
+ bool Init(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aClearMem,
+ uint8_t aClearValue,
+ int32_t aStride = 0);
+
+ AlignedArray<uint8_t> mArray;
+ int32_t mStride;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ Atomic<int32_t> mMapCount;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACERAWDATA_H_ */
diff --git a/system/graphics/2d/SourceSurfaceSkia.cpp b/system/graphics/2d/SourceSurfaceSkia.cpp
new file mode 100644
index 000000000..0738f3d39
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceSkia.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "Logging.h"
+#include "SourceSurfaceSkia.h"
+#include "HelpersSkia.h"
+#include "DrawTargetSkia.h"
+#include "DataSurfaceHelpers.h"
+#include "skia/include/core/SkData.h"
+#include "mozilla/CheckedInt.h"
+
+namespace mozilla {
+namespace gfx {
+
+SourceSurfaceSkia::SourceSurfaceSkia()
+ : mDrawTarget(nullptr)
+{
+}
+
+SourceSurfaceSkia::~SourceSurfaceSkia()
+{
+ if (mDrawTarget) {
+ mDrawTarget->SnapshotDestroyed();
+ mDrawTarget = nullptr;
+ }
+}
+
+IntSize
+SourceSurfaceSkia::GetSize() const
+{
+ return mSize;
+}
+
+SurfaceFormat
+SourceSurfaceSkia::GetFormat() const
+{
+ return mFormat;
+}
+
+sk_sp<SkImage>
+SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) {
+ // If we were provided a lock object, we can let the caller access
+ // a shared SkImage and we know it won't go away while the lock is held.
+ if (aLock) {
+ // We are locked, so now we can check mDrawTarget.
+ // If it's null, then we're not shared and we can unlock eagerly.
+ if (!mDrawTarget) {
+ aLock->reset();
+ }
+ } else {
+ // Otherwise we need to call DrawTargetWillChange to ensure we have our
+ // own copy of the SkImage.
+ DrawTargetWillChange();
+ }
+ sk_sp<SkImage> image = mImage;
+ return image;
+}
+
+static sk_sp<SkData>
+MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
+{
+ CheckedInt<size_t> size = aStride;
+ size *= aSize.height;
+ if (size.isValid()) {
+ void* mem = sk_malloc_flags(size.value(), 0);
+ if (mem) {
+ if (aData) {
+ memcpy(mem, aData, size.value());
+ }
+ return SkData::MakeFromMalloc(mem, size.value());
+ }
+ }
+ return nullptr;
+}
+
+bool
+SourceSurfaceSkia::InitFromData(unsigned char* aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat)
+{
+ sk_sp<SkData> data = MakeSkData(aData, aSize, aStride);
+ if (!data) {
+ return false;
+ }
+
+ SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
+ mImage = SkImage::MakeRasterData(info, data, aStride);
+ if (!mImage) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mStride = aStride;
+ return true;
+}
+
+bool
+SourceSurfaceSkia::InitFromImage(sk_sp<SkImage> aImage,
+ SurfaceFormat aFormat,
+ DrawTargetSkia* aOwner)
+{
+ if (!aImage) {
+ return false;
+ }
+
+ mSize = IntSize(aImage->width(), aImage->height());
+
+ // For the raster image case, we want to use the format and stride
+ // information that the underlying raster image is using, which is
+ // reliable.
+ // For the GPU case (for which peekPixels is false), we can't easily
+ // figure this information out. It is better to report the originally
+ // intended format and stride that we will convert to if this GPU
+ // image is ever read back into a raster image.
+ SkPixmap pixmap;
+ if (aImage->peekPixels(&pixmap)) {
+ mFormat =
+ aFormat != SurfaceFormat::UNKNOWN ?
+ aFormat :
+ SkiaColorTypeToGfxFormat(pixmap.colorType(), pixmap.alphaType());
+ mStride = pixmap.rowBytes();
+ } else if (aFormat != SurfaceFormat::UNKNOWN) {
+ mFormat = aFormat;
+ SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
+ mStride = SkAlign4(info.minRowBytes());
+ } else {
+ return false;
+ }
+
+ mImage = aImage;
+
+ if (aOwner) {
+ mDrawTarget = aOwner;
+ }
+
+ return true;
+}
+
+uint8_t*
+SourceSurfaceSkia::GetData()
+{
+#ifdef USE_SKIA_GPU
+ if (mImage->isTextureBacked()) {
+ sk_sp<SkImage> raster;
+ if (sk_sp<SkData> data = MakeSkData(nullptr, mSize, mStride)) {
+ SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
+ if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
+ raster = SkImage::MakeRasterData(info, data, mStride);
+ }
+ }
+ if (raster) {
+ mImage = raster;
+ } else {
+ gfxCriticalError() << "Failed making Skia raster image for GPU surface";
+ }
+ }
+#endif
+ SkPixmap pixmap;
+ if (!mImage->peekPixels(&pixmap)) {
+ gfxCriticalError() << "Failed accessing pixels for Skia raster image";
+ }
+ return reinterpret_cast<uint8_t*>(pixmap.writable_addr());
+}
+
+void
+SourceSurfaceSkia::DrawTargetWillChange()
+{
+ if (mDrawTarget) {
+ // Raster snapshots do not use Skia's internal copy-on-write mechanism,
+ // so we need to do an explicit copy here.
+ // GPU snapshots, for which peekPixels is false, will already be dealt
+ // with automatically via the internal copy-on-write mechanism, so we
+ // don't need to do anything for them here.
+ SkPixmap pixmap;
+ if (mImage->peekPixels(&pixmap)) {
+ mImage = SkImage::MakeRasterCopy(pixmap);
+ if (!mImage) {
+ gfxCriticalError() << "Failed copying Skia raster snapshot";
+ }
+ }
+ mDrawTarget = nullptr;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/2d/SourceSurfaceSkia.h b/system/graphics/2d/SourceSurfaceSkia.h
new file mode 100644
index 000000000..ffb8e47bf
--- /dev/null
+++ b/system/graphics/2d/SourceSurfaceSkia.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SOURCESURFACESKIA_H_
+#define MOZILLA_GFX_SOURCESURFACESKIA_H_
+
+#include "2D.h"
+#include <vector>
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/core/SkImage.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+
+typedef mozilla::MutexAutoLock MutexAutoLock;
+
+namespace mozilla {
+
+namespace gfx {
+
+class DrawTargetSkia;
+
+class SourceSurfaceSkia : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceSkia)
+ SourceSurfaceSkia();
+ ~SourceSurfaceSkia();
+
+ virtual SurfaceType GetType() const { return SurfaceType::SKIA; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+
+ sk_sp<SkImage> GetImage(Maybe<MutexAutoLock>* aLock);
+
+ bool InitFromData(unsigned char* aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat);
+
+ bool InitFromImage(sk_sp<SkImage> aImage,
+ SurfaceFormat aFormat = SurfaceFormat::UNKNOWN,
+ DrawTargetSkia* aOwner = nullptr);
+
+ virtual uint8_t* GetData();
+
+ virtual int32_t Stride() { return mStride; }
+
+private:
+ friend class DrawTargetSkia;
+
+ void DrawTargetWillChange();
+
+ sk_sp<SkImage> mImage;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ int32_t mStride;
+ RefPtr<DrawTargetSkia> mDrawTarget;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACESKIA_H_ */
diff --git a/system/graphics/2d/StackArray.h b/system/graphics/2d/StackArray.h
new file mode 100644
index 000000000..a2451f930
--- /dev/null
+++ b/system/graphics/2d/StackArray.h
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A handy class that will allocate data for size*T objects on the stack and
+ * otherwise allocate them on the heap. It is similar in purpose to AutoTArray */
+
+template <class T, size_t size>
+class StackArray
+{
+public:
+ StackArray(size_t count) {
+ if (count > size) {
+ mData = new T[count];
+ } else {
+ mData = mStackData;
+ }
+ }
+ ~StackArray() {
+ if (mData != mStackData) {
+ delete[] mData;
+ }
+ }
+ T& operator[](size_t n) { return mData[n]; }
+ const T& operator[](size_t n) const { return mData[n]; }
+ T* data() { return mData; };
+private:
+ T mStackData[size];
+ T* mData;
+};
diff --git a/system/graphics/2d/Tools.h b/system/graphics/2d/Tools.h
new file mode 100644
index 000000000..585cbbb2c
--- /dev/null
+++ b/system/graphics/2d/Tools.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TOOLS_H_
+#define MOZILLA_GFX_TOOLS_H_
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "Types.h"
+#include "Point.h"
+#include <math.h>
+
+namespace mozilla {
+namespace gfx {
+
+static inline bool
+IsOperatorBoundByMask(CompositionOp aOp) {
+ switch (aOp) {
+ case CompositionOp::OP_IN:
+ case CompositionOp::OP_OUT:
+ case CompositionOp::OP_DEST_IN:
+ case CompositionOp::OP_DEST_ATOP:
+ case CompositionOp::OP_SOURCE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+template <class T>
+struct ClassStorage
+{
+ char bytes[sizeof(T)];
+
+ const T *addr() const { return (const T *)bytes; }
+ T *addr() { return (T *)(void *)bytes; }
+};
+
+static inline bool
+FuzzyEqual(Float aA, Float aB, Float aErr)
+{
+ if ((aA + aErr >= aB) && (aA - aErr <= aB)) {
+ return true;
+ }
+ return false;
+}
+
+static inline void
+NudgeToInteger(float *aVal)
+{
+ float r = floorf(*aVal + 0.5f);
+ // The error threshold should be proportional to the rounded value. This
+ // bounds the relative error introduced by the nudge operation. However,
+ // when the rounded value is 0, the error threshold can't be proportional
+ // to the rounded value (we'd never round), so we just choose the same
+ // threshold as for a rounded value of 1.
+ if (FuzzyEqual(r, *aVal, r == 0.0f ? 1e-6f : fabs(r*1e-6f))) {
+ *aVal = r;
+ }
+}
+
+static inline void
+NudgeToInteger(float *aVal, float aErr)
+{
+ float r = floorf(*aVal + 0.5f);
+ if (FuzzyEqual(r, *aVal, aErr)) {
+ *aVal = r;
+ }
+}
+
+static inline Float
+Distance(Point aA, Point aB)
+{
+ return hypotf(aB.x - aA.x, aB.y - aA.y);
+}
+
+static inline int
+BytesPerPixel(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8:
+ return 1;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return 2;
+ case SurfaceFormat::R8G8B8:
+ case SurfaceFormat::B8G8R8:
+ return 3;
+ case SurfaceFormat::HSV:
+ case SurfaceFormat::Lab:
+ return 3 * sizeof(float);
+ case SurfaceFormat::Depth:
+ return sizeof(uint16_t);
+ default:
+ return 4;
+ }
+}
+
+static inline bool
+IsOpaqueFormat(SurfaceFormat aFormat) {
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ case SurfaceFormat::X8R8G8B8:
+ case SurfaceFormat::YUV:
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::YUV422:
+ case SurfaceFormat::R5G6B5_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template<typename T, int alignment = 16>
+struct AlignedArray
+{
+ typedef T value_type;
+
+ AlignedArray()
+ : mPtr(nullptr)
+ , mStorage(nullptr)
+ {
+ }
+
+ explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false)
+ : mStorage(nullptr)
+ , mCount(0)
+ {
+ Realloc(aCount, aZero);
+ }
+
+ MOZ_ALWAYS_INLINE ~AlignedArray()
+ {
+ Dealloc();
+ }
+
+ void Dealloc()
+ {
+ // If we fail this assert we'll need to uncomment the loop below to make
+ // sure dtors are properly invoked. If we do that, we should check that the
+ // comment about compiler dead code elimination is in fact true for all the
+ // compilers that we care about.
+ static_assert(mozilla::IsPod<T>::value,
+ "Destructors must be invoked for this type");
+#if 0
+ for (size_t i = 0; i < mCount; ++i) {
+ // Since we used the placement |operator new| function to construct the
+ // elements of this array we need to invoke their destructors manually.
+ // For types where the destructor does nothing the compiler's dead code
+ // elimination step should optimize this loop away.
+ mPtr[i].~T();
+ }
+#endif
+
+ free(mStorage);
+ mStorage = nullptr;
+ mPtr = nullptr;
+ }
+
+ MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false)
+ {
+ free(mStorage);
+ CheckedInt32 storageByteCount =
+ CheckedInt32(sizeof(T)) * aCount + (alignment - 1);
+ if (!storageByteCount.isValid()) {
+ mStorage = nullptr;
+ mPtr = nullptr;
+ mCount = 0;
+ return;
+ }
+ // We don't create an array of T here, since we don't want ctors to be
+ // invoked at the wrong places if we realign below.
+ if (aZero) {
+ // calloc can be more efficient than new[] for large chunks,
+ // so we use calloc/malloc/free for everything.
+ mStorage = static_cast<uint8_t *>(calloc(1, storageByteCount.value()));
+ } else {
+ mStorage = static_cast<uint8_t *>(malloc(storageByteCount.value()));
+ }
+ if (!mStorage) {
+ mStorage = nullptr;
+ mPtr = nullptr;
+ mCount = 0;
+ return;
+ }
+ if (uintptr_t(mStorage) % alignment) {
+ // Our storage does not start at a <alignment>-byte boundary. Make sure mPtr does!
+ mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment));
+ } else {
+ mPtr = (T*)(mStorage);
+ }
+ // Now that mPtr is pointing to the aligned position we can use placement
+ // |operator new| to invoke any ctors at the correct positions. For types
+ // that have a no-op default constructor the compiler's dead code
+ // elimination step should optimize this away.
+ mPtr = new (mPtr) T[aCount];
+ mCount = aCount;
+ }
+
+ void Swap(AlignedArray<T, alignment>& aOther)
+ {
+ mozilla::Swap(mPtr, aOther.mPtr);
+ mozilla::Swap(mStorage, aOther.mStorage);
+ mozilla::Swap(mCount, aOther.mCount);
+ }
+
+ MOZ_ALWAYS_INLINE operator T*()
+ {
+ return mPtr;
+ }
+
+ T *mPtr;
+
+private:
+ uint8_t *mStorage;
+ size_t mCount;
+};
+
+/**
+ * Returns aWidth * aBytesPerPixel increased, if necessary, so that it divides
+ * exactly into |alignment|.
+ *
+ * Note that currently |alignment| must be a power-of-2. If for some reason we
+ * want to support NPOT alignment we can revert back to this functions old
+ * implementation.
+ */
+template<int alignment>
+int32_t GetAlignedStride(int32_t aWidth, int32_t aBytesPerPixel)
+{
+ static_assert(alignment > 0 && (alignment & (alignment-1)) == 0,
+ "This implementation currently require power-of-two alignment");
+ const int32_t mask = alignment - 1;
+ CheckedInt32 stride = CheckedInt32(aWidth) * CheckedInt32(aBytesPerPixel) + CheckedInt32(mask);
+ if (stride.isValid()) {
+ return stride.value() & ~mask;
+ }
+ return 0;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TOOLS_H_ */
diff --git a/system/graphics/2d/Triangle.h b/system/graphics/2d/Triangle.h
new file mode 100644
index 000000000..c667abe47
--- /dev/null
+++ b/system/graphics/2d/Triangle.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TRIANGLE_H
+#define MOZILLA_GFX_TRIANGLE_H
+
+#include <algorithm>
+
+#include "mozilla/Move.h"
+#include "Point.h"
+#include "Rect.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * A simple triangle data structure.
+ */
+template<class Units, class F = Float>
+struct TriangleTyped
+{
+ PointTyped<Units, F> p1, p2, p3;
+ F width, height;
+
+ TriangleTyped()
+ : p1(), p2(), p3() {}
+
+ TriangleTyped(PointTyped<Units, F> aP1,
+ PointTyped<Units, F> aP2,
+ PointTyped<Units, F> aP3)
+ : p1(aP1), p2(aP2), p3(aP3) {}
+
+ RectTyped<Units, F> BoundingBox() const
+ {
+ F minX = std::min(std::min(p1.x, p2.x), p3.x);
+ F maxX = std::max(std::max(p1.x, p2.x), p3.x);
+
+ F minY = std::min(std::min(p1.y, p2.y), p3.y);
+ F maxY = std::max(std::max(p1.y, p2.y), p3.y);
+
+ return RectTyped<Units, F>(minX, minY, maxX - minX, maxY - minY);
+ }
+};
+
+typedef TriangleTyped<UnknownUnits, Float> Triangle;
+
+template<class Units, class F = Float>
+struct TexturedTriangleTyped : public TriangleTyped<Units, F>
+{
+ explicit TexturedTriangleTyped(const TriangleTyped<Units, F>& aTriangle)
+ : TriangleTyped<Units, F>(aTriangle) {}
+
+ explicit TexturedTriangleTyped(TriangleTyped<Units, F>&& aTriangle)
+ : TriangleTyped<Units, F>(Move(aTriangle)) {}
+
+ TriangleTyped<Units, F> textureCoords;
+};
+
+typedef TexturedTriangleTyped<UnknownUnits, Float> TexturedTriangle;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TRIANGLE_H */
diff --git a/system/graphics/2d/Types.h b/system/graphics/2d/Types.h
new file mode 100644
index 000000000..3cdf077b1
--- /dev/null
+++ b/system/graphics/2d/Types.h
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TYPES_H_
+#define MOZILLA_GFX_TYPES_H_
+
+#include "mozilla/EndianUtils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace mozilla {
+namespace gfx {
+
+typedef float Float;
+
+enum class SurfaceType : int8_t {
+ DATA, /* Data surface - bitmap in memory */
+ D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */
+ D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
+ CAIRO, /* Surface wrapping a cairo surface */
+ CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
+ COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
+ COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
+ SKIA, /* Surface wrapping a Skia bitmap */
+ DUAL_DT, /* Snapshot of a dual drawtarget */
+ D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
+ RECORDING, /* Surface used for recording */
+ TILED /* Surface from a tiled DrawTarget */
+};
+
+enum class SurfaceFormat : int8_t {
+ // The following values are named to reflect layout of colors in memory, from
+ // lowest byte to highest byte. The 32-bit value layout depends on machine
+ // endianness.
+ // in-memory 32-bit LE value 32-bit BE value
+ B8G8R8A8, // [BB, GG, RR, AA] 0xAARRGGBB 0xBBGGRRAA
+ B8G8R8X8, // [BB, GG, RR, 00] 0x00RRGGBB 0xBBGGRR00
+ R8G8B8A8, // [RR, GG, BB, AA] 0xAABBGGRR 0xRRGGBBAA
+ R8G8B8X8, // [RR, GG, BB, 00] 0x00BBGGRR 0xRRGGBB00
+ A8R8G8B8, // [AA, RR, GG, BB] 0xBBGGRRAA 0xAARRGGBB
+ X8R8G8B8, // [00, RR, GG, BB] 0xBBGGRR00 0x00RRGGBB
+
+ R8G8B8,
+ B8G8R8,
+
+ // The _UINT16 suffix here indicates that the name reflects the layout when
+ // viewed as a uint16_t value. In memory these values are stored using native
+ // endianness.
+ R5G6B5_UINT16, // 0bRRRRRGGGGGGBBBBB
+
+ // This one is a single-byte, so endianness isn't an issue.
+ A8,
+
+ // These ones are their own special cases.
+ YUV,
+ NV12,
+ YUV422,
+ HSV,
+ Lab,
+ Depth,
+
+ // This represents the unknown format.
+ UNKNOWN,
+
+ A8R8G8B8_UINT32 = B8G8R8A8, // 0xAARRGGBB
+ X8R8G8B8_UINT32 = B8G8R8X8 // 0x00RRGGBB
+};
+
+inline bool IsOpaque(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ case SurfaceFormat::R5G6B5_UINT16:
+ case SurfaceFormat::YUV:
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::YUV422:
+ return true;
+ default:
+ return false;
+ }
+}
+
+enum class FilterType : int8_t {
+ BLEND = 0,
+ TRANSFORM,
+ MORPHOLOGY,
+ COLOR_MATRIX,
+ FLOOD,
+ TILE,
+ TABLE_TRANSFER,
+ DISCRETE_TRANSFER,
+ LINEAR_TRANSFER,
+ GAMMA_TRANSFER,
+ CONVOLVE_MATRIX,
+ DISPLACEMENT_MAP,
+ TURBULENCE,
+ ARITHMETIC_COMBINE,
+ COMPOSITE,
+ DIRECTIONAL_BLUR,
+ GAUSSIAN_BLUR,
+ POINT_DIFFUSE,
+ POINT_SPECULAR,
+ SPOT_DIFFUSE,
+ SPOT_SPECULAR,
+ DISTANT_DIFFUSE,
+ DISTANT_SPECULAR,
+ CROP,
+ PREMULTIPLY,
+ UNPREMULTIPLY
+};
+
+enum class DrawTargetType : int8_t {
+ SOFTWARE_RASTER = 0,
+ HARDWARE_RASTER,
+ VECTOR
+};
+
+enum class BackendType : int8_t {
+ NONE = 0,
+ DIRECT2D, // Used for version independent D2D objects.
+ CAIRO,
+ SKIA,
+ RECORDING,
+ DIRECT2D1_1,
+
+ // Add new entries above this line.
+ BACKEND_LAST
+};
+
+enum class FontType : int8_t {
+ DWRITE,
+ GDI,
+ MAC,
+ SKIA,
+ CAIRO,
+ COREGRAPHICS,
+ FONTCONFIG
+};
+
+enum class NativeSurfaceType : int8_t {
+ D3D10_TEXTURE,
+ CAIRO_CONTEXT,
+ CGCONTEXT,
+ CGCONTEXT_ACCELERATED,
+ OPENGL_TEXTURE
+};
+
+enum class NativeFontType : int8_t {
+ DWRITE_FONT_FACE,
+ GDI_FONT_FACE,
+ MAC_FONT_FACE,
+ SKIA_FONT_FACE,
+ CAIRO_FONT_FACE
+};
+
+enum class FontStyle : int8_t {
+ NORMAL,
+ ITALIC,
+ BOLD,
+ BOLD_ITALIC
+};
+
+enum class FontHinting : int8_t {
+ NONE,
+ LIGHT,
+ NORMAL,
+ FULL
+};
+
+enum class CompositionOp : int8_t {
+ OP_OVER,
+ OP_ADD,
+ OP_ATOP,
+ OP_OUT,
+ OP_IN,
+ OP_SOURCE,
+ OP_DEST_IN,
+ OP_DEST_OUT,
+ OP_DEST_OVER,
+ OP_DEST_ATOP,
+ OP_XOR,
+ OP_MULTIPLY,
+ OP_SCREEN,
+ OP_OVERLAY,
+ OP_DARKEN,
+ OP_LIGHTEN,
+ OP_COLOR_DODGE,
+ OP_COLOR_BURN,
+ OP_HARD_LIGHT,
+ OP_SOFT_LIGHT,
+ OP_DIFFERENCE,
+ OP_EXCLUSION,
+ OP_HUE,
+ OP_SATURATION,
+ OP_COLOR,
+ OP_LUMINOSITY,
+ OP_COUNT
+};
+
+enum class Axis : int8_t {
+ X_AXIS,
+ Y_AXIS,
+ BOTH
+};
+
+enum class ExtendMode : int8_t {
+ CLAMP, // Do not repeat
+ REPEAT, // Repeat in both axis
+ REPEAT_X, // Only X axis
+ REPEAT_Y, // Only Y axis
+ REFLECT // Mirror the image
+};
+
+enum class FillRule : int8_t {
+ FILL_WINDING,
+ FILL_EVEN_ODD
+};
+
+enum class AntialiasMode : int8_t {
+ NONE,
+ GRAY,
+ SUBPIXEL,
+ DEFAULT
+};
+
+// See https://en.wikipedia.org/wiki/Texture_filtering
+enum class SamplingFilter : int8_t {
+ GOOD,
+ LINEAR,
+ POINT,
+ SENTINEL // one past the last valid value
+};
+
+enum class PatternType : int8_t {
+ COLOR,
+ SURFACE,
+ LINEAR_GRADIENT,
+ RADIAL_GRADIENT
+};
+
+enum class JoinStyle : int8_t {
+ BEVEL,
+ ROUND,
+ MITER, //!< Mitered if within the miter limit, else, if the backed supports
+ //!< it (D2D), the miter is clamped. If the backend does not support
+ //!< miter clamping the behavior is as for MITER_OR_BEVEL.
+ MITER_OR_BEVEL //!< Mitered if within the miter limit, else beveled.
+};
+
+enum class CapStyle : int8_t {
+ BUTT,
+ ROUND,
+ SQUARE
+};
+
+enum class SamplingBounds : int8_t {
+ UNBOUNDED,
+ BOUNDED
+};
+
+/* Color is stored in non-premultiplied form */
+struct Color
+{
+public:
+ Color()
+ : r(0.0f), g(0.0f), b(0.0f), a(0.0f)
+ {}
+ Color(Float aR, Float aG, Float aB, Float aA)
+ : r(aR), g(aG), b(aB), a(aA)
+ {}
+ Color(Float aR, Float aG, Float aB)
+ : r(aR), g(aG), b(aB), a(1.0f)
+ {}
+
+ static Color FromABGR(uint32_t aColor)
+ {
+ Color newColor(((aColor >> 0) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 8) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 16) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 24) & 0xff) * (1.0f / 255.0f));
+
+ return newColor;
+ }
+
+ // The "Unusual" prefix is to avoid unintentionally using this function when
+ // FromABGR(), which is much more common, is needed.
+ static Color UnusualFromARGB(uint32_t aColor)
+ {
+ Color newColor(((aColor >> 16) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 8) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 0) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 24) & 0xff) * (1.0f / 255.0f));
+
+ return newColor;
+ }
+
+ uint32_t ToABGR() const
+ {
+ return uint32_t(r * 255.0f) | uint32_t(g * 255.0f) << 8 |
+ uint32_t(b * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
+ }
+
+ // The "Unusual" prefix is to avoid unintentionally using this function when
+ // ToABGR(), which is much more common, is needed.
+ uint32_t UnusualToARGB() const
+ {
+ return uint32_t(b * 255.0f) | uint32_t(g * 255.0f) << 8 |
+ uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
+ }
+
+ bool operator==(const Color& aColor) const {
+ return r == aColor.r && g == aColor.g && b == aColor.b && a == aColor.a;
+ }
+
+ bool operator!=(const Color& aColor) const {
+ return !(*this == aColor);
+ }
+
+ Float r, g, b, a;
+};
+
+struct GradientStop
+{
+ bool operator<(const GradientStop& aOther) const {
+ return offset < aOther.offset;
+ }
+
+ Float offset;
+ Color color;
+};
+
+enum class JobStatus {
+ Complete,
+ Wait,
+ Yield,
+ Error
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+// XXX: temporary
+typedef mozilla::gfx::SurfaceFormat gfxImageFormat;
+
+#if defined(XP_WIN) && defined(MOZ_GFX)
+#ifdef GFX2D_INTERNAL
+#define GFX2D_API __declspec(dllexport)
+#else
+#define GFX2D_API __declspec(dllimport)
+#endif
+#else
+#define GFX2D_API
+#endif
+
+namespace mozilla {
+
+// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum
+// values from being used for indexing. Wrapping the enum in a struct does at
+// least gives us name scoping.
+struct RectCorner {
+ enum {
+ // This order is important since Rect::AtCorner, AppendRoundedRectToPath
+ // and other code depends on it!
+ TopLeft = 0,
+ TopRight = 1,
+ BottomRight = 2,
+ BottomLeft = 3,
+ Count = 4
+ };
+};
+
+// Side constants for use in various places.
+enum Side { eSideTop, eSideRight, eSideBottom, eSideLeft };
+
+enum SideBits {
+ eSideBitsNone = 0,
+ eSideBitsTop = 1 << eSideTop,
+ eSideBitsRight = 1 << eSideRight,
+ eSideBitsBottom = 1 << eSideBottom,
+ eSideBitsLeft = 1 << eSideLeft,
+ eSideBitsTopBottom = eSideBitsTop | eSideBitsBottom,
+ eSideBitsLeftRight = eSideBitsLeft | eSideBitsRight,
+ eSideBitsAll = eSideBitsTopBottom | eSideBitsLeftRight
+};
+
+} // namespace mozilla
+
+#define NS_SIDE_TOP mozilla::eSideTop
+#define NS_SIDE_RIGHT mozilla::eSideRight
+#define NS_SIDE_BOTTOM mozilla::eSideBottom
+#define NS_SIDE_LEFT mozilla::eSideLeft
+
+#endif /* MOZILLA_GFX_TYPES_H_ */
diff --git a/system/graphics/2d/UserData.h b/system/graphics/2d/UserData.h
new file mode 100644
index 000000000..a10dbc8b8
--- /dev/null
+++ b/system/graphics/2d/UserData.h
@@ -0,0 +1,128 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_USERDATA_H_
+#define MOZILLA_GFX_USERDATA_H_
+
+#include <stdlib.h>
+#include "Types.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct UserDataKey {
+ int unused;
+};
+
+/* this class is basically a clone of the user data concept from cairo */
+class UserData
+{
+ typedef void (*destroyFunc)(void *data);
+public:
+ UserData() : count(0), entries(nullptr) {}
+
+ /* Attaches untyped userData associated with key. destroy is called on destruction */
+ void Add(UserDataKey *key, void *userData, destroyFunc destroy)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ if (entries[i].destroy) {
+ entries[i].destroy(entries[i].userData);
+ }
+ entries[i].userData = userData;
+ entries[i].destroy = destroy;
+ return;
+ }
+ }
+
+ // We could keep entries in a std::vector instead of managing it by hand
+ // but that would propagate an stl dependency out which we'd rather not
+ // do (see bug 666609). Plus, the entries array is expect to stay small
+ // so doing a realloc everytime we add a new entry shouldn't be too costly
+ entries = static_cast<Entry*>(realloc(entries, sizeof(Entry)*(count+1)));
+
+ if (!entries) {
+ MOZ_CRASH("GFX: UserData::Add");
+ }
+
+ entries[count].key = key;
+ entries[count].userData = userData;
+ entries[count].destroy = destroy;
+
+ count++;
+ }
+
+ /* Remove and return user data associated with key, without destroying it */
+ void* Remove(UserDataKey *key)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ void *userData = entries[i].userData;
+ // decrement before looping so entries[i+1] doesn't read past the end:
+ --count;
+ for (;i<count; i++) {
+ entries[i] = entries[i+1];
+ }
+ return userData;
+ }
+ }
+ return nullptr;
+ }
+
+ /* Retrives the userData for the associated key */
+ void *Get(UserDataKey *key) const
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ return entries[i].userData;
+ }
+ }
+ return nullptr;
+ }
+
+ bool Has(UserDataKey *key)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void Destroy()
+ {
+ for (int i=0; i<count; i++) {
+ if (entries[i].destroy) {
+ entries[i].destroy(entries[i].userData);
+ }
+ }
+ free(entries);
+ entries = nullptr;
+ count = 0;
+ }
+
+ ~UserData()
+ {
+ Destroy();
+ }
+
+private:
+ struct Entry {
+ const UserDataKey *key;
+ void *userData;
+ destroyFunc destroy;
+ };
+
+ int count;
+ Entry *entries;
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_USERDATA_H_ */
diff --git a/system/graphics/2d/convolver.cpp b/system/graphics/2d/convolver.cpp
new file mode 100644
index 000000000..0221f1563
--- /dev/null
+++ b/system/graphics/2d/convolver.cpp
@@ -0,0 +1,562 @@
+// Copyright (c) 2006-2011 The Chromium Authors. 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 "2D.h"
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/include/core/SkTypes.h"
+
+
+#if defined(USE_SSE2)
+#include "convolverSSE2.h"
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+#include "convolverLS3.h"
+#endif
+
+using mozilla::gfx::Factory;
+
+#if defined(SK_CPU_LENDIAN)
+#define R_OFFSET_IDX 0
+#define G_OFFSET_IDX 1
+#define B_OFFSET_IDX 2
+#define A_OFFSET_IDX 3
+#else
+#define R_OFFSET_IDX 3
+#define G_OFFSET_IDX 2
+#define B_OFFSET_IDX 1
+#define A_OFFSET_IDX 0
+#endif
+
+#if defined(USE_SSE2)
+#define ConvolveHorizontally4_SIMD ConvolveHorizontally4_SSE2
+#define ConvolveHorizontally_SIMD ConvolveHorizontally_SSE2
+#define ConvolveVertically_SIMD ConvolveVertically_SSE2
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+#define ConvolveHorizontally4_SIMD ConvolveHorizontally4_LS3
+#define ConvolveHorizontally_SIMD ConvolveHorizontally_LS3
+#define ConvolveVertically_SIMD ConvolveVertically_LS3
+#endif
+
+namespace skia {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline unsigned char ClampTo8(int a) {
+ if (static_cast<unsigned>(a) < 256)
+ return a; // Avoid the extra check in the common case.
+ if (a < 0)
+ return 0;
+ return 255;
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+ // The number of pixels in each row is given in |source_row_pixel_width|.
+ // The maximum number of rows needed in the buffer is |max_y_filter_size|
+ // (we only need to store enough rows for the biggest filter).
+ //
+ // We use the |first_input_row| to compute the coordinates of all of the
+ // following rows returned by Advance().
+ CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+ int first_input_row)
+ : row_byte_width_(dest_row_pixel_width * 4),
+ num_rows_(max_y_filter_size),
+ next_row_(0),
+ next_row_coordinate_(first_input_row) {
+ buffer_.resize(row_byte_width_ * max_y_filter_size);
+ row_addresses_.resize(num_rows_);
+ }
+
+ // Moves to the next row in the buffer, returning a pointer to the beginning
+ // of it.
+ unsigned char* AdvanceRow() {
+ unsigned char* row = &buffer_[next_row_ * row_byte_width_];
+ next_row_coordinate_++;
+
+ // Set the pointer to the next row to use, wrapping around if necessary.
+ next_row_++;
+ if (next_row_ == num_rows_)
+ next_row_ = 0;
+ return row;
+ }
+
+ // Returns a pointer to an "unrolled" array of rows. These rows will start
+ // at the y coordinate placed into |*first_row_index| and will continue in
+ // order for the maximum number of rows in this circular buffer.
+ //
+ // The |first_row_index_| may be negative. This means the circular buffer
+ // starts before the top of the image (it hasn't been filled yet).
+ unsigned char* const* GetRowAddresses(int* first_row_index) {
+ // Example for a 4-element circular buffer holding coords 6-9.
+ // Row 0 Coord 8
+ // Row 1 Coord 9
+ // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
+ // Row 3 Coord 7
+ //
+ // The "next" row is also the first (lowest) coordinate. This computation
+ // may yield a negative value, but that's OK, the math will work out
+ // since the user of this buffer will compute the offset relative
+ // to the first_row_index and the negative rows will never be used.
+ *first_row_index = next_row_coordinate_ - num_rows_;
+
+ int cur_row = next_row_;
+ for (int i = 0; i < num_rows_; i++) {
+ row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+ // Advance to the next row, wrapping if necessary.
+ cur_row++;
+ if (cur_row == num_rows_)
+ cur_row = 0;
+ }
+ return &row_addresses_[0];
+ }
+
+ private:
+ // The buffer storing the rows. They are packed, each one row_byte_width_.
+ std::vector<unsigned char> buffer_;
+
+ // Number of bytes per row in the |buffer_|.
+ int row_byte_width_;
+
+ // The number of rows available in the buffer.
+ int num_rows_;
+
+ // The next row index we should write into. This wraps around as the
+ // circular buffer is used.
+ int next_row_;
+
+ // The y coordinate of the |next_row_|. This is incremented each time a
+ // new row is appended and does not wrap.
+ int next_row_coordinate_;
+
+ // Buffer used by GetRowAddresses().
+ std::vector<unsigned char*> row_addresses_;
+};
+
+} // namespace
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+template<bool has_alpha>
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ // Loop over each pixel on this row in the output image.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const unsigned char* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ int accum[4] = {0};
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_x];
+ accum[0] += cur_filter * row_to_filter[filter_x * 4 + R_OFFSET_IDX];
+ accum[1] += cur_filter * row_to_filter[filter_x * 4 + G_OFFSET_IDX];
+ accum[2] += cur_filter * row_to_filter[filter_x * 4 + B_OFFSET_IDX];
+ if (has_alpha)
+ accum[3] += cur_filter * row_to_filter[filter_x * 4 + A_OFFSET_IDX];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[out_x * 4 + R_OFFSET_IDX] = ClampTo8(accum[0]);
+ out_row[out_x * 4 + G_OFFSET_IDX] = ClampTo8(accum[1]);
+ out_row[out_x * 4 + B_OFFSET_IDX] = ClampTo8(accum[2]);
+ if (has_alpha)
+ out_row[out_x * 4 + A_OFFSET_IDX] = ClampTo8(accum[3]);
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ // We go through each column in the output and do a vertical convolution,
+ // generating one output pixel each time.
+ for (int out_x = 0; out_x < pixel_width; out_x++) {
+ // Compute the number of bytes over in each row that the current column
+ // we're convolving starts at. The pixel will cover the next 4 bytes.
+ int byte_offset = out_x * 4;
+
+ // Apply the filter to one column of pixels.
+ int accum[4] = {0};
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_y];
+ accum[0] += cur_filter
+ * source_data_rows[filter_y][byte_offset + R_OFFSET_IDX];
+ accum[1] += cur_filter
+ * source_data_rows[filter_y][byte_offset + G_OFFSET_IDX];
+ accum[2] += cur_filter
+ * source_data_rows[filter_y][byte_offset + B_OFFSET_IDX];
+ if (has_alpha)
+ accum[3] += cur_filter
+ * source_data_rows[filter_y][byte_offset + A_OFFSET_IDX];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of precision.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[byte_offset + R_OFFSET_IDX] = ClampTo8(accum[0]);
+ out_row[byte_offset + G_OFFSET_IDX] = ClampTo8(accum[1]);
+ out_row[byte_offset + B_OFFSET_IDX] = ClampTo8(accum[2]);
+ if (has_alpha) {
+ unsigned char alpha = ClampTo8(accum[3]);
+
+ // Make sure the alpha channel doesn't come out smaller than any of the
+ // color channels. We use premultipled alpha channels, so this should
+ // never happen, but rounding errors will cause this from time to time.
+ // These "impossible" colors will cause overflows (and hence random pixel
+ // values) when the resulting bitmap is drawn to the screen.
+ //
+ // We only need to do this when generating the final output row (here).
+ int max_color_channel = std::max(out_row[byte_offset + R_OFFSET_IDX],
+ std::max(out_row[byte_offset + G_OFFSET_IDX], out_row[byte_offset + B_OFFSET_IDX]));
+ if (alpha < max_color_channel)
+ out_row[byte_offset + A_OFFSET_IDX] = max_color_channel;
+ else
+ out_row[byte_offset + A_OFFSET_IDX] = alpha;
+ } else {
+ // No alpha channel, the image is opaque.
+ out_row[byte_offset + A_OFFSET_IDX] = 0xff;
+ }
+ }
+}
+
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width, unsigned char* out_row,
+ bool has_alpha, bool use_simd) {
+
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ // If the binary was not built with SSE2 support, we had to fallback to C version.
+ if (use_simd) {
+ ConvolveVertically_SIMD(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row, has_alpha);
+ } else
+#endif
+ {
+ if (has_alpha) {
+ ConvolveVertically<true>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ } else {
+ ConvolveVertically<false>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ }
+ }
+}
+
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha, bool use_simd) {
+ int width = filter.num_values();
+ int processed = 0;
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ int simd_width = width & ~3;
+ if (use_simd && simd_width) {
+ // SIMD implementation works with 4 pixels at a time.
+ // Therefore we process as much as we can using SSE and then use
+ // C implementation for leftovers
+ ConvolveHorizontally_SIMD(src_data, filter, out_row);
+ processed = simd_width;
+ }
+#endif
+
+ if (width > processed) {
+#if defined(_MIPS_ARCH_LOONGSON3A)
+ ConvolveHorizontally1_LS3(src_data, filter, out_row);
+#else
+ if (has_alpha) {
+ ConvolveHorizontally<true>(src_data, filter, out_row);
+ } else {
+ ConvolveHorizontally<false>(src_data, filter, out_row);
+ }
+#endif
+ }
+}
+
+// ConvolutionFilter1D ---------------------------------------------------------
+
+ConvolutionFilter1D::ConvolutionFilter1D()
+ : max_filter_(0) {
+}
+
+ConvolutionFilter1D::~ConvolutionFilter1D() {
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length) {
+ SkASSERT(filter_length > 0);
+
+ std::vector<Fixed> fixed_values;
+ fixed_values.reserve(filter_length);
+
+ for (int i = 0; i < filter_length; ++i)
+ fixed_values.push_back(FloatToFixed(filter_values[i]));
+
+ AddFilter(filter_offset, &fixed_values[0], filter_length);
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length) {
+ // It is common for leading/trailing filter values to be zeros. In such
+ // cases it is beneficial to only store the central factors.
+ // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on
+ // a 1080p image this optimization gives a ~10% speed improvement.
+ int first_non_zero = 0;
+ while (first_non_zero < filter_length && filter_values[first_non_zero] == 0)
+ first_non_zero++;
+
+ if (first_non_zero < filter_length) {
+ // Here we have at least one non-zero factor.
+ int last_non_zero = filter_length - 1;
+ while (last_non_zero >= 0 && filter_values[last_non_zero] == 0)
+ last_non_zero--;
+
+ filter_offset += first_non_zero;
+ filter_length = last_non_zero + 1 - first_non_zero;
+ SkASSERT(filter_length > 0);
+
+ for (int i = first_non_zero; i <= last_non_zero; i++)
+ filter_values_.push_back(filter_values[i]);
+ } else {
+ // Here all the factors were zeroes.
+ filter_length = 0;
+ }
+
+ FilterInstance instance;
+
+ // We pushed filter_length elements onto filter_values_
+ instance.data_location = (static_cast<int>(filter_values_.size()) -
+ filter_length);
+ instance.offset = filter_offset;
+ instance.length = filter_length;
+ filters_.push_back(instance);
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& filter_x,
+ const ConvolutionFilter1D& filter_y,
+ int output_byte_row_stride,
+ unsigned char* output) {
+ bool use_simd = Factory::HasSSE2();
+
+#if !defined(USE_SSE2)
+ // Even we have runtime support for SSE2 instructions, since the binary
+ // was not built with SSE2 support, we had to fallback to C version.
+ use_simd = false;
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+ use_simd = true;
+#endif
+
+
+ int max_y_filter_size = filter_y.max_filter();
+
+ // The next row in the input that we will generate a horizontally
+ // convolved row for. If the filter doesn't start at the beginning of the
+ // image (this is the case when we are only resizing a subset), then we
+ // don't want to generate any output rows before that. Compute the starting
+ // row for convolution as the first pixel for the first vertical filter.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter_y.FilterForValue(0, &filter_offset, &filter_length);
+ int next_x_row = filter_offset;
+
+ // We loop over each row in the input doing a horizontal convolution. This
+ // will result in a horizontally convolved image. We write the results into
+ // a circular buffer of convolved rows and do vertical convolution as rows
+ // are available. This prevents us from having to store the entire
+ // intermediate image and helps cache coherency.
+ // We will need four extra rows to allow horizontal convolution could be done
+ // simultaneously. We also padding each row in row buffer to be aligned-up to
+ // 16 bytes.
+ // TODO(jiesun): We do not use aligned load from row buffer in vertical
+ // convolution pass yet. Somehow Windows does not like it.
+ int row_buffer_width = (filter_x.num_values() + 15) & ~0xF;
+ int row_buffer_height = max_y_filter_size + (use_simd ? 4 : 0);
+ CircularRowBuffer row_buffer(row_buffer_width,
+ row_buffer_height,
+ filter_offset);
+
+ // Loop over every possible output row, processing just enough horizontal
+ // convolutions to run each subsequent vertical convolution.
+ SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4);
+ int num_output_rows = filter_y.num_values();
+ int pixel_width = filter_x.num_values();
+
+
+ // We need to check which is the last line to convolve before we advance 4
+ // lines in one iteration.
+ int last_filter_offset, last_filter_length;
+ // SSE2 can access up to 3 extra pixels past the end of the
+ // buffer. At the bottom of the image, we have to be careful
+ // not to access data past the end of the buffer. Normally
+ // we fall back to the C++ implementation for the last row.
+ // If the last row is less than 3 pixels wide, we may have to fall
+ // back to the C++ version for more rows. Compute how many
+ // rows we need to avoid the SSE implementation for here.
+ filter_x.FilterForValue(filter_x.num_values() - 1, &last_filter_offset,
+ &last_filter_length);
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ int avoid_simd_rows = 1 + 3 /
+ (last_filter_offset + last_filter_length);
+#endif
+ filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
+ &last_filter_length);
+
+ for (int out_y = 0; out_y < num_output_rows; out_y++) {
+ filter_values = filter_y.FilterForValue(out_y,
+ &filter_offset, &filter_length);
+
+ // Generate output rows until we have enough to run the current filter.
+ if (use_simd) {
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ // We don't want to process too much rows in batches of 4 because
+ // we can go out-of-bounds at the end
+ while (next_x_row < filter_offset + filter_length) {
+ if (next_x_row + 3 < last_filter_offset + last_filter_length -
+ avoid_simd_rows) {
+ const unsigned char* src[4];
+ unsigned char* out_row[4];
+ for (int i = 0; i < 4; ++i) {
+ src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
+ out_row[i] = row_buffer.AdvanceRow();
+ }
+ ConvolveHorizontally4_SIMD(src, filter_x, out_row);
+ next_x_row += 4;
+ } else {
+ // Check if we need to avoid SSE2 for this row.
+ if (next_x_row < last_filter_offset + last_filter_length -
+ avoid_simd_rows) {
+ ConvolveHorizontally_SIMD(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ }
+ next_x_row++;
+ }
+ }
+#endif
+ } else {
+ while (next_x_row < filter_offset + filter_length) {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ next_x_row++;
+ }
+ }
+
+ // Compute where in the output image this row of final data will go.
+ unsigned char* cur_output_row = &output[out_y * output_byte_row_stride];
+
+ // Get the list of rows that the circular buffer has, in order.
+ int first_row_in_circular_buffer;
+ unsigned char* const* rows_to_convolve =
+ row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+ // Now compute the start of the subset of those rows that the filter
+ // needs.
+ unsigned char* const* first_row_for_filter =
+ &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+ ConvolveVertically(filter_values, filter_length,
+ first_row_for_filter, pixel_width,
+ cur_output_row, source_has_alpha, use_simd);
+ }
+}
+
+} // namespace skia
diff --git a/system/graphics/2d/convolver.h b/system/graphics/2d/convolver.h
new file mode 100644
index 000000000..0c4591ac8
--- /dev/null
+++ b/system/graphics/2d/convolver.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2006-2012 The Chromium Authors. 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 SKIA_EXT_CONVOLVER_H_
+#define SKIA_EXT_CONVOLVER_H_
+
+#include <cmath>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "mozilla/Assertions.h"
+#include "skia/include/core/SkTypes.h"
+
+// avoid confusion with Mac OS X's math library (Carbon)
+#if defined(__APPLE__)
+#undef FloatToFixed
+#undef FixedToFloat
+#endif
+
+namespace skia {
+
+// Represents a filter in one dimension. Each output pixel has one entry in this
+// object for the filter values contributing to it. You build up the filter
+// list by calling AddFilter for each output pixel (in order).
+//
+// We do 2-dimensional convolution by first convolving each row by one
+// ConvolutionFilter1D, then convolving each column by another one.
+//
+// Entries are stored in fixed point, shifted left by kShiftBits.
+class ConvolutionFilter1D {
+ public:
+ typedef short Fixed;
+
+ // The number of bits that fixed point values are shifted by.
+ enum { kShiftBits = 14 };
+
+ ConvolutionFilter1D();
+ ~ConvolutionFilter1D();
+
+ // Convert between floating point and our fixed point representation.
+ static Fixed FloatToFixed(float f) {
+ return static_cast<Fixed>(f * (1 << kShiftBits));
+ }
+ static unsigned char FixedToChar(Fixed x) {
+ return static_cast<unsigned char>(x >> kShiftBits);
+ }
+ static float FixedToFloat(Fixed x) {
+ // The cast relies on Fixed being a short, implying that on
+ // the platforms we care about all (16) bits will fit into
+ // the mantissa of a (32-bit) float.
+ static_assert(sizeof(Fixed) == 2,
+ "fixed type should fit in float mantissa");
+ float raw = static_cast<float>(x);
+ return ldexpf(raw, -kShiftBits);
+ }
+
+ // Returns the maximum pixel span of a filter.
+ int max_filter() const { return max_filter_; }
+
+ // Returns the number of filters in this filter. This is the dimension of the
+ // output image.
+ int num_values() const { return static_cast<int>(filters_.size()); }
+
+ // Appends the given list of scaling values for generating a given output
+ // pixel. |filter_offset| is the distance from the edge of the image to where
+ // the scaling factors start. The scaling factors apply to the source pixels
+ // starting from this position, and going for the next |filter_length| pixels.
+ //
+ // You will probably want to make sure your input is normalized (that is,
+ // all entries in |filter_values| sub to one) to prevent affecting the overall
+ // brighness of the image.
+ //
+ // The filter_length must be > 0.
+ //
+ // This version will automatically convert your input to fixed point.
+ void AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length);
+
+ // Same as the above version, but the input is already fixed point.
+ void AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length);
+
+ // Retrieves a filter for the given |value_offset|, a position in the output
+ // image in the direction we're convolving. The offset and length of the
+ // filter values are put into the corresponding out arguments (see AddFilter
+ // above for what these mean), and a pointer to the first scaling factor is
+ // returned. There will be |filter_length| values in this array.
+ inline const Fixed* FilterForValue(int value_offset,
+ int* filter_offset,
+ int* filter_length) const {
+ const FilterInstance& filter = filters_[value_offset];
+ *filter_offset = filter.offset;
+ *filter_length = filter.length;
+ if (filter.length == 0) {
+ return NULL;
+ }
+ return &filter_values_[filter.data_location];
+ }
+
+
+ inline void PaddingForSIMD(int padding_count) {
+ // Padding |padding_count| of more dummy coefficients after the coefficients
+ // of last filter to prevent SIMD instructions which load 8 or 16 bytes
+ // together to access invalid memory areas. We are not trying to align the
+ // coefficients right now due to the opaqueness of <vector> implementation.
+ // This has to be done after all |AddFilter| calls.
+ for (int i = 0; i < padding_count; ++i)
+ filter_values_.push_back(static_cast<Fixed>(0));
+ }
+
+ private:
+ struct FilterInstance {
+ // Offset within filter_values for this instance of the filter.
+ int data_location;
+
+ // Distance from the left of the filter to the center. IN PIXELS
+ int offset;
+
+ // Number of values in this filter instance.
+ int length;
+ };
+
+ // Stores the information for each filter added to this class.
+ std::vector<FilterInstance> filters_;
+
+ // We store all the filter values in this flat list, indexed by
+ // |FilterInstance.data_location| to avoid the mallocs required for storing
+ // each one separately.
+ std::vector<Fixed> filter_values_;
+
+ // The maximum size of any filter we've added.
+ int max_filter_;
+};
+
+// Does a two-dimensional convolution on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& xfilter,
+ const ConvolutionFilter1D& yfilter,
+ int output_byte_row_stride,
+ unsigned char* output);
+
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha, bool use_sse2);
+
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width, unsigned char* out_row,
+ bool has_alpha, bool use_sse2);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_H_
diff --git a/system/graphics/2d/convolverLS3.cpp b/system/graphics/2d/convolverLS3.cpp
new file mode 100644
index 000000000..a1a41c98b
--- /dev/null
+++ b/system/graphics/2d/convolverLS3.cpp
@@ -0,0 +1,927 @@
+// Copyright (c) 2014-2015 The Chromium Authors. 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 "convolver.h"
+#include <algorithm>
+#include "skia/include/core/SkTypes.h"
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+
+#include "MMIHelpers.h"
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+void ConvolveHorizontally_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ int tmp, filter_offset, filter_length;
+ double zero, mask[4], shuf_50, shuf_fa;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ "li %[tmp], 1 \n\t"
+ "dsll32 %[tmp], 0x10 \n\t"
+ "daddiu %[tmp], -1 \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask1] \n\t"
+ "ori %[tmp], $0, 0x50 \n\t"
+ "mtc1 %[tmp], %[shuf_50] \n\t"
+ "ori %[tmp], $0, 0xfa \n\t"
+ "mtc1 %[tmp], %[shuf_fa] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
+ [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
+ [tmp]"=&r"(tmp)
+ );
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+ double accumh, accuml;
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const void *row_to_filter =
+ reinterpret_cast<const void*>(&src_data[filter_offset << 2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum, accum, accum)
+ ".set pop \n\t"
+ :[accumh]"=f"(accumh), [accuml]"=f"(accuml)
+ );
+
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
+ double src16h, src16l, mul_hih, mul_hil, mul_loh, mul_lol;
+ double coeffh, coeffl, src8h, src8l, th, tl, coeff16h, coeff16l;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
+ // [16] xx xx xx xx c3 c2 c1 c0
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // [16] xx xx xx xx c1 c1 c0 c0
+ _mm_pshuflh(coeff16, coeff, shuf_50)
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ // Load four pixels => unpack the first two pixels to 16 bits =>
+ // multiply with coefficients => accumulate the convolution result.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ "gsldlc1 %[src8h], 0xf(%[rtf]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[rtf]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[rtf]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[rtf]) \n\t"
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0*c0 b0*c0 g0*c0 r0*c0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ // [32] a1*c1 b1*c1 g1*c1 r1*c1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ // Duplicate 3rd and 4th coefficients for all channels =>
+ // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients
+ // => accumulate the convolution results.
+ // [16] xx xx xx xx c3 c3 c2 c2
+ _mm_pshuflh(coeff16, coeff, shuf_fa)
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ // [16] a3 g3 b3 r3 a2 g2 b2 r2
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a2*c2 b2*c2 g2*c2 r2*c2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ // [32] a3*c3 b3*c3 g3*c3 r3*c3
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa),
+ [fval]"r"(filter_values), [rtf]"r"(row_to_filter)
+ );
+
+ // Advance the pixel and coefficients pointers.
+ row_to_filter += 16;
+ filter_values += 4;
+ }
+
+ // When |filter_length| is not divisible by 4, we need to decimate some of
+ // the filter coefficient that was loaded incorrectly to zero; Other than
+ // that the algorithm is same with above, except that the 4th pixel will be
+ // always absent.
+ int r = filter_length & 3;
+ if (r) {
+ double coeffh, coeffl, th, tl, coeff16h, coeff16l;
+ double src8h, src8l, src16h, src16l, mul_hih, mul_hil, mul_loh, mul_lol;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // Mask out extra filter taps.
+ "and %[coeffl], %[coeffl], %[mask] \n\t"
+ _mm_pshuflh(coeff16, coeff, shuf_50)
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ "gsldlc1 %[src8h], 0xf(%[rtf]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[rtf]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[rtf]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[rtf]) \n\t"
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pshuflh(coeff16, coeff, shuf_fa)
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
+ :[fval]"r"(filter_values), [rtf]"r"(row_to_filter),
+ [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r]),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+ }
+
+ double t, sra;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ // Shift right for fixed point implementation.
+ _mm_psraw(accum, accum, sra)
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ _mm_packsswh(accum, accum, zero, t)
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ _mm_packushb(accum, accum, zero, t)
+ // Store the pixel value of 32 bits.
+ "swc1 %[accuml], (%[out_row]) \n\t"
+ ".set pop \n\t"
+ :[sra]"=&f"(sra), [t]"=&f"(t), [tmp]"=&r"(tmp),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml)
+ :[sk_sra]"i"(ConvolutionFilter1D::kShiftBits),
+ [out_row]"r"(out_row), [zeroh]"f"(zero), [zerol]"f"(zero)
+ :"memory"
+ );
+
+ out_row += 4;
+ }
+}
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// Process one pixel at a time.
+void ConvolveHorizontally1_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ double zero;
+ double sra;
+
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "xor %[zero], %[zero], %[zero] \n"
+ "mtc1 %[sk_sra], %[sra] \n"
+ ".set pop \n"
+ :[zero]"=&f"(zero), [sra]"=&f"(sra)
+ :[sk_sra]"r"(ConvolutionFilter1D::kShiftBits)
+ );
+ // Loop over each pixel on this row in the output image.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset;
+ int filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const unsigned char* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ double accuml;
+ double accumh;
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "xor %[accuml], %[accuml], %[accuml] \n"
+ "xor %[accumh], %[accumh], %[accumh] \n"
+ ".set pop \n"
+ :[accuml]"=&f"(accuml), [accumh]"=&f"(accumh)
+ );
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ double src8;
+ double src16;
+ double coeff;
+ double coeff16;
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "lwc1 %[src8], %[rtf] \n"
+ "mtc1 %[fv], %[coeff] \n"
+ "pshufh %[coeff16], %[coeff], %[zero] \n"
+ "punpcklbh %[src16], %[src8], %[zero] \n"
+ "pmullh %[src8], %[src16], %[coeff16] \n"
+ "pmulhh %[coeff], %[src16], %[coeff16] \n"
+ "punpcklhw %[src16], %[src8], %[coeff] \n"
+ "punpckhhw %[coeff16], %[src8], %[coeff] \n"
+ "paddw %[accuml], %[accuml], %[src16] \n"
+ "paddw %[accumh], %[accumh], %[coeff16] \n"
+ ".set pop \n"
+ :[accuml]"+f"(accuml), [accumh]"+f"(accumh),
+ [src8]"=&f"(src8), [src16]"=&f"(src16),
+ [coeff]"=&f"(coeff), [coeff16]"=&f"(coeff16)
+ :[rtf]"m"(row_to_filter[filter_x * 4]),
+ [fv]"r"(filter_values[filter_x]), [zero]"f"(zero)
+ );
+ }
+
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ "psraw %[accuml], %[accuml], %[sra] \n"
+ "psraw %[accumh], %[accumh], %[sra] \n"
+ // Store the new pixel.
+ "packsswh %[accuml], %[accuml], %[accumh] \n"
+ "packushb %[accuml], %[accuml], %[zero] \n"
+ "swc1 %[accuml], %[out_row] \n"
+ ".set pop \n"
+ :[accuml]"+f"(accuml), [accumh]"+f"(accumh)
+ :[sra]"f"(sra), [zero]"f"(zero), [out_row]"m"(out_row[out_x * 4])
+ :"memory"
+ );
+ }
+}
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+ int num_values = filter.num_values();
+ int tmp, filter_offset, filter_length;
+ double zero, mask[4], shuf_50, shuf_fa;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ "li %[tmp], 1 \n\t"
+ "dsll32 %[tmp], 0x10 \n\t"
+ "daddiu %[tmp], -1 \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask1] \n\t"
+ "ori %[tmp], $0, 0x50 \n\t"
+ "mtc1 %[tmp], %[shuf_50] \n\t"
+ "ori %[tmp], $0, 0xfa \n\t"
+ "mtc1 %[tmp], %[shuf_fa] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
+ [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
+ [tmp]"=&r"(tmp)
+ );
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+ double accum0h, accum0l, accum1h, accum1l;
+ double accum2h, accum2l, accum3h, accum3l;
+
+ // four pixels in a column per iteration.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ _mm_xor(accum3, accum3, accum3)
+ ".set pop \n\t"
+ :[accum0h]"=f"(accum0h), [accum0l]"=f"(accum0l),
+ [accum1h]"=f"(accum1h), [accum1l]"=f"(accum1l),
+ [accum2h]"=f"(accum2h), [accum2l]"=f"(accum2l),
+ [accum3h]"=f"(accum3h), [accum3l]"=f"(accum3l)
+ );
+
+ int start = (filter_offset<<2);
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+ double coeffh, coeffl, coeff16loh, coeff16lol, coeff16hih, coeff16hil;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // [16] xx xx xx xx c3 c2 c1 c0
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // [16] xx xx xx xx c1 c1 c0 c0
+ _mm_pshuflh(coeff16lo, coeff, shuf_50)
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ _mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
+ // [16] xx xx xx xx c3 c3 c2 c2
+ _mm_pshuflh(coeff16hi, coeff, shuf_fa)
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ _mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
+ ".set pop \n\t"
+ :[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
+ [coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
+ :[fval]"r"(filter_values), [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+
+#define ITERATION(_src, _accumh, _accuml) \
+ asm volatile ( \
+ ".set push \n\t" \
+ ".set arch=loongson3a \n\t" \
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t" \
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t" \
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t" \
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t" \
+ _mm_punpcklbh(src16, src8, zero) \
+ _mm_pmulhh(mul_hi, src16, coeff16lo) \
+ _mm_pmullh(mul_lo, src16, coeff16lo) \
+ _mm_punpcklhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhbh(src16, src8, zero) \
+ _mm_pmulhh(mul_hi, src16, coeff16hi) \
+ _mm_pmullh(mul_lo, src16, coeff16hi) \
+ _mm_punpcklhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ ".set pop \n\t" \
+ :[th]"=&f"(th), [tl]"=&f"(tl), \
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l), \
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l), \
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil), \
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol), \
+ [accumh]"+f"(_accumh), [accuml]"+f"(_accuml) \
+ :[zeroh]"f"(zero), [zerol]"f"(zero), [src]"r"(_src), \
+ [coeff16loh]"f"(coeff16loh), [coeff16lol]"f"(coeff16lol), \
+ [coeff16hih]"f"(coeff16hih), [coeff16hil]"f"(coeff16hil) \
+ );
+
+ ITERATION(src_data[0] + start, accum0h, accum0l);
+ ITERATION(src_data[1] + start, accum1h, accum1l);
+ ITERATION(src_data[2] + start, accum2h, accum2l);
+ ITERATION(src_data[3] + start, accum3h, accum3l);
+
+ start += 16;
+ filter_values += 4;
+ }
+
+ int r = filter_length & 3;
+ if (r) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+ double coeffh, coeffl, coeff16loh, coeff16lol, coeff16hih, coeff16hil;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // Mask out extra filter taps.
+ "and %[coeffl], %[coeffl], %[mask] \n\t"
+ _mm_pshuflh(coeff16lo, coeff, shuf_50)
+ /* c1 c1 c1 c1 c0 c0 c0 c0 */
+ _mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
+ _mm_pshuflh(coeff16hi, coeff, shuf_fa)
+ _mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
+ ".set pop \n\t"
+ :[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
+ [coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
+ :[fval]"r"(filter_values), [mask]"f"(mask[r]),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+
+ ITERATION(src_data[0] + start, accum0h, accum0l);
+ ITERATION(src_data[1] + start, accum1h, accum1l);
+ ITERATION(src_data[2] + start, accum2h, accum2l);
+ ITERATION(src_data[3] + start, accum3h, accum3l);
+ }
+
+ double t, sra;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ _mm_psraw(accum0, accum0, sra)
+ _mm_packsswh(accum0, accum0, zero, t)
+ _mm_packushb(accum0, accum0, zero, t)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_packsswh(accum1, accum1, zero, t)
+ _mm_packushb(accum1, accum1, zero, t)
+ _mm_psraw(accum2, accum2, sra)
+ _mm_packsswh(accum2, accum2, zero, t)
+ _mm_packushb(accum2, accum2, zero, t)
+ _mm_psraw(accum3, accum3, sra)
+ _mm_packsswh(accum3, accum3, zero, t)
+ _mm_packushb(accum3, accum3, zero, t)
+ "swc1 %[accum0l], (%[out_row0]) \n\t"
+ "swc1 %[accum1l], (%[out_row1]) \n\t"
+ "swc1 %[accum2l], (%[out_row2]) \n\t"
+ "swc1 %[accum3l], (%[out_row3]) \n\t"
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l),
+ [sra]"=&f"(sra), [t]"=&f"(t), [tmp]"=&r"(tmp)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [out_row0]"r"(out_row[0]), [out_row1]"r"(out_row[1]),
+ [out_row2]"r"(out_row[2]), [out_row3]"r"(out_row[3]),
+ [sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
+ :"memory"
+ );
+
+ out_row[0] += 4;
+ out_row[1] += 4;
+ out_row[2] += 4;
+ out_row[3] += 4;
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically_LS3_impl(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ uint64_t tmp;
+ int width = pixel_width & ~3;
+ double zero, sra, coeff16h, coeff16l;
+ double accum0h, accum0l, accum1h, accum1l;
+ double accum2h, accum2l, accum3h, accum3l;
+ const void *src;
+ int out_x;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [sra]"=f"(sra), [tmp]"=&r"(tmp)
+ :[sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
+ );
+
+ // Output four pixels per iteration (16 bytes).
+ for (out_x = 0; out_x < width; out_x += 4) {
+ // Accumulated result for each pixel. 32 bits per RGBA channel.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ _mm_xor(accum3, accum3, accum3)
+ ".set pop \n\t"
+ :[accum0h]"=f"(accum0h), [accum0l]"=f"(accum0l),
+ [accum1h]"=f"(accum1h), [accum1l]"=f"(accum1l),
+ [accum2h]"=f"(accum2h), [accum2l]"=f"(accum2l),
+ [accum3h]"=f"(accum3h), [accum3l]"=f"(accum3l)
+ );
+
+ // Convolve with one filter coefficient per iteration.
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+
+ src = reinterpret_cast<const void*>(
+ &source_data_rows[filter_y][out_x << 2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Duplicate the filter coefficient 8 times.
+ // [16] cj cj cj cj cj cj cj cj
+ "gsldlc1 %[coeff16l], 7+%[fval] \n\t"
+ "gsldrc1 %[coeff16l], %[fval] \n\t"
+ "pshufh %[coeff16l], %[coeff16l], %[zerol] \n\t"
+ "mov.d %[coeff16h], %[coeff16l] \n\t"
+ // Load four pixels (16 bytes) together.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t"
+ // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0 b0 g0 r0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum0, accum0, t)
+ // [32] a1 b1 g1 r1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum1, accum1, t)
+ // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [fval]"m"(filter_values[filter_y]),
+ [src]"r"(src)
+ );
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // [32] a2 b2 g2 r2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum2, accum2, t)
+ // [32] a3 b3 g3 r3
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum3, accum3, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [mul_hih]"+f"(mul_hih), [mul_hil]"+f"(mul_hil),
+ [mul_loh]"+f"(mul_loh), [mul_lol]"+f"(mul_lol),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l)
+ );
+ }
+
+ double t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Shift right for fixed point implementation.
+ _mm_psraw(accum0, accum0, sra)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_psraw(accum2, accum2, sra)
+ _mm_psraw(accum3, accum3, sra)
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packsswh(accum0, accum0, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_packsswh(accum2, accum2, accum3, t)
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packushb(accum0, accum0, accum2, t)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l),
+ [t]"=&f"(t)
+ :[sra]"f"(sra)
+ );
+
+ if (has_alpha) {
+ double ah, al, bh, bl, srl8, srl16, sll24;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 8 \n\t"
+ "mtc1 %[tmp], %[srl8] \n\t"
+ "li %[tmp], 16 \n\t"
+ "mtc1 %[tmp], %[srl16] \n\t"
+ "li %[tmp], 24 \n\t"
+ "mtc1 %[tmp], %[sll24] \n\t"
+ // Compute the max(ri, gi, bi) for each pixel.
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ _mm_psraw(a, accum0, srl8)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, accum0) // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ _mm_psrlw(a, accum0, srl16)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, b) // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ _mm_psllw(b, b, sll24)
+ // Make sure the value of alpha channel is always larger than maximum
+ // value of color channels.
+ _mm_pmaxub(accum0, b, accum0)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp), [ah]"=&f"(ah), [al]"=&f"(al),
+ [bh]"=&f"(bh), [bl]"=&f"(bl), [srl8]"=&f"(srl8),
+ [srl16]"=&f"(srl16), [sll24]"=&f"(sll24)
+ );
+ } else {
+ double maskh, maskl;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Set value of alpha channels to 0xFF.
+ "li %[tmp], 0xff000000 \n\t"
+ "mtc1 %[tmp], %[maskl] \n\t"
+ "punpcklwd %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ _mm_or(accum0, accum0, mask)
+ ".set pop \n\t"
+ :[maskh]"=&f"(maskh), [maskl]"=&f"(maskl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp)
+ );
+ }
+
+ // Store the convolution result (16 bytes) and advance the pixel pointers.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gssdlc1 %[accum0h], 0xf(%[out_row]) \n\t"
+ "gssdrc1 %[accum0h], 0x8(%[out_row]) \n\t"
+ "gssdlc1 %[accum0l], 0x7(%[out_row]) \n\t"
+ "gssdrc1 %[accum0l], 0x0(%[out_row]) \n\t"
+ ".set pop \n\t"
+ ::[accum0h]"f"(accum0h), [accum0l]"f"(accum0l),
+ [out_row]"r"(out_row)
+ :"memory"
+ );
+ out_row += 16;
+ }
+
+ // When the width of the output is not divisible by 4, We need to save one
+ // pixel (4 bytes) each time. And also the fourth pixel is always absent.
+ if (pixel_width & 3) {
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ ".set pop \n\t"
+ :[accum0h]"=&f"(accum0h), [accum0l]"=&f"(accum0l),
+ [accum1h]"=&f"(accum1h), [accum1l]"=&f"(accum1l),
+ [accum2h]"=&f"(accum2h), [accum2l]"=&f"(accum2l)
+ );
+ for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
+ double src8h, src8l, src16h, src16l;
+ double th, tl, mul_hih, mul_hil, mul_loh, mul_lol;
+ src = reinterpret_cast<const void*>(
+ &source_data_rows[filter_y][out_x<<2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeff16l], 7+%[fval] \n\t"
+ "gsldrc1 %[coeff16l], %[fval] \n\t"
+ "pshufh %[coeff16l], %[coeff16l], %[zerol] \n\t"
+ "mov.d %[coeff16h], %[coeff16l] \n\t"
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t"
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0 b0 g0 r0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum0, accum0, t)
+ // [32] a1 b1 g1 r1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum1, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a2 b2 g2 r2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum2, accum2, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [fval]"m"(filter_values[filter_y]),
+ [src]"r"(src)
+ );
+ }
+
+ double t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_psraw(accum0, accum0, sra)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_psraw(accum2, accum2, sra)
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packsswh(accum0, accum0, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_packsswh(accum2, accum2, zero, t)
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packushb(accum0, accum0, accum2, t)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [t]"=&f"(t)
+ :[zeroh]"f"(zero), [zerol]"f"(zero), [sra]"f"(sra)
+ );
+ if (has_alpha) {
+ double ah, al, bh, bl, srl8, srl16, sll24;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 8 \n\t"
+ "mtc1 %[tmp], %[srl8] \n\t"
+ "li %[tmp], 16 \n\t"
+ "mtc1 %[tmp], %[srl16] \n\t"
+ "li %[tmp], 24 \n\t"
+ "mtc1 %[tmp], %[sll24] \n\t"
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ _mm_psrlw(a, accum0, srl8)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, accum0) // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ _mm_psrlw(a, accum0, srl16)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, b) // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ _mm_psllw(b, b, sll24)
+ _mm_pmaxub(accum0, b, accum0)
+ ".set pop \n\t"
+ :[ah]"=&f"(ah), [al]"=&f"(al), [bh]"=&f"(bh), [bl]"=&f"(bl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l), [tmp]"=&r"(tmp),
+ [srl8]"=&f"(srl8), [srl16]"=&f"(srl16), [sll24]"=&f"(sll24)
+ );
+ } else {
+ double maskh, maskl;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Set value of alpha channels to 0xFF.
+ "li %[tmp], 0xff000000 \n\t"
+ "mtc1 %[tmp], %[maskl] \n\t"
+ "punpcklwd %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ _mm_or(accum0, accum0, mask)
+ ".set pop \n\t"
+ :[maskh]"=&f"(maskh), [maskl]"=&f"(maskl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp)
+ );
+ }
+
+ double s4, s64;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 4 \n\t"
+ "mtc1 %[tmp], %[s4] \n\t"
+ "li %[tmp], 64 \n\t"
+ "mtc1 %[tmp], %[s64] \n\t"
+ ".set pop \n\t"
+ :[s4]"=f"(s4), [s64]"=f"(s64),
+ [tmp]"=&r"(tmp)
+ );
+ for (int out_x = width; out_x < pixel_width; out_x++) {
+ double t;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "swc1 %[accum0l], (%[out_row]) \n\t"
+ _mm_psrlq(accum0, accum0, s4, s64, t)
+ ".set pop \n\t"
+ :[t]"=&f"(t),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l)
+ :[out_row]"r"(out_row), [s4]"f"(s4), [s64]"f"(s64)
+ :"memory"
+ );
+ out_row += 4;
+ }
+ }
+}
+
+void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_LS3_impl<true>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ } else {
+ ConvolveVertically_LS3_impl<false>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ }
+}
+
+} // namespace skia
+
+#endif /* _MIPS_ARCH_LOONGSON3A */
diff --git a/system/graphics/2d/convolverLS3.h b/system/graphics/2d/convolverLS3.h
new file mode 100644
index 000000000..af531c927
--- /dev/null
+++ b/system/graphics/2d/convolverLS3.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2014-2015 The Chromium Authors. 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 SKIA_EXT_CONVOLVER_LS3_H_
+#define SKIA_EXT_CONVOLVER_LS3_H_
+
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/include/core/SkTypes.h"
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+void ConvolveHorizontally_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row);
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// Process one pixel at a time.
+void ConvolveHorizontally1_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row);
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]);
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_LS3_H_
diff --git a/system/graphics/2d/convolverSSE2.cpp b/system/graphics/2d/convolverSSE2.cpp
new file mode 100644
index 000000000..20ddd45f1
--- /dev/null
+++ b/system/graphics/2d/convolverSSE2.cpp
@@ -0,0 +1,471 @@
+// Copyright (c) 2006-2011 The Chromium Authors. 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 "convolver.h"
+#include <algorithm>
+#include "skia/include/core/SkTypes.h"
+
+#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[4];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ __m128i accum = _mm_setzero_si128();
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const __m128i* row_to_filter =
+ reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]);
+
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
+
+ // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
+ __m128i coeff, coeff16;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Load four pixels => unpack the first two pixels to 16 bits =>
+ // multiply with coefficients => accumulate the convolution result.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src8 = _mm_loadu_si128(row_to_filter);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0*c0 b0*c0 g0*c0 r0*c0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a1*c1 b1*c1 g1*c1 r1*c1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Duplicate 3rd and 4th coefficients for all channels =>
+ // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients
+ // => accumulate the convolution results.
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ // [16] a3 g3 b3 r3 a2 g2 b2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2*c2 b2*c2 g2*c2 r2*c2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a3*c3 b3*c3 g3*c3 r3*c3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Advance the pixel and coefficients pointers.
+ row_to_filter += 1;
+ filter_values += 4;
+ }
+
+ // When |filter_length| is not divisible by 4, we need to decimate some of
+ // the filter coefficient that was loaded incorrectly to zero; Other than
+ // that the algorithm is same with above, exceot that the 4th pixel will be
+ // always absent.
+ int r = filter_length&3;
+ if (r) {
+ // Note: filter_values must be padded to align_up(filter_offset, 8).
+ __m128i coeff, coeff16;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r]);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Note: line buffer must be padded to align_up(filter_offset, 16).
+ // We resolve this by use C-version for the last horizontal line.
+ __m128i src8 = _mm_loadu_si128(row_to_filter);
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ accum = _mm_packs_epi32(accum, zero);
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ accum = _mm_packus_epi16(accum, zero);
+
+ // Store the pixel value of 32 bits.
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum);
+ out_row += 4;
+ }
+}
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+ int num_values = filter.num_values();
+
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[4];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // four pixels in a column per iteration.
+ __m128i accum0 = _mm_setzero_si128();
+ __m128i accum1 = _mm_setzero_si128();
+ __m128i accum2 = _mm_setzero_si128();
+ __m128i accum3 = _mm_setzero_si128();
+ int start = (filter_offset<<2);
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) {
+ __m128i coeff, coeff16lo, coeff16hi;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+#define ITERATION(src, accum) \
+ src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \
+ src16 = _mm_unpacklo_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16lo); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ src16 = _mm_unpackhi_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16hi); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t)
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+
+ start += 16;
+ filter_values += 4;
+ }
+
+ int r = filter_length & 3;
+ if (r) {
+ // Note: filter_values must be padded to align_up(filter_offset, 8);
+ __m128i coeff;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r]);
+
+ __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ /* c1 c1 c1 c1 c0 c0 c0 c0 */
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum0 = _mm_packs_epi32(accum0, zero);
+ accum0 = _mm_packus_epi16(accum0, zero);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_packs_epi32(accum1, zero);
+ accum1 = _mm_packus_epi16(accum1, zero);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_packs_epi32(accum2, zero);
+ accum2 = _mm_packus_epi16(accum2, zero);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_packs_epi32(accum3, zero);
+ accum3 = _mm_packus_epi16(accum3, zero);
+
+ *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0);
+ *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1);
+ *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2);
+ *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3);
+
+ out_row[0] += 4;
+ out_row[1] += 4;
+ out_row[2] += 4;
+ out_row[3] += 4;
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ int width = pixel_width & ~3;
+
+ __m128i zero = _mm_setzero_si128();
+ __m128i accum0, accum1, accum2, accum3, coeff16;
+ const __m128i* src;
+ // Output four pixels per iteration (16 bytes).
+ for (int out_x = 0; out_x < width; out_x += 4) {
+
+ // Accumulated result for each pixel. 32 bits per RGBA channel.
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ accum3 = _mm_setzero_si128();
+
+ // Convolve with one filter coefficient per iteration.
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+
+ // Duplicate the filter coefficient 8 times.
+ // [16] cj cj cj cj cj cj cj cj
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+
+ // Load four pixels (16 bytes) together.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][out_x << 2]);
+ __m128i src8 = _mm_loadu_si128(src);
+
+ // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+
+ // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ // [32] a3 b3 g3 r3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum3 = _mm_add_epi32(accum3, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, accum3);
+
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+
+ if (has_alpha) {
+ // Compute the max(ri, gi, bi) for each pixel.
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+
+ // Make sure the value of alpha channel is always larger than maximum
+ // value of color channels.
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ // Set value of alpha channels to 0xFF.
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ // Store the convolution result (16 bytes) and advance the pixel pointers.
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0);
+ out_row += 16;
+ }
+
+ // When the width of the output is not divisible by 4, We need to save one
+ // pixel (4 bytes) each time. And also the fourth pixel is always absent.
+ if (pixel_width & 3) {
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][width<<2]);
+ __m128i src8 = _mm_loadu_si128(src);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, zero);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+ if (has_alpha) {
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ for (int out_x = width; out_x < pixel_width; out_x++) {
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0);
+ accum0 = _mm_srli_si128(accum0, 4);
+ out_row += 4;
+ }
+ }
+}
+
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_SSE2_impl<true>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ } else {
+ ConvolveVertically_SSE2_impl<false>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ }
+}
+
+} // namespace skia
diff --git a/system/graphics/2d/convolverSSE2.h b/system/graphics/2d/convolverSSE2.h
new file mode 100644
index 000000000..a54ce676b
--- /dev/null
+++ b/system/graphics/2d/convolverSSE2.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2006-2011 The Chromium Authors. 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 SKIA_EXT_CONVOLVER_SSE_H_
+#define SKIA_EXT_CONVOLVER_SSE_H_
+
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/include/core/SkTypes.h"
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row);
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]);
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_SSE_H_
diff --git a/system/graphics/2d/genshaders.sh b/system/graphics/2d/genshaders.sh
new file mode 100644
index 000000000..74b1a3d7b
--- /dev/null
+++ b/system/graphics/2d/genshaders.sh
@@ -0,0 +1,10 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+fxc ShadersD2D.fx -nologo -FhShadersD2D.h -Tfx_4_0 -Vn d2deffect
+fxc ShadersD2D1.hlsl -ESampleRadialGradientPS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientPS
+cat tmpfile > ShadersD2D1.h
+fxc ShadersD2D1.hlsl -ESampleRadialGradientA0PS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientA0PS
+cat tmpfile >> ShadersD2D1.h
+rm tmpfile
diff --git a/system/graphics/2d/gfx2d.sln b/system/graphics/2d/gfx2d.sln
new file mode 100644
index 000000000..40a137a1c
--- /dev/null
+++ b/system/graphics/2d/gfx2d.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx2d", "gfx2d.vcxproj", "{49E973D7-53C9-3D66-BE58-52125FAE193D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest", "unittest\unittest.vcxproj", "{CCF4BC8B-0CED-47CA-B621-ABF1832527D9}"
+ ProjectSection(ProjectDependencies) = postProject
+ {49E973D7-53C9-3D66-BE58-52125FAE193D} = {49E973D7-53C9-3D66-BE58-52125FAE193D}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Debug|Win32.Build.0 = Debug|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Release|Win32.ActiveCfg = Release|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Release|Win32.Build.0 = Release|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Debug|Win32.Build.0 = Debug|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Release|Win32.ActiveCfg = Release|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/system/graphics/2d/gfx2d.vcxproj b/system/graphics/2d/gfx2d.vcxproj
new file mode 100644
index 000000000..8b4607899
--- /dev/null
+++ b/system/graphics/2d/gfx2d.vcxproj
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <ExecutablePath>$(DXSDK_DIR)\Utilities\bin\x86;$(ExecutablePath)</ExecutablePath>
+ <IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <PreBuildEvent>
+ <Command>xcopy $(ProjectDir)..\..\mfbt\*.h mozilla\ /Y</Command>
+ <Message>Copying MFBT files</Message>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>./</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="2D.h" />
+ <ClInclude Include="BaseMargin.h" />
+ <ClInclude Include="BasePoint.h" />
+ <ClInclude Include="BaseRect.h" />
+ <ClInclude Include="BaseSize.h" />
+ <ClInclude Include="DrawEventRecorder.h" />
+ <ClInclude Include="DrawTargetD2D.h" />
+ <ClInclude Include="DrawTargetDual.h" />
+ <ClInclude Include="DrawTargetRecording.h" />
+ <ClInclude Include="GradientStopsD2D.h" />
+ <ClInclude Include="HelpersD2D.h" />
+ <ClInclude Include="ImageScaling.h" />
+ <ClInclude Include="Logging.h" />
+ <ClInclude Include="Matrix.h" />
+ <ClInclude Include="PathD2D.h" />
+ <ClInclude Include="PathHelpers.h" />
+ <ClInclude Include="PathRecording.h" />
+ <ClInclude Include="Point.h" />
+ <ClInclude Include="RecordedEvent.h" />
+ <ClInclude Include="RecordingTypes.h" />
+ <ClInclude Include="Rect.h" />
+ <ClInclude Include="ScaledFontBase.h" />
+ <ClInclude Include="ScaledFontDWrite.h" />
+ <ClInclude Include="SourceSurfaceD2D.h" />
+ <ClInclude Include="SourceSurfaceD2DTarget.h" />
+ <ClInclude Include="SourceSurfaceRawData.h" />
+ <ClInclude Include="Tools.h" />
+ <ClInclude Include="Types.h" />
+ <ClInclude Include="UserData.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="DrawEventRecorder.cpp" />
+ <ClCompile Include="DrawTargetD2D.cpp" />
+ <ClCompile Include="DrawTargetDual.cpp" />
+ <ClCompile Include="DrawTargetRecording.cpp" />
+ <ClCompile Include="Factory.cpp" />
+ <ClCompile Include="ImageScaling.cpp" />
+ <ClCompile Include="ImageScalingSSE2.cpp" />
+ <ClCompile Include="Matrix.cpp" />
+ <ClCompile Include="PathD2D.cpp" />
+ <ClCompile Include="PathRecording.cpp" />
+ <ClCompile Include="RecordedEvent.cpp" />
+ <ClCompile Include="ScaledFontBase.cpp" />
+ <ClCompile Include="ScaledFontDWrite.cpp" />
+ <ClCompile Include="SourceSurfaceD2D.cpp" />
+ <ClCompile Include="SourceSurfaceD2DTarget.cpp" />
+ <ClCompile Include="SourceSurfaceRawData.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Makefile.in" />
+ <CustomBuild Include="ShadersD2D.fx">
+ <FileType>Document</FileType>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">fxc /Tfx_4_0 /FhShadersD2D.h ShadersD2D.fx /Vn d2deffect</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ShadersD2D.h</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/system/graphics/2d/image_operations.cpp b/system/graphics/2d/image_operations.cpp
new file mode 100644
index 000000000..e9492a150
--- /dev/null
+++ b/system/graphics/2d/image_operations.cpp
@@ -0,0 +1,385 @@
+// Copyright (c) 2006-2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2018 Mark Straver BASc. 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 "base/basictypes.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "image_operations.h"
+
+#include "base/stack_container.h"
+#include "convolver.h"
+#include "skia/include/core/SkColorPriv.h"
+#include "skia/include/core/SkBitmap.h"
+#include "skia/include/core/SkRect.h"
+#include "skia/include/core/SkFontLCDConfig.h"
+
+namespace skia {
+
+namespace resize {
+
+// TODO(egouriou): Take advantage of periods in the convolution.
+// Practical resizing filters are periodic outside of the border area.
+// For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
+// source become p pixels in the destination) will have a period of p.
+// A nice consequence is a period of 1 when downscaling by an integral
+// factor. Downscaling from typical display resolutions is also bound
+// to produce interesting periods as those are chosen to have multiple
+// small factors.
+// Small periods reduce computational load and improve cache usage if
+// the coefficients can be shared. For periods of 1 we can consider
+// loading the factors only once outside the borders.
+void ComputeFilters(ImageOperations::ResizeMethod method,
+ int src_size, int dst_size,
+ int dest_subset_lo, int dest_subset_size,
+ ConvolutionFilter1D* output) {
+ // method_ will only ever refer to an "algorithm method".
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ float scale = static_cast<float>(dst_size) / static_cast<float>(src_size);
+
+ int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi)
+
+ // When we're doing a magnification, the scale will be larger than one. This
+ // means the destination pixels are much smaller than the source pixels, and
+ // that the range covered by the filter won't necessarily cover any source
+ // pixel boundaries. Therefore, we use these clamped values (max of 1) for
+ // some computations.
+ float clamped_scale = std::min(1.0f, scale);
+
+ float src_support = GetFilterSupport(method, clamped_scale) / clamped_scale;
+
+ // Speed up the divisions below by turning them into multiplies.
+ float inv_scale = 1.0f / scale;
+
+ StackVector<float, 64> filter_values;
+ StackVector<int16_t, 64> fixed_filter_values;
+
+ // Loop over all pixels in the output range. We will generate one set of
+ // filter values for each one. Those values will tell us how to blend the
+ // source pixels to compute the destination pixel.
+ for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
+ dest_subset_i++) {
+ // Reset the arrays. We don't declare them inside so they can re-use the
+ // same malloc-ed buffer.
+ filter_values->clear();
+ fixed_filter_values->clear();
+
+ // This is the pixel in the source directly under the pixel in the dest.
+ // Note that we base computations on the "center" of the pixels. To see
+ // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
+ // downscale should "cover" the pixels around the pixel with *its center*
+ // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
+ // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
+ float src_pixel = (static_cast<float>(dest_subset_i) + 0.5f) * inv_scale;
+
+ // Compute the (inclusive) range of source pixels the filter covers.
+ int src_begin = std::max(0, FloorInt(src_pixel - src_support));
+ int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
+
+ // Compute the unnormalized filter value at each location of the source
+ // it covers.
+ float filter_sum = 0.0f; // Sum of the filter values for normalizing.
+ for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
+ cur_filter_pixel++) {
+ // Distance from the center of the filter, this is the filter coordinate
+ // in source space. We also need to consider the center of the pixel
+ // when comparing distance against 'src_pixel'. In the 5x downscale
+ // example used above the distance from the center of the filter to
+ // the pixel with coordinates (2, 2) should be 0, because its center
+ // is at (2.5, 2.5).
+ float src_filter_dist =
+ ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel);
+
+ // Since the filter really exists in dest space, map it there.
+ float dest_filter_dist = src_filter_dist * clamped_scale;
+
+ // Compute the filter value at that location.
+ float filter_value = ComputeFilter(method, dest_filter_dist);
+ filter_values->push_back(filter_value);
+
+ filter_sum += filter_value;
+ }
+
+ // The filter must be normalized so that we don't affect the brightness of
+ // the image. Convert to normalized fixed point.
+ int16_t fixed_sum = 0;
+ for (size_t i = 0; i < filter_values->size(); i++) {
+ int16_t cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
+ fixed_sum += cur_fixed;
+ fixed_filter_values->push_back(cur_fixed);
+ }
+
+ // The conversion to fixed point will leave some rounding errors, which
+ // we add back in to avoid affecting the brightness of the image. We
+ // arbitrarily add this to the center of the filter array (this won't always
+ // be the center of the filter function since it could get clipped on the
+ // edges, but it doesn't matter enough to worry about that case).
+ int16_t leftovers = output->FloatToFixed(1.0f) - fixed_sum;
+ fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
+
+ // Now it's ready to go.
+ output->AddFilter(src_begin, &fixed_filter_values[0],
+ static_cast<int>(fixed_filter_values->size()));
+ }
+
+ output->PaddingForSIMD(8);
+}
+
+} // namespace resize
+
+ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
+ ImageOperations::ResizeMethod method) {
+ // If we already have an "Algorithm Method", just return that.
+ if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD &&
+ method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) {
+ return method;
+ }
+ // Convert any "Quality Method" into an "Algorithm Method"
+ switch (method) {
+ case ImageOperations::RESIZE_GOOD:
+ // Users of RESIZE_GOOD are willing to trade quality to get speed.
+ // In visual tests we see that Hamming-1 is not as good as
+ // Lanczos-2, however it is about 40% faster, and Lanczos-2 itself is
+ // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed
+ // an unacceptable trade-off between quality and speed due to the limited
+ // pixel space it operates in, so we pick Lanczos-2 here.
+ case ImageOperations::RESIZE_BETTER:
+ return ImageOperations::RESIZE_LANCZOS2;
+ default:
+ return ImageOperations::RESIZE_LANCZOS3;
+ }
+}
+
+// Resize ----------------------------------------------------------------------
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels /* = nullptr */) {
+ if (method == ImageOperations::RESIZE_SUBPIXEL)
+ return ResizeSubpixel(source, dest_width, dest_height, dest_subset);
+ else
+ return ResizeBasic(source, method, dest_width, dest_height, dest_subset,
+ dest_pixels);
+}
+
+// static
+SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset) {
+ // Currently only works on Linux/BSD because these are the only platforms
+ // where SkFontLCDConfig::GetSubpixelOrder is defined.
+#if defined(XP_UNIX)
+ // Understand the display.
+ const SkFontLCDConfig::LCDOrder order = SkFontLCDConfig::GetSubpixelOrder();
+ const SkFontLCDConfig::LCDOrientation orientation =
+ SkFontLCDConfig::GetSubpixelOrientation();
+
+ // Decide on which dimension, if any, to deploy subpixel rendering.
+ int w = 1;
+ int h = 1;
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ w = dest_width < source.width() ? 3 : 1;
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ h = dest_height < source.height() ? 3 : 1;
+ break;
+ }
+
+ // Resize the image.
+ const int width = dest_width * w;
+ const int height = dest_height * h;
+ SkIRect subset = { dest_subset.fLeft, dest_subset.fTop,
+ dest_subset.fLeft + dest_subset.width() * w,
+ dest_subset.fTop + dest_subset.height() * h };
+ SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width,
+ height, subset);
+ const int row_words = img.rowBytes() / 4;
+ if (w == 1 && h == 1)
+ return img;
+
+ // Render into subpixels.
+ SkBitmap result;
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
+
+
+ result.allocPixels(info);
+ if (!result.readyToDraw())
+ return img;
+
+ SkAutoLockPixels locker(img);
+ if (!img.readyToDraw())
+ return img;
+
+ uint32_t* src_row = img.getAddr32(0, 0);
+ uint32_t* dst_row = result.getAddr32(0, 0);
+ for (int y = 0; y < dest_subset.height(); y++) {
+ uint32_t* src = src_row;
+ uint32_t* dst = dst_row;
+ for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) {
+ uint8_t r = 0, g = 0, b = 0, a = 0;
+ switch (order) {
+ case SkFontLCDConfig::kRGB_LCDOrder:
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ r = SkGetPackedR32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ b = SkGetPackedB32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ r = SkGetPackedR32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ b = SkGetPackedB32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontLCDConfig::kBGR_LCDOrder:
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ b = SkGetPackedB32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ r = SkGetPackedR32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ b = SkGetPackedB32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ r = SkGetPackedR32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontLCDConfig::kNONE_LCDOrder:
+ break;
+ }
+ // Premultiplied alpha is very fragile.
+ a = a > r ? a : r;
+ a = a > g ? a : g;
+ a = a > b ? a : b;
+ *dst = SkPackARGB32(a, r, g, b);
+ }
+ src_row += h * row_words;
+ dst_row += result.rowBytes() / 4;
+ }
+ result.setAlphaType(img.alphaType());
+ return result;
+#else
+ return SkBitmap();
+#endif // OS_POSIX && !OS_MACOSX
+}
+
+// static
+SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels /* = nullptr */) {
+ // Ensure that the ResizeMethod enumeration is sound.
+ SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
+ (method <= RESIZE_LAST_QUALITY_METHOD)) ||
+ ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= RESIZE_LAST_ALGORITHM_METHOD)));
+
+ // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
+ // return empty.
+ if (source.width() < 1 || source.height() < 1 ||
+ dest_width < 1 || dest_height < 1)
+ return SkBitmap();
+
+ method = ResizeMethodToAlgorithmMethod(method);
+ // Check that we deal with an "algorithm methods" from this point onward.
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ SkAutoLockPixels locker(source);
+ if (!source.readyToDraw())
+ return SkBitmap();
+
+ ConvolutionFilter1D x_filter;
+ ConvolutionFilter1D y_filter;
+
+ resize::ComputeFilters(method, source.width(), dest_width, dest_subset.fLeft, dest_subset.width(), &x_filter);
+ resize::ComputeFilters(method, source.height(), dest_height, dest_subset.fTop, dest_subset.height(), &y_filter);
+
+ // Get a source bitmap encompassing this touched area. We construct the
+ // offsets and row strides such that it looks like a new bitmap, while
+ // referring to the old data.
+ const uint8_t* source_subset =
+ reinterpret_cast<const uint8_t*>(source.getPixels());
+
+ // Convolve into the result.
+ SkBitmap result;
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
+
+ if (dest_pixels) {
+ result.installPixels(info, dest_pixels, info.minRowBytes());
+ } else {
+ result.allocPixels(info);
+ }
+
+ if (!result.readyToDraw())
+ return SkBitmap();
+
+ BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
+ !source.isOpaque(), x_filter, y_filter,
+ static_cast<int>(result.rowBytes()),
+ static_cast<unsigned char*>(result.getPixels()));
+
+ // Preserve the "opaque" flag for use as an optimization later.
+ result.setAlphaType(source.alphaType());
+
+ return result;
+}
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ void* dest_pixels /* = nullptr */) {
+ SkIRect dest_subset = { 0, 0, dest_width, dest_height };
+ return Resize(source, method, dest_width, dest_height, dest_subset,
+ dest_pixels);
+}
+
+} // namespace skia
diff --git a/system/graphics/2d/image_operations.h b/system/graphics/2d/image_operations.h
new file mode 100644
index 000000000..8e3191363
--- /dev/null
+++ b/system/graphics/2d/image_operations.h
@@ -0,0 +1,285 @@
+// Copyright (c) 2006-2011 The Chromium Authors. 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 SKIA_EXT_IMAGE_OPERATIONS_H_
+#define SKIA_EXT_IMAGE_OPERATIONS_H_
+
+#include "skia/include/core/SkTypes.h"
+#include "Types.h"
+#include "convolver.h"
+#include "skia/include/core/SkRect.h"
+
+class SkBitmap;
+struct SkIRect;
+
+namespace skia {
+
+class ImageOperations {
+ public:
+ enum ResizeMethod {
+ //
+ // Quality Methods
+ //
+ // Those enumeration values express a desired quality/speed tradeoff.
+ // They are translated into an algorithm-specific method that depends
+ // on the capabilities (CPU, GPU) of the underlying platform.
+ // It is possible for all three methods to be mapped to the same
+ // algorithm on a given platform.
+
+ // Good quality resizing. Fastest resizing with acceptable visual quality.
+ // This is typically intended for use during interactive layouts
+ // where slower platforms may want to trade image quality for large
+ // increase in resizing performance.
+ //
+ // For example the resizing implementation may devolve to linear
+ // filtering if this enables GPU acceleration to be used.
+ //
+ // Note that the underlying resizing method may be determined
+ // on the fly based on the parameters for a given resize call.
+ // For example an implementation using a GPU-based linear filter
+ // in the common case may still use a higher-quality software-based
+ // filter in cases where using the GPU would actually be slower - due
+ // to too much latency - or impossible - due to image format or size
+ // constraints.
+ RESIZE_GOOD,
+
+ // Medium quality resizing. Close to high quality resizing (better
+ // than linear interpolation) with potentially some quality being
+ // traded-off for additional speed compared to RESIZE_BEST.
+ //
+ // This is intended, for example, for generation of large thumbnails
+ // (hundreds of pixels in each dimension) from large sources, where
+ // a linear filter would produce too many artifacts but where
+ // a RESIZE_HIGH might be too costly time-wise.
+ RESIZE_BETTER,
+
+ // High quality resizing. The algorithm is picked to favor image quality.
+ RESIZE_BEST,
+
+ //
+ // Algorithm-specific enumerations
+ //
+
+ // Box filter. This is a weighted average of all of the pixels touching
+ // the destination pixel. For enlargement, this is nearest neighbor.
+ //
+ // You probably don't want this, it is here for testing since it is easy to
+ // compute. Use RESIZE_LANCZOS3 instead.
+ RESIZE_BOX,
+
+ // 1-cycle Hamming filter. This is tall is the middle and falls off towards
+ // the window edges but without going to 0. This is about 40% faster than
+ // a 2-cycle Lanczos.
+ RESIZE_HAMMING1,
+
+ // 2-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then returns to zero. Does not provide as good a frequency
+ // response as a 3-cycle Lanczos but is roughly 30% faster.
+ RESIZE_LANCZOS2,
+
+ // 3-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then oscillates 2 more times. It gives nice sharp edges.
+ RESIZE_LANCZOS3,
+
+ // Lanczos filter + subpixel interpolation. If subpixel rendering is not
+ // appropriate we automatically fall back to Lanczos.
+ RESIZE_SUBPIXEL,
+
+ // enum aliases for first and last methods by algorithm or by quality.
+ RESIZE_FIRST_QUALITY_METHOD = RESIZE_GOOD,
+ RESIZE_LAST_QUALITY_METHOD = RESIZE_BEST,
+ RESIZE_FIRST_ALGORITHM_METHOD = RESIZE_BOX,
+ RESIZE_LAST_ALGORITHM_METHOD = RESIZE_SUBPIXEL,
+ };
+
+ // Resizes the given source bitmap using the specified resize method, so that
+ // the entire image is (dest_size) big. The dest_subset is the rectangle in
+ // this destination image that should actually be returned.
+ //
+ // The output image will be (dest_subset.width(), dest_subset.height()). This
+ // will save work if you do not need the entire bitmap.
+ //
+ // The destination subset must be smaller than the destination image.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels = nullptr);
+
+ // Alternate version for resizing and returning the entire bitmap rather than
+ // a subset.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ void* dest_pixels = nullptr);
+
+ private:
+ ImageOperations(); // Class for scoping only.
+
+ // Supports all methods except RESIZE_SUBPIXEL.
+ static SkBitmap ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels = nullptr);
+
+ // Subpixel renderer.
+ static SkBitmap ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset);
+};
+
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+ return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+ return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+inline float EvalBox(float x) {
+ return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+// lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+// sinc(x) = sin(pi*x) / (pi*x);
+inline float EvalLanczos(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the discontinuity at the origin.
+ float xpi = x * static_cast<float>(M_PI);
+ return (sinf(xpi) / xpi) * // sinc(x)
+ sinf(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
+}
+
+// Evaluates the Hamming filter of the given filter size window for the given
+// position.
+//
+// The filter covers [-filter_size, +filter_size]. Outside of this window
+// the value of the function is 0. Inside of the window, the value is sinus
+// cardinal multiplied by a recentered Hamming function. The traditional
+// Hamming formula for a window of size N and n ranging in [0, N-1] is:
+// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
+// In our case we want the function centered for x == 0 and at its minimum
+// on both ends of the window (x == +/- filter_size), hence the adjusted
+// formula:
+// hamming(x) = (0.54 -
+// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
+// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
+// = 0.54 + 0.46 * cos(pi * x / filter_size)
+inline float EvalHamming(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the sinc discontinuity at the origin.
+ const float xpi = x * static_cast<float>(M_PI);
+
+ return ((sinf(xpi) / xpi) * // sinc(x)
+ (0.54f + 0.46f * cosf(xpi / filter_size))); // hamming(x)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+
+namespace resize {
+
+ // Returns the number of pixels that the filer spans, in filter space (the
+ // destination image).
+ inline float GetFilterSupport(ImageOperations::ResizeMethod method,
+ float scale) {
+ switch (method) {
+ case ImageOperations::RESIZE_BOX:
+ // The box filter just scales with the image scaling.
+ return 0.5f; // Only want one side of the filter = /2.
+ case ImageOperations::RESIZE_HAMMING1:
+ // The Hamming filter takes as much space in the source image in
+ // each direction as the size of the window = 1 for Hamming1.
+ return 1.0f;
+ case ImageOperations::RESIZE_LANCZOS2:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 2 for Lanczos2.
+ return 2.0f;
+ case ImageOperations::RESIZE_LANCZOS3:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 3 for Lanczos3.
+ return 3.0f;
+ default:
+ return 1.0f;
+ }
+ }
+
+ // Computes one set of filters either horizontally or vertically. The caller
+ // will specify the "min" and "max" rather than the bottom/top and
+ // right/bottom so that the same code can be re-used in each dimension.
+ //
+ // |src_depend_lo| and |src_depend_size| gives the range for the source
+ // depend rectangle (horizontally or vertically at the caller's discretion
+ // -- see above for what this means).
+ //
+ // Likewise, the range of destination values to compute and the scale factor
+ // for the transform is also specified.
+ void ComputeFilters(ImageOperations::ResizeMethod method,
+ int src_size, int dst_size,
+ int dest_subset_lo, int dest_subset_size,
+ ConvolutionFilter1D* output);
+
+ // Computes the filter value given the coordinate in filter space.
+ inline float ComputeFilter(ImageOperations::ResizeMethod method, float pos) {
+ switch (method) {
+ case ImageOperations::RESIZE_BOX:
+ return EvalBox(pos);
+ case ImageOperations::RESIZE_HAMMING1:
+ return EvalHamming(1, pos);
+ case ImageOperations::RESIZE_LANCZOS2:
+ return EvalLanczos(2, pos);
+ case ImageOperations::RESIZE_LANCZOS3:
+ return EvalLanczos(3, pos);
+ default:
+ return 0;
+ }
+ }
+}
+
+} // namespace skia
+
+#endif // SKIA_EXT_IMAGE_OPERATIONS_H_
diff --git a/system/graphics/2d/moz.build b/system/graphics/2d/moz.build
new file mode 100644
index 000000000..9a1a4bcb0
--- /dev/null
+++ b/system/graphics/2d/moz.build
@@ -0,0 +1,206 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla += [
+ 'GenericRefCounted.h',
+]
+
+EXPORTS.mozilla.gfx += [
+ '2D.h',
+ 'BaseCoord.h',
+ 'BaseMargin.h',
+ 'BasePoint.h',
+ 'BasePoint3D.h',
+ 'BasePoint4D.h',
+ 'BaseRect.h',
+ 'BaseSize.h',
+ 'BezierUtils.h',
+ 'Blur.h',
+ 'BorrowedContext.h',
+ 'Coord.h',
+ 'CriticalSection.h',
+ 'DataSurfaceHelpers.h',
+ 'DrawEventRecorder.h',
+ 'DrawTargetTiled.h',
+ 'Filters.h',
+ 'Helpers.h',
+ 'HelpersCairo.h',
+ 'IterableArena.h',
+ 'JobScheduler.h',
+ 'JobScheduler_posix.h',
+ 'JobScheduler_win32.h',
+ 'Logging.h',
+ 'LoggingConstants.h',
+ 'Matrix.h',
+ 'MatrixFwd.h',
+ 'NumericTools.h',
+ 'PathHelpers.h',
+ 'PatternHelpers.h',
+ 'Point.h',
+ 'Polygon.h',
+ 'Quaternion.h',
+ 'RecordedEvent.h',
+ 'RecordingTypes.h',
+ 'Rect.h',
+ 'Scale.h',
+ 'ScaleFactor.h',
+ 'ScaleFactors2D.h',
+ 'SourceSurfaceCairo.h',
+ 'SourceSurfaceRawData.h',
+ 'StackArray.h',
+ 'Tools.h',
+ 'Triangle.h',
+ 'Types.h',
+ 'UserData.h',
+]
+
+EXPORTS.mozilla.gfx += ['ssse3-scaler.h']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'DrawTargetD2D1.cpp',
+ 'ExtendInputEffectD2D1.cpp',
+ 'FilterNodeD2D1.cpp',
+ 'JobScheduler_win32.cpp',
+ 'NativeFontResourceDWrite.cpp',
+ 'NativeFontResourceGDI.cpp',
+ 'PathD2D.cpp',
+ 'RadialGradientEffectD2D1.cpp',
+ 'ScaledFontDWrite.cpp',
+ 'ScaledFontWin.cpp',
+ 'SourceSurfaceD2D1.cpp',
+ ]
+ DEFINES['WIN32'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
+ SOURCES += [
+ 'JobScheduler_posix.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ SOURCES += [
+ 'ScaledFontFontconfig.cpp',
+ ]
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'convolver.cpp',
+ 'DrawTargetSkia.cpp',
+ 'image_operations.cpp', # Uses _USE_MATH_DEFINES
+ 'PathSkia.cpp',
+ 'SourceSurfaceSkia.cpp',
+ ]
+ if CONFIG['CLANG_CXX']:
+ # Suppress warnings from Skia header files.
+ SOURCES['DrawTargetSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ SOURCES['PathSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ SOURCES['SourceSurfaceSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ EXPORTS.mozilla.gfx += [
+ 'HelpersSkia.h',
+ ]
+
+# Are we targeting x86 or x64? If so, build SSE2 files.
+if CONFIG['INTEL_ARCHITECTURE']:
+ SOURCES += [
+ 'BlurSSE2.cpp',
+ 'FilterProcessingSSE2.cpp',
+ 'ImageScalingSSE2.cpp',
+ 'ssse3-scaler.c',
+ ]
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'convolverSSE2.cpp',
+ ]
+ DEFINES['USE_SSE2'] = True
+ # The file uses SSE2 intrinsics, so it needs special compile flags on some
+ # compilers.
+ SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES['ssse3-scaler.c'].flags += CONFIG['SSSE3_FLAGS']
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+elif CONFIG['CPU_ARCH'].startswith('mips'):
+ SOURCES += [
+ 'BlurLS3.cpp',
+ ]
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'convolverLS3.cpp',
+ ]
+
+SOURCES += [
+ 'BezierUtils.cpp',
+ 'Blur.cpp',
+ 'DataSourceSurface.cpp',
+ 'DataSurfaceHelpers.cpp',
+ 'DrawEventRecorder.cpp',
+ 'DrawingJob.cpp',
+ 'DrawTarget.cpp',
+ 'DrawTargetCairo.cpp',
+ 'DrawTargetCapture.cpp',
+ 'DrawTargetDual.cpp',
+ 'DrawTargetRecording.cpp',
+ 'DrawTargetTiled.cpp',
+ 'Factory.cpp', # Need to suppress warnings in Skia header files.
+ 'FilterNodeSoftware.cpp',
+ 'FilterProcessing.cpp',
+ 'FilterProcessingScalar.cpp',
+ 'ImageScaling.cpp',
+ 'JobScheduler.cpp',
+ 'Matrix.cpp',
+ 'Path.cpp',
+ 'PathCairo.cpp',
+ 'PathHelpers.cpp',
+ 'PathRecording.cpp',
+ 'Quaternion.cpp',
+ 'RecordedEvent.cpp',
+ 'Scale.cpp',
+ 'ScaledFontBase.cpp',
+ 'ScaledFontCairo.cpp',
+ 'SFNTData.cpp',
+ 'SFNTNameTable.cpp',
+ 'SourceSurfaceCairo.cpp',
+ 'SourceSurfaceRawData.cpp',
+]
+
+if CONFIG['CLANG_CXX']:
+ SOURCES['Factory.cpp'].flags += ['-Wno-implicit-fallthrough']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
+ SOURCES += ['BlurNEON.cpp']
+ SOURCES['BlurNEON.cpp'].flags += CONFIG['NEON_FLAGS']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_CAIRO'):
+ DEFINES[var] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ DEFINES['MOZ_ENABLE_FREETYPE'] = True
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ LOCAL_INCLUDES += [
+ '/libs/skia/skia/include/private',
+ '/libs/skia/skia/src/core',
+ '/libs/skia/skia/src/image',
+ ]
+if CONFIG['MOZ_ENABLE_SKIA_GPU']:
+ LOCAL_INCLUDES += [
+ '/libs/skia/skia/src/gpu',
+ ]
+
diff --git a/system/graphics/2d/ssse3-scaler.c b/system/graphics/2d/ssse3-scaler.c
new file mode 100644
index 000000000..745f58f6f
--- /dev/null
+++ b/system/graphics/2d/ssse3-scaler.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright © 2013 Soren Sandmann Pedersen
+ * Copyright © 2013 Red Hat, Inc.
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Soren Sandmann (soren.sandmann@gmail.com)
+ * Jeff Muizelaar (jmuizelaar@mozilla.com)
+ */
+
+/* This has been adapted from the ssse3 code from pixman. It's currently
+ * a mess as I want to try it out in practice before finalizing the details.
+ */
+
+#include <stdlib.h>
+#include <mmintrin.h>
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <tmmintrin.h>
+#include <stdint.h>
+#include <assert.h>
+#include "ssse3-scaler.h"
+
+typedef int32_t pixman_fixed_16_16_t;
+typedef pixman_fixed_16_16_t pixman_fixed_t;
+#define pixman_fixed_1 (pixman_int_to_fixed(1))
+#define pixman_fixed_to_int(f) ((int) ((f) >> 16))
+#define pixman_int_to_fixed(i) ((pixman_fixed_t) ((i) << 16))
+#define pixman_double_to_fixed(d) ((pixman_fixed_t) ((d) * 65536.0))
+#define PIXMAN_FIXED_INT_MAX 32767
+#define PIXMAN_FIXED_INT_MIN -32768
+typedef struct pixman_vector pixman_vector_t;
+
+typedef int pixman_bool_t;
+typedef int64_t pixman_fixed_32_32_t;
+typedef pixman_fixed_32_32_t pixman_fixed_48_16_t;
+typedef struct { pixman_fixed_48_16_t v[3]; } pixman_vector_48_16_t;
+
+struct pixman_vector
+{
+ pixman_fixed_t vector[3];
+};
+typedef struct pixman_transform pixman_transform_t;
+
+struct pixman_transform
+{
+ pixman_fixed_t matrix[3][3];
+};
+
+#ifdef _MSC_VER
+#define force_inline __forceinline
+#else
+#define force_inline __inline__ __attribute__((always_inline))
+#endif
+
+#define BILINEAR_INTERPOLATION_BITS 6
+
+static force_inline int
+pixman_fixed_to_bilinear_weight (pixman_fixed_t x)
+{
+ return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) &
+ ((1 << BILINEAR_INTERPOLATION_BITS) - 1);
+}
+
+static void
+pixman_transform_point_31_16_3d (const pixman_transform_t *t,
+ const pixman_vector_48_16_t *v,
+ pixman_vector_48_16_t *result)
+{
+ int i;
+ int64_t tmp[3][2];
+
+ /* input vector values must have no more than 31 bits (including sign)
+ * in the integer part */
+ assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+
+ for (i = 0; i < 3; i++)
+ {
+ tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16);
+ tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF);
+ tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16);
+ tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF);
+ tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16);
+ tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF);
+ }
+
+ result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16);
+ result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16);
+ result->v[2] = tmp[2][0] + ((tmp[2][1] + 0x8000) >> 16);
+}
+
+static pixman_bool_t
+pixman_transform_point_3d (const struct pixman_transform *transform,
+ struct pixman_vector * vector)
+{
+ pixman_vector_48_16_t tmp;
+ tmp.v[0] = vector->vector[0];
+ tmp.v[1] = vector->vector[1];
+ tmp.v[2] = vector->vector[2];
+
+ pixman_transform_point_31_16_3d (transform, &tmp, &tmp);
+
+ vector->vector[0] = tmp.v[0];
+ vector->vector[1] = tmp.v[1];
+ vector->vector[2] = tmp.v[2];
+
+ return vector->vector[0] == tmp.v[0] &&
+ vector->vector[1] == tmp.v[1] &&
+ vector->vector[2] == tmp.v[2];
+}
+
+
+struct bits_image_t
+{
+ uint32_t * bits;
+ int rowstride;
+ pixman_transform_t *transform;
+};
+
+typedef struct bits_image_t bits_image_t;
+typedef struct {
+ int unused;
+} pixman_iter_info_t;
+
+typedef struct pixman_iter_t pixman_iter_t;
+typedef void (* pixman_iter_fini_t) (pixman_iter_t *iter);
+
+struct pixman_iter_t
+{
+ int x, y;
+ pixman_iter_fini_t fini;
+ bits_image_t *image;
+ uint32_t * buffer;
+ int width;
+ int height;
+ void * data;
+};
+
+typedef struct
+{
+ int y;
+ uint64_t * buffer;
+} line_t;
+
+typedef struct
+{
+ line_t lines[2];
+ pixman_fixed_t y;
+ pixman_fixed_t x;
+ uint64_t data[1];
+} bilinear_info_t;
+
+static void
+ssse3_fetch_horizontal (bits_image_t *image, line_t *line,
+ int y, pixman_fixed_t x, pixman_fixed_t ux, int n)
+{
+ uint32_t *bits = image->bits + y * image->rowstride;
+ __m128i vx = _mm_set_epi16 (
+ - (x + 1), x, - (x + 1), x,
+ - (x + ux + 1), x + ux, - (x + ux + 1), x + ux);
+ __m128i vux = _mm_set_epi16 (
+ - 2 * ux, 2 * ux, - 2 * ux, 2 * ux,
+ - 2 * ux, 2 * ux, - 2 * ux, 2 * ux);
+ __m128i vaddc = _mm_set_epi16 (1, 0, 1, 0, 1, 0, 1, 0);
+ __m128i *b = (__m128i *)line->buffer;
+ __m128i vrl0, vrl1;
+
+ while ((n -= 2) >= 0)
+ {
+ __m128i vw, vr, s;
+#ifdef HACKY_PADDING
+ if (pixman_fixed_to_int(x + ux) >= image->rowstride) {
+ vrl1 = _mm_setzero_si128();
+ printf("overread 2loop\n");
+ } else {
+ if (pixman_fixed_to_int(x + ux) < 0)
+ printf("underflow\n");
+ vrl1 = _mm_loadl_epi64(
+ (__m128i *)(bits + (pixman_fixed_to_int(x + ux) < 0 ? 0 : pixman_fixed_to_int(x + ux))));
+ }
+#else
+ vrl1 = _mm_loadl_epi64(
+ (__m128i *)(bits + pixman_fixed_to_int(x + ux)));
+#endif
+ /* vrl1: R1, L1 */
+
+ final_pixel:
+#ifdef HACKY_PADDING
+ vrl0 = _mm_loadl_epi64 (
+ (__m128i *)(bits + (pixman_fixed_to_int (x) < 0 ? 0 : pixman_fixed_to_int (x))));
+#else
+ vrl0 = _mm_loadl_epi64 (
+ (__m128i *)(bits + pixman_fixed_to_int (x)));
+#endif
+ /* vrl0: R0, L0 */
+
+ /* The weights are based on vx which is a vector of
+ *
+ * - (x + 1), x, - (x + 1), x,
+ * - (x + ux + 1), x + ux, - (x + ux + 1), x + ux
+ *
+ * so the 16 bit weights end up like this:
+ *
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ *
+ * and after shifting and packing, we get these bytes:
+ *
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ *
+ * which means the first and the second input pixel
+ * have to be interleaved like this:
+ *
+ * la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
+ * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
+ *
+ * before maddubsw can be used.
+ */
+
+ vw = _mm_add_epi16 (
+ vaddc, _mm_srli_epi16 (vx, 16 - BILINEAR_INTERPOLATION_BITS));
+ /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ */
+
+ vw = _mm_packus_epi16 (vw, vw);
+ /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ */
+ vx = _mm_add_epi16 (vx, vux);
+
+ x += 2 * ux;
+
+ vr = _mm_unpacklo_epi16 (vrl1, vrl0);
+ /* vr: rar0, rar1, rgb0, rgb1, lar0, lar1, lgb0, lgb1 */
+
+ s = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (1, 0, 3, 2));
+ /* s: lar0, lar1, lgb0, lgb1, rar0, rar1, rgb0, rgb1 */
+
+ vr = _mm_unpackhi_epi8 (vr, s);
+ /* vr: la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
+ * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
+ */
+
+ vr = _mm_maddubs_epi16 (vr, vw);
+
+ /* When the weight is 0, the inverse weight is
+ * 128 which can't be represented in a signed byte.
+ * As a result maddubsw computes the following:
+ *
+ * r = l * -128 + r * 0
+ *
+ * rather than the desired
+ *
+ * r = l * 128 + r * 0
+ *
+ * We fix this by taking the absolute value of the
+ * result.
+ */
+ // we can drop this if we use lower precision
+
+ vr = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (2, 0, 3, 1));
+ /* vr: A0, R0, A1, R1, G0, B0, G1, B1 */
+ _mm_store_si128 (b++, vr);
+ }
+
+ if (n == -1)
+ {
+ vrl1 = _mm_setzero_si128();
+ goto final_pixel;
+ }
+
+ line->y = y;
+}
+
+// scale a line of destination pixels
+static uint32_t *
+ssse3_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask)
+{
+ pixman_fixed_t fx, ux;
+ bilinear_info_t *info = iter->data;
+ line_t *line0, *line1;
+ int y0, y1;
+ int32_t dist_y;
+ __m128i vw, uvw;
+ int i;
+
+ fx = info->x;
+ ux = iter->image->transform->matrix[0][0];
+
+ y0 = pixman_fixed_to_int (info->y);
+ if (y0 < 0)
+ *(volatile char*)0 = 9;
+ y1 = y0 + 1;
+
+ // clamping in y direction
+ if (y1 >= iter->height) {
+ y1 = iter->height - 1;
+ }
+
+ line0 = &info->lines[y0 & 0x01];
+ line1 = &info->lines[y1 & 0x01];
+
+ if (line0->y != y0)
+ {
+ ssse3_fetch_horizontal (
+ iter->image, line0, y0, fx, ux, iter->width);
+ }
+
+ if (line1->y != y1)
+ {
+ ssse3_fetch_horizontal (
+ iter->image, line1, y1, fx, ux, iter->width);
+ }
+
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ dist_y = pixman_fixed_to_bilinear_weight (info->y);
+ dist_y <<= (16 - BILINEAR_INTERPOLATION_BITS);
+
+ vw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+
+#else
+ // setup the weights for the top (vw) and bottom (uvw) lines
+ dist_y = pixman_fixed_to_bilinear_weight (info->y);
+ // we use 15 instead of 16 because we need an extra bit to handle when the weights are 0 and 1
+ dist_y <<= (15 - BILINEAR_INTERPOLATION_BITS);
+
+ vw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+
+
+ dist_y = (1 << BILINEAR_INTERPOLATION_BITS) - pixman_fixed_to_bilinear_weight (info->y);
+ dist_y <<= (15 - BILINEAR_INTERPOLATION_BITS);
+ uvw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+#endif
+
+ for (i = 0; i + 3 < iter->width; i += 4)
+ {
+ __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
+ __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
+ __m128i top1 = _mm_load_si128 ((__m128i *)(line0->buffer + i + 2));
+ __m128i bot1 = _mm_load_si128 ((__m128i *)(line1->buffer + i + 2));
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ __m128i r0, r1, tmp, p;
+
+ r0 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot0, top0), vw);
+ tmp = _mm_cmplt_epi16 (bot0, top0);
+ tmp = _mm_and_si128 (tmp, vw);
+ r0 = _mm_sub_epi16 (r0, tmp);
+ r0 = _mm_add_epi16 (r0, top0);
+ r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
+ /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */
+ //r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */
+
+ // tmp = bot1 < top1 ? vw : 0;
+ // r1 = (bot1 - top1)*vw + top1 - tmp
+ // r1 = bot1*vw - vw*top1 + top1 - tmp
+ // r1 = bot1*vw + top1 - vw*top1 - tmp
+ // r1 = bot1*vw + top1*(1 - vw) - tmp
+ r1 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot1, top1), vw);
+ tmp = _mm_cmplt_epi16 (bot1, top1);
+ tmp = _mm_and_si128 (tmp, vw);
+ r1 = _mm_sub_epi16 (r1, tmp);
+ r1 = _mm_add_epi16 (r1, top1);
+ r1 = _mm_srli_epi16 (r1, BILINEAR_INTERPOLATION_BITS);
+ //r1 = _mm_shuffle_epi32 (r1, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r1: A3 R3 G3 B3 A2 R2 G2 B2 */
+#else
+ __m128i r0, r1, p;
+ top0 = _mm_mulhi_epu16 (top0, uvw);
+ bot0 = _mm_mulhi_epu16 (bot0, vw);
+ r0 = _mm_add_epi16(top0, bot0);
+ r0 = _mm_srli_epi16(r0, BILINEAR_INTERPOLATION_BITS-1);
+
+ top1 = _mm_mulhi_epu16 (top1, uvw);
+ bot1 = _mm_mulhi_epu16 (bot1, vw);
+ r1 = _mm_add_epi16(top1, bot1);
+ r1 = _mm_srli_epi16(r1, BILINEAR_INTERPOLATION_BITS-1);
+#endif
+
+ p = _mm_packus_epi16 (r0, r1);
+ _mm_storeu_si128 ((__m128i *)(iter->buffer + i), p);
+ }
+
+ while (i < iter->width)
+ {
+ __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
+ __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
+
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ __m128i r0, tmp, p;
+ r0 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot0, top0), vw);
+ tmp = _mm_cmplt_epi16 (bot0, top0);
+ tmp = _mm_and_si128 (tmp, vw);
+ r0 = _mm_sub_epi16 (r0, tmp);
+ r0 = _mm_add_epi16 (r0, top0);
+ r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
+ /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */
+ r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */
+#else
+ __m128i r0, p;
+ top0 = _mm_mulhi_epu16 (top0, uvw);
+ bot0 = _mm_mulhi_epu16 (bot0, vw);
+ r0 = _mm_add_epi16(top0, bot0);
+ r0 = _mm_srli_epi16(r0, BILINEAR_INTERPOLATION_BITS-1);
+#endif
+
+ p = _mm_packus_epi16 (r0, r0);
+
+ if (iter->width - i == 1)
+ {
+ *(uint32_t *)(iter->buffer + i) = _mm_cvtsi128_si32 (p);
+ i++;
+ }
+ else
+ {
+ _mm_storel_epi64 ((__m128i *)(iter->buffer + i), p);
+ i += 2;
+ }
+ }
+
+ info->y += iter->image->transform->matrix[1][1];
+
+ return iter->buffer;
+}
+
+static void
+ssse3_bilinear_cover_iter_fini (pixman_iter_t *iter)
+{
+ free (iter->data);
+}
+
+static void
+ssse3_bilinear_cover_iter_init (pixman_iter_t *iter)
+{
+ int width = iter->width;
+ bilinear_info_t *info;
+ pixman_vector_t v;
+
+ if (iter->x > PIXMAN_FIXED_INT_MAX ||
+ iter->x < PIXMAN_FIXED_INT_MIN ||
+ iter->y > PIXMAN_FIXED_INT_MAX ||
+ iter->y < PIXMAN_FIXED_INT_MIN)
+ goto fail;
+
+ /* Reference point is the center of the pixel */
+ v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2;
+ v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2;
+ v.vector[2] = pixman_fixed_1;
+
+ if (!pixman_transform_point_3d (iter->image->transform, &v))
+ goto fail;
+
+ info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t) + 64);
+ if (!info)
+ goto fail;
+
+ info->x = v.vector[0] - pixman_fixed_1 / 2;
+ info->y = v.vector[1] - pixman_fixed_1 / 2;
+
+#define ALIGN(addr) \
+ ((void *)((((uintptr_t)(addr)) + 15) & (~15)))
+
+ /* It is safe to set the y coordinates to -1 initially
+ * because COVER_CLIP_BILINEAR ensures that we will only
+ * be asked to fetch lines in the [0, height) interval
+ */
+ info->lines[0].y = -1;
+ info->lines[0].buffer = ALIGN (&(info->data[0]));
+ info->lines[1].y = -1;
+ info->lines[1].buffer = ALIGN (info->lines[0].buffer + width);
+
+ iter->fini = ssse3_bilinear_cover_iter_fini;
+
+ iter->data = info;
+ return;
+
+fail:
+ /* Something went wrong, either a bad matrix or OOM; in such cases,
+ * we don't guarantee any particular rendering.
+ */
+ iter->fini = NULL;
+}
+
+/* scale the src from src_width/height to dest_width/height drawn
+ * into the rectangle x,y width,height
+ * src_stride and dst_stride are 4 byte units */
+bool ssse3_scale_data(uint32_t *src, int src_width, int src_height, int src_stride,
+ uint32_t *dest, int dest_width, int dest_height,
+ int dest_stride,
+ int x, int y,
+ int width, int height)
+{
+ //XXX: assert(src_width > 1)
+ pixman_transform_t transform = {
+ { { pixman_fixed_1, 0, 0 },
+ { 0, pixman_fixed_1, 0 },
+ { 0, 0, pixman_fixed_1 } }
+ };
+ double width_scale = ((double)src_width)/dest_width;
+ double height_scale = ((double)src_height)/dest_height;
+#define AVOID_PADDING
+#ifdef AVOID_PADDING
+ // scale up by enough that we don't read outside of the bounds of the source surface
+ // currently this is required to avoid reading out of bounds.
+ if (width_scale < 1) {
+ width_scale = (double)(src_width-1)/dest_width;
+ transform.matrix[0][2] = pixman_fixed_1/2;
+ }
+ if (height_scale < 1) {
+ height_scale = (double)(src_height-1)/dest_height;
+ transform.matrix[1][2] = pixman_fixed_1/2;
+ }
+#endif
+ transform.matrix[0][0] = pixman_double_to_fixed(width_scale);
+ transform.matrix[1][1] = pixman_double_to_fixed(height_scale);
+ transform.matrix[2][2] = pixman_fixed_1;
+
+ bits_image_t image;
+ image.bits = src;
+ image.transform = &transform;
+ image.rowstride = src_stride;
+
+ pixman_iter_t iter;
+ iter.image = &image;
+ iter.x = x;
+ iter.y = y;
+ iter.width = width;
+ iter.height = src_height;
+ iter.buffer = dest;
+ iter.data = NULL;
+
+ ssse3_bilinear_cover_iter_init(&iter);
+
+ if (!iter.fini)
+ return false;
+
+ if (iter.data) {
+ for (int iy = 0; iy < height; iy++) {
+ ssse3_fetch_bilinear_cover(&iter, NULL);
+ iter.buffer += dest_stride;
+ }
+ ssse3_bilinear_cover_iter_fini(&iter);
+ }
+ return true;
+}
diff --git a/system/graphics/2d/ssse3-scaler.h b/system/graphics/2d/ssse3-scaler.h
new file mode 100644
index 000000000..ea8d8a066
--- /dev/null
+++ b/system/graphics/2d/ssse3-scaler.h
@@ -0,0 +1,24 @@
+#/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_2D_SSSE3_SCALER_H_
+#define MOZILLA_GFX_2D_SSSE3_SCALER_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+bool ssse3_scale_data(uint32_t *src, int src_width, int src_height,
+ int src_stride,
+ uint32_t *dest, int dest_width, int dest_height,
+ int dest_rowstride,
+ int x, int y,
+ int width, int height);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MOZILLA_GFX_2D_SSS3_SCALER_H_
diff --git a/system/graphics/2d/u16string.h b/system/graphics/2d/u16string.h
new file mode 100644
index 000000000..2d00c3a23
--- /dev/null
+++ b/system/graphics/2d/u16string.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_u16string_h
+#define mozilla_gfx_u16string_h
+
+#include <string>
+
+#include "mozilla/Char16.h"
+
+namespace mozilla {
+
+#if defined(_MSC_VER)
+typedef std::u16string u16string;
+#else
+typedef std::basic_string<char16_t> u16string;
+#endif
+
+} // mozilla
+
+#endif // mozilla_gfx_u16string_h
diff --git a/system/graphics/2d/unittest/Main.cpp b/system/graphics/2d/unittest/Main.cpp
new file mode 100644
index 000000000..46a1af5b6
--- /dev/null
+++ b/system/graphics/2d/unittest/Main.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SanityChecks.h"
+#include "TestPoint.h"
+#include "TestScaling.h"
+#include "TestBugs.h"
+#ifdef WIN32
+#include "TestDrawTargetD2D.h"
+#endif
+
+#include <string>
+#include <sstream>
+
+struct TestObject {
+ TestBase *test;
+ std::string name;
+};
+
+
+using namespace std;
+
+int
+main()
+{
+ TestObject tests[] =
+ {
+ { new SanityChecks(), "Sanity Checks" },
+ #ifdef WIN32
+ { new TestDrawTargetD2D(), "DrawTarget (D2D)" },
+ #endif
+ { new TestPoint(), "Point Tests" },
+ { new TestScaling(), "Scaling Tests" }
+ { new TestBugs(), "Bug Tests" }
+ };
+
+ int totalFailures = 0;
+ int totalTests = 0;
+ stringstream message;
+ printf("------ STARTING RUNNING TESTS ------\n");
+ for (int i = 0; i < sizeof(tests) / sizeof(TestObject); i++) {
+ message << "--- RUNNING TESTS: " << tests[i].name << " ---\n";
+ printf(message.str().c_str());
+ message.str("");
+ int failures = 0;
+ totalTests += tests[i].test->RunTests(&failures);
+ totalFailures += failures;
+ // Done with this test!
+ delete tests[i].test;
+ }
+ message << "------ FINISHED RUNNING TESTS ------\nTests run: " << totalTests << " - Passes: " << totalTests - totalFailures << " - Failures: " << totalFailures << "\n";
+ printf(message.str().c_str());
+ return totalFailures;
+}
diff --git a/system/graphics/2d/unittest/SanityChecks.cpp b/system/graphics/2d/unittest/SanityChecks.cpp
new file mode 100644
index 000000000..7f109facd
--- /dev/null
+++ b/system/graphics/2d/unittest/SanityChecks.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SanityChecks.h"
+
+SanityChecks::SanityChecks()
+{
+ REGISTER_TEST(SanityChecks, AlwaysPasses);
+}
+
+void
+SanityChecks::AlwaysPasses()
+{
+ bool testMustPass = true;
+
+ VERIFY(testMustPass);
+}
diff --git a/system/graphics/2d/unittest/SanityChecks.h b/system/graphics/2d/unittest/SanityChecks.h
new file mode 100644
index 000000000..f2a4a0b59
--- /dev/null
+++ b/system/graphics/2d/unittest/SanityChecks.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "TestBase.h"
+
+class SanityChecks : public TestBase
+{
+public:
+ SanityChecks();
+
+ void AlwaysPasses();
+};
diff --git a/system/graphics/2d/unittest/TestBase.cpp b/system/graphics/2d/unittest/TestBase.cpp
new file mode 100644
index 000000000..5818a7299
--- /dev/null
+++ b/system/graphics/2d/unittest/TestBase.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestBase.h"
+
+#include <sstream>
+
+using namespace std;
+
+int
+TestBase::RunTests(int *aFailures)
+{
+ int testsRun = 0;
+ *aFailures = 0;
+
+ for(unsigned int i = 0; i < mTests.size(); i++) {
+ stringstream stream;
+ stream << "Test (" << mTests[i].name << "): ";
+ LogMessage(stream.str());
+ stream.str("");
+
+ mTestFailed = false;
+
+ // Don't try this at home! We know these are actually pointers to members
+ // of child clases, so we reinterpret cast those child class pointers to
+ // TestBase and then call the functions. Because the compiler believes
+ // these function calls are members of TestBase.
+ ((*reinterpret_cast<TestBase*>((mTests[i].implPointer))).*(mTests[i].funcCall))();
+
+ if (!mTestFailed) {
+ LogMessage("PASSED\n");
+ } else {
+ LogMessage("FAILED\n");
+ (*aFailures)++;
+ }
+ testsRun++;
+ }
+
+ return testsRun;
+}
+
+void
+TestBase::LogMessage(string aMessage)
+{
+ printf("%s", aMessage.c_str());
+}
diff --git a/system/graphics/2d/unittest/TestBase.h b/system/graphics/2d/unittest/TestBase.h
new file mode 100644
index 000000000..a57d6a730
--- /dev/null
+++ b/system/graphics/2d/unittest/TestBase.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#ifdef _MSC_VER
+// On MSVC otherwise our generic member pointer trick doesn't work.
+#pragma pointers_to_members(full_generality, single_inheritance)
+#endif
+
+#define VERIFY(arg) if (!(arg)) { \
+ LogMessage("VERIFY FAILED: "#arg"\n"); \
+ mTestFailed = true; \
+ }
+
+#define REGISTER_TEST(className, testName) \
+ mTests.push_back(Test(static_cast<TestCall>(&className::testName), #testName, this))
+
+class TestBase
+{
+public:
+ TestBase() {}
+
+ typedef void (TestBase::*TestCall)();
+
+ int RunTests(int *aFailures);
+
+protected:
+ static void LogMessage(std::string aMessage);
+
+ struct Test {
+ Test(TestCall aCall, std::string aName, void *aImplPointer)
+ : funcCall(aCall)
+ , name(aName)
+ , implPointer(aImplPointer)
+ {
+ }
+ TestCall funcCall;
+ std::string name;
+ void *implPointer;
+ };
+ std::vector<Test> mTests;
+
+ bool mTestFailed;
+
+private:
+ // This doesn't really work with our generic member pointer trick.
+ TestBase(const TestBase &aOther);
+};
diff --git a/system/graphics/2d/unittest/TestBugs.cpp b/system/graphics/2d/unittest/TestBugs.cpp
new file mode 100644
index 000000000..f127eed8b
--- /dev/null
+++ b/system/graphics/2d/unittest/TestBugs.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestBugs.h"
+#include "2D.h"
+#include <string.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TestBugs::TestBugs()
+{
+ REGISTER_TEST(TestBugs, CairoClip918671);
+ REGISTER_TEST(TestBugs, PushPopClip950550);
+}
+
+void
+TestBugs::CairoClip918671()
+{
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(100, 100),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<DrawTarget> ref = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(100, 100),
+ SurfaceFormat::B8G8R8A8);
+ // Create a path that extends around the center rect but doesn't intersect it.
+ RefPtr<PathBuilder> pb1 = dt->CreatePathBuilder();
+ pb1->MoveTo(Point(10, 10));
+ pb1->LineTo(Point(90, 10));
+ pb1->LineTo(Point(90, 20));
+ pb1->LineTo(Point(10, 20));
+ pb1->Close();
+ pb1->MoveTo(Point(90, 90));
+ pb1->LineTo(Point(91, 90));
+ pb1->LineTo(Point(91, 91));
+ pb1->LineTo(Point(91, 90));
+ pb1->Close();
+
+ RefPtr<Path> path1 = pb1->Finish();
+ dt->PushClip(path1);
+
+ // This center rect must NOT be rectilinear!
+ RefPtr<PathBuilder> pb2 = dt->CreatePathBuilder();
+ pb2->MoveTo(Point(50, 50));
+ pb2->LineTo(Point(55, 51));
+ pb2->LineTo(Point(54, 55));
+ pb2->LineTo(Point(50, 56));
+ pb2->Close();
+
+ RefPtr<Path> path2 = pb2->Finish();
+ dt->PushClip(path2);
+
+ dt->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1,0,0)));
+
+ RefPtr<SourceSurface> surf1 = dt->Snapshot();
+ RefPtr<SourceSurface> surf2 = ref->Snapshot();
+
+ RefPtr<DataSourceSurface> dataSurf1 = surf1->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurf2 = surf2->GetDataSurface();
+
+ for (int y = 0; y < dt->GetSize().height; y++) {
+ VERIFY(memcmp(dataSurf1->GetData() + y * dataSurf1->Stride(),
+ dataSurf2->GetData() + y * dataSurf2->Stride(),
+ dataSurf1->GetSize().width * 4) == 0);
+ }
+
+}
+
+void
+TestBugs::PushPopClip950550()
+{
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(500, 500),
+ SurfaceFormat::B8G8R8A8);
+ dt->PushClipRect(Rect(0, 0, 100, 100));
+ Matrix m(1, 0, 0, 1, 45, -100);
+ dt->SetTransform(m);
+ dt->PopClip();
+
+ // We fail the test if we assert in this call because our draw target's
+ // transforms are out of sync.
+ dt->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
+}
+
diff --git a/system/graphics/2d/unittest/TestBugs.h b/system/graphics/2d/unittest/TestBugs.h
new file mode 100644
index 000000000..0c715df44
--- /dev/null
+++ b/system/graphics/2d/unittest/TestBugs.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestBugs : public TestBase
+{
+public:
+ TestBugs();
+
+ void CairoClip918671();
+ void PushPopClip950550();
+};
+
diff --git a/system/graphics/2d/unittest/TestCairo.cpp b/system/graphics/2d/unittest/TestCairo.cpp
new file mode 100644
index 000000000..7df604695
--- /dev/null
+++ b/system/graphics/2d/unittest/TestCairo.cpp
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "cairo.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+namespace layers {
+
+void TryCircle(double centerX, double centerY, double radius) {
+ printf("TestCairo:TryArcs centerY %f, radius %f\n",centerY,radius);
+
+ cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,8,21);
+ ASSERT_TRUE(surf != nullptr);
+
+ cairo_t *cairo = cairo_create(surf);
+ ASSERT_TRUE(cairo != nullptr);
+
+ cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
+ cairo_arc(cairo, 0.0, centerY, radius, 0.0, 6.2831853071795862);
+ cairo_fill_preserve(cairo);
+
+ cairo_surface_destroy(surf);
+ cairo_destroy(cairo);
+}
+
+TEST(Cairo, Simple) {
+ TryCircle(0.0, 0.0, 14.0);
+ TryCircle(0.0, 1.0, 22.4);
+ TryCircle(1.0, 0.0, 1422.4);
+ TryCircle(1.0, 1.0, 3422.4);
+ TryCircle(-10.0, 1.0, -2);
+}
+
+TEST(Cairo, Bug825721) {
+ // OK:
+ TryCircle(0.0, 0.0, 8761126469220696064.0);
+ TryCircle(0.0, 1.0, 8761126469220696064.0);
+
+ // OK:
+ TryCircle(1.0, 0.0, 5761126469220696064.0);
+
+ // This was the crash in 825721. Note that centerY has to be non-zero,
+ // and radius has to be not only large, but in particular range.
+ // 825721 has a band-aid fix, where the crash is inevitable, but does
+ // not fix the cause. The same code crashes in cairo standalone.
+ TryCircle(0.0, 1.0, 5761126469220696064.0);
+}
+
+TEST(Cairo, Bug1063486) {
+
+ double x1, y1, x2, y2;
+ const double epsilon = .01;
+
+ cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+ ASSERT_TRUE(surf != nullptr);
+
+ cairo_t *cairo = cairo_create(surf);
+ ASSERT_TRUE(cairo != nullptr);
+
+ printf("Path 1\n");
+ cairo_move_to(cairo, -20, -10);
+ cairo_line_to(cairo, 20, -10);
+ cairo_line_to(cairo, 20, 10);
+ cairo_curve_to(cairo, 10,10, -10,10, -20,10);
+ cairo_curve_to(cairo, -30,10, -30,-10, -20,-10);
+
+ cairo_path_extents(cairo, &x1, &y1, &x2, &y2);
+
+ ASSERT_LT(std::abs(-27.5 - x1), epsilon); // the failing coordinate
+ ASSERT_LT(std::abs(-10 - y1), epsilon);
+ ASSERT_LT(std::abs(20 - x2), epsilon);
+ ASSERT_LT(std::abs(10 - y2), epsilon);
+
+ printf("Path 2\n");
+ cairo_new_path(cairo);
+ cairo_move_to(cairo, 10, 30);
+ cairo_line_to(cairo, 90, 30);
+ cairo_curve_to(cairo, 30,30, 30,30, 10,30);
+ cairo_curve_to(cairo, 0,30, 0,0, 30,5);
+
+ cairo_path_extents(cairo, &x1, &y1, &x2, &y2);
+
+ ASSERT_LT(std::abs(4.019531 - x1), epsilon); // the failing coordinate
+ ASSERT_LT(std::abs(4.437500 - y1), epsilon);
+ ASSERT_LT(std::abs(90. - x2), epsilon);
+ ASSERT_LT(std::abs(30. - y2), epsilon);
+
+ cairo_surface_destroy(surf);
+ cairo_destroy(cairo);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/2d/unittest/TestDrawTargetBase.cpp b/system/graphics/2d/unittest/TestDrawTargetBase.cpp
new file mode 100644
index 000000000..2a0d95ed6
--- /dev/null
+++ b/system/graphics/2d/unittest/TestDrawTargetBase.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestDrawTargetBase.h"
+#include <sstream>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace std;
+
+TestDrawTargetBase::TestDrawTargetBase()
+{
+ REGISTER_TEST(TestDrawTargetBase, Initialized);
+ REGISTER_TEST(TestDrawTargetBase, FillCompletely);
+ REGISTER_TEST(TestDrawTargetBase, FillRect);
+}
+
+void
+TestDrawTargetBase::Initialized()
+{
+ VERIFY(mDT);
+}
+
+void
+TestDrawTargetBase::FillCompletely()
+{
+ mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+
+ RefreshSnapshot();
+
+ VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::FillRect()
+{
+ mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+ mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
+
+ RefreshSnapshot();
+
+ VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
+ VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
+ VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
+ VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::RefreshSnapshot()
+{
+ RefPtr<SourceSurface> snapshot = mDT->Snapshot();
+ mDataSnapshot = snapshot->GetDataSurface();
+}
+
+void
+TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
+{
+ uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+ uint32_t expected = RGBAPixelFromColor(aColor);
+
+ for (int y = 0; y < DT_HEIGHT; y++) {
+ for (int x = 0; x < DT_WIDTH; x++) {
+ if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
+ LogMessage("VerifyAllPixels Failed\n");
+ mTestFailed = true;
+ return;
+ }
+ }
+ }
+}
+
+void
+TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
+{
+ uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+ uint32_t expected = RGBAPixelFromColor(aColor);
+ uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
+
+ if (rawActual != expected) {
+ stringstream message;
+ uint32_t actb = rawActual & 0xFF;
+ uint32_t actg = (rawActual & 0xFF00) >> 8;
+ uint32_t actr = (rawActual & 0xFF0000) >> 16;
+ uint32_t acta = (rawActual & 0xFF000000) >> 24;
+ uint32_t expb = expected & 0xFF;
+ uint32_t expg = (expected & 0xFF00) >> 8;
+ uint32_t expr = (expected & 0xFF0000) >> 16;
+ uint32_t expa = (expected & 0xFF000000) >> 24;
+
+ message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
+ " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
+ " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
+
+ LogMessage(message.str());
+ mTestFailed = true;
+ return;
+ }
+}
+
+uint32_t
+TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
+{
+ return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
+ uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
+}
diff --git a/system/graphics/2d/unittest/TestDrawTargetBase.h b/system/graphics/2d/unittest/TestDrawTargetBase.h
new file mode 100644
index 000000000..06a62413f
--- /dev/null
+++ b/system/graphics/2d/unittest/TestDrawTargetBase.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "2D.h"
+#include "TestBase.h"
+
+#define DT_WIDTH 500
+#define DT_HEIGHT 500
+
+/* This general DrawTarget test class can be reimplemented by a child class
+ * with optional additional drawtarget-specific tests. And is intended to run
+ * on a 500x500 32 BPP drawtarget.
+ */
+class TestDrawTargetBase : public TestBase
+{
+public:
+ void Initialized();
+ void FillCompletely();
+ void FillRect();
+
+protected:
+ TestDrawTargetBase();
+
+ void RefreshSnapshot();
+
+ void VerifyAllPixels(const mozilla::gfx::Color &aColor);
+ void VerifyPixel(const mozilla::gfx::IntPoint &aPoint,
+ mozilla::gfx::Color &aColor);
+
+ uint32_t RGBAPixelFromColor(const mozilla::gfx::Color &aColor);
+
+ RefPtr<mozilla::gfx::DrawTarget> mDT;
+ RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot;
+};
diff --git a/system/graphics/2d/unittest/TestDrawTargetD2D.cpp b/system/graphics/2d/unittest/TestDrawTargetD2D.cpp
new file mode 100644
index 000000000..0715f93d8
--- /dev/null
+++ b/system/graphics/2d/unittest/TestDrawTargetD2D.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestDrawTargetD2D.h"
+
+using namespace mozilla::gfx;
+TestDrawTargetD2D::TestDrawTargetD2D()
+{
+ ::D3D10CreateDevice1(nullptr,
+ D3D10_DRIVER_TYPE_HARDWARE,
+ nullptr,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_0,
+ D3D10_1_SDK_VERSION,
+ getter_AddRefs(mDevice));
+
+ Factory::SetDirect3D10Device(mDevice);
+
+ mDT = Factory::CreateDrawTarget(BackendType::DIRECT2D, IntSize(DT_WIDTH, DT_HEIGHT), SurfaceFormat::B8G8R8A8);
+}
diff --git a/system/graphics/2d/unittest/TestDrawTargetD2D.h b/system/graphics/2d/unittest/TestDrawTargetD2D.h
new file mode 100644
index 000000000..97bb88cf2
--- /dev/null
+++ b/system/graphics/2d/unittest/TestDrawTargetD2D.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "TestDrawTargetBase.h"
+
+#include <d3d10_1.h>
+
+class TestDrawTargetD2D : public TestDrawTargetBase
+{
+public:
+ TestDrawTargetD2D();
+
+private:
+ RefPtr<ID3D10Device1> mDevice;
+};
diff --git a/system/graphics/2d/unittest/TestPoint.cpp b/system/graphics/2d/unittest/TestPoint.cpp
new file mode 100644
index 000000000..6aa2b6a35
--- /dev/null
+++ b/system/graphics/2d/unittest/TestPoint.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestPoint.h"
+
+#include "Point.h"
+
+using namespace mozilla::gfx;
+
+TestPoint::TestPoint()
+{
+ REGISTER_TEST(TestPoint, Addition);
+ REGISTER_TEST(TestPoint, Subtraction);
+}
+
+void
+TestPoint::Addition()
+{
+ Point a, b;
+ a.x = 2;
+ a.y = 2;
+ b.x = 5;
+ b.y = -5;
+
+ a += b;
+
+ VERIFY(a.x == 7.f);
+ VERIFY(a.y == -3.f);
+}
+
+void
+TestPoint::Subtraction()
+{
+ Point a, b;
+ a.x = 2;
+ a.y = 2;
+ b.x = 5;
+ b.y = -5;
+
+ a -= b;
+
+ VERIFY(a.x == -3.f);
+ VERIFY(a.y == 7.f);
+}
diff --git a/system/graphics/2d/unittest/TestPoint.h b/system/graphics/2d/unittest/TestPoint.h
new file mode 100644
index 000000000..813d66c2f
--- /dev/null
+++ b/system/graphics/2d/unittest/TestPoint.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestPoint : public TestBase
+{
+public:
+ TestPoint();
+
+ void Addition();
+ void Subtraction();
+};
diff --git a/system/graphics/2d/unittest/TestScaling.cpp b/system/graphics/2d/unittest/TestScaling.cpp
new file mode 100644
index 000000000..60e1acbf0
--- /dev/null
+++ b/system/graphics/2d/unittest/TestScaling.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestScaling.h"
+
+#include "ImageScaling.h"
+
+using namespace mozilla::gfx;
+
+TestScaling::TestScaling()
+{
+ REGISTER_TEST(TestScaling, BasicHalfScale);
+ REGISTER_TEST(TestScaling, DoubleHalfScale);
+ REGISTER_TEST(TestScaling, UnevenHalfScale);
+ REGISTER_TEST(TestScaling, OddStrideHalfScale);
+ REGISTER_TEST(TestScaling, VerticalHalfScale);
+ REGISTER_TEST(TestScaling, HorizontalHalfScale);
+ REGISTER_TEST(TestScaling, MixedHalfScale);
+}
+
+void
+TestScaling::BasicHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(220, 240));
+
+ VERIFY(scaler.GetSize().width == 250);
+ VERIFY(scaler.GetSize().height == 250);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 250; y++) {
+ for (int x = 0; x < 250; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::DoubleHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(120, 110));
+ VERIFY(scaler.GetSize().width == 125);
+ VERIFY(scaler.GetSize().height == 125);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 125; y++) {
+ for (int x = 0; x < 125; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::UnevenHalfScale()
+{
+ std::vector<uint8_t> data;
+ // Use a 16-byte aligned stride still, we test none-aligned strides
+ // separately.
+ data.resize(499 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ if (x < 498) {
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ }
+ if (y < 498) {
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ if (x < 498) {
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(499, 499));
+
+ scaler.ScaleForSize(IntSize(220, 220));
+ VERIFY(scaler.GetSize().width == 249);
+ VERIFY(scaler.GetSize().height == 249);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 249; y++) {
+ for (int x = 0; x < 249; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::OddStrideHalfScale()
+{
+ std::vector<uint8_t> data;
+ // Use a 4-byte aligned stride to test if that doesn't cause any issues.
+ data.resize(499 * 499 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 499 + x] = 0xff00ff00;
+ if (x < 498) {
+ pixels[y * 499 + x + 1] = 0xff00ffff;
+ }
+ if (y < 498) {
+ pixels[(y + 1) * 499 + x] = 0xff000000;
+ if (x < 498) {
+ pixels[(y + 1) * 499 + x + 1] = 0xff0000ff;
+ }
+ }
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 499 * 4, IntSize(499, 499));
+
+ scaler.ScaleForSize(IntSize(220, 220));
+ VERIFY(scaler.GetSize().width == 249);
+ VERIFY(scaler.GetSize().height == 249);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 249; y++) {
+ for (int x = 0; x < 249; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+void
+TestScaling::VerticalHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(400, 240));
+ VERIFY(scaler.GetSize().width == 500);
+ VERIFY(scaler.GetSize().height == 250);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 250; y++) {
+ for (int x = 0; x < 500; x += 2) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff);
+ }
+ }
+}
+
+void
+TestScaling::HorizontalHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(520 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y ++) {
+ for (int x = 0; x < 520; x += 8) {
+ pixels[y * 520 + x] = 0xff00ff00;
+ pixels[y * 520 + x + 1] = 0xff00ffff;
+ pixels[y * 520 + x + 2] = 0xff000000;
+ pixels[y * 520 + x + 3] = 0xff0000ff;
+ pixels[y * 520 + x + 4] = 0xffff00ff;
+ pixels[y * 520 + x + 5] = 0xff0000ff;
+ pixels[y * 520 + x + 6] = 0xffffffff;
+ pixels[y * 520 + x + 7] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 520 * 4, IntSize(520, 500));
+
+ scaler.ScaleForSize(IntSize(240, 400));
+ VERIFY(scaler.GetSize().width == 260);
+ VERIFY(scaler.GetSize().height == 500);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 500; y++) {
+ for (int x = 0; x < 260; x += 4) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff00007f);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 2] == 0xff7f00ff);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 3] == 0xff7f7fff);
+ }
+ }
+}
+
+void
+TestScaling::MixedHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(120, 240));
+ VERIFY(scaler.GetSize().width == 125);
+ VERIFY(scaler.GetSize().height == 250);
+ scaler.ScaleForSize(IntSize(240, 120));
+ VERIFY(scaler.GetSize().width == 250);
+ VERIFY(scaler.GetSize().height == 125);
+}
diff --git a/system/graphics/2d/unittest/TestScaling.h b/system/graphics/2d/unittest/TestScaling.h
new file mode 100644
index 000000000..e9bd1a8e0
--- /dev/null
+++ b/system/graphics/2d/unittest/TestScaling.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestScaling : public TestBase
+{
+public:
+ TestScaling();
+
+ void BasicHalfScale();
+ void DoubleHalfScale();
+ void UnevenHalfScale();
+ void OddStrideHalfScale();
+ void VerticalHalfScale();
+ void HorizontalHalfScale();
+ void MixedHalfScale();
+};
diff --git a/system/graphics/2d/unittest/unittest.vcxproj b/system/graphics/2d/unittest/unittest.vcxproj
new file mode 100644
index 000000000..7ddf92530
--- /dev/null
+++ b/system/graphics/2d/unittest/unittest.vcxproj
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{CCF4BC8B-0CED-47CA-B621-ABF1832527D9}</ProjectGuid>
+ <RootNamespace>unittest</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LibraryPath>$(DXSDK_DIR)\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath>
+ <IncludePath>$(ProjectDir)..\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LibraryPath>$(DXSDK_DIR)\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>../$(Configuration)/gfx2d.lib;dxguid.lib;d3d10_1.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>../$(Configuration)/gfx2d.lib;dxguid.lib;d3d10_1.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Main.cpp" />
+ <ClCompile Include="SanityChecks.cpp" />
+ <ClCompile Include="TestBase.cpp" />
+ <ClCompile Include="TestDrawTargetBase.cpp" />
+ <ClCompile Include="TestDrawTargetD2D.cpp" />
+ <ClCompile Include="TestPoint.cpp" />
+ <ClCompile Include="TestScaling.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="TestDrawTargetBase.h" />
+ <ClInclude Include="SanityChecks.h" />
+ <ClInclude Include="TestBase.h" />
+ <ClInclude Include="TestDrawTargetD2D.h" />
+ <ClInclude Include="TestPoint.h" />
+ <ClInclude Include="TestScaling.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/system/graphics/config/gfxConfig.cpp b/system/graphics/config/gfxConfig.cpp
new file mode 100644
index 000000000..d973e622d
--- /dev/null
+++ b/system/graphics/config/gfxConfig.cpp
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "gfxConfig.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "plstr.h"
+
+namespace mozilla {
+namespace gfx {
+
+static UniquePtr<gfxConfig> sConfig;
+
+/* static */ FeatureState&
+gfxConfig::GetFeature(Feature aFeature)
+{
+ return sConfig->GetState(aFeature);
+}
+
+/* static */ bool
+gfxConfig::IsEnabled(Feature aFeature)
+{
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.IsEnabled();
+}
+
+/* static */ bool
+gfxConfig::IsDisabledByDefault(Feature aFeature)
+{
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.DisabledByDefault();
+}
+
+/* static */ bool
+gfxConfig::IsForcedOnByUser(Feature aFeature)
+{
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.IsForcedOnByUser();
+}
+
+/* static */ FeatureStatus
+gfxConfig::GetValue(Feature aFeature)
+{
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.GetValue();
+}
+
+/* static */ bool
+gfxConfig::SetDefault(Feature aFeature,
+ bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ return state.SetDefault(aEnable, aDisableStatus, aDisableMessage);
+}
+
+/* static */ void
+gfxConfig::DisableByDefault(Feature aFeature,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.DisableByDefault(aDisableStatus, aDisableMessage, aFailureId);
+}
+
+/* static */ void
+gfxConfig::EnableByDefault(Feature aFeature)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.EnableByDefault();
+}
+
+/* static */ void
+gfxConfig::SetDefaultFromPref(Feature aFeature,
+ const char* aPrefName,
+ bool aIsEnablePref,
+ bool aDefaultValue)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ return state.SetDefaultFromPref(aPrefName, aIsEnablePref, aDefaultValue);
+}
+
+/* static */ bool
+gfxConfig::InitOrUpdate(Feature aFeature,
+ bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ return state.InitOrUpdate(aEnable, aDisableStatus, aDisableMessage);
+}
+
+/* static */ void
+gfxConfig::SetFailed(Feature aFeature, FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.SetFailed(aStatus, aMessage, aFailureId);
+}
+
+/* static */ void
+gfxConfig::Disable(Feature aFeature, FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.Disable(aStatus, aMessage, aFailureId);
+}
+
+/* static */ void
+gfxConfig::UserEnable(Feature aFeature, const char* aMessage)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserEnable(aMessage);
+}
+
+/* static */ void
+gfxConfig::UserForceEnable(Feature aFeature, const char* aMessage)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserForceEnable(aMessage);
+}
+
+/* static */ void
+gfxConfig::UserDisable(Feature aFeature, const char* aMessage, const nsACString& aFailureId)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserDisable(aMessage, aFailureId);
+}
+
+/* static */ void
+gfxConfig::Reenable(Feature aFeature, Fallback aFallback)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+ MOZ_ASSERT(IsFeatureStatusFailure(state.GetValue()));
+
+ const char* message = state.GetRuntimeMessage();
+ EnableFallback(aFallback, message);
+ state.SetRuntime(FeatureStatus::Available, nullptr);
+}
+
+/* static */ void
+gfxConfig::Inherit(Feature aFeature, FeatureStatus aStatus)
+{
+ FeatureState& state = sConfig->GetState(aFeature);
+
+ state.Reset();
+
+ switch (aStatus) {
+ case FeatureStatus::Unused:
+ break;
+ case FeatureStatus::Available:
+ gfxConfig::EnableByDefault(aFeature);
+ break;
+ case FeatureStatus::ForceEnabled:
+ gfxConfig::EnableByDefault(aFeature);
+ gfxConfig::UserForceEnable(aFeature, "Inherited from parent process");
+ break;
+ default:
+ gfxConfig::SetDefault(
+ aFeature,
+ false,
+ aStatus,
+ "Disabled in parent process");
+ break;
+ }
+}
+
+/* static */ bool
+gfxConfig::UseFallback(Fallback aFallback)
+{
+ return sConfig->UseFallbackImpl(aFallback);
+}
+
+/* static */ void
+gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage)
+{
+ // Ignore aMessage for now.
+ sConfig->EnableFallbackImpl(aFallback, aMessage);
+}
+
+bool
+gfxConfig::UseFallbackImpl(Fallback aFallback) const
+{
+ return !!(mFallbackBits & (uint64_t(1) << uint64_t(aFallback)));
+}
+
+void
+gfxConfig::EnableFallbackImpl(Fallback aFallback, const char* aMessage)
+{
+ if (!UseFallbackImpl(aFallback)) {
+ MOZ_ASSERT(mNumFallbackLogEntries < kNumFallbacks);
+
+ FallbackLogEntry& entry = mFallbackLog[mNumFallbackLogEntries];
+ mNumFallbackLogEntries++;
+
+ entry.mFallback = aFallback;
+ PL_strncpyz(entry.mMessage, aMessage, sizeof(entry.mMessage));
+ }
+ mFallbackBits |= (uint64_t(1) << uint64_t(aFallback));
+}
+
+struct FeatureInfo {
+ const char* name;
+ const char* description;
+};
+static const FeatureInfo sFeatureInfo[] = {
+#define FOR_EACH_FEATURE(name, type, desc) {#name, desc},
+ GFX_FEATURE_MAP(FOR_EACH_FEATURE)
+#undef FOR_EACH_FEATURE
+ {nullptr, nullptr}
+};
+
+/* static */ void
+gfxConfig::ForEachFeature(const FeatureIterCallback& aCallback)
+{
+ for (size_t i = 0; i < kNumFeatures; i++) {
+ FeatureState& state = GetFeature(static_cast<Feature>(i));
+ if (!state.IsInitialized()) {
+ continue;
+ }
+
+ aCallback(sFeatureInfo[i].name,
+ sFeatureInfo[i].description,
+ state);
+ }
+}
+
+static const char* sFallbackNames[] = {
+#define FOR_EACH_FALLBACK(name) #name,
+ GFX_FALLBACK_MAP(FOR_EACH_FALLBACK)
+#undef FOR_EACH_FALLBACK
+ nullptr
+};
+
+/* static */ void
+gfxConfig::ForEachFallback(const FallbackIterCallback& aCallback)
+{
+ sConfig->ForEachFallbackImpl(aCallback);
+}
+
+void
+gfxConfig::ForEachFallbackImpl(const FallbackIterCallback& aCallback)
+{
+ for (size_t i = 0; i < mNumFallbackLogEntries; i++) {
+ const FallbackLogEntry& entry = mFallbackLog[i];
+ aCallback(sFallbackNames[size_t(entry.mFallback)], entry.mMessage);
+ }
+}
+
+/* static */ const nsCString&
+gfxConfig::GetFailureId(Feature aFeature)
+{
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.GetFailureId();
+}
+
+/* static */ void
+gfxConfig::ImportChange(Feature aFeature, const FeatureChange& aChange)
+{
+ if (aChange.type() == FeatureChange::Tnull_t) {
+ return;
+ }
+
+ const FeatureFailure& failure = aChange.get_FeatureFailure();
+ gfxConfig::SetFailed(
+ aFeature,
+ failure.status(),
+ failure.message().get(),
+ failure.failureId());
+}
+
+/* static */ void
+gfxConfig::Init()
+{
+ sConfig = mozilla::MakeUnique<gfxConfig>();
+}
+
+/* static */ void
+gfxConfig::Shutdown()
+{
+ sConfig = nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/config/gfxConfig.h b/system/graphics/config/gfxConfig.h
new file mode 100644
index 000000000..d9e511b51
--- /dev/null
+++ b/system/graphics/config/gfxConfig.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_gfx_config_gfxConfig_h
+#define mozilla_gfx_config_gfxConfig_h
+
+#include "gfxFeature.h"
+#include "gfxFallback.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Function.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Defined in GraphicsMessages.ipdlh.
+class FeatureChange;
+
+// Manages the history and state of a graphics feature. The flow of a feature
+// is:
+// - A default value, set by all.js, gfxPrefs, or gfxPlatform.
+// - A user value, set by an external value or user pref.
+// - An environment value, determined by system/hardware factors or nsIGfxInfo.
+// - A runtime value, determined by any failures encountered after enabling
+// the feature.
+//
+// Each state change for a feature is recorded in this class.
+class gfxConfig
+{
+public:
+ // Return the full state history of a feature.
+ static FeatureState& GetFeature(Feature aFeature);
+
+ // Query whether a parameter is enabled, taking into account any user or
+ // runtime overrides. The algorithm works as follow:
+ //
+ // 1. If a runtime decision disabled the feature, return false.
+ // 2. If the user force-enabled the feature, return true.
+ // 3. If the environment disabled the feature, return false.
+ // 4. If the user specified a decision, return it.
+ // 5. Return the base setting for the feature.
+ static bool IsEnabled(Feature aFeature);
+
+ // Query the history of a parameter. ForcedOnByUser returns whether or not
+ // the user specifically used a "force" preference to enable the parameter.
+ // IsDisabledByDefault returns whether or not the initial status of the
+ // feature, before adding user prefs and runtime decisions, was disabled.
+ static bool IsForcedOnByUser(Feature aFeature);
+
+ // This returns true if the feature was disabled by default, or was never
+ // initialized to begin with.
+ static bool IsDisabledByDefault(Feature aFeature);
+
+ // Query the status value of a parameter. This is computed similar to
+ // IsEnabled:
+ //
+ // 1. If a runtime failure was set, return it.
+ // 2. If the user force-enabled the feature, return ForceEnabled.
+ // 3. If an environment status was set, return it.
+ // 4. If a user status was set, return it.
+ // 5. Return the default status.
+ static FeatureStatus GetValue(Feature aFeature);
+
+ // Initialize the base value of a parameter. The return value is aEnable.
+ static bool SetDefault(Feature aFeature,
+ bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage);
+ static void DisableByDefault(Feature aFeature,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = EmptyCString());
+ static void EnableByDefault(Feature aFeature);
+
+ // Inherit a computed value from another process.
+ static void Inherit(Feature aFeature, FeatureStatus aStatus);
+
+ // Set a environment status that overrides both the default and user
+ // statuses; this should be used to disable features based on system
+ // or hardware problems that can be determined up-front. The only
+ // status that can override this decision is the user force-enabling
+ // the feature.
+ static void Disable(Feature aFeature,
+ FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = EmptyCString());
+
+ // Given a preference name, infer the default value and whether or not the
+ // user has changed it. |aIsEnablePref| specifies whether or not the pref
+ // is intended to enable a feature (true), or disable it (false).
+ static void SetDefaultFromPref(Feature aFeature,
+ const char* aPrefName,
+ bool aIsEnablePref,
+ bool aDefaultValue);
+
+ // Disable a parameter based on a runtime decision. This permanently
+ // disables the feature, since runtime decisions override all other
+ // decisions.
+ static void SetFailed(Feature aFeature,
+ FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = EmptyCString());
+
+ // Force a feature to be disabled permanently. This is the same as
+ // SetFailed(), but the name may be clearer depending on the context.
+ static void ForceDisable(Feature aFeature,
+ FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = EmptyCString())
+ {
+ SetFailed(aFeature, aStatus, aMessage, aFailureId);
+ }
+
+ // Convenience helpers for SetFailed().
+ static bool MaybeSetFailed(Feature aFeature,
+ bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = EmptyCString())
+ {
+ if (!aEnable) {
+ SetFailed(aFeature, aDisableStatus, aDisableMessage, aFailureId);
+ return false;
+ }
+ return true;
+ }
+
+ // Convenience helper for SetFailed().
+ static bool MaybeSetFailed(Feature aFeature,
+ FeatureStatus aStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = EmptyCString())
+ {
+ return MaybeSetFailed(
+ aFeature,
+ (aStatus != FeatureStatus::Available &&
+ aStatus != FeatureStatus::ForceEnabled),
+ aStatus,
+ aDisableMessage, aFailureId);
+ }
+
+ // Re-enables a feature that was previously disabled, by attaching it to a
+ // fallback. The fallback inherits the message that was used for disabling
+ // the feature. This can be used, for example, when D3D11 fails at runtime
+ // but we acquire a second, successful device with WARP.
+ static void Reenable(Feature aFeature, Fallback aFallback);
+
+ // Same as SetDefault, except if the feature already has a default value
+ // set, the new value will be set as a runtime value. This is useful for
+ // when the base value can change (for example, via an update from the
+ // parent process).
+ static bool InitOrUpdate(Feature aFeature,
+ bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage);
+
+ // Set a user status that overrides the base value (but not runtime value)
+ // of a parameter.
+ static void UserEnable(Feature aFeature, const char* aMessage);
+ static void UserForceEnable(Feature aFeature, const char* aMessage);
+ static void UserDisable(Feature aFeature, const char* aMessage, const nsACString& aFailureId = EmptyCString());
+
+ // Query whether a fallback has been toggled.
+ static bool UseFallback(Fallback aFallback);
+
+ // Enable a fallback.
+ static void EnableFallback(Fallback aFallback, const char* aMessage);
+
+ // Run a callback for each initialized FeatureState.
+ typedef mozilla::function<void(const char* aName,
+ const char* aDescription,
+ FeatureState& aFeature)> FeatureIterCallback;
+ static void ForEachFeature(const FeatureIterCallback& aCallback);
+
+ // Run a callback for each enabled fallback.
+ typedef mozilla::function<void(const char* aName, const char* aMsg)>
+ FallbackIterCallback;
+ static void ForEachFallback(const FallbackIterCallback& aCallback);
+
+ // Get the most descriptive failure id message for this feature.
+ static const nsCString& GetFailureId(Feature aFeature);
+
+ static void ImportChange(Feature aFeature, const FeatureChange& aChange);
+
+ static void Init();
+ static void Shutdown();
+
+private:
+ void ForEachFallbackImpl(const FallbackIterCallback& aCallback);
+
+private:
+ FeatureState& GetState(Feature aFeature) {
+ MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
+ return mFeatures[size_t(aFeature)];
+ }
+ const FeatureState& GetState(Feature aFeature) const {
+ MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
+ return mFeatures[size_t(aFeature)];
+ }
+
+ bool UseFallbackImpl(Fallback aFallback) const;
+ void EnableFallbackImpl(Fallback aFallback, const char* aMessage);
+
+private:
+ static const size_t kNumFeatures = size_t(Feature::NumValues);
+ static const size_t kNumFallbacks = size_t(Fallback::NumValues);
+
+private:
+ FeatureState mFeatures[kNumFeatures];
+ uint64_t mFallbackBits;
+
+private:
+ struct FallbackLogEntry {
+ Fallback mFallback;
+ char mMessage[80];
+ };
+
+ FallbackLogEntry mFallbackLog[kNumFallbacks];
+ size_t mNumFallbackLogEntries;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxConfig_h
diff --git a/system/graphics/config/gfxFallback.h b/system/graphics/config/gfxFallback.h
new file mode 100644
index 000000000..18797862a
--- /dev/null
+++ b/system/graphics/config/gfxFallback.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_gfx_config_gfxFallback_h
+#define mozilla_gfx_config_gfxFallback_h
+
+#include <stdint.h>
+#include "gfxTelemetry.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GFX_FALLBACK_MAP(_) \
+ /* Name */ \
+ _(PLACEHOLDER_DO_NOT_USE) \
+ /* Add new entries above this comment */
+
+enum class Fallback : uint32_t {
+#define MAKE_ENUM(name) name,
+ GFX_FALLBACK_MAP(MAKE_ENUM)
+#undef MAKE_ENUM
+ NumValues
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxFallback_h
diff --git a/system/graphics/config/gfxFeature.cpp b/system/graphics/config/gfxFeature.cpp
new file mode 100644
index 000000000..4a37c4951
--- /dev/null
+++ b/system/graphics/config/gfxFeature.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFeature.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace gfx {
+
+bool
+FeatureState::IsEnabled() const
+{
+ return IsInitialized() && IsFeatureStatusSuccess(GetValue());
+}
+
+FeatureStatus
+FeatureState::GetValue() const
+{
+ if (!IsInitialized()) {
+ return FeatureStatus::Unused;
+ }
+
+ if (mRuntime.mStatus != FeatureStatus::Unused) {
+ return mRuntime.mStatus;
+ }
+ if (mUser.mStatus == FeatureStatus::ForceEnabled) {
+ return FeatureStatus::ForceEnabled;
+ }
+ if (mEnvironment.mStatus != FeatureStatus::Unused) {
+ return mEnvironment.mStatus;
+ }
+ if (mUser.mStatus != FeatureStatus::Unused) {
+ return mUser.mStatus;
+ }
+ return mDefault.mStatus;
+}
+
+bool
+FeatureState::SetDefault(bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage)
+{
+ if (!aEnable) {
+ DisableByDefault(aDisableStatus, aDisableMessage,
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_DISABLED"));
+ return false;
+ }
+ EnableByDefault();
+ return true;
+}
+
+void
+FeatureState::SetDefaultFromPref(const char* aPrefName,
+ bool aIsEnablePref,
+ bool aDefaultValue)
+{
+ bool baseValue = Preferences::GetDefaultBool(aPrefName, aDefaultValue);
+ SetDefault(baseValue == aIsEnablePref, FeatureStatus::Disabled, "Disabled by default");
+
+ if (Preferences::HasUserValue(aPrefName)) {
+ bool userValue = Preferences::GetBool(aPrefName, aDefaultValue);
+ if (userValue == aIsEnablePref) {
+ nsCString message("Enabled via ");
+ message.AppendASCII(aPrefName);
+ UserEnable(message.get());
+ } else {
+ nsCString message("Disabled via ");
+ message.AppendASCII(aPrefName);
+ UserDisable(message.get(), NS_LITERAL_CSTRING("FEATURE_FAILURE_PREF_OFF"));
+ }
+ }
+}
+
+bool
+FeatureState::InitOrUpdate(bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage)
+{
+ if (!IsInitialized()) {
+ return SetDefault(aEnable, aDisableStatus, aDisableMessage);
+ }
+ return MaybeSetFailed(aEnable, aDisableStatus, aDisableMessage, nsCString());
+}
+
+void
+FeatureState::UserEnable(const char* aMessage)
+{
+ AssertInitialized();
+ SetUser(FeatureStatus::Available, aMessage);
+}
+
+void
+FeatureState::UserForceEnable(const char* aMessage)
+{
+ AssertInitialized();
+ SetUser(FeatureStatus::ForceEnabled, aMessage);
+}
+
+void
+FeatureState::UserDisable(const char* aMessage, const nsACString& aFailureId)
+{
+ AssertInitialized();
+ SetUser(FeatureStatus::Disabled, aMessage);
+ SetFailureId(aFailureId);
+}
+
+void
+FeatureState::Disable(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ AssertInitialized();
+
+ // We should never bother setting an environment status to "enabled," since
+ // it could override an explicit user decision to disable it.
+ MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+ SetEnvironment(aStatus, aMessage);
+ SetFailureId(aFailureId);
+}
+
+void
+FeatureState::SetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ AssertInitialized();
+
+ // We should never bother setting a runtime status to "enabled," since it could
+ // override an explicit user decision to disable it.
+ MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+ SetRuntime(aStatus, aMessage);
+ SetFailureId(aFailureId);
+}
+
+bool
+FeatureState::MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ if (!aEnable) {
+ SetFailed(aStatus, aMessage, aFailureId);
+ return false;
+ }
+ return true;
+}
+
+bool
+FeatureState::MaybeSetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ return MaybeSetFailed(IsFeatureStatusSuccess(aStatus), aStatus, aMessage,
+ aFailureId);
+}
+
+bool
+FeatureState::DisabledByDefault() const
+{
+ return mDefault.mStatus != FeatureStatus::Available;
+}
+
+bool
+FeatureState::IsForcedOnByUser() const
+{
+ AssertInitialized();
+ return mUser.mStatus == FeatureStatus::ForceEnabled;
+}
+
+void
+FeatureState::EnableByDefault()
+{
+ // User/runtime decisions should not have been made yet.
+ MOZ_ASSERT(!mUser.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mDefault.Set(FeatureStatus::Available);
+}
+
+void
+FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ // User/runtime decisions should not have been made yet.
+ MOZ_ASSERT(!mUser.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mDefault.Set(aStatus, aMessage);
+ SetFailureId(aFailureId);
+}
+
+void
+FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage)
+{
+ // Default decision must have been made, but not runtime or environment.
+ MOZ_ASSERT(mDefault.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mUser.Set(aStatus, aMessage);
+}
+
+void
+FeatureState::SetEnvironment(FeatureStatus aStatus, const char* aMessage)
+{
+ // Default decision must have been made, but not runtime.
+ MOZ_ASSERT(mDefault.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mEnvironment.Set(aStatus, aMessage);
+}
+
+void
+FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage)
+{
+ AssertInitialized();
+
+ mRuntime.Set(aStatus, aMessage);
+}
+
+const char*
+FeatureState::GetRuntimeMessage() const
+{
+ MOZ_ASSERT(IsFeatureStatusFailure(mRuntime.mStatus));
+ return mRuntime.mMessage;
+}
+
+void
+FeatureState::ForEachStatusChange(const StatusIterCallback& aCallback) const
+{
+ AssertInitialized();
+
+ aCallback("default", mDefault.mStatus, mDefault.MessageOrNull());
+ if (mUser.IsInitialized()) {
+ aCallback("user", mUser.mStatus, mUser.Message());
+ }
+ if (mEnvironment.IsInitialized()) {
+ aCallback("env", mEnvironment.mStatus, mEnvironment.Message());
+ }
+ if (mRuntime.IsInitialized()) {
+ aCallback("runtime", mRuntime.mStatus, mRuntime.Message());
+ }
+}
+
+void
+FeatureState::SetFailureId(const nsACString& aFailureId)
+{
+ if (mFailureId.IsEmpty()) {
+ mFailureId = aFailureId;
+ }
+}
+
+const char*
+FeatureState::GetFailureMessage() const
+{
+ AssertInitialized();
+ MOZ_ASSERT(!IsEnabled());
+
+ if (mRuntime.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mRuntime.mStatus))
+ {
+ return mRuntime.mMessage;
+ }
+ if (mEnvironment.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mEnvironment.mStatus))
+ {
+ return mEnvironment.mMessage;
+ }
+ if (mUser.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mUser.mStatus))
+ {
+ return mUser.mMessage;
+ }
+
+ MOZ_ASSERT(IsFeatureStatusFailure(mDefault.mStatus));
+ return mDefault.mMessage;
+}
+
+const nsCString&
+FeatureState::GetFailureId() const
+{
+ MOZ_ASSERT(!IsEnabled());
+ return mFailureId;
+}
+
+void
+FeatureState::Reset()
+{
+ mDefault.Set(FeatureStatus::Unused);
+ mUser.Set(FeatureStatus::Unused);
+ mEnvironment.Set(FeatureStatus::Unused);
+ mRuntime.Set(FeatureStatus::Unused);
+ mFailureId = nsCString();
+}
+
+void
+FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage /* = nullptr */)
+{
+ mStatus = aStatus;
+ if (aMessage) {
+ SprintfLiteral(mMessage, "%s", aMessage);
+ } else {
+ mMessage[0] = '\0';
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/config/gfxFeature.h b/system/graphics/config/gfxFeature.h
new file mode 100644
index 000000000..78346aa53
--- /dev/null
+++ b/system/graphics/config/gfxFeature.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_gfx_config_gfxFeature_h
+#define mozilla_gfx_config_gfxFeature_h
+
+#include <stdint.h>
+#include "gfxTelemetry.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Function.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GFX_FEATURE_MAP(_) \
+ /* Name, Type, Description */ \
+ _(HW_COMPOSITING, Feature, "Compositing") \
+ _(D3D11_COMPOSITING, Feature, "Direct3D11 Compositing") \
+ _(OPENGL_COMPOSITING, Feature, "OpenGL Compositing") \
+ _(DIRECT2D, Feature, "Direct2D") \
+ _(D3D11_HW_ANGLE, Feature, "Direct3D11 hardware ANGLE") \
+ _(DIRECT_DRAW, Feature, "DirectDraw") \
+ _(GPU_PROCESS, Feature, "GPU Process") \
+ /* Add new entries above this comment */
+
+enum class Feature : uint32_t {
+#define MAKE_ENUM(name, type, desc) name,
+ GFX_FEATURE_MAP(MAKE_ENUM)
+#undef MAKE_ENUM
+ NumValues
+};
+
+class FeatureState
+{
+ friend class gfxConfig;
+
+ public:
+ bool IsEnabled() const;
+ FeatureStatus GetValue() const;
+
+ void EnableByDefault();
+ void DisableByDefault(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+ bool SetDefault(bool aEnable, FeatureStatus aDisableStatus, const char* aDisableMessage);
+ bool InitOrUpdate(bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aMessage);
+ void SetDefaultFromPref(const char* aPrefName,
+ bool aIsEnablePref,
+ bool aDefaultValue);
+ void UserEnable(const char* aMessage);
+ void UserForceEnable(const char* aMessage);
+ void UserDisable(const char* aMessage, const nsACString& aFailureId);
+ void Disable(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+ void ForceDisable(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId) {
+ SetFailed(aStatus, aMessage, aFailureId);
+ }
+ void SetFailed(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+ bool MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+ bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+
+ // aType is "base", "user", "env", or "runtime".
+ // aMessage may be null.
+ typedef mozilla::function<void(const char* aType,
+ FeatureStatus aStatus,
+ const char* aMessage)> StatusIterCallback;
+ void ForEachStatusChange(const StatusIterCallback& aCallback) const;
+
+ const char* GetFailureMessage() const;
+ const nsCString& GetFailureId() const;
+
+ bool DisabledByDefault() const;
+
+ private:
+ void SetUser(FeatureStatus aStatus, const char* aMessage);
+ void SetEnvironment(FeatureStatus aStatus, const char* aMessage);
+ void SetRuntime(FeatureStatus aStatus, const char* aMessage);
+ bool IsForcedOnByUser() const;
+ const char* GetRuntimeMessage() const;
+ bool IsInitialized() const {
+ return mDefault.IsInitialized();
+ }
+
+ void AssertInitialized() const {
+ MOZ_ASSERT(IsInitialized());
+ }
+
+ // Clear all state.
+ void Reset();
+
+ private:
+ void SetFailureId(const nsACString& aFailureId);
+
+ struct Instance {
+ char mMessage[64];
+ FeatureStatus mStatus;
+
+ void Set(FeatureStatus aStatus, const char* aMessage = nullptr);
+ bool IsInitialized() const {
+ return mStatus != FeatureStatus::Unused;
+ }
+ const char* MessageOrNull() const {
+ return mMessage[0] != '\0' ? mMessage : nullptr;
+ }
+ const char* Message() const {
+ MOZ_ASSERT(MessageOrNull());
+ return mMessage;
+ }
+ };
+
+ // The default state is the state we decide on startup, based on the operating
+ // system or a base preference.
+ //
+ // The user state factors in any changes to preferences that the user made.
+ //
+ // The environment state factors in any additional decisions made, such as
+ // availability or blacklisting.
+ //
+ // The runtime state factors in any problems discovered at runtime.
+ Instance mDefault;
+ Instance mUser;
+ Instance mEnvironment;
+ Instance mRuntime;
+
+ // Store the first reported failureId for now but we might want to track this
+ // by instance later if we need a specific breakdown.
+ nsCString mFailureId;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxFeature_h
diff --git a/system/graphics/config/gfxVarReceiver.h b/system/graphics/config/gfxVarReceiver.h
new file mode 100644
index 000000000..4025650c5
--- /dev/null
+++ b/system/graphics/config/gfxVarReceiver.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_gfx_config_gfxVarReceiver_h
+#define mozilla_gfx_config_gfxVarReceiver_h
+
+namespace mozilla {
+namespace gfx {
+
+class GfxVarUpdate;
+
+// This allows downstream processes (such as PContent, PGPU) to listen for
+// updates on gfxVarReceiver.
+class gfxVarReceiver
+{
+public:
+ virtual void OnVarChanged(const GfxVarUpdate& aVar) = 0;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxVarReceiver_h
diff --git a/system/graphics/config/gfxVars.cpp b/system/graphics/config/gfxVars.cpp
new file mode 100644
index 000000000..3676a7f57
--- /dev/null
+++ b/system/graphics/config/gfxVars.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxVars.h"
+#include "gfxVarReceiver.h"
+#include "mozilla/dom/ContentChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+StaticAutoPtr<gfxVars> gfxVars::sInstance;
+StaticAutoPtr<nsTArray<gfxVars::VarBase*>> gfxVars::sVarList;
+
+void
+gfxVars::Initialize()
+{
+ if (sInstance) {
+ return;
+ }
+
+ // sVarList must be initialized first since it's used in the constructor for
+ // sInstance.
+ sVarList = new nsTArray<gfxVars::VarBase*>();
+ sInstance = new gfxVars;
+
+ // Like Preferences, we want content to synchronously get initial data on
+ // init. Note the GPU process is not handled here - it cannot send sync
+ // messages, so instead the initial data is pushed down.
+ if (XRE_IsContentProcess()) {
+ InfallibleTArray<GfxVarUpdate> vars;
+ dom::ContentChild::GetSingleton()->SendGetGfxVars(&vars);
+ for (const auto& var : vars) {
+ ApplyUpdate(var);
+ }
+ }
+}
+
+gfxVars::gfxVars()
+{
+}
+
+void
+gfxVars::Shutdown()
+{
+ sInstance = nullptr;
+ sVarList = nullptr;
+}
+
+/* static */ void
+gfxVars::ApplyUpdate(const GfxVarUpdate& aUpdate)
+{
+ // Only subprocesses receive updates and apply them locally.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ sVarList->ElementAt(aUpdate.index())->SetValue(aUpdate.value());
+}
+
+/* static */ void
+gfxVars::AddReceiver(gfxVarReceiver* aReceiver)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Don't double-add receivers, in case a broken content process sends two
+ // init messages.
+ if (!sInstance->mReceivers.Contains(aReceiver)) {
+ sInstance->mReceivers.AppendElement(aReceiver);
+ }
+}
+
+/* static */ void
+gfxVars::RemoveReceiver(gfxVarReceiver* aReceiver)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sInstance) {
+ sInstance->mReceivers.RemoveElement(aReceiver);
+ }
+}
+
+/* static */ nsTArray<GfxVarUpdate>
+gfxVars::FetchNonDefaultVars()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sVarList);
+
+ nsTArray<GfxVarUpdate> updates;
+ for (size_t i = 0; i < sVarList->Length(); i++) {
+ VarBase* var = sVarList->ElementAt(i);
+ if (var->HasDefaultValue()) {
+ continue;
+ }
+
+ GfxVarValue value;
+ var->GetValue(&value);
+
+ updates.AppendElement(GfxVarUpdate(i, value));
+ }
+
+ return updates;
+}
+
+gfxVars::VarBase::VarBase()
+{
+ mIndex = gfxVars::sVarList->Length();
+ gfxVars::sVarList->AppendElement(this);
+}
+
+void
+gfxVars::NotifyReceivers(VarBase* aVar)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ GfxVarValue value;
+ aVar->GetValue(&value);
+
+ GfxVarUpdate update(aVar->Index(), value);
+ for (auto& receiver : mReceivers) {
+ receiver->OnVarChanged(update);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/config/gfxVars.h b/system/graphics/config/gfxVars.h
new file mode 100644
index 000000000..fd4864dd5
--- /dev/null
+++ b/system/graphics/config/gfxVars.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_gfx_config_gfxVars_h
+#define mozilla_gfx_config_gfxVars_h
+
+#include <stdint.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/Types.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace gfx {
+
+class gfxVarReceiver;
+
+// Generator for graphics vars.
+#define GFX_VARS_LIST(_) \
+ /* C++ Name, Data Type, Default Value */ \
+ _(BrowserTabsRemoteAutostart, bool, false) \
+ _(ContentBackend, BackendType, BackendType::NONE) \
+ _(TileSize, IntSize, IntSize(-1, -1)) \
+ _(UseXRender, bool, false) \
+ _(OffscreenFormat, gfxImageFormat, mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \
+ _(RequiresAcceleratedGLContextForCompositorOGL, bool, false) \
+ _(CanUseHardwareVideoDecoding, bool, false) \
+ _(PDMWMFDisableD3D11Dlls, nsCString, nsCString()) \
+ _(PDMWMFDisableD3D9Dlls, nsCString, nsCString()) \
+
+ /* Add new entries above this line. */
+
+// Some graphics settings are computed on the UI process and must be
+// communicated to content and GPU processes. gfxVars helps facilitate
+// this. Its function is similar to gfxPrefs, except rather than hold
+// user preferences, it holds dynamically computed values.
+//
+// Each variable in GFX_VARS_LIST exposes the following static methods:
+//
+// const DataType& CxxName();
+// void SetCxxName(const DataType& aValue);
+//
+// Note that the setter may only be called in the UI process; a gfxVar must be
+// a variable that is determined in the UI process and pushed to child
+// processes.
+class gfxVars final
+{
+public:
+ static void Initialize();
+ static void Shutdown();
+
+ static void ApplyUpdate(const GfxVarUpdate& aUpdate);
+ static void AddReceiver(gfxVarReceiver* aReceiver);
+ static void RemoveReceiver(gfxVarReceiver* aReceiver);
+
+ // Return a list of updates for all variables with non-default values.
+ static nsTArray<GfxVarUpdate> FetchNonDefaultVars();
+
+public:
+ // Each variable must expose Set and Get methods for IPDL.
+ class VarBase
+ {
+ public:
+ VarBase();
+ virtual void SetValue(const GfxVarValue& aValue) = 0;
+ virtual void GetValue(GfxVarValue* aOutValue) = 0;
+ virtual bool HasDefaultValue() const = 0;
+ size_t Index() const {
+ return mIndex;
+ }
+ private:
+ size_t mIndex;
+ };
+
+private:
+ static StaticAutoPtr<gfxVars> sInstance;
+ static StaticAutoPtr<nsTArray<VarBase*>> sVarList;
+
+ template <typename T, T Default()>
+ class VarImpl final : public VarBase
+ {
+ public:
+ VarImpl()
+ : mValue(Default())
+ {}
+ void SetValue(const GfxVarValue& aValue) override {
+ aValue.get(&mValue);
+ }
+ void GetValue(GfxVarValue* aOutValue) override {
+ *aOutValue = GfxVarValue(mValue);
+ }
+ bool HasDefaultValue() const override {
+ return mValue == Default();
+ }
+ const T& Get() const {
+ return mValue;
+ }
+ // Return true if the value changed, false otherwise.
+ bool Set(const T& aValue) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mValue == aValue) {
+ return false;
+ }
+ mValue = aValue;
+ return true;
+ }
+ private:
+ T mValue;
+ };
+
+#define GFX_VAR_DECL(CxxName, DataType, DefaultValue) \
+private: \
+ static DataType Get##CxxName##Default() { \
+ return DefaultValue; \
+ } \
+ VarImpl<DataType, Get##CxxName##Default> mVar##CxxName; \
+public: \
+ static const DataType& CxxName() { \
+ return sInstance->mVar##CxxName.Get(); \
+ } \
+ static void Set##CxxName(const DataType& aValue) { \
+ if (sInstance->mVar##CxxName.Set(aValue)) { \
+ sInstance->NotifyReceivers(&sInstance->mVar##CxxName); \
+ } \
+ }
+
+ GFX_VARS_LIST(GFX_VAR_DECL)
+#undef GFX_VAR_DECL
+
+private:
+ gfxVars();
+
+ void NotifyReceivers(VarBase* aVar);
+
+private:
+ nsTArray<gfxVarReceiver*> mReceivers;
+};
+
+#undef GFX_VARS_LIST
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxVars_h
diff --git a/system/graphics/config/moz.build b/system/graphics/config/moz.build
new file mode 100644
index 000000000..7311d8b36
--- /dev/null
+++ b/system/graphics/config/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'gfxConfig.h',
+ 'gfxFallback.h',
+ 'gfxFeature.h',
+]
+
+EXPORTS.mozilla.gfx += [
+ 'gfxVarReceiver.h',
+ 'gfxVars.h',
+]
+
+SOURCES += [
+ 'gfxConfig.cpp',
+ 'gfxFeature.cpp',
+ 'gfxVars.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/system/graphics/doc/AsyncPanZoom-HighLevel.png b/system/graphics/doc/AsyncPanZoom-HighLevel.png
new file mode 100644
index 000000000..d19dcb7c8
--- /dev/null
+++ b/system/graphics/doc/AsyncPanZoom-HighLevel.png
Binary files differ
diff --git a/system/graphics/doc/AsyncPanZoom.md b/system/graphics/doc/AsyncPanZoom.md
new file mode 100644
index 000000000..1fc58e03d
--- /dev/null
+++ b/system/graphics/doc/AsyncPanZoom.md
@@ -0,0 +1,299 @@
+Asynchronous Panning and Zooming {#apz}
+================================
+
+**This document is a work in progress. Some information may be missing or incomplete.**
+
+## Goals
+
+We need to be able to provide a visual response to user input with minimal latency.
+In particular, on devices with touch input, content must track the finger exactly while panning, or the user experience is very poor.
+According to the UX team, 120ms is an acceptable latency between user input and response.
+
+## Context and surrounding architecture
+
+The fundamental problem we are trying to solve with the Asynchronous Panning and Zooming (APZ) code is that of responsiveness.
+By default, web browsers operate in a "game loop" that looks like this:
+
+ while true:
+ process input
+ do computations
+ repaint content
+ display repainted content
+
+In browsers the "do computation" step can be arbitrarily expensive because it can involve running event handlers in web content.
+Therefore, there can be an arbitrary delay between the input being received and the on-screen display getting updated.
+
+Responsiveness is always good, and with touch-based interaction it is even more important than with mouse or keyboard input.
+In order to ensure responsiveness, we split the "game loop" model of the browser into a multithreaded variant which looks something like this:
+
+ Thread 1 (compositor thread)
+ while true:
+ receive input
+ send a copy of input to thread 2
+ adjust painted content based on input
+ display adjusted painted content
+
+ Thread 2 (main thread)
+ while true:
+ receive input from thread 1
+ do computations
+ repaint content
+ update the copy of painted content in thread 1
+
+This multithreaded model is called off-main-thread compositing (OMTC), because the compositing (where the content is displayed on-screen) happens on a separate thread from the main thread.
+Note that this is a very very simplified model, but in this model the "adjust painted content based on input" is the primary function of the APZ code.
+
+The "painted content" is stored on a set of "layers", that are conceptually double-buffered.
+That is, when the main thread does its repaint, it paints into one set of layers (the "client" layers).
+The update that is sent to the compositor thread copies all the changes from the client layers into another set of layers that the compositor holds.
+These layers are called the "shadow" layers or the "compositor" layers.
+The compositor in theory can continuously composite these shadow layers to the screen while the main thread is busy doing other things and painting a new set of client layers.
+
+The APZ code takes the input events that are coming in from the hardware and uses them to figure out what the user is trying to do (e.g. pan the page, zoom in).
+It then expresses this user intention in the form of translation and/or scale transformation matrices.
+These transformation matrices are applied to the shadow layers at composite time, so that what the user sees on-screen reflects what they are trying to do as closely as possible.
+
+## Technical overview
+
+As per the heavily simplified model described above, the fundamental purpose of the APZ code is to take input events and produce transformation matrices.
+This section attempts to break that down and identify the different problems that make this task non-trivial.
+
+### Checkerboarding
+
+The content area that is painted and stored in a shadow layer is called the "displayport".
+The APZ code is responsible for determining how large the displayport should be.
+On the one hand, we want the displayport to be as large as possible.
+At the very least it needs to be larger than what is visible on-screen, because otherwise, as soon as the user pans, there will be some unpainted area of the page exposed.
+However, we cannot always set the displayport to be the entire page, because the page can be arbitrarily long and this would require an unbounded amount of memory to store.
+Therefore, a good displayport size is one that is larger than the visible area but not so large that it is a huge drain on memory.
+Because the displayport is usually smaller than the whole page, it is always possible for the user to scroll so fast that they end up in an area of the page outside the displayport.
+When this happens, they see unpainted content; this is referred to as "checkerboarding", and we try to avoid it where possible.
+
+There are many possible ways to determine what the displayport should be in order to balance the tradeoffs involved (i.e. having one that is too big is bad for memory usage, and having one that is too small results in excessive checkerboarding).
+Ideally, the displayport should cover exactly the area that we know the user will make visible.
+Although we cannot know this for sure, we can use heuristics based on current panning velocity and direction to ensure a reasonably-chosen displayport area.
+This calculation is done in the APZ code, and a new desired displayport is frequently sent to the main thread as the user is panning around.
+
+### Multiple layers
+
+Consider, for example, a scrollable page that contains an iframe which itself is scrollable.
+The iframe can be scrolled independently of the top-level page, and we would like both the page and the iframe to scroll responsively.
+This means that we want independent asynchronous panning for both the top-level page and the iframe.
+In addition to iframes, elements that have the overflow:scroll CSS property set are also scrollable, and also end up on separate scrollable layers.
+In the general case, the layers are arranged in a tree structure, and so within the APZ code we have a matching tree of AsyncPanZoomController (APZC) objects, one for each scrollable layer.
+To manage this tree of APZC instances, we have a single APZCTreeManager object.
+Each APZC is relatively independent and handles the scrolling for its associated layer, but there are some cases in which they need to interact; these cases are described in the sections below.
+
+### Hit detection
+
+Consider again the case where we have a scrollable page that contains an iframe which itself is scrollable.
+As described above, we will have two APZC instances - one for the page and one for the iframe.
+When the user puts their finger down on the screen and moves it, we need to do some sort of hit detection in order to determine whether their finger is on the iframe or on the top-level page.
+Based on where their finger lands, the appropriate APZC instance needs to handle the input.
+This hit detection is also done in the APZCTreeManager, as it has the necessary information about the sizes and positions of the layers.
+Currently this hit detection is not perfect, as it uses rects and does not account for things like rounded corners and opacity.
+
+Also note that for some types of input (e.g. when the user puts two fingers down to do a pinch) we do not want the input to be "split" across two different APZC instances.
+In the case of a pinch, for example, we find a "common ancestor" APZC instance - one that is zoomable and contains all of the touch input points, and direct the input to that APZC instance.
+
+### Scroll Handoff
+
+Consider yet again the case where we have a scrollable page that contains an iframe which itself is scrollable.
+Say the user scrolls the iframe so that it reaches the bottom.
+If the user continues panning on the iframe, the expectation is that the top-level page will start scrolling.
+However, as discussed in the section on hit detection, the APZC instance for the iframe is separate from the APZC instance for the top-level page.
+Thus, we need the two APZC instances to communicate in some way such that input events on the iframe result in scrolling on the top-level page.
+This behaviour is referred to as "scroll handoff" (or "fling handoff" in the case where analogous behaviour results from the scrolling momentum of the page after the user has lifted their finger).
+
+### Input event untransformation
+
+The APZC architecture by definition results in two copies of a "scroll position" for each scrollable layer.
+There is the original copy on the main thread that is accessible to web content and the layout and painting code.
+And there is a second copy on the compositor side, which is updated asynchronously based on user input, and corresponds to what the user visually sees on the screen.
+Although these two copies may diverge temporarily, they are reconciled periodically.
+In particular, they diverge while the APZ code is performing an async pan or zoom action on behalf of the user, and are reconciled when the APZ code requests a repaint from the main thread.
+
+Because of the way input events are stored, this has some unfortunate consequences.
+Input events are stored relative to the device screen - so if the user touches at the same physical spot on the device, the same input events will be delivered regardless of the content scroll position.
+When the main thread receives a touch event, it combines that with the content scroll position in order to figure out what DOM element the user touched.
+However, because we now have two different scroll positions, this process may not work perfectly.
+A concrete example follows:
+
+Consider a device with screen size 600 pixels tall.
+On this device, a user is viewing a document that is 1000 pixels tall, and that is scrolled down by 200 pixels.
+That is, the vertical section of the document from 200px to 800px is visible.
+Now, if the user touches a point 100px from the top of the physical display, the hardware will generate a touch event with y=100.
+This will get sent to the main thread, which will add the scroll position (200) and get a document-relative touch event with y=300.
+This new y-value will be used in hit detection to figure out what the user touched.
+If the document had a absolute-positioned div at y=300, then that would receive the touch event.
+
+Now let us add some async scrolling to this example.
+Say that the user additionally scrolls the document by another 10 pixels asynchronously (i.e. only on the compositor thread), and then does the same touch event.
+The same input event is generated by the hardware, and as before, the document will deliver the touch event to the div at y=300.
+However, visually, the document is scrolled by an additional 10 pixels so this outcome is wrong.
+What needs to happen is that the APZ code needs to intercept the touch event and account for the 10 pixels of asynchronous scroll.
+Therefore, the input event with y=100 gets converted to y=110 in the APZ code before being passed on to the main thread.
+The main thread then adds the scroll position it knows about and determines that the user touched at a document-relative position of y=310.
+
+Analogous input event transformations need to be done for horizontal scrolling and zooming.
+
+### Content independently adjusting scrolling
+
+As described above, there are two copies of the scroll position in the APZ architecture - one on the main thread and one on the compositor thread.
+Usually for architectures like this, there is a single "source of truth" value and the other value is simply a copy.
+However, in this case that is not easily possible to do.
+The reason is that both of these values can be legitimately modified.
+On the compositor side, the input events the user is triggering modify the scroll position, which is then propagated to the main thread.
+However, on the main thread, web content might be running Javascript code that programatically sets the scroll position (via window.scrollTo, for example).
+Scroll changes driven from the main thread are just as legitimate and need to be propagated to the compositor thread, so that the visual display updates in response.
+
+Because the cross-thread messaging is asynchronous, reconciling the two types of scroll changes is a tricky problem.
+Our design solves this using various flags and generation counters.
+The general heuristic we have is that content-driven scroll position changes (e.g. scrollTo from JS) are never lost.
+For instance, if the user is doing an async scroll with their finger and content does a scrollTo in the middle, then some of the async scroll would occur before the "jump" and the rest after the "jump".
+
+### Content preventing default behaviour of input events
+
+Another problem that we need to deal with is that web content is allowed to intercept touch events and prevent the "default behaviour" of scrolling.
+This ability is defined in web standards and is non-negotiable.
+Touch event listeners in web content are allowed call preventDefault() on the touchstart or first touchmove event for a touch point; doing this is supposed to "consume" the event and prevent touch-based panning.
+As we saw in a previous section, the input event needs to be untransformed by the APZ code before it can be delivered to content.
+But, because of the preventDefault problem, we cannot fully process the touch event in the APZ code until content has had a chance to handle it.
+Web browsers in general solve this problem by inserting a delay of up to 300ms before processing the input - that is, web content is allowed up to 300ms to process the event and call preventDefault on it.
+If web content takes longer than 300ms, or if it completes handling of the event without calling preventDefault, then the browser immediately starts processing the events.
+
+The way the APZ implementation deals with this is that upon receiving a touch event, it immediately returns an untransformed version that can be dispatched to content.
+It also schedules a 400ms timeout (600ms on Android) during which content is allowed to prevent scrolling.
+There is an API that allows the main-thread event dispatching code to notify the APZ as to whether or not the default action should be prevented.
+If the APZ content response timeout expires, or if the main-thread event dispatching code notifies the APZ of the preventDefault status, then the APZ continues with the processing of the events (which may involve discarding the events).
+
+The touch-action CSS property from the pointer-events spec is intended to allow eliminating this 400ms delay in many cases (although for backwards compatibility it will still be needed for a while).
+Note that even with touch-action implemented, there may be cases where the APZ code does not know the touch-action behaviour of the point the user touched.
+In such cases, the APZ code will still wait up to 400ms for the main thread to provide it with the touch-action behaviour information.
+
+## Technical details
+
+This section describes various pieces of the APZ code, and goes into more specific detail on APIs and code than the previous sections.
+The primary purpose of this section is to help people who plan on making changes to the code, while also not going into so much detail that it needs to be updated with every patch.
+
+### Overall flow of input events
+
+This section describes how input events flow through the APZ code.
+<ol>
+<li value="1">
+Input events arrive from the hardware/widget code into the APZ via APZCTreeManager::ReceiveInputEvent.
+The thread that invokes this is called the input thread, and may or may not be the same as the Gecko main thread.
+</li>
+<li value="2">
+Conceptually the first thing that the APZCTreeManager does is to associate these events with "input blocks".
+An input block is a set of events that share certain properties, and generally are intended to represent a single gesture.
+For example with touch events, all events following a touchstart up to but not including the next touchstart are in the same block.
+All of the events in a given block will go to the same APZC instance and will either all be processed or all be dropped.
+</li>
+<li value="3">
+Using the first event in the input block, the APZCTreeManager does a hit-test to see which APZC it hits.
+This hit-test uses the event regions populated on the layers, which may be larger than the true hit area of the layer.
+If no APZC is hit, the events are discarded and we jump to step 6.
+Otherwise, the input block is tagged with the hit APZC as a tentative target and put into a global APZ input queue.
+</li>
+<li value="4">
+ <ol>
+ <li value="i">
+ If the input events landed outside the dispatch-to-content event region for the layer, any available events in the input block are processed.
+ These may trigger behaviours like scrolling or tap gestures.
+ </li>
+ <li value="ii">
+ If the input events landed inside the dispatch-to-content event region for the layer, the events are left in the queue and a 400ms timeout is initiated.
+ If the timeout expires before step 9 is completed, the APZ assumes the input block was not cancelled and the tentative target is correct, and processes them as part of step 10.
+ </li>
+ </ol>
+</li>
+<li value="5">
+The call stack unwinds back to APZCTreeManager::ReceiveInputEvent, which does an in-place modification of the input event so that any async transforms are removed.
+</li>
+<li value="6">
+The call stack unwinds back to the widget code that called ReceiveInputEvent.
+This code now has the event in the coordinate space Gecko is expecting, and so can dispatch it to the Gecko main thread.
+</li>
+<li value="7">
+Gecko performs its own usual hit-testing and event dispatching for the event.
+As part of this, it records whether any touch listeners cancelled the input block by calling preventDefault().
+It also activates inactive scrollframes that were hit by the input events.
+</li>
+<li value="8">
+The call stack unwinds back to the widget code, which sends two notifications to the APZ code on the input thread.
+The first notification is via APZCTreeManager::ContentReceivedInputBlock, and informs the APZ whether the input block was cancelled.
+The second notification is via APZCTreeManager::SetTargetAPZC, and informs the APZ of the results of the Gecko hit-test during event dispatch.
+Note that Gecko may report that the input event did not hit any scrollable frame at all.
+The SetTargetAPZC notification happens only once per input block, while the ContentReceivedInputBlock notification may happen once per block, or multiple times per block, depending on the input type.
+</li>
+<li value="9">
+ <ol>
+ <li value="i">
+ If the events were processed as part of step 4(i), the notifications from step 8 are ignored and step 10 is skipped.
+ </li>
+ <li value="ii">
+ If events were queued as part of step 4(ii), and steps 5-8 take less than 400ms, the arrival of both notifications from step 8 will mark the input block ready for processing.
+ </li>
+ <li value="iii">
+ If events were queued as part of step 4(ii), but steps 5-8 take longer than 400ms, the notifications from step 8 will be ignored and step 10 will already have happened.
+ </li>
+ </ol>
+</li>
+<li value="10">
+If events were queued as part of step 4(ii) they are now either processed (if the input block was not cancelled and Gecko detected a scrollframe under the input event, or if the timeout expired) or dropped (all other cases).
+Note that the APZC that processes the events may be different at this step than the tentative target from step 3, depending on the SetTargetAPZC notification.
+Processing the events may trigger behaviours like scrolling or tap gestures.
+</li>
+</ol>
+
+If the CSS touch-action property is enabled, the above steps are modified as follows:
+<ul>
+<li>
+ In step 4, the APZC also requires the allowed touch-action behaviours for the input event.
+ This might have been determined as part of the hit-test in APZCTreeManager; if not, the events are queued.
+</li>
+<li>
+ In step 6, the widget code determines the content element at the point under the input element, and notifies the APZ code of the allowed touch-action behaviours.
+ This notification is sent via a call to APZCTreeManager::SetAllowedTouchBehavior on the input thread.
+</li>
+<li>
+ In step 9(ii), the input block will only be marked ready for processing once all three notifications arrive.
+</li>
+</ul>
+
+#### Threading considerations
+
+The bulk of the input processing in the APZ code happens on what we call "the input thread".
+In practice the input thread could be the Gecko main thread, the compositor thread, or some other thread.
+There are obvious downsides to using the Gecko main thread - that is, "asynchronous" panning and zooming is not really asynchronous as input events can only be processed while Gecko is idle.
+In an e10s environment, using the Gecko main thread of the chrome process is acceptable, because the code running in that process is more controllable and well-behaved than arbitrary web content.
+Using the compositor thread as the input thread could work on some platforms, but may be inefficient on others.
+For example, on Android (Fennec) we receive input events from the system on a dedicated UI thread.
+We would have to redispatch the input events to the compositor thread if we wanted to the input thread to be the same as the compositor thread.
+This introduces a potential for higher latency, particularly if the compositor does any blocking operations - blocking SwapBuffers operations, for example.
+As a result, the APZ code itself does not assume that the input thread will be the same as the Gecko main thread or the compositor thread.
+
+#### Active vs. inactive scrollframes
+
+The number of scrollframes on a page is potentially unbounded.
+However, we do not want to create a separate layer for each scrollframe right away, as this would require large amounts of memory.
+Therefore, scrollframes as designated as either "active" or "inactive".
+Active scrollframes are the ones that do have their contents put on a separate layer (or set of layers), and inactive ones do not.
+
+Consider a page with a scrollframe that is initially inactive.
+When layout generates the layers for this page, the content of the scrollframe will be flattened into some other PaintedLayer (call it P).
+The layout code also adds the area (or bounding region in case of weird shapes) of the scrollframe to the dispatch-to-content region of P.
+
+When the user starts interacting with that content, the hit-test in the APZ code finds the dispatch-to-content region of P.
+The input block therefore has a tentative target of P when it goes into step 4(ii) in the flow above.
+When gecko processes the input event, it must detect the inactive scrollframe and activate it, as part of step 7.
+Finally, the widget code sends the SetTargetAPZC notification in step 8 to notify the APZ that the input block should really apply to this new layer.
+The issue here is that the layer transaction containing the new layer must reach the compositor and APZ before the SetTargetAPZC notification.
+If this does not occur within the 400ms timeout, the APZ code will be unable to update the tentative target, and will continue to use P for that input block.
+Input blocks that start after the layer transaction will get correctly routed to the new layer as there will now be a layer and APZC instance for the active scrollframe.
+
+This model implies that when the user initially attempts to scroll an inactive scrollframe, it may end up scrolling an ancestor scrollframe.
+(This is because in the absence of the SetTargetAPZC notification, the input events will get applied to the closest ancestor scrollframe's APZC.)
+Only after the round-trip to the gecko thread is complete is there a layer for async scrolling to actually occur on the scrollframe itself.
+At that point the scrollframe will start receiving new input blocks and will scroll normally.
diff --git a/system/graphics/doc/B2GInputFlow.svg b/system/graphics/doc/B2GInputFlow.svg
new file mode 100644
index 000000000..ee6f4332c
--- /dev/null
+++ b/system/graphics/doc/B2GInputFlow.svg
@@ -0,0 +1,349 @@
+<?xml version="1.0"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="800">
+ <title>Touch input event flow on B2G</title>
+ <g id="arrows"></g>
+ <style type="text/css"><![CDATA[
+ text {
+ fill: black;
+ text-anchor: middle;
+ white-space: pre-line;
+ font-size: 14px;
+ }
+ rect {
+ fill: none;
+ }
+ line {
+ stroke: black;
+ }
+ .parentinput rect {
+ stroke: black;
+ }
+ text.parentinput {
+ fill: black;
+ text-anchor: start;
+ }
+ .parentmain rect {
+ stroke: orange;
+ }
+ text.parentmain {
+ fill: orange;
+ text-anchor: start;
+ }
+ .parentcompositor rect {
+ stroke: green;
+ }
+ text.parentcompositor {
+ fill: green;
+ text-anchor: start;
+ }
+ .childmain rect {
+ stroke: red;
+ }
+ text.childmain {
+ fill: red;
+ text-anchor: start;
+ }
+ .bothmain rect {
+ stroke: blue;
+ }
+ text.bothmain {
+ fill: blue;
+ text-anchor: start;
+ }
+ ]]></style>
+ <script type="text/javascript"><![CDATA[
+ var svg = "http://www.w3.org/2000/svg";
+ var maxY = 0;
+
+ function breaks(text) {
+ var count = 0;
+ for (var i = text.length - 1; i >= 0; i--) {
+ if (text.charAt(i) == '\n') {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ function makeAction(text, x, y, thread) {
+ maxY = Math.max(maxY, y);
+ var g = document.createElementNS(svg, "g");
+ g.setAttribute("class", "action " + thread);
+ g.setAttribute("transform", "translate(" + x + ", " + (y + 30) + ")");
+ var r = document.createElementNS(svg, "rect");
+ r.setAttribute("width", "100");
+ r.setAttribute("height", "40");
+ var t = document.createElementNS(svg, "text");
+ t.setAttribute("x", "50");
+ t.setAttribute("y", 25 - (7 * breaks(text)));
+ t.appendChild(document.createTextNode(text));
+ g.appendChild(r);
+ g.appendChild(t);
+ return g;
+ }
+
+ function makeChoice(text, x, y, thread) {
+ maxY = Math.max(maxY, y);
+ var g = document.createElementNS(svg, "g");
+ g.setAttribute("class", "choice " + thread);
+ g.setAttribute("transform", "translate(" + (x + 15) + ", " + (y + 15) + ")");
+ var g2 = document.createElementNS(svg, "g");
+ g2.setAttribute("transform", "rotate(-45, 35, 35)");
+ var r = document.createElementNS(svg, "rect");
+ r.setAttribute("width", "70");
+ r.setAttribute("height", "70");
+ g2.appendChild(r);
+ var t = document.createElementNS(svg, "text");
+ t.setAttribute("x", "35");
+ t.setAttribute("y", 40 - (7 * breaks(text)));
+ t.appendChild(document.createTextNode(text));
+ g.appendChild(g2);
+ g.appendChild(t);
+ return g;
+ }
+
+ function makeLabelChoice(label, point) {
+ var t = document.createElementNS(svg, "text");
+ t.setAttribute("x", point.x);
+ t.setAttribute("y", point.y);
+ t.appendChild(document.createTextNode(label));
+ return t;
+ }
+
+ function makeLine(sx, sy, ex, ey) {
+ maxY = Math.max(maxY, sy, ey);
+ var l = document.createElementNS(svg, "line");
+ l.setAttribute("x1", sx);
+ l.setAttribute("y1", sy);
+ l.setAttribute("x2", ex);
+ l.setAttribute("y2", ey);
+ return l;
+ }
+
+ function makeArrow(start, end) {
+ var g = document.createElementNS(svg, "g");
+ g.appendChild(makeLine(start.x, start.y, end.x, end.y));
+ if (start.x != end.x) {
+ start.x = end.x + (4 * Math.sign(start.x - end.x));
+ g.appendChild(makeLine(start.x, start.y - 4, end.x, end.y));
+ g.appendChild(makeLine(start.x, start.y + 4, end.x, end.y));
+ } else if (start.y != end.y) {
+ start.y = end.y + (4 * Math.sign(start.y - end.y));
+ g.appendChild(makeLine(start.x - 4, start.y, end.x, end.y));
+ g.appendChild(makeLine(start.x + 4, start.y, end.x, end.y));
+ }
+ return g;
+ }
+
+ function makeVHArrow(start, end) {
+ var g = document.createElementNS(svg, "g");
+ g.appendChild(makeLine(start.x, start.y, start.x, end.y));
+ start.y = end.y;
+ g.appendChild(makeArrow(start, end));
+ return g;
+ }
+
+ function makeHVArrow(start, end) {
+ var g = document.createElementNS(svg, "g");
+ g.appendChild(makeLine(start.x, start.y, end.x, start.y));
+ start.x = end.x;
+ g.appendChild(makeArrow(start, end));
+ return g;
+ }
+
+ function makeVHVArrow(start, end, length) {
+ var g = document.createElementNS(svg, "g");
+ g.appendChild(makeLine(start.x, start.y, start.x, start.y + length));
+ start.y += length;
+ g.appendChild(makeLine(start.x, start.y, end.x, start.y));
+ start.x = end.x;
+ g.appendChild(makeArrow(start, end));
+ return g;
+ }
+
+ function makeHVHArrow(start, end, length) {
+ var g = document.createElementNS(svg, "g");
+ g.appendChild(makeLine(start.x, start.y, start.x + length, start.y));
+ start.x += length;
+ g.appendChild(makeLine(start.x, start.y, start.x, end.y));
+ start.y = end.y;
+ g.appendChild(makeArrow(start, end));
+ return g;
+ }
+
+ function translation(group) {
+ var r = new RegExp("translate\\((\\d+), (\\d+)\\)");
+ var result = r.exec(group.getAttribute("transform"));
+ return { x: parseInt(result[1]), y: parseInt(result[2]) };
+ }
+
+ function isAction(group) {
+ return group.classList.contains("action");
+ }
+
+ function isChoice(group) {
+ return group.classList.contains("choice");
+ }
+
+ function offset(point, x, y) {
+ point.x += x;
+ point.y += y;
+ return point;
+ }
+
+ function rightOf(group) {
+ var t = translation(group);
+ if (isAction(group)) {
+ return offset(t, 100, 20);
+ }
+ if (isChoice(group)) {
+ return offset(t, 85, 35);
+ }
+ return t;
+ }
+
+ function leftOf(group) {
+ var t = translation(group);
+ if (isAction(group)) {
+ return offset(t, 0, 20);
+ }
+ if (isChoice(group)) {
+ return offset(t, -15, 35);
+ }
+ return t;
+ }
+
+ function topOf(group) {
+ var t = translation(group);
+ if (isAction(group)) {
+ return offset(t, 50, 0);
+ }
+ if (isChoice(group)) {
+ return offset(t, 35, -15);
+ }
+ return t;
+ }
+
+ function bottomOf(group) {
+ var t = translation(group);
+ if (isAction(group)) {
+ return offset(t, 50, 40);
+ }
+ if (isChoice(group)) {
+ return offset(t, 35, 85);
+ }
+ return t;
+ }
+
+ function midpoint(start, end) {
+ return { x: (start.x + end.x) / 2,
+ y: (start.y + end.y) / 2 };
+ }
+
+ function makeLegend(label, thread) {
+ var t = document.createElementNS(svg, "text");
+ t.setAttribute("x", "10");
+ t.setAttribute("y", maxY);
+ t.setAttribute("class", thread);
+ maxY += 15;
+ t.appendChild(document.createTextNode(label));
+ return t;
+ }
+
+ var android = makeAction("Android/Gonk", 20, 0, "parentinput");
+ var sendNative = makeAction("DOMWindowUtils\nsendNativeTouchPoint", 20, 100, "parentmain");
+ var apzHitTest = makeAction("APZ hit test", 150, 0, "parentcompositor");
+ var apzUntransform = makeAction("APZ\nuntransform", 300, 0, "parentcompositor");
+ var apzGesture = makeAction("APZ gesture\ndetection", 450, 0, "parentcompositor");
+ var apzTransform = makeAction("APZ transform\nupdate", 600, 0, "parentcompositor");
+ var compositor = makeAction("Compositor", 750, 0, "parentcompositor");
+ var nsAppShell = makeAction("nsAppShell", 150, 100, "parentmain");
+ var rootHitTest = makeAction("Gecko hit test\n(root process)", 300, 100, "parentmain");
+ var rootEsm = makeAction("Gecko ESM\n(root process)", 450, 100, "parentmain");
+ var isEdgeGesture = makeChoice("Edge gesture?", 300, 200, "parentmain");
+ var edgeConsume = makeAction("Consume\nevent block", 150, 200, "parentmain");
+ var bepjsm = makeAction("BEParent.jsm\nsendTouchEvent", 450, 200, "parentmain");
+ var iframeSend = makeAction("HTMLIFrameElement\nsendTouchEvent", 20, 275, "parentmain");
+ var isApzTarget = makeChoice("Target\nhas APZ?", 600, 200, "parentmain");
+ var sendTouchEvent = makeAction("Target\nsendTouchEventToWindow", 750, 100, "parentmain");
+ var injectTouch = makeAction("injectTouchEvent", 750, 200, "parentmain");
+ var targetESM = makeAction("Target window\nESM", 750, 450, "bothmain");
+ var tabParent = makeAction("TabParent", 750, 350, "parentmain");
+ var geckoUntransform = makeAction("Gecko\nuntransform", 600, 350, "parentmain");
+ var tabChild = makeAction("TabChild", 450, 350, "childmain");
+ var isApzcEnabled = makeChoice("APZ\nenabled?", 300, 350, "childmain");
+ var tabGesture = makeAction("TabChild gesture\ndetection", 150, 350, "childmain");
+ var childHitTest = makeAction("Gecko hit test\n(child process)", 300, 450, "childmain");
+ var childEsm = makeAction("Gecko ESM\n(child process)", 450, 450, "childmain");
+ var childContent = makeAction("Content\n(child process)", 600, 450, "childmain");
+
+ document.documentElement.appendChild(android);
+ document.documentElement.appendChild(sendNative);
+ document.documentElement.appendChild(apzHitTest);
+ document.documentElement.appendChild(apzUntransform);
+ document.documentElement.appendChild(apzGesture);
+ document.documentElement.appendChild(apzTransform);
+ document.documentElement.appendChild(compositor);
+ document.documentElement.appendChild(nsAppShell);
+ document.documentElement.appendChild(rootHitTest);
+ document.documentElement.appendChild(rootEsm);
+ document.documentElement.appendChild(isEdgeGesture);
+ document.documentElement.appendChild(edgeConsume);
+ document.documentElement.appendChild(bepjsm);
+ document.documentElement.appendChild(iframeSend);
+ document.documentElement.appendChild(isApzTarget);
+ document.documentElement.appendChild(sendTouchEvent);
+ document.documentElement.appendChild(injectTouch);
+ document.documentElement.appendChild(targetESM);
+ document.documentElement.appendChild(tabParent);
+ document.documentElement.appendChild(geckoUntransform);
+ document.documentElement.appendChild(tabChild);
+ document.documentElement.appendChild(isApzcEnabled);
+ document.documentElement.appendChild(tabGesture);
+ document.documentElement.appendChild(childHitTest);
+ document.documentElement.appendChild(childEsm);
+ document.documentElement.appendChild(childContent);
+
+ document.documentElement.appendChild(makeLabelChoice("Y", offset(leftOf(isEdgeGesture), -5, -5)));
+ document.documentElement.appendChild(makeLabelChoice("N", offset(rightOf(isEdgeGesture), 5, -5)));
+ document.documentElement.appendChild(makeLabelChoice("N", offset(topOf(isApzTarget), 8, -10)));
+ document.documentElement.appendChild(makeLabelChoice("Y", offset(rightOf(isApzTarget), 10, 14)));
+ document.documentElement.appendChild(makeLabelChoice("N", offset(leftOf(isApzcEnabled), -5, -5)));
+ document.documentElement.appendChild(makeLabelChoice("Y", offset(bottomOf(isApzcEnabled), 10, 14)));
+
+ var arrows = document.getElementById('arrows');
+ arrows.appendChild(makeArrow(rightOf(android), leftOf(apzHitTest)));
+ arrows.appendChild(makeVHVArrow(topOf(sendNative), midpoint(rightOf(android), leftOf(apzHitTest)), -20));
+ arrows.appendChild(makeArrow(rightOf(apzHitTest), leftOf(apzUntransform)));
+ arrows.appendChild(makeArrow(rightOf(apzUntransform), leftOf(apzGesture)));
+ arrows.appendChild(makeArrow(rightOf(apzGesture), leftOf(apzTransform)));
+ arrows.appendChild(makeArrow(rightOf(apzTransform), leftOf(compositor)));
+ arrows.appendChild(makeVHVArrow(midpoint(leftOf(apzUntransform), rightOf(apzGesture)), topOf(nsAppShell), 40));
+ arrows.appendChild(makeArrow(rightOf(nsAppShell), leftOf(rootHitTest)));
+ arrows.appendChild(makeArrow(rightOf(rootHitTest), leftOf(rootEsm)));
+ arrows.appendChild(makeVHVArrow(bottomOf(rootEsm), topOf(isEdgeGesture), 15));
+ arrows.appendChild(makeArrow(leftOf(isEdgeGesture), rightOf(edgeConsume)));
+ arrows.appendChild(makeArrow(rightOf(isEdgeGesture), leftOf(bepjsm), 20));
+ arrows.appendChild(makeHVArrow(rightOf(iframeSend), bottomOf(bepjsm)));
+ arrows.appendChild(makeArrow(rightOf(bepjsm), leftOf(isApzTarget)));
+ arrows.appendChild(makeArrow(rightOf(isApzTarget), leftOf(injectTouch)));
+ arrows.appendChild(makeArrow(bottomOf(injectTouch), topOf(tabParent)));
+ arrows.appendChild(makeVHArrow(topOf(isApzTarget), leftOf(sendTouchEvent)));
+ arrows.appendChild(makeHVHArrow(rightOf(sendTouchEvent), rightOf(targetESM), 30));
+ arrows.appendChild(makeArrow(leftOf(tabParent), rightOf(geckoUntransform)));
+ arrows.appendChild(makeArrow(leftOf(geckoUntransform), rightOf(tabChild)));
+ arrows.appendChild(makeArrow(leftOf(tabChild), rightOf(isApzcEnabled)));
+ arrows.appendChild(makeArrow(leftOf(isApzcEnabled), rightOf(tabGesture)));
+ arrows.appendChild(makeArrow(bottomOf(isApzcEnabled), topOf(childHitTest)));
+ arrows.appendChild(makeVHArrow(bottomOf(tabGesture), leftOf(childHitTest)));
+ arrows.appendChild(makeArrow(rightOf(childHitTest), leftOf(childEsm)));
+ arrows.appendChild(makeArrow(rightOf(childEsm), leftOf(childContent)));
+ arrows.appendChild(makeVHVArrow(midpoint(leftOf(apzGesture), rightOf(apzTransform)), topOf(tabChild), 300));
+
+ document.documentElement.appendChild(makeLegend("Main process input thread", "parentinput"));
+ document.documentElement.appendChild(makeLegend("Main process main thread", "parentmain"));
+ document.documentElement.appendChild(makeLegend("Main process compositor thread", "parentcompositor"));
+ document.documentElement.appendChild(makeLegend("Child process main thread", "childmain"));
+ document.documentElement.appendChild(makeLegend("Undetermined process main thread", "bothmain"));
+ ]]></script>
+</svg>
diff --git a/system/graphics/doc/GraphicsOverview.md b/system/graphics/doc/GraphicsOverview.md
new file mode 100644
index 000000000..b834172c8
--- /dev/null
+++ b/system/graphics/doc/GraphicsOverview.md
@@ -0,0 +1,83 @@
+Mozilla Graphics Overview {#graphicsoverview}
+=================
+## Work in progress. Possibly incorrect or incomplete.
+
+Overview
+--------
+The graphics systems is responsible for rendering (painting, drawing) the frame tree (rendering tree) elements as created by the layout system. Each leaf in the tree has content, either bounded by a rectangle (or perhaps another shape, in the case of SVG.)
+
+The simple approach for producing the result would thus involve traversing the frame tree, in a correct order, drawing each frame into the resulting buffer and displaying (printing non-withstanding) that buffer when the traversal is done. It is worth spending some time on the "correct order" note above. If there are no overlapping frames, this is fairly simple - any order will do, as long as there is no background. If there is background, we just have to worry about drawing that first. Since we do not control the content, chances are the page is more complicated. There are overlapping frames, likely with transparency, so we need to make sure the elements are draw "back to front", in layers, so to speak. Layers are an important concept, and we will revisit them shortly, as they are central to fixing a major issue with the above simple approach.
+
+While the above simple approach will work, the performance will suffer. Each time anything changes in any of the frames, the complete process needs to be repeated, everything needs to be redrawn. Further, there is very little space to take advantage of the modern graphics (GPU) hardware, or multi-core computers. If you recall from the previous sections, the frame tree is only accessible from the UI thread, so while we're doing all this work, the UI is basically blocked.
+
+### (Retained) Layers
+
+Layers framework was introduced to address the above performance issues, by having a part of the design address each item. At the high level:
+
+1. We create a layer tree. The leaf elements of the tree contain all frames (possibly multiple frames per leaf).
+2. We render each layer tree element and cache (retain) the result.
+3. We composite (combine) all the leaf elements into the final result.
+
+Let's examine each of these steps, in reverse order.
+
+### Compositing
+We use the term composite as it implies that the order is important. If the elements being composited overlap, whether there is transparency involved or not, the order in which they are combined will effect the result.
+Compositing is where we can use some of the power of the modern graphics hardware. It is optimal for doing this job. In the scenarios where only the position of individual frames changes, without the content inside them changing, we see why caching each layer would be advantageous - we only need to repeat the final compositing step, completely skipping the layer tree creation and the rendering of each leaf, thus speeding up the process considerably.
+
+Another benefit is equally apparent in the context of the stated deficiencies of the simple approach. We can use the available graphics hardware accelerated APIs to do the compositing step. Direct3D, OpenGL can be used on different platforms and are well suited to accelerate this step.
+
+Finally, we can now envision performing the compositing step on a separate thread, unblocking the UI thread for other work, and doing more work in parallel. More on this below.
+
+It is important to note that the number of operations in this step is proportional to the number of layer tree (leaf) elements, so there is additional work and complexity involved, when the layer tree is large.
+
+#### Render and retain layer elements
+As we saw, the compositing step benefits from caching the intermediate result. This does result in the extra memory usage, so needs to be considered during the layer tree creation. Beyond the caching, we can accelerate the rendering of each element by (indirectly) using the available platform APIs (e.g., Direct2D, CoreGraphics, even some of the 3D APIs like OpenGL or Direct3D) as available. This is actually done through a platform independent API (see Moz2D) below, but is important to realize it does get accelerated appropriately.
+
+#### Creating the layer tree
+We need to create a layer tree (from the frames tree), which will give us the correct result while striking the right balance between a layer per frame element and a single layer for the complete frames tree. As was mentioned above, there is an overhead in traversing the whole tree and caching each of the elements, balanced by the performance improvements. Some of the performance improvements are only noticed when something changes (e.g., one element is moving, we only need to redo the compositing step).
+
+### Refresh Driver
+
+### Layers
+
+#### Rendering each layer
+
+### Tiling vs. Buffer Rotation vs. Full paint
+
+#### Compositing for the final result
+
+### Graphics API
+
+#### Moz2D
+* The Moz2D graphics API, part of the Azure project, is a cross-platform interface onto the various graphics backends that Gecko uses for rendering such as Direct2D (1.0 and 1.1), Skia, Cairo, Quartz, and NV Path. Adding a new graphics platform to Gecko is accomplished by adding a backend to Moz2D.
+\see [Moz2D documentation on wiki](https://wiki.mozilla.org/Platform/GFX/Moz2D)
+
+#### Compositing
+
+#### Image Decoding
+
+#### Image Animation
+
+### Funny words
+There are a lot of code words that we use to refer to projects, libraries, areas of the code. Here's an attempt to cover some of those:
+* Azure - See Moz2D in the Graphics API section above.
+* Backend - See Moz2D in the Graphics API section above.
+* Cairo - http://www.cairographics.org/. Cairo is a 2D graphics library with support for multiple output devices. Currently supported output targets include the X Window System (via both Xlib and XCB), Quartz, Win32, image buffers, PostScript, PDF, and SVG file output.
+* Moz2D - See Moz2D in the Graphics API section above.
+* Thebes - Graphics API that preceded Moz2D.
+* Reflow
+* Display list
+
+### [Historical Documents](http://www.youtube.com/watch?v=lLZQz26-kms)
+A number of posts and blogs that will give you more details or more background, or reasoning that led to different solutions and approaches.
+
+* 2010-01 [Layers: Cross Platform Acceleration] (http://www.basschouten.com/blog1.php/layers-cross-platform-acceleration)
+* 2010-04 [Layers] (http://robert.ocallahan.org/2010/04/layers_01.html)
+* 2010-07 [Retained Layers](http://robert.ocallahan.org/2010/07/retained-layers_16.html)
+* 2011-04 [Introduction](https://blog.mozilla.org/joe/2011/04/26/introducing-the-azure-project/ Moz2D)
+* 2011-07 [Layers](http://chrislord.net/index.php/2011/07/25/shadow-layers-and-learning-by-failing/ Shadow)
+* 2011-09 [Graphics API Design](http://robert.ocallahan.org/2011/09/graphics-api-design.html)
+* 2012-04 [Moz2D Canvas on OSX](http://muizelaar.blogspot.ca/2012/04/azure-canvas-on-os-x.html)
+* 2012-05 [Mask Layers](http://featherweightmusings.blogspot.co.uk/2012/05/mask-layers_26.html)
+* 2013-07 [Graphics related](http://www.basschouten.com/blog1.php)
+
diff --git a/system/graphics/doc/LayersHistory.md b/system/graphics/doc/LayersHistory.md
new file mode 100644
index 000000000..2833aa3c5
--- /dev/null
+++ b/system/graphics/doc/LayersHistory.md
@@ -0,0 +1,60 @@
+This is an overview of the major events in the history of our Layers infrastructure.
+
+- iPhone released in July 2007 (Built on a toolkit called LayerKit)
+
+- Core Animation (October 2007) LayerKit was publicly renamed to OS X 10.5
+
+- Webkit CSS 3d transforms (July 2009)
+
+- Original layers API (March 2010) Introduced the idea of a layer manager that
+ would composite. One of the first use cases for this was hardware accelerated
+ YUV conversion for video.
+
+- Retained layers (July 7 2010 - Bug 564991)
+This was an important concept that introduced the idea of persisting the layer
+content across paints in gecko controlled buffers instead of just by the OS. This introduced
+the concept of buffer rotation to deal with scrolling instead of using the
+native scrolling APIs like ScrollWindowEx
+
+- Layers IPC (July 2010 - Bug 570294)
+This introduced shadow layers and edit lists and was originally done for e10s v1
+
+- 3d transforms (September 2011 - Bug 505115)
+
+- OMTC (December 2011 - Bug 711168)
+This was prototyped on OS X but shipped first for Fennec
+
+- Tiling v1 (April 2012 - Bug 739679)
+Originally done for Fennec.
+This was done to avoid situations where we had to do a bunch of work for
+scrolling a small amount. i.e. buffer rotation. It allowed us to have a
+variety of interesting features like progressive painting and lower resolution
+painting.
+
+- C++ Async pan zoom controller (July 2012 - Bug 750974)
+The existing APZ code was in Java for Fennec so this was reimplemented.
+
+- Streaming WebGL Buffers (February 2013 - Bug 716859)
+Infrastructure to allow OMTC WebGL and avoid the need to glFinish() every
+frame.
+
+- Compositor API (April 2013 - Bug 825928)
+The planning for this started around November 2012.
+Layers refactoring created a compositor API that abstracted away the differences between the
+D3D vs OpenGL. The main piece of API is DrawQuad.
+
+- Tiling v2 (Mar 7 2014 - Bug 963073)
+Tiling for B2G. This work is mainly porting tiled layers to new textures,
+implementing double-buffered tiles and implementing a texture client pool, to
+be used by tiled content clients.
+
+ A large motivation for the pool was the very slow performance of allocating tiles because
+of the sync messages to the compositor.
+
+ The slow performance of allocating was directly addressed by bug 959089 which allowed us
+to allocate gralloc buffers without sync messages to the compositor thread.
+
+- B2G WebGL performance (May 2014 - Bug 1006957, 1001417, 1024144)
+This work improved the synchronization mechanism between the compositor
+and the producer.
+
diff --git a/system/graphics/doc/MainPage.md b/system/graphics/doc/MainPage.md
new file mode 100644
index 000000000..70a9fc60a
--- /dev/null
+++ b/system/graphics/doc/MainPage.md
@@ -0,0 +1,21 @@
+Mozilla Graphics {#mainpage}
+======================
+
+## Work in progress. Possibly incorrect or incomplete.
+
+
+Introduction
+-------
+This collection of linked pages contains a combination of Doxygen
+extracted source code documentation and design documents for the
+Mozilla graphics architecture. The design documents live in gfx/docs directory.
+
+This [wiki page](https://wiki.mozilla.org/Platform/GFX) contains
+information about graphics and the graphics team at MoCo.
+
+Continue here for a [very high level introductory overview](@ref graphicsoverview)
+if you don't know where to start.
+
+Useful pointers for creating documentation
+------
+[The mechanics of creating these files](https://wiki.mozilla.org/Platform/GFX/DesignDocumentationGuidelines)
diff --git a/system/graphics/doc/MozSurface.md b/system/graphics/doc/MozSurface.md
new file mode 100644
index 000000000..ae8c45f42
--- /dev/null
+++ b/system/graphics/doc/MozSurface.md
@@ -0,0 +1,124 @@
+MozSurface {#mozsurface}
+==========
+
+**This document is work in progress. Some information may be missing or incomplete.**
+
+## Goals
+
+We need to be able to safely and efficiently render web content into surfaces that may be shared accross processes.
+MozSurface is a cross-process and backend-independent Surface API and not a stream API.
+
+## Owner
+
+Nicolas Silva
+
+## Definitions
+
+## Use cases
+
+Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
+
+## Requirement
+
+* It must be possible to efficiently share a MozSurface with a separate thread or process through IPDL
+* It must be possible to obtain read access a MozSurface on both the client and the host side at the same time.
+* The creation, update and destrution of surfaces must be safe and race-free. In particular, the ownership of the shared data must be clearly defined.
+* MozSurface must be a cross-backend/cross-platform abstraction that we will use on all of the supported platforms.
+* It must be possible to efficiently draw into a MozSurface using Moz2D.
+* While it should be possible to share MozSurfaces accross processes, it should not be limited to that. MozSurface should also be the preferred abstraction for use with surfaces that are not shared with the compositor process.
+
+## TextureClient and TextureHost
+
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface. The current plan is to evolve TextureClient into MozSurface. In its current state, TextureClient doesn't meet all the requirements and desisgn decisions of MozSurface yet.
+
+In particular, TextureClient/TextureHost are designed around cross-process sharing specifically. See the SharedMozSurface design document for more information about TextureClient and TextureHost.
+
+## Locking semantics
+
+In order to access the shared surface data users of MozSurface must acquire and release a lock on the surface, specifying the open mode (read/write/read+write).
+
+ bool Lock(OpenMode aMode);
+ void Unlock();
+
+This locking API has two purposes:
+
+* Ensure that access to the shared data is race-free.
+* Let the implemetation do whatever is necessary for the user to have access to the data. For example it can be mapping and unmapping the surface data in memory if the underlying backend requires it.
+
+The lock is expected to behave as a cross-process blocking read/write lock that is not reentrant.
+
+## Immutable surfaces
+
+In some cases we know in advance that a surface will not be modified after it has been shared. This is for example true for video frames. In this case the surface can be marked as immutable and the underlying implementation doesn't need to hold an actual blocking lock on the shared data.
+Trying to acquire a write lock on a MozSurface that is marked as immutable and already shared must fail (return false).
+Note that it is still required to use the Lock/Unlock API to read the data, in order for the implementation to be able to properly map and unmap the memory. This is just an optimization and a safety check.
+
+## Drawing into a surface
+
+In most cases we want to be able to paint directly into a surface through the Moz2D API.
+
+A surface lets you *borrow* a DrawTarget that is only valid between Lock and Unlock.
+
+ DrawTarget* GetAsDrawTarget();
+
+It is invalid to hold a reference to the DrawTarget after Unlock, and a different DrawTarget may be obtained during the next Lock/Unlock interval.
+
+In some cases we want to use MozSurface without drawing into it. For instance to share video frames accross processes. Some surface types may also not be accessible through a DrawTarget (for example YCbCr surfaces).
+
+ bool CanExposeDrawTarget();
+
+helps with making sure that a Surface supports exposing a Moz2D DrawTarget.
+
+## Using a MozSurface as a source for Compositing
+
+To interface with the Compositor API, MozSurface gives access to TextureSource objects. TextureSource is the cross-backend representation of a texture that Compositor understands.
+While MozSurface handles memory management of (potentially shared) texture data, TextureSource is only an abstraction for Compositing.
+
+## Fence synchronization
+
+TODO: We need to figure this out. Right now we have a Gonk specific implementation, but no cross-platform abstraction/design.
+
+## Ownership of the shared data
+
+MozSurface (TextureClient/TextureHost in its current form) defines ownership rules that depend on the configuration of the surface, in order to satisy efficiency and safety requirements.
+
+These rules rely on the fact that the underlying shared data is strictly owned by the MozSurface. This means that keeping direct references to the shared data is illegal and unsafe.
+
+## Internal buffers / direct texturing
+
+Some MozSurface implementations use CPU-side shared memory to share the texture data accross processes, and require a GPU texture upload when interfacing with a TextureSource. In this case we say that the surface has an internal buffer (because it is implicitly equivalent to double buffering where the shared data is the back buffer and the GPU side texture is the front buffer). We also say that it doesn't do "direct texturing" meaning that we don't draw directly into the GPU-side texture.
+
+Examples:
+
+ * Shmem MozSurface + OpenGL TextureSource: Has an internal buffer (no direct texturing)
+ * Gralloc MozSurface + Gralloc TextureSource: No internal buffer (direct texturing)
+
+While direct texturing is usually the most efficient way, it is not always available depending on the platform and the required allocation size or format. Textures with internal buffers have less restrictions around locking since the host side will only need to read from the MozSurface once per update, meaning that we can often get away with single buffering where we would need double buffering with direct texturing.
+
+## Alternative solutions
+
+## Backends
+
+We have MozSurface implementaions (classes inheriting from TextureClient/TextureHost) for OpenGL, Software, D3D9, and D3D11 backends.
+Some implemtations can be used with any backend (ex. ShmemTextureClient/Host).
+
+## Users of MozSurface
+
+MozSurface is the mechanism used by layers to share surfaces with the compositor, but it is not limited to layers. It should be used by anything that draws into a surface that may be shared with the compositor thread.
+
+## Testing
+
+TODO - How can we make MozSurface more testable and what should we test?
+
+## Future work
+
+### Using a MozSurface as a source for Drawing
+
+MozSurface should be able to expose a borrowed Moz2D SourceSurface that is valid between Lock and Unlock similarly to how it exposes a DrawTarget.
+
+## Comparison with other APIs
+
+MozSurface is somewhat equivalent to Gralloc on Android/Gonk: it is a reference counted cross-process surface with locking semantics. While Gralloc can interface itself with OpenGL textures for compositing, MozSurface can interface itself to TextureSource objects.
+
+MozSurface should not be confused with higher level APIs such as EGLStream. A swap-chain API like EGLStream can be implemented on top of MozSurface, but MozSurface's purpose is to define and manage the memory and resources of shared texture data.
+
diff --git a/system/graphics/doc/SharedMozSurface.md b/system/graphics/doc/SharedMozSurface.md
new file mode 100644
index 000000000..3ff3e53dd
--- /dev/null
+++ b/system/graphics/doc/SharedMozSurface.md
@@ -0,0 +1,147 @@
+Shared MozSurface {#mozsurface}
+==========
+
+**This document is work in progress. Some information may be missing or incomplete.**
+
+Shared MozSurfaces represent an important use case of MozSurface, anything that is in the MozSurface design document also applies to shared MozSurfaces.
+
+## Goals
+
+We need to be able to safely and efficiently render web content into surfaces that may be shared accross processes.
+MozSurface is a cross-process and backend-independent Surface API and not a stream API.
+
+## Owner
+
+Nicolas Silva
+
+## Definitions
+
+* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
+
+## Use cases
+
+Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
+
+## Requirement
+
+Shared MozSurfaces represent an important use case of MozSurface, it has the same requirements as MozSurface.
+
+## TextureClient and TextureHost
+
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
+Inline documentation about TextureClient and TextureHost can be found in:
+
+* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
+* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
+
+TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
+
+A MozSurface containing data that is shared between a client process and a host process exists in the following form:
+
+```
+ .
+ Client process . Host process
+ .
+ ________________ ______________ ______________
+ | | | | | |
+ | TextureClient +----+ <SharedData> +----+ TextureHost |
+ |________________| |______________| |______________|
+ .
+ .
+ .
+ Figure 1) A Surface as seen by the client and the host processes
+```
+
+The above figure is a logical representation, not a class diagram.
+`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
+
+## Deallocation protocol
+
+The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
+The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
+When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
+
+
+```
+ client side . host side
+ .
+ (A) Client: Send Remove -. .
+ \ .
+ \ . ... can receive and send ...
+ \
+ Can receive `--> (B) Host: Receive Remove
+ Can't send |
+ .-- (C) Host: Send Delete
+ /
+ / . ... can't receive nor send ...
+ / .
+ (D) Client: Receive Delete <--' .
+ .
+ Figure 2) MozSurface deallocation handshake
+```
+
+This handshake protocol is twofold:
+
+* It defines where and when it is possible to deallocate the shared data without races
+* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
+
+### Deallocating on the host side
+
+In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
+
+### Deallocating on the client side
+
+In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
+
+In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
+Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
+
+### Sharing state
+
+The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
+
+The sharing state of a MozSurface can be one of the following:
+
+* (1) Uninitialized (it doesn't have any shared data)
+* (2) Local (it isn't shared with the another thread/process)
+* (3) Shared (the state you would expect it to be most of the time)
+* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
+
+Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
+
+The deallocation protocol above, applies to the Shared state (3).
+In the other cases:
+
+* (1) Unitilialized: There is nothing to do.
+* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
+* (4) Invalid: There is nothing to do (deallocation has already happenned).
+
+## Alternative solutions
+
+### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
+
+The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
+
+Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
+
+We need a given surface to be accessible on both the client and host for the following reasons:
+
+* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
+* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
+* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
+
+## Testing
+
+TODO - How can we make shared MozSurfaces more testable and what should we test?
+
+## Future work
+
+### Rename TextureClient/TextureHost
+
+The current terminology is very confusing.
+
+### Unify TextureClient and TextureHost
+
+TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
+
+## Comparison with other APIs
diff --git a/system/graphics/doc/Silk.md b/system/graphics/doc/Silk.md
new file mode 100644
index 000000000..c71e79e43
--- /dev/null
+++ b/system/graphics/doc/Silk.md
@@ -0,0 +1,246 @@
+Silk Architecture Overview
+=================
+
+#Architecture
+Our current architecture is to align three components to hardware vsync timers:
+
+1. Compositor
+2. RefreshDriver / Painting
+3. Input Events
+
+The flow of our rendering engine is as follows:
+
+1. Hardware Vsync event occurs on an OS specific *Hardware Vsync Thread* on a per monitor basis.
+2. The *Hardware Vsync Thread* attached to the monitor notifies the **CompositorVsyncDispatchers** and **RefreshTimerVsyncDispatcher**.
+3. For every Firefox window on the specific monitor, notify a **CompositorVsyncDispatcher**. The **CompositorVsyncDispatcher** is specific to one window.
+4. The **CompositorVsyncDispatcher** notifies a **CompositorWidgetVsyncObserver** when remote compositing, or a **CompositorVsyncScheduler::Observer** when compositing in-process.
+5. If remote compositing, a vsync notification is sent from the **CompositorWidgetVsyncObserver** to the **VsyncBridgeChild** on the UI process, which sends an IPDL message to the **VsyncBridgeParent** on the compositor thread of the GPU process, which then dispatches to **CompositorVsyncScheduler::Observer**.
+6. The **RefreshTimerVsyncDispatcher** notifies the Chrome **RefreshTimer** that a vsync has occured.
+7. The **RefreshTimerVsyncDispatcher** sends IPC messages to all content processes to tick their respective active **RefreshTimer**.
+8. The **Compositor** dispatches input events on the *Compositor Thread*, then composites. Input events are only dispatched on the *Compositor Thread* on b2g.
+9. The **RefreshDriver** paints on the *Main Thread*.
+
+The implementation is broken into the following sections and will reference this figure. Note that **Objects** are bold fonts while *Threads* are italicized.
+
+<img src="silkArchitecture.png" width="900px" height="630px" />
+
+#Hardware Vsync
+Hardware vsync events from (1), occur on a specific **Display** Object.
+The **Display** object is responsible for enabling / disabling vsync on a per connected display basis.
+For example, if two monitors are connected, two **Display** objects will be created, each listening to vsync events for their respective displays.
+We require one **Display** object per monitor as each monitor may have different vsync rates.
+As a fallback solution, we have one global **Display** object that can synchronize across all connected displays.
+The global **Display** is useful if a window is positioned halfway between the two monitors.
+Each platform will have to implement a specific **Display** object to hook and listen to vsync events.
+As of this writing, both Firefox OS and OS X create their own hardware specific *Hardware Vsync Thread* that executes after a vsync has occured.
+OS X creates one *Hardware Vsync Thread* per **CVDisplayLinkRef**.
+We do not currently support multiple displays, so we use one global **CVDisplayLinkRef** that works across all active displays.
+On Windows, we have to create a new platform *thread* that waits for DwmFlush(), which works across all active displays.
+Once the thread wakes up from DwmFlush(), the actual vsync timestamp is retrieved from DwmGetCompositionTimingInfo(), which is the timestamp that is actually passed into the compositor and refresh driver.
+
+When a vsync occurs on a **Display**, the *Hardware Vsync Thread* callback fetches all **CompositorVsyncDispatchers** associated with the **Display**.
+Each **CompositorVsyncDispatcher** is notified that a vsync has occured with the vsync's timestamp.
+It is the responsibility of the **CompositorVsyncDispatcher** to notify the **Compositor** that is awaiting vsync notifications.
+The **Display** will then notify the associated **RefreshTimerVsyncDispatcher**, which should notify all active **RefreshDrivers** to tick.
+
+All **Display** objects are encapsulated in a **VsyncSource** object.
+The **VsyncSource** object lives in **gfxPlatform** and is instantiated only on the parent process when **gfxPlatform** is created.
+The **VsyncSource** is destroyed when **gfxPlatform** is destroyed.
+There is only one **VsyncSource** object throughout the entire lifetime of Firefox.
+Each platform is expected to implement their own **VsyncSource** to manage vsync events.
+On Firefox OS, this is through the **HwcComposer2D**.
+On OS X, this is through **CVDisplayLinkRef**.
+On Windows, it should be through **DwmGetCompositionTimingInfo**.
+
+#Compositor
+When the **CompositorVsyncDispatcher** is notified of the vsync event, the **CompositorVsyncScheduler::Observer** associated with the **CompositorVsyncDispatcher** begins execution.
+Since the **CompositorVsyncDispatcher** executes on the *Hardware Vsync Thread* and the **Compositor** composites on the *CompositorThread*, the **CompositorVsyncScheduler::Observer** posts a task to the *CompositorThread*.
+The **CompositorBridgeParent** then composites.
+The model where the **CompositorVsyncDispatcher** notifies components on the *Hardware Vsync Thread*, and the component schedules the task on the appropriate thread is used everywhere.
+
+The **CompositorVsyncScheduler::Observer** listens to vsync events as needed and stops listening to vsync when composites are no longer scheduled or required.
+Every **CompositorBridgeParent** is associated and tied to one **CompositorVsyncScheduler::Observer**, which is associated with the **CompositorVsyncDispatcher**.
+Each **CompositorBridgeParent** is associated with one widget and is created when a new platform window or **nsBaseWidget** is created.
+The **CompositorBridgeParent**, **CompositorVsyncDispatcher**, **CompositorVsyncScheduler::Observer**, and **nsBaseWidget** all have the same lifetimes, which are created and destroyed together.
+
+##Out-of-process Compositors
+When compositing out-of-process, this model changes slightly.
+In this case there are effectively two observers: a UI process observer (**CompositorWidgetVsyncObserver**), and the **CompositorVsyncScheduler::Observer** in the GPU process.
+There are also two dispatchers: the widget dispatcher in the UI process (**CompositorVsyncDispatcher**), and the IPDL-based dispatcher in the GPU process (**CompositorBridgeParent::NotifyVsync**).
+The UI process observer and the GPU process dispatcher are linked via an IPDL protocol called PVsyncBridge.
+**PVsyncBridge** is a top-level protocol for sending vsync notifications to the compositor thread in the GPU process.
+The compositor controls vsync observation through a separate actor, **PCompositorWidget**, which (as a subactor for **CompositorBridgeChild**) links the compositor thread in the GPU process to the main thread in the UI process.
+
+Out-of-process compositors do not go through **CompositorVsyncDispatcher** directly.
+Instead, the **CompositorWidgetDelegate** in the UI process creates one, and gives it a **CompositorWidgetVsyncObserver**.
+This observer forwards notifications to a Vsync I/O thread, where **VsyncBridgeChild** then forwards the notification again to the compositor thread in the GPU process.
+The notification is received by a **VsyncBridgeParent**.
+The GPU process uses the layers ID in the notification to find the correct compositor to dispatch the notification to.
+
+###CompositorVsyncDispatcher
+The **CompositorVsyncDispatcher** executes on the *Hardware Vsync Thread*.
+It contains references to the **nsBaseWidget** it is associated with and has a lifetime equal to the **nsBaseWidget**.
+The **CompositorVsyncDispatcher** is responsible for notifying the **CompositorBridgeParent** that a vsync event has occured.
+There can be multiple **CompositorVsyncDispatchers** per **Display**, one **CompositorVsyncDispatcher** per window.
+The only responsibility of the **CompositorVsyncDispatcher** is to notify components when a vsync event has occured, and to stop listening to vsync when no components require vsync events.
+We require one **CompositorVsyncDispatcher** per window so that we can handle multiple **Displays**.
+When compositing in-process, the **CompositorVsyncDispatcher** is attached to the CompositorWidget for the
+window. When out-of-process, it is attached to the CompositorWidgetDelegate, which forwards
+observer notifications over IPDL. In the latter case, its lifetime is tied to a CompositorSession
+rather than the nsIWidget.
+
+###Multiple Displays
+The **VsyncSource** has an API to switch a **CompositorVsyncDispatcher** from one **Display** to another **Display**.
+For example, when one window either goes into full screen mode or moves from one connected monitor to another.
+When one window moves to another monitor, we expect a platform specific notification to occur.
+The detection of when a window enters full screen mode or moves is not covered by Silk itself, but the framework is built to support this use case.
+The expected flow is that the OS notification occurs on **nsIWidget**, which retrieves the associated **CompositorVsyncDispatcher**.
+The **CompositorVsyncDispatcher** then notifies the **VsyncSource** to switch to the correct **Display** the **CompositorVsyncDispatcher** is connected to.
+Because the notification works through the **nsIWidget**, the actual switching of the **CompositorVsyncDispatcher** to the correct **Display** should occur on the *Main Thread*.
+The current implementation of Silk does not handle this case and needs to be built out.
+
+###CompositorVsyncScheduler::Observer
+The **CompositorVsyncScheduler::Observer** handles the vsync notifications and interactions with the **CompositorVsyncDispatcher**.
+When the **Compositor** requires a scheduled composite, it notifies the **CompositorVsyncScheduler::Observer** that it needs to listen to vsync.
+The **CompositorVsyncScheduler::Observer** then observes / unobserves vsync as needed from the **CompositorVsyncDispatcher** to enable composites.
+
+###GeckoTouchDispatcher
+The **GeckoTouchDispatcher** is a singleton that resamples touch events to smooth out jank while tracking a user's finger.
+Because input and composite are linked together, the **CompositorVsyncScheduler::Observer** has a reference to the **GeckoTouchDispatcher** and vice versa.
+
+###Input Events
+One large goal of Silk is to align touch events with vsync events.
+On Firefox OS, touchscreens often have different touch scan rates than the display refreshes.
+A Flame device has a touch refresh rate of 75 HZ, while a Nexus 4 has a touch refresh rate of 100 HZ, while the device's display refresh rate is 60HZ.
+When a vsync event occurs, we resample touch events, and then dispatch the resampled touch event to APZ.
+Touch events on Firefox OS occur on a *Touch Input Thread* whereas they are processed by APZ on the *APZ Controller Thread*.
+We use [Google Android's touch resampling](http://www.masonchang.com/blog/2014/8/25/androids-touch-resampling-algorithm) algorithm to resample touch events.
+
+Currently, we have a strict ordering between Composites and touch events.
+When a touch event occurs on the *Touch Input Thread*, we store the touch event in a queue.
+When a vsync event occurs, the **CompositorVsyncDispatcher** notifies the **Compositor** of a vsync event, which notifies the **GeckoTouchDispatcher**.
+The **GeckoTouchDispatcher** processes the touch event first on the *APZ Controller Thread*, which is the same as the *Compositor Thread* on b2g, then the **Compositor** finishes compositing.
+We require this strict ordering because if a vsync notification is dispatched to both the **Compositor** and **GeckoTouchDispatcher** at the same time, a race condition occurs between processing the touch event and therefore position versus compositing.
+In practice, this creates very janky scrolling.
+As of this writing, we have not analyzed input events on desktop platforms.
+
+One slight quirk is that input events can start a composite, for example during a scroll and after the **Compositor** is no longer listening to vsync events.
+In these cases, we notify the **Compositor** to observe vsync so that it dispatches touch events.
+If touch events were not dispatched, and since the **Compositor** is not listening to vsync events, the touch events would never be dispatched.
+The **GeckoTouchDispatcher** handles this case by always forcing the **Compositor** to listen to vsync events while touch events are occurring.
+
+###Widget, Compositor, CompositorVsyncDispatcher, GeckoTouchDispatcher Shutdown Procedure
+When the [nsBaseWidget shuts down](http://hg.mozilla.org/mozilla-central/file/0df249a0e4d3/widget/nsBaseWidget.cpp#l182) - It calls nsBaseWidget::DestroyCompositor on the *Gecko Main Thread*.
+During nsBaseWidget::DestroyCompositor, it first destroys the CompositorBridgeChild.
+CompositorBridgeChild sends a sync IPC call to CompositorBridgeParent::RecvStop, which calls [CompositorBridgeParent::Destroy](http://hg.mozilla.org/mozilla-central/file/ab0490972e1e/gfx/layers/ipc/CompositorBridgeParent.cpp#l509).
+During this time, the *main thread* is blocked on the parent process.
+CompositorBridgeParent::RecvStop runs on the *Compositor thread* and cleans up some resources, including setting the **CompositorVsyncScheduler::Observer** to nullptr.
+CompositorBridgeParent::RecvStop also explicitly keeps the CompositorBridgeParent alive and posts another task to run CompositorBridgeParent::DeferredDestroy on the Compositor loop so that all ipdl code can finish executing.
+The **CompositorVsyncScheduler::Observer** also unobserves from vsync and cancels any pending composite tasks.
+Once CompositorBridgeParent::RecvStop finishes, the *main thread* in the parent process continues shutting down the nsBaseWidget.
+
+At the same time, the *Compositor thread* is executing tasks until CompositorBridgeParent::DeferredDestroy runs, which flushes the compositor message loop.
+Now we have two tasks as both the nsBaseWidget releases a reference to the Compositor on the *main thread* during destruction and the CompositorBridgeParent::DeferredDestroy releases a reference to the CompositorBridgeParent on the *Compositor Thread*.
+Finally, the CompositorBridgeParent itself is destroyed on the *main thread* once both references are gone due to explicit [main thread destruction](http://hg.mozilla.org/mozilla-central/file/50b95032152c/gfx/layers/ipc/CompositorBridgeParent.h#l148).
+
+With the **CompositorVsyncScheduler::Observer**, any accesses to the widget after nsBaseWidget::DestroyCompositor executes are invalid.
+Any accesses to the compositor between the time the nsBaseWidget::DestroyCompositor runs and the CompositorVsyncScheduler::Observer's destructor runs aren't safe yet a hardware vsync event could occur between these times.
+Since any tasks posted on the Compositor loop after CompositorBridgeParent::DeferredDestroy is posted are invalid, we make sure that no vsync tasks can be posted once CompositorBridgeParent::RecvStop executes and DeferredDestroy is posted on the Compositor thread.
+When the sync call to CompositorBridgeParent::RecvStop executes, we explicitly set the CompositorVsyncScheduler::Observer to null to prevent vsync notifications from occurring.
+If vsync notifications were allowed to occur, since the **CompositorVsyncScheduler::Observer**'s vsync notification executes on the *hardware vsync thread*, it would post a task to the Compositor loop and may execute after CompositorBridgeParent::DeferredDestroy.
+Thus, we explicitly shut down vsync events in the **CompositorVsyncDispatcher** and **CompositorVsyncScheduler::Observer** during nsBaseWidget::Shutdown to prevent any vsync tasks from executing after CompositorBridgeParent::DeferredDestroy.
+
+The **CompositorVsyncDispatcher** may be destroyed on either the *main thread* or *Compositor Thread*, since both the nsBaseWidget and **CompositorVsyncScheduler::Observer** race to destroy on different threads.
+nsBaseWidget is destroyed on the *main thread* and releases a reference to the **CompositorVsyncDispatcher** during destruction.
+The **CompositorVsyncScheduler::Observer** has a race to be destroyed either during CompositorBridgeParent shutdown or from the **GeckoTouchDispatcher** which is destroyed on the main thread with [ClearOnShutdown](http://hg.mozilla.org/mozilla-central/file/21567e9a6e40/xpcom/base/ClearOnShutdown.h#l15).
+Whichever object, the CompositorBridgeParent or the **GeckoTouchDispatcher** is destroyed last will hold the last reference to the **CompositorVsyncDispatcher**, which destroys the object.
+
+#Refresh Driver
+The Refresh Driver is ticked from a [single active timer](http://hg.mozilla.org/mozilla-central/file/ab0490972e1e/layout/base/nsRefreshDriver.cpp#l11).
+The assumption is that there are multiple **RefreshDrivers** connected to a single **RefreshTimer**.
+There are two **RefreshTimers**: an active and an inactive **RefreshTimer**.
+Each Tab has its own **RefreshDriver**, which connects to one of the global **RefreshTimers**.
+The **RefreshTimers** execute on the *Main Thread* and tick their connected **RefreshDrivers**.
+We do not want to break this model of multiple **RefreshDrivers** per a set of two global **RefreshTimers**.
+Each **RefreshDriver** switches between the active and inactive **RefreshTimer**.
+
+Instead, we create a new **RefreshTimer**, the **VsyncRefreshTimer** which ticks based on vsync messages.
+We replace the current active timer with a **VsyncRefreshTimer**.
+All tabs will then tick based on this new active timer.
+Since the **RefreshTimer** has a lifetime of the process, we only need to create a single **RefreshTimerVsyncDispatcher** per **Display** when Firefox starts.
+Even if we do not have any content processes, the Chrome process will still need a **VsyncRefreshTimer**, thus we can associate the **RefreshTimerVsyncDispatcher** with each **Display**.
+
+When Firefox starts, we initially create a new **VsyncRefreshTimer** in the Chrome process.
+The **VsyncRefreshTimer** will listen to vsync notifications from **RefreshTimerVsyncDispatcher** on the global **Display**.
+When nsRefreshDriver::Shutdown executes, it will delete the **VsyncRefreshTimer**.
+This creates a problem as all the **RefreshTimers** are currently manually memory managed whereas **VsyncObservers** are ref counted.
+To work around this problem, we create a new **RefreshDriverVsyncObserver** as an inner class to **VsyncRefreshTimer**, which actually receives vsync notifications. It then ticks the **RefreshDrivers** inside **VsyncRefreshTimer**.
+
+With Content processes, the start up process is more complicated.
+We send vsync IPC messages via the use of the PBackground thread on the parent process, which allows us to send messages from the Parent process' without waiting on the *main thread*.
+This sends messages from the Parent::*PBackground Thread* to the Child::*Main Thread*.
+The *main thread* receiving IPC messages on the content process is acceptable because **RefreshDrivers** must execute on the *main thread*.
+However, there is some amount of time required to setup the IPC connection upon process creation and during this time, the **RefreshDrivers** must tick to set up the process.
+To get around this, we initially use software **RefreshTimers** that already exist during content process startup and swap in the **VsyncRefreshTimer** once the IPC connection is created.
+
+During nsRefreshDriver::ChooseTimer, we create an async PBackground IPC open request to create a **VsyncParent** and **VsyncChild**.
+At the same time, we create a software **RefreshTimer** and tick the **RefreshDrivers** as normal.
+Once the PBackground callback is executed and an IPC connection exists, we swap all **RefreshDrivers** currently associated with the active **RefreshTimer** and swap the **RefreshDrivers** to use the **VsyncRefreshTimer**.
+Since all interactions on the content process occur on the main thread, there are no need for locks.
+The **VsyncParent** listens to vsync events through the **VsyncRefreshTimerDispatcher** on the parent side and sends vsync IPC messages to the **VsyncChild**.
+The **VsyncChild** notifies the **VsyncRefreshTimer** on the content process.
+
+During the shutdown process of the content process, ActorDestroy is called on the **VsyncChild** and **VsyncParent** due to the normal PBackground shutdown process.
+Once ActorDestroy is called, no IPC messages should be sent across the channel.
+After ActorDestroy is called, the IPDL machinery will delete the **VsyncParent/Child** pair.
+The **VsyncParent**, due to being a **VsyncObserver**, is ref counted.
+After **VsyncParent::ActorDestroy** is called, it unregisters itself from the **RefreshTimerVsyncDispatcher**, which holds the last reference to the **VsyncParent**, and the object will be deleted.
+
+Thus the overall flow during normal execution is:
+
+1. VsyncSource::Display::RefreshTimerVsyncDispatcher receives a Vsync notification from the OS in the parent process.
+2. RefreshTimerVsyncDispatcher notifies VsyncRefreshTimer::RefreshDriverVsyncObserver that a vsync occured on the parent process on the hardware vsync thread.
+3. RefreshTimerVsyncDispatcher notifies the VsyncParent on the hardware vsync thread that a vsync occured.
+4. The VsyncRefreshTimer::RefreshDriverVsyncObserver in the parent process posts a task to the main thread that ticks the refresh drivers.
+5. VsyncParent posts a task to the PBackground thread to send a vsync IPC message to VsyncChild.
+6. VsyncChild receive a vsync notification on the content process on the main thread and ticks their respective RefreshDrivers.
+
+###Compressing Vsync Messages
+Vsync messages occur quite often and the *main thread* can be busy for long periods of time due to JavaScript.
+Consistently sending vsync messages to the refresh driver timer can flood the *main thread* with refresh driver ticks, causing even more delays.
+To avoid this problem, we compress vsync messages on both the parent and child processes.
+
+On the parent process, newer vsync messages update a vsync timestamp but do not actually queue any tasks on the *main thread*.
+Once the parent process' *main thread* executes the refresh driver tick, it uses the most updated vsync timestamp to tick the refresh driver.
+After the refresh driver has ticked, one single vsync message is queued for another refresh driver tick task.
+On the content process, the IPDL **compress** keyword automatically compresses IPC messages.
+
+### Multiple Monitors
+In order to have multiple monitor support for the **RefreshDrivers**, we have multiple active **RefreshTimers**.
+Each **RefreshTimer** is associated with a specific **Display** via an id and tick when it's respective **Display** vsync occurs.
+We have **N RefreshTimers**, where N is the number of connected displays.
+Each **RefreshTimer** still has multiple **RefreshDrivers**.
+
+When a tab or window changes monitors, the **nsIWidget** receives a display changed notification.
+Based on which display the window is on, the window switches to the correct **RefreshTimerVsyncDispatcher** and **CompositorVsyncDispatcher** on the parent process based on the display id.
+Each **TabParent** should also send a notification to their child.
+Each **TabChild**, given the display ID, switches to the correct **RefreshTimer** associated with the display ID.
+When each display vsync occurs, it sends one IPC message to notify vsync.
+The vsync message contains a display ID, to tick the appropriate **RefreshTimer** on the content process.
+There is still only one **VsyncParent/VsyncChild** pair, just each vsync notification will include a display ID, which maps to the correct **RefreshTimer**.
+
+#Object Lifetime
+1. CompositorVsyncDispatcher - Lives as long as the nsBaseWidget associated with the VsyncDispatcher
+2. CompositorVsyncScheduler::Observer - Lives and dies the same time as the CompositorBridgeParent.
+3. RefreshTimerVsyncDispatcher - As long as the associated display object, which is the lifetime of Firefox.
+4. VsyncSource - Lives as long as the gfxPlatform on the chrome process, which is the lifetime of Firefox.
+5. VsyncParent/VsyncChild - Lives as long as the content process
+6. RefreshTimer - Lives as long as the process
+
+#Threads
+All **VsyncObservers** are notified on the *Hardware Vsync Thread*. It is the responsibility of the **VsyncObservers** to post tasks to their respective correct thread. For example, the **CompositorVsyncScheduler::Observer** will be notified on the *Hardware Vsync Thread*, and post a task to the *Compositor Thread* to do the actual composition.
+
+1. Compositor Thread - Nothing changes
+2. Main Thread - PVsyncChild receives IPC messages on the main thread. We also enable/disable vsync on the main thread.
+3. PBackground Thread - Creates a connection from the PBackground thread on the parent process to the main thread in the content process.
+4. Hardware Vsync Thread - Every platform is different, but we always have the concept of a hardware vsync thread. Sometimes this is actually created by the host OS. On Windows, we have to create a separate platform thread that blocks on DwmFlush().
diff --git a/system/graphics/doc/silkArchitecture.png b/system/graphics/doc/silkArchitecture.png
new file mode 100644
index 000000000..938c585e4
--- /dev/null
+++ b/system/graphics/doc/silkArchitecture.png
Binary files differ
diff --git a/system/graphics/docs/index.rst b/system/graphics/docs/index.rst
new file mode 100644
index 000000000..c6621c81a
--- /dev/null
+++ b/system/graphics/docs/index.rst
@@ -0,0 +1,9 @@
+========
+Graphics
+========
+
+The graphics team's documentation is currently using doxygen. We're tracking the work to integrate it better at https://bugzilla.mozilla.org/show_bug.cgi?id=1150232.
+
+For now you can read the graphics source code documentation here:
+
+http://people.mozilla.org/~bgirard/doxygen/gfx/
diff --git a/system/graphics/gl/DecomposeIntoNoRepeatTriangles.cpp b/system/graphics/gl/DecomposeIntoNoRepeatTriangles.cpp
new file mode 100644
index 000000000..1e6553604
--- /dev/null
+++ b/system/graphics/gl/DecomposeIntoNoRepeatTriangles.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DecomposeIntoNoRepeatTriangles.h"
+#include "gfxMatrix.h"
+
+namespace mozilla {
+namespace gl {
+
+void
+RectTriangles::AppendRectToCoordArray(InfallibleTArray<coord>& array,
+ GLfloat x0, GLfloat y0,
+ GLfloat x1, GLfloat y1)
+{
+ coord* v = array.AppendElements(6);
+
+ v[0].x = x0; v[0].y = y0;
+ v[1].x = x1; v[1].y = y0;
+ v[2].x = x0; v[2].y = y1;
+ v[3].x = x0; v[3].y = y1;
+ v[4].x = x1; v[4].y = y0;
+ v[5].x = x1; v[5].y = y1;
+}
+
+void
+RectTriangles::addRect(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
+ GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1,
+ bool flip_y /* = false */)
+{
+ if (flip_y) {
+ std::swap(ty0, ty1);
+ }
+ AppendRectToCoordArray(mVertexCoords, x0, y0, x1, y1);
+ AppendRectToCoordArray(mTexCoords, tx0, ty0, tx1, ty1);
+}
+
+static GLfloat
+WrapTexCoord(GLfloat v)
+{
+ // This should return values in range [0, 1.0)
+ return v - floorf(v);
+}
+
+void
+DecomposeIntoNoRepeatTriangles(const gfx::IntRect& aTexCoordRect,
+ const gfx::IntSize& aTexSize,
+ RectTriangles& aRects,
+ bool aFlipY /* = false */)
+{
+ // normalize this
+ gfx::IntRect tcr(aTexCoordRect);
+ while (tcr.x >= aTexSize.width)
+ tcr.x -= aTexSize.width;
+ while (tcr.y >= aTexSize.height)
+ tcr.y -= aTexSize.height;
+
+ // Compute top left and bottom right tex coordinates
+ GLfloat tl[2] =
+ { GLfloat(tcr.x) / GLfloat(aTexSize.width),
+ GLfloat(tcr.y) / GLfloat(aTexSize.height) };
+ GLfloat br[2] =
+ { GLfloat(tcr.XMost()) / GLfloat(aTexSize.width),
+ GLfloat(tcr.YMost()) / GLfloat(aTexSize.height) };
+
+ // then check if we wrap in either the x or y axis; if we do,
+ // then also use fmod to figure out the "true" non-wrapping
+ // texture coordinates.
+
+ bool xwrap = false, ywrap = false;
+ if (tcr.x < 0 || tcr.x > aTexSize.width ||
+ tcr.XMost() < 0 || tcr.XMost() > aTexSize.width)
+ {
+ xwrap = true;
+ tl[0] = WrapTexCoord(tl[0]);
+ br[0] = WrapTexCoord(br[0]);
+ }
+
+ if (tcr.y < 0 || tcr.y > aTexSize.height ||
+ tcr.YMost() < 0 || tcr.YMost() > aTexSize.height)
+ {
+ ywrap = true;
+ tl[1] = WrapTexCoord(tl[1]);
+ br[1] = WrapTexCoord(br[1]);
+ }
+
+ NS_ASSERTION(tl[0] >= 0.0f && tl[0] <= 1.0f &&
+ tl[1] >= 0.0f && tl[1] <= 1.0f &&
+ br[0] >= 0.0f && br[0] <= 1.0f &&
+ br[1] >= 0.0f && br[1] <= 1.0f,
+ "Somehow generated invalid texture coordinates");
+
+ // If xwrap is false, the texture will be sampled from tl[0]
+ // .. br[0]. If xwrap is true, then it will be split into tl[0]
+ // .. 1.0, and 0.0 .. br[0]. Same for the Y axis. The
+ // destination rectangle is also split appropriately, according
+ // to the calculated xmid/ymid values.
+
+ // There isn't a 1:1 mapping between tex coords and destination coords;
+ // when computing midpoints, we have to take that into account. We
+ // need to map the texture coords, which are (in the wrap case):
+ // |tl->1| and |0->br| to the |0->1| range of the vertex coords. So
+ // we have the length (1-tl)+(br) that needs to map into 0->1.
+ // These are only valid if there is wrap involved, they won't be used
+ // otherwise.
+ GLfloat xlen = (1.0f - tl[0]) + br[0];
+ GLfloat ylen = (1.0f - tl[1]) + br[1];
+
+ NS_ASSERTION(!xwrap || xlen > 0.0f, "xlen isn't > 0, what's going on?");
+ NS_ASSERTION(!ywrap || ylen > 0.0f, "ylen isn't > 0, what's going on?");
+ NS_ASSERTION(aTexCoordRect.width <= aTexSize.width &&
+ aTexCoordRect.height <= aTexSize.height, "tex coord rect would cause tiling!");
+
+ if (!xwrap && !ywrap) {
+ aRects.addRect(0.0f, 0.0f,
+ 1.0f, 1.0f,
+ tl[0], tl[1],
+ br[0], br[1],
+ aFlipY);
+ } else if (!xwrap && ywrap) {
+ GLfloat ymid = (1.0f - tl[1]) / ylen;
+ aRects.addRect(0.0f, 0.0f,
+ 1.0f, ymid,
+ tl[0], tl[1],
+ br[0], 1.0f,
+ aFlipY);
+ aRects.addRect(0.0f, ymid,
+ 1.0f, 1.0f,
+ tl[0], 0.0f,
+ br[0], br[1],
+ aFlipY);
+ } else if (xwrap && !ywrap) {
+ GLfloat xmid = (1.0f - tl[0]) / xlen;
+ aRects.addRect(0.0f, 0.0f,
+ xmid, 1.0f,
+ tl[0], tl[1],
+ 1.0f, br[1],
+ aFlipY);
+ aRects.addRect(xmid, 0.0f,
+ 1.0f, 1.0f,
+ 0.0f, tl[1],
+ br[0], br[1],
+ aFlipY);
+ } else {
+ GLfloat xmid = (1.0f - tl[0]) / xlen;
+ GLfloat ymid = (1.0f - tl[1]) / ylen;
+ aRects.addRect(0.0f, 0.0f,
+ xmid, ymid,
+ tl[0], tl[1],
+ 1.0f, 1.0f,
+ aFlipY);
+ aRects.addRect(xmid, 0.0f,
+ 1.0f, ymid,
+ 0.0f, tl[1],
+ br[0], 1.0f,
+ aFlipY);
+ aRects.addRect(0.0f, ymid,
+ xmid, 1.0f,
+ tl[0], 0.0f,
+ 1.0f, br[1],
+ aFlipY);
+ aRects.addRect(xmid, ymid,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ br[0], br[1],
+ aFlipY);
+ }
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/DecomposeIntoNoRepeatTriangles.h b/system/graphics/gl/DecomposeIntoNoRepeatTriangles.h
new file mode 100644
index 000000000..2d79b0a33
--- /dev/null
+++ b/system/graphics/gl/DecomposeIntoNoRepeatTriangles.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DecomposeIntoNoRepeatTriangles_h_
+#define DecomposeIntoNoRepeatTriangles_h_
+
+#include "GLTypes.h"
+#include "nsRect.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace gl {
+
+/** Helper for DecomposeIntoNoRepeatTriangles
+ */
+class RectTriangles {
+public:
+ typedef struct { GLfloat x,y; } coord;
+
+ // Always pass texture coordinates upright. If you want to flip the
+ // texture coordinates emitted to the tex_coords array, set flip_y to
+ // true.
+ void addRect(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
+ GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1,
+ bool flip_y = false);
+
+ /**
+ * these return a float pointer to the start of each array respectively.
+ * Use it for glVertexAttribPointer calls.
+ * We can return nullptr if we choose to use Vertex Buffer Objects here.
+ */
+ InfallibleTArray<coord>& vertCoords() {
+ return mVertexCoords;
+ }
+
+ InfallibleTArray<coord>& texCoords() {
+ return mTexCoords;
+ }
+
+ unsigned int elements() {
+ return mVertexCoords.Length();
+ }
+private:
+ // Reserve inline storage for one quad (2 triangles, 3 coords).
+ AutoTArray<coord, 6> mVertexCoords;
+ AutoTArray<coord, 6> mTexCoords;
+
+ static void
+ AppendRectToCoordArray(InfallibleTArray<coord>& array, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1);
+};
+
+/**
+ * Decompose drawing the possibly-wrapped aTexCoordRect rectangle
+ * of a texture of aTexSize into one or more rectangles (represented
+ * as 2 triangles) and associated tex coordinates, such that
+ * we don't have to use the REPEAT wrap mode. If aFlipY is true, the
+ * texture coordinates will be specified vertically flipped.
+ *
+ * The resulting triangle vertex coordinates will be in the space of
+ * (0.0, 0.0) to (1.0, 1.0) -- transform the coordinates appropriately
+ * if you need a different space.
+ *
+ * The resulting vertex coordinates should be drawn using GL_TRIANGLES,
+ * and rects.numRects * 3 * 6
+ */
+void DecomposeIntoNoRepeatTriangles(const gfx::IntRect& aTexCoordRect,
+ const gfx::IntSize& aTexSize,
+ RectTriangles& aRects,
+ bool aFlipY = false);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // DecomposeIntoNoRepeatTriangles_h_
diff --git a/system/graphics/gl/EGLUtils.cpp b/system/graphics/gl/EGLUtils.cpp
new file mode 100644
index 000000000..bc55d7fb0
--- /dev/null
+++ b/system/graphics/gl/EGLUtils.cpp
@@ -0,0 +1,114 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "EGLUtils.h"
+
+#include "GLContextEGL.h"
+
+namespace mozilla {
+namespace gl {
+
+bool
+DoesEGLContextSupportSharingWithEGLImage(GLContext* gl)
+{
+ return sEGLLibrary.HasKHRImageBase() &&
+ sEGLLibrary.HasKHRImageTexture2D() &&
+ gl->IsExtensionSupported(GLContext::OES_EGL_image);
+}
+
+EGLImage
+CreateEGLImage(GLContext* gl, GLuint tex)
+{
+ MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));
+
+ EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
+ EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
+ EGLImage image = sEGLLibrary.fCreateImage(EGL_DISPLAY(),
+ eglContext,
+ LOCAL_EGL_GL_TEXTURE_2D,
+ clientBuffer,
+ nullptr);
+ return image;
+}
+
+////////////////////////////////////////////////////////////////////////
+// EGLImageWrapper
+
+/*static*/ EGLImageWrapper*
+EGLImageWrapper::Create(GLContext* gl, GLuint tex)
+{
+ MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));
+
+ GLLibraryEGL& library = sEGLLibrary;
+ EGLDisplay display = EGL_DISPLAY();
+ EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
+ EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
+ EGLImage image = library.fCreateImage(display,
+ eglContext,
+ LOCAL_EGL_GL_TEXTURE_2D,
+ clientBuffer,
+ nullptr);
+ if (!image) {
+#ifdef DEBUG
+ printf_stderr("Could not create EGL images: ERROR (0x%04x)\n",
+ sEGLLibrary.fGetError());
+#endif
+ return nullptr;
+ }
+
+ return new EGLImageWrapper(library, display, image);
+}
+
+EGLImageWrapper::~EGLImageWrapper()
+{
+ mLibrary.fDestroyImage(mDisplay, mImage);
+}
+
+bool
+EGLImageWrapper::FenceSync(GLContext* gl)
+{
+ MOZ_ASSERT(!mSync);
+
+ if (mLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) {
+ mSync = mLibrary.fCreateSync(mDisplay,
+ LOCAL_EGL_SYNC_FENCE,
+ nullptr);
+ // We need to flush to make sure the sync object enters the command stream;
+ // we can't use EGL_SYNC_FLUSH_COMMANDS_BIT at wait time, because the wait
+ // happens on a different thread/context.
+ gl->fFlush();
+ }
+
+ if (!mSync) {
+ // we failed to create one, so just do a finish
+ gl->fFinish();
+ }
+
+ return true;
+}
+
+bool
+EGLImageWrapper::ClientWaitSync()
+{
+ if (!mSync) {
+ // if we have no sync object, then we did a Finish() earlier
+ return true;
+ }
+
+ // wait at most 1 second; this should really be never/rarely hit
+ const uint64_t ns_per_ms = 1000 * 1000;
+ EGLTime timeout = 1000 * ns_per_ms;
+
+ EGLint result = mLibrary.fClientWaitSync(mDisplay,
+ mSync,
+ 0,
+ timeout);
+ mLibrary.fDestroySync(mDisplay, mSync);
+ mSync = nullptr;
+
+ return result == LOCAL_EGL_CONDITION_SATISFIED;
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/EGLUtils.h b/system/graphics/gl/EGLUtils.h
new file mode 100644
index 000000000..71ca01e9c
--- /dev/null
+++ b/system/graphics/gl/EGLUtils.h
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef EGLUTILS_H_
+#define EGLUTILS_H_
+
+#include "GLContextTypes.h"
+#include "GLTypes.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLLibraryEGL;
+
+bool DoesEGLContextSupportSharingWithEGLImage(GLContext* gl);
+EGLImage CreateEGLImage(GLContext* gl, GLuint tex);
+
+////////////////////////////////////////////////////////////////////////
+// EGLImageWrapper
+
+class EGLImageWrapper
+{
+public:
+ static EGLImageWrapper* Create(GLContext* gl, GLuint tex);
+
+private:
+ GLLibraryEGL& mLibrary;
+ const EGLDisplay mDisplay;
+public:
+ const EGLImage mImage;
+private:
+ EGLSync mSync;
+
+ EGLImageWrapper(GLLibraryEGL& library,
+ EGLDisplay display,
+ EGLImage image)
+ : mLibrary(library)
+ , mDisplay(display)
+ , mImage(image)
+ , mSync(0)
+ {
+ MOZ_ASSERT(mImage);
+ }
+
+public:
+ ~EGLImageWrapper();
+
+ // Insert a sync point on the given context, which should be the current active
+ // context.
+ bool FenceSync(GLContext* gl);
+
+ bool ClientWaitSync();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/gl/GLBlitHelper.cpp b/system/graphics/gl/GLBlitHelper.cpp
new file mode 100644
index 000000000..12e30c880
--- /dev/null
+++ b/system/graphics/gl/GLBlitHelper.cpp
@@ -0,0 +1,803 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxUtils.h"
+#include "GLBlitHelper.h"
+#include "GLContext.h"
+#include "GLScreenBuffer.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/Preferences.h"
+#include "ImageContainer.h"
+#include "HeapCopyOfStackArray.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/UniquePtr.h"
+
+using mozilla::layers::PlanarYCbCrImage;
+using mozilla::layers::PlanarYCbCrData;
+
+namespace mozilla {
+namespace gl {
+
+GLBlitHelper::GLBlitHelper(GLContext* gl)
+ : mGL(gl)
+ , mTexBlit_Buffer(0)
+ , mTexBlit_VertShader(0)
+ , mTex2DBlit_FragShader(0)
+ , mTex2DRectBlit_FragShader(0)
+ , mTex2DBlit_Program(0)
+ , mTex2DRectBlit_Program(0)
+ , mYFlipLoc(-1)
+ , mTextureTransformLoc(-1)
+ , mTexExternalBlit_FragShader(0)
+ , mTexYUVPlanarBlit_FragShader(0)
+ , mTexNV12PlanarBlit_FragShader(0)
+ , mTexExternalBlit_Program(0)
+ , mTexYUVPlanarBlit_Program(0)
+ , mTexNV12PlanarBlit_Program(0)
+ , mFBO(0)
+ , mSrcTexY(0)
+ , mSrcTexCb(0)
+ , mSrcTexCr(0)
+ , mSrcTexEGL(0)
+ , mYTexScaleLoc(-1)
+ , mCbCrTexScaleLoc(-1)
+ , mYuvColorMatrixLoc(-1)
+ , mTexWidth(0)
+ , mTexHeight(0)
+ , mCurYScale(1.0f)
+ , mCurCbCrScale(1.0f)
+{
+}
+
+GLBlitHelper::~GLBlitHelper()
+{
+ if (!mGL->MakeCurrent())
+ return;
+
+ DeleteTexBlitProgram();
+
+ GLuint tex[] = {
+ mSrcTexY,
+ mSrcTexCb,
+ mSrcTexCr,
+ mSrcTexEGL,
+ };
+
+ mSrcTexY = mSrcTexCb = mSrcTexCr = mSrcTexEGL = 0;
+ mGL->fDeleteTextures(ArrayLength(tex), tex);
+
+ if (mFBO) {
+ mGL->fDeleteFramebuffers(1, &mFBO);
+ }
+ mFBO = 0;
+}
+
+// Allowed to be destructive of state we restore in functions below.
+bool
+GLBlitHelper::InitTexQuadProgram(BlitType target)
+{
+ const char kTexBlit_VertShaderSource[] = "\
+ #version 100 \n\
+ #ifdef GL_ES \n\
+ precision mediump float; \n\
+ #endif \n\
+ attribute vec2 aPosition; \n\
+ \n\
+ uniform float uYflip; \n\
+ varying vec2 vTexCoord; \n\
+ \n\
+ void main(void) \n\
+ { \n\
+ vTexCoord = aPosition; \n\
+ vTexCoord.y = abs(vTexCoord.y - uYflip); \n\
+ vec2 vertPos = aPosition * 2.0 - 1.0; \n\
+ gl_Position = vec4(vertPos, 0.0, 1.0); \n\
+ } \n\
+ ";
+
+ const char kTex2DBlit_FragShaderSource[] = "\
+ #version 100 \n\
+ #ifdef GL_ES \n\
+ #ifdef GL_FRAGMENT_PRECISION_HIGH \n\
+ precision highp float; \n\
+ #else \n\
+ precision mediump float; \n\
+ #endif \n\
+ #endif \n\
+ uniform sampler2D uTexUnit; \n\
+ \n\
+ varying vec2 vTexCoord; \n\
+ \n\
+ void main(void) \n\
+ { \n\
+ gl_FragColor = texture2D(uTexUnit, vTexCoord); \n\
+ } \n\
+ ";
+
+ const char kTex2DRectBlit_FragShaderSource[] = "\
+ #version 100 \n\
+ #ifdef GL_FRAGMENT_PRECISION_HIGH \n\
+ precision highp float; \n\
+ #else \n\
+ precision mediump float; \n\
+ #endif \n\
+ \n\
+ uniform sampler2D uTexUnit; \n\
+ uniform vec2 uTexCoordMult; \n\
+ \n\
+ varying vec2 vTexCoord; \n\
+ \n\
+ void main(void) \n\
+ { \n\
+ gl_FragColor = texture2DRect(uTexUnit, \n\
+ vTexCoord * uTexCoordMult); \n\
+ } \n\
+ ";
+ /* From Rec601:
+ [R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+ [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+ [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+ For [0,1] instead of [0,255], and to 5 places:
+ [R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+ [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+ [B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+ From Rec709:
+ [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+ [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+ [B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+ For [0,1] instead of [0,255], and to 5 places:
+ [R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+ [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+ [B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+ */
+ const char kTexYUVPlanarBlit_FragShaderSource[] = "\
+ #version 100 \n\
+ #ifdef GL_ES \n\
+ precision mediump float; \n\
+ #endif \n\
+ varying vec2 vTexCoord; \n\
+ uniform sampler2D uYTexture; \n\
+ uniform sampler2D uCbTexture; \n\
+ uniform sampler2D uCrTexture; \n\
+ uniform vec2 uYTexScale; \n\
+ uniform vec2 uCbCrTexScale; \n\
+ uniform mat3 uYuvColorMatrix; \n\
+ void main() \n\
+ { \n\
+ float y = texture2D(uYTexture, vTexCoord * uYTexScale).r; \n\
+ float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).r; \n\
+ float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).r; \n\
+ y = y - 0.06275; \n\
+ cb = cb - 0.50196; \n\
+ cr = cr - 0.50196; \n\
+ vec3 yuv = vec3(y, cb, cr); \n\
+ gl_FragColor.rgb = uYuvColorMatrix * yuv; \n\
+ gl_FragColor.a = 1.0; \n\
+ } \n\
+ ";
+
+ bool success = false;
+
+ GLuint* programPtr;
+ GLuint* fragShaderPtr;
+ const char* fragShaderSource;
+ switch (target) {
+ case ConvertEGLImage:
+ case BlitTex2D:
+ programPtr = &mTex2DBlit_Program;
+ fragShaderPtr = &mTex2DBlit_FragShader;
+ fragShaderSource = kTex2DBlit_FragShaderSource;
+ break;
+ case BlitTexRect:
+ programPtr = &mTex2DRectBlit_Program;
+ fragShaderPtr = &mTex2DRectBlit_FragShader;
+ fragShaderSource = kTex2DRectBlit_FragShaderSource;
+ break;
+ case ConvertPlanarYCbCr:
+ programPtr = &mTexYUVPlanarBlit_Program;
+ fragShaderPtr = &mTexYUVPlanarBlit_FragShader;
+ fragShaderSource = kTexYUVPlanarBlit_FragShaderSource;
+ break;
+ default:
+ return false;
+ }
+
+ GLuint& program = *programPtr;
+ GLuint& fragShader = *fragShaderPtr;
+
+ // Use do-while(false) to let us break on failure
+ do {
+ if (program) {
+ // Already have it...
+ success = true;
+ break;
+ }
+
+ if (!mTexBlit_Buffer) {
+
+ /* CCW tri-strip:
+ * 2---3
+ * | \ |
+ * 0---1
+ */
+ GLfloat verts[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+ HeapCopyOfStackArray<GLfloat> vertsOnHeap(verts);
+
+ MOZ_ASSERT(!mTexBlit_Buffer);
+ mGL->fGenBuffers(1, &mTexBlit_Buffer);
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
+
+ // Make sure we have a sane size.
+ mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, vertsOnHeap.ByteLength(), vertsOnHeap.Data(), LOCAL_GL_STATIC_DRAW);
+ }
+
+ if (!mTexBlit_VertShader) {
+
+ const char* vertShaderSource = kTexBlit_VertShaderSource;
+
+ mTexBlit_VertShader = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
+ mGL->fShaderSource(mTexBlit_VertShader, 1, &vertShaderSource, nullptr);
+ mGL->fCompileShader(mTexBlit_VertShader);
+ }
+
+ MOZ_ASSERT(!fragShader);
+ fragShader = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
+ mGL->fShaderSource(fragShader, 1, &fragShaderSource, nullptr);
+ mGL->fCompileShader(fragShader);
+
+ program = mGL->fCreateProgram();
+ mGL->fAttachShader(program, mTexBlit_VertShader);
+ mGL->fAttachShader(program, fragShader);
+ mGL->fBindAttribLocation(program, 0, "aPosition");
+ mGL->fLinkProgram(program);
+
+ if (GLContext::ShouldSpew()) {
+ GLint status = 0;
+ mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_COMPILE_STATUS, &status);
+ if (status != LOCAL_GL_TRUE) {
+ NS_ERROR("Vert shader compilation failed.");
+
+ GLint length = 0;
+ mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
+ if (!length) {
+ printf_stderr("No shader info log available.\n");
+ break;
+ }
+
+ auto buffer = MakeUnique<char[]>(length);
+ mGL->fGetShaderInfoLog(mTexBlit_VertShader, length, nullptr, buffer.get());
+
+ printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
+ break;
+ }
+
+ status = 0;
+ mGL->fGetShaderiv(fragShader, LOCAL_GL_COMPILE_STATUS, &status);
+ if (status != LOCAL_GL_TRUE) {
+ NS_ERROR("Frag shader compilation failed.");
+
+ GLint length = 0;
+ mGL->fGetShaderiv(fragShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
+ if (!length) {
+ printf_stderr("No shader info log available.\n");
+ break;
+ }
+
+ auto buffer = MakeUnique<char[]>(length);
+ mGL->fGetShaderInfoLog(fragShader, length, nullptr, buffer.get());
+
+ printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
+ break;
+ }
+ }
+
+ GLint status = 0;
+ mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &status);
+ if (status != LOCAL_GL_TRUE) {
+ if (GLContext::ShouldSpew()) {
+ NS_ERROR("Linking blit program failed.");
+ GLint length = 0;
+ mGL->fGetProgramiv(program, LOCAL_GL_INFO_LOG_LENGTH, &length);
+ if (!length) {
+ printf_stderr("No program info log available.\n");
+ break;
+ }
+
+ auto buffer = MakeUnique<char[]>(length);
+ mGL->fGetProgramInfoLog(program, length, nullptr, buffer.get());
+
+ printf_stderr("Program info log (%d bytes): %s\n", length, buffer.get());
+ }
+ break;
+ }
+
+ // Cache and set attribute and uniform
+ mGL->fUseProgram(program);
+ switch (target) {
+ case BlitTex2D:
+ case BlitTexRect:
+ case ConvertEGLImage: {
+ GLint texUnitLoc = mGL->fGetUniformLocation(program, "uTexUnit");
+ MOZ_ASSERT(texUnitLoc != -1, "uniform uTexUnit not found");
+ mGL->fUniform1i(texUnitLoc, 0);
+ break;
+ }
+ case ConvertPlanarYCbCr: {
+ GLint texY = mGL->fGetUniformLocation(program, "uYTexture");
+ GLint texCb = mGL->fGetUniformLocation(program, "uCbTexture");
+ GLint texCr = mGL->fGetUniformLocation(program, "uCrTexture");
+ mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale");
+ mCbCrTexScaleLoc = mGL->fGetUniformLocation(program, "uCbCrTexScale");
+ mYuvColorMatrixLoc = mGL->fGetUniformLocation(program, "uYuvColorMatrix");
+
+ DebugOnly<bool> hasUniformLocations = texY != -1 &&
+ texCb != -1 &&
+ texCr != -1 &&
+ mYTexScaleLoc != -1 &&
+ mCbCrTexScaleLoc != -1 &&
+ mYuvColorMatrixLoc != -1;
+ MOZ_ASSERT(hasUniformLocations, "uniforms not found");
+
+ mGL->fUniform1i(texY, Channel_Y);
+ mGL->fUniform1i(texCb, Channel_Cb);
+ mGL->fUniform1i(texCr, Channel_Cr);
+ break;
+ }
+ default:
+ return false;
+ }
+ MOZ_ASSERT(mGL->fGetAttribLocation(program, "aPosition") == 0);
+ mYFlipLoc = mGL->fGetUniformLocation(program, "uYflip");
+ MOZ_ASSERT(mYFlipLoc != -1, "uniform: uYflip not found");
+ mTextureTransformLoc = mGL->fGetUniformLocation(program, "uTextureTransform");
+ if (mTextureTransformLoc >= 0) {
+ // Set identity matrix as default
+ gfx::Matrix4x4 identity;
+ mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &identity._11);
+ }
+ success = true;
+ } while (false);
+
+ if (!success) {
+ // Clean up:
+ DeleteTexBlitProgram();
+ return false;
+ }
+
+ mGL->fUseProgram(program);
+ mGL->fEnableVertexAttribArray(0);
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
+ mGL->fVertexAttribPointer(0,
+ 2,
+ LOCAL_GL_FLOAT,
+ false,
+ 0,
+ nullptr);
+ return true;
+}
+
+bool
+GLBlitHelper::UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize)
+{
+ if (!InitTexQuadProgram(target)) {
+ return false;
+ }
+
+ if (target == BlitTexRect) {
+ GLint texCoordMultLoc = mGL->fGetUniformLocation(mTex2DRectBlit_Program, "uTexCoordMult");
+ MOZ_ASSERT(texCoordMultLoc != -1, "uniform not found");
+ mGL->fUniform2f(texCoordMultLoc, srcSize.width, srcSize.height);
+ }
+
+ return true;
+}
+
+void
+GLBlitHelper::DeleteTexBlitProgram()
+{
+ if (mTexBlit_Buffer) {
+ mGL->fDeleteBuffers(1, &mTexBlit_Buffer);
+ mTexBlit_Buffer = 0;
+ }
+ if (mTexBlit_VertShader) {
+ mGL->fDeleteShader(mTexBlit_VertShader);
+ mTexBlit_VertShader = 0;
+ }
+ if (mTex2DBlit_FragShader) {
+ mGL->fDeleteShader(mTex2DBlit_FragShader);
+ mTex2DBlit_FragShader = 0;
+ }
+ if (mTex2DRectBlit_FragShader) {
+ mGL->fDeleteShader(mTex2DRectBlit_FragShader);
+ mTex2DRectBlit_FragShader = 0;
+ }
+ if (mTex2DBlit_Program) {
+ mGL->fDeleteProgram(mTex2DBlit_Program);
+ mTex2DBlit_Program = 0;
+ }
+ if (mTex2DRectBlit_Program) {
+ mGL->fDeleteProgram(mTex2DRectBlit_Program);
+ mTex2DRectBlit_Program = 0;
+ }
+ if (mTexExternalBlit_FragShader) {
+ mGL->fDeleteShader(mTexExternalBlit_FragShader);
+ mTexExternalBlit_FragShader = 0;
+ }
+ if (mTexYUVPlanarBlit_FragShader) {
+ mGL->fDeleteShader(mTexYUVPlanarBlit_FragShader);
+ mTexYUVPlanarBlit_FragShader = 0;
+ }
+ if (mTexNV12PlanarBlit_FragShader) {
+ mGL->fDeleteShader(mTexNV12PlanarBlit_FragShader);
+ mTexNV12PlanarBlit_FragShader = 0;
+ }
+ if (mTexExternalBlit_Program) {
+ mGL->fDeleteProgram(mTexExternalBlit_Program);
+ mTexExternalBlit_Program = 0;
+ }
+ if (mTexYUVPlanarBlit_Program) {
+ mGL->fDeleteProgram(mTexYUVPlanarBlit_Program);
+ mTexYUVPlanarBlit_Program = 0;
+ }
+ if (mTexNV12PlanarBlit_Program) {
+ mGL->fDeleteProgram(mTexNV12PlanarBlit_Program);
+ mTexNV12PlanarBlit_Program = 0;
+ }
+}
+
+void
+GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ bool internalFBs)
+{
+ MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
+ MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+
+ ScopedBindFramebuffer boundFB(mGL);
+ ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
+
+ if (internalFBs) {
+ mGL->Screen()->BindReadFB_Internal(srcFB);
+ mGL->Screen()->BindDrawFB_Internal(destFB);
+ } else {
+ mGL->BindReadFB(srcFB);
+ mGL->BindDrawFB(destFB);
+ }
+
+ mGL->fBlitFramebuffer(0, 0, srcSize.width, srcSize.height,
+ 0, 0, destSize.width, destSize.height,
+ LOCAL_GL_COLOR_BUFFER_BIT,
+ LOCAL_GL_NEAREST);
+}
+
+void
+GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ const GLFormats& srcFormats,
+ bool internalFBs)
+{
+ MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
+ MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+
+ if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+ BlitFramebufferToFramebuffer(srcFB, destFB,
+ srcSize, destSize,
+ internalFBs);
+ return;
+ }
+
+ GLuint tex = CreateTextureForOffscreen(mGL, srcFormats, srcSize);
+ MOZ_ASSERT(tex);
+
+ BlitFramebufferToTexture(srcFB, tex, srcSize, srcSize, internalFBs);
+ BlitTextureToFramebuffer(tex, destFB, srcSize, destSize, internalFBs);
+
+ mGL->fDeleteTextures(1, &tex);
+}
+
+void
+GLBlitHelper::BindAndUploadYUVTexture(Channel which,
+ uint32_t width,
+ uint32_t height,
+ void* data,
+ bool needsAllocation)
+{
+ MOZ_ASSERT(which < Channel_Max, "Invalid channel!");
+ GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr};
+ GLuint& tex = *srcTexArr[which];
+
+ // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
+ // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
+ GLenum format;
+ GLenum internalFormat;
+ if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
+ mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
+ format = LOCAL_GL_RED;
+ internalFormat = LOCAL_GL_R8;
+ } else {
+ format = LOCAL_GL_LUMINANCE;
+ internalFormat = LOCAL_GL_LUMINANCE;
+ }
+
+ if (!tex) {
+ MOZ_ASSERT(needsAllocation);
+ tex = CreateTexture(mGL, internalFormat, format, LOCAL_GL_UNSIGNED_BYTE,
+ gfx::IntSize(width, height), false);
+ }
+ mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + which);
+
+ mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
+ if (!needsAllocation) {
+ mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
+ 0,
+ 0,
+ 0,
+ width,
+ height,
+ format,
+ LOCAL_GL_UNSIGNED_BYTE,
+ data);
+ } else {
+ mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
+ 0,
+ internalFormat,
+ width,
+ height,
+ 0,
+ format,
+ LOCAL_GL_UNSIGNED_BYTE,
+ data);
+ }
+}
+
+void
+GLBlitHelper::BindAndUploadEGLImage(EGLImage image, GLuint target)
+{
+ MOZ_ASSERT(image != EGL_NO_IMAGE, "Bad EGLImage");
+
+ if (!mSrcTexEGL) {
+ mGL->fGenTextures(1, &mSrcTexEGL);
+ mGL->fBindTexture(target, mSrcTexEGL);
+ mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
+ mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
+ } else {
+ mGL->fBindTexture(target, mSrcTexEGL);
+ }
+ mGL->fEGLImageTargetTexture2D(target, image);
+}
+
+bool
+GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage)
+{
+ ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
+ const PlanarYCbCrData* yuvData = yuvImage->GetData();
+
+ bool needsAllocation = false;
+ if (mTexWidth != yuvData->mYStride || mTexHeight != yuvData->mYSize.height) {
+ mTexWidth = yuvData->mYStride;
+ mTexHeight = yuvData->mYSize.height;
+ needsAllocation = true;
+ }
+
+ GLint oldTex[3];
+ for (int i = 0; i < 3; i++) {
+ mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+ mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]);
+ }
+ BindAndUploadYUVTexture(Channel_Y, yuvData->mYStride, yuvData->mYSize.height, yuvData->mYChannel, needsAllocation);
+ BindAndUploadYUVTexture(Channel_Cb, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCbChannel, needsAllocation);
+ BindAndUploadYUVTexture(Channel_Cr, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCrChannel, needsAllocation);
+
+ if (needsAllocation) {
+ mGL->fUniform2f(mYTexScaleLoc, (float)yuvData->mYSize.width/yuvData->mYStride, 1.0f);
+ mGL->fUniform2f(mCbCrTexScaleLoc, (float)yuvData->mCbCrSize.width/yuvData->mCbCrStride, 1.0f);
+ }
+
+ float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(yuvData->mYUVColorSpace);
+ mGL->fUniformMatrix3fv(mYuvColorMatrixLoc, 1, 0, yuvToRgb);
+
+ mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+ for (int i = 0; i < 3; i++) {
+ mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+ mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]);
+ }
+ return true;
+}
+
+bool
+GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
+ const gfx::IntSize& destSize,
+ GLuint destFB,
+ OriginPos destOrigin)
+{
+ ScopedGLDrawState autoStates(mGL);
+
+ BlitType type;
+ OriginPos srcOrigin;
+
+ switch (srcImage->GetFormat()) {
+ case ImageFormat::PLANAR_YCBCR:
+ type = ConvertPlanarYCbCr;
+ srcOrigin = OriginPos::BottomLeft;
+ break;
+
+ default:
+ return false;
+ }
+
+ bool init = InitTexQuadProgram(type);
+ if (!init) {
+ return false;
+ }
+
+ const bool needsYFlip = (srcOrigin != destOrigin);
+ mGL->fUniform1f(mYFlipLoc, needsYFlip ? (float)1.0 : (float)0.0);
+
+ ScopedBindFramebuffer boundFB(mGL, destFB);
+ mGL->fColorMask(LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE);
+ mGL->fViewport(0, 0, destSize.width, destSize.height);
+
+ switch (type) {
+ case ConvertPlanarYCbCr: {
+ const auto saved = mGL->GetIntAs<GLint>(LOCAL_GL_UNPACK_ALIGNMENT);
+ mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
+ const auto ret = BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage));
+ mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, saved);
+ return ret;
+ }
+
+ default:
+ return false;
+ }
+}
+
+bool
+GLBlitHelper::BlitImageToTexture(layers::Image* srcImage,
+ const gfx::IntSize& destSize,
+ GLuint destTex,
+ GLenum destTarget,
+ OriginPos destOrigin)
+{
+ ScopedFramebufferForTexture autoFBForTex(mGL, destTex, destTarget);
+ if (!autoFBForTex.IsComplete())
+ return false;
+
+ return BlitImageToFramebuffer(srcImage, destSize, autoFBForTex.FB(), destOrigin);
+}
+
+void
+GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget,
+ bool internalFBs)
+{
+ MOZ_ASSERT(mGL->fIsTexture(srcTex));
+ MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+
+ if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+ ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
+ MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete());
+
+ BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB,
+ srcSize, destSize,
+ internalFBs);
+ return;
+ }
+
+ DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget,
+ internalFBs);
+}
+
+
+void
+GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget,
+ bool internalFBs)
+{
+ BlitType type;
+ switch (srcTarget) {
+ case LOCAL_GL_TEXTURE_2D:
+ type = BlitTex2D;
+ break;
+ case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+ type = BlitTexRect;
+ break;
+ default:
+ MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`.");
+ break;
+ }
+
+ ScopedGLDrawState autoStates(mGL);
+ if (internalFBs) {
+ mGL->Screen()->BindFB_Internal(destFB);
+ } else {
+ mGL->BindFB(destFB);
+ }
+
+ // Does destructive things to (only!) what we just saved above.
+ bool good = UseTexQuadProgram(type, srcSize);
+ if (!good) {
+ // We're up against the wall, so bail.
+ MOZ_DIAGNOSTIC_ASSERT(false,
+ "Error: Failed to prepare to blit texture->framebuffer.\n");
+ mGL->fScissor(0, 0, destSize.width, destSize.height);
+ mGL->fColorMask(1, 1, 1, 1);
+ mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
+ return;
+ }
+
+ mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+}
+
+void
+GLBlitHelper::BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum destTarget,
+ bool internalFBs)
+{
+ MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
+ MOZ_ASSERT(mGL->fIsTexture(destTex));
+
+ if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+ ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
+
+ BlitFramebufferToFramebuffer(srcFB, destWrapper.FB(),
+ srcSize, destSize,
+ internalFBs);
+ return;
+ }
+
+ ScopedBindTexture autoTex(mGL, destTex, destTarget);
+
+ ScopedBindFramebuffer boundFB(mGL);
+ if (internalFBs) {
+ mGL->Screen()->BindFB_Internal(srcFB);
+ } else {
+ mGL->BindFB(srcFB);
+ }
+
+ ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
+ mGL->fCopyTexSubImage2D(destTarget, 0,
+ 0, 0,
+ 0, 0,
+ srcSize.width, srcSize.height);
+}
+
+void
+GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget, GLenum destTarget)
+{
+ MOZ_ASSERT(mGL->fIsTexture(srcTex));
+ MOZ_ASSERT(mGL->fIsTexture(destTex));
+
+ // Generally, just use the CopyTexSubImage path
+ ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
+
+ BlitFramebufferToTexture(srcWrapper.FB(), destTex,
+ srcSize, destSize, destTarget);
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/GLBlitHelper.h b/system/graphics/gl/GLBlitHelper.h
new file mode 100644
index 000000000..9dd8cd887
--- /dev/null
+++ b/system/graphics/gl/GLBlitHelper.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLBLITHELPER_H_
+#define GLBLITHELPER_H_
+
+#include "GLContextTypes.h"
+#include "GLConsts.h"
+#include "nsSize.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/gfx/Point.h"
+
+namespace mozilla {
+
+namespace layers {
+class Image;
+class PlanarYCbCrImage;
+class GrallocImage;
+class SurfaceTextureImage;
+class MacIOSurfaceImage;
+class EGLImageImage;
+} // namespace layers
+
+namespace gl {
+
+class GLContext;
+
+/** Buffer blitting helper */
+class GLBlitHelper final
+{
+ enum Channel
+ {
+ Channel_Y = 0,
+ Channel_Cb,
+ Channel_Cr,
+ Channel_Max,
+ };
+
+ /**
+ * BlitTex2D is used to copy blit the content of a GL_TEXTURE_2D object,
+ * BlitTexRect is used to copy blit the content of a GL_TEXTURE_RECT object,
+ * The difference between BlitTex2D and BlitTexRect is the texture type, which affect
+ * the fragment shader a bit.
+ *
+ * ConvertGralloc is used to color convert copy blit the GrallocImage into a
+ * normal RGB texture by egl_image_external extension
+ * ConvertPlnarYcbCr is used to color convert copy blit the PlanarYCbCrImage
+ * into a normal RGB texture by create textures of each color channel, and
+ * convert it in GPU.
+ * Convert type is created for canvas.
+ */
+ enum BlitType
+ {
+ BlitTex2D,
+ BlitTexRect,
+ ConvertGralloc,
+ ConvertPlanarYCbCr,
+ ConvertSurfaceTexture,
+ ConvertEGLImage,
+ ConvertMacIOSurfaceImage
+ };
+ // The GLContext is the sole owner of the GLBlitHelper.
+ GLContext* mGL;
+
+ GLuint mTexBlit_Buffer;
+ GLuint mTexBlit_VertShader;
+ GLuint mTex2DBlit_FragShader;
+ GLuint mTex2DRectBlit_FragShader;
+ GLuint mTex2DBlit_Program;
+ GLuint mTex2DRectBlit_Program;
+
+ GLint mYFlipLoc;
+
+ GLint mTextureTransformLoc;
+
+ // Data for image blit path
+ GLuint mTexExternalBlit_FragShader;
+ GLuint mTexYUVPlanarBlit_FragShader;
+ GLuint mTexNV12PlanarBlit_FragShader;
+ GLuint mTexExternalBlit_Program;
+ GLuint mTexYUVPlanarBlit_Program;
+ GLuint mTexNV12PlanarBlit_Program;
+ GLuint mFBO;
+ GLuint mSrcTexY;
+ GLuint mSrcTexCb;
+ GLuint mSrcTexCr;
+ GLuint mSrcTexEGL;
+ GLint mYTexScaleLoc;
+ GLint mCbCrTexScaleLoc;
+ GLint mYuvColorMatrixLoc;
+ int mTexWidth;
+ int mTexHeight;
+
+ // Cache some uniform values
+ float mCurYScale;
+ float mCurCbCrScale;
+
+ void UseBlitProgram();
+ void SetBlitFramebufferForDestTexture(GLuint aTexture);
+
+ bool UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize);
+ bool InitTexQuadProgram(BlitType target = BlitTex2D);
+ void DeleteTexBlitProgram();
+ void BindAndUploadYUVTexture(Channel which, uint32_t width, uint32_t height, void* data, bool allocation);
+ void BindAndUploadEGLImage(EGLImage image, GLuint target);
+
+ bool BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage);
+
+ explicit GLBlitHelper(GLContext* gl);
+
+ friend class GLContext;
+
+public:
+ ~GLBlitHelper();
+
+ // If you don't have |srcFormats| for the 2nd definition,
+ // then you'll need the framebuffer_blit extensions to use
+ // the first BlitFramebufferToFramebuffer.
+ void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ bool internalFBs = false);
+ void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ const GLFormats& srcFormats,
+ bool internalFBs = false);
+ void BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+ bool internalFBs = false);
+ void DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+ bool internalFBs = false);
+ void BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum destTarget = LOCAL_GL_TEXTURE_2D,
+ bool internalFBs = false);
+ void BlitTextureToTexture(GLuint srcTex, GLuint destTex,
+ const gfx::IntSize& srcSize,
+ const gfx::IntSize& destSize,
+ GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+ GLenum destTarget = LOCAL_GL_TEXTURE_2D);
+ bool BlitImageToFramebuffer(layers::Image* srcImage, const gfx::IntSize& destSize,
+ GLuint destFB, OriginPos destOrigin);
+ bool BlitImageToTexture(layers::Image* srcImage, const gfx::IntSize& destSize,
+ GLuint destTex, GLenum destTarget, OriginPos destOrigin);
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GLBLITHELPER_H_
diff --git a/system/graphics/gl/GLConsts.h b/system/graphics/gl/GLConsts.h
new file mode 100644
index 000000000..ad6175e16
--- /dev/null
+++ b/system/graphics/gl/GLConsts.h
@@ -0,0 +1,5951 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONSTS_H_
+#define GLCONSTS_H_
+
+/**
+ * GENERATED FILE, DO NOT MODIFY DIRECTLY.
+ * This is a file generated directly from the official OpenGL registry
+ * xml available http://www.opengl.org/registry/#specfiles.
+ *
+ * To generate this file, see tutorial in 'GLParseRegistryXML.py'.
+ */
+
+// GL
+#define LOCAL_GL_1PASS_EXT 0x80A1
+#define LOCAL_GL_1PASS_SGIS 0x80A1
+#define LOCAL_GL_2D 0x0600
+#define LOCAL_GL_2PASS_0_EXT 0x80A2
+#define LOCAL_GL_2PASS_0_SGIS 0x80A2
+#define LOCAL_GL_2PASS_1_EXT 0x80A3
+#define LOCAL_GL_2PASS_1_SGIS 0x80A3
+#define LOCAL_GL_2X_BIT_ATI 0x00000001
+#define LOCAL_GL_2_BYTES 0x1407
+#define LOCAL_GL_3D 0x0601
+#define LOCAL_GL_3DC_XY_AMD 0x87FA
+#define LOCAL_GL_3DC_X_AMD 0x87F9
+#define LOCAL_GL_3D_COLOR 0x0602
+#define LOCAL_GL_3D_COLOR_TEXTURE 0x0603
+#define LOCAL_GL_3_BYTES 0x1408
+#define LOCAL_GL_422_AVERAGE_EXT 0x80CE
+#define LOCAL_GL_422_EXT 0x80CC
+#define LOCAL_GL_422_REV_AVERAGE_EXT 0x80CF
+#define LOCAL_GL_422_REV_EXT 0x80CD
+#define LOCAL_GL_4D_COLOR_TEXTURE 0x0604
+#define LOCAL_GL_4PASS_0_EXT 0x80A4
+#define LOCAL_GL_4PASS_0_SGIS 0x80A4
+#define LOCAL_GL_4PASS_1_EXT 0x80A5
+#define LOCAL_GL_4PASS_1_SGIS 0x80A5
+#define LOCAL_GL_4PASS_2_EXT 0x80A6
+#define LOCAL_GL_4PASS_2_SGIS 0x80A6
+#define LOCAL_GL_4PASS_3_EXT 0x80A7
+#define LOCAL_GL_4PASS_3_SGIS 0x80A7
+#define LOCAL_GL_4X_BIT_ATI 0x00000002
+#define LOCAL_GL_4_BYTES 0x1409
+#define LOCAL_GL_8X_BIT_ATI 0x00000004
+#define LOCAL_GL_ABGR_EXT 0x8000
+#define LOCAL_GL_ACCUM 0x0100
+#define LOCAL_GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD
+#define LOCAL_GL_ACCUM_ALPHA_BITS 0x0D5B
+#define LOCAL_GL_ACCUM_BLUE_BITS 0x0D5A
+#define LOCAL_GL_ACCUM_BUFFER_BIT 0x00000200
+#define LOCAL_GL_ACCUM_CLEAR_VALUE 0x0B80
+#define LOCAL_GL_ACCUM_GREEN_BITS 0x0D59
+#define LOCAL_GL_ACCUM_RED_BITS 0x0D58
+#define LOCAL_GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9
+#define LOCAL_GL_ACTIVE_ATTRIBUTES 0x8B89
+#define LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define LOCAL_GL_ACTIVE_PROGRAM 0x8259
+#define LOCAL_GL_ACTIVE_PROGRAM_EXT 0x8B8D
+#define LOCAL_GL_ACTIVE_RESOURCES 0x92F5
+#define LOCAL_GL_ACTIVE_STENCIL_FACE_EXT 0x8911
+#define LOCAL_GL_ACTIVE_SUBROUTINES 0x8DE5
+#define LOCAL_GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48
+#define LOCAL_GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6
+#define LOCAL_GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47
+#define LOCAL_GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49
+#define LOCAL_GL_ACTIVE_TEXTURE 0x84E0
+#define LOCAL_GL_ACTIVE_TEXTURE_ARB 0x84E0
+#define LOCAL_GL_ACTIVE_UNIFORMS 0x8B86
+#define LOCAL_GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+#define LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35
+#define LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define LOCAL_GL_ACTIVE_VARIABLES 0x9305
+#define LOCAL_GL_ACTIVE_VARYINGS_NV 0x8C81
+#define LOCAL_GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82
+#define LOCAL_GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5
+#define LOCAL_GL_ADD 0x0104
+#define LOCAL_GL_ADD_ATI 0x8963
+#define LOCAL_GL_ADD_BLEND_IMG 0x8C09
+#define LOCAL_GL_ADD_SIGNED 0x8574
+#define LOCAL_GL_ADD_SIGNED_ARB 0x8574
+#define LOCAL_GL_ADD_SIGNED_EXT 0x8574
+#define LOCAL_GL_ADJACENT_PAIRS_NV 0x90AE
+#define LOCAL_GL_AFFINE_2D_NV 0x9092
+#define LOCAL_GL_AFFINE_3D_NV 0x9094
+#define LOCAL_GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define LOCAL_GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define LOCAL_GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210
+#define LOCAL_GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211
+#define LOCAL_GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E
+#define LOCAL_GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F
+#define LOCAL_GL_ALL_ATTRIB_BITS 0xFFFFFFFF
+#define LOCAL_GL_ALL_BARRIER_BITS 0xFFFFFFFF
+#define LOCAL_GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF
+#define LOCAL_GL_ALL_COMPLETED_NV 0x84F2
+#define LOCAL_GL_ALL_SHADER_BITS 0xFFFFFFFF
+#define LOCAL_GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF
+#define LOCAL_GL_ALL_STATIC_DATA_IBM 103060
+#define LOCAL_GL_ALPHA 0x1906
+#define LOCAL_GL_ALPHA12 0x803D
+#define LOCAL_GL_ALPHA12_EXT 0x803D
+#define LOCAL_GL_ALPHA16 0x803E
+#define LOCAL_GL_ALPHA16F_ARB 0x881C
+#define LOCAL_GL_ALPHA16F_EXT 0x881C
+#define LOCAL_GL_ALPHA16I_EXT 0x8D8A
+#define LOCAL_GL_ALPHA16UI_EXT 0x8D78
+#define LOCAL_GL_ALPHA16_EXT 0x803E
+#define LOCAL_GL_ALPHA16_SNORM 0x9018
+#define LOCAL_GL_ALPHA32F_ARB 0x8816
+#define LOCAL_GL_ALPHA32F_EXT 0x8816
+#define LOCAL_GL_ALPHA32I_EXT 0x8D84
+#define LOCAL_GL_ALPHA32UI_EXT 0x8D72
+#define LOCAL_GL_ALPHA4 0x803B
+#define LOCAL_GL_ALPHA4_EXT 0x803B
+#define LOCAL_GL_ALPHA8 0x803C
+#define LOCAL_GL_ALPHA8I_EXT 0x8D90
+#define LOCAL_GL_ALPHA8UI_EXT 0x8D7E
+#define LOCAL_GL_ALPHA8_EXT 0x803C
+#define LOCAL_GL_ALPHA8_OES 0x803C
+#define LOCAL_GL_ALPHA8_SNORM 0x9014
+#define LOCAL_GL_ALPHA_BIAS 0x0D1D
+#define LOCAL_GL_ALPHA_BITS 0x0D55
+#define LOCAL_GL_ALPHA_FLOAT16_APPLE 0x881C
+#define LOCAL_GL_ALPHA_FLOAT16_ATI 0x881C
+#define LOCAL_GL_ALPHA_FLOAT32_APPLE 0x8816
+#define LOCAL_GL_ALPHA_FLOAT32_ATI 0x8816
+#define LOCAL_GL_ALPHA_INTEGER 0x8D97
+#define LOCAL_GL_ALPHA_INTEGER_EXT 0x8D97
+#define LOCAL_GL_ALPHA_MAX_CLAMP_INGR 0x8567
+#define LOCAL_GL_ALPHA_MAX_SGIX 0x8321
+#define LOCAL_GL_ALPHA_MIN_CLAMP_INGR 0x8563
+#define LOCAL_GL_ALPHA_MIN_SGIX 0x8320
+#define LOCAL_GL_ALPHA_SCALE 0x0D1C
+#define LOCAL_GL_ALPHA_SNORM 0x9010
+#define LOCAL_GL_ALPHA_TEST 0x0BC0
+#define LOCAL_GL_ALPHA_TEST_FUNC 0x0BC1
+#define LOCAL_GL_ALPHA_TEST_FUNC_QCOM 0x0BC1
+#define LOCAL_GL_ALPHA_TEST_QCOM 0x0BC0
+#define LOCAL_GL_ALPHA_TEST_REF 0x0BC2
+#define LOCAL_GL_ALPHA_TEST_REF_QCOM 0x0BC2
+#define LOCAL_GL_ALREADY_SIGNALED 0x911A
+#define LOCAL_GL_ALREADY_SIGNALED_APPLE 0x911A
+#define LOCAL_GL_ALWAYS 0x0207
+#define LOCAL_GL_ALWAYS_FAST_HINT_PGI 0x1A20C
+#define LOCAL_GL_ALWAYS_SOFT_HINT_PGI 0x1A20D
+#define LOCAL_GL_AMBIENT 0x1200
+#define LOCAL_GL_AMBIENT_AND_DIFFUSE 0x1602
+#define LOCAL_GL_AND 0x1501
+#define LOCAL_GL_AND_INVERTED 0x1504
+#define LOCAL_GL_AND_REVERSE 0x1502
+#define LOCAL_GL_ANY_SAMPLES_PASSED 0x8C2F
+#define LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A
+#define LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A
+#define LOCAL_GL_ANY_SAMPLES_PASSED_EXT 0x8C2F
+#define LOCAL_GL_ARC_TO_NV 0xFE
+#define LOCAL_GL_ARRAY_BUFFER 0x8892
+#define LOCAL_GL_ARRAY_BUFFER_ARB 0x8892
+#define LOCAL_GL_ARRAY_BUFFER_BINDING 0x8894
+#define LOCAL_GL_ARRAY_BUFFER_BINDING_ARB 0x8894
+#define LOCAL_GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9
+#define LOCAL_GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8
+#define LOCAL_GL_ARRAY_OBJECT_BUFFER_ATI 0x8766
+#define LOCAL_GL_ARRAY_OBJECT_OFFSET_ATI 0x8767
+#define LOCAL_GL_ARRAY_SIZE 0x92FB
+#define LOCAL_GL_ARRAY_STRIDE 0x92FE
+#define LOCAL_GL_ASYNC_DRAW_PIXELS_SGIX 0x835D
+#define LOCAL_GL_ASYNC_HISTOGRAM_SGIX 0x832C
+#define LOCAL_GL_ASYNC_MARKER_SGIX 0x8329
+#define LOCAL_GL_ASYNC_READ_PIXELS_SGIX 0x835E
+#define LOCAL_GL_ASYNC_TEX_IMAGE_SGIX 0x835C
+#define LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
+#define LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#define LOCAL_GL_ATC_RGB_AMD 0x8C92
+#define LOCAL_GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000
+#define LOCAL_GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER 0x92C0
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3
+#define LOCAL_GL_ATOMIC_COUNTER_BUFFER_START 0x92C2
+#define LOCAL_GL_ATTACHED_SHADERS 0x8B85
+#define LOCAL_GL_ATTENUATION_EXT 0x834D
+#define LOCAL_GL_ATTRIB_ARRAY_POINTER_NV 0x8645
+#define LOCAL_GL_ATTRIB_ARRAY_SIZE_NV 0x8623
+#define LOCAL_GL_ATTRIB_ARRAY_STRIDE_NV 0x8624
+#define LOCAL_GL_ATTRIB_ARRAY_TYPE_NV 0x8625
+#define LOCAL_GL_ATTRIB_STACK_DEPTH 0x0BB0
+#define LOCAL_GL_AUTO_GENERATE_MIPMAP 0x8295
+#define LOCAL_GL_AUTO_NORMAL 0x0D80
+#define LOCAL_GL_AUX0 0x0409
+#define LOCAL_GL_AUX1 0x040A
+#define LOCAL_GL_AUX2 0x040B
+#define LOCAL_GL_AUX3 0x040C
+#define LOCAL_GL_AUX_BUFFERS 0x0C00
+#define LOCAL_GL_AUX_DEPTH_STENCIL_APPLE 0x8A14
+#define LOCAL_GL_AVERAGE_EXT 0x8335
+#define LOCAL_GL_AVERAGE_HP 0x8160
+#define LOCAL_GL_BACK 0x0405
+#define LOCAL_GL_BACK_LEFT 0x0402
+#define LOCAL_GL_BACK_NORMALS_HINT_PGI 0x1A223
+#define LOCAL_GL_BACK_PRIMARY_COLOR_NV 0x8C77
+#define LOCAL_GL_BACK_RIGHT 0x0403
+#define LOCAL_GL_BACK_SECONDARY_COLOR_NV 0x8C78
+#define LOCAL_GL_BEVEL_NV 0x90A6
+#define LOCAL_GL_BGR 0x80E0
+#define LOCAL_GL_BGRA 0x80E1
+#define LOCAL_GL_BGRA8_EXT 0x93A1
+#define LOCAL_GL_BGRA_EXT 0x80E1
+#define LOCAL_GL_BGRA_IMG 0x80E1
+#define LOCAL_GL_BGRA_INTEGER 0x8D9B
+#define LOCAL_GL_BGRA_INTEGER_EXT 0x8D9B
+#define LOCAL_GL_BGR_EXT 0x80E0
+#define LOCAL_GL_BGR_INTEGER 0x8D9A
+#define LOCAL_GL_BGR_INTEGER_EXT 0x8D9A
+#define LOCAL_GL_BIAS_BIT_ATI 0x00000008
+#define LOCAL_GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541
+#define LOCAL_GL_BINNING_CONTROL_HINT_QCOM 0x8FB0
+#define LOCAL_GL_BINORMAL_ARRAY_EXT 0x843A
+#define LOCAL_GL_BINORMAL_ARRAY_POINTER_EXT 0x8443
+#define LOCAL_GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441
+#define LOCAL_GL_BINORMAL_ARRAY_TYPE_EXT 0x8440
+#define LOCAL_GL_BITMAP 0x1A00
+#define LOCAL_GL_BITMAP_TOKEN 0x0704
+#define LOCAL_GL_BLEND 0x0BE2
+#define LOCAL_GL_BLEND_ADVANCED_COHERENT_NV 0x9285
+#define LOCAL_GL_BLEND_COLOR 0x8005
+#define LOCAL_GL_BLEND_COLOR_EXT 0x8005
+#define LOCAL_GL_BLEND_DST 0x0BE0
+#define LOCAL_GL_BLEND_DST_ALPHA 0x80CA
+#define LOCAL_GL_BLEND_DST_ALPHA_EXT 0x80CA
+#define LOCAL_GL_BLEND_DST_ALPHA_OES 0x80CA
+#define LOCAL_GL_BLEND_DST_RGB 0x80C8
+#define LOCAL_GL_BLEND_DST_RGB_EXT 0x80C8
+#define LOCAL_GL_BLEND_DST_RGB_OES 0x80C8
+#define LOCAL_GL_BLEND_EQUATION 0x8009
+#define LOCAL_GL_BLEND_EQUATION_ALPHA 0x883D
+#define LOCAL_GL_BLEND_EQUATION_ALPHA_EXT 0x883D
+#define LOCAL_GL_BLEND_EQUATION_ALPHA_OES 0x883D
+#define LOCAL_GL_BLEND_EQUATION_EXT 0x8009
+#define LOCAL_GL_BLEND_EQUATION_OES 0x8009
+#define LOCAL_GL_BLEND_EQUATION_RGB 0x8009
+#define LOCAL_GL_BLEND_EQUATION_RGB_EXT 0x8009
+#define LOCAL_GL_BLEND_EQUATION_RGB_OES 0x8009
+#define LOCAL_GL_BLEND_OVERLAP_NV 0x9281
+#define LOCAL_GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280
+#define LOCAL_GL_BLEND_SRC 0x0BE1
+#define LOCAL_GL_BLEND_SRC_ALPHA 0x80CB
+#define LOCAL_GL_BLEND_SRC_ALPHA_EXT 0x80CB
+#define LOCAL_GL_BLEND_SRC_ALPHA_OES 0x80CB
+#define LOCAL_GL_BLEND_SRC_RGB 0x80C9
+#define LOCAL_GL_BLEND_SRC_RGB_EXT 0x80C9
+#define LOCAL_GL_BLEND_SRC_RGB_OES 0x80C9
+#define LOCAL_GL_BLOCK_INDEX 0x92FD
+#define LOCAL_GL_BLUE 0x1905
+#define LOCAL_GL_BLUE_BIAS 0x0D1B
+#define LOCAL_GL_BLUE_BITS 0x0D54
+#define LOCAL_GL_BLUE_BIT_ATI 0x00000004
+#define LOCAL_GL_BLUE_INTEGER 0x8D96
+#define LOCAL_GL_BLUE_INTEGER_EXT 0x8D96
+#define LOCAL_GL_BLUE_MAX_CLAMP_INGR 0x8566
+#define LOCAL_GL_BLUE_MIN_CLAMP_INGR 0x8562
+#define LOCAL_GL_BLUE_NV 0x1905
+#define LOCAL_GL_BLUE_SCALE 0x0D1A
+#define LOCAL_GL_BOLD_BIT_NV 0x01
+#define LOCAL_GL_BOOL 0x8B56
+#define LOCAL_GL_BOOL_ARB 0x8B56
+#define LOCAL_GL_BOOL_VEC2 0x8B57
+#define LOCAL_GL_BOOL_VEC2_ARB 0x8B57
+#define LOCAL_GL_BOOL_VEC3 0x8B58
+#define LOCAL_GL_BOOL_VEC3_ARB 0x8B58
+#define LOCAL_GL_BOOL_VEC4 0x8B59
+#define LOCAL_GL_BOOL_VEC4_ARB 0x8B59
+#define LOCAL_GL_BOUNDING_BOX_NV 0x908D
+#define LOCAL_GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C
+#define LOCAL_GL_BUFFER 0x82E0
+#define LOCAL_GL_BUFFER_ACCESS 0x88BB
+#define LOCAL_GL_BUFFER_ACCESS_ARB 0x88BB
+#define LOCAL_GL_BUFFER_ACCESS_FLAGS 0x911F
+#define LOCAL_GL_BUFFER_ACCESS_OES 0x88BB
+#define LOCAL_GL_BUFFER_BINDING 0x9302
+#define LOCAL_GL_BUFFER_DATA_SIZE 0x9303
+#define LOCAL_GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13
+#define LOCAL_GL_BUFFER_GPU_ADDRESS_NV 0x8F1D
+#define LOCAL_GL_BUFFER_IMMUTABLE_STORAGE 0x821F
+#define LOCAL_GL_BUFFER_KHR 0x82E0
+#define LOCAL_GL_BUFFER_MAPPED 0x88BC
+#define LOCAL_GL_BUFFER_MAPPED_ARB 0x88BC
+#define LOCAL_GL_BUFFER_MAPPED_OES 0x88BC
+#define LOCAL_GL_BUFFER_MAP_LENGTH 0x9120
+#define LOCAL_GL_BUFFER_MAP_OFFSET 0x9121
+#define LOCAL_GL_BUFFER_MAP_POINTER 0x88BD
+#define LOCAL_GL_BUFFER_MAP_POINTER_ARB 0x88BD
+#define LOCAL_GL_BUFFER_MAP_POINTER_OES 0x88BD
+#define LOCAL_GL_BUFFER_OBJECT_APPLE 0x85B3
+#define LOCAL_GL_BUFFER_OBJECT_EXT 0x9151
+#define LOCAL_GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12
+#define LOCAL_GL_BUFFER_SIZE 0x8764
+#define LOCAL_GL_BUFFER_SIZE_ARB 0x8764
+#define LOCAL_GL_BUFFER_STORAGE_FLAGS 0x8220
+#define LOCAL_GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
+#define LOCAL_GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200
+#define LOCAL_GL_BUFFER_USAGE 0x8765
+#define LOCAL_GL_BUFFER_USAGE_ARB 0x8765
+#define LOCAL_GL_BUFFER_VARIABLE 0x92E5
+#define LOCAL_GL_BUMP_ENVMAP_ATI 0x877B
+#define LOCAL_GL_BUMP_NUM_TEX_UNITS_ATI 0x8777
+#define LOCAL_GL_BUMP_ROT_MATRIX_ATI 0x8775
+#define LOCAL_GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776
+#define LOCAL_GL_BUMP_TARGET_ATI 0x877C
+#define LOCAL_GL_BUMP_TEX_UNITS_ATI 0x8778
+#define LOCAL_GL_BYTE 0x1400
+#define LOCAL_GL_C3F_V3F 0x2A24
+#define LOCAL_GL_C4F_N3F_V3F 0x2A26
+#define LOCAL_GL_C4UB_V2F 0x2A22
+#define LOCAL_GL_C4UB_V3F 0x2A23
+#define LOCAL_GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183
+#define LOCAL_GL_CAVEAT_SUPPORT 0x82B8
+#define LOCAL_GL_CCW 0x0901
+#define LOCAL_GL_CIRCULAR_CCW_ARC_TO_NV 0xF8
+#define LOCAL_GL_CIRCULAR_CW_ARC_TO_NV 0xFA
+#define LOCAL_GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC
+#define LOCAL_GL_CLAMP 0x2900
+#define LOCAL_GL_CLAMP_FRAGMENT_COLOR 0x891B
+#define LOCAL_GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B
+#define LOCAL_GL_CLAMP_READ_COLOR 0x891C
+#define LOCAL_GL_CLAMP_READ_COLOR_ARB 0x891C
+#define LOCAL_GL_CLAMP_TO_BORDER 0x812D
+#define LOCAL_GL_CLAMP_TO_BORDER_ARB 0x812D
+#define LOCAL_GL_CLAMP_TO_BORDER_NV 0x812D
+#define LOCAL_GL_CLAMP_TO_BORDER_SGIS 0x812D
+#define LOCAL_GL_CLAMP_TO_EDGE 0x812F
+#define LOCAL_GL_CLAMP_TO_EDGE_SGIS 0x812F
+#define LOCAL_GL_CLAMP_VERTEX_COLOR 0x891A
+#define LOCAL_GL_CLAMP_VERTEX_COLOR_ARB 0x891A
+#define LOCAL_GL_CLEAR 0x1500
+#define LOCAL_GL_CLEAR_BUFFER 0x82B4
+#define LOCAL_GL_CLEAR_TEXTURE 0x9365
+#define LOCAL_GL_CLIENT_ACTIVE_TEXTURE 0x84E1
+#define LOCAL_GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1
+#define LOCAL_GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF
+#define LOCAL_GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1
+#define LOCAL_GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000
+#define LOCAL_GL_CLIENT_PIXEL_STORE_BIT 0x00000001
+#define LOCAL_GL_CLIENT_STORAGE_BIT 0x0200
+#define LOCAL_GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002
+#define LOCAL_GL_CLIP_DISTANCE0 0x3000
+#define LOCAL_GL_CLIP_DISTANCE1 0x3001
+#define LOCAL_GL_CLIP_DISTANCE2 0x3002
+#define LOCAL_GL_CLIP_DISTANCE3 0x3003
+#define LOCAL_GL_CLIP_DISTANCE4 0x3004
+#define LOCAL_GL_CLIP_DISTANCE5 0x3005
+#define LOCAL_GL_CLIP_DISTANCE6 0x3006
+#define LOCAL_GL_CLIP_DISTANCE7 0x3007
+#define LOCAL_GL_CLIP_DISTANCE_NV 0x8C7A
+#define LOCAL_GL_CLIP_FAR_HINT_PGI 0x1A221
+#define LOCAL_GL_CLIP_NEAR_HINT_PGI 0x1A220
+#define LOCAL_GL_CLIP_PLANE0 0x3000
+#define LOCAL_GL_CLIP_PLANE0_IMG 0x3000
+#define LOCAL_GL_CLIP_PLANE1 0x3001
+#define LOCAL_GL_CLIP_PLANE1_IMG 0x3001
+#define LOCAL_GL_CLIP_PLANE2 0x3002
+#define LOCAL_GL_CLIP_PLANE2_IMG 0x3002
+#define LOCAL_GL_CLIP_PLANE3 0x3003
+#define LOCAL_GL_CLIP_PLANE3_IMG 0x3003
+#define LOCAL_GL_CLIP_PLANE4 0x3004
+#define LOCAL_GL_CLIP_PLANE4_IMG 0x3004
+#define LOCAL_GL_CLIP_PLANE5 0x3005
+#define LOCAL_GL_CLIP_PLANE5_IMG 0x3005
+#define LOCAL_GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0
+#define LOCAL_GL_CLOSE_PATH_NV 0x00
+#define LOCAL_GL_CMYKA_EXT 0x800D
+#define LOCAL_GL_CMYK_EXT 0x800C
+#define LOCAL_GL_CND0_ATI 0x896B
+#define LOCAL_GL_CND_ATI 0x896A
+#define LOCAL_GL_COEFF 0x0A00
+#define LOCAL_GL_COLOR 0x1800
+#define LOCAL_GL_COLOR3_BIT_PGI 0x00010000
+#define LOCAL_GL_COLOR4_BIT_PGI 0x00020000
+#define LOCAL_GL_COLORBURN_NV 0x929A
+#define LOCAL_GL_COLORDODGE_NV 0x9299
+#define LOCAL_GL_COLOR_ALPHA_PAIRING_ATI 0x8975
+#define LOCAL_GL_COLOR_ARRAY 0x8076
+#define LOCAL_GL_COLOR_ARRAY_ADDRESS_NV 0x8F23
+#define LOCAL_GL_COLOR_ARRAY_BUFFER_BINDING 0x8898
+#define LOCAL_GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898
+#define LOCAL_GL_COLOR_ARRAY_COUNT_EXT 0x8084
+#define LOCAL_GL_COLOR_ARRAY_EXT 0x8076
+#define LOCAL_GL_COLOR_ARRAY_LENGTH_NV 0x8F2D
+#define LOCAL_GL_COLOR_ARRAY_LIST_IBM 103072
+#define LOCAL_GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082
+#define LOCAL_GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7
+#define LOCAL_GL_COLOR_ARRAY_POINTER 0x8090
+#define LOCAL_GL_COLOR_ARRAY_POINTER_EXT 0x8090
+#define LOCAL_GL_COLOR_ARRAY_SIZE 0x8081
+#define LOCAL_GL_COLOR_ARRAY_SIZE_EXT 0x8081
+#define LOCAL_GL_COLOR_ARRAY_STRIDE 0x8083
+#define LOCAL_GL_COLOR_ARRAY_STRIDE_EXT 0x8083
+#define LOCAL_GL_COLOR_ARRAY_TYPE 0x8082
+#define LOCAL_GL_COLOR_ARRAY_TYPE_EXT 0x8082
+#define LOCAL_GL_COLOR_ATTACHMENT0 0x8CE0
+#define LOCAL_GL_COLOR_ATTACHMENT0_EXT 0x8CE0
+#define LOCAL_GL_COLOR_ATTACHMENT0_NV 0x8CE0
+#define LOCAL_GL_COLOR_ATTACHMENT0_OES 0x8CE0
+#define LOCAL_GL_COLOR_ATTACHMENT1 0x8CE1
+#define LOCAL_GL_COLOR_ATTACHMENT10 0x8CEA
+#define LOCAL_GL_COLOR_ATTACHMENT10_EXT 0x8CEA
+#define LOCAL_GL_COLOR_ATTACHMENT10_NV 0x8CEA
+#define LOCAL_GL_COLOR_ATTACHMENT11 0x8CEB
+#define LOCAL_GL_COLOR_ATTACHMENT11_EXT 0x8CEB
+#define LOCAL_GL_COLOR_ATTACHMENT11_NV 0x8CEB
+#define LOCAL_GL_COLOR_ATTACHMENT12 0x8CEC
+#define LOCAL_GL_COLOR_ATTACHMENT12_EXT 0x8CEC
+#define LOCAL_GL_COLOR_ATTACHMENT12_NV 0x8CEC
+#define LOCAL_GL_COLOR_ATTACHMENT13 0x8CED
+#define LOCAL_GL_COLOR_ATTACHMENT13_EXT 0x8CED
+#define LOCAL_GL_COLOR_ATTACHMENT13_NV 0x8CED
+#define LOCAL_GL_COLOR_ATTACHMENT14 0x8CEE
+#define LOCAL_GL_COLOR_ATTACHMENT14_EXT 0x8CEE
+#define LOCAL_GL_COLOR_ATTACHMENT14_NV 0x8CEE
+#define LOCAL_GL_COLOR_ATTACHMENT15 0x8CEF
+#define LOCAL_GL_COLOR_ATTACHMENT15_EXT 0x8CEF
+#define LOCAL_GL_COLOR_ATTACHMENT15_NV 0x8CEF
+#define LOCAL_GL_COLOR_ATTACHMENT1_EXT 0x8CE1
+#define LOCAL_GL_COLOR_ATTACHMENT1_NV 0x8CE1
+#define LOCAL_GL_COLOR_ATTACHMENT2 0x8CE2
+#define LOCAL_GL_COLOR_ATTACHMENT2_EXT 0x8CE2
+#define LOCAL_GL_COLOR_ATTACHMENT2_NV 0x8CE2
+#define LOCAL_GL_COLOR_ATTACHMENT3 0x8CE3
+#define LOCAL_GL_COLOR_ATTACHMENT3_EXT 0x8CE3
+#define LOCAL_GL_COLOR_ATTACHMENT3_NV 0x8CE3
+#define LOCAL_GL_COLOR_ATTACHMENT4 0x8CE4
+#define LOCAL_GL_COLOR_ATTACHMENT4_EXT 0x8CE4
+#define LOCAL_GL_COLOR_ATTACHMENT4_NV 0x8CE4
+#define LOCAL_GL_COLOR_ATTACHMENT5 0x8CE5
+#define LOCAL_GL_COLOR_ATTACHMENT5_EXT 0x8CE5
+#define LOCAL_GL_COLOR_ATTACHMENT5_NV 0x8CE5
+#define LOCAL_GL_COLOR_ATTACHMENT6 0x8CE6
+#define LOCAL_GL_COLOR_ATTACHMENT6_EXT 0x8CE6
+#define LOCAL_GL_COLOR_ATTACHMENT6_NV 0x8CE6
+#define LOCAL_GL_COLOR_ATTACHMENT7 0x8CE7
+#define LOCAL_GL_COLOR_ATTACHMENT7_EXT 0x8CE7
+#define LOCAL_GL_COLOR_ATTACHMENT7_NV 0x8CE7
+#define LOCAL_GL_COLOR_ATTACHMENT8 0x8CE8
+#define LOCAL_GL_COLOR_ATTACHMENT8_EXT 0x8CE8
+#define LOCAL_GL_COLOR_ATTACHMENT8_NV 0x8CE8
+#define LOCAL_GL_COLOR_ATTACHMENT9 0x8CE9
+#define LOCAL_GL_COLOR_ATTACHMENT9_EXT 0x8CE9
+#define LOCAL_GL_COLOR_ATTACHMENT9_NV 0x8CE9
+#define LOCAL_GL_COLOR_ATTACHMENT_EXT 0x90F0
+#define LOCAL_GL_COLOR_BUFFER_BIT 0x00004000
+#define LOCAL_GL_COLOR_BUFFER_BIT0_QCOM 0x00000001
+#define LOCAL_GL_COLOR_BUFFER_BIT1_QCOM 0x00000002
+#define LOCAL_GL_COLOR_BUFFER_BIT2_QCOM 0x00000004
+#define LOCAL_GL_COLOR_BUFFER_BIT3_QCOM 0x00000008
+#define LOCAL_GL_COLOR_BUFFER_BIT4_QCOM 0x00000010
+#define LOCAL_GL_COLOR_BUFFER_BIT5_QCOM 0x00000020
+#define LOCAL_GL_COLOR_BUFFER_BIT6_QCOM 0x00000040
+#define LOCAL_GL_COLOR_BUFFER_BIT7_QCOM 0x00000080
+#define LOCAL_GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835
+#define LOCAL_GL_COLOR_CLEAR_VALUE 0x0C22
+#define LOCAL_GL_COLOR_COMPONENTS 0x8283
+#define LOCAL_GL_COLOR_ENCODING 0x8296
+#define LOCAL_GL_COLOR_EXT 0x1800
+#define LOCAL_GL_COLOR_FLOAT_APPLE 0x8A0F
+#define LOCAL_GL_COLOR_INDEX 0x1900
+#define LOCAL_GL_COLOR_INDEX12_EXT 0x80E6
+#define LOCAL_GL_COLOR_INDEX16_EXT 0x80E7
+#define LOCAL_GL_COLOR_INDEX1_EXT 0x80E2
+#define LOCAL_GL_COLOR_INDEX2_EXT 0x80E3
+#define LOCAL_GL_COLOR_INDEX4_EXT 0x80E4
+#define LOCAL_GL_COLOR_INDEX8_EXT 0x80E5
+#define LOCAL_GL_COLOR_INDEXES 0x1603
+#define LOCAL_GL_COLOR_LOGIC_OP 0x0BF2
+#define LOCAL_GL_COLOR_MATERIAL 0x0B57
+#define LOCAL_GL_COLOR_MATERIAL_FACE 0x0B55
+#define LOCAL_GL_COLOR_MATERIAL_PARAMETER 0x0B56
+#define LOCAL_GL_COLOR_MATRIX 0x80B1
+#define LOCAL_GL_COLOR_MATRIX_SGI 0x80B1
+#define LOCAL_GL_COLOR_MATRIX_STACK_DEPTH 0x80B2
+#define LOCAL_GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2
+#define LOCAL_GL_COLOR_RENDERABLE 0x8286
+#define LOCAL_GL_COLOR_SAMPLES_NV 0x8E20
+#define LOCAL_GL_COLOR_SUM 0x8458
+#define LOCAL_GL_COLOR_SUM_ARB 0x8458
+#define LOCAL_GL_COLOR_SUM_CLAMP_NV 0x854F
+#define LOCAL_GL_COLOR_SUM_EXT 0x8458
+#define LOCAL_GL_COLOR_TABLE 0x80D0
+#define LOCAL_GL_COLOR_TABLE_ALPHA_SIZE 0x80DD
+#define LOCAL_GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD
+#define LOCAL_GL_COLOR_TABLE_BIAS 0x80D7
+#define LOCAL_GL_COLOR_TABLE_BIAS_SGI 0x80D7
+#define LOCAL_GL_COLOR_TABLE_BLUE_SIZE 0x80DC
+#define LOCAL_GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC
+#define LOCAL_GL_COLOR_TABLE_FORMAT 0x80D8
+#define LOCAL_GL_COLOR_TABLE_FORMAT_SGI 0x80D8
+#define LOCAL_GL_COLOR_TABLE_GREEN_SIZE 0x80DB
+#define LOCAL_GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB
+#define LOCAL_GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF
+#define LOCAL_GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF
+#define LOCAL_GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE
+#define LOCAL_GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE
+#define LOCAL_GL_COLOR_TABLE_RED_SIZE 0x80DA
+#define LOCAL_GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA
+#define LOCAL_GL_COLOR_TABLE_SCALE 0x80D6
+#define LOCAL_GL_COLOR_TABLE_SCALE_SGI 0x80D6
+#define LOCAL_GL_COLOR_TABLE_SGI 0x80D0
+#define LOCAL_GL_COLOR_TABLE_WIDTH 0x80D9
+#define LOCAL_GL_COLOR_TABLE_WIDTH_SGI 0x80D9
+#define LOCAL_GL_COLOR_WRITEMASK 0x0C23
+#define LOCAL_GL_COMBINE 0x8570
+#define LOCAL_GL_COMBINE4_NV 0x8503
+#define LOCAL_GL_COMBINER0_NV 0x8550
+#define LOCAL_GL_COMBINER1_NV 0x8551
+#define LOCAL_GL_COMBINER2_NV 0x8552
+#define LOCAL_GL_COMBINER3_NV 0x8553
+#define LOCAL_GL_COMBINER4_NV 0x8554
+#define LOCAL_GL_COMBINER5_NV 0x8555
+#define LOCAL_GL_COMBINER6_NV 0x8556
+#define LOCAL_GL_COMBINER7_NV 0x8557
+#define LOCAL_GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545
+#define LOCAL_GL_COMBINER_AB_OUTPUT_NV 0x854A
+#define LOCAL_GL_COMBINER_BIAS_NV 0x8549
+#define LOCAL_GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546
+#define LOCAL_GL_COMBINER_CD_OUTPUT_NV 0x854B
+#define LOCAL_GL_COMBINER_COMPONENT_USAGE_NV 0x8544
+#define LOCAL_GL_COMBINER_INPUT_NV 0x8542
+#define LOCAL_GL_COMBINER_MAPPING_NV 0x8543
+#define LOCAL_GL_COMBINER_MUX_SUM_NV 0x8547
+#define LOCAL_GL_COMBINER_SCALE_NV 0x8548
+#define LOCAL_GL_COMBINER_SUM_OUTPUT_NV 0x854C
+#define LOCAL_GL_COMBINE_ALPHA 0x8572
+#define LOCAL_GL_COMBINE_ALPHA_ARB 0x8572
+#define LOCAL_GL_COMBINE_ALPHA_EXT 0x8572
+#define LOCAL_GL_COMBINE_ARB 0x8570
+#define LOCAL_GL_COMBINE_EXT 0x8570
+#define LOCAL_GL_COMBINE_RGB 0x8571
+#define LOCAL_GL_COMBINE_RGB_ARB 0x8571
+#define LOCAL_GL_COMBINE_RGB_EXT 0x8571
+#define LOCAL_GL_COMMAND_BARRIER_BIT 0x00000040
+#define LOCAL_GL_COMMAND_BARRIER_BIT_EXT 0x00000040
+#define LOCAL_GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E
+#define LOCAL_GL_COMPARE_REF_TO_TEXTURE 0x884E
+#define LOCAL_GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E
+#define LOCAL_GL_COMPARE_R_TO_TEXTURE 0x884E
+#define LOCAL_GL_COMPARE_R_TO_TEXTURE_ARB 0x884E
+#define LOCAL_GL_COMPATIBLE_SUBROUTINES 0x8E4B
+#define LOCAL_GL_COMPILE 0x1300
+#define LOCAL_GL_COMPILE_AND_EXECUTE 0x1301
+#define LOCAL_GL_COMPILE_STATUS 0x8B81
+#define LOCAL_GL_COMPRESSED_ALPHA 0x84E9
+#define LOCAL_GL_COMPRESSED_ALPHA_ARB 0x84E9
+#define LOCAL_GL_COMPRESSED_INTENSITY 0x84EC
+#define LOCAL_GL_COMPRESSED_INTENSITY_ARB 0x84EC
+#define LOCAL_GL_COMPRESSED_LUMINANCE 0x84EA
+#define LOCAL_GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB
+#define LOCAL_GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI 0x8837
+#define LOCAL_GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB
+#define LOCAL_GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72
+#define LOCAL_GL_COMPRESSED_LUMINANCE_ARB 0x84EA
+#define LOCAL_GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70
+#define LOCAL_GL_COMPRESSED_R11_EAC 0x9270
+#define LOCAL_GL_COMPRESSED_R11_EAC_OES 0x9270
+#define LOCAL_GL_COMPRESSED_RED 0x8225
+#define LOCAL_GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
+#define LOCAL_GL_COMPRESSED_RED_RGTC1 0x8DBB
+#define LOCAL_GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
+#define LOCAL_GL_COMPRESSED_RG 0x8226
+#define LOCAL_GL_COMPRESSED_RG11_EAC 0x9272
+#define LOCAL_GL_COMPRESSED_RG11_EAC_OES 0x9272
+#define LOCAL_GL_COMPRESSED_RGB 0x84ED
+#define LOCAL_GL_COMPRESSED_RGB8_ETC2 0x9274
+#define LOCAL_GL_COMPRESSED_RGB8_ETC2_OES 0x9274
+#define LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
+#define LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES 0x9276
+#define LOCAL_GL_COMPRESSED_RGBA 0x84EE
+#define LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+#define LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC_OES 0x9278
+#define LOCAL_GL_COMPRESSED_RGBA_ARB 0x84EE
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6
+#define LOCAL_GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7
+#define LOCAL_GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
+#define LOCAL_GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
+#define LOCAL_GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG 0x9137
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG 0x9138
+#define LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE 0x83F2
+#define LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE 0x83F3
+#define LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#define LOCAL_GL_COMPRESSED_RGB_ARB 0x84ED
+#define LOCAL_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
+#define LOCAL_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
+#define LOCAL_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
+#define LOCAL_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
+#define LOCAL_GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#define LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#define LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#define LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define LOCAL_GL_COMPRESSED_RG_RGTC2 0x8DBD
+#define LOCAL_GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73
+#define LOCAL_GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71
+#define LOCAL_GL_COMPRESSED_SIGNED_R11_EAC 0x9271
+#define LOCAL_GL_COMPRESSED_SIGNED_R11_EAC_OES 0x9271
+#define LOCAL_GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
+#define LOCAL_GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
+#define LOCAL_GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
+#define LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
+#define LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC_OES 0x9273
+#define LOCAL_GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
+#define LOCAL_GL_COMPRESSED_SLUMINANCE 0x8C4A
+#define LOCAL_GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B
+#define LOCAL_GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B
+#define LOCAL_GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A
+#define LOCAL_GL_COMPRESSED_SRGB 0x8C48
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
+#define LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC_OES 0x9279
+#define LOCAL_GL_COMPRESSED_SRGB8_ETC2 0x9275
+#define LOCAL_GL_COMPRESSED_SRGB8_ETC2_OES 0x9275
+#define LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
+#define LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES 0x9277
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA 0x8C49
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV 0x8C4D
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV 0x8C4E
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
+#define LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV 0x8C4F
+#define LOCAL_GL_COMPRESSED_SRGB_EXT 0x8C48
+#define LOCAL_GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54
+#define LOCAL_GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55
+#define LOCAL_GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
+#define LOCAL_GL_COMPRESSED_SRGB_S3TC_DXT1_NV 0x8C4C
+#define LOCAL_GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define LOCAL_GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3
+#define LOCAL_GL_COMPUTE_PROGRAM_NV 0x90FB
+#define LOCAL_GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC
+#define LOCAL_GL_COMPUTE_SHADER 0x91B9
+#define LOCAL_GL_COMPUTE_SHADER_BIT 0x00000020
+#define LOCAL_GL_COMPUTE_SUBROUTINE 0x92ED
+#define LOCAL_GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3
+#define LOCAL_GL_COMPUTE_TEXTURE 0x82A0
+#define LOCAL_GL_COMPUTE_WORK_GROUP_SIZE 0x8267
+#define LOCAL_GL_COMP_BIT_ATI 0x00000002
+#define LOCAL_GL_CONDITION_SATISFIED 0x911C
+#define LOCAL_GL_CONDITION_SATISFIED_APPLE 0x911C
+#define LOCAL_GL_CONJOINT_NV 0x9284
+#define LOCAL_GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD
+#define LOCAL_GL_CONSTANT 0x8576
+#define LOCAL_GL_CONSTANT_ALPHA 0x8003
+#define LOCAL_GL_CONSTANT_ALPHA_EXT 0x8003
+#define LOCAL_GL_CONSTANT_ARB 0x8576
+#define LOCAL_GL_CONSTANT_ATTENUATION 0x1207
+#define LOCAL_GL_CONSTANT_BORDER 0x8151
+#define LOCAL_GL_CONSTANT_BORDER_HP 0x8151
+#define LOCAL_GL_CONSTANT_COLOR 0x8001
+#define LOCAL_GL_CONSTANT_COLOR0_NV 0x852A
+#define LOCAL_GL_CONSTANT_COLOR1_NV 0x852B
+#define LOCAL_GL_CONSTANT_COLOR_EXT 0x8001
+#define LOCAL_GL_CONSTANT_EXT 0x8576
+#define LOCAL_GL_CONST_EYE_NV 0x86E5
+#define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
+#define LOCAL_GL_CONTEXT_FLAGS 0x821E
+#define LOCAL_GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
+#define LOCAL_GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002
+#define LOCAL_GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001
+#define LOCAL_GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004
+#define LOCAL_GL_CONTEXT_PROFILE_MASK 0x9126
+#define LOCAL_GL_CONTEXT_ROBUST_ACCESS_EXT 0x90F3
+#define LOCAL_GL_CONTINUOUS_AMD 0x9007
+#define LOCAL_GL_CONTRAST_NV 0x92A1
+#define LOCAL_GL_CONVEX_HULL_NV 0x908B
+#define LOCAL_GL_CONVOLUTION_1D 0x8010
+#define LOCAL_GL_CONVOLUTION_1D_EXT 0x8010
+#define LOCAL_GL_CONVOLUTION_2D 0x8011
+#define LOCAL_GL_CONVOLUTION_2D_EXT 0x8011
+#define LOCAL_GL_CONVOLUTION_BORDER_COLOR 0x8154
+#define LOCAL_GL_CONVOLUTION_BORDER_COLOR_HP 0x8154
+#define LOCAL_GL_CONVOLUTION_BORDER_MODE 0x8013
+#define LOCAL_GL_CONVOLUTION_BORDER_MODE_EXT 0x8013
+#define LOCAL_GL_CONVOLUTION_FILTER_BIAS 0x8015
+#define LOCAL_GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015
+#define LOCAL_GL_CONVOLUTION_FILTER_SCALE 0x8014
+#define LOCAL_GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014
+#define LOCAL_GL_CONVOLUTION_FORMAT 0x8017
+#define LOCAL_GL_CONVOLUTION_FORMAT_EXT 0x8017
+#define LOCAL_GL_CONVOLUTION_HEIGHT 0x8019
+#define LOCAL_GL_CONVOLUTION_HEIGHT_EXT 0x8019
+#define LOCAL_GL_CONVOLUTION_HINT_SGIX 0x8316
+#define LOCAL_GL_CONVOLUTION_WIDTH 0x8018
+#define LOCAL_GL_CONVOLUTION_WIDTH_EXT 0x8018
+#define LOCAL_GL_CON_0_ATI 0x8941
+#define LOCAL_GL_CON_10_ATI 0x894B
+#define LOCAL_GL_CON_11_ATI 0x894C
+#define LOCAL_GL_CON_12_ATI 0x894D
+#define LOCAL_GL_CON_13_ATI 0x894E
+#define LOCAL_GL_CON_14_ATI 0x894F
+#define LOCAL_GL_CON_15_ATI 0x8950
+#define LOCAL_GL_CON_16_ATI 0x8951
+#define LOCAL_GL_CON_17_ATI 0x8952
+#define LOCAL_GL_CON_18_ATI 0x8953
+#define LOCAL_GL_CON_19_ATI 0x8954
+#define LOCAL_GL_CON_1_ATI 0x8942
+#define LOCAL_GL_CON_20_ATI 0x8955
+#define LOCAL_GL_CON_21_ATI 0x8956
+#define LOCAL_GL_CON_22_ATI 0x8957
+#define LOCAL_GL_CON_23_ATI 0x8958
+#define LOCAL_GL_CON_24_ATI 0x8959
+#define LOCAL_GL_CON_25_ATI 0x895A
+#define LOCAL_GL_CON_26_ATI 0x895B
+#define LOCAL_GL_CON_27_ATI 0x895C
+#define LOCAL_GL_CON_28_ATI 0x895D
+#define LOCAL_GL_CON_29_ATI 0x895E
+#define LOCAL_GL_CON_2_ATI 0x8943
+#define LOCAL_GL_CON_30_ATI 0x895F
+#define LOCAL_GL_CON_31_ATI 0x8960
+#define LOCAL_GL_CON_3_ATI 0x8944
+#define LOCAL_GL_CON_4_ATI 0x8945
+#define LOCAL_GL_CON_5_ATI 0x8946
+#define LOCAL_GL_CON_6_ATI 0x8947
+#define LOCAL_GL_CON_7_ATI 0x8948
+#define LOCAL_GL_CON_8_ATI 0x8949
+#define LOCAL_GL_CON_9_ATI 0x894A
+#define LOCAL_GL_COORD_REPLACE 0x8862
+#define LOCAL_GL_COORD_REPLACE_ARB 0x8862
+#define LOCAL_GL_COORD_REPLACE_NV 0x8862
+#define LOCAL_GL_COORD_REPLACE_OES 0x8862
+#define LOCAL_GL_COPY 0x1503
+#define LOCAL_GL_COPY_INVERTED 0x150C
+#define LOCAL_GL_COPY_PIXEL_TOKEN 0x0706
+#define LOCAL_GL_COPY_READ_BUFFER 0x8F36
+#define LOCAL_GL_COPY_READ_BUFFER_BINDING 0x8F36
+#define LOCAL_GL_COPY_READ_BUFFER_NV 0x8F36
+#define LOCAL_GL_COPY_WRITE_BUFFER 0x8F37
+#define LOCAL_GL_COPY_WRITE_BUFFER_BINDING 0x8F37
+#define LOCAL_GL_COPY_WRITE_BUFFER_NV 0x8F37
+#define LOCAL_GL_COUNTER_RANGE_AMD 0x8BC1
+#define LOCAL_GL_COUNTER_TYPE_AMD 0x8BC0
+#define LOCAL_GL_COUNT_DOWN_NV 0x9089
+#define LOCAL_GL_COUNT_UP_NV 0x9088
+#define LOCAL_GL_COVERAGE_ALL_FRAGMENTS_NV 0x8ED5
+#define LOCAL_GL_COVERAGE_ATTACHMENT_NV 0x8ED2
+#define LOCAL_GL_COVERAGE_AUTOMATIC_NV 0x8ED7
+#define LOCAL_GL_COVERAGE_BUFFERS_NV 0x8ED3
+#define LOCAL_GL_COVERAGE_BUFFER_BIT_NV 0x00008000
+#define LOCAL_GL_COVERAGE_COMPONENT4_NV 0x8ED1
+#define LOCAL_GL_COVERAGE_COMPONENT_NV 0x8ED0
+#define LOCAL_GL_COVERAGE_EDGE_FRAGMENTS_NV 0x8ED6
+#define LOCAL_GL_COVERAGE_SAMPLES_NV 0x8ED4
+#define LOCAL_GL_CPU_OPTIMIZED_QCOM 0x8FB1
+#define LOCAL_GL_CUBIC_CURVE_TO_NV 0x0C
+#define LOCAL_GL_CUBIC_EXT 0x8334
+#define LOCAL_GL_CUBIC_HP 0x815F
+#define LOCAL_GL_CULL_FACE 0x0B44
+#define LOCAL_GL_CULL_FACE_MODE 0x0B45
+#define LOCAL_GL_CULL_FRAGMENT_NV 0x86E7
+#define LOCAL_GL_CULL_MODES_NV 0x86E0
+#define LOCAL_GL_CULL_VERTEX_EXT 0x81AA
+#define LOCAL_GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB
+#define LOCAL_GL_CULL_VERTEX_IBM 103050
+#define LOCAL_GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC
+#define LOCAL_GL_CURRENT_ATTRIB_NV 0x8626
+#define LOCAL_GL_CURRENT_BINORMAL_EXT 0x843C
+#define LOCAL_GL_CURRENT_BIT 0x00000001
+#define LOCAL_GL_CURRENT_COLOR 0x0B00
+#define LOCAL_GL_CURRENT_FOG_COORD 0x8453
+#define LOCAL_GL_CURRENT_FOG_COORDINATE 0x8453
+#define LOCAL_GL_CURRENT_FOG_COORDINATE_EXT 0x8453
+#define LOCAL_GL_CURRENT_INDEX 0x0B01
+#define LOCAL_GL_CURRENT_MATRIX_ARB 0x8641
+#define LOCAL_GL_CURRENT_MATRIX_INDEX_ARB 0x8845
+#define LOCAL_GL_CURRENT_MATRIX_NV 0x8641
+#define LOCAL_GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640
+#define LOCAL_GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640
+#define LOCAL_GL_CURRENT_NORMAL 0x0B02
+#define LOCAL_GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865
+#define LOCAL_GL_CURRENT_PALETTE_MATRIX_ARB 0x8843
+#define LOCAL_GL_CURRENT_PALETTE_MATRIX_OES 0x8843
+#define LOCAL_GL_CURRENT_PROGRAM 0x8B8D
+#define LOCAL_GL_CURRENT_QUERY 0x8865
+#define LOCAL_GL_CURRENT_QUERY_ARB 0x8865
+#define LOCAL_GL_CURRENT_QUERY_EXT 0x8865
+#define LOCAL_GL_CURRENT_RASTER_COLOR 0x0B04
+#define LOCAL_GL_CURRENT_RASTER_DISTANCE 0x0B09
+#define LOCAL_GL_CURRENT_RASTER_INDEX 0x0B05
+#define LOCAL_GL_CURRENT_RASTER_NORMAL_SGIX 0x8406
+#define LOCAL_GL_CURRENT_RASTER_POSITION 0x0B07
+#define LOCAL_GL_CURRENT_RASTER_POSITION_VALID 0x0B08
+#define LOCAL_GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F
+#define LOCAL_GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06
+#define LOCAL_GL_CURRENT_SECONDARY_COLOR 0x8459
+#define LOCAL_GL_CURRENT_SECONDARY_COLOR_EXT 0x8459
+#define LOCAL_GL_CURRENT_TANGENT_EXT 0x843B
+#define LOCAL_GL_CURRENT_TEXTURE_COORDS 0x0B03
+#define LOCAL_GL_CURRENT_TIME_NV 0x8E28
+#define LOCAL_GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define LOCAL_GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626
+#define LOCAL_GL_CURRENT_VERTEX_EXT 0x87E2
+#define LOCAL_GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B
+#define LOCAL_GL_CURRENT_WEIGHT_ARB 0x86A8
+#define LOCAL_GL_CW 0x0900
+#define LOCAL_GL_DARKEN_NV 0x9297
+#define LOCAL_GL_DATA_BUFFER_AMD 0x9151
+#define LOCAL_GL_DEBUG_ASSERT_MESA 0x875B
+#define LOCAL_GL_DEBUG_CALLBACK_FUNCTION 0x8244
+#define LOCAL_GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
+#define LOCAL_GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244
+#define LOCAL_GL_DEBUG_CALLBACK_USER_PARAM 0x8245
+#define LOCAL_GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
+#define LOCAL_GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245
+#define LOCAL_GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149
+#define LOCAL_GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F
+#define LOCAL_GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B
+#define LOCAL_GL_DEBUG_CATEGORY_OTHER_AMD 0x9150
+#define LOCAL_GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D
+#define LOCAL_GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E
+#define LOCAL_GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C
+#define LOCAL_GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A
+#define LOCAL_GL_DEBUG_GROUP_STACK_DEPTH 0x826D
+#define LOCAL_GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D
+#define LOCAL_GL_DEBUG_LOGGED_MESSAGES 0x9145
+#define LOCAL_GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145
+#define LOCAL_GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145
+#define LOCAL_GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145
+#define LOCAL_GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243
+#define LOCAL_GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
+#define LOCAL_GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243
+#define LOCAL_GL_DEBUG_OBJECT_MESA 0x8759
+#define LOCAL_GL_DEBUG_OUTPUT 0x92E0
+#define LOCAL_GL_DEBUG_OUTPUT_KHR 0x92E0
+#define LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
+#define LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
+#define LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242
+#define LOCAL_GL_DEBUG_PRINT_MESA 0x875A
+#define LOCAL_GL_DEBUG_SEVERITY_HIGH 0x9146
+#define LOCAL_GL_DEBUG_SEVERITY_HIGH_AMD 0x9146
+#define LOCAL_GL_DEBUG_SEVERITY_HIGH_ARB 0x9146
+#define LOCAL_GL_DEBUG_SEVERITY_HIGH_KHR 0x9146
+#define LOCAL_GL_DEBUG_SEVERITY_LOW 0x9148
+#define LOCAL_GL_DEBUG_SEVERITY_LOW_AMD 0x9148
+#define LOCAL_GL_DEBUG_SEVERITY_LOW_ARB 0x9148
+#define LOCAL_GL_DEBUG_SEVERITY_LOW_KHR 0x9148
+#define LOCAL_GL_DEBUG_SEVERITY_MEDIUM 0x9147
+#define LOCAL_GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147
+#define LOCAL_GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
+#define LOCAL_GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147
+#define LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
+#define LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B
+#define LOCAL_GL_DEBUG_SOURCE_API 0x8246
+#define LOCAL_GL_DEBUG_SOURCE_API_ARB 0x8246
+#define LOCAL_GL_DEBUG_SOURCE_API_KHR 0x8246
+#define LOCAL_GL_DEBUG_SOURCE_APPLICATION 0x824A
+#define LOCAL_GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A
+#define LOCAL_GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A
+#define LOCAL_GL_DEBUG_SOURCE_OTHER 0x824B
+#define LOCAL_GL_DEBUG_SOURCE_OTHER_ARB 0x824B
+#define LOCAL_GL_DEBUG_SOURCE_OTHER_KHR 0x824B
+#define LOCAL_GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
+#define LOCAL_GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
+#define LOCAL_GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248
+#define LOCAL_GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
+#define LOCAL_GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
+#define LOCAL_GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249
+#define LOCAL_GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
+#define LOCAL_GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
+#define LOCAL_GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247
+#define LOCAL_GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
+#define LOCAL_GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
+#define LOCAL_GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D
+#define LOCAL_GL_DEBUG_TYPE_ERROR 0x824C
+#define LOCAL_GL_DEBUG_TYPE_ERROR_ARB 0x824C
+#define LOCAL_GL_DEBUG_TYPE_ERROR_KHR 0x824C
+#define LOCAL_GL_DEBUG_TYPE_MARKER 0x8268
+#define LOCAL_GL_DEBUG_TYPE_MARKER_KHR 0x8268
+#define LOCAL_GL_DEBUG_TYPE_OTHER 0x8251
+#define LOCAL_GL_DEBUG_TYPE_OTHER_ARB 0x8251
+#define LOCAL_GL_DEBUG_TYPE_OTHER_KHR 0x8251
+#define LOCAL_GL_DEBUG_TYPE_PERFORMANCE 0x8250
+#define LOCAL_GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
+#define LOCAL_GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250
+#define LOCAL_GL_DEBUG_TYPE_POP_GROUP 0x826A
+#define LOCAL_GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A
+#define LOCAL_GL_DEBUG_TYPE_PORTABILITY 0x824F
+#define LOCAL_GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F
+#define LOCAL_GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F
+#define LOCAL_GL_DEBUG_TYPE_PUSH_GROUP 0x8269
+#define LOCAL_GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269
+#define LOCAL_GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
+#define LOCAL_GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
+#define LOCAL_GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E
+#define LOCAL_GL_DECAL 0x2101
+#define LOCAL_GL_DECODE_EXT 0x8A49
+#define LOCAL_GL_DECR 0x1E03
+#define LOCAL_GL_DECR_WRAP 0x8508
+#define LOCAL_GL_DECR_WRAP_EXT 0x8508
+#define LOCAL_GL_DECR_WRAP_OES 0x8508
+#define LOCAL_GL_DEFORMATIONS_MASK_SGIX 0x8196
+#define LOCAL_GL_DELETE_STATUS 0x8B80
+#define LOCAL_GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9
+#define LOCAL_GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA
+#define LOCAL_GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858
+#define LOCAL_GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859
+#define LOCAL_GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A
+#define LOCAL_GL_DEPTH 0x1801
+#define LOCAL_GL_DEPTH24_STENCIL8 0x88F0
+#define LOCAL_GL_DEPTH24_STENCIL8_EXT 0x88F0
+#define LOCAL_GL_DEPTH24_STENCIL8_OES 0x88F0
+#define LOCAL_GL_DEPTH32F_STENCIL8 0x8CAD
+#define LOCAL_GL_DEPTH32F_STENCIL8_NV 0x8DAC
+#define LOCAL_GL_DEPTH_ATTACHMENT 0x8D00
+#define LOCAL_GL_DEPTH_ATTACHMENT_EXT 0x8D00
+#define LOCAL_GL_DEPTH_ATTACHMENT_OES 0x8D00
+#define LOCAL_GL_DEPTH_BIAS 0x0D1F
+#define LOCAL_GL_DEPTH_BITS 0x0D56
+#define LOCAL_GL_DEPTH_BOUNDS_EXT 0x8891
+#define LOCAL_GL_DEPTH_BOUNDS_TEST_EXT 0x8890
+#define LOCAL_GL_DEPTH_BUFFER_BIT 0x00000100
+#define LOCAL_GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100
+#define LOCAL_GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200
+#define LOCAL_GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400
+#define LOCAL_GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800
+#define LOCAL_GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000
+#define LOCAL_GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000
+#define LOCAL_GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000
+#define LOCAL_GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000
+#define LOCAL_GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF
+#define LOCAL_GL_DEPTH_CLAMP 0x864F
+#define LOCAL_GL_DEPTH_CLAMP_FAR_AMD 0x901F
+#define LOCAL_GL_DEPTH_CLAMP_NEAR_AMD 0x901E
+#define LOCAL_GL_DEPTH_CLAMP_NV 0x864F
+#define LOCAL_GL_DEPTH_CLEAR_VALUE 0x0B73
+#define LOCAL_GL_DEPTH_COMPONENT 0x1902
+#define LOCAL_GL_DEPTH_COMPONENT16 0x81A5
+#define LOCAL_GL_DEPTH_COMPONENT16_ARB 0x81A5
+#define LOCAL_GL_DEPTH_COMPONENT16_NONLINEAR_NV 0x8E2C
+#define LOCAL_GL_DEPTH_COMPONENT16_OES 0x81A5
+#define LOCAL_GL_DEPTH_COMPONENT16_SGIX 0x81A5
+#define LOCAL_GL_DEPTH_COMPONENT24 0x81A6
+#define LOCAL_GL_DEPTH_COMPONENT24_ARB 0x81A6
+#define LOCAL_GL_DEPTH_COMPONENT24_OES 0x81A6
+#define LOCAL_GL_DEPTH_COMPONENT24_SGIX 0x81A6
+#define LOCAL_GL_DEPTH_COMPONENT32 0x81A7
+#define LOCAL_GL_DEPTH_COMPONENT32F 0x8CAC
+#define LOCAL_GL_DEPTH_COMPONENT32F_NV 0x8DAB
+#define LOCAL_GL_DEPTH_COMPONENT32_ARB 0x81A7
+#define LOCAL_GL_DEPTH_COMPONENT32_OES 0x81A7
+#define LOCAL_GL_DEPTH_COMPONENT32_SGIX 0x81A7
+#define LOCAL_GL_DEPTH_COMPONENTS 0x8284
+#define LOCAL_GL_DEPTH_EXT 0x1801
+#define LOCAL_GL_DEPTH_FUNC 0x0B74
+#define LOCAL_GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX 0x8311
+#define LOCAL_GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX 0x8312
+#define LOCAL_GL_DEPTH_PASS_INSTRUMENT_SGIX 0x8310
+#define LOCAL_GL_DEPTH_RANGE 0x0B70
+#define LOCAL_GL_DEPTH_RENDERABLE 0x8287
+#define LOCAL_GL_DEPTH_SCALE 0x0D1E
+#define LOCAL_GL_DEPTH_STENCIL 0x84F9
+#define LOCAL_GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#define LOCAL_GL_DEPTH_STENCIL_EXT 0x84F9
+#define LOCAL_GL_DEPTH_STENCIL_MESA 0x8750
+#define LOCAL_GL_DEPTH_STENCIL_NV 0x84F9
+#define LOCAL_GL_DEPTH_STENCIL_OES 0x84F9
+#define LOCAL_GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA
+#define LOCAL_GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F
+#define LOCAL_GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E
+#define LOCAL_GL_DEPTH_TEST 0x0B71
+#define LOCAL_GL_DEPTH_TEXTURE_MODE 0x884B
+#define LOCAL_GL_DEPTH_TEXTURE_MODE_ARB 0x884B
+#define LOCAL_GL_DEPTH_WRITEMASK 0x0B72
+#define LOCAL_GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096
+#define LOCAL_GL_DETAIL_TEXTURE_2D_SGIS 0x8095
+#define LOCAL_GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C
+#define LOCAL_GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A
+#define LOCAL_GL_DETAIL_TEXTURE_MODE_SGIS 0x809B
+#define LOCAL_GL_DIFFERENCE_NV 0x929E
+#define LOCAL_GL_DIFFUSE 0x1201
+#define LOCAL_GL_DISCARD_ATI 0x8763
+#define LOCAL_GL_DISCARD_NV 0x8530
+#define LOCAL_GL_DISCRETE_AMD 0x9006
+#define LOCAL_GL_DISJOINT_NV 0x9283
+#define LOCAL_GL_DISPATCH_INDIRECT_BUFFER 0x90EE
+#define LOCAL_GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF
+#define LOCAL_GL_DISPLAY_LIST 0x82E7
+#define LOCAL_GL_DISTANCE_ATTENUATION_EXT 0x8129
+#define LOCAL_GL_DISTANCE_ATTENUATION_SGIS 0x8129
+#define LOCAL_GL_DITHER 0x0BD0
+#define LOCAL_GL_DOMAIN 0x0A02
+#define LOCAL_GL_DONT_CARE 0x1100
+#define LOCAL_GL_DOT2_ADD_ATI 0x896C
+#define LOCAL_GL_DOT3_ATI 0x8966
+#define LOCAL_GL_DOT3_RGB 0x86AE
+#define LOCAL_GL_DOT3_RGBA 0x86AF
+#define LOCAL_GL_DOT3_RGBA_ARB 0x86AF
+#define LOCAL_GL_DOT3_RGBA_EXT 0x8741
+#define LOCAL_GL_DOT3_RGBA_IMG 0x86AF
+#define LOCAL_GL_DOT3_RGB_ARB 0x86AE
+#define LOCAL_GL_DOT3_RGB_EXT 0x8740
+#define LOCAL_GL_DOT4_ATI 0x8967
+#define LOCAL_GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D
+#define LOCAL_GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3
+#define LOCAL_GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED
+#define LOCAL_GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1
+#define LOCAL_GL_DOT_PRODUCT_NV 0x86EC
+#define LOCAL_GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B
+#define LOCAL_GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2
+#define LOCAL_GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C
+#define LOCAL_GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE
+#define LOCAL_GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF
+#define LOCAL_GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0
+#define LOCAL_GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E
+#define LOCAL_GL_DOUBLE 0x140A
+#define LOCAL_GL_DOUBLEBUFFER 0x0C32
+#define LOCAL_GL_DOUBLE_EXT 0x140A
+#define LOCAL_GL_DOUBLE_MAT2 0x8F46
+#define LOCAL_GL_DOUBLE_MAT2_EXT 0x8F46
+#define LOCAL_GL_DOUBLE_MAT2x3 0x8F49
+#define LOCAL_GL_DOUBLE_MAT2x3_EXT 0x8F49
+#define LOCAL_GL_DOUBLE_MAT2x4 0x8F4A
+#define LOCAL_GL_DOUBLE_MAT2x4_EXT 0x8F4A
+#define LOCAL_GL_DOUBLE_MAT3 0x8F47
+#define LOCAL_GL_DOUBLE_MAT3_EXT 0x8F47
+#define LOCAL_GL_DOUBLE_MAT3x2 0x8F4B
+#define LOCAL_GL_DOUBLE_MAT3x2_EXT 0x8F4B
+#define LOCAL_GL_DOUBLE_MAT3x4 0x8F4C
+#define LOCAL_GL_DOUBLE_MAT3x4_EXT 0x8F4C
+#define LOCAL_GL_DOUBLE_MAT4 0x8F48
+#define LOCAL_GL_DOUBLE_MAT4_EXT 0x8F48
+#define LOCAL_GL_DOUBLE_MAT4x2 0x8F4D
+#define LOCAL_GL_DOUBLE_MAT4x2_EXT 0x8F4D
+#define LOCAL_GL_DOUBLE_MAT4x3 0x8F4E
+#define LOCAL_GL_DOUBLE_MAT4x3_EXT 0x8F4E
+#define LOCAL_GL_DOUBLE_VEC2 0x8FFC
+#define LOCAL_GL_DOUBLE_VEC2_EXT 0x8FFC
+#define LOCAL_GL_DOUBLE_VEC3 0x8FFD
+#define LOCAL_GL_DOUBLE_VEC3_EXT 0x8FFD
+#define LOCAL_GL_DOUBLE_VEC4 0x8FFE
+#define LOCAL_GL_DOUBLE_VEC4_EXT 0x8FFE
+#define LOCAL_GL_DRAW_BUFFER 0x0C01
+#define LOCAL_GL_DRAW_BUFFER0 0x8825
+#define LOCAL_GL_DRAW_BUFFER0_ARB 0x8825
+#define LOCAL_GL_DRAW_BUFFER0_ATI 0x8825
+#define LOCAL_GL_DRAW_BUFFER0_EXT 0x8825
+#define LOCAL_GL_DRAW_BUFFER0_NV 0x8825
+#define LOCAL_GL_DRAW_BUFFER1 0x8826
+#define LOCAL_GL_DRAW_BUFFER10 0x882F
+#define LOCAL_GL_DRAW_BUFFER10_ARB 0x882F
+#define LOCAL_GL_DRAW_BUFFER10_ATI 0x882F
+#define LOCAL_GL_DRAW_BUFFER10_EXT 0x882F
+#define LOCAL_GL_DRAW_BUFFER10_NV 0x882F
+#define LOCAL_GL_DRAW_BUFFER11 0x8830
+#define LOCAL_GL_DRAW_BUFFER11_ARB 0x8830
+#define LOCAL_GL_DRAW_BUFFER11_ATI 0x8830
+#define LOCAL_GL_DRAW_BUFFER11_EXT 0x8830
+#define LOCAL_GL_DRAW_BUFFER11_NV 0x8830
+#define LOCAL_GL_DRAW_BUFFER12 0x8831
+#define LOCAL_GL_DRAW_BUFFER12_ARB 0x8831
+#define LOCAL_GL_DRAW_BUFFER12_ATI 0x8831
+#define LOCAL_GL_DRAW_BUFFER12_EXT 0x8831
+#define LOCAL_GL_DRAW_BUFFER12_NV 0x8831
+#define LOCAL_GL_DRAW_BUFFER13 0x8832
+#define LOCAL_GL_DRAW_BUFFER13_ARB 0x8832
+#define LOCAL_GL_DRAW_BUFFER13_ATI 0x8832
+#define LOCAL_GL_DRAW_BUFFER13_EXT 0x8832
+#define LOCAL_GL_DRAW_BUFFER13_NV 0x8832
+#define LOCAL_GL_DRAW_BUFFER14 0x8833
+#define LOCAL_GL_DRAW_BUFFER14_ARB 0x8833
+#define LOCAL_GL_DRAW_BUFFER14_ATI 0x8833
+#define LOCAL_GL_DRAW_BUFFER14_EXT 0x8833
+#define LOCAL_GL_DRAW_BUFFER14_NV 0x8833
+#define LOCAL_GL_DRAW_BUFFER15 0x8834
+#define LOCAL_GL_DRAW_BUFFER15_ARB 0x8834
+#define LOCAL_GL_DRAW_BUFFER15_ATI 0x8834
+#define LOCAL_GL_DRAW_BUFFER15_EXT 0x8834
+#define LOCAL_GL_DRAW_BUFFER15_NV 0x8834
+#define LOCAL_GL_DRAW_BUFFER1_ARB 0x8826
+#define LOCAL_GL_DRAW_BUFFER1_ATI 0x8826
+#define LOCAL_GL_DRAW_BUFFER1_EXT 0x8826
+#define LOCAL_GL_DRAW_BUFFER1_NV 0x8826
+#define LOCAL_GL_DRAW_BUFFER2 0x8827
+#define LOCAL_GL_DRAW_BUFFER2_ARB 0x8827
+#define LOCAL_GL_DRAW_BUFFER2_ATI 0x8827
+#define LOCAL_GL_DRAW_BUFFER2_EXT 0x8827
+#define LOCAL_GL_DRAW_BUFFER2_NV 0x8827
+#define LOCAL_GL_DRAW_BUFFER3 0x8828
+#define LOCAL_GL_DRAW_BUFFER3_ARB 0x8828
+#define LOCAL_GL_DRAW_BUFFER3_ATI 0x8828
+#define LOCAL_GL_DRAW_BUFFER3_EXT 0x8828
+#define LOCAL_GL_DRAW_BUFFER3_NV 0x8828
+#define LOCAL_GL_DRAW_BUFFER4 0x8829
+#define LOCAL_GL_DRAW_BUFFER4_ARB 0x8829
+#define LOCAL_GL_DRAW_BUFFER4_ATI 0x8829
+#define LOCAL_GL_DRAW_BUFFER4_EXT 0x8829
+#define LOCAL_GL_DRAW_BUFFER4_NV 0x8829
+#define LOCAL_GL_DRAW_BUFFER5 0x882A
+#define LOCAL_GL_DRAW_BUFFER5_ARB 0x882A
+#define LOCAL_GL_DRAW_BUFFER5_ATI 0x882A
+#define LOCAL_GL_DRAW_BUFFER5_EXT 0x882A
+#define LOCAL_GL_DRAW_BUFFER5_NV 0x882A
+#define LOCAL_GL_DRAW_BUFFER6 0x882B
+#define LOCAL_GL_DRAW_BUFFER6_ARB 0x882B
+#define LOCAL_GL_DRAW_BUFFER6_ATI 0x882B
+#define LOCAL_GL_DRAW_BUFFER6_EXT 0x882B
+#define LOCAL_GL_DRAW_BUFFER6_NV 0x882B
+#define LOCAL_GL_DRAW_BUFFER7 0x882C
+#define LOCAL_GL_DRAW_BUFFER7_ARB 0x882C
+#define LOCAL_GL_DRAW_BUFFER7_ATI 0x882C
+#define LOCAL_GL_DRAW_BUFFER7_EXT 0x882C
+#define LOCAL_GL_DRAW_BUFFER7_NV 0x882C
+#define LOCAL_GL_DRAW_BUFFER8 0x882D
+#define LOCAL_GL_DRAW_BUFFER8_ARB 0x882D
+#define LOCAL_GL_DRAW_BUFFER8_ATI 0x882D
+#define LOCAL_GL_DRAW_BUFFER8_EXT 0x882D
+#define LOCAL_GL_DRAW_BUFFER8_NV 0x882D
+#define LOCAL_GL_DRAW_BUFFER9 0x882E
+#define LOCAL_GL_DRAW_BUFFER9_ARB 0x882E
+#define LOCAL_GL_DRAW_BUFFER9_ATI 0x882E
+#define LOCAL_GL_DRAW_BUFFER9_EXT 0x882E
+#define LOCAL_GL_DRAW_BUFFER9_NV 0x882E
+#define LOCAL_GL_DRAW_BUFFER_EXT 0x0C01
+#define LOCAL_GL_DRAW_FRAMEBUFFER 0x8CA9
+#define LOCAL_GL_DRAW_FRAMEBUFFER_ANGLE 0x8CA9
+#define LOCAL_GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9
+#define LOCAL_GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
+#define LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_ANGLE 0x8CA6
+#define LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_APPLE 0x8CA6
+#define LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_NV 0x8CA6
+#define LOCAL_GL_DRAW_FRAMEBUFFER_EXT 0x8CA9
+#define LOCAL_GL_DRAW_FRAMEBUFFER_NV 0x8CA9
+#define LOCAL_GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41
+#define LOCAL_GL_DRAW_INDIRECT_BUFFER 0x8F3F
+#define LOCAL_GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43
+#define LOCAL_GL_DRAW_INDIRECT_LENGTH_NV 0x8F42
+#define LOCAL_GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40
+#define LOCAL_GL_DRAW_PIXELS_APPLE 0x8A0A
+#define LOCAL_GL_DRAW_PIXEL_TOKEN 0x0705
+#define LOCAL_GL_DSDT8_MAG8_INTENSITY8_NV 0x870B
+#define LOCAL_GL_DSDT8_MAG8_NV 0x870A
+#define LOCAL_GL_DSDT8_NV 0x8709
+#define LOCAL_GL_DSDT_MAG_INTENSITY_NV 0x86DC
+#define LOCAL_GL_DSDT_MAG_NV 0x86F6
+#define LOCAL_GL_DSDT_MAG_VIB_NV 0x86F7
+#define LOCAL_GL_DSDT_NV 0x86F5
+#define LOCAL_GL_DST_ALPHA 0x0304
+#define LOCAL_GL_DST_ATOP_NV 0x928F
+#define LOCAL_GL_DST_COLOR 0x0306
+#define LOCAL_GL_DST_IN_NV 0x928B
+#define LOCAL_GL_DST_NV 0x9287
+#define LOCAL_GL_DST_OUT_NV 0x928D
+#define LOCAL_GL_DST_OVER_NV 0x9289
+#define LOCAL_GL_DS_BIAS_NV 0x8716
+#define LOCAL_GL_DS_SCALE_NV 0x8710
+#define LOCAL_GL_DT_BIAS_NV 0x8717
+#define LOCAL_GL_DT_SCALE_NV 0x8711
+#define LOCAL_GL_DU8DV8_ATI 0x877A
+#define LOCAL_GL_DUAL_ALPHA12_SGIS 0x8112
+#define LOCAL_GL_DUAL_ALPHA16_SGIS 0x8113
+#define LOCAL_GL_DUAL_ALPHA4_SGIS 0x8110
+#define LOCAL_GL_DUAL_ALPHA8_SGIS 0x8111
+#define LOCAL_GL_DUAL_INTENSITY12_SGIS 0x811A
+#define LOCAL_GL_DUAL_INTENSITY16_SGIS 0x811B
+#define LOCAL_GL_DUAL_INTENSITY4_SGIS 0x8118
+#define LOCAL_GL_DUAL_INTENSITY8_SGIS 0x8119
+#define LOCAL_GL_DUAL_LUMINANCE12_SGIS 0x8116
+#define LOCAL_GL_DUAL_LUMINANCE16_SGIS 0x8117
+#define LOCAL_GL_DUAL_LUMINANCE4_SGIS 0x8114
+#define LOCAL_GL_DUAL_LUMINANCE8_SGIS 0x8115
+#define LOCAL_GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C
+#define LOCAL_GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D
+#define LOCAL_GL_DUAL_TEXTURE_SELECT_SGIS 0x8124
+#define LOCAL_GL_DUDV_ATI 0x8779
+#define LOCAL_GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2
+#define LOCAL_GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4
+#define LOCAL_GL_DYNAMIC_ATI 0x8761
+#define LOCAL_GL_DYNAMIC_COPY 0x88EA
+#define LOCAL_GL_DYNAMIC_COPY_ARB 0x88EA
+#define LOCAL_GL_DYNAMIC_DRAW 0x88E8
+#define LOCAL_GL_DYNAMIC_DRAW_ARB 0x88E8
+#define LOCAL_GL_DYNAMIC_READ 0x88E9
+#define LOCAL_GL_DYNAMIC_READ_ARB 0x88E9
+#define LOCAL_GL_DYNAMIC_STORAGE_BIT 0x0100
+#define LOCAL_GL_EDGEFLAG_BIT_PGI 0x00040000
+#define LOCAL_GL_EDGE_FLAG 0x0B43
+#define LOCAL_GL_EDGE_FLAG_ARRAY 0x8079
+#define LOCAL_GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26
+#define LOCAL_GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B
+#define LOCAL_GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B
+#define LOCAL_GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D
+#define LOCAL_GL_EDGE_FLAG_ARRAY_EXT 0x8079
+#define LOCAL_GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30
+#define LOCAL_GL_EDGE_FLAG_ARRAY_LIST_IBM 103075
+#define LOCAL_GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085
+#define LOCAL_GL_EDGE_FLAG_ARRAY_POINTER 0x8093
+#define LOCAL_GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093
+#define LOCAL_GL_EDGE_FLAG_ARRAY_STRIDE 0x808C
+#define LOCAL_GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C
+#define LOCAL_GL_EIGHTH_BIT_ATI 0x00000020
+#define LOCAL_GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29
+#define LOCAL_GL_ELEMENT_ARRAY_APPLE 0x8A0C
+#define LOCAL_GL_ELEMENT_ARRAY_ATI 0x8768
+#define LOCAL_GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
+#define LOCAL_GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002
+#define LOCAL_GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define LOCAL_GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893
+#define LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895
+#define LOCAL_GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33
+#define LOCAL_GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E
+#define LOCAL_GL_ELEMENT_ARRAY_POINTER_ATI 0x876A
+#define LOCAL_GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D
+#define LOCAL_GL_ELEMENT_ARRAY_TYPE_ATI 0x8769
+#define LOCAL_GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F
+#define LOCAL_GL_EMBOSS_CONSTANT_NV 0x855E
+#define LOCAL_GL_EMBOSS_LIGHT_NV 0x855D
+#define LOCAL_GL_EMBOSS_MAP_NV 0x855F
+#define LOCAL_GL_EMISSION 0x1600
+#define LOCAL_GL_ENABLE_BIT 0x00002000
+#define LOCAL_GL_EQUAL 0x0202
+#define LOCAL_GL_EQUIV 0x1509
+#define LOCAL_GL_ETC1_RGB8_OES 0x8D64
+#define LOCAL_GL_ETC1_SRGB8_NV 0x88EE
+#define LOCAL_GL_EVAL_2D_NV 0x86C0
+#define LOCAL_GL_EVAL_BIT 0x00010000
+#define LOCAL_GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5
+#define LOCAL_GL_EVAL_TRIANGULAR_2D_NV 0x86C1
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE
+#define LOCAL_GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF
+#define LOCAL_GL_EXCLUSION_NV 0x92A0
+#define LOCAL_GL_EXP 0x0800
+#define LOCAL_GL_EXP2 0x0801
+#define LOCAL_GL_EXPAND_NEGATE_NV 0x8539
+#define LOCAL_GL_EXPAND_NORMAL_NV 0x8538
+#define LOCAL_GL_EXTENSIONS 0x1F03
+#define LOCAL_GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160
+#define LOCAL_GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2
+#define LOCAL_GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0
+#define LOCAL_GL_EYE_LINEAR 0x2400
+#define LOCAL_GL_EYE_LINE_SGIS 0x81F6
+#define LOCAL_GL_EYE_PLANE 0x2502
+#define LOCAL_GL_EYE_PLANE_ABSOLUTE_NV 0x855C
+#define LOCAL_GL_EYE_POINT_SGIS 0x81F4
+#define LOCAL_GL_EYE_RADIAL_NV 0x855B
+#define LOCAL_GL_E_TIMES_F_NV 0x8531
+#define LOCAL_GL_FACTOR_ALPHA_MODULATE_IMG 0x8C07
+#define LOCAL_GL_FACTOR_MAX_AMD 0x901D
+#define LOCAL_GL_FACTOR_MIN_AMD 0x901C
+#define LOCAL_GL_FAILURE_NV 0x9030
+#define LOCAL_GL_FALSE 0
+#define LOCAL_GL_FASTEST 0x1101
+#define LOCAL_GL_FEEDBACK 0x1C01
+#define LOCAL_GL_FEEDBACK_BUFFER_POINTER 0x0DF0
+#define LOCAL_GL_FEEDBACK_BUFFER_SIZE 0x0DF1
+#define LOCAL_GL_FEEDBACK_BUFFER_TYPE 0x0DF2
+#define LOCAL_GL_FENCE_APPLE 0x8A0B
+#define LOCAL_GL_FENCE_CONDITION_NV 0x84F4
+#define LOCAL_GL_FENCE_STATUS_NV 0x84F3
+#define LOCAL_GL_FIELDS_NV 0x8E27
+#define LOCAL_GL_FIELD_LOWER_NV 0x9023
+#define LOCAL_GL_FIELD_UPPER_NV 0x9022
+#define LOCAL_GL_FILE_NAME_NV 0x9074
+#define LOCAL_GL_FILL 0x1B02
+#define LOCAL_GL_FILTER 0x829A
+#define LOCAL_GL_FILTER4_SGIS 0x8146
+#define LOCAL_GL_FIRST_TO_REST_NV 0x90AF
+#define LOCAL_GL_FIRST_VERTEX_CONVENTION 0x8E4D
+#define LOCAL_GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D
+#define LOCAL_GL_FIXED 0x140C
+#define LOCAL_GL_FIXED_OES 0x140C
+#define LOCAL_GL_FIXED_ONLY 0x891D
+#define LOCAL_GL_FIXED_ONLY_ARB 0x891D
+#define LOCAL_GL_FLAT 0x1D00
+#define LOCAL_GL_FLOAT 0x1406
+#define LOCAL_GL_FLOAT16_NV 0x8FF8
+#define LOCAL_GL_FLOAT16_VEC2_NV 0x8FF9
+#define LOCAL_GL_FLOAT16_VEC3_NV 0x8FFA
+#define LOCAL_GL_FLOAT16_VEC4_NV 0x8FFB
+#define LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
+#define LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD
+#define LOCAL_GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D
+#define LOCAL_GL_FLOAT_MAT2 0x8B5A
+#define LOCAL_GL_FLOAT_MAT2_ARB 0x8B5A
+#define LOCAL_GL_FLOAT_MAT2x3 0x8B65
+#define LOCAL_GL_FLOAT_MAT2x3_NV 0x8B65
+#define LOCAL_GL_FLOAT_MAT2x4 0x8B66
+#define LOCAL_GL_FLOAT_MAT2x4_NV 0x8B66
+#define LOCAL_GL_FLOAT_MAT3 0x8B5B
+#define LOCAL_GL_FLOAT_MAT3_ARB 0x8B5B
+#define LOCAL_GL_FLOAT_MAT3x2 0x8B67
+#define LOCAL_GL_FLOAT_MAT3x2_NV 0x8B67
+#define LOCAL_GL_FLOAT_MAT3x4 0x8B68
+#define LOCAL_GL_FLOAT_MAT3x4_NV 0x8B68
+#define LOCAL_GL_FLOAT_MAT4 0x8B5C
+#define LOCAL_GL_FLOAT_MAT4_ARB 0x8B5C
+#define LOCAL_GL_FLOAT_MAT4x2 0x8B69
+#define LOCAL_GL_FLOAT_MAT4x2_NV 0x8B69
+#define LOCAL_GL_FLOAT_MAT4x3 0x8B6A
+#define LOCAL_GL_FLOAT_MAT4x3_NV 0x8B6A
+#define LOCAL_GL_FLOAT_R16_NV 0x8884
+#define LOCAL_GL_FLOAT_R32_NV 0x8885
+#define LOCAL_GL_FLOAT_RG16_NV 0x8886
+#define LOCAL_GL_FLOAT_RG32_NV 0x8887
+#define LOCAL_GL_FLOAT_RGB16_NV 0x8888
+#define LOCAL_GL_FLOAT_RGB32_NV 0x8889
+#define LOCAL_GL_FLOAT_RGBA16_NV 0x888A
+#define LOCAL_GL_FLOAT_RGBA32_NV 0x888B
+#define LOCAL_GL_FLOAT_RGBA_MODE_NV 0x888E
+#define LOCAL_GL_FLOAT_RGBA_NV 0x8883
+#define LOCAL_GL_FLOAT_RGB_NV 0x8882
+#define LOCAL_GL_FLOAT_RG_NV 0x8881
+#define LOCAL_GL_FLOAT_R_NV 0x8880
+#define LOCAL_GL_FLOAT_VEC2 0x8B50
+#define LOCAL_GL_FLOAT_VEC2_ARB 0x8B50
+#define LOCAL_GL_FLOAT_VEC3 0x8B51
+#define LOCAL_GL_FLOAT_VEC3_ARB 0x8B51
+#define LOCAL_GL_FLOAT_VEC4 0x8B52
+#define LOCAL_GL_FLOAT_VEC4_ARB 0x8B52
+#define LOCAL_GL_FOG 0x0B60
+#define LOCAL_GL_FOG_BIT 0x00000080
+#define LOCAL_GL_FOG_COLOR 0x0B66
+#define LOCAL_GL_FOG_COORD 0x8451
+#define LOCAL_GL_FOG_COORDINATE 0x8451
+#define LOCAL_GL_FOG_COORDINATE_ARRAY 0x8457
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_EXT 0x8457
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_POINTER 0x8456
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_TYPE 0x8454
+#define LOCAL_GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454
+#define LOCAL_GL_FOG_COORDINATE_EXT 0x8451
+#define LOCAL_GL_FOG_COORDINATE_SOURCE 0x8450
+#define LOCAL_GL_FOG_COORDINATE_SOURCE_EXT 0x8450
+#define LOCAL_GL_FOG_COORD_ARRAY 0x8457
+#define LOCAL_GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28
+#define LOCAL_GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D
+#define LOCAL_GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32
+#define LOCAL_GL_FOG_COORD_ARRAY_POINTER 0x8456
+#define LOCAL_GL_FOG_COORD_ARRAY_STRIDE 0x8455
+#define LOCAL_GL_FOG_COORD_ARRAY_TYPE 0x8454
+#define LOCAL_GL_FOG_COORD_SRC 0x8450
+#define LOCAL_GL_FOG_DENSITY 0x0B62
+#define LOCAL_GL_FOG_DISTANCE_MODE_NV 0x855A
+#define LOCAL_GL_FOG_END 0x0B64
+#define LOCAL_GL_FOG_FUNC_POINTS_SGIS 0x812B
+#define LOCAL_GL_FOG_FUNC_SGIS 0x812A
+#define LOCAL_GL_FOG_HINT 0x0C54
+#define LOCAL_GL_FOG_INDEX 0x0B61
+#define LOCAL_GL_FOG_MODE 0x0B65
+#define LOCAL_GL_FOG_OFFSET_SGIX 0x8198
+#define LOCAL_GL_FOG_OFFSET_VALUE_SGIX 0x8199
+#define LOCAL_GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC
+#define LOCAL_GL_FOG_START 0x0B63
+#define LOCAL_GL_FONT_ASCENDER_BIT_NV 0x00200000
+#define LOCAL_GL_FONT_DESCENDER_BIT_NV 0x00400000
+#define LOCAL_GL_FONT_HAS_KERNING_BIT_NV 0x10000000
+#define LOCAL_GL_FONT_HEIGHT_BIT_NV 0x00800000
+#define LOCAL_GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000
+#define LOCAL_GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000
+#define LOCAL_GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000
+#define LOCAL_GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000
+#define LOCAL_GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000
+#define LOCAL_GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000
+#define LOCAL_GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000
+#define LOCAL_GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000
+#define LOCAL_GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000
+#define LOCAL_GL_FORCE_BLUE_TO_ONE_NV 0x8860
+#define LOCAL_GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983
+#define LOCAL_GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982
+#define LOCAL_GL_FRACTIONAL_EVEN 0x8E7C
+#define LOCAL_GL_FRACTIONAL_ODD 0x8E7B
+#define LOCAL_GL_FRAGMENTS_INSTRUMENT_COUNTERS_SGIX 0x8314
+#define LOCAL_GL_FRAGMENTS_INSTRUMENT_MAX_SGIX 0x8315
+#define LOCAL_GL_FRAGMENTS_INSTRUMENT_SGIX 0x8313
+#define LOCAL_GL_FRAGMENT_ALPHA_MODULATE_IMG 0x8C08
+#define LOCAL_GL_FRAGMENT_COLOR_EXT 0x834C
+#define LOCAL_GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402
+#define LOCAL_GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403
+#define LOCAL_GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401
+#define LOCAL_GL_FRAGMENT_DEPTH 0x8452
+#define LOCAL_GL_FRAGMENT_DEPTH_EXT 0x8452
+#define LOCAL_GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D
+#define LOCAL_GL_FRAGMENT_LIGHT0_SGIX 0x840C
+#define LOCAL_GL_FRAGMENT_LIGHT1_SGIX 0x840D
+#define LOCAL_GL_FRAGMENT_LIGHT2_SGIX 0x840E
+#define LOCAL_GL_FRAGMENT_LIGHT3_SGIX 0x840F
+#define LOCAL_GL_FRAGMENT_LIGHT4_SGIX 0x8410
+#define LOCAL_GL_FRAGMENT_LIGHT5_SGIX 0x8411
+#define LOCAL_GL_FRAGMENT_LIGHT6_SGIX 0x8412
+#define LOCAL_GL_FRAGMENT_LIGHT7_SGIX 0x8413
+#define LOCAL_GL_FRAGMENT_LIGHTING_SGIX 0x8400
+#define LOCAL_GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A
+#define LOCAL_GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408
+#define LOCAL_GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B
+#define LOCAL_GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409
+#define LOCAL_GL_FRAGMENT_MATERIAL_EXT 0x8349
+#define LOCAL_GL_FRAGMENT_NORMAL_EXT 0x834A
+#define LOCAL_GL_FRAGMENT_PROGRAM_ARB 0x8804
+#define LOCAL_GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873
+#define LOCAL_GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA 0x8BB3
+#define LOCAL_GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA 0x8BB2
+#define LOCAL_GL_FRAGMENT_PROGRAM_CALLBACK_MESA 0x8BB1
+#define LOCAL_GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D
+#define LOCAL_GL_FRAGMENT_PROGRAM_NV 0x8870
+#define LOCAL_GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4
+#define LOCAL_GL_FRAGMENT_PROGRAM_POSITION_MESA 0x8BB0
+#define LOCAL_GL_FRAGMENT_SHADER 0x8B30
+#define LOCAL_GL_FRAGMENT_SHADER_ARB 0x8B30
+#define LOCAL_GL_FRAGMENT_SHADER_ATI 0x8920
+#define LOCAL_GL_FRAGMENT_SHADER_BIT 0x00000002
+#define LOCAL_GL_FRAGMENT_SHADER_BIT_EXT 0x00000002
+#define LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B
+#define LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B
+#define LOCAL_GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52
+#define LOCAL_GL_FRAGMENT_SUBROUTINE 0x92EC
+#define LOCAL_GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2
+#define LOCAL_GL_FRAGMENT_TEXTURE 0x829F
+#define LOCAL_GL_FRAMEBUFFER 0x8D40
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ANGLE 0x93A3
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT 0x8210
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES 0x8CD1
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES 0x8CD0
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES 0x8CD3
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES 0x8CD2
+#define LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT 0x8D6C
+#define LOCAL_GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
+#define LOCAL_GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400
+#define LOCAL_GL_FRAMEBUFFER_BINDING 0x8CA6
+#define LOCAL_GL_FRAMEBUFFER_BINDING_ANGLE 0x8CA6
+#define LOCAL_GL_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define LOCAL_GL_FRAMEBUFFER_BINDING_OES 0x8CA6
+#define LOCAL_GL_FRAMEBUFFER_BLEND 0x828B
+#define LOCAL_GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define LOCAL_GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
+#define LOCAL_GL_FRAMEBUFFER_COMPLETE_OES 0x8CD5
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT 0x8218
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313
+#define LOCAL_GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310
+#define LOCAL_GL_FRAMEBUFFER_EXT 0x8D40
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES 0x8CD6
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES 0x8CD9
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES 0x8CDB
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES 0x8CDA
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES 0x8CD7
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE 0x8D56
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_APPLE 0x8D56
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_NV 0x8D56
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
+#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES 0x8CDC
+#define LOCAL_GL_FRAMEBUFFER_OES 0x8D40
+#define LOCAL_GL_FRAMEBUFFER_RENDERABLE 0x8289
+#define LOCAL_GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A
+#define LOCAL_GL_FRAMEBUFFER_SRGB 0x8DB9
+#define LOCAL_GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA
+#define LOCAL_GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
+#define LOCAL_GL_FRAMEBUFFER_UNDEFINED 0x8219
+#define LOCAL_GL_FRAMEBUFFER_UNDEFINED_OES 0x8219
+#define LOCAL_GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define LOCAL_GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD
+#define LOCAL_GL_FRAMEBUFFER_UNSUPPORTED_OES 0x8CDD
+#define LOCAL_GL_FRAMEZOOM_FACTOR_SGIX 0x818C
+#define LOCAL_GL_FRAMEZOOM_SGIX 0x818B
+#define LOCAL_GL_FRAME_NV 0x8E26
+#define LOCAL_GL_FRONT 0x0404
+#define LOCAL_GL_FRONT_AND_BACK 0x0408
+#define LOCAL_GL_FRONT_FACE 0x0B46
+#define LOCAL_GL_FRONT_LEFT 0x0400
+#define LOCAL_GL_FRONT_RIGHT 0x0401
+#define LOCAL_GL_FULL_RANGE_EXT 0x87E1
+#define LOCAL_GL_FULL_STIPPLE_HINT_PGI 0x1A219
+#define LOCAL_GL_FULL_SUPPORT 0x82B7
+#define LOCAL_GL_FUNC_ADD 0x8006
+#define LOCAL_GL_FUNC_ADD_EXT 0x8006
+#define LOCAL_GL_FUNC_ADD_OES 0x8006
+#define LOCAL_GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define LOCAL_GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B
+#define LOCAL_GL_FUNC_REVERSE_SUBTRACT_OES 0x800B
+#define LOCAL_GL_FUNC_SUBTRACT 0x800A
+#define LOCAL_GL_FUNC_SUBTRACT_EXT 0x800A
+#define LOCAL_GL_FUNC_SUBTRACT_OES 0x800A
+#define LOCAL_GL_GCCSO_SHADER_BINARY_FJ 0x9260
+#define LOCAL_GL_GENERATE_MIPMAP 0x8191
+#define LOCAL_GL_GENERATE_MIPMAP_HINT 0x8192
+#define LOCAL_GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#define LOCAL_GL_GENERATE_MIPMAP_SGIS 0x8191
+#define LOCAL_GL_GENERIC_ATTRIB_NV 0x8C7D
+#define LOCAL_GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002
+#define LOCAL_GL_GEOMETRY_DEFORMATION_SGIX 0x8194
+#define LOCAL_GL_GEOMETRY_INPUT_TYPE 0x8917
+#define LOCAL_GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB
+#define LOCAL_GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB
+#define LOCAL_GL_GEOMETRY_OUTPUT_TYPE 0x8918
+#define LOCAL_GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC
+#define LOCAL_GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC
+#define LOCAL_GL_GEOMETRY_PROGRAM_NV 0x8C26
+#define LOCAL_GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3
+#define LOCAL_GL_GEOMETRY_SHADER 0x8DD9
+#define LOCAL_GL_GEOMETRY_SHADER_ARB 0x8DD9
+#define LOCAL_GL_GEOMETRY_SHADER_BIT 0x00000004
+#define LOCAL_GL_GEOMETRY_SHADER_EXT 0x8DD9
+#define LOCAL_GL_GEOMETRY_SHADER_INVOCATIONS 0x887F
+#define LOCAL_GL_GEOMETRY_SUBROUTINE 0x92EB
+#define LOCAL_GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1
+#define LOCAL_GL_GEOMETRY_TEXTURE 0x829E
+#define LOCAL_GL_GEOMETRY_VERTICES_OUT 0x8916
+#define LOCAL_GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA
+#define LOCAL_GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
+#define LOCAL_GL_GEQUAL 0x0206
+#define LOCAL_GL_GET_TEXTURE_IMAGE_FORMAT 0x8291
+#define LOCAL_GL_GET_TEXTURE_IMAGE_TYPE 0x8292
+#define LOCAL_GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA
+#define LOCAL_GL_GLOBAL_ALPHA_SUN 0x81D9
+#define LOCAL_GL_GLYPH_HAS_KERNING_BIT_NV 0x100
+#define LOCAL_GL_GLYPH_HEIGHT_BIT_NV 0x02
+#define LOCAL_GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10
+#define LOCAL_GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04
+#define LOCAL_GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08
+#define LOCAL_GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80
+#define LOCAL_GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20
+#define LOCAL_GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40
+#define LOCAL_GL_GLYPH_WIDTH_BIT_NV 0x01
+#define LOCAL_GL_GPU_ADDRESS_NV 0x8F34
+#define LOCAL_GL_GPU_DISJOINT_EXT 0x8FBB
+#define LOCAL_GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#define LOCAL_GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#define LOCAL_GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
+#define LOCAL_GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
+#define LOCAL_GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#define LOCAL_GL_GPU_OPTIMIZED_QCOM 0x8FB2
+#define LOCAL_GL_GREATER 0x0204
+#define LOCAL_GL_GREEN 0x1904
+#define LOCAL_GL_GREEN_BIAS 0x0D19
+#define LOCAL_GL_GREEN_BITS 0x0D53
+#define LOCAL_GL_GREEN_BIT_ATI 0x00000002
+#define LOCAL_GL_GREEN_INTEGER 0x8D95
+#define LOCAL_GL_GREEN_INTEGER_EXT 0x8D95
+#define LOCAL_GL_GREEN_MAX_CLAMP_INGR 0x8565
+#define LOCAL_GL_GREEN_MIN_CLAMP_INGR 0x8561
+#define LOCAL_GL_GREEN_NV 0x1904
+#define LOCAL_GL_GREEN_SCALE 0x0D18
+#define LOCAL_GL_GUILTY_CONTEXT_RESET_ARB 0x8253
+#define LOCAL_GL_GUILTY_CONTEXT_RESET_EXT 0x8253
+#define LOCAL_GL_HALF_APPLE 0x140B
+#define LOCAL_GL_HALF_BIAS_NEGATE_NV 0x853B
+#define LOCAL_GL_HALF_BIAS_NORMAL_NV 0x853A
+#define LOCAL_GL_HALF_BIT_ATI 0x00000008
+#define LOCAL_GL_HALF_FLOAT 0x140B
+#define LOCAL_GL_HALF_FLOAT_ARB 0x140B
+#define LOCAL_GL_HALF_FLOAT_NV 0x140B
+#define LOCAL_GL_HALF_FLOAT_OES 0x8D61
+#define LOCAL_GL_HARDLIGHT_NV 0x929B
+#define LOCAL_GL_HARDMIX_NV 0x92A9
+#define LOCAL_GL_HIGH_FLOAT 0x8DF2
+#define LOCAL_GL_HIGH_INT 0x8DF5
+#define LOCAL_GL_HILO16_NV 0x86F8
+#define LOCAL_GL_HILO8_NV 0x885E
+#define LOCAL_GL_HILO_NV 0x86F4
+#define LOCAL_GL_HINT_BIT 0x00008000
+#define LOCAL_GL_HISTOGRAM 0x8024
+#define LOCAL_GL_HISTOGRAM_ALPHA_SIZE 0x802B
+#define LOCAL_GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B
+#define LOCAL_GL_HISTOGRAM_BLUE_SIZE 0x802A
+#define LOCAL_GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A
+#define LOCAL_GL_HISTOGRAM_EXT 0x8024
+#define LOCAL_GL_HISTOGRAM_FORMAT 0x8027
+#define LOCAL_GL_HISTOGRAM_FORMAT_EXT 0x8027
+#define LOCAL_GL_HISTOGRAM_GREEN_SIZE 0x8029
+#define LOCAL_GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029
+#define LOCAL_GL_HISTOGRAM_LUMINANCE_SIZE 0x802C
+#define LOCAL_GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C
+#define LOCAL_GL_HISTOGRAM_RED_SIZE 0x8028
+#define LOCAL_GL_HISTOGRAM_RED_SIZE_EXT 0x8028
+#define LOCAL_GL_HISTOGRAM_SINK 0x802D
+#define LOCAL_GL_HISTOGRAM_SINK_EXT 0x802D
+#define LOCAL_GL_HISTOGRAM_WIDTH 0x8026
+#define LOCAL_GL_HISTOGRAM_WIDTH_EXT 0x8026
+#define LOCAL_GL_HI_BIAS_NV 0x8714
+#define LOCAL_GL_HI_SCALE_NV 0x870E
+#define LOCAL_GL_HORIZONTAL_LINE_TO_NV 0x06
+#define LOCAL_GL_HSL_COLOR_NV 0x92AF
+#define LOCAL_GL_HSL_HUE_NV 0x92AD
+#define LOCAL_GL_HSL_LUMINOSITY_NV 0x92B0
+#define LOCAL_GL_HSL_SATURATION_NV 0x92AE
+#define LOCAL_GL_IDENTITY_NV 0x862A
+#define LOCAL_GL_IGNORE_BORDER_HP 0x8150
+#define LOCAL_GL_IMAGE_1D 0x904C
+#define LOCAL_GL_IMAGE_1D_ARRAY 0x9052
+#define LOCAL_GL_IMAGE_1D_ARRAY_EXT 0x9052
+#define LOCAL_GL_IMAGE_1D_EXT 0x904C
+#define LOCAL_GL_IMAGE_2D 0x904D
+#define LOCAL_GL_IMAGE_2D_ARRAY 0x9053
+#define LOCAL_GL_IMAGE_2D_ARRAY_EXT 0x9053
+#define LOCAL_GL_IMAGE_2D_EXT 0x904D
+#define LOCAL_GL_IMAGE_2D_MULTISAMPLE 0x9055
+#define LOCAL_GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056
+#define LOCAL_GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056
+#define LOCAL_GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055
+#define LOCAL_GL_IMAGE_2D_RECT 0x904F
+#define LOCAL_GL_IMAGE_2D_RECT_EXT 0x904F
+#define LOCAL_GL_IMAGE_3D 0x904E
+#define LOCAL_GL_IMAGE_3D_EXT 0x904E
+#define LOCAL_GL_IMAGE_BINDING_ACCESS 0x8F3E
+#define LOCAL_GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E
+#define LOCAL_GL_IMAGE_BINDING_FORMAT 0x906E
+#define LOCAL_GL_IMAGE_BINDING_FORMAT_EXT 0x906E
+#define LOCAL_GL_IMAGE_BINDING_LAYER 0x8F3D
+#define LOCAL_GL_IMAGE_BINDING_LAYERED 0x8F3C
+#define LOCAL_GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C
+#define LOCAL_GL_IMAGE_BINDING_LAYER_EXT 0x8F3D
+#define LOCAL_GL_IMAGE_BINDING_LEVEL 0x8F3B
+#define LOCAL_GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B
+#define LOCAL_GL_IMAGE_BINDING_NAME 0x8F3A
+#define LOCAL_GL_IMAGE_BINDING_NAME_EXT 0x8F3A
+#define LOCAL_GL_IMAGE_BUFFER 0x9051
+#define LOCAL_GL_IMAGE_BUFFER_EXT 0x9051
+#define LOCAL_GL_IMAGE_CLASS_10_10_10_2 0x82C3
+#define LOCAL_GL_IMAGE_CLASS_11_11_10 0x82C2
+#define LOCAL_GL_IMAGE_CLASS_1_X_16 0x82BE
+#define LOCAL_GL_IMAGE_CLASS_1_X_32 0x82BB
+#define LOCAL_GL_IMAGE_CLASS_1_X_8 0x82C1
+#define LOCAL_GL_IMAGE_CLASS_2_X_16 0x82BD
+#define LOCAL_GL_IMAGE_CLASS_2_X_32 0x82BA
+#define LOCAL_GL_IMAGE_CLASS_2_X_8 0x82C0
+#define LOCAL_GL_IMAGE_CLASS_4_X_16 0x82BC
+#define LOCAL_GL_IMAGE_CLASS_4_X_32 0x82B9
+#define LOCAL_GL_IMAGE_CLASS_4_X_8 0x82BF
+#define LOCAL_GL_IMAGE_COMPATIBILITY_CLASS 0x82A8
+#define LOCAL_GL_IMAGE_CUBE 0x9050
+#define LOCAL_GL_IMAGE_CUBE_EXT 0x9050
+#define LOCAL_GL_IMAGE_CUBE_MAP_ARRAY 0x9054
+#define LOCAL_GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054
+#define LOCAL_GL_IMAGE_CUBIC_WEIGHT_HP 0x815E
+#define LOCAL_GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9
+#define LOCAL_GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8
+#define LOCAL_GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7
+#define LOCAL_GL_IMAGE_MAG_FILTER_HP 0x815C
+#define LOCAL_GL_IMAGE_MIN_FILTER_HP 0x815D
+#define LOCAL_GL_IMAGE_PIXEL_FORMAT 0x82A9
+#define LOCAL_GL_IMAGE_PIXEL_TYPE 0x82AA
+#define LOCAL_GL_IMAGE_ROTATE_ANGLE_HP 0x8159
+#define LOCAL_GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A
+#define LOCAL_GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B
+#define LOCAL_GL_IMAGE_SCALE_X_HP 0x8155
+#define LOCAL_GL_IMAGE_SCALE_Y_HP 0x8156
+#define LOCAL_GL_IMAGE_TEXEL_SIZE 0x82A7
+#define LOCAL_GL_IMAGE_TRANSFORM_2D_HP 0x8161
+#define LOCAL_GL_IMAGE_TRANSLATE_X_HP 0x8157
+#define LOCAL_GL_IMAGE_TRANSLATE_Y_HP 0x8158
+#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B
+#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A
+#define LOCAL_GL_INCR 0x1E02
+#define LOCAL_GL_INCR_WRAP 0x8507
+#define LOCAL_GL_INCR_WRAP_EXT 0x8507
+#define LOCAL_GL_INCR_WRAP_OES 0x8507
+#define LOCAL_GL_INDEX 0x8222
+#define LOCAL_GL_INDEX_ARRAY 0x8077
+#define LOCAL_GL_INDEX_ARRAY_ADDRESS_NV 0x8F24
+#define LOCAL_GL_INDEX_ARRAY_BUFFER_BINDING 0x8899
+#define LOCAL_GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899
+#define LOCAL_GL_INDEX_ARRAY_COUNT_EXT 0x8087
+#define LOCAL_GL_INDEX_ARRAY_EXT 0x8077
+#define LOCAL_GL_INDEX_ARRAY_LENGTH_NV 0x8F2E
+#define LOCAL_GL_INDEX_ARRAY_LIST_IBM 103073
+#define LOCAL_GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083
+#define LOCAL_GL_INDEX_ARRAY_POINTER 0x8091
+#define LOCAL_GL_INDEX_ARRAY_POINTER_EXT 0x8091
+#define LOCAL_GL_INDEX_ARRAY_STRIDE 0x8086
+#define LOCAL_GL_INDEX_ARRAY_STRIDE_EXT 0x8086
+#define LOCAL_GL_INDEX_ARRAY_TYPE 0x8085
+#define LOCAL_GL_INDEX_ARRAY_TYPE_EXT 0x8085
+#define LOCAL_GL_INDEX_BITS 0x0D51
+#define LOCAL_GL_INDEX_BIT_PGI 0x00080000
+#define LOCAL_GL_INDEX_CLEAR_VALUE 0x0C20
+#define LOCAL_GL_INDEX_LOGIC_OP 0x0BF1
+#define LOCAL_GL_INDEX_MATERIAL_EXT 0x81B8
+#define LOCAL_GL_INDEX_MATERIAL_FACE_EXT 0x81BA
+#define LOCAL_GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9
+#define LOCAL_GL_INDEX_MODE 0x0C30
+#define LOCAL_GL_INDEX_OFFSET 0x0D13
+#define LOCAL_GL_INDEX_SHIFT 0x0D12
+#define LOCAL_GL_INDEX_TEST_EXT 0x81B5
+#define LOCAL_GL_INDEX_TEST_FUNC_EXT 0x81B6
+#define LOCAL_GL_INDEX_TEST_REF_EXT 0x81B7
+#define LOCAL_GL_INDEX_WRITEMASK 0x0C21
+#define LOCAL_GL_INFO_LOG_LENGTH 0x8B84
+#define LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB 0x8254
+#define LOCAL_GL_INNOCENT_CONTEXT_RESET_EXT 0x8254
+#define LOCAL_GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180
+#define LOCAL_GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181
+#define LOCAL_GL_INT 0x1404
+#define LOCAL_GL_INT16_NV 0x8FE4
+#define LOCAL_GL_INT16_VEC2_NV 0x8FE5
+#define LOCAL_GL_INT16_VEC3_NV 0x8FE6
+#define LOCAL_GL_INT16_VEC4_NV 0x8FE7
+#define LOCAL_GL_INT64_NV 0x140E
+#define LOCAL_GL_INT64_VEC2_NV 0x8FE9
+#define LOCAL_GL_INT64_VEC3_NV 0x8FEA
+#define LOCAL_GL_INT64_VEC4_NV 0x8FEB
+#define LOCAL_GL_INT8_NV 0x8FE0
+#define LOCAL_GL_INT8_VEC2_NV 0x8FE1
+#define LOCAL_GL_INT8_VEC3_NV 0x8FE2
+#define LOCAL_GL_INT8_VEC4_NV 0x8FE3
+#define LOCAL_GL_INTENSITY 0x8049
+#define LOCAL_GL_INTENSITY12 0x804C
+#define LOCAL_GL_INTENSITY12_EXT 0x804C
+#define LOCAL_GL_INTENSITY16 0x804D
+#define LOCAL_GL_INTENSITY16F_ARB 0x881D
+#define LOCAL_GL_INTENSITY16I_EXT 0x8D8B
+#define LOCAL_GL_INTENSITY16UI_EXT 0x8D79
+#define LOCAL_GL_INTENSITY16_EXT 0x804D
+#define LOCAL_GL_INTENSITY16_SNORM 0x901B
+#define LOCAL_GL_INTENSITY32F_ARB 0x8817
+#define LOCAL_GL_INTENSITY32I_EXT 0x8D85
+#define LOCAL_GL_INTENSITY32UI_EXT 0x8D73
+#define LOCAL_GL_INTENSITY4 0x804A
+#define LOCAL_GL_INTENSITY4_EXT 0x804A
+#define LOCAL_GL_INTENSITY8 0x804B
+#define LOCAL_GL_INTENSITY8I_EXT 0x8D91
+#define LOCAL_GL_INTENSITY8UI_EXT 0x8D7F
+#define LOCAL_GL_INTENSITY8_EXT 0x804B
+#define LOCAL_GL_INTENSITY8_SNORM 0x9017
+#define LOCAL_GL_INTENSITY_EXT 0x8049
+#define LOCAL_GL_INTENSITY_FLOAT16_APPLE 0x881D
+#define LOCAL_GL_INTENSITY_FLOAT16_ATI 0x881D
+#define LOCAL_GL_INTENSITY_FLOAT32_APPLE 0x8817
+#define LOCAL_GL_INTENSITY_FLOAT32_ATI 0x8817
+#define LOCAL_GL_INTENSITY_SNORM 0x9013
+#define LOCAL_GL_INTERLACE_OML 0x8980
+#define LOCAL_GL_INTERLACE_READ_INGR 0x8568
+#define LOCAL_GL_INTERLACE_READ_OML 0x8981
+#define LOCAL_GL_INTERLACE_SGIX 0x8094
+#define LOCAL_GL_INTERLEAVED_ATTRIBS 0x8C8C
+#define LOCAL_GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C
+#define LOCAL_GL_INTERLEAVED_ATTRIBS_NV 0x8C8C
+#define LOCAL_GL_INTERNALFORMAT_ALPHA_SIZE 0x8274
+#define LOCAL_GL_INTERNALFORMAT_ALPHA_TYPE 0x827B
+#define LOCAL_GL_INTERNALFORMAT_BLUE_SIZE 0x8273
+#define LOCAL_GL_INTERNALFORMAT_BLUE_TYPE 0x827A
+#define LOCAL_GL_INTERNALFORMAT_DEPTH_SIZE 0x8275
+#define LOCAL_GL_INTERNALFORMAT_DEPTH_TYPE 0x827C
+#define LOCAL_GL_INTERNALFORMAT_GREEN_SIZE 0x8272
+#define LOCAL_GL_INTERNALFORMAT_GREEN_TYPE 0x8279
+#define LOCAL_GL_INTERNALFORMAT_PREFERRED 0x8270
+#define LOCAL_GL_INTERNALFORMAT_RED_SIZE 0x8271
+#define LOCAL_GL_INTERNALFORMAT_RED_TYPE 0x8278
+#define LOCAL_GL_INTERNALFORMAT_SHARED_SIZE 0x8277
+#define LOCAL_GL_INTERNALFORMAT_STENCIL_SIZE 0x8276
+#define LOCAL_GL_INTERNALFORMAT_STENCIL_TYPE 0x827D
+#define LOCAL_GL_INTERNALFORMAT_SUPPORTED 0x826F
+#define LOCAL_GL_INTERPOLATE 0x8575
+#define LOCAL_GL_INTERPOLATE_ARB 0x8575
+#define LOCAL_GL_INTERPOLATE_EXT 0x8575
+#define LOCAL_GL_INT_10_10_10_2_OES 0x8DF7
+#define LOCAL_GL_INT_2_10_10_10_REV 0x8D9F
+#define LOCAL_GL_INT_IMAGE_1D 0x9057
+#define LOCAL_GL_INT_IMAGE_1D_ARRAY 0x905D
+#define LOCAL_GL_INT_IMAGE_1D_ARRAY_EXT 0x905D
+#define LOCAL_GL_INT_IMAGE_1D_EXT 0x9057
+#define LOCAL_GL_INT_IMAGE_2D 0x9058
+#define LOCAL_GL_INT_IMAGE_2D_ARRAY 0x905E
+#define LOCAL_GL_INT_IMAGE_2D_ARRAY_EXT 0x905E
+#define LOCAL_GL_INT_IMAGE_2D_EXT 0x9058
+#define LOCAL_GL_INT_IMAGE_2D_MULTISAMPLE 0x9060
+#define LOCAL_GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061
+#define LOCAL_GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061
+#define LOCAL_GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060
+#define LOCAL_GL_INT_IMAGE_2D_RECT 0x905A
+#define LOCAL_GL_INT_IMAGE_2D_RECT_EXT 0x905A
+#define LOCAL_GL_INT_IMAGE_3D 0x9059
+#define LOCAL_GL_INT_IMAGE_3D_EXT 0x9059
+#define LOCAL_GL_INT_IMAGE_BUFFER 0x905C
+#define LOCAL_GL_INT_IMAGE_BUFFER_EXT 0x905C
+#define LOCAL_GL_INT_IMAGE_CUBE 0x905B
+#define LOCAL_GL_INT_IMAGE_CUBE_EXT 0x905B
+#define LOCAL_GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
+#define LOCAL_GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F
+#define LOCAL_GL_INT_SAMPLER_1D 0x8DC9
+#define LOCAL_GL_INT_SAMPLER_1D_ARRAY 0x8DCE
+#define LOCAL_GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE
+#define LOCAL_GL_INT_SAMPLER_1D_EXT 0x8DC9
+#define LOCAL_GL_INT_SAMPLER_2D 0x8DCA
+#define LOCAL_GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define LOCAL_GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF
+#define LOCAL_GL_INT_SAMPLER_2D_EXT 0x8DCA
+#define LOCAL_GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109
+#define LOCAL_GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C
+#define LOCAL_GL_INT_SAMPLER_2D_RECT 0x8DCD
+#define LOCAL_GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD
+#define LOCAL_GL_INT_SAMPLER_3D 0x8DCB
+#define LOCAL_GL_INT_SAMPLER_3D_EXT 0x8DCB
+#define LOCAL_GL_INT_SAMPLER_BUFFER 0x8DD0
+#define LOCAL_GL_INT_SAMPLER_BUFFER_AMD 0x9002
+#define LOCAL_GL_INT_SAMPLER_BUFFER_EXT 0x8DD0
+#define LOCAL_GL_INT_SAMPLER_CUBE 0x8DCC
+#define LOCAL_GL_INT_SAMPLER_CUBE_EXT 0x8DCC
+#define LOCAL_GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
+#define LOCAL_GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E
+#define LOCAL_GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57
+#define LOCAL_GL_INT_VEC2 0x8B53
+#define LOCAL_GL_INT_VEC2_ARB 0x8B53
+#define LOCAL_GL_INT_VEC3 0x8B54
+#define LOCAL_GL_INT_VEC3_ARB 0x8B54
+#define LOCAL_GL_INT_VEC4 0x8B55
+#define LOCAL_GL_INT_VEC4_ARB 0x8B55
+#define LOCAL_GL_INVALID_ENUM 0x0500
+#define LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+#define LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+#define LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION_OES 0x0506
+#define LOCAL_GL_INVALID_INDEX 0xFFFFFFFF
+#define LOCAL_GL_INVALID_OPERATION 0x0502
+#define LOCAL_GL_INVALID_VALUE 0x0501
+#define LOCAL_GL_INVARIANT_DATATYPE_EXT 0x87EB
+#define LOCAL_GL_INVARIANT_EXT 0x87C2
+#define LOCAL_GL_INVARIANT_VALUE_EXT 0x87EA
+#define LOCAL_GL_INVERSE_NV 0x862B
+#define LOCAL_GL_INVERSE_TRANSPOSE_NV 0x862D
+#define LOCAL_GL_INVERT 0x150A
+#define LOCAL_GL_INVERTED_SCREEN_W_REND 0x8491
+#define LOCAL_GL_INVERT_OVG_NV 0x92B4
+#define LOCAL_GL_INVERT_RGB_NV 0x92A3
+#define LOCAL_GL_IR_INSTRUMENT1_SGIX 0x817F
+#define LOCAL_GL_ISOLINES 0x8E7A
+#define LOCAL_GL_IS_PER_PATCH 0x92E7
+#define LOCAL_GL_IS_ROW_MAJOR 0x9300
+#define LOCAL_GL_ITALIC_BIT_NV 0x02
+#define LOCAL_GL_IUI_N3F_V2F_EXT 0x81AF
+#define LOCAL_GL_IUI_N3F_V3F_EXT 0x81B0
+#define LOCAL_GL_IUI_V2F_EXT 0x81AD
+#define LOCAL_GL_IUI_V3F_EXT 0x81AE
+#define LOCAL_GL_KEEP 0x1E00
+#define LOCAL_GL_LARGE_CCW_ARC_TO_NV 0x16
+#define LOCAL_GL_LARGE_CW_ARC_TO_NV 0x18
+#define LOCAL_GL_LAST_VERTEX_CONVENTION 0x8E4E
+#define LOCAL_GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E
+#define LOCAL_GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027
+#define LOCAL_GL_LAYER_NV 0x8DAA
+#define LOCAL_GL_LAYER_PROVOKING_VERTEX 0x825E
+#define LOCAL_GL_LAYOUT_DEFAULT_INTEL 0
+#define LOCAL_GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2
+#define LOCAL_GL_LAYOUT_LINEAR_INTEL 1
+#define LOCAL_GL_LEFT 0x0406
+#define LOCAL_GL_LEQUAL 0x0203
+#define LOCAL_GL_LERP_ATI 0x8969
+#define LOCAL_GL_LESS 0x0201
+#define LOCAL_GL_LIGHT0 0x4000
+#define LOCAL_GL_LIGHT1 0x4001
+#define LOCAL_GL_LIGHT2 0x4002
+#define LOCAL_GL_LIGHT3 0x4003
+#define LOCAL_GL_LIGHT4 0x4004
+#define LOCAL_GL_LIGHT5 0x4005
+#define LOCAL_GL_LIGHT6 0x4006
+#define LOCAL_GL_LIGHT7 0x4007
+#define LOCAL_GL_LIGHTEN_NV 0x9298
+#define LOCAL_GL_LIGHTING 0x0B50
+#define LOCAL_GL_LIGHTING_BIT 0x00000040
+#define LOCAL_GL_LIGHT_ENV_MODE_SGIX 0x8407
+#define LOCAL_GL_LIGHT_MODEL_AMBIENT 0x0B53
+#define LOCAL_GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
+#define LOCAL_GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8
+#define LOCAL_GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51
+#define LOCAL_GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0
+#define LOCAL_GL_LIGHT_MODEL_TWO_SIDE 0x0B52
+#define LOCAL_GL_LINE 0x1B01
+#define LOCAL_GL_LINEAR 0x2601
+#define LOCAL_GL_LINEARBURN_NV 0x92A5
+#define LOCAL_GL_LINEARDODGE_NV 0x92A4
+#define LOCAL_GL_LINEARLIGHT_NV 0x92A7
+#define LOCAL_GL_LINEAR_ATTENUATION 0x1208
+#define LOCAL_GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170
+#define LOCAL_GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F
+#define LOCAL_GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098
+#define LOCAL_GL_LINEAR_DETAIL_COLOR_SGIS 0x8099
+#define LOCAL_GL_LINEAR_DETAIL_SGIS 0x8097
+#define LOCAL_GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define LOCAL_GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define LOCAL_GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE
+#define LOCAL_GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF
+#define LOCAL_GL_LINEAR_SHARPEN_SGIS 0x80AD
+#define LOCAL_GL_LINES 0x0001
+#define LOCAL_GL_LINES_ADJACENCY 0x000A
+#define LOCAL_GL_LINES_ADJACENCY_ARB 0x000A
+#define LOCAL_GL_LINES_ADJACENCY_EXT 0x000A
+#define LOCAL_GL_LINE_BIT 0x00000004
+#define LOCAL_GL_LINE_LOOP 0x0002
+#define LOCAL_GL_LINE_QUALITY_HINT_SGIX 0x835B
+#define LOCAL_GL_LINE_RESET_TOKEN 0x0707
+#define LOCAL_GL_LINE_SMOOTH 0x0B20
+#define LOCAL_GL_LINE_SMOOTH_HINT 0x0C52
+#define LOCAL_GL_LINE_STIPPLE 0x0B24
+#define LOCAL_GL_LINE_STIPPLE_PATTERN 0x0B25
+#define LOCAL_GL_LINE_STIPPLE_REPEAT 0x0B26
+#define LOCAL_GL_LINE_STRIP 0x0003
+#define LOCAL_GL_LINE_STRIP_ADJACENCY 0x000B
+#define LOCAL_GL_LINE_STRIP_ADJACENCY_ARB 0x000B
+#define LOCAL_GL_LINE_STRIP_ADJACENCY_EXT 0x000B
+#define LOCAL_GL_LINE_TOKEN 0x0702
+#define LOCAL_GL_LINE_TO_NV 0x04
+#define LOCAL_GL_LINE_WIDTH 0x0B21
+#define LOCAL_GL_LINE_WIDTH_GRANULARITY 0x0B23
+#define LOCAL_GL_LINE_WIDTH_RANGE 0x0B22
+#define LOCAL_GL_LINK_STATUS 0x8B82
+#define LOCAL_GL_LIST_BASE 0x0B32
+#define LOCAL_GL_LIST_BIT 0x00020000
+#define LOCAL_GL_LIST_INDEX 0x0B33
+#define LOCAL_GL_LIST_MODE 0x0B30
+#define LOCAL_GL_LIST_PRIORITY_SGIX 0x8182
+#define LOCAL_GL_LOAD 0x0101
+#define LOCAL_GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED
+#define LOCAL_GL_LOCAL_CONSTANT_EXT 0x87C3
+#define LOCAL_GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC
+#define LOCAL_GL_LOCAL_EXT 0x87C4
+#define LOCAL_GL_LOCATION 0x930E
+#define LOCAL_GL_LOCATION_COMPONENT 0x934A
+#define LOCAL_GL_LOCATION_INDEX 0x930F
+#define LOCAL_GL_LOGIC_OP 0x0BF1
+#define LOCAL_GL_LOGIC_OP_MODE 0x0BF0
+#define LOCAL_GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
+#define LOCAL_GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252
+#define LOCAL_GL_LOWER_LEFT 0x8CA1
+#define LOCAL_GL_LOW_FLOAT 0x8DF0
+#define LOCAL_GL_LOW_INT 0x8DF3
+#define LOCAL_GL_LO_BIAS_NV 0x8715
+#define LOCAL_GL_LO_SCALE_NV 0x870F
+#define LOCAL_GL_LUMINANCE 0x1909
+#define LOCAL_GL_LUMINANCE12 0x8041
+#define LOCAL_GL_LUMINANCE12_ALPHA12 0x8047
+#define LOCAL_GL_LUMINANCE12_ALPHA12_EXT 0x8047
+#define LOCAL_GL_LUMINANCE12_ALPHA4 0x8046
+#define LOCAL_GL_LUMINANCE12_ALPHA4_EXT 0x8046
+#define LOCAL_GL_LUMINANCE12_EXT 0x8041
+#define LOCAL_GL_LUMINANCE16 0x8042
+#define LOCAL_GL_LUMINANCE16F_ARB 0x881E
+#define LOCAL_GL_LUMINANCE16F_EXT 0x881E
+#define LOCAL_GL_LUMINANCE16I_EXT 0x8D8C
+#define LOCAL_GL_LUMINANCE16UI_EXT 0x8D7A
+#define LOCAL_GL_LUMINANCE16_ALPHA16 0x8048
+#define LOCAL_GL_LUMINANCE16_ALPHA16_EXT 0x8048
+#define LOCAL_GL_LUMINANCE16_ALPHA16_SNORM 0x901A
+#define LOCAL_GL_LUMINANCE16_EXT 0x8042
+#define LOCAL_GL_LUMINANCE16_SNORM 0x9019
+#define LOCAL_GL_LUMINANCE32F_ARB 0x8818
+#define LOCAL_GL_LUMINANCE32F_EXT 0x8818
+#define LOCAL_GL_LUMINANCE32I_EXT 0x8D86
+#define LOCAL_GL_LUMINANCE32UI_EXT 0x8D74
+#define LOCAL_GL_LUMINANCE4 0x803F
+#define LOCAL_GL_LUMINANCE4_ALPHA4 0x8043
+#define LOCAL_GL_LUMINANCE4_ALPHA4_EXT 0x8043
+#define LOCAL_GL_LUMINANCE4_ALPHA4_OES 0x8043
+#define LOCAL_GL_LUMINANCE4_EXT 0x803F
+#define LOCAL_GL_LUMINANCE6_ALPHA2 0x8044
+#define LOCAL_GL_LUMINANCE6_ALPHA2_EXT 0x8044
+#define LOCAL_GL_LUMINANCE8 0x8040
+#define LOCAL_GL_LUMINANCE8I_EXT 0x8D92
+#define LOCAL_GL_LUMINANCE8UI_EXT 0x8D80
+#define LOCAL_GL_LUMINANCE8_ALPHA8 0x8045
+#define LOCAL_GL_LUMINANCE8_ALPHA8_EXT 0x8045
+#define LOCAL_GL_LUMINANCE8_ALPHA8_OES 0x8045
+#define LOCAL_GL_LUMINANCE8_ALPHA8_SNORM 0x9016
+#define LOCAL_GL_LUMINANCE8_EXT 0x8040
+#define LOCAL_GL_LUMINANCE8_OES 0x8040
+#define LOCAL_GL_LUMINANCE8_SNORM 0x9015
+#define LOCAL_GL_LUMINANCE_ALPHA 0x190A
+#define LOCAL_GL_LUMINANCE_ALPHA16F_ARB 0x881F
+#define LOCAL_GL_LUMINANCE_ALPHA16F_EXT 0x881F
+#define LOCAL_GL_LUMINANCE_ALPHA16I_EXT 0x8D8D
+#define LOCAL_GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B
+#define LOCAL_GL_LUMINANCE_ALPHA32F_ARB 0x8819
+#define LOCAL_GL_LUMINANCE_ALPHA32F_EXT 0x8819
+#define LOCAL_GL_LUMINANCE_ALPHA32I_EXT 0x8D87
+#define LOCAL_GL_LUMINANCE_ALPHA32UI_EXT 0x8D75
+#define LOCAL_GL_LUMINANCE_ALPHA8I_EXT 0x8D93
+#define LOCAL_GL_LUMINANCE_ALPHA8UI_EXT 0x8D81
+#define LOCAL_GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F
+#define LOCAL_GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F
+#define LOCAL_GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819
+#define LOCAL_GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819
+#define LOCAL_GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D
+#define LOCAL_GL_LUMINANCE_ALPHA_SNORM 0x9012
+#define LOCAL_GL_LUMINANCE_FLOAT16_APPLE 0x881E
+#define LOCAL_GL_LUMINANCE_FLOAT16_ATI 0x881E
+#define LOCAL_GL_LUMINANCE_FLOAT32_APPLE 0x8818
+#define LOCAL_GL_LUMINANCE_FLOAT32_ATI 0x8818
+#define LOCAL_GL_LUMINANCE_INTEGER_EXT 0x8D9C
+#define LOCAL_GL_LUMINANCE_SNORM 0x9011
+#define LOCAL_GL_MAD_ATI 0x8968
+#define LOCAL_GL_MAGNITUDE_BIAS_NV 0x8718
+#define LOCAL_GL_MAGNITUDE_SCALE_NV 0x8712
+#define LOCAL_GL_MAJOR_VERSION 0x821B
+#define LOCAL_GL_MALI_PROGRAM_BINARY_ARM 0x8F61
+#define LOCAL_GL_MALI_SHADER_BINARY_ARM 0x8F60
+#define LOCAL_GL_MANUAL_GENERATE_MIPMAP 0x8294
+#define LOCAL_GL_MAP1_BINORMAL_EXT 0x8446
+#define LOCAL_GL_MAP1_COLOR_4 0x0D90
+#define LOCAL_GL_MAP1_GRID_DOMAIN 0x0DD0
+#define LOCAL_GL_MAP1_GRID_SEGMENTS 0x0DD1
+#define LOCAL_GL_MAP1_INDEX 0x0D91
+#define LOCAL_GL_MAP1_NORMAL 0x0D92
+#define LOCAL_GL_MAP1_TANGENT_EXT 0x8444
+#define LOCAL_GL_MAP1_TEXTURE_COORD_1 0x0D93
+#define LOCAL_GL_MAP1_TEXTURE_COORD_2 0x0D94
+#define LOCAL_GL_MAP1_TEXTURE_COORD_3 0x0D95
+#define LOCAL_GL_MAP1_TEXTURE_COORD_4 0x0D96
+#define LOCAL_GL_MAP1_VERTEX_3 0x0D97
+#define LOCAL_GL_MAP1_VERTEX_4 0x0D98
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668
+#define LOCAL_GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669
+#define LOCAL_GL_MAP2_BINORMAL_EXT 0x8447
+#define LOCAL_GL_MAP2_COLOR_4 0x0DB0
+#define LOCAL_GL_MAP2_GRID_DOMAIN 0x0DD2
+#define LOCAL_GL_MAP2_GRID_SEGMENTS 0x0DD3
+#define LOCAL_GL_MAP2_INDEX 0x0DB1
+#define LOCAL_GL_MAP2_NORMAL 0x0DB2
+#define LOCAL_GL_MAP2_TANGENT_EXT 0x8445
+#define LOCAL_GL_MAP2_TEXTURE_COORD_1 0x0DB3
+#define LOCAL_GL_MAP2_TEXTURE_COORD_2 0x0DB4
+#define LOCAL_GL_MAP2_TEXTURE_COORD_3 0x0DB5
+#define LOCAL_GL_MAP2_TEXTURE_COORD_4 0x0DB6
+#define LOCAL_GL_MAP2_VERTEX_3 0x0DB7
+#define LOCAL_GL_MAP2_VERTEX_4 0x0DB8
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678
+#define LOCAL_GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679
+#define LOCAL_GL_MAP_ATTRIB_U_ORDER_NV 0x86C3
+#define LOCAL_GL_MAP_ATTRIB_V_ORDER_NV 0x86C4
+#define LOCAL_GL_MAP_COHERENT_BIT 0x0080
+#define LOCAL_GL_MAP_COLOR 0x0D10
+#define LOCAL_GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
+#define LOCAL_GL_MAP_FLUSH_EXPLICIT_BIT_EXT 0x0010
+#define LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
+#define LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT_EXT 0x0008
+#define LOCAL_GL_MAP_INVALIDATE_RANGE_BIT 0x0004
+#define LOCAL_GL_MAP_INVALIDATE_RANGE_BIT_EXT 0x0004
+#define LOCAL_GL_MAP_PERSISTENT_BIT 0x0040
+#define LOCAL_GL_MAP_READ_BIT 0x0001
+#define LOCAL_GL_MAP_READ_BIT_EXT 0x0001
+#define LOCAL_GL_MAP_STENCIL 0x0D11
+#define LOCAL_GL_MAP_TESSELLATION_NV 0x86C2
+#define LOCAL_GL_MAP_UNSYNCHRONIZED_BIT 0x0020
+#define LOCAL_GL_MAP_UNSYNCHRONIZED_BIT_EXT 0x0020
+#define LOCAL_GL_MAP_WRITE_BIT 0x0002
+#define LOCAL_GL_MAP_WRITE_BIT_EXT 0x0002
+#define LOCAL_GL_MATERIAL_SIDE_HINT_PGI 0x1A22C
+#define LOCAL_GL_MATRIX0_ARB 0x88C0
+#define LOCAL_GL_MATRIX0_NV 0x8630
+#define LOCAL_GL_MATRIX10_ARB 0x88CA
+#define LOCAL_GL_MATRIX11_ARB 0x88CB
+#define LOCAL_GL_MATRIX12_ARB 0x88CC
+#define LOCAL_GL_MATRIX13_ARB 0x88CD
+#define LOCAL_GL_MATRIX14_ARB 0x88CE
+#define LOCAL_GL_MATRIX15_ARB 0x88CF
+#define LOCAL_GL_MATRIX16_ARB 0x88D0
+#define LOCAL_GL_MATRIX17_ARB 0x88D1
+#define LOCAL_GL_MATRIX18_ARB 0x88D2
+#define LOCAL_GL_MATRIX19_ARB 0x88D3
+#define LOCAL_GL_MATRIX1_ARB 0x88C1
+#define LOCAL_GL_MATRIX1_NV 0x8631
+#define LOCAL_GL_MATRIX20_ARB 0x88D4
+#define LOCAL_GL_MATRIX21_ARB 0x88D5
+#define LOCAL_GL_MATRIX22_ARB 0x88D6
+#define LOCAL_GL_MATRIX23_ARB 0x88D7
+#define LOCAL_GL_MATRIX24_ARB 0x88D8
+#define LOCAL_GL_MATRIX25_ARB 0x88D9
+#define LOCAL_GL_MATRIX26_ARB 0x88DA
+#define LOCAL_GL_MATRIX27_ARB 0x88DB
+#define LOCAL_GL_MATRIX28_ARB 0x88DC
+#define LOCAL_GL_MATRIX29_ARB 0x88DD
+#define LOCAL_GL_MATRIX2_ARB 0x88C2
+#define LOCAL_GL_MATRIX2_NV 0x8632
+#define LOCAL_GL_MATRIX30_ARB 0x88DE
+#define LOCAL_GL_MATRIX31_ARB 0x88DF
+#define LOCAL_GL_MATRIX3_ARB 0x88C3
+#define LOCAL_GL_MATRIX3_NV 0x8633
+#define LOCAL_GL_MATRIX4_ARB 0x88C4
+#define LOCAL_GL_MATRIX4_NV 0x8634
+#define LOCAL_GL_MATRIX5_ARB 0x88C5
+#define LOCAL_GL_MATRIX5_NV 0x8635
+#define LOCAL_GL_MATRIX6_ARB 0x88C6
+#define LOCAL_GL_MATRIX6_NV 0x8636
+#define LOCAL_GL_MATRIX7_ARB 0x88C7
+#define LOCAL_GL_MATRIX7_NV 0x8637
+#define LOCAL_GL_MATRIX8_ARB 0x88C8
+#define LOCAL_GL_MATRIX9_ARB 0x88C9
+#define LOCAL_GL_MATRIX_EXT 0x87C0
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_ARB 0x8844
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES 0x8B9E
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_OES 0x8844
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_POINTER_OES 0x8849
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_SIZE_OES 0x8846
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_STRIDE_OES 0x8848
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847
+#define LOCAL_GL_MATRIX_INDEX_ARRAY_TYPE_OES 0x8847
+#define LOCAL_GL_MATRIX_MODE 0x0BA0
+#define LOCAL_GL_MATRIX_PALETTE_ARB 0x8840
+#define LOCAL_GL_MATRIX_PALETTE_OES 0x8840
+#define LOCAL_GL_MATRIX_STRIDE 0x92FF
+#define LOCAL_GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000
+#define LOCAL_GL_MAT_AMBIENT_BIT_PGI 0x00100000
+#define LOCAL_GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000
+#define LOCAL_GL_MAT_DIFFUSE_BIT_PGI 0x00400000
+#define LOCAL_GL_MAT_EMISSION_BIT_PGI 0x00800000
+#define LOCAL_GL_MAT_SHININESS_BIT_PGI 0x02000000
+#define LOCAL_GL_MAT_SPECULAR_BIT_PGI 0x04000000
+#define LOCAL_GL_MAX 0x8008
+#define LOCAL_GL_MAX_3D_TEXTURE_SIZE 0x8073
+#define LOCAL_GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073
+#define LOCAL_GL_MAX_3D_TEXTURE_SIZE_OES 0x8073
+#define LOCAL_GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138
+#define LOCAL_GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405
+#define LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
+#define LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF
+#define LOCAL_GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360
+#define LOCAL_GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D
+#define LOCAL_GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361
+#define LOCAL_GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F
+#define LOCAL_GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC
+#define LOCAL_GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8
+#define LOCAL_GL_MAX_ATTRIB_STACK_DEPTH 0x0D35
+#define LOCAL_GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED
+#define LOCAL_GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B
+#define LOCAL_GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177
+#define LOCAL_GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178
+#define LOCAL_GL_MAX_CLIP_DISTANCES 0x0D32
+#define LOCAL_GL_MAX_CLIP_PLANES 0x0D32
+#define LOCAL_GL_MAX_CLIP_PLANES_IMG 0x0D32
+#define LOCAL_GL_MAX_COLOR_ATTACHMENTS 0x8CDF
+#define LOCAL_GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF
+#define LOCAL_GL_MAX_COLOR_ATTACHMENTS_NV 0x8CDF
+#define LOCAL_GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3
+#define LOCAL_GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3
+#define LOCAL_GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E
+#define LOCAL_GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7
+#define LOCAL_GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1
+#define LOCAL_GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266
+#define LOCAL_GL_MAX_COMBINED_DIMENSIONS 0x8282
+#define LOCAL_GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33
+#define LOCAL_GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32
+#define LOCAL_GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF
+#define LOCAL_GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39
+#define LOCAL_GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39
+#define LOCAL_GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39
+#define LOCAL_GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC
+#define LOCAL_GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E
+#define LOCAL_GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F
+#define LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D
+#define LOCAL_GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E
+#define LOCAL_GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31
+#define LOCAL_GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265
+#define LOCAL_GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264
+#define LOCAL_GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB
+#define LOCAL_GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF
+#define LOCAL_GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD
+#define LOCAL_GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB
+#define LOCAL_GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262
+#define LOCAL_GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC
+#define LOCAL_GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB
+#define LOCAL_GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263
+#define LOCAL_GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344
+#define LOCAL_GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345
+#define LOCAL_GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE
+#define LOCAL_GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB
+#define LOCAL_GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF
+#define LOCAL_GL_MAX_CONVOLUTION_HEIGHT 0x801B
+#define LOCAL_GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B
+#define LOCAL_GL_MAX_CONVOLUTION_WIDTH 0x801A
+#define LOCAL_GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A
+#define LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C
+#define LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C
+#define LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES 0x851C
+#define LOCAL_GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C
+#define LOCAL_GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C
+#define LOCAL_GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144
+#define LOCAL_GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144
+#define LOCAL_GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
+#define LOCAL_GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144
+#define LOCAL_GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143
+#define LOCAL_GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143
+#define LOCAL_GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
+#define LOCAL_GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143
+#define LOCAL_GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1
+#define LOCAL_GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0
+#define LOCAL_GL_MAX_DEFORMATION_ORDER_SGIX 0x8197
+#define LOCAL_GL_MAX_DEPTH 0x8280
+#define LOCAL_GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F
+#define LOCAL_GL_MAX_DRAW_BUFFERS 0x8824
+#define LOCAL_GL_MAX_DRAW_BUFFERS_ARB 0x8824
+#define LOCAL_GL_MAX_DRAW_BUFFERS_ATI 0x8824
+#define LOCAL_GL_MAX_DRAW_BUFFERS_EXT 0x8824
+#define LOCAL_GL_MAX_DRAW_BUFFERS_NV 0x8824
+#define LOCAL_GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
+#define LOCAL_GL_MAX_ELEMENTS_INDICES 0x80E9
+#define LOCAL_GL_MAX_ELEMENTS_INDICES_EXT 0x80E9
+#define LOCAL_GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define LOCAL_GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8
+#define LOCAL_GL_MAX_ELEMENT_INDEX 0x8D6B
+#define LOCAL_GL_MAX_EVAL_ORDER 0x0D30
+#define LOCAL_GL_MAX_EXT 0x8008
+#define LOCAL_GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C
+#define LOCAL_GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6
+#define LOCAL_GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0
+#define LOCAL_GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3
+#define LOCAL_GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE
+#define LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
+#define LOCAL_GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C
+#define LOCAL_GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C
+#define LOCAL_GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404
+#define LOCAL_GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868
+#define LOCAL_GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA
+#define LOCAL_GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D
+#define LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49
+#define LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define LOCAL_GL_MAX_FRAMEBUFFER_HEIGHT 0x9316
+#define LOCAL_GL_MAX_FRAMEBUFFER_LAYERS 0x9317
+#define LOCAL_GL_MAX_FRAMEBUFFER_SAMPLES 0x9318
+#define LOCAL_GL_MAX_FRAMEBUFFER_WIDTH 0x9315
+#define LOCAL_GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D
+#define LOCAL_GL_MAX_GENERAL_COMBINERS_NV 0x854D
+#define LOCAL_GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5
+#define LOCAL_GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF
+#define LOCAL_GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4
+#define LOCAL_GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD
+#define LOCAL_GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123
+#define LOCAL_GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124
+#define LOCAL_GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0
+#define LOCAL_GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0
+#define LOCAL_GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
+#define LOCAL_GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A
+#define LOCAL_GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A
+#define LOCAL_GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7
+#define LOCAL_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29
+#define LOCAL_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29
+#define LOCAL_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29
+#define LOCAL_GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1
+#define LOCAL_GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1
+#define LOCAL_GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1
+#define LOCAL_GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C
+#define LOCAL_GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF
+#define LOCAL_GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF
+#define LOCAL_GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF
+#define LOCAL_GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD
+#define LOCAL_GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD
+#define LOCAL_GL_MAX_HEIGHT 0x827F
+#define LOCAL_GL_MAX_IMAGE_SAMPLES 0x906D
+#define LOCAL_GL_MAX_IMAGE_SAMPLES_EXT 0x906D
+#define LOCAL_GL_MAX_IMAGE_UNITS 0x8F38
+#define LOCAL_GL_MAX_IMAGE_UNITS_EXT 0x8F38
+#define LOCAL_GL_MAX_INTEGER_SAMPLES 0x9110
+#define LOCAL_GL_MAX_LABEL_LENGTH 0x82E8
+#define LOCAL_GL_MAX_LABEL_LENGTH_KHR 0x82E8
+#define LOCAL_GL_MAX_LAYERS 0x8281
+#define LOCAL_GL_MAX_LIGHTS 0x0D31
+#define LOCAL_GL_MAX_LIST_NESTING 0x0B31
+#define LOCAL_GL_MAX_MAP_TESSELLATION_NV 0x86D6
+#define LOCAL_GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841
+#define LOCAL_GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36
+#define LOCAL_GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11
+#define LOCAL_GL_MAX_MULTIVIEW_BUFFERS_EXT 0x90F2
+#define LOCAL_GL_MAX_NAME_LENGTH 0x92F6
+#define LOCAL_GL_MAX_NAME_STACK_DEPTH 0x0D37
+#define LOCAL_GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7
+#define LOCAL_GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8
+#define LOCAL_GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA
+#define LOCAL_GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD
+#define LOCAL_GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE
+#define LOCAL_GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC
+#define LOCAL_GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB
+#define LOCAL_GL_MAX_PALETTE_MATRICES_ARB 0x8842
+#define LOCAL_GL_MAX_PALETTE_MATRICES_OES 0x8842
+#define LOCAL_GL_MAX_PATCH_VERTICES 0x8E7D
+#define LOCAL_GL_MAX_PIXEL_MAP_TABLE 0x0D34
+#define LOCAL_GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337
+#define LOCAL_GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1
+#define LOCAL_GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1
+#define LOCAL_GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B
+#define LOCAL_GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD
+#define LOCAL_GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908
+#define LOCAL_GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5
+#define LOCAL_GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5
+#define LOCAL_GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4
+#define LOCAL_GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5
+#define LOCAL_GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6
+#define LOCAL_GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6
+#define LOCAL_GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1
+#define LOCAL_GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4
+#define LOCAL_GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8
+#define LOCAL_GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7
+#define LOCAL_GL_MAX_PROGRAM_MATRICES_ARB 0x862F
+#define LOCAL_GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810
+#define LOCAL_GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F
+#define LOCAL_GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27
+#define LOCAL_GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9
+#define LOCAL_GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0
+#define LOCAL_GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1
+#define LOCAL_GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8
+#define LOCAL_GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909
+#define LOCAL_GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45
+#define LOCAL_GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44
+#define LOCAL_GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5
+#define LOCAL_GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905
+#define LOCAL_GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905
+#define LOCAL_GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905
+#define LOCAL_GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F
+#define LOCAL_GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F
+#define LOCAL_GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F
+#define LOCAL_GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F
+#define LOCAL_GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D
+#define LOCAL_GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C
+#define LOCAL_GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28
+#define LOCAL_GL_MAX_PROJECTION_STACK_DEPTH 0x0D38
+#define LOCAL_GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7
+#define LOCAL_GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8
+#define LOCAL_GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
+#define LOCAL_GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
+#define LOCAL_GL_MAX_RENDERBUFFER_SIZE 0x84E8
+#define LOCAL_GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
+#define LOCAL_GL_MAX_RENDERBUFFER_SIZE_OES 0x84E8
+#define LOCAL_GL_MAX_SAMPLES 0x8D57
+#define LOCAL_GL_MAX_SAMPLES_ANGLE 0x8D57
+#define LOCAL_GL_MAX_SAMPLES_APPLE 0x8D57
+#define LOCAL_GL_MAX_SAMPLES_EXT 0x8D57
+#define LOCAL_GL_MAX_SAMPLES_IMG 0x9135
+#define LOCAL_GL_MAX_SAMPLES_NV 0x8D57
+#define LOCAL_GL_MAX_SAMPLE_MASK_WORDS 0x8E59
+#define LOCAL_GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59
+#define LOCAL_GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
+#define LOCAL_GL_MAX_SERVER_WAIT_TIMEOUT_APPLE 0x9111
+#define LOCAL_GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35
+#define LOCAL_GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE
+#define LOCAL_GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD
+#define LOCAL_GL_MAX_SHININESS_NV 0x8504
+#define LOCAL_GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199
+#define LOCAL_GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199
+#define LOCAL_GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A
+#define LOCAL_GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A
+#define LOCAL_GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198
+#define LOCAL_GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198
+#define LOCAL_GL_MAX_SPOT_EXPONENT_NV 0x8505
+#define LOCAL_GL_MAX_SUBROUTINES 0x8DE7
+#define LOCAL_GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8
+#define LOCAL_GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3
+#define LOCAL_GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD
+#define LOCAL_GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB
+#define LOCAL_GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C
+#define LOCAL_GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83
+#define LOCAL_GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8
+#define LOCAL_GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81
+#define LOCAL_GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85
+#define LOCAL_GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89
+#define LOCAL_GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F
+#define LOCAL_GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4
+#define LOCAL_GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE
+#define LOCAL_GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC
+#define LOCAL_GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D
+#define LOCAL_GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86
+#define LOCAL_GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9
+#define LOCAL_GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82
+#define LOCAL_GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A
+#define LOCAL_GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80
+#define LOCAL_GL_MAX_TESS_GEN_LEVEL 0x8E7E
+#define LOCAL_GL_MAX_TESS_PATCH_COMPONENTS 0x8E84
+#define LOCAL_GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B
+#define LOCAL_GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B
+#define LOCAL_GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B
+#define LOCAL_GL_MAX_TEXTURE_COORDS 0x8871
+#define LOCAL_GL_MAX_TEXTURE_COORDS_ARB 0x8871
+#define LOCAL_GL_MAX_TEXTURE_COORDS_NV 0x8871
+#define LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872
+#define LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872
+#define LOCAL_GL_MAX_TEXTURE_LOD_BIAS 0x84FD
+#define LOCAL_GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD
+#define LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#define LOCAL_GL_MAX_TEXTURE_SIZE 0x0D33
+#define LOCAL_GL_MAX_TEXTURE_STACK_DEPTH 0x0D39
+#define LOCAL_GL_MAX_TEXTURE_UNITS 0x84E2
+#define LOCAL_GL_MAX_TEXTURE_UNITS_ARB 0x84E2
+#define LOCAL_GL_MAX_TRACK_MATRICES_NV 0x862F
+#define LOCAL_GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80
+#define LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80
+#define LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30
+#define LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F
+#define LOCAL_GL_MAX_UNIFORM_LOCATIONS 0x826E
+#define LOCAL_GL_MAX_VARYING_COMPONENTS 0x8B4B
+#define LOCAL_GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B
+#define LOCAL_GL_MAX_VARYING_FLOATS 0x8B4B
+#define LOCAL_GL_MAX_VARYING_FLOATS_ARB 0x8B4B
+#define LOCAL_GL_MAX_VARYING_VECTORS 0x8DFC
+#define LOCAL_GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520
+#define LOCAL_GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2
+#define LOCAL_GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC
+#define LOCAL_GL_MAX_VERTEX_ATTRIBS 0x8869
+#define LOCAL_GL_MAX_VERTEX_ATTRIBS_ARB 0x8869
+#define LOCAL_GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA
+#define LOCAL_GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9
+#define LOCAL_GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5
+#define LOCAL_GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2
+#define LOCAL_GL_MAX_VERTEX_HINT_PGI 0x1A22D
+#define LOCAL_GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA
+#define LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
+#define LOCAL_GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5
+#define LOCAL_GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7
+#define LOCAL_GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9
+#define LOCAL_GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8
+#define LOCAL_GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6
+#define LOCAL_GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6
+#define LOCAL_GL_MAX_VERTEX_STREAMS 0x8E71
+#define LOCAL_GL_MAX_VERTEX_STREAMS_ATI 0x876B
+#define LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C
+#define LOCAL_GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B
+#define LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A
+#define LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define LOCAL_GL_MAX_VERTEX_UNITS_ARB 0x86A4
+#define LOCAL_GL_MAX_VERTEX_UNITS_OES 0x86A4
+#define LOCAL_GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE
+#define LOCAL_GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE
+#define LOCAL_GL_MAX_VIEWPORTS 0x825B
+#define LOCAL_GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define LOCAL_GL_MAX_WIDTH 0x827E
+#define LOCAL_GL_MEDIUM_FLOAT 0x8DF1
+#define LOCAL_GL_MEDIUM_INT 0x8DF4
+#define LOCAL_GL_MIN 0x8007
+#define LOCAL_GL_MINMAX 0x802E
+#define LOCAL_GL_MINMAX_EXT 0x802E
+#define LOCAL_GL_MINMAX_FORMAT 0x802F
+#define LOCAL_GL_MINMAX_FORMAT_EXT 0x802F
+#define LOCAL_GL_MINMAX_SINK 0x8030
+#define LOCAL_GL_MINMAX_SINK_EXT 0x8030
+#define LOCAL_GL_MINOR_VERSION 0x821C
+#define LOCAL_GL_MINUS_CLAMPED_NV 0x92B3
+#define LOCAL_GL_MINUS_NV 0x929F
+#define LOCAL_GL_MIN_EXT 0x8007
+#define LOCAL_GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B
+#define LOCAL_GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B
+#define LOCAL_GL_MIN_LOD_WARNING_AMD 0x919C
+#define LOCAL_GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC
+#define LOCAL_GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904
+#define LOCAL_GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904
+#define LOCAL_GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904
+#define LOCAL_GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E
+#define LOCAL_GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E
+#define LOCAL_GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E
+#define LOCAL_GL_MIN_SAMPLE_SHADING_VALUE 0x8C37
+#define LOCAL_GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37
+#define LOCAL_GL_MIN_SPARSE_LEVEL_AMD 0x919B
+#define LOCAL_GL_MIN_SPARSE_LEVEL_ARB 0x919B
+#define LOCAL_GL_MIPMAP 0x8293
+#define LOCAL_GL_MIRRORED_REPEAT 0x8370
+#define LOCAL_GL_MIRRORED_REPEAT_ARB 0x8370
+#define LOCAL_GL_MIRRORED_REPEAT_IBM 0x8370
+#define LOCAL_GL_MIRRORED_REPEAT_OES 0x8370
+#define LOCAL_GL_MIRROR_CLAMP_ATI 0x8742
+#define LOCAL_GL_MIRROR_CLAMP_EXT 0x8742
+#define LOCAL_GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912
+#define LOCAL_GL_MIRROR_CLAMP_TO_EDGE 0x8743
+#define LOCAL_GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743
+#define LOCAL_GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743
+#define LOCAL_GL_MITER_REVERT_NV 0x90A7
+#define LOCAL_GL_MITER_TRUNCATE_NV 0x90A8
+#define LOCAL_GL_MODELVIEW 0x1700
+#define LOCAL_GL_MODELVIEW0_ARB 0x1700
+#define LOCAL_GL_MODELVIEW0_EXT 0x1700
+#define LOCAL_GL_MODELVIEW0_MATRIX_EXT 0x0BA6
+#define LOCAL_GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3
+#define LOCAL_GL_MODELVIEW10_ARB 0x872A
+#define LOCAL_GL_MODELVIEW11_ARB 0x872B
+#define LOCAL_GL_MODELVIEW12_ARB 0x872C
+#define LOCAL_GL_MODELVIEW13_ARB 0x872D
+#define LOCAL_GL_MODELVIEW14_ARB 0x872E
+#define LOCAL_GL_MODELVIEW15_ARB 0x872F
+#define LOCAL_GL_MODELVIEW16_ARB 0x8730
+#define LOCAL_GL_MODELVIEW17_ARB 0x8731
+#define LOCAL_GL_MODELVIEW18_ARB 0x8732
+#define LOCAL_GL_MODELVIEW19_ARB 0x8733
+#define LOCAL_GL_MODELVIEW1_ARB 0x850A
+#define LOCAL_GL_MODELVIEW1_EXT 0x850A
+#define LOCAL_GL_MODELVIEW1_MATRIX_EXT 0x8506
+#define LOCAL_GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502
+#define LOCAL_GL_MODELVIEW20_ARB 0x8734
+#define LOCAL_GL_MODELVIEW21_ARB 0x8735
+#define LOCAL_GL_MODELVIEW22_ARB 0x8736
+#define LOCAL_GL_MODELVIEW23_ARB 0x8737
+#define LOCAL_GL_MODELVIEW24_ARB 0x8738
+#define LOCAL_GL_MODELVIEW25_ARB 0x8739
+#define LOCAL_GL_MODELVIEW26_ARB 0x873A
+#define LOCAL_GL_MODELVIEW27_ARB 0x873B
+#define LOCAL_GL_MODELVIEW28_ARB 0x873C
+#define LOCAL_GL_MODELVIEW29_ARB 0x873D
+#define LOCAL_GL_MODELVIEW2_ARB 0x8722
+#define LOCAL_GL_MODELVIEW30_ARB 0x873E
+#define LOCAL_GL_MODELVIEW31_ARB 0x873F
+#define LOCAL_GL_MODELVIEW3_ARB 0x8723
+#define LOCAL_GL_MODELVIEW4_ARB 0x8724
+#define LOCAL_GL_MODELVIEW5_ARB 0x8725
+#define LOCAL_GL_MODELVIEW6_ARB 0x8726
+#define LOCAL_GL_MODELVIEW7_ARB 0x8727
+#define LOCAL_GL_MODELVIEW8_ARB 0x8728
+#define LOCAL_GL_MODELVIEW9_ARB 0x8729
+#define LOCAL_GL_MODELVIEW_MATRIX 0x0BA6
+#define LOCAL_GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES 0x898D
+#define LOCAL_GL_MODELVIEW_PROJECTION_NV 0x8629
+#define LOCAL_GL_MODELVIEW_STACK_DEPTH 0x0BA3
+#define LOCAL_GL_MODULATE 0x2100
+#define LOCAL_GL_MODULATE_ADD_ATI 0x8744
+#define LOCAL_GL_MODULATE_COLOR_IMG 0x8C04
+#define LOCAL_GL_MODULATE_SIGNED_ADD_ATI 0x8745
+#define LOCAL_GL_MODULATE_SUBTRACT_ATI 0x8746
+#define LOCAL_GL_MOVE_TO_CONTINUES_NV 0x90B6
+#define LOCAL_GL_MOVE_TO_NV 0x02
+#define LOCAL_GL_MOVE_TO_RESETS_NV 0x90B5
+#define LOCAL_GL_MOV_ATI 0x8961
+#define LOCAL_GL_MULT 0x0103
+#define LOCAL_GL_MULTIPLY_NV 0x9294
+#define LOCAL_GL_MULTISAMPLE 0x809D
+#define LOCAL_GL_MULTISAMPLE_3DFX 0x86B2
+#define LOCAL_GL_MULTISAMPLE_ARB 0x809D
+#define LOCAL_GL_MULTISAMPLE_BIT 0x20000000
+#define LOCAL_GL_MULTISAMPLE_BIT_3DFX 0x20000000
+#define LOCAL_GL_MULTISAMPLE_BIT_ARB 0x20000000
+#define LOCAL_GL_MULTISAMPLE_BIT_EXT 0x20000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000
+#define LOCAL_GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000
+#define LOCAL_GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12
+#define LOCAL_GL_MULTISAMPLE_EXT 0x809D
+#define LOCAL_GL_MULTISAMPLE_FILTER_HINT_NV 0x8534
+#define LOCAL_GL_MULTISAMPLE_SGIS 0x809D
+#define LOCAL_GL_MULTIVIEW_EXT 0x90F1
+#define LOCAL_GL_MUL_ATI 0x8964
+#define LOCAL_GL_MVP_MATRIX_EXT 0x87E3
+#define LOCAL_GL_N3F_V3F 0x2A25
+#define LOCAL_GL_NAMED_STRING_LENGTH_ARB 0x8DE9
+#define LOCAL_GL_NAMED_STRING_TYPE_ARB 0x8DEA
+#define LOCAL_GL_NAME_LENGTH 0x92F9
+#define LOCAL_GL_NAME_STACK_DEPTH 0x0D70
+#define LOCAL_GL_NAND 0x150E
+#define LOCAL_GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203
+#define LOCAL_GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204
+#define LOCAL_GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202
+#define LOCAL_GL_NEAREST 0x2600
+#define LOCAL_GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E
+#define LOCAL_GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D
+#define LOCAL_GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define LOCAL_GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define LOCAL_GL_NEGATE_BIT_ATI 0x00000004
+#define LOCAL_GL_NEGATIVE_ONE_EXT 0x87DF
+#define LOCAL_GL_NEGATIVE_W_EXT 0x87DC
+#define LOCAL_GL_NEGATIVE_X_EXT 0x87D9
+#define LOCAL_GL_NEGATIVE_Y_EXT 0x87DA
+#define LOCAL_GL_NEGATIVE_Z_EXT 0x87DB
+#define LOCAL_GL_NEVER 0x0200
+#define LOCAL_GL_NEXT_BUFFER_NV -2
+#define LOCAL_GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025
+#define LOCAL_GL_NICEST 0x1102
+#define LOCAL_GL_NONE 0
+#define LOCAL_GL_NONE_OES 0
+#define LOCAL_GL_NOOP 0x1505
+#define LOCAL_GL_NOR 0x1508
+#define LOCAL_GL_NORMALIZE 0x0BA1
+#define LOCAL_GL_NORMALIZED_RANGE_EXT 0x87E0
+#define LOCAL_GL_NORMAL_ARRAY 0x8075
+#define LOCAL_GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22
+#define LOCAL_GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897
+#define LOCAL_GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897
+#define LOCAL_GL_NORMAL_ARRAY_COUNT_EXT 0x8080
+#define LOCAL_GL_NORMAL_ARRAY_EXT 0x8075
+#define LOCAL_GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C
+#define LOCAL_GL_NORMAL_ARRAY_LIST_IBM 103071
+#define LOCAL_GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081
+#define LOCAL_GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6
+#define LOCAL_GL_NORMAL_ARRAY_POINTER 0x808F
+#define LOCAL_GL_NORMAL_ARRAY_POINTER_EXT 0x808F
+#define LOCAL_GL_NORMAL_ARRAY_STRIDE 0x807F
+#define LOCAL_GL_NORMAL_ARRAY_STRIDE_EXT 0x807F
+#define LOCAL_GL_NORMAL_ARRAY_TYPE 0x807E
+#define LOCAL_GL_NORMAL_ARRAY_TYPE_EXT 0x807E
+#define LOCAL_GL_NORMAL_BIT_PGI 0x08000000
+#define LOCAL_GL_NORMAL_MAP 0x8511
+#define LOCAL_GL_NORMAL_MAP_ARB 0x8511
+#define LOCAL_GL_NORMAL_MAP_EXT 0x8511
+#define LOCAL_GL_NORMAL_MAP_NV 0x8511
+#define LOCAL_GL_NORMAL_MAP_OES 0x8511
+#define LOCAL_GL_NOTEQUAL 0x0205
+#define LOCAL_GL_NO_ERROR 0
+#define LOCAL_GL_NO_RESET_NOTIFICATION_ARB 0x8261
+#define LOCAL_GL_NO_RESET_NOTIFICATION_EXT 0x8261
+#define LOCAL_GL_NUM_ACTIVE_VARIABLES 0x9304
+#define LOCAL_GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A
+#define LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2
+#define LOCAL_GL_NUM_EXTENSIONS 0x821D
+#define LOCAL_GL_NUM_FILL_STREAMS_NV 0x8E29
+#define LOCAL_GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F
+#define LOCAL_GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E
+#define LOCAL_GL_NUM_GENERAL_COMBINERS_NV 0x854E
+#define LOCAL_GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973
+#define LOCAL_GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971
+#define LOCAL_GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972
+#define LOCAL_GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974
+#define LOCAL_GL_NUM_PASSES_ATI 0x8970
+#define LOCAL_GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
+#define LOCAL_GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#define LOCAL_GL_NUM_SAMPLE_COUNTS 0x9380
+#define LOCAL_GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#define LOCAL_GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9
+#define LOCAL_GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024
+#define LOCAL_GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8
+#define LOCAL_GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89
+#define LOCAL_GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A
+#define LOCAL_GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86
+#define LOCAL_GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87
+#define LOCAL_GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85
+#define LOCAL_GL_OBJECT_BUFFER_SIZE_ATI 0x8764
+#define LOCAL_GL_OBJECT_BUFFER_USAGE_ATI 0x8765
+#define LOCAL_GL_OBJECT_COMPILE_STATUS_ARB 0x8B81
+#define LOCAL_GL_OBJECT_DELETE_STATUS_ARB 0x8B80
+#define LOCAL_GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3
+#define LOCAL_GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1
+#define LOCAL_GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84
+#define LOCAL_GL_OBJECT_LINEAR 0x2401
+#define LOCAL_GL_OBJECT_LINE_SGIS 0x81F7
+#define LOCAL_GL_OBJECT_LINK_STATUS_ARB 0x8B82
+#define LOCAL_GL_OBJECT_PLANE 0x2501
+#define LOCAL_GL_OBJECT_POINT_SGIS 0x81F5
+#define LOCAL_GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88
+#define LOCAL_GL_OBJECT_SUBTYPE_ARB 0x8B4F
+#define LOCAL_GL_OBJECT_TYPE 0x9112
+#define LOCAL_GL_OBJECT_TYPE_APPLE 0x9112
+#define LOCAL_GL_OBJECT_TYPE_ARB 0x8B4E
+#define LOCAL_GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83
+#define LOCAL_GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F
+#define LOCAL_GL_OCCLUSION_TEST_HP 0x8165
+#define LOCAL_GL_OCCLUSION_TEST_RESULT_HP 0x8166
+#define LOCAL_GL_OFFSET 0x92FC
+#define LOCAL_GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856
+#define LOCAL_GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857
+#define LOCAL_GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854
+#define LOCAL_GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855
+#define LOCAL_GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850
+#define LOCAL_GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851
+#define LOCAL_GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852
+#define LOCAL_GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853
+#define LOCAL_GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3
+#define LOCAL_GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1
+#define LOCAL_GL_OFFSET_TEXTURE_2D_NV 0x86E8
+#define LOCAL_GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2
+#define LOCAL_GL_OFFSET_TEXTURE_BIAS_NV 0x86E3
+#define LOCAL_GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1
+#define LOCAL_GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C
+#define LOCAL_GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D
+#define LOCAL_GL_OFFSET_TEXTURE_SCALE_NV 0x86E2
+#define LOCAL_GL_ONE 1
+#define LOCAL_GL_ONE_EXT 0x87DE
+#define LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004
+#define LOCAL_GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define LOCAL_GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002
+#define LOCAL_GL_ONE_MINUS_DST_ALPHA 0x0305
+#define LOCAL_GL_ONE_MINUS_DST_COLOR 0x0307
+#define LOCAL_GL_ONE_MINUS_SRC1_ALPHA 0x88FB
+#define LOCAL_GL_ONE_MINUS_SRC1_COLOR 0x88FA
+#define LOCAL_GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define LOCAL_GL_ONE_MINUS_SRC_COLOR 0x0301
+#define LOCAL_GL_OPERAND0_ALPHA 0x8598
+#define LOCAL_GL_OPERAND0_ALPHA_ARB 0x8598
+#define LOCAL_GL_OPERAND0_ALPHA_EXT 0x8598
+#define LOCAL_GL_OPERAND0_RGB 0x8590
+#define LOCAL_GL_OPERAND0_RGB_ARB 0x8590
+#define LOCAL_GL_OPERAND0_RGB_EXT 0x8590
+#define LOCAL_GL_OPERAND1_ALPHA 0x8599
+#define LOCAL_GL_OPERAND1_ALPHA_ARB 0x8599
+#define LOCAL_GL_OPERAND1_ALPHA_EXT 0x8599
+#define LOCAL_GL_OPERAND1_RGB 0x8591
+#define LOCAL_GL_OPERAND1_RGB_ARB 0x8591
+#define LOCAL_GL_OPERAND1_RGB_EXT 0x8591
+#define LOCAL_GL_OPERAND2_ALPHA 0x859A
+#define LOCAL_GL_OPERAND2_ALPHA_ARB 0x859A
+#define LOCAL_GL_OPERAND2_ALPHA_EXT 0x859A
+#define LOCAL_GL_OPERAND2_RGB 0x8592
+#define LOCAL_GL_OPERAND2_RGB_ARB 0x8592
+#define LOCAL_GL_OPERAND2_RGB_EXT 0x8592
+#define LOCAL_GL_OPERAND3_ALPHA_NV 0x859B
+#define LOCAL_GL_OPERAND3_RGB_NV 0x8593
+#define LOCAL_GL_OP_ADD_EXT 0x8787
+#define LOCAL_GL_OP_CLAMP_EXT 0x878E
+#define LOCAL_GL_OP_CROSS_PRODUCT_EXT 0x8797
+#define LOCAL_GL_OP_DOT3_EXT 0x8784
+#define LOCAL_GL_OP_DOT4_EXT 0x8785
+#define LOCAL_GL_OP_EXP_BASE_2_EXT 0x8791
+#define LOCAL_GL_OP_FLOOR_EXT 0x878F
+#define LOCAL_GL_OP_FRAC_EXT 0x8789
+#define LOCAL_GL_OP_INDEX_EXT 0x8782
+#define LOCAL_GL_OP_LOG_BASE_2_EXT 0x8792
+#define LOCAL_GL_OP_MADD_EXT 0x8788
+#define LOCAL_GL_OP_MAX_EXT 0x878A
+#define LOCAL_GL_OP_MIN_EXT 0x878B
+#define LOCAL_GL_OP_MOV_EXT 0x8799
+#define LOCAL_GL_OP_MULTIPLY_MATRIX_EXT 0x8798
+#define LOCAL_GL_OP_MUL_EXT 0x8786
+#define LOCAL_GL_OP_NEGATE_EXT 0x8783
+#define LOCAL_GL_OP_POWER_EXT 0x8793
+#define LOCAL_GL_OP_RECIP_EXT 0x8794
+#define LOCAL_GL_OP_RECIP_SQRT_EXT 0x8795
+#define LOCAL_GL_OP_ROUND_EXT 0x8790
+#define LOCAL_GL_OP_SET_GE_EXT 0x878C
+#define LOCAL_GL_OP_SET_LT_EXT 0x878D
+#define LOCAL_GL_OP_SUB_EXT 0x8796
+#define LOCAL_GL_OR 0x1507
+#define LOCAL_GL_ORDER 0x0A01
+#define LOCAL_GL_OR_INVERTED 0x150D
+#define LOCAL_GL_OR_REVERSE 0x150B
+#define LOCAL_GL_OUTPUT_COLOR0_EXT 0x879B
+#define LOCAL_GL_OUTPUT_COLOR1_EXT 0x879C
+#define LOCAL_GL_OUTPUT_FOG_EXT 0x87BD
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5
+#define LOCAL_GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6
+#define LOCAL_GL_OUTPUT_VERTEX_EXT 0x879A
+#define LOCAL_GL_OUT_OF_MEMORY 0x0505
+#define LOCAL_GL_OVERLAY_NV 0x9296
+#define LOCAL_GL_PACK_ALIGNMENT 0x0D05
+#define LOCAL_GL_PACK_CMYK_HINT_EXT 0x800E
+#define LOCAL_GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D
+#define LOCAL_GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C
+#define LOCAL_GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E
+#define LOCAL_GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B
+#define LOCAL_GL_PACK_COMPRESSED_SIZE_SGIX 0x831C
+#define LOCAL_GL_PACK_IMAGE_DEPTH_SGIS 0x8131
+#define LOCAL_GL_PACK_IMAGE_HEIGHT 0x806C
+#define LOCAL_GL_PACK_IMAGE_HEIGHT_EXT 0x806C
+#define LOCAL_GL_PACK_INVERT_MESA 0x8758
+#define LOCAL_GL_PACK_LSB_FIRST 0x0D01
+#define LOCAL_GL_PACK_MAX_COMPRESSED_SIZE_SGIX 0x831B
+#define LOCAL_GL_PACK_RESAMPLE_OML 0x8984
+#define LOCAL_GL_PACK_RESAMPLE_SGIX 0x842C
+#define LOCAL_GL_PACK_REVERSE_ROW_ORDER_ANGLE 0x93A4
+#define LOCAL_GL_PACK_ROW_BYTES_APPLE 0x8A15
+#define LOCAL_GL_PACK_ROW_LENGTH 0x0D02
+#define LOCAL_GL_PACK_SKIP_IMAGES 0x806B
+#define LOCAL_GL_PACK_SKIP_IMAGES_EXT 0x806B
+#define LOCAL_GL_PACK_SKIP_PIXELS 0x0D04
+#define LOCAL_GL_PACK_SKIP_ROWS 0x0D03
+#define LOCAL_GL_PACK_SKIP_VOLUMES_SGIS 0x8130
+#define LOCAL_GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0
+#define LOCAL_GL_PACK_SWAP_BYTES 0x0D00
+#define LOCAL_GL_PALETTE4_R5_G6_B5_OES 0x8B92
+#define LOCAL_GL_PALETTE4_RGB5_A1_OES 0x8B94
+#define LOCAL_GL_PALETTE4_RGB8_OES 0x8B90
+#define LOCAL_GL_PALETTE4_RGBA4_OES 0x8B93
+#define LOCAL_GL_PALETTE4_RGBA8_OES 0x8B91
+#define LOCAL_GL_PALETTE8_R5_G6_B5_OES 0x8B97
+#define LOCAL_GL_PALETTE8_RGB5_A1_OES 0x8B99
+#define LOCAL_GL_PALETTE8_RGB8_OES 0x8B95
+#define LOCAL_GL_PALETTE8_RGBA4_OES 0x8B98
+#define LOCAL_GL_PALETTE8_RGBA8_OES 0x8B96
+#define LOCAL_GL_PARALLEL_ARRAYS_INTEL 0x83F4
+#define LOCAL_GL_PARAMETER_BUFFER_ARB 0x80EE
+#define LOCAL_GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF
+#define LOCAL_GL_PARTIAL_SUCCESS_NV 0x902E
+#define LOCAL_GL_PASS_THROUGH_NV 0x86E6
+#define LOCAL_GL_PASS_THROUGH_TOKEN 0x0700
+#define LOCAL_GL_PATCHES 0x000E
+#define LOCAL_GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73
+#define LOCAL_GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74
+#define LOCAL_GL_PATCH_VERTICES 0x8E72
+#define LOCAL_GL_PATH_CLIENT_LENGTH_NV 0x907F
+#define LOCAL_GL_PATH_COMMAND_COUNT_NV 0x909D
+#define LOCAL_GL_PATH_COMPUTED_LENGTH_NV 0x90A0
+#define LOCAL_GL_PATH_COORD_COUNT_NV 0x909E
+#define LOCAL_GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF
+#define LOCAL_GL_PATH_DASH_ARRAY_COUNT_NV 0x909F
+#define LOCAL_GL_PATH_DASH_CAPS_NV 0x907B
+#define LOCAL_GL_PATH_DASH_OFFSET_NV 0x907E
+#define LOCAL_GL_PATH_DASH_OFFSET_RESET_NV 0x90B4
+#define LOCAL_GL_PATH_END_CAPS_NV 0x9076
+#define LOCAL_GL_PATH_ERROR_POSITION_NV 0x90AB
+#define LOCAL_GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1
+#define LOCAL_GL_PATH_FILL_COVER_MODE_NV 0x9082
+#define LOCAL_GL_PATH_FILL_MASK_NV 0x9081
+#define LOCAL_GL_PATH_FILL_MODE_NV 0x9080
+#define LOCAL_GL_PATH_FOG_GEN_MODE_NV 0x90AC
+#define LOCAL_GL_PATH_FORMAT_PS_NV 0x9071
+#define LOCAL_GL_PATH_FORMAT_SVG_NV 0x9070
+#define LOCAL_GL_PATH_GEN_COEFF_NV 0x90B1
+#define LOCAL_GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2
+#define LOCAL_GL_PATH_GEN_COMPONENTS_NV 0x90B3
+#define LOCAL_GL_PATH_GEN_MODE_NV 0x90B0
+#define LOCAL_GL_PATH_INITIAL_DASH_CAP_NV 0x907C
+#define LOCAL_GL_PATH_INITIAL_END_CAP_NV 0x9077
+#define LOCAL_GL_PATH_JOIN_STYLE_NV 0x9079
+#define LOCAL_GL_PATH_MITER_LIMIT_NV 0x907A
+#define LOCAL_GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A
+#define LOCAL_GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD
+#define LOCAL_GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE
+#define LOCAL_GL_PATH_STENCIL_FUNC_NV 0x90B7
+#define LOCAL_GL_PATH_STENCIL_REF_NV 0x90B8
+#define LOCAL_GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9
+#define LOCAL_GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2
+#define LOCAL_GL_PATH_STROKE_COVER_MODE_NV 0x9083
+#define LOCAL_GL_PATH_STROKE_MASK_NV 0x9084
+#define LOCAL_GL_PATH_STROKE_WIDTH_NV 0x9075
+#define LOCAL_GL_PATH_TERMINAL_DASH_CAP_NV 0x907D
+#define LOCAL_GL_PATH_TERMINAL_END_CAP_NV 0x9078
+#define LOCAL_GL_PERCENTAGE_AMD 0x8BC3
+#define LOCAL_GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0
+#define LOCAL_GL_PERFMON_RESULT_AMD 0x8BC6
+#define LOCAL_GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4
+#define LOCAL_GL_PERFMON_RESULT_SIZE_AMD 0x8BC5
+#define LOCAL_GL_PERFORMANCE_MONITOR_AMD 0x9152
+#define LOCAL_GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC
+#define LOCAL_GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB
+#define LOCAL_GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA
+#define LOCAL_GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8
+#define LOCAL_GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9
+#define LOCAL_GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF
+#define LOCAL_GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1
+#define LOCAL_GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2
+#define LOCAL_GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0
+#define LOCAL_GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE
+#define LOCAL_GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4
+#define LOCAL_GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3
+#define LOCAL_GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5
+#define LOCAL_GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9
+#define LOCAL_GL_PERFQUERY_FLUSH_INTEL 0x83FA
+#define LOCAL_GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001
+#define LOCAL_GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500
+#define LOCAL_GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD
+#define LOCAL_GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000
+#define LOCAL_GL_PERFQUERY_WAIT_INTEL 0x83FB
+#define LOCAL_GL_PERSPECTIVE_CORRECTION_HINT 0x0C50
+#define LOCAL_GL_PERTURB_EXT 0x85AE
+#define LOCAL_GL_PER_STAGE_CONSTANTS_NV 0x8535
+#define LOCAL_GL_PHONG_HINT_WIN 0x80EB
+#define LOCAL_GL_PHONG_WIN 0x80EA
+#define LOCAL_GL_PINLIGHT_NV 0x92A8
+#define LOCAL_GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
+#define LOCAL_GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080
+#define LOCAL_GL_PIXEL_COUNTER_BITS_NV 0x8864
+#define LOCAL_GL_PIXEL_COUNT_AVAILABLE_NV 0x8867
+#define LOCAL_GL_PIXEL_COUNT_NV 0x8866
+#define LOCAL_GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333
+#define LOCAL_GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355
+#define LOCAL_GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354
+#define LOCAL_GL_PIXEL_GROUP_COLOR_SGIS 0x8356
+#define LOCAL_GL_PIXEL_MAG_FILTER_EXT 0x8331
+#define LOCAL_GL_PIXEL_MAP_A_TO_A 0x0C79
+#define LOCAL_GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9
+#define LOCAL_GL_PIXEL_MAP_B_TO_B 0x0C78
+#define LOCAL_GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8
+#define LOCAL_GL_PIXEL_MAP_G_TO_G 0x0C77
+#define LOCAL_GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7
+#define LOCAL_GL_PIXEL_MAP_I_TO_A 0x0C75
+#define LOCAL_GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5
+#define LOCAL_GL_PIXEL_MAP_I_TO_B 0x0C74
+#define LOCAL_GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4
+#define LOCAL_GL_PIXEL_MAP_I_TO_G 0x0C73
+#define LOCAL_GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3
+#define LOCAL_GL_PIXEL_MAP_I_TO_I 0x0C70
+#define LOCAL_GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0
+#define LOCAL_GL_PIXEL_MAP_I_TO_R 0x0C72
+#define LOCAL_GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2
+#define LOCAL_GL_PIXEL_MAP_R_TO_R 0x0C76
+#define LOCAL_GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6
+#define LOCAL_GL_PIXEL_MAP_S_TO_S 0x0C71
+#define LOCAL_GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1
+#define LOCAL_GL_PIXEL_MIN_FILTER_EXT 0x8332
+#define LOCAL_GL_PIXEL_MODE_BIT 0x00000020
+#define LOCAL_GL_PIXEL_PACK_BUFFER 0x88EB
+#define LOCAL_GL_PIXEL_PACK_BUFFER_ARB 0x88EB
+#define LOCAL_GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
+#define LOCAL_GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED
+#define LOCAL_GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED
+#define LOCAL_GL_PIXEL_PACK_BUFFER_EXT 0x88EB
+#define LOCAL_GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3
+#define LOCAL_GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4
+#define LOCAL_GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2
+#define LOCAL_GL_PIXEL_TEXTURE_SGIS 0x8353
+#define LOCAL_GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX 0x8189
+#define LOCAL_GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX 0x818A
+#define LOCAL_GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX 0x8188
+#define LOCAL_GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX 0x8187
+#define LOCAL_GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B
+#define LOCAL_GL_PIXEL_TEX_GEN_Q_CEILING_SGIX 0x8184
+#define LOCAL_GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX 0x8186
+#define LOCAL_GL_PIXEL_TEX_GEN_Q_ROUND_SGIX 0x8185
+#define LOCAL_GL_PIXEL_TEX_GEN_SGIX 0x8139
+#define LOCAL_GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E
+#define LOCAL_GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F
+#define LOCAL_GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145
+#define LOCAL_GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144
+#define LOCAL_GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143
+#define LOCAL_GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142
+#define LOCAL_GL_PIXEL_TILE_HEIGHT_SGIX 0x8141
+#define LOCAL_GL_PIXEL_TILE_WIDTH_SGIX 0x8140
+#define LOCAL_GL_PIXEL_TRANSFORM_2D_EXT 0x8330
+#define LOCAL_GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338
+#define LOCAL_GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF
+#define LOCAL_GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC
+#define LOCAL_GL_PLUS_CLAMPED_ALPHA_NV 0x92B2
+#define LOCAL_GL_PLUS_CLAMPED_NV 0x92B1
+#define LOCAL_GL_PLUS_DARKER_NV 0x9292
+#define LOCAL_GL_PLUS_NV 0x9291
+#define LOCAL_GL_PN_TRIANGLES_ATI 0x87F0
+#define LOCAL_GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3
+#define LOCAL_GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7
+#define LOCAL_GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8
+#define LOCAL_GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2
+#define LOCAL_GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6
+#define LOCAL_GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5
+#define LOCAL_GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4
+#define LOCAL_GL_POINT 0x1B00
+#define LOCAL_GL_POINTS 0x0000
+#define LOCAL_GL_POINT_BIT 0x00000002
+#define LOCAL_GL_POINT_DISTANCE_ATTENUATION 0x8129
+#define LOCAL_GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129
+#define LOCAL_GL_POINT_FADE_THRESHOLD_SIZE 0x8128
+#define LOCAL_GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128
+#define LOCAL_GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128
+#define LOCAL_GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128
+#define LOCAL_GL_POINT_SIZE 0x0B11
+#define LOCAL_GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES 0x8B9F
+#define LOCAL_GL_POINT_SIZE_ARRAY_OES 0x8B9C
+#define LOCAL_GL_POINT_SIZE_ARRAY_POINTER_OES 0x898C
+#define LOCAL_GL_POINT_SIZE_ARRAY_STRIDE_OES 0x898B
+#define LOCAL_GL_POINT_SIZE_ARRAY_TYPE_OES 0x898A
+#define LOCAL_GL_POINT_SIZE_GRANULARITY 0x0B13
+#define LOCAL_GL_POINT_SIZE_MAX 0x8127
+#define LOCAL_GL_POINT_SIZE_MAX_ARB 0x8127
+#define LOCAL_GL_POINT_SIZE_MAX_EXT 0x8127
+#define LOCAL_GL_POINT_SIZE_MAX_SGIS 0x8127
+#define LOCAL_GL_POINT_SIZE_MIN 0x8126
+#define LOCAL_GL_POINT_SIZE_MIN_ARB 0x8126
+#define LOCAL_GL_POINT_SIZE_MIN_EXT 0x8126
+#define LOCAL_GL_POINT_SIZE_MIN_SGIS 0x8126
+#define LOCAL_GL_POINT_SIZE_RANGE 0x0B12
+#define LOCAL_GL_POINT_SMOOTH 0x0B10
+#define LOCAL_GL_POINT_SMOOTH_HINT 0x0C51
+#define LOCAL_GL_POINT_SPRITE 0x8861
+#define LOCAL_GL_POINT_SPRITE_ARB 0x8861
+#define LOCAL_GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0
+#define LOCAL_GL_POINT_SPRITE_NV 0x8861
+#define LOCAL_GL_POINT_SPRITE_OES 0x8861
+#define LOCAL_GL_POINT_SPRITE_R_MODE_NV 0x8863
+#define LOCAL_GL_POINT_TOKEN 0x0701
+#define LOCAL_GL_POLYGON 0x0009
+#define LOCAL_GL_POLYGON_BIT 0x00000008
+#define LOCAL_GL_POLYGON_MODE 0x0B40
+#define LOCAL_GL_POLYGON_OFFSET_BIAS_EXT 0x8039
+#define LOCAL_GL_POLYGON_OFFSET_EXT 0x8037
+#define LOCAL_GL_POLYGON_OFFSET_FACTOR 0x8038
+#define LOCAL_GL_POLYGON_OFFSET_FACTOR_EXT 0x8038
+#define LOCAL_GL_POLYGON_OFFSET_FILL 0x8037
+#define LOCAL_GL_POLYGON_OFFSET_LINE 0x2A02
+#define LOCAL_GL_POLYGON_OFFSET_POINT 0x2A01
+#define LOCAL_GL_POLYGON_OFFSET_UNITS 0x2A00
+#define LOCAL_GL_POLYGON_SMOOTH 0x0B41
+#define LOCAL_GL_POLYGON_SMOOTH_HINT 0x0C53
+#define LOCAL_GL_POLYGON_STIPPLE 0x0B42
+#define LOCAL_GL_POLYGON_STIPPLE_BIT 0x00000010
+#define LOCAL_GL_POLYGON_TOKEN 0x0703
+#define LOCAL_GL_POSITION 0x1203
+#define LOCAL_GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB
+#define LOCAL_GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB
+#define LOCAL_GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7
+#define LOCAL_GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7
+#define LOCAL_GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA
+#define LOCAL_GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA
+#define LOCAL_GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6
+#define LOCAL_GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6
+#define LOCAL_GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2
+#define LOCAL_GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2
+#define LOCAL_GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9
+#define LOCAL_GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9
+#define LOCAL_GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5
+#define LOCAL_GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5
+#define LOCAL_GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8
+#define LOCAL_GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8
+#define LOCAL_GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4
+#define LOCAL_GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4
+#define LOCAL_GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023
+#define LOCAL_GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023
+#define LOCAL_GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F
+#define LOCAL_GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F
+#define LOCAL_GL_POST_CONVOLUTION_BLUE_BIAS 0x8022
+#define LOCAL_GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022
+#define LOCAL_GL_POST_CONVOLUTION_BLUE_SCALE 0x801E
+#define LOCAL_GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E
+#define LOCAL_GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1
+#define LOCAL_GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1
+#define LOCAL_GL_POST_CONVOLUTION_GREEN_BIAS 0x8021
+#define LOCAL_GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021
+#define LOCAL_GL_POST_CONVOLUTION_GREEN_SCALE 0x801D
+#define LOCAL_GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D
+#define LOCAL_GL_POST_CONVOLUTION_RED_BIAS 0x8020
+#define LOCAL_GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020
+#define LOCAL_GL_POST_CONVOLUTION_RED_SCALE 0x801C
+#define LOCAL_GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C
+#define LOCAL_GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162
+#define LOCAL_GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B
+#define LOCAL_GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179
+#define LOCAL_GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C
+#define LOCAL_GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A
+#define LOCAL_GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8
+#define LOCAL_GL_PRESENT_DURATION_NV 0x8E2B
+#define LOCAL_GL_PRESENT_TIME_NV 0x8E2A
+#define LOCAL_GL_PRESERVE_ATI 0x8762
+#define LOCAL_GL_PREVIOUS 0x8578
+#define LOCAL_GL_PREVIOUS_ARB 0x8578
+#define LOCAL_GL_PREVIOUS_EXT 0x8578
+#define LOCAL_GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4
+#define LOCAL_GL_PRIMARY_COLOR 0x8577
+#define LOCAL_GL_PRIMARY_COLOR_ARB 0x8577
+#define LOCAL_GL_PRIMARY_COLOR_EXT 0x8577
+#define LOCAL_GL_PRIMARY_COLOR_NV 0x852C
+#define LOCAL_GL_PRIMITIVES_GENERATED 0x8C87
+#define LOCAL_GL_PRIMITIVES_GENERATED_EXT 0x8C87
+#define LOCAL_GL_PRIMITIVES_GENERATED_NV 0x8C87
+#define LOCAL_GL_PRIMITIVE_ID_NV 0x8C7C
+#define LOCAL_GL_PRIMITIVE_RESTART 0x8F9D
+#define LOCAL_GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#define LOCAL_GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221
+#define LOCAL_GL_PRIMITIVE_RESTART_INDEX 0x8F9E
+#define LOCAL_GL_PRIMITIVE_RESTART_INDEX_NV 0x8559
+#define LOCAL_GL_PRIMITIVE_RESTART_NV 0x8558
+#define LOCAL_GL_PROGRAM 0x82E2
+#define LOCAL_GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0
+#define LOCAL_GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805
+#define LOCAL_GL_PROGRAM_ATTRIBS_ARB 0x88AC
+#define LOCAL_GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906
+#define LOCAL_GL_PROGRAM_BINARY_ANGLE 0x93A6
+#define LOCAL_GL_PROGRAM_BINARY_FORMATS 0x87FF
+#define LOCAL_GL_PROGRAM_BINARY_FORMATS_OES 0x87FF
+#define LOCAL_GL_PROGRAM_BINARY_LENGTH 0x8741
+#define LOCAL_GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#define LOCAL_GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
+#define LOCAL_GL_PROGRAM_BINDING_ARB 0x8677
+#define LOCAL_GL_PROGRAM_ERROR_POSITION_ARB 0x864B
+#define LOCAL_GL_PROGRAM_ERROR_POSITION_NV 0x864B
+#define LOCAL_GL_PROGRAM_ERROR_STRING_ARB 0x8874
+#define LOCAL_GL_PROGRAM_ERROR_STRING_NV 0x8874
+#define LOCAL_GL_PROGRAM_FORMAT_ARB 0x8876
+#define LOCAL_GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+#define LOCAL_GL_PROGRAM_INPUT 0x92E3
+#define LOCAL_GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0
+#define LOCAL_GL_PROGRAM_KHR 0x82E2
+#define LOCAL_GL_PROGRAM_LENGTH_ARB 0x8627
+#define LOCAL_GL_PROGRAM_LENGTH_NV 0x8627
+#define LOCAL_GL_PROGRAM_MATRIX_EXT 0x8E2D
+#define LOCAL_GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F
+#define LOCAL_GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2
+#define LOCAL_GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808
+#define LOCAL_GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE
+#define LOCAL_GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2
+#define LOCAL_GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA
+#define LOCAL_GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6
+#define LOCAL_GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A
+#define LOCAL_GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809
+#define LOCAL_GL_PROGRAM_OBJECT_ARB 0x8B40
+#define LOCAL_GL_PROGRAM_OBJECT_EXT 0x8B40
+#define LOCAL_GL_PROGRAM_OUTPUT 0x92E4
+#define LOCAL_GL_PROGRAM_PARAMETERS_ARB 0x88A8
+#define LOCAL_GL_PROGRAM_PARAMETER_NV 0x8644
+#define LOCAL_GL_PROGRAM_PIPELINE 0x82E4
+#define LOCAL_GL_PROGRAM_PIPELINE_BINDING 0x825A
+#define LOCAL_GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A
+#define LOCAL_GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F
+#define LOCAL_GL_PROGRAM_POINT_SIZE 0x8642
+#define LOCAL_GL_PROGRAM_POINT_SIZE_ARB 0x8642
+#define LOCAL_GL_PROGRAM_POINT_SIZE_EXT 0x8642
+#define LOCAL_GL_PROGRAM_RESIDENT_NV 0x8647
+#define LOCAL_GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907
+#define LOCAL_GL_PROGRAM_SEPARABLE 0x8258
+#define LOCAL_GL_PROGRAM_SEPARABLE_EXT 0x8258
+#define LOCAL_GL_PROGRAM_STRING_ARB 0x8628
+#define LOCAL_GL_PROGRAM_STRING_NV 0x8628
+#define LOCAL_GL_PROGRAM_TARGET_NV 0x8646
+#define LOCAL_GL_PROGRAM_TEMPORARIES_ARB 0x88A4
+#define LOCAL_GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807
+#define LOCAL_GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806
+#define LOCAL_GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6
+#define LOCAL_GL_PROJECTION 0x1701
+#define LOCAL_GL_PROJECTION_MATRIX 0x0BA7
+#define LOCAL_GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES 0x898E
+#define LOCAL_GL_PROJECTION_STACK_DEPTH 0x0BA4
+#define LOCAL_GL_PROVOKING_VERTEX 0x8E4F
+#define LOCAL_GL_PROVOKING_VERTEX_EXT 0x8E4F
+#define LOCAL_GL_PROXY_COLOR_TABLE 0x80D3
+#define LOCAL_GL_PROXY_COLOR_TABLE_SGI 0x80D3
+#define LOCAL_GL_PROXY_HISTOGRAM 0x8025
+#define LOCAL_GL_PROXY_HISTOGRAM_EXT 0x8025
+#define LOCAL_GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5
+#define LOCAL_GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5
+#define LOCAL_GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4
+#define LOCAL_GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4
+#define LOCAL_GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163
+#define LOCAL_GL_PROXY_TEXTURE_1D 0x8063
+#define LOCAL_GL_PROXY_TEXTURE_1D_ARRAY 0x8C19
+#define LOCAL_GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19
+#define LOCAL_GL_PROXY_TEXTURE_1D_EXT 0x8063
+#define LOCAL_GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B
+#define LOCAL_GL_PROXY_TEXTURE_2D 0x8064
+#define LOCAL_GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B
+#define LOCAL_GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B
+#define LOCAL_GL_PROXY_TEXTURE_2D_EXT 0x8064
+#define LOCAL_GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101
+#define LOCAL_GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103
+#define LOCAL_GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C
+#define LOCAL_GL_PROXY_TEXTURE_3D 0x8070
+#define LOCAL_GL_PROXY_TEXTURE_3D_EXT 0x8070
+#define LOCAL_GL_PROXY_TEXTURE_4D_SGIS 0x8135
+#define LOCAL_GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD
+#define LOCAL_GL_PROXY_TEXTURE_CUBE_MAP 0x851B
+#define LOCAL_GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B
+#define LOCAL_GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B
+#define LOCAL_GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B
+#define LOCAL_GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B
+#define LOCAL_GL_PROXY_TEXTURE_RECTANGLE 0x84F7
+#define LOCAL_GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7
+#define LOCAL_GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
+#define LOCAL_GL_PURGEABLE_APPLE 0x8A1D
+#define LOCAL_GL_Q 0x2003
+#define LOCAL_GL_QUADRATIC_ATTENUATION 0x1209
+#define LOCAL_GL_QUADRATIC_CURVE_TO_NV 0x0A
+#define LOCAL_GL_QUADS 0x0007
+#define LOCAL_GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C
+#define LOCAL_GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C
+#define LOCAL_GL_QUAD_ALPHA4_SGIS 0x811E
+#define LOCAL_GL_QUAD_ALPHA8_SGIS 0x811F
+#define LOCAL_GL_QUAD_INTENSITY4_SGIS 0x8122
+#define LOCAL_GL_QUAD_INTENSITY8_SGIS 0x8123
+#define LOCAL_GL_QUAD_LUMINANCE4_SGIS 0x8120
+#define LOCAL_GL_QUAD_LUMINANCE8_SGIS 0x8121
+#define LOCAL_GL_QUAD_MESH_SUN 0x8614
+#define LOCAL_GL_QUAD_STRIP 0x0008
+#define LOCAL_GL_QUAD_TEXTURE_SELECT_SGIS 0x8125
+#define LOCAL_GL_QUARTER_BIT_ATI 0x00000010
+#define LOCAL_GL_QUERY 0x82E3
+#define LOCAL_GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF
+#define LOCAL_GL_QUERY_BUFFER 0x9192
+#define LOCAL_GL_QUERY_BUFFER_AMD 0x9192
+#define LOCAL_GL_QUERY_BUFFER_BARRIER_BIT 0x00008000
+#define LOCAL_GL_QUERY_BUFFER_BINDING 0x9193
+#define LOCAL_GL_QUERY_BUFFER_BINDING_AMD 0x9193
+#define LOCAL_GL_QUERY_BY_REGION_NO_WAIT 0x8E16
+#define LOCAL_GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16
+#define LOCAL_GL_QUERY_BY_REGION_WAIT 0x8E15
+#define LOCAL_GL_QUERY_BY_REGION_WAIT_NV 0x8E15
+#define LOCAL_GL_QUERY_COUNTER_BITS 0x8864
+#define LOCAL_GL_QUERY_COUNTER_BITS_ARB 0x8864
+#define LOCAL_GL_QUERY_COUNTER_BITS_EXT 0x8864
+#define LOCAL_GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008
+#define LOCAL_GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002
+#define LOCAL_GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001
+#define LOCAL_GL_QUERY_KHR 0x82E3
+#define LOCAL_GL_QUERY_NO_WAIT 0x8E14
+#define LOCAL_GL_QUERY_NO_WAIT_NV 0x8E14
+#define LOCAL_GL_QUERY_OBJECT_AMD 0x9153
+#define LOCAL_GL_QUERY_OBJECT_EXT 0x9153
+#define LOCAL_GL_QUERY_RESULT 0x8866
+#define LOCAL_GL_QUERY_RESULT_ARB 0x8866
+#define LOCAL_GL_QUERY_RESULT_AVAILABLE 0x8867
+#define LOCAL_GL_QUERY_RESULT_AVAILABLE_ARB 0x8867
+#define LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT 0x8867
+#define LOCAL_GL_QUERY_RESULT_EXT 0x8866
+#define LOCAL_GL_QUERY_RESULT_NO_WAIT 0x9194
+#define LOCAL_GL_QUERY_RESULT_NO_WAIT_AMD 0x9194
+#define LOCAL_GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004
+#define LOCAL_GL_QUERY_WAIT 0x8E13
+#define LOCAL_GL_QUERY_WAIT_NV 0x8E13
+#define LOCAL_GL_R 0x2002
+#define LOCAL_GL_R11F_G11F_B10F 0x8C3A
+#define LOCAL_GL_R11F_G11F_B10F_EXT 0x8C3A
+#define LOCAL_GL_R16 0x822A
+#define LOCAL_GL_R16F 0x822D
+#define LOCAL_GL_R16F_EXT 0x822D
+#define LOCAL_GL_R16I 0x8233
+#define LOCAL_GL_R16UI 0x8234
+#define LOCAL_GL_R16_SNORM 0x8F98
+#define LOCAL_GL_R1UI_C3F_V3F_SUN 0x85C6
+#define LOCAL_GL_R1UI_C4F_N3F_V3F_SUN 0x85C8
+#define LOCAL_GL_R1UI_C4UB_V3F_SUN 0x85C5
+#define LOCAL_GL_R1UI_N3F_V3F_SUN 0x85C7
+#define LOCAL_GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB
+#define LOCAL_GL_R1UI_T2F_N3F_V3F_SUN 0x85CA
+#define LOCAL_GL_R1UI_T2F_V3F_SUN 0x85C9
+#define LOCAL_GL_R1UI_V3F_SUN 0x85C4
+#define LOCAL_GL_R32F 0x822E
+#define LOCAL_GL_R32F_EXT 0x822E
+#define LOCAL_GL_R32I 0x8235
+#define LOCAL_GL_R32UI 0x8236
+#define LOCAL_GL_R3_G3_B2 0x2A10
+#define LOCAL_GL_R8 0x8229
+#define LOCAL_GL_R8I 0x8231
+#define LOCAL_GL_R8UI 0x8232
+#define LOCAL_GL_R8_EXT 0x8229
+#define LOCAL_GL_R8_SNORM 0x8F94
+#define LOCAL_GL_RASTERIZER_DISCARD 0x8C89
+#define LOCAL_GL_RASTERIZER_DISCARD_EXT 0x8C89
+#define LOCAL_GL_RASTERIZER_DISCARD_NV 0x8C89
+#define LOCAL_GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262
+#define LOCAL_GL_READ_BUFFER 0x0C02
+#define LOCAL_GL_READ_BUFFER_EXT 0x0C02
+#define LOCAL_GL_READ_BUFFER_NV 0x0C02
+#define LOCAL_GL_READ_FRAMEBUFFER 0x8CA8
+#define LOCAL_GL_READ_FRAMEBUFFER_ANGLE 0x8CA8
+#define LOCAL_GL_READ_FRAMEBUFFER_APPLE 0x8CA8
+#define LOCAL_GL_READ_FRAMEBUFFER_BINDING 0x8CAA
+#define LOCAL_GL_READ_FRAMEBUFFER_BINDING_ANGLE 0x8CAA
+#define LOCAL_GL_READ_FRAMEBUFFER_BINDING_APPLE 0x8CAA
+#define LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA
+#define LOCAL_GL_READ_FRAMEBUFFER_BINDING_NV 0x8CAA
+#define LOCAL_GL_READ_FRAMEBUFFER_EXT 0x8CA8
+#define LOCAL_GL_READ_FRAMEBUFFER_NV 0x8CA8
+#define LOCAL_GL_READ_ONLY 0x88B8
+#define LOCAL_GL_READ_ONLY_ARB 0x88B8
+#define LOCAL_GL_READ_PIXELS 0x828C
+#define LOCAL_GL_READ_PIXELS_FORMAT 0x828D
+#define LOCAL_GL_READ_PIXELS_TYPE 0x828E
+#define LOCAL_GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B
+#define LOCAL_GL_READ_PIXEL_DATA_RANGE_NV 0x8879
+#define LOCAL_GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D
+#define LOCAL_GL_READ_WRITE 0x88BA
+#define LOCAL_GL_READ_WRITE_ARB 0x88BA
+#define LOCAL_GL_RECIP_ADD_SIGNED_ALPHA_IMG 0x8C05
+#define LOCAL_GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE
+#define LOCAL_GL_RECT_NV 0xF6
+#define LOCAL_GL_RED 0x1903
+#define LOCAL_GL_REDUCE 0x8016
+#define LOCAL_GL_REDUCE_EXT 0x8016
+#define LOCAL_GL_RED_BIAS 0x0D15
+#define LOCAL_GL_RED_BITS 0x0D52
+#define LOCAL_GL_RED_BIT_ATI 0x00000001
+#define LOCAL_GL_RED_EXT 0x1903
+#define LOCAL_GL_RED_INTEGER 0x8D94
+#define LOCAL_GL_RED_INTEGER_EXT 0x8D94
+#define LOCAL_GL_RED_MAX_CLAMP_INGR 0x8564
+#define LOCAL_GL_RED_MIN_CLAMP_INGR 0x8560
+#define LOCAL_GL_RED_NV 0x1903
+#define LOCAL_GL_RED_SCALE 0x0D14
+#define LOCAL_GL_RED_SNORM 0x8F90
+#define LOCAL_GL_REFERENCED_BY_COMPUTE_SHADER 0x930B
+#define LOCAL_GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A
+#define LOCAL_GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309
+#define LOCAL_GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307
+#define LOCAL_GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308
+#define LOCAL_GL_REFERENCED_BY_VERTEX_SHADER 0x9306
+#define LOCAL_GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E
+#define LOCAL_GL_REFERENCE_PLANE_SGIX 0x817D
+#define LOCAL_GL_REFLECTION_MAP 0x8512
+#define LOCAL_GL_REFLECTION_MAP_ARB 0x8512
+#define LOCAL_GL_REFLECTION_MAP_EXT 0x8512
+#define LOCAL_GL_REFLECTION_MAP_NV 0x8512
+#define LOCAL_GL_REFLECTION_MAP_OES 0x8512
+#define LOCAL_GL_REGISTER_COMBINERS_NV 0x8522
+#define LOCAL_GL_REG_0_ATI 0x8921
+#define LOCAL_GL_REG_10_ATI 0x892B
+#define LOCAL_GL_REG_11_ATI 0x892C
+#define LOCAL_GL_REG_12_ATI 0x892D
+#define LOCAL_GL_REG_13_ATI 0x892E
+#define LOCAL_GL_REG_14_ATI 0x892F
+#define LOCAL_GL_REG_15_ATI 0x8930
+#define LOCAL_GL_REG_16_ATI 0x8931
+#define LOCAL_GL_REG_17_ATI 0x8932
+#define LOCAL_GL_REG_18_ATI 0x8933
+#define LOCAL_GL_REG_19_ATI 0x8934
+#define LOCAL_GL_REG_1_ATI 0x8922
+#define LOCAL_GL_REG_20_ATI 0x8935
+#define LOCAL_GL_REG_21_ATI 0x8936
+#define LOCAL_GL_REG_22_ATI 0x8937
+#define LOCAL_GL_REG_23_ATI 0x8938
+#define LOCAL_GL_REG_24_ATI 0x8939
+#define LOCAL_GL_REG_25_ATI 0x893A
+#define LOCAL_GL_REG_26_ATI 0x893B
+#define LOCAL_GL_REG_27_ATI 0x893C
+#define LOCAL_GL_REG_28_ATI 0x893D
+#define LOCAL_GL_REG_29_ATI 0x893E
+#define LOCAL_GL_REG_2_ATI 0x8923
+#define LOCAL_GL_REG_30_ATI 0x893F
+#define LOCAL_GL_REG_31_ATI 0x8940
+#define LOCAL_GL_REG_3_ATI 0x8924
+#define LOCAL_GL_REG_4_ATI 0x8925
+#define LOCAL_GL_REG_5_ATI 0x8926
+#define LOCAL_GL_REG_6_ATI 0x8927
+#define LOCAL_GL_REG_7_ATI 0x8928
+#define LOCAL_GL_REG_8_ATI 0x8929
+#define LOCAL_GL_REG_9_ATI 0x892A
+#define LOCAL_GL_RELATIVE_ARC_TO_NV 0xFF
+#define LOCAL_GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D
+#define LOCAL_GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07
+#define LOCAL_GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17
+#define LOCAL_GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19
+#define LOCAL_GL_RELATIVE_LINE_TO_NV 0x05
+#define LOCAL_GL_RELATIVE_MOVE_TO_NV 0x03
+#define LOCAL_GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B
+#define LOCAL_GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13
+#define LOCAL_GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15
+#define LOCAL_GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11
+#define LOCAL_GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F
+#define LOCAL_GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09
+#define LOCAL_GL_RELEASED_APPLE 0x8A19
+#define LOCAL_GL_RENDER 0x1C00
+#define LOCAL_GL_RENDERBUFFER 0x8D41
+#define LOCAL_GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define LOCAL_GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
+#define LOCAL_GL_RENDERBUFFER_ALPHA_SIZE_OES 0x8D53
+#define LOCAL_GL_RENDERBUFFER_BINDING 0x8CA7
+#define LOCAL_GL_RENDERBUFFER_BINDING_ANGLE 0x8CA7
+#define LOCAL_GL_RENDERBUFFER_BINDING_EXT 0x8CA7
+#define LOCAL_GL_RENDERBUFFER_BINDING_OES 0x8CA7
+#define LOCAL_GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define LOCAL_GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52
+#define LOCAL_GL_RENDERBUFFER_BLUE_SIZE_OES 0x8D52
+#define LOCAL_GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10
+#define LOCAL_GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define LOCAL_GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
+#define LOCAL_GL_RENDERBUFFER_DEPTH_SIZE_OES 0x8D54
+#define LOCAL_GL_RENDERBUFFER_EXT 0x8D41
+#define LOCAL_GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
+#define LOCAL_GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define LOCAL_GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51
+#define LOCAL_GL_RENDERBUFFER_GREEN_SIZE_OES 0x8D51
+#define LOCAL_GL_RENDERBUFFER_HEIGHT 0x8D43
+#define LOCAL_GL_RENDERBUFFER_HEIGHT_EXT 0x8D43
+#define LOCAL_GL_RENDERBUFFER_HEIGHT_OES 0x8D43
+#define LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44
+#define LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT_OES 0x8D44
+#define LOCAL_GL_RENDERBUFFER_OES 0x8D41
+#define LOCAL_GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define LOCAL_GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
+#define LOCAL_GL_RENDERBUFFER_RED_SIZE_OES 0x8D50
+#define LOCAL_GL_RENDERBUFFER_SAMPLES 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_SAMPLES_ANGLE 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_SAMPLES_APPLE 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_SAMPLES_IMG 0x9133
+#define LOCAL_GL_RENDERBUFFER_SAMPLES_NV 0x8CAB
+#define LOCAL_GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+#define LOCAL_GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
+#define LOCAL_GL_RENDERBUFFER_STENCIL_SIZE_OES 0x8D55
+#define LOCAL_GL_RENDERBUFFER_WIDTH 0x8D42
+#define LOCAL_GL_RENDERBUFFER_WIDTH_EXT 0x8D42
+#define LOCAL_GL_RENDERBUFFER_WIDTH_OES 0x8D42
+#define LOCAL_GL_RENDERER 0x1F01
+#define LOCAL_GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM 0x8FB3
+#define LOCAL_GL_RENDER_MODE 0x0C40
+#define LOCAL_GL_REPEAT 0x2901
+#define LOCAL_GL_REPLACE 0x1E01
+#define LOCAL_GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3
+#define LOCAL_GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2
+#define LOCAL_GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0
+#define LOCAL_GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1
+#define LOCAL_GL_REPLACEMENT_CODE_SUN 0x81D8
+#define LOCAL_GL_REPLACE_EXT 0x8062
+#define LOCAL_GL_REPLACE_MIDDLE_SUN 0x0002
+#define LOCAL_GL_REPLACE_OLDEST_SUN 0x0003
+#define LOCAL_GL_REPLACE_VALUE_AMD 0x874B
+#define LOCAL_GL_REPLICATE_BORDER 0x8153
+#define LOCAL_GL_REPLICATE_BORDER_HP 0x8153
+#define LOCAL_GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68
+#define LOCAL_GL_RESAMPLE_AVERAGE_OML 0x8988
+#define LOCAL_GL_RESAMPLE_DECIMATE_OML 0x8989
+#define LOCAL_GL_RESAMPLE_DECIMATE_SGIX 0x8430
+#define LOCAL_GL_RESAMPLE_REPLICATE_OML 0x8986
+#define LOCAL_GL_RESAMPLE_REPLICATE_SGIX 0x842E
+#define LOCAL_GL_RESAMPLE_ZERO_FILL_OML 0x8987
+#define LOCAL_GL_RESAMPLE_ZERO_FILL_SGIX 0x842F
+#define LOCAL_GL_RESCALE_NORMAL 0x803A
+#define LOCAL_GL_RESCALE_NORMAL_EXT 0x803A
+#define LOCAL_GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
+#define LOCAL_GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256
+#define LOCAL_GL_RESTART_PATH_NV 0xF0
+#define LOCAL_GL_RESTART_SUN 0x0001
+#define LOCAL_GL_RETAINED_APPLE 0x8A1B
+#define LOCAL_GL_RETURN 0x0102
+#define LOCAL_GL_RG 0x8227
+#define LOCAL_GL_RG16 0x822C
+#define LOCAL_GL_RG16F 0x822F
+#define LOCAL_GL_RG16F_EXT 0x822F
+#define LOCAL_GL_RG16I 0x8239
+#define LOCAL_GL_RG16UI 0x823A
+#define LOCAL_GL_RG16_SNORM 0x8F99
+#define LOCAL_GL_RG32F 0x8230
+#define LOCAL_GL_RG32F_EXT 0x8230
+#define LOCAL_GL_RG32I 0x823B
+#define LOCAL_GL_RG32UI 0x823C
+#define LOCAL_GL_RG8 0x822B
+#define LOCAL_GL_RG8I 0x8237
+#define LOCAL_GL_RG8UI 0x8238
+#define LOCAL_GL_RG8_EXT 0x822B
+#define LOCAL_GL_RG8_SNORM 0x8F95
+#define LOCAL_GL_RGB 0x1907
+#define LOCAL_GL_RGB10 0x8052
+#define LOCAL_GL_RGB10_A2 0x8059
+#define LOCAL_GL_RGB10_A2UI 0x906F
+#define LOCAL_GL_RGB10_A2_EXT 0x8059
+#define LOCAL_GL_RGB10_EXT 0x8052
+#define LOCAL_GL_RGB12 0x8053
+#define LOCAL_GL_RGB12_EXT 0x8053
+#define LOCAL_GL_RGB16 0x8054
+#define LOCAL_GL_RGB16F 0x881B
+#define LOCAL_GL_RGB16F_ARB 0x881B
+#define LOCAL_GL_RGB16F_EXT 0x881B
+#define LOCAL_GL_RGB16I 0x8D89
+#define LOCAL_GL_RGB16I_EXT 0x8D89
+#define LOCAL_GL_RGB16UI 0x8D77
+#define LOCAL_GL_RGB16UI_EXT 0x8D77
+#define LOCAL_GL_RGB16_EXT 0x8054
+#define LOCAL_GL_RGB16_SNORM 0x8F9A
+#define LOCAL_GL_RGB2_EXT 0x804E
+#define LOCAL_GL_RGB32F 0x8815
+#define LOCAL_GL_RGB32F_ARB 0x8815
+#define LOCAL_GL_RGB32F_EXT 0x8815
+#define LOCAL_GL_RGB32I 0x8D83
+#define LOCAL_GL_RGB32I_EXT 0x8D83
+#define LOCAL_GL_RGB32UI 0x8D71
+#define LOCAL_GL_RGB32UI_EXT 0x8D71
+#define LOCAL_GL_RGB4 0x804F
+#define LOCAL_GL_RGB4_EXT 0x804F
+#define LOCAL_GL_RGB4_S3TC 0x83A1
+#define LOCAL_GL_RGB5 0x8050
+#define LOCAL_GL_RGB565 0x8D62
+#define LOCAL_GL_RGB565_OES 0x8D62
+#define LOCAL_GL_RGB5_A1 0x8057
+#define LOCAL_GL_RGB5_A1_EXT 0x8057
+#define LOCAL_GL_RGB5_A1_OES 0x8057
+#define LOCAL_GL_RGB5_EXT 0x8050
+#define LOCAL_GL_RGB8 0x8051
+#define LOCAL_GL_RGB8I 0x8D8F
+#define LOCAL_GL_RGB8I_EXT 0x8D8F
+#define LOCAL_GL_RGB8UI 0x8D7D
+#define LOCAL_GL_RGB8UI_EXT 0x8D7D
+#define LOCAL_GL_RGB8_EXT 0x8051
+#define LOCAL_GL_RGB8_OES 0x8051
+#define LOCAL_GL_RGB8_SNORM 0x8F96
+#define LOCAL_GL_RGB9_E5 0x8C3D
+#define LOCAL_GL_RGB9_E5_EXT 0x8C3D
+#define LOCAL_GL_RGBA 0x1908
+#define LOCAL_GL_RGBA12 0x805A
+#define LOCAL_GL_RGBA12_EXT 0x805A
+#define LOCAL_GL_RGBA16 0x805B
+#define LOCAL_GL_RGBA16F 0x881A
+#define LOCAL_GL_RGBA16F_ARB 0x881A
+#define LOCAL_GL_RGBA16F_EXT 0x881A
+#define LOCAL_GL_RGBA16I 0x8D88
+#define LOCAL_GL_RGBA16I_EXT 0x8D88
+#define LOCAL_GL_RGBA16UI 0x8D76
+#define LOCAL_GL_RGBA16UI_EXT 0x8D76
+#define LOCAL_GL_RGBA16_EXT 0x805B
+#define LOCAL_GL_RGBA16_SNORM 0x8F9B
+#define LOCAL_GL_RGBA2 0x8055
+#define LOCAL_GL_RGBA2_EXT 0x8055
+#define LOCAL_GL_RGBA32F 0x8814
+#define LOCAL_GL_RGBA32F_ARB 0x8814
+#define LOCAL_GL_RGBA32F_EXT 0x8814
+#define LOCAL_GL_RGBA32I 0x8D82
+#define LOCAL_GL_RGBA32I_EXT 0x8D82
+#define LOCAL_GL_RGBA32UI 0x8D70
+#define LOCAL_GL_RGBA32UI_EXT 0x8D70
+#define LOCAL_GL_RGBA4 0x8056
+#define LOCAL_GL_RGBA4_DXT5_S3TC 0x83A5
+#define LOCAL_GL_RGBA4_EXT 0x8056
+#define LOCAL_GL_RGBA4_OES 0x8056
+#define LOCAL_GL_RGBA4_S3TC 0x83A3
+#define LOCAL_GL_RGBA8 0x8058
+#define LOCAL_GL_RGBA8I 0x8D8E
+#define LOCAL_GL_RGBA8I_EXT 0x8D8E
+#define LOCAL_GL_RGBA8UI 0x8D7C
+#define LOCAL_GL_RGBA8UI_EXT 0x8D7C
+#define LOCAL_GL_RGBA8_EXT 0x8058
+#define LOCAL_GL_RGBA8_OES 0x8058
+#define LOCAL_GL_RGBA8_SNORM 0x8F97
+#define LOCAL_GL_RGBA_DXT5_S3TC 0x83A4
+#define LOCAL_GL_RGBA_FLOAT16_APPLE 0x881A
+#define LOCAL_GL_RGBA_FLOAT16_ATI 0x881A
+#define LOCAL_GL_RGBA_FLOAT32_APPLE 0x8814
+#define LOCAL_GL_RGBA_FLOAT32_ATI 0x8814
+#define LOCAL_GL_RGBA_FLOAT_MODE_ARB 0x8820
+#define LOCAL_GL_RGBA_FLOAT_MODE_ATI 0x8820
+#define LOCAL_GL_RGBA_INTEGER 0x8D99
+#define LOCAL_GL_RGBA_INTEGER_EXT 0x8D99
+#define LOCAL_GL_RGBA_INTEGER_MODE_EXT 0x8D9E
+#define LOCAL_GL_RGBA_MODE 0x0C31
+#define LOCAL_GL_RGBA_S3TC 0x83A2
+#define LOCAL_GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C
+#define LOCAL_GL_RGBA_SNORM 0x8F93
+#define LOCAL_GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9
+#define LOCAL_GL_RGB_422_APPLE 0x8A1F
+#define LOCAL_GL_RGB_FLOAT16_APPLE 0x881B
+#define LOCAL_GL_RGB_FLOAT16_ATI 0x881B
+#define LOCAL_GL_RGB_FLOAT32_APPLE 0x8815
+#define LOCAL_GL_RGB_FLOAT32_ATI 0x8815
+#define LOCAL_GL_RGB_INTEGER 0x8D98
+#define LOCAL_GL_RGB_INTEGER_EXT 0x8D98
+#define LOCAL_GL_RGB_RAW_422_APPLE 0x8A51
+#define LOCAL_GL_RGB_S3TC 0x83A0
+#define LOCAL_GL_RGB_SCALE 0x8573
+#define LOCAL_GL_RGB_SCALE_ARB 0x8573
+#define LOCAL_GL_RGB_SCALE_EXT 0x8573
+#define LOCAL_GL_RGB_SNORM 0x8F92
+#define LOCAL_GL_RG_EXT 0x8227
+#define LOCAL_GL_RG_INTEGER 0x8228
+#define LOCAL_GL_RG_SNORM 0x8F91
+#define LOCAL_GL_RIGHT 0x0407
+#define LOCAL_GL_ROUND_NV 0x90A4
+#define LOCAL_GL_S 0x2000
+#define LOCAL_GL_SAMPLER 0x82E6
+#define LOCAL_GL_SAMPLER_1D 0x8B5D
+#define LOCAL_GL_SAMPLER_1D_ARB 0x8B5D
+#define LOCAL_GL_SAMPLER_1D_ARRAY 0x8DC0
+#define LOCAL_GL_SAMPLER_1D_ARRAY_EXT 0x8DC0
+#define LOCAL_GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3
+#define LOCAL_GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3
+#define LOCAL_GL_SAMPLER_1D_SHADOW 0x8B61
+#define LOCAL_GL_SAMPLER_1D_SHADOW_ARB 0x8B61
+#define LOCAL_GL_SAMPLER_2D 0x8B5E
+#define LOCAL_GL_SAMPLER_2D_ARB 0x8B5E
+#define LOCAL_GL_SAMPLER_2D_ARRAY 0x8DC1
+#define LOCAL_GL_SAMPLER_2D_ARRAY_EXT 0x8DC1
+#define LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#define LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4
+#define LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW_NV 0x8DC4
+#define LOCAL_GL_SAMPLER_2D_MULTISAMPLE 0x9108
+#define LOCAL_GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B
+#define LOCAL_GL_SAMPLER_2D_RECT 0x8B63
+#define LOCAL_GL_SAMPLER_2D_RECT_ARB 0x8B63
+#define LOCAL_GL_SAMPLER_2D_RECT_SHADOW 0x8B64
+#define LOCAL_GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64
+#define LOCAL_GL_SAMPLER_2D_SHADOW 0x8B62
+#define LOCAL_GL_SAMPLER_2D_SHADOW_ARB 0x8B62
+#define LOCAL_GL_SAMPLER_2D_SHADOW_EXT 0x8B62
+#define LOCAL_GL_SAMPLER_3D 0x8B5F
+#define LOCAL_GL_SAMPLER_3D_ARB 0x8B5F
+#define LOCAL_GL_SAMPLER_3D_OES 0x8B5F
+#define LOCAL_GL_SAMPLER_BINDING 0x8919
+#define LOCAL_GL_SAMPLER_BUFFER 0x8DC2
+#define LOCAL_GL_SAMPLER_BUFFER_AMD 0x9001
+#define LOCAL_GL_SAMPLER_BUFFER_EXT 0x8DC2
+#define LOCAL_GL_SAMPLER_CUBE 0x8B60
+#define LOCAL_GL_SAMPLER_CUBE_ARB 0x8B60
+#define LOCAL_GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
+#define LOCAL_GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C
+#define LOCAL_GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
+#define LOCAL_GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D
+#define LOCAL_GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#define LOCAL_GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5
+#define LOCAL_GL_SAMPLER_CUBE_SHADOW_NV 0x8DC5
+#define LOCAL_GL_SAMPLER_EXTERNAL_OES 0x8D66
+#define LOCAL_GL_SAMPLER_KHR 0x82E6
+#define LOCAL_GL_SAMPLER_OBJECT_AMD 0x9155
+#define LOCAL_GL_SAMPLER_RENDERBUFFER_NV 0x8E56
+#define LOCAL_GL_SAMPLES 0x80A9
+#define LOCAL_GL_SAMPLES_3DFX 0x86B4
+#define LOCAL_GL_SAMPLES_ARB 0x80A9
+#define LOCAL_GL_SAMPLES_EXT 0x80A9
+#define LOCAL_GL_SAMPLES_PASSED 0x8914
+#define LOCAL_GL_SAMPLES_PASSED_ARB 0x8914
+#define LOCAL_GL_SAMPLES_SGIS 0x80A9
+#define LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E
+#define LOCAL_GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E
+#define LOCAL_GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E
+#define LOCAL_GL_SAMPLE_ALPHA_TO_ONE 0x809F
+#define LOCAL_GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F
+#define LOCAL_GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F
+#define LOCAL_GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F
+#define LOCAL_GL_SAMPLE_BUFFERS 0x80A8
+#define LOCAL_GL_SAMPLE_BUFFERS_3DFX 0x86B3
+#define LOCAL_GL_SAMPLE_BUFFERS_ARB 0x80A8
+#define LOCAL_GL_SAMPLE_BUFFERS_EXT 0x80A8
+#define LOCAL_GL_SAMPLE_BUFFERS_SGIS 0x80A8
+#define LOCAL_GL_SAMPLE_COVERAGE 0x80A0
+#define LOCAL_GL_SAMPLE_COVERAGE_ARB 0x80A0
+#define LOCAL_GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define LOCAL_GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB
+#define LOCAL_GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define LOCAL_GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA
+#define LOCAL_GL_SAMPLE_MASK 0x8E51
+#define LOCAL_GL_SAMPLE_MASK_EXT 0x80A0
+#define LOCAL_GL_SAMPLE_MASK_INVERT_EXT 0x80AB
+#define LOCAL_GL_SAMPLE_MASK_INVERT_SGIS 0x80AB
+#define LOCAL_GL_SAMPLE_MASK_NV 0x8E51
+#define LOCAL_GL_SAMPLE_MASK_SGIS 0x80A0
+#define LOCAL_GL_SAMPLE_MASK_VALUE 0x8E52
+#define LOCAL_GL_SAMPLE_MASK_VALUE_EXT 0x80AA
+#define LOCAL_GL_SAMPLE_MASK_VALUE_NV 0x8E52
+#define LOCAL_GL_SAMPLE_MASK_VALUE_SGIS 0x80AA
+#define LOCAL_GL_SAMPLE_PATTERN_EXT 0x80AC
+#define LOCAL_GL_SAMPLE_PATTERN_SGIS 0x80AC
+#define LOCAL_GL_SAMPLE_POSITION 0x8E50
+#define LOCAL_GL_SAMPLE_POSITION_NV 0x8E50
+#define LOCAL_GL_SAMPLE_SHADING 0x8C36
+#define LOCAL_GL_SAMPLE_SHADING_ARB 0x8C36
+#define LOCAL_GL_SATURATE_BIT_ATI 0x00000040
+#define LOCAL_GL_SCALAR_EXT 0x87BE
+#define LOCAL_GL_SCALEBIAS_HINT_SGIX 0x8322
+#define LOCAL_GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA
+#define LOCAL_GL_SCALED_RESOLVE_NICEST_EXT 0x90BB
+#define LOCAL_GL_SCALE_BY_FOUR_NV 0x853F
+#define LOCAL_GL_SCALE_BY_ONE_HALF_NV 0x8540
+#define LOCAL_GL_SCALE_BY_TWO_NV 0x853E
+#define LOCAL_GL_SCISSOR_BIT 0x00080000
+#define LOCAL_GL_SCISSOR_BOX 0x0C10
+#define LOCAL_GL_SCISSOR_TEST 0x0C11
+#define LOCAL_GL_SCREEN_COORDINATES_REND 0x8490
+#define LOCAL_GL_SCREEN_NV 0x9295
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY 0x845E
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_EXT 0x845E
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B
+#define LOCAL_GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B
+#define LOCAL_GL_SECONDARY_COLOR_NV 0x852D
+#define LOCAL_GL_SECONDARY_INTERPOLATOR_ATI 0x896D
+#define LOCAL_GL_SELECT 0x1C02
+#define LOCAL_GL_SELECTION_BUFFER_POINTER 0x0DF3
+#define LOCAL_GL_SELECTION_BUFFER_SIZE 0x0DF4
+#define LOCAL_GL_SEPARABLE_2D 0x8012
+#define LOCAL_GL_SEPARABLE_2D_EXT 0x8012
+#define LOCAL_GL_SEPARATE_ATTRIBS 0x8C8D
+#define LOCAL_GL_SEPARATE_ATTRIBS_EXT 0x8C8D
+#define LOCAL_GL_SEPARATE_ATTRIBS_NV 0x8C8D
+#define LOCAL_GL_SEPARATE_SPECULAR_COLOR 0x81FA
+#define LOCAL_GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA
+#define LOCAL_GL_SET 0x150F
+#define LOCAL_GL_SET_AMD 0x874A
+#define LOCAL_GL_SGX_BINARY_IMG 0x8C0A
+#define LOCAL_GL_SGX_PROGRAM_BINARY_IMG 0x9130
+#define LOCAL_GL_SHADER 0x82E1
+#define LOCAL_GL_SHADER_BINARY_DMP 0x9250
+#define LOCAL_GL_SHADER_BINARY_FORMATS 0x8DF8
+#define LOCAL_GL_SHADER_BINARY_VIV 0x8FC4
+#define LOCAL_GL_SHADER_COMPILER 0x8DFA
+#define LOCAL_GL_SHADER_CONSISTENT_NV 0x86DD
+#define LOCAL_GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010
+#define LOCAL_GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+#define LOCAL_GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020
+#define LOCAL_GL_SHADER_IMAGE_ATOMIC 0x82A6
+#define LOCAL_GL_SHADER_IMAGE_LOAD 0x82A4
+#define LOCAL_GL_SHADER_IMAGE_STORE 0x82A5
+#define LOCAL_GL_SHADER_INCLUDE_ARB 0x8DAE
+#define LOCAL_GL_SHADER_KHR 0x82E1
+#define LOCAL_GL_SHADER_OBJECT_ARB 0x8B48
+#define LOCAL_GL_SHADER_OBJECT_EXT 0x8B48
+#define LOCAL_GL_SHADER_OPERATION_NV 0x86DF
+#define LOCAL_GL_SHADER_SOURCE_LENGTH 0x8B88
+#define LOCAL_GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
+#define LOCAL_GL_SHADER_STORAGE_BLOCK 0x92E6
+#define LOCAL_GL_SHADER_STORAGE_BUFFER 0x90D2
+#define LOCAL_GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3
+#define LOCAL_GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF
+#define LOCAL_GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5
+#define LOCAL_GL_SHADER_STORAGE_BUFFER_START 0x90D4
+#define LOCAL_GL_SHADER_TYPE 0x8B4F
+#define LOCAL_GL_SHADE_MODEL 0x0B54
+#define LOCAL_GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define LOCAL_GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C
+#define LOCAL_GL_SHADOW_AMBIENT_SGIX 0x80BF
+#define LOCAL_GL_SHADOW_ATTENUATION_EXT 0x834E
+#define LOCAL_GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB
+#define LOCAL_GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0
+#define LOCAL_GL_SHININESS 0x1601
+#define LOCAL_GL_SHORT 0x1402
+#define LOCAL_GL_SIGNALED 0x9119
+#define LOCAL_GL_SIGNALED_APPLE 0x9119
+#define LOCAL_GL_SIGNED_ALPHA8_NV 0x8706
+#define LOCAL_GL_SIGNED_ALPHA_NV 0x8705
+#define LOCAL_GL_SIGNED_HILO16_NV 0x86FA
+#define LOCAL_GL_SIGNED_HILO8_NV 0x885F
+#define LOCAL_GL_SIGNED_HILO_NV 0x86F9
+#define LOCAL_GL_SIGNED_IDENTITY_NV 0x853C
+#define LOCAL_GL_SIGNED_INTENSITY8_NV 0x8708
+#define LOCAL_GL_SIGNED_INTENSITY_NV 0x8707
+#define LOCAL_GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704
+#define LOCAL_GL_SIGNED_LUMINANCE8_NV 0x8702
+#define LOCAL_GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703
+#define LOCAL_GL_SIGNED_LUMINANCE_NV 0x8701
+#define LOCAL_GL_SIGNED_NEGATE_NV 0x853D
+#define LOCAL_GL_SIGNED_NORMALIZED 0x8F9C
+#define LOCAL_GL_SIGNED_RGB8_NV 0x86FF
+#define LOCAL_GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D
+#define LOCAL_GL_SIGNED_RGBA8_NV 0x86FC
+#define LOCAL_GL_SIGNED_RGBA_NV 0x86FB
+#define LOCAL_GL_SIGNED_RGB_NV 0x86FE
+#define LOCAL_GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C
+#define LOCAL_GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC
+#define LOCAL_GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE
+#define LOCAL_GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD
+#define LOCAL_GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF
+#define LOCAL_GL_SINGLE_COLOR 0x81F9
+#define LOCAL_GL_SINGLE_COLOR_EXT 0x81F9
+#define LOCAL_GL_SKIP_COMPONENTS1_NV -6
+#define LOCAL_GL_SKIP_COMPONENTS2_NV -5
+#define LOCAL_GL_SKIP_COMPONENTS3_NV -4
+#define LOCAL_GL_SKIP_COMPONENTS4_NV -3
+#define LOCAL_GL_SKIP_DECODE_EXT 0x8A4A
+#define LOCAL_GL_SKIP_MISSING_GLYPH_NV 0x90A9
+#define LOCAL_GL_SLICE_ACCUM_SUN 0x85CC
+#define LOCAL_GL_SLIM10U_SGIX 0x831E
+#define LOCAL_GL_SLIM12S_SGIX 0x831F
+#define LOCAL_GL_SLIM8U_SGIX 0x831D
+#define LOCAL_GL_SLUMINANCE 0x8C46
+#define LOCAL_GL_SLUMINANCE8 0x8C47
+#define LOCAL_GL_SLUMINANCE8_ALPHA8 0x8C45
+#define LOCAL_GL_SLUMINANCE8_ALPHA8_EXT 0x8C45
+#define LOCAL_GL_SLUMINANCE8_ALPHA8_NV 0x8C45
+#define LOCAL_GL_SLUMINANCE8_EXT 0x8C47
+#define LOCAL_GL_SLUMINANCE8_NV 0x8C47
+#define LOCAL_GL_SLUMINANCE_ALPHA 0x8C44
+#define LOCAL_GL_SLUMINANCE_ALPHA_EXT 0x8C44
+#define LOCAL_GL_SLUMINANCE_ALPHA_NV 0x8C44
+#define LOCAL_GL_SLUMINANCE_EXT 0x8C46
+#define LOCAL_GL_SLUMINANCE_NV 0x8C46
+#define LOCAL_GL_SMALL_CCW_ARC_TO_NV 0x12
+#define LOCAL_GL_SMALL_CW_ARC_TO_NV 0x14
+#define LOCAL_GL_SMOOTH 0x1D01
+#define LOCAL_GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10
+#define LOCAL_GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
+#define LOCAL_GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
+#define LOCAL_GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
+#define LOCAL_GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
+#define LOCAL_GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E
+#define LOCAL_GL_SOFTLIGHT_NV 0x929C
+#define LOCAL_GL_SOURCE0_ALPHA 0x8588
+#define LOCAL_GL_SOURCE0_ALPHA_ARB 0x8588
+#define LOCAL_GL_SOURCE0_ALPHA_EXT 0x8588
+#define LOCAL_GL_SOURCE0_RGB 0x8580
+#define LOCAL_GL_SOURCE0_RGB_ARB 0x8580
+#define LOCAL_GL_SOURCE0_RGB_EXT 0x8580
+#define LOCAL_GL_SOURCE1_ALPHA 0x8589
+#define LOCAL_GL_SOURCE1_ALPHA_ARB 0x8589
+#define LOCAL_GL_SOURCE1_ALPHA_EXT 0x8589
+#define LOCAL_GL_SOURCE1_RGB 0x8581
+#define LOCAL_GL_SOURCE1_RGB_ARB 0x8581
+#define LOCAL_GL_SOURCE1_RGB_EXT 0x8581
+#define LOCAL_GL_SOURCE2_ALPHA 0x858A
+#define LOCAL_GL_SOURCE2_ALPHA_ARB 0x858A
+#define LOCAL_GL_SOURCE2_ALPHA_EXT 0x858A
+#define LOCAL_GL_SOURCE2_RGB 0x8582
+#define LOCAL_GL_SOURCE2_RGB_ARB 0x8582
+#define LOCAL_GL_SOURCE2_RGB_EXT 0x8582
+#define LOCAL_GL_SOURCE3_ALPHA_NV 0x858B
+#define LOCAL_GL_SOURCE3_RGB_NV 0x8583
+#define LOCAL_GL_SPARE0_NV 0x852E
+#define LOCAL_GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532
+#define LOCAL_GL_SPARE1_NV 0x852F
+#define LOCAL_GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9
+#define LOCAL_GL_SPECULAR 0x1202
+#define LOCAL_GL_SPHERE_MAP 0x2402
+#define LOCAL_GL_SPOT_CUTOFF 0x1206
+#define LOCAL_GL_SPOT_DIRECTION 0x1204
+#define LOCAL_GL_SPOT_EXPONENT 0x1205
+#define LOCAL_GL_SPRITE_AXIAL_SGIX 0x814C
+#define LOCAL_GL_SPRITE_AXIS_SGIX 0x814A
+#define LOCAL_GL_SPRITE_EYE_ALIGNED_SGIX 0x814E
+#define LOCAL_GL_SPRITE_MODE_SGIX 0x8149
+#define LOCAL_GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D
+#define LOCAL_GL_SPRITE_SGIX 0x8148
+#define LOCAL_GL_SPRITE_TRANSLATION_SGIX 0x814B
+#define LOCAL_GL_SQUARE_NV 0x90A3
+#define LOCAL_GL_SRC0_ALPHA 0x8588
+#define LOCAL_GL_SRC0_RGB 0x8580
+#define LOCAL_GL_SRC1_ALPHA 0x8589
+#define LOCAL_GL_SRC1_COLOR 0x88F9
+#define LOCAL_GL_SRC1_RGB 0x8581
+#define LOCAL_GL_SRC2_ALPHA 0x858A
+#define LOCAL_GL_SRC2_RGB 0x8582
+#define LOCAL_GL_SRC_ALPHA 0x0302
+#define LOCAL_GL_SRC_ALPHA_SATURATE 0x0308
+#define LOCAL_GL_SRC_ATOP_NV 0x928E
+#define LOCAL_GL_SRC_COLOR 0x0300
+#define LOCAL_GL_SRC_IN_NV 0x928A
+#define LOCAL_GL_SRC_NV 0x9286
+#define LOCAL_GL_SRC_OUT_NV 0x928C
+#define LOCAL_GL_SRC_OVER_NV 0x9288
+#define LOCAL_GL_SRGB 0x8C40
+#define LOCAL_GL_SRGB8 0x8C41
+#define LOCAL_GL_SRGB8_ALPHA8 0x8C43
+#define LOCAL_GL_SRGB8_ALPHA8_EXT 0x8C43
+#define LOCAL_GL_SRGB8_EXT 0x8C41
+#define LOCAL_GL_SRGB8_NV 0x8C41
+#define LOCAL_GL_SRGB_ALPHA 0x8C42
+#define LOCAL_GL_SRGB_ALPHA_EXT 0x8C42
+#define LOCAL_GL_SRGB_DECODE_ARB 0x8299
+#define LOCAL_GL_SRGB_EXT 0x8C40
+#define LOCAL_GL_SRGB_READ 0x8297
+#define LOCAL_GL_SRGB_WRITE 0x8298
+#define LOCAL_GL_STACK_OVERFLOW 0x0503
+#define LOCAL_GL_STACK_OVERFLOW_KHR 0x0503
+#define LOCAL_GL_STACK_UNDERFLOW 0x0504
+#define LOCAL_GL_STACK_UNDERFLOW_KHR 0x0504
+#define LOCAL_GL_STANDARD_FONT_NAME_NV 0x9072
+#define LOCAL_GL_STATE_RESTORE 0x8BDC
+#define LOCAL_GL_STATIC_ATI 0x8760
+#define LOCAL_GL_STATIC_COPY 0x88E6
+#define LOCAL_GL_STATIC_COPY_ARB 0x88E6
+#define LOCAL_GL_STATIC_DRAW 0x88E4
+#define LOCAL_GL_STATIC_DRAW_ARB 0x88E4
+#define LOCAL_GL_STATIC_READ 0x88E5
+#define LOCAL_GL_STATIC_READ_ARB 0x88E5
+#define LOCAL_GL_STATIC_VERTEX_ARRAY_IBM 103061
+#define LOCAL_GL_STENCIL 0x1802
+#define LOCAL_GL_STENCIL_ATTACHMENT 0x8D20
+#define LOCAL_GL_STENCIL_ATTACHMENT_EXT 0x8D20
+#define LOCAL_GL_STENCIL_ATTACHMENT_OES 0x8D20
+#define LOCAL_GL_STENCIL_BACK_FAIL 0x8801
+#define LOCAL_GL_STENCIL_BACK_FAIL_ATI 0x8801
+#define LOCAL_GL_STENCIL_BACK_FUNC 0x8800
+#define LOCAL_GL_STENCIL_BACK_FUNC_ATI 0x8800
+#define LOCAL_GL_STENCIL_BACK_OP_VALUE_AMD 0x874D
+#define LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802
+#define LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803
+#define LOCAL_GL_STENCIL_BACK_REF 0x8CA3
+#define LOCAL_GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define LOCAL_GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define LOCAL_GL_STENCIL_BITS 0x0D57
+#define LOCAL_GL_STENCIL_BUFFER_BIT 0x00000400
+#define LOCAL_GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000
+#define LOCAL_GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000
+#define LOCAL_GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000
+#define LOCAL_GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000
+#define LOCAL_GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000
+#define LOCAL_GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000
+#define LOCAL_GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000
+#define LOCAL_GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000
+#define LOCAL_GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3
+#define LOCAL_GL_STENCIL_CLEAR_VALUE 0x0B91
+#define LOCAL_GL_STENCIL_COMPONENTS 0x8285
+#define LOCAL_GL_STENCIL_EXT 0x1802
+#define LOCAL_GL_STENCIL_FAIL 0x0B94
+#define LOCAL_GL_STENCIL_FUNC 0x0B92
+#define LOCAL_GL_STENCIL_INDEX 0x1901
+#define LOCAL_GL_STENCIL_INDEX1 0x8D46
+#define LOCAL_GL_STENCIL_INDEX16 0x8D49
+#define LOCAL_GL_STENCIL_INDEX16_EXT 0x8D49
+#define LOCAL_GL_STENCIL_INDEX1_EXT 0x8D46
+#define LOCAL_GL_STENCIL_INDEX1_OES 0x8D46
+#define LOCAL_GL_STENCIL_INDEX4 0x8D47
+#define LOCAL_GL_STENCIL_INDEX4_EXT 0x8D47
+#define LOCAL_GL_STENCIL_INDEX4_OES 0x8D47
+#define LOCAL_GL_STENCIL_INDEX8 0x8D48
+#define LOCAL_GL_STENCIL_INDEX8_EXT 0x8D48
+#define LOCAL_GL_STENCIL_INDEX8_OES 0x8D48
+#define LOCAL_GL_STENCIL_OP_VALUE_AMD 0x874C
+#define LOCAL_GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define LOCAL_GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define LOCAL_GL_STENCIL_REF 0x0B97
+#define LOCAL_GL_STENCIL_RENDERABLE 0x8288
+#define LOCAL_GL_STENCIL_TAG_BITS_EXT 0x88F2
+#define LOCAL_GL_STENCIL_TEST 0x0B90
+#define LOCAL_GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910
+#define LOCAL_GL_STENCIL_VALUE_MASK 0x0B93
+#define LOCAL_GL_STENCIL_WRITEMASK 0x0B98
+#define LOCAL_GL_STEREO 0x0C33
+#define LOCAL_GL_STORAGE_CACHED_APPLE 0x85BE
+#define LOCAL_GL_STORAGE_CLIENT_APPLE 0x85B4
+#define LOCAL_GL_STORAGE_PRIVATE_APPLE 0x85BD
+#define LOCAL_GL_STORAGE_SHARED_APPLE 0x85BF
+#define LOCAL_GL_STREAM_COPY 0x88E2
+#define LOCAL_GL_STREAM_COPY_ARB 0x88E2
+#define LOCAL_GL_STREAM_DRAW 0x88E0
+#define LOCAL_GL_STREAM_DRAW_ARB 0x88E0
+#define LOCAL_GL_STREAM_READ 0x88E1
+#define LOCAL_GL_STREAM_READ_ARB 0x88E1
+#define LOCAL_GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216
+#define LOCAL_GL_STRICT_LIGHTING_HINT_PGI 0x1A217
+#define LOCAL_GL_STRICT_SCISSOR_HINT_PGI 0x1A218
+#define LOCAL_GL_SUBPIXEL_BITS 0x0D50
+#define LOCAL_GL_SUBSAMPLE_DISTANCE_AMD 0x883F
+#define LOCAL_GL_SUBTRACT 0x84E7
+#define LOCAL_GL_SUBTRACT_ARB 0x84E7
+#define LOCAL_GL_SUB_ATI 0x8965
+#define LOCAL_GL_SUCCESS_NV 0x902F
+#define LOCAL_GL_SURFACE_MAPPED_NV 0x8700
+#define LOCAL_GL_SURFACE_REGISTERED_NV 0x86FD
+#define LOCAL_GL_SURFACE_STATE_NV 0x86EB
+#define LOCAL_GL_SWIZZLE_STQ_ATI 0x8977
+#define LOCAL_GL_SWIZZLE_STQ_DQ_ATI 0x8979
+#define LOCAL_GL_SWIZZLE_STRQ_ATI 0x897A
+#define LOCAL_GL_SWIZZLE_STRQ_DQ_ATI 0x897B
+#define LOCAL_GL_SWIZZLE_STR_ATI 0x8976
+#define LOCAL_GL_SWIZZLE_STR_DR_ATI 0x8978
+#define LOCAL_GL_SYNC_CL_EVENT_ARB 0x8240
+#define LOCAL_GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241
+#define LOCAL_GL_SYNC_CONDITION 0x9113
+#define LOCAL_GL_SYNC_CONDITION_APPLE 0x9113
+#define LOCAL_GL_SYNC_FENCE 0x9116
+#define LOCAL_GL_SYNC_FENCE_APPLE 0x9116
+#define LOCAL_GL_SYNC_FLAGS 0x9115
+#define LOCAL_GL_SYNC_FLAGS_APPLE 0x9115
+#define LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#define LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT_APPLE 0x00000001
+#define LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE_APPLE 0x9117
+#define LOCAL_GL_SYNC_OBJECT_APPLE 0x8A53
+#define LOCAL_GL_SYNC_STATUS 0x9114
+#define LOCAL_GL_SYNC_STATUS_APPLE 0x9114
+#define LOCAL_GL_SYNC_X11_FENCE_EXT 0x90E1
+#define LOCAL_GL_SYSTEM_FONT_NAME_NV 0x9073
+#define LOCAL_GL_T 0x2001
+#define LOCAL_GL_T2F_C3F_V3F 0x2A2A
+#define LOCAL_GL_T2F_C4F_N3F_V3F 0x2A2C
+#define LOCAL_GL_T2F_C4UB_V3F 0x2A29
+#define LOCAL_GL_T2F_IUI_N3F_V2F_EXT 0x81B3
+#define LOCAL_GL_T2F_IUI_N3F_V3F_EXT 0x81B4
+#define LOCAL_GL_T2F_IUI_V2F_EXT 0x81B1
+#define LOCAL_GL_T2F_IUI_V3F_EXT 0x81B2
+#define LOCAL_GL_T2F_N3F_V3F 0x2A2B
+#define LOCAL_GL_T2F_V3F 0x2A27
+#define LOCAL_GL_T4F_C4F_N3F_V4F 0x2A2D
+#define LOCAL_GL_T4F_V4F 0x2A28
+#define LOCAL_GL_TABLE_TOO_LARGE 0x8031
+#define LOCAL_GL_TABLE_TOO_LARGE_EXT 0x8031
+#define LOCAL_GL_TANGENT_ARRAY_EXT 0x8439
+#define LOCAL_GL_TANGENT_ARRAY_POINTER_EXT 0x8442
+#define LOCAL_GL_TANGENT_ARRAY_STRIDE_EXT 0x843F
+#define LOCAL_GL_TANGENT_ARRAY_TYPE_EXT 0x843E
+#define LOCAL_GL_TESSELLATION_FACTOR_AMD 0x9005
+#define LOCAL_GL_TESSELLATION_MODE_AMD 0x9004
+#define LOCAL_GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75
+#define LOCAL_GL_TESS_CONTROL_PROGRAM_NV 0x891E
+#define LOCAL_GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74
+#define LOCAL_GL_TESS_CONTROL_SHADER 0x8E88
+#define LOCAL_GL_TESS_CONTROL_SHADER_BIT 0x00000008
+#define LOCAL_GL_TESS_CONTROL_SUBROUTINE 0x92E9
+#define LOCAL_GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF
+#define LOCAL_GL_TESS_CONTROL_TEXTURE 0x829C
+#define LOCAL_GL_TESS_EVALUATION_PROGRAM_NV 0x891F
+#define LOCAL_GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75
+#define LOCAL_GL_TESS_EVALUATION_SHADER 0x8E87
+#define LOCAL_GL_TESS_EVALUATION_SHADER_BIT 0x00000010
+#define LOCAL_GL_TESS_EVALUATION_SUBROUTINE 0x92EA
+#define LOCAL_GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0
+#define LOCAL_GL_TESS_EVALUATION_TEXTURE 0x829D
+#define LOCAL_GL_TESS_GEN_MODE 0x8E76
+#define LOCAL_GL_TESS_GEN_POINT_MODE 0x8E79
+#define LOCAL_GL_TESS_GEN_SPACING 0x8E77
+#define LOCAL_GL_TESS_GEN_VERTEX_ORDER 0x8E78
+#define LOCAL_GL_TEXCOORD1_BIT_PGI 0x10000000
+#define LOCAL_GL_TEXCOORD2_BIT_PGI 0x20000000
+#define LOCAL_GL_TEXCOORD3_BIT_PGI 0x40000000
+#define LOCAL_GL_TEXCOORD4_BIT_PGI 0x80000000
+#define LOCAL_GL_TEXTURE 0x1702
+#define LOCAL_GL_TEXTURE0 0x84C0
+#define LOCAL_GL_TEXTURE0_ARB 0x84C0
+#define LOCAL_GL_TEXTURE1 0x84C1
+#define LOCAL_GL_TEXTURE10 0x84CA
+#define LOCAL_GL_TEXTURE10_ARB 0x84CA
+#define LOCAL_GL_TEXTURE11 0x84CB
+#define LOCAL_GL_TEXTURE11_ARB 0x84CB
+#define LOCAL_GL_TEXTURE12 0x84CC
+#define LOCAL_GL_TEXTURE12_ARB 0x84CC
+#define LOCAL_GL_TEXTURE13 0x84CD
+#define LOCAL_GL_TEXTURE13_ARB 0x84CD
+#define LOCAL_GL_TEXTURE14 0x84CE
+#define LOCAL_GL_TEXTURE14_ARB 0x84CE
+#define LOCAL_GL_TEXTURE15 0x84CF
+#define LOCAL_GL_TEXTURE15_ARB 0x84CF
+#define LOCAL_GL_TEXTURE16 0x84D0
+#define LOCAL_GL_TEXTURE16_ARB 0x84D0
+#define LOCAL_GL_TEXTURE17 0x84D1
+#define LOCAL_GL_TEXTURE17_ARB 0x84D1
+#define LOCAL_GL_TEXTURE18 0x84D2
+#define LOCAL_GL_TEXTURE18_ARB 0x84D2
+#define LOCAL_GL_TEXTURE19 0x84D3
+#define LOCAL_GL_TEXTURE19_ARB 0x84D3
+#define LOCAL_GL_TEXTURE1_ARB 0x84C1
+#define LOCAL_GL_TEXTURE2 0x84C2
+#define LOCAL_GL_TEXTURE20 0x84D4
+#define LOCAL_GL_TEXTURE20_ARB 0x84D4
+#define LOCAL_GL_TEXTURE21 0x84D5
+#define LOCAL_GL_TEXTURE21_ARB 0x84D5
+#define LOCAL_GL_TEXTURE22 0x84D6
+#define LOCAL_GL_TEXTURE22_ARB 0x84D6
+#define LOCAL_GL_TEXTURE23 0x84D7
+#define LOCAL_GL_TEXTURE23_ARB 0x84D7
+#define LOCAL_GL_TEXTURE24 0x84D8
+#define LOCAL_GL_TEXTURE24_ARB 0x84D8
+#define LOCAL_GL_TEXTURE25 0x84D9
+#define LOCAL_GL_TEXTURE25_ARB 0x84D9
+#define LOCAL_GL_TEXTURE26 0x84DA
+#define LOCAL_GL_TEXTURE26_ARB 0x84DA
+#define LOCAL_GL_TEXTURE27 0x84DB
+#define LOCAL_GL_TEXTURE27_ARB 0x84DB
+#define LOCAL_GL_TEXTURE28 0x84DC
+#define LOCAL_GL_TEXTURE28_ARB 0x84DC
+#define LOCAL_GL_TEXTURE29 0x84DD
+#define LOCAL_GL_TEXTURE29_ARB 0x84DD
+#define LOCAL_GL_TEXTURE2_ARB 0x84C2
+#define LOCAL_GL_TEXTURE3 0x84C3
+#define LOCAL_GL_TEXTURE30 0x84DE
+#define LOCAL_GL_TEXTURE30_ARB 0x84DE
+#define LOCAL_GL_TEXTURE31 0x84DF
+#define LOCAL_GL_TEXTURE31_ARB 0x84DF
+#define LOCAL_GL_TEXTURE3_ARB 0x84C3
+#define LOCAL_GL_TEXTURE4 0x84C4
+#define LOCAL_GL_TEXTURE4_ARB 0x84C4
+#define LOCAL_GL_TEXTURE5 0x84C5
+#define LOCAL_GL_TEXTURE5_ARB 0x84C5
+#define LOCAL_GL_TEXTURE6 0x84C6
+#define LOCAL_GL_TEXTURE6_ARB 0x84C6
+#define LOCAL_GL_TEXTURE7 0x84C7
+#define LOCAL_GL_TEXTURE7_ARB 0x84C7
+#define LOCAL_GL_TEXTURE8 0x84C8
+#define LOCAL_GL_TEXTURE8_ARB 0x84C8
+#define LOCAL_GL_TEXTURE9 0x84C9
+#define LOCAL_GL_TEXTURE9_ARB 0x84C9
+#define LOCAL_GL_TEXTURE_1D 0x0DE0
+#define LOCAL_GL_TEXTURE_1D_ARRAY 0x8C18
+#define LOCAL_GL_TEXTURE_1D_ARRAY_EXT 0x8C18
+#define LOCAL_GL_TEXTURE_1D_BINDING_EXT 0x8068
+#define LOCAL_GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D
+#define LOCAL_GL_TEXTURE_1D_STACK_MESAX 0x8759
+#define LOCAL_GL_TEXTURE_2D 0x0DE1
+#define LOCAL_GL_TEXTURE_2D_ARRAY 0x8C1A
+#define LOCAL_GL_TEXTURE_2D_ARRAY_EXT 0x8C1A
+#define LOCAL_GL_TEXTURE_2D_BINDING_EXT 0x8069
+#define LOCAL_GL_TEXTURE_2D_MULTISAMPLE 0x9100
+#define LOCAL_GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
+#define LOCAL_GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E
+#define LOCAL_GL_TEXTURE_2D_STACK_MESAX 0x875A
+#define LOCAL_GL_TEXTURE_3D 0x806F
+#define LOCAL_GL_TEXTURE_3D_BINDING_EXT 0x806A
+#define LOCAL_GL_TEXTURE_3D_BINDING_OES 0x806A
+#define LOCAL_GL_TEXTURE_3D_EXT 0x806F
+#define LOCAL_GL_TEXTURE_3D_OES 0x806F
+#define LOCAL_GL_TEXTURE_4DSIZE_SGIS 0x8136
+#define LOCAL_GL_TEXTURE_4D_BINDING_SGIS 0x814F
+#define LOCAL_GL_TEXTURE_4D_SGIS 0x8134
+#define LOCAL_GL_TEXTURE_ALPHA_MODULATE_IMG 0x8C06
+#define LOCAL_GL_TEXTURE_ALPHA_SIZE 0x805F
+#define LOCAL_GL_TEXTURE_ALPHA_SIZE_EXT 0x805F
+#define LOCAL_GL_TEXTURE_ALPHA_TYPE 0x8C13
+#define LOCAL_GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13
+#define LOCAL_GL_TEXTURE_APPLICATION_MODE_EXT 0x834F
+#define LOCAL_GL_TEXTURE_BASE_LEVEL 0x813C
+#define LOCAL_GL_TEXTURE_BASE_LEVEL_SGIS 0x813C
+#define LOCAL_GL_TEXTURE_BINDING_1D 0x8068
+#define LOCAL_GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C
+#define LOCAL_GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C
+#define LOCAL_GL_TEXTURE_BINDING_2D 0x8069
+#define LOCAL_GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define LOCAL_GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D
+#define LOCAL_GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104
+#define LOCAL_GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105
+#define LOCAL_GL_TEXTURE_BINDING_3D 0x806A
+#define LOCAL_GL_TEXTURE_BINDING_3D_OES 0x806A
+#define LOCAL_GL_TEXTURE_BINDING_BUFFER 0x8C2C
+#define LOCAL_GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C
+#define LOCAL_GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514
+#define LOCAL_GL_TEXTURE_BINDING_CUBE_MAP_OES 0x8514
+#define LOCAL_GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67
+#define LOCAL_GL_TEXTURE_BINDING_RECTANGLE 0x84F6
+#define LOCAL_GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6
+#define LOCAL_GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
+#define LOCAL_GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53
+#define LOCAL_GL_TEXTURE_BIT 0x00040000
+#define LOCAL_GL_TEXTURE_BLUE_SIZE 0x805E
+#define LOCAL_GL_TEXTURE_BLUE_SIZE_EXT 0x805E
+#define LOCAL_GL_TEXTURE_BLUE_TYPE 0x8C12
+#define LOCAL_GL_TEXTURE_BLUE_TYPE_ARB 0x8C12
+#define LOCAL_GL_TEXTURE_BORDER 0x1005
+#define LOCAL_GL_TEXTURE_BORDER_COLOR 0x1004
+#define LOCAL_GL_TEXTURE_BORDER_COLOR_NV 0x1004
+#define LOCAL_GL_TEXTURE_BORDER_VALUES_NV 0x871A
+#define LOCAL_GL_TEXTURE_BUFFER 0x8C2A
+#define LOCAL_GL_TEXTURE_BUFFER_ARB 0x8C2A
+#define LOCAL_GL_TEXTURE_BUFFER_BINDING 0x8C2A
+#define LOCAL_GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D
+#define LOCAL_GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D
+#define LOCAL_GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D
+#define LOCAL_GL_TEXTURE_BUFFER_EXT 0x8C2A
+#define LOCAL_GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E
+#define LOCAL_GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E
+#define LOCAL_GL_TEXTURE_BUFFER_OFFSET 0x919D
+#define LOCAL_GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F
+#define LOCAL_GL_TEXTURE_BUFFER_SIZE 0x919E
+#define LOCAL_GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171
+#define LOCAL_GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176
+#define LOCAL_GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172
+#define LOCAL_GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175
+#define LOCAL_GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173
+#define LOCAL_GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174
+#define LOCAL_GL_TEXTURE_COLOR_SAMPLES_NV 0x9046
+#define LOCAL_GL_TEXTURE_COLOR_TABLE_SGI 0x80BC
+#define LOCAL_GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF
+#define LOCAL_GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF
+#define LOCAL_GL_TEXTURE_COMPARE_FUNC 0x884D
+#define LOCAL_GL_TEXTURE_COMPARE_FUNC_ARB 0x884D
+#define LOCAL_GL_TEXTURE_COMPARE_FUNC_EXT 0x884D
+#define LOCAL_GL_TEXTURE_COMPARE_MODE 0x884C
+#define LOCAL_GL_TEXTURE_COMPARE_MODE_ARB 0x884C
+#define LOCAL_GL_TEXTURE_COMPARE_MODE_EXT 0x884C
+#define LOCAL_GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B
+#define LOCAL_GL_TEXTURE_COMPARE_SGIX 0x819A
+#define LOCAL_GL_TEXTURE_COMPONENTS 0x1003
+#define LOCAL_GL_TEXTURE_COMPRESSED 0x86A1
+#define LOCAL_GL_TEXTURE_COMPRESSED_ARB 0x86A1
+#define LOCAL_GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2
+#define LOCAL_GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3
+#define LOCAL_GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1
+#define LOCAL_GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0
+#define LOCAL_GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0
+#define LOCAL_GL_TEXTURE_COMPRESSION_HINT 0x84EF
+#define LOCAL_GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF
+#define LOCAL_GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6
+#define LOCAL_GL_TEXTURE_COORD_ARRAY 0x8078
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_EXT 0x8078
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_POINTER 0x8092
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_SIZE 0x8088
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_TYPE 0x8089
+#define LOCAL_GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089
+#define LOCAL_GL_TEXTURE_COORD_NV 0x8C79
+#define LOCAL_GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045
+#define LOCAL_GL_TEXTURE_CROP_RECT_OES 0x8B9D
+#define LOCAL_GL_TEXTURE_CUBE_MAP 0x8513
+#define LOCAL_GL_TEXTURE_CUBE_MAP_ARB 0x8513
+#define LOCAL_GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
+#define LOCAL_GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009
+#define LOCAL_GL_TEXTURE_CUBE_MAP_EXT 0x8513
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES 0x8516
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES 0x8518
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A
+#define LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES 0x851A
+#define LOCAL_GL_TEXTURE_CUBE_MAP_OES 0x8513
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES 0x8515
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES 0x8517
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519
+#define LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES 0x8519
+#define LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
+#define LOCAL_GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001
+#define LOCAL_GL_TEXTURE_DEFORMATION_SGIX 0x8195
+#define LOCAL_GL_TEXTURE_DEPTH 0x8071
+#define LOCAL_GL_TEXTURE_DEPTH_EXT 0x8071
+#define LOCAL_GL_TEXTURE_DEPTH_QCOM 0x8BD4
+#define LOCAL_GL_TEXTURE_DEPTH_SIZE 0x884A
+#define LOCAL_GL_TEXTURE_DEPTH_SIZE_ARB 0x884A
+#define LOCAL_GL_TEXTURE_DEPTH_TYPE 0x8C16
+#define LOCAL_GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16
+#define LOCAL_GL_TEXTURE_DS_SIZE_NV 0x871D
+#define LOCAL_GL_TEXTURE_DT_SIZE_NV 0x871E
+#define LOCAL_GL_TEXTURE_ENV 0x2300
+#define LOCAL_GL_TEXTURE_ENV_BIAS_SGIX 0x80BE
+#define LOCAL_GL_TEXTURE_ENV_COLOR 0x2201
+#define LOCAL_GL_TEXTURE_ENV_MODE 0x2200
+#define LOCAL_GL_TEXTURE_EXTERNAL_OES 0x8D65
+#define LOCAL_GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
+#define LOCAL_GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008
+#define LOCAL_GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147
+#define LOCAL_GL_TEXTURE_FILTER_CONTROL 0x8500
+#define LOCAL_GL_TEXTURE_FILTER_CONTROL_EXT 0x8500
+#define LOCAL_GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107
+#define LOCAL_GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C
+#define LOCAL_GL_TEXTURE_FORMAT_QCOM 0x8BD6
+#define LOCAL_GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
+#define LOCAL_GL_TEXTURE_GATHER 0x82A2
+#define LOCAL_GL_TEXTURE_GATHER_SHADOW 0x82A3
+#define LOCAL_GL_TEXTURE_GEN_MODE 0x2500
+#define LOCAL_GL_TEXTURE_GEN_MODE_OES 0x2500
+#define LOCAL_GL_TEXTURE_GEN_Q 0x0C63
+#define LOCAL_GL_TEXTURE_GEN_R 0x0C62
+#define LOCAL_GL_TEXTURE_GEN_S 0x0C60
+#define LOCAL_GL_TEXTURE_GEN_STR_OES 0x8D60
+#define LOCAL_GL_TEXTURE_GEN_T 0x0C61
+#define LOCAL_GL_TEXTURE_GEQUAL_R_SGIX 0x819D
+#define LOCAL_GL_TEXTURE_GREEN_SIZE 0x805D
+#define LOCAL_GL_TEXTURE_GREEN_SIZE_EXT 0x805D
+#define LOCAL_GL_TEXTURE_GREEN_TYPE 0x8C11
+#define LOCAL_GL_TEXTURE_GREEN_TYPE_ARB 0x8C11
+#define LOCAL_GL_TEXTURE_HEIGHT 0x1001
+#define LOCAL_GL_TEXTURE_HEIGHT_QCOM 0x8BD3
+#define LOCAL_GL_TEXTURE_HI_SIZE_NV 0x871B
+#define LOCAL_GL_TEXTURE_IMAGE_FORMAT 0x828F
+#define LOCAL_GL_TEXTURE_IMAGE_TYPE 0x8290
+#define LOCAL_GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8
+#define LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
+#define LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F
+#define LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF
+#define LOCAL_GL_TEXTURE_INDEX_SIZE_EXT 0x80ED
+#define LOCAL_GL_TEXTURE_INTENSITY_SIZE 0x8061
+#define LOCAL_GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061
+#define LOCAL_GL_TEXTURE_INTENSITY_TYPE 0x8C15
+#define LOCAL_GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15
+#define LOCAL_GL_TEXTURE_INTERNAL_FORMAT 0x1003
+#define LOCAL_GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5
+#define LOCAL_GL_TEXTURE_LEQUAL_R_SGIX 0x819C
+#define LOCAL_GL_TEXTURE_LIGHTING_MODE_HP 0x8167
+#define LOCAL_GL_TEXTURE_LIGHT_EXT 0x8350
+#define LOCAL_GL_TEXTURE_LOD_BIAS 0x8501
+#define LOCAL_GL_TEXTURE_LOD_BIAS_EXT 0x8501
+#define LOCAL_GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190
+#define LOCAL_GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E
+#define LOCAL_GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F
+#define LOCAL_GL_TEXTURE_LO_SIZE_NV 0x871C
+#define LOCAL_GL_TEXTURE_LUMINANCE_SIZE 0x8060
+#define LOCAL_GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060
+#define LOCAL_GL_TEXTURE_LUMINANCE_TYPE 0x8C14
+#define LOCAL_GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14
+#define LOCAL_GL_TEXTURE_MAG_FILTER 0x2800
+#define LOCAL_GL_TEXTURE_MAG_SIZE_NV 0x871F
+#define LOCAL_GL_TEXTURE_MATERIAL_FACE_EXT 0x8351
+#define LOCAL_GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352
+#define LOCAL_GL_TEXTURE_MATRIX 0x0BA8
+#define LOCAL_GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES 0x898F
+#define LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#define LOCAL_GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B
+#define LOCAL_GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369
+#define LOCAL_GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A
+#define LOCAL_GL_TEXTURE_MAX_LEVEL 0x813D
+#define LOCAL_GL_TEXTURE_MAX_LEVEL_APPLE 0x813D
+#define LOCAL_GL_TEXTURE_MAX_LEVEL_SGIS 0x813D
+#define LOCAL_GL_TEXTURE_MAX_LOD 0x813B
+#define LOCAL_GL_TEXTURE_MAX_LOD_SGIS 0x813B
+#define LOCAL_GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF
+#define LOCAL_GL_TEXTURE_MIN_FILTER 0x2801
+#define LOCAL_GL_TEXTURE_MIN_LOD 0x813A
+#define LOCAL_GL_TEXTURE_MIN_LOD_SGIS 0x813A
+#define LOCAL_GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E
+#define LOCAL_GL_TEXTURE_NORMAL_EXT 0x85AF
+#define LOCAL_GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9
+#define LOCAL_GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB
+#define LOCAL_GL_TEXTURE_POST_SPECULAR_HP 0x8168
+#define LOCAL_GL_TEXTURE_PRE_SPECULAR_HP 0x8169
+#define LOCAL_GL_TEXTURE_PRIORITY 0x8066
+#define LOCAL_GL_TEXTURE_PRIORITY_EXT 0x8066
+#define LOCAL_GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7
+#define LOCAL_GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8
+#define LOCAL_GL_TEXTURE_RECTANGLE 0x84F5
+#define LOCAL_GL_TEXTURE_RECTANGLE_ARB 0x84F5
+#define LOCAL_GL_TEXTURE_RECTANGLE_NV 0x84F5
+#define LOCAL_GL_TEXTURE_RED_SIZE 0x805C
+#define LOCAL_GL_TEXTURE_RED_SIZE_EXT 0x805C
+#define LOCAL_GL_TEXTURE_RED_TYPE 0x8C10
+#define LOCAL_GL_TEXTURE_RED_TYPE_ARB 0x8C10
+#define LOCAL_GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54
+#define LOCAL_GL_TEXTURE_RENDERBUFFER_NV 0x8E55
+#define LOCAL_GL_TEXTURE_RESIDENT 0x8067
+#define LOCAL_GL_TEXTURE_RESIDENT_EXT 0x8067
+#define LOCAL_GL_TEXTURE_SAMPLES 0x9106
+#define LOCAL_GL_TEXTURE_SAMPLES_IMG 0x9136
+#define LOCAL_GL_TEXTURE_SHADER_NV 0x86DE
+#define LOCAL_GL_TEXTURE_SHADOW 0x82A1
+#define LOCAL_GL_TEXTURE_SHARED_SIZE 0x8C3F
+#define LOCAL_GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F
+#define LOCAL_GL_TEXTURE_SPARSE_ARB 0x91A6
+#define LOCAL_GL_TEXTURE_SRGB_DECODE_EXT 0x8A48
+#define LOCAL_GL_TEXTURE_STACK_DEPTH 0x0BA5
+#define LOCAL_GL_TEXTURE_STENCIL_SIZE 0x88F1
+#define LOCAL_GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
+#define LOCAL_GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC
+#define LOCAL_GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001
+#define LOCAL_GL_TEXTURE_SWIZZLE_A 0x8E45
+#define LOCAL_GL_TEXTURE_SWIZZLE_A_EXT 0x8E45
+#define LOCAL_GL_TEXTURE_SWIZZLE_B 0x8E44
+#define LOCAL_GL_TEXTURE_SWIZZLE_B_EXT 0x8E44
+#define LOCAL_GL_TEXTURE_SWIZZLE_G 0x8E43
+#define LOCAL_GL_TEXTURE_SWIZZLE_G_EXT 0x8E43
+#define LOCAL_GL_TEXTURE_SWIZZLE_R 0x8E42
+#define LOCAL_GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#define LOCAL_GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46
+#define LOCAL_GL_TEXTURE_SWIZZLE_R_EXT 0x8E42
+#define LOCAL_GL_TEXTURE_TARGET_QCOM 0x8BDA
+#define LOCAL_GL_TEXTURE_TOO_LARGE_EXT 0x8065
+#define LOCAL_GL_TEXTURE_TYPE_QCOM 0x8BD7
+#define LOCAL_GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F
+#define LOCAL_GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
+#define LOCAL_GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100
+#define LOCAL_GL_TEXTURE_USAGE_ANGLE 0x93A2
+#define LOCAL_GL_TEXTURE_VIEW 0x82B5
+#define LOCAL_GL_TEXTURE_VIEW_MIN_LAYER 0x82DD
+#define LOCAL_GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB
+#define LOCAL_GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE
+#define LOCAL_GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC
+#define LOCAL_GL_TEXTURE_WIDTH 0x1000
+#define LOCAL_GL_TEXTURE_WIDTH_QCOM 0x8BD2
+#define LOCAL_GL_TEXTURE_WRAP_Q_SGIS 0x8137
+#define LOCAL_GL_TEXTURE_WRAP_R 0x8072
+#define LOCAL_GL_TEXTURE_WRAP_R_EXT 0x8072
+#define LOCAL_GL_TEXTURE_WRAP_R_OES 0x8072
+#define LOCAL_GL_TEXTURE_WRAP_S 0x2802
+#define LOCAL_GL_TEXTURE_WRAP_T 0x2803
+#define LOCAL_GL_TEXT_FRAGMENT_SHADER_ATI 0x8200
+#define LOCAL_GL_TIMEOUT_EXPIRED 0x911B
+#define LOCAL_GL_TIMEOUT_EXPIRED_APPLE 0x911B
+#define LOCAL_GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF
+#define LOCAL_GL_TIMEOUT_IGNORED_APPLE 0xFFFFFFFFFFFFFFFF
+#define LOCAL_GL_TIMESTAMP 0x8E28
+#define LOCAL_GL_TIMESTAMP_EXT 0x8E28
+#define LOCAL_GL_TIME_ELAPSED 0x88BF
+#define LOCAL_GL_TIME_ELAPSED_EXT 0x88BF
+#define LOCAL_GL_TOP_LEVEL_ARRAY_SIZE 0x930C
+#define LOCAL_GL_TOP_LEVEL_ARRAY_STRIDE 0x930D
+#define LOCAL_GL_TRACE_ALL_BITS_MESA 0xFFFF
+#define LOCAL_GL_TRACE_ARRAYS_BIT_MESA 0x0004
+#define LOCAL_GL_TRACE_ERRORS_BIT_MESA 0x0020
+#define LOCAL_GL_TRACE_MASK_MESA 0x8755
+#define LOCAL_GL_TRACE_NAME_MESA 0x8756
+#define LOCAL_GL_TRACE_OPERATIONS_BIT_MESA 0x0001
+#define LOCAL_GL_TRACE_PIXELS_BIT_MESA 0x0010
+#define LOCAL_GL_TRACE_PRIMITIVES_BIT_MESA 0x0002
+#define LOCAL_GL_TRACE_TEXTURES_BIT_MESA 0x0008
+#define LOCAL_GL_TRACK_MATRIX_NV 0x8648
+#define LOCAL_GL_TRACK_MATRIX_TRANSFORM_NV 0x8649
+#define LOCAL_GL_TRANSFORM_BIT 0x00001000
+#define LOCAL_GL_TRANSFORM_FEEDBACK 0x8E22
+#define LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24
+#define LOCAL_GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BINDING 0x8E25
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84
+#define LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C
+#define LOCAL_GL_TRANSFORM_FEEDBACK_NV 0x8E22
+#define LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23
+#define LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
+#define LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88
+#define LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88
+#define LOCAL_GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYING 0x92F4
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76
+#define LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76
+#define LOCAL_GL_TRANSFORM_HINT_APPLE 0x85B1
+#define LOCAL_GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE 0x93A0
+#define LOCAL_GL_TRANSLATE_2D_NV 0x9090
+#define LOCAL_GL_TRANSLATE_3D_NV 0x9091
+#define LOCAL_GL_TRANSLATE_X_NV 0x908E
+#define LOCAL_GL_TRANSLATE_Y_NV 0x908F
+#define LOCAL_GL_TRANSPOSE_AFFINE_2D_NV 0x9096
+#define LOCAL_GL_TRANSPOSE_AFFINE_3D_NV 0x9098
+#define LOCAL_GL_TRANSPOSE_COLOR_MATRIX 0x84E6
+#define LOCAL_GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6
+#define LOCAL_GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7
+#define LOCAL_GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3
+#define LOCAL_GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3
+#define LOCAL_GL_TRANSPOSE_NV 0x862C
+#define LOCAL_GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E
+#define LOCAL_GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4
+#define LOCAL_GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4
+#define LOCAL_GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5
+#define LOCAL_GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5
+#define LOCAL_GL_TRIANGLES 0x0004
+#define LOCAL_GL_TRIANGLES_ADJACENCY 0x000C
+#define LOCAL_GL_TRIANGLES_ADJACENCY_ARB 0x000C
+#define LOCAL_GL_TRIANGLES_ADJACENCY_EXT 0x000C
+#define LOCAL_GL_TRIANGLE_FAN 0x0006
+#define LOCAL_GL_TRIANGLE_LIST_SUN 0x81D7
+#define LOCAL_GL_TRIANGLE_MESH_SUN 0x8615
+#define LOCAL_GL_TRIANGLE_STRIP 0x0005
+#define LOCAL_GL_TRIANGLE_STRIP_ADJACENCY 0x000D
+#define LOCAL_GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D
+#define LOCAL_GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D
+#define LOCAL_GL_TRIANGULAR_NV 0x90A5
+#define LOCAL_GL_TRUE 1
+#define LOCAL_GL_TYPE 0x92FA
+#define LOCAL_GL_UNCORRELATED_NV 0x9282
+#define LOCAL_GL_UNDEFINED_APPLE 0x8A1C
+#define LOCAL_GL_UNDEFINED_VERTEX 0x8260
+#define LOCAL_GL_UNIFORM 0x92E1
+#define LOCAL_GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+#define LOCAL_GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA
+#define LOCAL_GL_UNIFORM_BARRIER_BIT 0x00000004
+#define LOCAL_GL_UNIFORM_BARRIER_BIT_EXT 0x00000004
+#define LOCAL_GL_UNIFORM_BLOCK 0x92E2
+#define LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+#define LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43
+#define LOCAL_GL_UNIFORM_BLOCK_BINDING 0x8A3F
+#define LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+#define LOCAL_GL_UNIFORM_BLOCK_INDEX 0x8A3A
+#define LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1
+#define LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44
+#define LOCAL_GL_UNIFORM_BUFFER 0x8A11
+#define LOCAL_GL_UNIFORM_BUFFER_BINDING 0x8A28
+#define LOCAL_GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF
+#define LOCAL_GL_UNIFORM_BUFFER_EXT 0x8DEE
+#define LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34
+#define LOCAL_GL_UNIFORM_BUFFER_SIZE 0x8A2A
+#define LOCAL_GL_UNIFORM_BUFFER_START 0x8A29
+#define LOCAL_GL_UNIFORM_IS_ROW_MAJOR 0x8A3E
+#define LOCAL_GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+#define LOCAL_GL_UNIFORM_NAME_LENGTH 0x8A39
+#define LOCAL_GL_UNIFORM_OFFSET 0x8A3B
+#define LOCAL_GL_UNIFORM_SIZE 0x8A38
+#define LOCAL_GL_UNIFORM_TYPE 0x8A37
+#define LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255
+#define LOCAL_GL_UNKNOWN_CONTEXT_RESET_EXT 0x8255
+#define LOCAL_GL_UNPACK_ALIGNMENT 0x0CF5
+#define LOCAL_GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2
+#define LOCAL_GL_UNPACK_CMYK_HINT_EXT 0x800F
+#define LOCAL_GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129
+#define LOCAL_GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128
+#define LOCAL_GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A
+#define LOCAL_GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127
+#define LOCAL_GL_UNPACK_COMPRESSED_SIZE_SGIX 0x831A
+#define LOCAL_GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5
+#define LOCAL_GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133
+#define LOCAL_GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define LOCAL_GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E
+#define LOCAL_GL_UNPACK_LSB_FIRST 0x0CF1
+#define LOCAL_GL_UNPACK_RESAMPLE_OML 0x8985
+#define LOCAL_GL_UNPACK_RESAMPLE_SGIX 0x842D
+#define LOCAL_GL_UNPACK_ROW_BYTES_APPLE 0x8A16
+#define LOCAL_GL_UNPACK_ROW_LENGTH 0x0CF2
+#define LOCAL_GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
+#define LOCAL_GL_UNPACK_SKIP_IMAGES 0x806D
+#define LOCAL_GL_UNPACK_SKIP_IMAGES_EXT 0x806D
+#define LOCAL_GL_UNPACK_SKIP_PIXELS 0x0CF4
+#define LOCAL_GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
+#define LOCAL_GL_UNPACK_SKIP_ROWS 0x0CF3
+#define LOCAL_GL_UNPACK_SKIP_ROWS_EXT 0x0CF3
+#define LOCAL_GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132
+#define LOCAL_GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1
+#define LOCAL_GL_UNPACK_SWAP_BYTES 0x0CF0
+#define LOCAL_GL_UNSIGNALED 0x9118
+#define LOCAL_GL_UNSIGNALED_APPLE 0x9118
+#define LOCAL_GL_UNSIGNED_BYTE 0x1401
+#define LOCAL_GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define LOCAL_GL_UNSIGNED_BYTE_2_3_3_REV_EXT 0x8362
+#define LOCAL_GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define LOCAL_GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032
+#define LOCAL_GL_UNSIGNED_IDENTITY_NV 0x8536
+#define LOCAL_GL_UNSIGNED_INT 0x1405
+#define LOCAL_GL_UNSIGNED_INT16_NV 0x8FF0
+#define LOCAL_GL_UNSIGNED_INT16_VEC2_NV 0x8FF1
+#define LOCAL_GL_UNSIGNED_INT16_VEC3_NV 0x8FF2
+#define LOCAL_GL_UNSIGNED_INT16_VEC4_NV 0x8FF3
+#define LOCAL_GL_UNSIGNED_INT64_AMD 0x8BC2
+#define LOCAL_GL_UNSIGNED_INT64_ARB 0x140F
+#define LOCAL_GL_UNSIGNED_INT64_NV 0x140F
+#define LOCAL_GL_UNSIGNED_INT64_VEC2_NV 0x8FF5
+#define LOCAL_GL_UNSIGNED_INT64_VEC3_NV 0x8FF6
+#define LOCAL_GL_UNSIGNED_INT64_VEC4_NV 0x8FF7
+#define LOCAL_GL_UNSIGNED_INT8_NV 0x8FEC
+#define LOCAL_GL_UNSIGNED_INT8_VEC2_NV 0x8FED
+#define LOCAL_GL_UNSIGNED_INT8_VEC3_NV 0x8FEE
+#define LOCAL_GL_UNSIGNED_INT8_VEC4_NV 0x8FEF
+#define LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
+#define LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B
+#define LOCAL_GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define LOCAL_GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036
+#define LOCAL_GL_UNSIGNED_INT_10_10_10_2_OES 0x8DF6
+#define LOCAL_GL_UNSIGNED_INT_24_8 0x84FA
+#define LOCAL_GL_UNSIGNED_INT_24_8_EXT 0x84FA
+#define LOCAL_GL_UNSIGNED_INT_24_8_MESA 0x8751
+#define LOCAL_GL_UNSIGNED_INT_24_8_NV 0x84FA
+#define LOCAL_GL_UNSIGNED_INT_24_8_OES 0x84FA
+#define LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#define LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368
+#define LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E
+#define LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E
+#define LOCAL_GL_UNSIGNED_INT_8_24_REV_MESA 0x8752
+#define LOCAL_GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define LOCAL_GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035
+#define LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV_EXT 0x8367
+#define LOCAL_GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB
+#define LOCAL_GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_1D 0x9062
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D 0x9063
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_3D 0x9064
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_CUBE 0x9066
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
+#define LOCAL_GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A
+#define LOCAL_GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F
+#define LOCAL_GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58
+#define LOCAL_GL_UNSIGNED_INT_VEC2 0x8DC6
+#define LOCAL_GL_UNSIGNED_INT_VEC2_EXT 0x8DC6
+#define LOCAL_GL_UNSIGNED_INT_VEC3 0x8DC7
+#define LOCAL_GL_UNSIGNED_INT_VEC3_EXT 0x8DC7
+#define LOCAL_GL_UNSIGNED_INT_VEC4 0x8DC8
+#define LOCAL_GL_UNSIGNED_INT_VEC4_EXT 0x8DC8
+#define LOCAL_GL_UNSIGNED_INVERT_NV 0x8537
+#define LOCAL_GL_UNSIGNED_NORMALIZED 0x8C17
+#define LOCAL_GL_UNSIGNED_NORMALIZED_ARB 0x8C17
+#define LOCAL_GL_UNSIGNED_NORMALIZED_EXT 0x8C17
+#define LOCAL_GL_UNSIGNED_SHORT 0x1403
+#define LOCAL_GL_UNSIGNED_SHORT_15_1_MESA 0x8753
+#define LOCAL_GL_UNSIGNED_SHORT_1_15_REV_MESA 0x8754
+#define LOCAL_GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define LOCAL_GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366
+#define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033
+#define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365
+#define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365
+#define LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define LOCAL_GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034
+#define LOCAL_GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define LOCAL_GL_UNSIGNED_SHORT_5_6_5_EXT 0x8363
+#define LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV_EXT 0x8364
+#define LOCAL_GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA
+#define LOCAL_GL_UNSIGNED_SHORT_8_8_MESA 0x85BA
+#define LOCAL_GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
+#define LOCAL_GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB
+#define LOCAL_GL_UPPER_LEFT 0x8CA2
+#define LOCAL_GL_USE_MISSING_GLYPH_NV 0x90AA
+#define LOCAL_GL_UTF16_NV 0x909B
+#define LOCAL_GL_UTF8_NV 0x909A
+#define LOCAL_GL_V2F 0x2A20
+#define LOCAL_GL_V3F 0x2A21
+#define LOCAL_GL_VALIDATE_STATUS 0x8B83
+#define LOCAL_GL_VARIABLE_A_NV 0x8523
+#define LOCAL_GL_VARIABLE_B_NV 0x8524
+#define LOCAL_GL_VARIABLE_C_NV 0x8525
+#define LOCAL_GL_VARIABLE_D_NV 0x8526
+#define LOCAL_GL_VARIABLE_E_NV 0x8527
+#define LOCAL_GL_VARIABLE_F_NV 0x8528
+#define LOCAL_GL_VARIABLE_G_NV 0x8529
+#define LOCAL_GL_VARIANT_ARRAY_EXT 0x87E8
+#define LOCAL_GL_VARIANT_ARRAY_POINTER_EXT 0x87E9
+#define LOCAL_GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6
+#define LOCAL_GL_VARIANT_ARRAY_TYPE_EXT 0x87E7
+#define LOCAL_GL_VARIANT_DATATYPE_EXT 0x87E5
+#define LOCAL_GL_VARIANT_EXT 0x87C1
+#define LOCAL_GL_VARIANT_VALUE_EXT 0x87E4
+#define LOCAL_GL_VBO_FREE_MEMORY_ATI 0x87FB
+#define LOCAL_GL_VECTOR_EXT 0x87BF
+#define LOCAL_GL_VENDOR 0x1F00
+#define LOCAL_GL_VERSION 0x1F02
+#define LOCAL_GL_VERSION_ES_CL_1_0 1
+#define LOCAL_GL_VERSION_ES_CL_1_1 1
+#define LOCAL_GL_VERSION_ES_CM_1_1 1
+#define LOCAL_GL_VERTEX23_BIT_PGI 0x00000004
+#define LOCAL_GL_VERTEX4_BIT_PGI 0x00000008
+#define LOCAL_GL_VERTEX_ARRAY 0x8074
+#define LOCAL_GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21
+#define LOCAL_GL_VERTEX_ARRAY_BINDING 0x85B5
+#define LOCAL_GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5
+#define LOCAL_GL_VERTEX_ARRAY_BINDING_OES 0x85B5
+#define LOCAL_GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896
+#define LOCAL_GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896
+#define LOCAL_GL_VERTEX_ARRAY_COUNT_EXT 0x807D
+#define LOCAL_GL_VERTEX_ARRAY_EXT 0x8074
+#define LOCAL_GL_VERTEX_ARRAY_KHR 0x8074
+#define LOCAL_GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B
+#define LOCAL_GL_VERTEX_ARRAY_LIST_IBM 103070
+#define LOCAL_GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080
+#define LOCAL_GL_VERTEX_ARRAY_OBJECT_AMD 0x9154
+#define LOCAL_GL_VERTEX_ARRAY_OBJECT_EXT 0x9154
+#define LOCAL_GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5
+#define LOCAL_GL_VERTEX_ARRAY_POINTER 0x808E
+#define LOCAL_GL_VERTEX_ARRAY_POINTER_EXT 0x808E
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_APPLE 0x851D
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_NV 0x851D
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F
+#define LOCAL_GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533
+#define LOCAL_GL_VERTEX_ARRAY_SIZE 0x807A
+#define LOCAL_GL_VERTEX_ARRAY_SIZE_EXT 0x807A
+#define LOCAL_GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F
+#define LOCAL_GL_VERTEX_ARRAY_STRIDE 0x807C
+#define LOCAL_GL_VERTEX_ARRAY_STRIDE_EXT 0x807C
+#define LOCAL_GL_VERTEX_ARRAY_TYPE 0x807B
+#define LOCAL_GL_VERTEX_ARRAY_TYPE_EXT 0x807B
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE 0x88FE
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR_EXT 0x88FE
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR_NV 0x88FE
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625
+#define LOCAL_GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E
+#define LOCAL_GL_VERTEX_ATTRIB_BINDING 0x82D4
+#define LOCAL_GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00
+#define LOCAL_GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03
+#define LOCAL_GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05
+#define LOCAL_GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04
+#define LOCAL_GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02
+#define LOCAL_GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01
+#define LOCAL_GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07
+#define LOCAL_GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09
+#define LOCAL_GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08
+#define LOCAL_GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06
+#define LOCAL_GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5
+#define LOCAL_GL_VERTEX_BINDING_DIVISOR 0x82D6
+#define LOCAL_GL_VERTEX_BINDING_OFFSET 0x82D7
+#define LOCAL_GL_VERTEX_BINDING_STRIDE 0x82D8
+#define LOCAL_GL_VERTEX_BLEND_ARB 0x86A7
+#define LOCAL_GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B
+#define LOCAL_GL_VERTEX_DATA_HINT_PGI 0x1A22A
+#define LOCAL_GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4
+#define LOCAL_GL_VERTEX_ID_NV 0x8C7B
+#define LOCAL_GL_VERTEX_ID_SWIZZLE_AMD 0x91A5
+#define LOCAL_GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF
+#define LOCAL_GL_VERTEX_PRECLIP_SGIX 0x83EE
+#define LOCAL_GL_VERTEX_PROGRAM_ARB 0x8620
+#define LOCAL_GL_VERTEX_PROGRAM_BINDING_NV 0x864A
+#define LOCAL_GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA 0x8BB7
+#define LOCAL_GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA 0x8BB6
+#define LOCAL_GL_VERTEX_PROGRAM_CALLBACK_MESA 0x8BB5
+#define LOCAL_GL_VERTEX_PROGRAM_NV 0x8620
+#define LOCAL_GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2
+#define LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#define LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642
+#define LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642
+#define LOCAL_GL_VERTEX_PROGRAM_POSITION_MESA 0x8BB4
+#define LOCAL_GL_VERTEX_PROGRAM_TWO_SIDE 0x8643
+#define LOCAL_GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643
+#define LOCAL_GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643
+#define LOCAL_GL_VERTEX_SHADER 0x8B31
+#define LOCAL_GL_VERTEX_SHADER_ARB 0x8B31
+#define LOCAL_GL_VERTEX_SHADER_BINDING_EXT 0x8781
+#define LOCAL_GL_VERTEX_SHADER_BIT 0x00000001
+#define LOCAL_GL_VERTEX_SHADER_BIT_EXT 0x00000001
+#define LOCAL_GL_VERTEX_SHADER_EXT 0x8780
+#define LOCAL_GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF
+#define LOCAL_GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1
+#define LOCAL_GL_VERTEX_SHADER_LOCALS_EXT 0x87D3
+#define LOCAL_GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2
+#define LOCAL_GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4
+#define LOCAL_GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0
+#define LOCAL_GL_VERTEX_SOURCE_ATI 0x8774
+#define LOCAL_GL_VERTEX_STATE_PROGRAM_NV 0x8621
+#define LOCAL_GL_VERTEX_STREAM0_ATI 0x876C
+#define LOCAL_GL_VERTEX_STREAM1_ATI 0x876D
+#define LOCAL_GL_VERTEX_STREAM2_ATI 0x876E
+#define LOCAL_GL_VERTEX_STREAM3_ATI 0x876F
+#define LOCAL_GL_VERTEX_STREAM4_ATI 0x8770
+#define LOCAL_GL_VERTEX_STREAM5_ATI 0x8771
+#define LOCAL_GL_VERTEX_STREAM6_ATI 0x8772
+#define LOCAL_GL_VERTEX_STREAM7_ATI 0x8773
+#define LOCAL_GL_VERTEX_SUBROUTINE 0x92E8
+#define LOCAL_GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE
+#define LOCAL_GL_VERTEX_TEXTURE 0x829B
+#define LOCAL_GL_VERTEX_WEIGHTING_EXT 0x8509
+#define LOCAL_GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C
+#define LOCAL_GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510
+#define LOCAL_GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D
+#define LOCAL_GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F
+#define LOCAL_GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E
+#define LOCAL_GL_VERTICAL_LINE_TO_NV 0x08
+#define LOCAL_GL_VIBRANCE_BIAS_NV 0x8719
+#define LOCAL_GL_VIBRANCE_SCALE_NV 0x8713
+#define LOCAL_GL_VIDEO_BUFFER_BINDING_NV 0x9021
+#define LOCAL_GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D
+#define LOCAL_GL_VIDEO_BUFFER_NV 0x9020
+#define LOCAL_GL_VIDEO_BUFFER_PITCH_NV 0x9028
+#define LOCAL_GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B
+#define LOCAL_GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A
+#define LOCAL_GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039
+#define LOCAL_GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038
+#define LOCAL_GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C
+#define LOCAL_GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026
+#define LOCAL_GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029
+#define LOCAL_GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A
+#define LOCAL_GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B
+#define LOCAL_GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C
+#define LOCAL_GL_VIEWPORT 0x0BA2
+#define LOCAL_GL_VIEWPORT_BIT 0x00000800
+#define LOCAL_GL_VIEWPORT_BOUNDS_RANGE 0x825D
+#define LOCAL_GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F
+#define LOCAL_GL_VIEWPORT_SUBPIXEL_BITS 0x825C
+#define LOCAL_GL_VIEW_CLASS_128_BITS 0x82C4
+#define LOCAL_GL_VIEW_CLASS_16_BITS 0x82CA
+#define LOCAL_GL_VIEW_CLASS_24_BITS 0x82C9
+#define LOCAL_GL_VIEW_CLASS_32_BITS 0x82C8
+#define LOCAL_GL_VIEW_CLASS_48_BITS 0x82C7
+#define LOCAL_GL_VIEW_CLASS_64_BITS 0x82C6
+#define LOCAL_GL_VIEW_CLASS_8_BITS 0x82CB
+#define LOCAL_GL_VIEW_CLASS_96_BITS 0x82C5
+#define LOCAL_GL_VIEW_CLASS_BPTC_FLOAT 0x82D3
+#define LOCAL_GL_VIEW_CLASS_BPTC_UNORM 0x82D2
+#define LOCAL_GL_VIEW_CLASS_RGTC1_RED 0x82D0
+#define LOCAL_GL_VIEW_CLASS_RGTC2_RG 0x82D1
+#define LOCAL_GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC
+#define LOCAL_GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD
+#define LOCAL_GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE
+#define LOCAL_GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF
+#define LOCAL_GL_VIEW_COMPATIBILITY_CLASS 0x82B6
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197
+#define LOCAL_GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197
+#define LOCAL_GL_VIVIDLIGHT_NV 0x92A6
+#define LOCAL_GL_VOLATILE_APPLE 0x8A1A
+#define LOCAL_GL_WAIT_FAILED 0x911D
+#define LOCAL_GL_WAIT_FAILED_APPLE 0x911D
+#define LOCAL_GL_WEIGHT_ARRAY_ARB 0x86AD
+#define LOCAL_GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E
+#define LOCAL_GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E
+#define LOCAL_GL_WEIGHT_ARRAY_BUFFER_BINDING_OES 0x889E
+#define LOCAL_GL_WEIGHT_ARRAY_OES 0x86AD
+#define LOCAL_GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC
+#define LOCAL_GL_WEIGHT_ARRAY_POINTER_OES 0x86AC
+#define LOCAL_GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB
+#define LOCAL_GL_WEIGHT_ARRAY_SIZE_OES 0x86AB
+#define LOCAL_GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA
+#define LOCAL_GL_WEIGHT_ARRAY_STRIDE_OES 0x86AA
+#define LOCAL_GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9
+#define LOCAL_GL_WEIGHT_ARRAY_TYPE_OES 0x86A9
+#define LOCAL_GL_WEIGHT_SUM_UNITY_ARB 0x86A6
+#define LOCAL_GL_WIDE_LINE_HINT_PGI 0x1A222
+#define LOCAL_GL_WRAP_BORDER_SUN 0x81D4
+#define LOCAL_GL_WRITEONLY_RENDERING_QCOM 0x8823
+#define LOCAL_GL_WRITE_DISCARD_NV 0x88BE
+#define LOCAL_GL_WRITE_ONLY 0x88B9
+#define LOCAL_GL_WRITE_ONLY_ARB 0x88B9
+#define LOCAL_GL_WRITE_ONLY_OES 0x88B9
+#define LOCAL_GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A
+#define LOCAL_GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878
+#define LOCAL_GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C
+#define LOCAL_GL_W_EXT 0x87D8
+#define LOCAL_GL_XOR 0x1506
+#define LOCAL_GL_XOR_NV 0x1506
+#define LOCAL_GL_X_EXT 0x87D5
+#define LOCAL_GL_YCBAYCR8A_4224_NV 0x9032
+#define LOCAL_GL_YCBCR_422_APPLE 0x85B9
+#define LOCAL_GL_YCBCR_MESA 0x8757
+#define LOCAL_GL_YCBYCR8_422_NV 0x9031
+#define LOCAL_GL_YCRCBA_SGIX 0x8319
+#define LOCAL_GL_YCRCB_422_SGIX 0x81BB
+#define LOCAL_GL_YCRCB_444_SGIX 0x81BC
+#define LOCAL_GL_YCRCB_SGIX 0x8318
+#define LOCAL_GL_Y_EXT 0x87D6
+#define LOCAL_GL_Z400_BINARY_AMD 0x8740
+#define LOCAL_GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036
+#define LOCAL_GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037
+#define LOCAL_GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035
+#define LOCAL_GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034
+#define LOCAL_GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033
+#define LOCAL_GL_ZERO 0
+#define LOCAL_GL_ZERO_EXT 0x87DD
+#define LOCAL_GL_ZOOM_X 0x0D16
+#define LOCAL_GL_ZOOM_Y 0x0D17
+#define LOCAL_GL_Z_EXT 0x87D7
+
+
+// EGL
+#define LOCAL_EGL_ALPHA_FORMAT 0x3088
+#define LOCAL_EGL_ALPHA_FORMAT_NONPRE 0x308B
+#define LOCAL_EGL_ALPHA_FORMAT_PRE 0x308C
+#define LOCAL_EGL_ALPHA_MASK_SIZE 0x303E
+#define LOCAL_EGL_ALPHA_SIZE 0x3021
+#define LOCAL_EGL_ALREADY_SIGNALED_NV 0x30EA
+#define LOCAL_EGL_AUTO_STEREO_NV 0x3136
+#define LOCAL_EGL_BACK_BUFFER 0x3084
+#define LOCAL_EGL_BAD_ACCESS 0x3002
+#define LOCAL_EGL_BAD_ALLOC 0x3003
+#define LOCAL_EGL_BAD_ATTRIBUTE 0x3004
+#define LOCAL_EGL_BAD_CONFIG 0x3005
+#define LOCAL_EGL_BAD_CONTEXT 0x3006
+#define LOCAL_EGL_BAD_CURRENT_SURFACE 0x3007
+#define LOCAL_EGL_BAD_DISPLAY 0x3008
+#define LOCAL_EGL_BAD_MATCH 0x3009
+#define LOCAL_EGL_BAD_NATIVE_PIXMAP 0x300A
+#define LOCAL_EGL_BAD_NATIVE_WINDOW 0x300B
+#define LOCAL_EGL_BAD_PARAMETER 0x300C
+#define LOCAL_EGL_BAD_STATE_KHR 0x321C
+#define LOCAL_EGL_BAD_STREAM_KHR 0x321B
+#define LOCAL_EGL_BAD_SURFACE 0x300D
+#define LOCAL_EGL_BIND_TO_TEXTURE_RGB 0x3039
+#define LOCAL_EGL_BIND_TO_TEXTURE_RGBA 0x303A
+#define LOCAL_EGL_BITMAP_ORIGIN_KHR 0x30C8
+#define LOCAL_EGL_BITMAP_PITCH_KHR 0x30C7
+#define LOCAL_EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR 0x30CC
+#define LOCAL_EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR 0x30CB
+#define LOCAL_EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR 0x30CA
+#define LOCAL_EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR 0x30CD
+#define LOCAL_EGL_BITMAP_PIXEL_RED_OFFSET_KHR 0x30C9
+#define LOCAL_EGL_BITMAP_PIXEL_SIZE_KHR 0x3110
+#define LOCAL_EGL_BITMAP_POINTER_KHR 0x30C6
+#define LOCAL_EGL_BLUE_SIZE 0x3022
+#define LOCAL_EGL_BUFFER_AGE_EXT 0x313D
+#define LOCAL_EGL_BUFFER_COUNT_NV 0x321D
+#define LOCAL_EGL_BUFFER_DESTROYED 0x3095
+#define LOCAL_EGL_BUFFER_PRESERVED 0x3094
+#define LOCAL_EGL_BUFFER_SIZE 0x3020
+#define LOCAL_EGL_CLIENT_APIS 0x308D
+#define LOCAL_EGL_CLIENT_PIXMAP_POINTER_HI 0x8F74
+#define LOCAL_EGL_CL_EVENT_HANDLE_KHR 0x309C
+#define LOCAL_EGL_COLORSPACE 0x3087
+#define LOCAL_EGL_COLORSPACE_LINEAR 0x308A
+#define LOCAL_EGL_COLORSPACE_sRGB 0x3089
+#define LOCAL_EGL_COLOR_ARGB_HI 0x8F73
+#define LOCAL_EGL_COLOR_BUFFER_TYPE 0x303F
+#define LOCAL_EGL_COLOR_FORMAT_HI 0x8F70
+#define LOCAL_EGL_COLOR_RGBA_HI 0x8F72
+#define LOCAL_EGL_COLOR_RGB_HI 0x8F71
+#define LOCAL_EGL_CONDITION_SATISFIED_KHR 0x30F6
+#define LOCAL_EGL_CONDITION_SATISFIED_NV 0x30EC
+#define LOCAL_EGL_CONFIG_CAVEAT 0x3027
+#define LOCAL_EGL_CONFIG_ID 0x3028
+#define LOCAL_EGL_CONFORMANT 0x3042
+#define LOCAL_EGL_CONFORMANT_KHR 0x3042
+#define LOCAL_EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR 0x321E
+#define LOCAL_EGL_CONSUMER_FRAME_KHR 0x3213
+#define LOCAL_EGL_CONSUMER_LATENCY_USEC_KHR 0x3210
+#define LOCAL_EGL_CONTEXT_CLIENT_TYPE 0x3097
+#define LOCAL_EGL_CONTEXT_CLIENT_VERSION 0x3098
+#define LOCAL_EGL_CONTEXT_FLAGS_KHR 0x30FC
+#define LOCAL_EGL_CONTEXT_LOST 0x300E
+#define LOCAL_EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
+#define LOCAL_EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
+#define LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
+#define LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
+#define LOCAL_EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
+#define LOCAL_EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
+#define LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
+#define LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
+#define LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
+#define LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
+#define LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
+#define LOCAL_EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101
+#define LOCAL_EGL_CONTEXT_PRIORITY_LEVEL_IMG 0x3100
+#define LOCAL_EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103
+#define LOCAL_EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102
+#define LOCAL_EGL_CORE_NATIVE_ENGINE 0x305B
+#define LOCAL_EGL_COVERAGE_BUFFERS_NV 0x30E0
+#define LOCAL_EGL_COVERAGE_SAMPLES_NV 0x30E1
+#define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV 0x3132
+#define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV 0x3133
+#define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NV 0x3131
+#define LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
+#define LOCAL_EGL_DXGI_KEYED_MUTEX_ANGLE 0x33A2
+#define LOCAL_EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
+#define LOCAL_EGL_DEPTH_ENCODING_NONE_NV 0
+#define LOCAL_EGL_DEPTH_ENCODING_NONLINEAR_NV 0x30E3
+#define LOCAL_EGL_DEPTH_ENCODING_NV 0x30E2
+#define LOCAL_EGL_DEPTH_SIZE 0x3025
+#define LOCAL_EGL_DISCARD_SAMPLES_ARM 0x3286
+#define LOCAL_EGL_DISPLAY_SCALING 10000
+#define LOCAL_EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
+#define LOCAL_EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
+#define LOCAL_EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
+#define LOCAL_EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
+#define LOCAL_EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
+#define LOCAL_EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
+#define LOCAL_EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
+#define LOCAL_EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
+#define LOCAL_EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
+#define LOCAL_EGL_DONT_CARE ((EGLint)-1)
+#define LOCAL_EGL_DRAW 0x3059
+#define LOCAL_EGL_DRM_BUFFER_FORMAT_ARGB32_MESA 0x31D2
+#define LOCAL_EGL_DRM_BUFFER_FORMAT_MESA 0x31D0
+#define LOCAL_EGL_DRM_BUFFER_MESA 0x31D3
+#define LOCAL_EGL_DRM_BUFFER_STRIDE_MESA 0x31D4
+#define LOCAL_EGL_DRM_BUFFER_USE_MESA 0x31D1
+#define LOCAL_EGL_DRM_BUFFER_USE_SCANOUT_MESA 0x00000001
+#define LOCAL_EGL_DRM_BUFFER_USE_SHARE_MESA 0x00000002
+#define LOCAL_EGL_EXTENSIONS 0x3055
+#define LOCAL_EGL_FALSE 0
+#define LOCAL_EGL_FOREVER_KHR 0xFFFFFFFFFFFFFFFF
+#define LOCAL_EGL_FOREVER_NV 0xFFFFFFFFFFFFFFFF
+#define LOCAL_EGL_FORMAT_RGBA_8888_EXACT_KHR 0x30C2
+#define LOCAL_EGL_FORMAT_RGBA_8888_KHR 0x30C3
+#define LOCAL_EGL_FORMAT_RGB_565_EXACT_KHR 0x30C0
+#define LOCAL_EGL_FORMAT_RGB_565_KHR 0x30C1
+#define LOCAL_EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147
+#define LOCAL_EGL_GL_COLORSPACE_KHR 0x309D
+#define LOCAL_EGL_GL_COLORSPACE_LINEAR_KHR 0x308A
+#define LOCAL_EGL_GL_COLORSPACE_SRGB_KHR 0x3089
+#define LOCAL_EGL_GL_RENDERBUFFER_KHR 0x30B9
+#define LOCAL_EGL_GL_TEXTURE_2D_KHR 0x30B1
+#define LOCAL_EGL_GL_TEXTURE_3D_KHR 0x30B2
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR 0x30B4
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR 0x30B6
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR 0x30B8
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR 0x30B3
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR 0x30B5
+#define LOCAL_EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR 0x30B7
+#define LOCAL_EGL_GL_TEXTURE_LEVEL_KHR 0x30BC
+#define LOCAL_EGL_GL_TEXTURE_ZOFFSET_KHR 0x30BD
+#define LOCAL_EGL_GREEN_SIZE 0x3023
+#define LOCAL_EGL_HEIGHT 0x3056
+#define LOCAL_EGL_HORIZONTAL_RESOLUTION 0x3090
+#define LOCAL_EGL_IMAGE_PRESERVED_KHR 0x30D2
+#define LOCAL_EGL_INTEROP_BIT_KHR 0x0010
+#define LOCAL_EGL_ITU_REC2020_EXT 0x3281
+#define LOCAL_EGL_ITU_REC601_EXT 0x327F
+#define LOCAL_EGL_ITU_REC709_EXT 0x3280
+#define LOCAL_EGL_LARGEST_PBUFFER 0x3058
+#define LOCAL_EGL_LEVEL 0x3029
+#define LOCAL_EGL_LINUX_DMA_BUF_EXT 0x3270
+#define LOCAL_EGL_LINUX_DRM_FOURCC_EXT 0x3271
+#define LOCAL_EGL_LOCK_SURFACE_BIT_KHR 0x0080
+#define LOCAL_EGL_LOCK_USAGE_HINT_KHR 0x30C5
+#define LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT 0x31BF
+#define LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
+#define LOCAL_EGL_LOWER_LEFT_KHR 0x30CE
+#define LOCAL_EGL_LUMINANCE_BUFFER 0x308F
+#define LOCAL_EGL_LUMINANCE_SIZE 0x303D
+#define LOCAL_EGL_MAP_PRESERVE_PIXELS_KHR 0x30C4
+#define LOCAL_EGL_MATCH_FORMAT_KHR 0x3043
+#define LOCAL_EGL_MATCH_NATIVE_PIXMAP 0x3041
+#define LOCAL_EGL_MAX_PBUFFER_HEIGHT 0x302A
+#define LOCAL_EGL_MAX_PBUFFER_PIXELS 0x302B
+#define LOCAL_EGL_MAX_PBUFFER_WIDTH 0x302C
+#define LOCAL_EGL_MAX_SWAP_INTERVAL 0x303C
+#define LOCAL_EGL_MIN_SWAP_INTERVAL 0x303B
+#define LOCAL_EGL_MIPMAP_LEVEL 0x3083
+#define LOCAL_EGL_MIPMAP_TEXTURE 0x3082
+#define LOCAL_EGL_MULTISAMPLE_RESOLVE 0x3099
+#define LOCAL_EGL_MULTISAMPLE_RESOLVE_BOX 0x309B
+#define LOCAL_EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200
+#define LOCAL_EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A
+#define LOCAL_EGL_MULTIVIEW_VIEW_COUNT_EXT 0x3134
+#define LOCAL_EGL_NATIVE_BUFFER_ANDROID 0x3140
+#define LOCAL_EGL_NATIVE_PIXMAP_KHR 0x30B0
+#define LOCAL_EGL_NATIVE_RENDERABLE 0x302D
+#define LOCAL_EGL_NATIVE_VISUAL_ID 0x302E
+#define LOCAL_EGL_NATIVE_VISUAL_TYPE 0x302F
+#define LOCAL_EGL_NONE 0x3038
+#define LOCAL_EGL_NON_CONFORMANT_CONFIG 0x3051
+#define LOCAL_EGL_NOT_INITIALIZED 0x3001
+#define LOCAL_EGL_NO_CONTEXT ((EGLContext)0)
+#define LOCAL_EGL_NO_DISPLAY ((EGLDisplay)0)
+#define LOCAL_EGL_NO_FILE_DESCRIPTOR_KHR ((EGLNativeFileDescriptorKHR)(-1))
+#define LOCAL_EGL_NO_IMAGE_KHR ((EGLImageKHR)0)
+#define LOCAL_EGL_NO_NATIVE_FENCE_FD_ANDROID -1
+#define LOCAL_EGL_NO_RESET_NOTIFICATION_EXT 0x31BE
+#define LOCAL_EGL_NO_RESET_NOTIFICATION_KHR 0x31BE
+#define LOCAL_EGL_NO_STREAM_KHR ((EGLStreamKHR)0)
+#define LOCAL_EGL_NO_SURFACE ((EGLSurface)0)
+#define LOCAL_EGL_NO_SYNC_KHR ((EGLSyncKHR)0)
+#define LOCAL_EGL_NO_SYNC_NV ((EGLSyncNV)0)
+#define LOCAL_EGL_NO_TEXTURE 0x305C
+#define LOCAL_EGL_OPENGL_API 0x30A2
+#define LOCAL_EGL_OPENGL_BIT 0x0008
+#define LOCAL_EGL_OPENGL_ES2_BIT 0x0004
+#define LOCAL_EGL_OPENGL_ES3_BIT_KHR 0x00000040
+#define LOCAL_EGL_OPENGL_ES_API 0x30A0
+#define LOCAL_EGL_OPENGL_ES_BIT 0x0001
+#define LOCAL_EGL_OPENMAX_IL_BIT_KHR 0x0020
+#define LOCAL_EGL_OPENVG_API 0x30A1
+#define LOCAL_EGL_OPENVG_BIT 0x0002
+#define LOCAL_EGL_OPENVG_IMAGE 0x3096
+#define LOCAL_EGL_OPTIMAL_FORMAT_BIT_KHR 0x0100
+#define LOCAL_EGL_PBUFFER_BIT 0x0001
+#define LOCAL_EGL_PBUFFER_IMAGE_BIT_TAO 0x0008
+#define LOCAL_EGL_PBUFFER_PALETTE_IMAGE_BIT_TAO 0x0010
+#define LOCAL_EGL_PIXEL_ASPECT_RATIO 0x3092
+#define LOCAL_EGL_PIXMAP_BIT 0x0002
+#define LOCAL_EGL_PLATFORM_GBM_MESA 0x31D7
+#define LOCAL_EGL_PLATFORM_WAYLAND_EXT 0x31D8
+#define LOCAL_EGL_PLATFORM_X11_EXT 0x31D5
+#define LOCAL_EGL_PLATFORM_X11_SCREEN_EXT 0x31D6
+#define LOCAL_EGL_POST_SUB_BUFFER_SUPPORTED_NV 0x30BE
+#define LOCAL_EGL_PRODUCER_FRAME_KHR 0x3212
+#define LOCAL_EGL_PROTECTED_CONTENT_EXT 0x32C0
+#define LOCAL_EGL_READ 0x305A
+#define LOCAL_EGL_READ_SURFACE_BIT_KHR 0x0001
+#define LOCAL_EGL_RECORDABLE_ANDROID 0x3142
+#define LOCAL_EGL_RED_SIZE 0x3024
+#define LOCAL_EGL_RENDERABLE_TYPE 0x3040
+#define LOCAL_EGL_RENDER_BUFFER 0x3086
+#define LOCAL_EGL_RGB_BUFFER 0x308E
+#define LOCAL_EGL_SAMPLES 0x3031
+#define LOCAL_EGL_SAMPLE_BUFFERS 0x3032
+#define LOCAL_EGL_SAMPLE_RANGE_HINT_EXT 0x327C
+#define LOCAL_EGL_SHARED_IMAGE_NOK 0x30DA
+#define LOCAL_EGL_SIGNALED_KHR 0x30F2
+#define LOCAL_EGL_SIGNALED_NV 0x30E8
+#define LOCAL_EGL_SINGLE_BUFFER 0x3085
+#define LOCAL_EGL_SLOW_CONFIG 0x3050
+#define LOCAL_EGL_STENCIL_SIZE 0x3026
+#define LOCAL_EGL_STREAM_BIT_KHR 0x0800
+#define LOCAL_EGL_STREAM_FIFO_LENGTH_KHR 0x31FC
+#define LOCAL_EGL_STREAM_STATE_CONNECTING_KHR 0x3216
+#define LOCAL_EGL_STREAM_STATE_CREATED_KHR 0x3215
+#define LOCAL_EGL_STREAM_STATE_DISCONNECTED_KHR 0x321A
+#define LOCAL_EGL_STREAM_STATE_EMPTY_KHR 0x3217
+#define LOCAL_EGL_STREAM_STATE_KHR 0x3214
+#define LOCAL_EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR 0x3218
+#define LOCAL_EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR 0x3219
+#define LOCAL_EGL_STREAM_TIME_CONSUMER_KHR 0x31FE
+#define LOCAL_EGL_STREAM_TIME_NOW_KHR 0x31FD
+#define LOCAL_EGL_STREAM_TIME_PRODUCER_KHR 0x31FF
+#define LOCAL_EGL_SUCCESS 0x3000
+#define LOCAL_EGL_SURFACE_TYPE 0x3033
+#define LOCAL_EGL_SWAP_BEHAVIOR 0x3093
+#define LOCAL_EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
+#define LOCAL_EGL_SYNC_CL_EVENT_COMPLETE_KHR 0x30FF
+#define LOCAL_EGL_SYNC_CL_EVENT_KHR 0x30FE
+#define LOCAL_EGL_SYNC_CONDITION_KHR 0x30F8
+#define LOCAL_EGL_SYNC_CONDITION_NV 0x30EE
+#define LOCAL_EGL_SYNC_FENCE_KHR 0x30F9
+#define LOCAL_EGL_SYNC_FENCE_NV 0x30EF
+#define LOCAL_EGL_SYNC_FLUSH_COMMANDS_BIT_KHR 0x0001
+#define LOCAL_EGL_SYNC_FLUSH_COMMANDS_BIT_NV 0x0001
+#define LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144
+#define LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145
+#define LOCAL_EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID 0x3146
+#define LOCAL_EGL_SYNC_NEW_FRAME_NV 0x321F
+#define LOCAL_EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR 0x30F0
+#define LOCAL_EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV 0x30E6
+#define LOCAL_EGL_SYNC_REUSABLE_KHR 0x30FA
+#define LOCAL_EGL_SYNC_STATUS_KHR 0x30F1
+#define LOCAL_EGL_SYNC_STATUS_NV 0x30E7
+#define LOCAL_EGL_SYNC_TYPE_KHR 0x30F7
+#define LOCAL_EGL_SYNC_TYPE_NV 0x30ED
+#define LOCAL_EGL_TEXTURE_2D 0x305F
+#define LOCAL_EGL_TEXTURE_FORMAT 0x3080
+#define LOCAL_EGL_TEXTURE_RGB 0x305D
+#define LOCAL_EGL_TEXTURE_RGBA 0x305E
+#define LOCAL_EGL_TEXTURE_TARGET 0x3081
+#define LOCAL_EGL_TIMEOUT_EXPIRED_KHR 0x30F5
+#define LOCAL_EGL_TIMEOUT_EXPIRED_NV 0x30EB
+#define LOCAL_EGL_TRANSPARENT_BLUE_VALUE 0x3035
+#define LOCAL_EGL_TRANSPARENT_GREEN_VALUE 0x3036
+#define LOCAL_EGL_TRANSPARENT_RED_VALUE 0x3037
+#define LOCAL_EGL_TRANSPARENT_RGB 0x3052
+#define LOCAL_EGL_TRANSPARENT_TYPE 0x3034
+#define LOCAL_EGL_TRUE 1
+#define LOCAL_EGL_UNKNOWN ((EGLint)-1)
+#define LOCAL_EGL_UNSIGNALED_KHR 0x30F3
+#define LOCAL_EGL_UNSIGNALED_NV 0x30E9
+#define LOCAL_EGL_UPPER_LEFT_KHR 0x30CF
+#define LOCAL_EGL_VENDOR 0x3053
+#define LOCAL_EGL_VERSION 0x3054
+#define LOCAL_EGL_VERTICAL_RESOLUTION 0x3091
+#define LOCAL_EGL_VG_ALPHA_FORMAT 0x3088
+#define LOCAL_EGL_VG_ALPHA_FORMAT_NONPRE 0x308B
+#define LOCAL_EGL_VG_ALPHA_FORMAT_PRE 0x308C
+#define LOCAL_EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040
+#define LOCAL_EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR 0x0040
+#define LOCAL_EGL_VG_COLORSPACE 0x3087
+#define LOCAL_EGL_VG_COLORSPACE_LINEAR 0x308A
+#define LOCAL_EGL_VG_COLORSPACE_LINEAR_BIT 0x0020
+#define LOCAL_EGL_VG_COLORSPACE_LINEAR_BIT_KHR 0x0020
+#define LOCAL_EGL_VG_COLORSPACE_sRGB 0x3089
+#define LOCAL_EGL_VG_PARENT_IMAGE_KHR 0x30BA
+#define LOCAL_EGL_WIDTH 0x3057
+#define LOCAL_EGL_WINDOW_BIT 0x0004
+#define LOCAL_EGL_WRITE_SURFACE_BIT_KHR 0x0002
+#define LOCAL_EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D
+#define LOCAL_EGL_YUV_CHROMA_SITING_0_5_EXT 0x3285
+#define LOCAL_EGL_YUV_CHROMA_SITING_0_EXT 0x3284
+#define LOCAL_EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E
+#define LOCAL_EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B
+#define LOCAL_EGL_YUV_FULL_RANGE_EXT 0x3282
+#define LOCAL_EGL_YUV_NARROW_RANGE_EXT 0x3283
+
+
+// GLX
+#define LOCAL_GLX_3DFX_FULLSCREEN_MODE_MESA 0x2
+#define LOCAL_GLX_3DFX_WINDOW_MODE_MESA 0x1
+#define LOCAL_GLX_ACCUM_ALPHA_SIZE 17
+#define LOCAL_GLX_ACCUM_BLUE_SIZE 16
+#define LOCAL_GLX_ACCUM_BUFFER_BIT 0x00000080
+#define LOCAL_GLX_ACCUM_BUFFER_BIT_SGIX 0x00000080
+#define LOCAL_GLX_ACCUM_GREEN_SIZE 15
+#define LOCAL_GLX_ACCUM_RED_SIZE 14
+#define LOCAL_GLX_ALPHA_SIZE 11
+#define LOCAL_GLX_AUX0_EXT 0x20E2
+#define LOCAL_GLX_AUX1_EXT 0x20E3
+#define LOCAL_GLX_AUX2_EXT 0x20E4
+#define LOCAL_GLX_AUX3_EXT 0x20E5
+#define LOCAL_GLX_AUX4_EXT 0x20E6
+#define LOCAL_GLX_AUX5_EXT 0x20E7
+#define LOCAL_GLX_AUX6_EXT 0x20E8
+#define LOCAL_GLX_AUX7_EXT 0x20E9
+#define LOCAL_GLX_AUX8_EXT 0x20EA
+#define LOCAL_GLX_AUX9_EXT 0x20EB
+#define LOCAL_GLX_AUX_BUFFERS 7
+#define LOCAL_GLX_AUX_BUFFERS_BIT 0x00000010
+#define LOCAL_GLX_AUX_BUFFERS_BIT_SGIX 0x00000010
+#define LOCAL_GLX_BACK_BUFFER_AGE_EXT 0x20F4
+#define LOCAL_GLX_BACK_EXT 0x20E0
+#define LOCAL_GLX_BACK_LEFT_BUFFER_BIT 0x00000004
+#define LOCAL_GLX_BACK_LEFT_BUFFER_BIT_SGIX 0x00000004
+#define LOCAL_GLX_BACK_LEFT_EXT 0x20E0
+#define LOCAL_GLX_BACK_RIGHT_BUFFER_BIT 0x00000008
+#define LOCAL_GLX_BACK_RIGHT_BUFFER_BIT_SGIX 0x00000008
+#define LOCAL_GLX_BACK_RIGHT_EXT 0x20E1
+#define LOCAL_GLX_BAD_ATTRIBUTE 2
+#define LOCAL_GLX_BAD_CONTEXT 5
+#define LOCAL_GLX_BAD_ENUM 7
+#define LOCAL_GLX_BAD_HYPERPIPE_CONFIG_SGIX 91
+#define LOCAL_GLX_BAD_HYPERPIPE_SGIX 92
+#define LOCAL_GLX_BAD_SCREEN 1
+#define LOCAL_GLX_BAD_VALUE 6
+#define LOCAL_GLX_BAD_VISUAL 4
+#define LOCAL_GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2
+#define LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1
+#define LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0
+#define LOCAL_GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3
+#define LOCAL_GLX_BLENDED_RGBA_SGIS 0x8025
+#define LOCAL_GLX_BLUE_SIZE 10
+#define LOCAL_GLX_BUFFER_CLOBBER_MASK_SGIX 0x08000000
+#define LOCAL_GLX_BUFFER_SIZE 2
+#define LOCAL_GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK 0x04000000
+#define LOCAL_GLX_BufferSwapComplete 1
+#define LOCAL_GLX_COLOR_INDEX_BIT 0x00000002
+#define LOCAL_GLX_COLOR_INDEX_BIT_SGIX 0x00000002
+#define LOCAL_GLX_COLOR_INDEX_TYPE 0x8015
+#define LOCAL_GLX_COLOR_INDEX_TYPE_SGIX 0x8015
+#define LOCAL_GLX_COLOR_SAMPLES_NV 0x20B3
+#define LOCAL_GLX_CONFIG_CAVEAT 0x20
+#define LOCAL_GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB 0x2095
+#define LOCAL_GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define LOCAL_GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define LOCAL_GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
+#define LOCAL_GLX_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define LOCAL_GLX_CONTEXT_FLAGS_ARB 0x2094
+#define LOCAL_GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define LOCAL_GLX_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008
+#define LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
+#define LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
+#define LOCAL_GLX_COPY_COMPLETE_INTEL 0x8181
+#define LOCAL_GLX_COVERAGE_SAMPLES_NV 100001
+#define LOCAL_GLX_DAMAGED 0x8020
+#define LOCAL_GLX_DAMAGED_SGIX 0x8020
+#define LOCAL_GLX_DEPTH_BUFFER_BIT 0x00000020
+#define LOCAL_GLX_DEPTH_BUFFER_BIT_SGIX 0x00000020
+#define LOCAL_GLX_DEPTH_SIZE 12
+#define LOCAL_GLX_DEVICE_ID_NV 0x20CD
+#define LOCAL_GLX_DIGITAL_MEDIA_PBUFFER_SGIX 0x8024
+#define LOCAL_GLX_DIRECT_COLOR 0x8003
+#define LOCAL_GLX_DIRECT_COLOR_EXT 0x8003
+#define LOCAL_GLX_DONT_CARE 0xFFFFFFFF
+#define LOCAL_GLX_DOUBLEBUFFER 5
+#define LOCAL_GLX_DRAWABLE_TYPE 0x8010
+#define LOCAL_GLX_DRAWABLE_TYPE_SGIX 0x8010
+#define LOCAL_GLX_EVENT_MASK 0x801F
+#define LOCAL_GLX_EVENT_MASK_SGIX 0x801F
+#define LOCAL_GLX_EXCHANGE_COMPLETE_INTEL 0x8180
+#define LOCAL_GLX_EXTENSIONS 0x3
+#define LOCAL_GLX_EXTENSION_NAME "GLX"
+#define LOCAL_GLX_FBCONFIG_ID 0x8013
+#define LOCAL_GLX_FBCONFIG_ID_SGIX 0x8013
+#define LOCAL_GLX_FLIP_COMPLETE_INTEL 0x8182
+#define LOCAL_GLX_FLOAT_COMPONENTS_NV 0x20B0
+#define LOCAL_GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
+#define LOCAL_GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20B2
+#define LOCAL_GLX_FRONT_EXT 0x20DE
+#define LOCAL_GLX_FRONT_LEFT_BUFFER_BIT 0x00000001
+#define LOCAL_GLX_FRONT_LEFT_BUFFER_BIT_SGIX 0x00000001
+#define LOCAL_GLX_FRONT_LEFT_EXT 0x20DE
+#define LOCAL_GLX_FRONT_RIGHT_BUFFER_BIT 0x00000002
+#define LOCAL_GLX_FRONT_RIGHT_BUFFER_BIT_SGIX 0x00000002
+#define LOCAL_GLX_FRONT_RIGHT_EXT 0x20DF
+#define LOCAL_GLX_GPU_CLOCK_AMD 0x21A4
+#define LOCAL_GLX_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2
+#define LOCAL_GLX_GPU_NUM_PIPES_AMD 0x21A5
+#define LOCAL_GLX_GPU_NUM_RB_AMD 0x21A7
+#define LOCAL_GLX_GPU_NUM_SIMD_AMD 0x21A6
+#define LOCAL_GLX_GPU_NUM_SPI_AMD 0x21A8
+#define LOCAL_GLX_GPU_OPENGL_VERSION_STRING_AMD 0x1F02
+#define LOCAL_GLX_GPU_RAM_AMD 0x21A3
+#define LOCAL_GLX_GPU_RENDERER_STRING_AMD 0x1F01
+#define LOCAL_GLX_GPU_VENDOR_AMD 0x1F00
+#define LOCAL_GLX_GRAY_SCALE 0x8006
+#define LOCAL_GLX_GRAY_SCALE_EXT 0x8006
+#define LOCAL_GLX_GREEN_SIZE 9
+#define LOCAL_GLX_HEIGHT 0x801E
+#define LOCAL_GLX_HEIGHT_SGIX 0x801E
+#define LOCAL_GLX_HYPERPIPE_DISPLAY_PIPE_SGIX 0x00000001
+#define LOCAL_GLX_HYPERPIPE_ID_SGIX 0x8030
+#define LOCAL_GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX 80
+#define LOCAL_GLX_HYPERPIPE_PIXEL_AVERAGE_SGIX 0x00000004
+#define LOCAL_GLX_HYPERPIPE_RENDER_PIPE_SGIX 0x00000002
+#define LOCAL_GLX_HYPERPIPE_STEREO_SGIX 0x00000003
+#define LOCAL_GLX_LARGEST_PBUFFER 0x801C
+#define LOCAL_GLX_LARGEST_PBUFFER_SGIX 0x801C
+#define LOCAL_GLX_LATE_SWAPS_TEAR_EXT 0x20F3
+#define LOCAL_GLX_LEVEL 3
+#define LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
+#define LOCAL_GLX_MAX_PBUFFER_HEIGHT 0x8017
+#define LOCAL_GLX_MAX_PBUFFER_HEIGHT_SGIX 0x8017
+#define LOCAL_GLX_MAX_PBUFFER_PIXELS 0x8018
+#define LOCAL_GLX_MAX_PBUFFER_PIXELS_SGIX 0x8018
+#define LOCAL_GLX_MAX_PBUFFER_WIDTH 0x8016
+#define LOCAL_GLX_MAX_PBUFFER_WIDTH_SGIX 0x8016
+#define LOCAL_GLX_MAX_SWAP_INTERVAL_EXT 0x20F2
+#define LOCAL_GLX_MIPMAP_TEXTURE_EXT 0x20D7
+#define LOCAL_GLX_MULTISAMPLE_SUB_RECT_HEIGHT_SGIS 0x8027
+#define LOCAL_GLX_MULTISAMPLE_SUB_RECT_WIDTH_SGIS 0x8026
+#define LOCAL_GLX_NONE 0x8000
+#define LOCAL_GLX_NONE_EXT 0x8000
+#define LOCAL_GLX_NON_CONFORMANT_CONFIG 0x800D
+#define LOCAL_GLX_NON_CONFORMANT_VISUAL_EXT 0x800D
+#define LOCAL_GLX_NO_EXTENSION 3
+#define LOCAL_GLX_NO_RESET_NOTIFICATION_ARB 0x8261
+#define LOCAL_GLX_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF
+#define LOCAL_GLX_NUM_VIDEO_SLOTS_NV 0x20F0
+#define LOCAL_GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX 0x801A
+#define LOCAL_GLX_OPTIMAL_PBUFFER_WIDTH_SGIX 0x8019
+#define LOCAL_GLX_PBUFFER 0x8023
+#define LOCAL_GLX_PBUFFER_BIT 0x00000004
+#define LOCAL_GLX_PBUFFER_BIT_SGIX 0x00000004
+#define LOCAL_GLX_PBUFFER_CLOBBER_MASK 0x08000000
+#define LOCAL_GLX_PBUFFER_HEIGHT 0x8040
+#define LOCAL_GLX_PBUFFER_SGIX 0x8023
+#define LOCAL_GLX_PBUFFER_WIDTH 0x8041
+#define LOCAL_GLX_PIPE_RECT_LIMITS_SGIX 0x00000002
+#define LOCAL_GLX_PIPE_RECT_SGIX 0x00000001
+#define LOCAL_GLX_PIXMAP_BIT 0x00000002
+#define LOCAL_GLX_PIXMAP_BIT_SGIX 0x00000002
+#define LOCAL_GLX_PRESERVED_CONTENTS 0x801B
+#define LOCAL_GLX_PRESERVED_CONTENTS_SGIX 0x801B
+#define LOCAL_GLX_PSEUDO_COLOR 0x8004
+#define LOCAL_GLX_PSEUDO_COLOR_EXT 0x8004
+#define LOCAL_GLX_PbufferClobber 0
+#define LOCAL_GLX_RED_SIZE 8
+#define LOCAL_GLX_RENDER_TYPE 0x8011
+#define LOCAL_GLX_RENDER_TYPE_SGIX 0x8011
+#define LOCAL_GLX_RGBA 4
+#define LOCAL_GLX_RGBA_BIT 0x00000001
+#define LOCAL_GLX_RGBA_BIT_SGIX 0x00000001
+#define LOCAL_GLX_RGBA_FLOAT_BIT_ARB 0x00000004
+#define LOCAL_GLX_RGBA_FLOAT_TYPE_ARB 0x20B9
+#define LOCAL_GLX_RGBA_TYPE 0x8014
+#define LOCAL_GLX_RGBA_TYPE_SGIX 0x8014
+#define LOCAL_GLX_RGBA_UNSIGNED_FLOAT_BIT_EXT 0x00000008
+#define LOCAL_GLX_RGBA_UNSIGNED_FLOAT_TYPE_EXT 0x20B1
+#define LOCAL_GLX_SAMPLES 100001
+#define LOCAL_GLX_SAMPLES_3DFX 0x8051
+#define LOCAL_GLX_SAMPLES_ARB 100001
+#define LOCAL_GLX_SAMPLES_SGIS 100001
+#define LOCAL_GLX_SAMPLE_BUFFERS 100000
+#define LOCAL_GLX_SAMPLE_BUFFERS_3DFX 0x8050
+#define LOCAL_GLX_SAMPLE_BUFFERS_ARB 100000
+#define LOCAL_GLX_SAMPLE_BUFFERS_BIT_SGIX 0x00000100
+#define LOCAL_GLX_SAMPLE_BUFFERS_SGIS 100000
+#define LOCAL_GLX_SAVED 0x8021
+#define LOCAL_GLX_SAVED_SGIX 0x8021
+#define LOCAL_GLX_SCREEN 0x800C
+#define LOCAL_GLX_SCREEN_EXT 0x800C
+#define LOCAL_GLX_SHARE_CONTEXT_EXT 0x800A
+#define LOCAL_GLX_SLOW_CONFIG 0x8001
+#define LOCAL_GLX_SLOW_VISUAL_EXT 0x8001
+#define LOCAL_GLX_STATIC_COLOR 0x8005
+#define LOCAL_GLX_STATIC_COLOR_EXT 0x8005
+#define LOCAL_GLX_STATIC_GRAY 0x8007
+#define LOCAL_GLX_STATIC_GRAY_EXT 0x8007
+#define LOCAL_GLX_STENCIL_BUFFER_BIT 0x00000040
+#define LOCAL_GLX_STENCIL_BUFFER_BIT_SGIX 0x00000040
+#define LOCAL_GLX_STENCIL_SIZE 13
+#define LOCAL_GLX_STEREO 6
+#define LOCAL_GLX_SWAP_COPY_OML 0x8062
+#define LOCAL_GLX_SWAP_EXCHANGE_OML 0x8061
+#define LOCAL_GLX_SWAP_INTERVAL_EXT 0x20F1
+#define LOCAL_GLX_SWAP_METHOD_OML 0x8060
+#define LOCAL_GLX_SWAP_UNDEFINED_OML 0x8063
+#define LOCAL_GLX_SYNC_FRAME_SGIX 0x00000000
+#define LOCAL_GLX_SYNC_SWAP_SGIX 0x00000001
+#define LOCAL_GLX_TEXTURE_1D_BIT_EXT 0x00000001
+#define LOCAL_GLX_TEXTURE_1D_EXT 0x20DB
+#define LOCAL_GLX_TEXTURE_2D_BIT_EXT 0x00000002
+#define LOCAL_GLX_TEXTURE_2D_EXT 0x20DC
+#define LOCAL_GLX_TEXTURE_FORMAT_EXT 0x20D5
+#define LOCAL_GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8
+#define LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA
+#define LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9
+#define LOCAL_GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004
+#define LOCAL_GLX_TEXTURE_RECTANGLE_EXT 0x20DD
+#define LOCAL_GLX_TEXTURE_TARGET_EXT 0x20D6
+#define LOCAL_GLX_TRANSPARENT_ALPHA_VALUE 0x28
+#define LOCAL_GLX_TRANSPARENT_ALPHA_VALUE_EXT 0x28
+#define LOCAL_GLX_TRANSPARENT_BLUE_VALUE 0x27
+#define LOCAL_GLX_TRANSPARENT_BLUE_VALUE_EXT 0x27
+#define LOCAL_GLX_TRANSPARENT_GREEN_VALUE 0x26
+#define LOCAL_GLX_TRANSPARENT_GREEN_VALUE_EXT 0x26
+#define LOCAL_GLX_TRANSPARENT_INDEX 0x8009
+#define LOCAL_GLX_TRANSPARENT_INDEX_EXT 0x8009
+#define LOCAL_GLX_TRANSPARENT_INDEX_VALUE 0x24
+#define LOCAL_GLX_TRANSPARENT_INDEX_VALUE_EXT 0x24
+#define LOCAL_GLX_TRANSPARENT_RED_VALUE 0x25
+#define LOCAL_GLX_TRANSPARENT_RED_VALUE_EXT 0x25
+#define LOCAL_GLX_TRANSPARENT_RGB 0x8008
+#define LOCAL_GLX_TRANSPARENT_RGB_EXT 0x8008
+#define LOCAL_GLX_TRANSPARENT_TYPE 0x23
+#define LOCAL_GLX_TRANSPARENT_TYPE_EXT 0x23
+#define LOCAL_GLX_TRUE_COLOR 0x8002
+#define LOCAL_GLX_TRUE_COLOR_EXT 0x8002
+#define LOCAL_GLX_UNIQUE_ID_NV 0x20CE
+#define LOCAL_GLX_USE_GL 1
+#define LOCAL_GLX_VENDOR 0x1
+#define LOCAL_GLX_VERSION 0x2
+#define LOCAL_GLX_VIDEO_OUT_ALPHA_NV 0x20C4
+#define LOCAL_GLX_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6
+#define LOCAL_GLX_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7
+#define LOCAL_GLX_VIDEO_OUT_COLOR_NV 0x20C3
+#define LOCAL_GLX_VIDEO_OUT_DEPTH_NV 0x20C5
+#define LOCAL_GLX_VIDEO_OUT_FIELD_1_NV 0x20C9
+#define LOCAL_GLX_VIDEO_OUT_FIELD_2_NV 0x20CA
+#define LOCAL_GLX_VIDEO_OUT_FRAME_NV 0x20C8
+#define LOCAL_GLX_VIDEO_OUT_STACKED_FIELDS_1_2_NV 0x20CB
+#define LOCAL_GLX_VIDEO_OUT_STACKED_FIELDS_2_1_NV 0x20CC
+#define LOCAL_GLX_VISUAL_CAVEAT_EXT 0x20
+#define LOCAL_GLX_VISUAL_ID 0x800B
+#define LOCAL_GLX_VISUAL_ID_EXT 0x800B
+#define LOCAL_GLX_VISUAL_SELECT_GROUP_SGIX 0x8028
+#define LOCAL_GLX_WIDTH 0x801D
+#define LOCAL_GLX_WIDTH_SGIX 0x801D
+#define LOCAL_GLX_WINDOW 0x8022
+#define LOCAL_GLX_WINDOW_BIT 0x00000001
+#define LOCAL_GLX_WINDOW_BIT_SGIX 0x00000001
+#define LOCAL_GLX_WINDOW_SGIX 0x8022
+#define LOCAL_GLX_X_RENDERABLE 0x8012
+#define LOCAL_GLX_X_RENDERABLE_SGIX 0x8012
+#define LOCAL_GLX_X_VISUAL_TYPE 0x22
+#define LOCAL_GLX_X_VISUAL_TYPE_EXT 0x22
+#define LOCAL_GLX_Y_INVERTED_EXT 0x20D4
+
+// WGL
+#define LOCAL_WGL_ACCELERATION_ARB 0x2003
+#define LOCAL_WGL_ACCELERATION_EXT 0x2003
+#define LOCAL_WGL_ACCESS_READ_ONLY_NV 0x00000000
+#define LOCAL_WGL_ACCESS_READ_WRITE_NV 0x00000001
+#define LOCAL_WGL_ACCESS_WRITE_DISCARD_NV 0x00000002
+#define LOCAL_WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define LOCAL_WGL_ACCUM_ALPHA_BITS_EXT 0x2021
+#define LOCAL_WGL_ACCUM_BITS_ARB 0x201D
+#define LOCAL_WGL_ACCUM_BITS_EXT 0x201D
+#define LOCAL_WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define LOCAL_WGL_ACCUM_BLUE_BITS_EXT 0x2020
+#define LOCAL_WGL_ACCUM_GREEN_BITS_ARB 0x201F
+#define LOCAL_WGL_ACCUM_GREEN_BITS_EXT 0x201F
+#define LOCAL_WGL_ACCUM_RED_BITS_ARB 0x201E
+#define LOCAL_WGL_ACCUM_RED_BITS_EXT 0x201E
+#define LOCAL_WGL_ALPHA_BITS_ARB 0x201B
+#define LOCAL_WGL_ALPHA_BITS_EXT 0x201B
+#define LOCAL_WGL_ALPHA_SHIFT_ARB 0x201C
+#define LOCAL_WGL_ALPHA_SHIFT_EXT 0x201C
+#define LOCAL_WGL_AUX0_ARB 0x2087
+#define LOCAL_WGL_AUX1_ARB 0x2088
+#define LOCAL_WGL_AUX2_ARB 0x2089
+#define LOCAL_WGL_AUX3_ARB 0x208A
+#define LOCAL_WGL_AUX4_ARB 0x208B
+#define LOCAL_WGL_AUX5_ARB 0x208C
+#define LOCAL_WGL_AUX6_ARB 0x208D
+#define LOCAL_WGL_AUX7_ARB 0x208E
+#define LOCAL_WGL_AUX8_ARB 0x208F
+#define LOCAL_WGL_AUX9_ARB 0x2090
+#define LOCAL_WGL_AUX_BUFFERS_ARB 0x2024
+#define LOCAL_WGL_AUX_BUFFERS_EXT 0x2024
+#define LOCAL_WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002
+#define LOCAL_WGL_BACK_LEFT_ARB 0x2085
+#define LOCAL_WGL_BACK_RIGHT_ARB 0x2086
+#define LOCAL_WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1
+#define LOCAL_WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0
+#define LOCAL_WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071
+#define LOCAL_WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070
+#define LOCAL_WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1
+#define LOCAL_WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2
+#define LOCAL_WGL_BIND_TO_VIDEO_RGB_NV 0x20C0
+#define LOCAL_WGL_BLUE_BITS_ARB 0x2019
+#define LOCAL_WGL_BLUE_BITS_EXT 0x2019
+#define LOCAL_WGL_BLUE_SHIFT_ARB 0x201A
+#define LOCAL_WGL_BLUE_SHIFT_EXT 0x201A
+#define LOCAL_WGL_COLOR_BITS_ARB 0x2014
+#define LOCAL_WGL_COLOR_BITS_EXT 0x2014
+#define LOCAL_WGL_COLOR_SAMPLES_NV 0x20B9
+#define LOCAL_WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define LOCAL_WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define LOCAL_WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define LOCAL_WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
+#define LOCAL_WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define LOCAL_WGL_CONTEXT_FLAGS_ARB 0x2094
+#define LOCAL_WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define LOCAL_WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
+#define LOCAL_WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define LOCAL_WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define LOCAL_WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define LOCAL_WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008
+#define LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
+#define LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
+#define LOCAL_WGL_COVERAGE_SAMPLES_NV 0x2042
+#define LOCAL_WGL_CUBE_MAP_FACE_ARB 0x207C
+#define LOCAL_WGL_DEPTH_BITS_ARB 0x2022
+#define LOCAL_WGL_DEPTH_BITS_EXT 0x2022
+#define LOCAL_WGL_DEPTH_BUFFER_BIT_ARB 0x00000004
+#define LOCAL_WGL_DEPTH_COMPONENT_NV 0x20A7
+#define LOCAL_WGL_DEPTH_FLOAT_EXT 0x2040
+#define LOCAL_WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5
+#define LOCAL_WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050
+#define LOCAL_WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051
+#define LOCAL_WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052
+#define LOCAL_WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053
+#define LOCAL_WGL_DOUBLE_BUFFER_ARB 0x2011
+#define LOCAL_WGL_DOUBLE_BUFFER_EXT 0x2011
+#define LOCAL_WGL_DRAW_TO_BITMAP_ARB 0x2002
+#define LOCAL_WGL_DRAW_TO_BITMAP_EXT 0x2002
+#define LOCAL_WGL_DRAW_TO_PBUFFER_ARB 0x202D
+#define LOCAL_WGL_DRAW_TO_PBUFFER_EXT 0x202D
+#define LOCAL_WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define LOCAL_WGL_DRAW_TO_WINDOW_EXT 0x2001
+#define LOCAL_WGL_FLOAT_COMPONENTS_NV 0x20B0
+#define LOCAL_WGL_FONT_LINES 0
+#define LOCAL_WGL_FONT_POLYGONS 1
+#define LOCAL_WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
+#define LOCAL_WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9
+#define LOCAL_WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001
+#define LOCAL_WGL_FRONT_LEFT_ARB 0x2083
+#define LOCAL_WGL_FRONT_RIGHT_ARB 0x2084
+#define LOCAL_WGL_FULL_ACCELERATION_ARB 0x2027
+#define LOCAL_WGL_FULL_ACCELERATION_EXT 0x2027
+#define LOCAL_WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F
+#define LOCAL_WGL_GAMMA_TABLE_SIZE_I3D 0x204E
+#define LOCAL_WGL_GENERIC_ACCELERATION_ARB 0x2026
+#define LOCAL_WGL_GENERIC_ACCELERATION_EXT 0x2026
+#define LOCAL_WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049
+#define LOCAL_WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048
+#define LOCAL_WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C
+#define LOCAL_WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A
+#define LOCAL_WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B
+#define LOCAL_WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046
+#define LOCAL_WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045
+#define LOCAL_WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047
+#define LOCAL_WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044
+#define LOCAL_WGL_GPU_CLOCK_AMD 0x21A4
+#define LOCAL_WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2
+#define LOCAL_WGL_GPU_NUM_PIPES_AMD 0x21A5
+#define LOCAL_WGL_GPU_NUM_RB_AMD 0x21A7
+#define LOCAL_WGL_GPU_NUM_SIMD_AMD 0x21A6
+#define LOCAL_WGL_GPU_NUM_SPI_AMD 0x21A8
+#define LOCAL_WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02
+#define LOCAL_WGL_GPU_RAM_AMD 0x21A3
+#define LOCAL_WGL_GPU_RENDERER_STRING_AMD 0x1F01
+#define LOCAL_WGL_GPU_VENDOR_AMD 0x1F00
+#define LOCAL_WGL_GREEN_BITS_ARB 0x2017
+#define LOCAL_WGL_GREEN_BITS_EXT 0x2017
+#define LOCAL_WGL_GREEN_SHIFT_ARB 0x2018
+#define LOCAL_WGL_GREEN_SHIFT_EXT 0x2018
+#define LOCAL_WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002
+#define LOCAL_WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001
+#define LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
+#define LOCAL_WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030
+#define LOCAL_WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030
+#define LOCAL_WGL_MAX_PBUFFER_PIXELS_ARB 0x202E
+#define LOCAL_WGL_MAX_PBUFFER_PIXELS_EXT 0x202E
+#define LOCAL_WGL_MAX_PBUFFER_WIDTH_ARB 0x202F
+#define LOCAL_WGL_MAX_PBUFFER_WIDTH_EXT 0x202F
+#define LOCAL_WGL_MIPMAP_LEVEL_ARB 0x207B
+#define LOCAL_WGL_MIPMAP_TEXTURE_ARB 0x2074
+#define LOCAL_WGL_NEED_PALETTE_ARB 0x2004
+#define LOCAL_WGL_NEED_PALETTE_EXT 0x2004
+#define LOCAL_WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
+#define LOCAL_WGL_NEED_SYSTEM_PALETTE_EXT 0x2005
+#define LOCAL_WGL_NO_ACCELERATION_ARB 0x2025
+#define LOCAL_WGL_NO_ACCELERATION_EXT 0x2025
+#define LOCAL_WGL_NO_RESET_NOTIFICATION_ARB 0x8261
+#define LOCAL_WGL_NO_TEXTURE_ARB 0x2077
+#define LOCAL_WGL_NUMBER_OVERLAYS_ARB 0x2008
+#define LOCAL_WGL_NUMBER_OVERLAYS_EXT 0x2008
+#define LOCAL_WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define LOCAL_WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000
+#define LOCAL_WGL_NUMBER_UNDERLAYS_ARB 0x2009
+#define LOCAL_WGL_NUMBER_UNDERLAYS_EXT 0x2009
+#define LOCAL_WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF
+#define LOCAL_WGL_NUM_VIDEO_SLOTS_NV 0x20F0
+#define LOCAL_WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032
+#define LOCAL_WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031
+#define LOCAL_WGL_PBUFFER_HEIGHT_ARB 0x2035
+#define LOCAL_WGL_PBUFFER_HEIGHT_EXT 0x2035
+#define LOCAL_WGL_PBUFFER_LARGEST_ARB 0x2033
+#define LOCAL_WGL_PBUFFER_LARGEST_EXT 0x2033
+#define LOCAL_WGL_PBUFFER_LOST_ARB 0x2036
+#define LOCAL_WGL_PBUFFER_WIDTH_ARB 0x2034
+#define LOCAL_WGL_PBUFFER_WIDTH_EXT 0x2034
+#define LOCAL_WGL_PIXEL_TYPE_ARB 0x2013
+#define LOCAL_WGL_PIXEL_TYPE_EXT 0x2013
+#define LOCAL_WGL_RED_BITS_ARB 0x2015
+#define LOCAL_WGL_RED_BITS_EXT 0x2015
+#define LOCAL_WGL_RED_SHIFT_ARB 0x2016
+#define LOCAL_WGL_RED_SHIFT_EXT 0x2016
+#define LOCAL_WGL_SAMPLES_3DFX 0x2061
+#define LOCAL_WGL_SAMPLES_ARB 0x2042
+#define LOCAL_WGL_SAMPLES_EXT 0x2042
+#define LOCAL_WGL_SAMPLE_BUFFERS_3DFX 0x2060
+#define LOCAL_WGL_SAMPLE_BUFFERS_ARB 0x2041
+#define LOCAL_WGL_SAMPLE_BUFFERS_EXT 0x2041
+#define LOCAL_WGL_SHARE_ACCUM_ARB 0x200E
+#define LOCAL_WGL_SHARE_ACCUM_EXT 0x200E
+#define LOCAL_WGL_SHARE_DEPTH_ARB 0x200C
+#define LOCAL_WGL_SHARE_DEPTH_EXT 0x200C
+#define LOCAL_WGL_SHARE_STENCIL_ARB 0x200D
+#define LOCAL_WGL_SHARE_STENCIL_EXT 0x200D
+#define LOCAL_WGL_STENCIL_BITS_ARB 0x2023
+#define LOCAL_WGL_STENCIL_BITS_EXT 0x2023
+#define LOCAL_WGL_STENCIL_BUFFER_BIT_ARB 0x00000008
+#define LOCAL_WGL_STEREO_ARB 0x2012
+#define LOCAL_WGL_STEREO_EMITTER_DISABLE_3DL 0x2056
+#define LOCAL_WGL_STEREO_EMITTER_ENABLE_3DL 0x2055
+#define LOCAL_WGL_STEREO_EXT 0x2012
+#define LOCAL_WGL_STEREO_POLARITY_INVERT_3DL 0x2058
+#define LOCAL_WGL_STEREO_POLARITY_NORMAL_3DL 0x2057
+#define LOCAL_WGL_SUPPORT_GDI_ARB 0x200F
+#define LOCAL_WGL_SUPPORT_GDI_EXT 0x200F
+#define LOCAL_WGL_SUPPORT_OPENGL_ARB 0x2010
+#define LOCAL_WGL_SUPPORT_OPENGL_EXT 0x2010
+#define LOCAL_WGL_SWAP_COPY_ARB 0x2029
+#define LOCAL_WGL_SWAP_COPY_EXT 0x2029
+#define LOCAL_WGL_SWAP_EXCHANGE_ARB 0x2028
+#define LOCAL_WGL_SWAP_EXCHANGE_EXT 0x2028
+#define LOCAL_WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
+#define LOCAL_WGL_SWAP_LAYER_BUFFERS_EXT 0x2006
+#define LOCAL_WGL_SWAP_MAIN_PLANE 0x00000001
+#define LOCAL_WGL_SWAP_METHOD_ARB 0x2007
+#define LOCAL_WGL_SWAP_METHOD_EXT 0x2007
+#define LOCAL_WGL_SWAP_OVERLAY1 0x00000002
+#define LOCAL_WGL_SWAP_OVERLAY10 0x00000400
+#define LOCAL_WGL_SWAP_OVERLAY11 0x00000800
+#define LOCAL_WGL_SWAP_OVERLAY12 0x00001000
+#define LOCAL_WGL_SWAP_OVERLAY13 0x00002000
+#define LOCAL_WGL_SWAP_OVERLAY14 0x00004000
+#define LOCAL_WGL_SWAP_OVERLAY15 0x00008000
+#define LOCAL_WGL_SWAP_OVERLAY2 0x00000004
+#define LOCAL_WGL_SWAP_OVERLAY3 0x00000008
+#define LOCAL_WGL_SWAP_OVERLAY4 0x00000010
+#define LOCAL_WGL_SWAP_OVERLAY5 0x00000020
+#define LOCAL_WGL_SWAP_OVERLAY6 0x00000040
+#define LOCAL_WGL_SWAP_OVERLAY7 0x00000080
+#define LOCAL_WGL_SWAP_OVERLAY8 0x00000100
+#define LOCAL_WGL_SWAP_OVERLAY9 0x00000200
+#define LOCAL_WGL_SWAP_UNDEFINED_ARB 0x202A
+#define LOCAL_WGL_SWAP_UNDEFINED_EXT 0x202A
+#define LOCAL_WGL_SWAP_UNDERLAY1 0x00010000
+#define LOCAL_WGL_SWAP_UNDERLAY10 0x02000000
+#define LOCAL_WGL_SWAP_UNDERLAY11 0x04000000
+#define LOCAL_WGL_SWAP_UNDERLAY12 0x08000000
+#define LOCAL_WGL_SWAP_UNDERLAY13 0x10000000
+#define LOCAL_WGL_SWAP_UNDERLAY14 0x20000000
+#define LOCAL_WGL_SWAP_UNDERLAY15 0x40000000
+#define LOCAL_WGL_SWAP_UNDERLAY2 0x00020000
+#define LOCAL_WGL_SWAP_UNDERLAY3 0x00040000
+#define LOCAL_WGL_SWAP_UNDERLAY4 0x00080000
+#define LOCAL_WGL_SWAP_UNDERLAY5 0x00100000
+#define LOCAL_WGL_SWAP_UNDERLAY6 0x00200000
+#define LOCAL_WGL_SWAP_UNDERLAY7 0x00400000
+#define LOCAL_WGL_SWAP_UNDERLAY8 0x00800000
+#define LOCAL_WGL_SWAP_UNDERLAY9 0x01000000
+#define LOCAL_WGL_TEXTURE_1D_ARB 0x2079
+#define LOCAL_WGL_TEXTURE_2D_ARB 0x207A
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_ARB 0x2078
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F
+#define LOCAL_WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081
+#define LOCAL_WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6
+#define LOCAL_WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8
+#define LOCAL_WGL_TEXTURE_FLOAT_RGB_NV 0x20B7
+#define LOCAL_WGL_TEXTURE_FLOAT_RG_NV 0x20B6
+#define LOCAL_WGL_TEXTURE_FLOAT_R_NV 0x20B5
+#define LOCAL_WGL_TEXTURE_FORMAT_ARB 0x2072
+#define LOCAL_WGL_TEXTURE_RECTANGLE_NV 0x20A2
+#define LOCAL_WGL_TEXTURE_RGBA_ARB 0x2076
+#define LOCAL_WGL_TEXTURE_RGB_ARB 0x2075
+#define LOCAL_WGL_TEXTURE_TARGET_ARB 0x2073
+#define LOCAL_WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
+#define LOCAL_WGL_TRANSPARENT_ARB 0x200A
+#define LOCAL_WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
+#define LOCAL_WGL_TRANSPARENT_EXT 0x200A
+#define LOCAL_WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
+#define LOCAL_WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
+#define LOCAL_WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
+#define LOCAL_WGL_TRANSPARENT_VALUE_EXT 0x200B
+#define LOCAL_WGL_TYPE_COLORINDEX_ARB 0x202C
+#define LOCAL_WGL_TYPE_COLORINDEX_EXT 0x202C
+#define LOCAL_WGL_TYPE_RGBA_ARB 0x202B
+#define LOCAL_WGL_TYPE_RGBA_EXT 0x202B
+#define LOCAL_WGL_TYPE_RGBA_FLOAT_ARB 0x21A0
+#define LOCAL_WGL_TYPE_RGBA_FLOAT_ATI 0x21A0
+#define LOCAL_WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8
+#define LOCAL_WGL_UNIQUE_ID_NV 0x20CE
+#define LOCAL_WGL_VIDEO_OUT_ALPHA_NV 0x20C4
+#define LOCAL_WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6
+#define LOCAL_WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7
+#define LOCAL_WGL_VIDEO_OUT_COLOR_NV 0x20C3
+#define LOCAL_WGL_VIDEO_OUT_DEPTH_NV 0x20C5
+#define LOCAL_WGL_VIDEO_OUT_FIELD_1 0x20C9
+#define LOCAL_WGL_VIDEO_OUT_FIELD_2 0x20CA
+#define LOCAL_WGL_VIDEO_OUT_FRAME 0x20C8
+#define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB
+#define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC
+
+#endif // GLCONSTS_H_
diff --git a/system/graphics/gl/GLContext.cpp b/system/graphics/gl/GLContext.cpp
new file mode 100644
index 000000000..c84616f19
--- /dev/null
+++ b/system/graphics/gl/GLContext.cpp
@@ -0,0 +1,2982 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContext.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <vector>
+
+#include "GLBlitHelper.h"
+#include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
+
+#include "gfxEnv.h"
+#include "gfxUtils.h"
+#include "GLContextProvider.h"
+#include "GLTextureImage.h"
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+#include "prenv.h"
+#include "prlink.h"
+#include "ScopedGLHelpers.h"
+#include "SharedSurfaceGL.h"
+#include "GfxTexturesReporter.h"
+#include "TextureGarbageBin.h"
+#include "gfx2DGlue.h"
+#include "gfxPrefs.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/gfx/Logging.h"
+
+#include "OGLShaderProgram.h" // for ShaderProgramType
+
+#include "mozilla/DebugOnly.h"
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+#ifdef MOZ_GL_DEBUG
+unsigned GLContext::sCurrentGLContextTLS = -1;
+#endif
+
+// If adding defines, don't forget to undefine symbols. See #undef block below.
+#define CORE_SYMBOL(x) { (PRFuncPtr*) &mSymbols.f##x, { #x, nullptr } }
+#define CORE_EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, { #x, #x #y, #x #z, nullptr } }
+#define EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, { #x #y, #x #z, nullptr } }
+#define EXT_SYMBOL3(x,y,z,w) { (PRFuncPtr*) &mSymbols.f##x, { #x #y, #x #z, #x #w, nullptr } }
+#define END_SYMBOLS { nullptr, { nullptr } }
+
+// should match the order of GLExtensions, and be null-terminated.
+static const char* const sExtensionNames[] = {
+ "NO_EXTENSION",
+ "GL_AMD_compressed_ATC_texture",
+ "GL_ANGLE_depth_texture",
+ "GL_ANGLE_framebuffer_blit",
+ "GL_ANGLE_framebuffer_multisample",
+ "GL_ANGLE_instanced_arrays",
+ "GL_ANGLE_texture_compression_dxt3",
+ "GL_ANGLE_texture_compression_dxt5",
+ "GL_ANGLE_timer_query",
+ "GL_APPLE_client_storage",
+ "GL_APPLE_framebuffer_multisample",
+ "GL_APPLE_sync",
+ "GL_APPLE_texture_range",
+ "GL_APPLE_vertex_array_object",
+ "GL_ARB_ES2_compatibility",
+ "GL_ARB_ES3_compatibility",
+ "GL_ARB_color_buffer_float",
+ "GL_ARB_copy_buffer",
+ "GL_ARB_depth_texture",
+ "GL_ARB_draw_buffers",
+ "GL_ARB_draw_instanced",
+ "GL_ARB_framebuffer_object",
+ "GL_ARB_framebuffer_sRGB",
+ "GL_ARB_geometry_shader4",
+ "GL_ARB_gpu_shader5",
+ "GL_ARB_half_float_pixel",
+ "GL_ARB_instanced_arrays",
+ "GL_ARB_internalformat_query",
+ "GL_ARB_invalidate_subdata",
+ "GL_ARB_map_buffer_range",
+ "GL_ARB_occlusion_query2",
+ "GL_ARB_pixel_buffer_object",
+ "GL_ARB_robustness",
+ "GL_ARB_sampler_objects",
+ "GL_ARB_seamless_cube_map",
+ "GL_ARB_shader_texture_lod",
+ "GL_ARB_sync",
+ "GL_ARB_texture_compression",
+ "GL_ARB_texture_float",
+ "GL_ARB_texture_non_power_of_two",
+ "GL_ARB_texture_rectangle",
+ "GL_ARB_texture_rg",
+ "GL_ARB_texture_storage",
+ "GL_ARB_texture_swizzle",
+ "GL_ARB_timer_query",
+ "GL_ARB_transform_feedback2",
+ "GL_ARB_uniform_buffer_object",
+ "GL_ARB_vertex_array_object",
+ "GL_EXT_bgra",
+ "GL_EXT_blend_minmax",
+ "GL_EXT_color_buffer_float",
+ "GL_EXT_color_buffer_half_float",
+ "GL_EXT_copy_texture",
+ "GL_EXT_disjoint_timer_query",
+ "GL_EXT_draw_buffers",
+ "GL_EXT_draw_buffers2",
+ "GL_EXT_draw_instanced",
+ "GL_EXT_draw_range_elements",
+ "GL_EXT_frag_depth",
+ "GL_EXT_framebuffer_blit",
+ "GL_EXT_framebuffer_multisample",
+ "GL_EXT_framebuffer_object",
+ "GL_EXT_framebuffer_sRGB",
+ "GL_EXT_gpu_shader4",
+ "GL_EXT_gpu_shader5",
+ "GL_EXT_multisampled_render_to_texture",
+ "GL_EXT_occlusion_query_boolean",
+ "GL_EXT_packed_depth_stencil",
+ "GL_EXT_read_format_bgra",
+ "GL_EXT_robustness",
+ "GL_EXT_sRGB",
+ "GL_EXT_sRGB_write_control",
+ "GL_EXT_shader_texture_lod",
+ "GL_EXT_texture3D",
+ "GL_EXT_texture_compression_dxt1",
+ "GL_EXT_texture_compression_s3tc",
+ "GL_EXT_texture_filter_anisotropic",
+ "GL_EXT_texture_format_BGRA8888",
+ "GL_EXT_texture_sRGB",
+ "GL_EXT_texture_storage",
+ "GL_EXT_timer_query",
+ "GL_EXT_transform_feedback",
+ "GL_EXT_unpack_subimage",
+ "GL_IMG_read_format",
+ "GL_IMG_texture_compression_pvrtc",
+ "GL_IMG_texture_npot",
+ "GL_KHR_debug",
+ "GL_NV_draw_instanced",
+ "GL_NV_fence",
+ "GL_NV_framebuffer_blit",
+ "GL_NV_geometry_program4",
+ "GL_NV_gpu_shader5",
+ "GL_NV_half_float",
+ "GL_NV_instanced_arrays",
+ "GL_NV_primitive_restart",
+ "GL_NV_texture_barrier",
+ "GL_NV_transform_feedback",
+ "GL_NV_transform_feedback2",
+ "GL_OES_EGL_image",
+ "GL_OES_EGL_image_external",
+ "GL_OES_EGL_sync",
+ "GL_OES_compressed_ETC1_RGB8_texture",
+ "GL_OES_depth24",
+ "GL_OES_depth32",
+ "GL_OES_depth_texture",
+ "GL_OES_element_index_uint",
+ "GL_OES_framebuffer_object",
+ "GL_OES_packed_depth_stencil",
+ "GL_OES_rgb8_rgba8",
+ "GL_OES_standard_derivatives",
+ "GL_OES_stencil8",
+ "GL_OES_texture_3D",
+ "GL_OES_texture_float",
+ "GL_OES_texture_float_linear",
+ "GL_OES_texture_half_float",
+ "GL_OES_texture_half_float_linear",
+ "GL_OES_texture_npot",
+ "GL_OES_vertex_array_object"
+};
+
+static bool
+ParseGLSLVersion(GLContext* gl, uint32_t* out_version)
+{
+ if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+ MOZ_ASSERT(false, "An OpenGL error has been triggered before.");
+ return false;
+ }
+
+ /**
+ * OpenGL 2.x, 3.x, 4.x specifications:
+ * The VERSION and SHADING_LANGUAGE_VERSION strings are laid out as follows:
+ *
+ * <version number><space><vendor-specific information>
+ *
+ * The version number is either of the form major_number.minor_number or
+ * major_number.minor_number.release_number, where the numbers all have
+ * one or more digits.
+ *
+ * SHADING_LANGUAGE_VERSION is *almost* identical to VERSION. The
+ * difference is that the minor version always has two digits and the
+ * prefix has an additional 'GLSL ES'
+ *
+ *
+ * OpenGL ES 2.0, 3.0 specifications:
+ * The VERSION string is laid out as follows:
+ *
+ * "OpenGL ES N.M vendor-specific information"
+ *
+ * The version number is either of the form major_number.minor_number or
+ * major_number.minor_number.release_number, where the numbers all have
+ * one or more digits.
+ *
+ *
+ * Note:
+ * We don't care about release_number.
+ */
+ const char* versionString = (const char*) gl->fGetString(LOCAL_GL_SHADING_LANGUAGE_VERSION);
+
+ if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+ MOZ_ASSERT(false, "glGetString(GL_SHADING_LANGUAGE_VERSION) has generated an error");
+ return false;
+ }
+
+ if (!versionString) {
+ MOZ_ASSERT(false, "LOCAL_GL_SHADING_LANGUAGE_VERSION undefined, returning 100");
+ *out_version = 100;
+ return true;
+ }
+
+ const auto fnSkipPrefix = [&versionString](const char* prefix) {
+ const auto len = strlen(prefix);
+ if (strncmp(versionString, prefix, len) == 0)
+ versionString += len;
+ };
+
+ const char kGLESVersionPrefix[] = "OpenGL ES GLSL ES";
+ fnSkipPrefix(kGLESVersionPrefix);
+
+ if (gl->WorkAroundDriverBugs()) {
+ // Nexus 7 2013 (bug 1234441)
+ const char kBadGLESVersionPrefix[] = "OpenGL ES GLSL";
+ fnSkipPrefix(kBadGLESVersionPrefix);
+ }
+
+ const char* itr = versionString;
+ char* end = nullptr;
+ auto majorVersion = strtol(itr, &end, 10);
+
+ if (!end) {
+ MOZ_ASSERT(false, "Failed to parse the GL major version number.");
+ return false;
+ }
+
+ if (*end != '.') {
+ MOZ_ASSERT(false, "Failed to parse GL's major-minor version number separator.");
+ return false;
+ }
+
+ // we skip the '.' between the major and the minor version
+ itr = end + 1;
+ end = nullptr;
+
+ auto minorVersion = strtol(itr, &end, 10);
+ if (!end) {
+ MOZ_ASSERT(false, "Failed to parse GL's minor version number.");
+ return false;
+ }
+
+ if (majorVersion <= 0 || majorVersion >= 100) {
+ MOZ_ASSERT(false, "Invalid major version.");
+ return false;
+ }
+
+ if (minorVersion < 0 || minorVersion >= 100) {
+ MOZ_ASSERT(false, "Invalid minor version.");
+ return false;
+ }
+
+ *out_version = (uint32_t) majorVersion * 100 + (uint32_t) minorVersion;
+ return true;
+}
+
+static bool
+ParseGLVersion(GLContext* gl, uint32_t* out_version)
+{
+ if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+ MOZ_ASSERT(false, "An OpenGL error has been triggered before.");
+ return false;
+ }
+
+ /**
+ * B2G emulator bug work around: The emulator implements OpenGL ES 2.0 on
+ * OpenGL 3.2. The bug is that GetIntegerv(LOCAL_GL_{MAJOR,MINOR}_VERSION)
+ * returns OpenGL 3.2 instead of generating an error.
+ */
+ if (!gl->IsGLES()) {
+ /**
+ * OpenGL 3.1 and OpenGL ES 3.0 both introduce GL_{MAJOR,MINOR}_VERSION
+ * with GetIntegerv. So we first try those constants even though we
+ * might not have an OpenGL context supporting them, as this is a
+ * better way than parsing GL_VERSION.
+ */
+ GLint majorVersion = 0;
+ GLint minorVersion = 0;
+
+ const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAJOR_VERSION,
+ &majorVersion) &&
+ gl->GetPotentialInteger(LOCAL_GL_MINOR_VERSION,
+ &minorVersion));
+
+ // If it's not an OpenGL (ES) 3.0 context, we will have an error
+ if (ok &&
+ majorVersion > 0 &&
+ minorVersion >= 0)
+ {
+ *out_version = majorVersion * 100 + minorVersion * 10;
+ return true;
+ }
+ }
+
+ /**
+ * We were not able to use GL_{MAJOR,MINOR}_VERSION, so we parse
+ * GL_VERSION.
+ *
+ *
+ * OpenGL 2.x, 3.x, 4.x specifications:
+ * The VERSION and SHADING_LANGUAGE_VERSION strings are laid out as follows:
+ *
+ * <version number><space><vendor-specific information>
+ *
+ * The version number is either of the form major_number.minor_number or
+ * major_number.minor_number.release_number, where the numbers all have
+ * one or more digits.
+ *
+ *
+ * OpenGL ES 2.0, 3.0 specifications:
+ * The VERSION string is laid out as follows:
+ *
+ * "OpenGL ES N.M vendor-specific information"
+ *
+ * The version number is either of the form major_number.minor_number or
+ * major_number.minor_number.release_number, where the numbers all have
+ * one or more digits.
+ *
+ *
+ * Note:
+ * We don't care about release_number.
+ */
+ const char* versionString = (const char*)gl->fGetString(LOCAL_GL_VERSION);
+
+ if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
+ MOZ_ASSERT(false, "glGetString(GL_VERSION) has generated an error");
+ return false;
+ } else if (!versionString) {
+ MOZ_ASSERT(false, "glGetString(GL_VERSION) has returned 0");
+ return false;
+ }
+
+ const char kGLESVersionPrefix[] = "OpenGL ES ";
+ if (strncmp(versionString, kGLESVersionPrefix, strlen(kGLESVersionPrefix)) == 0) {
+ versionString += strlen(kGLESVersionPrefix);
+ }
+
+ const char* itr = versionString;
+ char* end = nullptr;
+ auto majorVersion = strtol(itr, &end, 10);
+
+ if (!end) {
+ MOZ_ASSERT(false, "Failed to parse the GL major version number.");
+ return false;
+ } else if (*end != '.') {
+ MOZ_ASSERT(false, "Failed to parse GL's major-minor version number separator.");
+ return false;
+ }
+
+ // we skip the '.' between the major and the minor version
+ itr = end + 1;
+
+ end = nullptr;
+
+ auto minorVersion = strtol(itr, &end, 10);
+ if (!end) {
+ MOZ_ASSERT(false, "Failed to parse GL's minor version number.");
+ return false;
+ }
+
+ if (majorVersion <= 0 || majorVersion >= 100) {
+ MOZ_ASSERT(false, "Invalid major version.");
+ return false;
+ } else if (minorVersion < 0 || minorVersion >= 10) {
+ MOZ_ASSERT(false, "Invalid minor version.");
+ return false;
+ }
+
+ *out_version = (uint32_t)majorVersion * 100 + (uint32_t)minorVersion * 10;
+ return true;
+}
+
+static uint8_t
+ChooseDebugFlags(CreateContextFlags createFlags)
+{
+ uint8_t debugFlags = 0;
+
+#ifdef MOZ_GL_DEBUG
+ if (gfxEnv::GlDebug()) {
+ debugFlags |= GLContext::DebugFlagEnabled;
+ }
+
+ // Enables extra verbose output, informing of the start and finish of every GL call.
+ // Useful e.g. to record information to investigate graphics system crashes/lockups
+ if (gfxEnv::GlDebugVerbose()) {
+ debugFlags |= GLContext::DebugFlagTrace;
+ }
+
+ // Aborts on GL error. Can be useful to debug quicker code that is known not to
+ // generate any GL error in principle.
+ bool abortOnError = false;
+
+ if (createFlags & CreateContextFlags::NO_VALIDATION) {
+ abortOnError = true;
+
+ const auto fnStringsMatch = [](const char* a, const char* b) {
+ return strcmp(a, b) == 0;
+ };
+
+ const char* envAbortOnError = PR_GetEnv("MOZ_GL_DEBUG_ABORT_ON_ERROR");
+ if (envAbortOnError && fnStringsMatch(envAbortOnError, "0")) {
+ abortOnError = false;
+ }
+ }
+
+ if (abortOnError) {
+ debugFlags |= GLContext::DebugFlagAbortOnError;
+ }
+#endif
+
+ return debugFlags;
+}
+
+GLContext::GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
+ GLContext* sharedContext, bool isOffscreen)
+ : mIsOffscreen(isOffscreen),
+ mContextLost(false),
+ mVersion(0),
+ mProfile(ContextProfile::Unknown),
+ mShadingLanguageVersion(0),
+ mVendor(GLVendor::Other),
+ mRenderer(GLRenderer::Other),
+ mTopError(LOCAL_GL_NO_ERROR),
+ mDebugFlags(ChooseDebugFlags(flags)),
+ mSharedContext(sharedContext),
+ mCaps(caps),
+ mScreen(nullptr),
+ mLockedSurface(nullptr),
+ mMaxTextureSize(0),
+ mMaxCubeMapTextureSize(0),
+ mMaxTextureImageSize(0),
+ mMaxRenderbufferSize(0),
+ mMaxSamples(0),
+ mNeedsTextureSizeChecks(false),
+ mNeedsFlushBeforeDeleteFB(false),
+ mTextureAllocCrashesOnMapFailure(false),
+ mNeedsCheckAfterAttachTextureToFb(false),
+ mWorkAroundDriverBugs(true),
+ mHeavyGLCallsSinceLastFlush(false)
+{
+ mMaxViewportDims[0] = 0;
+ mMaxViewportDims[1] = 0;
+ mOwningThreadId = PlatformThread::CurrentId();
+}
+
+GLContext::~GLContext() {
+ NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
+#ifdef MOZ_GL_DEBUG
+ if (mSharedContext) {
+ GLContext* tip = mSharedContext;
+ while (tip->mSharedContext)
+ tip = tip->mSharedContext;
+ tip->SharedContextDestroyed(this);
+ tip->ReportOutstandingNames();
+ } else {
+ ReportOutstandingNames();
+ }
+#endif
+}
+
+/*static*/ void
+GLContext::StaticDebugCallback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const GLvoid* userParam)
+{
+ GLContext* gl = (GLContext*)userParam;
+ gl->DebugCallback(source, type, id, severity, length, message);
+}
+
+static void
+ClearSymbols(const GLLibraryLoader::SymLoadStruct* symbols)
+{
+ while (symbols->symPointer) {
+ *symbols->symPointer = nullptr;
+ symbols++;
+ }
+}
+
+bool
+GLContext::InitWithPrefix(const char* prefix, bool trygl)
+{
+ MOZ_RELEASE_ASSERT(!mSymbols.fBindFramebuffer,
+ "GFX: InitWithPrefix should only be called once.");
+
+ if (!InitWithPrefixImpl(prefix, trygl)) {
+ // If initialization fails, zero the symbols to avoid hard-to-understand bugs.
+ mSymbols.Zero();
+ NS_WARNING("GLContext::InitWithPrefix failed!");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+LoadGLSymbols(GLContext* gl, const char* prefix, bool trygl,
+ const GLLibraryLoader::SymLoadStruct* list, const char* desc)
+{
+ if (gl->LoadSymbols(list, trygl, prefix))
+ return true;
+
+ ClearSymbols(list);
+
+ if (desc) {
+ const nsPrintfCString err("Failed to load symbols for %s.", desc);
+ NS_ERROR(err.BeginReading());
+ }
+ return false;
+}
+
+bool
+GLContext::LoadExtSymbols(const char* prefix, bool trygl, const SymLoadStruct* list,
+ GLExtensions ext)
+{
+ const char* extName = sExtensionNames[size_t(ext)];
+ if (!LoadGLSymbols(this, prefix, trygl, list, extName)) {
+ MarkExtensionUnsupported(ext);
+ return false;
+ }
+ return true;
+};
+
+bool
+GLContext::LoadFeatureSymbols(const char* prefix, bool trygl, const SymLoadStruct* list,
+ GLFeature feature)
+{
+ const char* featureName = GetFeatureName(feature);
+ if (!LoadGLSymbols(this, prefix, trygl, list, featureName)) {
+ MarkUnsupported(feature);
+ return false;
+ }
+ return true;
+};
+
+bool
+GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
+{
+ // wglGetProcAddress requires a current context.
+ if (!MakeCurrent(true))
+ return false;
+
+ mWorkAroundDriverBugs = gfxPrefs::WorkAroundDriverBugs();
+
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fActiveTexture, { "ActiveTexture", "ActiveTextureARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fAttachShader, { "AttachShader", "AttachShaderARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindAttribLocation, { "BindAttribLocation", "BindAttribLocationARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindBuffer, { "BindBuffer", "BindBufferARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindTexture, { "BindTexture", "BindTextureARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBlendColor, { "BlendColor", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBlendEquation, { "BlendEquation", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBlendEquationSeparate, { "BlendEquationSeparate", "BlendEquationSeparateEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBlendFunc, { "BlendFunc", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBlendFuncSeparate, { "BlendFuncSeparate", "BlendFuncSeparateEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBufferData, { "BufferData", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBufferSubData, { "BufferSubData", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClear, { "Clear", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearColor, { "ClearColor", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearStencil, { "ClearStencil", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fColorMask, { "ColorMask", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCompressedTexImage2D, {"CompressedTexImage2D", nullptr} },
+ { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage2D, {"CompressedTexSubImage2D", nullptr} },
+ { (PRFuncPtr*) &mSymbols.fCullFace, { "CullFace", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDetachShader, { "DetachShader", "DetachShaderARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDepthFunc, { "DepthFunc", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDepthMask, { "DepthMask", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDisable, { "Disable", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDisableVertexAttribArray, { "DisableVertexAttribArray", "DisableVertexAttribArrayARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDrawArrays, { "DrawArrays", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDrawElements, { "DrawElements", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEnable, { "Enable", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEnableVertexAttribArray, { "EnableVertexAttribArray", "EnableVertexAttribArrayARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fFinish, { "Finish", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fFlush, { "Flush", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fFrontFace, { "FrontFace", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetActiveAttrib, { "GetActiveAttrib", "GetActiveAttribARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetActiveUniform, { "GetActiveUniform", "GetActiveUniformARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetAttachedShaders, { "GetAttachedShaders", "GetAttachedShadersARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetAttribLocation, { "GetAttribLocation", "GetAttribLocationARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetIntegerv, { "GetIntegerv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetFloatv, { "GetFloatv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetBooleanv, { "GetBooleanv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetBufferParameteriv, { "GetBufferParameteriv", "GetBufferParameterivARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetError, { "GetError", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetProgramiv, { "GetProgramiv", "GetProgramivARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetProgramInfoLog, { "GetProgramInfoLog", "GetProgramInfoLogARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexParameteri, { "TexParameteri", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexParameteriv, { "TexParameteriv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexParameterf, { "TexParameterf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetString, { "GetString", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetTexParameterfv, { "GetTexParameterfv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetTexParameteriv, { "GetTexParameteriv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetUniformfv, { "GetUniformfv", "GetUniformfvARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetUniformiv, { "GetUniformiv", "GetUniformivARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetUniformLocation, { "GetUniformLocation", "GetUniformLocationARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetVertexAttribfv, { "GetVertexAttribfv", "GetVertexAttribfvARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetVertexAttribiv, { "GetVertexAttribiv", "GetVertexAttribivARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetVertexAttribPointerv, { "GetVertexAttribPointerv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fHint, { "Hint", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsBuffer, { "IsBuffer", "IsBufferARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsEnabled, { "IsEnabled", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsProgram, { "IsProgram", "IsProgramARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsShader, { "IsShader", "IsShaderARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsTexture, { "IsTexture", "IsTextureARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fLineWidth, { "LineWidth", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fLinkProgram, { "LinkProgram", "LinkProgramARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPixelStorei, { "PixelStorei", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPolygonOffset, { "PolygonOffset", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fReadPixels, { "ReadPixels", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSampleCoverage, { "SampleCoverage", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fScissor, { "Scissor", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilFunc, { "StencilFunc", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilFuncSeparate, { "StencilFuncSeparate", "StencilFuncSeparateEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilMask, { "StencilMask", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilMaskSeparate, { "StencilMaskSeparate", "StencilMaskSeparateEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilOp, { "StencilOp", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fStencilOpSeparate, { "StencilOpSeparate", "StencilOpSeparateEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexImage2D, { "TexImage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexSubImage2D, { "TexSubImage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1f, { "Uniform1f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1fv, { "Uniform1fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1i, { "Uniform1i", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1iv, { "Uniform1iv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2f, { "Uniform2f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2fv, { "Uniform2fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2i, { "Uniform2i", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2iv, { "Uniform2iv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3f, { "Uniform3f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3fv, { "Uniform3fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3i, { "Uniform3i", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3iv, { "Uniform3iv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4f, { "Uniform4f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4fv, { "Uniform4fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4i, { "Uniform4i", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4iv, { "Uniform4iv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix2fv, { "UniformMatrix2fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix3fv, { "UniformMatrix3fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix4fv, { "UniformMatrix4fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUseProgram, { "UseProgram", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fValidateProgram, { "ValidateProgram", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, { "VertexAttribPointer", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib1f, { "VertexAttrib1f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib2f, { "VertexAttrib2f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib3f, { "VertexAttrib3f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib4f, { "VertexAttrib4f", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib1fv, { "VertexAttrib1fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib2fv, { "VertexAttrib2fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib3fv, { "VertexAttrib3fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttrib4fv, { "VertexAttrib4fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fViewport, { "Viewport", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCompileShader, { "CompileShader", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCopyTexImage2D, { "CopyTexImage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCopyTexSubImage2D, { "CopyTexSubImage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetShaderiv, { "GetShaderiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetShaderInfoLog, { "GetShaderInfoLog", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetShaderSource, { "GetShaderSource", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fShaderSource, { "ShaderSource", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, { "VertexAttribPointer", nullptr } },
+
+ { (PRFuncPtr*) &mSymbols.fGenBuffers, { "GenBuffers", "GenBuffersARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenTextures, { "GenTextures", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCreateProgram, { "CreateProgram", "CreateProgramARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCreateShader, { "CreateShader", "CreateShaderARB", nullptr } },
+
+ { (PRFuncPtr*) &mSymbols.fDeleteBuffers, { "DeleteBuffers", "DeleteBuffersARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteTextures, { "DeleteTextures", "DeleteTexturesARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteProgram, { "DeleteProgram", "DeleteProgramARB", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteShader, { "DeleteShader", "DeleteShaderARB", nullptr } },
+
+ END_SYMBOLS
+ };
+
+ if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL"))
+ return false;
+
+ ////////////////
+
+ MOZ_ASSERT(mProfile != ContextProfile::Unknown);
+
+ uint32_t version = 0;
+ ParseGLVersion(this, &version);
+
+ mShadingLanguageVersion = 100;
+ ParseGLSLVersion(this, &mShadingLanguageVersion);
+
+ if (ShouldSpew()) {
+ printf_stderr("OpenGL version detected: %u\n", version);
+ printf_stderr("OpenGL shading language version detected: %u\n", mShadingLanguageVersion);
+ printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR));
+ printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER));
+ }
+
+ if (version >= mVersion) {
+ mVersion = version;
+ }
+ // Don't fail if version < mVersion, see bug 999445,
+ // Mac OSX 10.6/10.7 machines with Intel GPUs claim only OpenGL 1.4 but
+ // have all the GL2+ extensions that we need.
+
+ ////////////////
+
+ // Load OpenGL ES 2.0 symbols, or desktop if we aren't using ES 2.
+ if (IsGLES()) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetShaderPrecisionFormat, { "GetShaderPrecisionFormat", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearDepthf, { "ClearDepthf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDepthRangef, { "DepthRangef", nullptr } },
+ END_SYMBOLS
+ };
+
+ if (!LoadGLSymbols(this, prefix, trygl, symbols, "OpenGL ES"))
+ return false;
+ } else {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fClearDepth, { "ClearDepth", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDepthRange, { "DepthRange", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fReadBuffer, { "ReadBuffer", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fMapBuffer, { "MapBuffer", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUnmapBuffer, { "UnmapBuffer", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPointParameterf, { "PointParameterf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDrawBuffer, { "DrawBuffer", nullptr } },
+ // The following functions are only used by Skia/GL in desktop mode.
+ // Other parts of Gecko should avoid using these
+ { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffers", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClientActiveTexture, { "ClientActiveTexture", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDisableClientState, { "DisableClientState", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEnableClientState, { "EnableClientState", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fLoadIdentity, { "LoadIdentity", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fLoadMatrixf, { "LoadMatrixf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fMatrixMode, { "MatrixMode", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexGeni, { "TexGeni", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexGenf, { "TexGenf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexGenfv, { "TexGenfv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexPointer, { "VertexPointer", nullptr } },
+ END_SYMBOLS
+ };
+
+ if (!LoadGLSymbols(this, prefix, trygl, symbols, "Desktop OpenGL"))
+ return false;
+ }
+
+ ////////////////
+
+ const char* glVendorString = (const char*)fGetString(LOCAL_GL_VENDOR);
+ const char* glRendererString = (const char*)fGetString(LOCAL_GL_RENDERER);
+ if (!glVendorString || !glRendererString)
+ return false;
+
+ // The order of these strings must match up with the order of the enum
+ // defined in GLContext.h for vendor IDs.
+ const char* vendorMatchStrings[size_t(GLVendor::Other) + 1] = {
+ "Intel",
+ "NVIDIA",
+ "ATI",
+ "Qualcomm",
+ "Imagination",
+ "nouveau",
+ "Vivante",
+ "VMware, Inc.",
+ "ARM",
+ "Unknown"
+ };
+
+ mVendor = GLVendor::Other;
+ for (size_t i = 0; i < size_t(GLVendor::Other); ++i) {
+ if (DoesStringMatch(glVendorString, vendorMatchStrings[i])) {
+ mVendor = GLVendor(i);
+ break;
+ }
+ }
+
+ // The order of these strings must match up with the order of the enum
+ // defined in GLContext.h for renderer IDs.
+ const char* rendererMatchStrings[size_t(GLRenderer::Other) + 1] = {
+ "Adreno 200",
+ "Adreno 205",
+ "Adreno (TM) 200",
+ "Adreno (TM) 205",
+ "Adreno (TM) 305",
+ "Adreno (TM) 320",
+ "Adreno (TM) 330",
+ "Adreno (TM) 420",
+ "Mali-400 MP",
+ "Mali-450 MP",
+ "PowerVR SGX 530",
+ "PowerVR SGX 540",
+ "PowerVR SGX 544MP",
+ "NVIDIA Tegra",
+ "Android Emulator",
+ "Gallium 0.4 on llvmpipe",
+ "Intel HD Graphics 3000 OpenGL Engine",
+ "Microsoft Basic Render Driver",
+ "Unknown"
+ };
+
+ mRenderer = GLRenderer::Other;
+ for (size_t i = 0; i < size_t(GLRenderer::Other); ++i) {
+ if (DoesStringMatch(glRendererString, rendererMatchStrings[i])) {
+ mRenderer = GLRenderer(i);
+ break;
+ }
+ }
+
+ if (ShouldSpew()) {
+ printf_stderr("GL_VENDOR: %s\n", glVendorString);
+ printf_stderr("mVendor: %s\n", vendorMatchStrings[size_t(mVendor)]);
+ printf_stderr("GL_RENDERER: %s\n", glRendererString);
+ printf_stderr("mRenderer: %s\n", rendererMatchStrings[size_t(mRenderer)]);
+ }
+
+ ////////////////
+
+ // We need this for retrieving the list of extensions on Core profiles.
+ if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetStringi, { "GetStringi", nullptr } },
+ END_SYMBOLS
+ };
+
+ if (!LoadGLSymbols(this, prefix, trygl, symbols, "get_string_indexed")) {
+ MOZ_RELEASE_ASSERT(false, "GFX: get_string_indexed is required!");
+ return false;
+ }
+ }
+
+ InitExtensions();
+ InitFeatures();
+
+ // Disable extensions with partial or incorrect support.
+ if (WorkAroundDriverBugs()) {
+ if (Renderer() == GLRenderer::AdrenoTM320) {
+ MarkUnsupported(GLFeature::standard_derivatives);
+ }
+
+ if (Vendor() == GLVendor::Vivante) {
+ // bug 958256
+ MarkUnsupported(GLFeature::standard_derivatives);
+ }
+
+ if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) {
+ // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer)
+ // multisampling hardcodes blending with the default blendfunc, which breaks WebGL.
+ MarkUnsupported(GLFeature::framebuffer_multisample);
+ }
+ }
+
+ if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
+ MOZ_ASSERT((mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
+ "ARB_pixel_buffer_object supported without glMapBuffer/UnmapBuffer"
+ " being available!");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ const auto fnLoadForFeature = [this, prefix, trygl](const SymLoadStruct* list,
+ GLFeature feature)
+ {
+ return this->LoadFeatureSymbols(prefix, trygl, list, feature);
+ };
+
+ // Check for ARB_framebuffer_objects
+ if (IsSupported(GLFeature::framebuffer_object)) {
+ // https://www.opengl.org/registry/specs/ARB/framebuffer_object.txt
+ const SymLoadStruct symbols[] = {
+ CORE_SYMBOL(IsRenderbuffer),
+ CORE_SYMBOL(BindRenderbuffer),
+ CORE_SYMBOL(DeleteRenderbuffers),
+ CORE_SYMBOL(GenRenderbuffers),
+ CORE_SYMBOL(RenderbufferStorage),
+ CORE_SYMBOL(RenderbufferStorageMultisample),
+ CORE_SYMBOL(GetRenderbufferParameteriv),
+ CORE_SYMBOL(IsFramebuffer),
+ CORE_SYMBOL(BindFramebuffer),
+ CORE_SYMBOL(DeleteFramebuffers),
+ CORE_SYMBOL(GenFramebuffers),
+ CORE_SYMBOL(CheckFramebufferStatus),
+ CORE_SYMBOL(FramebufferTexture2D),
+ CORE_SYMBOL(FramebufferTextureLayer),
+ CORE_SYMBOL(FramebufferRenderbuffer),
+ CORE_SYMBOL(GetFramebufferAttachmentParameteriv),
+ CORE_SYMBOL(BlitFramebuffer),
+ CORE_SYMBOL(GenerateMipmap),
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::framebuffer_object);
+ }
+
+ if (!IsSupported(GLFeature::framebuffer_object)) {
+ // Check for aux symbols based on extensions
+ if (IsSupported(GLFeature::framebuffer_object_EXT_OES)) {
+ const SymLoadStruct symbols[] = {
+ CORE_EXT_SYMBOL2(IsRenderbuffer, EXT, OES),
+ CORE_EXT_SYMBOL2(BindRenderbuffer, EXT, OES),
+ CORE_EXT_SYMBOL2(DeleteRenderbuffers, EXT, OES),
+ CORE_EXT_SYMBOL2(GenRenderbuffers, EXT, OES),
+ CORE_EXT_SYMBOL2(RenderbufferStorage, EXT, OES),
+ CORE_EXT_SYMBOL2(GetRenderbufferParameteriv, EXT, OES),
+ CORE_EXT_SYMBOL2(IsFramebuffer, EXT, OES),
+ CORE_EXT_SYMBOL2(BindFramebuffer, EXT, OES),
+ CORE_EXT_SYMBOL2(DeleteFramebuffers, EXT, OES),
+ CORE_EXT_SYMBOL2(GenFramebuffers, EXT, OES),
+ CORE_EXT_SYMBOL2(CheckFramebufferStatus, EXT, OES),
+ CORE_EXT_SYMBOL2(FramebufferTexture2D, EXT, OES),
+ CORE_EXT_SYMBOL2(FramebufferRenderbuffer, EXT, OES),
+ CORE_EXT_SYMBOL2(GetFramebufferAttachmentParameteriv, EXT, OES),
+ CORE_EXT_SYMBOL2(GenerateMipmap, EXT, OES),
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::framebuffer_object_EXT_OES);
+ }
+
+ if (IsSupported(GLFeature::framebuffer_blit)) {
+ const SymLoadStruct symbols[] = {
+ EXT_SYMBOL3(BlitFramebuffer, ANGLE, EXT, NV),
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::framebuffer_blit);
+ }
+
+ if (IsSupported(GLFeature::framebuffer_multisample)) {
+ const SymLoadStruct symbols[] = {
+ EXT_SYMBOL3(RenderbufferStorageMultisample, ANGLE, APPLE, EXT),
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::framebuffer_multisample);
+ }
+
+ if (IsExtensionSupported(GLContext::ARB_geometry_shader4) ||
+ IsExtensionSupported(GLContext::NV_geometry_program4))
+ {
+ const SymLoadStruct symbols[] = {
+ EXT_SYMBOL2(FramebufferTextureLayer, ARB, EXT),
+ END_SYMBOLS
+ };
+ if (!LoadGLSymbols(this, prefix, trygl, symbols,
+ "ARB_geometry_shader4/NV_geometry_program4"))
+ {
+ MarkExtensionUnsupported(GLContext::ARB_geometry_shader4);
+ MarkExtensionUnsupported(GLContext::NV_geometry_program4);
+ }
+ }
+ }
+
+ if (!IsSupported(GLFeature::framebuffer_object) &&
+ !IsSupported(GLFeature::framebuffer_object_EXT_OES))
+ {
+ NS_ERROR("GLContext requires support for framebuffer objects.");
+ return false;
+ }
+ MOZ_RELEASE_ASSERT(mSymbols.fBindFramebuffer, "GFX: mSymbols.fBindFramebuffer zero or not set.");
+
+ ////////////////
+
+ LoadMoreSymbols(prefix, trygl);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
+ raw_fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mScissorRect);
+ raw_fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ raw_fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxCubeMapTextureSize);
+ raw_fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
+ raw_fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
+
+#ifdef MOZ_X11
+ if (mWorkAroundDriverBugs) {
+ if (mVendor == GLVendor::Nouveau) {
+ // see bug 814716. Clamp MaxCubeMapTextureSize at 2K for Nouveau.
+ mMaxCubeMapTextureSize = std::min(mMaxCubeMapTextureSize, 2048);
+ mNeedsTextureSizeChecks = true;
+ } else if (mVendor == GLVendor::Intel) {
+ // Bug 1199923. Driver seems to report a larger max size than
+ // actually supported.
+ mMaxTextureSize /= 2;
+ mMaxRenderbufferSize /= 2;
+ mNeedsTextureSizeChecks = true;
+ }
+ }
+#endif
+ if (mWorkAroundDriverBugs &&
+ Renderer() == GLRenderer::AdrenoTM420) {
+ // see bug 1194923. Calling glFlush before glDeleteFramebuffers
+ // prevents occasional driver crash.
+ mNeedsFlushBeforeDeleteFB = true;
+ }
+
+ mMaxTextureImageSize = mMaxTextureSize;
+
+ if (IsSupported(GLFeature::framebuffer_multisample)) {
+ fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // We're ready for final setup.
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ // TODO: Remove SurfaceCaps::any.
+ if (mCaps.any) {
+ mCaps.any = false;
+ mCaps.color = true;
+ mCaps.alpha = false;
+ }
+
+ mTexGarbageBin = new TextureGarbageBin(this);
+
+ MOZ_ASSERT(IsCurrent());
+
+ if (ShouldSpew() && IsExtensionSupported(KHR_debug)) {
+ fEnable(LOCAL_GL_DEBUG_OUTPUT);
+ fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ fDebugMessageCallback(&StaticDebugCallback, (void*)this);
+ fDebugMessageControl(LOCAL_GL_DONT_CARE,
+ LOCAL_GL_DONT_CARE,
+ LOCAL_GL_DONT_CARE,
+ 0, nullptr,
+ true);
+ }
+
+ mVersionString = nsPrintfCString("%u.%u.%u", mVersion / 100, (mVersion / 10) % 10,
+ mVersion % 10);
+ return true;
+}
+
+void
+GLContext::LoadMoreSymbols(const char* prefix, bool trygl)
+{
+ const auto fnLoadForExt = [this, prefix, trygl](const SymLoadStruct* list,
+ GLExtensions ext)
+ {
+ return this->LoadExtSymbols(prefix, trygl, list, ext);
+ };
+
+ const auto fnLoadForFeature = [this, prefix, trygl](const SymLoadStruct* list,
+ GLFeature feature)
+ {
+ return this->LoadFeatureSymbols(prefix, trygl, list, feature);
+ };
+
+ const auto fnLoadFeatureByCore = [this, fnLoadForFeature](const SymLoadStruct* coreList,
+ const SymLoadStruct* extList,
+ GLFeature feature)
+ {
+ const bool useCore = this->IsFeatureProvidedByCoreSymbols(feature);
+ const auto list = useCore ? coreList : extList;
+ return fnLoadForFeature(list, feature);
+ };
+
+ if (IsSupported(GLFeature::robustness)) {
+ bool hasRobustness = false;
+
+ if (!hasRobustness && IsExtensionSupported(ARB_robustness)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusARB", nullptr } },
+ END_SYMBOLS
+ };
+ if (fnLoadForExt(symbols, ARB_robustness)) {
+ hasRobustness = true;
+ }
+ }
+
+ if (!hasRobustness && IsExtensionSupported(EXT_robustness)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusEXT", nullptr } },
+ END_SYMBOLS
+ };
+ if (fnLoadForExt(symbols, EXT_robustness)) {
+ hasRobustness = true;
+ }
+ }
+
+ if (!hasRobustness) {
+ MarkUnsupported(GLFeature::robustness);
+ }
+ }
+
+ if (IsSupported(GLFeature::sync)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fFenceSync, { "FenceSync", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsSync, { "IsSync", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteSync, { "DeleteSync", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClientWaitSync, { "ClientWaitSync", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fWaitSync, { "WaitSync", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetInteger64v, { "GetInteger64v", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetSynciv, { "GetSynciv", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::sync);
+ }
+
+ if (IsExtensionSupported(OES_EGL_image)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fEGLImageTargetTexture2D, { "EGLImageTargetTexture2DOES", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEGLImageTargetRenderbufferStorage, { "EGLImageTargetRenderbufferStorageOES", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, OES_EGL_image);
+ }
+
+ if (IsExtensionSupported(APPLE_texture_range)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTextureRangeAPPLE, { "TextureRangeAPPLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, APPLE_texture_range);
+ }
+
+ if (IsSupported(GLFeature::vertex_array_object)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fIsVertexArray, { "IsVertexArray", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenVertexArrays, { "GenVertexArrays", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindVertexArray, { "BindVertexArray", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, { "DeleteVertexArrays", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fIsVertexArray, { "IsVertexArrayARB", "IsVertexArrayOES", "IsVertexArrayAPPLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenVertexArrays, { "GenVertexArraysARB", "GenVertexArraysOES", "GenVertexArraysAPPLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindVertexArray, { "BindVertexArrayARB", "BindVertexArrayOES", "BindVertexArrayAPPLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, { "DeleteVertexArraysARB", "DeleteVertexArraysOES", "DeleteVertexArraysAPPLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::vertex_array_object);
+ }
+
+ if (IsSupported(GLFeature::draw_instanced)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, { "DrawArraysInstanced", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, { "DrawElementsInstanced", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, { "DrawArraysInstancedARB", "DrawArraysInstancedEXT", "DrawArraysInstancedNV", "DrawArraysInstancedANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, { "DrawElementsInstancedARB", "DrawElementsInstancedEXT", "DrawElementsInstancedNV", "DrawElementsInstancedANGLE", nullptr }
+ },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_instanced);
+ }
+
+ if (IsSupported(GLFeature::instanced_arrays)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, { "VertexAttribDivisor", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, { "VertexAttribDivisorARB", "VertexAttribDivisorNV", "VertexAttribDivisorANGLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::instanced_arrays);
+ }
+
+ if (IsSupported(GLFeature::texture_storage)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3D", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2DEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3DEXT", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_storage);
+ }
+
+ if (IsSupported(GLFeature::sampler_objects)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGenSamplers, { "GenSamplers", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteSamplers, { "DeleteSamplers", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsSampler, { "IsSampler", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindSampler, { "BindSampler", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSamplerParameteri, { "SamplerParameteri", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSamplerParameteriv, { "SamplerParameteriv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSamplerParameterf, { "SamplerParameterf", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSamplerParameterfv, { "SamplerParameterfv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetSamplerParameteriv, { "GetSamplerParameteriv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetSamplerParameterfv, { "GetSamplerParameterfv", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::sampler_objects);
+ }
+
+ // ARB_transform_feedback2/NV_transform_feedback2 is a
+ // superset of EXT_transform_feedback/NV_transform_feedback
+ // and adds glPauseTransformFeedback &
+ // glResumeTransformFeedback, which are required for WebGL2.
+ if (IsSupported(GLFeature::transform_feedback2)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBindBufferBase, { "BindBufferBase", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindBufferRange, { "BindBufferRange", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, { "GenTransformFeedbacks", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, { "BindTransformFeedback", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, { "DeleteTransformFeedbacks", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, { "IsTransformFeedback", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, { "BeginTransformFeedback", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, { "EndTransformFeedback", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, { "TransformFeedbackVaryings", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVarying", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedback", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedback", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBindBufferBase, { "BindBufferBaseEXT", "BindBufferBaseNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindBufferRange, { "BindBufferRangeEXT", "BindBufferRangeNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, { "GenTransformFeedbacksNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, { "BindTransformFeedbackNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, { "DeleteTransformFeedbacksNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, { "IsTransformFeedbackNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, { "BeginTransformFeedbackEXT", "BeginTransformFeedbackNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, { "EndTransformFeedbackEXT", "EndTransformFeedbackNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, { "TransformFeedbackVaryingsEXT", "TransformFeedbackVaryingsNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVaryingEXT", "GetTransformFeedbackVaryingNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedbackNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedbackNV", nullptr } },
+ END_SYMBOLS
+ };
+ if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_storage)) {
+ // Also mark bind_buffer_offset as unsupported.
+ MarkUnsupported(GLFeature::bind_buffer_offset);
+ }
+ }
+
+ if (IsSupported(GLFeature::bind_buffer_offset)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBindBufferOffset, { "BindBufferOffset", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBindBufferOffset,
+ { "BindBufferOffsetEXT", "BindBufferOffsetNV", nullptr }
+ },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::bind_buffer_offset);
+ }
+
+ if (IsSupported(GLFeature::query_counter)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounter", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounterEXT", "QueryCounterANGLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::query_counter);
+ }
+
+ if (IsSupported(GLFeature::query_objects)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQuery", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueries", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueries", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQuery", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQuery", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQueryEXT", "BeginQueryANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueriesEXT", "GenQueriesANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueriesEXT", "DeleteQueriesANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQueryEXT", "EndQueryANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryivEXT", "GetQueryivANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuivEXT", "GetQueryObjectuivANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQueryEXT", "IsQueryANGLE", nullptr } },
+ END_SYMBOLS
+ };
+ if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::query_objects)) {
+ MarkUnsupported(GLFeature::get_query_object_i64v);
+ MarkUnsupported(GLFeature::get_query_object_iv);
+ MarkUnsupported(GLFeature::occlusion_query);
+ MarkUnsupported(GLFeature::occlusion_query_boolean);
+ MarkUnsupported(GLFeature::occlusion_query2);
+ }
+ }
+
+ if (IsSupported(GLFeature::get_query_object_i64v)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64v", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64v", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64vEXT", "GetQueryObjecti64vANGLE", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64vEXT", "GetQueryObjectui64vANGLE", nullptr } },
+ END_SYMBOLS
+ };
+ if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_query_object_i64v)) {
+ MarkUnsupported(GLFeature::query_counter);
+ }
+ }
+
+ if (IsSupported(GLFeature::get_query_object_iv)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectiv", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectivEXT", "GetQueryObjectivANGLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_query_object_iv);
+ }
+
+ if (IsSupported(GLFeature::clear_buffers)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fClearBufferfi, { "ClearBufferfi", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearBufferfv, { "ClearBufferfv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearBufferiv, { "ClearBufferiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClearBufferuiv, { "ClearBufferuiv", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::clear_buffers);
+ }
+
+ if (IsSupported(GLFeature::copy_buffer)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCopyBufferSubData, { "CopyBufferSubData", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::copy_buffer);
+ }
+
+ if (IsSupported(GLFeature::draw_buffers)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffers", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffersARB", "DrawBuffersEXT", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers);
+ }
+
+ if (IsSupported(GLFeature::draw_range_elements)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawRangeElements, { "DrawRangeElements", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDrawRangeElements, { "DrawRangeElementsEXT", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_range_elements);
+ }
+
+ if (IsSupported(GLFeature::get_integer_indexed)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegeri_v", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] ={
+ { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegerIndexedvEXT", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_integer_indexed);
+ }
+
+ if (IsSupported(GLFeature::get_integer64_indexed)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetInteger64i_v, { "GetInteger64i_v", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::get_integer64_indexed);
+ }
+
+ if (IsSupported(GLFeature::gpu_shader4)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetVertexAttribIiv, { "GetVertexAttribIiv", "GetVertexAttribIivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetVertexAttribIuiv, { "GetVertexAttribIuiv", "GetVertexAttribIuivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribI4i, { "VertexAttribI4i", "VertexAttribI4iEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribI4iv, { "VertexAttribI4iv","VertexAttribI4ivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribI4ui, { "VertexAttribI4ui", "VertexAttribI4uiEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribI4uiv, { "VertexAttribI4uiv", "VertexAttribI4uivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fVertexAttribIPointer, { "VertexAttribIPointer", "VertexAttribIPointerEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1ui, { "Uniform1ui", "Uniform1uiEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2ui, { "Uniform2ui", "Uniform2uiEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3ui, { "Uniform3ui", "Uniform3uiEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4ui, { "Uniform4ui", "Uniform4uiEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform1uiv, { "Uniform1uiv", "Uniform1uivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform2uiv, { "Uniform2uiv", "Uniform2uivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform3uiv, { "Uniform3uiv", "Uniform3uivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniform4uiv, { "Uniform4uiv", "Uniform4uivEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetFragDataLocation, { "GetFragDataLocation", "GetFragDataLocationEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetUniformuiv, { "GetUniformuiv", "GetUniformuivEXT", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::gpu_shader4);
+ }
+
+ if (IsSupported(GLFeature::map_buffer_range)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fMapBufferRange, { "MapBufferRange", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fFlushMappedBufferRange, { "FlushMappedBufferRange", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUnmapBuffer, { "UnmapBuffer", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::map_buffer_range);
+ }
+
+ if (IsSupported(GLFeature::texture_3D)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexImage3D, { "TexImage3D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3D", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3DEXT", "TexSubImage3DOES", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D);
+ }
+
+ if (IsSupported(GLFeature::texture_3D_compressed)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, { "CompressedTexImage3D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, { "CompressedTexSubImage3D", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, { "CompressedTexImage3DARB", "CompressedTexImage3DOES", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, { "CompressedTexSubImage3DARB", "CompressedTexSubImage3DOES", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D_compressed);
+ }
+
+ if (IsSupported(GLFeature::texture_3D_copy)) {
+ const SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, { "CopyTexSubImage3D", nullptr } },
+ END_SYMBOLS
+ };
+ const SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, { "CopyTexSubImage3DEXT", "CopyTexSubImage3DOES", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D_copy);
+ }
+
+ if (IsSupported(GLFeature::uniform_buffer_object)) {
+ // Note: Don't query for glGetActiveUniformName because it is not
+ // supported by GL ES 3.
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetUniformIndices, { "GetUniformIndices", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetActiveUniformsiv, { "GetActiveUniformsiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetUniformBlockIndex, { "GetUniformBlockIndex", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockiv, { "GetActiveUniformBlockiv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockName, { "GetActiveUniformBlockName", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformBlockBinding, { "UniformBlockBinding", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::uniform_buffer_object);
+ }
+
+ if (IsSupported(GLFeature::uniform_matrix_nonsquare)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix2x3fv, { "UniformMatrix2x3fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix2x4fv, { "UniformMatrix2x4fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix3x2fv, { "UniformMatrix3x2fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix3x4fv, { "UniformMatrix3x4fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix4x2fv, { "UniformMatrix4x2fv", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUniformMatrix4x3fv, { "UniformMatrix4x3fv", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::uniform_matrix_nonsquare);
+ }
+
+ if (IsSupported(GLFeature::internalformat_query)) {
+ const SymLoadStruct symbols[] = {
+ CORE_SYMBOL(GetInternalformativ),
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::internalformat_query);
+ }
+
+ if (IsSupported(GLFeature::invalidate_framebuffer)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fInvalidateFramebuffer, { "InvalidateFramebuffer", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fInvalidateSubFramebuffer, { "InvalidateSubFramebuffer", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer);
+ }
+
+ if (IsSupported(GLFeature::prim_restart)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex, { "PrimitiveRestartIndex", "PrimitiveRestartIndexNV", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::prim_restart);
+ }
+
+ if (IsExtensionSupported(KHR_debug)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fDebugMessageControl, { "DebugMessageControl", "DebugMessageControlKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDebugMessageInsert, { "DebugMessageInsert", "DebugMessageInsertKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDebugMessageCallback, { "DebugMessageCallback", "DebugMessageCallbackKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetDebugMessageLog, { "GetDebugMessageLog", "GetDebugMessageLogKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetPointerv, { "GetPointerv", "GetPointervKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPushDebugGroup, { "PushDebugGroup", "PushDebugGroupKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fPopDebugGroup, { "PopDebugGroup", "PopDebugGroupKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fObjectLabel, { "ObjectLabel", "ObjectLabelKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetObjectLabel, { "GetObjectLabel", "GetObjectLabelKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fObjectPtrLabel, { "ObjectPtrLabel", "ObjectPtrLabelKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetObjectPtrLabel, { "GetObjectPtrLabel", "GetObjectPtrLabelKHR", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, KHR_debug);
+ }
+
+ if (IsExtensionSupported(NV_fence)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGenFences, { "GenFencesNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDeleteFences, { "DeleteFencesNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fSetFence, { "SetFenceNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTestFence, { "TestFenceNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fFinishFence, { "FinishFenceNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fIsFence, { "IsFenceNV", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetFenceiv, { "GetFenceivNV", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, NV_fence);
+ }
+
+ if (IsExtensionSupported(NV_texture_barrier)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTextureBarrier, { "TextureBarrierNV", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, NV_texture_barrier);
+ }
+
+ if (IsSupported(GLFeature::read_buffer)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fReadBuffer, { "ReadBuffer", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForFeature(symbols, GLFeature::read_buffer);
+ }
+
+ if (IsExtensionSupported(APPLE_framebuffer_multisample)) {
+ const SymLoadStruct symbols[] = {
+ { (PRFuncPtr*) &mSymbols.fResolveMultisampleFramebufferAPPLE, { "ResolveMultisampleFramebufferAPPLE", nullptr } },
+ END_SYMBOLS
+ };
+ fnLoadForExt(symbols, APPLE_framebuffer_multisample);
+ }
+
+ // Load developer symbols, don't fail if we can't find them.
+ const SymLoadStruct devSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fGetTexImage, { "GetTexImage", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetTexLevelParameteriv, { "GetTexLevelParameteriv", nullptr } },
+ END_SYMBOLS
+ };
+ const bool warnOnFailures = ShouldSpew();
+ LoadSymbols(devSymbols, trygl, prefix, warnOnFailures);
+}
+
+#undef CORE_SYMBOL
+#undef CORE_EXT_SYMBOL2
+#undef EXT_SYMBOL2
+#undef EXT_SYMBOL3
+#undef END_SYMBOLS
+
+void
+GLContext::DebugCallback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message)
+{
+ nsAutoCString sourceStr;
+ switch (source) {
+ case LOCAL_GL_DEBUG_SOURCE_API:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_API");
+ break;
+ case LOCAL_GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_WINDOW_SYSTEM");
+ break;
+ case LOCAL_GL_DEBUG_SOURCE_SHADER_COMPILER:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_SHADER_COMPILER");
+ break;
+ case LOCAL_GL_DEBUG_SOURCE_THIRD_PARTY:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_THIRD_PARTY");
+ break;
+ case LOCAL_GL_DEBUG_SOURCE_APPLICATION:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_APPLICATION");
+ break;
+ case LOCAL_GL_DEBUG_SOURCE_OTHER:
+ sourceStr = NS_LITERAL_CSTRING("SOURCE_OTHER");
+ break;
+ default:
+ sourceStr = nsPrintfCString("<source 0x%04x>", source);
+ break;
+ }
+
+ nsAutoCString typeStr;
+ switch (type) {
+ case LOCAL_GL_DEBUG_TYPE_ERROR:
+ typeStr = NS_LITERAL_CSTRING("TYPE_ERROR");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ typeStr = NS_LITERAL_CSTRING("TYPE_DEPRECATED_BEHAVIOR");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ typeStr = NS_LITERAL_CSTRING("TYPE_UNDEFINED_BEHAVIOR");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_PORTABILITY:
+ typeStr = NS_LITERAL_CSTRING("TYPE_PORTABILITY");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_PERFORMANCE:
+ typeStr = NS_LITERAL_CSTRING("TYPE_PERFORMANCE");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_OTHER:
+ typeStr = NS_LITERAL_CSTRING("TYPE_OTHER");
+ break;
+ case LOCAL_GL_DEBUG_TYPE_MARKER:
+ typeStr = NS_LITERAL_CSTRING("TYPE_MARKER");
+ break;
+ default:
+ typeStr = nsPrintfCString("<type 0x%04x>", type);
+ break;
+ }
+
+ nsAutoCString sevStr;
+ switch (severity) {
+ case LOCAL_GL_DEBUG_SEVERITY_HIGH:
+ sevStr = NS_LITERAL_CSTRING("SEVERITY_HIGH");
+ break;
+ case LOCAL_GL_DEBUG_SEVERITY_MEDIUM:
+ sevStr = NS_LITERAL_CSTRING("SEVERITY_MEDIUM");
+ break;
+ case LOCAL_GL_DEBUG_SEVERITY_LOW:
+ sevStr = NS_LITERAL_CSTRING("SEVERITY_LOW");
+ break;
+ case LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION:
+ sevStr = NS_LITERAL_CSTRING("SEVERITY_NOTIFICATION");
+ break;
+ default:
+ sevStr = nsPrintfCString("<severity 0x%04x>", severity);
+ break;
+ }
+
+ printf_stderr("[KHR_debug: 0x%" PRIxPTR "] ID %u: %s, %s, %s:\n %s\n",
+ (uintptr_t)this,
+ id,
+ sourceStr.BeginReading(),
+ typeStr.BeginReading(),
+ sevStr.BeginReading(),
+ message);
+}
+
+void
+GLContext::InitExtensions()
+{
+ MOZ_ASSERT(IsCurrent());
+
+ std::vector<nsCString> driverExtensionList;
+
+ if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) {
+ GLuint count = 0;
+ GetUIntegerv(LOCAL_GL_NUM_EXTENSIONS, &count);
+ for (GLuint i = 0; i < count; i++) {
+ // This is UTF-8.
+ const char* rawExt = (const char*)fGetStringi(LOCAL_GL_EXTENSIONS, i);
+
+ // We CANNOT use nsDependentCString here, because the spec doesn't guarantee
+ // that the pointers returned are different, only that their contents are.
+ // On Flame, each of these index string queries returns the same address.
+ driverExtensionList.push_back(nsCString(rawExt));
+ }
+ } else {
+ MOZ_ALWAYS_TRUE(!fGetError());
+ const char* rawExts = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
+ MOZ_ALWAYS_TRUE(!fGetError());
+
+ if (rawExts) {
+ nsDependentCString exts(rawExts);
+ SplitByChar(exts, ' ', &driverExtensionList);
+ }
+ }
+
+ const bool shouldDumpExts = ShouldDumpExts();
+ if (shouldDumpExts) {
+ printf_stderr("%i GL driver extensions: (*: recognized)\n",
+ (uint32_t)driverExtensionList.size());
+ }
+
+ MarkBitfieldByStrings(driverExtensionList, shouldDumpExts, sExtensionNames,
+ &mAvailableExtensions);
+
+ if (WorkAroundDriverBugs()) {
+ if (Vendor() == GLVendor::Qualcomm) {
+ // Some Adreno drivers do not report GL_OES_EGL_sync, but they really do support it.
+ MarkExtensionSupported(OES_EGL_sync);
+ }
+
+ if (Vendor() == GLVendor::ATI) {
+ // ATI drivers say this extension exists, but we can't
+ // actually find the EGLImageTargetRenderbufferStorageOES
+ // extension function pointer in the drivers.
+ MarkExtensionUnsupported(OES_EGL_image);
+ }
+
+ if (Vendor() == GLVendor::Imagination &&
+ Renderer() == GLRenderer::SGX540)
+ {
+ // Bug 980048
+ MarkExtensionUnsupported(OES_EGL_sync);
+ }
+
+ if (Vendor() == GLVendor::ARM &&
+ (Renderer() == GLRenderer::Mali400MP ||
+ Renderer() == GLRenderer::Mali450MP))
+ {
+ // Bug 1264505
+ MarkExtensionUnsupported(OES_EGL_image_external);
+ }
+
+ if (Vendor() == GLVendor::VMware &&
+ Renderer() == GLRenderer::GalliumLlvmpipe)
+ {
+ // The llvmpipe driver that is used on linux try servers appears to have
+ // buggy support for s3tc/dxt1 compressed textures.
+ // See Bug 975824.
+ MarkExtensionUnsupported(EXT_texture_compression_s3tc);
+ MarkExtensionUnsupported(EXT_texture_compression_dxt1);
+ MarkExtensionUnsupported(ANGLE_texture_compression_dxt3);
+ MarkExtensionUnsupported(ANGLE_texture_compression_dxt5);
+ }
+ }
+
+ if (shouldDumpExts) {
+ printf_stderr("\nActivated extensions:\n");
+
+ for (size_t i = 0; i < mAvailableExtensions.size(); i++) {
+ if (!mAvailableExtensions[i])
+ continue;
+
+ const char* ext = sExtensionNames[i];
+ printf_stderr("[%i] %s\n", (uint32_t)i, ext);
+ }
+ }
+}
+
+void
+GLContext::PlatformStartup()
+{
+ RegisterStrongMemoryReporter(new GfxTexturesReporter());
+}
+
+// Common code for checking for both GL extensions and GLX extensions.
+bool
+GLContext::ListHasExtension(const GLubyte* extensions, const char* extension)
+{
+ // fix bug 612572 - we were crashing as we were calling this function with extensions==null
+ if (extensions == nullptr || extension == nullptr)
+ return false;
+
+ const GLubyte* start;
+ GLubyte* where;
+ GLubyte* terminator;
+
+ /* Extension names should not have spaces. */
+ where = (GLubyte*) strchr(extension, ' ');
+ if (where || *extension == '\0')
+ return false;
+
+ /*
+ * It takes a bit of care to be fool-proof about parsing the
+ * OpenGL extensions string. Don't be fooled by sub-strings,
+ * etc.
+ */
+ start = extensions;
+ for (;;) {
+ where = (GLubyte*) strstr((const char*) start, extension);
+ if (!where) {
+ break;
+ }
+ terminator = where + strlen(extension);
+ if (where == start || *(where - 1) == ' ') {
+ if (*terminator == ' ' || *terminator == '\0') {
+ return true;
+ }
+ }
+ start = terminator;
+ }
+ return false;
+}
+
+GLFormats
+GLContext::ChooseGLFormats(const SurfaceCaps& caps) const
+{
+ GLFormats formats;
+
+ // If we're on ES2 hardware and we have an explicit request for 16 bits of color or less
+ // OR we don't support full 8-bit color, return a 4444 or 565 format.
+ bool bpp16 = caps.bpp16;
+ if (IsGLES()) {
+ if (!IsExtensionSupported(OES_rgb8_rgba8))
+ bpp16 = true;
+ } else {
+ // RGB565 is uncommon on desktop, requiring ARB_ES2_compatibility.
+ // Since it's also vanishingly useless there, let's not support it.
+ bpp16 = false;
+ }
+
+ if (bpp16) {
+ MOZ_ASSERT(IsGLES());
+ if (caps.alpha) {
+ formats.color_texInternalFormat = LOCAL_GL_RGBA;
+ formats.color_texFormat = LOCAL_GL_RGBA;
+ formats.color_texType = LOCAL_GL_UNSIGNED_SHORT_4_4_4_4;
+ formats.color_rbFormat = LOCAL_GL_RGBA4;
+ } else {
+ formats.color_texInternalFormat = LOCAL_GL_RGB;
+ formats.color_texFormat = LOCAL_GL_RGB;
+ formats.color_texType = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
+ formats.color_rbFormat = LOCAL_GL_RGB565;
+ }
+ } else {
+ formats.color_texType = LOCAL_GL_UNSIGNED_BYTE;
+
+ if (caps.alpha) {
+ formats.color_texInternalFormat = IsGLES() ? LOCAL_GL_RGBA : LOCAL_GL_RGBA8;
+ formats.color_texFormat = LOCAL_GL_RGBA;
+ formats.color_rbFormat = LOCAL_GL_RGBA8;
+ } else {
+ formats.color_texInternalFormat = IsGLES() ? LOCAL_GL_RGB : LOCAL_GL_RGB8;
+ formats.color_texFormat = LOCAL_GL_RGB;
+ formats.color_rbFormat = LOCAL_GL_RGB8;
+ }
+ }
+
+ uint32_t msaaLevel = gfxPrefs::MSAALevel();
+ GLsizei samples = msaaLevel * msaaLevel;
+ samples = std::min(samples, mMaxSamples);
+
+ // Bug 778765.
+ if (WorkAroundDriverBugs() && samples == 1) {
+ samples = 0;
+ }
+ formats.samples = samples;
+
+
+ // Be clear that these are 0 if unavailable.
+ formats.depthStencil = 0;
+ if (IsSupported(GLFeature::packed_depth_stencil)) {
+ formats.depthStencil = LOCAL_GL_DEPTH24_STENCIL8;
+ }
+
+ formats.depth = 0;
+ if (IsGLES()) {
+ if (IsExtensionSupported(OES_depth24)) {
+ formats.depth = LOCAL_GL_DEPTH_COMPONENT24;
+ } else {
+ formats.depth = LOCAL_GL_DEPTH_COMPONENT16;
+ }
+ } else {
+ formats.depth = LOCAL_GL_DEPTH_COMPONENT24;
+ }
+
+ formats.stencil = LOCAL_GL_STENCIL_INDEX8;
+
+ return formats;
+}
+
+bool
+GLContext::IsFramebufferComplete(GLuint fb, GLenum* pStatus)
+{
+ MOZ_ASSERT(fb);
+
+ ScopedBindFramebuffer autoFB(this, fb);
+ MOZ_ASSERT(fIsFramebuffer(fb));
+
+ GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (pStatus)
+ *pStatus = status;
+
+ return status == LOCAL_GL_FRAMEBUFFER_COMPLETE;
+}
+
+void
+GLContext::AttachBuffersToFB(GLuint colorTex, GLuint colorRB,
+ GLuint depthRB, GLuint stencilRB,
+ GLuint fb, GLenum target)
+{
+ MOZ_ASSERT(fb);
+ MOZ_ASSERT( !(colorTex && colorRB) );
+
+ ScopedBindFramebuffer autoFB(this, fb);
+ MOZ_ASSERT(fIsFramebuffer(fb)); // It only counts after being bound.
+
+ if (colorTex) {
+ MOZ_ASSERT(fIsTexture(colorTex));
+ MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D ||
+ target == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+ fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ target,
+ colorTex,
+ 0);
+ } else if (colorRB) {
+ MOZ_ASSERT(fIsRenderbuffer(colorRB));
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER,
+ colorRB);
+ }
+
+ if (depthRB) {
+ MOZ_ASSERT(fIsRenderbuffer(depthRB));
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_DEPTH_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER,
+ depthRB);
+ }
+
+ if (stencilRB) {
+ MOZ_ASSERT(fIsRenderbuffer(stencilRB));
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_STENCIL_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER,
+ stencilRB);
+ }
+}
+
+bool
+GLContext::AssembleOffscreenFBs(const GLuint colorMSRB,
+ const GLuint depthRB,
+ const GLuint stencilRB,
+ const GLuint texture,
+ GLuint* drawFB_out,
+ GLuint* readFB_out)
+{
+ if (!colorMSRB && !texture) {
+ MOZ_ASSERT(!depthRB && !stencilRB);
+
+ if (drawFB_out)
+ *drawFB_out = 0;
+ if (readFB_out)
+ *readFB_out = 0;
+
+ return true;
+ }
+
+ ScopedBindFramebuffer autoFB(this);
+
+ GLuint drawFB = 0;
+ GLuint readFB = 0;
+
+ if (texture) {
+ readFB = 0;
+ fGenFramebuffers(1, &readFB);
+ BindFB(readFB);
+ fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D,
+ texture,
+ 0);
+ }
+
+ if (colorMSRB) {
+ drawFB = 0;
+ fGenFramebuffers(1, &drawFB);
+ BindFB(drawFB);
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER,
+ colorMSRB);
+ } else {
+ drawFB = readFB;
+ }
+ MOZ_ASSERT(GetFB() == drawFB);
+
+ if (depthRB) {
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_DEPTH_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER,
+ depthRB);
+ }
+
+ if (stencilRB) {
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_STENCIL_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER,
+ stencilRB);
+ }
+
+ // We should be all resized. Check for framebuffer completeness.
+ GLenum status;
+ bool isComplete = true;
+
+ if (!IsFramebufferComplete(drawFB, &status)) {
+ NS_WARNING("DrawFBO: Incomplete");
+ #ifdef MOZ_GL_DEBUG
+ if (ShouldSpew()) {
+ printf_stderr("Framebuffer status: %X\n", status);
+ }
+ #endif
+ isComplete = false;
+ }
+
+ if (!IsFramebufferComplete(readFB, &status)) {
+ NS_WARNING("ReadFBO: Incomplete");
+ #ifdef MOZ_GL_DEBUG
+ if (ShouldSpew()) {
+ printf_stderr("Framebuffer status: %X\n", status);
+ }
+ #endif
+ isComplete = false;
+ }
+
+ if (drawFB_out) {
+ *drawFB_out = drawFB;
+ } else if (drawFB) {
+ NS_RUNTIMEABORT("drawFB created when not requested!");
+ }
+
+ if (readFB_out) {
+ *readFB_out = readFB;
+ } else if (readFB) {
+ NS_RUNTIMEABORT("readFB created when not requested!");
+ }
+
+ return isComplete;
+}
+
+
+void
+GLContext::ClearSafely()
+{
+ // bug 659349 --- we must be very careful here: clearing a GL framebuffer is nontrivial, relies on a lot of state,
+ // and in the case of the backbuffer of a WebGL context, state is exposed to scripts.
+ //
+ // The code here is taken from WebGLContext::ForceClearFramebufferWithDefaultValues, but I didn't find a good way of
+ // sharing code with it. WebGL's code is somewhat performance-critical as it is typically called on every frame, so
+ // WebGL keeps track of GL state to avoid having to query it everytime, and also tries to only do work for actually
+ // present buffers (e.g. stencil buffer). Doing that here seems like premature optimization,
+ // as ClearSafely() is called only when e.g. a canvas is resized, not on every animation frame.
+
+ realGLboolean scissorTestEnabled;
+ realGLboolean ditherEnabled;
+ realGLboolean colorWriteMask[4];
+ realGLboolean depthWriteMask;
+ GLint stencilWriteMaskFront, stencilWriteMaskBack;
+ GLfloat colorClearValue[4];
+ GLfloat depthClearValue;
+ GLint stencilClearValue;
+
+ // save current GL state
+ fGetBooleanv(LOCAL_GL_SCISSOR_TEST, &scissorTestEnabled);
+ fGetBooleanv(LOCAL_GL_DITHER, &ditherEnabled);
+ fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
+ fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
+ fGetIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &stencilWriteMaskFront);
+ fGetIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
+ fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
+ fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
+ fGetIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
+
+ // prepare GL state for clearing
+ fDisable(LOCAL_GL_SCISSOR_TEST);
+ fDisable(LOCAL_GL_DITHER);
+
+ fColorMask(1, 1, 1, 1);
+ fClearColor(0.f, 0.f, 0.f, 0.f);
+
+ fDepthMask(1);
+ fClearDepth(1.0f);
+
+ fStencilMask(0xffffffff);
+ fClearStencil(0);
+
+ // do clear
+ fClear(LOCAL_GL_COLOR_BUFFER_BIT |
+ LOCAL_GL_DEPTH_BUFFER_BIT |
+ LOCAL_GL_STENCIL_BUFFER_BIT);
+
+ // restore GL state after clearing
+ fColorMask(colorWriteMask[0],
+ colorWriteMask[1],
+ colorWriteMask[2],
+ colorWriteMask[3]);
+ fClearColor(colorClearValue[0],
+ colorClearValue[1],
+ colorClearValue[2],
+ colorClearValue[3]);
+
+ fDepthMask(depthWriteMask);
+ fClearDepth(depthClearValue);
+
+ fStencilMaskSeparate(LOCAL_GL_FRONT, stencilWriteMaskFront);
+ fStencilMaskSeparate(LOCAL_GL_BACK, stencilWriteMaskBack);
+ fClearStencil(stencilClearValue);
+
+ if (ditherEnabled)
+ fEnable(LOCAL_GL_DITHER);
+ else
+ fDisable(LOCAL_GL_DITHER);
+
+ if (scissorTestEnabled)
+ fEnable(LOCAL_GL_SCISSOR_TEST);
+ else
+ fDisable(LOCAL_GL_SCISSOR_TEST);
+
+}
+
+void
+GLContext::MarkDestroyed()
+{
+ if (IsDestroyed())
+ return;
+
+ // Null these before they're naturally nulled after dtor, as we want GLContext to
+ // still be alive in *their* dtors.
+ mScreen = nullptr;
+ mBlitHelper = nullptr;
+ mReadTexImageHelper = nullptr;
+
+ mIsDestroyed = true;
+ mSymbols.Zero();
+ if (MakeCurrent(true)) {
+ mTexGarbageBin->GLContextTeardown();
+ }
+}
+
+#ifdef MOZ_GL_DEBUG
+/* static */ void
+GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
+{
+ int somethingOnTheStack;
+ const void* someStackPtr = &somethingOnTheStack;
+ const int page_bits = 12;
+ intptr_t page = reinterpret_cast<uintptr_t>(ptr) >> page_bits;
+ intptr_t someStackPage = reinterpret_cast<uintptr_t>(someStackPtr) >> page_bits;
+ uintptr_t pageDistance = std::abs(page - someStackPage);
+
+ // Explanation for the "distance <= 1" check here as opposed to just
+ // an equality check.
+ //
+ // Here we assume that pages immediately adjacent to the someStackAddress page,
+ // are also stack pages. That allows to catch the case where the calling frame put
+ // a buffer on the stack, and we just crossed the page boundary. That is likely
+ // to happen, precisely, when using stack arrays. I hit that specifically
+ // with CompositorOGL::Initialize.
+ //
+ // In theory we could be unlucky and wrongly assert here. If that happens,
+ // it will only affect debug builds, and looking at stacks we'll be able to
+ // see that this assert is wrong and revert to the conservative and safe
+ // approach of only asserting when address and someStackAddress are
+ // on the same page.
+ bool isStackAddress = pageDistance <= 1;
+ MOZ_ASSERT(!isStackAddress,
+ "Please don't pass stack arrays to the GL. "
+ "Consider using HeapCopyOfStackArray. "
+ "See bug 1005658.");
+}
+
+void
+GLContext::CreatedProgram(GLContext* aOrigin, GLuint aName)
+{
+ mTrackedPrograms.AppendElement(NamedResource(aOrigin, aName));
+}
+
+void
+GLContext::CreatedShader(GLContext* aOrigin, GLuint aName)
+{
+ mTrackedShaders.AppendElement(NamedResource(aOrigin, aName));
+}
+
+void
+GLContext::CreatedBuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames)
+{
+ for (GLsizei i = 0; i < aCount; ++i) {
+ mTrackedBuffers.AppendElement(NamedResource(aOrigin, aNames[i]));
+ }
+}
+
+void
+GLContext::CreatedQueries(GLContext* aOrigin, GLsizei aCount, GLuint* aNames)
+{
+ for (GLsizei i = 0; i < aCount; ++i) {
+ mTrackedQueries.AppendElement(NamedResource(aOrigin, aNames[i]));
+ }
+}
+
+void
+GLContext::CreatedTextures(GLContext* aOrigin, GLsizei aCount, GLuint* aNames)
+{
+ for (GLsizei i = 0; i < aCount; ++i) {
+ mTrackedTextures.AppendElement(NamedResource(aOrigin, aNames[i]));
+ }
+}
+
+void
+GLContext::CreatedFramebuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames)
+{
+ for (GLsizei i = 0; i < aCount; ++i) {
+ mTrackedFramebuffers.AppendElement(NamedResource(aOrigin, aNames[i]));
+ }
+}
+
+void
+GLContext::CreatedRenderbuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames)
+{
+ for (GLsizei i = 0; i < aCount; ++i) {
+ mTrackedRenderbuffers.AppendElement(NamedResource(aOrigin, aNames[i]));
+ }
+}
+
+static void
+RemoveNamesFromArray(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames, nsTArray<GLContext::NamedResource>& aArray)
+{
+ for (GLsizei j = 0; j < aCount; ++j) {
+ GLuint name = aNames[j];
+ // name 0 can be ignored
+ if (name == 0)
+ continue;
+
+ for (uint32_t i = 0; i < aArray.Length(); ++i) {
+ if (aArray[i].name == name) {
+ aArray.RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+}
+
+void
+GLContext::DeletedProgram(GLContext* aOrigin, GLuint aName)
+{
+ RemoveNamesFromArray(aOrigin, 1, &aName, mTrackedPrograms);
+}
+
+void
+GLContext::DeletedShader(GLContext* aOrigin, GLuint aName)
+{
+ RemoveNamesFromArray(aOrigin, 1, &aName, mTrackedShaders);
+}
+
+void
+GLContext::DeletedBuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames)
+{
+ RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedBuffers);
+}
+
+void
+GLContext::DeletedQueries(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames)
+{
+ RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedQueries);
+}
+
+void
+GLContext::DeletedTextures(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames)
+{
+ RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedTextures);
+}
+
+void
+GLContext::DeletedFramebuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames)
+{
+ RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedFramebuffers);
+}
+
+void
+GLContext::DeletedRenderbuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames)
+{
+ RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedRenderbuffers);
+}
+
+static void
+MarkContextDestroyedInArray(GLContext* aContext, nsTArray<GLContext::NamedResource>& aArray)
+{
+ for (uint32_t i = 0; i < aArray.Length(); ++i) {
+ if (aArray[i].origin == aContext)
+ aArray[i].originDeleted = true;
+ }
+}
+
+void
+GLContext::SharedContextDestroyed(GLContext* aChild)
+{
+ MarkContextDestroyedInArray(aChild, mTrackedPrograms);
+ MarkContextDestroyedInArray(aChild, mTrackedShaders);
+ MarkContextDestroyedInArray(aChild, mTrackedTextures);
+ MarkContextDestroyedInArray(aChild, mTrackedFramebuffers);
+ MarkContextDestroyedInArray(aChild, mTrackedRenderbuffers);
+ MarkContextDestroyedInArray(aChild, mTrackedBuffers);
+ MarkContextDestroyedInArray(aChild, mTrackedQueries);
+}
+
+static void
+ReportArrayContents(const char* title, const nsTArray<GLContext::NamedResource>& aArray)
+{
+ if (aArray.Length() == 0)
+ return;
+
+ printf_stderr("%s:\n", title);
+
+ nsTArray<GLContext::NamedResource> copy(aArray);
+ copy.Sort();
+
+ GLContext* lastContext = nullptr;
+ for (uint32_t i = 0; i < copy.Length(); ++i) {
+ if (lastContext != copy[i].origin) {
+ if (lastContext)
+ printf_stderr("\n");
+ printf_stderr(" [%p - %s] ", copy[i].origin, copy[i].originDeleted ? "deleted" : "live");
+ lastContext = copy[i].origin;
+ }
+ printf_stderr("%d ", copy[i].name);
+ }
+ printf_stderr("\n");
+}
+
+void
+GLContext::ReportOutstandingNames()
+{
+ if (!ShouldSpew())
+ return;
+
+ printf_stderr("== GLContext %p Outstanding ==\n", this);
+
+ ReportArrayContents("Outstanding Textures", mTrackedTextures);
+ ReportArrayContents("Outstanding Buffers", mTrackedBuffers);
+ ReportArrayContents("Outstanding Queries", mTrackedQueries);
+ ReportArrayContents("Outstanding Programs", mTrackedPrograms);
+ ReportArrayContents("Outstanding Shaders", mTrackedShaders);
+ ReportArrayContents("Outstanding Framebuffers", mTrackedFramebuffers);
+ ReportArrayContents("Outstanding Renderbuffers", mTrackedRenderbuffers);
+}
+
+#endif /* DEBUG */
+
+void
+GLContext::GuaranteeResolve()
+{
+ if (mScreen) {
+ mScreen->AssureBlitted();
+ }
+ fFinish();
+}
+
+const gfx::IntSize&
+GLContext::OffscreenSize() const
+{
+ MOZ_ASSERT(IsOffscreen());
+ return mScreen->Size();
+}
+
+bool
+GLContext::CreateScreenBufferImpl(const IntSize& size, const SurfaceCaps& caps)
+{
+ UniquePtr<GLScreenBuffer> newScreen = GLScreenBuffer::Create(this, size, caps);
+ if (!newScreen)
+ return false;
+
+ if (!newScreen->Resize(size)) {
+ return false;
+ }
+
+ // This will rebind to 0 (Screen) if needed when
+ // it falls out of scope.
+ ScopedBindFramebuffer autoFB(this);
+
+ mScreen = Move(newScreen);
+
+ return true;
+}
+
+bool
+GLContext::ResizeScreenBuffer(const IntSize& size)
+{
+ if (!IsOffscreenSizeAllowed(size))
+ return false;
+
+ return mScreen->Resize(size);
+}
+
+void
+GLContext::ForceDirtyScreen()
+{
+ ScopedBindFramebuffer autoFB(0);
+
+ BeforeGLDrawCall();
+ // no-op; just pretend we did something
+ AfterGLDrawCall();
+}
+
+void
+GLContext::CleanDirtyScreen()
+{
+ ScopedBindFramebuffer autoFB(0);
+
+ BeforeGLReadCall();
+ // no-op; we just want to make sure the Read FBO is updated if it needs to be
+ AfterGLReadCall();
+}
+
+void
+GLContext::EmptyTexGarbageBin()
+{
+ TexGarbageBin()->EmptyGarbage();
+}
+
+bool
+GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const
+{
+ int32_t biggerDimension = std::max(aSize.width, aSize.height);
+ int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
+ return biggerDimension <= maxAllowed;
+}
+
+bool
+GLContext::IsOwningThreadCurrent()
+{
+ return PlatformThread::CurrentId() == mOwningThreadId;
+}
+
+GLBlitHelper*
+GLContext::BlitHelper()
+{
+ if (!mBlitHelper) {
+ mBlitHelper.reset(new GLBlitHelper(this));
+ }
+
+ return mBlitHelper.get();
+}
+
+GLReadTexImageHelper*
+GLContext::ReadTexImageHelper()
+{
+ if (!mReadTexImageHelper) {
+ mReadTexImageHelper = MakeUnique<GLReadTexImageHelper>(this);
+ }
+
+ return mReadTexImageHelper.get();
+}
+
+void
+GLContext::FlushIfHeavyGLCallsSinceLastFlush()
+{
+ if (!mHeavyGLCallsSinceLastFlush) {
+ return;
+ }
+ MakeCurrent();
+ fFlush();
+}
+
+/*static*/ bool
+GLContext::ShouldDumpExts()
+{
+ return gfxEnv::GlDumpExtensions();
+}
+
+bool
+DoesStringMatch(const char* aString, const char* aWantedString)
+{
+ if (!aString || !aWantedString)
+ return false;
+
+ const char* occurrence = strstr(aString, aWantedString);
+
+ // aWanted not found
+ if (!occurrence)
+ return false;
+
+ // aWantedString preceded by alpha character
+ if (occurrence != aString && isalpha(*(occurrence-1)))
+ return false;
+
+ // aWantedVendor followed by alpha character
+ const char* afterOccurrence = occurrence + strlen(aWantedString);
+ if (isalpha(*afterOccurrence))
+ return false;
+
+ return true;
+}
+
+/*static*/ bool
+GLContext::ShouldSpew()
+{
+ return gfxEnv::GlSpew();
+}
+
+void
+SplitByChar(const nsACString& str, const char delim, std::vector<nsCString>* const out)
+{
+ uint32_t start = 0;
+ while (true) {
+ int32_t end = str.FindChar(' ', start);
+ if (end == -1)
+ break;
+
+ uint32_t len = (uint32_t)end - start;
+ nsDependentCSubstring substr(str, start, len);
+ out->push_back(nsCString(substr));
+
+ start = end + 1;
+ continue;
+ }
+
+ nsDependentCSubstring substr(str, start);
+ out->push_back(nsCString(substr));
+}
+
+void
+GLContext::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
+{
+ MOZ_ASSERT(src && dest);
+ MOZ_ASSERT(dest->GetSize() == src->mSize);
+ MOZ_ASSERT(dest->GetFormat() == (src->mHasAlpha ? SurfaceFormat::B8G8R8A8
+ : SurfaceFormat::B8G8R8X8));
+
+ MakeCurrent();
+
+ SharedSurface* prev = GetLockedSurface();
+
+ const bool needsSwap = src != prev;
+ if (needsSwap) {
+ if (prev)
+ prev->UnlockProd();
+ src->LockProd();
+ }
+
+ GLuint tempFB = 0;
+ GLuint tempTex = 0;
+
+ {
+ ScopedBindFramebuffer autoFB(this);
+
+ // We're consuming from the producer side, so which do we use?
+ // Really, we just want a read-only lock, so ConsumerAcquire is the best match.
+ src->ProducerReadAcquire();
+
+ if (src->mAttachType == AttachmentType::Screen) {
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+ } else {
+ fGenFramebuffers(1, &tempFB);
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, tempFB);
+
+ switch (src->mAttachType) {
+ case AttachmentType::GLTexture:
+ fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+ src->ProdTextureTarget(), src->ProdTexture(), 0);
+ break;
+ case AttachmentType::GLRenderbuffer:
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER, src->ProdRenderbuffer());
+ break;
+ default:
+ MOZ_CRASH("GFX: bad `src->mAttachType`.");
+ }
+
+ DebugOnly<GLenum> status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
+ }
+
+ if (src->NeedsIndirectReads()) {
+ fGenTextures(1, &tempTex);
+ {
+ ScopedBindTexture autoTex(this, tempTex);
+
+ GLenum format = src->mHasAlpha ? LOCAL_GL_RGBA
+ : LOCAL_GL_RGB;
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+ fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, format, 0, 0, width,
+ height, 0);
+ }
+
+ fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D, tempTex, 0);
+ }
+
+ ReadPixelsIntoDataSurface(this, dest);
+
+ src->ProducerReadRelease();
+ }
+
+ if (tempFB)
+ fDeleteFramebuffers(1, &tempFB);
+
+ if (tempTex) {
+ fDeleteTextures(1, &tempTex);
+ }
+
+ if (needsSwap) {
+ src->UnlockProd();
+ if (prev)
+ prev->LockProd();
+ }
+}
+
+// Do whatever tear-down is necessary after drawing to our offscreen FBO,
+// if it's bound.
+void
+GLContext::AfterGLDrawCall()
+{
+ if (mScreen) {
+ mScreen->AfterDrawCall();
+ }
+ mHeavyGLCallsSinceLastFlush = true;
+}
+
+// Do whatever setup is necessary to read from our offscreen FBO, if it's
+// bound.
+void
+GLContext::BeforeGLReadCall()
+{
+ if (mScreen)
+ mScreen->BeforeReadCall();
+}
+
+void
+GLContext::fBindFramebuffer(GLenum target, GLuint framebuffer)
+{
+ if (!mScreen) {
+ raw_fBindFramebuffer(target, framebuffer);
+ return;
+ }
+
+ switch (target) {
+ case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
+ mScreen->BindDrawFB(framebuffer);
+ return;
+
+ case LOCAL_GL_READ_FRAMEBUFFER_EXT:
+ mScreen->BindReadFB(framebuffer);
+ return;
+
+ case LOCAL_GL_FRAMEBUFFER:
+ mScreen->BindFB(framebuffer);
+ return;
+
+ default:
+ // Nothing we care about, likely an error.
+ break;
+ }
+
+ raw_fBindFramebuffer(target, framebuffer);
+}
+
+void
+GLContext::fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border)
+{
+ if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
+ // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
+ // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
+ level = -1;
+ width = -1;
+ height = -1;
+ border = -1;
+ }
+
+ BeforeGLReadCall();
+ bool didCopyTexImage2D = false;
+ if (mScreen) {
+ didCopyTexImage2D = mScreen->CopyTexImage2D(target, level, internalformat, x,
+ y, width, height, border);
+ }
+
+ if (!didCopyTexImage2D) {
+ raw_fCopyTexImage2D(target, level, internalformat, x, y, width, height,
+ border);
+ }
+ AfterGLReadCall();
+}
+
+void
+GLContext::fGetIntegerv(GLenum pname, GLint* params)
+{
+ switch (pname) {
+ // LOCAL_GL_FRAMEBUFFER_BINDING is equal to
+ // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT,
+ // so we don't need two cases.
+ case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT:
+ if (mScreen) {
+ *params = mScreen->GetDrawFB();
+ } else {
+ raw_fGetIntegerv(pname, params);
+ }
+ break;
+
+ case LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT:
+ if (mScreen) {
+ *params = mScreen->GetReadFB();
+ } else {
+ raw_fGetIntegerv(pname, params);
+ }
+ break;
+
+ case LOCAL_GL_MAX_TEXTURE_SIZE:
+ MOZ_ASSERT(mMaxTextureSize>0);
+ *params = mMaxTextureSize;
+ break;
+
+ case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
+ MOZ_ASSERT(mMaxCubeMapTextureSize>0);
+ *params = mMaxCubeMapTextureSize;
+ break;
+
+ case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
+ MOZ_ASSERT(mMaxRenderbufferSize>0);
+ *params = mMaxRenderbufferSize;
+ break;
+
+ case LOCAL_GL_VIEWPORT:
+ for (size_t i = 0; i < 4; i++) {
+ params[i] = mViewportRect[i];
+ }
+ break;
+
+ case LOCAL_GL_SCISSOR_BOX:
+ for (size_t i = 0; i < 4; i++) {
+ params[i] = mScissorRect[i];
+ }
+ break;
+
+ default:
+ raw_fGetIntegerv(pname, params);
+ break;
+ }
+}
+
+void
+GLContext::fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+ GLenum type, GLvoid* pixels)
+{
+ BeforeGLReadCall();
+
+ bool didReadPixels = false;
+ if (mScreen) {
+ didReadPixels = mScreen->ReadPixels(x, y, width, height, format, type, pixels);
+ }
+
+ if (!didReadPixels) {
+ raw_fReadPixels(x, y, width, height, format, type, pixels);
+ }
+
+ AfterGLReadCall();
+}
+
+void
+GLContext::fDeleteFramebuffers(GLsizei n, const GLuint* names)
+{
+ if (mScreen) {
+ // Notify mScreen which framebuffers we're deleting.
+ // Otherwise, we will get framebuffer binding mispredictions.
+ for (int i = 0; i < n; i++) {
+ mScreen->DeletingFB(names[i]);
+ }
+ }
+
+ // Avoid crash by flushing before glDeleteFramebuffers. See bug 1194923.
+ if (mNeedsFlushBeforeDeleteFB) {
+ fFlush();
+ }
+
+ if (n == 1 && *names == 0) {
+ // Deleting framebuffer 0 causes hangs on the DROID. See bug 623228.
+ } else {
+ raw_fDeleteFramebuffers(n, names);
+ }
+ TRACKING_CONTEXT(DeletedFramebuffers(this, n, names));
+}
+
+void
+GLContext::fTexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum format, GLenum type, const GLvoid* pixels) {
+ if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
+ // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
+ // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
+ level = -1;
+ width = -1;
+ height = -1;
+ border = -1;
+ }
+ raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+}
+
+GLuint
+GLContext::GetDrawFB()
+{
+ if (mScreen)
+ return mScreen->GetDrawFB();
+
+ GLuint ret = 0;
+ GetUIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, &ret);
+ return ret;
+}
+
+GLuint
+GLContext::GetReadFB()
+{
+ if (mScreen)
+ return mScreen->GetReadFB();
+
+ GLenum bindEnum = IsSupported(GLFeature::split_framebuffer)
+ ? LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT
+ : LOCAL_GL_FRAMEBUFFER_BINDING;
+
+ GLuint ret = 0;
+ GetUIntegerv(bindEnum, &ret);
+ return ret;
+}
+
+GLuint
+GLContext::GetFB()
+{
+ if (mScreen) {
+ // This has a very important extra assert that checks that we're
+ // not accidentally ignoring a situation where the draw and read
+ // FBs differ.
+ return mScreen->GetFB();
+ }
+
+ GLuint ret = 0;
+ GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret);
+ return ret;
+}
+
+bool
+GLContext::InitOffscreen(const gfx::IntSize& size, const SurfaceCaps& caps)
+{
+ if (!CreateScreenBuffer(size, caps))
+ return false;
+
+ MakeCurrent();
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+ fScissor(0, 0, size.width, size.height);
+ fViewport(0, 0, size.width, size.height);
+
+ mCaps = mScreen->mCaps;
+ MOZ_ASSERT(!mCaps.any);
+
+ return true;
+}
+
+bool
+GLContext::IsDrawingToDefaultFramebuffer()
+{
+ return Screen()->IsDrawFramebufferDefault();
+}
+
+GLuint
+CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
+ GLenum aType, const gfx::IntSize& aSize, bool linear)
+{
+ GLuint tex = 0;
+ aGL->fGenTextures(1, &tex);
+ ScopedBindTexture autoTex(aGL, tex);
+
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR
+ : LOCAL_GL_NEAREST);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR
+ : LOCAL_GL_NEAREST);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+
+ aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
+ 0,
+ aInternalFormat,
+ aSize.width, aSize.height,
+ 0,
+ aFormat,
+ aType,
+ nullptr);
+
+ return tex;
+}
+
+GLuint
+CreateTextureForOffscreen(GLContext* aGL, const GLFormats& aFormats,
+ const gfx::IntSize& aSize)
+{
+ MOZ_ASSERT(aFormats.color_texInternalFormat);
+ MOZ_ASSERT(aFormats.color_texFormat);
+ MOZ_ASSERT(aFormats.color_texType);
+
+ GLenum internalFormat = aFormats.color_texInternalFormat;
+ GLenum unpackFormat = aFormats.color_texFormat;
+ GLenum unpackType = aFormats.color_texType;
+ if (aGL->IsANGLE()) {
+ MOZ_ASSERT(internalFormat == LOCAL_GL_RGBA);
+ MOZ_ASSERT(unpackFormat == LOCAL_GL_RGBA);
+ MOZ_ASSERT(unpackType == LOCAL_GL_UNSIGNED_BYTE);
+ internalFormat = LOCAL_GL_BGRA_EXT;
+ unpackFormat = LOCAL_GL_BGRA_EXT;
+ }
+
+ return CreateTexture(aGL, internalFormat, unpackFormat, unpackType, aSize);
+}
+
+uint32_t
+GetBytesPerTexel(GLenum format, GLenum type)
+{
+ // If there is no defined format or type, we're not taking up any memory
+ if (!format || !type) {
+ return 0;
+ }
+
+ if (format == LOCAL_GL_DEPTH_COMPONENT) {
+ if (type == LOCAL_GL_UNSIGNED_SHORT)
+ return 2;
+ else if (type == LOCAL_GL_UNSIGNED_INT)
+ return 4;
+ } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+ if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+ return 4;
+ }
+
+ if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
+ uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
+ switch (format) {
+ case LOCAL_GL_ALPHA:
+ case LOCAL_GL_LUMINANCE:
+ return 1 * multiplier;
+ case LOCAL_GL_LUMINANCE_ALPHA:
+ return 2 * multiplier;
+ case LOCAL_GL_RGB:
+ return 3 * multiplier;
+ case LOCAL_GL_RGBA:
+ case LOCAL_GL_BGRA_EXT:
+ return 4 * multiplier;
+ default:
+ break;
+ }
+ } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+ type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+ type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+ {
+ return 2;
+ }
+
+ gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
+ return 0;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/GLContext.h b/system/graphics/gl/GLContext.h
new file mode 100644
index 000000000..5f0f5a4e8
--- /dev/null
+++ b/system/graphics/gl/GLContext.h
@@ -0,0 +1,3728 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXT_H_
+#define GLCONTEXT_H_
+
+#include <bitset>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <map>
+#include <queue>
+#include <stack>
+
+#ifdef DEBUG
+#include <string.h>
+#endif
+
+#ifdef GetClassName
+#undef GetClassName
+#endif
+
+// Define MOZ_GL_DEBUG unconditionally to enable GL debugging in opt
+// builds.
+#ifdef DEBUG
+#define MOZ_GL_DEBUG 1
+#endif
+
+#include "../../mfbt/RefPtr.h"
+#include "../../mfbt/UniquePtr.h"
+
+#include "GLDefs.h"
+#include "GLLibraryLoader.h"
+#include "nsISupportsImpl.h"
+#include "plstr.h"
+#include "GLContextTypes.h"
+#include "SurfaceTypes.h"
+#include "GLContextSymbols.h"
+#include "base/platform_thread.h" // for PlatformThreadId
+#include "mozilla/GenericRefCounted.h"
+#include "mozilla/WeakPtr.h"
+#include "gfx2DGlue.h"
+#include "GeckoProfiler.h"
+
+class nsIWidget;
+
+namespace android {
+ class GraphicBuffer;
+} // namespace android
+
+namespace mozilla {
+ namespace gfx {
+ class DataSourceSurface;
+ class SourceSurface;
+ } // namespace gfx
+
+ namespace gl {
+ class GLBlitHelper;
+ class GLBlitTextureImageHelper;
+ class GLContext;
+ class GLLibraryEGL;
+ class GLReadTexImageHelper;
+ class GLScreenBuffer;
+ class SharedSurface;
+ class TextureGarbageBin;
+ struct SurfaceCaps;
+ } // namespace gl
+
+ namespace layers {
+ class ColorTextureLayerProgram;
+ } // namespace layers
+} // namespace mozilla
+
+namespace mozilla {
+namespace gl {
+
+enum class GLFeature {
+ bind_buffer_offset,
+ blend_minmax,
+ clear_buffers,
+ copy_buffer,
+ depth_texture,
+ draw_buffers,
+ draw_instanced,
+ draw_range_elements,
+ element_index_uint,
+ ES2_compatibility,
+ ES3_compatibility,
+ EXT_color_buffer_float,
+ frag_color_float,
+ frag_depth,
+ framebuffer_blit,
+ framebuffer_multisample,
+ framebuffer_object,
+ framebuffer_object_EXT_OES,
+ get_integer_indexed,
+ get_integer64_indexed,
+ get_query_object_i64v,
+ get_query_object_iv,
+ get_string_indexed,
+ gpu_shader4,
+ gpu_shader5,
+ instanced_arrays,
+ instanced_non_arrays,
+ internalformat_query,
+ invalidate_framebuffer,
+ map_buffer_range,
+ occlusion_query,
+ occlusion_query_boolean,
+ occlusion_query2,
+ packed_depth_stencil,
+ prim_restart,
+ prim_restart_fixed,
+ query_counter,
+ query_objects,
+ query_time_elapsed,
+ read_buffer,
+ renderbuffer_color_float,
+ renderbuffer_color_half_float,
+ robustness,
+ sRGB_framebuffer,
+ sRGB_texture,
+ sampler_objects,
+ seamless_cube_map_opt_in,
+ shader_texture_lod,
+ split_framebuffer,
+ standard_derivatives,
+ sync,
+ texture_3D,
+ texture_3D_compressed,
+ texture_3D_copy,
+ texture_float,
+ texture_float_linear,
+ texture_half_float,
+ texture_half_float_linear,
+ texture_non_power_of_two,
+ texture_rg,
+ texture_storage,
+ texture_swizzle,
+ transform_feedback2,
+ uniform_buffer_object,
+ uniform_matrix_nonsquare,
+ vertex_array_object,
+ EnumMax
+};
+
+enum class ContextProfile : uint8_t {
+ Unknown = 0,
+ OpenGL, // only for IsAtLeast's <profile> parameter
+ OpenGLCore,
+ OpenGLCompatibility,
+ OpenGLES
+};
+
+enum class GLVendor {
+ Intel,
+ NVIDIA,
+ ATI,
+ Qualcomm,
+ Imagination,
+ Nouveau,
+ Vivante,
+ VMware,
+ ARM,
+ Other
+};
+
+enum class GLRenderer {
+ Adreno200,
+ Adreno205,
+ AdrenoTM200,
+ AdrenoTM205,
+ AdrenoTM305,
+ AdrenoTM320,
+ AdrenoTM330,
+ AdrenoTM420,
+ Mali400MP,
+ Mali450MP,
+ SGX530,
+ SGX540,
+ SGX544MP,
+ Tegra,
+ AndroidEmulator,
+ GalliumLlvmpipe,
+ IntelHD3000,
+ MicrosoftBasicRenderDriver,
+ Other
+};
+
+class GLContext
+ : public GLLibraryLoader
+ , public GenericAtomicRefCounted
+ , public SupportsWeakPtr<GLContext>
+{
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
+
+// -----------------------------------------------------------------------------
+// basic enums
+public:
+
+// -----------------------------------------------------------------------------
+// basic getters
+public:
+
+ /**
+ * Returns true if the context is using ANGLE. This should only be overridden
+ * for an ANGLE implementation.
+ */
+ virtual bool IsANGLE() const {
+ return false;
+ }
+
+ /**
+ * Returns true if the context is using WARP. This should only be overridden
+ * for an ANGLE implementation.
+ */
+ virtual bool IsWARP() const {
+ return false;
+ }
+
+ virtual void GetWSIInfo(nsCString* const out) const = 0;
+
+ /**
+ * Return true if we are running on a OpenGL core profile context
+ */
+ inline bool IsCoreProfile() const {
+ MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
+
+ return mProfile == ContextProfile::OpenGLCore;
+ }
+
+ /**
+ * Return true if we are running on a OpenGL compatibility profile context
+ * (legacy profile 2.1 on Max OS X)
+ */
+ inline bool IsCompatibilityProfile() const {
+ MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
+
+ return mProfile == ContextProfile::OpenGLCompatibility;
+ }
+
+ /**
+ * Return true if the context is a true OpenGL ES context or an ANGLE context
+ */
+ inline bool IsGLES() const {
+ MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
+
+ return mProfile == ContextProfile::OpenGLES;
+ }
+
+ static const char* GetProfileName(ContextProfile profile)
+ {
+ switch (profile)
+ {
+ case ContextProfile::OpenGL:
+ return "OpenGL";
+ case ContextProfile::OpenGLCore:
+ return "OpenGL Core";
+ case ContextProfile::OpenGLCompatibility:
+ return "OpenGL Compatibility";
+ case ContextProfile::OpenGLES:
+ return "OpenGL ES";
+ default:
+ break;
+ }
+
+ MOZ_ASSERT(profile != ContextProfile::Unknown, "unknown context profile");
+ return "OpenGL unknown profile";
+ }
+
+ /**
+ * Return true if we are running on a OpenGL core profile context
+ */
+ const char* ProfileString() const {
+ return GetProfileName(mProfile);
+ }
+
+ /**
+ * Return true if the context is compatible with given parameters
+ *
+ * IsAtLeast(ContextProfile::OpenGL, N) is exactly same as
+ * IsAtLeast(ContextProfile::OpenGLCore, N) || IsAtLeast(ContextProfile::OpenGLCompatibility, N)
+ */
+ inline bool IsAtLeast(ContextProfile profile, unsigned int version) const
+ {
+ MOZ_ASSERT(profile != ContextProfile::Unknown, "IsAtLeast: bad <profile> parameter");
+ MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
+ MOZ_ASSERT(mVersion != 0, "unknown context version");
+
+ if (version > mVersion) {
+ return false;
+ }
+
+ if (profile == ContextProfile::OpenGL) {
+ return mProfile == ContextProfile::OpenGLCore ||
+ mProfile == ContextProfile::OpenGLCompatibility;
+ }
+
+ return profile == mProfile;
+ }
+
+ /**
+ * Return the version of the context.
+ * Example :
+ * If this a OpenGL 2.1, that will return 210
+ */
+ inline uint32_t Version() const {
+ return mVersion;
+ }
+
+ const char* VersionString() const {
+ return mVersionString.get();
+ }
+
+ inline uint32_t ShadingLanguageVersion() const {
+ return mShadingLanguageVersion;
+ }
+
+ GLVendor Vendor() const {
+ return mVendor;
+ }
+
+ GLRenderer Renderer() const {
+ return mRenderer;
+ }
+
+ bool IsContextLost() const {
+ return mContextLost;
+ }
+
+ /**
+ * If this context is double-buffered, returns TRUE.
+ */
+ virtual bool IsDoubleBuffered() const {
+ return false;
+ }
+
+ virtual GLContextType GetContextType() const = 0;
+
+ virtual bool IsCurrent() = 0;
+
+ /**
+ * Get the default framebuffer for this context.
+ */
+ virtual GLuint GetDefaultFramebuffer() {
+ return 0;
+ }
+
+protected:
+ bool mIsOffscreen;
+ bool mContextLost;
+ bool mIsDestroyed = false;
+
+ /**
+ * mVersion store the OpenGL's version, multiplied by 100. For example, if
+ * the context is an OpenGL 2.1 context, mVersion value will be 210.
+ */
+ uint32_t mVersion;
+ nsCString mVersionString;
+ ContextProfile mProfile;
+
+ uint32_t mShadingLanguageVersion;
+
+ GLVendor mVendor;
+ GLRenderer mRenderer;
+
+ void SetProfileVersion(ContextProfile profile, uint32_t version) {
+ MOZ_ASSERT(!mSymbols.fBindFramebuffer,
+ "SetProfileVersion can only be called before initialization!");
+ MOZ_ASSERT(profile != ContextProfile::Unknown &&
+ profile != ContextProfile::OpenGL,
+ "Invalid `profile` for SetProfileVersion");
+ MOZ_ASSERT(version >= 100, "Invalid `version` for SetProfileVersion");
+
+ mVersion = version;
+ mProfile = profile;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Extensions management
+/**
+ * This mechanism is designed to know if an extension is supported. In the long
+ * term, we would like to only use the extension group queries XXX_* to have
+ * full compatibility with context version and profiles (especialy the core that
+ * officialy don't bring any extensions).
+ */
+public:
+
+ /**
+ * Known GL extensions that can be queried by
+ * IsExtensionSupported. The results of this are cached, and as
+ * such it's safe to use this even in performance critical code.
+ * If you add to this array, remember to add to the string names
+ * in GLContext.cpp.
+ */
+ enum GLExtensions {
+ Extension_None = 0,
+ AMD_compressed_ATC_texture,
+ ANGLE_depth_texture,
+ ANGLE_framebuffer_blit,
+ ANGLE_framebuffer_multisample,
+ ANGLE_instanced_arrays,
+ ANGLE_texture_compression_dxt3,
+ ANGLE_texture_compression_dxt5,
+ ANGLE_timer_query,
+ APPLE_client_storage,
+ APPLE_framebuffer_multisample,
+ APPLE_sync,
+ APPLE_texture_range,
+ APPLE_vertex_array_object,
+ ARB_ES2_compatibility,
+ ARB_ES3_compatibility,
+ ARB_color_buffer_float,
+ ARB_copy_buffer,
+ ARB_depth_texture,
+ ARB_draw_buffers,
+ ARB_draw_instanced,
+ ARB_framebuffer_object,
+ ARB_framebuffer_sRGB,
+ ARB_geometry_shader4,
+ ARB_gpu_shader5,
+ ARB_half_float_pixel,
+ ARB_instanced_arrays,
+ ARB_internalformat_query,
+ ARB_invalidate_subdata,
+ ARB_map_buffer_range,
+ ARB_occlusion_query2,
+ ARB_pixel_buffer_object,
+ ARB_robustness,
+ ARB_sampler_objects,
+ ARB_seamless_cube_map,
+ ARB_shader_texture_lod,
+ ARB_sync,
+ ARB_texture_compression,
+ ARB_texture_float,
+ ARB_texture_non_power_of_two,
+ ARB_texture_rectangle,
+ ARB_texture_rg,
+ ARB_texture_storage,
+ ARB_texture_swizzle,
+ ARB_timer_query,
+ ARB_transform_feedback2,
+ ARB_uniform_buffer_object,
+ ARB_vertex_array_object,
+ EXT_bgra,
+ EXT_blend_minmax,
+ EXT_color_buffer_float,
+ EXT_color_buffer_half_float,
+ EXT_copy_texture,
+ EXT_disjoint_timer_query,
+ EXT_draw_buffers,
+ EXT_draw_buffers2,
+ EXT_draw_instanced,
+ EXT_draw_range_elements,
+ EXT_frag_depth,
+ EXT_framebuffer_blit,
+ EXT_framebuffer_multisample,
+ EXT_framebuffer_object,
+ EXT_framebuffer_sRGB,
+ EXT_gpu_shader4,
+ EXT_gpu_shader5,
+ EXT_multisampled_render_to_texture,
+ EXT_occlusion_query_boolean,
+ EXT_packed_depth_stencil,
+ EXT_read_format_bgra,
+ EXT_robustness,
+ EXT_sRGB,
+ EXT_sRGB_write_control,
+ EXT_shader_texture_lod,
+ EXT_texture3D,
+ EXT_texture_compression_dxt1,
+ EXT_texture_compression_s3tc,
+ EXT_texture_filter_anisotropic,
+ EXT_texture_format_BGRA8888,
+ EXT_texture_sRGB,
+ EXT_texture_storage,
+ EXT_timer_query,
+ EXT_transform_feedback,
+ EXT_unpack_subimage,
+ IMG_read_format,
+ IMG_texture_compression_pvrtc,
+ IMG_texture_npot,
+ KHR_debug,
+ NV_draw_instanced,
+ NV_fence,
+ NV_framebuffer_blit,
+ NV_geometry_program4,
+ NV_gpu_shader5,
+ NV_half_float,
+ NV_instanced_arrays,
+ NV_primitive_restart,
+ NV_texture_barrier,
+ NV_transform_feedback,
+ NV_transform_feedback2,
+ OES_EGL_image,
+ OES_EGL_image_external,
+ OES_EGL_sync,
+ OES_compressed_ETC1_RGB8_texture,
+ OES_depth24,
+ OES_depth32,
+ OES_depth_texture,
+ OES_element_index_uint,
+ OES_framebuffer_object,
+ OES_packed_depth_stencil,
+ OES_rgb8_rgba8,
+ OES_standard_derivatives,
+ OES_stencil8,
+ OES_texture_3D,
+ OES_texture_float,
+ OES_texture_float_linear,
+ OES_texture_half_float,
+ OES_texture_half_float_linear,
+ OES_texture_npot,
+ OES_vertex_array_object,
+ Extensions_Max,
+ Extensions_End
+ };
+
+ bool IsExtensionSupported(GLExtensions aKnownExtension) const {
+ return mAvailableExtensions[aKnownExtension];
+ }
+
+protected:
+ void MarkExtensionUnsupported(GLExtensions aKnownExtension) {
+ mAvailableExtensions[aKnownExtension] = 0;
+ }
+
+ void MarkExtensionSupported(GLExtensions aKnownExtension) {
+ mAvailableExtensions[aKnownExtension] = 1;
+ }
+
+ std::bitset<Extensions_Max> mAvailableExtensions;
+
+// -----------------------------------------------------------------------------
+// Feature queries
+/*
+ * This mecahnism introduces a new way to check if a OpenGL feature is
+ * supported, regardless of whether it is supported by an extension or natively
+ * by the context version/profile
+ */
+public:
+ bool IsSupported(GLFeature feature) const {
+ return mAvailableFeatures[size_t(feature)];
+ }
+
+ static const char* GetFeatureName(GLFeature feature);
+
+private:
+ std::bitset<size_t(GLFeature::EnumMax)> mAvailableFeatures;
+
+ /**
+ * Init features regarding OpenGL extension and context version and profile
+ */
+ void InitFeatures();
+
+ /**
+ * Mark the feature and associated extensions as unsupported
+ */
+ void MarkUnsupported(GLFeature feature);
+
+ /**
+ * Is this feature supported using the core (unsuffixed) symbols?
+ */
+ bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
+
+// -----------------------------------------------------------------------------
+// Robustness handling
+private:
+ /**
+ * The derived class is expected to provide information on whether or not it
+ * supports robustness.
+ */
+ virtual bool SupportsRobustness() const = 0;
+
+public:
+// -----------------------------------------------------------------------------
+// Error handling
+ static const char* GLErrorToString(GLenum aError) {
+ switch (aError) {
+ case LOCAL_GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM";
+ case LOCAL_GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE";
+ case LOCAL_GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION";
+ case LOCAL_GL_STACK_OVERFLOW:
+ return "GL_STACK_OVERFLOW";
+ case LOCAL_GL_STACK_UNDERFLOW:
+ return "GL_STACK_UNDERFLOW";
+ case LOCAL_GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY";
+ case LOCAL_GL_TABLE_TOO_LARGE:
+ return "GL_TABLE_TOO_LARGE";
+ case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
+ return "GL_INVALID_FRAMEBUFFER_OPERATION";
+ default:
+ return "";
+ }
+ }
+
+private:
+ GLenum mTopError;
+
+ GLenum RawGetError() {
+ return mSymbols.fGetError();
+ }
+
+ GLenum RawGetErrorAndClear() {
+ GLenum err = RawGetError();
+
+ if (err)
+ while (RawGetError()) {}
+
+ return err;
+ }
+
+public:
+ GLenum FlushErrors() {
+ GLenum err = RawGetErrorAndClear();
+ if (!mTopError)
+ mTopError = err;
+ return err;
+ }
+
+ // We smash all errors together, so you never have to loop on this. We
+ // guarantee that immediately after this call, there are no errors left.
+ GLenum fGetError() {
+ FlushErrors();
+
+ GLenum err = mTopError;
+ mTopError = LOCAL_GL_NO_ERROR;
+ return err;
+ }
+
+ ////////////////////////////////////
+ // Use this safer option.
+ class LocalErrorScope;
+
+private:
+ std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
+
+public:
+ class LocalErrorScope {
+ GLContext& mGL;
+ GLenum mOldTop;
+ bool mHasBeenChecked;
+
+ public:
+ explicit LocalErrorScope(GLContext& gl)
+ : mGL(gl)
+ , mHasBeenChecked(false)
+ {
+ mGL.mLocalErrorScopeStack.push(this);
+
+ mGL.FlushErrors();
+
+ mOldTop = mGL.mTopError;
+ mGL.mTopError = LOCAL_GL_NO_ERROR;
+ }
+
+ GLenum GetError() {
+ MOZ_ASSERT(!mHasBeenChecked);
+ mHasBeenChecked = true;
+
+ const GLenum ret = mGL.fGetError();
+
+ while (mGL.fGetError()) {}
+
+ return ret;
+ }
+
+ ~LocalErrorScope() {
+ MOZ_ASSERT(mHasBeenChecked);
+
+ MOZ_ASSERT(mGL.fGetError() == LOCAL_GL_NO_ERROR);
+
+ MOZ_ASSERT(mGL.mLocalErrorScopeStack.top() == this);
+ mGL.mLocalErrorScopeStack.pop();
+
+ mGL.mTopError = mOldTop;
+ }
+ };
+
+ bool GetPotentialInteger(GLenum pname, GLint* param) {
+ LocalErrorScope localError(*this);
+
+ fGetIntegerv(pname, param);
+
+ GLenum err = localError.GetError();
+ MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM);
+ return err == LOCAL_GL_NO_ERROR;
+ }
+
+private:
+ static void GLAPIENTRY StaticDebugCallback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const GLvoid* userParam);
+ void DebugCallback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message);
+
+
+// -----------------------------------------------------------------------------
+// MOZ_GL_DEBUG implementation
+private:
+
+#undef BEFORE_GL_CALL
+#undef AFTER_GL_CALL
+
+#ifdef MOZ_GL_DEBUG
+
+#ifndef MOZ_FUNCTION_NAME
+# ifdef __GNUC__
+# define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
+# elif defined(_MSC_VER)
+# define MOZ_FUNCTION_NAME __FUNCTION__
+# else
+# define MOZ_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
+# endif
+#endif
+
+ void BeforeGLCall(const char* funcName) {
+ MOZ_ASSERT(IsCurrent());
+
+ if (mDebugFlags) {
+ FlushErrors();
+
+ if (mDebugFlags & DebugFlagTrace) {
+ printf_stderr("[gl:%p] > %s\n", this, funcName);
+ }
+
+ GLContext* tlsContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
+ if (this != tlsContext) {
+ printf_stderr("Fatal: %s called on non-current context %p. The"
+ " current context for this thread is %p.\n",
+ funcName, this, tlsContext);
+ MOZ_CRASH("GFX: GLContext is not current.");
+ }
+ }
+ }
+
+ void AfterGLCall(const char* funcName) {
+ if (mDebugFlags) {
+ // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
+ // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
+ // tend to be meaningless
+ mSymbols.fFinish();
+ GLenum err = FlushErrors();
+
+ if (mDebugFlags & DebugFlagTrace) {
+ printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
+ GLErrorToString(err), err);
+ }
+
+ if (err != LOCAL_GL_NO_ERROR &&
+ !mLocalErrorScopeStack.size())
+ {
+ printf_stderr("[gl:%p] %s: Generated unexpected %s error."
+ " (0x%04x)\n", this, funcName,
+ GLErrorToString(err), err);
+
+ if (mDebugFlags & DebugFlagAbortOnError) {
+ MOZ_CRASH("Unexpected error with MOZ_GL_DEBUG_ABORT_ON_ERROR. (Run"
+ " with MOZ_GL_DEBUG_ABORT_ON_ERROR=0 to disable)");
+ }
+ }
+ }
+ }
+
+ GLContext* TrackingContext()
+ {
+ GLContext* tip = this;
+ while (tip->mSharedContext)
+ tip = tip->mSharedContext;
+ return tip;
+ }
+
+ static void AssertNotPassingStackBufferToTheGL(const void* ptr);
+
+#define BEFORE_GL_CALL \
+ do { \
+ BeforeGLCall(MOZ_FUNCTION_NAME); \
+ } while (0)
+
+#define AFTER_GL_CALL \
+ do { \
+ AfterGLCall(MOZ_FUNCTION_NAME); \
+ } while (0)
+
+#define TRACKING_CONTEXT(a) \
+ do { \
+ TrackingContext()->a; \
+ } while (0)
+
+#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) AssertNotPassingStackBufferToTheGL(ptr)
+
+#else // ifdef MOZ_GL_DEBUG
+
+#define BEFORE_GL_CALL do { } while (0)
+#define AFTER_GL_CALL do { } while (0)
+#define TRACKING_CONTEXT(a) do {} while (0)
+#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) do {} while (0)
+
+#endif // ifdef MOZ_GL_DEBUG
+
+#define ASSERT_SYMBOL_PRESENT(func) \
+ do {\
+ MOZ_ASSERT(strstr(MOZ_FUNCTION_NAME, #func) != nullptr, "Mismatched symbol check.");\
+ if (MOZ_UNLIKELY(!mSymbols.func)) {\
+ printf_stderr("RUNTIME ASSERT: Uninitialized GL function: %s\n", #func);\
+ MOZ_CRASH("GFX: Uninitialized GL function");\
+ }\
+ } while (0)
+
+ // Do whatever setup is necessary to draw to our offscreen FBO, if it's
+ // bound.
+ void BeforeGLDrawCall() { }
+
+ // Do whatever tear-down is necessary after drawing to our offscreen FBO,
+ // if it's bound.
+ void AfterGLDrawCall();
+
+ // Do whatever setup is necessary to read from our offscreen FBO, if it's
+ // bound.
+ void BeforeGLReadCall();
+
+ // Do whatever tear-down is necessary after reading from our offscreen FBO,
+ // if it's bound.
+ void AfterGLReadCall() { }
+
+
+// -----------------------------------------------------------------------------
+// GL official entry points
+public:
+
+ void fActiveTexture(GLenum texture) {
+ BEFORE_GL_CALL;
+ mSymbols.fActiveTexture(texture);
+ AFTER_GL_CALL;
+ }
+
+ void fAttachShader(GLuint program, GLuint shader) {
+ BEFORE_GL_CALL;
+ mSymbols.fAttachShader(program, shader);
+ AFTER_GL_CALL;
+ }
+
+ void fBeginQuery(GLenum target, GLuint id) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBeginQuery);
+ mSymbols.fBeginQuery(target, id);
+ AFTER_GL_CALL;
+ }
+
+ void fBindAttribLocation(GLuint program, GLuint index, const GLchar* name) {
+ BEFORE_GL_CALL;
+ mSymbols.fBindAttribLocation(program, index, name);
+ AFTER_GL_CALL;
+ }
+
+ void fBindBuffer(GLenum target, GLuint buffer) {
+ BEFORE_GL_CALL;
+ mSymbols.fBindBuffer(target, buffer);
+ AFTER_GL_CALL;
+ }
+
+ void fBindFramebuffer(GLenum target, GLuint framebuffer);
+
+ void fInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateFramebuffer);
+ mSymbols.fInvalidateFramebuffer(target, numAttachments, attachments);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateSubFramebuffer);
+ mSymbols.fInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fBindTexture(GLenum target, GLuint texture) {
+ BEFORE_GL_CALL;
+ mSymbols.fBindTexture(target, texture);
+ AFTER_GL_CALL;
+ }
+
+ void fBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+ BEFORE_GL_CALL;
+ mSymbols.fBlendColor(red, green, blue, alpha);
+ AFTER_GL_CALL;
+ }
+
+ void fBlendEquation(GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fBlendEquation(mode);
+ AFTER_GL_CALL;
+ }
+
+ void fBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
+ BEFORE_GL_CALL;
+ mSymbols.fBlendEquationSeparate(modeRGB, modeAlpha);
+ AFTER_GL_CALL;
+ }
+
+ void fBlendFunc(GLenum sfactor, GLenum dfactor) {
+ BEFORE_GL_CALL;
+ mSymbols.fBlendFunc(sfactor, dfactor);
+ AFTER_GL_CALL;
+ }
+
+ void fBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {
+ BEFORE_GL_CALL;
+ mSymbols.fBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
+ AFTER_GL_CALL;
+ }
+
+private:
+ void raw_fBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(data);
+ BEFORE_GL_CALL;
+ mSymbols.fBufferData(target, size, data, usage);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+public:
+ void fBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
+ raw_fBufferData(target, size, data, usage);
+
+ // bug 744888
+ if (WorkAroundDriverBugs() &&
+ !data &&
+ Vendor() == GLVendor::NVIDIA)
+ {
+ UniquePtr<char[]> buf = MakeUnique<char[]>(1);
+ buf[0] = 0;
+ fBufferSubData(target, size-1, 1, buf.get());
+ }
+ }
+
+ void fBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(data);
+ BEFORE_GL_CALL;
+ mSymbols.fBufferSubData(target, offset, size, data);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+private:
+ void raw_fClear(GLbitfield mask) {
+ BEFORE_GL_CALL;
+ mSymbols.fClear(mask);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fClear(GLbitfield mask) {
+ BeforeGLDrawCall();
+ raw_fClear(mask);
+ AfterGLDrawCall();
+ }
+
+ void fClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ mSymbols.fClearBufferfi(buffer, drawbuffer, depth, stencil);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ mSymbols.fClearBufferfv(buffer, drawbuffer, value);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ mSymbols.fClearBufferiv(buffer, drawbuffer, value);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
+ BeforeGLDrawCall();
+ BEFORE_GL_CALL;
+ mSymbols.fClearBufferuiv(buffer, drawbuffer, value);
+ AFTER_GL_CALL;
+ AfterGLDrawCall();
+ }
+
+ void fClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ BEFORE_GL_CALL;
+ mSymbols.fClearColor(r, g, b, a);
+ AFTER_GL_CALL;
+ }
+
+ void fClearStencil(GLint s) {
+ BEFORE_GL_CALL;
+ mSymbols.fClearStencil(s);
+ AFTER_GL_CALL;
+ }
+
+ void fClientActiveTexture(GLenum texture) {
+ BEFORE_GL_CALL;
+ mSymbols.fClientActiveTexture(texture);
+ AFTER_GL_CALL;
+ }
+
+ void fColorMask(realGLboolean red, realGLboolean green, realGLboolean blue, realGLboolean alpha) {
+ BEFORE_GL_CALL;
+ mSymbols.fColorMask(red, green, blue, alpha);
+ AFTER_GL_CALL;
+ }
+
+ void fCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* pixels) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
+ BEFORE_GL_CALL;
+ mSymbols.fCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, pixels);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+ void fCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* pixels) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
+ BEFORE_GL_CALL;
+ mSymbols.fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, pixels);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+ void fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border);
+
+ void fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ BeforeGLReadCall();
+ raw_fCopyTexSubImage2D(target, level, xoffset, yoffset,
+ x, y, width, height);
+ AfterGLReadCall();
+ }
+
+ void fCullFace(GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fCullFace(mode);
+ AFTER_GL_CALL;
+ }
+
+ void fDebugMessageCallback(GLDEBUGPROC callback, const GLvoid* userParam) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDebugMessageCallback);
+ mSymbols.fDebugMessageCallback(callback, userParam);
+ AFTER_GL_CALL;
+ }
+
+ void fDebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, realGLboolean enabled) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDebugMessageControl);
+ mSymbols.fDebugMessageControl(source, type, severity, count, ids, enabled);
+ AFTER_GL_CALL;
+ }
+
+ void fDebugMessageInsert(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDebugMessageInsert);
+ mSymbols.fDebugMessageInsert(source, type, id, severity, length, buf);
+ AFTER_GL_CALL;
+ }
+
+ void fDetachShader(GLuint program, GLuint shader) {
+ BEFORE_GL_CALL;
+ mSymbols.fDetachShader(program, shader);
+ AFTER_GL_CALL;
+ }
+
+ void fDepthFunc(GLenum func) {
+ BEFORE_GL_CALL;
+ mSymbols.fDepthFunc(func);
+ AFTER_GL_CALL;
+ }
+
+ void fDepthMask(realGLboolean flag) {
+ BEFORE_GL_CALL;
+ mSymbols.fDepthMask(flag);
+ AFTER_GL_CALL;
+ }
+
+ void fDisable(GLenum capability) {
+ BEFORE_GL_CALL;
+ mSymbols.fDisable(capability);
+ AFTER_GL_CALL;
+ }
+
+ void fDisableClientState(GLenum capability) {
+ BEFORE_GL_CALL;
+ mSymbols.fDisableClientState(capability);
+ AFTER_GL_CALL;
+ }
+
+ void fDisableVertexAttribArray(GLuint index) {
+ BEFORE_GL_CALL;
+ mSymbols.fDisableVertexAttribArray(index);
+ AFTER_GL_CALL;
+ }
+
+ void fDrawBuffer(GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fDrawBuffer(mode);
+ AFTER_GL_CALL;
+ }
+
+private:
+ void raw_fDrawArrays(GLenum mode, GLint first, GLsizei count) {
+ BEFORE_GL_CALL;
+ mSymbols.fDrawArrays(mode, first, count);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) {
+ BEFORE_GL_CALL;
+ mSymbols.fDrawElements(mode, count, type, indices);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fDrawArrays(GLenum mode, GLint first, GLsizei count) {
+ BeforeGLDrawCall();
+ raw_fDrawArrays(mode, first, count);
+ AfterGLDrawCall();
+ }
+
+ void fDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) {
+ BeforeGLDrawCall();
+ raw_fDrawElements(mode, count, type, indices);
+ AfterGLDrawCall();
+ }
+
+ void fEnable(GLenum capability) {
+ BEFORE_GL_CALL;
+ mSymbols.fEnable(capability);
+ AFTER_GL_CALL;
+ }
+
+ void fEnableClientState(GLenum capability) {
+ BEFORE_GL_CALL;
+ mSymbols.fEnableClientState(capability);
+ AFTER_GL_CALL;
+ }
+
+ void fEnableVertexAttribArray(GLuint index) {
+ BEFORE_GL_CALL;
+ mSymbols.fEnableVertexAttribArray(index);
+ AFTER_GL_CALL;
+ }
+
+ void fEndQuery(GLenum target) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fEndQuery);
+ mSymbols.fEndQuery(target);
+ AFTER_GL_CALL;
+ }
+
+ void fFinish() {
+ BEFORE_GL_CALL;
+ mSymbols.fFinish();
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = false;
+ }
+
+ void fFlush() {
+ BEFORE_GL_CALL;
+ mSymbols.fFlush();
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = false;
+ }
+
+ void fFrontFace(GLenum face) {
+ BEFORE_GL_CALL;
+ mSymbols.fFrontFace(face);
+ AFTER_GL_CALL;
+ }
+
+ void fGetActiveAttrib(GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetActiveAttrib(program, index, maxLength, length, size, type, name);
+ AFTER_GL_CALL;
+ }
+
+ void fGetActiveUniform(GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetActiveUniform(program, index, maxLength, length, size, type, name);
+ AFTER_GL_CALL;
+ }
+
+ void fGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetAttachedShaders(program, maxCount, count, shaders);
+ AFTER_GL_CALL;
+ }
+
+ GLint fGetAttribLocation(GLuint program, const GLchar* name) {
+ BEFORE_GL_CALL;
+ GLint retval = mSymbols.fGetAttribLocation(program, name);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+private:
+ void raw_fGetIntegerv(GLenum pname, GLint* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetIntegerv(pname, params);
+ AFTER_GL_CALL;
+ }
+
+public:
+
+ void fGetIntegerv(GLenum pname, GLint* params);
+
+ void GetUIntegerv(GLenum pname, GLuint* params) {
+ fGetIntegerv(pname, reinterpret_cast<GLint*>(params));
+ }
+
+ template<typename T>
+ T GetIntAs(GLenum pname) {
+ static_assert(sizeof(T) == sizeof(GLint), "Invalid T.");
+ T ret = 0;
+ fGetIntegerv(pname, (GLint*)&ret);
+ return ret;
+ }
+
+ void fGetFloatv(GLenum pname, GLfloat* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetFloatv(pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetBooleanv(GLenum pname, realGLboolean* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetBooleanv(pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetBufferParameteriv(target, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ GLuint fGetDebugMessageLog(GLuint count, GLsizei bufsize, GLenum* sources, GLenum* types, GLuint* ids, GLenum* severities, GLsizei* lengths, GLchar* messageLog) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetDebugMessageLog);
+ GLuint ret = mSymbols.fGetDebugMessageLog(count, bufsize, sources, types, ids, severities, lengths, messageLog);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fGetPointerv(GLenum pname, GLvoid** params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetPointerv);
+ mSymbols.fGetPointerv(pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetObjectLabel(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei* length, GLchar* label) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetObjectLabel);
+ mSymbols.fGetObjectLabel(identifier, name, bufSize, length, label);
+ AFTER_GL_CALL;
+ }
+
+ void fGetObjectPtrLabel(const GLvoid* ptr, GLsizei bufSize, GLsizei* length, GLchar* label) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetObjectPtrLabel);
+ mSymbols.fGetObjectPtrLabel(ptr, bufSize, length, label);
+ AFTER_GL_CALL;
+ }
+
+ void fGenerateMipmap(GLenum target) {
+ BEFORE_GL_CALL;
+ mSymbols.fGenerateMipmap(target);
+ AFTER_GL_CALL;
+ }
+
+ void fGetProgramiv(GLuint program, GLenum pname, GLint* param) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetProgramiv(program, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetProgramInfoLog(program, bufSize, length, infoLog);
+ AFTER_GL_CALL;
+ }
+
+ void fTexParameteri(GLenum target, GLenum pname, GLint param) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexParameteri(target, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fTexParameteriv(GLenum target, GLenum pname, const GLint* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexParameteriv(target, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fTexParameterf(GLenum target, GLenum pname, GLfloat param) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexParameterf(target, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ const GLubyte* fGetString(GLenum name) {
+ BEFORE_GL_CALL;
+ const GLubyte* result = mSymbols.fGetString(name);
+ AFTER_GL_CALL;
+ return result;
+ }
+
+ void fGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* img) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetTexImage);
+ mSymbols.fGetTexImage(target, level, format, type, img);
+ AFTER_GL_CALL;
+ }
+
+ void fGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetTexLevelParameteriv);
+ mSymbols.fGetTexLevelParameteriv(target, level, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetTexParameterfv(target, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetTexParameteriv(GLenum target, GLenum pname, GLint* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetTexParameteriv(target, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetUniformfv(GLuint program, GLint location, GLfloat* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetUniformfv(program, location, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetUniformiv(GLuint program, GLint location, GLint* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetUniformiv(program, location, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetUniformuiv(GLuint program, GLint location, GLuint* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetUniformuiv);
+ mSymbols.fGetUniformuiv(program, location, params);
+ AFTER_GL_CALL;
+ }
+
+ GLint fGetUniformLocation (GLint programObj, const GLchar* name) {
+ BEFORE_GL_CALL;
+ GLint retval = mSymbols.fGetUniformLocation(programObj, name);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ void fGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* retval) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetVertexAttribfv(index, pname, retval);
+ AFTER_GL_CALL;
+ }
+
+ void fGetVertexAttribiv(GLuint index, GLenum pname, GLint* retval) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetVertexAttribiv(index, pname, retval);
+ AFTER_GL_CALL;
+ }
+
+ void fGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** retval) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetVertexAttribPointerv(index, pname, retval);
+ AFTER_GL_CALL;
+ }
+
+ void fHint(GLenum target, GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fHint(target, mode);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsBuffer(GLuint buffer) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsBuffer(buffer);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ realGLboolean fIsEnabled(GLenum capability) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsEnabled(capability);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ realGLboolean fIsProgram(GLuint program) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsProgram(program);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ realGLboolean fIsShader(GLuint shader) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsShader(shader);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ realGLboolean fIsTexture(GLuint texture) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsTexture(texture);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ void fLineWidth(GLfloat width) {
+ BEFORE_GL_CALL;
+ mSymbols.fLineWidth(width);
+ AFTER_GL_CALL;
+ }
+
+ void fLinkProgram(GLuint program) {
+ BEFORE_GL_CALL;
+ mSymbols.fLinkProgram(program);
+ AFTER_GL_CALL;
+ }
+
+ void fObjectLabel(GLenum identifier, GLuint name, GLsizei length, const GLchar* label) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fObjectLabel);
+ mSymbols.fObjectLabel(identifier, name, length, label);
+ AFTER_GL_CALL;
+ }
+
+ void fObjectPtrLabel(const GLvoid* ptr, GLsizei length, const GLchar* label) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fObjectPtrLabel);
+ mSymbols.fObjectPtrLabel(ptr, length, label);
+ AFTER_GL_CALL;
+ }
+
+ void fLoadIdentity() {
+ BEFORE_GL_CALL;
+ mSymbols.fLoadIdentity();
+ AFTER_GL_CALL;
+ }
+
+ void fLoadMatrixf(const GLfloat* matrix) {
+ BEFORE_GL_CALL;
+ mSymbols.fLoadMatrixf(matrix);
+ AFTER_GL_CALL;
+ }
+
+ void fMatrixMode(GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fMatrixMode(mode);
+ AFTER_GL_CALL;
+ }
+
+ void fPixelStorei(GLenum pname, GLint param) {
+ BEFORE_GL_CALL;
+ mSymbols.fPixelStorei(pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fTextureRangeAPPLE(GLenum target, GLsizei length, GLvoid* pointer) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pointer);
+ BEFORE_GL_CALL;
+ mSymbols.fTextureRangeAPPLE(target, length, pointer);
+ AFTER_GL_CALL;
+ }
+
+ void fPointParameterf(GLenum pname, GLfloat param) {
+ BEFORE_GL_CALL;
+ mSymbols.fPointParameterf(pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fPolygonOffset(GLfloat factor, GLfloat bias) {
+ BEFORE_GL_CALL;
+ mSymbols.fPolygonOffset(factor, bias);
+ AFTER_GL_CALL;
+ }
+
+ void fPopDebugGroup() {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fPopDebugGroup);
+ mSymbols.fPopDebugGroup();
+ AFTER_GL_CALL;
+ }
+
+ void fPushDebugGroup(GLenum source, GLuint id, GLsizei length, const GLchar* message) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fPushDebugGroup);
+ mSymbols.fPushDebugGroup(source, id, length, message);
+ AFTER_GL_CALL;
+ }
+
+ void fReadBuffer(GLenum mode) {
+ BEFORE_GL_CALL;
+ mSymbols.fReadBuffer(mode);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
+ BEFORE_GL_CALL;
+ mSymbols.fReadPixels(x, y, width, height, format, type, pixels);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+ void fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+ GLenum type, GLvoid* pixels);
+
+public:
+ void fSampleCoverage(GLclampf value, realGLboolean invert) {
+ BEFORE_GL_CALL;
+ mSymbols.fSampleCoverage(value, invert);
+ AFTER_GL_CALL;
+ }
+
+ void fScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
+ if (mScissorRect[0] == x &&
+ mScissorRect[1] == y &&
+ mScissorRect[2] == width &&
+ mScissorRect[3] == height)
+ {
+ return;
+ }
+ mScissorRect[0] = x;
+ mScissorRect[1] = y;
+ mScissorRect[2] = width;
+ mScissorRect[3] = height;
+ BEFORE_GL_CALL;
+ mSymbols.fScissor(x, y, width, height);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilFunc(GLenum func, GLint reference, GLuint mask) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilFunc(func, reference, mask);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilFuncSeparate(GLenum frontfunc, GLenum backfunc, GLint reference, GLuint mask) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilFuncSeparate(frontfunc, backfunc, reference, mask);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilMask(GLuint mask) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilMask(mask);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilMaskSeparate(GLenum face, GLuint mask) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilMaskSeparate(face, mask);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilOp(fail, zfail, zpass);
+ AFTER_GL_CALL;
+ }
+
+ void fStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
+ BEFORE_GL_CALL;
+ mSymbols.fStencilOpSeparate(face, sfail, dpfail, dppass);
+ AFTER_GL_CALL;
+ }
+
+ void fTexGeni(GLenum coord, GLenum pname, GLint param) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexGeni(coord, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fTexGenf(GLenum coord, GLenum pname, GLfloat param) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexGenf(coord, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fTexGenfv(GLenum coord, GLenum pname, const GLfloat* params) {
+ BEFORE_GL_CALL;
+ mSymbols.fTexGenfv(coord, pname, params);
+ AFTER_GL_CALL;
+ }
+
+private:
+ void raw_fTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
+ BEFORE_GL_CALL;
+ mSymbols.fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+public:
+ void fTexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum format, GLenum type, const GLvoid* pixels);
+
+ void fTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) {
+ ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
+ BEFORE_GL_CALL;
+ mSymbols.fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+ void fUniform1f(GLint location, GLfloat v0) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform1f(location, v0);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform1fv(GLint location, GLsizei count, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform1fv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform1i(GLint location, GLint v0) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform1i(location, v0);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform1iv(GLint location, GLsizei count, const GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform1iv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2f(GLint location, GLfloat v0, GLfloat v1) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform2f(location, v0, v1);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2fv(GLint location, GLsizei count, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform2fv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2i(GLint location, GLint v0, GLint v1) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform2i(location, v0, v1);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2iv(GLint location, GLsizei count, const GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform2iv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform3f(location, v0, v1, v2);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3fv(GLint location, GLsizei count, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform3fv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform3i(location, v0, v1, v2);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3iv(GLint location, GLsizei count, const GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform3iv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform4f(location, v0, v1, v2, v3);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4fv(GLint location, GLsizei count, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform4fv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform4i(location, v0, v1, v2, v3);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4iv(GLint location, GLsizei count, const GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniform4iv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix2fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniformMatrix2fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix2x3fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix2x3fv);
+ mSymbols.fUniformMatrix2x3fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix2x4fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix2x4fv);
+ mSymbols.fUniformMatrix2x4fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix3fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniformMatrix3fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix3x2fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix3x2fv);
+ mSymbols.fUniformMatrix3x2fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix3x4fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix3x4fv);
+ mSymbols.fUniformMatrix3x4fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix4fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fUniformMatrix4fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix4x2fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix4x2fv);
+ mSymbols.fUniformMatrix4x2fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformMatrix4x3fv(GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniformMatrix4x3fv);
+ mSymbols.fUniformMatrix4x3fv(location, count, transpose, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUseProgram(GLuint program) {
+ BEFORE_GL_CALL;
+ mSymbols.fUseProgram(program);
+ AFTER_GL_CALL;
+ }
+
+ void fValidateProgram(GLuint program) {
+ BEFORE_GL_CALL;
+ mSymbols.fValidateProgram(program);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribPointer(GLuint index, GLint size, GLenum type, realGLboolean normalized, GLsizei stride, const GLvoid* pointer) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib1f(GLuint index, GLfloat x) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib1f(index, x);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib2f(index, x, y);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib3f(index, x, y, z);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib4f(index, x, y, z, w);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib1fv(GLuint index, const GLfloat* v) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib1fv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib2fv(GLuint index, const GLfloat* v) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib2fv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib3fv(GLuint index, const GLfloat* v) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib3fv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttrib4fv(GLuint index, const GLfloat* v) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexAttrib4fv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) {
+ BEFORE_GL_CALL;
+ mSymbols.fVertexPointer(size, type, stride, pointer);
+ AFTER_GL_CALL;
+ }
+
+ void fCompileShader(GLuint shader) {
+ BEFORE_GL_CALL;
+ mSymbols.fCompileShader(shader);
+ AFTER_GL_CALL;
+ }
+
+private:
+
+ friend class SharedSurface_IOSurface;
+
+ void raw_fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+ {
+ BEFORE_GL_CALL;
+ mSymbols.fCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+ {
+ BEFORE_GL_CALL;
+ mSymbols.fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fGetShaderiv(GLuint shader, GLenum pname, GLint* param) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetShaderiv(shader, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetShaderInfoLog(shader, bufSize, length, infoLog);
+ AFTER_GL_CALL;
+ }
+
+private:
+ void raw_fGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+ MOZ_ASSERT(IsGLES());
+
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetShaderPrecisionFormat);
+ mSymbols.fGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+ if (IsGLES()) {
+ raw_fGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+ } else {
+ // Fall back to automatic values because almost all desktop hardware supports the OpenGL standard precisions.
+ GetShaderPrecisionFormatNonES2(shadertype, precisiontype, range, precision);
+ }
+ }
+
+ void fGetShaderSource(GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetShaderSource(obj, maxLength, length, source);
+ AFTER_GL_CALL;
+ }
+
+ void fShaderSource(GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths) {
+ BEFORE_GL_CALL;
+ mSymbols.fShaderSource(shader, count, strings, lengths);
+ AFTER_GL_CALL;
+ }
+
+private:
+ friend class SharedSurface;
+
+ void raw_fBindFramebuffer(GLenum target, GLuint framebuffer) {
+ BEFORE_GL_CALL;
+ mSymbols.fBindFramebuffer(target, framebuffer);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fBindRenderbuffer(GLenum target, GLuint renderbuffer) {
+ BEFORE_GL_CALL;
+ mSymbols.fBindRenderbuffer(target, renderbuffer);
+ AFTER_GL_CALL;
+ }
+
+ GLenum fCheckFramebufferStatus(GLenum target) {
+ BEFORE_GL_CALL;
+ GLenum retval = mSymbols.fCheckFramebufferStatus(target);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ void fFramebufferRenderbuffer(GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbuffer) {
+ BEFORE_GL_CALL;
+ mSymbols.fFramebufferRenderbuffer(target, attachmentPoint, renderbufferTarget, renderbuffer);
+ AFTER_GL_CALL;
+ }
+
+ void fFramebufferTexture2D(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint texture, GLint level) {
+ BEFORE_GL_CALL;
+ mSymbols.fFramebufferTexture2D(target, attachmentPoint, textureTarget, texture, level);
+ AFTER_GL_CALL;
+ if (mNeedsCheckAfterAttachTextureToFb) {
+ fCheckFramebufferStatus(target);
+ }
+ }
+
+ void fFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fFramebufferTextureLayer);
+ mSymbols.fFramebufferTextureLayer(target, attachment, texture, level, layer);
+ AFTER_GL_CALL;
+ }
+
+ void fGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetFramebufferAttachmentParameteriv(target, attachment, pname, value);
+ AFTER_GL_CALL;
+ }
+
+ void fGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* value) {
+ BEFORE_GL_CALL;
+ mSymbols.fGetRenderbufferParameteriv(target, pname, value);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsFramebuffer (GLuint framebuffer) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsFramebuffer(framebuffer);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+public:
+ realGLboolean fIsRenderbuffer (GLuint renderbuffer) {
+ BEFORE_GL_CALL;
+ realGLboolean retval = mSymbols.fIsRenderbuffer(renderbuffer);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+ void fRenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height) {
+ BEFORE_GL_CALL;
+ mSymbols.fRenderbufferStorage(target, internalFormat, width, height);
+ AFTER_GL_CALL;
+ }
+
+private:
+ void raw_fDepthRange(GLclampf a, GLclampf b) {
+ MOZ_ASSERT(!IsGLES());
+
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDepthRange);
+ mSymbols.fDepthRange(a, b);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDepthRangef(GLclampf a, GLclampf b) {
+ MOZ_ASSERT(IsGLES());
+
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDepthRangef);
+ mSymbols.fDepthRangef(a, b);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fClearDepth(GLclampf v) {
+ MOZ_ASSERT(!IsGLES());
+
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fClearDepth);
+ mSymbols.fClearDepth(v);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fClearDepthf(GLclampf v) {
+ MOZ_ASSERT(IsGLES());
+
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fClearDepthf);
+ mSymbols.fClearDepthf(v);
+ AFTER_GL_CALL;
+ }
+
+public:
+ void fDepthRange(GLclampf a, GLclampf b) {
+ if (IsGLES()) {
+ raw_fDepthRangef(a, b);
+ } else {
+ raw_fDepthRange(a, b);
+ }
+ }
+
+ void fClearDepth(GLclampf v) {
+ if (IsGLES()) {
+ raw_fClearDepthf(v);
+ } else {
+ raw_fClearDepth(v);
+ }
+ }
+
+ void* fMapBuffer(GLenum target, GLenum access) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fMapBuffer);
+ void* ret = mSymbols.fMapBuffer(target, access);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ realGLboolean fUnmapBuffer(GLenum target) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUnmapBuffer);
+ realGLboolean ret = mSymbols.fUnmapBuffer(target);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+
+private:
+ GLuint raw_fCreateProgram() {
+ BEFORE_GL_CALL;
+ GLuint ret = mSymbols.fCreateProgram();
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ GLuint raw_fCreateShader(GLenum t) {
+ BEFORE_GL_CALL;
+ GLuint ret = mSymbols.fCreateShader(t);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void raw_fGenBuffers(GLsizei n, GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fGenBuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fGenFramebuffers(GLsizei n, GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fGenFramebuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fGenRenderbuffers(GLsizei n, GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fGenRenderbuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fGenTextures(GLsizei n, GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fGenTextures(n, names);
+ AFTER_GL_CALL;
+ }
+
+public:
+ GLuint fCreateProgram() {
+ GLuint ret = raw_fCreateProgram();
+ TRACKING_CONTEXT(CreatedProgram(this, ret));
+ return ret;
+ }
+
+ GLuint fCreateShader(GLenum t) {
+ GLuint ret = raw_fCreateShader(t);
+ TRACKING_CONTEXT(CreatedShader(this, ret));
+ return ret;
+ }
+
+ void fGenBuffers(GLsizei n, GLuint* names) {
+ raw_fGenBuffers(n, names);
+ TRACKING_CONTEXT(CreatedBuffers(this, n, names));
+ }
+
+ void fGenFramebuffers(GLsizei n, GLuint* names) {
+ raw_fGenFramebuffers(n, names);
+ TRACKING_CONTEXT(CreatedFramebuffers(this, n, names));
+ }
+
+ void fGenRenderbuffers(GLsizei n, GLuint* names) {
+ raw_fGenRenderbuffers(n, names);
+ TRACKING_CONTEXT(CreatedRenderbuffers(this, n, names));
+ }
+
+ void fGenTextures(GLsizei n, GLuint* names) {
+ raw_fGenTextures(n, names);
+ TRACKING_CONTEXT(CreatedTextures(this, n, names));
+ }
+
+private:
+ void raw_fDeleteProgram(GLuint program) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteProgram(program);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDeleteShader(GLuint shader) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteShader(shader);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDeleteBuffers(GLsizei n, const GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteBuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDeleteFramebuffers(GLsizei n, const GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteFramebuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDeleteRenderbuffers(GLsizei n, const GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteRenderbuffers(n, names);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDeleteTextures(GLsizei n, const GLuint* names) {
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteTextures(n, names);
+ AFTER_GL_CALL;
+ }
+
+public:
+
+ void fDeleteProgram(GLuint program) {
+ raw_fDeleteProgram(program);
+ TRACKING_CONTEXT(DeletedProgram(this, program));
+ }
+
+ void fDeleteShader(GLuint shader) {
+ raw_fDeleteShader(shader);
+ TRACKING_CONTEXT(DeletedShader(this, shader));
+ }
+
+ void fDeleteBuffers(GLsizei n, const GLuint* names) {
+ raw_fDeleteBuffers(n, names);
+ TRACKING_CONTEXT(DeletedBuffers(this, n, names));
+ }
+
+ void fDeleteFramebuffers(GLsizei n, const GLuint* names);
+
+ void fDeleteRenderbuffers(GLsizei n, const GLuint* names) {
+ raw_fDeleteRenderbuffers(n, names);
+ TRACKING_CONTEXT(DeletedRenderbuffers(this, n, names));
+ }
+
+ void fDeleteTextures(GLsizei n, const GLuint* names) {
+ raw_fDeleteTextures(n, names);
+ TRACKING_CONTEXT(DeletedTextures(this, n, names));
+ }
+
+ GLenum fGetGraphicsResetStatus() {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetGraphicsResetStatus);
+ GLenum ret = mSymbols.fGetGraphicsResetStatus();
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Extension ARB_sync (GL)
+public:
+ GLsync fFenceSync(GLenum condition, GLbitfield flags) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fFenceSync);
+ GLsync ret = mSymbols.fFenceSync(condition, flags);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ realGLboolean fIsSync(GLsync sync) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fIsSync);
+ realGLboolean ret = mSymbols.fIsSync(sync);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fDeleteSync(GLsync sync) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDeleteSync);
+ mSymbols.fDeleteSync(sync);
+ AFTER_GL_CALL;
+ }
+
+ GLenum fClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fClientWaitSync);
+ GLenum ret = mSymbols.fClientWaitSync(sync, flags, timeout);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fWaitSync);
+ mSymbols.fWaitSync(sync, flags, timeout);
+ AFTER_GL_CALL;
+ }
+
+ void fGetInteger64v(GLenum pname, GLint64* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetInteger64v);
+ mSymbols.fGetInteger64v(pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetSynciv);
+ mSymbols.fGetSynciv(sync, pname, bufSize, length, values);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Extension OES_EGL_image (GLES)
+public:
+ void fEGLImageTargetTexture2D(GLenum target, GLeglImage image) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fEGLImageTargetTexture2D);
+ mSymbols.fEGLImageTargetTexture2D(target, image);
+ AFTER_GL_CALL;
+ mHeavyGLCallsSinceLastFlush = true;
+ }
+
+ void fEGLImageTargetRenderbufferStorage(GLenum target, GLeglImage image)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fEGLImageTargetRenderbufferStorage);
+ mSymbols.fEGLImageTargetRenderbufferStorage(target, image);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_bind_buffer_offset
+public:
+ void fBindBufferOffset(GLenum target, GLuint index, GLuint buffer, GLintptr offset)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindBufferOffset);
+ mSymbols.fBindBufferOffset(target, index, buffer, offset);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_draw_buffers
+public:
+ void fDrawBuffers(GLsizei n, const GLenum* bufs) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDrawBuffers);
+ mSymbols.fDrawBuffers(n, bufs);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_draw_instanced
+public:
+ void fDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
+ {
+ BeforeGLDrawCall();
+ raw_fDrawArraysInstanced(mode, first, count, primcount);
+ AfterGLDrawCall();
+ }
+
+ void fDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLsizei primcount)
+ {
+ BeforeGLDrawCall();
+ raw_fDrawElementsInstanced(mode, count, type, indices, primcount);
+ AfterGLDrawCall();
+ }
+
+private:
+ void raw_fDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDrawArraysInstanced);
+ mSymbols.fDrawArraysInstanced(mode, first, count, primcount);
+ AFTER_GL_CALL;
+ }
+
+ void raw_fDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLsizei primcount)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDrawElementsInstanced);
+ mSymbols.fDrawElementsInstanced(mode, count, type, indices, primcount);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Feature draw_range_elements
+public:
+ void fDrawRangeElements(GLenum mode, GLuint start, GLuint end,
+ GLsizei count, GLenum type, const GLvoid* indices)
+ {
+ BeforeGLDrawCall();
+ raw_fDrawRangeElements(mode, start, end, count, type, indices);
+ AfterGLDrawCall();
+ }
+
+private:
+ void raw_fDrawRangeElements(GLenum mode, GLuint start, GLuint end,
+ GLsizei count, GLenum type, const GLvoid* indices)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDrawRangeElements);
+ mSymbols.fDrawRangeElements(mode, start, end, count, type, indices);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Package XXX_framebuffer_blit
+public:
+ // Draw/Read
+ void fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
+ BeforeGLDrawCall();
+ BeforeGLReadCall();
+ raw_fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ AfterGLReadCall();
+ AfterGLDrawCall();
+ }
+
+
+private:
+ void raw_fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBlitFramebuffer);
+ mSymbols.fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_framebuffer_multisample
+public:
+ void fRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fRenderbufferStorageMultisample);
+ mSymbols.fRenderbufferStorageMultisample(target, samples, internalFormat, width, height);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// GL 3.0, GL ES 3.0 & EXT_gpu_shader4
+public:
+ void fGetVertexAttribIiv(GLuint index, GLenum pname, GLint* params)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetVertexAttribIiv);
+ BEFORE_GL_CALL;
+ mSymbols.fGetVertexAttribIiv(index, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetVertexAttribIuiv);
+ BEFORE_GL_CALL;
+ mSymbols.fGetVertexAttribIuiv(index, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribI4i);
+ mSymbols.fVertexAttribI4i(index, x, y, z, w);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribI4iv(GLuint index, const GLint* v)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribI4iv);
+ mSymbols.fVertexAttribI4iv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribI4ui);
+ mSymbols.fVertexAttribI4ui(index, x, y, z, w);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribI4uiv(GLuint index, const GLuint* v)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribI4uiv);
+ mSymbols.fVertexAttribI4uiv(index, v);
+ AFTER_GL_CALL;
+ }
+
+ void fVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* offset)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribIPointer);
+ mSymbols.fVertexAttribIPointer(index, size, type, stride, offset);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform1ui(GLint location, GLuint v0) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform1ui);
+ mSymbols.fUniform1ui(location, v0);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2ui(GLint location, GLuint v0, GLuint v1) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform2ui);
+ mSymbols.fUniform2ui(location, v0, v1);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform3ui);
+ mSymbols.fUniform3ui(location, v0, v1, v2);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform4ui);
+ mSymbols.fUniform4ui(location, v0, v1, v2, v3);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform1uiv(GLint location, GLsizei count, const GLuint* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform1uiv);
+ mSymbols.fUniform1uiv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform2uiv(GLint location, GLsizei count, const GLuint* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform2uiv);
+ mSymbols.fUniform2uiv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform3uiv(GLint location, GLsizei count, const GLuint* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform3uiv);
+ mSymbols.fUniform3uiv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ void fUniform4uiv(GLint location, GLsizei count, const GLuint* value) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fUniform4uiv);
+ mSymbols.fUniform4uiv(location, count, value);
+ AFTER_GL_CALL;
+ }
+
+ GLint fGetFragDataLocation(GLuint program, const GLchar* name)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetFragDataLocation);
+ GLint result = mSymbols.fGetFragDataLocation(program, name);
+ AFTER_GL_CALL;
+ return result;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_instanced_arrays
+public:
+ void fVertexAttribDivisor(GLuint index, GLuint divisor)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fVertexAttribDivisor);
+ mSymbols.fVertexAttribDivisor(index, divisor);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Feature internalformat_query
+public:
+ void fGetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetInternalformativ);
+ mSymbols.fGetInternalformativ(target, internalformat, pname, bufSize, params);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_query_counter
+/**
+ * XXX_query_counter:
+ * - depends on XXX_query_objects
+ * - provide all followed entry points
+ * - provide GL_TIMESTAMP
+ */
+public:
+ void fQueryCounter(GLuint id, GLenum target) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fQueryCounter);
+ mSymbols.fQueryCounter(id, target);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_query_objects
+/**
+ * XXX_query_objects:
+ * - provide all followed entry points
+ *
+ * XXX_occlusion_query2:
+ * - depends on XXX_query_objects
+ * - provide ANY_SAMPLES_PASSED
+ *
+ * XXX_occlusion_query_boolean:
+ * - depends on XXX_occlusion_query2
+ * - provide ANY_SAMPLES_PASSED_CONSERVATIVE
+ */
+public:
+ void fDeleteQueries(GLsizei n, const GLuint* names) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDeleteQueries);
+ mSymbols.fDeleteQueries(n, names);
+ AFTER_GL_CALL;
+ TRACKING_CONTEXT(DeletedQueries(this, n, names));
+ }
+
+ void fGenQueries(GLsizei n, GLuint* names) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGenQueries);
+ mSymbols.fGenQueries(n, names);
+ AFTER_GL_CALL;
+ TRACKING_CONTEXT(CreatedQueries(this, n, names));
+ }
+
+ void fGetQueryiv(GLenum target, GLenum pname, GLint* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetQueryiv);
+ mSymbols.fGetQueryiv(target, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetQueryObjectuiv);
+ mSymbols.fGetQueryObjectuiv(id, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsQuery(GLuint query) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fIsQuery);
+ realGLboolean retval = mSymbols.fIsQuery(query);
+ AFTER_GL_CALL;
+ return retval;
+ }
+
+// -----------------------------------------------------------------------------
+// Package XXX_get_query_object_i64v
+/**
+ * XXX_get_query_object_i64v:
+ * - depends on XXX_query_objects
+ * - provide the followed entry point
+ */
+public:
+ void fGetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetQueryObjecti64v);
+ mSymbols.fGetQueryObjecti64v(id, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetQueryObjectui64v);
+ mSymbols.fGetQueryObjectui64v(id, pname, params);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Package XXX_get_query_object_iv
+/**
+ * XXX_get_query_object_iv:
+ * - depends on XXX_query_objects
+ * - provide the followed entry point
+ *
+ * XXX_occlusion_query:
+ * - depends on XXX_get_query_object_iv
+ * - provide LOCAL_GL_SAMPLES_PASSED
+ */
+public:
+ void fGetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetQueryObjectiv);
+ mSymbols.fGetQueryObjectiv(id, pname, params);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// GL 4.0, GL ES 3.0, ARB_transform_feedback2, NV_transform_feedback2
+public:
+ void fBindBufferBase(GLenum target, GLuint index, GLuint buffer)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindBufferBase);
+ mSymbols.fBindBufferBase(target, index, buffer);
+ AFTER_GL_CALL;
+ }
+
+ void fBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindBufferRange);
+ mSymbols.fBindBufferRange(target, index, buffer, offset, size);
+ AFTER_GL_CALL;
+ }
+
+ void fGenTransformFeedbacks(GLsizei n, GLuint* ids)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGenTransformFeedbacks);
+ mSymbols.fGenTransformFeedbacks(n, ids);
+ AFTER_GL_CALL;
+ }
+
+ void fDeleteTransformFeedbacks(GLsizei n, const GLuint* ids)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDeleteTransformFeedbacks);
+ mSymbols.fDeleteTransformFeedbacks(n, ids);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsTransformFeedback(GLuint id)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fIsTransformFeedback);
+ realGLboolean result = mSymbols.fIsTransformFeedback(id);
+ AFTER_GL_CALL;
+ return result;
+ }
+
+ void fBindTransformFeedback(GLenum target, GLuint id)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindTransformFeedback);
+ mSymbols.fBindTransformFeedback(target, id);
+ AFTER_GL_CALL;
+ }
+
+ void fBeginTransformFeedback(GLenum primitiveMode)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBeginTransformFeedback);
+ mSymbols.fBeginTransformFeedback(primitiveMode);
+ AFTER_GL_CALL;
+ }
+
+ void fEndTransformFeedback()
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fEndTransformFeedback);
+ mSymbols.fEndTransformFeedback();
+ AFTER_GL_CALL;
+ }
+
+ void fTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fTransformFeedbackVaryings);
+ mSymbols.fTransformFeedbackVaryings(program, count, varyings, bufferMode);
+ AFTER_GL_CALL;
+ }
+
+ void fGetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetTransformFeedbackVarying);
+ mSymbols.fGetTransformFeedbackVarying(program, index, bufSize, length, size, type, name);
+ AFTER_GL_CALL;
+ }
+
+ void fPauseTransformFeedback()
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fPauseTransformFeedback);
+ mSymbols.fPauseTransformFeedback();
+ AFTER_GL_CALL;
+ }
+
+ void fResumeTransformFeedback()
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fResumeTransformFeedback);
+ mSymbols.fResumeTransformFeedback();
+ AFTER_GL_CALL;
+ }
+
+ void fGetIntegeri_v(GLenum param, GLuint index, GLint* values)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetIntegeri_v);
+ mSymbols.fGetIntegeri_v(param, index, values);
+ AFTER_GL_CALL;
+ }
+
+ void fGetInteger64i_v(GLenum target, GLuint index, GLint64* data) {
+ ASSERT_SYMBOL_PRESENT(fGetInteger64i_v);
+ BEFORE_GL_CALL;
+ mSymbols.fGetInteger64i_v(target, index, data);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Package XXX_vertex_array_object
+public:
+ void fBindVertexArray(GLuint array)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindVertexArray);
+ mSymbols.fBindVertexArray(array);
+ AFTER_GL_CALL;
+ }
+
+ void fDeleteVertexArrays(GLsizei n, const GLuint* arrays)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDeleteVertexArrays);
+ mSymbols.fDeleteVertexArrays(n, arrays);
+ AFTER_GL_CALL;
+ }
+
+ void fGenVertexArrays(GLsizei n, GLuint* arrays)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGenVertexArrays);
+ mSymbols.fGenVertexArrays(n, arrays);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsVertexArray(GLuint array)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fIsVertexArray);
+ realGLboolean ret = mSymbols.fIsVertexArray(array);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+// -----------------------------------------------------------------------------
+// Extension NV_fence
+public:
+ void fGenFences(GLsizei n, GLuint* fences)
+ {
+ ASSERT_SYMBOL_PRESENT(fGenFences);
+ BEFORE_GL_CALL;
+ mSymbols.fGenFences(n, fences);
+ AFTER_GL_CALL;
+ }
+
+ void fDeleteFences(GLsizei n, const GLuint* fences)
+ {
+ ASSERT_SYMBOL_PRESENT(fDeleteFences);
+ BEFORE_GL_CALL;
+ mSymbols.fDeleteFences(n, fences);
+ AFTER_GL_CALL;
+ }
+
+ void fSetFence(GLuint fence, GLenum condition)
+ {
+ ASSERT_SYMBOL_PRESENT(fSetFence);
+ BEFORE_GL_CALL;
+ mSymbols.fSetFence(fence, condition);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fTestFence(GLuint fence)
+ {
+ ASSERT_SYMBOL_PRESENT(fTestFence);
+ BEFORE_GL_CALL;
+ realGLboolean ret = mSymbols.fTestFence(fence);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fFinishFence(GLuint fence)
+ {
+ ASSERT_SYMBOL_PRESENT(fFinishFence);
+ BEFORE_GL_CALL;
+ mSymbols.fFinishFence(fence);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsFence(GLuint fence)
+ {
+ ASSERT_SYMBOL_PRESENT(fIsFence);
+ BEFORE_GL_CALL;
+ realGLboolean ret = mSymbols.fIsFence(fence);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fGetFenceiv(GLuint fence, GLenum pname, GLint* params)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetFenceiv);
+ BEFORE_GL_CALL;
+ mSymbols.fGetFenceiv(fence, pname, params);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Extension NV_texture_barrier
+public:
+ void fTextureBarrier()
+ {
+ ASSERT_SYMBOL_PRESENT(fTextureBarrier);
+ BEFORE_GL_CALL;
+ mSymbols.fTextureBarrier();
+ AFTER_GL_CALL;
+ }
+
+// Core GL & Extension ARB_copy_buffer
+public:
+ void fCopyBufferSubData(GLenum readtarget, GLenum writetarget,
+ GLintptr readoffset, GLintptr writeoffset,
+ GLsizeiptr size)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fCopyBufferSubData);
+ mSymbols.fCopyBufferSubData(readtarget, writetarget, readoffset, writeoffset, size);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Core GL & Extension ARB_map_buffer_range
+public:
+ void* fMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
+ GLbitfield access)
+ {
+ ASSERT_SYMBOL_PRESENT(fMapBufferRange);
+ BEFORE_GL_CALL;
+ void* data = mSymbols.fMapBufferRange(target, offset, length, access);
+ AFTER_GL_CALL;
+ return data;
+ }
+
+ void fFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) {
+ ASSERT_SYMBOL_PRESENT(fFlushMappedBufferRange);
+ BEFORE_GL_CALL;
+ mSymbols.fFlushMappedBufferRange(target, offset, length);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Core GL & Extension ARB_sampler_objects
+public:
+ void fGenSamplers(GLsizei count, GLuint* samplers)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGenSamplers);
+ mSymbols.fGenSamplers(count, samplers);
+ AFTER_GL_CALL;
+ }
+
+ void fDeleteSamplers(GLsizei count, const GLuint* samplers)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fDeleteSamplers);
+ mSymbols.fDeleteSamplers(count, samplers);
+ AFTER_GL_CALL;
+ }
+
+ realGLboolean fIsSampler(GLuint sampler)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fIsSampler);
+ realGLboolean result = mSymbols.fIsSampler(sampler);
+ AFTER_GL_CALL;
+ return result;
+ }
+
+ void fBindSampler(GLuint unit, GLuint sampler)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fBindSampler);
+ mSymbols.fBindSampler(unit, sampler);
+ AFTER_GL_CALL;
+ }
+
+ void fSamplerParameteri(GLuint sampler, GLenum pname, GLint param)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fSamplerParameteri);
+ mSymbols.fSamplerParameteri(sampler, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fSamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fSamplerParameteriv);
+ mSymbols.fSamplerParameteriv(sampler, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fSamplerParameterf);
+ mSymbols.fSamplerParameterf(sampler, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fSamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fSamplerParameterfv);
+ mSymbols.fSamplerParameterfv(sampler, pname, param);
+ AFTER_GL_CALL;
+ }
+
+ void fGetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetSamplerParameteriv);
+ mSymbols.fGetSamplerParameteriv(sampler, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetSamplerParameterfv);
+ mSymbols.fGetSamplerParameterfv(sampler, pname, params);
+ AFTER_GL_CALL;
+ }
+
+
+// -----------------------------------------------------------------------------
+// Core GL & Extension ARB_uniform_buffer_object
+public:
+ void fGetUniformIndices(GLuint program, GLsizei uniformCount,
+ const GLchar* const* uniformNames, GLuint* uniformIndices)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetUniformIndices);
+ BEFORE_GL_CALL;
+ mSymbols.fGetUniformIndices(program, uniformCount, uniformNames, uniformIndices);
+ AFTER_GL_CALL;
+ }
+
+ void fGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices,
+ GLenum pname, GLint* params)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetActiveUniformsiv);
+ BEFORE_GL_CALL;
+ mSymbols.fGetActiveUniformsiv(program, uniformCount, uniformIndices, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ GLuint fGetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName) {
+ ASSERT_SYMBOL_PRESENT(fGetUniformBlockIndex);
+ BEFORE_GL_CALL;
+ GLuint result = mSymbols.fGetUniformBlockIndex(program, uniformBlockName);
+ AFTER_GL_CALL;
+ return result;
+ }
+
+ void fGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex,
+ GLenum pname, GLint* params)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetActiveUniformBlockiv);
+ BEFORE_GL_CALL;
+ mSymbols.fGetActiveUniformBlockiv(program, uniformBlockIndex, pname, params);
+ AFTER_GL_CALL;
+ }
+
+ void fGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize,
+ GLsizei* length, GLchar* uniformBlockName)
+ {
+ ASSERT_SYMBOL_PRESENT(fGetActiveUniformBlockName);
+ BEFORE_GL_CALL;
+ mSymbols.fGetActiveUniformBlockName(program, uniformBlockIndex, bufSize, length, uniformBlockName);
+ AFTER_GL_CALL;
+ }
+
+ void fUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
+ ASSERT_SYMBOL_PRESENT(fUniformBlockBinding);
+ BEFORE_GL_CALL;
+ mSymbols.fUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Core GL 4.2, GL ES 3.0 & Extension ARB_texture_storage/EXT_texture_storage
+ void fTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fTexStorage2D);
+ mSymbols.fTexStorage2D(target, levels, internalformat, width, height);
+ AFTER_GL_CALL;
+ }
+
+ void fTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fTexStorage3D);
+ mSymbols.fTexStorage3D(target, levels, internalformat, width, height, depth);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// 3D Textures
+ void fTexImage3D(GLenum target, GLint level,
+ GLint internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLint border, GLenum format, GLenum type,
+ const GLvoid * data)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fTexImage3D);
+ mSymbols.fTexImage3D(target, level, internalFormat,
+ width, height, depth,
+ border, format, type,
+ data);
+ AFTER_GL_CALL;
+ }
+
+ void fTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
+ GLenum format, GLenum type, const GLvoid* pixels)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fTexSubImage3D);
+ mSymbols.fTexSubImage3D(target, level, xoffset, yoffset, zoffset,
+ width, height, depth, format, type,
+ pixels);
+ AFTER_GL_CALL;
+ }
+
+ void fCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset,
+ GLint yoffset, GLint zoffset, GLint x,
+ GLint y, GLsizei width, GLsizei height)
+ {
+ BeforeGLReadCall();
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fCopyTexSubImage3D);
+ mSymbols.fCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset,
+ x, y, width, height);
+ AFTER_GL_CALL;
+ AfterGLReadCall();
+ }
+
+ void fCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLint border, GLsizei imageSize, const GLvoid* data)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fCompressedTexImage3D);
+ mSymbols.fCompressedTexImage3D(target, level, internalformat,
+ width, height, depth,
+ border, imageSize, data);
+ AFTER_GL_CALL;
+ }
+
+ void fCompressedTexSubImage3D(GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint zoffset,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLenum format, GLsizei imageSize, const GLvoid* data)
+ {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fCompressedTexSubImage3D);
+ mSymbols.fCompressedTexSubImage3D(target, level,
+ xoffset, yoffset, zoffset,
+ width, height, depth,
+ format, imageSize, data);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// get_string_indexed
+
+ const GLubyte* fGetStringi(GLenum name, GLuint index) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fGetStringi);
+ const GLubyte* ret = mSymbols.fGetStringi(name, index);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+// -----------------------------------------------------------------------------
+// APPLE_framebuffer_multisample
+
+ void fResolveMultisampleFramebufferAPPLE() {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fResolveMultisampleFramebufferAPPLE);
+ mSymbols.fResolveMultisampleFramebufferAPPLE();
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// prim_restart
+
+ void fPrimitiveRestartIndex(GLuint index) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fPrimitiveRestartIndex);
+ mSymbols.fPrimitiveRestartIndex(index);
+ AFTER_GL_CALL;
+ }
+
+// -----------------------------------------------------------------------------
+// Constructor
+protected:
+ explicit GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
+ GLContext* sharedContext = nullptr,
+ bool isOffscreen = false);
+
+
+// -----------------------------------------------------------------------------
+// Destructor
+public:
+ virtual ~GLContext();
+
+ // Mark this context as destroyed. This will nullptr out all
+ // the GL function pointers!
+ void MarkDestroyed();
+
+// -----------------------------------------------------------------------------
+// Everything that isn't standard GL APIs
+protected:
+ typedef gfx::SurfaceFormat SurfaceFormat;
+
+ virtual bool MakeCurrentImpl(bool aForce) = 0;
+
+public:
+#ifdef MOZ_GL_DEBUG
+ static void StaticInit() {
+ PR_NewThreadPrivateIndex(&sCurrentGLContextTLS, nullptr);
+ }
+#endif
+
+ bool MakeCurrent(bool aForce = false) {
+ if (IsDestroyed()) {
+ return false;
+ }
+#ifdef MOZ_GL_DEBUG
+ PR_SetThreadPrivate(sCurrentGLContextTLS, this);
+
+ // XXX this assertion is disabled because it's triggering on Mac;
+ // we need to figure out why and reenable it.
+#if 0
+ // IsOwningThreadCurrent is a bit of a misnomer;
+ // the "owning thread" is the creation thread,
+ // and the only thread that can own this. We don't
+ // support contexts used on multiple threads.
+ NS_ASSERTION(IsOwningThreadCurrent(),
+ "MakeCurrent() called on different thread than this context was created on!");
+#endif
+#endif
+ return MakeCurrentImpl(aForce);
+ }
+
+ virtual bool Init() = 0;
+
+ virtual bool SetupLookupFunction() = 0;
+
+ virtual void ReleaseSurface() {}
+
+ bool IsDestroyed() {
+ return mIsDestroyed;
+ }
+
+ GLContext* GetSharedContext() { return mSharedContext; }
+
+ /**
+ * Returns true if the thread on which this context was created is the currently
+ * executing thread.
+ */
+ bool IsOwningThreadCurrent();
+
+ static void PlatformStartup();
+
+public:
+ /**
+ * If this context wraps a double-buffered target, swap the back
+ * and front buffers. It should be assumed that after a swap, the
+ * contents of the new back buffer are undefined.
+ */
+ virtual bool SwapBuffers() { return false; }
+
+ /**
+ * Defines a two-dimensional texture image for context target surface
+ */
+ virtual bool BindTexImage() { return false; }
+ /*
+ * Releases a color buffer that is being used as a texture
+ */
+ virtual bool ReleaseTexImage() { return false; }
+
+ // Before reads from offscreen texture
+ void GuaranteeResolve();
+
+ /*
+ * Resize the current offscreen buffer. Returns true on success.
+ * If it returns false, the context should be treated as unusable
+ * and should be recreated. After the resize, the viewport is not
+ * changed; glViewport should be called as appropriate.
+ *
+ * Only valid if IsOffscreen() returns true.
+ */
+ bool ResizeOffscreen(const gfx::IntSize& size) {
+ return ResizeScreenBuffer(size);
+ }
+
+ /*
+ * Return size of this offscreen context.
+ *
+ * Only valid if IsOffscreen() returns true.
+ */
+ const gfx::IntSize& OffscreenSize() const;
+
+ void BindFB(GLuint fb) {
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
+ MOZ_ASSERT(!fb || fIsFramebuffer(fb));
+ }
+
+ void BindDrawFB(GLuint fb) {
+ fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, fb);
+ }
+
+ void BindReadFB(GLuint fb) {
+ fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, fb);
+ }
+
+ GLuint GetDrawFB();
+
+ GLuint GetReadFB();
+
+ GLuint GetFB();
+
+private:
+ void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+ switch (precisiontype) {
+ case LOCAL_GL_LOW_FLOAT:
+ case LOCAL_GL_MEDIUM_FLOAT:
+ case LOCAL_GL_HIGH_FLOAT:
+ // Assume IEEE 754 precision
+ range[0] = 127;
+ range[1] = 127;
+ *precision = 23;
+ break;
+ case LOCAL_GL_LOW_INT:
+ case LOCAL_GL_MEDIUM_INT:
+ case LOCAL_GL_HIGH_INT:
+ // Some (most) hardware only supports single-precision floating-point numbers,
+ // which can accurately represent integers up to +/-16777216
+ range[0] = 24;
+ range[1] = 24;
+ *precision = 0;
+ break;
+ }
+ }
+
+public:
+
+ void ForceDirtyScreen();
+ void CleanDirtyScreen();
+
+ virtual GLenum GetPreferredARGB32Format() const { return LOCAL_GL_RGBA; }
+
+ virtual GLenum GetPreferredEGLImageTextureTarget() const {
+ return IsExtensionSupported(OES_EGL_image_external) ?
+ LOCAL_GL_TEXTURE_EXTERNAL : LOCAL_GL_TEXTURE_2D;
+ }
+
+ virtual bool RenewSurface(nsIWidget* aWidget) { return false; }
+
+ // Shared code for GL extensions and GLX extensions.
+ static bool ListHasExtension(const GLubyte* extensions,
+ const char* extension);
+
+ GLint GetMaxTextureImageSize() { return mMaxTextureImageSize; }
+
+public:
+ std::map<GLuint, SharedSurface*> mFBOMapping;
+
+ enum {
+ DebugFlagEnabled = 1 << 0,
+ DebugFlagTrace = 1 << 1,
+ DebugFlagAbortOnError = 1 << 2
+ };
+
+ const uint8_t mDebugFlags;
+
+protected:
+ RefPtr<GLContext> mSharedContext;
+
+ // The thread id which this context was created.
+ PlatformThreadId mOwningThreadId;
+
+ GLContextSymbols mSymbols;
+
+#ifdef MOZ_GL_DEBUG
+ // Non-zero debug flags will check that we don't send call
+ // to a GLContext that isn't current on the current
+ // thread.
+ // Store the current context when binding to thread local
+ // storage to support debug flags on an arbitrary thread.
+ static unsigned sCurrentGLContextTLS;
+#endif
+
+ UniquePtr<GLBlitHelper> mBlitHelper;
+ UniquePtr<GLReadTexImageHelper> mReadTexImageHelper;
+
+public:
+ GLBlitHelper* BlitHelper();
+ GLBlitTextureImageHelper* BlitTextureImageHelper();
+ GLReadTexImageHelper* ReadTexImageHelper();
+
+ // Assumes shares are created by all sharing with the same global context.
+ bool SharesWith(const GLContext* other) const {
+ MOZ_ASSERT(!this->mSharedContext || !this->mSharedContext->mSharedContext);
+ MOZ_ASSERT(!other->mSharedContext || !other->mSharedContext->mSharedContext);
+ MOZ_ASSERT(!this->mSharedContext ||
+ !other->mSharedContext ||
+ this->mSharedContext == other->mSharedContext);
+
+ const GLContext* thisShared = this->mSharedContext ? this->mSharedContext
+ : this;
+ const GLContext* otherShared = other->mSharedContext ? other->mSharedContext
+ : other;
+
+ return thisShared == otherShared;
+ }
+
+ bool InitOffscreen(const gfx::IntSize& size, const SurfaceCaps& caps);
+
+protected:
+ // Note that it does -not- clear the resized buffers.
+ bool CreateScreenBuffer(const gfx::IntSize& size, const SurfaceCaps& caps) {
+ if (!IsOffscreenSizeAllowed(size))
+ return false;
+
+ return CreateScreenBufferImpl(size, caps);
+ }
+
+ bool CreateScreenBufferImpl(const gfx::IntSize& size,
+ const SurfaceCaps& caps);
+
+public:
+ bool ResizeScreenBuffer(const gfx::IntSize& size);
+
+protected:
+ SurfaceCaps mCaps;
+
+public:
+ const SurfaceCaps& Caps() const {
+ return mCaps;
+ }
+
+ // Only varies based on bpp16 and alpha.
+ GLFormats ChooseGLFormats(const SurfaceCaps& caps) const;
+
+ bool IsFramebufferComplete(GLuint fb, GLenum* status = nullptr);
+
+ // Does not check completeness.
+ void AttachBuffersToFB(GLuint colorTex, GLuint colorRB,
+ GLuint depthRB, GLuint stencilRB,
+ GLuint fb, GLenum target = LOCAL_GL_TEXTURE_2D);
+
+ // Passing null is fine if the value you'd get is 0.
+ bool AssembleOffscreenFBs(const GLuint colorMSRB,
+ const GLuint depthRB,
+ const GLuint stencilRB,
+ const GLuint texture,
+ GLuint* drawFB,
+ GLuint* readFB);
+
+protected:
+ friend class GLScreenBuffer;
+ UniquePtr<GLScreenBuffer> mScreen;
+
+ SharedSurface* mLockedSurface;
+
+public:
+ void LockSurface(SharedSurface* surf) {
+ MOZ_ASSERT(!mLockedSurface);
+ mLockedSurface = surf;
+ }
+
+ void UnlockSurface(SharedSurface* surf) {
+ MOZ_ASSERT(mLockedSurface == surf);
+ mLockedSurface = nullptr;
+ }
+
+ SharedSurface* GetLockedSurface() const {
+ return mLockedSurface;
+ }
+
+ bool IsOffscreen() const {
+ return mIsOffscreen;
+ }
+
+ GLScreenBuffer* Screen() const {
+ return mScreen.get();
+ }
+
+ /* Clear to transparent black, with 0 depth and stencil,
+ * while preserving current ClearColor etc. values.
+ * Useful for resizing offscreen buffers.
+ */
+ void ClearSafely();
+
+ bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
+
+ bool IsDrawingToDefaultFramebuffer();
+
+protected:
+ RefPtr<TextureGarbageBin> mTexGarbageBin;
+
+public:
+ TextureGarbageBin* TexGarbageBin() {
+ MOZ_ASSERT(mTexGarbageBin);
+ return mTexGarbageBin;
+ }
+
+ void EmptyTexGarbageBin();
+
+ bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const;
+
+protected:
+ bool InitWithPrefix(const char* prefix, bool trygl);
+
+private:
+ bool InitWithPrefixImpl(const char* prefix, bool trygl);
+ void LoadMoreSymbols(const char* prefix, bool trygl);
+ bool LoadExtSymbols(const char* prefix, bool trygl, const SymLoadStruct* list,
+ GLExtensions ext);
+ bool LoadFeatureSymbols(const char* prefix, bool trygl, const SymLoadStruct* list,
+ GLFeature feature);
+
+protected:
+ void InitExtensions();
+
+ GLint mViewportRect[4];
+ GLint mScissorRect[4];
+
+ GLint mMaxTextureSize;
+ GLint mMaxCubeMapTextureSize;
+ GLint mMaxTextureImageSize;
+ GLint mMaxRenderbufferSize;
+ GLint mMaxViewportDims[2];
+ GLsizei mMaxSamples;
+ bool mNeedsTextureSizeChecks;
+ bool mNeedsFlushBeforeDeleteFB;
+ bool mTextureAllocCrashesOnMapFailure;
+ bool mNeedsCheckAfterAttachTextureToFb;
+ bool mWorkAroundDriverBugs;
+
+ bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
+ if (mNeedsTextureSizeChecks) {
+ // some drivers incorrectly handle some large texture sizes that are below the
+ // max texture size that they report. So we check ourselves against our own values
+ // (mMax[CubeMap]TextureSize).
+ // see bug 737182 for Mac Intel 2D textures
+ // see bug 684882 for Mac Intel cube map textures
+ // see bug 814716 for Mesa Nouveau
+ GLsizei maxSize = target == LOCAL_GL_TEXTURE_CUBE_MAP ||
+ (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+ target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
+ ? mMaxCubeMapTextureSize
+ : mMaxTextureSize;
+ return width <= maxSize && height <= maxSize;
+ }
+ return true;
+ }
+
+
+public:
+ GLsizei MaxSamples() const {
+ return mMaxSamples;
+ }
+
+ void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
+ if (mViewportRect[0] == x &&
+ mViewportRect[1] == y &&
+ mViewportRect[2] == width &&
+ mViewportRect[3] == height)
+ {
+ return;
+ }
+ mViewportRect[0] = x;
+ mViewportRect[1] = y;
+ mViewportRect[2] = width;
+ mViewportRect[3] = height;
+ BEFORE_GL_CALL;
+ mSymbols.fViewport(x, y, width, height);
+ AFTER_GL_CALL;
+ }
+
+#undef ASSERT_SYMBOL_PRESENT
+
+#ifdef MOZ_GL_DEBUG
+ void CreatedProgram(GLContext* aOrigin, GLuint aName);
+ void CreatedShader(GLContext* aOrigin, GLuint aName);
+ void CreatedBuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
+ void CreatedQueries(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
+ void CreatedTextures(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
+ void CreatedFramebuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
+ void CreatedRenderbuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
+ void DeletedProgram(GLContext* aOrigin, GLuint aName);
+ void DeletedShader(GLContext* aOrigin, GLuint aName);
+ void DeletedBuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
+ void DeletedQueries(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
+ void DeletedTextures(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
+ void DeletedFramebuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
+ void DeletedRenderbuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
+
+ void SharedContextDestroyed(GLContext* aChild);
+ void ReportOutstandingNames();
+
+ struct NamedResource {
+ NamedResource()
+ : origin(nullptr), name(0), originDeleted(false)
+ { }
+
+ NamedResource(GLContext* aOrigin, GLuint aName)
+ : origin(aOrigin), name(aName), originDeleted(false)
+ { }
+
+ GLContext* origin;
+ GLuint name;
+ bool originDeleted;
+
+ // for sorting
+ bool operator<(const NamedResource& aOther) const {
+ if (intptr_t(origin) < intptr_t(aOther.origin))
+ return true;
+ if (name < aOther.name)
+ return true;
+ return false;
+ }
+ bool operator==(const NamedResource& aOther) const {
+ return origin == aOther.origin &&
+ name == aOther.name &&
+ originDeleted == aOther.originDeleted;
+ }
+ };
+
+ nsTArray<NamedResource> mTrackedPrograms;
+ nsTArray<NamedResource> mTrackedShaders;
+ nsTArray<NamedResource> mTrackedTextures;
+ nsTArray<NamedResource> mTrackedFramebuffers;
+ nsTArray<NamedResource> mTrackedRenderbuffers;
+ nsTArray<NamedResource> mTrackedBuffers;
+ nsTArray<NamedResource> mTrackedQueries;
+#endif
+
+
+protected:
+ bool mHeavyGLCallsSinceLastFlush;
+
+public:
+ void FlushIfHeavyGLCallsSinceLastFlush();
+ static bool ShouldSpew();
+ static bool ShouldDumpExts();
+ void Readback(SharedSurface* src, gfx::DataSourceSurface* dest);
+};
+
+bool DoesStringMatch(const char* aString, const char* aWantedString);
+
+void SplitByChar(const nsACString& str, const char delim,
+ std::vector<nsCString>* const out);
+
+template<size_t N>
+bool
+MarkBitfieldByString(const nsACString& str, const char* const (&markStrList)[N],
+ std::bitset<N>* const out_markList)
+{
+ for (size_t i = 0; i < N; i++) {
+ if (str.Equals(markStrList[i])) {
+ (*out_markList)[i] = 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+template<size_t N>
+void
+MarkBitfieldByStrings(const std::vector<nsCString>& strList,
+ bool dumpStrings, const char* const (&markStrList)[N],
+ std::bitset<N>* const out_markList)
+{
+ for (auto itr = strList.begin(); itr != strList.end(); ++itr) {
+ const nsACString& str = *itr;
+ const bool wasMarked = MarkBitfieldByString(str, markStrList,
+ out_markList);
+ if (dumpStrings)
+ printf_stderr(" %s%s\n", str.BeginReading(), wasMarked ? "(*)" : "");
+ }
+}
+
+/**
+ * Helper function that creates a 2D texture aSize.width x aSize.height with
+ * storage type specified by aFormats. Returns GL texture object id.
+ *
+ * See mozilla::gl::CreateTexture.
+ */
+GLuint CreateTextureForOffscreen(GLContext* aGL, const GLFormats& aFormats,
+ const gfx::IntSize& aSize);
+
+/**
+ * Helper function that creates a 2D texture aSize.width x aSize.height with
+ * storage type aInternalFormat. Returns GL texture object id.
+ *
+ * Initialize textyre parameters to:
+ * GL_TEXTURE_MIN_FILTER = GL_LINEAR
+ * GL_TEXTURE_MAG_FILTER = GL_LINEAR
+ * GL_TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE
+ * GL_TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE
+ */
+GLuint CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
+ GLenum aType, const gfx::IntSize& aSize, bool linear = true);
+
+/**
+ * Helper function that calculates the number of bytes required per
+ * texel for a texture from its format and type.
+ */
+uint32_t GetBytesPerTexel(GLenum format, GLenum type);
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* GLCONTEXT_H_ */
diff --git a/system/graphics/gl/GLContextEGL.h b/system/graphics/gl/GLContextEGL.h
new file mode 100644
index 000000000..3aa2c4cdc
--- /dev/null
+++ b/system/graphics/gl/GLContextEGL.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTEGL_H_
+#define GLCONTEXTEGL_H_
+
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContextEGL : public GLContext
+{
+ friend class TextureImageEGL;
+
+ static already_AddRefed<GLContextEGL>
+ CreateGLContext(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContextEGL* shareContext,
+ bool isOffscreen,
+ EGLConfig config,
+ EGLSurface surface,
+ nsACString* const out_failureId);
+
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEGL, override)
+ GLContextEGL(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContext* shareContext,
+ bool isOffscreen,
+ EGLConfig config,
+ EGLSurface surface,
+ EGLContext context);
+
+ ~GLContextEGL();
+
+ virtual GLContextType GetContextType() const override { return GLContextType::EGL; }
+
+ static GLContextEGL* Cast(GLContext* gl) {
+ MOZ_ASSERT(gl->GetContextType() == GLContextType::EGL);
+ return static_cast<GLContextEGL*>(gl);
+ }
+
+ static EGLSurface CreateSurfaceForWindow(nsIWidget* aWidget);
+
+ static void DestroySurface(EGLSurface aSurface);
+
+ bool Init() override;
+
+ virtual bool IsDoubleBuffered() const override {
+ return mIsDoubleBuffered;
+ }
+
+ void SetIsDoubleBuffered(bool aIsDB) {
+ mIsDoubleBuffered = aIsDB;
+ }
+
+ virtual bool SupportsRobustness() const override {
+ return sEGLLibrary.HasRobustness();
+ }
+
+ virtual bool IsANGLE() const override {
+ return sEGLLibrary.IsANGLE();
+ }
+
+ virtual bool IsWARP() const override {
+ return sEGLLibrary.IsWARP();
+ }
+
+ virtual bool BindTexImage() override;
+
+ virtual bool ReleaseTexImage() override;
+
+ void SetEGLSurfaceOverride(EGLSurface surf);
+
+ virtual bool MakeCurrentImpl(bool aForce) override;
+
+ virtual bool IsCurrent() override;
+
+ virtual bool RenewSurface(nsIWidget* aWidget) override;
+
+ virtual void ReleaseSurface() override;
+
+ virtual bool SetupLookupFunction() override;
+
+ virtual bool SwapBuffers() override;
+
+ virtual void GetWSIInfo(nsCString* const out) const override;
+
+ // hold a reference to the given surface
+ // for the lifetime of this context.
+ void HoldSurface(gfxASurface* aSurf);
+
+ EGLSurface GetEGLSurface() const {
+ return mSurface;
+ }
+
+ EGLDisplay GetEGLDisplay() const {
+ return sEGLLibrary.Display();
+ }
+
+ bool BindTex2DOffscreen(GLContext* aOffscreen);
+ void UnbindTex2DOffscreen(GLContext* aOffscreen);
+ void BindOffscreenFramebuffer();
+
+ static already_AddRefed<GLContextEGL>
+ CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
+ const gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ nsACString* const out_FailureId);
+
+protected:
+ friend class GLContextProviderEGL;
+
+public:
+ const EGLConfig mConfig;
+protected:
+ EGLSurface mSurface;
+public:
+ const EGLContext mContext;
+protected:
+ EGLSurface mSurfaceOverride;
+ RefPtr<gfxASurface> mThebesSurface;
+ bool mBound;
+
+ bool mIsPBuffer;
+ bool mIsDoubleBuffered;
+ bool mCanBindToTexture;
+ bool mShareWithEGLImage;
+ bool mOwnsContext;
+
+ static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
+ EGLenum bindToTextureFormat,
+ gfx::IntSize& pbsize);
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GLCONTEXTEGL_H_
diff --git a/system/graphics/gl/GLContextFeatures.cpp b/system/graphics/gl/GLContextFeatures.cpp
new file mode 100644
index 000000000..d4f37803f
--- /dev/null
+++ b/system/graphics/gl/GLContextFeatures.cpp
@@ -0,0 +1,940 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContext.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+namespace gl {
+
+const size_t kMAX_EXTENSION_GROUP_SIZE = 5;
+
+enum class GLVersion : uint32_t {
+ NONE = 0, // Feature is not supported natively by GL
+ GL1_2 = 120,
+ GL1_3 = 130,
+ GL2 = 200,
+ GL2_1 = 210,
+ GL3 = 300,
+ GL3_1 = 310,
+ GL3_2 = 320,
+ GL3_3 = 330,
+ GL4 = 400,
+ GL4_1 = 410,
+ GL4_2 = 420,
+ GL4_3 = 430,
+};
+
+enum class GLESVersion : uint32_t {
+ NONE = 0, // Feature is not support natively by GL ES
+ ES2 = 200,
+ ES3 = 300,
+ ES3_1 = 310,
+ ES3_2 = 320,
+};
+
+// ARB_ES2_compatibility is natively supported in OpenGL 4.1.
+static const GLVersion kGLCoreVersionForES2Compat = GLVersion::GL4_1;
+
+// ARB_ES3_compatibility is natively supported in OpenGL 4.3.
+static const GLVersion kGLCoreVersionForES3Compat = GLVersion::GL4_3;
+
+struct FeatureInfo
+{
+ const char* mName;
+
+ /* The (desktop) OpenGL version that provides this feature */
+ GLVersion mOpenGLVersion;
+
+ /* The OpenGL ES version that provides this feature */
+ GLESVersion mOpenGLESVersion;
+
+ /* If there is an ARB extension, and its function symbols are
+ * not decorated with an ARB suffix, then its extension ID should go
+ * here, and NOT in mExtensions. For example, ARB_vertex_array_object
+ * functions do not have an ARB suffix, because it is an extension that
+ * was created to match core GL functionality and will never differ.
+ * Some ARB extensions do have a suffix, if they were created before
+ * a core version of the functionality existed.
+ *
+ * If there is no such ARB extension, pass 0 (GLContext::Extension_None)
+ */
+ GLContext::GLExtensions mARBExtensionWithoutARBSuffix;
+
+ /* Extensions that also provide this feature */
+ GLContext::GLExtensions mExtensions[kMAX_EXTENSION_GROUP_SIZE];
+};
+
+static const FeatureInfo sFeatureInfoArr[] = {
+ {
+ "bind_buffer_offset",
+ GLVersion::NONE,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+
+ GLContext::EXT_transform_feedback,
+ GLContext::NV_transform_feedback2,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "blend_minmax",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_blend_minmax,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "clear_buffers",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "copy_buffer",
+ GLVersion::GL3_1,
+ GLESVersion::ES3,
+ GLContext::ARB_copy_buffer,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "depth_texture",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_depth_texture,
+ GLContext::OES_depth_texture,
+ // Intentionally avoid putting ANGLE_depth_texture here,
+ // it does not offer quite the same functionality.
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "draw_buffers",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_draw_buffers,
+ GLContext::EXT_draw_buffers,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "draw_instanced",
+ GLVersion::GL3_1,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_draw_instanced,
+ GLContext::EXT_draw_instanced,
+ GLContext::NV_draw_instanced,
+ GLContext::ANGLE_instanced_arrays,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "draw_range_elements",
+ GLVersion::GL1_2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_draw_range_elements,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "element_index_uint",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::OES_element_index_uint,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "ES2_compatibility",
+ kGLCoreVersionForES2Compat,
+ GLESVersion::ES2, // OpenGL ES version
+ GLContext::ARB_ES2_compatibility, // no suffix on ARB extension
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "ES3_compatibility",
+ kGLCoreVersionForES3Compat,
+ GLESVersion::ES3, // OpenGL ES version
+ GLContext::ARB_ES3_compatibility, // no suffix on ARB extension
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "EXT_color_buffer_float",
+ GLVersion::GL3,
+ GLESVersion::ES3_2,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_color_buffer_float,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // Removes clamping for float color outputs from frag shaders.
+ "frag_color_float",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_color_buffer_float,
+ GLContext::EXT_color_buffer_float,
+ GLContext::EXT_color_buffer_half_float,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "frag_depth",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_frag_depth,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // Check for just the blit framebuffer blit part of
+ // ARB_framebuffer_object
+ "framebuffer_blit",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_framebuffer_object,
+ {
+ GLContext::ANGLE_framebuffer_blit,
+ GLContext::EXT_framebuffer_blit,
+ GLContext::NV_framebuffer_blit,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // Check for just the multisample renderbuffer part of
+ // ARB_framebuffer_object
+ "framebuffer_multisample",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_framebuffer_object,
+ {
+ GLContext::ANGLE_framebuffer_multisample,
+ GLContext::APPLE_framebuffer_multisample,
+ GLContext::EXT_framebuffer_multisample,
+ GLContext::EXT_multisampled_render_to_texture,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // ARB_framebuffer_object support
+ "framebuffer_object",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_framebuffer_object,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // EXT_framebuffer_object/OES_framebuffer_object support
+ "framebuffer_object_EXT_OES",
+ GLVersion::GL3,
+ GLESVersion::ES2,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_framebuffer_object,
+ GLContext::OES_framebuffer_object,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "get_integer_indexed",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_draw_buffers2,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "get_integer64_indexed",
+ GLVersion::GL3_2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "get_query_object_i64v",
+ GLVersion::GL3_3,
+ GLESVersion::NONE,
+ GLContext::ARB_timer_query,
+ {
+ GLContext::ANGLE_timer_query,
+ GLContext::EXT_disjoint_timer_query,
+ GLContext::EXT_timer_query,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "get_query_object_iv",
+ GLVersion::GL2,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ /*
+ * XXX_get_query_object_iv only provide GetQueryObjectiv provided by
+ * ARB_occlusion_query (added by OpenGL 2.0).
+ */
+ },
+ {
+ "get_string_indexed",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ // glGetStringi
+ },
+ {
+ "gpu_shader4",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_gpu_shader4,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "gpu_shader5",
+ GLVersion::GL4,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_gpu_shader5,
+ GLContext::EXT_gpu_shader5,
+ GLContext::NV_gpu_shader5,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "instanced_arrays",
+ GLVersion::GL3_3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_instanced_arrays,
+ GLContext::NV_instanced_arrays,
+ GLContext::ANGLE_instanced_arrays,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "instanced_non_arrays",
+ GLVersion::GL3_3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_instanced_arrays,
+ GLContext::Extensions_End
+ }
+ /* This is an expanded version of `instanced_arrays` that allows for all
+ * enabled active attrib arrays to have non-zero divisors.
+ * ANGLE_instanced_arrays and NV_instanced_arrays forbid this, but GLES3
+ * has no such restriction.
+ */
+ },
+ {
+ "internalformat_query",
+ GLVersion::GL4_2,
+ GLESVersion::ES3,
+ GLContext::ARB_internalformat_query,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "invalidate_framebuffer",
+ GLVersion::GL4_3,
+ GLESVersion::ES3,
+ GLContext::ARB_invalidate_subdata,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "map_buffer_range",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_map_buffer_range,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "occlusion_query",
+ GLVersion::GL2,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ // XXX_occlusion_query depend on ARB_occlusion_query (added in OpenGL 2.0)
+ },
+ {
+ "occlusion_query_boolean",
+ kGLCoreVersionForES3Compat,
+ GLESVersion::ES3,
+ GLContext::ARB_ES3_compatibility,
+ {
+ GLContext::EXT_occlusion_query_boolean,
+ GLContext::Extensions_End
+ }
+ /*
+ * XXX_occlusion_query_boolean provide ANY_SAMPLES_PASSED_CONSERVATIVE,
+ * but EXT_occlusion_query_boolean is only a OpenGL ES extension. But
+ * it is supported on desktop if ARB_ES3_compatibility because
+ * EXT_occlusion_query_boolean (added in OpenGL ES 3.0).
+ */
+ },
+ {
+ "occlusion_query2",
+ GLVersion::GL3_3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_occlusion_query2,
+ GLContext::ARB_ES3_compatibility,
+ GLContext::EXT_occlusion_query_boolean,
+ GLContext::Extensions_End
+ }
+ /*
+ * XXX_occlusion_query2 (add in OpenGL 3.3) provide ANY_SAMPLES_PASSED,
+ * which is provided by ARB_occlusion_query2, EXT_occlusion_query_boolean
+ * (added in OpenGL ES 3.0) and ARB_ES3_compatibility
+ */
+ },
+ {
+ "packed_depth_stencil",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_packed_depth_stencil,
+ GLContext::OES_packed_depth_stencil,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "prim_restart",
+ GLVersion::GL3_1,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ //GLContext::NV_primitive_restart, // Has different enum values.
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "prim_restart_fixed",
+ kGLCoreVersionForES3Compat,
+ GLESVersion::ES3,
+ GLContext::ARB_ES3_compatibility,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "query_counter",
+ GLVersion::GL3_3,
+ GLESVersion::NONE,
+ GLContext::ARB_timer_query,
+ {
+ GLContext::ANGLE_timer_query,
+ GLContext::EXT_disjoint_timer_query,
+ // EXT_timer_query does NOT support GL_TIMESTAMP retrieval with
+ // QueryCounter.
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "query_objects",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ANGLE_timer_query,
+ GLContext::EXT_disjoint_timer_query,
+ GLContext::EXT_occlusion_query_boolean,
+ GLContext::Extensions_End
+ }
+ /*
+ * XXX_query_objects only provide entry points commonly supported by
+ * ARB_occlusion_query (added in OpenGL 2.0), EXT_occlusion_query_boolean
+ * (added in OpenGL ES 3.0), and ARB_timer_query (added in OpenGL 3.3)
+ */
+ },
+ {
+ "query_time_elapsed",
+ GLVersion::GL3_3,
+ GLESVersion::NONE,
+ GLContext::ARB_timer_query,
+ {
+ GLContext::ANGLE_timer_query,
+ GLContext::EXT_disjoint_timer_query,
+ GLContext::EXT_timer_query,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "read_buffer",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "renderbuffer_color_float",
+ GLVersion::GL3,
+ GLESVersion::ES3_2,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_float,
+ GLContext::EXT_color_buffer_float,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "renderbuffer_color_half_float",
+ GLVersion::GL3,
+ GLESVersion::ES3_2,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_float,
+ GLContext::EXT_color_buffer_float,
+ GLContext::EXT_color_buffer_half_float,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "robustness",
+ GLVersion::NONE,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_robustness,
+ GLContext::EXT_robustness,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "sRGB_framebuffer",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_framebuffer_sRGB,
+ {
+ GLContext::EXT_framebuffer_sRGB,
+ GLContext::EXT_sRGB_write_control,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "sRGB_texture",
+ GLVersion::GL2_1,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_sRGB,
+ GLContext::EXT_texture_sRGB,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "sampler_objects",
+ GLVersion::GL3_3,
+ GLESVersion::ES3,
+ GLContext::ARB_sampler_objects,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "seamless_cube_map_opt_in",
+ GLVersion::GL3_2,
+ GLESVersion::NONE,
+ GLContext::ARB_seamless_cube_map,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "shader_texture_lod",
+ GLVersion::NONE,
+ GLESVersion::NONE,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_shader_texture_lod,
+ GLContext::EXT_shader_texture_lod,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ // Do we have separate DRAW and READ framebuffer bind points?
+ "split_framebuffer",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_framebuffer_object,
+ {
+ GLContext::ANGLE_framebuffer_blit,
+ GLContext::APPLE_framebuffer_multisample,
+ GLContext::EXT_framebuffer_blit,
+ GLContext::NV_framebuffer_blit,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "standard_derivatives",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::OES_standard_derivatives,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "sync",
+ GLVersion::GL3_2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_sync,
+ GLContext::APPLE_sync,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_3D",
+ GLVersion::GL1_2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_texture3D,
+ GLContext::OES_texture_3D,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_3D_compressed",
+ GLVersion::GL1_3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_compression,
+ GLContext::OES_texture_3D,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_3D_copy",
+ GLVersion::GL1_2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::EXT_copy_texture,
+ GLContext::OES_texture_3D,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_float",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_float,
+ GLContext::OES_texture_float,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_float_linear",
+ GLVersion::GL3_1,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_float,
+ GLContext::OES_texture_float_linear,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_half_float",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_half_float_pixel,
+ GLContext::ARB_texture_float,
+ GLContext::NV_half_float,
+ GLContext::Extensions_End
+ }
+ /**
+ * We are not including OES_texture_half_float in this feature, because:
+ * GL_HALF_FLOAT = 0x140B
+ * GL_HALF_FLOAT_ARB = 0x140B == GL_HALF_FLOAT
+ * GL_HALF_FLOAT_NV = 0x140B == GL_HALF_FLOAT
+ * GL_HALF_FLOAT_OES = 0x8D61 != GL_HALF_FLOAT
+ * WebGL handles this specifically with an OES_texture_half_float check.
+ */
+ },
+ {
+ "texture_half_float_linear",
+ GLVersion::GL3_1,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_half_float_pixel,
+ GLContext::ARB_texture_float,
+ GLContext::NV_half_float,
+ GLContext::OES_texture_half_float_linear,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_non_power_of_two",
+ GLVersion::GL2,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::ARB_texture_non_power_of_two,
+ GLContext::OES_texture_npot,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_rg",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_texture_rg,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_storage",
+ GLVersion::GL4_2,
+ GLESVersion::ES3,
+ GLContext::ARB_texture_storage,
+ {
+ /*
+ * Not including GL_EXT_texture_storage here because it
+ * doesn't guarantee glTexStorage3D, which is required for
+ * WebGL 2.
+ */
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "texture_swizzle",
+ GLVersion::GL3_3,
+ GLESVersion::ES3,
+ GLContext::ARB_texture_swizzle,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "transform_feedback2",
+ GLVersion::GL4,
+ GLESVersion::ES3,
+ GLContext::ARB_transform_feedback2,
+ {
+ GLContext::NV_transform_feedback2,
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "uniform_buffer_object",
+ GLVersion::GL3_1,
+ GLESVersion::ES3,
+ GLContext::ARB_uniform_buffer_object,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "uniform_matrix_nonsquare",
+ GLVersion::GL2_1,
+ GLESVersion::ES3,
+ GLContext::Extension_None,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
+ "vertex_array_object",
+ GLVersion::GL3,
+ GLESVersion::ES3,
+ GLContext::ARB_vertex_array_object, // ARB extension
+ {
+ GLContext::OES_vertex_array_object,
+ GLContext::APPLE_vertex_array_object,
+ GLContext::Extensions_End
+ }
+ }
+};
+
+static inline const FeatureInfo&
+GetFeatureInfo(GLFeature feature)
+{
+ static_assert(MOZ_ARRAY_LENGTH(sFeatureInfoArr) == size_t(GLFeature::EnumMax),
+ "Mismatched lengths for sFeatureInfoInfos and GLFeature enums");
+
+ MOZ_ASSERT(feature < GLFeature::EnumMax,
+ "GLContext::GetFeatureInfoInfo : unknown <feature>");
+
+ return sFeatureInfoArr[size_t(feature)];
+}
+
+static inline uint32_t
+ProfileVersionForFeature(GLFeature feature, ContextProfile profile)
+{
+ MOZ_ASSERT(profile != ContextProfile::Unknown,
+ "GLContext::ProfileVersionForFeature : unknown <profile>");
+
+ const FeatureInfo& featureInfo = GetFeatureInfo(feature);
+
+ if (profile == ContextProfile::OpenGLES)
+ return (uint32_t)featureInfo.mOpenGLESVersion;
+
+ return (uint32_t)featureInfo.mOpenGLVersion;
+}
+
+bool
+IsFeaturePartOfProfileVersion(GLFeature feature,
+ ContextProfile profile, unsigned int version)
+{
+ unsigned int profileVersion = ProfileVersionForFeature(feature, profile);
+
+ /**
+ * if `profileVersion` is zero, it means that no version of the profile
+ * added support for the feature.
+ */
+ return profileVersion && version >= profileVersion;
+}
+
+bool
+GLContext::IsFeatureProvidedByCoreSymbols(GLFeature feature)
+{
+ if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion))
+ return true;
+
+ if (IsExtensionSupported(GetFeatureInfo(feature).mARBExtensionWithoutARBSuffix))
+ return true;
+
+ return false;
+}
+
+const char*
+GLContext::GetFeatureName(GLFeature feature)
+{
+ return GetFeatureInfo(feature).mName;
+}
+
+void
+GLContext::InitFeatures()
+{
+ for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); featureId++) {
+ GLFeature feature = GLFeature(featureId);
+
+ if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion)) {
+ mAvailableFeatures[featureId] = true;
+ continue;
+ }
+
+ mAvailableFeatures[featureId] = false;
+
+ const FeatureInfo& featureInfo = GetFeatureInfo(feature);
+
+ if (IsExtensionSupported(featureInfo.mARBExtensionWithoutARBSuffix)) {
+ mAvailableFeatures[featureId] = true;
+ continue;
+ }
+
+ for (size_t j = 0; true; j++) {
+ MOZ_ASSERT(j < kMAX_EXTENSION_GROUP_SIZE,
+ "kMAX_EXTENSION_GROUP_SIZE too small");
+
+ if (featureInfo.mExtensions[j] == GLContext::Extensions_End)
+ break;
+
+ if (IsExtensionSupported(featureInfo.mExtensions[j])) {
+ mAvailableFeatures[featureId] = true;
+ break;
+ }
+ }
+ }
+
+ if (ShouldDumpExts()) {
+ for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); featureId++) {
+ GLFeature feature = GLFeature(featureId);
+ printf_stderr("[%s] Feature::%s\n",
+ IsSupported(feature) ? "enabled" : "disabled",
+ GetFeatureName(feature));
+ }
+ }
+}
+
+void
+GLContext::MarkUnsupported(GLFeature feature)
+{
+ mAvailableFeatures[size_t(feature)] = false;
+
+ const FeatureInfo& featureInfo = GetFeatureInfo(feature);
+
+ for (size_t i = 0; true; i++) {
+ MOZ_ASSERT(i < kMAX_EXTENSION_GROUP_SIZE, "kMAX_EXTENSION_GROUP_SIZE too small");
+
+ if (featureInfo.mExtensions[i] == GLContext::Extensions_End)
+ break;
+
+ MarkExtensionUnsupported(featureInfo.mExtensions[i]);
+ }
+
+ MOZ_ASSERT(!IsSupported(feature), "GLContext::MarkUnsupported has failed!");
+
+ NS_WARNING(nsPrintfCString("%s marked as unsupported",
+ GetFeatureName(feature)).get());
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/GLContextGLX.h b/system/graphics/gl/GLContextGLX.h
new file mode 100644
index 000000000..f7dd90c2b
--- /dev/null
+++ b/system/graphics/gl/GLContextGLX.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTGLX_H_
+#define GLCONTEXTGLX_H_
+
+#include "GLContext.h"
+#include "GLXLibrary.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContextGLX : public GLContext
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextGLX, override)
+ static already_AddRefed<GLContextGLX>
+ CreateGLContext(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContextGLX* shareContext,
+ bool isOffscreen,
+ Display* display,
+ GLXDrawable drawable,
+ GLXFBConfig cfg,
+ bool deleteDrawable,
+ gfxXlibSurface* pixmap = nullptr,
+ ContextProfile profile = ContextProfile::OpenGLCompatibility);
+
+ // Finds a GLXFBConfig compatible with the provided window.
+ static bool
+ FindFBConfigForWindow(Display* display, int screen, Window window,
+ ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
+ GLXFBConfig* const out_config, int* const out_visid);
+
+ ~GLContextGLX();
+
+ virtual GLContextType GetContextType() const override { return GLContextType::GLX; }
+
+ static GLContextGLX* Cast(GLContext* gl) {
+ MOZ_ASSERT(gl->GetContextType() == GLContextType::GLX);
+ return static_cast<GLContextGLX*>(gl);
+ }
+
+ bool Init() override;
+
+ virtual bool MakeCurrentImpl(bool aForce) override;
+
+ virtual bool IsCurrent() override;
+
+ virtual bool SetupLookupFunction() override;
+
+ virtual bool IsDoubleBuffered() const override;
+
+ virtual bool SupportsRobustness() const override;
+
+ virtual bool SwapBuffers() override;
+
+ virtual void GetWSIInfo(nsCString* const out) const override;
+
+ // Overrides the current GLXDrawable backing the context and makes the
+ // context current.
+ bool OverrideDrawable(GLXDrawable drawable);
+
+ // Undoes the effect of a drawable override.
+ bool RestoreDrawable();
+
+private:
+ friend class GLContextProviderGLX;
+
+ GLContextGLX(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContext* shareContext,
+ bool isOffscreen,
+ Display* aDisplay,
+ GLXDrawable aDrawable,
+ GLXContext aContext,
+ bool aDeleteDrawable,
+ bool aDoubleBuffered,
+ gfxXlibSurface* aPixmap,
+ ContextProfile profile);
+
+ GLXContext mContext;
+ Display* mDisplay;
+ GLXDrawable mDrawable;
+ Maybe<GLXDrawable> mOverrideDrawable;
+ bool mDeleteDrawable;
+ bool mDoubleBuffered;
+
+ GLXLibrary* mGLX;
+
+ RefPtr<gfxXlibSurface> mPixmap;
+ bool mOwnsContext;
+};
+
+}
+}
+
+#endif // GLCONTEXTGLX_H_
diff --git a/system/graphics/gl/GLContextProvider.h b/system/graphics/gl/GLContextProvider.h
new file mode 100644
index 000000000..d9b3f0419
--- /dev/null
+++ b/system/graphics/gl/GLContextProvider.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTPROVIDER_H_
+#define GLCONTEXTPROVIDER_H_
+
+#include "GLContextTypes.h"
+#include "SurfaceTypes.h"
+
+#include "nsSize.h" // for gfx::IntSize (needed by GLContextProviderImpl.h below)
+
+class nsIWidget;
+
+namespace mozilla {
+namespace widget {
+ class CompositorWidget;
+}
+namespace gl {
+
+#define IN_GL_CONTEXT_PROVIDER_H
+
+// Null is always there
+#define GL_CONTEXT_PROVIDER_NAME GLContextProviderNull
+#include "GLContextProviderImpl.h"
+#undef GL_CONTEXT_PROVIDER_NAME
+
+#ifdef XP_WIN
+ #define GL_CONTEXT_PROVIDER_NAME GLContextProviderWGL
+ #include "GLContextProviderImpl.h"
+ #undef GL_CONTEXT_PROVIDER_NAME
+ #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderWGL
+ #define DEFAULT_IMPL WGL
+#endif
+
+#if defined(MOZ_X11)
+ #define GL_CONTEXT_PROVIDER_NAME GLContextProviderGLX
+ #include "GLContextProviderImpl.h"
+ #undef GL_CONTEXT_PROVIDER_NAME
+ #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderGLX
+#endif
+
+#define GL_CONTEXT_PROVIDER_NAME GLContextProviderEGL
+#include "GLContextProviderImpl.h"
+#undef GL_CONTEXT_PROVIDER_NAME
+#ifndef GL_CONTEXT_PROVIDER_DEFAULT
+ #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEGL
+#endif
+
+#if defined(MOZ_WIDGET_UIKIT)
+#define GL_CONTEXT_PROVIDER_NAME GLContextProviderEAGL
+#include "GLContextProviderImpl.h"
+#undef GL_CONTEXT_PROVIDER_NAME
+#ifndef GL_CONTEXT_PROVIDER_DEFAULT
+#define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEAGL
+#endif
+#endif
+
+#ifdef MOZ_GL_PROVIDER
+ #define GL_CONTEXT_PROVIDER_NAME MOZ_GL_PROVIDER
+ #include "GLContextProviderImpl.h"
+ #undef GL_CONTEXT_PROVIDER_NAME
+ #define GL_CONTEXT_PROVIDER_DEFAULT MOZ_GL_PROVIDER
+#endif
+
+#ifdef GL_CONTEXT_PROVIDER_DEFAULT
+ typedef GL_CONTEXT_PROVIDER_DEFAULT GLContextProvider;
+#else
+ typedef GLContextProviderNull GLContextProvider;
+#endif
+
+#undef IN_GL_CONTEXT_PROVIDER_H
+
+} // namespace gl
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/gl/GLContextProviderEGL.cpp b/system/graphics/gl/GLContextProviderEGL.cpp
new file mode 100644
index 000000000..ad515e604
--- /dev/null
+++ b/system/graphics/gl/GLContextProviderEGL.cpp
@@ -0,0 +1,961 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(MOZ_WIDGET_GTK)
+ #include <gdk/gdkx.h>
+ // we're using default display for now
+ #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow*)aWidget->GetNativeData(NS_NATIVE_WINDOW)))
+#else
+ #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
+#endif
+
+#if defined(XP_UNIX)
+
+ #define GLES2_LIB "libGLESv2.so"
+ #define GLES2_LIB2 "libGLESv2.so.2"
+
+#elif defined(XP_WIN)
+ #include "nsIFile.h"
+
+ #define GLES2_LIB "libGLESv2.dll"
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN 1
+ #endif
+
+ #include <windows.h>
+
+ // a little helper
+ class AutoDestroyHWND {
+ public:
+ AutoDestroyHWND(HWND aWnd = nullptr)
+ : mWnd(aWnd)
+ {
+ }
+
+ ~AutoDestroyHWND() {
+ if (mWnd) {
+ ::DestroyWindow(mWnd);
+ }
+ }
+
+ operator HWND() {
+ return mWnd;
+ }
+
+ HWND forget() {
+ HWND w = mWnd;
+ mWnd = nullptr;
+ return w;
+ }
+
+ HWND operator=(HWND aWnd) {
+ if (mWnd && mWnd != aWnd) {
+ ::DestroyWindow(mWnd);
+ }
+ mWnd = aWnd;
+ return mWnd;
+ }
+
+ HWND mWnd;
+ };
+#else
+ #error "Platform not recognized"
+#endif
+
+#include "gfxASurface.h"
+#include "gfxFailure.h"
+#include "gfxPlatform.h"
+#include "gfxUtils.h"
+#include "GLBlitHelper.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "GLLibraryEGL.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include "nsThreadUtils.h"
+#include "ScopedGLHelpers.h"
+#include "TextureImageEGL.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::widget;
+
+#define ADD_ATTR_2(_array, _k, _v) do { \
+ (_array).AppendElement(_k); \
+ (_array).AppendElement(_v); \
+} while (0)
+
+#define ADD_ATTR_1(_array, _k) do { \
+ (_array).AppendElement(_k); \
+} while (0)
+
+static bool
+CreateConfig(EGLConfig* aConfig, nsIWidget* aWidget);
+
+// append three zeros at the end of attribs list to work around
+// EGL implementation bugs that iterate until they find 0, instead of
+// EGL_NONE. See bug 948406.
+#define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
+ LOCAL_EGL_NONE, 0, 0, 0
+
+static EGLint gTerminationAttribs[] = {
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static int
+next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+
+ return v;
+}
+
+static bool
+is_power_of_two(int v)
+{
+ NS_ASSERTION(v >= 0, "bad value");
+
+ if (v == 0)
+ return true;
+
+ return (v & (v-1)) == 0;
+}
+
+static void
+DestroySurface(const EGLSurface surf) {
+ if (!surf) {
+ // Nothing to do.
+ return;
+ }
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surf);
+}
+
+static EGLSurface
+CreateSurfaceForWindow(nsIWidget* widget, const EGLConfig& config) {
+ EGLSurface newSurface = nullptr;
+
+ MOZ_ASSERT(widget);
+ newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config,
+ GET_NATIVE_WINDOW(widget), 0);
+ return newSurface;
+}
+
+GLContextEGL::GLContextEGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ GLContext* shareContext, bool isOffscreen, EGLConfig config,
+ EGLSurface surface, EGLContext context)
+ : GLContext(flags, caps, shareContext, isOffscreen)
+ , mConfig(config)
+ , mSurface(surface)
+ , mContext(context)
+ , mSurfaceOverride(EGL_NO_SURFACE)
+ , mThebesSurface(nullptr)
+ , mBound(false)
+ , mIsPBuffer(false)
+ , mIsDoubleBuffered(false)
+ , mCanBindToTexture(false)
+ , mShareWithEGLImage(false)
+ , mOwnsContext(true)
+{
+ // any EGL contexts will always be GLESv2
+ SetProfileVersion(ContextProfile::OpenGLES, 200);
+
+#ifdef DEBUG
+ printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
+#endif
+}
+
+GLContextEGL::~GLContextEGL()
+{
+ MarkDestroyed();
+
+ // Wrapped context should not destroy eglContext/Surface
+ if (!mOwnsContext) {
+ return;
+ }
+
+#ifdef DEBUG
+ printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
+#endif
+
+ sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext);
+ sEGLLibrary.UnsetCachedCurrentContext();
+
+ DestroySurface(mSurface);
+}
+
+bool
+GLContextEGL::Init()
+{
+ if (!OpenLibrary(GLES2_LIB)) {
+#if defined(XP_UNIX)
+ if (!OpenLibrary(GLES2_LIB2)) {
+ NS_WARNING("Couldn't load GLES2 LIB.");
+ return false;
+ }
+#endif
+ }
+
+ SetupLookupFunction();
+ if (!InitWithPrefix("gl", true))
+ return false;
+
+ MOZ_ASSERT(IsCurrent());
+
+ static_assert(sizeof(GLint) >= sizeof(int32_t), "GLint is smaller than int32_t");
+ mMaxTextureImageSize = INT32_MAX;
+
+ mShareWithEGLImage = sEGLLibrary.HasKHRImageBase() &&
+ sEGLLibrary.HasKHRImageTexture2D() &&
+ IsExtensionSupported(OES_EGL_image);
+
+ return true;
+}
+
+bool
+GLContextEGL::BindTexImage()
+{
+ if (!mSurface)
+ return false;
+
+ if (mBound && !ReleaseTexImage())
+ return false;
+
+ EGLBoolean success = sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = true;
+ return true;
+}
+
+bool
+GLContextEGL::ReleaseTexImage()
+{
+ if (!mBound)
+ return true;
+
+ if (!mSurface)
+ return false;
+
+ EGLBoolean success;
+ success = sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface,
+ LOCAL_EGL_BACK_BUFFER);
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = false;
+ return true;
+}
+
+void
+GLContextEGL::SetEGLSurfaceOverride(const EGLSurface surf) {
+
+ MOZ_ASSERT(!surf || surf != mSurface);
+
+ if (Screen()) {
+ /* Blit `draw` to `read` if we need to, before we potentially juggle
+ * `read` around. If we don't, we might attach a different `read`,
+ * and *then* hit AssureBlitted, which will blit a dirty `draw` onto
+ * the wrong `read`!
+ */
+ Screen()->AssureBlitted();
+ }
+
+ mSurfaceOverride = surf;
+ MOZ_ALWAYS_TRUE(MakeCurrent(true));
+}
+
+bool
+GLContextEGL::MakeCurrentImpl(bool aForce) {
+ if (IsDestroyed()) {
+ //Clear and exit
+ sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), nullptr, nullptr, nullptr);
+ return false;
+ }
+
+ bool succeeded = true;
+
+ // Assume that EGL has the same problem as WGL does,
+ // where MakeCurrent with an already-current context is
+ // still expensive.
+ bool hasDifferentContext = false;
+ if (sEGLLibrary.CachedCurrentContext() != mContext) {
+ // even if the cached context doesn't match the current one
+ // might still
+ if (sEGLLibrary.fGetCurrentContext() != mContext) {
+ hasDifferentContext = true;
+ } else {
+ sEGLLibrary.SetCachedCurrentContext(mContext);
+ }
+ }
+
+ if (aForce || hasDifferentContext) {
+ EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
+ ? mSurfaceOverride
+ : mSurface;
+ if (surface == EGL_NO_SURFACE) {
+ return false;
+ }
+ succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
+ surface, surface,
+ mContext);
+ if (!succeeded) {
+ int eglError = sEGLLibrary.fGetError();
+ if (eglError == LOCAL_EGL_CONTEXT_LOST) {
+ mContextLost = true;
+ NS_WARNING("EGL context has been lost.");
+ } else {
+ NS_WARNING("Failed to make GL context current!");
+#ifdef DEBUG
+ printf_stderr("EGL Error: 0x%04x\n", eglError);
+#endif
+ }
+ } else {
+ sEGLLibrary.SetCachedCurrentContext(mContext);
+ }
+ } else {
+ MOZ_ASSERT(sEGLLibrary.CachedCurrentContextMatches());
+ }
+
+ return succeeded;
+}
+
+bool
+GLContextEGL::IsCurrent() {
+ return sEGLLibrary.fGetCurrentContext() == mContext;
+}
+
+bool
+GLContextEGL::RenewSurface(nsIWidget* const aWidget) {
+ if (!mOwnsContext) {
+ return false;
+ }
+ // unconditionally release the surface and create a new one. Don't try to optimize this away.
+ // If we get here, then by definition we know that we want to get a new surface.
+ ReleaseSurface();
+ mSurface = mozilla::gl::CreateSurfaceForWindow(aWidget, mConfig);
+ if (!mSurface) {
+ return false;
+ }
+ return MakeCurrent(true);
+}
+
+void
+GLContextEGL::ReleaseSurface() {
+ if (!mOwnsContext) {
+ return;
+ }
+
+ DestroySurface(mSurface);
+ mSurface = nullptr;
+}
+
+bool
+GLContextEGL::SetupLookupFunction()
+{
+ mLookupFunc = (PlatformLookupFunction)sEGLLibrary.mSymbols.fGetProcAddress;
+ return true;
+}
+
+bool
+GLContextEGL::SwapBuffers()
+{
+ EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
+ ? mSurfaceOverride
+ : mSurface;
+ if (surface) {
+ return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), surface);
+ } else {
+ return false;
+ }
+}
+
+void
+GLContextEGL::GetWSIInfo(nsCString* const out) const
+{
+ out->AppendLiteral("EGL_VENDOR: ");
+ out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VENDOR));
+
+ out->AppendLiteral("\nEGL_VERSION: ");
+ out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VERSION));
+
+ out->AppendLiteral("\nEGL_EXTENSIONS: ");
+ out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_EXTENSIONS));
+
+ out->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
+ out->Append((const char*)sEGLLibrary.fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
+}
+
+// hold a reference to the given surface
+// for the lifetime of this context.
+void
+GLContextEGL::HoldSurface(gfxASurface* aSurf) {
+ mThebesSurface = aSurf;
+}
+
+/* static */ EGLSurface
+GLContextEGL::CreateSurfaceForWindow(nsIWidget* aWidget)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library!\n");
+ return nullptr;
+ }
+
+ EGLConfig config;
+ if (!CreateConfig(&config, aWidget)) {
+ MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
+ return nullptr;
+ }
+
+ EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
+ if (!surface) {
+ MOZ_CRASH("GFX: Failed to create EGLSurface for window!\n");
+ return nullptr;
+ }
+ return surface;
+}
+
+/* static */ void
+GLContextEGL::DestroySurface(EGLSurface aSurface)
+{
+ if (aSurface != EGL_NO_SURFACE) {
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), aSurface);
+ }
+}
+
+already_AddRefed<GLContextEGL>
+GLContextEGL::CreateGLContext(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContextEGL* shareContext,
+ bool isOffscreen,
+ EGLConfig config,
+ EGLSurface surface,
+ nsACString* const out_failureId)
+{
+ if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
+ NS_WARNING("Failed to bind API to GLES!");
+ return nullptr;
+ }
+
+ EGLContext eglShareContext = shareContext ? shareContext->mContext
+ : EGL_NO_CONTEXT;
+
+ nsTArray<EGLint> contextAttribs;
+
+ contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_CLIENT_VERSION);
+ if (flags & CreateContextFlags::PREFER_ES3)
+ contextAttribs.AppendElement(3);
+ else
+ contextAttribs.AppendElement(2);
+
+ if (sEGLLibrary.HasRobustness()) {
+// contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT);
+// contextAttribs.AppendElement(LOCAL_EGL_TRUE);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+ contextAttribs.AppendElement(gTerminationAttribs[i]);
+ }
+
+ EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(),
+ config,
+ eglShareContext,
+ contextAttribs.Elements());
+ if (!context && shareContext) {
+ shareContext = nullptr;
+ context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), config, EGL_NO_CONTEXT,
+ contextAttribs.Elements());
+ }
+ if (!context) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_CREATE");
+ NS_WARNING("Failed to create EGLContext!");
+ return nullptr;
+ }
+
+ RefPtr<GLContextEGL> glContext = new GLContextEGL(flags, caps, shareContext,
+ isOffscreen, config, surface,
+ context);
+
+ if (!glContext->Init()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_INIT");
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+EGLSurface
+GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
+ EGLenum bindToTextureFormat,
+ mozilla::gfx::IntSize& pbsize)
+{
+ nsTArray<EGLint> pbattrs(16);
+ EGLSurface surface = nullptr;
+
+TRY_AGAIN_POWER_OF_TWO:
+ pbattrs.Clear();
+ pbattrs.AppendElement(LOCAL_EGL_WIDTH); pbattrs.AppendElement(pbsize.width);
+ pbattrs.AppendElement(LOCAL_EGL_HEIGHT); pbattrs.AppendElement(pbsize.height);
+
+ if (bindToTextureFormat != LOCAL_EGL_NONE) {
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET);
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D);
+
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
+ pbattrs.AppendElement(bindToTextureFormat);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+ pbattrs.AppendElement(gTerminationAttribs[i]);
+ }
+
+ surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
+ if (!surface) {
+ if (!is_power_of_two(pbsize.width) ||
+ !is_power_of_two(pbsize.height))
+ {
+ if (!is_power_of_two(pbsize.width))
+ pbsize.width = next_power_of_two(pbsize.width);
+ if (!is_power_of_two(pbsize.height))
+ pbsize.height = next_power_of_two(pbsize.height);
+
+ NS_WARNING("Failed to create pbuffer, trying power of two dims");
+ goto TRY_AGAIN_POWER_OF_TWO;
+ }
+
+ NS_WARNING("Failed to create pbuffer surface");
+ return nullptr;
+ }
+
+ return surface;
+}
+
+static const EGLint kEGLConfigAttribsOffscreenPBuffer[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ // Old versions of llvmpipe seem to need this to properly create the pbuffer (bug 981856)
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGB16[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 5,
+ LOCAL_EGL_GREEN_SIZE, 6,
+ LOCAL_EGL_BLUE_SIZE, 5,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGB24[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGBA32[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 8,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static bool
+CreateConfig(EGLConfig* aConfig, int32_t depth, nsIWidget* aWidget)
+{
+ EGLConfig configs[64];
+ const EGLint* attribs;
+ EGLint ncfg = ArrayLength(configs);
+
+ switch (depth) {
+ case 16:
+ attribs = kEGLConfigAttribsRGB16;
+ break;
+ case 24:
+ attribs = kEGLConfigAttribsRGB24;
+ break;
+ case 32:
+ attribs = kEGLConfigAttribsRGBA32;
+ break;
+ default:
+ NS_ERROR("Unknown pixel depth");
+ return false;
+ }
+
+ if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs,
+ configs, ncfg, &ncfg) ||
+ ncfg < 1) {
+ return false;
+ }
+
+ for (int j = 0; j < ncfg; ++j) {
+ EGLConfig config = configs[j];
+ EGLint r, g, b, a;
+ if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_RED_SIZE, &r) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_GREEN_SIZE, &g) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_BLUE_SIZE, &b) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_ALPHA_SIZE, &a) &&
+ ((depth == 16 && r == 5 && g == 6 && b == 5) ||
+ (depth == 24 && r == 8 && g == 8 && b == 8) ||
+ (depth == 32 && r == 8 && g == 8 && b == 8 && a == 8)))
+ {
+ *aConfig = config;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if a suitable EGLConfig was found and pass it out
+// through aConfig. Return false otherwise.
+//
+// NB: It's entirely legal for the returned EGLConfig to be valid yet
+// have the value null.
+static bool
+CreateConfig(EGLConfig* aConfig, nsIWidget* aWidget)
+{
+ int32_t depth = gfxPlatform::GetPlatform()->GetScreenDepth();
+ if (!CreateConfig(aConfig, depth, aWidget)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
+ return nullptr;
+ }
+
+ if (!aContext || !aSurface)
+ return nullptr;
+
+ SurfaceCaps caps = SurfaceCaps::Any();
+ EGLConfig config = EGL_NO_CONFIG;
+ RefPtr<GLContextEGL> gl = new GLContextEGL(CreateContextFlags::NONE, caps, nullptr,
+ false, config, (EGLSurface)aSurface,
+ (EGLContext)aContext);
+ gl->SetIsDoubleBuffered(true);
+ gl->mOwnsContext = false;
+
+ return gl.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
+ return nullptr;
+ }
+
+ bool doubleBuffered = true;
+
+ EGLConfig config;
+ if (!CreateConfig(&config, aWidget)) {
+ MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
+ return nullptr;
+ }
+
+ EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
+ if (!surface) {
+ MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::Any();
+ RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(CreateContextFlags::NONE,
+ caps, nullptr, false, config,
+ surface, &discardFailureId);
+ if (!gl) {
+ MOZ_CRASH("GFX: Failed to create EGLContext!\n");
+ mozilla::gl::DestroySurface(surface);
+ return nullptr;
+ }
+
+ gl->MakeCurrent();
+ gl->SetIsDoubleBuffered(doubleBuffered);
+
+ return gl.forget();
+}
+
+static void
+FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
+ bool es3, nsTArray<EGLint>* out)
+{
+ out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
+ out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
+
+ out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE);
+ if (es3) {
+ out->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR);
+ } else {
+ out->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT);
+ }
+
+ out->AppendElement(LOCAL_EGL_RED_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 5);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_GREEN_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 6);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_BLUE_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 5);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_ALPHA_SIZE);
+ if (alpha) {
+ out->AppendElement(bpp16 ? 4 : 8);
+ } else {
+ out->AppendElement(0);
+ }
+
+ out->AppendElement(LOCAL_EGL_DEPTH_SIZE);
+ out->AppendElement(depth ? 16 : 0);
+
+ out->AppendElement(LOCAL_EGL_STENCIL_SIZE);
+ out->AppendElement(stencil ? 8 : 0);
+
+ // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+ out->AppendElement(LOCAL_EGL_NONE);
+ out->AppendElement(0);
+
+ out->AppendElement(0);
+ out->AppendElement(0);
+}
+
+static GLint
+GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib)
+{
+ EGLint bits = 0;
+ egl->fGetConfigAttrib(egl->Display(), config, attrib, &bits);
+ MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
+
+ return bits;
+}
+
+static EGLConfig
+ChooseConfig(GLLibraryEGL* egl, CreateContextFlags flags, const SurfaceCaps& minCaps,
+ SurfaceCaps* const out_configCaps)
+{
+ nsTArray<EGLint> configAttribList;
+ FillContextAttribs(minCaps.alpha, minCaps.depth, minCaps.stencil, minCaps.bpp16,
+ bool(flags & CreateContextFlags::PREFER_ES3), &configAttribList);
+
+ const EGLint* configAttribs = configAttribList.Elements();
+
+ // We're guaranteed to get at least minCaps, and the sorting dictated by the spec for
+ // eglChooseConfig reasonably assures that a reasonable 'best' config is on top.
+ const EGLint kMaxConfigs = 1;
+ EGLConfig configs[kMaxConfigs];
+ EGLint foundConfigs = 0;
+ if (!egl->fChooseConfig(egl->Display(), configAttribs, configs, kMaxConfigs,
+ &foundConfigs)
+ || foundConfigs == 0)
+ {
+ return EGL_NO_CONFIG;
+ }
+
+ EGLConfig config = configs[0];
+
+ *out_configCaps = minCaps; // Pick up any preserve, etc.
+ out_configCaps->color = true;
+ out_configCaps->alpha = bool(GetAttrib(egl, config, LOCAL_EGL_ALPHA_SIZE));
+ out_configCaps->depth = bool(GetAttrib(egl, config, LOCAL_EGL_DEPTH_SIZE));
+ out_configCaps->stencil = bool(GetAttrib(egl, config, LOCAL_EGL_STENCIL_SIZE));
+ out_configCaps->bpp16 = (GetAttrib(egl, config, LOCAL_EGL_RED_SIZE) < 8);
+
+ return config;
+}
+
+/*static*/ already_AddRefed<GLContextEGL>
+GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
+ const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ nsACString* const out_failureId)
+{
+ bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
+ if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) {
+ return nullptr;
+ }
+
+ SurfaceCaps configCaps;
+ EGLConfig config = ChooseConfig(&sEGLLibrary, flags, minCaps, &configCaps);
+ if (config == EGL_NO_CONFIG) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_NO_CONFIG");
+ NS_WARNING("Failed to find a compatible config.");
+ return nullptr;
+ }
+
+ if (GLContext::ShouldSpew()) {
+ sEGLLibrary.DumpEGLConfig(config);
+ }
+
+ mozilla::gfx::IntSize pbSize(size);
+ EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
+ LOCAL_EGL_NONE,
+ pbSize);
+ if (!surface) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_POT");
+ NS_WARNING("Failed to create PBuffer for context!");
+ return nullptr;
+ }
+
+ RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, nullptr,
+ true, config, surface,
+ out_failureId);
+ if (!gl) {
+ NS_WARNING("Failed to create GLContext from PBuffer");
+ sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderEGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
+ SurfaceCaps dummyCaps = SurfaceCaps::Any();
+ return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps,
+ out_failureId);
+}
+
+// Under EGL, on Android, pbuffers are supported fine, though
+// often without the ability to texture from them directly.
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
+ if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) { // Needed for IsANGLE().
+ return nullptr;
+ }
+
+ bool canOffscreenUseHeadless = true;
+ if (sEGLLibrary.IsANGLE()) {
+ // ANGLE needs to use PBuffers.
+ canOffscreenUseHeadless = false;
+ }
+
+ RefPtr<GLContext> gl;
+ SurfaceCaps minOffscreenCaps = minCaps;
+
+ if (canOffscreenUseHeadless) {
+ gl = CreateHeadless(flags, out_failureId);
+ if (!gl) {
+ return nullptr;
+ }
+ } else {
+ SurfaceCaps minBackbufferCaps = minOffscreenCaps;
+ if (minOffscreenCaps.antialias) {
+ minBackbufferCaps.antialias = false;
+ minBackbufferCaps.depth = false;
+ minBackbufferCaps.stencil = false;
+ }
+
+ gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size,
+ minBackbufferCaps,
+ out_failureId);
+ if (!gl)
+ return nullptr;
+
+ // Pull the actual resulting caps to ensure that our offscreen matches our
+ // backbuffer.
+ minOffscreenCaps.alpha = gl->Caps().alpha;
+ if (!minOffscreenCaps.antialias) {
+ // Only update these if we don't have AA. If we do have AA, we ignore
+ // backbuffer depth/stencil.
+ minOffscreenCaps.depth = gl->Caps().depth;
+ minOffscreenCaps.stencil = gl->Caps().stencil;
+ }
+ }
+
+ // Init the offscreen with the updated offscreen caps.
+ if (!gl->InitOffscreen(size, minOffscreenCaps)) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_OFFSCREEN");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+// Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
+// and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
+// and 3) each EGL context eats 750k on B2G (bug 813783)
+/*static*/ GLContext*
+GLContextProviderEGL::GetGlobalContext()
+{
+ return nullptr;
+}
+
+/*static*/ void
+GLContextProviderEGL::Shutdown()
+{
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
diff --git a/system/graphics/gl/GLContextProviderGLX.cpp b/system/graphics/gl/GLContextProviderGLX.cpp
new file mode 100644
index 000000000..c44b1a9f0
--- /dev/null
+++ b/system/graphics/gl/GLContextProviderGLX.cpp
@@ -0,0 +1,1435 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WIDGET_GTK
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#define GET_NATIVE_WINDOW(aWidget) GDK_WINDOW_XID((GdkWindow*) aWidget->GetNativeData(NS_NATIVE_WINDOW))
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "X11UndefineNone.h"
+
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/widget/X11CompositorWidget.h"
+#include "mozilla/Unused.h"
+
+#include "prenv.h"
+#include "GLContextProvider.h"
+#include "GLLibraryLoader.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include "GLXLibrary.h"
+#include "gfxXlibSurface.h"
+#include "gfxContext.h"
+#include "gfxEnv.h"
+#include "gfxPlatform.h"
+#include "GLContextGLX.h"
+#include "gfxUtils.h"
+#include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "gfxPrefs.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+
+GLXLibrary sGLXLibrary;
+
+// Check that we have at least version aMajor.aMinor .
+bool
+GLXLibrary::GLXVersionCheck(int aMajor, int aMinor)
+{
+ return aMajor < mGLXMajorVersion ||
+ (aMajor == mGLXMajorVersion && aMinor <= mGLXMinorVersion);
+}
+
+static inline bool
+HasExtension(const char* aExtensions, const char* aRequiredExtension)
+{
+ return GLContext::ListHasExtension(
+ reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
+}
+
+bool
+GLXLibrary::EnsureInitialized()
+{
+ if (mInitialized) {
+ return true;
+ }
+
+ // Don't repeatedly try to initialize.
+ if (mTriedInitializing) {
+ return false;
+ }
+ mTriedInitializing = true;
+
+ // Force enabling s3 texture compression. (Bug 774134)
+ PR_SetEnv("force_s3tc_enable=true");
+
+ if (!mOGLLibrary) {
+ const char* libGLfilename = nullptr;
+
+ // see e.g. bug 608526: it is intrinsically interesting to know whether we have dynamically linked to libGL.so.1
+ // because at least the NVIDIA implementation requires an executable stack, which causes mprotect calls,
+ // which trigger glibc bug http://sourceware.org/bugzilla/show_bug.cgi?id=12225
+#ifdef __OpenBSD__
+ libGLfilename = "libGL.so";
+#else
+ libGLfilename = "libGL.so.1";
+#endif
+
+ mOGLLibrary = PR_LoadLibrary(libGLfilename);
+ if (!mOGLLibrary) {
+ NS_WARNING("Couldn't load OpenGL shared library.");
+ return false;
+ }
+ }
+
+ if (gfxEnv::GlxDebug()) {
+ mDebug = true;
+ }
+
+ GLLibraryLoader::SymLoadStruct symbols[] = {
+ /* functions that were in GLX 1.0 */
+ { (PRFuncPtr*) &xDestroyContextInternal, { "glXDestroyContext", nullptr } },
+ { (PRFuncPtr*) &xMakeCurrentInternal, { "glXMakeCurrent", nullptr } },
+ { (PRFuncPtr*) &xSwapBuffersInternal, { "glXSwapBuffers", nullptr } },
+ { (PRFuncPtr*) &xQueryVersionInternal, { "glXQueryVersion", nullptr } },
+ { (PRFuncPtr*) &xGetCurrentContextInternal, { "glXGetCurrentContext", nullptr } },
+ { (PRFuncPtr*) &xWaitGLInternal, { "glXWaitGL", nullptr } },
+ { (PRFuncPtr*) &xWaitXInternal, { "glXWaitX", nullptr } },
+ /* functions introduced in GLX 1.1 */
+ { (PRFuncPtr*) &xQueryExtensionsStringInternal, { "glXQueryExtensionsString", nullptr } },
+ { (PRFuncPtr*) &xGetClientStringInternal, { "glXGetClientString", nullptr } },
+ { (PRFuncPtr*) &xQueryServerStringInternal, { "glXQueryServerString", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols13[] = {
+ /* functions introduced in GLX 1.3 */
+ { (PRFuncPtr*) &xChooseFBConfigInternal, { "glXChooseFBConfig", nullptr } },
+ { (PRFuncPtr*) &xGetFBConfigAttribInternal, { "glXGetFBConfigAttrib", nullptr } },
+ // WARNING: xGetFBConfigs not set in symbols13_ext
+ { (PRFuncPtr*) &xGetFBConfigsInternal, { "glXGetFBConfigs", nullptr } },
+ // WARNING: symbols13_ext sets xCreateGLXPixmapWithConfig instead
+ { (PRFuncPtr*) &xCreatePixmapInternal, { "glXCreatePixmap", nullptr } },
+ { (PRFuncPtr*) &xDestroyPixmapInternal, { "glXDestroyPixmap", nullptr } },
+ { (PRFuncPtr*) &xCreateNewContextInternal, { "glXCreateNewContext", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols13_ext[] = {
+ /* extension equivalents for functions introduced in GLX 1.3 */
+ // GLX_SGIX_fbconfig extension
+ { (PRFuncPtr*) &xChooseFBConfigInternal, { "glXChooseFBConfigSGIX", nullptr } },
+ { (PRFuncPtr*) &xGetFBConfigAttribInternal, { "glXGetFBConfigAttribSGIX", nullptr } },
+ // WARNING: no xGetFBConfigs equivalent in extensions
+ // WARNING: different from symbols13:
+ { (PRFuncPtr*) &xCreateGLXPixmapWithConfigInternal, { "glXCreateGLXPixmapWithConfigSGIX", nullptr } },
+ { (PRFuncPtr*) &xDestroyPixmapInternal, { "glXDestroyGLXPixmap", nullptr } }, // not from ext
+ { (PRFuncPtr*) &xCreateNewContextInternal, { "glXCreateContextWithConfigSGIX", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols14[] = {
+ /* functions introduced in GLX 1.4 */
+ { (PRFuncPtr*) &xGetProcAddressInternal, { "glXGetProcAddress", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols14_ext[] = {
+ /* extension equivalents for functions introduced in GLX 1.4 */
+ // GLX_ARB_get_proc_address extension
+ { (PRFuncPtr*) &xGetProcAddressInternal, { "glXGetProcAddressARB", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols_texturefrompixmap[] = {
+ { (PRFuncPtr*) &xBindTexImageInternal, { "glXBindTexImageEXT", nullptr } },
+ { (PRFuncPtr*) &xReleaseTexImageInternal, { "glXReleaseTexImageEXT", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols_createcontext[] = {
+ { (PRFuncPtr*) &xCreateContextAttribsInternal, { "glXCreateContextAttribsARB", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols_videosync[] = {
+ { (PRFuncPtr*) &xGetVideoSyncInternal, { "glXGetVideoSyncSGI", nullptr } },
+ { (PRFuncPtr*) &xWaitVideoSyncInternal, { "glXWaitVideoSyncSGI", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ GLLibraryLoader::SymLoadStruct symbols_swapcontrol[] = {
+ { (PRFuncPtr*) &xSwapIntervalInternal, { "glXSwapIntervalEXT", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &symbols[0])) {
+ NS_WARNING("Couldn't find required entry point in OpenGL shared library");
+ return false;
+ }
+
+ Display* display = DefaultXDisplay();
+ int screen = DefaultScreen(display);
+
+ if (!xQueryVersion(display, &mGLXMajorVersion, &mGLXMinorVersion)) {
+ mGLXMajorVersion = 0;
+ mGLXMinorVersion = 0;
+ return false;
+ }
+
+ if (!GLXVersionCheck(1, 1))
+ // Not possible to query for extensions.
+ return false;
+
+ const char* clientVendor = xGetClientString(display, LOCAL_GLX_VENDOR);
+ const char* serverVendor = xQueryServerString(display, screen, LOCAL_GLX_VENDOR);
+ const char* extensionsStr = xQueryExtensionsString(display, screen);
+
+ GLLibraryLoader::SymLoadStruct* sym13;
+ if (!GLXVersionCheck(1, 3)) {
+ // Even if we don't have 1.3, we might have equivalent extensions
+ // (as on the Intel X server).
+ if (!HasExtension(extensionsStr, "GLX_SGIX_fbconfig")) {
+ return false;
+ }
+ sym13 = symbols13_ext;
+ } else {
+ sym13 = symbols13;
+ }
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, sym13)) {
+ NS_WARNING("Couldn't find required entry point in OpenGL shared library");
+ return false;
+ }
+
+ GLLibraryLoader::SymLoadStruct* sym14;
+ if (!GLXVersionCheck(1, 4)) {
+ // Even if we don't have 1.4, we might have equivalent extensions
+ // (as on the Intel X server).
+ if (!HasExtension(extensionsStr, "GLX_ARB_get_proc_address")) {
+ return false;
+ }
+ sym14 = symbols14_ext;
+ } else {
+ sym14 = symbols14;
+ }
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, sym14)) {
+ NS_WARNING("Couldn't find required entry point in OpenGL shared library");
+ return false;
+ }
+
+ if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") &&
+ GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_texturefrompixmap,
+ (GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
+ {
+ mUseTextureFromPixmap = gfxPrefs::UseGLXTextureFromPixmap();
+ } else {
+ mUseTextureFromPixmap = false;
+ NS_WARNING("Texture from pixmap disabled");
+ }
+
+ if (HasExtension(extensionsStr, "GLX_ARB_create_context") &&
+ HasExtension(extensionsStr, "GLX_ARB_create_context_profile") &&
+ GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_createcontext,
+ (GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
+ {
+ mHasCreateContextAttribs = true;
+ }
+
+ if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness"))
+ {
+ mHasRobustness = true;
+ }
+
+ if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
+ GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_videosync,
+ (GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
+ {
+ mHasVideoSync = true;
+ }
+
+ if (!(HasExtension(extensionsStr, "GLX_EXT_swap_control") &&
+ GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_swapcontrol)))
+ {
+ NS_WARNING("GLX_swap_control unsupported, ASAP mode may still block on buffer swaps.");
+ }
+
+ mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
+ mIsNVIDIA = serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
+ mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
+
+ mInitialized = true;
+
+ return true;
+}
+
+bool
+GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface)
+{
+ if (!EnsureInitialized()) {
+ return false;
+ }
+
+ if (aSurface->GetType() != gfxSurfaceType::Xlib || !mUseTextureFromPixmap) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+GLXLibrary::SupportsVideoSync()
+{
+ if (!EnsureInitialized()) {
+ return false;
+ }
+
+ return mHasVideoSync;
+}
+
+GLXPixmap
+GLXLibrary::CreatePixmap(gfxASurface* aSurface)
+{
+ if (!SupportsTextureFromPixmap(aSurface)) {
+ return X11None;
+ }
+
+ gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface);
+ const XRenderPictFormat* format = xs->XRenderFormat();
+ if (!format || format->type != PictTypeDirect) {
+ return X11None;
+ }
+ const XRenderDirectFormat& direct = format->direct;
+ int alphaSize = FloorLog2(direct.alphaMask + 1);
+ NS_ASSERTION((1 << alphaSize) - 1 == direct.alphaMask,
+ "Unexpected render format with non-adjacent alpha bits");
+
+ int attribs[] = { LOCAL_GLX_DOUBLEBUFFER, False,
+ LOCAL_GLX_DRAWABLE_TYPE, LOCAL_GLX_PIXMAP_BIT,
+ LOCAL_GLX_ALPHA_SIZE, alphaSize,
+ (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT
+ : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT), True,
+ LOCAL_GLX_RENDER_TYPE, LOCAL_GLX_RGBA_BIT,
+ X11None };
+
+ int numConfigs = 0;
+ Display* display = xs->XDisplay();
+ int xscreen = DefaultScreen(display);
+
+ ScopedXFree<GLXFBConfig> cfgs(xChooseFBConfig(display,
+ xscreen,
+ attribs,
+ &numConfigs));
+
+ // Find an fbconfig that matches the pixel format used on the Pixmap.
+ int matchIndex = -1;
+ unsigned long redMask =
+ static_cast<unsigned long>(direct.redMask) << direct.red;
+ unsigned long greenMask =
+ static_cast<unsigned long>(direct.greenMask) << direct.green;
+ unsigned long blueMask =
+ static_cast<unsigned long>(direct.blueMask) << direct.blue;
+ // This is true if the Pixmap has bits for alpha or unused bits.
+ bool haveNonColorBits =
+ ~(redMask | greenMask | blueMask) != -1UL << format->depth;
+
+ for (int i = 0; i < numConfigs; i++) {
+ int id = X11None;
+ sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id);
+ Visual* visual;
+ int depth;
+ FindVisualAndDepth(display, id, &visual, &depth);
+ if (!visual ||
+ visual->c_class != TrueColor ||
+ visual->red_mask != redMask ||
+ visual->green_mask != greenMask ||
+ visual->blue_mask != blueMask ) {
+ continue;
+ }
+
+ // Historically Xlib Visuals did not try to represent an alpha channel
+ // and there was no means to use an alpha channel on a Pixmap. The
+ // Xlib Visual from the fbconfig was not intended to have any
+ // information about alpha bits.
+ //
+ // Since then, RENDER has added formats for 32 bit depth Pixmaps.
+ // Some of these formats have bits for alpha and some have unused
+ // bits.
+ //
+ // Then the Composite extension added a 32 bit depth Visual intended
+ // for Windows with an alpha channel, so bits not in the visual color
+ // masks were expected to be treated as alpha bits.
+ //
+ // Usually GLX counts only color bits in the Visual depth, but the
+ // depth of Composite's ARGB Visual includes alpha bits. However,
+ // bits not in the color masks are not necessarily alpha bits because
+ // sometimes (NVIDIA) 32 bit Visuals are added for fbconfigs with 32
+ // bit BUFFER_SIZE but zero alpha bits and 24 color bits (NVIDIA
+ // again).
+ //
+ // This checks that the depth matches in one of the two ways.
+ // NVIDIA now forces format->depth == depth so only the first way
+ // is checked for NVIDIA
+ if (depth != format->depth &&
+ (mIsNVIDIA || depth != format->depth - alphaSize) ) {
+ continue;
+ }
+
+ // If all bits of the Pixmap are color bits and the Pixmap depth
+ // matches the depth of the fbconfig visual, then we can assume that
+ // the driver will do whatever is necessary to ensure that any
+ // GLXPixmap alpha bits are treated as set. We can skip the
+ // ALPHA_SIZE check in this situation. We need to skip this check for
+ // situations (ATI) where there are no fbconfigs without alpha bits.
+ //
+ // glXChooseFBConfig should prefer configs with smaller
+ // LOCAL_GLX_BUFFER_SIZE, so we should still get zero alpha bits if
+ // available, except perhaps with NVIDIA drivers where buffer size is
+ // not the specified sum of the component sizes.
+ if (haveNonColorBits) {
+ // There are bits in the Pixmap format that haven't been matched
+ // against the fbconfig visual. These bits could either represent
+ // alpha or be unused, so just check that the number of alpha bits
+ // matches.
+ int size = 0;
+ sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i],
+ LOCAL_GLX_ALPHA_SIZE, &size);
+ if (size != alphaSize) {
+ continue;
+ }
+ }
+
+ matchIndex = i;
+ break;
+ }
+ if (matchIndex == -1) {
+ // GLX can't handle A8 surfaces, so this is not really unexpected. The
+ // caller should deal with this situation.
+ NS_WARNING_ASSERTION(
+ format->depth == 8,
+ "[GLX] Couldn't find a FBConfig matching Pixmap format");
+ return X11None;
+ }
+
+ int pixmapAttribs[] = { LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT,
+ LOCAL_GLX_TEXTURE_FORMAT_EXT,
+ (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT
+ : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT),
+ X11None};
+
+ GLXPixmap glxpixmap = xCreatePixmap(display,
+ cfgs[matchIndex],
+ xs->XDrawable(),
+ pixmapAttribs);
+
+ return glxpixmap;
+}
+
+void
+GLXLibrary::DestroyPixmap(Display* aDisplay, GLXPixmap aPixmap)
+{
+ if (!mUseTextureFromPixmap) {
+ return;
+ }
+
+ xDestroyPixmap(aDisplay, aPixmap);
+}
+
+void
+GLXLibrary::BindTexImage(Display* aDisplay, GLXPixmap aPixmap)
+{
+ if (!mUseTextureFromPixmap) {
+ return;
+ }
+
+ // Make sure all X drawing to the surface has finished before binding to a texture.
+ if (mClientIsMesa) {
+ // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a
+ // noop when direct rendering unless the current drawable is a
+ // single-buffer window.
+ FinishX(aDisplay);
+ } else {
+ xWaitX();
+ }
+ xBindTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT, nullptr);
+}
+
+void
+GLXLibrary::ReleaseTexImage(Display* aDisplay, GLXPixmap aPixmap)
+{
+ if (!mUseTextureFromPixmap) {
+ return;
+ }
+
+ xReleaseTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT);
+}
+
+void
+GLXLibrary::UpdateTexImage(Display* aDisplay, GLXPixmap aPixmap)
+{
+ // NVIDIA drivers don't require a rebind of the pixmap in order
+ // to display an updated image, and it's faster not to do it.
+ if (mIsNVIDIA) {
+ xWaitX();
+ return;
+ }
+
+ ReleaseTexImage(aDisplay, aPixmap);
+ BindTexImage(aDisplay, aPixmap);
+}
+
+#ifdef DEBUG
+
+static int (*sOldErrorHandler)(Display*, XErrorEvent*);
+ScopedXErrorHandler::ErrorEvent sErrorEvent;
+static int GLXErrorHandler(Display* display, XErrorEvent* ev)
+{
+ if (!sErrorEvent.mError.error_code) {
+ sErrorEvent.mError = *ev;
+ }
+ return 0;
+}
+
+void
+GLXLibrary::BeforeGLXCall()
+{
+ if (mDebug) {
+ sOldErrorHandler = XSetErrorHandler(GLXErrorHandler);
+ }
+}
+
+void
+GLXLibrary::AfterGLXCall()
+{
+ if (mDebug) {
+ FinishX(DefaultXDisplay());
+ if (sErrorEvent.mError.error_code) {
+ char buffer[2048];
+ XGetErrorText(DefaultXDisplay(), sErrorEvent.mError.error_code, buffer, sizeof(buffer));
+ printf_stderr("X ERROR: %s (%i) - Request: %i.%i, Serial: %lu",
+ buffer,
+ sErrorEvent.mError.error_code,
+ sErrorEvent.mError.request_code,
+ sErrorEvent.mError.minor_code,
+ sErrorEvent.mError.serial);
+ NS_ABORT();
+ }
+ XSetErrorHandler(sOldErrorHandler);
+ }
+}
+
+#define BEFORE_GLX_CALL do { \
+ sGLXLibrary.BeforeGLXCall(); \
+} while (0)
+
+#define AFTER_GLX_CALL do { \
+ sGLXLibrary.AfterGLXCall(); \
+} while (0)
+
+#else
+
+#define BEFORE_GLX_CALL do { } while(0)
+#define AFTER_GLX_CALL do { } while(0)
+
+#endif
+
+void
+GLXLibrary::xDestroyContext(Display* display, GLXContext context)
+{
+ BEFORE_GLX_CALL;
+ xDestroyContextInternal(display, context);
+ AFTER_GLX_CALL;
+}
+
+Bool
+GLXLibrary::xMakeCurrent(Display* display,
+ GLXDrawable drawable,
+ GLXContext context)
+{
+ BEFORE_GLX_CALL;
+ Bool result = xMakeCurrentInternal(display, drawable, context);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXContext
+GLXLibrary::xGetCurrentContext()
+{
+ BEFORE_GLX_CALL;
+ GLXContext result = xGetCurrentContextInternal();
+ AFTER_GLX_CALL;
+ return result;
+}
+
+/* static */ void*
+GLXLibrary::xGetProcAddress(const char* procName)
+{
+ BEFORE_GLX_CALL;
+ void* result = sGLXLibrary.xGetProcAddressInternal(procName);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXFBConfig*
+GLXLibrary::xChooseFBConfig(Display* display,
+ int screen,
+ const int* attrib_list,
+ int* nelements)
+{
+ BEFORE_GLX_CALL;
+ GLXFBConfig* result = xChooseFBConfigInternal(display, screen, attrib_list, nelements);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXFBConfig*
+GLXLibrary::xGetFBConfigs(Display* display,
+ int screen,
+ int* nelements)
+{
+ BEFORE_GLX_CALL;
+ GLXFBConfig* result = xGetFBConfigsInternal(display, screen, nelements);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXContext
+GLXLibrary::xCreateNewContext(Display* display,
+ GLXFBConfig config,
+ int render_type,
+ GLXContext share_list,
+ Bool direct)
+{
+ BEFORE_GLX_CALL;
+ GLXContext result = xCreateNewContextInternal(display, config,
+ render_type,
+ share_list, direct);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+int
+GLXLibrary::xGetFBConfigAttrib(Display* display,
+ GLXFBConfig config,
+ int attribute,
+ int* value)
+{
+ BEFORE_GLX_CALL;
+ int result = xGetFBConfigAttribInternal(display, config,
+ attribute, value);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+void
+GLXLibrary::xSwapBuffers(Display* display, GLXDrawable drawable)
+{
+ BEFORE_GLX_CALL;
+ xSwapBuffersInternal(display, drawable);
+ AFTER_GLX_CALL;
+}
+
+const char*
+GLXLibrary::xQueryExtensionsString(Display* display,
+ int screen)
+{
+ BEFORE_GLX_CALL;
+ const char* result = xQueryExtensionsStringInternal(display, screen);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+const char*
+GLXLibrary::xGetClientString(Display* display,
+ int screen)
+{
+ BEFORE_GLX_CALL;
+ const char* result = xGetClientStringInternal(display, screen);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+const char*
+GLXLibrary::xQueryServerString(Display* display,
+ int screen, int name)
+{
+ BEFORE_GLX_CALL;
+ const char* result = xQueryServerStringInternal(display, screen, name);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXPixmap
+GLXLibrary::xCreatePixmap(Display* display,
+ GLXFBConfig config,
+ Pixmap pixmap,
+ const int* attrib_list)
+{
+ BEFORE_GLX_CALL;
+ GLXPixmap result = xCreatePixmapInternal(display, config,
+ pixmap, attrib_list);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+GLXPixmap
+GLXLibrary::xCreateGLXPixmapWithConfig(Display* display,
+ GLXFBConfig config,
+ Pixmap pixmap)
+{
+ BEFORE_GLX_CALL;
+ GLXPixmap result = xCreateGLXPixmapWithConfigInternal(display, config, pixmap);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+void
+GLXLibrary::xDestroyPixmap(Display* display, GLXPixmap pixmap)
+{
+ BEFORE_GLX_CALL;
+ xDestroyPixmapInternal(display, pixmap);
+ AFTER_GLX_CALL;
+}
+
+Bool
+GLXLibrary::xQueryVersion(Display* display,
+ int* major,
+ int* minor)
+{
+ BEFORE_GLX_CALL;
+ Bool result = xQueryVersionInternal(display, major, minor);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+void
+GLXLibrary::xBindTexImage(Display* display,
+ GLXDrawable drawable,
+ int buffer,
+ const int* attrib_list)
+{
+ BEFORE_GLX_CALL;
+ xBindTexImageInternal(display, drawable, buffer, attrib_list);
+ AFTER_GLX_CALL;
+}
+
+void
+GLXLibrary::xReleaseTexImage(Display* display,
+ GLXDrawable drawable,
+ int buffer)
+{
+ BEFORE_GLX_CALL;
+ xReleaseTexImageInternal(display, drawable, buffer);
+ AFTER_GLX_CALL;
+}
+
+void
+GLXLibrary::xWaitGL()
+{
+ BEFORE_GLX_CALL;
+ xWaitGLInternal();
+ AFTER_GLX_CALL;
+}
+
+void
+GLXLibrary::xWaitX()
+{
+ BEFORE_GLX_CALL;
+ xWaitXInternal();
+ AFTER_GLX_CALL;
+}
+
+GLXContext
+GLXLibrary::xCreateContextAttribs(Display* display,
+ GLXFBConfig config,
+ GLXContext share_list,
+ Bool direct,
+ const int* attrib_list)
+{
+ BEFORE_GLX_CALL;
+ GLXContext result = xCreateContextAttribsInternal(display,
+ config,
+ share_list,
+ direct,
+ attrib_list);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+int
+GLXLibrary::xGetVideoSync(unsigned int* count)
+{
+ BEFORE_GLX_CALL;
+ int result = xGetVideoSyncInternal(count);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+int
+GLXLibrary::xWaitVideoSync(int divisor, int remainder, unsigned int* count)
+{
+ BEFORE_GLX_CALL;
+ int result = xWaitVideoSyncInternal(divisor, remainder, count);
+ AFTER_GLX_CALL;
+ return result;
+}
+
+void
+GLXLibrary::xSwapInterval(Display* display, GLXDrawable drawable, int interval)
+{
+ BEFORE_GLX_CALL;
+ xSwapIntervalInternal(display, drawable, interval);
+ AFTER_GLX_CALL;
+}
+
+already_AddRefed<GLContextGLX>
+GLContextGLX::CreateGLContext(CreateContextFlags flags, const SurfaceCaps& caps,
+ GLContextGLX* shareContext, bool isOffscreen,
+ Display* display, GLXDrawable drawable, GLXFBConfig cfg,
+ bool deleteDrawable, gfxXlibSurface* pixmap,
+ ContextProfile profile)
+{
+ GLXLibrary& glx = sGLXLibrary;
+
+ int db = 0;
+ int err = glx.xGetFBConfigAttrib(display, cfg,
+ LOCAL_GLX_DOUBLEBUFFER, &db);
+ if (LOCAL_GLX_BAD_ATTRIBUTE != err) {
+ if (ShouldSpew()) {
+ printf("[GLX] FBConfig is %sdouble-buffered\n", db ? "" : "not ");
+ }
+ }
+
+ GLXContext context;
+ RefPtr<GLContextGLX> glContext;
+ bool error;
+
+ OffMainThreadScopedXErrorHandler xErrorHandler;
+
+ do {
+ error = false;
+
+ GLXContext glxContext = shareContext ? shareContext->mContext : nullptr;
+ if (glx.HasCreateContextAttribs()) {
+ AutoTArray<int, 11> attrib_list;
+ if (glx.HasRobustness()) {
+ int robust_attribs[] = {
+ LOCAL_GL_CONTEXT_FLAGS_ARB, LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+ LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_GL_LOSE_CONTEXT_ON_RESET_ARB,
+ };
+ attrib_list.AppendElements(robust_attribs, MOZ_ARRAY_LENGTH(robust_attribs));
+ }
+ if (profile == ContextProfile::OpenGLCore) {
+ int core_attribs[] = {
+ LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB, 2,
+ LOCAL_GLX_CONTEXT_FLAGS_ARB, LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ };
+ attrib_list.AppendElements(core_attribs, MOZ_ARRAY_LENGTH(core_attribs));
+ };
+ attrib_list.AppendElement(0);
+
+ context = glx.xCreateContextAttribs(
+ display,
+ cfg,
+ glxContext,
+ True,
+ attrib_list.Elements());
+ } else {
+ context = glx.xCreateNewContext(
+ display,
+ cfg,
+ LOCAL_GLX_RGBA_TYPE,
+ glxContext,
+ True);
+ }
+
+ if (context) {
+ glContext = new GLContextGLX(flags, caps, shareContext, isOffscreen, display,
+ drawable, context, deleteDrawable, db, pixmap,
+ profile);
+ if (!glContext->Init())
+ error = true;
+ } else {
+ error = true;
+ }
+
+ error |= xErrorHandler.SyncAndGetError(display);
+
+ if (error) {
+ if (shareContext) {
+ shareContext = nullptr;
+ continue;
+ }
+
+ NS_WARNING("Failed to create GLXContext!");
+ glContext = nullptr; // note: this must be done while the graceful X error handler is set,
+ // because glxMakeCurrent can give a GLXBadDrawable error
+ }
+
+ return glContext.forget();
+ } while (true);
+}
+
+GLContextGLX::~GLContextGLX()
+{
+ MarkDestroyed();
+
+ // Wrapped context should not destroy glxContext/Surface
+ if (!mOwnsContext) {
+ return;
+ }
+
+ mGLX->xDestroyContext(mDisplay, mContext);
+
+ if (mDeleteDrawable) {
+ mGLX->xDestroyPixmap(mDisplay, mDrawable);
+ }
+ MOZ_ASSERT(!mOverrideDrawable);
+}
+
+
+bool
+GLContextGLX::Init()
+{
+ SetupLookupFunction();
+ if (!InitWithPrefix("gl", true)) {
+ return false;
+ }
+
+ // EXT_framebuffer_object is not supported on Core contexts
+ // so we'll also check for ARB_framebuffer_object
+ if (!IsExtensionSupported(EXT_framebuffer_object) && !IsSupported(GLFeature::framebuffer_object))
+ return false;
+
+ return true;
+}
+
+bool
+GLContextGLX::MakeCurrentImpl(bool aForce)
+{
+ bool succeeded = true;
+
+ // With the ATI FGLRX driver, glxMakeCurrent is very slow even when the context doesn't change.
+ // (This is not the case with other drivers such as NVIDIA).
+ // So avoid calling it more than necessary. Since GLX documentation says that:
+ // "glXGetCurrentContext returns client-side information.
+ // It does not make a round trip to the server."
+ // I assume that it's not worth using our own TLS slot here.
+ if (aForce || mGLX->xGetCurrentContext() != mContext) {
+ if (mGLX->IsMesa()) {
+ // Read into the event queue to ensure that Mesa receives a
+ // DRI2InvalidateBuffers event before drawing. See bug 1280653.
+ Unused << XPending(mDisplay);
+ }
+ if (IsDestroyed()) {
+ MOZ_ALWAYS_TRUE(mGLX->xMakeCurrent(mDisplay, X11None, nullptr));
+ return false; // We did not MakeCurrent mContext, but that's what we wanted!
+ }
+
+ succeeded = mGLX->xMakeCurrent(mDisplay, mDrawable, mContext);
+ NS_ASSERTION(succeeded, "Failed to make GL context current!");
+
+ if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
+ // Many GLX implementations default to blocking until the next
+ // VBlank when calling glXSwapBuffers. We want to run unthrottled
+ // in ASAP mode. See bug 1280744.
+ const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0);
+ mGLX->xSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1);
+ }
+ }
+
+ return succeeded;
+}
+
+bool
+GLContextGLX::IsCurrent() {
+ return mGLX->xGetCurrentContext() == mContext;
+}
+
+bool
+GLContextGLX::SetupLookupFunction()
+{
+ mLookupFunc = (PlatformLookupFunction)&GLXLibrary::xGetProcAddress;
+ return true;
+}
+
+bool
+GLContextGLX::IsDoubleBuffered() const
+{
+ return mDoubleBuffered;
+}
+
+bool
+GLContextGLX::SupportsRobustness() const
+{
+ return mGLX->HasRobustness();
+}
+
+bool
+GLContextGLX::SwapBuffers()
+{
+ if (!mDoubleBuffered)
+ return false;
+ mGLX->xSwapBuffers(mDisplay, mDrawable);
+ return true;
+}
+
+void
+GLContextGLX::GetWSIInfo(nsCString* const out) const
+{
+ Display* display = DefaultXDisplay();
+ int screen = DefaultScreen(display);
+
+ int majorVersion, minorVersion;
+ sGLXLibrary.xQueryVersion(display, &majorVersion, &minorVersion);
+
+ out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
+
+ out->AppendLiteral("\nGLX_VENDOR(client): ");
+ out->Append(sGLXLibrary.xGetClientString(display, LOCAL_GLX_VENDOR));
+
+ out->AppendLiteral("\nGLX_VENDOR(server): ");
+ out->Append(sGLXLibrary.xQueryServerString(display, screen, LOCAL_GLX_VENDOR));
+
+ out->AppendLiteral("\nExtensions: ");
+ out->Append(sGLXLibrary.xQueryExtensionsString(display, screen));
+}
+
+bool
+GLContextGLX::OverrideDrawable(GLXDrawable drawable)
+{
+ if (Screen()) {
+ Screen()->AssureBlitted();
+ }
+ mOverrideDrawable = Some(drawable);
+ return MakeCurrent(true);
+}
+
+bool
+GLContextGLX::RestoreDrawable()
+{
+ mOverrideDrawable = Nothing();
+ return MakeCurrent(true);
+}
+
+GLContextGLX::GLContextGLX(
+ CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContext* shareContext,
+ bool isOffscreen,
+ Display* aDisplay,
+ GLXDrawable aDrawable,
+ GLXContext aContext,
+ bool aDeleteDrawable,
+ bool aDoubleBuffered,
+ gfxXlibSurface* aPixmap,
+ ContextProfile profile)
+ : GLContext(flags, caps, shareContext, isOffscreen),
+ mContext(aContext),
+ mDisplay(aDisplay),
+ mDrawable(aDrawable),
+ mDeleteDrawable(aDeleteDrawable),
+ mDoubleBuffered(aDoubleBuffered),
+ mGLX(&sGLXLibrary),
+ mPixmap(aPixmap),
+ mOwnsContext(true)
+{
+ MOZ_ASSERT(mGLX);
+ // See 899855
+ SetProfileVersion(profile, 200);
+}
+
+
+static GLContextGLX*
+GetGlobalContextGLX()
+{
+ return static_cast<GLContextGLX*>(GLContextProviderGLX::GetGlobalContext());
+}
+
+static bool
+AreCompatibleVisuals(Visual* one, Visual* two)
+{
+ if (one->c_class != two->c_class) {
+ return false;
+ }
+
+ if (one->red_mask != two->red_mask ||
+ one->green_mask != two->green_mask ||
+ one->blue_mask != two->blue_mask) {
+ return false;
+ }
+
+ if (one->bits_per_rgb != two->bits_per_rgb) {
+ return false;
+ }
+
+ return true;
+}
+
+static StaticRefPtr<GLContext> gGlobalContext;
+
+already_AddRefed<GLContext>
+GLContextProviderGLX::CreateWrappingExisting(void* aContext, void* aSurface)
+{
+ if (!sGLXLibrary.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ if (aContext && aSurface) {
+ SurfaceCaps caps = SurfaceCaps::Any();
+ RefPtr<GLContextGLX> glContext =
+ new GLContextGLX(CreateContextFlags::NONE, caps,
+ nullptr, // SharedContext
+ false, // Offscreen
+ (Display*)DefaultXDisplay(), // Display
+ (GLXDrawable)aSurface, (GLXContext)aContext,
+ false, // aDeleteDrawable,
+ true,
+ (gfxXlibSurface*)nullptr,
+ ContextProfile::OpenGLCompatibility);
+
+ glContext->mOwnsContext = false;
+ gGlobalContext = glContext;
+
+ return glContext.forget();
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+CreateForWidget(Display* aXDisplay, Window aXWindow, bool aForceAccelerated)
+{
+ if (!sGLXLibrary.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ // Currently, we take whatever Visual the window already has, and
+ // try to create an fbconfig for that visual. This isn't
+ // necessarily what we want in the long run; an fbconfig may not
+ // be available for the existing visual, or if it is, the GL
+ // performance might be suboptimal. But using the existing visual
+ // is a relatively safe intermediate step.
+
+ if (!aXDisplay) {
+ NS_ERROR("X Display required for GLX Context provider");
+ return nullptr;
+ }
+
+ int xscreen = DefaultScreen(aXDisplay);
+
+ ScopedXFree<GLXFBConfig> cfgs;
+ GLXFBConfig config;
+ int visid;
+ if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs,
+ &config, &visid))
+ {
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::Any();
+ GLContextGLX* shareContext = GetGlobalContextGLX();
+ RefPtr<GLContextGLX> gl = GLContextGLX::CreateGLContext(CreateContextFlags::NONE,
+ caps, shareContext, false,
+ aXDisplay, aXWindow, config,
+ false);
+ return gl.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderGLX::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ X11CompositorWidget* compWidget = aCompositorWidget->AsX11();
+ MOZ_ASSERT(compWidget);
+
+ return CreateForWidget(compWidget->XDisplay(),
+ compWidget->XWindow(),
+ aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderGLX::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ Display* display = (Display*)aWidget->GetNativeData(NS_NATIVE_COMPOSITOR_DISPLAY);
+ Window window = GET_NATIVE_WINDOW(aWidget);
+
+ return CreateForWidget(display,
+ window,
+ aForceAccelerated);
+}
+
+static bool
+ChooseConfig(GLXLibrary* glx, Display* display, int screen, const SurfaceCaps& minCaps,
+ ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
+ GLXFBConfig* const out_config, int* const out_visid)
+{
+ ScopedXFree<GLXFBConfig>& scopedConfigArr = *out_scopedConfigArr;
+
+ if (minCaps.antialias)
+ return false;
+
+ int attribs[] = {
+ LOCAL_GLX_DRAWABLE_TYPE, LOCAL_GLX_PIXMAP_BIT,
+ LOCAL_GLX_X_RENDERABLE, True,
+ LOCAL_GLX_RED_SIZE, 8,
+ LOCAL_GLX_GREEN_SIZE, 8,
+ LOCAL_GLX_BLUE_SIZE, 8,
+ LOCAL_GLX_ALPHA_SIZE, minCaps.alpha ? 8 : 0,
+ LOCAL_GLX_DEPTH_SIZE, minCaps.depth ? 16 : 0,
+ LOCAL_GLX_STENCIL_SIZE, minCaps.stencil ? 8 : 0,
+ 0
+ };
+
+ int numConfigs = 0;
+ scopedConfigArr = glx->xChooseFBConfig(display, screen, attribs, &numConfigs);
+ if (!scopedConfigArr || !numConfigs)
+ return false;
+
+ // Issues with glxChooseFBConfig selection and sorting:
+ // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't request
+ // alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
+ // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
+ // * STENCIL_SIZE is smallest first, but it might return `8` even though we ask for
+ // `0`.
+
+ // For now, we don't care about these. We *will* care when we do XPixmap sharing.
+
+ for (int i = 0; i < numConfigs; ++i) {
+ GLXFBConfig curConfig = scopedConfigArr[i];
+
+ int visid;
+ if (glx->xGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID, &visid)
+ != Success)
+ {
+ continue;
+ }
+
+ if (!visid)
+ continue;
+
+ *out_config = curConfig;
+ *out_visid = visid;
+ return true;
+ }
+
+ return false;
+}
+
+bool
+GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window,
+ ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
+ GLXFBConfig* const out_config, int* const out_visid)
+{
+ ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
+ int numConfigs;
+ if (sGLXLibrary.IsATI() ||
+ !sGLXLibrary.GLXVersionCheck(1, 3)) {
+ const int attribs[] = {
+ LOCAL_GLX_DOUBLEBUFFER, False,
+ 0
+ };
+ cfgs = sGLXLibrary.xChooseFBConfig(display,
+ screen,
+ attribs,
+ &numConfigs);
+ } else {
+ cfgs = sGLXLibrary.xGetFBConfigs(display,
+ screen,
+ &numConfigs);
+ }
+
+ if (!cfgs) {
+ NS_WARNING("[GLX] glXGetFBConfigs() failed");
+ return false;
+ }
+ NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
+
+ // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
+ // we could probably do this first and replace the glXGetFBConfigs
+ // with glXChooseConfigs. Docs are sparklingly clear as always.
+ XWindowAttributes windowAttrs;
+ if (!XGetWindowAttributes(display, window, &windowAttrs)) {
+ NS_WARNING("[GLX] XGetWindowAttributes() failed");
+ return false;
+ }
+ const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
+#ifdef DEBUG
+ printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
+#endif
+
+ for (int i = 0; i < numConfigs; i++) {
+ int visid = X11None;
+ sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+ if (!visid) {
+ continue;
+ }
+ if (sGLXLibrary.IsATI()) {
+ int depth;
+ Visual* visual;
+ FindVisualAndDepth(display, visid, &visual, &depth);
+ if (depth == windowAttrs.depth &&
+ AreCompatibleVisuals(windowAttrs.visual, visual)) {
+ *out_config = cfgs[i];
+ *out_visid = visid;
+ return true;
+ }
+ } else {
+ if (windowVisualID == static_cast<VisualID>(visid)) {
+ *out_config = cfgs[i];
+ *out_visid = visid;
+ return true;
+ }
+ }
+ }
+
+ NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
+ return false;
+}
+
+static already_AddRefed<GLContextGLX>
+CreateOffscreenPixmapContext(CreateContextFlags flags, const IntSize& size,
+ const SurfaceCaps& minCaps, nsACString* const out_failureId,
+ ContextProfile profile = ContextProfile::OpenGLCompatibility)
+{
+ GLXLibrary* glx = &sGLXLibrary;
+ if (!glx->EnsureInitialized())
+ return nullptr;
+
+ Display* display = DefaultXDisplay();
+ int screen = DefaultScreen(display);
+
+ ScopedXFree<GLXFBConfig> scopedConfigArr;
+ GLXFBConfig config;
+ int visid;
+ if (!ChooseConfig(glx, display, screen, minCaps, &scopedConfigArr, &config, &visid)) {
+ NS_WARNING("Failed to find a compatible config.");
+ return nullptr;
+ }
+
+ Visual* visual;
+ int depth;
+ FindVisualAndDepth(display, visid, &visual, &depth);
+
+ OffMainThreadScopedXErrorHandler xErrorHandler;
+ bool error = false;
+
+ Drawable drawable;
+ GLXPixmap pixmap = 0;
+
+ gfx::IntSize dummySize(16, 16);
+ RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(DefaultScreenOfDisplay(display),
+ visual,
+ dummySize);
+ if (surface->CairoStatus() != 0) {
+ mozilla::Unused << xErrorHandler.SyncAndGetError(display);
+ return nullptr;
+ }
+
+ // Handle slightly different signature between glXCreatePixmap and
+ // its pre-GLX-1.3 extension equivalent (though given the ABI, we
+ // might not need to).
+ drawable = surface->XDrawable();
+ if (glx->GLXVersionCheck(1, 3)) {
+ pixmap = glx->xCreatePixmap(display, config, drawable, nullptr);
+ } else {
+ pixmap = glx->xCreateGLXPixmapWithConfig(display, config, drawable);
+ }
+
+ if (pixmap == 0) {
+ error = true;
+ }
+
+ bool serverError = xErrorHandler.SyncAndGetError(display);
+ if (error || serverError)
+ return nullptr;
+
+ GLContextGLX* shareContext = GetGlobalContextGLX();
+ return GLContextGLX::CreateGLContext(flags, minCaps, shareContext, true, display,
+ pixmap, config, true, surface, profile);
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderGLX::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ IntSize dummySize = IntSize(16, 16);
+ SurfaceCaps dummyCaps = SurfaceCaps::Any();
+ return CreateOffscreenPixmapContext(flags, dummySize, dummyCaps, out_failureId);
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderGLX::CreateOffscreen(const IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ SurfaceCaps minBackbufferCaps = minCaps;
+ if (minCaps.antialias) {
+ minBackbufferCaps.antialias = false;
+ minBackbufferCaps.depth = false;
+ minBackbufferCaps.stencil = false;
+ }
+
+ ContextProfile profile = ContextProfile::OpenGLCore;
+ if (flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE) {
+ profile = ContextProfile::OpenGLCompatibility;
+ }
+
+ RefPtr<GLContext> gl;
+ gl = CreateOffscreenPixmapContext(flags, size, minBackbufferCaps, out_failureId,
+ profile);
+ if (!gl)
+ return nullptr;
+
+ if (!gl->InitOffscreen(size, minCaps)) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_GLX_INIT");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+/*static*/ GLContext*
+GLContextProviderGLX::GetGlobalContext()
+{
+ // TODO: get GLX context sharing to work well with multiple threads
+ if (gfxEnv::DisableContextSharingGlx())
+ return nullptr;
+
+ static bool triedToCreateContext = false;
+ if (!triedToCreateContext) {
+ triedToCreateContext = true;
+
+ MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized.");
+ nsCString discardFailureId;
+ RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, &discardFailureId);
+ gGlobalContext = temp;
+ }
+
+ return gGlobalContext;
+}
+
+/*static*/ void
+GLContextProviderGLX::Shutdown()
+{
+ gGlobalContext = nullptr;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
+
diff --git a/system/graphics/gl/GLContextProviderImpl.h b/system/graphics/gl/GLContextProviderImpl.h
new file mode 100644
index 000000000..0a5d30eaa
--- /dev/null
+++ b/system/graphics/gl/GLContextProviderImpl.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IN_GL_CONTEXT_PROVIDER_H
+#error GLContextProviderImpl.h must only be included from GLContextProvider.h
+#endif
+
+#ifndef GL_CONTEXT_PROVIDER_NAME
+#error GL_CONTEXT_PROVIDER_NAME not defined
+#endif
+
+class GL_CONTEXT_PROVIDER_NAME
+{
+public:
+ /**
+ * Create a context that renders to the surface of the widget represented by
+ * the compositor widget that is passed in. The context is always created
+ * with an RGB pixel format, with no alpha, depth or stencil.
+ * If any of those features are needed, either use a framebuffer, or
+ * use CreateOffscreen.
+ *
+ * This context will attempt to share resources with all other window
+ * contexts. As such, it's critical that resources allocated that are not
+ * needed by other contexts be deleted before the context is destroyed.
+ *
+ * The GetSharedContext() method will return non-null if sharing
+ * was successful.
+ *
+ * Note: a context created for a widget /must not/ hold a strong
+ * reference to the widget; otherwise a cycle can be created through
+ * a GL layer manager.
+ *
+ * @param aCompositorWidget Widget whose surface to create a context for
+ * @param aForceAccelerated true if only accelerated contexts are allowed
+ *
+ * @return Context to use for the window
+ */
+ static already_AddRefed<GLContext>
+ CreateForCompositorWidget(mozilla::widget::CompositorWidget* aCompositorWidget, bool aForceAccelerated);
+
+ /**
+ * Create a context that renders to the surface of the widget that is
+ * passed in. The context is always created with an RGB pixel format,
+ * with no alpha, depth or stencil. If any of those features are needed,
+ * either use a framebuffer, or use CreateOffscreen.
+ *
+ * This context will attempt to share resources with all other window
+ * contexts. As such, it's critical that resources allocated that are not
+ * needed by other contexts be deleted before the context is destroyed.
+ *
+ * The GetSharedContext() method will return non-null if sharing
+ * was successful.
+ *
+ * Note: a context created for a widget /must not/ hold a strong
+ * reference to the widget; otherwise a cycle can be created through
+ * a GL layer manager.
+ *
+ * @param aWidget Widget whose surface to create a context for
+ * @param aForceAccelerated true if only accelerated contexts are allowed
+ *
+ * @return Context to use for the window
+ */
+ static already_AddRefed<GLContext>
+ CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated);
+
+ /**
+ * Create a context for offscreen rendering. The target of this
+ * context should be treated as opaque -- it might be a FBO, or a
+ * pbuffer, or some other construct. Users of this GLContext
+ * should bind framebuffer 0 directly to use this offscreen buffer.
+ *
+ * The offscreen context returned by this method will always have
+ * the ability to be rendered into a context created by a window.
+ * It might or might not share resources with the global context;
+ * query GetSharedContext() for a non-null result to check. If
+ * resource sharing can be avoided on the target platform, it will
+ * be, in order to isolate the offscreen context.
+ *
+ * @param size The initial size of this offscreen context.
+ * @param minCaps The required SurfaceCaps for this offscreen context. The resulting
+ * context *may* have more/better caps than requested, but it cannot
+ * have fewer/worse caps than requested.
+ * @param flags The set of CreateContextFlags to be used for this
+ * offscreen context.
+ *
+ * @return Context to use for offscreen rendering
+ */
+ static already_AddRefed<GLContext>
+ CreateOffscreen(const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId);
+
+ // Just create a context. We'll add offscreen stuff ourselves.
+ static already_AddRefed<GLContext>
+ CreateHeadless(CreateContextFlags flags, nsACString* const out_failureId);
+
+ /**
+ * Create wrapping Gecko GLContext for external gl context.
+ *
+ * @param aContext External context which will be wrapped by Gecko GLContext.
+ * @param aSurface External surface which is used for external context.
+ *
+ * @return Wrapping Context to use for rendering
+ */
+ static already_AddRefed<GLContext>
+ CreateWrappingExisting(void* aContext, void* aSurface);
+
+ /**
+ * Get a pointer to the global context, creating it if it doesn't exist.
+ */
+ static GLContext*
+ GetGlobalContext();
+
+ /**
+ * Free any resources held by this Context Provider.
+ */
+ static void
+ Shutdown();
+};
diff --git a/system/graphics/gl/GLContextProviderNull.cpp b/system/graphics/gl/GLContextProviderNull.cpp
new file mode 100644
index 000000000..513ed2eda
--- /dev/null
+++ b/system/graphics/gl/GLContextProviderNull.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContextProvider.h"
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::widget;
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateWrappingExisting(void*, void*)
+{
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateOffscreen(const gfx::IntSize&,
+ const SurfaceCaps&,
+ CreateContextFlags,
+ nsACString* const out_failureId)
+{
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NULL");
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateHeadless(CreateContextFlags, nsACString* const out_failureId)
+{
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NULL");
+ return nullptr;
+}
+
+GLContext*
+GLContextProviderNull::GetGlobalContext()
+{
+ return nullptr;
+}
+
+void
+GLContextProviderNull::Shutdown()
+{
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/GLContextProviderWGL.cpp b/system/graphics/gl/GLContextProviderWGL.cpp
new file mode 100644
index 000000000..43f5b67e8
--- /dev/null
+++ b/system/graphics/gl/GLContextProviderWGL.cpp
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContextProvider.h"
+#include "GLContextWGL.h"
+#include "GLLibraryLoader.h"
+#include "nsDebug.h"
+#include "nsIGfxInfo.h"
+#include "nsIWidget.h"
+#include "gfxPlatform.h"
+#include "gfxUtils.h"
+#include "gfxWindowsSurface.h"
+
+#include "prenv.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+
+WGLLibrary sWGLLib;
+
+HWND
+WGLLibrary::CreateDummyWindow(HDC* aWindowDC)
+{
+ WNDCLASSW wc;
+ if (!GetClassInfoW(GetModuleHandle(nullptr), L"GLContextWGLClass", &wc)) {
+ ZeroMemory(&wc, sizeof(WNDCLASSW));
+ wc.style = CS_OWNDC;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.lpfnWndProc = DefWindowProc;
+ wc.lpszClassName = L"GLContextWGLClass";
+ if (!RegisterClassW(&wc)) {
+ NS_WARNING("Failed to register GLContextWGLClass?!");
+ // er. failed to register our class?
+ return nullptr;
+ }
+ }
+
+ HWND win = CreateWindowW(L"GLContextWGLClass", L"GLContextWGL", 0,
+ 0, 0, 16, 16,
+ nullptr, nullptr, GetModuleHandle(nullptr),
+ nullptr);
+ NS_ENSURE_TRUE(win, nullptr);
+
+ HDC dc = GetDC(win);
+ NS_ENSURE_TRUE(dc, nullptr);
+
+ if (mWindowPixelFormat == 0) {
+ PIXELFORMATDESCRIPTOR pfd;
+ ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
+ pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.cRedBits = 8;
+ pfd.cGreenBits = 8;
+ pfd.cBlueBits = 8;
+ pfd.cAlphaBits = 8;
+ pfd.cDepthBits = 0;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+
+ mWindowPixelFormat = ChoosePixelFormat(dc, &pfd);
+ }
+
+ if (!mWindowPixelFormat ||
+ !SetPixelFormat(dc, mWindowPixelFormat, nullptr))
+ {
+ NS_WARNING("SetPixelFormat failed!");
+ DestroyWindow(win);
+ return nullptr;
+ }
+
+ if (aWindowDC) {
+ *aWindowDC = dc;
+ }
+
+ return win;
+}
+
+static inline bool
+HasExtension(const char* aExtensions, const char* aRequiredExtension)
+{
+ return GLContext::ListHasExtension(
+ reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
+}
+
+bool
+WGLLibrary::EnsureInitialized()
+{
+ if (mInitialized)
+ return true;
+
+ std::string libGLFilename = "Opengl32.dll";
+ // SU_SPIES_DIRECTORY is for AMD CodeXL/gDEBugger
+ if (PR_GetEnv("SU_SPIES_DIRECTORY")) {
+ libGLFilename = std::string(PR_GetEnv("SU_SPIES_DIRECTORY")) + "\\opengl32.dll";
+ }
+
+ if (!mOGLLibrary) {
+ mOGLLibrary = PR_LoadLibrary(libGLFilename.c_str());
+ if (!mOGLLibrary) {
+ NS_WARNING("Couldn't load OpenGL library.");
+ return false;
+ }
+ }
+
+ const GLLibraryLoader::SymLoadStruct earlySymbols[] = {
+ { (PRFuncPtr*) &fCreateContext, { "wglCreateContext", nullptr } },
+ { (PRFuncPtr*) &fMakeCurrent, { "wglMakeCurrent", nullptr } },
+ { (PRFuncPtr*) &fGetProcAddress, { "wglGetProcAddress", nullptr } },
+ { (PRFuncPtr*) &fDeleteContext, { "wglDeleteContext", nullptr } },
+ { (PRFuncPtr*) &fGetCurrentContext, { "wglGetCurrentContext", nullptr } },
+ { (PRFuncPtr*) &fGetCurrentDC, { "wglGetCurrentDC", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &earlySymbols[0])) {
+ NS_WARNING("Couldn't find required entry points in OpenGL DLL (early init)");
+ return false;
+ }
+
+ // This is ridiculous -- we have to actually create a context to
+ // get the OpenGL ICD to load.
+ mWindow = CreateDummyWindow(&mWindowDC);
+ NS_ENSURE_TRUE(mWindow, false);
+
+ // create rendering context
+ mWindowGLContext = fCreateContext(mWindowDC);
+ NS_ENSURE_TRUE(mWindowGLContext, false);
+
+ if (!fMakeCurrent(mWindowDC, mWindowGLContext)) {
+ NS_WARNING("wglMakeCurrent failed");
+ return false;
+ }
+
+ const auto curCtx = fGetCurrentContext();
+ const auto curDC = fGetCurrentDC();
+
+ const auto lookupFunc = (GLLibraryLoader::PlatformLookupFunction)fGetProcAddress;
+
+ // Now we can grab all the other symbols that we couldn't without having
+ // a context current.
+ const GLLibraryLoader::SymLoadStruct pbufferSymbols[] = {
+ { (PRFuncPtr*) &fCreatePbuffer, { "wglCreatePbufferARB", "wglCreatePbufferEXT", nullptr } },
+ { (PRFuncPtr*) &fDestroyPbuffer, { "wglDestroyPbufferARB", "wglDestroyPbufferEXT", nullptr } },
+ { (PRFuncPtr*) &fGetPbufferDC, { "wglGetPbufferDCARB", "wglGetPbufferDCEXT", nullptr } },
+ { (PRFuncPtr*) &fBindTexImage, { "wglBindTexImageARB", "wglBindTexImageEXT", nullptr } },
+ { (PRFuncPtr*) &fReleaseTexImage, { "wglReleaseTexImageARB", "wglReleaseTexImageEXT", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ const GLLibraryLoader::SymLoadStruct pixFmtSymbols[] = {
+ { (PRFuncPtr*) &fChoosePixelFormat, { "wglChoosePixelFormatARB", "wglChoosePixelFormatEXT", nullptr } },
+ { (PRFuncPtr*) &fGetPixelFormatAttribiv, { "wglGetPixelFormatAttribivARB", "wglGetPixelFormatAttribivEXT", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pbufferSymbols[0], lookupFunc)) {
+ // this isn't an error, just means that pbuffers aren't supported
+ fCreatePbuffer = nullptr;
+ }
+
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pixFmtSymbols[0], lookupFunc)) {
+ // this isn't an error, just means that we don't have the pixel format extension
+ fChoosePixelFormat = nullptr;
+ }
+
+ const GLLibraryLoader::SymLoadStruct extensionsSymbols[] = {
+ { (PRFuncPtr*) &fGetExtensionsString, { "wglGetExtensionsStringARB", nullptr} },
+ { nullptr, { nullptr } }
+ };
+
+ const GLLibraryLoader::SymLoadStruct robustnessSymbols[] = {
+ { (PRFuncPtr*) &fCreateContextAttribs, { "wglCreateContextAttribsARB", nullptr} },
+ { nullptr, { nullptr } }
+ };
+
+ const GLLibraryLoader::SymLoadStruct dxInteropSymbols[] = {
+ { (PRFuncPtr*)&fDXSetResourceShareHandle,{ "wglDXSetResourceShareHandleNV", nullptr } },
+ { (PRFuncPtr*)&fDXOpenDevice, { "wglDXOpenDeviceNV", nullptr } },
+ { (PRFuncPtr*)&fDXCloseDevice, { "wglDXCloseDeviceNV", nullptr } },
+ { (PRFuncPtr*)&fDXRegisterObject, { "wglDXRegisterObjectNV", nullptr } },
+ { (PRFuncPtr*)&fDXUnregisterObject, { "wglDXUnregisterObjectNV", nullptr } },
+ { (PRFuncPtr*)&fDXObjectAccess, { "wglDXObjectAccessNV", nullptr } },
+ { (PRFuncPtr*)&fDXLockObjects, { "wglDXLockObjectsNV", nullptr } },
+ { (PRFuncPtr*)&fDXUnlockObjects, { "wglDXUnlockObjectsNV", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0], lookupFunc)) {
+ const char* extString = fGetExtensionsString(mWindowDC);
+ MOZ_ASSERT(extString);
+ MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string"));
+
+ if (HasExtension(extString, "WGL_ARB_create_context")) {
+ if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0], lookupFunc)) {
+ if (HasExtension(extString, "WGL_ARB_create_context_robustness")) {
+ mHasRobustness = true;
+ }
+ } else {
+ NS_ERROR("WGL supports ARB_create_context without supplying its functions.");
+ fCreateContextAttribs = nullptr;
+ }
+ }
+
+ ////
+
+ mHasDXInterop = HasExtension(extString, "WGL_NV_DX_interop");
+ mHasDXInterop2 = HasExtension(extString, "WGL_NV_DX_interop2");
+
+ nsCString blocklistId;
+ if (gfxUtils::IsFeatureBlacklisted(nullptr, nsIGfxInfo::FEATURE_DX_INTEROP2,
+ &blocklistId) &&
+ !gfxPrefs::IgnoreDXInterop2Blacklist())
+ {
+ mHasDXInterop2 = false;
+ }
+
+ if (mHasDXInterop || mHasDXInterop2) {
+ if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &dxInteropSymbols[0],
+ lookupFunc))
+ {
+ NS_ERROR("WGL supports NV_DX_interop(2) without supplying its functions.");
+ fDXSetResourceShareHandle = nullptr;
+ fDXOpenDevice = nullptr;
+ fDXCloseDevice = nullptr;
+ fDXRegisterObject = nullptr;
+ fDXUnregisterObject = nullptr;
+ fDXObjectAccess = nullptr;
+ fDXLockObjects = nullptr;
+ fDXUnlockObjects = nullptr;
+
+ mHasDXInterop = false;
+ mHasDXInterop2 = false;
+ }
+ }
+ }
+
+ // reset back to the previous context, just in case
+ fMakeCurrent(curDC, curCtx);
+
+ if (mHasRobustness) {
+ fDeleteContext(mWindowGLContext);
+
+ const int attribs[] = {
+ LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+ LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+ 0
+ };
+
+ mWindowGLContext = fCreateContextAttribs(mWindowDC, nullptr, attribs);
+ if (!mWindowGLContext) {
+ mHasRobustness = false;
+ mWindowGLContext = fCreateContext(mWindowDC);
+ }
+ }
+
+ mInitialized = true;
+
+ return true;
+}
+
+GLContextWGL::GLContextWGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ bool isOffscreen, HDC aDC, HGLRC aContext, HWND aWindow)
+ : GLContext(flags, caps, nullptr, isOffscreen),
+ mDC(aDC),
+ mContext(aContext),
+ mWnd(aWindow),
+ mPBuffer(nullptr),
+ mPixelFormat(0),
+ mIsDoubleBuffered(false)
+{
+ // See 899855
+ SetProfileVersion(ContextProfile::OpenGLCompatibility, 200);
+}
+
+GLContextWGL::GLContextWGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ bool isOffscreen, HANDLE aPbuffer, HDC aDC, HGLRC aContext,
+ int aPixelFormat)
+ : GLContext(flags, caps, nullptr, isOffscreen),
+ mDC(aDC),
+ mContext(aContext),
+ mWnd(nullptr),
+ mPBuffer(aPbuffer),
+ mPixelFormat(aPixelFormat),
+ mIsDoubleBuffered(false)
+{
+ // See 899855
+ SetProfileVersion(ContextProfile::OpenGLCompatibility, 200);
+}
+
+GLContextWGL::~GLContextWGL()
+{
+ MarkDestroyed();
+
+ sWGLLib.fDeleteContext(mContext);
+
+ if (mPBuffer)
+ sWGLLib.fDestroyPbuffer(mPBuffer);
+ if (mWnd)
+ DestroyWindow(mWnd);
+}
+
+bool
+GLContextWGL::Init()
+{
+ if (!mDC || !mContext)
+ return false;
+
+ SetupLookupFunction();
+ return InitWithPrefix("gl", true);
+}
+
+bool
+GLContextWGL::MakeCurrentImpl(bool aForce)
+{
+ if (IsDestroyed()) {
+ MOZ_ALWAYS_TRUE(sWGLLib.fMakeCurrent(0, 0));
+ }
+
+ BOOL succeeded = true;
+
+ // wglGetCurrentContext seems to just pull the HGLRC out
+ // of its TLS slot, so no need to do our own tls slot.
+ // You would think that wglMakeCurrent would avoid doing
+ // work if mContext was already current, but not so much..
+ if (aForce || sWGLLib.fGetCurrentContext() != mContext) {
+ succeeded = sWGLLib.fMakeCurrent(mDC, mContext);
+ NS_ASSERTION(succeeded, "Failed to make GL context current!");
+ }
+
+ return succeeded;
+}
+
+bool
+GLContextWGL::IsCurrent()
+{
+ return sWGLLib.fGetCurrentContext() == mContext;
+}
+
+void
+GLContextWGL::SetIsDoubleBuffered(bool aIsDB)
+{
+ mIsDoubleBuffered = aIsDB;
+}
+
+bool
+GLContextWGL::IsDoubleBuffered() const
+{
+ return mIsDoubleBuffered;
+}
+
+bool
+GLContextWGL::SupportsRobustness() const
+{
+ return sWGLLib.HasRobustness();
+}
+
+bool
+GLContextWGL::SwapBuffers() {
+ if (!mIsDoubleBuffered)
+ return false;
+ return ::SwapBuffers(mDC);
+}
+
+void
+GLContextWGL::GetWSIInfo(nsCString* const out) const
+{
+ out->AppendLiteral("wglGetExtensionsString: ");
+ out->Append(sWGLLib.fGetExtensionsString(mDC));
+}
+
+bool
+GLContextWGL::SetupLookupFunction()
+{
+ // Make sure that we have a ref to the OGL library;
+ // when run under CodeXL, wglGetProcAddress won't return
+ // the right thing for some core functions.
+ MOZ_ASSERT(mLibrary == nullptr);
+
+ mLibrary = sWGLLib.GetOGLLibrary();
+ mLookupFunc = (PlatformLookupFunction)sWGLLib.fGetProcAddress;
+ return true;
+}
+
+static bool
+GetMaxSize(HDC hDC, int format, IntSize& size)
+{
+ int query[] = {LOCAL_WGL_MAX_PBUFFER_WIDTH_ARB, LOCAL_WGL_MAX_PBUFFER_HEIGHT_ARB};
+ int result[2];
+
+ // (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, int* piValues)
+ if (!sWGLLib.fGetPixelFormatAttribiv(hDC, format, 0, 2, query, result))
+ return false;
+
+ size.width = result[0];
+ size.height = result[1];
+ return true;
+}
+
+static bool
+IsValidSizeForFormat(HDC hDC, int format,
+ const IntSize& requested)
+{
+ IntSize max;
+ if (!GetMaxSize(hDC, format, max))
+ return true;
+
+ if (requested.width > max.width)
+ return false;
+ if (requested.height > max.height)
+ return false;
+
+ return true;
+}
+
+static GLContextWGL*
+GetGlobalContextWGL()
+{
+ return static_cast<GLContextWGL*>(GLContextProviderWGL::GetGlobalContext());
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateWrappingExisting(void*, void*)
+{
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ if (!sWGLLib.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ /**
+ * We need to make sure we call SetPixelFormat -after- calling
+ * EnsureInitialized, otherwise it can load/unload the dll and
+ * wglCreateContext will fail.
+ */
+
+ HDC dc = (HDC)aWidget->GetNativeData(NS_NATIVE_GRAPHIC);
+
+ SetPixelFormat(dc, sWGLLib.GetWindowPixelFormat(), nullptr);
+ HGLRC context;
+
+ if (sWGLLib.HasRobustness()) {
+ int attribs[] = {
+ LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+ LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+ 0
+ };
+
+ context = sWGLLib.fCreateContextAttribs(dc, nullptr, attribs);
+ } else {
+ context = sWGLLib.fCreateContext(dc);
+ }
+
+ if (!context) {
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::ForRGBA();
+ RefPtr<GLContextWGL> glContext = new GLContextWGL(CreateContextFlags::NONE, caps,
+ false, dc, context);
+ if (!glContext->Init()) {
+ return nullptr;
+ }
+
+ glContext->SetIsDoubleBuffered(true);
+
+ return glContext.forget();
+}
+
+static already_AddRefed<GLContextWGL>
+CreatePBufferOffscreenContext(CreateContextFlags flags, const IntSize& aSize)
+{
+ WGLLibrary& wgl = sWGLLib;
+
+ const int pfAttribs[] = {
+ LOCAL_WGL_SUPPORT_OPENGL_ARB, LOCAL_GL_TRUE,
+ LOCAL_WGL_ACCELERATION_ARB, LOCAL_WGL_FULL_ACCELERATION_ARB,
+
+ LOCAL_WGL_DRAW_TO_PBUFFER_ARB, LOCAL_GL_TRUE,
+ LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE,
+ LOCAL_WGL_STEREO_ARB, LOCAL_GL_FALSE,
+
+ 0
+ };
+
+ // We only need one!
+ static const uint32_t kMaxFormats = 1024;
+ int formats[kMaxFormats];
+ uint32_t foundFormats;
+ HDC windowDC = wgl.GetWindowDC();
+ if (!wgl.fChoosePixelFormat(windowDC, pfAttribs, nullptr, kMaxFormats, formats,
+ &foundFormats)
+ || foundFormats == 0)
+ {
+ return nullptr;
+ }
+
+ // We don't care; just pick the first one.
+ int chosenFormat = formats[0];
+ if (!IsValidSizeForFormat(windowDC, chosenFormat, aSize))
+ return nullptr;
+
+ const int pbAttribs[] = { 0 };
+ HANDLE pbuffer = wgl.fCreatePbuffer(windowDC, chosenFormat, aSize.width, aSize.height,
+ pbAttribs);
+ if (!pbuffer) {
+ return nullptr;
+ }
+
+ HDC pbdc = wgl.fGetPbufferDC(pbuffer);
+ NS_ASSERTION(pbdc, "expected a dc");
+
+ HGLRC context;
+ if (wgl.HasRobustness()) {
+ const int attribs[] = {
+ LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+ LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+ 0
+ };
+ context = wgl.fCreateContextAttribs(pbdc, nullptr, attribs);
+ } else {
+ context = wgl.fCreateContext(pbdc);
+ }
+
+ if (!context) {
+ wgl.fDestroyPbuffer(pbuffer);
+ return nullptr;
+ }
+
+ SurfaceCaps dummyCaps = SurfaceCaps::Any();
+ RefPtr<GLContextWGL> glContext = new GLContextWGL(flags, dummyCaps, true, pbuffer,
+ pbdc, context, chosenFormat);
+ return glContext.forget();
+}
+
+static already_AddRefed<GLContextWGL>
+CreateWindowOffscreenContext()
+{
+ HDC dc;
+ HWND win = sWGLLib.CreateDummyWindow(&dc);
+ if (!win) {
+ return nullptr;
+ }
+
+ HGLRC context = sWGLLib.fCreateContext(dc);
+ if (sWGLLib.HasRobustness()) {
+ int attribs[] = {
+ LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+ LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+ 0
+ };
+
+ context = sWGLLib.fCreateContextAttribs(dc, nullptr, attribs);
+ } else {
+ context = sWGLLib.fCreateContext(dc);
+ }
+
+ if (!context) {
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::ForRGBA();
+ RefPtr<GLContextWGL> glContext = new GLContextWGL(CreateContextFlags::NONE, caps,
+ true, dc, context, win);
+ return glContext.forget();
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderWGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ if (!sWGLLib.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ RefPtr<GLContextWGL> glContext;
+
+ // Always try to create a pbuffer context first, because we
+ // want the context isolation.
+ if (sWGLLib.fCreatePbuffer &&
+ sWGLLib.fChoosePixelFormat)
+ {
+ IntSize dummySize = IntSize(16, 16);
+ glContext = CreatePBufferOffscreenContext(flags, dummySize);
+ }
+
+ // If it failed, then create a window context and use a FBO.
+ if (!glContext) {
+ glContext = CreateWindowOffscreenContext();
+ }
+
+ if (!glContext ||
+ !glContext->Init())
+ {
+ return nullptr;
+ }
+
+ RefPtr<GLContext> retGL = glContext.get();
+ return retGL.forget();
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderWGL::CreateOffscreen(const IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ RefPtr<GLContext> gl = CreateHeadless(flags, out_failureId);
+ if (!gl)
+ return nullptr;
+
+ if (!gl->InitOffscreen(size, minCaps)) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WGL_INIT");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+/*static*/ GLContext*
+GLContextProviderWGL::GetGlobalContext()
+{
+ return nullptr;
+}
+
+/*static*/ void
+GLContextProviderWGL::Shutdown()
+{
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/GLContextSymbols.h b/system/graphics/gl/GLContextSymbols.h
new file mode 100644
index 000000000..61069e3f7
--- /dev/null
+++ b/system/graphics/gl/GLContextSymbols.h
@@ -0,0 +1,705 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTSYMBOLS_H_
+#define GLCONTEXTSYMBOLS_H_
+
+#include "GLDefs.h"
+
+/*
+ * This file should only be included by GLContext.h, and should be
+ * autogenerated in the future.
+ */
+
+#ifndef GLAPIENTRY
+#ifdef XP_WIN
+#define GLAPIENTRY __stdcall
+#else
+#define GLAPIENTRY
+#endif
+#define GLAPI
+#endif
+
+namespace mozilla {
+namespace gl {
+
+struct GLContextSymbols
+{
+ GLContextSymbols() {
+ Zero();
+ }
+
+ void Zero() {
+ memset(this, 0, sizeof(GLContextSymbols));
+ }
+
+ typedef void (GLAPIENTRY * PFNGLACTIVETEXTUREPROC) (GLenum texture);
+ PFNGLACTIVETEXTUREPROC fActiveTexture;
+ typedef void (GLAPIENTRY * PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
+ PFNGLATTACHSHADERPROC fAttachShader;
+ typedef void (GLAPIENTRY * PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);
+ PFNGLBEGINQUERYPROC fBeginQuery;
+ typedef void (GLAPIENTRY * PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar* name);
+ PFNGLBINDATTRIBLOCATIONPROC fBindAttribLocation;
+ typedef void (GLAPIENTRY * PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
+ PFNGLBINDBUFFERPROC fBindBuffer;
+ typedef void (GLAPIENTRY * PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
+ PFNGLBINDTEXTUREPROC fBindTexture;
+ typedef void (GLAPIENTRY * PFNGLBINDVERTEXARRAYPROC) (GLuint array);
+ PFNGLBINDVERTEXARRAYPROC fBindVertexArray;
+ typedef void (GLAPIENTRY * PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+ PFNGLBLENDCOLORPROC fBlendColor;
+ typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONPROC) (GLenum mode);
+ PFNGLBLENDEQUATIONPROC fBlendEquation;
+ typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum, GLenum);
+ PFNGLBLENDEQUATIONSEPARATEPROC fBlendEquationSeparate;
+ typedef void (GLAPIENTRY * PFNGLBLENDFUNCPROC) (GLenum, GLenum);
+ PFNGLBLENDFUNCPROC fBlendFunc;
+ typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+ PFNGLBLENDFUNCSEPARATEPROC fBlendFuncSeparate;
+ typedef void (GLAPIENTRY * PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
+ PFNGLBUFFERDATAPROC fBufferData;
+ typedef void (GLAPIENTRY * PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
+ PFNGLBUFFERSUBDATAPROC fBufferSubData;
+ typedef void (GLAPIENTRY * PFNGLCLEARPROC) (GLbitfield);
+ PFNGLCLEARPROC fClear;
+ typedef void (GLAPIENTRY * PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
+ PFNGLCLEARBUFFERFIPROC fClearBufferfi;
+ typedef void (GLAPIENTRY * PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat* value);
+ PFNGLCLEARBUFFERFVPROC fClearBufferfv;
+ typedef void (GLAPIENTRY * PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint* value);
+ PFNGLCLEARBUFFERIVPROC fClearBufferiv;
+ typedef void (GLAPIENTRY * PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint* value);
+ PFNGLCLEARBUFFERUIVPROC fClearBufferuiv;
+ typedef void (GLAPIENTRY * PFNGLCLEARCOLORPROC) (GLfloat, GLfloat, GLfloat, GLfloat);
+ PFNGLCLEARCOLORPROC fClearColor;
+ typedef void (GLAPIENTRY * PFNGLCLEARSTENCILPROC) (GLint);
+ PFNGLCLEARSTENCILPROC fClearStencil;
+ typedef void (GLAPIENTRY * PFNGLCOLORMASKPROC) (realGLboolean red, realGLboolean green, realGLboolean blue, realGLboolean alpha);
+ PFNGLCOLORMASKPROC fColorMask;
+ typedef void (GLAPIENTRY * PFNGLCOMPRESSEDTEXIMAGE2D) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* pixels);
+ PFNGLCOMPRESSEDTEXIMAGE2D fCompressedTexImage2D;
+ typedef void (GLAPIENTRY * PFNGLCOMPRESSEDTEXSUBIMAGE2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* pixels);
+ PFNGLCOMPRESSEDTEXSUBIMAGE2D fCompressedTexSubImage2D;
+ typedef void (GLAPIENTRY * PFNGLCULLFACEPROC) (GLenum mode);
+ PFNGLCULLFACEPROC fCullFace;
+ typedef void (GLAPIENTRY * PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
+ PFNGLDETACHSHADERPROC fDetachShader;
+ typedef void (GLAPIENTRY * PFNGLDEPTHFUNCPROC) (GLenum);
+ PFNGLDEPTHFUNCPROC fDepthFunc;
+ typedef void (GLAPIENTRY * PFNGLDEPTHMASKPROC) (realGLboolean);
+ PFNGLDEPTHMASKPROC fDepthMask;
+ typedef void (GLAPIENTRY * PFNGLDISABLEPROC) (GLenum);
+ PFNGLDISABLEPROC fDisable;
+ typedef void (GLAPIENTRY * PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint);
+ PFNGLDISABLEVERTEXATTRIBARRAYPROC fDisableVertexAttribArray;
+ typedef void (GLAPIENTRY * PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count);
+ PFNGLDRAWARRAYSPROC fDrawArrays;
+ typedef void (GLAPIENTRY * PFNGLDRAWBUFFERPROC) (GLenum mode);
+ PFNGLDRAWBUFFERPROC fDrawBuffer;
+ typedef void (GLAPIENTRY * PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum* bufs);
+ PFNGLDRAWBUFFERSPROC fDrawBuffers;
+ typedef void (GLAPIENTRY * PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
+ PFNGLDRAWELEMENTSPROC fDrawElements;
+ typedef void (GLAPIENTRY * PFNGLENABLEPROC) (GLenum);
+ PFNGLENABLEPROC fEnable;
+ typedef void (GLAPIENTRY * PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint);
+ PFNGLENABLEVERTEXATTRIBARRAYPROC fEnableVertexAttribArray;
+ typedef void (GLAPIENTRY * PFNGLFINISHPROC) (void);
+ PFNGLFINISHPROC fFinish;
+ typedef void (GLAPIENTRY * PFNGLENDQUERYPROC) (GLenum target);
+ PFNGLENDQUERYPROC fEndQuery;
+ typedef void (GLAPIENTRY * PFNGLFLUSHPROC) (void);
+ PFNGLFLUSHPROC fFlush;
+ typedef void (GLAPIENTRY * PFNGLFRONTFACEPROC) (GLenum);
+ PFNGLFRONTFACEPROC fFrontFace;
+ typedef void (GLAPIENTRY * PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+ PFNGLGETACTIVEATTRIBPROC fGetActiveAttrib;
+ typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+ PFNGLGETACTIVEUNIFORMPROC fGetActiveUniform;
+ typedef void (GLAPIENTRY * PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
+ PFNGLGETATTACHEDSHADERSPROC fGetAttachedShaders;
+ typedef GLint (GLAPIENTRY * PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar* name);
+ PFNGLGETATTRIBLOCATIONPROC fGetAttribLocation;
+ typedef void (GLAPIENTRY * PFNGLGETINTEGERVPROC) (GLenum pname, GLint* params);
+ PFNGLGETINTEGERVPROC fGetIntegerv;
+ typedef void (GLAPIENTRY * PFNGLGETFLOATVPROC) (GLenum pname, GLfloat* params);
+ PFNGLGETFLOATVPROC fGetFloatv;
+ typedef void (GLAPIENTRY * PFNGLGETBOOLEANBPROC) (GLenum pname, realGLboolean* params);
+ PFNGLGETBOOLEANBPROC fGetBooleanv;
+ typedef void (GLAPIENTRY * PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint* params);
+ PFNGLGETBUFFERPARAMETERIVPROC fGetBufferParameteriv;
+ typedef void (GLAPIENTRY * PFNGLGENERATEMIPMAPPROC) (GLenum target);
+ PFNGLGENERATEMIPMAPPROC fGenerateMipmap;
+ typedef GLenum (GLAPIENTRY * PFNGLGETERRORPROC) (void);
+ PFNGLGETERRORPROC fGetError;
+ typedef void (GLAPIENTRY * PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint* param);
+ PFNGLGETPROGRAMIVPROC fGetProgramiv;
+ typedef void (GLAPIENTRY * PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+ PFNGLGETPROGRAMINFOLOGPROC fGetProgramInfoLog;
+ typedef void (GLAPIENTRY * PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint* params);
+ PFNGLGETQUERYIVPROC fGetQueryiv;
+ typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint* params);
+ PFNGLGETQUERYOBJECTIVPROC fGetQueryObjectiv;
+ typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint* params);
+ PFNGLGETQUERYOBJECTUIVPROC fGetQueryObjectuiv;
+ typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64* params);
+ PFNGLGETQUERYOBJECTI64VPROC fGetQueryObjecti64v;
+ typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64* params);
+ PFNGLGETQUERYOBJECTUI64VPROC fGetQueryObjectui64v;
+ typedef void (GLAPIENTRY * PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target);
+ PFNGLQUERYCOUNTERPROC fQueryCounter;
+ typedef void (GLAPIENTRY * PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
+ PFNGLTEXPARAMETERIPROC fTexParameteri;
+ typedef void (GLAPIENTRY * PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint* param);
+ PFNGLTEXPARAMETERIVPROC fTexParameteriv;
+ typedef void (GLAPIENTRY * PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param);
+ PFNGLTEXPARAMETERFPROC fTexParameterf;
+ typedef GLubyte* (GLAPIENTRY * PFNGLGETSTRINGPROC) (GLenum);
+ PFNGLGETSTRINGPROC fGetString;
+ typedef void (GLAPIENTRY * PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLvoid* image);
+ PFNGLGETTEXIMAGEPROC fGetTexImage;
+ typedef void (GLAPIENTRY * PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint* params);
+ PFNGLGETTEXLEVELPARAMETERIVPROC fGetTexLevelParameteriv;
+ typedef void (GLAPIENTRY * PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat* params);
+ PFNGLGETTEXPARAMETERFVPROC fGetTexParameterfv;
+ typedef void (GLAPIENTRY * PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint* params);
+ PFNGLGETTEXPARAMETERIVPROC fGetTexParameteriv;
+ typedef void (GLAPIENTRY * PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat* params);
+ PFNGLGETUNIFORMFVPROC fGetUniformfv;
+ typedef void (GLAPIENTRY * PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint* params);
+ PFNGLGETUNIFORMIVPROC fGetUniformiv;
+ typedef void (GLAPIENTRY * PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint* params);
+ PFNGLGETUNIFORMUIVPROC fGetUniformuiv;
+ typedef GLint (GLAPIENTRY * PFNGLGETUNIFORMLOCATIONPROC) (GLint programObj, const GLchar* name);
+ PFNGLGETUNIFORMLOCATIONPROC fGetUniformLocation;
+ typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBFVPROC) (GLuint, GLenum, GLfloat*);
+ PFNGLGETVERTEXATTRIBFVPROC fGetVertexAttribfv;
+ typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIVPROC) (GLuint, GLenum, GLint*);
+ PFNGLGETVERTEXATTRIBIVPROC fGetVertexAttribiv;
+ typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint, GLenum, GLvoid**);
+ PFNGLGETVERTEXATTRIBPOINTERVPROC fGetVertexAttribPointerv;
+ typedef void (GLAPIENTRY * PFNGLHINTPROC) (GLenum target, GLenum mode);
+ PFNGLHINTPROC fHint;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISBUFFERPROC) (GLuint buffer);
+ PFNGLISBUFFERPROC fIsBuffer;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISENABLEDPROC) (GLenum cap);
+ PFNGLISENABLEDPROC fIsEnabled;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISPROGRAMPROC) (GLuint program);
+ PFNGLISPROGRAMPROC fIsProgram;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISQUERYPROC) (GLuint id);
+ PFNGLISQUERYPROC fIsQuery;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISSHADERPROC) (GLuint shader);
+ PFNGLISSHADERPROC fIsShader;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISTEXTUREPROC) (GLuint texture);
+ PFNGLISTEXTUREPROC fIsTexture;
+ typedef void (GLAPIENTRY * PFNGLLINEWIDTHPROC) (GLfloat width);
+ PFNGLLINEWIDTHPROC fLineWidth;
+ typedef void (GLAPIENTRY * PFNGLLINKPROGRAMPROC) (GLuint program);
+ PFNGLLINKPROGRAMPROC fLinkProgram;
+ typedef void (GLAPIENTRY * PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
+ PFNGLPIXELSTOREIPROC fPixelStorei;
+ typedef void (GLAPIENTRY * PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param);
+ PFNGLPOINTPARAMETERFPROC fPointParameterf;
+ typedef void (GLAPIENTRY * PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat bias);
+ PFNGLPOLYGONOFFSETPROC fPolygonOffset;
+ typedef void (GLAPIENTRY * PFNGLREADBUFFERPROC) (GLenum);
+ PFNGLREADBUFFERPROC fReadBuffer;
+ typedef void (GLAPIENTRY * PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels);
+ PFNGLREADPIXELSPROC fReadPixels;
+ typedef void (GLAPIENTRY * PFNGLSAMPLECOVERAGEPROC) (GLclampf value, realGLboolean invert);
+ PFNGLSAMPLECOVERAGEPROC fSampleCoverage;
+ typedef void (GLAPIENTRY * PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask);
+ PFNGLSTENCILFUNCPROC fStencilFunc;
+ typedef void (GLAPIENTRY * PFNGLSTENCILFUNCSEPARATEPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask);
+ PFNGLSTENCILFUNCSEPARATEPROC fStencilFuncSeparate;
+ typedef void (GLAPIENTRY * PFNGLSTENCILMASKPROC) (GLuint mask);
+ PFNGLSTENCILMASKPROC fStencilMask;
+ typedef void (GLAPIENTRY * PFNGLSTENCILMASKSEPARATEPROC) (GLenum, GLuint);
+ PFNGLSTENCILMASKSEPARATEPROC fStencilMaskSeparate;
+ typedef void (GLAPIENTRY * PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass);
+ PFNGLSTENCILOPPROC fStencilOp;
+ typedef void (GLAPIENTRY * PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+ PFNGLSTENCILOPSEPARATEPROC fStencilOpSeparate;
+ typedef void (GLAPIENTRY * PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+ PFNGLTEXIMAGE2DPROC fTexImage2D;
+ typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
+ PFNGLTEXSUBIMAGE2DPROC fTexSubImage2D;
+ typedef void (GLAPIENTRY * PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, GLvoid* pointer);
+ PFNGLTEXTURERANGEAPPLEPROC fTextureRangeAPPLE;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
+ PFNGLUNIFORM1FPROC fUniform1f;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat* value);
+ PFNGLUNIFORM1FVPROC fUniform1fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
+ PFNGLUNIFORM1IPROC fUniform1i;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint* value);
+ PFNGLUNIFORM1IVPROC fUniform1iv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
+ PFNGLUNIFORM2FPROC fUniform2f;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat* value);
+ PFNGLUNIFORM2FVPROC fUniform2fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1);
+ PFNGLUNIFORM2IPROC fUniform2i;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint* value);
+ PFNGLUNIFORM2IVPROC fUniform2iv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+ PFNGLUNIFORM3FPROC fUniform3f;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat* value);
+ PFNGLUNIFORM3FVPROC fUniform3fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2);
+ PFNGLUNIFORM3IPROC fUniform3i;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint* value);
+ PFNGLUNIFORM3IVPROC fUniform3iv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+ PFNGLUNIFORM4FPROC fUniform4f;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat* value);
+ PFNGLUNIFORM4FVPROC fUniform4fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+ PFNGLUNIFORM4IPROC fUniform4i;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint* value);
+ PFNGLUNIFORM4IVPROC fUniform4iv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX2FVPROC fUniformMatrix2fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX2X3FVPROC fUniformMatrix2x3fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX2X4FVPROC fUniformMatrix2x4fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX3FVPROC fUniformMatrix3fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX3X2FVPROC fUniformMatrix3x2fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX3X4FVPROC fUniformMatrix3x4fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX4FVPROC fUniformMatrix4fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX4X2FVPROC fUniformMatrix4x2fv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
+ PFNGLUNIFORMMATRIX4X3FVPROC fUniformMatrix4x3fv;
+
+ typedef void (GLAPIENTRY * PFNGLUSEPROGRAMPROC) (GLuint program);
+ PFNGLUSEPROGRAMPROC fUseProgram;
+ typedef void (GLAPIENTRY * PFNGLVALIDATEPROGRAMPROC) (GLuint program);
+ PFNGLVALIDATEPROGRAMPROC fValidateProgram;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, realGLboolean normalized, GLsizei stride, const GLvoid* pointer);
+ PFNGLVERTEXATTRIBPOINTERPROC fVertexAttribPointer;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x);
+ PFNGLVERTEXATTRIB1FPROC fVertexAttrib1f;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y);
+ PFNGLVERTEXATTRIB2FPROC fVertexAttrib2f;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);
+ PFNGLVERTEXATTRIB3FPROC fVertexAttrib3f;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+ PFNGLVERTEXATTRIB4FPROC fVertexAttrib4f;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat* v);
+ PFNGLVERTEXATTRIB1FVPROC fVertexAttrib1fv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat* v);
+ PFNGLVERTEXATTRIB2FVPROC fVertexAttrib2fv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat* v);
+ PFNGLVERTEXATTRIB3FVPROC fVertexAttrib3fv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat* v);
+ PFNGLVERTEXATTRIB4FVPROC fVertexAttrib4fv;
+ typedef void (GLAPIENTRY * PFNGLCOMPILESHADERPROC) (GLuint shader);
+ PFNGLCOMPILESHADERPROC fCompileShader;
+ typedef void (GLAPIENTRY * PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+ PFNGLCOPYTEXIMAGE2DPROC fCopyTexImage2D;
+ typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNGLCOPYTEXSUBIMAGE2DPROC fCopyTexSubImage2D;
+ typedef void (GLAPIENTRY * PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint* param);
+ PFNGLGETSHADERIVPROC fGetShaderiv;
+ typedef void (GLAPIENTRY * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+ PFNGLGETSHADERINFOLOGPROC fGetShaderInfoLog;
+ typedef void (GLAPIENTRY * PFNGETSHADERPRECISIONFORMAT) (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
+ PFNGETSHADERPRECISIONFORMAT fGetShaderPrecisionFormat;
+ typedef void (GLAPIENTRY * PFNGLGETSHADERSOURCEPROC) (GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source);
+ PFNGLGETSHADERSOURCEPROC fGetShaderSource;
+ typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths);
+ PFNGLSHADERSOURCEPROC fShaderSource;
+
+ typedef void (GLAPIENTRY * PFNGLBINDFRAMEBUFFER) (GLenum target, GLuint framebuffer);
+ PFNGLBINDFRAMEBUFFER fBindFramebuffer;
+ typedef void (GLAPIENTRY * PFNGLBINDRENDERBUFFER) (GLenum target, GLuint renderbuffer);
+ PFNGLBINDRENDERBUFFER fBindRenderbuffer;
+ typedef GLenum (GLAPIENTRY * PFNGLCHECKFRAMEBUFFERSTATUS) (GLenum target);
+ PFNGLCHECKFRAMEBUFFERSTATUS fCheckFramebufferStatus;
+ typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERRENDERBUFFER) (GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbuffer);
+ PFNGLFRAMEBUFFERRENDERBUFFER fFramebufferRenderbuffer;
+ typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERTEXTURE2D) (GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint texture, GLint level);
+ PFNGLFRAMEBUFFERTEXTURE2D fFramebufferTexture2D;
+ typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
+ PFNGLFRAMEBUFFERTEXTURELAYERPROC fFramebufferTextureLayer;
+ typedef void (GLAPIENTRY * PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIV) (GLenum target, GLenum attachment, GLenum pname, GLint* value);
+ PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIV fGetFramebufferAttachmentParameteriv;
+ typedef void (GLAPIENTRY * PFNGLGETRENDERBUFFERPARAMETERIV) (GLenum target, GLenum pname, GLint* value);
+ PFNGLGETRENDERBUFFERPARAMETERIV fGetRenderbufferParameteriv;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISFRAMEBUFFER) (GLuint framebuffer);
+ PFNGLISFRAMEBUFFER fIsFramebuffer;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISRENDERBUFFER) (GLuint renderbuffer);
+ PFNGLISRENDERBUFFER fIsRenderbuffer;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISVERTEXARRAY) (GLuint array);
+ PFNGLISVERTEXARRAY fIsVertexArray;
+ typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
+ PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage;
+
+ typedef void (GLAPIENTRY * PFNINVALIDATEFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments);
+ PFNINVALIDATEFRAMEBUFFER fInvalidateFramebuffer;
+ typedef void (GLAPIENTRY * PFNINVALIDATESUBFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNINVALIDATESUBFRAMEBUFFER fInvalidateSubFramebuffer;
+
+ // These functions are only used by Skia/GL in desktop mode.
+ // Other parts of Gecko should avoid using these
+ typedef void (GLAPIENTRY * PFNGLCLIENTACTIVETEXTURE) (GLenum texture);
+ PFNGLCLIENTACTIVETEXTURE fClientActiveTexture;
+ typedef void (GLAPIENTRY * PFNDISABLECLIENTSTATE) (GLenum capability);
+ PFNDISABLECLIENTSTATE fDisableClientState;
+ typedef void (GLAPIENTRY * PFNENABLECLIENTSTATE) (GLenum capability);
+ PFNENABLECLIENTSTATE fEnableClientState;
+ typedef void (GLAPIENTRY * PFNLOADIDENTITY) (void);
+ PFNLOADIDENTITY fLoadIdentity;
+ typedef void (GLAPIENTRY * PFNLOADMATRIXD) (const GLdouble* matrix);
+ PFNLOADMATRIXD fLoadMatrixd;
+ typedef void (GLAPIENTRY * PFNLOADMATRIXF) (const GLfloat* matrix);
+ PFNLOADMATRIXF fLoadMatrixf;
+ typedef void (GLAPIENTRY * PFNMATRIXMODE) (GLenum mode);
+ PFNMATRIXMODE fMatrixMode;
+ typedef void (GLAPIENTRY * PFNTEXGENI) (GLenum coord, GLenum pname, GLint param);
+ PFNTEXGENI fTexGeni;
+ typedef void (GLAPIENTRY * PFNTEXGENF) (GLenum coord, GLenum pname, GLfloat param);
+ PFNTEXGENF fTexGenf;
+ typedef void (GLAPIENTRY * PFNTEXGENFV) (GLenum coord, GLenum pname, const GLfloat* param);
+ PFNTEXGENFV fTexGenfv;
+ typedef void (GLAPIENTRY * PFNVERTEXPOINTER) (GLint size, GLenum type, GLsizei stride, const GLvoid* pointer);
+ PFNVERTEXPOINTER fVertexPointer;
+
+ typedef void (GLAPIENTRY * PFNGLBLITFRAMEBUFFER) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+ PFNGLBLITFRAMEBUFFER fBlitFramebuffer;
+ typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGEMULTISAMPLE) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height);
+ PFNGLRENDERBUFFERSTORAGEMULTISAMPLE fRenderbufferStorageMultisample;
+
+
+ /* These are different between GLES2 and desktop GL; we hide those differences, use the GL
+ * names, but the most limited data type.
+ */
+ typedef void (GLAPIENTRY * PFNGLDEPTHRANGEFPROC) (GLclampf, GLclampf);
+ PFNGLDEPTHRANGEFPROC fDepthRangef;
+ typedef void (GLAPIENTRY * PFNGLCLEARDEPTHFPROC) (GLclampf);
+ PFNGLCLEARDEPTHFPROC fClearDepthf;
+
+ typedef void (GLAPIENTRY * PFNGLDEPTHRANGEPROC) (GLclampd, GLclampd);
+ PFNGLDEPTHRANGEPROC fDepthRange;
+ typedef void (GLAPIENTRY * PFNGLCLEARDEPTHPROC) (GLclampd);
+ PFNGLCLEARDEPTHPROC fClearDepth;
+
+ /* These are special because we end up tracking these so that we don't
+ * have to query the values from GL.
+ */
+
+ typedef void (GLAPIENTRY * PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNGLVIEWPORTPROC fViewport;
+ typedef void (GLAPIENTRY * PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNGLSCISSORPROC fScissor;
+
+
+ /* These are special -- they create or delete GL resources that can live
+ * in a shared namespace. In DEBUG, we wrap these calls so that we can
+ * check when we have something that failed to do cleanup at the time the
+ * final context is destroyed.
+ */
+
+ typedef GLuint (GLAPIENTRY * PFNGLCREATEPROGRAMPROC) (void);
+ PFNGLCREATEPROGRAMPROC fCreateProgram;
+ typedef GLuint (GLAPIENTRY * PFNGLCREATESHADERPROC) (GLenum type);
+ PFNGLCREATESHADERPROC fCreateShader;
+ typedef void (GLAPIENTRY * PFNGLGENBUFFERSPROC) (GLsizei n, GLuint* buffers);
+ PFNGLGENBUFFERSPROC fGenBuffers;
+ typedef void (GLAPIENTRY * PFNGLGENQUERIESPROC) (GLsizei n, GLuint* queries);
+ PFNGLGENQUERIESPROC fGenQueries;
+ typedef void (GLAPIENTRY * PFNGLGENTEXTURESPROC) (GLsizei n, GLuint* textures);
+ PFNGLGENTEXTURESPROC fGenTextures;
+ typedef void (GLAPIENTRY * PFNGLGENFRAMEBUFFERS) (GLsizei n, GLuint* ids);
+ PFNGLGENFRAMEBUFFERS fGenFramebuffers;
+ typedef void (GLAPIENTRY * PFNGLGENRENDERBUFFERS) (GLsizei n, GLuint* ids);
+ PFNGLGENRENDERBUFFERS fGenRenderbuffers;
+ typedef void (GLAPIENTRY * PFNGLGENVERTEXARRAYS) (GLsizei n, GLuint* arrays);
+ PFNGLGENVERTEXARRAYS fGenVertexArrays;
+
+ typedef void (GLAPIENTRY * PFNGLDELETEPROGRAMPROC) (GLuint program);
+ PFNGLDELETEPROGRAMPROC fDeleteProgram;
+ typedef void (GLAPIENTRY * PFNGLDELETESHADERPROC) (GLuint shader);
+ PFNGLDELETESHADERPROC fDeleteShader;
+ typedef void (GLAPIENTRY * PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint* buffers);
+ PFNGLDELETEBUFFERSPROC fDeleteBuffers;
+ typedef void (GLAPIENTRY * PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint* queries);
+ PFNGLDELETEQUERIESPROC fDeleteQueries;
+ typedef void (GLAPIENTRY * PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint* textures);
+ PFNGLDELETETEXTURESPROC fDeleteTextures;
+ typedef void (GLAPIENTRY * PFNGLDELETEFRAMEBUFFERS) (GLsizei n, const GLuint* ids);
+ PFNGLDELETEFRAMEBUFFERS fDeleteFramebuffers;
+ typedef void (GLAPIENTRY * PFNGLDELETERENDERBUFFERS) (GLsizei n, const GLuint* ids);
+ PFNGLDELETERENDERBUFFERS fDeleteRenderbuffers;
+ typedef void (GLAPIENTRY * PFNGLDELETEVERTEXARRAYS) (GLsizei n, const GLuint* arrays);
+ PFNGLDELETEVERTEXARRAYS fDeleteVertexArrays;
+
+ typedef void* (GLAPIENTRY * PFNGLMAPBUFFER) (GLenum target, GLenum access);
+ PFNGLMAPBUFFER fMapBuffer;
+ typedef realGLboolean (GLAPIENTRY * PFNGLUNMAPBUFFER) (GLenum target);
+ PFNGLUNMAPBUFFER fUnmapBuffer;
+
+ // ARB_copy_buffer / OpenGL 3.1 / OpenGL ES 3.0
+ typedef void (GLAPIENTRY * PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget,
+ GLintptr readOffset, GLintptr writeOffset,
+ GLsizeiptr size);
+ PFNGLCOPYBUFFERSUBDATAPROC fCopyBufferSubData;
+
+ typedef GLenum (GLAPIENTRY * PFNGLGETGRAPHICSRESETSTATUS) (void);
+ PFNGLGETGRAPHICSRESETSTATUS fGetGraphicsResetStatus;
+
+ // ARB_sync
+ typedef GLsync (GLAPIENTRY * PFNGLFENCESYNC) (GLenum condition, GLbitfield flags);
+ PFNGLFENCESYNC fFenceSync;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISSYNC) (GLsync sync);
+ PFNGLISSYNC fIsSync;
+ typedef void (GLAPIENTRY * PFNGLDELETESYNC) (GLsync sync);
+ PFNGLDELETESYNC fDeleteSync;
+ typedef GLenum (GLAPIENTRY * PFNGLCLIENTWAITSYNC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
+ PFNGLCLIENTWAITSYNC fClientWaitSync;
+ typedef void (GLAPIENTRY * PFNGLWAITSYNC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
+ PFNGLWAITSYNC fWaitSync;
+ typedef void (GLAPIENTRY * PFNGLGETINTEGER64V) (GLenum pname, GLint64* params);
+ PFNGLGETINTEGER64V fGetInteger64v;
+ typedef void (GLAPIENTRY * PFNGLGETSYNCIV) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values);
+ PFNGLGETSYNCIV fGetSynciv;
+
+ // OES_egl_image
+ typedef void (GLAPIENTRY * PFNGLEGLIMAGETARGETTEXTURE2D)(GLenum target, GLeglImage image);
+ PFNGLEGLIMAGETARGETTEXTURE2D fEGLImageTargetTexture2D;
+ typedef void (GLAPIENTRY * PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGE)(GLenum target, GLeglImage image);
+ PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGE fEGLImageTargetRenderbufferStorage;
+
+ // ARB_draw_instanced
+ typedef void (GLAPIENTRY * PFNGLDRAWARRAYSINSTANCED) (GLenum mode, GLint first, GLsizei count, GLsizei primcount);
+ PFNGLDRAWARRAYSINSTANCED fDrawArraysInstanced;
+ typedef void (GLAPIENTRY * PFNGLDRAWELEMENTSINSTANCED) (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLsizei primcount);
+ PFNGLDRAWELEMENTSINSTANCED fDrawElementsInstanced;
+
+ // ARB_instanced_array
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBDIVISOR) (GLuint index, GLuint divisor);
+ PFNGLVERTEXATTRIBDIVISOR fVertexAttribDivisor;
+
+ // ARB_internalformat_query
+ typedef void (GLAPIENTRY * PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params);
+ PFNGLGETINTERNALFORMATIVPROC fGetInternalformativ;
+
+ // ARB_transform_feedback2 / OpenGL 4.0 / OpenGL ES 3.0
+ typedef void (GLAPIENTRY * PFNGLBINDBUFFERBASE) (GLenum target, GLuint index, GLuint buffer);
+ PFNGLBINDBUFFERBASE fBindBufferBase;
+ typedef void (GLAPIENTRY * PFNGLBINDBUFFERRANGE) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
+ PFNGLBINDBUFFERRANGE fBindBufferRange;
+
+ typedef void (GLAPIENTRY * PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint* ids);
+ PFNGLGENTRANSFORMFEEDBACKSPROC fGenTransformFeedbacks;
+ typedef void (GLAPIENTRY * PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint* ids);
+ PFNGLDELETETRANSFORMFEEDBACKSPROC fDeleteTransformFeedbacks;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id);
+ PFNGLISTRANSFORMFEEDBACKPROC fIsTransformFeedback;
+ typedef void (GLAPIENTRY * PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id);
+ PFNGLBINDTRANSFORMFEEDBACKPROC fBindTransformFeedback;
+ typedef void (GLAPIENTRY * PFNGLTRANSFORMFEEDBACKVARYINGS) (GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode);
+ PFNGLTRANSFORMFEEDBACKVARYINGS fTransformFeedbackVaryings;
+ typedef void (GLAPIENTRY * PFNGLGETTRANSFORMFEEDBACKVARYING) (GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name);
+ PFNGLGETTRANSFORMFEEDBACKVARYING fGetTransformFeedbackVarying;
+ typedef void (GLAPIENTRY * PFNGLBEGINTRANSFORMFEEDBACK) (GLenum primitiveMode);
+ PFNGLBEGINTRANSFORMFEEDBACK fBeginTransformFeedback;
+ typedef void (GLAPIENTRY * PFNGLENDTRANSFORMFEEDBACK) (void);
+ PFNGLENDTRANSFORMFEEDBACK fEndTransformFeedback;
+ typedef void (GLAPIENTRY * PFNGLPAUSETRANSFORMFEEDBACKPROC) ();
+ PFNGLPAUSETRANSFORMFEEDBACKPROC fPauseTransformFeedback;
+ typedef void (GLAPIENTRY * PFNGLRESUMETRANSFORMFEEDBACKPROC) ();
+ PFNGLRESUMETRANSFORMFEEDBACKPROC fResumeTransformFeedback;
+
+ typedef void (GLAPIENTRY * PFNGLGETINTEGERI_V) (GLenum param, GLuint index, GLint* values);
+ PFNGLGETINTEGERI_V fGetIntegeri_v;
+ typedef void (GLAPIENTRY * PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64* data);
+ PFNGLGETINTEGER64I_VPROC fGetInteger64i_v;
+
+ // EXT_transform_feedback only
+ typedef void (GLAPIENTRY * PFNGLBINDBUFFEROFFSET) (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
+ PFNGLBINDBUFFEROFFSET fBindBufferOffset;
+
+ // KHR_debug
+ typedef void (GLAPIENTRY * PFNGLDEBUGMESSAGECONTROL) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, realGLboolean enabled);
+ PFNGLDEBUGMESSAGECONTROL fDebugMessageControl;
+ typedef void (GLAPIENTRY * PFNGLDEBUGMESSAGEINSERT) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf);
+ PFNGLDEBUGMESSAGEINSERT fDebugMessageInsert;
+ typedef void (GLAPIENTRY * PFNGLDEBUGMESSAGECALLBACK) (GLDEBUGPROC callback, const GLvoid* userParam);
+ PFNGLDEBUGMESSAGECALLBACK fDebugMessageCallback;
+ typedef GLuint (GLAPIENTRY * PFNGLDEBUGMESSAGELOG) (GLuint count, GLsizei bufsize, GLenum* sources, GLenum* types, GLuint* ids, GLenum* severities, GLsizei* lengths, GLchar* messageLog);
+ PFNGLDEBUGMESSAGELOG fGetDebugMessageLog;
+ typedef void (GLAPIENTRY * PFNGLGETPOINTERV) (GLenum pname, GLvoid** params);
+ PFNGLGETPOINTERV fGetPointerv;
+ typedef void (GLAPIENTRY * PFNGLPUSHDEBUGGROUP) (GLenum source, GLuint id, GLsizei length, const GLchar* message);
+ PFNGLPUSHDEBUGGROUP fPushDebugGroup;
+ typedef void (GLAPIENTRY * PFNGLPOPDEBUGGROUP) (void);
+ PFNGLPOPDEBUGGROUP fPopDebugGroup;
+ typedef void (GLAPIENTRY * PFNGLOBJECTLABEL) (GLenum identifier, GLuint name, GLsizei length, const GLchar* label);
+ PFNGLOBJECTLABEL fObjectLabel;
+ typedef void (GLAPIENTRY * PFNGLGETOBJECTLABEL) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei* length, GLchar* label);
+ PFNGLGETOBJECTLABEL fGetObjectLabel;
+ typedef void (GLAPIENTRY * PFNGLOBJECTPTRLABEL) (const GLvoid* ptr, GLsizei length, const GLchar* label);
+ PFNGLOBJECTPTRLABEL fObjectPtrLabel;
+ typedef void (GLAPIENTRY * PFNGLGETOBJECTPTRLABEL) (const GLvoid* ptr, GLsizei bufSize, GLsizei* length, GLchar* label);
+ PFNGLGETOBJECTPTRLABEL fGetObjectPtrLabel;
+
+ // draw_range_elements
+ typedef void (GLAPIENTRY * PFNGLDRAWRANGEELEMENTS) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices);
+ PFNGLDRAWRANGEELEMENTS fDrawRangeElements;
+
+ // NV_fence
+ typedef void (GLAPIENTRY * pfnGenFencesT) (GLsizei n, GLuint* fences);
+ pfnGenFencesT fGenFences;
+ typedef void (GLAPIENTRY * pfnDeleteFencesT) (GLsizei n, const GLuint* fences);
+ pfnDeleteFencesT fDeleteFences;
+ typedef void (GLAPIENTRY * pfnSetFenceT) (GLuint fence, GLenum condition);
+ pfnSetFenceT fSetFence;
+ typedef realGLboolean (GLAPIENTRY * pfnTestFenceT) (GLuint fence);
+ pfnTestFenceT fTestFence;
+ typedef void (GLAPIENTRY * pfnFinishFenceT) (GLuint fence);
+ pfnFinishFenceT fFinishFence;
+ typedef realGLboolean (GLAPIENTRY * pfnIsFenceT) (GLuint fence);
+ pfnIsFenceT fIsFence;
+ typedef void (GLAPIENTRY * pfnGetFenceivT) (GLuint fence, GLenum pname, GLint* params);
+ pfnGetFenceivT fGetFenceiv;
+
+ // map_buffer_range
+ typedef void* (GLAPIENTRY * PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+ PFNGLMAPBUFFERRANGEPROC fMapBufferRange;
+ typedef void (GLAPIENTRY * PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length);
+ PFNGLFLUSHMAPPEDBUFFERRANGEPROC fFlushMappedBufferRange;
+
+ // sampler_object
+ typedef void (GLAPIENTRY * PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint* samplers);
+ PFNGLGENSAMPLERSPROC fGenSamplers;
+ typedef void (GLAPIENTRY * PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint* samplers);
+ PFNGLDELETESAMPLERSPROC fDeleteSamplers;
+ typedef realGLboolean (GLAPIENTRY * PFNGLISSAMPLERPROC) (GLuint sampler);
+ PFNGLISSAMPLERPROC fIsSampler;
+ typedef void (GLAPIENTRY * PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
+ PFNGLBINDSAMPLERPROC fBindSampler;
+ typedef void (GLAPIENTRY * PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param);
+ PFNGLSAMPLERPARAMETERIPROC fSamplerParameteri;
+ typedef void (GLAPIENTRY * PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint* param);
+ PFNGLSAMPLERPARAMETERIVPROC fSamplerParameteriv;
+ typedef void (GLAPIENTRY * PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param);
+ PFNGLSAMPLERPARAMETERFPROC fSamplerParameterf;
+ typedef void (GLAPIENTRY * PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat* param);
+ PFNGLSAMPLERPARAMETERFVPROC fSamplerParameterfv;
+ typedef void (GLAPIENTRY * PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint* params);
+ PFNGLGETSAMPLERPARAMETERIVPROC fGetSamplerParameteriv;
+ typedef void (GLAPIENTRY * PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat* params);
+ PFNGLGETSAMPLERPARAMETERFVPROC fGetSamplerParameterfv;
+
+ // texture_storage
+ typedef void (GLAPIENTRY * PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
+ PFNGLTEXSTORAGE2DPROC fTexStorage2D;
+ typedef void (GLAPIENTRY * PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
+ PFNGLTEXSTORAGE3DPROC fTexStorage3D;
+
+ // uniform_buffer_object
+ typedef void (GLAPIENTRY * PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount,
+ const GLchar* const* uniformNames, GLuint* uniformIndices);
+ PFNGLGETUNIFORMINDICESPROC fGetUniformIndices;
+ typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint* uniformIndices,
+ GLenum pname, GLint* params);
+ PFNGLGETACTIVEUNIFORMSIVPROC fGetActiveUniformsiv;
+ typedef GLuint (GLAPIENTRY * PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar* uniformBlockName);
+ PFNGLGETUNIFORMBLOCKINDEXPROC fGetUniformBlockIndex;
+ typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params);
+ PFNGLGETACTIVEUNIFORMBLOCKIVPROC fGetActiveUniformBlockiv;
+ typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName);
+ PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC fGetActiveUniformBlockName;
+ typedef void (GLAPIENTRY * PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+ PFNGLUNIFORMBLOCKBINDINGPROC fUniformBlockBinding;
+
+ // EXT_gpu_shader4
+ typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint* params);
+ PFNGLGETVERTEXATTRIBIIVPROC fGetVertexAttribIiv;
+ typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint* params);
+ PFNGLGETVERTEXATTRIBIUIVPROC fGetVertexAttribIuiv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w);
+ PFNGLVERTEXATTRIBI4IPROC fVertexAttribI4i;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint* v);
+ PFNGLVERTEXATTRIBI4IVPROC fVertexAttribI4iv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
+ PFNGLVERTEXATTRIBI4UIPROC fVertexAttribI4ui;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint* v);
+ PFNGLVERTEXATTRIBI4UIVPROC fVertexAttribI4uiv;
+ typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* ptr);
+ PFNGLVERTEXATTRIBIPOINTERPROC fVertexAttribIPointer;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0);
+ PFNGLUNIFORM1UIPROC fUniform1ui;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1);
+ PFNGLUNIFORM2UIPROC fUniform2ui;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2);
+ PFNGLUNIFORM3UIPROC fUniform3ui;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
+ PFNGLUNIFORM4UIPROC fUniform4ui;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint* value);
+ PFNGLUNIFORM1UIVPROC fUniform1uiv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint* value);
+ PFNGLUNIFORM2UIVPROC fUniform2uiv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint* value);
+ PFNGLUNIFORM3UIVPROC fUniform3uiv;
+ typedef void (GLAPIENTRY * PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint* value);
+ PFNGLUNIFORM4UIVPROC fUniform4uiv;
+ typedef GLint (GLAPIENTRY * PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar* name);
+ PFNGLGETFRAGDATALOCATIONPROC fGetFragDataLocation;
+
+ // 3D Textures
+ typedef void (GLAPIENTRY * PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level,
+ GLenum internalFormat,
+ GLenum width, GLsizei height, GLsizei depth,
+ GLint border, GLenum format, GLenum type,
+ const GLvoid* pixels);
+ PFNGLTEXIMAGE3DPROC fTexImage3D;
+ typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset,
+ GLint yoffset, GLint zoffset, GLsizei width,
+ GLsizei height, GLsizei depth, GLenum format,
+ GLenum type, const GLvoid* pixels);
+ PFNGLTEXSUBIMAGE3DPROC fTexSubImage3D;
+ typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset,
+ GLint yoffset, GLint zoffset, GLint x,
+ GLint y, GLsizei width, GLsizei height);
+ PFNGLCOPYTEXSUBIMAGE3DPROC fCopyTexSubImage3D;
+ typedef void (GLAPIENTRY * PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLint border, GLsizei imageSize, const GLvoid* data);
+ PFNGLCOMPRESSEDTEXIMAGE3DPROC fCompressedTexImage3D;
+ typedef void (GLAPIENTRY * PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint zoffset,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLenum format, GLsizei imageSize, const GLvoid* data);
+ PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC fCompressedTexSubImage3D;
+
+ // get_string_indexed
+ typedef const GLubyte* (GLAPIENTRY * PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);
+ PFNGLGETSTRINGIPROC fGetStringi;
+
+ // APPLE_framebuffer_multisample
+ typedef void (GLAPIENTRY * PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE) (void);
+ PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE fResolveMultisampleFramebufferAPPLE;
+
+ // NV_texture_barrier
+ typedef void (GLAPIENTRY * PFNTEXTUREBARRIERPROC) (void);
+ PFNTEXTUREBARRIERPROC fTextureBarrier;
+
+ // NV_primitive_restart
+ void (GLAPIENTRY * fPrimitiveRestartIndex) (GLuint index);
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif /* GLCONTEXTSYMBOLS_H_ */
diff --git a/system/graphics/gl/GLContextTypes.cpp b/system/graphics/gl/GLContextTypes.cpp
new file mode 100644
index 000000000..2cca59dc9
--- /dev/null
+++ b/system/graphics/gl/GLContextTypes.cpp
@@ -0,0 +1,14 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContextTypes.h"
+#include <cstring>
+
+using namespace mozilla::gl;
+
+GLFormats::GLFormats()
+{
+ std::memset(this, 0, sizeof(GLFormats));
+}
diff --git a/system/graphics/gl/GLContextTypes.h b/system/graphics/gl/GLContextTypes.h
new file mode 100644
index 000000000..de59acf49
--- /dev/null
+++ b/system/graphics/gl/GLContextTypes.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXT_TYPES_H_
+#define GLCONTEXT_TYPES_H_
+
+#include "GLTypes.h"
+#include "mozilla/TypedEnumBits.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+
+enum class GLContextType {
+ Unknown,
+ WGL,
+ CGL,
+ GLX,
+ EGL,
+ EAGL
+};
+
+enum class OriginPos : uint8_t {
+ TopLeft,
+ BottomLeft
+};
+
+struct GLFormats
+{
+ // Constructs a zeroed object:
+ GLFormats();
+
+ GLenum color_texInternalFormat;
+ GLenum color_texFormat;
+ GLenum color_texType;
+ GLenum color_rbFormat;
+
+ GLenum depthStencil;
+ GLenum depth;
+ GLenum stencil;
+
+ GLsizei samples;
+};
+
+enum class CreateContextFlags : int8_t {
+ NONE = 0,
+ REQUIRE_COMPAT_PROFILE = 1 << 0,
+ // Force the use of hardware backed GL, don't allow software implementations.
+ FORCE_ENABLE_HARDWARE = 1 << 1,
+ /* Don't force discrete GPU to be used (if applicable) */
+ ALLOW_OFFLINE_RENDERER = 1 << 2,
+ // Ask for ES3 if possible
+ PREFER_ES3 = 1 << 3,
+
+ NO_VALIDATION = 1 << 4,
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags)
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* GLCONTEXT_TYPES_H_ */
diff --git a/system/graphics/gl/GLContextWGL.h b/system/graphics/gl/GLContextWGL.h
new file mode 100644
index 000000000..52d28042c
--- /dev/null
+++ b/system/graphics/gl/GLContextWGL.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTWGL_H_
+#define GLCONTEXTWGL_H_
+
+#include "GLContext.h"
+#include "WGLLibrary.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContextWGL : public GLContext
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextWGL, override)
+ // From Window: (possibly for offscreen!)
+ GLContextWGL(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ bool isOffscreen,
+ HDC aDC,
+ HGLRC aContext,
+ HWND aWindow = nullptr);
+
+ // From PBuffer
+ GLContextWGL(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ bool isOffscreen,
+ HANDLE aPbuffer,
+ HDC aDC,
+ HGLRC aContext,
+ int aPixelFormat);
+
+ ~GLContextWGL();
+
+ virtual GLContextType GetContextType() const override { return GLContextType::WGL; }
+
+ static GLContextWGL* Cast(GLContext* gl) {
+ MOZ_ASSERT(gl->GetContextType() == GLContextType::WGL);
+ return static_cast<GLContextWGL*>(gl);
+ }
+
+ bool Init() override;
+
+ virtual bool MakeCurrentImpl(bool aForce) override;
+
+ virtual bool IsCurrent() override;
+
+ void SetIsDoubleBuffered(bool aIsDB);
+
+ virtual bool IsDoubleBuffered() const override;
+
+ virtual bool SupportsRobustness() const override;
+
+ virtual bool SwapBuffers() override;
+
+ virtual void GetWSIInfo(nsCString* const out) const override;
+
+ virtual bool SetupLookupFunction() override;
+
+ HGLRC Context() { return mContext; }
+
+protected:
+ friend class GLContextProviderWGL;
+
+ HDC mDC;
+ HGLRC mContext;
+ HWND mWnd;
+ HANDLE mPBuffer;
+ int mPixelFormat;
+ bool mIsDoubleBuffered;
+};
+
+}
+}
+
+#endif // GLCONTEXTWGL_H_
diff --git a/system/graphics/gl/GLDebugUtils.cpp b/system/graphics/gl/GLDebugUtils.cpp
new file mode 100644
index 000000000..cc8c611d4
--- /dev/null
+++ b/system/graphics/gl/GLDebugUtils.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLDebugUtils.h"
+#include "GLConsts.h"
+
+namespace mozilla {
+namespace gl {
+
+const char*
+GLenumToStr(GLenum e) {
+ switch (e) {
+#define HANDLE_GL_ENUM(x) case LOCAL_##x: return #x
+ HANDLE_GL_ENUM(GL_TRIANGLES);
+ HANDLE_GL_ENUM(GL_TRIANGLE_STRIP);
+ HANDLE_GL_ENUM(GL_TRIANGLE_FAN);
+ HANDLE_GL_ENUM(GL_FRAMEBUFFER);
+ HANDLE_GL_ENUM(GL_RENDERBUFFER);
+ HANDLE_GL_ENUM(GL_DEPTH_ATTACHMENT);
+ HANDLE_GL_ENUM(GL_STENCIL_ATTACHMENT);
+ HANDLE_GL_ENUM(GL_DEPTH_STENCIL_ATTACHMENT);
+ HANDLE_GL_ENUM(GL_TEXTURE_2D);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_NEGATIVE_X);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_POSITIVE_X);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_POSITIVE_Y);
+ HANDLE_GL_ENUM(GL_TEXTURE_CUBE_MAP_POSITIVE_Z);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT0);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT1);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT2);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT3);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT4);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT5);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT6);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT7);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT8);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT9);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT10);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT11);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT12);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT13);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT14);
+ HANDLE_GL_ENUM(GL_COLOR_ATTACHMENT15);
+ HANDLE_GL_ENUM(GL_UNSIGNED_BYTE);
+ HANDLE_GL_ENUM(GL_UNSIGNED_SHORT);
+ HANDLE_GL_ENUM(GL_UNSIGNED_INT);
+ HANDLE_GL_ENUM(GL_RGBA);
+ HANDLE_GL_ENUM(GL_DEPTH_COMPONENT);
+#undef HANDLE_GL_ENUM
+ }
+
+ return "(unknown)";
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/GLDebugUtils.h b/system/graphics/gl/GLDebugUtils.h
new file mode 100644
index 000000000..d82849252
--- /dev/null
+++ b/system/graphics/gl/GLDebugUtils.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLDEBUGUTILS_H_
+#define GLDEBUGUTILS_H_
+
+#include "GLTypes.h"
+
+namespace mozilla {
+namespace gl {
+
+const char* GLenumToStr(GLenum e);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // !GLDEBUGUTILS_H_
diff --git a/system/graphics/gl/GLDefs.h b/system/graphics/gl/GLDefs.h
new file mode 100644
index 000000000..bffd1a727
--- /dev/null
+++ b/system/graphics/gl/GLDefs.h
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(LOCALGL_H_)
+#define LOCALGL_H_
+
+#include "GLTypes.h"
+#include "GLConsts.h"
+
+// TODO: use official constant names instead of followed ones.
+// IMG_texture_compression_pvrtc
+#define LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1 0x8C00
+#define LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 0x8C01
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1 0x8C02
+#define LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 0x8C03
+
+// OES_EGL_image_external
+#define LOCAL_GL_TEXTURE_EXTERNAL 0x8D65
+#define LOCAL_GL_TEXTURE_BINDING_EXTERNAL 0x8D67
+
+// EGL_KHR_fence_sync
+#define LOCAL_EGL_SYNC_FENCE 0x30F9
+#define LOCAL_EGL_SYNC_TYPE 0x30F7
+#define LOCAL_EGL_SYNC_STATUS 0x30F1
+#define LOCAL_EGL_SYNC_CONDITION 0x30F8
+#define LOCAL_EGL_SIGNALED 0x30F2
+#define LOCAL_EGL_UNSIGNALED 0x30F3
+#define LOCAL_EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0
+#define LOCAL_EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
+#define LOCAL_EGL_FOREVER 0xFFFFFFFFFFFFFFFFull
+#define LOCAL_EGL_TIMEOUT_EXPIRED 0x30F5
+#define LOCAL_EGL_CONDITION_SATISFIED 0x30F6
+#define LOCAL_EGL_SUCCESS 0x3000
+
+// EGL_KHR_gl_texture_2D_image
+#define LOCAL_EGL_GL_TEXTURE_2D 0x30B1
+
+// EGL_KHR_image_base (not supplied by EGL_KHR_image!)
+#define LOCAL_EGL_IMAGE_PRESERVED 0x30D2
+
+// AMD_compressed_ATC_texture
+#define LOCAL_GL_ATC_RGB 0x8C92
+#define LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA 0x8C93
+#define LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA 0x87EE
+
+// EGL_ANGLE_platform_angle
+#define LOCAL_EGL_PLATFORM_ANGLE_ANGLE 0x3202
+#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
+#define LOCAL_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204
+#define LOCAL_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205
+#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE 0x3206
+
+// EGL_ANGLE_platform_angle_d3d
+#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
+#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
+#define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209
+#define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A
+#define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE 0x320B
+#define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE 0x320C
+#define LOCAL_EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F
+
+// EGL_ANGLE_direct3d_display
+#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
+#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
+
+// WGL_NV_DX_interop
+#define LOCAL_WGL_ACCESS_READ_ONLY 0x0000
+#define LOCAL_WGL_ACCESS_READ_WRITE 0x0001
+#define LOCAL_WGL_ACCESS_WRITE_DISCARD 0x0002
+
+// Others
+#define LOCAL_EGL_PRESERVED_RESOURCES 0x3030
+#define LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
+#define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
+#define LOCAL_GL_CONTEXT_LOST 0x9242
+#define LOCAL_GL_CONTEXT_FLAGS_ARB 0x2094
+#define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
+
+#endif
diff --git a/system/graphics/gl/GLLibraryEGL.cpp b/system/graphics/gl/GLLibraryEGL.cpp
new file mode 100644
index 000000000..c0ab95c83
--- /dev/null
+++ b/system/graphics/gl/GLLibraryEGL.cpp
@@ -0,0 +1,694 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLLibraryEGL.h"
+
+#include "angle/Platform.h"
+#include "gfxConfig.h"
+#include "gfxUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/Logging.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIGfxInfo.h"
+#include "nsPrintfCString.h"
+#ifdef XP_WIN
+#include "nsWindowsHelpers.h"
+#endif
+#include "OGLShaderProgram.h"
+#include "prenv.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "gfxEnv.h"
+#include "gfxPrefs.h"
+#include "ScopedGLHelpers.h"
+#include "GLReadTexImageHelper.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+namespace mozilla {
+namespace gl {
+
+StaticMutex GLLibraryEGL::sMutex;
+GLLibraryEGL sEGLLibrary;
+
+// should match the order of EGLExtensions, and be null-terminated.
+static const char* sEGLExtensionNames[] = {
+ "EGL_KHR_image_base",
+ "EGL_KHR_image_pixmap",
+ "EGL_KHR_gl_texture_2D_image",
+ "EGL_KHR_lock_surface",
+ "EGL_ANGLE_surface_d3d_texture_2d_share_handle",
+ "EGL_EXT_create_context_robustness",
+ "EGL_KHR_image",
+ "EGL_KHR_fence_sync",
+ "EGL_ANGLE_platform_angle",
+ "EGL_ANGLE_platform_angle_d3d"
+};
+
+#ifdef XP_WIN
+// see the comment in GLLibraryEGL::EnsureInitialized() for the rationale here.
+static PRLibrary*
+LoadLibraryForEGLOnWindows(const nsAString& filename)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ file->Append(filename);
+ PRLibrary* lib = nullptr;
+ rv = file->Load(&lib);
+ if (NS_FAILED(rv)) {
+ nsPrintfCString msg("Failed to load %s - Expect EGL initialization to fail",
+ NS_LossyConvertUTF16toASCII(filename).get());
+ NS_WARNING(msg.get());
+ }
+ return lib;
+}
+
+#endif // XP_WIN
+
+static EGLDisplay
+GetAndInitWARPDisplay(GLLibraryEGL& egl, void* displayType)
+{
+ EGLint attrib_list[] = { LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
+ LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
+ // Requires:
+ LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
+ LOCAL_EGL_NONE };
+ EGLDisplay display = egl.fGetPlatformDisplayEXT(LOCAL_EGL_PLATFORM_ANGLE_ANGLE,
+ displayType,
+ attrib_list);
+
+ if (display == EGL_NO_DISPLAY) {
+ const EGLint err = egl.fGetError();
+ if (err != LOCAL_EGL_SUCCESS) {
+ gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
+ MOZ_CRASH("GFX: Unexpected GL error.");
+ }
+ return EGL_NO_DISPLAY;
+ }
+
+ if (!egl.fInitialize(display, nullptr, nullptr))
+ return EGL_NO_DISPLAY;
+
+ return display;
+}
+
+static bool
+IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ nsACString* const out_failureId)
+{
+ int32_t angleSupport;
+ nsCString failureId;
+ gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+ nsIGfxInfo::FEATURE_WEBGL_ANGLE,
+ failureId,
+ &angleSupport);
+ if (failureId.IsEmpty() && angleSupport != nsIGfxInfo::FEATURE_STATUS_OK) {
+ // This shouldn't happen, if we see this it's because we've missed
+ // some failure paths
+ failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_ACCL_ANGLE_NOT_OK");
+ }
+ if (out_failureId->IsEmpty()) {
+ *out_failureId = failureId;
+ }
+ return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
+}
+
+static EGLDisplay
+GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
+{
+ EGLDisplay display = egl.fGetDisplay(displayType);
+ if (display == EGL_NO_DISPLAY)
+ return EGL_NO_DISPLAY;
+
+ if (!egl.fInitialize(display, nullptr, nullptr))
+ return EGL_NO_DISPLAY;
+
+ return display;
+}
+
+class AngleErrorReporting: public angle::Platform {
+public:
+ AngleErrorReporting()
+ {
+ // No static constructor
+ }
+
+ void SetFailureId(nsACString* const aFailureId)
+ {
+ mFailureId = aFailureId;
+ }
+
+ void logError(const char *errorMessage) override
+ {
+ if (!mFailureId) {
+ return;
+ }
+
+ nsCString str(errorMessage);
+ Tokenizer tokenizer(str);
+
+ // Parse "ANGLE Display::initialize error " << error.getID() << ": "
+ // << error.getMessage()
+ nsCString currWord;
+ Tokenizer::Token intToken;
+ if (tokenizer.CheckWord("ANGLE") &&
+ tokenizer.CheckWhite() &&
+ tokenizer.CheckWord("Display") &&
+ tokenizer.CheckChar(':') &&
+ tokenizer.CheckChar(':') &&
+ tokenizer.CheckWord("initialize") &&
+ tokenizer.CheckWhite() &&
+ tokenizer.CheckWord("error") &&
+ tokenizer.CheckWhite() &&
+ tokenizer.Check(Tokenizer::TOKEN_INTEGER, intToken)) {
+ *mFailureId = "FAILURE_ID_ANGLE_ID_";
+ mFailureId->AppendPrintf("%i", intToken.AsInteger());
+ } else {
+ *mFailureId = "FAILURE_ID_ANGLE_UNKNOWN";
+ }
+ }
+private:
+ nsACString* mFailureId;
+};
+
+AngleErrorReporting gAngleErrorReporter;
+
+static EGLDisplay
+GetAndInitDisplayForAccelANGLE(GLLibraryEGL& egl, nsACString* const out_failureId)
+{
+ EGLDisplay ret = 0;
+
+ FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
+
+ if (!gfxPrefs::WebGLANGLETryD3D11())
+ d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref",
+ NS_LITERAL_CSTRING("FAILURE_ID_ANGLE_PREF"));
+
+ if (gfxPrefs::WebGLANGLEForceD3D11())
+ d3d11ANGLE.UserForceEnable("User force-enabled D3D11 ANGLE on disabled hardware");
+
+ gAngleErrorReporter.SetFailureId(out_failureId);
+ egl.fANGLEPlatformInitialize(&gAngleErrorReporter);
+
+ auto guardShutdown = mozilla::MakeScopeExit([&] {
+ gAngleErrorReporter.SetFailureId(nullptr);
+ // NOTE: Ideally we should be calling ANGLEPlatformShutdown after the
+ // ANGLE display is destroyed. However gAngleErrorReporter
+ // will live longer than the ANGLE display so we're fine.
+ });
+
+ if (gfxConfig::IsForcedOnByUser(Feature::D3D11_HW_ANGLE)) {
+ return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
+ }
+
+ if (d3d11ANGLE.IsEnabled()) {
+ ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
+ }
+
+ if (!ret) {
+ ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY);
+ }
+
+ if (!ret && out_failureId->IsEmpty()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_ACCL_ANGLE_NO_DISP");
+ }
+
+ return ret;
+}
+
+bool
+GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface)
+{
+ StaticMutexAutoUnlock lock(sMutex);
+ if (!mReadbackGL) {
+ nsCString discardFailureId;
+ mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE,
+ &discardFailureId);
+ }
+
+ ScopedTexture destTex(mReadbackGL);
+ const GLuint target = mReadbackGL->GetPreferredEGLImageTextureTarget();
+ ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target);
+ mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
+ mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
+ mReadbackGL->fEGLImageTargetTexture2D(target, image);
+
+ ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(target,
+ out_surface->GetFormat());
+ int shaderConfig = config.mFeatures;
+ mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
+ out_surface->GetSize(), shaderConfig);
+
+ return true;
+}
+
+bool
+GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId)
+{
+ if (mInitialized) {
+ return true;
+ }
+
+#ifdef XP_WIN
+ if (!mEGLLibrary) {
+ // On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul and
+ // we should look for them there. We have to load the libs in this
+ // order, because libEGL.dll depends on libGLESv2.dll which depends on the DXSDK
+ // libraries. This matters especially for WebRT apps which are in a different directory.
+ // See bug 760323 and bug 749459
+
+ // Also note that we intentionally leak the libs we load.
+
+ do {
+ // Windows 8.1 has d3dcompiler_47.dll in the system directory.
+ // Try it first. Note that _46 will never be in the system
+ // directory and we ship with at least _43. So there is no point
+ // trying _46 and _43 in the system directory.
+
+ if (LoadLibrarySystem32(L"d3dcompiler_47.dll"))
+ break;
+
+#ifdef MOZ_D3DCOMPILER_VISTA_DLL
+ if (LoadLibraryForEGLOnWindows(NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_VISTA_DLL))))
+ break;
+#endif
+
+#ifdef MOZ_D3DCOMPILER_XP_DLL
+ if (LoadLibraryForEGLOnWindows(NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_XP_DLL))))
+ break;
+#endif
+
+ MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
+ } while (false);
+
+ LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
+
+ mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
+
+ if (!mEGLLibrary) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD");
+ return false;
+ }
+ }
+
+#else // !Windows
+
+ // On non-Windows we use system copies of libEGL. We look for
+ // libEGL.so and libEGL.so.1 in that order.
+
+ if (!mEGLLibrary) {
+ printf_stderr("Attempting load of libEGL.so\n");
+ mEGLLibrary = PR_LoadLibrary("libEGL.so");
+ }
+#if defined(XP_UNIX)
+ if (!mEGLLibrary) {
+ mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
+ }
+#endif
+
+ if (!mEGLLibrary) {
+ NS_WARNING("Couldn't load EGL LIB.");
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_2");
+ return false;
+ }
+
+#endif // !Windows
+
+#define SYMBOL(name) \
+{ (PRFuncPtr*) &mSymbols.f##name, { "egl" #name, nullptr } }
+
+ GLLibraryLoader::SymLoadStruct earlySymbols[] = {
+ SYMBOL(GetDisplay),
+ SYMBOL(Terminate),
+ SYMBOL(GetCurrentSurface),
+ SYMBOL(GetCurrentContext),
+ SYMBOL(MakeCurrent),
+ SYMBOL(DestroyContext),
+ SYMBOL(CreateContext),
+ SYMBOL(DestroySurface),
+ SYMBOL(CreateWindowSurface),
+ SYMBOL(CreatePbufferSurface),
+ SYMBOL(CreatePixmapSurface),
+ SYMBOL(BindAPI),
+ SYMBOL(Initialize),
+ SYMBOL(ChooseConfig),
+ SYMBOL(GetError),
+ SYMBOL(GetConfigs),
+ SYMBOL(GetConfigAttrib),
+ SYMBOL(WaitNative),
+ SYMBOL(GetProcAddress),
+ SYMBOL(SwapBuffers),
+ SYMBOL(CopyBuffers),
+ SYMBOL(QueryString),
+ SYMBOL(QueryContext),
+ SYMBOL(BindTexImage),
+ SYMBOL(ReleaseTexImage),
+ SYMBOL(QuerySurface),
+ { nullptr, { nullptr } }
+ };
+
+ if (!GLLibraryLoader::LoadSymbols(mEGLLibrary, &earlySymbols[0])) {
+ NS_WARNING("Couldn't find required entry points in EGL library (early init)");
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
+ return false;
+ }
+
+ InitClientExtensions();
+
+ const auto lookupFunction =
+ (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
+
+ // Client exts are ready. (But not display exts!)
+ if (IsExtensionSupported(ANGLE_platform_angle_d3d)) {
+ GLLibraryLoader::SymLoadStruct d3dSymbols[] = {
+ { (PRFuncPtr*)&mSymbols.fANGLEPlatformInitialize, { "ANGLEPlatformInitialize", nullptr } },
+ { (PRFuncPtr*)&mSymbols.fANGLEPlatformShutdown, { "ANGLEPlatformShutdown", nullptr } },
+ { (PRFuncPtr*)&mSymbols.fGetPlatformDisplayEXT, { "eglGetPlatformDisplayEXT", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+ &d3dSymbols[0],
+ lookupFunction);
+ if (!success) {
+ NS_ERROR("EGL supports ANGLE_platform_angle_d3d without exposing its functions!");
+
+ MarkExtensionUnsupported(ANGLE_platform_angle_d3d);
+
+ mSymbols.fGetPlatformDisplayEXT = nullptr;
+ }
+ }
+
+ // Check the ANGLE support the system has
+ nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+ mIsANGLE = IsExtensionSupported(ANGLE_platform_angle);
+
+ EGLDisplay chosenDisplay = nullptr;
+
+ if (IsExtensionSupported(ANGLE_platform_angle_d3d)) {
+ nsCString accelAngleFailureId;
+ bool accelAngleSupport = IsAccelAngleSupported(gfxInfo, &accelAngleFailureId);
+ bool shouldTryAccel = forceAccel || accelAngleSupport;
+ bool shouldTryWARP = !forceAccel; // Only if ANGLE not supported or fails
+
+ // If WARP preferred, will override ANGLE support
+ if (gfxPrefs::WebGLANGLEForceWARP()) {
+ shouldTryWARP = true;
+ shouldTryAccel = false;
+ if (accelAngleFailureId.IsEmpty()) {
+ accelAngleFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_FORCE_WARP");
+ }
+ }
+
+ // Hardware accelerated ANGLE path (supported or force accel)
+ if (shouldTryAccel) {
+ chosenDisplay = GetAndInitDisplayForAccelANGLE(*this, out_failureId);
+ }
+
+ // Fallback to a WARP display if ANGLE fails, or if WARP is forced
+ if (!chosenDisplay && shouldTryWARP) {
+ chosenDisplay = GetAndInitWARPDisplay(*this, EGL_DEFAULT_DISPLAY);
+ if (!chosenDisplay) {
+ if (out_failureId->IsEmpty()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WARP_FALLBACK");
+ }
+ NS_ERROR("Fallback WARP context failed to initialize.");
+ return false;
+ }
+ mIsWARP = true;
+ }
+ } else {
+ chosenDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
+ }
+
+ if (!chosenDisplay) {
+ if (out_failureId->IsEmpty()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_DISPLAY");
+ }
+ NS_WARNING("Failed to initialize a display.");
+ return false;
+ }
+ mEGLDisplay = chosenDisplay;
+
+ InitDisplayExtensions();
+
+ ////////////////////////////////////
+ // Alright, load display exts.
+
+ if (IsExtensionSupported(KHR_lock_surface)) {
+ GLLibraryLoader::SymLoadStruct lockSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fLockSurface, { "eglLockSurfaceKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fUnlockSurface, { "eglUnlockSurfaceKHR", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+ &lockSymbols[0],
+ lookupFunction);
+ if (!success) {
+ NS_ERROR("EGL supports KHR_lock_surface without exposing its functions!");
+
+ MarkExtensionUnsupported(KHR_lock_surface);
+
+ mSymbols.fLockSurface = nullptr;
+ mSymbols.fUnlockSurface = nullptr;
+ }
+ }
+
+ if (IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle)) {
+ GLLibraryLoader::SymLoadStruct d3dSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fQuerySurfacePointerANGLE, { "eglQuerySurfacePointerANGLE", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+ &d3dSymbols[0],
+ lookupFunction);
+ if (!success) {
+ NS_ERROR("EGL supports ANGLE_surface_d3d_texture_2d_share_handle without exposing its functions!");
+
+ MarkExtensionUnsupported(ANGLE_surface_d3d_texture_2d_share_handle);
+
+ mSymbols.fQuerySurfacePointerANGLE = nullptr;
+ }
+ }
+
+ if (IsExtensionSupported(KHR_fence_sync)) {
+ GLLibraryLoader::SymLoadStruct syncSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCreateSync, { "eglCreateSyncKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDestroySync, { "eglDestroySyncKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fClientWaitSync, { "eglClientWaitSyncKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fGetSyncAttrib, { "eglGetSyncAttribKHR", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+ &syncSymbols[0],
+ lookupFunction);
+ if (!success) {
+ NS_ERROR("EGL supports KHR_fence_sync without exposing its functions!");
+
+ MarkExtensionUnsupported(KHR_fence_sync);
+
+ mSymbols.fCreateSync = nullptr;
+ mSymbols.fDestroySync = nullptr;
+ mSymbols.fClientWaitSync = nullptr;
+ mSymbols.fGetSyncAttrib = nullptr;
+ }
+ }
+
+ if (IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base)) {
+ GLLibraryLoader::SymLoadStruct imageSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fCreateImage, { "eglCreateImageKHR", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fDestroyImage, { "eglDestroyImageKHR", nullptr } },
+ { nullptr, { nullptr } }
+ };
+
+ bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+ &imageSymbols[0],
+ lookupFunction);
+ if (!success) {
+ NS_ERROR("EGL supports KHR_image(_base) without exposing its functions!");
+
+ MarkExtensionUnsupported(KHR_image);
+ MarkExtensionUnsupported(KHR_image_base);
+ MarkExtensionUnsupported(KHR_image_pixmap);
+
+ mSymbols.fCreateImage = nullptr;
+ mSymbols.fDestroyImage = nullptr;
+ }
+ } else {
+ MarkExtensionUnsupported(KHR_image_pixmap);
+ }
+
+ mInitialized = true;
+ return true;
+}
+
+template<size_t N>
+static void
+MarkExtensions(const char* rawExtString, bool shouldDumpExts, const char* extType,
+ std::bitset<N>* const out)
+{
+ MOZ_ASSERT(rawExtString);
+
+ const nsDependentCString extString(rawExtString);
+
+ std::vector<nsCString> extList;
+ SplitByChar(extString, ' ', &extList);
+
+ if (shouldDumpExts) {
+ printf_stderr("%u EGL %s extensions: (*: recognized)\n",
+ (uint32_t)extList.size(), extType);
+ }
+
+ MarkBitfieldByStrings(extList, shouldDumpExts, sEGLExtensionNames, out);
+}
+
+void
+GLLibraryEGL::InitClientExtensions()
+{
+ const bool shouldDumpExts = GLContext::ShouldDumpExts();
+
+ const char* rawExtString = nullptr;
+
+ rawExtString = (const char*)fQueryString(nullptr, LOCAL_EGL_EXTENSIONS);
+
+ if (!rawExtString) {
+ if (shouldDumpExts) {
+ printf_stderr("No EGL client extensions.\n");
+ }
+ return;
+ }
+
+ MarkExtensions(rawExtString, shouldDumpExts, "client", &mAvailableExtensions);
+}
+
+void
+GLLibraryEGL::InitDisplayExtensions()
+{
+ MOZ_ASSERT(mEGLDisplay);
+
+ const bool shouldDumpExts = GLContext::ShouldDumpExts();
+
+ const auto rawExtString = (const char*)fQueryString(mEGLDisplay,
+ LOCAL_EGL_EXTENSIONS);
+ if (!rawExtString) {
+ NS_WARNING("Failed to query EGL display extensions!.");
+ return;
+ }
+
+ MarkExtensions(rawExtString, shouldDumpExts, "display", &mAvailableExtensions);
+}
+
+void
+GLLibraryEGL::DumpEGLConfig(EGLConfig cfg)
+{
+ int attrval;
+ int err;
+
+#define ATTR(_x) do { \
+ fGetConfigAttrib(mEGLDisplay, cfg, LOCAL_EGL_##_x, &attrval); \
+ if ((err = fGetError()) != 0x3000) { \
+ printf_stderr(" %s: ERROR (0x%04x)\n", #_x, err); \
+ } else { \
+ printf_stderr(" %s: %d (0x%04x)\n", #_x, attrval, attrval); \
+ } \
+ } while(0)
+
+ printf_stderr("EGL Config: %d [%p]\n", (int)(intptr_t)cfg, cfg);
+
+ ATTR(BUFFER_SIZE);
+ ATTR(ALPHA_SIZE);
+ ATTR(BLUE_SIZE);
+ ATTR(GREEN_SIZE);
+ ATTR(RED_SIZE);
+ ATTR(DEPTH_SIZE);
+ ATTR(STENCIL_SIZE);
+ ATTR(CONFIG_CAVEAT);
+ ATTR(CONFIG_ID);
+ ATTR(LEVEL);
+ ATTR(MAX_PBUFFER_HEIGHT);
+ ATTR(MAX_PBUFFER_PIXELS);
+ ATTR(MAX_PBUFFER_WIDTH);
+ ATTR(NATIVE_RENDERABLE);
+ ATTR(NATIVE_VISUAL_ID);
+ ATTR(NATIVE_VISUAL_TYPE);
+ ATTR(PRESERVED_RESOURCES);
+ ATTR(SAMPLES);
+ ATTR(SAMPLE_BUFFERS);
+ ATTR(SURFACE_TYPE);
+ ATTR(TRANSPARENT_TYPE);
+ ATTR(TRANSPARENT_RED_VALUE);
+ ATTR(TRANSPARENT_GREEN_VALUE);
+ ATTR(TRANSPARENT_BLUE_VALUE);
+ ATTR(BIND_TO_TEXTURE_RGB);
+ ATTR(BIND_TO_TEXTURE_RGBA);
+ ATTR(MIN_SWAP_INTERVAL);
+ ATTR(MAX_SWAP_INTERVAL);
+ ATTR(LUMINANCE_SIZE);
+ ATTR(ALPHA_MASK_SIZE);
+ ATTR(COLOR_BUFFER_TYPE);
+ ATTR(RENDERABLE_TYPE);
+ ATTR(CONFORMANT);
+
+#undef ATTR
+}
+
+void
+GLLibraryEGL::DumpEGLConfigs()
+{
+ int nc = 0;
+ fGetConfigs(mEGLDisplay, nullptr, 0, &nc);
+ EGLConfig* ec = new EGLConfig[nc];
+ fGetConfigs(mEGLDisplay, ec, nc, &nc);
+
+ for (int i = 0; i < nc; ++i) {
+ printf_stderr("========= EGL Config %d ========\n", i);
+ DumpEGLConfig(ec[i]);
+ }
+
+ delete [] ec;
+}
+
+#ifdef DEBUG
+static bool
+ShouldTrace()
+{
+ static bool ret = gfxEnv::GlDebugVerbose();
+ return ret;
+}
+
+/*static*/ void
+GLLibraryEGL::BeforeGLCall(const char* glFunction)
+{
+ if (ShouldTrace()) {
+ printf_stderr("[egl] > %s\n", glFunction);
+ }
+}
+
+/*static*/ void
+GLLibraryEGL::AfterGLCall(const char* glFunction)
+{
+ if (ShouldTrace()) {
+ printf_stderr("[egl] < %s\n", glFunction);
+ }
+}
+#endif
+
+} /* namespace gl */
+} /* namespace mozilla */
+
diff --git a/system/graphics/gl/GLLibraryEGL.h b/system/graphics/gl/GLLibraryEGL.h
new file mode 100644
index 000000000..88f57d4c9
--- /dev/null
+++ b/system/graphics/gl/GLLibraryEGL.h
@@ -0,0 +1,658 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLLIBRARYEGL_H_
+#define GLLIBRARYEGL_H_
+
+#if defined(MOZ_X11)
+#include "mozilla/X11Util.h"
+#endif
+
+#include "GLLibraryLoader.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/ThreadLocal.h"
+#include "nsIFile.h"
+#include "GeckoProfiler.h"
+
+#include <bitset>
+#include <vector>
+
+#ifdef XP_WIN
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN 1
+ #endif
+
+ #include <windows.h>
+
+ typedef HDC EGLNativeDisplayType;
+ typedef HBITMAP EGLNativePixmapType;
+ typedef HWND EGLNativeWindowType;
+#else
+ typedef void* EGLNativeDisplayType;
+ typedef void* EGLNativePixmapType;
+ typedef void* EGLNativeWindowType;
+
+#endif
+
+#if defined(MOZ_X11)
+#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)mozilla::DefaultXDisplay())
+#else
+#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
+#endif
+
+namespace angle {
+class Platform;
+}
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+}
+
+namespace gl {
+
+#undef BEFORE_GL_CALL
+#undef AFTER_GL_CALL
+
+#ifdef DEBUG
+
+#ifndef MOZ_FUNCTION_NAME
+# ifdef __GNUC__
+# define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
+# elif defined(_MSC_VER)
+# define MOZ_FUNCTION_NAME __FUNCTION__
+# else
+# define MOZ_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
+# endif
+#endif
+
+#define BEFORE_GL_CALL do { \
+ BeforeGLCall(MOZ_FUNCTION_NAME); \
+} while (0)
+
+#define AFTER_GL_CALL do { \
+ AfterGLCall(MOZ_FUNCTION_NAME); \
+} while (0)
+#else // !DEBUG
+#define BEFORE_GL_CALL
+#define AFTER_GL_CALL
+#endif
+
+class GLContext;
+
+class GLLibraryEGL
+{
+public:
+ GLLibraryEGL()
+ : mInitialized(false),
+ mEGLLibrary(nullptr),
+ mEGLDisplay(EGL_NO_DISPLAY),
+ mIsANGLE(false),
+ mIsWARP(false)
+ {
+ ClearSymbols();
+ }
+
+ void ClearSymbols() {
+ mSymbols.fANGLEPlatformInitialize = nullptr;
+ mSymbols.fANGLEPlatformShutdown = nullptr;
+ mSymbols.fGetDisplay = nullptr;
+ mSymbols.fGetPlatformDisplayEXT = nullptr;
+ mSymbols.fTerminate = nullptr;
+ mSymbols.fGetCurrentSurface = nullptr;
+ mSymbols.fGetCurrentContext = nullptr;
+ mSymbols.fMakeCurrent = nullptr;
+ mSymbols.fDestroyContext = nullptr;
+ mSymbols.fCreateContext = nullptr;
+ mSymbols.fDestroySurface = nullptr;
+ mSymbols.fCreateWindowSurface = nullptr;
+ mSymbols.fCreatePbufferSurface = nullptr;
+ mSymbols.fCreatePixmapSurface = nullptr;
+ mSymbols.fBindAPI = nullptr;
+ mSymbols.fInitialize = nullptr;
+ mSymbols.fChooseConfig = nullptr;
+ mSymbols.fGetError = nullptr;
+ mSymbols.fGetConfigAttrib = nullptr;
+ mSymbols.fGetConfigs = nullptr;
+ mSymbols.fWaitNative = nullptr;
+ mSymbols.fGetProcAddress = nullptr;
+ mSymbols.fSwapBuffers = nullptr;
+ mSymbols.fCopyBuffers = nullptr;
+ mSymbols.fQueryString = nullptr;
+ mSymbols.fQueryStringImplementationANDROID = nullptr;
+ mSymbols.fQueryContext = nullptr;
+ mSymbols.fBindTexImage = nullptr;
+ mSymbols.fReleaseTexImage = nullptr;
+ mSymbols.fCreateImage = nullptr;
+ mSymbols.fDestroyImage = nullptr;
+ mSymbols.fLockSurface = nullptr;
+ mSymbols.fUnlockSurface = nullptr;
+ mSymbols.fQuerySurface = nullptr;
+ mSymbols.fQuerySurfacePointerANGLE = nullptr;
+ mSymbols.fCreateSync = nullptr;
+ mSymbols.fDestroySync = nullptr;
+ mSymbols.fClientWaitSync = nullptr;
+ mSymbols.fGetSyncAttrib = nullptr;
+ mSymbols.fDupNativeFenceFDANDROID = nullptr;
+ }
+
+ void InitClientExtensions();
+ void InitDisplayExtensions();
+
+ /**
+ * Known GL extensions that can be queried by
+ * IsExtensionSupported. The results of this are cached, and as
+ * such it's safe to use this even in performance critical code.
+ * If you add to this array, remember to add to the string names
+ * in GLContext.cpp.
+ */
+ enum EGLExtensions {
+ KHR_image_base,
+ KHR_image_pixmap,
+ KHR_gl_texture_2D_image,
+ KHR_lock_surface,
+ ANGLE_surface_d3d_texture_2d_share_handle,
+ EXT_create_context_robustness,
+ KHR_image,
+ KHR_fence_sync,
+ ANGLE_platform_angle,
+ ANGLE_platform_angle_d3d,
+ Extensions_Max
+ };
+
+ bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
+ return mAvailableExtensions[aKnownExtension];
+ }
+
+ void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
+ mAvailableExtensions[aKnownExtension] = false;
+ }
+
+protected:
+ std::bitset<Extensions_Max> mAvailableExtensions;
+
+public:
+
+ EGLDisplay fGetDisplay(void* display_id)
+ {
+ BEFORE_GL_CALL;
+ EGLDisplay disp = mSymbols.fGetDisplay(display_id);
+ AFTER_GL_CALL;
+ return disp;
+ }
+
+ EGLDisplay fGetPlatformDisplayEXT(EGLenum platform, void* native_display, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLDisplay disp = mSymbols.fGetPlatformDisplayEXT(platform, native_display, attrib_list);
+ AFTER_GL_CALL;
+ return disp;
+ }
+
+ EGLBoolean fTerminate(EGLDisplay display)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean ret = mSymbols.fTerminate(display);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ EGLSurface fGetCurrentSurface(EGLint id)
+ {
+ BEFORE_GL_CALL;
+ EGLSurface surf = mSymbols.fGetCurrentSurface(id);
+ AFTER_GL_CALL;
+ return surf;
+ }
+
+ EGLContext fGetCurrentContext()
+ {
+ BEFORE_GL_CALL;
+ EGLContext context = mSymbols.fGetCurrentContext();
+ AFTER_GL_CALL;
+ return context;
+ }
+
+ EGLBoolean fMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fMakeCurrent(dpy, draw, read, ctx);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fDestroyContext(EGLDisplay dpy, EGLContext ctx)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fDestroyContext(dpy, ctx);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLContext fCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLContext ctx = mSymbols.fCreateContext(dpy, config, share_context, attrib_list);
+ AFTER_GL_CALL;
+ return ctx;
+ }
+
+ EGLBoolean fDestroySurface(EGLDisplay dpy, EGLSurface surface)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fDestroySurface(dpy, surface);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLSurface fCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLSurface surf = mSymbols.fCreateWindowSurface(dpy, config, win, attrib_list);
+ AFTER_GL_CALL;
+ return surf;
+ }
+
+ EGLSurface fCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLSurface surf = mSymbols.fCreatePbufferSurface(dpy, config, attrib_list);
+ AFTER_GL_CALL;
+ return surf;
+ }
+
+ EGLSurface fCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLSurface surf = mSymbols.fCreatePixmapSurface(dpy, config, pixmap, attrib_list);
+ AFTER_GL_CALL;
+ return surf;
+ }
+
+ EGLBoolean fBindAPI(EGLenum api)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fBindAPI(api);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fInitialize(dpy, major, minor);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fChooseConfig(dpy, attrib_list, configs, config_size, num_config);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLint fGetError()
+ {
+ BEFORE_GL_CALL;
+ EGLint i = mSymbols.fGetError();
+ AFTER_GL_CALL;
+ return i;
+ }
+
+ EGLBoolean fGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fGetConfigAttrib(dpy, config, attribute, value);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fGetConfigs(dpy, configs, config_size, num_config);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fWaitNative(EGLint engine)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fWaitNative(engine);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLCastToRelevantPtr fGetProcAddress(const char* procname)
+ {
+ BEFORE_GL_CALL;
+ EGLCastToRelevantPtr p = mSymbols.fGetProcAddress(procname);
+ AFTER_GL_CALL;
+ return p;
+ }
+
+ EGLBoolean fSwapBuffers(EGLDisplay dpy, EGLSurface surface)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fSwapBuffers(dpy, surface);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fCopyBuffers(dpy, surface, target);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ const GLubyte* fQueryString(EGLDisplay dpy, EGLint name)
+ {
+ BEFORE_GL_CALL;
+ const GLubyte* b;
+ if (mSymbols.fQueryStringImplementationANDROID) {
+ b = mSymbols.fQueryStringImplementationANDROID(dpy, name);
+ } else {
+ b = mSymbols.fQueryString(dpy, name);
+ }
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fQueryContext(dpy, ctx, attribute, value);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fBindTexImage(dpy, surface, buffer);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fReleaseTexImage(dpy, surface, buffer);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLImage fCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLImage i = mSymbols.fCreateImage(dpy, ctx, target, buffer, attrib_list);
+ AFTER_GL_CALL;
+ return i;
+ }
+
+ EGLBoolean fDestroyImage(EGLDisplay dpy, EGLImage image)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fDestroyImage(dpy, image);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ // New extension which allow us to lock texture and get raw image pointer
+ EGLBoolean fLockSurface(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fLockSurface(dpy, surface, attrib_list);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fUnlockSurface(EGLDisplay dpy, EGLSurface surface)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fUnlockSurface(dpy, surface);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fQuerySurface(dpy, surface, attribute, value);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLBoolean fQuerySurfacePointerANGLE(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fQuerySurfacePointerANGLE(dpy, surface, attribute, value);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLSync fCreateSync(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list)
+ {
+ BEFORE_GL_CALL;
+ EGLSync ret = mSymbols.fCreateSync(dpy, type, attrib_list);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ EGLBoolean fDestroySync(EGLDisplay dpy, EGLSync sync)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fDestroySync(dpy, sync);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLint fClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout)
+ {
+ BEFORE_GL_CALL;
+ EGLint ret = mSymbols.fClientWaitSync(dpy, sync, flags, timeout);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ EGLBoolean fGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint* value)
+ {
+ BEFORE_GL_CALL;
+ EGLBoolean b = mSymbols.fGetSyncAttrib(dpy, sync, attribute, value);
+ AFTER_GL_CALL;
+ return b;
+ }
+
+ EGLint fDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSync sync)
+ {
+ MOZ_ASSERT(mSymbols.fDupNativeFenceFDANDROID);
+ BEFORE_GL_CALL;
+ EGLint ret = mSymbols.fDupNativeFenceFDANDROID(dpy, sync);
+ AFTER_GL_CALL;
+ return ret;
+ }
+
+ void fANGLEPlatformInitialize(angle::Platform* platform)
+ {
+ MOZ_ASSERT(mSymbols.fANGLEPlatformInitialize);
+ BEFORE_GL_CALL;
+ mSymbols.fANGLEPlatformInitialize(platform);
+ AFTER_GL_CALL;
+ }
+
+ void fANGLEPlatformShutdown()
+ {
+ MOZ_ASSERT(mSymbols.fANGLEPlatformShutdown);
+ BEFORE_GL_CALL;
+ mSymbols.fANGLEPlatformShutdown();
+ AFTER_GL_CALL;
+ }
+
+ EGLDisplay Display() {
+ MOZ_ASSERT(mInitialized);
+ return mEGLDisplay;
+ }
+
+ bool IsANGLE() const {
+ MOZ_ASSERT(mInitialized);
+ return mIsANGLE;
+ }
+
+ bool IsWARP() const {
+ MOZ_ASSERT(mInitialized);
+ return mIsWARP;
+ }
+
+ bool HasKHRImageBase() {
+ return IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base);
+ }
+
+ bool HasKHRImagePixmap() {
+ return IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_pixmap);
+ }
+
+ bool HasKHRImageTexture2D() {
+ return IsExtensionSupported(KHR_gl_texture_2D_image);
+ }
+
+ bool HasANGLESurfaceD3DTexture2DShareHandle() {
+ return IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle);
+ }
+
+ bool HasRobustness() const {
+ return IsExtensionSupported(EXT_create_context_robustness);
+ }
+
+ bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
+
+ bool EnsureInitialized(bool forceAccel, nsACString* const out_failureId);
+
+ void DumpEGLConfig(EGLConfig cfg);
+ void DumpEGLConfigs();
+
+ struct {
+ typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void* display_id);
+ pfnGetDisplay fGetDisplay;
+ typedef EGLDisplay(GLAPIENTRY * pfnGetPlatformDisplayEXT)(EGLenum platform, void* native_display, const EGLint* attrib_list);
+ pfnGetPlatformDisplayEXT fGetPlatformDisplayEXT;
+ typedef EGLBoolean (GLAPIENTRY * pfnTerminate)(EGLDisplay dpy);
+ pfnTerminate fTerminate;
+ typedef EGLSurface (GLAPIENTRY * pfnGetCurrentSurface)(EGLint);
+ pfnGetCurrentSurface fGetCurrentSurface;
+ typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
+ pfnGetCurrentContext fGetCurrentContext;
+ typedef EGLBoolean (GLAPIENTRY * pfnMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+ pfnMakeCurrent fMakeCurrent;
+ typedef EGLBoolean (GLAPIENTRY * pfnDestroyContext)(EGLDisplay dpy, EGLContext ctx);
+ pfnDestroyContext fDestroyContext;
+ typedef EGLContext (GLAPIENTRY * pfnCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list);
+ pfnCreateContext fCreateContext;
+ typedef EGLBoolean (GLAPIENTRY * pfnDestroySurface)(EGLDisplay dpy, EGLSurface surface);
+ pfnDestroySurface fDestroySurface;
+ typedef EGLSurface (GLAPIENTRY * pfnCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list);
+ pfnCreateWindowSurface fCreateWindowSurface;
+ typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list);
+ pfnCreatePbufferSurface fCreatePbufferSurface;
+ typedef EGLSurface (GLAPIENTRY * pfnCreatePixmapSurface)(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list);
+ pfnCreatePixmapSurface fCreatePixmapSurface;
+ typedef EGLBoolean (GLAPIENTRY * pfnBindAPI)(EGLenum api);
+ pfnBindAPI fBindAPI;
+ typedef EGLBoolean (GLAPIENTRY * pfnInitialize)(EGLDisplay dpy, EGLint* major, EGLint* minor);
+ pfnInitialize fInitialize;
+ typedef EGLBoolean (GLAPIENTRY * pfnChooseConfig)(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config);
+ pfnChooseConfig fChooseConfig;
+ typedef EGLint (GLAPIENTRY * pfnGetError)(void);
+ pfnGetError fGetError;
+ typedef EGLBoolean (GLAPIENTRY * pfnGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value);
+ pfnGetConfigAttrib fGetConfigAttrib;
+ typedef EGLBoolean (GLAPIENTRY * pfnGetConfigs)(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config);
+ pfnGetConfigs fGetConfigs;
+ typedef EGLBoolean (GLAPIENTRY * pfnWaitNative)(EGLint engine);
+ pfnWaitNative fWaitNative;
+ typedef EGLCastToRelevantPtr (GLAPIENTRY * pfnGetProcAddress)(const char* procname);
+ pfnGetProcAddress fGetProcAddress;
+ typedef EGLBoolean (GLAPIENTRY * pfnSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
+ pfnSwapBuffers fSwapBuffers;
+ typedef EGLBoolean (GLAPIENTRY * pfnCopyBuffers)(EGLDisplay dpy, EGLSurface surface,
+ EGLNativePixmapType target);
+ pfnCopyBuffers fCopyBuffers;
+ typedef const GLubyte* (GLAPIENTRY * pfnQueryString)(EGLDisplay, EGLint name);
+ pfnQueryString fQueryString;
+ pfnQueryString fQueryStringImplementationANDROID;
+ typedef EGLBoolean (GLAPIENTRY * pfnQueryContext)(EGLDisplay dpy, EGLContext ctx,
+ EGLint attribute, EGLint* value);
+ pfnQueryContext fQueryContext;
+ typedef EGLBoolean (GLAPIENTRY * pfnBindTexImage)(EGLDisplay, EGLSurface surface, EGLint buffer);
+ pfnBindTexImage fBindTexImage;
+ typedef EGLBoolean (GLAPIENTRY * pfnReleaseTexImage)(EGLDisplay, EGLSurface surface, EGLint buffer);
+ pfnReleaseTexImage fReleaseTexImage;
+ typedef EGLImage (GLAPIENTRY * pfnCreateImage)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list);
+ pfnCreateImage fCreateImage;
+ typedef EGLBoolean (GLAPIENTRY * pfnDestroyImage)(EGLDisplay dpy, EGLImage image);
+ pfnDestroyImage fDestroyImage;
+
+ // New extension which allow us to lock texture and get raw image pointer
+ typedef EGLBoolean (GLAPIENTRY * pfnLockSurface)(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list);
+ pfnLockSurface fLockSurface;
+ typedef EGLBoolean (GLAPIENTRY * pfnUnlockSurface)(EGLDisplay dpy, EGLSurface surface);
+ pfnUnlockSurface fUnlockSurface;
+ typedef EGLBoolean (GLAPIENTRY * pfnQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value);
+ pfnQuerySurface fQuerySurface;
+
+ typedef EGLBoolean (GLAPIENTRY * pfnQuerySurfacePointerANGLE)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value);
+ pfnQuerySurfacePointerANGLE fQuerySurfacePointerANGLE;
+
+ typedef EGLSync (GLAPIENTRY * pfnCreateSync)(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list);
+ pfnCreateSync fCreateSync;
+ typedef EGLBoolean (GLAPIENTRY * pfnDestroySync)(EGLDisplay dpy, EGLSync sync);
+ pfnDestroySync fDestroySync;
+ typedef EGLint (GLAPIENTRY * pfnClientWaitSync)(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+ pfnClientWaitSync fClientWaitSync;
+ typedef EGLBoolean (GLAPIENTRY * pfnGetSyncAttrib)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint* value);
+ pfnGetSyncAttrib fGetSyncAttrib;
+ typedef EGLint (GLAPIENTRY * pfnDupNativeFenceFDANDROID)(EGLDisplay dpy, EGLSync sync);
+ pfnDupNativeFenceFDANDROID fDupNativeFenceFDANDROID;
+
+ typedef void (GLAPIENTRY * pfnANGLEPlatformInitialize)(angle::Platform* platform);
+ pfnANGLEPlatformInitialize fANGLEPlatformInitialize;
+ typedef void (GLAPIENTRY * pfnANGLEPlatformShutdown)();
+ pfnANGLEPlatformShutdown fANGLEPlatformShutdown;
+ } mSymbols;
+
+#ifdef DEBUG
+ static void BeforeGLCall(const char* glFunction);
+ static void AfterGLCall(const char* glFunction);
+#endif
+
+ EGLContext CachedCurrentContext() {
+ return nullptr;
+ }
+ void UnsetCachedCurrentContext() {}
+ void SetCachedCurrentContext(EGLContext aCtx) { }
+ bool CachedCurrentContextMatches() { return true; }
+
+private:
+ bool mInitialized;
+ PRLibrary* mEGLLibrary;
+ EGLDisplay mEGLDisplay;
+ RefPtr<GLContext> mReadbackGL;
+
+ bool mIsANGLE;
+ bool mIsWARP;
+ static StaticMutex sMutex;
+};
+
+extern GLLibraryEGL sEGLLibrary;
+#define EGL_DISPLAY() sEGLLibrary.Display()
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* GLLIBRARYEGL_H_ */
+
diff --git a/system/graphics/gl/GLLibraryLoader.cpp b/system/graphics/gl/GLLibraryLoader.cpp
new file mode 100644
index 000000000..801ffe37c
--- /dev/null
+++ b/system/graphics/gl/GLLibraryLoader.cpp
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLLibraryLoader.h"
+
+#include "nsDebug.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace gl {
+
+bool
+GLLibraryLoader::OpenLibrary(const char* library)
+{
+ PRLibSpec lspec;
+ lspec.type = PR_LibSpec_Pathname;
+ lspec.value.pathname = library;
+
+ mLibrary = PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL);
+ if (!mLibrary)
+ return false;
+
+ return true;
+}
+
+bool
+GLLibraryLoader::LoadSymbols(const SymLoadStruct* firstStruct,
+ bool tryplatform,
+ const char* prefix,
+ bool warnOnFailure)
+{
+ return LoadSymbols(mLibrary,
+ firstStruct,
+ tryplatform ? mLookupFunc : nullptr,
+ prefix,
+ warnOnFailure);
+}
+
+PRFuncPtr
+GLLibraryLoader::LookupSymbol(PRLibrary* lib,
+ const char* sym,
+ PlatformLookupFunction lookupFunction)
+{
+ PRFuncPtr res = 0;
+
+ // try finding it in the library directly, if we have one
+ if (lib) {
+ res = PR_FindFunctionSymbol(lib, sym);
+ }
+
+ // then try looking it up via the lookup symbol
+ if (!res && lookupFunction) {
+ res = lookupFunction(sym);
+ }
+
+ // finally just try finding it in the process
+ if (!res) {
+ PRLibrary* leakedLibRef;
+ res = PR_FindFunctionSymbolAndLibrary(sym, &leakedLibRef);
+ }
+
+ return res;
+}
+
+bool
+GLLibraryLoader::LoadSymbols(PRLibrary* lib,
+ const SymLoadStruct* firstStruct,
+ PlatformLookupFunction lookupFunction,
+ const char* prefix,
+ bool warnOnFailure)
+{
+ char sbuf[MAX_SYMBOL_LENGTH * 2];
+ int failCount = 0;
+
+ const SymLoadStruct* ss = firstStruct;
+ while (ss->symPointer) {
+ *ss->symPointer = 0;
+
+ for (int i = 0; i < MAX_SYMBOL_NAMES; i++) {
+ if (ss->symNames[i] == nullptr)
+ break;
+
+ const char* s = ss->symNames[i];
+ if (prefix && *prefix != 0) {
+ strcpy(sbuf, prefix);
+ strcat(sbuf, ss->symNames[i]);
+ s = sbuf;
+ }
+
+ PRFuncPtr p = LookupSymbol(lib, s, lookupFunction);
+ if (p) {
+ *ss->symPointer = p;
+ break;
+ }
+ }
+
+ if (*ss->symPointer == 0) {
+ if (warnOnFailure) {
+ printf_stderr("Can't find symbol '%s'.\n", ss->symNames[0]);
+ }
+
+ failCount++;
+ }
+
+ ss++;
+ }
+
+ return failCount == 0 ? true : false;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
+
diff --git a/system/graphics/gl/GLLibraryLoader.h b/system/graphics/gl/GLLibraryLoader.h
new file mode 100644
index 000000000..70ba43c4d
--- /dev/null
+++ b/system/graphics/gl/GLLibraryLoader.h
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLLIBRARYLOADER_H_
+#define GLLIBRARYLOADER_H_
+
+#include <stdio.h>
+
+#include "GLDefs.h"
+#include "nscore.h"
+#include "prlink.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLLibraryLoader
+{
+public:
+ bool OpenLibrary(const char* library);
+
+ typedef PRFuncPtr (GLAPIENTRY * PlatformLookupFunction) (const char*);
+
+ enum {
+ MAX_SYMBOL_NAMES = 6,
+ MAX_SYMBOL_LENGTH = 128
+ };
+
+ typedef struct {
+ PRFuncPtr* symPointer;
+ const char* symNames[MAX_SYMBOL_NAMES];
+ } SymLoadStruct;
+
+ bool LoadSymbols(const SymLoadStruct* firstStruct,
+ bool tryplatform = false,
+ const char* prefix = nullptr,
+ bool warnOnFailure = true);
+
+ /*
+ * Static version of the functions in this class
+ */
+ static PRFuncPtr LookupSymbol(PRLibrary* lib,
+ const char* symname,
+ PlatformLookupFunction lookupFunction = nullptr);
+ static bool LoadSymbols(PRLibrary* lib,
+ const SymLoadStruct* firstStruct,
+ PlatformLookupFunction lookupFunction = nullptr,
+ const char* prefix = nullptr,
+ bool warnOnFailure = true);
+protected:
+ GLLibraryLoader() {
+ mLibrary = nullptr;
+ mLookupFunc = nullptr;
+ }
+
+ PRLibrary* mLibrary;
+ PlatformLookupFunction mLookupFunc;
+};
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* GLLIBRARYLOADER_H_ */
diff --git a/system/graphics/gl/GLParseRegistryXML.py b/system/graphics/gl/GLParseRegistryXML.py
new file mode 100644
index 000000000..60daca6e5
--- /dev/null
+++ b/system/graphics/gl/GLParseRegistryXML.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+# coding=utf8
+
+
+################################################################################
+# TUTORIAL
+# This script will generate GLConsts.h
+#
+# Step 1:
+# Download the last gl.xml, egl.xml, glx.xml and wgl.xml from
+# http://www.opengl.org/registry/#specfiles into a directory of your choice
+#
+# Step 2:
+# Execute this script ./GLParseRegistryXML.py [your dir containing XML files]
+#
+# Step 3:
+# Do not add the downloaded XML in the patch
+#
+# Step 4:
+# Enjoy =)
+#
+################################################################################
+
+# includes
+import os
+import sys
+import xml.etree.ElementTree
+
+
+################################################################################
+# export management
+
+class GLConstHeader:
+ def __init__(self, f):
+ self.f = f
+
+
+ def write(self, arg):
+ if isinstance(arg, list):
+ self.f.write('\n'.join(arg) + '\n')
+ elif isinstance(arg, (int, long)):
+ self.f.write('\n' * arg)
+ else:
+ self.f.write(str(arg) + '\n')
+
+
+ def formatFileBegin(self):
+ self.write([
+ '/* This Source Code Form is subject to the terms of the Mozilla Public',
+ ' * License, v. 2.0. If a copy of the MPL was not distributed with this',
+ ' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */',
+ '',
+ '#ifndef GLCONSTS_H_',
+ '#define GLCONSTS_H_',
+ '',
+ '/**',
+ ' * GENERATED FILE, DO NOT MODIFY DIRECTLY.',
+ ' * This is a file generated directly from the official OpenGL registry',
+ ' * xml available http://www.opengl.org/registry/#specfiles.',
+ ' *',
+ ' * To generate this file, see tutorial in \'GLParseRegistryXML.py\'.',
+ ' */',
+ ''
+ ])
+
+
+ def formatLibBegin(self, lib):
+ # lib would be 'GL', 'EGL', 'GLX' or 'WGL'
+ self.write('// ' + lib)
+
+
+ def formatLibConstant(self, lib, name, value):
+ # lib would be 'GL', 'EGL', 'GLX' or 'WGL'
+ # name is the name of the const (example: MAX_TEXTURE_SIZE)
+ # value is the value of the const (example: 0xABCD)
+
+ define = '#define LOCAL_' + lib + '_' + name
+ whitespace = 60 - len(define)
+
+ if whitespace < 0:
+ whitespace = whitespace % 8
+
+ self.write(define + ' ' * whitespace + ' ' + value)
+
+
+ def formatLibEnd(self, lib):
+ # lib would be 'GL', 'EGL', 'GLX' or 'WGL'
+ self.write(2)
+
+
+ def formatFileEnd(self):
+ self.write([
+ '',
+ '#endif // GLCONSTS_H_'
+ ])
+
+
+################################################################################
+# underground code
+
+def getScriptDir():
+ return os.path.dirname(__file__) + '/'
+
+
+def getXMLDir():
+ if len(sys.argv) == 1:
+ return './'
+
+ dirPath = sys.argv[1]
+ if dirPath[-1] != '/':
+ dirPath += '/'
+
+ return dirPath
+
+
+class GLConst:
+ def __init__(self, lib, name, value, type):
+ self.lib = lib
+ self.name = name
+ self.value = value
+ self.type = type
+
+
+class GLDatabase:
+
+ LIBS = ['GL', 'EGL', 'GLX', 'WGL']
+
+ def __init__(self):
+ self.consts = {}
+ self.libs = set(GLDatabase.LIBS)
+ self.vendors = set(['EXT', 'ATI'])
+ # there is no vendor="EXT" and vendor="ATI" in gl.xml,
+ # so we manualy declare them
+
+
+ def loadXML(self, path):
+ xmlPath = getXMLDir() + path
+
+ if not os.path.isfile(xmlPath):
+ print 'missing file "' + xmlPath + '"'
+ return False
+
+ tree = xml.etree.ElementTree.parse(xmlPath)
+ root = tree.getroot()
+
+ for enums in root.iter('enums'):
+ vendor = enums.get('vendor')
+ if not vendor:
+ # there some standart enums that do have the vendor attribute,
+ # so we fake them as ARB's enums
+ vendor = 'ARB'
+
+ if vendor not in self.vendors:
+ # we map this new vendor in the vendors set.
+ self.vendors.add(vendor)
+
+ namespaceType = enums.get('type')
+
+ for enum in enums:
+ if enum.tag != 'enum':
+ # this is not an enum => we skip it
+ continue
+
+ lib = enum.get('name').split('_')[0]
+
+ if lib not in self.libs:
+ # unknown library => we skip it
+ continue
+
+ name = enum.get('name')[len(lib) + 1:]
+ value = enum.get('value')
+ type = enum.get('type')
+
+ if not type:
+ # if no type specified, we get the namespace's default type
+ type = namespaceType
+
+ self.consts[lib + '_' + name] = GLConst(lib, name, value, type)
+
+ return True
+
+
+ def exportConsts(self, path):
+ with open(getScriptDir() + path,'w') as f:
+
+ headerFile = GLConstHeader(f)
+ headerFile.formatFileBegin()
+
+ constNames = self.consts.keys()
+ constNames.sort()
+
+ for lib in GLDatabase.LIBS:
+ headerFile.formatLibBegin(lib)
+
+ for constName in constNames:
+ const = self.consts[constName]
+
+ if const.lib != lib:
+ continue
+
+ headerFile.formatLibConstant(lib, const.name, const.value)
+
+ headerFile.formatLibEnd(lib)
+
+ headerFile.formatFileEnd()
+
+
+glDatabase = GLDatabase()
+
+success = glDatabase.loadXML('gl.xml')
+success = success and glDatabase.loadXML('egl.xml')
+success = success and glDatabase.loadXML('glx.xml')
+success = success and glDatabase.loadXML('wgl.xml')
+
+if success:
+ glDatabase.exportConsts('GLConsts.h')
diff --git a/system/graphics/gl/GLReadTexImageHelper.cpp b/system/graphics/gl/GLReadTexImageHelper.cpp
new file mode 100644
index 000000000..8009de74b
--- /dev/null
+++ b/system/graphics/gl/GLReadTexImageHelper.cpp
@@ -0,0 +1,658 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLReadTexImageHelper.h"
+
+#include "gfx2DGlue.h"
+#include "gfxColor.h"
+#include "gfxTypes.h"
+#include "GLContext.h"
+#include "OGLShaderProgram.h"
+#include "ScopedGLHelpers.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::gfx;
+
+GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl)
+ : mGL(gl)
+{
+ mPrograms[0] = 0;
+ mPrograms[1] = 0;
+ mPrograms[2] = 0;
+ mPrograms[3] = 0;
+}
+
+GLReadTexImageHelper::~GLReadTexImageHelper()
+{
+ if (!mGL->MakeCurrent())
+ return;
+
+ mGL->fDeleteProgram(mPrograms[0]);
+ mGL->fDeleteProgram(mPrograms[1]);
+ mGL->fDeleteProgram(mPrograms[2]);
+ mGL->fDeleteProgram(mPrograms[3]);
+}
+
+static const GLchar
+readTextureImageVS[] =
+ "attribute vec2 aVertex;\n"
+ "attribute vec2 aTexCoord;\n"
+ "varying vec2 vTexCoord;\n"
+ "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
+
+static const GLchar
+readTextureImageFS_TEXTURE_2D[] =
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec2 vTexCoord;\n"
+ "uniform sampler2D uTexture;\n"
+ "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
+
+
+static const GLchar
+readTextureImageFS_TEXTURE_2D_BGRA[] =
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec2 vTexCoord;\n"
+ "uniform sampler2D uTexture;\n"
+ "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
+
+static const GLchar
+readTextureImageFS_TEXTURE_EXTERNAL[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec2 vTexCoord;\n"
+ "uniform samplerExternalOES uTexture;\n"
+ "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
+
+static const GLchar
+readTextureImageFS_TEXTURE_RECTANGLE[] =
+ "#extension GL_ARB_texture_rectangle\n"
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec2 vTexCoord;\n"
+ "uniform sampler2DRect uTexture;\n"
+ "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
+
+GLuint
+GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget,
+ int aConfig)
+{
+ int variant = 0;
+ const GLchar* readTextureImageFS = nullptr;
+ if (aTextureTarget == LOCAL_GL_TEXTURE_2D) {
+ if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) {
+ // Need to swizzle R/B.
+ readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA;
+ variant = 1;
+ } else {
+ readTextureImageFS = readTextureImageFS_TEXTURE_2D;
+ variant = 0;
+ }
+ } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
+ readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL;
+ variant = 2;
+ } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
+ readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE;
+ variant = 3;
+ }
+
+ /* This might be overkill, but assure that we don't access out-of-bounds */
+ MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms));
+ if (!mPrograms[variant]) {
+ GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
+ const GLchar* vsSourcePtr = &readTextureImageVS[0];
+ mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr);
+ mGL->fCompileShader(vs);
+
+ GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
+ mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr);
+ mGL->fCompileShader(fs);
+
+ GLuint program = mGL->fCreateProgram();
+ mGL->fAttachShader(program, vs);
+ mGL->fAttachShader(program, fs);
+ mGL->fBindAttribLocation(program, 0, "aVertex");
+ mGL->fBindAttribLocation(program, 1, "aTexCoord");
+ mGL->fLinkProgram(program);
+
+ GLint success;
+ mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success);
+
+ if (!success) {
+ mGL->fDeleteProgram(program);
+ program = 0;
+ }
+
+ mGL->fDeleteShader(vs);
+ mGL->fDeleteShader(fs);
+
+ mPrograms[variant] = program;
+ }
+
+ return mPrograms[variant];
+}
+
+bool
+GLReadTexImageHelper::DidGLErrorOccur(const char* str)
+{
+ GLenum error = mGL->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ printf_stderr("GL ERROR: %s (0x%04x) %s\n",
+ mGL->GLErrorToString(error), error, str);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+GetActualReadFormats(GLContext* gl,
+ GLenum destFormat, GLenum destType,
+ GLenum* out_readFormat, GLenum* out_readType)
+{
+ MOZ_ASSERT(out_readFormat);
+ MOZ_ASSERT(out_readType);
+
+ if (destFormat == LOCAL_GL_RGBA &&
+ destType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ *out_readFormat = destFormat;
+ *out_readType = destType;
+ return true;
+ }
+
+ bool fallback = true;
+ if (gl->IsGLES()) {
+ GLenum auxFormat = 0;
+ GLenum auxType = 0;
+
+ gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat);
+ gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType);
+
+ if (destFormat == auxFormat &&
+ destType == auxType)
+ {
+ fallback = false;
+ }
+ } else {
+ switch (destFormat) {
+ case LOCAL_GL_RGB: {
+ if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV)
+ fallback = false;
+ break;
+ }
+ case LOCAL_GL_BGRA: {
+ if (destType == LOCAL_GL_UNSIGNED_BYTE ||
+ destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV)
+ {
+ fallback = false;
+ }
+ break;
+ }
+ }
+ }
+
+ if (fallback) {
+ *out_readFormat = LOCAL_GL_RGBA;
+ *out_readType = LOCAL_GL_UNSIGNED_BYTE;
+ return false;
+ } else {
+ *out_readFormat = destFormat;
+ *out_readType = destType;
+ return true;
+ }
+}
+
+void
+SwapRAndBComponents(DataSourceSurface* surf)
+{
+ DataSourceSurface::MappedSurface map;
+ if (!surf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+ MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface.");
+ return;
+ }
+ MOZ_ASSERT(map.mStride >= 0);
+
+ const size_t rowBytes = surf->GetSize().width*4;
+ const size_t rowHole = map.mStride - rowBytes;
+
+ uint8_t* row = map.mData;
+ if (!row) {
+ MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from"
+ " DataSourceSurface.");
+ surf->Unmap();
+ return;
+ }
+
+ const size_t rows = surf->GetSize().height;
+ for (size_t i = 0; i < rows; i++) {
+ const uint8_t* rowEnd = row + rowBytes;
+
+ while (row != rowEnd) {
+ Swap(row[0], row[2]);
+ row += 4;
+ }
+
+ row += rowHole;
+ }
+
+ surf->Unmap();
+}
+
+static int
+CalcRowStride(int width, int pixelSize, int alignment)
+{
+ MOZ_ASSERT(alignment);
+
+ int rowStride = width * pixelSize;
+ if (rowStride % alignment) { // Extra at the end of the line?
+ int alignmentCount = rowStride / alignment;
+ rowStride = (alignmentCount+1) * alignment;
+ }
+ return rowStride;
+}
+
+static int
+GuessAlignment(int width, int pixelSize, int rowStride)
+{
+ int alignment = 8; // Max GLES allows.
+ while (CalcRowStride(width, pixelSize, alignment) != rowStride) {
+ alignment /= 2;
+ if (!alignment) {
+ NS_WARNING("Bad alignment for GLES. Will use temp surf for readback.");
+ return 0;
+ }
+ }
+ return alignment;
+}
+
+void
+ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
+{
+ gl->MakeCurrent();
+ MOZ_ASSERT(dest->GetSize().width != 0);
+ MOZ_ASSERT(dest->GetSize().height != 0);
+
+ bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ dest->GetFormat() == SurfaceFormat::R8G8B8A8;
+
+ int destPixelSize;
+ GLenum destFormat;
+ GLenum destType;
+
+ switch (dest->GetFormat()) {
+ case SurfaceFormat::B8G8R8A8:
+ case SurfaceFormat::B8G8R8X8:
+ // Needs host (little) endian ARGB.
+ destFormat = LOCAL_GL_BGRA;
+ destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ case SurfaceFormat::R8G8B8X8:
+ // Needs host (little) endian ABGR.
+ destFormat = LOCAL_GL_RGBA;
+ destType = LOCAL_GL_UNSIGNED_BYTE;
+ break;
+ case SurfaceFormat::R5G6B5_UINT16:
+ destFormat = LOCAL_GL_RGB;
+ destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
+ break;
+ default:
+ MOZ_CRASH("GFX: Bad format, read pixels.");
+ }
+ destPixelSize = BytesPerPixel(dest->GetFormat());
+ MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
+
+ GLenum readFormat = destFormat;
+ GLenum readType = destType;
+ bool needsTempSurf = !GetActualReadFormats(gl,
+ destFormat, destType,
+ &readFormat, &readType);
+
+ RefPtr<DataSourceSurface> tempSurf;
+ DataSourceSurface* readSurf = dest;
+ int readAlignment = GuessAlignment(dest->GetSize().width,
+ destPixelSize,
+ dest->Stride());
+ if (!readAlignment) {
+ needsTempSurf = true;
+ }
+ if (needsTempSurf) {
+ if (GLContext::ShouldSpew()) {
+ NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
+ }
+ SurfaceFormat readFormatGFX;
+
+ switch (readFormat) {
+ case LOCAL_GL_RGBA: {
+ readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8
+ : SurfaceFormat::R8G8B8X8;
+ break;
+ }
+ case LOCAL_GL_BGRA: {
+ readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
+ : SurfaceFormat::B8G8R8X8;
+ break;
+ }
+ case LOCAL_GL_RGB: {
+ MOZ_ASSERT(destPixelSize == 2);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
+ readFormatGFX = SurfaceFormat::R5G6B5_UINT16;
+ break;
+ }
+ default: {
+ MOZ_CRASH("GFX: Bad read format, read format.");
+ }
+ }
+
+ switch (readType) {
+ case LOCAL_GL_UNSIGNED_BYTE: {
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
+ readAlignment = 1;
+ break;
+ }
+ case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
+ MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
+ readAlignment = 4;
+ break;
+ }
+ case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
+ readAlignment = 2;
+ break;
+ }
+ default: {
+ MOZ_CRASH("GFX: Bad read type, read type.");
+ }
+ }
+
+ int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
+ tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
+ readFormatGFX,
+ stride);
+ if (NS_WARN_IF(!tempSurf)) {
+ return;
+ }
+
+ readSurf = tempSurf;
+ }
+ MOZ_ASSERT(readAlignment);
+ MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
+
+ GLsizei width = dest->GetSize().width;
+ GLsizei height = dest->GetSize().height;
+
+ {
+ ScopedPackState safePackState(gl);
+ gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
+
+ gl->fReadPixels(0, 0,
+ width, height,
+ readFormat, readType,
+ readSurf->GetData());
+ }
+
+ if (readSurf != dest) {
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+ gfx::Factory::CopyDataSourceSurface(readSurf, dest);
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+YInvertImageSurface(gfx::DataSourceSurface* aSurf)
+{
+ RefPtr<DataSourceSurface> temp =
+ Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
+ aSurf->GetFormat(),
+ aSurf->Stride());
+ if (NS_WARN_IF(!temp)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!temp->Map(DataSourceSurface::MapType::WRITE, &map)) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ map.mData,
+ temp->GetSize(),
+ map.mStride,
+ temp->GetFormat());
+ if (!dt) {
+ temp->Unmap();
+ return nullptr;
+ }
+
+ dt->SetTransform(Matrix::Scaling(1.0, -1.0) *
+ Matrix::Translation(0.0, aSurf->GetSize().height));
+ Rect rect(0, 0, aSurf->GetSize().width, aSurf->GetSize().height);
+ dt->DrawSurface(aSurf, rect, rect, DrawSurfaceOptions(),
+ DrawOptions(1.0, CompositionOp::OP_SOURCE, AntialiasMode::NONE));
+ temp->Unmap();
+ return temp.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat)
+{
+ gl->MakeCurrent();
+ gl->GuaranteeResolve();
+ gl->fActiveTexture(LOCAL_GL_TEXTURE0);
+ gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
+
+ IntSize size;
+ gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width);
+ gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height);
+
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8,
+ GetAlignedStride<4>(size.width, BytesPerPixel(SurfaceFormat::B8G8R8A8)));
+
+ if (NS_WARN_IF(!surf)) {
+ return nullptr;
+ }
+
+ uint32_t currentPackAlignment = 0;
+ gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)&currentPackAlignment);
+ if (currentPackAlignment != 4) {
+ gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
+ }
+
+ gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData());
+
+ if (currentPackAlignment != 4) {
+ gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
+ }
+
+ if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) {
+ SwapRAndBComponents(surf);
+ }
+
+ if (aYInvert) {
+ surf = YInvertImageSurface(surf);
+ }
+
+ return surf.forget();
+}
+
+#define CLEANUP_IF_GLERROR_OCCURRED(x) \
+ if (DidGLErrorOccur(x)) { \
+ return false; \
+ }
+
+already_AddRefed<DataSourceSurface>
+GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
+ GLenum aTextureTarget,
+ const gfx::IntSize& aSize,
+ /* ShaderConfigOGL.mFeature */ int aConfig,
+ bool aYInvert)
+{
+ /* Allocate resulting image surface */
+ int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
+ RefPtr<DataSourceSurface> isurf =
+ Factory::CreateDataSourceSurfaceWithStride(aSize,
+ SurfaceFormat::R8G8B8A8,
+ stride);
+ if (NS_WARN_IF(!isurf)) {
+ return nullptr;
+ }
+
+ if (!ReadTexImage(isurf, aTextureId, aTextureTarget, aSize, aConfig, aYInvert)) {
+ return nullptr;
+ }
+
+ return isurf.forget();
+}
+
+bool
+GLReadTexImageHelper::ReadTexImage(DataSourceSurface* aDest,
+ GLuint aTextureId,
+ GLenum aTextureTarget,
+ const gfx::IntSize& aSize,
+ /* ShaderConfigOGL.mFeature */ int aConfig,
+ bool aYInvert)
+{
+ MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
+ aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
+ aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+
+ mGL->MakeCurrent();
+
+ GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
+ GLuint rb, fb;
+
+ do {
+ mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
+ mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
+ mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
+ mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit);
+ mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
+ switch (aTextureTarget) {
+ case LOCAL_GL_TEXTURE_2D:
+ mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex);
+ break;
+ case LOCAL_GL_TEXTURE_EXTERNAL:
+ mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex);
+ break;
+ case LOCAL_GL_TEXTURE_RECTANGLE:
+ mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex);
+ break;
+ default: /* Already checked above */
+ break;
+ }
+
+ ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false);
+ ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false);
+ ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height);
+
+ /* Setup renderbuffer */
+ mGL->fGenRenderbuffers(1, &rb);
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
+
+ GLenum rbInternalFormat =
+ mGL->IsGLES()
+ ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4)
+ : LOCAL_GL_RGBA;
+ mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height);
+ CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
+
+ /* Setup framebuffer */
+ mGL->fGenFramebuffers(1, &fb);
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
+ mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER, rb);
+ CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
+
+ MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE);
+
+ /* Setup vertex and fragment shader */
+ GLuint program = TextureImageProgramFor(aTextureTarget, aConfig);
+ MOZ_ASSERT(program);
+
+ mGL->fUseProgram(program);
+ CLEANUP_IF_GLERROR_OCCURRED("when using program");
+ mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0);
+ CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
+
+ /* Setup quad geometry */
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f;
+ float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f;
+
+ const float
+ vertexArray[4*2] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+ ScopedVertexAttribPointer autoAttrib0(mGL, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, vertexArray);
+
+ const float u0 = 0.0f;
+ const float u1 = w;
+ const float v0 = aYInvert ? h : 0.0f;
+ const float v1 = aYInvert ? 0.0f : h;
+ const float texCoordArray[8] = { u0, v0,
+ u1, v0,
+ u0, v1,
+ u1, v1 };
+ ScopedVertexAttribPointer autoAttrib1(mGL, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, texCoordArray);
+
+ /* Bind the texture */
+ if (aTextureId) {
+ mGL->fBindTexture(aTextureTarget, aTextureId);
+ CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
+ }
+
+ /* Draw quad */
+ mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f);
+ mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
+ CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer");
+
+ mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+ CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
+
+ /* Read-back draw results */
+ ReadPixelsIntoDataSurface(mGL, aDest);
+ CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
+ } while (false);
+
+ /* Restore GL state */
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
+ mGL->fUseProgram(oldprog);
+
+ // note that deleting 0 has no effect in any of these calls
+ mGL->fDeleteRenderbuffers(1, &rb);
+ mGL->fDeleteFramebuffers(1, &fb);
+
+ if (aTextureId)
+ mGL->fBindTexture(aTextureTarget, oldTex);
+
+ if (oldTexUnit != LOCAL_GL_TEXTURE0)
+ mGL->fActiveTexture(oldTexUnit);
+
+ return true;
+}
+
+#undef CLEANUP_IF_GLERROR_OCCURRED
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/GLReadTexImageHelper.h b/system/graphics/gl/GLReadTexImageHelper.h
new file mode 100644
index 000000000..2de33c2eb
--- /dev/null
+++ b/system/graphics/gl/GLReadTexImageHelper.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLREADTEXIMAGEHELPER_H_
+#define GLREADTEXIMAGEHELPER_H_
+
+#include "GLContextTypes.h"
+#include "mozilla/Attributes.h"
+#include "nsSize.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace gl {
+
+// Returns true if the `dest{Format,Type}` are the same as the
+// `read{Format,Type}`.
+bool GetActualReadFormats(GLContext* gl,
+ GLenum destFormat, GLenum destType,
+ GLenum* out_readFormat, GLenum* out_readType);
+
+void ReadPixelsIntoDataSurface(GLContext* aGL,
+ gfx::DataSourceSurface* aSurface);
+
+already_AddRefed<gfx::DataSourceSurface>
+ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat);
+
+already_AddRefed<gfx::DataSourceSurface>
+YInvertImageSurface(gfx::DataSourceSurface* aSurf);
+
+void
+SwapRAndBComponents(gfx::DataSourceSurface* surf);
+
+class GLReadTexImageHelper final
+{
+ // The GLContext is the sole owner of the GLBlitHelper.
+ GLContext* mGL;
+
+ GLuint mPrograms[4];
+
+ GLuint TextureImageProgramFor(GLenum aTextureTarget, int aShader);
+
+ bool DidGLErrorOccur(const char* str);
+
+public:
+
+ explicit GLReadTexImageHelper(GLContext* gl);
+ ~GLReadTexImageHelper();
+
+ /**
+ * Read the image data contained in aTexture, and return it as an ImageSurface.
+ * If GL_RGBA is given as the format, a SurfaceFormat::A8R8G8B8_UINT32 surface is returned.
+ * Not implemented yet:
+ * If GL_RGB is given as the format, a SurfaceFormat::X8R8G8B8_UINT32 surface is returned.
+ * If GL_LUMINANCE is given as the format, a SurfaceFormat::A8 surface is returned.
+ *
+ * THIS IS EXPENSIVE. It is ridiculously expensive. Only do this
+ * if you absolutely positively must, and never in any performance
+ * critical path.
+ *
+ * NOTE: aShaderProgram is really mozilla::layers::ShaderProgramType. It is
+ * passed as int to eliminate including LayerManagerOGLProgram.h here.
+ */
+ already_AddRefed<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId,
+ GLenum aTextureTarget,
+ const gfx::IntSize& aSize,
+ /* ShaderProgramType */ int aShaderProgram,
+ bool aYInvert = false);
+
+ bool ReadTexImage(gfx::DataSourceSurface* aDest,
+ GLuint aTextureId,
+ GLenum aTextureTarget,
+ const gfx::IntSize& aSize,
+ int aShaderProgram,
+ bool aYInvert = false);
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/gl/GLScreenBuffer.cpp b/system/graphics/gl/GLScreenBuffer.cpp
new file mode 100644
index 000000000..0ac0322be
--- /dev/null
+++ b/system/graphics/gl/GLScreenBuffer.cpp
@@ -0,0 +1,1040 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLScreenBuffer.h"
+
+#include <cstring>
+#include "CompositorTypes.h"
+#include "GLContext.h"
+#include "GLBlitHelper.h"
+#include "GLReadTexImageHelper.h"
+#include "SharedSurfaceEGL.h"
+#include "SharedSurfaceGL.h"
+#include "ScopedGLHelpers.h"
+#include "gfx2DGlue.h"
+#include "../layers/ipc/ShadowLayers.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+
+#ifdef XP_WIN
+#include "SharedSurfaceANGLE.h" // for SurfaceFactory_ANGLEShareHandle
+#include "SharedSurfaceD3D11Interop.h" // for SurfaceFactory_D3D11Interop
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+#ifdef GL_PROVIDER_GLX
+#include "GLXLibrary.h"
+#include "SharedSurfaceGLX.h"
+#endif
+
+
+namespace mozilla {
+namespace gl {
+
+using gfx::SurfaceFormat;
+using namespace mozilla::layers;
+
+UniquePtr<GLScreenBuffer>
+GLScreenBuffer::Create(GLContext* gl,
+ const gfx::IntSize& size,
+ const SurfaceCaps& caps)
+{
+ UniquePtr<GLScreenBuffer> ret;
+ if (caps.antialias &&
+ !gl->IsSupported(GLFeature::framebuffer_multisample))
+ {
+ return Move(ret);
+ }
+
+ layers::TextureFlags flags = layers::TextureFlags::ORIGIN_BOTTOM_LEFT;
+ if (!caps.premultAlpha) {
+ flags |= layers::TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ UniquePtr<SurfaceFactory> factory = MakeUnique<SurfaceFactory_Basic>(gl, caps, flags);
+
+ ret.reset( new GLScreenBuffer(gl, caps, Move(factory)) );
+ return Move(ret);
+}
+
+/* static */ UniquePtr<SurfaceFactory>
+GLScreenBuffer::CreateFactory(GLContext* gl,
+ const SurfaceCaps& caps,
+ KnowsCompositor* compositorConnection,
+ const layers::TextureFlags& flags)
+{
+ return CreateFactory(gl, caps, compositorConnection->GetTextureForwarder(),
+ compositorConnection->GetCompositorBackendType(), flags);
+}
+
+/* static */ UniquePtr<SurfaceFactory>
+GLScreenBuffer::CreateFactory(GLContext* gl,
+ const SurfaceCaps& caps,
+ LayersIPCChannel* ipcChannel,
+ const mozilla::layers::LayersBackend backend,
+ const layers::TextureFlags& flags)
+{
+ UniquePtr<SurfaceFactory> factory = nullptr;
+ if (!gfxPrefs::WebGLForceLayersReadback()) {
+ switch (backend) {
+ case mozilla::layers::LayersBackend::LAYERS_OPENGL: {
+#if defined(GL_PROVIDER_GLX)
+ if (sGLXLibrary.UseTextureFromPixmap())
+ factory = SurfaceFactory_GLXDrawable::Create(gl, caps, ipcChannel, flags);
+#elif defined(MOZ_WIDGET_UIKIT)
+ factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, ipcChannel, mFlags);
+#else
+ if (gl->GetContextType() == GLContextType::EGL) {
+ if (XRE_IsParentProcess()) {
+ factory = SurfaceFactory_EGLImage::Create(gl, caps, ipcChannel, flags);
+ }
+ }
+#endif
+ break;
+ }
+ case mozilla::layers::LayersBackend::LAYERS_D3D11: {
+#ifdef XP_WIN
+ // Enable surface sharing only if ANGLE and compositing devices
+ // are both WARP or both not WARP
+ gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get();
+ if (gl->IsANGLE() &&
+ (gl->IsWARP() == dm->IsWARP()) &&
+ dm->TextureSharingWorks())
+ {
+ factory = SurfaceFactory_ANGLEShareHandle::Create(gl, caps, ipcChannel, flags);
+ }
+
+ if (!factory && gfxPrefs::WebGLDXGLEnabled()) {
+ factory = SurfaceFactory_D3D11Interop::Create(gl, caps, ipcChannel, flags);
+ }
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+
+#ifdef GL_PROVIDER_GLX
+ if (!factory && sGLXLibrary.UseTextureFromPixmap()) {
+ factory = SurfaceFactory_GLXDrawable::Create(gl, caps, ipcChannel, flags);
+ }
+#endif
+ }
+
+ return factory;
+}
+
+GLScreenBuffer::GLScreenBuffer(GLContext* gl,
+ const SurfaceCaps& caps,
+ UniquePtr<SurfaceFactory> factory)
+ : mGL(gl)
+ , mCaps(caps)
+ , mFactory(Move(factory))
+ , mNeedsBlit(true)
+ , mUserReadBufferMode(LOCAL_GL_BACK)
+ , mUserDrawBufferMode(LOCAL_GL_BACK)
+ , mUserDrawFB(0)
+ , mUserReadFB(0)
+ , mInternalDrawFB(0)
+ , mInternalReadFB(0)
+#ifdef DEBUG
+ , mInInternalMode_DrawFB(true)
+ , mInInternalMode_ReadFB(true)
+#endif
+{ }
+
+GLScreenBuffer::~GLScreenBuffer()
+{
+ mFactory = nullptr;
+ mDraw = nullptr;
+ mRead = nullptr;
+
+ if (!mBack)
+ return;
+
+ // Detach mBack cleanly.
+ mBack->Surf()->ProducerRelease();
+}
+
+void
+GLScreenBuffer::BindAsFramebuffer(GLContext* const gl, GLenum target) const
+{
+ GLuint drawFB = DrawFB();
+ GLuint readFB = ReadFB();
+
+ if (!gl->IsSupported(GLFeature::split_framebuffer)) {
+ MOZ_ASSERT(drawFB == readFB);
+ gl->raw_fBindFramebuffer(target, readFB);
+ return;
+ }
+
+ switch (target) {
+ case LOCAL_GL_FRAMEBUFFER:
+ gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
+ gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
+ break;
+
+ case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
+ gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
+ break;
+
+ case LOCAL_GL_READ_FRAMEBUFFER_EXT:
+ gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
+ break;
+
+ default:
+ MOZ_CRASH("GFX: Bad `target` for BindFramebuffer.");
+ }
+}
+
+void
+GLScreenBuffer::BindFB(GLuint fb)
+{
+ GLuint drawFB = DrawFB();
+ GLuint readFB = ReadFB();
+
+ mUserDrawFB = fb;
+ mUserReadFB = fb;
+ mInternalDrawFB = (fb == 0) ? drawFB : fb;
+ mInternalReadFB = (fb == 0) ? readFB : fb;
+
+ if (mInternalDrawFB == mInternalReadFB) {
+ mGL->raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mInternalDrawFB);
+ } else {
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+ mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+ mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+ }
+
+#ifdef DEBUG
+ mInInternalMode_DrawFB = false;
+ mInInternalMode_ReadFB = false;
+#endif
+}
+
+void
+GLScreenBuffer::BindDrawFB(GLuint fb)
+{
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+ GLuint drawFB = DrawFB();
+ mUserDrawFB = fb;
+ mInternalDrawFB = (fb == 0) ? drawFB : fb;
+
+ mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+
+#ifdef DEBUG
+ mInInternalMode_DrawFB = false;
+#endif
+}
+
+void
+GLScreenBuffer::BindReadFB(GLuint fb)
+{
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+ GLuint readFB = ReadFB();
+ mUserReadFB = fb;
+ mInternalReadFB = (fb == 0) ? readFB : fb;
+
+ mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+
+#ifdef DEBUG
+ mInInternalMode_ReadFB = false;
+#endif
+}
+
+void
+GLScreenBuffer::BindFB_Internal(GLuint fb)
+{
+ mInternalDrawFB = mUserDrawFB = fb;
+ mInternalReadFB = mUserReadFB = fb;
+ mGL->raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mInternalDrawFB);
+
+#ifdef DEBUG
+ mInInternalMode_DrawFB = true;
+ mInInternalMode_ReadFB = true;
+#endif
+}
+
+void
+GLScreenBuffer::BindDrawFB_Internal(GLuint fb)
+{
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+ mInternalDrawFB = mUserDrawFB = fb;
+ mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+
+#ifdef DEBUG
+ mInInternalMode_DrawFB = true;
+#endif
+}
+
+void
+GLScreenBuffer::BindReadFB_Internal(GLuint fb)
+{
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+ mInternalReadFB = mUserReadFB = fb;
+ mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+
+#ifdef DEBUG
+ mInInternalMode_ReadFB = true;
+#endif
+}
+
+
+GLuint
+GLScreenBuffer::GetDrawFB() const
+{
+#ifdef DEBUG
+ MOZ_ASSERT(mGL->IsCurrent());
+ MOZ_ASSERT(!mInInternalMode_DrawFB);
+
+ // Don't need a branch here, because:
+ // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT == LOCAL_GL_FRAMEBUFFER_BINDING == 0x8CA6
+ // We use raw_ here because this is debug code and we need to see what
+ // the driver thinks.
+ GLuint actual = 0;
+ mGL->raw_fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
+
+ GLuint predicted = mInternalDrawFB;
+ if (predicted != actual) {
+ printf_stderr("Misprediction: Bound draw FB predicted: %d. Was: %d.\n",
+ predicted, actual);
+ MOZ_ASSERT(false, "Draw FB binding misprediction!");
+ }
+#endif
+
+ return mUserDrawFB;
+}
+
+GLuint
+GLScreenBuffer::GetReadFB() const
+{
+#ifdef DEBUG
+ MOZ_ASSERT(mGL->IsCurrent());
+ MOZ_ASSERT(!mInInternalMode_ReadFB);
+
+ // We use raw_ here because this is debug code and we need to see what
+ // the driver thinks.
+ GLuint actual = 0;
+ if (mGL->IsSupported(GLFeature::split_framebuffer))
+ mGL->raw_fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
+ else
+ mGL->raw_fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&actual);
+
+ GLuint predicted = mInternalReadFB;
+ if (predicted != actual) {
+ printf_stderr("Misprediction: Bound read FB predicted: %d. Was: %d.\n",
+ predicted, actual);
+ MOZ_ASSERT(false, "Read FB binding misprediction!");
+ }
+#endif
+
+ return mUserReadFB;
+}
+
+GLuint
+GLScreenBuffer::GetFB() const
+{
+ MOZ_ASSERT(GetDrawFB() == GetReadFB());
+ return GetDrawFB();
+}
+
+
+void
+GLScreenBuffer::DeletingFB(GLuint fb)
+{
+ if (fb == mInternalDrawFB) {
+ mInternalDrawFB = 0;
+ mUserDrawFB = 0;
+ }
+ if (fb == mInternalReadFB) {
+ mInternalReadFB = 0;
+ mUserReadFB = 0;
+ }
+}
+
+
+void
+GLScreenBuffer::AfterDrawCall()
+{
+ if (mUserDrawFB != 0)
+ return;
+
+ RequireBlit();
+}
+
+void
+GLScreenBuffer::BeforeReadCall()
+{
+ if (mUserReadFB != 0)
+ return;
+
+ AssureBlitted();
+}
+
+bool
+GLScreenBuffer::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border)
+{
+ SharedSurface* surf;
+ if (GetReadFB() == 0) {
+ surf = SharedSurf();
+ } else {
+ surf = mGL->mFBOMapping[GetReadFB()];
+ }
+ if (surf) {
+ return surf->CopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+ }
+
+ return false;
+}
+
+bool
+GLScreenBuffer::ReadPixels(GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ GLvoid* pixels)
+{
+ // If the currently bound framebuffer is backed by a SharedSurface
+ // then it might want to override how we read pixel data from it.
+ // This is normally only the default framebuffer, but we can also
+ // have SharedSurfaces bound to other framebuffers when doing
+ // readback for BasicLayers.
+ SharedSurface* surf;
+ if (GetReadFB() == 0) {
+ surf = SharedSurf();
+ } else {
+ surf = mGL->mFBOMapping[GetReadFB()];
+ }
+ if (surf) {
+ return surf->ReadPixels(x, y, width, height, format, type, pixels);
+ }
+
+ return false;
+}
+
+void
+GLScreenBuffer::RequireBlit()
+{
+ mNeedsBlit = true;
+}
+
+void
+GLScreenBuffer::AssureBlitted()
+{
+ if (!mNeedsBlit)
+ return;
+
+ if (mDraw) {
+ GLuint drawFB = DrawFB();
+ GLuint readFB = ReadFB();
+
+ MOZ_ASSERT(drawFB != 0);
+ MOZ_ASSERT(drawFB != readFB);
+ MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+ MOZ_ASSERT(mDraw->mSize == mRead->Size());
+
+ ScopedBindFramebuffer boundFB(mGL);
+ ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
+
+ BindReadFB_Internal(drawFB);
+ BindDrawFB_Internal(readFB);
+
+ if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+ const gfx::IntSize& srcSize = mDraw->mSize;
+ const gfx::IntSize& destSize = mRead->Size();
+
+ mGL->raw_fBlitFramebuffer(0, 0, srcSize.width, srcSize.height,
+ 0, 0, destSize.width, destSize.height,
+ LOCAL_GL_COLOR_BUFFER_BIT,
+ LOCAL_GL_NEAREST);
+ } else if (mGL->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample)) {
+ mGL->fResolveMultisampleFramebufferAPPLE();
+ } else {
+ MOZ_CRASH("GFX: No available blit methods.");
+ }
+ // Done!
+ }
+
+ mNeedsBlit = false;
+}
+
+void
+GLScreenBuffer::Morph(UniquePtr<SurfaceFactory> newFactory)
+{
+ MOZ_RELEASE_ASSERT(newFactory, "newFactory must not be null");
+ mFactory = Move(newFactory);
+}
+
+bool
+GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& size)
+{
+ ScopedBindFramebuffer autoFB(mGL);
+
+ const bool readNeedsUnlock = (mRead && SharedSurf());
+ if (readNeedsUnlock) {
+ SharedSurf()->UnlockProd();
+ }
+
+ surf->LockProd();
+
+ if (mRead &&
+ surf->mAttachType == SharedSurf()->mAttachType &&
+ size == Size())
+ {
+ // Same size, same type, ready for reuse!
+ mRead->Attach(surf);
+ } else {
+ // Else something changed, so resize:
+ UniquePtr<DrawBuffer> draw;
+ bool drawOk = true;
+
+ /* Don't change out the draw buffer unless we resize. In the
+ * preserveDrawingBuffer:true case, prior contents of the buffer must
+ * be retained. If we're using a draw buffer, it's an MSAA buffer, so
+ * even if we copy the previous frame into the (single-sampled) read
+ * buffer, if we need to re-resolve from draw to read (as triggered by
+ * drawing), we'll need the old MSAA content to still be in the draw
+ * buffer.
+ */
+ if (!mDraw || size != Size())
+ drawOk = CreateDraw(size, &draw); // Can be null.
+
+ UniquePtr<ReadBuffer> read = CreateRead(surf);
+ bool readOk = !!read;
+
+ if (!drawOk || !readOk) {
+ surf->UnlockProd();
+ if (readNeedsUnlock) {
+ SharedSurf()->LockProd();
+ }
+ return false;
+ }
+
+ if (draw)
+ mDraw = Move(draw);
+
+ mRead = Move(read);
+ }
+
+ // Check that we're all set up.
+ MOZ_ASSERT(SharedSurf() == surf);
+
+ // Update the ReadBuffer mode.
+ if (mGL->IsSupported(gl::GLFeature::read_buffer)) {
+ BindFB(0);
+ mRead->SetReadBuffer(mUserReadBufferMode);
+ }
+
+ // Update the DrawBuffer mode.
+ if (mGL->IsSupported(gl::GLFeature::draw_buffers)) {
+ BindFB(0);
+ SetDrawBuffer(mUserDrawBufferMode);
+ }
+
+ RequireBlit();
+
+ return true;
+}
+
+bool
+GLScreenBuffer::Swap(const gfx::IntSize& size)
+{
+ RefPtr<SharedSurfaceTextureClient> newBack = mFactory->NewTexClient(size);
+ if (!newBack)
+ return false;
+
+ // In the case of DXGL interop, the new surface needs to be acquired before
+ // it is attached so that the interop surface is locked, which populates
+ // the GL renderbuffer. This results in the renderbuffer being ready and
+ // attachment to framebuffer succeeds in Attach() call.
+ newBack->Surf()->ProducerAcquire();
+
+ if (!Attach(newBack->Surf(), size)) {
+ newBack->Surf()->ProducerRelease();
+ return false;
+ }
+ // Attach was successful.
+
+ mFront = mBack;
+ mBack = newBack;
+
+ if (ShouldPreserveBuffer() &&
+ mFront &&
+ mBack &&
+ !mDraw)
+ {
+ auto src = mFront->Surf();
+ auto dest = mBack->Surf();
+
+ //uint32_t srcPixel = ReadPixel(src);
+ //uint32_t destPixel = ReadPixel(dest);
+ //printf_stderr("Before: src: 0x%08x, dest: 0x%08x\n", srcPixel, destPixel);
+#ifdef DEBUG
+ GLContext::LocalErrorScope errorScope(*mGL);
+#endif
+
+ SharedSurface::ProdCopy(src, dest, mFactory.get());
+
+#ifdef DEBUG
+ MOZ_ASSERT(!errorScope.GetError());
+#endif
+
+ //srcPixel = ReadPixel(src);
+ //destPixel = ReadPixel(dest);
+ //printf_stderr("After: src: 0x%08x, dest: 0x%08x\n", srcPixel, destPixel);
+ }
+
+ // XXX: We would prefer to fence earlier on platforms that don't need
+ // the full ProducerAcquire/ProducerRelease semantics, so that the fence
+ // doesn't include the copy operation. Unfortunately, the current API
+ // doesn't expose a good way to do that.
+ if (mFront) {
+ mFront->Surf()->ProducerRelease();
+ }
+
+ return true;
+}
+
+bool
+GLScreenBuffer::PublishFrame(const gfx::IntSize& size)
+{
+ AssureBlitted();
+
+ bool good = Swap(size);
+ return good;
+}
+
+bool
+GLScreenBuffer::Resize(const gfx::IntSize& size)
+{
+ RefPtr<SharedSurfaceTextureClient> newBack = mFactory->NewTexClient(size);
+ if (!newBack)
+ return false;
+
+ if (!Attach(newBack->Surf(), size))
+ return false;
+
+ if (mBack)
+ mBack->Surf()->ProducerRelease();
+
+ mBack = newBack;
+
+ mBack->Surf()->ProducerAcquire();
+
+ return true;
+}
+
+bool
+GLScreenBuffer::CreateDraw(const gfx::IntSize& size,
+ UniquePtr<DrawBuffer>* out_buffer)
+{
+ GLContext* gl = mFactory->mGL;
+ const GLFormats& formats = mFactory->mFormats;
+ const SurfaceCaps& caps = mFactory->DrawCaps();
+
+ return DrawBuffer::Create(gl, caps, formats, size, out_buffer);
+}
+
+UniquePtr<ReadBuffer>
+GLScreenBuffer::CreateRead(SharedSurface* surf)
+{
+ GLContext* gl = mFactory->mGL;
+ const GLFormats& formats = mFactory->mFormats;
+ const SurfaceCaps& caps = mFactory->ReadCaps();
+
+ return ReadBuffer::Create(gl, caps, formats, surf);
+}
+
+void
+GLScreenBuffer::SetDrawBuffer(GLenum mode)
+{
+ MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::draw_buffers));
+ MOZ_ASSERT(GetDrawFB() == 0);
+
+ if (!mGL->IsSupported(GLFeature::draw_buffers))
+ return;
+
+ mUserDrawBufferMode = mode;
+
+ GLuint fb = mDraw ? mDraw->mFB : mRead->mFB;
+ GLenum internalMode;
+
+ switch (mode) {
+ case LOCAL_GL_BACK:
+ internalMode = (fb == 0) ? LOCAL_GL_BACK
+ : LOCAL_GL_COLOR_ATTACHMENT0;
+ break;
+
+ case LOCAL_GL_NONE:
+ internalMode = LOCAL_GL_NONE;
+ break;
+
+ default:
+ MOZ_CRASH("GFX: Bad value.");
+ }
+
+ mGL->MakeCurrent();
+ mGL->fDrawBuffers(1, &internalMode);
+}
+
+void
+GLScreenBuffer::SetReadBuffer(GLenum mode)
+{
+ MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::read_buffer));
+ MOZ_ASSERT(GetReadFB() == 0);
+
+ mUserReadBufferMode = mode;
+ mRead->SetReadBuffer(mUserReadBufferMode);
+}
+
+bool
+GLScreenBuffer::IsDrawFramebufferDefault() const
+{
+ if (!mDraw)
+ return IsReadFramebufferDefault();
+ return mDraw->mFB == 0;
+}
+
+bool
+GLScreenBuffer::IsReadFramebufferDefault() const
+{
+ return SharedSurf()->mAttachType == AttachmentType::Screen;
+}
+
+uint32_t
+GLScreenBuffer::DepthBits() const
+{
+ const GLFormats& formats = mFactory->mFormats;
+
+ if (formats.depth == LOCAL_GL_DEPTH_COMPONENT16)
+ return 16;
+
+ return 24;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Utils
+
+static void
+RenderbufferStorageBySamples(GLContext* aGL, GLsizei aSamples,
+ GLenum aInternalFormat, const gfx::IntSize& aSize)
+{
+ if (aSamples) {
+ aGL->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
+ aSamples,
+ aInternalFormat,
+ aSize.width, aSize.height);
+ } else {
+ aGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
+ aInternalFormat,
+ aSize.width, aSize.height);
+ }
+}
+
+static GLuint
+CreateRenderbuffer(GLContext* aGL, GLenum aFormat, GLsizei aSamples,
+ const gfx::IntSize& aSize)
+{
+ GLuint rb = 0;
+ aGL->fGenRenderbuffers(1, &rb);
+ ScopedBindRenderbuffer autoRB(aGL, rb);
+
+ RenderbufferStorageBySamples(aGL, aSamples, aFormat, aSize);
+
+ return rb;
+}
+
+static void
+CreateRenderbuffersForOffscreen(GLContext* aGL, const GLFormats& aFormats,
+ const gfx::IntSize& aSize, bool aMultisample,
+ GLuint* aColorMSRB, GLuint* aDepthRB,
+ GLuint* aStencilRB)
+{
+ GLsizei samples = aMultisample ? aFormats.samples : 0;
+ if (aColorMSRB) {
+ MOZ_ASSERT(aFormats.samples > 0);
+ MOZ_ASSERT(aFormats.color_rbFormat);
+
+ GLenum colorFormat = aFormats.color_rbFormat;
+ if (aGL->IsANGLE()) {
+ MOZ_ASSERT(colorFormat == LOCAL_GL_RGBA8);
+ colorFormat = LOCAL_GL_BGRA8_EXT;
+ }
+
+ *aColorMSRB = CreateRenderbuffer(aGL, colorFormat, samples, aSize);
+ }
+
+ if (aDepthRB &&
+ aStencilRB &&
+ aFormats.depthStencil)
+ {
+ *aDepthRB = CreateRenderbuffer(aGL, aFormats.depthStencil, samples, aSize);
+ *aStencilRB = *aDepthRB;
+ } else {
+ if (aDepthRB) {
+ MOZ_ASSERT(aFormats.depth);
+
+ *aDepthRB = CreateRenderbuffer(aGL, aFormats.depth, samples, aSize);
+ }
+
+ if (aStencilRB) {
+ MOZ_ASSERT(aFormats.stencil);
+
+ *aStencilRB = CreateRenderbuffer(aGL, aFormats.stencil, samples, aSize);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// DrawBuffer
+
+bool
+DrawBuffer::Create(GLContext* const gl,
+ const SurfaceCaps& caps,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ UniquePtr<DrawBuffer>* out_buffer)
+{
+ MOZ_ASSERT(out_buffer);
+ *out_buffer = nullptr;
+
+ if (!caps.color) {
+ MOZ_ASSERT(!caps.alpha && !caps.depth && !caps.stencil);
+
+ // Nothing is needed.
+ return true;
+ }
+
+ if (caps.antialias) {
+ if (formats.samples == 0)
+ return false; // Can't create it.
+
+ MOZ_ASSERT(formats.samples <= gl->MaxSamples());
+ }
+
+ GLuint colorMSRB = 0;
+ GLuint depthRB = 0;
+ GLuint stencilRB = 0;
+
+ GLuint* pColorMSRB = caps.antialias ? &colorMSRB : nullptr;
+ GLuint* pDepthRB = caps.depth ? &depthRB : nullptr;
+ GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
+
+ if (!formats.color_rbFormat)
+ pColorMSRB = nullptr;
+
+ if (pDepthRB && pStencilRB) {
+ if (!formats.depth && !formats.depthStencil)
+ pDepthRB = nullptr;
+
+ if (!formats.stencil && !formats.depthStencil)
+ pStencilRB = nullptr;
+ } else {
+ if (!formats.depth)
+ pDepthRB = nullptr;
+
+ if (!formats.stencil)
+ pStencilRB = nullptr;
+ }
+
+ GLContext::LocalErrorScope localError(*gl);
+
+ CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias,
+ pColorMSRB, pDepthRB, pStencilRB);
+
+ GLuint fb = 0;
+ gl->fGenFramebuffers(1, &fb);
+ gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb);
+
+ const GLsizei samples = formats.samples;
+ UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, samples, fb, colorMSRB,
+ depthRB, stencilRB) );
+
+ GLenum err = localError.GetError();
+ MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
+ if (err || !gl->IsFramebufferComplete(fb))
+ return false;
+
+ *out_buffer = Move(ret);
+ return true;
+}
+
+DrawBuffer::~DrawBuffer()
+{
+ if (!mGL->MakeCurrent())
+ return;
+
+ GLuint fb = mFB;
+ GLuint rbs[] = {
+ mColorMSRB,
+ mDepthRB,
+ mStencilRB
+ };
+
+ mGL->fDeleteFramebuffers(1, &fb);
+ mGL->fDeleteRenderbuffers(3, rbs);
+}
+
+////////////////////////////////////////////////////////////////////////
+// ReadBuffer
+
+UniquePtr<ReadBuffer>
+ReadBuffer::Create(GLContext* gl,
+ const SurfaceCaps& caps,
+ const GLFormats& formats,
+ SharedSurface* surf)
+{
+ MOZ_ASSERT(surf);
+
+ if (surf->mAttachType == AttachmentType::Screen) {
+ // Don't need anything. Our read buffer will be the 'screen'.
+ return UniquePtr<ReadBuffer>( new ReadBuffer(gl, 0, 0, 0,
+ surf) );
+ }
+
+ GLuint depthRB = 0;
+ GLuint stencilRB = 0;
+
+ GLuint* pDepthRB = caps.depth ? &depthRB : nullptr;
+ GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
+
+ GLContext::LocalErrorScope localError(*gl);
+
+ CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
+ nullptr, pDepthRB, pStencilRB);
+
+ GLuint colorTex = 0;
+ GLuint colorRB = 0;
+ GLenum target = 0;
+
+ switch (surf->mAttachType) {
+ case AttachmentType::GLTexture:
+ colorTex = surf->ProdTexture();
+ target = surf->ProdTextureTarget();
+ break;
+ case AttachmentType::GLRenderbuffer:
+ colorRB = surf->ProdRenderbuffer();
+ break;
+ default:
+ MOZ_CRASH("GFX: Unknown attachment type, create?");
+ }
+ MOZ_ASSERT(colorTex || colorRB);
+
+ GLuint fb = 0;
+ gl->fGenFramebuffers(1, &fb);
+ gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
+ gl->mFBOMapping[fb] = surf;
+
+ UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB,
+ stencilRB, surf) );
+
+ GLenum err = localError.GetError();
+ MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
+ if (err)
+ return nullptr;
+
+ const bool needsAcquire = !surf->IsProducerAcquired();
+ if (needsAcquire) {
+ surf->ProducerReadAcquire();
+ }
+ const bool isComplete = gl->IsFramebufferComplete(fb);
+ if (needsAcquire) {
+ surf->ProducerReadRelease();
+ }
+
+ if (!isComplete)
+ return nullptr;
+
+ return Move(ret);
+}
+
+ReadBuffer::~ReadBuffer()
+{
+ if (!mGL->MakeCurrent())
+ return;
+
+ GLuint fb = mFB;
+ GLuint rbs[] = {
+ mDepthRB,
+ mStencilRB
+ };
+
+ mGL->fDeleteFramebuffers(1, &fb);
+ mGL->fDeleteRenderbuffers(2, rbs);
+ mGL->mFBOMapping.erase(mFB);
+}
+
+void
+ReadBuffer::Attach(SharedSurface* surf)
+{
+ MOZ_ASSERT(surf && mSurf);
+ MOZ_ASSERT(surf->mAttachType == mSurf->mAttachType);
+ MOZ_ASSERT(surf->mSize == mSurf->mSize);
+
+ // Nothing else is needed for AttachType Screen.
+ if (surf->mAttachType != AttachmentType::Screen) {
+ GLuint colorTex = 0;
+ GLuint colorRB = 0;
+ GLenum target = 0;
+
+ switch (surf->mAttachType) {
+ case AttachmentType::GLTexture:
+ colorTex = surf->ProdTexture();
+ target = surf->ProdTextureTarget();
+ break;
+ case AttachmentType::GLRenderbuffer:
+ colorRB = surf->ProdRenderbuffer();
+ break;
+ default:
+ MOZ_CRASH("GFX: Unknown attachment type, attach?");
+ }
+
+ mGL->AttachBuffersToFB(colorTex, colorRB, 0, 0, mFB, target);
+ mGL->mFBOMapping[mFB] = surf;
+ MOZ_ASSERT(mGL->IsFramebufferComplete(mFB));
+ }
+
+ mSurf = surf;
+}
+
+const gfx::IntSize&
+ReadBuffer::Size() const
+{
+ return mSurf->mSize;
+}
+
+void
+ReadBuffer::SetReadBuffer(GLenum userMode) const
+{
+ if (!mGL->IsSupported(GLFeature::read_buffer))
+ return;
+
+ GLenum internalMode;
+
+ switch (userMode) {
+ case LOCAL_GL_BACK:
+ case LOCAL_GL_FRONT:
+ internalMode = (mFB == 0) ? userMode
+ : LOCAL_GL_COLOR_ATTACHMENT0;
+ break;
+
+ case LOCAL_GL_NONE:
+ internalMode = LOCAL_GL_NONE;
+ break;
+
+ default:
+ MOZ_CRASH("GFX: Bad value.");
+ }
+
+ mGL->MakeCurrent();
+ mGL->fReadBuffer(internalMode);
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/GLScreenBuffer.h b/system/graphics/gl/GLScreenBuffer.h
new file mode 100644
index 000000000..20692cf48
--- /dev/null
+++ b/system/graphics/gl/GLScreenBuffer.h
@@ -0,0 +1,302 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* GLScreenBuffer is the abstraction for the "default framebuffer" used
+ * by an offscreen GLContext. Since it's only for offscreen GLContext's,
+ * it's only useful for things like WebGL, and is NOT used by the
+ * compositor's GLContext. Remember that GLContext provides an abstraction
+ * so that even if you want to draw to the 'screen', even if that's not
+ * actually the screen, just draw to 0. This GLScreenBuffer class takes the
+ * logic handling out of GLContext.
+*/
+
+#ifndef SCREEN_BUFFER_H_
+#define SCREEN_BUFFER_H_
+
+#include "GLContextTypes.h"
+#include "GLDefs.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/UniquePtr.h"
+#include "SharedSurface.h"
+#include "SurfaceTypes.h"
+
+namespace mozilla {
+namespace layers {
+class KnowsCompositor;
+class LayersIPCChannel;
+class SharedSurfaceTextureClient;
+} // namespace layers
+
+namespace gl {
+
+class GLContext;
+class SharedSurface;
+class ShSurfHandle;
+class SurfaceFactory;
+
+class DrawBuffer
+{
+public:
+ // Fallible!
+ // But it may return true with *out_buffer==nullptr if unneeded.
+ static bool Create(GLContext* const gl,
+ const SurfaceCaps& caps,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ UniquePtr<DrawBuffer>* out_buffer);
+
+protected:
+ GLContext* const mGL;
+public:
+ const gfx::IntSize mSize;
+ const GLsizei mSamples;
+ const GLuint mFB;
+protected:
+ const GLuint mColorMSRB;
+ const GLuint mDepthRB;
+ const GLuint mStencilRB;
+
+ DrawBuffer(GLContext* gl,
+ const gfx::IntSize& size,
+ GLsizei samples,
+ GLuint fb,
+ GLuint colorMSRB,
+ GLuint depthRB,
+ GLuint stencilRB)
+ : mGL(gl)
+ , mSize(size)
+ , mSamples(samples)
+ , mFB(fb)
+ , mColorMSRB(colorMSRB)
+ , mDepthRB(depthRB)
+ , mStencilRB(stencilRB)
+ {}
+
+public:
+ virtual ~DrawBuffer();
+};
+
+class ReadBuffer
+{
+public:
+ // Infallible, always non-null.
+ static UniquePtr<ReadBuffer> Create(GLContext* gl,
+ const SurfaceCaps& caps,
+ const GLFormats& formats,
+ SharedSurface* surf);
+
+protected:
+ GLContext* const mGL;
+public:
+ const GLuint mFB;
+protected:
+ // mFB has the following attachments:
+ const GLuint mDepthRB;
+ const GLuint mStencilRB;
+ // note no mColorRB here: this is provided by mSurf.
+ SharedSurface* mSurf;
+
+ ReadBuffer(GLContext* gl,
+ GLuint fb,
+ GLuint depthRB,
+ GLuint stencilRB,
+ SharedSurface* surf)
+ : mGL(gl)
+ , mFB(fb)
+ , mDepthRB(depthRB)
+ , mStencilRB(stencilRB)
+ , mSurf(surf)
+ {}
+
+public:
+ virtual ~ReadBuffer();
+
+ // Cannot attach a surf of a different AttachType or Size than before.
+ void Attach(SharedSurface* surf);
+
+ const gfx::IntSize& Size() const;
+
+ SharedSurface* SharedSurf() const {
+ return mSurf;
+ }
+
+ void SetReadBuffer(GLenum mode) const;
+};
+
+
+class GLScreenBuffer
+{
+public:
+ // Infallible.
+ static UniquePtr<GLScreenBuffer> Create(GLContext* gl,
+ const gfx::IntSize& size,
+ const SurfaceCaps& caps);
+
+ static UniquePtr<SurfaceFactory>
+ CreateFactory(GLContext* gl,
+ const SurfaceCaps& caps,
+ layers::KnowsCompositor* compositorConnection,
+ const layers::TextureFlags& flags);
+ static UniquePtr<SurfaceFactory>
+ CreateFactory(GLContext* gl,
+ const SurfaceCaps& caps,
+ layers::LayersIPCChannel* ipcChannel,
+ const mozilla::layers::LayersBackend backend,
+ const layers::TextureFlags& flags);
+
+protected:
+ GLContext* const mGL; // Owns us.
+public:
+ const SurfaceCaps mCaps;
+protected:
+ UniquePtr<SurfaceFactory> mFactory;
+
+ RefPtr<layers::SharedSurfaceTextureClient> mBack;
+ RefPtr<layers::SharedSurfaceTextureClient> mFront;
+
+ UniquePtr<DrawBuffer> mDraw;
+ UniquePtr<ReadBuffer> mRead;
+
+ bool mNeedsBlit;
+
+ GLenum mUserReadBufferMode;
+ GLenum mUserDrawBufferMode;
+
+ // Below are the parts that help us pretend to be framebuffer 0:
+ GLuint mUserDrawFB;
+ GLuint mUserReadFB;
+ GLuint mInternalDrawFB;
+ GLuint mInternalReadFB;
+
+#ifdef DEBUG
+ bool mInInternalMode_DrawFB;
+ bool mInInternalMode_ReadFB;
+#endif
+
+ GLScreenBuffer(GLContext* gl,
+ const SurfaceCaps& caps,
+ UniquePtr<SurfaceFactory> factory);
+
+public:
+ virtual ~GLScreenBuffer();
+
+ SurfaceFactory* Factory() const {
+ return mFactory.get();
+ }
+
+ const RefPtr<layers::SharedSurfaceTextureClient>& Front() const {
+ return mFront;
+ }
+
+ SharedSurface* SharedSurf() const {
+ MOZ_ASSERT(mRead);
+ return mRead->SharedSurf();
+ }
+
+ bool ShouldPreserveBuffer() const {
+ return mCaps.preserve;
+ }
+
+ GLuint DrawFB() const {
+ if (!mDraw)
+ return ReadFB();
+
+ return mDraw->mFB;
+ }
+
+ GLuint ReadFB() const {
+ return mRead->mFB;
+ }
+
+ GLsizei Samples() const {
+ if (!mDraw)
+ return 0;
+
+ return mDraw->mSamples;
+ }
+
+ uint32_t DepthBits() const;
+
+ void DeletingFB(GLuint fb);
+
+ const gfx::IntSize& Size() const {
+ MOZ_ASSERT(mRead);
+ MOZ_ASSERT(!mDraw || mDraw->mSize == mRead->Size());
+ return mRead->Size();
+ }
+
+ void BindAsFramebuffer(GLContext* const gl, GLenum target) const;
+
+ void RequireBlit();
+ void AssureBlitted();
+ void AfterDrawCall();
+ void BeforeReadCall();
+
+ bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border);
+
+ void SetReadBuffer(GLenum userMode);
+ void SetDrawBuffer(GLenum userMode);
+
+ GLenum GetReadBufferMode() const { return mUserReadBufferMode; }
+ GLenum GetDrawBufferMode() const { return mUserDrawBufferMode; }
+
+ /**
+ * Attempts to read pixels from the current bound framebuffer, if
+ * it is backed by a SharedSurface.
+ *
+ * Returns true if the pixel data has been read back, false
+ * otherwise.
+ */
+ bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid* pixels);
+
+ // Morph changes the factory used to create surfaces.
+ void Morph(UniquePtr<SurfaceFactory> newFactory);
+
+protected:
+ // Returns false on error or inability to resize.
+ bool Swap(const gfx::IntSize& size);
+
+public:
+ bool PublishFrame(const gfx::IntSize& size);
+
+ bool Resize(const gfx::IntSize& size);
+
+protected:
+ bool Attach(SharedSurface* surf, const gfx::IntSize& size);
+
+ bool CreateDraw(const gfx::IntSize& size, UniquePtr<DrawBuffer>* out_buffer);
+ UniquePtr<ReadBuffer> CreateRead(SharedSurface* surf);
+
+public:
+ /* `fb` in these functions is the framebuffer the GLContext is hoping to
+ * bind. When this is 0, we intercept the call and bind our own
+ * framebuffers. As a client of these functions, just bind 0 when you want
+ * to draw to the default framebuffer/'screen'.
+ */
+ void BindFB(GLuint fb);
+ void BindDrawFB(GLuint fb);
+ void BindReadFB(GLuint fb);
+ GLuint GetFB() const;
+ GLuint GetDrawFB() const;
+ GLuint GetReadFB() const;
+
+ // Here `fb` is the actual framebuffer you want bound. Binding 0 will
+ // bind the (generally useless) default framebuffer.
+ void BindFB_Internal(GLuint fb);
+ void BindDrawFB_Internal(GLuint fb);
+ void BindReadFB_Internal(GLuint fb);
+
+ bool IsDrawFramebufferDefault() const;
+ bool IsReadFramebufferDefault() const;
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // SCREEN_BUFFER_H_
diff --git a/system/graphics/gl/GLTextureImage.cpp b/system/graphics/gl/GLTextureImage.cpp
new file mode 100644
index 000000000..c91d558af
--- /dev/null
+++ b/system/graphics/gl/GLTextureImage.cpp
@@ -0,0 +1,521 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLTextureImage.h"
+#include "GLContext.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "gfxUtils.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "ScopedGLHelpers.h"
+#include "GLUploadHelpers.h"
+#include "GfxTexturesReporter.h"
+
+#include "TextureImageEGL.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gl {
+
+already_AddRefed<TextureImage>
+CreateTextureImage(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+{
+ switch (gl->GetContextType()) {
+ case GLContextType::EGL:
+ return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
+ default: {
+ GLint maxTextureSize;
+ gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
+ NS_ASSERTION(aWrapMode == LOCAL_GL_CLAMP_TO_EDGE, "Can't support wrapping with tiles!");
+ return CreateTiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat);
+ } else {
+ return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags);
+ }
+ }
+ }
+}
+
+
+static already_AddRefed<TextureImage>
+TileGenFunc(GLContext* gl,
+ const IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+{
+ switch (gl->GetContextType()) {
+ case GLContextType::EGL:
+ return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
+ default:
+ return CreateBasicTextureImage(gl, aSize, aContentType,
+ LOCAL_GL_CLAMP_TO_EDGE, aFlags);
+ }
+}
+
+already_AddRefed<TextureImage>
+TextureImage::Create(GLContext* gl,
+ const gfx::IntSize& size,
+ TextureImage::ContentType contentType,
+ GLenum wrapMode,
+ TextureImage::Flags flags)
+{
+ return CreateTextureImage(gl, size, contentType, wrapMode, flags);
+}
+
+bool
+TextureImage::UpdateFromDataSource(gfx::DataSourceSurface* aSurface,
+ const nsIntRegion* aDestRegion,
+ const gfx::IntPoint* aSrcPoint)
+{
+ nsIntRegion destRegion = aDestRegion ? *aDestRegion
+ : IntRect(0, 0,
+ aSurface->GetSize().width,
+ aSurface->GetSize().height);
+ gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint
+ : gfx::IntPoint(0, 0);
+ return DirectUpdate(aSurface, destRegion, srcPoint);
+}
+
+gfx::IntRect TextureImage::GetTileRect() {
+ return gfx::IntRect(gfx::IntPoint(0,0), mSize);
+}
+
+gfx::IntRect TextureImage::GetSrcTileRect() {
+ return GetTileRect();
+}
+
+void
+TextureImage::UpdateUploadSize(size_t amount)
+{
+ if (mUploadSize > 0) {
+ GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize);
+ }
+ mUploadSize = amount;
+ GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize);
+}
+
+BasicTextureImage::~BasicTextureImage()
+{
+ GLContext* ctx = mGLContext;
+ if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
+ ctx = ctx->GetSharedContext();
+ }
+
+ // If we have a context, then we need to delete the texture;
+ // if we don't have a context (either real or shared),
+ // then they went away when the contex was deleted, because it
+ // was the only one that had access to it.
+ if (ctx && ctx->MakeCurrent()) {
+ ctx->fDeleteTextures(1, &mTexture);
+ }
+}
+
+void
+BasicTextureImage::BindTexture(GLenum aTextureUnit)
+{
+ mGLContext->fActiveTexture(aTextureUnit);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+}
+
+bool
+BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
+{
+ nsIntRegion region;
+ if (mTextureState == Valid) {
+ region = aRegion;
+ } else {
+ region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height));
+ }
+ bool needInit = mTextureState == Created;
+ size_t uploadSize;
+
+ mTextureFormat =
+ UploadSurfaceToTexture(mGLContext,
+ aSurf,
+ region,
+ mTexture,
+ mSize,
+ &uploadSize,
+ needInit,
+ aFrom);
+
+ if (uploadSize > 0) {
+ UpdateUploadSize(uploadSize);
+ }
+ mTextureState = Valid;
+ return true;
+}
+
+void
+BasicTextureImage::Resize(const gfx::IntSize& aSize)
+{
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+
+ // This matches the logic in UploadImageDataToTexture so that
+ // we avoid mixing formats.
+ GLenum format;
+ GLenum type;
+ if (mGLContext->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
+ MOZ_ASSERT(!mGLContext->IsGLES());
+ format = LOCAL_GL_BGRA;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ } else {
+ format = LOCAL_GL_RGBA;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ }
+
+ mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
+ 0,
+ LOCAL_GL_RGBA,
+ aSize.width,
+ aSize.height,
+ 0,
+ format,
+ type,
+ nullptr);
+
+ mTextureState = Allocated;
+ mSize = aSize;
+}
+
+gfx::IntSize TextureImage::GetSize() const {
+ return mSize;
+}
+
+TextureImage::TextureImage(const gfx::IntSize& aSize,
+ GLenum aWrapMode, ContentType aContentType,
+ Flags aFlags)
+ : mSize(aSize)
+ , mWrapMode(aWrapMode)
+ , mContentType(aContentType)
+ , mTextureFormat(gfx::SurfaceFormat::UNKNOWN)
+ , mSamplingFilter(SamplingFilter::GOOD)
+ , mFlags(aFlags)
+ , mUploadSize(0)
+{}
+
+BasicTextureImage::BasicTextureImage(GLuint aTexture,
+ const gfx::IntSize& aSize,
+ GLenum aWrapMode,
+ ContentType aContentType,
+ GLContext* aContext,
+ TextureImage::Flags aFlags)
+ : TextureImage(aSize, aWrapMode, aContentType, aFlags)
+ , mTexture(aTexture)
+ , mTextureState(Created)
+ , mGLContext(aContext)
+{}
+
+static bool
+WantsSmallTiles(GLContext* gl)
+{
+ // We can't use small tiles on the SGX 540, because of races in texture upload.
+ if (gl->WorkAroundDriverBugs() &&
+ gl->Renderer() == GLRenderer::SGX540)
+ return false;
+
+ // We should use small tiles for good performance if we can't use
+ // glTexSubImage2D() for some reason.
+ if (!CanUploadSubTextures(gl))
+ return true;
+
+ // Don't use small tiles otherwise. (If we implement incremental texture upload,
+ // then we will want to revisit this.)
+ return false;
+}
+
+TiledTextureImage::TiledTextureImage(GLContext* aGL,
+ gfx::IntSize aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+ : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
+ , mCurrentImage(0)
+ , mIterationCallback(nullptr)
+ , mIterationCallbackData(nullptr)
+ , mRows(0)
+ , mColumns(0)
+ , mGL(aGL)
+ , mTextureState(Created)
+ , mImageFormat(aImageFormat)
+{
+ if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) {
+ mTileSize = 256;
+ } else {
+ mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize);
+ }
+ if (aSize.width != 0 && aSize.height != 0) {
+ Resize(aSize);
+ }
+}
+
+TiledTextureImage::~TiledTextureImage()
+{
+}
+
+bool
+TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
+{
+ if (mSize.width == 0 || mSize.height == 0) {
+ return true;
+ }
+
+ nsIntRegion region;
+
+ if (mTextureState != Valid) {
+ IntRect bounds = IntRect(0, 0, mSize.width, mSize.height);
+ region = nsIntRegion(bounds);
+ } else {
+ region = aRegion;
+ }
+
+ bool result = true;
+ int oldCurrentImage = mCurrentImage;
+ BeginBigImageIteration();
+ do {
+ IntRect tileRect = GetSrcTileRect();
+ int xPos = tileRect.x;
+ int yPos = tileRect.y;
+
+ nsIntRegion tileRegion;
+ tileRegion.And(region, tileRect); // intersect with tile
+
+ if (tileRegion.IsEmpty())
+ continue;
+
+ tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
+
+ result &= mImages[mCurrentImage]->
+ DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos));
+
+ if (mCurrentImage == mImages.Length() - 1) {
+ // We know we're done, but we still need to ensure that the callback
+ // gets called (e.g. to update the uploaded region).
+ NextTile();
+ break;
+ }
+ // Override a callback cancelling iteration if the texture wasn't valid.
+ // We need to force the update in that situation, or we may end up
+ // showing invalid/out-of-date texture data.
+ } while (NextTile() || (mTextureState != Valid));
+ mCurrentImage = oldCurrentImage;
+
+ mTextureFormat = mImages[0]->GetTextureFormat();
+ mTextureState = Valid;
+ return result;
+}
+
+void TiledTextureImage::BeginBigImageIteration()
+{
+ mCurrentImage = 0;
+}
+
+bool TiledTextureImage::NextTile()
+{
+ bool continueIteration = true;
+
+ if (mIterationCallback)
+ continueIteration = mIterationCallback(this, mCurrentImage,
+ mIterationCallbackData);
+
+ if (mCurrentImage + 1 < mImages.Length()) {
+ mCurrentImage++;
+ return continueIteration;
+ }
+ return false;
+}
+
+void TiledTextureImage::SetIterationCallback(BigImageIterationCallback aCallback,
+ void* aCallbackData)
+{
+ mIterationCallback = aCallback;
+ mIterationCallbackData = aCallbackData;
+}
+
+gfx::IntRect TiledTextureImage::GetTileRect()
+{
+ if (!GetTileCount()) {
+ return gfx::IntRect();
+ }
+ gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect();
+ unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
+ unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
+ rect.MoveBy(xPos, yPos);
+ return rect;
+}
+
+gfx::IntRect TiledTextureImage::GetSrcTileRect()
+{
+ gfx::IntRect rect = GetTileRect();
+ const bool needsYFlip = mFlags & OriginBottomLeft;
+ unsigned int srcY = needsYFlip ? mSize.height - rect.height - rect.y
+ : rect.y;
+ return gfx::IntRect(rect.x, srcY, rect.width, rect.height);
+}
+
+void
+TiledTextureImage::BindTexture(GLenum aTextureUnit)
+{
+ if (!GetTileCount()) {
+ return;
+ }
+ mImages[mCurrentImage]->BindTexture(aTextureUnit);
+}
+
+/*
+ * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
+ * column. A tile on a column is reused if it hasn't changed size, otherwise it
+ * is discarded/replaced. Extra tiles on a column are pruned after iterating
+ * each column, and extra rows are pruned after iteration over the entire image
+ * finishes.
+ */
+void TiledTextureImage::Resize(const gfx::IntSize& aSize)
+{
+ if (mSize == aSize && mTextureState != Created) {
+ return;
+ }
+
+ // calculate rows and columns, rounding up
+ unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
+ unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
+
+ // Iterate over old tile-store and insert/remove tiles as necessary
+ int row;
+ unsigned int i = 0;
+ for (row = 0; row < (int)rows; row++) {
+ // If we've gone beyond how many rows there were before, set mColumns to
+ // zero so that we only create new tiles.
+ if (row >= (int)mRows)
+ mColumns = 0;
+
+ // Similarly, if we're on the last row of old tiles and the height has
+ // changed, discard all tiles in that row.
+ // This will cause the pruning of columns not to work, but we don't need
+ // to worry about that, as no more tiles will be reused past this point
+ // anyway.
+ if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
+ mColumns = 0;
+
+ int col;
+ for (col = 0; col < (int)columns; col++) {
+ IntSize size( // use tilesize first, then the remainder
+ (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
+ (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
+
+ bool replace = false;
+
+ // Check if we can re-use old tiles.
+ if (col < (int)mColumns) {
+ // Reuse an existing tile. If the tile is an end-tile and the
+ // width differs, replace it instead.
+ if (mSize.width != aSize.width) {
+ if (col == (int)mColumns - 1) {
+ // Tile at the end of the old column, replace it with
+ // a new one.
+ replace = true;
+ } else if (col == (int)columns - 1) {
+ // Tile at the end of the new column, create a new one.
+ } else {
+ // Before the last column on both the old and new sizes,
+ // reuse existing tile.
+ i++;
+ continue;
+ }
+ } else {
+ // Width hasn't changed, reuse existing tile.
+ i++;
+ continue;
+ }
+ }
+
+ // Create a new tile.
+ RefPtr<TextureImage> teximg =
+ TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat);
+ if (replace)
+ mImages.ReplaceElementAt(i, teximg);
+ else
+ mImages.InsertElementAt(i, teximg);
+ i++;
+ }
+
+ // Prune any unused tiles on the end of the column.
+ if (row < (int)mRows) {
+ for (col = (int)mColumns - col; col > 0; col--) {
+ mImages.RemoveElementAt(i);
+ }
+ }
+ }
+
+ // Prune any unused tiles at the end of the store.
+ unsigned int length = mImages.Length();
+ for (; i < length; i++)
+ mImages.RemoveElementAt(mImages.Length()-1);
+
+ // Reset tile-store properties.
+ mRows = rows;
+ mColumns = columns;
+ mSize = aSize;
+ mTextureState = Allocated;
+ mCurrentImage = 0;
+}
+
+uint32_t TiledTextureImage::GetTileCount()
+{
+ return mImages.Length();
+}
+
+already_AddRefed<TextureImage>
+CreateTiledTextureImage(GLContext* aGL,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+{
+ RefPtr<TextureImage> texImage = static_cast<TextureImage*>(
+ new gl::TiledTextureImage(aGL, aSize, aContentType, aFlags, aImageFormat));
+ return texImage.forget();
+}
+
+
+already_AddRefed<TextureImage>
+CreateBasicTextureImage(GLContext* aGL,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags)
+{
+ bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
+ if (!aGL->MakeCurrent()) {
+ return nullptr;
+ }
+
+ GLuint texture = 0;
+ aGL->fGenTextures(1, &texture);
+
+ ScopedBindTexture bind(aGL, texture);
+
+ GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
+
+ RefPtr<BasicTextureImage> texImage =
+ new BasicTextureImage(texture, aSize, aWrapMode, aContentType,
+ aGL, aFlags);
+ return texImage.forget();
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/GLTextureImage.h b/system/graphics/gl/GLTextureImage.h
new file mode 100644
index 000000000..1dd22fb54
--- /dev/null
+++ b/system/graphics/gl/GLTextureImage.h
@@ -0,0 +1,306 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLTEXTUREIMAGE_H_
+#define GLTEXTUREIMAGE_H_
+
+#include "nsRegion.h"
+#include "nsTArray.h"
+#include "gfxTypes.h"
+#include "GLContextTypes.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/RefPtr.h"
+
+class gfxASurface;
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class DrawTarget;
+} // namespace gfx
+} // namespace mozilla
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+
+/**
+ * A TextureImage provides a mechanism to synchronize data from a
+ * surface to a texture in the server. TextureImages are associated
+ * with one and only one GLContext.
+ */
+class TextureImage
+{
+ NS_INLINE_DECL_REFCOUNTING(TextureImage)
+public:
+ enum TextureState
+ {
+ Created, // Texture created, but has not had glTexImage called to initialize it.
+ Allocated, // Texture memory exists, but contents are invalid.
+ Valid // Texture fully ready to use.
+ };
+
+ enum Flags {
+ NoFlags = 0x0,
+ UseNearestFilter = 0x1,
+ OriginBottomLeft = 0x2,
+ DisallowBigImage = 0x4
+ };
+
+ typedef gfxContentType ContentType;
+ typedef gfxImageFormat ImageFormat;
+
+ static already_AddRefed<TextureImage> Create(
+ GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags = TextureImage::NoFlags);
+
+ /**
+ * The Image may contain several textures for different regions (tiles).
+ * These functions iterate over each sub texture image tile.
+ */
+ virtual void BeginBigImageIteration() {
+ }
+
+ virtual bool NextTile() {
+ return false;
+ }
+
+ // Function prototype for a tile iteration callback. Returning false will
+ // cause iteration to be interrupted (i.e. the corresponding NextTile call
+ // will return false).
+ typedef bool (* BigImageIterationCallback)(TextureImage* aImage,
+ int aTileNumber,
+ void* aCallbackData);
+
+ // Sets a callback to be called every time NextTile is called.
+ virtual void SetIterationCallback(BigImageIterationCallback aCallback,
+ void* aCallbackData) {
+ }
+
+ virtual gfx::IntRect GetTileRect();
+
+ virtual GLuint GetTextureID() = 0;
+
+ virtual uint32_t GetTileCount() {
+ return 1;
+ }
+
+ /**
+ * Set this TextureImage's size, and ensure a texture has been
+ * allocated.
+ * After a resize, the contents are undefined.
+ */
+ virtual void Resize(const gfx::IntSize& aSize) = 0;
+
+ /**
+ * Mark this texture as having valid contents. Call this after modifying
+ * the texture contents externally.
+ */
+ virtual void MarkValid() {}
+
+ /**
+ * aSurf - the source surface to update from
+ * aRegion - the region in this image to update
+ * aFrom - offset in the source to update from
+ */
+ virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0)) = 0;
+ bool UpdateFromDataSource(gfx::DataSourceSurface* aSurf,
+ const nsIntRegion* aDstRegion = nullptr,
+ const gfx::IntPoint* aSrcOffset = nullptr);
+
+ virtual void BindTexture(GLenum aTextureUnit) = 0;
+
+ /**
+ * Returns the image format of the texture. Only valid after
+ * DirectUpdate has been called.
+ */
+ virtual gfx::SurfaceFormat GetTextureFormat() {
+ return mTextureFormat;
+ }
+
+ /** Can be called safely at any time. */
+
+ /**
+ * If this TextureImage has a permanent gfxASurface backing,
+ * return it. Otherwise return nullptr.
+ */
+ virtual already_AddRefed<gfxASurface> GetBackingSurface()
+ { return nullptr; }
+
+
+ gfx::IntSize GetSize() const;
+ ContentType GetContentType() const { return mContentType; }
+ GLenum GetWrapMode() const { return mWrapMode; }
+
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) {
+ mSamplingFilter = aSamplingFilter;
+ }
+
+protected:
+ friend class GLContext;
+
+ void UpdateUploadSize(size_t amount);
+
+ /**
+ * After the ctor, the TextureImage is invalid. Implementations
+ * must allocate resources successfully before returning the new
+ * TextureImage from GLContext::CreateTextureImage(). That is,
+ * clients must not be given partially-constructed TextureImages.
+ */
+ TextureImage(const gfx::IntSize& aSize,
+ GLenum aWrapMode, ContentType aContentType,
+ Flags aFlags = NoFlags);
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~TextureImage() {
+ UpdateUploadSize(0);
+ }
+
+ virtual gfx::IntRect GetSrcTileRect();
+
+ gfx::IntSize mSize;
+ GLenum mWrapMode;
+ ContentType mContentType;
+ gfx::SurfaceFormat mTextureFormat;
+ gfx::SamplingFilter mSamplingFilter;
+ Flags mFlags;
+ size_t mUploadSize;
+};
+
+/**
+ * BasicTextureImage is the baseline TextureImage implementation ---
+ * it updates its texture by allocating a scratch buffer for the
+ * client to draw into, then using glTexSubImage2D() to upload the new
+ * pixels. Platforms must provide the code to create a new surface
+ * into which the updated pixels will be drawn, and the code to
+ * convert the update surface's pixels into an image on which we can
+ * glTexSubImage2D().
+ */
+class BasicTextureImage
+ : public TextureImage
+{
+public:
+ virtual ~BasicTextureImage();
+
+ BasicTextureImage(GLuint aTexture,
+ const gfx::IntSize& aSize,
+ GLenum aWrapMode,
+ ContentType aContentType,
+ GLContext* aContext,
+ TextureImage::Flags aFlags = TextureImage::NoFlags);
+
+ virtual void BindTexture(GLenum aTextureUnit);
+
+ virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
+ virtual GLuint GetTextureID() { return mTexture; }
+
+ virtual void MarkValid() { mTextureState = Valid; }
+
+ virtual void Resize(const gfx::IntSize& aSize);
+
+protected:
+ GLuint mTexture;
+ TextureState mTextureState;
+ RefPtr<GLContext> mGLContext;
+};
+
+/**
+ * A container class that complements many sub TextureImages into a big TextureImage.
+ * Aims to behave just like the real thing.
+ */
+
+class TiledTextureImage final
+ : public TextureImage
+{
+public:
+ TiledTextureImage(GLContext* aGL,
+ gfx::IntSize aSize,
+ TextureImage::ContentType,
+ TextureImage::Flags aFlags = TextureImage::NoFlags,
+ TextureImage::ImageFormat aImageFormat = gfx::SurfaceFormat::UNKNOWN);
+ ~TiledTextureImage();
+ void DumpDiv();
+ virtual void Resize(const gfx::IntSize& aSize);
+ virtual uint32_t GetTileCount();
+ virtual void BeginBigImageIteration();
+ virtual bool NextTile();
+ virtual void SetIterationCallback(BigImageIterationCallback aCallback,
+ void* aCallbackData);
+ virtual gfx::IntRect GetTileRect();
+ virtual GLuint GetTextureID() {
+ return mImages[mCurrentImage]->GetTextureID();
+ }
+ virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
+ virtual void BindTexture(GLenum);
+
+protected:
+ virtual gfx::IntRect GetSrcTileRect();
+
+ unsigned int mCurrentImage;
+ BigImageIterationCallback mIterationCallback;
+ void* mIterationCallbackData;
+ nsTArray< RefPtr<TextureImage> > mImages;
+ unsigned int mTileSize;
+ unsigned int mRows, mColumns;
+ GLContext* mGL;
+ TextureState mTextureState;
+ TextureImage::ImageFormat mImageFormat;
+};
+
+/**
+ * Creates a TextureImage of the basic implementation, can be useful in cases
+ * where we know we don't want to use platform-specific TextureImage.
+ * In doubt, use GLContext::CreateTextureImage instead.
+ */
+already_AddRefed<TextureImage>
+CreateBasicTextureImage(GLContext* aGL,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags);
+
+/**
+ * Creates a TiledTextureImage backed by platform-specific or basic TextureImages.
+ * In doubt, use GLContext::CreateTextureImage instead.
+ */
+already_AddRefed<TextureImage>
+CreateTiledTextureImage(GLContext* aGL,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat);
+
+/**
+ * Return a valid, allocated TextureImage of |aSize| with
+ * |aContentType|. If |aContentType| is COLOR, |aImageFormat| can be used
+ * to hint at the preferred RGB format, however it is not necessarily
+ * respected. The TextureImage's texture is configured to use
+ * |aWrapMode| (usually GL_CLAMP_TO_EDGE or GL_REPEAT) and by
+ * default, GL_LINEAR filtering. Specify
+ * |aFlags=UseNearestFilter| for GL_NEAREST filtering. Specify
+ * |aFlags=OriginBottomLeft| if the image is origin-bottom-left, instead of the
+ * default origin-top-left. Return
+ * nullptr if creating the TextureImage fails.
+ *
+ * The returned TextureImage may only be used with this GLContext.
+ * Attempting to use the returned TextureImage after this
+ * GLContext is destroyed will result in undefined (and likely
+ * crashy) behavior.
+ */
+already_AddRefed<TextureImage>
+CreateTextureImage(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags = TextureImage::NoFlags,
+ TextureImage::ImageFormat aImageFormat = gfx::SurfaceFormat::UNKNOWN);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif /* GLTEXTUREIMAGE_H_ */
diff --git a/system/graphics/gl/GLTypes.h b/system/graphics/gl/GLTypes.h
new file mode 100644
index 000000000..af2701986
--- /dev/null
+++ b/system/graphics/gl/GLTypes.h
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(GLTYPES_H_)
+#define GLTYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef GLAPIENTRY
+# ifdef WIN32
+# include <windef.h>
+# define GLAPIENTRY APIENTRY
+# define GLAPI
+# else
+# define GLAPIENTRY
+# define GLAPI
+# endif
+#endif
+
+typedef int8_t realGLboolean;
+
+#if !defined(__gltypes_h_) && !defined(__gl_h_)
+#define __gltypes_h_
+#define __gl_h_
+
+typedef uint32_t GLenum;
+typedef uint32_t GLbitfield;
+typedef uint32_t GLuint;
+typedef int32_t GLint;
+typedef int32_t GLsizei;
+typedef int8_t GLbyte;
+typedef int16_t GLshort;
+typedef uint8_t GLubyte;
+typedef uint16_t GLushort;
+typedef float GLfloat;
+typedef float GLclampf;
+#ifndef GLdouble_defined
+typedef double GLdouble;
+#endif
+typedef double GLclampd;
+typedef void GLvoid;
+
+typedef char GLchar;
+#ifndef __gl2_h_
+typedef intptr_t GLsizeiptr;
+typedef intptr_t GLintptr;
+#endif
+
+#endif /* #if !defined(__gltypes_h_) && !defined(__gl_h_) */
+
+#include <stdint.h>
+
+// ARB_sync
+typedef struct __GLsync* GLsync;
+typedef int64_t GLint64;
+typedef uint64_t GLuint64;
+
+// OES_EGL_image (GLES)
+typedef void* GLeglImage;
+
+// KHR_debug
+typedef void (GLAPIENTRY *GLDEBUGPROC)(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const GLvoid* userParam);
+
+// EGL types
+typedef void* EGLImage;
+typedef int EGLint;
+typedef unsigned int EGLBoolean;
+typedef unsigned int EGLenum;
+typedef void* EGLConfig;
+typedef void* EGLContext;
+typedef void* EGLDisplay;
+typedef void* EGLSurface;
+typedef void* EGLClientBuffer;
+typedef void* EGLCastToRelevantPtr;
+typedef void* EGLImage;
+typedef void* EGLSync;
+typedef uint64_t EGLTime;
+
+#define EGL_NO_CONTEXT ((EGLContext)0)
+#define EGL_NO_DISPLAY ((EGLDisplay)0)
+#define EGL_NO_SURFACE ((EGLSurface)0)
+#define EGL_NO_CONFIG ((EGLConfig)nullptr)
+#define EGL_NO_SYNC ((EGLSync)0)
+#define EGL_NO_IMAGE ((EGLImage)0)
+
+#endif
diff --git a/system/graphics/gl/GLUploadHelpers.cpp b/system/graphics/gl/GLUploadHelpers.cpp
new file mode 100644
index 000000000..0bbb61434
--- /dev/null
+++ b/system/graphics/gl/GLUploadHelpers.cpp
@@ -0,0 +1,592 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLUploadHelpers.h"
+
+#include "GLContext.h"
+#include "mozilla/gfx/2D.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/Tools.h" // For BytesPerPixel
+#include "nsRegion.h"
+#include "GfxTexturesReporter.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace gl {
+
+static unsigned int
+DataOffset(const IntPoint& aPoint, int32_t aStride, SurfaceFormat aFormat)
+{
+ unsigned int data = aPoint.y * aStride;
+ data += aPoint.x * BytesPerPixel(aFormat);
+ return data;
+}
+
+static GLint GetAddressAlignment(ptrdiff_t aAddress)
+{
+ if (!(aAddress & 0x7)) {
+ return 8;
+ } else if (!(aAddress & 0x3)) {
+ return 4;
+ } else if (!(aAddress & 0x1)) {
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+// Take texture data in a given buffer and copy it into a larger buffer,
+// padding out the edge pixels for filtering if necessary
+static void
+CopyAndPadTextureData(const GLvoid* srcBuffer,
+ GLvoid* dstBuffer,
+ GLsizei srcWidth, GLsizei srcHeight,
+ GLsizei dstWidth, GLsizei dstHeight,
+ GLsizei stride, GLint pixelsize)
+{
+ unsigned char* rowDest = static_cast<unsigned char*>(dstBuffer);
+ const unsigned char* source = static_cast<const unsigned char*>(srcBuffer);
+
+ for (GLsizei h = 0; h < srcHeight; ++h) {
+ memcpy(rowDest, source, srcWidth * pixelsize);
+ rowDest += dstWidth * pixelsize;
+ source += stride;
+ }
+
+ GLsizei padHeight = srcHeight;
+
+ // Pad out an extra row of pixels so that edge filtering doesn't use garbage data
+ if (dstHeight > srcHeight) {
+ memcpy(rowDest, source - stride, srcWidth * pixelsize);
+ padHeight++;
+ }
+
+ // Pad out an extra column of pixels
+ if (dstWidth > srcWidth) {
+ rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize;
+ for (GLsizei h = 0; h < padHeight; ++h) {
+ memcpy(rowDest, rowDest - pixelsize, pixelsize);
+ rowDest += dstWidth * pixelsize;
+ }
+ }
+}
+
+// In both of these cases (for the Adreno at least) it is impossible
+// to determine good or bad driver versions for POT texture uploads,
+// so blacklist them all. Newer drivers use a different rendering
+// string in the form "Adreno (TM) 200" and the drivers we've seen so
+// far work fine with NPOT textures, so don't blacklist those until we
+// have evidence of any problems with them.
+bool
+CanUploadSubTextures(GLContext* gl)
+{
+ if (!gl->WorkAroundDriverBugs())
+ return true;
+
+ // There are certain GPUs that we don't want to use glTexSubImage2D on
+ // because that function can be very slow and/or buggy
+ if (gl->Renderer() == GLRenderer::Adreno200 ||
+ gl->Renderer() == GLRenderer::Adreno205)
+ {
+ return false;
+ }
+
+ // On PowerVR glTexSubImage does a readback, so it will be slower
+ // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms
+ if (gl->Renderer() == GLRenderer::SGX540 ||
+ gl->Renderer() == GLRenderer::SGX530)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+static void
+TexSubImage2DWithUnpackSubimageGLES(GLContext* gl,
+ GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height,
+ GLsizei stride, GLint pixelsize,
+ GLenum format, GLenum type,
+ const GLvoid* pixels)
+{
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra
+ // driver crash where the driver apparently tries to read
+ // (stride - width * pixelsize) bytes past the end of the last input
+ // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH,
+ // and then we upload the final row separately. See bug 697990.
+ int rowLength = stride/pixelsize;
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
+ gl->fTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height-1,
+ format,
+ type,
+ pixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
+ gl->fTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset+height-1,
+ width,
+ 1,
+ format,
+ type,
+ (const unsigned char*)pixels+(height-1)*stride);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+}
+
+static void
+TexSubImage2DWithoutUnpackSubimage(GLContext* gl,
+ GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height,
+ GLsizei stride, GLint pixelsize,
+ GLenum format, GLenum type,
+ const GLvoid* pixels)
+{
+ // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH
+ // isn't supported. We make a copy of the texture data we're using,
+ // such that we're using the whole row of data in the copy. This turns
+ // out to be more efficient than uploading row-by-row; see bug 698197.
+
+ // Width and height are never more than 16384. At 16Ki*16Ki, 4Bpp is 1GiB, but
+ // if we allow 8Bpp (16-bit channels, or higher) here, that's 2GiB+, which would
+ // overflow on 32-bit.
+ MOZ_ASSERT(width <= 16384);
+ MOZ_ASSERT(height <= 16384);
+ MOZ_ASSERT(pixelsize < 8);
+
+ const auto size = CheckedInt<size_t>(width) * height * pixelsize;
+ if (!size.isValid()) {
+ // This should never happen, but we use a defensive check.
+ MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
+ return;
+ }
+
+ unsigned char* newPixels = new (fallible) unsigned char[size.value()];
+
+ if (newPixels) {
+ unsigned char* rowDest = newPixels;
+ const unsigned char* rowSource = (const unsigned char*)pixels;
+ for (int h = 0; h < height; h++) {
+ memcpy(rowDest, rowSource, width*pixelsize);
+ rowDest += width*pixelsize;
+ rowSource += stride;
+ }
+
+ stride = width*pixelsize;
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)newPixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ gl->fTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ format,
+ type,
+ newPixels);
+ delete [] newPixels;
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+
+ } else {
+ // If we did not have sufficient memory for the required
+ // temporary buffer, then fall back to uploading row-by-row.
+ const unsigned char* rowSource = (const unsigned char*)pixels;
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+
+ for (int i = 0; i < height; i++) {
+ gl->fTexSubImage2D(target, level,
+ xoffset, yoffset + i,
+ width, 1,
+ format, type, rowSource);
+ rowSource += stride;
+ }
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+ }
+}
+
+static void
+TexSubImage2DHelper(GLContext* gl,
+ GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height, GLsizei stride,
+ GLint pixelsize, GLenum format,
+ GLenum type, const GLvoid* pixels)
+{
+ if (gl->IsGLES()) {
+ if (stride == width * pixelsize) {
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ gl->fTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ format,
+ type,
+ pixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+ } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage)) {
+ TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset,
+ width, height, stride,
+ pixelsize, format, type, pixels);
+
+ } else {
+ TexSubImage2DWithoutUnpackSubimage(gl, target, level, xoffset, yoffset,
+ width, height, stride,
+ pixelsize, format, type, pixels);
+ }
+ } else {
+ // desktop GL (non-ES) path
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ int rowLength = stride/pixelsize;
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
+ gl->fTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ format,
+ type,
+ pixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+ }
+}
+
+static void
+TexImage2DHelper(GLContext* gl,
+ GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLsizei stride,
+ GLint pixelsize, GLint border, GLenum format,
+ GLenum type, const GLvoid* pixels)
+{
+ if (gl->IsGLES()) {
+
+ NS_ASSERTION(format == (GLenum)internalformat,
+ "format and internalformat not the same for glTexImage2D on GLES2");
+
+ MOZ_ASSERT(width >= 0 && height >= 0);
+ if (!CanUploadNonPowerOfTwo(gl)
+ && (stride != width * pixelsize
+ || !IsPowerOfTwo((uint32_t)width)
+ || !IsPowerOfTwo((uint32_t)height))) {
+
+ // Pad out texture width and height to the next power of two
+ // as we don't support/want non power of two texture uploads
+ GLsizei paddedWidth = RoundUpPow2((uint32_t)width);
+ GLsizei paddedHeight = RoundUpPow2((uint32_t)height);
+
+ // Width and height are never more than 16384. At 16Ki*16Ki, 4Bpp
+ // is 1GiB, but if we allow 8Bpp (or higher) here, that's 2GiB,
+ // which would overflow on 32-bit.
+ MOZ_ASSERT(width <= 16384);
+ MOZ_ASSERT(height <= 16384);
+ MOZ_ASSERT(pixelsize < 8);
+
+ const auto size =
+ CheckedInt<size_t>(paddedWidth) * paddedHeight * pixelsize;
+ if (!size.isValid()) {
+ // This should never happen, but we use a defensive check.
+ MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
+ return;
+ }
+
+ GLvoid* paddedPixels = new unsigned char[size.value()];
+
+ // Pad out texture data to be in a POT sized buffer for uploading to
+ // a POT sized texture
+ CopyAndPadTextureData(pixels, paddedPixels, width, height,
+ paddedWidth, paddedHeight, stride, pixelsize);
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)paddedPixels),
+ GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize)));
+ gl->fTexImage2D(target,
+ border,
+ internalformat,
+ paddedWidth,
+ paddedHeight,
+ border,
+ format,
+ type,
+ paddedPixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+
+ delete[] static_cast<unsigned char*>(paddedPixels);
+ return;
+ }
+
+ if (stride == width * pixelsize) {
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ gl->fTexImage2D(target,
+ border,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ pixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+ } else {
+ // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are
+ // implemented in TexSubImage2D.
+ gl->fTexImage2D(target,
+ border,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ nullptr);
+ TexSubImage2DHelper(gl,
+ target,
+ level,
+ 0,
+ 0,
+ width,
+ height,
+ stride,
+ pixelsize,
+ format,
+ type,
+ pixels);
+ }
+ } else {
+ // desktop GL (non-ES) path
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
+ std::min(GetAddressAlignment((ptrdiff_t)pixels),
+ GetAddressAlignment((ptrdiff_t)stride)));
+ int rowLength = stride/pixelsize;
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
+ gl->fTexImage2D(target,
+ level,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ pixels);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+ }
+}
+
+SurfaceFormat
+UploadImageDataToTexture(GLContext* gl,
+ unsigned char* aData,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ const nsIntRegion& aDstRegion,
+ GLuint aTexture,
+ const gfx::IntSize& aSize,
+ size_t* aOutUploadSize,
+ bool aNeedInit,
+ GLenum aTextureUnit,
+ GLenum aTextureTarget)
+{
+ gl->MakeCurrent();
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(aTextureTarget, aTexture);
+
+ GLenum format = 0;
+ GLenum internalFormat = 0;
+ GLenum type = 0;
+ int32_t pixelSize = BytesPerPixel(aFormat);
+ SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN;
+
+ MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA ||
+ gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA);
+
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
+ format = LOCAL_GL_BGRA;
+ surfaceFormat = SurfaceFormat::R8G8B8A8;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ } else {
+ format = LOCAL_GL_RGBA;
+ surfaceFormat = SurfaceFormat::B8G8R8A8;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ }
+ internalFormat = LOCAL_GL_RGBA;
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ // Treat BGRX surfaces as BGRA except for the surface
+ // format used.
+ if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
+ format = LOCAL_GL_BGRA;
+ surfaceFormat = SurfaceFormat::R8G8B8X8;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ } else {
+ format = LOCAL_GL_RGBA;
+ surfaceFormat = SurfaceFormat::B8G8R8X8;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ }
+ internalFormat = LOCAL_GL_RGBA;
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
+ // Upload our RGBA as BGRA, but store that the uploaded format is
+ // BGRA. (sample from R to get B)
+ format = LOCAL_GL_BGRA;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ surfaceFormat = SurfaceFormat::B8G8R8A8;
+ } else {
+ format = LOCAL_GL_RGBA;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ surfaceFormat = SurfaceFormat::R8G8B8A8;
+ }
+ internalFormat = LOCAL_GL_RGBA;
+ break;
+ case SurfaceFormat::R8G8B8X8:
+ // Treat RGBX surfaces as RGBA except for the surface
+ // format used.
+ if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
+ format = LOCAL_GL_BGRA;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ surfaceFormat = SurfaceFormat::B8G8R8X8;
+ } else {
+ format = LOCAL_GL_RGBA;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ surfaceFormat = SurfaceFormat::R8G8B8X8;
+ }
+ internalFormat = LOCAL_GL_RGBA;
+ break;
+ case SurfaceFormat::R5G6B5_UINT16:
+ internalFormat = format = LOCAL_GL_RGB;
+ type = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
+ surfaceFormat = SurfaceFormat::R5G6B5_UINT16;
+ break;
+ case SurfaceFormat::A8:
+ internalFormat = format = LOCAL_GL_LUMINANCE;
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ // We don't have a specific luminance shader
+ surfaceFormat = SurfaceFormat::A8;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unhandled image surface format!");
+ }
+
+ if (aOutUploadSize) {
+ *aOutUploadSize = 0;
+ }
+
+ if (surfaceFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ if (aNeedInit || !CanUploadSubTextures(gl)) {
+ // If the texture needs initialized, or we are unable to
+ // upload sub textures, then initialize and upload the entire
+ // texture.
+ TexImage2DHelper(gl,
+ aTextureTarget,
+ 0,
+ internalFormat,
+ aSize.width,
+ aSize.height,
+ aStride,
+ pixelSize,
+ 0,
+ format,
+ type,
+ aData);
+
+ if (aOutUploadSize && aNeedInit) {
+ uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
+ size_t numTexels = size_t(aSize.width) * size_t(aSize.height);
+ *aOutUploadSize += texelSize * numTexels;
+ }
+ } else {
+ // Upload each rect in the region to the texture
+ for (auto iter = aDstRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ const unsigned char* rectData =
+ aData + DataOffset(rect.TopLeft(), aStride, aFormat);
+
+ TexSubImage2DHelper(gl,
+ aTextureTarget,
+ 0,
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height,
+ aStride,
+ pixelSize,
+ format,
+ type,
+ rectData);
+ }
+ }
+
+ return surfaceFormat;
+}
+
+SurfaceFormat
+UploadSurfaceToTexture(GLContext* gl,
+ DataSourceSurface* aSurface,
+ const nsIntRegion& aDstRegion,
+ GLuint aTexture,
+ const gfx::IntSize& aSize,
+ size_t* aOutUploadSize,
+ bool aNeedInit,
+ const gfx::IntPoint& aSrcPoint,
+ GLenum aTextureUnit,
+ GLenum aTextureTarget)
+{
+
+ int32_t stride = aSurface->Stride();
+ SurfaceFormat format = aSurface->GetFormat();
+ unsigned char* data = aSurface->GetData() +
+ DataOffset(aSrcPoint, stride, format);
+
+ return UploadImageDataToTexture(gl, data, stride, format,
+ aDstRegion, aTexture, aSize,
+ aOutUploadSize, aNeedInit,
+ aTextureUnit, aTextureTarget);
+}
+
+bool
+CanUploadNonPowerOfTwo(GLContext* gl)
+{
+ if (!gl->WorkAroundDriverBugs())
+ return true;
+
+ // Some GPUs driver crash when uploading non power of two 565 textures.
+ return gl->Renderer() != GLRenderer::Adreno200 &&
+ gl->Renderer() != GLRenderer::Adreno205;
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/GLUploadHelpers.h b/system/graphics/gl/GLUploadHelpers.h
new file mode 100644
index 000000000..866d44adb
--- /dev/null
+++ b/system/graphics/gl/GLUploadHelpers.h
@@ -0,0 +1,84 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLUploadHelpers_h_
+#define GLUploadHelpers_h_
+
+#include "GLDefs.h"
+#include "mozilla/gfx/Types.h"
+#include "nsPoint.h"
+#include "nsRegionFwd.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace gl {
+
+class GLContext;
+
+/**
+ * Uploads image data to an OpenGL texture, initializing the texture
+ * first if necessary.
+ *
+ * \param gl The GL Context to use.
+ * \param aData Start of image data of surface to upload.
+ * Corresponds to the first pixel of the texture.
+ * \param aStride The image data's stride.
+ * \param aFormat The image data's format.
+ * \param aDstRegion Region of the texture to upload.
+ * \param aTexture The OpenGL texture to use. Must refer to a valid texture.
+ * \param aSize The full size of the texture.
+ * \param aOutUploadSize If set, the number of bytes the texture requires will
+ * be returned here.
+ * \param aNeedInit Indicates whether a new texture must be allocated.
+ * \param aTextureUnit The texture unit used temporarily to upload the surface.
+ * This may be overridden, so clients should not rely on
+ * the aTexture being bound to aTextureUnit after this call,
+ * or even on aTextureUnit being active.
+ * \param aTextureTarget The texture target to use.
+ * \return Surface format of this texture.
+ */
+gfx::SurfaceFormat
+UploadImageDataToTexture(GLContext* gl,
+ unsigned char* aData,
+ int32_t aStride,
+ gfx::SurfaceFormat aFormat,
+ const nsIntRegion& aDstRegion,
+ GLuint aTexture,
+ const gfx::IntSize& aSize,
+ size_t* aOutUploadSize = nullptr,
+ bool aNeedInit = false,
+ GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
+ GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
+
+/**
+ * Convenience wrapper around UploadImageDataToTexture for
+ * gfx::DataSourceSurface's.
+ *
+ * \param aSurface The surface from which to upload image data.
+ * \param aSrcPoint Offset into aSurface where this texture's data begins.
+ */
+gfx::SurfaceFormat
+UploadSurfaceToTexture(GLContext* gl,
+ gfx::DataSourceSurface* aSurface,
+ const nsIntRegion& aDstRegion,
+ GLuint aTexture,
+ const gfx::IntSize& aSize,
+ size_t* aOutUploadSize = nullptr,
+ bool aNeedInit = false,
+ const gfx::IntPoint& aSrcPoint = gfx::IntPoint(0, 0),
+ GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
+ GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
+
+bool CanUploadSubTextures(GLContext* gl);
+bool CanUploadNonPowerOfTwo(GLContext* gl);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/gl/GLXLibrary.h b/system/graphics/gl/GLXLibrary.h
new file mode 100644
index 000000000..d584b3b41
--- /dev/null
+++ b/system/graphics/gl/GLXLibrary.h
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GLXLIBRARY_H
+#define GFX_GLXLIBRARY_H
+
+#include "GLContextTypes.h"
+typedef realGLboolean GLboolean;
+
+// stuff from glx.h
+#include "X11/Xlib.h"
+typedef struct __GLXcontextRec* GLXContext;
+typedef XID GLXPixmap;
+typedef XID GLXDrawable;
+/* GLX 1.3 and later */
+typedef struct __GLXFBConfigRec* GLXFBConfig;
+typedef XID GLXFBConfigID;
+typedef XID GLXContextID;
+typedef XID GLXWindow;
+typedef XID GLXPbuffer;
+// end of stuff from glx.h
+#include "prenv.h"
+
+struct PRLibrary;
+class gfxASurface;
+
+namespace mozilla {
+namespace gl {
+
+class GLXLibrary
+{
+public:
+ constexpr GLXLibrary()
+ : xDestroyContextInternal(nullptr)
+ , xMakeCurrentInternal(nullptr)
+ , xGetCurrentContextInternal(nullptr)
+ , xGetProcAddressInternal(nullptr)
+ , xChooseFBConfigInternal(nullptr)
+ , xGetFBConfigsInternal(nullptr)
+ , xCreateNewContextInternal(nullptr)
+ , xGetFBConfigAttribInternal(nullptr)
+ , xSwapBuffersInternal(nullptr)
+ , xQueryExtensionsStringInternal(nullptr)
+ , xGetClientStringInternal(nullptr)
+ , xQueryServerStringInternal(nullptr)
+ , xCreatePixmapInternal(nullptr)
+ , xCreateGLXPixmapWithConfigInternal(nullptr)
+ , xDestroyPixmapInternal(nullptr)
+ , xQueryVersionInternal(nullptr)
+ , xBindTexImageInternal(nullptr)
+ , xReleaseTexImageInternal(nullptr)
+ , xWaitGLInternal(nullptr)
+ , xWaitXInternal(nullptr)
+ , xCreateContextAttribsInternal(nullptr)
+ , xGetVideoSyncInternal(nullptr)
+ , xWaitVideoSyncInternal(nullptr)
+ , xSwapIntervalInternal(nullptr)
+ , mInitialized(false), mTriedInitializing(false)
+ , mUseTextureFromPixmap(false), mDebug(false)
+ , mHasRobustness(false), mHasCreateContextAttribs(false)
+ , mHasVideoSync(false)
+ , mIsATI(false), mIsNVIDIA(false)
+ , mClientIsMesa(false), mGLXMajorVersion(0)
+ , mGLXMinorVersion(0)
+ , mOGLLibrary(nullptr)
+ {}
+
+ void xDestroyContext(Display* display, GLXContext context);
+ Bool xMakeCurrent(Display* display,
+ GLXDrawable drawable,
+ GLXContext context);
+
+ GLXContext xGetCurrentContext();
+ static void* xGetProcAddress(const char* procName);
+ GLXFBConfig* xChooseFBConfig(Display* display,
+ int screen,
+ const int* attrib_list,
+ int* nelements);
+ GLXFBConfig* xGetFBConfigs(Display* display,
+ int screen,
+ int* nelements);
+ GLXContext xCreateNewContext(Display* display,
+ GLXFBConfig config,
+ int render_type,
+ GLXContext share_list,
+ Bool direct);
+ int xGetFBConfigAttrib(Display* display,
+ GLXFBConfig config,
+ int attribute,
+ int* value);
+ void xSwapBuffers(Display* display, GLXDrawable drawable);
+ const char* xQueryExtensionsString(Display* display,
+ int screen);
+ const char* xGetClientString(Display* display,
+ int screen);
+ const char* xQueryServerString(Display* display,
+ int screen, int name);
+ GLXPixmap xCreatePixmap(Display* display,
+ GLXFBConfig config,
+ Pixmap pixmap,
+ const int* attrib_list);
+ GLXPixmap xCreateGLXPixmapWithConfig(Display* display,
+ GLXFBConfig config,
+ Pixmap pixmap);
+ void xDestroyPixmap(Display* display, GLXPixmap pixmap);
+ Bool xQueryVersion(Display* display,
+ int* major,
+ int* minor);
+ void xBindTexImage(Display* display,
+ GLXDrawable drawable,
+ int buffer,
+ const int* attrib_list);
+ void xReleaseTexImage(Display* display,
+ GLXDrawable drawable,
+ int buffer);
+ void xWaitGL();
+ void xWaitX();
+
+ GLXContext xCreateContextAttribs(Display* display,
+ GLXFBConfig config,
+ GLXContext share_list,
+ Bool direct,
+ const int* attrib_list);
+
+ int xGetVideoSync(unsigned int* count);
+ int xWaitVideoSync(int divisor, int remainder, unsigned int* count);
+ void xSwapInterval(Display* dpy, GLXDrawable drawable, int interval);
+
+ bool EnsureInitialized();
+
+ GLXPixmap CreatePixmap(gfxASurface* aSurface);
+ void DestroyPixmap(Display* aDisplay, GLXPixmap aPixmap);
+ void BindTexImage(Display* aDisplay, GLXPixmap aPixmap);
+ void ReleaseTexImage(Display* aDisplay, GLXPixmap aPixmap);
+ void UpdateTexImage(Display* aDisplay, GLXPixmap aPixmap);
+
+ bool UseTextureFromPixmap() { return mUseTextureFromPixmap; }
+ bool HasRobustness() { return mHasRobustness; }
+ bool HasCreateContextAttribs() { return mHasCreateContextAttribs; }
+ bool SupportsTextureFromPixmap(gfxASurface* aSurface);
+ bool SupportsVideoSync();
+ bool SupportsSwapControl() const { return bool(xSwapIntervalInternal); }
+ bool IsATI() { return mIsATI; }
+ bool IsMesa() { return mClientIsMesa; }
+ bool GLXVersionCheck(int aMajor, int aMinor);
+
+private:
+
+ typedef void (GLAPIENTRY * PFNGLXDESTROYCONTEXTPROC) (Display*,
+ GLXContext);
+ PFNGLXDESTROYCONTEXTPROC xDestroyContextInternal;
+ typedef Bool (GLAPIENTRY * PFNGLXMAKECURRENTPROC) (Display*,
+ GLXDrawable,
+ GLXContext);
+ PFNGLXMAKECURRENTPROC xMakeCurrentInternal;
+ typedef GLXContext (GLAPIENTRY * PFNGLXGETCURRENTCONTEXT) ();
+ PFNGLXGETCURRENTCONTEXT xGetCurrentContextInternal;
+ typedef void* (GLAPIENTRY * PFNGLXGETPROCADDRESSPROC) (const char*);
+ PFNGLXGETPROCADDRESSPROC xGetProcAddressInternal;
+ typedef GLXFBConfig* (GLAPIENTRY * PFNGLXCHOOSEFBCONFIG) (Display*,
+ int,
+ const int*,
+ int*);
+ PFNGLXCHOOSEFBCONFIG xChooseFBConfigInternal;
+ typedef GLXFBConfig* (GLAPIENTRY * PFNGLXGETFBCONFIGS) (Display*,
+ int,
+ int*);
+ PFNGLXGETFBCONFIGS xGetFBConfigsInternal;
+ typedef GLXContext (GLAPIENTRY * PFNGLXCREATENEWCONTEXT) (Display*,
+ GLXFBConfig,
+ int,
+ GLXContext,
+ Bool);
+ PFNGLXCREATENEWCONTEXT xCreateNewContextInternal;
+ typedef int (GLAPIENTRY * PFNGLXGETFBCONFIGATTRIB) (Display*,
+ GLXFBConfig,
+ int,
+ int*);
+ PFNGLXGETFBCONFIGATTRIB xGetFBConfigAttribInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXSWAPBUFFERS) (Display*,
+ GLXDrawable);
+ PFNGLXSWAPBUFFERS xSwapBuffersInternal;
+ typedef const char* (GLAPIENTRY * PFNGLXQUERYEXTENSIONSSTRING) (Display*,
+ int);
+ PFNGLXQUERYEXTENSIONSSTRING xQueryExtensionsStringInternal;
+ typedef const char* (GLAPIENTRY * PFNGLXGETCLIENTSTRING) (Display*,
+ int);
+ PFNGLXGETCLIENTSTRING xGetClientStringInternal;
+ typedef const char* (GLAPIENTRY * PFNGLXQUERYSERVERSTRING) (Display*,
+ int,
+ int);
+ PFNGLXQUERYSERVERSTRING xQueryServerStringInternal;
+
+ typedef GLXPixmap (GLAPIENTRY * PFNGLXCREATEPIXMAP) (Display*,
+ GLXFBConfig,
+ Pixmap,
+ const int*);
+ PFNGLXCREATEPIXMAP xCreatePixmapInternal;
+ typedef GLXPixmap (GLAPIENTRY * PFNGLXCREATEGLXPIXMAPWITHCONFIG)
+ (Display*,
+ GLXFBConfig,
+ Pixmap);
+ PFNGLXCREATEGLXPIXMAPWITHCONFIG xCreateGLXPixmapWithConfigInternal;
+ typedef void (GLAPIENTRY * PFNGLXDESTROYPIXMAP) (Display*,
+ GLXPixmap);
+ PFNGLXDESTROYPIXMAP xDestroyPixmapInternal;
+ typedef Bool (GLAPIENTRY * PFNGLXQUERYVERSION) (Display*,
+ int*,
+ int*);
+ PFNGLXQUERYVERSION xQueryVersionInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXBINDTEXIMAGE) (Display*,
+ GLXDrawable,
+ int,
+ const int*);
+ PFNGLXBINDTEXIMAGE xBindTexImageInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXRELEASETEXIMAGE) (Display*,
+ GLXDrawable,
+ int);
+ PFNGLXRELEASETEXIMAGE xReleaseTexImageInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXWAITGL) ();
+ PFNGLXWAITGL xWaitGLInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXWAITX) ();
+ PFNGLXWAITGL xWaitXInternal;
+
+ typedef GLXContext (GLAPIENTRY * PFNGLXCREATECONTEXTATTRIBS) (Display*,
+ GLXFBConfig,
+ GLXContext,
+ Bool,
+ const int*);
+ PFNGLXCREATECONTEXTATTRIBS xCreateContextAttribsInternal;
+
+ typedef int (GLAPIENTRY * PFNGLXGETVIDEOSYNCSGI) (unsigned int* count);
+ PFNGLXGETVIDEOSYNCSGI xGetVideoSyncInternal;
+
+ typedef int (GLAPIENTRY * PFNGLXWAITVIDEOSYNCSGI) (int divisor, int remainder, unsigned int* count);
+ PFNGLXWAITVIDEOSYNCSGI xWaitVideoSyncInternal;
+
+ typedef void (GLAPIENTRY * PFNGLXSWAPINTERVALEXT) (Display* dpy, GLXDrawable drawable, int interval);
+ PFNGLXSWAPINTERVALEXT xSwapIntervalInternal;
+
+#ifdef DEBUG
+ void BeforeGLXCall();
+ void AfterGLXCall();
+#endif
+
+ bool mInitialized;
+ bool mTriedInitializing;
+ bool mUseTextureFromPixmap;
+ bool mDebug;
+ bool mHasRobustness;
+ bool mHasCreateContextAttribs;
+ bool mHasVideoSync;
+ bool mIsATI;
+ bool mIsNVIDIA;
+ bool mClientIsMesa;
+ int mGLXMajorVersion;
+ int mGLXMinorVersion;
+ PRLibrary* mOGLLibrary;
+};
+
+// a global GLXLibrary instance
+extern GLXLibrary sGLXLibrary;
+
+} /* namespace gl */
+} /* namespace mozilla */
+#endif /* GFX_GLXLIBRARY_H */
+
diff --git a/system/graphics/gl/GfxTexturesReporter.cpp b/system/graphics/gl/GfxTexturesReporter.cpp
new file mode 100644
index 000000000..82cc85ad4
--- /dev/null
+++ b/system/graphics/gl/GfxTexturesReporter.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+#include <sstream>
+#include "GfxTexturesReporter.h"
+#include "gfxPrefs.h"
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+NS_IMPL_ISUPPORTS(GfxTexturesReporter, nsIMemoryReporter)
+
+Atomic<size_t> GfxTexturesReporter::sAmount(0);
+Atomic<size_t> GfxTexturesReporter::sPeakAmount(0);
+Atomic<size_t> GfxTexturesReporter::sTileWasteAmount(0);
+
+std::string
+FormatBytes(size_t amount)
+{
+ std::stringstream stream;
+ int depth = 0;
+ double val = amount;
+ while (val > 1024) {
+ val /= 1024;
+ depth++;
+ }
+
+ const char* unit;
+ switch(depth) {
+ case 0:
+ unit = "bytes";
+ break;
+ case 1:
+ unit = "KB";
+ break;
+ case 2:
+ unit = "MB";
+ break;
+ case 3:
+ unit = "GB";
+ break;
+ default:
+ unit = "";
+ break;
+ }
+
+ stream << val << " " << unit;
+ return stream.str();
+}
+
+/* static */ void
+GfxTexturesReporter::UpdateAmount(MemoryUse action, size_t amount)
+{
+ if (action == MemoryFreed) {
+ MOZ_RELEASE_ASSERT(amount <= sAmount, "GFX: Current texture usage greater than update amount.");
+ sAmount -= amount;
+
+ if (gfxPrefs::GfxLoggingTextureUsageEnabled()) {
+ printf_stderr("Current texture usage: %s\n", FormatBytes(sAmount).c_str());
+ }
+ } else {
+ sAmount += amount;
+ if (sAmount > sPeakAmount) {
+ sPeakAmount.exchange(sAmount);
+ if (gfxPrefs::GfxLoggingPeakTextureUsageEnabled()) {
+ printf_stderr("Peak texture usage: %s\n", FormatBytes(sPeakAmount).c_str());
+ }
+ }
+ }
+}
diff --git a/system/graphics/gl/GfxTexturesReporter.h b/system/graphics/gl/GfxTexturesReporter.h
new file mode 100644
index 000000000..d2ce145d6
--- /dev/null
+++ b/system/graphics/gl/GfxTexturesReporter.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFXTEXTURESREPORTER_H_
+#define GFXTEXTURESREPORTER_H_
+
+#include "mozilla/Atomics.h"
+#include "nsIMemoryReporter.h"
+#include "GLTypes.h"
+
+namespace mozilla {
+namespace gl {
+
+class GfxTexturesReporter final : public nsIMemoryReporter
+{
+ ~GfxTexturesReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ GfxTexturesReporter()
+ {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount|
+ // being static. Assert this.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ enum MemoryUse {
+ // when memory being allocated is reported to a memory reporter
+ MemoryAllocated,
+ // when memory being freed is reported to a memory reporter
+ MemoryFreed
+ };
+
+ // When memory is used/freed for tile textures, call this method to update
+ // the value reported by this memory reporter.
+ static void UpdateAmount(MemoryUse action, size_t amount);
+
+ static void UpdateWasteAmount(size_t delta) {
+ sTileWasteAmount += delta;
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "gfx-tiles-waste", KIND_OTHER, UNITS_BYTES,
+ int64_t(sTileWasteAmount),
+ "Memory lost due to tiles extending past content boundaries");
+
+ MOZ_COLLECT_REPORT(
+ "gfx-textures", KIND_OTHER, UNITS_BYTES,
+ int64_t(sAmount),
+ "Memory used for storing GL textures.");
+
+ MOZ_COLLECT_REPORT(
+ "gfx-textures-peak", KIND_OTHER, UNITS_BYTES,
+ int64_t(sPeakAmount),
+ "Peak memory used for storing GL textures.");
+
+ return NS_OK;
+ }
+
+private:
+ static Atomic<size_t> sAmount;
+ static Atomic<size_t> sPeakAmount;
+ // Count the amount of memory lost to tile waste
+ static Atomic<size_t> sTileWasteAmount;
+};
+
+class GfxTextureWasteTracker {
+public:
+ GfxTextureWasteTracker()
+ : mBytes(0)
+ {
+ MOZ_COUNT_CTOR(GfxTextureWasteTracker);
+ }
+
+ void Update(int32_t aPixelArea, int32_t aBytesPerPixel) {
+ GfxTexturesReporter::UpdateWasteAmount(-mBytes);
+ mBytes = aPixelArea * aBytesPerPixel;
+ GfxTexturesReporter::UpdateWasteAmount(mBytes);
+ }
+
+ ~GfxTextureWasteTracker() {
+ GfxTexturesReporter::UpdateWasteAmount(-mBytes);
+ MOZ_COUNT_DTOR(GfxTextureWasteTracker);
+ }
+private:
+ GfxTextureWasteTracker(const GfxTextureWasteTracker& aRef);
+
+ int32_t mBytes;
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GFXTEXTURESREPORTER_H_
diff --git a/system/graphics/gl/HeapCopyOfStackArray.h b/system/graphics/gl/HeapCopyOfStackArray.h
new file mode 100644
index 000000000..362c98451
--- /dev/null
+++ b/system/graphics/gl/HeapCopyOfStackArray.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef HEAPCOPYOFSTACKARRAY_H_
+#define HEAPCOPYOFSTACKARRAY_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+
+#include <string.h>
+
+namespace mozilla {
+
+// Takes a stack array and copies it into a heap buffer.
+// Useful to retain the convenience of declaring static arrays, while
+// avoiding passing stack pointers to the GL (see bug 1005658).
+
+template <typename ElemType>
+class HeapCopyOfStackArray
+{
+public:
+ template<size_t N>
+ MOZ_IMPLICIT HeapCopyOfStackArray(ElemType (&array)[N])
+ : mArrayLength(N)
+ , mArrayData(MakeUnique<ElemType[]>(N))
+ {
+ memcpy(mArrayData.get(), &array[0], N * sizeof(ElemType));
+ }
+
+ ElemType* Data() const { return mArrayData.get(); }
+ size_t ArrayLength() const { return mArrayLength; }
+ size_t ByteLength() const { return mArrayLength * sizeof(ElemType); }
+
+private:
+ HeapCopyOfStackArray() = delete;
+ HeapCopyOfStackArray(const HeapCopyOfStackArray&) = delete;
+
+ const size_t mArrayLength;
+ UniquePtr<ElemType[]> const mArrayData;
+};
+
+} // namespace mozilla
+
+#endif // HEAPCOPYOFSTACKARRAY_H_
diff --git a/system/graphics/gl/ScopedGLHelpers.cpp b/system/graphics/gl/ScopedGLHelpers.cpp
new file mode 100644
index 000000000..fade35788
--- /dev/null
+++ b/system/graphics/gl/ScopedGLHelpers.cpp
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/UniquePtr.h"
+
+#include "GLContext.h"
+#include "ScopedGLHelpers.h"
+
+namespace mozilla {
+namespace gl {
+
+#ifdef DEBUG
+bool
+IsContextCurrent(GLContext* gl)
+{
+ return gl->IsCurrent();
+}
+#endif
+
+/* ScopedGLState - Wraps glEnable/glDisable. **********************************/
+
+// Use |newState = true| to enable, |false| to disable.
+ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability, bool aNewState)
+ : ScopedGLWrapper<ScopedGLState>(aGL)
+ , mCapability(aCapability)
+{
+ mOldState = mGL->fIsEnabled(mCapability);
+
+ // Early out if we're already in the right state.
+ if (aNewState == mOldState)
+ return;
+
+ if (aNewState) {
+ mGL->fEnable(mCapability);
+ } else {
+ mGL->fDisable(mCapability);
+ }
+}
+
+ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability)
+ : ScopedGLWrapper<ScopedGLState>(aGL)
+ , mCapability(aCapability)
+{
+ mOldState = mGL->fIsEnabled(mCapability);
+}
+
+void
+ScopedGLState::UnwrapImpl()
+{
+ if (mOldState) {
+ mGL->fEnable(mCapability);
+ } else {
+ mGL->fDisable(mCapability);
+ }
+}
+
+
+/* ScopedBindFramebuffer - Saves and restores with GetUserBoundFB and BindUserFB. */
+
+void
+ScopedBindFramebuffer::Init()
+{
+ if (mGL->IsSupported(GLFeature::split_framebuffer)) {
+ mOldReadFB = mGL->GetReadFB();
+ mOldDrawFB = mGL->GetDrawFB();
+ } else {
+ mOldReadFB = mOldDrawFB = mGL->GetFB();
+ }
+}
+
+ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL)
+ : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
+{
+ Init();
+}
+
+ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint aNewFB)
+ : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
+{
+ Init();
+ mGL->BindFB(aNewFB);
+}
+
+void
+ScopedBindFramebuffer::UnwrapImpl()
+{
+ if (mOldReadFB == mOldDrawFB) {
+ mGL->BindFB(mOldDrawFB);
+ } else {
+ mGL->BindDrawFB(mOldDrawFB);
+ mGL->BindReadFB(mOldReadFB);
+ }
+}
+
+
+/* ScopedBindTextureUnit ******************************************************/
+
+ScopedBindTextureUnit::ScopedBindTextureUnit(GLContext* aGL, GLenum aTexUnit)
+ : ScopedGLWrapper<ScopedBindTextureUnit>(aGL)
+{
+ MOZ_ASSERT(aTexUnit >= LOCAL_GL_TEXTURE0);
+ mGL->GetUIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &mOldTexUnit);
+ mGL->fActiveTexture(aTexUnit);
+}
+
+void
+ScopedBindTextureUnit::UnwrapImpl() {
+ mGL->fActiveTexture(mOldTexUnit);
+}
+
+
+/* ScopedTexture **************************************************************/
+
+ScopedTexture::ScopedTexture(GLContext* aGL)
+ : ScopedGLWrapper<ScopedTexture>(aGL)
+{
+ mGL->fGenTextures(1, &mTexture);
+}
+
+void
+ScopedTexture::UnwrapImpl()
+{
+ mGL->fDeleteTextures(1, &mTexture);
+}
+
+/* ScopedFramebuffer **************************************************************/
+
+ScopedFramebuffer::ScopedFramebuffer(GLContext* aGL)
+ : ScopedGLWrapper<ScopedFramebuffer>(aGL)
+{
+ mGL->fGenFramebuffers(1, &mFB);
+}
+
+void
+ScopedFramebuffer::UnwrapImpl()
+{
+ mGL->fDeleteFramebuffers(1, &mFB);
+}
+
+
+/* ScopedRenderbuffer **************************************************************/
+
+ScopedRenderbuffer::ScopedRenderbuffer(GLContext* aGL)
+ : ScopedGLWrapper<ScopedRenderbuffer>(aGL)
+{
+ mGL->fGenRenderbuffers(1, &mRB);
+}
+
+void
+ScopedRenderbuffer::UnwrapImpl()
+{
+ mGL->fDeleteRenderbuffers(1, &mRB);
+}
+
+/* ScopedBindTexture **********************************************************/
+
+static GLuint
+GetBoundTexture(GLContext* gl, GLenum texTarget)
+{
+ GLenum bindingTarget;
+ switch (texTarget) {
+ case LOCAL_GL_TEXTURE_2D:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D;
+ break;
+
+ case LOCAL_GL_TEXTURE_CUBE_MAP:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_CUBE_MAP;
+ break;
+
+ case LOCAL_GL_TEXTURE_3D:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_3D;
+ break;
+
+ case LOCAL_GL_TEXTURE_2D_ARRAY:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D_ARRAY;
+ break;
+
+ case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_RECTANGLE_ARB;
+ break;
+
+ case LOCAL_GL_TEXTURE_EXTERNAL:
+ bindingTarget = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
+ break;
+
+ default:
+ MOZ_CRASH("bad texTarget");
+ }
+
+ GLuint ret = 0;
+ gl->GetUIntegerv(bindingTarget, &ret);
+ return ret;
+}
+
+ScopedBindTexture::ScopedBindTexture(GLContext* aGL, GLuint aNewTex, GLenum aTarget)
+ : ScopedGLWrapper<ScopedBindTexture>(aGL)
+ , mTarget(aTarget)
+ , mOldTex(GetBoundTexture(aGL, aTarget))
+{
+ mGL->fBindTexture(mTarget, aNewTex);
+}
+
+void
+ScopedBindTexture::UnwrapImpl()
+{
+ mGL->fBindTexture(mTarget, mOldTex);
+}
+
+
+/* ScopedBindRenderbuffer *****************************************************/
+
+void
+ScopedBindRenderbuffer::Init()
+{
+ mOldRB = 0;
+ mGL->GetUIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &mOldRB);
+}
+
+ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL)
+ : ScopedGLWrapper<ScopedBindRenderbuffer>(aGL)
+{
+ Init();
+}
+
+ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL, GLuint aNewRB)
+ : ScopedGLWrapper<ScopedBindRenderbuffer>(aGL)
+{
+ Init();
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, aNewRB);
+}
+
+void
+ScopedBindRenderbuffer::UnwrapImpl()
+{
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOldRB);
+}
+
+
+/* ScopedFramebufferForTexture ************************************************/
+ScopedFramebufferForTexture::ScopedFramebufferForTexture(GLContext* aGL,
+ GLuint aTexture,
+ GLenum aTarget)
+ : ScopedGLWrapper<ScopedFramebufferForTexture>(aGL)
+ , mComplete(false)
+ , mFB(0)
+{
+ mGL->fGenFramebuffers(1, &mFB);
+ ScopedBindFramebuffer autoFB(aGL, mFB);
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ aTarget,
+ aTexture,
+ 0);
+
+ GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ mComplete = true;
+ } else {
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+ }
+}
+
+void ScopedFramebufferForTexture::UnwrapImpl()
+{
+ if (!mFB)
+ return;
+
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+}
+
+
+/* ScopedFramebufferForRenderbuffer *******************************************/
+
+
+ScopedFramebufferForRenderbuffer::ScopedFramebufferForRenderbuffer(GLContext* aGL,
+ GLuint aRB)
+ : ScopedGLWrapper<ScopedFramebufferForRenderbuffer>(aGL)
+ , mComplete(false)
+ , mFB(0)
+{
+ mGL->fGenFramebuffers(1, &mFB);
+ ScopedBindFramebuffer autoFB(aGL, mFB);
+ mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER,
+ aRB);
+
+ GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ mComplete = true;
+ } else {
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+ }
+}
+
+void
+ScopedFramebufferForRenderbuffer::UnwrapImpl()
+{
+ if (!mFB)
+ return;
+
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+}
+
+/* ScopedViewportRect *********************************************************/
+
+ScopedViewportRect::ScopedViewportRect(GLContext* aGL,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height)
+ : ScopedGLWrapper<ScopedViewportRect>(aGL)
+{
+ mGL->fGetIntegerv(LOCAL_GL_VIEWPORT, mSavedViewportRect);
+ mGL->fViewport(x, y, width, height);
+}
+
+void ScopedViewportRect::UnwrapImpl()
+{
+ mGL->fViewport(mSavedViewportRect[0],
+ mSavedViewportRect[1],
+ mSavedViewportRect[2],
+ mSavedViewportRect[3]);
+}
+
+/* ScopedScissorRect **********************************************************/
+
+ScopedScissorRect::ScopedScissorRect(GLContext* aGL,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height)
+ : ScopedGLWrapper<ScopedScissorRect>(aGL)
+{
+ mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
+ mGL->fScissor(x, y, width, height);
+}
+
+ScopedScissorRect::ScopedScissorRect(GLContext* aGL)
+ : ScopedGLWrapper<ScopedScissorRect>(aGL)
+{
+ mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
+}
+
+void ScopedScissorRect::UnwrapImpl()
+{
+ mGL->fScissor(mSavedScissorRect[0],
+ mSavedScissorRect[1],
+ mSavedScissorRect[2],
+ mSavedScissorRect[3]);
+}
+
+/* ScopedVertexAttribPointer **************************************************/
+
+ScopedVertexAttribPointer::ScopedVertexAttribPointer(GLContext* aGL,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ realGLboolean normalized,
+ GLsizei stride,
+ GLuint buffer,
+ const GLvoid* pointer)
+ : ScopedGLWrapper<ScopedVertexAttribPointer>(aGL)
+{
+ WrapImpl(index);
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, buffer);
+ mGL->fVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ mGL->fEnableVertexAttribArray(index);
+}
+
+ScopedVertexAttribPointer::ScopedVertexAttribPointer(GLContext* aGL,
+ GLuint index)
+ : ScopedGLWrapper<ScopedVertexAttribPointer>(aGL)
+{
+ WrapImpl(index);
+}
+
+void
+ScopedVertexAttribPointer::WrapImpl(GLuint index)
+{
+ mAttribIndex = index;
+
+ /*
+ * mGL->fGetVertexAttribiv takes:
+ * VERTEX_ATTRIB_ARRAY_ENABLED
+ * VERTEX_ATTRIB_ARRAY_SIZE,
+ * VERTEX_ATTRIB_ARRAY_STRIDE,
+ * VERTEX_ATTRIB_ARRAY_TYPE,
+ * VERTEX_ATTRIB_ARRAY_NORMALIZED,
+ * VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
+ * CURRENT_VERTEX_ATTRIB
+ *
+ * CURRENT_VERTEX_ATTRIB is vertex shader state. \o/
+ * Others appear to be vertex array state,
+ * or alternatively in the internal vertex array state
+ * for a buffer object.
+ */
+
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &mAttribEnabled);
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &mAttribSize);
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &mAttribStride);
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, &mAttribType);
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &mAttribNormalized);
+ mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &mAttribBufferBinding);
+ mGL->fGetVertexAttribPointerv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &mAttribPointer);
+
+ // Note that uniform values are program state, so we don't need to rebind those.
+
+ mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &mBoundBuffer);
+}
+
+void
+ScopedVertexAttribPointer::UnwrapImpl()
+{
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mAttribBufferBinding);
+ mGL->fVertexAttribPointer(mAttribIndex, mAttribSize, mAttribType, mAttribNormalized, mAttribStride, mAttribPointer);
+ if (mAttribEnabled)
+ mGL->fEnableVertexAttribArray(mAttribIndex);
+ else
+ mGL->fDisableVertexAttribArray(mAttribIndex);
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundBuffer);
+}
+
+ScopedGLDrawState::ScopedGLDrawState(GLContext* aGL)
+ : blend (aGL, LOCAL_GL_BLEND, false)
+ , cullFace (aGL, LOCAL_GL_CULL_FACE, false)
+ , depthTest (aGL, LOCAL_GL_DEPTH_TEST, false)
+ , dither (aGL, LOCAL_GL_DITHER, false)
+ , polyOffsFill(aGL, LOCAL_GL_POLYGON_OFFSET_FILL, false)
+ , sampleAToC (aGL, LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)
+ , sampleCover (aGL, LOCAL_GL_SAMPLE_COVERAGE, false)
+ , scissor (aGL, LOCAL_GL_SCISSOR_TEST, false)
+ , stencil (aGL, LOCAL_GL_STENCIL_TEST, false)
+ , mGL(aGL)
+{
+ mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &boundProgram);
+ mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &boundBuffer);
+ mGL->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &maxAttrib);
+ attrib_enabled = MakeUnique<GLint[]>(maxAttrib);
+
+ for (GLuint i = 0; i < maxAttrib; i++) {
+ mGL->fGetVertexAttribiv(i, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &attrib_enabled[i]);
+ mGL->fDisableVertexAttribArray(i);
+ }
+ // Only Attrib0's client side state affected
+ mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib0_size);
+ mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib0_stride);
+ mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, &attrib0_type);
+ mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &attrib0_normalized);
+ mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &attrib0_bufferBinding);
+ mGL->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &attrib0_pointer);
+ mGL->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
+ mGL->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
+ mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, scissorBox);
+}
+
+ScopedGLDrawState::~ScopedGLDrawState()
+{
+ MOZ_ASSERT(mGL->IsCurrent());
+
+ mGL->fScissor(scissorBox[0], scissorBox[1],
+ scissorBox[2], scissorBox[3]);
+
+ mGL->fViewport(viewport[0], viewport[1],
+ viewport[2], viewport[3]);
+
+ mGL->fColorMask(colorMask[0],
+ colorMask[1],
+ colorMask[2],
+ colorMask[3]);
+
+ for (unsigned int i = 0; i < maxAttrib; i++) {
+ if (attrib_enabled[i])
+ mGL->fEnableVertexAttribArray(i);
+ else
+ mGL->fDisableVertexAttribArray(i);
+ }
+
+
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0_bufferBinding);
+ mGL->fVertexAttribPointer(0,
+ attrib0_size,
+ attrib0_type,
+ attrib0_normalized,
+ attrib0_stride,
+ attrib0_pointer);
+
+ mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, boundBuffer);
+
+ mGL->fUseProgram(boundProgram);
+}
+
+////////////////////////////////////////////////////////////////////////
+// ScopedPackState
+
+static bool
+HasPBOState(const GLContext* gl)
+{
+ return (!gl->IsGLES() || gl->Version() >= 300);
+}
+
+ScopedPackState::ScopedPackState(GLContext* gl)
+ : ScopedGLWrapper<ScopedPackState>(gl)
+{
+ mGL->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &mAlignment);
+
+ if (mAlignment != 4) mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
+
+ if (!HasPBOState(mGL))
+ return;
+
+ mGL->fGetIntegerv(LOCAL_GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&mPixelBuffer);
+ mGL->fGetIntegerv(LOCAL_GL_PACK_ROW_LENGTH, &mRowLength);
+ mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_PIXELS, &mSkipPixels);
+ mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_ROWS, &mSkipRows);
+
+ if (mPixelBuffer != 0) mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
+ if (mRowLength != 0) mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
+ if (mSkipPixels != 0) mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
+ if (mSkipRows != 0) mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
+}
+
+void
+ScopedPackState::UnwrapImpl()
+{
+ mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mAlignment);
+
+ if (!HasPBOState(mGL))
+ return;
+
+ mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mPixelBuffer);
+ mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mRowLength);
+ mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mSkipPixels);
+ mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mSkipRows);
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/ScopedGLHelpers.h b/system/graphics/gl/ScopedGLHelpers.h
new file mode 100644
index 000000000..f0a91c69a
--- /dev/null
+++ b/system/graphics/gl/ScopedGLHelpers.h
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SCOPEDGLHELPERS_H_
+#define SCOPEDGLHELPERS_H_
+
+#include "GLDefs.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+
+#ifdef DEBUG
+bool IsContextCurrent(GLContext* gl);
+#endif
+
+//RAII via CRTP!
+template <class Derived>
+struct ScopedGLWrapper
+{
+private:
+ bool mIsUnwrapped;
+
+protected:
+ GLContext* const mGL;
+
+ explicit ScopedGLWrapper(GLContext* gl)
+ : mIsUnwrapped(false)
+ , mGL(gl)
+ {
+ MOZ_ASSERT(&ScopedGLWrapper<Derived>::Unwrap == &Derived::Unwrap);
+ MOZ_ASSERT(&Derived::UnwrapImpl);
+ MOZ_ASSERT(IsContextCurrent(mGL));
+ }
+
+ virtual ~ScopedGLWrapper() {
+ if (!mIsUnwrapped)
+ Unwrap();
+ }
+
+public:
+ void Unwrap() {
+ MOZ_ASSERT(!mIsUnwrapped);
+ MOZ_ASSERT(IsContextCurrent(mGL));
+
+ Derived* derived = static_cast<Derived*>(this);
+ derived->UnwrapImpl();
+
+ mIsUnwrapped = true;
+ }
+};
+
+// Wraps glEnable/Disable.
+struct ScopedGLState
+ : public ScopedGLWrapper<ScopedGLState>
+{
+ friend struct ScopedGLWrapper<ScopedGLState>;
+
+protected:
+ const GLenum mCapability;
+ bool mOldState;
+
+public:
+ // Use |newState = true| to enable, |false| to disable.
+ ScopedGLState(GLContext* aGL, GLenum aCapability, bool aNewState);
+ // variant that doesn't change state; simply records existing state to be
+ // restored by the destructor
+ ScopedGLState(GLContext* aGL, GLenum aCapability);
+
+protected:
+ void UnwrapImpl();
+};
+
+// Saves and restores with GetUserBoundFB and BindUserFB.
+struct ScopedBindFramebuffer
+ : public ScopedGLWrapper<ScopedBindFramebuffer>
+{
+ friend struct ScopedGLWrapper<ScopedBindFramebuffer>;
+
+protected:
+ GLuint mOldReadFB;
+ GLuint mOldDrawFB;
+
+private:
+ void Init();
+
+public:
+ explicit ScopedBindFramebuffer(GLContext* aGL);
+ ScopedBindFramebuffer(GLContext* aGL, GLuint aNewFB);
+
+protected:
+ void UnwrapImpl();
+};
+
+struct ScopedBindTextureUnit
+ : public ScopedGLWrapper<ScopedBindTextureUnit>
+{
+ friend struct ScopedGLWrapper<ScopedBindTextureUnit>;
+
+protected:
+ GLenum mOldTexUnit;
+
+public:
+ ScopedBindTextureUnit(GLContext* aGL, GLenum aTexUnit);
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedTexture
+ : public ScopedGLWrapper<ScopedTexture>
+{
+ friend struct ScopedGLWrapper<ScopedTexture>;
+
+protected:
+ GLuint mTexture;
+
+public:
+ explicit ScopedTexture(GLContext* aGL);
+ GLuint Texture() { return mTexture; }
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedFramebuffer
+ : public ScopedGLWrapper<ScopedFramebuffer>
+{
+ friend struct ScopedGLWrapper<ScopedFramebuffer>;
+
+protected:
+ GLuint mFB;
+
+public:
+ explicit ScopedFramebuffer(GLContext* aGL);
+ GLuint FB() { return mFB; }
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedRenderbuffer
+ : public ScopedGLWrapper<ScopedRenderbuffer>
+{
+ friend struct ScopedGLWrapper<ScopedRenderbuffer>;
+
+protected:
+ GLuint mRB;
+
+public:
+ explicit ScopedRenderbuffer(GLContext* aGL);
+ GLuint RB() { return mRB; }
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedBindTexture
+ : public ScopedGLWrapper<ScopedBindTexture>
+{
+ friend struct ScopedGLWrapper<ScopedBindTexture>;
+
+protected:
+ const GLenum mTarget;
+ const GLuint mOldTex;
+
+public:
+ ScopedBindTexture(GLContext* aGL, GLuint aNewTex,
+ GLenum aTarget = LOCAL_GL_TEXTURE_2D);
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedBindRenderbuffer
+ : public ScopedGLWrapper<ScopedBindRenderbuffer>
+{
+ friend struct ScopedGLWrapper<ScopedBindRenderbuffer>;
+
+protected:
+ GLuint mOldRB;
+
+private:
+ void Init();
+
+public:
+ explicit ScopedBindRenderbuffer(GLContext* aGL);
+
+ ScopedBindRenderbuffer(GLContext* aGL, GLuint aNewRB);
+
+protected:
+ void UnwrapImpl();
+};
+
+
+struct ScopedFramebufferForTexture
+ : public ScopedGLWrapper<ScopedFramebufferForTexture>
+{
+ friend struct ScopedGLWrapper<ScopedFramebufferForTexture>;
+
+protected:
+ bool mComplete; // True if the framebuffer we create is complete.
+ GLuint mFB;
+
+public:
+ ScopedFramebufferForTexture(GLContext* aGL, GLuint aTexture,
+ GLenum aTarget = LOCAL_GL_TEXTURE_2D);
+
+ bool IsComplete() const {
+ return mComplete;
+ }
+
+ GLuint FB() const {
+ MOZ_ASSERT(IsComplete());
+ return mFB;
+ }
+
+protected:
+ void UnwrapImpl();
+};
+
+struct ScopedFramebufferForRenderbuffer
+ : public ScopedGLWrapper<ScopedFramebufferForRenderbuffer>
+{
+ friend struct ScopedGLWrapper<ScopedFramebufferForRenderbuffer>;
+
+protected:
+ bool mComplete; // True if the framebuffer we create is complete.
+ GLuint mFB;
+
+public:
+ ScopedFramebufferForRenderbuffer(GLContext* aGL, GLuint aRB);
+
+ bool IsComplete() const {
+ return mComplete;
+ }
+
+ GLuint FB() const {
+ return mFB;
+ }
+
+protected:
+ void UnwrapImpl();
+};
+
+struct ScopedViewportRect
+ : public ScopedGLWrapper<ScopedViewportRect>
+{
+ friend struct ScopedGLWrapper<ScopedViewportRect>;
+
+protected:
+ GLint mSavedViewportRect[4];
+
+public:
+ ScopedViewportRect(GLContext* aGL, GLint x, GLint y, GLsizei width, GLsizei height);
+
+protected:
+ void UnwrapImpl();
+};
+
+struct ScopedScissorRect
+ : public ScopedGLWrapper<ScopedScissorRect>
+{
+ friend struct ScopedGLWrapper<ScopedScissorRect>;
+
+protected:
+ GLint mSavedScissorRect[4];
+
+public:
+ ScopedScissorRect(GLContext* aGL, GLint x, GLint y, GLsizei width, GLsizei height);
+ explicit ScopedScissorRect(GLContext* aGL);
+
+protected:
+ void UnwrapImpl();
+};
+
+struct ScopedVertexAttribPointer
+ : public ScopedGLWrapper<ScopedVertexAttribPointer>
+{
+ friend struct ScopedGLWrapper<ScopedVertexAttribPointer>;
+
+protected:
+ GLuint mAttribIndex;
+ GLint mAttribEnabled;
+ GLint mAttribSize;
+ GLint mAttribStride;
+ GLint mAttribType;
+ GLint mAttribNormalized;
+ GLint mAttribBufferBinding;
+ void* mAttribPointer;
+ GLuint mBoundBuffer;
+
+public:
+ ScopedVertexAttribPointer(GLContext* aGL, GLuint index, GLint size, GLenum type, realGLboolean normalized,
+ GLsizei stride, GLuint buffer, const GLvoid* pointer);
+ explicit ScopedVertexAttribPointer(GLContext* aGL, GLuint index);
+
+protected:
+ void WrapImpl(GLuint index);
+ void UnwrapImpl();
+};
+
+struct ScopedGLDrawState
+{
+ explicit ScopedGLDrawState(GLContext* gl);
+ ~ScopedGLDrawState();
+
+ GLuint boundProgram;
+ GLuint boundBuffer;
+
+ ScopedGLState blend;
+ ScopedGLState cullFace;
+ ScopedGLState depthTest;
+ ScopedGLState dither;
+ ScopedGLState polyOffsFill;
+ ScopedGLState sampleAToC;
+ ScopedGLState sampleCover;
+ ScopedGLState scissor;
+ ScopedGLState stencil;
+
+ GLuint maxAttrib;
+ UniquePtr<GLint[]> attrib_enabled;
+ GLint attrib0_size;
+ GLint attrib0_stride;
+ GLint attrib0_type;
+ GLint attrib0_normalized;
+ GLint attrib0_bufferBinding;
+ void* attrib0_pointer;
+
+ realGLboolean colorMask[4];
+ GLint viewport[4];
+ GLint scissorBox[4];
+ GLContext* const mGL;
+};
+
+struct ScopedPackState
+ : public ScopedGLWrapper<ScopedPackState>
+{
+ friend struct ScopedGLWrapper<ScopedPackState>;
+
+protected:
+ GLint mAlignment;
+
+ GLuint mPixelBuffer;
+ GLint mRowLength;
+ GLint mSkipPixels;
+ GLint mSkipRows;
+
+public:
+ explicit ScopedPackState(GLContext* gl);
+
+protected:
+ void UnwrapImpl();
+};
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* SCOPEDGLHELPERS_H_ */
diff --git a/system/graphics/gl/SharedSurface.cpp b/system/graphics/gl/SharedSurface.cpp
new file mode 100644
index 000000000..fa1d19172
--- /dev/null
+++ b/system/graphics/gl/SharedSurface.cpp
@@ -0,0 +1,621 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurface.h"
+
+#include "../2d/2D.h"
+#include "GLBlitHelper.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
+#include "nsThreadUtils.h"
+#include "ScopedGLHelpers.h"
+#include "SharedSurfaceGL.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace gl {
+
+/*static*/ void
+SharedSurface::ProdCopy(SharedSurface* src, SharedSurface* dest,
+ SurfaceFactory* factory)
+{
+ GLContext* gl = src->mGL;
+
+ // If `src` begins locked, it must end locked, though we may
+ // temporarily unlock it if we need to.
+ MOZ_ASSERT((src == gl->GetLockedSurface()) == src->IsLocked());
+
+ gl->MakeCurrent();
+
+ if (src->mAttachType == AttachmentType::Screen &&
+ dest->mAttachType == AttachmentType::Screen)
+ {
+ // Here, we actually need to blit through a temp surface, so let's make one.
+ UniquePtr<SharedSurface_Basic> tempSurf;
+ tempSurf = SharedSurface_Basic::Create(gl, factory->mFormats, src->mSize,
+ factory->mCaps.alpha);
+
+ ProdCopy(src, tempSurf.get(), factory);
+ ProdCopy(tempSurf.get(), dest, factory);
+ return;
+ }
+
+ if (src->mAttachType == AttachmentType::Screen) {
+ SharedSurface* origLocked = gl->GetLockedSurface();
+ bool srcNeedsUnlock = false;
+ bool origNeedsRelock = false;
+ if (origLocked != src) {
+ if (origLocked) {
+ origLocked->UnlockProd();
+ origNeedsRelock = true;
+ }
+
+ src->LockProd();
+ srcNeedsUnlock = true;
+ }
+
+ if (dest->mAttachType == AttachmentType::GLTexture) {
+ GLuint destTex = dest->ProdTexture();
+ GLenum destTarget = dest->ProdTextureTarget();
+
+ gl->BlitHelper()->BlitFramebufferToTexture(0, destTex,
+ src->mSize,
+ dest->mSize,
+ destTarget,
+ true);
+ } else if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+ GLuint destRB = dest->ProdRenderbuffer();
+ ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+ gl->BlitHelper()->BlitFramebufferToFramebuffer(0,
+ destWrapper.FB(),
+ src->mSize,
+ dest->mSize,
+ true);
+ } else {
+ MOZ_CRASH("GFX: Unhandled dest->mAttachType 1.");
+ }
+
+ if (srcNeedsUnlock)
+ src->UnlockProd();
+
+ if (origNeedsRelock)
+ origLocked->LockProd();
+
+ return;
+ }
+
+ if (dest->mAttachType == AttachmentType::Screen) {
+ SharedSurface* origLocked = gl->GetLockedSurface();
+ bool destNeedsUnlock = false;
+ bool origNeedsRelock = false;
+ if (origLocked != dest) {
+ if (origLocked) {
+ origLocked->UnlockProd();
+ origNeedsRelock = true;
+ }
+
+ dest->LockProd();
+ destNeedsUnlock = true;
+ }
+
+ if (src->mAttachType == AttachmentType::GLTexture) {
+ GLuint srcTex = src->ProdTexture();
+ GLenum srcTarget = src->ProdTextureTarget();
+
+ gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, 0,
+ src->mSize,
+ dest->mSize,
+ srcTarget,
+ true);
+ } else if (src->mAttachType == AttachmentType::GLRenderbuffer) {
+ GLuint srcRB = src->ProdRenderbuffer();
+ ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
+
+ gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(),
+ 0,
+ src->mSize,
+ dest->mSize,
+ true);
+ } else {
+ MOZ_CRASH("GFX: Unhandled src->mAttachType 2.");
+ }
+
+ if (destNeedsUnlock)
+ dest->UnlockProd();
+
+ if (origNeedsRelock)
+ origLocked->LockProd();
+
+ return;
+ }
+
+ // Alright, done with cases involving Screen types.
+ // Only {src,dest}x{texture,renderbuffer} left.
+
+ if (src->mAttachType == AttachmentType::GLTexture) {
+ GLuint srcTex = src->ProdTexture();
+ GLenum srcTarget = src->ProdTextureTarget();
+
+ if (dest->mAttachType == AttachmentType::GLTexture) {
+ GLuint destTex = dest->ProdTexture();
+ GLenum destTarget = dest->ProdTextureTarget();
+
+ gl->BlitHelper()->BlitTextureToTexture(srcTex, destTex,
+ src->mSize, dest->mSize,
+ srcTarget, destTarget);
+
+ return;
+ }
+
+ if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+ GLuint destRB = dest->ProdRenderbuffer();
+ ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+ gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, destWrapper.FB(),
+ src->mSize, dest->mSize, srcTarget);
+
+ return;
+ }
+
+ MOZ_CRASH("GFX: Unhandled dest->mAttachType 3.");
+ }
+
+ if (src->mAttachType == AttachmentType::GLRenderbuffer) {
+ GLuint srcRB = src->ProdRenderbuffer();
+ ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
+
+ if (dest->mAttachType == AttachmentType::GLTexture) {
+ GLuint destTex = dest->ProdTexture();
+ GLenum destTarget = dest->ProdTextureTarget();
+
+ gl->BlitHelper()->BlitFramebufferToTexture(srcWrapper.FB(), destTex,
+ src->mSize, dest->mSize, destTarget);
+
+ return;
+ }
+
+ if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+ GLuint destRB = dest->ProdRenderbuffer();
+ ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+ gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), destWrapper.FB(),
+ src->mSize, dest->mSize);
+
+ return;
+ }
+
+ MOZ_CRASH("GFX: Unhandled dest->mAttachType 4.");
+ }
+
+ MOZ_CRASH("GFX: Unhandled src->mAttachType 5.");
+}
+
+////////////////////////////////////////////////////////////////////////
+// SharedSurface
+
+
+SharedSurface::SharedSurface(SharedSurfaceType type,
+ AttachmentType attachType,
+ GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ bool canRecycle)
+ : mType(type)
+ , mAttachType(attachType)
+ , mGL(gl)
+ , mSize(size)
+ , mHasAlpha(hasAlpha)
+ , mCanRecycle(canRecycle)
+ , mIsLocked(false)
+ , mIsProducerAcquired(false)
+#ifdef DEBUG
+ , mOwningThread(NS_GetCurrentThread())
+#endif
+{ }
+
+layers::TextureFlags
+SharedSurface::GetTextureFlags() const
+{
+ return layers::TextureFlags::NO_FLAGS;
+}
+
+void
+SharedSurface::LockProd()
+{
+ MOZ_ASSERT(!mIsLocked);
+
+ LockProdImpl();
+
+ mGL->LockSurface(this);
+ mIsLocked = true;
+}
+
+void
+SharedSurface::UnlockProd()
+{
+ if (!mIsLocked)
+ return;
+
+ UnlockProdImpl();
+
+ mGL->UnlockSurface(this);
+ mIsLocked = false;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SurfaceFactory
+
+static void
+ChooseBufferBits(const SurfaceCaps& caps,
+ SurfaceCaps* const out_drawCaps,
+ SurfaceCaps* const out_readCaps)
+{
+ MOZ_ASSERT(out_drawCaps);
+ MOZ_ASSERT(out_readCaps);
+
+ SurfaceCaps screenCaps;
+
+ screenCaps.color = caps.color;
+ screenCaps.alpha = caps.alpha;
+ screenCaps.bpp16 = caps.bpp16;
+
+ screenCaps.depth = caps.depth;
+ screenCaps.stencil = caps.stencil;
+
+ screenCaps.antialias = caps.antialias;
+ screenCaps.preserve = caps.preserve;
+
+ if (caps.antialias) {
+ *out_drawCaps = screenCaps;
+ out_readCaps->Clear();
+
+ // Color caps need to be duplicated in readCaps.
+ out_readCaps->color = caps.color;
+ out_readCaps->alpha = caps.alpha;
+ out_readCaps->bpp16 = caps.bpp16;
+ } else {
+ out_drawCaps->Clear();
+ *out_readCaps = screenCaps;
+ }
+}
+
+SurfaceFactory::SurfaceFactory(SharedSurfaceType type, GLContext* gl,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+ : mType(type)
+ , mGL(gl)
+ , mCaps(caps)
+ , mAllocator(allocator)
+ , mFlags(flags)
+ , mFormats(gl->ChooseGLFormats(caps))
+ , mMutex("SurfaceFactor::mMutex")
+{
+ ChooseBufferBits(mCaps, &mDrawCaps, &mReadCaps);
+}
+
+SurfaceFactory::~SurfaceFactory()
+{
+ while (!mRecycleTotalPool.empty()) {
+ RefPtr<layers::SharedSurfaceTextureClient> tex = *mRecycleTotalPool.begin();
+ StopRecycling(tex);
+ tex->CancelWaitForRecycle();
+ }
+
+ MOZ_RELEASE_ASSERT(mRecycleTotalPool.empty(),"GFX: Surface recycle pool not empty.");
+
+ // If we mRecycleFreePool.clear() before StopRecycling(), we may try to recycle it,
+ // fail, call StopRecycling(), then return here and call it again.
+ mRecycleFreePool.clear();
+}
+
+already_AddRefed<layers::SharedSurfaceTextureClient>
+SurfaceFactory::NewTexClient(const gfx::IntSize& size)
+{
+ while (!mRecycleFreePool.empty()) {
+ RefPtr<layers::SharedSurfaceTextureClient> cur = mRecycleFreePool.front();
+ mRecycleFreePool.pop();
+
+ if (cur->Surf()->mSize == size) {
+ cur->Surf()->WaitForBufferOwnership();
+ return cur.forget();
+ }
+
+ StopRecycling(cur);
+ }
+
+ UniquePtr<SharedSurface> surf = Move(CreateShared(size));
+ if (!surf)
+ return nullptr;
+
+ RefPtr<layers::SharedSurfaceTextureClient> ret;
+ ret = layers::SharedSurfaceTextureClient::Create(Move(surf), this, mAllocator, mFlags);
+
+ StartRecycling(ret);
+
+ return ret.forget();
+}
+
+void
+SurfaceFactory::StartRecycling(layers::SharedSurfaceTextureClient* tc)
+{
+ tc->SetRecycleCallback(&SurfaceFactory::RecycleCallback, static_cast<void*>(this));
+
+ bool didInsert = mRecycleTotalPool.insert(tc);
+ MOZ_RELEASE_ASSERT(didInsert, "GFX: Shared surface texture client was not inserted to recycle.");
+ mozilla::Unused << didInsert;
+}
+
+void
+SurfaceFactory::StopRecycling(layers::SharedSurfaceTextureClient* tc)
+{
+ MutexAutoLock autoLock(mMutex);
+ // Must clear before releasing ref.
+ tc->ClearRecycleCallback();
+
+ bool didErase = mRecycleTotalPool.erase(tc);
+ MOZ_RELEASE_ASSERT(didErase, "GFX: Shared texture surface client was not erased.");
+ mozilla::Unused << didErase;
+}
+
+/*static*/ void
+SurfaceFactory::RecycleCallback(layers::TextureClient* rawTC, void* rawFactory)
+{
+ RefPtr<layers::SharedSurfaceTextureClient> tc;
+ tc = static_cast<layers::SharedSurfaceTextureClient*>(rawTC);
+ SurfaceFactory* factory = static_cast<SurfaceFactory*>(rawFactory);
+
+ if (tc->Surf()->mCanRecycle) {
+ if (factory->Recycle(tc))
+ return;
+ }
+
+ // Did not recover the tex client. End the (re)cycle!
+ factory->StopRecycling(tc);
+}
+
+bool
+SurfaceFactory::Recycle(layers::SharedSurfaceTextureClient* texClient)
+{
+ MOZ_ASSERT(texClient);
+ MutexAutoLock autoLock(mMutex);
+
+ if (mRecycleFreePool.size() >= 2) {
+ return false;
+ }
+
+ RefPtr<layers::SharedSurfaceTextureClient> texClientRef = texClient;
+ mRecycleFreePool.push(texClientRef);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ScopedReadbackFB
+
+ScopedReadbackFB::ScopedReadbackFB(SharedSurface* src)
+ : mGL(src->mGL)
+ , mAutoFB(mGL)
+ , mTempFB(0)
+ , mTempTex(0)
+ , mSurfToUnlock(nullptr)
+ , mSurfToLock(nullptr)
+{
+ switch (src->mAttachType) {
+ case AttachmentType::GLRenderbuffer:
+ {
+ mGL->fGenFramebuffers(1, &mTempFB);
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mTempFB);
+
+ GLuint rb = src->ProdRenderbuffer();
+ mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER, rb);
+ break;
+ }
+ case AttachmentType::GLTexture:
+ {
+ mGL->fGenFramebuffers(1, &mTempFB);
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mTempFB);
+
+ GLuint tex = src->ProdTexture();
+ GLenum texImageTarget = src->ProdTextureTarget();
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ texImageTarget, tex, 0);
+ break;
+ }
+ case AttachmentType::Screen:
+ {
+ SharedSurface* origLocked = mGL->GetLockedSurface();
+ if (origLocked != src) {
+ if (origLocked) {
+ mSurfToLock = origLocked;
+ mSurfToLock->UnlockProd();
+ }
+
+ mSurfToUnlock = src;
+ mSurfToUnlock->LockProd();
+ }
+
+ // TODO: This should just be BindFB, but we don't have
+ // the patch for this yet. (bug 1045955)
+ MOZ_ASSERT(mGL->Screen());
+ mGL->Screen()->BindReadFB_Internal(0);
+ break;
+ }
+ default:
+ MOZ_CRASH("GFX: Unhandled `mAttachType`.");
+ }
+
+ if (src->NeedsIndirectReads()) {
+ mGL->fGenTextures(1, &mTempTex);
+
+ {
+ ScopedBindTexture autoTex(mGL, mTempTex);
+
+ GLenum format = src->mHasAlpha ? LOCAL_GL_RGBA
+ : LOCAL_GL_RGB;
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+ mGL->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, format, 0, 0, width,
+ height, 0);
+ }
+
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D, mTempTex, 0);
+ }
+}
+
+ScopedReadbackFB::~ScopedReadbackFB()
+{
+ if (mTempFB) {
+ mGL->fDeleteFramebuffers(1, &mTempFB);
+ }
+ if (mTempTex) {
+ mGL->fDeleteTextures(1, &mTempTex);
+ }
+ if (mSurfToUnlock) {
+ mSurfToUnlock->UnlockProd();
+ }
+ if (mSurfToLock) {
+ mSurfToLock->LockProd();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class AutoLockBits
+{
+ gfx::DrawTarget* mDT;
+ uint8_t* mLockedBits;
+
+public:
+ explicit AutoLockBits(gfx::DrawTarget* dt)
+ : mDT(dt)
+ , mLockedBits(nullptr)
+ {
+ MOZ_ASSERT(mDT);
+ }
+
+ bool Lock(uint8_t** data, gfx::IntSize* size, int32_t* stride,
+ gfx::SurfaceFormat* format)
+ {
+ if (!mDT->LockBits(data, size, stride, format))
+ return false;
+
+ mLockedBits = *data;
+ return true;
+ }
+
+ ~AutoLockBits() {
+ if (mLockedBits)
+ mDT->ReleaseBits(mLockedBits);
+ }
+};
+
+bool
+ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst)
+{
+ AutoLockBits lock(dst);
+
+ uint8_t* dstBytes;
+ gfx::IntSize dstSize;
+ int32_t dstStride;
+ gfx::SurfaceFormat dstFormat;
+ if (!lock.Lock(&dstBytes, &dstSize, &dstStride, &dstFormat))
+ return false;
+
+ const bool isDstRGBA = (dstFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ dstFormat == gfx::SurfaceFormat::R8G8B8X8);
+ MOZ_ASSERT_IF(!isDstRGBA, dstFormat == gfx::SurfaceFormat::B8G8R8A8 ||
+ dstFormat == gfx::SurfaceFormat::B8G8R8X8);
+
+ size_t width = src->mSize.width;
+ size_t height = src->mSize.height;
+ MOZ_ASSERT(width == (size_t)dstSize.width);
+ MOZ_ASSERT(height == (size_t)dstSize.height);
+
+ GLenum readGLFormat;
+ GLenum readType;
+
+ {
+ ScopedReadbackFB autoReadback(src);
+
+
+ // We have a source FB, now we need a format.
+ GLenum dstGLFormat = isDstRGBA ? LOCAL_GL_BGRA : LOCAL_GL_RGBA;
+ GLenum dstType = LOCAL_GL_UNSIGNED_BYTE;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ GLContext* gl = src->mGL;
+ GetActualReadFormats(gl, dstGLFormat, dstType, &readGLFormat,
+ &readType);
+
+ MOZ_ASSERT(readGLFormat == LOCAL_GL_RGBA ||
+ readGLFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // ReadPixels from the current FB into lockedBits.
+ {
+ size_t alignment = 8;
+ if (dstStride % 4 == 0)
+ alignment = 4;
+
+ ScopedPackState scopedPackState(gl);
+ if (alignment != 4) {
+ gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, alignment);
+ }
+
+ gl->raw_fReadPixels(0, 0, width, height, readGLFormat, readType,
+ dstBytes);
+ }
+ }
+
+ const bool isReadRGBA = readGLFormat == LOCAL_GL_RGBA;
+
+ if (isReadRGBA != isDstRGBA) {
+ for (size_t j = 0; j < height; ++j) {
+ uint8_t* rowItr = dstBytes + j*dstStride;
+ uint8_t* rowEnd = rowItr + 4*width;
+ while (rowItr != rowEnd) {
+ Swap(rowItr[0], rowItr[2]);
+ rowItr += 4;
+ }
+ }
+ }
+
+ return true;
+}
+
+uint32_t
+ReadPixel(SharedSurface* src)
+{
+ GLContext* gl = src->mGL;
+
+ uint32_t pixel;
+
+ ScopedReadbackFB a(src);
+ {
+ ScopedPackState scopedPackState(gl);
+
+ UniquePtr<uint8_t[]> bytes(new uint8_t[4]);
+ gl->raw_fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
+ bytes.get());
+ memcpy(&pixel, bytes.get(), 4);
+ }
+
+ return pixel;
+}
+
+} // namespace gl
+
+} /* namespace mozilla */
diff --git a/system/graphics/gl/SharedSurface.h b/system/graphics/gl/SharedSurface.h
new file mode 100644
index 000000000..0cd21d1b7
--- /dev/null
+++ b/system/graphics/gl/SharedSurface.h
@@ -0,0 +1,333 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* SharedSurface abstracts an actual surface (can be a GL texture, but
+ * not necessarily) that handles sharing.
+ * Its specializations are:
+ * SharedSurface_Basic (client-side bitmap, does readback)
+ * SharedSurface_GLTexture
+ * SharedSurface_EGLImage
+ * SharedSurface_ANGLEShareHandle
+ */
+
+#ifndef SHARED_SURFACE_H_
+#define SHARED_SURFACE_H_
+
+#include <queue>
+#include <set>
+#include <stdint.h>
+
+#include "GLContextTypes.h"
+#include "GLDefs.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
+#include "ScopedGLHelpers.h"
+#include "SurfaceTypes.h"
+
+class nsIThread;
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+class LayersIPCChannel;
+class SharedSurfaceTextureClient;
+enum class TextureFlags : uint32_t;
+class SurfaceDescriptor;
+class TextureClient;
+} // namespace layers
+
+namespace gl {
+
+class GLContext;
+class SurfaceFactory;
+class ShSurfHandle;
+
+class SharedSurface
+{
+public:
+ static void ProdCopy(SharedSurface* src, SharedSurface* dest,
+ SurfaceFactory* factory);
+
+ const SharedSurfaceType mType;
+ const AttachmentType mAttachType;
+ const WeakPtr<GLContext> mGL;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ const bool mCanRecycle;
+protected:
+ bool mIsLocked;
+ bool mIsProducerAcquired;
+#ifdef DEBUG
+ nsIThread* const mOwningThread;
+#endif
+
+ SharedSurface(SharedSurfaceType type,
+ AttachmentType attachType,
+ GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ bool canRecycle);
+
+public:
+ virtual ~SharedSurface() {
+ }
+
+ // Specifies to the TextureClient any flags which
+ // are required by the SharedSurface backend.
+ virtual layers::TextureFlags GetTextureFlags() const;
+
+ bool IsLocked() const { return mIsLocked; }
+ bool IsProducerAcquired() const { return mIsProducerAcquired; }
+
+ // This locks the SharedSurface as the production buffer for the context.
+ // This is needed by backends which use PBuffers and/or EGLSurfaces.
+ void LockProd();
+
+ // Unlocking is harmless if we're already unlocked.
+ void UnlockProd();
+
+protected:
+ virtual void LockProdImpl() = 0;
+ virtual void UnlockProdImpl() = 0;
+
+ virtual void ProducerAcquireImpl() = 0;
+ virtual void ProducerReleaseImpl() = 0;
+ virtual void ProducerReadAcquireImpl() { ProducerAcquireImpl(); }
+ virtual void ProducerReadReleaseImpl() { ProducerReleaseImpl(); }
+
+public:
+ void ProducerAcquire() {
+ MOZ_ASSERT(!mIsProducerAcquired);
+ ProducerAcquireImpl();
+ mIsProducerAcquired = true;
+ }
+ void ProducerRelease() {
+ MOZ_ASSERT(mIsProducerAcquired);
+ ProducerReleaseImpl();
+ mIsProducerAcquired = false;
+ }
+ void ProducerReadAcquire() {
+ MOZ_ASSERT(!mIsProducerAcquired);
+ ProducerReadAcquireImpl();
+ mIsProducerAcquired = true;
+ }
+ void ProducerReadRelease() {
+ MOZ_ASSERT(mIsProducerAcquired);
+ ProducerReadReleaseImpl();
+ mIsProducerAcquired = false;
+ }
+
+ // This function waits until the buffer is no longer being used.
+ // To optimize the performance, some implementaions recycle SharedSurfaces
+ // even when its buffer is still being used.
+ virtual void WaitForBufferOwnership() {}
+
+ // For use when AttachType is correct.
+ virtual GLenum ProdTextureTarget() const {
+ MOZ_ASSERT(mAttachType == AttachmentType::GLTexture);
+ return LOCAL_GL_TEXTURE_2D;
+ }
+
+ virtual GLuint ProdTexture() {
+ MOZ_ASSERT(mAttachType == AttachmentType::GLTexture);
+ MOZ_CRASH("GFX: Did you forget to override this function?");
+ }
+
+ virtual GLuint ProdRenderbuffer() {
+ MOZ_ASSERT(mAttachType == AttachmentType::GLRenderbuffer);
+ MOZ_CRASH("GFX: Did you forget to override this function?");
+ }
+
+ virtual bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border)
+ {
+ return false;
+ }
+
+ virtual bool ReadPixels(GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ GLvoid* pixels)
+ {
+ return false;
+ }
+
+ virtual bool NeedsIndirectReads() const {
+ return false;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) {
+ return false;
+ }
+};
+
+template<typename T>
+class RefSet
+{
+ std::set<T*> mSet;
+
+public:
+ ~RefSet() {
+ clear();
+ }
+
+ auto begin() -> decltype(mSet.begin()) {
+ return mSet.begin();
+ }
+
+ void clear() {
+ for (auto itr = mSet.begin(); itr != mSet.end(); ++itr) {
+ (*itr)->Release();
+ }
+ mSet.clear();
+ }
+
+ bool empty() const {
+ return mSet.empty();
+ }
+
+ bool insert(T* x) {
+ if (mSet.insert(x).second) {
+ x->AddRef();
+ return true;
+ }
+
+ return false;
+ }
+
+ bool erase(T* x) {
+ if (mSet.erase(x)) {
+ x->Release();
+ return true;
+ }
+
+ return false;
+ }
+};
+
+template<typename T>
+class RefQueue
+{
+ std::queue<T*> mQueue;
+
+public:
+ ~RefQueue() {
+ clear();
+ }
+
+ void clear() {
+ while (!empty()) {
+ pop();
+ }
+ }
+
+ bool empty() const {
+ return mQueue.empty();
+ }
+
+ size_t size() const {
+ return mQueue.size();
+ }
+
+ void push(T* x) {
+ mQueue.push(x);
+ x->AddRef();
+ }
+
+ T* front() const {
+ return mQueue.front();
+ }
+
+ void pop() {
+ T* x = mQueue.front();
+ x->Release();
+ mQueue.pop();
+ }
+};
+
+class SurfaceFactory : public SupportsWeakPtr<SurfaceFactory>
+{
+public:
+ // Should use the VIRTUAL version, but it's currently incompatible
+ // with SupportsWeakPtr. (bug 1049278)
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SurfaceFactory)
+
+ const SharedSurfaceType mType;
+ GLContext* const mGL;
+ const SurfaceCaps mCaps;
+ const RefPtr<layers::LayersIPCChannel> mAllocator;
+ const layers::TextureFlags mFlags;
+ const GLFormats mFormats;
+ Mutex mMutex;
+protected:
+ SurfaceCaps mDrawCaps;
+ SurfaceCaps mReadCaps;
+ RefQueue<layers::SharedSurfaceTextureClient> mRecycleFreePool;
+ RefSet<layers::SharedSurfaceTextureClient> mRecycleTotalPool;
+
+ SurfaceFactory(SharedSurfaceType type, GLContext* gl, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+
+public:
+ virtual ~SurfaceFactory();
+
+ const SurfaceCaps& DrawCaps() const {
+ return mDrawCaps;
+ }
+
+ const SurfaceCaps& ReadCaps() const {
+ return mReadCaps;
+ }
+
+protected:
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) = 0;
+
+ void StartRecycling(layers::SharedSurfaceTextureClient* tc);
+ void SetRecycleCallback(layers::SharedSurfaceTextureClient* tc);
+ void StopRecycling(layers::SharedSurfaceTextureClient* tc);
+
+public:
+ UniquePtr<SharedSurface> NewSharedSurface(const gfx::IntSize& size);
+ //already_AddRefed<ShSurfHandle> NewShSurfHandle(const gfx::IntSize& size);
+ already_AddRefed<layers::SharedSurfaceTextureClient> NewTexClient(const gfx::IntSize& size);
+
+ static void RecycleCallback(layers::TextureClient* tc, void* /*closure*/);
+
+ // Auto-deletes surfs of the wrong type.
+ bool Recycle(layers::SharedSurfaceTextureClient* texClient);
+};
+
+class ScopedReadbackFB
+{
+ GLContext* const mGL;
+ ScopedBindFramebuffer mAutoFB;
+ GLuint mTempFB;
+ GLuint mTempTex;
+ SharedSurface* mSurfToUnlock;
+ SharedSurface* mSurfToLock;
+
+public:
+ explicit ScopedReadbackFB(SharedSurface* src);
+ ~ScopedReadbackFB();
+};
+
+bool ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst);
+uint32_t ReadPixel(SharedSurface* src);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // SHARED_SURFACE_H_
diff --git a/system/graphics/gl/SharedSurfaceANGLE.cpp b/system/graphics/gl/SharedSurfaceANGLE.cpp
new file mode 100644
index 000000000..c0175ffbe
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceANGLE.cpp
@@ -0,0 +1,355 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfaceANGLE.h"
+
+#include <d3d11.h>
+#include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+
+namespace mozilla {
+namespace gl {
+
+// Returns `EGL_NO_SURFACE` (`0`) on error.
+static EGLSurface
+CreatePBufferSurface(GLLibraryEGL* egl,
+ EGLDisplay display,
+ EGLConfig config,
+ const gfx::IntSize& size)
+{
+ auto width = size.width;
+ auto height = size.height;
+
+ EGLint attribs[] = {
+ LOCAL_EGL_WIDTH, width,
+ LOCAL_EGL_HEIGHT, height,
+ LOCAL_EGL_NONE
+ };
+
+ DebugOnly<EGLint> preCallErr = egl->fGetError();
+ MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
+ EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
+ EGLint err = egl->fGetError();
+ if (err != LOCAL_EGL_SUCCESS)
+ return 0;
+
+ return surface;
+}
+
+/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
+SharedSurface_ANGLEShareHandle::Create(GLContext* gl, EGLConfig config,
+ const gfx::IntSize& size, bool hasAlpha)
+{
+ GLLibraryEGL* egl = &sEGLLibrary;
+ MOZ_ASSERT(egl);
+ MOZ_ASSERT(egl->IsExtensionSupported(
+ GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
+ MOZ_ASSERT(config);
+
+ EGLDisplay display = egl->Display();
+ EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
+ if (!pbuffer)
+ return nullptr;
+
+ // Declare everything before 'goto's.
+ HANDLE shareHandle = nullptr;
+ bool ok = egl->fQuerySurfacePointerANGLE(display,
+ pbuffer,
+ LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
+ &shareHandle);
+ if (!ok) {
+ egl->fDestroySurface(egl->Display(), pbuffer);
+ return nullptr;
+ }
+ void* opaqueKeyedMutex = nullptr;
+ egl->fQuerySurfacePointerANGLE(display,
+ pbuffer,
+ LOCAL_EGL_DXGI_KEYED_MUTEX_ANGLE,
+ &opaqueKeyedMutex);
+ RefPtr<IDXGIKeyedMutex> keyedMutex = static_cast<IDXGIKeyedMutex*>(opaqueKeyedMutex);
+
+ typedef SharedSurface_ANGLEShareHandle ptrT;
+ UniquePtr<ptrT> ret( new ptrT(gl, egl, size, hasAlpha, pbuffer, shareHandle,
+ keyedMutex) );
+ return Move(ret);
+}
+
+EGLDisplay
+SharedSurface_ANGLEShareHandle::Display()
+{
+ return mEGL->Display();
+}
+
+SharedSurface_ANGLEShareHandle::SharedSurface_ANGLEShareHandle(GLContext* gl,
+ GLLibraryEGL* egl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ EGLSurface pbuffer,
+ HANDLE shareHandle,
+ const RefPtr<IDXGIKeyedMutex>& keyedMutex)
+ : SharedSurface(SharedSurfaceType::EGLSurfaceANGLE,
+ AttachmentType::Screen,
+ gl,
+ size,
+ hasAlpha,
+ true)
+ , mEGL(egl)
+ , mPBuffer(pbuffer)
+ , mShareHandle(shareHandle)
+ , mKeyedMutex(keyedMutex)
+{
+}
+
+
+SharedSurface_ANGLEShareHandle::~SharedSurface_ANGLEShareHandle()
+{
+ mEGL->fDestroySurface(Display(), mPBuffer);
+}
+
+void
+SharedSurface_ANGLEShareHandle::LockProdImpl()
+{
+ GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(mPBuffer);
+}
+
+void
+SharedSurface_ANGLEShareHandle::UnlockProdImpl()
+{
+}
+
+void
+SharedSurface_ANGLEShareHandle::ProducerAcquireImpl()
+{
+ if (mKeyedMutex) {
+ HRESULT hr = mKeyedMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: ANGLE share handle timeout");
+ }
+ }
+}
+
+void
+SharedSurface_ANGLEShareHandle::ProducerReleaseImpl()
+{
+ if (mKeyedMutex) {
+ // XXX: ReleaseSync() has an implicit flush of the D3D commands
+ // whether we need Flush() or not depends on the ANGLE semantics.
+ // For now, we'll just do it
+ mGL->fFlush();
+ mKeyedMutex->ReleaseSync(0);
+ return;
+ }
+ mGL->fFinish();
+}
+
+void
+SharedSurface_ANGLEShareHandle::ProducerReadAcquireImpl()
+{
+ ProducerAcquireImpl();
+}
+
+void
+SharedSurface_ANGLEShareHandle::ProducerReadReleaseImpl()
+{
+ if (mKeyedMutex) {
+ mKeyedMutex->ReleaseSync(0);
+ return;
+ }
+}
+
+bool
+SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ *out_descriptor = layers::SurfaceDescriptorD3D10((WindowsHandle)mShareHandle, format,
+ mSize);
+ return true;
+}
+
+class ScopedLockTexture final
+{
+public:
+ explicit ScopedLockTexture(ID3D11Texture2D* texture, bool* succeeded)
+ : mIsLocked(false)
+ , mTexture(texture)
+ {
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on the main thread to use d3d11 immediate context");
+ MOZ_ASSERT(mTexture);
+ MOZ_ASSERT(succeeded);
+ *succeeded = false;
+
+ HRESULT hr;
+ mTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (mMutex) {
+ hr = mMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: ANGLE scoped lock timeout");
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ return;
+ }
+ }
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return;
+ }
+
+ device->GetImmediateContext(getter_AddRefs(mDeviceContext));
+
+ mTexture->GetDesc(&mDesc);
+ mDesc.BindFlags = 0;
+ mDesc.Usage = D3D11_USAGE_STAGING;
+ mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ mDesc.MiscFlags = 0;
+
+ hr = device->CreateTexture2D(&mDesc, nullptr, getter_AddRefs(mCopiedTexture));
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ mDeviceContext->CopyResource(mCopiedTexture, mTexture);
+
+ hr = mDeviceContext->Map(mCopiedTexture, 0, D3D11_MAP_READ, 0, &mSubresource);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ *succeeded = true;
+ mIsLocked = true;
+ }
+
+ ~ScopedLockTexture()
+ {
+ mDeviceContext->Unmap(mCopiedTexture, 0);
+ if (mMutex) {
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+ mIsLocked = false;
+ }
+
+ bool mIsLocked;
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID3D11Texture2D> mCopiedTexture;
+ RefPtr<IDXGIKeyedMutex> mMutex;
+ RefPtr<ID3D11DeviceContext> mDeviceContext;
+ D3D11_TEXTURE2D_DESC mDesc;
+ D3D11_MAPPED_SUBRESOURCE mSubresource;
+};
+
+bool
+SharedSurface_ANGLEShareHandle::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+ MOZ_ASSERT(out_surface);
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<ID3D11Texture2D> tex;
+ HRESULT hr = device->OpenSharedResource(mShareHandle,
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(tex));
+
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ bool succeeded = false;
+ ScopedLockTexture scopedLock(tex, &succeeded);
+ if (!succeeded) {
+ return false;
+ }
+
+ const uint8_t* data = reinterpret_cast<uint8_t*>(scopedLock.mSubresource.pData);
+ uint32_t srcStride = scopedLock.mSubresource.RowPitch;
+
+ gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+ if (!map.IsMapped()) {
+ return false;
+ }
+
+ if (map.GetStride() == srcStride) {
+ memcpy(map.GetData(), data, out_surface->GetSize().height * map.GetStride());
+ } else {
+ const uint8_t bytesPerPixel = BytesPerPixel(out_surface->GetFormat());
+ for (int32_t i = 0; i < out_surface->GetSize().height; i++) {
+ memcpy(map.GetData() + i * map.GetStride(),
+ data + i * srcStride,
+ bytesPerPixel * out_surface->GetSize().width);
+ }
+ }
+
+ DXGI_FORMAT srcFormat = scopedLock.mDesc.Format;
+ MOZ_ASSERT(srcFormat == DXGI_FORMAT_B8G8R8A8_UNORM ||
+ srcFormat == DXGI_FORMAT_B8G8R8X8_UNORM ||
+ srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
+ bool isSrcRGB = srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ gfx::SurfaceFormat destFormat = out_surface->GetFormat();
+ MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+ destFormat == gfx::SurfaceFormat::B8G8R8A8);
+ bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ destFormat == gfx::SurfaceFormat::R8G8B8A8;
+
+ if (isSrcRGB != isDestRGB) {
+ SwapRAndBComponents(out_surface);
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory
+
+/*static*/ UniquePtr<SurfaceFactory_ANGLEShareHandle>
+SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+{
+ GLLibraryEGL* egl = &sEGLLibrary;
+ if (!egl)
+ return nullptr;
+
+ auto ext = GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle;
+ if (!egl->IsExtensionSupported(ext))
+ return nullptr;
+
+ EGLConfig config = GLContextEGL::Cast(gl)->mConfig;
+
+ typedef SurfaceFactory_ANGLEShareHandle ptrT;
+ UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, egl, config) );
+ return Move(ret);
+}
+
+SurfaceFactory_ANGLEShareHandle::SurfaceFactory_ANGLEShareHandle(GLContext* gl,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags,
+ GLLibraryEGL* egl,
+ EGLConfig config)
+ : SurfaceFactory(SharedSurfaceType::EGLSurfaceANGLE, gl, caps, allocator, flags)
+ , mProdGL(gl)
+ , mEGL(egl)
+ , mConfig(config)
+{ }
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/SharedSurfaceANGLE.h b/system/graphics/gl/SharedSurfaceANGLE.h
new file mode 100644
index 000000000..5e7fe32f3
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceANGLE.h
@@ -0,0 +1,101 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_SURFACE_ANGLE_H_
+#define SHARED_SURFACE_ANGLE_H_
+
+#include <windows.h>
+#include "SharedSurface.h"
+
+struct IDXGIKeyedMutex;
+struct ID3D11Texture2D;
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+class GLLibraryEGL;
+
+class SharedSurface_ANGLEShareHandle
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_ANGLEShareHandle> Create(GLContext* gl,
+ EGLConfig config,
+ const gfx::IntSize& size,
+ bool hasAlpha);
+
+ static SharedSurface_ANGLEShareHandle* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::EGLSurfaceANGLE);
+
+ return (SharedSurface_ANGLEShareHandle*)surf;
+ }
+
+protected:
+ GLLibraryEGL* const mEGL;
+ const EGLSurface mPBuffer;
+public:
+ const HANDLE mShareHandle;
+protected:
+ RefPtr<IDXGIKeyedMutex> mKeyedMutex;
+
+ SharedSurface_ANGLEShareHandle(GLContext* gl,
+ GLLibraryEGL* egl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ EGLSurface pbuffer,
+ HANDLE shareHandle,
+ const RefPtr<IDXGIKeyedMutex>& keyedMutex);
+
+ EGLDisplay Display();
+
+public:
+ virtual ~SharedSurface_ANGLEShareHandle();
+
+ virtual void LockProdImpl() override;
+ virtual void UnlockProdImpl() override;
+
+ virtual void ProducerAcquireImpl() override;
+ virtual void ProducerReleaseImpl() override;
+ virtual void ProducerReadAcquireImpl() override;
+ virtual void ProducerReadReleaseImpl() override;
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
+};
+
+
+
+class SurfaceFactory_ANGLEShareHandle
+ : public SurfaceFactory
+{
+protected:
+ GLContext* const mProdGL;
+ GLLibraryEGL* const mEGL;
+ const EGLConfig mConfig;
+
+public:
+ static UniquePtr<SurfaceFactory_ANGLEShareHandle> Create(GLContext* gl,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+
+protected:
+ SurfaceFactory_ANGLEShareHandle(GLContext* gl, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags, GLLibraryEGL* egl,
+ EGLConfig config);
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+ return SharedSurface_ANGLEShareHandle::Create(mProdGL, mConfig, size, hasAlpha);
+ }
+};
+
+} /* namespace gfx */
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACE_ANGLE_H_ */
diff --git a/system/graphics/gl/SharedSurfaceD3D11Interop.cpp b/system/graphics/gl/SharedSurfaceD3D11Interop.cpp
new file mode 100644
index 000000000..42da52e11
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceD3D11Interop.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfaceD3D11Interop.h"
+
+#include <d3d11.h>
+#include "gfxPrefs.h"
+#include "GLContext.h"
+#include "WGLLibrary.h"
+#include "nsPrintfCString.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureForwarder.h"
+
+namespace mozilla {
+namespace gl {
+
+/*
+Sample Code for WGL_NV_DX_interop2:
+Example: Render to Direct3D 11 backbuffer with openGL:
+
+// create D3D11 device, context and swap chain.
+ID3D11Device *device;
+ID3D11DeviceContext *devCtx;
+IDXGISwapChain *swapChain;
+
+DXGI_SWAP_CHAIN_DESC scd;
+
+<set appropriate swap chain parameters in scd>
+
+hr = D3D11CreateDeviceAndSwapChain(NULL, // pAdapter
+ D3D_DRIVER_TYPE_HARDWARE, // DriverType
+ NULL, // Software
+ 0, // Flags (Do not set D3D11_CREATE_DEVICE_SINGLETHREADED)
+ NULL, // pFeatureLevels
+ 0, // FeatureLevels
+ D3D11_SDK_VERSION, // SDKVersion
+ &scd, // pSwapChainDesc
+ &swapChain, // ppSwapChain
+ &device, // ppDevice
+ NULL, // pFeatureLevel
+ &devCtx); // ppImmediateContext
+
+// Fetch the swapchain backbuffer
+ID3D11Texture2D *dxColorbuffer;
+swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
+
+// Create depth stencil texture
+ID3D11Texture2D *dxDepthBuffer;
+D3D11_TEXTURE2D_DESC depthDesc;
+depthDesc.Usage = D3D11_USAGE_DEFAULT;
+<set other depthDesc parameters appropriately>
+
+// Create Views
+ID3D11RenderTargetView *colorBufferView;
+D3D11_RENDER_TARGET_VIEW_DESC rtd;
+<set rtd parameters appropriately>
+device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
+
+ID3D11DepthStencilView *depthBufferView;
+D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
+<set dsd parameters appropriately>
+device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
+
+// Attach back buffer and depth texture to redertarget for the device.
+devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
+
+// Register D3D11 device with GL
+HANDLE gl_handleD3D;
+gl_handleD3D = wglDXOpenDeviceNV(device);
+
+// register the Direct3D color and depth/stencil buffers as
+// renderbuffers in opengl
+GLuint gl_names[2];
+HANDLE gl_handles[2];
+
+glGenRenderbuffers(2, gl_names);
+
+gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
+ gl_names[0],
+ GL_RENDERBUFFER,
+ WGL_ACCESS_READ_WRITE_NV);
+
+gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
+ gl_names[1],
+ GL_RENDERBUFFER,
+ WGL_ACCESS_READ_WRITE_NV);
+
+// attach the Direct3D buffers to an FBO
+glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, gl_names[0]);
+glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, gl_names[1]);
+glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, gl_names[1]);
+
+while (!done) {
+ <direct3d renders to the render targets>
+
+ // lock the render targets for GL access
+ wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles);
+
+ <opengl renders to the render targets>
+
+ // unlock the render targets
+ wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles);
+
+ <direct3d renders to the render targets and presents
+ the results on the screen>
+}
+*/
+
+////////////////////////////////////////////////////////////////////////////////
+// DXInterop2Device
+
+class DXInterop2Device : public RefCounted<DXInterop2Device>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(DXInterop2Device)
+
+ WGLLibrary* const mWGL;
+ const RefPtr<ID3D11Device> mD3D; // Only needed for lifetime guarantee.
+ const HANDLE mInteropDevice;
+ GLContext* const mGL;
+
+ static already_AddRefed<DXInterop2Device> Open(WGLLibrary* wgl, GLContext* gl)
+ {
+ MOZ_ASSERT(wgl->HasDXInterop2());
+
+ const RefPtr<ID3D11Device> d3d = gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!d3d) {
+ gfxCriticalNote << "DXInterop2Device::Open: Failed to create D3D11 device.";
+ return nullptr;
+ }
+
+ if (!gl->MakeCurrent())
+ return nullptr;
+
+ const auto interopDevice = wgl->fDXOpenDevice(d3d);
+ if (!interopDevice) {
+ gfxCriticalNote << "DXInterop2Device::Open: DXOpenDevice failed.";
+ return nullptr;
+ }
+
+ return MakeAndAddRef<DXInterop2Device>(wgl, d3d, interopDevice, gl);
+ }
+
+ DXInterop2Device(WGLLibrary* wgl, ID3D11Device* d3d, HANDLE interopDevice,
+ GLContext* gl)
+ : mWGL(wgl)
+ , mD3D(d3d)
+ , mInteropDevice(interopDevice)
+ , mGL(gl)
+ { }
+
+ ~DXInterop2Device() {
+ const auto isCurrent = mGL->MakeCurrent();
+
+ if (mWGL->fDXCloseDevice(mInteropDevice))
+ return;
+
+ if (isCurrent) {
+ // That shouldn't have failed.
+ const uint32_t error = GetLastError();
+ const nsPrintfCString errorMessage("wglDXCloseDevice(0x%p) failed:"
+ " GetLastError(): %u\n",
+ mInteropDevice, error);
+ gfxCriticalError() << errorMessage.BeginReading();
+ }
+ }
+
+ HANDLE RegisterObject(void* d3dObject, GLuint name, GLenum type,
+ GLenum access) const
+ {
+ if (!mGL->MakeCurrent())
+ return nullptr;
+
+ const auto ret = mWGL->fDXRegisterObject(mInteropDevice, d3dObject, name, type,
+ access);
+ if (ret)
+ return ret;
+
+ const uint32_t error = GetLastError();
+ const nsPrintfCString errorMessage("wglDXRegisterObject(0x%p, 0x%p, %u, 0x%04x,"
+ " 0x%04x) failed: GetLastError(): %u\n",
+ mInteropDevice, d3dObject, name, type, access,
+ error);
+ gfxCriticalNote << errorMessage.BeginReading();
+ return nullptr;
+ }
+
+ bool UnregisterObject(HANDLE lockHandle) const {
+ const auto isCurrent = mGL->MakeCurrent();
+
+ if (mWGL->fDXUnregisterObject(mInteropDevice, lockHandle))
+ return true;
+
+ if (!isCurrent) {
+ // That shouldn't have failed.
+ const uint32_t error = GetLastError();
+ const nsPrintfCString errorMessage("wglDXUnregisterObject(0x%p, 0x%p) failed:"
+ " GetLastError(): %u\n",
+ mInteropDevice, lockHandle, error);
+ gfxCriticalError() << errorMessage.BeginReading();
+ }
+ return false;
+ }
+
+ bool LockObject(HANDLE lockHandle) const {
+ MOZ_ASSERT(mGL->IsCurrent());
+
+ if (mWGL->fDXLockObjects(mInteropDevice, 1, &lockHandle))
+ return true;
+
+ if (!mGL->MakeCurrent())
+ return false;
+
+ gfxCriticalNote << "wglDXLockObjects called without mGL being current."
+ << " Retrying after MakeCurrent.";
+
+ if (mWGL->fDXLockObjects(mInteropDevice, 1, &lockHandle))
+ return true;
+
+ const uint32_t error = GetLastError();
+ const nsPrintfCString errorMessage("wglDXLockObjects(0x%p, 1, {0x%p}) failed:"
+ " GetLastError(): %u\n",
+ mInteropDevice, lockHandle, error);
+ gfxCriticalError() << errorMessage.BeginReading();
+ return false;
+ }
+
+ bool UnlockObject(HANDLE lockHandle) const {
+ MOZ_ASSERT(mGL->IsCurrent());
+
+ if (mWGL->fDXUnlockObjects(mInteropDevice, 1, &lockHandle))
+ return true;
+
+ if (!mGL->MakeCurrent())
+ return false;
+
+ gfxCriticalNote << "wglDXUnlockObjects called without mGL being current."
+ << " Retrying after MakeCurrent.";
+
+ if (mWGL->fDXUnlockObjects(mInteropDevice, 1, &lockHandle))
+ return true;
+
+ const uint32_t error = GetLastError();
+ const nsPrintfCString errorMessage("wglDXUnlockObjects(0x%p, 1, {0x%p}) failed:"
+ " GetLastError(): %u\n",
+ mInteropDevice, lockHandle, error);
+ gfxCriticalError() << errorMessage.BeginReading();
+ return false;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Shared Surface
+
+/*static*/ UniquePtr<SharedSurface_D3D11Interop>
+SharedSurface_D3D11Interop::Create(DXInterop2Device* interop,
+ GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha)
+{
+ const auto& d3d = interop->mD3D;
+
+ // Create a texture in case we need to readback.
+ DXGI_FORMAT format = hasAlpha ? DXGI_FORMAT_B8G8R8A8_UNORM
+ : DXGI_FORMAT_B8G8R8X8_UNORM;
+ CD3D11_TEXTURE2D_DESC desc(format, size.width, size.height, 1, 1);
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+
+ RefPtr<ID3D11Texture2D> texD3D;
+ auto hr = d3d->CreateTexture2D(&desc, nullptr, getter_AddRefs(texD3D));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to create texture for CanvasLayer!");
+ return nullptr;
+ }
+
+ RefPtr<IDXGIResource> texDXGI;
+ hr = texD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(texDXGI));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open texture for sharing!");
+ return nullptr;
+ }
+
+ HANDLE dxgiHandle;
+ texDXGI->GetSharedHandle(&dxgiHandle);
+
+ ////
+
+ if (!gl->MakeCurrent()) {
+ NS_WARNING("MakeCurrent failed.");
+ return nullptr;
+ }
+
+ GLuint rbGL = 0;
+ gl->fGenRenderbuffers(1, &rbGL);
+ const auto lockHandle = interop->RegisterObject(texD3D, rbGL, LOCAL_GL_RENDERBUFFER,
+ LOCAL_WGL_ACCESS_WRITE_DISCARD_NV);
+ if (!lockHandle) {
+ NS_WARNING("Failed to register D3D object with WGL.");
+ gl->fDeleteRenderbuffers(1, &rbGL);
+ return nullptr;
+ }
+
+ ////
+
+ typedef SharedSurface_D3D11Interop ptrT;
+ UniquePtr<ptrT> ret ( new ptrT(gl, size, hasAlpha, rbGL, interop, lockHandle,
+ texD3D, dxgiHandle) );
+ return Move(ret);
+}
+
+SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha, GLuint rbGL,
+ DXInterop2Device* interop,
+ HANDLE lockHandle,
+ ID3D11Texture2D* texD3D,
+ HANDLE dxgiHandle)
+ : SharedSurface(SharedSurfaceType::DXGLInterop2,
+ AttachmentType::GLRenderbuffer,
+ gl,
+ size,
+ hasAlpha,
+ true)
+ , mProdRB(rbGL)
+ , mInterop(interop)
+ , mLockHandle(lockHandle)
+ , mTexD3D(texD3D)
+ , mDXGIHandle(dxgiHandle)
+ , mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish())
+ , mLockedForGL(false)
+{ }
+
+SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
+{
+ MOZ_ASSERT(!IsProducerAcquired());
+
+ if (!mGL || !mGL->MakeCurrent())
+ return;
+
+ if (!mInterop->UnregisterObject(mLockHandle)) {
+ NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
+ }
+
+ mGL->fDeleteRenderbuffers(1, &mProdRB);
+}
+
+void
+SharedSurface_D3D11Interop::ProducerAcquireImpl()
+{
+ MOZ_ASSERT(!mLockedForGL);
+
+ // Now we have the mutex, we can lock for GL.
+ MOZ_ALWAYS_TRUE( mInterop->LockObject(mLockHandle) );
+
+ mLockedForGL = true;
+}
+
+void
+SharedSurface_D3D11Interop::ProducerReleaseImpl()
+{
+ MOZ_ASSERT(mLockedForGL);
+
+ if (mNeedsFinish) {
+ mGL->fFinish();
+ } else {
+ // We probably don't even need this.
+ mGL->fFlush();
+ }
+ MOZ_ALWAYS_TRUE( mInterop->UnlockObject(mLockHandle) );
+
+ mLockedForGL = false;
+}
+
+bool
+SharedSurface_D3D11Interop::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ const auto format = (mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8);
+ *out_descriptor = layers::SurfaceDescriptorD3D10(WindowsHandle(mDXGIHandle), format,
+ mSize);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Factory
+
+/*static*/ UniquePtr<SurfaceFactory_D3D11Interop>
+SurfaceFactory_D3D11Interop::Create(GLContext* gl, const SurfaceCaps& caps,
+ layers::LayersIPCChannel* allocator,
+ const layers::TextureFlags& flags)
+{
+ WGLLibrary* wgl = &sWGLLib;
+ if (!wgl || !wgl->HasDXInterop2())
+ return nullptr;
+
+ const RefPtr<DXInterop2Device> interop = DXInterop2Device::Open(wgl, gl);
+ if (!interop) {
+ NS_WARNING("Failed to open D3D device for use by WGL.");
+ return nullptr;
+ }
+
+ typedef SurfaceFactory_D3D11Interop ptrT;
+ UniquePtr<ptrT> ret(new ptrT(gl, caps, allocator, flags, interop));
+ return Move(ret);
+}
+
+SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(GLContext* gl,
+ const SurfaceCaps& caps,
+ layers::LayersIPCChannel* allocator,
+ const layers::TextureFlags& flags,
+ DXInterop2Device* interop)
+ : SurfaceFactory(SharedSurfaceType::DXGLInterop2, gl, caps, allocator, flags)
+ , mInterop(interop)
+{ }
+
+SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop()
+{ }
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/SharedSurfaceD3D11Interop.h b/system/graphics/gl/SharedSurfaceD3D11Interop.h
new file mode 100644
index 000000000..dbee50428
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceD3D11Interop.h
@@ -0,0 +1,101 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_SURFACE_D3D11_INTEROP_H_
+#define SHARED_SURFACE_D3D11_INTEROP_H_
+
+#include <windows.h>
+#include <d3d11.h>
+#include "SharedSurface.h"
+
+namespace mozilla {
+namespace gl {
+
+class DXInterop2Device;
+class GLContext;
+class WGLLibrary;
+
+class SharedSurface_D3D11Interop
+ : public SharedSurface
+{
+public:
+ const GLuint mProdRB;
+ const RefPtr<DXInterop2Device> mInterop;
+ const HANDLE mLockHandle;
+ const RefPtr<ID3D11Texture2D> mTexD3D;
+ const HANDLE mDXGIHandle;
+ const bool mNeedsFinish;
+
+protected:
+ bool mLockedForGL;
+
+public:
+ static UniquePtr<SharedSurface_D3D11Interop> Create(DXInterop2Device* interop,
+ GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha);
+
+ static SharedSurface_D3D11Interop* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::DXGLInterop2);
+ return (SharedSurface_D3D11Interop*)surf;
+ }
+
+protected:
+ SharedSurface_D3D11Interop(GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLuint renderbufferGL,
+ DXInterop2Device* interop,
+ HANDLE lockHandle,
+ ID3D11Texture2D* texD3D,
+ HANDLE dxgiHandle);
+
+public:
+ virtual ~SharedSurface_D3D11Interop() override;
+
+ virtual void LockProdImpl() override { }
+ virtual void UnlockProdImpl() override { }
+
+ virtual void ProducerAcquireImpl() override;
+ virtual void ProducerReleaseImpl() override;
+
+ virtual GLuint ProdRenderbuffer() override {
+ return mProdRB;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+};
+
+class SurfaceFactory_D3D11Interop
+ : public SurfaceFactory
+{
+public:
+ const RefPtr<DXInterop2Device> mInterop;
+
+ static UniquePtr<SurfaceFactory_D3D11Interop> Create(GLContext* gl,
+ const SurfaceCaps& caps,
+ layers::LayersIPCChannel* allocator,
+ const layers::TextureFlags& flags);
+
+protected:
+ SurfaceFactory_D3D11Interop(GLContext* gl, const SurfaceCaps& caps,
+ layers::LayersIPCChannel* allocator,
+ const layers::TextureFlags& flags,
+ DXInterop2Device* interop);
+
+public:
+ virtual ~SurfaceFactory_D3D11Interop() override;
+
+protected:
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+ return SharedSurface_D3D11Interop::Create(mInterop, mGL, size, hasAlpha);
+ }
+};
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACE_D3D11_INTEROP_H_ */
diff --git a/system/graphics/gl/SharedSurfaceEGL.cpp b/system/graphics/gl/SharedSurfaceEGL.cpp
new file mode 100644
index 000000000..5538138e4
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceEGL.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfaceEGL.h"
+
+#include "GLBlitHelper.h"
+#include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+#include "GLReadTexImageHelper.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "SharedSurface.h"
+#include "TextureGarbageBin.h"
+
+namespace mozilla {
+namespace gl {
+
+/*static*/ UniquePtr<SharedSurface_EGLImage>
+SharedSurface_EGLImage::Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ EGLContext context)
+{
+ GLLibraryEGL* egl = &sEGLLibrary;
+ MOZ_ASSERT(egl);
+ MOZ_ASSERT(context);
+
+ UniquePtr<SharedSurface_EGLImage> ret;
+
+ if (!HasExtensions(egl, prodGL)) {
+ return Move(ret);
+ }
+
+ MOZ_ALWAYS_TRUE(prodGL->MakeCurrent());
+ GLuint prodTex = CreateTextureForOffscreen(prodGL, formats, size);
+ if (!prodTex) {
+ return Move(ret);
+ }
+
+ EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(uintptr_t(prodTex));
+ EGLImage image = egl->fCreateImage(egl->Display(), context,
+ LOCAL_EGL_GL_TEXTURE_2D, buffer,
+ nullptr);
+ if (!image) {
+ prodGL->fDeleteTextures(1, &prodTex);
+ return Move(ret);
+ }
+
+ ret.reset( new SharedSurface_EGLImage(prodGL, egl, size, hasAlpha,
+ formats, prodTex, image) );
+ return Move(ret);
+}
+
+bool
+SharedSurface_EGLImage::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
+{
+ return egl->HasKHRImageBase() &&
+ egl->IsExtensionSupported(GLLibraryEGL::KHR_gl_texture_2D_image) &&
+ (gl->IsExtensionSupported(GLContext::OES_EGL_image_external) ||
+ gl->IsExtensionSupported(GLContext::OES_EGL_image));
+}
+
+SharedSurface_EGLImage::SharedSurface_EGLImage(GLContext* gl,
+ GLLibraryEGL* egl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ const GLFormats& formats,
+ GLuint prodTex,
+ EGLImage image)
+ : SharedSurface(SharedSurfaceType::EGLImageShare,
+ AttachmentType::GLTexture,
+ gl,
+ size,
+ hasAlpha,
+ false) // Can't recycle, as mSync changes never update TextureHost.
+ , mMutex("SharedSurface_EGLImage mutex")
+ , mEGL(egl)
+ , mFormats(formats)
+ , mProdTex(prodTex)
+ , mImage(image)
+ , mSync(0)
+{}
+
+SharedSurface_EGLImage::~SharedSurface_EGLImage()
+{
+ mEGL->fDestroyImage(Display(), mImage);
+
+ if (mSync) {
+ // We can't call this unless we have the ext, but we will always have
+ // the ext if we have something to destroy.
+ mEGL->fDestroySync(Display(), mSync);
+ mSync = 0;
+ }
+
+ if (!mGL || !mGL->MakeCurrent())
+ return;
+
+ mGL->fDeleteTextures(1, &mProdTex);
+ mProdTex = 0;
+}
+
+layers::TextureFlags
+SharedSurface_EGLImage::GetTextureFlags() const
+{
+ return layers::TextureFlags::DEALLOCATE_CLIENT;
+}
+
+void
+SharedSurface_EGLImage::ProducerReleaseImpl()
+{
+ MutexAutoLock lock(mMutex);
+ mGL->MakeCurrent();
+
+ if (mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
+ mGL->IsExtensionSupported(GLContext::OES_EGL_sync))
+ {
+ if (mSync) {
+ MOZ_RELEASE_ASSERT(false, "GFX: Non-recycleable should not Fence twice.");
+ MOZ_ALWAYS_TRUE( mEGL->fDestroySync(Display(), mSync) );
+ mSync = 0;
+ }
+
+ mSync = mEGL->fCreateSync(Display(),
+ LOCAL_EGL_SYNC_FENCE,
+ nullptr);
+ if (mSync) {
+ mGL->fFlush();
+ return;
+ }
+ }
+
+ MOZ_ASSERT(!mSync);
+ mGL->fFinish();
+}
+
+void
+SharedSurface_EGLImage::ProducerReadAcquireImpl()
+{
+ // Wait on the fence, because presumably we're going to want to read this surface
+ if (mSync) {
+ mEGL->fClientWaitSync(Display(), mSync, 0, LOCAL_EGL_FOREVER);
+ }
+}
+
+EGLDisplay
+SharedSurface_EGLImage::Display() const
+{
+ return mEGL->Display();
+}
+
+bool
+SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ *out_descriptor = layers::EGLImageDescriptor((uintptr_t)mImage, (uintptr_t)mSync,
+ mSize, mHasAlpha);
+ return true;
+}
+
+bool
+SharedSurface_EGLImage::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+ MOZ_ASSERT(out_surface);
+ MOZ_ASSERT(NS_IsMainThread());
+ return sEGLLibrary.ReadbackEGLImage(mImage, out_surface);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/*static*/ UniquePtr<SurfaceFactory_EGLImage>
+SurfaceFactory_EGLImage::Create(GLContext* prodGL, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+{
+ EGLContext context = GLContextEGL::Cast(prodGL)->mContext;
+
+ typedef SurfaceFactory_EGLImage ptrT;
+ UniquePtr<ptrT> ret;
+
+ GLLibraryEGL* egl = &sEGLLibrary;
+ if (SharedSurface_EGLImage::HasExtensions(egl, prodGL)) {
+ ret.reset( new ptrT(prodGL, caps, allocator, flags, context) );
+ }
+
+ return Move(ret);
+}
+
+} // namespace gl
+
+} /* namespace mozilla */
diff --git a/system/graphics/gl/SharedSurfaceEGL.h b/system/graphics/gl/SharedSurfaceEGL.h
new file mode 100644
index 000000000..98d085005
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceEGL.h
@@ -0,0 +1,118 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_SURFACE_EGL_H_
+#define SHARED_SURFACE_EGL_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "SharedSurface.h"
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+class GLLibraryEGL;
+class TextureGarbageBin;
+
+class SharedSurface_EGLImage
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_EGLImage> Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ EGLContext context);
+
+ static SharedSurface_EGLImage* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::EGLImageShare);
+
+ return (SharedSurface_EGLImage*)surf;
+ }
+
+ static bool HasExtensions(GLLibraryEGL* egl, GLContext* gl);
+
+protected:
+ mutable Mutex mMutex;
+ GLLibraryEGL* const mEGL;
+ const GLFormats mFormats;
+ GLuint mProdTex;
+public:
+ const EGLImage mImage;
+protected:
+ EGLSync mSync;
+
+ SharedSurface_EGLImage(GLContext* gl,
+ GLLibraryEGL* egl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ const GLFormats& formats,
+ GLuint prodTex,
+ EGLImage image);
+
+ EGLDisplay Display() const;
+ void UpdateProdTexture(const MutexAutoLock& curAutoLock);
+
+public:
+ virtual ~SharedSurface_EGLImage();
+
+ virtual layers::TextureFlags GetTextureFlags() const override;
+
+ virtual void LockProdImpl() override {}
+ virtual void UnlockProdImpl() override {}
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override;
+
+ virtual void ProducerReadAcquireImpl() override;
+ virtual void ProducerReadReleaseImpl() override {};
+
+ virtual GLuint ProdTexture() override {
+ return mProdTex;
+ }
+
+ // Implementation-specific functions below:
+ // Returns texture and target
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
+};
+
+
+
+class SurfaceFactory_EGLImage
+ : public SurfaceFactory
+{
+public:
+ // Fallible:
+ static UniquePtr<SurfaceFactory_EGLImage> Create(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+
+protected:
+ const EGLContext mContext;
+
+ SurfaceFactory_EGLImage(GLContext* prodGL, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags,
+ EGLContext context)
+ : SurfaceFactory(SharedSurfaceType::EGLImageShare, prodGL, caps, allocator, flags)
+ , mContext(context)
+ { }
+
+public:
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+ return SharedSurface_EGLImage::Create(mGL, mFormats, size, hasAlpha, mContext);
+ }
+};
+
+} // namespace gl
+
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACE_EGL_H_ */
diff --git a/system/graphics/gl/SharedSurfaceGL.cpp b/system/graphics/gl/SharedSurfaceGL.cpp
new file mode 100644
index 000000000..adb4429ae
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceGL.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfaceGL.h"
+
+#include "GLBlitHelper.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/2D.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureForwarder.h"
+
+namespace mozilla {
+namespace gl {
+
+using gfx::IntSize;
+using gfx::SurfaceFormat;
+
+/*static*/ UniquePtr<SharedSurface_Basic>
+SharedSurface_Basic::Create(GLContext* gl,
+ const GLFormats& formats,
+ const IntSize& size,
+ bool hasAlpha)
+{
+ UniquePtr<SharedSurface_Basic> ret;
+ gl->MakeCurrent();
+
+ GLContext::LocalErrorScope localError(*gl);
+ GLuint tex = CreateTextureForOffscreen(gl, formats, size);
+
+ GLenum err = localError.GetError();
+ MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
+ if (err) {
+ gl->fDeleteTextures(1, &tex);
+ return Move(ret);
+ }
+
+ bool ownsTex = true;
+ ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, tex, ownsTex) );
+ return Move(ret);
+}
+
+
+/*static*/ UniquePtr<SharedSurface_Basic>
+SharedSurface_Basic::Wrap(GLContext* gl,
+ const IntSize& size,
+ bool hasAlpha,
+ GLuint tex)
+{
+ bool ownsTex = false;
+ UniquePtr<SharedSurface_Basic> ret( new SharedSurface_Basic(gl, size, hasAlpha, tex,
+ ownsTex) );
+ return Move(ret);
+}
+
+SharedSurface_Basic::SharedSurface_Basic(GLContext* gl,
+ const IntSize& size,
+ bool hasAlpha,
+ GLuint tex,
+ bool ownsTex)
+ : SharedSurface(SharedSurfaceType::Basic,
+ AttachmentType::GLTexture,
+ gl,
+ size,
+ hasAlpha,
+ true)
+ , mTex(tex)
+ , mOwnsTex(ownsTex)
+ , mFB(0)
+{
+ mGL->MakeCurrent();
+ mGL->fGenFramebuffers(1, &mFB);
+
+ ScopedBindFramebuffer autoFB(mGL, mFB);
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D,
+ mTex,
+ 0);
+
+ DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
+}
+
+SharedSurface_Basic::~SharedSurface_Basic()
+{
+ if (!mGL || !mGL->MakeCurrent())
+ return;
+
+ if (mFB)
+ mGL->fDeleteFramebuffers(1, &mFB);
+
+ if (mOwnsTex)
+ mGL->fDeleteTextures(1, &mTex);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+SurfaceFactory_Basic::SurfaceFactory_Basic(GLContext* gl, const SurfaceCaps& caps,
+ const layers::TextureFlags& flags)
+ : SurfaceFactory(SharedSurfaceType::Basic, gl, caps, nullptr, flags)
+{ }
+
+
+////////////////////////////////////////////////////////////////////////
+// SharedSurface_GLTexture
+
+/*static*/ UniquePtr<SharedSurface_GLTexture>
+SharedSurface_GLTexture::Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const IntSize& size,
+ bool hasAlpha)
+{
+ MOZ_ASSERT(prodGL);
+
+ prodGL->MakeCurrent();
+
+ UniquePtr<SharedSurface_GLTexture> ret;
+ GLContext::LocalErrorScope localError(*prodGL);
+
+ GLuint tex = CreateTextureForOffscreen(prodGL, formats, size);
+
+ GLenum err = localError.GetError();
+ MOZ_ASSERT_IF(err, err == LOCAL_GL_OUT_OF_MEMORY);
+ if (err) {
+ prodGL->fDeleteTextures(1, &tex);
+ return Move(ret);
+ }
+
+ ret.reset(new SharedSurface_GLTexture(prodGL, size,
+ hasAlpha, tex));
+ return Move(ret);
+}
+
+SharedSurface_GLTexture::~SharedSurface_GLTexture()
+{
+ if (!mGL->MakeCurrent())
+ return;
+
+ if (mTex) {
+ mGL->fDeleteTextures(1, &mTex);
+ }
+
+ if (mSync) {
+ mGL->fDeleteSync(mSync);
+ }
+}
+
+void
+SharedSurface_GLTexture::ProducerReleaseImpl()
+{
+ mGL->MakeCurrent();
+
+ if (mGL->IsSupported(GLFeature::sync)) {
+ if (mSync) {
+ mGL->fDeleteSync(mSync);
+ mSync = 0;
+ }
+
+ mSync = mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ if (mSync) {
+ mGL->fFlush();
+ return;
+ }
+ }
+ MOZ_ASSERT(!mSync);
+
+ mGL->fFinish();
+}
+
+bool
+SharedSurface_GLTexture::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ *out_descriptor = layers::SurfaceDescriptorSharedGLTexture(ProdTexture(),
+ ProdTextureTarget(),
+ (uintptr_t)mSync,
+ mSize,
+ mHasAlpha);
+
+ // Transfer ownership of the fence to the host
+ mSync = nullptr;
+ return true;
+}
+
+
+} // namespace gl
+
+} /* namespace mozilla */
diff --git a/system/graphics/gl/SharedSurfaceGL.h b/system/graphics/gl/SharedSurfaceGL.h
new file mode 100644
index 000000000..757445271
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceGL.h
@@ -0,0 +1,167 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_SURFACE_GL_H_
+#define SHARED_SURFACE_GL_H_
+
+#include "ScopedGLHelpers.h"
+#include "SharedSurface.h"
+#include "SurfaceTypes.h"
+#include "GLContextTypes.h"
+#include "gfxTypes.h"
+#include "mozilla/Mutex.h"
+
+#include <queue>
+
+namespace mozilla {
+ namespace gl {
+ class GLContext;
+ } // namespace gl
+ namespace gfx {
+ class DataSourceSurface;
+ } // namespace gfx
+} // namespace mozilla
+
+namespace mozilla {
+namespace gl {
+
+// For readback and bootstrapping:
+class SharedSurface_Basic
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_Basic> Create(GLContext* gl,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha);
+
+ static UniquePtr<SharedSurface_Basic> Wrap(GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLuint tex);
+
+ static SharedSurface_Basic* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::Basic);
+
+ return (SharedSurface_Basic*)surf;
+ }
+
+protected:
+ const GLuint mTex;
+ const bool mOwnsTex;
+ GLuint mFB;
+
+ SharedSurface_Basic(GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLuint tex,
+ bool ownsTex);
+
+public:
+ virtual ~SharedSurface_Basic();
+
+ virtual void LockProdImpl() override {}
+ virtual void UnlockProdImpl() override {}
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override {}
+
+ virtual GLuint ProdTexture() override {
+ return mTex;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override {
+ MOZ_CRASH("GFX: ToSurfaceDescriptor");
+ return false;
+ }
+};
+
+class SurfaceFactory_Basic
+ : public SurfaceFactory
+{
+public:
+ SurfaceFactory_Basic(GLContext* gl, const SurfaceCaps& caps,
+ const layers::TextureFlags& flags);
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+ return SharedSurface_Basic::Create(mGL, mFormats, size, hasAlpha);
+ }
+};
+
+
+// Using shared GL textures:
+class SharedSurface_GLTexture
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_GLTexture> Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha);
+
+ static SharedSurface_GLTexture* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::SharedGLTexture);
+
+ return (SharedSurface_GLTexture*)surf;
+ }
+
+protected:
+ const GLuint mTex;
+ GLsync mSync;
+
+ SharedSurface_GLTexture(GLContext* prodGL,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLuint tex)
+ : SharedSurface(SharedSurfaceType::SharedGLTexture,
+ AttachmentType::GLTexture,
+ prodGL,
+ size,
+ hasAlpha, true)
+ , mTex(tex)
+ , mSync(0)
+ {
+ }
+
+public:
+ virtual ~SharedSurface_GLTexture();
+
+ virtual void LockProdImpl() override {}
+ virtual void UnlockProdImpl() override {}
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override;
+
+ virtual GLuint ProdTexture() override {
+ return mTex;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+};
+
+class SurfaceFactory_GLTexture
+ : public SurfaceFactory
+{
+public:
+ SurfaceFactory_GLTexture(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+ : SurfaceFactory(SharedSurfaceType::SharedGLTexture, prodGL, caps, allocator, flags)
+ {
+ }
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+ return SharedSurface_GLTexture::Create(mGL, mFormats, size, hasAlpha);
+ }
+};
+
+} // namespace gl
+
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACE_GL_H_ */
diff --git a/system/graphics/gl/SharedSurfaceGLX.cpp b/system/graphics/gl/SharedSurfaceGLX.cpp
new file mode 100644
index 000000000..69bee24ba
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceGLX.cpp
@@ -0,0 +1,144 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfaceGLX.h"
+#include "gfxXlibSurface.h"
+#include "GLXLibrary.h"
+#include "GLContextProvider.h"
+#include "GLContextGLX.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/gfx/SourceSurfaceCairo.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+namespace gl {
+
+/* static */
+UniquePtr<SharedSurface_GLXDrawable>
+SharedSurface_GLXDrawable::Create(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const gfx::IntSize& size,
+ bool deallocateClient,
+ bool inSameProcess)
+{
+ UniquePtr<SharedSurface_GLXDrawable> ret;
+ Display* display = DefaultXDisplay();
+ Screen* screen = XDefaultScreenOfDisplay(display);
+ Visual* visual = gfxXlibSurface::FindVisual(screen, gfx::SurfaceFormat::A8R8G8B8_UINT32);
+
+ RefPtr<gfxXlibSurface> surf = gfxXlibSurface::Create(screen, visual, size);
+ if (!deallocateClient)
+ surf->ReleasePixmap();
+
+ ret.reset(new SharedSurface_GLXDrawable(prodGL, size, inSameProcess, surf));
+ return Move(ret);
+}
+
+
+SharedSurface_GLXDrawable::SharedSurface_GLXDrawable(GLContext* gl,
+ const gfx::IntSize& size,
+ bool inSameProcess,
+ const RefPtr<gfxXlibSurface>& xlibSurface)
+ : SharedSurface(SharedSurfaceType::GLXDrawable,
+ AttachmentType::Screen,
+ gl,
+ size,
+ true,
+ true)
+ , mXlibSurface(xlibSurface)
+ , mInSameProcess(inSameProcess)
+{}
+
+void
+SharedSurface_GLXDrawable::ProducerReleaseImpl()
+{
+ mGL->MakeCurrent();
+ mGL->fFlush();
+}
+
+void
+SharedSurface_GLXDrawable::LockProdImpl()
+{
+ mGL->Screen()->SetReadBuffer(LOCAL_GL_FRONT);
+ GLContextGLX::Cast(mGL)->OverrideDrawable(mXlibSurface->GetGLXPixmap());
+}
+
+void
+SharedSurface_GLXDrawable::UnlockProdImpl()
+{
+ GLContextGLX::Cast(mGL)->RestoreDrawable();
+}
+
+bool
+SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ if (!mXlibSurface)
+ return false;
+
+ *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
+ return true;
+}
+
+bool
+SharedSurface_GLXDrawable::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+ MOZ_ASSERT(out_surface);
+ RefPtr<gfx::DataSourceSurface> dataSurf =
+ new gfx::DataSourceSurfaceCairo(mXlibSurface->CairoSurface());
+
+ gfx::DataSourceSurface::ScopedMap mapSrc(dataSurf, gfx::DataSourceSurface::READ);
+ if (!mapSrc.IsMapped()) {
+ return false;
+ }
+
+ gfx::DataSourceSurface::ScopedMap mapDest(out_surface, gfx::DataSourceSurface::WRITE);
+ if (!mapDest.IsMapped()) {
+ return false;
+ }
+
+ if (mapDest.GetStride() == mapSrc.GetStride()) {
+ memcpy(mapDest.GetData(),
+ mapSrc.GetData(),
+ out_surface->GetSize().height * mapDest.GetStride());
+ } else {
+ for (int32_t i = 0; i < dataSurf->GetSize().height; i++) {
+ memcpy(mapDest.GetData() + i * mapDest.GetStride(),
+ mapSrc.GetData() + i * mapSrc.GetStride(),
+ std::min(mapSrc.GetStride(), mapDest.GetStride()));
+ }
+ }
+
+ return true;
+}
+
+/* static */
+UniquePtr<SurfaceFactory_GLXDrawable>
+SurfaceFactory_GLXDrawable::Create(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+{
+ MOZ_ASSERT(caps.alpha, "GLX surfaces require an alpha channel!");
+
+ typedef SurfaceFactory_GLXDrawable ptrT;
+ UniquePtr<ptrT> ret(new ptrT(prodGL, caps, allocator,
+ flags & ~layers::TextureFlags::ORIGIN_BOTTOM_LEFT));
+ return Move(ret);
+}
+
+UniquePtr<SharedSurface>
+SurfaceFactory_GLXDrawable::CreateShared(const gfx::IntSize& size)
+{
+ bool deallocateClient = !!(mFlags & layers::TextureFlags::DEALLOCATE_CLIENT);
+ return SharedSurface_GLXDrawable::Create(mGL, mCaps, size, deallocateClient,
+ mAllocator->IsSameProcess());
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/SharedSurfaceGLX.h b/system/graphics/gl/SharedSurfaceGLX.h
new file mode 100644
index 000000000..19a0338b2
--- /dev/null
+++ b/system/graphics/gl/SharedSurfaceGLX.h
@@ -0,0 +1,68 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_SURFACE_GLX_H_
+#define SHARED_SURFACE_GLX_H_
+
+#include "SharedSurface.h"
+#include "mozilla/RefPtr.h"
+
+class gfxXlibSurface;
+
+namespace mozilla {
+namespace gl {
+
+class SharedSurface_GLXDrawable
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_GLXDrawable> Create(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const gfx::IntSize& size,
+ bool deallocateClient,
+ bool inSameProcess);
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override;
+
+ virtual void LockProdImpl() override;
+ virtual void UnlockProdImpl() override;
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
+private:
+ SharedSurface_GLXDrawable(GLContext* gl,
+ const gfx::IntSize& size,
+ bool inSameProcess,
+ const RefPtr<gfxXlibSurface>& xlibSurface);
+
+ RefPtr<gfxXlibSurface> mXlibSurface;
+ bool mInSameProcess;
+};
+
+class SurfaceFactory_GLXDrawable
+ : public SurfaceFactory
+{
+public:
+ static UniquePtr<SurfaceFactory_GLXDrawable> Create(GLContext* prodGL,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override;
+
+private:
+ SurfaceFactory_GLXDrawable(GLContext* prodGL, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+ : SurfaceFactory(SharedSurfaceType::GLXDrawable, prodGL, caps, allocator, flags)
+ { }
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // SHARED_SURFACE_GLX_H_
diff --git a/system/graphics/gl/SkiaGLGlue.cpp b/system/graphics/gl/SkiaGLGlue.cpp
new file mode 100644
index 000000000..eb84934a6
--- /dev/null
+++ b/system/graphics/gl/SkiaGLGlue.cpp
@@ -0,0 +1,327 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "skia/include/gpu/GrContext.h"
+#include "skia/include/gpu/gl/GrGLInterface.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/ThreadLocal.h"
+#include "mozilla/DebugOnly.h"
+
+/* SkPostConfig.h includes windows.h, which includes windef.h
+ * which redefines min/max. We don't want that. */
+#ifdef _WIN32
+#undef min
+#undef max
+#endif
+
+#include "GLContext.h"
+#include "SkiaGLGlue.h"
+
+using mozilla::gl::GLContext;
+using mozilla::gl::GLFeature;
+using mozilla::gl::SkiaGLGlue;
+using mozilla::gfx::DrawTarget;
+
+template<typename R, typename... A>
+static inline GrGLFunction<R (*)(A...)>
+WrapGL(RefPtr<GLContext> aContext, R (GLContext::*aFunc)(A...))
+{
+ return [aContext, aFunc] (A... args) -> R
+ {
+ aContext->MakeCurrent();
+ return (aContext->*aFunc)(args...);
+ };
+}
+
+template<typename R, typename... A>
+static inline GrGLFunction<R (*)(A...)>
+WrapGL(RefPtr<GLContext> aContext, R (*aFunc)(GLContext*, A...))
+{
+ return [aContext, aFunc] (A... args) -> R
+ {
+ aContext->MakeCurrent();
+ return (*aFunc)(aContext, args...);
+ };
+}
+
+// Core GL functions required by Ganesh
+
+static const GLubyte*
+glGetString_mozilla(GLContext* aContext, GrGLenum aName)
+{
+ // GLContext only exposes a OpenGL 2.0 style API, so we have to intercept a bunch
+ // of checks that Ganesh makes to determine which capabilities are present
+ // on the GL implementation and change them to match what GLContext actually exposes.
+
+ if (aName == LOCAL_GL_VERSION) {
+ if (aContext->IsGLES()) {
+ return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
+ } else {
+ return reinterpret_cast<const GLubyte*>("2.0");
+ }
+ } else if (aName == LOCAL_GL_EXTENSIONS) {
+ // Only expose the bare minimum extensions we want to support to ensure a functional Ganesh
+ // as GLContext only exposes certain extensions
+ static bool extensionsStringBuilt = false;
+ static char extensionsString[1024];
+
+ if (!extensionsStringBuilt) {
+ extensionsString[0] = '\0';
+
+ if (aContext->IsGLES()) {
+ // OES is only applicable to GLES2
+ if (aContext->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) {
+ strcat(extensionsString, "GL_OES_packed_depth_stencil ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
+ strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::OES_texture_npot)) {
+ strcat(extensionsString, "GL_OES_texture_npot ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::OES_vertex_array_object)) {
+ strcat(extensionsString, "GL_OES_vertex_array_object ");
+ }
+
+ if (aContext->IsSupported(GLFeature::standard_derivatives)) {
+ strcat(extensionsString, "GL_OES_standard_derivatives ");
+ }
+ } else {
+ if (aContext->IsSupported(GLFeature::framebuffer_object)) {
+ strcat(extensionsString, "GL_ARB_framebuffer_object ");
+ } else if (aContext->IsExtensionSupported(GLContext::EXT_framebuffer_object)) {
+ strcat(extensionsString, "GL_EXT_framebuffer_object ");
+ }
+
+ if (aContext->IsSupported(GLFeature::texture_rg)) {
+ strcat(extensionsString, "GL_ARB_texture_rg ");
+ }
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) {
+ strcat(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
+ strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::EXT_bgra)) {
+ strcat(extensionsString, "GL_EXT_bgra ");
+ }
+
+ if (aContext->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
+ strcat(extensionsString, "GL_EXT_read_format_bgra ");
+ }
+
+ extensionsStringBuilt = true;
+#ifdef DEBUG
+ printf_stderr("Exported SkiaGL extensions: %s\n", extensionsString);
+#endif
+ }
+
+ return reinterpret_cast<const GLubyte*>(extensionsString);
+
+ } else if (aName == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
+ if (aContext->IsGLES()) {
+ return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
+ } else {
+ return reinterpret_cast<const GLubyte*>("1.10");
+ }
+ }
+
+ return aContext->fGetString(aName);
+}
+
+static GrGLInterface* CreateGrGLInterfaceFromGLContext(GLContext* context)
+{
+ GrGLInterface* i = new GrGLInterface();
+
+ context->MakeCurrent();
+
+ // We support both desktop GL and GLES2
+ if (context->IsGLES()) {
+ i->fStandard = kGLES_GrGLStandard;
+ } else {
+ i->fStandard = kGL_GrGLStandard;
+ }
+
+ GrGLFunction<GrGLGetStringProc> getString = WrapGL(context, &glGetString_mozilla);
+ GrGLFunction<GrGLGetIntegervProc> getIntegerv = WrapGL(context, &GLContext::fGetIntegerv);
+
+ GrGLExtensions extensions;
+ if (!extensions.init(i->fStandard, getString, nullptr, getIntegerv)) {
+ delete i;
+ return nullptr;
+ }
+
+ i->fExtensions.swap(&extensions);
+
+ // Core GL functions required by Ganesh
+ i->fFunctions.fActiveTexture = WrapGL(context, &GLContext::fActiveTexture);
+ i->fFunctions.fAttachShader = WrapGL(context, &GLContext::fAttachShader);
+ i->fFunctions.fBindAttribLocation = WrapGL(context, &GLContext::fBindAttribLocation);
+ i->fFunctions.fBindBuffer = WrapGL(context, &GLContext::fBindBuffer);
+ i->fFunctions.fBindFramebuffer = WrapGL(context, &GLContext::fBindFramebuffer);
+ i->fFunctions.fBindRenderbuffer = WrapGL(context, &GLContext::fBindRenderbuffer);
+ i->fFunctions.fBindTexture = WrapGL(context, &GLContext::fBindTexture);
+ i->fFunctions.fBlendFunc = WrapGL(context, &GLContext::fBlendFunc);
+ i->fFunctions.fBlendColor = WrapGL(context, &GLContext::fBlendColor);
+ i->fFunctions.fBlendEquation = WrapGL(context, &GLContext::fBlendEquation);
+ i->fFunctions.fBufferData = WrapGL(context, &GLContext::fBufferData);
+ i->fFunctions.fBufferSubData = WrapGL(context, &GLContext::fBufferSubData);
+ i->fFunctions.fCheckFramebufferStatus = WrapGL(context, &GLContext::fCheckFramebufferStatus);
+ i->fFunctions.fClear = WrapGL(context, &GLContext::fClear);
+ i->fFunctions.fClearColor = WrapGL(context, &GLContext::fClearColor);
+ i->fFunctions.fClearStencil = WrapGL(context, &GLContext::fClearStencil);
+ i->fFunctions.fColorMask = WrapGL(context, &GLContext::fColorMask);
+ i->fFunctions.fCompileShader = WrapGL(context, &GLContext::fCompileShader);
+ i->fFunctions.fCopyTexSubImage2D = WrapGL(context, &GLContext::fCopyTexSubImage2D);
+ i->fFunctions.fCreateProgram = WrapGL(context, &GLContext::fCreateProgram);
+ i->fFunctions.fCreateShader = WrapGL(context, &GLContext::fCreateShader);
+ i->fFunctions.fCullFace = WrapGL(context, &GLContext::fCullFace);
+ i->fFunctions.fDeleteBuffers = WrapGL(context, &GLContext::fDeleteBuffers);
+ i->fFunctions.fDeleteFramebuffers = WrapGL(context, &GLContext::fDeleteFramebuffers);
+ i->fFunctions.fDeleteProgram = WrapGL(context, &GLContext::fDeleteProgram);
+ i->fFunctions.fDeleteRenderbuffers = WrapGL(context, &GLContext::fDeleteRenderbuffers);
+ i->fFunctions.fDeleteShader = WrapGL(context, &GLContext::fDeleteShader);
+ i->fFunctions.fDeleteTextures = WrapGL(context, &GLContext::fDeleteTextures);
+ i->fFunctions.fDepthMask = WrapGL(context, &GLContext::fDepthMask);
+ i->fFunctions.fDisable = WrapGL(context, &GLContext::fDisable);
+ i->fFunctions.fDisableVertexAttribArray = WrapGL(context, &GLContext::fDisableVertexAttribArray);
+ i->fFunctions.fDrawArrays = WrapGL(context, &GLContext::fDrawArrays);
+ i->fFunctions.fDrawElements = WrapGL(context, &GLContext::fDrawElements);
+ i->fFunctions.fDrawRangeElements = WrapGL(context, &GLContext::fDrawRangeElements);
+ i->fFunctions.fEnable = WrapGL(context, &GLContext::fEnable);
+ i->fFunctions.fEnableVertexAttribArray = WrapGL(context, &GLContext::fEnableVertexAttribArray);
+ i->fFunctions.fFinish = WrapGL(context, &GLContext::fFinish);
+ i->fFunctions.fFlush = WrapGL(context, &GLContext::fFlush);
+ i->fFunctions.fFramebufferRenderbuffer = WrapGL(context, &GLContext::fFramebufferRenderbuffer);
+ i->fFunctions.fFramebufferTexture2D = WrapGL(context, &GLContext::fFramebufferTexture2D);
+ i->fFunctions.fFrontFace = WrapGL(context, &GLContext::fFrontFace);
+ i->fFunctions.fGenBuffers = WrapGL(context, &GLContext::fGenBuffers);
+ i->fFunctions.fGenFramebuffers = WrapGL(context, &GLContext::fGenFramebuffers);
+ i->fFunctions.fGenRenderbuffers = WrapGL(context, &GLContext::fGenRenderbuffers);
+ i->fFunctions.fGetFramebufferAttachmentParameteriv = WrapGL(context, &GLContext::fGetFramebufferAttachmentParameteriv);
+ i->fFunctions.fGenTextures = WrapGL(context, &GLContext::fGenTextures);
+ i->fFunctions.fGenerateMipmap = WrapGL(context, &GLContext::fGenerateMipmap);
+ i->fFunctions.fGetBufferParameteriv = WrapGL(context, &GLContext::fGetBufferParameteriv);
+ i->fFunctions.fGetError = WrapGL(context, &GLContext::fGetError);
+ i->fFunctions.fGetIntegerv = getIntegerv;
+ i->fFunctions.fGetProgramInfoLog = WrapGL(context, &GLContext::fGetProgramInfoLog);
+ i->fFunctions.fGetProgramiv = WrapGL(context, &GLContext::fGetProgramiv);
+ i->fFunctions.fGetRenderbufferParameteriv = WrapGL(context, &GLContext::fGetRenderbufferParameteriv);
+ i->fFunctions.fGetShaderInfoLog = WrapGL(context, &GLContext::fGetShaderInfoLog);
+ i->fFunctions.fGetShaderiv = WrapGL(context, &GLContext::fGetShaderiv);
+ i->fFunctions.fGetShaderPrecisionFormat = WrapGL(context, &GLContext::fGetShaderPrecisionFormat);
+ i->fFunctions.fGetString = getString;
+ i->fFunctions.fGetUniformLocation = WrapGL(context, &GLContext::fGetUniformLocation);
+ i->fFunctions.fLineWidth = WrapGL(context, &GLContext::fLineWidth);
+ i->fFunctions.fLinkProgram = WrapGL(context, &GLContext::fLinkProgram);
+ i->fFunctions.fPixelStorei = WrapGL(context, &GLContext::fPixelStorei);
+ i->fFunctions.fReadPixels = WrapGL(context, &GLContext::fReadPixels);
+ i->fFunctions.fRenderbufferStorage = WrapGL(context, &GLContext::fRenderbufferStorage);
+ i->fFunctions.fScissor = WrapGL(context, &GLContext::fScissor);
+ i->fFunctions.fShaderSource = WrapGL(context, &GLContext::fShaderSource);
+ i->fFunctions.fStencilFunc = WrapGL(context, &GLContext::fStencilFunc);
+ i->fFunctions.fStencilMask = WrapGL(context, &GLContext::fStencilMask);
+ i->fFunctions.fStencilOp = WrapGL(context, &GLContext::fStencilOp);
+ i->fFunctions.fTexImage2D = WrapGL(context, &GLContext::fTexImage2D);
+ i->fFunctions.fTexParameteri = WrapGL(context, &GLContext::fTexParameteri);
+ i->fFunctions.fTexParameteriv = WrapGL(context, &GLContext::fTexParameteriv);
+ i->fFunctions.fTexSubImage2D = WrapGL(context, &GLContext::fTexSubImage2D);
+ i->fFunctions.fUniform1f = WrapGL(context, &GLContext::fUniform1f);
+ i->fFunctions.fUniform1i = WrapGL(context, &GLContext::fUniform1i);
+ i->fFunctions.fUniform1fv = WrapGL(context, &GLContext::fUniform1fv);
+ i->fFunctions.fUniform1iv = WrapGL(context, &GLContext::fUniform1iv);
+ i->fFunctions.fUniform2f = WrapGL(context, &GLContext::fUniform2f);
+ i->fFunctions.fUniform2i = WrapGL(context, &GLContext::fUniform2i);
+ i->fFunctions.fUniform2fv = WrapGL(context, &GLContext::fUniform2fv);
+ i->fFunctions.fUniform2iv = WrapGL(context, &GLContext::fUniform2iv);
+ i->fFunctions.fUniform3f = WrapGL(context, &GLContext::fUniform3f);
+ i->fFunctions.fUniform3i = WrapGL(context, &GLContext::fUniform3i);
+ i->fFunctions.fUniform3fv = WrapGL(context, &GLContext::fUniform3fv);
+ i->fFunctions.fUniform3iv = WrapGL(context, &GLContext::fUniform3iv);
+ i->fFunctions.fUniform4f = WrapGL(context, &GLContext::fUniform4f);
+ i->fFunctions.fUniform4i = WrapGL(context, &GLContext::fUniform4i);
+ i->fFunctions.fUniform4fv = WrapGL(context, &GLContext::fUniform4fv);
+ i->fFunctions.fUniform4iv = WrapGL(context, &GLContext::fUniform4iv);
+ i->fFunctions.fUniformMatrix2fv = WrapGL(context, &GLContext::fUniformMatrix2fv);
+ i->fFunctions.fUniformMatrix3fv = WrapGL(context, &GLContext::fUniformMatrix3fv);
+ i->fFunctions.fUniformMatrix4fv = WrapGL(context, &GLContext::fUniformMatrix4fv);
+ i->fFunctions.fUseProgram = WrapGL(context, &GLContext::fUseProgram);
+ i->fFunctions.fVertexAttrib1f = WrapGL(context, &GLContext::fVertexAttrib1f);
+ i->fFunctions.fVertexAttrib2fv = WrapGL(context, &GLContext::fVertexAttrib2fv);
+ i->fFunctions.fVertexAttrib3fv = WrapGL(context, &GLContext::fVertexAttrib3fv);
+ i->fFunctions.fVertexAttrib4fv = WrapGL(context, &GLContext::fVertexAttrib4fv);
+ i->fFunctions.fVertexAttribPointer = WrapGL(context, &GLContext::fVertexAttribPointer);
+ i->fFunctions.fViewport = WrapGL(context, &GLContext::fViewport);
+
+ // Required for either desktop OpenGL 2.0 or OpenGL ES 2.0
+ i->fFunctions.fStencilFuncSeparate = WrapGL(context, &GLContext::fStencilFuncSeparate);
+ i->fFunctions.fStencilMaskSeparate = WrapGL(context, &GLContext::fStencilMaskSeparate);
+ i->fFunctions.fStencilOpSeparate = WrapGL(context, &GLContext::fStencilOpSeparate);
+
+ // GLContext supports glMapBuffer
+ i->fFunctions.fMapBuffer = WrapGL(context, &GLContext::fMapBuffer);
+ i->fFunctions.fUnmapBuffer = WrapGL(context, &GLContext::fUnmapBuffer);
+
+ // GLContext supports glRenderbufferStorageMultisample/glBlitFramebuffer
+ i->fFunctions.fRenderbufferStorageMultisample = WrapGL(context, &GLContext::fRenderbufferStorageMultisample);
+ i->fFunctions.fBlitFramebuffer = WrapGL(context, &GLContext::fBlitFramebuffer);
+
+ // GLContext supports glCompressedTexImage2D
+ i->fFunctions.fCompressedTexImage2D = WrapGL(context, &GLContext::fCompressedTexImage2D);
+
+ // GL_OES_vertex_array_object
+ i->fFunctions.fBindVertexArray = WrapGL(context, &GLContext::fBindVertexArray);
+ i->fFunctions.fDeleteVertexArrays = WrapGL(context, &GLContext::fDeleteVertexArrays);
+ i->fFunctions.fGenVertexArrays = WrapGL(context, &GLContext::fGenVertexArrays);
+
+ // Desktop GL
+ i->fFunctions.fGetTexLevelParameteriv = WrapGL(context, &GLContext::fGetTexLevelParameteriv);
+ i->fFunctions.fDrawBuffer = WrapGL(context, &GLContext::fDrawBuffer);
+ i->fFunctions.fReadBuffer = WrapGL(context, &GLContext::fReadBuffer);
+
+ // Desktop OpenGL > 1.5
+ i->fFunctions.fGenQueries = WrapGL(context, &GLContext::fGenQueries);
+ i->fFunctions.fDeleteQueries = WrapGL(context, &GLContext::fDeleteQueries);
+ i->fFunctions.fBeginQuery = WrapGL(context, &GLContext::fBeginQuery);
+ i->fFunctions.fEndQuery = WrapGL(context, &GLContext::fEndQuery);
+ i->fFunctions.fGetQueryiv = WrapGL(context, &GLContext::fGetQueryiv);
+ i->fFunctions.fGetQueryObjectiv = WrapGL(context, &GLContext::fGetQueryObjectiv);
+ i->fFunctions.fGetQueryObjectuiv = WrapGL(context, &GLContext::fGetQueryObjectuiv);
+
+ // Desktop OpenGL > 2.0
+ i->fFunctions.fDrawBuffers = WrapGL(context, &GLContext::fDrawBuffers);
+
+ return i;
+}
+
+SkiaGLGlue::SkiaGLGlue(GLContext* context)
+ : mGLContext(context)
+{
+ mGrGLInterface.reset(CreateGrGLInterfaceFromGLContext(mGLContext));
+ mGrContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)mGrGLInterface.get()));
+}
+
+SkiaGLGlue::~SkiaGLGlue()
+{
+ /*
+ * These members have inter-dependencies, but do not keep each other alive, so
+ * destruction order is very important here: mGrContext uses mGrGLInterface, and
+ * through it, uses mGLContext
+ */
+ mGrContext = nullptr;
+ if (mGrGLInterface) {
+ // Ensure that no references to the GLContext remain, even if the GrContext still lives.
+ mGrGLInterface->fFunctions = GrGLInterface::Functions();
+ mGrGLInterface = nullptr;
+ }
+ mGLContext = nullptr;
+}
diff --git a/system/graphics/gl/SkiaGLGlue.h b/system/graphics/gl/SkiaGLGlue.h
new file mode 100644
index 000000000..4110c0c54
--- /dev/null
+++ b/system/graphics/gl/SkiaGLGlue.h
@@ -0,0 +1,64 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SKIA_GL_GLUE_H_
+#define SKIA_GL_GLUE_H_
+
+#ifdef USE_SKIA_GPU
+
+#include "skia/include/core/SkRefCnt.h"
+#include "mozilla/RefPtr.h"
+
+struct GrGLInterface;
+class GrContext;
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+
+class SkiaGLGlue : public GenericAtomicRefCounted
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SkiaGLGlue)
+ explicit SkiaGLGlue(GLContext* context);
+ GLContext* GetGLContext() const { return mGLContext.get(); }
+ GrContext* GetGrContext() const { return mGrContext.get(); }
+
+protected:
+ virtual ~SkiaGLGlue();
+
+private:
+ RefPtr<GLContext> mGLContext;
+ sk_sp<GrGLInterface> mGrGLInterface;
+ sk_sp<GrContext> mGrContext;
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#else // USE_SKIA_GPU
+
+class GrContext;
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+
+class SkiaGLGlue : public GenericAtomicRefCounted
+{
+public:
+ SkiaGLGlue(GLContext* context);
+ GLContext* GetGLContext() const { return nullptr; }
+ GrContext* GetGrContext() const { return nullptr; }
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // USE_SKIA_GPU
+
+#endif // SKIA_GL_GLUE_H_
diff --git a/system/graphics/gl/SurfaceTypes.cpp b/system/graphics/gl/SurfaceTypes.cpp
new file mode 100644
index 000000000..9c3e8a2e1
--- /dev/null
+++ b/system/graphics/gl/SurfaceTypes.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SurfaceTypes.h"
+
+#include "mozilla/layers/TextureForwarder.h"
+
+namespace mozilla {
+namespace gl {
+
+SurfaceCaps::SurfaceCaps()
+{
+ Clear();
+}
+
+/* These are defined out of line so that we don't need to include
+ * ISurfaceAllocator.h in the header */
+SurfaceCaps::SurfaceCaps(const SurfaceCaps& other) = default;
+SurfaceCaps&
+SurfaceCaps::operator=(const SurfaceCaps& other) = default;
+
+void
+SurfaceCaps::Clear()
+{
+ any = false;
+ color = false;
+ alpha = false;
+ bpp16 = false;
+ depth = false;
+ stencil = false;
+ antialias = false;
+ premultAlpha = true;
+ preserve = false;
+ surfaceAllocator = nullptr;
+}
+
+SurfaceCaps::~SurfaceCaps()
+{
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/SurfaceTypes.h b/system/graphics/gl/SurfaceTypes.h
new file mode 100644
index 000000000..aae21e0a9
--- /dev/null
+++ b/system/graphics/gl/SurfaceTypes.h
@@ -0,0 +1,98 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SURFACE_TYPES_H_
+#define SURFACE_TYPES_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Attributes.h"
+#include <stdint.h>
+
+namespace mozilla {
+namespace layers {
+class LayersIPCChannel;
+} // namespace layers
+
+namespace gl {
+
+struct SurfaceCaps final
+{
+ bool any;
+ bool color, alpha;
+ bool bpp16;
+ bool depth, stencil;
+ bool antialias;
+ bool premultAlpha;
+ bool preserve;
+
+ // The surface allocator that we want to create this
+ // for. May be null.
+ RefPtr<layers::LayersIPCChannel> surfaceAllocator;
+
+ SurfaceCaps();
+ SurfaceCaps(const SurfaceCaps& other);
+ ~SurfaceCaps();
+
+ void Clear();
+
+ SurfaceCaps& operator=(const SurfaceCaps& other);
+
+ // We can't use just 'RGB' here, since it's an ancient Windows macro.
+ static SurfaceCaps ForRGB() {
+ SurfaceCaps caps;
+
+ caps.color = true;
+
+ return caps;
+ }
+
+ static SurfaceCaps ForRGBA() {
+ SurfaceCaps caps;
+
+ caps.color = true;
+ caps.alpha = true;
+
+ return caps;
+ }
+
+ static SurfaceCaps Any() {
+ SurfaceCaps caps;
+
+ caps.any = true;
+
+ return caps;
+ }
+};
+
+enum class SharedSurfaceType : uint8_t {
+ Unknown = 0,
+
+ Basic,
+ EGLImageShare,
+ EGLSurfaceANGLE,
+ DXGLInterop,
+ DXGLInterop2,
+ Gralloc,
+ IOSurface,
+ GLXDrawable,
+ SharedGLTexture,
+
+ Max
+};
+
+enum class AttachmentType : uint8_t {
+ Screen = 0,
+
+ GLTexture,
+ GLRenderbuffer,
+
+ Max
+};
+
+} // namespace gl
+
+} /* namespace mozilla */
+
+#endif /* SURFACE_TYPES_H_ */
diff --git a/system/graphics/gl/TextureGarbageBin.cpp b/system/graphics/gl/TextureGarbageBin.cpp
new file mode 100644
index 000000000..577b5df89
--- /dev/null
+++ b/system/graphics/gl/TextureGarbageBin.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureGarbageBin.h"
+#include "GLContext.h"
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+void
+TextureGarbageBin::GLContextTeardown()
+{
+ EmptyGarbage();
+
+ MutexAutoLock lock(mMutex);
+ mGL = nullptr;
+}
+
+void
+TextureGarbageBin::Trash(GLuint tex)
+{
+ MutexAutoLock lock(mMutex);
+ if (!mGL)
+ return;
+
+ mGarbageTextures.push(tex);
+}
+
+void
+TextureGarbageBin::EmptyGarbage()
+{
+ MutexAutoLock lock(mMutex);
+ if (!mGL)
+ return;
+
+ MOZ_RELEASE_ASSERT(mGL->IsCurrent(), "GFX: GL context not current.");
+ while (!mGarbageTextures.empty()) {
+ GLuint tex = mGarbageTextures.top();
+ mGarbageTextures.pop();
+ mGL->fDeleteTextures(1, &tex);
+ }
+}
diff --git a/system/graphics/gl/TextureGarbageBin.h b/system/graphics/gl/TextureGarbageBin.h
new file mode 100644
index 000000000..3cd89f2a7
--- /dev/null
+++ b/system/graphics/gl/TextureGarbageBin.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TEXTUREGARBAGEBIN_H_
+#define TEXTUREGARBAGEBIN_H_
+
+#include <stack>
+
+#include "mozilla/Mutex.h"
+#include "nsISupportsImpl.h"
+
+#include "GLContextTypes.h"
+
+namespace mozilla {
+namespace gl {
+
+class TextureGarbageBin final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureGarbageBin)
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~TextureGarbageBin()
+ {
+ }
+
+ GLContext* mGL;
+ Mutex mMutex;
+ std::stack<GLuint> mGarbageTextures;
+
+public:
+ explicit TextureGarbageBin(GLContext* gl)
+ : mGL(gl)
+ , mMutex("TextureGarbageBin mutex")
+ {}
+
+ void GLContextTeardown();
+ void Trash(GLuint tex);
+ void EmptyGarbage();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // TEXTUREGARBAGEBIN_H_
diff --git a/system/graphics/gl/TextureImageEGL.cpp b/system/graphics/gl/TextureImageEGL.cpp
new file mode 100644
index 000000000..87a547c26
--- /dev/null
+++ b/system/graphics/gl/TextureImageEGL.cpp
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureImageEGL.h"
+#include "GLLibraryEGL.h"
+#include "GLContext.h"
+#include "GLUploadHelpers.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace gl {
+
+static GLenum
+GLFormatForImage(gfx::SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ return LOCAL_GL_RGBA;
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ return LOCAL_GL_RGB;
+ case gfx::SurfaceFormat::A8:
+ return LOCAL_GL_LUMINANCE;
+ default:
+ NS_WARNING("Unknown GL format for Surface format");
+ }
+ return 0;
+}
+
+static GLenum
+GLTypeForImage(gfx::SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ case gfx::SurfaceFormat::A8:
+ return LOCAL_GL_UNSIGNED_BYTE;
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ return LOCAL_GL_UNSIGNED_SHORT_5_6_5;
+ default:
+ NS_WARNING("Unknown GL format for Surface format");
+ }
+ return 0;
+}
+
+TextureImageEGL::TextureImageEGL(GLuint aTexture,
+ const gfx::IntSize& aSize,
+ GLenum aWrapMode,
+ ContentType aContentType,
+ GLContext* aContext,
+ Flags aFlags,
+ TextureState aTextureState,
+ TextureImage::ImageFormat aImageFormat)
+ : TextureImage(aSize, aWrapMode, aContentType, aFlags)
+ , mGLContext(aContext)
+ , mUpdateFormat(gfx::ImageFormatToSurfaceFormat(aImageFormat))
+ , mEGLImage(nullptr)
+ , mTexture(aTexture)
+ , mSurface(nullptr)
+ , mConfig(nullptr)
+ , mTextureState(aTextureState)
+ , mBound(false)
+{
+ if (mUpdateFormat == gfx::SurfaceFormat::UNKNOWN) {
+ mUpdateFormat =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType());
+ }
+
+ if (mUpdateFormat == gfx::SurfaceFormat::R5G6B5_UINT16) {
+ mTextureFormat = gfx::SurfaceFormat::R8G8B8X8;
+ } else if (mUpdateFormat == gfx::SurfaceFormat::B8G8R8X8) {
+ mTextureFormat = gfx::SurfaceFormat::B8G8R8X8;
+ } else {
+ mTextureFormat = gfx::SurfaceFormat::B8G8R8A8;
+ }
+}
+
+TextureImageEGL::~TextureImageEGL()
+{
+ if (mGLContext->IsDestroyed() || !mGLContext->IsOwningThreadCurrent()) {
+ return;
+ }
+
+ // If we have a context, then we need to delete the texture;
+ // if we don't have a context (either real or shared),
+ // then they went away when the contex was deleted, because it
+ // was the only one that had access to it.
+ if (mGLContext->MakeCurrent()) {
+ mGLContext->fDeleteTextures(1, &mTexture);
+ }
+ ReleaseTexImage();
+ DestroyEGLSurface();
+}
+
+bool
+TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0,0) */)
+{
+ gfx::IntRect bounds = aRegion.GetBounds();
+
+ nsIntRegion region;
+ if (mTextureState != Valid) {
+ bounds = gfx::IntRect(0, 0, mSize.width, mSize.height);
+ region = nsIntRegion(bounds);
+ } else {
+ region = aRegion;
+ }
+
+ bool needInit = mTextureState == Created;
+ size_t uploadSize = 0;
+ mTextureFormat =
+ UploadSurfaceToTexture(mGLContext,
+ aSurf,
+ region,
+ mTexture,
+ mSize,
+ &uploadSize,
+ needInit,
+ aFrom);
+ if (uploadSize > 0) {
+ UpdateUploadSize(uploadSize);
+ }
+
+ mTextureState = Valid;
+ return true;
+}
+
+void
+TextureImageEGL::BindTexture(GLenum aTextureUnit)
+{
+ // Ensure the texture is allocated before it is used.
+ if (mTextureState == Created) {
+ Resize(mSize);
+ }
+
+ mGLContext->fActiveTexture(aTextureUnit);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+}
+
+void
+TextureImageEGL::Resize(const gfx::IntSize& aSize)
+{
+ if (mSize == aSize && mTextureState != Created)
+ return;
+
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+
+ mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
+ 0,
+ GLFormatForImage(mUpdateFormat),
+ aSize.width,
+ aSize.height,
+ 0,
+ GLFormatForImage(mUpdateFormat),
+ GLTypeForImage(mUpdateFormat),
+ nullptr);
+
+ mTextureState = Allocated;
+ mSize = aSize;
+}
+
+bool
+TextureImageEGL::BindTexImage()
+{
+ if (mBound && !ReleaseTexImage())
+ return false;
+
+ EGLBoolean success =
+ sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface,
+ LOCAL_EGL_BACK_BUFFER);
+
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = true;
+ return true;
+}
+
+bool
+TextureImageEGL::ReleaseTexImage()
+{
+ if (!mBound)
+ return true;
+
+ EGLBoolean success =
+ sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface,
+ LOCAL_EGL_BACK_BUFFER);
+
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = false;
+ return true;
+}
+
+void
+TextureImageEGL::DestroyEGLSurface(void)
+{
+ if (!mSurface)
+ return;
+
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface);
+ mSurface = nullptr;
+}
+
+already_AddRefed<TextureImage>
+CreateTextureImageEGL(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+{
+ RefPtr<TextureImage> t = new gl::TiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat);
+ return t.forget();
+}
+
+already_AddRefed<TextureImage>
+TileGenFuncEGL(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat)
+{
+ gl->MakeCurrent();
+
+ GLuint texture;
+ gl->fGenTextures(1, &texture);
+
+ RefPtr<TextureImageEGL> teximage =
+ new TextureImageEGL(texture, aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType,
+ gl, aFlags, TextureImage::Created, aImageFormat);
+
+ teximage->BindTexture(LOCAL_GL_TEXTURE0);
+
+ GLint texfilter = aFlags & TextureImage::UseNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+
+ return teximage.forget();
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/gl/TextureImageEGL.h b/system/graphics/gl/TextureImageEGL.h
new file mode 100644
index 000000000..0a645742f
--- /dev/null
+++ b/system/graphics/gl/TextureImageEGL.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TEXTUREIMAGEEGL_H_
+#define TEXTUREIMAGEEGL_H_
+
+#include "GLTextureImage.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gl {
+
+class TextureImageEGL final
+ : public TextureImage
+{
+public:
+ TextureImageEGL(GLuint aTexture,
+ const gfx::IntSize& aSize,
+ GLenum aWrapMode,
+ ContentType aContentType,
+ GLContext* aContext,
+ Flags aFlags = TextureImage::NoFlags,
+ TextureState aTextureState = Created,
+ TextureImage::ImageFormat aImageFormat = SurfaceFormat::UNKNOWN);
+
+ virtual ~TextureImageEGL();
+
+ virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
+
+ virtual void BindTexture(GLenum aTextureUnit);
+
+ virtual GLuint GetTextureID()
+ {
+ // Ensure the texture is allocated before it is used.
+ if (mTextureState == Created) {
+ Resize(mSize);
+ }
+ return mTexture;
+ };
+
+ virtual void Resize(const gfx::IntSize& aSize);
+
+ bool BindTexImage();
+
+ bool ReleaseTexImage();
+
+ virtual bool CreateEGLSurface(gfxASurface* aSurface)
+ {
+ return false;
+ }
+
+ virtual void DestroyEGLSurface(void);
+
+protected:
+ typedef gfxImageFormat ImageFormat;
+
+ GLContext* mGLContext;
+
+ gfx::SurfaceFormat mUpdateFormat;
+ EGLImage mEGLImage;
+ GLuint mTexture;
+ EGLSurface mSurface;
+ EGLConfig mConfig;
+ TextureState mTextureState;
+
+ bool mBound;
+};
+
+already_AddRefed<TextureImage>
+CreateTextureImageEGL(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ GLenum aWrapMode,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat);
+
+already_AddRefed<TextureImage>
+TileGenFuncEGL(GLContext* gl,
+ const gfx::IntSize& aSize,
+ TextureImage::ContentType aContentType,
+ TextureImage::Flags aFlags,
+ TextureImage::ImageFormat aImageFormat);
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // TEXTUREIMAGEEGL_H_
diff --git a/system/graphics/gl/WGLLibrary.h b/system/graphics/gl/WGLLibrary.h
new file mode 100644
index 000000000..3c261e10d
--- /dev/null
+++ b/system/graphics/gl/WGLLibrary.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContextTypes.h"
+#include <windows.h>
+
+struct PRLibrary;
+
+namespace mozilla {
+namespace gl {
+
+class WGLLibrary
+{
+public:
+ WGLLibrary()
+ : mInitialized(false)
+ , mOGLLibrary(nullptr)
+ , mHasRobustness(false)
+ , mHasDXInterop(false)
+ , mHasDXInterop2(false)
+ , mWindow (0)
+ , mWindowDC(0)
+ , mWindowGLContext(0)
+ , mWindowPixelFormat(0)
+ {}
+
+ typedef HGLRC (GLAPIENTRY * PFNWGLCREATECONTEXTPROC) (HDC);
+ PFNWGLCREATECONTEXTPROC fCreateContext;
+ typedef BOOL (GLAPIENTRY * PFNWGLDELETECONTEXTPROC) (HGLRC);
+ PFNWGLDELETECONTEXTPROC fDeleteContext;
+ typedef BOOL (GLAPIENTRY * PFNWGLMAKECURRENTPROC) (HDC, HGLRC);
+ PFNWGLMAKECURRENTPROC fMakeCurrent;
+ typedef PROC (GLAPIENTRY * PFNWGLGETPROCADDRESSPROC) (LPCSTR);
+ PFNWGLGETPROCADDRESSPROC fGetProcAddress;
+ typedef HGLRC (GLAPIENTRY * PFNWGLGETCURRENTCONTEXT) (void);
+ PFNWGLGETCURRENTCONTEXT fGetCurrentContext;
+ typedef HDC (GLAPIENTRY * PFNWGLGETCURRENTDC) (void);
+ PFNWGLGETCURRENTDC fGetCurrentDC;
+ typedef BOOL (GLAPIENTRY * PFNWGLSHARELISTS) (HGLRC oldContext, HGLRC newContext);
+ PFNWGLSHARELISTS fShareLists;
+
+ typedef HANDLE (WINAPI * PFNWGLCREATEPBUFFERPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList);
+ PFNWGLCREATEPBUFFERPROC fCreatePbuffer;
+ typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERPROC) (HANDLE hPbuffer);
+ PFNWGLDESTROYPBUFFERPROC fDestroyPbuffer;
+ typedef HDC (WINAPI * PFNWGLGETPBUFFERDCPROC) (HANDLE hPbuffer);
+ PFNWGLGETPBUFFERDCPROC fGetPbufferDC;
+
+ typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEPROC) (HANDLE hPbuffer, int iBuffer);
+ PFNWGLBINDTEXIMAGEPROC fBindTexImage;
+ typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEPROC) (HANDLE hPbuffer, int iBuffer);
+ PFNWGLRELEASETEXIMAGEPROC fReleaseTexImage;
+
+ typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATPROC) (HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
+ PFNWGLCHOOSEPIXELFORMATPROC fChoosePixelFormat;
+ typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, int* piValues);
+ PFNWGLGETPIXELFORMATATTRIBIVPROC fGetPixelFormatAttribiv;
+
+ typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGPROC) (HDC hdc);
+ PFNWGLGETEXTENSIONSSTRINGPROC fGetExtensionsString;
+
+ typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSPROC) (HDC hdc, HGLRC hShareContext, const int* attribList);
+ PFNWGLCREATECONTEXTATTRIBSPROC fCreateContextAttribs;
+
+ // WGL_NV_DX_interop:
+ // BOOL wglDXSetResourceShareHandleNV(void* dxObject, HANDLE shareHandle);
+ typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLEPROC) (void* dxObject, HANDLE shareHandle);
+ PFNWGLDXSETRESOURCESHAREHANDLEPROC fDXSetResourceShareHandle;
+
+ // HANDLE wglDXOpenDeviceNV(void* dxDevice);
+ typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICEPROC) (void* dxDevice);
+ PFNWGLDXOPENDEVICEPROC fDXOpenDevice;
+
+ // BOOL wglDXCloseDeviceNV(HANDLE hDevice);
+ typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICEPROC) (HANDLE hDevice);
+ PFNWGLDXCLOSEDEVICEPROC fDXCloseDevice;
+
+ // HANDLE wglDXRegisterObjectNV(HANDLE hDevice, void* dxObject, GLuint name, GLenum type, GLenum access);
+ typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTPROC) (HANDLE hDevice, void* dxObject, GLuint name, GLenum type, GLenum access);
+ PFNWGLDXREGISTEROBJECTPROC fDXRegisterObject;
+
+ // BOOL wglDXUnregisterObjectNV(HANDLE hDevice, HANDLE hObject);
+ typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECT) (HANDLE hDevice, HANDLE hObject);
+ PFNWGLDXUNREGISTEROBJECT fDXUnregisterObject;
+
+ // BOOL wglDXObjectAccessNV(HANDLE hObject, GLenum access);
+ typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSPROC) (HANDLE hObject, GLenum access);
+ PFNWGLDXOBJECTACCESSPROC fDXObjectAccess;
+
+ // BOOL wglDXLockObjectsNV(HANDLE hDevice, GLint count, HANDLE* hObjects);
+ typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSPROC) (HANDLE hDevice, GLint count, HANDLE* hObjects);
+ PFNWGLDXLOCKOBJECTSPROC fDXLockObjects;
+
+ // BOOL wglDXUnlockObjectsNV(HANDLE hDevice, GLint count, HANDLE* hObjects);
+ typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSPROC) (HANDLE hDevice, GLint count, HANDLE* hObjects);
+ PFNWGLDXUNLOCKOBJECTSPROC fDXUnlockObjects;
+
+ bool EnsureInitialized();
+ HWND CreateDummyWindow(HDC* aWindowDC = nullptr);
+
+ bool HasRobustness() const { return mHasRobustness; }
+ bool HasDXInterop() const { return mHasDXInterop; }
+ bool HasDXInterop2() const { return mHasDXInterop2; }
+ bool IsInitialized() const { return mInitialized; }
+ HWND GetWindow() const { return mWindow; }
+ HDC GetWindowDC() const {return mWindowDC; }
+ HGLRC GetWindowGLContext() const {return mWindowGLContext; }
+ int GetWindowPixelFormat() const { return mWindowPixelFormat; }
+ PRLibrary* GetOGLLibrary() { return mOGLLibrary; }
+
+private:
+ bool mInitialized;
+ PRLibrary* mOGLLibrary;
+ bool mHasRobustness;
+ bool mHasDXInterop;
+ bool mHasDXInterop2;
+
+ HWND mWindow;
+ HDC mWindowDC;
+ HGLRC mWindowGLContext;
+ int mWindowPixelFormat;
+
+};
+
+// a global WGLLibrary instance
+extern WGLLibrary sWGLLib;
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/system/graphics/gl/moz.build b/system/graphics/gl/moz.build
new file mode 100644
index 000000000..7bf5a4ab0
--- /dev/null
+++ b/system/graphics/gl/moz.build
@@ -0,0 +1,127 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gl_provider = 'Null'
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ gl_provider = 'WGL'
+elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ if CONFIG['MOZ_EGL_XRENDER_COMPOSITE']:
+ gl_provider = 'EGL'
+ else:
+ gl_provider = 'GLX'
+
+if CONFIG['MOZ_GL_PROVIDER']:
+ gl_provider = CONFIG['MOZ_GL_PROVIDER']
+
+EXPORTS += [
+ 'DecomposeIntoNoRepeatTriangles.h',
+ 'EGLUtils.h',
+ 'GfxTexturesReporter.h',
+ 'GLBlitHelper.h',
+ 'GLConsts.h',
+ 'GLContext.h',
+ 'GLContextEGL.h',
+ 'GLContextProvider.h',
+ 'GLContextProviderImpl.h',
+ 'GLContextSymbols.h',
+ 'GLContextTypes.h',
+ 'GLDefs.h',
+ 'GLLibraryEGL.h',
+ 'GLLibraryLoader.h',
+ 'GLReadTexImageHelper.h',
+ 'GLScreenBuffer.h',
+ 'GLTextureImage.h',
+ 'GLTypes.h',
+ 'GLUploadHelpers.h',
+ 'HeapCopyOfStackArray.h',
+ 'ScopedGLHelpers.h',
+ 'SharedSurface.h',
+ 'SharedSurfaceEGL.h',
+ 'SharedSurfaceGL.h',
+ 'SurfaceTypes.h',
+ 'TextureGarbageBin.h',
+]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS += [
+ 'GLContextGLX.h',
+ 'GLXLibrary.h',
+ ]
+
+# Win32 is a special snowflake, for ANGLE
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS += [
+ 'GLContextWGL.h',
+ 'SharedSurfaceANGLE.h', # Needs <windows.h> for `HANDLE`.
+ 'SharedSurfaceD3D11Interop.h',
+ 'WGLLibrary.h',
+ ]
+ SOURCES += [
+ 'GLContextProviderWGL.cpp',
+ 'SharedSurfaceANGLE.cpp',
+ 'SharedSurfaceD3D11Interop.cpp',
+ ]
+if CONFIG['MOZ_ENABLE_SKIA_GPU']:
+ EXPORTS += ['SkiaGLGlue.h']
+ SOURCES += [
+ 'SkiaGLGlue.cpp',
+ ]
+ if CONFIG['CLANG_CXX']:
+ # Suppress warnings from Skia header files.
+ SOURCES['SkiaGLGlue.cpp'].flags += ['-Wno-implicit-fallthrough']
+
+if gl_provider == 'GLX':
+ # GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES
+ # as it includes X11 headers which cause conflicts.
+ SOURCES += [
+ 'GLContextProviderGLX.cpp',
+ 'SharedSurfaceGLX.cpp'
+ ]
+ EXPORTS += [
+ 'SharedSurfaceGLX.h'
+ ]
+
+SOURCES += [
+ 'DecomposeIntoNoRepeatTriangles.cpp',
+ 'EGLUtils.cpp',
+ 'GfxTexturesReporter.cpp',
+ 'GLBlitHelper.cpp',
+ 'GLContext.cpp',
+ 'GLContextFeatures.cpp',
+ 'GLContextProviderEGL.cpp',
+ 'GLContextTypes.cpp',
+ 'GLDebugUtils.cpp',
+ 'GLLibraryEGL.cpp',
+ 'GLLibraryLoader.cpp',
+ 'GLReadTexImageHelper.cpp',
+ 'GLScreenBuffer.cpp',
+ 'GLTextureImage.cpp',
+ 'GLUploadHelpers.cpp',
+ 'ScopedGLHelpers.cpp',
+ 'SharedSurface.cpp',
+ 'SharedSurfaceEGL.cpp',
+ 'SharedSurfaceGL.cpp',
+ 'SurfaceTypes.cpp',
+ 'TextureGarbageBin.cpp',
+ 'TextureImageEGL.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_D3DCOMPILER_VISTA_DLL']:
+ DEFINES['MOZ_D3DCOMPILER_VISTA_DLL'] = CONFIG['MOZ_D3DCOMPILER_VISTA_DLL']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+CFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CFLAGS += CONFIG['TK_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/system/graphics/ipc/CompositorSession.cpp b/system/graphics/ipc/CompositorSession.cpp
new file mode 100644
index 000000000..52b42bbc0
--- /dev/null
+++ b/system/graphics/ipc/CompositorSession.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorSession.h"
+#include "base/process_util.h"
+#include "GPUChild.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/GPUProcessHost.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace widget;
+
+
+CompositorSession::CompositorSession(CompositorWidgetDelegate* aDelegate,
+ CompositorBridgeChild* aChild,
+ const uint64_t& aRootLayerTreeId)
+ : mCompositorWidgetDelegate(aDelegate),
+ mCompositorBridgeChild(aChild),
+ mRootLayerTreeId(aRootLayerTreeId)
+{
+}
+
+CompositorSession::~CompositorSession()
+{
+}
+
+CompositorBridgeChild*
+CompositorSession::GetCompositorBridgeChild()
+{
+ return mCompositorBridgeChild;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/ipc/CompositorSession.h b/system/graphics/ipc/CompositorSession.h
new file mode 100644
index 000000000..0afcc924a
--- /dev/null
+++ b/system/graphics/ipc/CompositorSession.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_CompositorSession_h_
+#define _include_mozilla_gfx_ipc_CompositorSession_h_
+
+#include "base/basictypes.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "nsISupportsImpl.h"
+
+class nsIWidget;
+
+namespace mozilla {
+namespace widget {
+class CompositorWidget;
+class CompositorWidgetDelegate;
+} // namespace widget
+namespace gfx {
+class GPUProcessHost;
+class GPUProcessManager;
+} // namespace gfx
+namespace layers {
+
+class GeckoContentController;
+class IAPZCTreeManager;
+class CompositorBridgeParent;
+class CompositorBridgeChild;
+class ClientLayerManager;
+
+// A CompositorSession provides access to a compositor without exposing whether
+// or not it's in-process or out-of-process.
+class CompositorSession
+{
+ friend class gfx::GPUProcessManager;
+
+protected:
+ typedef gfx::GPUProcessHost GPUProcessHost;
+ typedef widget::CompositorWidget CompositorWidget;
+ typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession)
+
+ virtual bool Reset(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier) = 0;
+
+ virtual void Shutdown() = 0;
+
+ // This returns a CompositorBridgeParent if the compositor resides in the same process.
+ virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
+
+ // Set the GeckoContentController for the root of the layer tree.
+ virtual void SetContentController(GeckoContentController* aController) = 0;
+
+ // Return the Async Pan/Zoom Tree Manager for this compositor.
+ virtual RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
+
+ // Return the child end of the compositor IPC bridge.
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ // Return the proxy for accessing the compositor's widget.
+ CompositorWidgetDelegate* GetCompositorWidgetDelegate() {
+ return mCompositorWidgetDelegate;
+ }
+
+ // Return the id of the root layer tree.
+ uint64_t RootLayerTreeId() const {
+ return mRootLayerTreeId;
+ }
+
+protected:
+ CompositorSession(CompositorWidgetDelegate* aDelegate,
+ CompositorBridgeChild* aChild,
+ const uint64_t& aRootLayerTreeId);
+ virtual ~CompositorSession();
+
+protected:
+ CompositorWidgetDelegate* mCompositorWidgetDelegate;
+ RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+ uint64_t mRootLayerTreeId;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(CompositorSession);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_CompositorSession_h_
diff --git a/system/graphics/ipc/CompositorWidgetVsyncObserver.cpp b/system/graphics/ipc/CompositorWidgetVsyncObserver.cpp
new file mode 100644
index 000000000..e4c8e9fb2
--- /dev/null
+++ b/system/graphics/ipc/CompositorWidgetVsyncObserver.cpp
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorWidgetVsyncObserver.h"
+#include "mozilla/gfx/VsyncBridgeChild.h"
+
+namespace mozilla {
+namespace widget {
+
+CompositorWidgetVsyncObserver::CompositorWidgetVsyncObserver(
+ RefPtr<VsyncBridgeChild> aVsyncBridge,
+ const uint64_t& aRootLayerTreeId)
+ : mVsyncBridge(aVsyncBridge),
+ mRootLayerTreeId(aRootLayerTreeId)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+bool
+CompositorWidgetVsyncObserver::NotifyVsync(TimeStamp aTimeStamp)
+{
+ // Vsync notifications should only arrive on the vsync thread.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mVsyncBridge->NotifyVsync(aTimeStamp, mRootLayerTreeId);
+ return true;
+}
+
+} // namespace widget
+} // namespace mozilla
diff --git a/system/graphics/ipc/CompositorWidgetVsyncObserver.h b/system/graphics/ipc/CompositorWidgetVsyncObserver.h
new file mode 100644
index 000000000..f37aafcc2
--- /dev/null
+++ b/system/graphics/ipc/CompositorWidgetVsyncObserver.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h
+#define mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h
+
+#include "mozilla/VsyncDispatcher.h"
+
+namespace mozilla {
+namespace gfx {
+class VsyncBridgeChild;
+} // namespace gfx
+
+namespace widget {
+
+class CompositorWidgetVsyncObserver : public VsyncObserver
+{
+ typedef gfx::VsyncBridgeChild VsyncBridgeChild;
+
+ public:
+ CompositorWidgetVsyncObserver(RefPtr<VsyncBridgeChild> aVsyncBridge,
+ const uint64_t& aRootLayerTreeId);
+
+ bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+
+ private:
+ RefPtr<VsyncBridgeChild> mVsyncBridge;
+ uint64_t mRootLayerTreeId;
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h
diff --git a/system/graphics/ipc/D3DMessageUtils.cpp b/system/graphics/ipc/D3DMessageUtils.cpp
new file mode 100644
index 000000000..8df13a896
--- /dev/null
+++ b/system/graphics/ipc/D3DMessageUtils.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "D3DMessageUtils.h"
+#if defined(XP_WIN)
+# include "gfxWindowsPlatform.h"
+#endif
+
+bool
+DxgiAdapterDesc::operator ==(const DxgiAdapterDesc& aOther) const
+{
+ return memcmp(&aOther, this, sizeof(*this)) == 0;
+}
+
+#if defined(XP_WIN)
+static_assert(sizeof(DxgiAdapterDesc) == sizeof(DXGI_ADAPTER_DESC),
+ "DXGI_ADAPTER_DESC doe snot match DxgiAdapterDesc");
+
+const DxgiAdapterDesc&
+DxgiAdapterDesc::From(const DXGI_ADAPTER_DESC& aDesc)
+{
+ return reinterpret_cast<const DxgiAdapterDesc&>(aDesc);
+}
+
+const DXGI_ADAPTER_DESC&
+DxgiAdapterDesc::ToDesc() const
+{
+ return reinterpret_cast<const DXGI_ADAPTER_DESC&>(*this);
+}
+#endif
+
+namespace IPC {
+
+void
+ParamTraits<DxgiAdapterDesc>::Write(Message* aMsg, const paramType& aParam)
+{
+#if defined(XP_WIN)
+ aMsg->WriteBytes(aParam.Description, sizeof(aParam.Description));
+ WriteParam(aMsg, aParam.VendorId);
+ WriteParam(aMsg, aParam.DeviceId);
+ WriteParam(aMsg, aParam.SubSysId);
+ WriteParam(aMsg, aParam.Revision);
+ WriteParam(aMsg, aParam.DedicatedVideoMemory);
+ WriteParam(aMsg, aParam.DedicatedSystemMemory);
+ WriteParam(aMsg, aParam.SharedSystemMemory);
+ WriteParam(aMsg, aParam.AdapterLuid.LowPart);
+ WriteParam(aMsg, aParam.AdapterLuid.HighPart);
+#else
+ MOZ_ASSERT_UNREACHABLE("DxgiAdapterDesc is Windows-only");
+#endif
+}
+
+bool
+ParamTraits<DxgiAdapterDesc>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+{
+#if defined(XP_WIN)
+ if (!aMsg->ReadBytesInto(aIter, aResult->Description, sizeof(aResult->Description))) {
+ return false;
+ }
+
+ if (ReadParam(aMsg, aIter, &aResult->VendorId) &&
+ ReadParam(aMsg, aIter, &aResult->DeviceId) &&
+ ReadParam(aMsg, aIter, &aResult->SubSysId) &&
+ ReadParam(aMsg, aIter, &aResult->Revision) &&
+ ReadParam(aMsg, aIter, &aResult->DedicatedVideoMemory) &&
+ ReadParam(aMsg, aIter, &aResult->DedicatedSystemMemory) &&
+ ReadParam(aMsg, aIter, &aResult->SharedSystemMemory) &&
+ ReadParam(aMsg, aIter, &aResult->AdapterLuid.LowPart) &&
+ ReadParam(aMsg, aIter, &aResult->AdapterLuid.HighPart))
+ {
+ return true;
+ }
+#else
+ MOZ_ASSERT_UNREACHABLE("DxgiAdapterDesc is Windows-only");
+#endif
+ return false;
+}
+
+} // namespace IPC
diff --git a/system/graphics/ipc/D3DMessageUtils.h b/system/graphics/ipc/D3DMessageUtils.h
new file mode 100644
index 000000000..a049b7780
--- /dev/null
+++ b/system/graphics/ipc/D3DMessageUtils.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_gfx_ipc_D3DMessageUtils_h__
+#define _include_gfx_ipc_D3DMessageUtils_h__
+
+#include "chrome/common/ipc_message_utils.h"
+#include "ipc/IPCMessageUtils.h"
+
+// Can't include dxgi.h, since it leaks random identifiers and #defines, and
+// IPDL causes this file to be #included all over.
+typedef struct DXGI_ADAPTER_DESC DXGI_ADAPTER_DESC;
+
+struct DxgiAdapterDesc
+{
+#if defined(XP_WIN)
+ WCHAR Description[128];
+ UINT VendorId;
+ UINT DeviceId;
+ UINT SubSysId;
+ UINT Revision;
+ SIZE_T DedicatedVideoMemory;
+ SIZE_T DedicatedSystemMemory;
+ SIZE_T SharedSystemMemory;
+ LUID AdapterLuid;
+
+ static const DxgiAdapterDesc& From(const DXGI_ADAPTER_DESC& aDesc);
+ const DXGI_ADAPTER_DESC& ToDesc() const;
+#endif
+
+ bool operator ==(const DxgiAdapterDesc& aOther) const;
+};
+
+namespace IPC {
+
+template <>
+struct ParamTraits<DxgiAdapterDesc>
+{
+ typedef DxgiAdapterDesc paramType;
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // _include_gfx_ipc_D3DMessageUtils_h__
diff --git a/system/graphics/ipc/GPUChild.cpp b/system/graphics/ipc/GPUChild.cpp
new file mode 100644
index 000000000..9a8272123
--- /dev/null
+++ b/system/graphics/ipc/GPUChild.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "GPUChild.h"
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "GPUProcessHost.h"
+#include "GPUProcessManager.h"
+#include "mozilla/dom/CheckerboardReportService.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/Services.h"
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+GPUChild::GPUChild(GPUProcessHost* aHost)
+ : mHost(aHost),
+ mGPUReady(false)
+{
+ MOZ_COUNT_CTOR(GPUChild);
+}
+
+GPUChild::~GPUChild()
+{
+ MOZ_COUNT_DTOR(GPUChild);
+}
+
+void
+GPUChild::Init()
+{
+ // Build a list of prefs the GPU process will need. Note that because we
+ // limit the GPU process to prefs contained in gfxPrefs, we can simplify
+ // the message in two ways: one, we only need to send its index in gfxPrefs
+ // rather than its name, and two, we only need to send prefs that don't
+ // have their default value.
+ nsTArray<GfxPrefSetting> prefs;
+ for (auto pref : gfxPrefs::all()) {
+ if (pref->HasDefaultValue()) {
+ continue;
+ }
+
+ GfxPrefValue value;
+ pref->GetCachedValue(&value);
+ prefs.AppendElement(GfxPrefSetting(pref->Index(), value));
+ }
+
+ nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars();
+
+ DevicePrefs devicePrefs;
+ devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING);
+ devicePrefs.d3d11Compositing() = gfxConfig::GetValue(Feature::D3D11_COMPOSITING);
+ devicePrefs.oglCompositing() = gfxConfig::GetValue(Feature::OPENGL_COMPOSITING);
+ devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);
+
+ SendInit(prefs, updates, devicePrefs);
+
+ gfxVars::AddReceiver(this);
+}
+
+void
+GPUChild::OnVarChanged(const GfxVarUpdate& aVar)
+{
+ SendUpdateVar(aVar);
+}
+
+void
+GPUChild::EnsureGPUReady()
+{
+ if (mGPUReady) {
+ return;
+ }
+
+ GPUDeviceData data;
+ SendGetDeviceStatus(&data);
+
+ gfxPlatform::GetPlatform()->ImportGPUDeviceData(data);
+ mGPUReady = true;
+}
+
+bool
+GPUChild::RecvInitComplete(const GPUDeviceData& aData)
+{
+ // We synchronously requested GPU parameters before this arrived.
+ if (mGPUReady) {
+ return true;
+ }
+
+ gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData);
+ mGPUReady = true;
+ return true;
+}
+
+bool
+GPUChild::RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog)
+{
+ layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get()));
+ return true;
+}
+
+bool
+GPUChild::RecvGraphicsError(const nsCString& aError)
+{
+ gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
+ if (lf) {
+ std::stringstream message;
+ message << "GP+" << aError.get();
+ lf->UpdateStringsVector(message.str());
+ }
+ return true;
+}
+
+bool
+GPUChild::RecvNotifyUiObservers(const nsCString& aTopic)
+{
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
+ }
+ return true;
+}
+
+bool
+GPUChild::RecvNotifyDeviceReset()
+{
+ mHost->mListener->OnProcessDeviceReset(mHost);
+ return true;
+}
+
+void
+GPUChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ gfxVars::RemoveReceiver(this);
+ mHost->OnChannelClosed();
+}
+
+class DeferredDeleteGPUChild : public Runnable
+{
+public:
+ explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild)
+ : mChild(Move(aChild))
+ {
+ }
+
+ NS_IMETHODIMP Run() override {
+ return NS_OK;
+ }
+
+private:
+ UniquePtr<GPUChild> mChild;
+};
+
+/* static */ void
+GPUChild::Destroy(UniquePtr<GPUChild>&& aChild)
+{
+ NS_DispatchToMainThread(new DeferredDeleteGPUChild(Move(aChild)));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/GPUChild.h b/system/graphics/ipc/GPUChild.h
new file mode 100644
index 000000000..4b7fb182c
--- /dev/null
+++ b/system/graphics/ipc/GPUChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_GPUChild_h_
+#define _include_mozilla_gfx_ipc_GPUChild_h_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/PGPUChild.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+
+namespace mozilla {
+namespace gfx {
+
+class GPUProcessHost;
+
+class GPUChild final
+ : public PGPUChild,
+ public gfxVarReceiver
+{
+public:
+ explicit GPUChild(GPUProcessHost* aHost);
+ ~GPUChild();
+
+ void Init();
+
+ void EnsureGPUReady();
+
+ // gfxVarReceiver overrides.
+ void OnVarChanged(const GfxVarUpdate& aVar) override;
+
+ // PGPUChild overrides.
+ bool RecvInitComplete(const GPUDeviceData& aData) override;
+ bool RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ bool RecvGraphicsError(const nsCString& aError) override;
+ bool RecvNotifyUiObservers(const nsCString& aTopic) override;
+ bool RecvNotifyDeviceReset() override;
+
+ static void Destroy(UniquePtr<GPUChild>&& aChild);
+
+private:
+ GPUProcessHost* mHost;
+ bool mGPUReady;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_GPUChild_h_
diff --git a/system/graphics/ipc/GPUParent.cpp b/system/graphics/ipc/GPUParent.cpp
new file mode 100644
index 000000000..c634dd56f
--- /dev/null
+++ b/system/graphics/ipc/GPUParent.cpp
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifdef XP_WIN
+#include "WMF.h"
+#endif
+#include "GPUParent.h"
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "GPUProcessHost.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+#include "prenv.h"
+#include "ProcessUtils.h"
+#include "VsyncBridgeParent.h"
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+#ifdef MOZ_WIDGET_GTK
+# include <gtk/gtk.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+using namespace ipc;
+using namespace layers;
+
+static GPUParent* sGPUParent;
+
+GPUParent::GPUParent()
+{
+ sGPUParent = this;
+}
+
+GPUParent::~GPUParent()
+{
+ sGPUParent = nullptr;
+}
+
+/* static */ GPUParent*
+GPUParent::GetSingleton()
+{
+ return sGPUParent;
+}
+
+bool
+GPUParent::Init(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ // Initialize the thread manager before starting IPC. Otherwise, messages
+ // may be posted to the main thread and we won't be able to process them.
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+
+ // Now it's safe to start IPC.
+ if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
+ return false;
+ }
+
+ nsDebugImpl::SetMultiprocessMode("GPU");
+
+ // Ensure gfxPrefs are initialized.
+ gfxPrefs::GetSingleton();
+ gfxConfig::Init();
+ gfxVars::Initialize();
+ gfxPlatform::InitNullMetadata();
+ // Ensure our Factory is initialised, mainly for gfx logging to work.
+ gfxPlatform::InitMoz2DLogging();
+#if defined(XP_WIN)
+ DeviceManagerDx::Init();
+#endif
+
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ CompositorThreadHolder::Start();
+ APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
+ APZCTreeManager::InitializeGlobalState();
+ LayerTreeOwnerTracker::Initialize();
+ mozilla::ipc::SetThisProcessName("GPU Process");
+#ifdef XP_WIN
+ wmf::MFStartup();
+#endif
+ return true;
+}
+
+void
+GPUParent::NotifyDeviceReset()
+{
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> void {
+ GPUParent::GetSingleton()->NotifyDeviceReset();
+ }));
+ return;
+ }
+
+ // Reset and reinitialize the compositor devices
+#ifdef XP_WIN
+ if (!DeviceManagerDx::Get()->MaybeResetAndReacquireDevices()) {
+ // If the device doesn't need to be reset then the device
+ // has already been reset by a previous NotifyDeviceReset message.
+ return;
+ }
+#endif
+
+ // Notify the main process that there's been a device reset
+ // and that they should reset their compositors and repaint
+ Unused << SendNotifyDeviceReset();
+}
+
+bool
+GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs,
+ nsTArray<GfxVarUpdate>&& vars,
+ const DevicePrefs& devicePrefs)
+{
+ const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all();
+ for (auto& setting : prefs) {
+ gfxPrefs::Pref* pref = globalPrefs[setting.index()];
+ pref->SetCachedValue(setting.value());
+ }
+ for (const auto& var : vars) {
+ gfxVars::ApplyUpdate(var);
+ }
+
+ // Inherit device preferences.
+ gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing());
+ gfxConfig::Inherit(Feature::D3D11_COMPOSITING, devicePrefs.d3d11Compositing());
+ gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing());
+ gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1());
+
+#if defined(XP_WIN)
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ DeviceManagerDx::Get()->CreateCompositorDevices();
+ }
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ char* display_name = PR_GetEnv("DISPLAY");
+ if (display_name) {
+ int argc = 3;
+ char option_name[] = "--display";
+ char* argv[] = {
+ // argv0 is unused because g_set_prgname() was called in
+ // XRE_InitChildProcess().
+ nullptr,
+ option_name,
+ display_name,
+ nullptr
+ };
+ char** argvp = argv;
+ gtk_init(&argc, &argvp);
+ } else {
+ gtk_init(nullptr, nullptr);
+ }
+#endif
+
+ // Send a message to the UI process that we're done.
+ GPUDeviceData data;
+ RecvGetDeviceStatus(&data);
+ Unused << SendInitComplete(data);
+
+ return true;
+}
+
+bool
+GPUParent::RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint)
+{
+ mVsyncBridge = VsyncBridgeParent::Start(Move(aVsyncEndpoint));
+ return true;
+}
+
+bool
+GPUParent::RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ ImageBridgeParent::CreateForGPUProcess(Move(aEndpoint));
+ return true;
+}
+
+bool
+GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
+{
+ gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
+ pref->SetCachedValue(setting.value());
+ return true;
+}
+
+bool
+GPUParent::RecvUpdateVar(const GfxVarUpdate& aUpdate)
+{
+ gfxVars::ApplyUpdate(aUpdate);
+ return true;
+}
+
+static void
+CopyFeatureChange(Feature aFeature, FeatureChange* aOut)
+{
+ FeatureState& feature = gfxConfig::GetFeature(aFeature);
+ if (feature.DisabledByDefault() || feature.IsEnabled()) {
+ // No change:
+ // - Disabled-by-default means the parent process told us not to use this feature.
+ // - Enabled means we were told to use this feature, and we didn't discover anything
+ // that would prevent us from doing so.
+ *aOut = null_t();
+ return;
+ }
+
+ MOZ_ASSERT(!feature.IsEnabled());
+
+ nsCString message;
+ message.AssignASCII(feature.GetFailureMessage());
+
+ *aOut = FeatureFailure(feature.GetValue(), message, feature.GetFailureId());
+}
+
+bool
+GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut)
+{
+ CopyFeatureChange(Feature::D3D11_COMPOSITING, &aOut->d3d11Compositing());
+ CopyFeatureChange(Feature::OPENGL_COMPOSITING, &aOut->oglCompositing());
+
+#if defined(XP_WIN)
+ if (DeviceManagerDx* dm = DeviceManagerDx::Get()) {
+ D3D11DeviceStatus deviceStatus;
+ dm->ExportDeviceInfo(&deviceStatus);
+ aOut->gpuDevice() = deviceStatus;
+ }
+#else
+ aOut->gpuDevice() = null_t();
+#endif
+
+ return true;
+}
+
+static void
+OpenParent(RefPtr<CompositorBridgeParent> aParent,
+ Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ if (!aParent->Bind(Move(aEndpoint))) {
+ MOZ_CRASH("Failed to bind compositor");
+ }
+}
+
+bool
+GPUParent::RecvNewWidgetCompositor(Endpoint<layers::PCompositorBridgeParent>&& aEndpoint,
+ const CSSToLayoutDeviceScale& aScale,
+ const TimeDuration& aVsyncRate,
+ const bool& aUseExternalSurfaceSize,
+ const IntSize& aSurfaceSize)
+{
+ RefPtr<CompositorBridgeParent> cbp =
+ new CompositorBridgeParent(aScale, aVsyncRate, aUseExternalSurfaceSize, aSurfaceSize);
+
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+ loop->PostTask(NewRunnableFunction(OpenParent, cbp, Move(aEndpoint)));
+ return true;
+}
+
+bool
+GPUParent::RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ return CompositorBridgeParent::CreateForContent(Move(aEndpoint));
+}
+
+bool
+GPUParent::RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ return ImageBridgeParent::CreateForContent(Move(aEndpoint));
+}
+
+bool
+GPUParent::RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
+{
+ return dom::VideoDecoderManagerParent::CreateForContent(Move(aEndpoint));
+}
+
+bool
+GPUParent::RecvAddLayerTreeIdMapping(nsTArray<LayerTreeIdMapping>&& aMappings)
+{
+ for (const LayerTreeIdMapping& map : aMappings) {
+ LayerTreeOwnerTracker::Get()->Map(map.layersId(), map.ownerId());
+ }
+ return true;
+}
+
+bool
+GPUParent::RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping)
+{
+ LayerTreeOwnerTracker::Get()->Unmap(aMapping.layersId(), aMapping.ownerId());
+ CompositorBridgeParent::DeallocateLayerTreeId(aMapping.layersId());
+ return true;
+}
+
+bool
+GPUParent::RecvNotifyGpuObservers(const nsCString& aTopic)
+{
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
+ }
+ return true;
+}
+
+void
+GPUParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down GPU process early due to a crash!");
+ ProcessChild::QuickExit();
+ }
+
+#ifdef XP_WIN
+ wmf::MFShutdown();
+#endif
+
+#ifndef NS_FREE_PERMANENT_DATA
+ // No point in going through XPCOM shutdown because we don't keep persistent
+ // state.
+ ProcessChild::QuickExit();
+#endif
+
+ if (mVsyncBridge) {
+ mVsyncBridge->Shutdown();
+ mVsyncBridge = nullptr;
+ }
+ dom::VideoDecoderManagerParent::ShutdownVideoBridge();
+ CompositorThreadHolder::Shutdown();
+ Factory::ShutDown();
+#if defined(XP_WIN)
+ DeviceManagerDx::Shutdown();
+#endif
+ LayerTreeOwnerTracker::Shutdown();
+ gfxVars::Shutdown();
+ gfxConfig::Shutdown();
+ gfxPrefs::DestroySingleton();
+ XRE_ShutdownChildProcess();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/GPUParent.h b/system/graphics/ipc/GPUParent.h
new file mode 100644
index 000000000..975b75da6
--- /dev/null
+++ b/system/graphics/ipc/GPUParent.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_gfx_ipc_GPUParent_h__
+#define _include_gfx_ipc_GPUParent_h__
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PGPUParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncBridgeParent;
+
+class GPUParent final : public PGPUParent
+{
+public:
+ GPUParent();
+ ~GPUParent();
+
+ static GPUParent* GetSingleton();
+
+ bool Init(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+ void NotifyDeviceReset();
+
+ bool RecvInit(nsTArray<GfxPrefSetting>&& prefs,
+ nsTArray<GfxVarUpdate>&& vars,
+ const DevicePrefs& devicePrefs) override;
+ bool RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
+ bool RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
+ bool RecvUpdatePref(const GfxPrefSetting& pref) override;
+ bool RecvUpdateVar(const GfxVarUpdate& pref) override;
+ bool RecvNewWidgetCompositor(
+ Endpoint<PCompositorBridgeParent>&& aEndpoint,
+ const CSSToLayoutDeviceScale& aScale,
+ const TimeDuration& aVsyncRate,
+ const bool& aUseExternalSurface,
+ const IntSize& aSurfaceSize) override;
+ bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
+ bool RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
+ bool RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override;
+ bool RecvGetDeviceStatus(GPUDeviceData* aOutStatus) override;
+ bool RecvAddLayerTreeIdMapping(nsTArray<LayerTreeIdMapping>&& aMappings) override;
+ bool RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override;
+ bool RecvNotifyGpuObservers(const nsCString& aTopic) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ RefPtr<VsyncBridgeParent> mVsyncBridge;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_gfx_ipc_GPUParent_h__
diff --git a/system/graphics/ipc/GPUProcessHost.cpp b/system/graphics/ipc/GPUProcessHost.cpp
new file mode 100644
index 000000000..71d8144cc
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessHost.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sts=8 sw=2 ts=2 tw=99 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUProcessHost.h"
+#include "GPUChild.h"
+#include "chrome/common/process_watcher.h"
+#include "gfxPrefs.h"
+#include "mozilla/gfx/Logging.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace ipc;
+
+GPUProcessHost::GPUProcessHost(Listener* aListener)
+ : GeckoChildProcessHost(GeckoProcessType_GPU),
+ mListener(aListener),
+ mTaskFactory(this),
+ mLaunchPhase(LaunchPhase::Unlaunched),
+ mProcessToken(0),
+ mShutdownRequested(false),
+ mChannelClosed(false)
+{
+ MOZ_COUNT_CTOR(GPUProcessHost);
+}
+
+GPUProcessHost::~GPUProcessHost()
+{
+ MOZ_COUNT_DTOR(GPUProcessHost);
+}
+
+bool
+GPUProcessHost::Launch()
+{
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+ MOZ_ASSERT(!mGPUChild);
+
+ mLaunchPhase = LaunchPhase::Waiting;
+ mLaunchTime = TimeStamp::Now();
+
+ if (!GeckoChildProcessHost::AsyncLaunch()) {
+ mLaunchPhase = LaunchPhase::Complete;
+ return false;
+ }
+ return true;
+}
+
+bool
+GPUProcessHost::WaitForLaunch()
+{
+ if (mLaunchPhase == LaunchPhase::Complete) {
+ return !!mGPUChild;
+ }
+
+ int32_t timeoutMs = gfxPrefs::GPUProcessDevTimeoutMs();
+
+ // Our caller expects the connection to be finished after we return, so we
+ // immediately set up the IPDL actor and fire callbacks. The IO thread will
+ // still dispatch a notification to the main thread - we'll just ignore it.
+ bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
+ InitAfterConnect(result);
+ return result;
+}
+
+void
+GPUProcessHost::OnChannelConnected(int32_t peer_pid)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+ // Post a task to the main thread. Take the lock because mTaskFactory is not
+ // thread-safe.
+ RefPtr<Runnable> runnable;
+ {
+ MonitorAutoLock lock(mMonitor);
+ runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+GPUProcessHost::OnChannelError()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelError();
+
+ // Post a task to the main thread. Take the lock because mTaskFactory is not
+ // thread-safe.
+ RefPtr<Runnable> runnable;
+ {
+ MonitorAutoLock lock(mMonitor);
+ runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelErrorTask);
+ }
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+GPUProcessHost::OnChannelConnectedTask()
+{
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(true);
+ }
+}
+
+void
+GPUProcessHost::OnChannelErrorTask()
+{
+ if (mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(false);
+ }
+}
+
+static uint64_t sProcessTokenCounter = 0;
+
+void
+GPUProcessHost::InitAfterConnect(bool aSucceeded)
+{
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+ MOZ_ASSERT(!mGPUChild);
+
+ mLaunchPhase = LaunchPhase::Complete;
+
+ if (aSucceeded) {
+ mProcessToken = ++sProcessTokenCounter;
+ mGPUChild = MakeUnique<GPUChild>(this);
+ DebugOnly<bool> rv =
+ mGPUChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
+ MOZ_ASSERT(rv);
+
+ mGPUChild->Init();
+ }
+
+ if (mListener) {
+ mListener->OnProcessLaunchComplete(this);
+ }
+}
+
+void
+GPUProcessHost::Shutdown()
+{
+ MOZ_ASSERT(!mShutdownRequested);
+
+ mListener = nullptr;
+
+ if (mGPUChild) {
+ // OnChannelClosed uses this to check if the shutdown was expected or
+ // unexpected.
+ mShutdownRequested = true;
+
+#ifdef NS_FREE_PERMANENT_DATA
+ // The channel might already be closed if we got here unexpectedly.
+ if (!mChannelClosed) {
+ mGPUChild->Close();
+ }
+#else
+ // No need to communicate shutdown, the GPU process doesn't need to
+ // communicate anything back.
+ KillHard("NormalShutdown");
+#endif
+
+ // If we're shutting down unexpectedly, we're in the middle of handling an
+ // ActorDestroy for PGPUChild, which is still on the stack. We'll return
+ // back to OnChannelClosed.
+ //
+ // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
+ // acknowledges shutdown.
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void
+GPUProcessHost::OnChannelClosed()
+{
+ if (!mShutdownRequested) {
+ // This is an unclean shutdown. Notify our listener that we're going away.
+ mChannelClosed = true;
+ if (mListener) {
+ mListener->OnProcessUnexpectedShutdown(this);
+ }
+ }
+
+ // Release the actor.
+ GPUChild::Destroy(Move(mGPUChild));
+ MOZ_ASSERT(!mGPUChild);
+
+ // If the owner of GPUProcessHost already requested shutdown, we can now
+ // schedule destruction. Otherwise we must wait for someone to call
+ // Shutdown. Note that GPUProcessManager calls Shutdown within
+ // OnProcessUnexpectedShutdown.
+ if (mShutdownRequested) {
+ DestroyProcess();
+ }
+}
+
+void
+GPUProcessHost::KillHard(const char* aReason)
+{
+ ProcessHandle handle = GetChildProcessHandle();
+ if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ SetAlreadyDead();
+}
+
+uint64_t
+GPUProcessHost::GetProcessToken() const
+{
+ return mProcessToken;
+}
+
+static void
+DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
+{
+ XRE_GetIOMessageLoop()->
+ PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
+}
+
+void
+GPUProcessHost::KillProcess()
+{
+ KillHard("DiagnosticKill");
+}
+
+void
+GPUProcessHost::DestroyProcess()
+{
+ // Cancel all tasks. We don't want anything triggering after our caller
+ // expects this to go away.
+ {
+ MonitorAutoLock lock(mMonitor);
+ mTaskFactory.RevokeAll();
+ }
+
+ MessageLoop::current()->
+ PostTask(NewRunnableFunction(DelayedDeleteSubprocess, this));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/GPUProcessHost.h b/system/graphics/ipc/GPUProcessHost.h
new file mode 100644
index 000000000..8fefd757e
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessHost.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _include_mozilla_gfx_ipc_GPUProcessHost_h_
+#define _include_mozilla_gfx_ipc_GPUProcessHost_h_
+
+#include "mozilla/Function.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+class nsITimer;
+
+namespace mozilla {
+namespace gfx {
+
+class GPUChild;
+
+// GPUProcessHost is the "parent process" container for a subprocess handle and
+// IPC connection. It owns the parent process IPDL actor, which in this case,
+// is a GPUChild.
+//
+// GPUProcessHosts are allocated and managed by GPUProcessManager. For all
+// intents and purposes it is a singleton, though more than one may be allocated
+// at a time due to its shutdown being asynchronous.
+class GPUProcessHost final : public ipc::GeckoChildProcessHost
+{
+ friend class GPUChild;
+
+public:
+ class Listener {
+ public:
+ virtual void OnProcessLaunchComplete(GPUProcessHost* aHost)
+ {}
+
+ // The GPUProcessHost has unexpectedly shutdown or had its connection
+ // severed. This is not called if an error occurs after calling
+ // Shutdown().
+ virtual void OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
+ {}
+
+ virtual void OnProcessDeviceReset(GPUProcessHost* aHost)
+ {}
+ };
+
+public:
+ explicit GPUProcessHost(Listener* listener);
+ ~GPUProcessHost();
+
+ // Launch the subprocess asynchronously. On failure, false is returned.
+ // Otherwise, true is returned, and the OnLaunchComplete listener callback
+ // will be invoked either when a connection has been established, or if a
+ // connection could not be established due to an asynchronous error.
+ bool Launch();
+
+ // If the process is being launched, block until it has launched and
+ // connected. If a launch task is pending, it will fire immediately.
+ //
+ // Returns true if the process is successfully connected; false otherwise.
+ bool WaitForLaunch();
+
+ // Inform the process that it should clean up its resources and shut down.
+ // This initiates an asynchronous shutdown sequence. After this method returns,
+ // it is safe for the caller to forget its pointer to the GPUProcessHost.
+ //
+ // After this returns, the attached Listener is no longer used.
+ void Shutdown();
+
+ // Return the actor for the top-level actor of the process. If the process
+ // has not connected yet, this returns null.
+ GPUChild* GetActor() const {
+ return mGPUChild.get();
+ }
+
+ // Return a unique id for this process, guaranteed not to be shared with any
+ // past or future instance of GPUProcessHost.
+ uint64_t GetProcessToken() const;
+
+ bool IsConnected() const {
+ return !!mGPUChild;
+ }
+
+ // Return the time stamp for when we tried to launch the GPU process.
+ // Note this doesn't denote a successful launch, just when we attempted launch.
+ TimeStamp GetLaunchTime() const {
+ return mLaunchTime;
+ }
+
+ // Called on the IO thread.
+ void OnChannelConnected(int32_t peer_pid) override;
+ void OnChannelError() override;
+
+ void SetListener(Listener* aListener);
+
+ // Used for tests and diagnostics
+ void KillProcess();
+
+private:
+ // Called on the main thread.
+ void OnChannelConnectedTask();
+ void OnChannelErrorTask();
+
+ // Called on the main thread after a connection has been established.
+ void InitAfterConnect(bool aSucceeded);
+
+ // Called on the main thread when the mGPUChild actor is shutting down.
+ void OnChannelClosed();
+
+ // Kill the remote process, triggering IPC shutdown.
+ void KillHard(const char* aReason);
+
+ void DestroyProcess();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GPUProcessHost);
+
+ Listener* mListener;
+ ipc::TaskFactory<GPUProcessHost> mTaskFactory;
+
+ enum class LaunchPhase {
+ Unlaunched,
+ Waiting,
+ Complete
+ };
+ LaunchPhase mLaunchPhase;
+
+ UniquePtr<GPUChild> mGPUChild;
+ uint64_t mProcessToken;
+
+ bool mShutdownRequested;
+ bool mChannelClosed;
+
+ TimeStamp mLaunchTime;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_
diff --git a/system/graphics/ipc/GPUProcessImpl.cpp b/system/graphics/ipc/GPUProcessImpl.cpp
new file mode 100644
index 000000000..689b71ad1
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessImpl.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "GPUProcessImpl.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "nsXPCOM.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace ipc;
+
+GPUProcessImpl::GPUProcessImpl(ProcessId aParentPid)
+ : ProcessChild(aParentPid)
+{
+}
+
+GPUProcessImpl::~GPUProcessImpl()
+{
+}
+
+bool
+GPUProcessImpl::Init()
+{
+ return mGPU.Init(ParentPid(),
+ IOThreadChild::message_loop(),
+ IOThreadChild::channel());
+}
+
+void
+GPUProcessImpl::CleanUp()
+{
+ NS_ShutdownXPCOM(nullptr);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/GPUProcessImpl.h b/system/graphics/ipc/GPUProcessImpl.h
new file mode 100644
index 000000000..b22f00493
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessImpl.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_gfx_ipc_GPUProcessImpl_h__
+#define _include_gfx_ipc_GPUProcessImpl_h__
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "GPUParent.h"
+
+#if defined(XP_WIN)
+# include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+// This class owns the subprocess instance of a PGPU - which in this case,
+// is a GPUParent. It is instantiated as a singleton in XRE_InitChildProcess.
+class GPUProcessImpl final : public ipc::ProcessChild
+{
+public:
+ explicit GPUProcessImpl(ProcessId aParentPid);
+ ~GPUProcessImpl();
+
+ bool Init() override;
+ void CleanUp() override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GPUProcessImpl);
+
+ GPUParent mGPU;
+
+#if defined(XP_WIN)
+ // This object initializes and configures COM.
+ mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_gfx_ipc_GPUProcessImpl_h__
diff --git a/system/graphics/ipc/GPUProcessListener.h b/system/graphics/ipc/GPUProcessListener.h
new file mode 100644
index 000000000..eb9ada799
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessListener.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_GPUProcessListener_h_
+#define _include_mozilla_gfx_ipc_GPUProcessListener_h_
+
+namespace mozilla {
+namespace gfx {
+
+class GPUProcessListener
+{
+ public:
+ virtual ~GPUProcessListener()
+ {}
+
+ // Called when the compositor has died and the rendering stack must be
+ // recreated.
+ virtual void OnCompositorUnexpectedShutdown()
+ {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_GPUProcessListener_h_
diff --git a/system/graphics/ipc/GPUProcessManager.cpp b/system/graphics/ipc/GPUProcessManager.cpp
new file mode 100644
index 000000000..3a28a0390
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessManager.cpp
@@ -0,0 +1,787 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "GPUProcessManager.h"
+#include "GPUProcessHost.h"
+#include "GPUProcessListener.h"
+#include "GPUChild.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/InProcessCompositorSession.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/RemoteCompositorSession.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
+#include "gfxConfig.h"
+#include "nsBaseWidget.h"
+#include "nsContentUtils.h"
+#include "VsyncBridgeChild.h"
+#include "VsyncIOThreadHolder.h"
+#include "VsyncSource.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "MediaPrefs.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::widget;
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla::layers;
+
+static StaticAutoPtr<GPUProcessManager> sSingleton;
+
+GPUProcessManager*
+GPUProcessManager::Get()
+{
+ return sSingleton;
+}
+
+void
+GPUProcessManager::Initialize()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ sSingleton = new GPUProcessManager();
+}
+
+void
+GPUProcessManager::Shutdown()
+{
+ sSingleton = nullptr;
+}
+
+GPUProcessManager::GPUProcessManager()
+ : mTaskFactory(this),
+ mNextLayerTreeId(0),
+ mNumProcessAttempts(0),
+ mDeviceResetCount(0),
+ mProcess(nullptr),
+ mGPUChild(nullptr)
+{
+ MOZ_COUNT_CTOR(GPUProcessManager);
+
+ mObserver = new Observer(this);
+ nsContentUtils::RegisterShutdownObserver(mObserver);
+
+ mDeviceResetLastTime = TimeStamp::Now();
+
+ LayerTreeOwnerTracker::Initialize();
+}
+
+GPUProcessManager::~GPUProcessManager()
+{
+ MOZ_COUNT_DTOR(GPUProcessManager);
+
+ LayerTreeOwnerTracker::Shutdown();
+
+ // The GPU process should have already been shut down.
+ MOZ_ASSERT(!mProcess && !mGPUChild);
+
+ // We should have already removed observers.
+ MOZ_ASSERT(!mObserver);
+}
+
+NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver);
+
+GPUProcessManager::Observer::Observer(GPUProcessManager* aManager)
+ : mManager(aManager)
+{
+}
+
+NS_IMETHODIMP
+GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mManager->OnXPCOMShutdown();
+ }
+ return NS_OK;
+}
+
+void
+GPUProcessManager::OnXPCOMShutdown()
+{
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ mObserver = nullptr;
+ }
+
+ CleanShutdown();
+}
+
+void
+GPUProcessManager::LaunchGPUProcess()
+{
+ if (mProcess) {
+ return;
+ }
+
+ // Start the Vsync I/O thread so can use it as soon as the process launches.
+ EnsureVsyncIOThread();
+
+ mNumProcessAttempts++;
+
+ // The subprocess is launched asynchronously, so we wait for a callback to
+ // acquire the IPDL actor.
+ mProcess = new GPUProcessHost(this);
+ if (!mProcess->Launch()) {
+ DisableGPUProcess("Failed to launch GPU process");
+ }
+}
+
+void
+GPUProcessManager::DisableGPUProcess(const char* aMessage)
+{
+ if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+ return;
+ }
+
+ gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
+ gfxCriticalNote << aMessage;
+
+ DestroyProcess();
+ ShutdownVsyncIOThread();
+}
+
+void
+GPUProcessManager::EnsureGPUReady()
+{
+ if (mProcess && !mProcess->IsConnected()) {
+ if (!mProcess->WaitForLaunch()) {
+ // If this fails, we should have fired OnProcessLaunchComplete and
+ // removed the process.
+ MOZ_ASSERT(!mProcess && !mGPUChild);
+ return;
+ }
+ }
+
+ if (mGPUChild) {
+ mGPUChild->EnsureGPUReady();
+ }
+}
+
+void
+GPUProcessManager::EnsureImageBridgeChild()
+{
+ if (ImageBridgeChild::GetSingleton()) {
+ return;
+ }
+
+ EnsureGPUReady();
+
+ if (!mGPUChild) {
+ ImageBridgeChild::InitSameProcess();
+ return;
+ }
+
+ ipc::Endpoint<PImageBridgeParent> parentPipe;
+ ipc::Endpoint<PImageBridgeChild> childPipe;
+ nsresult rv = PImageBridge::CreateEndpoints(
+ mGPUChild->OtherPid(),
+ base::GetCurrentProcId(),
+ &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ DisableGPUProcess("Failed to create PImageBridge endpoints");
+ return;
+ }
+
+ mGPUChild->SendInitImageBridge(Move(parentPipe));
+ ImageBridgeChild::InitWithGPUProcess(Move(childPipe));
+}
+
+void
+GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
+{
+ MOZ_ASSERT(mProcess && mProcess == aHost);
+
+ if (!mProcess->IsConnected()) {
+ DisableGPUProcess("Failed to launch GPU process");
+ return;
+ }
+
+ mGPUChild = mProcess->GetActor();
+ mProcessToken = mProcess->GetProcessToken();
+
+ Endpoint<PVsyncBridgeParent> vsyncParent;
+ Endpoint<PVsyncBridgeChild> vsyncChild;
+ nsresult rv = PVsyncBridge::CreateEndpoints(
+ mGPUChild->OtherPid(),
+ base::GetCurrentProcId(),
+ &vsyncParent,
+ &vsyncChild);
+ if (NS_FAILED(rv)) {
+ DisableGPUProcess("Failed to create PVsyncBridge endpoints");
+ return;
+ }
+
+ mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, Move(vsyncChild));
+ mGPUChild->SendInitVsyncBridge(Move(vsyncParent));
+
+ nsTArray<LayerTreeIdMapping> mappings;
+ LayerTreeOwnerTracker::Get()->Iterate([&](uint64_t aLayersId, base::ProcessId aProcessId) {
+ mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId));
+ });
+ mGPUChild->SendAddLayerTreeIdMapping(mappings);
+}
+
+static bool
+ShouldLimitDeviceResets(uint32_t count, int32_t deltaMilliseconds)
+{
+ // We decide to limit by comparing the amount of resets that have happened
+ // and time since the last reset to two prefs.
+ int32_t timeLimit = gfxPrefs::DeviceResetThresholdMilliseconds();
+ int32_t countLimit = gfxPrefs::DeviceResetLimitCount();
+
+ bool hasTimeLimit = timeLimit != -1;
+ bool hasCountLimit = countLimit != -1;
+
+ bool triggeredTime = deltaMilliseconds < timeLimit;
+ bool triggeredCount = count > (uint32_t)countLimit;
+
+ // If we have both prefs set then it needs to trigger both limits,
+ // otherwise we only test the pref that is set or none
+ if (hasTimeLimit && hasCountLimit) {
+ return triggeredTime && triggeredCount;
+ } else if (hasTimeLimit) {
+ return triggeredTime;
+ } else if (hasCountLimit) {
+ return triggeredCount;
+ }
+
+ return false;
+}
+
+void
+GPUProcessManager::OnProcessDeviceReset(GPUProcessHost* aHost)
+{
+ // Detect whether the device is resetting too quickly or too much
+ // indicating that we should give up and use software
+ mDeviceResetCount++;
+
+ auto newTime = TimeStamp::Now();
+ auto delta = (int32_t)(newTime - mDeviceResetLastTime).ToMilliseconds();
+ mDeviceResetLastTime = newTime;
+
+ if (ShouldLimitDeviceResets(mDeviceResetCount, delta)) {
+ DestroyProcess();
+ DisableGPUProcess("GPU processed experienced too many device resets");
+
+ HandleProcessLost();
+ return;
+ }
+
+ // We're good, do a reset like normal
+ for (auto& session : mRemoteSessions) {
+ session->NotifyDeviceReset();
+ }
+}
+
+void
+GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
+{
+ MOZ_ASSERT(mProcess && mProcess == aHost);
+
+ DestroyProcess();
+
+ if (mNumProcessAttempts > uint32_t(gfxPrefs::GPUProcessDevMaxRestarts())) {
+ DisableGPUProcess("GPU processed crashed too many times");
+ }
+
+ HandleProcessLost();
+}
+
+void
+GPUProcessManager::HandleProcessLost()
+{
+ if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+ LaunchGPUProcess();
+ }
+
+ // The shutdown and restart sequence for the GPU process is as follows:
+ //
+ // (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on
+ // each channel owning a bridge to the GPU process, on the thread
+ // owning that channel.
+ //
+ // (2) The first channel to process its ActorDestroy message will post a
+ // message to the main thread to call NotifyRemoteActorDestroyed on
+ // the GPUProcessManager, which calls OnProcessUnexpectedShutdown if
+ // it has not handled shutdown for this process yet.
+ //
+ // (3) We then notify each widget that its session with the compositor is
+ // now invalid. The widget is responsible for destroying its layer
+ // manager and CompositorBridgeChild. Note that at this stage, not
+ // all actors may have received ActorDestroy yet. CompositorBridgeChild
+ // may attempt to send messages, and if this happens, it will probably
+ // report a MsgDropped error. This is okay.
+ //
+ // (4) At this point, the UI process has a clean slate: no layers should
+ // exist for the old compositor. We may make a decision on whether or
+ // not to re-launch the GPU process. Currently, we do not relaunch it,
+ // and any new compositors will be created in-process and will default
+ // to software.
+ //
+ // (5) Next we notify each ContentParent of the lost connection. It will
+ // request new endpoints from the GPUProcessManager and forward them
+ // to its ContentChild. The parent-side of these endpoints may come
+ // from the compositor thread of the UI process, or the compositor
+ // thread of the GPU process. However, no actual compositors should
+ // exist yet.
+ //
+ // (6) Each ContentChild will receive new endpoints. It will destroy its
+ // Compositor/ImageBridgeChild singletons and recreate them, as well
+ // as invalidate all retained layers.
+ //
+ // (7) In addition, each ContentChild will ask each of its TabChildren
+ // to re-request association with the compositor for the window
+ // owning the tab. The sequence of calls looks like:
+ // (a) [CONTENT] ContentChild::RecvReinitRendering
+ // (b) [CONTENT] TabChild::ReinitRendering
+ // (c) [CONTENT] TabChild::SendEnsureLayersConnected
+ // (d) [UI] TabParent::RecvEnsureLayersConnected
+ // (e) [UI] RenderFrameParent::EnsureLayersConnected
+ // (f) [UI] CompositorBridgeChild::SendNotifyChildRecreated
+ //
+ // Note that at step (e), RenderFrameParent will call GetLayerManager
+ // on the nsIWidget owning the tab. This step ensures that a compositor
+ // exists for the window. If we decided to launch a new GPU Process,
+ // at this point we block until the process has launched and we're
+ // able to create a new window compositor. Otherwise, if compositing
+ // is now in-process, this will simply create a new
+ // CompositorBridgeParent in the UI process. If there are multiple tabs
+ // in the same window, additional tabs will simply return the already-
+ // established compositor.
+ //
+ // Finally, this step serves one other crucial function: tabs must be
+ // associated with a window compositor or else they can't forward
+ // layer transactions. So this step both ensures that a compositor
+ // exists, and that the tab can forward layers.
+ //
+ // (8) Last, if the window had no remote tabs, step (7) will not have
+ // applied, and the window will not have a new compositor just yet.
+ // The next refresh tick and paint will ensure that one exists, again
+ // via nsIWidget::GetLayerManager.
+
+ // Build a list of sessions to notify, since notification might delete
+ // entries from the list.
+ nsTArray<RefPtr<RemoteCompositorSession>> sessions;
+ for (auto& session : mRemoteSessions) {
+ sessions.AppendElement(session);
+ }
+
+ // Notify each widget that we have lost the GPU process. This will ensure
+ // that each widget destroys its layer manager and CompositorBridgeChild.
+ for (const auto& session : sessions) {
+ session->NotifySessionLost();
+ }
+
+ // Notify content. This will ensure that each content process re-establishes
+ // a connection to the compositor thread (whether it's in-process or in a
+ // newly launched GPU process).
+ for (const auto& listener : mListeners) {
+ listener->OnCompositorUnexpectedShutdown();
+ }
+}
+
+void
+GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
+{
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
+ &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+
+ if (mProcessToken != aProcessToken) {
+ // This token is for an older process; we can safely ignore it.
+ return;
+ }
+
+ // One of the bridged top-level actors for the GPU process has been
+ // prematurely terminated, and we're receiving a notification. This
+ // can happen if the ActorDestroy for a bridged protocol fires
+ // before the ActorDestroy for PGPUChild.
+ OnProcessUnexpectedShutdown(mProcess);
+}
+
+void
+GPUProcessManager::CleanShutdown()
+{
+ DestroyProcess();
+ mVsyncIOThread = nullptr;
+}
+
+void
+GPUProcessManager::KillProcess()
+{
+ if (!mProcess) {
+ return;
+ }
+
+ mProcess->KillProcess();
+}
+
+void
+GPUProcessManager::DestroyProcess()
+{
+ if (!mProcess) {
+ return;
+ }
+
+ mProcess->Shutdown();
+ mProcessToken = 0;
+ mProcess = nullptr;
+ mGPUChild = nullptr;
+ if (mVsyncBridge) {
+ mVsyncBridge->Close();
+ mVsyncBridge = nullptr;
+ }
+}
+
+RefPtr<CompositorSession>
+GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
+ LayerManager* aLayerManager,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize)
+{
+ uint64_t layerTreeId = AllocateLayerTreeId();
+
+ EnsureGPUReady();
+ EnsureImageBridgeChild();
+
+ if (mGPUChild) {
+ RefPtr<CompositorSession> session = CreateRemoteSession(
+ aWidget,
+ aLayerManager,
+ layerTreeId,
+ aScale,
+ aUseAPZ,
+ aUseExternalSurfaceSize,
+ aSurfaceSize);
+ if (session) {
+ return session;
+ }
+
+ // We couldn't create a remote compositor, so abort the process.
+ DisableGPUProcess("Failed to create remote compositor");
+ }
+
+ return InProcessCompositorSession::Create(
+ aWidget,
+ aLayerManager,
+ layerTreeId,
+ aScale,
+ aUseAPZ,
+ aUseExternalSurfaceSize,
+ aSurfaceSize);
+}
+
+RefPtr<CompositorSession>
+GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget,
+ LayerManager* aLayerManager,
+ const uint64_t& aRootLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize)
+{
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+ ipc::Endpoint<PCompositorBridgeParent> parentPipe;
+ ipc::Endpoint<PCompositorBridgeChild> childPipe;
+
+ nsresult rv = PCompositorBridge::CreateEndpoints(
+ mGPUChild->OtherPid(),
+ base::GetCurrentProcId(),
+ &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Failed to create PCompositorBridge endpoints: " << hexa(int(rv));
+ return nullptr;
+ }
+
+ RefPtr<CompositorBridgeChild> child = CompositorBridgeChild::CreateRemote(
+ mProcessToken,
+ aLayerManager,
+ Move(childPipe));
+ if (!child) {
+ gfxCriticalNote << "Failed to create CompositorBridgeChild";
+ return nullptr;
+ }
+
+ CompositorWidgetInitData initData;
+ aWidget->GetCompositorWidgetInitData(&initData);
+
+ TimeDuration vsyncRate =
+ gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
+
+ bool ok = mGPUChild->SendNewWidgetCompositor(
+ Move(parentPipe),
+ aScale,
+ vsyncRate,
+ aUseExternalSurfaceSize,
+ aSurfaceSize);
+ if (!ok) {
+ return nullptr;
+ }
+
+ RefPtr<CompositorVsyncDispatcher> dispatcher = aWidget->GetCompositorVsyncDispatcher();
+ RefPtr<CompositorWidgetVsyncObserver> observer =
+ new CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId);
+
+ CompositorWidgetChild* widget = new CompositorWidgetChild(dispatcher, observer);
+ if (!child->SendPCompositorWidgetConstructor(widget, initData)) {
+ return nullptr;
+ }
+ if (!child->SendInitialize(aRootLayerTreeId)) {
+ return nullptr;
+ }
+
+ RefPtr<APZCTreeManagerChild> apz = nullptr;
+ if (aUseAPZ) {
+ PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(0);
+ if (!papz) {
+ return nullptr;
+ }
+ apz = static_cast<APZCTreeManagerChild*>(papz);
+ }
+
+ RefPtr<RemoteCompositorSession> session =
+ new RemoteCompositorSession(aWidget, child, widget, apz, aRootLayerTreeId);
+ return session.forget();
+#else
+ gfxCriticalNote << "Platform does not support out-of-process compositing";
+ return nullptr;
+#endif
+}
+
+bool
+GPUProcessManager::CreateContentBridges(base::ProcessId aOtherProcess,
+ ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
+ ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
+ ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager)
+{
+ if (!CreateContentCompositorBridge(aOtherProcess, aOutCompositor) ||
+ !CreateContentImageBridge(aOtherProcess, aOutImageBridge))
+ {
+ return false;
+ }
+ // VideoDeocderManager is only supported in the GPU process, so we allow this to be
+ // fallible.
+ CreateContentVideoDecoderManager(aOtherProcess, aOutVideoManager);
+ return true;
+}
+
+bool
+GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
+ ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint)
+{
+ EnsureGPUReady();
+
+ ipc::Endpoint<PCompositorBridgeParent> parentPipe;
+ ipc::Endpoint<PCompositorBridgeChild> childPipe;
+
+ base::ProcessId gpuPid = mGPUChild
+ ? mGPUChild->OtherPid()
+ : base::GetCurrentProcId();
+
+ nsresult rv = PCompositorBridge::CreateEndpoints(
+ gpuPid,
+ aOtherProcess,
+ &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv));
+ return false;
+ }
+
+ if (mGPUChild) {
+ mGPUChild->SendNewContentCompositorBridge(Move(parentPipe));
+ } else {
+ if (!CompositorBridgeParent::CreateForContent(Move(parentPipe))) {
+ return false;
+ }
+ }
+
+ *aOutEndpoint = Move(childPipe);
+ return true;
+}
+
+bool
+GPUProcessManager::CreateContentImageBridge(base::ProcessId aOtherProcess,
+ ipc::Endpoint<PImageBridgeChild>* aOutEndpoint)
+{
+ EnsureImageBridgeChild();
+
+ base::ProcessId gpuPid = mGPUChild
+ ? mGPUChild->OtherPid()
+ : base::GetCurrentProcId();
+
+ ipc::Endpoint<PImageBridgeParent> parentPipe;
+ ipc::Endpoint<PImageBridgeChild> childPipe;
+ nsresult rv = PImageBridge::CreateEndpoints(
+ gpuPid,
+ aOtherProcess,
+ &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv));
+ return false;
+ }
+
+ if (mGPUChild) {
+ mGPUChild->SendNewContentImageBridge(Move(parentPipe));
+ } else {
+ if (!ImageBridgeParent::CreateForContent(Move(parentPipe))) {
+ return false;
+ }
+ }
+
+ *aOutEndpoint = Move(childPipe);
+ return true;
+}
+
+base::ProcessId
+GPUProcessManager::GPUProcessPid()
+{
+ base::ProcessId gpuPid = mGPUChild
+ ? mGPUChild->OtherPid()
+ : -1;
+ return gpuPid;
+}
+
+void
+GPUProcessManager::CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
+ ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndpoint)
+{
+ if (!mGPUChild || !MediaPrefs::PDMUseGPUDecoder()) {
+ return;
+ }
+
+ ipc::Endpoint<dom::PVideoDecoderManagerParent> parentPipe;
+ ipc::Endpoint<dom::PVideoDecoderManagerChild> childPipe;
+
+ nsresult rv = dom::PVideoDecoderManager::CreateEndpoints(
+ mGPUChild->OtherPid(),
+ aOtherProcess,
+ &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Could not create content video decoder: " << hexa(int(rv));
+ return;
+ }
+
+ mGPUChild->SendNewContentVideoDecoderManager(Move(parentPipe));
+
+ *aOutEndpoint = Move(childPipe);
+ return;
+}
+
+already_AddRefed<IAPZCTreeManager>
+GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
+{
+ return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
+}
+
+void
+GPUProcessManager::MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId)
+{
+ LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId);
+
+ if (mGPUChild) {
+ AutoTArray<LayerTreeIdMapping, 1> mappings;
+ mappings.AppendElement(LayerTreeIdMapping(aLayersId, aOwningId));
+ mGPUChild->SendAddLayerTreeIdMapping(mappings);
+ }
+}
+
+void
+GPUProcessManager::UnmapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId)
+{
+ LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId);
+
+ if (mGPUChild) {
+ mGPUChild->SendRemoveLayerTreeIdMapping(LayerTreeIdMapping(aLayersId, aOwningId));
+ return;
+ }
+ CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
+}
+
+bool
+GPUProcessManager::IsLayerTreeIdMapped(uint64_t aLayersId, base::ProcessId aRequestingId)
+{
+ return LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, aRequestingId);
+}
+
+uint64_t
+GPUProcessManager::AllocateLayerTreeId()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return ++mNextLayerTreeId;
+}
+
+void
+GPUProcessManager::EnsureVsyncIOThread()
+{
+ if (mVsyncIOThread) {
+ return;
+ }
+
+ mVsyncIOThread = new VsyncIOThreadHolder();
+ MOZ_RELEASE_ASSERT(mVsyncIOThread->Start());
+}
+
+void
+GPUProcessManager::ShutdownVsyncIOThread()
+{
+ mVsyncIOThread = nullptr;
+}
+
+void
+GPUProcessManager::RegisterSession(RemoteCompositorSession* aSession)
+{
+ mRemoteSessions.AppendElement(aSession);
+}
+
+void
+GPUProcessManager::UnregisterSession(RemoteCompositorSession* aSession)
+{
+ mRemoteSessions.RemoveElement(aSession);
+}
+
+void
+GPUProcessManager::AddListener(GPUProcessListener* aListener)
+{
+ mListeners.AppendElement(aListener);
+}
+
+void
+GPUProcessManager::RemoveListener(GPUProcessListener* aListener)
+{
+ mListeners.RemoveElement(aListener);
+}
+
+bool
+GPUProcessManager::NotifyGpuObservers(const char* aTopic)
+{
+ if (!mGPUChild) {
+ return false;
+ }
+ nsCString topic(aTopic);
+ mGPUChild->SendNotifyGpuObservers(topic);
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/GPUProcessManager.h b/system/graphics/ipc/GPUProcessManager.h
new file mode 100644
index 000000000..5b75ea605
--- /dev/null
+++ b/system/graphics/ipc/GPUProcessManager.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_GPUProcessManager_h_
+#define _include_mozilla_gfx_ipc_GPUProcessManager_h_
+
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "Units.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/gfx/GPUProcessHost.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/TaskFactory.h"
+#include "mozilla/ipc/Transport.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+class nsBaseWidget;
+
+
+namespace mozilla {
+namespace layers {
+class IAPZCTreeManager;
+class CompositorSession;
+class CompositorUpdateObserver;
+class PCompositorBridgeChild;
+class PImageBridgeChild;
+class RemoteCompositorSession;
+} // namespace layers
+namespace widget {
+class CompositorWidget;
+} // namespace widget
+namespace dom {
+class ContentParent;
+class TabParent;
+class PVideoDecoderManagerChild;
+} // namespace dom
+namespace ipc {
+class GeckoChildProcessHost;
+} // namespace ipc
+namespace gfx {
+
+class GPUChild;
+class GPUProcessListener;
+class VsyncBridgeChild;
+class VsyncIOThreadHolder;
+
+// The GPUProcessManager is a singleton responsible for creating GPU-bound
+// objects that may live in another process. Currently, it provides access
+// to the compositor via CompositorBridgeParent.
+class GPUProcessManager final : public GPUProcessHost::Listener
+{
+ friend class layers::RemoteCompositorSession;
+
+ typedef layers::CompositorSession CompositorSession;
+ typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
+ typedef layers::IAPZCTreeManager IAPZCTreeManager;
+ typedef layers::LayerManager LayerManager;
+ typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
+ typedef layers::PImageBridgeChild PImageBridgeChild;
+ typedef layers::RemoteCompositorSession RemoteCompositorSession;
+
+public:
+ static void Initialize();
+ static void Shutdown();
+ static GPUProcessManager* Get();
+
+ ~GPUProcessManager();
+
+ // If not using a GPU process, launch a new GPU process asynchronously.
+ void LaunchGPUProcess();
+
+ // Ensure that GPU-bound methods can be used. If no GPU process is being
+ // used, or one is launched and ready, this function returns immediately.
+ // Otherwise it blocks until the GPU process has finished launching.
+ void EnsureGPUReady();
+
+ RefPtr<CompositorSession> CreateTopLevelCompositor(
+ nsBaseWidget* aWidget,
+ LayerManager* aLayerManager,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ bool CreateContentBridges(
+ base::ProcessId aOtherProcess,
+ ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
+ ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
+ ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager);
+
+ // This returns a reference to the APZCTreeManager to which
+ // pan/zoom-related events can be sent.
+ already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
+
+ // Maps the layer tree and process together so that aOwningPID is allowed
+ // to access aLayersId across process.
+ void MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId);
+
+ // Release compositor-thread resources referred to by |aID|.
+ //
+ // Must run on the content main thread.
+ void UnmapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId);
+
+ // Checks to see if aLayersId and aRequestingPID have been mapped by MapLayerTreeId
+ bool IsLayerTreeIdMapped(uint64_t aLayersId, base::ProcessId aRequestingId);
+
+ // Allocate an ID that can be used to refer to a layer tree and
+ // associated resources that live only on the compositor thread.
+ //
+ // Must run on the content main thread.
+ uint64_t AllocateLayerTreeId();
+
+
+ void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
+ void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
+ void OnProcessDeviceReset(GPUProcessHost* aHost) override;
+
+ // Notify the GPUProcessManager that a top-level PGPU protocol has been
+ // terminated. This may be called from any thread.
+ void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
+
+ void AddListener(GPUProcessListener* aListener);
+ void RemoveListener(GPUProcessListener* aListener);
+
+ // Send a message to the GPU process observer service to broadcast. Returns
+ // true if the message was sent, false if not.
+ bool NotifyGpuObservers(const char* aTopic);
+
+ // Used for tests and diagnostics
+ void KillProcess();
+
+ // Returns -1 if there is no GPU process, or the platform pid for it.
+ base::ProcessId GPUProcessPid();
+
+ // Returns access to the PGPU protocol if a GPU process is present.
+ GPUChild* GetGPUChild() {
+ return mGPUChild;
+ }
+
+ // Returns whether or not a GPU process was ever launched.
+ bool AttemptedGPUProcess() const {
+ return mNumProcessAttempts > 0;
+ }
+
+private:
+ // Called from our xpcom-shutdown observer.
+ void OnXPCOMShutdown();
+
+ bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
+ ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
+ bool CreateContentImageBridge(base::ProcessId aOtherProcess,
+ ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
+ void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
+ ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
+
+ // Called from RemoteCompositorSession. We track remote sessions so we can
+ // notify their owning widgets that the session must be restarted.
+ void RegisterSession(RemoteCompositorSession* aSession);
+ void UnregisterSession(RemoteCompositorSession* aSession);
+
+private:
+ GPUProcessManager();
+
+ // Permanently disable the GPU process and record a message why.
+ void DisableGPUProcess(const char* aMessage);
+
+ // Shutdown the GPU process.
+ void CleanShutdown();
+ void DestroyProcess();
+
+ void HandleProcessLost();
+
+ void EnsureVsyncIOThread();
+ void ShutdownVsyncIOThread();
+
+ void EnsureImageBridgeChild();
+
+ RefPtr<CompositorSession> CreateRemoteSession(
+ nsBaseWidget* aWidget,
+ LayerManager* aLayerManager,
+ const uint64_t& aRootLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ DISALLOW_COPY_AND_ASSIGN(GPUProcessManager);
+
+ class Observer final : public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ explicit Observer(GPUProcessManager* aManager);
+
+ protected:
+ ~Observer() {}
+
+ GPUProcessManager* mManager;
+ };
+ friend class Observer;
+
+private:
+ RefPtr<Observer> mObserver;
+ ipc::TaskFactory<GPUProcessManager> mTaskFactory;
+ RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
+ uint64_t mNextLayerTreeId;
+ uint32_t mNumProcessAttempts;
+
+ nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
+ nsTArray<GPUProcessListener*> mListeners;
+
+ uint32_t mDeviceResetCount;
+ TimeStamp mDeviceResetLastTime;
+
+ // Fields that are associated with the current GPU process.
+ GPUProcessHost* mProcess;
+ MOZ_INIT_OUTSIDE_CTOR uint64_t mProcessToken;
+ GPUChild* mGPUChild;
+ RefPtr<VsyncBridgeChild> mVsyncBridge;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_
diff --git a/system/graphics/ipc/GfxMessageUtils.h b/system/graphics/ipc/GfxMessageUtils.h
new file mode 100644
index 000000000..6a4a1a680
--- /dev/null
+++ b/system/graphics/ipc/GfxMessageUtils.h
@@ -0,0 +1,1272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __GFXMESSAGEUTILS_H__
+#define __GFXMESSAGEUTILS_H__
+
+#include "FilterSupport.h"
+#include "FrameMetrics.h"
+#include "ImageTypes.h"
+#include "RegionBuilder.h"
+#include "base/process_util.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "gfxPoint.h"
+#include "gfxRect.h"
+#include "gfxTelemetry.h"
+#include "gfxTypes.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsRect.h"
+#include "nsRegion.h"
+
+#include <stdint.h>
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4800 )
+#endif
+
+namespace mozilla {
+
+typedef gfxImageFormat PixelFormat;
+
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::gfx::Matrix>
+{
+ typedef mozilla::gfx::Matrix paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam._11);
+ WriteParam(aMsg, aParam._12);
+ WriteParam(aMsg, aParam._21);
+ WriteParam(aMsg, aParam._22);
+ WriteParam(aMsg, aParam._31);
+ WriteParam(aMsg, aParam._32);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ if (ReadParam(aMsg, aIter, &aResult->_11) &&
+ ReadParam(aMsg, aIter, &aResult->_12) &&
+ ReadParam(aMsg, aIter, &aResult->_21) &&
+ ReadParam(aMsg, aIter, &aResult->_22) &&
+ ReadParam(aMsg, aIter, &aResult->_31) &&
+ ReadParam(aMsg, aIter, &aResult->_32))
+ return true;
+
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"[[%g %g] [%g %g] [%g %g]]", aParam._11, aParam._12, aParam._21, aParam._22,
+ aParam._31, aParam._32));
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::Matrix4x4>
+{
+ typedef mozilla::gfx::Matrix4x4 paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+#define Wr(_f) WriteParam(msg, param. _f)
+ Wr(_11); Wr(_12); Wr(_13); Wr(_14);
+ Wr(_21); Wr(_22); Wr(_23); Wr(_24);
+ Wr(_31); Wr(_32); Wr(_33); Wr(_34);
+ Wr(_41); Wr(_42); Wr(_43); Wr(_44);
+#undef Wr
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+#define Rd(_f) ReadParam(msg, iter, &result-> _f)
+ return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) &&
+ Rd(_21) && Rd(_22) && Rd(_23) && Rd(_24) &&
+ Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) &&
+ Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44));
+#undef Rd
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::Matrix5x4>
+{
+ typedef mozilla::gfx::Matrix5x4 paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+#define Wr(_f) WriteParam(msg, param. _f)
+ Wr(_11); Wr(_12); Wr(_13); Wr(_14);
+ Wr(_21); Wr(_22); Wr(_23); Wr(_24);
+ Wr(_31); Wr(_32); Wr(_33); Wr(_34);
+ Wr(_41); Wr(_42); Wr(_43); Wr(_44);
+ Wr(_51); Wr(_52); Wr(_53); Wr(_54);
+#undef Wr
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+#define Rd(_f) ReadParam(msg, iter, &result-> _f)
+ return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) &&
+ Rd(_21) && Rd(_22) && Rd(_23) && Rd(_24) &&
+ Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) &&
+ Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44) &&
+ Rd(_51) && Rd(_52) && Rd(_53) && Rd(_54));
+#undef Rd
+ }
+};
+
+template<>
+struct ParamTraits<gfxPoint>
+{
+ typedef gfxPoint paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->x) &&
+ ReadParam(aMsg, aIter, &aResult->y));
+ }
+};
+
+template<>
+struct ParamTraits<gfxSize>
+{
+ typedef gfxSize paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.width);
+ WriteParam(aMsg, aParam.height);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ if (ReadParam(aMsg, aIter, &aResult->width) &&
+ ReadParam(aMsg, aIter, &aResult->height))
+ return true;
+
+ return false;
+ }
+};
+
+template<>
+struct ParamTraits<gfxRect>
+{
+ typedef gfxRect paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ WriteParam(aMsg, aParam.width);
+ WriteParam(aMsg, aParam.height);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->x) &&
+ ReadParam(aMsg, aIter, &aResult->y) &&
+ ReadParam(aMsg, aIter, &aResult->width) &&
+ ReadParam(aMsg, aIter, &aResult->height);
+ }
+};
+
+template <>
+struct ParamTraits<gfxContentType>
+ : public ContiguousEnumSerializer<
+ gfxContentType,
+ gfxContentType::COLOR,
+ gfxContentType::SENTINEL>
+{};
+
+template <>
+struct ParamTraits<gfxSurfaceType>
+ : public ContiguousEnumSerializer<
+ gfxSurfaceType,
+ gfxSurfaceType::Image,
+ gfxSurfaceType::Max>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::SamplingFilter>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::SamplingFilter,
+ mozilla::gfx::SamplingFilter::GOOD,
+ mozilla::gfx::SamplingFilter::SENTINEL>
+{};
+
+template <>
+struct ParamTraits<mozilla::layers::LayersBackend>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::LayersBackend,
+ mozilla::layers::LayersBackend::LAYERS_NONE,
+ mozilla::layers::LayersBackend::LAYERS_LAST>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::BackendType>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::BackendType,
+ mozilla::gfx::BackendType::NONE,
+ mozilla::gfx::BackendType::BACKEND_LAST>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::FeatureStatus>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::FeatureStatus,
+ mozilla::gfx::FeatureStatus::Unused,
+ mozilla::gfx::FeatureStatus::LAST>
+{};
+
+template <>
+struct ParamTraits<mozilla::layers::ScaleMode>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::ScaleMode,
+ mozilla::layers::ScaleMode::SCALE_NONE,
+ mozilla::layers::ScaleMode::SENTINEL>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::AttributeName>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::AttributeName,
+ mozilla::gfx::eBlendBlendmode,
+ mozilla::gfx::eLastAttributeName>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::AttributeType>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::AttributeType,
+ mozilla::gfx::AttributeType::eBool,
+ mozilla::gfx::AttributeType::Max>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::PrimitiveType>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::PrimitiveType,
+ mozilla::gfx::PrimitiveType::Empty,
+ mozilla::gfx::PrimitiveType::Max>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::ColorSpace>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::ColorSpace,
+ mozilla::gfx::ColorSpace::SRGB,
+ mozilla::gfx::ColorSpace::Max>
+{};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureFlags>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::TextureFlags,
+ mozilla::layers::TextureFlags::ALL_BITS>
+{};
+
+template <>
+struct ParamTraits<mozilla::layers::DiagnosticTypes>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::DiagnosticTypes,
+ mozilla::layers::DiagnosticTypes::ALL_BITS>
+{};
+
+/*
+template <>
+struct ParamTraits<mozilla::PixelFormat>
+ : public EnumSerializer<mozilla::PixelFormat,
+ SurfaceFormat::A8R8G8B8_UINT32,
+ SurfaceFormat::UNKNOWN>
+{};
+*/
+
+template<>
+struct ParamTraits<mozilla::gfx::Color>
+{
+ typedef mozilla::gfx::Color paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.r);
+ WriteParam(msg, param.g);
+ WriteParam(msg, param.b);
+ WriteParam(msg, param.a);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->r) &&
+ ReadParam(msg, iter, &result->g) &&
+ ReadParam(msg, iter, &result->b) &&
+ ReadParam(msg, iter, &result->a));
+ }
+};
+
+template<>
+struct ParamTraits<nsPoint>
+{
+ typedef nsPoint paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y));
+ }
+};
+
+template<>
+struct ParamTraits<nsIntPoint>
+{
+ typedef nsIntPoint paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y));
+ }
+};
+
+template<typename T>
+struct ParamTraits<mozilla::gfx::IntSizeTyped<T> >
+{
+ typedef mozilla::gfx::IntSizeTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<typename Region, typename Rect, typename Iter>
+struct RegionParamTraits
+{
+ typedef Region paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+
+ for (auto iter = param.RectIter(); !iter.Done(); iter.Next()) {
+ const Rect& r = iter.Get();
+ MOZ_RELEASE_ASSERT(!r.IsEmpty(), "GFX: rect is empty.");
+ WriteParam(msg, r);
+ }
+ // empty rects are sentinel values because nsRegions will never
+ // contain them
+ WriteParam(msg, Rect());
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ RegionBuilder<Region> builder;
+ Rect rect;
+ while (ReadParam(msg, iter, &rect)) {
+ if (rect.IsEmpty()) {
+ *result = builder.ToRegion();
+ return true;
+ }
+ builder.OrWith(rect);
+ }
+
+ return false;
+ }
+};
+
+template<class Units>
+struct ParamTraits<mozilla::gfx::IntRegionTyped<Units>>
+ : RegionParamTraits<mozilla::gfx::IntRegionTyped<Units>,
+ mozilla::gfx::IntRectTyped<Units>,
+ typename mozilla::gfx::IntRegionTyped<Units>::RectIterator>
+{};
+
+template<>
+struct ParamTraits<mozilla::gfx::IntSize>
+{
+ typedef mozilla::gfx::IntSize paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::CoordTyped<T> >
+{
+ typedef mozilla::gfx::CoordTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.value);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->value));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::IntCoordTyped<T> >
+{
+ typedef mozilla::gfx::IntCoordTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.value);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->value));
+ }
+};
+
+template<class T, class U>
+struct ParamTraits< mozilla::gfx::ScaleFactor<T, U> >
+{
+ typedef mozilla::gfx::ScaleFactor<T, U> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.scale);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->scale));
+ }
+};
+
+template<class T, class U>
+struct ParamTraits< mozilla::gfx::ScaleFactors2D<T, U> >
+{
+ typedef mozilla::gfx::ScaleFactors2D<T, U> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.xScale);
+ WriteParam(msg, param.yScale);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->xScale) &&
+ ReadParam(msg, iter, &result->yScale));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::PointTyped<T> >
+{
+ typedef mozilla::gfx::PointTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y));
+ }
+};
+
+template<class F, class T>
+struct ParamTraits< mozilla::gfx::Point3DTyped<F, T> >
+{
+ typedef mozilla::gfx::Point3DTyped<F, T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ WriteParam(msg, param.z);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y) &&
+ ReadParam(msg, iter, &result->z));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::IntPointTyped<T> >
+{
+ typedef mozilla::gfx::IntPointTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::SizeTyped<T> >
+{
+ typedef mozilla::gfx::SizeTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::RectTyped<T> >
+{
+ typedef mozilla::gfx::RectTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y) &&
+ ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::IntRectTyped<T> >
+{
+ typedef mozilla::gfx::IntRectTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y) &&
+ ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::Margin>
+{
+ typedef mozilla::gfx::Margin paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.top);
+ WriteParam(msg, param.right);
+ WriteParam(msg, param.bottom);
+ WriteParam(msg, param.left);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->top) &&
+ ReadParam(msg, iter, &result->right) &&
+ ReadParam(msg, iter, &result->bottom) &&
+ ReadParam(msg, iter, &result->left));
+ }
+};
+
+template<class T>
+struct ParamTraits< mozilla::gfx::MarginTyped<T> >
+{
+ typedef mozilla::gfx::MarginTyped<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.top);
+ WriteParam(msg, param.right);
+ WriteParam(msg, param.bottom);
+ WriteParam(msg, param.left);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->top) &&
+ ReadParam(msg, iter, &result->right) &&
+ ReadParam(msg, iter, &result->bottom) &&
+ ReadParam(msg, iter, &result->left));
+ }
+};
+
+template<>
+struct ParamTraits<nsRect>
+{
+ typedef nsRect paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.x);
+ WriteParam(msg, param.y);
+ WriteParam(msg, param.width);
+ WriteParam(msg, param.height);
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ return (ReadParam(msg, iter, &result->x) &&
+ ReadParam(msg, iter, &result->y) &&
+ ReadParam(msg, iter, &result->width) &&
+ ReadParam(msg, iter, &result->height));
+ }
+};
+
+template<>
+struct ParamTraits<nsRegion>
+ : RegionParamTraits<nsRegion, nsRect, nsRegion::RectIterator>
+{};
+
+template<>
+struct ParamTraits<mozilla::layers::FrameMetrics::ScrollOffsetUpdateType>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType,
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eNone,
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eSentinel>
+{};
+
+// Helper class for reading bitfields.
+// If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>.
+template <typename ParamType>
+struct BitfieldHelper
+{
+ // We need this helper because we can't get the address of a bitfield to
+ // pass directly to ReadParam. So instead we read it into a temporary bool
+ // and set the bitfield using a setter function
+ static bool ReadBoolForBitfield(const Message* aMsg, PickleIterator* aIter,
+ ParamType* aResult, void (ParamType::*aSetter)(bool))
+ {
+ bool value;
+ if (ReadParam(aMsg, aIter, &value)) {
+ (aResult->*aSetter)(value);
+ return true;
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FrameMetrics>
+ : BitfieldHelper<mozilla::layers::FrameMetrics>
+{
+ typedef mozilla::layers::FrameMetrics paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mScrollId);
+ WriteParam(aMsg, aParam.mPresShellResolution);
+ WriteParam(aMsg, aParam.mCompositionBounds);
+ WriteParam(aMsg, aParam.mDisplayPort);
+ WriteParam(aMsg, aParam.mCriticalDisplayPort);
+ WriteParam(aMsg, aParam.mScrollableRect);
+ WriteParam(aMsg, aParam.mCumulativeResolution);
+ WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
+ WriteParam(aMsg, aParam.mScrollOffset);
+ WriteParam(aMsg, aParam.mZoom);
+ WriteParam(aMsg, aParam.mScrollGeneration);
+ WriteParam(aMsg, aParam.mSmoothScrollOffset);
+ WriteParam(aMsg, aParam.mRootCompositionSize);
+ WriteParam(aMsg, aParam.mDisplayPortMargins);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mViewport);
+ WriteParam(aMsg, aParam.mExtraResolution);
+ WriteParam(aMsg, aParam.mPaintRequestTime);
+ WriteParam(aMsg, aParam.mScrollUpdateType);
+ WriteParam(aMsg, aParam.mIsRootContent);
+ WriteParam(aMsg, aParam.mDoSmoothScroll);
+ WriteParam(aMsg, aParam.mUseDisplayPortMargins);
+ WriteParam(aMsg, aParam.mIsScrollInfoLayer);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mScrollId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
+ ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
+ ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+ ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mViewport) &&
+ ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsRootContent) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetDoSmoothScroll) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUseDisplayPortMargins) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsScrollInfoLayer));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollSnapInfo>
+{
+ typedef mozilla::layers::ScrollSnapInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mScrollSnapTypeX);
+ WriteParam(aMsg, aParam.mScrollSnapTypeY);
+ WriteParam(aMsg, aParam.mScrollSnapIntervalX);
+ WriteParam(aMsg, aParam.mScrollSnapIntervalY);
+ WriteParam(aMsg, aParam.mScrollSnapDestination);
+ WriteParam(aMsg, aParam.mScrollSnapCoordinates);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeX) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeY) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalX) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalY) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapDestination) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapCoordinates));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::LayerClip>
+{
+ typedef mozilla::layers::LayerClip paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mClipRect);
+ WriteParam(aMsg, aParam.mMaskLayerIndex);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mClipRect) &&
+ ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollMetadata>
+ : BitfieldHelper<mozilla::layers::ScrollMetadata>
+{
+ typedef mozilla::layers::ScrollMetadata paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mMetrics);
+ WriteParam(aMsg, aParam.mSnapInfo);
+ WriteParam(aMsg, aParam.mScrollParentId);
+ WriteParam(aMsg, aParam.mBackgroundColor);
+ WriteParam(aMsg, aParam.GetContentDescription());
+ WriteParam(aMsg, aParam.mLineScrollAmount);
+ WriteParam(aMsg, aParam.mPageScrollAmount);
+ WriteParam(aMsg, aParam.mScrollClip);
+ WriteParam(aMsg, aParam.mHasScrollgrab);
+ WriteParam(aMsg, aParam.mAllowVerticalScrollWithWheel);
+ WriteParam(aMsg, aParam.mIsLayersIdRoot);
+ WriteParam(aMsg, aParam.mUsesContainerScrolling);
+ WriteParam(aMsg, aParam.mForceDisableApz);
+ }
+
+ static bool ReadContentDescription(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ nsCString str;
+ if (!ReadParam(aMsg, aIter, &str)) {
+ return false;
+ }
+ aResult->SetContentDescription(str);
+ return true;
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mMetrics) &&
+ ReadParam(aMsg, aIter, &aResult->mSnapInfo) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollParentId) &&
+ ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
+ ReadContentDescription(aMsg, aIter, aResult) &&
+ ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
+ ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollClip) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetHasScrollgrab) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetAllowVerticalScrollWithWheel) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsLayersIdRoot) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUsesContainerScrolling) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetForceDisableApz));
+ }
+};
+
+template<>
+struct ParamTraits<GeckoProcessType>
+ : public ContiguousEnumSerializer<
+ GeckoProcessType,
+ GeckoProcessType_Default,
+ GeckoProcessType_End>
+{};
+
+template<>
+struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
+{
+ typedef mozilla::layers::TextureFactoryIdentifier paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mParentBackend);
+ WriteParam(aMsg, aParam.mParentProcessType);
+ WriteParam(aMsg, aParam.mMaxTextureSize);
+ WriteParam(aMsg, aParam.mSupportsTextureBlitting);
+ WriteParam(aMsg, aParam.mSupportsPartialUploads);
+ WriteParam(aMsg, aParam.mSupportsComponentAlpha);
+ WriteParam(aMsg, aParam.mSyncHandle);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool result = ReadParam(aMsg, aIter, &aResult->mParentBackend) &&
+ ReadParam(aMsg, aIter, &aResult->mParentProcessType) &&
+ ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) &&
+ ReadParam(aMsg, aIter, &aResult->mSyncHandle);
+ return result;
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::layers::TextureInfo>
+{
+ typedef mozilla::layers::TextureInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mCompositableType);
+ WriteParam(aMsg, aParam.mTextureFlags);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->mCompositableType) &&
+ ReadParam(aMsg, aIter, &aResult->mTextureFlags);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableType>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::CompositableType,
+ mozilla::layers::CompositableType::UNKNOWN,
+ mozilla::layers::CompositableType::COUNT>
+{};
+
+template <>
+struct ParamTraits<mozilla::gfx::SurfaceFormat>
+ : public ContiguousEnumSerializer<
+ mozilla::gfx::SurfaceFormat,
+ mozilla::gfx::SurfaceFormat::B8G8R8A8,
+ mozilla::gfx::SurfaceFormat::UNKNOWN>
+{};
+
+template <>
+struct ParamTraits<mozilla::StereoMode>
+ : public ContiguousEnumSerializer<
+ mozilla::StereoMode,
+ mozilla::StereoMode::MONO,
+ mozilla::StereoMode::MAX>
+{};
+
+template <>
+struct ParamTraits<mozilla::YUVColorSpace>
+ : public ContiguousEnumSerializer<
+ mozilla::YUVColorSpace,
+ mozilla::YUVColorSpace::BT601,
+ mozilla::YUVColorSpace::UNKNOWN>
+{};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollableLayerGuid>
+{
+ typedef mozilla::layers::ScrollableLayerGuid paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mLayersId);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mScrollId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mLayersId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollId));
+ }
+};
+
+
+template <>
+struct ParamTraits<mozilla::layers::ZoomConstraints>
+{
+ typedef mozilla::layers::ZoomConstraints paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mAllowZoom);
+ WriteParam(aMsg, aParam.mAllowDoubleTapZoom);
+ WriteParam(aMsg, aParam.mMinZoom);
+ WriteParam(aMsg, aParam.mMaxZoom);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mAllowZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mAllowDoubleTapZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mMinZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mMaxZoom));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::EventRegions>
+{
+ typedef mozilla::layers::EventRegions paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mHitRegion);
+ WriteParam(aMsg, aParam.mDispatchToContentHitRegion);
+ WriteParam(aMsg, aParam.mNoActionRegion);
+ WriteParam(aMsg, aParam.mHorizontalPanRegion);
+ WriteParam(aMsg, aParam.mVerticalPanRegion);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mHitRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mNoActionRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mHorizontalPanRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mVerticalPanRegion));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::AttributeMap>
+{
+ typedef mozilla::gfx::AttributeMap paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.Count());
+ for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) {
+ mozilla::gfx::AttributeName name =
+ mozilla::gfx::AttributeName(iter.Key());
+ mozilla::gfx::AttributeType type =
+ mozilla::gfx::AttributeMap::GetType(iter.UserData());
+
+ WriteParam(aMsg, type);
+ WriteParam(aMsg, name);
+
+ switch (type) {
+
+#define CASE_TYPE(typeName) \
+ case mozilla::gfx::AttributeType::e##typeName: \
+ WriteParam(aMsg, aParam.Get##typeName(name)); \
+ break;
+
+ CASE_TYPE(Bool)
+ CASE_TYPE(Uint)
+ CASE_TYPE(Float)
+ CASE_TYPE(Size)
+ CASE_TYPE(IntSize)
+ CASE_TYPE(IntPoint)
+ CASE_TYPE(Matrix)
+ CASE_TYPE(Matrix5x4)
+ CASE_TYPE(Point3D)
+ CASE_TYPE(Color)
+ CASE_TYPE(AttributeMap)
+ CASE_TYPE(Floats)
+
+#undef CASE_TYPE
+
+ default:
+ MOZ_CRASH("GFX: unhandled attribute type");
+ }
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ uint32_t count;
+ if (!ReadParam(aMsg, aIter, &count)) {
+ return false;
+ }
+ for (uint32_t i = 0; i < count; i++) {
+ mozilla::gfx::AttributeType type;
+ if (!ReadParam(aMsg, aIter, &type)) {
+ return false;
+ }
+ mozilla::gfx::AttributeName name;
+ if (!ReadParam(aMsg, aIter, &name)) {
+ return false;
+ }
+ switch (type) {
+
+#define HANDLE_TYPE(type, typeName) \
+ case mozilla::gfx::AttributeType::e##typeName: \
+ { \
+ type value; \
+ if (!ReadParam(aMsg, aIter, &value)) { \
+ return false; \
+ } \
+ aResult->Set(name, value); \
+ break; \
+ }
+
+ HANDLE_TYPE(bool, Bool)
+ HANDLE_TYPE(uint32_t, Uint)
+ HANDLE_TYPE(float, Float)
+ HANDLE_TYPE(mozilla::gfx::Size, Size)
+ HANDLE_TYPE(mozilla::gfx::IntSize, IntSize)
+ HANDLE_TYPE(mozilla::gfx::IntPoint, IntPoint)
+ HANDLE_TYPE(mozilla::gfx::Matrix, Matrix)
+ HANDLE_TYPE(mozilla::gfx::Matrix5x4, Matrix5x4)
+ HANDLE_TYPE(mozilla::gfx::Point3D, Point3D)
+ HANDLE_TYPE(mozilla::gfx::Color, Color)
+ HANDLE_TYPE(mozilla::gfx::AttributeMap, AttributeMap)
+
+#undef HANDLE_TYPE
+
+ case mozilla::gfx::AttributeType::eFloats:
+ {
+ nsTArray<float> value;
+ if (!ReadParam(aMsg, aIter, &value)) {
+ return false;
+ }
+ aResult->Set(name, &value[0], value.Length());
+ break;
+ }
+ default:
+ MOZ_CRASH("GFX: unhandled attribute type");
+ }
+ }
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::FilterPrimitiveDescription>
+{
+ typedef mozilla::gfx::FilterPrimitiveDescription paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.Type());
+ WriteParam(aMsg, aParam.PrimitiveSubregion());
+ WriteParam(aMsg, aParam.FilterSpaceBounds());
+ WriteParam(aMsg, aParam.IsTainted());
+ WriteParam(aMsg, aParam.OutputColorSpace());
+ WriteParam(aMsg, aParam.NumberOfInputs());
+ for (size_t i = 0; i < aParam.NumberOfInputs(); i++) {
+ WriteParam(aMsg, aParam.InputPrimitiveIndex(i));
+ WriteParam(aMsg, aParam.InputColorSpace(i));
+ }
+ WriteParam(aMsg, aParam.Attributes());
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ mozilla::gfx::PrimitiveType type;
+ mozilla::gfx::IntRect primitiveSubregion;
+ mozilla::gfx::IntRect filterSpaceBounds;
+ bool isTainted = false;
+ mozilla::gfx::ColorSpace outputColorSpace;
+ size_t numberOfInputs = 0;
+ if (!ReadParam(aMsg, aIter, &type) ||
+ !ReadParam(aMsg, aIter, &primitiveSubregion) ||
+ !ReadParam(aMsg, aIter, &filterSpaceBounds) ||
+ !ReadParam(aMsg, aIter, &isTainted) ||
+ !ReadParam(aMsg, aIter, &outputColorSpace) ||
+ !ReadParam(aMsg, aIter, &numberOfInputs)) {
+ return false;
+ }
+
+ aResult->SetType(type);
+ aResult->SetPrimitiveSubregion(primitiveSubregion);
+ aResult->SetFilterSpaceBounds(filterSpaceBounds);
+ aResult->SetIsTainted(isTainted);
+ aResult->SetOutputColorSpace(outputColorSpace);
+
+ for (size_t i = 0; i < numberOfInputs; i++) {
+ int32_t inputPrimitiveIndex = 0;
+ mozilla::gfx::ColorSpace inputColorSpace;
+ if (!ReadParam(aMsg, aIter, &inputPrimitiveIndex) ||
+ !ReadParam(aMsg, aIter, &inputColorSpace)) {
+ return false;
+ }
+ aResult->SetInputPrimitive(i, inputPrimitiveIndex);
+ aResult->SetInputColorSpace(i, inputColorSpace);
+ }
+
+ return ReadParam(aMsg, aIter, &aResult->Attributes());
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::FilterDescription>
+{
+ typedef mozilla::gfx::FilterDescription paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mPrimitives);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mPrimitives));
+ }
+};
+
+typedef mozilla::layers::GeckoContentController::TapType TapType;
+
+template <>
+struct ParamTraits<TapType>
+ : public ContiguousEnumSerializer<
+ TapType,
+ TapType::eSingleTap,
+ TapType::eSentinel>
+{};
+
+typedef mozilla::layers::GeckoContentController::APZStateChange APZStateChange;
+
+template <>
+struct ParamTraits<APZStateChange>
+ : public ContiguousEnumSerializer<
+ APZStateChange,
+ APZStateChange::eTransformBegin,
+ APZStateChange::eSentinel>
+{};
+
+template<>
+struct ParamTraits<mozilla::layers::EventRegionsOverride>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::EventRegionsOverride,
+ mozilla::layers::EventRegionsOverride::ALL_BITS>
+{};
+
+template<>
+struct ParamTraits<mozilla::layers::AsyncDragMetrics::DragDirection>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::AsyncDragMetrics::DragDirection,
+ mozilla::layers::AsyncDragMetrics::DragDirection::NONE,
+ mozilla::layers::AsyncDragMetrics::DragDirection::SENTINEL>
+{};
+
+template<>
+struct ParamTraits<mozilla::layers::AsyncDragMetrics>
+{
+ typedef mozilla::layers::AsyncDragMetrics paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mViewId);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mDragStartSequenceNumber);
+ WriteParam(aMsg, aParam.mScrollbarDragOffset);
+ WriteParam(aMsg, aParam.mScrollTrack);
+ WriteParam(aMsg, aParam.mDirection);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mViewId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollTrack) &&
+ ReadParam(aMsg, aIter, &aResult->mDirection));
+ }
+};
+
+} /* namespace IPC */
+
+#endif /* __GFXMESSAGEUTILS_H__ */
diff --git a/system/graphics/ipc/GraphicsMessages.ipdlh b/system/graphics/ipc/GraphicsMessages.ipdlh
new file mode 100644
index 000000000..6da1cc142
--- /dev/null
+++ b/system/graphics/ipc/GraphicsMessages.ipdlh
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using mozilla::gfx::FeatureStatus from "gfxTelemetry.h";
+using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using gfxImageFormat from "mozilla/gfx/Types.h";
+
+namespace mozilla {
+namespace gfx {
+
+struct D3D11DeviceStatus
+{
+ bool isWARP;
+ bool textureSharingWorks;
+ uint32_t featureLevel;
+ DxgiAdapterDesc adapter;
+};
+
+struct DevicePrefs
+{
+ FeatureStatus hwCompositing;
+ FeatureStatus d3d11Compositing;
+ FeatureStatus oglCompositing;
+ FeatureStatus useD2D1;
+};
+
+struct ContentDeviceData
+{
+ DevicePrefs prefs;
+ D3D11DeviceStatus d3d11;
+};
+
+// Represents the state of a feature that has failed to initialize.
+struct FeatureFailure
+{
+ FeatureStatus status;
+ nsCString message;
+ nsCString failureId;
+};
+
+// If a feature state has changed from Enabled -> Failure, this will be non-
+// null.
+union FeatureChange
+{
+ null_t;
+ FeatureFailure;
+};
+
+union GPUDeviceStatus
+{
+ null_t;
+ D3D11DeviceStatus;
+};
+
+struct GPUDeviceData
+{
+ FeatureChange d3d11Compositing;
+ FeatureChange oglCompositing;
+ GPUDeviceStatus gpuDevice;
+};
+
+union GfxVarValue
+{
+ BackendType;
+ bool;
+ gfxImageFormat;
+ IntSize;
+ nsCString;
+};
+
+struct GfxVarUpdate
+{
+ size_t index;
+ GfxVarValue value;
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/InProcessCompositorSession.cpp b/system/graphics/ipc/InProcessCompositorSession.cpp
new file mode 100644
index 000000000..5f308df17
--- /dev/null
+++ b/system/graphics/ipc/InProcessCompositorSession.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InProcessCompositorSession.h"
+
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+// so we can cast an APZCTreeManager to an IAPZCTreeManager
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+
+namespace mozilla {
+namespace layers {
+
+InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidget* aWidget,
+ CompositorBridgeChild* aChild,
+ CompositorBridgeParent* aParent)
+ : CompositorSession(aWidget->AsDelegate(), aChild, aParent->RootLayerTreeId()),
+ mCompositorBridgeParent(aParent),
+ mCompositorWidget(aWidget)
+{
+}
+
+/* static */ RefPtr<InProcessCompositorSession>
+InProcessCompositorSession::Create(nsIWidget* aWidget,
+ LayerManager* aLayerManager,
+ const uint64_t& aRootLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize)
+{
+ CompositorWidgetInitData initData;
+ aWidget->GetCompositorWidgetInitData(&initData);
+
+ RefPtr<CompositorWidget> widget = CompositorWidget::CreateLocal(initData, aWidget);
+ RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager);
+ RefPtr<CompositorBridgeParent> parent =
+ child->InitSameProcess(widget, aRootLayerTreeId, aScale, aUseAPZ, aUseExternalSurfaceSize, aSurfaceSize);
+
+ return new InProcessCompositorSession(widget, child, parent);
+}
+
+CompositorBridgeParent*
+InProcessCompositorSession::GetInProcessBridge() const
+{
+ return mCompositorBridgeParent;
+}
+
+void
+InProcessCompositorSession::SetContentController(GeckoContentController* aController)
+{
+ mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController);
+}
+
+RefPtr<IAPZCTreeManager>
+InProcessCompositorSession::GetAPZCTreeManager() const
+{
+ return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
+}
+
+bool
+InProcessCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier)
+{
+ return mCompositorBridgeParent->ResetCompositor(aBackendHints, aOutIdentifier);
+}
+
+void
+InProcessCompositorSession::Shutdown()
+{
+ // Destroy will synchronously wait for the parent to acknowledge shutdown,
+ // at which point CBP will defer a Release on the compositor thread. We
+ // can safely release our reference now, and let the destructor run on either
+ // thread.
+ mCompositorBridgeChild->Destroy();
+ mCompositorBridgeChild = nullptr;
+ mCompositorBridgeParent = nullptr;
+ mCompositorWidget = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/ipc/InProcessCompositorSession.h b/system/graphics/ipc/InProcessCompositorSession.h
new file mode 100644
index 000000000..b599cdc1b
--- /dev/null
+++ b/system/graphics/ipc/InProcessCompositorSession.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
+#define _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
+
+#include "CompositorSession.h"
+#include "Layers.h"
+#include "mozilla/gfx/Point.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+// A CompositorSession where both the child and parent CompositorBridge reside
+// in the same process.
+class InProcessCompositorSession final : public CompositorSession
+{
+public:
+ static RefPtr<InProcessCompositorSession> Create(
+ nsIWidget* aWidget,
+ LayerManager* aLayerManager,
+ const uint64_t& aRootLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ CompositorBridgeParent* GetInProcessBridge() const override;
+ void SetContentController(GeckoContentController* aController) override;
+ RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
+ bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override;
+ void Shutdown() override;
+
+private:
+ InProcessCompositorSession(widget::CompositorWidget* aWidget,
+ CompositorBridgeChild* aChild,
+ CompositorBridgeParent* aParent);
+
+private:
+ RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+ RefPtr<CompositorWidget> mCompositorWidget;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_InProcessCompositorSession_h_
diff --git a/system/graphics/ipc/PGPU.ipdl b/system/graphics/ipc/PGPU.ipdl
new file mode 100644
index 000000000..db3f6e503
--- /dev/null
+++ b/system/graphics/ipc/PGPU.ipdl
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include GraphicsMessages;
+include protocol PCompositorBridge;
+include protocol PImageBridge;
+include protocol PVsyncBridge;
+include protocol PVideoDecoderManager;
+
+using base::ProcessId from "base/process.h";
+using mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using mozilla::CSSToLayoutDeviceScale from "Units.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+
+namespace mozilla {
+namespace gfx {
+
+union GfxPrefValue {
+ bool;
+ int32_t;
+ uint32_t;
+ float;
+};
+
+struct GfxPrefSetting {
+ int32_t index;
+ GfxPrefValue value;
+};
+
+struct LayerTreeIdMapping {
+ uint64_t layersId;
+ ProcessId ownerId;
+};
+
+sync protocol PGPU
+{
+parent:
+ // Sent by the UI process to initiate core settings.
+ async Init(GfxPrefSetting[] prefs,
+ GfxVarUpdate[] vars,
+ DevicePrefs devicePrefs);
+
+ async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
+ async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
+
+ // Called to update a gfx preference or variable.
+ async UpdatePref(GfxPrefSetting pref);
+ async UpdateVar(GfxVarUpdate var);
+
+ // Create a new top-level compositor.
+ async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
+ CSSToLayoutDeviceScale scale,
+ TimeDuration vsyncRate,
+ bool useExternalSurface,
+ IntSize surfaceSize);
+
+ // Create a new content-process compositor bridge.
+ async NewContentCompositorBridge(Endpoint<PCompositorBridgeParent> endpoint);
+ async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint);
+ async NewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent> endpoint);
+
+ // Called to notify the GPU process of who owns a layersId.
+ sync AddLayerTreeIdMapping(LayerTreeIdMapping[] mapping);
+ async RemoveLayerTreeIdMapping(LayerTreeIdMapping mapping);
+
+ // Request the current DeviceStatus from the GPU process. This blocks until
+ // one is available (i.e., Init has completed).
+ sync GetDeviceStatus() returns (GPUDeviceData status);
+
+ // Have a message be broadcasted to the GPU process by the GPU process
+ // observer service.
+ async NotifyGpuObservers(nsCString aTopic);
+
+child:
+ // Sent when the GPU process has initialized devices. This occurs once, after
+ // Init().
+ async InitComplete(GPUDeviceData data);
+
+ // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled.
+ async ReportCheckerboard(uint32_t severity, nsCString log);
+
+ // Graphics errors, analogous to PContent::GraphicsError
+ async GraphicsError(nsCString aError);
+
+ // Have a message be broadcasted to the UI process by the UI process
+ // observer service.
+ async NotifyUiObservers(nsCString aTopic);
+
+ async NotifyDeviceReset();
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/PVsyncBridge.ipdl b/system/graphics/ipc/PVsyncBridge.ipdl
new file mode 100644
index 000000000..03c68bbde
--- /dev/null
+++ b/system/graphics/ipc/PVsyncBridge.ipdl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+
+namespace mozilla {
+namespace gfx {
+
+// This protocol only serves one purpose: deliver vsync notifications from a
+// dedicated thread in the UI process to the compositor thread in the
+// compositor process. The child side exists in the UI process, and the
+// parent side in the GPU process.
+sync protocol PVsyncBridge
+{
+parent:
+ async NotifyVsync(TimeStamp vsyncTimeStamp, uint64_t layersId);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/RemoteCompositorSession.cpp b/system/graphics/ipc/RemoteCompositorSession.cpp
new file mode 100644
index 000000000..8a4a96375
--- /dev/null
+++ b/system/graphics/ipc/RemoteCompositorSession.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemoteCompositorSession.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/Unused.h"
+#include "nsBaseWidget.h"
+#include "GPUProcessManager.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace widget;
+
+RemoteCompositorSession::RemoteCompositorSession(nsBaseWidget* aWidget,
+ CompositorBridgeChild* aChild,
+ CompositorWidgetDelegate* aWidgetDelegate,
+ APZCTreeManagerChild* aAPZ,
+ const uint64_t& aRootLayerTreeId)
+ : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId),
+ mWidget(aWidget),
+ mAPZ(aAPZ)
+{
+ GPUProcessManager::Get()->RegisterSession(this);
+ if (mAPZ) {
+ mAPZ->SetCompositorSession(this);
+ }
+}
+
+RemoteCompositorSession::~RemoteCompositorSession()
+{
+ // This should have been shutdown first.
+ MOZ_ASSERT(!mCompositorBridgeChild);
+}
+
+void
+RemoteCompositorSession::NotifyDeviceReset()
+{
+ MOZ_ASSERT(mWidget);
+ mWidget->OnRenderingDeviceReset();
+}
+
+void
+RemoteCompositorSession::NotifySessionLost()
+{
+ // Re-entrancy should be impossible: when we are being notified of a lost
+ // session, we have by definition not shut down yet. We will shutdown, but
+ // then will be removed from the notification list.
+ MOZ_ASSERT(mWidget);
+ mWidget->NotifyRemoteCompositorSessionLost(this);
+}
+
+CompositorBridgeParent*
+RemoteCompositorSession::GetInProcessBridge() const
+{
+ return nullptr;
+}
+
+void
+RemoteCompositorSession::SetContentController(GeckoContentController* aController)
+{
+ mContentController = aController;
+ mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), 0);
+}
+
+GeckoContentController*
+RemoteCompositorSession::GetContentController()
+{
+ return mContentController.get();
+}
+
+nsIWidget*
+RemoteCompositorSession::GetWidget()
+{
+ return mWidget;
+}
+
+RefPtr<IAPZCTreeManager>
+RemoteCompositorSession::GetAPZCTreeManager() const
+{
+ return mAPZ;
+}
+
+bool
+RemoteCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier)
+{
+ bool didReset;
+ Unused << mCompositorBridgeChild->SendReset(aBackendHints, &didReset, aOutIdentifier);
+ return didReset;
+}
+
+void
+RemoteCompositorSession::Shutdown()
+{
+ mContentController = nullptr;
+ if (mAPZ) {
+ mAPZ->SetCompositorSession(nullptr);
+ }
+ mCompositorBridgeChild->Destroy();
+ mCompositorBridgeChild = nullptr;
+ mCompositorWidgetDelegate = nullptr;
+ mWidget = nullptr;
+ GPUProcessManager::Get()->UnregisterSession(this);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/ipc/RemoteCompositorSession.h b/system/graphics/ipc/RemoteCompositorSession.h
new file mode 100644
index 000000000..b4f3c9d80
--- /dev/null
+++ b/system/graphics/ipc/RemoteCompositorSession.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_mozilla_gfx_ipc_RemoteCompositorSession_h
+#define include_mozilla_gfx_ipc_RemoteCompositorSession_h
+
+#include "CompositorSession.h"
+#include "mozilla/gfx/Point.h"
+#include "Units.h"
+#include "nsBaseWidget.h"
+
+namespace mozilla {
+namespace layers {
+
+class RemoteCompositorSession final : public CompositorSession
+{
+public:
+ RemoteCompositorSession(nsBaseWidget* aWidget,
+ CompositorBridgeChild* aChild,
+ CompositorWidgetDelegate* aWidgetDelegate,
+ APZCTreeManagerChild* aAPZ,
+ const uint64_t& aRootLayerTreeId);
+ ~RemoteCompositorSession() override;
+
+ CompositorBridgeParent* GetInProcessBridge() const override;
+ void SetContentController(GeckoContentController* aController) override;
+ GeckoContentController* GetContentController();
+ nsIWidget* GetWidget();
+ RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
+ bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override;
+ void Shutdown() override;
+
+ void NotifyDeviceReset();
+ void NotifySessionLost();
+
+private:
+ nsBaseWidget* mWidget;
+ RefPtr<APZCTreeManagerChild> mAPZ;
+ RefPtr<GeckoContentController> mContentController;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h
diff --git a/system/graphics/ipc/SharedDIB.cpp b/system/graphics/ipc/SharedDIB.cpp
new file mode 100644
index 000000000..ec68a61fa
--- /dev/null
+++ b/system/graphics/ipc/SharedDIB.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedDIB.h"
+
+namespace mozilla {
+namespace gfx {
+
+SharedDIB::SharedDIB() :
+ mShMem(nullptr)
+{
+}
+
+SharedDIB::~SharedDIB()
+{
+ Close();
+}
+
+nsresult
+SharedDIB::Create(uint32_t aSize)
+{
+ Close();
+
+ mShMem = new base::SharedMemory();
+ if (!mShMem || !mShMem->Create("", false, false, aSize))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+bool
+SharedDIB::IsValid()
+{
+ if (!mShMem)
+ return false;
+
+ return mShMem->IsHandleValid(mShMem->handle());
+}
+
+nsresult
+SharedDIB::Close()
+{
+ delete mShMem;
+
+ mShMem = nullptr;
+
+ return NS_OK;
+}
+
+nsresult
+SharedDIB::Attach(Handle aHandle, uint32_t aSize)
+{
+ Close();
+
+ mShMem = new base::SharedMemory(aHandle, false);
+ if(!mShMem)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+nsresult
+SharedDIB::ShareToProcess(base::ProcessId aTargetPid, Handle *aNewHandle)
+{
+ if (!mShMem)
+ return NS_ERROR_UNEXPECTED;
+
+ if (!mShMem->ShareToProcess(aTargetPid, aNewHandle))
+ return NS_ERROR_UNEXPECTED;
+
+ return NS_OK;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/SharedDIB.h b/system/graphics/ipc/SharedDIB.h
new file mode 100644
index 000000000..6c48b2875
--- /dev/null
+++ b/system/graphics/ipc/SharedDIB.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_SharedDIB_h__
+#define gfx_SharedDIB_h__
+
+#include "base/shared_memory.h"
+#include "nscore.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SharedDIB
+{
+public:
+ typedef base::SharedMemoryHandle Handle;
+
+ static const uint32_t kBytesPerPixel = 4;
+
+public:
+ SharedDIB();
+ ~SharedDIB();
+
+ // Create and allocate a new shared dib.
+ nsresult Create(uint32_t aSize);
+
+ // Destroy or release resources associated with this dib.
+ nsresult Close();
+
+ // Returns true if this object contains a valid dib.
+ bool IsValid();
+
+ // Wrap a new shared dib around allocated shared memory. Note aHandle must point
+ // to a memory section large enough to hold a dib of size aSize, otherwise this
+ // will fail.
+ nsresult Attach(Handle aHandle, uint32_t aSize);
+
+ // Returns a SharedMemoryHandle suitable for sharing with another process.
+ nsresult ShareToProcess(base::ProcessId aTargetPid, Handle *aNewHandle);
+
+protected:
+ base::SharedMemory *mShMem;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/ipc/SharedDIBSurface.cpp b/system/graphics/ipc/SharedDIBSurface.cpp
new file mode 100644
index 000000000..696bb300c
--- /dev/null
+++ b/system/graphics/ipc/SharedDIBSurface.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedDIBSurface.h"
+
+#include "cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const cairo_user_data_key_t SHAREDDIB_KEY = {0};
+
+bool
+SharedDIBSurface::Create(HDC adc, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent)
+{
+ nsresult rv = mSharedDIB.Create(adc, aWidth, aHeight, aTransparent);
+ if (NS_FAILED(rv) || !mSharedDIB.IsValid())
+ return false;
+
+ InitSurface(aWidth, aHeight, aTransparent);
+ return true;
+}
+
+bool
+SharedDIBSurface::Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent)
+{
+ nsresult rv = mSharedDIB.Attach(aHandle, aWidth, aHeight, aTransparent);
+ if (NS_FAILED(rv) || !mSharedDIB.IsValid())
+ return false;
+
+ InitSurface(aWidth, aHeight, aTransparent);
+ return true;
+}
+
+void
+SharedDIBSurface::InitSurface(uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent)
+{
+ long stride = long(aWidth * SharedDIB::kBytesPerPixel);
+ unsigned char* data = reinterpret_cast<unsigned char*>(mSharedDIB.GetBits());
+
+ gfxImageFormat format = aTransparent ? SurfaceFormat::A8R8G8B8_UINT32 : SurfaceFormat::X8R8G8B8_UINT32;
+
+ gfxImageSurface::InitWithData(data, IntSize(aWidth, aHeight),
+ stride, format);
+
+ cairo_surface_set_user_data(mSurface, &SHAREDDIB_KEY, this, nullptr);
+}
+
+bool
+SharedDIBSurface::IsSharedDIBSurface(gfxASurface* aSurface)
+{
+ return aSurface &&
+ aSurface->GetType() == gfxSurfaceType::Image &&
+ aSurface->GetData(&SHAREDDIB_KEY);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/SharedDIBSurface.h b/system/graphics/ipc/SharedDIBSurface.h
new file mode 100644
index 000000000..79f0c3bbe
--- /dev/null
+++ b/system/graphics/ipc/SharedDIBSurface.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_SharedDIBSurface_h
+#define mozilla_gfx_SharedDIBSurface_h
+
+#include "gfxImageSurface.h"
+#include "SharedDIBWin.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * A SharedDIBSurface owns an underlying SharedDIBWin.
+ */
+class SharedDIBSurface : public gfxImageSurface
+{
+public:
+ typedef base::SharedMemoryHandle Handle;
+
+ SharedDIBSurface() { }
+ ~SharedDIBSurface() { }
+
+ /**
+ * Create this image surface backed by shared memory.
+ */
+ bool Create(HDC adc, uint32_t aWidth, uint32_t aHeight, bool aTransparent);
+
+ /**
+ * Attach this surface to shared memory from another process.
+ */
+ bool Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent);
+
+ /**
+ * After drawing to a surface via GDI, GDI must be flushed before the bitmap
+ * is valid.
+ */
+ void Flush() { ::GdiFlush(); }
+
+ HDC GetHDC() { return mSharedDIB.GetHDC(); }
+
+ nsresult ShareToProcess(base::ProcessId aTargetPid, Handle* aNewHandle) {
+ return mSharedDIB.ShareToProcess(aTargetPid, aNewHandle);
+ }
+
+ static bool IsSharedDIBSurface(gfxASurface* aSurface);
+
+private:
+ SharedDIBWin mSharedDIB;
+
+ void InitSurface(uint32_t aWidth, uint32_t aHeight, bool aTransparent);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_SharedDIBSurface_h
diff --git a/system/graphics/ipc/SharedDIBWin.cpp b/system/graphics/ipc/SharedDIBWin.cpp
new file mode 100644
index 000000000..197e197d4
--- /dev/null
+++ b/system/graphics/ipc/SharedDIBWin.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedDIBWin.h"
+#include "gfxAlphaRecovery.h"
+#include "nsMathUtils.h"
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const uint32_t kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2();
+static const uint32_t kHeaderBytes =
+ (uint32_t(sizeof(BITMAPV4HEADER)) + kByteAlign - 1) & ~(kByteAlign - 1);
+
+SharedDIBWin::SharedDIBWin() :
+ mSharedHdc(nullptr)
+ , mSharedBmp(nullptr)
+ , mOldObj(nullptr)
+{
+}
+
+SharedDIBWin::~SharedDIBWin()
+{
+ Close();
+}
+
+nsresult
+SharedDIBWin::Close()
+{
+ if (mSharedHdc && mOldObj)
+ ::SelectObject(mSharedHdc, mOldObj);
+
+ if (mSharedHdc)
+ ::DeleteObject(mSharedHdc);
+
+ if (mSharedBmp)
+ ::DeleteObject(mSharedBmp);
+
+ mSharedHdc = nullptr;
+ mOldObj = mSharedBmp = nullptr;
+
+ SharedDIB::Close();
+
+ return NS_OK;
+}
+
+nsresult
+SharedDIBWin::Create(HDC aHdc, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent)
+{
+ Close();
+
+ // create the offscreen shared dib
+ BITMAPV4HEADER bmih;
+ uint32_t size = SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih);
+
+ nsresult rv = SharedDIB::Create(size);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (NS_FAILED(SetupSurface(aHdc, &bmih))) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+SharedDIBWin::Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent)
+{
+ Close();
+
+ BITMAPV4HEADER bmih;
+ SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih);
+
+ nsresult rv = SharedDIB::Attach(aHandle, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (NS_FAILED(SetupSurface(nullptr, &bmih))) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+uint32_t
+SharedDIBWin::SetupBitmapHeader(uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent, BITMAPV4HEADER *aHeader)
+{
+ // D3D cannot handle an offscreen memory that pitch (SysMemPitch) is negative.
+ // So we create top-to-bottom DIB.
+ memset((void*)aHeader, 0, sizeof(BITMAPV4HEADER));
+ aHeader->bV4Size = sizeof(BITMAPV4HEADER);
+ aHeader->bV4Width = aWidth;
+ aHeader->bV4Height = -LONG(aHeight); // top-to-buttom DIB
+ aHeader->bV4Planes = 1;
+ aHeader->bV4BitCount = 32;
+ aHeader->bV4V4Compression = BI_BITFIELDS;
+ aHeader->bV4RedMask = 0x00FF0000;
+ aHeader->bV4GreenMask = 0x0000FF00;
+ aHeader->bV4BlueMask = 0x000000FF;
+
+ if (aTransparent)
+ aHeader->bV4AlphaMask = 0xFF000000;
+
+ return (kHeaderBytes + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel));
+}
+
+nsresult
+SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr)
+{
+ mSharedHdc = ::CreateCompatibleDC(aHdc);
+
+ if (!mSharedHdc)
+ return NS_ERROR_FAILURE;
+
+ mSharedBmp = ::CreateDIBSection(mSharedHdc,
+ (BITMAPINFO*)aHdr,
+ DIB_RGB_COLORS,
+ &mBitmapBits,
+ mShMem->handle(),
+ kHeaderBytes);
+ if (!mSharedBmp)
+ return NS_ERROR_FAILURE;
+
+ mOldObj = SelectObject(mSharedHdc, mSharedBmp);
+
+ return NS_OK;
+}
+
+} // gfx
+} // mozilla
diff --git a/system/graphics/ipc/SharedDIBWin.h b/system/graphics/ipc/SharedDIBWin.h
new file mode 100644
index 000000000..06759662b
--- /dev/null
+++ b/system/graphics/ipc/SharedDIBWin.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_SharedDIBWin_h__
+#define gfx_SharedDIBWin_h__
+
+#include <windows.h>
+
+#include "SharedDIB.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SharedDIBWin : public SharedDIB
+{
+public:
+ SharedDIBWin();
+ ~SharedDIBWin();
+
+ // Allocate a new win32 dib section compatible with an hdc. The dib will
+ // be selected into the hdc on return.
+ nsresult Create(HDC aHdc, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent);
+
+ // Wrap a dib section around an existing shared memory object. aHandle should
+ // point to a section large enough for the dib's memory, otherwise this call
+ // will fail.
+ nsresult Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent);
+
+ // Destroy or release resources associated with this dib.
+ nsresult Close();
+
+ // Return the HDC of the shared dib.
+ HDC GetHDC() { return mSharedHdc; }
+
+ // Return the bitmap bits.
+ void* GetBits() { return mBitmapBits; }
+
+private:
+ HDC mSharedHdc;
+ HBITMAP mSharedBmp;
+ HGDIOBJ mOldObj;
+ void* mBitmapBits;
+
+ uint32_t SetupBitmapHeader(uint32_t aWidth, uint32_t aHeight,
+ bool aTransparent, BITMAPV4HEADER *aHeader);
+ nsresult SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr);
+};
+
+} // gfx
+} // mozilla
+
+#endif
diff --git a/system/graphics/ipc/VsyncBridgeChild.cpp b/system/graphics/ipc/VsyncBridgeChild.cpp
new file mode 100644
index 000000000..8cf0ec8e0
--- /dev/null
+++ b/system/graphics/ipc/VsyncBridgeChild.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "VsyncBridgeChild.h"
+#include "VsyncIOThreadHolder.h"
+#include "mozilla/dom/ContentChild.h"
+#include "GPUProcessManager.h"
+
+namespace mozilla {
+namespace gfx {
+
+VsyncBridgeChild::VsyncBridgeChild(RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken)
+ : mThread(aThread),
+ mLoop(nullptr),
+ mProcessToken(aProcessToken)
+{
+}
+
+VsyncBridgeChild::~VsyncBridgeChild()
+{
+}
+
+/* static */ RefPtr<VsyncBridgeChild>
+VsyncBridgeChild::Create(RefPtr<VsyncIOThreadHolder> aThread,
+ const uint64_t& aProcessToken,
+ Endpoint<PVsyncBridgeChild>&& aEndpoint)
+{
+ RefPtr<VsyncBridgeChild> child = new VsyncBridgeChild(aThread, aProcessToken);
+
+ RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeChild>&&>(
+ child, &VsyncBridgeChild::Open, Move(aEndpoint));
+ aThread->GetThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+
+ return child;
+}
+
+void
+VsyncBridgeChild::Open(Endpoint<PVsyncBridgeChild>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ // The GPU Process Manager might be gone if we receive ActorDestroy very
+ // late in shutdown.
+ if (GPUProcessManager* gpm = GPUProcessManager::Get())
+ gpm->NotifyRemoteActorDestroyed(mProcessToken);
+ return;
+ }
+
+ mLoop = MessageLoop::current();
+
+ // Last reference is freed in DeallocPVsyncBridgeChild.
+ AddRef();
+}
+
+class NotifyVsyncTask : public Runnable
+{
+public:
+ NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge,
+ TimeStamp aTimeStamp,
+ const uint64_t& aLayersId)
+ : mVsyncBridge(aVsyncBridge),
+ mTimeStamp(aTimeStamp),
+ mLayersId(aLayersId)
+ {}
+
+ NS_IMETHOD Run() override {
+ mVsyncBridge->NotifyVsyncImpl(mTimeStamp, mLayersId);
+ return NS_OK;
+ }
+
+private:
+ RefPtr<VsyncBridgeChild> mVsyncBridge;
+ TimeStamp mTimeStamp;
+ uint64_t mLayersId;
+};
+
+bool
+VsyncBridgeChild::IsOnVsyncIOThread() const
+{
+ return MessageLoop::current() == mLoop;
+}
+
+void
+VsyncBridgeChild::NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId)
+{
+ // This should be on the Vsync thread (not the Vsync I/O thread).
+ MOZ_ASSERT(!IsOnVsyncIOThread());
+
+ RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aTimeStamp, aLayersId);
+ mLoop->PostTask(task.forget());
+}
+
+void
+VsyncBridgeChild::NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId)
+{
+ // This should be on the Vsync I/O thread.
+ MOZ_ASSERT(IsOnVsyncIOThread());
+
+ if (!mProcessToken) {
+ return;
+ }
+ SendNotifyVsync(aTimeStamp, aLayersId);
+}
+
+void
+VsyncBridgeChild::Close()
+{
+ if (!IsOnVsyncIOThread()) {
+ mLoop->PostTask(NewRunnableMethod(this, &VsyncBridgeChild::Close));
+ return;
+ }
+
+ // We clear mProcessToken when the channel is closed.
+ if (!mProcessToken) {
+ return;
+ }
+
+ // Clear the process token so we don't notify the GPUProcessManager. It already
+ // knows we're closed since it manually called Close, and in fact the GPM could
+ // have already been destroyed during shutdown.
+ mProcessToken = 0;
+
+ // Close the underlying IPC channel.
+ PVsyncBridgeChild::Close();
+}
+
+void
+VsyncBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mProcessToken) {
+ GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ mProcessToken = 0;
+ }
+}
+
+void
+VsyncBridgeChild::DeallocPVsyncBridgeChild()
+{
+ Release();
+}
+
+void
+VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason)
+{
+ MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in VsyncBridgeChild");
+}
+
+void
+VsyncBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/VsyncBridgeChild.h b/system/graphics/ipc/VsyncBridgeChild.h
new file mode 100644
index 000000000..309cb0ab5
--- /dev/null
+++ b/system/graphics/ipc/VsyncBridgeChild.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_VsyncBridgeChild_h
+#define include_gfx_ipc_VsyncBridgeChild_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVsyncBridgeChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncIOThreadHolder;
+
+class VsyncBridgeChild final : public PVsyncBridgeChild
+{
+ friend class NotifyVsyncTask;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeChild)
+
+ static RefPtr<VsyncBridgeChild> Create(RefPtr<VsyncIOThreadHolder> aThread,
+ const uint64_t& aProcessToken,
+ Endpoint<PVsyncBridgeChild>&& aEndpoint);
+
+ void Close();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPVsyncBridgeChild() override;
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ void NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId);
+
+ virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+private:
+ VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken);
+ ~VsyncBridgeChild();
+
+ void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint);
+
+ void NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId);
+
+ bool IsOnVsyncIOThread() const;
+
+private:
+ RefPtr<VsyncIOThreadHolder> mThread;
+ MessageLoop* mLoop;
+ uint64_t mProcessToken;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // include_gfx_ipc_VsyncBridgeChild_h
diff --git a/system/graphics/ipc/VsyncBridgeParent.cpp b/system/graphics/ipc/VsyncBridgeParent.cpp
new file mode 100644
index 000000000..a5c81a7ef
--- /dev/null
+++ b/system/graphics/ipc/VsyncBridgeParent.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "VsyncBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+namespace gfx {
+
+RefPtr<VsyncBridgeParent>
+VsyncBridgeParent::Start(Endpoint<PVsyncBridgeParent>&& aEndpoint)
+{
+ RefPtr<VsyncBridgeParent> parent = new VsyncBridgeParent();
+
+ RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeParent>&&>(
+ parent, &VsyncBridgeParent::Open, Move(aEndpoint));
+ CompositorThreadHolder::Loop()->PostTask(task.forget());
+
+ return parent;
+}
+
+VsyncBridgeParent::VsyncBridgeParent()
+ : mOpen(false)
+{
+ MOZ_COUNT_CTOR(VsyncBridgeParent);
+}
+
+VsyncBridgeParent::~VsyncBridgeParent()
+{
+ MOZ_COUNT_DTOR(VsyncBridgeParent);
+}
+
+void
+VsyncBridgeParent::Open(Endpoint<PVsyncBridgeParent>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind VsyncBridgeParent to endpoint");
+ }
+ AddRef();
+ mOpen = true;
+}
+
+bool
+VsyncBridgeParent::RecvNotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId)
+{
+ CompositorBridgeParent::NotifyVsync(aTimeStamp, aLayersId);
+ return true;
+}
+
+void
+VsyncBridgeParent::Shutdown()
+{
+ MessageLoop* ccloop = CompositorThreadHolder::Loop();
+ if (MessageLoop::current() != ccloop) {
+ ccloop->PostTask(NewRunnableMethod(this, &VsyncBridgeParent::ShutdownImpl));
+ return;
+ }
+
+ ShutdownImpl();
+}
+
+void
+VsyncBridgeParent::ShutdownImpl()
+{
+ if (mOpen) {
+ Close();
+ mOpen = false;
+ }
+}
+
+void
+VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mOpen = false;
+}
+
+void
+VsyncBridgeParent::DeallocPVsyncBridgeParent()
+{
+ Release();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/VsyncBridgeParent.h b/system/graphics/ipc/VsyncBridgeParent.h
new file mode 100644
index 000000000..1458585ef
--- /dev/null
+++ b/system/graphics/ipc/VsyncBridgeParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_VsyncBridgeParent_h
+#define include_gfx_ipc_VsyncBridgeParent_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVsyncBridgeParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncBridgeParent final : public PVsyncBridgeParent
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent)
+
+ static RefPtr<VsyncBridgeParent> Start(Endpoint<PVsyncBridgeParent>&& aEndpoint);
+
+ bool RecvNotifyVsync(const TimeStamp& vsyncTimeStamp, const uint64_t& aLayersId) override;
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPVsyncBridgeParent() override;
+
+ void Shutdown();
+
+private:
+ VsyncBridgeParent();
+ ~VsyncBridgeParent();
+
+ void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint);
+ void ShutdownImpl();
+
+private:
+ bool mOpen;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // include_gfx_ipc_VsyncBridgeParent_h
diff --git a/system/graphics/ipc/VsyncIOThreadHolder.cpp b/system/graphics/ipc/VsyncIOThreadHolder.cpp
new file mode 100644
index 000000000..1567cda1d
--- /dev/null
+++ b/system/graphics/ipc/VsyncIOThreadHolder.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VsyncIOThreadHolder.h"
+
+namespace mozilla {
+namespace gfx {
+
+VsyncIOThreadHolder::VsyncIOThreadHolder()
+{
+ MOZ_COUNT_CTOR(VsyncIOThreadHolder);
+}
+
+VsyncIOThreadHolder::~VsyncIOThreadHolder()
+{
+ MOZ_COUNT_DTOR(VsyncIOThreadHolder);
+
+ if (!mThread) {
+ return;
+ }
+
+ if (NS_IsMainThread()) {
+ mThread->AsyncShutdown();
+ } else {
+ NS_DispatchToMainThread(NewRunnableMethod(mThread, &nsIThread::AsyncShutdown));
+ }
+}
+
+bool
+VsyncIOThreadHolder::Start()
+{
+ nsresult rv = NS_NewNamedThread("VsyncIOThread", getter_AddRefs(mThread));
+ return NS_SUCCEEDED(rv);
+}
+
+RefPtr<nsIThread>
+VsyncIOThreadHolder::GetThread() const
+{
+ return mThread;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/ipc/VsyncIOThreadHolder.h b/system/graphics/ipc/VsyncIOThreadHolder.h
new file mode 100644
index 000000000..d30553a82
--- /dev/null
+++ b/system/graphics/ipc/VsyncIOThreadHolder.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_ipc_VsyncIOThreadHolder_h
+#define mozilla_gfx_ipc_VsyncIOThreadHolder_h
+
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncIOThreadHolder final
+{
+public:
+ VsyncIOThreadHolder();
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncIOThreadHolder)
+
+ bool Start();
+
+ RefPtr<nsIThread> GetThread() const;
+
+private:
+ ~VsyncIOThreadHolder();
+
+private:
+ RefPtr<nsIThread> mThread;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_ipc_VsyncIOThreadHolder_h
diff --git a/system/graphics/ipc/moz.build b/system/graphics/ipc/moz.build
new file mode 100644
index 000000000..312fabcf6
--- /dev/null
+++ b/system/graphics/ipc/moz.build
@@ -0,0 +1,80 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla += [
+ 'D3DMessageUtils.h',
+ 'GfxMessageUtils.h'
+]
+
+EXPORTS.mozilla.gfx += [
+ 'GPUChild.h',
+ 'GPUParent.h',
+ 'GPUProcessHost.h',
+ 'GPUProcessImpl.h',
+ 'GPUProcessListener.h',
+ 'GPUProcessManager.h',
+ 'SharedDIB.h',
+ 'VsyncBridgeChild.h',
+ 'VsyncBridgeParent.h',
+ 'VsyncIOThreadHolder.h',
+]
+
+EXPORTS.mozilla.layers += [
+ 'CompositorSession.h',
+ 'InProcessCompositorSession.h',
+ 'RemoteCompositorSession.h',
+]
+
+EXPORTS.mozilla.widget += [
+ 'CompositorWidgetVsyncObserver.h',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS.mozilla.gfx += [
+ 'SharedDIBSurface.h',
+ 'SharedDIBWin.h',
+ ]
+ SOURCES += [
+ 'SharedDIBSurface.cpp',
+ 'SharedDIBWin.cpp',
+ ]
+
+SOURCES += [
+ 'CompositorSession.cpp',
+ 'CompositorWidgetVsyncObserver.cpp',
+ 'D3DMessageUtils.cpp',
+ 'GPUChild.cpp',
+ 'GPUProcessHost.cpp',
+ 'GPUProcessImpl.cpp',
+ 'GPUProcessManager.cpp',
+ 'InProcessCompositorSession.cpp',
+ 'RemoteCompositorSession.cpp',
+ 'SharedDIB.cpp',
+ 'VsyncBridgeChild.cpp',
+ 'VsyncBridgeParent.cpp',
+ 'VsyncIOThreadHolder.cpp',
+]
+
+SOURCES += [
+ 'GPUParent.cpp',
+]
+
+IPDL_SOURCES = [
+ 'GraphicsMessages.ipdlh',
+ 'PGPU.ipdl',
+ 'PVsyncBridge.ipdl',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/ipc',
+ '/xpcom/threads',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/system/graphics/layers/AsyncCanvasRenderer.cpp b/system/graphics/layers/AsyncCanvasRenderer.cpp
new file mode 100644
index 000000000..d9a0e9886
--- /dev/null
+++ b/system/graphics/layers/AsyncCanvasRenderer.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AsyncCanvasRenderer.h"
+
+#include "gfxUtils.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+AsyncCanvasRenderer::AsyncCanvasRenderer()
+ : mHTMLCanvasElement(nullptr)
+ , mContext(nullptr)
+ , mGLContext(nullptr)
+ , mIsAlphaPremultiplied(true)
+ , mWidth(0)
+ , mHeight(0)
+ , mCanvasClientAsyncID(0)
+ , mCanvasClient(nullptr)
+ , mMutex("AsyncCanvasRenderer::mMutex")
+{
+ MOZ_COUNT_CTOR(AsyncCanvasRenderer);
+}
+
+AsyncCanvasRenderer::~AsyncCanvasRenderer()
+{
+ MOZ_COUNT_DTOR(AsyncCanvasRenderer);
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutAttributesChanged()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutInvalidation()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
+{
+ mCanvasClient = aClient;
+ if (aClient) {
+ mCanvasClientAsyncID = aClient->GetAsyncID();
+ } else {
+ mCanvasClientAsyncID = 0;
+ }
+}
+
+void
+AsyncCanvasRenderer::SetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = NS_GetCurrentThread();
+}
+
+void
+AsyncCanvasRenderer::ResetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = nullptr;
+}
+
+already_AddRefed<nsIThread>
+AsyncCanvasRenderer::GetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ nsCOMPtr<nsIThread> result = mActiveThread;
+ return result.forget();
+}
+
+void
+AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
+{
+ MutexAutoLock lock(mMutex);
+
+ if (!aTextureClient) {
+ mSurfaceForBasic = nullptr;
+ return;
+ }
+
+ TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
+ if (!texLock.Succeeded()) {
+ return;
+ }
+
+ const gfx::IntSize& size = aTextureClient->GetSize();
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ // Avoid to create buffer every time.
+ if (!mSurfaceForBasic ||
+ size != mSurfaceForBasic->GetSize() ||
+ format != mSurfaceForBasic->GetFormat())
+ {
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+ }
+
+ MappedTextureData mapped;
+ if (!aTextureClient->BorrowMappedData(mapped)) {
+ return;
+ }
+
+ const uint8_t* lockedBytes = mapped.data;
+ gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
+ gfx::DataSourceSurface::MapType::WRITE);
+ if (!map.IsMapped()) {
+ return;
+ }
+
+ MOZ_ASSERT(map.GetStride() == mapped.stride);
+ memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
+
+ if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
+ gl::SwapRAndBComponents(mSurfaceForBasic);
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::UpdateTarget()
+{
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* frontbuffer = nullptr;
+ gl::GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+
+ if (!frontbuffer) {
+ return nullptr;
+ }
+
+ if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
+ return nullptr;
+ }
+
+ const gfx::IntSize& size = frontbuffer->mSize;
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+
+
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ if (!frontbuffer->ReadbackBySharedHandle(surface)) {
+ return nullptr;
+ }
+
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(surface, surface);
+ }
+
+ return surface.forget();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::GetSurface()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mMutex);
+ if (mSurfaceForBasic) {
+ // Since SourceSurface isn't thread-safe, we need copy to a new SourceSurface.
+ RefPtr<gfx::DataSourceSurface> result =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(mSurfaceForBasic->GetSize(),
+ mSurfaceForBasic->GetFormat(),
+ mSurfaceForBasic->Stride());
+
+ gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic, gfx::DataSourceSurface::READ);
+ gfx::DataSourceSurface::ScopedMap dstMap(result, gfx::DataSourceSurface::WRITE);
+
+ if (NS_WARN_IF(!srcMap.IsMapped()) ||
+ NS_WARN_IF(!dstMap.IsMapped())) {
+ return nullptr;
+ }
+
+ memcpy(dstMap.GetData(),
+ srcMap.GetData(),
+ srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
+ return result.forget();
+ } else {
+ return UpdateTarget();
+ }
+}
+
+nsresult
+AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
+ const char16_t *aEncoderOptions,
+ nsIInputStream **aStream)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<gfx::DataSourceSurface> surface = GetSurface();
+ if (!surface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Handle y flip.
+ RefPtr<gfx::DataSourceSurface> dataSurf = gl::YInvertImageSurface(surface);
+
+ return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions, aStream);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/AsyncCanvasRenderer.h b/system/graphics/layers/AsyncCanvasRenderer.h
new file mode 100644
index 000000000..404695de4
--- /dev/null
+++ b/system/graphics/layers/AsyncCanvasRenderer.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
+#define MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
+
+#include "LayersTypes.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Mutex.h"
+#include "nsCOMPtr.h" // for nsCOMPtr
+
+class nsICanvasRenderingContextInternal;
+class nsIInputStream;
+class nsIThread;
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+}
+
+namespace gl {
+class GLContext;
+}
+
+namespace dom {
+class HTMLCanvasElement;
+}
+
+namespace layers {
+
+class CanvasClient;
+class TextureClient;
+
+/**
+ * Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create
+ * AsyncCanvasRenderer which is thread-safe wrapper object for communicating
+ * among main, worker and ImageBridgeChild threads.
+ *
+ * Each HTMLCanvasElement object is responsible for creating
+ * AsyncCanvasRenderer object. Once Canvas is transfered to worker,
+ * OffscreenCanvas will keep reference pointer of this object.
+ *
+ * Sometimes main thread needs AsyncCanvasRenderer's result, such as layers
+ * fallback to BasicLayerManager or calling toDataURL in Javascript. Simply call
+ * GetSurface() in main thread will readback the result to mSurface.
+ *
+ * If layers backend is LAYERS_CLIENT, this object will pass to ImageBridgeChild
+ * for submitting frames to Compositor.
+ */
+class AsyncCanvasRenderer final
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncCanvasRenderer)
+
+public:
+ AsyncCanvasRenderer();
+
+ void NotifyElementAboutAttributesChanged();
+ void NotifyElementAboutInvalidation();
+
+ void SetCanvasClient(CanvasClient* aClient);
+
+ void SetWidth(uint32_t aWidth)
+ {
+ mWidth = aWidth;
+ }
+
+ void SetHeight(uint32_t aHeight)
+ {
+ mHeight = aHeight;
+ }
+
+ void SetIsAlphaPremultiplied(bool aIsAlphaPremultiplied)
+ {
+ mIsAlphaPremultiplied = aIsAlphaPremultiplied;
+ }
+
+ // Active thread means the thread which spawns GLContext.
+ void SetActiveThread();
+ void ResetActiveThread();
+
+ // This will readback surface and return the surface
+ // in the DataSourceSurface.
+ // Can be called in main thread only.
+ already_AddRefed<gfx::DataSourceSurface> GetSurface();
+
+ // For SharedSurface_Basic case, before the frame sending to the compositor,
+ // we readback it to a texture client because SharedSurface_Basic cannot shared.
+ // We don't want to readback it again here, so just copy the content of that
+ // texture client here to avoid readback again.
+ void CopyFromTextureClient(TextureClient *aClient);
+
+ // Readback current WebGL's content and convert it to InputStream. This
+ // function called GetSurface implicitly and GetSurface handles only get
+ // called in the main thread. So this function can be called in main thread.
+ nsresult
+ GetInputStream(const char *aMimeType,
+ const char16_t *aEncoderOptions,
+ nsIInputStream **aStream);
+
+ gfx::IntSize GetSize() const
+ {
+ return gfx::IntSize(mWidth, mHeight);
+ }
+
+ uint64_t GetCanvasClientAsyncID() const
+ {
+ return mCanvasClientAsyncID;
+ }
+
+ CanvasClient* GetCanvasClient() const
+ {
+ return mCanvasClient;
+ }
+
+ already_AddRefed<nsIThread> GetActiveThread();
+
+ // The lifetime is controllered by HTMLCanvasElement.
+ // Only accessed in main thread.
+ dom::HTMLCanvasElement* mHTMLCanvasElement;
+
+ // Only accessed in active thread.
+ nsICanvasRenderingContextInternal* mContext;
+
+ // We need to keep a reference to the context around here, otherwise the
+ // canvas' surface texture destructor will deref and destroy it too early
+ // Only accessed in active thread.
+ RefPtr<gl::GLContext> mGLContext;
+private:
+
+ virtual ~AsyncCanvasRenderer();
+
+ // Readback current WebGL's content and return it as DataSourceSurface.
+ already_AddRefed<gfx::DataSourceSurface> UpdateTarget();
+
+ bool mIsAlphaPremultiplied;
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint64_t mCanvasClientAsyncID;
+
+ // The lifetime of this pointer is controlled by OffscreenCanvas
+ // Can be accessed in active thread and ImageBridge thread.
+ // But we never accessed it at the same time on both thread. So no
+ // need to protect this member.
+ CanvasClient* mCanvasClient;
+
+ // When backend is LAYER_BASIC and SharedSurface type is Basic.
+ // CanvasClient will readback the GLContext to a TextureClient
+ // in order to send frame to compositor. To avoid readback again,
+ // we copy from this TextureClient to this mSurfaceForBasic directly
+ // by calling CopyFromTextureClient().
+ RefPtr<gfx::DataSourceSurface> mSurfaceForBasic;
+
+ // Protect non thread-safe objects.
+ Mutex mMutex;
+
+ // Can be accessed in any thread, need protect by mutex.
+ nsCOMPtr<nsIThread> mActiveThread;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
diff --git a/system/graphics/layers/AtomicRefCountedWithFinalize.h b/system/graphics/layers/AtomicRefCountedWithFinalize.h
new file mode 100644
index 000000000..1dd35e626
--- /dev/null
+++ b/system/graphics/layers/AtomicRefCountedWithFinalize.h
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+#define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Likely.h"
+#include "MainThreadUtils.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "mozilla/gfx/Logging.h"
+
+#define ADDREF_MANUALLY(obj) (obj)->AddRefManually(__FUNCTION__, __FILE__, __LINE__)
+#define RELEASE_MANUALLY(obj) (obj)->ReleaseManually(__FUNCTION__, __FILE__, __LINE__)
+
+namespace mozilla {
+
+template<class U>
+class StaticRefPtr;
+
+namespace gl {
+template<typename T>
+class RefSet;
+
+template<typename T>
+class RefQueue;
+} // namespace gl
+
+template<typename T>
+class AtomicRefCountedWithFinalize
+{
+protected:
+ explicit AtomicRefCountedWithFinalize(const char* aName)
+ : mRecycleCallback(nullptr)
+ , mRefCount(0)
+#ifdef DEBUG
+ , mSpew(false)
+ , mManualAddRefs(0)
+ , mManualReleases(0)
+#endif
+#ifdef NS_BUILD_REFCNT_LOGGING
+ , mName(aName)
+#endif
+ {}
+
+ ~AtomicRefCountedWithFinalize() {
+ if (mRefCount >= 0) {
+ gfxCriticalError() << "Deleting referenced object? " << mRefCount;
+ }
+ }
+
+public:
+ // Mark user classes that are considered flawless.
+ template<class U>
+ friend class ::mozilla::StaticRefPtr;
+
+ template<class U>
+ friend struct mozilla::RefPtrTraits;
+
+ template<typename U>
+ friend class ::mozilla::gl::RefSet;
+
+ template<typename U>
+ friend class ::mozilla::gl::RefQueue;
+
+ //friend class mozilla::gl::SurfaceFactory;
+
+ void AddRefManually(const char* funcName, const char* fileName, uint32_t lineNum) {
+#ifdef DEBUG
+ uint32_t count = ++mManualAddRefs;
+ if (mSpew) {
+ printf_stderr("AddRefManually() #%u in %s at %s:%u\n", count, funcName,
+ fileName, lineNum);
+ }
+#else
+ (void)funcName;
+ (void)fileName;
+ (void)lineNum;
+#endif
+ AddRef();
+ }
+
+ void ReleaseManually(const char* funcName, const char* fileName, uint32_t lineNum) {
+#ifdef DEBUG
+ uint32_t count = ++mManualReleases;
+ if (mSpew) {
+ printf_stderr("ReleaseManually() #%u in %s at %s:%u\n", count, funcName,
+ fileName, lineNum);
+ }
+#else
+ (void)funcName;
+ (void)fileName;
+ (void)lineNum;
+#endif
+ Release();
+ }
+
+private:
+ void AddRef() {
+ MOZ_ASSERT(mRefCount >= 0, "AddRef() during/after Finalize()/dtor.");
+#ifdef NS_BUILD_REFCNT_LOGGING
+ int currCount = ++mRefCount;
+ NS_LOG_ADDREF(this, currCount, mName, sizeof(*this));
+#else
+ ++mRefCount;
+#endif
+ }
+
+ void Release() {
+ MOZ_ASSERT(mRefCount > 0, "Release() during/after Finalize()/dtor.");
+ // Read mRecycleCallback early so that it does not get set to
+ // deleted memory, if the object is goes away. See bug 994903.
+ // This saves us in the case where there is no callback, so that
+ // we can do the "else if" below.
+ RecycleCallback recycleCallback = mRecycleCallback;
+ int currCount = --mRefCount;
+ if (currCount < 0) {
+ gfxCriticalError() << "Invalid reference count release" << currCount;
+ ++mRefCount;
+ return;
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ NS_LOG_RELEASE(this, currCount, mName);
+#endif
+
+ if (0 == currCount) {
+ mRefCount = detail::DEAD;
+ MOZ_ASSERT(IsDead());
+
+ // Recycle listeners must call ClearRecycleCallback
+ // before releasing their strong reference.
+ if (mRecycleCallback) {
+ gfxCriticalError() << "About to release with valid callback";
+ mRecycleCallback = nullptr;
+ }
+
+ MOZ_ASSERT(mManualAddRefs == mManualReleases);
+
+ T* derived = static_cast<T*>(this);
+ derived->Finalize();
+ delete derived;
+ } else if (1 == currCount && recycleCallback) {
+ // There is nothing enforcing this in the code, except how the callers
+ // are being careful to never let the reference count go down if there
+ // is a callback.
+ MOZ_ASSERT(!IsDead());
+ T* derived = static_cast<T*>(this);
+ recycleCallback(derived, mClosure);
+ }
+ }
+
+public:
+ typedef void (*RecycleCallback)(T* aObject, void* aClosure);
+ /**
+ * Set a callback responsible for recycling this object
+ * before it is finalized.
+ */
+ void SetRecycleCallback(RecycleCallback aCallback, void* aClosure)
+ {
+ MOZ_ASSERT(!IsDead());
+ mRecycleCallback = aCallback;
+ mClosure = aClosure;
+ }
+ void ClearRecycleCallback()
+ {
+ MOZ_ASSERT(!IsDead());
+ SetRecycleCallback(nullptr, nullptr);
+ }
+
+ bool HasRecycleCallback() const
+ {
+ MOZ_ASSERT(!IsDead());
+ return !!mRecycleCallback;
+ }
+
+ bool IsDead() const
+ {
+ return mRefCount < 0;
+ }
+
+private:
+ RecycleCallback mRecycleCallback;
+ void *mClosure;
+ Atomic<int> mRefCount;
+#ifdef DEBUG
+public:
+ bool mSpew;
+private:
+ Atomic<uint32_t> mManualAddRefs;
+ Atomic<uint32_t> mManualReleases;
+#endif
+#ifdef NS_BUILD_REFCNT_LOGGING
+ const char* mName;
+#endif
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/AxisPhysicsMSDModel.cpp b/system/graphics/layers/AxisPhysicsMSDModel.cpp
new file mode 100644
index 000000000..fc0dae8a6
--- /dev/null
+++ b/system/graphics/layers/AxisPhysicsMSDModel.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsMSDModel.h"
+#include <math.h> // for sqrt and fabs
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Constructs an AxisPhysicsMSDModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulated spring,
+ * in AppUnits.
+ * @param aInitialDestination sets the resting position of the simulated spring,
+ * in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulated spring,
+ * in AppUnits / second. Critically-damped and over-damped systems are
+ * guaranteed not to overshoot aInitialDestination if this is set to 0;
+ * however, it is possible to overshoot and oscillate if not set to 0 or
+ * the system is under-damped.
+ * @param aSpringConstant sets the strength of the simulated spring. Greater
+ * values of mSpringConstant result in a stiffer / stronger spring.
+ * @param aDampingRatio controls the amount of dampening force and determines
+ * if the system is under-damped, critically-damped, or over-damped.
+ */
+AxisPhysicsMSDModel::AxisPhysicsMSDModel(double aInitialPosition,
+ double aInitialDestination,
+ double aInitialVelocity,
+ double aSpringConstant,
+ double aDampingRatio)
+ : AxisPhysicsModel(aInitialPosition, aInitialVelocity)
+ , mDestination(aInitialDestination)
+ , mSpringConstant(aSpringConstant)
+ , mSpringConstantSqrtXTwo(sqrt(mSpringConstant) * 2.0)
+ , mDampingRatio(aDampingRatio)
+{
+}
+
+AxisPhysicsMSDModel::~AxisPhysicsMSDModel()
+{
+}
+
+double
+AxisPhysicsMSDModel::Acceleration(const State &aState)
+{
+ // Simulate a Mass-Damper-Spring Model; assume a unit mass
+
+ // Hooke’s Law: http://en.wikipedia.org/wiki/Hooke%27s_law
+ double spring_force = (mDestination - aState.p) * mSpringConstant;
+ double damp_force = -aState.v * mDampingRatio * mSpringConstantSqrtXTwo;
+
+ return spring_force + damp_force;
+}
+
+
+double
+AxisPhysicsMSDModel::GetDestination() const
+{
+ return mDestination;
+}
+
+void
+AxisPhysicsMSDModel::SetDestination(double aDestination)
+{
+ mDestination = aDestination;
+}
+
+bool
+AxisPhysicsMSDModel::IsFinished(double aSmallestVisibleIncrement)
+{
+ // In order to satisfy the condition of reaching the destination, the distance
+ // between the simulation position and the destination must be less than
+ // aSmallestVisibleIncrement while the speed is simultaneously less than
+ // finishVelocity. This enables an under-damped system to overshoot the
+ // destination when desired without prematurely triggering the finished state.
+ // If finishVelocity is set too low, the animation may end long after
+ // oscillation has finished, resulting in unnecessary processing.
+ // If set too high, the animation may prematurely terminate when expected
+ // to overshoot the destination in an under-damped system.
+ // aSmallestVisibleIncrement * 2 was selected through experimentation that
+ // revealed that a critically damped system will terminate within 100ms.
+ const double finishVelocity = aSmallestVisibleIncrement * 2;
+
+ return fabs(mDestination - GetPosition ()) < aSmallestVisibleIncrement
+ && fabs(GetVelocity()) <= finishVelocity;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/AxisPhysicsMSDModel.h b/system/graphics/layers/AxisPhysicsMSDModel.h
new file mode 100644
index 000000000..ff01996e1
--- /dev/null
+++ b/system/graphics/layers/AxisPhysicsMSDModel.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsMSDModel_h
+#define mozilla_layers_AxisPhysicsMSDModel_h
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * AxisPhysicsMSDModel encapsulates a 1-dimensional MSD (Mass-Spring-Damper)
+ * model. A unit mass is assumed.
+ */
+class AxisPhysicsMSDModel : public AxisPhysicsModel {
+public:
+ AxisPhysicsMSDModel(double aInitialPosition, double aInitialDestination,
+ double aInitialVelocity, double aSpringConstant,
+ double aDampingRatio);
+
+ ~AxisPhysicsMSDModel();
+
+ /**
+ * Gets the raw destination of this axis at this moment.
+ */
+ double GetDestination() const;
+
+ /**
+ * Sets the raw destination of this axis at this moment.
+ */
+ void SetDestination(double aDestination);
+
+ /**
+ * Returns true when the position is close to the destination and the
+ * velocity is low.
+ */
+ bool IsFinished(double aSmallestVisibleIncrement);
+
+protected:
+ virtual double Acceleration(const State &aState);
+
+private:
+
+ /**
+ * mDestination represents the target position and the resting position of
+ * the simulated spring.
+ */
+ double mDestination;
+
+ /**
+ * Greater values of mSpringConstant result in a stiffer / stronger spring.
+ */
+ double mSpringConstant;
+
+ /**
+ * mSpringConstantSqrtTimesTwo is calculated from mSpringConstant to reduce
+ * calculations performed in the inner loop.
+ */
+ double mSpringConstantSqrtXTwo;
+
+ /**
+ * Damping Ratio: http://en.wikipedia.org/wiki/Damping_ratio
+ *
+ * When mDampingRatio < 1.0, this is an under damped system.
+ * - Overshoots destination and oscillates with the amplitude gradually
+ * decreasing to zero.
+ *
+ * When mDampingRatio == 1.0, this is a critically damped system.
+ * - Reaches destination as quickly as possible without oscillating.
+ *
+ * When mDampingRatio > 1.0, this is an over damped system.
+ * - Reaches destination (exponentially decays) without oscillating.
+ */
+ double mDampingRatio;
+
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/AxisPhysicsModel.cpp b/system/graphics/layers/AxisPhysicsModel.cpp
new file mode 100644
index 000000000..4d281f755
--- /dev/null
+++ b/system/graphics/layers/AxisPhysicsModel.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The simulation is advanced forward in time with a fixed time step to ensure
+ * that it remains deterministic given variable framerates. To determine the
+ * position at any variable time, two samples are interpolated.
+ *
+ * kFixedtimestep is set to 120hz in order to ensure that every frame in a
+ * common 60hz refresh rate display will have at least one physics simulation
+ * sample. More accuracy can be obtained by reducing kFixedTimestep to smaller
+ * intervals, such as 240hz or 1000hz, at the cost of more CPU cycles. If
+ * kFixedTimestep is increased to much longer intervals, interpolation will
+ * become less effective at reducing temporal jitter and the simulation will
+ * lose accuracy.
+ */
+const double AxisPhysicsModel::kFixedTimestep = 1.0 / 120.0; // 120hz
+
+/**
+ * Constructs an AxisPhysicsModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulation,
+ * in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulation,
+ * in AppUnits / second.
+ */
+AxisPhysicsModel::AxisPhysicsModel(double aInitialPosition,
+ double aInitialVelocity)
+ : mProgress(1.0)
+ , mPrevState(aInitialPosition, aInitialVelocity)
+ , mNextState(aInitialPosition, aInitialVelocity)
+{
+
+}
+
+AxisPhysicsModel::~AxisPhysicsModel()
+{
+
+}
+
+double
+AxisPhysicsModel::GetVelocity()
+{
+ return LinearInterpolate(mPrevState.v, mNextState.v, mProgress);
+}
+
+double
+AxisPhysicsModel::GetPosition()
+{
+ return LinearInterpolate(mPrevState.p, mNextState.p, mProgress);
+}
+
+void
+AxisPhysicsModel::SetVelocity(double aVelocity)
+{
+ mNextState.v = aVelocity;
+ mNextState.p = GetPosition();
+ mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::SetPosition(double aPosition)
+{
+ mNextState.v = GetVelocity();
+ mNextState.p = aPosition;
+ mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::Simulate(const TimeDuration& aDeltaTime)
+{
+ for(mProgress += aDeltaTime.ToSeconds() / kFixedTimestep;
+ mProgress > 1.0; mProgress -= 1.0) {
+ Integrate(kFixedTimestep);
+ }
+}
+
+void
+AxisPhysicsModel::Integrate(double aDeltaTime)
+{
+ mPrevState = mNextState;
+
+ // RK4 (Runge-Kutta method) Integration
+ // http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
+ Derivative a = Evaluate( mNextState, 0.0, Derivative() );
+ Derivative b = Evaluate( mNextState, aDeltaTime * 0.5, a );
+ Derivative c = Evaluate( mNextState, aDeltaTime * 0.5, b );
+ Derivative d = Evaluate( mNextState, aDeltaTime, c );
+
+ double dpdt = 1.0 / 6.0 * (a.dp + 2.0 * (b.dp + c.dp) + d.dp);
+ double dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
+
+ mNextState.p += dpdt * aDeltaTime;
+ mNextState.v += dvdt * aDeltaTime;
+}
+
+AxisPhysicsModel::Derivative
+AxisPhysicsModel::Evaluate(const State &aInitState, double aDeltaTime,
+ const Derivative &aDerivative)
+{
+ State state( aInitState.p + aDerivative.dp*aDeltaTime, aInitState.v + aDerivative.dv*aDeltaTime );
+
+ return Derivative( state.v, Acceleration(state) );
+}
+
+double
+AxisPhysicsModel::LinearInterpolate(double aV1, double aV2, double aBlend)
+{
+ return aV1 * (1.0 - aBlend) + aV2 * aBlend;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/AxisPhysicsModel.h b/system/graphics/layers/AxisPhysicsModel.h
new file mode 100644
index 000000000..8a0ec5765
--- /dev/null
+++ b/system/graphics/layers/AxisPhysicsModel.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsModel_h
+#define mozilla_layers_AxisPhysicsModel_h
+
+#include "AxisPhysicsModel.h"
+#include <sys/types.h> // for int32_t
+#include "mozilla/TimeStamp.h" // for TimeDuration
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * AxisPhysicsModel encapsulates a generic 1-dimensional physically-based motion
+ * model.
+ *
+ * It performs frame-rate independent interpolation and RK4 integration for
+ * smooth animation with stable, deterministic behavior.
+ * Implementations are expected to subclass and override the Acceleration()
+ * method.
+ */
+class AxisPhysicsModel {
+public:
+ AxisPhysicsModel(double aInitialPosition, double aInitialVelocity);
+ ~AxisPhysicsModel();
+
+ /**
+ * Advance the physics simulation.
+ * |aDelta| is the time since the last sample.
+ */
+ void Simulate(const TimeDuration& aDeltaTime);
+
+ /**
+ * Gets the raw velocity of this axis at this moment.
+ */
+ double GetVelocity();
+
+ /**
+ * Sets the raw velocity of this axis at this moment.
+ */
+ void SetVelocity(double aVelocity);
+
+ /**
+ * Gets the raw position of this axis at this moment.
+ */
+ double GetPosition();
+
+ /**
+ * Sets the raw position of this axis at this moment.
+ */
+ void SetPosition(double aPosition);
+
+protected:
+
+ struct State
+ {
+ State(double ap, double av) : p(ap), v(av) {};
+ double p; // Position
+ double v; // Velocity
+ };
+
+ struct Derivative
+ {
+ Derivative() : dp(0.0), dv(0.0) {};
+ Derivative(double aDp, double aDv) : dp(aDp), dv(aDv) {};
+ double dp; // dp / delta time = Position
+ double dv; // dv / delta time = Velocity
+ };
+
+ /**
+ * Acceleration must be overridden and return the number of
+ * axis-position-units / second that should be added or removed from the
+ * velocity.
+ */
+ virtual double Acceleration(const State &aState) = 0;
+
+private:
+
+ /**
+ * Duration of fixed delta time step (seconds)
+ */
+ static const double kFixedTimestep;
+
+ /**
+ * 0.0 - 1.0 value indicating progress between current and next simulation
+ * sample. Normalized to units of kFixedTimestep duration.
+ */
+ double mProgress;
+
+ /**
+ * Sample of simulation state as it existed
+ * (1.0 - mProgress) * kFixedTimestep seconds in the past.
+ */
+ State mPrevState;
+
+ /**
+ * Sample of simulation state as it will be in mProgress * kFixedTimestep
+ * seconds in the future.
+ */
+ State mNextState;
+
+ /**
+ * Perform RK4 (Runge-Kutta method) Integration to calculate the next
+ * simulation sample.
+ */
+ void Integrate(double aDeltaTime);
+
+ /**
+ * Apply delta velocity and position represented by aDerivative over
+ * aDeltaTime seconds, calculate new acceleration, and return new deltas.
+ */
+ Derivative Evaluate(const State &aInitState, double aDeltaTime,
+ const Derivative &aDerivative);
+
+ /**
+ * Helper function for performing linear interpolation (lerp) of double's
+ */
+ static double LinearInterpolate(double aV1, double aV2, double aBlend);
+
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/BSPTree.cpp b/system/graphics/layers/BSPTree.cpp
new file mode 100644
index 000000000..e1ed8621a
--- /dev/null
+++ b/system/graphics/layers/BSPTree.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BSPTree.h"
+#include "mozilla/gfx/Polygon.h"
+
+namespace mozilla {
+namespace layers {
+
+LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers)
+{
+ LayerPolygon layer = Move(aLayers.front());
+ aLayers.pop_front();
+ return layer;
+}
+
+void
+BSPTree::BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
+ nsTArray<LayerPolygon>& aLayers) const
+{
+ const gfx::Point3D& normal = aNode->First().GetNormal();
+
+ UniquePtr<BSPTreeNode> *front = &aNode->front;
+ UniquePtr<BSPTreeNode> *back = &aNode->back;
+
+ // Since the goal is to return the draw order from back to front, we reverse
+ // the traversal order if the current polygon is facing towards the camera.
+ const bool reverseOrder = normal.z > 0.0f;
+
+ if (reverseOrder) {
+ std::swap(front, back);
+ }
+
+ if (*front) {
+ BuildDrawOrder(*front, aLayers);
+ }
+
+ for (LayerPolygon& layer : aNode->layers) {
+ MOZ_ASSERT(layer.geometry);
+
+ if (layer.geometry->GetPoints().Length() >= 3) {
+ aLayers.AppendElement(Move(layer));
+ }
+ }
+
+ if (*back) {
+ BuildDrawOrder(*back, aLayers);
+ }
+}
+
+void
+BSPTree::BuildTree(UniquePtr<BSPTreeNode>& aRoot,
+ std::deque<LayerPolygon>& aLayers)
+{
+ if (aLayers.empty()) {
+ return;
+ }
+
+ const gfx::Polygon3D& plane = aRoot->First();
+ std::deque<LayerPolygon> backLayers, frontLayers;
+
+ for (LayerPolygon& layerPolygon : aLayers) {
+ const Maybe<gfx::Polygon3D>& geometry = layerPolygon.geometry;
+
+ size_t pos = 0, neg = 0;
+ nsTArray<float> dots = geometry->CalculateDotProducts(plane, pos, neg);
+
+ // Back polygon
+ if (pos == 0 && neg > 0) {
+ backLayers.push_back(Move(layerPolygon));
+ }
+ // Front polygon
+ else if (pos > 0 && neg == 0) {
+ frontLayers.push_back(Move(layerPolygon));
+ }
+ // Coplanar polygon
+ else if (pos == 0 && neg == 0) {
+ aRoot->layers.push_back(Move(layerPolygon));
+ }
+ // Polygon intersects with the splitting plane.
+ else if (pos > 0 && neg > 0) {
+ nsTArray<gfx::Point3D> backPoints, frontPoints;
+ geometry->SplitPolygon(plane, dots, backPoints, frontPoints);
+
+ const gfx::Point3D& normal = geometry->GetNormal();
+ Layer *layer = layerPolygon.layer;
+
+ backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
+ frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
+ }
+ }
+
+ if (!backLayers.empty()) {
+ aRoot->back.reset(new BSPTreeNode(PopFront(backLayers)));
+ BuildTree(aRoot->back, backLayers);
+ }
+
+ if (!frontLayers.empty()) {
+ aRoot->front.reset(new BSPTreeNode(PopFront(frontLayers)));
+ BuildTree(aRoot->front, frontLayers);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/BSPTree.h b/system/graphics/layers/BSPTree.h
new file mode 100644
index 000000000..24a82f2ac
--- /dev/null
+++ b/system/graphics/layers/BSPTree.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_BSPTREE_H
+#define MOZILLA_LAYERS_BSPTREE_H
+
+#include "mozilla/gfx/Polygon.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+#include <deque>
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+// Represents a layer that might have a non-rectangular geometry.
+struct LayerPolygon {
+ explicit LayerPolygon(Layer *aLayer)
+ : layer(aLayer) {}
+
+ LayerPolygon(Layer *aLayer,
+ gfx::Polygon3D&& aGeometry)
+ : layer(aLayer), geometry(Some(aGeometry)) {}
+
+ LayerPolygon(Layer *aLayer,
+ nsTArray<gfx::Point3D>&& aPoints, const gfx::Point3D& aNormal)
+ : layer(aLayer), geometry(Some(gfx::Polygon3D(Move(aPoints), aNormal))) {}
+
+ Layer *layer;
+ Maybe<gfx::Polygon3D> geometry;
+};
+
+LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers);
+
+// Represents a node in a BSP tree. The node contains at least one layer with
+// associated geometry that is used as a splitting plane, and at most two child
+// nodes that represent the splitting planes that further subdivide the space.
+struct BSPTreeNode {
+ explicit BSPTreeNode(LayerPolygon&& layer)
+ {
+ layers.push_back(Move(layer));
+ }
+
+ const gfx::Polygon3D& First() const
+ {
+ MOZ_ASSERT(layers[0].geometry);
+ return *layers[0].geometry;
+ }
+
+ UniquePtr<BSPTreeNode> front;
+ UniquePtr<BSPTreeNode> back;
+ std::deque<LayerPolygon> layers;
+};
+
+// BSPTree class takes a list of layers as an input and uses binary space
+// partitioning algorithm to create a tree structure that can be used for
+// depth sorting.
+//
+// Sources for more information:
+// https://en.wikipedia.org/wiki/Binary_space_partitioning
+// ftp://ftp.sgi.com/other/bspfaq/faq/bspfaq.html
+class BSPTree {
+public:
+ // This constructor takes the ownership of layers in the given list.
+ explicit BSPTree(std::deque<LayerPolygon>& aLayers)
+ {
+ MOZ_ASSERT(!aLayers.empty());
+ mRoot.reset(new BSPTreeNode(PopFront(aLayers)));
+
+ BuildTree(mRoot, aLayers);
+ }
+
+ // Returns the root node of the BSP tree.
+ const UniquePtr<BSPTreeNode>& GetRoot() const
+ {
+ return mRoot;
+ }
+
+ // Builds and returns the back-to-front draw order for the created BSP tree.
+ nsTArray<LayerPolygon> GetDrawOrder() const
+ {
+ nsTArray<LayerPolygon> layers;
+ BuildDrawOrder(mRoot, layers);
+ return layers;
+ }
+
+private:
+ UniquePtr<BSPTreeNode> mRoot;
+
+ // BuildDrawOrder and BuildTree are called recursively. The depth of the
+ // recursion depends on the amount of polygons and their intersections.
+ void BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
+ nsTArray<LayerPolygon>& aLayers) const;
+ void BuildTree(UniquePtr<BSPTreeNode>& aRoot,
+ std::deque<LayerPolygon>& aLayers);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_LAYERS_BSPTREE_H */
diff --git a/system/graphics/layers/BufferTexture.cpp b/system/graphics/layers/BufferTexture.cpp
new file mode 100644
index 000000000..88c59ac90
--- /dev/null
+++ b/system/graphics/layers/BufferTexture.cpp
@@ -0,0 +1,594 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/fallible.h"
+#include "libyuv.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+class MemoryTextureData : public BufferTextureData
+{
+public:
+ static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ MemoryTextureData(const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ uint8_t* aBuffer, size_t aBufferSize)
+ : BufferTextureData(aDesc, aMoz2DBackend)
+ , mBuffer(aBuffer)
+ , mBufferSize(aBufferSize)
+ {
+ MOZ_ASSERT(aBuffer);
+ MOZ_ASSERT(aBufferSize);
+ }
+
+ virtual uint8_t* GetBuffer() override { return mBuffer; }
+
+ virtual size_t GetBufferSize() override { return mBufferSize; }
+
+protected:
+ uint8_t* mBuffer;
+ size_t mBufferSize;
+};
+
+class ShmemTextureData : public BufferTextureData
+{
+public:
+ static ShmemTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ ShmemTextureData(const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
+ : BufferTextureData(aDesc, aMoz2DBackend)
+ , mShmem(aShmem)
+ {
+ MOZ_ASSERT(mShmem.Size<uint8_t>());
+ }
+
+ virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
+
+ virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
+
+protected:
+ mozilla::ipc::Shmem mShmem;
+};
+
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+ return gfx::gfxVars::UseXRender();
+#endif
+ return false;
+}
+
+bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend)
+{
+ return aLayersBackend != LayersBackend::LAYERS_BASIC
+ || UsingX11Compositor()
+ || aFormat == gfx::SurfaceFormat::UNKNOWN;
+}
+
+BufferTextureData*
+BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator || aAllocator->IsSameProcess()) {
+ return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aFlags,
+ aAllocFlags, aAllocator);
+ } else {
+ return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aFlags,
+ aAllocFlags, aAllocator);
+ }
+}
+
+BufferTextureData*
+BufferTextureData::CreateInternal(LayersIPCChannel* aAllocator,
+ const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ int32_t aBufferSize,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || aAllocator->IsSameProcess()) {
+ uint8_t* buffer = new (fallible) uint8_t[aBufferSize];
+ if (!buffer) {
+ return nullptr;
+ }
+
+ GfxMemoryImageReporter::DidAlloc(buffer);
+
+ return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
+ } else {
+ ipc::Shmem shm;
+ if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
+ return nullptr;
+ }
+
+ return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
+ }
+}
+
+BufferTextureData*
+BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ int32_t aBufferSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ aAllocator->GetCompositorBackendType())
+ : true;
+
+ // Initialize the metadata with something, even if it will have to be rewritten
+ // afterwards since we don't know the dimensions of the texture at this point.
+ BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
+ 0, 0, 0, StereoMode::MONO,
+ aYUVColorSpace,
+ hasIntermediateBuffer);
+
+ return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr,
+ desc, gfx::BackendType::NONE, aBufferSize, aTextureFlags);
+}
+
+BufferTextureData*
+BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
+ if (bufSize == 0) {
+ return nullptr;
+ }
+
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ ImageDataSerializer::ComputeYCbCrOffsets(aYSize.width, aYSize.height,
+ aCbCrSize.width, aCbCrSize.height,
+ yOffset, cbOffset, crOffset);
+
+ bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ aAllocator->GetCompositorBackendType())
+ : true;
+
+ YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
+ crOffset, aStereoMode, aYUVColorSpace,
+ hasIntermediateBuffer);
+
+ return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor,
+ gfx::BackendType::NONE, bufSize, aTextureFlags);
+}
+
+void
+BufferTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = GetSize();
+ aInfo.format = GetFormat();
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = true;
+
+ if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+ aInfo.hasIntermediateBuffer = mDescriptor.get_YCbCrDescriptor().hasIntermediateBuffer();
+ } else {
+ aInfo.hasIntermediateBuffer = mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
+ }
+
+ switch (aInfo.format) {
+ case gfx::SurfaceFormat::YUV:
+ case gfx::SurfaceFormat::UNKNOWN:
+ aInfo.supportsMoz2D = false;
+ break;
+ default:
+ aInfo.supportsMoz2D = true;
+ }
+}
+
+gfx::IntSize
+BufferTextureData::GetSize() const
+{
+ return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<gfx::IntSize>
+BufferTextureData::GetCbCrSize() const
+{
+ return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<YUVColorSpace>
+BufferTextureData::GetYUVColorSpace() const
+{
+ return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<StereoMode>
+BufferTextureData::GetStereoMode() const
+{
+ return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
+}
+
+gfx::SurfaceFormat
+BufferTextureData::GetFormat() const
+{
+ return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
+}
+
+already_AddRefed<gfx::DrawTarget>
+BufferTextureData::BorrowDrawTarget()
+{
+ if (mDrawTarget) {
+ mDrawTarget->SetTransform(gfx::Matrix());
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+ }
+
+ if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
+ return nullptr;
+ }
+
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ mDrawTarget = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend,
+ GetBuffer(), rgb.size(),
+ stride, rgb.format(), true);
+
+ if (mDrawTarget) {
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+ }
+
+ // TODO - should we warn? should we really fallback to cairo? perhaps
+ // at least update mMoz2DBackend...
+ if (mMoz2DBackend != gfx::BackendType::CAIRO) {
+ mDrawTarget = gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
+ GetBuffer(), rgb.size(),
+ stride, rgb.format(), true);
+ }
+
+ if (!mDrawTarget) {
+ gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mMoz2DBackend;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+}
+
+bool
+BufferTextureData::BorrowMappedData(MappedTextureData& aData)
+{
+ if (GetFormat() == gfx::SurfaceFormat::YUV) {
+ return false;
+ }
+
+ gfx::IntSize size = GetSize();
+
+ aData.data = GetBuffer();
+ aData.size = size;
+ aData.format = GetFormat();
+ aData.stride = ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
+
+ return true;
+}
+
+bool
+BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+ if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
+ return false;
+ }
+
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ uint8_t* data = GetBuffer();
+ auto ySize = desc.ySize();
+ auto cbCrSize = desc.cbCrSize();
+
+ aMap.stereoMode = desc.stereoMode();
+ aMap.metadata = nullptr;
+
+ aMap.y.data = data + desc.yOffset();
+ aMap.y.size = ySize;
+ aMap.y.stride = ySize.width;
+ aMap.y.skip = 0;
+
+ aMap.cb.data = data + desc.cbOffset();
+ aMap.cb.size = cbCrSize;
+ aMap.cb.stride = cbCrSize.width;
+ aMap.cb.skip = 0;
+
+ aMap.cr.data = data + desc.crOffset();
+ aMap.cr.size = cbCrSize;
+ aMap.cr.stride = cbCrSize.width;
+ aMap.cr.skip = 0;
+
+ return true;
+}
+
+bool
+BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
+ return false;
+ }
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride,
+ rgb.size(), rgb.format());
+
+ if (!surface) {
+ gfxCriticalError() << "Failed to get serializer as surface!";
+ return false;
+ }
+
+ RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
+ return false;
+ }
+
+ if (surface->GetSize() != srcSurf->GetSize() || surface->GetFormat() != srcSurf->GetFormat()) {
+ gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format (BT)! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+ return false;
+ }
+
+ gfx::DataSourceSurface::MappedSurface sourceMap;
+ gfx::DataSourceSurface::MappedSurface destMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (BT).";
+ return false;
+ }
+
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
+ srcSurf->Unmap();
+ gfxCriticalError() << "Failed to map destination surface for UpdateFromSurface.";
+ return false;
+ }
+
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(destMap.mData + destMap.mStride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ surface->Unmap();
+
+ return true;
+}
+
+void
+BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor)
+{
+ MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
+ mDescriptor = aDescriptor;
+}
+
+bool
+MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+ if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
+ aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
+
+ return true;
+}
+
+static bool
+InitBuffer(uint8_t* buf, size_t bufSize,
+ gfx::SurfaceFormat aFormat, TextureAllocationFlags aAllocFlags,
+ bool aAlreadyZero)
+{
+ if (!buf) {
+ gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
+ return false;
+ }
+
+ if ((aAllocFlags & ALLOC_CLEAR_BUFFER) ||
+ (aAllocFlags & ALLOC_CLEAR_BUFFER_BLACK)) {
+ if (aFormat == gfx::SurfaceFormat::B8G8R8X8) {
+ // Even though BGRX was requested, XRGB_UINT32 is what is meant,
+ // so use 0xFF000000 to put alpha in the right place.
+ libyuv::ARGBRect(buf, bufSize, 0, 0, bufSize / sizeof(uint32_t), 1, 0xFF000000);
+ } else if (!aAlreadyZero) {
+ memset(buf, 0, bufSize);
+ }
+ }
+
+ if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
+ memset(buf, 0xFF, bufSize);
+ }
+
+ return true;
+}
+
+MemoryTextureData*
+MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ // Should have used CreateForYCbCr.
+ MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
+
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+
+ uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
+ if (!bufSize) {
+ return nullptr;
+ }
+
+ uint8_t* buf = new (fallible) uint8_t[bufSize];
+ if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(aFormat, aLayersBackend);
+
+ GfxMemoryImageReporter::DidAlloc(buf);
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
+
+ return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
+}
+
+void
+MemoryTextureData::Deallocate(LayersIPCChannel*)
+{
+ MOZ_ASSERT(mBuffer);
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ delete [] mBuffer;
+ mBuffer = nullptr;
+}
+
+TextureData*
+MemoryTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
+ aLayersBackend, aFlags, aAllocFlags, aAllocator);
+}
+
+bool
+ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+ if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(mShmem));
+
+ return true;
+}
+
+ShmemTextureData*
+ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ // Should have used CreateForYCbCr.
+ MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
+
+ if (!aAllocator) {
+ return nullptr;
+ }
+
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+
+ uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
+ if (!bufSize) {
+ return nullptr;
+ }
+
+ mozilla::ipc::Shmem shm;
+ if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
+ return nullptr;
+ }
+
+ uint8_t* buf = shm.get<uint8_t>();
+ if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(aFormat, aLayersBackend);
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
+
+ return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
+
+ return nullptr;
+}
+
+TextureData*
+ShmemTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
+ aLayersBackend, aFlags, aAllocFlags, aAllocator);
+}
+
+void
+ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ aAllocator->DeallocShmem(mShmem);
+}
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/BufferTexture.h b/system/graphics/layers/BufferTexture.h
new file mode 100644
index 000000000..2999d1d94
--- /dev/null
+++ b/system/graphics/layers/BufferTexture.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_BUFFERETEXTURE
+#define MOZILLA_LAYERS_BUFFERETEXTURE
+
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend);
+
+class BufferTextureData : public TextureData
+{
+public:
+ static BufferTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // It is generally better to use CreateForYCbCr instead.
+ // This creates a half-initialized texture since we don't know the sizes and
+ // offsets in the buffer.
+ static BufferTextureData* CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ int32_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ virtual bool Lock(OpenMode aMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool BorrowMappedData(MappedTextureData& aMap) override;
+
+ virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
+
+ // use TextureClient's default implementation
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ virtual BufferTextureData* AsBufferTextureData() override { return this; }
+
+ // Don't use this.
+ void SetDesciptor(const BufferDescriptor& aDesc);
+
+ Maybe<gfx::IntSize> GetCbCrSize() const;
+
+ Maybe<YUVColorSpace> GetYUVColorSpace() const;
+
+ Maybe<StereoMode> GetStereoMode() const;
+
+protected:
+ gfx::IntSize GetSize() const;
+
+ gfx::SurfaceFormat GetFormat() const;
+
+ static BufferTextureData* CreateInternal(LayersIPCChannel* aAllocator,
+ const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ int32_t aBufferSize,
+ TextureFlags aTextureFlags);
+
+ virtual uint8_t* GetBuffer() = 0;
+ virtual size_t GetBufferSize() = 0;
+
+ BufferTextureData(const BufferDescriptor& aDescriptor, gfx::BackendType aMoz2DBackend)
+ : mDescriptor(aDescriptor)
+ , mMoz2DBackend(aMoz2DBackend)
+ {}
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ BufferDescriptor mDescriptor;
+ gfx::BackendType mMoz2DBackend;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/layers/BufferUnrotate.cpp b/system/graphics/layers/BufferUnrotate.cpp
new file mode 100644
index 000000000..224818773
--- /dev/null
+++ b/system/graphics/layers/BufferUnrotate.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm> // min & max
+#include <cstdlib>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
+ int aByteStride, int aXBoundary, int aYBoundary)
+{
+ if (aXBoundary != 0) {
+ uint8_t* line = new uint8_t[aByteWidth];
+ uint32_t smallStart = 0;
+ uint32_t smallLen = aXBoundary;
+ uint32_t smallDest = aByteWidth - aXBoundary;
+ uint32_t largeStart = aXBoundary;
+ uint32_t largeLen = aByteWidth - aXBoundary;
+ uint32_t largeDest = 0;
+ if (aXBoundary > aByteWidth / 2) {
+ smallStart = aXBoundary;
+ smallLen = aByteWidth - aXBoundary;
+ smallDest = 0;
+ largeStart = 0;
+ largeLen = aXBoundary;
+ largeDest = smallLen;
+ }
+
+ for (int y = 0; y < aHeight; y++) {
+ int yOffset = y * aByteStride;
+ memcpy(line, &aBuffer[yOffset + smallStart], smallLen);
+ memmove(&aBuffer[yOffset + largeDest], &aBuffer[yOffset + largeStart], largeLen);
+ memcpy(&aBuffer[yOffset + smallDest], line, smallLen);
+ }
+
+ delete[] line;
+ }
+
+ if (aYBoundary != 0) {
+ uint32_t smallestHeight = std::min(aHeight - aYBoundary, aYBoundary);
+ uint32_t largestHeight = std::max(aHeight - aYBoundary, aYBoundary);
+ uint32_t smallOffset = 0;
+ uint32_t largeOffset = aYBoundary * aByteStride;
+ uint32_t largeDestOffset = 0;
+ uint32_t smallDestOffset = largestHeight * aByteStride;
+ if (aYBoundary > aHeight / 2) {
+ smallOffset = aYBoundary * aByteStride;
+ largeOffset = 0;
+ largeDestOffset = smallestHeight * aByteStride;
+ smallDestOffset = 0;
+ }
+
+ uint8_t* smallestSide = new uint8_t[aByteStride * smallestHeight];
+ memcpy(smallestSide, &aBuffer[smallOffset], aByteStride * smallestHeight);
+ memmove(&aBuffer[largeDestOffset], &aBuffer[largeOffset], aByteStride * largestHeight);
+ memcpy(&aBuffer[smallDestOffset], smallestSide, aByteStride * smallestHeight);
+ delete[] smallestSide;
+ }
+}
+
diff --git a/system/graphics/layers/BufferUnrotate.h b/system/graphics/layers/BufferUnrotate.h
new file mode 100644
index 000000000..a292d02fc
--- /dev/null
+++ b/system/graphics/layers/BufferUnrotate.h
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BUFFERUNROTATE_H
+#define GFX_BUFFERUNROTATE_H
+
+#include "mozilla/Types.h"
+
+void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
+ int aByteStride, int aXByteBoundary, int aYBoundary);
+
+#endif // GFX_BUFFERUNROTATE_H
diff --git a/system/graphics/layers/Compositor.cpp b/system/graphics/layers/Compositor.cpp
new file mode 100644
index 000000000..e091975bf
--- /dev/null
+++ b/system/graphics/layers/Compositor.cpp
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/Compositor.h"
+#include "base/message_loop.h" // for MessageLoop
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "gfx2DGlue.h"
+#include "nsAppRunner.h"
+
+namespace mozilla {
+
+namespace layers {
+
+Compositor::Compositor(widget::CompositorWidget* aWidget,
+ CompositorBridgeParent* aParent)
+ : mCompositorID(0)
+ , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
+ , mParent(aParent)
+ , mPixelsPerFrame(0)
+ , mPixelsFilled(0)
+ , mScreenRotation(ROTATION_0)
+ , mWidget(aWidget)
+ , mIsDestroyed(false)
+ , mClearColor(0.0, 0.0, 0.0, 0.0)
+ , mDefaultClearColor(0.0, 0.0, 0.0, 0.0)
+{
+}
+
+Compositor::~Compositor()
+{
+ ReadUnlockTextures();
+}
+
+void
+Compositor::Destroy()
+{
+ ReadUnlockTextures();
+ FlushPendingNotifyNotUsed();
+ mIsDestroyed = true;
+}
+
+void
+Compositor::EndFrame()
+{
+ ReadUnlockTextures();
+ mLastCompositionEndTime = TimeStamp::Now();
+}
+
+void
+Compositor::ReadUnlockTextures()
+{
+ for (auto& texture : mUnlockAfterComposition) {
+ texture->ReadUnlock();
+ }
+ mUnlockAfterComposition.Clear();
+}
+
+void
+Compositor::UnlockAfterComposition(TextureHost* aTexture)
+{
+ mUnlockAfterComposition.AppendElement(aTexture);
+}
+
+void
+Compositor::NotifyNotUsedAfterComposition(TextureHost* aTextureHost)
+{
+ MOZ_ASSERT(!mIsDestroyed);
+
+ mNotifyNotUsedAfterComposition.AppendElement(aTextureHost);
+
+ // If Compositor holds many TextureHosts without compositing,
+ // the TextureHosts should be flushed to reduce memory consumption.
+ const int thresholdCount = 5;
+ const double thresholdSec = 2.0f;
+ if (mNotifyNotUsedAfterComposition.Length() > thresholdCount) {
+ TimeDuration duration = mLastCompositionEndTime ? TimeStamp::Now() - mLastCompositionEndTime : TimeDuration();
+ // Check if we could flush
+ if (duration.ToSeconds() > thresholdSec) {
+ FlushPendingNotifyNotUsed();
+ }
+ }
+}
+
+void
+Compositor::FlushPendingNotifyNotUsed()
+{
+ for (auto& textureHost : mNotifyNotUsedAfterComposition) {
+ textureHost->CallNotifyNotUsed();
+ }
+ mNotifyNotUsedAfterComposition.Clear();
+}
+
+/* static */ void
+Compositor::AssertOnCompositorThread()
+{
+ MOZ_ASSERT(!CompositorThreadHolder::Loop() ||
+ CompositorThreadHolder::Loop() == MessageLoop::current(),
+ "Can only call this from the compositor thread!");
+}
+
+bool
+Compositor::ShouldDrawDiagnostics(DiagnosticFlags aFlags)
+{
+ if ((aFlags & DiagnosticFlags::TILE) && !(mDiagnosticTypes & DiagnosticTypes::TILE_BORDERS)) {
+ return false;
+ }
+ if ((aFlags & DiagnosticFlags::BIGIMAGE) &&
+ !(mDiagnosticTypes & DiagnosticTypes::BIGIMAGE_BORDERS)) {
+ return false;
+ }
+ if (mDiagnosticTypes == DiagnosticTypes::NO_DIAGNOSTIC) {
+ return false;
+ }
+ return true;
+}
+
+void
+Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
+ const nsIntRegion& aVisibleRegion,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+ if (!ShouldDrawDiagnostics(aFlags)) {
+ return;
+ }
+
+ if (aVisibleRegion.GetNumRects() > 1) {
+ for (auto iter = aVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ DrawDiagnostics(aFlags | DiagnosticFlags::REGION_RECT,
+ IntRectToRect(iter.Get()), aClipRect, aTransform,
+ aFlashCounter);
+ }
+ }
+
+ DrawDiagnostics(aFlags, IntRectToRect(aVisibleRegion.GetBounds()),
+ aClipRect, aTransform, aFlashCounter);
+}
+
+void
+Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+ if (!ShouldDrawDiagnostics(aFlags)) {
+ return;
+ }
+
+ DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
+ aFlashCounter);
+}
+
+void
+Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+ int lWidth = 2;
+
+ gfx::Color color;
+ if (aFlags & DiagnosticFlags::CONTENT) {
+ color = gfx::Color(0.0f, 1.0f, 0.0f, 1.0f); // green
+ if (aFlags & DiagnosticFlags::COMPONENT_ALPHA) {
+ color = gfx::Color(0.0f, 1.0f, 1.0f, 1.0f); // greenish blue
+ }
+ } else if (aFlags & DiagnosticFlags::IMAGE) {
+ if (aFlags & DiagnosticFlags::NV12) {
+ color = gfx::Color(1.0f, 1.0f, 0.0f, 1.0f); // yellow
+ } else if (aFlags & DiagnosticFlags::YCBCR) {
+ color = gfx::Color(1.0f, 0.55f, 0.0f, 1.0f); // orange
+ } else {
+ color = gfx::Color(1.0f, 0.0f, 0.0f, 1.0f); // red
+ }
+ } else if (aFlags & DiagnosticFlags::COLOR) {
+ color = gfx::Color(0.0f, 0.0f, 1.0f, 1.0f); // blue
+ } else if (aFlags & DiagnosticFlags::CONTAINER) {
+ color = gfx::Color(0.8f, 0.0f, 0.8f, 1.0f); // purple
+ }
+
+ // make tile borders a bit more transparent to keep layer borders readable.
+ if (aFlags & DiagnosticFlags::TILE ||
+ aFlags & DiagnosticFlags::BIGIMAGE ||
+ aFlags & DiagnosticFlags::REGION_RECT) {
+ lWidth = 1;
+ color.r *= 0.7f;
+ color.g *= 0.7f;
+ color.b *= 0.7f;
+ color.a = color.a * 0.5f;
+ } else {
+ color.a = color.a * 0.7f;
+ }
+
+
+ if (mDiagnosticTypes & DiagnosticTypes::FLASH_BORDERS) {
+ float flash = (float)aFlashCounter / (float)DIAGNOSTIC_FLASH_COUNTER_MAX;
+ color.r *= flash;
+ color.g *= flash;
+ color.b *= flash;
+ }
+
+ SlowDrawRect(aVisibleRect, color, aClipRect, aTransform, lWidth);
+}
+
+static void
+UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aIntersection,
+ gfx::Rect aTextureCoords)
+{
+ // Calculate the relative offset of the intersection within the layer.
+ float dx = (aIntersection.x - aRect.x) / aRect.width;
+ float dy = (aIntersection.y - aRect.y) / aRect.height;
+
+ // Update the texture offset.
+ float x = aTextureCoords.x + dx * aTextureCoords.width;
+ float y = aTextureCoords.y + dy * aTextureCoords.height;
+
+ // Scale the texture width and height.
+ float w = aTextureCoords.width * aIntersection.width / aRect.width;
+ float h = aTextureCoords.height * aIntersection.height / aRect.height;
+
+ static const auto ValidateAndClamp = [](float& f) {
+ // Allow some numerical inaccuracy.
+ MOZ_ASSERT(f >= -0.0001f && f <= 1.0001f);
+
+ if (f >= 1.0f) f = 1.0f;
+ if (f <= 0.0f) f = 0.0f;
+ };
+
+ auto UpdatePoint = [&](const gfx::Point& p, gfx::Point& t)
+ {
+ t.x = x + (p.x - aIntersection.x) / aIntersection.width * w;
+ t.y = y + (p.y - aIntersection.y) / aIntersection.height * h;
+
+ ValidateAndClamp(t.x);
+ ValidateAndClamp(t.y);
+ };
+
+ UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
+ UpdatePoint(aTriangle.p2, aTriangle.textureCoords.p2);
+ UpdatePoint(aTriangle.p3, aTriangle.textureCoords.p3);
+}
+
+void
+Compositor::DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon3D>& aGeometry)
+{
+ if (!aGeometry) {
+ DrawQuad(aRect, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+ return;
+ }
+
+ // Cull invisible polygons.
+ if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
+ return;
+ }
+
+ gfx::Polygon3D clipped = aGeometry->ClipPolygon(aRect);
+ nsTArray<gfx::Triangle> triangles = clipped.ToTriangles();
+
+ for (gfx::Triangle& geometry : triangles) {
+ const gfx::Rect intersection = aRect.Intersect(geometry.BoundingBox());
+
+ // Cull invisible triangles.
+ if (intersection.IsEmpty()) {
+ continue;
+ }
+
+ MOZ_ASSERT(aRect.width > 0.0f && aRect.height > 0.0f);
+ MOZ_ASSERT(intersection.width > 0.0f && intersection.height > 0.0f);
+
+ gfx::TexturedTriangle triangle(Move(geometry));
+ triangle.width = aRect.width;
+ triangle.height = aRect.height;
+
+ // Since the texture was created for non-split geometry, we need to
+ // update the texture coordinates to account for the split.
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB) {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ UpdateTextureCoordinates(triangle, aRect, intersection,
+ texturedEffect->mTextureCoords);
+ }
+
+ DrawTriangle(triangle, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+ }
+}
+
+void
+Compositor::SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& aColor,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform, int aStrokeWidth)
+{
+ // TODO This should draw a rect using a single draw call but since
+ // this is only used for debugging overlays it's not worth optimizing ATM.
+ float opacity = 1.0f;
+ EffectChain effects;
+
+ effects.mPrimaryEffect = new EffectSolidColor(aColor);
+ // left
+ this->DrawQuad(gfx::Rect(aRect.x, aRect.y,
+ aStrokeWidth, aRect.height),
+ aClipRect, effects, opacity,
+ aTransform);
+ // top
+ this->DrawQuad(gfx::Rect(aRect.x + aStrokeWidth, aRect.y,
+ aRect.width - 2 * aStrokeWidth, aStrokeWidth),
+ aClipRect, effects, opacity,
+ aTransform);
+ // right
+ this->DrawQuad(gfx::Rect(aRect.x + aRect.width - aStrokeWidth, aRect.y,
+ aStrokeWidth, aRect.height),
+ aClipRect, effects, opacity,
+ aTransform);
+ // bottom
+ this->DrawQuad(gfx::Rect(aRect.x + aStrokeWidth, aRect.y + aRect.height - aStrokeWidth,
+ aRect.width - 2 * aStrokeWidth, aStrokeWidth),
+ aClipRect, effects, opacity,
+ aTransform);
+}
+
+void
+Compositor::FillRect(const gfx::Rect& aRect, const gfx::Color& aColor,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform)
+{
+ float opacity = 1.0f;
+ EffectChain effects;
+
+ effects.mPrimaryEffect = new EffectSolidColor(aColor);
+ this->DrawQuad(aRect,
+ aClipRect, effects, opacity,
+ aTransform);
+}
+
+
+static float
+WrapTexCoord(float v)
+{
+ // This should return values in range [0, 1.0)
+ return v - floorf(v);
+}
+
+static void
+SetRects(size_t n,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects,
+ float x0, float y0, float x1, float y1,
+ float tx0, float ty0, float tx1, float ty1,
+ bool flip_y)
+{
+ if (flip_y) {
+ std::swap(ty0, ty1);
+ }
+ (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
+ (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
+}
+
+#ifdef DEBUG
+static inline bool
+FuzzyEqual(float a, float b)
+{
+ return fabs(a - b) < 0.0001f;
+}
+static inline bool
+FuzzyLTE(float a, float b)
+{
+ return a <= b + 0.0001f;
+}
+#endif
+
+size_t
+DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects)
+{
+ gfx::Rect texCoordRect = aTexCoordRect;
+
+ // If the texture should be flipped, it will have negative height. Detect that
+ // here and compensate for it. We will flip each rect as we emit it.
+ bool flipped = false;
+ if (texCoordRect.height < 0) {
+ flipped = true;
+ texCoordRect.y += texCoordRect.height;
+ texCoordRect.height = -texCoordRect.height;
+ }
+
+ // Wrap the texture coordinates so they are within [0,1] and cap width/height
+ // at 1. We rely on this below.
+ texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x),
+ WrapTexCoord(texCoordRect.y)),
+ gfx::Size(std::min(texCoordRect.width, 1.0f),
+ std::min(texCoordRect.height, 1.0f)));
+
+ NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
+ texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
+ texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
+ texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
+ texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
+ texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
+ "We just wrapped the texture coordinates, didn't we?");
+
+ // Get the top left and bottom right points of the rectangle. Note that
+ // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
+ gfx::Point tl = texCoordRect.TopLeft();
+ gfx::Point br = texCoordRect.BottomRight();
+
+ NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
+ tl.y >= 0.0f && tl.y <= 1.0f &&
+ br.x >= tl.x && br.x <= 2.0f &&
+ br.y >= tl.y && br.y <= 2.0f &&
+ FuzzyLTE(br.x - tl.x, 1.0f) &&
+ FuzzyLTE(br.y - tl.y, 1.0f),
+ "Somehow generated invalid texture coordinates");
+
+ // Then check if we wrap in either the x or y axis.
+ bool xwrap = br.x > 1.0f;
+ bool ywrap = br.y > 1.0f;
+
+ // If xwrap is false, the texture will be sampled from tl.x .. br.x.
+ // If xwrap is true, then it will be split into tl.x .. 1.0, and
+ // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
+ // rectangle is also split appropriately, according to the calculated
+ // xmid/ymid values.
+ if (!xwrap && !ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
+ tl.x, tl.y, br.x, br.y,
+ flipped);
+ return 1;
+ }
+
+ // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
+ // wrap them here as well.
+ br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
+ ywrap ? WrapTexCoord(br.y) : br.y);
+
+ // If we wrap around along the x axis, we will draw first from
+ // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
+ // The same applies for the Y axis. The midpoints we calculate here are
+ // only valid if we actually wrap around.
+ GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
+ GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
+
+ // Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y
+ // to calculate width and height, respectively, to ensure that size will
+ // remain consistent going from absolute to relative and back again.
+ NS_ASSERTION(!xwrap ||
+ (xmid >= aRect.x &&
+ xmid <= aRect.XMost() &&
+ FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.XMost() - aRect.x)),
+ "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
+ NS_ASSERTION(!ywrap ||
+ (ymid >= aRect.y &&
+ ymid <= aRect.YMost() &&
+ FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.YMost() - aRect.y)),
+ "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
+
+ if (!xwrap && ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, aRect.XMost(), ymid,
+ tl.x, tl.y, br.x, 1.0f,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ aRect.x, ymid, aRect.XMost(), aRect.YMost(),
+ tl.x, 0.0f, br.x, br.y,
+ flipped);
+ return 2;
+ }
+
+ if (xwrap && !ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, xmid, aRect.YMost(),
+ tl.x, tl.y, 1.0f, br.y,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ xmid, aRect.y, aRect.XMost(), aRect.YMost(),
+ 0.0f, tl.y, br.x, br.y,
+ flipped);
+ return 2;
+ }
+
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, xmid, ymid,
+ tl.x, tl.y, 1.0f, 1.0f,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ xmid, aRect.y, aRect.XMost(), ymid,
+ 0.0f, tl.y, br.x, 1.0f,
+ flipped);
+ SetRects(2, aLayerRects, aTextureRects,
+ aRect.x, ymid, xmid, aRect.YMost(),
+ tl.x, 0.0f, 1.0f, br.y,
+ flipped);
+ SetRects(3, aLayerRects, aTextureRects,
+ xmid, ymid, aRect.XMost(), aRect.YMost(),
+ 0.0f, 0.0f, br.x, br.y,
+ flipped);
+ return 4;
+}
+
+gfx::IntRect
+Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad)
+{
+ // Compute the clip.
+ gfx::IntPoint rtOffset = GetCurrentRenderTarget()->GetOrigin();
+ gfx::IntSize rtSize = GetCurrentRenderTarget()->GetSize();
+
+ gfx::IntRect renderBounds(0, 0, rtSize.width, rtSize.height);
+ renderBounds.IntersectRect(renderBounds, aClipRect);
+ renderBounds.MoveBy(rtOffset);
+
+ // Apply the layer transform.
+ gfx::RectDouble dest = aTransform.TransformAndClipBounds(
+ gfx::RectDouble(aRect.x, aRect.y, aRect.width, aRect.height),
+ gfx::RectDouble(renderBounds.x, renderBounds.y, renderBounds.width, renderBounds.height));
+ dest -= rtOffset;
+
+ // Ensure we don't round out to -1, which trips up Direct3D.
+ dest.IntersectRect(dest, gfx::RectDouble(0, 0, rtSize.width, rtSize.height));
+
+ if (aOutLayerQuad) {
+ *aOutLayerQuad = gfx::Rect(dest.x, dest.y, dest.width, dest.height);
+ }
+
+ // Round out to integer.
+ gfx::IntRect result;
+ dest.RoundOut();
+ dest.ToIntRect(&result);
+
+ // Create a transform from adjusted clip space to render target space,
+ // translate it for the backdrop rect, then transform it into the backdrop's
+ // uv-space.
+ gfx::Matrix4x4 transform;
+ transform.PostScale(rtSize.width, rtSize.height, 1.0);
+ transform.PostTranslate(-result.x, -result.y, 0.0);
+ transform.PostScale(1 / float(result.width), 1 / float(result.height), 1.0);
+ *aOutTransform = transform;
+ return result;
+}
+
+gfx::IntRect
+Compositor::ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad)
+{
+ gfx::Rect boundingBox = aTriangle.BoundingBox();
+ return ComputeBackdropCopyRect(boundingBox, aClipRect, aTransform,
+ aOutTransform, aOutLayerQuad);
+}
+
+void
+Compositor::SetInvalid()
+{
+ mParent = nullptr;
+}
+
+bool
+Compositor::IsValid() const
+{
+ return !!mParent;
+}
+
+void
+Compositor::SetDispAcquireFence(Layer* aLayer)
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/Compositor.h b/system/graphics/layers/Compositor.h
new file mode 100644
index 000000000..4b5b2a9cb
--- /dev/null
+++ b/system/graphics/layers/Compositor.h
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITOR_H
+#define MOZILLA_GFX_COMPOSITOR_H
+
+#include "Units.h" // for ScreenPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Polygon.h" // for Polygon3D
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float
+#include "mozilla/gfx/Triangle.h" // for Triangle, TexturedTriangle
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticTypes, etc
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h"
+#include <vector>
+#include "mozilla/WidgetUtils.h"
+
+/**
+ * Different elements of a web pages are rendered into separate "layers" before
+ * they are flattened into the final image that is brought to the screen.
+ * See Layers.h for more informations about layers and why we use retained
+ * structures.
+ * Most of the documentation for layers is directly in the source code in the
+ * form of doc comments. An overview can also be found in the the wiki:
+ * https://wiki.mozilla.org/Gecko:Overview#Graphics
+ *
+ *
+ * # Main interfaces and abstractions
+ *
+ * - Layer, ShadowableLayer and LayerComposite
+ * (see Layers.h and ipc/ShadowLayers.h)
+ * - CompositableClient and CompositableHost
+ * (client/CompositableClient.h composite/CompositableHost.h)
+ * - TextureClient and TextureHost
+ * (client/TextureClient.h composite/TextureHost.h)
+ * - TextureSource
+ * (composite/TextureHost.h)
+ * - Forwarders
+ * (ipc/CompositableForwarder.h ipc/ShadowLayers.h)
+ * - Compositor
+ * (this file)
+ * - IPDL protocols
+ * (.ipdl files under the gfx/layers/ipc directory)
+ *
+ * The *Client and Shadowable* classes are always used on the content thread.
+ * Forwarders are always used on the content thread.
+ * The *Host and Shadow* classes are always used on the compositor thread.
+ * Compositors, TextureSource, and Effects are always used on the compositor
+ * thread.
+ * Most enums and constants are declared in LayersTypes.h and CompositorTypes.h.
+ *
+ *
+ * # Texture transfer
+ *
+ * Most layer classes own a Compositable plus some extra information like
+ * transforms and clip rects. They are platform independent.
+ * Compositable classes manipulate Texture objects and are reponsible for
+ * things like tiling, buffer rotation or double buffering. Compositables
+ * are also platform-independent. Examples of compositable classes are:
+ * - ImageClient
+ * - CanvasClient
+ * - ContentHost
+ * - etc.
+ * Texture classes (TextureClient and TextureHost) are thin abstractions over
+ * platform-dependent texture memory. They are maniplulated by compositables
+ * and don't know about buffer rotations and such. The purposes of TextureClient
+ * and TextureHost are to synchronize, serialize and deserialize texture data.
+ * TextureHosts provide access to TextureSources that are views on the
+ * Texture data providing the necessary api for Compositor backend to composite
+ * them.
+ *
+ * Compositable and Texture clients and hosts are created using factory methods.
+ * They should only be created by using their constructor in exceptional
+ * circumstances. The factory methods are located:
+ * TextureClient - CompositableClient::CreateTextureClient
+ * TextureHost - TextureHost::CreateTextureHost, which calls a
+ * platform-specific function, e.g., CreateTextureHostOGL
+ * CompositableClient - in the appropriate subclass, e.g.,
+ * CanvasClient::CreateCanvasClient
+ * CompositableHost - CompositableHost::Create
+ *
+ *
+ * # IPDL
+ *
+ * If off-main-thread compositing (OMTC) is enabled, compositing is performed
+ * in a dedicated thread. In some setups compositing happens in a dedicated
+ * process. Documentation may refer to either the compositor thread or the
+ * compositor process.
+ * See explanations in ShadowLayers.h.
+ *
+ *
+ * # Backend implementations
+ *
+ * Compositor backends like OpenGL or flavours of D3D live in their own directory
+ * under gfx/layers/. To add a new backend, implement at least the following
+ * interfaces:
+ * - Compositor (ex. CompositorOGL)
+ * - TextureHost (ex. SurfaceTextureHost)
+ * Depending on the type of data that needs to be serialized, you may need to
+ * add specific TextureClient implementations.
+ */
+
+class nsIWidget;
+
+namespace mozilla {
+namespace gfx {
+class Matrix;
+class DrawTarget;
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+struct Effect;
+struct EffectChain;
+class Image;
+class ImageHostOverlay;
+class Layer;
+class TextureSource;
+class DataTextureSource;
+class CompositingRenderTarget;
+class CompositorBridgeParent;
+class LayerManagerComposite;
+class CompositorOGL;
+class CompositorD3D11;
+class BasicCompositor;
+class TextureHost;
+class TextureReadLock;
+
+enum SurfaceInitMode
+{
+ INIT_MODE_NONE,
+ INIT_MODE_CLEAR
+};
+
+/**
+ * Common interface for compositor backends.
+ *
+ * Compositor provides a cross-platform interface to a set of operations for
+ * compositing quads. Compositor knows nothing about the layer tree. It must be
+ * told everything about each composited quad - contents, location, transform,
+ * opacity, etc.
+ *
+ * In theory it should be possible for different widgets to use the same
+ * compositor. In practice, we use one compositor per window.
+ *
+ * # Usage
+ *
+ * For an example of a user of Compositor, see LayerManagerComposite.
+ *
+ * Initialization: create a Compositor object, call Initialize().
+ *
+ * Destruction: destroy any resources associated with the compositor, call
+ * Destroy(), delete the Compositor object.
+ *
+ * Composition:
+ * call BeginFrame,
+ * for each quad to be composited:
+ * call MakeCurrent if necessary (not necessary if no other context has been
+ * made current),
+ * take care of any texture upload required to composite the quad, this step
+ * is backend-dependent,
+ * construct an EffectChain for the quad,
+ * call DrawQuad,
+ * call EndFrame.
+ * If the compositor is usually used for compositing but compositing is
+ * temporarily done without the compositor, call EndFrameForExternalComposition
+ * after compositing each frame so the compositor can remain internally
+ * consistent.
+ *
+ * By default, the compositor will render to the screen, to render to a target,
+ * call SetTargetContext or SetRenderTarget, the latter with a target created
+ * by CreateRenderTarget or CreateRenderTargetFromSource.
+ *
+ * The target and viewport methods can be called before any DrawQuad call and
+ * affect any subsequent DrawQuad calls.
+ */
+class Compositor
+{
+protected:
+ virtual ~Compositor();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(Compositor)
+
+ explicit Compositor(widget::CompositorWidget* aWidget,
+ CompositorBridgeParent* aParent = nullptr);
+
+ virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) { return nullptr; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) = 0;
+ virtual void Destroy();
+ bool IsDestroyed() const { return mIsDestroyed; }
+
+ virtual void DetachWidget() { mWidget = nullptr; }
+
+ /**
+ * Return true if the effect type is supported.
+ *
+ * By default Compositor implementations should support all effects but in
+ * some rare cases it is not possible to support an effect efficiently.
+ * This is the case for BasicCompositor with EffectYCbCr.
+ */
+ virtual bool SupportsEffect(EffectTypes aEffect) { return true; }
+
+ /**
+ * Request a texture host identifier that may be used for creating textures
+ * across process or thread boundaries that are compatible with this
+ * compositor.
+ */
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() = 0;
+
+ /**
+ * Properties of the compositor.
+ */
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) = 0;
+ virtual int32_t GetMaxTextureSize() const = 0;
+
+ /**
+ * Set the target for rendering. Results will have been written to aTarget by
+ * the time that EndFrame returns.
+ *
+ * If this method is not used, or we pass in nullptr, we target the compositor's
+ * usual swap chain and render to the screen.
+ */
+ void SetTargetContext(gfx::DrawTarget* aTarget, const gfx::IntRect& aRect)
+ {
+ mTarget = aTarget;
+ mTargetBounds = aRect;
+ }
+ gfx::DrawTarget* GetTargetContext() const
+ {
+ return mTarget;
+ }
+ void ClearTargetContext()
+ {
+ mTarget = nullptr;
+ }
+
+ typedef uint32_t MakeCurrentFlags;
+ static const MakeCurrentFlags ForceMakeCurrent = 0x1;
+ /**
+ * Make this compositor's rendering context the current context for the
+ * underlying graphics API. This may be a global operation, depending on the
+ * API. Our context will remain the current one until someone else changes it.
+ *
+ * Clients of the compositor should call this at the start of the compositing
+ * process, it might be required by texture uploads etc.
+ *
+ * If aFlags == ForceMakeCurrent then we will (re-)set our context on the
+ * underlying API even if it is already the current context.
+ */
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) = 0;
+
+ /**
+ * Creates a Surface that can be used as a rendering target by this
+ * compositor.
+ */
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect& aRect, SurfaceInitMode aInit) = 0;
+
+ /**
+ * Creates a Surface that can be used as a rendering target by this
+ * compositor, and initializes the surface by copying from aSource.
+ * If aSource is null, then the current screen buffer is used as source.
+ *
+ * aSourcePoint specifies the point in aSource to copy data from.
+ */
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) = 0;
+
+ /**
+ * Sets the given surface as the target for subsequent calls to DrawQuad.
+ * Passing null as aSurface sets the screen as the target.
+ */
+ virtual void SetRenderTarget(CompositingRenderTarget* aSurface) = 0;
+
+ /**
+ * Returns the current target for rendering. Will return null if we are
+ * rendering to the screen.
+ */
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const = 0;
+
+ /**
+ * Mostly the compositor will pull the size from a widget and this method will
+ * be ignored, but compositor implementations are free to use it if they like.
+ */
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) = 0;
+
+ /**
+ * Declare an offset to use when rendering layers. This will be ignored when
+ * rendering to a target instead of the screen.
+ */
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
+
+ void DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon3D>& aGeometry);
+
+ void DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const Maybe<gfx::Polygon3D>& aGeometry)
+ {
+ DrawGeometry(aRect, aClipRect, aEffectChain, aOpacity,
+ aTransform, aRect, aGeometry);
+ }
+
+ /**
+ * Tell the compositor to draw a quad. What to do draw and how it is
+ * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
+ * aTransform transforms from user space to screen space. If texture coords are
+ * required, these will be in the primary effect in the effect chain.
+ * aVisibleRect is used to determine which edges should be antialiased,
+ * without applying the effect to the inner edges of a tiled layer.
+ */
+ virtual void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) = 0;
+
+ /**
+ * Overload of DrawQuad, with aVisibleRect defaulted to the value of aRect.
+ * Use this when you are drawing a single quad that is not part of a tiled
+ * layer.
+ */
+ void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform) {
+ DrawQuad(aRect, aClipRect, aEffectChain, aOpacity, aTransform, aRect);
+ }
+
+ virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+ {
+ MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
+ }
+
+ /**
+ * Draw an unfilled solid color rect. Typically used for debugging overlays.
+ */
+ void SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& color,
+ const gfx::IntRect& aClipRect = gfx::IntRect(),
+ const gfx::Matrix4x4& aTransform = gfx::Matrix4x4(),
+ int aStrokeWidth = 1);
+
+ /**
+ * Draw a solid color filled rect. This is a simple DrawQuad helper.
+ */
+ void FillRect(const gfx::Rect& aRect, const gfx::Color& color,
+ const gfx::IntRect& aClipRect = gfx::IntRect(),
+ const gfx::Matrix4x4& aTransform = gfx::Matrix4x4());
+
+ void SetClearColor(const gfx::Color& aColor) {
+ mClearColor = aColor;
+ }
+
+ void SetDefaultClearColor(const gfx::Color& aColor) {
+ mDefaultClearColor = aColor;
+ }
+
+ void SetClearColorToDefault() {
+ mClearColor = mDefaultClearColor;
+ }
+
+ /*
+ * Clear aRect on current render target.
+ */
+ virtual void ClearRect(const gfx::Rect& aRect) = 0;
+
+ /**
+ * Start a new frame.
+ *
+ * aInvalidRect is the invalid region of the screen; it can be ignored for
+ * compositors where the performance for compositing the entire window is
+ * sufficient.
+ *
+ * aClipRectIn is the clip rect for the window in window space (optional).
+ * aTransform is the transform from user space to window space.
+ * aRenderBounds bounding rect for rendering, in user space.
+ *
+ * If aClipRectIn is null, this method sets *aClipRectOut to the clip rect
+ * actually used for rendering (if aClipRectIn is non-null, we will use that
+ * for the clip rect).
+ *
+ * If aRenderBoundsOut is non-null, it will be set to the render bounds
+ * actually used by the compositor in window space. If aRenderBoundsOut
+ * is returned empty, composition should be aborted.
+ *
+ * If aOpaque is true, then all of aInvalidRegion will be drawn to with
+ * opaque content.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect* aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect* aClipRectOut = nullptr,
+ gfx::IntRect* aRenderBoundsOut = nullptr) = 0;
+
+ /**
+ * Flush the current frame to the screen and tidy up.
+ *
+ * Derived class overriding this should call Compositor::EndFrame.
+ */
+ virtual void EndFrame();
+
+ virtual void SetDispAcquireFence(Layer* aLayer);
+
+ /**
+ * Post-rendering stuff if the rendering is done outside of this Compositor
+ * e.g., by Composer2D.
+ * aTransform is the transform from user space to window space.
+ */
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) = 0;
+
+ /**
+ * Whether textures created by this compositor can receive partial updates.
+ */
+ virtual bool SupportsPartialTextureUpdate() = 0;
+
+ void SetDiagnosticTypes(DiagnosticTypes aDiagnostics)
+ {
+ mDiagnosticTypes = aDiagnostics;
+ }
+
+ DiagnosticTypes GetDiagnosticTypes() const
+ {
+ return mDiagnosticTypes;
+ }
+
+ void DrawDiagnostics(DiagnosticFlags aFlags,
+ const gfx::Rect& visibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter = DIAGNOSTIC_FLASH_COUNTER_MAX);
+
+ void DrawDiagnostics(DiagnosticFlags aFlags,
+ const nsIntRegion& visibleRegion,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter = DIAGNOSTIC_FLASH_COUNTER_MAX);
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const = 0;
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const = 0;
+
+ virtual CompositorOGL* AsCompositorOGL() { return nullptr; }
+ virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; }
+ virtual BasicCompositor* AsBasicCompositor() { return nullptr; }
+
+ /**
+ * Each Compositor has a unique ID.
+ * This ID is used to keep references to each Compositor in a map accessed
+ * from the compositor thread only, so that async compositables can find
+ * the right compositor parent and schedule compositing even if the compositor
+ * changed.
+ */
+ uint32_t GetCompositorID() const
+ {
+ return mCompositorID;
+ }
+ void SetCompositorID(uint32_t aID)
+ {
+ MOZ_ASSERT(mCompositorID == 0, "The compositor ID must be set only once.");
+ mCompositorID = aID;
+ }
+
+ /**
+ * Notify the compositor that composition is being paused. This allows the
+ * compositor to temporarily release any resources.
+ * Between calling Pause and Resume, compositing may fail.
+ */
+ virtual void Pause() {}
+ /**
+ * Notify the compositor that composition is being resumed. The compositor
+ * regain any resources it requires for compositing.
+ * Returns true if succeeded.
+ */
+ virtual bool Resume() { return true; }
+
+ /**
+ * Call before rendering begins to ensure the compositor is ready to
+ * composite. Returns false if rendering should be aborted.
+ */
+ virtual bool Ready() { return true; }
+
+ virtual void ForcePresent() { }
+
+ virtual bool IsPendingComposite() { return false; }
+
+ virtual void FinishPendingComposite() {}
+
+ widget::CompositorWidget* GetWidget() const { return mWidget; }
+
+ virtual bool HasImageHostOverlays() { return false; }
+
+ virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) {}
+
+ virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) {}
+
+ /**
+ * Debug-build assertion that can be called to ensure code is running on the
+ * compositor thread.
+ */
+ static void AssertOnCompositorThread();
+
+ size_t GetFillRatio() {
+ float fillRatio = 0;
+ if (mPixelsFilled > 0 && mPixelsPerFrame > 0) {
+ fillRatio = 100.0f * float(mPixelsFilled) / float(mPixelsPerFrame);
+ if (fillRatio > 999.0f) {
+ fillRatio = 999.0f;
+ }
+ }
+ return fillRatio;
+ }
+
+ ScreenRotation GetScreenRotation() const {
+ return mScreenRotation;
+ }
+ void SetScreenRotation(ScreenRotation aRotation) {
+ mScreenRotation = aRotation;
+ }
+
+ TimeStamp GetCompositionTime() const {
+ return mCompositionTime;
+ }
+ void SetCompositionTime(TimeStamp aTimeStamp) {
+ mCompositionTime = aTimeStamp;
+ if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() &&
+ mCompositionTime >= mCompositeUntilTime) {
+ mCompositeUntilTime = TimeStamp();
+ }
+ }
+
+ void CompositeUntil(TimeStamp aTimeStamp) {
+ if (mCompositeUntilTime.IsNull() ||
+ mCompositeUntilTime < aTimeStamp) {
+ mCompositeUntilTime = aTimeStamp;
+ }
+ }
+ TimeStamp GetCompositeUntilTime() const {
+ return mCompositeUntilTime;
+ }
+
+ // A stale Compositor has no CompositorBridgeParent; it will not process
+ // frames and should not be used.
+ void SetInvalid();
+ bool IsValid() const;
+ CompositorBridgeParent* GetCompositorBridgeParent() const {
+ return mParent;
+ }
+
+ /// Most compositor backends operate asynchronously under the hood. This
+ /// means that when a layer stops using a texture it is often desirable to
+ /// wait for the end of the next composition before releasing the texture's
+ /// ReadLock.
+ /// This function provides a convenient way to do this delayed unlocking, if
+ /// the texture itself requires it.
+ void UnlockAfterComposition(TextureHost* aTexture);
+
+ /// Most compositor backends operate asynchronously under the hood. This
+ /// means that when a layer stops using a texture it is often desirable to
+ /// wait for the end of the next composition before NotifyNotUsed() call.
+ /// This function provides a convenient way to do this delayed NotifyNotUsed()
+ /// call, if the texture itself requires it.
+ /// See bug 1260611 and bug 1252835
+ void NotifyNotUsedAfterComposition(TextureHost* aTextureHost);
+
+ void FlushPendingNotifyNotUsed();
+
+protected:
+ void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter);
+
+ bool ShouldDrawDiagnostics(DiagnosticFlags);
+
+ // Should be called at the end of each composition.
+ void ReadUnlockTextures();
+
+ /**
+ * Given a layer rect, clip, and transform, compute the area of the backdrop that
+ * needs to be copied for mix-blending. The output transform translates from 0..1
+ * space into the backdrop rect space.
+ *
+ * The transformed layer quad is also optionally returned - this is the same as
+ * the result rect, before rounding.
+ */
+ gfx::IntRect ComputeBackdropCopyRect(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad = nullptr);
+
+ gfx::IntRect ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad = nullptr);
+
+
+ /**
+ * An array of locks that will need to be unlocked after the next composition.
+ */
+ nsTArray<RefPtr<TextureHost>> mUnlockAfterComposition;
+
+ /**
+ * An array of TextureHosts that will need to call NotifyNotUsed() after the next composition.
+ */
+ nsTArray<RefPtr<TextureHost>> mNotifyNotUsedAfterComposition;
+
+ /**
+ * Last Composition end time.
+ */
+ TimeStamp mLastCompositionEndTime;
+
+ /**
+ * Render time for the current composition.
+ */
+ TimeStamp mCompositionTime;
+ /**
+ * When nonnull, during rendering, some compositable indicated that it will
+ * change its rendering at this time. In order not to miss it, we composite
+ * on every vsync until this time occurs (this is the latest such time).
+ */
+ TimeStamp mCompositeUntilTime;
+
+ uint32_t mCompositorID;
+ DiagnosticTypes mDiagnosticTypes;
+ CompositorBridgeParent* mParent;
+
+ /**
+ * We keep track of the total number of pixels filled as we composite the
+ * current frame. This value is an approximation and is not accurate,
+ * especially in the presence of transforms.
+ */
+ size_t mPixelsPerFrame;
+ size_t mPixelsFilled;
+
+ ScreenRotation mScreenRotation;
+
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ widget::CompositorWidget* mWidget;
+
+ bool mIsDestroyed;
+
+ gfx::Color mClearColor;
+ gfx::Color mDefaultClearColor;
+
+private:
+ static LayersBackend sBackend;
+
+};
+
+// Returns the number of rects. (Up to 4)
+typedef gfx::Rect decomposedRectArrayT[4];
+size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects);
+
+static inline bool
+BlendOpIsMixBlendMode(gfx::CompositionOp aOp)
+{
+ switch (aOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ case gfx::CompositionOp::OP_SCREEN:
+ case gfx::CompositionOp::OP_OVERLAY:
+ case gfx::CompositionOp::OP_DARKEN:
+ case gfx::CompositionOp::OP_LIGHTEN:
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ case gfx::CompositionOp::OP_EXCLUSION:
+ case gfx::CompositionOp::OP_HUE:
+ case gfx::CompositionOp::OP_SATURATION:
+ case gfx::CompositionOp::OP_COLOR:
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COMPOSITOR_H */
diff --git a/system/graphics/layers/CompositorTypes.h b/system/graphics/layers/CompositorTypes.h
new file mode 100644
index 000000000..c6ce4c2cb
--- /dev/null
+++ b/system/graphics/layers/CompositorTypes.h
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITORTYPES_H
+#define MOZILLA_LAYERS_COMPOSITORTYPES_H
+
+#include <stdint.h> // for uint32_t
+#include <sys/types.h> // for int32_t
+#include "LayersTypes.h" // for LayersBackend, etc
+#include "nsXULAppAPI.h" // for GeckoProcessType, etc
+#include "mozilla/gfx/Types.h"
+#include "mozilla/EnumSet.h"
+
+#include "mozilla/TypedEnumBits.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Flags used by texture clients and texture hosts. These are passed from client
+ * side to host side when textures and compositables are created. Usually set
+ * by the compositableCient, they may be modified by either the compositable or
+ * texture clients.
+ */
+enum class TextureFlags : uint32_t {
+ NO_FLAGS = 0,
+ // Use nearest-neighbour texture filtering (as opposed to linear filtering).
+ USE_NEAREST_FILTER = 1 << 0,
+ // The compositor assumes everything is origin-top-left by default.
+ ORIGIN_BOTTOM_LEFT = 1 << 1,
+ // Force the texture to be represented using a single tile (note that this means
+ // tiled textures, not tiled layers).
+ DISALLOW_BIGIMAGE = 1 << 2,
+ // The buffer will be treated as if the RB bytes are swapped.
+ // This is useful for rendering using Cairo/Thebes, because there is no
+ // BGRX Android pixel format, and so we have to do byte swapping.
+ //
+ // For example, if the GraphicBuffer has an Android pixel format of
+ // PIXEL_FORMAT_RGBA_8888 and isRBSwapped is true, when it is sampled
+ // (for example, with GL), a BGRA shader should be used.
+ RB_SWAPPED = 1 << 3,
+ // Data in this texture has not been alpha-premultiplied.
+ // XXX - Apparently only used with ImageClient/Host
+ NON_PREMULTIPLIED = 1 << 4,
+ // The TextureClient should be recycled with recycle callback when no longer
+ // in used. When the texture is used in host side, ref count of TextureClient
+ // is transparently added by ShadowLayerForwarder or ImageBridgeChild.
+ RECYCLE = 1 << 5,
+ // If DEALLOCATE_CLIENT is set, the shared data is deallocated on the
+ // client side and requires some extra synchronizaion to ensure race-free
+ // deallocation.
+ // The default behaviour is to deallocate on the host side.
+ DEALLOCATE_CLIENT = 1 << 6,
+ DEALLOCATE_SYNC = 1 << 6, // XXX - make it a separate flag.
+ DEALLOCATE_MAIN_THREAD = 1 << 8,
+ // After being shared ith the compositor side, an immutable texture is never
+ // modified, it can only be read. It is safe to not Lock/Unlock immutable
+ // textures.
+ IMMUTABLE = 1 << 9,
+ // The contents of the texture must be uploaded or copied immediately
+ // during the transaction, because the producer may want to write
+ // to it again.
+ IMMEDIATE_UPLOAD = 1 << 10,
+ // The texture is part of a component-alpha pair
+ COMPONENT_ALPHA = 1 << 11,
+ // The texture is being allocated for a compositor that no longer exists.
+ // This flag is only used in the parent process.
+ INVALID_COMPOSITOR = 1 << 12,
+ // The texture was created by converting from YCBCR to RGB
+ RGB_FROM_YCBCR = 1 << 13,
+
+ // OR union of all valid bits
+ ALL_BITS = (1 << 14) - 1,
+ // the default flags
+ DEFAULT = NO_FLAGS
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextureFlags)
+
+static inline bool
+TextureRequiresLocking(TextureFlags aFlags)
+{
+ // If we're not double buffered, or uploading
+ // within a transaction, then we need to support
+ // locking correctly.
+ return !(aFlags & (TextureFlags::IMMEDIATE_UPLOAD |
+ TextureFlags::IMMUTABLE));
+}
+
+/**
+ * The type of debug diagnostic to enable.
+ */
+enum class DiagnosticTypes : uint8_t {
+ NO_DIAGNOSTIC = 0,
+ TILE_BORDERS = 1 << 0,
+ LAYER_BORDERS = 1 << 1,
+ BIGIMAGE_BORDERS = 1 << 2,
+ FLASH_BORDERS = 1 << 3,
+ ALL_BITS = (1 << 4) - 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticTypes)
+
+#define DIAGNOSTIC_FLASH_COUNTER_MAX 100
+
+/**
+ * Information about the object that is being diagnosed.
+ */
+enum class DiagnosticFlags : uint16_t {
+ NO_DIAGNOSTIC = 0,
+ IMAGE = 1 << 0,
+ CONTENT = 1 << 1,
+ CANVAS = 1 << 2,
+ COLOR = 1 << 3,
+ CONTAINER = 1 << 4,
+ TILE = 1 << 5,
+ BIGIMAGE = 1 << 6,
+ COMPONENT_ALPHA = 1 << 7,
+ REGION_RECT = 1 << 8,
+ NV12 = 1 << 9,
+ YCBCR = 1 << 10
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticFlags)
+
+/**
+ * See gfx/layers/Effects.h
+ */
+enum class EffectTypes : uint8_t {
+ MASK,
+ BLEND_MODE,
+ COLOR_MATRIX,
+ MAX_SECONDARY, // sentinel for the count of secondary effect types
+ RGB,
+ YCBCR,
+ NV12,
+ COMPONENT_ALPHA,
+ SOLID_COLOR,
+ RENDER_TARGET,
+ MAX //sentinel for the count of all effect types
+};
+
+/**
+ * How the Compositable should manage textures.
+ */
+enum class CompositableType : uint8_t {
+ UNKNOWN,
+ CONTENT_TILED, // tiled painted layer
+ IMAGE, // image with single buffering
+ IMAGE_BRIDGE, // ImageBridge protocol
+ CONTENT_SINGLE, // painted layer interface, single buffering
+ CONTENT_DOUBLE, // painted layer interface, double buffering
+ COUNT
+};
+
+#ifdef XP_WIN
+typedef void* SyncHandle;
+#else
+typedef uintptr_t SyncHandle;
+#endif // XP_WIN
+
+/**
+ * Sent from the compositor to the content-side LayerManager, includes properties
+ * of the compositor and should (in the future) include information about what
+ * kinds of buffer and texture clients to create.
+ */
+struct TextureFactoryIdentifier
+{
+ LayersBackend mParentBackend;
+ GeckoProcessType mParentProcessType;
+ int32_t mMaxTextureSize;
+ bool mSupportsTextureBlitting;
+ bool mSupportsPartialUploads;
+ bool mSupportsComponentAlpha;
+ SyncHandle mSyncHandle;
+
+ explicit TextureFactoryIdentifier(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
+ GeckoProcessType aParentProcessType = GeckoProcessType_Default,
+ int32_t aMaxTextureSize = 4096,
+ bool aSupportsTextureBlitting = false,
+ bool aSupportsPartialUploads = false,
+ bool aSupportsComponentAlpha = true,
+ SyncHandle aSyncHandle = 0)
+ : mParentBackend(aLayersBackend)
+ , mParentProcessType(aParentProcessType)
+ , mMaxTextureSize(aMaxTextureSize)
+ , mSupportsTextureBlitting(aSupportsTextureBlitting)
+ , mSupportsPartialUploads(aSupportsPartialUploads)
+ , mSupportsComponentAlpha(aSupportsComponentAlpha)
+ , mSyncHandle(aSyncHandle)
+ {}
+};
+
+/**
+ * Information required by the compositor from the content-side for creating or
+ * using compositables and textures.
+ * XXX - TextureInfo is a bad name: this information is useful for the compositable,
+ * not the Texture. And ith new Textures, only the compositable type is really
+ * useful. This may (should) be removed in the near future.
+ */
+struct TextureInfo
+{
+ CompositableType mCompositableType;
+ TextureFlags mTextureFlags;
+
+ TextureInfo()
+ : mCompositableType(CompositableType::UNKNOWN)
+ , mTextureFlags(TextureFlags::NO_FLAGS)
+ {}
+
+ explicit TextureInfo(CompositableType aType,
+ TextureFlags aTextureFlags = TextureFlags::DEFAULT)
+ : mCompositableType(aType)
+ , mTextureFlags(aTextureFlags)
+ {}
+
+ bool operator==(const TextureInfo& aOther) const
+ {
+ return mCompositableType == aOther.mCompositableType &&
+ mTextureFlags == aOther.mTextureFlags;
+ }
+};
+
+/**
+ * How a SurfaceDescriptor will be opened.
+ *
+ * See ShadowLayerForwarder::OpenDescriptor for example.
+ */
+enum class OpenMode : uint8_t {
+ OPEN_NONE = 0,
+ OPEN_READ = 0x1,
+ OPEN_WRITE = 0x2,
+ OPEN_READ_WRITE = OPEN_READ|OPEN_WRITE,
+ OPEN_READ_ONLY = OPEN_READ,
+ OPEN_WRITE_ONLY = OPEN_WRITE
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
+
+// The kinds of mask texture a shader can support
+// We rely on the items in this enum being sequential
+enum class MaskType : uint8_t {
+ MaskNone = 0, // no mask layer
+ Mask, // mask layer
+ NumMaskTypes
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/CopyableCanvasLayer.cpp b/system/graphics/layers/CopyableCanvasLayer.cpp
new file mode 100644
index 000000000..7ec9ce0cd
--- /dev/null
+++ b/system/graphics/layers/CopyableCanvasLayer.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CopyableCanvasLayer.h"
+
+#include "BasicLayersImpl.h" // for FillWithMask, etc
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "SharedSurface.h" // for SharedSurface
+#include "SharedSurfaceGL.h" // for SharedSurface
+#include "gfxPattern.h" // for gfxPattern, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "gfxUtils.h"
+#include "client/TextureClientSharedSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImplData) :
+ CanvasLayer(aLayerManager, aImplData)
+ , mGLFrontbuffer(nullptr)
+ , mIsAlphaPremultiplied(true)
+ , mOriginPos(gl::OriginPos::TopLeft)
+ , mIsMirror(false)
+{
+ MOZ_COUNT_CTOR(CopyableCanvasLayer);
+}
+
+CopyableCanvasLayer::~CopyableCanvasLayer()
+{
+ MOZ_COUNT_DTOR(CopyableCanvasLayer);
+}
+
+void
+CopyableCanvasLayer::Initialize(const Data& aData)
+{
+ if (aData.mGLContext) {
+ mGLContext = aData.mGLContext;
+ mIsAlphaPremultiplied = aData.mIsGLAlphaPremult;
+ mOriginPos = gl::OriginPos::BottomLeft;
+ mIsMirror = aData.mIsMirror;
+
+ MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
+
+ if (aData.mFrontbufferGLTex) {
+ gfx::IntSize size(aData.mSize.width, aData.mSize.height);
+ mGLFrontbuffer = SharedSurface_Basic::Wrap(aData.mGLContext, size, aData.mHasAlpha,
+ aData.mFrontbufferGLTex);
+ mBufferProvider = aData.mBufferProvider;
+ }
+ } else if (aData.mBufferProvider) {
+ mBufferProvider = aData.mBufferProvider;
+ } else if (aData.mRenderer) {
+ mAsyncRenderer = aData.mRenderer;
+ mOriginPos = gl::OriginPos::BottomLeft;
+ } else {
+ MOZ_CRASH("GFX: CanvasLayer created without BufferProvider, DrawTarget or GLContext?");
+ }
+
+ mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
+}
+
+bool
+CopyableCanvasLayer::IsDataValid(const Data& aData)
+{
+ return mGLContext == aData.mGLContext;
+}
+
+DataSourceSurface*
+CopyableCanvasLayer::GetTempSurface(const IntSize& aSize,
+ const SurfaceFormat aFormat)
+{
+ if (!mCachedTempSurface ||
+ aSize != mCachedTempSurface->GetSize() ||
+ aFormat != mCachedTempSurface->GetFormat())
+ {
+ // Create a surface aligned to 8 bytes since that's the highest alignment WebGL can handle.
+ uint32_t stride = GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
+ mCachedTempSurface = Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, stride);
+ }
+
+ return mCachedTempSurface;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/CopyableCanvasLayer.h b/system/graphics/layers/CopyableCanvasLayer.h
new file mode 100644
index 000000000..92eec0090
--- /dev/null
+++ b/system/graphics/layers/CopyableCanvasLayer.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_COPYABLECANVASLAYER_H
+#define GFX_COPYABLECANVASLAYER_H
+
+#include <stdint.h> // for uint32_t
+#include "GLContextTypes.h" // for GLContext
+#include "Layers.h" // for CanvasLayer, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxTypes.h"
+#include "gfxPlatform.h" // for gfxImageFormat
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+
+namespace gl {
+class SharedSurface;
+} // namespace gl
+
+namespace layers {
+
+/**
+ * A shared CanvasLayer implementation that supports copying
+ * its contents into a gfxASurface using UpdateSurface.
+ */
+class CopyableCanvasLayer : public CanvasLayer
+{
+public:
+ CopyableCanvasLayer(LayerManager* aLayerManager, void *aImplData);
+
+protected:
+ virtual ~CopyableCanvasLayer();
+
+public:
+ virtual void Initialize(const Data& aData) override;
+
+ virtual bool IsDataValid(const Data& aData) override;
+
+ bool IsGLLayer() { return !!mGLContext; }
+
+protected:
+ RefPtr<gl::GLContext> mGLContext;
+ RefPtr<PersistentBufferProvider> mBufferProvider;
+ UniquePtr<gl::SharedSurface> mGLFrontbuffer;
+
+ bool mIsAlphaPremultiplied;
+ gl::OriginPos mOriginPos;
+ bool mIsMirror;
+
+ RefPtr<gfx::DataSourceSurface> mCachedTempSurface;
+
+ gfx::DataSourceSurface* GetTempSurface(const gfx::IntSize& aSize,
+ const gfx::SurfaceFormat aFormat);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/D3D11ShareHandleImage.cpp b/system/graphics/layers/D3D11ShareHandleImage.cpp
new file mode 100644
index 000000000..e00a5e3b7
--- /dev/null
+++ b/system/graphics/layers/D3D11ShareHandleImage.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "D3D11ShareHandleImage.h"
+#include "gfxImageSurface.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "d3d11.h"
+
+namespace mozilla {
+namespace layers {
+
+D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize& aSize,
+ const gfx::IntRect& aRect)
+ : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE),
+ mSize(aSize),
+ mPictureRect(aRect)
+{
+}
+
+bool
+D3D11ShareHandleImage::AllocateTexture(D3D11RecycleAllocator* aAllocator, ID3D11Device* aDevice)
+{
+ if (aAllocator) {
+ mTextureClient = aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, mSize);
+ if (mTextureClient) {
+ mTexture = static_cast<D3D11TextureData*>(mTextureClient->GetInternalData())->GetD3D11Texture();
+ return true;
+ }
+ return false;
+ } else {
+ MOZ_ASSERT(aDevice);
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ mSize.width, mSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+
+ HRESULT hr = aDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(mTexture));
+ return SUCCEEDED(hr);
+ }
+}
+
+gfx::IntSize
+D3D11ShareHandleImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+D3D11ShareHandleImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient;
+}
+
+already_AddRefed<gfx::SourceSurface>
+D3D11ShareHandleImage::GetAsSourceSurface()
+{
+ RefPtr<ID3D11Texture2D> texture = GetTexture();
+ if (!texture) {
+ NS_WARNING("Cannot readback from shared texture because no texture is available.");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ texture->GetDevice(getter_AddRefs(device));
+
+ D3D11_TEXTURE2D_DESC desc;
+ texture->GetDesc(&desc);
+
+ CD3D11_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height);
+ softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ softDesc.BindFlags = 0;
+ softDesc.MiscFlags = 0;
+ softDesc.MipLevels = 1;
+ softDesc.Usage = D3D11_USAGE_STAGING;
+
+ RefPtr<ID3D11Texture2D> softTexture;
+ HRESULT hr = device->CreateTexture2D(&softDesc,
+ NULL,
+ static_cast<ID3D11Texture2D**>(getter_AddRefs(softTexture)));
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to create 2D staging texture.");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ device->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ return nullptr;
+ }
+
+ context->CopyResource(softTexture, texture);
+
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ gfx::DataSourceSurface::MappedSurface mappedSurface;
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
+ return nullptr;
+ }
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = context->Map(softTexture, 0, D3D11_MAP_READ, 0, &map);
+ if (!SUCCEEDED(hr)) {
+ surface->Unmap();
+ return nullptr;
+ }
+
+ for (int y = 0; y < mSize.height; y++) {
+ memcpy(mappedSurface.mData + mappedSurface.mStride * y,
+ (unsigned char*)(map.pData) + map.RowPitch * y,
+ mSize.width * 4);
+ }
+
+ context->Unmap(softTexture, 0);
+ surface->Unmap();
+
+ return surface.forget();
+}
+
+ID3D11Texture2D*
+D3D11ShareHandleImage::GetTexture() const {
+ return mTexture;
+}
+
+already_AddRefed<TextureClient>
+D3D11RecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return CreateD3D11TextureClientWithDevice(aSize, aFormat,
+ aTextureFlags, aAllocFlags,
+ mDevice, mSurfaceAllocator->GetTextureForwarder());
+}
+
+already_AddRefed<TextureClient>
+D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize)
+{
+ RefPtr<TextureClient> textureClient =
+ CreateOrRecycle(aFormat,
+ aSize,
+ BackendSelector::Content,
+ layers::TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
+ return textureClient.forget();
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/D3D11ShareHandleImage.h b/system/graphics/layers/D3D11ShareHandleImage.h
new file mode 100644
index 000000000..bff03e93a
--- /dev/null
+++ b/system/graphics/layers/D3D11ShareHandleImage.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_D311_SHARE_HANDLE_IMAGE_H
+#define GFX_D311_SHARE_HANDLE_IMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "d3d11.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class D3D11RecycleAllocator : public TextureClientRecycleAllocator
+{
+public:
+ explicit D3D11RecycleAllocator(KnowsCompositor* aAllocator,
+ ID3D11Device* aDevice)
+ : TextureClientRecycleAllocator(aAllocator)
+ , mDevice(aDevice)
+ {}
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize);
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags) override;
+
+ RefPtr<ID3D11Device> mDevice;
+};
+
+// Image class that wraps a ID3D11Texture2D. This class copies the image
+// passed into SetData(), so that it can be accessed from other D3D devices.
+// This class also manages the synchronization of the copy, to ensure the
+// resource is ready to use.
+class D3D11ShareHandleImage final : public Image {
+public:
+ D3D11ShareHandleImage(const gfx::IntSize& aSize,
+ const gfx::IntRect& aRect);
+ ~D3D11ShareHandleImage() override {}
+
+ bool AllocateTexture(D3D11RecycleAllocator* aAllocator, ID3D11Device* aDevice);
+
+ gfx::IntSize GetSize() override;
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+ virtual gfx::IntRect GetPictureRect() override { return mPictureRect; }
+
+ ID3D11Texture2D* GetTexture() const;
+
+private:
+ gfx::IntSize mSize;
+ gfx::IntRect mPictureRect;
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ID3D11Texture2D> mTexture;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/system/graphics/layers/D3D9SurfaceImage.cpp b/system/graphics/layers/D3D9SurfaceImage.cpp
new file mode 100644
index 000000000..c5538105d
--- /dev/null
+++ b/system/graphics/layers/D3D9SurfaceImage.cpp
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "D3D9SurfaceImage.h"
+#include "gfx2DGlue.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+DXGID3D9TextureData::DXGID3D9TextureData(gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture, HANDLE aHandle,
+ IDirect3DDevice9* aDevice)
+: mDevice(aDevice)
+, mTexture(aTexture)
+, mFormat(aFormat)
+, mHandle(aHandle)
+{
+ MOZ_COUNT_CTOR(DXGID3D9TextureData);
+}
+
+DXGID3D9TextureData::~DXGID3D9TextureData()
+{
+ gfxWindowsPlatform::sD3D9SharedTextures -= mDesc.Width * mDesc.Height * 4;
+ MOZ_COUNT_DTOR(DXGID3D9TextureData);
+}
+
+// static
+DXGID3D9TextureData*
+DXGID3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags,
+ IDirect3DDevice9* aDevice)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+ MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8A8);
+ if (aFormat != gfx::SurfaceFormat::B8G8R8A8) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HANDLE shareHandle = nullptr;
+ HRESULT hr = aDevice->CreateTexture(aSize.width, aSize.height,
+ 1,
+ D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(texture),
+ &shareHandle);
+ if (FAILED(hr) || !shareHandle) {
+ return nullptr;
+ }
+
+ D3DSURFACE_DESC surfaceDesc;
+ hr = texture->GetLevelDesc(0, &surfaceDesc);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ DXGID3D9TextureData* data = new DXGID3D9TextureData(aFormat, texture, shareHandle, aDevice);
+ data->mDesc = surfaceDesc;
+
+ gfxWindowsPlatform::sD3D9SharedTextures += aSize.width * aSize.height * 4;
+ return data;
+}
+
+void
+DXGID3D9TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = GetSize();
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+}
+
+already_AddRefed<IDirect3DSurface9>
+DXGID3D9TextureData::GetD3D9Surface() const
+{
+ RefPtr<IDirect3DSurface9> textureSurface;
+ HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(textureSurface));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ return textureSurface.forget();
+}
+
+bool
+DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorD3D10((WindowsHandle)(mHandle), mFormat, GetSize());
+ return true;
+}
+
+D3D9SurfaceImage::D3D9SurfaceImage()
+ : Image(nullptr, ImageFormat::D3D9_RGB32_TEXTURE)
+ , mSize(0, 0)
+ , mShareHandle(0)
+ , mValid(true)
+{}
+
+D3D9SurfaceImage::~D3D9SurfaceImage()
+{
+}
+
+HRESULT
+D3D9SurfaceImage::AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+ IDirect3DSurface9* aSurface,
+ const gfx::IntRect& aRegion)
+{
+ NS_ENSURE_TRUE(aSurface, E_POINTER);
+ HRESULT hr;
+ RefPtr<IDirect3DSurface9> surface = aSurface;
+
+ RefPtr<IDirect3DDevice9> device;
+ hr = surface->GetDevice(getter_AddRefs(device));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
+
+ RefPtr<IDirect3D9> d3d9;
+ hr = device->GetDirect3D(getter_AddRefs(d3d9));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
+
+ D3DSURFACE_DESC desc;
+ surface->GetDesc(&desc);
+ // Ensure we can convert the textures format to RGB conversion
+ // in StretchRect. Fail if we can't.
+ hr = d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ desc.Format,
+ D3DFMT_A8R8G8B8);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+ // DXVA surfaces aren't created sharable, so we need to copy the surface
+ // to a sharable texture to that it's accessible to the layer manager's
+ // device.
+ if (aAllocator) {
+ mTextureClient =
+ aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, aRegion.Size());
+ if (!mTextureClient) {
+ return E_FAIL;
+ }
+
+ DXGID3D9TextureData* texData =
+ static_cast<DXGID3D9TextureData*>(mTextureClient->GetInternalData());
+ mTexture = texData->GetD3D9Texture();
+ mShareHandle = texData->GetShareHandle();
+ mDesc = texData->GetDesc();
+ } else {
+ hr = device->CreateTexture(aRegion.Size().width, aRegion.Size().height,
+ 1,
+ D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(mTexture),
+ &mShareHandle);
+ if (FAILED(hr) || !mShareHandle) {
+ return E_FAIL;
+ }
+
+ hr = mTexture->GetLevelDesc(0, &mDesc);
+ if (FAILED(hr)) {
+ return E_FAIL;
+ }
+ }
+
+ // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
+ RefPtr<IDirect3DSurface9> textureSurface = GetD3D9Surface();
+ if (!textureSurface) {
+ return E_FAIL;
+ }
+
+ RECT src = { aRegion.x, aRegion.y, aRegion.x+aRegion.width, aRegion.y+aRegion.height };
+ hr = device->StretchRect(surface, &src, textureSurface, nullptr, D3DTEXF_NONE);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+ mSize = aRegion.Size();
+ return S_OK;
+}
+
+already_AddRefed<IDirect3DSurface9>
+D3D9SurfaceImage::GetD3D9Surface()
+{
+ RefPtr<IDirect3DSurface9> textureSurface;
+ HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(textureSurface));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+ return textureSurface.forget();
+}
+
+const D3DSURFACE_DESC&
+D3D9SurfaceImage::GetDesc() const
+{
+ return mDesc;
+}
+
+HANDLE
+D3D9SurfaceImage::GetShareHandle() const
+{
+ return mShareHandle;
+}
+
+gfx::IntSize
+D3D9SurfaceImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+D3D9SurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ MOZ_ASSERT(mTextureClient);
+ MOZ_ASSERT(mTextureClient->GetAllocator() == aForwarder->GetTextureForwarder());
+ return mTextureClient;
+}
+
+already_AddRefed<gfx::SourceSurface>
+D3D9SurfaceImage::GetAsSourceSurface()
+{
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ HRESULT hr;
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ // Readback the texture from GPU memory into system memory, so that
+ // we can copy it into the Cairo image. This is expensive.
+ RefPtr<IDirect3DSurface9> textureSurface = GetD3D9Surface();
+ if (!textureSurface) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DDevice9> device;
+ hr = textureSurface->GetDevice(getter_AddRefs(device));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<IDirect3DSurface9> systemMemorySurface;
+ hr = device->CreateOffscreenPlainSurface(mSize.width,
+ mSize.height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(systemMemorySurface),
+ 0);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ hr = device->GetRenderTargetData(textureSurface, systemMemorySurface);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ D3DLOCKED_RECT rect;
+ hr = systemMemorySurface->LockRect(&rect, nullptr, 0);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ gfx::DataSourceSurface::MappedSurface mappedSurface;
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
+ systemMemorySurface->UnlockRect();
+ return nullptr;
+ }
+
+ const unsigned char* src = (const unsigned char*)(rect.pBits);
+ const unsigned srcPitch = rect.Pitch;
+ for (int y = 0; y < mSize.height; y++) {
+ memcpy(mappedSurface.mData + mappedSurface.mStride * y,
+ (unsigned char*)(src) + srcPitch * y,
+ mSize.width * 4);
+ }
+
+ systemMemorySurface->UnlockRect();
+ surface->Unmap();
+
+ return surface.forget();
+}
+
+already_AddRefed<TextureClient>
+D3D9RecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ TextureData* data = DXGID3D9TextureData::Create(aSize, aFormat, aTextureFlags, mDevice);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ mSurfaceAllocator->GetTextureForwarder());
+}
+
+already_AddRefed<TextureClient>
+D3D9RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize)
+{
+ return CreateOrRecycle(aFormat, aSize, BackendSelector::Content,
+ TextureFlags::DEFAULT);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/D3D9SurfaceImage.h b/system/graphics/layers/D3D9SurfaceImage.h
new file mode 100644
index 000000000..81cfa2cad
--- /dev/null
+++ b/system/graphics/layers/D3D9SurfaceImage.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_D3DSURFACEIMAGE_H
+#define GFX_D3DSURFACEIMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "d3d9.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureClient;
+
+class D3D9RecycleAllocator : public TextureClientRecycleAllocator
+{
+public:
+ explicit D3D9RecycleAllocator(KnowsCompositor* aAllocator,
+ IDirect3DDevice9* aDevice)
+ : TextureClientRecycleAllocator(aAllocator)
+ , mDevice(aDevice)
+ {}
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize);
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags) override;
+
+ RefPtr<IDirect3DDevice9> mDevice;
+};
+
+/**
+ * Wraps a D3D9 texture, shared with the compositor though DXGI.
+ * At the moment it is only used with D3D11 compositing, and the corresponding
+ * TextureHost is DXGITextureHostD3D11.
+ */
+class DXGID3D9TextureData : public TextureData
+{
+public:
+ static DXGID3D9TextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureFlags aFlags, IDirect3DDevice9* aDevice);
+
+ ~DXGID3D9TextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override {}
+
+ IDirect3DDevice9* GetD3D9Device() { return mDevice; }
+ IDirect3DTexture9* GetD3D9Texture() { return mTexture; }
+ HANDLE GetShareHandle() const { return mHandle; }
+ already_AddRefed<IDirect3DSurface9> GetD3D9Surface() const;
+
+ const D3DSURFACE_DESC& GetDesc() const
+ {
+ return mDesc;
+ }
+
+ gfx::IntSize GetSize() const { return gfx::IntSize(mDesc.Width, mDesc.Height); }
+
+protected:
+ DXGID3D9TextureData(gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture, HANDLE aHandle,
+ IDirect3DDevice9* aDevice);
+
+ RefPtr<IDirect3DDevice9> mDevice;
+ RefPtr<IDirect3DTexture9> mTexture;
+ gfx::SurfaceFormat mFormat;
+ HANDLE mHandle;
+ D3DSURFACE_DESC mDesc;
+};
+
+
+// Image class that wraps a IDirect3DSurface9. This class copies the image
+// passed into SetData(), so that it can be accessed from other D3D devices.
+// This class also manages the synchronization of the copy, to ensure the
+// resource is ready to use.
+class D3D9SurfaceImage : public Image {
+public:
+ explicit D3D9SurfaceImage();
+ virtual ~D3D9SurfaceImage();
+
+ HRESULT AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+ IDirect3DSurface9* aSurface,
+ const gfx::IntRect& aRegion);
+
+ // Returns the description of the shared surface.
+ const D3DSURFACE_DESC& GetDesc() const;
+
+ gfx::IntSize GetSize() override;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ already_AddRefed<IDirect3DSurface9> GetD3D9Surface();
+
+ HANDLE GetShareHandle() const;
+
+ virtual bool IsValid() override { return mValid; }
+
+ void Invalidate() { mValid = false; }
+
+private:
+
+ gfx::IntSize mSize;
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<IDirect3DTexture9> mTexture;
+ HANDLE mShareHandle;
+ D3DSURFACE_DESC mDesc;
+ bool mValid;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/system/graphics/layers/DirectedGraph.h b/system/graphics/layers/DirectedGraph.h
new file mode 100644
index 000000000..272e16c1f
--- /dev/null
+++ b/system/graphics/layers/DirectedGraph.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DIRECTEDGRAPH_H
+#define GFX_DIRECTEDGRAPH_H
+
+#include "gfxTypes.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+template <typename T>
+class DirectedGraph {
+public:
+
+ class Edge {
+ public:
+ Edge(T aFrom, T aTo) : mFrom(aFrom), mTo(aTo) {}
+
+ bool operator==(const Edge& aOther) const
+ {
+ return mFrom == aOther.mFrom && mTo == aOther.mTo;
+ }
+
+ T mFrom;
+ T mTo;
+ };
+
+ class RemoveEdgesToComparator
+ {
+ public:
+ bool Equals(const Edge& a, T const& b) const { return a.mTo == b; }
+ };
+
+ /**
+ * Add a new edge to the graph.
+ */
+ void AddEdge(Edge aEdge)
+ {
+ NS_ASSERTION(!mEdges.Contains(aEdge), "Adding a duplicate edge!");
+ mEdges.AppendElement(aEdge);
+ }
+
+ void AddEdge(T aFrom, T aTo)
+ {
+ AddEdge(Edge(aFrom, aTo));
+ }
+
+ /**
+ * Get the list of edges.
+ */
+ const nsTArray<Edge>& GetEdgeList() const
+ {
+ return mEdges;
+ }
+
+ /**
+ * Remove the given edge from the graph.
+ */
+ void RemoveEdge(Edge aEdge)
+ {
+ mEdges.RemoveElement(aEdge);
+ }
+
+ /**
+ * Remove all edges going into aNode.
+ */
+ void RemoveEdgesTo(T aNode)
+ {
+ RemoveEdgesToComparator c;
+ while (mEdges.RemoveElement(aNode, c)) {}
+ }
+
+ /**
+ * Get the number of edges going into aNode.
+ */
+ unsigned int NumEdgesTo(T aNode)
+ {
+ unsigned int count = 0;
+ for (unsigned int i = 0; i < mEdges.Length(); i++) {
+ if (mEdges.ElementAt(i).mTo == aNode) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the list of all edges going from aNode
+ */
+ void GetEdgesFrom(T aNode, nsTArray<Edge>& aResult)
+ {
+ for (unsigned int i = 0; i < mEdges.Length(); i++) {
+ if (mEdges.ElementAt(i).mFrom == aNode) {
+ aResult.AppendElement(mEdges.ElementAt(i));
+ }
+ }
+ }
+
+ /**
+ * Get the total number of edges.
+ */
+ unsigned int GetEdgeCount() { return mEdges.Length(); }
+
+private:
+
+ nsTArray<Edge> mEdges;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_DIRECTEDGRAPH_H
diff --git a/system/graphics/layers/Effects.cpp b/system/graphics/layers/Effects.cpp
new file mode 100644
index 000000000..4383af9d2
--- /dev/null
+++ b/system/graphics/layers/Effects.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Effects.h"
+#include "LayersLogging.h" // for AppendToString
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+
+using namespace mozilla::layers;
+
+void
+TexturedEffect::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ AppendToString(aStream, mTextureCoords, " [texture-coords=", "]");
+
+ if (mPremultiplied) {
+ aStream << " [premultiplied]";
+ } else {
+ aStream << " [not-premultiplied]";
+ }
+
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+}
+
+void
+EffectMask::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectMask (0x%p)", this).get();
+ AppendToString(aStream, mSize, " [size=", "]");
+ AppendToString(aStream, mMaskTransform, " [mask-transform=", "]");
+}
+
+void
+EffectRenderTarget::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ TexturedEffect::PrintInfo(aStream, aPrefix);
+ aStream << nsPrintfCString(" [render-target=%p]", mRenderTarget.get()).get();
+}
+
+void
+EffectSolidColor::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectSolidColor (0x%p) [color=%x]", this, mColor.ToABGR()).get();
+}
+
+void
+EffectBlendMode::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectBlendMode (0x%p) [blendmode=%i]", this, (int)mBlendMode).get();
+}
+
+void
+EffectColorMatrix::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectColorMatrix (0x%p)", this).get();
+ AppendToString(aStream, mColorMatrix, " [matrix=", "]");
+}
+
diff --git a/system/graphics/layers/Effects.h b/system/graphics/layers/Effects.h
new file mode 100644
index 000000000..a0859a539
--- /dev/null
+++ b/system/graphics/layers/Effects.h
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_EFFECTS_H
+#define MOZILLA_LAYERS_EFFECTS_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter, etc
+#include "mozilla/layers/CompositorTypes.h" // for EffectTypes, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/EnumeratedArray.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Effects and effect chains are used by the compositor API (see Compositor.h).
+ * An effect chain represents a rendering method, for example some shader and
+ * the data required for that shader to run. An effect is some component of the
+ * chain and its data.
+ *
+ * An effect chain consists of a primary effect - how the 'texture' memory should
+ * be interpreted (RGBA, BGRX, YCBCR, etc.) - and any number of secondary effects
+ * - any way in which rendering can be changed, e.g., applying a mask layer.
+ *
+ * During the rendering process, an effect chain is created by the layer being
+ * rendered and the primary effect is added by the compositable host. Secondary
+ * effects may be added by the layer or compositable. The effect chain is passed
+ * to the compositor by the compositable host as a parameter to DrawQuad.
+ */
+
+struct Effect
+{
+ NS_INLINE_DECL_REFCOUNTING(Effect)
+
+ explicit Effect(EffectTypes aType) : mType(aType) {}
+
+ EffectTypes mType;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
+
+protected:
+ virtual ~Effect() {}
+};
+
+// Render from a texture
+struct TexturedEffect : public Effect
+{
+ TexturedEffect(EffectTypes aType,
+ TextureSource *aTexture,
+ bool aPremultiplied,
+ gfx::SamplingFilter aSamplingFilter)
+ : Effect(aType)
+ , mTextureCoords(0, 0, 1.0f, 1.0f)
+ , mTexture(aTexture)
+ , mPremultiplied(aPremultiplied)
+ , mSamplingFilter(aSamplingFilter)
+ {}
+
+ virtual const char* Name() = 0;
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::Rect mTextureCoords;
+ TextureSource* mTexture;
+ bool mPremultiplied;
+ gfx::SamplingFilter mSamplingFilter;
+ LayerRenderState mState;
+};
+
+// Support an alpha mask.
+struct EffectMask : public Effect
+{
+ EffectMask(TextureSource *aMaskTexture,
+ gfx::IntSize aSize,
+ const gfx::Matrix4x4 &aMaskTransform)
+ : Effect(EffectTypes::MASK)
+ , mMaskTexture(aMaskTexture)
+ , mSize(aSize)
+ , mMaskTransform(aMaskTransform)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ TextureSource* mMaskTexture;
+ gfx::IntSize mSize;
+ gfx::Matrix4x4 mMaskTransform;
+};
+
+struct EffectBlendMode : public Effect
+{
+ explicit EffectBlendMode(gfx::CompositionOp aBlendMode)
+ : Effect(EffectTypes::BLEND_MODE)
+ , mBlendMode(aBlendMode)
+ { }
+
+ virtual const char* Name() { return "EffectBlendMode"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::CompositionOp mBlendMode;
+};
+
+// Render to a render target rather than the screen.
+struct EffectRenderTarget : public TexturedEffect
+{
+ explicit EffectRenderTarget(CompositingRenderTarget *aRenderTarget)
+ : TexturedEffect(EffectTypes::RENDER_TARGET, aRenderTarget, true,
+ gfx::SamplingFilter::LINEAR)
+ , mRenderTarget(aRenderTarget)
+ {}
+
+ virtual const char* Name() { return "EffectRenderTarget"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ RefPtr<CompositingRenderTarget> mRenderTarget;
+
+protected:
+ EffectRenderTarget(EffectTypes aType, CompositingRenderTarget *aRenderTarget)
+ : TexturedEffect(aType, aRenderTarget, true, gfx::SamplingFilter::LINEAR)
+ , mRenderTarget(aRenderTarget)
+ {}
+
+};
+
+// Render to a render target rather than the screen.
+struct EffectColorMatrix : public Effect
+{
+ explicit EffectColorMatrix(gfx::Matrix5x4 aMatrix)
+ : Effect(EffectTypes::COLOR_MATRIX)
+ , mColorMatrix(aMatrix)
+ {}
+
+ virtual const char* Name() { return "EffectColorMatrix"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+ const gfx::Matrix5x4 mColorMatrix;
+};
+
+
+struct EffectRGB : public TexturedEffect
+{
+ EffectRGB(TextureSource *aTexture,
+ bool aPremultiplied,
+ gfx::SamplingFilter aSamplingFilter,
+ bool aFlipped = false)
+ : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aSamplingFilter)
+ {}
+
+ virtual const char* Name() { return "EffectRGB"; }
+};
+
+struct EffectYCbCr : public TexturedEffect
+{
+ EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::YCBCR, aSource, false, aSamplingFilter)
+ , mYUVColorSpace(aYUVColorSpace)
+ {}
+
+ virtual const char* Name() { return "EffectYCbCr"; }
+
+ YUVColorSpace mYUVColorSpace;
+};
+
+struct EffectNV12 : public TexturedEffect
+{
+ EffectNV12(TextureSource *aSource, gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::NV12, aSource, false, aSamplingFilter)
+ {}
+
+ virtual const char* Name() { return "EffectNV12"; }
+};
+
+struct EffectComponentAlpha : public TexturedEffect
+{
+ EffectComponentAlpha(TextureSource *aOnBlack,
+ TextureSource *aOnWhite,
+ gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::COMPONENT_ALPHA, nullptr, false, aSamplingFilter)
+ , mOnBlack(aOnBlack)
+ , mOnWhite(aOnWhite)
+ {}
+
+ virtual const char* Name() { return "EffectComponentAlpha"; }
+
+ TextureSource* mOnBlack;
+ TextureSource* mOnWhite;
+};
+
+struct EffectSolidColor : public Effect
+{
+ explicit EffectSolidColor(const gfx::Color &aColor)
+ : Effect(EffectTypes::SOLID_COLOR)
+ , mColor(aColor)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::Color mColor;
+};
+
+struct EffectChain
+{
+ EffectChain() : mLayerRef(nullptr) {}
+ explicit EffectChain(void* aLayerRef) : mLayerRef(aLayerRef) {}
+
+ RefPtr<Effect> mPrimaryEffect;
+ EnumeratedArray<EffectTypes, EffectTypes::MAX_SECONDARY, RefPtr<Effect>>
+ mSecondaryEffects;
+ void* mLayerRef; //!< For LayerScope logging
+};
+
+/**
+ * Create a Textured effect corresponding to aFormat and using
+ * aSource as the (first) texture source.
+ *
+ * Note that aFormat can be different form aSource->GetFormat if, we are
+ * creating an effect that takes several texture sources (like with YCBCR
+ * where aFormat would be FOMRAT_YCBCR and each texture source would be
+ * a one-channel A8 texture)
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(gfx::SurfaceFormat aFormat,
+ TextureSource* aSource,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aSource);
+ RefPtr<TexturedEffect> result;
+ switch (aFormat) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ result = new EffectRGB(aSource, isAlphaPremultiplied, aSamplingFilter);
+ break;
+ case gfx::SurfaceFormat::NV12:
+ result = new EffectNV12(aSource, aSamplingFilter);
+ break;
+ case gfx::SurfaceFormat::YUV:
+ MOZ_ASSERT_UNREACHABLE("gfx::SurfaceFormat::YUV is invalid");
+ break;
+ default:
+ NS_WARNING("unhandled program type");
+ break;
+ }
+
+ result->mState = state;
+
+ return result.forget();
+}
+
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureHost* aHost,
+ TextureSource* aSource,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aHost);
+ MOZ_ASSERT(aSource);
+
+ RefPtr<TexturedEffect> result;
+ if (aHost->GetReadFormat() == gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aHost->GetYUVColorSpace() != YUVColorSpace::UNKNOWN);
+ result = new EffectYCbCr(aSource, aHost->GetYUVColorSpace(), aSamplingFilter);
+ } else {
+ result = CreateTexturedEffect(aHost->GetReadFormat(),
+ aSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ state);
+ }
+ return result.forget();
+}
+
+/**
+ * Create a textured effect based on aSource format and the presence of
+ * aSourceOnWhite.
+ *
+ * aSourceOnWhite can be null.
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureSource* aSource,
+ TextureSource* aSourceOnWhite,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aSource);
+ if (aSourceOnWhite) {
+ MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+ MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
+ return MakeAndAddRef<EffectComponentAlpha>(aSource, aSourceOnWhite,
+ aSamplingFilter);
+ }
+
+ return CreateTexturedEffect(aSource->GetFormat(),
+ aSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ state);
+}
+
+/**
+ * Create a textured effect based on aSource format.
+ *
+ * This version excudes the possibility of component alpha.
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureSource *aTexture,
+ const gfx::SamplingFilter aSamplingFilter,
+ const LayerRenderState &state = LayerRenderState())
+{
+ return CreateTexturedEffect(aTexture, nullptr, aSamplingFilter, true, state);
+}
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/FrameMetrics.cpp b/system/graphics/layers/FrameMetrics.cpp
new file mode 100644
index 000000000..7b5108557
--- /dev/null
+++ b/system/graphics/layers/FrameMetrics.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameMetrics.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+namespace layers {
+
+const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
+
+void
+ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
+ MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());
+ mUsesContainerScrolling = aValue;
+}
+
+StaticAutoPtr<const ScrollMetadata> ScrollMetadata::sNullMetadata;
+
+}
+}
diff --git a/system/graphics/layers/FrameMetrics.h b/system/graphics/layers/FrameMetrics.h
new file mode 100644
index 000000000..0261dbad7
--- /dev/null
+++ b/system/graphics/layers/FrameMetrics.h
@@ -0,0 +1,1112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FRAMEMETRICS_H
+#define GFX_FRAMEMETRICS_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include "Units.h" // for CSSRect, CSSPixel, etc
+#include "mozilla/HashFunctions.h" // for HashGeneric
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/gfx/Logging.h" // for Log
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsString.h"
+#include "nsStyleCoord.h" // for nsStyleCoord
+
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Helper struct to hold a couple of fields that can be updated as part of
+ * an empty transaction.
+ */
+struct ScrollUpdateInfo {
+ uint32_t mScrollGeneration;
+ CSSPoint mScrollOffset;
+};
+
+/**
+ * The viewport and displayport metrics for the painted frame at the
+ * time of a layer-tree transaction. These metrics are especially
+ * useful for shadow layers, because the metrics values are updated
+ * atomically with new pixels.
+ */
+struct FrameMetrics {
+ friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
+public:
+ // We use IDs to identify frames across processes.
+ typedef uint64_t ViewID;
+ static const ViewID NULL_SCROLL_ID; // This container layer does not scroll.
+ static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes
+ // will begin at.
+
+ enum ScrollOffsetUpdateType : uint8_t {
+ eNone, // The default; the scroll offset was not updated
+ eMainThread, // The scroll offset was updated by the main thread.
+ ePending, // The scroll offset was updated on the main thread, but not
+ // painted, so the layer texture data is still at the old
+ // offset.
+ eUserAction, // In an APZ repaint request, this means the APZ generated
+ // the scroll position based on user action (the alternative
+ // is eNone which means it's just request a repaint because
+ // it got a scroll update from the main thread).
+ eRestore, // The scroll offset was updated by the main thread, but as
+ // a restore from history or after a frame reconstruction.
+ // In this case, APZ can ignore the offset change if the
+ // user has done an APZ scroll already.
+
+ eSentinel // For IPC use only
+ };
+
+ FrameMetrics()
+ : mScrollId(NULL_SCROLL_ID)
+ , mPresShellResolution(1)
+ , mCompositionBounds(0, 0, 0, 0)
+ , mDisplayPort(0, 0, 0, 0)
+ , mCriticalDisplayPort(0, 0, 0, 0)
+ , mScrollableRect(0, 0, 0, 0)
+ , mCumulativeResolution()
+ , mDevPixelsPerCSSPixel(1)
+ , mScrollOffset(0, 0)
+ , mZoom()
+ , mScrollGeneration(0)
+ , mSmoothScrollOffset(0, 0)
+ , mRootCompositionSize(0, 0)
+ , mDisplayPortMargins(0, 0, 0, 0)
+ , mPresShellId(-1)
+ , mViewport(0, 0, 0, 0)
+ , mExtraResolution()
+ , mPaintRequestTime()
+ , mScrollUpdateType(eNone)
+ , mIsRootContent(false)
+ , mDoSmoothScroll(false)
+ , mUseDisplayPortMargins(false)
+ , mIsScrollInfoLayer(false)
+ {
+ }
+
+ // Default copy ctor and operator= are fine
+
+ bool operator==(const FrameMetrics& aOther) const
+ {
+ // Put mScrollId at the top since it's the most likely one to fail.
+ return mScrollId == aOther.mScrollId &&
+ mPresShellResolution == aOther.mPresShellResolution &&
+ mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
+ mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
+ mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
+ mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
+ mCumulativeResolution == aOther.mCumulativeResolution &&
+ mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
+ mScrollOffset == aOther.mScrollOffset &&
+ // don't compare mZoom
+ mScrollGeneration == aOther.mScrollGeneration &&
+ mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
+ mRootCompositionSize == aOther.mRootCompositionSize &&
+ mDisplayPortMargins == aOther.mDisplayPortMargins &&
+ mPresShellId == aOther.mPresShellId &&
+ mViewport.IsEqualEdges(aOther.mViewport) &&
+ mExtraResolution == aOther.mExtraResolution &&
+ mPaintRequestTime == aOther.mPaintRequestTime &&
+ mScrollUpdateType == aOther.mScrollUpdateType &&
+ mIsRootContent == aOther.mIsRootContent &&
+ mDoSmoothScroll == aOther.mDoSmoothScroll &&
+ mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
+ mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
+ }
+
+ bool operator!=(const FrameMetrics& aOther) const
+ {
+ return !operator==(aOther);
+ }
+
+ bool IsScrollable() const
+ {
+ return mScrollId != NULL_SCROLL_ID;
+ }
+
+ CSSToScreenScale2D DisplayportPixelsPerCSSPixel() const
+ {
+ // Note: use 'mZoom * ParentLayerToLayerScale(1.0f)' as the CSS-to-Layer scale
+ // instead of LayersPixelsPerCSSPixel(), because displayport calculations
+ // are done in the context of a repaint request, where we ask Layout to
+ // repaint at a new resolution that includes any async zoom. Until this
+ // repaint request is processed, LayersPixelsPerCSSPixel() does not yet
+ // include the async zoom, but it will when the displayport is interpreted
+ // for the repaint.
+ return mZoom * ParentLayerToLayerScale(1.0f) / mExtraResolution;
+ }
+
+ CSSToLayerScale2D LayersPixelsPerCSSPixel() const
+ {
+ return mDevPixelsPerCSSPixel * mCumulativeResolution;
+ }
+
+ // Get the amount by which this frame has been zoomed since the last repaint.
+ LayerToParentLayerScale GetAsyncZoom() const
+ {
+ // The async portion of the zoom should be the same along the x and y
+ // axes.
+ return (mZoom / LayersPixelsPerCSSPixel()).ToScaleFactor();
+ }
+
+ // Ensure the scrollableRect is at least as big as the compositionBounds
+ // because the scrollableRect can be smaller if the content is not large
+ // and the scrollableRect hasn't been updated yet.
+ // We move the scrollableRect up because we don't know if we can move it
+ // down. i.e. we know that scrollableRect can go back as far as zero.
+ // but we don't know how much further ahead it can go.
+ CSSRect GetExpandedScrollableRect() const
+ {
+ CSSRect scrollableRect = mScrollableRect;
+ CSSSize compSize = CalculateCompositedSizeInCssPixels();
+ if (scrollableRect.width < compSize.width) {
+ scrollableRect.x = std::max(0.f,
+ scrollableRect.x - (compSize.width - scrollableRect.width));
+ scrollableRect.width = compSize.width;
+ }
+
+ if (scrollableRect.height < compSize.height) {
+ scrollableRect.y = std::max(0.f,
+ scrollableRect.y - (compSize.height - scrollableRect.height));
+ scrollableRect.height = compSize.height;
+ }
+
+ return scrollableRect;
+ }
+
+ CSSSize CalculateCompositedSizeInCssPixels() const
+ {
+ if (GetZoom() == CSSToParentLayerScale2D(0, 0)) {
+ return CSSSize(); // avoid division by zero
+ }
+ return mCompositionBounds.Size() / GetZoom();
+ }
+
+ CSSRect CalculateCompositedRectInCssPixels() const
+ {
+ if (GetZoom() == CSSToParentLayerScale2D(0, 0)) {
+ return CSSRect(); // avoid division by zero
+ }
+ return mCompositionBounds / GetZoom();
+ }
+
+ CSSSize CalculateBoundedCompositedSizeInCssPixels() const
+ {
+ CSSSize size = CalculateCompositedSizeInCssPixels();
+ size.width = std::min(size.width, mRootCompositionSize.width);
+ size.height = std::min(size.height, mRootCompositionSize.height);
+ return size;
+ }
+
+ CSSRect CalculateScrollRange() const
+ {
+ CSSSize scrollPortSize = CalculateCompositedSizeInCssPixels();
+ CSSRect scrollRange = mScrollableRect;
+ scrollRange.width = std::max(scrollRange.width - scrollPortSize.width, 0.0f);
+ scrollRange.height = std::max(scrollRange.height - scrollPortSize.height, 0.0f);
+ return scrollRange;
+ }
+
+ void ScrollBy(const CSSPoint& aPoint)
+ {
+ mScrollOffset += aPoint;
+ }
+
+ void ZoomBy(float aScale)
+ {
+ ZoomBy(gfxSize(aScale, aScale));
+ }
+
+ void ZoomBy(const gfxSize& aScale)
+ {
+ mZoom.xScale *= aScale.width;
+ mZoom.yScale *= aScale.height;
+ }
+
+ void CopyScrollInfoFrom(const FrameMetrics& aOther)
+ {
+ mScrollOffset = aOther.mScrollOffset;
+ mScrollGeneration = aOther.mScrollGeneration;
+ }
+
+ void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
+ {
+ mSmoothScrollOffset = aOther.mSmoothScrollOffset;
+ mScrollGeneration = aOther.mScrollGeneration;
+ mDoSmoothScroll = aOther.mDoSmoothScroll;
+ }
+
+ void UpdatePendingScrollInfo(const ScrollUpdateInfo& aInfo)
+ {
+ mScrollOffset = aInfo.mScrollOffset;
+ mScrollGeneration = aInfo.mScrollGeneration;
+ mScrollUpdateType = ePending;
+ }
+
+ void SetRepaintDrivenByUserAction(bool aUserAction)
+ {
+ mScrollUpdateType = aUserAction ? eUserAction : eNone;
+ }
+
+public:
+ void SetPresShellResolution(float aPresShellResolution)
+ {
+ mPresShellResolution = aPresShellResolution;
+ }
+
+ float GetPresShellResolution() const
+ {
+ return mPresShellResolution;
+ }
+
+ void SetCompositionBounds(const ParentLayerRect& aCompositionBounds)
+ {
+ mCompositionBounds = aCompositionBounds;
+ }
+
+ const ParentLayerRect& GetCompositionBounds() const
+ {
+ return mCompositionBounds;
+ }
+
+ void SetDisplayPort(const CSSRect& aDisplayPort)
+ {
+ mDisplayPort = aDisplayPort;
+ }
+
+ const CSSRect& GetDisplayPort() const
+ {
+ return mDisplayPort;
+ }
+
+ void SetCriticalDisplayPort(const CSSRect& aCriticalDisplayPort)
+ {
+ mCriticalDisplayPort = aCriticalDisplayPort;
+ }
+
+ const CSSRect& GetCriticalDisplayPort() const
+ {
+ return mCriticalDisplayPort;
+ }
+
+ void SetCumulativeResolution(const LayoutDeviceToLayerScale2D& aCumulativeResolution)
+ {
+ mCumulativeResolution = aCumulativeResolution;
+ }
+
+ const LayoutDeviceToLayerScale2D& GetCumulativeResolution() const
+ {
+ return mCumulativeResolution;
+ }
+
+ void SetDevPixelsPerCSSPixel(const CSSToLayoutDeviceScale& aDevPixelsPerCSSPixel)
+ {
+ mDevPixelsPerCSSPixel = aDevPixelsPerCSSPixel;
+ }
+
+ const CSSToLayoutDeviceScale& GetDevPixelsPerCSSPixel() const
+ {
+ return mDevPixelsPerCSSPixel;
+ }
+
+ void SetIsRootContent(bool aIsRootContent)
+ {
+ mIsRootContent = aIsRootContent;
+ }
+
+ bool IsRootContent() const
+ {
+ return mIsRootContent;
+ }
+
+ void SetScrollOffset(const CSSPoint& aScrollOffset)
+ {
+ mScrollOffset = aScrollOffset;
+ }
+
+ const CSSPoint& GetScrollOffset() const
+ {
+ return mScrollOffset;
+ }
+
+ void SetSmoothScrollOffset(const CSSPoint& aSmoothScrollDestination)
+ {
+ mSmoothScrollOffset = aSmoothScrollDestination;
+ }
+
+ const CSSPoint& GetSmoothScrollOffset() const
+ {
+ return mSmoothScrollOffset;
+ }
+
+ void SetZoom(const CSSToParentLayerScale2D& aZoom)
+ {
+ mZoom = aZoom;
+ }
+
+ const CSSToParentLayerScale2D& GetZoom() const
+ {
+ return mZoom;
+ }
+
+ void SetScrollOffsetUpdated(uint32_t aScrollGeneration)
+ {
+ mScrollUpdateType = eMainThread;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ void SetScrollOffsetRestored(uint32_t aScrollGeneration)
+ {
+ mScrollUpdateType = eRestore;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration)
+ {
+ mDoSmoothScroll = true;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ ScrollOffsetUpdateType GetScrollUpdateType() const
+ {
+ return mScrollUpdateType;
+ }
+
+ bool GetScrollOffsetUpdated() const
+ {
+ return mScrollUpdateType != eNone;
+ }
+
+ bool GetDoSmoothScroll() const
+ {
+ return mDoSmoothScroll;
+ }
+
+ uint32_t GetScrollGeneration() const
+ {
+ return mScrollGeneration;
+ }
+
+ ViewID GetScrollId() const
+ {
+ return mScrollId;
+ }
+
+ void SetScrollId(ViewID scrollId)
+ {
+ mScrollId = scrollId;
+ }
+
+ void SetRootCompositionSize(const CSSSize& aRootCompositionSize)
+ {
+ mRootCompositionSize = aRootCompositionSize;
+ }
+
+ const CSSSize& GetRootCompositionSize() const
+ {
+ return mRootCompositionSize;
+ }
+
+ void SetDisplayPortMargins(const ScreenMargin& aDisplayPortMargins)
+ {
+ mDisplayPortMargins = aDisplayPortMargins;
+ }
+
+ const ScreenMargin& GetDisplayPortMargins() const
+ {
+ return mDisplayPortMargins;
+ }
+
+ void SetUseDisplayPortMargins(bool aValue)
+ {
+ mUseDisplayPortMargins = aValue;
+ }
+
+ bool GetUseDisplayPortMargins() const
+ {
+ return mUseDisplayPortMargins;
+ }
+
+ uint32_t GetPresShellId() const
+ {
+ return mPresShellId;
+ }
+
+ void SetPresShellId(uint32_t aPresShellId)
+ {
+ mPresShellId = aPresShellId;
+ }
+
+ void SetViewport(const CSSRect& aViewport)
+ {
+ mViewport = aViewport;
+ }
+
+ const CSSRect& GetViewport() const
+ {
+ return mViewport;
+ }
+
+ void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution)
+ {
+ mExtraResolution = aExtraResolution;
+ }
+
+ const ScreenToLayerScale2D& GetExtraResolution() const
+ {
+ return mExtraResolution;
+ }
+
+ const CSSRect& GetScrollableRect() const
+ {
+ return mScrollableRect;
+ }
+
+ void SetScrollableRect(const CSSRect& aScrollableRect)
+ {
+ mScrollableRect = aScrollableRect;
+ }
+
+ void SetPaintRequestTime(const TimeStamp& aTime) {
+ mPaintRequestTime = aTime;
+ }
+ const TimeStamp& GetPaintRequestTime() const {
+ return mPaintRequestTime;
+ }
+
+ void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
+ mIsScrollInfoLayer = aIsScrollInfoLayer;
+ }
+ bool IsScrollInfoLayer() const {
+ return mIsScrollInfoLayer;
+ }
+
+private:
+ // A unique ID assigned to each scrollable frame.
+ ViewID mScrollId;
+
+ // The pres-shell resolution that has been induced on the document containing
+ // this scroll frame as a result of zooming this scroll frame (whether via
+ // user action, or choosing an initial zoom level on page load). This can
+ // only be different from 1.0 for frames that are zoomable, which currently
+ // is just the root content document's root scroll frame (mIsRoot = true).
+ // This is a plain float rather than a ScaleFactor because in and of itself
+ // it does not convert between any coordinate spaces for which we have names.
+ float mPresShellResolution;
+
+ // This is the area within the widget that we're compositing to. It is in the
+ // same coordinate space as the reference frame for the scrolled frame.
+ //
+ // This is useful because, on mobile, the viewport and composition dimensions
+ // are not always the same. In this case, we calculate the displayport using
+ // an area bigger than the region we're compositing to. If we used the
+ // viewport dimensions to calculate the displayport, we'd run into situations
+ // where we're prerendering the wrong regions and the content may be clipped,
+ // or too much of it prerendered. If the composition dimensions are the same
+ // as the viewport dimensions, there is no need for this and we can just use
+ // the viewport instead.
+ //
+ // This value is valid for nested scrollable layers as well, and is still
+ // relative to the layer tree origin. This value is provided by Gecko at
+ // layout/paint time.
+ ParentLayerRect mCompositionBounds;
+
+ // The area of a frame's contents that has been painted, relative to
+ // mCompositionBounds.
+ //
+ // Note that this is structured in such a way that it doesn't depend on the
+ // method layout uses to scroll content.
+ //
+ // May be larger or smaller than |mScrollableRect|.
+ //
+ // To pre-render a margin of 100 CSS pixels around the window,
+ // { x = -100, y = - 100,
+ // width = window.innerWidth + 200, height = window.innerHeight + 200 }
+ CSSRect mDisplayPort;
+
+ // If non-empty, the area of a frame's contents that is considered critical
+ // to paint. Area outside of this area (i.e. area inside mDisplayPort, but
+ // outside of mCriticalDisplayPort) is considered low-priority, and may be
+ // painted with lower precision, or not painted at all.
+ //
+ // The same restrictions for mDisplayPort apply here.
+ CSSRect mCriticalDisplayPort;
+
+ // The scrollable bounds of a frame. This is determined by reflow.
+ // Ordinarily the x and y will be 0 and the width and height will be the
+ // size of the element being scrolled. However for RTL pages or elements
+ // the x value may be negative.
+ //
+ // This is relative to the document. It is in the same coordinate space as
+ // |mScrollOffset|, but a different coordinate space than |mViewport| and
+ // |mDisplayPort|. Note also that this coordinate system is understood by
+ // window.scrollTo().
+ //
+ // This is valid on any layer unless it has no content.
+ CSSRect mScrollableRect;
+
+ // The cumulative resolution that the current frame has been painted at.
+ // This is the product of the pres-shell resolutions of the document
+ // containing this scroll frame and its ancestors, and any css-driven
+ // resolution. This information is provided by Gecko at layout/paint time.
+ // Note that this is allowed to have different x- and y-scales, but only
+ // for subframes (mIsRoot = false). (The same applies to other scales that
+ // "inherit" the 2D-ness of this one, such as mZoom.)
+ LayoutDeviceToLayerScale2D mCumulativeResolution;
+
+ // New fields from now on should be made private and old fields should
+ // be refactored to be private.
+
+ // The conversion factor between CSS pixels and device pixels for this frame.
+ // This can vary based on a variety of things, such as reflowing-zoom. The
+ // conversion factor for device pixels to layers pixels is just the
+ // resolution.
+ CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+
+ // The position of the top-left of the CSS viewport, relative to the document
+ // (or the document relative to the viewport, if that helps understand it).
+ //
+ // Thus it is relative to the document. It is in the same coordinate space as
+ // |mScrollableRect|, but a different coordinate space than |mViewport| and
+ // |mDisplayPort|.
+ //
+ // It is required that the rect:
+ // { x = mScrollOffset.x, y = mScrollOffset.y,
+ // width = mCompositionBounds.x / mResolution.scale,
+ // height = mCompositionBounds.y / mResolution.scale }
+ // Be within |mScrollableRect|.
+ //
+ // This is valid for any layer, but is always relative to this frame and
+ // not any parents, regardless of parent transforms.
+ CSSPoint mScrollOffset;
+
+ // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
+ // but will be drawn to the screen at mZoom. In the steady state, the
+ // two will be the same, but during an async zoom action the two may
+ // diverge. This information is initialized in Gecko but updated in the APZC.
+ CSSToParentLayerScale2D mZoom;
+
+ // The scroll generation counter used to acknowledge the scroll offset update.
+ uint32_t mScrollGeneration;
+
+ // If mDoSmoothScroll is true, the scroll offset will be animated smoothly
+ // to this value.
+ CSSPoint mSmoothScrollOffset;
+
+ // The size of the root scrollable's composition bounds, but in local CSS pixels.
+ CSSSize mRootCompositionSize;
+
+ // A display port expressed as layer margins that apply to the rect of what
+ // is drawn of the scrollable element.
+ ScreenMargin mDisplayPortMargins;
+
+ uint32_t mPresShellId;
+
+ // The CSS viewport, which is the dimensions we're using to constrain the
+ // <html> element of this frame, relative to the top-left of the layer. Note
+ // that its offset is structured in such a way that it doesn't depend on the
+ // method layout uses to scroll content.
+ //
+ // This is mainly useful on the root layer, however nested iframes can have
+ // their own viewport, which will just be the size of the window of the
+ // iframe. For layers that don't correspond to a document, this metric is
+ // meaningless and invalid.
+ CSSRect mViewport;
+
+ // The extra resolution at which content in this scroll frame is drawn beyond
+ // that necessary to draw one Layer pixel per Screen pixel.
+ ScreenToLayerScale2D mExtraResolution;
+
+ // The time at which the APZC last requested a repaint for this scrollframe.
+ TimeStamp mPaintRequestTime;
+
+ // Whether mScrollOffset was updated by something other than the APZ code, and
+ // if the APZC receiving this metrics should update its local copy.
+ ScrollOffsetUpdateType mScrollUpdateType;
+
+ // Whether or not this is the root scroll frame for the root content document.
+ bool mIsRootContent:1;
+
+ // When mDoSmoothScroll, the scroll offset should be animated to
+ // smoothly transition to mScrollOffset rather than be updated instantly.
+ bool mDoSmoothScroll:1;
+
+ // If this is true then we use the display port margins on this metrics,
+ // otherwise use the display port rect.
+ bool mUseDisplayPortMargins:1;
+
+ // Whether or not this frame has a "scroll info layer" to capture events.
+ bool mIsScrollInfoLayer:1;
+
+ // WARNING!!!!
+ //
+ // When adding a new field:
+ //
+ // - First, consider whether the field can be added to ScrollMetadata
+ // instead. If so, prefer that.
+ //
+ // - Otherwise, the following places should be updated to include them
+ // (as needed):
+ // FrameMetrics::operator ==
+ // AsyncPanZoomController::NotifyLayersUpdated
+ // The ParamTraits specialization in GfxMessageUtils.h
+ //
+ // Please add new fields above this comment.
+
+ // Private helpers for IPC purposes
+ void SetDoSmoothScroll(bool aValue) {
+ mDoSmoothScroll = aValue;
+ }
+};
+
+struct ScrollSnapInfo {
+ ScrollSnapInfo()
+ : mScrollSnapTypeX(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ , mScrollSnapTypeY(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ {}
+
+ bool operator==(const ScrollSnapInfo& aOther) const
+ {
+ return mScrollSnapTypeX == aOther.mScrollSnapTypeX &&
+ mScrollSnapTypeY == aOther.mScrollSnapTypeY &&
+ mScrollSnapIntervalX == aOther.mScrollSnapIntervalX &&
+ mScrollSnapIntervalY == aOther.mScrollSnapIntervalY &&
+ mScrollSnapDestination == aOther.mScrollSnapDestination &&
+ mScrollSnapCoordinates == aOther.mScrollSnapCoordinates;
+ }
+
+ // The scroll frame's scroll-snap-type.
+ // One of NS_STYLE_SCROLL_SNAP_{NONE, MANDATORY, PROXIMITY}.
+ uint8_t mScrollSnapTypeX;
+ uint8_t mScrollSnapTypeY;
+
+ // The intervals derived from the scroll frame's scroll-snap-points.
+ Maybe<nscoord> mScrollSnapIntervalX;
+ Maybe<nscoord> mScrollSnapIntervalY;
+
+ // The scroll frame's scroll-snap-destination, in cooked form (to avoid
+ // shipping the raw nsStyleCoord::CalcValue over IPC).
+ nsPoint mScrollSnapDestination;
+
+ // The scroll-snap-coordinates of any descendant frames of the scroll frame,
+ // relative to the origin of the scrolled frame.
+ nsTArray<nsPoint> mScrollSnapCoordinates;
+};
+
+/**
+ * A clip that applies to a layer, that may be scrolled by some of the
+ * scroll frames associated with the layer.
+ */
+struct LayerClip {
+ friend struct IPC::ParamTraits<mozilla::layers::LayerClip>;
+
+public:
+ LayerClip()
+ : mClipRect()
+ , mMaskLayerIndex()
+ {}
+
+ explicit LayerClip(const ParentLayerIntRect& aClipRect)
+ : mClipRect(aClipRect)
+ , mMaskLayerIndex()
+ {}
+
+ bool operator==(const LayerClip& aOther) const
+ {
+ return mClipRect == aOther.mClipRect &&
+ mMaskLayerIndex == aOther.mMaskLayerIndex;
+ }
+
+ void SetClipRect(const ParentLayerIntRect& aClipRect) {
+ mClipRect = aClipRect;
+ }
+ const ParentLayerIntRect& GetClipRect() const {
+ return mClipRect;
+ }
+
+ void SetMaskLayerIndex(const Maybe<size_t>& aIndex) {
+ mMaskLayerIndex = aIndex;
+ }
+ const Maybe<size_t>& GetMaskLayerIndex() const {
+ return mMaskLayerIndex;
+ }
+
+private:
+ ParentLayerIntRect mClipRect;
+
+ // Optionally, specifies a mask layer that's part of the clip.
+ // This is an index into the MetricsMaskLayers array on the Layer.
+ Maybe<size_t> mMaskLayerIndex;
+};
+
+typedef Maybe<LayerClip> MaybeLayerClip; // for passing over IPDL
+
+/**
+ * Metadata about a scroll frame that's stored in the layer tree for use by
+ * the compositor (including APZ). This includes the scroll frame's FrameMetrics,
+ * as well as other metadata. We don't put the other metadata into FrameMetrics
+ * to avoid FrameMetrics becoming too bloated (as a FrameMetrics is e.g. sent
+ * over IPC for every repaint request for every active scroll frame).
+ */
+struct ScrollMetadata {
+ friend struct IPC::ParamTraits<mozilla::layers::ScrollMetadata>;
+
+ typedef FrameMetrics::ViewID ViewID;
+public:
+ static StaticAutoPtr<const ScrollMetadata> sNullMetadata; // We sometimes need an empty metadata
+
+ ScrollMetadata()
+ : mMetrics()
+ , mSnapInfo()
+ , mScrollParentId(FrameMetrics::NULL_SCROLL_ID)
+ , mBackgroundColor()
+ , mContentDescription()
+ , mLineScrollAmount(0, 0)
+ , mPageScrollAmount(0, 0)
+ , mScrollClip()
+ , mHasScrollgrab(false)
+ , mAllowVerticalScrollWithWheel(false)
+ , mIsLayersIdRoot(false)
+ , mUsesContainerScrolling(false)
+ , mForceDisableApz(false)
+ {}
+
+ bool operator==(const ScrollMetadata& aOther) const
+ {
+ return mMetrics == aOther.mMetrics &&
+ mSnapInfo == aOther.mSnapInfo &&
+ mScrollParentId == aOther.mScrollParentId &&
+ mBackgroundColor == aOther.mBackgroundColor &&
+ // don't compare mContentDescription
+ mLineScrollAmount == aOther.mLineScrollAmount &&
+ mPageScrollAmount == aOther.mPageScrollAmount &&
+ mScrollClip == aOther.mScrollClip &&
+ mHasScrollgrab == aOther.mHasScrollgrab &&
+ mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
+ mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
+ mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
+ mForceDisableApz == aOther.mForceDisableApz;
+ }
+
+ bool operator!=(const ScrollMetadata& aOther) const
+ {
+ return !operator==(aOther);
+ }
+
+ bool IsDefault() const
+ {
+ ScrollMetadata def;
+
+ def.mMetrics.SetPresShellId(mMetrics.GetPresShellId());
+ return (def == *this);
+ }
+
+ FrameMetrics& GetMetrics() { return mMetrics; }
+ const FrameMetrics& GetMetrics() const { return mMetrics; }
+
+ void SetSnapInfo(ScrollSnapInfo&& aSnapInfo) {
+ mSnapInfo = Move(aSnapInfo);
+ }
+ const ScrollSnapInfo& GetSnapInfo() const { return mSnapInfo; }
+
+ ViewID GetScrollParentId() const {
+ return mScrollParentId;
+ }
+
+ void SetScrollParentId(ViewID aParentId) {
+ mScrollParentId = aParentId;
+ }
+ const gfx::Color& GetBackgroundColor() const {
+ return mBackgroundColor;
+ }
+ void SetBackgroundColor(const gfx::Color& aBackgroundColor) {
+ mBackgroundColor = aBackgroundColor;
+ }
+ const nsCString& GetContentDescription() const {
+ return mContentDescription;
+ }
+ void SetContentDescription(const nsCString& aContentDescription) {
+ mContentDescription = aContentDescription;
+ }
+ const LayoutDeviceIntSize& GetLineScrollAmount() const {
+ return mLineScrollAmount;
+ }
+ void SetLineScrollAmount(const LayoutDeviceIntSize& size) {
+ mLineScrollAmount = size;
+ }
+ const LayoutDeviceIntSize& GetPageScrollAmount() const {
+ return mPageScrollAmount;
+ }
+ void SetPageScrollAmount(const LayoutDeviceIntSize& size) {
+ mPageScrollAmount = size;
+ }
+
+ void SetScrollClip(const Maybe<LayerClip>& aScrollClip) {
+ mScrollClip = aScrollClip;
+ }
+ const Maybe<LayerClip>& GetScrollClip() const {
+ return mScrollClip;
+ }
+ bool HasScrollClip() const {
+ return mScrollClip.isSome();
+ }
+ const LayerClip& ScrollClip() const {
+ return mScrollClip.ref();
+ }
+ LayerClip& ScrollClip() {
+ return mScrollClip.ref();
+ }
+
+ bool HasMaskLayer() const {
+ return HasScrollClip() && ScrollClip().GetMaskLayerIndex();
+ }
+ Maybe<ParentLayerIntRect> GetClipRect() const {
+ return mScrollClip.isSome() ? Some(mScrollClip->GetClipRect()) : Nothing();
+ }
+
+ void SetHasScrollgrab(bool aHasScrollgrab) {
+ mHasScrollgrab = aHasScrollgrab;
+ }
+ bool GetHasScrollgrab() const {
+ return mHasScrollgrab;
+ }
+ bool AllowVerticalScrollWithWheel() const {
+ return mAllowVerticalScrollWithWheel;
+ }
+ void SetAllowVerticalScrollWithWheel(bool aValue) {
+ mAllowVerticalScrollWithWheel = aValue;
+ }
+ void SetIsLayersIdRoot(bool aValue) {
+ mIsLayersIdRoot = aValue;
+ }
+ bool IsLayersIdRoot() const {
+ return mIsLayersIdRoot;
+ }
+ // Implemented out of line because the implementation needs gfxPrefs.h
+ // and we don't want to include that from FrameMetrics.h.
+ void SetUsesContainerScrolling(bool aValue);
+ bool UsesContainerScrolling() const {
+ return mUsesContainerScrolling;
+ }
+ void SetForceDisableApz(bool aForceDisable) {
+ mForceDisableApz = aForceDisable;
+ }
+ bool IsApzForceDisabled() const {
+ return mForceDisableApz;
+ }
+
+private:
+ FrameMetrics mMetrics;
+
+ // Information used to determine where to snap to for a given scroll.
+ ScrollSnapInfo mSnapInfo;
+
+ // The ViewID of the scrollable frame to which overscroll should be handed off.
+ ViewID mScrollParentId;
+
+ // The background color to use when overscrolling.
+ gfx::Color mBackgroundColor;
+
+ // A description of the content element corresponding to this frame.
+ // This is empty unless this is a scrollable layer and the
+ // apz.printtree pref is turned on.
+ nsCString mContentDescription;
+
+ // The value of GetLineScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mLineScrollAmount;
+
+ // The value of GetPageScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mPageScrollAmount;
+
+ // A clip to apply when compositing the layer bearing this ScrollMetadata,
+ // after applying any transform arising from scrolling this scroll frame.
+ // Note that, unlike most other fields of ScrollMetadata, this is allowed
+ // to differ between different layers scrolled by the same scroll frame.
+ // TODO: Group the fields of ScrollMetadata into sub-structures to separate
+ // fields with this property better.
+ Maybe<LayerClip> mScrollClip;
+
+ // Whether or not this frame is for an element marked 'scrollgrab'.
+ bool mHasScrollgrab:1;
+
+ // Whether or not the frame can be vertically scrolled with a mouse wheel.
+ bool mAllowVerticalScrollWithWheel:1;
+
+ // Whether these framemetrics are for the root scroll frame (root element if
+ // we don't have a root scroll frame) for its layers id.
+ bool mIsLayersIdRoot:1;
+
+ // True if scrolling using containers, false otherwise. This can be removed
+ // when containerful scrolling is eliminated.
+ bool mUsesContainerScrolling:1;
+
+ // Whether or not the compositor should actually do APZ-scrolling on this
+ // scrollframe.
+ bool mForceDisableApz:1;
+
+ // WARNING!!!!
+ //
+ // When adding new fields to ScrollMetadata, the following places should be
+ // updated to include them (as needed):
+ // ScrollMetadata::operator ==
+ // AsyncPanZoomController::NotifyLayersUpdated
+ // The ParamTraits specialization in GfxMessageUtils.h
+ //
+ // Please add new fields above this comment.
+};
+
+/**
+ * This class allows us to uniquely identify a scrollable layer. The
+ * mLayersId identifies the layer tree (corresponding to a child process
+ * and/or tab) that the scrollable layer belongs to. The mPresShellId
+ * is a temporal identifier (corresponding to the document loaded that
+ * contains the scrollable layer, which may change over time). The
+ * mScrollId corresponds to the actual frame that is scrollable.
+ */
+struct ScrollableLayerGuid {
+ uint64_t mLayersId;
+ uint32_t mPresShellId;
+ FrameMetrics::ViewID mScrollId;
+
+ ScrollableLayerGuid()
+ : mLayersId(0)
+ , mPresShellId(0)
+ , mScrollId(0)
+ {
+ }
+
+ ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId,
+ FrameMetrics::ViewID aScrollId)
+ : mLayersId(aLayersId)
+ , mPresShellId(aPresShellId)
+ , mScrollId(aScrollId)
+ {
+ }
+
+ ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics)
+ : mLayersId(aLayersId)
+ , mPresShellId(aMetrics.GetPresShellId())
+ , mScrollId(aMetrics.GetScrollId())
+ {
+ }
+
+ ScrollableLayerGuid(const ScrollableLayerGuid& other)
+ : mLayersId(other.mLayersId)
+ , mPresShellId(other.mPresShellId)
+ , mScrollId(other.mScrollId)
+ {
+ }
+
+ ~ScrollableLayerGuid()
+ {
+ }
+
+ bool operator==(const ScrollableLayerGuid& other) const
+ {
+ return mLayersId == other.mLayersId
+ && mPresShellId == other.mPresShellId
+ && mScrollId == other.mScrollId;
+ }
+
+ bool operator!=(const ScrollableLayerGuid& other) const
+ {
+ return !(*this == other);
+ }
+
+ bool operator<(const ScrollableLayerGuid& other) const
+ {
+ if (mLayersId < other.mLayersId) {
+ return true;
+ }
+ if (mLayersId == other.mLayersId) {
+ if (mPresShellId < other.mPresShellId) {
+ return true;
+ }
+ if (mPresShellId == other.mPresShellId) {
+ return mScrollId < other.mScrollId;
+ }
+ }
+ return false;
+ }
+
+ uint32_t Hash() const
+ {
+ return HashGeneric(mLayersId, mPresShellId, mScrollId);
+ }
+};
+
+template <int LogLevel>
+gfx::Log<LogLevel>& operator<<(gfx::Log<LogLevel>& log, const ScrollableLayerGuid& aGuid) {
+ return log << '(' << aGuid.mLayersId << ',' << aGuid.mPresShellId << ',' << aGuid.mScrollId << ')';
+}
+
+struct ZoomConstraints {
+ bool mAllowZoom;
+ bool mAllowDoubleTapZoom;
+ CSSToParentLayerScale mMinZoom;
+ CSSToParentLayerScale mMaxZoom;
+
+ ZoomConstraints()
+ : mAllowZoom(true)
+ , mAllowDoubleTapZoom(true)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ZoomConstraints(bool aAllowZoom,
+ bool aAllowDoubleTapZoom,
+ const CSSToParentLayerScale& aMinZoom,
+ const CSSToParentLayerScale& aMaxZoom)
+ : mAllowZoom(aAllowZoom)
+ , mAllowDoubleTapZoom(aAllowDoubleTapZoom)
+ , mMinZoom(aMinZoom)
+ , mMaxZoom(aMaxZoom)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ZoomConstraints(const ZoomConstraints& other)
+ : mAllowZoom(other.mAllowZoom)
+ , mAllowDoubleTapZoom(other.mAllowDoubleTapZoom)
+ , mMinZoom(other.mMinZoom)
+ , mMaxZoom(other.mMaxZoom)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ~ZoomConstraints()
+ {
+ MOZ_COUNT_DTOR(ZoomConstraints);
+ }
+
+ bool operator==(const ZoomConstraints& other) const
+ {
+ return mAllowZoom == other.mAllowZoom
+ && mAllowDoubleTapZoom == other.mAllowDoubleTapZoom
+ && mMinZoom == other.mMinZoom
+ && mMaxZoom == other.mMaxZoom;
+ }
+
+ bool operator!=(const ZoomConstraints& other) const
+ {
+ return !(*this == other);
+ }
+};
+
+typedef Maybe<ZoomConstraints> MaybeZoomConstraints;
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_FRAMEMETRICS_H */
diff --git a/system/graphics/layers/GLImages.cpp b/system/graphics/layers/GLImages.cpp
new file mode 100644
index 000000000..008067df7
--- /dev/null
+++ b/system/graphics/layers/GLImages.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLImages.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "ScopedGLHelpers.h"
+#include "GLImages.h"
+#include "GLBlitHelper.h"
+#include "GLReadTexImageHelper.h"
+#include "GLLibraryEGL.h"
+#include "mozilla/gfx/Logging.h"
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+static RefPtr<GLContext> sSnapshotContext;
+
+EGLImageImage::EGLImageImage(EGLImage aImage, EGLSync aSync,
+ const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+ bool aOwns)
+ : GLImage(ImageFormat::EGLIMAGE),
+ mImage(aImage),
+ mSync(aSync),
+ mSize(aSize),
+ mPos(aOrigin),
+ mOwns(aOwns)
+{
+}
+
+EGLImageImage::~EGLImageImage()
+{
+ if (!mOwns) {
+ return;
+ }
+
+ if (mImage) {
+ sEGLLibrary.fDestroyImage(EGL_DISPLAY(), mImage);
+ mImage = nullptr;
+ }
+
+ if (mSync) {
+ sEGLLibrary.fDestroySync(EGL_DISPLAY(), mSync);
+ mSync = nullptr;
+ }
+}
+
+already_AddRefed<gfx::SourceSurface>
+GLImage::GetAsSourceSurface()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
+
+ if (!sSnapshotContext) {
+ nsCString discardFailureId;
+ sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE,
+ &discardFailureId);
+ if (!sSnapshotContext) {
+ NS_WARNING("Failed to create snapshot GLContext");
+ return nullptr;
+ }
+ }
+
+ sSnapshotContext->MakeCurrent();
+ ScopedTexture scopedTex(sSnapshotContext);
+ ScopedBindTexture boundTex(sSnapshotContext, scopedTex.Texture());
+
+ gfx::IntSize size = GetSize();
+ sSnapshotContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA,
+ size.width, size.height, 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+
+ ScopedFramebufferForTexture autoFBForTex(sSnapshotContext, scopedTex.Texture());
+ if (!autoFBForTex.IsComplete()) {
+ gfxCriticalError() << "GetAsSourceSurface: ScopedFramebufferForTexture failed.";
+ return nullptr;
+ }
+
+ const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
+
+ if (!sSnapshotContext->BlitHelper()->BlitImageToFramebuffer(this, size,
+ autoFBForTex.FB(),
+ destOrigin))
+ {
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> source =
+ gfx::Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!source)) {
+ return nullptr;
+ }
+
+ ScopedBindFramebuffer bind(sSnapshotContext, autoFBForTex.FB());
+ ReadPixelsIntoDataSurface(sSnapshotContext, source);
+ return source.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/GLImages.h b/system/graphics/layers/GLImages.h
new file mode 100644
index 000000000..e7110db9d
--- /dev/null
+++ b/system/graphics/layers/GLImages.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GLIMAGES_H
+#define GFX_GLIMAGES_H
+
+#include "GLContextTypes.h"
+#include "GLTypes.h"
+#include "ImageContainer.h" // for Image
+#include "ImageTypes.h" // for ImageFormat::SHARED_GLTEXTURE
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+
+namespace mozilla {
+namespace layers {
+
+class GLImage : public Image {
+public:
+ explicit GLImage(ImageFormat aFormat) : Image(nullptr, aFormat){}
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ GLImage* AsGLImage() override {
+ return this;
+ }
+};
+
+class EGLImageImage : public GLImage {
+public:
+ EGLImageImage(EGLImage aImage, EGLSync aSync,
+ const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+ bool aOwns);
+
+ gfx::IntSize GetSize() override { return mSize; }
+ gl::OriginPos GetOriginPos() const {
+ return mPos;
+ }
+ EGLImage GetImage() const {
+ return mImage;
+ }
+ EGLSync GetSync() const {
+ return mSync;
+ }
+
+ EGLImageImage* AsEGLImageImage() override {
+ return this;
+ }
+
+protected:
+ virtual ~EGLImageImage();
+
+private:
+ EGLImage mImage;
+ EGLSync mSync;
+ gfx::IntSize mSize;
+ gl::OriginPos mPos;
+ bool mOwns;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_GLIMAGES_H
diff --git a/system/graphics/layers/GPUVideoImage.h b/system/graphics/layers/GPUVideoImage.h
new file mode 100644
index 000000000..9a661088b
--- /dev/null
+++ b/system/graphics/layers/GPUVideoImage.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GPU_VIDEO_IMAGE_H
+#define GFX_GPU_VIDEO_IMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/GPUVideoTextureClient.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+namespace mozilla {
+namespace dom {
+class VideoDecoderManagerChild;
+}
+namespace layers {
+
+// Image class that refers to a decoded video frame within
+// the GPU process.
+class GPUVideoImage final : public Image {
+public:
+ GPUVideoImage(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize)
+ : Image(nullptr, ImageFormat::GPU_VIDEO)
+ , mSize(aSize)
+ {
+ // Create the TextureClient immediately since the GPUVideoTextureData
+ // is responsible for deallocating the SurfaceDescriptor.
+ //
+ // Use the RECYCLE texture flag, since it's likely that our 'real'
+ // TextureData (in the decoder thread of the GPU process) is using
+ // it too, and we want to make sure we don't send the delete message
+ // until we've stopped being used on the compositor.
+ mTextureClient =
+ TextureClient::CreateWithData(new GPUVideoTextureData(aManager, aSD, aSize),
+ TextureFlags::RECYCLE,
+ ImageBridgeChild::GetSingleton().get());
+ }
+
+ ~GPUVideoImage() override {}
+
+ gfx::IntSize GetSize() override { return mSize; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
+ {
+ if (!mTextureClient) {
+ return nullptr;
+ }
+ GPUVideoTextureData* data = mTextureClient->GetInternalData()->AsGPUVideoTextureData();
+ return data->GetAsSourceSurface();
+ }
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override
+ {
+ MOZ_ASSERT(aForwarder == ImageBridgeChild::GetSingleton(), "Must only use GPUVideo on ImageBridge");
+ return mTextureClient;
+ }
+
+private:
+ gfx::IntSize mSize;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_GPU_VIDEO_IMAGE_H
diff --git a/system/graphics/layers/IMFYCbCrImage.cpp b/system/graphics/layers/IMFYCbCrImage.cpp
new file mode 100644
index 000000000..47c0ce927
--- /dev/null
+++ b/system/graphics/layers/IMFYCbCrImage.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IMFYCbCrImage.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureClient.h"
+#include "d3d9.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+IMFYCbCrImage::IMFYCbCrImage(IMFMediaBuffer* aBuffer, IMF2DBuffer* a2DBuffer)
+ : RecyclingPlanarYCbCrImage(nullptr)
+ , mBuffer(aBuffer)
+ , m2DBuffer(a2DBuffer)
+{}
+
+IMFYCbCrImage::~IMFYCbCrImage()
+{
+ if (m2DBuffer) {
+ m2DBuffer->Unlock2D();
+ }
+ else {
+ mBuffer->Unlock();
+ }
+}
+
+struct AutoLockTexture
+{
+ AutoLockTexture(ID3D11Texture2D* aTexture)
+ {
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: IMFYCbCrImage timeout");
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ }
+ }
+
+ ~AutoLockTexture()
+ {
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+
+ RefPtr<IDXGIKeyedMutex> mMutex;
+};
+
+static already_AddRefed<IDirect3DTexture9>
+InitTextures(IDirect3DDevice9* aDevice,
+ const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ RefPtr<IDirect3DSurface9>& aSurface,
+ HANDLE& aHandle,
+ D3DLOCKED_RECT& aLockedRect)
+{
+ if (!aDevice) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> result;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_DEFAULT,
+ getter_AddRefs(result), &aHandle))) {
+ return nullptr;
+ }
+ if (!result) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> tmpTexture;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(tmpTexture), nullptr))) {
+ return nullptr;
+ }
+ if (!tmpTexture) {
+ return nullptr;
+ }
+
+ tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface));
+ if (FAILED(aSurface->LockRect(&aLockedRect, nullptr, 0)) ||
+ !aLockedRect.pBits) {
+ NS_WARNING("Could not lock surface");
+ return nullptr;
+ }
+
+ return result.forget();
+}
+
+static bool
+FinishTextures(IDirect3DDevice9* aDevice,
+ IDirect3DTexture9* aTexture,
+ IDirect3DSurface9* aSurface)
+{
+ if (!aDevice) {
+ return false;
+ }
+
+ HRESULT hr = aSurface->UnlockRect();
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<IDirect3DSurface9> dstSurface;
+ hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ hr = aDevice->UpdateSurface(aSurface, nullptr, dstSurface, nullptr);
+ if (FAILED(hr)) {
+ return false;
+ }
+ return true;
+}
+
+TextureClient*
+IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (mTextureClient) {
+ return mTextureClient;
+ }
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ }
+
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (!device || backend != LayersBackend::LAYERS_D3D11) {
+ return nullptr;
+ }
+
+ if (!gfx::DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ if (mData.mYStride < 0 || mData.mCbCrStride < 0) {
+ // D3D11 only supports unsigned stride values.
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM,
+ mData.mYSize.width, mData.mYSize.height, 1, 1);
+
+ if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ } else {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+
+ RefPtr<ID3D11Texture2D> textureY;
+ D3D11_SUBRESOURCE_DATA yData = { mData.mYChannel, (UINT)mData.mYStride, 0 };
+ HRESULT hr = device->CreateTexture2D(&newDesc, &yData, getter_AddRefs(textureY));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ newDesc.Width = mData.mCbCrSize.width;
+ newDesc.Height = mData.mCbCrSize.height;
+
+ RefPtr<ID3D11Texture2D> textureCb;
+ D3D11_SUBRESOURCE_DATA cbData = { mData.mCbChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &cbData, getter_AddRefs(textureCb));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<ID3D11Texture2D> textureCr;
+ D3D11_SUBRESOURCE_DATA crData = { mData.mCrChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &crData, getter_AddRefs(textureCr));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ // Even though the textures we created are meant to be protected by a keyed mutex,
+ // it appears that D3D doesn't include the initial memory upload within this
+ // synchronization. Add an empty lock/unlock pair since that appears to
+ // be sufficient to make sure we synchronize.
+ {
+ AutoLockTexture lockCr(textureCr);
+ }
+
+ mTextureClient = TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT,
+ textureY, textureCb, textureCr,
+ GetSize(), mData.mYSize, mData.mCbCrSize),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/IMFYCbCrImage.h b/system/graphics/layers/IMFYCbCrImage.h
new file mode 100644
index 000000000..511f865de
--- /dev/null
+++ b/system/graphics/layers/IMFYCbCrImage.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMFYCBCRIMAGE_H
+#define GFX_IMFYCBCRIMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mfidl.h"
+
+namespace mozilla {
+namespace layers {
+
+class IMFYCbCrImage : public RecyclingPlanarYCbCrImage
+{
+public:
+ IMFYCbCrImage(IMFMediaBuffer* aBuffer, IMF2DBuffer* a2DBuffer);
+
+ virtual bool IsValid() { return true; }
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+protected:
+
+ ~IMFYCbCrImage();
+
+ RefPtr<IMFMediaBuffer> mBuffer;
+ RefPtr<IMF2DBuffer> m2DBuffer;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/system/graphics/layers/IPDLActor.h b/system/graphics/layers/IPDLActor.h
new file mode 100644
index 000000000..c94a67f52
--- /dev/null
+++ b/system/graphics/layers/IPDLActor.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_IPDLACTOR_H
+#define MOZILLA_LAYERS_IPDLACTOR_H
+
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/Unused.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+/// A base class to facilitate the deallocation of IPDL actors.
+///
+/// Implements the parent side of the simple deallocation handshake.
+/// Override the Destroy method rather than the ActorDestroy method.
+template<typename Protocol>
+class ParentActor : public Protocol
+{
+public:
+ ParentActor() : mDestroyed(false) {}
+
+ ~ParentActor() { MOZ_ASSERT(mDestroyed); }
+
+ bool CanSend() const { return !mDestroyed; }
+
+ // Override this rather than ActorDestroy
+ virtual void Destroy() {}
+
+ virtual bool RecvDestroy() override
+ {
+ DestroyIfNeeded();
+ Unused << Protocol::Send__delete__(this);
+ return true;
+ }
+
+ typedef ipc::IProtocol::ActorDestroyReason Why;
+
+ virtual void ActorDestroy(Why) override {
+ DestroyIfNeeded();
+ }
+
+protected:
+ void DestroyIfNeeded() {
+ if (!mDestroyed) {
+ Destroy();
+ mDestroyed = true;
+ }
+ }
+
+private:
+ bool mDestroyed;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/system/graphics/layers/ImageContainer.cpp b/system/graphics/layers/ImageContainer.cpp
new file mode 100644
index 000000000..ecf2d10b4
--- /dev/null
+++ b/system/graphics/layers/ImageContainer.cpp
@@ -0,0 +1,796 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "ImageContainer.h"
+#include <string.h> // for memcpy, memset
+#include "GLImages.h" // for SurfaceTextureImage
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxUtils.h" // for gfxUtils
+#include "libyuv.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/ImageContainerChild.h"
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/SharedPlanarYCbCrImage.h"
+#include "mozilla/layers/SharedRGBImage.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "nsISupportsUtils.h" // for NS_IF_ADDREF
+#include "YCbCrUtils.h" // for YCbCr conversions
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/CheckedInt.h"
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#include <d3d10_1.h>
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace android;
+using namespace mozilla::gfx;
+
+Atomic<int32_t> Image::sSerialCounter(0);
+
+Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
+
+RefPtr<PlanarYCbCrImage>
+ImageFactory::CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin *aRecycleBin)
+{
+ return new RecyclingPlanarYCbCrImage(aRecycleBin);
+}
+
+BufferRecycleBin::BufferRecycleBin()
+ : mLock("mozilla.layers.BufferRecycleBin.mLock")
+ // This member is only valid when the bin is not empty and will be properly
+ // initialized in RecycleBuffer, but initializing it here avoids static analysis
+ // noise.
+ , mRecycledBufferSize(0)
+{
+}
+
+void
+BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer, uint32_t aSize)
+{
+ MutexAutoLock lock(mLock);
+
+ if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
+ mRecycledBuffers.Clear();
+ }
+ mRecycledBufferSize = aSize;
+ mRecycledBuffers.AppendElement(Move(aBuffer));
+}
+
+UniquePtr<uint8_t[]>
+BufferRecycleBin::GetBuffer(uint32_t aSize)
+{
+ MutexAutoLock lock(mLock);
+
+ if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
+ return MakeUnique<uint8_t[]>(aSize);
+
+ uint32_t last = mRecycledBuffers.Length() - 1;
+ UniquePtr<uint8_t[]> result = Move(mRecycledBuffers[last]);
+ mRecycledBuffers.RemoveElementAt(last);
+ return result;
+}
+
+void
+BufferRecycleBin::ClearRecycledBuffers()
+{
+ MutexAutoLock lock(mLock);
+ if (!mRecycledBuffers.IsEmpty()) {
+ mRecycledBuffers.Clear();
+ }
+ mRecycledBufferSize = 0;
+}
+
+void
+ImageContainer::EnsureImageClient(bool aCreate)
+{
+ // If we're not forcing a new ImageClient, then we can skip this if we don't have an existing
+ // ImageClient, or if the existing one belongs to an IPC actor that is still open.
+ if (!aCreate && (!mImageClient || mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen())) {
+ return;
+ }
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (imageBridge) {
+ mIPDLChild = new ImageContainerChild(this);
+ mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this, mIPDLChild);
+ if (mImageClient) {
+ mAsyncContainerID = mImageClient->GetAsyncID();
+ }
+ }
+}
+
+ImageContainer::ImageContainer(Mode flag)
+: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+ mGenerationCounter(++sGenerationCounter),
+ mPaintCount(0),
+ mDroppedImageCount(0),
+ mImageFactory(new ImageFactory()),
+ mRecycleBin(new BufferRecycleBin()),
+ mCurrentProducerID(-1)
+{
+ if (flag == ASYNCHRONOUS) {
+ EnsureImageClient(true);
+ } else {
+ mAsyncContainerID = sInvalidAsyncContainerId;
+ }
+}
+
+ImageContainer::ImageContainer(uint64_t aAsyncContainerID)
+ : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+ mGenerationCounter(++sGenerationCounter),
+ mPaintCount(0),
+ mDroppedImageCount(0),
+ mImageFactory(nullptr),
+ mRecycleBin(nullptr),
+ mAsyncContainerID(aAsyncContainerID),
+ mCurrentProducerID(-1)
+{
+ MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
+}
+
+ImageContainer::~ImageContainer()
+{
+ if (mIPDLChild) {
+ mIPDLChild->ForgetImageContainer();
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->ReleaseImageContainer(mIPDLChild);
+ }
+ }
+}
+
+RefPtr<PlanarYCbCrImage>
+ImageContainer::CreatePlanarYCbCrImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ EnsureImageClient(false);
+ if (mImageClient && mImageClient->AsImageClientSingle()) {
+ return new SharedPlanarYCbCrImage(mImageClient);
+ }
+ return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
+}
+
+RefPtr<SharedRGBImage>
+ImageContainer::CreateSharedRGBImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ EnsureImageClient(false);
+ if (!mImageClient || !mImageClient->AsImageClientSingle()) {
+ return nullptr;
+ }
+ return new SharedRGBImage(mImageClient);
+}
+
+void
+ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ mGenerationCounter = ++sGenerationCounter;
+
+ if (!aImages.IsEmpty()) {
+ NS_ASSERTION(mCurrentImages.IsEmpty() ||
+ mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
+ mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
+ "frame IDs shouldn't go backwards");
+ if (aImages[0].mProducerID != mCurrentProducerID) {
+ mFrameIDsNotYetComposited.Clear();
+ mCurrentProducerID = aImages[0].mProducerID;
+ } else if (!aImages[0].mTimeStamp.IsNull()) {
+ // Check for expired frames
+ for (auto& img : mCurrentImages) {
+ if (img.mProducerID != aImages[0].mProducerID ||
+ img.mTimeStamp.IsNull() ||
+ img.mTimeStamp >= aImages[0].mTimeStamp) {
+ break;
+ }
+ if (!img.mComposited && !img.mTimeStamp.IsNull() &&
+ img.mFrameID != aImages[0].mFrameID) {
+ mFrameIDsNotYetComposited.AppendElement(img.mFrameID);
+ }
+ }
+
+ // Remove really old frames, assuming they'll never be composited.
+ const uint32_t maxFrames = 100;
+ if (mFrameIDsNotYetComposited.Length() > maxFrames) {
+ uint32_t dropFrames = mFrameIDsNotYetComposited.Length() - maxFrames;
+ mDroppedImageCount += dropFrames;
+ mFrameIDsNotYetComposited.RemoveElementsAt(0, dropFrames);
+ }
+ }
+ }
+
+ nsTArray<OwningImage> newImages;
+
+ for (uint32_t i = 0; i < aImages.Length(); ++i) {
+ NS_ASSERTION(aImages[i].mImage, "image can't be null");
+ NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
+ "Multiple images require timestamps");
+ if (i > 0) {
+ NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
+ "Timestamps must not decrease");
+ NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
+ "FrameIDs must increase");
+ NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
+ "ProducerIDs must be the same");
+ }
+ OwningImage* img = newImages.AppendElement();
+ img->mImage = aImages[i].mImage;
+ img->mTimeStamp = aImages[i].mTimeStamp;
+ img->mFrameID = aImages[i].mFrameID;
+ img->mProducerID = aImages[i].mProducerID;
+ for (auto& oldImg : mCurrentImages) {
+ if (oldImg.mFrameID == img->mFrameID &&
+ oldImg.mProducerID == img->mProducerID) {
+ img->mComposited = oldImg.mComposited;
+ break;
+ }
+ }
+ }
+
+ mCurrentImages.SwapElements(newImages);
+}
+
+void
+ImageContainer::ClearImagesFromImageBridge()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ SetCurrentImageInternal(nsTArray<NonOwningImage>());
+}
+
+void
+ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
+{
+ MOZ_ASSERT(!aImages.IsEmpty());
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mImageClient) {
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->UpdateImageClient(mImageClient, this);
+ }
+ }
+ SetCurrentImageInternal(aImages);
+}
+
+void
+ImageContainer::ClearAllImages()
+{
+ if (mImageClient) {
+ // Let ImageClient release all TextureClients. This doesn't return
+ // until ImageBridge has called ClearCurrentImageFromImageBridge.
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->FlushAllImages(mImageClient, this);
+ }
+ return;
+ }
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ SetCurrentImageInternal(nsTArray<NonOwningImage>());
+}
+
+void
+ImageContainer::ClearCachedResources()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mImageClient && mImageClient->AsImageClientSingle()) {
+ if (!mImageClient->HasTextureClientRecycler()) {
+ return;
+ }
+ mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
+ return;
+ }
+ return mRecycleBin->ClearRecycledBuffers();
+}
+
+void
+ImageContainer::SetCurrentImageInTransaction(Image *aImage)
+{
+ AutoTArray<NonOwningImage,1> images;
+ images.AppendElement(NonOwningImage(aImage));
+ SetCurrentImagesInTransaction(images);
+}
+
+void
+ImageContainer::SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+ NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
+
+ SetCurrentImageInternal(aImages);
+}
+
+bool ImageContainer::IsAsync() const
+{
+ return mAsyncContainerID != sInvalidAsyncContainerId;
+}
+
+uint64_t ImageContainer::GetAsyncContainerID()
+{
+ NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
+ EnsureImageClient(false);
+ return mAsyncContainerID;
+}
+
+bool
+ImageContainer::HasCurrentImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ return !mCurrentImages.IsEmpty();
+}
+
+void
+ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
+ uint32_t* aGenerationCounter)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ *aImages = mCurrentImages;
+ if (aGenerationCounter) {
+ *aGenerationCounter = mGenerationCounter;
+ }
+}
+
+gfx::IntSize
+ImageContainer::GetCurrentSize()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (mCurrentImages.IsEmpty()) {
+ return gfx::IntSize(0, 0);
+ }
+
+ return mCurrentImages[0].mImage->GetSize();
+}
+
+void
+ImageContainer::NotifyCompositeInternal(const ImageCompositeNotification& aNotification)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // An image composition notification is sent the first time a particular
+ // image is composited by an ImageHost. Thus, every time we receive such
+ // a notification, a new image has been painted.
+ ++mPaintCount;
+
+ if (aNotification.producerID() == mCurrentProducerID) {
+ uint32_t i;
+ for (i = 0; i < mFrameIDsNotYetComposited.Length(); ++i) {
+ if (mFrameIDsNotYetComposited[i] <= aNotification.frameID()) {
+ if (mFrameIDsNotYetComposited[i] < aNotification.frameID()) {
+ ++mDroppedImageCount;
+ }
+ } else {
+ break;
+ }
+ }
+ mFrameIDsNotYetComposited.RemoveElementsAt(0, i);
+ for (auto& img : mCurrentImages) {
+ if (img.mFrameID == aNotification.frameID()) {
+ img.mComposited = true;
+ }
+ }
+ }
+
+ if (!aNotification.imageTimeStamp().IsNull()) {
+ mPaintDelay = aNotification.firstCompositeTimeStamp() -
+ aNotification.imageTimeStamp();
+ }
+}
+
+PlanarYCbCrImage::PlanarYCbCrImage()
+ : Image(nullptr, ImageFormat::PLANAR_YCBCR)
+ , mOffscreenFormat(SurfaceFormat::UNKNOWN)
+ , mBufferSize(0)
+{
+}
+
+RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage()
+{
+ if (mBuffer) {
+ mRecycleBin->RecycleBuffer(Move(mBuffer), mBufferSize);
+ }
+}
+
+size_t
+RecyclingPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // Ignoring:
+ // - mData - just wraps mBuffer
+ // - Surfaces should be reported under gfx-surfaces-*:
+ // - mSourceSurface
+ // - Base class:
+ // - mImplData is not used
+ // Not owned:
+ // - mRecycleBin
+ size_t size = aMallocSizeOf(mBuffer.get());
+
+ // Could add in the future:
+ // - mBackendData (from base class)
+
+ return size;
+}
+
+UniquePtr<uint8_t[]>
+RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
+{
+ return mRecycleBin->GetBuffer(aSize);
+}
+
+static void
+CopyPlane(uint8_t *aDst, const uint8_t *aSrc,
+ const gfx::IntSize &aSize, int32_t aStride, int32_t aSkip)
+{
+ int32_t height = aSize.height;
+ int32_t width = aSize.width;
+
+ MOZ_RELEASE_ASSERT(width <= aStride);
+
+ if (!aSkip) {
+ // Fast path: planar input.
+ memcpy(aDst, aSrc, height * aStride);
+ } else {
+ for (int y = 0; y < height; ++y) {
+ const uint8_t *src = aSrc;
+ uint8_t *dst = aDst;
+ // Slow path
+ for (int x = 0; x < width; ++x) {
+ *dst++ = *src++;
+ src += aSkip;
+ }
+ aSrc += aStride;
+ aDst += aStride;
+ }
+ }
+}
+
+bool
+RecyclingPlanarYCbCrImage::CopyData(const Data& aData)
+{
+ // update buffer size
+ // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
+ const auto checkedSize =
+ CheckedInt<uint32_t>(aData.mCbCrStride) * aData.mCbCrSize.height * 2 +
+ CheckedInt<uint32_t>(aData.mYStride) * aData.mYSize.height;
+
+ if (!checkedSize.isValid())
+ return false;
+
+ const auto size = checkedSize.value();
+
+ // get new buffer
+ mBuffer = AllocateBuffer(size);
+ if (!mBuffer)
+ return false;
+
+ // update buffer size
+ mBufferSize = size;
+
+ mData = aData;
+ mData.mYChannel = mBuffer.get();
+ mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
+ mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
+ mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0;
+
+ CopyPlane(mData.mYChannel, aData.mYChannel,
+ aData.mYSize, aData.mYStride, aData.mYSkip);
+ CopyPlane(mData.mCbChannel, aData.mCbChannel,
+ aData.mCbCrSize, aData.mCbCrStride, aData.mCbSkip);
+ CopyPlane(mData.mCrChannel, aData.mCrChannel,
+ aData.mCbCrSize, aData.mCbCrStride, aData.mCrSkip);
+
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+ return true;
+}
+
+gfxImageFormat
+PlanarYCbCrImage::GetOffscreenFormat()
+{
+ return mOffscreenFormat == SurfaceFormat::UNKNOWN ?
+ gfxVars::OffscreenFormat() :
+ mOffscreenFormat;
+}
+
+bool
+PlanarYCbCrImage::AdoptData(const Data &aData)
+{
+ mData = aData;
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+ return true;
+}
+
+uint8_t*
+RecyclingPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
+{
+ // get new buffer
+ mBuffer = AllocateBuffer(aSize);
+ if (mBuffer) {
+ // update buffer size
+ mBufferSize = aSize;
+ }
+ return mBuffer.get();
+}
+
+already_AddRefed<gfx::SourceSurface>
+PlanarYCbCrImage::GetAsSourceSurface()
+{
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ gfx::IntSize size(mSize);
+ gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+ gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
+ if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(), mapping.GetStride());
+
+ mSourceSurface = surface;
+
+ return surface.forget();
+}
+
+NVImage::NVImage()
+ : Image(nullptr, ImageFormat::NV_IMAGE)
+ , mBufferSize(0)
+{
+}
+
+NVImage::~NVImage()
+{
+}
+
+IntSize
+NVImage::GetSize()
+{
+ return mSize;
+}
+
+IntRect
+NVImage::GetPictureRect()
+{
+ return mData.GetPictureRect();
+}
+
+already_AddRefed<SourceSurface>
+NVImage::GetAsSourceSurface()
+{
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
+ // logics in PlanarYCbCrImage::GetAsSourceSurface().
+ const int bufferLength = mData.mYSize.height * mData.mYStride +
+ mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
+ uint8_t* buffer = new uint8_t[bufferLength];
+
+ Data aData = mData;
+ aData.mCbCrStride = aData.mCbCrSize.width;
+ aData.mCbSkip = 0;
+ aData.mCrSkip = 0;
+ aData.mYChannel = buffer;
+ aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
+ aData.mCrChannel = aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
+
+ if (mData.mCbChannel < mData.mCrChannel) { // NV12
+ libyuv::NV12ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCbChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ } else { // NV21
+ libyuv::NV21ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCrChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ }
+
+ // The logics in PlanarYCbCrImage::GetAsSourceSurface().
+ gfx::IntSize size(mSize);
+ gfx::SurfaceFormat format =
+ gfx::ImageFormatToSurfaceFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(), mapping.GetStride());
+
+ mSourceSurface = surface;
+
+ // Release the temporary buffer.
+ delete[] buffer;
+
+ return surface.forget();
+}
+
+bool
+NVImage::IsValid()
+{
+ return !!mBufferSize;
+}
+
+uint32_t
+NVImage::GetBufferSize() const
+{
+ return mBufferSize;
+}
+
+NVImage*
+NVImage::AsNVImage()
+{
+ return this;
+};
+
+bool
+NVImage::SetData(const Data& aData)
+{
+ MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
+ MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
+
+ // Calculate buffer size
+ // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
+ const auto checkedSize =
+ CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
+ CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
+
+ if (!checkedSize.isValid())
+ return false;
+
+ const auto size = checkedSize.value();
+
+ // Allocate a new buffer.
+ mBuffer = AllocateBuffer(size);
+ if (!mBuffer) {
+ return false;
+ }
+
+ // Update mBufferSize.
+ mBufferSize = size;
+
+ // Update mData.
+ mData = aData;
+ mData.mYChannel = mBuffer.get();
+ mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
+ mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
+
+ // Update mSize.
+ mSize = aData.mPicSize;
+
+ // Copy the input data into mBuffer.
+ // This copies the y-channel and the interleaving CbCr-channel.
+ memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
+
+ return true;
+}
+
+const NVImage::Data*
+NVImage::GetData() const
+{
+ return &mData;
+}
+
+UniquePtr<uint8_t>
+NVImage::AllocateBuffer(uint32_t aSize)
+{
+ UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
+ return buffer;
+}
+
+SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
+ : Image(nullptr, ImageFormat::CAIRO_SURFACE),
+ mSize(aSize),
+ mSourceSurface(aSourceSurface),
+ mTextureFlags(TextureFlags::DEFAULT)
+{}
+
+SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
+ : Image(nullptr, ImageFormat::CAIRO_SURFACE),
+ mSize(aSourceSurface->GetSize()),
+ mSourceSurface(aSourceSurface),
+ mTextureFlags(TextureFlags::DEFAULT)
+{}
+
+SourceSurfaceImage::~SourceSurfaceImage()
+{
+}
+
+TextureClient*
+SourceSurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (!aForwarder) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> textureClient = mTextureClients.Get(aForwarder->GetSerial());
+ if (textureClient) {
+ return textureClient;
+ }
+
+ RefPtr<SourceSurface> surface = GetAsSourceSurface();
+ MOZ_ASSERT(surface);
+ if (!surface) {
+ return nullptr;
+ }
+
+ if (!textureClient) {
+ // gfx::BackendType::NONE means default to content backend
+ textureClient = TextureClient::CreateFromSurface(aForwarder,
+ surface,
+ BackendSelector::Content,
+ mTextureFlags,
+ ALLOC_DEFAULT);
+ }
+ if (!textureClient) {
+ return nullptr;
+ }
+
+ textureClient->SyncWithObject(aForwarder->GetSyncObject());
+
+ mTextureClients.Put(aForwarder->GetSerial(), textureClient);
+ return textureClient;
+}
+
+PImageContainerChild*
+ImageContainer::GetPImageContainerChild()
+{
+ return mIPDLChild;
+}
+
+ImageContainer::ProducerID
+ImageContainer::AllocateProducerID()
+{
+ // Callable on all threads.
+ static Atomic<ImageContainer::ProducerID> sProducerID(0u);
+ return ++sProducerID;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ImageContainer.h b/system/graphics/layers/ImageContainer.h
new file mode 100644
index 000000000..ea619b77b
--- /dev/null
+++ b/system/graphics/layers/ImageContainer.h
@@ -0,0 +1,892 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGECONTAINER_H
+#define GFX_IMAGECONTAINER_H
+
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include "gfxTypes.h"
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Mutex.h" // for Mutex
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // For IntSize
+#include "mozilla/layers/GonkNativeHandle.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc
+#include "nsAutoRef.h" // for nsCountedRef
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/Atomics.h"
+#include "mozilla/WeakPtr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "nsDataHashtable.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/UniquePtr.h"
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * We need to be able to hold a reference to a Moz2D SourceSurface from Image
+ * subclasses. This is potentially a problem since Images can be addrefed
+ * or released off the main thread. We can ensure that we never AddRef
+ * a SourceSurface off the main thread, but we might want to Release due
+ * to an Image being destroyed off the main thread.
+ *
+ * We use nsCountedRef<nsMainThreadSourceSurfaceRef> to reference the
+ * SourceSurface. When AddRefing, we assert that we're on the main thread.
+ * When Releasing, if we're not on the main thread, we post an event to
+ * the main thread to do the actual release.
+ */
+class nsMainThreadSourceSurfaceRef;
+
+template <>
+class nsAutoRefTraits<nsMainThreadSourceSurfaceRef> {
+public:
+ typedef mozilla::gfx::SourceSurface* RawRef;
+
+ /**
+ * The XPCOM event that will do the actual release on the main thread.
+ */
+ class SurfaceReleaser : public mozilla::Runnable {
+ public:
+ explicit SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
+ NS_IMETHOD Run() override {
+ mRef->Release();
+ return NS_OK;
+ }
+ RawRef mRef;
+ };
+
+ static RawRef Void() { return nullptr; }
+ static void Release(RawRef aRawRef)
+ {
+ if (NS_IsMainThread()) {
+ aRawRef->Release();
+ return;
+ }
+ nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
+ NS_DispatchToMainThread(runnable);
+ }
+ static void AddRef(RawRef aRawRef)
+ {
+ NS_ASSERTION(NS_IsMainThread(),
+ "Can only add a reference on the main thread");
+ aRawRef->AddRef();
+ }
+};
+
+class nsOwningThreadSourceSurfaceRef;
+
+template <>
+class nsAutoRefTraits<nsOwningThreadSourceSurfaceRef> {
+public:
+ typedef mozilla::gfx::SourceSurface* RawRef;
+
+ /**
+ * The XPCOM event that will do the actual release on the creation thread.
+ */
+ class SurfaceReleaser : public mozilla::Runnable {
+ public:
+ explicit SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
+ NS_IMETHOD Run() override {
+ mRef->Release();
+ return NS_OK;
+ }
+ RawRef mRef;
+ };
+
+ static RawRef Void() { return nullptr; }
+ void Release(RawRef aRawRef)
+ {
+ MOZ_ASSERT(mOwningThread);
+ bool current;
+ mOwningThread->IsOnCurrentThread(&current);
+ if (current) {
+ aRawRef->Release();
+ return;
+ }
+ nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
+ mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+ }
+ void AddRef(RawRef aRawRef)
+ {
+ MOZ_ASSERT(!mOwningThread);
+ NS_GetCurrentThread(getter_AddRefs(mOwningThread));
+ aRawRef->AddRef();
+ }
+
+private:
+ nsCOMPtr<nsIThread> mOwningThread;
+};
+
+#endif
+
+#ifdef XP_WIN
+struct ID3D10Texture2D;
+struct ID3D10Device;
+struct ID3D10ShaderResourceView;
+#endif
+
+typedef void* HANDLE;
+
+namespace mozilla {
+
+
+namespace layers {
+
+class ImageClient;
+class ImageCompositeNotification;
+class ImageContainerChild;
+class PImageContainerChild;
+class SharedPlanarYCbCrImage;
+class PlanarYCbCrImage;
+class TextureClient;
+class KnowsCompositor;
+class NVImage;
+
+struct ImageBackendData
+{
+ virtual ~ImageBackendData() {}
+
+protected:
+ ImageBackendData() {}
+};
+
+/* Forward declarations for Image derivatives. */
+class GLImage;
+class EGLImageImage;
+class SharedRGBImage;
+
+/**
+ * A class representing a buffer of pixel data. The data can be in one
+ * of various formats including YCbCr.
+ *
+ * Create an image using an ImageContainer. Fill the image with data, and
+ * then call ImageContainer::SetImage to display it. An image must not be
+ * modified after calling SetImage. Image implementations do not need to
+ * perform locking; when filling an Image, the Image client is responsible
+ * for ensuring only one thread accesses the Image at a time, and after
+ * SetImage the image is immutable.
+ *
+ * When resampling an Image, only pixels within the buffer should be
+ * sampled. For example, cairo images should be sampled in EXTEND_PAD mode.
+ */
+class Image {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Image)
+
+public:
+ ImageFormat GetFormat() { return mFormat; }
+ void* GetImplData() { return mImplData; }
+
+ virtual gfx::IntSize GetSize() = 0;
+ virtual gfx::IntPoint GetOrigin()
+ {
+ return gfx::IntPoint(0, 0);
+ }
+ virtual gfx::IntRect GetPictureRect()
+ {
+ return gfx::IntRect(GetOrigin().x, GetOrigin().y, GetSize().width, GetSize().height);
+ }
+
+ ImageBackendData* GetBackendData(LayersBackend aBackend)
+ { return mBackendData[aBackend]; }
+ void SetBackendData(LayersBackend aBackend, ImageBackendData* aData)
+ { mBackendData[aBackend] = aData; }
+
+ int32_t GetSerial() { return mSerial; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() = 0;
+
+ virtual bool IsValid() { return true; }
+
+ virtual uint8_t* GetBuffer() { return nullptr; }
+
+ /**
+ * For use with the TextureForwarder only (so that the later can
+ * synchronize the TextureClient with the TextureHost).
+ */
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) { return nullptr; }
+
+ /* Access to derived classes. */
+ virtual EGLImageImage* AsEGLImageImage() { return nullptr; }
+ virtual GLImage* AsGLImage() { return nullptr; }
+ virtual PlanarYCbCrImage* AsPlanarYCbCrImage() { return nullptr; }
+
+ virtual NVImage* AsNVImage() { return nullptr; }
+
+protected:
+ Image(void* aImplData, ImageFormat aFormat) :
+ mImplData(aImplData),
+ mSerial(++sSerialCounter),
+ mFormat(aFormat)
+ {}
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Image() {}
+
+ mozilla::EnumeratedArray<mozilla::layers::LayersBackend,
+ mozilla::layers::LayersBackend::LAYERS_LAST,
+ nsAutoPtr<ImageBackendData>>
+ mBackendData;
+
+ void* mImplData;
+ int32_t mSerial;
+ ImageFormat mFormat;
+
+ static mozilla::Atomic<int32_t> sSerialCounter;
+};
+
+/**
+ * A RecycleBin is owned by an ImageContainer. We store buffers in it that we
+ * want to recycle from one image to the next.It's a separate object from
+ * ImageContainer because images need to store a strong ref to their RecycleBin
+ * and we must avoid creating a reference loop between an ImageContainer and
+ * its active image.
+ */
+class BufferRecycleBin final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferRecycleBin)
+
+ //typedef mozilla::gl::GLContext GLContext;
+
+public:
+ BufferRecycleBin();
+
+ void RecycleBuffer(mozilla::UniquePtr<uint8_t[]> aBuffer, uint32_t aSize);
+ // Returns a recycled buffer of the right size, or allocates a new buffer.
+ mozilla::UniquePtr<uint8_t[]> GetBuffer(uint32_t aSize);
+ virtual void ClearRecycledBuffers();
+private:
+ typedef mozilla::Mutex Mutex;
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~BufferRecycleBin()
+ {
+ }
+
+ // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
+ // and mRecycledTextureSizes
+ Mutex mLock;
+
+ // We should probably do something to prune this list on a timer so we don't
+ // eat excess memory while video is paused...
+ nsTArray<mozilla::UniquePtr<uint8_t[]>> mRecycledBuffers;
+ // This is only valid if mRecycledBuffers is non-empty
+ uint32_t mRecycledBufferSize;
+};
+
+/**
+ * A class that manages Image creation for a LayerManager. The only reason
+ * we need a separate class here is that LayerManagers aren't threadsafe
+ * (because layers can only be used on the main thread) and we want to
+ * be able to create images from any thread, to facilitate video playback
+ * without involving the main thread, for example.
+ * Different layer managers can implement child classes of this making it
+ * possible to create layer manager specific images.
+ * This class is not meant to be used directly but rather can be set on an
+ * image container. This is usually done by the layer system internally and
+ * not explicitly by users. For PlanarYCbCr or Cairo images the default
+ * implementation will creates images whose data lives in system memory, for
+ * MacIOSurfaces the default implementation will be a simple MacIOSurface
+ * wrapper.
+ */
+
+class ImageFactory
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
+protected:
+ friend class ImageContainer;
+
+ ImageFactory() {}
+ virtual ~ImageFactory() {}
+
+ virtual RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage(
+ const gfx::IntSize& aScaleHint,
+ BufferRecycleBin *aRecycleBin);
+};
+
+/**
+ * A class that manages Images for an ImageLayer. The only reason
+ * we need a separate class here is that ImageLayers aren't threadsafe
+ * (because layers can only be used on the main thread) and we want to
+ * be able to set the current Image from any thread, to facilitate
+ * video playback without involving the main thread, for example.
+ *
+ * An ImageContainer can operate in one of these modes:
+ * 1) Normal. Triggered by constructing the ImageContainer with
+ * DISABLE_ASYNC or when compositing is happening on the main thread.
+ * SetCurrentImages changes ImageContainer state but nothing is sent to the
+ * compositor until the next layer transaction.
+ * 2) Asynchronous. Initiated by constructing the ImageContainer with
+ * ENABLE_ASYNC when compositing is happening on the main thread.
+ * SetCurrentImages sends a message through the ImageBridge to the compositor
+ * thread to update the image, without going through the main thread or
+ * a layer transaction.
+ * The ImageContainer uses a shared memory block containing a cross-process mutex
+ * to communicate with the compositor thread. SetCurrentImage synchronously
+ * updates the shared state to point to the new image and the old image
+ * is immediately released (not true in Normal or Asynchronous modes).
+ */
+class ImageContainer final : public SupportsWeakPtr<ImageContainer>
+{
+ friend class ImageContainerChild;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
+
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
+
+ enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 };
+
+ static const uint64_t sInvalidAsyncContainerId = 0;
+
+ explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
+
+ /**
+ * Create ImageContainer just to hold another ASYNCHRONOUS ImageContainer's
+ * async container ID.
+ * @param aAsyncContainerID async container ID for which we are a proxy
+ */
+ explicit ImageContainer(uint64_t aAsyncContainerID);
+
+ typedef uint32_t FrameID;
+ typedef uint32_t ProducerID;
+
+ RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
+
+ // Factory methods for shared image types.
+ RefPtr<SharedRGBImage> CreateSharedRGBImage();
+
+ struct NonOwningImage {
+ explicit NonOwningImage(Image* aImage = nullptr,
+ TimeStamp aTimeStamp = TimeStamp(),
+ FrameID aFrameID = 0,
+ ProducerID aProducerID = 0)
+ : mImage(aImage), mTimeStamp(aTimeStamp), mFrameID(aFrameID),
+ mProducerID(aProducerID) {}
+ Image* mImage;
+ TimeStamp mTimeStamp;
+ FrameID mFrameID;
+ ProducerID mProducerID;
+ };
+ /**
+ * Set aImages as the list of timestamped to display. The Images must have
+ * been created by this ImageContainer.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ * aImages must be non-empty. The first timestamp in the list may be
+ * null but the others must not be, and the timestamps must increase.
+ * Every element of aImages must have non-null mImage.
+ * mFrameID can be zero, in which case you won't get meaningful
+ * painted/dropped frame counts. Otherwise you should use a unique and
+ * increasing ID for each decoded and submitted frame (but it's OK to
+ * pass the same frame to SetCurrentImages).
+ * mProducerID is a unique ID for the stream of images. A change in the
+ * mProducerID means changing to a new mFrameID namespace. All frames in
+ * aImages must have the same mProducerID.
+ *
+ * The Image data must not be modified after this method is called!
+ * Note that this must not be called if ENABLE_ASYNC has not been set.
+ *
+ * The implementation calls CurrentImageChanged() while holding
+ * mReentrantMonitor.
+ *
+ * If this ImageContainer has an ImageClient for async video:
+ * Schedule a task to send the image to the compositor using the
+ * PImageBridge protcol without using the main thread.
+ */
+ void SetCurrentImages(const nsTArray<NonOwningImage>& aImages);
+
+ /**
+ * Clear all images. Let ImageClient release all TextureClients.
+ */
+ void ClearAllImages();
+
+ /**
+ * Clear any resources that are not immediately necessary. This may be called
+ * in low-memory conditions.
+ */
+ void ClearCachedResources();
+
+ /**
+ * Clear the current images.
+ * This function is expect to be called only from a CompositableClient
+ * that belongs to ImageBridgeChild. Created to prevent dead lock.
+ * See Bug 901224.
+ */
+ void ClearImagesFromImageBridge();
+
+ /**
+ * Set an Image as the current image to display. The Image must have
+ * been created by this ImageContainer.
+ * Must be called on the main thread, within a layers transaction.
+ *
+ * This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ * aImage can be null. While it's null, nothing will be painted.
+ *
+ * The Image data must not be modified after this method is called!
+ * Note that this must not be called if ENABLE_ASYNC been set.
+ *
+ * You won't get meaningful painted/dropped counts when using this method.
+ */
+ void SetCurrentImageInTransaction(Image* aImage);
+ void SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages);
+
+ /**
+ * Returns true if this ImageContainer uses the ImageBridge IPDL protocol.
+ *
+ * Can be called from any thread.
+ */
+ bool IsAsync() const;
+
+ /**
+ * If this ImageContainer uses ImageBridge, returns the ID associated to
+ * this container, for use in the ImageBridge protocol.
+ * Returns 0 if this ImageContainer does not use ImageBridge. Note that
+ * 0 is always an invalid ID for asynchronous image containers.
+ *
+ * Can be called from any thread.
+ */
+ uint64_t GetAsyncContainerID();
+
+ /**
+ * Returns if the container currently has an image.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ */
+ bool HasCurrentImage();
+
+ struct OwningImage {
+ OwningImage() : mFrameID(0), mProducerID(0), mComposited(false) {}
+ RefPtr<Image> mImage;
+ TimeStamp mTimeStamp;
+ FrameID mFrameID;
+ ProducerID mProducerID;
+ bool mComposited;
+ };
+ /**
+ * Copy the current Image list to aImages.
+ * This has to add references since otherwise there are race conditions
+ * where the current image is destroyed before the caller can add
+ * a reference.
+ * Can be called on any thread.
+ * May return an empty list to indicate there is no current image.
+ * If aGenerationCounter is non-null, sets *aGenerationCounter to a value
+ * that's unique for this ImageContainer state.
+ */
+ void GetCurrentImages(nsTArray<OwningImage>* aImages,
+ uint32_t* aGenerationCounter = nullptr);
+
+ /**
+ * Returns the size of the image in pixels.
+ * Can be called on any thread. This method takes mReentrantMonitor when accessing
+ * thread-shared state.
+ */
+ gfx::IntSize GetCurrentSize();
+
+ /**
+ * Sets a size that the image is expected to be rendered at.
+ * This is a hint for image backends to optimize scaling.
+ * Default implementation in this class is to ignore the hint.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ */
+ void SetScaleHint(const gfx::IntSize& aScaleHint)
+ { mScaleHint = aScaleHint; }
+
+ void SetImageFactory(ImageFactory *aFactory)
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mImageFactory = aFactory ? aFactory : new ImageFactory();
+ }
+
+ ImageFactory* GetImageFactory() const
+ {
+ return mImageFactory;
+ }
+
+ /**
+ * Returns the delay between the last composited image's presentation
+ * timestamp and when it was first composited. It's possible for the delay
+ * to be negative if the first image in the list passed to SetCurrentImages
+ * has a presentation timestamp greater than "now".
+ * Returns 0 if the composited image had a null timestamp, or if no
+ * image has been composited yet.
+ */
+ TimeDuration GetPaintDelay()
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mPaintDelay;
+ }
+
+ /**
+ * Returns the number of images which have been contained in this container
+ * and painted at least once. Can be called from any thread.
+ */
+ uint32_t GetPaintCount() {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mPaintCount;
+ }
+
+ /**
+ * An entry in the current image list "expires" when the entry has an
+ * non-null timestamp, and in a SetCurrentImages call the new image list is
+ * non-empty, the timestamp of the first new image is non-null and greater
+ * than the timestamp associated with the image, and the first new image's
+ * frameID is not the same as the entry's.
+ * Every expired image that is never composited is counted as dropped.
+ */
+ uint32_t GetDroppedImageCount()
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mDroppedImageCount;
+ }
+
+ PImageContainerChild* GetPImageContainerChild();
+
+ /**
+ * Main thread only.
+ */
+ static ProducerID AllocateProducerID();
+
+private:
+ typedef mozilla::ReentrantMonitor ReentrantMonitor;
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~ImageContainer();
+
+ void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages);
+
+ // This is called to ensure we have an active image, this may not be true
+ // when we're storing image information in a RemoteImageData structure.
+ // NOTE: If we have remote data mRemoteDataMutex should be locked when
+ // calling this function!
+ void EnsureActiveImage();
+
+ void EnsureImageClient(bool aCreate);
+
+ void NotifyCompositeInternal(const ImageCompositeNotification& aNotification);
+
+ // ReentrantMonitor to protect thread safe access to the "current
+ // image", and any other state which is shared between threads.
+ ReentrantMonitor mReentrantMonitor;
+
+ nsTArray<OwningImage> mCurrentImages;
+
+ // Updates every time mActiveImage changes
+ uint32_t mGenerationCounter;
+
+ // Number of contained images that have been painted at least once. It's up
+ // to the ImageContainer implementation to ensure accesses to this are
+ // threadsafe.
+ uint32_t mPaintCount;
+
+ // See GetPaintDelay. Accessed only with mReentrantMonitor held.
+ TimeDuration mPaintDelay;
+
+ // See GetDroppedImageCount. Accessed only with mReentrantMonitor held.
+ uint32_t mDroppedImageCount;
+
+ // This is the image factory used by this container, layer managers using
+ // this container can set an alternative image factory that will be used to
+ // create images for this container.
+ RefPtr<ImageFactory> mImageFactory;
+
+ gfx::IntSize mScaleHint;
+
+ RefPtr<BufferRecycleBin> mRecycleBin;
+
+ // This member points to an ImageClient if this ImageContainer was
+ // sucessfully created with ENABLE_ASYNC, or points to null otherwise.
+ // 'unsuccessful' in this case only means that the ImageClient could not
+ // be created, most likely because off-main-thread compositing is not enabled.
+ // In this case the ImageContainer is perfectly usable, but it will forward
+ // frames to the compositor through transactions in the main thread rather than
+ // asynchronusly using the ImageBridge IPDL protocol.
+ RefPtr<ImageClient> mImageClient;
+
+ uint64_t mAsyncContainerID;
+
+ nsTArray<FrameID> mFrameIDsNotYetComposited;
+ // ProducerID for last current image(s), including the frames in
+ // mFrameIDsNotYetComposited
+ ProducerID mCurrentProducerID;
+
+ // Object must be released on the ImageBridge thread. Field is immutable
+ // after creation of the ImageContainer.
+ RefPtr<ImageContainerChild> mIPDLChild;
+
+ static mozilla::Atomic<uint32_t> sGenerationCounter;
+};
+
+class AutoLockImage
+{
+public:
+ explicit AutoLockImage(ImageContainer *aContainer)
+ {
+ aContainer->GetCurrentImages(&mImages);
+ }
+
+ bool HasImage() const { return !mImages.IsEmpty(); }
+ Image* GetImage() const
+ {
+ return mImages.IsEmpty() ? nullptr : mImages[0].mImage.get();
+ }
+
+private:
+ AutoTArray<ImageContainer::OwningImage,4> mImages;
+};
+
+struct PlanarYCbCrData {
+ // Luminance buffer
+ uint8_t* mYChannel;
+ int32_t mYStride;
+ gfx::IntSize mYSize;
+ int32_t mYSkip;
+ // Chroma buffers
+ uint8_t* mCbChannel;
+ uint8_t* mCrChannel;
+ int32_t mCbCrStride;
+ gfx::IntSize mCbCrSize;
+ int32_t mCbSkip;
+ int32_t mCrSkip;
+ // Picture region
+ uint32_t mPicX;
+ uint32_t mPicY;
+ gfx::IntSize mPicSize;
+ StereoMode mStereoMode;
+ YUVColorSpace mYUVColorSpace;
+
+ gfx::IntRect GetPictureRect() const {
+ return gfx::IntRect(mPicX, mPicY,
+ mPicSize.width,
+ mPicSize.height);
+ }
+
+ PlanarYCbCrData()
+ : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
+ , mCbChannel(nullptr), mCrChannel(nullptr)
+ , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
+ , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
+ , mYUVColorSpace(YUVColorSpace::BT601)
+ {}
+};
+
+/****** Image subtypes for the different formats ******/
+
+/**
+ * We assume that the image data is in the REC 470M color space (see
+ * Theora specification, section 4.3.1).
+ *
+ * The YCbCr format can be:
+ *
+ * 4:4:4 - CbCr width/height are the same as Y.
+ * 4:2:2 - CbCr width is half that of Y. Height is the same.
+ * 4:2:0 - CbCr width and height is half that of Y.
+ *
+ * The color format is detected based on the height/width ratios
+ * defined above.
+ *
+ * The Image that is rendered is the picture region defined by
+ * mPicX, mPicY and mPicSize. The size of the rendered image is
+ * mPicSize, not mYSize or mCbCrSize.
+ *
+ * mYSkip, mCbSkip, mCrSkip are added to support various output
+ * formats from hardware decoder. They are per-pixel skips in the
+ * source image.
+ *
+ * For example when image width is 640, mYStride is 670, mYSkip is 3,
+ * the mYChannel buffer looks like:
+ *
+ * |<----------------------- mYStride ----------------------------->|
+ * |<----------------- mYSize.width --------------->|
+ * 0 3 6 9 12 15 18 21 659 669
+ * |----------------------------------------------------------------|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * | |<->|
+ * mYSkip
+ */
+class PlanarYCbCrImage : public Image {
+public:
+ typedef PlanarYCbCrData Data;
+
+ enum {
+ MAX_DIMENSION = 16384
+ };
+
+ virtual ~PlanarYCbCrImage() {}
+
+ /**
+ * This makes a copy of the data buffers, in order to support functioning
+ * in all different layer managers.
+ */
+ virtual bool CopyData(const Data& aData) = 0;
+
+ /**
+ * This doesn't make a copy of the data buffers. Can be used when mBuffer is
+ * pre allocated with AllocateAndGetNewBuffer(size) and then AdoptData is
+ * called to only update the picture size, planes etc. fields in mData.
+ * The GStreamer media backend uses this to decode into PlanarYCbCrImage(s)
+ * directly.
+ */
+ virtual bool AdoptData(const Data &aData);
+
+ /**
+ * This allocates and returns a new buffer
+ */
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) = 0;
+
+ /**
+ * Ask this Image to not convert YUV to RGB during SetData, and make
+ * the original data available through GetData. This is optional,
+ * and not all PlanarYCbCrImages will support it.
+ */
+ virtual void SetDelayedConversion(bool aDelayed) { }
+
+ /**
+ * Grab the original YUV data. This is optional.
+ */
+ virtual const Data* GetData() { return &mData; }
+
+ /**
+ * Return the number of bytes of heap memory used to store this image.
+ */
+ virtual uint32_t GetDataSize() { return mBufferSize; }
+
+ virtual bool IsValid() { return !!mBufferSize; }
+
+ virtual gfx::IntSize GetSize() { return mSize; }
+
+ virtual gfx::IntPoint GetOrigin() { return mOrigin; }
+
+ explicit PlanarYCbCrImage();
+
+ virtual SharedPlanarYCbCrImage *AsSharedPlanarYCbCrImage() { return nullptr; }
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const = 0;
+
+ PlanarYCbCrImage* AsPlanarYCbCrImage() { return this; }
+
+protected:
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
+
+ void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
+ gfxImageFormat GetOffscreenFormat();
+
+ Data mData;
+ gfx::IntPoint mOrigin;
+ gfx::IntSize mSize;
+ gfxImageFormat mOffscreenFormat;
+ nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
+ uint32_t mBufferSize;
+};
+
+class RecyclingPlanarYCbCrImage: public PlanarYCbCrImage {
+public:
+ explicit RecyclingPlanarYCbCrImage(BufferRecycleBin *aRecycleBin) : mRecycleBin(aRecycleBin) {}
+ virtual ~RecyclingPlanarYCbCrImage() override;
+ virtual bool CopyData(const Data& aData) override;
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+protected:
+
+ /**
+ * Return a buffer to store image data in.
+ */
+ mozilla::UniquePtr<uint8_t[]> AllocateBuffer(uint32_t aSize);
+
+ RefPtr<BufferRecycleBin> mRecycleBin;
+ mozilla::UniquePtr<uint8_t[]> mBuffer;
+};
+
+/**
+ * NVImage is used to store YUV420SP_NV12 and YUV420SP_NV21 data natively, which
+ * are not supported by PlanarYCbCrImage. (PlanarYCbCrImage only stores YUV444P,
+ * YUV422P and YUV420P, it converts YUV420SP_NV12 and YUV420SP_NV21 data into
+ * YUV420P in its PlanarYCbCrImage::SetData() method.)
+ *
+ * PlanarYCbCrData is able to express all the YUV family and so we keep use it
+ * in NVImage.
+ */
+class NVImage: public Image {
+ typedef PlanarYCbCrData Data;
+
+public:
+ explicit NVImage();
+ virtual ~NVImage() override;
+
+ // Methods inherited from layers::Image.
+ virtual gfx::IntSize GetSize() override;
+ virtual gfx::IntRect GetPictureRect() override;
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual bool IsValid() override;
+ virtual NVImage* AsNVImage() override;
+
+ // Methods mimic layers::PlanarYCbCrImage.
+ virtual bool SetData(const Data& aData);
+ virtual const Data* GetData() const;
+ virtual uint32_t GetBufferSize() const;
+
+protected:
+
+ /**
+ * Return a buffer to store image data in.
+ */
+ mozilla::UniquePtr<uint8_t> AllocateBuffer(uint32_t aSize);
+
+ mozilla::UniquePtr<uint8_t> mBuffer;
+ uint32_t mBufferSize;
+ gfx::IntSize mSize;
+ Data mData;
+ nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
+};
+
+/**
+ * Currently, the data in a SourceSurfaceImage surface is treated as being in the
+ * device output color space. This class is very simple as all backends
+ * have to know about how to deal with drawing a cairo image.
+ */
+class SourceSurfaceImage final : public Image {
+public:
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
+ {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ void SetTextureFlags(TextureFlags aTextureFlags) { mTextureFlags = aTextureFlags; }
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual gfx::IntSize GetSize() override { return mSize; }
+
+ SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface);
+ explicit SourceSurfaceImage(gfx::SourceSurface* aSourceSurface);
+ ~SourceSurfaceImage();
+
+private:
+ gfx::IntSize mSize;
+ nsCountedRef<nsOwningThreadSourceSurfaceRef> mSourceSurface;
+ nsDataHashtable<nsUint32HashKey, RefPtr<TextureClient> > mTextureClients;
+ TextureFlags mTextureFlags;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ImageDataSerializer.cpp b/system/graphics/layers/ImageDataSerializer.cpp
new file mode 100644
index 000000000..db11e903c
--- /dev/null
+++ b/system/graphics/layers/ImageDataSerializer.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageDataSerializer.h"
+#include "gfx2DGlue.h" // for SurfaceFormatToImageFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc
+#include "mozilla/gfx/Types.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+namespace ImageDataSerializer {
+
+using namespace gfx;
+
+int32_t
+ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth)
+{
+ return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat));
+}
+
+int32_t
+GetRGBStride(const RGBDescriptor& aDescriptor)
+{
+ return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width);
+}
+
+uint32_t
+ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0);
+
+ // This takes care of checking whether there could be overflow
+ // with enough margin for the metadata.
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return 0;
+ }
+
+ // Note we're passing height instad of the bpp parameter, but the end
+ // result is the same - and the bpp was already taken care of in the
+ // ComputeRGBStride function.
+ int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width),
+ aSize.height);
+
+ if (bufsize < 0) {
+ // This should not be possible thanks to Factory::AllowedSurfaceSize
+ return 0;
+ }
+
+ return bufsize;
+}
+
+
+
+// Minimum required shmem size in bytes
+uint32_t
+ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
+ const gfx::IntSize& aCbCrSize, int32_t aCbCrStride)
+{
+ MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
+
+ if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(aCbCrStride, aCbCrSize.height))) {
+ return 0;
+ }
+ // Overflow checks are performed in AllowedSurfaceSize
+ return GetAlignedStride<4>(aYSize.height, aYStride) +
+ 2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
+}
+
+// Minimum required shmem size in bytes
+uint32_t
+ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize)
+{
+ return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
+}
+
+uint32_t
+ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize,
+ uint32_t aYOffset, uint32_t aCbOffset, uint32_t aCrOffset)
+{
+ MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
+
+ int32_t yStride = aYSize.width;
+ int32_t cbCrStride = aCbCrSize.width;
+ if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(yStride, aYSize.height)) ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(cbCrStride, aCbCrSize.height))) {
+ return 0;
+ }
+
+ uint32_t yLength = GetAlignedStride<4>(yStride, aYSize.height);
+ uint32_t cbCrLength = GetAlignedStride<4>(cbCrStride, aCbCrSize.height);
+ if (yLength == 0 || cbCrLength == 0) {
+ return 0;
+ }
+
+ CheckedInt<uint32_t> yEnd = aYOffset;
+ yEnd += yLength;
+ CheckedInt<uint32_t> cbEnd = aCbOffset;
+ cbEnd += cbCrLength;
+ CheckedInt<uint32_t> crEnd = aCrOffset;
+ crEnd += cbCrLength;
+
+ if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
+ yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) {
+ return 0;
+ }
+
+ return crEnd.value();
+}
+
+uint32_t
+ComputeYCbCrBufferSize(uint32_t aBufferSize)
+{
+ return GetAlignedStride<4>(aBufferSize, 1);
+}
+
+void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
+ int32_t cbCrStride, int32_t cbCrHeight,
+ uint32_t& outYOffset, uint32_t& outCbOffset,
+ uint32_t& outCrOffset)
+{
+ outYOffset = 0;
+ outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight);
+ outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight);
+}
+
+gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return aDescriptor.get_RGBDescriptor().format();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return gfx::SurfaceFormat::YUV;
+ default:
+ MOZ_CRASH("GFX: FormatFromBufferDescriptor");
+ }
+}
+
+gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return aDescriptor.get_RGBDescriptor().size();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return aDescriptor.get_YCbCrDescriptor().ySize();
+ default:
+ MOZ_CRASH("GFX: SizeFromBufferDescriptor");
+ }
+}
+
+Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+}
+
+Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+
+uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.yOffset();
+}
+
+uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.cbOffset();
+}
+
+uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.crOffset();
+}
+
+already_AddRefed<DataSourceSurface>
+DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface)
+{
+ gfx::IntSize ySize = aDescriptor.ySize();
+ gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
+ int32_t yStride = ySize.width;
+ int32_t cbCrStride = cbCrSize.width;
+
+ RefPtr<DataSourceSurface> result;
+ if (aSurface) {
+ MOZ_ASSERT(aSurface->GetSize() == ySize);
+ MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+ if (aSurface->GetSize() == ySize &&
+ aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) {
+ result = aSurface;
+ }
+ }
+
+ if (!result) {
+ result =
+ Factory::CreateDataSourceSurface(ySize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
+ return nullptr;
+ }
+
+ layers::PlanarYCbCrData ycbcrData;
+ ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
+ ycbcrData.mYStride = yStride;
+ ycbcrData.mYSize = ySize;
+ ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
+ ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
+ ycbcrData.mCbCrStride = cbCrStride;
+ ycbcrData.mCbCrSize = cbCrSize;
+ ycbcrData.mPicSize = ySize;
+ ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+
+ gfx::ConvertYCbCrToRGB(ycbcrData,
+ gfx::SurfaceFormat::B8G8R8X8,
+ ySize,
+ map.mData,
+ map.mStride);
+
+ result->Unmap();
+ return result.forget();
+}
+
+void
+ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
+ const YCbCrDescriptor& aDescriptor,
+ const gfx::SurfaceFormat& aDestFormat,
+ const gfx::IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride)
+{
+ MOZ_ASSERT(aBuffer);
+ gfx::IntSize ySize = aDescriptor.ySize();
+ gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
+ int32_t yStride = ySize.width;
+ int32_t cbCrStride = cbCrSize.width;
+
+ layers::PlanarYCbCrData ycbcrData;
+ ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
+ ycbcrData.mYStride = yStride;
+ ycbcrData.mYSize = ySize;
+ ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
+ ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
+ ycbcrData.mCbCrStride = cbCrStride;
+ ycbcrData.mCbCrSize = cbCrSize;
+ ycbcrData.mPicSize = ySize;
+ ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+
+ gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride);
+}
+
+} // namespace ImageDataSerializer
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ImageDataSerializer.h b/system/graphics/layers/ImageDataSerializer.h
new file mode 100644
index 000000000..4b3194b0c
--- /dev/null
+++ b/system/graphics/layers/ImageDataSerializer.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_BLOBSURFACE_H
+#define GFX_LAYERS_BLOBSURFACE_H
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class DrawTarget;
+} // namespace gfx
+} // namespace mozilla
+
+namespace mozilla {
+namespace layers {
+
+namespace ImageDataSerializer {
+
+// RGB
+
+int32_t ComputeRGBStride(gfx::SurfaceFormat aFormat, int32_t aWidth);
+
+int32_t GetRGBStride(const RGBDescriptor& aDescriptor);
+
+uint32_t ComputeRGBBufferSize(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+
+// YCbCr
+
+///This function is meant as a helper to know how much shared memory we need
+///to allocate in a shmem in order to place a shared YCbCr image blob of
+///given dimensions.
+uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
+ int32_t aYStride,
+ const gfx::IntSize& aCbCrSize,
+ int32_t aCbCrStride);
+uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
+ const gfx::IntSize& aCbCrSize);
+uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
+ const gfx::IntSize& aCbCrSize,
+ uint32_t aYOffset,
+ uint32_t aCbOffset,
+ uint32_t aCrOffset);
+uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
+
+void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
+ int32_t cbCrStride, int32_t cbCrHeight,
+ uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset);
+
+gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+already_AddRefed<gfx::DataSourceSurface>
+DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface = nullptr);
+
+void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
+ const YCbCrDescriptor& aDescriptor,
+ const gfx::SurfaceFormat& aDestFormat,
+ const gfx::IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride);
+
+} // ImageDataSerializer
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ImageLayers.cpp b/system/graphics/layers/ImageLayers.cpp
new file mode 100644
index 000000000..454ddb921
--- /dev/null
+++ b/system/graphics/layers/ImageLayers.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageLayers.h"
+#include "ImageContainer.h" // for ImageContainer
+#include "gfxRect.h" // for gfxRect
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for ImageContainer::Release, etc
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageLayer::ImageLayer(LayerManager* aManager, void* aImplData)
+: Layer(aManager, aImplData), mSamplingFilter(gfx::SamplingFilter::GOOD)
+, mScaleMode(ScaleMode::SCALE_NONE)
+{}
+
+ImageLayer::~ImageLayer()
+{}
+
+void ImageLayer::SetContainer(ImageContainer* aContainer)
+{
+ mContainer = aContainer;
+}
+
+void ImageLayer::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+{
+ gfx::Matrix4x4 local = GetLocalTransform();
+
+ // Snap image edges to pixel boundaries
+ gfxRect sourceRect(0, 0, 0, 0);
+ if (mContainer) {
+ sourceRect.SizeTo(mContainer->GetCurrentSize());
+ }
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+
+ if (mScaleMode != ScaleMode::SCALE_NONE &&
+ sourceRect.width != 0.0 && sourceRect.height != 0.0) {
+ NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+ "No other scalemodes than stretch and none supported yet.");
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
+
+ mEffectiveTransformForBuffer =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ } else {
+ mEffectiveTransformForBuffer = mEffectiveTransform;
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ImageLayers.h b/system/graphics/layers/ImageLayers.h
new file mode 100644
index 000000000..9c5d4f4f0
--- /dev/null
+++ b/system/graphics/layers/ImageLayers.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGELAYER_H
+#define GFX_IMAGELAYER_H
+
+#include "Layers.h" // for Layer, etc
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/LayersTypes.h"
+#include "nsAutoPtr.h" // for nsRefPtr
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class ImageContainer;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+/**
+ * A Layer which renders an Image.
+ */
+class ImageLayer : public Layer {
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the ImageContainer. aContainer must have the same layer manager
+ * as this layer.
+ */
+ virtual void SetContainer(ImageContainer* aContainer);
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the filter used to resample this image if necessary.
+ */
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mSamplingFilter != aSamplingFilter) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this));
+ mSamplingFilter = aSamplingFilter;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the size to scale the image to and the mode at which to scale.
+ */
+ void SetScaleToSize(const gfx::IntSize &aSize, ScaleMode aMode)
+ {
+ if (mScaleToSize != aSize || mScaleMode != aMode) {
+ mScaleToSize = aSize;
+ mScaleMode = aMode;
+ Mutated();
+ }
+ }
+
+
+ ImageContainer* GetContainer() { return mContainer; }
+ gfx::SamplingFilter GetSamplingFilter() { return mSamplingFilter; }
+ const gfx::IntSize& GetScaleToSize() { return mScaleToSize; }
+ ScaleMode GetScaleMode() { return mScaleMode; }
+
+ MOZ_LAYER_DECL_NAME("ImageLayer", TYPE_IMAGE)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
+
+ virtual const gfx::Matrix4x4& GetEffectiveTransformForBuffer() const override
+ {
+ return mEffectiveTransformForBuffer;
+ }
+
+protected:
+ ImageLayer(LayerManager* aManager, void* aImplData);
+ ~ImageLayer();
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ RefPtr<ImageContainer> mContainer;
+ gfx::SamplingFilter mSamplingFilter;
+ gfx::IntSize mScaleToSize;
+ ScaleMode mScaleMode;
+ gfx::Matrix4x4 mEffectiveTransformForBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_IMAGELAYER_H */
diff --git a/system/graphics/layers/ImageTypes.h b/system/graphics/layers/ImageTypes.h
new file mode 100644
index 000000000..7a3b81f6e
--- /dev/null
+++ b/system/graphics/layers/ImageTypes.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGETYPES_H
+#define GFX_IMAGETYPES_H
+
+namespace mozilla {
+
+enum class ImageFormat {
+ /**
+ * The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
+ * support this format, because the Ogg video decoder depends on it.
+ * The maximum image width and height is 16384.
+ */
+ PLANAR_YCBCR,
+
+ /**
+ * The NV_IMAGE format creates a NVImage. The PLANAR_YCBCR together with this
+ * complete the YUV format family.
+ */
+ NV_IMAGE,
+
+ /**
+ * The GRALLOC_PLANAR_YCBCR format creates a GrallocImage, a subtype of
+ * PlanarYCbCrImage. It takes a PlanarYCbCrImage data or the raw gralloc
+ * data and can be used as a texture by Gonk backend directly.
+ */
+ GRALLOC_PLANAR_YCBCR,
+
+ /**
+ * The GONK_CAMERA_IMAGE format creates a GonkCameraImage, which contains two
+ * parts. One is GrallocImage image for preview image. Another one is
+ * MediaBuffer from Gonk recording image. The preview image can be rendered in
+ * a layer for display. And the MediaBuffer will be used in component like OMX
+ * encoder. It is for GUM to support preview and recording image on Gonk
+ * camera.
+ */
+ GONK_CAMERA_IMAGE,
+
+ /**
+ * The SHARED_RGB format creates a SharedRGBImage, which stores RGB data in
+ * shared memory. Some Android hardware video decoders require this format.
+ * Currently only used on Android.
+ */
+ SHARED_RGB,
+
+ /**
+ * The CAIRO_SURFACE format creates a SourceSurfaceImage. All backends should
+ * support this format, because video rendering sometimes requires it.
+ *
+ * This format is useful even though a PaintedLayer could be used.
+ * It makes it easy to render a cairo surface when another Image format
+ * could be used. It can also avoid copying the surface data in some
+ * cases.
+ *
+ * Images in CAIRO_SURFACE format should only be created and
+ * manipulated on the main thread, since the underlying cairo surface
+ * is main-thread-only.
+ */
+ CAIRO_SURFACE,
+
+ /**
+ * A MacIOSurface object.
+ */
+ MAC_IOSURFACE,
+
+ /**
+ * An Android SurfaceTexture ID that can be shared across threads and
+ * processes.
+ */
+ SURFACE_TEXTURE,
+
+ /**
+ * An EGL Image that can be shared across threads.
+ */
+ EGLIMAGE,
+
+ /**
+ * The D3D9_RGB32_TEXTURE format creates a D3D9SurfaceImage, and wraps a
+ * IDirect3DTexture9 in RGB32 layout.
+ */
+ D3D9_RGB32_TEXTURE,
+
+ /**
+ * An Image type carries an opaque handle once for each stream.
+ * The opaque handle would be a platform specific identifier.
+ */
+ OVERLAY_IMAGE,
+
+ /**
+ * A share handle to a ID3D11Texture2D.
+ */
+ D3D11_SHARE_HANDLE_TEXTURE,
+
+ /**
+ * A wrapper around a drawable TextureClient.
+ */
+ TEXTURE_WRAPPER,
+
+ /**
+ * An opaque handle that refers to an Image stored in the GPU
+ * process.
+ */
+ GPU_VIDEO
+};
+
+enum class StereoMode {
+ MONO,
+ LEFT_RIGHT,
+ RIGHT_LEFT,
+ BOTTOM_TOP,
+ TOP_BOTTOM,
+ MAX,
+};
+
+enum class YUVColorSpace {
+ BT601,
+ BT709,
+ // This represents the unknown format.
+ UNKNOWN,
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/LayerMetricsWrapper.h b/system/graphics/layers/LayerMetricsWrapper.h
new file mode 100644
index 000000000..d26915633
--- /dev/null
+++ b/system/graphics/layers/LayerMetricsWrapper.h
@@ -0,0 +1,516 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERMETRICSWRAPPER_H
+#define GFX_LAYERMETRICSWRAPPER_H
+
+#include "Layers.h"
+#include "UnitTransforms.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A wrapper class around a target Layer with that allows user code to
+ * walk through the FrameMetrics objects on the layer the same way it
+ * would walk through a ContainerLayer hierarchy. Consider the following
+ * layer tree:
+ *
+ * +---+
+ * | A |
+ * +---+
+ * / | \
+ * / | \
+ * / | \
+ * +---+ +-----+ +---+
+ * | B | | C | | D |
+ * +---+ +-----+ +---+
+ * | FMn |
+ * | . |
+ * | . |
+ * | . |
+ * | FM1 |
+ * | FM0 |
+ * +-----+
+ * / \
+ * / \
+ * +---+ +---+
+ * | E | | F |
+ * +---+ +---+
+ *
+ * In this layer tree, there are six layers with A being the root and B,D,E,F
+ * being leaf nodes. Layer C is in the middle and has n+1 FrameMetrics, labelled
+ * FM0...FMn. FM0 is the FrameMetrics you get by calling c->GetFrameMetrics(0)
+ * and FMn is the FrameMetrics you can obtain by calling
+ * c->GetFrameMetrics(c->GetScrollMetadataCount() - 1). This layer tree is
+ * conceptually equivalent to this one below:
+ *
+ * +---+
+ * | A |
+ * +---+
+ * / | \
+ * / | \
+ * / | \
+ * +---+ +-----+ +---+
+ * | B | | Cn | | D |
+ * +---+ +-----+ +---+
+ * |
+ * .
+ * .
+ * .
+ * |
+ * +-----+
+ * | C1 |
+ * +-----+
+ * |
+ * +-----+
+ * | C0 |
+ * +-----+
+ * / \
+ * / \
+ * +---+ +---+
+ * | E | | F |
+ * +---+ +---+
+ *
+ * In this layer tree, the layer C has been expanded into a stack of container
+ * layers C1...Cn, where C1 has FrameMetrics FM1 and Cn has FrameMetrics Fn.
+ * Although in this example C (in the first layer tree) and C0 (in the second
+ * layer tree) are both ContainerLayers (because they have children), they
+ * do not have to be. They may just be PaintedLayers or ColorLayers, for example,
+ * which do not have any children. However, the type of C will always be the
+ * same as the type of C0.
+ *
+ * The LayerMetricsWrapper class allows client code to treat the first layer
+ * tree as though it were the second. That is, instead of client code having
+ * to iterate through the FrameMetrics objects directly, it can use a
+ * LayerMetricsWrapper to encapsulate that aspect of the layer tree and just
+ * walk the tree as if it were a stack of ContainerLayers.
+ *
+ * The functions on this class do different things depending on which
+ * simulated ContainerLayer is being wrapped. For example, if the
+ * LayerMetricsWrapper is pretending to be C0, the GetNextSibling() function
+ * will return null even though the underlying layer C does actually have
+ * a next sibling. The LayerMetricsWrapper pretending to be Cn will return
+ * D as the next sibling.
+ *
+ * Implementation notes:
+ *
+ * The AtTopLayer() and AtBottomLayer() functions in this class refer to
+ * Cn and C0 in the second layer tree above; that is, they are predicates
+ * to test if the LayerMetricsWrapper is simulating the topmost or bottommost
+ * layer, as those will have special behaviour.
+ *
+ * It is possible to wrap a nullptr in a LayerMetricsWrapper, in which case
+ * the IsValid() function will return false. This is required to allow
+ * LayerMetricsWrapper to be a MOZ_STACK_CLASS (desirable because it is used
+ * in loops and recursion).
+ *
+ * This class purposely does not expose the wrapped layer directly to avoid
+ * user code from accidentally calling functions directly on it. Instead
+ * any necessary functions should be wrapped in this class. It does expose
+ * the wrapped layer as a void* for printf purposes.
+ *
+ * The implementation may look like it special-cases mIndex == 0 and/or
+ * GetScrollMetadataCount() == 0. This is an artifact of the fact that both
+ * mIndex and GetScrollMetadataCount() are uint32_t and GetScrollMetadataCount()
+ * can return 0 but mIndex cannot store -1. This seems better than the
+ * alternative of making mIndex a int32_t that can store -1, but then having
+ * to cast to uint32_t all over the place.
+ */
+class MOZ_STACK_CLASS LayerMetricsWrapper {
+public:
+ enum StartAt {
+ TOP,
+ BOTTOM,
+ };
+
+ LayerMetricsWrapper()
+ : mLayer(nullptr)
+ , mIndex(0)
+ {
+ }
+
+ explicit LayerMetricsWrapper(Layer* aRoot, StartAt aStart = StartAt::TOP)
+ : mLayer(aRoot)
+ , mIndex(0)
+ {
+ if (!mLayer) {
+ return;
+ }
+
+ switch (aStart) {
+ case StartAt::TOP:
+ mIndex = mLayer->GetScrollMetadataCount();
+ if (mIndex > 0) {
+ mIndex--;
+ }
+ break;
+ case StartAt::BOTTOM:
+ mIndex = 0;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown startAt value");
+ break;
+ }
+ }
+
+ explicit LayerMetricsWrapper(Layer* aLayer, uint32_t aMetricsIndex)
+ : mLayer(aLayer)
+ , mIndex(aMetricsIndex)
+ {
+ MOZ_ASSERT(mLayer);
+ MOZ_ASSERT(mIndex == 0 || mIndex < mLayer->GetScrollMetadataCount());
+ }
+
+ bool IsValid() const
+ {
+ return mLayer != nullptr;
+ }
+
+ explicit operator bool() const
+ {
+ return IsValid();
+ }
+
+ bool IsScrollInfoLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ // If we are not at the bottommost layer then it's
+ // a stack of container layers all the way down to
+ // mLayer, which we can ignore. We only care about
+ // non-container descendants.
+ return Metrics().IsScrollable()
+ && mLayer->AsContainerLayer()
+ && !mLayer->GetFirstChild();
+ }
+
+ LayerMetricsWrapper GetParent() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex + 1);
+ }
+ if (mLayer->GetParent()) {
+ return LayerMetricsWrapper(mLayer->GetParent(), StartAt::BOTTOM);
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ LayerMetricsWrapper GetFirstChild() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtBottomLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex - 1);
+ }
+ return LayerMetricsWrapper(mLayer->GetFirstChild());
+ }
+
+ LayerMetricsWrapper GetLastChild() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtBottomLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex - 1);
+ }
+ return LayerMetricsWrapper(mLayer->GetLastChild());
+ }
+
+ LayerMetricsWrapper GetPrevSibling() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer->GetPrevSibling());
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ LayerMetricsWrapper GetNextSibling() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer->GetNextSibling());
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ const ScrollMetadata& Metadata() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mIndex >= mLayer->GetScrollMetadataCount()) {
+ return *ScrollMetadata::sNullMetadata;
+ }
+ return mLayer->GetScrollMetadata(mIndex);
+ }
+
+ const FrameMetrics& Metrics() const
+ {
+ return Metadata().GetMetrics();
+ }
+
+ AsyncPanZoomController* GetApzc() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mIndex >= mLayer->GetScrollMetadataCount()) {
+ return nullptr;
+ }
+ return mLayer->GetAsyncPanZoomController(mIndex);
+ }
+
+ void SetApzc(AsyncPanZoomController* aApzc) const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mLayer->GetScrollMetadataCount() == 0) {
+ MOZ_ASSERT(mIndex == 0);
+ MOZ_ASSERT(aApzc == nullptr);
+ return;
+ }
+ MOZ_ASSERT(mIndex < mLayer->GetScrollMetadataCount());
+ mLayer->SetAsyncPanZoomController(mIndex, aApzc);
+ }
+
+ const char* Name() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->Name();
+ }
+ return "DummyContainerLayer";
+ }
+
+ LayerManager* Manager() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->Manager();
+ }
+
+ gfx::Matrix4x4 GetTransform() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetTransform();
+ }
+ return gfx::Matrix4x4();
+ }
+
+ CSSTransformMatrix GetTransformTyped() const
+ {
+ return ViewAs<CSSTransformMatrix>(GetTransform());
+ }
+
+ bool TransformIsPerspective() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ // mLayer->GetTransformIsPerspective() tells us whether
+ // mLayer->GetTransform() is a perspective transform. Since
+ // mLayer->GetTransform() is only used at the bottom layer, we only
+ // need to check GetTransformIsPerspective() at the bottom layer too.
+ if (AtBottomLayer()) {
+ return mLayer->GetTransformIsPerspective();
+ }
+ return false;
+ }
+
+ EventRegions GetEventRegions() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetEventRegions();
+ }
+ return EventRegions();
+ }
+
+ bool HasTransformAnimation() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->HasTransformAnimation();
+ }
+ return false;
+ }
+
+ RefLayer* AsRefLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->AsRefLayer();
+ }
+ return nullptr;
+ }
+
+ LayerIntRegion GetVisibleRegion() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetVisibleRegion();
+ }
+ LayerIntRegion region = mLayer->GetVisibleRegion();
+ region.Transform(mLayer->GetTransform());
+ return region;
+ }
+
+ Maybe<ParentLayerIntRect> GetClipRect() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ Maybe<ParentLayerIntRect> result;
+
+ // The layer can have a clip rect and a scrolled clip, which are considered
+ // to apply only to the bottommost LayerMetricsWrapper.
+ // TODO: These actually apply in a different coordinate space than the
+ // scroll clip of the bottommost metrics, so we shouldn't be intersecting
+ // them with the scroll clip; bug 1269537 tracks fixing this.
+ if (AtBottomLayer()) {
+ result = mLayer->GetClipRect();
+ result = IntersectMaybeRects(result, mLayer->GetScrolledClipRect());
+ }
+
+ // The scroll metadata can have a clip rect as well.
+ result = IntersectMaybeRects(result, Metadata().GetClipRect());
+
+ return result;
+ }
+
+ float GetPresShellResolution() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer() && mLayer->AsContainerLayer()) {
+ return mLayer->AsContainerLayer()->GetPresShellResolution();
+ }
+
+ return 1.0f;
+ }
+
+ EventRegionsOverride GetEventRegionsOverride() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mLayer->AsContainerLayer()) {
+ return mLayer->AsContainerLayer()->GetEventRegionsOverride();
+ }
+ return EventRegionsOverride::NoOverride;
+ }
+
+ Layer::ScrollDirection GetScrollbarDirection() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetScrollbarDirection();
+ }
+
+ FrameMetrics::ViewID GetScrollbarTargetContainerId() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetScrollbarTargetContainerId();
+ }
+
+ int32_t GetScrollbarSize() const
+ {
+ if (GetScrollbarDirection() == Layer::VERTICAL) {
+ return mLayer->GetVisibleRegion().GetBounds().height;
+ } else {
+ return mLayer->GetVisibleRegion().GetBounds().width;
+ }
+ }
+
+ bool IsScrollbarContainer() const
+ {
+ MOZ_ASSERT(IsValid());
+ return mLayer->IsScrollbarContainer();
+ }
+
+ FrameMetrics::ViewID GetFixedPositionScrollContainerId() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetFixedPositionScrollContainerId();
+ }
+
+ // Expose an opaque pointer to the layer. Mostly used for printf
+ // purposes. This is not intended to be a general-purpose accessor
+ // for the underlying layer.
+ const void* GetLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return (void*)mLayer;
+ }
+
+ bool operator==(const LayerMetricsWrapper& aOther) const
+ {
+ return mLayer == aOther.mLayer
+ && mIndex == aOther.mIndex;
+ }
+
+ bool operator!=(const LayerMetricsWrapper& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ static const FrameMetrics& TopmostScrollableMetrics(Layer* aLayer)
+ {
+ for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) {
+ if (aLayer->GetFrameMetrics(i - 1).IsScrollable()) {
+ return aLayer->GetFrameMetrics(i - 1);
+ }
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+ static const FrameMetrics& BottommostScrollableMetrics(Layer* aLayer)
+ {
+ for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
+ if (aLayer->GetFrameMetrics(i).IsScrollable()) {
+ return aLayer->GetFrameMetrics(i);
+ }
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+ static const FrameMetrics& BottommostMetrics(Layer* aLayer)
+ {
+ if (aLayer->GetScrollMetadataCount() > 0) {
+ return aLayer->GetFrameMetrics(0);
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+private:
+ bool AtBottomLayer() const
+ {
+ return mIndex == 0;
+ }
+
+ bool AtTopLayer() const
+ {
+ return mLayer->GetScrollMetadataCount() == 0 || mIndex == mLayer->GetScrollMetadataCount() - 1;
+ }
+
+private:
+ Layer* mLayer;
+ uint32_t mIndex;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERMETRICSWRAPPER_H */
diff --git a/system/graphics/layers/LayerScope.cpp b/system/graphics/layers/LayerScope.cpp
new file mode 100644
index 000000000..a3b7778ef
--- /dev/null
+++ b/system/graphics/layers/LayerScope.cpp
@@ -0,0 +1,1833 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
+#include "LayerScope.h"
+
+#include "nsAppRunner.h"
+#include "Composer2D.h"
+#include "Effects.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+
+#include "TexturePoolOGL.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+#include "gfxContext.h"
+#include "gfxUtils.h"
+#include "gfxPrefs.h"
+#include "nsIWidget.h"
+
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "GLReadTexImageHelper.h"
+
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+
+#include <memory>
+#include "mozilla/LinkedList.h"
+#include "mozilla/Base64.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/StaticPtr.h"
+#include "nsThreadUtils.h"
+#include "nsISocketTransport.h"
+#include "nsIServerSocket.h"
+#include "nsReadLine.h"
+#include "nsNetCID.h"
+#include "nsIOutputStream.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIEventTarget.h"
+#include "nsProxyRelease.h"
+#include <list>
+
+// Undo the damage done by mozzconf.h
+#undef compress
+#include "mozilla/Compression.h"
+
+// Protocol buffer (generated automatically)
+#include "protobuf/LayerScopePacket.pb.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::Compression;
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+using namespace mozilla;
+using namespace layerscope;
+
+class DebugDataSender;
+class DebugGLData;
+
+/*
+ * Manage Websocket connections
+ */
+class LayerScopeWebSocketManager {
+public:
+ LayerScopeWebSocketManager();
+ ~LayerScopeWebSocketManager();
+
+ void RemoveAllConnections()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MutexAutoLock lock(mHandlerMutex);
+ mHandlers.Clear();
+ }
+
+ bool WriteAll(void *ptr, uint32_t size)
+ {
+ for (int32_t i = mHandlers.Length() - 1; i >= 0; --i) {
+ if (!mHandlers[i]->WriteToStream(ptr, size)) {
+ // Send failed, remove this handler
+ RemoveConnection(i);
+ }
+ }
+
+ return true;
+ }
+
+ bool IsConnected()
+ {
+ // This funtion can be called in both main thread and compositor thread.
+ MutexAutoLock lock(mHandlerMutex);
+ return (mHandlers.Length() != 0) ? true : false;
+ }
+
+ void AppendDebugData(DebugGLData *aDebugData);
+ void CleanDebugData();
+ void DispatchDebugData();
+
+private:
+ void AddConnection(nsISocketTransport *aTransport)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aTransport);
+
+ MutexAutoLock lock(mHandlerMutex);
+
+ RefPtr<SocketHandler> temp = new SocketHandler();
+ temp->OpenStream(aTransport);
+ mHandlers.AppendElement(temp.get());
+ }
+
+ void RemoveConnection(uint32_t aIndex)
+ {
+ // TBD: RemoveConnection is executed on the compositor thread and
+ // AddConntection is executed on the main thread, which might be
+ // a problem if a user disconnect and connect readlly quickly at
+ // viewer side.
+
+ // We should dispatch RemoveConnection onto main thead.
+ MOZ_ASSERT(aIndex < mHandlers.Length());
+
+ MutexAutoLock lock(mHandlerMutex);
+ mHandlers.RemoveElementAt(aIndex);
+ }
+
+ friend class SocketListener;
+ class SocketListener : public nsIServerSocketListener
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ SocketListener() { }
+
+ /* nsIServerSocketListener */
+ NS_IMETHOD OnSocketAccepted(nsIServerSocket *aServ,
+ nsISocketTransport *aTransport) override;
+ NS_IMETHOD OnStopListening(nsIServerSocket *aServ,
+ nsresult aStatus) override
+ {
+ return NS_OK;
+ }
+ private:
+ virtual ~SocketListener() { }
+ };
+
+ /*
+ * This class handle websocket protocol which included
+ * handshake and data frame's header
+ */
+ class SocketHandler : public nsIInputStreamCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ SocketHandler()
+ : mState(NoHandshake)
+ , mConnected(false)
+ { }
+
+ void OpenStream(nsISocketTransport* aTransport);
+ bool WriteToStream(void *aPtr, uint32_t aSize);
+
+ // nsIInputStreamCallback
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *aStream) override;
+
+ private:
+ virtual ~SocketHandler() { CloseConnection(); }
+
+ void ReadInputStreamData(nsTArray<nsCString>& aProtocolString);
+ bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString);
+ void ApplyMask(uint32_t aMask, uint8_t *aData, uint64_t aLen);
+ bool HandleDataFrame(uint8_t *aData, uint32_t aSize);
+ void CloseConnection();
+
+ nsresult HandleSocketMessage(nsIAsyncInputStream *aStream);
+ nsresult ProcessInput(uint8_t *aBuffer, uint32_t aCount);
+
+ private:
+ enum SocketStateType {
+ NoHandshake,
+ HandshakeSuccess,
+ HandshakeFailed
+ };
+ SocketStateType mState;
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIAsyncInputStream> mInputStream;
+ nsCOMPtr<nsISocketTransport> mTransport;
+ bool mConnected;
+ };
+
+ nsTArray<RefPtr<SocketHandler> > mHandlers;
+ nsCOMPtr<nsIThread> mDebugSenderThread;
+ RefPtr<DebugDataSender> mCurrentSender;
+ nsCOMPtr<nsIServerSocket> mServerSocket;
+
+ // Keep mHandlers accessing thread safe.
+ Mutex mHandlerMutex;
+};
+
+NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketListener,
+ nsIServerSocketListener);
+NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketHandler,
+ nsIInputStreamCallback);
+
+class DrawSession {
+public:
+ DrawSession()
+ : mOffsetX(0.0)
+ , mOffsetY(0.0)
+ , mRects(0)
+ { }
+
+ float mOffsetX;
+ float mOffsetY;
+ gfx::Matrix4x4 mMVMatrix;
+ size_t mRects;
+ gfx::Rect mLayerRects[4];
+ gfx::Rect mTextureRects[4];
+ std::list<GLuint> mTexIDs;
+};
+
+class ContentMonitor {
+public:
+ using THArray = nsTArray<const TextureHost *>;
+
+ // Notify the content of a TextureHost was changed.
+ void SetChangedHost(const TextureHost* host) {
+ if (THArray::NoIndex == mChangedHosts.IndexOf(host)) {
+ mChangedHosts.AppendElement(host);
+ }
+ }
+
+ // Clear changed flag of a host.
+ void ClearChangedHost(const TextureHost* host) {
+ if (THArray::NoIndex != mChangedHosts.IndexOf(host)) {
+ mChangedHosts.RemoveElement(host);
+ }
+ }
+
+ // Return true iff host is a new one or the content of it had been changed.
+ bool IsChangedOrNew(const TextureHost* host) {
+ if (THArray::NoIndex == mSeenHosts.IndexOf(host)) {
+ mSeenHosts.AppendElement(host);
+ return true;
+ }
+
+ if (decltype(mChangedHosts)::NoIndex != mChangedHosts.IndexOf(host)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void Empty() {
+ mSeenHosts.SetLength(0);
+ mChangedHosts.SetLength(0);
+ }
+private:
+ THArray mSeenHosts;
+ THArray mChangedHosts;
+};
+
+/*
+ * Hold all singleton objects used by LayerScope.
+ */
+class LayerScopeManager
+{
+public:
+ void CreateServerSocket()
+ {
+ // WebSocketManager must be created on the main thread.
+ if (NS_IsMainThread()) {
+ mWebSocketManager = mozilla::MakeUnique<LayerScopeWebSocketManager>();
+ } else {
+ // Dispatch creation to main thread, and make sure we
+ // dispatch this only once after booting
+ static bool dispatched = false;
+ if (dispatched) {
+ return;
+ }
+
+ DebugOnly<nsresult> rv =
+ NS_DispatchToMainThread(new CreateServerSocketRunnable(this));
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to dispatch WebSocket Creation to main thread");
+ dispatched = true;
+ }
+ }
+
+ void DestroyServerSocket()
+ {
+ // Destroy Web Server Socket
+ if (mWebSocketManager) {
+ mWebSocketManager->RemoveAllConnections();
+ }
+ }
+
+ LayerScopeWebSocketManager* GetSocketManager()
+ {
+ return mWebSocketManager.get();
+ }
+
+ ContentMonitor* GetContentMonitor()
+ {
+ if (!mContentMonitor.get()) {
+ mContentMonitor = mozilla::MakeUnique<ContentMonitor>();
+ }
+
+ return mContentMonitor.get();
+ }
+
+ void NewDrawSession() {
+ mSession = mozilla::MakeUnique<DrawSession>();
+ }
+
+ DrawSession& CurrentSession() {
+ return *mSession;
+ }
+
+ void SetPixelScale(double scale) {
+ mScale = scale;
+ }
+
+ double GetPixelScale() const {
+ return mScale;
+ }
+
+ LayerScopeManager()
+ : mScale(1.0)
+ {
+ }
+private:
+ friend class CreateServerSocketRunnable;
+ class CreateServerSocketRunnable : public Runnable
+ {
+ public:
+ explicit CreateServerSocketRunnable(LayerScopeManager *aLayerScopeManager)
+ : mLayerScopeManager(aLayerScopeManager)
+ {
+ }
+ NS_IMETHOD Run() override {
+ mLayerScopeManager->mWebSocketManager =
+ mozilla::MakeUnique<LayerScopeWebSocketManager>();
+ return NS_OK;
+ }
+ private:
+ LayerScopeManager* mLayerScopeManager;
+ };
+
+ mozilla::UniquePtr<LayerScopeWebSocketManager> mWebSocketManager;
+ mozilla::UniquePtr<DrawSession> mSession;
+ mozilla::UniquePtr<ContentMonitor> mContentMonitor;
+ double mScale;
+};
+
+LayerScopeManager gLayerScopeManager;
+
+/*
+ * The static helper functions that set data into the packet
+ * 1. DumpRect
+ * 2. DumpFilter
+ */
+template<typename T>
+static void DumpRect(T* aPacketRect, const Rect& aRect)
+{
+ aPacketRect->set_x(aRect.x);
+ aPacketRect->set_y(aRect.y);
+ aPacketRect->set_w(aRect.width);
+ aPacketRect->set_h(aRect.height);
+}
+
+static void DumpFilter(TexturePacket* aTexturePacket,
+ const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ aTexturePacket->set_mfilter(TexturePacket::GOOD);
+ break;
+ case SamplingFilter::LINEAR:
+ aTexturePacket->set_mfilter(TexturePacket::LINEAR);
+ break;
+ case SamplingFilter::POINT:
+ aTexturePacket->set_mfilter(TexturePacket::POINT);
+ break;
+ default:
+ MOZ_ASSERT(false, "Can't dump unexpected mSamplingFilter to texture packet!");
+ break;
+ }
+}
+
+/*
+ * DebugGLData is the base class of
+ * 1. DebugGLFrameStatusData (Frame start/end packet)
+ * 2. DebugGLColorData (Color data packet)
+ * 3. DebugGLTextureData (Texture data packet)
+ * 4. DebugGLLayersData (Layers Tree data packet)
+ * 5. DebugGLMetaData (Meta data packet)
+ */
+class DebugGLData: public LinkedListElement<DebugGLData> {
+public:
+ explicit DebugGLData(Packet::DataType aDataType)
+ : mDataType(aDataType)
+ { }
+
+ virtual ~DebugGLData() { }
+
+ virtual bool Write() = 0;
+
+protected:
+ static bool WriteToStream(Packet& aPacket) {
+ if (!gLayerScopeManager.GetSocketManager())
+ return true;
+
+ uint32_t size = aPacket.ByteSize();
+ auto data = MakeUnique<uint8_t[]>(size);
+ aPacket.SerializeToArray(data.get(), size);
+ return gLayerScopeManager.GetSocketManager()->WriteAll(data.get(), size);
+ }
+
+ Packet::DataType mDataType;
+};
+
+class DebugGLFrameStatusData final: public DebugGLData
+{
+public:
+ DebugGLFrameStatusData(Packet::DataType aDataType,
+ int64_t aValue)
+ : DebugGLData(aDataType),
+ mFrameStamp(aValue)
+ { }
+
+ explicit DebugGLFrameStatusData(Packet::DataType aDataType)
+ : DebugGLData(aDataType),
+ mFrameStamp(0)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ FramePacket* fp = packet.mutable_frame();
+ fp->set_value(static_cast<uint64_t>(mFrameStamp));
+
+ fp->set_scale(gLayerScopeManager.GetPixelScale());
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ int64_t mFrameStamp;
+};
+
+class DebugGLTextureData final: public DebugGLData {
+public:
+ DebugGLTextureData(GLContext* cx,
+ void* layerRef,
+ GLenum target,
+ GLuint name,
+ DataSourceSurface* img,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket)
+ : DebugGLData(Packet::TEXTURE),
+ mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
+ mTarget(target),
+ mName(name),
+ mContextAddress(reinterpret_cast<intptr_t>(cx)),
+ mDatasize(0),
+ mIsMask(aIsMask),
+ mPacket(Move(aPacket))
+ {
+ // pre-packing
+ // DataSourceSurface may have locked buffer,
+ // so we should compress now, and then it could
+ // be unlocked outside.
+ pack(img);
+ }
+
+ virtual bool Write() override {
+ return WriteToStream(*mPacket);
+ }
+
+private:
+ void pack(DataSourceSurface* aImage) {
+ mPacket->set_type(mDataType);
+
+ TexturePacket* tp = mPacket->mutable_texture();
+ tp->set_layerref(mLayerRef);
+ tp->set_name(mName);
+ tp->set_target(mTarget);
+ tp->set_dataformat(LOCAL_GL_RGBA);
+ tp->set_glcontext(static_cast<uint64_t>(mContextAddress));
+ tp->set_ismask(mIsMask);
+
+ if (aImage) {
+ tp->set_width(aImage->GetSize().width);
+ tp->set_height(aImage->GetSize().height);
+ tp->set_stride(aImage->Stride());
+
+ mDatasize = aImage->GetSize().height * aImage->Stride();
+
+ auto compresseddata = MakeUnique<char[]>(LZ4::maxCompressedSize(mDatasize));
+ if (compresseddata) {
+ int ndatasize = LZ4::compress((char*)aImage->GetData(),
+ mDatasize,
+ compresseddata.get());
+ if (ndatasize > 0) {
+ mDatasize = ndatasize;
+ tp->set_dataformat((1 << 16 | tp->dataformat()));
+ tp->set_data(compresseddata.get(), mDatasize);
+ } else {
+ NS_WARNING("Compress data failed");
+ tp->set_data(aImage->GetData(), mDatasize);
+ }
+ } else {
+ NS_WARNING("Couldn't new compressed data.");
+ tp->set_data(aImage->GetData(), mDatasize);
+ }
+ } else {
+ tp->set_width(0);
+ tp->set_height(0);
+ tp->set_stride(0);
+ }
+ }
+
+protected:
+ uint64_t mLayerRef;
+ GLenum mTarget;
+ GLuint mName;
+ intptr_t mContextAddress;
+ uint32_t mDatasize;
+ bool mIsMask;
+
+ // Packet data
+ UniquePtr<Packet> mPacket;
+};
+
+class DebugGLColorData final: public DebugGLData {
+public:
+ DebugGLColorData(void* layerRef,
+ const Color& color,
+ int width,
+ int height)
+ : DebugGLData(Packet::COLOR),
+ mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
+ mColor(color.ToABGR()),
+ mSize(width, height)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ ColorPacket* cp = packet.mutable_color();
+ cp->set_layerref(mLayerRef);
+ cp->set_color(mColor);
+ cp->set_width(mSize.width);
+ cp->set_height(mSize.height);
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ uint64_t mLayerRef;
+ uint32_t mColor;
+ IntSize mSize;
+};
+
+class DebugGLLayersData final: public DebugGLData {
+public:
+ explicit DebugGLLayersData(UniquePtr<Packet> aPacket)
+ : DebugGLData(Packet::LAYERS),
+ mPacket(Move(aPacket))
+ { }
+
+ virtual bool Write() override {
+ mPacket->set_type(mDataType);
+ return WriteToStream(*mPacket);
+ }
+
+protected:
+ UniquePtr<Packet> mPacket;
+};
+
+class DebugGLMetaData final: public DebugGLData
+{
+public:
+ DebugGLMetaData(Packet::DataType aDataType,
+ bool aValue)
+ : DebugGLData(aDataType),
+ mComposedByHwc(aValue)
+ { }
+
+ explicit DebugGLMetaData(Packet::DataType aDataType)
+ : DebugGLData(aDataType),
+ mComposedByHwc(false)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ MetaPacket* mp = packet.mutable_meta();
+ mp->set_composedbyhwc(mComposedByHwc);
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ bool mComposedByHwc;
+};
+
+class DebugGLDrawData final: public DebugGLData {
+public:
+ DebugGLDrawData(float aOffsetX,
+ float aOffsetY,
+ const gfx::Matrix4x4& aMVMatrix,
+ size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects,
+ const std::list<GLuint> aTexIDs,
+ void* aLayerRef)
+ : DebugGLData(Packet::DRAW),
+ mOffsetX(aOffsetX),
+ mOffsetY(aOffsetY),
+ mMVMatrix(aMVMatrix),
+ mRects(aRects),
+ mTexIDs(aTexIDs),
+ mLayerRef(reinterpret_cast<uint64_t>(aLayerRef))
+ {
+ for (size_t i = 0; i < mRects; i++){
+ mLayerRects[i] = aLayerRects[i];
+ mTextureRects[i] = aTextureRects[i];
+ }
+ }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ DrawPacket* dp = packet.mutable_draw();
+ dp->set_layerref(mLayerRef);
+
+ dp->set_offsetx(mOffsetX);
+ dp->set_offsety(mOffsetY);
+
+ auto element = reinterpret_cast<Float *>(&mMVMatrix);
+ for (int i = 0; i < 16; i++) {
+ dp->add_mvmatrix(*element++);
+ }
+ dp->set_totalrects(mRects);
+
+ MOZ_ASSERT(mRects > 0 && mRects < 4);
+ for (size_t i = 0; i < mRects; i++) {
+ // Vertex
+ DumpRect(dp->add_layerrect(), mLayerRects[i]);
+ // UV
+ DumpRect(dp->add_texturerect(), mTextureRects[i]);
+ }
+
+ for (GLuint texId: mTexIDs) {
+ dp->add_texids(texId);
+ }
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ float mOffsetX;
+ float mOffsetY;
+ gfx::Matrix4x4 mMVMatrix;
+ size_t mRects;
+ gfx::Rect mLayerRects[4];
+ gfx::Rect mTextureRects[4];
+ std::list<GLuint> mTexIDs;
+ uint64_t mLayerRef;
+};
+
+class DebugDataSender
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DebugDataSender)
+
+ // Append a DebugData into mList on mThread
+ class AppendTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ AppendTask(DebugDataSender *host, DebugGLData *d)
+ : mData(d),
+ mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ mHost->mList.insertBack(mData);
+ return NS_OK;
+ }
+
+ private:
+ virtual ~AppendTask() { }
+
+ DebugGLData *mData;
+ // Keep a strong reference to DebugDataSender to prevent this object
+ // accessing mHost on mThread, when it's been destroyed on the main
+ // thread.
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ // Clear all DebugData in mList on mThead.
+ class ClearTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ explicit ClearTask(DebugDataSender *host)
+ : mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ mHost->RemoveData();
+ return NS_OK;
+ }
+
+ private:
+ virtual ~ClearTask() { }
+
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ // Send all DebugData in mList via websocket, and then, clean up
+ // mList on mThread.
+ class SendTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit SendTask(DebugDataSender *host)
+ : mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ // Sendout all appended debug data.
+ DebugGLData *d = nullptr;
+ while ((d = mHost->mList.popFirst()) != nullptr) {
+ UniquePtr<DebugGLData> cleaner(d);
+ if (!d->Write()) {
+ gLayerScopeManager.DestroyServerSocket();
+ break;
+ }
+ }
+
+ // Cleanup.
+ mHost->RemoveData();
+ return NS_OK;
+ }
+ private:
+ virtual ~SendTask() { }
+
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ explicit DebugDataSender(nsIThread *thread)
+ : mThread(thread)
+ { }
+
+ void Append(DebugGLData *d) {
+ mThread->Dispatch(new AppendTask(this, d), NS_DISPATCH_NORMAL);
+ }
+
+ void Cleanup() {
+ mThread->Dispatch(new ClearTask(this), NS_DISPATCH_NORMAL);
+ }
+
+ void Send() {
+ mThread->Dispatch(new SendTask(this), NS_DISPATCH_NORMAL);
+ }
+
+protected:
+ virtual ~DebugDataSender() {}
+ void RemoveData() {
+ MOZ_ASSERT(NS_GetCurrentThread() == mThread);
+ if (mList.isEmpty())
+ return;
+
+ DebugGLData *d;
+ while ((d = mList.popFirst()) != nullptr)
+ delete d;
+ }
+
+ // We can only modify or aceess mList on mThread.
+ LinkedList<DebugGLData> mList;
+ nsCOMPtr<nsIThread> mThread;
+};
+
+NS_IMPL_ISUPPORTS(DebugDataSender::AppendTask, nsIRunnable);
+NS_IMPL_ISUPPORTS(DebugDataSender::ClearTask, nsIRunnable);
+NS_IMPL_ISUPPORTS(DebugDataSender::SendTask, nsIRunnable);
+
+
+/*
+ * LayerScope SendXXX Structure
+ * 1. SendLayer
+ * 2. SendEffectChain
+ * 1. SendTexturedEffect
+ * -> SendTextureSource
+ * 2. SendMaskEffect
+ * -> SendTextureSource
+ * 3. SendYCbCrEffect
+ * -> SendTextureSource
+ * 4. SendColor
+ */
+class SenderHelper
+{
+// Sender public APIs
+public:
+ static void SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight);
+
+ static void SendEffectChain(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth = 0,
+ int aHeight = 0);
+
+ static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
+
+ static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
+
+ static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
+
+ static void ClearSentTextureIds();
+
+// Sender private functions
+private:
+ static void SendColor(void* aLayerRef,
+ const Color& aColor,
+ int aWidth,
+ int aHeight);
+ static void SendTextureSource(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ bool aFlipY,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket);
+ static void SetAndSendTexture(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ const TexturedEffect* aEffect);
+ static void SendTexturedEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const TexturedEffect* aEffect);
+ static void SendMaskEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectMask* aEffect);
+ static void SendYCbCrEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectYCbCr* aEffect);
+ static GLuint GetTextureID(GLContext* aGLContext,
+ TextureSourceOGL* aSource);
+ static bool HasTextureIdBeenSent(GLuint aTextureId);
+// Data fields
+private:
+ static bool sLayersTreeSendable;
+ static bool sLayersBufferSendable;
+ static std::vector<GLuint> sSentTextureIds;
+};
+
+bool SenderHelper::sLayersTreeSendable = true;
+bool SenderHelper::sLayersBufferSendable = true;
+std::vector<GLuint> SenderHelper::sSentTextureIds;
+
+
+// ----------------------------------------------
+// SenderHelper implementation
+// ----------------------------------------------
+void
+SenderHelper::ClearSentTextureIds()
+{
+ sSentTextureIds.clear();
+}
+
+bool
+SenderHelper::HasTextureIdBeenSent(GLuint aTextureId)
+{
+ return std::find(sSentTextureIds.begin(), sSentTextureIds.end(), aTextureId) != sSentTextureIds.end();
+}
+
+void
+SenderHelper::SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight)
+{
+ MOZ_ASSERT(aLayer && aLayer->GetLayer());
+ if (!aLayer || !aLayer->GetLayer()) {
+ return;
+ }
+
+ switch (aLayer->GetLayer()->GetType()) {
+ case Layer::TYPE_COLOR: {
+ EffectChain effect;
+ aLayer->GenEffectChain(effect);
+
+ LayerScope::DrawBegin();
+ LayerScope::DrawEnd(nullptr, effect, aWidth, aHeight);
+ break;
+ }
+ case Layer::TYPE_IMAGE:
+ case Layer::TYPE_CANVAS:
+ case Layer::TYPE_PAINTED: {
+ // Get CompositableHost and Compositor
+ CompositableHost* compHost = aLayer->GetCompositableHost();
+ Compositor* comp = compHost->GetCompositor();
+ // Send EffectChain only for CompositorOGL
+ if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) {
+ CompositorOGL* compOGL = comp->AsCompositorOGL();
+ EffectChain effect;
+ // Generate primary effect (lock and gen)
+ AutoLockCompositableHost lock(compHost);
+ aLayer->GenEffectChain(effect);
+
+ LayerScope::DrawBegin();
+ LayerScope::DrawEnd(compOGL->gl(), effect, aWidth, aHeight);
+ }
+ break;
+ }
+ case Layer::TYPE_CONTAINER:
+ default:
+ break;
+ }
+}
+
+void
+SenderHelper::SendColor(void* aLayerRef,
+ const Color& aColor,
+ int aWidth,
+ int aHeight)
+{
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
+}
+
+GLuint
+SenderHelper::GetTextureID(GLContext* aGLContext,
+ TextureSourceOGL* aSource) {
+ GLenum textureTarget = aSource->GetTextureTarget();
+ aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::SamplingFilter::LINEAR);
+
+ GLuint texID = 0;
+ // This is horrid hack. It assumes that aGLContext matches the context
+ // aSource has bound to.
+ if (textureTarget == LOCAL_GL_TEXTURE_2D) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &texID);
+ } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &texID);
+ } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &texID);
+ }
+
+ return texID;
+}
+
+void
+SenderHelper::SendTextureSource(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ bool aFlipY,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket)
+{
+ MOZ_ASSERT(aGLContext);
+ if (!aGLContext) {
+ return;
+ }
+ GLuint texID = GetTextureID(aGLContext, aSource);
+ if (HasTextureIdBeenSent(texID)) {
+ return;
+ }
+
+ GLenum textureTarget = aSource->GetTextureTarget();
+ ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
+ aSource->GetFormat());
+ int shaderConfig = config.mFeatures;
+
+ gfx::IntSize size = aSource->GetSize();
+
+ // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
+ // texture correctly. texID is used for tracking in DebugGLTextureData.
+ RefPtr<DataSourceSurface> img =
+ aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
+ size,
+ shaderConfig, aFlipY);
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
+ texID, img, aIsMask, Move(aPacket)));
+
+ sSentTextureIds.push_back(texID);
+ gLayerScopeManager.CurrentSession().mTexIDs.push_back(texID);
+
+}
+
+void
+SenderHelper::SetAndSendTexture(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ const TexturedEffect* aEffect)
+{
+ // Expose packet creation here, so we could dump primary texture effect attributes.
+ auto packet = MakeUnique<layerscope::Packet>();
+ layerscope::TexturePacket* texturePacket = packet->mutable_texture();
+ texturePacket->set_mpremultiplied(aEffect->mPremultiplied);
+ DumpFilter(texturePacket, aEffect->mSamplingFilter);
+ DumpRect(texturePacket->mutable_mtexturecoords(), aEffect->mTextureCoords);
+ SendTextureSource(aGLContext, aLayerRef, aSource, false, false, Move(packet));
+}
+
+void
+SenderHelper::SendTexturedEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const TexturedEffect* aEffect)
+{
+ TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL();
+ if (!source) {
+ return;
+ }
+
+ // Fallback texture sending path.
+ SetAndSendTexture(aGLContext, aLayerRef, source, aEffect);
+}
+
+void
+SenderHelper::SendMaskEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectMask* aEffect)
+{
+ TextureSourceOGL* source = aEffect->mMaskTexture->AsSourceOGL();
+ if (!source) {
+ return;
+ }
+
+ // Expose packet creation here, so we could dump secondary mask effect attributes.
+ auto packet = MakeUnique<layerscope::Packet>();
+ TexturePacket::EffectMask* mask = packet->mutable_texture()->mutable_mask();
+ mask->mutable_msize()->set_w(aEffect->mSize.width);
+ mask->mutable_msize()->set_h(aEffect->mSize.height);
+ auto element = reinterpret_cast<const Float *>(&(aEffect->mMaskTransform));
+ for (int i = 0; i < 16; i++) {
+ mask->mutable_mmasktransform()->add_m(*element++);
+ }
+
+ SendTextureSource(aGLContext, aLayerRef, source, false, true, Move(packet));
+}
+
+void
+SenderHelper::SendYCbCrEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectYCbCr* aEffect)
+{
+ TextureSource* sourceYCbCr = aEffect->mTexture;
+ if (!sourceYCbCr)
+ return;
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSourceOGL *sources[] = {
+ sourceYCbCr->GetSubSource(Y)->AsSourceOGL(),
+ sourceYCbCr->GetSubSource(Cb)->AsSourceOGL(),
+ sourceYCbCr->GetSubSource(Cr)->AsSourceOGL()
+ };
+
+ for (auto source: sources) {
+ SetAndSendTexture(aGLContext, aLayerRef, source, aEffect);
+ }
+}
+
+void
+SenderHelper::SendEffectChain(GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight)
+{
+ if (!sLayersBufferSendable) return;
+
+ const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
+ MOZ_ASSERT(primaryEffect);
+
+ if (!primaryEffect) {
+ return;
+ }
+
+ switch (primaryEffect->mType) {
+ case EffectTypes::RGB: {
+ const TexturedEffect* texturedEffect =
+ static_cast<const TexturedEffect*>(primaryEffect);
+ SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect);
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ const EffectYCbCr* yCbCrEffect =
+ static_cast<const EffectYCbCr*>(primaryEffect);
+ SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect);
+ break;
+ }
+ case EffectTypes::SOLID_COLOR: {
+ const EffectSolidColor* solidColorEffect =
+ static_cast<const EffectSolidColor*>(primaryEffect);
+ SendColor(aEffectChain.mLayerRef, solidColorEffect->mColor,
+ aWidth, aHeight);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA:
+ case EffectTypes::RENDER_TARGET:
+ default:
+ break;
+ }
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ const EffectMask* effectMask =
+ static_cast<const EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ SendMaskEffect(aGLContext, aEffectChain.mLayerRef, effectMask);
+ }
+}
+
+void
+LayerScope::ContentChanged(TextureHost *host)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.GetContentMonitor()->SetChangedHost(host);
+}
+
+// ----------------------------------------------
+// SocketHandler implementation
+// ----------------------------------------------
+void
+LayerScopeWebSocketManager::SocketHandler::OpenStream(nsISocketTransport* aTransport)
+{
+ MOZ_ASSERT(aTransport);
+
+ mTransport = aTransport;
+ mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
+ 0,
+ 0,
+ getter_AddRefs(mOutputStream));
+
+ nsCOMPtr<nsIInputStream> debugInputStream;
+ mTransport->OpenInputStream(0,
+ 0,
+ 0,
+ getter_AddRefs(debugInputStream));
+ mInputStream = do_QueryInterface(debugInputStream);
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::WriteToStream(void *aPtr,
+ uint32_t aSize)
+{
+ if (mState == NoHandshake) {
+ // Not yet handshake, just return true in case of
+ // LayerScope remove this handle
+ return true;
+ } else if (mState == HandshakeFailed) {
+ return false;
+ }
+
+ if (!mOutputStream) {
+ return false;
+ }
+
+ // Generate WebSocket header
+ uint8_t wsHeader[10];
+ int wsHeaderSize = 0;
+ const uint8_t opcode = 0x2;
+ wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
+ if (aSize <= 125) {
+ wsHeaderSize = 2;
+ wsHeader[1] = aSize;
+ } else if (aSize < 65536) {
+ wsHeaderSize = 4;
+ wsHeader[1] = 0x7E;
+ NetworkEndian::writeUint16(wsHeader + 2, aSize);
+ } else {
+ wsHeaderSize = 10;
+ wsHeader[1] = 0x7F;
+ NetworkEndian::writeUint64(wsHeader + 2, aSize);
+ }
+
+ // Send WebSocket header
+ nsresult rv;
+ uint32_t cnt;
+ rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
+ wsHeaderSize, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ uint32_t written = 0;
+ while (written < aSize) {
+ uint32_t cnt;
+ rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written,
+ aSize - written, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ written += cnt;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+LayerScopeWebSocketManager::SocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
+{
+ MOZ_ASSERT(mInputStream);
+
+ if (!mInputStream) {
+ return NS_OK;
+ }
+
+ if (!mConnected) {
+ nsTArray<nsCString> protocolString;
+ ReadInputStreamData(protocolString);
+
+ if (WebSocketHandshake(protocolString)) {
+ mState = HandshakeSuccess;
+ mConnected = true;
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+ } else {
+ mState = HandshakeFailed;
+ }
+ return NS_OK;
+ } else {
+ return HandleSocketMessage(aStream);
+ }
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
+{
+ nsLineBuffer<char> lineBuffer;
+ nsCString line;
+ bool more = true;
+ do {
+ NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
+
+ if (line.Length() > 0) {
+ aProtocolString.AppendElement(line);
+ }
+ } while (more && line.Length() > 0);
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
+{
+ nsresult rv;
+ bool isWebSocket = false;
+ nsCString version;
+ nsCString wsKey;
+ nsCString protocol;
+
+ // Validate WebSocket client request.
+ if (aProtocolString.Length() == 0)
+ return false;
+
+ // Check that the HTTP method is GET
+ const char* HTTP_METHOD = "GET ";
+ if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
+ return false;
+ }
+
+ for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
+ const char* line = aProtocolString[i].get();
+ const char* prop_pos = strchr(line, ':');
+ if (prop_pos != nullptr) {
+ nsCString key(line, prop_pos - line);
+ nsCString value(prop_pos + 2);
+ if (key.EqualsIgnoreCase("upgrade") &&
+ value.EqualsIgnoreCase("websocket")) {
+ isWebSocket = true;
+ } else if (key.EqualsIgnoreCase("sec-websocket-version")) {
+ version = value;
+ } else if (key.EqualsIgnoreCase("sec-websocket-key")) {
+ wsKey = value;
+ } else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
+ protocol = value;
+ }
+ }
+ }
+
+ if (!isWebSocket) {
+ return false;
+ }
+
+ if (!(version.EqualsLiteral("7") ||
+ version.EqualsLiteral("8") ||
+ version.EqualsLiteral("13"))) {
+ return false;
+ }
+
+ if (!(protocol.EqualsIgnoreCase("binary"))) {
+ return false;
+ }
+
+ if (!mOutputStream) {
+ return false;
+ }
+
+ // Client request is valid. Start to generate and send server response.
+ nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ nsAutoCString res;
+ SHA1Sum sha1;
+ nsCString combined(wsKey + guid);
+ sha1.update(combined.get(), combined.Length());
+ uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
+ sha1.finish(digest);
+ nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
+ rv = Base64Encode(newString, res);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
+ response.AppendLiteral("Upgrade: websocket\r\n");
+ response.AppendLiteral("Connection: Upgrade\r\n");
+ response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
+ response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
+ uint32_t written = 0;
+ uint32_t size = response.Length();
+ while (written < size) {
+ uint32_t cnt;
+ rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
+ size - written, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ written += cnt;
+ }
+ mOutputStream->Flush();
+
+ return true;
+}
+
+nsresult
+LayerScopeWebSocketManager::SocketHandler::HandleSocketMessage(nsIAsyncInputStream *aStream)
+{
+ // The reading and parsing of this input stream is customized for layer viewer.
+ const uint32_t cPacketSize = 1024;
+ char buffer[cPacketSize];
+ uint32_t count = 0;
+ nsresult rv = NS_OK;
+
+ do {
+ rv = mInputStream->Read((char *)buffer, cPacketSize, &count);
+
+ // TODO: combine packets if we have to read more than once
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ if (count == 0) {
+ // NS_BASE_STREAM_CLOSED
+ CloseConnection();
+ break;
+ }
+
+ rv = ProcessInput(reinterpret_cast<uint8_t *>(buffer), count);
+ } while (NS_SUCCEEDED(rv) && mInputStream);
+ return rv;
+}
+
+nsresult
+LayerScopeWebSocketManager::SocketHandler::ProcessInput(uint8_t *aBuffer,
+ uint32_t aCount)
+{
+ uint32_t avail = aCount;
+
+ // Decode Websocket data frame
+ if (avail <= 2) {
+ NS_WARNING("Packet size is less than 2 bytes");
+ return NS_OK;
+ }
+
+ // First byte, data type, only care the opcode
+ // rsvBits: aBuffer[0] & 0x70 (0111 0000)
+ uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000
+ uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111
+
+ if (!finBit) {
+ NS_WARNING("We cannot handle multi-fragments messages in Layerscope websocket parser.");
+ return NS_OK;
+ }
+
+ // Second byte, data length
+ uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000
+ int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111
+
+ if (!maskBit) {
+ NS_WARNING("Client to Server should set the mask bit");
+ return NS_OK;
+ }
+
+ uint32_t framingLength = 2 + 4; // 4 for masks
+
+ if (payloadLength64 < 126) {
+ if (avail < framingLength)
+ return NS_OK;
+ } else if (payloadLength64 == 126) {
+ // 16 bit length field
+ framingLength += 2;
+ if (avail < framingLength) {
+ return NS_OK;
+ }
+
+ payloadLength64 = aBuffer[2] << 8 | aBuffer[3];
+ } else {
+ // 64 bit length
+ framingLength += 8;
+ if (avail < framingLength) {
+ return NS_OK;
+ }
+
+ if (aBuffer[2] & 0x80) {
+ // Section 4.2 says that the most significant bit MUST be
+ // 0. (i.e. this is really a 63 bit value)
+ NS_WARNING("High bit of 64 bit length set");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // copy this in case it is unaligned
+ payloadLength64 = NetworkEndian::readInt64(aBuffer + 2);
+ }
+
+ uint8_t *payload = aBuffer + framingLength;
+ avail -= framingLength;
+
+ uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
+ if (avail < payloadLength) {
+ NS_WARNING("Packet size mismatch the payload length");
+ return NS_OK;
+ }
+
+ // Apply mask
+ uint32_t mask = NetworkEndian::readUint32(payload - 4);
+ ApplyMask(mask, payload, payloadLength);
+
+ if (opcode == 0x8) {
+ // opcode == 0x8 means connection close
+ CloseConnection();
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ if (!HandleDataFrame(payload, payloadLength)) {
+ NS_WARNING("Cannot decode payload data by the protocol buffer");
+ }
+
+ return NS_OK;
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::ApplyMask(uint32_t aMask,
+ uint8_t *aData,
+ uint64_t aLen)
+{
+ if (!aData || aLen == 0) {
+ return;
+ }
+
+ // Optimally we want to apply the mask 32 bits at a time,
+ // but the buffer might not be alligned. So we first deal with
+ // 0 to 3 bytes of preamble individually
+ while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) {
+ *aData ^= aMask >> 24;
+ aMask = RotateLeft(aMask, 8);
+ aData++;
+ aLen--;
+ }
+
+ // perform mask on full words of data
+ uint32_t *iData = reinterpret_cast<uint32_t *>(aData);
+ uint32_t *end = iData + (aLen >> 2);
+ NetworkEndian::writeUint32(&aMask, aMask);
+ for (; iData < end; iData++) {
+ *iData ^= aMask;
+ }
+ aMask = NetworkEndian::readUint32(&aMask);
+ aData = (uint8_t *)iData;
+ aLen = aLen % 4;
+
+ // There maybe up to 3 trailing bytes that need to be dealt with
+ // individually
+ while (aLen) {
+ *aData ^= aMask >> 24;
+ aMask = RotateLeft(aMask, 8);
+ aData++;
+ aLen--;
+ }
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::HandleDataFrame(uint8_t *aData,
+ uint32_t aSize)
+{
+ // Handle payload data by protocol buffer
+ auto p = MakeUnique<CommandPacket>();
+ p->ParseFromArray(static_cast<void*>(aData), aSize);
+
+ if (!p->has_type()) {
+ MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it");
+ return false;
+ }
+
+ switch (p->type()) {
+ case CommandPacket::LAYERS_TREE:
+ if (p->has_value()) {
+ SenderHelper::SetLayersTreeSendable(p->value());
+ }
+ break;
+
+ case CommandPacket::LAYERS_BUFFER:
+ if (p->has_value()) {
+ SenderHelper::SetLayersBufferSendable(p->value());
+ }
+ break;
+
+ case CommandPacket::NO_OP:
+ default:
+ NS_WARNING("Invalid message type");
+ break;
+ }
+ return true;
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::CloseConnection()
+{
+ gLayerScopeManager.GetSocketManager()->CleanDebugData();
+ if (mInputStream) {
+ mInputStream->AsyncWait(nullptr, 0, 0, nullptr);
+ mInputStream = nullptr;
+ }
+ if (mOutputStream) {
+ mOutputStream = nullptr;
+ }
+ if (mTransport) {
+ mTransport->Close(NS_BASE_STREAM_CLOSED);
+ mTransport = nullptr;
+ }
+ mConnected = false;
+}
+
+// ----------------------------------------------
+// LayerScopeWebSocketManager implementation
+// ----------------------------------------------
+LayerScopeWebSocketManager::LayerScopeWebSocketManager()
+ : mHandlerMutex("LayerScopeWebSocketManager::mHandlerMutex")
+{
+ NS_NewThread(getter_AddRefs(mDebugSenderThread));
+
+ mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
+ int port = gfxPrefs::LayerScopePort();
+ mServerSocket->Init(port, false, -1);
+ mServerSocket->AsyncListen(new SocketListener);
+}
+
+LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
+{
+ mServerSocket->Close();
+}
+
+void
+LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
+{
+ if (!mCurrentSender) {
+ mCurrentSender = new DebugDataSender(mDebugSenderThread);
+ }
+
+ mCurrentSender->Append(aDebugData);
+}
+
+void
+LayerScopeWebSocketManager::CleanDebugData()
+{
+ if (mCurrentSender) {
+ mCurrentSender->Cleanup();
+ }
+}
+
+void
+LayerScopeWebSocketManager::DispatchDebugData()
+{
+ MOZ_ASSERT(mCurrentSender.get() != nullptr);
+
+ mCurrentSender->Send();
+ mCurrentSender = nullptr;
+}
+
+NS_IMETHODIMP LayerScopeWebSocketManager::SocketListener::OnSocketAccepted(
+ nsIServerSocket *aServ,
+ nsISocketTransport *aTransport)
+{
+ if (!gLayerScopeManager.GetSocketManager())
+ return NS_OK;
+
+ printf_stderr("*** LayerScope: Accepted connection\n");
+ gLayerScopeManager.GetSocketManager()->AddConnection(aTransport);
+ gLayerScopeManager.GetContentMonitor()->Empty();
+ return NS_OK;
+}
+
+// ----------------------------------------------
+// LayerScope implementation
+// ----------------------------------------------
+/*static*/
+void
+LayerScope::Init()
+{
+ if (!gfxPrefs::LayerScopeEnabled() || XRE_IsGPUProcess()) {
+ return;
+ }
+
+ gLayerScopeManager.CreateServerSocket();
+}
+
+/*static*/
+void
+LayerScope::DrawBegin()
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.NewDrawSession();
+}
+
+/*static*/
+void
+LayerScope::SetRenderOffset(float aX, float aY)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.CurrentSession().mOffsetX = aX;
+ gLayerScopeManager.CurrentSession().mOffsetY = aY;
+}
+
+/*static*/
+void
+LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.CurrentSession().mMVMatrix = aMatrix;
+}
+
+/*static*/
+void
+LayerScope::SetDrawRects(size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ MOZ_ASSERT(aRects > 0 && aRects <= 4);
+ MOZ_ASSERT(aLayerRects);
+
+ gLayerScopeManager.CurrentSession().mRects = aRects;
+
+ for (size_t i = 0; i < aRects; i++){
+ gLayerScopeManager.CurrentSession().mLayerRects[i] = aLayerRects[i];
+ gLayerScopeManager.CurrentSession().mTextureRects[i] = aTextureRects[i];
+ }
+}
+
+/*static*/
+void
+LayerScope::DrawEnd(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight)
+{
+ // Protect this public function
+ if (!CheckSendable()) {
+ return;
+ }
+
+ // 1. Send textures.
+ SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
+
+ // 2. Send parameters of draw call, such as uniforms and attributes of
+ // vertex adnd fragment shader.
+ DrawSession& draws = gLayerScopeManager.CurrentSession();
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLDrawData(draws.mOffsetX, draws.mOffsetY,
+ draws.mMVMatrix, draws.mRects,
+ draws.mLayerRects,
+ draws.mTextureRects,
+ draws.mTexIDs,
+ aEffectChain.mLayerRef));
+
+}
+
+/*static*/
+void
+LayerScope::SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight)
+{
+ // Protect this public function
+ if (!CheckSendable()) {
+ return;
+ }
+ SenderHelper::SendLayer(aLayer, aWidth, aHeight);
+}
+
+/*static*/
+void
+LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
+{
+ // Protect this public function
+ if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) {
+ return;
+ }
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLLayersData(Move(aPacket)));
+}
+
+/*static*/
+bool
+LayerScope::CheckSendable()
+{
+ // Only compositor threads check LayerScope status
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || gIsGtest);
+
+ if (!gfxPrefs::LayerScopeEnabled()) {
+ return false;
+ }
+ if (!gLayerScopeManager.GetSocketManager()) {
+ Init();
+ return false;
+ }
+ if (!gLayerScopeManager.GetSocketManager()->IsConnected()) {
+ return false;
+ }
+ return true;
+}
+
+/*static*/
+void
+LayerScope::CleanLayer()
+{
+ if (CheckSendable()) {
+ gLayerScopeManager.GetSocketManager()->CleanDebugData();
+ }
+}
+
+/*static*/
+void
+LayerScope::SetHWComposed()
+{
+ if (CheckSendable()) {
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLMetaData(Packet::META, true));
+ }
+}
+
+/*static*/
+void
+LayerScope::SetPixelScale(double devPixelsPerCSSPixel)
+{
+ gLayerScopeManager.SetPixelScale(devPixelsPerCSSPixel);
+}
+
+// ----------------------------------------------
+// LayerScopeAutoFrame implementation
+// ----------------------------------------------
+LayerScopeAutoFrame::LayerScopeAutoFrame(int64_t aFrameStamp)
+{
+ // Do Begin Frame
+ BeginFrame(aFrameStamp);
+}
+
+LayerScopeAutoFrame::~LayerScopeAutoFrame()
+{
+ // Do End Frame
+ EndFrame();
+}
+
+void
+LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp)
+{
+ if (!LayerScope::CheckSendable()) {
+ return;
+ }
+ SenderHelper::ClearSentTextureIds();
+
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp));
+}
+
+void
+LayerScopeAutoFrame::EndFrame()
+{
+ if (!LayerScope::CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLFrameStatusData(Packet::FRAMEEND));
+ gLayerScopeManager.GetSocketManager()->DispatchDebugData();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/LayerScope.h b/system/graphics/layers/LayerScope.h
new file mode 100644
index 000000000..24fde2397
--- /dev/null
+++ b/system/graphics/layers/LayerScope.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSCOPE_H
+#define GFX_LAYERSCOPE_H
+
+#include <stdint.h>
+#include <mozilla/UniquePtr.h>
+#include "gfxMatrix.h"
+
+namespace mozilla {
+
+namespace gl { class GLContext; }
+
+namespace layers {
+
+
+namespace layerscope { class Packet; }
+
+struct EffectChain;
+class LayerComposite;
+class TextureHost;
+
+class LayerScope {
+public:
+ static void DrawBegin();
+ static void SetRenderOffset(float aX, float aY);
+ static void SetLayerTransform(const gfx::Matrix4x4& aMatrix);
+ static void SetDrawRects(size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects);
+ static void DrawEnd(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight);
+
+ static void SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight);
+ static void SendLayerDump(UniquePtr<layerscope::Packet> aPacket);
+ static bool CheckSendable();
+ static void CleanLayer();
+ static void SetHWComposed();
+
+ static void SetPixelScale(double devPixelsPerCSSPixel);
+ static void ContentChanged(TextureHost *host);
+private:
+ static void Init();
+};
+
+// Perform BeginFrame and EndFrame automatically
+class LayerScopeAutoFrame {
+public:
+ explicit LayerScopeAutoFrame(int64_t aFrameStamp);
+ ~LayerScopeAutoFrame();
+
+private:
+ static void BeginFrame(int64_t aFrameStamp);
+ static void EndFrame();
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSCOPE_H */
diff --git a/system/graphics/layers/LayerSorter.cpp b/system/graphics/layers/LayerSorter.cpp
new file mode 100644
index 000000000..ce180c374
--- /dev/null
+++ b/system/graphics/layers/LayerSorter.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerSorter.h"
+#include <math.h> // for fabs
+#include <stdint.h> // for uint32_t
+#include <stdio.h> // for fprintf, stderr, FILE
+#include <stdlib.h> // for getenv
+#include "DirectedGraph.h" // for DirectedGraph
+#include "Layers.h" // for Layer
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxLineSegment.h" // for gfxLineSegment
+#include "gfxPoint.h" // for gfxPoint
+#include "gfxQuad.h" // for gfxQuad
+#include "gfxRect.h" // for gfxRect
+#include "gfxTypes.h" // for gfxFloat
+#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
+#include "mozilla/Sprintf.h" // for SprintfLiteral
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, etc
+#include "limits.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+enum LayerSortOrder {
+ Undefined,
+ ABeforeB,
+ BBeforeA,
+};
+
+/**
+ * Recover the z component from a 2d transformed point by finding the intersection
+ * of a line through the point in the z direction and the transformed plane.
+ *
+ * We want to solve:
+ *
+ * point = normal . (p0 - l0) / normal . l
+ */
+static gfxFloat RecoverZDepth(const Matrix4x4& aTransform, const gfxPoint& aPoint)
+{
+ const Point3D l(0, 0, 1);
+ Point3D l0 = Point3D(aPoint.x, aPoint.y, 0);
+ Point3D p0 = aTransform.TransformPoint(Point3D(0, 0, 0));
+ Point3D normal = aTransform.GetNormalVector();
+
+ gfxFloat n = normal.DotProduct(p0 - l0);
+ gfxFloat d = normal.DotProduct(l);
+
+ if (!d) {
+ return 0;
+ }
+
+ return n/d;
+}
+
+/**
+ * Determine if this transform layer should be drawn before another when they
+ * are both preserve-3d children.
+ *
+ * We want to find the relative z depths of the 2 layers at points where they
+ * intersect when projected onto the 2d screen plane. Intersections are defined
+ * as corners that are positioned within the other quad, as well as intersections
+ * of the lines.
+ *
+ * We then choose the intersection point with the greatest difference in Z
+ * depths and use this point to determine an ordering for the two layers.
+ * For layers that are intersecting in 3d space, this essentially guesses an
+ * order. In a lot of cases we only intersect right at the edge point (3d cubes
+ * in particular) and this generates the 'correct' looking ordering. For planes
+ * that truely intersect, then there is no correct ordering and this remains
+ * unsolved without changing our rendering code.
+ */
+static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
+ gfxRect ourRect = ThebesRect(aOne->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ gfxRect otherRect = ThebesRect(aTwo->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+
+ MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
+ aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
+ // Effective transform of leaves may had been projected to 2D.
+ Matrix4x4 ourTransform =
+ aOne->GetLocalTransform() * aOne->GetParent()->GetEffectiveTransform();
+ Matrix4x4 otherTransform =
+ aTwo->GetLocalTransform() * aTwo->GetParent()->GetEffectiveTransform();
+
+ // Transform both rectangles and project into 2d space.
+ gfxQuad ourTransformedRect = ourRect.TransformToQuad(ourTransform);
+ gfxQuad otherTransformedRect = otherRect.TransformToQuad(otherTransform);
+
+ gfxRect ourBounds = ourTransformedRect.GetBounds();
+ gfxRect otherBounds = otherTransformedRect.GetBounds();
+
+ if (!ourBounds.Intersects(otherBounds)) {
+ return Undefined;
+ }
+
+ // Make a list of all points that are within the other rect.
+ // Could we just check Contains() on the bounds rects. ie, is it possible
+ // for layers to overlap without intersections (in 2d space) and yet still
+ // have their bounds rects not completely enclose each other?
+ nsTArray<gfxPoint> points;
+ for (uint32_t i = 0; i < 4; i++) {
+ if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
+ points.AppendElement(otherTransformedRect.mPoints[i]);
+ }
+ if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
+ points.AppendElement(ourTransformedRect.mPoints[i]);
+ }
+ }
+
+ // Look for intersections between lines (in 2d space) and use these as
+ // depth testing points.
+ for (uint32_t i = 0; i < 4; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ gfxPoint intersection;
+ gfxLineSegment one(ourTransformedRect.mPoints[i],
+ ourTransformedRect.mPoints[(i + 1) % 4]);
+ gfxLineSegment two(otherTransformedRect.mPoints[j],
+ otherTransformedRect.mPoints[(j + 1) % 4]);
+ if (one.Intersects(two, intersection)) {
+ points.AppendElement(intersection);
+ }
+ }
+ }
+
+ // No intersections, no defined order between these layers.
+ if (points.IsEmpty()) {
+ return Undefined;
+ }
+
+ // Find the relative Z depths of each intersection point and check that the layers are in the same order.
+ gfxFloat highest = 0;
+ for (uint32_t i = 0; i < points.Length(); i++) {
+ gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
+ gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
+
+ gfxFloat difference = otherDepth - ourDepth;
+
+ if (fabs(difference) > fabs(highest)) {
+ highest = difference;
+ }
+ }
+ // If layers have the same depth keep the original order
+ if (fabs(highest) < 0.1 || highest >= 0) {
+ return ABeforeB;
+ } else {
+ return BBeforeA;
+ }
+}
+
+#ifdef DEBUG
+// #define USE_XTERM_COLORING
+#ifdef USE_XTERM_COLORING
+// List of color values, which can be added to the xterm foreground offset or
+// background offset to generate a xterm color code.
+// NOTE: The colors that we don't explicitly use (by name) are commented out,
+// to avoid triggering Wunused-const-variable build warnings.
+static const int XTERM_FOREGROUND_COLOR_OFFSET = 30;
+static const int XTERM_BACKGROUND_COLOR_OFFSET = 40;
+static const int BLACK = 0;
+//static const int RED = 1;
+static const int GREEN = 2;
+//static const int YELLOW = 3;
+//static const int BLUE = 4;
+//static const int MAGENTA = 5;
+//static const int CYAN = 6;
+//static const int WHITE = 7;
+
+static const int RESET = 0;
+// static const int BRIGHT = 1;
+// static const int DIM = 2;
+// static const int UNDERLINE = 3;
+// static const int BLINK = 4;
+// static const int REVERSE = 7;
+// static const int HIDDEN = 8;
+
+static void SetTextColor(uint32_t aColor)
+{
+ char command[13];
+
+ /* Command is the control command to the terminal */
+ SprintfLiteral(command, "%c[%d;%d;%dm", 0x1B, RESET,
+ aColor + XTERM_FOREGROUND_COLOR_OFFSET,
+ BLACK + XTERM_BACKGROUND_COLOR_OFFSET);
+ printf("%s", command);
+}
+
+static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
+{
+ SetTextColor(aColor);
+ fprintf(aFile, "%p", aLayer);
+ SetTextColor(GREEN);
+}
+#else
+
+const char *colors[] = { "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White" };
+
+static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
+{
+ fprintf(aFile, "%p(%s)", aLayer, colors[aColor]);
+}
+#endif
+
+static void print_layer(FILE* aFile, Layer* aLayer)
+{
+ print_layer_internal(aFile, aLayer, aLayer->GetDebugColorIndex());
+}
+
+static void DumpLayerList(nsTArray<Layer*>& aLayers)
+{
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ print_layer(stderr, aLayers.ElementAt(i));
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n");
+}
+
+static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
+{
+ const nsTArray<DirectedGraph<Layer*>::Edge>& edges = aGraph.GetEdgeList();
+
+ for (uint32_t i = 0; i < edges.Length(); i++) {
+ fprintf(stderr, "From: ");
+ print_layer(stderr, edges.ElementAt(i).mFrom);
+ fprintf(stderr, ", To: ");
+ print_layer(stderr, edges.ElementAt(i).mTo);
+ fprintf(stderr, "\n");
+ }
+}
+#endif
+
+// The maximum number of layers that we will attempt to sort. Anything
+// greater than this will be left unsorted. We should consider enabling
+// depth buffering for the scene in this case.
+#define MAX_SORTABLE_LAYERS 100
+
+
+uint32_t gColorIndex = 1;
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
+{
+ uint32_t nodeCount = aLayers.Length();
+ if (nodeCount > MAX_SORTABLE_LAYERS) {
+ return;
+ }
+ DirectedGraph<Layer*> graph;
+
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ if (aLayers.ElementAt(i)->GetDebugColorIndex() == 0) {
+ aLayers.ElementAt(i)->SetDebugColorIndex(gColorIndex++);
+ if (gColorIndex > 7) {
+ gColorIndex = 1;
+ }
+ }
+ }
+ fprintf(stderr, " --- Layers before sorting: --- \n");
+ DumpLayerList(aLayers);
+ }
+#endif
+
+ // Iterate layers and determine edges.
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ for (uint32_t j = i + 1; j < nodeCount; j++) {
+ Layer* a = aLayers.ElementAt(i);
+ Layer* b = aLayers.ElementAt(j);
+ LayerSortOrder order = CompareDepth(a, b);
+ if (order == ABeforeB) {
+ graph.AddEdge(a, b);
+ } else if (order == BBeforeA) {
+ graph.AddEdge(b, a);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ fprintf(stderr, " --- Edge List: --- \n");
+ DumpEdgeList(graph);
+ }
+#endif
+
+ // Build a new array using the graph.
+ nsTArray<Layer*> noIncoming;
+ nsTArray<Layer*> sortedList;
+
+ // Make a list of all layers with no incoming edges.
+ noIncoming.AppendElements(aLayers);
+ const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
+ for (uint32_t i = 0; i < edges.Length(); i++) {
+ noIncoming.RemoveElement(edges.ElementAt(i).mTo);
+ }
+
+ // Move each item without incoming edges into the sorted list,
+ // and remove edges from it.
+ do {
+ if (!noIncoming.IsEmpty()) {
+ uint32_t last = noIncoming.Length() - 1;
+
+ Layer* layer = noIncoming.ElementAt(last);
+ MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList
+
+ noIncoming.RemoveElementAt(last);
+ sortedList.AppendElement(layer);
+
+ nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
+ graph.GetEdgesFrom(layer, outgoing);
+ for (uint32_t i = 0; i < outgoing.Length(); i++) {
+ DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
+ graph.RemoveEdge(edge);
+ if (!graph.NumEdgesTo(edge.mTo)) {
+ // If this node also has no edges now, add it to the list
+ noIncoming.AppendElement(edge.mTo);
+ }
+ }
+ }
+
+ // If there are no nodes without incoming edges, but there
+ // are still edges, then we have a cycle.
+ if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
+ // Find the node with the least incoming edges.
+ uint32_t minEdges = UINT_MAX;
+ Layer* minNode = nullptr;
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ uint32_t edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
+ if (edgeCount && edgeCount < minEdges) {
+ minEdges = edgeCount;
+ minNode = aLayers.ElementAt(i);
+ if (minEdges == 1) {
+ break;
+ }
+ }
+ }
+
+ if (minNode) {
+ // Remove all of them!
+ graph.RemoveEdgesTo(minNode);
+ noIncoming.AppendElement(minNode);
+ }
+ }
+ } while (!noIncoming.IsEmpty());
+ NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ fprintf(stderr, " --- Layers after sorting: --- \n");
+ DumpLayerList(sortedList);
+ }
+#endif
+
+ aLayers.Clear();
+ aLayers.AppendElements(sortedList);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/LayerSorter.h b/system/graphics/layers/LayerSorter.h
new file mode 100644
index 000000000..b9f776e83
--- /dev/null
+++ b/system/graphics/layers/LayerSorter.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSORTER_H
+#define GFX_LAYERSORTER_H
+
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSORTER_H */
diff --git a/system/graphics/layers/LayerTreeInvalidation.cpp b/system/graphics/layers/LayerTreeInvalidation.cpp
new file mode 100644
index 000000000..5604fafee
--- /dev/null
+++ b/system/graphics/layers/LayerTreeInvalidation.cpp
@@ -0,0 +1,679 @@
+/*-*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeInvalidation.h"
+
+#include <stdint.h> // for uint32_t
+#include "ImageContainer.h" // for ImageContainer
+#include "ImageLayers.h" // for ImageLayer, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "Units.h" // for ParentLayerIntRect
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsDataHashtable.h" // for nsDataHashtable
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsHashKeys.h" // for nsPtrHashKey
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for IntRect
+#include "nsTArray.h" // for AutoTArray, nsTArray_Impl
+#include "mozilla/Poison.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+struct LayerPropertiesBase;
+UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false);
+
+/**
+ * Get accumulated transform of from the context creating layer to the
+ * given layer.
+ */
+static Matrix4x4
+GetTransformIn3DContext(Layer* aLayer) {
+ Matrix4x4 transform = aLayer->GetLocalTransform();
+ for (Layer* layer = aLayer->GetParent();
+ layer && layer->Extend3DContext();
+ layer = layer->GetParent()) {
+ transform = transform * layer->GetLocalTransform();
+ }
+ return transform;
+}
+
+/**
+ * Get a transform for the given layer depending on extending 3D
+ * context.
+ *
+ * @return local transform for layers not participating 3D rendering
+ * context, or the accmulated transform in the context for else.
+ */
+static Matrix4x4
+GetTransformForInvalidation(Layer* aLayer) {
+ return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext() ?
+ aLayer->GetLocalTransform() : GetTransformIn3DContext(aLayer));
+}
+
+static IntRect
+TransformRect(const IntRect& aRect, const Matrix4x4& aTransform)
+{
+ if (aRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
+ rect.RoundOut();
+
+ IntRect intRect;
+ if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
+ return IntRect();
+ }
+
+ return intRect;
+}
+
+static void
+AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const Matrix4x4& aTransform)
+{
+ for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
+ aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
+ }
+ aDest.SimplifyOutward(20);
+}
+
+static void
+AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource)
+{
+ aDest.Or(aDest, aSource);
+ aDest.SimplifyOutward(20);
+}
+
+/**
+ * Walks over this layer, and all descendant layers.
+ * If any of these are a ContainerLayer that reports invalidations to a PresShell,
+ * then report that the entire bounds have changed.
+ */
+static void
+NotifySubdocumentInvalidation(Layer* aLayer, NotifySubDocInvalidationFunc aCallback)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [aCallback] (Layer* layer)
+ {
+ layer->ClearInvalidRect();
+ if (layer->GetMaskLayer()) {
+ NotifySubdocumentInvalidation(layer->GetMaskLayer(), aCallback);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
+ NotifySubdocumentInvalidation(maskLayer, aCallback);
+ }
+ },
+ [aCallback] (Layer* layer)
+ {
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (container) {
+ aCallback(container, container->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+ });
+}
+
+struct LayerPropertiesBase : public LayerProperties
+{
+ explicit LayerPropertiesBase(Layer* aLayer)
+ : mLayer(aLayer)
+ , mMaskLayer(nullptr)
+ , mVisibleRegion(mLayer->GetLocalVisibleRegion().ToUnknownRegion())
+ , mPostXScale(aLayer->GetPostXScale())
+ , mPostYScale(aLayer->GetPostYScale())
+ , mOpacity(aLayer->GetLocalOpacity())
+ , mUseClipRect(!!aLayer->GetLocalClipRect())
+ {
+ MOZ_COUNT_CTOR(LayerPropertiesBase);
+ if (aLayer->GetMaskLayer()) {
+ mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
+ }
+ for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
+ mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
+ }
+ if (mUseClipRect) {
+ mClipRect = *aLayer->GetLocalClipRect();
+ }
+ mTransform = GetTransformForInvalidation(aLayer);
+ }
+ LayerPropertiesBase()
+ : mLayer(nullptr)
+ , mMaskLayer(nullptr)
+ {
+ MOZ_COUNT_CTOR(LayerPropertiesBase);
+ }
+ ~LayerPropertiesBase()
+ {
+ MOZ_COUNT_DTOR(LayerPropertiesBase);
+ }
+
+protected:
+ LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
+ LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
+
+public:
+ virtual nsIntRegion ComputeDifferences(Layer* aRoot,
+ NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged);
+
+ virtual void MoveBy(const IntPoint& aOffset);
+
+ nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ // Bug 1251615: This canary is sometimes hit. We're still not sure why.
+ mCanary.Check();
+ bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
+ mLayer->GetPostXScale() != mPostXScale ||
+ mLayer->GetPostYScale() != mPostYScale;
+ const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect();
+ nsIntRegion result;
+
+ bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
+ if (!ancestorMaskChanged) {
+ for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
+ if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) {
+ ancestorMaskChanged = true;
+ break;
+ }
+ }
+ }
+
+ Layer* otherMask = mLayer->GetMaskLayer();
+ if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
+ ancestorMaskChanged ||
+ (mUseClipRect != !!otherClip) ||
+ mLayer->GetLocalOpacity() != mOpacity ||
+ transformChanged)
+ {
+ aGeometryChanged = true;
+ result = OldTransformedBounds();
+ AddRegion(result, NewTransformedBounds());
+
+ // We can't bail out early because we need to update mChildrenChanged.
+ }
+
+ AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
+ AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform);
+
+ if (mMaskLayer && otherMask) {
+ AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
+ }
+
+ for (size_t i = 0;
+ i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
+ i++)
+ {
+ AddTransformedRegion(result,
+ mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
+ }
+
+ if (mUseClipRect && otherClip) {
+ if (!mClipRect.IsEqualInterior(*otherClip)) {
+ aGeometryChanged = true;
+ nsIntRegion tmp;
+ tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
+ AddRegion(result, tmp);
+ }
+ }
+
+ mLayer->ClearInvalidRect();
+ return result;
+ }
+
+ void CheckCanary()
+ {
+ mCanary.Check();
+ mLayer->CheckCanary();
+ }
+
+ virtual IntRect NewTransformedBounds()
+ {
+ return TransformRect(mLayer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds(),
+ GetTransformForInvalidation(mLayer));
+ }
+
+ virtual IntRect OldTransformedBounds()
+ {
+ return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ return IntRect();
+ }
+
+ RefPtr<Layer> mLayer;
+ UniquePtr<LayerPropertiesBase> mMaskLayer;
+ nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
+ nsIntRegion mVisibleRegion;
+ Matrix4x4 mTransform;
+ float mPostXScale;
+ float mPostYScale;
+ float mOpacity;
+ ParentLayerIntRect mClipRect;
+ bool mUseClipRect;
+ mozilla::CorruptionCanary mCanary;
+};
+
+struct ContainerLayerProperties : public LayerPropertiesBase
+{
+ explicit ContainerLayerProperties(ContainerLayer* aLayer)
+ : LayerPropertiesBase(aLayer)
+ , mPreXScale(aLayer->GetPreXScale())
+ , mPreYScale(aLayer->GetPreYScale())
+ {
+ for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ child->CheckCanary();
+ mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));
+ }
+ }
+
+protected:
+ ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
+ ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
+
+public:
+ nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged) override
+ {
+ // Make sure we got our virtual call right
+ mSubtypeCanary.Check();
+ ContainerLayer* container = mLayer->AsContainerLayer();
+ nsIntRegion invalidOfLayer; // Invalid regions of this layer.
+ nsIntRegion result; // Invliad regions for children only.
+
+ container->CheckCanary();
+
+ bool childrenChanged = false;
+
+ if (mPreXScale != container->GetPreXScale() ||
+ mPreYScale != container->GetPreYScale()) {
+ aGeometryChanged = true;
+ invalidOfLayer = OldTransformedBounds();
+ AddRegion(invalidOfLayer, NewTransformedBounds());
+ childrenChanged = true;
+
+ // Can't bail out early, we need to update the child container layers
+ }
+
+ // A low frame rate is especially visible to users when scrolling, so we
+ // particularly want to avoid unnecessary invalidation at that time. For us
+ // here, that means avoiding unnecessary invalidation of child items when
+ // other children are added to or removed from our container layer, since
+ // that may be caused by children being scrolled in or out of view. We are
+ // less concerned with children changing order.
+ // TODO: Consider how we could avoid unnecessary invalidation when children
+ // change order, and whether the overhead would be worth it.
+
+ nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length());
+ for (uint32_t i = 0; i < mChildren.Length(); ++i) {
+ mChildren[i]->CheckCanary();
+ oldIndexMap.Put(mChildren[i]->mLayer, i);
+ }
+
+ uint32_t i = 0; // cursor into the old child list mChildren
+ for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
+ bool invalidateChildsCurrentArea = false;
+ if (i < mChildren.Length()) {
+ uint32_t childsOldIndex;
+ if (oldIndexMap.Get(child, &childsOldIndex)) {
+ if (childsOldIndex >= i) {
+ // Invalidate the old areas of layers that used to be between the
+ // current |child| and the previous |child| that was also in the
+ // old list mChildren (if any of those children have been reordered
+ // rather than removed, we will invalidate their new area when we
+ // encounter them in the new list):
+ for (uint32_t j = i; j < childsOldIndex; ++j) {
+ AddRegion(result, mChildren[j]->OldTransformedBounds());
+ childrenChanged |= true;
+ }
+ if (childsOldIndex >= mChildren.Length()) {
+ MOZ_CRASH("Out of bounds");
+ }
+ // Invalidate any regions of the child that have changed:
+ nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged);
+ i = childsOldIndex + 1;
+ if (!region.IsEmpty()) {
+ AddRegion(result, region);
+ childrenChanged |= true;
+ }
+ } else {
+ // We've already seen this child in mChildren (which means it must
+ // have been reordered) and invalidated its old area. We need to
+ // invalidate its new area too:
+ invalidateChildsCurrentArea = true;
+ }
+ } else {
+ // |child| is new
+ invalidateChildsCurrentArea = true;
+ }
+ } else {
+ // |child| is new, or was reordered to a higher index
+ invalidateChildsCurrentArea = true;
+ }
+ if (invalidateChildsCurrentArea) {
+ aGeometryChanged = true;
+ AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(),
+ GetTransformForInvalidation(child));
+ if (aCallback) {
+ NotifySubdocumentInvalidation(child, aCallback);
+ } else {
+ ClearInvalidations(child);
+ }
+ }
+ childrenChanged |= invalidateChildsCurrentArea;
+ }
+
+ // Process remaining removed children.
+ while (i < mChildren.Length()) {
+ childrenChanged |= true;
+ AddRegion(result, mChildren[i]->OldTransformedBounds());
+ i++;
+ }
+
+ if (aCallback) {
+ aCallback(container, result);
+ }
+
+ if (childrenChanged) {
+ container->SetChildrenChanged(true);
+ }
+
+ if (!mLayer->Extend3DContext()) {
+ // |result| contains invalid regions only of children.
+ result.Transform(GetTransformForInvalidation(mLayer));
+ }
+ // else, effective transforms have applied on children.
+
+ result.OrWith(invalidOfLayer);
+
+ return result;
+ }
+
+ IntRect NewTransformedBounds() override
+ {
+ if (mLayer->Extend3DContext()) {
+ IntRect result;
+ for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+ result = result.Union(child->NewTransformedBounds());
+ }
+ return result;
+ }
+
+ return LayerPropertiesBase::NewTransformedBounds();
+ }
+
+ IntRect OldTransformedBounds() override
+ {
+ if (mLayer->Extend3DContext()) {
+ IntRect result;
+ for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+ result = result.Union(child->OldTransformedBounds());
+ }
+ return result;
+ }
+ return LayerPropertiesBase::OldTransformedBounds();
+ }
+
+ // The old list of children:
+ mozilla::CorruptionCanary mSubtypeCanary;
+ nsTArray<UniquePtr<LayerPropertiesBase>> mChildren;
+ float mPreXScale;
+ float mPreYScale;
+};
+
+struct ColorLayerProperties : public LayerPropertiesBase
+{
+ explicit ColorLayerProperties(ColorLayer *aLayer)
+ : LayerPropertiesBase(aLayer)
+ , mColor(aLayer->GetColor())
+ , mBounds(aLayer->GetBounds())
+ { }
+
+protected:
+ ColorLayerProperties(const ColorLayerProperties& a) = delete;
+ ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
+
+public:
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
+
+ if (mColor != color->GetColor()) {
+ aGeometryChanged = true;
+ return NewTransformedBounds();
+ }
+
+ nsIntRegion boundsDiff;
+ boundsDiff.Xor(mBounds, color->GetBounds());
+
+ nsIntRegion result;
+ AddTransformedRegion(result, boundsDiff, mTransform);
+
+ return result;
+ }
+
+ Color mColor;
+ IntRect mBounds;
+};
+
+static ImageHost* GetImageHost(Layer* aLayer)
+{
+ LayerComposite* composite = aLayer->AsLayerComposite();
+ if (composite) {
+ return static_cast<ImageHost*>(composite->GetCompositableHost());
+ }
+ return nullptr;
+}
+
+struct ImageLayerProperties : public LayerPropertiesBase
+{
+ explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
+ : LayerPropertiesBase(aImage)
+ , mContainer(aImage->GetContainer())
+ , mImageHost(GetImageHost(aImage))
+ , mSamplingFilter(aImage->GetSamplingFilter())
+ , mScaleToSize(aImage->GetScaleToSize())
+ , mScaleMode(aImage->GetScaleMode())
+ , mLastProducerID(-1)
+ , mLastFrameID(-1)
+ , mIsMask(aIsMask)
+ {
+ if (mImageHost) {
+ mLastProducerID = mImageHost->GetLastProducerID();
+ mLastFrameID = mImageHost->GetLastFrameID();
+ }
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
+
+ if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
+ aGeometryChanged = true;
+ IntRect result = NewTransformedBounds();
+ result = result.Union(OldTransformedBounds());
+ return result;
+ }
+
+ ImageContainer* container = imageLayer->GetContainer();
+ ImageHost* host = GetImageHost(imageLayer);
+ if (mContainer != container ||
+ mSamplingFilter != imageLayer->GetSamplingFilter() ||
+ mScaleToSize != imageLayer->GetScaleToSize() ||
+ mScaleMode != imageLayer->GetScaleMode() ||
+ host != mImageHost ||
+ (host && host->GetProducerID() != mLastProducerID) ||
+ (host && host->GetFrameID() != mLastFrameID)) {
+ aGeometryChanged = true;
+
+ if (mIsMask) {
+ // Mask layers have an empty visible region, so we have to
+ // use the image size instead.
+ IntSize size;
+ if (container) {
+ size = container->GetCurrentSize();
+ }
+ if (host) {
+ size = host->GetImageSize();
+ }
+ IntRect rect(0, 0, size.width, size.height);
+ return TransformRect(rect, GetTransformForInvalidation(mLayer));
+ }
+ return NewTransformedBounds();
+ }
+
+ return IntRect();
+ }
+
+ RefPtr<ImageContainer> mContainer;
+ RefPtr<ImageHost> mImageHost;
+ SamplingFilter mSamplingFilter;
+ gfx::IntSize mScaleToSize;
+ ScaleMode mScaleMode;
+ int32_t mLastProducerID;
+ int32_t mLastFrameID;
+ bool mIsMask;
+};
+
+struct CanvasLayerProperties : public LayerPropertiesBase
+{
+ explicit CanvasLayerProperties(CanvasLayer* aCanvas)
+ : LayerPropertiesBase(aCanvas)
+ , mImageHost(GetImageHost(aCanvas))
+ {
+ mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
+
+ ImageHost* host = GetImageHost(canvasLayer);
+ if (host && host->GetFrameID() != mFrameID) {
+ aGeometryChanged = true;
+
+ return NewTransformedBounds();
+ }
+
+ return IntRect();
+ }
+
+ RefPtr<ImageHost> mImageHost;
+ int32_t mFrameID;
+};
+
+UniquePtr<LayerPropertiesBase>
+CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */)
+{
+ if (!aRoot) {
+ return MakeUnique<LayerPropertiesBase>();
+ }
+
+ MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
+
+ aRoot->CheckCanary();
+
+ switch (aRoot->GetType()) {
+ case Layer::TYPE_CONTAINER:
+ case Layer::TYPE_REF:
+ return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
+ case Layer::TYPE_COLOR:
+ return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
+ case Layer::TYPE_IMAGE:
+ return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
+ case Layer::TYPE_CANVAS:
+ return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
+ case Layer::TYPE_READBACK:
+ case Layer::TYPE_SHADOW:
+ case Layer::TYPE_PAINTED:
+ return MakeUnique<LayerPropertiesBase>(aRoot);
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
+ return MakeUnique<LayerPropertiesBase>(aRoot);
+}
+
+/* static */ UniquePtr<LayerProperties>
+LayerProperties::CloneFrom(Layer* aRoot)
+{
+ return CloneLayerTreePropertiesInternal(aRoot);
+}
+
+/* static */ void
+LayerProperties::ClearInvalidations(Layer *aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ layer->ClearInvalidRect();
+ if (layer->GetMaskLayer()) {
+ ClearInvalidations(layer->GetMaskLayer());
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ ClearInvalidations(layer->GetAncestorMaskLayerAt(i));
+ }
+
+ }
+ );
+}
+
+nsIntRegion
+LayerPropertiesBase::ComputeDifferences(Layer* aRoot, NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged = nullptr)
+{
+ NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
+ if (mLayer != aRoot) {
+ if (aCallback) {
+ NotifySubdocumentInvalidation(aRoot, aCallback);
+ } else {
+ ClearInvalidations(aRoot);
+ }
+ IntRect result = TransformRect(aRoot->GetLocalVisibleRegion().ToUnknownRegion().GetBounds(),
+ aRoot->GetLocalTransform());
+ result = result.Union(OldTransformedBounds());
+ if (aGeometryChanged != nullptr) {
+ *aGeometryChanged = true;
+ }
+ return result;
+ } else {
+ bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
+ nsIntRegion invalid = ComputeChange(aCallback, geometryChanged);
+ if (aGeometryChanged != nullptr) {
+ *aGeometryChanged = geometryChanged;
+ }
+ return invalid;
+ }
+}
+
+void
+LayerPropertiesBase::MoveBy(const IntPoint& aOffset)
+{
+ mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/LayerTreeInvalidation.h b/system/graphics/layers/LayerTreeInvalidation.h
new file mode 100644
index 000000000..4c08f5245
--- /dev/null
+++ b/system/graphics/layers/LayerTreeInvalidation.h
@@ -0,0 +1,77 @@
+/*-*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYER_TREE_INVALIDATION_H
+#define GFX_LAYER_TREE_INVALIDATION_H
+
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/Point.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+class ContainerLayer;
+
+/**
+ * Callback for ContainerLayer invalidations.
+ *
+ * @param aContainer ContainerLayer being invalidated.
+ * @param aRegion Invalidated region in the ContainerLayer's coordinate
+ * space.
+ */
+typedef void (*NotifySubDocInvalidationFunc)(ContainerLayer* aLayer,
+ const nsIntRegion& aRegion);
+
+/**
+ * A set of cached layer properties (including those of child layers),
+ * used for comparing differences in layer trees.
+ */
+struct LayerProperties
+{
+protected:
+ LayerProperties() {}
+
+ LayerProperties(const LayerProperties& a) = delete;
+ LayerProperties& operator=(const LayerProperties& a) = delete;
+
+public:
+ virtual ~LayerProperties() {}
+
+ /**
+ * Copies the current layer tree properties into
+ * a new LayerProperties object.
+ *
+ * @param Layer tree to copy, or nullptr if we have no
+ * initial layer tree.
+ */
+ static UniquePtr<LayerProperties> CloneFrom(Layer* aRoot);
+
+ /**
+ * Clear all invalidation status from this layer tree.
+ */
+ static void ClearInvalidations(Layer* aRoot);
+
+ /**
+ * Compares a set of existing layer tree properties to the current layer
+ * tree and generates the changed rectangle.
+ *
+ * @param aRoot Root layer of the layer tree to compare against.
+ * @param aCallback If specified, callback to call when ContainerLayers
+ * are invalidated.
+ * @return Painted area changed by the layer tree changes.
+ */
+ virtual nsIntRegion ComputeDifferences(Layer* aRoot,
+ NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged = nullptr) = 0;
+
+ virtual void MoveBy(const gfx::IntPoint& aOffset) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYER_TREE_INVALIDATON_H */
diff --git a/system/graphics/layers/LayerUserData.h b/system/graphics/layers/LayerUserData.h
new file mode 100644
index 000000000..9aaed2158
--- /dev/null
+++ b/system/graphics/layers/LayerUserData.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERUSERDATA_H
+#define GFX_LAYERUSERDATA_H
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Base class for userdata objects attached to layers and layer managers.
+ *
+ * We define it here in a separate header so clients only need to include
+ * this header for their class definitions, rather than pulling in Layers.h.
+ * Everything else in Layers.h should be forward-declarable.
+ */
+class LayerUserData {
+public:
+ virtual ~LayerUserData() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERUSERDATA_H */
diff --git a/system/graphics/layers/Layers.cpp b/system/graphics/layers/Layers.cpp
new file mode 100644
index 000000000..991e8ed2f
--- /dev/null
+++ b/system/graphics/layers/Layers.cpp
@@ -0,0 +1,2498 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Layers.h"
+#include <algorithm> // for max, min
+#include "apz/src/AsyncPanZoomController.h"
+#include "CompositableHost.h" // for CompositableHost
+#include "ImageContainer.h" // for ImageContainer, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "LayerSorter.h" // for SortLayersBy3DZOrder
+#include "LayersLogging.h" // for AppendToString
+#include "LayerUserData.h"
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "UnitTransforms.h" // for ViewAs
+#include "gfxEnv.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h"
+#include "gfxUtils.h" // for gfxUtils, etc
+#include "gfx2DGlue.h"
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/ToString.h"
+#include "mozilla/dom/Animation.h" // for ComputedTimingFunction
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersMessages.h" // for TransformFunction, etc
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowableLayer
+#include "nsAString.h"
+#include "nsCSSValue.h" // for nsCSSValue::Array, etc
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsStyleStruct.h" // for nsTimingFunction, etc
+#include "protobuf/LayerScopePacket.pb.h"
+#include "mozilla/Compression.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+uint8_t gLayerManagerLayerBuilder;
+
+namespace mozilla {
+namespace layers {
+
+FILE*
+FILEOrDefault(FILE* aFile)
+{
+ return aFile ? aFile : stderr;
+}
+
+typedef FrameMetrics::ViewID ViewID;
+
+using namespace mozilla::gfx;
+using namespace mozilla::Compression;
+
+//--------------------------------------------------
+// LayerManager
+
+/* static */ mozilla::LogModule*
+LayerManager::GetLog()
+{
+ static LazyLogModule sLog("Layers");
+ return sLog;
+}
+
+FrameMetrics::ViewID
+LayerManager::GetRootScrollableLayerId()
+{
+ if (!mRoot) {
+ return FrameMetrics::NULL_SCROLL_ID;
+ }
+
+ LayerMetricsWrapper layerMetricsRoot = LayerMetricsWrapper(mRoot);
+
+ LayerMetricsWrapper rootScrollableLayerMetrics =
+ BreadthFirstSearch<ForwardIterator>(
+ layerMetricsRoot,
+ [](LayerMetricsWrapper aLayerMetrics)
+ {
+ return aLayerMetrics.Metrics().IsScrollable();
+ }
+ );
+
+ return rootScrollableLayerMetrics.IsValid() ?
+ rootScrollableLayerMetrics.Metrics().GetScrollId() :
+ FrameMetrics::NULL_SCROLL_ID;
+}
+
+LayerMetricsWrapper
+LayerManager::GetRootContentLayer()
+{
+ if (!mRoot) {
+ return LayerMetricsWrapper();
+ }
+
+ LayerMetricsWrapper root(mRoot);
+
+ return BreadthFirstSearch<ForwardIterator>(root,
+ [](LayerMetricsWrapper aLayerMetrics)
+ {
+ return aLayerMetrics.Metrics().IsRootContent();
+ }
+ );
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateOptimalDrawTarget(const gfx::IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSize,
+ aFormat);
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateOptimalMaskDrawTarget(const gfx::IntSize &aSize)
+{
+ return CreateOptimalDrawTarget(aSize, SurfaceFormat::A8);
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateDrawTarget(const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ return gfxPlatform::GetPlatform()->
+ CreateOffscreenCanvasDrawTarget(aSize, aFormat);
+}
+
+already_AddRefed<PersistentBufferProvider>
+LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat)
+{
+ RefPtr<PersistentBufferProviderBasic> bufferProvider =
+ PersistentBufferProviderBasic::Create(aSize, aFormat,
+ gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
+
+ if (!bufferProvider) {
+ bufferProvider = PersistentBufferProviderBasic::Create(aSize, aFormat,
+ gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
+ }
+
+ return bufferProvider.forget();
+}
+
+#ifdef DEBUG
+void
+LayerManager::Mutated(Layer* aLayer)
+{
+}
+#endif // DEBUG
+
+already_AddRefed<ImageContainer>
+LayerManager::CreateImageContainer(ImageContainer::Mode flag)
+{
+ RefPtr<ImageContainer> container = new ImageContainer(flag);
+ return container.forget();
+}
+
+bool
+LayerManager::AreComponentAlphaLayersEnabled()
+{
+ return gfxPrefs::ComponentAlphaEnabled();
+}
+
+/*static*/ void
+LayerManager::LayerUserDataDestroy(void* data)
+{
+ delete static_cast<LayerUserData*>(data);
+}
+
+UniquePtr<LayerUserData>
+LayerManager::RemoveUserData(void* aKey)
+{
+ UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+ return d;
+}
+
+//--------------------------------------------------
+// Layer
+
+Layer::Layer(LayerManager* aManager, void* aImplData) :
+ mManager(aManager),
+ mParent(nullptr),
+ mNextSibling(nullptr),
+ mPrevSibling(nullptr),
+ mImplData(aImplData),
+ mMaskLayer(nullptr),
+ mPostXScale(1.0f),
+ mPostYScale(1.0f),
+ mOpacity(1.0),
+ mMixBlendMode(CompositionOp::OP_OVER),
+ mForceIsolatedGroup(false),
+ mContentFlags(0),
+ mUseTileSourceRect(false),
+ mIsFixedPosition(false),
+ mTransformIsPerspective(false),
+ mFixedPositionData(nullptr),
+ mStickyPositionData(nullptr),
+ mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
+ mScrollbarDirection(ScrollDirection::NONE),
+ mScrollbarThumbRatio(0.0f),
+ mIsScrollbarContainer(false),
+#ifdef DEBUG
+ mDebugColorIndex(0),
+#endif
+ mAnimationGeneration(0)
+{
+ MOZ_COUNT_CTOR(Layer);
+}
+
+Layer::~Layer()
+{
+ MOZ_COUNT_DTOR(Layer);
+}
+
+Animation*
+Layer::AddAnimation()
+{
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AddAnimation", this));
+
+ MOZ_ASSERT(!mPendingAnimations, "should have called ClearAnimations first");
+
+ Animation* anim = mAnimations.AppendElement();
+
+ Mutated();
+ return anim;
+}
+
+void
+Layer::ClearAnimations()
+{
+ mPendingAnimations = nullptr;
+
+ if (mAnimations.IsEmpty() && mAnimationData.IsEmpty()) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClearAnimations", this));
+ mAnimations.Clear();
+ mAnimationData.Clear();
+ Mutated();
+}
+
+Animation*
+Layer::AddAnimationForNextTransaction()
+{
+ MOZ_ASSERT(mPendingAnimations,
+ "should have called ClearAnimationsForNextTransaction first");
+
+ Animation* anim = mPendingAnimations->AppendElement();
+
+ return anim;
+}
+
+void
+Layer::ClearAnimationsForNextTransaction()
+{
+ // Ensure we have a non-null mPendingAnimations to mark a future clear.
+ if (!mPendingAnimations) {
+ mPendingAnimations = new AnimationArray;
+ }
+
+ mPendingAnimations->Clear();
+}
+
+static inline void
+SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
+{
+ aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
+}
+
+static nsCSSValueSharedList*
+CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
+{
+ nsAutoPtr<nsCSSValueList> result;
+ nsCSSValueList** resultTail = getter_Transfers(result);
+ for (uint32_t i = 0; i < aFunctions.Length(); i++) {
+ RefPtr<nsCSSValue::Array> arr;
+ switch (aFunctions[i].type()) {
+ case TransformFunction::TRotationX:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotationY:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotationZ:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotation:
+ {
+ const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotation3D:
+ {
+ float x = aFunctions[i].get_Rotation3D().x();
+ float y = aFunctions[i].get_Rotation3D().y();
+ float z = aFunctions[i].get_Rotation3D().z();
+ const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
+ SetCSSAngle(angle, arr->Item(4));
+ break;
+ }
+ case TransformFunction::TScale:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
+ break;
+ }
+ case TransformFunction::TTranslation:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_translate3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
+ arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
+ arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
+ break;
+ }
+ case TransformFunction::TSkewX:
+ {
+ const CSSAngle& x = aFunctions[i].get_SkewX().x();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
+ resultTail);
+ SetCSSAngle(x, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TSkewY:
+ {
+ const CSSAngle& y = aFunctions[i].get_SkewY().y();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
+ resultTail);
+ SetCSSAngle(y, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TSkew:
+ {
+ const CSSAngle& x = aFunctions[i].get_Skew().x();
+ const CSSAngle& y = aFunctions[i].get_Skew().y();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skew,
+ resultTail);
+ SetCSSAngle(x, arr->Item(1));
+ SetCSSAngle(y, arr->Item(2));
+ break;
+ }
+ case TransformFunction::TTransformMatrix:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
+ resultTail);
+ const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
+ arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
+ arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
+ arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
+ arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
+ arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
+ arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
+ arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
+ arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
+ arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
+ arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
+ arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
+ arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
+ arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
+ arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
+ break;
+ }
+ case TransformFunction::TPerspective:
+ {
+ float perspective = aFunctions[i].get_Perspective().value();
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_perspective,
+ resultTail);
+ arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
+ break;
+ }
+ default:
+ NS_ASSERTION(false, "All functions should be implemented?");
+ }
+ }
+ if (aFunctions.Length() == 0) {
+ result = new nsCSSValueList();
+ result->mValue.SetNoneValue();
+ }
+ return new nsCSSValueSharedList(result.forget());
+}
+
+void
+Layer::SetAnimations(const AnimationArray& aAnimations)
+{
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this));
+
+ mAnimations = aAnimations;
+ mAnimationData.Clear();
+ for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+ Animation& animation = mAnimations[i];
+ // Adjust fill mode to fill forwards so that if the main thread is delayed
+ // in clearing this animation we don't introduce flicker by jumping back to
+ // the old underlying value
+ switch (static_cast<dom::FillMode>(animation.fillMode())) {
+ case dom::FillMode::None:
+ animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Forwards);
+ break;
+ case dom::FillMode::Backwards:
+ animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
+ break;
+ default:
+ break;
+ }
+
+ AnimData* data = mAnimationData.AppendElement();
+ InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
+ data->mFunctions;
+ const InfallibleTArray<AnimationSegment>& segments = animation.segments();
+ for (uint32_t j = 0; j < segments.Length(); j++) {
+ TimingFunction tf = segments.ElementAt(j).sampleFn();
+
+ Maybe<ComputedTimingFunction> ctf =
+ AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
+ functions.AppendElement(ctf);
+ }
+
+ // Precompute the StyleAnimationValues that we need if this is a transform
+ // animation.
+ InfallibleTArray<StyleAnimationValue>& startValues = data->mStartValues;
+ InfallibleTArray<StyleAnimationValue>& endValues = data->mEndValues;
+ for (uint32_t j = 0; j < segments.Length(); j++) {
+ const AnimationSegment& segment = segments[j];
+ StyleAnimationValue* startValue = startValues.AppendElement();
+ StyleAnimationValue* endValue = endValues.AppendElement();
+ if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
+ const InfallibleTArray<TransformFunction>& startFunctions =
+ segment.startState().get_ArrayOfTransformFunction();
+ startValue->SetTransformValue(CreateCSSValueList(startFunctions));
+
+ const InfallibleTArray<TransformFunction>& endFunctions =
+ segment.endState().get_ArrayOfTransformFunction();
+ endValue->SetTransformValue(CreateCSSValueList(endFunctions));
+ } else {
+ NS_ASSERTION(segment.endState().type() == Animatable::Tfloat,
+ "Unknown Animatable type");
+ startValue->SetFloatValue(segment.startState().get_float());
+ endValue->SetFloatValue(segment.endState().get_float());
+ }
+ }
+ }
+
+ Mutated();
+}
+
+void
+Layer::StartPendingAnimations(const TimeStamp& aReadyTime)
+{
+ ForEachNode<ForwardIterator>(
+ this,
+ [&aReadyTime](Layer *layer)
+ {
+ bool updated = false;
+ for (size_t animIdx = 0, animEnd = layer->mAnimations.Length();
+ animIdx < animEnd; animIdx++) {
+ Animation& anim = layer->mAnimations[animIdx];
+ if (anim.startTime().IsNull()) {
+ anim.startTime() = aReadyTime - anim.initialCurrentTime();
+ updated = true;
+ }
+ }
+ if (updated) {
+ layer->Mutated();
+ }
+ });
+}
+
+void
+Layer::SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller)
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+ mApzcs[aIndex] = controller;
+}
+
+AsyncPanZoomController*
+Layer::GetAsyncPanZoomController(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+#ifdef DEBUG
+ if (mApzcs[aIndex]) {
+ MOZ_ASSERT(GetFrameMetrics(aIndex).IsScrollable());
+ }
+#endif
+ return mApzcs[aIndex];
+}
+
+void
+Layer::ScrollMetadataChanged()
+{
+ mApzcs.SetLength(GetScrollMetadataCount());
+}
+
+void
+Layer::ApplyPendingUpdatesToSubtree()
+{
+ ForEachNode<ForwardIterator>(
+ this,
+ [] (Layer *layer)
+ {
+ layer->ApplyPendingUpdatesForThisTransaction();
+ });
+
+ // Once we're done recursing through the whole tree, clear the pending
+ // updates from the manager.
+ Manager()->ClearPendingScrollInfoUpdate();
+}
+
+bool
+Layer::IsOpaqueForVisibility()
+{
+ return GetEffectiveOpacity() == 1.0f &&
+ GetEffectiveMixBlendMode() == CompositionOp::OP_OVER;
+}
+
+bool
+Layer::CanUseOpaqueSurface()
+{
+ // If the visible content in the layer is opaque, there is no need
+ // for an alpha channel.
+ if (GetContentFlags() & CONTENT_OPAQUE)
+ return true;
+ // Also, if this layer is the bottommost layer in a container which
+ // doesn't need an alpha channel, we can use an opaque surface for this
+ // layer too. Any transparent areas must be covered by something else
+ // in the container.
+ ContainerLayer* parent = GetParent();
+ return parent && parent->GetFirstChild() == this &&
+ parent->CanUseOpaqueSurface();
+}
+
+// NB: eventually these methods will be defined unconditionally, and
+// can be moved into Layers.h
+const Maybe<ParentLayerIntRect>&
+Layer::GetLocalClipRect()
+{
+ if (LayerComposite* shadow = AsLayerComposite()) {
+ return shadow->GetShadowClipRect();
+ }
+ return GetClipRect();
+}
+
+const LayerIntRegion&
+Layer::GetLocalVisibleRegion()
+{
+ if (LayerComposite* shadow = AsLayerComposite()) {
+ return shadow->GetShadowVisibleRegion();
+ }
+ return GetVisibleRegion();
+}
+
+Matrix4x4
+Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
+ Matrix* aResidualTransform)
+{
+ if (aResidualTransform) {
+ *aResidualTransform = Matrix();
+ }
+
+ if (!mManager->IsSnappingEffectiveTransforms()) {
+ return aTransform;
+ }
+
+ Matrix matrix2D;
+ if (aTransform.CanDraw2D(&matrix2D) &&
+ !matrix2D.HasNonTranslation() &&
+ matrix2D.HasNonIntegerTranslation()) {
+ auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
+ Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x,
+ snappedTranslation.y);
+ Matrix4x4 result = Matrix4x4::From2D(snappedMatrix);
+ if (aResidualTransform) {
+ // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
+ // (I.e., appying snappedMatrix after aResidualTransform gives the
+ // ideal transform.)
+ *aResidualTransform =
+ Matrix::Translation(matrix2D._31 - snappedTranslation.x,
+ matrix2D._32 - snappedTranslation.y);
+ }
+ return result;
+ }
+
+ return SnapTransformTranslation3D(aTransform, aResidualTransform);
+}
+
+Matrix4x4
+Layer::SnapTransformTranslation3D(const Matrix4x4& aTransform,
+ Matrix* aResidualTransform)
+{
+ if(aTransform.IsSingular() ||
+ aTransform.HasPerspectiveComponent() ||
+ aTransform.HasNonTranslation() ||
+ !aTransform.HasNonIntegerTranslation()) {
+ // For a singular transform, there is no reversed matrix, so we
+ // don't snap it.
+ // For a perspective transform, the content is transformed in
+ // non-linear, so we don't snap it too.
+ return aTransform;
+ }
+
+ // Snap for 3D Transforms
+
+ Point3D transformedOrigin = aTransform.TransformPoint(Point3D());
+
+ // Compute the transformed snap by rounding the values of
+ // transformed origin.
+ auto transformedSnapXY = IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
+ Matrix4x4 inverse = aTransform;
+ inverse.Invert();
+ // see Matrix4x4::ProjectPoint()
+ Float transformedSnapZ =
+ inverse._33 == 0 ? 0 : (-(transformedSnapXY.x * inverse._13 +
+ transformedSnapXY.y * inverse._23 +
+ inverse._43) / inverse._33);
+ Point3D transformedSnap =
+ Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
+ if (transformedOrigin == transformedSnap) {
+ return aTransform;
+ }
+
+ // Compute the snap from the transformed snap.
+ Point3D snap = inverse.TransformPoint(transformedSnap);
+ if (snap.z > 0.001 || snap.z < -0.001) {
+ // Allow some level of accumulated computation error.
+ MOZ_ASSERT(inverse._33 == 0.0);
+ return aTransform;
+ }
+
+ // The difference between the origin and snap is the residual transform.
+ if (aResidualTransform) {
+ // The residual transform is to translate the snap to the origin
+ // of the content buffer.
+ *aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
+ }
+
+ // Translate transformed origin to transformed snap since the
+ // residual transform would trnslate the snap to the origin.
+ Point3D transformedShift = transformedSnap - transformedOrigin;
+ Matrix4x4 result = aTransform;
+ result.PostTranslate(transformedShift.x,
+ transformedShift.y,
+ transformedShift.z);
+
+ // For non-2d transform, residual translation could be more than
+ // 0.5 pixels for every axis.
+
+ return result;
+}
+
+Matrix4x4
+Layer::SnapTransform(const Matrix4x4& aTransform,
+ const gfxRect& aSnapRect,
+ Matrix* aResidualTransform)
+{
+ if (aResidualTransform) {
+ *aResidualTransform = Matrix();
+ }
+
+ Matrix matrix2D;
+ Matrix4x4 result;
+ if (mManager->IsSnappingEffectiveTransforms() &&
+ aTransform.Is2D(&matrix2D) &&
+ gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
+ matrix2D.PreservesAxisAlignedRectangles()) {
+ auto transformedTopLeft = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopLeft())));
+ auto transformedTopRight = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopRight())));
+ auto transformedBottomRight = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.BottomRight())));
+
+ Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
+ transformedTopLeft, transformedTopRight, transformedBottomRight);
+
+ result = Matrix4x4::From2D(snappedMatrix);
+ if (aResidualTransform && !snappedMatrix.IsSingular()) {
+ // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
+ // (i.e., appying snappedMatrix after aResidualTransform gives the
+ // ideal transform.
+ Matrix snappedMatrixInverse = snappedMatrix;
+ snappedMatrixInverse.Invert();
+ *aResidualTransform = matrix2D * snappedMatrixInverse;
+ }
+ } else {
+ result = aTransform;
+ }
+ return result;
+}
+
+static bool
+AncestorLayerMayChangeTransform(Layer* aLayer)
+{
+ for (Layer* l = aLayer; l; l = l->GetParent()) {
+ if (l->GetContentFlags() & Layer::CONTENT_MAY_CHANGE_TRANSFORM) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Layer::MayResample()
+{
+ Matrix transform2d;
+ return !GetEffectiveTransform().Is2D(&transform2d) ||
+ ThebesMatrix(transform2d).HasNonIntegerTranslation() ||
+ AncestorLayerMayChangeTransform(this);
+}
+
+RenderTargetIntRect
+Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect)
+{
+ ContainerLayer* container = GetParent();
+ ContainerLayer* containerChild = nullptr;
+ NS_ASSERTION(GetParent(), "This can't be called on the root!");
+
+ // Find the layer creating the 3D context.
+ while (container->Extend3DContext() &&
+ !container->UseIntermediateSurface()) {
+ containerChild = container;
+ container = container->GetParent();
+ MOZ_ASSERT(container);
+ }
+
+ // Find the nearest layer with a clip, or this layer.
+ // ContainerState::SetupScrollingMetadata() may install a clip on
+ // the layer.
+ Layer *clipLayer =
+ containerChild && containerChild->GetLocalClipRect() ?
+ containerChild : this;
+
+ // Establish initial clip rect: it's either the one passed in, or
+ // if the parent has an intermediate surface, it's the extents of that surface.
+ RenderTargetIntRect currentClip;
+ if (container->UseIntermediateSurface()) {
+ currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size());
+ } else {
+ currentClip = aCurrentScissorRect;
+ }
+
+ if (!clipLayer->GetLocalClipRect()) {
+ return currentClip;
+ }
+
+ if (GetLocalVisibleRegion().IsEmpty() &&
+ !(AsLayerComposite() && AsLayerComposite()->NeedToDrawCheckerboarding())) {
+ // When our visible region is empty, our parent may not have created the
+ // intermediate surface that we would require for correct clipping; however,
+ // this does not matter since we are invisible.
+ // Make sure we still compute a clip rect if we want to draw checkboarding
+ // for this layer, since we want to do this even if the layer is invisible.
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+
+ const RenderTargetIntRect clipRect =
+ ViewAs<RenderTargetPixel>(*clipLayer->GetLocalClipRect(),
+ PixelCastJustification::RenderTargetIsParentLayerForRoot);
+ if (clipRect.IsEmpty()) {
+ // We might have a non-translation transform in the container so we can't
+ // use the code path below.
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+
+ RenderTargetIntRect scissor = clipRect;
+ if (!container->UseIntermediateSurface()) {
+ gfx::Matrix matrix;
+ DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix);
+ // See DefaultComputeEffectiveTransforms below
+ NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(),
+ "Non preserves axis aligned transform with clipped child should have forced intermediate surface");
+ gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height);
+ gfxRect trScissor = gfx::ThebesRect(matrix.TransformBounds(r));
+ trScissor.Round();
+ IntRect tmp;
+ if (!gfxUtils::GfxRectToIntRect(trScissor, &tmp)) {
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+ scissor = ViewAs<RenderTargetPixel>(tmp);
+
+ // Find the nearest ancestor with an intermediate surface
+ do {
+ container = container->GetParent();
+ } while (container && !container->UseIntermediateSurface());
+ }
+
+ if (container) {
+ scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft());
+ }
+ return currentClip.Intersect(scissor);
+}
+
+Maybe<ParentLayerIntRect>
+Layer::GetScrolledClipRect() const
+{
+ return mScrolledClip ? Some(mScrolledClip->GetClipRect()) : Nothing();
+}
+
+const ScrollMetadata&
+Layer::GetScrollMetadata(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+ return mScrollMetadata[aIndex];
+}
+
+const FrameMetrics&
+Layer::GetFrameMetrics(uint32_t aIndex) const
+{
+ return GetScrollMetadata(aIndex).GetMetrics();
+}
+
+bool
+Layer::HasScrollableFrameMetrics() const
+{
+ for (uint32_t i = 0; i < GetScrollMetadataCount(); i++) {
+ if (GetFrameMetrics(i).IsScrollable()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Layer::IsScrollInfoLayer() const
+{
+ // A scrollable container layer with no children
+ return AsContainerLayer()
+ && HasScrollableFrameMetrics()
+ && !GetFirstChild();
+}
+
+Matrix4x4
+Layer::GetTransform() const
+{
+ Matrix4x4 transform = mTransform;
+ transform.PostScale(GetPostXScale(), GetPostYScale(), 1.0f);
+ if (const ContainerLayer* c = AsContainerLayer()) {
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ }
+ return transform;
+}
+
+const CSSTransformMatrix
+Layer::GetTransformTyped() const
+{
+ return ViewAs<CSSTransformMatrix>(GetTransform());
+}
+
+Matrix4x4
+Layer::GetLocalTransform()
+{
+ if (LayerComposite* shadow = AsLayerComposite())
+ return shadow->GetShadowTransform();
+ else
+ return GetTransform();
+}
+
+const LayerToParentLayerMatrix4x4
+Layer::GetLocalTransformTyped()
+{
+ return ViewAs<LayerToParentLayerMatrix4x4>(GetLocalTransform());
+}
+
+bool
+Layer::HasTransformAnimation() const
+{
+ for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+ if (mAnimations[i].property() == eCSSProperty_transform) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+Layer::ApplyPendingUpdatesForThisTransaction()
+{
+ if (mPendingTransform && *mPendingTransform != mTransform) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
+ mTransform = *mPendingTransform;
+ Mutated();
+ }
+ mPendingTransform = nullptr;
+
+ if (mPendingAnimations) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
+ mPendingAnimations->SwapElements(mAnimations);
+ mPendingAnimations = nullptr;
+ Mutated();
+ }
+
+ for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+ FrameMetrics& fm = mScrollMetadata[i].GetMetrics();
+ Maybe<ScrollUpdateInfo> update = Manager()->GetPendingScrollInfoUpdate(fm.GetScrollId());
+ if (update) {
+ fm.UpdatePendingScrollInfo(update.value());
+ Mutated();
+ }
+ }
+}
+
+float
+Layer::GetLocalOpacity()
+{
+ float opacity = mOpacity;
+ if (LayerComposite* shadow = AsLayerComposite())
+ opacity = shadow->GetShadowOpacity();
+ return std::min(std::max(opacity, 0.0f), 1.0f);
+}
+
+float
+Layer::GetEffectiveOpacity()
+{
+ float opacity = GetLocalOpacity();
+ for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
+ c = c->GetParent()) {
+ opacity *= c->GetLocalOpacity();
+ }
+ return opacity;
+}
+
+CompositionOp
+Layer::GetEffectiveMixBlendMode()
+{
+ if(mMixBlendMode != CompositionOp::OP_OVER)
+ return mMixBlendMode;
+ for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
+ c = c->GetParent()) {
+ if(c->mMixBlendMode != CompositionOp::OP_OVER)
+ return c->mMixBlendMode;
+ }
+
+ return mMixBlendMode;
+}
+
+void
+Layer::ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface)
+{
+ if (GetMaskLayer()) {
+ ComputeEffectiveTransformForMaskLayer(GetMaskLayer(), aTransformToSurface);
+ }
+ for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = GetAncestorMaskLayerAt(i);
+ ComputeEffectiveTransformForMaskLayer(maskLayer, aTransformToSurface);
+ }
+}
+
+/* static */ void
+Layer::ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer, const gfx::Matrix4x4& aTransformToSurface)
+{
+ aMaskLayer->mEffectiveTransform = aTransformToSurface;
+
+#ifdef DEBUG
+ bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+#endif
+ // The mask layer can have an async transform applied to it in some
+ // situations, so be sure to use its GetLocalTransform() rather than
+ // its GetTransform().
+ aMaskLayer->mEffectiveTransform = aMaskLayer->GetLocalTransform() *
+ aMaskLayer->mEffectiveTransform;
+}
+
+RenderTargetRect
+Layer::TransformRectToRenderTarget(const LayerIntRect& aRect)
+{
+ LayerRect rect(aRect);
+ RenderTargetRect quad = RenderTargetRect::FromUnknownRect(
+ GetEffectiveTransform().TransformBounds(rect.ToUnknownRect()));
+ return quad;
+}
+
+bool
+Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
+ IntPoint* aLayerOffset)
+{
+ MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
+
+ if (!GetParent()) {
+ return false;
+ }
+
+ IntPoint offset;
+ aResult = GetLocalVisibleRegion().ToUnknownRegion();
+ for (Layer* layer = this; layer; layer = layer->GetParent()) {
+ gfx::Matrix matrix;
+ if (!layer->GetLocalTransform().Is2D(&matrix) ||
+ !matrix.IsTranslation()) {
+ return false;
+ }
+
+ // The offset of |layer| to its parent.
+ auto currentLayerOffset = IntPoint::Round(matrix.GetTranslation());
+
+ // Translate the accumulated visible region of |this| by the offset of
+ // |layer|.
+ aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
+
+ // If the parent layer clips its lower layers, clip the visible region
+ // we're accumulating.
+ if (layer->GetLocalClipRect()) {
+ aResult.AndWith(layer->GetLocalClipRect()->ToUnknownRect());
+ }
+
+ // Now we need to walk across the list of siblings for this parent layer,
+ // checking to see if any of these layer trees obscure |this|. If so,
+ // remove these areas from the visible region as well. This will pick up
+ // chrome overlays like a tab modal prompt.
+ Layer* sibling;
+ for (sibling = layer->GetNextSibling(); sibling;
+ sibling = sibling->GetNextSibling()) {
+ gfx::Matrix siblingMatrix;
+ if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
+ !siblingMatrix.IsTranslation()) {
+ continue;
+ }
+
+ // Retreive the translation from sibling to |layer|. The accumulated
+ // visible region is currently oriented with |layer|.
+ auto siblingOffset = IntPoint::Round(siblingMatrix.GetTranslation());
+ nsIntRegion siblingVisibleRegion(sibling->GetLocalVisibleRegion().ToUnknownRegion());
+ // Translate the siblings region to |layer|'s origin.
+ siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
+ // Apply the sibling's clip.
+ // Layer clip rects are not affected by the layer's transform.
+ Maybe<ParentLayerIntRect> clipRect = sibling->GetLocalClipRect();
+ if (clipRect) {
+ siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
+ }
+ // Subtract the sibling visible region from the visible region of |this|.
+ aResult.SubOut(siblingVisibleRegion);
+ }
+
+ // Keep track of the total offset for aLayerOffset. We use this in plugin
+ // positioning code.
+ offset += currentLayerOffset;
+ }
+
+ *aLayerOffset = IntPoint(offset.x, offset.y);
+ return true;
+}
+
+Maybe<ParentLayerIntRect>
+Layer::GetCombinedClipRect() const
+{
+ Maybe<ParentLayerIntRect> clip = GetClipRect();
+
+ clip = IntersectMaybeRects(clip, GetScrolledClipRect());
+
+ for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+ clip = IntersectMaybeRects(clip, mScrollMetadata[i].GetClipRect());
+ }
+
+ return clip;
+}
+
+ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData),
+ mFirstChild(nullptr),
+ mLastChild(nullptr),
+ mPreXScale(1.0f),
+ mPreYScale(1.0f),
+ mInheritedXScale(1.0f),
+ mInheritedYScale(1.0f),
+ mPresShellResolution(1.0f),
+ mScaleToResolution(false),
+ mUseIntermediateSurface(false),
+ mSupportsComponentAlphaChildren(false),
+ mMayHaveReadbackChild(false),
+ mChildrenChanged(false),
+ mEventRegionsOverride(EventRegionsOverride::NoOverride)
+{
+ MOZ_COUNT_CTOR(ContainerLayer);
+ mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
+}
+
+ContainerLayer::~ContainerLayer()
+{
+ MOZ_COUNT_DTOR(ContainerLayer);
+}
+
+bool
+ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
+{
+ if(aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if(aChild->GetParent()) {
+ NS_ERROR("aChild already in the tree");
+ return false;
+ }
+ if (aChild->GetNextSibling() || aChild->GetPrevSibling()) {
+ NS_ERROR("aChild already has siblings?");
+ return false;
+ }
+ if (aAfter && (aAfter->Manager() != Manager() ||
+ aAfter->GetParent() != this))
+ {
+ NS_ERROR("aAfter is not our child");
+ return false;
+ }
+
+ aChild->SetParent(this);
+ if (aAfter == mLastChild) {
+ mLastChild = aChild;
+ }
+ if (!aAfter) {
+ aChild->SetNextSibling(mFirstChild);
+ if (mFirstChild) {
+ mFirstChild->SetPrevSibling(aChild);
+ }
+ mFirstChild = aChild;
+ NS_ADDREF(aChild);
+ DidInsertChild(aChild);
+ return true;
+ }
+
+ Layer* next = aAfter->GetNextSibling();
+ aChild->SetNextSibling(next);
+ aChild->SetPrevSibling(aAfter);
+ if (next) {
+ next->SetPrevSibling(aChild);
+ }
+ aAfter->SetNextSibling(aChild);
+ NS_ADDREF(aChild);
+ DidInsertChild(aChild);
+ return true;
+}
+
+bool
+ContainerLayer::RemoveChild(Layer *aChild)
+{
+ if (aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if (aChild->GetParent() != this) {
+ NS_ERROR("aChild not our child");
+ return false;
+ }
+
+ Layer* prev = aChild->GetPrevSibling();
+ Layer* next = aChild->GetNextSibling();
+ if (prev) {
+ prev->SetNextSibling(next);
+ } else {
+ this->mFirstChild = next;
+ }
+ if (next) {
+ next->SetPrevSibling(prev);
+ } else {
+ this->mLastChild = prev;
+ }
+
+ aChild->SetNextSibling(nullptr);
+ aChild->SetPrevSibling(nullptr);
+ aChild->SetParent(nullptr);
+
+ this->DidRemoveChild(aChild);
+ NS_RELEASE(aChild);
+ return true;
+}
+
+
+bool
+ContainerLayer::RepositionChild(Layer* aChild, Layer* aAfter)
+{
+ if (aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if (aChild->GetParent() != this) {
+ NS_ERROR("aChild not our child");
+ return false;
+ }
+ if (aAfter && (aAfter->Manager() != Manager() ||
+ aAfter->GetParent() != this))
+ {
+ NS_ERROR("aAfter is not our child");
+ return false;
+ }
+ if (aChild == aAfter) {
+ NS_ERROR("aChild cannot be the same as aAfter");
+ return false;
+ }
+
+ Layer* prev = aChild->GetPrevSibling();
+ Layer* next = aChild->GetNextSibling();
+ if (prev == aAfter) {
+ // aChild is already in the correct position, nothing to do.
+ return true;
+ }
+ if (prev) {
+ prev->SetNextSibling(next);
+ } else {
+ mFirstChild = next;
+ }
+ if (next) {
+ next->SetPrevSibling(prev);
+ } else {
+ mLastChild = prev;
+ }
+ if (!aAfter) {
+ aChild->SetPrevSibling(nullptr);
+ aChild->SetNextSibling(mFirstChild);
+ if (mFirstChild) {
+ mFirstChild->SetPrevSibling(aChild);
+ }
+ mFirstChild = aChild;
+ return true;
+ }
+
+ Layer* afterNext = aAfter->GetNextSibling();
+ if (afterNext) {
+ afterNext->SetPrevSibling(aChild);
+ } else {
+ mLastChild = aChild;
+ }
+ aAfter->SetNextSibling(aChild);
+ aChild->SetPrevSibling(aAfter);
+ aChild->SetNextSibling(afterNext);
+ return true;
+}
+
+void
+ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
+ mInheritedXScale, mInheritedYScale,
+ mPresShellResolution, mScaleToResolution,
+ mEventRegionsOverride);
+}
+
+bool
+ContainerLayer::Creates3DContextWithExtendingChildren()
+{
+ if (Extend3DContext()) {
+ return false;
+ }
+ for (Layer* child = GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ if (child->Extend3DContext()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+ContainerLayer::HasMultipleChildren()
+{
+ uint32_t count = 0;
+ for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+ const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
+ if (clipRect && clipRect->IsEmpty())
+ continue;
+ if (child->GetLocalVisibleRegion().IsEmpty())
+ continue;
+ ++count;
+ if (count > 1)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Collect all leaf descendants of the current 3D context.
+ */
+void
+ContainerLayer::Collect3DContextLeaves(nsTArray<Layer*>& aToSort)
+{
+ ForEachNode<ForwardIterator>(
+ (Layer*) this,
+ [this, &aToSort](Layer* layer)
+ {
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (layer == this || (container && container->Extend3DContext() &&
+ !container->UseIntermediateSurface())) {
+ return TraversalFlag::Continue;
+ }
+ else {
+ aToSort.AppendElement(layer);
+ return TraversalFlag::Skip;
+ }
+ }
+ );
+}
+
+void
+ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+{
+ AutoTArray<Layer*, 10> toSort;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ ContainerLayer* container = l->AsContainerLayer();
+ if (container && container->Extend3DContext() &&
+ !container->UseIntermediateSurface()) {
+ container->Collect3DContextLeaves(toSort);
+ } else {
+ if (toSort.Length() > 0) {
+ SortLayersBy3DZOrder(toSort);
+ aArray.AppendElements(Move(toSort));
+ // XXX The move analysis gets confused here, because toSort gets moved
+ // here, and then gets used again outside of the loop. To clarify that
+ // we realize that the array is going to be empty to the move checker,
+ // we clear it again here. (This method renews toSort for the move
+ // analysis)
+ toSort.ClearAndRetainStorage();
+ }
+ aArray.AppendElement(l);
+ }
+ }
+ if (toSort.Length() > 0) {
+ SortLayersBy3DZOrder(toSort);
+ aArray.AppendElements(Move(toSort));
+ }
+}
+
+void
+ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
+{
+ Matrix residual;
+ Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+
+ // Keep 3D transforms for leaves to keep z-order sorting correct.
+ if (!Extend3DContext() && !Is3DContextLeaf()) {
+ idealTransform.ProjectTo2D();
+ }
+
+ bool useIntermediateSurface;
+ if (HasMaskLayers() ||
+ GetForceIsolatedGroup()) {
+ useIntermediateSurface = true;
+#ifdef MOZ_DUMP_PAINTING
+ } else if (gfxEnv::DumpPaintIntermediate() && !Extend3DContext()) {
+ useIntermediateSurface = true;
+#endif
+ } else {
+ /* Don't use an intermediate surface for opacity when it's within a 3d
+ * context, since we'd rather keep the 3d effects. This matches the
+ * WebKit/blink behaviour, but is changing in the latest spec.
+ */
+ float opacity = GetEffectiveOpacity();
+ CompositionOp blendMode = GetEffectiveMixBlendMode();
+ if ((HasMultipleChildren() || Creates3DContextWithExtendingChildren()) &&
+ ((opacity != 1.0f && !Extend3DContext()) ||
+ (blendMode != CompositionOp::OP_OVER))) {
+ useIntermediateSurface = true;
+ } else if (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren()) {
+ useIntermediateSurface = true;
+ } else {
+ useIntermediateSurface = false;
+ gfx::Matrix contTransform;
+ bool checkClipRect = false;
+ bool checkMaskLayers = false;
+
+ if (!idealTransform.Is2D(&contTransform)) {
+ // In 3D case, always check if we should use IntermediateSurface.
+ checkClipRect = true;
+ checkMaskLayers = true;
+ } else {
+ if (gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) {
+ checkClipRect = true;
+ }
+ /* In 2D case, only translation and/or positive scaling can be done w/o using IntermediateSurface.
+ * Otherwise, when rotation or flip happen, we should check whether to use IntermediateSurface.
+ */
+ if (contTransform.HasNonAxisAlignedTransform() || contTransform.HasNegativeScaling()) {
+ checkMaskLayers = true;
+ }
+ }
+
+ if (checkClipRect || checkMaskLayers) {
+ for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+ const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
+ /* We can't (easily) forward our transform to children with a non-empty clip
+ * rect since it would need to be adjusted for the transform. See
+ * the calculations performed by CalculateScissorRect above.
+ * Nor for a child with a mask layer.
+ */
+ if (checkClipRect && (clipRect && !clipRect->IsEmpty() && !child->GetLocalVisibleRegion().IsEmpty())) {
+ useIntermediateSurface = true;
+ break;
+ }
+ if (checkMaskLayers && child->HasMaskLayers()) {
+ useIntermediateSurface = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ NS_ASSERTION(!Extend3DContext() || !useIntermediateSurface, "Can't have an intermediate surface with preserve-3d!");
+
+ if (useIntermediateSurface) {
+ mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
+ } else {
+ mEffectiveTransform = idealTransform;
+ }
+
+ // For layers extending 3d context, its ideal transform should be
+ // applied on children.
+ if (!Extend3DContext()) {
+ // Without this projection, non-container children would get a 3D
+ // transform while 2D is expected.
+ idealTransform.ProjectTo2D();
+ }
+ mUseIntermediateSurface = useIntermediateSurface && !GetLocalVisibleRegion().IsEmpty();
+ if (useIntermediateSurface) {
+ ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
+ } else {
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+void
+ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
+{
+ if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
+ !Manager()->AreComponentAlphaLayersEnabled()) {
+ mSupportsComponentAlphaChildren = false;
+ if (aNeedsSurfaceCopy) {
+ *aNeedsSurfaceCopy = false;
+ }
+ return;
+ }
+
+ mSupportsComponentAlphaChildren = false;
+ bool needsSurfaceCopy = false;
+ CompositionOp blendMode = GetEffectiveMixBlendMode();
+ if (UseIntermediateSurface()) {
+ if (GetLocalVisibleRegion().GetNumRects() == 1 &&
+ (GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ mSupportsComponentAlphaChildren = true;
+ } else {
+ gfx::Matrix transform;
+ if (HasOpaqueAncestorLayer(this) &&
+ GetEffectiveTransform().Is2D(&transform) &&
+ !gfx::ThebesMatrix(transform).HasNonIntegerTranslation() &&
+ blendMode == gfx::CompositionOp::OP_OVER) {
+ mSupportsComponentAlphaChildren = true;
+ needsSurfaceCopy = true;
+ }
+ }
+ } else if (blendMode == gfx::CompositionOp::OP_OVER) {
+ mSupportsComponentAlphaChildren =
+ (GetContentFlags() & Layer::CONTENT_OPAQUE) ||
+ (GetParent() && GetParent()->SupportsComponentAlphaChildren());
+ }
+
+ if (aNeedsSurfaceCopy) {
+ *aNeedsSurfaceCopy = mSupportsComponentAlphaChildren && needsSurfaceCopy;
+ }
+}
+
+void
+ContainerLayer::ComputeEffectiveTransformsForChildren(const Matrix4x4& aTransformToSurface)
+{
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ l->ComputeEffectiveTransforms(aTransformToSurface);
+ }
+}
+
+/* static */ bool
+ContainerLayer::HasOpaqueAncestorLayer(Layer* aLayer)
+{
+ for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
+ if (l->GetContentFlags() & Layer::CONTENT_OPAQUE)
+ return true;
+ }
+ return false;
+}
+
+void
+ContainerLayer::DidRemoveChild(Layer* aLayer)
+{
+ PaintedLayer* tl = aLayer->AsPaintedLayer();
+ if (tl && tl->UsedForReadback()) {
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (l->GetType() == TYPE_READBACK) {
+ static_cast<ReadbackLayer*>(l)->NotifyPaintedLayerRemoved(tl);
+ }
+ }
+ }
+ if (aLayer->GetType() == TYPE_READBACK) {
+ static_cast<ReadbackLayer*>(aLayer)->NotifyRemoved();
+ }
+}
+
+void
+ContainerLayer::DidInsertChild(Layer* aLayer)
+{
+ if (aLayer->GetType() == TYPE_READBACK) {
+ mMayHaveReadbackChild = true;
+ }
+}
+
+void
+RefLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = RefLayerAttributes(GetReferentId(), mEventRegionsOverride);
+}
+
+/**
+ * StartFrameTimeRecording, together with StopFrameTimeRecording
+ * enable recording of frame intervals.
+ *
+ * To allow concurrent consumers, a cyclic array is used which serves all
+ * consumers, practically stateless with regard to consumers.
+ *
+ * To save resources, the buffer is allocated on first call to StartFrameTimeRecording
+ * and recording is paused if no consumer which called StartFrameTimeRecording is able
+ * to get valid results (because the cyclic buffer was overwritten since that call).
+ *
+ * To determine availability of the data upon StopFrameTimeRecording:
+ * - mRecording.mNextIndex increases on each PostPresent, and never resets.
+ * - Cyclic buffer position is realized as mNextIndex % bufferSize.
+ * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is called,
+ * the required start index is passed as an arg, and we're able to calculate the required
+ * length. If this length is bigger than bufferSize, it means data was overwritten.
+ * otherwise, we can return the entire sequence.
+ * - To determine if we need to pause, mLatestStartIndex is updated to mNextIndex
+ * on each call to StartFrameTimeRecording. If this index gets overwritten,
+ * it means that all earlier start indices obtained via StartFrameTimeRecording
+ * were also overwritten, hence, no point in recording, so pause.
+ * - mCurrentRunStartIndex indicates the oldest index of the recording after which
+ * the recording was not paused. If StopFrameTimeRecording is invoked with a start index
+ * older than this, it means that some frames were not recorded, so data is invalid.
+ */
+uint32_t
+LayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+ if (mRecording.mIsPaused) {
+ mRecording.mIsPaused = false;
+
+ if (!mRecording.mIntervals.Length()) { // Initialize recording buffers
+ mRecording.mIntervals.SetLength(aBufferSize);
+ }
+
+ // After being paused, recent values got invalid. Update them to now.
+ mRecording.mLastFrameTime = TimeStamp::Now();
+
+ // Any recording which started before this is invalid, since we were paused.
+ mRecording.mCurrentRunStartIndex = mRecording.mNextIndex;
+ }
+
+ // If we'll overwrite this index, there are no more consumers with aStartIndex
+ // for which we're able to provide the full recording, so no point in keep recording.
+ mRecording.mLatestStartIndex = mRecording.mNextIndex;
+ return mRecording.mNextIndex;
+}
+
+void
+LayerManager::RecordFrame()
+{
+ if (!mRecording.mIsPaused) {
+ TimeStamp now = TimeStamp::Now();
+ uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length();
+ mRecording.mIntervals[i] = static_cast<float>((now - mRecording.mLastFrameTime)
+ .ToMilliseconds());
+ mRecording.mNextIndex++;
+ mRecording.mLastFrameTime = now;
+
+ if (mRecording.mNextIndex > (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) {
+ // We've just overwritten the most recent recording start -> pause.
+ mRecording.mIsPaused = true;
+ }
+ }
+}
+
+void
+LayerManager::PostPresent()
+{
+ if (!mTabSwitchStart.IsNull()) {
+ mTabSwitchStart = TimeStamp();
+ }
+}
+
+void
+LayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals)
+{
+ uint32_t bufferSize = mRecording.mIntervals.Length();
+ uint32_t length = mRecording.mNextIndex - aStartIndex;
+ if (mRecording.mIsPaused || length > bufferSize || aStartIndex < mRecording.mCurrentRunStartIndex) {
+ // aStartIndex is too old. Also if aStartIndex was issued before mRecordingNextIndex overflowed (uint32_t)
+ // and stopped after the overflow (would happen once every 828 days of constant 60fps).
+ length = 0;
+ }
+
+ if (!length) {
+ aFrameIntervals.Clear();
+ return; // empty recording, return empty arrays.
+ }
+ // Set length in advance to avoid possibly repeated reallocations
+ aFrameIntervals.SetLength(length);
+
+ uint32_t cyclicPos = aStartIndex % bufferSize;
+ for (uint32_t i = 0; i < length; i++, cyclicPos++) {
+ if (cyclicPos == bufferSize) {
+ cyclicPos = 0;
+ }
+ aFrameIntervals[i] = mRecording.mIntervals[cyclicPos];
+ }
+}
+
+void
+LayerManager::BeginTabSwitch()
+{
+ mTabSwitchStart = TimeStamp::Now();
+}
+
+static void PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite);
+
+#ifdef MOZ_DUMP_PAINTING
+template <typename T>
+void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf)
+{
+ nsCString string(aObj->Name());
+ string.Append('-');
+ string.AppendInt((uint64_t)aObj);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
+ }
+ gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "\";");
+ }
+}
+
+void WriteSnapshotToDumpFile(Layer* aLayer, DataSourceSurface* aSurf)
+{
+ WriteSnapshotToDumpFile_internal(aLayer, aSurf);
+}
+
+void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf)
+{
+ WriteSnapshotToDumpFile_internal(aManager, aSurf);
+}
+
+void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget)
+{
+ RefPtr<SourceSurface> surf = aTarget->Snapshot();
+ RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
+ WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
+}
+#endif
+
+void
+Layer::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml, bool aSorted)
+{
+#ifdef MOZ_DUMP_PAINTING
+ bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsLayerComposite() &&
+ AsLayerComposite()->GetCompositableHost();
+ bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
+ AsShadowableLayer()->GetCompositableClient();
+ nsCString layerId(Name());
+ layerId.Append('-');
+ layerId.AppendInt((uint64_t)this);
+#endif
+ if (aDumpHtml) {
+ aStream << nsPrintfCString("<li><a id=\"%p\" ", this).get();
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpCompositorTexture || dumpClientTexture) {
+ aStream << nsPrintfCString("href=\"javascript:ViewImage('%s')\"", layerId.BeginReading()).get();
+ }
+#endif
+ aStream << ">";
+ }
+ DumpSelf(aStream, aPrefix);
+
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpCompositorTexture) {
+ AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
+ } else if (dumpClientTexture) {
+ if (aDumpHtml) {
+ aStream << nsPrintfCString("<script>array[\"%s\"]=\"", layerId.BeginReading()).get();
+ }
+ AsShadowableLayer()->GetCompositableClient()->Dump(aStream, aPrefix,
+ aDumpHtml, TextureDumpMode::DoNotCompress);
+ if (aDumpHtml) {
+ aStream << "\";</script>";
+ }
+ }
+#endif
+
+ if (aDumpHtml) {
+ aStream << "</a>";
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpClientTexture) {
+ aStream << nsPrintfCString("<br><img id=\"%s\">\n", layerId.BeginReading()).get();
+ }
+#endif
+ }
+
+ if (Layer* mask = GetMaskLayer()) {
+ aStream << nsPrintfCString("%s Mask layer:\n", aPrefix).get();
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mask->Dump(aStream, pfx.get(), aDumpHtml);
+ }
+
+ for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+ aStream << nsPrintfCString("%s Ancestor mask layer %d:\n", aPrefix, uint32_t(i)).get();
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ GetAncestorMaskLayerAt(i)->Dump(aStream, pfx.get(), aDumpHtml);
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
+ const nsCString& str = mExtraDumpInfo[i];
+ aStream << aPrefix << " Info:\n" << str.get();
+ }
+#endif
+
+ if (ContainerLayer* container = AsContainerLayer()) {
+ AutoTArray<Layer*, 12> children;
+ if (aSorted) {
+ container->SortChildrenBy3DZOrder(children);
+ } else {
+ for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
+ children.AppendElement(l);
+ }
+ }
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+
+ for (Layer* child : children) {
+ child->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
+ }
+
+ if (aDumpHtml) {
+ aStream << "</ul>";
+ }
+ }
+
+ if (aDumpHtml) {
+ aStream << "</li>";
+ }
+}
+
+void
+Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix)
+{
+ PrintInfo(aStream, aPrefix);
+ aStream << "\n";
+}
+
+void
+Layer::Dump(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ DumpPacket(aPacket, aParent);
+
+ if (Layer* kid = GetFirstChild()) {
+ kid->Dump(aPacket, this);
+ }
+
+ if (Layer* next = GetNextSibling()) {
+ next->Dump(aPacket, aParent);
+ }
+}
+
+void
+Layer::SetDisplayListLog(const char* log)
+{
+ if (gfxUtils::DumpDisplayList()) {
+ mDisplayListLog = log;
+ }
+}
+
+void
+Layer::GetDisplayListLog(nsCString& log)
+{
+ log.SetLength(0);
+
+ if (gfxUtils::DumpDisplayList()) {
+ // This function returns a plain text string which consists of two things
+ // 1. DisplayList log.
+ // 2. Memory address of this layer.
+ // We know the target layer of each display item by information in #1.
+ // Here is an example of a Text display item line log in #1
+ // Text p=0xa9850c00 f=0x0xaa405b00(.....
+ // f keeps the address of the target client layer of a display item.
+ // For LayerScope, display-item-to-client-layer mapping is not enough since
+ // LayerScope, which lives in the chrome process, knows only composite layers.
+ // As so, we need display-item-to-client-layer-to-layer-composite
+ // mapping. That's the reason we insert #2 into the log
+ log.AppendPrintf("0x%p\n%s",(void*) this, mDisplayListLog.get());
+ }
+}
+
+void
+Layer::Log(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ LogSelf(aPrefix);
+
+ if (Layer* kid = GetFirstChild()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ kid->Log(pfx.get());
+ }
+
+ if (Layer* next = GetNextSibling())
+ next->Log(aPrefix);
+}
+
+void
+Layer::LogSelf(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ std::stringstream ss;
+ PrintInfo(ss, aPrefix);
+ MOZ_LAYERS_LOG(("%s", ss.str().c_str()));
+
+ if (mMaskLayer) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " \\ MaskLayer ";
+ mMaskLayer->LogSelf(pfx.get());
+ }
+}
+
+void
+Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this).get();
+
+ layers::PrintInfo(aStream, AsLayerComposite());
+
+ if (mClipRect) {
+ AppendToString(aStream, *mClipRect, " [clip=", "]");
+ }
+ if (mScrolledClip) {
+ AppendToString(aStream, mScrolledClip->GetClipRect(), " [scrolled-clip=", "]");
+ }
+ if (1.0 != mPostXScale || 1.0 != mPostYScale) {
+ aStream << nsPrintfCString(" [postScale=%g, %g]", mPostXScale, mPostYScale).get();
+ }
+ if (!mTransform.IsIdentity()) {
+ AppendToString(aStream, mTransform, " [transform=", "]");
+ }
+ if (!GetEffectiveTransform().IsIdentity()) {
+ AppendToString(aStream, GetEffectiveTransform(), " [effective-transform=", "]");
+ }
+ if (mTransformIsPerspective) {
+ aStream << " [perspective]";
+ }
+ if (!mLayerBounds.IsEmpty()) {
+ AppendToString(aStream, mLayerBounds, " [bounds=", "]");
+ }
+ if (!mVisibleRegion.IsEmpty()) {
+ AppendToString(aStream, mVisibleRegion.ToUnknownRegion(), " [visible=", "]");
+ } else {
+ aStream << " [not visible]";
+ }
+ if (!mEventRegions.IsEmpty()) {
+ AppendToString(aStream, mEventRegions, " ", "");
+ }
+ if (1.0 != mOpacity) {
+ aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get();
+ }
+ if (IsOpaque()) {
+ aStream << " [opaqueContent]";
+ }
+ if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
+ aStream << " [componentAlpha]";
+ }
+ if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ aStream << " [backfaceHidden]";
+ }
+ if (Extend3DContext()) {
+ aStream << " [extend3DContext]";
+ }
+ if (Combines3DTransformWithAncestors()) {
+ aStream << " [combines3DTransformWithAncestors]";
+ }
+ if (Is3DContextLeaf()) {
+ aStream << " [is3DContextLeaf]";
+ }
+ if (IsScrollbarContainer()) {
+ aStream << " [scrollbar]";
+ }
+ if (GetScrollbarDirection() == VERTICAL) {
+ aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
+ }
+ if (GetScrollbarDirection() == HORIZONTAL) {
+ aStream << nsPrintfCString(" [hscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
+ }
+ if (GetIsFixedPosition()) {
+ LayerPoint anchor = GetFixedPositionAnchor();
+ aStream << nsPrintfCString(" [isFixedPosition scrollId=%lld sides=0x%x anchor=%s]",
+ GetFixedPositionScrollContainerId(),
+ GetFixedPositionSides(),
+ ToString(anchor).c_str()).get();
+ }
+ if (GetIsStickyPosition()) {
+ aStream << nsPrintfCString(" [isStickyPosition scrollId=%d outer=(%.3f,%.3f)-(%.3f,%.3f) "
+ "inner=(%.3f,%.3f)-(%.3f,%.3f)]", mStickyPositionData->mScrollId,
+ mStickyPositionData->mOuter.x, mStickyPositionData->mOuter.y,
+ mStickyPositionData->mOuter.XMost(), mStickyPositionData->mOuter.YMost(),
+ mStickyPositionData->mInner.x, mStickyPositionData->mInner.y,
+ mStickyPositionData->mInner.XMost(), mStickyPositionData->mInner.YMost()).get();
+ }
+ if (mMaskLayer) {
+ aStream << nsPrintfCString(" [mMaskLayer=%p]", mMaskLayer.get()).get();
+ }
+ for (uint32_t i = 0; i < mScrollMetadata.Length(); i++) {
+ if (!mScrollMetadata[i].IsDefault()) {
+ aStream << nsPrintfCString(" [metrics%d=", i).get();
+ AppendToString(aStream, mScrollMetadata[i], "", "]");
+ }
+ }
+}
+
+// The static helper function sets the transform matrix into the packet
+static void
+DumpTransform(layerscope::LayersPacket::Layer::Matrix* aLayerMatrix, const Matrix4x4& aMatrix)
+{
+ aLayerMatrix->set_is2d(aMatrix.Is2D());
+ if (aMatrix.Is2D()) {
+ Matrix m = aMatrix.As2D();
+ aLayerMatrix->set_isid(m.IsIdentity());
+ if (!m.IsIdentity()) {
+ aLayerMatrix->add_m(m._11), aLayerMatrix->add_m(m._12);
+ aLayerMatrix->add_m(m._21), aLayerMatrix->add_m(m._22);
+ aLayerMatrix->add_m(m._31), aLayerMatrix->add_m(m._32);
+ }
+ } else {
+ aLayerMatrix->add_m(aMatrix._11), aLayerMatrix->add_m(aMatrix._12);
+ aLayerMatrix->add_m(aMatrix._13), aLayerMatrix->add_m(aMatrix._14);
+ aLayerMatrix->add_m(aMatrix._21), aLayerMatrix->add_m(aMatrix._22);
+ aLayerMatrix->add_m(aMatrix._23), aLayerMatrix->add_m(aMatrix._24);
+ aLayerMatrix->add_m(aMatrix._31), aLayerMatrix->add_m(aMatrix._32);
+ aLayerMatrix->add_m(aMatrix._33), aLayerMatrix->add_m(aMatrix._34);
+ aLayerMatrix->add_m(aMatrix._41), aLayerMatrix->add_m(aMatrix._42);
+ aLayerMatrix->add_m(aMatrix._43), aLayerMatrix->add_m(aMatrix._44);
+ }
+}
+
+// The static helper function sets the IntRect into the packet
+template <typename T, typename Sub, typename Point, typename SizeT, typename MarginT>
+static void
+DumpRect(layerscope::LayersPacket::Layer::Rect* aLayerRect,
+ const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect)
+{
+ aLayerRect->set_x(aRect.x);
+ aLayerRect->set_y(aRect.y);
+ aLayerRect->set_w(aRect.width);
+ aLayerRect->set_h(aRect.height);
+}
+
+// The static helper function sets the nsIntRegion into the packet
+static void
+DumpRegion(layerscope::LayersPacket::Layer::Region* aLayerRegion, const nsIntRegion& aRegion)
+{
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ DumpRect(aLayerRegion->add_r(), iter.Get());
+ }
+}
+
+void
+Layer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ // Add a new layer (UnknownLayer)
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->add_layer();
+ // Basic information
+ layer->set_type(LayersPacket::Layer::UnknownLayer);
+ layer->set_ptr(reinterpret_cast<uint64_t>(this));
+ layer->set_parentptr(reinterpret_cast<uint64_t>(aParent));
+ // Shadow
+ if (LayerComposite* lc = AsLayerComposite()) {
+ LayersPacket::Layer::Shadow* s = layer->mutable_shadow();
+ if (const Maybe<ParentLayerIntRect>& clipRect = lc->GetShadowClipRect()) {
+ DumpRect(s->mutable_clip(), *clipRect);
+ }
+ if (!lc->GetShadowBaseTransform().IsIdentity()) {
+ DumpTransform(s->mutable_transform(), lc->GetShadowBaseTransform());
+ }
+ if (!lc->GetShadowVisibleRegion().IsEmpty()) {
+ DumpRegion(s->mutable_vregion(), lc->GetShadowVisibleRegion().ToUnknownRegion());
+ }
+ }
+ // Clip
+ if (mClipRect) {
+ DumpRect(layer->mutable_clip(), *mClipRect);
+ }
+ // Transform
+ if (!mTransform.IsIdentity()) {
+ DumpTransform(layer->mutable_transform(), mTransform);
+ }
+ // Visible region
+ if (!mVisibleRegion.ToUnknownRegion().IsEmpty()) {
+ DumpRegion(layer->mutable_vregion(), mVisibleRegion.ToUnknownRegion());
+ }
+ // EventRegions
+ if (!mEventRegions.IsEmpty()) {
+ const EventRegions &e = mEventRegions;
+ if (!e.mHitRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_hitregion(), e.mHitRegion);
+ }
+ if (!e.mDispatchToContentHitRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_dispatchregion(), e.mDispatchToContentHitRegion);
+ }
+ if (!e.mNoActionRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_noactionregion(), e.mNoActionRegion);
+ }
+ if (!e.mHorizontalPanRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_hpanregion(), e.mHorizontalPanRegion);
+ }
+ if (!e.mVerticalPanRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_vpanregion(), e.mVerticalPanRegion);
+ }
+ }
+ // Opacity
+ layer->set_opacity(mOpacity);
+ // Content opaque
+ layer->set_copaque(static_cast<bool>(GetContentFlags() & CONTENT_OPAQUE));
+ // Component alpha
+ layer->set_calpha(static_cast<bool>(GetContentFlags() & CONTENT_COMPONENT_ALPHA));
+ // Vertical or horizontal bar
+ if (GetScrollbarDirection() != NONE) {
+ layer->set_direct(GetScrollbarDirection() == VERTICAL ?
+ LayersPacket::Layer::VERTICAL :
+ LayersPacket::Layer::HORIZONTAL);
+ layer->set_barid(GetScrollbarTargetContainerId());
+ }
+
+ // Mask layer
+ if (mMaskLayer) {
+ layer->set_mask(reinterpret_cast<uint64_t>(mMaskLayer.get()));
+ }
+
+ // DisplayList log.
+ if (mDisplayListLog.Length() > 0) {
+ layer->set_displaylistloglength(mDisplayListLog.Length());
+ auto compressedData =
+ MakeUnique<char[]>(LZ4::maxCompressedSize(mDisplayListLog.Length()));
+ int compressedSize = LZ4::compress((char*)mDisplayListLog.get(),
+ mDisplayListLog.Length(),
+ compressedData.get());
+ layer->set_displaylistlog(compressedData.get(), compressedSize);
+ }
+}
+
+bool
+Layer::IsBackfaceHidden()
+{
+ if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ Layer* container = AsContainerLayer() ? this : GetParent();
+ if (container) {
+ // The effective transform can include non-preserve-3d parent
+ // transforms, since we don't always require an intermediate.
+ if (container->Extend3DContext() || container->Is3DContextLeaf()) {
+ return container->GetEffectiveTransform().IsBackfaceVisible();
+ }
+ return container->GetBaseTransform().IsBackfaceVisible();
+ }
+ }
+ return false;
+}
+
+UniquePtr<LayerUserData>
+Layer::RemoveUserData(void* aKey)
+{
+ UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+ return d;
+}
+
+void
+PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (!mValidRegion.IsEmpty()) {
+ AppendToString(aStream, mValidRegion, " [valid=", "]");
+ }
+}
+
+void
+PaintedLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::PaintedLayer);
+ if (!mValidRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_valid(), mValidRegion);
+ }
+}
+
+void
+ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (UseIntermediateSurface()) {
+ aStream << " [usesTmpSurf]";
+ }
+ if (1.0 != mPreXScale || 1.0 != mPreYScale) {
+ aStream << nsPrintfCString(" [preScale=%g, %g]", mPreXScale, mPreYScale).get();
+ }
+ if (mScaleToResolution) {
+ aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get();
+ }
+ if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) {
+ aStream << " [force-dtc]";
+ }
+ if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) {
+ aStream << " [force-ehr]";
+ }
+}
+
+void
+ContainerLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ContainerLayer);
+}
+
+void
+ColorLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ AppendToString(aStream, mColor, " [color=", "]");
+ AppendToString(aStream, mBounds, " [bounds=", "]");
+}
+
+void
+ColorLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ColorLayer);
+ layer->set_color(mColor.ToABGR());
+}
+
+CanvasLayer::CanvasLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData)
+ , mPreTransCallback(nullptr)
+ , mPreTransCallbackData(nullptr)
+ , mPostTransCallback(nullptr)
+ , mPostTransCallbackData(nullptr)
+ , mSamplingFilter(gfx::SamplingFilter::GOOD)
+ , mDirty(false)
+{}
+
+CanvasLayer::~CanvasLayer()
+{}
+
+void
+CanvasLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (mSamplingFilter != SamplingFilter::GOOD) {
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+ }
+}
+
+// This help function is used to assign the correct enum value
+// to the packet
+static void
+DumpFilter(layerscope::LayersPacket::Layer* aLayer,
+ const SamplingFilter& aSamplingFilter)
+{
+ using namespace layerscope;
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_GOOD);
+ break;
+ case SamplingFilter::LINEAR:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_LINEAR);
+ break;
+ case SamplingFilter::POINT:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_POINT);
+ break;
+ default:
+ // ignore it
+ break;
+ }
+}
+
+void
+CanvasLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::CanvasLayer);
+ DumpFilter(layer, mSamplingFilter);
+}
+
+void
+ImageLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (mSamplingFilter != SamplingFilter::GOOD) {
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+ }
+}
+
+void
+ImageLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ImageLayer);
+ DumpFilter(layer, mSamplingFilter);
+}
+
+void
+RefLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ ContainerLayer::PrintInfo(aStream, aPrefix);
+ if (0 != mId) {
+ AppendToString(aStream, mId, " [id=", "]");
+ }
+}
+
+void
+RefLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::RefLayer);
+ layer->set_refid(mId);
+}
+
+void
+ReadbackLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ AppendToString(aStream, mSize, " [size=", "]");
+ if (mBackgroundLayer) {
+ AppendToString(aStream, mBackgroundLayer, " [backgroundLayer=", "]");
+ AppendToString(aStream, mBackgroundLayerOffset, " [backgroundOffset=", "]");
+ } else if (mBackgroundColor.a == 1.f) {
+ AppendToString(aStream, mBackgroundColor, " [backgroundColor=", "]");
+ } else {
+ aStream << " [nobackground]";
+ }
+}
+
+void
+ReadbackLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ReadbackLayer);
+ LayersPacket::Layer::Size* size = layer->mutable_size();
+ size->set_w(mSize.width);
+ size->set_h(mSize.height);
+}
+
+//--------------------------------------------------
+// LayerManager
+
+void
+LayerManager::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml, bool aSorted)
+{
+#ifdef MOZ_DUMP_PAINTING
+ if (aDumpHtml) {
+ aStream << "<ul><li>";
+ }
+#endif
+ DumpSelf(aStream, aPrefix, aSorted);
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (!GetRoot()) {
+ aStream << nsPrintfCString("%s(null)", pfx.get()).get();
+ if (aDumpHtml) {
+ aStream << "</li></ul>";
+ }
+ return;
+ }
+
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+ GetRoot()->Dump(aStream, pfx.get(), aDumpHtml);
+ if (aDumpHtml) {
+ aStream << "</ul></li></ul>";
+ }
+ aStream << "\n";
+}
+
+void
+LayerManager::DumpSelf(std::stringstream& aStream, const char* aPrefix, bool aSorted)
+{
+ PrintInfo(aStream, aPrefix);
+ aStream << " --- in " << (aSorted ? "3D-sorted rendering order" : "content order");
+ aStream << "\n";
+}
+
+void
+LayerManager::Dump(bool aSorted)
+{
+ std::stringstream ss;
+ Dump(ss, "", false, aSorted);
+ print_stderr(ss);
+}
+
+void
+LayerManager::Dump(layerscope::LayersPacket* aPacket)
+{
+ DumpPacket(aPacket);
+
+ if (GetRoot()) {
+ GetRoot()->Dump(aPacket, this);
+ }
+}
+
+void
+LayerManager::Log(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ LogSelf(aPrefix);
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (!GetRoot()) {
+ MOZ_LAYERS_LOG(("%s(null)", pfx.get()));
+ return;
+ }
+
+ GetRoot()->Log(pfx.get());
+}
+
+void
+LayerManager::LogSelf(const char* aPrefix)
+{
+ nsAutoCString str;
+ std::stringstream ss;
+ PrintInfo(ss, aPrefix);
+ MOZ_LAYERS_LOG(("%s", ss.str().c_str()));
+}
+
+void
+LayerManager::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix << nsPrintfCString("%sLayerManager (0x%p)", Name(), this).get();
+}
+
+void
+LayerManager::DumpPacket(layerscope::LayersPacket* aPacket)
+{
+ using namespace layerscope;
+ // Add a new layer data (LayerManager)
+ LayersPacket::Layer* layer = aPacket->add_layer();
+ layer->set_type(LayersPacket::Layer::LayerManager);
+ layer->set_ptr(reinterpret_cast<uint64_t>(this));
+ // Layer Tree Root
+ layer->set_parentptr(0);
+}
+
+/*static*/ bool
+LayerManager::IsLogEnabled()
+{
+ return MOZ_LOG_TEST(GetLog(), LogLevel::Debug);
+}
+
+void
+LayerManager::SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo)
+{
+ mPendingScrollUpdates[aScrollId] = aUpdateInfo;
+}
+
+Maybe<ScrollUpdateInfo>
+LayerManager::GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId)
+{
+ auto it = mPendingScrollUpdates.find(aScrollId);
+ if (it != mPendingScrollUpdates.end()) {
+ return Some(it->second);
+ }
+ return Nothing();
+}
+
+void
+LayerManager::ClearPendingScrollInfoUpdate()
+{
+ mPendingScrollUpdates.clear();
+}
+
+void
+PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite)
+{
+ if (!aLayerComposite) {
+ return;
+ }
+ if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
+ AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
+ }
+ if (!aLayerComposite->GetShadowBaseTransform().IsIdentity()) {
+ AppendToString(aStream, aLayerComposite->GetShadowBaseTransform(), " [shadow-transform=", "]");
+ }
+ if (!aLayerComposite->GetShadowVisibleRegion().IsEmpty()) {
+ AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion().ToUnknownRegion(), " [shadow-visible=", "]");
+ }
+}
+
+void
+SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget)
+{
+ bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA);
+ if (aTarget->IsCurrentGroupOpaque()) {
+ aTarget->SetPermitSubpixelAA(permitSubpixelAA);
+ return;
+ }
+
+ const IntRect& bounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
+ gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y),
+ Float(bounds.width), Float(bounds.height)));
+ transformedBounds.RoundOut();
+ IntRect intTransformedBounds;
+ transformedBounds.ToIntRect(&intTransformedBounds);
+ permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
+ aTarget->GetOpaqueRect().Contains(intTransformedBounds);
+ aTarget->SetPermitSubpixelAA(permitSubpixelAA);
+}
+
+IntRect
+ToOutsideIntRect(const gfxRect &aRect)
+{
+ return IntRect::RoundOut(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/Layers.h b/system/graphics/layers/Layers.h
new file mode 100644
index 000000000..805d41d48
--- /dev/null
+++ b/system/graphics/layers/Layers.h
@@ -0,0 +1,2619 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_H
+#define GFX_LAYERS_H
+
+#include <map>
+#include <stdint.h> // for uint32_t, uint64_t, uint8_t
+#include <stdio.h> // for FILE
+#include <sys/types.h> // for int32_t, int64_t
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect
+#include "gfxContext.h"
+#include "gfxTypes.h"
+#include "gfxPoint.h" // for gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/EventForwards.h" // for nsPaintEvent
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/Poison.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/BaseMargin.h" // for BaseMargin
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/TiledRegion.h" // for TiledIntRegion
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/gfx/UserData.h" // for UserData, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAutoPtr.h" // for nsAutoPtr, nsRefPtr, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsCSSPropertyID.h" // for nsCSSPropertyID
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsString.h" // for nsCString
+#include "nsTArray.h" // for nsTArray
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nscore.h" // for nsACString, nsAString
+#include "mozilla/Logging.h" // for PRLogModuleInfo
+#include "nsIWidget.h" // For plugin window configuration information structs
+#include "ImageContainer.h"
+
+class gfxContext;
+
+extern uint8_t gLayerManagerLayerBuilder;
+
+namespace mozilla {
+
+class ComputedTimingFunction;
+class FrameLayerBuilder;
+class StyleAnimationValue;
+
+namespace gl {
+class GLContext;
+} // namespace gl
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace dom {
+class OverfillCallback;
+} // namespace dom
+
+namespace layers {
+
+class Animation;
+class AnimationData;
+class AsyncCanvasRenderer;
+class AsyncPanZoomController;
+class BasicLayerManager;
+class ClientLayerManager;
+class Layer;
+class LayerMetricsWrapper;
+class PaintedLayer;
+class ContainerLayer;
+class ImageLayer;
+class ColorLayer;
+class CanvasLayer;
+class ReadbackLayer;
+class ReadbackProcessor;
+class RefLayer;
+class LayerComposite;
+class ShadowableLayer;
+class ShadowLayerForwarder;
+class LayerManagerComposite;
+class SpecificLayerAttributes;
+class Compositor;
+class FrameUniformityData;
+class PersistentBufferProvider;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+#define MOZ_LAYER_DECL_NAME(n, e) \
+ virtual const char* Name() const override { return n; } \
+ virtual LayerType GetType() const override { return e; }
+
+// Defined in LayerUserData.h; please include that file instead.
+class LayerUserData;
+
+/*
+ * Motivation: For truly smooth animation and video playback, we need to
+ * be able to compose frames and render them on a dedicated thread (i.e.
+ * off the main thread where DOM manipulation, script execution and layout
+ * induce difficult-to-bound latency). This requires Gecko to construct
+ * some kind of persistent scene structure (graph or tree) that can be
+ * safely transmitted across threads. We have other scenarios (e.g. mobile
+ * browsing) where retaining some rendered data between paints is desired
+ * for performance, so again we need a retained scene structure.
+ *
+ * Our retained scene structure is a layer tree. Each layer represents
+ * content which can be composited onto a destination surface; the root
+ * layer is usually composited into a window, and non-root layers are
+ * composited into their parent layers. Layers have attributes (e.g.
+ * opacity and clipping) that influence their compositing.
+ *
+ * We want to support a variety of layer implementations, including
+ * a simple "immediate mode" implementation that doesn't retain any
+ * rendered data between paints (i.e. uses cairo in just the way that
+ * Gecko used it before layers were introduced). But we also don't want
+ * to have bifurcated "layers"/"non-layers" rendering paths in Gecko.
+ * Therefore the layers API is carefully designed to permit maximally
+ * efficient implementation in an "immediate mode" style. See the
+ * BasicLayerManager for such an implementation.
+ */
+
+/**
+ * A LayerManager controls a tree of layers. All layers in the tree
+ * must use the same LayerManager.
+ *
+ * All modifications to a layer tree must happen inside a transaction.
+ * Only the state of the layer tree at the end of a transaction is
+ * rendered. Transactions cannot be nested
+ *
+ * Each transaction has two phases:
+ * 1) Construction: layers are created, inserted, removed and have
+ * properties set on them in this phase.
+ * BeginTransaction and BeginTransactionWithTarget start a transaction in
+ * the Construction phase.
+ * 2) Drawing: PaintedLayers are rendered into in this phase, in tree
+ * order. When the client has finished drawing into the PaintedLayers, it should
+ * call EndTransaction to complete the transaction.
+ *
+ * All layer API calls happen on the main thread.
+ *
+ * Layers are refcounted. The layer manager holds a reference to the
+ * root layer, and each container layer holds a reference to its children.
+ */
+class LayerManager {
+ NS_INLINE_DECL_REFCOUNTING(LayerManager)
+
+protected:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+public:
+ LayerManager()
+ : mDestroyed(false)
+ , mSnapEffectiveTransforms(true)
+ , mId(0)
+ , mInTransaction(false)
+ , mPaintedPixelCount(0)
+ {}
+
+ /**
+ * Release layers and resources held by this layer manager, and mark
+ * it as destroyed. Should do any cleanup necessary in preparation
+ * for its widget going away. After this call, only user data calls
+ * are valid on the layer manager.
+ */
+ virtual void Destroy()
+ {
+ mDestroyed = true;
+ mUserData.Destroy();
+ mRoot = nullptr;
+ }
+ bool IsDestroyed() { return mDestroyed; }
+
+ virtual ShadowLayerForwarder* AsShadowForwarder()
+ { return nullptr; }
+
+ virtual LayerManagerComposite* AsLayerManagerComposite()
+ { return nullptr; }
+
+ virtual ClientLayerManager* AsClientLayerManager()
+ { return nullptr; }
+
+ virtual BasicLayerManager* AsBasicLayerManager()
+ { return nullptr; }
+
+ /**
+ * Returns true if this LayerManager is owned by an nsIWidget,
+ * and is used for drawing into the widget.
+ */
+ virtual bool IsWidgetLayerManager() { return true; }
+ virtual bool IsInactiveLayerManager() { return false; }
+
+ /**
+ * Start a new transaction. Nested transactions are not allowed so
+ * there must be no transaction currently in progress.
+ * This transaction will update the state of the window from which
+ * this LayerManager was obtained.
+ */
+ virtual bool BeginTransaction() = 0;
+ /**
+ * Start a new transaction. Nested transactions are not allowed so
+ * there must be no transaction currently in progress.
+ * This transaction will render the contents of the layer tree to
+ * the given target context. The rendering will be complete when
+ * EndTransaction returns.
+ */
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) = 0;
+
+ enum EndTransactionFlags {
+ END_DEFAULT = 0,
+ END_NO_IMMEDIATE_REDRAW = 1 << 0, // Do not perform the drawing phase
+ END_NO_COMPOSITE = 1 << 1, // Do not composite after drawing painted layer contents.
+ END_NO_REMOTE_COMPOSITE = 1 << 2 // Do not schedule a composition with a remote Compositor, if one exists.
+ };
+
+ FrameLayerBuilder* GetLayerBuilder() {
+ return reinterpret_cast<FrameLayerBuilder*>(GetUserData(&gLayerManagerLayerBuilder));
+ }
+
+ /**
+ * Attempts to end an "empty transaction". There must have been no
+ * changes to the layer tree since the BeginTransaction().
+ * It's possible for this to fail; PaintedLayers may need to be updated
+ * due to VRAM data being lost, for example. In such cases this method
+ * returns false, and the caller must proceed with a normal layer tree
+ * update and EndTransaction.
+ */
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) = 0;
+
+ /**
+ * Function called to draw the contents of each PaintedLayer.
+ * aRegionToDraw contains the region that needs to be drawn.
+ * This would normally be a subregion of the visible region.
+ * The callee must draw all of aRegionToDraw. Drawing outside
+ * aRegionToDraw will be clipped out or ignored.
+ * The callee must draw all of aRegionToDraw.
+ * This region is relative to 0,0 in the PaintedLayer.
+ *
+ * aDirtyRegion should contain the total region that is be due to be painted
+ * during the transaction, even though only aRegionToDraw should be drawn
+ * during this call. aRegionToDraw must be entirely contained within
+ * aDirtyRegion. If the total dirty region is unknown it is okay to pass a
+ * subregion of the total dirty region, e.g. just aRegionToDraw, though it
+ * may not be as efficient.
+ *
+ * aRegionToInvalidate contains a region whose contents have been
+ * changed by the layer manager and which must therefore be invalidated.
+ * For example, this could be non-empty if a retained layer internally
+ * switches from RGBA to RGB or back ... we might want to repaint it to
+ * consistently use subpixel-AA or not.
+ * This region is relative to 0,0 in the PaintedLayer.
+ * aRegionToInvalidate may contain areas that are outside
+ * aRegionToDraw; the callee must ensure that these areas are repainted
+ * in the current layer manager transaction or in a later layer
+ * manager transaction.
+ *
+ * aContext must not be used after the call has returned.
+ * We guarantee that buffered contents in the visible
+ * region are valid once drawing is complete.
+ *
+ * The origin of aContext is 0,0 in the PaintedLayer.
+ */
+ typedef void (* DrawPaintedLayerCallback)(PaintedLayer* aLayer,
+ gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aDirtyRegion,
+ DrawRegionClip aClip,
+ const nsIntRegion& aRegionToInvalidate,
+ void* aCallbackData);
+
+ /**
+ * Finish the construction phase of the transaction, perform the
+ * drawing phase, and end the transaction.
+ * During the drawing phase, all PaintedLayers in the tree are
+ * drawn in tree order, exactly once each, except for those layers
+ * where it is known that the visible region is empty.
+ */
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) = 0;
+
+ /**
+ * Schedule a composition with the remote Compositor, if one exists
+ * for this LayerManager. Useful in conjunction with the END_NO_REMOTE_COMPOSITE
+ * flag to EndTransaction.
+ */
+ virtual void Composite() {}
+
+ virtual bool HasShadowManagerInternal() const { return false; }
+ bool HasShadowManager() const { return HasShadowManagerInternal(); }
+ virtual void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations) {}
+ bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
+
+
+ /**
+ * Returns true if the layer manager can't render component alpha
+ * layers, and layer building should do it's best to avoid
+ * creating them.
+ */
+ virtual bool ShouldAvoidComponentAlphaLayers() { return false; }
+
+ /**
+ * Returns true if this LayerManager can properly support layers with
+ * SurfaceMode::SURFACE_COMPONENT_ALPHA. LayerManagers that can't will use
+ * transparent surfaces (and lose subpixel-AA for text).
+ */
+ virtual bool AreComponentAlphaLayersEnabled();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the root layer. The root layer is initially null. If there is
+ * no root layer, EndTransaction won't draw anything.
+ */
+ virtual void SetRoot(Layer* aLayer) = 0;
+ /**
+ * Can be called anytime
+ */
+ Layer* GetRoot() { return mRoot; }
+
+ /**
+ * Does a breadth-first search from the root layer to find the first
+ * scrollable layer, and returns its ViewID. Note that there may be
+ * other layers in the tree which share the same ViewID.
+ * Can be called any time.
+ */
+ FrameMetrics::ViewID GetRootScrollableLayerId();
+
+ /**
+ * Returns a LayerMetricsWrapper containing the Root
+ * Content Documents layer.
+ */
+ LayerMetricsWrapper GetRootContentLayer();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Called when a managee has mutated.
+ * Subclasses overriding this method must first call their
+ * superclass's impl
+ */
+#ifdef DEBUG
+ // In debug builds, we check some properties of |aLayer|.
+ virtual void Mutated(Layer* aLayer);
+#else
+ virtual void Mutated(Layer* aLayer) { }
+#endif
+
+ /**
+ * Hints that can be used during PaintedLayer creation to influence the type
+ * or properties of the layer created.
+ *
+ * NONE: No hint.
+ * SCROLLABLE: This layer may represent scrollable content.
+ */
+ enum PaintedLayerCreationHint {
+ NONE, SCROLLABLE
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a PaintedLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a PaintedLayer for this manager's layer tree, with a creation hint
+ * parameter to help optimise the type of layer created.
+ */
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint) {
+ return CreatePaintedLayer();
+ }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ContainerLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create an ImageLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ColorLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a CanvasLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ReadbackLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nullptr; }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a RefLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<RefLayer> CreateRefLayer() { return nullptr; }
+
+
+ /**
+ * Can be called anytime, from any thread.
+ *
+ * Creates an Image container which forwards its images to the compositor within
+ * layer transactions on the main thread or asynchronously using the ImageBridge IPDL protocol.
+ * In the case of asynchronous, If the protocol is not available, the returned ImageContainer
+ * will forward images within layer transactions.
+ */
+ static already_AddRefed<ImageContainer> CreateImageContainer(ImageContainer::Mode flag
+ = ImageContainer::SYNCHRONOUS);
+
+ /**
+ * Type of layer manager his is. This is to be used sparsely in order to
+ * avoid a lot of Layers backend specific code. It should be used only when
+ * Layers backend specific functionality is necessary.
+ */
+ virtual LayersBackend GetBackendType() = 0;
+
+ /**
+ * Type of layers backend that will be used to composite this layer tree.
+ * When compositing is done remotely, then this returns the layers type
+ * of the compositor.
+ */
+ virtual LayersBackend GetCompositorBackendType() { return GetBackendType(); }
+
+ /**
+ * Creates a DrawTarget which is optimized for inter-operating with this
+ * layer manager.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalDrawTarget(const IntSize &aSize,
+ SurfaceFormat imageFormat);
+
+ /**
+ * Creates a DrawTarget for alpha masks which is optimized for inter-
+ * operating with this layer manager. In contrast to CreateOptimalDrawTarget,
+ * this surface is optimised for drawing alpha only and we assume that
+ * drawing the mask is fairly simple.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalMaskDrawTarget(const IntSize &aSize);
+
+ /**
+ * Creates a DrawTarget for use with canvas which is optimized for
+ * inter-operating with this layermanager.
+ */
+ virtual already_AddRefed<mozilla::gfx::DrawTarget>
+ CreateDrawTarget(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat);
+
+ /**
+ * Creates a PersistentBufferProvider for use with canvas which is optimized for
+ * inter-operating with this layermanager.
+ */
+ virtual already_AddRefed<PersistentBufferProvider>
+ CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat);
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) { return true; }
+
+ /**
+ * returns the maximum texture size on this layer backend, or INT32_MAX
+ * if there is no maximum
+ */
+ virtual int32_t GetMaxTextureSize() const = 0;
+
+ /**
+ * Return the name of the layer manager's backend.
+ */
+ virtual void GetBackendName(nsAString& aName) = 0;
+
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ {
+ mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerUserDataDestroy);
+ }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ UniquePtr<LayerUserData> RemoveUserData(void* aKey);
+
+ /**
+ * This getter can be used anytime.
+ */
+ bool HasUserData(void* aKey)
+ {
+ return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
+ }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey) const
+ {
+ return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey)));
+ }
+
+ /**
+ * Must be called outside of a layers transaction.
+ *
+ * For the subtree rooted at |aSubtree|, this attempts to free up
+ * any free-able resources like retained buffers, but may do nothing
+ * at all. After this call, the layer tree is left in an undefined
+ * state; the layers in |aSubtree|'s subtree may no longer have
+ * buffers with valid content and may no longer be able to draw
+ * their visible and valid regions.
+ *
+ * In general, a painting or forwarding transaction on |this| must
+ * complete on the tree before it returns to a valid state.
+ *
+ * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree|
+ * is null. |aSubtree|'s manager must be this.
+ */
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) {}
+
+ /**
+ * Flag the next paint as the first for a document.
+ */
+ virtual void SetIsFirstPaint() {}
+
+ /**
+ * Make sure that the previous transaction has been entirely
+ * completed.
+ *
+ * Note: This may sychronously wait on a remote compositor
+ * to complete rendering.
+ */
+ virtual void FlushRendering() { }
+
+ /**
+ * Checks if we need to invalidate the OS widget to trigger
+ * painting when updating this layer manager.
+ */
+ virtual bool NeedsWidgetInvalidation() { return true; }
+
+ virtual const char* Name() const { return "???"; }
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * aStream.
+ */
+ void Dump(std::stringstream& aStream, const char* aPrefix="",
+ bool aDumpHtml=false, bool aSorted=false);
+ /**
+ * Dump information about just this layer manager itself to aStream
+ */
+ void DumpSelf(std::stringstream& aStream, const char* aPrefix="", bool aSorted=false);
+ void Dump(bool aSorted=false);
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * layerscope packet.
+ */
+ void Dump(layerscope::LayersPacket* aPacket);
+
+ /**
+ * Log information about this layer manager and its managed tree to
+ * the NSPR log (if enabled for "Layers").
+ */
+ void Log(const char* aPrefix="");
+ /**
+ * Log information about just this layer manager itself to the NSPR
+ * log (if enabled for "Layers").
+ */
+ void LogSelf(const char* aPrefix="");
+
+ /**
+ * Record (and return) frame-intervals and paint-times for frames which were presented
+ * between calling StartFrameTimeRecording and StopFrameTimeRecording.
+ *
+ * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
+ * (elements were overwritten since Start), result is considered invalid and hence empty.
+ * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
+ * Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
+ * - Note: the first frame-interval may be longer than expected because last frame
+ * might have been presented some time before calling StartFrameTimeRecording.
+ */
+
+ /**
+ * Returns a handle which represents current recording start position.
+ */
+ virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize);
+
+ /**
+ * Clears, then populates aFrameIntervals with the recorded frame timing
+ * data. The array will be empty if data was overwritten since
+ * aStartIndex was obtained.
+ */
+ virtual void StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals);
+
+ void RecordFrame();
+ void PostPresent();
+
+ void BeginTabSwitch();
+
+ static bool IsLogEnabled();
+ static mozilla::LogModule* GetLog();
+
+ bool IsCompositingCheap(LayersBackend aBackend)
+ {
+ // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to
+ // avoid loading the compositor!
+ return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend;
+ }
+
+ virtual bool IsCompositingCheap() { return true; }
+
+ bool IsInTransaction() const { return mInTransaction; }
+ virtual void GetFrameUniformity(FrameUniformityData* aOutData) { }
+ virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { return true; }
+ virtual void RunOverfillCallback(const uint32_t aOverfill) { }
+
+ virtual void SetRegionToClear(const nsIntRegion& aRegion)
+ {
+ mRegionToClear = aRegion;
+ }
+
+ virtual float RequestProperty(const nsAString& property) { return -1; }
+
+ const TimeStamp& GetAnimationReadyTime() const {
+ return mAnimationReadyTime;
+ }
+
+ virtual bool AsyncPanZoomEnabled() const {
+ return false;
+ }
+
+ static void LayerUserDataDestroy(void* data);
+
+ void AddPaintedPixelCount(int32_t aCount) {
+ mPaintedPixelCount += aCount;
+ }
+
+ uint32_t GetAndClearPaintedPixelCount() {
+ uint32_t count = mPaintedPixelCount;
+ mPaintedPixelCount = 0;
+ return count;
+ }
+
+protected:
+ RefPtr<Layer> mRoot;
+ gfx::UserData mUserData;
+ bool mDestroyed;
+ bool mSnapEffectiveTransforms;
+
+ nsIntRegion mRegionToClear;
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~LayerManager() {}
+
+ // Print interesting information about this into aStreamo. Internally
+ // used to implement Dump*() and Log*().
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ // Print interesting information about this into layerscope packet.
+ // Internally used to implement Dump().
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket);
+
+ uint64_t mId;
+ bool mInTransaction;
+ // The time when painting most recently finished. This is recorded so that
+ // we can time any play-pending animations from this point.
+ TimeStamp mAnimationReadyTime;
+ // The count of pixels that were painted in the current transaction.
+ uint32_t mPaintedPixelCount;
+private:
+ struct FramesTimingRecording
+ {
+ // Stores state and data for frame intervals and paint times recording.
+ // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details.
+ FramesTimingRecording()
+ : mNextIndex(0)
+ , mLatestStartIndex(0)
+ , mCurrentRunStartIndex(0)
+ , mIsPaused(true)
+ {}
+ nsTArray<float> mIntervals;
+ TimeStamp mLastFrameTime;
+ uint32_t mNextIndex;
+ uint32_t mLatestStartIndex;
+ uint32_t mCurrentRunStartIndex;
+ bool mIsPaused;
+ };
+ FramesTimingRecording mRecording;
+
+ TimeStamp mTabSwitchStart;
+
+public:
+ /*
+ * Methods to store/get/clear a "pending scroll info update" object on a
+ * per-scrollid basis. This is used for empty transactions that push over
+ * scroll position updates to the APZ code.
+ */
+ void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo);
+ Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId);
+ void ClearPendingScrollInfoUpdate();
+private:
+ std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates;
+};
+
+typedef InfallibleTArray<Animation> AnimationArray;
+
+struct AnimData {
+ InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
+ InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
+ InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
+};
+
+/**
+ * A Layer represents anything that can be rendered onto a destination
+ * surface.
+ */
+class Layer {
+ NS_INLINE_DECL_REFCOUNTING(Layer)
+
+public:
+ // Keep these in alphabetical order
+ enum LayerType {
+ TYPE_CANVAS,
+ TYPE_COLOR,
+ TYPE_CONTAINER,
+ TYPE_IMAGE,
+ TYPE_READBACK,
+ TYPE_REF,
+ TYPE_SHADOW,
+ TYPE_PAINTED
+ };
+
+ /**
+ * Returns the LayerManager this Layer belongs to. Note that the layer
+ * manager might be in a destroyed state, at which point it's only
+ * valid to set/get user data from it.
+ */
+ LayerManager* Manager() { return mManager; }
+
+ enum {
+ /**
+ * If this is set, the caller is promising that by the end of this
+ * transaction the entire visible region (as specified by
+ * SetVisibleRegion) will be filled with opaque content.
+ */
+ CONTENT_OPAQUE = 0x01,
+ /**
+ * If this is set, the caller is notifying that the contents of this layer
+ * require per-component alpha for optimal fidelity. However, there is no
+ * guarantee that component alpha will be supported for this layer at
+ * paint time.
+ * This should never be set at the same time as CONTENT_OPAQUE.
+ */
+ CONTENT_COMPONENT_ALPHA = 0x02,
+
+ /**
+ * If this is set then one of the descendant layers of this one has
+ * CONTENT_COMPONENT_ALPHA set.
+ */
+ CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04,
+
+ /**
+ * If this is set then this layer is part of a preserve-3d group, and should
+ * be sorted with sibling layers that are also part of the same group.
+ */
+ CONTENT_EXTEND_3D_CONTEXT = 0x08,
+ /**
+ * This indicates that the transform may be changed on during an empty
+ * transaction where there is no possibility of redrawing the content, so the
+ * implementation should be ready for that.
+ */
+ CONTENT_MAY_CHANGE_TRANSFORM = 0x10,
+
+ /**
+ * Disable subpixel AA for this layer. This is used if the display isn't suited
+ * for subpixel AA like hidpi or rotated content.
+ */
+ CONTENT_DISABLE_SUBPIXEL_AA = 0x20,
+
+ /**
+ * If this is set then the layer contains content that may look objectionable
+ * if not handled as an active layer (such as text with an animated transform).
+ * This is for internal layout/FrameLayerBuilder usage only until flattening
+ * code is obsoleted. See bug 633097
+ */
+ CONTENT_DISABLE_FLATTENING = 0x40,
+
+ /**
+ * This layer is hidden if the backface of the layer is visible
+ * to user.
+ */
+ CONTENT_BACKFACE_HIDDEN = 0x80
+ };
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * This lets layout make some promises about what will be drawn into the
+ * visible region of the PaintedLayer. This enables internal quality
+ * and performance optimizations.
+ */
+ void SetContentFlags(uint32_t aFlags)
+ {
+ NS_ASSERTION((aFlags & (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA)) !=
+ (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA),
+ "Can't be opaque and require component alpha");
+ if (mContentFlags != aFlags) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ContentFlags", this));
+ mContentFlags = aFlags;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * The union of the bounds of all the display item that got flattened
+ * into this layer. This is intended to be an approximation to the
+ * size of the layer if the nearest scrollable ancestor had an infinitely
+ * large displayport. Computing this more exactly is too expensive,
+ * but this approximation is sufficient for what we need to use it for.
+ */
+ virtual void SetLayerBounds(const gfx::IntRect& aLayerBounds)
+ {
+ if (!mLayerBounds.IsEqualEdges(aLayerBounds)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) LayerBounds", this));
+ mLayerBounds = aLayerBounds;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer which region will be visible. The visible region
+ * is a region which contains all the contents of the layer that can
+ * actually affect the rendering of the window. It can exclude areas
+ * that are covered by opaque contents of other layers, and it can
+ * exclude areas where this layer simply contains no content at all.
+ * (This can be an overapproximation to the "true" visible region.)
+ *
+ * There is no general guarantee that drawing outside the bounds of the
+ * visible region will be ignored. So if a layer draws outside the bounds
+ * of its visible region, it needs to ensure that what it draws is valid.
+ */
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ // IsEmpty is required otherwise we get invalidation glitches.
+ // See bug 1288464 for investigating why.
+ if (!mVisibleRegion.IsEqual(aRegion) || aRegion.IsEmpty()) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) VisibleRegion was %s is %s", this,
+ mVisibleRegion.ToString().get(), aRegion.ToString().get()));
+ mVisibleRegion = aRegion;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the (sub)document metrics used to render the Layer subtree
+ * rooted at this. Note that a layer may have multiple FrameMetrics
+ * objects; calling this function will remove all of them and replace
+ * them with the provided FrameMetrics. See the documentation for
+ * SetFrameMetrics(const nsTArray<FrameMetrics>&) for more details.
+ */
+ void SetScrollMetadata(const ScrollMetadata& aScrollMetadata)
+ {
+ Manager()->ClearPendingScrollInfoUpdate();
+ if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
+ mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata);
+ ScrollMetadataChanged();
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the (sub)document metrics used to render the Layer subtree
+ * rooted at this. There might be multiple metrics on this layer
+ * because the layer may, for example, be contained inside multiple
+ * nested scrolling subdocuments. In general a Layer having multiple
+ * ScrollMetadata objects is conceptually equivalent to having a stack
+ * of ContainerLayers that have been flattened into this Layer.
+ * See the documentation in LayerMetricsWrapper.h for a more detailed
+ * explanation of this conceptual equivalence.
+ *
+ * Note also that there is actually a many-to-many relationship between
+ * Layers and ScrollMetadata, because multiple Layers may have identical
+ * ScrollMetadata objects. This happens when those layers belong to the
+ * same scrolling subdocument and therefore end up with the same async
+ * transform when they are scrolled by the APZ code.
+ */
+ void SetScrollMetadata(const nsTArray<ScrollMetadata>& aMetadataArray)
+ {
+ Manager()->ClearPendingScrollInfoUpdate();
+ if (mScrollMetadata != aMetadataArray) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
+ mScrollMetadata = aMetadataArray;
+ ScrollMetadataChanged();
+ Mutated();
+ }
+ }
+
+ /*
+ * Compositor event handling
+ * =========================
+ * When a touch-start event (or similar) is sent to the AsyncPanZoomController,
+ * it needs to decide whether the event should be sent to the main thread.
+ * Each layer has a list of event handling regions. When the compositor needs
+ * to determine how to handle a touch event, it scans the layer tree from top
+ * to bottom in z-order (traversing children before their parents). Points
+ * outside the clip region for a layer cause that layer (and its subtree)
+ * to be ignored. If a layer has a mask layer, and that mask layer's alpha
+ * value is zero at the event point, then the layer and its subtree should
+ * be ignored.
+ * For each layer, if the point is outside its hit region, we ignore the layer
+ * and move onto the next. If the point is inside its hit region but
+ * outside the dispatch-to-content region, we can initiate a gesture without
+ * consulting the content thread. Otherwise we must dispatch the event to
+ * content.
+ * Note that if a layer or any ancestor layer has a ForceEmptyHitRegion
+ * override in GetEventRegionsOverride() then the hit-region must be treated
+ * as empty. Similarly, if there is a ForceDispatchToContent override then
+ * the dispatch-to-content region must be treated as encompassing the entire
+ * hit region, and therefore we must consult the content thread before
+ * initiating a gesture. (If both flags are set, ForceEmptyHitRegion takes
+ * priority.)
+ */
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the event handling region.
+ */
+ void SetEventRegions(const EventRegions& aRegions)
+ {
+ if (mEventRegions != aRegions) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) eventregions were %s, now %s", this,
+ mEventRegions.ToString().get(), aRegions.ToString().get()));
+ mEventRegions = aRegions;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the opacity which will be applied to this layer as it
+ * is composited to the destination.
+ */
+ void SetOpacity(float aOpacity)
+ {
+ if (mOpacity != aOpacity) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Opacity", this));
+ mOpacity = aOpacity;
+ Mutated();
+ }
+ }
+
+ void SetMixBlendMode(gfx::CompositionOp aMixBlendMode)
+ {
+ if (mMixBlendMode != aMixBlendMode) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MixBlendMode", this));
+ mMixBlendMode = aMixBlendMode;
+ Mutated();
+ }
+ }
+
+ void SetForceIsolatedGroup(bool aForceIsolatedGroup)
+ {
+ if(mForceIsolatedGroup != aForceIsolatedGroup) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this));
+ mForceIsolatedGroup = aForceIsolatedGroup;
+ Mutated();
+ }
+ }
+
+ bool GetForceIsolatedGroup() const
+ {
+ return mForceIsolatedGroup;
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set a clip rect which will be applied to this layer as it is
+ * composited to the destination. The coordinates are relative to
+ * the parent layer (i.e. the contents of this layer
+ * are transformed before this clip rect is applied).
+ * For the root layer, the coordinates are relative to the widget,
+ * in device pixels.
+ * If aRect is null no clipping will be performed.
+ */
+ void SetClipRect(const Maybe<ParentLayerIntRect>& aRect)
+ {
+ if (mClipRect) {
+ if (!aRect) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is <none>", this,
+ mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height));
+ mClipRect.reset();
+ Mutated();
+ } else {
+ if (!aRect->IsEqualEdges(*mClipRect)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is %d,%d,%d,%d", this,
+ mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height,
+ aRect->x, aRect->y, aRect->width, aRect->height));
+ mClipRect = aRect;
+ Mutated();
+ }
+ }
+ } else {
+ if (aRect) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was <none> is %d,%d,%d,%d", this,
+ aRect->x, aRect->y, aRect->width, aRect->height));
+ mClipRect = aRect;
+ Mutated();
+ }
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set an optional scrolled clip on the layer.
+ * The scrolled clip, if present, consists of a clip rect and an optional mask.
+ * This scrolled clip is always scrolled by all scroll frames associated with
+ * this layer. (By contrast, the scroll clips stored in ScrollMetadata are
+ * only scrolled by scroll frames above that ScrollMetadata, and the layer's
+ * mClipRect is always fixed to the layer contents (which may or may not be
+ * scrolled by some of the scroll frames associated with the layer, depending
+ * on whether the layer is fixed).)
+ */
+ void SetScrolledClip(const Maybe<LayerClip>& aScrolledClip)
+ {
+ if (mScrolledClip != aScrolledClip) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrolledClip", this));
+ mScrolledClip = aScrolledClip;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set a layer to mask this layer.
+ *
+ * The mask layer should be applied using its effective transform (after it
+ * is calculated by ComputeEffectiveTransformForMaskLayer), this should use
+ * this layer's parent's transform and the mask layer's transform, but not
+ * this layer's. That is, the mask layer is specified relative to this layer's
+ * position in it's parent layer's coord space.
+ * Currently, only 2D translations are supported for the mask layer transform.
+ *
+ * Ownership of aMaskLayer passes to this.
+ * Typical use would be an ImageLayer with an alpha image used for masking.
+ * See also ContainerState::BuildMaskLayer in FrameLayerBuilder.cpp.
+ */
+ void SetMaskLayer(Layer* aMaskLayer)
+ {
+#ifdef DEBUG
+ if (aMaskLayer) {
+ bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+ NS_ASSERTION(maskIs2D, "Mask layer has invalid transform.");
+ }
+#endif
+
+ if (mMaskLayer != aMaskLayer) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MaskLayer", this));
+ mMaskLayer = aMaskLayer;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Add mask layers associated with LayerClips.
+ */
+ void SetAncestorMaskLayers(const nsTArray<RefPtr<Layer>>& aLayers) {
+ if (aLayers != mAncestorMaskLayers) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AncestorMaskLayers", this));
+ mAncestorMaskLayers = aLayers;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Add a mask layer associated with a LayerClip.
+ */
+ void AddAncestorMaskLayer(const RefPtr<Layer>& aLayer) {
+ mAncestorMaskLayers.AppendElement(aLayer);
+ Mutated();
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer what its transform should be. The transformation
+ * is applied when compositing the layer into its parent container.
+ */
+ void SetBaseTransform(const gfx::Matrix4x4& aMatrix)
+ {
+ NS_ASSERTION(!aMatrix.IsSingular(),
+ "Shouldn't be trying to draw with a singular matrix!");
+ mPendingTransform = nullptr;
+ if (mTransform == aMatrix) {
+ return;
+ }
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) BaseTransform", this));
+ mTransform = aMatrix;
+ Mutated();
+ }
+
+ /**
+ * Can be called at any time.
+ *
+ * Like SetBaseTransform(), but can be called before the next
+ * transform (i.e. outside an open transaction). Semantically, this
+ * method enqueues a new transform value to be set immediately after
+ * the next transaction is opened.
+ */
+ void SetBaseTransformForNextTransaction(const gfx::Matrix4x4& aMatrix)
+ {
+ mPendingTransform = new gfx::Matrix4x4(aMatrix);
+ }
+
+ void SetPostScale(float aXScale, float aYScale)
+ {
+ if (mPostXScale == aXScale && mPostYScale == aYScale) {
+ return;
+ }
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PostScale", this));
+ mPostXScale = aXScale;
+ mPostYScale = aYScale;
+ Mutated();
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * A layer is "fixed position" when it draws content from a content
+ * (not chrome) document, the topmost content document has a root scrollframe
+ * with a displayport, but the layer does not move when that displayport scrolls.
+ */
+ void SetIsFixedPosition(bool aFixedPosition)
+ {
+ if (mIsFixedPosition != aFixedPosition) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) IsFixedPosition", this));
+ mIsFixedPosition = aFixedPosition;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * This flag is true when the transform on the layer is a perspective
+ * transform. The compositor treats perspective transforms specially
+ * for async scrolling purposes.
+ */
+ void SetTransformIsPerspective(bool aTransformIsPerspective)
+ {
+ if (mTransformIsPerspective != aTransformIsPerspective) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) TransformIsPerspective", this));
+ mTransformIsPerspective = aTransformIsPerspective;
+ Mutated();
+ }
+ }
+
+ // Call AddAnimation to add a new animation to this layer from layout code.
+ // Caller must fill in all the properties of the returned animation.
+ // A later animation overrides an earlier one.
+ Animation* AddAnimation();
+ // ClearAnimations clears animations on this layer.
+ void ClearAnimations();
+ // This is only called when the layer tree is updated. Do not call this from
+ // layout code. To add an animation to this layer, use AddAnimation.
+ void SetAnimations(const AnimationArray& aAnimations);
+ // Go through all animations in this layer and its children and, for
+ // any animations with a null start time, update their start time such
+ // that at |aReadyTime| the animation's current time corresponds to its
+ // 'initial current time' value.
+ void StartPendingAnimations(const TimeStamp& aReadyTime);
+
+ // These are a parallel to AddAnimation and clearAnimations, except
+ // they add pending animations that apply only when the next
+ // transaction is begun. (See also
+ // SetBaseTransformForNextTransaction.)
+ Animation* AddAnimationForNextTransaction();
+ void ClearAnimationsForNextTransaction();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer represents a fixed position element, this data is stored on the
+ * layer for use by the compositor.
+ *
+ * - |aScrollId| identifies the scroll frame that this element is fixed
+ * with respect to.
+ *
+ * - |aAnchor| is the point on the layer that is considered the "anchor"
+ * point, that is, the point which remains in the same position when
+ * compositing the layer tree with a transformation (such as when
+ * asynchronously scrolling and zooming).
+ *
+ * - |aSides| is the set of sides to which the element is fixed relative to.
+ * This is used if the viewport size is changed in the compositor and
+ * fixed position items need to shift accordingly. This value is made up
+ * combining appropriate values from mozilla::SideBits.
+ */
+ void SetFixedPositionData(FrameMetrics::ViewID aScrollId,
+ const LayerPoint& aAnchor,
+ int32_t aSides)
+ {
+ if (!mFixedPositionData ||
+ mFixedPositionData->mScrollId != aScrollId ||
+ mFixedPositionData->mAnchor != aAnchor ||
+ mFixedPositionData->mSides != aSides) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FixedPositionData", this));
+ if (!mFixedPositionData) {
+ mFixedPositionData = MakeUnique<FixedPositionData>();
+ }
+ mFixedPositionData->mScrollId = aScrollId;
+ mFixedPositionData->mAnchor = aAnchor;
+ mFixedPositionData->mSides = aSides;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer is "sticky position", |aScrollId| holds the scroll identifier
+ * of the scrollable content that contains it. The difference between the two
+ * rectangles |aOuter| and |aInner| is treated as two intervals in each
+ * dimension, with the current scroll position at the origin. For each
+ * dimension, while that component of the scroll position lies within either
+ * interval, the layer should not move relative to its scrolling container.
+ */
+ void SetStickyPositionData(FrameMetrics::ViewID aScrollId, LayerRect aOuter,
+ LayerRect aInner)
+ {
+ if (!mStickyPositionData ||
+ !mStickyPositionData->mOuter.IsEqualEdges(aOuter) ||
+ !mStickyPositionData->mInner.IsEqualEdges(aInner)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) StickyPositionData", this));
+ if (!mStickyPositionData) {
+ mStickyPositionData = new StickyPositionData;
+ }
+ mStickyPositionData->mScrollId = aScrollId;
+ mStickyPositionData->mOuter = aOuter;
+ mStickyPositionData->mInner = aInner;
+ Mutated();
+ }
+ }
+
+ enum ScrollDirection {
+ NONE,
+ VERTICAL,
+ HORIZONTAL
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer is a scrollbar layer, |aScrollId| holds the scroll identifier
+ * of the scrollable content that the scrollbar is for.
+ */
+ void SetScrollbarData(FrameMetrics::ViewID aScrollId, ScrollDirection aDir, float aThumbRatio)
+ {
+ if (mScrollbarTargetId != aScrollId ||
+ mScrollbarDirection != aDir ||
+ mScrollbarThumbRatio != aThumbRatio)
+ {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this));
+ mScrollbarTargetId = aScrollId;
+ mScrollbarDirection = aDir;
+ mScrollbarThumbRatio = aThumbRatio;
+ Mutated();
+ }
+ }
+
+ // Set during construction for the container layer of scrollbar components.
+ void SetIsScrollbarContainer()
+ {
+ if (!mIsScrollbarContainer) {
+ mIsScrollbarContainer = true;
+ Mutated();
+ }
+ }
+
+ // These getters can be used anytime.
+ float GetOpacity() { return mOpacity; }
+ gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; }
+ const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; }
+ const Maybe<LayerClip>& GetScrolledClip() const { return mScrolledClip; }
+ Maybe<ParentLayerIntRect> GetScrolledClipRect() const;
+ uint32_t GetContentFlags() { return mContentFlags; }
+ const gfx::IntRect& GetLayerBounds() const { return mLayerBounds; }
+ const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
+ const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const;
+ const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const;
+ uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); }
+ const nsTArray<ScrollMetadata>& GetAllScrollMetadata() { return mScrollMetadata; }
+ bool HasScrollableFrameMetrics() const;
+ bool IsScrollInfoLayer() const;
+ const EventRegions& GetEventRegions() const { return mEventRegions; }
+ ContainerLayer* GetParent() { return mParent; }
+ Layer* GetNextSibling() {
+ if (mNextSibling) {
+ mNextSibling->CheckCanary();
+ }
+ return mNextSibling;
+ }
+ const Layer* GetNextSibling() const {
+ if (mNextSibling) {
+ mNextSibling->CheckCanary();
+ }
+ return mNextSibling;
+ }
+ Layer* GetPrevSibling() { return mPrevSibling; }
+ const Layer* GetPrevSibling() const { return mPrevSibling; }
+ virtual Layer* GetFirstChild() const { return nullptr; }
+ virtual Layer* GetLastChild() const { return nullptr; }
+ gfx::Matrix4x4 GetTransform() const;
+ // Same as GetTransform(), but returns the transform as a strongly-typed
+ // matrix. Eventually this will replace GetTransform().
+ const CSSTransformMatrix GetTransformTyped() const;
+ const gfx::Matrix4x4& GetBaseTransform() const { return mTransform; }
+ // Note: these are virtual because ContainerLayerComposite overrides them.
+ virtual float GetPostXScale() const { return mPostXScale; }
+ virtual float GetPostYScale() const { return mPostYScale; }
+ bool GetIsFixedPosition() { return mIsFixedPosition; }
+ bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
+ bool GetIsStickyPosition() { return mStickyPositionData; }
+ FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mFixedPositionData ? mFixedPositionData->mScrollId : FrameMetrics::NULL_SCROLL_ID; }
+ LayerPoint GetFixedPositionAnchor() { return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint(); }
+ int32_t GetFixedPositionSides() { return mFixedPositionData ? mFixedPositionData->mSides : eSideBitsNone; }
+ FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; }
+ const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
+ const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
+ FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
+ ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
+ float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; }
+ bool IsScrollbarContainer() { return mIsScrollbarContainer; }
+ Layer* GetMaskLayer() const { return mMaskLayer; }
+ void CheckCanary() const { mCanary.Check(); }
+
+ // Ancestor mask layers are associated with FrameMetrics, but for simplicity
+ // in maintaining the layer tree structure we attach them to the layer.
+ size_t GetAncestorMaskLayerCount() const {
+ return mAncestorMaskLayers.Length();
+ }
+ Layer* GetAncestorMaskLayerAt(size_t aIndex) const {
+ return mAncestorMaskLayers.ElementAt(aIndex);
+ }
+ const nsTArray<RefPtr<Layer>>& GetAllAncestorMaskLayers() const {
+ return mAncestorMaskLayers;
+ }
+
+ bool HasMaskLayers() const {
+ return GetMaskLayer() || mAncestorMaskLayers.Length() > 0;
+ }
+
+ /*
+ * Get the combined clip rect of the Layer clip and all clips on FrameMetrics.
+ * This is intended for use in Layout. The compositor needs to apply async
+ * transforms to find the combined clip.
+ */
+ Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
+
+ /**
+ * Retrieve the root level visible region for |this| taking into account
+ * clipping applied to parent layers of |this| as well as subtracting
+ * visible regions of higher siblings of this layer and each ancestor.
+ *
+ * Note translation values for offsets of visible regions and accumulated
+ * aLayerOffset are integer rounded using IntPoint::Round.
+ *
+ * @param aResult - the resulting visible region of this layer.
+ * @param aLayerOffset - this layer's total offset from the root layer.
+ * @return - false if during layer tree traversal a parent or sibling
+ * transform is found to be non-translational. This method returns early
+ * in this case, results will not be valid. Returns true on successful
+ * traversal.
+ */
+ bool GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
+ nsIntPoint* aLayerOffset);
+
+ // Note that all lengths in animation data are either in CSS pixels or app
+ // units and must be converted to device pixels by the compositor.
+ AnimationArray& GetAnimations() { return mAnimations; }
+ InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; }
+
+ uint64_t GetAnimationGeneration() { return mAnimationGeneration; }
+ void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; }
+
+ bool HasTransformAnimation() const;
+
+ /**
+ * Returns the local transform for this layer: either mTransform or,
+ * for shadow layers, GetShadowBaseTransform(), in either case with the
+ * pre- and post-scales applied.
+ */
+ gfx::Matrix4x4 GetLocalTransform();
+
+ /**
+ * Same as GetLocalTransform(), but returns a strongly-typed matrix.
+ * Eventually, this will replace GetLocalTransform().
+ */
+ const LayerToParentLayerMatrix4x4 GetLocalTransformTyped();
+
+ /**
+ * Returns the local opacity for this layer: either mOpacity or,
+ * for shadow layers, GetShadowOpacity()
+ */
+ float GetLocalOpacity();
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * Apply pending changes to layers before drawing them, if those
+ * pending changes haven't been overridden by later changes.
+ */
+ void ApplyPendingUpdatesToSubtree();
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * Write layer-subtype-specific attributes into aAttrs. Used to
+ * synchronize layer attributes to their shadows'.
+ */
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { }
+
+ // Returns true if it's OK to save the contents of aLayer in an
+ // opaque surface (a surface without an alpha channel).
+ // If we can use a surface without an alpha channel, we should, because
+ // it will often make painting of antialiased text faster and higher
+ // quality.
+ bool CanUseOpaqueSurface();
+
+ SurfaceMode GetSurfaceMode()
+ {
+ if (CanUseOpaqueSurface())
+ return SurfaceMode::SURFACE_OPAQUE;
+ if (mContentFlags & CONTENT_COMPONENT_ALPHA)
+ return SurfaceMode::SURFACE_COMPONENT_ALPHA;
+ return SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
+
+ // Returns true if this layer can be treated as opaque for visibility
+ // computation. A layer may be non-opaque for visibility even if it
+ // is not transparent, for example, if it has a mix-blend-mode.
+ bool IsOpaqueForVisibility();
+
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ {
+ mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerManager::LayerUserDataDestroy);
+ }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ UniquePtr<LayerUserData> RemoveUserData(void* aKey);
+ /**
+ * This getter can be used anytime.
+ */
+ bool HasUserData(void* aKey)
+ {
+ return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
+ }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey) const
+ {
+ return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey)));
+ }
+
+ /**
+ * |Disconnect()| is used by layers hooked up over IPC. It may be
+ * called at any time, and may not be called at all. Using an
+ * IPC-enabled layer after Destroy() (drawing etc.) results in a
+ * safe no-op; no crashy or uaf etc.
+ *
+ * XXX: this interface is essentially LayerManager::Destroy, but at
+ * Layer granularity. It might be beneficial to unify them.
+ */
+ virtual void Disconnect() {}
+
+ /**
+ * Dynamic downcast to a PaintedLayer. Returns null if this is not
+ * a PaintedLayer.
+ */
+ virtual PaintedLayer* AsPaintedLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a ContainerLayer. Returns null if this is not
+ * a ContainerLayer.
+ */
+ virtual ContainerLayer* AsContainerLayer() { return nullptr; }
+ virtual const ContainerLayer* AsContainerLayer() const { return nullptr; }
+
+ /**
+ * Dynamic cast to a RefLayer. Returns null if this is not a
+ * RefLayer.
+ */
+ virtual RefLayer* AsRefLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a Color. Returns null if this is not a
+ * ColorLayer.
+ */
+ virtual ColorLayer* AsColorLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a LayerComposite. Return null if this is not a
+ * LayerComposite. Can be used anytime.
+ */
+ virtual LayerComposite* AsLayerComposite() { return nullptr; }
+
+ /**
+ * Dynamic cast to a ShadowableLayer. Return null if this is not a
+ * ShadowableLayer. Can be used anytime.
+ */
+ virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
+
+ // These getters can be used anytime. They return the effective
+ // values that should be used when drawing this layer to screen,
+ // accounting for this layer possibly being a shadow.
+ const Maybe<ParentLayerIntRect>& GetLocalClipRect();
+ const LayerIntRegion& GetLocalVisibleRegion();
+
+ bool Extend3DContext() {
+ return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT;
+ }
+ bool Combines3DTransformWithAncestors() {
+ return GetParent() &&
+ reinterpret_cast<Layer*>(GetParent())->Extend3DContext();
+ }
+ bool Is3DContextLeaf() {
+ return !Extend3DContext() && Combines3DTransformWithAncestors();
+ }
+ /**
+ * It is true if the user can see the back of the layer and the
+ * backface is hidden. The compositor should skip the layer if the
+ * result is true.
+ */
+ bool IsBackfaceHidden();
+ bool IsVisible() {
+ // For containers extending 3D context, visible region
+ // is meaningless, since they are just intermediate result of
+ // content.
+ return !GetLocalVisibleRegion().IsEmpty() || Extend3DContext();
+ }
+
+ /**
+ * Return true if current layer content is opaque.
+ * It does not guarantee that layer content is always opaque.
+ */
+ virtual bool IsOpaque() { return GetContentFlags() & CONTENT_OPAQUE; }
+
+ /**
+ * Returns the product of the opacities of this layer and all ancestors up
+ * to and excluding the nearest ancestor that has UseIntermediateSurface() set.
+ */
+ float GetEffectiveOpacity();
+
+ /**
+ * Returns the blendmode of this layer.
+ */
+ gfx::CompositionOp GetEffectiveMixBlendMode();
+
+ /**
+ * This returns the effective transform computed by
+ * ComputeEffectiveTransforms. Typically this is a transform that transforms
+ * this layer all the way to some intermediate surface or destination
+ * surface. For non-BasicLayers this will be a transform to the nearest
+ * ancestor with UseIntermediateSurface() (or to the root, if there is no
+ * such ancestor), but for BasicLayers it's different.
+ */
+ const gfx::Matrix4x4& GetEffectiveTransform() const { return mEffectiveTransform; }
+
+ /**
+ * This returns the effective transform for Layer's buffer computed by
+ * ComputeEffectiveTransforms. Typically this is a transform that transforms
+ * this layer's buffer all the way to some intermediate surface or destination
+ * surface. For non-BasicLayers this will be a transform to the nearest
+ * ancestor with UseIntermediateSurface() (or to the root, if there is no
+ * such ancestor), but for BasicLayers it's different.
+ *
+ * By default, its value is same to GetEffectiveTransform().
+ * When ImageLayer is rendered with ScaleMode::STRETCH,
+ * it becomes different from GetEffectiveTransform().
+ */
+ virtual const gfx::Matrix4x4& GetEffectiveTransformForBuffer() const
+ {
+ return mEffectiveTransform;
+ }
+
+ /**
+ * @param aTransformToSurface the composition of the transforms
+ * from the parent layer (if any) to the destination pixel grid.
+ *
+ * Computes mEffectiveTransform for this layer and all its descendants.
+ * mEffectiveTransform transforms this layer up to the destination
+ * pixel grid (whatever aTransformToSurface is relative to).
+ *
+ * We promise that when this is called on a layer, all ancestor layers
+ * have already had ComputeEffectiveTransforms called.
+ */
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0;
+
+ /**
+ * Computes the effective transform for mask layers, if this layer has any.
+ */
+ void ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface);
+ static void ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer,
+ const gfx::Matrix4x4& aTransformToSurface);
+
+ /**
+ * Calculate the scissor rect required when rendering this layer.
+ * Returns a rectangle relative to the intermediate surface belonging to the
+ * nearest ancestor that has an intermediate surface, or relative to the root
+ * viewport if no ancestor has an intermediate surface, corresponding to the
+ * clip rect for this layer intersected with aCurrentScissorRect.
+ */
+ RenderTargetIntRect CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect);
+
+ virtual const char* Name() const =0;
+ virtual LayerType GetType() const =0;
+
+ /**
+ * Only the implementation should call this. This is per-implementation
+ * private data. Normally, all layers with a given layer manager
+ * use the same type of ImplData.
+ */
+ void* ImplData() { return mImplData; }
+
+ /**
+ * Only the implementation should use these methods.
+ */
+ void SetParent(ContainerLayer* aParent) { mParent = aParent; }
+ void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; }
+ void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; }
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * aStream.
+ */
+ void Dump(std::stringstream& aStream, const char* aPrefix="",
+ bool aDumpHtml=false, bool aSorted=false);
+ /**
+ * Dump information about just this layer manager itself to aStream.
+ */
+ void DumpSelf(std::stringstream& aStream, const char* aPrefix="");
+
+ /**
+ * Dump information about this layer and its child & sibling layers to
+ * layerscope packet.
+ */
+ void Dump(layerscope::LayersPacket* aPacket, const void* aParent);
+
+ /**
+ * Log information about this layer manager and its managed tree to
+ * the NSPR log (if enabled for "Layers").
+ */
+ void Log(const char* aPrefix="");
+ /**
+ * Log information about just this layer manager itself to the NSPR
+ * log (if enabled for "Layers").
+ */
+ void LogSelf(const char* aPrefix="");
+
+ // Print interesting information about this into aStream. Internally
+ // used to implement Dump*() and Log*(). If subclasses have
+ // additional interesting properties, they should override this with
+ // an implementation that first calls the base implementation then
+ // appends additional info to aTo.
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ // Just like PrintInfo, but this function dump information into layerscope packet,
+ // instead of a StringStream. It is also internally used to implement Dump();
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent);
+
+ /**
+ * Store display list log.
+ */
+ void SetDisplayListLog(const char *log);
+
+ /**
+ * Return display list log.
+ */
+ void GetDisplayListLog(nsCString& log);
+
+ static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
+
+ /**
+ * Returns the current area of the layer (in layer-space coordinates)
+ * marked as needed to be recomposited.
+ */
+ const virtual gfx::TiledIntRegion& GetInvalidRegion() { return mInvalidRegion; }
+ void AddInvalidRegion(const nsIntRegion& aRegion) {
+ mInvalidRegion.Add(aRegion);
+ }
+
+ /**
+ * Mark the entirety of the layer's visible region as being invalid.
+ */
+ void SetInvalidRectToVisibleRegion()
+ {
+ mInvalidRegion.SetEmpty();
+ mInvalidRegion.Add(GetVisibleRegion().ToUnknownRegion());
+ }
+
+ /**
+ * Adds to the current invalid rect.
+ */
+ void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Add(aRect); }
+
+ /**
+ * Clear the invalid rect, marking the layer as being identical to what is currently
+ * composited.
+ */
+ void ClearInvalidRect() { mInvalidRegion.SetEmpty(); }
+
+ // These functions allow attaching an AsyncPanZoomController to this layer,
+ // and can be used anytime.
+ // A layer has an APZC at index aIndex only-if GetFrameMetrics(aIndex).IsScrollable();
+ // attempting to get an APZC for a non-scrollable metrics will return null.
+ // The aIndex for these functions must be less than GetScrollMetadataCount().
+ void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller);
+ AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const;
+ // The ScrollMetadataChanged function is used internally to ensure the APZC array length
+ // matches the frame metrics array length.
+private:
+ void ScrollMetadataChanged();
+public:
+
+ void ApplyPendingUpdatesForThisTransaction();
+
+#ifdef DEBUG
+ void SetDebugColorIndex(uint32_t aIndex) { mDebugColorIndex = aIndex; }
+ uint32_t GetDebugColorIndex() { return mDebugColorIndex; }
+#endif
+
+ virtual LayerRenderState GetRenderState() { return LayerRenderState(); }
+
+ void Mutated()
+ {
+ mManager->Mutated(this);
+ }
+
+ virtual int32_t GetMaxLayerSize() { return Manager()->GetMaxTextureSize(); }
+
+ /**
+ * Returns true if this layer's effective transform is not just
+ * a translation by integers, or if this layer or some ancestor layer
+ * is marked as having a transform that may change without a full layer
+ * transaction.
+ */
+ bool MayResample();
+
+ RenderTargetRect TransformRectToRenderTarget(const LayerIntRect& aRect);
+
+ /**
+ * Add debugging information to the layer dump.
+ */
+ void AddExtraDumpInfo(const nsACString& aStr)
+ {
+#ifdef MOZ_DUMP_PAINTING
+ mExtraDumpInfo.AppendElement(aStr);
+#endif
+ }
+
+ /**
+ * Clear debugging information. Useful for recycling.
+ */
+ void ClearExtraDumpInfo()
+ {
+#ifdef MOZ_DUMP_PAINTING
+ mExtraDumpInfo.Clear();
+#endif
+ }
+
+protected:
+ Layer(LayerManager* aManager, void* aImplData);
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Layer();
+
+ /**
+ * We can snap layer transforms for two reasons:
+ * 1) To avoid unnecessary resampling when a transform is a translation
+ * by a non-integer number of pixels.
+ * Snapping the translation to an integer number of pixels avoids
+ * blurring the layer and can be faster to composite.
+ * 2) When a layer is used to render a rectangular object, we need to
+ * emulate the rendering of rectangular inactive content and snap the
+ * edges of the rectangle to pixel boundaries. This is both to ensure
+ * layer rendering is consistent with inactive content rendering, and to
+ * avoid seams.
+ * This function implements type 1 snapping. If aTransform is a 2D
+ * translation, and this layer's layer manager has enabled snapping
+ * (which is the default), return aTransform with the translation snapped
+ * to nearest pixels. Otherwise just return aTransform. Call this when the
+ * layer does not correspond to a single rectangular content object.
+ * This function does not try to snap if aTransform has a scale, because in
+ * that case resampling is inevitable and there's no point in trying to
+ * avoid it. In fact snapping can cause problems because pixel edges in the
+ * layer's content can be rendered unpredictably (jiggling) as the scale
+ * interacts with the snapping of the translation, especially with animated
+ * transforms.
+ * @param aResidualTransform a transform to apply before the result transform
+ * in order to get the results to completely match aTransform.
+ */
+ gfx::Matrix4x4 SnapTransformTranslation(const gfx::Matrix4x4& aTransform,
+ gfx::Matrix* aResidualTransform);
+ gfx::Matrix4x4 SnapTransformTranslation3D(const gfx::Matrix4x4& aTransform,
+ gfx::Matrix* aResidualTransform);
+ /**
+ * See comment for SnapTransformTranslation.
+ * This function implements type 2 snapping. If aTransform is a translation
+ * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
+ * and return the transform that maps aSnapRect to that rect. Otherwise
+ * just return aTransform.
+ * @param aSnapRect a rectangle whose edges should be snapped to pixel
+ * boundaries in the destination surface.
+ * @param aResidualTransform a transform to apply before the result transform
+ * in order to get the results to completely match aTransform.
+ */
+ gfx::Matrix4x4 SnapTransform(const gfx::Matrix4x4& aTransform,
+ const gfxRect& aSnapRect,
+ gfx::Matrix* aResidualTransform);
+
+ LayerManager* mManager;
+ ContainerLayer* mParent;
+ Layer* mNextSibling;
+ Layer* mPrevSibling;
+ void* mImplData;
+ RefPtr<Layer> mMaskLayer;
+ nsTArray<RefPtr<Layer>> mAncestorMaskLayers;
+ // Look for out-of-bound in the middle of the structure
+ mozilla::CorruptionCanary mCanary;
+ gfx::UserData mUserData;
+ gfx::IntRect mLayerBounds;
+ LayerIntRegion mVisibleRegion;
+ nsTArray<ScrollMetadata> mScrollMetadata;
+ EventRegions mEventRegions;
+ gfx::Matrix4x4 mTransform;
+ // A mutation of |mTransform| that we've queued to be applied at the
+ // end of the next transaction (if nothing else overrides it in the
+ // meantime).
+ nsAutoPtr<gfx::Matrix4x4> mPendingTransform;
+ float mPostXScale;
+ float mPostYScale;
+ gfx::Matrix4x4 mEffectiveTransform;
+ AnimationArray mAnimations;
+ // See mPendingTransform above.
+ nsAutoPtr<AnimationArray> mPendingAnimations;
+ InfallibleTArray<AnimData> mAnimationData;
+ float mOpacity;
+ gfx::CompositionOp mMixBlendMode;
+ bool mForceIsolatedGroup;
+ Maybe<ParentLayerIntRect> mClipRect;
+ Maybe<LayerClip> mScrolledClip;
+ gfx::IntRect mTileSourceRect;
+ gfx::TiledIntRegion mInvalidRegion;
+ nsTArray<RefPtr<AsyncPanZoomController> > mApzcs;
+ uint32_t mContentFlags;
+ bool mUseTileSourceRect;
+ bool mIsFixedPosition;
+ bool mTransformIsPerspective;
+ struct FixedPositionData {
+ FrameMetrics::ViewID mScrollId;
+ LayerPoint mAnchor;
+ int32_t mSides;
+ };
+ UniquePtr<FixedPositionData> mFixedPositionData;
+ struct StickyPositionData {
+ FrameMetrics::ViewID mScrollId;
+ LayerRect mOuter;
+ LayerRect mInner;
+ };
+ nsAutoPtr<StickyPositionData> mStickyPositionData;
+ FrameMetrics::ViewID mScrollbarTargetId;
+ ScrollDirection mScrollbarDirection;
+ // The scrollbar thumb ratio is the ratio of the thumb position (in the CSS
+ // pixels of the scrollframe's parent's space) to the scroll position (in the
+ // CSS pixels of the scrollframe's space).
+ float mScrollbarThumbRatio;
+ bool mIsScrollbarContainer;
+#ifdef DEBUG
+ uint32_t mDebugColorIndex;
+#endif
+ // If this layer is used for OMTA, then this counter is used to ensure we
+ // stay in sync with the animation manager
+ uint64_t mAnimationGeneration;
+#ifdef MOZ_DUMP_PAINTING
+ nsTArray<nsCString> mExtraDumpInfo;
+#endif
+ // Store display list log.
+ nsCString mDisplayListLog;
+};
+
+/**
+ * A Layer which we can paint into. It is a conceptually
+ * infinite surface, but each PaintedLayer has an associated "valid region"
+ * of contents that it is currently storing, which is finite. PaintedLayer
+ * implementations can store content between paints.
+ *
+ * PaintedLayers are rendered into during the drawing phase of a transaction.
+ *
+ * Currently the contents of a PaintedLayer are in the device output color
+ * space.
+ */
+class PaintedLayer : public Layer {
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer that the content in some region has changed and
+ * will need to be repainted. This area is removed from the valid
+ * region.
+ */
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set whether ComputeEffectiveTransforms should compute the
+ * "residual translation" --- the translation that should be applied *before*
+ * mEffectiveTransform to get the ideal transform for this PaintedLayer.
+ * When this is true, ComputeEffectiveTransforms will compute the residual
+ * and ensure that the layer is invalidated whenever the residual changes.
+ * When it's false, a change in the residual will not trigger invalidation
+ * and GetResidualTranslation will return 0,0.
+ * So when the residual is to be ignored, set this to false for better
+ * performance.
+ */
+ void SetAllowResidualTranslation(bool aAllow) { mAllowResidualTranslation = aAllow; }
+
+ /**
+ * Can be used anytime
+ */
+ const nsIntRegion& GetValidRegion() const { return mValidRegion; }
+
+ virtual PaintedLayer* AsPaintedLayer() override { return this; }
+
+ MOZ_LAYER_DECL_NAME("PaintedLayer", TYPE_PAINTED)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+ gfx::Matrix residual;
+ mEffectiveTransform = SnapTransformTranslation(idealTransform,
+ mAllowResidualTranslation ? &residual : nullptr);
+ // The residual can only be a translation because SnapTransformTranslation
+ // only changes the transform if it's a translation
+ NS_ASSERTION(residual.IsTranslation(),
+ "Residual transform can only be a translation");
+ if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
+ mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
+ DebugOnly<mozilla::gfx::Point> transformedOrig =
+ idealTransform.TransformPoint(mozilla::gfx::Point());
+#ifdef DEBUG
+ DebugOnly<mozilla::gfx::Point> transformed = idealTransform.TransformPoint(
+ mozilla::gfx::Point(mResidualTranslation.x, mResidualTranslation.y)
+ ) - *&transformedOrig;
+#endif
+ NS_ASSERTION(-0.5 <= (&transformed)->x && (&transformed)->x < 0.5 &&
+ -0.5 <= (&transformed)->y && (&transformed)->y < 0.5,
+ "Residual translation out of range");
+ mValidRegion.SetEmpty();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+ LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; }
+
+ bool UsedForReadback() { return mUsedForReadback; }
+ void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
+
+ /**
+ * Returns true if aLayer is optimized for the given PaintedLayerCreationHint.
+ */
+ virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint)
+ { return true; }
+
+ /**
+ * Returns the residual translation. Apply this translation when drawing
+ * into the PaintedLayer so that when mEffectiveTransform is applied afterwards
+ * by layer compositing, the results exactly match the "ideal transform"
+ * (the product of the transform of this layer and its ancestors).
+ * Returns 0,0 unless SetAllowResidualTranslation(true) has been called.
+ * The residual translation components are always in the range [-0.5, 0.5).
+ */
+ gfxPoint GetResidualTranslation() const { return mResidualTranslation; }
+
+protected:
+ PaintedLayer(LayerManager* aManager, void* aImplData,
+ LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE)
+ : Layer(aManager, aImplData)
+ , mValidRegion()
+ , mCreationHint(aCreationHint)
+ , mUsedForReadback(false)
+ , mAllowResidualTranslation(false)
+ {
+ mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
+ }
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ /**
+ * ComputeEffectiveTransforms snaps the ideal transform to get mEffectiveTransform.
+ * mResidualTranslation is the translation that should be applied *before*
+ * mEffectiveTransform to get the ideal transform.
+ */
+ gfxPoint mResidualTranslation;
+ nsIntRegion mValidRegion;
+ /**
+ * The creation hint that was used when constructing this layer.
+ */
+ const LayerManager::PaintedLayerCreationHint mCreationHint;
+ /**
+ * Set when this PaintedLayer is participating in readback, i.e. some
+ * ReadbackLayer (may) be getting its background from this layer.
+ */
+ bool mUsedForReadback;
+ /**
+ * True when
+ */
+ bool mAllowResidualTranslation;
+};
+
+/**
+ * A Layer which other layers render into. It holds references to its
+ * children.
+ */
+class ContainerLayer : public Layer {
+public:
+
+ ~ContainerLayer();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Insert aChild into the child list of this container. aChild must
+ * not be currently in any child list or the root for the layer manager.
+ * If aAfter is non-null, it must be a child of this container and
+ * we insert after that layer. If it's null we insert at the start.
+ */
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter);
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Remove aChild from the child list of this container. aChild must
+ * be a child of this container.
+ */
+ virtual bool RemoveChild(Layer* aChild);
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Reposition aChild from the child list of this container. aChild must
+ * be a child of this container.
+ * If aAfter is non-null, it must be a child of this container and we
+ * reposition after that layer. If it's null, we reposition at the start.
+ */
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter);
+
+ void SetPreScale(float aXScale, float aYScale)
+ {
+ if (mPreXScale == aXScale && mPreYScale == aYScale) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PreScale", this));
+ mPreXScale = aXScale;
+ mPreYScale = aYScale;
+ Mutated();
+ }
+
+ void SetInheritedScale(float aXScale, float aYScale)
+ {
+ if (mInheritedXScale == aXScale && mInheritedYScale == aYScale) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) InheritedScale", this));
+ mInheritedXScale = aXScale;
+ mInheritedYScale = aYScale;
+ Mutated();
+ }
+
+ void SetScaleToResolution(bool aScaleToResolution, float aResolution)
+ {
+ if (mScaleToResolution == aScaleToResolution && mPresShellResolution == aResolution) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScaleToResolution", this));
+ mScaleToResolution = aScaleToResolution;
+ mPresShellResolution = aResolution;
+ Mutated();
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+
+ void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+
+ // These getters can be used anytime.
+
+ virtual ContainerLayer* AsContainerLayer() override { return this; }
+ virtual const ContainerLayer* AsContainerLayer() const override { return this; }
+
+ virtual Layer* GetFirstChild() const override { return mFirstChild; }
+ virtual Layer* GetLastChild() const override { return mLastChild; }
+ float GetPreXScale() const { return mPreXScale; }
+ float GetPreYScale() const { return mPreYScale; }
+ float GetInheritedXScale() const { return mInheritedXScale; }
+ float GetInheritedYScale() const { return mInheritedYScale; }
+ float GetPresShellResolution() const { return mPresShellResolution; }
+ bool ScaleToResolution() const { return mScaleToResolution; }
+
+ MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER)
+
+ /**
+ * ContainerLayer backends need to override ComputeEffectiveTransforms
+ * since the decision about whether to use a temporary surface for the
+ * container is backend-specific. ComputeEffectiveTransforms must also set
+ * mUseIntermediateSurface.
+ */
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override = 0;
+
+ /**
+ * Call this only after ComputeEffectiveTransforms has been invoked
+ * on this layer.
+ * Returns true if this will use an intermediate surface. This is largely
+ * backend-dependent, but it affects the operation of GetEffectiveOpacity().
+ */
+ bool UseIntermediateSurface() { return mUseIntermediateSurface; }
+
+ /**
+ * Returns the rectangle covered by the intermediate surface,
+ * in this layer's coordinate system.
+ *
+ * NOTE: Since this layer has an intermediate surface it follows
+ * that LayerPixel == RenderTargetPixel
+ */
+ RenderTargetIntRect GetIntermediateSurfaceRect()
+ {
+ NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface");
+ return RenderTargetIntRect::FromUnknownRect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+
+ /**
+ * Returns true if this container has more than one non-empty child
+ */
+ bool HasMultipleChildren();
+
+ /**
+ * Returns true if this container supports children with component alpha.
+ * Should only be called while painting a child of this layer.
+ */
+ bool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; }
+
+ /**
+ * Returns true if aLayer or any layer in its parent chain has the opaque
+ * content flag set.
+ */
+ static bool HasOpaqueAncestorLayer(Layer* aLayer);
+
+ void SetChildrenChanged(bool aVal) {
+ mChildrenChanged = aVal;
+ }
+
+ void SetEventRegionsOverride(EventRegionsOverride aVal) {
+ if (mEventRegionsOverride == aVal) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) EventRegionsOverride", this));
+ mEventRegionsOverride = aVal;
+ Mutated();
+ }
+
+ EventRegionsOverride GetEventRegionsOverride() const {
+ return mEventRegionsOverride;
+ }
+
+protected:
+ friend class ReadbackProcessor;
+
+ void DidInsertChild(Layer* aLayer);
+ void DidRemoveChild(Layer* aLayer);
+
+ void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
+
+ ContainerLayer(LayerManager* aManager, void* aImplData);
+
+ /**
+ * A default implementation of ComputeEffectiveTransforms for use by OpenGL
+ * and D3D.
+ */
+ void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
+
+ /**
+ * A default implementation to compute and set the value for SupportsComponentAlphaChildren().
+ *
+ * If aNeedsSurfaceCopy is provided, then it is set to true if the caller needs to copy the background
+ * up into the intermediate surface created, false otherwise.
+ */
+ void DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy = nullptr);
+
+ /**
+ * Loops over the children calling ComputeEffectiveTransforms on them.
+ */
+ void ComputeEffectiveTransformsForChildren(const gfx::Matrix4x4& aTransformToSurface);
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ /**
+ * True for if the container start a new 3D context extended by one
+ * or more children.
+ */
+ bool Creates3DContextWithExtendingChildren();
+
+ Layer* mFirstChild;
+ Layer* mLastChild;
+ float mPreXScale;
+ float mPreYScale;
+ // The resolution scale inherited from the parent layer. This will already
+ // be part of mTransform.
+ float mInheritedXScale;
+ float mInheritedYScale;
+ // For layers corresponding to an nsDisplayResolution, the resolution of the
+ // associated pres shell; for other layers, 1.0.
+ float mPresShellResolution;
+ // Whether the compositor should scale to mPresShellResolution.
+ bool mScaleToResolution;
+ bool mUseIntermediateSurface;
+ bool mSupportsComponentAlphaChildren;
+ bool mMayHaveReadbackChild;
+ // This is updated by ComputeDifferences. This will be true if we need to invalidate
+ // the intermediate surface.
+ bool mChildrenChanged;
+ EventRegionsOverride mEventRegionsOverride;
+};
+
+/**
+ * A Layer which just renders a solid color in its visible region. It actually
+ * can fill any area that contains the visible region, so if you need to
+ * restrict the area filled, set a clip region on this layer.
+ */
+class ColorLayer : public Layer {
+public:
+ virtual ColorLayer* AsColorLayer() override { return this; }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the color of the layer.
+ */
+ virtual void SetColor(const gfx::Color& aColor)
+ {
+ if (mColor != aColor) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Color", this));
+ mColor = aColor;
+ Mutated();
+ }
+ }
+
+ void SetBounds(const gfx::IntRect& aBounds)
+ {
+ if (!mBounds.IsEqualEdges(aBounds)) {
+ mBounds = aBounds;
+ Mutated();
+ }
+ }
+
+ const gfx::IntRect& GetBounds()
+ {
+ return mBounds;
+ }
+
+ // This getter can be used anytime.
+ virtual const gfx::Color& GetColor() { return mColor; }
+
+ MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+ mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+protected:
+ ColorLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData)
+ , mColor()
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ gfx::IntRect mBounds;
+ gfx::Color mColor;
+};
+
+/**
+ * A Layer for HTML Canvas elements. It's backed by either a
+ * gfxASurface or a GLContext (for WebGL layers), and has some control
+ * for intelligent updating from the source if necessary (for example,
+ * if hardware compositing is not available, for reading from the GL
+ * buffer into an image surface that we can layer composite.)
+ *
+ * After Initialize is called, the underlying canvas Surface/GLContext
+ * must not be modified during a layer transaction.
+ */
+class CanvasLayer : public Layer {
+public:
+ struct Data {
+ Data()
+ : mBufferProvider(nullptr)
+ , mGLContext(nullptr)
+ , mRenderer(nullptr)
+ , mFrontbufferGLTex(0)
+ , mSize(0,0)
+ , mHasAlpha(false)
+ , mIsGLAlphaPremult(true)
+ , mIsMirror(false)
+ { }
+
+ // One of these three must be specified for Canvas2D, but never more than one
+ PersistentBufferProvider* mBufferProvider; // A BufferProvider for the Canvas contents
+ mozilla::gl::GLContext* mGLContext; // or this, for GL.
+ AsyncCanvasRenderer* mRenderer; // or this, for OffscreenCanvas
+
+ // Frontbuffer override
+ uint32_t mFrontbufferGLTex;
+
+ // The size of the canvas content
+ gfx::IntSize mSize;
+
+ // Whether the canvas drawingbuffer has an alpha channel.
+ bool mHasAlpha;
+
+ // Whether mGLContext contains data that is alpha-premultiplied.
+ bool mIsGLAlphaPremult;
+
+ // Whether the canvas front buffer is already being rendered somewhere else.
+ // When true, do not swap buffers or Morph() to another factory on mGLContext
+ bool mIsMirror;
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Initialize this CanvasLayer with the given data. The data must
+ * have either mSurface or mGLContext initialized (but not both), as
+ * well as mSize.
+ *
+ * This must only be called once.
+ */
+ virtual void Initialize(const Data& aData) = 0;
+
+ /**
+ * Check the data is owned by this layer is still valid for rendering
+ */
+ virtual bool IsDataValid(const Data& aData) { return true; }
+
+ /**
+ * Notify this CanvasLayer that the canvas surface contents have
+ * changed (or will change) before the next transaction.
+ */
+ void Updated() { mDirty = true; SetInvalidRectToVisibleRegion(); }
+
+ /**
+ * Notify this CanvasLayer that the canvas surface contents have
+ * been painted since the last change.
+ */
+ void Painted() { mDirty = false; }
+
+ /**
+ * Returns true if the canvas surface contents have changed since the
+ * last paint.
+ */
+ bool IsDirty()
+ {
+ // We can only tell if we are dirty if we're part of the
+ // widget's retained layer tree.
+ if (!mManager || !mManager->IsWidgetLayerManager()) {
+ return true;
+ }
+ return mDirty;
+ }
+
+ /**
+ * Register a callback to be called at the start of each transaction.
+ */
+ typedef void PreTransactionCallback(void* closureData);
+ void SetPreTransactionCallback(PreTransactionCallback* callback, void* closureData)
+ {
+ mPreTransCallback = callback;
+ mPreTransCallbackData = closureData;
+ }
+
+ const nsIntRect& GetBounds() const { return mBounds; }
+
+protected:
+ void FirePreTransactionCallback()
+ {
+ if (mPreTransCallback) {
+ mPreTransCallback(mPreTransCallbackData);
+ }
+ }
+
+public:
+ /**
+ * Register a callback to be called at the end of each transaction.
+ */
+ typedef void (* DidTransactionCallback)(void* aClosureData);
+ void SetDidTransactionCallback(DidTransactionCallback aCallback, void* aClosureData)
+ {
+ mPostTransCallback = aCallback;
+ mPostTransCallbackData = aClosureData;
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the filter used to resample this image (if necessary).
+ */
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mSamplingFilter != aSamplingFilter) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this));
+ mSamplingFilter = aSamplingFilter;
+ Mutated();
+ }
+ }
+ gfx::SamplingFilter GetSamplingFilter() const { return mSamplingFilter; }
+
+ MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
+ nullptr)*
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+ bool GetIsAsyncRenderer() const
+ {
+ return !!mAsyncRenderer;
+ }
+
+protected:
+ CanvasLayer(LayerManager* aManager, void* aImplData);
+ virtual ~CanvasLayer();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ void FireDidTransactionCallback()
+ {
+ if (mPostTransCallback) {
+ mPostTransCallback(mPostTransCallbackData);
+ }
+ }
+
+ /**
+ * 0, 0, canvaswidth, canvasheight
+ */
+ gfx::IntRect mBounds;
+ PreTransactionCallback* mPreTransCallback;
+ void* mPreTransCallbackData;
+ DidTransactionCallback mPostTransCallback;
+ void* mPostTransCallbackData;
+ gfx::SamplingFilter mSamplingFilter;
+ RefPtr<AsyncCanvasRenderer> mAsyncRenderer;
+
+private:
+ /**
+ * Set to true in Updated(), cleared during a transaction.
+ */
+ bool mDirty;
+};
+
+/**
+ * ContainerLayer that refers to a "foreign" layer tree, through an
+ * ID. Usage of RefLayer looks like
+ *
+ * Construction phase:
+ * allocate ID for layer subtree
+ * create RefLayer, SetReferentId(ID)
+ *
+ * Composition:
+ * look up subtree for GetReferentId()
+ * ConnectReferentLayer(subtree)
+ * compose
+ * ClearReferentLayer()
+ *
+ * Clients will usually want to Connect/Clear() on each transaction to
+ * avoid difficulties managing memory across multiple layer subtrees.
+ */
+class RefLayer : public ContainerLayer {
+ friend class LayerManager;
+
+private:
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the ID of the layer's referent.
+ */
+ void SetReferentId(uint64_t aId)
+ {
+ MOZ_ASSERT(aId != 0);
+ if (mId != aId) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ReferentId", this));
+ mId = aId;
+ Mutated();
+ }
+ }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Connect this ref layer to its referent, temporarily.
+ * ClearReferentLayer() must be called after composition.
+ */
+ void ConnectReferentLayer(Layer* aLayer)
+ {
+ MOZ_ASSERT(!mFirstChild && !mLastChild);
+ MOZ_ASSERT(!aLayer->GetParent());
+ if (aLayer->Manager() != Manager()) {
+ // This can happen when e.g. rendering while dragging tabs
+ // between windows - aLayer's manager may be the manager for the
+ // old window's tab. In that case, it will be changed before the
+ // next render (see SetLayerManager). It is simply easier to
+ // ignore the rendering here than it is to pause it.
+ NS_WARNING("ConnectReferentLayer failed - Incorrect LayerManager");
+ return;
+ }
+
+ mFirstChild = mLastChild = aLayer;
+ aLayer->SetParent(this);
+ }
+
+ /**
+ * DRAWING PHASE ONLY
+ * |aLayer| is the same as the argument to ConnectReferentLayer().
+ */
+ void DetachReferentLayer(Layer* aLayer)
+ {
+ mFirstChild = mLastChild = nullptr;
+ aLayer->SetParent(nullptr);
+ }
+
+ // These getters can be used anytime.
+ virtual RefLayer* AsRefLayer() override { return this; }
+
+ virtual int64_t GetReferentId() { return mId; }
+
+ /**
+ * DRAWING PHASE ONLY
+ */
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+
+ MOZ_LAYER_DECL_NAME("RefLayer", TYPE_REF)
+
+protected:
+ RefLayer(LayerManager* aManager, void* aImplData)
+ : ContainerLayer(aManager, aImplData) , mId(0)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ // 0 is a special value that means "no ID".
+ uint64_t mId;
+};
+
+void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget);
+
+#ifdef MOZ_DUMP_PAINTING
+void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf);
+void WriteSnapshotToDumpFile(LayerManager* aManager, gfx::DataSourceSurface* aSurf);
+void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget);
+#endif
+
+// A utility function used by different LayerManager implementations.
+gfx::IntRect ToOutsideIntRect(const gfxRect &aRect);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERS_H */
diff --git a/system/graphics/layers/LayersLogging.cpp b/system/graphics/layers/LayersLogging.cpp
new file mode 100644
index 000000000..bf3a13957
--- /dev/null
+++ b/system/graphics/layers/LayersLogging.cpp
@@ -0,0 +1,410 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayersLogging.h"
+#include <stdint.h> // for uint8_t
+#include "ImageTypes.h" // for ImageFormat
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "nsDebug.h" // for NS_ERROR
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "base/basictypes.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, const void* p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("%p", p).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << n;
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Color& c,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "rgba(%d, %d, %d, %f)",
+ uint8_t(c.r*255.f), uint8_t(c.g*255.f), uint8_t(c.b*255.f), c.a).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsPoint& p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("(x=%d, y=%d)", p.x, p.y).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRect& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsIntPoint& p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("(x=%d, y=%d)", p.x, p.y).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const IntRect& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsIntRegion& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const EventRegions& e,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx << "{";
+ if (!e.mHitRegion.IsEmpty()) {
+ AppendToString(aStream, e.mHitRegion, " hitregion=", "");
+ }
+ if (!e.mDispatchToContentHitRegion.IsEmpty()) {
+ AppendToString(aStream, e.mDispatchToContentHitRegion, " dispatchtocontentregion=", "");
+ }
+ if (!e.mNoActionRegion.IsEmpty()) {
+ AppendToString(aStream, e.mNoActionRegion, " NoActionRegion=","");
+ }
+ if (!e.mHorizontalPanRegion.IsEmpty()) {
+ AppendToString(aStream, e.mHorizontalPanRegion, " HorizontalPanRegion=", "");
+ }
+ if (!e.mVerticalPanRegion.IsEmpty()) {
+ AppendToString(aStream, e.mVerticalPanRegion, " VerticalPanRegion=", "");
+ }
+ aStream << "}" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ScrollMetadata& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ AppendToString(aStream, m.GetMetrics(), "{ [metrics=");
+ AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
+ if (m.GetScrollParentId() != FrameMetrics::NULL_SCROLL_ID) {
+ AppendToString(aStream, m.GetScrollParentId(), "] [scrollParent=");
+ }
+ if (m.HasScrollClip()) {
+ AppendToString(aStream, m.ScrollClip().GetClipRect(), "] [clip=");
+ }
+ aStream << "] }" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const FrameMetrics& m,
+ const char* pfx, const char* sfx, bool detailed)
+{
+ aStream << pfx;
+ AppendToString(aStream, m.GetCompositionBounds(), "{ [cb=");
+ AppendToString(aStream, m.GetScrollableRect(), "] [sr=");
+ AppendToString(aStream, m.GetScrollOffset(), "] [s=");
+ if (m.GetDoSmoothScroll()) {
+ AppendToString(aStream, m.GetSmoothScrollOffset(), "] [ss=");
+ }
+ AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
+ AppendToString(aStream, m.GetCriticalDisplayPort(), "] [cdp=");
+ if (!detailed) {
+ AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
+ if (m.IsRootContent()) {
+ aStream << "] [rcd";
+ }
+ AppendToString(aStream, m.GetZoom(), "] [z=", "] }");
+ } else {
+ AppendToString(aStream, m.GetDisplayPortMargins(), " [dpm=");
+ aStream << nsPrintfCString("] um=%d", m.GetUseDisplayPortMargins()).get();
+ AppendToString(aStream, m.GetRootCompositionSize(), "] [rcs=");
+ AppendToString(aStream, m.GetViewport(), "] [v=");
+ aStream << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
+ m.GetDevPixelsPerCSSPixel().scale,
+ m.GetPresShellResolution()).get();
+ AppendToString(aStream, m.GetCumulativeResolution(), " cr=");
+ AppendToString(aStream, m.GetZoom(), " z=");
+ AppendToString(aStream, m.GetExtraResolution(), " er=");
+ aStream << nsPrintfCString(")] [u=(%d %d %lu)",
+ m.GetScrollUpdateType(), m.GetDoSmoothScroll(),
+ m.GetScrollGeneration()).get();
+ aStream << nsPrintfCString("] [i=(%ld %lld %d)] }",
+ m.GetPresShellId(), m.GetScrollId(), m.IsRootContent()).get();
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx
+ << nsPrintfCString("{ l=%" PRIu64 ", p=%u, v=%" PRIu64 " }", s.mLayersId, s.mPresShellId, s.mScrollId).get()
+ << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx
+ << nsPrintfCString("{ z=%d dt=%d min=%f max=%f }", z.mAllowZoom, z.mAllowDoubleTapZoom, z.mMinZoom.scale, z.mMaxZoom.scale).get()
+ << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Matrix& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ if (m.IsIdentity()) {
+ aStream << "[ I ]";
+ } else {
+ aStream << nsPrintfCString(
+ "[ %g %g; %g %g; %g %g; ]",
+ m._11, m._12, m._21, m._22, m._31, m._32).get();
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Matrix5x4& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]",
+ m._11, m._12, m._13, m._14,
+ m._21, m._22, m._23, m._24,
+ m._31, m._32, m._33, m._34,
+ m._41, m._42, m._43, m._44,
+ m._51, m._52, m._53, m._54).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const SamplingFilter filter,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ switch (filter) {
+ case SamplingFilter::GOOD: aStream << "SamplingFilter::GOOD"; break;
+ case SamplingFilter::LINEAR: aStream << "SamplingFilter::LINEAR"; break;
+ case SamplingFilter::POINT: aStream << "SamplingFilter::POINT"; break;
+ default:
+ NS_ERROR("unknown SamplingFilter type");
+ aStream << "???";
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, TextureFlags flags,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ if (flags == TextureFlags::NO_FLAGS) {
+ aStream << "NoFlags";
+ } else {
+
+#define AppendFlag(test) \
+{ \
+ if (!!(flags & test)) { \
+ if (previous) { \
+ aStream << "|"; \
+ } \
+ aStream << #test; \
+ previous = true; \
+ } \
+}
+ bool previous = false;
+ AppendFlag(TextureFlags::USE_NEAREST_FILTER);
+ AppendFlag(TextureFlags::ORIGIN_BOTTOM_LEFT);
+ AppendFlag(TextureFlags::DISALLOW_BIGIMAGE);
+
+#undef AppendFlag
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, mozilla::gfx::SurfaceFormat format,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch (format) {
+ case SurfaceFormat::B8G8R8A8: aStream << "SurfaceFormat::B8G8R8A8"; break;
+ case SurfaceFormat::B8G8R8X8: aStream << "SurfaceFormat::B8G8R8X8"; break;
+ case SurfaceFormat::R8G8B8A8: aStream << "SurfaceFormat::R8G8B8A8"; break;
+ case SurfaceFormat::R8G8B8X8: aStream << "SurfaceFormat::R8G8B8X8"; break;
+ case SurfaceFormat::R5G6B5_UINT16:
+ aStream << "SurfaceFormat::R5G6B5_UINT16"; break;
+ case SurfaceFormat::A8: aStream << "SurfaceFormat::A8"; break;
+ case SurfaceFormat::YUV: aStream << "SurfaceFormat::YUV"; break;
+ case SurfaceFormat::NV12: aStream << "SurfaceFormat::NV12"; break;
+ case SurfaceFormat::YUV422: aStream << "SurfaceFormat::YUV422"; break;
+ case SurfaceFormat::UNKNOWN: aStream << "SurfaceFormat::UNKNOWN"; break;
+ default:
+ NS_ERROR("unknown surface format");
+ aStream << "???";
+ }
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, gfx::SurfaceType aType,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch(aType) {
+ case SurfaceType::DATA:
+ aStream << "SurfaceType::DATA"; break;
+ case SurfaceType::D2D1_BITMAP:
+ aStream << "SurfaceType::D2D1_BITMAP"; break;
+ case SurfaceType::D2D1_DRAWTARGET:
+ aStream << "SurfaceType::D2D1_DRAWTARGET"; break;
+ case SurfaceType::CAIRO:
+ aStream << "SurfaceType::CAIRO"; break;
+ case SurfaceType::CAIRO_IMAGE:
+ aStream << "SurfaceType::CAIRO_IMAGE"; break;
+ case SurfaceType::COREGRAPHICS_IMAGE:
+ aStream << "SurfaceType::COREGRAPHICS_IMAGE"; break;
+ case SurfaceType::COREGRAPHICS_CGCONTEXT:
+ aStream << "SurfaceType::COREGRAPHICS_CGCONTEXT"; break;
+ case SurfaceType::SKIA:
+ aStream << "SurfaceType::SKIA"; break;
+ case SurfaceType::DUAL_DT:
+ aStream << "SurfaceType::DUAL_DT"; break;
+ case SurfaceType::D2D1_1_IMAGE:
+ aStream << "SurfaceType::D2D1_1_IMAGE"; break;
+ case SurfaceType::RECORDING:
+ aStream << "SurfaceType::RECORDING"; break;
+ case SurfaceType::TILED:
+ aStream << "SurfaceType::TILED"; break;
+ default:
+ NS_ERROR("unknown surface type");
+ aStream << "???";
+ }
+ aStream << sfx;
+}
+
+
+void
+AppendToString(std::stringstream& aStream, ImageFormat format,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch (format) {
+ case ImageFormat::PLANAR_YCBCR:
+ aStream << "ImageFormat::PLANAR_YCBCR"; break;
+ case ImageFormat::GRALLOC_PLANAR_YCBCR:
+ aStream << "ImageFormat::GRALLOC_PLANAR_YCBCR"; break;
+ case ImageFormat::SHARED_RGB:
+ aStream << "ImageFormat::SHARED_RGB"; break;
+ case ImageFormat::CAIRO_SURFACE:
+ aStream << "ImageFormat::CAIRO_SURFACE"; break;
+ case ImageFormat::MAC_IOSURFACE:
+ aStream << "ImageFormat::MAC_IOSURFACE"; break;
+ case ImageFormat::SURFACE_TEXTURE:
+ aStream << "ImageFormat::SURFACE_TEXTURE"; break;
+ case ImageFormat::EGLIMAGE:
+ aStream << "ImageFormat::EGLIMAGE"; break;
+ case ImageFormat::D3D9_RGB32_TEXTURE:
+ aStream << "ImageFormat::D3D9_RBG32_TEXTURE"; break;
+ case ImageFormat::OVERLAY_IMAGE:
+ aStream << "ImageFormat::OVERLAY_IMAGE"; break;
+ case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
+ aStream << "ImageFormat::D3D11_SHARE_HANDLE_TEXTURE"; break;
+ default:
+ NS_ERROR("unknown image format");
+ aStream << "???";
+ }
+
+ aStream << sfx;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+void
+print_stderr(std::stringstream& aStr)
+{
+ printf_stderr("%s", aStr.str().c_str());
+}
+
+void
+fprint_stderr(FILE* aFile, std::stringstream& aStr)
+{
+ if (aFile == stderr) {
+ print_stderr(aStr);
+ } else {
+ fprintf_stderr(aFile, "%s", aStr.str().c_str());
+ }
+}
diff --git a/system/graphics/layers/LayersLogging.h b/system/graphics/layers/LayersLogging.h
new file mode 100644
index 000000000..cb123a245
--- /dev/null
+++ b/system/graphics/layers/LayersLogging.h
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSLOGGING_H
+#define GFX_LAYERSLOGGING_H
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, etc
+#include "mozilla/gfx/TiledRegion.h" // for TiledRegion
+#include "mozilla/gfx/Types.h" // for SamplingFilter, SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsRegion.h" // for nsRegion, nsIntRegion
+#include "nscore.h" // for nsACString, etc
+
+namespace mozilla {
+namespace gfx {
+template <class units, class F> struct RectTyped;
+} // namespace gfx
+
+enum class ImageFormat;
+
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, const void* p,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const gfx::Color& c,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsPoint& p,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsRect& r,
+ const char* pfx="", const char* sfx="");
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::PointTyped<T>& p,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx << p << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntPointTyped<T>& p,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx << p << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::RectTyped<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%f, y=%f, w=%f, h=%f)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectTyped<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsIntRegion& r,
+ const char* pfx="", const char* sfx="");
+
+template <typename units>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRegionTyped<units>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+template <typename T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::TiledRegion<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ AppendToString(aStream, r.GetRegion());
+ aStream << " (bounds=";
+ AppendToString(aStream, r.GetBounds());
+ aStream << ", covers=" << r.CoversBounds() << ")" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const EventRegions& e,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const ScrollMetadata& m,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const FrameMetrics& m,
+ const char* pfx="", const char* sfx="", bool detailed = false);
+
+void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
+ const char* pfx="", const char* sfx="");
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(l=%f, t=%f, r=%f, b=%f)",
+ m.left, m.top, m.right, m.bottom).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::SizeTyped<T>& sz,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(w=%f, h=%f)",
+ sz.width, sz.height).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntSizeTyped<T>& sz,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(w=%d, h=%d)",
+ sz.width, sz.height).get();
+ aStream << sfx;
+}
+
+template<class src, class dst>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::ScaleFactors2D<src, dst>& scale,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ std::streamsize oldPrecision = aStream.precision(3);
+ if (scale.AreScalesSame()) {
+ aStream << scale.xScale;
+ } else {
+ aStream << '(' << scale.xScale << ',' << scale.yScale << ')';
+ }
+ aStream.precision(oldPrecision);
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix& m,
+ const char* pfx="", const char* sfx="");
+
+template<class SourceUnits, class TargetUnits>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& m,
+ const char* pfx="", const char* sfx="")
+{
+ if (m.Is2D()) {
+ mozilla::gfx::Matrix matrix = m.As2D();
+ AppendToString(aStream, matrix, pfx, sfx);
+ return;
+ }
+
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
+ m._11, m._12, m._13, m._14,
+ m._21, m._22, m._23, m._24,
+ m._31, m._32, m._33, m._34,
+ m._41, m._42, m._43, m._44).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream,
+ const mozilla::gfx::SamplingFilter samplingFilter,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, mozilla::layers::TextureFlags flags,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, mozilla::gfx::SurfaceFormat format,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, gfx::SurfaceType format,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, ImageFormat format,
+ const char* pfx="", const char* sfx="");
+
+// Sometimes, you just want a string from a single value.
+template <typename T>
+std::string
+Stringify(const T& obj)
+{
+ std::stringstream ss;
+ AppendToString(ss, obj);
+ return ss.str();
+}
+
+} // namespace layers
+} // namespace mozilla
+
+// versions of printf_stderr and fprintf_stderr that deal with line
+// truncation on android by printing individual lines out of the
+// stringstream as separate calls to logcat.
+void print_stderr(std::stringstream& aStr);
+void fprint_stderr(FILE* aFile, std::stringstream& aStr);
+
+#endif /* GFX_LAYERSLOGGING_H */
diff --git a/system/graphics/layers/LayersTypes.cpp b/system/graphics/layers/LayersTypes.cpp
new file mode 100644
index 000000000..a79f44554
--- /dev/null
+++ b/system/graphics/layers/LayersTypes.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+LayerRenderState::LayerRenderState()
+ : mFlags(LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT)
+ , mHasOwnOffset(false)
+{
+}
+
+LayerRenderState::LayerRenderState(const LayerRenderState& aOther)
+ : mFlags(aOther.mFlags)
+ , mHasOwnOffset(aOther.mHasOwnOffset)
+ , mOffset(aOther.mOffset)
+{
+}
+
+LayerRenderState::~LayerRenderState()
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/LayersTypes.h b/system/graphics/layers/LayersTypes.h
new file mode 100644
index 000000000..8d52ed2fd
--- /dev/null
+++ b/system/graphics/layers/LayersTypes.h
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSTYPES_H
+#define GFX_LAYERSTYPES_H
+
+#include <stdint.h> // for uint32_t
+
+#include "Units.h"
+#include "mozilla/gfx/Point.h" // for IntPoint
+#include "mozilla/TypedEnumBits.h"
+#include "nsRegion.h"
+
+#include <stdio.h> // FILE
+#include "mozilla/Logging.h" // for PR_LOG
+
+#ifndef MOZ_LAYERS_HAVE_LOG
+# define MOZ_LAYERS_HAVE_LOG
+#endif
+#define MOZ_LAYERS_LOG(_args) \
+ MOZ_LOG(LayerManager::GetLog(), LogLevel::Debug, _args)
+#define MOZ_LAYERS_LOG_IF_SHADOWABLE(layer, _args) \
+ do { if (layer->AsShadowableLayer()) { MOZ_LOG(LayerManager::GetLog(), LogLevel::Debug, _args); } } while (0)
+
+#define INVALID_OVERLAY -1
+
+namespace android {
+class MOZ_EXPORT GraphicBuffer;
+} // namespace android
+
+namespace mozilla {
+namespace layers {
+
+class TextureHost;
+
+#undef NONE
+#undef OPAQUE
+
+enum class LayersBackend : int8_t {
+ LAYERS_NONE = 0,
+ LAYERS_BASIC,
+ LAYERS_OPENGL,
+ LAYERS_D3D9,
+ LAYERS_D3D11,
+ LAYERS_CLIENT,
+ LAYERS_LAST
+};
+
+enum class BufferMode : int8_t {
+ BUFFER_NONE,
+ BUFFERED
+};
+
+enum class DrawRegionClip : int8_t {
+ DRAW,
+ NONE
+};
+
+enum class SurfaceMode : int8_t {
+ SURFACE_NONE = 0,
+ SURFACE_OPAQUE,
+ SURFACE_SINGLE_CHANNEL_ALPHA,
+ SURFACE_COMPONENT_ALPHA
+};
+
+// LayerRenderState for Composer2D
+// We currently only support Composer2D using gralloc. If we want to be backed
+// by other surfaces we will need a more generic LayerRenderState.
+enum class LayerRenderStateFlags : int8_t {
+ LAYER_RENDER_STATE_DEFAULT = 0,
+ ORIGIN_BOTTOM_LEFT = 1 << 0,
+ BUFFER_ROTATION = 1 << 1,
+ // Notify Composer2D to swap the RB pixels of gralloc buffer
+ FORMAT_RB_SWAP = 1 << 2,
+ // We record opaqueness here alongside the actual surface we're going to
+ // render. This avoids confusion when a layer might return different kinds
+ // of surfaces over time (e.g. video frames).
+ OPAQUE = 1 << 3
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LayerRenderStateFlags)
+
+struct LayerRenderState {
+ // Constructors and destructor are defined in LayersTypes.cpp so we don't
+ // have to pull in a definition for GraphicBuffer.h here. In KK at least,
+ // that results in nasty pollution such as libui's hardware.h #defining
+ // 'version_major' and 'version_minor' which conflict with Theora's codec.c...
+ LayerRenderState();
+ LayerRenderState(const LayerRenderState& aOther);
+ ~LayerRenderState();
+
+ void SetOffset(const nsIntPoint& aOffset)
+ {
+ mOffset = aOffset;
+ mHasOwnOffset = true;
+ }
+
+ // see LayerRenderStateFlags
+ LayerRenderStateFlags mFlags;
+ // true if mOffset is applicable
+ bool mHasOwnOffset;
+ // the location of the layer's origin on mSurface
+ nsIntPoint mOffset;
+};
+
+enum class ScaleMode : int8_t {
+ SCALE_NONE,
+ STRETCH,
+ SENTINEL
+// Unimplemented - PRESERVE_ASPECT_RATIO_CONTAIN
+};
+
+struct EventRegions {
+ // The hit region for a layer contains all areas on the layer that are
+ // sensitive to events. This region is an over-approximation and may
+ // contain regions that are not actually sensitive, but any such regions
+ // will be included in the mDispatchToContentHitRegion.
+ nsIntRegion mHitRegion;
+ // The mDispatchToContentHitRegion for a layer contains all areas for
+ // which the main-thread must be consulted before responding to events.
+ // This region will be a subregion of mHitRegion.
+ nsIntRegion mDispatchToContentHitRegion;
+
+ // The following regions represent the touch-action areas of this layer.
+ // All of these regions are approximations to the true region, but any
+ // variance between the approximation and the true region is guaranteed
+ // to be included in the mDispatchToContentHitRegion.
+ nsIntRegion mNoActionRegion;
+ nsIntRegion mHorizontalPanRegion;
+ nsIntRegion mVerticalPanRegion;
+
+ EventRegions()
+ {
+ }
+
+ explicit EventRegions(nsIntRegion aHitRegion)
+ : mHitRegion(aHitRegion)
+ {
+ }
+
+ bool operator==(const EventRegions& aRegions) const
+ {
+ return mHitRegion == aRegions.mHitRegion &&
+ mDispatchToContentHitRegion == aRegions.mDispatchToContentHitRegion &&
+ mNoActionRegion == aRegions.mNoActionRegion &&
+ mHorizontalPanRegion == aRegions.mHorizontalPanRegion &&
+ mVerticalPanRegion == aRegions.mVerticalPanRegion;
+ }
+ bool operator!=(const EventRegions& aRegions) const
+ {
+ return !(*this == aRegions);
+ }
+
+ void ApplyTranslationAndScale(float aXTrans, float aYTrans, float aXScale, float aYScale)
+ {
+ mHitRegion.ScaleRoundOut(aXScale, aYScale);
+ mDispatchToContentHitRegion.ScaleRoundOut(aXScale, aYScale);
+ mNoActionRegion.ScaleRoundOut(aXScale, aYScale);
+ mHorizontalPanRegion.ScaleRoundOut(aXScale, aYScale);
+ mVerticalPanRegion.ScaleRoundOut(aXScale, aYScale);
+
+ mHitRegion.MoveBy(aXTrans, aYTrans);
+ mDispatchToContentHitRegion.MoveBy(aXTrans, aYTrans);
+ mNoActionRegion.MoveBy(aXTrans, aYTrans);
+ mHorizontalPanRegion.MoveBy(aXTrans, aYTrans);
+ mVerticalPanRegion.MoveBy(aXTrans, aYTrans);
+ }
+
+ void Transform(const gfx::Matrix4x4& aTransform)
+ {
+ mHitRegion.Transform(aTransform);
+ mDispatchToContentHitRegion.Transform(aTransform);
+ mNoActionRegion.Transform(aTransform);
+ mHorizontalPanRegion.Transform(aTransform);
+ mVerticalPanRegion.Transform(aTransform);
+ }
+
+ bool IsEmpty() const
+ {
+ return mHitRegion.IsEmpty()
+ && mDispatchToContentHitRegion.IsEmpty()
+ && mNoActionRegion.IsEmpty()
+ && mHorizontalPanRegion.IsEmpty()
+ && mVerticalPanRegion.IsEmpty();
+ }
+
+ nsCString ToString() const
+ {
+ nsCString result = mHitRegion.ToString();
+ result.AppendLiteral(";dispatchToContent=");
+ result.Append(mDispatchToContentHitRegion.ToString());
+ return result;
+ }
+};
+
+// Bit flags that go on a ContainerLayer (or RefLayer) and override the
+// event regions in the entire subtree below. This is needed for propagating
+// various flags across processes since the child-process layout code doesn't
+// know about parent-process listeners or CSS rules.
+enum EventRegionsOverride {
+ // The default, no flags set
+ NoOverride = 0,
+ // Treat all hit regions in the subtree as dispatch-to-content
+ ForceDispatchToContent = (1 << 0),
+ // Treat all hit regions in the subtree as empty
+ ForceEmptyHitRegion = (1 << 1),
+ // OR union of all valid bit flags, for use in BitFlagsEnumSerializer
+ ALL_BITS = (1 << 2) - 1
+};
+
+MOZ_ALWAYS_INLINE EventRegionsOverride
+operator|(EventRegionsOverride a, EventRegionsOverride b)
+{
+ return (EventRegionsOverride)((int)a | (int)b);
+}
+
+MOZ_ALWAYS_INLINE EventRegionsOverride&
+operator|=(EventRegionsOverride& a, EventRegionsOverride b)
+{
+ a = a | b;
+ return a;
+}
+
+// Flags used as an argument to functions that dump textures.
+enum TextureDumpMode {
+ Compress, // dump texture with LZ4 compression
+ DoNotCompress // dump texture uncompressed
+};
+
+// Some specialized typedefs of Matrix4x4Typed.
+typedef gfx::Matrix4x4Typed<LayerPixel, CSSTransformedLayerPixel> CSSTransformMatrix;
+// Several different async transforms can contribute to a layer's transform
+// (specifically, an async animation can contribute a transform, and each APZC
+// that scrolls a layer can contribute async scroll/zoom and overscroll
+// transforms).
+// To try to model this with typed units, we represent individual async
+// transforms as ParentLayer -> ParentLayer transforms (aliased as
+// AsyncTransformComponentMatrix), and we represent the product of all of them
+// as a CSSTransformLayer -> ParentLayer transform (aliased as
+// AsyncTransformMatrix). To create an AsyncTransformMatrix from component
+// matrices, a ViewAs operation is needed. A MultipleAsyncTransforms
+// PixelCastJustification is provided for this purpose.
+typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> AsyncTransformComponentMatrix;
+typedef gfx::Matrix4x4Typed<CSSTransformedLayerPixel, ParentLayerPixel> AsyncTransformMatrix;
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSTYPES_H */
diff --git a/system/graphics/layers/MacIOSurfaceHelpers.cpp b/system/graphics/layers/MacIOSurfaceHelpers.cpp
new file mode 100644
index 000000000..4ca32427a
--- /dev/null
+++ b/system/graphics/layers/MacIOSurfaceHelpers.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "libyuv.h"
+#include "MacIOSurfaceHelpers.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "YCbCrUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+#define ALIGNED_32(x) ((x+31)&~31)
+#define ALIGNEDPTR_32(x) reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(x)+31)&~31)
+
+static already_AddRefed<SourceSurface>
+CreateSourceSurfaceFromLockedMacIOSurface(MacIOSurface* aSurface)
+{
+ size_t bytesPerRow = aSurface->GetBytesPerRow();
+ size_t ioWidth = aSurface->GetDevicePixelWidth();
+ size_t ioHeight = aSurface->GetDevicePixelHeight();
+ SurfaceFormat ioFormat = aSurface->GetFormat();
+
+ if ((ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422) &&
+ (ioWidth > PlanarYCbCrImage::MAX_DIMENSION ||
+ ioHeight > PlanarYCbCrImage::MAX_DIMENSION)) {
+ return nullptr;
+ }
+
+ SurfaceFormat format =
+ (ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+
+ RefPtr<DataSourceSurface> dataSurface =
+ Factory::CreateDataSourceSurface(IntSize::Truncate(ioWidth, ioHeight), format);
+ if (NS_WARN_IF(!dataSurface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface mappedSurface;
+ if (!dataSurface->Map(DataSourceSurface::WRITE, &mappedSurface)) {
+ return nullptr;
+ }
+
+ if (ioFormat == SurfaceFormat::NV12) {
+ /* Extract and separate the CbCr planes */
+ size_t cbCrStride = aSurface->GetBytesPerRow(1);
+ size_t cbCrWidth = aSurface->GetDevicePixelWidth(1);
+ size_t cbCrHeight = aSurface->GetDevicePixelHeight(1);
+
+ auto cbPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight);
+ auto crPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight);
+
+ uint8_t* src = (uint8_t*)aSurface->GetBaseAddressOfPlane(1);
+ uint8_t* cbDest = cbPlane.get();
+ uint8_t* crDest = crPlane.get();
+
+ for (size_t i = 0; i < cbCrHeight; i++) {
+ uint8_t* rowSrc = src + cbCrStride * i;
+ for (size_t j = 0; j < cbCrWidth; j++) {
+ *cbDest = *rowSrc;
+ cbDest++;
+ rowSrc++;
+ *crDest = *rowSrc;
+ crDest++;
+ rowSrc++;
+ }
+ }
+
+ /* Convert to RGB */
+ PlanarYCbCrData data;
+ data.mYChannel = (uint8_t*)aSurface->GetBaseAddressOfPlane(0);
+ data.mYStride = aSurface->GetBytesPerRow(0);
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
+ data.mCbChannel = cbPlane.get();
+ data.mCrChannel = crPlane.get();
+ data.mCbCrStride = cbCrWidth;
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
+ data.mPicSize = data.mYSize;
+
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ } else if (ioFormat == SurfaceFormat::YUV422) {
+ if (ioWidth == ALIGNED_32(ioWidth)) {
+ // Optimization when width is aligned to 32.
+ IntSize size = IntSize::Truncate(ioWidth, ioHeight);
+ libyuv::ConvertToARGB((uint8_t*)aSurface->GetBaseAddress(), 0 /* not used */,
+ mappedSurface.mData, mappedSurface.mStride,
+ 0, 0,
+ size.width, size.height,
+ size.width, size.height,
+ libyuv::kRotate0, libyuv::FOURCC_UYVY);
+ } else {
+ /* Convert to YV16 */
+ size_t cbCrWidth = (ioWidth+1)>>1;
+ size_t cbCrHeight = ioHeight;
+ // Ensure our stride is a multiple of 32 to allow for memory aligned rows.
+ size_t cbCrStride = ALIGNED_32(cbCrWidth);
+ size_t strideDelta = cbCrStride - cbCrWidth;
+ MOZ_ASSERT(strideDelta <= 31);
+
+ auto yPlane = MakeUnique<uint8_t[]>(cbCrStride * 2 * ioHeight + 31);
+ auto cbPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31);
+ auto crPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31);
+
+ uint8_t* src = (uint8_t*)aSurface->GetBaseAddress();
+ uint8_t* yDest = ALIGNEDPTR_32(yPlane.get());
+ uint8_t* cbDest = ALIGNEDPTR_32(cbPlane.get());
+ uint8_t* crDest = ALIGNEDPTR_32(crPlane.get());
+
+ for (size_t i = 0; i < ioHeight; i++) {
+ uint8_t* rowSrc = src + bytesPerRow * i;
+ for (size_t j = 0; j < cbCrWidth; j++) {
+ *cbDest = *rowSrc;
+ cbDest++;
+ rowSrc++;
+ *yDest = *rowSrc;
+ yDest++;
+ rowSrc++;
+ *crDest = *rowSrc;
+ crDest++;
+ rowSrc++;
+ *yDest = *rowSrc;
+ yDest++;
+ rowSrc++;
+ }
+ if (strideDelta) {
+ cbDest += strideDelta;
+ crDest += strideDelta;
+ yDest += strideDelta << 1;
+ }
+ }
+
+ /* Convert to RGB */
+ PlanarYCbCrData data;
+ data.mYChannel = ALIGNEDPTR_32(yPlane.get());
+ data.mYStride = cbCrStride * 2;
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
+ data.mCbChannel = ALIGNEDPTR_32(cbPlane.get());
+ data.mCrChannel = ALIGNEDPTR_32(crPlane.get());
+ data.mCbCrStride = cbCrStride;
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
+ data.mPicSize = data.mYSize;
+
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ }
+ } else {
+ unsigned char* ioData = (unsigned char*)aSurface->GetBaseAddress();
+
+ for (size_t i = 0; i < ioHeight; ++i) {
+ memcpy(mappedSurface.mData + i * mappedSurface.mStride,
+ ioData + i * bytesPerRow,
+ ioWidth * 4);
+ }
+ }
+
+ dataSurface->Unmap();
+
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+CreateSourceSurfaceFromMacIOSurface(MacIOSurface* aSurface)
+{
+ aSurface->Lock();
+ RefPtr<SourceSurface> result = CreateSourceSurfaceFromLockedMacIOSurface(aSurface);
+ aSurface->Unlock();
+ return result.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/layers/MacIOSurfaceHelpers.h b/system/graphics/layers/MacIOSurfaceHelpers.h
new file mode 100644
index 000000000..80f9cf872
--- /dev/null
+++ b/system/graphics/layers/MacIOSurfaceHelpers.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MACIOSURFACEHELPERS_H
+#define GFX_MACIOSURFACEHELPERS_H
+
+class MacIOSurface;
+template<class T> struct already_AddRefed;
+
+namespace mozilla {
+
+namespace gfx {
+class SourceSurface;
+}
+
+namespace layers {
+
+// Unlike MacIOSurface::GetAsSurface, this also handles IOSurface formats
+// with multiple planes and does YCbCr to RGB conversion, if necessary.
+already_AddRefed<gfx::SourceSurface>
+CreateSourceSurfaceFromMacIOSurface(MacIOSurface* aSurface);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_MACIOSURFACEHELPERS_H
diff --git a/system/graphics/layers/MacIOSurfaceImage.cpp b/system/graphics/layers/MacIOSurfaceImage.cpp
new file mode 100644
index 000000000..75fb1f2d6
--- /dev/null
+++ b/system/graphics/layers/MacIOSurfaceImage.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurfaceHelpers.h"
+#include "MacIOSurfaceImage.h"
+#include "gfxPlatform.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+TextureClient*
+MacIOSurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (!mTextureClient) {
+ BackendType backend = BackendType::NONE;
+ mTextureClient = TextureClient::CreateWithData(
+ MacIOSurfaceTextureData::Create(mSurface, backend),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+ }
+ return mTextureClient;
+}
+
+already_AddRefed<SourceSurface>
+MacIOSurfaceImage::GetAsSourceSurface()
+{
+ return CreateSourceSurfaceFromMacIOSurface(mSurface);
+}
diff --git a/system/graphics/layers/MacIOSurfaceImage.h b/system/graphics/layers/MacIOSurfaceImage.h
new file mode 100644
index 000000000..769d63ef4
--- /dev/null
+++ b/system/graphics/layers/MacIOSurfaceImage.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MACIOSURFACEIMAGE_H
+#define GFX_MACIOSURFACEIMAGE_H
+
+#include "ImageContainer.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class MacIOSurfaceImage : public Image {
+public:
+ explicit MacIOSurfaceImage(MacIOSurface* aSurface)
+ : Image(nullptr, ImageFormat::MAC_IOSURFACE),
+ mSurface(aSurface)
+ {}
+
+ MacIOSurface* GetSurface() { return mSurface; }
+
+ gfx::IntSize GetSize() override {
+ return gfx::IntSize::Truncate(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+ }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override {
+ return this;
+ }
+
+private:
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_SHAREDTEXTUREIMAGE_H
diff --git a/system/graphics/layers/PersistentBufferProvider.cpp b/system/graphics/layers/PersistentBufferProvider.cpp
new file mode 100644
index 000000000..d6269257f
--- /dev/null
+++ b/system/graphics/layers/PersistentBufferProvider.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PersistentBufferProvider.h"
+
+#include "Layers.h"
+#include "mozilla/gfx/Logging.h"
+#include "pratom.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
+: mDrawTarget(aDt)
+{
+ MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
+}
+
+PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
+{
+ MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+ MOZ_ASSERT(!mSnapshot);
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+}
+
+bool
+PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+ RefPtr<gfx::DrawTarget> dt(aDT);
+ MOZ_ASSERT(mDrawTarget == dt);
+ if (dt) {
+ // Since SkiaGL default to storing drawing command until flush
+ // we have to flush it before present.
+ dt->Flush();
+ }
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderBasic::BorrowSnapshot()
+{
+ mSnapshot = mDrawTarget->Snapshot();
+ RefPtr<SourceSurface> snapshot = mSnapshot;
+ return snapshot.forget();
+}
+
+void
+PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+ RefPtr<SourceSurface> snapshot = aSnapshot;
+ MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+ mSnapshot = nullptr;
+}
+
+//static
+already_AddRefed<PersistentBufferProviderBasic>
+PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend)
+{
+ RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+
+ if (!dt) {
+ return nullptr;
+ }
+
+ RefPtr<PersistentBufferProviderBasic> provider =
+ new PersistentBufferProviderBasic(dt);
+
+ return provider.forget();
+}
+
+
+//static
+already_AddRefed<PersistentBufferProviderShared>
+PersistentBufferProviderShared::Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd)
+{
+ if (!aFwd || !aFwd->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
+ aFwd, aFormat, aSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<PersistentBufferProviderShared> provider =
+ new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
+ return provider.forget();
+}
+
+PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd,
+ RefPtr<TextureClient>& aTexture)
+
+: mSize(aSize)
+, mFormat(aFormat)
+, mFwd(aFwd)
+, mFront(Nothing())
+{
+ if (mTextures.append(aTexture)) {
+ mBack = Some<uint32_t>(0);
+ }
+ MOZ_COUNT_CTOR(PersistentBufferProviderShared);
+}
+
+PersistentBufferProviderShared::~PersistentBufferProviderShared()
+{
+ MOZ_COUNT_DTOR(PersistentBufferProviderShared);
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().RemoveObject(this);
+ }
+
+ Destroy();
+}
+
+bool
+PersistentBufferProviderShared::SetForwarder(ShadowLayerForwarder* aFwd)
+{
+ MOZ_ASSERT(aFwd);
+ if (!aFwd) {
+ return false;
+ }
+
+ if (mFwd == aFwd) {
+ // The forwarder should not change most of the time.
+ return true;
+ }
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().RemoveObject(this);
+ }
+
+ if (mFwd->GetTextureForwarder() != aFwd->GetTextureForwarder() ||
+ mFwd->GetCompositorBackendType() != aFwd->GetCompositorBackendType()) {
+ // We are going to be used with an different and/or incompatible forwarder.
+ // This should be extremely rare. We have to copy the front buffer into a
+ // texture that is compatible with the new forwarder.
+
+ // Grab the current front buffer.
+ RefPtr<TextureClient> prevTexture = GetTexture(mFront);
+
+ // Get rid of everything else
+ Destroy();
+
+ if (prevTexture) {
+ RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+ aFwd, mFormat, mSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ MOZ_ASSERT(newTexture);
+ if (!newTexture) {
+ return false;
+ }
+
+ // If we early-return in one of the following branches, we will
+ // leave the buffer provider in an empty state, since we called
+ // Destroy. Not ideal but at least we won't try to use it with a
+ // an incompatible ipc channel.
+
+ if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
+ return false;
+ }
+
+ if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
+ newTexture->Unlock();
+ return false;
+ }
+
+ bool success = prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
+
+ prevTexture->Unlock();
+ newTexture->Unlock();
+
+ if (!success) {
+ return false;
+ }
+
+ if (!mTextures.append(newTexture)) {
+ return false;
+ }
+ mFront = Some<uint32_t>(mTextures.length() - 1);
+ mBack = mFront;
+ }
+ }
+
+ mFwd = aFwd;
+
+ return true;
+}
+
+TextureClient*
+PersistentBufferProviderShared::GetTexture(Maybe<uint32_t> aIndex)
+{
+ if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
+ return nullptr;
+ }
+ return mTextures[aIndex.value()];
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+ if (!mFwd->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!mSnapshot);
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().MarkUsed(this);
+ } else {
+ mFwd->GetActiveResourceTracker().AddObject(this);
+ }
+
+ if (mDrawTarget) {
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+ }
+
+ mFront = Nothing();
+
+ auto previousBackBuffer = mBack;
+
+ TextureClient* tex = GetTexture(mBack);
+
+ // First try to reuse the current back buffer. If we can do that it means
+ // we can skip copying its content to the new back buffer.
+ if (tex && tex->IsReadLocked()) {
+ // The back buffer is currently used by the compositor, we can't draw
+ // into it.
+ tex = nullptr;
+ }
+
+ if (!tex) {
+ // Try to grab an already allocated texture if any is available.
+ for (uint32_t i = 0; i < mTextures.length(); ++i) {
+ if (!mTextures[i]->IsReadLocked()) {
+ mBack = Some(i);
+ tex = mTextures[i];
+ break;
+ }
+ }
+ }
+
+ if (!tex) {
+ // We have to allocate a new texture.
+ if (mTextures.length() >= 4) {
+ // We should never need to buffer that many textures, something's wrong.
+ MOZ_ASSERT(false);
+ // In theory we throttle the main thread when the compositor can't keep up,
+ // so we shoud never get in a situation where we sent 4 textures to the
+ // compositor and the latter as not released any of them.
+ // This seems to happen, however, in some edge cases such as just after a
+ // device reset (cf. Bug 1291163).
+ // It would be pretty bad to keep piling textures up at this point so we
+ // call NotifyInactive to remove some of our textures.
+ NotifyInactive();
+ // Give up now. The caller can fall-back to a non-shared buffer provider.
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+ mFwd, mFormat, mSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ MOZ_ASSERT(newTexture);
+ if (newTexture) {
+ if (mTextures.append(newTexture)) {
+ tex = newTexture;
+ mBack = Some<uint32_t>(mTextures.length() - 1);
+ }
+ }
+ }
+
+ if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
+ return nullptr;
+ }
+
+ if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
+ TextureClient* previous = GetTexture(previousBackBuffer);
+ if (previous && previous->Lock(OpenMode::OPEN_READ)) {
+ DebugOnly<bool> success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
+ MOZ_ASSERT(success);
+
+ previous->Unlock();
+ }
+ }
+
+ mDrawTarget = tex->BorrowDrawTarget();
+
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+}
+
+bool
+PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+ RefPtr<gfx::DrawTarget> dt(aDT);
+ MOZ_ASSERT(mDrawTarget == dt);
+ // Can't change the current front buffer while its snapshot is borrowed!
+ MOZ_ASSERT(!mSnapshot);
+
+ mDrawTarget = nullptr;
+ dt = nullptr;
+
+ TextureClient* back = GetTexture(mBack);
+ MOZ_ASSERT(back);
+
+ if (back) {
+ back->Unlock();
+ mFront = mBack;
+ }
+
+ return !!back;
+}
+
+TextureClient*
+PersistentBufferProviderShared::GetTextureClient()
+{
+ // Can't access the front buffer while drawing.
+ MOZ_ASSERT(!mDrawTarget);
+ TextureClient* texture = GetTexture(mFront);
+ if (texture) {
+ texture->EnableReadLock();
+ } else {
+ gfxCriticalNote << "PersistentBufferProviderShared: front buffer unavailable";
+ }
+ return texture;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderShared::BorrowSnapshot()
+{
+ MOZ_ASSERT(!mDrawTarget);
+
+ auto front = GetTexture(mFront);
+ if (!front || front->IsLocked()) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ if (!front->Lock(OpenMode::OPEN_READ)) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt = front->BorrowDrawTarget();
+
+ if (!dt) {
+ front->Unlock();
+ return nullptr;
+ }
+
+ mSnapshot = dt->Snapshot();
+
+ RefPtr<SourceSurface> snapshot = mSnapshot;
+ return snapshot.forget();
+}
+
+void
+PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+ RefPtr<SourceSurface> snapshot = aSnapshot;
+ MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+
+ mSnapshot = nullptr;
+ snapshot = nullptr;
+
+ auto front = GetTexture(mFront);
+ if (front) {
+ front->Unlock();
+ }
+}
+
+void
+PersistentBufferProviderShared::NotifyInactive()
+{
+ RefPtr<TextureClient> front = GetTexture(mFront);
+ RefPtr<TextureClient> back = GetTexture(mBack);
+
+ // Clear all textures (except the front and back ones that we just kept).
+ mTextures.clear();
+
+ if (back) {
+ if (mTextures.append(back)) {
+ mBack = Some<uint32_t>(0);
+ }
+ if (front == back) {
+ mFront = mBack;
+ }
+ }
+
+ if (front && front != back) {
+ if (mTextures.append(front)) {
+ mFront = Some<uint32_t>(mTextures.length() - 1);
+ }
+ }
+}
+
+void
+PersistentBufferProviderShared::Destroy()
+{
+ mSnapshot = nullptr;
+ mDrawTarget = nullptr;
+
+ for (uint32_t i = 0; i < mTextures.length(); ++i) {
+ TextureClient* texture = mTextures[i];
+ if (texture && texture->IsLocked()) {
+ MOZ_ASSERT(false);
+ texture->Unlock();
+ }
+ }
+
+ mTextures.clear();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/PersistentBufferProvider.h b/system/graphics/layers/PersistentBufferProvider.h
new file mode 100644
index 000000000..1a1355ca9
--- /dev/null
+++ b/system/graphics/layers/PersistentBufferProvider.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PersistentBUFFERPROVIDER_H
+#define MOZILLA_GFX_PersistentBUFFERPROVIDER_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+
+namespace gfx {
+ class SourceSurface;
+ class DrawTarget;
+}
+
+namespace layers {
+
+class CopyableCanvasLayer;
+
+/**
+ * A PersistentBufferProvider is for users which require the temporary use of
+ * a DrawTarget to draw into. When they're done drawing they return the
+ * DrawTarget, when they later need to continue drawing they get a DrawTarget
+ * from the provider again, the provider will guarantee the contents of the
+ * previously returned DrawTarget is persisted into the one newly returned.
+ */
+class PersistentBufferProvider : public RefCounted<PersistentBufferProvider>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProvider)
+
+ virtual ~PersistentBufferProvider() { }
+
+ virtual LayersBackend GetType() { return LayersBackend::LAYERS_NONE; }
+
+ /**
+ * Get a DrawTarget from the PersistentBufferProvider.
+ *
+ * \param aPersistedRect This indicates the area of the DrawTarget that needs
+ * to have remained the same since the call to
+ * ReturnDrawTarget.
+ */
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) = 0;
+
+ /**
+ * Return a DrawTarget to the PersistentBufferProvider and indicate the
+ * contents of this DrawTarget is to be considered current by the
+ * BufferProvider. The caller should forget any references to the DrawTarget.
+ */
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
+
+ virtual TextureClient* GetTextureClient() { return nullptr; }
+
+ virtual void OnShutdown() {}
+
+ virtual bool SetForwarder(ShadowLayerForwarder* aFwd) { return true; }
+
+ /**
+ * Return true if this provider preserves the drawing state (clips, transforms,
+ * etc.) across frames. In practice this means users of the provider can skip
+ * popping all of the clips at the end of the frames and pushing them back at
+ * the beginning of the following frames, which can be costly (cf. bug 1294351).
+ */
+ virtual bool PreservesDrawingState() const = 0;
+};
+
+
+class PersistentBufferProviderBasic : public PersistentBufferProvider
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
+
+ static already_AddRefed<PersistentBufferProviderBasic>
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend);
+
+ explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget);
+
+ virtual LayersBackend GetType() override { return LayersBackend::LAYERS_BASIC; }
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+ virtual bool PreservesDrawingState() const override { return true; }
+private:
+ ~PersistentBufferProviderBasic();
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RefPtr<gfx::SourceSurface> mSnapshot;
+};
+
+
+/**
+ * Provides access to a buffer which can be sent to the compositor without
+ * requiring a copy.
+ */
+class PersistentBufferProviderShared : public PersistentBufferProvider
+ , public ActiveResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
+
+ static already_AddRefed<PersistentBufferProviderShared>
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd);
+
+ virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; }
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+ virtual TextureClient* GetTextureClient() override;
+
+ virtual void NotifyInactive() override;
+
+ virtual void OnShutdown() override { Destroy(); }
+
+ virtual bool SetForwarder(ShadowLayerForwarder* aFwd) override;
+
+ virtual bool PreservesDrawingState() const override { return false; }
+protected:
+ PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd,
+ RefPtr<TextureClient>& aTexture);
+
+ ~PersistentBufferProviderShared();
+
+ TextureClient* GetTexture(Maybe<uint32_t> aIndex);
+ bool CheckIndex(uint32_t aIndex) { return aIndex < mTextures.length(); }
+
+ void Destroy();
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<ShadowLayerForwarder> mFwd;
+ Vector<RefPtr<TextureClient>, 4> mTextures;
+ // Offset of the texture in mTextures that the canvas uses.
+ Maybe<uint32_t> mBack;
+ // Offset of the texture in mTextures that is presented to the compositor.
+ Maybe<uint32_t> mFront;
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RefPtr<gfx::SourceSurface > mSnapshot;
+};
+
+struct AutoReturnSnapshot
+{
+ PersistentBufferProvider* mBufferProvider;
+ RefPtr<gfx::SourceSurface>* mSnapshot;
+
+ explicit AutoReturnSnapshot(PersistentBufferProvider* aProvider = nullptr)
+ : mBufferProvider(aProvider)
+ , mSnapshot(nullptr)
+ {}
+
+ ~AutoReturnSnapshot()
+ {
+ if (mBufferProvider) {
+ mBufferProvider->ReturnSnapshot(mSnapshot ? mSnapshot->forget() : nullptr);
+ }
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ReadbackLayer.h b/system/graphics/layers/ReadbackLayer.h
new file mode 100644
index 000000000..4f7059110
--- /dev/null
+++ b/system/graphics/layers/ReadbackLayer.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKLAYER_H
+#define GFX_READBACKLAYER_H
+
+#include <stdint.h> // for uint64_t
+#include "Layers.h" // for Layer, etc
+#include "mozilla/gfx/Rect.h" // for gfxRect
+#include "mozilla/gfx/Point.h" // for IntPoint
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAutoPtr.h" // for nsAutoPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nscore.h" // for nsACString
+
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+/**
+ * A ReadbackSink receives a stream of updates to a rectangle of pixels.
+ * These update callbacks are always called on the main thread, either during
+ * EndTransaction or from the event loop.
+ */
+class ReadbackSink {
+public:
+ ReadbackSink() {}
+ virtual ~ReadbackSink() {}
+
+ /**
+ * Sends an update to indicate that the background is currently unknown.
+ */
+ virtual void SetUnknown(uint64_t aSequenceNumber) = 0;
+ /**
+ * Called by the layer system to indicate that the contents of part of
+ * the readback area are changing.
+ * @param aRect is the rectangle of content that is being updated,
+ * in the coordinate system of the ReadbackLayer.
+ * @param aSequenceNumber updates issued out of order should be ignored.
+ * Only use updates whose sequence counter is greater than all other updates
+ * seen so far. Return null when a non-fresh sequence value is given.
+ * @return a context into which the update should be drawn. This should be
+ * set up to clip to aRect. Zero should never be passed as a sequence number.
+ * If this returns null, EndUpdate should NOT be called. If it returns
+ * non-null, EndUpdate must be called.
+ *
+ * We don't support partially unknown backgrounds. Therefore, the
+ * first BeginUpdate after a SetUnknown will have the complete background.
+ */
+ virtual already_AddRefed<gfx::DrawTarget>
+ BeginUpdate(const gfx::IntRect& aRect, uint64_t aSequenceNumber) = 0;
+ /**
+ * EndUpdate must be called immediately after BeginUpdate, without returning
+ * to the event loop.
+ * @param aContext the context returned by BeginUpdate
+ * Implicitly Restore()s the state of aContext.
+ */
+ virtual void EndUpdate(const gfx::IntRect& aRect) = 0;
+};
+
+/**
+ * A ReadbackLayer never renders anything. It enables clients to extract
+ * the rendered contents of the layer tree below the ReadbackLayer.
+ * The rendered contents are delivered asynchronously via calls to a
+ * ReadbackSink object supplied by the client.
+ *
+ * This is a "best effort" API; it is possible for the layer system to tell
+ * the ReadbackSink that the contents of the readback area are unknown.
+ *
+ * This API exists to work around the limitations of transparent windowless
+ * plugin rendering APIs. It should not be used for anything else.
+ */
+class ReadbackLayer : public Layer {
+public:
+ MOZ_LAYER_DECL_NAME("ReadbackLayer", TYPE_READBACK)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(GetLocalTransform(), gfxRect(0, 0, mSize.width, mSize.height),
+ nullptr)*
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the callback object to which readback updates will be delivered.
+ * This also resets the "needed rectangle" so that on the next layer tree
+ * transaction we will try to deliver the full contents of the readback
+ * area to the sink.
+ * This layer takes ownership of the sink. It will be deleted when the
+ * layer is destroyed or when a new sink is set.
+ * Initially the contents of the readback area are completely unknown.
+ */
+ void SetSink(ReadbackSink* aSink)
+ {
+ SetUnknown();
+ mSink = aSink;
+ }
+ ReadbackSink* GetSink() { return mSink; }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the size of content that should be read back. The readback area
+ * has its top-left at 0,0 and has size aSize.
+ * Can only be called while the sink is null!
+ */
+ void SetSize(const gfx::IntSize& aSize)
+ {
+ NS_ASSERTION(!mSink, "Should have no sink while changing size!");
+ mSize = aSize;
+ }
+ const gfx::IntSize& GetSize() { return mSize; }
+ gfx::IntRect GetRect() { return gfx::IntRect(gfx::IntPoint(0, 0), mSize); }
+
+ bool IsBackgroundKnown()
+ {
+ return mBackgroundLayer || mBackgroundColor.a == 1.f;
+ }
+
+ void NotifyRemoved() {
+ SetUnknown();
+ mSink = nullptr;
+ }
+
+ void NotifyPaintedLayerRemoved(PaintedLayer* aLayer)
+ {
+ if (mBackgroundLayer == aLayer) {
+ mBackgroundLayer = nullptr;
+ }
+ }
+
+ const nsIntPoint& GetBackgroundLayerOffset() { return mBackgroundLayerOffset; }
+
+ uint64_t AllocateSequenceNumber() { return ++mSequenceCounter; }
+
+ void SetUnknown()
+ {
+ if (IsBackgroundKnown()) {
+ if (mSink) {
+ mSink->SetUnknown(AllocateSequenceNumber());
+ }
+ mBackgroundLayer = nullptr;
+ mBackgroundColor = gfx::Color();
+ }
+ }
+
+protected:
+ friend class ReadbackProcessor;
+
+ ReadbackLayer(LayerManager* aManager, void* aImplData) :
+ Layer(aManager, aImplData),
+ mSequenceCounter(0),
+ mSize(0,0),
+ mBackgroundLayer(nullptr),
+ mBackgroundLayerOffset(0, 0),
+ mBackgroundColor(gfx::Color())
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ uint64_t mSequenceCounter;
+ nsAutoPtr<ReadbackSink> mSink;
+ gfx::IntSize mSize;
+
+ // This can refer to any (earlier) sibling PaintedLayer. That PaintedLayer
+ // must have mUsedForReadback set on it. If the PaintedLayer is removed
+ // for the container, this will be set to null by NotifyPaintedLayerRemoved.
+ // This PaintedLayer contains the contents which have previously been reported
+ // to mSink. The PaintedLayer had only an integer translation transform,
+ // and it covered the entire readback area. This layer also had only an
+ // integer translation transform.
+ PaintedLayer* mBackgroundLayer;
+ // When mBackgroundLayer is non-null, this is the offset to add to
+ // convert from the coordinates of mBackgroundLayer to the coordinates
+ // of this layer.
+ nsIntPoint mBackgroundLayerOffset;
+ // When mBackgroundColor is opaque, this is the color of the ColorLayer
+ // that contained the contents we reported to mSink, which covered the
+ // entire readback area.
+ gfx::Color mBackgroundColor;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKLAYER_H */
diff --git a/system/graphics/layers/ReadbackProcessor.cpp b/system/graphics/layers/ReadbackProcessor.cpp
new file mode 100644
index 000000000..9df789635
--- /dev/null
+++ b/system/graphics/layers/ReadbackProcessor.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ReadbackProcessor.h"
+#include <sys/types.h> // for int32_t
+#include "Layers.h" // for Layer, PaintedLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "UnitTransforms.h" // for ViewAs
+#include "Units.h" // for ParentLayerIntRect
+#include "gfxContext.h" // for gfxContext
+#include "gfxUtils.h"
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for Intsize
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+void
+ReadbackProcessor::BuildUpdates(ContainerLayer* aContainer)
+{
+ NS_ASSERTION(mAllUpdates.IsEmpty(), "Some updates not processed?");
+
+ if (!aContainer->mMayHaveReadbackChild)
+ return;
+
+ aContainer->mMayHaveReadbackChild = false;
+ // go backwards so the updates read from earlier layers are later in the
+ // array.
+ for (Layer* l = aContainer->GetLastChild(); l; l = l->GetPrevSibling()) {
+ if (l->GetType() == Layer::TYPE_READBACK) {
+ aContainer->mMayHaveReadbackChild = true;
+ BuildUpdatesForLayer(static_cast<ReadbackLayer*>(l));
+ }
+ }
+}
+
+static Layer*
+FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset)
+{
+ gfx::Matrix transform;
+ if (!aLayer->GetTransform().Is2D(&transform) ||
+ transform.HasNonIntegerTranslation())
+ return nullptr;
+ nsIntPoint transformOffset(int32_t(transform._31), int32_t(transform._32));
+
+ for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) {
+ gfx::Matrix backgroundTransform;
+ if (!l->GetTransform().Is2D(&backgroundTransform) ||
+ gfx::ThebesMatrix(backgroundTransform).HasNonIntegerTranslation())
+ return nullptr;
+
+ nsIntPoint backgroundOffset(int32_t(backgroundTransform._31), int32_t(backgroundTransform._32));
+ IntRect rectInBackground(transformOffset - backgroundOffset, aLayer->GetSize());
+ const nsIntRegion visibleRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ if (!visibleRegion.Intersects(rectInBackground))
+ continue;
+ // Since l is present in the background, from here on we either choose l
+ // or nothing.
+ if (!visibleRegion.Contains(rectInBackground))
+ return nullptr;
+
+ if (l->GetEffectiveOpacity() != 1.0 ||
+ l->HasMaskLayers() ||
+ !(l->GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ return nullptr;
+ }
+
+ // cliprects are post-transform
+ const Maybe<ParentLayerIntRect>& clipRect = l->GetLocalClipRect();
+ if (clipRect && !clipRect->Contains(ViewAs<ParentLayerPixel>(IntRect(transformOffset, aLayer->GetSize()))))
+ return nullptr;
+
+ Layer::LayerType type = l->GetType();
+ if (type != Layer::TYPE_COLOR && type != Layer::TYPE_PAINTED)
+ return nullptr;
+
+ *aOffset = backgroundOffset - transformOffset;
+ return l;
+ }
+
+ return nullptr;
+}
+
+void
+ReadbackProcessor::BuildUpdatesForLayer(ReadbackLayer* aLayer)
+{
+ if (!aLayer->mSink)
+ return;
+
+ nsIntPoint offset;
+ Layer* newBackground = FindBackgroundLayer(aLayer, &offset);
+ if (!newBackground) {
+ aLayer->SetUnknown();
+ return;
+ }
+
+ if (newBackground->GetType() == Layer::TYPE_COLOR) {
+ ColorLayer* colorLayer = static_cast<ColorLayer*>(newBackground);
+ if (aLayer->mBackgroundColor != colorLayer->GetColor()) {
+ aLayer->mBackgroundLayer = nullptr;
+ aLayer->mBackgroundColor = colorLayer->GetColor();
+ NS_ASSERTION(aLayer->mBackgroundColor.a == 1.f,
+ "Color layer said it was opaque!");
+ RefPtr<DrawTarget> dt =
+ aLayer->mSink->BeginUpdate(aLayer->GetRect(),
+ aLayer->AllocateSequenceNumber());
+ if (dt) {
+ ColorPattern color(ToDeviceColor(aLayer->mBackgroundColor));
+ IntSize size = aLayer->GetSize();
+ dt->FillRect(Rect(0, 0, size.width, size.height), color);
+ aLayer->mSink->EndUpdate(aLayer->GetRect());
+ }
+ }
+ } else {
+ NS_ASSERTION(newBackground->AsPaintedLayer(), "Must be PaintedLayer");
+ PaintedLayer* paintedLayer = static_cast<PaintedLayer*>(newBackground);
+ // updateRect is relative to the PaintedLayer
+ IntRect updateRect = aLayer->GetRect() - offset;
+ if (paintedLayer != aLayer->mBackgroundLayer ||
+ offset != aLayer->mBackgroundLayerOffset) {
+ aLayer->mBackgroundLayer = paintedLayer;
+ aLayer->mBackgroundLayerOffset = offset;
+ aLayer->mBackgroundColor = Color();
+ paintedLayer->SetUsedForReadback(true);
+ } else {
+ nsIntRegion invalid;
+ invalid.Sub(updateRect, paintedLayer->GetValidRegion());
+ updateRect = invalid.GetBounds();
+ }
+
+ Update update = { aLayer, updateRect, aLayer->AllocateSequenceNumber() };
+ mAllUpdates.AppendElement(update);
+ }
+}
+
+void
+ReadbackProcessor::GetPaintedLayerUpdates(PaintedLayer* aLayer,
+ nsTArray<Update>* aUpdates,
+ nsIntRegion* aUpdateRegion)
+{
+ // All PaintedLayers used for readback are in mAllUpdates (some possibly
+ // with an empty update rect).
+ aLayer->SetUsedForReadback(false);
+ if (aUpdateRegion) {
+ aUpdateRegion->SetEmpty();
+ }
+ for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
+ const Update& update = mAllUpdates[i - 1];
+ if (update.mLayer->mBackgroundLayer == aLayer) {
+ aLayer->SetUsedForReadback(true);
+ // Don't bother asking for updates if we have an empty update rect.
+ if (!update.mUpdateRect.IsEmpty()) {
+ aUpdates->AppendElement(update);
+ if (aUpdateRegion) {
+ aUpdateRegion->Or(*aUpdateRegion, update.mUpdateRect);
+ }
+ }
+ mAllUpdates.RemoveElementAt(i - 1);
+ }
+ }
+}
+
+ReadbackProcessor::~ReadbackProcessor()
+{
+ for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
+ const Update& update = mAllUpdates[i - 1];
+ // Unprocessed update. Notify the readback sink that this content is
+ // unknown.
+ update.mLayer->SetUnknown();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ReadbackProcessor.h b/system/graphics/layers/ReadbackProcessor.h
new file mode 100644
index 000000000..b07f796c3
--- /dev/null
+++ b/system/graphics/layers/ReadbackProcessor.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKPROCESSOR_H
+#define GFX_READBACKPROCESSOR_H
+
+#include <stdint.h> // for uint64_t
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+
+class ContainerLayer;
+class ReadbackLayer;
+class PaintedLayer;
+
+class ReadbackProcessor {
+public:
+ /**
+ * Called by the container before processing any child layers. Call this
+ * if any child layer might have changed in any way (other than content-only
+ * changes to layers other than ColorLayers and PaintedLayers).
+ *
+ * This method recomputes the relationship between ReadbackLayers and
+ * sibling layers, and dispatches changes to ReadbackLayers. Except that
+ * if a PaintedLayer needs its contents sent to some ReadbackLayer, we'll
+ * just record that internally and later the PaintedLayer should call
+ * GetPaintedLayerUpdates when it paints, to find out which rectangle needs
+ * to be sent, and the ReadbackLayer it needs to be sent to.
+ */
+ void BuildUpdates(ContainerLayer* aContainer);
+
+ struct Update {
+ /**
+ * The layer a PaintedLayer should send its contents to.
+ */
+ ReadbackLayer* mLayer;
+ /**
+ * The rectangle of content that it should send, in the PaintedLayer's
+ * coordinate system. This rectangle is guaranteed to be in the PaintedLayer's
+ * visible region. Translate it to mLayer's coordinate system
+ * by adding mLayer->GetBackgroundLayerOffset().
+ */
+ gfx::IntRect mUpdateRect;
+ /**
+ * The sequence counter value to use when calling DoUpdate
+ */
+ uint64_t mSequenceCounter;
+ };
+ /**
+ * Appends any ReadbackLayers that need to be updated, and the rects that
+ * need to be updated, to aUpdates. Only need to call this for PaintedLayers
+ * that have been marked UsedForReadback().
+ * Each Update's mLayer's mBackgroundLayer will have been set to aLayer.
+ * If a PaintedLayer doesn't call GetPaintedLayerUpdates, then all the
+ * ReadbackLayers that needed data from that PaintedLayer will be marked
+ * as having unknown backgrounds.
+ * @param aUpdateRegion if non-null, this region is set to the union
+ * of the mUpdateRects.
+ */
+ void GetPaintedLayerUpdates(PaintedLayer* aLayer,
+ nsTArray<Update>* aUpdates,
+ nsIntRegion* aUpdateRegion = nullptr);
+
+ ~ReadbackProcessor();
+
+protected:
+ void BuildUpdatesForLayer(ReadbackLayer* aLayer);
+
+ nsTArray<Update> mAllUpdates;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKPROCESSOR_H */
diff --git a/system/graphics/layers/RenderTrace.cpp b/system/graphics/layers/RenderTrace.cpp
new file mode 100644
index 000000000..977c772a7
--- /dev/null
+++ b/system/graphics/layers/RenderTrace.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderTrace.h"
+
+// If rendertrace is off let's no compile this code
+#ifdef MOZ_RENDERTRACE
+#include "Layers.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+
+namespace mozilla {
+namespace layers {
+
+static gfx::Matrix4x4 GetRootTransform(Layer *aLayer) {
+ gfx::Matrix4x4 layerTrans = aLayer->GetTransform();
+ layerTrans.ProjectTo2D();
+ if (aLayer->GetParent() != nullptr) {
+ return GetRootTransform(aLayer->GetParent()) * layerTrans;
+ }
+ return layerTrans;
+}
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform) {
+ int colorId = 0;
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&colorId] (Layer *layer)
+ {
+ gfx::Matrix4x4 trans = aRootTransform * layer->GetTransform();
+ trans.ProjectTo2D();
+ gfx::IntRect clipRect = layer->GetLocalVisibleRegion().GetBounds();
+ Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ trans.TransformBounds(rect);
+
+ if (strcmp(layer->Name(), "ContainerLayer") != 0 &&
+ strcmp(layer->Name(), "ContainerLayerComposite") != 0) {
+ printf_stderr("%s RENDERTRACE %u rect #%02X%s %i %i %i %i\n",
+ layer->Name(), (int)PR_IntervalNow(),
+ colorId, aColor,
+ (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+ }
+ colorId++;
+ });
+}
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect) {
+ gfx::Matrix4x4 trans = GetRootTransform(aLayer);
+ gfx::Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ trans.TransformBounds(rect);
+
+ printf_stderr("%s RENDERTRACE %u fillrect #%s %i %i %i %i\n",
+ aLayer->Name(), (int)PR_IntervalNow(),
+ aColor,
+ (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+}
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor) {
+ // Clear with an empty rect
+ RenderTraceInvalidateStart(aLayer, aColor, gfx::IntRect());
+}
+
+void renderTraceEventStart(const char *aComment, const char *aColor) {
+ printf_stderr("%s RENDERTRACE %u fillrect #%s 0 0 10 10\n",
+ aComment, (int)PR_IntervalNow(), aColor);
+}
+
+void renderTraceEventEnd(const char *aComment, const char *aColor) {
+ printf_stderr("%s RENDERTRACE %u fillrect #%s 0 0 0 0\n",
+ aComment, (int)PR_IntervalNow(), aColor);
+}
+
+void renderTraceEventEnd(const char *aColor) {
+ renderTraceEventEnd("", aColor);
+}
+
+}
+}
+
+#endif
+
diff --git a/system/graphics/layers/RenderTrace.h b/system/graphics/layers/RenderTrace.h
new file mode 100644
index 000000000..d7c28fddf
--- /dev/null
+++ b/system/graphics/layers/RenderTrace.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is a general tool that will let you visualize platform operation.
+// Currently used for the layer system, the general syntax allows this
+// tools to be adapted to trace other operations.
+//
+// For the front end see: https://github.com/staktrace/rendertrace
+
+// Uncomment this line to enable RENDERTRACE
+//#define MOZ_RENDERTRACE
+
+#ifndef GFX_RENDERTRACE_H
+#define GFX_RENDERTRACE_H
+
+#include "nsRect.h"
+#include "mozilla/gfx/Matrix.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform = gfx::Matrix4x4(), bool aReset = true);
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect);
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor);
+
+void renderTraceEventStart(const char *aComment, const char *aColor);
+void renderTraceEventEnd(const char *aComment, const char *aColor);
+void renderTraceEventEnd(const char *aColor);
+
+struct RenderTraceScope {
+public:
+ RenderTraceScope(const char *aComment, const char *aColor)
+ : mComment(aComment)
+ , mColor(aColor)
+ {
+ renderTraceEventStart(mComment, mColor);
+ }
+ ~RenderTraceScope() {
+ renderTraceEventEnd(mComment, mColor);
+ }
+private:
+ const char *mComment;
+ const char *mColor;
+};
+
+#ifndef MOZ_RENDERTRACE
+inline void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform, bool aReset)
+{}
+
+inline void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect)
+{}
+
+inline void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor)
+{}
+
+inline void renderTraceEventStart(const char *aComment, const char *aColor)
+{}
+
+inline void renderTraceEventEnd(const char *aComment, const char *aColor)
+{}
+
+inline void renderTraceEventEnd(const char *aColor)
+{}
+
+#endif // MOZ_RENDERTRACE
+
+} // namespace layers
+} // namespace mozilla
+
+#endif //GFX_RENDERTRACE_H
diff --git a/system/graphics/layers/RotatedBuffer.cpp b/system/graphics/layers/RotatedBuffer.cpp
new file mode 100644
index 000000000..f0114bddf
--- /dev/null
+++ b/system/graphics/layers/RotatedBuffer.cpp
@@ -0,0 +1,786 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RotatedBuffer.h"
+#include <sys/types.h> // for int32_t
+#include <algorithm> // for max
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayersImpl.h" // for ToData
+#include "BufferUnrotate.h" // for BufferUnrotate
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "Layers.h" // for PaintedLayer, Layer, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Point.h" // for Point, IntPoint
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for ExtendMode::ExtendMode::CLAMP, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowableLayer
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "gfx2DGlue.h"
+#include "nsLayoutUtils.h" // for invalidation debugging
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+IntRect
+RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
+{
+ // quadrantTranslation is the amount we translate the top-left
+ // of the quadrant by to get coordinates relative to the layer
+ IntPoint quadrantTranslation = -mBufferRotation;
+ quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
+ quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
+ return mBufferRect + quadrantTranslation;
+}
+
+Rect
+RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
+{
+ Rect result;
+ if (aXSide == LEFT) {
+ result.x = 0;
+ result.width = mBufferRotation.x;
+ } else {
+ result.x = mBufferRotation.x;
+ result.width = mBufferRect.width - mBufferRotation.x;
+ }
+ if (aYSide == TOP) {
+ result.y = 0;
+ result.height = mBufferRotation.y;
+ } else {
+ result.y = mBufferRotation.y;
+ result.height = mBufferRect.height - mBufferRotation.y;
+ }
+ return result;
+}
+
+/**
+ * @param aXSide LEFT means we draw from the left side of the buffer (which
+ * is drawn on the right side of mBufferRect). RIGHT means we draw from
+ * the right side of the buffer (which is drawn on the left side of
+ * mBufferRect).
+ * @param aYSide TOP means we draw from the top side of the buffer (which
+ * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
+ * the bottom side of the buffer (which is drawn on the top side of
+ * mBufferRect).
+ */
+void
+RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
+ XSide aXSide, YSide aYSide,
+ ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const
+{
+ // The rectangle that we're going to fill. Basically we're going to
+ // render the buffer at mBufferRect + quadrantTranslation to get the
+ // pixels in the right place, but we're only going to paint within
+ // mBufferRect
+ IntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
+ IntRect fillRect;
+ if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
+ return;
+
+ gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
+
+ MOZ_ASSERT(aSource != BUFFER_BOTH);
+ RefPtr<SourceSurface> snapshot = GetSourceSurface(aSource);
+
+ if (!snapshot) {
+ gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
+ return;
+ }
+
+ // direct2d is much slower when using OP_SOURCE so use OP_OVER and
+ // (maybe) a clear instead. Normally we need to draw in a single operation
+ // (to avoid flickering) but direct2d is ok since it defers rendering.
+ // We should try abstract this logic in a helper when we have other use
+ // cases.
+ if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
+ aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
+ aOperator == CompositionOp::OP_SOURCE) {
+ aOperator = CompositionOp::OP_OVER;
+ if (snapshot->GetFormat() == SurfaceFormat::B8G8R8A8) {
+ aTarget->ClearRect(IntRectToRect(fillRect));
+ }
+ }
+
+ // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
+ // We also can't do a ClearRect+FillRect since we need the drawing to happen
+ // as an atomic operation (to prevent flickering).
+ // We also need this clip in the case where we have a mask, since the mask surface
+ // might cover more than fillRect, but we only want to touch the pixels inside
+ // fillRect.
+ aTarget->PushClipRect(IntRectToRect(fillRect));
+
+ if (aMask) {
+ Matrix oldTransform = aTarget->GetTransform();
+
+ // Transform from user -> buffer space.
+ Matrix transform =
+ Matrix::Translation(quadrantTranslation.x, quadrantTranslation.y);
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ transform *= oldTransform;
+ transform *= inverseMask;
+
+ SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
+
+ aTarget->SetTransform(*aMaskTransform);
+ aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
+ aTarget->SetTransform(oldTransform);
+ } else {
+ DrawSurfaceOptions options;
+ aTarget->DrawSurface(snapshot, IntRectToRect(fillRect),
+ GetSourceRectangle(aXSide, aYSide),
+ options,
+ DrawOptions(aOpacity, aOperator));
+ }
+
+ aTarget->PopClip();
+}
+
+void
+RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const
+{
+ PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ // See above, in Azure Repeat should always be a safe, even faster choice
+ // though! Particularly on D2D Repeat should be a lot faster, need to look
+ // into that. TODO[Bas]
+ DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
+}
+
+already_AddRefed<SourceSurface>
+SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const
+{
+ RefPtr<SourceSurface> surf;
+ if (aSource == BUFFER_BLACK) {
+ surf = mSource;
+ } else {
+ MOZ_ASSERT(aSource == BUFFER_WHITE);
+ surf = mSourceOnWhite;
+ }
+
+ MOZ_ASSERT(surf);
+ return surf.forget();
+}
+
+/* static */ bool
+RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
+{
+ // Assume clipping is cheap if the draw target just has an integer
+ // translation, and the visible region is simple.
+ return !aTarget->GetTransform().HasNonIntegerTranslation() &&
+ aRegion.GetNumRects() <= 1;
+}
+
+void
+RotatedContentBuffer::DrawTo(PaintedLayer* aLayer,
+ DrawTarget* aTarget,
+ float aOpacity,
+ CompositionOp aOp,
+ SourceSurface* aMask,
+ const Matrix* aMaskTransform)
+{
+ if (!EnsureBuffer()) {
+ return;
+ }
+
+ bool clipped = false;
+
+ // If the entire buffer is valid, we can just draw the whole thing,
+ // no need to clip. But we'll still clip if clipping is cheap ---
+ // that might let us copy a smaller region of the buffer.
+ // Also clip to the visible region if we're told to.
+ if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
+ (ToData(aLayer)->GetClipToVisibleRegion() &&
+ !aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect())) ||
+ IsClippingCheap(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion())) {
+ // We don't want to draw invalid stuff, so we need to clip. Might as
+ // well clip to the smallest area possible --- the visible region.
+ // Bug 599189 if there is a non-integer-translation transform in aTarget,
+ // we might sample pixels outside GetLocalVisibleRegion(), which is wrong
+ // and may cause gray lines.
+ gfxUtils::ClipToRegion(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ clipped = true;
+ }
+
+ DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
+ if (clipped) {
+ aTarget->PopClip();
+ }
+}
+
+DrawTarget*
+RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
+ ContextSource aSource,
+ DrawIterator* aIter)
+{
+ IntRect bounds = aBounds;
+ if (aIter) {
+ // If an iterator was provided, then BeginPaint must have been run with
+ // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
+ // Iterate over each of them, and return an appropriate buffer each time we find
+ // one that intersects the draw region. The iterator mCount value tracks which
+ // quadrants we have considered across multiple calls to this function.
+ aIter->mDrawRegion.SetEmpty();
+ while (aIter->mCount < 4) {
+ IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
+ (aIter->mCount & 2) ? TOP : BOTTOM);
+ aIter->mDrawRegion.And(aBounds, quadrant);
+ aIter->mCount++;
+ if (!aIter->mDrawRegion.IsEmpty()) {
+ break;
+ }
+ }
+ if (aIter->mDrawRegion.IsEmpty()) {
+ return nullptr;
+ }
+ bounds = aIter->mDrawRegion.GetBounds();
+ }
+
+ if (!EnsureBuffer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
+ if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
+ if (!EnsureBufferOnWhite()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid() && mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
+ } else if (aSource == BUFFER_WHITE) {
+ if (!EnsureBufferOnWhite()) {
+ return nullptr;
+ }
+ mLoanedDrawTarget = mDTBufferOnWhite;
+ } else {
+ // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
+ mLoanedDrawTarget = mDTBuffer;
+ }
+
+ // Figure out which quadrant to draw in
+ int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
+ int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
+ XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
+ YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
+ IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
+ NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
+
+ mLoanedTransform = mLoanedDrawTarget->GetTransform();
+ mLoanedDrawTarget->SetTransform(Matrix(mLoanedTransform).
+ PreTranslate(-quadrantRect.x,
+ -quadrantRect.y));
+
+ return mLoanedDrawTarget;
+}
+
+void
+BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
+{
+ MOZ_ASSERT(mLoanedDrawTarget);
+ MOZ_ASSERT(aReturned == mLoanedDrawTarget);
+ if (mLoanedDrawTarget) {
+ mLoanedDrawTarget->SetTransform(mLoanedTransform);
+ mLoanedDrawTarget = nullptr;
+ }
+ aReturned = nullptr;
+}
+
+gfxContentType
+RotatedContentBuffer::BufferContentType()
+{
+ if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
+ SurfaceFormat format = SurfaceFormat::B8G8R8A8;
+
+ if (mBufferProvider) {
+ format = mBufferProvider->GetFormat();
+ } else if (mDTBuffer && mDTBuffer->IsValid()) {
+ format = mDTBuffer->GetFormat();
+ }
+
+ return ContentForFormat(format);
+ }
+ return gfxContentType::SENTINEL;
+}
+
+bool
+RotatedContentBuffer::BufferSizeOkFor(const IntSize& aSize)
+{
+ return (aSize == mBufferRect.Size() ||
+ (SizedToVisibleBounds != mBufferSizePolicy &&
+ aSize < mBufferRect.Size()));
+}
+
+bool
+RotatedContentBuffer::EnsureBuffer()
+{
+ NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
+ if (!mDTBuffer || !mDTBuffer->IsValid()) {
+ if (mBufferProvider) {
+ mDTBuffer = mBufferProvider->BorrowDrawTarget();
+ }
+ }
+
+ NS_WARNING_ASSERTION(mDTBuffer && mDTBuffer->IsValid(), "no buffer");
+ return !!mDTBuffer;
+}
+
+bool
+RotatedContentBuffer::EnsureBufferOnWhite()
+{
+ NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
+ if (!mDTBufferOnWhite) {
+ if (mBufferProviderOnWhite) {
+ mDTBufferOnWhite =
+ mBufferProviderOnWhite->BorrowDrawTarget();
+ }
+ }
+
+ NS_WARNING_ASSERTION(mDTBufferOnWhite, "no buffer");
+ return !!mDTBufferOnWhite;
+}
+
+bool
+RotatedContentBuffer::HaveBuffer() const
+{
+ return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid());
+}
+
+bool
+RotatedContentBuffer::HaveBufferOnWhite() const
+{
+ return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+}
+
+static void
+WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
+{
+ if (*aRotationPoint < 0) {
+ *aRotationPoint += aSize;
+ } else if (*aRotationPoint >= aSize) {
+ *aRotationPoint -= aSize;
+ }
+}
+
+static IntRect
+ComputeBufferRect(const IntRect& aRequestedRect)
+{
+ IntRect rect(aRequestedRect);
+ // Set a minimum width to guarantee a minimum size of buffers we
+ // allocate (and work around problems on some platforms with smaller
+ // dimensions). 64 is the magic number needed to work around the
+ // rendering glitch, and guarantees image rows can be SIMD'd for
+ // even r5g6b5 surfaces pretty much everywhere.
+ rect.width = std::max(aRequestedRect.width, 64);
+ return rect;
+}
+
+void
+RotatedContentBuffer::FlushBuffers()
+{
+ if (mDTBuffer) {
+ mDTBuffer->Flush();
+ }
+ if (mDTBufferOnWhite) {
+ mDTBufferOnWhite->Flush();
+ }
+}
+
+RotatedContentBuffer::PaintState
+RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
+ uint32_t aFlags)
+{
+ PaintState result;
+ // We need to disable rotation if we're going to be resampled when
+ // drawing, because we might sample across the rotation boundary.
+ bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
+ !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION));
+
+ nsIntRegion validRegion = aLayer->GetValidRegion();
+
+ bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
+ ContentType layerContentType =
+ canUseOpaqueSurface ? gfxContentType::COLOR :
+ gfxContentType::COLOR_ALPHA;
+
+ SurfaceMode mode;
+ nsIntRegion neededRegion;
+ IntRect destBufferRect;
+
+ bool canReuseBuffer = HaveBuffer();
+
+ while (true) {
+ mode = aLayer->GetSurfaceMode();
+ neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
+ canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
+ result.mContentType = layerContentType;
+
+ if (canReuseBuffer) {
+ if (mBufferRect.Contains(neededRegion.GetBounds())) {
+ // We don't need to adjust mBufferRect.
+ destBufferRect = mBufferRect;
+ } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
+ // The buffer's big enough but doesn't contain everything that's
+ // going to be visible. We'll move it.
+ destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
+ } else {
+ destBufferRect = neededRegion.GetBounds();
+ }
+ } else {
+ // We won't be reusing the buffer. Compute a new rect.
+ destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+ }
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!aLayer->GetParent() ||
+ !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
+ !aLayer->AsShadowableLayer() ||
+ !aLayer->AsShadowableLayer()->HasShadow()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ result.mContentType = gfxContentType::COLOR;
+ }
+ }
+
+ if ((aFlags & PAINT_WILL_RESAMPLE) &&
+ (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
+ neededRegion.GetNumRects() > 1))
+ {
+ // The area we add to neededRegion might not be painted opaquely.
+ if (mode == SurfaceMode::SURFACE_OPAQUE) {
+ result.mContentType = gfxContentType::COLOR_ALPHA;
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
+
+ // We need to validate the entire buffer, to make sure that only valid
+ // pixels are sampled.
+ neededRegion = destBufferRect;
+ }
+
+ // If we have an existing buffer, but the content type has changed or we
+ // have transitioned into/out of component alpha, then we need to recreate it.
+ if (canReuseBuffer &&
+ (result.mContentType != BufferContentType() ||
+ (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
+ {
+ // Restart the decision process; we won't re-enter since we guard on
+ // being able to re-use the buffer.
+ canReuseBuffer = false;
+ continue;
+ }
+
+ break;
+ }
+
+ if (HaveBuffer() &&
+ (result.mContentType != BufferContentType() ||
+ (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
+ {
+ // We're effectively clearing the valid region, so we need to draw
+ // the entire needed region now.
+ canReuseBuffer = false;
+ result.mRegionToInvalidate = aLayer->GetValidRegion();
+ validRegion.SetEmpty();
+ Clear();
+
+#if defined(MOZ_DUMP_PAINTING)
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ if (result.mContentType != BufferContentType()) {
+ printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
+ } else if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) {
+ printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
+ }
+ }
+#endif
+ }
+
+ NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
+ "Destination rect doesn't contain what we need to paint");
+
+ result.mRegionToDraw.Sub(neededRegion, validRegion);
+
+ if (result.mRegionToDraw.IsEmpty())
+ return result;
+
+ if (HaveBuffer()) {
+ // Do not modify result.mRegionToDraw or result.mContentType after this call.
+ // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
+ // or call CreateBuffer before this call.
+ FinalizeFrame(result.mRegionToDraw);
+ }
+
+ IntRect drawBounds = result.mRegionToDraw.GetBounds();
+ RefPtr<DrawTarget> destDTBuffer;
+ RefPtr<DrawTarget> destDTBufferOnWhite;
+ uint32_t bufferFlags = 0;
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ bufferFlags |= BUFFER_COMPONENT_ALPHA;
+ }
+ if (canReuseBuffer) {
+ if (!EnsureBuffer()) {
+ return result;
+ }
+ IntRect keepArea;
+ if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
+ // Set mBufferRotation so that the pixels currently in mDTBuffer
+ // will still be rendered in the right place when mBufferRect
+ // changes to destBufferRect.
+ IntPoint newRotation = mBufferRotation +
+ (destBufferRect.TopLeft() - mBufferRect.TopLeft());
+ WrapRotationAxis(&newRotation.x, mBufferRect.width);
+ WrapRotationAxis(&newRotation.y, mBufferRect.height);
+ NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
+ "newRotation out of bounds");
+ int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
+ int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
+ bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
+ (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
+ if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
+ (newRotation != IntPoint(0,0) && !canHaveRotation)) {
+ // The stuff we need to redraw will wrap around an edge of the
+ // buffer (and the caller doesn't know how to support that), so
+ // move the pixels we can keep into a position that lets us
+ // redraw in just one quadrant.
+ if (mBufferRotation == IntPoint(0,0)) {
+ IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
+ IntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid());
+ mDTBuffer->CopyRect(srcRect, dest);
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mDTBufferOnWhite->CopyRect(srcRect, dest);
+ }
+ result.mDidSelfCopy = true;
+ mDidSelfCopy = true;
+ // Don't set destBuffer; we special-case self-copies, and
+ // just did the necessary work above.
+ mBufferRect = destBufferRect;
+ } else {
+ // With azure and a data surface perform an buffer unrotate
+ // (SelfCopy).
+ unsigned char* data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+
+ if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
+ uint8_t bytesPerPixel = BytesPerPixel(format);
+ BufferUnrotate(data,
+ size.width * bytesPerPixel,
+ size.height, stride,
+ newRotation.x * bytesPerPixel, newRotation.y);
+ mDTBuffer->ReleaseBits(data);
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
+ uint8_t bytesPerPixel = BytesPerPixel(format);
+ BufferUnrotate(data,
+ size.width * bytesPerPixel,
+ size.height, stride,
+ newRotation.x * bytesPerPixel, newRotation.y);
+ mDTBufferOnWhite->ReleaseBits(data);
+ }
+
+ // Buffer unrotate moves all the pixels, note that
+ // we self copied for SyncBackToFrontBuffer
+ result.mDidSelfCopy = true;
+ mDidSelfCopy = true;
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0, 0);
+ }
+
+ if (!result.mDidSelfCopy) {
+ destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+ CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
+ &destDTBuffer, &destDTBufferOnWhite);
+ if (!destDTBuffer ||
+ (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
+ if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+ gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+ }
+ return result;
+ }
+ }
+ }
+ } else {
+ mBufferRect = destBufferRect;
+ mBufferRotation = newRotation;
+ }
+ } else {
+ // No pixels are going to be kept. The whole visible region
+ // will be redrawn, so we don't need to copy anything, so we don't
+ // set destBuffer.
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0,0);
+ }
+ } else {
+ // The buffer's not big enough, so allocate a new one
+ CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
+ &destDTBuffer, &destDTBufferOnWhite);
+ if (!destDTBuffer ||
+ (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
+ if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+ gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+ }
+ return result;
+ }
+ }
+
+ NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
+ "If we're resampling, we need to validate the entire buffer");
+
+ // If we have no buffered data already, then destBuffer will be a fresh buffer
+ // and we do not need to clear it below.
+ bool isClear = !HaveBuffer();
+
+ if (destDTBuffer) {
+ if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
+ // Copy the bits
+ IntPoint offset = -destBufferRect.TopLeft();
+ Matrix mat = Matrix::Translation(offset.x, offset.y);
+ destDTBuffer->SetTransform(mat);
+ if (!EnsureBuffer()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid(), "Have we got a Thebes buffer for some reason?");
+ DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+ destDTBuffer->SetTransform(Matrix());
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!destDTBufferOnWhite || !EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid(), "Have we got a Thebes buffer for some reason?");
+ destDTBufferOnWhite->SetTransform(mat);
+ DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+ destDTBufferOnWhite->SetTransform(Matrix());
+ }
+ }
+
+ mDTBuffer = destDTBuffer.forget();
+ mDTBufferOnWhite = destDTBufferOnWhite.forget();
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0,0);
+ }
+ NS_ASSERTION(canHaveRotation || mBufferRotation == IntPoint(0,0),
+ "Rotation disabled, but we have nonzero rotation?");
+
+ nsIntRegion invalidate;
+ invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
+ result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
+ result.mClip = DrawRegionClip::DRAW;
+ result.mMode = mode;
+
+ return result;
+}
+
+DrawTarget*
+RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
+ DrawIterator* aIter /* = nullptr */)
+{
+ if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
+ return nullptr;
+ }
+
+ DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
+ BUFFER_BOTH, aIter);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
+ if (aIter) {
+ // The iterators draw region currently only contains the bounds of the region,
+ // this makes it the precise region.
+ aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
+ drawPtr = &aIter->mDrawRegion;
+ }
+ if (result->GetBackendType() == BackendType::DIRECT2D ||
+ result->GetBackendType() == BackendType::DIRECT2D1_1) {
+ // Simplify the draw region to avoid hitting expensive drawing paths
+ // for complex regions.
+ drawPtr->SimplifyOutwardByArea(100 * 100);
+ }
+
+ if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!mDTBuffer || !mDTBuffer->IsValid() ||
+ !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
+ // This can happen in release builds if allocating one of the two buffers
+ // failed. This in turn can happen if unreasonably large textures are
+ // requested.
+ return nullptr;
+ }
+ for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+ ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+ mDTBufferOnWhite->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+ ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ }
+ } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
+ // HaveBuffer() => we have an existing buffer that we must clear
+ for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ result->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
+ }
+ }
+
+ return result;
+}
+
+already_AddRefed<SourceSurface>
+RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
+{
+ if (!mDTBuffer || !mDTBuffer->IsValid()) {
+ gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer);
+ return nullptr;
+ }
+
+ if (aSource == BUFFER_BLACK) {
+ return mDTBuffer->Snapshot();
+ } else {
+ if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
+ gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite);
+ return nullptr;
+ }
+ MOZ_ASSERT(aSource == BUFFER_WHITE);
+ return mDTBufferOnWhite->Snapshot();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/system/graphics/layers/RotatedBuffer.h b/system/graphics/layers/RotatedBuffer.h
new file mode 100644
index 000000000..3b7e18123
--- /dev/null
+++ b/system/graphics/layers/RotatedBuffer.h
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ROTATEDBUFFER_H_
+#define ROTATEDBUFFER_H_
+
+#include "gfxTypes.h"
+#include <stdint.h> // for uint32_t
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h" // for DrawTarget, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace gfx {
+class Matrix;
+} // namespace gfx
+
+namespace layers {
+
+class TextureClient;
+class PaintedLayer;
+
+/**
+ * This is a cairo/Thebes surface, but with a literal twist. Scrolling
+ * causes the layer's visible region to move. We want to keep
+ * reusing the same surface if the region size hasn't changed, but we don't
+ * want to keep moving the contents of the surface around in memory. So
+ * we use a trick.
+ * Consider just the vertical case, and suppose the buffer is H pixels
+ * high and we're scrolling down by N pixels. Instead of copying the
+ * buffer contents up by N pixels, we leave the buffer contents in place,
+ * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
+ * Then we can refresh the screen by painting rows N to H-1 of the buffer
+ * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
+ * at row H-N on the screen.
+ * mBufferRotation.y would be N in this example.
+ */
+class RotatedBuffer {
+public:
+ typedef gfxContentType ContentType;
+
+ RotatedBuffer(const gfx::IntRect& aBufferRect,
+ const gfx::IntPoint& aBufferRotation)
+ : mBufferRect(aBufferRect)
+ , mBufferRotation(aBufferRotation)
+ , mDidSelfCopy(false)
+ { }
+ RotatedBuffer()
+ : mDidSelfCopy(false)
+ { }
+
+ /*
+ * Which buffer should be drawn to/read from.
+ */
+ enum ContextSource {
+ BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha.
+ BUFFER_WHITE, // The buffer with white background, only valid with component alpha.
+ BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading.
+ };
+ // It is the callers repsonsibility to ensure aTarget is flushed after calling
+ // this method.
+ void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource,
+ float aOpacity = 1.0,
+ gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
+ gfx::SourceSurface* aMask = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr) const;
+
+ /**
+ * |BufferRect()| is the rect of device pixels that this
+ * RotatedBuffer covers. That is what DrawBufferWithRotation()
+ * will paint when it's called.
+ */
+ const gfx::IntRect& BufferRect() const { return mBufferRect; }
+ const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
+
+ virtual bool HaveBuffer() const = 0;
+ virtual bool HaveBufferOnWhite() const = 0;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
+
+protected:
+
+ enum XSide {
+ LEFT, RIGHT
+ };
+ enum YSide {
+ TOP, BOTTOM
+ };
+ gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
+
+ gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const;
+
+ /*
+ * If aMask is non-null, then it is used as an alpha mask for rendering this
+ * buffer. aMaskTransform must be non-null if aMask is non-null, and is used
+ * to adjust the coordinate space of the mask.
+ */
+ void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
+ ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const;
+
+ /** The area of the PaintedLayer that is covered by the buffer as a whole */
+ gfx::IntRect mBufferRect;
+ /**
+ * The x and y rotation of the buffer. Conceptually the buffer
+ * has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
+ * is tiled to fill the plane, and the result is clipped to mBufferRect.
+ * So the pixel at mBufferRotation within the buffer is what gets painted at
+ * mBufferRect.TopLeft().
+ * This is "rotation" in the sense of rotating items in a linear buffer,
+ * where items falling off the end of the buffer are returned to the
+ * buffer at the other end, not 2D rotation!
+ */
+ gfx::IntPoint mBufferRotation;
+ // When this is true it means that all pixels have moved inside the buffer.
+ // It's not possible to sync with another buffer without a full copy.
+ bool mDidSelfCopy;
+};
+
+class SourceRotatedBuffer : public RotatedBuffer
+{
+public:
+ SourceRotatedBuffer(gfx::SourceSurface* aSource, gfx::SourceSurface* aSourceOnWhite,
+ const gfx::IntRect& aBufferRect,
+ const gfx::IntPoint& aBufferRotation)
+ : RotatedBuffer(aBufferRect, aBufferRotation)
+ , mSource(aSource)
+ , mSourceOnWhite(aSourceOnWhite)
+ { }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
+
+ virtual bool HaveBuffer() const { return !!mSource; }
+ virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; }
+
+private:
+ RefPtr<gfx::SourceSurface> mSource;
+ RefPtr<gfx::SourceSurface> mSourceOnWhite;
+};
+
+// Mixin class for classes which need logic for loaning out a draw target.
+// See comments on BorrowDrawTargetForQuadrantUpdate.
+class BorrowDrawTarget
+{
+protected:
+ void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
+
+ // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not
+ // be used, we just keep a reference to ensure it is kept alive and so we can
+ // correctly restore state when it is returned.
+ RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
+ gfx::Matrix mLoanedTransform;
+};
+
+/**
+ * This class encapsulates the buffer used to retain PaintedLayer contents,
+ * i.e., the contents of the layer's GetVisibleRegion().
+ */
+class RotatedContentBuffer : public RotatedBuffer
+ , public BorrowDrawTarget
+{
+public:
+ typedef gfxContentType ContentType;
+
+ /**
+ * Controls the size of the backing buffer of this.
+ * - SizedToVisibleBounds: the backing buffer is exactly the same
+ * size as the bounds of PaintedLayer's visible region
+ * - ContainsVisibleBounds: the backing buffer is large enough to
+ * fit visible bounds. May be larger.
+ */
+ enum BufferSizePolicy {
+ SizedToVisibleBounds,
+ ContainsVisibleBounds
+ };
+
+ explicit RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy)
+ : mBufferProvider(nullptr)
+ , mBufferProviderOnWhite(nullptr)
+ , mBufferSizePolicy(aBufferSizePolicy)
+ {
+ MOZ_COUNT_CTOR(RotatedContentBuffer);
+ }
+ virtual ~RotatedContentBuffer()
+ {
+ MOZ_COUNT_DTOR(RotatedContentBuffer);
+ }
+
+ /**
+ * Wipe out all retained contents. Call this when the entire
+ * buffer becomes invalid.
+ */
+ void Clear()
+ {
+ mDTBuffer = nullptr;
+ mDTBufferOnWhite = nullptr;
+ mBufferProvider = nullptr;
+ mBufferProviderOnWhite = nullptr;
+ mBufferRect.SetEmpty();
+ }
+
+ /**
+ * This is returned by BeginPaint. The caller should draw into mTarget.
+ * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
+ * by RotatedContentBuffer and must be redrawn on the screen.
+ * mRegionToInvalidate is set when the buffer has changed from
+ * opaque to transparent or vice versa, since the details of rendering can
+ * depend on the buffer type. mDidSelfCopy is true if we kept our buffer
+ * but used MovePixels() to shift its content.
+ */
+ struct PaintState {
+ PaintState()
+ : mRegionToDraw()
+ , mRegionToInvalidate()
+ , mMode(SurfaceMode::SURFACE_NONE)
+ , mClip(DrawRegionClip::NONE)
+ , mContentType(gfxContentType::SENTINEL)
+ , mDidSelfCopy(false)
+ {}
+
+ nsIntRegion mRegionToDraw;
+ nsIntRegion mRegionToInvalidate;
+ SurfaceMode mMode;
+ DrawRegionClip mClip;
+ ContentType mContentType;
+ bool mDidSelfCopy;
+ };
+
+ enum {
+ PAINT_WILL_RESAMPLE = 0x01,
+ PAINT_NO_ROTATION = 0x02,
+ PAINT_CAN_DRAW_ROTATED = 0x04
+ };
+ /**
+ * Start a drawing operation. This returns a PaintState describing what
+ * needs to be drawn to bring the buffer up to date in the visible region.
+ * This queries aLayer to get the currently valid and visible regions.
+ * The returned mTarget may be null if mRegionToDraw is empty.
+ * Otherwise it must not be null.
+ * mRegionToInvalidate will contain mRegionToDraw.
+ * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
+ * buffer will be resampled when rendering (i.e the effective transform
+ * combined with the scale for the resolution is not just an integer
+ * translation). This will disable buffer rotation (since we don't want
+ * to resample across the rotation boundary) and will ensure that we
+ * make the entire buffer contents valid (since we don't want to sample
+ * invalid pixels outside the visible region, if the visible region doesn't
+ * fill the buffer bounds).
+ * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
+ * rotated content that crosses the physical buffer boundary. The caller
+ * will need to call BorrowDrawTargetForPainting multiple times to achieve
+ * this.
+ */
+ PaintState BeginPaint(PaintedLayer* aLayer,
+ uint32_t aFlags);
+
+ struct DrawIterator {
+ friend class RotatedContentBuffer;
+ DrawIterator()
+ : mCount(0)
+ {}
+
+ nsIntRegion mDrawRegion;
+
+ private:
+ uint32_t mCount;
+ };
+
+ /**
+ * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
+ * this. See notes on BorrowDrawTargetForQuadrantUpdate.
+ * May return null. If the return value is non-null, it must be
+ * 'un-borrowed' using ReturnDrawTarget.
+ *
+ * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
+ * must call this function repeatedly (with an iterator) until it returns
+ * nullptr. The caller should draw the mDrawRegion of the iterator instead
+ * of mRegionToDraw in the PaintState.
+ *
+ * @param aPaintState Paint state data returned by a call to BeginPaint
+ * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
+ * was specified to BeginPaint.
+ */
+ gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ DrawIterator* aIter = nullptr);
+
+ enum {
+ BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
+ // component alpha.
+ };
+ /**
+ * Return a new surface of |aSize| and |aType|.
+ *
+ * If the created buffer supports azure content, then the result(s) will
+ * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface
+ * will be used.
+ */
+ virtual void
+ CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) = 0;
+
+ /**
+ * Get the underlying buffer, if any. This is useful because we can pass
+ * in the buffer as the default "reference surface" if there is one.
+ * Don't use it for anything else!
+ */
+ gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; }
+ gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
+
+ /**
+ * Complete the drawing operation. The region to draw must have been
+ * drawn before this is called. The contents of the buffer are drawn
+ * to aTarget.
+ */
+ void DrawTo(PaintedLayer* aLayer,
+ gfx::DrawTarget* aTarget,
+ float aOpacity,
+ gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform);
+
+protected:
+ // new texture client versions
+ void SetBufferProvider(TextureClient* aClient)
+ {
+ // Only this buffer provider can give us a buffer. If we
+ // already have one, something has gone wrong.
+ MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid());
+
+ mBufferProvider = aClient;
+ if (!mBufferProvider) {
+ mDTBuffer = nullptr;
+ }
+ }
+
+ void SetBufferProviderOnWhite(TextureClient* aClient)
+ {
+ // Only this buffer provider can give us a buffer. If we
+ // already have one, something has gone wrong.
+ MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
+
+ mBufferProviderOnWhite = aClient;
+ if (!mBufferProviderOnWhite) {
+ mDTBufferOnWhite = nullptr;
+ }
+ }
+
+ /**
+ * Get a draw target at the specified resolution for updating |aBounds|,
+ * which must be contained within a single quadrant.
+ *
+ * The result should only be held temporarily by the caller (it will be kept
+ * alive by this). Once used it should be returned using ReturnDrawTarget.
+ * BorrowDrawTargetForQuadrantUpdate may not be called more than once without
+ * first calling ReturnDrawTarget.
+ *
+ * ReturnDrawTarget will restore the transform on the draw target. But it is
+ * the callers responsibility to restore the clip. The caller should flush the
+ * draw target, if necessary.
+ */
+ gfx::DrawTarget*
+ BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
+ ContextSource aSource,
+ DrawIterator* aIter);
+
+ static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
+
+protected:
+ /**
+ * Return the buffer's content type. Requires a valid buffer or
+ * buffer provider.
+ */
+ gfxContentType BufferContentType();
+ bool BufferSizeOkFor(const gfx::IntSize& aSize);
+ /**
+ * If the buffer hasn't been mapped, map it.
+ */
+ bool EnsureBuffer();
+ bool EnsureBufferOnWhite();
+
+ // Flush our buffers if they are mapped.
+ void FlushBuffers();
+
+ /**
+ * True if we have a buffer where we can get it (but not necessarily
+ * mapped currently).
+ */
+ virtual bool HaveBuffer() const;
+ virtual bool HaveBufferOnWhite() const;
+
+ /**
+ * Any actions that should be performed at the last moment before we begin
+ * rendering the next frame. I.e., after we calculate what we will draw,
+ * but before we rotate the buffer and possibly create new buffers.
+ * aRegionToDraw is the region which is guaranteed to be overwritten when
+ * drawing the next frame.
+ */
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
+
+ RefPtr<gfx::DrawTarget> mDTBuffer;
+ RefPtr<gfx::DrawTarget> mDTBufferOnWhite;
+
+ /**
+ * These members are only set transiently. They're used to map mDTBuffer
+ * when we're using surfaces that require explicit map/unmap. Only one
+ * may be used at a time.
+ */
+ TextureClient* mBufferProvider;
+ TextureClient* mBufferProviderOnWhite;
+
+ BufferSizePolicy mBufferSizePolicy;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* ROTATEDBUFFER_H_ */
diff --git a/system/graphics/layers/TextureDIB.cpp b/system/graphics/layers/TextureDIB.cpp
new file mode 100644
index 000000000..775538cf3
--- /dev/null
+++ b/system/graphics/layers/TextureDIB.cpp
@@ -0,0 +1,507 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureDIB.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class MemoryDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ mSurface = nullptr;
+ }
+
+ MemoryDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ {
+ MOZ_COUNT_CTOR(MemoryDIBTextureData);
+ }
+
+ virtual ~MemoryDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(MemoryDIBTextureData);
+ }
+};
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class ShmemDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+ void DeallocateData()
+ {
+ if (mSurface) {
+ ::DeleteObject(mBitmap);
+ ::DeleteDC(mDC);
+ ::CloseHandle(mFileMapping);
+ mBitmap = NULL;
+ mDC = NULL;
+ mFileMapping = NULL;
+ mSurface = nullptr;
+ }
+ }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ DeallocateData();
+ }
+
+ ShmemDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface,
+ HANDLE aFileMapping, HANDLE aHostHandle,
+ HDC aDC, HBITMAP aBitmap)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ , mFileMapping(aFileMapping)
+ , mHostHandle(aHostHandle)
+ , mDC(aDC)
+ , mBitmap(aBitmap)
+ {
+ MOZ_COUNT_CTOR(ShmemDIBTextureData);
+ }
+
+ virtual ~ShmemDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(ShmemDIBTextureData);
+
+ // The host side has its own references and handles to this data, we can
+ // safely clear ours.
+ DeallocateData();
+ }
+
+ HANDLE mFileMapping;
+ HANDLE mHostHandle;
+ HDC mDC;
+ HBITMAP mBitmap;
+};
+
+void
+DIBTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasIntermediateBuffer = true;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<gfx::DrawTarget>
+DIBTextureData::BorrowDrawTarget()
+{
+ return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
+}
+
+DIBTextureData*
+DIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return nullptr;
+ }
+ if (aAllocator->IsSameProcess()) {
+ return MemoryDIBTextureData::Create(aSize, aFormat);
+ } else {
+ return ShmemDIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+}
+
+TextureData*
+MemoryDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return MemoryDIBTextureData::Create(mSize, mFormat);
+}
+
+bool
+MemoryDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ // The host will release this ref when it receives the surface descriptor.
+ // We AddRef in case we die before the host receives the pointer.
+ aOutDescriptor = SurfaceDescriptorDIB(reinterpret_cast<uintptr_t>(mSurface.get()));
+ mSurface->AddRef();
+ return true;
+}
+
+DIBTextureData*
+MemoryDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+{
+ RefPtr<gfxWindowsSurface> surface
+ = new gfxWindowsSurface(aSize, SurfaceFormatToImageFormat(aFormat));
+ if (!surface || surface->CairoStatus()) {
+ NS_WARNING("Could not create DIB surface");
+ return nullptr;
+ }
+
+ return new MemoryDIBTextureData(aSize, aFormat, surface);
+}
+
+bool
+MemoryDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DIB).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(imgSurf->Data() + imgSurf->Stride() * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ return true;
+}
+
+TextureData*
+ShmemDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return ShmemDIBTextureData::Create(mSize, mFormat, aAllocator);
+}
+
+bool
+ShmemDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DTD).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ GdiFlush();
+
+ uint32_t stride = mSize.width * BytesPerPixel(mFormat);
+ uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_WRITE, 0, 0, stride * mSize.height);
+
+ if (!data) {
+ gfxCriticalError() << "Failed to map view of file for UpdateFromSurface.";
+ srcSurf->Unmap();
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(data + stride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ ::UnmapViewOfFile(data);
+
+ srcSurf->Unmap();
+ return true;
+}
+
+bool
+ShmemDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ ::GdiFlush();
+ aOutDescriptor = SurfaceDescriptorFileMapping((WindowsHandle)mHostHandle, mFormat, mSize);
+ return true;
+}
+
+DIBTextureData*
+ShmemDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator->GetParentPid() != base::ProcessId());
+
+ DWORD mapSize = aSize.width * aSize.height * BytesPerPixel(aFormat);
+ HANDLE fileMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, mapSize, NULL);
+
+ if (!fileMapping) {
+ gfxCriticalError() << "Failed to create memory file mapping for " << mapSize << " bytes.";
+ return nullptr;
+ }
+
+ BITMAPV4HEADER header;
+ memset(&header, 0, sizeof(BITMAPV4HEADER));
+ header.bV4Size = sizeof(BITMAPV4HEADER);
+ header.bV4Width = aSize.width;
+ header.bV4Height = -LONG(aSize.height); // top-to-buttom DIB
+ header.bV4Planes = 1;
+ header.bV4BitCount = 32;
+ header.bV4V4Compression = BI_BITFIELDS;
+ header.bV4RedMask = 0x00FF0000;
+ header.bV4GreenMask = 0x0000FF00;
+ header.bV4BlueMask = 0x000000FF;
+
+ HDC nulldc = ::GetDC(NULL);
+
+ HDC dc = ::CreateCompatibleDC(nulldc);
+
+ ::ReleaseDC(nullptr, nulldc);
+
+ if (!dc) {
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Failed to create DC for bitmap.";
+ return nullptr;
+ }
+
+ void* bits;
+ HBITMAP bitmap = ::CreateDIBSection(dc, (BITMAPINFO*)&header,
+ DIB_RGB_COLORS, &bits,
+ fileMapping, 0);
+
+ if (!bitmap) {
+ gfxCriticalError() << "Failed to create DIB section for a bitmap of size "
+ << aSize << " and mapSize " << mapSize;
+ ::CloseHandle(fileMapping);
+ ::DeleteDC(dc);
+ return nullptr;
+ }
+
+ ::SelectObject(dc, bitmap);
+
+ RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(dc, 0);
+ if (surface->CairoStatus())
+ {
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Could not create surface, status: "
+ << surface->CairoStatus();
+ return nullptr;
+ }
+
+ HANDLE hostHandle = NULL;
+
+ if (!ipc::DuplicateHandle(fileMapping, aAllocator->GetParentPid(),
+ &hostHandle, 0, DUPLICATE_SAME_ACCESS)) {
+ gfxCriticalError() << "Failed to duplicate handle to parent process for surface.";
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ return nullptr;
+ }
+
+ return new ShmemDIBTextureData(aSize, aFormat, surface,
+ fileMapping, hostHandle,
+ dc, bitmap);
+}
+
+
+bool
+TextureHostDirectUpload::Lock()
+{
+ MOZ_ASSERT(!mIsLocked);
+ mIsLocked = true;
+ return true;
+}
+
+void
+TextureHostDirectUpload::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+void
+TextureHostDirectUpload::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+}
+
+void
+TextureHostDirectUpload::DeallocateDeviceData()
+{
+ if (mTextureSource) {
+ mTextureSource->DeallocateDeviceData();
+ }
+}
+
+bool
+TextureHostDirectUpload::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mTextureSource) {
+ Updated();
+ }
+
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+DIBTextureHost::DIBTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorDIB& aDescriptor)
+ : TextureHostDirectUpload(aFlags, SurfaceFormat::B8G8R8X8, IntSize())
+{
+ // We added an extra ref for transport, so we shouldn't AddRef now.
+ mSurface =
+ dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
+ MOZ_ASSERT(mSurface);
+
+ mSize = mSurface->GetSize();
+ mFormat = mSurface->GetSurfaceFormat();
+}
+
+void
+DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ if (mSurface->CairoStatus()) {
+ gfxWarning() << "Bad Cairo surface internal update " << mSurface->CairoStatus();
+ mTextureSource = nullptr;
+ return;
+ }
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat);
+
+ if (!surf || !mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+TextureHostFileMapping::TextureHostFileMapping(TextureFlags aFlags,
+ const SurfaceDescriptorFileMapping& aDescriptor)
+ : TextureHostDirectUpload(aFlags, aDescriptor.format(), aDescriptor.size())
+ , mFileMapping((HANDLE)aDescriptor.handle())
+{
+}
+
+TextureHostFileMapping::~TextureHostFileMapping()
+{
+ ::CloseHandle(mFileMapping);
+}
+
+UserDataKey kFileMappingKey;
+
+static void UnmapFileData(void* aData)
+{
+ MOZ_ASSERT(aData);
+ ::UnmapViewOfFile(aData);
+}
+
+void
+TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ uint8_t* data = nullptr;
+ int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height, BytesPerPixel(mFormat));
+ if (totalBytes > 0) {
+ data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, totalBytes);
+ }
+
+ if (data) {
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat);
+ if (surf) {
+ surf->AddUserData(&kFileMappingKey, data, UnmapFileData);
+ if (!mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+}
+}
diff --git a/system/graphics/layers/TextureDIB.h b/system/graphics/layers/TextureDIB.h
new file mode 100644
index 000000000..7eed7e58c
--- /dev/null
+++ b/system/graphics/layers/TextureDIB.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREDIB_H
+#define MOZILLA_GFX_TEXTUREDIB_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "gfxWindowsPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+class DIBTextureData : public TextureData
+{
+public:
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+protected:
+ DIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface)
+ : mSurface(aSurface)
+ , mSize(aSize)
+ , mFormat(aFormat)
+ {
+ MOZ_ASSERT(aSurface);
+ }
+
+ RefPtr<gfxWindowsSurface> mSurface;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+};
+
+/**
+ * This is meant for a texture host which does a direct upload from
+ * Updated to a Compositor specific DataTextureSource and therefor doesn't
+ * need any specific Lock/Unlock magic.
+ */
+class TextureHostDirectUpload : public TextureHost
+{
+public:
+ TextureHostDirectUpload(TextureFlags aFlags,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize)
+ : TextureHost(aFlags)
+ , mFormat(aFormat)
+ , mSize(aSize)
+ , mIsLocked(false)
+ { }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual bool HasIntermediateBuffer() const { return true; }
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+protected:
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<Compositor> mCompositor;
+ gfx::SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ bool mIsLocked;
+};
+
+class DIBTextureHost : public TextureHostDirectUpload
+{
+public:
+ DIBTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorDIB& aDescriptor);
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // TODO: cf bug 872568
+ }
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ RefPtr<gfxWindowsSurface> mSurface;
+};
+
+class TextureHostFileMapping : public TextureHostDirectUpload
+{
+public:
+ TextureHostFileMapping(TextureFlags aFlags,
+ const SurfaceDescriptorFileMapping& aDescriptor);
+ ~TextureHostFileMapping();
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ MOZ_CRASH("GFX: TextureHostFileMapping::GetAsSurface not implemented");
+ // Not implemented! It would be tricky to keep track of the
+ // scope of the file mapping. We could do this through UserData
+ // on the DataSourceSurface but we don't need this right now.
+ }
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ HANDLE mFileMapping;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_TEXTUREDIB_H */
diff --git a/system/graphics/layers/TextureWrapperImage.cpp b/system/graphics/layers/TextureWrapperImage.cpp
new file mode 100644
index 000000000..e1ead5d68
--- /dev/null
+++ b/system/graphics/layers/TextureWrapperImage.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "TextureWrapperImage.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+TextureWrapperImage::TextureWrapperImage(TextureClient* aClient, const IntRect& aPictureRect)
+ : Image(nullptr, ImageFormat::TEXTURE_WRAPPER),
+ mPictureRect(aPictureRect),
+ mTextureClient(aClient)
+{
+}
+
+TextureWrapperImage::~TextureWrapperImage()
+{
+}
+
+gfx::IntSize
+TextureWrapperImage::GetSize()
+{
+ return mTextureClient->GetSize();
+}
+
+gfx::IntRect
+TextureWrapperImage::GetPictureRect()
+{
+ return mPictureRect;
+}
+
+already_AddRefed<gfx::SourceSurface>
+TextureWrapperImage::GetAsSourceSurface()
+{
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_READ);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt = mTextureClient->BorrowDrawTarget();
+ if (!dt) {
+ return nullptr;
+ }
+
+ return dt->Snapshot();
+}
+
+TextureClient*
+TextureWrapperImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/TextureWrapperImage.h b/system/graphics/layers/TextureWrapperImage.h
new file mode 100644
index 000000000..ef67c3dd4
--- /dev/null
+++ b/system/graphics/layers/TextureWrapperImage.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
+#define GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+// Wraps a TextureClient into an Image. This may only be used on the main
+// thread, and only with TextureClients that support BorrowDrawTarget().
+class TextureWrapperImage final : public Image
+{
+public:
+ TextureWrapperImage(TextureClient* aClient, const gfx::IntRect& aPictureRect);
+ ~TextureWrapperImage() override;
+
+ gfx::IntSize GetSize() override;
+ gfx::IntRect GetPictureRect() override;
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+private:
+ gfx::IntRect mPictureRect;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
diff --git a/system/graphics/layers/TiledLayerBuffer.h b/system/graphics/layers/TiledLayerBuffer.h
new file mode 100644
index 000000000..c1efcc39e
--- /dev/null
+++ b/system/graphics/layers/TiledLayerBuffer.h
@@ -0,0 +1,220 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TILEDLAYERBUFFER_H
+#define GFX_TILEDLAYERBUFFER_H
+
+// Debug defines
+//#define GFX_TILEDLAYER_DEBUG_OVERLAY
+//#define GFX_TILEDLAYER_PREF_WARNINGS
+//#define GFX_TILEDLAYER_RETAINING_LOG
+
+#include <stdint.h> // for uint16_t, uint32_t
+#include <sys/types.h> // for int32_t
+#include "LayersLogging.h" // for print_stderr
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h" // for gfxCriticalError
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+
+struct TileUnit {};
+template<> struct IsPixel<TileUnit> : mozilla::TrueType {};
+
+namespace layers {
+
+// You can enable all the TILING_LOG print statements by
+// changing the 0 to a 1 in the following #define.
+#define ENABLE_TILING_LOG 0
+
+#if ENABLE_TILING_LOG
+# define TILING_LOG(...) printf_stderr(__VA_ARGS__);
+#else
+# define TILING_LOG(...)
+#endif
+
+// Normal integer division truncates towards zero,
+// we instead want to floor to hangle negative numbers.
+static inline int floor_div(int a, int b)
+{
+ int rem = a % b;
+ int div = a/b;
+ if (rem == 0) {
+ return div;
+ } else {
+ // If the signs are different substract 1.
+ int sub;
+ sub = a ^ b;
+ // The results of this shift is either 0 or -1.
+ sub >>= 8*sizeof(int)-1;
+ return div+sub;
+ }
+}
+
+// Tiles are aligned to a grid with one of the grid points at (0,0) and other
+// grid points spaced evenly in the x- and y-directions by GetTileSize()
+// multiplied by mResolution. GetScaledTileSize() provides convenience for
+// accessing these values.
+//
+// This tile buffer stores a valid region, which defines the areas that have
+// up-to-date content. The contents of tiles within this region will be reused
+// from paint to paint. It also stores the region that was modified in the last
+// paint operation; this is useful when one tiled layer buffer shadows another
+// (as in an off-main-thread-compositing scenario), so that the shadow tiled
+// layer buffer can correctly reflect the updates of the master layer buffer.
+//
+// The associated Tile may be of any type as long as the derived class can
+// validate and return tiles of that type. Tiles will be frequently copied, so
+// the tile type should be a reference or some other type with an efficient
+// copy constructor.
+//
+// The contents of the tile buffer will be rendered at the resolution specified
+// in mResolution, which can be altered with SetResolution. The resolution
+// should always be a factor of the tile length, to avoid tiles covering
+// non-integer amounts of pixels.
+
+// Size and Point in number of tiles rather than in pixels
+typedef gfx::IntSizeTyped<TileUnit> TileIntSize;
+typedef gfx::IntPointTyped<TileUnit> TileIntPoint;
+
+/**
+ * Stores the origin and size of a tile buffer and handles switching between
+ * tile indices and tile positions.
+ *
+ * Tile positions in TileIntPoint take the first tile offset into account which
+ * means that two TilesPlacement of the same layer and resolution give tile
+ * positions in the same coordinate space (useful when changing the offset and/or
+ * size of a tile buffer).
+ */
+struct TilesPlacement {
+ // in tiles
+ TileIntPoint mFirst;
+ TileIntSize mSize;
+
+ TilesPlacement(int aFirstX, int aFirstY,
+ int aRetainedWidth, int aRetainedHeight)
+ : mFirst(aFirstX, aFirstY)
+ , mSize(aRetainedWidth, aRetainedHeight)
+ {}
+
+ int TileIndex(TileIntPoint aPosition) const {
+ return (aPosition.x - mFirst.x) * mSize.height + aPosition.y - mFirst.y;
+ }
+
+ TileIntPoint TilePosition(size_t aIndex) const {
+ return TileIntPoint(
+ mFirst.x + aIndex / mSize.height,
+ mFirst.y + aIndex % mSize.height
+ );
+ }
+
+ bool HasTile(TileIntPoint aPosition) const {
+ return aPosition.x >= mFirst.x && aPosition.x < mFirst.x + mSize.width &&
+ aPosition.y >= mFirst.y && aPosition.y < mFirst.y + mSize.height;
+ }
+};
+
+
+// Given a position i, this function returns the position inside the current tile.
+inline int GetTileStart(int i, int aTileLength) {
+ return (i >= 0) ? (i % aTileLength)
+ : ((aTileLength - (-i % aTileLength)) %
+ aTileLength);
+}
+
+// Rounds the given coordinate down to the nearest tile boundary.
+inline int RoundDownToTileEdge(int aX, int aTileLength) { return aX - GetTileStart(aX, aTileLength); }
+
+template<typename Derived, typename Tile>
+class TiledLayerBuffer
+{
+public:
+ TiledLayerBuffer()
+ : mTiles(0, 0, 0, 0)
+ , mResolution(1)
+ , mTileSize(mozilla::gfx::gfxVars::TileSize())
+ {}
+
+ ~TiledLayerBuffer() {}
+
+ gfx::IntPoint GetTileOffset(TileIntPoint aPosition) const {
+ gfx::IntSize scaledTileSize = GetScaledTileSize();
+ return gfx::IntPoint(aPosition.x * scaledTileSize.width,
+ aPosition.y * scaledTileSize.height) + mTileOrigin;
+ }
+
+ const TilesPlacement& GetPlacement() const { return mTiles; }
+
+ const gfx::IntSize& GetTileSize() const { return mTileSize; }
+
+ gfx::IntSize GetScaledTileSize() const { return gfx::IntSize::Round(gfx::Size(mTileSize) / mResolution); }
+
+ unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
+
+ Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
+
+ const nsIntRegion& GetValidRegion() const { return mValidRegion; }
+ const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
+ void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
+
+ // Get and set draw scaling. mResolution affects the resolution at which the
+ // contents of the buffer are drawn. mResolution has no effect on the
+ // coordinate space of the valid region, but does affect the size of an
+ // individual tile's rect in relation to the valid region.
+ // Setting the resolution will invalidate the buffer.
+ float GetResolution() const { return mResolution; }
+ bool IsLowPrecision() const { return mResolution < 1; }
+
+ void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml,
+ TextureDumpMode aCompress);
+
+protected:
+
+ nsIntRegion mValidRegion;
+ nsIntRegion mPaintedRegion;
+
+ /**
+ * mRetainedTiles is a rectangular buffer of mTiles.mSize.width x mTiles.mSize.height
+ * stored as column major with the same origin as mValidRegion.GetBounds().
+ * Any tile that does not intersect mValidRegion is a PlaceholderTile.
+ * Only the region intersecting with mValidRegion should be read from a tile,
+ * another other region is assumed to be uninitialized. The contents of the
+ * tiles is scaled by mResolution.
+ */
+ nsTArray<Tile> mRetainedTiles;
+ TilesPlacement mTiles;
+ float mResolution;
+ gfx::IntSize mTileSize;
+ gfx::IntPoint mTileOrigin;
+};
+
+template<typename Derived, typename Tile> void
+TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress)
+{
+ for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
+ const TileIntPoint tilePosition = mTiles.TilePosition(i);
+ gfx::IntPoint tileOffset = GetTileOffset(tilePosition);
+
+ aStream << "\n" << aPrefix << "Tile (x=" <<
+ tileOffset.x << ", y=" << tileOffset.y << "): ";
+ if (!mRetainedTiles[i].IsPlaceholderTile()) {
+ mRetainedTiles[i].DumpTexture(aStream, aCompress);
+ } else {
+ aStream << "empty tile";
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_TILEDLAYERBUFFER_H
diff --git a/system/graphics/layers/TransactionIdAllocator.h b/system/graphics/layers/TransactionIdAllocator.h
new file mode 100644
index 000000000..f6940a35e
--- /dev/null
+++ b/system/graphics/layers/TransactionIdAllocator.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TRANSACTION_ID_ALLOCATOR_H
+#define GFX_TRANSACTION_ID_ALLOCATOR_H
+
+#include "nsISupportsImpl.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace layers {
+
+class TransactionIdAllocator {
+protected:
+ virtual ~TransactionIdAllocator() {}
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(TransactionIdAllocator)
+
+ /**
+ * Allocate a unique id number for the current refresh tick, can
+ * only be called while IsInRefresh().
+ *
+ * If too many id's are allocated without being returned then
+ * the refresh driver will suspend until they catch up.
+ */
+ virtual uint64_t GetTransactionId() = 0;
+
+ /**
+ * Return the transaction id that for the last non-revoked transaction.
+ * This allows the caller to tell whether a composite was triggered by
+ * a paint that occurred after a call to TransactionId().
+ */
+ virtual uint64_t LastTransactionId() const = 0;
+
+ /**
+ * Notify that all work (including asynchronous composites)
+ * for a given transaction id has been completed.
+ *
+ * If the refresh driver has been suspended because
+ * of having too many outstanding id's, then this may
+ * resume it.
+ */
+ virtual void NotifyTransactionCompleted(uint64_t aTransactionId) = 0;
+
+ /**
+ * Revoke a transaction id that isn't needed to track
+ * completion of asynchronous work. This is similar
+ * to NotifyTransactionCompleted except avoids
+ * return ordering issues.
+ */
+ virtual void RevokeTransactionId(uint64_t aTransactionId) = 0;
+
+ /**
+ * Get the start time of the current refresh tick.
+ */
+ virtual mozilla::TimeStamp GetTransactionStart() = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+
+#endif /* GFX_TRANSACTION_ID_ALLOCATOR_H */
diff --git a/system/graphics/layers/TreeTraversal.h b/system/graphics/layers/TreeTraversal.h
new file mode 100644
index 000000000..369eb624f
--- /dev/null
+++ b/system/graphics/layers/TreeTraversal.h
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_TreeTraversal_h
+#define mozilla_layers_TreeTraversal_h
+
+#include <queue>
+
+namespace mozilla {
+namespace layers {
+
+
+/*
+ * Returned by |aPostAction| and |aPreAction| in ForEachNode, indicates
+ * the behavior to follow either action:
+ *
+ * TraversalFlag::Skip - the node's children are not traversed. If this
+ * flag is returned by |aPreAction|, |aPostAction| is skipped for the
+ * current node, as well.
+ * TraversalFlag::Continue - traversal continues normally.
+ * TraversalFlag::Abort - traversal stops immediately.
+ */
+enum class TraversalFlag { Skip, Continue, Abort };
+
+/*
+ * Iterator types to be specified in traversal function calls:
+ *
+ * ForwardIterator - for nodes using GetFirstChild() and GetNextSibling()
+ * ReverseIterator - for nodes using GetLastChild() and GetPrevSibling()
+ */
+class ForwardIterator
+{
+ public:
+ template <typename Node>
+ static Node* FirstChild(Node* n) {
+ return n->GetFirstChild();
+ }
+ template <typename Node>
+ static Node* NextSibling(Node* n) {
+ return n->GetNextSibling();
+ }
+ template <typename Node>
+ static Node FirstChild(Node n) {
+ return n.GetFirstChild();
+ }
+ template <typename Node>
+ static Node NextSibling(Node n) {
+ return n.GetNextSibling();
+ }
+};
+class ReverseIterator
+{
+ public:
+ template <typename Node>
+ static Node* FirstChild(Node* n) {
+ return n->GetLastChild();
+ }
+ template <typename Node>
+ static Node* NextSibling(Node* n) {
+ return n->GetPrevSibling();
+ }
+ template <typename Node>
+ static Node FirstChild(Node n) {
+ return n.GetLastChild();
+ }
+ template <typename Node>
+ static Node NextSibling(Node n) {
+ return n.GetPrevSibling();
+ }
+};
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
+ *
+ * Returns true if traversal aborted, false if continued normally. If
+ * TraversalFlag::Skip is returned in |aPreAction|, then |aPostAction|
+ * is not performed.
+ *
+ * |Iterator| should have static methods named NextSibling() and FirstChild()
+ * that accept an argument of type Node. For convenience, classes
+ * |ForwardIterator| and |ReverseIterator| are provided which implement these
+ * methods as GetNextSibling()/GetFirstChild() and GetPrevSibling()/GetLastChild(),
+ * respectively.
+ */
+template <typename Iterator, typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value &&
+ IsSame<decltype(aPostAction(aRoot)),TraversalFlag>::value, bool>::Type
+{
+ if (!aRoot) {
+ return false;
+ }
+
+ TraversalFlag result = aPreAction(aRoot);
+
+ if (result == TraversalFlag::Abort) {
+ return true;
+ }
+
+ if (result == TraversalFlag::Continue) {
+ for (Node child = Iterator::FirstChild(aRoot);
+ child;
+ child = Iterator::NextSibling(child)) {
+ bool abort = ForEachNode<Iterator>(child, aPreAction, aPostAction);
+ if (abort) {
+ return true;
+ }
+ }
+
+ result = aPostAction(aRoot);
+
+ if (result == TraversalFlag::Abort) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
+ */
+template <typename Iterator, typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value &&
+ IsSame<decltype(aPostAction(aRoot)),void>::value, void>::Type
+{
+ if (!aRoot) {
+ return;
+ }
+
+ aPreAction(aRoot);
+
+ for (Node child = Iterator::FirstChild(aRoot);
+ child;
+ child = Iterator::NextSibling(child)) {
+ ForEachNode<Iterator>(child, aPreAction, aPostAction);
+ }
+
+ aPostAction(aRoot);
+}
+
+/*
+ * ForEachNode pre-order traversal, using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PreAction>
+auto ForEachNode(Node aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+ return ForEachNode<Iterator>(aRoot, aPreAction, [](Node aNode){ return TraversalFlag::Continue; });
+}
+
+/*
+ * ForEachNode pre-order, not using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PreAction>
+auto ForEachNode(Node aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value, void>::Type
+{
+ ForEachNode<Iterator>(aRoot, aPreAction, [](Node aNode){});
+}
+
+/*
+ * ForEachNode post-order traversal, using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+ return ForEachNode<Iterator>(aRoot, [](Node aNode){ return TraversalFlag::Continue; }, aPostAction);
+}
+
+/*
+ * ForEachNode post-order, not using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), void>::value, void>::Type
+{
+ ForEachNode<Iterator>(aRoot, [](Node aNode){}, aPostAction);
+}
+
+/*
+ * Do a breadth-first search of the tree rooted at |aRoot|, and return the
+ * first visited node that satisfies |aCondition|, or nullptr if no such node
+ * was found.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node()
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node BreadthFirstSearch(Node aRoot, const Condition& aCondition)
+{
+ if (!aRoot) {
+ return Node();
+ }
+
+ std::queue<Node> queue;
+ queue.push(aRoot);
+ while (!queue.empty()) {
+ Node node = queue.front();
+ queue.pop();
+
+ if (aCondition(node)) {
+ return node;
+ }
+
+ for (Node child = Iterator::FirstChild(node);
+ child;
+ child = Iterator::NextSibling(child)) {
+ queue.push(child);
+ }
+ }
+
+ return Node();
+}
+
+/*
+ * Do a pre-order, depth-first search of the tree rooted at |aRoot|, and
+ * return the first visited node that satisfies |aCondition|, or nullptr
+ * if no such node was found.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node().
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node DepthFirstSearch(Node aRoot, const Condition& aCondition)
+{
+ Node result = Node();
+
+ ForEachNode<Iterator>(aRoot,
+ [&aCondition, &result](Node aNode)
+ {
+ if (aCondition(aNode)) {
+ result = aNode;
+ return TraversalFlag::Abort;
+ }
+
+ return TraversalFlag::Continue;
+ });
+
+ return result;
+}
+
+/*
+ * Perform a post-order, depth-first search starting at aRoot.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node().
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node DepthFirstSearchPostOrder(Node aRoot, const Condition& aCondition)
+{
+ Node result = Node();
+
+ ForEachNodePostOrder<Iterator>(aRoot,
+ [&aCondition, &result](Node aNode)
+ {
+ if (aCondition(aNode)) {
+ result = aNode;
+ return TraversalFlag::Abort;
+ }
+
+ return TraversalFlag::Continue;
+ });
+
+ return result;
+}
+
+}
+}
+
+#endif // mozilla_layers_TreeTraversal_h
diff --git a/system/graphics/layers/apz/public/CompositorController.h b/system/graphics/layers/apz/public/CompositorController.h
new file mode 100644
index 000000000..c59dfcdf6
--- /dev/null
+++ b/system/graphics/layers/apz/public/CompositorController.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorController_h
+#define mozilla_layers_CompositorController_h
+
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class CompositorController
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+ virtual void ScheduleRenderOnCompositorThread() = 0;
+ virtual void ScheduleHideAllPluginWindows() = 0;
+ virtual void ScheduleShowAllPluginWindows() = 0;
+
+protected:
+ virtual ~CompositorController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorController_h
diff --git a/system/graphics/layers/apz/public/GeckoContentController.h b/system/graphics/layers/apz/public/GeckoContentController.h
new file mode 100644
index 000000000..57aaa165e
--- /dev/null
+++ b/system/graphics/layers/apz/public/GeckoContentController.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GeckoContentController_h
+#define mozilla_layers_GeckoContentController_h
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "InputData.h" // for PinchGestureInput
+#include "Units.h" // for CSSPoint, CSSRect, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/EventForwards.h" // for Modifiers
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class Runnable;
+
+namespace layers {
+
+class GeckoContentController
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController)
+
+ /**
+ * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
+ * Implementations per-platform are responsible for actually handling this.
+ *
+ * This method must always be called on the repaint thread, which depends
+ * on the GeckoContentController. For ChromeProcessController it is the
+ * Gecko main thread, while for RemoteContentController it is the compositor
+ * thread where it can send IPDL messages.
+ */
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
+
+ /**
+ * Different types of tap-related events that can be sent in
+ * the HandleTap function. The names should be relatively self-explanatory.
+ * Note that the eLongTapUp will always be preceded by an eLongTap, but not
+ * all eLongTap notifications will be followed by an eLongTapUp (for instance,
+ * if the user moves their finger after triggering the long-tap but before
+ * lifting it).
+ * The difference between eDoubleTap and eSecondTap is subtle - the eDoubleTap
+ * is for an actual double-tap "gesture" while eSecondTap is for the same user
+ * input but where a double-tap gesture is not allowed. This is used to fire
+ * a click event with detail=2 to web content (similar to what a mouse double-
+ * click would do).
+ */
+ enum class TapType {
+ eSingleTap,
+ eDoubleTap,
+ eSecondTap,
+ eLongTap,
+ eLongTapUp,
+
+ eSentinel,
+ };
+
+ /**
+ * Requests handling of a tap event. |aPoint| is in LD pixels, relative to the
+ * current scroll offset.
+ */
+ virtual void HandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) = 0;
+
+ /**
+ * When the apz.allow_zooming pref is set to false, the APZ will not
+ * translate pinch gestures to actual zooming. Instead, it will call this
+ * method to notify gecko of the pinch gesture, and allow it to deal with it
+ * however it wishes. Note that this function is not called if the pinch is
+ * prevented by content calling preventDefault() on the touch events, or via
+ * use of the touch-action property.
+ * @param aType One of PINCHGESTURE_START, PINCHGESTURE_SCALE, or
+ * PINCHGESTURE_END, indicating the phase of the pinch.
+ * @param aGuid The guid of the APZ that is detecting the pinch. This is
+ * generally the root APZC for the layers id.
+ * @param aSpanChange For the START or END event, this is always 0.
+ * For a SCALE event, this is the difference in span between the
+ * previous state and the new state.
+ * @param aModifiers The keyboard modifiers depressed during the pinch.
+ */
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) = 0;
+
+ /**
+ * Schedules a runnable to run on the controller/UI thread at some time
+ * in the future.
+ * This method must always be called on the controller thread.
+ */
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) = 0;
+
+ /**
+ * Returns true if we are currently on the thread that can send repaint requests.
+ */
+ virtual bool IsRepaintThread() = 0;
+
+ /**
+ * Runs the given task on the "repaint" thread.
+ */
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) = 0;
+
+ enum class APZStateChange {
+ /**
+ * APZ started modifying the view (including panning, zooming, and fling).
+ */
+ eTransformBegin,
+ /**
+ * APZ finished modifying the view.
+ */
+ eTransformEnd,
+ /**
+ * APZ started a touch.
+ * |aArg| is 1 if touch can be a pan, 0 otherwise.
+ */
+ eStartTouch,
+ /**
+ * APZ started a pan.
+ */
+ eStartPanning,
+ /**
+ * APZ finished processing a touch.
+ * |aArg| is 1 if touch was a click, 0 otherwise.
+ */
+ eEndTouch,
+
+ // Sentinel value for IPC, this must be the last item in the enum and
+ // should not be used as an actual message value.
+ eSentinel
+ };
+ /**
+ * General notices of APZ state changes for consumers.
+ * |aGuid| identifies the APZC originating the state change.
+ * |aChange| identifies the type of state change
+ * |aArg| is used by some state changes to pass extra information (see
+ * the documentation for each state change above)
+ */
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg = 0) {}
+
+ /**
+ * Notify content of a MozMouseScrollFailed event.
+ */
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+ {}
+
+ /**
+ * Notify content that the repaint requests have been flushed.
+ */
+ virtual void NotifyFlushComplete() = 0;
+
+ virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
+ virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
+ virtual void SetScrollingRootContent(bool isRootContent) {}
+
+ GeckoContentController() {}
+
+ /**
+ * Needs to be called on the main thread.
+ */
+ virtual void Destroy() {}
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~GeckoContentController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_GeckoContentController_h
diff --git a/system/graphics/layers/apz/public/IAPZCTreeManager.cpp b/system/graphics/layers/apz/public/IAPZCTreeManager.cpp
new file mode 100644
index 000000000..f60cb9c87
--- /dev/null
+++ b/system/graphics/layers/apz/public/IAPZCTreeManager.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/IAPZCTreeManager.h"
+
+#include "gfxPrefs.h" // for gfxPrefs
+#include "InputData.h" // for InputData, etc
+#include "mozilla/EventStateManager.h" // for WheelPrefs
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
+#include "mozilla/MouseEvents.h" // for WidgetMouseEvent
+#include "mozilla/TouchEvents.h" // for WidgetTouchEvent
+
+namespace mozilla {
+namespace layers {
+
+static bool
+WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
+{
+ return aEvent.mMessage == eMouseMove ||
+ aEvent.mMessage == eMouseDown ||
+ aEvent.mMessage == eMouseUp ||
+ aEvent.mMessage == eDragEnd;
+}
+
+// Returns whether or not a wheel event action will be (or was) performed by
+// APZ. If this returns true, the event must not perform a synchronous
+// scroll.
+//
+// Even if this returns false, all wheel events in APZ-aware widgets must
+// be sent through APZ so they are transformed correctly for TabParent.
+static bool
+WillHandleWheelEvent(WidgetWheelEvent* aEvent)
+{
+ return EventStateManager::WheelEventIsScrollAction(aEvent) &&
+ (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
+}
+
+nsEventStatus
+IAPZCTreeManager::ReceiveInputEvent(
+ WidgetInputEvent& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+ // it if the input event goes into a block.
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = 0;
+ }
+
+ switch (aEvent.mClass) {
+ case eMouseEventClass:
+ case eDragEventClass: {
+
+ WidgetMouseEvent& mouseEvent = *aEvent.AsMouseEvent();
+
+ // Note, we call this before having transformed the reference point.
+ if (mouseEvent.IsReal()) {
+ UpdateWheelTransaction(mouseEvent.mRefPoint, mouseEvent.mMessage);
+ }
+
+ if (WillHandleMouseEvent(mouseEvent)) {
+
+ MouseInput input(mouseEvent);
+ input.mOrigin = ScreenPoint(mouseEvent.mRefPoint.x, mouseEvent.mRefPoint.y);
+
+ nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+
+ mouseEvent.mRefPoint.x = input.mOrigin.x;
+ mouseEvent.mRefPoint.y = input.mOrigin.y;
+ mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+ return status;
+
+ }
+
+ TransformEventRefPoint(&mouseEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+ }
+ case eTouchEventClass: {
+
+ WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
+ MultiTouchInput touchInput(touchEvent);
+ nsEventStatus result = ReceiveInputEvent(touchInput, aOutTargetGuid, aOutInputBlockId);
+ // touchInput was modified in-place to possibly remove some
+ // touch points (if we are overscrolled), and the coordinates were
+ // modified using the APZ untransform. We need to copy these changes
+ // back into the WidgetInputEvent.
+ touchEvent.mTouches.Clear();
+ touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
+ for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
+ *touchEvent.mTouches.AppendElement() =
+ touchInput.mTouches[i].ToNewDOMTouch();
+ }
+ touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
+ return result;
+
+ }
+ case eWheelEventClass: {
+ WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
+
+ if (WillHandleWheelEvent(&wheelEvent)) {
+
+ ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
+ if (gfxPrefs::SmoothScrollEnabled() &&
+ ((wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
+ gfxPrefs::WheelSmoothScrollEnabled()) ||
+ (wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE &&
+ gfxPrefs::PageSmoothScrollEnabled())))
+ {
+ scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
+ }
+
+ ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
+ ScrollWheelInput input(wheelEvent.mTime, wheelEvent.mTimeStamp, 0,
+ scrollMode,
+ ScrollWheelInput::DeltaTypeForDeltaMode(
+ wheelEvent.mDeltaMode),
+ origin,
+ wheelEvent.mDeltaX, wheelEvent.mDeltaY,
+ wheelEvent.mAllowToOverrideSystemScrollSpeed);
+
+ // We add the user multiplier as a separate field, rather than premultiplying
+ // it, because if the input is converted back to a WidgetWheelEvent, then
+ // EventStateManager would apply the delta a second time. We could in theory
+ // work around this by asking ESM to customize the event much sooner, and
+ // then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
+ // now, this seems easier.
+ EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
+ &input.mUserDeltaMultiplierX,
+ &input.mUserDeltaMultiplierY);
+
+ nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+ wheelEvent.mRefPoint.x = input.mOrigin.x;
+ wheelEvent.mRefPoint.y = input.mOrigin.y;
+ wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+ return status;
+ }
+
+ UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+ TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+
+ }
+ default: {
+
+ UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+ TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+
+ }
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Invalid WidgetInputEvent type.");
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/public/IAPZCTreeManager.h b/system/graphics/layers/apz/public/IAPZCTreeManager.h
new file mode 100644
index 000000000..f3cc37a71
--- /dev/null
+++ b/system/graphics/layers/apz/public/IAPZCTreeManager.h
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_IAPZCTreeManager_h
+#define mozilla_layers_IAPZCTreeManager_h
+
+#include <stdint.h> // for uint64_t, uint32_t
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus
+#include "mozilla/layers/APZUtils.h" // for HitTestResult
+#include "nsTArrayForwardDeclare.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "Units.h" // for CSSPoint, CSSRect, etc
+
+namespace mozilla {
+class InputData;
+
+namespace layers {
+
+enum AllowedTouchBehavior {
+ NONE = 0,
+ VERTICAL_PAN = 1 << 0,
+ HORIZONTAL_PAN = 1 << 1,
+ PINCH_ZOOM = 1 << 2,
+ DOUBLE_TAP_ZOOM = 1 << 3,
+ UNKNOWN = 1 << 4
+};
+
+enum ZoomToRectBehavior : uint32_t {
+ DEFAULT_BEHAVIOR = 0,
+ DISABLE_ZOOM_OUT = 1 << 0,
+ PAN_INTO_VIEW_ONLY = 1 << 1,
+ ONLY_ZOOM_TO_DEFAULT_SCALE = 1 << 2
+};
+
+class AsyncDragMetrics;
+
+class IAPZCTreeManager {
+ NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
+
+public:
+
+ /**
+ * General handler for incoming input events. Manipulates the frame metrics
+ * based on what type of input it is. For example, a PinchGestureEvent will
+ * cause scaling. This should only be called externally to this class, and
+ * must be called on the controller thread.
+ *
+ * This function transforms |aEvent| to have its coordinates in DOM space.
+ * This is so that the event can be passed through the DOM and content can
+ * handle them. The event may need to be converted to a WidgetInputEvent
+ * by the caller if it wants to do this.
+ *
+ * The following values may be returned by this function:
+ * nsEventStatus_eConsumeNoDefault is returned to indicate the
+ * APZ is consuming this event and the caller should discard the event with
+ * extreme prejudice. The exact scenarios under which this is returned is
+ * implementation-dependent and may vary.
+ * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
+ * use this event. This might be because it was directed at a point on
+ * the screen where there was no APZ, or because the thing the user was
+ * trying to do was not allowed. (For example, attempting to pan a
+ * non-pannable document).
+ * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
+ * code may have used this event to do some user-visible thing. Note that
+ * in some cases CONSUMED is returned even if the event was NOT used. This
+ * is because we cannot always know at the time of event delivery whether
+ * the event will be used or not. So we err on the side of sending
+ * CONSUMED when we are uncertain.
+ *
+ * @param aEvent input event object; is modified in-place
+ * @param aOutTargetGuid returns the guid of the apzc this event was
+ * delivered to. May be null.
+ * @param aOutInputBlockId returns the id of the input block that this event
+ * was added to, if that was the case. May be null.
+ */
+ virtual nsEventStatus ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) = 0;
+
+ /**
+ * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an
+ * already-existing instance of an WidgetInputEvent which may be an
+ * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the
+ * event can be passed through the DOM and content can handle them.
+ *
+ * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be
+ * called on the main thread. See widget/InputData.h for more information on
+ * why we have InputData and WidgetInputEvent separated. If this function is
+ * used, the controller thread must be the main thread, or undefined behaviour
+ * may occur.
+ * NOTE: On unix, mouse events are treated as touch and are forwarded
+ * to the appropriate apz as such.
+ *
+ * See documentation for other ReceiveInputEvent above.
+ */
+ nsEventStatus ReceiveInputEvent(
+ WidgetInputEvent& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId);
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up. |aRect| must be given in CSS pixels, relative to the document.
+ * |aFlags| is a combination of the ZoomToRectBehavior enum values.
+ */
+ virtual void ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) = 0;
+
+ /**
+ * If we have touch listeners, this should always be called when we know
+ * definitively whether or not content has preventDefaulted any touch events
+ * that have come in. If |aPreventDefault| is true, any touch events in the
+ * queue will be discarded. This function must be called on the controller
+ * thread.
+ */
+ virtual void ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) = 0;
+
+ /**
+ * When the event regions code is enabled, this function should be invoked to
+ * to confirm the target of the input block. This is only needed in cases
+ * where the initial input event of the block hit a dispatch-to-content region
+ * but is safe to call for all input blocks. This function should always be
+ * invoked on the controller thread.
+ * The different elements in the array of targets correspond to the targets
+ * for the different touch points. In the case where the touch point has no
+ * target, or the target is not a scrollable frame, the target's |mScrollId|
+ * should be set to FrameMetrics::NULL_SCROLL_ID.
+ */
+ virtual void SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ * If the |aConstraints| is Nothing() then previously-provided constraints for
+ * the given |aGuid| are cleared.
+ */
+ virtual void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) = 0;
+
+ /**
+ * Cancels any currently running animation. Note that all this does is set the
+ * state of the AsyncPanZoomController back to NOTHING, but it is the
+ * animation's responsibility to check this before advancing.
+ */
+ virtual void CancelAnimation(const ScrollableLayerGuid &aGuid) = 0;
+
+ /**
+ * Adjusts the root APZC to compensate for a shift in the surface. See the
+ * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
+ * some more details. This is only currently needed due to surface shifts
+ * caused by the dynamic toolbar on Android.
+ */
+ virtual void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) = 0;
+
+ virtual void SetDPI(float aDpiValue) = 0;
+
+ /**
+ * Sets allowed touch behavior values for current touch-session for specific
+ * input block (determined by aInputBlock).
+ * Should be invoked by the widget. Each value of the aValues arrays
+ * corresponds to the different touch point that is currently active.
+ * Must be called after receiving the TOUCH_START event that starts the
+ * touch-session.
+ * This must be called on the controller thread.
+ */
+ virtual void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) = 0;
+
+ virtual void StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) = 0;
+
+ /**
+ * Function used to disable LongTap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ virtual void SetLongTapEnabled(bool aTapGestureEnabled) = 0;
+
+ /**
+ * Process touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ virtual void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) = 0;
+
+protected:
+
+ // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
+
+ virtual void TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) = 0;
+
+ virtual void UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) = 0;
+
+ // Discourage destruction outside of decref
+
+ virtual ~IAPZCTreeManager() { }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_IAPZCTreeManager_h
diff --git a/system/graphics/layers/apz/public/MetricsSharingController.h b/system/graphics/layers/apz/public/MetricsSharingController.h
new file mode 100644
index 000000000..21981b1d3
--- /dev/null
+++ b/system/graphics/layers/apz/public/MetricsSharingController.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_MetricsSharingController_h
+#define mozilla_layers_MetricsSharingController_h
+
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutexHandle
+#include "mozilla/ipc/SharedMemoryBasic.h" // for SharedMemoryBasic
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class MetricsSharingController
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+ virtual base::ProcessId RemotePid() = 0;
+ virtual bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId) = 0;
+ virtual bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId) = 0;
+
+protected:
+ virtual ~MetricsSharingController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_MetricsSharingController_h
diff --git a/system/graphics/layers/apz/src/APZCTreeManager.cpp b/system/graphics/layers/apz/src/APZCTreeManager.cpp
new file mode 100644
index 000000000..297bf57fe
--- /dev/null
+++ b/system/graphics/layers/apz/src/APZCTreeManager.cpp
@@ -0,0 +1,2113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stack>
+#include "APZCTreeManager.h"
+#include "AsyncPanZoomController.h"
+#include "Compositor.h" // for Compositor
+#include "DragTracker.h" // for DragTracker
+#include "gfxPrefs.h" // for gfxPrefs
+#include "HitTestingTreeNode.h" // for HitTestingTreeNode
+#include "InputBlockState.h" // for InputBlockState
+#include "InputData.h" // for InputData, etc
+#include "Layers.h" // for Layer, etc
+#include "mozilla/dom/Touch.h" // for Touch
+#include "mozilla/gfx/GPUParent.h" // for GPUParent
+#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "mozilla/TouchEvents.h"
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/EventStateManager.h" // for WheelPrefs
+#include "nsDebug.h" // for NS_WARNING
+#include "nsPoint.h" // for nsIntPoint
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "OverscrollHandoffState.h" // for OverscrollHandoffState
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
+#include "LayersLogging.h" // for Stringify
+#include "Units.h" // for ParentlayerPixel
+#include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
+#include "UnitTransforms.h" // for ViewAs
+
+#define ENABLE_APZCTM_LOGGING 0
+// #define ENABLE_APZCTM_LOGGING 1
+
+#if ENABLE_APZCTM_LOGGING
+# define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
+#else
+# define APZCTM_LOG(...)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+typedef mozilla::gfx::Point Point;
+typedef mozilla::gfx::Point4D Point4D;
+typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+
+float APZCTreeManager::sDPI = 160.0;
+
+struct APZCTreeManager::TreeBuildingState {
+ TreeBuildingState(const CompositorBridgeParent::LayerTreeState* const aLayerTreeState,
+ bool aIsFirstPaint, uint64_t aOriginatingLayersId,
+ APZTestData* aTestData, uint32_t aPaintSequence)
+ : mLayerTreeState(aLayerTreeState)
+ , mIsFirstPaint(aIsFirstPaint)
+ , mOriginatingLayersId(aOriginatingLayersId)
+ , mPaintLogger(aTestData, aPaintSequence)
+ {
+ }
+
+ // State that doesn't change as we recurse in the tree building
+ const CompositorBridgeParent::LayerTreeState* const mLayerTreeState;
+ const bool mIsFirstPaint;
+ const uint64_t mOriginatingLayersId;
+ const APZPaintLogHelper mPaintLogger;
+
+ // State that is updated as we perform the tree build
+
+ // A list of nodes that need to be destroyed at the end of the tree building.
+ // This is initialized with all nodes in the old tree, and nodes are removed
+ // from it as we reuse them in the new tree.
+ nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
+
+ // This map is populated as we place APZCs into the new tree. Its purpose is
+ // to facilitate re-using the same APZC for different layers that scroll
+ // together (and thus have the same ScrollableLayerGuid).
+ std::map<ScrollableLayerGuid, AsyncPanZoomController*> mApzcMap;
+};
+
+// Returns whether or not a wheel event action will be (or was) performed by
+// APZ. If this returns true, the event must not perform a synchronous
+// scroll.
+//
+// Even if this returns false, all wheel events in APZ-aware widgets must
+// be sent through APZ so they are transformed correctly for TabParent.
+static bool
+WillHandleWheelEvent(WidgetWheelEvent* aEvent)
+{
+ return EventStateManager::WheelEventIsScrollAction(aEvent) &&
+ (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
+}
+
+class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
+ : mTreeManager(aTreeManager)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
+ }
+ }
+
+ void Unregister()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
+ }
+ mTreeManager = nullptr;
+ }
+
+protected:
+ virtual ~CheckerboardFlushObserver() {}
+
+private:
+ RefPtr<APZCTreeManager> mTreeManager;
+};
+
+NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
+
+NS_IMETHODIMP
+APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t*)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTreeManager.get());
+
+ MutexAutoLock lock(mTreeManager->mTreeLock);
+ if (mTreeManager->mRootNode) {
+ ForEachNode<ReverseIterator>(mTreeManager->mRootNode.get(),
+ [](HitTestingTreeNode* aNode)
+ {
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->FlushActiveCheckerboardReport();
+ }
+ });
+ }
+ if (XRE_IsGPUProcess()) {
+ if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
+ nsCString topic("APZ:FlushActiveCheckerboard:Done");
+ Unused << gpu->SendNotifyUiObservers(topic);
+ }
+ } else {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
+ }
+ }
+ return NS_OK;
+}
+
+
+/*static*/ const ScreenMargin
+APZCTreeManager::CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ return AsyncPanZoomController::CalculatePendingDisplayPort(
+ aFrameMetrics, aVelocity);
+}
+
+APZCTreeManager::APZCTreeManager()
+ : mInputQueue(new InputQueue()),
+ mTreeLock("APZCTreeLock"),
+ mHitResultForInputBlock(HitNothing),
+ mRetainedTouchIdentifier(-1),
+ mApzcTreeLog("apzctree")
+{
+ RefPtr<APZCTreeManager> self(this);
+ NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
+ self->mFlushObserver = new CheckerboardFlushObserver(self);
+ }));
+ AsyncPanZoomController::InitializeGlobalState();
+ mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
+}
+
+APZCTreeManager::~APZCTreeManager()
+{
+}
+
+/*static*/ void
+APZCTreeManager::InitializeGlobalState()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ AsyncPanZoomController::InitializeGlobalState();
+}
+
+AsyncPanZoomController*
+APZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ return new AsyncPanZoomController(aLayersId, this, mInputQueue,
+ aController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
+}
+
+TimeStamp
+APZCTreeManager::GetFrameTime()
+{
+ return TimeStamp::Now();
+}
+
+void
+APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags> &aValues)
+{
+ mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void
+APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+ Layer* aRoot,
+ bool aIsFirstPaint,
+ uint64_t aOriginatingLayersId,
+ uint32_t aPaintSequenceNumber)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ MutexAutoLock lock(mTreeLock);
+
+ // For testing purposes, we log some data to the APZTestData associated with
+ // the layers id that originated this update.
+ APZTestData* testData = nullptr;
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
+ testData = &state->mApzTestData;
+ testData->StartNewPaint(aPaintSequenceNumber);
+ }
+ }
+
+ const CompositorBridgeParent::LayerTreeState* treeState =
+ CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
+ MOZ_ASSERT(treeState);
+ TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
+ testData, aPaintSequenceNumber);
+
+ // We do this business with collecting the entire tree into an array because otherwise
+ // it's very hard to determine which APZC instances need to be destroyed. In the worst
+ // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
+ // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
+ // completely different place. In scenario (a) we would want to destroy the APZC while
+ // walking the layer tree and noticing that the layer/APZC is no longer there. But if
+ // we do that then we run into a problem in scenario (b) because we might encounter that
+ // layer later during the walk. To handle both of these we have to 'remember' that the
+ // layer was not found, and then do the destroy only at the end of the tree walk after
+ // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
+ // as part of a recursive tree walk is hard and so maintaining a list and removing
+ // APZCs that are still alive is much simpler.
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [&state] (HitTestingTreeNode* aNode)
+ {
+ state.mNodesToDestroy.AppendElement(aNode);
+ });
+ mRootNode = nullptr;
+
+ if (aRoot) {
+ std::stack<gfx::TreeAutoIndent> indents;
+ std::stack<gfx::Matrix4x4> ancestorTransforms;
+ HitTestingTreeNode* parent = nullptr;
+ HitTestingTreeNode* next = nullptr;
+ uint64_t layersId = aRootLayerTreeId;
+ ancestorTransforms.push(Matrix4x4());
+
+ mApzcTreeLog << "[start]\n";
+ LayerMetricsWrapper root(aRoot);
+ mTreeLock.AssertCurrentThreadOwns();
+
+ ForEachNode<ReverseIterator>(root,
+ [&](LayerMetricsWrapper aLayerMetrics)
+ {
+ mApzcTreeLog << aLayerMetrics.Name() << '\t';
+
+ HitTestingTreeNode* node = PrepareNodeForLayer(aLayerMetrics,
+ aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
+ parent, next, state);
+ MOZ_ASSERT(node);
+ AsyncPanZoomController* apzc = node->GetApzc();
+ aLayerMetrics.SetApzc(apzc);
+
+ mApzcTreeLog << '\n';
+
+ // Accumulate the CSS transform between layers that have an APZC.
+ // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
+ // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
+ // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
+ // transform to layer L when we recurse into the children below. If we are at a layer
+ // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
+ // the new accumulation as we go down.
+ // If a transform is a perspective transform, it's ignored for this purpose
+ // (see bug 1168263).
+ Matrix4x4 currentTransform = aLayerMetrics.TransformIsPerspective() ? Matrix4x4() : aLayerMetrics.GetTransform();
+ if (!apzc) {
+ currentTransform = currentTransform * ancestorTransforms.top();
+ }
+ ancestorTransforms.push(currentTransform);
+
+ // Note that |node| at this point will not have any children, otherwise we
+ // we would have to set next to node->GetFirstChild().
+ MOZ_ASSERT(!node->GetFirstChild());
+ parent = node;
+ next = nullptr;
+ layersId = (aLayerMetrics.AsRefLayer() ? aLayerMetrics.AsRefLayer()->GetReferentId() : layersId);
+ indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
+ },
+ [&](LayerMetricsWrapper aLayerMetrics)
+ {
+ next = parent;
+ parent = parent->GetParent();
+ layersId = next->GetLayersId();
+ ancestorTransforms.pop();
+ indents.pop();
+ });
+
+ mApzcTreeLog << "[end]\n";
+ }
+
+ // We do not support tree structures where the root node has siblings.
+ MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
+
+ for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
+ APZCTM_LOG("Destroying node at %p with APZC %p\n",
+ state.mNodesToDestroy[i].get(),
+ state.mNodesToDestroy[i]->GetApzc());
+ state.mNodesToDestroy[i]->Destroy();
+ }
+
+#if ENABLE_APZCTM_LOGGING
+ // Make the hit-test tree line up with the layer dump
+ printf_stderr("APZCTreeManager (%p)\n", this);
+ mRootNode->Dump(" ");
+#endif
+}
+
+// Compute the clip region to be used for a layer with an APZC. This function
+// is only called for layers which actually have scrollable metrics and an APZC.
+static ParentLayerIntRegion
+ComputeClipRegion(GeckoContentController* aController,
+ const LayerMetricsWrapper& aLayer)
+{
+ ParentLayerIntRegion clipRegion;
+ if (aLayer.GetClipRect()) {
+ clipRegion = *aLayer.GetClipRect();
+ } else {
+ // if there is no clip on this layer (which should only happen for the
+ // root scrollable layer in a process, or for some of the LayerMetrics
+ // expansions of a multi-metrics layer), fall back to using the comp
+ // bounds which should be equivalent.
+ clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
+ }
+
+ return clipRegion;
+}
+
+void
+APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+ const AsyncPanZoomController* apzc)
+{
+ const FrameMetrics& metrics = aLayer.Metrics();
+ mApzcTreeLog << "APZC " << apzc->GetGuid()
+ << "\tcb=" << metrics.GetCompositionBounds()
+ << "\tsr=" << metrics.GetScrollableRect()
+ << (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
+ << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
+ << aLayer.Metadata().GetContentDescription().get();
+}
+
+void
+APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling)
+{
+ if (aNextSibling) {
+ aNextSibling->SetPrevSibling(aNode);
+ } else if (aParent) {
+ aParent->SetLastChild(aNode);
+ } else {
+ MOZ_ASSERT(!mRootNode);
+ mRootNode = aNode;
+ aNode->MakeRoot();
+ }
+}
+
+static EventRegions
+GetEventRegions(const LayerMetricsWrapper& aLayer)
+{
+ if (aLayer.IsScrollInfoLayer()) {
+ ParentLayerIntRect compositionBounds(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
+ nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
+ EventRegions eventRegions(hitRegion);
+ eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
+ return eventRegions;
+ }
+ return aLayer.GetEventRegions();
+}
+
+already_AddRefed<HitTestingTreeNode>
+APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
+ AsyncPanZoomController* aApzc,
+ uint64_t aLayersId)
+{
+ // Find a node without an APZC and return it. Note that unless the layer tree
+ // actually changes, this loop should generally do an early-return on the
+ // first iteration, so it should be cheap in the common case.
+ for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
+ RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
+ if (!node->IsPrimaryHolder()) {
+ aState.mNodesToDestroy.RemoveElement(node);
+ node->RecycleWith(aApzc, aLayersId);
+ return node.forget();
+ }
+ }
+ RefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false, aLayersId);
+ return node.forget();
+}
+
+static EventRegionsOverride
+GetEventRegionsOverride(HitTestingTreeNode* aParent,
+ const LayerMetricsWrapper& aLayer)
+{
+ // Make it so that if the flag is set on the layer tree, it automatically
+ // propagates to all the nodes in the corresponding subtree rooted at that
+ // layer in the hit-test tree. This saves having to walk up the tree every
+ // we want to see if a hit-test node is affected by this flag.
+ EventRegionsOverride result = aLayer.GetEventRegionsOverride();
+ if (aParent) {
+ result |= aParent->GetEventRegionsOverride();
+ }
+ return result;
+}
+
+void
+APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (!apzc) {
+ return;
+ }
+
+ uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
+ mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
+}
+
+HitTestingTreeNode*
+APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+ const FrameMetrics& aMetrics,
+ uint64_t aLayersId,
+ const gfx::Matrix4x4& aAncestorTransform,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling,
+ TreeBuildingState& aState)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ bool needsApzc = true;
+ if (!aMetrics.IsScrollable()) {
+ needsApzc = false;
+ }
+
+ const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!(state && state->mController.get())) {
+ needsApzc = false;
+ }
+
+ RefPtr<HitTestingTreeNode> node = nullptr;
+ if (!needsApzc) {
+ node = RecycleOrCreateNode(aState, nullptr, aLayersId);
+ AttachNodeToTree(node, aParent, aNextSibling);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
+ GetEventRegionsOverride(aParent, aLayer));
+ node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
+ aLayer.GetScrollbarDirection(),
+ aLayer.GetScrollbarSize(),
+ aLayer.IsScrollbarContainer());
+ node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
+ return node;
+ }
+
+ AsyncPanZoomController* apzc = nullptr;
+ // If we get here, aLayer is a scrollable layer and somebody
+ // has registered a GeckoContentController for it, so we need to ensure
+ // it has an APZC instance to manage its scrolling.
+
+ // aState.mApzcMap allows reusing the exact same APZC instance for different layers
+ // with the same FrameMetrics data. This is needed because in some cases content
+ // that is supposed to scroll together is split into multiple layers because of
+ // e.g. non-scrolling content interleaved in z-index order.
+ ScrollableLayerGuid guid(aLayersId, aMetrics);
+ auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
+ if (!insertResult.second) {
+ apzc = insertResult.first->second;
+ PrintAPZCInfo(aLayer, apzc);
+ }
+ APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
+
+ // If we haven't encountered a layer already with the same metrics, then we need to
+ // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
+ // below.
+ if (apzc == nullptr) {
+ apzc = aLayer.GetApzc();
+
+ // If the content represented by the scrollable layer has changed (which may
+ // be possible because of DLBI heuristics) then we don't want to keep using
+ // the same old APZC for the new content. Also, when reparenting a tab into a
+ // new window a layer might get moved to a different layer tree with a
+ // different APZCTreeManager. In these cases we don't want to reuse the same
+ // APZC, so null it out so we run through the code to find another one or
+ // create one.
+ if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
+ apzc = nullptr;
+ }
+
+ // See if we can find an APZC from the previous tree that matches the
+ // ScrollableLayerGuid from this layer. If there is one, then we know that
+ // the layout of the page changed causing the layer tree to be rebuilt, but
+ // the underlying content for the APZC is still there somewhere. Therefore,
+ // we want to find the APZC instance and continue using it here.
+ //
+ // We particularly want to find the primary-holder node from the previous
+ // tree that matches, because we don't want that node to get destroyed. If
+ // it does get destroyed, then the APZC will get destroyed along with it by
+ // definition, but we want to keep that APZC around in the new tree.
+ // We leave non-primary-holder nodes in the destroy list because we don't
+ // care about those nodes getting destroyed.
+ for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
+ RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
+ if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
+ node = n;
+ if (apzc != nullptr) {
+ // If there is an APZC already then it should match the one from the
+ // old primary-holder node
+ MOZ_ASSERT(apzc == node->GetApzc());
+ }
+ apzc = node->GetApzc();
+ break;
+ }
+ }
+
+ // The APZC we get off the layer may have been destroyed previously if the
+ // layer was inactive or omitted from the layer tree for whatever reason
+ // from a layers update. If it later comes back it will have a reference to
+ // a destroyed APZC and so we need to throw that out and make a new one.
+ bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
+ if (newApzc) {
+ MOZ_ASSERT(aState.mLayerTreeState);
+ apzc = NewAPZCInstance(aLayersId, state->mController);
+ apzc->SetCompositorController(aState.mLayerTreeState->GetCompositorController());
+ if (state->mCrossProcessParent) {
+ apzc->SetMetricsSharingController(state->CrossProcessSharingController());
+ } else {
+ apzc->SetMetricsSharingController(aState.mLayerTreeState->InProcessSharingController());
+ }
+ MOZ_ASSERT(node == nullptr);
+ node = new HitTestingTreeNode(apzc, true, aLayersId);
+ } else {
+ // If we are re-using a node for this layer clear the tree pointers
+ // so that it doesn't continue pointing to nodes that might no longer
+ // be in the tree. These pointers will get reset properly as we continue
+ // building the tree. Also remove it from the set of nodes that are going
+ // to be destroyed, because it's going to remain active.
+ aState.mNodesToDestroy.RemoveElement(node);
+ node->SetPrevSibling(nullptr);
+ node->SetLastChild(nullptr);
+ }
+
+ APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
+
+ apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
+ aLayersId == aState.mOriginatingLayersId);
+
+ // Since this is the first time we are encountering an APZC with this guid,
+ // the node holding it must be the primary holder. It may be newly-created
+ // or not, depending on whether it went through the newApzc branch above.
+ MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
+
+ ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ Some(clipRegion),
+ GetEventRegionsOverride(aParent, aLayer));
+ apzc->SetAncestorTransform(aAncestorTransform);
+
+ PrintAPZCInfo(aLayer, apzc);
+
+ // Bind the APZC instance into the tree of APZCs
+ AttachNodeToTree(node, aParent, aNextSibling);
+
+ // For testing, log the parent scroll id of every APZC that has a
+ // parent. This allows test code to reconstruct the APZC tree.
+ // Note that we currently only do this for APZCs in the layer tree
+ // that originated the update, because the only identifying information
+ // we are logging about APZCs is the scroll id, and otherwise we could
+ // confuse APZCs from different layer trees with the same scroll id.
+ if (aLayersId == aState.mOriginatingLayersId) {
+ if (apzc->HasNoParentWithSameLayersId()) {
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "hasNoParentWithSameLayersId", true);
+ } else {
+ MOZ_ASSERT(apzc->GetParent());
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "parentScrollId", apzc->GetParent()->GetGuid().mScrollId);
+ }
+ if (aMetrics.IsRootContent()) {
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "isRootContent", true);
+ }
+ // Note that the async scroll offset is in ParentLayer pixels
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "asyncScrollOffset",
+ apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL));
+ }
+
+ if (newApzc) {
+ auto it = mZoomConstraints.find(guid);
+ if (it != mZoomConstraints.end()) {
+ // We have a zoomconstraints for this guid, apply it.
+ apzc->UpdateZoomConstraints(it->second);
+ } else if (!apzc->HasNoParentWithSameLayersId()) {
+ // This is a sub-APZC, so inherit the zoom constraints from its parent.
+ // This ensures that if e.g. user-scalable=no was specified, none of the
+ // APZCs for that subtree allow double-tap to zoom.
+ apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
+ }
+ // Otherwise, this is the root of a layers id, but we didn't have a saved
+ // zoom constraints. Leave it empty for now.
+ }
+
+ // Add a guid -> APZC mapping for the newly created APZC.
+ insertResult.first->second = apzc;
+ } else {
+ // We already built an APZC earlier in this tree walk, but we have another layer
+ // now that will also be using that APZC. The hit-test region on the APZC needs
+ // to be updated to deal with the new layer's hit region.
+
+ node = RecycleOrCreateNode(aState, apzc, aLayersId);
+ AttachNodeToTree(node, aParent, aNextSibling);
+
+ // Even though different layers associated with a given APZC may be at
+ // different levels in the layer tree (e.g. one being an uncle of another),
+ // we require from Layout that the CSS transforms up to their common
+ // ancestor be roughly the same. There are cases in which the transforms
+ // are not exactly the same, for example if the parent is container layer
+ // for an opacity, and this container layer has a resolution-induced scale
+ // as its base transform and a prescale that is supposed to undo that scale.
+ // Due to floating point inaccuracies those transforms can end up not quite
+ // canceling each other. That's why we're using a fuzzy comparison here
+ // instead of an exact one.
+ MOZ_ASSERT(aAncestorTransform.FuzzyEqualsMultiplicative(apzc->GetAncestorTransform()));
+
+ ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ Some(clipRegion),
+ GetEventRegionsOverride(aParent, aLayer));
+ }
+
+ node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
+ aLayer.GetScrollbarDirection(),
+ aLayer.GetScrollbarSize(),
+ aLayer.IsScrollbarContainer());
+ node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
+ return node;
+}
+
+template<typename PanGestureOrScrollWheelInput>
+static bool
+WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
+{
+ if (!NS_IsMainThread()) {
+ return true;
+ }
+
+ WidgetWheelEvent wheelEvent = aPanInput.ToWidgetWheelEvent(nullptr);
+ return WillHandleWheelEvent(&wheelEvent);
+}
+
+void
+APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
+{
+ // Previously, paints were throttled and therefore this method was used to
+ // ensure any pending paints were flushed. Now, paints are flushed
+ // immediately, so it is safe to simply send a notification now.
+ APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ MOZ_ASSERT(state && state->mController);
+ state->mController->DispatchToRepaintThread(NewRunnableMethod(
+ state->mController, &GeckoContentController::NotifyFlushComplete));
+}
+
+nsEventStatus
+APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+ // it if the input event goes into a block.
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
+ }
+ nsEventStatus result = nsEventStatus_eIgnore;
+ HitTestResult hitResult = HitNothing;
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
+ result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
+ break;
+ } case MOUSE_INPUT: {
+ MouseInput& mouseInput = aEvent.AsMouseInput();
+ mouseInput.mHandledByAPZ = true;
+
+ if (DragTracker::StartsDrag(mouseInput)) {
+ // If this is the start of a drag we need to unambiguously know if it's
+ // going to land on a scrollbar or not. We can't apply an untransform
+ // here without knowing that, so we need to ensure the untransform is
+ // a no-op.
+ FlushRepaintsToClearScreenToGeckoTransform();
+ }
+
+ bool hitScrollbar = false;
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
+ &hitResult, &hitScrollbar);
+
+ // When the mouse is outside the window we still want to handle dragging
+ // but we won't find an APZC. Fallback to root APZC then.
+ { // scope lock
+ MutexAutoLock lock(mTreeLock);
+ if (!apzc && mRootNode) {
+ apzc = mRootNode->GetApzc();
+ }
+ }
+
+ if (apzc) {
+ bool targetConfirmed = (hitResult != HitNothing && hitResult != HitDispatchToContentRegion);
+ if (gfxPrefs::APZDragEnabled() && hitScrollbar) {
+ // If scrollbar dragging is enabled and we hit a scrollbar, wait
+ // for the main-thread confirmation because it contains drag metrics
+ // that we need.
+ targetConfirmed = false;
+ }
+ result = mInputQueue->ReceiveInputEvent(
+ apzc, targetConfirmed,
+ mouseInput, aOutInputBlockId);
+
+ if (result == nsEventStatus_eConsumeDoDefault) {
+ // This input event is part of a drag block, so whether or not it is
+ // directed at a scrollbar depends on whether the drag block started
+ // on a scrollbar.
+ hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
+ }
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+
+ if (!hitScrollbar) {
+ // The input was not targeted at a scrollbar, so we untransform it
+ // like we do for other content. Scrollbars are "special" because they
+ // have special handling in AsyncCompositionManager when resolution is
+ // applied. TODO: we should find a better way to deal with this.
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+ Maybe<ScreenPoint> untransformedRefPoint = UntransformBy(
+ outTransform, mouseInput.mOrigin);
+ if (untransformedRefPoint) {
+ mouseInput.mOrigin = *untransformedRefPoint;
+ }
+ } else {
+ // Likewise, if the input was targeted at a scrollbar, we don't want to
+ // apply the callback transform in the main thread, so we remove the
+ // scrollid from the guid. We need to keep the layersId intact so
+ // that the response from the child process doesn't get discarded.
+ aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
+ }
+ }
+ break;
+ } case SCROLLWHEEL_INPUT: {
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
+
+ wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
+ if (!wheelInput.mHandledByAPZ) {
+ return result;
+ }
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ // For wheel events, the call to ReceiveInputEvent below may result in
+ // scrolling, which changes the async transform. However, the event we
+ // want to pass to gecko should be the pre-scroll event coordinates,
+ // transformed into the gecko space. (pre-scroll because the mouse
+ // cursor is stationary during wheel scrolling, unlike touchmove
+ // events). Since we just flushed the pending repaints the transform to
+ // gecko space should only consist of overscroll-cancelling transforms.
+ ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedOrigin = UntransformBy(
+ transformToGecko, wheelInput.mOrigin);
+
+ if (!untransformedOrigin) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ wheelInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ wheelInput.mOrigin = *untransformedOrigin;
+ }
+ break;
+ } case PANGESTURE_INPUT: {
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ PanGestureInput& panInput = aEvent.AsPanGestureInput();
+ panInput.mHandledByAPZ = WillHandleInput(panInput);
+ if (!panInput.mHandledByAPZ) {
+ return result;
+ }
+
+ // If/when we enable support for pan inputs off-main-thread, we'll need
+ // to duplicate this EventStateManager code or something. See the other
+ // call to GetUserPrefsForWheelEvent in this file for why these fields
+ // are stored separately.
+ MOZ_ASSERT(NS_IsMainThread());
+ WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
+ EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
+ &panInput.mUserDeltaMultiplierX,
+ &panInput.mUserDeltaMultiplierY);
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ // For pan gesture events, the call to ReceiveInputEvent below may result in
+ // scrolling, which changes the async transform. However, the event we
+ // want to pass to gecko should be the pre-scroll event coordinates,
+ // transformed into the gecko space. (pre-scroll because the mouse
+ // cursor is stationary during pan gesture scrolling, unlike touchmove
+ // events). Since we just flushed the pending repaints the transform to
+ // gecko space should only consist of overscroll-cancelling transforms.
+ ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedStartPoint = UntransformBy(
+ transformToGecko, panInput.mPanStartPoint);
+ Maybe<ScreenPoint> untransformedDisplacement = UntransformVector(
+ transformToGecko, panInput.mPanDisplacement, panInput.mPanStartPoint);
+
+ if (!untransformedStartPoint || !untransformedDisplacement) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ panInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ panInput.mPanStartPoint = *untransformedStartPoint;
+ panInput.mPanDisplacement = *untransformedDisplacement;
+ }
+ break;
+ } case PINCHGESTURE_INPUT: { // note: no one currently sends these
+ PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedFocusPoint = UntransformBy(
+ outTransform, pinchInput.mFocusPoint);
+
+ if (!untransformedFocusPoint) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ pinchInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ pinchInput.mFocusPoint = *untransformedFocusPoint;
+ }
+ break;
+ } case TAPGESTURE_INPUT: { // note: no one currently sends these
+ TapGestureInput& tapInput = aEvent.AsTapGestureInput();
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(tapInput.mPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenIntPoint> untransformedPoint =
+ UntransformBy(outTransform, tapInput.mPoint);
+
+ if (!untransformedPoint) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ tapInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ tapInput.mPoint = *untransformedPoint;
+ }
+ break;
+ } case SENTINEL_INPUT: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputType.");
+ break;
+ }
+ }
+ return result;
+}
+
+static TouchBehaviorFlags
+ConvertToTouchBehavior(HitTestResult result)
+{
+ switch (result) {
+ case HitNothing:
+ return AllowedTouchBehavior::NONE;
+ case HitLayer:
+ return AllowedTouchBehavior::VERTICAL_PAN
+ | AllowedTouchBehavior::HORIZONTAL_PAN
+ | AllowedTouchBehavior::PINCH_ZOOM
+ | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+ case HitLayerTouchActionNone:
+ return AllowedTouchBehavior::NONE;
+ case HitLayerTouchActionPanX:
+ return AllowedTouchBehavior::HORIZONTAL_PAN;
+ case HitLayerTouchActionPanY:
+ return AllowedTouchBehavior::VERTICAL_PAN;
+ case HitLayerTouchActionPanXY:
+ return AllowedTouchBehavior::HORIZONTAL_PAN
+ | AllowedTouchBehavior::VERTICAL_PAN;
+ case HitDispatchToContentRegion:
+ default:
+ return AllowedTouchBehavior::UNKNOWN;
+ }
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
+ nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
+ HitTestResult* aOutHitResult)
+{
+ RefPtr<AsyncPanZoomController> apzc;
+ if (aEvent.mTouches.Length() == 0) {
+ return apzc.forget();
+ }
+
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ HitTestResult hitResult;
+ apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, &hitResult);
+ if (aOutTouchBehaviors) {
+ aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
+ }
+ for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
+ RefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, &hitResult);
+ if (aOutTouchBehaviors) {
+ aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
+ }
+ apzc = GetMultitouchTarget(apzc, apzc2);
+ APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
+ }
+
+ if (aOutHitResult) {
+ // XXX we should probably be combining the hit results from the different
+ // touch points somehow, instead of just using the last one.
+ *aOutHitResult = hitResult;
+ }
+ return apzc.forget();
+}
+
+nsEventStatus
+APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ aInput.mHandledByAPZ = true;
+ nsTArray<TouchBehaviorFlags> touchBehaviors;
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
+ // If we are panned into overscroll and a second finger goes down,
+ // ignore that second touch point completely. The touch-start for it is
+ // dropped completely; subsequent touch events until the touch-end for it
+ // will have this touch point filtered out.
+ // (By contrast, if we're in overscroll but not panning, such as after
+ // putting two fingers down during an overscroll animation, we process the
+ // second touch and proceed to pinch.)
+ if (mApzcForInputBlock &&
+ mApzcForInputBlock->IsInPanningState() &&
+ BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
+ if (mRetainedTouchIdentifier == -1) {
+ mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ mHitResultForInputBlock = HitNothing;
+ mApzcForInputBlock = GetTouchInputBlockAPZC(aInput, &touchBehaviors, &mHitResultForInputBlock);
+ MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
+ for (size_t i = 0; i < touchBehaviors.Length(); i++) {
+ APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", touchBehaviors[i]);
+ if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
+ // If there's any unknown items in the list, throw it out and we'll
+ // wait for the main thread to send us a notification.
+ touchBehaviors.Clear();
+ break;
+ }
+ }
+ } else if (mApzcForInputBlock) {
+ APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
+ }
+
+ // If we receive a touch-cancel, it means all touches are finished, so we
+ // can stop ignoring any that we were ignoring.
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
+ mRetainedTouchIdentifier = -1;
+ }
+
+ // If we are currently ignoring any touch points, filter them out from the
+ // set of touch points included in this event. Note that we modify aInput
+ // itself, so that the touch points are also filtered out when the caller
+ // passes the event on to content.
+ if (mRetainedTouchIdentifier != -1) {
+ for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
+ if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
+ aInput.mTouches.RemoveElementAt(j);
+ if (!touchBehaviors.IsEmpty()) {
+ MOZ_ASSERT(touchBehaviors.Length() > j);
+ touchBehaviors.RemoveElementAt(j);
+ }
+ --j;
+ }
+ }
+ if (aInput.mTouches.IsEmpty()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+
+ nsEventStatus result = nsEventStatus_eIgnore;
+ if (mApzcForInputBlock) {
+ MOZ_ASSERT(mHitResultForInputBlock != HitNothing);
+
+ mApzcForInputBlock->GetGuid(aOutTargetGuid);
+ uint64_t inputBlockId = 0;
+ result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
+ /* aTargetConfirmed = */ mHitResultForInputBlock != HitDispatchToContentRegion,
+ aInput, &inputBlockId);
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = inputBlockId;
+ }
+ if (!touchBehaviors.IsEmpty()) {
+ mInputQueue->SetAllowedTouchBehavior(inputBlockId, touchBehaviors);
+ }
+
+ // For computing the event to pass back to Gecko, use up-to-date transforms
+ // (i.e. not anything cached in an input block).
+ // This ensures that transformToApzc and transformToGecko are in sync.
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+
+ for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
+ SingleTouchData& touchData = aInput.mTouches[i];
+ Maybe<ScreenIntPoint> untransformedScreenPoint = UntransformBy(
+ outTransform, touchData.mScreenPoint);
+ if (!untransformedScreenPoint) {
+ return nsEventStatus_eIgnore;
+ }
+ touchData.mScreenPoint = *untransformedScreenPoint;
+ }
+ }
+
+ mTouchCounter.Update(aInput);
+
+ // If it's the end of the touch sequence then clear out variables so we
+ // don't keep dangling references and leak things.
+ if (mTouchCounter.GetActiveTouchCount() == 0) {
+ mApzcForInputBlock = nullptr;
+ mHitResultForInputBlock = HitNothing;
+ mRetainedTouchIdentifier = -1;
+ }
+
+ return result;
+}
+
+void
+APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage)
+{
+ WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
+ if (!txn) {
+ return;
+ }
+
+ // If the transaction has simply timed out, we don't need to do anything
+ // else.
+ if (txn->MaybeTimeout(TimeStamp::Now())) {
+ return;
+ }
+
+ switch (aEventMessage) {
+ case eMouseMove:
+ case eDragOver: {
+
+ ScreenIntPoint point =
+ ViewAs<ScreenPixel>(aRefPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+
+ txn->OnMouseMove(point);
+
+ return;
+ }
+ case eKeyPress:
+ case eKeyUp:
+ case eKeyDown:
+ case eMouseUp:
+ case eMouseDown:
+ case eMouseDoubleClick:
+ case eMouseAuxClick:
+ case eMouseClick:
+ case eContextMenu:
+ case eDrop:
+ txn->EndTransaction();
+ return;
+ default:
+ break;
+ }
+}
+
+void
+APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ // Transform the aRefPoint.
+ // If the event hits an overscrolled APZC, instruct the caller to ignore it.
+ HitTestResult hitResult = HitNothing;
+ PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
+ ScreenIntPoint refPointAsScreen =
+ ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+ apzc->GetGuid(aOutTargetGuid);
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+ Maybe<ScreenIntPoint> untransformedRefPoint =
+ UntransformBy(outTransform, refPointAsScreen);
+ if (untransformedRefPoint) {
+ *aRefPoint =
+ ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
+ }
+ }
+}
+
+void
+APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+ if (mApzcForInputBlock) {
+ mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
+ }
+}
+
+void
+APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags)
+{
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (apzc) {
+ apzc->ZoomToRect(aRect, aFlags);
+ }
+}
+
+void
+APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void
+APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ RefPtr<AsyncPanZoomController> target = nullptr;
+ if (aTargets.Length() > 0) {
+ target = GetTargetAPZC(aTargets[0]);
+ }
+ for (size_t i = 1; i < aTargets.Length(); i++) {
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
+ target = GetMultitouchTarget(target, apzc);
+ }
+ mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
+}
+
+void
+APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
+ mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
+}
+
+void
+APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+
+ // Propagate the zoom constraints down to the subtree, stopping at APZCs
+ // which have their own zoom constraints or are in a different layers id.
+ if (aConstraints) {
+ APZCTM_LOG("Recording constraints %s for guid %s\n",
+ Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
+ mZoomConstraints[aGuid] = aConstraints.ref();
+ } else {
+ APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
+ mZoomConstraints.erase(aGuid);
+ }
+ if (node && aConstraints) {
+ ForEachNode<ReverseIterator>(node.get(),
+ [&aConstraints, &node, this](HitTestingTreeNode* aNode)
+ {
+ if (aNode != node) {
+ if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
+ // We can have subtrees with their own zoom constraints or separate layers
+ // id - leave these alone.
+ if (childApzc->HasNoParentWithSameLayersId() ||
+ this->mZoomConstraints.find(childApzc->GetGuid()) != this->mZoomConstraints.end()) {
+ return TraversalFlag::Skip;
+ }
+ }
+ }
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref());
+ }
+ return TraversalFlag::Continue;
+ });
+ }
+}
+
+void
+APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform()
+{
+ // As the name implies, we flush repaint requests for the entire APZ tree in
+ // order to clear the screen-to-gecko transform (aka the "untransform" applied
+ // to incoming input events before they can be passed on to Gecko).
+ //
+ // The primary reason we do this is to avoid the problem where input events,
+ // after being untransformed, end up hit-testing differently in Gecko. This
+ // might happen in cases where the input event lands on content that is async-
+ // scrolled into view, but Gecko still thinks it is out of view given the
+ // visible area of a scrollframe.
+ //
+ // Another reason we want to clear the untransform is that if our APZ hit-test
+ // hits a dispatch-to-content region then that's an ambiguous result and we
+ // need to ask Gecko what actually got hit. In order to do this we need to
+ // untransform the input event into Gecko space - but to do that we need to
+ // know which APZC got hit! This leads to a circular dependency; the only way
+ // to get out of it is to make sure that the untransform for all the possible
+ // matched APZCs is the same. It is simplest to ensure that by flushing the
+ // pending repaint requests, which makes all of the untransforms empty (and
+ // therefore equal).
+ MutexAutoLock lock(mTreeLock);
+ mTreeLock.AssertCurrentThreadOwns();
+
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode)
+ {
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->FlushRepaintForNewInputBlock();
+ }
+ });
+}
+
+void
+APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
+{
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (apzc) {
+ apzc->CancelAnimation();
+ }
+}
+
+void
+APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
+ if (apzc) {
+ apzc->AdjustScrollForSurfaceShift(aShift);
+ }
+}
+
+void
+APZCTreeManager::ClearTree()
+{
+ // Ensure that no references to APZCs are alive in any lingering input
+ // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
+ // the InputQueue.
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod(mInputQueue, &InputQueue::Clear));
+
+ MutexAutoLock lock(mTreeLock);
+
+ // Collect the nodes into a list, and then destroy each one.
+ // We can't destroy them as we collect them, because ForEachNode()
+ // does a pre-order traversal of the tree, and Destroy() nulls out
+ // the fields needed to reach the children of the node.
+ nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [&nodesToDestroy](HitTestingTreeNode* aNode)
+ {
+ nodesToDestroy.AppendElement(aNode);
+ });
+
+ for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
+ nodesToDestroy[i]->Destroy();
+ }
+ mRootNode = nullptr;
+
+ RefPtr<APZCTreeManager> self(this);
+ NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
+ self->mFlushObserver->Unregister();
+ self->mFlushObserver = nullptr;
+ }));
+}
+
+RefPtr<HitTestingTreeNode>
+APZCTreeManager::GetRootNode() const
+{
+ MutexAutoLock lock(mTreeLock);
+ return mRootNode;
+}
+
+/**
+ * Transform a displacement from the ParentLayer coordinates of a source APZC
+ * to the ParentLayer coordinates of a target APZC.
+ * @param aTreeManager the tree manager for the APZC tree containing |aSource|
+ * and |aTarget|
+ * @param aSource the source APZC
+ * @param aTarget the target APZC
+ * @param aStartPoint the start point of the displacement
+ * @param aEndPoint the end point of the displacement
+ * @return true on success, false if aStartPoint or aEndPoint cannot be transformed into target's coordinate space
+ */
+static bool
+TransformDisplacement(APZCTreeManager* aTreeManager,
+ AsyncPanZoomController* aSource,
+ AsyncPanZoomController* aTarget,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint) {
+ if (aSource == aTarget) {
+ return true;
+ }
+
+ // Convert start and end points to Screen coordinates.
+ ParentLayerToScreenMatrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
+ ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint);
+ ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint);
+
+ // Convert start and end points to aTarget's ParentLayer coordinates.
+ ScreenToParentLayerMatrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
+ Maybe<ParentLayerPoint> startPoint = UntransformBy(transformToApzc, screenStart);
+ Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd);
+ if (!startPoint || !endPoint) {
+ return false;
+ }
+ aEndPoint = *endPoint;
+ aStartPoint = *startPoint;
+
+ return true;
+}
+
+void
+APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState)
+{
+ const OverscrollHandoffChain& overscrollHandoffChain = aOverscrollHandoffState.mChain;
+ uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
+ RefPtr<AsyncPanZoomController> next;
+ // If we have reached the end of the overscroll handoff chain, there is
+ // nothing more to scroll, so we ignore the rest of the pan gesture.
+ if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
+ // Nothing more to scroll - ignore the rest of the pan gesture.
+ return;
+ }
+
+ next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
+
+ if (next == nullptr || next->IsDestroyed()) {
+ return;
+ }
+
+ // Convert the start and end points from |aPrev|'s coordinate space to
+ // |next|'s coordinate space.
+ if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
+ return;
+ }
+
+ // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
+ // again with an incremented index.
+ if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) {
+ // Transform |aStartPoint| and |aEndPoint| (which now represent the
+ // portion of the displacement that wasn't consumed by APZCs later
+ // in the handoff chain) back into |aPrev|'s coordinate space. This
+ // allows the caller (which is |aPrev|) to interpret the unconsumed
+ // displacement in its own coordinate space, and make use of it
+ // (e.g. by going into overscroll).
+ if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) {
+ NS_WARNING("Failed to untransform scroll points during dispatch");
+ }
+ }
+}
+
+void
+APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev,
+ FlingHandoffState& aHandoffState)
+{
+ // If immediate handoff is disallowed, do not allow handoff beyond the
+ // single APZC that's scrolled by the input block that triggered this fling.
+ if (aHandoffState.mIsHandoff &&
+ !gfxPrefs::APZAllowImmediateHandoff() &&
+ aHandoffState.mScrolledApzc == aPrev) {
+ return;
+ }
+
+ const OverscrollHandoffChain* chain = aHandoffState.mChain;
+ RefPtr<AsyncPanZoomController> current;
+ uint32_t overscrollHandoffChainLength = chain->Length();
+ uint32_t startIndex;
+
+ // This will store any velocity left over after the entire handoff.
+ ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity;
+
+ // The fling's velocity needs to be transformed from the screen coordinates
+ // of |aPrev| to the screen coordinates of |next|. To transform a velocity
+ // correctly, we need to convert it to a displacement. For now, we do this
+ // by anchoring it to a start point of (0, 0).
+ // TODO: For this to be correct in the presence of 3D transforms, we should
+ // use the end point of the touch that started the fling as the start point
+ // rather than (0, 0).
+ ParentLayerPoint startPoint; // (0, 0)
+ ParentLayerPoint endPoint;
+
+ if (aHandoffState.mIsHandoff) {
+ startIndex = chain->IndexOf(aPrev) + 1;
+
+ // IndexOf will return aOverscrollHandoffChain->Length() if
+ // |aPrev| is not found.
+ if (startIndex >= overscrollHandoffChainLength) {
+ return;
+ }
+ } else {
+ startIndex = 0;
+ }
+
+ for (; startIndex < overscrollHandoffChainLength; startIndex++) {
+ current = chain->GetApzcAtIndex(startIndex);
+
+ // Make sure the apcz about to be handled can be handled
+ if (current == nullptr || current->IsDestroyed()) {
+ return;
+ }
+
+ endPoint = startPoint + aHandoffState.mVelocity;
+
+ // Only transform when current apcz can be transformed with previous
+ if (startIndex > 0) {
+ if (!TransformDisplacement(this,
+ chain->GetApzcAtIndex(startIndex - 1),
+ current,
+ startPoint,
+ endPoint)) {
+ return;
+ }
+ }
+
+ ParentLayerPoint transformedVelocity = endPoint - startPoint;
+ aHandoffState.mVelocity = transformedVelocity;
+
+ if (current->AttemptFling(aHandoffState)) {
+ // Coming out of AttemptFling(), the handoff state's velocity is the
+ // residual velocity after attempting to fling |current|.
+ ParentLayerPoint residualVelocity = aHandoffState.mVelocity;
+
+ // If there's no residual velocity, there's nothing more to hand off.
+ if (IsZero(residualVelocity)) {
+ finalResidualVelocity = ParentLayerPoint();
+ break;
+ }
+
+ // If there is residual velocity, subtract the proportion of used
+ // velocity from finalResidualVelocity and continue handoff along the
+ // chain.
+ if (!FuzzyEqualsAdditive(transformedVelocity.x,
+ residualVelocity.x, COORDINATE_EPSILON)) {
+ finalResidualVelocity.x *= (residualVelocity.x / transformedVelocity.x);
+ }
+ if (!FuzzyEqualsAdditive(transformedVelocity.y,
+ residualVelocity.y, COORDINATE_EPSILON)) {
+ finalResidualVelocity.y *= (residualVelocity.y / transformedVelocity.y);
+ }
+ }
+ }
+
+ // Set the handoff state's velocity to any residual velocity left over
+ // after the entire handoff process.
+ aHandoffState.mVelocity = finalResidualVelocity;
+}
+
+bool
+APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
+{
+ RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
+ return target != nullptr;
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+ RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
+ return apzc.forget();
+}
+
+already_AddRefed<HitTestingTreeNode>
+APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+ RefPtr<HitTestingTreeNode> target = DepthFirstSearchPostOrder<ReverseIterator>(mRootNode.get(),
+ [&aGuid, &aComparator](HitTestingTreeNode* node)
+ {
+ bool matches = false;
+ if (node->GetApzc()) {
+ if (aComparator) {
+ matches = aComparator(aGuid, node->GetApzc()->GetGuid());
+ } else {
+ matches = node->GetApzc()->Matches(aGuid);
+ }
+ }
+ return matches;
+ }
+ );
+ return target.forget();
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar)
+{
+ MutexAutoLock lock(mTreeLock);
+ HitTestResult hitResult = HitNothing;
+ ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
+ PixelCastJustification::ScreenIsParentLayerForRoot);
+ RefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point,
+ &hitResult, aOutHitScrollbar);
+
+ if (aOutHitResult) {
+ *aOutHitResult = hitResult;
+ }
+ return target.forget();
+}
+
+static bool
+GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne, const ScrollableLayerGuid& aTwo)
+{
+ return aOne.mLayersId == aTwo.mLayersId
+ && aOne.mScrollId == aTwo.mScrollId;
+}
+
+RefPtr<const OverscrollHandoffChain>
+APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget)
+{
+ // Scroll grabbing is a mechanism that allows content to specify that
+ // the initial target of a pan should be not the innermost scrollable
+ // frame at the touch point (which is what GetTargetAPZC finds), but
+ // something higher up in the tree.
+ // It's not sufficient to just find the initial target, however, as
+ // overscroll can be handed off to another APZC. Without scroll grabbing,
+ // handoff just occurs from child to parent. With scroll grabbing, the
+ // handoff order can be different, so we build a chain of APZCs in the
+ // order in which scroll will be handed off to them.
+
+ // Grab tree lock since we'll be walking the APZC tree.
+ MutexAutoLock lock(mTreeLock);
+
+ // Build the chain. If there is a scroll parent link, we use that. This is
+ // needed to deal with scroll info layers, because they participate in handoff
+ // but do not follow the expected layer tree structure. If there are no
+ // scroll parent links we just walk up the tree to find the scroll parent.
+ OverscrollHandoffChain* result = new OverscrollHandoffChain;
+ AsyncPanZoomController* apzc = aInitialTarget;
+ while (apzc != nullptr) {
+ result->Add(apzc);
+
+ if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
+ if (!apzc->IsRootForLayersId()) {
+ // This probably indicates a bug or missed case in layout code
+ NS_WARNING("Found a non-root APZ with no handoff parent");
+ }
+ apzc = apzc->GetParent();
+ continue;
+ }
+
+ // Guard against a possible infinite-loop condition. If we hit this, the
+ // layout code that generates the handoff parents did something wrong.
+ MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
+
+ // Find the AsyncPanZoomController instance with a matching layersId and
+ // the scroll id that matches apzc->GetScrollHandoffParentId().
+ // As an optimization, we start by walking up the APZC tree from 'apzc'
+ // until we reach the top of the layer subtree for this layers id.
+ AsyncPanZoomController* scrollParent = nullptr;
+ AsyncPanZoomController* parent = apzc;
+ while (!parent->HasNoParentWithSameLayersId()) {
+ parent = parent->GetParent();
+ // While walking up to find the root of the subtree, if we encounter the
+ // handoff parent, we don't actually need to do the search so we can
+ // just abort here.
+ if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
+ scrollParent = parent;
+ break;
+ }
+ }
+ // If that heuristic didn't turn up the scroll parent, do a full tree search.
+ if (!scrollParent) {
+ ScrollableLayerGuid guid(parent->GetGuid().mLayersId, 0, apzc->GetScrollHandoffParentId());
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+ scrollParent = node ? node->GetApzc() : nullptr;
+ }
+ apzc = scrollParent;
+ }
+
+ // Now adjust the chain to account for scroll grabbing. Sorting is a bit
+ // of an overkill here, but scroll grabbing will likely be generalized
+ // to scroll priorities, so we might as well do it this way.
+ result->SortByScrollPriority();
+
+ // Print the overscroll chain for debugging.
+ for (uint32_t i = 0; i < result->Length(); ++i) {
+ APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
+ }
+
+ return result;
+}
+
+void
+APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
+{
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
+}
+
+RefPtr<HitTestingTreeNode>
+APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics)
+{
+ MutexAutoLock lock(mTreeLock);
+
+ return DepthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [&aDragMetrics](HitTestingTreeNode* aNode) {
+ return aNode->MatchesScrollDragMetrics(aDragMetrics);
+ });
+}
+
+AsyncPanZoomController*
+APZCTreeManager::GetTargetApzcForNode(HitTestingTreeNode* aNode)
+{
+ for (const HitTestingTreeNode* n = aNode;
+ n && n->GetLayersId() == aNode->GetLayersId();
+ n = n->GetParent()) {
+ if (n->GetApzc()) {
+ APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
+ return n->GetApzc();
+ }
+ if (n->GetFixedPosTarget() != FrameMetrics::NULL_SCROLL_ID) {
+ ScrollableLayerGuid guid(n->GetLayersId(), 0, n->GetFixedPosTarget());
+ RefPtr<HitTestingTreeNode> fpNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
+ APZCTM_LOG("Found target node %p using fixed-pos lookup on %" PRIu64 "\n", fpNode.get(), n->GetFixedPosTarget());
+ return fpNode ? fpNode->GetApzc() : nullptr;
+ }
+ }
+ return nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
+ const ParentLayerPoint& aHitTestPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ // This walks the tree in depth-first, reverse order, so that it encounters
+ // APZCs front-to-back on the screen.
+ HitTestingTreeNode* resultNode;
+ HitTestingTreeNode* root = aNode;
+ std::stack<ParentLayerPoint> hitTestPoints;
+ hitTestPoints.push(aHitTestPoint);
+
+ ForEachNode<ReverseIterator>(root,
+ [&hitTestPoints](HitTestingTreeNode* aNode) {
+ if (aNode->IsOutsideClip(hitTestPoints.top())) {
+ // If the point being tested is outside the clip region for this node
+ // then we don't need to test against this node or any of its children.
+ // Just skip it and move on.
+ APZCTM_LOG("Point %f %f outside clip for node %p\n",
+ hitTestPoints.top().x, hitTestPoints.top().y, aNode);
+ return TraversalFlag::Skip;
+ }
+ // First check the subtree rooted at this node, because deeper nodes
+ // are more "in front".
+ Maybe<LayerPoint> hitTestPointForChildLayers = aNode->Untransform(hitTestPoints.top());
+ APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
+ Stringify(hitTestPoints.top()).c_str(),
+ hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil");
+ if (!hitTestPointForChildLayers) {
+ return TraversalFlag::Skip;
+ }
+ hitTestPoints.push(ViewAs<ParentLayerPixel>(hitTestPointForChildLayers.ref(),
+ PixelCastJustification::MovingDownToChildren));
+ return TraversalFlag::Continue;
+ },
+ [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) {
+ hitTestPoints.pop();
+ HitTestResult hitResult = aNode->HitTest(hitTestPoints.top());
+ APZCTM_LOG("Testing ParentLayer point %s against node %p\n",
+ Stringify(hitTestPoints.top()).c_str(), aNode);
+ if (hitResult != HitTestResult::HitNothing) {
+ resultNode = aNode;
+ // If event regions are disabled, *aOutHitResult will be HitLayer
+ *aOutHitResult = hitResult;
+ return TraversalFlag::Abort;
+ }
+ return TraversalFlag::Continue;
+ }
+ );
+
+ if (*aOutHitResult != HitNothing) {
+ MOZ_ASSERT(resultNode);
+ if (aOutHitScrollbar) {
+ for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
+ if (n->IsScrollbarNode()) {
+ *aOutHitScrollbar = true;
+ }
+ }
+ }
+
+ AsyncPanZoomController* result = GetTargetApzcForNode(resultNode);
+ if (!result) {
+ result = FindRootApzcForLayersId(resultNode->GetLayersId());
+ MOZ_ASSERT(result);
+ APZCTM_LOG("Found target %p using root lookup\n", result);
+ }
+ APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
+ result, resultNode, *aOutHitResult);
+ return result;
+ }
+
+ return nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [aLayersId](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc
+ && apzc->GetLayersId() == aLayersId
+ && apzc->IsRootForLayersId();
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootContentApzcForLayersId(uint64_t aLayersId) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [aLayersId](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc
+ && apzc->GetLayersId() == aLayersId
+ && apzc->IsRootContent();
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootContentOrRootApzc() const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ // Note: this should find the RCD node if there is one, or the root APZC if
+ // there is not.
+ // Since BreadthFirstSearch is a pre-order search, we first do a search for
+ // the RCD, and then if we don't find one, we do a search for the root APZC.
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc && apzc->IsRootContent();
+ });
+ if (resultNode) {
+ return resultNode->GetApzc();
+ }
+ resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return (apzc != nullptr);
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+/* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
+ some useful transformations that input events may need applied. This is best
+ illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
+ is the layer that corresponds to the argument |aApzc|, and layer R is the root
+ of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
+ When layer L is displayed to the screen by the compositor, the set of transforms that
+ are applied to L are (in order from top to bottom):
+
+ L's CSS transform (hereafter referred to as transform matrix LC)
+ L's nontransient async transform (hereafter referred to as transform matrix LN)
+ L's transient async transform (hereafter referred to as transform matrix LT)
+ M's CSS transform (hereafter referred to as transform matrix MC)
+ M's nontransient async transform (hereafter referred to as transform matrix MN)
+ M's transient async transform (hereafter referred to as transform matrix MT)
+ ...
+ R's CSS transform (hereafter referred to as transform matrix RC)
+ R's nontransient async transform (hereafter referred to as transform matrix RN)
+ R's transient async transform (hereafter referred to as transform matrix RT)
+
+ Also, for any layer, the async transform is the combination of its transient and non-transient
+ parts. That is, for any layer L:
+ LA === LN * LT
+ LA.Inverse() === LT.Inverse() * LN.Inverse()
+
+ If we want user input to modify L's transient async transform, we have to first convert
+ user input from screen space to the coordinate space of L's transient async transform. Doing
+ this involves applying the following transforms (in order from top to bottom):
+ RT.Inverse()
+ RN.Inverse()
+ RC.Inverse()
+ ...
+ MT.Inverse()
+ MN.Inverse()
+ MC.Inverse()
+ This combined transformation is returned by GetScreenToApzcTransform().
+
+ Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
+ out all of the async transforms that are involved in this chain. This is because async
+ transforms are stored only in the compositor and gecko does not account for them when
+ doing display-list-based hit-testing for event dispatching.
+ Furthermore, because these input events are processed by Gecko in a FIFO queue that
+ includes other things (specifically paint requests), it is possible that by time the
+ input event reaches gecko, it will have painted something else. Therefore, we need to
+ apply another transform to the input events to account for the possible disparity between
+ what we know gecko last painted and the last paint request we sent to gecko. Let this
+ transform be represented by LD, MD, ... RD.
+ Therefore, given a user input in screen space, the following transforms need to be applied
+ (in order from top to bottom):
+ RT.Inverse()
+ RN.Inverse()
+ RC.Inverse()
+ ...
+ MT.Inverse()
+ MN.Inverse()
+ MC.Inverse()
+ LT.Inverse()
+ LN.Inverse()
+ LC.Inverse()
+ LC
+ LD
+ MC
+ MD
+ ...
+ RC
+ RD
+ This sequence can be simplified and refactored to the following:
+ GetScreenToApzcTransform()
+ LA.Inverse()
+ LD
+ MC
+ MD
+ ...
+ RC
+ RD
+ Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform()
+ returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can
+ combine it with GetScreenToApzcTransform() to get the final transform required in this case.
+
+ Note that for many of these layers, there will be no AsyncPanZoomController attached, and
+ so the async transform will be the identity transform. So, in the example above, if layers
+ L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
+ RN and RD will be identity transforms.
+ Additionally, for space-saving purposes, each APZC instance stores its layer's individual
+ CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
+ layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
+ The APZC instances track the last dispatched paint request and so are able to calculate LD and
+ PD using those internally stored values.
+ The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
+ required can be generated.
+ */
+
+/*
+ * See the long comment above for a detailed explanation of this function.
+ */
+ScreenToParentLayerMatrix4x4
+APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const
+{
+ Matrix4x4 result;
+ MutexAutoLock lock(mTreeLock);
+
+ // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
+ // explained in the comment above. This function is called with aApzc at L, and the loop
+ // below performs one iteration, where parent is at P. The comments explain what values are stored
+ // in the variables at these two levels. All the comments use standard matrix notation where the
+ // leftmost matrix in a multiplication is applied first.
+
+ // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
+
+ // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ result = ancestorUntransform;
+
+ for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
+ // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
+ ancestorUntransform = parent->GetAncestorTransform().Inverse();
+ // asyncUntransform is updated to PA.Inverse() when parent == P
+ Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
+ // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
+ Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
+
+ // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ result = untransformSinceLastApzc * result;
+
+ // The above value for result when parent == P matches the required output
+ // as explained in the comment above this method. Note that any missing
+ // terms are guaranteed to be identity transforms.
+ }
+
+ return ViewAs<ScreenToParentLayerMatrix4x4>(result);
+}
+
+/*
+ * See the long comment above GetScreenToApzcTransform() for a detailed
+ * explanation of this function.
+ */
+ParentLayerToScreenMatrix4x4
+APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const
+{
+ Matrix4x4 result;
+ MutexAutoLock lock(mTreeLock);
+
+ // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
+ // explained in the comment above. This function is called with aApzc at L, and the loop
+ // below performs one iteration, where parent is at P. The comments explain what values are stored
+ // in the variables at these two levels. All the comments use standard matrix notation where the
+ // leftmost matrix in a multiplication is applied first.
+
+ // asyncUntransform is LA.Inverse()
+ Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
+
+ // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
+ result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
+
+ for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
+ // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
+ result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
+
+ // The above value for result when parent == P matches the required output
+ // as explained in the comment above this method. Note that any missing
+ // terms are guaranteed to be identity transforms.
+ }
+
+ return ViewAs<ParentLayerToScreenMatrix4x4>(result);
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<AsyncPanZoomController> apzc;
+ // For now, we only ever want to do pinching on the root-content APZC for
+ // a given layers id.
+ if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
+ // If the two APZCs have the same layers id, find the root-content APZC
+ // for that layers id. Don't call CommonAncestor() because there may not
+ // be a common ancestor for the layers id (e.g. if one APZCs is inside a
+ // fixed-position element).
+ apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
+ } else {
+ // Otherwise, find the common ancestor (to reach a common layers id), and
+ // get the root-content APZC for that layers id.
+ apzc = CommonAncestor(aApzc1, aApzc2);
+ if (apzc) {
+ apzc = FindRootContentApzcForLayersId(apzc->GetLayersId());
+ }
+ }
+ return apzc.forget();
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+ RefPtr<AsyncPanZoomController> ancestor;
+
+ // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
+ // will return null.
+
+ // Calculate depth of the APZCs in the tree
+ int depth1 = 0, depth2 = 0;
+ for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
+ depth1++;
+ }
+ for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
+ depth2++;
+ }
+
+ // At most one of the following two loops will be executed; the deeper APZC pointer
+ // will get walked up to the depth of the shallower one.
+ int minDepth = depth1 < depth2 ? depth1 : depth2;
+ while (depth1 > minDepth) {
+ depth1--;
+ aApzc1 = aApzc1->GetParent();
+ }
+ while (depth2 > minDepth) {
+ depth2--;
+ aApzc2 = aApzc2->GetParent();
+ }
+
+ // Walk up the ancestor chains of both APZCs, always staying at the same depth for
+ // either APZC, and return the the first common ancestor encountered.
+ while (true) {
+ if (aApzc1 == aApzc2) {
+ ancestor = aApzc1;
+ break;
+ }
+ if (depth1 <= 0) {
+ break;
+ }
+ aApzc1 = aApzc1->GetParent();
+ aApzc2 = aApzc2->GetParent();
+ }
+ return ancestor.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/APZCTreeManager.h b/system/graphics/layers/apz/src/APZCTreeManager.h
new file mode 100644
index 000000000..c98e292ef
--- /dev/null
+++ b/system/graphics/layers/apz/src/APZCTreeManager.h
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManager_h
+#define mozilla_layers_APZCTreeManager_h
+
+#include <map> // for std::map
+
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/TouchCounter.h"// for TouchCounter
+#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
+#include "mozilla/Mutex.h" // for Mutex
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp
+#include "nsCOMPtr.h" // for already_AddRefed
+
+
+namespace mozilla {
+class MultiTouchInput;
+
+namespace layers {
+
+class Layer;
+class AsyncPanZoomController;
+class APZCTreeManagerParent;
+class CompositorBridgeParent;
+class OverscrollHandoffChain;
+struct OverscrollHandoffState;
+struct FlingHandoffState;
+class LayerMetricsWrapper;
+class InputQueue;
+class GeckoContentController;
+class HitTestingTreeNode;
+
+/**
+ * ****************** NOTE ON LOCK ORDERING IN APZ **************************
+ *
+ * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
+ * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
+ *
+ * To avoid deadlock, we impose a lock ordering between these locks, which is:
+ *
+ * tree lock -> APZC locks
+ *
+ * The interpretation of the lock ordering is that if lock A precedes lock B
+ * in the ordering sequence, then you must NOT wait on A while holding B.
+ *
+ * **************************************************************************
+ */
+
+/**
+ * This class manages the tree of AsyncPanZoomController instances. There is one
+ * instance of this class owned by each CompositorBridgeParent, and it contains as
+ * many AsyncPanZoomController instances as there are scrollable container layers.
+ * This class generally lives on the compositor thread, although some functions
+ * may be called from other threads as noted; thread safety is ensured internally.
+ *
+ * The bulk of the work of this class happens as part of the UpdateHitTestingTree
+ * function, which is when a layer tree update is received by the compositor.
+ * This function walks through the layer tree and creates a tree of
+ * HitTestingTreeNode instances to match the layer tree and for use in
+ * hit-testing on the controller thread. APZC instances may be preserved across
+ * calls to this function if the corresponding layers are still present in the layer
+ * tree.
+ *
+ * The other functions on this class are used by various pieces of client code to
+ * notify the APZC instances of events relevant to them. This includes, for example,
+ * user input events that drive panning and zooming, changes to the scroll viewport
+ * area, and changes to pan/zoom constraints.
+ *
+ * Note that the ClearTree function MUST be called when this class is no longer needed;
+ * see the method documentation for details.
+ *
+ * Behaviour of APZ is controlled by a number of preferences shown \ref APZCPrefs "here".
+ */
+class APZCTreeManager : public IAPZCTreeManager {
+
+ typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
+ typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics;
+
+ // Helper struct to hold some state while we build the hit-testing tree. The
+ // sole purpose of this struct is to shorten the argument list to
+ // UpdateHitTestingTree. All the state that we don't need to
+ // push on the stack during recursion and pop on unwind is stored here.
+ struct TreeBuildingState;
+
+public:
+ APZCTreeManager();
+
+ /**
+ * Initializes the global state used in AsyncPanZoomController.
+ * This is normally called when it is first needed in the constructor
+ * of APZCTreeManager, but can be called manually to force it to be
+ * initialized earlier.
+ */
+ static void InitializeGlobalState();
+
+ /**
+ * Rebuild the hit-testing tree based on the layer update that just came up.
+ * Preserve nodes and APZC instances where possible, but retire those whose
+ * layers are no longer in the layer tree.
+ *
+ * This must be called on the compositor thread as it walks the layer tree.
+ *
+ * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
+ * to this APZCTreeManager
+ * @param aRoot The root of the (full) layer tree
+ * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint
+ * applies.
+ * @param aIsFirstPaint True if the layers update that this is called in response
+ * to included a first-paint. If this is true, the part of
+ * the tree that is affected by the first-paint flag is
+ * indicated by the aFirstPaintLayersId parameter.
+ * @param aPaintSequenceNumber The sequence number of the paint that triggered
+ * this layer update. Note that every layer child
+ * process' layer subtree has its own sequence
+ * numbers.
+ */
+ void UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+ Layer* aRoot,
+ bool aIsFirstPaint,
+ uint64_t aOriginatingLayersId,
+ uint32_t aPaintSequenceNumber);
+
+ /**
+ * Walk the tree of APZCs and flushes the repaint requests for all the APZCS
+ * corresponding to the given layers id. Finally, sends a flush complete
+ * notification to the GeckoContentController for the layers id.
+ */
+ void FlushApzRepaints(uint64_t aLayersId);
+
+ /**
+ * General handler for incoming input events. Manipulates the frame metrics
+ * based on what type of input it is. For example, a PinchGestureEvent will
+ * cause scaling. This should only be called externally to this class, and
+ * must be called on the controller thread.
+ *
+ * This function transforms |aEvent| to have its coordinates in DOM space.
+ * This is so that the event can be passed through the DOM and content can
+ * handle them. The event may need to be converted to a WidgetInputEvent
+ * by the caller if it wants to do this.
+ *
+ * The following values may be returned by this function:
+ * nsEventStatus_eConsumeNoDefault is returned to indicate the
+ * APZ is consuming this event and the caller should discard the event with
+ * extreme prejudice. The exact scenarios under which this is returned is
+ * implementation-dependent and may vary.
+ * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
+ * use this event. This might be because it was directed at a point on
+ * the screen where there was no APZ, or because the thing the user was
+ * trying to do was not allowed. (For example, attempting to pan a
+ * non-pannable document).
+ * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
+ * code may have used this event to do some user-visible thing. Note that
+ * in some cases CONSUMED is returned even if the event was NOT used. This
+ * is because we cannot always know at the time of event delivery whether
+ * the event will be used or not. So we err on the side of sending
+ * CONSUMED when we are uncertain.
+ *
+ * @param aEvent input event object; is modified in-place
+ * @param aOutTargetGuid returns the guid of the apzc this event was
+ * delivered to. May be null.
+ * @param aOutInputBlockId returns the id of the input block that this event
+ * was added to, if that was the case. May be null.
+ */
+ nsEventStatus ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up. |aRect| must be given in CSS pixels, relative to the document.
+ * |aFlags| is a combination of the ZoomToRectBehavior enum values.
+ */
+ void ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ /**
+ * If we have touch listeners, this should always be called when we know
+ * definitively whether or not content has preventDefaulted any touch events
+ * that have come in. If |aPreventDefault| is true, any touch events in the
+ * queue will be discarded. This function must be called on the controller
+ * thread.
+ */
+ void ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ /**
+ * When the event regions code is enabled, this function should be invoked to
+ * to confirm the target of the input block. This is only needed in cases
+ * where the initial input event of the block hit a dispatch-to-content region
+ * but is safe to call for all input blocks. This function should always be
+ * invoked on the controller thread.
+ * The different elements in the array of targets correspond to the targets
+ * for the different touch points. In the case where the touch point has no
+ * target, or the target is not a scrollable frame, the target's |mScrollId|
+ * should be set to FrameMetrics::NULL_SCROLL_ID.
+ */
+ void SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ /**
+ * Helper function for SetTargetAPZC when used with single-target events,
+ * such as mouse wheel events.
+ */
+ void SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget);
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ * If the |aConstraints| is Nothing() then previously-provided constraints for
+ * the given |aGuid| are cleared.
+ */
+ void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ /**
+ * Cancels any currently running animation. Note that all this does is set the
+ * state of the AsyncPanZoomController back to NOTHING, but it is the
+ * animation's responsibility to check this before advancing.
+ */
+ void CancelAnimation(const ScrollableLayerGuid &aGuid) override;
+
+ /**
+ * Adjusts the root APZC to compensate for a shift in the surface. See the
+ * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
+ * some more details. This is only currently needed due to surface shifts
+ * caused by the dynamic toolbar on Android.
+ */
+ void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ /**
+ * Calls Destroy() on all APZC instances attached to the tree, and resets the
+ * tree back to empty. This function must be called exactly once during the
+ * lifetime of this APZCTreeManager, when this APZCTreeManager is no longer
+ * needed. Failing to call this function may prevent objects from being freed
+ * properly.
+ */
+ void ClearTree();
+
+ /**
+ * Tests if a screen point intersect an apz in the tree.
+ */
+ bool HitTestAPZC(const ScreenIntPoint& aPoint);
+
+ /**
+ * See AsyncPanZoomController::CalculatePendingDisplayPort. This
+ * function simply delegates to that one, so that non-layers code
+ * never needs to include AsyncPanZoomController.h
+ */
+ static const ScreenMargin CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ /**
+ * Sets the dpi value used by all AsyncPanZoomControllers.
+ * DPI defaults to 72 if not set using SetDPI() at any point.
+ */
+ void SetDPI(float aDpiValue) override { sDPI = aDpiValue; }
+
+ /**
+ * Returns the current dpi value in use.
+ */
+ static float GetDPI() { return sDPI; }
+
+ /**
+ * Find the hit testing node for the scrollbar thumb that matches these
+ * drag metrics.
+ */
+ RefPtr<HitTestingTreeNode> FindScrollNode(const AsyncDragMetrics& aDragMetrics);
+
+ /**
+ * Sets allowed touch behavior values for current touch-session for specific
+ * input block (determined by aInputBlock).
+ * Should be invoked by the widget. Each value of the aValues arrays
+ * corresponds to the different touch point that is currently active.
+ * Must be called after receiving the TOUCH_START event that starts the
+ * touch-session.
+ * This must be called on the controller thread.
+ */
+ void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ /**
+ * This is a callback for AsyncPanZoomController to call when it wants to
+ * scroll in response to a touch-move event, or when it needs to hand off
+ * overscroll to the next APZC. Note that because of scroll grabbing, the
+ * first APZC to scroll may not be the one that is receiving the touch events.
+ *
+ * |aAPZC| is the APZC that received the touch events triggering the scroll
+ * (in the case of an initial scroll), or the last APZC to scroll (in the
+ * case of overscroll)
+ * |aStartPoint| and |aEndPoint| are in |aAPZC|'s transformed screen
+ * coordinates (i.e. the same coordinates in which touch points are given to
+ * APZCs). The amount of (over)scroll is represented by two points rather
+ * than a displacement because with certain 3D transforms, the same
+ * displacement between different points in transformed coordinates can
+ * represent different displacements in untransformed coordinates.
+ * |aOverscrollHandoffChain| is the overscroll handoff chain used for
+ * determining the order in which scroll should be handed off between
+ * APZCs
+ * |aOverscrollHandoffChainIndex| is the next position in the overscroll
+ * handoff chain that should be scrolled.
+ *
+ * aStartPoint and aEndPoint will be modified depending on how much of the
+ * scroll each APZC consumes. This is to allow the sending APZC to go into
+ * an overscrolled state if no APZC further up in the handoff chain accepted
+ * the entire scroll.
+ *
+ * The way this method works is best illustrated with an example.
+ * Consider three nested APZCs, A, B, and C, with C being the innermost one.
+ * Say B is scroll-grabbing.
+ * The touch events go to C because it's the innermost one (so e.g. taps
+ * should go through C), but the overscroll handoff chain is B -> C -> A
+ * because B is scroll-grabbing.
+ * For convenience I'll refer to the three APZC objects as A, B, and C, and
+ * to the tree manager object as TM.
+ * Here's what happens when C receives a touch-move event:
+ * - C.TrackTouch() calls TM.DispatchScroll() with index = 0.
+ * - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain).
+ * - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1.
+ * - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain)
+ * - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2.
+ * - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain)
+ * - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3.
+ * - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain.
+ *
+ * Note: this should be used for panning only. For handing off overscroll for
+ * a fling, use DispatchFling().
+ */
+ void DispatchScroll(AsyncPanZoomController* aApzc,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ /**
+ * This is a callback for AsyncPanZoomController to call when it wants to
+ * start a fling in response to a touch-end event, or when it needs to hand
+ * off a fling to the next APZC. Note that because of scroll grabbing, the
+ * first APZC to fling may not be the one that is receiving the touch events.
+ *
+ * @param aApzc the APZC that wants to start or hand off the fling
+ * @param aHandoffState a collection of state about the operation,
+ * which contains the following:
+ *
+ * mVelocity the current velocity of the fling, in |aApzc|'s screen
+ * pixels per millisecond
+ * mChain the chain of APZCs along which the fling
+ * should be handed off
+ * mIsHandoff is true if |aApzc| is handing off an existing fling (in
+ * this case the fling is given to the next APZC in the
+ * handoff chain after |aApzc|), and false is |aApzc| wants
+ * start a fling (in this case the fling is given to the
+ * first APZC in the chain)
+ *
+ * aHandoffState.mVelocity will be modified depending on how much of that
+ * velocity has been consumed by APZCs in the overscroll hand-off chain.
+ * The caller can use this value to determine whether it should consume
+ * the excess velocity by going into an overscroll fling.
+ */
+ void DispatchFling(AsyncPanZoomController* aApzc,
+ FlingHandoffState& aHandoffState);
+
+ void StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ /*
+ * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|.
+ */
+ RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget);
+
+ /**
+ * Function used to disable LongTap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ void SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
+
+ void TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) override;
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~APZCTreeManager();
+
+ // Protected hooks for gtests subclass
+ virtual AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController);
+public:
+ // Public hooks for gtests subclass
+ virtual TimeStamp GetFrameTime();
+
+public:
+ /* Some helper functions to find an APZC given some identifying input. These functions
+ lock the tree of APZCs while they find the right one, and then return an addref'd
+ pointer to it. This allows caller code to just use the target APZC without worrying
+ about it going away. These are public for testing code and generally should not be
+ used by other production code.
+ */
+ RefPtr<HitTestingTreeNode> GetRootNode() const;
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar = nullptr);
+ ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
+ ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
+
+ /**
+ * Process touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
+private:
+ typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
+
+ /* Helpers */
+ void AttachNodeToTree(HitTestingTreeNode* aNode,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling);
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
+ already_AddRefed<HitTestingTreeNode> GetTargetNode(const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator);
+ HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode,
+ const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator);
+ AsyncPanZoomController* GetTargetApzcForNode(HitTestingTreeNode* aNode);
+ AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode,
+ const ParentLayerPoint& aHitTestPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar);
+ AsyncPanZoomController* FindRootApzcForLayersId(uint64_t aLayersId) const;
+ AsyncPanZoomController* FindRootContentApzcForLayersId(uint64_t aLayersId) const;
+ AsyncPanZoomController* FindRootContentOrRootApzc() const;
+ already_AddRefed<AsyncPanZoomController> GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
+ already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
+ already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
+ nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
+ HitTestResult* aOutHitResult);
+ nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId);
+ void FlushRepaintsToClearScreenToGeckoTransform();
+
+ already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState,
+ AsyncPanZoomController* aApzc,
+ uint64_t aLayersId);
+ HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+ const FrameMetrics& aMetrics,
+ uint64_t aLayersId,
+ const gfx::Matrix4x4& aAncestorTransform,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling,
+ TreeBuildingState& aState);
+
+ void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+ const AsyncPanZoomController* apzc);
+
+protected:
+ /* The input queue where input events are held until we know enough to
+ * figure out where they're going. Protected so gtests can access it.
+ */
+ RefPtr<InputQueue> mInputQueue;
+
+private:
+ /* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
+ * This lock does not need to be held while manipulating a single APZC instance in
+ * isolation (that is, if its tree pointers are not being accessed or mutated). The
+ * lock also needs to be held when accessing the mRootNode instance variable, as that
+ * is considered part of the APZC tree management state.
+ * Finally, the lock needs to be held when accessing mZoomConstraints.
+ * IMPORTANT: See the note about lock ordering at the top of this file. */
+ mutable mozilla::Mutex mTreeLock;
+ RefPtr<HitTestingTreeNode> mRootNode;
+ /* Holds the zoom constraints for scrollable layers, as determined by the
+ * the main-thread gecko code. */
+ std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints;
+ /* This tracks the APZC that should receive all inputs for the current input event block.
+ * This allows touch points to move outside the thing they started on, but still have the
+ * touch events delivered to the same initial APZC. This will only ever be touched on the
+ * input delivery thread, and so does not require locking.
+ */
+ RefPtr<AsyncPanZoomController> mApzcForInputBlock;
+ /* The hit result for the current input event block; this should always be in
+ * sync with mApzcForInputBlock.
+ */
+ HitTestResult mHitResultForInputBlock;
+ /* Sometimes we want to ignore all touches except one. In such cases, this
+ * is set to the identifier of the touch we are not ignoring; in other cases,
+ * this is set to -1.
+ */
+ int32_t mRetainedTouchIdentifier;
+ /* Tracks the number of touch points we are tracking that are currently on
+ * the screen. */
+ TouchCounter mTouchCounter;
+ /* For logging the APZC tree for debugging (enabled by the apz.printtree
+ * pref). */
+ gfx::TreeLog mApzcTreeLog;
+
+ class CheckerboardFlushObserver;
+ friend class CheckerboardFlushObserver;
+ RefPtr<CheckerboardFlushObserver> mFlushObserver;
+
+ static float sDPI;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PanZoomController_h
diff --git a/system/graphics/layers/apz/src/APZUtils.h b/system/graphics/layers/apz/src/APZUtils.h
new file mode 100644
index 000000000..5acbfd70f
--- /dev/null
+++ b/system/graphics/layers/apz/src/APZUtils.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZUtils_h
+#define mozilla_layers_APZUtils_h
+
+#include <stdint.h> // for uint32_t
+#include "LayersTypes.h"
+#include "UnitTransforms.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/FloatingPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+enum HitTestResult {
+ HitNothing,
+ HitLayer,
+ HitLayerTouchActionNone,
+ HitLayerTouchActionPanX,
+ HitLayerTouchActionPanY,
+ HitLayerTouchActionPanXY,
+ HitDispatchToContentRegion,
+};
+
+enum CancelAnimationFlags : uint32_t {
+ Default = 0x0, /* Cancel all animations */
+ ExcludeOverscroll = 0x1, /* Don't clear overscroll */
+ ScrollSnap = 0x2 /* Snap to snap points */
+};
+
+inline CancelAnimationFlags
+operator|(CancelAnimationFlags a, CancelAnimationFlags b)
+{
+ return static_cast<CancelAnimationFlags>(static_cast<int>(a)
+ | static_cast<int>(b));
+}
+
+enum class ScrollSource {
+ // scrollTo() or something similar.
+ DOM,
+
+ // Touch-screen or trackpad with gesture support.
+ Touch,
+
+ // Mouse wheel.
+ Wheel
+};
+
+typedef uint32_t TouchBehaviorFlags;
+
+// Epsilon to be used when comparing 'float' coordinate values
+// with FuzzyEqualsAdditive. The rationale is that 'float' has 7 decimal
+// digits of precision, and coordinate values should be no larger than in the
+// ten thousands. Note also that the smallest legitimate difference in page
+// coordinates is 1 app unit, which is 1/60 of a (CSS pixel), so this epsilon
+// isn't too large.
+const float COORDINATE_EPSILON = 0.01f;
+
+template <typename Units>
+static bool IsZero(const gfx::PointTyped<Units>& aPoint)
+{
+ return FuzzyEqualsAdditive(aPoint.x, 0.0f, COORDINATE_EPSILON)
+ && FuzzyEqualsAdditive(aPoint.y, 0.0f, COORDINATE_EPSILON);
+}
+
+// Deem an AsyncTransformComponentMatrix (obtained by multiplying together
+// one or more AsyncTransformComponentMatrix objects) as constituting a
+// complete async transform.
+inline AsyncTransformMatrix
+CompleteAsyncTransform(const AsyncTransformComponentMatrix& aMatrix)
+{
+ return ViewAs<AsyncTransformMatrix>(aMatrix,
+ PixelCastJustification::MultipleAsyncTransforms);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZUtils_h
diff --git a/system/graphics/layers/apz/src/AsyncDragMetrics.h b/system/graphics/layers/apz/src/AsyncDragMetrics.h
new file mode 100644
index 000000000..a27edca2f
--- /dev/null
+++ b/system/graphics/layers/apz/src/AsyncDragMetrics.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DragMetrics_h
+#define mozilla_layers_DragMetrics_h
+
+#include "FrameMetrics.h"
+
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+
+namespace layers {
+
+class AsyncDragMetrics {
+ friend struct IPC::ParamTraits<mozilla::layers::AsyncDragMetrics>;
+
+public:
+ enum DragDirection {
+ NONE,
+ VERTICAL,
+ HORIZONTAL,
+ SENTINEL,
+ };
+
+ // IPC constructor
+ AsyncDragMetrics()
+ : mViewId(0)
+ , mPresShellId(0)
+ , mDragStartSequenceNumber(0)
+ , mScrollbarDragOffset(0)
+ , mDirection(NONE)
+ {}
+
+ AsyncDragMetrics(const FrameMetrics::ViewID& aViewId,
+ uint32_t aPresShellId,
+ uint64_t aDragStartSequenceNumber,
+ CSSIntCoord aScrollbarDragOffset,
+ const CSSIntRect& aScrollTrack,
+ DragDirection aDirection)
+ : mViewId(aViewId)
+ , mPresShellId(aPresShellId)
+ , mDragStartSequenceNumber(aDragStartSequenceNumber)
+ , mScrollbarDragOffset(aScrollbarDragOffset)
+ , mScrollTrack(aScrollTrack)
+ , mDirection(aDirection)
+ {}
+
+ FrameMetrics::ViewID mViewId;
+ uint32_t mPresShellId;
+ uint64_t mDragStartSequenceNumber;
+ CSSIntCoord mScrollbarDragOffset;
+ CSSIntRect mScrollTrack;
+ DragDirection mDirection;
+};
+
+}
+}
+
+#endif
diff --git a/system/graphics/layers/apz/src/AsyncPanZoomAnimation.h b/system/graphics/layers/apz/src/AsyncPanZoomAnimation.h
new file mode 100644
index 000000000..04b730653
--- /dev/null
+++ b/system/graphics/layers/apz/src/AsyncPanZoomAnimation.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AsyncPanZoomAnimation_h_
+#define mozilla_layers_AsyncPanZoomAnimation_h_
+
+#include "base/message_loop.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "FrameMetrics.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+class WheelScrollAnimation;
+class SmoothScrollAnimation;
+
+class AsyncPanZoomAnimation {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
+
+public:
+ explicit AsyncPanZoomAnimation()
+ { }
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) = 0;
+
+ bool Sample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) {
+ // In some situations, particularly when handoff is involved, it's possible
+ // for |aDelta| to be negative on the first call to sample. Ignore such a
+ // sample here, to avoid each derived class having to deal with this case.
+ if (aDelta.ToMilliseconds() <= 0) {
+ return true;
+ }
+
+ return DoSample(aFrameMetrics, aDelta);
+ }
+
+ /**
+ * Get the deferred tasks in |mDeferredTasks| and place them in |aTasks|. See
+ * |mDeferredTasks| for more information. Clears |mDeferredTasks|.
+ */
+ nsTArray<RefPtr<Runnable>> TakeDeferredTasks() {
+ return Move(mDeferredTasks);
+ }
+
+ virtual WheelScrollAnimation* AsWheelScrollAnimation() {
+ return nullptr;
+ }
+ virtual SmoothScrollAnimation* AsSmoothScrollAnimation() {
+ return nullptr;
+ }
+
+ virtual bool WantsRepaints() {
+ return true;
+ }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~AsyncPanZoomAnimation()
+ { }
+
+ /**
+ * Tasks scheduled for execution after the APZC's mMonitor is released.
+ * Derived classes can add tasks here in Sample(), and the APZC can call
+ * ExecuteDeferredTasks() to execute them.
+ */
+ nsTArray<RefPtr<Runnable>> mDeferredTasks;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AsyncPanZoomAnimation_h_
diff --git a/system/graphics/layers/apz/src/AsyncPanZoomController.cpp b/system/graphics/layers/apz/src/AsyncPanZoomController.cpp
new file mode 100644
index 000000000..4808daf49
--- /dev/null
+++ b/system/graphics/layers/apz/src/AsyncPanZoomController.cpp
@@ -0,0 +1,3986 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h> // for fabsf, fabs, atan2
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include <algorithm> // for max, min
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc
+#include "Axis.h" // for AxisX, AxisY, Axis, etc
+#include "CheckerboardEvent.h" // for CheckerboardEvent
+#include "Compositor.h" // for Compositor
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "GenericFlingAnimation.h" // for GenericFlingAnimation
+#include "GestureEventListener.h" // for GestureEventListener
+#include "HitTestingTreeNode.h" // for HitTestingTreeNode
+#include "InputData.h" // for MultiTouchInput, etc
+#include "InputBlockState.h" // for InputBlockState, TouchBlockState
+#include "InputQueue.h" // for InputQueue
+#include "Overscroll.h" // for OverscrollAnimation
+#include "OverscrollHandoffState.h" // for OverscrollHandoffState
+#include "Units.h" // for CSSRect, CSSPoint, etc
+#include "UnitTransforms.h" // for TransformTo
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxTypes.h" // for gfxFloat
+#include "LayersLogging.h" // for print_stderr
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_*
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/EventForwards.h" // for nsEventStatus_*
+#include "mozilla/EventStateManager.h"
+#include "mozilla/MouseEvents.h" // for WidgetWheelEvent
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
+#include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
+ // note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
+#include "mozilla/dom/Touch.h" // for Touch
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Coord.h"
+#include "mozilla/gfx/Point.h" // for Point, RoundedToInt, etc
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
+#include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
+#include "mozilla/layers/CompositorController.h" // for CompositorController
+#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
+#include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
+#include "mozilla/layers/ScrollInputMethods.h" // for ScrollInputMethod
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Unused.h" // for unused
+#include "mozilla/FloatingPoint.h" // for FuzzyEquals*
+#include "nsAlgorithm.h" // for clamped
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsIDOMWindowUtils.h" // for nsIDOMWindowUtils
+#include "nsMathUtils.h" // for NS_hypot
+#include "nsPoint.h" // for nsIntPoint
+#include "nsStyleConsts.h"
+#include "nsStyleStruct.h" // for nsTimingFunction
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsViewportInfo.h" // for kViewportMinScale, kViewportMaxScale
+#include "prsystem.h" // for PR_GetPhysicalMemorySize
+#include "SharedMemoryBasic.h" // for SharedMemoryBasic
+#include "ScrollSnap.h" // for ScrollSnapUtils
+#include "WheelScrollAnimation.h"
+
+#define ENABLE_APZC_LOGGING 0
+// #define ENABLE_APZC_LOGGING 1
+
+#if ENABLE_APZC_LOGGING
+# define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
+# define APZC_LOG_FM(fm, prefix, ...) \
+ { std::stringstream ss; \
+ ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
+ AppendToString(ss, fm, ":", "", true); \
+ APZC_LOG("%s\n", ss.str().c_str()); \
+ }
+#else
+# define APZC_LOG(...)
+# define APZC_LOG_FM(fm, prefix, ...)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
+typedef GeckoContentController::APZStateChange APZStateChange;
+typedef GeckoContentController::TapType TapType;
+typedef mozilla::gfx::Point Point;
+typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+using mozilla::gfx::CoordTyped;
+using mozilla::gfx::IntCoordTyped;
+using mozilla::gfx::IntRectTyped;
+using mozilla::gfx::PointTyped;
+using mozilla::gfx::RectTyped;
+using mozilla::gfx::ScaleFactors2D;
+
+// Platform-specific implementations.
+typedef GenericOverscrollEffect OverscrollEffect;
+typedef PlatformSpecificStateBase PlatformSpecificState; // no extra state, just use the base class
+typedef GenericFlingAnimation FlingAnimation;
+
+/**
+ * \page APZCPrefs APZ preferences
+ *
+ * The following prefs are used to control the behaviour of the APZC.
+ * The default values are provided in gfxPrefs.h.
+ *
+ * \li\b apz.allow_checkerboarding
+ * Pref that allows or disallows checkerboarding
+ *
+ * \li\b apz.allow_immediate_handoff
+ * If set to true, scroll can be handed off from one APZC to another within
+ * a single input block. If set to false, a single input block can only
+ * scroll one APZC.
+ *
+ * \li\b apz.axis_lock.mode
+ * The preferred axis locking style. See AxisLockMode for possible values.
+ *
+ * \li\b apz.axis_lock.lock_angle
+ * Angle from axis within which we stay axis-locked.\n
+ * Units: radians
+ *
+ * \li\b apz.axis_lock.breakout_threshold
+ * Distance in inches the user must pan before axis lock can be broken.\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.axis_lock.breakout_angle
+ * Angle at which axis lock can be broken.\n
+ * Units: radians
+ *
+ * \li\b apz.axis_lock.direct_pan_angle
+ * If the angle from an axis to the line drawn by a pan move is less than
+ * this value, we can assume that panning can be done in the allowed direction
+ * (horizontal or vertical).\n
+ * Currently used only for touch-action css property stuff and was addded to
+ * keep behaviour consistent with IE.\n
+ * Units: radians
+ *
+ * \li\b apz.content_response_timeout
+ * Amount of time before we timeout response from content. For example, if
+ * content is being unruly/slow and we don't get a response back within this
+ * time, we will just pretend that content did not preventDefault any touch
+ * events we dispatched to it.\n
+ * Units: milliseconds
+ *
+ * \li\b apz.danger_zone_x
+ * \li\b apz.danger_zone_y
+ * When drawing high-res tiles, we drop down to drawing low-res tiles
+ * when we know we can't keep up with the scrolling. The way we determine
+ * this is by checking if we are entering the "danger zone", which is the
+ * boundary of the painted content. For example, if the painted content
+ * goes from y=0...1000 and the visible portion is y=250...750 then
+ * we're far from checkerboarding. If we get to y=490...990 though then we're
+ * only 10 pixels away from showing checkerboarding so we are probably in
+ * a state where we can't keep up with scrolling. The danger zone prefs specify
+ * how wide this margin is; in the above example a y-axis danger zone of 10
+ * pixels would make us drop to low-res at y=490...990.\n
+ * This value is in layer pixels.
+ *
+ * \li\b apz.disable_for_scroll_linked_effects
+ * Setting this pref to true will disable APZ scrolling on documents where
+ * scroll-linked effects are detected. A scroll linked effect is detected if
+ * positioning or transform properties are updated inside a scroll event
+ * dispatch; we assume that such an update is in response to the scroll event
+ * and is therefore a scroll-linked effect which will be laggy with APZ
+ * scrolling.
+ *
+ * \li\b apz.displayport_expiry_ms
+ * While a scrollable frame is scrolling async, we set a displayport on it
+ * to make sure it is layerized. However this takes up memory, so once the
+ * scrolling stops we want to remove the displayport. This pref controls how
+ * long after scrolling stops the displayport is removed. A value of 0 will
+ * disable the expiry behavior entirely.
+ * Units: milliseconds
+ *
+ * \li\b apz.enlarge_displayport_when_clipped
+ * Pref that enables enlarging of the displayport along one axis when the
+ * generated displayport's size is beyond that of the scrollable rect on the
+ * opposite axis.
+ *
+ * \li\b apz.fling_accel_interval_ms
+ * The time that determines whether a second fling will be treated as
+ * accelerated. If two flings are started within this interval, the second one
+ * will be accelerated. Setting an interval of 0 means that acceleration will
+ * be disabled.\n
+ * Units: milliseconds
+ *
+ * \li\b apz.fling_accel_min_velocity
+ * The minimum velocity of the second fling for it to be considered for fling
+ * acceleration.
+ * Units: screen pixels per milliseconds
+ *
+ * \li\b apz.fling_accel_base_mult
+ * \li\b apz.fling_accel_supplemental_mult
+ * When applying an acceleration on a fling, the new computed velocity is
+ * (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
+ * The base_mult and supplemental_mult multiplier values are controlled by
+ * these prefs. Note that "old_velocity" here is the initial velocity of the
+ * previous fling _after_ acceleration was applied to it (if applicable).
+ *
+ * \li\b apz.fling_curve_function_x1
+ * \li\b apz.fling_curve_function_y1
+ * \li\b apz.fling_curve_function_x2
+ * \li\b apz.fling_curve_function_y2
+ * \li\b apz.fling_curve_threshold_inches_per_ms
+ * These five parameters define a Bezier curve function and threshold used to
+ * increase the actual velocity relative to the user's finger velocity. When the
+ * finger velocity is below the threshold (or if the threshold is not positive),
+ * the velocity is used as-is. If the finger velocity exceeds the threshold
+ * velocity, then the function defined by the curve is applied on the part of
+ * the velocity that exceeds the threshold. Note that the upper bound of the
+ * velocity is still specified by the \b apz.max_velocity_inches_per_ms pref, and
+ * the function will smoothly curve the velocity from the threshold to the
+ * max. In general the function parameters chosen should define an ease-out
+ * curve in order to increase the velocity in this range, or an ease-in curve to
+ * decrease the velocity. A straight-line curve is equivalent to disabling the
+ * curve entirely by setting the threshold to -1. The max velocity pref must
+ * also be set in order for the curving to take effect, as it defines the upper
+ * bound of the velocity curve.\n
+ * The points (x1, y1) and (x2, y2) used as the two intermediate control points
+ * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
+ * Some example values for these prefs can be found at\n
+ * https://dxr.mozilla.org/mozilla-central/rev/70e05c6832e831374604ac3ce7433971368dffe0/layout/style/nsStyleStruct.cpp#2729
+ *
+ * \li\b apz.fling_friction
+ * Amount of friction applied during flings. This is used in the following
+ * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
+ * for a new sample, v(t0) is the velocity at the previous sample, f is the
+ * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
+ * that has elapsed between the two samples.\n
+ *
+ * \li\b apz.fling_min_velocity_threshold
+ * Minimum velocity for a fling to actually kick off. If the user pans and lifts
+ * their finger such that the velocity is smaller than this amount, no fling
+ * is initiated.\n
+ * Units: screen pixels per millisecond
+ *
+ * \li\b apz.fling_stop_on_tap_threshold
+ * When flinging, if the velocity is above this number, then a tap on the
+ * screen will stop the fling without dispatching a tap to content. If the
+ * velocity is below this threshold a tap will also be dispatched.
+ * Note: when modifying this pref be sure to run the APZC gtests as some of
+ * them depend on the value of this pref.\n
+ * Units: screen pixels per millisecond
+ *
+ * \li\b apz.fling_stopped_threshold
+ * When flinging, if the velocity goes below this number, we just stop the
+ * animation completely. This is to prevent asymptotically approaching 0
+ * velocity and rerendering unnecessarily.\n
+ * Units: screen pixels per millisecond.\n
+ *
+ * \li\b apz.max_velocity_inches_per_ms
+ * Maximum velocity. Velocity will be capped at this value if a faster fling
+ * occurs. Negative values indicate unlimited velocity.\n
+ * Units: (real-world, i.e. screen) inches per millisecond
+ *
+ * \li\b apz.max_velocity_queue_size
+ * Maximum size of velocity queue. The queue contains last N velocity records.
+ * On touch end we calculate the average velocity in order to compensate
+ * touch/mouse drivers misbehaviour.
+ *
+ * \li\b apz.min_skate_speed
+ * Minimum amount of speed along an axis before we switch to "skate" multipliers
+ * rather than using the "stationary" multipliers.\n
+ * Units: CSS pixels per millisecond
+ *
+ * \li\b apz.overscroll.enabled
+ * Pref that enables overscrolling. If this is disabled, excess scroll that
+ * cannot be handed off is discarded.
+ *
+ * \li\b apz.overscroll.min_pan_distance_ratio
+ * The minimum ratio of the pan distance along one axis to the pan distance
+ * along the other axis needed to initiate overscroll along the first axis
+ * during panning.
+ *
+ * \li\b apz.overscroll.stretch_factor
+ * How much overscrolling can stretch content along an axis.
+ * The maximum stretch along an axis is a factor of (1 + kStretchFactor).
+ * (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
+ * is 1, you can stretch at most by a factor of 2).
+ *
+ * \li\b apz.overscroll.spring_stiffness
+ * The stiffness of the spring used in the physics model for the overscroll
+ * animation.
+ *
+ * \li\b apz.overscroll.spring_friction
+ * The friction of the spring used in the physics model for the overscroll
+ * animation.
+ * Even though a realistic physics model would dictate that this be the same
+ * as \b apz.fling_friction, we allow it to be set to be something different,
+ * because in practice we want flings to skate smoothly (low friction), while
+ * we want the overscroll bounce-back to oscillate few times (high friction).
+ *
+ * \li\b apz.overscroll.stop_distance_threshold
+ * \li\b apz.overscroll.stop_velocity_threshold
+ * Thresholds for stopping the overscroll animation. When both the distance
+ * and the velocity fall below their thresholds, we stop oscillating.\n
+ * Units: screen pixels (for distance)
+ * screen pixels per millisecond (for velocity)
+ *
+ * \li\b apz.paint_skipping.enabled
+ * When APZ is scrolling and sending repaint requests to the main thread, often
+ * the main thread doesn't actually need to do a repaint. This pref allows the
+ * main thread to skip doing those repaints in cases where it doesn't need to.
+ *
+ * \li\b apz.record_checkerboarding
+ * Whether or not to record detailed info on checkerboarding events.
+ *
+ * \li\b apz.test.logging_enabled
+ * Enable logging of APZ test data (see bug 961289).
+ *
+ * \li\b apz.touch_move_tolerance
+ * See the description for apz.touch_start_tolerance below. This is a similar
+ * threshold, except it is used to suppress touchmove events from being delivered
+ * to content for NON-scrollable frames (or more precisely, for APZCs where
+ * ArePointerEventsConsumable returns false).\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.touch_start_tolerance
+ * Constant describing the tolerance in distance we use, multiplied by the
+ * device DPI, before we start panning the screen. This is to prevent us from
+ * accidentally processing taps as touch moves, and from very short/accidental
+ * touches moving the screen. touchmove events are also not delivered to content
+ * within this distance on scrollable frames.\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.velocity_bias
+ * How much to adjust the displayport in the direction of scrolling. This value
+ * is multiplied by the velocity and added to the displayport offset.
+ *
+ * \li\b apz.velocity_relevance_time_ms
+ * When computing a fling velocity from the most recently stored velocity
+ * information, only velocities within the most X milliseconds are used.
+ * This pref controls the value of X.\n
+ * Units: ms
+ *
+ * \li\b apz.x_skate_size_multiplier
+ * \li\b apz.y_skate_size_multiplier
+ * The multiplier we apply to the displayport size if it is skating (current
+ * velocity is above \b apz.min_skate_speed). We prefer to increase the size of
+ * the Y axis because it is more natural in the case that a user is reading a
+ * page page that scrolls up/down. Note that one, both or neither of these may be
+ * used at any instant.\n
+ * In general we want \b apz.[xy]_skate_size_multiplier to be smaller than the corresponding
+ * stationary size multiplier because when panning fast we would like to paint
+ * less and get faster, more predictable paint times. When panning slowly we
+ * can afford to paint more even though it's slower.
+ *
+ * \li\b apz.x_stationary_size_multiplier
+ * \li\b apz.y_stationary_size_multiplier
+ * The multiplier we apply to the displayport size if it is not skating (see
+ * documentation for the skate size multipliers above).
+ *
+ * \li\b apz.x_skate_highmem_adjust
+ * \li\b apz.y_skate_highmem_adjust
+ * On high memory systems, we adjust the displayport during skating
+ * to be larger so we can reduce checkerboarding.
+ *
+ * \li\b apz.zoom_animation_duration_ms
+ * This controls how long the zoom-to-rect animation takes.\n
+ * Units: ms
+ *
+ * \li\b apz.scale_repaint_delay_ms
+ * How long to delay between repaint requests during a scale.
+ * A negative number prevents repaint requests during a scale.\n
+ * Units: ms
+ *
+ */
+
+/**
+ * Computed time function used for sampling frames of a zoom to animation.
+ */
+StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
+
+/**
+ * Computed time function used for curving up velocity when it gets high.
+ */
+StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
+
+/**
+ * The estimated duration of a paint for the purposes of calculating a new
+ * displayport, in milliseconds.
+ */
+static const double kDefaultEstimatedPaintDurationMs = 50;
+
+/**
+ * Returns true if this is a high memory system and we can use
+ * extra memory for a larger displayport to reduce checkerboarding.
+ */
+static bool gIsHighMemSystem = false;
+static bool IsHighMemSystem()
+{
+ return gIsHighMemSystem;
+}
+
+/**
+ * Is aAngle within the given threshold of the horizontal axis?
+ * @param aAngle an angle in radians in the range [0, pi]
+ * @param aThreshold an angle in radians in the range [0, pi/2]
+ */
+static bool IsCloseToHorizontal(float aAngle, float aThreshold)
+{
+ return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
+}
+
+// As above, but for the vertical axis.
+static bool IsCloseToVertical(float aAngle, float aThreshold)
+{
+ return (fabs(aAngle - (M_PI / 2)) < aThreshold);
+}
+
+// Counter used to give each APZC a unique id
+static uint32_t sAsyncPanZoomControllerCount = 0;
+
+TimeStamp
+AsyncPanZoomController::GetFrameTime() const
+{
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
+}
+
+class MOZ_STACK_CLASS StateChangeNotificationBlocker {
+public:
+ explicit StateChangeNotificationBlocker(AsyncPanZoomController* aApzc)
+ : mApzc(aApzc)
+ {
+ ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
+ mInitialState = mApzc->mState;
+ mApzc->mNotificationBlockers++;
+ }
+
+ ~StateChangeNotificationBlocker()
+ {
+ AsyncPanZoomController::PanZoomState newState;
+ {
+ ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
+ mApzc->mNotificationBlockers--;
+ newState = mApzc->mState;
+ }
+ mApzc->DispatchStateChangeNotification(mInitialState, newState);
+ }
+
+private:
+ AsyncPanZoomController* mApzc;
+ AsyncPanZoomController::PanZoomState mInitialState;
+};
+
+class ZoomAnimation: public AsyncPanZoomAnimation {
+public:
+ ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale2D aStartZoom,
+ CSSPoint aEndOffset, CSSToParentLayerScale2D aEndZoom)
+ : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
+ , mStartOffset(aStartOffset)
+ , mStartZoom(aStartZoom)
+ , mEndOffset(aEndOffset)
+ , mEndZoom(aEndZoom)
+ {}
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ mDuration += aDelta;
+ double animPosition = mDuration / mTotalDuration;
+
+ if (animPosition >= 1.0) {
+ aFrameMetrics.SetZoom(mEndZoom);
+ aFrameMetrics.SetScrollOffset(mEndOffset);
+ return false;
+ }
+
+ // Sample the zoom at the current time point. The sampled zoom
+ // will affect the final computed resolution.
+ float sampledPosition =
+ gZoomAnimationFunction->GetValue(animPosition,
+ ComputedTimingFunction::BeforeFlag::Unset);
+
+ // We scale the scrollOffset linearly with sampledPosition, so the zoom
+ // needs to scale inversely to match.
+ aFrameMetrics.SetZoom(CSSToParentLayerScale2D(
+ 1 / (sampledPosition / mEndZoom.xScale + (1 - sampledPosition) / mStartZoom.xScale),
+ 1 / (sampledPosition / mEndZoom.yScale + (1 - sampledPosition) / mStartZoom.yScale)));
+
+ aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
+ mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
+ mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
+ )));
+
+ return true;
+ }
+
+ virtual bool WantsRepaints() override
+ {
+ return false;
+ }
+
+private:
+ TimeDuration mDuration;
+ const TimeDuration mTotalDuration;
+
+ // Old metrics from before we started a zoom animation. This is only valid
+ // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
+ // interpolate between the start and end frames. We only use the
+ // |mViewportScrollOffset| and |mResolution| fields on this.
+ CSSPoint mStartOffset;
+ CSSToParentLayerScale2D mStartZoom;
+
+ // Target metrics for a zoom to animation. This is only valid when we are in
+ // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
+ // |mResolution| fields on this.
+ CSSPoint mEndOffset;
+ CSSToParentLayerScale2D mEndZoom;
+};
+
+
+class SmoothScrollAnimation : public AsyncPanZoomAnimation {
+public:
+ SmoothScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint &aInitialPosition,
+ const nsPoint &aInitialVelocity,
+ const nsPoint& aDestination, double aSpringConstant,
+ double aDampingRatio)
+ : mApzc(aApzc)
+ , mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
+ aSpringConstant, aDampingRatio)
+ , mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
+ aSpringConstant, aDampingRatio)
+ {
+ }
+
+ /**
+ * Advances a smooth scroll simulation based on the time passed in |aDelta|.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the smooth scroll should be advanced by one frame,
+ * or false if the smooth scroll has ended.
+ */
+ bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override {
+ nsPoint oneParentLayerPixel =
+ CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
+ if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
+ mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
+ // Set the scroll offset to the exact destination. If we allow the scroll
+ // offset to end up being a bit off from the destination, we can get
+ // artefacts like "scroll to the next snap point in this direction"
+ // scrolling to the snap point we're already supposed to be at.
+ aFrameMetrics.SetScrollOffset(
+ aFrameMetrics.CalculateScrollRange().ClampPoint(
+ CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
+ mYAxisModel.GetDestination()))));
+ return false;
+ }
+
+ mXAxisModel.Simulate(aDelta);
+ mYAxisModel.Simulate(aDelta);
+
+ CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
+ mYAxisModel.GetPosition()));
+ CSSPoint css_velocity = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetVelocity(),
+ mYAxisModel.GetVelocity()));
+
+ // Convert from points/second to points/ms
+ ParentLayerPoint velocity = ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
+
+ // Keep the velocity updated for the Axis class so that any animations
+ // chained off of the smooth scroll will inherit it.
+ if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) {
+ mApzc.mX.SetVelocity(0);
+ } else {
+ mApzc.mX.SetVelocity(velocity.x);
+ }
+ if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
+ mApzc.mY.SetVelocity(0);
+ } else {
+ mApzc.mY.SetVelocity(velocity.y);
+ }
+ // If we overscroll, hand off to a fling animation that will complete the
+ // spring back.
+ CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
+ ParentLayerPoint displacement = (position - aFrameMetrics.GetScrollOffset()) * zoom;
+
+ ParentLayerPoint overscroll;
+ ParentLayerPoint adjustedOffset;
+ mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
+
+ aFrameMetrics.ScrollBy(adjustedOffset / zoom);
+
+ // The smooth scroll may have caused us to reach the end of our scroll range.
+ // This can happen if either the layout.css.scroll-behavior.damping-ratio
+ // preference is set to less than 1 (underdamped) or if a smooth scroll
+ // inherits velocity from a fling gesture.
+ if (!IsZero(overscroll)) {
+ // Hand off a fling with the remaining momentum to the next APZC in the
+ // overscroll handoff chain.
+
+ // We may have reached the end of the scroll range along one axis but
+ // not the other. In such a case we only want to hand off the relevant
+ // component of the fling.
+ if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ velocity.y = 0;
+ }
+
+ // To hand off the fling, we attempt to find a target APZC and start a new
+ // fling with the same velocity on that APZC. For simplicity, the actual
+ // overscroll of the current sample is discarded rather than being handed
+ // off. The compositor should sample animations sufficiently frequently
+ // that this is not noticeable. The target APZC is chosen by seeing if
+ // there is an APZC further in the handoff chain which is pannable; if
+ // there isn't, we take the new fling ourselves, entering an overscrolled
+ // state.
+ // Note: APZC is holding mMonitor, so directly calling
+ // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
+ // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
+ // called after mMonitor is released.
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint>(&mApzc,
+ &AsyncPanZoomController::HandleSmoothScrollOverscroll,
+ velocity));
+ return false;
+ }
+
+ return true;
+ }
+
+ void SetDestination(const nsPoint& aNewDestination) {
+ mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
+ mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
+ }
+
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(
+ nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
+ }
+
+ SmoothScrollAnimation* AsSmoothScrollAnimation() override {
+ return this;
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+ AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
+};
+
+/*static*/ void
+AsyncPanZoomController::InitializeGlobalState()
+{
+ static bool sInitialized = false;
+ if (sInitialized)
+ return;
+ sInitialized = true;
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gZoomAnimationFunction = new ComputedTimingFunction();
+ gZoomAnimationFunction->Init(
+ nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
+ ClearOnShutdown(&gZoomAnimationFunction);
+ gVelocityCurveFunction = new ComputedTimingFunction();
+ gVelocityCurveFunction->Init(
+ nsTimingFunction(gfxPrefs::APZCurveFunctionX1(),
+ gfxPrefs::APZCurveFunctionY2(),
+ gfxPrefs::APZCurveFunctionX2(),
+ gfxPrefs::APZCurveFunctionY2()));
+ ClearOnShutdown(&gVelocityCurveFunction);
+
+ uint64_t sysmem = PR_GetPhysicalMemorySize();
+ uint64_t threshold = 1LL << 32; // 4 GB in bytes
+ gIsHighMemSystem = sysmem >= threshold;
+}
+
+AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
+ APZCTreeManager* aTreeManager,
+ const RefPtr<InputQueue>& aInputQueue,
+ GeckoContentController* aGeckoContentController,
+ GestureBehavior aGestures)
+ : mLayersId(aLayersId),
+ mGeckoContentController(aGeckoContentController),
+ mRefPtrMonitor("RefPtrMonitor"),
+ // mTreeManager must be initialized before GetFrameTime() is called
+ mTreeManager(aTreeManager),
+ mFrameMetrics(mScrollMetadata.GetMetrics()),
+ mMonitor("AsyncPanZoomController"),
+ mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
+ mX(this),
+ mY(this),
+ mPanDirRestricted(false),
+ mZoomConstraints(false, false,
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1),
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)),
+ mLastSampleTime(GetFrameTime()),
+ mLastCheckerboardReport(GetFrameTime()),
+ mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
+ mState(NOTHING),
+ mNotificationBlockers(0),
+ mInputQueue(aInputQueue),
+ mPinchPaintTimerSet(false),
+ mAPZCId(sAsyncPanZoomControllerCount++),
+ mSharedLock(nullptr),
+ mAsyncTransformAppliedToContent(false),
+ mCheckerboardEventLock("APZCBELock")
+{
+ if (aGestures == USE_GESTURE_DETECTOR) {
+ mGestureEventListener = new GestureEventListener(this);
+ }
+}
+
+AsyncPanZoomController::~AsyncPanZoomController()
+{
+ MOZ_ASSERT(IsDestroyed());
+}
+
+PlatformSpecificStateBase*
+AsyncPanZoomController::GetPlatformSpecificState()
+{
+ if (!mPlatformSpecificState) {
+ mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
+ }
+ return mPlatformSpecificState.get();
+}
+
+already_AddRefed<GeckoContentController>
+AsyncPanZoomController::GetGeckoContentController() const {
+ MonitorAutoLock lock(mRefPtrMonitor);
+ RefPtr<GeckoContentController> controller = mGeckoContentController;
+ return controller.forget();
+}
+
+already_AddRefed<GestureEventListener>
+AsyncPanZoomController::GetGestureEventListener() const {
+ MonitorAutoLock lock(mRefPtrMonitor);
+ RefPtr<GestureEventListener> listener = mGestureEventListener;
+ return listener.forget();
+}
+
+const RefPtr<InputQueue>&
+AsyncPanZoomController::GetInputQueue() const {
+ return mInputQueue;
+}
+
+void
+AsyncPanZoomController::Destroy()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ CancelAnimation(CancelAnimationFlags::ScrollSnap);
+
+ { // scope the lock
+ MonitorAutoLock lock(mRefPtrMonitor);
+ mGeckoContentController = nullptr;
+ mGestureEventListener = nullptr;
+ }
+ mParent = nullptr;
+ mTreeManager = nullptr;
+
+ // Only send the release message if the SharedFrameMetrics has been created.
+ if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
+ Unused << mMetricsSharingController->StopSharingMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
+ }
+
+ { // scope the lock
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mSharedFrameMetricsBuffer = nullptr;
+ delete mSharedLock;
+ mSharedLock = nullptr;
+ }
+}
+
+bool
+AsyncPanZoomController::IsDestroyed() const
+{
+ return mTreeManager == nullptr;
+}
+
+/* static */ScreenCoord
+AsyncPanZoomController::GetTouchStartTolerance()
+{
+ return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
+}
+
+/* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
+{
+ return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
+}
+
+bool
+AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
+ if (aTouchPoints == 0) {
+ // Cant' do anything with zero touch points
+ return false;
+ }
+
+ // This logic is simplified, erring on the side of returning true
+ // if we're not sure. It's safer to pretend that we can consume the
+ // event and then not be able to than vice-versa.
+ // We could probably enhance this logic to determine things like "we're
+ // not pannable, so we can only zoom in, and the zoom is already maxed
+ // out, so we're not zoomable either" but no need for that at this point.
+
+ bool pannable = aBlock->GetOverscrollHandoffChain()->CanBePanned(this);
+ bool zoomable = mZoomConstraints.mAllowZoom;
+
+ pannable &= (aBlock->TouchActionAllowsPanningX() || aBlock->TouchActionAllowsPanningY());
+ zoomable &= (aBlock->TouchActionAllowsPinchZoom());
+
+ // XXX once we fix bug 1031443, consumable should be assigned
+ // pannable || zoomable if aTouchPoints > 1.
+ bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
+ if (!consumable) {
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisStart(
+AsyncDragMetrics::DragDirection aDir,
+const PointTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisStart(AsyncDragMetrics::DragDirection aDir, const RectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static IntCoordTyped<Units> GetAxisStart(AsyncDragMetrics::DragDirection aDir, const IntRectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static IntCoordTyped<Units> GetAxisEnd(AsyncDragMetrics::DragDirection aDir, const IntRectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x + aValue.width;
+ } else {
+ return aValue.y + aValue.height;
+ }
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisSize(AsyncDragMetrics::DragDirection aDir, const RectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.width;
+ } else {
+ return aValue.height;
+ }
+}
+
+template <typename FromUnits, typename ToUnits>
+static float GetAxisScale(AsyncDragMetrics::DragDirection aDir, const ScaleFactors2D<FromUnits, ToUnits>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.xScale;
+ } else {
+ return aValue.yScale;
+ }
+}
+
+nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ if (!gfxPrefs::APZDragEnabled()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!GetApzcTreeManager()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ RefPtr<HitTestingTreeNode> node =
+ GetApzcTreeManager()->FindScrollNode(aDragMetrics);
+ if (!node) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSPoint scrollFramePoint = aEvent.mLocalOrigin / GetFrameMetrics().GetZoom();
+ // The scrollbar can be transformed with the frame but the pres shell
+ // resolution is only applied to the scroll frame.
+ CSSPoint scrollbarPoint = scrollFramePoint * mFrameMetrics.GetPresShellResolution();
+ CSSRect cssCompositionBound = mFrameMetrics.CalculateCompositedRectInCssPixels();
+
+ CSSCoord mousePosition = GetAxisStart(aDragMetrics.mDirection, scrollbarPoint) -
+ CSSCoord(aDragMetrics.mScrollbarDragOffset) -
+ GetAxisStart(aDragMetrics.mDirection, cssCompositionBound) -
+ CSSCoord(GetAxisStart(aDragMetrics.mDirection, aDragMetrics.mScrollTrack));
+
+ CSSCoord scrollMax = CSSCoord(GetAxisEnd(aDragMetrics.mDirection, aDragMetrics.mScrollTrack));
+ scrollMax -= node->GetScrollSize() /
+ GetAxisScale(aDragMetrics.mDirection, mFrameMetrics.GetZoom()) *
+ mFrameMetrics.GetPresShellResolution();
+
+ float scrollPercent = mousePosition / scrollMax;
+
+ CSSCoord minScrollPosition =
+ GetAxisStart(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect().TopLeft());
+ CSSCoord maxScrollPosition =
+ GetAxisSize(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect()) -
+ GetAxisSize(aDragMetrics.mDirection, cssCompositionBound);
+ CSSCoord scrollPosition = scrollPercent * maxScrollPosition;
+
+ scrollPosition = std::max(scrollPosition, minScrollPosition);
+ scrollPosition = std::min(scrollPosition, maxScrollPosition);
+
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ if (aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) {
+ scrollOffset.x = scrollPosition;
+ } else {
+ scrollOffset.y = scrollPosition;
+ }
+ mFrameMetrics.SetScrollOffset(scrollOffset);
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
+ const ScreenToParentLayerMatrix4x4& aTransformToApzc) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
+ if (!multiTouchInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ if (listener) {
+ rv = listener->HandleInputEvent(multiTouchInput);
+ if (rv == nsEventStatus_eConsumeNoDefault) {
+ return rv;
+ }
+ }
+
+ switch (multiTouchInput.mType) {
+ case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
+ default: NS_WARNING("Unhandled multitouch"); break;
+ }
+ break;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
+ if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ switch (panGestureInput.mType) {
+ case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
+ case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
+ default: NS_WARNING("Unhandled pan gesture"); break;
+ }
+ break;
+ }
+ case MOUSE_INPUT: {
+ MouseInput mouseInput = aEvent.AsMouseInput();
+ if (!mouseInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ // TODO Need to implement blocks to properly handle this.
+ //rv = HandleDragEvent(mouseInput, dragMetrics);
+ break;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
+ if (!scrollInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = OnScrollWheel(scrollInput);
+ break;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
+ if (!pinchInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = HandleGestureEvent(pinchInput);
+ break;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput tapInput = aEvent.AsTapGestureInput();
+ if (!tapInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = HandleGestureEvent(tapInput);
+ break;
+ }
+ default: NS_WARNING("Unhandled input event type"); break;
+ }
+
+ return rv;
+}
+
+nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (aEvent.mInputType) {
+ case PINCHGESTURE_INPUT: {
+ const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
+ switch (pinchGestureInput.mType) {
+ case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
+ case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
+ case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
+ default: NS_WARNING("Unhandled pinch gesture"); break;
+ }
+ break;
+ }
+ case TAPGESTURE_INPUT: {
+ const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
+ switch (tapGestureInput.mType) {
+ case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_SECOND: rv = OnSecondTap(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
+ default: NS_WARNING("Unhandled tap gesture"); break;
+ }
+ break;
+ }
+ default: NS_WARNING("Unhandled input event"); break;
+ }
+
+ return rv;
+}
+
+void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
+{
+ mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-start in state %d\n", this, mState);
+ mPanDirRestricted = false;
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+
+ switch (mState) {
+ case FLING:
+ case ANIMATING_ZOOM:
+ case SMOOTH_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ case WHEEL_SCROLL:
+ case PAN_MOMENTUM:
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
+ MOZ_FALLTHROUGH;
+ case NOTHING: {
+ mX.StartTouch(point.x, aEvent.mTime);
+ mY.StartTouch(point.y, aEvent.mTime);
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eStartTouch,
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
+ }
+ SetState(TOUCHING);
+ break;
+ }
+ case TOUCHING:
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PINCHING:
+ NS_WARNING("Received impossible touch in OnTouchStart");
+ break;
+ default:
+ NS_WARNING("Unhandled case in OnTouchStart");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-move in state %d\n", this, mState);
+ switch (mState) {
+ case FLING:
+ case SMOOTH_SCROLL:
+ case NOTHING:
+ case ANIMATING_ZOOM:
+ // May happen if the user double-taps and drags without lifting after the
+ // second tap. Ignore the move if this happens.
+ return nsEventStatus_eIgnore;
+
+ case TOUCHING: {
+ ScreenCoord panThreshold = GetTouchStartTolerance();
+ UpdateWithTouchAtDevicePoint(aEvent);
+
+ if (PanDistance() < panThreshold) {
+ return nsEventStatus_eIgnore;
+ }
+
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (gfxPrefs::TouchActionEnabled() && GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+ // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
+ // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
+ // status immediately to trigger cancel event further. It should happen independent of
+ // the parent type (whether it is scrolling or not).
+ StartPanning(aEvent);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ return StartPanning(aEvent);
+ }
+
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PAN_MOMENTUM:
+ TrackTouch(aEvent);
+ return nsEventStatus_eConsumeNoDefault;
+
+ case PINCHING:
+ // The scale gesture listener should have handled this.
+ NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
+ return nsEventStatus_eIgnore;
+
+ case WHEEL_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
+ // as touch blocks that begin in an overscrolled state cancel the
+ // animation. The same is true for wheel scroll animations.
+ NS_WARNING("Received impossible touch in OnTouchMove");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-end in state %d\n", this, mState);
+
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->SetScrollingRootContent(false);
+ }
+
+ OnTouchEndOrCancel();
+
+ // In case no touch behavior triggered previously we can avoid sending
+ // scroll events or requesting content repaint. This condition is added
+ // to make tests consistent - in case touch-action is NONE (and therefore
+ // no pans/zooms can be performed) we expected neither scroll or repaint
+ // events.
+ if (mState != NOTHING) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ }
+
+ switch (mState) {
+ case FLING:
+ // Should never happen.
+ NS_WARNING("Received impossible touch end in OnTouchEnd.");
+ MOZ_FALLTHROUGH;
+ case ANIMATING_ZOOM:
+ case SMOOTH_SCROLL:
+ case NOTHING:
+ // May happen if the user double-taps and drags without lifting after the
+ // second tap. Ignore if this happens.
+ return nsEventStatus_eIgnore;
+
+ case TOUCHING:
+ // We may have some velocity stored on the axis from move events
+ // that were not big enough to trigger scrolling. Clear that out.
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ APZC_LOG("%p still has %u touch points active\n", this,
+ GetCurrentTouchBlock()->GetActiveTouchCount());
+ // In cases where the user is panning, then taps the second finger without
+ // entering a pinch, we will arrive here when the second finger is lifted.
+ // However the first finger is still down so we want to remain in state
+ // TOUCHING.
+ if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
+ // It's possible we may be overscrolled if the user tapped during a
+ // previous overscroll pan. Make sure to snap back in this situation.
+ // An ancestor APZC could be overscrolled instead of this APZC, so
+ // walk the handoff chain as well.
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
+ // SnapBackOverscrolledApzc() will put any APZC it causes to snap back
+ // into the OVERSCROLL_ANIMATION state. If that's not us, since we're
+ // done TOUCHING enter the NOTHING state.
+ if (mState != OVERSCROLL_ANIMATION) {
+ SetState(NOTHING);
+ }
+ }
+ return nsEventStatus_eIgnore;
+
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PAN_MOMENTUM:
+ {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
+ mX.EndTouch(aEvent.mTime);
+ mY.EndTouch(aEvent.mTime);
+ ParentLayerPoint flingVelocity = GetVelocityVector();
+ // Clear our velocities; if DispatchFling() gives the fling to us,
+ // the fling velocity gets *added* to our existing velocity in
+ // AcceptFling().
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ // Clear our state so that we don't stay in the PANNING state
+ // if DispatchFling() gives the fling to somone else. However,
+ // don't send the state change notification until we've determined
+ // what our final state is to avoid notification churn.
+ StateChangeNotificationBlocker blocker(this);
+ SetState(NOTHING);
+
+ APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
+ flingVelocity.Length().value, gfxPrefs::APZFlingMinVelocityThreshold());
+
+ if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ // Make a local copy of the tree manager pointer and check that it's not
+ // null before calling DispatchFling(). This is necessary because Destroy(),
+ // which nulls out mTreeManager, could be called concurrently.
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ FlingHandoffState handoffState{flingVelocity,
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
+ false /* not handoff */,
+ GetCurrentTouchBlock()->GetScrolledApzc()};
+ treeManagerLocal->DispatchFling(this, handoffState);
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ case PINCHING:
+ SetState(NOTHING);
+ // Scale gesture listener should have handled this.
+ NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
+ return nsEventStatus_eIgnore;
+
+ case WHEEL_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ // Should not receive a touch-end in the OVERSCROLL_ANIMATION state
+ // as touch blocks that begin in an overscrolled state cancel the
+ // animation. The same is true for WHEEL_SCROLL.
+ NS_WARNING("Received impossible touch in OnTouchEnd");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
+ OnTouchEndOrCancel();
+ CancelAnimationAndGestureState();
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
+
+ mPinchPaintTimerSet = false;
+ // Note that there may not be a touch block at this point, if we received the
+ // PinchGestureEvent directly from widget code without any touch events.
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // For platforms that don't support APZ zooming, dispatch a message to the
+ // content controller, it may want to do something else with this gesture.
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
+ }
+ }
+
+ SetState(PINCHING);
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale in state %d\n", this, mState);
+
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (mState != PINCHING) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(),
+ ViewAs<LayoutDevicePixel>(aEvent.mCurrentSpan - aEvent.mPreviousSpan,
+ PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
+ aEvent.modifiers);
+ }
+ }
+
+ // Only the root APZC is zoomable, and the root APZC is not allowed to have
+ // different x and y scales. If it did, the calculations in this function
+ // would have to be adjusted (as e.g. it would no longer be valid to take
+ // the minimum or maximum of the ratios of the widths and heights of the
+ // page rect and the composition bounds).
+ MOZ_ASSERT(mFrameMetrics.IsRootContent());
+ MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom().ToScaleFactor();
+ ParentLayerPoint focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
+ CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
+
+ ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
+ mLastZoomFocus = focusPoint;
+ // If displacing by the change in focus point will take us off page bounds,
+ // then reduce the displacement such that it doesn't.
+ focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
+ focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
+ ScrollBy(focusChange / userZoom);
+
+ // If the span is zero or close to it, we don't want to process this zoom
+ // change because we're going to get wonky numbers for the spanRatio. So
+ // let's bail out here. Note that we do this after the focus-change-scroll
+ // above, so that if we have a pinch with zero span but changing focus,
+ // such as generated by some Synaptics touchpads on Windows, we still
+ // scroll properly.
+ float prevSpan = aEvent.mPreviousSpan;
+ if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
+ // We might have done a nonzero ScrollBy above, so update metrics and
+ // repaint/recomposite
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
+
+ // When we zoom in with focus, we can zoom too much towards the boundaries
+ // that we actually go over them. These are the needed displacements along
+ // either axis such that we don't overscroll the boundaries when zooming.
+ CSSPoint neededDisplacement;
+
+ CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
+ CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
+ realMinZoom.scale = std::max(realMinZoom.scale,
+ mFrameMetrics.GetCompositionBounds().width / mFrameMetrics.GetScrollableRect().width);
+ realMinZoom.scale = std::max(realMinZoom.scale,
+ mFrameMetrics.GetCompositionBounds().height / mFrameMetrics.GetScrollableRect().height);
+ if (realMaxZoom < realMinZoom) {
+ realMaxZoom = realMinZoom;
+ }
+
+ bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
+ (spanRatio < 1.0 && userZoom > realMinZoom);
+
+ if (!mZoomConstraints.mAllowZoom) {
+ doScale = false;
+ }
+
+ if (doScale) {
+ spanRatio = clamped(spanRatio,
+ realMinZoom.scale / userZoom.scale,
+ realMaxZoom.scale / userZoom.scale);
+
+ // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
+ // up above we clamped it.
+ neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
+ neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
+
+ ScaleWithFocus(spanRatio, cssFocusPoint);
+
+ if (neededDisplacement != CSSPoint()) {
+ ScrollBy(neededDisplacement);
+ }
+
+ // We don't want to redraw on every scale, so throttle it.
+ if (!mPinchPaintTimerSet) {
+ const int delay = gfxPrefs::APZScaleRepaintDelay();
+ if (delay >= 0) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ mPinchPaintTimerSet = true;
+ controller->PostDelayedTask(
+ NewRunnableMethod(this,
+ &AsyncPanZoomController::DoDelayedRequestContentRepaint),
+ delay);
+ }
+ }
+ }
+
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // We did a ScrollBy call above even if we didn't do a scale, so we
+ // should composite for that.
+ ScheduleComposite();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale-end in state %d\n", this, mState);
+
+ mPinchPaintTimerSet = false;
+
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
+ }
+ }
+
+ SetState(NOTHING);
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ScheduleComposite();
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // Non-negative focus point would indicate that one finger is still down
+ if (aEvent.mLocalFocusPoint.x != -1 && aEvent.mLocalFocusPoint.y != -1) {
+ mPanDirRestricted = false;
+ mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
+ SetState(TOUCHING);
+ } else {
+ // Otherwise, handle the fingers being lifted.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // We can get into a situation where we are overscrolled at the end of a
+ // pinch if we go into overscroll with a two-finger pan, and then turn
+ // that into a pinch by increasing the span sufficiently. In such a case,
+ // there is no snap-back animation to get us out of overscroll, so we need
+ // to get out of it somehow.
+ // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
+ // further up in the handoff chain rather than on the current APZC, so
+ // we need to clear overscroll along the entire handoff chain.
+ if (HasReadyTouchBlock()) {
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
+ } else {
+ ClearOverscroll();
+ }
+ // Along with clearing the overscroll, we also want to snap to the nearest
+ // snap point as appropriate.
+ ScrollSnap();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+bool
+AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut)
+{
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ ScreenToScreenMatrix4x4 transformScreenToGecko =
+ treeManagerLocal->GetScreenToApzcTransform(this)
+ * treeManagerLocal->GetApzcToGeckoTransform(this);
+
+ Maybe<ScreenIntPoint> layoutPoint = UntransformBy(
+ transformScreenToGecko, aPoint);
+ if (!layoutPoint) {
+ return false;
+ }
+
+ *aOut = LayoutDevicePoint(ViewAs<LayoutDevicePixel>(*layoutPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ return true;
+ }
+ return false;
+}
+
+static bool
+AllowsScrollingMoreThanOnePage(double aMultiplier)
+{
+ const int32_t kMinAllowPageScroll =
+ EventStateManager::MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
+ return Abs(aMultiplier) >= kMinAllowPageScroll;
+}
+
+ParentLayerPoint
+AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) const
+{
+ ParentLayerSize scrollAmount;
+ ParentLayerSize pageScrollSize;
+
+ {
+ // Grab the lock to access the frame metrics.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ LayoutDeviceIntSize scrollAmountLD = mScrollMetadata.GetLineScrollAmount();
+ LayoutDeviceIntSize pageScrollSizeLD = mScrollMetadata.GetPageScrollAmount();
+ scrollAmount = scrollAmountLD /
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
+ pageScrollSize = pageScrollSizeLD /
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
+ }
+
+ ParentLayerPoint delta;
+ switch (aEvent.mDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_LINE: {
+ delta.x = aEvent.mDeltaX * scrollAmount.width;
+ delta.y = aEvent.mDeltaY * scrollAmount.height;
+ break;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PAGE: {
+ delta.x = aEvent.mDeltaX * pageScrollSize.width;
+ delta.y = aEvent.mDeltaY * pageScrollSize.height;
+ break;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PIXEL: {
+ delta = ToParentLayerCoordinates(ScreenPoint(aEvent.mDeltaX, aEvent.mDeltaY), aEvent.mOrigin);
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
+ }
+
+ // Apply user-set multipliers.
+ delta.x *= aEvent.mUserDeltaMultiplierX;
+ delta.y *= aEvent.mUserDeltaMultiplierY;
+
+ // For the conditions under which we allow system scroll overrides, see
+ // EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction
+ // and WheelTransaction::OverrideSystemScrollSpeed. Note that we do *not*
+ // restrict this to the root content, see bug 1217715 for discussion on this.
+ if (gfxPrefs::MouseWheelHasRootScrollDeltaOverride() &&
+ !aEvent.IsCustomizedByUserPrefs() &&
+ aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
+ aEvent.mAllowToOverrideSystemScrollSpeed) {
+ delta.x = WidgetWheelEvent::ComputeOverriddenDelta(delta.x, false);
+ delta.y = WidgetWheelEvent::ComputeOverriddenDelta(delta.y, true);
+ }
+
+ // If this is a line scroll, and this event was part of a scroll series, then
+ // it might need extra acceleration. See WheelHandlingHelper.cpp.
+ if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
+ aEvent.mScrollSeriesNumber > 0)
+ {
+ int32_t start = gfxPrefs::MouseWheelAccelerationStart();
+ if (start >= 0 && aEvent.mScrollSeriesNumber >= uint32_t(start)) {
+ int32_t factor = gfxPrefs::MouseWheelAccelerationFactor();
+ if (factor > 0) {
+ delta.x = ComputeAcceleratedWheelDelta(delta.x, aEvent.mScrollSeriesNumber, factor);
+ delta.y = ComputeAcceleratedWheelDelta(delta.y, aEvent.mScrollSeriesNumber, factor);
+ }
+ }
+ }
+
+ // We shouldn't scroll more than one page at once except when the
+ // user preference is large.
+ if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierX) &&
+ Abs(delta.x) > pageScrollSize.width) {
+ delta.x = (delta.x >= 0)
+ ? pageScrollSize.width
+ : -pageScrollSize.width;
+ }
+ if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierY) &&
+ Abs(delta.y) > pageScrollSize.height) {
+ delta.y = (delta.y >= 0)
+ ? pageScrollSize.height
+ : -pageScrollSize.height;
+ }
+
+ return delta;
+}
+
+// Return whether or not the underlying layer can be scrolled on either axis.
+bool
+AsyncPanZoomController::CanScroll(const InputData& aEvent) const
+{
+ ParentLayerPoint delta;
+ if (aEvent.mInputType == SCROLLWHEEL_INPUT) {
+ delta = GetScrollWheelDelta(aEvent.AsScrollWheelInput());
+ } else if (aEvent.mInputType == PANGESTURE_INPUT) {
+ const PanGestureInput& panInput = aEvent.AsPanGestureInput();
+ delta = ToParentLayerCoordinates(panInput.UserMultipliedPanDisplacement(), panInput.mPanStartPoint);
+ }
+ if (!delta.x && !delta.y) {
+ return false;
+ }
+
+ return CanScrollWithWheel(delta);
+}
+
+bool
+AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mX.CanScroll(aDelta.x)) {
+ return true;
+ }
+ if (mY.CanScroll(aDelta.y) && mScrollMetadata.AllowVerticalScrollWithWheel()) {
+ return true;
+ }
+ return false;
+}
+
+bool
+AsyncPanZoomController::CanScroll(Layer::ScrollDirection aDirection) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ switch (aDirection) {
+ case Layer::HORIZONTAL: return mX.CanScroll();
+ case Layer::VERTICAL: return mY.CanScroll();
+ default: MOZ_ASSERT(false); return false;
+ }
+}
+
+bool
+AsyncPanZoomController::AllowScrollHandoffInCurrentBlock() const
+{
+ bool result = mInputQueue->AllowScrollHandoff();
+ if (!gfxPrefs::APZAllowImmediateHandoff()) {
+ if (InputBlockState* currentBlock = GetCurrentInputBlock()) {
+ // Do not allow handoff beyond the first APZC to scroll.
+ if (currentBlock->GetScrolledApzc() == this) {
+ result = false;
+ }
+ }
+ }
+ return result;
+}
+
+void AsyncPanZoomController::DoDelayedRequestContentRepaint()
+{
+ if (!IsDestroyed() && mPinchPaintTimerSet) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ }
+ mPinchPaintTimerSet = false;
+}
+
+nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
+{
+ ParentLayerPoint delta = GetScrollWheelDelta(aEvent);
+ APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str());
+
+ if ((delta.x || delta.y) && !CanScrollWithWheel(delta)) {
+ // We can't scroll this apz anymore, so we simply drop the event.
+ if (mInputQueue->GetActiveWheelTransaction() &&
+ gfxPrefs::MouseScrollTestingEnabled()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyMozMouseScrollEvent(
+ mFrameMetrics.GetScrollId(),
+ NS_LITERAL_STRING("MozMouseScrollFailed"));
+ }
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (delta.x == 0 && delta.y == 0) {
+ // Avoid spurious state changes and unnecessary work
+ return nsEventStatus_eIgnore;
+ }
+
+ switch (aEvent.mScrollMode) {
+ case ScrollWheelInput::SCROLLMODE_INSTANT: {
+
+ // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
+ // next snap point. Check for this, and adjust the delta to take into
+ // account the snap point.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
+
+ ScreenPoint distance = ToScreenCoordinates(
+ ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
+
+ CancelAnimation();
+
+ MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
+ OverscrollHandoffState handoffState(
+ *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(),
+ distance,
+ ScrollSource::Wheel);
+ ParentLayerPoint startPoint = aEvent.mLocalOrigin;
+ ParentLayerPoint endPoint = aEvent.mLocalOrigin - delta;
+ CallDispatchScroll(startPoint, endPoint, handoffState);
+
+ SetState(NOTHING);
+
+ // The calls above handle their own locking; moreover,
+ // ToScreenCoordinates() and CallDispatchScroll() can grab the tree lock.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+
+ break;
+ }
+
+ case ScrollWheelInput::SCROLLMODE_SMOOTH: {
+ // The lock must be held across the entire update operation, so the
+ // compositor doesn't end the animation before we get a chance to
+ // update it.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // Perform scroll snapping if appropriate.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ // If we're already in a wheel scroll or smooth scroll animation,
+ // the delta is applied to its destination, not to the current
+ // scroll position. Take this into account when finding a snap point.
+ if (mState == WHEEL_SCROLL) {
+ startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
+ } else if (mState == SMOOTH_SCROLL) {
+ startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
+ }
+ if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
+ // If we're scroll snapping, use a smooth scroll animation to get
+ // the desired physics. Note that SmoothScrollTo() will re-use an
+ // existing smooth scroll animation if there is one.
+ APZC_LOG("%p wheel scrolling to snap point %s\n", this, Stringify(startPosition).c_str());
+ SmoothScrollTo(startPosition);
+ break;
+ }
+
+ // Otherwise, use a wheel scroll animation, also reusing one if possible.
+ if (mState != WHEEL_SCROLL) {
+ CancelAnimation();
+ SetState(WHEEL_SCROLL);
+
+ nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
+ StartAnimation(new WheelScrollAnimation(
+ *this, initialPosition, aEvent.mDeltaType));
+ }
+
+ nsPoint deltaInAppUnits =
+ CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
+ // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
+ // appunits/second
+ nsPoint velocity =
+ CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
+
+ WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
+ animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
+ break;
+ }
+
+ case ScrollWheelInput::SCROLLMODE_SENTINEL: {
+ MOZ_ASSERT_UNREACHABLE("Invalid ScrollMode.");
+ break;
+ }
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+void
+AsyncPanZoomController::NotifyMozMouseScrollEvent(const nsString& aString) const
+{
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+
+ controller->NotifyMozMouseScrollEvent(mFrameMetrics.GetScrollId(), aString);
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
+
+ mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ GetCurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
+
+ mX.CancelGesture();
+ mY.CancelGesture();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+
+nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
+
+ if (GetAxisLockMode() == FREE) {
+ SetState(PANNING);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ float dx = aEvent.mPanDisplacement.x, dy = aEvent.mPanDisplacement.y;
+
+ if (dx || dy) {
+ double angle = atan2(dy, dx); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+ HandlePanning(angle);
+ } else {
+ SetState(PANNING);
+ }
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, true);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
+ APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ if (!aFingersOnTouchpad) {
+ // When a SMOOTH_SCROLL scroll is being processed on a frame, mouse
+ // wheel and trackpad momentum scroll position updates will not cancel the
+ // SMOOTH_SCROLL scroll animations, enabling scripts that depend on
+ // them to be responsive without forcing the user to wait for the momentum
+ // scrolling to completely stop.
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ if (mState == NOTHING) {
+ // This event block was interrupted by something else. If the user's fingers
+ // are still on on the touchpad we want to resume scrolling, otherwise we
+ // ignore the rest of the scroll gesture.
+ if (!aFingersOnTouchpad) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ // Resume / restart the pan.
+ // PanBegin will call back into this function with mState == PANNING.
+ return OnPanBegin(aEvent);
+ }
+
+ // Note that there is a multiplier that applies onto the "physical" pan
+ // displacement (how much the user's fingers moved) that produces the "logical"
+ // pan displacement (how much the page should move). For some of the code
+ // below it makes more sense to use the physical displacement rather than
+ // the logical displacement, and vice-versa.
+ ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement;
+ ParentLayerPoint logicalPanDisplacement = aEvent.UserMultipliedLocalPanDisplacement();
+
+ // We need to update the axis velocity in order to get a useful display port
+ // size and position. We need to do so even if this is a momentum pan (i.e.
+ // aFingersOnTouchpad == false); in that case the "with touch" part is not
+ // really appropriate, so we may want to rethink this at some point.
+ mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.x, logicalPanDisplacement.x, aEvent.mTime);
+ mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.y, logicalPanDisplacement.y, aEvent.mTime);
+
+ HandlePanningUpdate(physicalPanDisplacement);
+
+ ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y));
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ OverscrollHandoffState handoffState(
+ *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(),
+ panDistance,
+ ScrollSource::Wheel);
+
+ // Create fake "touch" positions that will result in the desired scroll motion.
+ // Note that the pan displacement describes the change in scroll position:
+ // positive displacement values mean that the scroll position increases.
+ // However, an increase in scroll position means that the scrolled contents
+ // are moved to the left / upwards. Since our simulated "touches" determine
+ // the motion of the scrolled contents, not of the scroll position, they need
+ // to move in the opposite direction of the pan displacement.
+ ParentLayerPoint startPoint = aEvent.mLocalPanStartPoint;
+ ParentLayerPoint endPoint = aEvent.mLocalPanStartPoint - logicalPanDisplacement;
+ CallDispatchScroll(startPoint, endPoint, handoffState);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-end in state %d\n", this, mState);
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, true);
+
+ mX.EndTouch(aEvent.mTime);
+ mY.EndTouch(aEvent.mTime);
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't enlarge the display port unnecessarily.
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
+ GetCurrentPanGestureBlock()->GetOverscrollHandoffChain();
+ if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL)) {
+ mX.SetVelocity(0);
+ }
+ if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL)) {
+ mY.SetVelocity(0);
+ }
+
+ SetState(NOTHING);
+ RequestContentRepaint();
+
+ if (!aEvent.mFollowedByMomentum) {
+ ScrollSnap();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ SetState(PAN_MOMENTUM);
+ ScrollSnapToDestination();
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, false);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMomentumEnd(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-momentumend in state %d\n", this, mState);
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, false);
+
+ // We need to reset the velocity to zero. We don't really have a "touch"
+ // here because the touch has already ended long before the momentum
+ // animation started, but I guess it doesn't really matter for now.
+ mX.CancelGesture();
+ mY.CancelGesture();
+ SetState(NOTHING);
+
+ RequestContentRepaint();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a long-press in state %d\n", this, mState);
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
+ TouchBlockState* touch = GetCurrentTouchBlock();
+ if (!touch) {
+ APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ if (touch->IsDuringFastFling()) {
+ APZC_LOG("%p dropping long-press because of fast fling\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
+ controller->HandleTap(TapType::eLongTap, geckoScreenPoint, aEvent.modifiers, GetGuid(), blockId);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eLongTapUp, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::GenerateSingleTap(TapType aType,
+ const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
+ TouchBlockState* touch = GetCurrentTouchBlock();
+ // |touch| may be null in the case where this function is
+ // invoked by GestureEventListener on a timeout. In that case we already
+ // verified that the single tap is allowed so we let it through.
+ // XXX there is a bug here that in such a case the touch block that
+ // generated this tap will not get its mSingleTapOccurred flag set.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1256344#c6
+ if (touch) {
+ if (touch->IsDuringFastFling()) {
+ APZC_LOG("%p dropping single-tap because it was during a fast-fling\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ touch->SetSingleTapOccurred();
+ }
+ // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
+ // calling controller->HandleTap directly might mean that content receives
+ // the single tap message before the corresponding touch-up. To avoid that we
+ // schedule the singletap message to run on the next spin of the event loop.
+ // See bug 965381 for the issue this was causing.
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TapType, LayoutDevicePoint, mozilla::Modifiers,
+ ScrollableLayerGuid, uint64_t>(controller,
+ &GeckoContentController::HandleTap,
+ aType, geckoScreenPoint,
+ aModifiers, GetGuid(),
+ touch ? touch->GetBlockId() : 0);
+
+ controller->PostDelayedTask(runnable.forget(), 0);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ return nsEventStatus_eIgnore;
+}
+
+void AsyncPanZoomController::OnTouchEndOrCancel() {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eEndTouch, GetCurrentTouchBlock()->SingleTapOccurred());
+ }
+}
+
+nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
+ // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
+ // sending event to content
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (!(mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
+ return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a double-tap in state %d\n", this, mState);
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
+ controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
+ aEvent.modifiers, GetGuid(), GetCurrentTouchBlock()->GetBlockId());
+ }
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnSecondTap(const TapGestureInput& aEvent)
+{
+ APZC_LOG("%p got a second-tap in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eSecondTap, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
+ // XXX: Implement this.
+ return nsEventStatus_eIgnore;
+}
+
+
+ScreenToParentLayerMatrix4x4 AsyncPanZoomController::GetTransformToThis() const {
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ return treeManagerLocal->GetScreenToApzcTransform(this);
+ }
+ return ScreenToParentLayerMatrix4x4();
+}
+
+ScreenPoint AsyncPanZoomController::ToScreenCoordinates(const ParentLayerPoint& aVector,
+ const ParentLayerPoint& aAnchor) const {
+ return TransformVector(GetTransformToThis().Inverse(), aVector, aAnchor);
+}
+
+// TODO: figure out a good way to check the w-coordinate is positive and return the result
+ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPoint& aVector,
+ const ScreenPoint& aAnchor) const {
+ return TransformVector(GetTransformToThis(), aVector, aAnchor);
+}
+
+bool AsyncPanZoomController::Contains(const ScreenIntPoint& aPoint) const
+{
+ ScreenToParentLayerMatrix4x4 transformToThis = GetTransformToThis();
+ Maybe<ParentLayerIntPoint> point = UntransformBy(transformToThis, aPoint);
+ if (!point) {
+ return false;
+ }
+
+ ParentLayerIntRect cb;
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ GetFrameMetrics().GetCompositionBounds().ToIntRect(&cb);
+ }
+ return cb.Contains(*point);
+}
+
+ScreenCoord AsyncPanZoomController::PanDistance() const {
+ ParentLayerPoint panVector;
+ ParentLayerPoint panStart;
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ panVector = ParentLayerPoint(mX.PanDistance(), mY.PanDistance());
+ panStart = PanStart();
+ }
+ return ToScreenCoordinates(panVector, panStart).Length();
+}
+
+ParentLayerPoint AsyncPanZoomController::PanStart() const {
+ return ParentLayerPoint(mX.PanStart(), mY.PanStart());
+}
+
+const ParentLayerPoint AsyncPanZoomController::GetVelocityVector() const {
+ return ParentLayerPoint(mX.GetVelocity(), mY.GetVelocity());
+}
+
+void AsyncPanZoomController::SetVelocityVector(const ParentLayerPoint& aVelocityVector) {
+ mX.SetVelocity(aVelocityVector.x);
+ mY.SetVelocity(aVelocityVector.y);
+}
+
+void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
+ // Handling of cross sliding will need to be added in this method after touch-action released
+ // enabled by default.
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+ if (mX.CanScrollNow() && mY.CanScrollNow()) {
+ if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mY.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_X);
+ } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mX.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_Y);
+ } else {
+ SetState(PANNING);
+ }
+ } else if (mX.CanScrollNow() || mY.CanScrollNow()) {
+ SetState(PANNING);
+ } else {
+ SetState(NOTHING);
+ }
+ } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningX()) {
+ // Using bigger angle for panning to keep behavior consistent
+ // with IE.
+ if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
+ mY.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_X);
+ mPanDirRestricted = true;
+ } else {
+ // Don't treat these touches as pan/zoom movements since 'touch-action' value
+ // requires it.
+ SetState(NOTHING);
+ }
+ } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningY()) {
+ if (IsCloseToVertical(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
+ mX.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_Y);
+ mPanDirRestricted = true;
+ } else {
+ SetState(NOTHING);
+ }
+ } else {
+ SetState(NOTHING);
+ }
+ if (!IsInPanningState()) {
+ // If we didn't enter a panning state because touch-action disallowed it,
+ // make sure to clear any leftover velocity from the pre-threshold
+ // touchmoves.
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ }
+}
+
+void AsyncPanZoomController::HandlePanning(double aAngle) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ MOZ_ASSERT(GetCurrentInputBlock());
+ RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
+ GetCurrentInputBlock()->GetOverscrollHandoffChain();
+ bool canScrollHorizontal = !mX.IsAxisLocked() &&
+ overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL);
+ bool canScrollVertical = !mY.IsAxisLocked() &&
+ overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
+
+ if (!canScrollHorizontal || !canScrollVertical) {
+ SetState(PANNING);
+ } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mY.SetAxisLocked(true);
+ if (canScrollHorizontal) {
+ SetState(PANNING_LOCKED_X);
+ }
+ } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mX.SetAxisLocked(true);
+ if (canScrollVertical) {
+ SetState(PANNING_LOCKED_Y);
+ }
+ } else {
+ SetState(PANNING);
+ }
+}
+
+void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
+ // If we're axis-locked, check if the user is trying to break the lock
+ if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
+
+ double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+
+ float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
+
+ if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
+ if (mState == PANNING_LOCKED_X) {
+ if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
+ mY.SetAxisLocked(false);
+ SetState(PANNING);
+ }
+ } else if (mState == PANNING_LOCKED_Y) {
+ if (!IsCloseToVertical(angle, gfxPrefs::APZAxisBreakoutAngle())) {
+ mX.SetAxisLocked(false);
+ SetState(PANNING);
+ }
+ }
+ }
+ }
+}
+
+nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+ float dx = mX.PanDistance(point.x);
+ float dy = mY.PanDistance(point.y);
+
+ double angle = atan2(dy, dx); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+
+ if (gfxPrefs::TouchActionEnabled()) {
+ HandlePanningWithTouchAction(angle);
+ } else {
+ if (GetAxisLockMode() == FREE) {
+ SetState(PANNING);
+ } else {
+ HandlePanning(angle);
+ }
+ }
+
+ if (IsInPanningState()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyAPZStateChange(GetGuid(), APZStateChange::eStartPanning);
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ // Don't consume an event that didn't trigger a panning.
+ return nsEventStatus_eIgnore;
+}
+
+void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+ mX.UpdateWithTouchAtDevicePoint(point.x, 0, aEvent.mTime);
+ mY.UpdateWithTouchAtDevicePoint(point.y, 0, aEvent.mTime);
+}
+
+bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState) {
+
+ // "start - end" rather than "end - start" because e.g. moving your finger
+ // down (*positive* direction along y axis) causes the vertical scroll offset
+ // to *decrease* as the page follows your finger.
+ ParentLayerPoint displacement = aStartPoint - aEndPoint;
+
+ ParentLayerPoint overscroll; // will be used outside monitor block
+
+ // If the direction of panning is reversed within the same input block,
+ // a later event in the block could potentially scroll an APZC earlier
+ // in the handoff chain, than an earlier event in the block (because
+ // the earlier APZC was scrolled to its extent in the original direction).
+ // We want to disallow this.
+ bool scrollThisApzc = false;
+ if (InputBlockState* block = GetCurrentInputBlock()) {
+ scrollThisApzc = !block->GetScrolledApzc() || block->IsDownchainOfScrolledApzc(this);
+ }
+
+ if (scrollThisApzc) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerPoint adjustedDisplacement;
+ bool forceVerticalOverscroll =
+ (aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
+ !mScrollMetadata.AllowVerticalScrollWithWheel());
+ bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y,
+ forceVerticalOverscroll);
+ bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
+
+ if (xChanged || yChanged) {
+ ScheduleComposite();
+ }
+
+ if (!IsZero(adjustedDisplacement)) {
+ ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
+ if (CancelableBlockState* block = GetCurrentInputBlock()) {
+ if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) {
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->SetScrollingRootContent(IsRootContent());
+ }
+ }
+ block->SetScrolledApzc(this);
+ }
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // Adjust the start point to reflect the consumed portion of the scroll.
+ aStartPoint = aEndPoint + overscroll;
+ } else {
+ overscroll = displacement;
+ }
+
+ // If we consumed the entire displacement as a normal scroll, great.
+ if (IsZero(overscroll)) {
+ return true;
+ }
+
+ if (AllowScrollHandoffInCurrentBlock()) {
+ // If there is overscroll, first try to hand it off to an APZC later
+ // in the handoff chain to consume (either as a normal scroll or as
+ // overscroll).
+ // Note: "+ overscroll" rather than "- overscroll" because "overscroll"
+ // is what's left of "displacement", and "displacement" is "start - end".
+ ++aOverscrollHandoffState.mChainIndex;
+ CallDispatchScroll(aStartPoint, aEndPoint, aOverscrollHandoffState);
+
+ overscroll = aStartPoint - aEndPoint;
+ if (IsZero(overscroll)) {
+ return true;
+ }
+ }
+
+ // If there is no APZC later in the handoff chain that accepted the
+ // overscroll, try to accept it ourselves. We only accept it if we
+ // are pannable.
+ APZC_LOG("%p taking overscroll during panning\n", this);
+ OverscrollForPanning(overscroll, aOverscrollHandoffState.mPanDistance);
+ aStartPoint = aEndPoint + overscroll;
+
+ return IsZero(overscroll);
+}
+
+void AsyncPanZoomController::OverscrollForPanning(ParentLayerPoint& aOverscroll,
+ const ScreenPoint& aPanDistance) {
+ // Only allow entering overscroll along an axis if the pan distance along
+ // that axis is greater than the pan distance along the other axis by a
+ // configurable factor. If we are already overscrolled, don't check this.
+ if (!IsOverscrolled()) {
+ if (aPanDistance.x < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.y) {
+ aOverscroll.x = 0;
+ }
+ if (aPanDistance.y < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.x) {
+ aOverscroll.y = 0;
+ }
+ }
+
+ OverscrollBy(aOverscroll);
+}
+
+void AsyncPanZoomController::OverscrollBy(ParentLayerPoint& aOverscroll) {
+ if (!gfxPrefs::APZOverscrollEnabled()) {
+ return;
+ }
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ // Do not go into overscroll in a direction in which we have no room to
+ // scroll to begin with.
+ bool xCanScroll = mX.CanScroll();
+ bool yCanScroll = mY.CanScroll();
+ bool xConsumed = FuzzyEqualsAdditive(aOverscroll.x, 0.0f, COORDINATE_EPSILON);
+ bool yConsumed = FuzzyEqualsAdditive(aOverscroll.y, 0.0f, COORDINATE_EPSILON);
+
+ bool shouldOverscrollX = xCanScroll && !xConsumed;
+ bool shouldOverscrollY = yCanScroll && !yConsumed;
+
+ mOverscrollEffect->ConsumeOverscroll(aOverscroll, shouldOverscrollX, shouldOverscrollY);
+}
+
+RefPtr<const OverscrollHandoffChain> AsyncPanZoomController::BuildOverscrollHandoffChain() {
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ return treeManagerLocal->BuildOverscrollHandoffChain(this);
+ }
+
+ // This APZC IsDestroyed(). To avoid callers having to special-case this
+ // scenario, just build a 1-element chain containing ourselves.
+ OverscrollHandoffChain* result = new OverscrollHandoffChain;
+ result->Add(this);
+ return result;
+}
+
+void AsyncPanZoomController::AcceptFling(FlingHandoffState& aHandoffState) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // We may have a pre-existing velocity for whatever reason (for example,
+ // a previously handed off fling). We don't want to clobber that.
+ APZC_LOG("%p accepting fling with velocity %s\n", this,
+ Stringify(aHandoffState.mVelocity).c_str());
+ if (mX.CanScroll()) {
+ mX.SetVelocity(mX.GetVelocity() + aHandoffState.mVelocity.x);
+ aHandoffState.mVelocity.x = 0;
+ }
+ if (mY.CanScroll()) {
+ mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y);
+ aHandoffState.mVelocity.y = 0;
+ }
+
+ // If there's a scroll snap point near the predicted fling destination,
+ // scroll there using a smooth scroll animation. Otherwise, start a
+ // fling animation.
+ ScrollSnapToDestination();
+ if (mState != SMOOTH_SCROLL) {
+ SetState(FLING);
+ FlingAnimation *fling = new FlingAnimation(*this,
+ GetPlatformSpecificState(),
+ aHandoffState.mChain,
+ aHandoffState.mIsHandoff,
+ aHandoffState.mScrolledApzc);
+ StartAnimation(fling);
+ }
+}
+
+bool AsyncPanZoomController::AttemptFling(FlingHandoffState& aHandoffState) {
+ // If we are pannable, take over the fling ourselves.
+ if (IsPannable()) {
+ AcceptFling(aHandoffState);
+ return true;
+ }
+
+ return false;
+}
+
+void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc) {
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ if (treeManagerLocal) {
+ FlingHandoffState handoffState{aVelocity,
+ aOverscrollHandoffChain,
+ true /* handoff */,
+ aScrolledApzc};
+ treeManagerLocal->DispatchFling(this, handoffState);
+ if (!IsZero(handoffState.mVelocity) && IsPannable() && gfxPrefs::APZOverscrollEnabled()) {
+ mOverscrollEffect->HandleFlingOverscroll(handoffState.mVelocity);
+ }
+ }
+}
+
+void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity) {
+ // We must call BuildOverscrollHandoffChain from this deferred callback
+ // function in order to avoid a deadlock when acquiring the tree lock.
+ HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr);
+}
+
+void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) {
+ if (mState == SMOOTH_SCROLL && mAnimation) {
+ APZC_LOG("%p updating destination on existing animation\n", this);
+ RefPtr<SmoothScrollAnimation> animation(
+ static_cast<SmoothScrollAnimation*>(mAnimation.get()));
+ animation->SetDestination(CSSPoint::ToAppUnits(aDestination));
+ } else {
+ CancelAnimation();
+ SetState(SMOOTH_SCROLL);
+ nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
+ // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
+ // appunits/second
+ nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
+ mY.GetVelocity())) * 1000.0f;
+ nsPoint destination = CSSPoint::ToAppUnits(aDestination);
+
+ StartAnimation(new SmoothScrollAnimation(*this,
+ initialPosition, initialVelocity,
+ destination,
+ gfxPrefs::ScrollBehaviorSpringConstant(),
+ gfxPrefs::ScrollBehaviorDampingRatio()));
+ }
+}
+
+void AsyncPanZoomController::StartOverscrollAnimation(const ParentLayerPoint& aVelocity) {
+ SetState(OVERSCROLL_ANIMATION);
+ StartAnimation(new OverscrollAnimation(*this, aVelocity));
+}
+
+void AsyncPanZoomController::CallDispatchScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState) {
+ // Make a local copy of the tree manager pointer and check if it's not
+ // null before calling DispatchScroll(). This is necessary because
+ // Destroy(), which nulls out mTreeManager, could be called concurrently.
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ if (!treeManagerLocal) {
+ return;
+ }
+ treeManagerLocal->DispatchScroll(this,
+ aStartPoint, aEndPoint,
+ aOverscrollHandoffState);
+}
+
+void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
+ ParentLayerPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
+ ParentLayerPoint touchPoint = GetFirstTouchPoint(aEvent);
+
+ ScreenPoint panDistance = ToScreenCoordinates(
+ ParentLayerPoint(mX.PanDistance(touchPoint.x),
+ mY.PanDistance(touchPoint.y)),
+ PanStart());
+ HandlePanningUpdate(panDistance);
+
+ UpdateWithTouchAtDevicePoint(aEvent);
+
+ if (prevTouchPoint != touchPoint) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ OverscrollHandoffState handoffState(
+ *GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
+ panDistance,
+ ScrollSource::Touch);
+ CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
+ }
+}
+
+ParentLayerPoint AsyncPanZoomController::GetFirstTouchPoint(const MultiTouchInput& aEvent) {
+ return ((SingleTouchData&)aEvent.mTouches[0]).mLocalScreenPoint;
+}
+
+void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mAnimation = aAnimation;
+ mLastSampleTime = GetFrameTime();
+ ScheduleComposite();
+}
+
+void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
+ SetState(NOTHING);
+ mAnimation = nullptr;
+ // Since there is no animation in progress now the axes should
+ // have no velocity either. If we are dropping the velocity from a non-zero
+ // value we should trigger a repaint as the displayport margins are dependent
+ // on the velocity and the last repaint request might not have good margins
+ // any more.
+ bool repaint = !IsZero(GetVelocityVector());
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ mX.SetAxisLocked(false);
+ mY.SetAxisLocked(false);
+ // Setting the state to nothing and cancelling the animation can
+ // preempt normal mechanisms for relieving overscroll, so we need to clear
+ // overscroll here.
+ if (!(aFlags & ExcludeOverscroll) && IsOverscrolled()) {
+ ClearOverscroll();
+ repaint = true;
+ }
+ // Similar to relieving overscroll, we also need to snap to any snap points
+ // if appropriate.
+ if (aFlags & CancelAnimationFlags::ScrollSnap) {
+ ScrollSnap();
+ }
+ if (repaint) {
+ RequestContentRepaint();
+ ScheduleComposite();
+ UpdateSharedCompositorFrameMetrics();
+ }
+}
+
+void AsyncPanZoomController::ClearOverscroll() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mX.ClearOverscroll();
+ mY.ClearOverscroll();
+}
+
+void AsyncPanZoomController::SetCompositorController(CompositorController* aCompositorController)
+{
+ mCompositorController = aCompositorController;
+}
+
+void AsyncPanZoomController::SetMetricsSharingController(MetricsSharingController* aMetricsSharingController)
+{
+ mMetricsSharingController = aMetricsSharingController;
+}
+
+void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSPoint adjustment =
+ ViewAs<ParentLayerPixel>(aShift, PixelCastJustification::ScreenIsParentLayerForRoot)
+ / mFrameMetrics.GetZoom();
+ APZC_LOG("%p adjusting scroll position by %s for surface shift\n",
+ this, Stringify(adjustment).c_str());
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ scrollOffset.y = mY.ClampOriginToScrollableRect(scrollOffset.y + adjustment.y);
+ scrollOffset.x = mX.ClampOriginToScrollableRect(scrollOffset.x + adjustment.x);
+ mFrameMetrics.SetScrollOffset(scrollOffset);
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
+ mFrameMetrics.ScrollBy(aOffset);
+}
+
+void AsyncPanZoomController::ScaleWithFocus(float aScale,
+ const CSSPoint& aFocus) {
+ mFrameMetrics.ZoomBy(aScale);
+ // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
+ // at the same position on the screen before and after the change in zoom. The below code
+ // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
+ // in-depth explanation of how.
+ mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
+}
+
+/**
+ * Enlarges the displayport along both axes based on the velocity.
+ */
+static CSSSize
+CalculateDisplayPortSize(const CSSSize& aCompositionSize,
+ const CSSPoint& aVelocity)
+{
+ bool xIsStationarySpeed = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed();
+ bool yIsStationarySpeed = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed();
+ float xMultiplier = xIsStationarySpeed
+ ? gfxPrefs::APZXStationarySizeMultiplier()
+ : gfxPrefs::APZXSkateSizeMultiplier();
+ float yMultiplier = yIsStationarySpeed
+ ? gfxPrefs::APZYStationarySizeMultiplier()
+ : gfxPrefs::APZYSkateSizeMultiplier();
+
+ if (IsHighMemSystem() && !xIsStationarySpeed) {
+ xMultiplier += gfxPrefs::APZXSkateHighMemAdjust();
+ }
+
+ if (IsHighMemSystem() && !yIsStationarySpeed) {
+ yMultiplier += gfxPrefs::APZYSkateHighMemAdjust();
+ }
+
+ return aCompositionSize * CSSSize(xMultiplier, yMultiplier);
+}
+
+/**
+ * Ensures that the displayport is at least as large as the visible area
+ * inflated by the danger zone. If this is not the case then the
+ * "AboutToCheckerboard" function in TiledContentClient.cpp will return true
+ * even in the stable state.
+ */
+static CSSSize
+ExpandDisplayPortToDangerZone(const CSSSize& aDisplayPortSize,
+ const FrameMetrics& aFrameMetrics)
+{
+ CSSSize dangerZone(0.0f, 0.0f);
+ if (aFrameMetrics.LayersPixelsPerCSSPixel().xScale != 0 &&
+ aFrameMetrics.LayersPixelsPerCSSPixel().yScale != 0) {
+ dangerZone = LayerSize(
+ gfxPrefs::APZDangerZoneX(),
+ gfxPrefs::APZDangerZoneY()) / aFrameMetrics.LayersPixelsPerCSSPixel();
+ }
+ const CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
+
+ const float xSize = std::max(aDisplayPortSize.width,
+ compositionSize.width + (2 * dangerZone.width));
+
+ const float ySize = std::max(aDisplayPortSize.height,
+ compositionSize.height + (2 * dangerZone.height));
+
+ return CSSSize(xSize, ySize);
+}
+
+/**
+ * Attempts to redistribute any area in the displayport that would get clipped
+ * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
+ * other axis, while maintaining total displayport area.
+ */
+static void
+RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
+ const CSSRect& aScrollableRect)
+{
+ // As aDisplayPortSize.height * aDisplayPortSize.width does not change,
+ // we are just scaling by the ratio and its inverse.
+ if (aDisplayPortSize.height > aScrollableRect.height) {
+ aDisplayPortSize.width *= (aDisplayPortSize.height / aScrollableRect.height);
+ aDisplayPortSize.height = aScrollableRect.height;
+ } else if (aDisplayPortSize.width > aScrollableRect.width) {
+ aDisplayPortSize.height *= (aDisplayPortSize.width / aScrollableRect.width);
+ aDisplayPortSize.width = aScrollableRect.width;
+ }
+}
+
+/* static */
+const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ if (aFrameMetrics.IsScrollInfoLayer()) {
+ // Don't compute margins. Since we can't asynchronously scroll this frame,
+ // we don't want to paint anything more than the composition bounds.
+ return ScreenMargin();
+ }
+
+ CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
+ CSSPoint velocity;
+ if (aFrameMetrics.GetZoom() != CSSToParentLayerScale2D(0, 0)) {
+ velocity = aVelocity / aFrameMetrics.GetZoom(); // avoid division by zero
+ }
+ CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
+
+ // Calculate the displayport size based on how fast we're moving along each axis.
+ CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
+
+ displayPortSize = ExpandDisplayPortToDangerZone(displayPortSize, aFrameMetrics);
+
+ if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
+ RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
+ }
+
+ // We calculate a "displayport" here which is relative to the scroll offset.
+ // Note that the scroll offset we have here in the APZ code may not be the
+ // same as the base rect that gets used on the layout side when the displayport
+ // margins are actually applied, so it is important to only consider the
+ // displayport as margins relative to a scroll offset rather than relative to
+ // something more unchanging like the scrollable rect origin.
+
+ // Center the displayport based on its expansion over the composition size.
+ CSSRect displayPort((compositionSize.width - displayPortSize.width) / 2.0f,
+ (compositionSize.height - displayPortSize.height) / 2.0f,
+ displayPortSize.width, displayPortSize.height);
+
+ // Offset the displayport, depending on how fast we're moving and the
+ // estimated time it takes to paint, to try to minimise checkerboarding.
+ float paintFactor = kDefaultEstimatedPaintDurationMs;
+ displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
+
+ APZC_LOG_FM(aFrameMetrics,
+ "Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
+ displayPort.x, displayPort.y, displayPort.width, displayPort.height,
+ ToString(aVelocity).c_str(), paintFactor);
+
+ CSSMargin cssMargins;
+ cssMargins.left = -displayPort.x;
+ cssMargins.top = -displayPort.y;
+ cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
+ cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
+
+ return cssMargins * aFrameMetrics.DisplayportPixelsPerCSSPixel();
+}
+
+void AsyncPanZoomController::ScheduleComposite() {
+ if (mCompositorController) {
+ mCompositorController->ScheduleRenderOnCompositorThread();
+ }
+}
+
+void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
+ ScheduleComposite();
+ RequestContentRepaint();
+}
+
+void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
+ APZC_LOG("%p flushing repaint for new input block\n", this);
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+bool AsyncPanZoomController::SnapBackIfOverscrolled() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ // It's possible that we're already in the middle of an overscroll
+ // animation - if so, don't start a new one.
+ if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) {
+ APZC_LOG("%p is overscrolled, starting snap-back\n", this);
+ StartOverscrollAnimation(ParentLayerPoint(0, 0));
+ return true;
+ }
+ // If we don't kick off an overscroll animation, we still need to ask the
+ // main thread to snap to any nearby snap points, assuming we haven't already
+ // done so when we started this fling
+ if (mState != FLING) {
+ ScrollSnap();
+ }
+ return false;
+}
+
+bool AsyncPanZoomController::IsFlingingFast() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mState == FLING &&
+ GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+ APZC_LOG("%p is moving fast\n", this);
+ return true;
+ }
+ return false;
+}
+
+bool AsyncPanZoomController::IsPannable() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mX.CanScroll() || mY.CanScroll();
+}
+
+int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ return listener ? listener->GetLastTouchIdentifier() : -1;
+}
+
+void AsyncPanZoomController::RequestContentRepaint(bool aUserAction) {
+ // Reinvoke this method on the repaint thread if it's not there already. It's
+ // important to do this before the call to CalculatePendingDisplayPort, so
+ // that CalculatePendingDisplayPort uses the most recent available version of
+ // mFrameMetrics, just before the paint request is dispatched to content.
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ if (!controller->IsRepaintThread()) {
+ // use the local variable to resolve the function overload.
+ auto func = static_cast<void (AsyncPanZoomController::*)(bool)>
+ (&AsyncPanZoomController::RequestContentRepaint);
+ controller->DispatchToRepaintThread(NewRunnableMethod<bool>(this, func, aUserAction));
+ return;
+ }
+
+ MOZ_ASSERT(controller->IsRepaintThread());
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ParentLayerPoint velocity = GetVelocityVector();
+ mFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(mFrameMetrics, velocity));
+ mFrameMetrics.SetUseDisplayPortMargins(true);
+ mFrameMetrics.SetPaintRequestTime(TimeStamp::Now());
+ mFrameMetrics.SetRepaintDrivenByUserAction(aUserAction);
+ RequestContentRepaint(mFrameMetrics, velocity);
+}
+
+/*static*/ CSSRect
+GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
+{
+ // This computation is based on what happens in CalculatePendingDisplayPort. If that
+ // changes then this might need to change too
+ CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
+ aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
+ baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
+ return baseRect;
+}
+
+void
+AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ MOZ_ASSERT(controller->IsRepaintThread());
+
+ // If we're trying to paint what we already think is painted, discard this
+ // request since it's a pointless paint.
+ ScreenMargin marginDelta = (mLastPaintRequestMetrics.GetDisplayPortMargins()
+ - aFrameMetrics.GetDisplayPortMargins());
+ if (fabsf(marginDelta.left) < EPSILON &&
+ fabsf(marginDelta.top) < EPSILON &&
+ fabsf(marginDelta.right) < EPSILON &&
+ fabsf(marginDelta.bottom) < EPSILON &&
+ fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
+ aFrameMetrics.GetScrollOffset().x) < EPSILON &&
+ fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
+ aFrameMetrics.GetScrollOffset().y) < EPSILON &&
+ aFrameMetrics.GetPresShellResolution() == mLastPaintRequestMetrics.GetPresShellResolution() &&
+ aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
+ fabsf(aFrameMetrics.GetViewport().width -
+ mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
+ fabsf(aFrameMetrics.GetViewport().height -
+ mLastPaintRequestMetrics.GetViewport().height) < EPSILON &&
+ aFrameMetrics.GetScrollGeneration() ==
+ mLastPaintRequestMetrics.GetScrollGeneration() &&
+ aFrameMetrics.GetScrollUpdateType() ==
+ mLastPaintRequestMetrics.GetScrollUpdateType()) {
+ return;
+ }
+
+ APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
+ std::stringstream info;
+ info << " velocity " << aVelocity;
+ std::string str = info.str();
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::RequestedDisplayPort, GetDisplayPortRect(aFrameMetrics),
+ str);
+ }
+ }
+
+ MOZ_ASSERT(aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eNone ||
+ aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eUserAction);
+ controller->RequestContentRepaint(aFrameMetrics);
+ mExpectedGeckoMetrics = aFrameMetrics;
+ mLastPaintRequestMetrics = aFrameMetrics;
+}
+
+bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
+ nsTArray<RefPtr<Runnable>>* aOutDeferredTasks)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // This function may get called multiple with the same sample time, because
+ // there may be multiple layers with this APZC, and each layer invokes this
+ // function during composition. However we only want to do one animation step
+ // per composition so we need to deduplicate these calls first.
+ if (mLastSampleTime == aSampleTime) {
+ return false;
+ }
+ TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime;
+ mLastSampleTime = aSampleTime;
+
+ if (mAnimation) {
+ bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta);
+ bool wantsRepaints = mAnimation->WantsRepaints();
+ *aOutDeferredTasks = mAnimation->TakeDeferredTasks();
+ if (!continueAnimation) {
+ mAnimation = nullptr;
+ SetState(NOTHING);
+ }
+ // Request a repaint at the end of the animation in case something such as a
+ // call to NotifyLayersUpdated was invoked during the animation and Gecko's
+ // current state is some intermediate point of the animation.
+ if (!continueAnimation || wantsRepaints) {
+ RequestContentRepaint();
+ }
+ UpdateSharedCompositorFrameMetrics();
+ return true;
+ }
+ return false;
+}
+
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetOverscrollTransform(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return AsyncTransformComponentMatrix();
+ }
+
+ if (!IsOverscrolled()) {
+ return AsyncTransformComponentMatrix();
+ }
+
+ // The overscroll effect is a uniform stretch along the overscrolled axis,
+ // with the edge of the content where we have reached the end of the
+ // scrollable area pinned into place.
+
+ // The kStretchFactor parameter determines how much overscroll can stretch the
+ // content.
+ const float kStretchFactor = gfxPrefs::APZOverscrollStretchFactor();
+
+ // Compute the amount of the stretch along each axis. The stretch is
+ // proportional to the amount by which we are overscrolled along that axis.
+ ParentLayerSize compositionSize(mX.GetCompositionLength(), mY.GetCompositionLength());
+ float scaleX = 1 + kStretchFactor * fabsf(mX.GetOverscroll()) / mX.GetCompositionLength();
+ float scaleY = 1 + kStretchFactor * fabsf(mY.GetOverscroll()) / mY.GetCompositionLength();
+
+ // The scale is applied relative to the origin of the composition bounds, i.e.
+ // it keeps the top-left corner of the content in place. This is fine if we
+ // are overscrolling at the top or on the left, but if we are overscrolling
+ // at the bottom or on the right, we want the bottom or right edge of the
+ // content to stay in place instead, so we add a translation to compensate.
+ ParentLayerPoint translation;
+ bool overscrolledOnRight = mX.GetOverscroll() > 0;
+ if (overscrolledOnRight) {
+ ParentLayerCoord overscrolledCompositionWidth = scaleX * compositionSize.width;
+ ParentLayerCoord extraCompositionWidth = overscrolledCompositionWidth - compositionSize.width;
+ translation.x = -extraCompositionWidth;
+ }
+ bool overscrolledAtBottom = mY.GetOverscroll() > 0;
+ if (overscrolledAtBottom) {
+ ParentLayerCoord overscrolledCompositionHeight = scaleY * compositionSize.height;
+ ParentLayerCoord extraCompositionHeight = overscrolledCompositionHeight - compositionSize.height;
+ translation.y = -extraCompositionHeight;
+ }
+
+ // Combine the transformations into a matrix.
+ return AsyncTransformComponentMatrix::Scaling(scaleX, scaleY, 1)
+ .PostTranslate(translation.x, translation.y, 0);
+}
+
+bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // Don't send any state-change notifications until the end of the function,
+ // because we may go through some intermediate states while we finish
+ // animations and start new ones.
+ StateChangeNotificationBlocker blocker(this);
+
+ // The eventual return value of this function. The compositor needs to know
+ // whether or not to advance by a frame as soon as it can. For example, if a
+ // fling is happening, it has to keep compositing so that the animation is
+ // smooth. If an animation frame is requested, it is the compositor's
+ // responsibility to schedule a composite.
+ mAsyncTransformAppliedToContent = false;
+ bool requestAnimationFrame = false;
+ nsTArray<RefPtr<Runnable>> deferredTasks;
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ requestAnimationFrame = UpdateAnimation(aSampleTime, &deferredTasks);
+
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent) {
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::UserVisible,
+ CSSRect(mFrameMetrics.GetScrollOffset(),
+ mFrameMetrics.CalculateCompositedSizeInCssPixels()));
+ }
+ }
+ }
+
+ // Execute any deferred tasks queued up by mAnimation's Sample() (called by
+ // UpdateAnimation()). This needs to be done after the monitor is released
+ // since the tasks are allowed to call APZCTreeManager methods which can grab
+ // the tree lock.
+ for (uint32_t i = 0; i < deferredTasks.Length(); ++i) {
+ deferredTasks[i]->Run();
+ deferredTasks[i] = nullptr;
+ }
+
+ // One of the deferred tasks may have started a new animation. In this case,
+ // we want to ask the compositor to schedule a new composite.
+ requestAnimationFrame |= (mAnimation != nullptr);
+
+ return requestAnimationFrame;
+}
+
+ParentLayerPoint
+AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
+ }
+
+ return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset)
+ * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
+}
+
+AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncTransform(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return AsyncTransform();
+ }
+
+ CSSPoint lastPaintScrollOffset;
+ if (mLastContentPaintMetrics.IsScrollable()) {
+ lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
+ }
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
+ mTestAsyncScrollOffset;
+
+ // If checkerboarding has been disallowed, clamp the scroll position to stay
+ // within rendered content.
+ if (!gfxPrefs::APZAllowCheckerboarding() &&
+ !mLastContentPaintMetrics.GetDisplayPort().IsEmpty()) {
+ CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
+ CSSPoint maxScrollOffset = lastPaintScrollOffset +
+ CSSPoint(mLastContentPaintMetrics.GetDisplayPort().XMost() - compositedSize.width,
+ mLastContentPaintMetrics.GetDisplayPort().YMost() - compositedSize.height);
+ CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.GetDisplayPort().TopLeft();
+
+ if (minScrollOffset.x < maxScrollOffset.x) {
+ currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
+ }
+ if (minScrollOffset.y < maxScrollOffset.y) {
+ currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
+ }
+ }
+
+ ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
+ * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
+
+ return AsyncTransform(
+ LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale),
+ -translation);
+}
+
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const
+{
+ return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
+ * GetOverscrollTransform(aMode);
+}
+
+Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ LayerPoint scrollChange =
+ (mLastContentPaintMetrics.GetScrollOffset() - mExpectedGeckoMetrics.GetScrollOffset())
+ * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
+ * mLastContentPaintMetrics.GetCumulativeResolution();
+
+ // We're interested in the async zoom change. Factor out the content scale
+ // that may change when dragging the window to a monitor with a different
+ // content scale.
+ LayoutDeviceToParentLayerScale2D lastContentZoom =
+ mLastContentPaintMetrics.GetZoom() / mLastContentPaintMetrics.GetDevPixelsPerCSSPixel();
+ LayoutDeviceToParentLayerScale2D lastDispatchedZoom =
+ mExpectedGeckoMetrics.GetZoom() / mExpectedGeckoMetrics.GetDevPixelsPerCSSPixel();
+ gfxSize zoomChange = lastContentZoom / lastDispatchedZoom;
+
+ return Matrix4x4::Translation(scrollChange.x, scrollChange.y, 0).
+ PostScale(zoomChange.width, zoomChange.height, 1);
+}
+
+uint32_t
+AsyncPanZoomController::GetCheckerboardMagnitude() const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
+ CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
+ CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
+
+ CSSIntRegion checkerboard;
+ // Round so as to minimize checkerboarding; if we're only showing fractional
+ // pixels of checkerboarding it's not really worth counting
+ checkerboard.Sub(RoundedIn(visible), RoundedOut(painted));
+ return checkerboard.Area();
+}
+
+void
+AsyncPanZoomController::ReportCheckerboard(const TimeStamp& aSampleTime)
+{
+ if (mLastCheckerboardReport == aSampleTime) {
+ // This function will get called multiple times for each APZC on a single
+ // composite (once for each layer it is attached to). Only report the
+ // checkerboard once per composite though.
+ return;
+ }
+ mLastCheckerboardReport = aSampleTime;
+
+ bool recordTrace = gfxPrefs::APZRecordCheckerboarding();
+ uint32_t magnitude = GetCheckerboardMagnitude();
+
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (!mCheckerboardEvent && recordTrace) {
+ mCheckerboardEvent = MakeUnique<CheckerboardEvent>(recordTrace);
+ }
+ mPotentialCheckerboardTracker.InTransform(IsTransformingState(mState));
+ if (magnitude) {
+ mPotentialCheckerboardTracker.CheckerboardSeen();
+ }
+ UpdateCheckerboardEvent(lock, magnitude);
+}
+
+void
+AsyncPanZoomController::UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+ uint32_t aMagnitude)
+{
+ if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(aMagnitude)) {
+ // This checkerboard event is done.
+ mPotentialCheckerboardTracker.CheckerboardDone();
+
+ if (gfxPrefs::APZRecordCheckerboarding()) {
+ // if the pref is enabled, also send it to the storage class. it may be
+ // chosen for public display on about:checkerboard, the hall of fame for
+ // checkerboard events.
+ uint32_t severity = mCheckerboardEvent->GetSeverity();
+ std::string log = mCheckerboardEvent->GetLog();
+ CheckerboardEventStorage::Report(severity, log);
+ }
+ mCheckerboardEvent = nullptr;
+ }
+}
+
+void
+AsyncPanZoomController::FlushActiveCheckerboardReport()
+{
+ MutexAutoLock lock(mCheckerboardEventLock);
+ // Pretend like we got a frame with 0 pixels checkerboarded. This will
+ // terminate the checkerboard event and flush it out
+ UpdateCheckerboardEvent(lock, 0);
+}
+
+bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (!gfxPrefs::APZAllowCheckerboarding() || mScrollMetadata.IsApzForceDisabled()) {
+ return false;
+ }
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
+ CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
+ painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1))); // fuzz for rounding error
+ CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
+ if (painted.Contains(visible)) {
+ return false;
+ }
+ APZC_LOG_FM(mFrameMetrics, "%p is currently checkerboarding (painted %s visble %s)",
+ this, Stringify(painted).c_str(), Stringify(visible).c_str());
+ return true;
+}
+
+void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata,
+ bool aIsFirstPaint,
+ bool aThisLayerTreeUpdated)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ bool isDefault = mScrollMetadata.IsDefault();
+
+ const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics();
+
+ if ((aScrollMetadata == mLastContentPaintMetadata) && !isDefault) {
+ // No new information here, skip it.
+ APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
+ return;
+ }
+
+ // If the mFrameMetrics scroll offset is different from the last scroll offset
+ // that the main-thread sent us, then we know that the user has been doing
+ // something that triggers a scroll. This check is the APZ equivalent of the
+ // check on the main-thread at
+ // https://hg.mozilla.org/mozilla-central/file/97a52326b06a/layout/generic/nsGfxScrollFrame.cpp#l4050
+ // There is code below (the use site of userScrolled) that prevents a restored-
+ // scroll-position update from overwriting a user scroll, again equivalent to
+ // how the main thread code does the same thing.
+ CSSPoint lastScrollOffset = mLastContentPaintMetadata.GetMetrics().GetScrollOffset();
+ bool userScrolled =
+ !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().x, lastScrollOffset.x) ||
+ !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().y, lastScrollOffset.y);
+
+ if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) {
+ mLastContentPaintMetadata = aScrollMetadata;
+ }
+
+ mScrollMetadata.SetScrollParentId(aScrollMetadata.GetScrollParentId());
+ APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
+ this, aIsFirstPaint, aThisLayerTreeUpdated);
+
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
+ std::string str;
+ if (aThisLayerTreeUpdated) {
+ if (!aLayerMetrics.GetPaintRequestTime().IsNull()) {
+ // Note that we might get the paint request time as non-null, but with
+ // aThisLayerTreeUpdated false. That can happen if we get a layer transaction
+ // from a different process right after we get the layer transaction with
+ // aThisLayerTreeUpdated == true. In this case we want to ignore the
+ // paint request time because it was already dumped in the previous layer
+ // transaction.
+ TimeDuration paintTime = TimeStamp::Now() - aLayerMetrics.GetPaintRequestTime();
+ std::stringstream info;
+ info << " painttime " << paintTime.ToMilliseconds();
+ str = info.str();
+ } else {
+ // This might be indicative of a wasted paint particularly if it happens
+ // during a checkerboard event.
+ str = " (this layertree updated)";
+ }
+ }
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::Page, aLayerMetrics.GetScrollableRect());
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::PaintedDisplayPort,
+ aLayerMetrics.GetDisplayPort() + aLayerMetrics.GetScrollOffset(),
+ str);
+ if (!aLayerMetrics.GetCriticalDisplayPort().IsEmpty()) {
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::PaintedCriticalDisplayPort,
+ aLayerMetrics.GetCriticalDisplayPort() + aLayerMetrics.GetScrollOffset());
+ }
+ }
+ }
+
+ bool needContentRepaint = false;
+ bool viewportUpdated = false;
+ if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().width, mFrameMetrics.GetCompositionBounds().width) &&
+ FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().height, mFrameMetrics.GetCompositionBounds().height)) {
+ // Remote content has sync'd up to the composition geometry
+ // change, so we can accept the viewport it's calculated.
+ if (mFrameMetrics.GetViewport().width != aLayerMetrics.GetViewport().width ||
+ mFrameMetrics.GetViewport().height != aLayerMetrics.GetViewport().height) {
+ needContentRepaint = true;
+ viewportUpdated = true;
+ }
+ mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
+ }
+
+ // If the layers update was not triggered by our own repaint request, then
+ // we want to take the new scroll offset. Check the scroll generation as well
+ // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
+ // update message.
+ bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
+ && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
+
+ if (scrollOffsetUpdated && userScrolled &&
+ aLayerMetrics.GetScrollUpdateType() == FrameMetrics::ScrollOffsetUpdateType::eRestore) {
+ APZC_LOG("%p dropping scroll update of type eRestore because of user scroll\n", this);
+ scrollOffsetUpdated = false;
+ }
+
+ bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
+ && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
+
+ // TODO if we're in a drag and scrollOffsetUpdated is set then we want to
+ // ignore it
+
+ if ((aIsFirstPaint && aThisLayerTreeUpdated) || isDefault) {
+ // Initialize our internal state to something sane when the content
+ // that was just painted is something we knew nothing about previously
+ CancelAnimation();
+
+ mScrollMetadata = aScrollMetadata;
+ mExpectedGeckoMetrics = aLayerMetrics;
+ ShareCompositorFrameMetrics();
+
+ if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
+ // A non-zero display port margin here indicates a displayport has
+ // been set by a previous APZC for the content at this guid. The
+ // scrollable rect may have changed since then, making the margins
+ // wrong, so we need to calculate a new display port.
+ APZC_LOG("%p detected non-empty margins which probably need updating\n", this);
+ needContentRepaint = true;
+ }
+ } else {
+ // If we're not taking the aLayerMetrics wholesale we still need to pull
+ // in some things into our local mFrameMetrics because these things are
+ // determined by Gecko and our copy in mFrameMetrics may be stale.
+
+ if (FuzzyEqualsAdditive(mFrameMetrics.GetCompositionBounds().width, aLayerMetrics.GetCompositionBounds().width) &&
+ mFrameMetrics.GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel() &&
+ !viewportUpdated) {
+ // Any change to the pres shell resolution was requested by APZ and is
+ // already included in our zoom; however, other components of the
+ // cumulative resolution (a parent document's pres-shell resolution, or
+ // the css-driven resolution) may have changed, and we need to update
+ // our zoom to reflect that. Note that we can't just take
+ // aLayerMetrics.mZoom because the APZ may have additional async zoom
+ // since the repaint request.
+ gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution()
+ / mFrameMetrics.GetCumulativeResolution();
+ float presShellResolutionChange = aLayerMetrics.GetPresShellResolution()
+ / mFrameMetrics.GetPresShellResolution();
+ if (presShellResolutionChange != 1.0f) {
+ needContentRepaint = true;
+ }
+ mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
+ } else {
+ // Take the new zoom as either device scale or composition width or
+ // viewport size got changed (e.g. due to orientation change, or content
+ // changing the meta-viewport tag).
+ mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
+ mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel());
+ }
+ if (!mFrameMetrics.GetScrollableRect().IsEqualEdges(aLayerMetrics.GetScrollableRect())) {
+ mFrameMetrics.SetScrollableRect(aLayerMetrics.GetScrollableRect());
+ needContentRepaint = true;
+ }
+ mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
+ mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
+ mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
+ mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
+ mScrollMetadata.SetHasScrollgrab(aScrollMetadata.GetHasScrollgrab());
+ mScrollMetadata.SetLineScrollAmount(aScrollMetadata.GetLineScrollAmount());
+ mScrollMetadata.SetPageScrollAmount(aScrollMetadata.GetPageScrollAmount());
+ mScrollMetadata.SetSnapInfo(ScrollSnapInfo(aScrollMetadata.GetSnapInfo()));
+ // The scroll clip can differ between layers associated a given scroll frame,
+ // so APZC (which keeps a single copy of ScrollMetadata per scroll frame)
+ // has no business using it.
+ mScrollMetadata.SetScrollClip(Nothing());
+ mScrollMetadata.SetIsLayersIdRoot(aScrollMetadata.IsLayersIdRoot());
+ mScrollMetadata.SetUsesContainerScrolling(aScrollMetadata.UsesContainerScrolling());
+ mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer());
+ mScrollMetadata.SetForceDisableApz(aScrollMetadata.IsApzForceDisabled());
+
+ if (scrollOffsetUpdated) {
+ APZC_LOG("%p updating scroll offset from %s to %s\n", this,
+ ToString(mFrameMetrics.GetScrollOffset()).c_str(),
+ ToString(aLayerMetrics.GetScrollOffset()).c_str());
+
+ // Send an acknowledgement with the new scroll generation so that any
+ // repaint requests later in this function go through.
+ // Because of the scroll generation update, any inflight paint requests are
+ // going to be ignored by layout, and so mExpectedGeckoMetrics
+ // becomes incorrect for the purposes of calculating the LD transform. To
+ // correct this we need to update mExpectedGeckoMetrics to be the
+ // last thing we know was painted by Gecko.
+ mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
+ mExpectedGeckoMetrics = aLayerMetrics;
+
+ // Cancel the animation (which might also trigger a repaint request)
+ // after we update the scroll offset above. Otherwise we can be left
+ // in a state where things are out of sync.
+ CancelAnimation();
+
+ // Since the scroll offset has changed, we need to recompute the
+ // displayport margins and send them to layout. Otherwise there might be
+ // scenarios where for example we scroll from the top of a page (where the
+ // top displayport margin is zero) to the bottom of a page, which will
+ // result in a displayport that doesn't extend upwards at all.
+ // Note that even if the CancelAnimation call above requested a repaint
+ // this is fine because we already have repaint request deduplication.
+ needContentRepaint = true;
+ }
+ }
+
+ if (smoothScrollRequested) {
+ // A smooth scroll has been requested for animation on the compositor
+ // thread. This flag will be reset by the main thread when it receives
+ // the scroll update acknowledgement.
+
+ APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this,
+ Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
+ Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(),
+ mState);
+
+ // See comment on the similar code in the |if (scrollOffsetUpdated)| block
+ // above.
+ mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
+ needContentRepaint = true;
+ mExpectedGeckoMetrics = aLayerMetrics;
+
+ SmoothScrollTo(mFrameMetrics.GetSmoothScrollOffset());
+ }
+
+ if (needContentRepaint) {
+ // This repaint request is not driven by a user action on the APZ side
+ RequestContentRepaint(false);
+ }
+ UpdateSharedCompositorFrameMetrics();
+}
+
+const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const {
+ mMonitor.AssertCurrentThreadIn();
+ return mFrameMetrics;
+}
+
+APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
+ mMonitor.AssertNotCurrentThreadIn();
+ return mTreeManager;
+}
+
+void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
+ if (!aRect.IsFinite()) {
+ NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...");
+ return;
+ } else if (aRect.IsEmpty() && (aFlags & DISABLE_ZOOM_OUT)) {
+ // Double-tap-to-zooming uses an empty rect to mean "zoom out".
+ // If zooming out is disabled, an empty rect is nonsensical
+ // and will produce undesirable scrolling.
+ NS_WARNING("ZoomToRect got called with an empty rect and zoom out disabled; ignoring...");
+ return;
+ }
+
+ // Only the root APZC is zoomable, and the root APZC is not allowed to have
+ // different x and y scales. If it did, the calculations in this function
+ // would have to be adjusted (as e.g. it would no longer be valid to take
+ // the minimum or maximum of the ratios of the widths and heights of the
+ // page rect and the composition bounds).
+ MOZ_ASSERT(mFrameMetrics.IsRootContent());
+ MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
+
+ SetState(ANIMATING_ZOOM);
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerRect compositionBounds = mFrameMetrics.GetCompositionBounds();
+ CSSRect cssPageRect = mFrameMetrics.GetScrollableRect();
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoom().ToScaleFactor();
+ CSSToParentLayerScale targetZoom;
+
+ // The minimum zoom to prevent over-zoom-out.
+ // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
+ // then the CSS content rect, in layers pixels, will be smaller than the
+ // composition bounds. If this happens, we can't fill the target composited
+ // area with this frame.
+ CSSToParentLayerScale localMinZoom(std::max(mZoomConstraints.mMinZoom.scale,
+ std::max(compositionBounds.width / cssPageRect.width,
+ compositionBounds.height / cssPageRect.height)));
+ CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom;
+
+ if (!aRect.IsEmpty()) {
+ // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
+ aRect = aRect.Intersect(cssPageRect);
+ targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+ compositionBounds.height / aRect.height));
+ }
+
+ // 1. If the rect is empty, the content-side logic for handling a double-tap
+ // requested that we zoom out.
+ // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
+ // 3. currentZoom is equal to localMinZoom and user still double-tapping it
+ // Treat these three cases as a request to zoom out as much as possible.
+ bool zoomOut;
+ if (aFlags & DISABLE_ZOOM_OUT) {
+ zoomOut = false;
+ } else {
+ zoomOut = aRect.IsEmpty() ||
+ (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
+ (currentZoom == localMinZoom && targetZoom <= localMinZoom);
+ }
+
+ if (zoomOut) {
+ CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
+ float y = scrollOffset.y;
+ float newHeight =
+ cssPageRect.width * (compositedSize.height / compositedSize.width);
+ float dh = compositedSize.height - newHeight;
+
+ aRect = CSSRect(0.0f,
+ y + dh/2,
+ cssPageRect.width,
+ newHeight);
+ aRect = aRect.Intersect(cssPageRect);
+ targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+ compositionBounds.height / aRect.height));
+ }
+
+ targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
+ FrameMetrics endZoomToMetrics = mFrameMetrics;
+ if (aFlags & PAN_INTO_VIEW_ONLY) {
+ targetZoom = currentZoom;
+ } else if(aFlags & ONLY_ZOOM_TO_DEFAULT_SCALE) {
+ CSSToParentLayerScale zoomAtDefaultScale =
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * LayoutDeviceToParentLayerScale(1.0);
+ if (targetZoom.scale > zoomAtDefaultScale.scale) {
+ // Only change the zoom if we are less than the default zoom
+ if (currentZoom.scale < zoomAtDefaultScale.scale) {
+ targetZoom = zoomAtDefaultScale;
+ } else {
+ targetZoom = currentZoom;
+ }
+ }
+ }
+ endZoomToMetrics.SetZoom(CSSToParentLayerScale2D(targetZoom));
+
+ // Adjust the zoomToRect to a sensible position to prevent overscrolling.
+ CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
+
+ // Vertically center the zoomed element in the screen.
+ if (!zoomOut && (sizeAfterZoom.height > aRect.height)) {
+ aRect.y -= (sizeAfterZoom.height - aRect.height) * 0.5f;
+ if (aRect.y < 0.0f) {
+ aRect.y = 0.0f;
+ }
+ }
+
+ // If either of these conditions are met, the page will be
+ // overscrolled after zoomed
+ if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
+ aRect.y = cssPageRect.height - sizeAfterZoom.height;
+ aRect.y = aRect.y > 0 ? aRect.y : 0;
+ }
+ if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
+ aRect.x = cssPageRect.width - sizeAfterZoom.width;
+ aRect.x = aRect.x > 0 ? aRect.x : 0;
+ }
+
+ endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
+
+ StartAnimation(new ZoomAnimation(
+ mFrameMetrics.GetScrollOffset(),
+ mFrameMetrics.GetZoom(),
+ endZoomToMetrics.GetScrollOffset(),
+ endZoomToMetrics.GetZoom()));
+
+ // Schedule a repaint now, so the new displayport will be painted before the
+ // animation finishes.
+ ParentLayerPoint velocity(0, 0);
+ endZoomToMetrics.SetDisplayPortMargins(
+ CalculatePendingDisplayPort(endZoomToMetrics, velocity));
+ endZoomToMetrics.SetUseDisplayPortMargins(true);
+ endZoomToMetrics.SetPaintRequestTime(TimeStamp::Now());
+ endZoomToMetrics.SetRepaintDrivenByUserAction(true);
+
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ if (controller->IsRepaintThread()) {
+ RequestContentRepaint(endZoomToMetrics, velocity);
+ } else {
+ // use a local var to resolve the function overload
+ auto func = static_cast<void (AsyncPanZoomController::*)(const FrameMetrics&, const ParentLayerPoint&)>
+ (&AsyncPanZoomController::RequestContentRepaint);
+ controller->DispatchToRepaintThread(
+ NewRunnableMethod<FrameMetrics, ParentLayerPoint>(
+ this, func, endZoomToMetrics, velocity));
+ }
+ }
+}
+
+CancelableBlockState*
+AsyncPanZoomController::GetCurrentInputBlock() const
+{
+ return GetInputQueue()->GetCurrentBlock();
+}
+
+TouchBlockState*
+AsyncPanZoomController::GetCurrentTouchBlock() const
+{
+ return GetInputQueue()->GetCurrentTouchBlock();
+}
+
+PanGestureBlockState*
+AsyncPanZoomController::GetCurrentPanGestureBlock() const
+{
+ return GetInputQueue()->GetCurrentPanGestureBlock();
+}
+
+void
+AsyncPanZoomController::ResetTouchInputState()
+{
+ MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ if (listener) {
+ listener->HandleInputEvent(cancel);
+ }
+ CancelAnimationAndGestureState();
+ // Clear overscroll along the entire handoff chain, in case an APZC
+ // later in the chain is overscrolled.
+ if (TouchBlockState* block = GetCurrentTouchBlock()) {
+ block->GetOverscrollHandoffChain()->ClearOverscroll();
+ }
+}
+
+void
+AsyncPanZoomController::CancelAnimationAndGestureState()
+{
+ mX.CancelGesture();
+ mY.CancelGesture();
+ CancelAnimation(CancelAnimationFlags::ScrollSnap);
+}
+
+bool
+AsyncPanZoomController::HasReadyTouchBlock() const
+{
+ return GetInputQueue()->HasReadyTouchBlock();
+}
+
+void AsyncPanZoomController::SetState(PanZoomState aNewState)
+{
+ PanZoomState oldState;
+
+ // Intentional scoping for mutex
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ APZC_LOG("%p changing from state %d to %d\n", this, mState, aNewState);
+ oldState = mState;
+ mState = aNewState;
+ }
+
+ DispatchStateChangeNotification(oldState, aNewState);
+}
+
+void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldState,
+ PanZoomState aNewState)
+{
+ { // scope the lock
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mNotificationBlockers > 0) {
+ return;
+ }
+ }
+
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eTransformBegin);
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // Let the compositor know about scroll state changes so it can manage
+ // windowed plugins.
+ if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+ mCompositorController->ScheduleHideAllPluginWindows();
+ }
+#endif
+ } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eTransformEnd);
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+ mCompositorController->ScheduleShowAllPluginWindows();
+ }
+#endif
+ }
+ }
+}
+
+bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
+ return !(aState == NOTHING || aState == TOUCHING);
+}
+
+bool AsyncPanZoomController::IsInPanningState() const {
+ return (mState == PANNING || mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y);
+}
+
+void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
+ APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
+ aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
+ if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
+ NS_WARNING("APZC received zoom constraints with NaN values; dropping...");
+ return;
+ }
+
+ CSSToParentLayerScale min = mFrameMetrics.GetDevPixelsPerCSSPixel()
+ * kViewportMinScale / ParentLayerToScreenScale(1);
+ CSSToParentLayerScale max = mFrameMetrics.GetDevPixelsPerCSSPixel()
+ * kViewportMaxScale / ParentLayerToScreenScale(1);
+
+ // inf float values and other bad cases should be sanitized by the code below.
+ mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
+ mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
+ mZoomConstraints.mMinZoom = (min > aConstraints.mMinZoom ? min : aConstraints.mMinZoom);
+ mZoomConstraints.mMaxZoom = (max > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : max);
+ if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
+ mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
+ }
+}
+
+ZoomConstraints
+AsyncPanZoomController::GetZoomConstraints() const
+{
+ return mZoomConstraints;
+}
+
+
+void AsyncPanZoomController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ APZThreadUtils::AssertOnControllerThread();
+ RefPtr<Runnable> task = aTask;
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->PostDelayedTask(task.forget(), aDelayMs);
+ }
+ // If there is no controller, that means this APZC has been destroyed, and
+ // we probably don't need to run the task. It will get destroyed when the
+ // RefPtr goes out of scope.
+}
+
+bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
+{
+ return aGuid == GetGuid();
+}
+
+bool AsyncPanZoomController::HasTreeManager(const APZCTreeManager* aTreeManager) const
+{
+ return GetApzcTreeManager() == aTreeManager;
+}
+
+void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const
+{
+ if (aGuidOut) {
+ *aGuidOut = GetGuid();
+ }
+}
+
+ScrollableLayerGuid AsyncPanZoomController::GetGuid() const
+{
+ return ScrollableLayerGuid(mLayersId, mFrameMetrics);
+}
+
+void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
+{
+ mMonitor.AssertCurrentThreadIn();
+
+ FrameMetrics* frame = mSharedFrameMetricsBuffer ?
+ static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
+
+ if (frame && mSharedLock && gfxPrefs::ProgressivePaint()) {
+ mSharedLock->Lock();
+ *frame = mFrameMetrics;
+ mSharedLock->Unlock();
+ }
+}
+
+void AsyncPanZoomController::ShareCompositorFrameMetrics()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // Only create the shared memory buffer if it hasn't already been created,
+ // we are using progressive tile painting, and we have a
+ // controller to pass the shared memory back to the content process/thread.
+ if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
+
+ // Create shared memory and initialize it with the current FrameMetrics value
+ mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
+ FrameMetrics* frame = nullptr;
+ mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
+ mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
+ frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
+
+ if (frame) {
+
+ { // scope the monitor, only needed to copy the FrameMetrics.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ *frame = mFrameMetrics;
+ }
+
+ // Get the process id of the content process
+ base::ProcessId otherPid = mMetricsSharingController->RemotePid();
+ ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
+
+ // Get the shared memory handle to share with the content process
+ mSharedFrameMetricsBuffer->ShareToProcess(otherPid, &mem);
+
+ // Get the cross process mutex handle to share with the content process
+ mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
+ CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(otherPid);
+
+ // Send the shared memory handle and cross process handle to the content
+ // process by an asynchronous ipc call. Include the APZC unique ID
+ // so the content process know which APZC sent this shared FrameMetrics.
+ if (!mMetricsSharingController->StartSharingMetrics(mem, handle, mLayersId, mAPZCId)) {
+ APZC_LOG("%p failed to share FrameMetrics with content process.", this);
+ }
+ }
+ }
+}
+
+Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
+ const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) {
+ mMonitor.AssertCurrentThreadIn();
+ APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str());
+ CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
+ if (Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
+ mScrollMetadata.GetSnapInfo(),
+ aUnit,
+ CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()),
+ CSSRect::ToAppUnits(scrollRange),
+ CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()),
+ CSSPoint::ToAppUnits(aDestination))) {
+ CSSPoint cssSnapPoint = CSSPoint::FromAppUnits(snapPoint.ref());
+ // GetSnapPointForDestination() can produce a destination that's outside
+ // of the scroll frame's scroll range. Clamp it here (this matches the
+ // behaviour of the main-thread code path, which clamps it in
+ // nsGfxScrollFrame::ScrollTo()).
+ return Some(scrollRange.ClampPoint(cssSnapPoint));
+ }
+ return Nothing();
+}
+
+void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
+ if (Maybe<CSSPoint> snapPoint =
+ FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) {
+ if (*snapPoint != mFrameMetrics.GetScrollOffset()) {
+ APZC_LOG("%p smooth scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str());
+ SmoothScrollTo(*snapPoint);
+ }
+ }
+}
+
+void AsyncPanZoomController::ScrollSnap() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ScrollSnapNear(mFrameMetrics.GetScrollOffset());
+}
+
+void AsyncPanZoomController::ScrollSnapToDestination() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ float friction = gfxPrefs::APZFlingFriction();
+ ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity());
+ ParentLayerPoint predictedDelta;
+ // "-velocity / log(1.0 - friction)" is the integral of the deceleration
+ // curve modeled for flings in the "Axis" class.
+ if (velocity.x != 0.0f) {
+ predictedDelta.x = -velocity.x / log(1.0 - friction);
+ }
+ if (velocity.y != 0.0f) {
+ predictedDelta.y = -velocity.y / log(1.0 - friction);
+ }
+ CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom();
+
+ // If the fling will overscroll, don't scroll snap, because then the user
+ // user would not see any overscroll animation.
+ bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) ||
+ (velocity.y * mY.GetOverscroll() >= 0));
+ if (!flingWillOverscroll) {
+ APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f "
+ "predictedDelta: %f, %f position: %f, %f "
+ "predictedDestination: %f, %f\n",
+ this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
+ (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
+ (float)mFrameMetrics.GetScrollOffset().y,
+ (float)predictedDestination.x, (float)predictedDestination.y);
+
+ ScrollSnapNear(predictedDestination);
+ }
+}
+
+bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
+ const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition)
+{
+ // Don't scroll snap for pixel scrolls. This matches the main thread
+ // behaviour in EventStateManager::DoScrollText().
+ if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
+ return false;
+ }
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom();
+ CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint(
+ aStartPosition + (aDelta / zoom));
+ nsIScrollableFrame::ScrollUnit unit =
+ ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType);
+
+ if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, unit)) {
+ aDelta = (*snapPoint - aStartPosition) * zoom;
+ aStartPosition = *snapPoint;
+ return true;
+ }
+ return false;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/AsyncPanZoomController.h b/system/graphics/layers/apz/src/AsyncPanZoomController.h
new file mode 100644
index 000000000..fa55957dc
--- /dev/null
+++ b/system/graphics/layers/apz/src/AsyncPanZoomController.h
@@ -0,0 +1,1223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AsyncPanZoomController_h
+#define mozilla_layers_AsyncPanZoomController_h
+
+#include "CrossProcessMutex.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/AsyncPanZoomAnimation.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Atomics.h"
+#include "InputData.h"
+#include "Axis.h"
+#include "InputQueue.h"
+#include "APZUtils.h"
+#include "Layers.h" // for Layer::ScrollDirection
+#include "LayersTypes.h"
+#include "mozilla/gfx/Matrix.h"
+#include "nsIScrollableFrame.h"
+#include "nsRegion.h"
+#include "nsTArray.h"
+#include "PotentialCheckerboardDurationTracker.h"
+
+#include "base/message_loop.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class SharedMemoryBasic;
+
+} // namespace ipc
+
+namespace layers {
+
+class AsyncDragMetrics;
+struct ScrollableLayerGuid;
+class CompositorController;
+class MetricsSharingController;
+class GestureEventListener;
+struct AsyncTransform;
+class AsyncPanZoomAnimation;
+class AndroidFlingAnimation;
+class GenericFlingAnimation;
+class InputBlockState;
+class TouchBlockState;
+class PanGestureBlockState;
+class OverscrollHandoffChain;
+class StateChangeNotificationBlocker;
+class CheckerboardEvent;
+class OverscrollEffectBase;
+class WidgetOverscrollEffect;
+class GenericOverscrollEffect;
+class AndroidSpecificState;
+
+// Base class for grouping platform-specific APZC state variables.
+class PlatformSpecificStateBase {
+public:
+ virtual ~PlatformSpecificStateBase() {}
+ virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; }
+};
+
+/**
+ * Controller for all panning and zooming logic. Any time a user input is
+ * detected and it must be processed in some way to affect what the user sees,
+ * it goes through here. Listens for any input event from InputData and can
+ * optionally handle WidgetGUIEvent-derived touch events, but this must be done
+ * on the main thread. Note that this class completely cross-platform.
+ *
+ * Input events originate on the UI thread of the platform that this runs on,
+ * and are then sent to this class. This class processes the event in some way;
+ * for example, a touch move will usually lead to a panning of content (though
+ * of course there are exceptions, such as if content preventDefaults the event,
+ * or if the target frame is not scrollable). The compositor interacts with this
+ * class by locking it and querying it for the current transform matrix based on
+ * the panning and zooming logic that was invoked on the UI thread.
+ *
+ * Currently, each outer DOM window (i.e. a website in a tab, but not any
+ * subframes) has its own AsyncPanZoomController. In the future, to support
+ * asynchronously scrolled subframes, we want to have one AsyncPanZoomController
+ * per frame.
+ */
+class AsyncPanZoomController {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController)
+
+ typedef mozilla::MonitorAutoLock MonitorAutoLock;
+ typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+
+public:
+ enum GestureBehavior {
+ // The platform code is responsible for forwarding gesture events here. We
+ // will not attempt to generate gesture events from MultiTouchInputs.
+ DEFAULT_GESTURES,
+ // An instance of GestureEventListener is used to detect gestures. This is
+ // handled completely internally within this class.
+ USE_GESTURE_DETECTOR
+ };
+
+ /**
+ * Constant describing the tolerance in distance we use, multiplied by the
+ * device DPI, before we start panning the screen. This is to prevent us from
+ * accidentally processing taps as touch moves, and from very short/accidental
+ * touches moving the screen.
+ * Note: It's an abuse of the 'Coord' class to use it to represent a 2D
+ * distance, but it's the closest thing we currently have.
+ */
+ static ScreenCoord GetTouchStartTolerance();
+
+ AsyncPanZoomController(uint64_t aLayersId,
+ APZCTreeManager* aTreeManager,
+ const RefPtr<InputQueue>& aInputQueue,
+ GeckoContentController* aController,
+ GestureBehavior aGestures = DEFAULT_GESTURES);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the gecko thread.
+ //
+
+ /**
+ * Read the various prefs and do any global initialization for all APZC instances.
+ * This must be run on the gecko thread before any APZC instances are actually
+ * used for anything meaningful.
+ */
+ static void InitializeGlobalState();
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the controller/UI thread.
+ //
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up.
+ */
+ void ZoomToRect(CSSRect aRect, const uint32_t aFlags);
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ */
+ void UpdateZoomConstraints(const ZoomConstraints& aConstraints);
+
+ /**
+ * Return the zoom constraints last set for this APZC (in the constructor
+ * or in UpdateZoomConstraints()).
+ */
+ ZoomConstraints GetZoomConstraints() const;
+
+ /**
+ * Schedules a runnable to run on the controller/UI thread at some time
+ * in the future.
+ */
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the compositor thread.
+ //
+
+ /**
+ * Advances any animations currently running to the given timestamp.
+ * This may be called multiple times with the same timestamp.
+ *
+ * The return value indicates whether or not any currently running animation
+ * should continue. If true, the compositor should schedule another composite.
+ */
+ bool AdvanceAnimations(const TimeStamp& aSampleTime);
+
+ bool UpdateAnimation(const TimeStamp& aSampleTime,
+ nsTArray<RefPtr<Runnable>>* aOutDeferredTasks);
+
+ /**
+ * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
+ * for the container layer corresponding to this APZC.
+ * |aIsFirstPaint| is a flag passed from the shadow
+ * layers code indicating that the scroll metadata being sent with this call are
+ * the initial metadata and the initial paint of the frame has just happened.
+ */
+ void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
+ bool aThisLayerTreeUpdated);
+
+ /**
+ * The platform implementation must set the compositor controller so that we can
+ * request composites.
+ */
+ void SetCompositorController(CompositorController* aCompositorController);
+
+ /**
+ * If we need to share the frame metrics with some other thread, this controller
+ * needs to be set and provides relevant information/APIs.
+ */
+ void SetMetricsSharingController(MetricsSharingController* aMetricsSharingController);
+
+ // --------------------------------------------------------------------------
+ // These methods can be called from any thread.
+ //
+
+ /**
+ * Shut down the controller/UI thread state and prepare to be
+ * deleted (which may happen from any thread).
+ */
+ void Destroy();
+
+ /**
+ * Returns true if Destroy() has already been called on this APZC instance.
+ */
+ bool IsDestroyed() const;
+
+ /**
+ * Returns the transform to take something from the coordinate space of the
+ * last thing we know gecko painted, to the coordinate space of the last thing
+ * we asked gecko to paint. In cases where that last request has not yet been
+ * processed, this is needed to transform input events properly into a space
+ * gecko will understand.
+ */
+ Matrix4x4 GetTransformToLastDispatchedPaint() const;
+
+ /**
+ * Returns the number of CSS pixels of checkerboard according to the metrics
+ * in this APZC.
+ */
+ uint32_t GetCheckerboardMagnitude() const;
+
+ /**
+ * Report the number of CSSPixel-milliseconds of checkerboard.
+ */
+ void ReportCheckerboard(const TimeStamp& aSampleTime);
+
+ /**
+ * Flush any active checkerboard report that's in progress. This basically
+ * pretends like any in-progress checkerboard event has terminated, and pushes
+ * out the report to the checkerboard reporting service. If the
+ * checkerboard event has not really finished, it will start a new event
+ * on the next composite.
+ */
+ void FlushActiveCheckerboardReport();
+
+ /**
+ * Returns whether or not the APZC is currently in a state of checkerboarding.
+ * This is a simple computation based on the last-painted content and whether
+ * the async transform has pushed it so far that it doesn't fully contain the
+ * composition bounds.
+ */
+ bool IsCurrentlyCheckerboarding() const;
+
+ /**
+ * Recalculates the displayport. Ideally, this should paint an area bigger
+ * than the composite-to dimensions so that when you scroll down, you don't
+ * checkerboard immediately. This includes a bunch of logic, including
+ * algorithms to bias painting in the direction of the velocity.
+ */
+ static const ScreenMargin CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ nsEventStatus HandleDragEvent(const MouseInput& aEvent,
+ const AsyncDragMetrics& aDragMetrics);
+
+ /**
+ * Handler for events which should not be intercepted by the touch listener.
+ */
+ nsEventStatus HandleInputEvent(const InputData& aEvent,
+ const ScreenToParentLayerMatrix4x4& aTransformToApzc);
+
+ /**
+ * Handler for gesture events.
+ * Currently some gestures are detected in GestureEventListener that calls
+ * APZC back through this handler in order to avoid recursive calls to
+ * APZC::HandleInputEvent() which is supposed to do the work for
+ * ReceiveInputEvent().
+ */
+ nsEventStatus HandleGestureEvent(const InputData& aEvent);
+
+ /**
+ * Handler for touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ void HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY);
+
+ /**
+ * Populates the provided object (if non-null) with the scrollable guid of this apzc.
+ */
+ void GetGuid(ScrollableLayerGuid* aGuidOut) const;
+
+ /**
+ * Returns the scrollable guid of this apzc.
+ */
+ ScrollableLayerGuid GetGuid() const;
+
+ /**
+ * Returns true if this APZC instance is for the layer identified by the guid.
+ */
+ bool Matches(const ScrollableLayerGuid& aGuid);
+
+ /**
+ * Returns true if the tree manager of this APZC is the same as the one
+ * passed in.
+ */
+ bool HasTreeManager(const APZCTreeManager* aTreeManager) const;
+
+ void StartAnimation(AsyncPanZoomAnimation* aAnimation);
+
+ /**
+ * Cancels any currently running animation.
+ * aFlags is a bit-field to provide specifics of how to cancel the animation.
+ * See CancelAnimationFlags.
+ */
+ void CancelAnimation(CancelAnimationFlags aFlags = Default);
+
+ /**
+ * Adjusts the scroll position to compensate for a shift in the surface, such
+ * that the content appears to remain visually in the same position. i.e. if
+ * the surface moves up by 10 screenpixels, the scroll position should also
+ * move up by 10 pixels so that what used to be at the top of the surface is
+ * now 10 pixels down the surface.
+ */
+ void AdjustScrollForSurfaceShift(const ScreenPoint& aShift);
+
+ /**
+ * Clear any overscroll on this APZC.
+ */
+ void ClearOverscroll();
+
+ /**
+ * Returns whether this APZC is for an element marked with the 'scrollgrab'
+ * attribute.
+ */
+ bool HasScrollgrab() const { return mScrollMetadata.GetHasScrollgrab(); }
+
+ /**
+ * Returns whether this APZC has room to be panned (in any direction).
+ */
+ bool IsPannable() const;
+
+ /**
+ * Returns true if the APZC has been flung with a velocity greater than the
+ * stop-on-tap fling velocity threshold (which is pref-controlled).
+ */
+ bool IsFlingingFast() const;
+
+ /**
+ * Returns the identifier of the touch in the last touch event processed by
+ * this APZC. This should only be called when the last touch event contained
+ * only one touch.
+ */
+ int32_t GetLastTouchIdentifier() const;
+
+ /**
+ * Returns the matrix that transforms points from global screen space into
+ * this APZC's ParentLayer space.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ScreenToParentLayerMatrix4x4 GetTransformToThis() const;
+
+ /**
+ * Convert the vector |aVector|, rooted at the point |aAnchor|, from
+ * this APZC's ParentLayer coordinates into screen coordinates.
+ * The anchor is necessary because with 3D tranforms, the location of the
+ * vector can affect the result of the transform.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ScreenPoint ToScreenCoordinates(const ParentLayerPoint& aVector,
+ const ParentLayerPoint& aAnchor) const;
+
+ /**
+ * Convert the vector |aVector|, rooted at the point |aAnchor|, from
+ * screen coordinates into this APZC's ParentLayer coordinates.
+ * The anchor is necessary because with 3D tranforms, the location of the
+ * vector can affect the result of the transform.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ParentLayerPoint ToParentLayerCoordinates(const ScreenPoint& aVector,
+ const ScreenPoint& aAnchor) const;
+
+ // Return whether or not a wheel event will be able to scroll in either
+ // direction.
+ bool CanScroll(const InputData& aEvent) const;
+
+ // Return whether or not a scroll delta will be able to scroll in either
+ // direction.
+ bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const;
+
+ // Return whether or not there is room to scroll this APZC
+ // in the given direction.
+ bool CanScroll(Layer::ScrollDirection aDirection) const;
+
+ void NotifyMozMouseScrollEvent(const nsString& aString) const;
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~AsyncPanZoomController();
+
+ // Returns the cached current frame time.
+ TimeStamp GetFrameTime() const;
+
+ /**
+ * Helper method for touches beginning. Sets everything up for panning and any
+ * multitouch gestures.
+ */
+ nsEventStatus OnTouchStart(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches moving. Does any transforms needed when panning.
+ */
+ nsEventStatus OnTouchMove(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches ending. Redraws the screen if necessary and does
+ * any cleanup after a touch has ended.
+ */
+ nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches being cancelled. Treated roughly the same as a
+ * touch ending (OnTouchEnd()).
+ */
+ nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for scales beginning. Distinct from the OnTouch* handlers in
+ * that this implies some outside implementation has determined that the user
+ * is pinching.
+ */
+ nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper method for scaling. As the user moves their fingers when pinching,
+ * this changes the scale of the page.
+ */
+ nsEventStatus OnScale(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper method for scales ending. Redraws the screen if necessary and does
+ * any cleanup after a scale has ended.
+ */
+ nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper methods for handling pan events.
+ */
+ nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent);
+ nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
+ nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
+ nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
+ nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
+ nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
+ nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
+
+ /**
+ * Helper methods for handling scroll wheel events.
+ */
+ nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent);
+
+ ParentLayerPoint GetScrollWheelDelta(const ScrollWheelInput& aEvent) const;
+
+ /**
+ * Helper methods for long press gestures.
+ */
+ nsEventStatus OnLongPress(const TapGestureInput& aEvent);
+ nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for single tap gestures.
+ */
+ nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for a single tap confirmed.
+ */
+ nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for double taps.
+ */
+ nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for double taps where the double-tap gesture is disabled.
+ */
+ nsEventStatus OnSecondTap(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method to cancel any gesture currently going to Gecko. Used
+ * primarily when a user taps the screen over some clickable content but then
+ * pans down instead of letting go (i.e. to cancel a previous touch so that a
+ * new one can properly take effect.
+ */
+ nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
+
+ /**
+ * Scrolls the viewport by an X,Y offset.
+ */
+ void ScrollBy(const CSSPoint& aOffset);
+
+ /**
+ * Scales the viewport by an amount (note that it multiplies this scale in to
+ * the current scale, it doesn't set it to |aScale|). Also considers a focus
+ * point so that the page zooms inward/outward from that point.
+ */
+ void ScaleWithFocus(float aScale,
+ const CSSPoint& aFocus);
+
+ /**
+ * Schedules a composite on the compositor thread.
+ */
+ void ScheduleComposite();
+
+ /**
+ * Schedules a composite, and if enough time has elapsed since the last
+ * paint, a paint.
+ */
+ void ScheduleCompositeAndMaybeRepaint();
+
+ /**
+ * Gets the displacement of the current touch since it began. That is, it is
+ * the distance between the current position and the initial position of the
+ * current touch (this only makes sense if a touch is currently happening and
+ * OnTouchMove() or the equivalent for pan gestures is being invoked).
+ * Note: It's an abuse of the 'Coord' class to use it to represent a 2D
+ * distance, but it's the closest thing we currently have.
+ */
+ ScreenCoord PanDistance() const;
+
+ /**
+ * Gets the start point of the current touch.
+ * Like PanDistance(), this only makes sense if a touch is currently
+ * happening and OnTouchMove() or the equivalent for pan gestures is
+ * being invoked.
+ */
+ ParentLayerPoint PanStart() const;
+
+ /**
+ * Gets a vector of the velocities of each axis.
+ */
+ const ParentLayerPoint GetVelocityVector() const;
+
+ /**
+ * Sets the velocities of each axis.
+ */
+ void SetVelocityVector(const ParentLayerPoint& aVelocityVector);
+
+ /**
+ * Gets the first touch point from a MultiTouchInput. This gets only
+ * the first one and assumes the rest are either missing or not relevant.
+ */
+ ParentLayerPoint GetFirstTouchPoint(const MultiTouchInput& aEvent);
+
+ /**
+ * Sets the panning state basing on the pan direction angle and current touch-action value.
+ */
+ void HandlePanningWithTouchAction(double angle);
+
+ /**
+ * Sets the panning state ignoring the touch action value.
+ */
+ void HandlePanning(double angle);
+
+ /**
+ * Update the panning state and axis locks.
+ */
+ void HandlePanningUpdate(const ScreenPoint& aDelta);
+
+ /**
+ * Sets up anything needed for panning. This takes us out of the "TOUCHING"
+ * state and starts actually panning us.
+ */
+ nsEventStatus StartPanning(const MultiTouchInput& aStartPoint);
+
+ /**
+ * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for
+ * both axes and factors in the time delta from the last update.
+ */
+ void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent);
+
+ /**
+ * Does any panning required due to a new touch event.
+ */
+ void TrackTouch(const MultiTouchInput& aEvent);
+
+ /**
+ * Utility function to send updated FrameMetrics to Gecko so that it can paint
+ * the displayport area. Calls into GeckoContentController to do the actual
+ * work. This call will use the current metrics. If this function is called
+ * from a non-main thread, it will redispatch itself to the main thread, and
+ * use the latest metrics during the redispatch.
+ */
+ void RequestContentRepaint(bool aUserAction = true);
+
+ /**
+ * Send the provided metrics to Gecko to trigger a repaint. This function
+ * may filter duplicate calls with the same metrics. This function must be
+ * called on the main thread.
+ */
+ void RequestContentRepaint(const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ /**
+ * Gets the current frame metrics. This is *not* the Gecko copy stored in the
+ * layers code.
+ */
+ const FrameMetrics& GetFrameMetrics() const;
+
+ /**
+ * Gets the pointer to the apzc tree manager. All the access to tree manager
+ * should be made via this method and not via private variable since this method
+ * ensures that no lock is set.
+ */
+ APZCTreeManager* GetApzcTreeManager() const;
+
+ /**
+ * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
+ * to the parent document. This excludes the transient compositor transform.
+ * NOTE: This must be converted to LayoutDevicePoint relative to the child
+ * document before sending over IPC to a child process.
+ */
+ bool ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut);
+
+ enum AxisLockMode {
+ FREE, /* No locking at all */
+ STANDARD, /* Default axis locking mode that remains locked until pan ends*/
+ STICKY, /* Allow lock to be broken, with hysteresis */
+ };
+
+ static AxisLockMode GetAxisLockMode();
+
+ // Helper function for OnSingleTapUp(), OnSingleTapConfirmed(), and
+ // OnLongPressUp().
+ nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType,
+ const ScreenIntPoint& aPoint,
+ mozilla::Modifiers aModifiers);
+
+ // Common processing at the end of a touch block.
+ void OnTouchEndOrCancel();
+
+ uint64_t mLayersId;
+ RefPtr<CompositorController> mCompositorController;
+ RefPtr<MetricsSharingController> mMetricsSharingController;
+
+ /* Access to the following two fields is protected by the mRefPtrMonitor,
+ since they are accessed on the UI thread but can be cleared on the
+ compositor thread. */
+ RefPtr<GeckoContentController> mGeckoContentController;
+ RefPtr<GestureEventListener> mGestureEventListener;
+ mutable Monitor mRefPtrMonitor;
+
+ // This is a raw pointer to avoid introducing a reference cycle between
+ // AsyncPanZoomController and APZCTreeManager. Since these objects don't
+ // live on the main thread, we can't use the cycle collector with them.
+ // The APZCTreeManager owns the lifetime of the APZCs, so nulling this
+ // pointer out in Destroy() will prevent accessing deleted memory.
+ Atomic<APZCTreeManager*> mTreeManager;
+
+ /* Utility functions that return a addrefed pointer to the corresponding fields. */
+ already_AddRefed<GeckoContentController> GetGeckoContentController() const;
+ already_AddRefed<GestureEventListener> GetGestureEventListener() const;
+
+ PlatformSpecificStateBase* GetPlatformSpecificState();
+
+protected:
+ // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
+ // monitor. Do not read from or modify either of them without locking.
+ ScrollMetadata mScrollMetadata;
+ FrameMetrics& mFrameMetrics; // for convenience, refers to mScrollMetadata.mMetrics
+
+ // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
+ // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
+ // monitor should be held. When setting |mState|, either the SetState()
+ // function can be used, or the monitor can be held and then |mState| updated.
+ // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h.
+ // This is mutable to allow entering it from 'const' methods; doing otherwise
+ // would significantly limit what methods could be 'const'.
+ mutable ReentrantMonitor mMonitor;
+
+private:
+ // Metadata of the container layer corresponding to this APZC. This is
+ // stored here so that it is accessible from the UI/controller thread.
+ // These are the metrics at last content paint, the most recent
+ // values we were notified of in NotifyLayersUpdate(). Since it represents
+ // the Gecko state, it should be used as a basis for untransformation when
+ // sending messages back to Gecko.
+ ScrollMetadata mLastContentPaintMetadata;
+ FrameMetrics& mLastContentPaintMetrics; // for convenience, refers to mLastContentPaintMetadata.mMetrics
+ // The last metrics used for a content repaint request.
+ FrameMetrics mLastPaintRequestMetrics;
+ // The metrics that we expect content to have. This is updated when we
+ // request a content repaint, and when we receive a shadow layers update.
+ // This allows us to transform events into Gecko's coordinate space.
+ FrameMetrics mExpectedGeckoMetrics;
+
+ AxisX mX;
+ AxisY mY;
+
+ // This flag is set to true when we are in a axis-locked pan as a result of
+ // the touch-action CSS property.
+ bool mPanDirRestricted;
+
+ // Most up-to-date constraints on zooming. These should always be reasonable
+ // values; for example, allowing a min zoom of 0.0 can cause very bad things
+ // to happen.
+ ZoomConstraints mZoomConstraints;
+
+ // The last time the compositor has sampled the content transform for this
+ // frame.
+ TimeStamp mLastSampleTime;
+
+ // The last sample time at which we submitted a checkerboarding report.
+ TimeStamp mLastCheckerboardReport;
+
+ // Stores the previous focus point if there is a pinch gesture happening. Used
+ // to allow panning by moving multiple fingers (thus moving the focus point).
+ ParentLayerPoint mLastZoomFocus;
+
+ RefPtr<AsyncPanZoomAnimation> mAnimation;
+
+ UniquePtr<OverscrollEffectBase> mOverscrollEffect;
+
+ // Groups state variables that are specific to a platform.
+ // Initialized on first use.
+ UniquePtr<PlatformSpecificStateBase> mPlatformSpecificState;
+
+ friend class Axis;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to expose
+ * the current async transform state to callers.
+ */
+public:
+ /**
+ * Allows callers to specify which type of async transform they want:
+ * NORMAL provides the actual async transforms of the APZC, whereas
+ * RESPECT_FORCE_DISABLE will provide empty async transforms if and only if
+ * the metrics has the mForceDisableApz flag set. In general the latter should
+ * only be used by call sites that are applying the transform to update
+ * a layer's position.
+ */
+ enum AsyncMode {
+ NORMAL,
+ RESPECT_FORCE_DISABLE,
+ };
+
+ /**
+ * Query the transforms that should be applied to the layer corresponding
+ * to this APZC due to asynchronous panning and zooming.
+ * This function returns the async transform via the |aOutTransform|
+ * out parameter.
+ */
+ ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncMode aMode) const;
+
+ /**
+ * Return a visual effect that reflects this apzc's
+ * overscrolled state, if any.
+ */
+ AsyncTransformComponentMatrix GetOverscrollTransform(AsyncMode aMode) const;
+
+ /**
+ * Returns the incremental transformation corresponding to the async pan/zoom
+ * in progress. That is, when this transform is multiplied with the layer's
+ * existing transform, it will make the layer appear with the desired pan/zoom
+ * amount.
+ */
+ AsyncTransform GetCurrentAsyncTransform(AsyncMode aMode) const;
+
+ /**
+ * Returns the same transform as GetCurrentAsyncTransform(), but includes
+ * any transform due to axis over-scroll.
+ */
+ AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * the state that tracks what this APZC is doing with the input events.
+ */
+protected:
+ enum PanZoomState {
+ NOTHING, /* no touch-start events received */
+ FLING, /* all touches removed, but we're still scrolling page */
+ TOUCHING, /* one touch-start event received */
+
+ PANNING, /* panning the frame */
+ PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
+ PANNING_LOCKED_Y, /* as above for Y axis */
+
+ PAN_MOMENTUM, /* like PANNING, but controlled by momentum PanGestureInput events */
+
+ PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
+ ANIMATING_ZOOM, /* animated zoom to a new rect */
+ OVERSCROLL_ANIMATION, /* Spring-based animation used to relieve overscroll once
+ the finger is lifted. */
+ SMOOTH_SCROLL, /* Smooth scrolling to destination. Used by
+ CSSOM-View smooth scroll-behavior */
+ WHEEL_SCROLL /* Smooth scrolling to a destination for a wheel event. */
+ };
+
+ // This is in theory protected by |mMonitor|; that is, it should be held whenever
+ // this is updated. In practice though... see bug 897017.
+ PanZoomState mState;
+
+private:
+ friend class StateChangeNotificationBlocker;
+ /**
+ * A counter of how many StateChangeNotificationBlockers are active.
+ * A non-zero count will prevent state change notifications from
+ * being dispatched. Only code that holds mMonitor should touch this.
+ */
+ int mNotificationBlockers;
+
+ /**
+ * Helper to set the current state. Holds the monitor before actually setting
+ * it and fires content controller events based on state changes. Always set
+ * the state using this call, do not set it directly.
+ */
+ void SetState(PanZoomState aState);
+ /**
+ * Fire content controller notifications about state changes, assuming no
+ * StateChangeNotificationBlocker has been activated.
+ */
+ void DispatchStateChangeNotification(PanZoomState aOldState, PanZoomState aNewState);
+ /**
+ * Internal helpers for checking general state of this apzc.
+ */
+ static bool IsTransformingState(PanZoomState aState);
+
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * blocks of touch events and the state needed to deal with content
+ * listeners.
+ */
+public:
+ /**
+ * Flush a repaint request if one is needed, without throttling it with the
+ * paint throttler.
+ */
+ void FlushRepaintForNewInputBlock();
+
+ /**
+ * Given the number of touch points in an input event and touch block they
+ * belong to, check if the event can result in a panning/zooming behavior.
+ * This is primarily used to figure out when to dispatch the pointercancel
+ * event for the pointer events spec.
+ */
+ bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
+
+ /**
+ * Clear internal state relating to touch input handling.
+ */
+ void ResetTouchInputState();
+
+ /**
+ * Gets a ref to the input queue that is shared across the entire tree manager.
+ */
+ const RefPtr<InputQueue>& GetInputQueue() const;
+
+private:
+ void CancelAnimationAndGestureState();
+
+ RefPtr<InputQueue> mInputQueue;
+ CancelableBlockState* GetCurrentInputBlock() const;
+ TouchBlockState* GetCurrentTouchBlock() const;
+ bool HasReadyTouchBlock() const;
+
+ PanGestureBlockState* GetCurrentPanGestureBlock() const;
+
+private:
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * fling animations, smooth scroll animations, and overscroll
+ * during a fling or smooth scroll.
+ */
+public:
+ /**
+ * Attempt a fling with the velocity specified in |aHandoffState|.
+ * If we are not pannable, the fling is handed off to the next APZC in
+ * the handoff chain via mTreeManager->DispatchFling().
+ * Returns true iff. the entire velocity of the fling was consumed by
+ * this APZC. |aHandoffState.mVelocity| is modified to contain any
+ * unused, residual velocity.
+ * |aHandoffState.mIsHandoff| should be true iff. the fling was handed off
+ * from a previous APZC, and determines whether acceleration is applied
+ * to the fling.
+ */
+ bool AttemptFling(FlingHandoffState& aHandoffState);
+
+private:
+ friend class AndroidFlingAnimation;
+ friend class GenericFlingAnimation;
+ friend class OverscrollAnimation;
+ friend class SmoothScrollAnimation;
+ friend class WheelScrollAnimation;
+
+ friend class GenericOverscrollEffect;
+ friend class WidgetOverscrollEffect;
+
+ // The initial velocity of the most recent fling.
+ ParentLayerPoint mLastFlingVelocity;
+ // The time at which the most recent fling started.
+ TimeStamp mLastFlingTime;
+ // Indicates if the repaint-during-pinch timer is currently set
+ bool mPinchPaintTimerSet;
+
+ // Deal with overscroll resulting from a fling animation. This is only ever
+ // called on APZC instances that were actually performing a fling.
+ // The overscroll is handled by trying to hand the fling off to an APZC
+ // later in the handoff chain, or if there are no takers, continuing the
+ // fling and entering an overscrolled state.
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc);
+
+ void HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity);
+
+ // Helper function used by AttemptFling().
+ void AcceptFling(FlingHandoffState& aHandoffState);
+
+ // Start an overscroll animation with the given initial velocity.
+ void StartOverscrollAnimation(const ParentLayerPoint& aVelocity);
+
+ void SmoothScrollTo(const CSSPoint& aDestination);
+
+ // Returns whether overscroll is allowed during an event.
+ bool AllowScrollHandoffInCurrentBlock() const;
+
+ // Invoked by the pinch repaint timer.
+ void DoDelayedRequestContentRepaint();
+
+ /* ===================================================================
+ * The functions and members in this section are used to make ancestor chains
+ * out of APZC instances. These chains can only be walked or manipulated
+ * while holding the lock in the associated APZCTreeManager instance.
+ */
+public:
+ void SetParent(AsyncPanZoomController* aParent) {
+ mParent = aParent;
+ }
+
+ AsyncPanZoomController* GetParent() const {
+ return mParent;
+ }
+
+ /* Returns true if there is no APZC higher in the tree with the same
+ * layers id.
+ */
+ bool HasNoParentWithSameLayersId() const {
+ return !mParent || (mParent->mLayersId != mLayersId);
+ }
+
+ bool IsRootForLayersId() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mScrollMetadata.IsLayersIdRoot();
+ }
+
+ bool IsRootContent() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics.IsRootContent();
+ }
+
+private:
+ // |mTreeManager| belongs in this section but it's declaration is a bit
+ // further above due to initialization-order constraints.
+
+ RefPtr<AsyncPanZoomController> mParent;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for scrolling,
+ * including handing off scroll to another APZC, and overscrolling.
+ */
+public:
+ FrameMetrics::ViewID GetScrollHandoffParentId() const {
+ return mScrollMetadata.GetScrollParentId();
+ }
+
+ /**
+ * Attempt to scroll in response to a touch-move from |aStartPoint| to
+ * |aEndPoint|, which are in our (transformed) screen coordinates.
+ * Due to overscroll handling, there may not actually have been a touch-move
+ * at these points, but this function will scroll as if there had been.
+ * If this attempt causes overscroll (i.e. the layer cannot be scrolled
+ * by the entire amount requested), the overscroll is passed back to the
+ * tree manager via APZCTreeManager::DispatchScroll(). If the tree manager
+ * does not find an APZC further in the handoff chain to accept the
+ * overscroll, and this APZC is pannable, this APZC enters an overscrolled
+ * state.
+ * |aOverscrollHandoffChain| and |aOverscrollHandoffChainIndex| are used by
+ * the tree manager to keep track of which APZC to hand off the overscroll
+ * to; this function increments the chain and the index and passes it on to
+ * APZCTreeManager::DispatchScroll() in the event of overscroll.
+ * Returns true iff. this APZC, or an APZC further down the
+ * handoff chain, accepted the scroll (possibly entering an overscrolled
+ * state). If this returns false, the caller APZC knows that it should enter
+ * an overscrolled state itself if it can.
+ * aStartPoint and aEndPoint are modified depending on how much of the
+ * scroll gesture was consumed by APZCs in the handoff chain.
+ */
+ bool AttemptScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ void FlushRepaintForOverscrollHandoff();
+
+ /**
+ * If overscrolled, start a snap-back animation and return true.
+ * Otherwise return false.
+ */
+ bool SnapBackIfOverscrolled();
+
+ /**
+ * Build the chain of APZCs along which scroll will be handed off when
+ * this APZC receives input events.
+ *
+ * Notes on lifetime and const-correctness:
+ * - The returned handoff chain is |const|, to indicate that it cannot be
+ * changed after being built.
+ * - When passing the chain to a function that uses it without storing it,
+ * pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
+ * - When storing the chain, store it by RefPtr-to-const (as in
+ * |RefPtr<const OverscrollHandoffChain>|). This ensures the chain is
+ * kept alive. Note that queueing a task that uses the chain as an
+ * argument constitutes storing, as the task may outlive its queuer.
+ * - When passing the chain to a function that will store it, pass it as
+ * |const RefPtr<const OverscrollHandoffChain>&|. This allows the
+ * function to copy it into the |RefPtr<const OverscrollHandoffChain>|
+ * that will store it, while avoiding an unnecessary copy (and thus
+ * AddRef() and Release()) when passing it.
+ */
+ RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
+
+private:
+ /**
+ * A helper function for calling APZCTreeManager::DispatchScroll().
+ * Guards against the case where the APZC is being concurrently destroyed
+ * (and thus mTreeManager is being nulled out).
+ */
+ void CallDispatchScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ /**
+ * A helper function for overscrolling during panning. This is a wrapper
+ * around OverscrollBy() that also implements restrictions on entering
+ * overscroll based on the pan angle.
+ */
+ void OverscrollForPanning(ParentLayerPoint& aOverscroll,
+ const ScreenPoint& aPanDistance);
+
+ /**
+ * Try to overscroll by 'aOverscroll'.
+ * If we are pannable on a particular axis, that component of 'aOverscroll'
+ * is transferred to any existing overscroll.
+ */
+ void OverscrollBy(ParentLayerPoint& aOverscroll);
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to maintain the
+ * area that this APZC instance is responsible for. This is used when
+ * hit-testing to see which APZC instance should handle touch events.
+ */
+public:
+ void SetAncestorTransform(const Matrix4x4& aTransformToLayer) {
+ mAncestorTransform = aTransformToLayer;
+ }
+
+ Matrix4x4 GetAncestorTransform() const {
+ return mAncestorTransform;
+ }
+
+ // Returns whether or not this apzc contains the given screen point within
+ // its composition bounds.
+ bool Contains(const ScreenIntPoint& aPoint) const;
+
+ bool IsOverscrolled() const {
+ return mX.IsOverscrolled() || mY.IsOverscrolled();
+ }
+
+ bool IsInPanningState() const;
+
+private:
+ /* This is the cumulative CSS transform for all the layers from (and including)
+ * the parent APZC down to (but excluding) this one, and excluding any
+ * perspective transforms. */
+ Matrix4x4 mAncestorTransform;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for sharing the
+ * FrameMetrics across processes for the progressive tiling code.
+ */
+private:
+ /* Unique id assigned to each APZC. Used with ViewID to uniquely identify
+ * shared FrameMeterics used in progressive tile painting. */
+ const uint32_t mAPZCId;
+
+ RefPtr<ipc::SharedMemoryBasic> mSharedFrameMetricsBuffer;
+ CrossProcessMutex* mSharedLock;
+ /**
+ * Called when ever mFrameMetrics is updated so that if it is being
+ * shared with the content process the shared FrameMetrics may be updated.
+ */
+ void UpdateSharedCompositorFrameMetrics();
+ /**
+ * Create a shared memory buffer for containing the FrameMetrics and
+ * a CrossProcessMutex that may be shared with the content process
+ * for use in progressive tiled update calculations.
+ */
+ void ShareCompositorFrameMetrics();
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for testing
+ * and assertion purposes only.
+ */
+public:
+ /**
+ * Set an extra offset for testing async scrolling.
+ */
+ void SetTestAsyncScrollOffset(const CSSPoint& aPoint)
+ {
+ mTestAsyncScrollOffset = aPoint;
+ }
+ /**
+ * Set an extra offset for testing async scrolling.
+ */
+ void SetTestAsyncZoom(const LayerToParentLayerScale& aZoom)
+ {
+ mTestAsyncZoom = aZoom;
+ }
+
+ void MarkAsyncTransformAppliedToContent()
+ {
+ mAsyncTransformAppliedToContent = true;
+ }
+
+ bool GetAsyncTransformAppliedToContent() const
+ {
+ return mAsyncTransformAppliedToContent;
+ }
+
+ uint64_t GetLayersId() const
+ {
+ return mLayersId;
+ }
+
+private:
+ // Extra offset to add to the async scroll position for testing
+ CSSPoint mTestAsyncScrollOffset;
+ // Extra zoom to include in the aync zoom for testing
+ LayerToParentLayerScale mTestAsyncZoom;
+ // Flag to track whether or not the APZ transform is not used. This
+ // flag is recomputed for every composition frame.
+ bool mAsyncTransformAppliedToContent;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for checkerboard
+ * recording.
+ */
+private:
+ // Helper function to update the in-progress checkerboard event, if any.
+ void UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+ uint32_t aMagnitude);
+
+ // Mutex protecting mCheckerboardEvent
+ Mutex mCheckerboardEventLock;
+ // This is created when this APZC instance is first included as part of a
+ // composite. If a checkerboard event takes place, this is destroyed at the
+ // end of the event, and a new one is created on the next composite.
+ UniquePtr<CheckerboardEvent> mCheckerboardEvent;
+ // This is used to track the total amount of time that we could reasonably
+ // be checkerboarding. Combined with other info, this allows us to meaningfully
+ // say how frequently users actually encounter checkerboarding.
+ PotentialCheckerboardDurationTracker mPotentialCheckerboardTracker;
+
+
+ /* ===================================================================
+ * The functions in this section are used for CSS scroll snapping.
+ */
+
+ // If |aEvent| should trigger scroll snapping, adjust |aDelta| to reflect
+ // the snapping (that is, make it a delta that will take us to the desired
+ // snap point). The delta is interpreted as being relative to
+ // |aStartPosition|, and if a target snap point is found, |aStartPosition|
+ // is also updated, to the value of the snap point.
+ // Returns true iff. a target snap point was found.
+ bool MaybeAdjustDeltaForScrollSnapping(const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition);
+
+ // Snap to a snap position nearby the current scroll position, if appropriate.
+ void ScrollSnap();
+
+ // Snap to a snap position nearby the destination predicted based on the
+ // current velocity, if appropriate.
+ void ScrollSnapToDestination();
+
+ // Snap to a snap position nearby the provided destination, if appropriate.
+ void ScrollSnapNear(const CSSPoint& aDestination);
+
+ // Find a snap point near |aDestination| that we should snap to.
+ // Returns the snap point if one was found, or an empty Maybe otherwise.
+ // |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
+ // GetSnapPointForDestination). It should generally be determined by the
+ // type of event that's triggering the scroll.
+ Maybe<CSSPoint> FindSnapPointNear(const CSSPoint& aDestination,
+ nsIScrollableFrame::ScrollUnit aUnit);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PanZoomController_h
diff --git a/system/graphics/layers/apz/src/Axis.cpp b/system/graphics/layers/apz/src/Axis.cpp
new file mode 100644
index 000000000..0e37fcc14
--- /dev/null
+++ b/system/graphics/layers/apz/src/Axis.cpp
@@ -0,0 +1,680 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Axis.h"
+#include <math.h> // for fabsf, pow, powf
+#include <algorithm> // for max
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/Attributes.h" // for final
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/mozalloc.h" // for operator new
+#include "mozilla/FloatingPoint.h" // for FuzzyEqualsAdditive
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "nsMathUtils.h" // for NS_lround
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc
+#include "nscore.h" // for NS_IMETHOD
+#include "gfxPrefs.h" // for the preferences
+
+#define AXIS_LOG(...)
+// #define AXIS_LOG(...) printf_stderr("AXIS: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+// When we compute the velocity we do so by taking two input events and
+// dividing the distance delta over the time delta. In some cases the time
+// delta can be really small, which can make the velocity computation very
+// volatile. To avoid this we impose a minimum time delta below which we do
+// not recompute the velocity.
+const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
+
+bool FuzzyEqualsCoordinate(float aValue1, float aValue2)
+{
+ return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON)
+ || FuzzyEqualsMultiplicative(aValue1, aValue2);
+}
+
+extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
+
+Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
+ : mPos(0),
+ mVelocitySampleTimeMs(0),
+ mVelocitySamplePos(0),
+ mVelocity(0.0f),
+ mAxisLocked(false),
+ mAsyncPanZoomController(aAsyncPanZoomController),
+ mOverscroll(0),
+ mFirstOverscrollAnimationSample(0),
+ mLastOverscrollPeak(0),
+ mOverscrollScale(1.0f)
+{
+}
+
+float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const {
+ ScreenPoint velocity = MakePoint(aVelocityInchesPerMs * APZCTreeManager::GetDPI());
+ // Use ToScreenCoordinates() to convert a point rather than a vector by
+ // treating the point as a vector, and using (0, 0) as the anchor.
+ ScreenPoint panStart = mAsyncPanZoomController->ToScreenCoordinates(
+ mAsyncPanZoomController->PanStart(),
+ ParentLayerPoint());
+ ParentLayerPoint localVelocity =
+ mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
+ return localVelocity.Length();
+}
+
+void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
+ // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
+ // We still update mPos so that the positioning is correct (and we don't run
+ // into problems like bug 1042734) but the velocity will remain where it was.
+ // In particular we don't update either mVelocitySampleTimeMs or
+ // mVelocitySamplePos so that eventually when we do get an event with the
+ // required time delta we use the corresponding distance delta as well.
+ AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
+ mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs));
+ mPos = aPos;
+ return;
+ }
+
+ float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
+
+ newVelocity = ApplyFlingCurveToVelocity(newVelocity);
+
+ AXIS_LOG("%p|%s updating velocity to %f with touch\n",
+ mAsyncPanZoomController, Name(), newVelocity);
+ mVelocity = newVelocity;
+ mPos = aPos;
+ mVelocitySampleTimeMs = aTimestampMs;
+ mVelocitySamplePos = aPos;
+
+ AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+float Axis::ApplyFlingCurveToVelocity(float aVelocity) const {
+ float newVelocity = aVelocity;
+ if (gfxPrefs::APZMaxVelocity() > 0.0f) {
+ bool velocityIsNegative = (newVelocity < 0);
+ newVelocity = fabs(newVelocity);
+
+ float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity());
+ newVelocity = std::min(newVelocity, maxVelocity);
+
+ if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
+ float curveThreshold = ToLocalVelocity(gfxPrefs::APZCurveThreshold());
+ if (newVelocity > curveThreshold) {
+ // here, 0 < curveThreshold < newVelocity <= maxVelocity, so we apply the curve
+ float scale = maxVelocity - curveThreshold;
+ float funcInput = (newVelocity - curveThreshold) / scale;
+ float funcOutput =
+ gVelocityCurveFunction->GetValue(funcInput,
+ ComputedTimingFunction::BeforeFlag::Unset);
+ float curvedVelocity = (funcOutput * scale) + curveThreshold;
+ AXIS_LOG("%p|%s curving up velocity from %f to %f\n",
+ mAsyncPanZoomController, Name(), newVelocity, curvedVelocity);
+ newVelocity = curvedVelocity;
+ }
+ }
+
+ if (velocityIsNegative) {
+ newVelocity = -newVelocity;
+ }
+ }
+
+ return newVelocity;
+}
+
+void Axis::AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity) {
+ mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity));
+ if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
+ mVelocityQueue.RemoveElementAt(0);
+ }
+}
+
+void Axis::HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ mVelocity = ApplyFlingCurveToVelocity(aSpeed);
+ mVelocitySampleTimeMs = aTimestampMs;
+
+ AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
+ mStartPos = aPos;
+ mPos = aPos;
+ mVelocitySampleTimeMs = aTimestampMs;
+ mVelocitySamplePos = aPos;
+ mAxisLocked = false;
+}
+
+bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
+ /* ParentLayerCoord */ float& aDisplacementOut,
+ /* ParentLayerCoord */ float& aOverscrollAmountOut,
+ bool aForceOverscroll /* = false */)
+{
+ if (mAxisLocked) {
+ aOverscrollAmountOut = 0;
+ aDisplacementOut = 0;
+ return false;
+ }
+ if (aForceOverscroll) {
+ aOverscrollAmountOut = aDisplacement;
+ aDisplacementOut = 0;
+ return false;
+ }
+
+ EndOverscrollAnimation();
+
+ ParentLayerCoord displacement = aDisplacement;
+
+ // First consume any overscroll in the opposite direction along this axis.
+ ParentLayerCoord consumedOverscroll = 0;
+ if (mOverscroll > 0 && aDisplacement < 0) {
+ consumedOverscroll = std::min(mOverscroll, -aDisplacement);
+ } else if (mOverscroll < 0 && aDisplacement > 0) {
+ consumedOverscroll = 0.f - std::min(-mOverscroll, aDisplacement);
+ }
+ mOverscroll -= consumedOverscroll;
+ displacement += consumedOverscroll;
+
+ // Split the requested displacement into an allowed displacement that does
+ // not overscroll, and an overscroll amount.
+ aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
+ if (aOverscrollAmountOut != 0.0f) {
+ // No need to have a velocity along this axis anymore; it won't take us
+ // anywhere, so we're just spinning needlessly.
+ AXIS_LOG("%p|%s has overscrolled, clearing velocity\n",
+ mAsyncPanZoomController, Name());
+ mVelocity = 0.0f;
+ displacement -= aOverscrollAmountOut;
+ }
+ aDisplacementOut = displacement;
+ return fabsf(consumedOverscroll) > EPSILON;
+}
+
+ParentLayerCoord Axis::ApplyResistance(ParentLayerCoord aRequestedOverscroll) const {
+ // 'resistanceFactor' is a value between 0 and 1, which:
+ // - tends to 1 as the existing overscroll tends to 0
+ // - tends to 0 as the existing overscroll tends to the composition length
+ // The actual overscroll is the requested overscroll multiplied by this
+ // factor; this should prevent overscrolling by more than the composition
+ // length.
+ float resistanceFactor = 1 - fabsf(GetOverscroll()) / GetCompositionLength();
+ return resistanceFactor < 0 ? ParentLayerCoord(0) : aRequestedOverscroll * resistanceFactor;
+}
+
+void Axis::OverscrollBy(ParentLayerCoord aOverscroll) {
+ MOZ_ASSERT(CanScroll());
+ // We can get some spurious calls to OverscrollBy() with near-zero values
+ // due to rounding error. Ignore those (they might trip the asserts below.)
+ if (FuzzyEqualsAdditive(aOverscroll.value, 0.0f, COORDINATE_EPSILON)) {
+ return;
+ }
+ EndOverscrollAnimation();
+ aOverscroll = ApplyResistance(aOverscroll);
+ if (aOverscroll > 0) {
+#ifdef DEBUG
+ if (!FuzzyEqualsCoordinate(GetCompositionEnd().value, GetPageEnd().value)) {
+ nsPrintfCString message("composition end (%f) is not equal (within error) to page end (%f)\n",
+ GetCompositionEnd().value, GetPageEnd().value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue > 0");
+ }
+#endif
+ MOZ_ASSERT(mOverscroll >= 0);
+ } else if (aOverscroll < 0) {
+#ifdef DEBUG
+ if (!FuzzyEqualsCoordinate(GetOrigin().value, GetPageStart().value)) {
+ nsPrintfCString message("composition origin (%f) is not equal (within error) to page origin (%f)\n",
+ GetOrigin().value, GetPageStart().value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue < 0");
+ }
+#endif
+ MOZ_ASSERT(mOverscroll <= 0);
+ }
+ mOverscroll += aOverscroll;
+}
+
+ParentLayerCoord Axis::GetOverscroll() const {
+ ParentLayerCoord result = (mOverscroll - mLastOverscrollPeak) / mOverscrollScale;
+
+ // Assert that we return overscroll in the correct direction
+#ifdef DEBUG
+ if ((result.value * mFirstOverscrollAnimationSample.value) < 0.0f) {
+ nsPrintfCString message("GetOverscroll() (%f) and first overscroll animation sample (%f) have different signs\n",
+ result.value, mFirstOverscrollAnimationSample.value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue");
+ }
+#endif
+
+ return result;
+}
+
+void Axis::StartOverscrollAnimation(float aVelocity) {
+ // Make sure any state from a previous animation has been cleared.
+ MOZ_ASSERT(mFirstOverscrollAnimationSample == 0 &&
+ mLastOverscrollPeak == 0 &&
+ mOverscrollScale == 1);
+
+ SetVelocity(aVelocity);
+}
+
+void Axis::EndOverscrollAnimation() {
+ ParentLayerCoord overscroll = GetOverscroll();
+ mFirstOverscrollAnimationSample = 0;
+ mLastOverscrollPeak = 0;
+ mOverscrollScale = 1.0f;
+ mOverscroll = overscroll;
+}
+
+void Axis::StepOverscrollAnimation(double aStepDurationMilliseconds) {
+ // Apply spring physics to the overscroll as time goes on.
+ // Note: this method of sampling isn't perfectly smooth, as it assumes
+ // a constant velocity over 'aDelta', instead of an accelerating velocity.
+ // (The way we applying friction to flings has the same issue.)
+ // Hooke's law with damping:
+ // F = -kx - bv
+ // where
+ // k is a constant related to the stiffness of the spring
+ // The larger the constant, the stiffer the spring.
+ // x is the displacement of the end of the spring from its equilibrium
+ // In our scenario, it's the amount of overscroll on the axis.
+ // b is a constant that provides damping (friction)
+ // v is the velocity of the point at the end of the spring
+ // See http://gafferongames.com/game-physics/spring-physics/
+ const float kSpringStiffness = gfxPrefs::APZOverscrollSpringStiffness();
+ const float kSpringFriction = gfxPrefs::APZOverscrollSpringFriction();
+
+ // Apply spring force.
+ float springForce = -1 * kSpringStiffness * mOverscroll;
+ // Assume unit mass, so force = acceleration.
+ float oldVelocity = mVelocity;
+ mVelocity += springForce * aStepDurationMilliseconds;
+
+ // Apply dampening.
+ mVelocity *= pow(double(1 - kSpringFriction), aStepDurationMilliseconds);
+ AXIS_LOG("%p|%s sampled overscroll animation, leaving velocity at %f\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+
+ // At the peak of each oscillation, record new offset and scaling factors for
+ // overscroll, to ensure that GetOverscroll always returns a value of the
+ // same sign, and that this value is correctly adjusted as the spring is
+ // dampened.
+ // To handle the case where one of the velocity samples is exaclty zero,
+ // consider a sign change to have occurred when the outgoing velocity is zero.
+ bool velocitySignChange = (oldVelocity * mVelocity) < 0 || mVelocity == 0;
+ if (mFirstOverscrollAnimationSample == 0.0f) {
+ mFirstOverscrollAnimationSample = mOverscroll;
+
+ // It's possible to start sampling overscroll with velocity == 0, or
+ // velocity in the opposite direction of overscroll, so make sure we
+ // correctly record the peak in this case.
+ if (mOverscroll != 0 && ((mOverscroll > 0 ? oldVelocity : -oldVelocity) <= 0.0f)) {
+ velocitySignChange = true;
+ }
+ }
+ if (velocitySignChange) {
+ bool oddOscillation = (mOverscroll.value * mFirstOverscrollAnimationSample.value) < 0.0f;
+ mLastOverscrollPeak = oddOscillation ? mOverscroll : -mOverscroll;
+ mOverscrollScale = 2.0f;
+ }
+
+ // Adjust the amount of overscroll based on the velocity.
+ // Note that we allow for oscillations.
+ mOverscroll += (mVelocity * aStepDurationMilliseconds);
+
+ // Our mechanism for translating a set of mOverscroll values that oscillate
+ // around zero to a set of GetOverscroll() values that have the same sign
+ // (so content is always stretched, never compressed) assumes that
+ // mOverscroll does not exceed mLastOverscrollPeak in magnitude. If our
+ // calculations were exact, this would be the case, as a dampened spring
+ // should never attain a displacement greater in magnitude than a previous
+ // peak. In our approximation calculations, however, this may not hold
+ // exactly. To ensure the assumption is not violated, we clamp the magnitude
+ // of mOverscroll.
+ if (mLastOverscrollPeak != 0 && fabs(mOverscroll) > fabs(mLastOverscrollPeak)) {
+ mOverscroll = (mOverscroll >= 0) ? fabs(mLastOverscrollPeak) : -fabs(mLastOverscrollPeak);
+ }
+}
+
+bool Axis::SampleOverscrollAnimation(const TimeDuration& aDelta) {
+ // Short-circuit early rather than running through all the sampling code.
+ if (mVelocity == 0.0f && mOverscroll == 0.0f) {
+ return false;
+ }
+
+ // We approximate the curve traced out by the velocity of the spring
+ // over time by breaking up the curve into small segments over which we
+ // consider the velocity to be constant. If the animation is sampled
+ // sufficiently often, then treating |aDelta| as a single segment of this
+ // sort would be fine, but the frequency at which the animation is sampled
+ // can be affected by external factors, and as the segment size grows larger,
+ // the approximation gets worse and the approximated curve can even diverge
+ // (i.e. oscillate forever, with displacements of increasing absolute value)!
+ // To avoid this, we break up |aDelta| into smaller segments of length 1 ms
+ // each, and a segment of any remaining fractional milliseconds.
+ double milliseconds = aDelta.ToMilliseconds();
+ int wholeMilliseconds = (int) aDelta.ToMilliseconds();
+ double fractionalMilliseconds = milliseconds - wholeMilliseconds;
+ for (int i = 0; i < wholeMilliseconds; ++i) {
+ StepOverscrollAnimation(1);
+ }
+ StepOverscrollAnimation(fractionalMilliseconds);
+
+ // If both the velocity and the displacement fall below a threshold, stop
+ // the animation so we don't continue doing tiny oscillations that aren't
+ // noticeable.
+ if (fabs(mOverscroll) < gfxPrefs::APZOverscrollStopDistanceThreshold() &&
+ fabs(mVelocity) < gfxPrefs::APZOverscrollStopVelocityThreshold()) {
+ // "Jump" to the at-rest state. The jump shouldn't be noticeable as the
+ // velocity and overscroll are already low.
+ AXIS_LOG("%p|%s oscillation dropped below threshold, going to rest\n",
+ mAsyncPanZoomController, Name());
+ ClearOverscroll();
+ mVelocity = 0;
+ return false;
+ }
+
+ // Otherwise, continue the animation.
+ return true;
+}
+
+bool Axis::IsOverscrolled() const {
+ return mOverscroll != 0.f;
+}
+
+void Axis::ClearOverscroll() {
+ EndOverscrollAnimation();
+ mOverscroll = 0;
+}
+
+ParentLayerCoord Axis::PanStart() const {
+ return mStartPos;
+}
+
+ParentLayerCoord Axis::PanDistance() const {
+ return fabs(mPos - mStartPos);
+}
+
+ParentLayerCoord Axis::PanDistance(ParentLayerCoord aPos) const {
+ return fabs(aPos - mStartPos);
+}
+
+void Axis::EndTouch(uint32_t aTimestampMs) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ mAxisLocked = false;
+ mVelocity = 0;
+ int count = 0;
+ while (!mVelocityQueue.IsEmpty()) {
+ uint32_t timeDelta = (aTimestampMs - mVelocityQueue[0].first);
+ if (timeDelta < gfxPrefs::APZVelocityRelevanceTime()) {
+ count++;
+ mVelocity += mVelocityQueue[0].second;
+ }
+ mVelocityQueue.RemoveElementAt(0);
+ }
+ if (count > 1) {
+ mVelocity /= count;
+ }
+ AXIS_LOG("%p|%s ending touch, computed velocity %f\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+}
+
+void Axis::CancelGesture() {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n",
+ mAsyncPanZoomController, Name());
+ mVelocity = 0.0f;
+ while (!mVelocityQueue.IsEmpty()) {
+ mVelocityQueue.RemoveElementAt(0);
+ }
+}
+
+bool Axis::CanScroll() const {
+ return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
+}
+
+bool Axis::CanScroll(ParentLayerCoord aDelta) const
+{
+ if (!CanScroll() || mAxisLocked) {
+ return false;
+ }
+
+ return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) > COORDINATE_EPSILON;
+}
+
+CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const
+{
+ CSSToParentLayerScale zoom = GetScaleForAxis(GetFrameMetrics().GetZoom());
+ ParentLayerCoord origin = aOrigin * zoom;
+
+ ParentLayerCoord result;
+ if (origin < GetPageStart()) {
+ result = GetPageStart();
+ } else if (origin + GetCompositionLength() > GetPageEnd()) {
+ result = GetPageEnd() - GetCompositionLength();
+ } else {
+ return aOrigin;
+ }
+
+ return result / zoom;
+}
+
+bool Axis::CanScrollNow() const {
+ return !mAxisLocked && CanScroll();
+}
+
+bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta,
+ float aFriction,
+ float aThreshold) {
+ if (fabsf(mVelocity) <= aThreshold) {
+ // If the velocity is very low, just set it to 0 and stop the fling,
+ // otherwise we'll just asymptotically approach 0 and the user won't
+ // actually see any changes.
+ mVelocity = 0.0f;
+ return false;
+ } else {
+ mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
+ }
+ AXIS_LOG("%p|%s reduced velocity to %f due to friction\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+ return true;
+}
+
+ParentLayerCoord Axis::DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const {
+ ParentLayerCoord newOrigin = GetOrigin() + aDisplacement;
+ ParentLayerCoord newCompositionEnd = GetCompositionEnd() + aDisplacement;
+ // If the current pan plus a displacement takes the window to the left of or
+ // above the current page rect.
+ bool minus = newOrigin < GetPageStart();
+ // If the current pan plus a displacement takes the window to the right of or
+ // below the current page rect.
+ bool plus = newCompositionEnd > GetPageEnd();
+ if (minus && plus) {
+ // Don't handle overscrolled in both directions; a displacement can't cause
+ // this, it must have already been zoomed out too far.
+ return 0;
+ }
+ if (minus) {
+ return newOrigin - GetPageStart();
+ }
+ if (plus) {
+ return newCompositionEnd - GetPageEnd();
+ }
+ return 0;
+}
+
+CSSCoord Axis::ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const {
+ // Internally, do computations in ParentLayer coordinates *before* the scale
+ // is applied.
+ CSSToParentLayerScale zoom = GetFrameMetrics().GetZoom().ToScaleFactor();
+ ParentLayerCoord focus = aFocus * zoom;
+ ParentLayerCoord originAfterScale = (GetOrigin() + focus) - (focus / aScale);
+
+ bool both = ScaleWillOverscrollBothSides(aScale);
+ bool minus = GetPageStart() - originAfterScale > COORDINATE_EPSILON;
+ bool plus = (originAfterScale + (GetCompositionLength() / aScale)) - GetPageEnd() > COORDINATE_EPSILON;
+
+ if ((minus && plus) || both) {
+ // If we ever reach here it's a bug in the client code.
+ MOZ_ASSERT(false, "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount");
+ return 0;
+ }
+ if (minus) {
+ return (originAfterScale - GetPageStart()) / zoom;
+ }
+ if (plus) {
+ return (originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd()) / zoom;
+ }
+ return 0;
+}
+
+bool Axis::IsAxisLocked() const {
+ return mAxisLocked;
+}
+
+float Axis::GetVelocity() const {
+ return mAxisLocked ? 0 : mVelocity;
+}
+
+void Axis::SetVelocity(float aVelocity) {
+ AXIS_LOG("%p|%s direct-setting velocity to %f\n",
+ mAsyncPanZoomController, Name(), aVelocity);
+ mVelocity = aVelocity;
+}
+
+ParentLayerCoord Axis::GetCompositionEnd() const {
+ return GetOrigin() + GetCompositionLength();
+}
+
+ParentLayerCoord Axis::GetPageEnd() const {
+ return GetPageStart() + GetPageLength();
+}
+
+ParentLayerCoord Axis::GetScrollRangeEnd() const {
+ return GetPageEnd() - GetCompositionLength();
+}
+
+ParentLayerCoord Axis::GetOrigin() const {
+ ParentLayerPoint origin = GetFrameMetrics().GetScrollOffset() * GetFrameMetrics().GetZoom();
+ return GetPointOffset(origin);
+}
+
+ParentLayerCoord Axis::GetCompositionLength() const {
+ return GetRectLength(GetFrameMetrics().GetCompositionBounds());
+}
+
+ParentLayerCoord Axis::GetPageStart() const {
+ ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom();
+ return GetRectOffset(pageRect);
+}
+
+ParentLayerCoord Axis::GetPageLength() const {
+ ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom();
+ return GetRectLength(pageRect);
+}
+
+bool Axis::ScaleWillOverscrollBothSides(float aScale) const {
+ const FrameMetrics& metrics = GetFrameMetrics();
+ ParentLayerRect screenCompositionBounds = metrics.GetCompositionBounds()
+ / ParentLayerToParentLayerScale(aScale);
+ return GetRectLength(screenCompositionBounds) - GetPageLength() > COORDINATE_EPSILON;
+}
+
+const FrameMetrics& Axis::GetFrameMetrics() const {
+ return mAsyncPanZoomController->GetFrameMetrics();
+}
+
+
+AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
+ : Axis(aAsyncPanZoomController)
+{
+
+}
+
+ParentLayerCoord AxisX::GetPointOffset(const ParentLayerPoint& aPoint) const
+{
+ return aPoint.x;
+}
+
+ParentLayerCoord AxisX::GetRectLength(const ParentLayerRect& aRect) const
+{
+ return aRect.width;
+}
+
+ParentLayerCoord AxisX::GetRectOffset(const ParentLayerRect& aRect) const
+{
+ return aRect.x;
+}
+
+CSSToParentLayerScale AxisX::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const
+{
+ return CSSToParentLayerScale(aScale.xScale);
+}
+
+ScreenPoint AxisX::MakePoint(ScreenCoord aCoord) const
+{
+ return ScreenPoint(aCoord, 0);
+}
+
+const char* AxisX::Name() const
+{
+ return "X";
+}
+
+AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
+ : Axis(aAsyncPanZoomController)
+{
+
+}
+
+ParentLayerCoord AxisY::GetPointOffset(const ParentLayerPoint& aPoint) const
+{
+ return aPoint.y;
+}
+
+ParentLayerCoord AxisY::GetRectLength(const ParentLayerRect& aRect) const
+{
+ return aRect.height;
+}
+
+ParentLayerCoord AxisY::GetRectOffset(const ParentLayerRect& aRect) const
+{
+ return aRect.y;
+}
+
+CSSToParentLayerScale AxisY::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const
+{
+ return CSSToParentLayerScale(aScale.yScale);
+}
+
+ScreenPoint AxisY::MakePoint(ScreenCoord aCoord) const
+{
+ return ScreenPoint(0, aCoord);
+}
+
+const char* AxisY::Name() const
+{
+ return "Y";
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/Axis.h b/system/graphics/layers/apz/src/Axis.h
new file mode 100644
index 000000000..7ff24d784
--- /dev/null
+++ b/system/graphics/layers/apz/src/Axis.h
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Axis_h
+#define mozilla_layers_Axis_h
+
+#include <sys/types.h> // for int32_t
+#include "APZUtils.h"
+#include "Units.h"
+#include "mozilla/TimeStamp.h" // for TimeDuration
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+
+const float EPSILON = 0.0001f;
+
+/**
+ * Compare two coordinates for equality, accounting for rounding error.
+ * Use both FuzzyEqualsAdditive() with COORDINATE_EPISLON, which accounts for
+ * things like the error introduced by rounding during a round-trip to app
+ * units, and FuzzyEqualsMultiplicative(), which accounts for accumulated error
+ * due to floating-point operations (which can be larger than COORDINATE_EPISLON
+ * for sufficiently large coordinate values).
+ */
+bool FuzzyEqualsCoordinate(float aValue1, float aValue2);
+
+struct FrameMetrics;
+class AsyncPanZoomController;
+
+/**
+ * Helper class to maintain each axis of movement (X,Y) for panning and zooming.
+ * Note that everything here is specific to one axis; that is, the X axis knows
+ * nothing about the Y axis and vice versa.
+ */
+class Axis {
+public:
+ explicit Axis(AsyncPanZoomController* aAsyncPanZoomController);
+
+ /**
+ * Notify this Axis that a new touch has been received, including a timestamp
+ * for when the touch was received. This triggers a recalculation of velocity.
+ * This can also used for pan gesture events. For those events, the "touch"
+ * location is stationary and the scroll displacement is passed in as
+ * aAdditionalDelta.
+ */
+ void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs);
+
+protected:
+ float ApplyFlingCurveToVelocity(float aVelocity) const;
+ void AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity);
+
+public:
+ void HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed);
+
+ /**
+ * Notify this Axis that a touch has begun, i.e. the user has put their finger
+ * on the screen but has not yet tried to pan.
+ */
+ void StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs);
+
+ /**
+ * Notify this Axis that a touch has ended gracefully. This may perform
+ * recalculations of the axis velocity.
+ */
+ void EndTouch(uint32_t aTimestampMs);
+
+ /**
+ * Notify this Axis that the gesture has ended forcefully. Useful for stopping
+ * flings when a user puts their finger down in the middle of one (i.e. to
+ * stop a previous touch including its fling so that a new one can take its
+ * place).
+ */
+ void CancelGesture();
+
+ /**
+ * Takes a requested displacement to the position of this axis, and adjusts it
+ * to account for overscroll (which might decrease the displacement; this is
+ * to prevent the viewport from overscrolling the page rect), and axis locking
+ * (which might prevent any displacement from happening). If overscroll
+ * ocurred, its amount is written to |aOverscrollAmountOut|.
+ * The |aDisplacementOut| parameter is set to the adjusted
+ * displacement, and the function returns true iff internal overscroll amounts
+ * were changed.
+ */
+ bool AdjustDisplacement(ParentLayerCoord aDisplacement,
+ /* ParentLayerCoord */ float& aDisplacementOut,
+ /* ParentLayerCoord */ float& aOverscrollAmountOut,
+ bool aForceOverscroll = false);
+
+ /**
+ * Overscrolls this axis by the requested amount in the requested direction.
+ * The axis must be at the end of its scroll range in this direction.
+ */
+ void OverscrollBy(ParentLayerCoord aOverscroll);
+
+ /**
+ * Return the amount of overscroll on this axis, in ParentLayer pixels.
+ *
+ * If this amount is nonzero, the relevant component of
+ * mAsyncPanZoomController->mFrameMetrics.mScrollOffset must be at its
+ * extreme allowed value in the relevant direction (that is, it must be at
+ * its maximum value if we are overscrolled at our composition length, and
+ * at its minimum value if we are overscrolled at the origin).
+ */
+ ParentLayerCoord GetOverscroll() const;
+
+ /**
+ * Start an overscroll animation with the given initial velocity.
+ */
+ void StartOverscrollAnimation(float aVelocity);
+
+ /**
+ * Sample the snap-back animation to relieve overscroll.
+ * |aDelta| is the time since the last sample.
+ */
+ bool SampleOverscrollAnimation(const TimeDuration& aDelta);
+
+ /**
+ * Stop an overscroll animation.
+ */
+ void EndOverscrollAnimation();
+
+ /**
+ * Return whether this axis is overscrolled in either direction.
+ */
+ bool IsOverscrolled() const;
+
+ /**
+ * Clear any overscroll amount on this axis.
+ */
+ void ClearOverscroll();
+
+ /**
+ * Gets the starting position of the touch supplied in StartTouch().
+ */
+ ParentLayerCoord PanStart() const;
+
+ /**
+ * Gets the distance between the starting position of the touch supplied in
+ * StartTouch() and the current touch from the last
+ * UpdateWithTouchAtDevicePoint().
+ */
+ ParentLayerCoord PanDistance() const;
+
+ /**
+ * Gets the distance between the starting position of the touch supplied in
+ * StartTouch() and the supplied position.
+ */
+ ParentLayerCoord PanDistance(ParentLayerCoord aPos) const;
+
+ /**
+ * Applies friction during a fling, or cancels the fling if the velocity is
+ * too low. Returns true if the fling should continue to another frame, or
+ * false if it should end.
+ * |aDelta| is the amount of time that has passed since the last time
+ * friction was applied.
+ * |aFriction| is the amount of friction to apply.
+ * |aThreshold| is the velocity below which the fling is cancelled.
+ */
+ bool FlingApplyFrictionOrCancel(const TimeDuration& aDelta,
+ float aFriction,
+ float aThreshold);
+
+ /**
+ * Returns true if the page has room to be scrolled along this axis.
+ */
+ bool CanScroll() const;
+
+ /**
+ * Returns whether this axis can scroll any more in a particular direction.
+ */
+ bool CanScroll(ParentLayerCoord aDelta) const;
+
+ /**
+ * Returns true if the page has room to be scrolled along this axis
+ * and this axis is not scroll-locked.
+ */
+ bool CanScrollNow() const;
+
+ /**
+ * Clamp a point to the page's scrollable bounds. That is, a scroll
+ * destination to the returned point will not contain any overscroll.
+ */
+ CSSCoord ClampOriginToScrollableRect(CSSCoord aOrigin) const;
+
+ void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; }
+
+ /**
+ * Gets the raw velocity of this axis at this moment.
+ */
+ float GetVelocity() const;
+
+ /**
+ * Sets the raw velocity of this axis at this moment.
+ * Intended to be called only when the axis "takes over" a velocity from
+ * another APZC, in which case there are no touch points available to call
+ * UpdateWithTouchAtDevicePoint. In other circumstances,
+ * UpdateWithTouchAtDevicePoint should be used and the velocity calculated
+ * there.
+ */
+ void SetVelocity(float aVelocity);
+
+ /**
+ * If a displacement will overscroll the axis, this returns the amount and in
+ * what direction.
+ */
+ ParentLayerCoord DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const;
+
+ /**
+ * If a scale will overscroll the axis, this returns the amount and in what
+ * direction.
+ *
+ * |aFocus| is the point at which the scale is focused at. We will offset the
+ * scroll offset in such a way that it remains in the same place on the page
+ * relative.
+ *
+ * Note: Unlike most other functions in Axis, this functions operates in
+ * CSS coordinates so there is no confusion as to whether the ParentLayer
+ * coordinates it operates in are before or after the scale is applied.
+ */
+ CSSCoord ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const;
+
+ /**
+ * Checks if an axis will overscroll in both directions by computing the
+ * content rect and checking that its height/width (depending on the axis)
+ * does not overextend past the viewport.
+ *
+ * This gets called by ScaleWillOverscroll().
+ */
+ bool ScaleWillOverscrollBothSides(float aScale) const;
+
+ /**
+ * Returns true if movement on this axis is locked.
+ */
+ bool IsAxisLocked() const;
+
+ ParentLayerCoord GetOrigin() const;
+ ParentLayerCoord GetCompositionLength() const;
+ ParentLayerCoord GetPageStart() const;
+ ParentLayerCoord GetPageLength() const;
+ ParentLayerCoord GetCompositionEnd() const;
+ ParentLayerCoord GetPageEnd() const;
+ ParentLayerCoord GetScrollRangeEnd() const;
+
+ ParentLayerCoord GetPos() const { return mPos; }
+
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const = 0;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const = 0;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const = 0;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const = 0;
+
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
+
+ virtual const char* Name() const = 0;
+
+protected:
+ ParentLayerCoord mPos;
+
+ // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
+ // used in the last velocity sampling. They get updated when a new sample is
+ // taken (which may not happen on every input event, if the time delta is too
+ // small).
+ uint32_t mVelocitySampleTimeMs;
+ ParentLayerCoord mVelocitySamplePos;
+
+ ParentLayerCoord mStartPos;
+ float mVelocity; // Units: ParentLayerCoords per millisecond
+ bool mAxisLocked; // Whether movement on this axis is locked.
+ AsyncPanZoomController* mAsyncPanZoomController;
+
+ // mOverscroll is the displacement of an oscillating spring from its resting
+ // state. The resting state moves as the overscroll animation progresses.
+ ParentLayerCoord mOverscroll;
+ // Used to record the initial overscroll when we start sampling for animation.
+ ParentLayerCoord mFirstOverscrollAnimationSample;
+ // These two variables are used in combination to make sure that
+ // GetOverscroll() never changes sign during animation. This is necessary,
+ // as mOverscroll itself oscillates around zero during animation.
+ // If we're not sampling overscroll animation, mOverscrollScale will be 1.0
+ // and mLastOverscrollPeak will be zero.
+ // If we are animating, after the overscroll reaches its peak,
+ // mOverscrollScale will be 2.0 and mLastOverscrollPeak will store the amount
+ // of overscroll at the last peak of the oscillation. Together, these values
+ // guarantee that the result of GetOverscroll() never changes sign.
+ ParentLayerCoord mLastOverscrollPeak;
+ float mOverscrollScale;
+
+ // A queue of (timestamp, velocity) pairs; these are the historical
+ // velocities at the given timestamps. Timestamps are in milliseconds,
+ // velocities are in screen pixels per ms. This member can only be
+ // accessed on the controller/UI thread.
+ nsTArray<std::pair<uint32_t, float> > mVelocityQueue;
+
+ const FrameMetrics& GetFrameMetrics() const;
+
+ // Adjust a requested overscroll amount for resistance, yielding a smaller
+ // actual overscroll amount.
+ ParentLayerCoord ApplyResistance(ParentLayerCoord aOverscroll) const;
+
+ // Helper function for SampleOverscrollAnimation().
+ void StepOverscrollAnimation(double aStepDurationMilliseconds);
+
+ // Convert a velocity from global inches/ms into ParentLayerCoords/ms.
+ float ToLocalVelocity(float aVelocityInchesPerMs) const;
+};
+
+class AxisX : public Axis {
+public:
+ explicit AxisX(AsyncPanZoomController* mAsyncPanZoomController);
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const override;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const override;
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const override;
+ virtual const char* Name() const override;
+};
+
+class AxisY : public Axis {
+public:
+ explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController);
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const override;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const override;
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const override;
+ virtual const char* Name() const override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/apz/src/CheckerboardEvent.cpp b/system/graphics/layers/apz/src/CheckerboardEvent.cpp
new file mode 100644
index 000000000..03aa20b14
--- /dev/null
+++ b/system/graphics/layers/apz/src/CheckerboardEvent.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CheckerboardEvent.h"
+
+#include <algorithm> // for std::sort
+
+namespace mozilla {
+namespace layers {
+
+// Relatively arbitrary limit to prevent a perma-checkerboard event from
+// eating up gobs of memory. Ideally we shouldn't have perma-checkerboarding
+// but better to guard against it.
+#define LOG_LENGTH_LIMIT (50 * 1024)
+
+const char* CheckerboardEvent::sDescriptions[] = {
+ "page",
+ "painted critical displayport",
+ "painted displayport",
+ "requested displayport",
+ "viewport",
+};
+
+const char* CheckerboardEvent::sColors[] = {
+ "brown",
+ "darkgreen",
+ "lightgreen",
+ "yellow",
+ "red",
+};
+
+CheckerboardEvent::CheckerboardEvent(bool aRecordTrace)
+ : mRecordTrace(aRecordTrace)
+ , mOriginTime(TimeStamp::Now())
+ , mCheckerboardingActive(false)
+ , mLastSampleTime(mOriginTime)
+ , mFrameCount(0)
+ , mTotalPixelMs(0)
+ , mPeakPixels(0)
+ , mRendertraceLock("Rendertrace")
+{
+}
+
+uint32_t
+CheckerboardEvent::GetSeverity()
+{
+ // Scale the total into a 32-bit value
+ return (uint32_t)sqrt((double)mTotalPixelMs);
+}
+
+uint32_t
+CheckerboardEvent::GetPeak()
+{
+ return mPeakPixels;
+}
+
+TimeDuration
+CheckerboardEvent::GetDuration()
+{
+ return mEndTime - mStartTime;
+}
+
+std::string
+CheckerboardEvent::GetLog()
+{
+ MonitorAutoLock lock(mRendertraceLock);
+ return mRendertraceInfo.str();
+}
+
+bool
+CheckerboardEvent::IsRecordingTrace()
+{
+ return mRecordTrace;
+}
+
+void
+CheckerboardEvent::UpdateRendertraceProperty(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo)
+{
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ if (!mCheckerboardingActive) {
+ mBufferedProperties[aProperty].Update(aProperty, aRect, aExtraInfo, lock);
+ } else {
+ LogInfo(aProperty, TimeStamp::Now(), aRect, aExtraInfo, lock);
+ }
+}
+
+void
+CheckerboardEvent::LogInfo(RendertraceProperty aProperty,
+ const TimeStamp& aTimestamp,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock)
+{
+ MOZ_ASSERT(mRecordTrace);
+ if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) {
+ // The log is already long enough, don't put more things into it. We'll
+ // append a truncation message when this event ends.
+ return;
+ }
+ // The log is consumed by the page at http://people.mozilla.org/~kgupta/rendertrace.html
+ // and will move to about:checkerboard in bug 1238042. The format is not
+ // formally specced, but an informal description can be found at
+ // https://github.com/staktrace/rendertrace/blob/master/index.html#L30
+ mRendertraceInfo << "RENDERTRACE "
+ << (aTimestamp - mOriginTime).ToMilliseconds() << " rect "
+ << sColors[aProperty] << " "
+ << aRect.x << " "
+ << aRect.y << " "
+ << aRect.width << " "
+ << aRect.height << " "
+ << "// " << sDescriptions[aProperty]
+ << aExtraInfo << std::endl;
+}
+
+bool
+CheckerboardEvent::RecordFrameInfo(uint32_t aCssPixelsCheckerboarded)
+{
+ TimeStamp sampleTime = TimeStamp::Now();
+ bool eventEnding = false;
+ if (aCssPixelsCheckerboarded > 0) {
+ if (!mCheckerboardingActive) {
+ StartEvent();
+ }
+ MOZ_ASSERT(mCheckerboardingActive);
+ MOZ_ASSERT(sampleTime >= mLastSampleTime);
+ mTotalPixelMs += (uint64_t)((sampleTime - mLastSampleTime).ToMilliseconds() * aCssPixelsCheckerboarded);
+ if (aCssPixelsCheckerboarded > mPeakPixels) {
+ mPeakPixels = aCssPixelsCheckerboarded;
+ }
+ mFrameCount++;
+ } else {
+ if (mCheckerboardingActive) {
+ StopEvent();
+ eventEnding = true;
+ }
+ MOZ_ASSERT(!mCheckerboardingActive);
+ }
+ mLastSampleTime = sampleTime;
+ return eventEnding;
+}
+
+void
+CheckerboardEvent::StartEvent()
+{
+ MOZ_ASSERT(!mCheckerboardingActive);
+ mCheckerboardingActive = true;
+ mStartTime = TimeStamp::Now();
+
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ std::vector<PropertyValue> history;
+ for (int i = 0; i < MAX_RendertraceProperty; i++) {
+ mBufferedProperties[i].Flush(history, lock);
+ }
+ std::sort(history.begin(), history.end());
+ for (const PropertyValue& p : history) {
+ LogInfo(p.mProperty, p.mTimeStamp, p.mRect, p.mExtraInfo, lock);
+ }
+ mRendertraceInfo << " -- checkerboarding starts below --" << std::endl;
+}
+
+void
+CheckerboardEvent::StopEvent()
+{
+ mCheckerboardingActive = false;
+ mEndTime = TimeStamp::Now();
+
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) {
+ mRendertraceInfo << "[logging aborted due to length limitations]\n";
+ }
+ mRendertraceInfo << "Checkerboarded for " << mFrameCount << " frames ("
+ << (mEndTime - mStartTime).ToMilliseconds() << " ms), "
+ << mPeakPixels << " peak, " << GetSeverity() << " severity." << std::endl;
+}
+
+bool
+CheckerboardEvent::PropertyValue::operator<(const PropertyValue& aOther) const
+{
+ if (mTimeStamp < aOther.mTimeStamp) {
+ return true;
+ } else if (mTimeStamp > aOther.mTimeStamp) {
+ return false;
+ }
+ return mProperty < aOther.mProperty;
+}
+
+CheckerboardEvent::PropertyBuffer::PropertyBuffer()
+ : mIndex(0)
+{
+}
+
+void
+CheckerboardEvent::PropertyBuffer::Update(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock)
+{
+ mValues[mIndex] = { aProperty, TimeStamp::Now(), aRect, aExtraInfo };
+ mIndex = (mIndex + 1) % BUFFER_SIZE;
+}
+
+void
+CheckerboardEvent::PropertyBuffer::Flush(std::vector<PropertyValue>& aOut,
+ const MonitorAutoLock& aProofOfLock)
+{
+ for (uint32_t i = 0; i < BUFFER_SIZE; i++) {
+ uint32_t ix = (mIndex + i) % BUFFER_SIZE;
+ if (!mValues[ix].mTimeStamp.IsNull()) {
+ aOut.push_back(mValues[ix]);
+ mValues[ix].mTimeStamp = TimeStamp();
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/CheckerboardEvent.h b/system/graphics/layers/apz/src/CheckerboardEvent.h
new file mode 100644
index 000000000..3459287eb
--- /dev/null
+++ b/system/graphics/layers/apz/src/CheckerboardEvent.h
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CheckerboardEvent_h
+#define mozilla_layers_CheckerboardEvent_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include <sstream>
+#include "Units.h"
+#include <vector>
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class records information relevant to one "checkerboard event", which is
+ * a contiguous set of frames where a given APZC was checkerboarding. The intent
+ * of this class is to record enough information that it can provide actionable
+ * steps to reduce the occurrence of checkerboarding. Furthermore, it records
+ * information about the severity of the checkerboarding so as to allow
+ * prioritizing the debugging of some checkerboarding events over others.
+ */
+class CheckerboardEvent {
+public:
+ enum RendertraceProperty {
+ Page,
+ PaintedCriticalDisplayPort,
+ PaintedDisplayPort,
+ RequestedDisplayPort,
+ UserVisible,
+
+ // sentinel final value
+ MAX_RendertraceProperty
+ };
+
+ static const char* sDescriptions[MAX_RendertraceProperty];
+ static const char* sColors[MAX_RendertraceProperty];
+
+public:
+ explicit CheckerboardEvent(bool aRecordTrace);
+
+ /**
+ * Gets the "severity" of the checkerboard event. This doesn't have units,
+ * it's just useful for comparing two checkerboard events to see which one
+ * is worse, for some implementation-specific definition of "worse".
+ */
+ uint32_t GetSeverity();
+
+ /**
+ * Gets the number of CSS pixels that were checkerboarded at the peak of the
+ * checkerboard event.
+ */
+ uint32_t GetPeak();
+
+ /**
+ * Gets the length of the checkerboard event.
+ */
+ TimeDuration GetDuration();
+
+ /**
+ * Gets the raw log of the checkerboard event. This can be called any time,
+ * although it really only makes sense to pull once the event is done, after
+ * RecordFrameInfo returns true.
+ */
+ std::string GetLog();
+
+ /**
+ * Returns true iff this event is recording a detailed trace of the event.
+ * This is the argument passed in to the constructor.
+ */
+ bool IsRecordingTrace();
+
+ /**
+ * Provide a new value for one of the rects that is tracked for
+ * checkerboard events.
+ */
+ void UpdateRendertraceProperty(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo = std::string());
+
+ /**
+ * Provide the number of CSS pixels that are checkerboarded in a composite
+ * at the current time.
+ * @return true if the checkerboard event has completed. The caller should
+ * stop updating this object once this happens.
+ */
+ bool RecordFrameInfo(uint32_t aCssPixelsCheckerboarded);
+
+private:
+ /**
+ * Helper method to do stuff when checkeboarding starts.
+ */
+ void StartEvent();
+ /**
+ * Helper method to do stuff when checkerboarding stops.
+ */
+ void StopEvent();
+
+ /**
+ * Helper method to log a rendertrace property and its value to the
+ * rendertrace info buffer (mRendertraceInfo).
+ */
+ void LogInfo(RendertraceProperty aProperty,
+ const TimeStamp& aTimestamp,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock);
+
+ /**
+ * Helper struct that holds a single rendertrace property value.
+ */
+ struct PropertyValue
+ {
+ RendertraceProperty mProperty;
+ TimeStamp mTimeStamp;
+ CSSRect mRect;
+ std::string mExtraInfo;
+
+ bool operator<(const PropertyValue& aOther) const;
+ };
+
+ /**
+ * A circular buffer that stores the most recent BUFFER_SIZE values of a
+ * given property.
+ */
+ class PropertyBuffer
+ {
+ public:
+ PropertyBuffer();
+ /**
+ * Add a new value to the buffer, overwriting the oldest one if needed.
+ */
+ void Update(RendertraceProperty aProperty, const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock);
+ /**
+ * Dump the recorded values, oldest to newest, to the given vector, and
+ * remove them from this buffer.
+ */
+ void Flush(std::vector<PropertyValue>& aOut,
+ const MonitorAutoLock& aProofOfLock);
+
+ private:
+ static const uint32_t BUFFER_SIZE = 5;
+
+ /**
+ * The index of the oldest value in the buffer. This is the next index
+ * that will be written to.
+ */
+ uint32_t mIndex;
+ PropertyValue mValues[BUFFER_SIZE];
+ };
+
+private:
+ /**
+ * If true, we should log the various properties during the checkerboard
+ * event.
+ */
+ const bool mRecordTrace;
+ /**
+ * A base time so that the other timestamps can be turned into durations.
+ */
+ const TimeStamp mOriginTime;
+ /**
+ * Whether or not a checkerboard event is currently occurring.
+ */
+ bool mCheckerboardingActive;
+
+ /**
+ * The start time of the checkerboard event.
+ */
+ TimeStamp mStartTime;
+ /**
+ * The end time of the checkerboard event.
+ */
+ TimeStamp mEndTime;
+ /**
+ * The sample time of the last frame recorded.
+ */
+ TimeStamp mLastSampleTime;
+ /**
+ * The number of contiguous frames with checkerboard.
+ */
+ uint32_t mFrameCount;
+ /**
+ * The total number of pixel-milliseconds of checkerboarding visible to
+ * the user during the checkerboarding event.
+ */
+ uint64_t mTotalPixelMs;
+ /**
+ * The largest number of pixels of checkerboarding visible to the user
+ * during any one frame, during this checkerboarding event.
+ */
+ uint32_t mPeakPixels;
+
+ /**
+ * Monitor that needs to be acquired before touching mBufferedProperties
+ * or mRendertraceInfo.
+ */
+ mutable Monitor mRendertraceLock;
+ /**
+ * A circular buffer to store some properties. This is used before the
+ * checkerboarding actually starts, so that we have some data on what
+ * was happening before the checkerboarding started.
+ */
+ PropertyBuffer mBufferedProperties[MAX_RendertraceProperty];
+ /**
+ * The rendertrace info buffer that gives us info on what was happening
+ * during the checkerboard event.
+ */
+ std::ostringstream mRendertraceInfo;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CheckerboardEvent_h
diff --git a/system/graphics/layers/apz/src/DragTracker.cpp b/system/graphics/layers/apz/src/DragTracker.cpp
new file mode 100644
index 000000000..ecd3ff16f
--- /dev/null
+++ b/system/graphics/layers/apz/src/DragTracker.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DragTracker.h"
+
+#include "InputData.h"
+
+#define DRAG_LOG(...)
+// #define DRAG_LOG(...) printf_stderr("DRAG: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+DragTracker::DragTracker()
+ : mInDrag(false)
+{
+}
+
+/*static*/ bool
+DragTracker::StartsDrag(const MouseInput& aInput)
+{
+ return aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_DOWN;
+}
+
+/*static*/ bool
+DragTracker::EndsDrag(const MouseInput& aInput)
+{
+ // On Windows, we don't receive a MOUSE_UP at the end of a drag if an
+ // actual drag session took place. As a backup, we detect the end of the
+ // drag using the MOUSE_DRAG_END event, which normally is routed directly
+ // to content, but we're specially routing to APZ for this purpose. Bug
+ // 1265105 tracks a solution to this at the Windows widget layer; once
+ // that is implemented, this workaround can be removed.
+ return (aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_UP)
+ || aInput.mType == MouseInput::MOUSE_DRAG_END;
+}
+
+void
+DragTracker::Update(const MouseInput& aInput)
+{
+ if (StartsDrag(aInput)) {
+ DRAG_LOG("Starting drag\n");
+ mInDrag = true;
+ } else if (EndsDrag(aInput)) {
+ DRAG_LOG("Ending drag\n");
+ mInDrag = false;
+ mOnScrollbar = Nothing();
+ }
+}
+
+bool
+DragTracker::InDrag() const
+{
+ return mInDrag;
+}
+
+bool
+DragTracker::IsOnScrollbar(bool aOnScrollbar)
+{
+ if (!mOnScrollbar) {
+ DRAG_LOG("Setting hitscrollbar %d\n", aOnScrollbar);
+ mOnScrollbar = Some(aOnScrollbar);
+ }
+ return mOnScrollbar.value();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/DragTracker.h b/system/graphics/layers/apz/src/DragTracker.h
new file mode 100644
index 000000000..9f7ff1222
--- /dev/null
+++ b/system/graphics/layers/apz/src/DragTracker.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DragTracker_h
+#define mozilla_layers_DragTracker_h
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class MouseInput;
+
+namespace layers {
+
+// DragTracker simply tracks a sequence of mouse inputs and allows us to tell
+// if we are in a drag or not (i.e. the left mouse button went down and hasn't
+// gone up yet).
+class DragTracker
+{
+public:
+ DragTracker();
+ static bool StartsDrag(const MouseInput& aInput);
+ static bool EndsDrag(const MouseInput& aInput);
+ void Update(const MouseInput& aInput);
+ bool InDrag() const;
+ bool IsOnScrollbar(bool aOnScrollbar);
+
+private:
+ Maybe<bool> mOnScrollbar;
+ bool mInDrag;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_DragTracker_h */
diff --git a/system/graphics/layers/apz/src/GenericFlingAnimation.h b/system/graphics/layers/apz/src/GenericFlingAnimation.h
new file mode 100644
index 000000000..88d799028
--- /dev/null
+++ b/system/graphics/layers/apz/src/GenericFlingAnimation.h
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GenericFlingAnimation_h_
+#define mozilla_layers_GenericFlingAnimation_h_
+
+#include "APZUtils.h"
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "FrameMetrics.h"
+#include "Layers.h"
+#include "Units.h"
+#include "OverscrollHandoffState.h"
+#include "gfxPrefs.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+#define FLING_LOG(...)
+// #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+class GenericFlingAnimation: public AsyncPanZoomAnimation {
+public:
+ GenericFlingAnimation(AsyncPanZoomController& aApzc,
+ PlatformSpecificStateBase* aPlatformSpecificState,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ bool aFlingIsHandedOff,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+ : mApzc(aApzc)
+ , mOverscrollHandoffChain(aOverscrollHandoffChain)
+ , mScrolledApzc(aScrolledApzc)
+ {
+ MOZ_ASSERT(mOverscrollHandoffChain);
+ TimeStamp now = aApzc.GetFrameTime();
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't take the 'overscroll' path in Sample()
+ // on account of one axis which can't scroll having a velocity.
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mX.SetVelocity(0);
+ }
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mY.SetVelocity(0);
+ }
+
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ // If the last fling was very recent and in the same direction as this one,
+ // boost the velocity to be the sum of the two. Check separate axes separately
+ // because we could have two vertical flings with small horizontal components
+ // on the opposite side of zero, and we still want the y-fling to get accelerated.
+ // Note that the acceleration code is only applied on the APZC that initiates
+ // the fling; the accelerated velocities are then handed off using the
+ // normal DispatchFling codepath.
+ // Acceleration is only applied in the APZC that originated the fling,
+ // not in APZCs further down the handoff chain during handoff.
+ bool applyAcceleration = !aFlingIsHandedOff;
+ if (applyAcceleration && !mApzc.mLastFlingTime.IsNull()
+ && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
+ && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
+ if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
+ velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
+ FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
+ mApzc.mX.SetVelocity(velocity.x);
+ }
+ if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
+ velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
+ FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
+ mApzc.mY.SetVelocity(velocity.y);
+ }
+ }
+
+ mApzc.mLastFlingTime = now;
+ mApzc.mLastFlingVelocity = velocity;
+ }
+
+ /**
+ * Advances a fling by an interpolated amount based on the passed in |aDelta|.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the fling animation should be advanced by one frame,
+ * or false if there is no fling or the fling has ended.
+ */
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ float friction = gfxPrefs::APZFlingFriction();
+ float threshold = gfxPrefs::APZFlingStoppedThreshold();
+
+ bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
+ shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
+ // If we shouldn't continue the fling, let's just stop and repaint.
+ if (!shouldContinueFlingX && !shouldContinueFlingY) {
+ FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
+ // This APZC or an APZC further down the handoff chain may be be overscrolled.
+ // Start a snap-back animation on the overscrolled APZC.
+ // Note:
+ // This needs to be a deferred task even though it can safely run
+ // while holding mMonitor, because otherwise, if the overscrolled APZC
+ // is this one, then the SetState(NOTHING) in UpdateAnimation will
+ // stomp on the SetState(SNAP_BACK) it does.
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<AsyncPanZoomController*>(mOverscrollHandoffChain.get(),
+ &OverscrollHandoffChain::SnapBackOverscrolledApzc,
+ &mApzc));
+ return false;
+ }
+
+ // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
+ // Since we need to hand off the velocity to the tree manager in such a case,
+ // we save it here. Would be ParentLayerVector instead of ParentLayerPoint
+ // if we had vector classes.
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
+
+ // Ordinarily we might need to do a ScheduleComposite if either of
+ // the following AdjustDisplacement calls returns true, but this
+ // is already running as part of a FlingAnimation, so we'll be compositing
+ // per frame of animation anyway.
+ ParentLayerPoint overscroll;
+ ParentLayerPoint adjustedOffset;
+ mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
+
+ aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
+
+ // The fling may have caused us to reach the end of our scroll range.
+ if (!IsZero(overscroll)) {
+ // Hand off the fling to the next APZC in the overscroll handoff chain.
+
+ // We may have reached the end of the scroll range along one axis but
+ // not the other. In such a case we only want to hand off the relevant
+ // component of the fling.
+ if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ velocity.y = 0;
+ }
+
+ // To hand off the fling, we attempt to find a target APZC and start a new
+ // fling with the same velocity on that APZC. For simplicity, the actual
+ // overscroll of the current sample is discarded rather than being handed
+ // off. The compositor should sample animations sufficiently frequently
+ // that this is not noticeable. The target APZC is chosen by seeing if
+ // there is an APZC further in the handoff chain which is pannable; if
+ // there isn't, we take the new fling ourselves, entering an overscrolled
+ // state.
+ // Note: APZC is holding mMonitor, so directly calling
+ // HandleFlingOverscroll() (which acquires the tree lock) would violate
+ // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
+ // called after mMonitor is released.
+ FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint,
+ RefPtr<const OverscrollHandoffChain>,
+ RefPtr<const AsyncPanZoomController>>(&mApzc,
+ &AsyncPanZoomController::HandleFlingOverscroll,
+ velocity,
+ mOverscrollHandoffChain,
+ mScrolledApzc));
+
+ // If there is a remaining velocity on this APZC, continue this fling
+ // as well. (This fling and the handed-off fling will run concurrently.)
+ // Note that AdjustDisplacement() will have zeroed out the velocity
+ // along the axes where we're overscrolled.
+ return !IsZero(mApzc.GetVelocityVector());
+ }
+
+ return true;
+ }
+
+private:
+ static bool SameDirection(float aVelocity1, float aVelocity2)
+ {
+ return (aVelocity1 == 0.0f)
+ || (aVelocity2 == 0.0f)
+ || (IsNegative(aVelocity1) == IsNegative(aVelocity2));
+ }
+
+ static float Accelerate(float aBase, float aSupplemental)
+ {
+ return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
+ + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
+ }
+
+ AsyncPanZoomController& mApzc;
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_GenericFlingAnimation_h_
diff --git a/system/graphics/layers/apz/src/GestureEventListener.cpp b/system/graphics/layers/apz/src/GestureEventListener.cpp
new file mode 100644
index 000000000..94ece07ee
--- /dev/null
+++ b/system/graphics/layers/apz/src/GestureEventListener.cpp
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GestureEventListener.h"
+#include <math.h> // for fabsf
+#include <stddef.h> // for size_t
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "base/task.h" // for CancelableTask, etc
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
+#include "nsDebug.h" // for NS_WARNING
+#include "nsMathUtils.h" // for NS_hypot
+#include "InputBlockState.h" // for TouchBlockState
+
+#define GEL_LOG(...)
+// #define GEL_LOG(...) printf_stderr("GEL: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Maximum time for a touch on the screen and corresponding lift of the finger
+ * to be considered a tap. This also applies to double taps, except that it is
+ * used twice.
+ */
+static const uint32_t MAX_TAP_TIME = 300;
+
+/**
+ * Amount of span or focus change needed to take us from the GESTURE_WAITING_PINCH
+ * state to the GESTURE_PINCH state. This is measured as either a change in distance
+ * between the fingers used to compute the span ratio, or the a change in
+ * position of the focus point between the two fingers.
+ */
+static const float PINCH_START_THRESHOLD = 35.0f;
+
+static bool sLongTapEnabled = true;
+
+ParentLayerPoint GetCurrentFocus(const MultiTouchInput& aEvent)
+{
+ const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
+ const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
+ return (firstTouch + secondTouch) / 2;
+}
+
+ParentLayerCoord GetCurrentSpan(const MultiTouchInput& aEvent)
+{
+ const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
+ const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
+ ParentLayerPoint delta = secondTouch - firstTouch;
+ return delta.Length();
+}
+
+TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch, TapGestureInput::TapGestureType aType)
+{
+ return TapGestureInput(aType,
+ aTouch.mTime,
+ aTouch.mTimeStamp,
+ aTouch.mTouches[0].mScreenPoint,
+ aTouch.modifiers);
+}
+
+GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
+ : mAsyncPanZoomController(aAsyncPanZoomController),
+ mState(GESTURE_NONE),
+ mSpanChange(0.0f),
+ mPreviousSpan(0.0f),
+ mFocusChange(0.0f),
+ mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLastTapInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLongTapTimeoutTask(nullptr),
+ mMaxTapTimeoutTask(nullptr)
+{
+}
+
+GestureEventListener::~GestureEventListener()
+{
+}
+
+nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
+{
+ GEL_LOG("Receiving event type %d with %" PRIuSIZE " touches in state %d\n", aEvent.mType, aEvent.mTouches.Length(), mState);
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ // Cache the current event since it may become the single or long tap that we
+ // send.
+ mLastTouchInput = aEvent;
+
+ switch (aEvent.mType) {
+ case MultiTouchInput::MULTITOUCH_START:
+ mTouches.Clear();
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ mTouches.AppendElement(aEvent.mTouches[i]);
+ }
+
+ if (aEvent.mTouches.Length() == 1) {
+ rv = HandleInputTouchSingleStart();
+ } else {
+ rv = HandleInputTouchMultiStart();
+ }
+ break;
+ case MultiTouchInput::MULTITOUCH_MOVE:
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches[j].mScreenPoint = aEvent.mTouches[i].mScreenPoint;
+ mTouches[j].mLocalScreenPoint = aEvent.mTouches[i].mLocalScreenPoint;
+ }
+ }
+ }
+ rv = HandleInputTouchMove();
+ break;
+ case MultiTouchInput::MULTITOUCH_END:
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches.RemoveElementAt(j);
+ break;
+ }
+ }
+ }
+
+ rv = HandleInputTouchEnd();
+ break;
+ case MultiTouchInput::MULTITOUCH_CANCEL:
+ mTouches.Clear();
+ rv = HandleInputTouchCancel();
+ break;
+ case MultiTouchInput::MULTITOUCH_SENTINEL:
+ MOZ_ASSERT_UNREACHABLE("Invalid MultTouchInput.");
+ break;
+ }
+
+ return rv;
+}
+
+int32_t GestureEventListener::GetLastTouchIdentifier() const
+{
+ if (mTouches.Length() != 1) {
+ NS_WARNING("GetLastTouchIdentifier() called when last touch event "
+ "did not have one touch");
+ }
+ return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
+}
+
+/* static */
+void GestureEventListener::SetLongTapEnabled(bool aLongTapEnabled)
+{
+ sLongTapEnabled = aLongTapEnabled;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchSingleStart()
+{
+ switch (mState) {
+ case GESTURE_NONE:
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_DOWN);
+ mTouchStartPosition = mLastTouchInput.mTouches[0].mLocalScreenPoint;
+
+ if (sLongTapEnabled) {
+ CreateLongTapTimeoutTask();
+ }
+ CreateMaxTapTimeoutTask();
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
+ break;
+ default:
+ NS_WARNING("Unhandled state upon single touch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
+{
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
+ // Cancel wait for double tap
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_LONG_TOUCH_DOWN:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_MULTI_TOUCH_DOWN:
+ case GESTURE_PINCH:
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ default:
+ NS_WARNING("Unhandled state upon multitouch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+bool GestureEventListener::MoveDistanceIsLarge()
+{
+ const ParentLayerPoint start = mLastTouchInput.mTouches[0].mLocalScreenPoint;
+ ParentLayerPoint delta = start - mTouchStartPosition;
+ ScreenPoint screenDelta = mAsyncPanZoomController->ToScreenCoordinates(delta, start);
+ return (screenDelta.Length() > AsyncPanZoomController::GetTouchStartTolerance());
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMove()
+{
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // Ignore this input signal as the corresponding events get handled by APZC
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN:
+ if (MoveDistanceIsLarge()) {
+ // So that we don't fire a long-tap-up if the user moves around after a
+ // long-tap
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ // If we move too much, bail out of the tap.
+ if (MoveDistanceIsLarge()) {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ }
+ break;
+ }
+
+ case GESTURE_MULTI_TOUCH_DOWN: {
+ if (mLastTouchInput.mTouches.Length() < 2) {
+ NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
+ break;
+ }
+
+ ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+ ParentLayerPoint currentFocus = GetCurrentFocus(mLastTouchInput);
+
+ mSpanChange += fabsf(currentSpan - mPreviousSpan);
+ mFocusChange += (currentFocus - mPreviousFocus).Length();
+ if (mSpanChange > PINCH_START_THRESHOLD ||
+ mFocusChange > PINCH_START_THRESHOLD) {
+ SetState(GESTURE_PINCH);
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ currentFocus,
+ currentSpan,
+ currentSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ } else {
+ // Prevent APZC::OnTouchMove from processing a move event when two
+ // touches are active
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+
+ mPreviousSpan = currentSpan;
+ mPreviousFocus = currentFocus;
+ break;
+ }
+
+ case GESTURE_PINCH: {
+ if (mLastTouchInput.mTouches.Length() < 2) {
+ NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state");
+ // Prevent APZC::OnTouchMove() from handling this wrong input
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ }
+
+ ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ GetCurrentFocus(mLastTouchInput),
+ currentSpan,
+ mPreviousSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ mPreviousSpan = currentSpan;
+
+ break;
+ }
+
+ default:
+ NS_WARNING("Unhandled state upon touch move");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchEnd()
+{
+ // We intentionally do not pass apzc return statuses up since
+ // it may cause apzc stay in the touching state even after
+ // gestures are completed (please see Bug 1013378 for reference).
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP));
+ mSingleTapSent = Some(tapupStatus != nsEventStatus_eIgnore);
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
+ CreateMaxTapTimeoutTask();
+ break;
+ }
+
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput,
+ mSingleTapSent.value() ? TapGestureInput::TAPGESTURE_SECOND
+ : TapGestureInput::TAPGESTURE_DOUBLE));
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_NONE);
+ TriggerSingleTapConfirmedEvent();
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN: {
+ SetState(GESTURE_NONE);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG_UP));
+ break;
+ }
+
+ case GESTURE_MULTI_TOUCH_DOWN:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_PINCH:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ ParentLayerPoint point(-1, -1);
+ if (mTouches.Length() == 1) {
+ // As user still keeps one finger down the event's focus point should
+ // contain meaningful data.
+ point = mTouches[0].mLocalScreenPoint;
+ }
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ point,
+ 1.0f,
+ 1.0f,
+ mLastTouchInput.modifiers);
+ mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ }
+
+ rv = nsEventStatus_eConsumeNoDefault;
+
+ break;
+
+ default:
+ NS_WARNING("Unhandled state upon touch end");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchCancel()
+{
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ CancelMaxTapTimeoutTask();
+ CancelLongTapTimeoutTask();
+ return nsEventStatus_eIgnore;
+}
+
+void GestureEventListener::HandleInputTimeoutLongTap()
+{
+ GEL_LOG("Running long-tap timeout task in state %d\n", mState);
+
+ mLongTapTimeoutTask = nullptr;
+
+ switch (mState) {
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ // just in case MAX_TAP_TIME > ContextMenuDelay cancel MAX_TAP timer
+ // and fall through
+ CancelMaxTapTimeoutTask();
+ MOZ_FALLTHROUGH;
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
+ SetState(GESTURE_LONG_TOUCH_DOWN);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG));
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled state upon long tap timeout");
+ SetState(GESTURE_NONE);
+ break;
+ }
+}
+
+void GestureEventListener::HandleInputTimeoutMaxTap(bool aDuringFastFling)
+{
+ GEL_LOG("Running max-tap timeout task in state %d\n", mState);
+
+ mMaxTapTimeoutTask = nullptr;
+
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) {
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN);
+ } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP ||
+ mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!aDuringFastFling && !mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ } else {
+ NS_WARNING("Unhandled state upon MAX_TAP timeout");
+ SetState(GESTURE_NONE);
+ }
+}
+
+void GestureEventListener::TriggerSingleTapConfirmedEvent()
+{
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTapInput, TapGestureInput::TAPGESTURE_CONFIRMED));
+}
+
+void GestureEventListener::SetState(GestureState aState)
+{
+ mState = aState;
+
+ if (mState == GESTURE_NONE) {
+ mSpanChange = 0.0f;
+ mPreviousSpan = 0.0f;
+ mFocusChange = 0.0f;
+ } else if (mState == GESTURE_MULTI_TOUCH_DOWN) {
+ mPreviousSpan = GetCurrentSpan(mLastTouchInput);
+ mPreviousFocus = GetCurrentFocus(mLastTouchInput);
+ }
+}
+
+void GestureEventListener::CancelLongTapTimeoutTask()
+{
+ if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ // being in this state means the task has been canceled already
+ return;
+ }
+
+ if (mLongTapTimeoutTask) {
+ mLongTapTimeoutTask->Cancel();
+ mLongTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateLongTapTimeoutTask()
+{
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &GestureEventListener::HandleInputTimeoutLongTap);
+
+ mLongTapTimeoutTask = task;
+ mAsyncPanZoomController->PostDelayedTask(
+ task.forget(),
+ gfxPrefs::UiClickHoldContextMenusDelay());
+}
+
+void GestureEventListener::CancelMaxTapTimeoutTask()
+{
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) {
+ // being in this state means the timer has just been triggered
+ return;
+ }
+
+ if (mMaxTapTimeoutTask) {
+ mMaxTapTimeoutTask->Cancel();
+ mMaxTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateMaxTapTimeoutTask()
+{
+ mLastTapInput = mLastTouchInput;
+
+ TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
+ MOZ_ASSERT(block);
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<bool>(this,
+ &GestureEventListener::HandleInputTimeoutMaxTap,
+ block->IsDuringFastFling());
+
+ mMaxTapTimeoutTask = task;
+ mAsyncPanZoomController->PostDelayedTask(
+ task.forget(),
+ MAX_TAP_TIME);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/GestureEventListener.h b/system/graphics/layers/apz/src/GestureEventListener.h
new file mode 100644
index 000000000..cfc9c291d
--- /dev/null
+++ b/system/graphics/layers/apz/src/GestureEventListener.h
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GestureEventListener_h
+#define mozilla_layers_GestureEventListener_h
+
+#include "InputData.h" // for MultiTouchInput, etc
+#include "Units.h"
+#include "mozilla/EventForwards.h" // for nsEventStatus
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h"
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace layers {
+
+class AsyncPanZoomController;
+
+/**
+ * Platform-non-specific, generalized gesture event listener. This class
+ * intercepts all touches events on their way to AsyncPanZoomController and
+ * determines whether or not they are part of a gesture.
+ *
+ * For example, seeing that two fingers are on the screen means that the user
+ * wants to do a pinch gesture, so we don't forward the touches along to
+ * AsyncPanZoomController since it will think that they are just trying to pan
+ * the screen. Instead, we generate a PinchGestureInput and send that. If the
+ * touch event is not part of a gesture, we just return nsEventStatus_eIgnore
+ * and AsyncPanZoomController is expected to handle it.
+ *
+ * Android doesn't use this class because it has its own built-in gesture event
+ * listeners that should generally be preferred.
+ */
+class GestureEventListener final {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener)
+
+ explicit GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the controller/UI thread.
+ //
+
+ /**
+ * General input handler for a touch event. If the touch event is not a part
+ * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
+ * it gets consumed here and never forwarded along.
+ */
+ nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
+
+ /**
+ * Returns the identifier of the touch in the last touch event processed by
+ * this GestureEventListener. This should only be called when the last touch
+ * event contained only one touch.
+ */
+ int32_t GetLastTouchIdentifier() const;
+
+ /**
+ * Function used to disable long tap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ static void SetLongTapEnabled(bool aLongTapEnabled);
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~GestureEventListener();
+
+ /**
+ * States of GEL finite-state machine.
+ */
+ enum GestureState {
+ // This is the initial and final state of any gesture.
+ // In this state there's no gesture going on, and we don't think we're
+ // about to enter one.
+ // Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_NONE,
+
+ // A touch start with a single touch point has just happened.
+ // After having gotten into this state we start timers for MAX_TAP_TIME and
+ // gfxPrefs::UiClickHoldContextMenusDelay().
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_FIRST_SINGLE_TOUCH_UP, GESTURE_LONG_TOUCH_DOWN,
+ // GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_DOWN,
+
+ // While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got
+ // triggered. Now we'll trigger either a single tap if a user lifts her
+ // finger or a long tap if gfxPrefs::UiClickHoldContextMenusDelay() happens
+ // first.
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_LONG_TOUCH_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN,
+
+ // A user put her finger down and lifted it up quickly enough.
+ // After having gotten into this state we clear the timer for MAX_TAP_TIME.
+ // Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_UP,
+
+ // A user put down her finger again right after a single tap thus the
+ // gesture can't be a single tap, but rather a double tap. But we're
+ // still not sure about that until the user lifts her finger again.
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE.
+ GESTURE_SECOND_SINGLE_TOUCH_DOWN,
+
+ // A long touch has happened, but the user still keeps her finger down.
+ // We'll trigger a "long tap up" event when the finger is up.
+ // Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_LONG_TOUCH_DOWN,
+
+ // We have detected that two or more fingers are on the screen, but there
+ // hasn't been enough movement yet to make us start actually zooming the
+ // screen.
+ // Allowed next states: GESTURE_PINCH, GESTURE_NONE
+ GESTURE_MULTI_TOUCH_DOWN,
+
+ // There are two or more fingers on the screen, and the user has already
+ // pinched enough for us to start zooming the screen.
+ // Allowed next states: GESTURE_NONE
+ GESTURE_PINCH
+ };
+
+ /**
+ * These HandleInput* functions comprise input alphabet of the GEL
+ * finite-state machine triggering state transitions.
+ */
+ nsEventStatus HandleInputTouchSingleStart();
+ nsEventStatus HandleInputTouchMultiStart();
+ nsEventStatus HandleInputTouchEnd();
+ nsEventStatus HandleInputTouchMove();
+ nsEventStatus HandleInputTouchCancel();
+ void HandleInputTimeoutLongTap();
+ void HandleInputTimeoutMaxTap(bool aDuringFastFling);
+
+ void TriggerSingleTapConfirmedEvent();
+
+ bool MoveDistanceIsLarge();
+
+ /**
+ * Do actual state transition and reset substates.
+ */
+ void SetState(GestureState aState);
+
+ RefPtr<AsyncPanZoomController> mAsyncPanZoomController;
+
+ /**
+ * Array containing all active touches. When a touch happens it, gets added to
+ * this array, even if we choose not to handle it. When it ends, we remove it.
+ * We need to maintain this array in order to detect the end of the
+ * "multitouch" states because touch start events contain all current touches,
+ * but touch end events contain only those touches that have gone.
+ */
+ nsTArray<SingleTouchData> mTouches;
+
+ /**
+ * Current state we're dealing with.
+ */
+ GestureState mState;
+
+ /**
+ * Total change in span since we detected a pinch gesture. Only used when we
+ * are in the |GESTURE_WAITING_PINCH| state and need to know how far zoomed
+ * out we are compared to our original pinch span. Note that this does _not_
+ * continue to be updated once we jump into the |GESTURE_PINCH| state.
+ */
+ ParentLayerCoord mSpanChange;
+
+ /**
+ * Previous span calculated for the purposes of setting inside a
+ * PinchGestureInput.
+ */
+ ParentLayerCoord mPreviousSpan;
+
+ /* Properties similar to mSpanChange and mPreviousSpan, but for the focus */
+ ParentLayerCoord mFocusChange;
+ ParentLayerPoint mPreviousFocus;
+
+ /**
+ * Cached copy of the last touch input.
+ */
+ MultiTouchInput mLastTouchInput;
+
+ /**
+ * Cached copy of the last tap gesture input.
+ * In the situation when we have a tap followed by a pinch we lose info
+ * about tap since we keep only last input and to dispatch it correctly
+ * we save last tap copy into this variable.
+ * For more info see bug 947892.
+ */
+ MultiTouchInput mLastTapInput;
+
+ /**
+ * Position of the last touch starting. This is only valid during an attempt
+ * to determine if a touch is a tap. If a touch point moves away from
+ * mTouchStartPosition to the distance greater than
+ * AsyncPanZoomController::GetTouchStartTolerance() while in
+ * GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN
+ * or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is
+ * not tap.
+ */
+ ParentLayerPoint mTouchStartPosition;
+
+ /**
+ * Task used to timeout a long tap. This gets posted to the UI thread such
+ * that it runs a time when a single tap happens. We cache it so that
+ * we can cancel it if any other touch event happens.
+ *
+ * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN
+ * and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states.
+ *
+ * CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set
+ * it to null.
+ */
+ RefPtr<CancelableRunnable> mLongTapTimeoutTask;
+ void CancelLongTapTimeoutTask();
+ void CreateLongTapTimeoutTask();
+
+ /**
+ * Task used to timeout a single tap or a double tap.
+ *
+ * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN,
+ * GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
+ *
+ * CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
+ * it to null.
+ */
+ RefPtr<CancelableRunnable> mMaxTapTimeoutTask;
+ void CancelMaxTapTimeoutTask();
+ void CreateMaxTapTimeoutTask();
+
+ /**
+ * Tracks whether the single-tap event was already sent to content. This is
+ * needed because it affects how the double-tap gesture, if detected, is
+ * handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP and
+ * GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is
+ * stored in a Maybe which is set to Nothing() at all other times.
+ */
+ Maybe<bool> mSingleTapSent;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/apz/src/HitTestingTreeNode.cpp b/system/graphics/layers/apz/src/HitTestingTreeNode.cpp
new file mode 100644
index 000000000..11867a133
--- /dev/null
+++ b/system/graphics/layers/apz/src/HitTestingTreeNode.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HitTestingTreeNode.h"
+
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "LayersLogging.h" // for Stringify
+#include "mozilla/gfx/Point.h" // for Point4D
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform::operator Matrix4x4()
+#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "UnitTransforms.h" // for ViewAs
+
+namespace mozilla {
+namespace layers {
+
+HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
+ bool aIsPrimaryHolder,
+ uint64_t aLayersId)
+ : mApzc(aApzc)
+ , mIsPrimaryApzcHolder(aIsPrimaryHolder)
+ , mLayersId(aLayersId)
+ , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
+ , mScrollDir(Layer::NONE)
+ , mScrollSize(0)
+ , mIsScrollbarContainer(false)
+ , mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
+ , mOverride(EventRegionsOverride::NoOverride)
+{
+if (mIsPrimaryApzcHolder) {
+ MOZ_ASSERT(mApzc);
+ }
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+}
+
+void
+HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc,
+ uint64_t aLayersId)
+{
+ MOZ_ASSERT(!mIsPrimaryApzcHolder);
+ Destroy(); // clear out tree pointers
+ mApzc = aApzc;
+ mLayersId = aLayersId;
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+ // The caller is expected to call SetHitTestData to repopulate the hit-test
+ // fields.
+}
+
+HitTestingTreeNode::~HitTestingTreeNode()
+{
+}
+
+void
+HitTestingTreeNode::Destroy()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ mPrevSibling = nullptr;
+ mLastChild = nullptr;
+ mParent = nullptr;
+
+ if (mApzc) {
+ if (mIsPrimaryApzcHolder) {
+ mApzc->Destroy();
+ }
+ mApzc = nullptr;
+ }
+
+ mLayersId = 0;
+}
+
+void
+HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
+{
+ mLastChild = aChild;
+ if (aChild) {
+ aChild->mParent = this;
+
+ if (aChild->GetApzc()) {
+ AsyncPanZoomController* parent = GetNearestContainingApzc();
+ // We assume that HitTestingTreeNodes with an ancestor/descendant
+ // relationship cannot both point to the same APZC instance. This
+ // assertion only covers a subset of cases in which that might occur,
+ // but it's better than nothing.
+ MOZ_ASSERT(aChild->GetApzc() != parent);
+ aChild->SetApzcParent(parent);
+ }
+ }
+}
+
+void
+HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+ Layer::ScrollDirection aDir,
+ int32_t aScrollSize,
+ bool aIsScrollContainer)
+{
+ mScrollViewId = aScrollViewId;
+ mScrollDir = aDir;
+ mScrollSize = aScrollSize;;
+ mIsScrollbarContainer = aIsScrollContainer;
+}
+
+bool
+HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
+{
+ return ((mScrollDir == Layer::HORIZONTAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) ||
+ (mScrollDir == Layer::VERTICAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::VERTICAL)) &&
+ mScrollViewId == aDragMetrics.mViewId;
+}
+
+int32_t
+HitTestingTreeNode::GetScrollSize() const
+{
+ return mScrollSize;
+}
+
+bool
+HitTestingTreeNode::IsScrollbarNode() const
+{
+ return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
+}
+
+void
+HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
+{
+ mFixedPosTarget = aFixedPosTarget;
+}
+
+FrameMetrics::ViewID
+HitTestingTreeNode::GetFixedPosTarget() const
+{
+ return mFixedPosTarget;
+}
+
+void
+HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
+{
+ mPrevSibling = aSibling;
+ if (aSibling) {
+ aSibling->mParent = mParent;
+
+ if (aSibling->GetApzc()) {
+ AsyncPanZoomController* parent = mParent ? mParent->GetNearestContainingApzc() : nullptr;
+ aSibling->SetApzcParent(parent);
+ }
+ }
+}
+
+void
+HitTestingTreeNode::MakeRoot()
+{
+ mParent = nullptr;
+
+ if (GetApzc()) {
+ SetApzcParent(nullptr);
+ }
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetFirstChild() const
+{
+ HitTestingTreeNode* child = GetLastChild();
+ while (child && child->GetPrevSibling()) {
+ child = child->GetPrevSibling();
+ }
+ return child;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetLastChild() const
+{
+ return mLastChild;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetPrevSibling() const
+{
+ return mPrevSibling;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetParent() const
+{
+ return mParent;
+}
+
+AsyncPanZoomController*
+HitTestingTreeNode::GetApzc() const
+{
+ return mApzc;
+}
+
+AsyncPanZoomController*
+HitTestingTreeNode::GetNearestContainingApzc() const
+{
+ for (const HitTestingTreeNode* n = this; n; n = n->GetParent()) {
+ if (n->GetApzc()) {
+ return n->GetApzc();
+ }
+ }
+ return nullptr;
+}
+
+bool
+HitTestingTreeNode::IsPrimaryHolder() const
+{
+ return mIsPrimaryApzcHolder;
+}
+
+uint64_t
+HitTestingTreeNode::GetLayersId() const
+{
+ return mLayersId;
+}
+
+void
+HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
+ const CSSTransformMatrix& aTransform,
+ const Maybe<ParentLayerIntRegion>& aClipRegion,
+ const EventRegionsOverride& aOverride)
+{
+ mEventRegions = aRegions;
+ mTransform = aTransform;
+ mClipRegion = aClipRegion;
+ mOverride = aOverride;
+}
+
+bool
+HitTestingTreeNode::IsOutsideClip(const ParentLayerPoint& aPoint) const
+{
+ // test against clip rect in ParentLayer coordinate space
+ return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
+}
+
+Maybe<LayerPoint>
+HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
+{
+ // convert into Layer coordinate space
+ LayerToParentLayerMatrix4x4 transform = mTransform *
+ CompleteAsyncTransform(
+ mApzc
+ ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
+ : AsyncTransformComponentMatrix());
+ return UntransformBy(transform.Inverse(), aPoint);
+}
+
+HitTestResult
+HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
+{
+ // This should only ever get called if the point is inside the clip region
+ // for this node.
+ MOZ_ASSERT(!IsOutsideClip(aPoint));
+
+ if (mOverride & EventRegionsOverride::ForceEmptyHitRegion) {
+ return HitTestResult::HitNothing;
+ }
+
+ // convert into Layer coordinate space
+ Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
+ if (!pointInLayerPixels) {
+ return HitTestResult::HitNothing;
+ }
+ auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
+
+ // test against event regions in Layer coordinate space
+ if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitNothing;
+ }
+ if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
+ mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
+ {
+ return HitTestResult::HitDispatchToContentRegion;
+ }
+ if (gfxPrefs::TouchActionEnabled()) {
+ if (mEventRegions.mNoActionRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitLayerTouchActionNone;
+ }
+ bool panX = mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y);
+ bool panY = mEventRegions.mVerticalPanRegion.Contains(point.x, point.y);
+ if (panX && panY) {
+ return HitTestResult::HitLayerTouchActionPanXY;
+ } else if (panX) {
+ return HitTestResult::HitLayerTouchActionPanX;
+ } else if (panY) {
+ return HitTestResult::HitLayerTouchActionPanY;
+ }
+ }
+ return HitTestResult::HitLayer;
+}
+
+EventRegionsOverride
+HitTestingTreeNode::GetEventRegionsOverride() const
+{
+ return mOverride;
+}
+
+void
+HitTestingTreeNode::Dump(const char* aPrefix) const
+{
+ if (mPrevSibling) {
+ mPrevSibling->Dump(aPrefix);
+ }
+ printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%sr=(%s) t=(%s) c=(%s)\n",
+ aPrefix, this, mApzc.get(),
+ mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=%" PRIu64, mLayersId).get(),
+ (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "",
+ (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
+ (mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() : "",
+ Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
+ mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
+ if (mLastChild) {
+ mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
+ }
+}
+
+void
+HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent)
+{
+ // precondition: GetApzc() is non-null
+ MOZ_ASSERT(GetApzc() != nullptr);
+ if (IsPrimaryHolder()) {
+ GetApzc()->SetParent(aParent);
+ } else {
+ MOZ_ASSERT(GetApzc()->GetParent() == aParent);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/HitTestingTreeNode.h b/system/graphics/layers/apz/src/HitTestingTreeNode.h
new file mode 100644
index 000000000..f3601eba7
--- /dev/null
+++ b/system/graphics/layers/apz/src/HitTestingTreeNode.h
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_HitTestingTreeNode_h
+#define mozilla_layers_HitTestingTreeNode_h
+
+#include "APZUtils.h" // for HitTestResult
+#include "FrameMetrics.h" // for ScrollableLayerGuid
+#include "Layers.h"
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/LayersTypes.h" // for EventRegions
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h" // for nsRefPtr
+
+namespace mozilla {
+namespace layers {
+
+class AsyncDragMetrics;
+class AsyncPanZoomController;
+
+/**
+ * This class represents a node in a tree that is used by the APZCTreeManager
+ * to do hit testing. The tree is roughly a copy of the layer tree, but will
+ * contain multiple nodes in cases where the layer has multiple FrameMetrics.
+ * In other words, the structure of this tree should be identical to the
+ * LayerMetrics tree (see documentation in LayerMetricsWrapper.h).
+ *
+ * Not all HitTestingTreeNode instances will have an APZC associated with them;
+ * only HitTestingTreeNodes that correspond to layers with scrollable metrics
+ * have APZCs.
+ * Multiple HitTestingTreeNode instances may share the same underlying APZC
+ * instance if the layers they represent share the same scrollable metrics (i.e.
+ * are part of the same animated geometry root). If this happens, exactly one of
+ * the HitTestingTreeNode instances will be designated as the "primary holder"
+ * of the APZC. When this primary holder is destroyed, it will destroy the APZC
+ * along with it; in contrast, destroying non-primary-holder nodes will not
+ * destroy the APZC.
+ * Code should not make assumptions about which of the nodes will be the
+ * primary holder, only that that there will be exactly one for each APZC in
+ * the tree.
+ *
+ * The reason this tree exists at all is so that we can do hit-testing on the
+ * thread that we receive input on (referred to the as the controller thread in
+ * APZ terminology), which may be different from the compositor thread.
+ * Accessing the compositor layer tree can only be done on the compositor
+ * thread, and so it is simpler to make a copy of the hit-testing related
+ * properties into a separate tree.
+ */
+class HitTestingTreeNode {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
+
+private:
+ ~HitTestingTreeNode();
+public:
+ HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder,
+ uint64_t aLayersId);
+ void RecycleWith(AsyncPanZoomController* aApzc, uint64_t aLayersId);
+ void Destroy();
+
+ /* Tree construction methods */
+
+ void SetLastChild(HitTestingTreeNode* aChild);
+ void SetPrevSibling(HitTestingTreeNode* aSibling);
+ void MakeRoot();
+
+ /* Tree walking methods. GetFirstChild is O(n) in the number of children. The
+ * other tree walking methods are all O(1). */
+
+ HitTestingTreeNode* GetFirstChild() const;
+ HitTestingTreeNode* GetLastChild() const;
+ HitTestingTreeNode* GetPrevSibling() const;
+ HitTestingTreeNode* GetParent() const;
+
+ /* APZC related methods */
+
+ AsyncPanZoomController* GetApzc() const;
+ AsyncPanZoomController* GetNearestContainingApzc() const;
+ bool IsPrimaryHolder() const;
+ uint64_t GetLayersId() const;
+
+ /* Hit test related methods */
+
+ void SetHitTestData(const EventRegions& aRegions,
+ const CSSTransformMatrix& aTransform,
+ const Maybe<ParentLayerIntRegion>& aClipRegion,
+ const EventRegionsOverride& aOverride);
+ bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
+
+ /* Scrollbar info */
+
+ void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+ Layer::ScrollDirection aDir,
+ int32_t aScrollSize,
+ bool aIsScrollContainer);
+ bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
+ int32_t GetScrollSize() const;
+ bool IsScrollbarNode() const;
+
+ /* Fixed pos info */
+
+ void SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget);
+ FrameMetrics::ViewID GetFixedPosTarget() const;
+
+ /* Convert aPoint into the LayerPixel space for the layer corresponding to
+ * this node. */
+ Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
+ /* Assuming aPoint is inside the clip region for this node, check which of the
+ * event region spaces it falls inside. */
+ HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
+ /* Returns the mOverride flag. */
+ EventRegionsOverride GetEventRegionsOverride() const;
+
+ /* Debug helpers */
+ void Dump(const char* aPrefix = "") const;
+
+private:
+ void SetApzcParent(AsyncPanZoomController* aApzc);
+
+ RefPtr<HitTestingTreeNode> mLastChild;
+ RefPtr<HitTestingTreeNode> mPrevSibling;
+ RefPtr<HitTestingTreeNode> mParent;
+
+ RefPtr<AsyncPanZoomController> mApzc;
+ bool mIsPrimaryApzcHolder;
+
+ uint64_t mLayersId;
+
+ FrameMetrics::ViewID mScrollViewId;
+ Layer::ScrollDirection mScrollDir;
+ int32_t mScrollSize;
+ bool mIsScrollbarContainer;
+
+ FrameMetrics::ViewID mFixedPosTarget;
+
+ /* Let {L,M} be the {layer, scrollable metrics} pair that this node
+ * corresponds to in the layer tree. mEventRegions contains the event regions
+ * from L, in the case where event-regions are enabled. If event-regions are
+ * disabled, it will contain the visible region of L, which we use as an
+ * approximation to the hit region for the purposes of obscuring other layers.
+ * This value is in L's LayerPixels.
+ */
+ EventRegions mEventRegions;
+
+ /* This is the transform from layer L. This does NOT include any async
+ * transforms. */
+ CSSTransformMatrix mTransform;
+
+ /* This is clip rect for L that we wish to use for hit-testing purposes. Note
+ * that this may not be exactly the same as the clip rect on layer L because
+ * of the touch-sensitive region provided by the GeckoContentController, or
+ * because we may use the composition bounds of the layer if the clip is not
+ * present. This value is in L's ParentLayerPixels. */
+ Maybe<ParentLayerIntRegion> mClipRegion;
+
+ /* Indicates whether or not the event regions on this node need to be
+ * overridden in a certain way. */
+ EventRegionsOverride mOverride;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_HitTestingTreeNode_h
diff --git a/system/graphics/layers/apz/src/InputBlockState.cpp b/system/graphics/layers/apz/src/InputBlockState.cpp
new file mode 100644
index 000000000..c9358e720
--- /dev/null
+++ b/system/graphics/layers/apz/src/InputBlockState.cpp
@@ -0,0 +1,864 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputBlockState.h"
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "AsyncScrollBase.h" // for kScrollSeriesTimeoutMs
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/MouseEvents.h"
+#include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
+#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
+#include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
+
+#define TBS_LOG(...)
+// #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
+
+InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed)
+ : mTargetApzc(aTargetApzc)
+ , mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed
+ : TargetConfirmationState::eUnconfirmed)
+ , mBlockId(sBlockCounter++)
+ , mTransformToApzc(aTargetApzc->GetTransformToThis())
+{
+ // We should never be constructed with a nullptr target.
+ MOZ_ASSERT(mTargetApzc);
+ mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
+}
+
+bool
+InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
+ || aState == TargetConfirmationState::eTimedOut);
+
+ if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
+ aState == TargetConfirmationState::eConfirmed) {
+ // The main thread finally responded. We had already timed out the
+ // confirmation, but we want to update the state internally so that we
+ // can record the time.
+ mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded;
+ }
+ if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) {
+ return false;
+ }
+ mTargetConfirmed = aState;
+
+ TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
+ if (mTargetApzc == aTargetApzc) {
+ // The confirmed target is the same as the tentative one, so we're done.
+ return true;
+ }
+
+ TBS_LOG("%p replacing unconfirmed target %p with real target %p\n",
+ this, mTargetApzc.get(), aTargetApzc.get());
+
+ UpdateTargetApzc(aTargetApzc);
+ return true;
+}
+
+void
+InputBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
+{
+ // note that aTargetApzc MAY be null here.
+ mTargetApzc = aTargetApzc;
+ mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : ScreenToParentLayerMatrix4x4();
+ mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr);
+}
+
+const RefPtr<AsyncPanZoomController>&
+InputBlockState::GetTargetApzc() const
+{
+ return mTargetApzc;
+}
+
+const RefPtr<const OverscrollHandoffChain>&
+InputBlockState::GetOverscrollHandoffChain() const
+{
+ return mOverscrollHandoffChain;
+}
+
+uint64_t
+InputBlockState::GetBlockId() const
+{
+ return mBlockId;
+}
+
+bool
+InputBlockState::IsTargetConfirmed() const
+{
+ return mTargetConfirmed != TargetConfirmationState::eUnconfirmed;
+}
+
+bool
+InputBlockState::HasReceivedRealConfirmedTarget() const
+{
+ return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
+ mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
+}
+
+bool
+InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const
+{
+ if (aA == aB) {
+ return true;
+ }
+
+ bool seenA = false;
+ for (size_t i = 0; i < mOverscrollHandoffChain->Length(); ++i) {
+ AsyncPanZoomController* apzc = mOverscrollHandoffChain->GetApzcAtIndex(i);
+ if (apzc == aB) {
+ return seenA;
+ }
+ if (apzc == aA) {
+ seenA = true;
+ }
+ }
+ return false;
+}
+
+
+void
+InputBlockState::SetScrolledApzc(AsyncPanZoomController* aApzc)
+{
+ // An input block should only have one scrolled APZC.
+ MOZ_ASSERT(!mScrolledApzc || (gfxPrefs::APZAllowImmediateHandoff() ? IsDownchainOf(mScrolledApzc, aApzc) : mScrolledApzc == aApzc));
+
+ mScrolledApzc = aApzc;
+}
+
+AsyncPanZoomController*
+InputBlockState::GetScrolledApzc() const
+{
+ return mScrolledApzc;
+}
+
+bool
+InputBlockState::IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const
+{
+ MOZ_ASSERT(aApzc && mScrolledApzc);
+
+ return IsDownchainOf(mScrolledApzc, aApzc);
+}
+
+CancelableBlockState::CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed)
+ : InputBlockState(aTargetApzc, aTargetConfirmed)
+ , mPreventDefault(false)
+ , mContentResponded(false)
+ , mContentResponseTimerExpired(false)
+{
+}
+
+bool
+CancelableBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (mContentResponded) {
+ return false;
+ }
+ TBS_LOG("%p got content response %d with timer expired %d\n",
+ this, aPreventDefault, mContentResponseTimerExpired);
+ mPreventDefault = aPreventDefault;
+ mContentResponded = true;
+ return true;
+}
+
+void
+CancelableBlockState::StartContentResponseTimer()
+{
+ MOZ_ASSERT(mContentResponseTimer.IsNull());
+ mContentResponseTimer = TimeStamp::Now();
+}
+
+bool
+CancelableBlockState::TimeoutContentResponse()
+{
+ if (mContentResponseTimerExpired) {
+ return false;
+ }
+ TBS_LOG("%p got content timer expired with response received %d\n",
+ this, mContentResponded);
+ if (!mContentResponded) {
+ mPreventDefault = false;
+ }
+ mContentResponseTimerExpired = true;
+ return true;
+}
+
+bool
+CancelableBlockState::IsContentResponseTimerExpired() const
+{
+ return mContentResponseTimerExpired;
+}
+
+bool
+CancelableBlockState::IsDefaultPrevented() const
+{
+ MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
+ return mPreventDefault;
+}
+
+bool
+CancelableBlockState::HasReceivedAllContentNotifications() const
+{
+ return HasReceivedRealConfirmedTarget() && mContentResponded;
+}
+
+bool
+CancelableBlockState::IsReadyForHandling() const
+{
+ if (!IsTargetConfirmed()) {
+ return false;
+ }
+ return mContentResponded || mContentResponseTimerExpired;
+}
+
+void
+CancelableBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
+}
+
+void
+CancelableBlockState::RecordContentResponseTime()
+{
+ if (!mContentResponseTimer) {
+ // We might get responses from content even though we didn't wait for them.
+ // In that case, ignore the time on them, because they're not relevant for
+ // tuning our timeout value. Also this function might get called multiple
+ // times on the same input block, so we should only record the time from the
+ // first successful call.
+ return;
+ }
+ if (!HasReceivedAllContentNotifications()) {
+ // Not done yet, we'll get called again
+ return;
+ }
+ mContentResponseTimer = TimeStamp();
+}
+
+DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const MouseInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mReceivedMouseUp(false)
+{
+}
+
+bool
+DragBlockState::HasReceivedMouseUp()
+{
+ return mReceivedMouseUp;
+}
+
+void
+DragBlockState::MarkMouseUpReceived()
+{
+ mReceivedMouseUp = true;
+}
+
+void
+DragBlockState::SetDragMetrics(const AsyncDragMetrics& aDragMetrics)
+{
+ mDragMetrics = aDragMetrics;
+}
+
+void
+DragBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ MouseInput mouseInput = aEvent.AsMouseInput();
+ if (!mouseInput.TransformToLocal(mTransformToApzc)) {
+ return;
+ }
+
+ GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics);
+}
+
+bool
+DragBlockState::MustStayActive()
+{
+ return !mReceivedMouseUp;
+}
+
+const char*
+DragBlockState::Type()
+{
+ return "drag";
+}
+// This is used to track the current wheel transaction.
+static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
+
+WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mScrollSeriesCounter(0)
+ , mTransactionEnded(false)
+{
+ sLastWheelBlockId = GetBlockId();
+
+ if (aTargetConfirmed) {
+ // Find the nearest APZC in the overscroll handoff chain that is scrollable.
+ // If we get a content confirmation later that the apzc is different, then
+ // content should have found a scrollable apzc, so we don't need to handle
+ // that case.
+ RefPtr<AsyncPanZoomController> apzc =
+ mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
+
+ // If nothing is scrollable, we don't consider this block as starting a
+ // transaction.
+ if (!apzc) {
+ EndTransaction();
+ return;
+ }
+
+ if (apzc != GetTargetApzc()) {
+ UpdateTargetApzc(apzc);
+ }
+ }
+}
+
+bool
+WheelBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (aPreventDefault) {
+ EndTransaction();
+ }
+ return CancelableBlockState::SetContentResponse(aPreventDefault);
+}
+
+bool
+WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ // The APZC that we find via APZCCallbackHelpers may not be the same APZC
+ // ESM or OverscrollHandoff would have computed. Make sure we get the right
+ // one by looking for the first apzc the next pending event can scroll.
+ RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
+ if (apzc && aFirstInput) {
+ apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
+ }
+
+ InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
+ return true;
+}
+
+void
+WheelBlockState::Update(ScrollWheelInput& aEvent)
+{
+ // We might not be in a transaction if the block never started in a
+ // transaction - for example, if nothing was scrollable.
+ if (!InTransaction()) {
+ return;
+ }
+
+ // The current "scroll series" is a like a sub-transaction. It has a separate
+ // timeout of 80ms. Since we need to compute wheel deltas at different phases
+ // of a transaction (for example, when it is updated, and later when the
+ // event action is taken), we affix the scroll series counter to the event.
+ // This makes GetScrollWheelDelta() consistent.
+ if (!mLastEventTime.IsNull() &&
+ (aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs)
+ {
+ mScrollSeriesCounter = 0;
+ }
+ aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter;
+
+ // If we can't scroll in the direction of the wheel event, we don't update
+ // the last move time. This allows us to timeout a transaction even if the
+ // mouse isn't moving.
+ //
+ // We skip this check if the target is not yet confirmed, so that when it is
+ // confirmed, we don't timeout the transaction.
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
+ return;
+ }
+
+ // Update the time of the last known good event, and reset the mouse move
+ // time to null. This will reset the delays on both the general transaction
+ // timeout and the mouse-move-in-frame timeout.
+ mLastEventTime = aEvent.mTimeStamp;
+ mLastMouseMove = TimeStamp();
+}
+
+bool
+WheelBlockState::MustStayActive()
+{
+ return !mTransactionEnded;
+}
+
+const char*
+WheelBlockState::Type()
+{
+ return "scroll wheel";
+}
+
+bool
+WheelBlockState::ShouldAcceptNewEvent() const
+{
+ if (!InTransaction()) {
+ // If we're not in a transaction, start a new one.
+ return false;
+ }
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ if (apzc->IsDestroyed()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+WheelBlockState::MaybeTimeout(const ScrollWheelInput& aEvent)
+{
+ MOZ_ASSERT(InTransaction());
+
+ if (MaybeTimeout(aEvent.mTimeStamp)) {
+ return true;
+ }
+
+ if (!mLastMouseMove.IsNull()) {
+ // If there's a recent mouse movement, we can time out the transaction early.
+ TimeDuration duration = TimeStamp::Now() - mLastMouseMove;
+ if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
+ TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
+ EndTransaction();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+WheelBlockState::MaybeTimeout(const TimeStamp& aTimeStamp)
+{
+ MOZ_ASSERT(InTransaction());
+
+ // End the transaction if the event occurred > 1.5s after the most recently
+ // seen wheel event.
+ TimeDuration duration = aTimeStamp - mLastEventTime;
+ if (duration.ToMilliseconds() < gfxPrefs::MouseWheelTransactionTimeoutMs()) {
+ return false;
+ }
+
+ TBS_LOG("%p wheel transaction timed out\n", this);
+
+ if (gfxPrefs::MouseScrollTestingEnabled()) {
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ apzc->NotifyMozMouseScrollEvent(NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"));
+ }
+
+ EndTransaction();
+ return true;
+}
+
+void
+WheelBlockState::OnMouseMove(const ScreenIntPoint& aPoint)
+{
+ MOZ_ASSERT(InTransaction());
+
+ if (!GetTargetApzc()->Contains(aPoint)) {
+ EndTransaction();
+ return;
+ }
+
+ if (mLastMouseMove.IsNull()) {
+ // If the cursor is moving inside the frame, and it is more than the
+ // ignoremovedelay time since the last scroll operation, we record
+ // this as the most recent mouse movement.
+ TimeStamp now = TimeStamp::Now();
+ TimeDuration duration = now - mLastEventTime;
+ if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
+ mLastMouseMove = now;
+ }
+ }
+}
+
+void
+WheelBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
+{
+ InputBlockState::UpdateTargetApzc(aTargetApzc);
+
+ // If we found there was no target apzc, then we end the transaction.
+ if (!GetTargetApzc()) {
+ EndTransaction();
+ }
+}
+
+bool
+WheelBlockState::InTransaction() const
+{
+ // We consider a wheel block to be in a transaction if it has a confirmed
+ // target and is the most recent wheel input block to be created.
+ if (GetBlockId() != sLastWheelBlockId) {
+ return false;
+ }
+
+ if (mTransactionEnded) {
+ return false;
+ }
+
+ MOZ_ASSERT(GetTargetApzc());
+ return true;
+}
+
+bool
+WheelBlockState::AllowScrollHandoff() const
+{
+ // If we're in a wheel transaction, we do not allow overscroll handoff until
+ // a new event ends the wheel transaction.
+ return !IsTargetConfirmed() || !InTransaction();
+}
+
+void
+WheelBlockState::EndTransaction()
+{
+ TBS_LOG("%p ending wheel transaction\n", this);
+ mTransactionEnded = true;
+}
+
+PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const PanGestureInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mInterrupted(false)
+ , mWaitingForContentResponse(false)
+{
+ if (aTargetConfirmed) {
+ // Find the nearest APZC in the overscroll handoff chain that is scrollable.
+ // If we get a content confirmation later that the apzc is different, then
+ // content should have found a scrollable apzc, so we don't need to handle
+ // that case.
+ RefPtr<AsyncPanZoomController> apzc =
+ mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
+
+ if (apzc && apzc != GetTargetApzc()) {
+ UpdateTargetApzc(apzc);
+ }
+ }
+}
+
+bool
+PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ // The APZC that we find via APZCCallbackHelpers may not be the same APZC
+ // ESM or OverscrollHandoff would have computed. Make sure we get the right
+ // one by looking for the first apzc the next pending event can scroll.
+ RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
+ if (apzc && aFirstInput) {
+ RefPtr<AsyncPanZoomController> scrollableApzc =
+ apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
+ if (scrollableApzc) {
+ apzc = scrollableApzc;
+ }
+ }
+
+ InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
+ return true;
+}
+
+bool
+PanGestureBlockState::MustStayActive()
+{
+ return !mInterrupted;
+}
+
+const char*
+PanGestureBlockState::Type()
+{
+ return "pan gesture";
+}
+
+bool
+PanGestureBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (aPreventDefault) {
+ TBS_LOG("%p setting interrupted flag\n", this);
+ mInterrupted = true;
+ }
+ bool stateChanged = CancelableBlockState::SetContentResponse(aPreventDefault);
+ if (mWaitingForContentResponse) {
+ mWaitingForContentResponse = false;
+ stateChanged = true;
+ }
+ return stateChanged;
+}
+
+bool
+PanGestureBlockState::HasReceivedAllContentNotifications() const
+{
+ return CancelableBlockState::HasReceivedAllContentNotifications()
+ && !mWaitingForContentResponse;
+}
+
+bool
+PanGestureBlockState::IsReadyForHandling() const
+{
+ if (!CancelableBlockState::IsReadyForHandling()) {
+ return false;
+ }
+ return !mWaitingForContentResponse ||
+ IsContentResponseTimerExpired();
+}
+
+bool
+PanGestureBlockState::AllowScrollHandoff() const
+{
+ return false;
+}
+
+void
+PanGestureBlockState::SetNeedsToWaitForContentResponse(bool aWaitForContentResponse)
+{
+ mWaitingForContentResponse = aWaitForContentResponse;
+}
+
+TouchBlockState::TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed, TouchCounter& aCounter)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mAllowedTouchBehaviorSet(false)
+ , mDuringFastFling(false)
+ , mSingleTapOccurred(false)
+ , mInSlop(false)
+ , mTouchCounter(aCounter)
+{
+ TBS_LOG("Creating %p\n", this);
+ if (!gfxPrefs::TouchActionEnabled()) {
+ mAllowedTouchBehaviorSet = true;
+ }
+}
+
+bool
+TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
+{
+ if (mAllowedTouchBehaviorSet) {
+ return false;
+ }
+ TBS_LOG("%p got allowed touch behaviours for %" PRIuSIZE " points\n", this, aBehaviors.Length());
+ mAllowedTouchBehaviors.AppendElements(aBehaviors);
+ mAllowedTouchBehaviorSet = true;
+ return true;
+}
+
+bool
+TouchBlockState::GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const
+{
+ if (!mAllowedTouchBehaviorSet) {
+ return false;
+ }
+ aOutBehaviors.AppendElements(mAllowedTouchBehaviors);
+ return true;
+}
+
+void
+TouchBlockState::CopyPropertiesFrom(const TouchBlockState& aOther)
+{
+ TBS_LOG("%p copying properties from %p\n", this, &aOther);
+ if (gfxPrefs::TouchActionEnabled()) {
+ MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet || aOther.IsContentResponseTimerExpired());
+ SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
+ }
+ mTransformToApzc = aOther.mTransformToApzc;
+}
+
+bool
+TouchBlockState::HasReceivedAllContentNotifications() const
+{
+ return CancelableBlockState::HasReceivedAllContentNotifications()
+ // See comment in TouchBlockState::IsReadyforHandling()
+ && (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
+}
+
+bool
+TouchBlockState::IsReadyForHandling() const
+{
+ if (!CancelableBlockState::IsReadyForHandling()) {
+ return false;
+ }
+
+ if (!gfxPrefs::TouchActionEnabled()) {
+ // If TouchActionEnabled() was false when this block was created, then
+ // mAllowedTouchBehaviorSet is guaranteed to the true. However, the pref
+ // may have been flipped to false after the block was created. In that case,
+ // we should eventually get the touch-behaviour notification, or expire the
+ // content response timeout, but we don't really need to wait for those,
+ // since we don't care about the touch-behaviour values any more.
+ return true;
+ }
+
+ return mAllowedTouchBehaviorSet || IsContentResponseTimerExpired();
+}
+
+void
+TouchBlockState::SetDuringFastFling()
+{
+ TBS_LOG("%p setting fast-motion flag\n", this);
+ mDuringFastFling = true;
+}
+
+bool
+TouchBlockState::IsDuringFastFling() const
+{
+ return mDuringFastFling;
+}
+
+void
+TouchBlockState::SetSingleTapOccurred()
+{
+ TBS_LOG("%p setting single-tap-occurred flag\n", this);
+ mSingleTapOccurred = true;
+}
+
+bool
+TouchBlockState::SingleTapOccurred() const
+{
+ return mSingleTapOccurred;
+}
+
+bool
+TouchBlockState::MustStayActive()
+{
+ return true;
+}
+
+const char*
+TouchBlockState::Type()
+{
+ return "touch";
+}
+
+void
+TouchBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
+ mTouchCounter.Update(aEvent.AsMultiTouchInput());
+ CancelableBlockState::DispatchEvent(aEvent);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPinchZoom() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ // Pointer events specification requires that all touch points allow zoom.
+ for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+ if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsDoubleTapZoom() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+ if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningX() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningY() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningXY() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
+ && (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+bool
+TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
+ bool aApzcCanConsumeEvents)
+{
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
+ // this is by definition the first event in this block. If it's the first
+ // touch, then we enter a slop state.
+ mInSlop = (aInput.mTouches.Length() == 1);
+ if (mInSlop) {
+ mSlopOrigin = aInput.mTouches[0].mScreenPoint;
+ TBS_LOG("%p entering slop with origin %s\n", this, Stringify(mSlopOrigin).c_str());
+ }
+ return false;
+ }
+ if (mInSlop) {
+ ScreenCoord threshold = aApzcCanConsumeEvents
+ ? AsyncPanZoomController::GetTouchStartTolerance()
+ : ScreenCoord(gfxPrefs::APZTouchMoveTolerance() * APZCTreeManager::GetDPI());
+ bool stayInSlop = (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) &&
+ (aInput.mTouches.Length() == 1) &&
+ ((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() < threshold);
+ if (!stayInSlop) {
+ // we're out of the slop zone, and will stay out for the remainder of
+ // this block
+ TBS_LOG("%p exiting slop\n", this);
+ mInSlop = false;
+ }
+ }
+ return mInSlop;
+}
+
+uint32_t
+TouchBlockState::GetActiveTouchCount() const
+{
+ return mTouchCounter.GetActiveTouchCount();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/InputBlockState.h b/system/graphics/layers/apz/src/InputBlockState.h
new file mode 100644
index 000000000..b52c4946b
--- /dev/null
+++ b/system/graphics/layers/apz/src/InputBlockState.h
@@ -0,0 +1,482 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputBlockState_h
+#define mozilla_layers_InputBlockState_h
+
+#include "InputData.h" // for MultiTouchInput
+#include "mozilla/RefCounted.h" // for RefCounted
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/APZUtils.h" // for TouchBehaviorFlags
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsTArray.h" // for nsTArray
+#include "TouchCounter.h"
+#include "OverscrollHandoffState.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class OverscrollHandoffChain;
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+
+/**
+ * A base class that stores state common to various input blocks.
+ * Note that the InputBlockState constructor acquires the tree lock, so callers
+ * from inside AsyncPanZoomController should ensure that the APZC lock is not
+ * held.
+ */
+class InputBlockState : public RefCounted<InputBlockState>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(InputBlockState)
+
+ static const uint64_t NO_BLOCK_ID = 0;
+
+ enum class TargetConfirmationState {
+ eUnconfirmed,
+ eTimedOut,
+ eTimedOutAndMainThreadResponded,
+ eConfirmed
+ };
+
+ explicit InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed);
+ virtual ~InputBlockState()
+ {}
+
+ virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput);
+ const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
+ const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
+ uint64_t GetBlockId() const;
+
+ bool IsTargetConfirmed() const;
+ bool HasReceivedRealConfirmedTarget() const;
+
+ void SetScrolledApzc(AsyncPanZoomController* aApzc);
+ AsyncPanZoomController* GetScrolledApzc() const;
+ bool IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const;
+
+protected:
+ virtual void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc);
+
+private:
+ // Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in
+ // |mOverscrollHandoffChain|.
+ bool IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const;
+
+private:
+ RefPtr<AsyncPanZoomController> mTargetApzc;
+ TargetConfirmationState mTargetConfirmed;
+ const uint64_t mBlockId;
+
+ // The APZC that was actually scrolled by events in this input block.
+ // This is used in configurations where a single input block is only
+ // allowed to scroll a single APZC (configurations where gfxPrefs::
+ // APZAllowImmediateHandoff() is false).
+ // Set the first time an input event in this block scrolls an APZC.
+ RefPtr<AsyncPanZoomController> mScrolledApzc;
+protected:
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+
+ // Used to transform events from global screen space to |mTargetApzc|'s
+ // screen space. It's cached at the beginning of the input block so that
+ // all events in the block are in the same coordinate space.
+ ScreenToParentLayerMatrix4x4 mTransformToApzc;
+};
+
+/**
+ * This class represents a set of events that can be cancelled by web content
+ * via event listeners.
+ *
+ * Each cancelable input block can be cancelled by web content, and
+ * this information is stored in the mPreventDefault flag. Because web
+ * content runs on the Gecko main thread, we cannot always wait for web content's
+ * response. Instead, there is a timeout that sets this flag in the case
+ * where web content doesn't respond in time. The mContentResponded
+ * and mContentResponseTimerExpired flags indicate which of these scenarios
+ * occurred.
+ */
+class CancelableBlockState : public InputBlockState
+{
+public:
+ CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed);
+
+ virtual TouchBlockState *AsTouchBlock() {
+ return nullptr;
+ }
+ virtual WheelBlockState *AsWheelBlock() {
+ return nullptr;
+ }
+ virtual DragBlockState *AsDragBlock() {
+ return nullptr;
+ }
+ virtual PanGestureBlockState *AsPanGestureBlock() {
+ return nullptr;
+ }
+
+ /**
+ * Record whether or not content cancelled this block of events.
+ * @param aPreventDefault true iff the block is cancelled.
+ * @return false if this block has already received a response from
+ * web content, true if not.
+ */
+ virtual bool SetContentResponse(bool aPreventDefault);
+
+ /**
+ * This should be called when this block is starting to wait for the
+ * necessary content response notifications. It is used to gather data
+ * on how long the content response notifications take.
+ */
+ void StartContentResponseTimer();
+
+ /**
+ * This should be called when a content response notification has been
+ * delivered to this block.
+ */
+ void RecordContentResponseTime();
+
+ /**
+ * Record that content didn't respond in time.
+ * @return false if this block already timed out, true if not.
+ */
+ bool TimeoutContentResponse();
+
+ /**
+ * Checks if the content response timer has already expired.
+ */
+ bool IsContentResponseTimerExpired() const;
+
+ /**
+ * @return true iff web content cancelled this block of events.
+ */
+ bool IsDefaultPrevented() const;
+
+ /**
+ * Dispatch the event to the target APZC. Mostly this is a hook for
+ * subclasses to do any per-event processing they need to.
+ */
+ virtual void DispatchEvent(const InputData& aEvent) const;
+
+ /**
+ * @return true iff this block has received all the information it could
+ * have gotten from the content thread.
+ */
+ virtual bool HasReceivedAllContentNotifications() const;
+
+ /**
+ * @return true iff this block has received all the information needed
+ * to properly dispatch the events in the block.
+ */
+ virtual bool IsReadyForHandling() const;
+
+ /**
+ * Return true if this input block must stay active if it would otherwise
+ * be removed as the last item in the pending queue.
+ */
+ virtual bool MustStayActive() = 0;
+
+ /**
+ * Return a descriptive name for the block kind.
+ */
+ virtual const char* Type() = 0;
+
+private:
+ TimeStamp mContentResponseTimer;
+ bool mPreventDefault;
+ bool mContentResponded;
+ bool mContentResponseTimerExpired;
+};
+
+/**
+ * A single block of wheel events.
+ */
+class WheelBlockState : public CancelableBlockState
+{
+public:
+ WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent);
+
+ bool SetContentResponse(bool aPreventDefault) override;
+ bool MustStayActive() override;
+ const char* Type() override;
+ bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput) override;
+
+ WheelBlockState *AsWheelBlock() override {
+ return this;
+ }
+
+ /**
+ * Determine whether this wheel block is accepting new events.
+ */
+ bool ShouldAcceptNewEvent() const;
+
+ /**
+ * Call to check whether a wheel event will cause the current transaction to
+ * timeout.
+ */
+ bool MaybeTimeout(const ScrollWheelInput& aEvent);
+
+ /**
+ * Called from APZCTM when a mouse move or drag+drop event occurs, before
+ * the event has been processed.
+ */
+ void OnMouseMove(const ScreenIntPoint& aPoint);
+
+ /**
+ * Returns whether or not the block is participating in a wheel transaction.
+ * This means that the block is the most recent input block to be created,
+ * and no events have occurred that would require scrolling a different
+ * frame.
+ *
+ * @return True if in a transaction, false otherwise.
+ */
+ bool InTransaction() const;
+
+ /**
+ * Mark the block as no longer participating in a wheel transaction. This
+ * will force future wheel events to begin a new input block.
+ */
+ void EndTransaction();
+
+ /**
+ * @return Whether or not overscrolling is prevented for this wheel block.
+ */
+ bool AllowScrollHandoff() const;
+
+ /**
+ * Called to check and possibly end the transaction due to a timeout.
+ *
+ * @return True if the transaction ended, false otherwise.
+ */
+ bool MaybeTimeout(const TimeStamp& aTimeStamp);
+
+ /**
+ * Update the wheel transaction state for a new event.
+ */
+ void Update(ScrollWheelInput& aEvent);
+
+protected:
+ void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
+
+private:
+ TimeStamp mLastEventTime;
+ TimeStamp mLastMouseMove;
+ uint32_t mScrollSeriesCounter;
+ bool mTransactionEnded;
+};
+
+/**
+ * A block of mouse events that are part of a drag
+ */
+class DragBlockState : public CancelableBlockState
+{
+public:
+ DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent);
+
+ bool MustStayActive() override;
+ const char* Type() override;
+
+ bool HasReceivedMouseUp();
+ void MarkMouseUpReceived();
+
+ DragBlockState *AsDragBlock() override {
+ return this;
+ }
+
+ void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
+
+ void DispatchEvent(const InputData& aEvent) const override;
+private:
+ AsyncDragMetrics mDragMetrics;
+ bool mReceivedMouseUp;
+};
+
+/**
+ * A single block of pan gesture events.
+ */
+class PanGestureBlockState : public CancelableBlockState
+{
+public:
+ PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent);
+
+ bool SetContentResponse(bool aPreventDefault) override;
+ bool HasReceivedAllContentNotifications() const override;
+ bool IsReadyForHandling() const override;
+ bool MustStayActive() override;
+ const char* Type() override;
+ bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput) override;
+
+ PanGestureBlockState *AsPanGestureBlock() override {
+ return this;
+ }
+
+ /**
+ * @return Whether or not overscrolling is prevented for this block.
+ */
+ bool AllowScrollHandoff() const;
+
+ bool WasInterrupted() const { return mInterrupted; }
+
+ void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
+
+private:
+ bool mInterrupted;
+ bool mWaitingForContentResponse;
+};
+
+/**
+ * This class represents a single touch block. A touch block is
+ * a set of touch events that can be cancelled by web content via
+ * touch event listeners.
+ *
+ * Every touch-start event creates a new touch block. In this case, the
+ * touch block consists of the touch-start, followed by all touch events
+ * up to but not including the next touch-start (except in the case where
+ * a long-tap happens, see below). Note that in particular we cannot know
+ * when a touch block ends until the next one is started. Most touch
+ * blocks are created by receipt of a touch-start event.
+ *
+ * Every long-tap event also creates a new touch block, since it can also
+ * be consumed by web content. In this case, when the long-tap event is
+ * dispatched to web content, a new touch block is started to hold the remaining
+ * touch events, up to but not including the next touch start (or long-tap).
+ *
+ * Additionally, if touch-action is enabled, each touch block should
+ * have a set of allowed touch behavior flags; one for each touch point.
+ * This also requires running code on the Gecko main thread, and so may
+ * be populated with some latency. The mAllowedTouchBehaviorSet and
+ * mAllowedTouchBehaviors variables track this information.
+ */
+class TouchBlockState : public CancelableBlockState
+{
+public:
+ explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed, TouchCounter& aTouchCounter);
+
+ TouchBlockState *AsTouchBlock() override {
+ return this;
+ }
+
+ /**
+ * Set the allowed touch behavior flags for this block.
+ * @return false if this block already has these flags set, true if not.
+ */
+ bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+ /**
+ * If the allowed touch behaviors have been set, populate them into
+ * |aOutBehaviors| and return true. Else, return false.
+ */
+ bool GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const;
+
+ /**
+ * Copy various properties from another block.
+ */
+ void CopyPropertiesFrom(const TouchBlockState& aOther);
+
+ /*
+ * @return true iff this block has received all the information it could
+ * have gotten from the content thread.
+ */
+ bool HasReceivedAllContentNotifications() const override;
+
+ /**
+ * @return true iff this block has received all the information needed
+ * to properly dispatch the events in the block.
+ */
+ bool IsReadyForHandling() const override;
+
+ /**
+ * Sets a flag that indicates this input block occurred while the APZ was
+ * in a state of fast flinging. This affects gestures that may be produced
+ * from input events in this block.
+ */
+ void SetDuringFastFling();
+ /**
+ * @return true iff SetDuringFastFling was called on this block.
+ */
+ bool IsDuringFastFling() const;
+ /**
+ * Set the single-tap-occurred flag that indicates that this touch block
+ * triggered a single tap event.
+ */
+ void SetSingleTapOccurred();
+ /**
+ * @return true iff the single-tap-occurred flag is set on this block.
+ */
+ bool SingleTapOccurred() const;
+
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * this touch block do not allow pinch-zooming.
+ */
+ bool TouchActionAllowsPinchZoom() const;
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * this touch block do not allow double-tap zooming.
+ */
+ bool TouchActionAllowsDoubleTapZoom() const;
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * the first touch point do not allow panning in the specified direction(s).
+ */
+ bool TouchActionAllowsPanningX() const;
+ bool TouchActionAllowsPanningY() const;
+ bool TouchActionAllowsPanningXY() const;
+
+ /**
+ * Notifies the input block of an incoming touch event so that the block can
+ * update its internal slop state. "Slop" refers to the area around the
+ * initial touchstart where we drop touchmove events so that content doesn't
+ * see them. The |aApzcCanConsumeEvents| parameter is factored into how large
+ * the slop area is - if this is true the slop area is larger.
+ * @return true iff the provided event is a touchmove in the slop area and
+ * so should not be sent to content.
+ */
+ bool UpdateSlopState(const MultiTouchInput& aInput,
+ bool aApzcCanConsumeEvents);
+
+ /**
+ * Returns the number of touch points currently active.
+ */
+ uint32_t GetActiveTouchCount() const;
+
+ void DispatchEvent(const InputData& aEvent) const override;
+ bool MustStayActive() override;
+ const char* Type() override;
+
+private:
+ nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
+ bool mAllowedTouchBehaviorSet;
+ bool mDuringFastFling;
+ bool mSingleTapOccurred;
+ bool mInSlop;
+ ScreenIntPoint mSlopOrigin;
+ // A reference to the InputQueue's touch counter
+ TouchCounter& mTouchCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_InputBlockState_h
diff --git a/system/graphics/layers/apz/src/InputQueue.cpp b/system/graphics/layers/apz/src/InputQueue.cpp
new file mode 100644
index 000000000..c0b31b2a7
--- /dev/null
+++ b/system/graphics/layers/apz/src/InputQueue.cpp
@@ -0,0 +1,730 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputQueue.h"
+
+#include "AsyncPanZoomController.h"
+#include "gfxPrefs.h"
+#include "InputBlockState.h"
+#include "LayersLogging.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
+
+#define INPQ_LOG(...)
+// #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+InputQueue::InputQueue()
+{
+}
+
+InputQueue::~InputQueue() {
+ mQueuedInputs.Clear();
+}
+
+nsEventStatus
+InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const InputData& aEvent,
+ uint64_t* aOutInputBlockId) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ const MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ return ReceiveTouchInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case SCROLLWHEEL_INPUT: {
+ const ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ return ReceiveScrollWheelInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case PANGESTURE_INPUT: {
+ const PanGestureInput& event = aEvent.AsPanGestureInput();
+ return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case MOUSE_INPUT: {
+ const MouseInput& event = aEvent.AsMouseInput();
+ return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ default:
+ // The return value for non-touch input is only used by tests, so just pass
+ // through the return value for now. This can be changed later if needed.
+ // TODO (bug 1098430): we will eventually need to have smarter handling for
+ // non-touch events as well.
+ return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
+ }
+}
+
+nsEventStatus
+InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MultiTouchInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ TouchBlockState* block = nullptr;
+ if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) {
+ nsTArray<TouchBehaviorFlags> currentBehaviors;
+ bool haveBehaviors = false;
+ if (!gfxPrefs::TouchActionEnabled()) {
+ haveBehaviors = true;
+ } else if (mActiveTouchBlock) {
+ haveBehaviors = mActiveTouchBlock->GetAllowedTouchBehaviors(currentBehaviors);
+ // If the behaviours aren't set, but the main-thread response timer on
+ // the block is expired we still treat it as though it has behaviors,
+ // because in that case we still want to interrupt the fast-fling and
+ // use the default behaviours.
+ haveBehaviors |= mActiveTouchBlock->IsContentResponseTimerExpired();
+ }
+
+ block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
+ INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ // XXX using the chain from |block| here may be wrong in cases where the
+ // target isn't confirmed and the real target turns out to be something
+ // else. For now assume this is rare enough that it's not an issue.
+ if (mQueuedInputs.IsEmpty() &&
+ aEvent.mTouches.Length() == 1 &&
+ block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
+ haveBehaviors) {
+ // If we're already in a fast fling, and a single finger goes down, then
+ // we want special handling for the touch event, because it shouldn't get
+ // delivered to content. Note that we don't set this flag when going
+ // from a fast fling to a pinch state (i.e. second finger goes down while
+ // the first finger is moving).
+ block->SetDuringFastFling();
+ block->SetConfirmedTargetApzc(aTarget,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ nullptr /* the block was just created so it has no events */);
+ if (gfxPrefs::TouchActionEnabled()) {
+ block->SetAllowedTouchBehaviors(currentBehaviors);
+ }
+ INPQ_LOG("block %p tagged as fast-motion\n", block);
+ }
+
+ CancelAnimationsForNewBlock(block);
+
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ block = mActiveTouchBlock.get();
+ if (!block) {
+ NS_WARNING("Received a non-start touch event while no touch blocks active!");
+ return nsEventStatus_eIgnore;
+ }
+
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block.
+ RefPtr<AsyncPanZoomController> target = block->GetTargetApzc();
+
+ nsEventStatus result = nsEventStatus_eIgnore;
+
+ // XXX calling ArePointerEventsConsumable on |target| may be wrong here if
+ // the target isn't confirmed and the real target turns out to be something
+ // else. For now assume this is rare enough that it's not an issue.
+ if (block->IsDuringFastFling()) {
+ INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ } else if (target && target->ArePointerEventsConsumable(block, aEvent.mTouches.Length())) {
+ if (block->UpdateSlopState(aEvent, true)) {
+ INPQ_LOG("dropping event due to block %p being in slop\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ } else {
+ result = nsEventStatus_eConsumeDoDefault;
+ }
+ } else if (block->UpdateSlopState(aEvent, false)) {
+ INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ }
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+ ProcessQueue();
+ return result;
+}
+
+nsEventStatus
+InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ // On a new mouse down we can have a new target so we must force a new block
+ // with a new target.
+ bool newBlock = DragTracker::StartsDrag(aEvent);
+
+ DragBlockState* block = newBlock ? nullptr : mActiveDragBlock.get();
+ if (block && block->HasReceivedMouseUp()) {
+ block = nullptr;
+ }
+
+ if (!block && mDragTracker.InDrag()) {
+ // If there's no current drag block, but we're getting a move with a button
+ // down, we need to start a new drag block because we're obviously already
+ // in the middle of a drag (it probably got interrupted by something else).
+ INPQ_LOG("got a drag event outside a drag block, need to create a block to hold it\n");
+ newBlock = true;
+ }
+
+ mDragTracker.Update(aEvent);
+
+ if (!newBlock && !block) {
+ // This input event is not in a drag block, so we're not doing anything
+ // with it, return eIgnore.
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!block) {
+ MOZ_ASSERT(newBlock);
+ block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
+
+ INPQ_LOG("started new drag block %p id %" PRIu64 " for %sconfirmed target %p\n",
+ block, block->GetBlockId(), aTargetConfirmed ? "" : "un", aTarget.get());
+
+ mActiveDragBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+ ProcessQueue();
+
+ if (DragTracker::EndsDrag(aEvent)) {
+ block->MarkMouseUpReceived();
+ }
+
+ // The event is part of a drag block and could potentially cause
+ // scrolling, so return DoDefault.
+ return nsEventStatus_eConsumeDoDefault;
+}
+
+nsEventStatus
+InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ WheelBlockState* block = mActiveWheelBlock.get();
+ // If the block is not accepting new events we'll create a new input block
+ // (and therefore a new wheel transaction).
+ if (block &&
+ (!block->ShouldAcceptNewEvent() ||
+ block->MaybeTimeout(aEvent)))
+ {
+ block = nullptr;
+ }
+
+ MOZ_ASSERT(!block || block->InTransaction());
+
+ if (!block) {
+ block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
+ INPQ_LOG("started new scroll wheel block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ mActiveWheelBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block, which is what
+ // ProcessQueue() does.
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+
+ // The WheelBlockState needs to affix a counter to the event before we process
+ // it. Note that the counter is affixed to the copy in the queue rather than
+ // |aEvent|.
+ block->Update(mQueuedInputs.LastElement()->Input()->AsScrollWheelInput());
+
+ ProcessQueue();
+
+ return nsEventStatus_eConsumeDoDefault;
+}
+
+static bool
+CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
+ PanGestureBlockState* aBlock)
+{
+ PanGestureInput horizontalComponent = aInitialEvent;
+ horizontalComponent.mPanDisplacement.y = 0;
+ RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
+ aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent);
+ return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc();
+}
+
+nsEventStatus
+InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
+ aEvent.mType == PanGestureInput::PANGESTURE_CANCELLED) {
+ // Ignore these events for now.
+ return nsEventStatus_eConsumeDoDefault;
+ }
+
+ PanGestureBlockState* block = nullptr;
+ if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
+ block = mActivePanGestureBlock.get();
+ }
+
+ PanGestureInput event = aEvent;
+ nsEventStatus result = nsEventStatus_eConsumeDoDefault;
+
+ if (!block || block->WasInterrupted()) {
+ if (event.mType != PanGestureInput::PANGESTURE_START) {
+ // Only PANGESTURE_START events are allowed to start a new pan gesture
+ // block, but we really want to start a new block here, so we magically
+ // turn this input into a PANGESTURE_START.
+ INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n",
+ event.mType);
+ event.mType = PanGestureInput::PANGESTURE_START;
+ }
+ block = new PanGestureBlockState(aTarget, aTargetConfirmed, event);
+ INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ if (aTargetConfirmed &&
+ event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
+ !CanScrollTargetHorizontally(event, block)) {
+ // This event may trigger a swipe gesture, depending on what our caller
+ // wants to do it. We need to suspend handling of this block until we get
+ // a content response which will tell us whether to proceed or abort the
+ // block.
+ block->SetNeedsToWaitForContentResponse(true);
+
+ // Inform our caller that we haven't scrolled in response to the event
+ // and that a swipe can be started from this event if desired.
+ result = nsEventStatus_eIgnore;
+ }
+
+ mActivePanGestureBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block, which is what
+ // ProcessQueue() does.
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(event, *block));
+ ProcessQueue();
+
+ return result;
+}
+
+void
+InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
+{
+ // We want to cancel animations here as soon as possible (i.e. without waiting for
+ // content responses) because a finger has gone down and we don't want to keep moving
+ // the content under the finger. However, to prevent "future" touchstart events from
+ // interfering with "past" animations (i.e. from a previous touch block that is still
+ // being processed) we only do this animation-cancellation if there are no older
+ // touch blocks still in the queue.
+ if (mQueuedInputs.IsEmpty()) {
+ aBlock->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll | ScrollSnap);
+ }
+}
+
+void
+InputQueue::MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock)
+{
+ bool waitForMainThread = false;
+ if (aBlock->IsTargetConfirmed()) {
+ // Content won't prevent-default this, so we can just set the flag directly.
+ INPQ_LOG("not waiting for content response on block %p\n", aBlock);
+ aBlock->SetContentResponse(false);
+ } else {
+ waitForMainThread = true;
+ }
+ if (aBlock->AsTouchBlock() && gfxPrefs::TouchActionEnabled()) {
+ // waitForMainThread is set to true unconditionally here, but if the APZCTM
+ // has the touch-action behaviours for this block, it will set it
+ // immediately after we unwind out of this ReceiveInputEvent call. So even
+ // though we are scheduling the main-thread timeout, we might end up not
+ // waiting.
+ INPQ_LOG("waiting for main thread touch-action info on block %p\n", aBlock);
+ waitForMainThread = true;
+ }
+ if (waitForMainThread) {
+ // We either don't know for sure if aTarget is the right APZC, or we may
+ // need to wait to give content the opportunity to prevent-default the
+ // touch events. Either way we schedule a timeout so the main thread stuff
+ // can run.
+ ScheduleMainThreadTimeout(aTarget, aBlock);
+ }
+}
+
+uint64_t
+InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
+{
+ TouchBlockState* block = StartNewTouchBlock(aTarget,
+ /* aTargetConfirmed = */ true,
+ /* aCopyPropertiesFromCurrent = */ true);
+ INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
+ block, block->GetBlockId(), aTarget);
+ ScheduleMainThreadTimeout(aTarget, block);
+ return block->GetBlockId();
+}
+
+TouchBlockState*
+InputQueue::StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ bool aCopyPropertiesFromCurrent)
+{
+ TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
+ mTouchCounter);
+ if (aCopyPropertiesFromCurrent) {
+ // We should never enter here without a current touch block, because this
+ // codepath is invoked from the OnLongPress handler in
+ // AsyncPanZoomController, which should bail out if there is no current
+ // touch block.
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ newBlock->CopyPropertiesFrom(*GetCurrentTouchBlock());
+ }
+
+ mActiveTouchBlock = newBlock;
+ return newBlock;
+}
+
+CancelableBlockState*
+InputQueue::GetCurrentBlock() const
+{
+ APZThreadUtils::AssertOnControllerThread();
+ return mQueuedInputs.IsEmpty() ? nullptr : mQueuedInputs[0]->Block();
+}
+
+TouchBlockState*
+InputQueue::GetCurrentTouchBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsTouchBlock() : mActiveTouchBlock.get();
+}
+
+WheelBlockState*
+InputQueue::GetCurrentWheelBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsWheelBlock() : mActiveWheelBlock.get();
+}
+
+DragBlockState*
+InputQueue::GetCurrentDragBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsDragBlock() : mActiveDragBlock.get();
+}
+
+PanGestureBlockState*
+InputQueue::GetCurrentPanGestureBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
+}
+
+WheelBlockState*
+InputQueue::GetActiveWheelTransaction() const
+{
+ WheelBlockState* block = mActiveWheelBlock.get();
+ if (!block || !block->InTransaction()) {
+ return nullptr;
+ }
+ return block;
+}
+
+bool
+InputQueue::HasReadyTouchBlock() const
+{
+ return !mQueuedInputs.IsEmpty() &&
+ mQueuedInputs[0]->Block()->AsTouchBlock() &&
+ mQueuedInputs[0]->Block()->IsReadyForHandling();
+}
+
+bool
+InputQueue::AllowScrollHandoff() const
+{
+ if (GetCurrentWheelBlock()) {
+ return GetCurrentWheelBlock()->AllowScrollHandoff();
+ }
+ if (GetCurrentPanGestureBlock()) {
+ return GetCurrentPanGestureBlock()->AllowScrollHandoff();
+ }
+ return true;
+}
+
+bool
+InputQueue::IsDragOnScrollbar(bool aHitScrollbar)
+{
+ if (!mDragTracker.InDrag()) {
+ return false;
+ }
+ // Now that we know we are in a drag, get the info from the drag tracker.
+ // We keep it in the tracker rather than the block because the block can get
+ // interrupted by something else (like a wheel event) and then a new block
+ // will get created without the info we want. The tracker will persist though.
+ return mDragTracker.IsOnScrollbar(aHitScrollbar);
+}
+
+void
+InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock) {
+ INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
+ aBlock->StartContentResponseTimer();
+ aTarget->PostDelayedTask(NewRunnableMethod<uint64_t>(this,
+ &InputQueue::MainThreadTimeout,
+ aBlock->GetBlockId()),
+ gfxPrefs::APZContentResponseTimeout());
+}
+
+CancelableBlockState*
+InputQueue::FindBlockForId(uint64_t aInputBlockId,
+ InputData** aOutFirstInput)
+{
+ for (const auto& queuedInput : mQueuedInputs) {
+ if (queuedInput->Block()->GetBlockId() == aInputBlockId) {
+ if (aOutFirstInput) {
+ *aOutFirstInput = queuedInput->Input();
+ }
+ return queuedInput->Block();
+ }
+ }
+
+ CancelableBlockState* block = nullptr;
+ if (mActiveTouchBlock && mActiveTouchBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveTouchBlock.get();
+ } else if (mActiveWheelBlock && mActiveWheelBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveWheelBlock.get();
+ } else if (mActiveDragBlock && mActiveDragBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveDragBlock.get();
+ } else if (mActivePanGestureBlock && mActivePanGestureBlock->GetBlockId() == aInputBlockId) {
+ block = mActivePanGestureBlock.get();
+ }
+ // Since we didn't encounter this block while iterating through mQueuedInputs,
+ // it must have no events associated with it at the moment.
+ if (aOutFirstInput) {
+ *aOutFirstInput = nullptr;
+ }
+ return block;
+}
+
+void
+InputQueue::MainThreadTimeout(uint64_t aInputBlockId) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block) {
+ // time out the touch-listener response and also confirm the existing
+ // target apzc in the case where the main thread doesn't get back to us
+ // fast enough.
+ success = block->TimeoutContentResponse();
+ success |= block->SetConfirmedTargetApzc(
+ block->GetTargetApzc(),
+ InputBlockState::TargetConfirmationState::eTimedOut,
+ firstInput);
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+ if (block) {
+ success = block->SetContentResponse(aPreventDefault);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
+ aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block) {
+ success = block->SetConfirmedTargetApzc(aTargetApzc,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ firstInput);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
+ aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block && block->AsDragBlock()) {
+ block->AsDragBlock()->SetDragMetrics(aDragMetrics);
+ success = block->SetConfirmedTargetApzc(aTargetApzc,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ firstInput);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+ if (block && block->AsTouchBlock()) {
+ success = block->AsTouchBlock()->SetAllowedTouchBehaviors(aBehaviors);
+ block->RecordContentResponseTime();
+ } else if (block) {
+ NS_WARNING("input block is not a touch block");
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ProcessQueue() {
+ APZThreadUtils::AssertOnControllerThread();
+
+ while (!mQueuedInputs.IsEmpty()) {
+ CancelableBlockState* curBlock = mQueuedInputs[0]->Block();
+ if (!curBlock->IsReadyForHandling()) {
+ break;
+ }
+
+ INPQ_LOG("processing input from block %p; preventDefault %d target %p\n",
+ curBlock, curBlock->IsDefaultPrevented(),
+ curBlock->GetTargetApzc().get());
+ RefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
+ // target may be null here if the initial target was unconfirmed and then
+ // we later got a confirmed null target. in that case drop the events.
+ if (target) {
+ if (curBlock->IsDefaultPrevented()) {
+ if (curBlock->AsTouchBlock()) {
+ target->ResetTouchInputState();
+ }
+ } else {
+ UpdateActiveApzc(target);
+ curBlock->DispatchEvent(*(mQueuedInputs[0]->Input()));
+ }
+ }
+ mQueuedInputs.RemoveElementAt(0);
+ }
+
+ if (CanDiscardBlock(mActiveTouchBlock)) {
+ mActiveTouchBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActiveWheelBlock)) {
+ mActiveWheelBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActiveDragBlock)) {
+ mActiveDragBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActivePanGestureBlock)) {
+ mActivePanGestureBlock = nullptr;
+ }
+}
+
+bool
+InputQueue::CanDiscardBlock(CancelableBlockState* aBlock)
+{
+ if (!aBlock ||
+ !aBlock->IsReadyForHandling() ||
+ aBlock->MustStayActive()) {
+ return false;
+ }
+ InputData* firstInput = nullptr;
+ FindBlockForId(aBlock->GetBlockId(), &firstInput);
+ if (firstInput) {
+ // The block has at least one input event still in the queue, so it's
+ // not depleted
+ return false;
+ }
+ return true;
+}
+
+void
+InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
+ if (mLastActiveApzc && mLastActiveApzc != aNewActive
+ && mTouchCounter.GetActiveTouchCount() > 0) {
+ mLastActiveApzc->ResetTouchInputState();
+ }
+ mLastActiveApzc = aNewActive;
+}
+
+void
+InputQueue::Clear()
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ mQueuedInputs.Clear();
+ mActiveTouchBlock = nullptr;
+ mActiveWheelBlock = nullptr;
+ mActiveDragBlock = nullptr;
+ mActivePanGestureBlock = nullptr;
+ mLastActiveApzc = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/InputQueue.h b/system/graphics/layers/apz/src/InputQueue.h
new file mode 100644
index 000000000..eaf9b20bc
--- /dev/null
+++ b/system/graphics/layers/apz/src/InputQueue.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputQueue_h
+#define mozilla_layers_InputQueue_h
+
+#include "APZUtils.h"
+#include "DragTracker.h"
+#include "InputData.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+#include "TouchCounter.h"
+
+namespace mozilla {
+
+class InputData;
+class MultiTouchInput;
+class ScrollWheelInput;
+
+namespace layers {
+
+class AsyncPanZoomController;
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+class AsyncDragMetrics;
+class QueuedInput;
+
+/**
+ * This class stores incoming input events, associated with "input blocks", until
+ * they are ready for handling.
+ */
+class InputQueue {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
+
+public:
+ InputQueue();
+
+ /**
+ * Notifies the InputQueue of a new incoming input event. The APZC that the
+ * input event was targeted to should be provided in the |aTarget| parameter.
+ * See the documentation on APZCTreeManager::ReceiveInputEvent for info on
+ * return values from this function, including |aOutInputBlockId|.
+ */
+ nsEventStatus ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const InputData& aEvent,
+ uint64_t* aOutInputBlockId);
+ /**
+ * This function should be invoked to notify the InputQueue when web content
+ * decides whether or not it wants to cancel a block of events. The block
+ * id to which this applies should be provided in |aInputBlockId|.
+ */
+ void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault);
+ /**
+ * This function should be invoked to notify the InputQueue once the target
+ * APZC to handle an input block has been confirmed. In practice this should
+ * generally be decidable upon receipt of the input event, but in some cases
+ * we may need to query the layout engine to know for sure. The input block
+ * this applies to should be specified via the |aInputBlockId| parameter.
+ */
+ void SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc);
+ /**
+ * This function is invoked to confirm that the drag block should be handled
+ * by the APZ.
+ */
+ void ConfirmDragBlock(uint64_t aInputBlockId,
+ const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ const AsyncDragMetrics& aDragMetrics);
+ /**
+ * This function should be invoked to notify the InputQueue of the touch-
+ * action properties for the different touch points in an input block. The
+ * input block this applies to should be specified by the |aInputBlockId|
+ * parameter. If touch-action is not enabled on the platform, this function
+ * does nothing and need not be called.
+ */
+ void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
+ /**
+ * Adds a new touch block at the end of the input queue that has the same
+ * allowed touch behaviour flags as the the touch block currently being
+ * processed. This should only be called when processing of a touch block
+ * triggers the creation of a new touch block. Returns the input block id
+ * of the the newly-created block.
+ */
+ uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
+ /**
+ * Returns the pending input block at the head of the queue, if there is one.
+ * This may return null if there all input events have been processed.
+ */
+ CancelableBlockState* GetCurrentBlock() const;
+ /*
+ * Returns the current pending input block as a specific kind of block. If
+ * GetCurrentBlock() returns null, these functions additionally check the
+ * mActiveXXXBlock field of the corresponding input type to see if there is
+ * a depleted but still active input block, and returns that if found. These
+ * functions may return null if no block is found.
+ */
+ TouchBlockState* GetCurrentTouchBlock() const;
+ WheelBlockState* GetCurrentWheelBlock() const;
+ DragBlockState* GetCurrentDragBlock() const;
+ PanGestureBlockState* GetCurrentPanGestureBlock() const;
+ /**
+ * Returns true iff the pending block at the head of the queue is a touch
+ * block and is ready for handling.
+ */
+ bool HasReadyTouchBlock() const;
+ /**
+ * If there is an active wheel transaction, returns the WheelBlockState
+ * representing the transaction. Otherwise, returns null. "Active" in this
+ * function name is the same kind of "active" as in mActiveWheelBlock - that
+ * is, new incoming wheel events will go into the "active" block.
+ */
+ WheelBlockState* GetActiveWheelTransaction() const;
+ /**
+ * Remove all input blocks from the input queue.
+ */
+ void Clear();
+ /**
+ * Whether the current pending block allows scroll handoff.
+ */
+ bool AllowScrollHandoff() const;
+ /**
+ * If there is currently a drag in progress, return whether or not it was
+ * targeted at a scrollbar. If the drag was newly-created and doesn't know,
+ * use the provided |aOnScrollbar| to populate that information.
+ */
+ bool IsDragOnScrollbar(bool aOnScrollbar);
+
+private:
+ ~InputQueue();
+
+ TouchBlockState* StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ bool aCopyPropertiesFromCurrent);
+
+ /**
+ * If animations are present for the current pending input block, cancel
+ * them as soon as possible.
+ */
+ void CancelAnimationsForNewBlock(CancelableBlockState* aBlock);
+
+ /**
+ * If we need to wait for a content response, schedule that now.
+ */
+ void MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock);
+
+ nsEventStatus ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MultiTouchInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent,
+ uint64_t* aOutInputBlockId);
+
+ /**
+ * Helper function that searches mQueuedInputs for the first block matching
+ * the given id, and returns it. If |aOutFirstInput| is non-null, it is
+ * populated with a pointer to the first input in mQueuedInputs that
+ * corresponds to the block, or null if no such input was found. Note that
+ * even if there are no inputs in mQueuedInputs, this function can return
+ * non-null if the block id provided matches one of the depleted-but-still-
+ * active blocks (mActiveTouchBlock, mActiveWheelBlock, etc.).
+ */
+ CancelableBlockState* FindBlockForId(uint64_t aInputBlockId,
+ InputData** aOutFirstInput);
+ void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock);
+ void MainThreadTimeout(uint64_t aInputBlockId);
+ void ProcessQueue();
+ bool CanDiscardBlock(CancelableBlockState* aBlock);
+ void UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive);
+
+private:
+ // The queue of input events that have not yet been fully processed.
+ // This member must only be accessed on the controller/UI thread.
+ nsTArray<UniquePtr<QueuedInput>> mQueuedInputs;
+
+ // These are the most recently created blocks of each input type. They are
+ // "active" in the sense that new inputs of that type are associated with
+ // them. Note that these pointers may be null if no inputs of the type have
+ // arrived, or if the inputs for the type formed a complete block that was
+ // then discarded.
+ RefPtr<TouchBlockState> mActiveTouchBlock;
+ RefPtr<WheelBlockState> mActiveWheelBlock;
+ RefPtr<DragBlockState> mActiveDragBlock;
+ RefPtr<PanGestureBlockState> mActivePanGestureBlock;
+
+ // The APZC to which the last event was delivered
+ RefPtr<AsyncPanZoomController> mLastActiveApzc;
+
+ // Track touches so we know when to clear mLastActiveApzc
+ TouchCounter mTouchCounter;
+
+ // Track mouse inputs so we know if we're in a drag or not
+ DragTracker mDragTracker;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_InputQueue_h
diff --git a/system/graphics/layers/apz/src/Overscroll.h b/system/graphics/layers/apz/src/Overscroll.h
new file mode 100644
index 000000000..44f0dab55
--- /dev/null
+++ b/system/graphics/layers/apz/src/Overscroll.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Overscroll_h
+#define mozilla_layers_Overscroll_h
+
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "FrameMetrics.h"
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+// Animation used by GenericOverscrollEffect.
+class OverscrollAnimation: public AsyncPanZoomAnimation {
+public:
+ explicit OverscrollAnimation(AsyncPanZoomController& aApzc, const ParentLayerPoint& aVelocity)
+ : mApzc(aApzc)
+ {
+ mApzc.mX.StartOverscrollAnimation(aVelocity.x);
+ mApzc.mY.StartOverscrollAnimation(aVelocity.y);
+ }
+ ~OverscrollAnimation()
+ {
+ mApzc.mX.EndOverscrollAnimation();
+ mApzc.mY.EndOverscrollAnimation();
+ }
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ // Can't inline these variables due to short-circuit evaluation.
+ bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
+ bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
+ if (!continueX && !continueY) {
+ // If we got into overscroll from a fling, that fling did not request a
+ // fling snap to avoid a resulting scrollTo from cancelling the overscroll
+ // animation too early. We do still want to request a fling snap, though,
+ // in case the end of the axis at which we're overscrolled is not a valid
+ // snap point, so we request one now. If there are no snap points, this will
+ // do nothing. If there are snap points, we'll get a scrollTo that snaps us
+ // back to the nearest valid snap point.
+ // The scroll snapping is done in a deferred task, otherwise the state
+ // change to NOTHING caused by the overscroll animation ending would
+ // clobber a possible state change to SMOOTH_SCROLL in ScrollSnap().
+ mDeferredTasks.AppendElement(NewRunnableMethod(&mApzc, &AsyncPanZoomController::ScrollSnap));
+ return false;
+ }
+ return true;
+ }
+
+ virtual bool WantsRepaints() override
+ {
+ return false;
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+// Base class for different overscroll effects;
+class OverscrollEffectBase {
+public:
+ virtual ~OverscrollEffectBase() {}
+ virtual void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) = 0;
+ virtual void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) = 0;
+};
+
+// A generic overscroll effect, implemented by AsyncPanZoomController itself.
+class GenericOverscrollEffect : public OverscrollEffectBase {
+public:
+ explicit GenericOverscrollEffect(AsyncPanZoomController& aApzc) : mApzc(aApzc) {}
+
+ void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) override {
+ if (aShouldOverscrollX) {
+ mApzc.mX.OverscrollBy(aOverscroll.x);
+ aOverscroll.x = 0;
+ }
+
+ if (aShouldOverscrollY) {
+ mApzc.mY.OverscrollBy(aOverscroll.y);
+ aOverscroll.y = 0;
+ }
+
+ if (aShouldOverscrollX || aShouldOverscrollY) {
+ mApzc.ScheduleComposite();
+ }
+ }
+
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) override {
+ mApzc.StartOverscrollAnimation(aVelocity);
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+// A widget-specific overscroll effect, implemented by the widget via
+// GeckoContentController.
+class WidgetOverscrollEffect : public OverscrollEffectBase {
+public:
+ explicit WidgetOverscrollEffect(AsyncPanZoomController& aApzc) : mApzc(aApzc) {}
+
+ void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) override {
+ RefPtr<GeckoContentController> controller = mApzc.GetGeckoContentController();
+ if (controller && (aShouldOverscrollX || aShouldOverscrollY)) {
+ controller->UpdateOverscrollOffset(aOverscroll.x, aOverscroll.y, mApzc.IsRootContent());
+ aOverscroll = ParentLayerPoint();
+ }
+ }
+
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) override {
+ RefPtr<GeckoContentController> controller = mApzc.GetGeckoContentController();
+ if (controller) {
+ controller->UpdateOverscrollVelocity(aVelocity.x, aVelocity.y, mApzc.IsRootContent());
+ }
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_Overscroll_h
diff --git a/system/graphics/layers/apz/src/OverscrollHandoffState.cpp b/system/graphics/layers/apz/src/OverscrollHandoffState.cpp
new file mode 100644
index 000000000..004fa3605
--- /dev/null
+++ b/system/graphics/layers/apz/src/OverscrollHandoffState.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OverscrollHandoffState.h"
+
+#include <algorithm> // for std::stable_sort
+#include "mozilla/Assertions.h"
+#include "AsyncPanZoomController.h"
+
+namespace mozilla {
+namespace layers {
+
+OverscrollHandoffChain::~OverscrollHandoffChain() {}
+
+void
+OverscrollHandoffChain::Add(AsyncPanZoomController* aApzc)
+{
+ mChain.push_back(aApzc);
+}
+
+struct CompareByScrollPriority
+{
+ bool operator()(const RefPtr<AsyncPanZoomController>& a,
+ const RefPtr<AsyncPanZoomController>& b) const
+ {
+ return a->HasScrollgrab() && !b->HasScrollgrab();
+ }
+};
+
+void
+OverscrollHandoffChain::SortByScrollPriority()
+{
+ // The sorting being stable ensures that the relative order between
+ // non-scrollgrabbing APZCs remains child -> parent.
+ // (The relative order between scrollgrabbing APZCs will also remain
+ // child -> parent, though that's just an artefact of the implementation
+ // and users of 'scrollgrab' should not rely on this.)
+ std::stable_sort(mChain.begin(), mChain.end(), CompareByScrollPriority());
+}
+
+const RefPtr<AsyncPanZoomController>&
+OverscrollHandoffChain::GetApzcAtIndex(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < Length());
+ return mChain[aIndex];
+}
+
+uint32_t
+OverscrollHandoffChain::IndexOf(const AsyncPanZoomController* aApzc) const
+{
+ uint32_t i;
+ for (i = 0; i < Length(); ++i) {
+ if (mChain[i] == aApzc) {
+ break;
+ }
+ }
+ return i;
+}
+
+void
+OverscrollHandoffChain::ForEachApzc(APZCMethod aMethod) const
+{
+ for (uint32_t i = 0; i < Length(); ++i) {
+ (mChain[i]->*aMethod)();
+ }
+}
+
+bool
+OverscrollHandoffChain::AnyApzc(APZCPredicate aPredicate) const
+{
+ MOZ_ASSERT(Length() > 0);
+ for (uint32_t i = 0; i < Length(); ++i) {
+ if ((mChain[i]->*aPredicate)()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+OverscrollHandoffChain::FlushRepaints() const
+{
+ ForEachApzc(&AsyncPanZoomController::FlushRepaintForOverscrollHandoff);
+}
+
+void
+OverscrollHandoffChain::CancelAnimations(CancelAnimationFlags aFlags) const
+{
+ MOZ_ASSERT(Length() > 0);
+ for (uint32_t i = 0; i < Length(); ++i) {
+ mChain[i]->CancelAnimation(aFlags);
+ }
+}
+
+void
+OverscrollHandoffChain::ClearOverscroll() const
+{
+ ForEachApzc(&AsyncPanZoomController::ClearOverscroll);
+}
+
+void
+OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const
+{
+ uint32_t i = IndexOf(aStart);
+ for (; i < Length(); ++i) {
+ AsyncPanZoomController* apzc = mChain[i];
+ if (!apzc->IsDestroyed()) {
+ apzc->SnapBackIfOverscrolled();
+ }
+ }
+}
+
+bool
+OverscrollHandoffChain::CanBePanned(const AsyncPanZoomController* aApzc) const
+{
+ // Find |aApzc| in the handoff chain.
+ uint32_t i = IndexOf(aApzc);
+
+ // See whether any APZC in the handoff chain starting from |aApzc|
+ // has room to be panned.
+ for (uint32_t j = i; j < Length(); ++j) {
+ if (mChain[j]->IsPannable()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+OverscrollHandoffChain::CanScrollInDirection(const AsyncPanZoomController* aApzc,
+ Layer::ScrollDirection aDirection) const
+{
+ // Find |aApzc| in the handoff chain.
+ uint32_t i = IndexOf(aApzc);
+
+ // See whether any APZC in the handoff chain starting from |aApzc|
+ // has room to scroll in the given direction.
+ for (uint32_t j = i; j < Length(); ++j) {
+ if (mChain[j]->CanScroll(aDirection)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+OverscrollHandoffChain::HasOverscrolledApzc() const
+{
+ return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
+}
+
+bool
+OverscrollHandoffChain::HasFastFlungApzc() const
+{
+ return AnyApzc(&AsyncPanZoomController::IsFlingingFast);
+}
+
+RefPtr<AsyncPanZoomController>
+OverscrollHandoffChain::FindFirstScrollable(const InputData& aInput) const
+{
+ for (size_t i = 0; i < Length(); i++) {
+ if (mChain[i]->CanScroll(aInput)) {
+ return mChain[i];
+ }
+ }
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/OverscrollHandoffState.h b/system/graphics/layers/apz/src/OverscrollHandoffState.h
new file mode 100644
index 000000000..4e7565a3f
--- /dev/null
+++ b/system/graphics/layers/apz/src/OverscrollHandoffState.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_OverscrollHandoffChain_h
+#define mozilla_layers_OverscrollHandoffChain_h
+
+#include <vector>
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
+#include "APZUtils.h" // for CancelAnimationFlags
+#include "Layers.h" // for Layer::ScrollDirection
+#include "Units.h" // for ScreenPoint
+
+namespace mozilla {
+
+class InputData;
+
+namespace layers {
+
+class AsyncPanZoomController;
+
+/**
+ * This class represents the chain of APZCs along which overscroll is handed off.
+ * It is created by APZCTreeManager by starting from an initial APZC which is
+ * the target for input events, and following the scroll parent ID links (often
+ * but not always corresponding to parent pointers in the APZC tree), then
+ * adjusting for scrollgrab.
+ */
+class OverscrollHandoffChain
+{
+protected:
+ // Reference-counted classes cannot have public destructors.
+ ~OverscrollHandoffChain();
+public:
+ // Threadsafe so that the controller and compositor threads can both maintain
+ // nsRefPtrs to the same handoff chain.
+ // Mutable so that we can pass around the class by
+ // RefPtr<const OverscrollHandoffChain> and thus enforce that, once built,
+ // the chain is not modified.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OverscrollHandoffChain)
+
+ /*
+ * Methods for building the handoff chain.
+ * These should be used only by AsyncPanZoomController::BuildOverscrollHandoffChain().
+ */
+ void Add(AsyncPanZoomController* aApzc);
+ void SortByScrollPriority();
+
+ /*
+ * Methods for accessing the handoff chain.
+ */
+ uint32_t Length() const { return mChain.size(); }
+ const RefPtr<AsyncPanZoomController>& GetApzcAtIndex(uint32_t aIndex) const;
+ // Returns Length() if |aApzc| is not on this chain.
+ uint32_t IndexOf(const AsyncPanZoomController* aApzc) const;
+
+ /*
+ * Convenience methods for performing operations on APZCs in the chain.
+ */
+
+ // Flush repaints all the way up the chain.
+ void FlushRepaints() const;
+
+ // Cancel animations all the way up the chain.
+ void CancelAnimations(CancelAnimationFlags aFlags = Default) const;
+
+ // Clear overscroll all the way up the chain.
+ void ClearOverscroll() const;
+
+ // Snap back the APZC that is overscrolled on the subset of the chain from
+ // |aStart| onwards, if any.
+ void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
+
+ // Determine whether the given APZC, or any APZC further in the chain,
+ // has room to be panned.
+ bool CanBePanned(const AsyncPanZoomController* aApzc) const;
+
+ // Determine whether the given APZC, or any APZC further in the chain,
+ // can scroll in the given direction.
+ bool CanScrollInDirection(const AsyncPanZoomController* aApzc,
+ Layer::ScrollDirection aDirection) const;
+
+ // Determine whether any APZC along this handoff chain is overscrolled.
+ bool HasOverscrolledApzc() const;
+
+ // Determine whether any APZC along this handoff chain has been flung fast.
+ bool HasFastFlungApzc() const;
+
+ RefPtr<AsyncPanZoomController> FindFirstScrollable(const InputData& aInput) const;
+
+private:
+ std::vector<RefPtr<AsyncPanZoomController>> mChain;
+
+ typedef void (AsyncPanZoomController::*APZCMethod)();
+ typedef bool (AsyncPanZoomController::*APZCPredicate)() const;
+ void ForEachApzc(APZCMethod aMethod) const;
+ bool AnyApzc(APZCPredicate aPredicate) const;
+};
+
+/**
+ * This class groups the state maintained during overscroll handoff.
+ */
+struct OverscrollHandoffState {
+ OverscrollHandoffState(const OverscrollHandoffChain& aChain,
+ const ScreenPoint& aPanDistance,
+ ScrollSource aScrollSource)
+ : mChain(aChain),
+ mChainIndex(0),
+ mPanDistance(aPanDistance),
+ mScrollSource(aScrollSource)
+ {}
+
+ // The chain of APZCs along which we hand off scroll.
+ // This is const to indicate that the chain does not change over the
+ // course of handoff.
+ const OverscrollHandoffChain& mChain;
+
+ // The index of the APZC in the chain that we are currently giving scroll to.
+ // This is non-const to indicate that this changes over the course of handoff.
+ uint32_t mChainIndex;
+
+ // The total distance since touch-start of the pan that triggered the
+ // handoff. This is const to indicate that it does not change over the
+ // course of handoff.
+ // The x/y components of this are non-negative.
+ const ScreenPoint mPanDistance;
+
+ ScrollSource mScrollSource;
+};
+
+/*
+ * This class groups the state maintained during fling handoff.
+ */
+struct FlingHandoffState {
+ // The velocity of the fling being handed off.
+ ParentLayerPoint mVelocity;
+
+ // The chain of APZCs along which we hand off the fling.
+ // Unlike in OverscrollHandoffState, this is stored by RefPtr because
+ // otherwise it may not stay alive for the entire handoff.
+ RefPtr<const OverscrollHandoffChain> mChain;
+
+ // Whether handoff has happened by this point, or we're still process
+ // the original fling.
+ bool mIsHandoff;
+
+ // The single APZC that was scrolled by the pan that started this fling.
+ // The fling is only allowed to scroll this APZC, too.
+ // Used only if immediate scroll handoff is disallowed.
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_OverscrollHandoffChain_h */
diff --git a/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.cpp b/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.cpp
new file mode 100644
index 000000000..1504bd4e9
--- /dev/null
+++ b/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PotentialCheckerboardDurationTracker.h"
+
+namespace mozilla {
+namespace layers {
+
+PotentialCheckerboardDurationTracker::PotentialCheckerboardDurationTracker()
+ : mInCheckerboard(false)
+ , mInTransform(false)
+{
+}
+
+void
+PotentialCheckerboardDurationTracker::CheckerboardSeen()
+{
+ mInCheckerboard = true;
+}
+
+void
+PotentialCheckerboardDurationTracker::CheckerboardDone()
+{
+ MOZ_ASSERT(Tracking());
+ mInCheckerboard = false;
+}
+
+void
+PotentialCheckerboardDurationTracker::InTransform(bool aInTransform)
+{
+ if (aInTransform == mInTransform) {
+ // no-op
+ return;
+ }
+
+ if (!Tracking()) {
+ // Because !Tracking(), mInTransform must be false, and so aInTransform
+ // must be true (or we would have early-exited this function already).
+ // Therefore, we are starting a potential checkerboard period.
+ mInTransform = aInTransform;
+ return;
+ }
+
+ mInTransform = aInTransform;
+}
+
+bool
+PotentialCheckerboardDurationTracker::Tracking() const
+{
+ return mInTransform || mInCheckerboard;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.h b/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.h
new file mode 100644
index 000000000..bcba113d3
--- /dev/null
+++ b/system/graphics/layers/apz/src/PotentialCheckerboardDurationTracker.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PotentialCheckerboardDurationTracker_h
+#define mozilla_layers_PotentialCheckerboardDurationTracker_h
+
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class allows the owner to track the duration of time considered
+ * "potentially checkerboarding". This is the union of two possibly-intersecting
+ * sets of time periods. The first set is that in which checkerboarding was
+ * actually happening, since by definition it could potentially be happening.
+ * The second set is that in which the APZC is actively transforming content
+ * in the compositor, since it could potentially transform it so as to display
+ * checkerboarding to the user.
+ * The caller of this class calls the appropriate methods to indicate the start
+ * and stop of these two sets, and this class manages accumulating the union
+ * of the various durations.
+ */
+class PotentialCheckerboardDurationTracker {
+public:
+ PotentialCheckerboardDurationTracker();
+
+ /**
+ * This should be called if checkerboarding is encountered. It can be called
+ * multiple times during a checkerboard event.
+ */
+ void CheckerboardSeen();
+ /**
+ * This should be called when checkerboarding is done. It must have been
+ * preceded by one or more calls to CheckerboardSeen().
+ */
+ void CheckerboardDone();
+
+ /**
+ * This should be called at composition time, to indicate if the APZC is in
+ * a transforming state or not.
+ */
+ void InTransform(bool aInTransform);
+
+private:
+ bool Tracking() const;
+
+private:
+ bool mInCheckerboard;
+ bool mInTransform;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PotentialCheckerboardDurationTracker_h
diff --git a/system/graphics/layers/apz/src/QueuedInput.cpp b/system/graphics/layers/apz/src/QueuedInput.cpp
new file mode 100644
index 000000000..1ce6a89a7
--- /dev/null
+++ b/system/graphics/layers/apz/src/QueuedInput.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "QueuedInput.h"
+
+#include "AsyncPanZoomController.h"
+#include "InputBlockState.h"
+#include "InputData.h"
+#include "OverscrollHandoffState.h"
+
+namespace mozilla {
+namespace layers {
+
+QueuedInput::QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock)
+ : mInput(MakeUnique<MultiTouchInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock)
+ : mInput(MakeUnique<ScrollWheelInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const MouseInput& aInput, DragBlockState& aBlock)
+ : mInput(MakeUnique<MouseInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock)
+ : mInput(MakeUnique<PanGestureInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+InputData*
+QueuedInput::Input()
+{
+ return mInput.get();
+}
+
+CancelableBlockState*
+QueuedInput::Block()
+{
+ return mBlock.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/QueuedInput.h b/system/graphics/layers/apz/src/QueuedInput.h
new file mode 100644
index 000000000..fdf9d97e5
--- /dev/null
+++ b/system/graphics/layers/apz/src/QueuedInput.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_QueuedInput_h
+#define mozilla_layers_QueuedInput_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+class InputData;
+class MultiTouchInput;
+class ScrollWheelInput;
+class MouseInput;
+class PanGestureInput;
+
+namespace layers {
+
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+
+/**
+ * This lightweight class holds a pointer to an input event that has not yet
+ * been completely processed, along with the input block that the input event
+ * is associated with.
+ */
+class QueuedInput
+{
+public:
+ QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock);
+ QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock);
+ QueuedInput(const MouseInput& aInput, DragBlockState& aBlock);
+ QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock);
+
+ InputData* Input();
+ CancelableBlockState* Block();
+
+private:
+ // A copy of the input event that is provided to the constructor. This must
+ // be non-null, and is owned by this QueuedInput instance (hence the
+ // UniquePtr).
+ UniquePtr<InputData> mInput;
+ // A pointer to the block that the input event is associated with. This must
+ // be non-null.
+ RefPtr<CancelableBlockState> mBlock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_QueuedInput_h
diff --git a/system/graphics/layers/apz/src/TouchCounter.cpp b/system/graphics/layers/apz/src/TouchCounter.cpp
new file mode 100644
index 000000000..96dc35dc7
--- /dev/null
+++ b/system/graphics/layers/apz/src/TouchCounter.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TouchCounter.h"
+
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+TouchCounter::TouchCounter()
+ : mActiveTouchCount(0)
+{
+}
+
+void
+TouchCounter::Update(const MultiTouchInput& aInput)
+{
+ switch (aInput.mType) {
+ case MultiTouchInput::MULTITOUCH_START:
+ // touch-start event contains all active touches of the current session
+ mActiveTouchCount = aInput.mTouches.Length();
+ break;
+ case MultiTouchInput::MULTITOUCH_END:
+ if (mActiveTouchCount >= aInput.mTouches.Length()) {
+ // touch-end event contains only released touches
+ mActiveTouchCount -= aInput.mTouches.Length();
+ } else {
+ NS_WARNING("Got an unexpected touchend/touchcancel");
+ mActiveTouchCount = 0;
+ }
+ break;
+ case MultiTouchInput::MULTITOUCH_CANCEL:
+ mActiveTouchCount = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+uint32_t
+TouchCounter::GetActiveTouchCount() const
+{
+ return mActiveTouchCount;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/TouchCounter.h b/system/graphics/layers/apz/src/TouchCounter.h
new file mode 100644
index 000000000..f2c45486e
--- /dev/null
+++ b/system/graphics/layers/apz/src/TouchCounter.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_TouchCounter_h
+#define mozilla_layers_TouchCounter_h
+
+#include "mozilla/EventForwards.h"
+
+namespace mozilla {
+
+class MultiTouchInput;
+
+namespace layers {
+
+// TouchCounter simply tracks the number of active touch points. Feed it
+// your input events to update the internal state.
+class TouchCounter
+{
+public:
+ TouchCounter();
+ void Update(const MultiTouchInput& aInput);
+ uint32_t GetActiveTouchCount() const;
+
+private:
+ uint32_t mActiveTouchCount;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_TouchCounter_h */
diff --git a/system/graphics/layers/apz/src/WheelScrollAnimation.cpp b/system/graphics/layers/apz/src/WheelScrollAnimation.cpp
new file mode 100644
index 000000000..af2430d1c
--- /dev/null
+++ b/system/graphics/layers/apz/src/WheelScrollAnimation.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WheelScrollAnimation.h"
+
+#include "AsyncPanZoomController.h"
+#include "gfxPrefs.h"
+#include "nsPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+WheelScrollAnimation::WheelScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint& aInitialPosition,
+ ScrollWheelInput::ScrollDeltaType aDeltaType)
+ : AsyncScrollBase(aInitialPosition)
+ , mApzc(aApzc)
+ , mFinalDestination(aInitialPosition)
+ , mDeltaType(aDeltaType)
+{
+}
+
+void
+WheelScrollAnimation::Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
+{
+ InitPreferences(aTime);
+
+ mFinalDestination += aDelta;
+
+ // Clamp the final destination to the scrollable area.
+ CSSPoint clamped = CSSPoint::FromAppUnits(mFinalDestination);
+ clamped.x = mApzc.mX.ClampOriginToScrollableRect(clamped.x);
+ clamped.y = mApzc.mY.ClampOriginToScrollableRect(clamped.y);
+ mFinalDestination = CSSPoint::ToAppUnits(clamped);
+
+ AsyncScrollBase::Update(aTime, mFinalDestination, aCurrentVelocity);
+}
+
+bool
+WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta)
+{
+ TimeStamp now = mApzc.GetFrameTime();
+ CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
+
+ // If the animation is finished, make sure the final position is correct by
+ // using one last displacement. Otherwise, compute the delta via the timing
+ // function as normal.
+ bool finished = IsFinished(now);
+ nsPoint sampledDest = finished
+ ? mDestination
+ : PositionAt(now);
+ ParentLayerPoint displacement =
+ (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom;
+
+ if (finished) {
+ mApzc.mX.SetVelocity(0);
+ mApzc.mY.SetVelocity(0);
+ } else if (!IsZero(displacement)) {
+ // Velocity is measured in ParentLayerCoords / Milliseconds
+ float xVelocity = displacement.x / aDelta.ToMilliseconds();
+ float yVelocity = displacement.y / aDelta.ToMilliseconds();
+ mApzc.mX.SetVelocity(xVelocity);
+ mApzc.mY.SetVelocity(yVelocity);
+ }
+
+ // Note: we ignore overscroll for wheel animations.
+ ParentLayerPoint adjustedOffset, overscroll;
+ mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
+ !mApzc.mScrollMetadata.AllowVerticalScrollWithWheel());
+
+ // If we expected to scroll, but there's no more scroll range on either axis,
+ // then end the animation early. Note that the initial displacement could be 0
+ // if the compositor ran very quickly (<1ms) after the animation was created.
+ // When that happens we want to make sure the animation continues.
+ if (!IsZero(displacement) && IsZero(adjustedOffset)) {
+ // Nothing more to do - end the animation.
+ return false;
+ }
+
+ aFrameMetrics.ScrollBy(adjustedOffset / zoom);
+ return !finished;
+}
+
+void
+WheelScrollAnimation::InitPreferences(TimeStamp aTime)
+{
+ if (!mIsFirstIteration) {
+ return;
+ }
+
+ switch (mDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_PAGE:
+ mOriginMaxMS = clamped(gfxPrefs::PageSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::PageSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ case ScrollWheelInput::SCROLLDELTA_PIXEL:
+ mOriginMaxMS = clamped(gfxPrefs::PixelSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::PixelSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ case ScrollWheelInput::SCROLLDELTA_LINE:
+ default:
+ mOriginMaxMS = clamped(gfxPrefs::WheelSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::WheelSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ }
+
+ // The pref is 100-based int percentage, while mIntervalRatio is 1-based ratio
+ mIntervalRatio = ((double)gfxPrefs::SmoothScrollDurationToIntervalRatio()) / 100.0;
+ mIntervalRatio = std::max(1.0, mIntervalRatio);
+
+ InitializeHistory(aTime);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/src/WheelScrollAnimation.h b/system/graphics/layers/apz/src/WheelScrollAnimation.h
new file mode 100644
index 000000000..ed4f91d46
--- /dev/null
+++ b/system/graphics/layers/apz/src/WheelScrollAnimation.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_WheelScrollAnimation_h_
+#define mozilla_layers_WheelScrollAnimation_h_
+
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncScrollBase.h"
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+
+class WheelScrollAnimation
+ : public AsyncPanZoomAnimation,
+ public AsyncScrollBase
+{
+public:
+ WheelScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint& aInitialPosition,
+ ScrollWheelInput::ScrollDeltaType aDeltaType);
+
+ bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
+ void Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
+
+ WheelScrollAnimation* AsWheelScrollAnimation() override {
+ return this;
+ }
+
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(mFinalDestination);
+ }
+
+private:
+ void InitPreferences(TimeStamp aTime);
+
+private:
+ AsyncPanZoomController& mApzc;
+ nsPoint mFinalDestination;
+ ScrollWheelInput::ScrollDeltaType mDeltaType;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_WheelScrollAnimation_h_
diff --git a/system/graphics/layers/apz/test/gtest/APZCBasicTester.h b/system/graphics/layers/apz/test/gtest/APZCBasicTester.h
new file mode 100644
index 000000000..baadef9e1
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/APZCBasicTester.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCBasicTester_h
+#define mozilla_layers_APZCBasicTester_h
+
+/**
+ * Defines a test fixture used for testing a single APZC.
+ */
+
+#include "APZTestCommon.h"
+
+class APZCBasicTester : public APZCTesterBase {
+public:
+ explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
+ : mGestureBehavior(aGestureBehavior)
+ {
+ }
+
+protected:
+ virtual void SetUp()
+ {
+ gfxPrefs::GetSingleton();
+ APZThreadUtils::SetThreadAssertionsEnabled(false);
+ APZThreadUtils::SetControllerThread(MessageLoop::current());
+
+ tm = new TestAPZCTreeManager(mcc);
+ apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
+ apzc->SetFrameMetrics(TestFrameMetrics());
+ apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
+ }
+
+ /**
+ * Get the APZC's scroll range in CSS pixels.
+ */
+ CSSRect GetScrollRange() const
+ {
+ const FrameMetrics& metrics = apzc->GetFrameMetrics();
+ return CSSRect(
+ metrics.GetScrollableRect().TopLeft(),
+ metrics.GetScrollableRect().Size() - metrics.CalculateCompositedSizeInCssPixels());
+ }
+
+ virtual void TearDown()
+ {
+ while (mcc->RunThroughDelayedTasks());
+ apzc->Destroy();
+ tm->ClearTree();
+ tm->ClearContentController();
+ }
+
+ void MakeApzcWaitForMainThread()
+ {
+ apzc->SetWaitForMainThread();
+ }
+
+ void MakeApzcZoomable()
+ {
+ apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f)));
+ }
+
+ void MakeApzcUnzoomable()
+ {
+ apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToParentLayerScale(1.0f), CSSToParentLayerScale(1.0f)));
+ }
+
+ void PanIntoOverscroll();
+
+ /**
+ * Sample animations once, 1 ms later than the last sample.
+ */
+ void SampleAnimationOnce()
+ {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ mcc->AdvanceBy(increment);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ }
+
+ /**
+ * Sample animations until we recover from overscroll.
+ * @param aExpectedScrollOffset the expected reported scroll offset
+ * throughout the animation
+ */
+ void SampleAnimationUntilRecoveredFromOverscroll(const ParentLayerPoint& aExpectedScrollOffset)
+ {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ bool recoveredFromOverscroll = false;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
+ // The reported scroll offset should be the same throughout.
+ EXPECT_EQ(aExpectedScrollOffset, pointOut);
+
+ // Trigger computation of the overscroll tranform, to make sure
+ // no assetions fire during the calculation.
+ apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL);
+
+ if (!apzc->IsOverscrolled()) {
+ recoveredFromOverscroll = true;
+ }
+
+ mcc->AdvanceBy(increment);
+ }
+ EXPECT_TRUE(recoveredFromOverscroll);
+ apzc->AssertStateIsReset();
+ }
+
+ void TestOverscroll();
+
+ AsyncPanZoomController::GestureBehavior mGestureBehavior;
+ RefPtr<TestAPZCTreeManager> tm;
+ RefPtr<TestAsyncPanZoomController> apzc;
+};
+
+#endif // mozilla_layers_APZCBasicTester_h
diff --git a/system/graphics/layers/apz/test/gtest/APZCTreeManagerTester.h b/system/graphics/layers/apz/test/gtest/APZCTreeManagerTester.h
new file mode 100644
index 000000000..a4e9a0f08
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/APZCTreeManagerTester.h
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerTester_h
+#define mozilla_layers_APZCTreeManagerTester_h
+
+/**
+ * Defines a test fixture used for testing multiple APZCs interacting in
+ * an APZCTreeManager.
+ */
+
+#include "APZTestCommon.h"
+#include "gfxPlatform.h"
+
+class APZCTreeManagerTester : public APZCTesterBase {
+protected:
+ virtual void SetUp() {
+ gfxPrefs::GetSingleton();
+ gfxPlatform::GetPlatform();
+ APZThreadUtils::SetThreadAssertionsEnabled(false);
+ APZThreadUtils::SetControllerThread(MessageLoop::current());
+
+ manager = new TestAPZCTreeManager(mcc);
+ }
+
+ virtual void TearDown() {
+ while (mcc->RunThroughDelayedTasks());
+ manager->ClearTree();
+ manager->ClearContentController();
+ }
+
+ /**
+ * Sample animations once for all APZCs, 1 ms later than the last sample.
+ */
+ void SampleAnimationsOnce() {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ mcc->AdvanceBy(increment);
+
+ for (const RefPtr<Layer>& layer : layers) {
+ if (TestAsyncPanZoomController* apzc = ApzcOf(layer)) {
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ }
+ }
+ }
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root;
+
+ RefPtr<TestAPZCTreeManager> manager;
+
+protected:
+ static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,
+ const CSSRect& aScrollableRect,
+ const ParentLayerRect& aCompositionBounds)
+ {
+ ScrollMetadata metadata;
+ FrameMetrics& metrics = metadata.GetMetrics();
+ metrics.SetScrollId(aScrollId);
+ // By convention in this test file, START_SCROLL_ID is the root, so mark it as such.
+ if (aScrollId == FrameMetrics::START_SCROLL_ID) {
+ metadata.SetIsLayersIdRoot(true);
+ }
+ metrics.SetCompositionBounds(aCompositionBounds);
+ metrics.SetScrollableRect(aScrollableRect);
+ metrics.SetScrollOffset(CSSPoint(0, 0));
+ metadata.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
+ metadata.SetLineScrollAmount(LayoutDeviceIntSize(5, 10));
+ metadata.SetAllowVerticalScrollWithWheel(true);
+ return metadata;
+ }
+
+ static void SetEventRegionsBasedOnBottommostMetrics(Layer* aLayer)
+ {
+ const FrameMetrics& metrics = aLayer->GetScrollMetadata(0).GetMetrics();
+ CSSRect scrollableRect = metrics.GetScrollableRect();
+ if (!scrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
+ // The purpose of this is to roughly mimic what layout would do in the
+ // case of a scrollable frame with the event regions and clip. This lets
+ // us exercise the hit-testing code in APZCTreeManager
+ EventRegions er = aLayer->GetEventRegions();
+ IntRect scrollRect = RoundedToInt(
+ scrollableRect * metrics.LayersPixelsPerCSSPixel()).ToUnknownRect();
+ er.mHitRegion = nsIntRegion(IntRect(
+ RoundedToInt(metrics.GetCompositionBounds().TopLeft().ToUnknownPoint()),
+ scrollRect.Size()));
+ aLayer->SetEventRegions(er);
+ }
+ }
+
+ static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
+ CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
+ ParentLayerIntRect compositionBounds = ViewAs<ParentLayerPixel>(
+ aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+ ScrollMetadata metadata = BuildScrollMetadata(aScrollId, aScrollableRect,
+ ParentLayerRect(compositionBounds));
+ aLayer->SetScrollMetadata(metadata);
+ aLayer->SetClipRect(Some(compositionBounds));
+ SetEventRegionsBasedOnBottommostMetrics(aLayer);
+ }
+
+ void SetScrollHandoff(Layer* aChild, Layer* aParent) {
+ ScrollMetadata metadata = aChild->GetScrollMetadata(0);
+ metadata.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId());
+ aChild->SetScrollMetadata(metadata);
+ }
+
+ static TestAsyncPanZoomController* ApzcOf(Layer* aLayer) {
+ EXPECT_EQ(1u, aLayer->GetScrollMetadataCount());
+ return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(0);
+ }
+
+ static TestAsyncPanZoomController* ApzcOf(Layer* aLayer, uint32_t aIndex) {
+ EXPECT_LT(aIndex, aLayer->GetScrollMetadataCount());
+ return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(aIndex);
+ }
+
+ void CreateSimpleScrollingLayer() {
+ const char* layerTreeSyntax = "t";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
+ }
+
+ void CreateSimpleDTCScrollingLayer() {
+ const char* layerTreeSyntax = "t";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
+
+ EventRegions regions;
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 200));
+ regions.mDispatchToContentHitRegion = regions.mHitRegion;
+ layers[0]->SetEventRegions(regions);
+ }
+
+ void CreateSimpleMultiLayerTree() {
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,50)),
+ nsIntRegion(IntRect(0,50,100,50)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ }
+
+ void CreatePotentiallyLeakingTree() {
+ const char* layerTreeSyntax = "c(c(c(t))c(c(t)))";
+ // LayerID 0 1 2 3 4 5 6
+ root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
+ }
+
+ void CreateBug1194876Tree() {
+ const char* layerTreeSyntax = "c(t)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollHandoff(layers[1], layers[0]);
+
+ // Make layers[1] the root content
+ ScrollMetadata childMetadata = layers[1]->GetScrollMetadata(0);
+ childMetadata.GetMetrics().SetIsRootContent(true);
+ layers[1]->SetScrollMetadata(childMetadata);
+
+ // Both layers are fully dispatch-to-content
+ EventRegions regions;
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ regions.mDispatchToContentHitRegion = regions.mHitRegion;
+ layers[0]->SetEventRegions(regions);
+ layers[1]->SetEventRegions(regions);
+ }
+};
+
+#endif // mozilla_layers_APZCTreeManagerTester_h
diff --git a/system/graphics/layers/apz/test/gtest/APZTestCommon.h b/system/graphics/layers/apz/test/gtest/APZTestCommon.h
new file mode 100644
index 000000000..488c8d21a
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/APZTestCommon.h
@@ -0,0 +1,608 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZTestCommon_h
+#define mozilla_layers_APZTestCommon_h
+
+/**
+ * Defines a set of mock classes and utility functions/classes for
+ * writing APZ gtests.
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "apz/src/AsyncPanZoomController.h"
+#include "apz/src/HitTestingTreeNode.h"
+#include "base/task.h"
+#include "Layers.h"
+#include "TestLayers.h"
+#include "UnitTransforms.h"
+#include "gfxPrefs.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::MockFunction;
+using ::testing::InSequence;
+typedef mozilla::layers::GeckoContentController::TapType TapType;
+
+template<class T>
+class ScopedGfxPref {
+public:
+ ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
+ : mSetPrefFunc(aSetPrefFunc)
+ {
+ mOldVal = aGetPrefFunc();
+ aSetPrefFunc(aVal);
+ }
+
+ ~ScopedGfxPref() {
+ mSetPrefFunc(mOldVal);
+ }
+
+private:
+ void (*mSetPrefFunc)(T);
+ T mOldVal;
+};
+
+#define SCOPED_GFX_PREF(prefBase, prefType, prefValue) \
+ ScopedGfxPref<prefType> pref_##prefBase( \
+ &(gfxPrefs::prefBase), \
+ &(gfxPrefs::Set##prefBase), \
+ prefValue)
+
+static TimeStamp GetStartupTime() {
+ static TimeStamp sStartupTime = TimeStamp::Now();
+ return sStartupTime;
+}
+
+class MockContentController : public GeckoContentController {
+public:
+ MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
+ MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
+ MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
+ MOCK_METHOD5(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
+ MOCK_METHOD4(NotifyPinchGesture, void(PinchGestureInput::PinchGestureType, const ScrollableLayerGuid&, LayoutDeviceCoord, Modifiers));
+ // Can't use the macros with already_AddRefed :(
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ RefPtr<Runnable> task = aTask;
+ }
+ bool IsRepaintThread() {
+ return NS_IsMainThread();
+ }
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
+ NS_DispatchToMainThread(Move(aTask));
+ }
+ MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
+ MOCK_METHOD0(NotifyFlushComplete, void());
+};
+
+class MockContentControllerDelayed : public MockContentController {
+public:
+ MockContentControllerDelayed()
+ : mTime(GetStartupTime())
+ {
+ }
+
+ const TimeStamp& Time() {
+ return mTime;
+ }
+
+ void AdvanceByMillis(int aMillis) {
+ AdvanceBy(TimeDuration::FromMilliseconds(aMillis));
+ }
+
+ void AdvanceBy(const TimeDuration& aIncrement) {
+ TimeStamp target = mTime + aIncrement;
+ while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) {
+ RunNextDelayedTask();
+ }
+ mTime = target;
+ }
+
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ RefPtr<Runnable> task = aTask;
+ TimeStamp runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs);
+ int insIndex = mTaskQueue.Length();
+ while (insIndex > 0) {
+ if (mTaskQueue[insIndex - 1].second <= runAtTime) {
+ break;
+ }
+ insIndex--;
+ }
+ mTaskQueue.InsertElementAt(insIndex, std::make_pair(task, runAtTime));
+ }
+
+ // Run all the tasks in the queue, returning the number of tasks
+ // run. Note that if a task queues another task while running, that
+ // new task will not be run. Therefore, there may be still be tasks
+ // in the queue after this function is called. Only when the return
+ // value is 0 is the queue guaranteed to be empty.
+ int RunThroughDelayedTasks() {
+ nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> runQueue;
+ runQueue.SwapElements(mTaskQueue);
+ int numTasks = runQueue.Length();
+ for (int i = 0; i < numTasks; i++) {
+ mTime = runQueue[i].second;
+ runQueue[i].first->Run();
+
+ // Deleting the task is important in order to release the reference to
+ // the callee object.
+ runQueue[i].first = nullptr;
+ }
+ return numTasks;
+ }
+
+private:
+ void RunNextDelayedTask() {
+ std::pair<RefPtr<Runnable>, TimeStamp> next = mTaskQueue[0];
+ mTaskQueue.RemoveElementAt(0);
+ mTime = next.second;
+ next.first->Run();
+ // Deleting the task is important in order to release the reference to
+ // the callee object.
+ next.first = nullptr;
+ }
+
+ // The following array is sorted by timestamp (tasks are inserted in order by
+ // timestamp).
+ nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> mTaskQueue;
+ TimeStamp mTime;
+};
+
+class TestAPZCTreeManager : public APZCTreeManager {
+public:
+ explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc) : mcc(aMcc) {}
+
+ RefPtr<InputQueue> GetInputQueue() const {
+ return mInputQueue;
+ }
+
+ void ClearContentController() {
+ mcc = nullptr;
+ }
+
+protected:
+ AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController) override;
+
+ TimeStamp GetFrameTime() override {
+ return mcc->Time();
+ }
+
+private:
+ RefPtr<MockContentControllerDelayed> mcc;
+};
+
+class TestAsyncPanZoomController : public AsyncPanZoomController {
+public:
+ TestAsyncPanZoomController(uint64_t aLayersId, MockContentControllerDelayed* aMcc,
+ TestAPZCTreeManager* aTreeManager,
+ GestureBehavior aBehavior = DEFAULT_GESTURES)
+ : AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(),
+ aMcc, aBehavior)
+ , mWaitForMainThread(false)
+ , mcc(aMcc)
+ {}
+
+ nsEventStatus ReceiveInputEvent(const InputData& aEvent, ScrollableLayerGuid* aDummy, uint64_t* aOutInputBlockId) {
+ // This is a function whose signature matches exactly the ReceiveInputEvent
+ // on APZCTreeManager. This allows us to templates for functions like
+ // TouchDown, TouchUp, etc so that we can reuse the code for dispatching
+ // events into both APZC and APZCTM.
+ return ReceiveInputEvent(aEvent, aOutInputBlockId);
+ }
+
+ nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
+ return GetInputQueue()->ReceiveInputEvent(this, !mWaitForMainThread, aEvent, aOutInputBlockId);
+ }
+
+ void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
+ GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+ }
+
+ void ConfirmTarget(uint64_t aInputBlockId) {
+ RefPtr<AsyncPanZoomController> target = this;
+ GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target);
+ }
+
+ void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+ GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
+ }
+
+ void SetFrameMetrics(const FrameMetrics& metrics) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mFrameMetrics = metrics;
+ }
+
+ FrameMetrics& GetFrameMetrics() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics;
+ }
+
+ ScrollMetadata& GetScrollMetadata() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mScrollMetadata;
+ }
+
+ const FrameMetrics& GetFrameMetrics() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics;
+ }
+
+ using AsyncPanZoomController::GetVelocityVector;
+
+ void AssertStateIsReset() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ EXPECT_EQ(NOTHING, mState);
+ }
+
+ void AssertStateIsFling() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ EXPECT_EQ(FLING, mState);
+ }
+
+ void AdvanceAnimationsUntilEnd(const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
+ while (AdvanceAnimations(mcc->Time())) {
+ mcc->AdvanceBy(aIncrement);
+ }
+ }
+
+ bool SampleContentTransformForFrame(AsyncTransform* aOutTransform,
+ ParentLayerPoint& aScrollOffset,
+ const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
+ mcc->AdvanceBy(aIncrement);
+ bool ret = AdvanceAnimations(mcc->Time());
+ if (aOutTransform) {
+ *aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL);
+ }
+ aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
+ return ret;
+ }
+
+ void SetWaitForMainThread() {
+ mWaitForMainThread = true;
+ }
+
+private:
+ bool mWaitForMainThread;
+ MockContentControllerDelayed* mcc;
+};
+
+class APZCTesterBase : public ::testing::Test {
+public:
+ APZCTesterBase() {
+ mcc = new NiceMock<MockContentControllerDelayed>();
+ }
+
+ template<class InputReceiver>
+ void Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeDuration aTapLength,
+ nsEventStatus (*aOutEventStatuses)[2] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ template<class InputReceiver>
+ void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint, TimeDuration aTapLength);
+
+ template<class InputReceiver>
+ void Pan(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aTouchStart,
+ const ScreenIntPoint& aTouchEnd,
+ bool aKeepFingerDown = false,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ /*
+ * A version of Pan() that only takes y coordinates rather than (x, y) points
+ * for the touch start and end points, and uses 10 for the x coordinates.
+ * This is for convenience, as most tests only need to pan in one direction.
+ */
+ template<class InputReceiver>
+ void Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
+ int aTouchEndY, bool aKeepFingerDown = false,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ /*
+ * Dispatches mock touch events to the apzc and checks whether apzc properly
+ * consumed them and triggered scrolling behavior.
+ */
+ template<class InputReceiver>
+ void PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
+ int aTouchEndY,
+ bool aExpectConsumed,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ void ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
+ int aTouchStartY,
+ int aTouchEndY,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ template<class InputReceiver>
+ void DoubleTap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t (*aOutInputBlockIds)[2] = nullptr);
+
+ template<class InputReceiver>
+ void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ uint64_t (*aOutInputBlockIds)[2] = nullptr);
+
+protected:
+ RefPtr<MockContentControllerDelayed> mcc;
+};
+
+template<class InputReceiver>
+void
+APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint, TimeDuration aTapLength,
+ nsEventStatus (*aOutEventStatuses)[2],
+ uint64_t* aOutInputBlockId)
+{
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+ mcc->AdvanceBy(aTapLength);
+
+ // If touch-action is enabled then simulate the allowed touch behaviour
+ // notification that the main thread is supposed to deliver.
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ TimeDuration aTapLength)
+{
+ nsEventStatus statuses[2];
+ Tap(aTarget, aPoint, aTapLength, &statuses);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aTouchStart,
+ const ScreenIntPoint& aTouchEnd,
+ bool aKeepFingerDown,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t* aOutInputBlockId)
+{
+ // Reduce the touch start and move tolerance to a tiny value.
+ // We can't use a scoped pref because this value might be read at some later
+ // time when the events are actually processed, rather than when we deliver
+ // them.
+ gfxPrefs::SetAPZTouchStartTolerance(1.0f / 1000.0f);
+ gfxPrefs::SetAPZTouchMoveTolerance(0.0f);
+ const int OVERCOME_TOUCH_TOLERANCE = 1;
+
+ const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50);
+
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ // Make sure the move is large enough to not be handled as a tap
+ nsEventStatus status = TouchDown(aTarget,
+ ScreenIntPoint(aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE),
+ mcc->Time(), aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ // Allowed touch behaviours must be set after sending touch-start.
+ if (status != nsEventStatus_eConsumeNoDefault) {
+ if (aAllowedTouchBehaviors) {
+ EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
+ aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
+ } else if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
+ }
+ }
+
+ status = TouchMove(aTarget, aTouchStart, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ status = TouchMove(aTarget, aTouchEnd, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ if (!aKeepFingerDown) {
+ status = TouchUp(aTarget, aTouchEnd, mcc->Time());
+ } else {
+ status = nsEventStatus_eIgnore;
+ }
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+
+ // Don't increment the time here. Animations started on touch-up, such as
+ // flings, are affected by elapsed time, and we want to be able to sample
+ // them immediately after they start, without time having elapsed.
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
+ int aTouchStartY, int aTouchEndY, bool aKeepFingerDown,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t* aOutInputBlockId)
+{
+ Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY),
+ aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ int aTouchStartY,
+ int aTouchEndY,
+ bool aExpectConsumed,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ uint64_t* aOutInputBlockId)
+{
+ nsEventStatus statuses[4]; // down, move, move, up
+ Pan(aTarget, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
+
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+
+ nsEventStatus touchMoveStatus;
+ if (aExpectConsumed) {
+ touchMoveStatus = nsEventStatus_eConsumeDoDefault;
+ } else {
+ touchMoveStatus = nsEventStatus_eIgnore;
+ }
+ EXPECT_EQ(touchMoveStatus, statuses[1]);
+ EXPECT_EQ(touchMoveStatus, statuses[2]);
+}
+
+void
+APZCTesterBase::ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
+ int aTouchStartY, int aTouchEndY,
+ uint64_t* aOutInputBlockId)
+{
+ Pan(aApzc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
+ aApzc->CancelAnimation();
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::DoubleTap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t (*aOutInputBlockIds)[2])
+{
+ uint64_t blockId;
+ nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+ if (aOutInputBlockIds) {
+ (*aOutInputBlockIds)[0] = blockId;
+ }
+ mcc->AdvanceByMillis(10);
+
+ // If touch-action is enabled then simulate the allowed touch behaviour
+ // notification that the main thread is supposed to deliver.
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, blockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+ mcc->AdvanceByMillis(10);
+ status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+ if (aOutInputBlockIds) {
+ (*aOutInputBlockIds)[1] = blockId;
+ }
+ mcc->AdvanceByMillis(10);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, blockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ uint64_t (*aOutInputBlockIds)[2])
+{
+ nsEventStatus statuses[4];
+ DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
+}
+
+AsyncPanZoomController*
+TestAPZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
+ return new TestAsyncPanZoomController(aLayersId, mcc, this,
+ AsyncPanZoomController::USE_GESTURE_DETECTOR);
+}
+
+FrameMetrics
+TestFrameMetrics()
+{
+ FrameMetrics fm;
+
+ fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
+ fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
+ fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
+ fm.SetScrollableRect(CSSRect(0, 0, 100, 100));
+
+ return fm;
+}
+
+uint32_t
+MillisecondsSinceStartup(TimeStamp aTime)
+{
+ return (aTime - GetStartupTime()).ToMilliseconds();
+}
+
+#endif // mozilla_layers_APZTestCommon_h
diff --git a/system/graphics/layers/apz/test/gtest/InputUtils.h b/system/graphics/layers/apz/test/gtest/InputUtils.h
new file mode 100644
index 000000000..5b18e117f
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/InputUtils.h
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputUtils_h
+#define mozilla_layers_InputUtils_h
+
+/**
+ * Defines a set of utility functions for generating input events
+ * to an APZC/APZCTM during APZ gtests.
+ */
+
+#include "APZTestCommon.h"
+
+/* The InputReceiver template parameter used in the helper functions below needs
+ * to be a class that implements functions with the signatures:
+ * nsEventStatus ReceiveInputEvent(const InputData& aEvent,
+ * ScrollableLayerGuid* aGuid,
+ * uint64_t* aOutInputBlockId);
+ * void SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ * const nsTArray<uint32_t>& aBehaviours);
+ * The classes that currently implement these are APZCTreeManager and
+ * TestAsyncPanZoomController. Using this template allows us to test individual
+ * APZC instances in isolation and also an entire APZ tree, while using the same
+ * code to dispatch input events.
+ */
+
+// Some helper functions for constructing input event objects suitable to be
+// passed either to an APZC (which expects an transformed point), or to an APZTM
+// (which expects an untransformed point). We handle both cases by setting both
+// the transformed and untransformed fields to the same value.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
+{
+ SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
+ touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
+ return touch;
+}
+
+// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
+{
+ return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
+}
+
+PinchGestureInput
+CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
+ const ScreenIntPoint& aFocus,
+ float aCurrentSpan, float aPreviousSpan)
+{
+ ParentLayerPoint localFocus(aFocus.x, aFocus.y);
+ PinchGestureInput result(aType, 0, TimeStamp(), localFocus,
+ aCurrentSpan, aPreviousSpan, 0);
+ result.mFocusPoint = aFocus;
+ return result;
+}
+
+template<class InputReceiver>
+void
+SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
+ uint64_t aInputBlockId,
+ int touchPoints = 1)
+{
+ nsTArray<uint32_t> defaultBehaviors;
+ // use the default value where everything is allowed
+ for (int i = 0; i < touchPoints; i++) {
+ defaultBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM
+ | mozilla::layers::AllowedTouchBehavior::DOUBLE_TAP_ZOOM);
+ }
+ aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
+}
+
+
+MultiTouchInput
+CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType, TimeStamp aTime)
+{
+ return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
+}
+
+template<class InputReceiver>
+void
+PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus,
+ const ScreenIntPoint& aSecondFocus, float aScale,
+ nsEventStatus (*aOutEventStatuses)[3] = nullptr)
+{
+ nsEventStatus actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
+ aFocus, 10.0, 10.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = actualStatus;
+ }
+ actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
+ aSecondFocus, 10.0 * aScale, 10.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = actualStatus;
+ }
+ actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
+ // note: negative values here tell APZC
+ // not to turn the pinch into a pan
+ aFocus, -1.0, -1.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = actualStatus;
+ }
+}
+
+template<class InputReceiver>
+void
+PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ bool aShouldTriggerPinch)
+{
+ nsEventStatus statuses[3]; // scalebegin, scale, scaleend
+ PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses);
+
+ nsEventStatus expectedStatus = aShouldTriggerPinch
+ ? nsEventStatus_eConsumeNoDefault
+ : nsEventStatus_eIgnore;
+ EXPECT_EQ(expectedStatus, statuses[0]);
+ EXPECT_EQ(expectedStatus, statuses[1]);
+}
+
+template<class InputReceiver>
+void
+PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ int& inputId,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr)
+{
+ // Having pinch coordinates in float type may cause problems with high-precision scale values
+ // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
+ float pinchLength = 100.0;
+ float pinchLengthScaled = pinchLength * aScale;
+
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
+ nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+
+ if (aAllowedTouchBehaviors) {
+ EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
+ aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
+ } else if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
+ }
+
+ MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+
+ MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+
+ MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+
+ inputId += 2;
+}
+
+template<class InputReceiver>
+void
+PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ int& inputId, bool aShouldTriggerPinch,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors)
+{
+ nsEventStatus statuses[4]; // down, move, move, up
+ PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
+
+ nsEventStatus expectedMoveStatus = aShouldTriggerPinch
+ ? nsEventStatus_eConsumeDoDefault
+ : nsEventStatus_eIgnore;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(expectedMoveStatus, statuses[1]);
+ EXPECT_EQ(expectedMoveStatus, statuses[2]);
+}
+
+template<class InputReceiver>
+nsEventStatus
+Wheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ aPoint, aDelta.x, aDelta.y, false);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+SmoothWheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
+ ScrollWheelInput::SCROLLMODE_SMOOTH, ScrollWheelInput::SCROLLDELTA_LINE,
+ aPoint, aDelta.x, aDelta.y, false);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_DOWN, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_MOVE, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_UP, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+
+#endif // mozilla_layers_InputUtils_h
diff --git a/system/graphics/layers/apz/test/gtest/TestBasic.cpp b/system/graphics/layers/apz/test/gtest/TestBasic.cpp
new file mode 100644
index 000000000..a5d8dfba5
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestBasic.cpp
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+TEST_F(APZCBasicTester, Overzoom) {
+ // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
+ fm.SetScrollableRect(CSSRect(0, 0, 125, 150));
+ fm.SetScrollOffset(CSSPoint(10, 0));
+ fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0));
+ fm.SetIsRootContent(true);
+ apzc->SetFrameMetrics(fm);
+
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true);
+
+ fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
+ // bug 936721 - PGO builds introduce rounding error so
+ // use a fuzzy match instead
+ EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
+ EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
+}
+
+TEST_F(APZCBasicTester, SimpleTransform) {
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+}
+
+
+TEST_F(APZCBasicTester, ComplexTransform) {
+ // This test assumes there is a page that gets rendered to
+ // two layers. In CSS pixels, the first layer is 50x50 and
+ // the second layer is 25x50. The widget scale factor is 3.0
+ // and the presShell resolution is 2.0. Therefore, these layers
+ // end up being 300x300 and 150x300 in layer pixels.
+ //
+ // The second (child) layer has an additional CSS transform that
+ // stretches it by 2.0 on the x-axis. Therefore, after applying
+ // CSS transforms, the two layers are the same size in screen
+ // pixels.
+ //
+ // The screen itself is 24x24 in screen pixels (therefore 4x4 in
+ // CSS pixels). The displayport is 1 extra CSS pixel on all
+ // sides.
+
+ RefPtr<TestAsyncPanZoomController> childApzc =
+ new TestAsyncPanZoomController(0, mcc, tm);
+
+ const char* layerTreeSyntax = "c(c)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 300, 300)),
+ nsIntRegion(IntRect(0, 0, 150, 300)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
+ transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
+
+ ScrollMetadata metadata;
+ FrameMetrics& metrics = metadata.GetMetrics();
+ metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24));
+ metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6));
+ metrics.SetScrollOffset(CSSPoint(10, 10));
+ metrics.SetScrollableRect(CSSRect(0, 0, 50, 50));
+ metrics.SetCumulativeResolution(LayoutDeviceToLayerScale2D(2, 2));
+ metrics.SetPresShellResolution(2.0f);
+ metrics.SetZoom(CSSToParentLayerScale2D(6, 6));
+ metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
+ metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
+
+ ScrollMetadata childMetadata = metadata;
+ FrameMetrics& childMetrics = childMetadata.GetMetrics();
+ childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
+
+ layers[0]->SetScrollMetadata(metadata);
+ layers[1]->SetScrollMetadata(childMetadata);
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // Both the parent and child layer should behave exactly the same here, because
+ // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
+
+ // initial transform
+ apzc->SetFrameMetrics(metrics);
+ apzc->NotifyLayersUpdated(metadata, true, true);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
+
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->NotifyLayersUpdated(childMetadata, true, true);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
+
+ // do an async scroll by 5 pixels and check the transform
+ metrics.ScrollBy(CSSPoint(5, 0));
+ apzc->SetFrameMetrics(metrics);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
+
+ childMetrics.ScrollBy(CSSPoint(5, 0));
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
+
+ // do an async zoom of 1.5x and check the transform
+ metrics.ZoomBy(1.5f);
+ apzc->SetFrameMetrics(metrics);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
+
+ childMetrics.ZoomBy(1.5f);
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
+
+ childApzc->Destroy();
+}
+
+TEST_F(APZCBasicTester, Fling) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // Fling down. Each step scroll further down
+ Pan(apzc, touchStart, touchEnd);
+ ParentLayerPoint lastPoint;
+ for (int i = 1; i < 50; i+=1) {
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(1));
+ EXPECT_GT(pointOut.y, lastPoint.y);
+ lastPoint = pointOut;
+ }
+}
+
+TEST_F(APZCBasicTester, FlingIntoOverscroll) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ // Scroll down by 25 px. Don't fling for simplicity.
+ ApzcPanNoFling(apzc, 50, 25);
+
+ // Now scroll back up by 20px, this time flinging after.
+ // The fling should cover the remaining 5 px of room to scroll, then
+ // go into overscroll, and finally snap-back to recover from overscroll.
+ Pan(apzc, 25, 45);
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ bool reachedOverscroll = false;
+ bool recoveredFromOverscroll = false;
+ while (apzc->AdvanceAnimations(mcc->Time())) {
+ if (!reachedOverscroll && apzc->IsOverscrolled()) {
+ reachedOverscroll = true;
+ }
+ if (reachedOverscroll && !apzc->IsOverscrolled()) {
+ recoveredFromOverscroll = true;
+ }
+ mcc->AdvanceBy(increment);
+ }
+ EXPECT_TRUE(reachedOverscroll);
+ EXPECT_TRUE(recoveredFromOverscroll);
+}
+
+TEST_F(APZCBasicTester, PanningTransformNotifications) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Scroll down by 25 px. Ensure we only get one set of
+ // state change notifications.
+ //
+ // Then, scroll back up by 20px, this time flinging after.
+ // The fling should cover the remaining 5 px of room to scroll, then
+ // go into overscroll, and finally snap-back to recover from overscroll.
+ // Again, ensure we only get one set of state change notifications for
+ // this entire procedure.
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call("Simple pan"));
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
+ EXPECT_CALL(check, Call("Complex pan"));
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
+ EXPECT_CALL(check, Call("Done"));
+ }
+
+ check.Call("Simple pan");
+ ApzcPanNoFling(apzc, 50, 25);
+ check.Call("Complex pan");
+ Pan(apzc, 25, 45);
+ apzc->AdvanceAnimationsUntilEnd();
+ check.Call("Done");
+}
+
+void APZCBasicTester::PanIntoOverscroll()
+{
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd);
+ EXPECT_TRUE(apzc->IsOverscrolled());
+}
+
+void APZCBasicTester::TestOverscroll()
+{
+ // Pan sufficiently to hit overscroll behavior
+ PanIntoOverscroll();
+
+ // Check that we recover from overscroll via an animation.
+ ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
+ SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
+}
+
+
+TEST_F(APZCBasicTester, OverScrollPanning) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ TestOverscroll();
+}
+
+// Tests that an overscroll animation doesn't trigger an assertion failure
+// in the case where a sample has a velocity of zero.
+TEST_F(APZCBasicTester, OverScroll_Bug1152051a) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Doctor the prefs to make the velocity zero at the end of the first sample.
+
+ // This ensures our incoming velocity to the overscroll animation is
+ // a round(ish) number, 4.9 (that being the distance of the pan before
+ // overscroll, which is 500 - 10 = 490 pixels, divided by the duration of
+ // the pan, which is 100 ms).
+ SCOPED_GFX_PREF(APZFlingFriction, float, 0);
+
+ // To ensure the velocity after the first sample is 0, set the spring
+ // stiffness to the incoming velocity (4.9) divided by the overscroll
+ // (400 pixels) times the step duration (1 ms).
+ SCOPED_GFX_PREF(APZOverscrollSpringStiffness, float, 0.01225f);
+
+ TestOverscroll();
+}
+
+// Tests that ending an overscroll animation doesn't leave around state that
+// confuses the next overscroll animation.
+TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ SCOPED_GFX_PREF(APZOverscrollStopDistanceThreshold, float, 0.1f);
+
+ // Pan sufficiently to hit overscroll behavior
+ PanIntoOverscroll();
+
+ // Sample animations once, to give the fling animation started on touch-up
+ // a chance to realize it's overscrolled, and schedule a call to
+ // HandleFlingOverscroll().
+ SampleAnimationOnce();
+
+ // This advances the time and runs the HandleFlingOverscroll task scheduled in
+ // the previous call, which starts an overscroll animation. It then samples
+ // the overscroll animation once, to get it to initialize the first overscroll
+ // sample.
+ SampleAnimationOnce();
+
+ // Do a touch-down to cancel the overscroll animation, and then a touch-up
+ // to schedule a new one since we're still overscrolled. We don't pan because
+ // panning can trigger functions that clear the overscroll animation state
+ // in other ways.
+ uint64_t blockId;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(apzc, blockId);
+ }
+ TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+
+ // Sample the second overscroll animation to its end.
+ // If the ending of the first overscroll animation fails to clear state
+ // properly, this will assert.
+ ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
+ SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
+}
+
+TEST_F(APZCBasicTester, OverScrollAbort) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Pan sufficiently to hit overscroll behavior
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd);
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // This sample call will run to the end of the fling animation
+ // and will schedule the overscroll animation.
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(10000));
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ // At this point, we have an active overscroll animation.
+ // Check that cancelling the animation clears the overscroll.
+ apzc->CancelAnimation();
+ EXPECT_FALSE(apzc->IsOverscrolled());
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCBasicTester, OverScrollPanningAbort) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Pan sufficiently to hit overscroll behaviour. Keep the finger down so
+ // the pan does not end.
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd, true); // keep finger down
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ // Check that calling CancelAnimation() while the user is still panning
+ // (and thus no fling or snap-back animation has had a chance to start)
+ // clears the overscroll.
+ apzc->CancelAnimation();
+ EXPECT_FALSE(apzc->IsOverscrolled());
+ apzc->AssertStateIsReset();
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestEventRegions.cpp b/system/graphics/layers/apz/test/gtest/TestEventRegions.cpp
new file mode 100644
index 000000000..bec768ed6
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestEventRegions.cpp
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZEventRegionsTester : public APZCTreeManagerTester {
+protected:
+ UniquePtr<ScopedLayerTreeRegistration> registration;
+ TestAsyncPanZoomController* rootApzc;
+
+ void CreateEventRegionsLayerTree1() {
+ const char* layerTreeSyntax = "c(tt)";
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 200, 200)), // root
+ nsIntRegion(IntRect(0, 0, 100, 200)), // left half
+ nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollHandoff(layers[1], root);
+ SetScrollHandoff(layers[2], root);
+
+ // Set up the event regions over a 200x200 area. The root layer has the
+ // whole 200x200 as the hit region; layers[1] has the left half and
+ // layers[2] has the bottom half. The bottom-left 100x100 area is also
+ // in the d-t-c region for both layers[1] and layers[2] (but layers[2] is
+ // on top so it gets the events by default if the main thread doesn't
+ // respond).
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
+ root->SetEventRegions(regions);
+ regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100));
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
+ layers[1]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
+ layers[2]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateEventRegionsLayerTree2() {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 500)),
+ nsIntRegion(IntRect(0, 150, 100, 100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+
+ // Set up the event regions so that the child thebes layer is positioned far
+ // away from the scrolling container layer.
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
+ root->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
+ layers[1]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateObscuringLayerTree() {
+ const char* layerTreeSyntax = "c(c(t)t)";
+ // LayerID 0 1 2 3
+ // 0 is the root.
+ // 1 is a parent scrollable layer.
+ // 2 is a child scrollable layer.
+ // 3 is the Obscurer, who ruins everything.
+ nsIntRegion layerVisibleRegions[] = {
+ // x coordinates are uninteresting
+ nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
+ nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
+ nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150]
+ nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200]
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100));
+ SetScrollHandoff(layers[2], layers[1]);
+ SetScrollHandoff(layers[1], root);
+
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
+ root->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
+ layers[1]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
+ layers[2]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateBug1119497LayerTree() {
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ // 0 is the root and has an APZC
+ // 1 is behind 2 and has an APZC
+ // 2 entirely covers 1 and should take all the input events, but has no APZC
+ // so hits to 2 should go to to the root APZC
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+
+ void CreateBug1117712LayerTree() {
+ const char* layerTreeSyntax = "c(c(t)t)";
+ // LayerID 0 1 2 3
+ // 0 is the root
+ // 1 is a container layer whose sole purpose to make a non-empty ancestor
+ // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
+ // transforms are different from 3's.
+ // 2 is a small layer that is the actual target
+ // 3 is a big layer obscuring 2 with a dispatch-to-content region
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 0, 0)),
+ nsIntRegion(IntRect(0, 0, 10, 10)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ };
+ Matrix4x4 layerTransforms[] = {
+ Matrix4x4(),
+ Matrix4x4::Translation(50, 0, 0),
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers);
+
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 10, 10));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[3], layers[2]);
+
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
+ layers[2]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ layers[3]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+};
+
+TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) {
+ CreateEventRegionsLayerTree1();
+
+ TestAsyncPanZoomController* root = ApzcOf(layers[0]);
+ TestAsyncPanZoomController* left = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on left"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on bottom"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on root"));
+ EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on bottom again"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on left this time"));
+ }
+
+ TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
+
+ // Tap in the exposed hit regions of each of the layers once and ensure
+ // the clicks are dispatched right away
+ Tap(manager, ScreenIntPoint(10, 10), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on left");
+ Tap(manager, ScreenIntPoint(110, 110), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on bottom");
+ Tap(manager, ScreenIntPoint(110, 10), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on root");
+
+ // Now tap on the dispatch-to-content region where the layers overlap
+ Tap(manager, ScreenIntPoint(10, 110), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout
+ check.Call("Tap pending on d-t-c region");
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on bottom again");
+
+ // Now let's do that again, but simulate a main-thread response
+ uint64_t inputBlockId = 0;
+ Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
+ nsTArray<ScrollableLayerGuid> targets;
+ targets.AppendElement(left->GetGuid());
+ manager->SetTargetAPZC(inputBlockId, targets);
+ while (mcc->RunThroughDelayedTasks()); // this runs the tap event
+ check.Call("Tapped on left this time");
+}
+
+TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
+ CreateEventRegionsLayerTree2();
+
+ // Tap in the area of the child layer that's not directly included in the
+ // parent layer's hit region. Verify that it comes out of the APZC's
+ // content controller, which indicates the input events got routed correctly
+ // to the APZC.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1);
+ Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
+}
+
+TEST_F(APZEventRegionsTester, Obscuration) {
+ CreateObscuringLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* parent = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* child = ApzcOf(layers[2]);
+
+ ApzcPanNoFling(parent, 75, 25);
+
+ HitTestResult result;
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result);
+ EXPECT_EQ(child, hit.get());
+ EXPECT_EQ(HitTestResult::HitLayer, result);
+}
+
+TEST_F(APZEventRegionsTester, Bug1119497) {
+ CreateBug1119497LayerTree();
+
+ HitTestResult result;
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result);
+ // We should hit layers[2], so |result| will be HitLayer but there's no
+ // actual APZC on layers[2], so it will be the APZC of the root layer.
+ EXPECT_EQ(ApzcOf(layers[0]), hit.get());
+ EXPECT_EQ(HitTestResult::HitLayer, result);
+}
+
+TEST_F(APZEventRegionsTester, Bug1117712) {
+ CreateBug1117712LayerTree();
+
+ TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
+
+ // These touch events should hit the dispatch-to-content region of layers[3]
+ // and so get queued with that APZC as the tentative target.
+ uint64_t inputBlockId = 0;
+ Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
+ // But now we tell the APZ that really it hit layers[2], and expect the tap
+ // to be delivered at the correct coordinates.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
+
+ nsTArray<ScrollableLayerGuid> targets;
+ targets.AppendElement(apzc2->GetGuid());
+ manager->SetTargetAPZC(inputBlockId, targets);
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestGestureDetector.cpp b/system/graphics/layers/apz/test/gtest/TestGestureDetector.cpp
new file mode 100644
index 000000000..ac60bec3a
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -0,0 +1,637 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+
+class APZCGestureDetectorTester : public APZCBasicTester {
+public:
+ APZCGestureDetectorTester()
+ : APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
+ {
+ }
+
+protected:
+ FrameMetrics GetPinchableFrameMetrics()
+ {
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
+ fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
+ fm.SetScrollOffset(CSSPoint(300, 300));
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ // APZC only allows zooming on the root scrollable frame.
+ fm.SetIsRootContent(true);
+ // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
+ return fm;
+ }
+};
+
+TEST_F(APZCGestureDetectorTester, Pan_After_Pinch) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ MakeApzcZoomable();
+
+ // Test parameters
+ float zoomAmount = 1.25;
+ float pinchLength = 100.0;
+ float pinchLengthScaled = pinchLength * zoomAmount;
+ int focusX = 250;
+ int focusY = 300;
+ int panDistance = 20;
+
+ int firstFingerId = 0;
+ int secondFingerId = firstFingerId + 1;
+
+ // Put fingers down
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Spread fingers out to enter the pinch state
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLength, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLength, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do the actual pinch of 1.25x
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that the zoom changed, just to make sure our code above did what it
+ // was supposed to.
+ FrameMetrics zoomedMetrics = apzc->GetFrameMetrics();
+ float newZoom = zoomedMetrics.GetZoom().ToScaleFactor().scale;
+ EXPECT_EQ(originalMetrics.GetZoom().ToScaleFactor().scale * zoomAmount, newZoom);
+
+ // Now we lift one finger...
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // ... and pan with the remaining finger. This pan just breaks through the
+ // distance threshold.
+ focusY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // This one does an actual pan of 20 pixels
+ focusY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the remaining finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that we scrolled
+ FrameMetrics finalMetrics = apzc->GetFrameMetrics();
+ EXPECT_EQ(zoomedMetrics.GetScrollOffset().y - (panDistance / newZoom), finalMetrics.GetScrollOffset().y);
+
+ // Clear out any remaining fling animation and pending tasks
+ apzc->AdvanceAnimationsUntilEnd();
+ while (mcc->RunThroughDelayedTasks());
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, Pan_With_Tap) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // Making the APZC zoomable isn't really needed for the correct operation of
+ // this test, but it could help catch regressions where we accidentally enter
+ // a pinch state.
+ MakeApzcZoomable();
+
+ // Test parameters
+ int touchX = 250;
+ int touchY = 300;
+ int panDistance = 20;
+
+ int firstFingerId = 0;
+ int secondFingerId = firstFingerId + 1;
+
+ // Put finger down
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Start a pan, break through the threshold
+ touchY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do an actual pan for a bit
+ touchY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Put a second finger down
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the second finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Bust through the threshold again
+ touchY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do some more actual panning
+ touchY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the first finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that we scrolled
+ FrameMetrics finalMetrics = apzc->GetFrameMetrics();
+ float zoom = finalMetrics.GetZoom().ToScaleFactor().scale;
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y - (panDistance * 2 / zoom), finalMetrics.GetScrollOffset().y);
+
+ // Clear out any remaining fling animation and pending tasks
+ apzc->AdvanceAnimationsUntilEnd();
+ while (mcc->RunThroughDelayedTasks());
+ apzc->AssertStateIsReset();
+}
+
+class APZCFlingStopTester : public APZCGestureDetectorTester {
+protected:
+ // Start a fling, and then tap while the fling is ongoing. When
+ // aSlow is false, the tap will happen while the fling is at a
+ // high velocity, and we check that the tap doesn't trigger sending a tap
+ // to content. If aSlow is true, the tap will happen while the fling
+ // is at a slow velocity, and we check that the tap does trigger sending
+ // a tap to content. See bug 1022956.
+ void DoFlingStopTest(bool aSlow) {
+ int touchStart = 50;
+ int touchEnd = 10;
+
+ // Start the fling down.
+ Pan(apzc, touchStart, touchEnd);
+ // The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
+
+ // If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
+ // the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
+ // friction and threshold values change, but they should be deterministic at least.
+ int timeDelta = aSlow ? 2000 : 10;
+ int tapCallsExpected = aSlow ? 2 : 1;
+
+ // Advance the fling animation by timeDelta milliseconds.
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
+
+ // Deliver a tap to abort the fling. Ensure that we get a SingleTap
+ // call out of it if and only if the fling is slow.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _)).Times(tapCallsExpected);
+ Tap(apzc, ScreenIntPoint(10, 10), 0);
+ while (mcc->RunThroughDelayedTasks());
+
+ // Deliver another tap, to make sure that taps are flowing properly once
+ // the fling is aborted.
+ Tap(apzc, ScreenIntPoint(100, 100), 0);
+ while (mcc->RunThroughDelayedTasks());
+
+ // Verify that we didn't advance any further after the fling was aborted, in either case.
+ ParentLayerPoint finalPointOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut);
+ EXPECT_EQ(pointOut.x, finalPointOut.x);
+ EXPECT_EQ(pointOut.y, finalPointOut.y);
+
+ apzc->AssertStateIsReset();
+ }
+
+ void DoFlingStopWithSlowListener(bool aPreventDefault) {
+ MakeApzcWaitForMainThread();
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ uint64_t blockId = 0;
+
+ // Start the fling down.
+ Pan(apzc, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
+ apzc->ConfirmTarget(blockId);
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ // Sample the fling a couple of times to ensure it's going.
+ ParentLayerPoint point, finalPoint;
+ AsyncTransform viewTransform;
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
+ apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, TimeDuration::FromMilliseconds(10));
+ EXPECT_GT(finalPoint.y, point.y);
+
+ // Now we put our finger down to stop the fling
+ TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+
+ // Re-sample to make sure it hasn't moved
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
+ EXPECT_EQ(finalPoint.x, point.x);
+ EXPECT_EQ(finalPoint.y, point.y);
+
+ // respond to the touchdown that stopped the fling.
+ // even if we do a prevent-default on it, the animation should remain stopped.
+ apzc->ContentReceivedInputBlock(blockId, aPreventDefault);
+
+ // Verify the page hasn't moved
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(70));
+ EXPECT_EQ(finalPoint.x, point.x);
+ EXPECT_EQ(finalPoint.y, point.y);
+
+ // clean up
+ TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCFlingStopTester, FlingStop) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopTest(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopTap) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopTest(true);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopWithSlowListener(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopWithSlowListener(true);
+}
+
+TEST_F(APZCGestureDetectorTester, ShortPress) {
+ MakeApzcUnzoomable();
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ // This verifies that the single tap notification is sent after the
+ // touchup is fully processed. The ordering here is important.
+ EXPECT_CALL(check, Call("pre-tap"));
+ EXPECT_CALL(check, Call("post-tap"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ }
+
+ check.Call("pre-tap");
+ TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+ check.Call("post-tap");
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, MediumPress) {
+ MakeApzcUnzoomable();
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ // This verifies that the single tap notification is sent after the
+ // touchup is fully processed. The ordering here is important.
+ EXPECT_CALL(check, Call("pre-tap"));
+ EXPECT_CALL(check, Call("post-tap"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ }
+
+ check.Call("pre-tap");
+ TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(400));
+ check.Call("post-tap");
+
+ apzc->AssertStateIsReset();
+}
+
+class APZCLongPressTester : public APZCGestureDetectorTester {
+protected:
+ void DoLongPressTest(uint32_t aBehavior) {
+ MakeApzcUnzoomable();
+
+ uint64_t blockId = 0;
+
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ // SetAllowedTouchBehavior() must be called after sending touch-start.
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+ apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
+ }
+ // Have content "respond" to the touchstart
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call("preHandleLongTap"));
+ blockId++;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTap"));
+
+ EXPECT_CALL(check, Call("preHandleLongTapUp"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTapUp"));
+ }
+
+ // Manually invoke the longpress while the touch is currently down.
+ check.Call("preHandleLongTap");
+ mcc->RunThroughDelayedTasks();
+ check.Call("postHandleLongTap");
+
+ // Dispatching the longpress event starts a new touch block, which
+ // needs a new content response and also has a pending timeout task
+ // in the queue. Deal with those here. We do the content response first
+ // with preventDefault=false, and then we run the timeout task which
+ // "loses the race" and does nothing.
+ apzc->ContentReceivedInputBlock(blockId, false);
+ mcc->AdvanceByMillis(1000);
+
+ // Finally, simulate lifting the finger. Since the long-press wasn't
+ // prevent-defaulted, we should get a long-tap-up event.
+ check.Call("preHandleLongTapUp");
+ status = TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+ mcc->RunThroughDelayedTasks();
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+ check.Call("postHandleLongTapUp");
+
+ apzc->AssertStateIsReset();
+ }
+
+ void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
+ MakeApzcUnzoomable();
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+
+ int touchX = 10,
+ touchStartY = 10,
+ touchEndY = 50;
+
+ uint64_t blockId = 0;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(touchX, touchStartY), mcc->Time(), &blockId);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ // SetAllowedTouchBehavior() must be called after sending touch-start.
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+ apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
+ }
+ // Have content "respond" to the touchstart
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call("preHandleLongTap"));
+ blockId++;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTap"));
+ }
+
+ // Manually invoke the longpress while the touch is currently down.
+ check.Call("preHandleLongTap");
+ mcc->RunThroughDelayedTasks();
+ check.Call("postHandleLongTap");
+
+ // There should be a TimeoutContentResponse task in the queue still,
+ // waiting for the response from the longtap event dispatched above.
+ // Send the signal that content has handled the long-tap, and then run
+ // the timeout task (it will be a no-op because the content "wins" the
+ // race. This takes the place of the "contextmenu" event.
+ apzc->ContentReceivedInputBlock(blockId, true);
+ mcc->AdvanceByMillis(1000);
+
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
+ status = apzc->ReceiveInputEvent(mti, nullptr);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
+ status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCLongPressTester, LongPress) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoLongPressTest(mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+TEST_F(APZCLongPressTester, LongPressWithTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoLongPressTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+}
+
+TEST_F(APZCLongPressTester, LongPressPreventDefault) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTap) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], false);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
+ MakeApzcWaitForMainThread();
+ MakeApzcUnzoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSecondTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], false);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], true);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], true);
+ apzc->ContentReceivedInputBlock(blockIds[1], true);
+
+ apzc->AssertStateIsReset();
+}
+
+// Test for bug 947892
+// We test whether we dispatch tap event when the tap is followed by pinch.
+TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+
+ int inputId = 0;
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+
+ int inputId = 0;
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
+ // Since we try to allow concurrent input blocks of different types to
+ // co-exist, the wheel block shouldn't interrupt the long-press detection.
+ // But more importantly, this shouldn't crash, which is what it did at one
+ // point in time.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(1);
+
+ uint64_t touchBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(apzc, touchBlockId);
+ }
+ mcc->AdvanceByMillis(10);
+ Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(touchBlockId, wheelBlockId);
+ mcc->AdvanceByMillis(1000);
+}
+
+TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
+ // In this test, even though the wheel block comes right after the tap, the
+ // tap should still be dispatched because it completes fully before the wheel
+ // block arrived.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ // We make the APZC zoomable so the gesture detector needs to wait to
+ // distinguish between tap and double-tap. During that timeout is when we
+ // insert the wheel event.
+ MakeApzcZoomable();
+
+ uint64_t touchBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100),
+ nullptr, &touchBlockId);
+ mcc->AdvanceByMillis(10);
+ Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(touchBlockId, wheelBlockId);
+ while (mcc->RunThroughDelayedTasks());
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestHitTesting.cpp b/system/graphics/layers/apz/test/gtest/TestHitTesting.cpp
new file mode 100644
index 000000000..7191adaf9
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestHitTesting.cpp
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZHitTestingTester : public APZCTreeManagerTester {
+protected:
+ ScreenToParentLayerMatrix4x4 transformToApzc;
+ ParentLayerToScreenMatrix4x4 transformToGecko;
+
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
+ if (hit) {
+ transformToApzc = manager->GetScreenToApzcTransform(hit.get());
+ transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
+ }
+ return hit.forget();
+ }
+
+protected:
+ void CreateHitTesting1LayerTree() {
+ const char* layerTreeSyntax = "c(tttt)";
+ // LayerID 0 1234
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(10,10,20,20)),
+ nsIntRegion(IntRect(10,10,20,20)),
+ nsIntRegion(IntRect(5,5,20,20)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ }
+
+ void CreateHitTesting2LayerTree() {
+ const char* layerTreeSyntax = "c(tc(t))";
+ // LayerID 0 12 3
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(10,10,40,40)),
+ nsIntRegion(IntRect(10,60,40,40)),
+ nsIntRegion(IntRect(10,60,40,40)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4::Scaling(2, 1, 1),
+ Matrix4x4(),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
+ }
+
+ void DisableApzOn(Layer* aLayer) {
+ ScrollMetadata m = aLayer->GetScrollMetadata(0);
+ m.SetForceDisableApz(true);
+ aLayer->SetScrollMetadata(m);
+ }
+
+ void CreateComplexMultiLayerTree() {
+ const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
+ // LayerID 0 12 3 45 6 7 89
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,300,400)), // root(0)
+ nsIntRegion(IntRect(0,0,100,100)), // thebes(1) in top-left
+ nsIntRegion(IntRect(50,50,200,300)), // container(2) centered in root(0)
+ nsIntRegion(IntRect(50,50,200,300)), // thebes(3) fully occupying parent container(2)
+ nsIntRegion(IntRect(0,200,100,100)), // thebes(4) in bottom-left
+ nsIntRegion(IntRect(200,0,100,400)), // container(5) along the right 100px of root(0)
+ nsIntRegion(IntRect(200,0,100,200)), // container(6) taking up the top half of parent container(5)
+ nsIntRegion(IntRect(200,0,100,200)), // thebes(7) fully occupying parent container(6)
+ nsIntRegion(IntRect(200,200,100,100)), // thebes(8) in bottom-right (below (6))
+ nsIntRegion(IntRect(200,300,100,100)), // thebes(9) in bottom-right (below (8))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3);
+ }
+
+ void CreateBug1148350LayerTree() {
+ const char* layerTreeSyntax = "c(t)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ }
+};
+
+// A simple hit testing test that doesn't involve any transforms on layers.
+TEST_F(APZHitTestingTester, HitTesting1) {
+ CreateHitTesting1LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ // No APZC attached so hit testing will return no APZC at (20,20)
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+
+ uint32_t paintSequenceNumber = 0;
+
+ // Now we have a root APZC that will match the page
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+ // expect hit point at LayerIntPoint(15, 15)
+ EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
+ EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
+
+ // Now we have a sub APZC with a better fit
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(ApzcOf(layers[3]), hit.get());
+ // expect hit point at LayerIntPoint(25, 25)
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+
+ // At this point, layers[4] obscures layers[3] at the point (15, 15) so
+ // hitting there should hit the root APZC
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+
+ // Now test hit testing when we have two scrollable layers
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(layers[4]), hit.get());
+ // expect hit point at LayerIntPoint(15, 15)
+ EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
+ EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
+
+ // Hit test ouside the reach of layer[3,4] but inside root
+ hit = GetTargetAPZC(ScreenPoint(90, 90));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+ // expect hit point at LayerIntPoint(90, 90)
+ EXPECT_EQ(ParentLayerPoint(90, 90), transformToApzc.TransformPoint(ScreenPoint(90, 90)));
+ EXPECT_EQ(ScreenPoint(90, 90), transformToGecko.TransformPoint(ParentLayerPoint(90, 90)));
+
+ // Hit test ouside the reach of any layer
+ hit = GetTargetAPZC(ScreenPoint(1000, 10));
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+ hit = GetTargetAPZC(ScreenPoint(-1000, 10));
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+}
+
+// A more involved hit testing test that involves css and async transforms.
+TEST_F(APZHitTestingTester, HitTesting2) {
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+
+ CreateHitTesting2LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ // At this point, the following holds (all coordinates in screen pixels):
+ // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
+ // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
+ // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
+ // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
+
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+ TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
+
+ // Hit an area that's clearly on the root layer but not any of the child layers.
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
+ EXPECT_EQ(apzcroot, hit.get());
+ EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc.TransformPoint(ScreenPoint(75, 25)));
+ EXPECT_EQ(ScreenPoint(75, 25), transformToGecko.TransformPoint(ParentLayerPoint(75, 25)));
+
+ // Hit an area on the root that would be on layers[3] if layers[2]
+ // weren't transformed.
+ // Note that if layers[2] were scrollable, then this would hit layers[2]
+ // because its composition bounds would be at (10,60)-(50,100) (and the
+ // scale-only transform that we set on layers[2] would be invalid because
+ // it would place the layer into overscroll, as its composition bounds
+ // start at x=10 but its content at x=20).
+ hit = GetTargetAPZC(ScreenPoint(15, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc.TransformPoint(ScreenPoint(15, 75)));
+ EXPECT_EQ(ScreenPoint(15, 75), transformToGecko.TransformPoint(ParentLayerPoint(15, 75)));
+
+ // Hit an area on layers[1].
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzc1, hit.get());
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+
+ // Hit an area on layers[3].
+ hit = GetTargetAPZC(ScreenPoint(25, 75));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc should unapply layers[2]'s transform
+ EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 75)));
+ // and transformToGecko should reapply it
+ EXPECT_EQ(ScreenPoint(25, 75), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
+
+ // Hit an area on layers[3] that would be on the root if layers[2]
+ // weren't transformed.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc should unapply layers[2]'s transform
+ EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // and transformToGecko should reapply it
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(37.5, 75)));
+
+ // Pan the root layer upward by 50 pixels.
+ // This causes layers[1] to scroll out of view, and an async transform
+ // of -50 to be set on the root layer.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+
+ // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
+ // Since this paint request is in the queue to Gecko, transformToGecko will
+ // take it into account.
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Hit where layers[3] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // and transformToGecko unapplies it and then reapplies it, because by the
+ // time the event being transformed reaches Gecko the new paint request will
+ // have been handled.
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
+
+ // Hit where layers[1] used to be and where layers[3] should now be.
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc unapplies both layers[2]'s css transform and the root's
+ // async transform
+ EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ // transformToGecko reapplies both the css transform and the async transform
+ // because we have already issued a paint request with it.
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
+
+ // This second pan will move the APZC by another 50 pixels.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Hit where layers[3] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // transformToGecko unapplies the full async transform of -100 pixels
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
+
+ // Hit where layers[1] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ // transformToGecko unapplies the full async transform of -100 pixels
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+}
+
+TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
+ CreateComplexMultiLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ /* The layer tree looks like this:
+
+ 0
+ |----|--+--|----|
+ 1 2 4 5
+ | /|\
+ 3 6 8 9
+ |
+ 7
+
+ Layers 1,2 have the same APZC
+ Layers 4,6,8 have the same APZC
+ Layer 7 has an APZC
+ Layer 9 has an APZC
+ */
+
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ // Ensure all the scrollable layers have an APZC
+ EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
+ EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
+ EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
+ // Ensure those that scroll together have the same APZCs
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+ EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
+ EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
+ // Ensure those that don't scroll together have different APZCs
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
+ EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
+ EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
+ EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
+ // Ensure the APZC parent chains are set up correctly
+ TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
+ TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
+ TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
+ EXPECT_EQ(nullptr, layers1_2->GetParent());
+ EXPECT_EQ(nullptr, layers4_6_8->GetParent());
+ EXPECT_EQ(layers4_6_8, layer7->GetParent());
+ EXPECT_EQ(nullptr, layer9->GetParent());
+ // Ensure the hit-testing tree looks like the layer tree
+ RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
+ RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
+ RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
+ RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
+ RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
+ EXPECT_EQ(nullptr, node1->GetPrevSibling());
+ EXPECT_EQ(nullptr, node3->GetPrevSibling());
+ EXPECT_EQ(nullptr, node6->GetPrevSibling());
+ EXPECT_EQ(nullptr, node7->GetPrevSibling());
+ EXPECT_EQ(nullptr, node1->GetLastChild());
+ EXPECT_EQ(nullptr, node3->GetLastChild());
+ EXPECT_EQ(nullptr, node4->GetLastChild());
+ EXPECT_EQ(nullptr, node7->GetLastChild());
+ EXPECT_EQ(nullptr, node8->GetLastChild());
+ EXPECT_EQ(nullptr, node9->GetLastChild());
+
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(ApzcOf(layers[1]), hit.get());
+ hit = GetTargetAPZC(ScreenPoint(275, 375));
+ EXPECT_EQ(ApzcOf(layers[9]), hit.get());
+ hit = GetTargetAPZC(ScreenPoint(250, 100));
+ EXPECT_EQ(ApzcOf(layers[7]), hit.get());
+}
+
+TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ // The main purpose of this test is to verify that touch-start events (or anything
+ // that starts a new input block) don't ever get untransformed. This should always
+ // hold because the APZ code should flush repaints when we start a new input block
+ // and the transform to gecko space should be empty.
+
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ // At this point, the following holds (all coordinates in screen pixels):
+ // layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-first-touch-start"));
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-second-fling"));
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-second-touch-start"));
+ }
+
+ // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Verify that a touch start doesn't get untransformed
+ ScreenIntPoint touchPoint(50, 50);
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
+
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+ check.Call("post-first-touch-start");
+
+ // Send a touchend to clear state
+ mti.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(mti, nullptr, nullptr);
+
+ mcc->AdvanceByMillis(1000);
+
+ // Now do two pans. The first of these will dispatch a repaint request, as above.
+ // The second will get stuck in the paint throttler because the first one doesn't
+ // get marked as "completed", so this will result in a non-empty LD transform.
+ // (Note that any outstanding repaint requests from the first half of this test
+ // don't impact this half because we advance the time by 1 second, which will trigger
+ // the max-wait-exceeded codepath in the paint throttler).
+ ApzcPanNoFling(apzcroot, 100, 50);
+ check.Call("post-second-fling");
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Ensure that a touch start again doesn't get untransformed by flushing
+ // a repaint
+ mti.mType = MultiTouchInput::MULTITOUCH_START;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+ check.Call("post-second-touch-start");
+
+ mti.mType = MultiTouchInput::MULTITOUCH_END;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+}
+
+TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
+ // The purpose of this test is to ensure that wheel events trigger a repaint
+ // flush as per bug 1166871, and that the wheel event untransform is a no-op.
+
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
+ ScreenPoint origin(100, 50);
+ for (int i = 0; i < 3; i++) {
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+
+ AsyncTransform viewTransform;
+ ParentLayerPoint point;
+ apzcroot->SampleContentTransformForFrame(&viewTransform, point);
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ((i + 1) * 10, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
+
+ mcc->AdvanceByMillis(5);
+ }
+}
+
+TEST_F(APZHitTestingTester, TestForceDisableApz) {
+ CreateSimpleScrollingLayer();
+ DisableApzOn(root);
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ ScreenPoint origin(100, 50);
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+
+ AsyncTransform viewTransform;
+ ParentLayerPoint point;
+ apzcroot->SampleContentTransformForFrame(&viewTransform, point);
+ // Since APZ is force-disabled, we expect to see the async transform via
+ // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ(10, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ(-10, viewTransform.mTranslation.y);
+ viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ(0, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ(0, viewTransform.mTranslation.y);
+
+ mcc->AdvanceByMillis(10);
+
+ // With untransforming events we should get normal behaviour (in this case,
+ // no noticeable untransform, because the repaint request already got
+ // flushed).
+ swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 0, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+}
+
+TEST_F(APZHitTestingTester, Bug1148350) {
+ CreateBug1148350LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped without transform"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped with interleaved transform"));
+ }
+
+ Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
+ mcc->RunThroughDelayedTasks();
+ check.Call("Tapped without transform");
+
+ uint64_t blockId;
+ TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
+ if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(manager, blockId);
+ }
+ mcc->AdvanceByMillis(100);
+
+ layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
+ layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
+ mcc->RunThroughDelayedTasks();
+ check.Call("Tapped with interleaved transform");
+}
+
+TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
+ // Create the layer tree.
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,100))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+
+ // Add root scroll metadata to the first painted layer.
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200));
+
+ // Add root and subframe scroll metadata to the second painted layer.
+ // Give the subframe metadata a scroll clip corresponding to the subframe's
+ // composition bounds.
+ // Importantly, give the layer a layer clip which leaks outside of the
+ // subframe's composition bounds.
+ ScrollMetadata rootMetadata = BuildScrollMetadata(
+ FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200),
+ ParentLayerRect(0,0,200,200));
+ ScrollMetadata subframeMetadata = BuildScrollMetadata(
+ FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200),
+ ParentLayerRect(0,0,200,100));
+ subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100))));
+ layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
+ layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200)));
+ SetEventRegionsBasedOnBottommostMetrics(layers[2]);
+
+ // Build the hit testing tree.
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ // Pan on a region that's inside layers[2]'s layer clip, but outside
+ // its subframe metadata's scroll clip.
+ Pan(manager, 120, 110);
+
+ // Test that the subframe hasn't scrolled.
+ EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset());
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestInputQueue.cpp b/system/graphics/layers/apz/test/gtest/TestInputQueue.cpp
new file mode 100644
index 000000000..07877d8bd
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestInputQueue.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+// Test of scenario described in bug 1269067 - that a continuing mouse drag
+// doesn't interrupt a wheel scrolling animation
+TEST_F(APZCTreeManagerTester, WheelInterruptedByMouseDrag) {
+ // Set up a scrollable layer
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
+
+ uint64_t dragBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ uint64_t tmpBlockId = 0;
+
+ // First start the mouse drag
+ MouseDown(apzc, ScreenIntPoint(5, 5), mcc->Time(), &dragBlockId);
+ MouseMove(apzc, ScreenIntPoint(6, 6), mcc->Time(), &tmpBlockId);
+ EXPECT_EQ(dragBlockId, tmpBlockId);
+
+ // Insert the wheel event, check that it has a new block id
+ SmoothWheel(apzc, ScreenIntPoint(6, 6), ScreenPoint(0, 1), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(dragBlockId, wheelBlockId);
+
+ // Continue the drag, check that the block id is the same as before
+ MouseMove(apzc, ScreenIntPoint(7, 5), mcc->Time(), &tmpBlockId);
+ EXPECT_EQ(dragBlockId, tmpBlockId);
+
+ // Finish the wheel animation
+ apzc->AdvanceAnimationsUntilEnd();
+
+ // Check that it scrolled
+ ParentLayerPoint scroll = apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
+ EXPECT_EQ(scroll.x, 0);
+ EXPECT_EQ(scroll.y, 10); // We scrolled 1 "line" or 10 pixels
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestPanning.cpp b/system/graphics/layers/apz/test/gtest/TestPanning.cpp
new file mode 100644
index 000000000..d3a835267
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestPanning.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCPanningTester : public APZCBasicTester {
+protected:
+ void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
+ {
+ if (aShouldTriggerScroll) {
+ // One repaint request for each pan.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
+ } else {
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+ }
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+
+ // Pan down
+ PanAndCheckStatus(apzc, touchStart, touchEnd, aShouldBeConsumed, &allowedTouchBehaviors);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ if (aShouldTriggerScroll) {
+ EXPECT_EQ(ParentLayerPoint(0, -(touchEnd-touchStart)), pointOut);
+ EXPECT_NE(AsyncTransform(), viewTransformOut);
+ } else {
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+ }
+
+ // Clear the fling from the previous pan, or stopping it will
+ // consume the next touchstart
+ apzc->CancelAnimation();
+
+ // Pan back
+ PanAndCheckStatus(apzc, touchEnd, touchStart, aShouldBeConsumed, &allowedTouchBehaviors);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+ }
+
+ void DoPanWithPreventDefaultTest()
+ {
+ MakeApzcWaitForMainThread();
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ uint64_t blockId = 0;
+
+ // Pan down
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+ PanAndCheckStatus(apzc, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
+
+ // Send the signal that content has handled and preventDefaulted the touch
+ // events. This flushes the event queue.
+ apzc->ContentReceivedInputBlock(blockId, true);
+
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCPanningTester, Pan) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
+// to bottom and back - from bottom to top.
+// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
+// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
+// behavior.
+// However, the events will be marked as consumed even if the behavior in PAN_X, because the user could
+// move their finger horizontally too - APZ has no way of knowing beforehand and so must consume the
+// events.
+TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionNone) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(false, false, 0);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoPanWithPreventDefaultTest();
+}
+
+TEST_F(APZCPanningTester, PanWithPreventDefault) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPanWithPreventDefaultTest();
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestPinching.cpp b/system/graphics/layers/apz/test/gtest/TestPinching.cpp
new file mode 100644
index 000000000..936e30873
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestPinching.cpp
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCPinchTester : public APZCBasicTester {
+public:
+ explicit APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
+ : APZCBasicTester(aGestureBehavior)
+ {
+ }
+
+protected:
+ FrameMetrics GetPinchableFrameMetrics()
+ {
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
+ fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
+ fm.SetScrollOffset(CSSPoint(300, 300));
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ // APZC only allows zooming on the root scrollable frame.
+ fm.SetIsRootContent(true);
+ // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
+ return fm;
+ }
+
+ void DoPinchTest(bool aShouldTriggerPinch,
+ nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
+ {
+ apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+ MakeApzcZoomable();
+
+ if (aShouldTriggerPinch) {
+ // One repaint request for each gesture.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
+ } else {
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+ }
+
+ int touchInputId = 0;
+ if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
+ PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+ touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+ } else {
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+ aShouldTriggerPinch);
+ }
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+
+ if (aShouldTriggerPinch) {
+ // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
+ EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(305, fm.GetScrollOffset().x);
+ EXPECT_EQ(310, fm.GetScrollOffset().y);
+ } else {
+ // The frame metrics should stay the same since touch-action:none makes
+ // apzc ignore pinch gestures.
+ EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(300, fm.GetScrollOffset().x);
+ EXPECT_EQ(300, fm.GetScrollOffset().y);
+ }
+
+ // part 2 of the test, move to the top-right corner of the page and pinch and
+ // make sure we stay in the correct spot
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ fm.SetScrollOffset(CSSPoint(930, 5));
+ apzc->SetFrameMetrics(fm);
+ // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
+
+ if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
+ PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+ touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+ } else {
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+ aShouldTriggerPinch);
+ }
+
+ fm = apzc->GetFrameMetrics();
+
+ if (aShouldTriggerPinch) {
+ // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
+ EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(880, fm.GetScrollOffset().x);
+ EXPECT_EQ(0, fm.GetScrollOffset().y);
+ } else {
+ EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(930, fm.GetScrollOffset().x);
+ EXPECT_EQ(5, fm.GetScrollOffset().y);
+ }
+ }
+};
+
+class APZCPinchGestureDetectorTester : public APZCPinchTester {
+public:
+ APZCPinchGestureDetectorTester()
+ : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
+ {
+ }
+
+ void DoPinchWithPreventDefaultTest() {
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ int touchInputId = 0;
+ uint64_t blockId = 0;
+ PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
+ nullptr, nullptr, &blockId);
+
+ // Send the prevent-default notification for the touch block
+ apzc->ContentReceivedInputBlock(blockId, true);
+
+ // verify the metrics didn't change (i.e. the pinch was ignored)
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPinchTest(true);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPinchTest(true);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
+ mozilla::layers::AllowedTouchBehavior::NONE };
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors;
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ DoPinchTest(true, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors;
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ // Since we are preventing the pinch action via touch-action we should not be
+ // sending the pinch gesture notifications that would normally be sent when
+ // APZAllowZooming is false.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
+ nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
+ mozilla::layers::AllowedTouchBehavior::NONE };
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
+ DoPinchWithPreventDefaultTest();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ // Since we are preventing the pinch action we should not be sending the pinch
+ // gesture notifications that would normally be sent when APZAllowZooming is
+ // false.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
+
+ DoPinchWithPreventDefaultTest();
+}
+
+TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
+ // set up APZ
+ apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+ MakeApzcUnzoomable();
+
+ nsEventStatus statuses[3]; // scalebegin, scale, scaleend
+ PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
+ 10, &statuses);
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+
+ // It starts from (300, 300), then moves the focus point from (250, 350) to
+ // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
+ // causes the scroll offset to change by half of that (25, 25) pixels.
+ EXPECT_EQ(325, fm.GetScrollOffset().x);
+ EXPECT_EQ(325, fm.GetScrollOffset().y);
+ EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // When APZAllowZooming is false, the ZoomConstraintsClient produces
+ // ZoomConstraints with mAllowZoom set to false.
+ MakeApzcUnzoomable();
+
+ // With APZAllowZooming false, we expect the NotifyPinchGesture function to
+ // get called as the pinch progresses, but the metrics shouldn't change.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+
+ int touchInputId = 0;
+ uint64_t blockId = 0;
+ PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
+ nullptr, nullptr, &blockId);
+
+ // verify the metrics didn't change (i.e. the pinch was ignored inside APZ)
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // When APZAllowZooming is false, the ZoomConstraintsClient produces
+ // ZoomConstraints with mAllowZoom set to false.
+ MakeApzcUnzoomable();
+
+ // With APZAllowZooming false, we expect the NotifyPinchGesture function to
+ // get called as the pinch progresses, but the metrics shouldn't change.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+
+ int inputId = 0;
+ ScreenIntPoint focus(250, 300);
+
+ // Do a pinch holding a zero span and moving the focus by y=100
+
+ MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiStart, nullptr);
+
+ focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in GestureEventListener.cpp
+ MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiMove1, nullptr);
+
+ focus.y -= 100; // do a two-finger scroll of 100 screen pixels
+ MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiMove2, nullptr);
+
+ MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiEnd, nullptr);
+
+ // Done, check the metrics to make sure we scrolled by 100 screen pixels,
+ // which is 50 CSS pixels for the pinchable frame metrics.
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y + 50, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestScrollHandoff.cpp b/system/graphics/layers/apz/test/gtest/TestScrollHandoff.cpp
new file mode 100644
index 000000000..88a54276a
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestScrollHandoff.cpp
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZScrollHandoffTester : public APZCTreeManagerTester {
+protected:
+ UniquePtr<ScopedLayerTreeRegistration> registration;
+ TestAsyncPanZoomController* rootApzc;
+
+ void CreateScrollHandoffLayerTree1() {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 50))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[1], root);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ rootApzc->GetFrameMetrics().SetIsRootContent(true); // make root APZC zoomable
+ }
+
+ void CreateScrollHandoffLayerTree2() {
+ const char* layerTreeSyntax = "c(c(t))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 50))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[1], root);
+ SetScrollHandoff(layers[2], layers[1]);
+ // No ScopedLayerTreeRegistration as that just needs to be done once per test
+ // and this is the second layer tree for a particular test.
+ MOZ_ASSERT(registration);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateScrollHandoffLayerTree3() {
+ const char* layerTreeSyntax = "c(c(t)c(t))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)), // root
+ nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling parent 1
+ nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling child 1
+ nsIntRegion(IntRect(0, 50, 100, 50)), // scrolling parent 2
+ nsIntRegion(IntRect(0, 50, 100, 50)) // scrolling child 2
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 4, CSSRect(0, 50, 100, 100));
+ SetScrollHandoff(layers[1], layers[0]);
+ SetScrollHandoff(layers[3], layers[0]);
+ SetScrollHandoff(layers[2], layers[1]);
+ SetScrollHandoff(layers[4], layers[3]);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+
+ void CreateScrollgrabLayerTree(bool makeParentScrollable = true) {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)), // scroll-grabbing parent
+ nsIntRegion(IntRect(0, 20, 100, 80)) // child
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ float parentHeight = makeParentScrollable ? 120 : 100;
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
+ SetScrollHandoff(layers[1], root);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ rootApzc->GetScrollMetadata().SetHasScrollgrab(true);
+ }
+
+ void TestFlingAcceleration() {
+ // Jack up the fling acceleration multiplier so we can easily determine
+ // whether acceleration occured.
+ const float kAcceleration = 100.0f;
+ SCOPED_GFX_PREF(APZFlingAccelBaseMultiplier, float, kAcceleration);
+ SCOPED_GFX_PREF(APZFlingAccelMinVelocity, float, 0.0);
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan once, enough to fully scroll the scrollgrab parent and then scroll
+ // and fling the child.
+ Pan(manager, 70, 40);
+
+ // Give the fling animation a chance to start.
+ SampleAnimationsOnce();
+
+ float childVelocityAfterFling1 = childApzc->GetVelocityVector().y;
+
+ // Pan again.
+ Pan(manager, 70, 40);
+
+ // Give the fling animation a chance to start.
+ // This time it should be accelerated.
+ SampleAnimationsOnce();
+
+ float childVelocityAfterFling2 = childApzc->GetVelocityVector().y;
+
+ // We should have accelerated once.
+ // The division by 2 is to account for friction.
+ EXPECT_GT(childVelocityAfterFling2,
+ childVelocityAfterFling1 * kAcceleration / 2);
+
+ // We should not have accelerated twice.
+ // The division by 4 is to account for friction.
+ EXPECT_LE(childVelocityAfterFling2,
+ childVelocityAfterFling1 * kAcceleration * kAcceleration / 4);
+ }
+};
+
+// Here we test that if the processing of a touch block is deferred while we
+// wait for content to send a prevent-default message, overscroll is still
+// handed off correctly when the block is processed.
+TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) {
+ // Set up the APZC tree.
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
+
+ // Enable touch-listeners so that we can separate the queueing of input
+ // events from them being processed.
+ childApzc->SetWaitForMainThread();
+
+ // Queue input events for a pan.
+ uint64_t blockId = 0;
+ ApzcPanNoFling(childApzc, 90, 30, &blockId);
+
+ // Allow the pan to be processed.
+ childApzc->ContentReceivedInputBlock(blockId, false);
+ childApzc->ConfirmTarget(blockId);
+
+ // Make sure overscroll was handed off correctly.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+// Here we test that if the layer structure changes in between two input
+// blocks being queued, and the first block is only processed after the second
+// one has been queued, overscroll handoff for the first block follows
+// the original layer structure while overscroll handoff for the second block
+// follows the new layer structure.
+TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
+ // Set up an initial APZC tree.
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
+
+ // Enable touch-listeners so that we can separate the queueing of input
+ // events from them being processed.
+ childApzc->SetWaitForMainThread();
+
+ // Queue input events for a pan.
+ uint64_t blockId = 0;
+ ApzcPanNoFling(childApzc, 90, 30, &blockId);
+
+ // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
+ // between the child and the root.
+ CreateScrollHandoffLayerTree2();
+ RefPtr<Layer> middle = layers[1];
+ childApzc->SetWaitForMainThread();
+ TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
+
+ // Queue input events for another pan.
+ uint64_t secondBlockId = 0;
+ ApzcPanNoFling(childApzc, 30, 90, &secondBlockId);
+
+ // Allow the first pan to be processed.
+ childApzc->ContentReceivedInputBlock(blockId, false);
+ childApzc->ConfirmTarget(blockId);
+
+ // Make sure things have scrolled according to the handoff chain in
+ // place at the time the touch-start of the first pan was queued.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Allow the second pan to be processed.
+ childApzc->ContentReceivedInputBlock(secondBlockId, false);
+ childApzc->ConfirmTarget(secondBlockId);
+
+ // Make sure things have scrolled according to the handoff chain in
+ // place at the time the touch-start of the second pan was queued.
+ EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+// Test that putting a second finger down on an APZC while a down-chain APZC
+// is overscrolled doesn't result in being stuck in overscroll.
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 10, 40, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+// This is almost exactly like StuckInOverscroll_Bug1073250, except the
+// APZC receiving the input events for the first touch block is the child
+// (and thus not the same APZC that overscrolls, which is the parent).
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Lift the finger, triggering an overscroll animation
+ // (but don't allow it to run).
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put the finger down again, interrupting the animation
+ // and entering the TOUCHING state.
+ TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Lift the finger once again.
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Lift the finger, triggering an overscroll animation
+ // (but don't allow it to run).
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put the finger down again, interrupting the animation
+ // and entering the TOUCHING state.
+ TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put a second finger down. Since we're in the TOUCHING state,
+ // the "are we panned into overscroll" check will fail and we
+ // will not ignore the second finger, instead entering the
+ // PINCHING state.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as TouchDown(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+// Test that flinging in a direction where one component of the fling goes into
+// overscroll but the other doesn't, results in just the one component being
+// handed off to the parent, while the original APZC continues flinging in the
+// other direction.
+TEST_F(APZScrollHandoffTester, PartialFlingHandoff) {
+ CreateScrollHandoffLayerTree1();
+
+ // Fling up and to the left. The child APZC has room to scroll up, but not
+ // to the left, so the horizontal component of the fling should be handed
+ // off to the parent APZC.
+ Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55));
+
+ RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]);
+
+ // Advance the child's fling animation once to give the partial handoff
+ // a chance to occur.
+ mcc->AdvanceByMillis(10);
+ child->AdvanceAnimations(mcc->Time());
+
+ // Assert that partial handoff has occurred.
+ child->AssertStateIsFling();
+ parent->AssertStateIsFling();
+}
+
+// Here we test that if two flings are happening simultaneously, overscroll
+// is handed off correctly for each.
+TEST_F(APZScrollHandoffTester, SimultaneousFlings) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ // Set up an initial APZC tree.
+ CreateScrollHandoffLayerTree3();
+
+ RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]);
+ RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]);
+ RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]);
+ RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]);
+
+ // Pan on the lower child.
+ Pan(child2, 45, 5);
+
+ // Pan on the upper child.
+ Pan(child1, 95, 55);
+
+ // Check that child1 and child2 are in a FLING state.
+ child1->AssertStateIsFling();
+ child2->AssertStateIsFling();
+
+ // Advance the animations on child1 and child2 until their end.
+ child1->AdvanceAnimationsUntilEnd();
+ child2->AdvanceAnimationsUntilEnd();
+
+ // Check that the flings have been handed off to the parents.
+ child1->AssertStateIsReset();
+ parent1->AssertStateIsFling();
+ child2->AssertStateIsReset();
+ parent2->AssertStateIsFling();
+}
+
+TEST_F(APZScrollHandoffTester, Scrollgrab) {
+ // Set up the layer tree
+ CreateScrollgrabLayerTree();
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to fully scroll the scrollgrab parent (20 px)
+ // and leave some more (another 15 px) for the child.
+ Pan(childApzc, 80, 45);
+
+ // Check that the parent and child have scrolled as much as we expect.
+ EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFling) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ // Set up the layer tree
+ CreateScrollgrabLayerTree();
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, not enough to fully scroll the scrollgrab parent.
+ Pan(childApzc, 80, 70);
+
+ // Check that it is the scrollgrab parent that's in a fling, not the child.
+ rootApzc->AssertStateIsFling();
+ childApzc->AssertStateIsReset();
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ CreateScrollgrabLayerTree(true /* make parent scrollable */);
+ TestFlingAcceleration();
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ CreateScrollgrabLayerTree(false /* do not make parent scrollable */);
+ TestFlingAcceleration();
+}
+
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
+ SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
+
+ CreateScrollHandoffLayerTree1();
+
+ RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to scroll it to its end and have scroll
+ // left to hand off. Since immediate handoff is disallowed, we expect
+ // the leftover scroll not to be handed off.
+ Pan(childApzc, 60, 5);
+
+ // Verify that the parent has not scrolled.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Pan again on the child. This time, since the child was scrolled to
+ // its end when the gesture began, we expect the scroll to be handed off.
+ Pan(childApzc, 60, 50);
+
+ // Verify that the parent scrolled.
+ EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
+ SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to get very close to the end, so that the
+ // subsequent fling reaches the end and has leftover velocity to hand off.
+ Pan(childApzc, 60, 12);
+
+ // Allow the fling to run its course.
+ childApzc->AdvanceAnimationsUntilEnd();
+ parentApzc->AdvanceAnimationsUntilEnd();
+
+ // Verify that the parent has not scrolled.
+ // The first comparison needs to be an ASSERT_NEAR because the fling
+ // computations are such that the final scroll position can be within
+ // COORDINATE_EPSILON of the end rather than right at the end.
+ ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetScrollOffset().y, COORDINATE_EPSILON);
+ EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Pan again on the child. This time, since the child was scrolled to
+ // its end when the gesture began, we expect the scroll to be handed off.
+ Pan(childApzc, 60, 50);
+
+ // Allow the fling to run its course. The fling should also be handed off.
+ childApzc->AdvanceAnimationsUntilEnd();
+ parentApzc->AdvanceAnimationsUntilEnd();
+
+ // Verify that the parent scrolled from the fling.
+ EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10);
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestSnapping.cpp b/system/graphics/layers/apz/test/gtest/TestSnapping.cpp
new file mode 100644
index 000000000..a7fb2e77d
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestSnapping.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCSnappingTester : public APZCTreeManagerTester
+{
+};
+
+TEST_F(APZCSnappingTester, Bug1265510)
+{
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 100, 100, 100))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
+ SetScrollHandoff(layers[1], root);
+
+ ScrollSnapInfo snap;
+ snap.mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY;
+ snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel());
+
+ ScrollMetadata metadata = root->GetScrollMetadata(0);
+ metadata.SetSnapInfo(ScrollSnapInfo(snap));
+ root->SetScrollMetadata(metadata);
+
+ UniquePtr<ScopedLayerTreeRegistration> registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* outer = ApzcOf(layers[0]);
+ TestAsyncPanZoomController* inner = ApzcOf(layers[1]);
+
+ // Position the mouse near the bottom of the outer frame and scroll by 60px.
+ // (6 lines of 10px each). APZC will actually scroll to y=100 because of the
+ // mandatory snap coordinate there.
+ TimeStamp now = mcc->Time();
+ SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), now);
+ // Advance in 5ms increments until we've scrolled by 70px. At this point, the
+ // closest snap point is y=100, and the inner frame should be under the mouse
+ // cursor.
+ while (outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y < 70) {
+ mcc->AdvanceByMillis(5);
+ outer->AdvanceAnimations(mcc->Time());
+ }
+ // Now do another wheel in a new transaction. This should start scrolling the
+ // inner frame; we verify that it does by checking the inner scroll position.
+ TimeStamp newTransactionTime = now + TimeDuration::FromMilliseconds(gfxPrefs::MouseWheelTransactionTimeoutMs() + 100);
+ SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), newTransactionTime);
+ inner->AdvanceAnimationsUntilEnd();
+ EXPECT_LT(0.0f, inner->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
+
+ // However, the outer frame should also continue to the snap point, otherwise
+ // it is demonstrating incorrect behaviour by violating the mandatory snapping.
+ outer->AdvanceAnimationsUntilEnd();
+ EXPECT_EQ(100.0f, outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
+}
diff --git a/system/graphics/layers/apz/test/gtest/TestTreeManager.cpp b/system/graphics/layers/apz/test/gtest/TestTreeManager.cpp
new file mode 100644
index 000000000..3152cb790
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/TestTreeManager.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
+ CreateSimpleMultiLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ // both layers have the same scrollId
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ // so they should have the same APZC
+ EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+
+ // Change the scrollId of layers[1], and verify the APZC changes
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[2]));
+
+ // Change the scrollId of layers[2] to match that of layers[1], ensure we get the same
+ // APZC for both again
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+}
+
+TEST_F(APZCTreeManagerTester, Bug1068268) {
+ CreatePotentiallyLeakingTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
+ RefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
+ RefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
+
+ EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
+ EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
+
+ EXPECT_EQ(node2->GetFirstChild(), node2->GetLastChild());
+ EXPECT_EQ(ApzcOf(layers[3]), node2->GetLastChild()->GetApzc());
+ EXPECT_EQ(node5->GetFirstChild(), node5->GetLastChild());
+ EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
+ EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
+}
+
+TEST_F(APZCTreeManagerTester, Bug1194876) {
+ CreateBug1194876Tree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ uint64_t blockId;
+ nsTArray<ScrollableLayerGuid> targets;
+
+ // First touch goes down, APZCTM will hit layers[1] because it is on top of
+ // layers[0], but we tell it the real target APZC is layers[0].
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(25, 50), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(mti, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, false);
+ targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+ manager->SetTargetAPZC(blockId, targets);
+
+ // Around here, the above touch will get processed by ApzcOf(layers[0])
+
+ // Second touch goes down (first touch remains down), APZCTM will again hit
+ // layers[1]. Again we tell it both touches landed on layers[0], but because
+ // layers[1] is the RCD layer, it will end up being the multitouch target.
+ mti.mTouches.AppendElement(SingleTouchData(1, ParentLayerPoint(75, 50), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(mti, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, false);
+ targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+ manager->SetTargetAPZC(blockId, targets);
+
+ // Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
+ // We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
+ // otherwise it will do things like dispatch spurious long-tap events.
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
+}
+
+TEST_F(APZCTreeManagerTester, Bug1198900) {
+ // This is just a test that cancels a wheel event to make sure it doesn't
+ // crash.
+ CreateSimpleDTCScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ ScreenPoint origin(100, 50);
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ uint64_t blockId;
+ manager->ReceiveInputEvent(swi, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, /* preventDefault= */ true);
+}
+
diff --git a/system/graphics/layers/apz/test/gtest/moz.build b/system/graphics/layers/apz/test/gtest/moz.build
new file mode 100644
index 000000000..634c47cab
--- /dev/null
+++ b/system/graphics/layers/apz/test/gtest/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'TestBasic.cpp',
+ 'TestEventRegions.cpp',
+ 'TestGestureDetector.cpp',
+ 'TestHitTesting.cpp',
+ 'TestInputQueue.cpp',
+ 'TestPanning.cpp',
+ 'TestPinching.cpp',
+ 'TestScrollHandoff.cpp',
+ 'TestSnapping.cpp',
+ 'TestTreeManager.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/gfx/2d',
+ '/gfx/layers',
+ '/gfx/tests/gtest' # for TestLayers.h, which is shared with the gfx gtests
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/system/graphics/layers/apz/test/mochitest/apz_test_native_event_utils.js b/system/graphics/layers/apz/test/mochitest/apz_test_native_event_utils.js
new file mode 100644
index 000000000..7f820a936
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/apz_test_native_event_utils.js
@@ -0,0 +1,261 @@
+// Utilities for synthesizing of native events.
+
+function getPlatform() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ return "windows";
+ }
+ if (navigator.platform.indexOf("Mac") == 0) {
+ return "mac";
+ }
+ // Check for Android before Linux
+ if (navigator.appVersion.indexOf("Android") >= 0) {
+ return "android"
+ }
+ if (navigator.platform.indexOf("Linux") == 0) {
+ return "linux";
+ }
+ return "unknown";
+}
+
+function nativeVerticalWheelEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 0x020A; // WM_MOUSEWHEEL
+ case "mac": return 0; // value is unused, can be anything
+ case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
+ }
+ throw "Native wheel events not supported on platform " + getPlatform();
+}
+
+function nativeHorizontalWheelEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 0x020E; // WM_MOUSEHWHEEL
+ case "mac": return 0; // value is unused, can be anything
+ case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
+ }
+ throw "Native wheel events not supported on platform " + getPlatform();
+}
+
+// Given a pixel scrolling delta, converts it to the platform's native units.
+function nativeScrollUnits(aElement, aDimen) {
+ switch (getPlatform()) {
+ case "linux": {
+ // GTK deltas are treated as line height divided by 3 by gecko.
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"];
+ return aDimen / (parseInt(lineHeight) * 3);
+ }
+ }
+ return aDimen;
+}
+
+function nativeMouseDownEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 2; // MOUSEEVENTF_LEFTDOWN
+ case "mac": return 1; // NSLeftMouseDown
+ case "linux": return 4; // GDK_BUTTON_PRESS
+ case "android": return 5; // ACTION_POINTER_DOWN
+ }
+ throw "Native mouse-down events not supported on platform " + getPlatform();
+}
+
+function nativeMouseMoveEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 1; // MOUSEEVENTF_MOVE
+ case "mac": return 5; // NSMouseMoved
+ case "linux": return 3; // GDK_MOTION_NOTIFY
+ case "android": return 7; // ACTION_HOVER_MOVE
+ }
+ throw "Native mouse-move events not supported on platform " + getPlatform();
+}
+
+function nativeMouseUpEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 4; // MOUSEEVENTF_LEFTUP
+ case "mac": return 2; // NSLeftMouseUp
+ case "linux": return 7; // GDK_BUTTON_RELEASE
+ case "android": return 6; // ACTION_POINTER_UP
+ }
+ throw "Native mouse-up events not supported on platform " + getPlatform();
+}
+
+// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
+// to device pixels relative to the screen.
+function coordinatesRelativeToScreen(aX, aY, aElement) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var scale = targetWindow.devicePixelRatio;
+ var rect = aElement.getBoundingClientRect();
+ return {
+ x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale,
+ y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale
+ };
+}
+
+// Get the bounding box of aElement, and return it in device pixels
+// relative to the screen.
+function rectRelativeToScreen(aElement) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var scale = targetWindow.devicePixelRatio;
+ var rect = aElement.getBoundingClientRect();
+ return {
+ x: (targetWindow.mozInnerScreenX + rect.left) * scale,
+ y: (targetWindow.mozInnerScreenY + rect.top) * scale,
+ w: (rect.width * scale),
+ h: (rect.height * scale)
+ };
+}
+
+// Synthesizes a native mousewheel event and returns immediately. This does not
+// guarantee anything; you probably want to use one of the other functions below
+// which actually wait for results.
+// aX and aY are relative to the top-left of |aElement|'s containing window.
+// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined
+// if not needed.
+function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ if (aDeltaX && aDeltaY) {
+ throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
+ }
+ aDeltaX = nativeScrollUnits(aElement, aDeltaX);
+ aDeltaY = nativeScrollUnits(aElement, aDeltaY);
+ var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
+ return true;
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// request has been successfully made to the OS. This does not necessarily
+// guarantee that the OS generates the event we requested. See
+// synthesizeNativeWheel for details on the parameters.
+function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aCallback && aTopic == "mousescrollevent") {
+ setTimeout(aCallback, 0);
+ }
+ }
+ };
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer);
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// wheel event is dispatched to |aElement|'s containing window. If the event
+// targets content in a subdocument, |aElement| should be inside the
+// subdocument. See synthesizeNativeWheel for details on the other parameters.
+function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ targetWindow.addEventListener("wheel", function wheelWaiter(e) {
+ targetWindow.removeEventListener("wheel", wheelWaiter);
+ setTimeout(aCallback, 0);
+ });
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// first resulting scroll event is dispatched to |aElement|'s containing window.
+// If the event targets content in a subdocument, |aElement| should be inside
+// the subdocument. See synthesizeNativeWheel for details on the other
+// parameters.
+function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var useCapture = true; // scroll events don't always bubble
+ targetWindow.addEventListener("scroll", function scrollWaiter(e) {
+ targetWindow.removeEventListener("scroll", scrollWaiter, useCapture);
+ setTimeout(aCallback, 0);
+ }, useCapture);
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
+}
+
+// Synthesizes a native mouse move event and returns immediately.
+// aX and aY are relative to the top-left of |aElement|'s containing window.
+function synthesizeNativeMouseMove(aElement, aX, aY) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
+ return true;
+}
+
+// Synthesizes a native mouse move event and invokes the callback once the
+// mouse move event is dispatched to |aElement|'s containing window. If the event
+// targets content in a subdocument, |aElement| should be inside the
+// subdocument. See synthesizeNativeMouseMove for details on the other
+// parameters.
+function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
+ targetWindow.removeEventListener("mousemove", mousemoveWaiter);
+ setTimeout(aCallback, 0);
+ });
+ return synthesizeNativeMouseMove(aElement, aX, aY);
+}
+
+// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
+// relative to the top-left of |aElement|'s bounding rect.
+function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
+ return true;
+}
+
+// A handy constant when synthesizing native touch drag events with the pref
+// "apz.touch_start_tolerance" set to 0. In this case, the first touchmove with
+// a nonzero pixel movement is consumed by the APZ to transition from the
+// "touching" state to the "panning" state, so calls to synthesizeNativeTouchDrag
+// should add an extra pixel pixel for this purpose. The TOUCH_SLOP provides
+// a constant that can be used for this purpose. Note that if the touch start
+// tolerance is set to something higher, the touch slop amount used must be
+// correspondingly increased so as to be higher than the tolerance.
+const TOUCH_SLOP = 1;
+function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
+ synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY));
+ for (var i = 1; i < steps; i++) {
+ var dx = i * (aDeltaX / steps);
+ var dy = i * (aDeltaY / steps);
+ synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ }
+ synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId);
+}
+
+function synthesizeNativeTap(aElement, aX, aY, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
+ return true;
+}
+
+function synthesizeNativeMouseEvent(aElement, aX, aY, aType, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, aType, 0, aElement, aObserver);
+ return true;
+}
+
+function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseDownEventMsg(), 0, aElement, function() {
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseUpEventMsg(), 0, aElement, aObserver);
+ });
+ return true;
+}
+
+// Move the mouse to (dx, dy) relative to |element|, and scroll the wheel
+// at that location.
+// Moving the mouse is necessary to avoid wheel events from two consecutive
+// moveMouseAndScrollWheelOver() calls on different elements being incorrectly
+// considered as part of the same wheel transaction.
+// We also wait for the mouse move event to be processed before sending the
+// wheel event, otherwise there is a chance they might get reordered, and
+// we have the transaction problem again.
+function moveMouseAndScrollWheelOver(element, dx, dy, testDriver, waitForScroll = true) {
+ return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
+ if (waitForScroll) {
+ synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
+ } else {
+ synthesizeNativeWheelAndWaitForWheelEvent(element, dx, dy, 0, -10, testDriver);
+ }
+ });
+}
diff --git a/system/graphics/layers/apz/test/mochitest/apz_test_utils.js b/system/graphics/layers/apz/test/mochitest/apz_test_utils.js
new file mode 100644
index 000000000..c97738434
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/apz_test_utils.js
@@ -0,0 +1,403 @@
+// Utilities for writing APZ tests using the framework added in bug 961289
+
+// ----------------------------------------------------------------------
+// Functions that convert the APZ test data into a more usable form.
+// Every place we have a WebIDL sequence whose elements are dictionaries
+// with two elements, a key, and a value, we convert this into a JS
+// object with a property for each key/value pair. (This is the structure
+// we really want, but we can't express in directly in WebIDL.)
+// ----------------------------------------------------------------------
+
+function convertEntries(entries) {
+ var result = {};
+ for (var i = 0; i < entries.length; ++i) {
+ result[entries[i].key] = entries[i].value;
+ }
+ return result;
+}
+
+function getPropertyAsRect(scrollFrames, scrollId, prop) {
+ SimpleTest.ok(scrollId in scrollFrames,
+ 'expected scroll frame data for scroll id ' + scrollId);
+ var scrollFrameData = scrollFrames[scrollId];
+ SimpleTest.ok('displayport' in scrollFrameData,
+ 'expected a ' + prop + ' for scroll id ' + scrollId);
+ var value = scrollFrameData[prop];
+ var pieces = value.replace(/[()\s]+/g, '').split(',');
+ SimpleTest.is(pieces.length, 4, "expected string of form (x,y,w,h)");
+ return { x: parseInt(pieces[0]),
+ y: parseInt(pieces[1]),
+ w: parseInt(pieces[2]),
+ h: parseInt(pieces[3]) };
+}
+
+function convertScrollFrameData(scrollFrames) {
+ var result = {};
+ for (var i = 0; i < scrollFrames.length; ++i) {
+ result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
+ }
+ return result;
+}
+
+function convertBuckets(buckets) {
+ var result = {};
+ for (var i = 0; i < buckets.length; ++i) {
+ result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
+ }
+ return result;
+}
+
+function convertTestData(testData) {
+ var result = {};
+ result.paints = convertBuckets(testData.paints);
+ result.repaintRequests = convertBuckets(testData.repaintRequests);
+ return result;
+}
+
+// Given APZ test data for a single paint on the compositor side,
+// reconstruct the APZC tree structure from the 'parentScrollId'
+// entries that were logged. More specifically, the subset of the
+// APZC tree structure corresponding to the layer subtree for the
+// content process that triggered the paint, is reconstructed (as
+// the APZ test data only contains information abot this subtree).
+function buildApzcTree(paint) {
+ // The APZC tree can potentially have multiple root nodes,
+ // so we invent a node that is the parent of all roots.
+ // This 'root' does not correspond to an APZC.
+ var root = {scrollId: -1, children: []};
+ for (var scrollId in paint) {
+ paint[scrollId].children = [];
+ paint[scrollId].scrollId = scrollId;
+ }
+ for (var scrollId in paint) {
+ var parentNode = null;
+ if ("hasNoParentWithSameLayersId" in paint[scrollId]) {
+ parentNode = root;
+ } else if ("parentScrollId" in paint[scrollId]) {
+ parentNode = paint[paint[scrollId].parentScrollId];
+ }
+ parentNode.children.push(paint[scrollId]);
+ }
+ return root;
+}
+
+// Given an APZC tree produced by buildApzcTree, return the RCD node in
+// the tree, or null if there was none.
+function findRcdNode(apzcTree) {
+ if (!!apzcTree.isRootContent) { // isRootContent will be undefined or "1"
+ return apzcTree;
+ }
+ for (var i = 0; i < apzcTree.children.length; i++) {
+ var rcd = findRcdNode(apzcTree.children[i]);
+ if (rcd != null) {
+ return rcd;
+ }
+ }
+ return null;
+}
+
+// Return whether an element whose id includes |elementId| has been layerized.
+// Assumes |elementId| will be present in the content description for the
+// element, and not in the content descriptions of other elements.
+function isLayerized(elementId) {
+ var contentTestData = SpecialPowers.getDOMWindowUtils(window).getContentAPZTestData();
+ ok(contentTestData.paints.length > 0, "expected at least one paint");
+ var seqno = contentTestData.paints[contentTestData.paints.length - 1].sequenceNumber;
+ contentTestData = convertTestData(contentTestData);
+ var paint = contentTestData.paints[seqno];
+ for (var scrollId in paint) {
+ if ("contentDescription" in paint[scrollId]) {
+ if (paint[scrollId]["contentDescription"].includes(elementId)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function flushApzRepaints(aCallback, aWindow = window) {
+ if (!aCallback) {
+ throw "A callback must be provided!";
+ }
+ var repaintDone = function() {
+ SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
+ setTimeout(aCallback, 0);
+ };
+ SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
+ if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) {
+ dump("Flushed APZ repaints, waiting for callback...\n");
+ } else {
+ dump("Flushing APZ repaints was a no-op, triggering callback directly...\n");
+ repaintDone();
+ }
+}
+
+// Flush repaints, APZ pending repaints, and any repaints resulting from that
+// flush. This is particularly useful if the test needs to reach some sort of
+// "idle" state in terms of repaints. Usually just doing waitForAllPaints
+// followed by flushApzRepaints is sufficient to flush all APZ state back to
+// the main thread, but it can leave a paint scheduled which will get triggered
+// at some later time. For tests that specifically test for painting at
+// specific times, this method is the way to go. Even if in doubt, this is the
+// preferred method as the extra step is "safe" and shouldn't interfere with
+// most tests.
+function waitForApzFlushedRepaints(aCallback) {
+ // First flush the main-thread paints and send transactions to the APZ
+ waitForAllPaints(function() {
+ // Then flush the APZ to make sure any repaint requests have been sent
+ // back to the main thread
+ flushApzRepaints(function() {
+ // Then flush the main-thread again to process the repaint requests.
+ // Once this is done, we should be in a stable state with nothing
+ // pending, so we can trigger the callback.
+ waitForAllPaints(aCallback);
+ });
+ });
+}
+
+// This function takes a set of subtests to run one at a time in new top-level
+// windows, and returns a Promise that is resolved once all the subtests are
+// done running.
+//
+// The aSubtests array is an array of objects with the following keys:
+// file: required, the filename of the subtest.
+// prefs: optional, an array of arrays containing key-value prefs to set.
+// dp_suppression: optional, a boolean on whether or not to respect displayport
+// suppression during the test.
+// onload: optional, a function that will be registered as a load event listener
+// for the child window that will hold the subtest. the function will be
+// passed exactly one argument, which will be the child window.
+// An example of an array is:
+// aSubtests = [
+// { 'file': 'test_file_name.html' },
+// { 'file': 'test_file_2.html', 'prefs': [['pref.name', true], ['other.pref', 1000]], 'dp_suppression': false }
+// { 'file': 'file_3.html', 'onload': function(w) { w.subtestDone(); } }
+// ];
+//
+// Each subtest should call the subtestDone() function when it is done, to
+// indicate that the window should be torn down and the next text should run.
+// The subtestDone() function is injected into the subtest's window by this
+// function prior to loading the subtest. For convenience, the |is| and |ok|
+// functions provided by SimpleTest are also mapped into the subtest's window.
+// For other things from the parent, the subtest can use window.opener.<whatever>
+// to access objects.
+function runSubtestsSeriallyInFreshWindows(aSubtests) {
+ return new Promise(function(resolve, reject) {
+ var testIndex = -1;
+ var w = null;
+
+ function advanceSubtestExecution() {
+ var test = aSubtests[testIndex];
+ if (w) {
+ if (typeof test.dp_suppression != 'undefined') {
+ // We modified the suppression when starting the test, so now undo that.
+ SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(!test.dp_suppression);
+ }
+ if (test.prefs) {
+ // We pushed some prefs for this test, pop them, and re-invoke
+ // advanceSubtestExecution() after that's been processed
+ SpecialPowers.popPrefEnv(function() {
+ w.close();
+ w = null;
+ advanceSubtestExecution();
+ });
+ return;
+ }
+
+ w.close();
+ }
+
+ testIndex++;
+ if (testIndex >= aSubtests.length) {
+ resolve();
+ return;
+ }
+
+ test = aSubtests[testIndex];
+ if (typeof test.dp_suppression != 'undefined') {
+ // Normally during a test, the displayport will get suppressed during page
+ // load, and unsuppressed at a non-deterministic time during the test. The
+ // unsuppression can trigger a repaint which interferes with the test, so
+ // to avoid that we can force the displayport to be unsuppressed for the
+ // entire test which is more deterministic.
+ SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(test.dp_suppression);
+ }
+
+ function spawnTest(aFile) {
+ w = window.open('', "_blank");
+ w.subtestDone = advanceSubtestExecution;
+ w.SimpleTest = SimpleTest;
+ w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
+ w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
+ if (test.onload) {
+ w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
+ }
+ w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
+ return w;
+ }
+
+ if (test.prefs) {
+ // Got some prefs for this subtest, push them
+ SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
+ w = spawnTest(test.file);
+ });
+ } else {
+ w = spawnTest(test.file);
+ }
+ }
+
+ advanceSubtestExecution();
+ });
+}
+
+function pushPrefs(prefs) {
+ return SpecialPowers.pushPrefEnv({'set': prefs});
+}
+
+function waitUntilApzStable() {
+ return new Promise(function(resolve, reject) {
+ SimpleTest.waitForFocus(function() {
+ waitForAllPaints(function() {
+ flushApzRepaints(resolve);
+ });
+ }, window);
+ });
+}
+
+function isApzEnabled() {
+ var enabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
+ if (!enabled) {
+ // All tests are required to have at least one assertion. Since APZ is
+ // disabled, and the main test is presumably not going to run, we stick in
+ // a dummy assertion here to keep the test passing.
+ SimpleTest.ok(true, "APZ is not enabled; this test will be skipped");
+ }
+ return enabled;
+}
+
+// Despite what this function name says, this does not *directly* run the
+// provided continuation testFunction. Instead, it returns a function that
+// can be used to run the continuation. The extra level of indirection allows
+// it to be more easily added to a promise chain, like so:
+// waitUntilApzStable().then(runContinuation(myTest));
+//
+// If you want to run the continuation directly, outside of a promise chain,
+// you can invoke the return value of this function, like so:
+// runContinuation(myTest)();
+function runContinuation(testFunction) {
+ // We need to wrap this in an extra function, so that the call site can
+ // be more readable without running the promise too early. In other words,
+ // if we didn't have this extra function, the promise would start running
+ // during construction of the promise chain, concurrently with the first
+ // promise in the chain.
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var testContinuation = null;
+
+ function driveTest() {
+ if (!testContinuation) {
+ testContinuation = testFunction(driveTest);
+ }
+ var ret = testContinuation.next();
+ if (ret.done) {
+ resolve();
+ }
+ }
+
+ driveTest();
+ });
+ };
+}
+
+// Take a snapshot of the given rect, *including compositor transforms* (i.e.
+// includes async scroll transforms applied by APZ). If you don't need the
+// compositor transforms, you can probably get away with using
+// SpecialPowers.snapshotWindowWithOptions or one of the friendlier wrappers.
+// The rect provided is expected to be relative to the screen, for example as
+// returned by rectRelativeToScreen in apz_test_native_event_utils.js.
+// Example usage:
+// var snapshot = getSnapshot(rectRelativeToScreen(myDiv));
+// which will take a snapshot of the 'myDiv' element. Note that if part of the
+// element is obscured by other things on top, the snapshot will include those
+// things. If it is clipped by a scroll container, the snapshot will include
+// that area anyway, so you will probably get parts of the scroll container in
+// the snapshot. If the rect extends outside the browser window then the
+// results are undefined.
+// The snapshot is returned in the form of a data URL.
+function getSnapshot(rect) {
+ function parentProcessSnapshot() {
+ addMessageListener('snapshot', function(rect) {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+
+ // reposition the rect relative to the top-level browser window
+ rect = JSON.parse(rect);
+ rect.x -= topWin.mozInnerScreenX;
+ rect.y -= topWin.mozInnerScreenY;
+
+ // take the snapshot
+ var canvas = topWin.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ canvas.width = rect.w;
+ canvas.height = rect.h;
+ var ctx = canvas.getContext("2d");
+ ctx.drawWindow(topWin, rect.x, rect.y, rect.w, rect.h, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_CARET);
+ return canvas.toDataURL();
+ });
+ }
+
+ if (typeof getSnapshot.chromeHelper == 'undefined') {
+ // This is the first time getSnapshot is being called; do initialization
+ getSnapshot.chromeHelper = SpecialPowers.loadChromeScript(parentProcessSnapshot);
+ SimpleTest.registerCleanupFunction(function() { getSnapshot.chromeHelper.destroy() });
+ }
+
+ return getSnapshot.chromeHelper.sendSyncMessage('snapshot', JSON.stringify(rect)).toString();
+}
+
+// Takes the document's query string and parses it, assuming the query string
+// is composed of key-value pairs where the value is in JSON format. The object
+// returned contains the various values indexed by their respective keys. In
+// case of duplicate keys, the last value be used.
+// Examples:
+// ?key="value"&key2=false&key3=500
+// produces { "key": "value", "key2": false, "key3": 500 }
+// ?key={"x":0,"y":50}&key2=[1,2,true]
+// produces { "key": { "x": 0, "y": 0 }, "key2": [1, 2, true] }
+function getQueryArgs() {
+ var args = {};
+ if (location.search.length > 0) {
+ var params = location.search.substr(1).split('&');
+ for (var p of params) {
+ var [k, v] = p.split('=');
+ args[k] = JSON.parse(v);
+ }
+ }
+ return args;
+}
+
+// Return a function that returns a promise to create a script element with the
+// given URI and append it to the head of the document in the given window.
+// As with runContinuation(), the extra function wrapper is for convenience
+// at the call site, so that this can be chained with other promises:
+// waitUntilApzStable().then(injectScript('foo'))
+// .then(injectScript('bar'));
+// If you want to do the injection right away, run the function returned by
+// this function:
+// injectScript('foo')();
+function injectScript(aScript, aWindow = window) {
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var e = aWindow.document.createElement('script');
+ e.type = 'text/javascript';
+ e.onload = function() {
+ resolve();
+ };
+ e.onerror = function() {
+ dump('Script [' + aScript + '] errored out\n');
+ reject();
+ };
+ e.src = aScript;
+ aWindow.document.getElementsByTagName('head')[0].appendChild(e);
+ });
+ };
+}
diff --git a/system/graphics/layers/apz/test/mochitest/chrome.ini b/system/graphics/layers/apz/test/mochitest/chrome.ini
new file mode 100644
index 000000000..d52da5928
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/chrome.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ apz_test_native_event_utils.js
+tags = apz-chrome
+
+[test_smoothness.html]
+# hardware vsync only on win/mac
+# e10s only since APZ is only enabled on e10s
+skip-if = debug || (os != 'mac' && os != 'win') || !e10s
diff --git a/system/graphics/layers/apz/test/mochitest/helper_basic_pan.html b/system/graphics/layers/apz/test/mochitest/helper_basic_pan.html
new file mode 100644
index 000000000..c33258da8
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_basic_pan.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollPage() {
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(document.body, 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ is(window.scrollY, 50, "check that the window scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollPage);
+
+ </script>
+</head>
+<body>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the page scrollable.
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1151663.html b/system/graphics/layers/apz/test/mochitest/helper_bug1151663.html
new file mode 100644
index 000000000..ef2fde9a9
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1151663.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1151663, helper page</title>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript">
+
+ // -------------------------------------------------------------------
+ // Infrastructure to get the test assertions to run at the right time.
+ // -------------------------------------------------------------------
+ var SimpleTest = window.opener.SimpleTest;
+
+ window.onload = function() {
+ window.addEventListener("MozAfterPaint", afterPaint, false);
+ };
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ function afterPaint(e) {
+ // If there is another paint pending, wait for it.
+ if (utils.isMozAfterPaintPending) {
+ return;
+ }
+
+ // Once there are no more paints pending, remove the
+ // MozAfterPaint listener and run the test logic.
+ window.removeEventListener("MozAfterPaint", afterPaint, false);
+ testBug1151663();
+ }
+
+ // --------------------------------------------------------------------
+ // The actual logic for testing bug 1151663.
+ //
+ // In this test we have a simple page which is scrollable, with a
+ // scrollable <div> which is also scrollable. We test that the
+ // <div> does not get an initial APZC, since primary scrollable
+ // frame is the page's root scroll frame.
+ // --------------------------------------------------------------------
+
+ function testBug1151663() {
+ // Get the content- and compositor-side test data from nsIDOMWindowUtils.
+ var contentTestData = utils.getContentAPZTestData();
+ var compositorTestData = utils.getCompositorAPZTestData();
+
+ // Get the sequence number of the last paint on the compositor side.
+ // We do this before converting the APZ test data because the conversion
+ // loses the order of the paints.
+ SimpleTest.ok(compositorTestData.paints.length > 0,
+ "expected at least one paint in compositor test data");
+ var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
+ var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
+
+ // Convert the test data into a representation that's easier to navigate.
+ contentTestData = convertTestData(contentTestData);
+ compositorTestData = convertTestData(compositorTestData);
+ var paint = compositorTestData.paints[lastCompositorPaintSeqNo];
+
+ // Reconstruct the APZC tree structure in the last paint.
+ var apzcTree = buildApzcTree(paint);
+
+ // The apzc tree for this page should consist of a single root APZC,
+ // which either is the RCD with no child APZCs (e10s/B2G case) or has a
+ // single child APZC which is for the RCD (fennec case).
+ var rcd = findRcdNode(apzcTree);
+ SimpleTest.ok(rcd != null, "found the RCD node");
+ SimpleTest.is(rcd.children.length, 0, "expected no children on the RCD");
+
+ window.opener.finishTest();
+ }
+ </script>
+</head>
+<body style="height: 500px; overflow: scroll">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
+ <div style="height: 50px; width: 50px; overflow: scroll">
+ <!-- Put enough content into the subframe to make it have a nonzero scroll range -->
+ <div style="height: 100px; width: 50px"></div>
+ </div>
+ <!-- Put enough content into the page to make it have a nonzero scroll range -->
+ <div style="height: 1000px"></div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1162771.html b/system/graphics/layers/apz/test/mochitest/helper_bug1162771.html
new file mode 100644
index 000000000..18e4a2f05
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1162771.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test for touchend on media elements</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var v = document.getElementById('video');
+ var a = document.getElementById('audio');
+ var d = document.getElementById('div');
+
+ document.body.ontouchstart = function(e) {
+ if (e.target === v || e.target === a || e.target === d) {
+ e.target.style.display = 'none';
+ ok(true, 'Set display to none on #' + e.target.id);
+ } else {
+ ok(false, 'Got unexpected touchstart on ' + e.target);
+ }
+ waitForAllPaints(testDriver);
+ };
+
+ document.body.ontouchend = function(e) {
+ if (e.target === v || e.target === a || e.target === d) {
+ e.target._gotTouchend = true;
+ ok(true, 'Got touchend event on #' + e.target.id);
+ }
+ testDriver();
+ };
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+
+ var pt = coordinatesRelativeToScreen(25, 5, v);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(v._gotTouchend, 'Touchend was received on video element');
+
+ pt = coordinatesRelativeToScreen(25, 5, a);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(a._gotTouchend, 'Touchend was received on audio element');
+
+ pt = coordinatesRelativeToScreen(25, 5, d);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(d._gotTouchend, 'Touchend was received on div element');
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ * {
+ font-size: 24px;
+ box-sizing: border-box;
+ }
+
+ #video {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left:0;
+ width: 33%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #8a8;
+ }
+
+ #audio {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left:33%;
+ width: 33%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #a88;
+ }
+
+ #div {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left: 66%;
+ width: 34%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #88a;
+ }
+ </style>
+</head>
+<body>
+ <p>Tap on the colored boxes to hide them.</p>
+ <video id="video"></video>
+ <audio id="audio" controls></audio>
+ <div id="div"></div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1271432.html b/system/graphics/layers/apz/test/mochitest/helper_bug1271432.html
new file mode 100644
index 000000000..8234b8232
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1271432.html
@@ -0,0 +1,574 @@
+<head>
+ <title>Ensure that the hit region doesn't get unexpectedly expanded</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+<script type="application/javascript">
+function* test(testDriver) {
+ var scroller = document.getElementById('scroller');
+ var scrollerPos = scroller.scrollTop;
+ var dx = 100, dy = 50;
+
+ is(window.scrollY, 0, "Initial page scroll position should be 0");
+ is(scrollerPos, 0, "Initial scroller position should be 0");
+
+ yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
+
+ is(window.scrollY, 0, "Page scroll position should still be 0");
+ ok(scroller.scrollTop > scrollerPos, "Scroller should have scrolled");
+
+ // wait for it to layerize fully and then try again
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ scrollerPos = scroller.scrollTop;
+
+ yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
+ is(window.scrollY, 0, "Page scroll position should still be 0 after layerization");
+ ok(scroller.scrollTop > scrollerPos, "Scroller should have continued scrolling");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+</script>
+<style>
+a#with_after_content {
+ background-color: #F16725;
+ opacity: 0.8;
+ display: inline-block;
+ margin-top: 40px;
+ margin-left: 40px;
+}
+a#with_after_content::after {
+ content: " ";
+ position: absolute;
+ width: 0px;
+ height: 0px;
+ bottom: 40px;
+ z-index: -1;
+ right: 40px;
+ background-color: transparent;
+ border-style: solid;
+ border-width: 15px 15px 15px 0;
+ border-color: #d54e0e transparent transparent transparent;
+ box-shadow: none;
+ box-sizing: border-box;
+}
+div#scroller {
+ overflow-y: scroll;
+ width: 50%;
+ height: 50%;
+}
+</style>
+</head>
+<body>
+<a id="with_after_content">Some text</a>
+
+<div id="scroller">
+Scrolling on the very left edge of this div will work.
+Scrolling on the right side of this div (starting with the left edge of the orange box above) should work, but doesn't.<br/>
+0<br>
+1<br>
+2<br>
+3<br>
+4<br>
+5<br>
+6<br>
+7<br>
+8<br>
+9<br>
+10<br>
+11<br>
+12<br>
+13<br>
+14<br>
+15<br>
+16<br>
+17<br>
+18<br>
+19<br>
+20<br>
+21<br>
+22<br>
+23<br>
+24<br>
+25<br>
+26<br>
+27<br>
+28<br>
+29<br>
+30<br>
+31<br>
+32<br>
+33<br>
+34<br>
+35<br>
+36<br>
+37<br>
+38<br>
+39<br>
+40<br>
+41<br>
+42<br>
+43<br>
+44<br>
+45<br>
+46<br>
+47<br>
+48<br>
+49<br>
+50<br>
+51<br>
+52<br>
+53<br>
+54<br>
+55<br>
+56<br>
+57<br>
+58<br>
+59<br>
+60<br>
+61<br>
+62<br>
+63<br>
+64<br>
+65<br>
+66<br>
+67<br>
+68<br>
+69<br>
+70<br>
+71<br>
+72<br>
+73<br>
+74<br>
+75<br>
+76<br>
+77<br>
+78<br>
+79<br>
+80<br>
+81<br>
+82<br>
+83<br>
+84<br>
+85<br>
+86<br>
+87<br>
+88<br>
+89<br>
+90<br>
+91<br>
+92<br>
+93<br>
+94<br>
+95<br>
+96<br>
+97<br>
+98<br>
+99<br>
+100<br>
+101<br>
+102<br>
+103<br>
+104<br>
+105<br>
+106<br>
+107<br>
+108<br>
+109<br>
+110<br>
+111<br>
+112<br>
+113<br>
+114<br>
+115<br>
+116<br>
+117<br>
+118<br>
+119<br>
+120<br>
+121<br>
+122<br>
+123<br>
+124<br>
+125<br>
+126<br>
+127<br>
+128<br>
+129<br>
+130<br>
+131<br>
+132<br>
+133<br>
+134<br>
+135<br>
+136<br>
+137<br>
+138<br>
+139<br>
+140<br>
+141<br>
+142<br>
+143<br>
+144<br>
+145<br>
+146<br>
+147<br>
+148<br>
+149<br>
+150<br>
+151<br>
+152<br>
+153<br>
+154<br>
+155<br>
+156<br>
+157<br>
+158<br>
+159<br>
+160<br>
+161<br>
+162<br>
+163<br>
+164<br>
+165<br>
+166<br>
+167<br>
+168<br>
+169<br>
+170<br>
+171<br>
+172<br>
+173<br>
+174<br>
+175<br>
+176<br>
+177<br>
+178<br>
+179<br>
+180<br>
+181<br>
+182<br>
+183<br>
+184<br>
+185<br>
+186<br>
+187<br>
+188<br>
+189<br>
+190<br>
+191<br>
+192<br>
+193<br>
+194<br>
+195<br>
+196<br>
+197<br>
+198<br>
+199<br>
+200<br>
+201<br>
+202<br>
+203<br>
+204<br>
+205<br>
+206<br>
+207<br>
+208<br>
+209<br>
+210<br>
+211<br>
+212<br>
+213<br>
+214<br>
+215<br>
+216<br>
+217<br>
+218<br>
+219<br>
+220<br>
+221<br>
+222<br>
+223<br>
+224<br>
+225<br>
+226<br>
+227<br>
+228<br>
+229<br>
+230<br>
+231<br>
+232<br>
+233<br>
+234<br>
+235<br>
+236<br>
+237<br>
+238<br>
+239<br>
+240<br>
+241<br>
+242<br>
+243<br>
+244<br>
+245<br>
+246<br>
+247<br>
+248<br>
+249<br>
+250<br>
+251<br>
+252<br>
+253<br>
+254<br>
+255<br>
+256<br>
+257<br>
+258<br>
+259<br>
+260<br>
+261<br>
+262<br>
+263<br>
+264<br>
+265<br>
+266<br>
+267<br>
+268<br>
+269<br>
+270<br>
+271<br>
+272<br>
+273<br>
+274<br>
+275<br>
+276<br>
+277<br>
+278<br>
+279<br>
+280<br>
+281<br>
+282<br>
+283<br>
+284<br>
+285<br>
+286<br>
+287<br>
+288<br>
+289<br>
+290<br>
+291<br>
+292<br>
+293<br>
+294<br>
+295<br>
+296<br>
+297<br>
+298<br>
+299<br>
+300<br>
+301<br>
+302<br>
+303<br>
+304<br>
+305<br>
+306<br>
+307<br>
+308<br>
+309<br>
+310<br>
+311<br>
+312<br>
+313<br>
+314<br>
+315<br>
+316<br>
+317<br>
+318<br>
+319<br>
+320<br>
+321<br>
+322<br>
+323<br>
+324<br>
+325<br>
+326<br>
+327<br>
+328<br>
+329<br>
+330<br>
+331<br>
+332<br>
+333<br>
+334<br>
+335<br>
+336<br>
+337<br>
+338<br>
+339<br>
+340<br>
+341<br>
+342<br>
+343<br>
+344<br>
+345<br>
+346<br>
+347<br>
+348<br>
+349<br>
+350<br>
+351<br>
+352<br>
+353<br>
+354<br>
+355<br>
+356<br>
+357<br>
+358<br>
+359<br>
+360<br>
+361<br>
+362<br>
+363<br>
+364<br>
+365<br>
+366<br>
+367<br>
+368<br>
+369<br>
+370<br>
+371<br>
+372<br>
+373<br>
+374<br>
+375<br>
+376<br>
+377<br>
+378<br>
+379<br>
+380<br>
+381<br>
+382<br>
+383<br>
+384<br>
+385<br>
+386<br>
+387<br>
+388<br>
+389<br>
+390<br>
+391<br>
+392<br>
+393<br>
+394<br>
+395<br>
+396<br>
+397<br>
+398<br>
+399<br>
+400<br>
+401<br>
+402<br>
+403<br>
+404<br>
+405<br>
+406<br>
+407<br>
+408<br>
+409<br>
+410<br>
+411<br>
+412<br>
+413<br>
+414<br>
+415<br>
+416<br>
+417<br>
+418<br>
+419<br>
+420<br>
+421<br>
+422<br>
+423<br>
+424<br>
+425<br>
+426<br>
+427<br>
+428<br>
+429<br>
+430<br>
+431<br>
+432<br>
+433<br>
+434<br>
+435<br>
+436<br>
+437<br>
+438<br>
+439<br>
+440<br>
+441<br>
+442<br>
+443<br>
+444<br>
+445<br>
+446<br>
+447<br>
+448<br>
+449<br>
+450<br>
+451<br>
+452<br>
+453<br>
+454<br>
+455<br>
+456<br>
+457<br>
+458<br>
+459<br>
+460<br>
+461<br>
+462<br>
+463<br>
+464<br>
+465<br>
+466<br>
+467<br>
+468<br>
+469<br>
+470<br>
+471<br>
+472<br>
+473<br>
+474<br>
+475<br>
+476<br>
+477<br>
+478<br>
+479<br>
+480<br>
+481<br>
+482<br>
+483<br>
+484<br>
+485<br>
+486<br>
+487<br>
+488<br>
+489<br>
+490<br>
+491<br>
+492<br>
+493<br>
+494<br>
+495<br>
+496<br>
+497<br>
+498<br>
+499<br>
+</div>
+<div style="height: 1000px">this div makes the page scrollable</div>
+</body>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1280013.html b/system/graphics/layers/apz/test/mochitest/helper_bug1280013.html
new file mode 100644
index 000000000..0c602901a
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1280013.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html style="overflow:hidden">
+<head>
+ <meta charset="utf-8">
+ <!-- The viewport tag will result in APZ being in a "zoomed-in" state, assuming
+ the device width is less than 980px. -->
+ <meta name="viewport" content="width=980; initial-scale=1.0">
+ <title>Test for bug 1280013</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+function* test(testDriver) {
+ SimpleTest.ok(screen.height > 500, "Screen height must be at least 500 pixels for this test to work");
+
+ // This listener will trigger the test to continue once APZ is done with
+ // processing the scroll.
+ SpecialPowers.Services.obs.addObserver(testDriver, "APZ:TransformEnd", false);
+
+ // Scroll down to the iframe. Do it in two drags instead of one in case the
+ // device screen is short
+ yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
+ yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
+ // Now the top of the visible area should be at y=700 of the top-level page,
+ // so if the screen is >= 500px tall, the entire iframe should be visible, at
+ // least vertically.
+
+ // However, because of the overflow:hidden on the root elements, all this
+ // scrolling is happening in APZ and is not reflected in the main-thread
+ // scroll position (it is stored in the callback transform instead). We check
+ // this by checking the scroll offset.
+ yield flushApzRepaints(testDriver);
+ SimpleTest.is(window.scrollY, 0, "Main-thread scroll position is still at 0");
+
+ // Scroll the iframe by 300px. Note that since the main-thread scroll position
+ // is still 0, the subframe's getBoundingClientRect is going to be off by
+ // 700 pixels, so we compensate for that here.
+ var subframe = document.getElementById('subframe');
+ yield synthesizeNativeTouchDrag(subframe, 10, 200 - 700, 0, -(300 + TOUCH_SLOP));
+
+ // Remove the observer, we don't need it any more.
+ SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
+
+ // Flush any pending paints
+ yield flushApzRepaints(testDriver);
+
+ // get the displayport for the subframe
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var contentPaints = utils.getContentAPZTestData().paints;
+ var lastPaint = convertScrollFrameData(contentPaints[contentPaints.length - 1].scrollFrames);
+ var foundIt = 0;
+ for (var scrollId in lastPaint) {
+ if (('contentDescription' in lastPaint[scrollId]) &&
+ (lastPaint[scrollId]['contentDescription'].includes('tall_html'))) {
+ var dp = getPropertyAsRect(lastPaint, scrollId, 'criticalDisplayport');
+ SimpleTest.ok(dp.y <= 0, 'The critical displayport top should be less than or equal to zero to cover the visible part of the subframe; it is ' + dp.y);
+ SimpleTest.ok(dp.y + dp.h >= subframe.clientHeight, 'The critical displayport bottom should be greater than the clientHeight; it is ' + (dp.y + dp.h));
+ foundIt++;
+ }
+ }
+ SimpleTest.is(foundIt, 1, "Found exactly one critical displayport for the subframe we were interested in.");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="overflow:hidden">
+ The iframe below is at (0, 800). Scroll it into view, and then scroll the contents. The content should be fully rendered in high-resolution.
+ <iframe id="subframe" style="position:absolute; left: 0px; top: 800px; width: 600px; height: 350px" src="helper_tall.html"></iframe>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1285070.html b/system/graphics/layers/apz/test/mochitest/helper_bug1285070.html
new file mode 100644
index 000000000..3a4879034
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1285070.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test pointer events are dispatched once for touch tap</title>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript">
+ function test() {
+ let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
+ "pointerup", "pointerleave", "pointerout"];
+ let pointerEventsCount = {};
+
+ pointerEventsList.forEach((eventName) => {
+ pointerEventsCount[eventName] = 0;
+ document.getElementById('div1').addEventListener(eventName, (event) => {
+ dump("Received event " + event.type + "\n");
+ ++pointerEventsCount[event.type];
+ }, false);
+ });
+
+ document.addEventListener("click", (event) => {
+ is(event.target, document.getElementById('div1'), "Clicked on div (at " + event.clientX + "," + event.clientY + ")");
+ for (var key in pointerEventsCount) {
+ is(pointerEventsCount[key], 1, "Event " + key + " should be generated once");
+ }
+ subtestDone();
+ }, false);
+
+ synthesizeNativeTap(document.getElementById('div1'), 100, 100, () => {
+ dump("Finished synthesizing tap, waiting for div to be clicked...\n");
+ });
+ }
+
+ waitUntilApzStable().then(test);
+
+ </script>
+</head>
+<body>
+ <div id="div1" style="width: 200px; height: 200px; background: black"></div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug1299195.html b/system/graphics/layers/apz/test/mochitest/helper_bug1299195.html
new file mode 100644
index 000000000..8e746749c
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug1299195.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test pointer events are dispatched once for touch tap</title>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript">
+ /** Test for Bug 1299195 **/
+ function runTests() {
+ let target0 = document.getElementById("target0");
+ let mouseup_count = 0;
+ let mousedown_count = 0;
+ let pointerup_count = 0;
+ let pointerdown_count = 0;
+
+ target0.addEventListener("mouseup", () => {
+ ++mouseup_count;
+ if (mouseup_count == 2) {
+ is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
+ is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
+ is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
+ is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
+ subtestDone();
+ }
+ });
+ target0.addEventListener("mousedown", () => {
+ ++mousedown_count;
+ });
+ target0.addEventListener("pointerup", () => {
+ ++pointerup_count;
+ });
+ target0.addEventListener("pointerdown", () => {
+ ++pointerdown_count;
+ });
+ synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+ synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+ }
+ waitUntilApzStable().then(runTests);
+ </script>
+</head>
+<body>
+ <div id="target0" style="width: 200px; height: 200px; background: green"></div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_bug982141.html b/system/graphics/layers/apz/test/mochitest/helper_bug982141.html
new file mode 100644
index 000000000..5d2f15397
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_bug982141.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982141
+-->
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="user-scalable=no">
+ <title>Test for Bug 982141, helper page</title>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript">
+
+ // -------------------------------------------------------------------
+ // Infrastructure to get the test assertions to run at the right time.
+ // -------------------------------------------------------------------
+ var SimpleTest = window.opener.SimpleTest;
+
+ window.onload = function() {
+ window.addEventListener("MozAfterPaint", afterPaint, false);
+ };
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ function afterPaint(e) {
+ // If there is another paint pending, wait for it.
+ if (utils.isMozAfterPaintPending) {
+ return;
+ }
+
+ // Once there are no more paints pending, remove the
+ // MozAfterPaint listener and run the test logic.
+ window.removeEventListener("MozAfterPaint", afterPaint, false);
+ testBug982141();
+ }
+
+ // --------------------------------------------------------------------
+ // The actual logic for testing bug 982141.
+ //
+ // In this test we have a simple page with a scrollable <div> which has
+ // enough content to make it scrollable. We test that this <div> got
+ // a displayport.
+ // --------------------------------------------------------------------
+
+ function testBug982141() {
+ // Get the content- and compositor-side test data from nsIDOMWindowUtils.
+ var contentTestData = utils.getContentAPZTestData();
+ var compositorTestData = utils.getCompositorAPZTestData();
+
+ // Get the sequence number of the last paint on the compositor side.
+ // We do this before converting the APZ test data because the conversion
+ // loses the order of the paints.
+ SimpleTest.ok(compositorTestData.paints.length > 0,
+ "expected at least one paint in compositor test data");
+ var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
+ var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
+
+ // Convert the test data into a representation that's easier to navigate.
+ contentTestData = convertTestData(contentTestData);
+ compositorTestData = convertTestData(compositorTestData);
+
+ // Reconstruct the APZC tree structure in the last paint.
+ var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]);
+
+ // The apzc tree for this page should consist of a single child APZC on
+ // the RCD node (the child is for scrollable <div>). Note that in e10s/B2G
+ // cases the RCD will be the root of the tree but on Fennec it will not.
+ var rcd = findRcdNode(apzcTree);
+ SimpleTest.ok(rcd != null, "found the RCD node");
+ SimpleTest.is(rcd.children.length, 1, "expected a single child APZC");
+ var childScrollId = rcd.children[0].scrollId;
+
+ // We should have content-side data for the same paint.
+ SimpleTest.ok(lastCompositorPaintSeqNo in contentTestData.paints,
+ "expected a content paint with sequence number" + lastCompositorPaintSeqNo);
+ var correspondingContentPaint = contentTestData.paints[lastCompositorPaintSeqNo];
+
+ var dp = getPropertyAsRect(correspondingContentPaint, childScrollId, 'displayport');
+ var subframe = document.getElementById('subframe');
+ // The clientWidth and clientHeight may be less than 50 if there are scrollbars showing.
+ // In general they will be (50 - <scrollbarwidth>, 50 - <scrollbarheight>).
+ SimpleTest.ok(subframe.clientWidth > 0, "Expected a non-zero clientWidth, got: " + subframe.clientWidth);
+ SimpleTest.ok(subframe.clientHeight > 0, "Expected a non-zero clientHeight, got: " + subframe.clientHeight);
+ SimpleTest.ok(dp.w >= subframe.clientWidth && dp.h >= subframe.clientHeight,
+ "expected a displayport at least as large as the scrollable element, got " + JSON.stringify(dp));
+
+ window.opener.finishTest();
+ }
+ </script>
+</head>
+<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
+ <!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
+ <div id="subframe" style="height: 50px; width: 50px; overflow: scroll">
+ <div style="width: 100px">
+ Wide content so that the vertical scrollbar for the parent div
+ doesn't eat into the 50px width and reduce the width of the
+ displayport.
+ </div>
+ Line 1<br>
+ Line 2<br>
+ Line 3<br>
+ Line 4<br>
+ Line 5<br>
+ Line 6<br>
+ Line 7<br>
+ Line 8<br>
+ Line 9<br>
+ Line 10<br>
+ Line 11<br>
+ Line 12<br>
+ Line 13<br>
+ Line 14<br>
+ Line 15<br>
+ Line 16<br>
+ Line 17<br>
+ Line 18<br>
+ Line 19<br>
+ Line 20<br>
+ Line 21<br>
+ Line 22<br>
+ Line 23<br>
+ Line 24<br>
+ Line 25<br>
+ Line 26<br>
+ Line 27<br>
+ Line 28<br>
+ Line 29<br>
+ Line 30<br>
+ Line 31<br>
+ Line 32<br>
+ Line 33<br>
+ Line 34<br>
+ Line 35<br>
+ Line 36<br>
+ Line 37<br>
+ Line 38<br>
+ Line 39<br>
+ Line 40<br>
+ Line 41<br>
+ Line 42<br>
+ Line 43<br>
+ Line 44<br>
+ Line 45<br>
+ Line 46<br>
+ Line 40<br>
+ Line 48<br>
+ Line 49<br>
+ Line 50<br>
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_click.html b/system/graphics/layers/apz/test/mochitest/helper_click.html
new file mode 100644
index 000000000..b74f175fe
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_click.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity mouse-clicking test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* clickButton(testDriver) {
+ document.addEventListener('click', clicked, false);
+
+ if (getQueryArgs()['dtc']) {
+ // force a dispatch-to-content region on the document
+ document.addEventListener('wheel', function() { /* no-op */ }, { passive: false });
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ }
+
+ synthesizeNativeClick(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing click, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable()
+.then(runContinuation(clickButton));
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_div_pan.html b/system/graphics/layers/apz/test/mochitest/helper_div_pan.html
new file mode 100644
index 000000000..f37be8ba6
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_div_pan.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test for scrollable div</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollOuter() {
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(document.getElementById('outer'), 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ var outerScroll = document.getElementById('outer').scrollTop;
+ is(outerScroll, 50, "check that the div scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollOuter);
+
+ </script>
+</head>
+<body>
+ <div id="outer" style="height: 250px; border: solid 1px black; overflow:scroll">
+ <div style="height: 5000px; background-color: lightblue">
+ This div makes the |outer| div scrollable.
+ </div>
+ </div>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the top-level page scrollable.
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_drag_click.html b/system/graphics/layers/apz/test/mochitest/helper_drag_click.html
new file mode 100644
index 000000000..cf7117339
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_drag_click.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity mouse-drag click test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ document.addEventListener('click', clicked, false);
+
+ // Ensure the pointer is inside the window
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseMoveEventMsg(), testDriver);
+ // mouse down, move it around, and release it near where it went down. this
+ // should generate a click at the release point
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseDownEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 100, 100, nativeMouseMoveEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 10, 10, nativeMouseMoveEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 8, 8, nativeMouseUpEventMsg(), testDriver);
+ dump("Finished synthesizing click with a drag in the middle\n");
+}
+
+function clicked(e) {
+ // The mouse down at (5, 5) should not have generated a click, but the up
+ // at (8, 8) should have.
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ is(e.clientX, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().left), 'x-coord of click event looks sane');
+ is(e.clientY, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().top), 'y-coord of click event looks sane');
+ subtestDone();
+}
+
+waitUntilApzStable()
+.then(runContinuation(test));
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_drag_scroll.html b/system/graphics/layers/apz/test/mochitest/helper_drag_scroll.html
new file mode 100644
index 000000000..3c06a5b7e
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_drag_scroll.html
@@ -0,0 +1,603 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Dragging the mouse on a content-implemented scrollbar</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <style>
+ body {
+ background: linear-gradient(135deg, red, blue);
+ }
+ #scrollbar {
+ position:fixed;
+ top: 0;
+ right: 10px;
+ height: 100%;
+ width: 150px;
+ background-color: gray;
+ }
+ </style>
+ <script type="text/javascript">
+var bar = null;
+var mouseDown = false;
+
+function moveTo(mouseY, testDriver) {
+ var fraction = (mouseY - bar.getBoundingClientRect().top) / bar.getBoundingClientRect().height;
+ fraction = Math.max(0, fraction);
+ fraction = Math.min(1, fraction);
+ var oldScrollPos = document.scrollingElement.scrollTop;
+ var newScrollPos = fraction * window.scrollMaxY;
+ SimpleTest.ok(newScrollPos > oldScrollPos, "Scroll position strictly increased");
+ // split the scroll in two with a paint in between, just to increase the
+ // complexity of the simulated web content, and to ensure this works as well.
+ document.scrollingElement.scrollTop = (oldScrollPos + newScrollPos) / 2;
+ waitForAllPaints(function() {
+ document.scrollingElement.scrollTop = newScrollPos;
+ testDriver();
+ });
+}
+
+function setupDragging(testDriver) {
+ bar = document.getElementById('scrollbar');
+ mouseDown = false;
+
+ bar.addEventListener('mousedown', function(e) {
+ mouseDown = true;
+ moveTo(e.clientY, testDriver);
+ }, true);
+
+ bar.addEventListener('mousemove', function(e) {
+ if (mouseDown) {
+ dump("Got mousemove clientY " + e.clientY + "\n");
+ moveTo(e.clientY, testDriver);
+ e.stopPropagation();
+ }
+ }, true);
+
+ bar.addEventListener('mouseup', function(e) {
+ mouseDown = false;
+ }, true);
+
+ window.addEventListener('mousemove', function(e) {
+ if (mouseDown) {
+ SimpleTest.ok(false, "The mousemove at " + e.clientY + " was not stopped by the bar listener, and is a glitchy event!");
+ setTimeout(testDriver, 0);
+ }
+ }, false);
+}
+
+function* test(testDriver) {
+ setupDragging(testDriver);
+
+ // Move the mouse to the "scrollbar" (the div upon which dragging changes scroll position)
+ yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseMoveEventMsg(), testDriver);
+ // mouse down
+ yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseDownEventMsg());
+ // drag vertically by 400px, in 50px increments
+ yield synthesizeNativeMouseEvent(bar, 10, 60, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 110, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 160, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 210, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 260, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 310, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 360, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseMoveEventMsg());
+ // and release
+ yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseUpEventMsg(), testDriver);
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body>
+
+<div id="scrollbar">Drag up and down on this bar. The background/scrollbar shouldn't glitch</div>
+This is a tall page<br/>
+1<br/>
+2<br/>
+3<br/>
+4<br/>
+5<br/>
+6<br/>
+7<br/>
+8<br/>
+9<br/>
+10<br/>
+11<br/>
+12<br/>
+13<br/>
+14<br/>
+15<br/>
+16<br/>
+17<br/>
+18<br/>
+19<br/>
+20<br/>
+21<br/>
+22<br/>
+23<br/>
+24<br/>
+25<br/>
+26<br/>
+27<br/>
+28<br/>
+29<br/>
+30<br/>
+31<br/>
+32<br/>
+33<br/>
+34<br/>
+35<br/>
+36<br/>
+37<br/>
+38<br/>
+39<br/>
+40<br/>
+41<br/>
+42<br/>
+43<br/>
+44<br/>
+45<br/>
+46<br/>
+47<br/>
+48<br/>
+49<br/>
+50<br/>
+51<br/>
+52<br/>
+53<br/>
+54<br/>
+55<br/>
+56<br/>
+57<br/>
+58<br/>
+59<br/>
+60<br/>
+61<br/>
+62<br/>
+63<br/>
+64<br/>
+65<br/>
+66<br/>
+67<br/>
+68<br/>
+69<br/>
+70<br/>
+71<br/>
+72<br/>
+73<br/>
+74<br/>
+75<br/>
+76<br/>
+77<br/>
+78<br/>
+79<br/>
+80<br/>
+81<br/>
+82<br/>
+83<br/>
+84<br/>
+85<br/>
+86<br/>
+87<br/>
+88<br/>
+89<br/>
+90<br/>
+91<br/>
+92<br/>
+93<br/>
+94<br/>
+95<br/>
+96<br/>
+97<br/>
+98<br/>
+99<br/>
+100<br/>
+101<br/>
+102<br/>
+103<br/>
+104<br/>
+105<br/>
+106<br/>
+107<br/>
+108<br/>
+109<br/>
+110<br/>
+111<br/>
+112<br/>
+113<br/>
+114<br/>
+115<br/>
+116<br/>
+117<br/>
+118<br/>
+119<br/>
+120<br/>
+121<br/>
+122<br/>
+123<br/>
+124<br/>
+125<br/>
+126<br/>
+127<br/>
+128<br/>
+129<br/>
+130<br/>
+131<br/>
+132<br/>
+133<br/>
+134<br/>
+135<br/>
+136<br/>
+137<br/>
+138<br/>
+139<br/>
+140<br/>
+141<br/>
+142<br/>
+143<br/>
+144<br/>
+145<br/>
+146<br/>
+147<br/>
+148<br/>
+149<br/>
+150<br/>
+151<br/>
+152<br/>
+153<br/>
+154<br/>
+155<br/>
+156<br/>
+157<br/>
+158<br/>
+159<br/>
+160<br/>
+161<br/>
+162<br/>
+163<br/>
+164<br/>
+165<br/>
+166<br/>
+167<br/>
+168<br/>
+169<br/>
+170<br/>
+171<br/>
+172<br/>
+173<br/>
+174<br/>
+175<br/>
+176<br/>
+177<br/>
+178<br/>
+179<br/>
+180<br/>
+181<br/>
+182<br/>
+183<br/>
+184<br/>
+185<br/>
+186<br/>
+187<br/>
+188<br/>
+189<br/>
+190<br/>
+191<br/>
+192<br/>
+193<br/>
+194<br/>
+195<br/>
+196<br/>
+197<br/>
+198<br/>
+199<br/>
+200<br/>
+201<br/>
+202<br/>
+203<br/>
+204<br/>
+205<br/>
+206<br/>
+207<br/>
+208<br/>
+209<br/>
+210<br/>
+211<br/>
+212<br/>
+213<br/>
+214<br/>
+215<br/>
+216<br/>
+217<br/>
+218<br/>
+219<br/>
+220<br/>
+221<br/>
+222<br/>
+223<br/>
+224<br/>
+225<br/>
+226<br/>
+227<br/>
+228<br/>
+229<br/>
+230<br/>
+231<br/>
+232<br/>
+233<br/>
+234<br/>
+235<br/>
+236<br/>
+237<br/>
+238<br/>
+239<br/>
+240<br/>
+241<br/>
+242<br/>
+243<br/>
+244<br/>
+245<br/>
+246<br/>
+247<br/>
+248<br/>
+249<br/>
+250<br/>
+251<br/>
+252<br/>
+253<br/>
+254<br/>
+255<br/>
+256<br/>
+257<br/>
+258<br/>
+259<br/>
+260<br/>
+261<br/>
+262<br/>
+263<br/>
+264<br/>
+265<br/>
+266<br/>
+267<br/>
+268<br/>
+269<br/>
+270<br/>
+271<br/>
+272<br/>
+273<br/>
+274<br/>
+275<br/>
+276<br/>
+277<br/>
+278<br/>
+279<br/>
+280<br/>
+281<br/>
+282<br/>
+283<br/>
+284<br/>
+285<br/>
+286<br/>
+287<br/>
+288<br/>
+289<br/>
+290<br/>
+291<br/>
+292<br/>
+293<br/>
+294<br/>
+295<br/>
+296<br/>
+297<br/>
+298<br/>
+299<br/>
+300<br/>
+301<br/>
+302<br/>
+303<br/>
+304<br/>
+305<br/>
+306<br/>
+307<br/>
+308<br/>
+309<br/>
+310<br/>
+311<br/>
+312<br/>
+313<br/>
+314<br/>
+315<br/>
+316<br/>
+317<br/>
+318<br/>
+319<br/>
+320<br/>
+321<br/>
+322<br/>
+323<br/>
+324<br/>
+325<br/>
+326<br/>
+327<br/>
+328<br/>
+329<br/>
+330<br/>
+331<br/>
+332<br/>
+333<br/>
+334<br/>
+335<br/>
+336<br/>
+337<br/>
+338<br/>
+339<br/>
+340<br/>
+341<br/>
+342<br/>
+343<br/>
+344<br/>
+345<br/>
+346<br/>
+347<br/>
+348<br/>
+349<br/>
+350<br/>
+351<br/>
+352<br/>
+353<br/>
+354<br/>
+355<br/>
+356<br/>
+357<br/>
+358<br/>
+359<br/>
+360<br/>
+361<br/>
+362<br/>
+363<br/>
+364<br/>
+365<br/>
+366<br/>
+367<br/>
+368<br/>
+369<br/>
+370<br/>
+371<br/>
+372<br/>
+373<br/>
+374<br/>
+375<br/>
+376<br/>
+377<br/>
+378<br/>
+379<br/>
+380<br/>
+381<br/>
+382<br/>
+383<br/>
+384<br/>
+385<br/>
+386<br/>
+387<br/>
+388<br/>
+389<br/>
+390<br/>
+391<br/>
+392<br/>
+393<br/>
+394<br/>
+395<br/>
+396<br/>
+397<br/>
+398<br/>
+399<br/>
+400<br/>
+401<br/>
+402<br/>
+403<br/>
+404<br/>
+405<br/>
+406<br/>
+407<br/>
+408<br/>
+409<br/>
+410<br/>
+411<br/>
+412<br/>
+413<br/>
+414<br/>
+415<br/>
+416<br/>
+417<br/>
+418<br/>
+419<br/>
+420<br/>
+421<br/>
+422<br/>
+423<br/>
+424<br/>
+425<br/>
+426<br/>
+427<br/>
+428<br/>
+429<br/>
+430<br/>
+431<br/>
+432<br/>
+433<br/>
+434<br/>
+435<br/>
+436<br/>
+437<br/>
+438<br/>
+439<br/>
+440<br/>
+441<br/>
+442<br/>
+443<br/>
+444<br/>
+445<br/>
+446<br/>
+447<br/>
+448<br/>
+449<br/>
+450<br/>
+451<br/>
+452<br/>
+453<br/>
+454<br/>
+455<br/>
+456<br/>
+457<br/>
+458<br/>
+459<br/>
+460<br/>
+461<br/>
+462<br/>
+463<br/>
+464<br/>
+465<br/>
+466<br/>
+467<br/>
+468<br/>
+469<br/>
+470<br/>
+471<br/>
+472<br/>
+473<br/>
+474<br/>
+475<br/>
+476<br/>
+477<br/>
+478<br/>
+479<br/>
+480<br/>
+481<br/>
+482<br/>
+483<br/>
+484<br/>
+485<br/>
+486<br/>
+487<br/>
+488<br/>
+489<br/>
+490<br/>
+491<br/>
+492<br/>
+493<br/>
+494<br/>
+495<br/>
+496<br/>
+497<br/>
+498<br/>
+499<br/>
+
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_iframe1.html b/system/graphics/layers/apz/test/mochitest/helper_iframe1.html
new file mode 100644
index 000000000..047da96bd
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_iframe1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<!-- The purpose of the 'id' on the HTML element is to get something
+ identifiable to show up in the root scroll frame's content description,
+ so we can check it for layerization. -->
+<html id="outer3">
+ <head>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ </head>
+ <body>
+ <div id="inner3" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_iframe2.html b/system/graphics/layers/apz/test/mochitest/helper_iframe2.html
new file mode 100644
index 000000000..fee3883e9
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_iframe2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<!-- The purpose of the 'id' on the HTML element is to get something
+ identifiable to show up in the root scroll frame's content description,
+ so we can check it for layerization. -->
+<html id="outer4">
+ <head>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ </head>
+ <body>
+ <div id="inner4" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_iframe_pan.html b/system/graphics/layers/apz/test/mochitest/helper_iframe_pan.html
new file mode 100644
index 000000000..47213f33a
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_iframe_pan.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test for scrollable div</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollOuter() {
+ var outer = document.getElementById('outer');
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll, outer.contentWindow);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(outer.contentDocument.body, 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ var outerScroll = document.getElementById('outer').contentWindow.scrollY;
+ is(outerScroll, 50, "check that the iframe scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollOuter);
+
+ </script>
+</head>
+<body>
+ <iframe id="outer" style="height: 250px; border: solid 1px black" src="data:text/html,<body style='height:5000px'>"></iframe>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the top-level page scrollable.
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_long_tap.html b/system/graphics/layers/apz/test/mochitest/helper_long_tap.html
new file mode 100644
index 000000000..604d03d64
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_long_tap.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Ensure we get a touch-cancel after a contextmenu comes up</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function longPressLink() {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
+ dump("Finished synthesizing touch-start, waiting for events...\n");
+ });
+}
+
+var eventsFired = 0;
+function recordEvent(e) {
+ if (getPlatform() == "windows") {
+ // On Windows we get a mouselongtap event once the long-tap has been detected
+ // by APZ, and that's what we use as the trigger to lift the finger. That then
+ // triggers the contextmenu. This matches the platform convention.
+ switch (eventsFired) {
+ case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
+ case 1:
+ is(e.type, 'mouselongtap', 'Got a mouselongtap');
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
+ break;
+ case 2: is(e.type, 'touchend', 'Got a touchend'); break;
+ case 3: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
+ default: ok(false, 'Got an unexpected event of type ' + e.type); break;
+ }
+ eventsFired++;
+
+ if (eventsFired == 4) {
+ dump("Finished waiting for events, doing an APZ flush to see if any more unexpected events come through...\n");
+ flushApzRepaints(function() {
+ dump("Done APZ flush, ending test...\n");
+ subtestDone();
+ });
+ }
+ } else {
+ // On non-Windows platforms we get a contextmenu event once the long-tap has
+ // been detected. Since we prevent-default that, we don't get a mouselongtap
+ // event at all, and instead get a touchcancel.
+ switch (eventsFired) {
+ case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
+ case 1: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
+ case 2: is(e.type, 'touchcancel', 'Got a touchcancel'); break;
+ default: ok(false, 'Got an unexpected event of type ' + e.type); break;
+ }
+ eventsFired++;
+
+ if (eventsFired == 3) {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
+ dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
+ flushApzRepaints(function() {
+ dump("Done APZ flush, ending test...\n");
+ subtestDone();
+ });
+ });
+ }
+ }
+}
+
+window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
+window.addEventListener('touchend', recordEvent, { passive: true, capture: true });
+window.addEventListener('touchcancel', recordEvent, true);
+window.addEventListener('contextmenu', recordEvent, true);
+SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
+
+waitUntilApzStable()
+.then(longPressLink);
+
+ </script>
+</head>
+<body>
+ <a id="b" href="#">Link to nowhere</a>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html b/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html
new file mode 100644
index 000000000..da866c1ce
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html
@@ -0,0 +1,46 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over inactive subframe with perspective</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var subframe = document.getElementById('scroll');
+
+ // scroll over the middle of the subframe, to make sure it scrolls,
+ // not the page
+ var scrollPos = subframe.scrollTop;
+ yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+ dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+ ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ #scroll {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ perspective: 400px;
+ }
+ #scrolled {
+ width: 200px;
+ height: 1000px; /* so the subframe has room to scroll */
+ background: linear-gradient(red, blue); /* so you can see it scroll */
+ transform: translateZ(0px); /* so the perspective makes it to the display list */
+ }
+ </style>
+</head>
+<body>
+ <div id="scroll">
+ <div id="scrolled"></div>
+ </div>
+ <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html b/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html
new file mode 100644
index 000000000..763aaf92b
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html
@@ -0,0 +1,47 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over inactive subframe with z-index</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var subframe = document.getElementById('scroll');
+
+ // scroll over the middle of the subframe, and make sure that it scrolls,
+ // not the page
+ var scrollPos = subframe.scrollTop;
+ yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+ dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+ ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ #scroll {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ }
+ #scrolled {
+ width: 200px;
+ height: 1000px; /* so the subframe has room to scroll */
+ z-index: 2;
+ background: linear-gradient(red, blue); /* so you can see it scroll */
+ transform: translateZ(0px); /* to force active layers */
+ will-change: transform; /* to force active layers */
+ }
+ </style>
+</head>
+<body>
+ <div id="scroll">
+ <div id="scrolled"></div>
+ </div>
+ <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html b/system/graphics/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
new file mode 100644
index 000000000..b9d187faf
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
@@ -0,0 +1,62 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var iframeWin = document.getElementById('iframe').contentWindow;
+
+ // scroll over the middle of the iframe's position:sticky element, check
+ // that it scrolls the iframe
+ var scrollPos = iframeWin.scrollY;
+ yield moveMouseAndScrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
+ ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
+
+ // same, but using the iframe's position:fixed element
+ scrollPos = iframeWin.scrollY;
+ yield moveMouseAndScrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
+ ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
+
+ // same, but scrolling the scrollable frame *inside* the position:fixed item
+ var fpos = document.getElementById('fpos_scrollable');
+ scrollPos = fpos.scrollTop;
+ yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
+ ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
+ // wait for it to layerize fully and then try again
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ scrollPos = fpos.scrollTop;
+ yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
+ ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
+
+ // same, but using the top-level window's position:sticky element
+ scrollPos = window.scrollY;
+ yield moveMouseAndScrollWheelOver(document.body, 50, 150, testDriver);
+ ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
+
+ // same, but using the top-level window's position:fixed element
+ scrollPos = window.scrollY;
+ yield moveMouseAndScrollWheelOver(document.body, 250, 150, testDriver);
+ ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:fixed element");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="height:5000px; margin:0">
+ <div style="position:sticky; width: 100px; height: 300px; top: 0; background-color:red">sticky</div>
+ <div style="position:fixed; width: 100px; height: 300px; top: 0; left: 200px; background-color: green">fixed</div>
+ <iframe id='iframe' width="300" height="400" src="data:text/html,<body style='height:5000px; margin:0'><div style='position:sticky; width:100px; height:300px; top: 0; background-color:red'>sticky</div><div style='position:fixed; right:0; top: 0; width:100px; height:300px; background-color:green'>fixed</div>"></iframe>
+
+ <div id="fpos_scrollable" style="position:fixed; width: 100px; height: 300px; top: 0; left: 400px; background-color: red; overflow:scroll">
+ <div style="background-color: blue; height: 1000px; margin: 3px">scrollable content inside a fixed-pos item</div>
+ </div>
+</body>
+</head>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_scrollto_tap.html b/system/graphics/layers/apz/test/mochitest/helper_scrollto_tap.html
new file mode 100644
index 000000000..fc444f2b7
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_scrollto_tap.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function startTest() {
+ if (window.scrollY == 0) {
+ // the scrollframe is not yet marked as APZ-scrollable. Mark it so and
+ // start over.
+ window.scrollTo(0, 1);
+ waitForApzFlushedRepaints(startTest);
+ return;
+ }
+
+ // This is a scroll by 20px that should use paint-skipping if possible.
+ // If paint-skipping is enabled, this should not trigger a paint, but go
+ // directly to the compositor using an empty transaction. We check for this
+ // by ensuring the document element did not get painted.
+ var utils = window.opener.SpecialPowers.getDOMWindowUtils(window);
+ var elem = document.documentElement;
+ var skipping = location.search == '?true';
+ utils.checkAndClearPaintedState(elem);
+ window.scrollTo(0, 20);
+ waitForAllPaints(function() {
+ if (skipping) {
+ is(utils.checkAndClearPaintedState(elem), false, "Document element didn't get painted");
+ }
+ // After that's done, we click on the button to make sure the
+ // skipped-paint codepath still has working APZ event transformations.
+ clickButton();
+ });
+}
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable().then(startTest);
+
+ </script>
+</head>
+<body style="height: 5000px">
+ <div style="height: 50px">spacer</div>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_subframe_style.css b/system/graphics/layers/apz/test/mochitest/helper_subframe_style.css
new file mode 100644
index 000000000..5af964080
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_subframe_style.css
@@ -0,0 +1,15 @@
+body {
+ height: 500px;
+}
+
+.inner-frame {
+ margin-top: 50px; /* this should be at least 30px */
+ height: 200%;
+ width: 75%;
+ overflow: scroll;
+}
+.inner-content {
+ height: 200%;
+ width: 200%;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+}
diff --git a/system/graphics/layers/apz/test/mochitest/helper_tall.html b/system/graphics/layers/apz/test/mochitest/helper_tall.html
new file mode 100644
index 000000000..7fde795fd
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_tall.html
@@ -0,0 +1,504 @@
+<html id="tall_html">
+<body>
+This is a tall page<br/>
+1<br/>
+2<br/>
+3<br/>
+4<br/>
+5<br/>
+6<br/>
+7<br/>
+8<br/>
+9<br/>
+10<br/>
+11<br/>
+12<br/>
+13<br/>
+14<br/>
+15<br/>
+16<br/>
+17<br/>
+18<br/>
+19<br/>
+20<br/>
+21<br/>
+22<br/>
+23<br/>
+24<br/>
+25<br/>
+26<br/>
+27<br/>
+28<br/>
+29<br/>
+30<br/>
+31<br/>
+32<br/>
+33<br/>
+34<br/>
+35<br/>
+36<br/>
+37<br/>
+38<br/>
+39<br/>
+40<br/>
+41<br/>
+42<br/>
+43<br/>
+44<br/>
+45<br/>
+46<br/>
+47<br/>
+48<br/>
+49<br/>
+50<br/>
+51<br/>
+52<br/>
+53<br/>
+54<br/>
+55<br/>
+56<br/>
+57<br/>
+58<br/>
+59<br/>
+60<br/>
+61<br/>
+62<br/>
+63<br/>
+64<br/>
+65<br/>
+66<br/>
+67<br/>
+68<br/>
+69<br/>
+70<br/>
+71<br/>
+72<br/>
+73<br/>
+74<br/>
+75<br/>
+76<br/>
+77<br/>
+78<br/>
+79<br/>
+80<br/>
+81<br/>
+82<br/>
+83<br/>
+84<br/>
+85<br/>
+86<br/>
+87<br/>
+88<br/>
+89<br/>
+90<br/>
+91<br/>
+92<br/>
+93<br/>
+94<br/>
+95<br/>
+96<br/>
+97<br/>
+98<br/>
+99<br/>
+100<br/>
+101<br/>
+102<br/>
+103<br/>
+104<br/>
+105<br/>
+106<br/>
+107<br/>
+108<br/>
+109<br/>
+110<br/>
+111<br/>
+112<br/>
+113<br/>
+114<br/>
+115<br/>
+116<br/>
+117<br/>
+118<br/>
+119<br/>
+120<br/>
+121<br/>
+122<br/>
+123<br/>
+124<br/>
+125<br/>
+126<br/>
+127<br/>
+128<br/>
+129<br/>
+130<br/>
+131<br/>
+132<br/>
+133<br/>
+134<br/>
+135<br/>
+136<br/>
+137<br/>
+138<br/>
+139<br/>
+140<br/>
+141<br/>
+142<br/>
+143<br/>
+144<br/>
+145<br/>
+146<br/>
+147<br/>
+148<br/>
+149<br/>
+150<br/>
+151<br/>
+152<br/>
+153<br/>
+154<br/>
+155<br/>
+156<br/>
+157<br/>
+158<br/>
+159<br/>
+160<br/>
+161<br/>
+162<br/>
+163<br/>
+164<br/>
+165<br/>
+166<br/>
+167<br/>
+168<br/>
+169<br/>
+170<br/>
+171<br/>
+172<br/>
+173<br/>
+174<br/>
+175<br/>
+176<br/>
+177<br/>
+178<br/>
+179<br/>
+180<br/>
+181<br/>
+182<br/>
+183<br/>
+184<br/>
+185<br/>
+186<br/>
+187<br/>
+188<br/>
+189<br/>
+190<br/>
+191<br/>
+192<br/>
+193<br/>
+194<br/>
+195<br/>
+196<br/>
+197<br/>
+198<br/>
+199<br/>
+200<br/>
+201<br/>
+202<br/>
+203<br/>
+204<br/>
+205<br/>
+206<br/>
+207<br/>
+208<br/>
+209<br/>
+210<br/>
+211<br/>
+212<br/>
+213<br/>
+214<br/>
+215<br/>
+216<br/>
+217<br/>
+218<br/>
+219<br/>
+220<br/>
+221<br/>
+222<br/>
+223<br/>
+224<br/>
+225<br/>
+226<br/>
+227<br/>
+228<br/>
+229<br/>
+230<br/>
+231<br/>
+232<br/>
+233<br/>
+234<br/>
+235<br/>
+236<br/>
+237<br/>
+238<br/>
+239<br/>
+240<br/>
+241<br/>
+242<br/>
+243<br/>
+244<br/>
+245<br/>
+246<br/>
+247<br/>
+248<br/>
+249<br/>
+250<br/>
+251<br/>
+252<br/>
+253<br/>
+254<br/>
+255<br/>
+256<br/>
+257<br/>
+258<br/>
+259<br/>
+260<br/>
+261<br/>
+262<br/>
+263<br/>
+264<br/>
+265<br/>
+266<br/>
+267<br/>
+268<br/>
+269<br/>
+270<br/>
+271<br/>
+272<br/>
+273<br/>
+274<br/>
+275<br/>
+276<br/>
+277<br/>
+278<br/>
+279<br/>
+280<br/>
+281<br/>
+282<br/>
+283<br/>
+284<br/>
+285<br/>
+286<br/>
+287<br/>
+288<br/>
+289<br/>
+290<br/>
+291<br/>
+292<br/>
+293<br/>
+294<br/>
+295<br/>
+296<br/>
+297<br/>
+298<br/>
+299<br/>
+300<br/>
+301<br/>
+302<br/>
+303<br/>
+304<br/>
+305<br/>
+306<br/>
+307<br/>
+308<br/>
+309<br/>
+310<br/>
+311<br/>
+312<br/>
+313<br/>
+314<br/>
+315<br/>
+316<br/>
+317<br/>
+318<br/>
+319<br/>
+320<br/>
+321<br/>
+322<br/>
+323<br/>
+324<br/>
+325<br/>
+326<br/>
+327<br/>
+328<br/>
+329<br/>
+330<br/>
+331<br/>
+332<br/>
+333<br/>
+334<br/>
+335<br/>
+336<br/>
+337<br/>
+338<br/>
+339<br/>
+340<br/>
+341<br/>
+342<br/>
+343<br/>
+344<br/>
+345<br/>
+346<br/>
+347<br/>
+348<br/>
+349<br/>
+350<br/>
+351<br/>
+352<br/>
+353<br/>
+354<br/>
+355<br/>
+356<br/>
+357<br/>
+358<br/>
+359<br/>
+360<br/>
+361<br/>
+362<br/>
+363<br/>
+364<br/>
+365<br/>
+366<br/>
+367<br/>
+368<br/>
+369<br/>
+370<br/>
+371<br/>
+372<br/>
+373<br/>
+374<br/>
+375<br/>
+376<br/>
+377<br/>
+378<br/>
+379<br/>
+380<br/>
+381<br/>
+382<br/>
+383<br/>
+384<br/>
+385<br/>
+386<br/>
+387<br/>
+388<br/>
+389<br/>
+390<br/>
+391<br/>
+392<br/>
+393<br/>
+394<br/>
+395<br/>
+396<br/>
+397<br/>
+398<br/>
+399<br/>
+400<br/>
+401<br/>
+402<br/>
+403<br/>
+404<br/>
+405<br/>
+406<br/>
+407<br/>
+408<br/>
+409<br/>
+410<br/>
+411<br/>
+412<br/>
+413<br/>
+414<br/>
+415<br/>
+416<br/>
+417<br/>
+418<br/>
+419<br/>
+420<br/>
+421<br/>
+422<br/>
+423<br/>
+424<br/>
+425<br/>
+426<br/>
+427<br/>
+428<br/>
+429<br/>
+430<br/>
+431<br/>
+432<br/>
+433<br/>
+434<br/>
+435<br/>
+436<br/>
+437<br/>
+438<br/>
+439<br/>
+440<br/>
+441<br/>
+442<br/>
+443<br/>
+444<br/>
+445<br/>
+446<br/>
+447<br/>
+448<br/>
+449<br/>
+450<br/>
+451<br/>
+452<br/>
+453<br/>
+454<br/>
+455<br/>
+456<br/>
+457<br/>
+458<br/>
+459<br/>
+460<br/>
+461<br/>
+462<br/>
+463<br/>
+464<br/>
+465<br/>
+466<br/>
+467<br/>
+468<br/>
+469<br/>
+470<br/>
+471<br/>
+472<br/>
+473<br/>
+474<br/>
+475<br/>
+476<br/>
+477<br/>
+478<br/>
+479<br/>
+480<br/>
+481<br/>
+482<br/>
+483<br/>
+484<br/>
+485<br/>
+486<br/>
+487<br/>
+488<br/>
+489<br/>
+490<br/>
+491<br/>
+492<br/>
+493<br/>
+494<br/>
+495<br/>
+496<br/>
+497<br/>
+498<br/>
+499<br/>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_tap.html b/system/graphics/layers/apz/test/mochitest/helper_tap.html
new file mode 100644
index 000000000..6fde9387d
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_tap.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable().then(clickButton);
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_tap_fullzoom.html b/system/graphics/layers/apz/test/mochitest/helper_tap_fullzoom.html
new file mode 100644
index 000000000..494363b9c
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_tap_fullzoom.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test with fullzoom</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+SpecialPowers.setFullZoom(window, 2.0);
+waitUntilApzStable().then(clickButton);
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px; position: relative; top: 100px"></button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_tap_passive.html b/system/graphics/layers/apz/test/mochitest/helper_tap_passive.html
new file mode 100644
index 000000000..dc3d85ed2
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_tap_passive.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Ensure APZ doesn't wait for passive listeners</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+var touchdownTime;
+
+function longPressLink() {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
+ dump("Finished synthesizing touch-start, waiting for events...\n");
+ });
+}
+
+var touchstartReceived = false;
+function recordEvent(e) {
+ if (!touchstartReceived) {
+ touchstartReceived = true;
+ is(e.type, 'touchstart', 'Got a touchstart');
+ e.preventDefault(); // should be a no-op because it's a passive listener
+ return;
+ }
+
+ // If APZ decides to wait for the content response on a particular input block,
+ // it needs to wait until both the touchstart and touchmove event are handled
+ // by the main thread. In this case there is no touchmove at all, so APZ would
+ // end up waiting indefinitely and time out the test. The fact that we get this
+ // contextmenu event (mouselongtap on Windows) at all means that APZ decided
+ // not to wait for the content response, which is the desired behaviour, since
+ // the touchstart listener was registered as a passive listener.
+ if (getPlatform() == "windows") {
+ is(e.type, 'mouselongtap', 'Got a mouselongtap');
+ } else {
+ is(e.type, 'contextmenu', 'Got a contextmenu');
+ }
+ e.preventDefault();
+
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
+ dump("Finished synthesizing touch-end to clear state; finishing test...\n");
+ subtestDone();
+ });
+}
+
+window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
+if (getPlatform() == "windows") {
+ SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
+} else {
+ window.addEventListener('contextmenu', recordEvent, true);
+}
+
+waitUntilApzStable()
+.then(longPressLink);
+
+ </script>
+</head>
+<body>
+ <a id="b" href="#">Link to nowhere</a>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_touch_action.html b/system/graphics/layers/apz/test/mochitest/helper_touch_action.html
new file mode 100644
index 000000000..4495dc76e
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_touch_action.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-action test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function checkScroll(x, y, desc) {
+ is(window.scrollX, x, desc + " - x axis");
+ is(window.scrollY, y, desc + " - y axis");
+}
+
+function* test(testDriver) {
+ var target = document.getElementById('target');
+
+ document.body.addEventListener('touchend', testDriver, { passive: true });
+
+ // drag the page up to scroll down by 50px
+ yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
+ "Synthesized native vertical drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(0, 50, "After first vertical drag, with pan-y" );
+
+ // switch style to pan-x
+ document.body.style.touchAction = 'pan-x';
+ ok(true, "Waiting for pan-x to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page up to scroll down by 50px, but it won't happen because pan-x
+ yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
+ "Synthesized native vertical drag (2), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(0, 50, "After second vertical drag, with pan-x");
+
+ // drag the page left to scroll right by 50px
+ yield ok(synthesizeNativeTouchDrag(target, 100, 10, -(50 + TOUCH_SLOP), 0),
+ "Synthesized horizontal drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(50, 50, "After first horizontal drag, with pan-x");
+
+ // drag the page diagonally right/down to scroll up/left by 40px each axis;
+ // only the x-axis will actually scroll because pan-x
+ yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 50, "After first diagonal drag, with pan-x");
+
+ // switch style back to pan-y
+ document.body.style.touchAction = 'pan-y';
+ ok(true, "Waiting for pan-y to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally right/down to scroll up/left by 40px each axis;
+ // only the y-axis will actually scroll because pan-y
+ yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (2), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 10, "After second diagonal drag, with pan-y");
+
+ // switch style to none
+ document.body.style.touchAction = 'none';
+ ok(true, "Waiting for none to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally up/left to scroll down/right by 40px each axis;
+ // neither will scroll because of touch-action
+ yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (3), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 10, "After third diagonal drag, with none");
+
+ document.body.style.touchAction = 'manipulation';
+ ok(true, "Waiting for manipulation to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally up/left to scroll down/right by 40px each axis;
+ // both will scroll because of touch-action
+ yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (4), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(50, 50, "After fourth diagonal drag, with manipulation");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="touch-action: pan-y">
+ <div style="width: 5000px; height: 5000px; background-color: lightgreen;">
+ This div makes the page scrollable on both axes.<br>
+ This is the second line of text.<br>
+ This is the third line of text.<br>
+ This is the fourth line of text.
+ </div>
+ <!-- This fixed-position div remains in the same place relative to the browser chrome, so we
+ can use it as a targeting device for synthetic touch events. The body will move around
+ as we scroll, so we'd have to be constantly adjusting the synthetic drag coordinates
+ if we used that as the target element. -->
+ <div style="position:fixed; left: 10px; top: 10px; width: 1px; height: 1px" id="target"></div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_touch_action_complex.html b/system/graphics/layers/apz/test/mochitest/helper_touch_action_complex.html
new file mode 100644
index 000000000..11d6e66e1
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_touch_action_complex.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Complex touch-action test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function checkScroll(target, x, y, desc) {
+ is(target.scrollLeft, x, desc + " - x axis");
+ is(target.scrollTop, y, desc + " - y axis");
+}
+
+function resetConfiguration(config, testDriver) {
+ // Cycle through all the configuration_X elements, setting them to display:none
+ // except for when X == config, in which case set it to display:block
+ var i = 0;
+ while (true) {
+ i++;
+ var element = document.getElementById('configuration_' + i);
+ if (element == null) {
+ if (i <= config) {
+ ok(false, "The configuration requested was not encountered!");
+ }
+ break;
+ }
+
+ if (i == config) {
+ element.style.display = 'block';
+ } else {
+ element.style.display = 'none';
+ }
+ }
+
+ // Also reset the scroll position on the scrollframe
+ var s = document.getElementById('scrollframe');
+ s.scrollLeft = 0;
+ s.scrollTop = 0;
+
+ return waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+}
+
+function* test(testDriver) {
+ var scrollframe = document.getElementById('scrollframe');
+
+ document.body.addEventListener('touchend', testDriver, { passive: true });
+
+ // Helper function for the tests below.
+ // Touch-pan configuration |configuration| towards scroll offset (dx, dy) with
+ // the pan touching down at (x, y). Check that the final scroll offset is
+ // (ex, ey). |desc| is some description string.
+ function* scrollAndCheck(configuration, x, y, dx, dy, ex, ey, desc) {
+ // Start with a clean slate
+ yield resetConfiguration(configuration, testDriver);
+ // Figure out the panning deltas
+ if (dx != 0) {
+ dx = -(dx + TOUCH_SLOP);
+ }
+ if (dy != 0) {
+ dy = -(dy + TOUCH_SLOP);
+ }
+ // Do the pan
+ yield ok(synthesizeNativeTouchDrag(scrollframe, x, y, dx, dy),
+ "Synthesized drag of (" + dx + ", " + dy + ") on configuration " + configuration);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Check for expected scroll position
+ checkScroll(scrollframe, ex, ey, 'configuration ' + configuration + ' ' + desc);
+ }
+
+ // Test configuration_1, which contains two sibling elements that are
+ // overlapping. The touch-action from the second sibling (which is on top)
+ // should be used for the overlapping area.
+ yield* scrollAndCheck(1, 25, 75, 20, 0, 20, 0, "first element horizontal scroll");
+ yield* scrollAndCheck(1, 25, 75, 0, 50, 0, 0, "first element vertical scroll");
+ yield* scrollAndCheck(1, 75, 75, 50, 0, 0, 0, "overlap horizontal scroll");
+ yield* scrollAndCheck(1, 75, 75, 0, 50, 0, 50, "overlap vertical scroll");
+ yield* scrollAndCheck(1, 125, 75, 20, 0, 0, 0, "second element horizontal scroll");
+ yield* scrollAndCheck(1, 125, 75, 0, 50, 0, 50, "second element vertical scroll");
+
+ // Test configuration_2, which contains two overlapping elements with a
+ // parent/child relationship. The parent has pan-x and the child has pan-y,
+ // which means that panning on the parent should work horizontally only, and
+ // on the child no panning should occur at all.
+ yield* scrollAndCheck(2, 125, 125, 50, 50, 0, 0, "child scroll");
+ yield* scrollAndCheck(2, 75, 75, 50, 50, 0, 0, "overlap scroll");
+ yield* scrollAndCheck(2, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
+ yield* scrollAndCheck(2, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
+
+ // Test configuration_3, which is the same as configuration_2, except the child
+ // has a rotation transform applied. This forces the event regions on the two
+ // elements to be built separately and then get merged.
+ yield* scrollAndCheck(3, 125, 125, 50, 50, 0, 0, "child scroll");
+ yield* scrollAndCheck(3, 75, 75, 50, 50, 0, 0, "overlap scroll");
+ yield* scrollAndCheck(3, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
+ yield* scrollAndCheck(3, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
+
+ // Test configuration_4 has two elements, one above the other, not overlapping,
+ // and the second element is a child of the first. The parent has pan-x, the
+ // child has pan-y, but that means panning horizontally on the parent should
+ // work and panning in any direction on the child should not do anything.
+ yield* scrollAndCheck(4, 75, 75, 50, 50, 50, 0, "parent diagonal scroll");
+ yield* scrollAndCheck(4, 75, 150, 50, 50, 0, 0, "child diagonal scroll");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body>
+ <div id="scrollframe" style="width: 300px; height: 300px; overflow:scroll">
+ <div id="scrolled_content" style="width: 1000px; height: 1000px; background-color: green">
+ </div>
+ <div id="configuration_1" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue"></div>
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: -100px; left: 50px; background-color: yellow"></div>
+ </div>
+ <div id="configuration_2" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow"></div>
+ </div>
+ </div>
+ <div id="configuration_3" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow; transform: rotate(90deg)"></div>
+ </div>
+ </div>
+ <div id="configuration_4" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 125px; background-color: yellow"></div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/helper_touch_action_regions.html b/system/graphics/layers/apz/test/mochitest/helper_touch_action_regions.html
new file mode 100644
index 000000000..cbd4cd61d
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/helper_touch_action_regions.html
@@ -0,0 +1,246 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test to ensure APZ doesn't always wait for touch-action</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function failure(e) {
+ ok(false, "This event listener should not have triggered: " + e.type);
+}
+
+function success(e) {
+ success.triggered = true;
+}
+
+// This helper function provides a way for the child process to synchronously
+// check how many touch events the chrome process main-thread has processed. This
+// function can be called with three values: 'start', 'report', and 'end'.
+// The 'start' invocation sets up the listeners, and should be invoked before
+// the touch events of interest are generated. This should only be called once.
+// This returns true on success, and false on failure.
+// The 'report' invocation can be invoked multiple times, and returns an object
+// (in JSON string format) containing the counters.
+// The 'end' invocation tears down the listeners, and should be invoked once
+// at the end to clean up. Returns true on success, false on failure.
+function chromeTouchEventCounter(operation) {
+ function chromeProcessCounter() {
+ addMessageListener('start', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ if (typeof topWin.eventCounts != 'undefined') {
+ dump('Found pre-existing eventCounts object on the top window!\n');
+ return false;
+ }
+ topWin.eventCounts = { 'touchstart': 0, 'touchmove': 0, 'touchend': 0 };
+ topWin.counter = function(e) {
+ topWin.eventCounts[e.type]++;
+ }
+
+ topWin.addEventListener('touchstart', topWin.counter, { passive: true });
+ topWin.addEventListener('touchmove', topWin.counter, { passive: true });
+ topWin.addEventListener('touchend', topWin.counter, { passive: true });
+
+ return true;
+ });
+
+ addMessageListener('report', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ return JSON.stringify(topWin.eventCounts);
+ });
+
+ addMessageListener('end', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ if (typeof topWin.eventCounts == 'undefined') {
+ dump('The eventCounts object was not found on the top window!\n');
+ return false;
+ }
+ topWin.removeEventListener('touchstart', topWin.counter);
+ topWin.removeEventListener('touchmove', topWin.counter);
+ topWin.removeEventListener('touchend', topWin.counter);
+ delete topWin.counter;
+ delete topWin.eventCounts;
+ return true;
+ });
+ }
+
+ if (typeof chromeTouchEventCounter.chromeHelper == 'undefined') {
+ // This is the first time getSnapshot is being called; do initialization
+ chromeTouchEventCounter.chromeHelper = SpecialPowers.loadChromeScript(chromeProcessCounter);
+ SimpleTest.registerCleanupFunction(function() { chromeTouchEventCounter.chromeHelper.destroy() });
+ }
+
+ return chromeTouchEventCounter.chromeHelper.sendSyncMessage(operation, "");
+}
+
+// Simple wrapper that waits until the chrome process has seen |count| instances
+// of the |eventType| event. Returns true on success, and false if 10 seconds
+// go by without the condition being satisfied.
+function waitFor(eventType, count) {
+ var start = Date.now();
+ while (JSON.parse(chromeTouchEventCounter('report'))[eventType] != count) {
+ if (Date.now() - start > 10000) {
+ // It's taking too long, let's abort
+ return false;
+ }
+ }
+ return true;
+}
+
+function* test(testDriver) {
+ // The main part of this test should run completely before the child process'
+ // main-thread deals with the touch event, so check to make sure that happens.
+ document.body.addEventListener('touchstart', failure, { passive: true });
+
+ // What we want here is to synthesize all of the touch events (from this code in
+ // the child process), and have the chrome process generate and process them,
+ // but not allow the events to be dispatched back into the child process until
+ // later. This allows us to ensure that the APZ in the chrome process is not
+ // waiting for the child process to send notifications upon processing the
+ // events. If it were doing so, the APZ would block and this test would fail.
+
+ // In order to actually implement this, we call the synthesize functions with
+ // a async callback in between. The synthesize functions just queue up a
+ // runnable on the child process main thread and return immediately, so with
+ // the async callbacks, the child process main thread queue looks like
+ // this after we're done setting it up:
+ // synthesizeTouchStart
+ // callback testDriver
+ // synthesizeTouchMove
+ // callback testDriver
+ // ...
+ // synthesizeTouchEnd
+ // callback testDriver
+ //
+ // If, after setting up this queue, we yield once, the first synthesization and
+ // callback will run - this will send a synthesization message to the chrome
+ // process, and return control back to us right away. When the chrome process
+ // processes with the synthesized event, it will dispatch the DOM touch event
+ // back to the child process over IPC, which will go into the end of the child
+ // process main thread queue, like so:
+ // synthesizeTouchStart (done)
+ // invoke testDriver (done)
+ // synthesizeTouchMove
+ // invoke testDriver
+ // ...
+ // synthesizeTouchEnd
+ // invoke testDriver
+ // handle DOM touchstart <-- touchstart goes at end of queue
+ //
+ // As we continue yielding one at a time, the synthesizations run, and the
+ // touch events get added to the end of the queue. As we yield, we take
+ // snapshots in the chrome process, to make sure that the APZ has started
+ // scrolling even though we know we haven't yet processed the DOM touch events
+ // in the child process yet.
+ //
+ // Note that the "async callback" we use here is SpecialPowers.executeSoon,
+ // because nothing else does exactly what we want:
+ // - setTimeout(..., 0) does not maintain ordering, because it respects the
+ // time delta provided (i.e. the callback can jump the queue to meet its
+ // deadline).
+ // - SpecialPowers.spinEventLoop and SpecialPowers.executeAfterFlushingMessageQueue
+ // are not e10s friendly, and can get arbitrarily delayed due to IPC
+ // round-trip time.
+ // - SimpleTest.executeSoon has a codepath that delegates to setTimeout, so
+ // is less reliable if it ever decides to switch to that codepath.
+
+ // The other problem we need to deal with is the asynchronicity in the chrome
+ // process. That is, we might request a snapshot before the chrome process has
+ // actually synthesized the event and processed it. To guard against this, we
+ // register a thing in the chrome process that counts the touch events that
+ // have been dispatched, and poll that thing synchronously in order to make
+ // sure we only snapshot after the event in question has been processed.
+ // That's what the chromeTouchEventCounter business is all about. The sync
+ // polling looks bad but in practice only ends up needing to poll once or
+ // twice before the condition is satisfied, and as an extra precaution we add
+ // a time guard so it fails after 10s of polling.
+
+ // So, here we go...
+
+ // Set up the chrome process touch listener
+ ok(chromeTouchEventCounter('start'), "Chrome touch counter registered");
+
+ // Set up the child process events and callbacks
+ var scroller = document.getElementById('scroller');
+ synthesizeNativeTouch(scroller, 10, 110, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ for (var i = 1; i < 10; i++) {
+ synthesizeNativeTouch(scroller, 10, 110 - (i * 10), SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ }
+ synthesizeNativeTouch(scroller, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ ok(true, "Finished setting up event queue");
+
+ // Get our baseline snapshot
+ var rect = rectRelativeToScreen(scroller);
+ var lastSnapshot = getSnapshot(rect);
+ ok(true, "Got baseline snapshot");
+
+ yield; // this will tell the chrome process to synthesize the touchstart event
+ // and then we wait to make sure it got processed:
+ ok(waitFor('touchstart', 1), "Touchstart processed in chrome process");
+
+ // Loop through the touchmove events
+ for (var i = 1; i < 10; i++) {
+ yield;
+ ok(waitFor('touchmove', i), "Touchmove processed in chrome process");
+
+ var snapshot = getSnapshot(rect);
+ if (i == 1) {
+ // The first touchmove is consumed to get us into the panning state, so
+ // no actual panning occurs
+ ok(lastSnapshot == snapshot, "Snapshot 1 was the same as baseline");
+ } else {
+ ok(lastSnapshot != snapshot, "Snapshot " + i + " was different from the previous one");
+ }
+ lastSnapshot = snapshot;
+ }
+
+ // Wait for the touchend as well, just for good form
+ yield;
+ ok(waitFor('touchend', 1), "Touchend processed in chrome process");
+
+ // Clean up the chrome process hooks
+ chromeTouchEventCounter('end');
+
+ // Now we are going to release our grip on the child process main thread,
+ // so that all the DOM events that were queued up can be processed. We
+ // register a touchstart listener to make sure this happens.
+ document.body.removeEventListener('touchstart', failure);
+ document.body.addEventListener('touchstart', success, { passive: true });
+ yield flushApzRepaints(testDriver);
+ ok(success.triggered, "The touchstart event handler was triggered after snapshotting completed");
+ document.body.removeEventListener('touchstart', success);
+}
+
+if (SpecialPowers.isMainProcess()) {
+ // This is probably android, where everything is single-process. The
+ // test structure depends on e10s, so the test won't run properly on
+ // this platform. Skip it
+ ok(true, "Skipping test because it is designed to run from the content process");
+ subtestDone();
+} else {
+ waitUntilApzStable()
+ .then(runContinuation(test))
+ .then(subtestDone);
+}
+
+ </script>
+</head>
+<body>
+ <div id="scroller" style="width: 400px; height: 400px; overflow: scroll; touch-action: pan-y">
+ <div style="width: 200px; height: 200px; background-color: lightgreen;">
+ This is a colored div that will move on the screen as the scroller scrolls.
+ </div>
+ <div style="width: 1000px; height: 1000px; background-color: lightblue">
+ This is a large div to make the scroller scrollable.
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/mochitest.ini b/system/graphics/layers/apz/test/mochitest/mochitest.ini
new file mode 100644
index 000000000..09e62428c
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/mochitest.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+ support-files =
+ apz_test_native_event_utils.js
+ apz_test_utils.js
+ helper_basic_pan.html
+ helper_bug982141.html
+ helper_bug1151663.html
+ helper_bug1162771.html
+ helper_bug1271432.html
+ helper_bug1280013.html
+ helper_bug1285070.html
+ helper_bug1299195.html
+ helper_click.html
+ helper_div_pan.html
+ helper_drag_click.html
+ helper_drag_scroll.html
+ helper_iframe_pan.html
+ helper_iframe1.html
+ helper_iframe2.html
+ helper_long_tap.html
+ helper_scroll_inactive_perspective.html
+ helper_scroll_inactive_zindex.html
+ helper_scroll_on_position_fixed.html
+ helper_scrollto_tap.html
+ helper_subframe_style.css
+ helper_tall.html
+ helper_tap.html
+ helper_tap_fullzoom.html
+ helper_tap_passive.html
+ helper_touch_action.html
+ helper_touch_action_regions.html
+ helper_touch_action_complex.html
+ tags = apz
+[test_bug982141.html]
+[test_bug1151663.html]
+[test_bug1151667.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1253683.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1277814.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1304689.html]
+[test_bug1304689-2.html]
+[test_frame_reconstruction.html]
+[test_group_mouseevents.html]
+ skip-if = (toolkit == 'android') # mouse events not supported on mobile
+[test_group_pointerevents.html]
+[test_group_touchevents.html]
+[test_group_wheelevents.html]
+ skip-if = (toolkit == 'android') # wheel events not supported on mobile
+[test_group_zoom.html]
+ skip-if = (toolkit != 'android') # only android supports zoom
+[test_interrupted_reflow.html]
+[test_layerization.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_inactive_bug1190112.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_inactive_flattened_frame.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_subframe_scrollbar.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_touch_listeners_impacting_wheel.html]
+ skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
+[test_wheel_scroll.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_wheel_transactions.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1151663.html b/system/graphics/layers/apz/test/mochitest/test_bug1151663.html
new file mode 100644
index 000000000..10810c6ca
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1151663.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1151663</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Run the actual test in its own window, because it requires that the
+ // root APZC be scrollable. Mochitest pages themselves often run
+ // inside an iframe which means we have no control over the root APZC.
+ var w = null;
+ window.onload = function() {
+ pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
+ w = window.open("helper_bug1151663.html", "_blank");
+ });
+ };
+ }
+
+ function finishTest() {
+ w.close();
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1151667.html b/system/graphics/layers/apz/test/mochitest/test_bug1151667.html
new file mode 100644
index 000000000..88facf6e9
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1151667.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151667
+-->
+<head>
+ <title>Test for Bug 1151667</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #subframe {
+ margin-top: 100px;
+ height: 500px;
+ width: 500px;
+ overflow: scroll;
+ }
+ #subframe-content {
+ height: 1000px;
+ width: 500px;
+ /* the background is so that we can see it scroll*/
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ #page-content {
+ height: 5000px;
+ width: 500px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151667">Mozilla Bug 1151667</a>
+<p id="display"></p>
+<div id="subframe">
+ <!-- This makes sure the subframe is scrollable -->
+ <div id="subframe-content"></div>
+</div>
+<!-- This makes sure the page is also scrollable, so it (rather than the subframe)
+ is considered the primary async-scrollable frame, and so the subframe isn't
+ layerized upon page load. -->
+<div id="page-content"></div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function startTest() {
+ var subframe = document.getElementById('subframe');
+ synthesizeNativeWheelAndWaitForScrollEvent(subframe, 100, 150, 0, -10, continueTest);
+}
+
+function continueTest() {
+ var subframe = document.getElementById('subframe');
+ is(subframe.scrollTop > 0, true, "We should have scrolled the subframe down");
+ is(document.documentElement.scrollTop, 0, "We should not have scrolled the page");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+waitUntilApzStable().then(startTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1253683.html b/system/graphics/layers/apz/test/mochitest/test_bug1253683.html
new file mode 100644
index 000000000..52c8e4a96
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1253683.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1253683
+-->
+<head>
+ <title>Test to ensure non-scrollable frames don't get layerized</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="container" style="height: 500px; overflow:scroll">
+ <pre id="no_layer" style="background-color: #f5f5f5; margin: 15px; padding: 15px; margin-top: 100px; border: 1px solid #eee; overflow:scroll">sample code here</pre>
+ <div style="height: 5000px">spacer to make the 'container' div the root scrollable element</div>
+ </div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function* test(testDriver) {
+ var container = document.getElementById('container');
+ var no_layer = document.getElementById('no_layer');
+
+ // Check initial state
+ is(container.scrollTop, 0, "Initial scrollY should be 0");
+ ok(!isLayerized('no_layer'), "initially 'no_layer' should not be layerized");
+
+ // Scrolling over outer1 should layerize outer1, but not inner1.
+ yield moveMouseAndScrollWheelOver(no_layer, 10, 10, testDriver, true);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ ok(container.scrollTop > 0, "We should have scrolled the body");
+ ok(!isLayerized('no_layer'), "no_layer should still not be layerized");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Turn off displayport expiry so that we don't miss failures where the
+ // displayport is set and expired before we check for layerization.
+ // Also enable APZ test logging, since we use that data to determine whether
+ // a scroll frame was layerized.
+ pushPrefs([["apz.displayport_expiry_ms", 0],
+ ["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1277814.html b/system/graphics/layers/apz/test/mochitest/test_bug1277814.html
new file mode 100644
index 000000000..877286468
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1277814.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1277814
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1277814</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ function* test(testDriver) {
+ // Trigger the buggy scenario
+ var subframe = document.getElementById('bug1277814-div');
+ subframe.classList.add('a');
+
+ // The transform change is animated, so let's step through 1s of animation
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ // Wait for the layer tree with any updated dispatch-to-content region to
+ // get pushed over to the APZ
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Trigger layerization of the subframe by scrolling the wheel over it
+ yield moveMouseAndScrollWheelOver(subframe, 10, 10, testDriver);
+
+ // Give APZ the chance to compute a displayport, and content
+ // to render based on it.
+ yield waitForApzFlushedRepaints(testDriver);
+
+ // Examine the content-side APZ test data
+ var contentTestData = utils.getContentAPZTestData();
+
+ // Test that the scroll frame for the div 'bug1277814-div' appears in
+ // the APZ test data. The bug this test is for causes the displayport
+ // calculation for this scroll frame to go wrong, causing it not to
+ // become layerized.
+ contentTestData = convertTestData(contentTestData);
+ var foundIt = false;
+ for (var seqNo in contentTestData.paints) {
+ var paint = contentTestData.paints[seqNo];
+ for (var scrollId in paint) {
+ var scrollFrame = paint[scrollId];
+ if ('contentDescription' in scrollFrame &&
+ scrollFrame['contentDescription'].includes('bug1277814-div')) {
+ foundIt = true;
+ }
+ }
+ }
+ SimpleTest.ok(foundIt, "expected to find APZ test data for bug1277814-div");
+ }
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ pushPrefs([["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+ }
+ </script>
+ <style>
+ #bug1277814-div
+ {
+ position: absolute;
+ left: 0;
+ top: 0;
+ padding: .5em;
+ overflow: auto;
+ color: white;
+ background: green;
+ max-width: 30em;
+ max-height: 6em;
+ visibility: hidden;
+ transform: scaleY(0);
+ transition: transform .15s ease-out, visibility 0s ease .15s;
+ }
+ #bug1277814-div.a
+ {
+ visibility: visible;
+ transform: scaleY(1);
+ transition: transform .15s ease-out;
+ }
+ </style>
+</head>
+<body>
+ <!-- Use a unique id because we'll be checking for it in the content
+ description logged in the APZ test data -->
+ <div id="bug1277814-div">
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ <button>click me</button>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1304689-2.html b/system/graphics/layers/apz/test/mochitest/test_bug1304689-2.html
new file mode 100644
index 000000000..356d7bcb3
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1304689-2.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #outer {
+ height: 400px;
+ width: 415px;
+ overflow: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ #outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+ var elm = document.getElementById('outer');
+
+ // Set margins on the element, to ensure it is layerized
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Start a smooth-scroll animation in the compositor and let it go a few
+ // frames, so that there is some "user scrolling" going on (per the comment
+ // in AsyncPanZoomController::NotifyLayersUpdated)
+ elm.scrollTop = 10;
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+
+ // Do another scroll update but also do a frame reconstruction within the same
+ // tick of the refresh driver.
+ elm.scrollTop = 100;
+ elm.classList.add('contentBefore');
+
+ // Now let everything settle and all the animations run out
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ yield flushApzRepaints(testDriver);
+ is(elm.scrollTop, 100, "The scrollTop now should be y=100");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ pushPrefs([["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+ </script>
+</head>
+<body>
+ <div id="outer">
+ <div id="inner">
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug1304689.html b/system/graphics/layers/apz/test/mochitest/test_bug1304689.html
new file mode 100644
index 000000000..a64f8a34e
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug1304689.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #outer {
+ height: 400px;
+ width: 415px;
+ overflow: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ #outer.instant {
+ scroll-behavior: auto;
+ }
+ #outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+ var elm = document.getElementById('outer');
+
+ // Set margins on the element, to ensure it is layerized
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Start a smooth-scroll animation in the compositor and let it go a few
+ // frames, so that there is some "user scrolling" going on (per the comment
+ // in AsyncPanZoomController::NotifyLayersUpdated)
+ elm.scrollTop = 10;
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+
+ // Do another scroll update but also do a frame reconstruction within the same
+ // tick of the refresh driver.
+ elm.classList.add('instant');
+ elm.scrollTop = 100;
+ elm.classList.add('contentBefore');
+
+ // Now let everything settle and all the animations run out
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ yield flushApzRepaints(testDriver);
+ is(elm.scrollTop, 100, "The scrollTop now should be y=100");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ pushPrefs([["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+ </script>
+</head>
+<body>
+ <div id="outer">
+ <div id="inner">
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_bug982141.html b/system/graphics/layers/apz/test/mochitest/test_bug982141.html
new file mode 100644
index 000000000..9984b79ff
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_bug982141.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982141
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 982141</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Run the actual test in its own window, because it requires that the
+ // root APZC not be scrollable. Mochitest pages themselves often run
+ // inside an iframe which means we have no control over the root APZC.
+ var w = null;
+ window.onload = function() {
+ pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
+ w = window.open("helper_bug982141.html", "_blank");
+ });
+ };
+ }
+
+ function finishTest() {
+ w.close();
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_frame_reconstruction.html b/system/graphics/layers/apz/test/mochitest/test_frame_reconstruction.html
new file mode 100644
index 000000000..589fb2843
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_frame_reconstruction.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1235899
+ -->
+ <head>
+ <title>Test for bug 1235899</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .outer {
+ height: 400px;
+ width: 415px;
+ overflow: hidden;
+ position: relative;
+ }
+ .inner {
+ height: 100%;
+ outline: none;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ .outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ </head>
+ <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1235899">Mozilla Bug 1235899</a>
+<p id="display"></p>
+<div id="content">
+ <p>You should be able to fling this list without it stopping abruptly</p>
+ <div class="outer">
+ <div class="inner">
+ <ol>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ </ol>
+ </div>
+ </div>
+</div>
+
+<pre id="test">
+<script type="application/javascript;version=1.7">
+function* test(testDriver) {
+ var elm = document.getElementsByClassName('inner')[0];
+ elm.scrollTop = 0;
+ yield flushApzRepaints(testDriver);
+
+ // Take over control of the refresh driver and compositor
+ var utils = SpecialPowers.DOMWindowUtils;
+ utils.advanceTimeAndRefresh(0);
+
+ // Kick off an APZ smooth-scroll to 0,200
+ elm.scrollTo(0, 200);
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
+
+ // Let's do a couple of frames of the animation, and make sure it's going
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ yield flushApzRepaints(testDriver);
+ ok(elm.scrollTop > 0, "APZ animation in progress", "scrollTop is now " + elm.scrollTop);
+ ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
+
+ var frameReconstructionTriggered = 0;
+ // Register the listener that triggers the frame reconstruction
+ elm.onscroll = function() {
+ // Do the reconstruction
+ elm.parentNode.classList.add('contentBefore');
+ frameReconstructionTriggered++;
+ // schedule a thing to undo the changes above
+ setTimeout(function() {
+ elm.parentNode.classList.remove('contentBefore');
+ }, 0);
+ }
+
+ // and do a few more frames of the animation, this should trigger the listener
+ // and the frame reconstruction
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ yield flushApzRepaints(testDriver);
+ ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
+ ok(frameReconstructionTriggered > 0, "Frame reconstruction triggered", "reconstruction triggered " + frameReconstructionTriggered + " times");
+
+ // and now run to completion
+ for (var i = 0; i < 100; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
+ yield flushApzRepaints(testDriver);
+
+ is(elm.scrollTop, 200, "Element should have scrolled by 200px");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
+ waitUntilApzStable()
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_group_mouseevents.html b/system/graphics/layers/apz/test/mochitest/test_group_mouseevents.html
new file mode 100644
index 000000000..dcf71f0cc
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_group_mouseevents.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various mouse tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var subtests = [
+ // Sanity test to synthesize a mouse click
+ {'file': 'helper_click.html?dtc=false'},
+ // Same as above, but with a dispatch-to-content region that exercises the
+ // main-thread notification codepaths for mouse events
+ {'file': 'helper_click.html?dtc=true'},
+ // Sanity test for click but with some mouse movement between the down and up
+ {'file': 'helper_drag_click.html'},
+ // Test for dragging on a fake-scrollbar element that scrolls the page
+ {'file': 'helper_drag_scroll.html'}
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_group_pointerevents.html b/system/graphics/layers/apz/test/mochitest/test_group_pointerevents.html
new file mode 100644
index 000000000..2e8d7c240
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_group_pointerevents.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1285070
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ var subtests = [
+ {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
+ {'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
+ ];
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_group_touchevents.html b/system/graphics/layers/apz/test/mochitest/test_group_touchevents.html
new file mode 100644
index 000000000..bc0261d46
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_group_touchevents.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various touch tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var basic_pan_prefs = [
+ // Dropping the touch slop to 0 makes the tests easier to write because
+ // we can just do a one-pixel drag to get over the pan threshold rather
+ // than having to hard-code some larger value.
+ ["apz.touch_start_tolerance", "0.0"],
+ // The touchstart from the drag can turn into a long-tap if the touch-move
+ // events get held up. Try to prevent that by making long-taps require
+ // a 10 second hold. Note that we also cannot enable chaos mode on this
+ // test for this reason, since chaos mode can cause the long-press timer
+ // to fire sooner than the pref dictates.
+ ["ui.click_hold_context_menus.delay", 10000],
+ // The subtests in this test do touch-drags to pan the page, but we don't
+ // want those pans to turn into fling animations, so we increase the
+ // fling min velocity requirement absurdly high.
+ ["apz.fling_min_velocity_threshold", "10000"],
+ // The helper_div_pan's div gets a displayport on scroll, but if the
+ // test takes too long the displayport can expire before the new scroll
+ // position is synced back to the main thread. So we disable displayport
+ // expiry for these tests.
+ ["apz.displayport_expiry_ms", 0],
+];
+
+var touch_action_prefs = basic_pan_prefs.slice(); // make a copy
+touch_action_prefs.push(["layout.css.touch_action.enabled", true]);
+
+var isWindows = (getPlatform() == "windows");
+
+var subtests = [
+ // Simple tests to exercise basic panning behaviour
+ {'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
+ {'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
+ {'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},
+
+ // Simple test to exercise touch-tapping behaviour
+ {'file': 'helper_tap.html'},
+ // Tapping, but with a full-zoom applied
+ {'file': 'helper_tap_fullzoom.html'},
+
+ // For the following two tests, disable displayport suppression to make sure it
+ // doesn't interfere with the test by scheduling paints non-deterministically.
+ {'file': 'helper_scrollto_tap.html?true', 'prefs': [["apz.paint_skipping.enabled", true]], 'dp_suppression': false},
+ {'file': 'helper_scrollto_tap.html?false', 'prefs': [["apz.paint_skipping.enabled", false]], 'dp_suppression': false},
+
+ // Taps on media elements to make sure the touchend event is delivered
+ // properly. We increase the long-tap timeout to ensure it doesn't get trip
+ // during the tap.
+ // Also this test (on Windows) cannot satisfy the OS requirement of providing
+ // an injected touch event every 100ms, because it waits for a paint between
+ // the touchstart and the touchend, so we have to use the "fake injection"
+ // code instead.
+ {'file': 'helper_bug1162771.html', 'prefs': [["ui.click_hold_context_menus.delay", 10000],
+ ["apz.test.fails_with_native_injection", isWindows]]},
+
+ // As with the previous test, this test cannot inject touch events every 100ms
+ // because it waits for a long-tap, so we have to use the "fake injection" code
+ // instead.
+ {'file': 'helper_long_tap.html', 'prefs': [["apz.test.fails_with_native_injection", isWindows]]},
+
+ // For the following test, we want to make sure APZ doesn't wait for a content
+ // response that is never going to arrive. To detect this we set the content response
+ // timeout to a day, so that the entire test times out and fails if APZ does
+ // end up waiting.
+ {'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
+ ["apz.test.fails_with_native_injection", isWindows]]},
+
+ // Simple test to exercise touch-action CSS property
+ {'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
+ // More complex touch-action tests, with overlapping regions and such
+ {'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
+ // Tests that touch-action CSS properties are handled in APZ without waiting
+ // on the main-thread, when possible
+ {'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
+];
+
+if (isApzEnabled()) {
+ ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
+ if (getPlatform() == "android") {
+ // This has a lot of subtests, and Android emulators are slow.
+ SimpleTest.requestLongerTimeout(2);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_group_wheelevents.html b/system/graphics/layers/apz/test/mochitest/test_group_wheelevents.html
new file mode 100644
index 000000000..98c36f320
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_group_wheelevents.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various wheel-scrolling tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var prefs = [
+ // turn off smooth scrolling so that we don't have to wait for
+ // APZ animations to finish before sampling the scroll offset
+ ['general.smoothScroll', false],
+ // ensure that any mouse movement will trigger a new wheel transaction,
+ // because in this test we move the mouse a bunch and want to recalculate
+ // the target APZC after each such movement.
+ ['mousewheel.transaction.ignoremovedelay', 0],
+ ['mousewheel.transaction.timeout', 0]
+]
+
+var subtests = [
+ {'file': 'helper_scroll_on_position_fixed.html', 'prefs': prefs},
+ {'file': 'helper_bug1271432.html', 'prefs': prefs},
+ {'file': 'helper_scroll_inactive_perspective.html', 'prefs': prefs},
+ {'file': 'helper_scroll_inactive_zindex.html', 'prefs': prefs}
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_group_zoom.html b/system/graphics/layers/apz/test/mochitest/test_group_zoom.html
new file mode 100644
index 000000000..4bf9c0bed
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_group_zoom.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various zoom-related tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var prefs = [
+ // We need the APZ paint logging information
+ ["apz.test.logging_enabled", true],
+ // Dropping the touch slop to 0 makes the tests easier to write because
+ // we can just do a one-pixel drag to get over the pan threshold rather
+ // than having to hard-code some larger value.
+ ["apz.touch_start_tolerance", "0.0"],
+ // The subtests in this test do touch-drags to pan the page, but we don't
+ // want those pans to turn into fling animations, so we increase the
+ // fling-stop threshold velocity to absurdly high.
+ ["apz.fling_stopped_threshold", "10000"],
+ // The helper_bug1280013's div gets a displayport on scroll, but if the
+ // test takes too long the displayport can expire before we read the value
+ // out of the test. So we disable displayport expiry for these tests.
+ ["apz.displayport_expiry_ms", 0],
+];
+
+var subtests = [
+ {'file': 'helper_bug1280013.html', 'prefs': prefs},
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_interrupted_reflow.html b/system/graphics/layers/apz/test/mochitest/test_interrupted_reflow.html
new file mode 100644
index 000000000..05c5e5478
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_interrupted_reflow.html
@@ -0,0 +1,719 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1292781
+ -->
+ <head>
+ <title>Test for bug 1292781</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .outer {
+ height: 400px;
+ width: 415px;
+ overflow: hidden;
+ position: relative;
+ }
+ .inner {
+ height: 100%;
+ outline: none;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: relative;
+ }
+ .inner div:nth-child(even) {
+ background-color: lightblue;
+ }
+ .inner div:nth-child(odd) {
+ background-color: lightgreen;
+ }
+ .outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ </head>
+ <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292781">Mozilla Bug 1292781</a>
+<p id="display"></p>
+<div id="content">
+ <p>The frame reconstruction should not leave this scrollframe in a bad state</p>
+ <div class="outer">
+ <div class="inner">
+ this is the top of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is near the top of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is near the bottom of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is the bottom of the scrollframe.
+ </div>
+ </div>
+</div>
+
+<pre id="test">
+<script type="text/javascript">
+
+// Returns a list of async scroll offsets that the |inner| element had, one for
+// each paint.
+function getAsyncScrollOffsets(aPaintsToIgnore) {
+ var offsets = [];
+ var compositorTestData = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData();
+ var buckets = compositorTestData.paints.slice(aPaintsToIgnore);
+ ok(buckets.length >= 3, "Expected at least three paints in the compositor test data");
+ var childIsLayerized = false;
+ for (var i = 0; i < buckets.length; ++i) {
+ var apzcTree = buildApzcTree(convertScrollFrameData(buckets[i].scrollFrames));
+ var rcd = findRcdNode(apzcTree);
+ if (rcd == null) {
+ continue;
+ }
+ if (rcd.children.length > 0) {
+ // The child may not be layerized in the first few paints, but once it is
+ // layerized, it should stay layerized.
+ childIsLayerized = true;
+ }
+ if (!childIsLayerized) {
+ continue;
+ }
+
+ ok(rcd.children.length == 1, "Root content APZC has exactly one child");
+ var scroll = rcd.children[0].asyncScrollOffset;
+ var pieces = scroll.replace(/[()\s]+/g, '').split(',');
+ is(pieces.length, 2, "expected string of form (x,y)");
+ offsets.push({ x: parseInt(pieces[0]),
+ y: parseInt(pieces[1]) });
+ }
+ return offsets;
+}
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+
+ // The APZ test data accumulates whenever a test turns it on. We just want
+ // the data for this test, so we check how many frames are already recorded
+ // and discard those later.
+ var framesToSkip = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData().paints.length;
+
+ var elm = document.getElementsByClassName('inner')[0];
+ // Set a zero-margin displayport to ensure that the element is async-scrollable
+ // otherwise on Fennec it is not
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, 0);
+
+ var maxScroll = elm.scrollTopMax;
+ elm.scrollTop = maxScroll;
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Force the next reflow to get interrupted
+ utils.forceReflowInterrupt();
+
+ // Make a change that triggers frame reconstruction, and then tick the refresh
+ // driver so that layout processes the pending restyles and then runs an
+ // interruptible reflow. That reflow *will* be interrupted (because of the flag
+ // we set above), and we should end up with a transient 0,0 scroll offset
+ // being sent to the compositor.
+ elm.parentNode.classList.add('contentBefore');
+ utils.advanceTimeAndRefresh(0);
+ // On android, and maybe non-e10s platforms generally, we need to manually
+ // kick the paint to send the layer transaction to the compositor.
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0) });
+
+ // Read the main-thread scroll offset; although this is temporarily 0,0 that
+ // temporary value is never exposed to content - instead reading this value
+ // will finish doing the interrupted reflow from above and then report the
+ // correct scroll offset.
+ is(elm.scrollTop, maxScroll, "Main-thread scroll position was restored");
+
+ // .. and now flush everything to make sure the state gets pushed over to the
+ // compositor and APZ as well.
+ utils.restoreNormalRefresh();
+ yield waitForApzFlushedRepaints(testDriver);
+
+ // Now we pull the compositor data and check it. What we expect to see is that
+ // the scroll position goes to maxScroll, then drops to 0 and then goes back
+ // to maxScroll. This test is specifically testing that last bit - that it
+ // properly gets restored from 0 to maxScroll.
+ // The one hitch is that on Android this page is loaded with some amount of
+ // zoom, and the async scroll is in ParentLayerPixel coordinates, so it will
+ // not match maxScroll exactly. Since we can't reliably compute what that
+ // ParentLayer scroll will be, we just make sure the async scroll is nonzero
+ // and use the first value we encounter to verify that it got restored properly.
+ // The other alternative is to spawn this test into a new window with 1.0 zoom
+ // but I'm tired of doing that for pretty much every test.
+ var state = 0;
+ var asyncScrollOffsets = getAsyncScrollOffsets(framesToSkip);
+ dump("Got scroll offsets: " + JSON.stringify(asyncScrollOffsets) + "\n");
+ var maxScrollParentLayerPixels = maxScroll;
+ while (asyncScrollOffsets.length > 0) {
+ let offset = asyncScrollOffsets.shift();
+ switch (state) {
+ // 0 is the initial state, the scroll offset might be zero but should
+ // become non-zero from when we set scrollTop to scrollTopMax
+ case 0:
+ if (offset.y == 0) {
+ break;
+ }
+ if (getPlatform() == "android") {
+ ok(offset.y > 0, "Async scroll y of scrollframe is " + offset.y);
+ maxScrollParentLayerPixels = offset.y;
+ } else {
+ is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe is " + offset.y);
+ }
+ state = 1;
+ break;
+
+ // state 1 starts out at maxScrollParentLayerPixels, should drop to 0
+ // because of the interrupted reflow putting the scroll into a transient
+ // zero state
+ case 1:
+ if (offset.y == maxScrollParentLayerPixels) {
+ break;
+ }
+ is(offset.y, 0, "Async scroll position was temporarily 0");
+ state = 2;
+ break;
+
+ // state 2 starts out the transient 0 scroll offset, and we expect the
+ // scroll position to get restored back to maxScrollParentLayerPixels
+ case 2:
+ if (offset.y == 0) {
+ break;
+ }
+ is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe restored to " + offset.y);
+ state = 3;
+ break;
+
+ // Terminal state. The scroll position should stay at maxScrollParentLayerPixels
+ case 3:
+ is(offset.y, maxScrollParentLayerPixels, "Scroll position maintained");
+ break;
+ }
+ }
+ is(state, 3, "The scroll position did drop to 0 and then get restored properly");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ pushPrefs([["apz.test.logging_enabled", true],
+ ["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_layerization.html b/system/graphics/layers/apz/test/mochitest/test_layerization.html
new file mode 100644
index 000000000..c74b181bd
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_layerization.html
@@ -0,0 +1,214 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
+-->
+<head>
+ <title>Test for layerization</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ <style>
+ #container {
+ display: flex;
+ overflow: scroll;
+ height: 500px;
+ }
+ .outer-frame {
+ height: 500px;
+ overflow: scroll;
+ flex-basis: 100%;
+ background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
+ }
+ #container-content {
+ height: 200%;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a>
+<p id="display"></p>
+<div id="container">
+ <div id="outer1" class="outer-frame">
+ <div id="inner1" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </div>
+ <div id="outer2" class="outer-frame">
+ <div id="inner2" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </div>
+ <iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe>
+ <iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe>
+<!-- The container-content div ensures 'container' is scrollable, so the
+ optimization that layerizes the primary async-scrollable frame on page
+ load layerizes it rather than its child subframes. -->
+ <div id="container-content"></div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+// Scroll the mouse wheel over |element|.
+function scrollWheelOver(element, waitForScroll, testDriver) {
+ moveMouseAndScrollWheelOver(element, 10, 10, testDriver, waitForScroll);
+}
+
+const DISPLAYPORT_EXPIRY = 100;
+
+// This helper function produces another helper function, which, when invoked,
+// invokes the provided testDriver argument in a setTimeout 0. This is really
+// just useful in cases when there are no paints pending, because then
+// waitForAllPaints will invoke its callback synchronously. If we did
+// waitForAllPaints(testDriver) that might cause reentrancy into the testDriver
+// which is bad. This function works around that.
+function asyncWrapper(testDriver) {
+ return function() {
+ setTimeout(testDriver, 0);
+ };
+}
+
+function* test(testDriver) {
+ // Initially, nothing should be layerized.
+ ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
+ ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
+ ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
+ ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
+ ok(!isLayerized('outer3'), "initially 'outer3' should not be layerized");
+ ok(!isLayerized('inner3'), "initially 'inner3' should not be layerized");
+ ok(!isLayerized('outer4'), "initially 'outer4' should not be layerized");
+ ok(!isLayerized('inner4'), "initially 'inner4' should not be layerized");
+
+ // Scrolling over outer1 should layerize outer1, but not inner1.
+ yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
+ ok(isLayerized('outer1'), "scrolling 'outer1' should cause it to be layerized");
+ ok(!isLayerized('inner1'), "scrolling 'outer1' should not cause 'inner1' to be layerized");
+
+ // Scrolling over inner2 should layerize both outer2 and inner2.
+ yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
+ ok(isLayerized('inner2'), "scrolling 'inner2' should cause it to be layerized");
+ ok(isLayerized('outer2'), "scrolling 'inner2' should also cause 'outer2' to be layerized");
+
+ // The second half of the test repeats the same checks as the first half,
+ // but with an iframe as the outer scrollable frame.
+
+ // Scrolling over outer3 should layerize outer3, but not inner3.
+ yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement, true, testDriver);
+ ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
+ ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");
+
+ // Scrolling over outer4 should layerize both outer4 and inner4.
+ yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'), true, testDriver);
+ ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
+ ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
+
+ // Now we enable displayport expiry, and verify that things are still
+ // layerized as they were before.
+ yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, testDriver);
+ ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
+ ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
+ ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
+ ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
+ ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
+ ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
+ ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
+ ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");
+
+ // Now we trigger a scroll on some of the things still layerized, so that
+ // the displayport expiry gets triggered.
+
+ // Expire displayport with scrolling on outer1
+ yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
+ ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");
+
+ // Expire displayport with scrolling on inner2
+ yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Once the expiry elapses, it will trigger expiry on outer2, so we check
+ // both, one at a time.
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");
+
+ // Scroll on inner3. inner3 isn't layerized, and this will cause it to
+ // get layerized, but it will also trigger displayport expiration for inner3
+ // which will eventually trigger displayport expiration on inner3 and outer3.
+ // Note that the displayport expiration might actually happen before the wheel
+ // input is processed in the compositor (see bug 1246480 comment 3), and so
+ // we make sure not to wait for a scroll event here, since it may never fire.
+ // However, if we do get a scroll event while waiting for the expiry, we need
+ // to restart the expiry timer because the displayport expiry got reset. There's
+ // no good way that I can think of to deterministically avoid doing this.
+ let inner3 = document.getElementById('outer3').contentDocument.getElementById('inner3');
+ yield scrollWheelOver(inner3, false, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ var timerId = setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ var timeoutResetter = function() {
+ ok(true, "Got a scroll event; resetting timer...");
+ clearTimeout(timerId);
+ setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ // by not updating timerId we ensure that this listener resets the timeout
+ // at most once.
+ };
+ inner3.addEventListener('scroll', timeoutResetter, false);
+ yield; // wait for the setTimeout to elapse
+ inner3.removeEventListener('scroll', timeoutResetter, false);
+
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");
+
+ // Scroll outer4 and wait for the expiry. It should NOT get expired because
+ // inner4 is still layerized
+ yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement, true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Wait for the expiry to elapse
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
+ ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
+ SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)
+
+ // Disable smooth scrolling, because it results in long-running scroll
+ // animations that can result in a 'scroll' event triggered by an earlier
+ // wheel event as corresponding to a later wheel event.
+ // Also enable APZ test logging, since we use that data to determine whether
+ // a scroll frame was layerized.
+ pushPrefs([["general.smoothScroll", false],
+ ["apz.displayport_expiry_ms", 0],
+ ["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html b/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
new file mode 100644
index 000000000..3349ef1ab
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
@@ -0,0 +1,541 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling flattened inactive frames</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+p {
+ width:200px;
+ height:200px;
+ border:solid 1px black;
+ overflow:auto;
+}
+</style>
+</head>
+<body>
+<div id="iframe-body" style="overflow: auto; height: 1000px">
+<hr>
+<hr>
+<hr>
+<p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p id="subframe">
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p>
+</div>
+<script clss="testbody" type="text/javascript;version=1.7">
+function ScrollTops() {
+ this.outerScrollTop = document.getElementById('iframe-body').scrollTop;
+ this.innerScrollTop = document.getElementById('subframe').scrollTop;
+}
+
+var DefaultEvent = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0, deltaY: 1,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+};
+
+function test() {
+ var subframe = document.getElementById('subframe');
+ var oldpos = new ScrollTops();
+ sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
+ ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
+ doOuterScroll(subframe, newpos);
+ });
+}
+
+function doOuterScroll(subframe, oldpos) {
+ var outer = document.getElementById('iframe-body');
+ sendWheelAndPaint(outer, 20, 5, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop != newpos.outerScrollTop, "viewport should have scrolled");
+ ok(oldpos.innerScrollTop == newpos.innerScrollTop, "subframe should not have scrolled");
+ doInnerScrollAgain(subframe, newpos);
+ });
+}
+
+function doInnerScrollAgain(subframe, oldpos) {
+ sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
+ ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 0],
+ ['mousewheel.transaction.ignoremovedelay', 0]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html b/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
new file mode 100644
index 000000000..51e16aab9
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling flattened inactive frames</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="container" style="height: 300px; width: 600px; overflow: auto; background: yellow">
+ <div id="outer" style="height: 400px; width: 500px; overflow: auto; background: black">
+ <div id="inner" style="mix-blend-mode: screen; height: 800px; overflow: auto; background: purple">
+ </div>
+ </div>
+</div>
+<script class="testbody" type="text/javascript;version=1.7">
+function test() {
+ var container = document.getElementById('container');
+ var outer = document.getElementById('outer');
+ var inner = document.getElementById('inner');
+ var outerScrollTop = outer.scrollTop;
+ var containerScrollTop = container.scrollTop;
+ var event = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0,
+ deltaY: 10,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 10,
+ };
+ sendWheelAndPaint(inner, 20, 30, event, function () {
+ ok(container.scrollTop == containerScrollTop, "container scrollframe should not have scrolled");
+ ok(outer.scrollTop > outerScrollTop, "nested scrollframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 1000000]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html b/system/graphics/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
new file mode 100644
index 000000000..4d9da8c2c
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling subframe scrollbars</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+p {
+ width:200px;
+ height:200px;
+ border:solid 1px black;
+}
+</style>
+</head>
+<body>
+<p id="subframe">
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+</p>
+<script clss="testbody" type="text/javascript;version=1.7">
+
+var DefaultEvent = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0, deltaY: 1,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+};
+
+var ScrollbarWidth = 0;
+
+function test() {
+ var subframe = document.getElementById('subframe');
+ var oldClientWidth = subframe.clientWidth;
+
+ subframe.style.overflow = 'auto';
+ subframe.getBoundingClientRect();
+
+ waitForAllPaintsFlushed(function () {
+ ScrollbarWidth = oldClientWidth - subframe.clientWidth;
+ if (!ScrollbarWidth) {
+ // Probably we have overlay scrollbars - abort the test.
+ ok(true, "overlay scrollbars - skipping test");
+ SimpleTest.finish();
+ return;
+ }
+
+ ok(subframe.scrollHeight > subframe.clientHeight, "subframe should have scrollable content");
+ testScrolling(subframe);
+ });
+}
+
+function testScrolling(subframe) {
+ // Send a wheel event roughly to where we think the trackbar is. We pick a
+ // point at the bottom, in the middle of the trackbar, where the slider is
+ // unlikely to be (since it starts at the top).
+ var posX = subframe.clientWidth + (ScrollbarWidth / 2);
+ var posY = subframe.clientHeight - 20;
+
+ var oldScrollTop = subframe.scrollTop;
+
+ sendWheelAndPaint(subframe, posX, posY, DefaultEvent, function () {
+ ok(subframe.scrollTop > oldScrollTop, "subframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 0],
+ ['mousewheel.transaction.ignoremovedelay', 0]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_smoothness.html b/system/graphics/layers/apz/test/mochitest/test_smoothness.html
new file mode 100644
index 000000000..88373957a
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_smoothness.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+ <title>Test Frame Uniformity While Scrolling</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+
+ <style>
+ #content {
+ height: 5000px;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ </style>
+ <script type="text/javascript">
+ var scrollEvents = 100;
+ var i = 0;
+ var testPref = "gfx.vsync.collect-scroll-transforms";
+ // Scroll points
+ var x = 100;
+ var y = 150;
+
+ SimpleTest.waitForExplicitFinish();
+ var utils = _getDOMWindowUtils(window);
+
+ function sendScrollEvent(aRafTimestamp) {
+ var scrollDiv = document.getElementById("content");
+
+ if (i < scrollEvents) {
+ i++;
+ // Scroll diff
+ var dx = 0;
+ var dy = -10; // Negative to scroll down
+ synthesizeNativeWheelAndWaitForWheelEvent(scrollDiv, x, y, dx, dy);
+ window.requestAnimationFrame(sendScrollEvent);
+ } else {
+ // Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5
+ // w/o silk + e10s + apz, I get up to 7. Lower is better.
+ // Windows, I get ~3. Values are not valid w/o hardware vsync
+ var uniformities = _getDOMWindowUtils().getFrameUniformityTestData();
+ for (var j = 0; j < uniformities.layerUniformities.length; j++) {
+ var layerResult = uniformities.layerUniformities[j];
+ var layerAddr = layerResult.layerAddress;
+ var uniformity = layerResult.frameUniformity;
+ var msg = "Layer: " + layerAddr.toString(16) + " Uniformity: " + uniformity;
+ SimpleTest.ok((uniformity >= 0) && (uniformity < 4.0), msg);
+ }
+ SimpleTest.finish();
+ }
+ }
+
+ function startTest() {
+ window.requestAnimationFrame(sendScrollEvent);
+ }
+
+ window.onload = function() {
+ var apzEnabled = SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled");
+ if (!apzEnabled) {
+ SimpleTest.ok(true, "APZ not enabled, skipping test");
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv({
+ "set" : [
+ [testPref, true]
+ ]
+ }, startTest);
+ }
+ </script>
+</head>
+
+<body>
+ <div id="content">
+ </div>
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html b/system/graphics/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html
new file mode 100644
index 000000000..913269a67
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1203140
+-->
+<head>
+ <title>Test for Bug 1203140</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203140">Mozilla Bug 1203140</a>
+<p id="display"></p>
+<div id="content" style="overflow-y:scroll; height: 400px">
+ <p>The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.</p>
+ <div id="box" style="width: 200px; height: 200px; background-color: blue"></div>
+ <div style="height: 1000px; width: 10px">Div to make 'content' scrollable</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes
+
+function takeSnapshots(e) {
+ // Grab some snapshots, and make sure some of them are different (i.e. check
+ // the page is scrolling in the compositor, concurrently with this wheel
+ // listener running).
+ // Note that we want this function to take less time than the content response
+ // timeout, otherwise the scrolling will start even if we haven't returned,
+ // and that would invalidate purpose of the test.
+ var start = Date.now();
+ var lastSnapshot = null;
+ var success = false;
+
+ // Get the position of the 'content' div relative to the screen
+ var rect = rectRelativeToScreen(document.getElementById('content'));
+
+ for (var i = 0; i < 10; i++) {
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(16);
+ var snapshot = getSnapshot(rect);
+ //dump("Took snapshot " + snapshot + "\n"); // this might help with debugging
+
+ if (lastSnapshot && lastSnapshot != snapshot) {
+ ok(true, "Found some different pixels in snapshot " + i + " compared to previous");
+ success = true;
+ }
+ lastSnapshot = snapshot;
+ }
+ ok(success, "Found some snapshots that were different");
+ ok((Date.now() - start) < kResponseTimeoutMs, "Snapshotting ran quickly enough");
+
+ // Until now, no scroll events will have been dispatched to content. That's
+ // because scroll events are dispatched on the main thread, which we've been
+ // hogging with the code above. At this point we restore the normal refresh
+ // behaviour and let the main thread go back to C++ code, so the scroll events
+ // fire and we unwind from the main test continuation.
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+}
+
+function* test(testDriver) {
+ var box = document.getElementById('box');
+
+ // Ensure the div is layerized by scrolling it
+ yield moveMouseAndScrollWheelOver(box, 10, 10, testDriver);
+
+ box.addEventListener('touchstart', function(e) {
+ ok(false, "This should never be run");
+ }, false);
+ box.addEventListener('wheel', takeSnapshots, { capture: false, passive: true });
+
+ // Let the event regions and layerization propagate to the APZ
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take over control of the refresh driver and compositor
+ var utils = SpecialPowers.DOMWindowUtils;
+ utils.advanceTimeAndRefresh(0);
+
+ // Trigger an APZ scroll using a wheel event. If APZ is waiting for a
+ // content response, it will wait for takeSnapshots to finish running before
+ // it starts scrolling, which will cause the checks in takeSnapshots to fail.
+ yield synthesizeNativeMouseMoveAndWaitForMoveEvent(box, 10, 10, testDriver);
+ yield synthesizeNativeWheelAndWaitForScrollEvent(box, 10, 10, 0, -50, testDriver);
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ // Disable touch events, so that APZ knows not to wait for touch listeners.
+ // Also explicitly set the content response timeout, so we know how long it
+ // is (see comment in takeSnapshots).
+ // Finally, enable smooth scrolling, so that the wheel-scroll we do as part
+ // of the test triggers an APZ animation rather than doing an instant scroll.
+ // Note that this pref doesn't work for the synthesized wheel events on OS X,
+ // those are hard-coded to be instant scrolls.
+ pushPrefs([["dom.w3c_touch_events.enabled", 0],
+ ["apz.content_response_timeout", kResponseTimeoutMs],
+ ["general.smoothscroll", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_wheel_scroll.html b/system/graphics/layers/apz/test/mochitest/test_wheel_scroll.html
new file mode 100644
index 000000000..479478d42
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_wheel_scroll.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
+-->
+<head>
+ <title>Test for Bug 1013412</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #content {
+ height: 800px;
+ overflow: scroll;
+ }
+
+ #scroller {
+ height: 2000px;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+
+ #scrollbox {
+ margin-top: 200px;
+ width: 500px;
+ height: 500px;
+ border-radius: 250px;
+ box-shadow: inset 0 0 0 60px #555;
+ background: #777;
+ }
+
+ #circle {
+ position: relative;
+ left: 240px;
+ top: 20px;
+ border: 10px solid white;
+ border-radius: 10px;
+ width: 0px;
+ height: 0px;
+ transform-origin: 10px 230px;
+ will-change: transform;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
+<p id="display"></p>
+<div id="content">
+ <p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
+ <div id="scroller">
+ <div id="scrollbox">
+ <div id="circle"></div>
+ </div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+var rotation = 0;
+var rotationAdjusted = false;
+
+var incrementForMode = function (mode) {
+ switch (mode) {
+ case WheelEvent.DOM_DELTA_PIXEL: return 1;
+ case WheelEvent.DOM_DELTA_LINE: return 15;
+ case WheelEvent.DOM_DELTA_PAGE: return 400;
+ }
+ return 0;
+};
+
+document.getElementById("scrollbox").addEventListener("wheel", function (e) {
+ rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
+ document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
+ rotationAdjusted = true;
+ e.preventDefault();
+});
+
+function* test(testDriver) {
+ var content = document.getElementById('content');
+ for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
+ yield synthesizeNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5, testDriver);
+ }
+ var scrollbox = document.getElementById('scrollbox');
+ is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
+ is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
+ is(rotationAdjusted, true, "The rotation should have been adjusted");
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+// If we allow smooth scrolling the "smooth" scrolling may cause the page to
+// glide past the scrollbox (which is supposed to stop the scrolling) and so
+// we might end up at the bottom of the page.
+pushPrefs([["general.smoothScroll", false]])
+.then(waitUntilApzStable)
+.then(runContinuation(test))
+.then(SimpleTest.finish);
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/mochitest/test_wheel_transactions.html b/system/graphics/layers/apz/test/mochitest/test_wheel_transactions.html
new file mode 100644
index 000000000..e00e992cd
--- /dev/null
+++ b/system/graphics/layers/apz/test/mochitest/test_wheel_transactions.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
+-->
+<head>
+ <title>Test for Bug 1175585</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #outer-frame {
+ height: 500px;
+ overflow: scroll;
+ background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
+ }
+ #inner-frame {
+ margin-top: 25%;
+ height: 200%;
+ width: 75%;
+ overflow: scroll;
+ }
+ #inner-content {
+ height: 200%;
+ width: 200%;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175585">APZ wheel transactions test</a>
+<p id="display"></p>
+<div id="outer-frame">
+ <div id="inner-frame">
+ <div id="inner-content"></div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function scrollWheelOver(element, deltaY, testDriver) {
+ synthesizeNativeWheelAndWaitForScrollEvent(element, 10, 10, 0, deltaY, testDriver);
+}
+
+function* test(testDriver) {
+ var outer = document.getElementById('outer-frame');
+ var inner = document.getElementById('inner-frame');
+ var innerContent = document.getElementById('inner-content');
+
+ // Register a wheel event listener that records the target of
+ // the last wheel event, so that we can make assertions about it.
+ var lastWheelTarget;
+ var wheelTargetRecorder = function(e) { lastWheelTarget = e.target; };
+ window.addEventListener("wheel", wheelTargetRecorder);
+
+ // Scroll |outer| to the bottom.
+ while (outer.scrollTop < outer.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ }
+
+ // Verify that this has brought |inner| under the wheel.
+ is(lastWheelTarget, innerContent, "'inner-content' should have been brought under the wheel");
+ window.removeEventListener("wheel", wheelTargetRecorder);
+
+ // Immediately after, scroll it back up a bit.
+ yield scrollWheelOver(outer, 10, testDriver);
+
+ // Check that it was |outer| that scrolled back, and |inner| didn't
+ // scroll at all, as all the above scrolls should be in the same
+ // transaction.
+ ok(outer.scrollTop < outer.scrollTopMax, "'outer' should have scrolled back a bit");
+ is(inner.scrollTop, 0, "'inner' should not have scrolled");
+
+ // The next part of the test is related to the transaction timeout.
+ // Turn it down a bit so waiting for the timeout to elapse doesn't
+ // slow down the test harness too much.
+ var timeout = 5;
+ yield SpecialPowers.pushPrefEnv({"set": [["mousewheel.transaction.timeout", timeout]]}, testDriver);
+ SimpleTest.requestFlakyTimeout("we are testing code that measures actual elapsed time between two events");
+
+ // Scroll up a bit more. It's still |outer| scrolling because
+ // |inner| is still scrolled all the way to the top.
+ yield scrollWheelOver(outer, 10, testDriver);
+
+ // Wait for the transaction timeout to elapse.
+ // timeout * 5 is used to make it less likely that the timeout is less than
+ // the system timestamp resolution
+ yield window.setTimeout(testDriver, timeout * 5);
+
+ // Now scroll down. The transaction having timed out, the event
+ // should pick up a new target, and that should be |inner|.
+ yield scrollWheelOver(outer, -10, testDriver);
+ ok(inner.scrollTop > 0, "'inner' should have been scrolled");
+
+ // Finally, test scroll handoff after a timeout.
+
+ // Continue scrolling |inner| down to the bottom.
+ var prevScrollTop = inner.scrollTop;
+ while (inner.scrollTop < inner.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ // Avoid a failure getting us into an infinite loop.
+ ok(inner.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
+ prevScrollTop = inner.scrollTop;
+ }
+
+ // Wait for the transaction timeout to elapse.
+ // timeout * 5 is used to make it less likely that the timeout is less than
+ // the system timestamp resolution
+ yield window.setTimeout(testDriver, timeout * 5);
+
+ // Continued downward scrolling should scroll |outer| to the bottom.
+ prevScrollTop = outer.scrollTop;
+ while (outer.scrollTop < outer.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ // Avoid a failure getting us into an infinite loop.
+ ok(outer.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
+ prevScrollTop = outer.scrollTop;
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// Disable smooth scrolling because it makes the test flaky (we don't have a good
+// way of detecting when the scrolling is finished).
+pushPrefs([["general.smoothScroll", false]])
+.then(waitUntilApzStable)
+.then(runContinuation(test))
+.then(SimpleTest.finish);
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
new file mode 100644
index 000000000..0e2698b86
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
new file mode 100644
index 000000000..ee2524a16
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(-450,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
new file mode 100644
index 000000000..2f3d94639
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="-449" reftest-async-scroll-y="0"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(-1,0) is to activate the right arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(-1,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h.html
new file mode 100644
index 000000000..1eca19246
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-h.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="449" reftest-async-scroll-y="0"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,0) is to activate the left arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
new file mode 100644
index 000000000..9ac5485bc
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
new file mode 100644
index 000000000..94fb501ba
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
new file mode 100644
index 000000000..9a2eb8818
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v.html
new file mode 100644
index 000000000..56fe23c28
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-v.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html
new file mode 100644
index 000000000..564697b37
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,8000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html
new file mode 100644
index 000000000..78cb0332c
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(-450,8000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
new file mode 100644
index 000000000..397d1cf9b
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="-440" reftest-async-scroll-y="7999"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(-10,1) is to activate the right/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(-10,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh.html
new file mode 100644
index 000000000..a1d1527dd
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-1-vh.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="449" reftest-async-scroll-y="7999"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html
new file mode 100644
index 000000000..5ed970f76
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1.html
new file mode 100644
index 000000000..09be51a79
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="224" reftest-async-scroll-y="4999"
+ reftest-async-zoom="2.0"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 4500px; height: 10000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html
new file mode 100644
index 000000000..5ed970f76
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2.html b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2.html
new file mode 100644
index 000000000..abe822c21
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/async-scrollbar-zoom-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="899" reftest-async-scroll-y="19999"
+ reftest-async-zoom="0.5"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 18000px; height: 40000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html b/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html
new file mode 100644
index 000000000..3db9f2969
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html
@@ -0,0 +1,27 @@
+<html>
+<script>
+ function run() {
+ document.body.classList.toggle('noscroll');
+ document.getElementById('spacer').style.height = '100%';
+ // Scroll to the very end, including any fractional pixels
+ document.body.scrollTop = document.body.scrollTopMax + 1;
+ }
+</script>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background-color: green;
+ }
+
+ .noscroll {
+ overflow: hidden;
+ height: 100%;
+ }
+</style>
+<body onload="run()">
+ <div id="spacer" style="height: 5000px">
+ This is the top of the page.
+ </div>
+ This is the bottom of the page.
+</body>
diff --git a/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html b/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html
new file mode 100644
index 000000000..479363f3f
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html
@@ -0,0 +1,53 @@
+<html class="reftest-wait">
+<!--
+For bug 1266833; syncing the scroll offset to APZ properly when the scroll
+position is clamped to a smaller value during a frame reconstruction.
+-->
+<script>
+ function run() {
+ document.body.scrollTop = document.body.scrollTopMax;
+
+ // Let the scroll position propagate to APZ before we do the frame
+ // reconstruction. Ideally we would wait for flushApzRepaints here but
+ // we don't have access to DOMWindowUtils in a reftest, so we just wait
+ // 100ms to approximate it. With bug 1266833 fixed, this test should
+ // never fail regardless of what this timeout value is.
+ setTimeout(frameReconstruction, 100);
+ }
+
+ function frameReconstruction() {
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '100%';
+ document.documentElement.classList.remove('reftest-wait');
+ }
+</script>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background-color: green;
+ }
+
+ .noscroll {
+ overflow: hidden;
+ height: 100%;
+ }
+
+ /* Toggling this on and off triggers a frame reconstruction on the <body> */
+ html.reconstruct-body::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+</style>
+<body onload="setTimeout(run, 0)">
+ <div id="spacer" style="height: 5000px">
+ This is the top of the page.
+ </div>
+ This is the bottom of the page.
+</body>
diff --git a/system/graphics/layers/apz/test/reftest/initial-scale-1-ref.html b/system/graphics/layers/apz/test/reftest/initial-scale-1-ref.html
new file mode 100644
index 000000000..dc99712d3
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/initial-scale-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body>
+This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
+with something a little more sane.
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/initial-scale-1.html b/system/graphics/layers/apz/test/reftest/initial-scale-1.html
new file mode 100644
index 000000000..45bed0809
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/initial-scale-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="initial-scale=0; width=device-width">
+</head>
+<body>
+This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
+with something a little more sane.
+</body>
+</html>
+
diff --git a/system/graphics/layers/apz/test/reftest/reftest-stylo.list b/system/graphics/layers/apz/test/reftest/reftest-stylo.list
new file mode 100644
index 000000000..cc2c76827
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/reftest-stylo.list
@@ -0,0 +1,20 @@
+# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
+# The following tests test the async positioning of the scrollbars.
+# Basic root-frame scrollbar with async scrolling
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v.html async-scrollbar-1-v.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-h.html async-scrollbar-1-h.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-vh.html async-scrollbar-1-vh.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl.html
+skip-if(!asyncPan) fuzzy-if(Android,13,8) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl.html
+skip-if(!asyncPan) fuzzy-if(Android,8,10) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl.html
+
+# Different async zoom levels. Since the scrollthumb gets async-scaled in the
+# compositor, the border-radius ends of the scrollthumb are going to be a little
+# off, hence the fuzzy-if clauses.
+skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1.html
+skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2.html
+
+# Meta-viewport tag support
+skip-if(!asyncZoom) == initial-scale-1.html initial-scale-1.html
+
+skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping.html
diff --git a/system/graphics/layers/apz/test/reftest/reftest.list b/system/graphics/layers/apz/test/reftest/reftest.list
new file mode 100644
index 000000000..4ab29420c
--- /dev/null
+++ b/system/graphics/layers/apz/test/reftest/reftest.list
@@ -0,0 +1,19 @@
+# The following tests test the async positioning of the scrollbars.
+# Basic root-frame scrollbar with async scrolling
+fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html
+fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
+fuzzy-if(Android,3,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
+fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
+fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
+fuzzy-if(Android,3,7) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
+
+# Different async zoom levels. Since the scrollthumb gets async-scaled in the
+# compositor, the border-radius ends of the scrollthumb are going to be a little
+# off, hence the fuzzy-if clauses.
+fuzzy-if(Android,54,18) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
+fuzzy-if(Android,45,21) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
+
+# Meta-viewport tag support
+skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html
+
+skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping-ref.html
diff --git a/system/graphics/layers/apz/testutil/APZTestData.cpp b/system/graphics/layers/apz/testutil/APZTestData.cpp
new file mode 100644
index 000000000..3c9440b64
--- /dev/null
+++ b/system/graphics/layers/apz/testutil/APZTestData.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZTestData.h"
+#include "mozilla/dom/APZTestDataBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace layers {
+
+struct APZTestDataToJSConverter {
+ template <typename Key, typename Value, typename KeyValuePair>
+ static void ConvertMap(const std::map<Key, Value>& aFrom,
+ dom::Sequence<KeyValuePair>& aOutTo,
+ void (*aElementConverter)(const Key&, const Value&, KeyValuePair&)) {
+ for (auto it = aFrom.begin(); it != aFrom.end(); ++it) {
+ aOutTo.AppendElement(fallible);
+ aElementConverter(it->first, it->second, aOutTo.LastElement());
+ }
+ }
+
+ static void ConvertAPZTestData(const APZTestData& aFrom,
+ dom::APZTestData& aOutTo) {
+ ConvertMap(aFrom.mPaints, aOutTo.mPaints.Construct(), ConvertBucket);
+ ConvertMap(aFrom.mRepaintRequests, aOutTo.mRepaintRequests.Construct(), ConvertBucket);
+ }
+
+ static void ConvertBucket(const SequenceNumber& aKey,
+ const APZTestData::Bucket& aValue,
+ dom::APZBucket& aOutKeyValuePair) {
+ aOutKeyValuePair.mSequenceNumber.Construct() = aKey;
+ ConvertMap(aValue, aOutKeyValuePair.mScrollFrames.Construct(), ConvertScrollFrameData);
+ }
+
+ static void ConvertScrollFrameData(const APZTestData::ViewID& aKey,
+ const APZTestData::ScrollFrameData& aValue,
+ dom::ScrollFrameData& aOutKeyValuePair) {
+ aOutKeyValuePair.mScrollId.Construct() = aKey;
+ ConvertMap(aValue, aOutKeyValuePair.mEntries.Construct(), ConvertEntry);
+ }
+
+ static void ConvertEntry(const std::string& aKey,
+ const std::string& aValue,
+ dom::ScrollFrameDataEntry& aOutKeyValuePair) {
+ ConvertString(aKey, aOutKeyValuePair.mKey.Construct());
+ ConvertString(aValue, aOutKeyValuePair.mValue.Construct());
+ }
+
+ static void ConvertString(const std::string& aFrom, nsString& aOutTo) {
+ aOutTo = NS_ConvertUTF8toUTF16(aFrom.c_str(), aFrom.size());
+ }
+};
+
+bool
+APZTestData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const
+{
+ dom::APZTestData result;
+ APZTestDataToJSConverter::ConvertAPZTestData(*this, result);
+ return dom::ToJSValue(aContext, result, aOutValue);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/testutil/APZTestData.h b/system/graphics/layers/apz/testutil/APZTestData.h
new file mode 100644
index 000000000..c0cc35b5a
--- /dev/null
+++ b/system/graphics/layers/apz/testutil/APZTestData.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZTestData_h
+#define mozilla_layers_APZTestData_h
+
+#include <map>
+
+#include "FrameMetrics.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "mozilla/Assertions.h" // for MOZ_ASSERT
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/ToString.h" // for ToString
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace layers {
+
+typedef uint32_t SequenceNumber;
+
+/**
+ * This structure is used to store information logged by various gecko
+ * components for later examination by test code.
+ * It consists of a bucket for every paint (initiated on the client side),
+ * and every repaint request (initiated on the compositor side by
+ * AsyncPanZoomController::RequestContentRepait), which are identified by
+ * sequence numbers, and within that, a set of arbitrary string key/value
+ * pairs for every scrollable frame, identified by a scroll id.
+ * There are two instances of this data structure for every content thread:
+ * one on the client side and one of the compositor side.
+ */
+// TODO(botond):
+// - Improve warnings/asserts.
+// - Add ability to associate a repaint request triggered during a layers update
+// with the sequence number of the paint that caused the layers update.
+class APZTestData {
+ typedef FrameMetrics::ViewID ViewID;
+ friend struct IPC::ParamTraits<APZTestData>;
+ friend struct APZTestDataToJSConverter;
+public:
+ void StartNewPaint(SequenceNumber aSequenceNumber) {
+ // We should never get more than one paint with the same sequence number.
+ MOZ_ASSERT(mPaints.find(aSequenceNumber) == mPaints.end());
+ mPaints.insert(DataStore::value_type(aSequenceNumber, Bucket()));
+ }
+ void LogTestDataForPaint(SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ LogTestDataImpl(mPaints, aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ void StartNewRepaintRequest(SequenceNumber aSequenceNumber) {
+ typedef std::pair<DataStore::iterator, bool> InsertResultT;
+ DebugOnly<InsertResultT> insertResult = mRepaintRequests.insert(DataStore::value_type(aSequenceNumber, Bucket()));
+ MOZ_ASSERT(((InsertResultT&)insertResult).second, "Already have a repaint request with this sequence number");
+ }
+ void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ LogTestDataImpl(mRepaintRequests, aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Convert this object to a JS representation.
+ bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const;
+
+ // Use dummy derived structures wrapping the tyepdefs to work around a type
+ // name length limit in MSVC.
+ typedef std::map<std::string, std::string> ScrollFrameDataBase;
+ struct ScrollFrameData : ScrollFrameDataBase {};
+ typedef std::map<ViewID, ScrollFrameData> BucketBase;
+ struct Bucket : BucketBase {};
+ typedef std::map<SequenceNumber, Bucket> DataStoreBase;
+ struct DataStore : DataStoreBase {};
+private:
+ DataStore mPaints;
+ DataStore mRepaintRequests;
+
+ void LogTestDataImpl(DataStore& aDataStore,
+ SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ auto bucketIterator = aDataStore.find(aSequenceNumber);
+ if (bucketIterator == aDataStore.end()) {
+ MOZ_ASSERT(false, "LogTestDataImpl called with nonexistent sequence number");
+ return;
+ }
+ Bucket& bucket = bucketIterator->second;
+ ScrollFrameData& scrollFrameData = bucket[aScrollId]; // create if doesn't exist
+ MOZ_ASSERT(scrollFrameData.find(aKey) == scrollFrameData.end()
+ || scrollFrameData[aKey] == aValue);
+ scrollFrameData.insert(ScrollFrameData::value_type(aKey, aValue));
+ }
+};
+
+// A helper class for logging data for a paint.
+class APZPaintLogHelper {
+public:
+ APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber)
+ : mTestData(aTestData),
+ mPaintSequenceNumber(aPaintSequenceNumber)
+ {}
+
+ template <typename Value>
+ void LogTestData(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const Value& aValue) const {
+ if (mTestData) { // avoid stringifying if mTestData == nullptr
+ LogTestData(aScrollId, aKey, ToString(aValue));
+ }
+ }
+
+ void LogTestData(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) const {
+ if (mTestData) {
+ mTestData->LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
+ }
+ }
+private:
+ APZTestData* mTestData;
+ SequenceNumber mPaintSequenceNumber;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData>
+{
+ typedef mozilla::layers::APZTestData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mPaints);
+ WriteParam(aMsg, aParam.mRepaintRequests);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mPaints) &&
+ ReadParam(aMsg, aIter, &aResult->mRepaintRequests));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::ScrollFrameData>
+ : ParamTraits<mozilla::layers::APZTestData::ScrollFrameDataBase> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::Bucket>
+ : ParamTraits<mozilla::layers::APZTestData::BucketBase> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::DataStore>
+ : ParamTraits<mozilla::layers::APZTestData::DataStoreBase> {};
+
+} // namespace IPC
+
+
+#endif /* mozilla_layers_APZTestData_h */
diff --git a/system/graphics/layers/apz/util/APZCCallbackHelper.cpp b/system/graphics/layers/apz/util/APZCCallbackHelper.cpp
new file mode 100644
index 000000000..e1d4cdf54
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZCCallbackHelper.cpp
@@ -0,0 +1,931 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCCallbackHelper.h"
+
+#include "TouchActionHelper.h"
+#include "gfxPlatform.h" // For gfxPlatform::UseTiling
+#include "gfxPrefs.h"
+#include "LayersLogging.h" // For Stringify
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/TouchEvents.h"
+#include "nsContentUtils.h"
+#include "nsContainerFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsRefreshDriver.h"
+#include "nsString.h"
+#include "nsView.h"
+#include "Layers.h"
+
+#define APZCCH_LOG(...)
+// #define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+using dom::TabParent;
+
+uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock = uint64_t(-1);
+
+void
+APZCCallbackHelper::AdjustDisplayPortForScrollDelta(
+ mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset)
+{
+ // Correct the display-port by the difference between the requested scroll
+ // offset and the resulting scroll offset after setting the requested value.
+ ScreenPoint shift =
+ (aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
+ aFrameMetrics.DisplayportPixelsPerCSSPixel();
+ ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
+ margins.left -= shift.x;
+ margins.right += shift.x;
+ margins.top -= shift.y;
+ margins.bottom += shift.y;
+ aFrameMetrics.SetDisplayPortMargins(margins);
+}
+
+static void
+RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
+{
+ ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
+ margins.right = margins.left = margins.LeftRight() / 2;
+ margins.top = margins.bottom = margins.TopBottom() / 2;
+ aFrameMetrics.SetDisplayPortMargins(margins);
+}
+
+static CSSPoint
+ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut)
+{
+ aSuccessOut = false;
+ CSSPoint targetScrollPosition = aMetrics.GetScrollOffset();
+
+ if (!aFrame) {
+ return targetScrollPosition;
+ }
+
+ CSSPoint geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
+
+ // If the repaint request was triggered due to a previous main-thread scroll
+ // offset update sent to the APZ, then we don't need to do another scroll here
+ // and we can just return.
+ if (!aMetrics.GetScrollOffsetUpdated()) {
+ return geckoScrollPosition;
+ }
+
+ // If the frame is overflow:hidden on a particular axis, we don't want to allow
+ // user-driven scroll on that axis. Simply set the scroll position on that axis
+ // to whatever it already is. Note that this will leave the APZ's async scroll
+ // position out of sync with the gecko scroll position, but APZ can deal with that
+ // (by design). Note also that when we run into this case, even if both axes
+ // have overflow:hidden, we want to set aSuccessOut to true, so that the displayport
+ // follows the async scroll position rather than the gecko scroll position.
+ if (aFrame->GetScrollStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
+ targetScrollPosition.y = geckoScrollPosition.y;
+ }
+ if (aFrame->GetScrollStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
+ targetScrollPosition.x = geckoScrollPosition.x;
+ }
+
+ // If the scrollable frame is currently in the middle of an async or smooth
+ // scroll then we don't want to interrupt it (see bug 961280).
+ // Also if the scrollable frame got a scroll request from a higher priority origin
+ // since the last layers update, then we don't want to push our scroll request
+ // because we'll clobber that one, which is bad.
+ bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
+ if (!scrollInProgress) {
+ aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
+ geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
+ aSuccessOut = true;
+ }
+ // Return the final scroll position after setting it so that anything that relies
+ // on it can have an accurate value. Note that even if we set it above re-querying it
+ // is a good idea because it may have gotten clamped or rounded.
+ return geckoScrollPosition;
+}
+
+/**
+ * Scroll the scroll frame associated with |aContent| to the scroll position
+ * requested in |aMetrics|.
+ * The scroll offset in |aMetrics| is updated to reflect the actual scroll
+ * position.
+ * The displayport stored in |aMetrics| and the callback-transform stored on
+ * the content are updated to reflect any difference between the requested
+ * and actual scroll positions.
+ */
+static void
+ScrollFrame(nsIContent* aContent,
+ FrameMetrics& aMetrics)
+{
+ // Scroll the window to the desired spot
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+ if (sf) {
+ sf->ResetScrollInfoIfGeneration(aMetrics.GetScrollGeneration());
+ sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer());
+ }
+ bool scrollUpdated = false;
+ CSSPoint apzScrollOffset = aMetrics.GetScrollOffset();
+ CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics, scrollUpdated);
+
+ if (scrollUpdated) {
+ if (aMetrics.IsScrollInfoLayer()) {
+ // In cases where the APZ scroll offset is different from the content scroll
+ // offset, we want to interpret the margins as relative to the APZ scroll
+ // offset except when the frame is not scrollable by APZ. Therefore, if the
+ // layer is a scroll info layer, we leave the margins as-is and they will
+ // be interpreted as relative to the content scroll offset.
+ if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
+ frame->SchedulePaint();
+ }
+ } else {
+ // Correct the display port due to the difference between mScrollOffset and the
+ // actual scroll offset.
+ APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
+ }
+ } else {
+ // For whatever reason we couldn't update the scroll offset on the scroll frame,
+ // which means the data APZ used for its displayport calculation is stale. Fall
+ // back to a sane default behaviour. Note that we don't tile-align the recentered
+ // displayport because tile-alignment depends on the scroll position, and the
+ // scroll position here is out of our control. See bug 966507 comment 21 for a
+ // more detailed explanation.
+ RecenterDisplayPort(aMetrics);
+ }
+
+ aMetrics.SetScrollOffset(actualScrollOffset);
+
+ // APZ transforms inputs assuming we applied the exact scroll offset it
+ // requested (|apzScrollOffset|). Since we may not have, record the difference
+ // between what APZ asked for and what we actually applied, and apply it to
+ // input events to compensate.
+ // Note that if the main-thread had a change in its scroll position, we don't
+ // want to record that difference here, because it can be large and throw off
+ // input events by a large amount. It is also going to be transient, because
+ // any main-thread scroll position change will be synced to APZ and we will
+ // get another repaint request when APZ confirms. In the interval while this
+ // is happening we can just leave the callback transform as it was.
+ bool mainThreadScrollChanged =
+ sf && sf->CurrentScrollGeneration() != aMetrics.GetScrollGeneration() && nsLayoutUtils::CanScrollOriginClobberApz(sf->LastScrollOrigin());
+ if (aContent && !mainThreadScrollChanged) {
+ CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset;
+ aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta),
+ nsINode::DeleteProperty<CSSPoint>);
+ }
+}
+
+static void
+SetDisplayPortMargins(nsIPresShell* aPresShell,
+ nsIContent* aContent,
+ const FrameMetrics& aMetrics)
+{
+ if (!aContent) {
+ return;
+ }
+
+ bool hadDisplayPort = nsLayoutUtils::HasDisplayPort(aContent);
+ ScreenMargin margins = aMetrics.GetDisplayPortMargins();
+ nsLayoutUtils::SetDisplayPortMargins(aContent, aPresShell, margins, 0);
+ if (!hadDisplayPort) {
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ aContent->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::Repaint);
+ }
+
+ CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels();
+ nsRect base(0, 0,
+ baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
+ baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
+ nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
+}
+
+static already_AddRefed<nsIPresShell>
+GetPresShell(const nsIContent* aContent)
+{
+ nsCOMPtr<nsIPresShell> result;
+ if (nsIDocument* doc = aContent->GetComposedDoc()) {
+ result = doc->GetShell();
+ }
+ return result.forget();
+}
+
+static void
+SetPaintRequestTime(nsIContent* aContent, const TimeStamp& aPaintRequestTime)
+{
+ aContent->SetProperty(nsGkAtoms::paintRequestTime,
+ new TimeStamp(aPaintRequestTime),
+ nsINode::DeleteProperty<TimeStamp>);
+}
+
+void
+APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics)
+{
+ if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
+ return;
+ }
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
+ if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) {
+ return;
+ }
+
+ MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+
+ if (gfxPrefs::APZAllowZooming()) {
+ // If zooming is disabled then we don't really want to let APZ fiddle
+ // with these things. In theory setting the resolution here should be a
+ // no-op, but setting the SPCSPS is bad because it can cause a stale value
+ // to be returned by window.innerWidth/innerHeight (see bug 1187792).
+
+ float presShellResolution = shell->GetResolution();
+
+ // If the pres shell resolution has changed on the content side side
+ // the time this repaint request was fired, consider this request out of date
+ // and drop it; setting a zoom based on the out-of-date resolution can have
+ // the effect of getting us stuck with the stale resolution.
+ if (!FuzzyEqualsMultiplicative(presShellResolution, aMetrics.GetPresShellResolution())) {
+ return;
+ }
+
+ // The pres shell resolution is updated by the the async zoom since the
+ // last paint.
+ presShellResolution = aMetrics.GetPresShellResolution()
+ * aMetrics.GetAsyncZoom().scale;
+ shell->SetResolutionAndScaleTo(presShellResolution);
+ }
+
+ // Do this as late as possible since scrolling can flush layout. It also
+ // adjusts the display port margins, so do it before we set those.
+ ScrollFrame(content, aMetrics);
+
+ SetDisplayPortMargins(shell, content, aMetrics);
+ SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
+}
+
+void
+APZCCallbackHelper::UpdateSubFrame(FrameMetrics& aMetrics)
+{
+ if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
+ return;
+ }
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+
+ // We don't currently support zooming for subframes, so nothing extra
+ // needs to be done beyond the tasks common to this and UpdateRootFrame.
+ ScrollFrame(content, aMetrics);
+ if (nsCOMPtr<nsIPresShell> shell = GetPresShell(content)) {
+ SetDisplayPortMargins(shell, content, aMetrics);
+ }
+ SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
+}
+
+bool
+APZCCallbackHelper::GetOrCreateScrollIdentifiers(nsIContent* aContent,
+ uint32_t* aPresShellIdOut,
+ FrameMetrics::ViewID* aViewIdOut)
+{
+ if (!aContent) {
+ return false;
+ }
+ *aViewIdOut = nsLayoutUtils::FindOrCreateIDFor(aContent);
+ if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
+ *aPresShellIdOut = shell->GetPresShellId();
+ return true;
+ }
+ return false;
+}
+
+void
+APZCCallbackHelper::InitializeRootDisplayport(nsIPresShell* aPresShell)
+{
+ // Create a view-id and set a zero-margin displayport for the root element
+ // of the root document in the chrome process. This ensures that the scroll
+ // frame for this element gets an APZC, which in turn ensures that all content
+ // in the chrome processes is covered by an APZC.
+ // The displayport is zero-margin because this element is generally not
+ // actually scrollable (if it is, APZC will set proper margins when it's
+ // scrolled).
+ if (!aPresShell) {
+ return;
+ }
+
+ MOZ_ASSERT(aPresShell->GetDocument());
+ nsIContent* content = aPresShell->GetDocument()->GetDocumentElement();
+ if (!content) {
+ return;
+ }
+
+ uint32_t presShellId;
+ FrameMetrics::ViewID viewId;
+ if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
+ // Note that the base rect that goes with these margins is set in
+ // nsRootBoxFrame::BuildDisplayList.
+ nsLayoutUtils::SetDisplayPortMargins(content, aPresShell, ScreenMargin(), 0,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ content->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::DoNotRepaint);
+ }
+}
+
+nsPresContext*
+APZCCallbackHelper::GetPresContextForContent(nsIContent* aContent)
+{
+ nsIDocument* doc = aContent->GetComposedDoc();
+ if (!doc) {
+ return nullptr;
+ }
+ nsIPresShell* shell = doc->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ return shell->GetPresContext();
+}
+
+nsIPresShell*
+APZCCallbackHelper::GetRootContentDocumentPresShellForContent(nsIContent* aContent)
+{
+ nsPresContext* context = GetPresContextForContent(aContent);
+ if (!context) {
+ return nullptr;
+ }
+ context = context->GetToplevelContentDocumentPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ return context->PresShell();
+}
+
+static nsIPresShell*
+GetRootDocumentPresShell(nsIContent* aContent)
+{
+ nsIDocument* doc = aContent->GetComposedDoc();
+ if (!doc) {
+ return nullptr;
+ }
+ nsIPresShell* shell = doc->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ nsPresContext* context = shell->GetPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ context = context->GetRootPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ return context->PresShell();
+}
+
+CSSPoint
+APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
+ const ScrollableLayerGuid& aGuid)
+{
+ CSSPoint input = aInput;
+ if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) {
+ return input;
+ }
+ nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
+ if (!content) {
+ return input;
+ }
+
+ // First, scale inversely by the root content document's pres shell
+ // resolution to cancel the scale-to-resolution transform that the
+ // compositor adds to the layer with the pres shell resolution. The points
+ // sent to Gecko by APZ don't have this transform unapplied (unlike other
+ // compositor-side transforms) because APZ doesn't know about it.
+ if (nsIPresShell* shell = GetRootDocumentPresShell(content)) {
+ input = input / shell->GetResolution();
+ }
+
+ // This represents any resolution on the Root Content Document (RCD)
+ // that's not on the Root Document (RD). That is, on platforms where
+ // RCD == RD, it's 1, and on platforms where RCD != RD, it's the RCD
+ // resolution. 'input' has this resolution applied, but the scroll
+ // delta retrieved below do not, so we need to apply them to the
+ // delta before adding the delta to 'input'. (Technically, deltas
+ // from scroll frames outside the RCD would already have this
+ // resolution applied, but we don't have such scroll frames in
+ // practice.)
+ float nonRootResolution = 1.0f;
+ if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
+ nonRootResolution = shell->GetCumulativeNonRootScaleResolution();
+ }
+ // Now apply the callback-transform. This is only approximately correct,
+ // see the comment on GetCumulativeApzCallbackTransform for details.
+ CSSPoint transform = nsLayoutUtils::GetCumulativeApzCallbackTransform(content->GetPrimaryFrame());
+ return input + transform * nonRootResolution;
+}
+
+LayoutDeviceIntPoint
+APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale)
+{
+ LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
+ point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
+ return LayoutDeviceIntPoint::Round(point);
+}
+
+void
+APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale)
+{
+ if (aEvent.AsTouchEvent()) {
+ WidgetTouchEvent& event = *(aEvent.AsTouchEvent());
+ for (size_t i = 0; i < event.mTouches.Length(); i++) {
+ event.mTouches[i]->mRefPoint = ApplyCallbackTransform(
+ event.mTouches[i]->mRefPoint, aGuid, aScale);
+ }
+ } else {
+ aEvent.mRefPoint = ApplyCallbackTransform(aEvent.mRefPoint, aGuid, aScale);
+ }
+}
+
+nsEventStatus
+APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent)
+{
+ nsEventStatus status = nsEventStatus_eConsumeNoDefault;
+ if (aEvent.mWidget) {
+ aEvent.mWidget->DispatchEvent(&aEvent, status);
+ }
+ return status;
+}
+
+nsEventStatus
+APZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessage aMsg,
+ uint64_t aTime,
+ const LayoutDevicePoint& aRefPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget)
+{
+ MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown ||
+ aMsg == eMouseUp || aMsg == eMouseLongTap);
+
+ WidgetMouseEvent event(true, aMsg, aWidget,
+ WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+ event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
+ event.mTime = aTime;
+ event.button = WidgetMouseEvent::eLeftButton;
+ event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+ if (aMsg == eMouseLongTap) {
+ event.mFlags.mOnlyChromeDispatch = true;
+ }
+ event.mIgnoreRootScrollFrame = true;
+ if (aMsg != eMouseMove) {
+ event.mClickCount = aClickCount;
+ }
+ event.mModifiers = aModifiers;
+ // Real touch events will generate corresponding pointer events. We set
+ // convertToPointer to false to prevent the synthesized mouse events generate
+ // pointer events again.
+ event.convertToPointer = false;
+ return DispatchWidgetEvent(event);
+}
+
+bool
+APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const nsString& aType,
+ const CSSPoint& aPoint,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame,
+ unsigned short aInputSourceArg)
+{
+ NS_ENSURE_TRUE(aPresShell, true);
+
+ bool defaultPrevented = false;
+ nsContentUtils::SendMouseEvent(aPresShell, aType, aPoint.x, aPoint.y,
+ aButton, nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED, aClickCount,
+ aModifiers, aIgnoreRootScrollFrame, 0, aInputSourceArg, false,
+ &defaultPrevented, false, /* aIsWidgetEventSynthesized = */ false);
+ return defaultPrevented;
+}
+
+
+void
+APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget)
+{
+ if (aWidget->Destroyed()) {
+ return;
+ }
+ APZCCH_LOG("Dispatching single-tap component events to %s\n",
+ Stringify(aPoint).c_str());
+ int time = 0;
+ DispatchSynthesizedMouseEvent(eMouseMove, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseDown, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseUp, time, aPoint, aModifiers, aClickCount, aWidget);
+}
+
+static dom::Element*
+GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
+{
+ if (!aScrollableFrame) {
+ return nullptr;
+ }
+ nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
+ if (!scrolledFrame) {
+ return nullptr;
+ }
+ // |scrolledFrame| should at this point be the root content frame of the
+ // nearest ancestor scrollable frame. The element corresponding to this
+ // frame should be the one with the displayport set on it, so find that
+ // element and return it.
+ nsIContent* content = scrolledFrame->GetContent();
+ MOZ_ASSERT(content->IsElement()); // roc says this must be true
+ return content->AsElement();
+}
+
+
+static dom::Element*
+GetRootDocumentElementFor(nsIWidget* aWidget)
+{
+ // This returns the root element that ChromeProcessController sets the
+ // displayport on during initialization.
+ if (nsView* view = nsView::GetViewFor(aWidget)) {
+ if (nsIPresShell* shell = view->GetPresShell()) {
+ MOZ_ASSERT(shell->GetDocument());
+ return shell->GetDocument()->GetDocumentElement();
+ }
+ }
+ return nullptr;
+}
+
+static nsIFrame*
+UpdateRootFrameForTouchTargetDocument(nsIFrame* aRootFrame)
+{
+ // No retargeting needed on desktop.
+ return aRootFrame;
+}
+
+// Determine the scrollable target frame for the given point and add it to
+// the target list. If the frame doesn't have a displayport, set one.
+// Return whether or not a displayport was set.
+static bool
+PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
+ const ScrollableLayerGuid& aGuid,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aRefPoint,
+ nsTArray<ScrollableLayerGuid>* aTargets)
+{
+ ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
+ nsPoint point =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
+ nsIFrame* target =
+ nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+ nsIScrollableFrame* scrollAncestor = target
+ ? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
+ : aRootFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
+
+ // Assuming that if there's no scrollAncestor, there's already a displayPort.
+ nsCOMPtr<dom::Element> dpElement = scrollAncestor
+ ? GetDisplayportElementFor(scrollAncestor)
+ : GetRootDocumentElementFor(aWidget);
+
+ nsAutoString dpElementDesc;
+ if (dpElement) {
+ dpElement->Describe(dpElementDesc);
+ }
+ APZCCH_LOG("For event at %s found scrollable element %p (%s)\n",
+ Stringify(aRefPoint).c_str(), dpElement.get(),
+ NS_LossyConvertUTF16toASCII(dpElementDesc).get());
+
+ bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+ dpElement, &(guid.mPresShellId), &(guid.mScrollId));
+ aTargets->AppendElement(guid);
+
+ if (!guidIsValid || nsLayoutUtils::HasDisplayPort(dpElement)) {
+ return false;
+ }
+
+ if (!scrollAncestor) {
+ MOZ_ASSERT(false); // If you hit this, please file a bug with STR.
+
+ // Attempt some sort of graceful handling based on a theory as to why we
+ // reach this point...
+ // If we get here, the document element is non-null, valid, but doesn't have
+ // a displayport. It's possible that the init code in ChromeProcessController
+ // failed for some reason, or the document element got swapped out at some
+ // later time. In this case let's try to set a displayport on the document
+ // element again and bail out on this operation.
+ APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n",
+ aWidget, dpElement.get());
+ APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresContext()->PresShell());
+ return false;
+ }
+
+ APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
+ bool activated = nsLayoutUtils::CalculateAndSetDisplayPortMargins(
+ scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
+ if (!activated) {
+ return false;
+ }
+
+ nsIFrame* frame = do_QueryFrame(scrollAncestor);
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(frame,
+ nsLayoutUtils::RepaintMode::Repaint);
+
+ return true;
+}
+
+static void
+SendLayersDependentApzcTargetConfirmation(nsIPresShell* aShell, uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ LayerManager* lm = aShell->GetLayerManager();
+ if (!lm) {
+ return;
+ }
+
+ LayerTransactionChild* shadow = lm->AsShadowForwarder()->GetShadowManager();
+ if (!shadow) {
+ return;
+ }
+
+ shadow->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
+}
+
+class DisplayportSetListener : public nsAPostRefreshObserver {
+public:
+ DisplayportSetListener(nsIPresShell* aPresShell,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+ : mPresShell(aPresShell)
+ , mInputBlockId(aInputBlockId)
+ , mTargets(aTargets)
+ {
+ }
+
+ virtual ~DisplayportSetListener()
+ {
+ }
+
+ void DidRefresh() override {
+ if (!mPresShell) {
+ MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
+ return;
+ }
+
+ APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
+ SendLayersDependentApzcTargetConfirmation(mPresShell, mInputBlockId, Move(mTargets));
+
+ if (!mPresShell->RemovePostRefreshObserver(this)) {
+ MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
+ // Graceful handling, just in case...
+ mPresShell = nullptr;
+ return;
+ }
+
+ delete this;
+ }
+
+private:
+ RefPtr<nsIPresShell> mPresShell;
+ uint64_t mInputBlockId;
+ nsTArray<ScrollableLayerGuid> mTargets;
+};
+
+// Sends a SetTarget notification for APZC, given one or more previous
+// calls to PrepareForAPZCSetTargetNotification().
+static void
+SendSetTargetAPZCNotificationHelper(nsIWidget* aWidget,
+ nsIPresShell* aShell,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets,
+ bool aWaitForRefresh)
+{
+ bool waitForRefresh = aWaitForRefresh;
+ if (waitForRefresh) {
+ APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n");
+ waitForRefresh = aShell->AddPostRefreshObserver(
+ new DisplayportSetListener(aShell, aInputBlockId, Move(aTargets)));
+ }
+ if (!waitForRefresh) {
+ APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
+ aWidget->SetConfirmedTargetAPZC(aInputBlockId, aTargets);
+ } else {
+ APZCCH_LOG("Successfully registered post-refresh observer\n");
+ }
+}
+
+void
+APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetGUIEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ if (!aWidget || !aDocument) {
+ return;
+ }
+ if (aInputBlockId == sLastTargetAPZCNotificationInputBlock) {
+ // We have already confirmed the target APZC for a previous event of this
+ // input block. If we activated a scroll frame for this input block,
+ // sending another target APZC confirmation would be harmful, as it might
+ // race the original confirmation (which needs to go through a layers
+ // transaction).
+ APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64 "\n", aInputBlockId);
+ return;
+ }
+ sLastTargetAPZCNotificationInputBlock = aInputBlockId;
+ if (nsIPresShell* shell = aDocument->GetShell()) {
+ if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+ rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
+ bool waitForRefresh = false;
+ nsTArray<ScrollableLayerGuid> targets;
+
+ if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
+ for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
+ waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets);
+ }
+ } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
+ waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, wheelEvent->mRefPoint, &targets);
+ } else if (const WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent()) {
+ waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, mouseEvent->mRefPoint, &targets);
+ }
+ // TODO: Do other types of events need to be handled?
+
+ if (!targets.IsEmpty()) {
+ SendSetTargetAPZCNotificationHelper(
+ aWidget,
+ shell,
+ aInputBlockId,
+ Move(targets),
+ waitForRefresh);
+ }
+ }
+ }
+}
+
+void
+APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
+ nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetTouchEvent& aEvent,
+ uint64_t aInputBlockId,
+ const SetAllowedTouchBehaviorCallback& aCallback)
+{
+ if (nsIPresShell* shell = aDocument->GetShell()) {
+ if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+ rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
+ nsTArray<TouchBehaviorFlags> flags;
+ for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ flags.AppendElement(
+ TouchActionHelper::GetAllowedTouchBehavior(aWidget,
+ rootFrame, aEvent.mTouches[i]->mRefPoint));
+ }
+ aCallback(aInputBlockId, Move(flags));
+ }
+ }
+}
+
+void
+APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+{
+ nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
+ if (!targetContent) {
+ return;
+ }
+ nsCOMPtr<nsIDocument> ownerDoc = targetContent->OwnerDoc();
+ if (!ownerDoc) {
+ return;
+ }
+
+ nsContentUtils::DispatchTrustedEvent(
+ ownerDoc, targetContent,
+ aEvent,
+ true, true);
+}
+
+void
+APZCCallbackHelper::NotifyFlushComplete(nsIPresShell* aShell)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // In some cases, flushing the APZ state to the main thread doesn't actually
+ // trigger a flush and repaint (this is an intentional optimization - the stuff
+ // visible to the user is still correct). However, reftests update their
+ // snapshot based on invalidation events that are emitted during paints,
+ // so we ensure that we kick off a paint when an APZ flush is done. Note that
+ // only chrome/testing code can trigger this behaviour.
+ if (aShell && aShell->GetRootFrame()) {
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ MOZ_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
+}
+
+static int32_t sActiveSuppressDisplayport = 0;
+static bool sDisplayPortSuppressionRespected = true;
+
+void
+APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell)
+{
+ if (aEnabled) {
+ sActiveSuppressDisplayport++;
+ } else {
+ bool isSuppressed = IsDisplayportSuppressed();
+ sActiveSuppressDisplayport--;
+ if (isSuppressed && !IsDisplayportSuppressed() &&
+ aShell && aShell->GetRootFrame()) {
+ // We unsuppressed the displayport, trigger a paint
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+ }
+
+ MOZ_ASSERT(sActiveSuppressDisplayport >= 0);
+}
+
+void
+APZCCallbackHelper::RespectDisplayPortSuppression(bool aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell)
+{
+ bool isSuppressed = IsDisplayportSuppressed();
+ sDisplayPortSuppressionRespected = aEnabled;
+ if (isSuppressed && !IsDisplayportSuppressed() &&
+ aShell && aShell->GetRootFrame()) {
+ // We unsuppressed the displayport, trigger a paint
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+}
+
+bool
+APZCCallbackHelper::IsDisplayportSuppressed()
+{
+ return sDisplayPortSuppressionRespected
+ && sActiveSuppressDisplayport > 0;
+}
+
+/* static */ bool
+APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
+{
+ return aFrame->IsProcessingAsyncScroll()
+ || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
+ || aFrame->LastSmoothScrollOrigin();
+}
+
+/* static */ void
+APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers,
+ nsIWidget* aWidget)
+{
+ EventMessage msg;
+ switch (aType) {
+ case PinchGestureInput::PINCHGESTURE_START:
+ msg = eMagnifyGestureStart;
+ break;
+ case PinchGestureInput::PINCHGESTURE_SCALE:
+ msg = eMagnifyGestureUpdate;
+ break;
+ case PinchGestureInput::PINCHGESTURE_END:
+ msg = eMagnifyGesture;
+ break;
+ case PinchGestureInput::PINCHGESTURE_SENTINEL:
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid gesture type");
+ return;
+ }
+
+ WidgetSimpleGestureEvent event(true, msg, aWidget);
+ event.mDelta = aSpanChange;
+ event.mModifiers = aModifiers;
+ DispatchWidgetEvent(event);
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/system/graphics/layers/apz/util/APZCCallbackHelper.h b/system/graphics/layers/apz/util/APZCCallbackHelper.h
new file mode 100644
index 000000000..8e888805c
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZCCallbackHelper.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCCallbackHelper_h
+#define mozilla_layers_APZCCallbackHelper_h
+
+#include "FrameMetrics.h"
+#include "InputData.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
+#include "mozilla/layers/APZUtils.h"
+#include "nsIDOMWindowUtils.h"
+
+class nsIContent;
+class nsIDocument;
+class nsIPresShell;
+class nsIScrollableFrame;
+class nsIWidget;
+template<class T> struct already_AddRefed;
+template<class T> class nsCOMPtr;
+
+namespace mozilla {
+namespace layers {
+
+typedef function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
+ SetAllowedTouchBehaviorCallback;
+
+/* This class contains some helper methods that facilitate implementing the
+ GeckoContentController callback interface required by the AsyncPanZoomController.
+ Since different platforms need to implement this interface in similar-but-
+ not-quite-the-same ways, this utility class provides some helpful methods
+ to hold code that can be shared across the different platform implementations.
+ */
+class APZCCallbackHelper
+{
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
+
+public:
+ /* Applies the scroll and zoom parameters from the given FrameMetrics object
+ to the root frame for the given metrics' scrollId. If tiled thebes layers
+ are enabled, this will align the displayport to tile boundaries. Setting
+ the scroll position can cause some small adjustments to be made to the
+ actual scroll position. aMetrics' display port and scroll position will
+ be updated with any modifications made. */
+ static void UpdateRootFrame(FrameMetrics& aMetrics);
+
+ /* Applies the scroll parameters from the given FrameMetrics object to the
+ subframe corresponding to given metrics' scrollId. If tiled thebes
+ layers are enabled, this will align the displayport to tile boundaries.
+ Setting the scroll position can cause some small adjustments to be made
+ to the actual scroll position. aMetrics' display port and scroll position
+ will be updated with any modifications made. */
+ static void UpdateSubFrame(FrameMetrics& aMetrics);
+
+ /* Get the presShellId and view ID for the given content element.
+ * If the view ID does not exist, one is created.
+ * The pres shell ID should generally already exist; if it doesn't for some
+ * reason, false is returned. */
+ static bool GetOrCreateScrollIdentifiers(nsIContent* aContent,
+ uint32_t* aPresShellIdOut,
+ FrameMetrics::ViewID* aViewIdOut);
+
+ /* Initialize a zero-margin displayport on the root document element of the
+ given presShell. */
+ static void InitializeRootDisplayport(nsIPresShell* aPresShell);
+
+ /* Get the pres context associated with the document enclosing |aContent|. */
+ static nsPresContext* GetPresContextForContent(nsIContent* aContent);
+
+ /* Get the pres shell associated with the root content document enclosing |aContent|. */
+ static nsIPresShell* GetRootContentDocumentPresShellForContent(nsIContent* aContent);
+
+ /* Apply an "input transform" to the given |aInput| and return the transformed value.
+ The input transform applied is the one for the content element corresponding to
+ |aGuid|; this is populated in a previous call to UpdateCallbackTransform. See that
+ method's documentations for details.
+ This method additionally adjusts |aInput| by inversely scaling by the provided
+ pres shell resolution, to cancel out a compositor-side transform (added in
+ bug 1076241) that APZ doesn't unapply. */
+ static CSSPoint ApplyCallbackTransform(const CSSPoint& aInput,
+ const ScrollableLayerGuid& aGuid);
+
+ /* Same as above, but operates on LayoutDeviceIntPoint.
+ Requires an additonal |aScale| parameter to convert between CSS and
+ LayoutDevice space. */
+ static mozilla::LayoutDeviceIntPoint
+ ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale);
+
+ /* Convenience function for applying a callback transform to all refpoints
+ * in the input event. */
+ static void ApplyCallbackTransform(WidgetEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale);
+
+ /* Dispatch a widget event via the widget stored in the event, if any.
+ * In a child process, allows the TabParent event-capture mechanism to
+ * intercept the event. */
+ static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
+
+ /* Synthesize a mouse event with the given parameters, and dispatch it
+ * via the given widget. */
+ static nsEventStatus DispatchSynthesizedMouseEvent(EventMessage aMsg,
+ uint64_t aTime,
+ const LayoutDevicePoint& aRefPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget);
+
+ /* Dispatch a mouse event with the given parameters.
+ * Return whether or not any listeners have called preventDefault on the event. */
+ static bool DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const nsString& aType,
+ const CSSPoint& aPoint,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame,
+ unsigned short aInputSourceArg);
+
+ /* Fire a single-tap event at the given point. The event is dispatched
+ * via the given widget. */
+ static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget);
+
+ /* Perform hit-testing on the touch points of |aEvent| to determine
+ * which scrollable frames they target. If any of these frames don't have
+ * a displayport, set one.
+ *
+ * If any displayports need to be set, the actual notification to APZ is
+ * sent to the compositor, which will then post a message back to APZ's
+ * controller thread. Otherwise, the provided widget's SetConfirmedTargetAPZC
+ * method is invoked immediately.
+ */
+ static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetGUIEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+
+ /* Figure out the allowed touch behaviors of each touch point in |aEvent|
+ * and send that information to the provided callback. */
+ static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetTouchEvent& aEvent,
+ uint64_t aInputBlockId,
+ const SetAllowedTouchBehaviorCallback& aCallback);
+
+ /* Notify content of a mouse scroll testing event. */
+ static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
+
+ /* Notify content that the repaint flush is complete. */
+ static void NotifyFlushComplete(nsIPresShell* aShell);
+
+ /* Temporarily ignore the Displayport for better paint performance. If at
+ * all possible, pass in a presShell if you have one at the call site, we
+ * use it to trigger a repaint once suppression is disabled. Without that
+ * the displayport may get left at the suppressed size for an extended
+ * period of time and result in unnecessary checkerboarding (see bug
+ * 1255054). */
+ static void SuppressDisplayport(const bool& aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell);
+
+ /* Whether or not displayport suppression should be turned on. Note that
+ * this only affects the return value of |IsDisplayportSuppressed()|, and
+ * doesn't change the value of the internal counter. As with
+ * SuppressDisplayport, this function should be passed a presShell to trigger
+ * a repaint if suppression is being turned off.
+ */
+ static void RespectDisplayPortSuppression(bool aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell);
+
+ /* Whether or not the displayport is currently suppressed. */
+ static bool IsDisplayportSuppressed();
+
+ static void
+ AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset);
+
+ /*
+ * Check if the scrollable frame is currently in the middle of an async
+ * or smooth scroll. We want to discard certain scroll input if this is
+ * true to prevent clobbering higher priority origins.
+ */
+ static bool
+ IsScrollInProgress(nsIScrollableFrame* aFrame);
+
+ /* Notify content of the progress of a pinch gesture that APZ won't do
+ * zooming for (because the apz.allow_zooming pref is false). This function
+ * will dispatch appropriate WidgetSimpleGestureEvent events to gecko.
+ */
+ static void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers,
+ nsIWidget* aWidget);
+private:
+ static uint64_t sLastTargetAPZCNotificationInputBlock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZCCallbackHelper_h */
diff --git a/system/graphics/layers/apz/util/APZEventState.cpp b/system/graphics/layers/apz/util/APZEventState.cpp
new file mode 100644
index 000000000..00a18d7b2
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZEventState.cpp
@@ -0,0 +1,509 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZEventState.h"
+
+#include "ActiveElementManager.h"
+#include "APZCCallbackHelper.h"
+#include "gfxPrefs.h"
+#include "LayersLogging.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "nsCOMPtr.h"
+#include "nsDocShell.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIScrollableFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "nsITimer.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIWidget.h"
+#include "nsLayoutUtils.h"
+#include "nsQueryFrame.h"
+#include "TouchManager.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsLayoutUtils.h"
+#include "nsIScrollableFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "mozilla/TouchEvents.h"
+
+#define APZES_LOG(...)
+// #define APZES_LOG(...) printf_stderr("APZES: " __VA_ARGS__)
+
+// Static helper functions
+namespace {
+
+int32_t
+WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
+{
+ int32_t result = 0;
+ if (aModifiers & mozilla::MODIFIER_SHIFT) {
+ result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
+ }
+ if (aModifiers & mozilla::MODIFIER_CONTROL) {
+ result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
+ }
+ if (aModifiers & mozilla::MODIFIER_ALT) {
+ result |= nsIDOMWindowUtils::MODIFIER_ALT;
+ }
+ if (aModifiers & mozilla::MODIFIER_META) {
+ result |= nsIDOMWindowUtils::MODIFIER_META;
+ }
+ if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
+ result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
+ }
+ if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_FN) {
+ result |= nsIDOMWindowUtils::MODIFIER_FN;
+ }
+ if (aModifiers & mozilla::MODIFIER_FNLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_FNLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_SYMBOL) {
+ result |= nsIDOMWindowUtils::MODIFIER_SYMBOL;
+ }
+ if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_OS) {
+ result |= nsIDOMWindowUtils::MODIFIER_OS;
+ }
+ return result;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace layers {
+
+static int32_t sActiveDurationMs = 10;
+static bool sActiveDurationMsSet = false;
+
+APZEventState::APZEventState(nsIWidget* aWidget,
+ ContentReceivedInputBlockCallback&& aCallback)
+ : mWidget(nullptr) // initialized in constructor body
+ , mActiveElementManager(new ActiveElementManager())
+ , mContentReceivedInputBlockCallback(Move(aCallback))
+ , mPendingTouchPreventedResponse(false)
+ , mPendingTouchPreventedBlockId(0)
+ , mEndTouchIsClick(false)
+ , mTouchEndCancelled(false)
+ , mLastTouchIdentifier(0)
+{
+ nsresult rv;
+ mWidget = do_GetWeakReference(aWidget, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "APZEventState constructed with a widget that"
+ " does not support weak references. APZ will NOT work!");
+
+ if (!sActiveDurationMsSet) {
+ Preferences::AddIntVarCache(&sActiveDurationMs,
+ "ui.touch_activation.duration_ms",
+ sActiveDurationMs);
+ sActiveDurationMsSet = true;
+ }
+}
+
+APZEventState::~APZEventState()
+{}
+
+class DelayedFireSingleTapEvent final : public nsITimerCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ DelayedFireSingleTapEvent(nsWeakPtr aWidget,
+ LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsITimer* aTimer)
+ : mWidget(aWidget)
+ , mPoint(aPoint)
+ , mModifiers(aModifiers)
+ , mClickCount(aClickCount)
+ // Hold the reference count until we are called back.
+ , mTimer(aTimer)
+ {
+ }
+
+ NS_IMETHOD Notify(nsITimer*) override
+ {
+ if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
+ APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, mClickCount, widget);
+ }
+ mTimer = nullptr;
+ return NS_OK;
+ }
+
+ void ClearTimer() {
+ mTimer = nullptr;
+ }
+
+private:
+ ~DelayedFireSingleTapEvent()
+ {
+ }
+
+ nsWeakPtr mWidget;
+ LayoutDevicePoint mPoint;
+ Modifiers mModifiers;
+ int32_t mClickCount;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
+
+void
+APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount)
+{
+ APZES_LOG("Handling single tap at %s on %s with %d\n",
+ Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return;
+ }
+
+ if (mTouchEndCancelled) {
+ return;
+ }
+
+ LayoutDevicePoint ldPoint = aPoint * aScale;
+ if (!mActiveElementManager->ActiveElementUsesStyle()) {
+ // If the active element isn't visually affected by the :active style, we
+ // have no need to wait the extra sActiveDurationMs to make the activation
+ // visually obvious to the user.
+ APZCCallbackHelper::FireSingleTapEvent(ldPoint, aModifiers, aClickCount, widget);
+ return;
+ }
+
+ APZES_LOG("Active element uses style, scheduling timer for click event\n");
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ RefPtr<DelayedFireSingleTapEvent> callback =
+ new DelayedFireSingleTapEvent(mWidget, ldPoint, aModifiers, aClickCount, timer);
+ nsresult rv = timer->InitWithCallback(callback,
+ sActiveDurationMs,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_FAILED(rv)) {
+ // Make |callback| not hold the timer, so they will both be destructed when
+ // we leave the scope of this function.
+ callback->ClearTimer();
+ }
+}
+
+bool
+APZEventState::FireContextmenuEvents(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const nsCOMPtr<nsIWidget>& aWidget)
+{
+ // Converting the modifiers to DOM format for the DispatchMouseEvent call
+ // is the most useless thing ever because nsDOMWindowUtils::SendMouseEvent
+ // just converts them back to widget format, but that API has many callers,
+ // including in JS code, so it's not trivial to change.
+ bool eventHandled =
+ APZCCallbackHelper::DispatchMouseEvent(aPresShell, NS_LITERAL_STRING("contextmenu"),
+ aPoint, 2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
+ nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+
+ APZES_LOG("Contextmenu event handled: %d\n", eventHandled);
+ if (eventHandled) {
+ // If the contextmenu event was handled then we're showing a contextmenu,
+ // and so we should remove any activation
+ mActiveElementManager->ClearActivation();
+#ifndef XP_WIN
+ } else {
+ // If the contextmenu wasn't consumed, fire the eMouseLongTap event.
+ nsEventStatus status = APZCCallbackHelper::DispatchSynthesizedMouseEvent(
+ eMouseLongTap, /*time*/ 0, aPoint * aScale, aModifiers,
+ /*clickCount*/ 1, aWidget);
+ eventHandled = (status == nsEventStatus_eConsumeNoDefault);
+ APZES_LOG("eMouseLongTap event handled: %d\n", eventHandled);
+#endif
+ }
+
+ return eventHandled;
+}
+
+void
+APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return;
+ }
+
+ SendPendingTouchPreventedResponse(false);
+
+#ifdef XP_WIN
+ // On Windows, we fire the contextmenu events when the user lifts their
+ // finger, in keeping with the platform convention. This happens in the
+ // ProcessLongTapUp function. However, we still fire the eMouseLongTap event
+ // at this time, because things like text selection or dragging may want
+ // to know about it.
+ nsEventStatus status = APZCCallbackHelper::DispatchSynthesizedMouseEvent(
+ eMouseLongTap, /*time*/ 0, aPoint * aScale, aModifiers, /*clickCount*/ 1,
+ widget);
+
+ bool eventHandled = (status == nsEventStatus_eConsumeNoDefault);
+#else
+ bool eventHandled = FireContextmenuEvents(aPresShell, aPoint, aScale,
+ aModifiers, widget);
+#endif
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
+
+ if (eventHandled) {
+ // Also send a touchcancel to content, so that listeners that might be
+ // waiting for a touchend don't trigger.
+ WidgetTouchEvent cancelTouchEvent(true, eTouchCancel, widget.get());
+ cancelTouchEvent.mModifiers = aModifiers;
+ auto ldPoint = LayoutDeviceIntPoint::Round(aPoint * aScale);
+ cancelTouchEvent.mTouches.AppendElement(new mozilla::dom::Touch(mLastTouchIdentifier,
+ ldPoint, LayoutDeviceIntPoint(), 0, 0));
+ APZCCallbackHelper::DispatchWidgetEvent(cancelTouchEvent);
+ }
+}
+
+void
+APZEventState::ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers)
+{
+#ifdef XP_WIN
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ FireContextmenuEvents(aPresShell, aPoint, aScale, aModifiers, widget);
+ }
+#endif
+}
+
+void
+APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse,
+ nsEventStatus aContentResponse)
+{
+ if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) {
+ mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget());
+ mLastTouchIdentifier = aEvent.mTouches[0]->Identifier();
+ }
+
+ bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault;
+ bool sentContentResponse = false;
+ APZES_LOG("Handling event type %d\n", aEvent.mMessage);
+ switch (aEvent.mMessage) {
+ case eTouchStart: {
+ mTouchEndCancelled = false;
+ sentContentResponse = SendPendingTouchPreventedResponse(false);
+ // sentContentResponse can be true here if we get two TOUCH_STARTs in a row
+ // and just responded to the first one.
+
+ // We're about to send a response back to APZ, but we should only do it
+ // for events that went through APZ (which should be all of them).
+ MOZ_ASSERT(aEvent.mFlags.mHandledByAPZ);
+
+ if (isTouchPrevented) {
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented);
+ sentContentResponse = true;
+ } else {
+ APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n",
+ aInputBlockId, Stringify(aGuid).c_str());
+ mPendingTouchPreventedResponse = true;
+ mPendingTouchPreventedGuid = aGuid;
+ mPendingTouchPreventedBlockId = aInputBlockId;
+ }
+ break;
+ }
+
+ case eTouchEnd:
+ if (isTouchPrevented) {
+ mTouchEndCancelled = true;
+ mEndTouchIsClick = false;
+ }
+ MOZ_FALLTHROUGH;
+ case eTouchCancel:
+ mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
+ MOZ_FALLTHROUGH;
+ case eTouchMove: {
+ if (mPendingTouchPreventedResponse) {
+ MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
+ }
+ sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented);
+ break;
+ }
+
+ default:
+ NS_WARNING("Unknown touch event type");
+ }
+
+ if (sentContentResponse &&
+ aApzResponse == nsEventStatus_eConsumeDoDefault &&
+ gfxPrefs::PointerEventsEnabled()) {
+ WidgetTouchEvent cancelEvent(aEvent);
+ cancelEvent.mMessage = eTouchCancel;
+ cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
+ for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
+ if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
+ touch->convertToPointer = true;
+ }
+ }
+ nsEventStatus status;
+ cancelEvent.mWidget->DispatchEvent(&cancelEvent, status);
+ }
+}
+
+void
+APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // If this event starts a swipe, indicate that it shouldn't result in a
+ // scroll by setting defaultPrevented to true.
+ bool defaultPrevented = aEvent.DefaultPrevented() || aEvent.TriggersSwipe();
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
+}
+
+void
+APZEventState::ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // If we get here and the drag block has not been confirmed by the code in
+ // nsSliderFrame, then no scrollbar reacted to the event thus APZC will
+ // ignore this drag block. We can send defaultPrevented as either true or
+ // false, it doesn't matter, because APZ won't have the scrollbar metrics
+ // anyway, and will know to drop the block.
+ bool defaultPrevented = false;
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
+}
+
+void
+APZEventState::ProcessAPZStateChange(ViewID aViewId,
+ APZStateChange aChange,
+ int aArg)
+{
+ switch (aChange)
+ {
+ case APZStateChange::eTransformBegin:
+ {
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+ if (sf) {
+ sf->SetTransformingByAPZ(true);
+ }
+ nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+ if (scrollbarMediator) {
+ scrollbarMediator->ScrollbarActivityStarted();
+ }
+
+ nsIContent* content = nsLayoutUtils::FindContentFor(aViewId);
+ nsIDocument* doc = content ? content->GetComposedDoc() : nullptr;
+ nsCOMPtr<nsIDocShell> docshell(doc ? doc->GetDocShell() : nullptr);
+ if (docshell && sf) {
+ nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+ nsdocshell->NotifyAsyncPanZoomStarted();
+ }
+ break;
+ }
+ case APZStateChange::eTransformEnd:
+ {
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+ if (sf) {
+ sf->SetTransformingByAPZ(false);
+ }
+ nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+ if (scrollbarMediator) {
+ scrollbarMediator->ScrollbarActivityStopped();
+ }
+
+ nsIContent* content = nsLayoutUtils::FindContentFor(aViewId);
+ nsIDocument* doc = content ? content->GetComposedDoc() : nullptr;
+ nsCOMPtr<nsIDocShell> docshell(doc ? doc->GetDocShell() : nullptr);
+ if (docshell && sf) {
+ nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+ nsdocshell->NotifyAsyncPanZoomStopped();
+ }
+ break;
+ }
+ case APZStateChange::eStartTouch:
+ {
+ mActiveElementManager->HandleTouchStart(aArg);
+ break;
+ }
+ case APZStateChange::eStartPanning:
+ {
+ // The user started to pan, so we don't want anything to be :active.
+ mActiveElementManager->ClearActivation();
+ break;
+ }
+ case APZStateChange::eEndTouch:
+ {
+ mEndTouchIsClick = aArg;
+ mActiveElementManager->HandleTouchEnd();
+ break;
+ }
+ case APZStateChange::eSentinel:
+ // Should never happen, but we want this case branch to stop the compiler
+ // whining about unhandled values.
+ MOZ_ASSERT(false);
+ break;
+ }
+}
+
+void
+APZEventState::ProcessClusterHit()
+{
+ // If we hit a cluster of links then we shouldn't activate any of them.
+ MOZ_ASSERT(false, "Cluster hits shouldn't happen on desktop!");
+ mActiveElementManager->ClearActivation();
+}
+
+bool
+APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
+{
+ if (mPendingTouchPreventedResponse) {
+ APZES_LOG("Sending response %d for pending guid: %s\n", aPreventDefault,
+ Stringify(mPendingTouchPreventedGuid).c_str());
+ mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
+ mPendingTouchPreventedBlockId, aPreventDefault);
+ mPendingTouchPreventedResponse = false;
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<nsIWidget>
+APZEventState::GetWidget() const
+{
+ nsCOMPtr<nsIWidget> result = do_QueryReferent(mWidget);
+ return result.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/APZEventState.h b/system/graphics/layers/apz/util/APZEventState.h
new file mode 100644
index 000000000..44188eaa7
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZEventState.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZEventState_h
+#define mozilla_layers_APZEventState_h
+
+#include <stdint.h>
+
+#include "FrameMetrics.h" // for ScrollableLayerGuid
+#include "Units.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
+#include "mozilla/layers/GeckoContentController.h" // for APZStateChange
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
+
+template <class> class nsCOMPtr;
+class nsIDocument;
+class nsIPresShell;
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ActiveElementManager;
+
+typedef function<void(const ScrollableLayerGuid&,
+ uint64_t /* input block id */,
+ bool /* prevent default */)>
+ ContentReceivedInputBlockCallback;
+
+/**
+ * A content-side component that keeps track of state for handling APZ
+ * gestures and sending APZ notifications.
+ */
+class APZEventState {
+ typedef GeckoContentController::APZStateChange APZStateChange;
+ typedef FrameMetrics::ViewID ViewID;
+public:
+ APZEventState(nsIWidget* aWidget,
+ ContentReceivedInputBlockCallback&& aCallback);
+
+ NS_INLINE_DECL_REFCOUNTING(APZEventState);
+
+ void ProcessSingleTap(const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount);
+ void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers);
+ void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse,
+ nsEventStatus aContentResponse);
+ void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessAPZStateChange(ViewID aViewId,
+ APZStateChange aChange,
+ int aArg);
+ void ProcessClusterHit();
+private:
+ ~APZEventState();
+ bool SendPendingTouchPreventedResponse(bool aPreventDefault);
+ bool FireContextmenuEvents(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const nsCOMPtr<nsIWidget>& aWidget);
+ already_AddRefed<nsIWidget> GetWidget() const;
+private:
+ nsWeakPtr mWidget;
+ RefPtr<ActiveElementManager> mActiveElementManager;
+ ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
+ bool mPendingTouchPreventedResponse;
+ ScrollableLayerGuid mPendingTouchPreventedGuid;
+ uint64_t mPendingTouchPreventedBlockId;
+ bool mEndTouchIsClick;
+ bool mTouchEndCancelled;
+ int32_t mLastTouchIdentifier;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZEventState_h */
diff --git a/system/graphics/layers/apz/util/APZThreadUtils.cpp b/system/graphics/layers/apz/util/APZThreadUtils.cpp
new file mode 100644
index 000000000..e1975a2a0
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZThreadUtils.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZThreadUtils.h"
+
+#include "mozilla/layers/Compositor.h"
+
+namespace mozilla {
+namespace layers {
+
+static bool sThreadAssertionsEnabled = true;
+static MessageLoop* sControllerThread;
+
+/*static*/ void
+APZThreadUtils::SetThreadAssertionsEnabled(bool aEnabled) {
+ sThreadAssertionsEnabled = aEnabled;
+}
+
+/*static*/ bool
+APZThreadUtils::GetThreadAssertionsEnabled() {
+ return sThreadAssertionsEnabled;
+}
+
+/*static*/ void
+APZThreadUtils::SetControllerThread(MessageLoop* aLoop)
+{
+ // We must either be setting the initial controller thread, or removing it,
+ // or re-using an existing controller thread.
+ MOZ_ASSERT(!sControllerThread || !aLoop || sControllerThread == aLoop);
+ sControllerThread = aLoop;
+}
+
+/*static*/ void
+APZThreadUtils::AssertOnControllerThread() {
+ if (!GetThreadAssertionsEnabled()) {
+ return;
+ }
+
+ MOZ_ASSERT(sControllerThread == MessageLoop::current());
+}
+
+/*static*/ void
+APZThreadUtils::AssertOnCompositorThread()
+{
+ if (GetThreadAssertionsEnabled()) {
+ Compositor::AssertOnCompositorThread();
+ }
+}
+
+/*static*/ void
+APZThreadUtils::RunOnControllerThread(already_AddRefed<Runnable> aTask)
+{
+ RefPtr<Runnable> task = aTask;
+
+ if (!sControllerThread) {
+ // Could happen on startup
+ NS_WARNING("Dropping task posted to controller thread");
+ return;
+ }
+
+ if (sControllerThread == MessageLoop::current()) {
+ task->Run();
+ } else {
+ sControllerThread->PostTask(task.forget());
+ }
+}
+
+/*static*/ bool
+APZThreadUtils::IsControllerThread()
+{
+ return sControllerThread == MessageLoop::current();
+}
+
+NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/APZThreadUtils.h b/system/graphics/layers/apz/util/APZThreadUtils.h
new file mode 100644
index 000000000..4b9b2c0d0
--- /dev/null
+++ b/system/graphics/layers/apz/util/APZThreadUtils.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZThreadUtils_h
+#define mozilla_layers_APZThreadUtils_h
+
+#include "base/message_loop.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+
+class Runnable;
+
+namespace layers {
+
+class APZThreadUtils
+{
+public:
+ /**
+ * In the gtest environment everything runs on one thread, so we
+ * shouldn't assert that we're on a particular thread. This enables
+ * that behaviour.
+ */
+ static void SetThreadAssertionsEnabled(bool aEnabled);
+ static bool GetThreadAssertionsEnabled();
+
+ /**
+ * Set the controller thread.
+ */
+ static void SetControllerThread(MessageLoop* aLoop);
+
+ /**
+ * This can be used to assert that the current thread is the
+ * controller/UI thread (on which input events are received).
+ * This does nothing if thread assertions are disabled.
+ */
+ static void AssertOnControllerThread();
+
+ /**
+ * This can be used to assert that the current thread is the
+ * compositor thread (which applies the async transform).
+ * This does nothing if thread assertions are disabled.
+ */
+ static void AssertOnCompositorThread();
+
+ /**
+ * Run the given task on the APZ "controller thread" for this platform. If
+ * this function is called from the controller thread itself then the task is
+ * run immediately without getting queued.
+ */
+ static void RunOnControllerThread(already_AddRefed<Runnable> aTask);
+
+ /**
+ * Returns true if currently on APZ "controller thread".
+ */
+ static bool IsControllerThread();
+};
+
+// A base class for GenericTimerCallback<Function>.
+// This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
+// template.
+class GenericTimerCallbackBase : public nsITimerCallback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+ virtual ~GenericTimerCallbackBase() {}
+};
+
+// An nsITimerCallback implementation that can be used with any function
+// object that's callable with no arguments.
+template <typename Function>
+class GenericTimerCallback final : public GenericTimerCallbackBase
+{
+public:
+ explicit GenericTimerCallback(const Function& aFunction) : mFunction(aFunction) {}
+
+ NS_IMETHOD Notify(nsITimer*) override
+ {
+ mFunction();
+ return NS_OK;
+ }
+private:
+ Function mFunction;
+};
+
+// Convenience function for constructing a GenericTimerCallback.
+// Returns a raw pointer, suitable for passing directly as an argument to
+// nsITimer::InitWithCallback(). The intention is to enable the following
+// terse inline usage:
+// timer->InitWithCallback(NewTimerCallback([](){ ... }), delay);
+template <typename Function>
+GenericTimerCallback<Function>* NewTimerCallback(const Function& aFunction)
+{
+ return new GenericTimerCallback<Function>(aFunction);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZThreadUtils_h */
diff --git a/system/graphics/layers/apz/util/ActiveElementManager.cpp b/system/graphics/layers/apz/util/ActiveElementManager.cpp
new file mode 100644
index 000000000..20d34aa2b
--- /dev/null
+++ b/system/graphics/layers/apz/util/ActiveElementManager.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActiveElementManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/Preferences.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "mozilla/dom/Element.h"
+#include "nsIDocument.h"
+#include "nsStyleSet.h"
+
+#define AEM_LOG(...)
+// #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+static int32_t sActivationDelayMs = 100;
+static bool sActivationDelayMsSet = false;
+
+ActiveElementManager::ActiveElementManager()
+ : mCanBePan(false),
+ mCanBePanSet(false),
+ mSetActiveTask(nullptr),
+ mActiveElementUsesStyle(false)
+{
+ if (!sActivationDelayMsSet) {
+ Preferences::AddIntVarCache(&sActivationDelayMs,
+ "ui.touch_activation.delay_ms",
+ sActivationDelayMs);
+ sActivationDelayMsSet = true;
+ }
+}
+
+ActiveElementManager::~ActiveElementManager() {}
+
+void
+ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget)
+{
+ if (mTarget) {
+ // Multiple fingers on screen (since HandleTouchEnd clears mTarget).
+ AEM_LOG("Multiple fingers on-screen, clearing target element\n");
+ CancelTask();
+ ResetActive();
+ ResetTouchBlockState();
+ return;
+ }
+
+ mTarget = do_QueryInterface(aTarget);
+ AEM_LOG("Setting target element to %p\n", mTarget.get());
+ TriggerElementActivation();
+}
+
+void
+ActiveElementManager::HandleTouchStart(bool aCanBePan)
+{
+ AEM_LOG("Touch start, aCanBePan: %d\n", aCanBePan);
+ if (mCanBePanSet) {
+ // Multiple fingers on screen (since HandleTouchEnd clears mCanBePanSet).
+ AEM_LOG("Multiple fingers on-screen, clearing touch block state\n");
+ CancelTask();
+ ResetActive();
+ ResetTouchBlockState();
+ return;
+ }
+
+ mCanBePan = aCanBePan;
+ mCanBePanSet = true;
+ TriggerElementActivation();
+}
+
+void
+ActiveElementManager::TriggerElementActivation()
+{
+ // Both HandleTouchStart() and SetTargetElement() call this. They can be
+ // called in either order. One will set mCanBePanSet, and the other, mTarget.
+ // We want to actually trigger the activation once both are set.
+ if (!(mTarget && mCanBePanSet)) {
+ return;
+ }
+
+ // If the touch cannot be a pan, make mTarget :active right away.
+ // Otherwise, wait a bit to see if the user will pan or not.
+ if (!mCanBePan) {
+ SetActive(mTarget);
+ } else {
+ CancelTask(); // this is only needed because of bug 1169802. Fixing that
+ // bug properly should make this unnecessary.
+ MOZ_ASSERT(mSetActiveTask == nullptr);
+
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<nsCOMPtr<dom::Element>>(this,
+ &ActiveElementManager::SetActiveTask,
+ mTarget);
+ mSetActiveTask = task;
+ MessageLoop::current()->PostDelayedTask(task.forget(), sActivationDelayMs);
+ AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask);
+ }
+}
+
+void
+ActiveElementManager::ClearActivation()
+{
+ AEM_LOG("Clearing element activation\n");
+ CancelTask();
+ ResetActive();
+}
+
+void
+ActiveElementManager::HandleTouchEndEvent(bool aWasClick)
+{
+ AEM_LOG("Touch end event, aWasClick: %d\n", aWasClick);
+
+ // If the touch was a click, make mTarget :active right away.
+ // nsEventStateManager will reset the active element when processing
+ // the mouse-down event generated by the click.
+ CancelTask();
+ if (aWasClick) {
+ SetActive(mTarget);
+ } else {
+ // We might reach here if mCanBePan was false on touch-start and
+ // so we set the element active right away. Now it turns out the
+ // action was not a click so we need to reset the active element.
+ ResetActive();
+ }
+
+ ResetTouchBlockState();
+}
+
+void
+ActiveElementManager::HandleTouchEnd()
+{
+ AEM_LOG("Touch end, clearing pan state\n");
+ mCanBePanSet = false;
+}
+
+bool
+ActiveElementManager::ActiveElementUsesStyle() const
+{
+ return mActiveElementUsesStyle;
+}
+
+static nsPresContext*
+GetPresContextFor(nsIContent* aContent)
+{
+ if (!aContent) {
+ return nullptr;
+ }
+ nsIPresShell* shell = aContent->OwnerDoc()->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ return shell->GetPresContext();
+}
+
+static bool
+ElementHasActiveStyle(dom::Element* aElement)
+{
+ nsPresContext* pc = GetPresContextFor(aElement);
+ if (!pc) {
+ return false;
+ }
+ StyleSetHandle styleSet = pc->StyleSet();
+ for (dom::Element* e = aElement; e; e = e->GetParentElement()) {
+ if (styleSet->HasStateDependentStyle(e, NS_EVENT_STATE_ACTIVE)) {
+ AEM_LOG("Element %p's style is dependent on the active state\n", e);
+ return true;
+ }
+ }
+ AEM_LOG("Element %p doesn't use active styles\n", aElement);
+ return false;
+}
+
+void
+ActiveElementManager::SetActive(dom::Element* aTarget)
+{
+ AEM_LOG("Setting active %p\n", aTarget);
+
+ if (nsPresContext* pc = GetPresContextFor(aTarget)) {
+ pc->EventStateManager()->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE);
+ mActiveElementUsesStyle = ElementHasActiveStyle(aTarget);
+ }
+}
+
+void
+ActiveElementManager::ResetActive()
+{
+ AEM_LOG("Resetting active from %p\n", mTarget.get());
+
+ // Clear the :active flag from mTarget by setting it on the document root.
+ if (mTarget) {
+ dom::Element* root = mTarget->OwnerDoc()->GetDocumentElement();
+ if (root) {
+ AEM_LOG("Found root %p, making active\n", root);
+ SetActive(root);
+ }
+ }
+}
+
+void
+ActiveElementManager::ResetTouchBlockState()
+{
+ mTarget = nullptr;
+ mCanBePanSet = false;
+}
+
+void
+ActiveElementManager::SetActiveTask(const nsCOMPtr<dom::Element>& aTarget)
+{
+ AEM_LOG("mSetActiveTask %p running\n", mSetActiveTask);
+
+ // This gets called from mSetActiveTask's Run() method. The message loop
+ // deletes the task right after running it, so we need to null out
+ // mSetActiveTask to make sure we're not left with a dangling pointer.
+ mSetActiveTask = nullptr;
+ SetActive(aTarget);
+}
+
+void
+ActiveElementManager::CancelTask()
+{
+ AEM_LOG("Cancelling task %p\n", mSetActiveTask);
+
+ if (mSetActiveTask) {
+ mSetActiveTask->Cancel();
+ mSetActiveTask = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/ActiveElementManager.h b/system/graphics/layers/apz/util/ActiveElementManager.h
new file mode 100644
index 000000000..83d0cb29f
--- /dev/null
+++ b/system/graphics/layers/apz/util/ActiveElementManager.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ActiveElementManager_h
+#define mozilla_layers_ActiveElementManager_h
+
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace dom {
+class Element;
+class EventTarget;
+} // namespace dom
+
+namespace layers {
+
+/**
+ * Manages setting and clearing the ':active' CSS pseudostate in the presence
+ * of touch input.
+ */
+class ActiveElementManager {
+ ~ActiveElementManager();
+public:
+ NS_INLINE_DECL_REFCOUNTING(ActiveElementManager)
+
+ ActiveElementManager();
+
+ /**
+ * Specify the target of a touch. Typically this should be called right
+ * after HandleTouchStart(), but in cases where the APZ needs to wait for
+ * a content response the HandleTouchStart() may be delayed, in which case
+ * this function can be called first.
+ * |aTarget| may be nullptr.
+ */
+ void SetTargetElement(dom::EventTarget* aTarget);
+ /**
+ * Handle a touch-start state notification from APZ. This notification
+ * may be delayed until after touch listeners have responded to the APZ.
+ * @param aCanBePan whether the touch can be a pan
+ */
+ void HandleTouchStart(bool aCanBePan);
+ /**
+ * Clear the active element.
+ */
+ void ClearActivation();
+ /**
+ * Handle a touch-end or touch-cancel event.
+ * @param aWasClick whether the touch was a click
+ */
+ void HandleTouchEndEvent(bool aWasClick);
+ /**
+ * Handle a touch-end state notification from APZ. This notification may be
+ * delayed until after touch listeners have responded to the APZ.
+ */
+ void HandleTouchEnd();
+ /**
+ * @return true iff the currently active element (or one of its ancestors)
+ * actually had a style for the :active pseudo-class. The currently active
+ * element is the root element if no other elements are active.
+ */
+ bool ActiveElementUsesStyle() const;
+private:
+ /**
+ * The target of the first touch point in the current touch block.
+ */
+ nsCOMPtr<dom::Element> mTarget;
+ /**
+ * Whether the current touch block can be a pan. Set in HandleTouchStart().
+ */
+ bool mCanBePan;
+ /**
+ * Whether mCanBePan has been set for the current touch block.
+ * We need to keep track of this to allow HandleTouchStart() and
+ * SetTargetElement() to be called in either order.
+ */
+ bool mCanBePanSet;
+ /**
+ * A task for calling SetActive() after a timeout.
+ */
+ RefPtr<CancelableRunnable> mSetActiveTask;
+ /**
+ * See ActiveElementUsesStyle() documentation.
+ */
+ bool mActiveElementUsesStyle;
+
+ // Helpers
+ void TriggerElementActivation();
+ void SetActive(dom::Element* aTarget);
+ void ResetActive();
+ void ResetTouchBlockState();
+ void SetActiveTask(const nsCOMPtr<dom::Element>& aTarget);
+ void CancelTask();
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ActiveElementManager_h */
diff --git a/system/graphics/layers/apz/util/CheckerboardReportService.cpp b/system/graphics/layers/apz/util/CheckerboardReportService.cpp
new file mode 100644
index 000000000..0924fe92d
--- /dev/null
+++ b/system/graphics/layers/apz/util/CheckerboardReportService.cpp
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CheckerboardReportService.h"
+
+#include "gfxPrefs.h" // for gfxPrefs
+#include "jsapi.h" // for JS_Now
+#include "MainThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/Assertions.h" // for MOZ_ASSERT
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/Unused.h"
+#include "mozilla/dom/CheckerboardReportServiceBinding.h" // for dom::CheckerboardReports
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "nsContentUtils.h" // for nsContentUtils
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace layers {
+
+/*static*/ StaticRefPtr<CheckerboardEventStorage> CheckerboardEventStorage::sInstance;
+
+/*static*/ already_AddRefed<CheckerboardEventStorage>
+CheckerboardEventStorage::GetInstance()
+{
+ // The instance in the parent process does all the work, so if this is getting
+ // called in the child process something is likely wrong.
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sInstance) {
+ sInstance = new CheckerboardEventStorage();
+ ClearOnShutdown(&sInstance);
+ }
+ RefPtr<CheckerboardEventStorage> instance = sInstance.get();
+ return instance.forget();
+}
+
+void
+CheckerboardEventStorage::Report(uint32_t aSeverity, const std::string& aLog)
+{
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction([aSeverity, aLog] () -> void {
+ CheckerboardEventStorage::Report(aSeverity, aLog);
+ });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+
+ if (XRE_IsGPUProcess()) {
+ if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
+ nsCString log(aLog.c_str());
+ Unused << gpu->SendReportCheckerboard(aSeverity, log);
+ }
+ return;
+ }
+
+ RefPtr<CheckerboardEventStorage> storage = GetInstance();
+ storage->ReportCheckerboard(aSeverity, aLog);
+}
+
+void
+CheckerboardEventStorage::ReportCheckerboard(uint32_t aSeverity, const std::string& aLog)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (aSeverity == 0) {
+ // This code assumes all checkerboard reports have a nonzero severity.
+ return;
+ }
+
+ CheckerboardReport severe(aSeverity, JS_Now(), aLog);
+ CheckerboardReport recent;
+
+ // First look in the "severe" reports to see if the new one belongs in that
+ // list.
+ for (int i = 0; i < SEVERITY_MAX_INDEX; i++) {
+ if (mCheckerboardReports[i].mSeverity >= severe.mSeverity) {
+ continue;
+ }
+ // The new one deserves to be in the "severe" list. Take the one getting
+ // bumped off the list, and put it in |recent| for possible insertion into
+ // the recents list.
+ recent = mCheckerboardReports[SEVERITY_MAX_INDEX - 1];
+
+ // Shuffle the severe list down, insert the new one.
+ for (int j = SEVERITY_MAX_INDEX - 1; j > i; j--) {
+ mCheckerboardReports[j] = mCheckerboardReports[j - 1];
+ }
+ mCheckerboardReports[i] = severe;
+ severe.mSeverity = 0; // mark |severe| as inserted
+ break;
+ }
+
+ // If |severe.mSeverity| is nonzero, the incoming report didn't get inserted
+ // into the severe list; put it into |recent| for insertion into the recent
+ // list.
+ if (severe.mSeverity) {
+ MOZ_ASSERT(recent.mSeverity == 0, "recent should be empty here");
+ recent = severe;
+ } // else |recent| may hold a report that got knocked out of the severe list.
+
+ if (recent.mSeverity == 0) {
+ // Nothing to be inserted into the recent list.
+ return;
+ }
+
+ // If it wasn't in the "severe" list, add it to the "recent" list.
+ for (int i = SEVERITY_MAX_INDEX; i < RECENT_MAX_INDEX; i++) {
+ if (mCheckerboardReports[i].mTimestamp >= recent.mTimestamp) {
+ continue;
+ }
+ // |recent| needs to be inserted at |i|. Shuffle the remaining ones down
+ // and insert it.
+ for (int j = RECENT_MAX_INDEX - 1; j > i; j--) {
+ mCheckerboardReports[j] = mCheckerboardReports[j - 1];
+ }
+ mCheckerboardReports[i] = recent;
+ break;
+ }
+}
+
+void
+CheckerboardEventStorage::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ for (int i = 0; i < RECENT_MAX_INDEX; i++) {
+ CheckerboardReport& r = mCheckerboardReports[i];
+ if (r.mSeverity == 0) {
+ continue;
+ }
+ dom::CheckerboardReport report;
+ report.mSeverity.Construct() = r.mSeverity;
+ report.mTimestamp.Construct() = r.mTimestamp / 1000; // micros to millis
+ report.mLog.Construct() = NS_ConvertUTF8toUTF16(r.mLog.c_str(), r.mLog.size());
+ report.mReason.Construct() = (i < SEVERITY_MAX_INDEX)
+ ? dom::CheckerboardReason::Severe
+ : dom::CheckerboardReason::Recent;
+ aOutReports.AppendElement(report);
+ }
+}
+
+} // namespace layers
+
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CheckerboardReportService, mParent)
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CheckerboardReportService, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CheckerboardReportService, Release)
+
+/*static*/ bool
+CheckerboardReportService::IsEnabled(JSContext* aCtx, JSObject* aGlobal)
+{
+ // Only allow this in the parent process
+ if (!XRE_IsParentProcess()) {
+ return false;
+ }
+ // Allow privileged code or about:checkerboard (unprivileged) to access this.
+ return nsContentUtils::IsCallerChrome()
+ || nsContentUtils::IsSpecificAboutPage(aGlobal, "about:checkerboard");
+}
+
+/*static*/ already_AddRefed<CheckerboardReportService>
+CheckerboardReportService::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv)
+{
+ RefPtr<CheckerboardReportService> ces = new CheckerboardReportService(aGlobal.GetAsSupports());
+ return ces.forget();
+}
+
+CheckerboardReportService::CheckerboardReportService(nsISupports* aParent)
+ : mParent(aParent)
+{
+}
+
+JSObject*
+CheckerboardReportService::WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto)
+{
+ return CheckerboardReportServiceBinding::Wrap(aCtx, this, aGivenProto);
+}
+
+nsISupports*
+CheckerboardReportService::GetParentObject()
+{
+ return mParent;
+}
+
+void
+CheckerboardReportService::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
+{
+ RefPtr<mozilla::layers::CheckerboardEventStorage> instance =
+ mozilla::layers::CheckerboardEventStorage::GetInstance();
+ MOZ_ASSERT(instance);
+ instance->GetReports(aOutReports);
+}
+
+bool
+CheckerboardReportService::IsRecordingEnabled() const
+{
+ return gfxPrefs::APZRecordCheckerboarding();
+}
+
+void
+CheckerboardReportService::SetRecordingEnabled(bool aEnabled)
+{
+ gfxPrefs::SetAPZRecordCheckerboarding(aEnabled);
+}
+
+void
+CheckerboardReportService::FlushActiveReports()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
+ if (gpu && gpu->NotifyGpuObservers("APZ:FlushActiveCheckerboard")) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/CheckerboardReportService.h b/system/graphics/layers/apz/util/CheckerboardReportService.h
new file mode 100644
index 000000000..743b29825
--- /dev/null
+++ b/system/graphics/layers/apz/util/CheckerboardReportService.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CheckerboardReportService_h
+#define mozilla_dom_CheckerboardReportService_h
+
+#include <string>
+
+#include "js/TypeDecls.h" // for JSContext, JSObject
+#include "mozilla/ErrorResult.h" // for ErrorResult
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsWrapperCache.h" // for nsWrapperCache
+
+namespace mozilla {
+
+namespace dom {
+struct CheckerboardReport;
+}
+
+namespace layers {
+
+// CheckerboardEventStorage is a singleton that stores info on checkerboard
+// events, so that they can be accessed from about:checkerboard and visualized.
+// Note that this class is NOT threadsafe, and all methods must be called on
+// the main thread.
+class CheckerboardEventStorage
+{
+ NS_INLINE_DECL_REFCOUNTING(CheckerboardEventStorage)
+
+public:
+ /**
+ * Get the singleton instance.
+ */
+ static already_AddRefed<CheckerboardEventStorage> GetInstance();
+
+ /**
+ * Get the stored checkerboard reports.
+ */
+ void GetReports(nsTArray<dom::CheckerboardReport>& aOutReports);
+
+ /**
+ * Save a checkerboard event log, optionally dropping older ones that were
+ * less severe or less recent. Zero-severity reports may be ignored entirely.
+ */
+ static void Report(uint32_t aSeverity, const std::string& aLog);
+
+private:
+ /* Stuff for refcounted singleton */
+ CheckerboardEventStorage() {}
+ virtual ~CheckerboardEventStorage() {}
+
+ static StaticRefPtr<CheckerboardEventStorage> sInstance;
+
+ void ReportCheckerboard(uint32_t aSeverity, const std::string& aLog);
+
+private:
+ /**
+ * Struct that this class uses internally to store a checkerboard report.
+ */
+ struct CheckerboardReport {
+ uint32_t mSeverity; // if 0, this report is empty
+ int64_t mTimestamp; // microseconds since epoch, as from JS_Now()
+ std::string mLog;
+
+ CheckerboardReport()
+ : mSeverity(0)
+ , mTimestamp(0)
+ {}
+
+ CheckerboardReport(uint32_t aSeverity, int64_t aTimestamp,
+ const std::string& aLog)
+ : mSeverity(aSeverity)
+ , mTimestamp(aTimestamp)
+ , mLog(aLog)
+ {}
+ };
+
+ // The first 5 (indices 0-4) are the most severe ones in decreasing order
+ // of severity; the next 5 (indices 5-9) are the most recent ones that are
+ // not already in the "severe" list.
+ static const int SEVERITY_MAX_INDEX = 5;
+ static const int RECENT_MAX_INDEX = 10;
+ CheckerboardReport mCheckerboardReports[RECENT_MAX_INDEX];
+};
+
+} // namespace layers
+
+namespace dom {
+
+class GlobalObject;
+
+/**
+ * CheckerboardReportService is a wrapper object that allows access to the
+ * stuff in CheckerboardEventStorage (above). We need this wrapper for proper
+ * garbage/cycle collection, since this can be accessed from JS.
+ */
+class CheckerboardReportService : public nsWrapperCache
+{
+public:
+ /**
+ * Check if the given page is allowed to access this object via the WebIDL
+ * bindings. It only returns true if the page is about:checkerboard.
+ */
+ static bool IsEnabled(JSContext* aCtx, JSObject* aGlobal);
+
+ /*
+ * Other standard WebIDL binding glue.
+ */
+
+ static already_AddRefed<CheckerboardReportService>
+ Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv);
+
+ explicit CheckerboardReportService(nsISupports* aSupports);
+
+ JSObject* WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto) override;
+
+ nsISupports* GetParentObject();
+
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CheckerboardReportService)
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CheckerboardReportService)
+
+public:
+ /*
+ * The methods exposed via the webidl.
+ */
+ void GetReports(nsTArray<dom::CheckerboardReport>& aOutReports);
+ bool IsRecordingEnabled() const;
+ void SetRecordingEnabled(bool aEnabled);
+ void FlushActiveReports();
+
+private:
+ virtual ~CheckerboardReportService() {}
+
+ nsCOMPtr<nsISupports> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_layers_CheckerboardReportService_h */
diff --git a/system/graphics/layers/apz/util/ChromeProcessController.cpp b/system/graphics/layers/apz/util/ChromeProcessController.cpp
new file mode 100644
index 000000000..ac8b3824f
--- /dev/null
+++ b/system/graphics/layers/apz/util/ChromeProcessController.cpp
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ChromeProcessController.h"
+
+#include "MainThreadUtils.h" // for NS_IsMainThread()
+#include "base/message_loop.h" // for MessageLoop
+#include "mozilla/dom/Element.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/DoubleTapToZoom.h"
+#include "nsIDocument.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIPresShell.h"
+#include "nsLayoutUtils.h"
+#include "nsView.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+
+ChromeProcessController::ChromeProcessController(nsIWidget* aWidget,
+ APZEventState* aAPZEventState,
+ IAPZCTreeManager* aAPZCTreeManager)
+ : mWidget(aWidget)
+ , mAPZEventState(aAPZEventState)
+ , mAPZCTreeManager(aAPZCTreeManager)
+ , mUILoop(MessageLoop::current())
+{
+ // Otherwise we're initializing mUILoop incorrectly.
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aAPZEventState);
+ MOZ_ASSERT(aAPZCTreeManager);
+
+ mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::InitializeRoot));
+}
+
+ChromeProcessController::~ChromeProcessController() {}
+
+void
+ChromeProcessController::InitializeRoot()
+{
+ APZCCallbackHelper::InitializeRootDisplayport(GetPresShell());
+}
+
+void
+ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ FrameMetrics metrics = aFrameMetrics;
+ if (metrics.IsRootContent()) {
+ APZCCallbackHelper::UpdateRootFrame(metrics);
+ } else {
+ APZCCallbackHelper::UpdateSubFrame(metrics);
+ }
+}
+
+void
+ChromeProcessController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
+{
+ MessageLoop::current()->PostDelayedTask(Move(aTask), aDelayMs);
+}
+
+bool
+ChromeProcessController::IsRepaintThread()
+{
+ return NS_IsMainThread();
+}
+
+void
+ChromeProcessController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ NS_DispatchToMainThread(Move(aTask));
+}
+
+void
+ChromeProcessController::Destroy()
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::Destroy));
+ return;
+ }
+
+ MOZ_ASSERT(MessageLoop::current() == mUILoop);
+ mWidget = nullptr;
+ mAPZEventState = nullptr;
+}
+
+nsIPresShell*
+ChromeProcessController::GetPresShell() const
+{
+ if (!mWidget) {
+ return nullptr;
+ }
+ if (nsView* view = nsView::GetViewFor(mWidget)) {
+ return view->GetPresShell();
+ }
+ return nullptr;
+}
+
+nsIDocument*
+ChromeProcessController::GetRootDocument() const
+{
+ if (nsIPresShell* presShell = GetPresShell()) {
+ return presShell->GetDocument();
+ }
+ return nullptr;
+}
+
+nsIDocument*
+ChromeProcessController::GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const
+{
+ nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId);
+ if (!content) {
+ return nullptr;
+ }
+ nsIPresShell* presShell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content);
+ if (presShell) {
+ return presShell->GetDocument();
+ }
+ return nullptr;
+}
+
+void
+ChromeProcessController::HandleDoubleTap(const mozilla::CSSPoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid)
+{
+ MOZ_ASSERT(MessageLoop::current() == mUILoop);
+
+ nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
+ if (!document.get()) {
+ return;
+ }
+
+ // CalculateRectToZoomTo performs a hit test on the frame associated with the
+ // Root Content Document. Unfortunately that frame does not know about the
+ // resolution of the document and so we must remove it before calculating
+ // the zoomToRect.
+ nsIPresShell* presShell = document->GetShell();
+ const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
+ CSSPoint point(aPoint.x / resolution, aPoint.y / resolution);
+ CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
+
+ uint32_t presShellId;
+ FrameMetrics::ViewID viewId;
+ if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+ document->GetDocumentElement(), &presShellId, &viewId)) {
+ mAPZCTreeManager->ZoomToRect(
+ ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
+ }
+}
+
+void
+ChromeProcessController::HandleTap(TapType aType,
+ const mozilla::LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t>(this,
+ &ChromeProcessController::HandleTap,
+ aType, aPoint, aModifiers, aGuid, aInputBlockId));
+ return;
+ }
+
+ if (!mAPZEventState) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ if (!presShell) {
+ return;
+ }
+ if (!presShell->GetPresContext()) {
+ return;
+ }
+ CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
+ CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
+
+ switch (aType) {
+ case TapType::eSingleTap:
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
+ break;
+ case TapType::eDoubleTap:
+ HandleDoubleTap(point, aModifiers, aGuid);
+ break;
+ case TapType::eSecondTap:
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
+ break;
+ case TapType::eLongTap:
+ mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
+ aInputBlockId);
+ break;
+ case TapType::eLongTapUp:
+ mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+ break;
+ case TapType::eSentinel:
+ // Should never happen, but we need to handle this case branch for the
+ // compiler to be happy.
+ MOZ_ASSERT(false);
+ break;
+ }
+}
+
+void
+ChromeProcessController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <PinchGestureInput::PinchGestureType,
+ ScrollableLayerGuid,
+ LayoutDeviceCoord,
+ Modifiers>(this,
+ &ChromeProcessController::NotifyPinchGesture,
+ aType, aGuid, aSpanChange, aModifiers));
+ return;
+ }
+
+ if (mWidget) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mWidget.get());
+ }
+}
+
+void
+ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <ScrollableLayerGuid,
+ APZStateChange,
+ int>(this, &ChromeProcessController::NotifyAPZStateChange,
+ aGuid, aChange, aArg));
+ return;
+ }
+
+ if (!mAPZEventState) {
+ return;
+ }
+
+ mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg);
+}
+
+void
+ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <FrameMetrics::ViewID,
+ nsString>(this, &ChromeProcessController::NotifyMozMouseScrollEvent,
+ aScrollId, aEvent));
+ return;
+ }
+
+ APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
+}
+
+void
+ChromeProcessController::NotifyFlushComplete()
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
+}
diff --git a/system/graphics/layers/apz/util/ChromeProcessController.h b/system/graphics/layers/apz/util/ChromeProcessController.h
new file mode 100644
index 000000000..9a43297d4
--- /dev/null
+++ b/system/graphics/layers/apz/util/ChromeProcessController.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ChromeProcessController_h
+#define mozilla_layers_ChromeProcessController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class nsIDOMWindowUtils;
+class nsIDocument;
+class nsIPresShell;
+class nsIWidget;
+
+class MessageLoop;
+
+namespace mozilla {
+
+namespace layers {
+
+class IAPZCTreeManager;
+class APZEventState;
+
+/**
+ * ChromeProcessController is a GeckoContentController attached to the root of
+ * a compositor's layer tree. It's used directly by APZ by default, and remoted
+ * using PAPZ if there is a gpu process.
+ *
+ * If ChromeProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
+class ChromeProcessController : public mozilla::layers::GeckoContentController
+{
+protected:
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
+
+public:
+ explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState, IAPZCTreeManager* aAPZCTreeManager);
+ ~ChromeProcessController();
+ virtual void Destroy() override;
+
+ // GeckoContentController interface
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
+ virtual bool IsRepaintThread() override;
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+ virtual void HandleTap(TapType aType,
+ const mozilla::LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+ virtual void NotifyFlushComplete() override;
+private:
+ nsCOMPtr<nsIWidget> mWidget;
+ RefPtr<APZEventState> mAPZEventState;
+ RefPtr<IAPZCTreeManager> mAPZCTreeManager;
+ MessageLoop* mUILoop;
+
+ void InitializeRoot();
+ nsIPresShell* GetPresShell() const;
+ nsIDocument* GetRootDocument() const;
+ nsIDocument* GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const;
+ void HandleDoubleTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ChromeProcessController_h */
diff --git a/system/graphics/layers/apz/util/ContentProcessController.cpp b/system/graphics/layers/apz/util/ContentProcessController.cpp
new file mode 100644
index 000000000..118a55723
--- /dev/null
+++ b/system/graphics/layers/apz/util/ContentProcessController.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContentProcessController.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZChild.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * There are cases where we try to create the APZChild before the corresponding
+ * TabChild has been created, we use an observer for the "tab-child-created"
+ * topic to set the TabChild in the APZChild when it has been created.
+ */
+class TabChildCreatedObserver : public nsIObserver
+{
+public:
+ TabChildCreatedObserver(ContentProcessController* aController, const dom::TabId& aTabId)
+ : mController(aController),
+ mTabId(aTabId)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ virtual ~TabChildCreatedObserver()
+ {}
+
+ // TabChildCreatedObserver is owned by mController, and mController outlives its
+ // TabChildCreatedObserver, so the raw pointer is fine.
+ ContentProcessController* mController;
+ dom::TabId mTabId;
+};
+
+NS_IMPL_ISUPPORTS(TabChildCreatedObserver, nsIObserver)
+
+NS_IMETHODIMP
+TabChildCreatedObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(strcmp(aTopic, "tab-child-created") == 0);
+
+ nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(aSubject));
+ NS_ENSURE_TRUE(tabChild, NS_ERROR_FAILURE);
+
+ dom::TabChild* browser = static_cast<dom::TabChild*>(tabChild.get());
+
+ if (browser->GetTabId() == mTabId) {
+ mController->SetBrowser(browser);
+ }
+ return NS_OK;
+}
+
+APZChild*
+ContentProcessController::Create(const dom::TabId& aTabId)
+{
+ RefPtr<dom::TabChild> browser = dom::TabChild::FindTabChild(aTabId);
+
+ ContentProcessController* controller = new ContentProcessController();
+
+ nsAutoPtr<APZChild> apz(new APZChild(controller));
+
+ if (browser) {
+
+ controller->SetBrowser(browser);
+
+ } else {
+
+ RefPtr<TabChildCreatedObserver> observer =
+ new TabChildCreatedObserver(controller, aTabId);
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os ||
+ NS_FAILED(os->AddObserver(observer, "tab-child-created", false))) {
+ return nullptr;
+ }
+ controller->SetObserver(observer);
+
+ }
+
+ return apz.forget();
+}
+
+ContentProcessController::ContentProcessController()
+ : mBrowser(nullptr)
+{
+}
+ContentProcessController::~ContentProcessController()
+{
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->RemoveObserver(mObserver, "tab-child-created");
+ }
+}
+
+void
+ContentProcessController::SetObserver(nsIObserver* aObserver)
+{
+ MOZ_ASSERT(!mBrowser);
+ mObserver = aObserver;
+}
+
+void
+ContentProcessController::SetBrowser(dom::TabChild* aBrowser)
+{
+ MOZ_ASSERT(!mBrowser);
+ mBrowser = aBrowser;
+
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->RemoveObserver(mObserver, "tab-child-created");
+ mObserver = nullptr;
+ }
+}
+void
+ContentProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ if (mBrowser) {
+ mBrowser->UpdateFrame(aFrameMetrics);
+ }
+}
+
+void
+ContentProcessController::HandleTap(
+ TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // This should never get called
+ MOZ_ASSERT(false);
+}
+
+void
+ContentProcessController::NotifyPinchGesture(
+ PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ // This should never get called
+ MOZ_ASSERT_UNREACHABLE("Unexpected message to content process");
+}
+
+void
+ContentProcessController::NotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (mBrowser) {
+ mBrowser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg);
+ }
+}
+
+void
+ContentProcessController::NotifyMozMouseScrollEvent(
+ const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ if (mBrowser) {
+ APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void
+ContentProcessController::NotifyFlushComplete()
+{
+ if (mBrowser) {
+ nsCOMPtr<nsIPresShell> shell;
+ if (nsCOMPtr<nsIDocument> doc = mBrowser->GetDocument()) {
+ shell = doc->GetShell();
+ }
+ APZCCallbackHelper::NotifyFlushComplete(shell.get());
+ }
+}
+
+void
+ContentProcessController::PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs)
+{
+ MOZ_ASSERT_UNREACHABLE("ContentProcessController should only be used remotely.");
+}
+
+bool
+ContentProcessController::IsRepaintThread()
+{
+ return NS_IsMainThread();
+}
+
+void
+ContentProcessController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ NS_DispatchToMainThread(Move(aTask));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/ContentProcessController.h b/system/graphics/layers/apz/util/ContentProcessController.h
new file mode 100644
index 000000000..916f962a8
--- /dev/null
+++ b/system/graphics/layers/apz/util/ContentProcessController.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ContentProcessController_h
+#define mozilla_layers_ContentProcessController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/dom/ipc/IdType.h"
+
+class nsIObserver;
+
+namespace mozilla {
+
+namespace dom {
+class TabChild;
+} // namespace dom
+
+namespace layers {
+
+class APZChild;
+
+/**
+ * ContentProcessController is a GeckoContentController for a TabChild, and is always
+ * remoted using PAPZ/APZChild.
+ *
+ * ContentProcessController is created in ContentChild when a layer tree id has
+ * been allocated for a PBrowser that lives in that content process, and is destroyed
+ * when the Destroy message is received, or when the tab dies.
+ *
+ * If ContentProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
+class ContentProcessController final
+ : public GeckoContentController
+{
+public:
+ ~ContentProcessController();
+
+ static APZChild* Create(const dom::TabId& aTabId);
+
+ // ContentProcessController
+
+ void SetBrowser(dom::TabChild* aBrowser);
+
+ // GeckoContentController
+
+ void RequestContentRepaint(const FrameMetrics& frame) override;
+
+ void HandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+
+ void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+
+ void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ void NotifyFlushComplete() override;
+
+ void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
+
+ bool IsRepaintThread() override;
+
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+private:
+ ContentProcessController();
+
+ void SetObserver(nsIObserver* aObserver);
+
+ RefPtr<dom::TabChild> mBrowser;
+ RefPtr<nsIObserver> mObserver;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_ContentProcessController_h
diff --git a/system/graphics/layers/apz/util/DoubleTapToZoom.cpp b/system/graphics/layers/apz/util/DoubleTapToZoom.cpp
new file mode 100644
index 000000000..62dd5feaa
--- /dev/null
+++ b/system/graphics/layers/apz/util/DoubleTapToZoom.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DoubleTapToZoom.h"
+
+#include <algorithm> // for std::min, std::max
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/dom/Element.h"
+#include "nsCOMPtr.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMHTMLLIElement.h"
+#include "nsIDOMHTMLQuoteElement.h"
+#include "nsIDOMWindow.h"
+#include "nsIFrame.h"
+#include "nsIFrameInlines.h"
+#include "nsIPresShell.h"
+#include "nsLayoutUtils.h"
+#include "nsStyleConsts.h"
+
+namespace mozilla {
+namespace layers {
+
+// Returns the DOM element found at |aPoint|, interpreted as being relative to
+// the root frame of |aShell|. If the point is inside a subdocument, returns
+// an element inside the subdocument, rather than the subdocument element
+// (and does so recursively).
+// The implementation was adapted from nsDocument::ElementFromPoint(), with
+// the notable exception that we don't pass nsLayoutUtils::IGNORE_CROSS_DOC
+// to GetFrameForPoint(), so as to get the behaviour described above in the
+// presence of subdocuments.
+static already_AddRefed<dom::Element>
+ElementFromPoint(const nsCOMPtr<nsIPresShell>& aShell,
+ const CSSPoint& aPoint)
+{
+ if (nsIFrame* rootFrame = aShell->GetRootFrame()) {
+ if (nsIFrame* frame = nsLayoutUtils::GetFrameForPoint(rootFrame,
+ CSSPoint::ToAppUnits(aPoint),
+ nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+ nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME)) {
+ while (frame && (!frame->GetContent() || frame->GetContent()->IsInAnonymousSubtree())) {
+ frame = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
+ }
+ nsIContent* content = frame->GetContent();
+ if (content && !content->IsElement()) {
+ content = content->GetParent();
+ }
+ if (content) {
+ nsCOMPtr<dom::Element> result = content->AsElement();
+ return result.forget();
+ }
+ }
+ }
+ return nullptr;
+}
+
+static bool
+ShouldZoomToElement(const nsCOMPtr<dom::Element>& aElement) {
+ if (nsIFrame* frame = aElement->GetPrimaryFrame()) {
+ if (frame->GetDisplay() == StyleDisplay::Inline) {
+ return false;
+ }
+ }
+ if (aElement->IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::q)) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+IsRectZoomedIn(const CSSRect& aRect, const CSSRect& aCompositedArea)
+{
+ // This functions checks to see if the area of the rect visible in the
+ // composition bounds (i.e. the overlapArea variable below) is approximately
+ // the max area of the rect we can show.
+ CSSRect overlap = aCompositedArea.Intersect(aRect);
+ float overlapArea = overlap.width * overlap.height;
+ float availHeight = std::min(aRect.width * aCompositedArea.height / aCompositedArea.width,
+ aRect.height);
+ float showing = overlapArea / (aRect.width * availHeight);
+ float ratioW = aRect.width / aCompositedArea.width;
+ float ratioH = aRect.height / aCompositedArea.height;
+
+ return showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9);
+}
+
+CSSRect
+CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument,
+ const CSSPoint& aPoint)
+{
+ // Ensure the layout information we get is up-to-date.
+ aRootContentDocument->FlushPendingNotifications(Flush_Layout);
+
+ // An empty rect as return value is interpreted as "zoom out".
+ const CSSRect zoomOut;
+
+ nsCOMPtr<nsIPresShell> shell = aRootContentDocument->GetShell();
+ if (!shell) {
+ return zoomOut;
+ }
+
+ nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable();
+ if (!rootScrollFrame) {
+ return zoomOut;
+ }
+
+ nsCOMPtr<dom::Element> element = ElementFromPoint(shell, aPoint);
+ if (!element) {
+ return zoomOut;
+ }
+
+ while (element && !ShouldZoomToElement(element)) {
+ element = element->GetParentElement();
+ }
+
+ if (!element) {
+ return zoomOut;
+ }
+
+ FrameMetrics metrics = nsLayoutUtils::CalculateBasicFrameMetrics(rootScrollFrame);
+ CSSRect compositedArea(metrics.GetScrollOffset(), metrics.CalculateCompositedSizeInCssPixels());
+ const CSSCoord margin = 15;
+ CSSRect rect = nsLayoutUtils::GetBoundingContentRect(element, rootScrollFrame);
+
+ // If the element is taller than the visible area of the page scale
+ // the height of the |rect| so that it has the same aspect ratio as
+ // the root frame. The clipped |rect| is centered on the y value of
+ // the touch point. This allows tall narrow elements to be zoomed.
+ if (!rect.IsEmpty() && compositedArea.width > 0.0f) {
+ const float widthRatio = rect.width / compositedArea.width;
+ float targetHeight = compositedArea.height * widthRatio;
+ if (widthRatio < 0.9 && targetHeight < rect.height) {
+ const CSSPoint scrollPoint = CSSPoint::FromAppUnits(rootScrollFrame->GetScrollPosition());
+ float newY = aPoint.y + scrollPoint.y - (targetHeight * 0.5f);
+ if ((newY + targetHeight) > (rect.y + rect.height)) {
+ rect.y += rect.height - targetHeight;
+ } else if (newY > rect.y) {
+ rect.y = newY;
+ }
+ rect.height = targetHeight;
+ }
+ }
+
+ rect = CSSRect(std::max(metrics.GetScrollableRect().x, rect.x - margin),
+ rect.y,
+ rect.width + 2 * margin,
+ rect.height);
+ // Constrict the rect to the screen's right edge
+ rect.width = std::min(rect.width, metrics.GetScrollableRect().XMost() - rect.x);
+
+ // If the rect is already taking up most of the visible area and is
+ // stretching the width of the page, then we want to zoom out instead.
+ if (IsRectZoomedIn(rect, compositedArea)) {
+ return zoomOut;
+ }
+
+ CSSRect rounded(rect);
+ rounded.Round();
+
+ // If the block we're zooming to is really tall, and the user double-tapped
+ // more than a screenful of height from the top of it, then adjust the
+ // y-coordinate so that we center the actual point the user double-tapped
+ // upon. This prevents flying to the top of the page when double-tapping
+ // to zoom in (bug 761721). The 1.2 multiplier is just a little fuzz to
+ // compensate for 'rect' including horizontal margins but not vertical ones.
+ CSSCoord cssTapY = metrics.GetScrollOffset().y + aPoint.y;
+ if ((rect.height > rounded.height) && (cssTapY > rounded.y + (rounded.height * 1.2))) {
+ rounded.y = cssTapY - (rounded.height / 2);
+ }
+
+ return rounded;
+}
+
+}
+}
diff --git a/system/graphics/layers/apz/util/DoubleTapToZoom.h b/system/graphics/layers/apz/util/DoubleTapToZoom.h
new file mode 100644
index 000000000..7b8723865
--- /dev/null
+++ b/system/graphics/layers/apz/util/DoubleTapToZoom.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DoubleTapToZoom_h
+#define mozilla_layers_DoubleTapToZoom_h
+
+#include "Units.h"
+
+class nsIDocument;
+template<class T> class nsCOMPtr;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * For a double tap at |aPoint|, return the rect to which the browser
+ * should zoom in response, or an empty rect if the browser should zoom out.
+ * |aDocument| should be the root content document for the content that was
+ * tapped.
+ */
+CSSRect CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument,
+ const CSSPoint& aPoint);
+
+}
+}
+
+#endif /* mozilla_layers_DoubleTapToZoom_h */
diff --git a/system/graphics/layers/apz/util/InputAPZContext.cpp b/system/graphics/layers/apz/util/InputAPZContext.cpp
new file mode 100644
index 000000000..af5bd9f0f
--- /dev/null
+++ b/system/graphics/layers/apz/util/InputAPZContext.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputAPZContext.h"
+
+namespace mozilla {
+namespace layers {
+
+ScrollableLayerGuid InputAPZContext::sGuid;
+uint64_t InputAPZContext::sBlockId = 0;
+nsEventStatus InputAPZContext::sApzResponse = nsEventStatus_eIgnore;
+bool InputAPZContext::sRoutedToChildProcess = false;
+
+/*static*/ ScrollableLayerGuid
+InputAPZContext::GetTargetLayerGuid()
+{
+ return sGuid;
+}
+
+/*static*/ uint64_t
+InputAPZContext::GetInputBlockId()
+{
+ return sBlockId;
+}
+
+/*static*/ nsEventStatus
+InputAPZContext::GetApzResponse()
+{
+ return sApzResponse;
+}
+
+/*static*/ void
+InputAPZContext::SetRoutedToChildProcess()
+{
+ sRoutedToChildProcess = true;
+}
+
+InputAPZContext::InputAPZContext(const ScrollableLayerGuid& aGuid,
+ const uint64_t& aBlockId,
+ const nsEventStatus& aApzResponse)
+ : mOldGuid(sGuid)
+ , mOldBlockId(sBlockId)
+ , mOldApzResponse(sApzResponse)
+ , mOldRoutedToChildProcess(sRoutedToChildProcess)
+{
+ sGuid = aGuid;
+ sBlockId = aBlockId;
+ sApzResponse = aApzResponse;
+ sRoutedToChildProcess = false;
+}
+
+InputAPZContext::~InputAPZContext()
+{
+ sGuid = mOldGuid;
+ sBlockId = mOldBlockId;
+ sApzResponse = mOldApzResponse;
+ sRoutedToChildProcess = mOldRoutedToChildProcess;
+}
+
+bool
+InputAPZContext::WasRoutedToChildProcess()
+{
+ return sRoutedToChildProcess;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/InputAPZContext.h b/system/graphics/layers/apz/util/InputAPZContext.h
new file mode 100644
index 000000000..0f232e1cb
--- /dev/null
+++ b/system/graphics/layers/apz/util/InputAPZContext.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputAPZContext_h
+#define mozilla_layers_InputAPZContext_h
+
+#include "FrameMetrics.h"
+#include "mozilla/EventForwards.h"
+
+namespace mozilla {
+namespace layers {
+
+// InputAPZContext is used to communicate the ScrollableLayerGuid,
+// input block ID, APZ response from nsIWidget to RenderFrameParent.
+// It is conceptually attached to any WidgetInputEvent
+// that has been processed by APZ directly from a widget.
+class MOZ_STACK_CLASS InputAPZContext
+{
+private:
+ static ScrollableLayerGuid sGuid;
+ static uint64_t sBlockId;
+ static nsEventStatus sApzResponse;
+ static bool sRoutedToChildProcess;
+
+public:
+ static ScrollableLayerGuid GetTargetLayerGuid();
+ static uint64_t GetInputBlockId();
+ static nsEventStatus GetApzResponse();
+ static void SetRoutedToChildProcess();
+
+ InputAPZContext(const ScrollableLayerGuid& aGuid,
+ const uint64_t& aBlockId,
+ const nsEventStatus& aApzResponse);
+ ~InputAPZContext();
+
+ bool WasRoutedToChildProcess();
+
+private:
+ ScrollableLayerGuid mOldGuid;
+ uint64_t mOldBlockId;
+ nsEventStatus mOldApzResponse;
+ bool mOldRoutedToChildProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_InputAPZContext_h */
diff --git a/system/graphics/layers/apz/util/ScrollInputMethods.h b/system/graphics/layers/apz/util/ScrollInputMethods.h
new file mode 100644
index 000000000..61a5865c4
--- /dev/null
+++ b/system/graphics/layers/apz/util/ScrollInputMethods.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ScrollInputMethods_h
+#define mozilla_layers_ScrollInputMethods_h
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * An enumeration that lists various input methods used to trigger scrolling.
+ */
+enum class ScrollInputMethod {
+
+ // === Driven by APZ ===
+
+ ApzTouch, // touch events
+ ApzWheelPixel, // wheel events, pixel scrolling mode
+ ApzWheelLine, // wheel events, line scrolling mode
+ ApzWheelPage, // wheel events, page scrolling mode
+ ApzPanGesture, // pan gesture events (generally triggered by trackpad)
+ ApzScrollbarDrag, // dragging the scrollbar
+
+ // === Driven by the main thread ===
+
+ // Keyboard
+ MainThreadScrollLine, // line scrolling
+ // (generally triggered by up/down arrow keys)
+ MainThreadScrollCharacter, // character scrolling
+ // (generally triggered by left/right arrow keys)
+ MainThreadScrollPage, // page scrolling
+ // (generally triggered by PageUp/PageDown keys)
+ MainThreadCompleteScroll, // scrolling to the end of the scroll range
+ // (generally triggered by Home/End keys)
+ MainThreadScrollCaretIntoView, // scrolling to bring the caret into view
+ // after moving the caret via the keyboard
+
+ // Touch
+ MainThreadTouch, // touch events
+
+ // Scrollbar
+ MainThreadScrollbarDrag, // dragging the scrollbar
+ MainThreadScrollbarButtonClick, // clicking the buttons at the ends of the
+ // scrollback track
+ MainThreadScrollbarTrackClick, // clicking the scrollbar track above or
+ // below the thumb
+
+ // Autoscrolling
+ MainThreadAutoscrolling, // autoscrolling
+
+ // New input methods can be added at the end, up to a maximum of 64.
+ // They should only be added at the end, to preserve the numerical values
+ // of the existing enumerators.
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ScrollInputMethods_h */
diff --git a/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.cpp b/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.cpp
new file mode 100644
index 000000000..758b705a3
--- /dev/null
+++ b/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScrollLinkedEffectDetector.h"
+
+#include "nsIDocument.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+uint32_t ScrollLinkedEffectDetector::sDepth = 0;
+bool ScrollLinkedEffectDetector::sFoundScrollLinkedEffect = false;
+
+/* static */ void
+ScrollLinkedEffectDetector::PositioningPropertyMutated()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sDepth > 0) {
+ // We are inside a scroll event dispatch
+ sFoundScrollLinkedEffect = true;
+ }
+}
+
+ScrollLinkedEffectDetector::ScrollLinkedEffectDetector(nsIDocument* aDoc)
+ : mDocument(aDoc)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ sDepth++;
+}
+
+ScrollLinkedEffectDetector::~ScrollLinkedEffectDetector()
+{
+ sDepth--;
+ if (sDepth == 0) {
+ // We have exited all (possibly-nested) scroll event dispatches,
+ // record whether or not we found an effect, and reset state
+ if (sFoundScrollLinkedEffect) {
+ mDocument->ReportHasScrollLinkedEffect();
+ sFoundScrollLinkedEffect = false;
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.h b/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.h
new file mode 100644
index 000000000..f792586cf
--- /dev/null
+++ b/system/graphics/layers/apz/util/ScrollLinkedEffectDetector.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ScrollLinkedEffectDetector_h
+#define mozilla_layers_ScrollLinkedEffectDetector_h
+
+#include "mozilla/RefPtr.h"
+
+class nsIDocument;
+
+namespace mozilla {
+namespace layers {
+
+// ScrollLinkedEffectDetector is used to detect the existence of a scroll-linked
+// effect on a webpage. Generally speaking, a scroll-linked effect is something
+// on the page that animates or changes with respect to the scroll position.
+// Content authors usually rely on running some JS in response to the scroll
+// event in order to implement such effects, and therefore it tends to be laggy
+// or work improperly with APZ enabled. This class helps us detect such an
+// effect so that we can warn the author and/or take other preventative
+// measures.
+class MOZ_STACK_CLASS ScrollLinkedEffectDetector
+{
+private:
+ static uint32_t sDepth;
+ static bool sFoundScrollLinkedEffect;
+
+public:
+ static void PositioningPropertyMutated();
+
+ explicit ScrollLinkedEffectDetector(nsIDocument* aDoc);
+ ~ScrollLinkedEffectDetector();
+
+private:
+ RefPtr<nsIDocument> mDocument;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ScrollLinkedEffectDetector_h */
diff --git a/system/graphics/layers/apz/util/TouchActionHelper.cpp b/system/graphics/layers/apz/util/TouchActionHelper.cpp
new file mode 100644
index 000000000..b35fd2ec7
--- /dev/null
+++ b/system/graphics/layers/apz/util/TouchActionHelper.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TouchActionHelper.h"
+
+#include "mozilla/layers/APZCTreeManager.h"
+#include "nsContainerFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+TouchActionHelper::UpdateAllowedBehavior(uint32_t aTouchActionValue,
+ bool aConsiderPanning,
+ TouchBehaviorFlags& aOutBehavior)
+{
+ if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_AUTO) {
+ // Double-tap-zooming need property value AUTO
+ aOutBehavior &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+ if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_MANIPULATION) {
+ // Pinch-zooming need value AUTO or MANIPULATION
+ aOutBehavior &= ~AllowedTouchBehavior::PINCH_ZOOM;
+ }
+ }
+
+ if (aConsiderPanning) {
+ if (aTouchActionValue == NS_STYLE_TOUCH_ACTION_NONE) {
+ aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN;
+ aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
+ }
+
+ // Values pan-x and pan-y set at the same time to the same element do not affect panning constraints.
+ // Therefore we need to check whether pan-x is set without pan-y and the same for pan-y.
+ if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y)) {
+ aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN;
+ } else if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X)) {
+ aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
+ }
+ }
+}
+
+TouchBehaviorFlags
+TouchActionHelper::GetAllowedTouchBehavior(nsIWidget* aWidget,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aPoint)
+{
+ TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN |
+ AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+
+ nsPoint relativePoint =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, aRootFrame);
+
+ nsIFrame *target = nsLayoutUtils::GetFrameForPoint(aRootFrame, relativePoint, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+ if (!target) {
+ return behavior;
+ }
+ nsIScrollableFrame *nearestScrollableParent = nsLayoutUtils::GetNearestScrollableFrame(target, 0);
+ nsIFrame* nearestScrollableFrame = do_QueryFrame(nearestScrollableParent);
+
+ // We're walking up the DOM tree until we meet the element with touch behavior and accumulating
+ // touch-action restrictions of all elements in this chain.
+ // The exact quote from the spec, that clarifies more:
+ // To determine the effect of a touch, find the nearest ancestor (starting from the element itself)
+ // that has a default touch behavior. Then examine the touch-action property of each element between
+ // the hit tested element and the element with the default touch behavior (including both the hit
+ // tested element and the element with the default touch behavior). If the touch-action property of
+ // any of those elements disallows the default touch behavior, do nothing. Otherwise allow the element
+ // to start considering the touch for the purposes of executing a default touch behavior.
+
+ // Currently we support only two touch behaviors: panning and zooming.
+ // For panning we walk up until we meet the first scrollable element (the element that supports panning)
+ // or root element.
+ // For zooming we walk up until the root element since Firefox currently supports only zooming of the
+ // root frame but not the subframes.
+
+ bool considerPanning = true;
+
+ for (nsIFrame *frame = target; frame && frame->GetContent() && behavior; frame = frame->GetParent()) {
+ UpdateAllowedBehavior(nsLayoutUtils::GetTouchActionFromFrame(frame), considerPanning, behavior);
+
+ if (frame == nearestScrollableFrame) {
+ // We met the scrollable element, after it we shouldn't consider touch-action
+ // values for the purpose of panning but only for zooming.
+ considerPanning = false;
+ }
+ }
+
+ return behavior;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/apz/util/TouchActionHelper.h b/system/graphics/layers/apz/util/TouchActionHelper.h
new file mode 100644
index 000000000..1dacfd4c0
--- /dev/null
+++ b/system/graphics/layers/apz/util/TouchActionHelper.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __mozilla_layers_TouchActionHelper_h__
+#define __mozilla_layers_TouchActionHelper_h__
+
+#include "mozilla/layers/APZUtils.h" // for TouchBehaviorFlags
+
+class nsIFrame;
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+/*
+ * Helper class to figure out the allowed touch behavior for frames, as per
+ * the touch-action spec.
+ */
+class TouchActionHelper
+{
+private:
+ static void UpdateAllowedBehavior(uint32_t aTouchActionValue,
+ bool aConsiderPanning,
+ TouchBehaviorFlags& aOutBehavior);
+
+public:
+ /*
+ * Performs hit testing on content, finds frame that corresponds to the aPoint and retrieves
+ * touch-action css property value from it according the rules specified in the spec:
+ * http://www.w3.org/TR/pointerevents/#the-touch-action-css-property.
+ */
+ static TouchBehaviorFlags GetAllowedTouchBehavior(nsIWidget* aWidget,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aPoint);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /*__mozilla_layers_TouchActionHelper_h__ */
diff --git a/system/graphics/layers/basic/AutoMaskData.h b/system/graphics/layers/basic/AutoMaskData.h
new file mode 100644
index 000000000..7bf7f9b3c
--- /dev/null
+++ b/system/graphics/layers/basic/AutoMaskData.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_AUTOMASKDATA_H_
+#define GFX_AUTOMASKDATA_H_
+
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Drawing with a mask requires a mask surface and a transform.
+ *
+ * This helper class manages the SourceSurface logic.
+ */
+class MOZ_STACK_CLASS AutoMoz2DMaskData {
+public:
+ AutoMoz2DMaskData() { }
+ ~AutoMoz2DMaskData() { }
+
+ void Construct(const gfx::Matrix& aTransform,
+ gfx::SourceSurface* aSurface)
+ {
+ MOZ_ASSERT(!IsConstructed());
+ mTransform = aTransform;
+ mSurface = aSurface;
+ }
+
+ gfx::SourceSurface* GetSurface()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mSurface.get();
+ }
+
+ const gfx::Matrix& GetTransform()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mTransform;
+ }
+
+private:
+ bool IsConstructed()
+ {
+ return !!mSurface;
+ }
+
+ gfx::Matrix mTransform;
+ RefPtr<gfx::SourceSurface> mSurface;
+
+ AutoMoz2DMaskData(const AutoMoz2DMaskData&) = delete;
+ AutoMoz2DMaskData& operator=(const AutoMoz2DMaskData&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_AUTOMASKDATA_H_
diff --git a/system/graphics/layers/basic/BasicCanvasLayer.cpp b/system/graphics/layers/basic/BasicCanvasLayer.cpp
new file mode 100644
index 000000000..83c5c272e
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCanvasLayer.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicCanvasLayer.h"
+#include "AsyncCanvasRenderer.h"
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "GLContext.h"
+#include "gfxUtils.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "client/TextureClientSharedSurface.h"
+
+class gfxContext;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<SourceSurface>
+BasicCanvasLayer::UpdateSurface()
+{
+ if (mAsyncRenderer) {
+ MOZ_ASSERT(!mBufferProvider);
+ MOZ_ASSERT(!mGLContext);
+ return mAsyncRenderer->GetSurface();
+ }
+
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return nullptr;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return nullptr;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+ MOZ_ASSERT(resultSurf);
+
+ return resultSurf.forget();
+}
+
+void
+BasicCanvasLayer::Paint(DrawTarget* aDT,
+ const Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden())
+ return;
+
+ RefPtr<SourceSurface> surface;
+ if (IsDirty()) {
+ Painted();
+
+ FirePreTransactionCallback();
+ surface = UpdateSurface();
+ FireDidTransactionCallback();
+ }
+
+ bool bufferPoviderSnapshot = false;
+ if (!surface && mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ bufferPoviderSnapshot = !!surface;
+ }
+
+ if (!surface) {
+ return;
+ }
+
+ const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
+
+ Matrix oldTM;
+ if (needsYFlip) {
+ oldTM = aDT->GetTransform();
+ aDT->SetTransform(Matrix(oldTM).
+ PreTranslate(0.0f, mBounds.height).
+ PreScale(1.0f, -1.0f));
+ }
+
+ FillRectWithMask(aDT, aDeviceOffset,
+ Rect(0, 0, mBounds.width, mBounds.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ if (needsYFlip) {
+ aDT->SetTransform(oldTM);
+ }
+
+ if (bufferPoviderSnapshot) {
+ mBufferProvider->ReturnSnapshot(surface.forget());
+ }
+}
+
+already_AddRefed<CanvasLayer>
+BasicLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCanvasLayer.h b/system/graphics/layers/basic/BasicCanvasLayer.h
new file mode 100644
index 000000000..a63d2b8c0
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCanvasLayer.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICCANVASLAYER_H
+#define GFX_BASICCANVASLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class BasicCanvasLayer : public CopyableCanvasLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicCanvasLayer(BasicLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ { }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+protected:
+
+ already_AddRefed<gfx::SourceSurface> UpdateSurface();
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicColorLayer.cpp b/system/graphics/layers/basic/BasicColorLayer.cpp
new file mode 100644
index 000000000..182bc785b
--- /dev/null
+++ b/system/graphics/layers/basic/BasicColorLayer.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicColorLayer : public ColorLayer, public BasicImplData {
+public:
+ explicit BasicColorLayer(BasicLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicColorLayer);
+ }
+
+protected:
+ virtual ~BasicColorLayer()
+ {
+ MOZ_COUNT_DTOR(BasicColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override
+ {
+ if (IsHidden()) {
+ return;
+ }
+
+ Rect snapped(mBounds.x, mBounds.y, mBounds.width, mBounds.height);
+ MaybeSnapToDevicePixels(snapped, *aDT, true);
+
+ // Clip drawing in case we're using (unbounded) operator source.
+ aDT->PushClipRect(snapped);
+ FillRectWithMask(aDT, aDeviceOffset, snapped, mColor,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+ aDT->PopClip();
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+BasicLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ColorLayer> layer = new BasicColorLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCompositor.cpp b/system/graphics/layers/basic/BasicCompositor.cpp
new file mode 100644
index 000000000..634d9e340
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCompositor.cpp
@@ -0,0 +1,996 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicCompositor.h"
+#include "BasicLayersImpl.h" // for FillRectWithMask
+#include "TextureHostBasic.h"
+#include "mozilla/layers/Effects.h"
+#include "nsIWidget.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/ssse3-scaler.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/SSE.h"
+#include "gfxUtils.h"
+#include "YCbCrUtils.h"
+#include <algorithm>
+#include "ImageContainer.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+class DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "DataTextureSourceBasic"; }
+
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface)
+ , mWrappingExistingData(!!aSurface)
+ {}
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
+ mSurface = aSurface;
+ return true;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+public:
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
+};
+
+/**
+ * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer
+ * yuv->rgb conversion. The conversion happens when GetSurface is called.
+ */
+class WrappingTextureSourceYCbCrBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "WrappingTextureSourceYCbCrBasic"; }
+
+ explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture)
+ : mTexture(aTexture)
+ , mSize(aTexture->GetSize())
+ , mNeedsUpdate(true)
+ {
+ mFromYCBCR = true;
+ }
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ return this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override
+ {
+ if (mSurface && !mNeedsUpdate) {
+ return mSurface;
+ }
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ if (!mSurface) {
+ mSurface = Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (!mSurface) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+
+ mSurface =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ mSurface);
+ mNeedsUpdate = false;
+ return mSurface;
+ }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSize;
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ return false;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mTexture = nullptr;
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ virtual void Unbind() override
+ {
+ mNeedsUpdate = true;
+ }
+
+ void SetBufferTextureHost(BufferTextureHost* aTexture) override
+ {
+ mTexture = aTexture;
+ mNeedsUpdate = true;
+ }
+
+ void ConvertAndScale(const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride)
+ {
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+ ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ aDestFormat,
+ aDestSize,
+ aDestBuffer,
+ aStride);
+ }
+public:
+ BufferTextureHost* mTexture;
+ const gfx::IntSize mSize;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mNeedsUpdate;
+};
+
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mDidExternalComposition(false)
+ , mIsPendingEndRemoteDrawing(false)
+{
+ MOZ_COUNT_CTOR(BasicCompositor);
+
+ mMaxTextureSize = Factory::GetMaxSurfaceSize(gfxVars::ContentBackend());
+}
+
+BasicCompositor::~BasicCompositor()
+{
+ MOZ_COUNT_DTOR(BasicCompositor);
+}
+
+bool
+BasicCompositor::Initialize(nsCString* const out_failureReason)
+{
+ return mWidget ? mWidget->InitCompositor(this) : false;
+};
+
+int32_t
+BasicCompositor::GetMaxTextureSize() const
+{
+ return mMaxTextureSize;
+}
+
+void
+BasicCompositingRenderTarget::BindRenderTarget()
+{
+ if (mClearOnBind) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mClearOnBind = false;
+ }
+}
+
+void BasicCompositor::DetachWidget()
+{
+ if (mWidget) {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+ mWidget->CleanupRemoteDrawing();
+ }
+ Compositor::DetachWidget();
+}
+
+TextureFactoryIdentifier
+BasicCompositor::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident(LayersBackend::LAYERS_BASIC,
+ XRE_GetProcessType(),
+ GetMaxTextureSize());
+ return ident;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ if (!target) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
+
+ return rt.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const IntPoint &aSourcePoint)
+{
+ MOZ_CRASH("GFX: Shouldn't be called!");
+ return nullptr;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect, BufferMode aBufferMode)
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt;
+ IntRect rect = aRect.ToUnknownRect();
+
+ if (aBufferMode != BufferMode::BUFFER_NONE) {
+ RefPtr<DrawTarget> target = mWidget->GetBackBufferDrawTarget(mDrawTarget, aRect, aClearRect);
+ if (!target) {
+ return nullptr;
+ }
+ MOZ_ASSERT(target != mDrawTarget);
+ rt = new BasicCompositingRenderTarget(target, rect);
+ } else {
+ IntRect windowRect = rect;
+ // Adjust bounds rect to account for new origin at (0, 0).
+ if (windowRect.Size() != mDrawTarget->GetSize()) {
+ windowRect.ExpandToEnclose(IntPoint(0, 0));
+ }
+ rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect);
+ if (!aClearRect.IsEmpty()) {
+ IntRect clearRect = aRect.ToUnknownRect();
+ mDrawTarget->ClearRect(Rect(clearRect - rt->GetOrigin()));
+ }
+ }
+
+ return rt.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSourceBasic> result = new DataTextureSourceBasic(nullptr);
+ if (aFlags & TextureFlags::RGB_FROM_YCBCR) {
+ result->mFromYCBCR = true;
+ }
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture)
+{
+ BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+ MOZ_ASSERT(bufferTexture);
+
+ if (!bufferTexture) {
+ return nullptr;
+ }
+ RefPtr<DataTextureSource> result = new WrappingTextureSourceYCbCrBasic(bufferTexture);
+ return result.forget();
+}
+
+bool
+BasicCompositor::SupportsEffect(EffectTypes aEffect)
+{
+ return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
+}
+
+static void
+DrawSurfaceWithTextureCoords(DrawTarget *aDest,
+ const gfx::Rect& aDestRect,
+ SourceSurface *aSource,
+ const gfx::Rect& aTextureCoords,
+ gfx::SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ SourceSurface *aMask,
+ const Matrix* aMaskTransform)
+{
+ if (!aSource) {
+ gfxWarning() << "DrawSurfaceWithTextureCoords problem " << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
+ return;
+ }
+
+ // Convert aTextureCoords into aSource's coordinate space
+ gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width,
+ aTextureCoords.y * aSource->GetSize().height,
+ aTextureCoords.width * aSource->GetSize().width,
+ aTextureCoords.height * aSource->GetSize().height);
+
+ // Floating point error can accumulate above and we know our visible region
+ // is integer-aligned, so round it out.
+ sourceRect.Round();
+
+ // Compute a transform that maps sourceRect to aDestRect.
+ Matrix matrix =
+ gfxUtils::TransformRectToRect(sourceRect,
+ gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
+
+ // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
+ gfx::Rect unitRect(0, 0, 1, 1);
+ ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
+
+ FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
+ mode, aMask, aMaskTransform, &matrix);
+}
+
+static void
+SetupMask(const EffectChain& aEffectChain,
+ DrawTarget* aDest,
+ const IntPoint& aOffset,
+ RefPtr<SourceSurface>& aMaskSurface,
+ Matrix& aMaskTransform)
+{
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
+ if (!aMaskSurface) {
+ gfxWarning() << "Invalid sourceMask effect";
+ }
+ MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ aMaskTransform = effectMask->mMaskTransform.As2D();
+ aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
+ }
+}
+
+static bool
+AttemptVideoScale(TextureSourceBasic* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3())
+ return false;
+ if (aNewTransform.IsTranslation()) // unscaled painting should take the regular path
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ // If we're not painting to aBuffer the clip will
+ // be applied later
+ IntRect fillRect = dstRect;
+ if (aDest == aBuffer) {
+ // we need to clip fillRect because LockBits ignores the clip on the aDest
+ fillRect = fillRect.Intersect(clipRect);
+ }
+
+ fillRect = fillRect.Intersect(IntRect(IntPoint(0, 0), aDest->GetSize()));
+ IntPoint offset = fillRect.TopLeft() - dstRect.TopLeft();
+
+ RefPtr<DataSourceSurface> srcSource = aSource->GetSurface(aDest)->GetDataSurface();
+ DataSourceSurface::ScopedMap mapSrc(srcSource, DataSourceSurface::READ);
+
+ bool success = ssse3_scale_data((uint32_t*)mapSrc.GetData(), srcSource->GetSize().width, srcSource->GetSize().height,
+ mapSrc.GetStride()/4,
+ ((uint32_t*)dstData) + fillRect.x + (dstStride / 4) * fillRect.y, dstRect.width, dstRect.height,
+ dstStride / 4,
+ offset.x, offset.y,
+ fillRect.width, fillRect.height);
+
+ aDest->ReleaseBits(dstData);
+ return success;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+static bool
+AttemptVideoConvertAndScale(TextureSource* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#if defined(XP_WIN) && defined(_M_X64)
+ // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+ return false;
+#endif
+
+ WrappingTextureSourceYCbCrBasic* wrappingSource = aSource->AsWrappingTextureSourceYCbCrBasic();
+ if (!wrappingSource)
+ return false;
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV conversion.
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ if (aDest == aBuffer && !clipRect.Contains(dstRect))
+ return false;
+ if (!IntRect(IntPoint(0, 0), aDest->GetSize()).Contains(dstRect))
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ wrappingSource->ConvertAndScale(dstFormat,
+ dstRect.Size(),
+ dstData + ptrdiff_t(dstRect.x) * BytesPerPixel(dstFormat) + ptrdiff_t(dstRect.y) * dstStride,
+ dstStride);
+ aDest->ReleaseBits(dstData);
+ return true;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+void
+BasicCompositor::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
+
+ // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
+ // |dest| is a temporary surface.
+ RefPtr<DrawTarget> dest = buffer;
+
+ AutoRestoreTransform autoRestoreTransform(dest);
+
+ Matrix newTransform;
+ Rect transformBounds;
+ Matrix4x4 new3DTransform;
+ IntPoint offset = mRenderTarget->GetOrigin();
+
+ if (aTransform.Is2D()) {
+ newTransform = aTransform.As2D();
+ } else {
+ // Create a temporary surface for the transform.
+ dest = Factory::CreateDrawTarget(gfxVars::ContentBackend(), RoundedOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
+ if (!dest) {
+ return;
+ }
+
+ dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));
+
+ // Get the bounds post-transform.
+ transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
+ transformBounds.RoundOut();
+
+ if (transformBounds.IsEmpty()) {
+ return;
+ }
+
+ newTransform = Matrix();
+
+ // When we apply the 3D transformation, we do it against a temporary
+ // surface, so undo the coordinate offset.
+ new3DTransform = aTransform;
+ new3DTransform.PreTranslate(aRect.x, aRect.y, 0);
+ }
+
+ // XXX the transform is probably just an integer offset so this whole
+ // business here is a bit silly.
+ Rect transformedClipRect = buffer->GetTransform().TransformBounds(Rect(aClipRect));
+
+ buffer->PushClipRect(Rect(aClipRect));
+
+ newTransform.PostTranslate(-offset.x, -offset.y);
+ buffer->SetTransform(newTransform);
+
+ RefPtr<SourceSurface> sourceMask;
+ Matrix maskTransform;
+ if (aTransform.Is2D()) {
+ SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
+ }
+
+ CompositionOp blendMode = CompositionOp::OP_OVER;
+ if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
+ blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
+ }
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+
+ bool unboundedOp = !IsOperatorBoundByMask(blendMode);
+ if (unboundedOp) {
+ dest->PushClipRect(aRect);
+ }
+
+ FillRectWithMask(dest, aRect, effectSolidColor->mColor,
+ DrawOptions(aOpacity, blendMode), sourceMask, &maskTransform);
+
+ if (unboundedOp) {
+ dest->PopClip();
+ }
+ break;
+ }
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
+
+ if (source && texturedEffect->mPremultiplied) {
+ // we have a fast path for video here
+ if (source->mFromYCBCR &&
+ AttemptVideoConvertAndScale(texturedEffect->mTexture, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in convert and scaling
+ } else if (source->mFromYCBCR &&
+ !source->GetSurface(dest)) {
+ gfxWarning() << "Failed to get YCbCr to rgb surface.";
+ } else if (source->mFromYCBCR &&
+ AttemptVideoScale(source, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in scaling
+ } else {
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ source->GetSurface(dest),
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else if (source) {
+ SourceSurface* srcSurf = source->GetSurface(dest);
+ if (srcSurf) {
+ RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
+
+ // Yes, we re-create the premultiplied data every time.
+ // This might be better with a cache, eventually.
+ RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ premultData,
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else {
+ gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
+ }
+
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<BasicCompositingRenderTarget> surface
+ = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());
+ RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ sourceSurf,
+ effectRenderTarget->mTextureCoords,
+ effectRenderTarget->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ default: {
+ NS_RUNTIMEABORT("Invalid effect type!");
+ break;
+ }
+ }
+
+ if (!aTransform.Is2D()) {
+ dest->Flush();
+
+ RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
+
+ SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
+
+ if (sourceMask) {
+ RefPtr<DrawTarget> transformDT =
+ dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
+ if (transformDT &&
+ transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
+ RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
+
+ // Transform the source by it's normal transform, and then the inverse
+ // of the mask transform so that it's in the mask's untransformed
+ // coordinate space.
+ Matrix sourceTransform = newTransform;
+ sourceTransform.PostTranslate(transformBounds.TopLeft());
+
+ Matrix inverseMask = maskTransform;
+ inverseMask.Invert();
+
+ sourceTransform *= inverseMask;
+
+ SurfacePattern source(transformSnapshot, ExtendMode::CLAMP, sourceTransform);
+
+ buffer->PushClipRect(transformBounds);
+
+ // Mask in the untransformed coordinate space, and then transform
+ // by the mask transform to put the result back into destination
+ // coords.
+ buffer->SetTransform(maskTransform);
+ buffer->MaskSurface(source, sourceMask, Point(0, 0));
+
+ buffer->PopClip();
+ }
+ } else {
+ buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
+ }
+ }
+
+ buffer->PopClip();
+}
+
+void
+BasicCompositor::ClearRect(const gfx::Rect& aRect)
+{
+ mRenderTarget->mDrawTarget->ClearRect(aRect);
+}
+
+void
+BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut /* = nullptr */,
+ gfx::IntRect *aRenderBoundsOut /* = nullptr */)
+{
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ LayoutDeviceIntRect intRect(LayoutDeviceIntPoint(), mWidget->GetClientSize());
+ IntRect rect = IntRect(0, 0, intRect.width, intRect.height);
+
+ LayoutDeviceIntRegion invalidRegionSafe;
+ if (mDidExternalComposition) {
+ // We do not know rendered region during external composition, just redraw
+ // whole widget.
+ invalidRegionSafe = intRect;
+ mDidExternalComposition = false;
+ } else {
+ // Sometimes the invalid region is larger than we want to draw.
+ invalidRegionSafe.And(
+ LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion), intRect);
+ }
+
+ mInvalidRegion = invalidRegionSafe;
+ mInvalidRect = mInvalidRegion.GetBounds();
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect();
+ }
+
+ BufferMode bufferMode = BufferMode::BUFFERED;
+ if (mTarget) {
+ // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy
+ // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered
+ // draw target that we composite into, then copy the results the destination.
+ mDrawTarget = mTarget;
+ bufferMode = BufferMode::BUFFER_NONE;
+ } else {
+ // StartRemoteDrawingInRegion can mutate mInvalidRegion.
+ mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode);
+ if (!mDrawTarget) {
+ return;
+ }
+ mInvalidRect = mInvalidRegion.GetBounds();
+ if (mInvalidRect.IsEmpty()) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ return;
+ }
+ }
+
+ if (!mDrawTarget || mInvalidRect.IsEmpty()) {
+ return;
+ }
+
+ LayoutDeviceIntRect clearRect;
+ if (!aOpaqueRegion.IsEmpty()) {
+ LayoutDeviceIntRegion clearRegion = mInvalidRegion;
+ clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));
+ clearRect = clearRegion.GetBounds();
+ } else {
+ clearRect = mInvalidRect;
+ }
+
+ // Prevent CreateRenderTargetForWindow from clearing unwanted area.
+ gfxUtils::ClipToRegion(mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ // Setup an intermediate render target to buffer all compositing. We will
+ // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
+ RefPtr<CompositingRenderTarget> target =
+ CreateRenderTargetForWindow(mInvalidRect,
+ clearRect,
+ bufferMode);
+
+ mDrawTarget->PopClip();
+
+ if (!target) {
+ if (!mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+ return;
+ }
+ SetRenderTarget(target);
+
+ // We only allocate a surface sized to the invalidated region, so we need to
+ // translate future coordinates.
+ mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));
+
+ gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = rect;
+ }
+
+ if (aClipRectIn) {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));
+ } else {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(rect));
+ if (aClipRectOut) {
+ *aClipRectOut = rect;
+ }
+ }
+}
+
+void
+BasicCompositor::EndFrame()
+{
+ Compositor::EndFrame();
+
+ // Pop aClipRectIn/bounds rect
+ mRenderTarget->mDrawTarget->PopClip();
+
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ // We're still clipped to mInvalidRegion, so just fill the bounds.
+ mRenderTarget->mDrawTarget->FillRect(
+ IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),
+ ColorPattern(Color(r, g, b, 0.2f)));
+ }
+
+ // Pop aInvalidregion
+ mRenderTarget->mDrawTarget->PopClip();
+
+ TryToEndRemoteDrawing();
+}
+
+void
+BasicCompositor::TryToEndRemoteDrawing(bool aForceToEnd)
+{
+ if (mIsDestroyed || !mRenderTarget) {
+ return;
+ }
+
+ // It it is not a good timing for EndRemoteDrawing, defter to call it.
+ if (!aForceToEnd && !mTarget && NeedsToDeferEndRemoteDrawing()) {
+ mIsPendingEndRemoteDrawing = true;
+
+ const uint32_t retryMs = 2;
+ RefPtr<BasicCompositor> self = this;
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction([self]() {
+ self->TryToEndRemoteDrawing();
+ });
+ MessageLoop::current()->PostDelayedTask(runnable.forget(), retryMs);
+ return;
+ }
+
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ // Note: Most platforms require us to buffer drawing to the widget surface.
+ // That's why we don't draw to mDrawTarget directly.
+ RefPtr<SourceSurface> source;
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ source = mWidget->EndBackBufferDrawing();
+ } else {
+ source = mRenderTarget->mDrawTarget->Snapshot();
+ }
+ RefPtr<DrawTarget> dest(mTarget ? mTarget : mDrawTarget);
+
+ nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint();
+
+ // The source DrawTarget is clipped to the invalidation region, so we have
+ // to copy the individual rectangles in the region or else we'll draw blank
+ // pixels.
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& r = iter.Get();
+ dest->CopySurface(source,
+ IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(),
+ IntPoint(r.x, r.y) - offset);
+ }
+ }
+
+ if (aForceToEnd || !mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+
+ mDrawTarget = nullptr;
+ mRenderTarget = nullptr;
+ mIsPendingEndRemoteDrawing = false;
+}
+
+bool
+BasicCompositor::NeedsToDeferEndRemoteDrawing()
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(mRenderTarget);
+
+ if (mTarget || mRenderTarget->mDrawTarget == mDrawTarget) {
+ return false;
+ }
+
+ return mWidget->NeedsToDeferEndRemoteDrawing();
+}
+
+void
+BasicCompositor::FinishPendingComposite()
+{
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+}
+
+void
+BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(!mTarget);
+ MOZ_ASSERT(!mDrawTarget);
+ MOZ_ASSERT(!mRenderTarget);
+
+ mDidExternalComposition = true;
+}
+
+BasicCompositor*
+AssertBasicCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor()
+ : nullptr;
+ MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+ return compositor;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCompositor.h b/system/graphics/layers/basic/BasicCompositor.h
new file mode 100644
index 000000000..73f3e82c3
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCompositor.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASICCOMPOSITOR_H
+#define MOZILLA_GFX_BASICCOMPOSITOR_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositingRenderTarget : public CompositingRenderTarget
+{
+public:
+ BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aRect)
+ : CompositingRenderTarget(aRect.TopLeft())
+ , mDrawTarget(aDrawTarget)
+ , mSize(aRect.Size())
+ { }
+
+ virtual const char* Name() const override { return "BasicCompositingRenderTarget"; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ void BindRenderTarget();
+
+ virtual gfx::SurfaceFormat GetFormat() const override
+ {
+ return mDrawTarget ? mDrawTarget->GetFormat()
+ : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN);
+ }
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+};
+
+class BasicCompositor : public Compositor
+{
+public:
+ explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+
+protected:
+ virtual ~BasicCompositor();
+
+public:
+
+ virtual BasicCompositor* AsBasicCompositor() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual void DetachWidget() override;
+
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect,
+ const LayoutDeviceIntRect& aClearRect,
+ BufferMode aBufferMode);
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) override;
+
+ virtual bool SupportsEffect(EffectTypes aEffect) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
+ {
+ mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
+ mRenderTarget->BindRenderTarget();
+ }
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mRenderTarget;
+ }
+
+ virtual void DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+ virtual void EndFrame() override;
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
+
+ virtual bool SupportsPartialTextureUpdate() override { return true; }
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override { return true; }
+ virtual int32_t GetMaxTextureSize() const override;
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
+
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
+ }
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override { }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Basic"; }
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_BASIC;
+ }
+
+ gfx::DrawTarget *GetDrawTarget() { return mDrawTarget; }
+
+ virtual bool IsPendingComposite() override
+ {
+ return mIsPendingEndRemoteDrawing;
+ }
+
+ virtual void FinishPendingComposite() override;
+
+private:
+
+ void TryToEndRemoteDrawing(bool aForceToEnd = false);
+
+ bool NeedsToDeferEndRemoteDrawing();
+
+ // The final destination surface
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ // The current render target for drawing
+ RefPtr<BasicCompositingRenderTarget> mRenderTarget;
+
+ LayoutDeviceIntRect mInvalidRect;
+ LayoutDeviceIntRegion mInvalidRegion;
+ bool mDidExternalComposition;
+
+ uint32_t mMaxTextureSize;
+ bool mIsPendingEndRemoteDrawing;
+};
+
+BasicCompositor* AssertBasicCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
diff --git a/system/graphics/layers/basic/BasicContainerLayer.cpp b/system/graphics/layers/basic/BasicContainerLayer.cpp
new file mode 100644
index 000000000..499e202c4
--- /dev/null
+++ b/system/graphics/layers/basic/BasicContainerLayer.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicContainerLayer.h"
+#include <sys/types.h> // for int32_t
+#include "BasicLayersImpl.h" // for ToData
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+#include "ReadbackProcessor.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+BasicContainerLayer::~BasicContainerLayer()
+{
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(BasicContainerLayer);
+}
+
+void
+BasicContainerLayer::ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
+{
+ // We push groups for container layers if we need to, which always
+ // are aligned in device space, so it doesn't really matter how we snap
+ // containers.
+ Matrix residual;
+ Matrix4x4 transformToSurface = aTransformToSurface;
+ bool participate3DCtx = Extend3DContext() || Is3DContextLeaf();
+ if (!participate3DCtx &&
+ GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ // For backface-hidden layers
+ transformToSurface.ProjectTo2D();
+ }
+ Matrix4x4 idealTransform = GetLocalTransform() * transformToSurface;
+ if (!participate3DCtx &&
+ !(GetContentFlags() & CONTENT_BACKFACE_HIDDEN)) {
+ // For non-backface-hidden layers,
+ // 3D components are required to handle CONTENT_BACKFACE_HIDDEN.
+ idealTransform.ProjectTo2D();
+ }
+
+ if (!idealTransform.CanDraw2D()) {
+ if (!Extend3DContext()) {
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(Matrix4x4());
+ ComputeEffectiveTransformForMaskLayers(Matrix4x4());
+ mUseIntermediateSurface = true;
+ return;
+ }
+
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ ComputeEffectiveTransformForMaskLayers(idealTransform);
+ mUseIntermediateSurface = false;
+ return;
+ }
+
+ // With 2D transform or extended 3D context.
+
+ Layer* child = GetFirstChild();
+ bool hasSingleBlendingChild = false;
+ if (!HasMultipleChildren() && child) {
+ hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
+ }
+
+ /* If we have a single childand it is not blending,, it can just inherit our opacity,
+ * otherwise we need a PushGroup and we need to mark ourselves as using
+ * an intermediate surface so our children don't inherit our opacity
+ * via GetEffectiveOpacity.
+ * Having a mask layer always forces our own push group
+ * Having a blend mode also always forces our own push group
+ */
+ mUseIntermediateSurface =
+ GetMaskLayer() ||
+ GetForceIsolatedGroup() ||
+ (GetMixBlendMode() != CompositionOp::OP_OVER && HasMultipleChildren()) ||
+ (GetEffectiveOpacity() != 1.0 && ((HasMultipleChildren() && !Extend3DContext()) || hasSingleBlendingChild));
+
+ mEffectiveTransform =
+ !mUseIntermediateSurface ?
+ idealTransform :
+ (!(GetContentFlags() & CONTENT_BACKFACE_HIDDEN) ?
+ SnapTransformTranslation(idealTransform, &residual) :
+ SnapTransformTranslation3D(idealTransform, &residual));
+ Matrix4x4 childTransformToSurface =
+ (!mUseIntermediateSurface ||
+ (mUseIntermediateSurface && !Extend3DContext() /* 2D */)) ?
+ idealTransform : Matrix4x4::From2D(residual);
+ ComputeEffectiveTransformsForChildren(childTransformToSurface);
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool
+BasicContainerLayer::ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect)
+{
+ Matrix transform;
+ if (!GetEffectiveTransform().CanDraw2D(&transform) ||
+ ThebesMatrix(transform).HasNonIntegerTranslation())
+ return false;
+
+ nsIntPoint offset(int32_t(transform._31), int32_t(transform._32));
+ gfx::IntRect rect = aInRect.Intersect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds() + offset);
+ nsIntRegion covered;
+
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (ToData(l)->IsHidden())
+ continue;
+
+ Matrix childTransform;
+ if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
+ ThebesMatrix(childTransform).HasNonIntegerTranslation() ||
+ l->GetEffectiveOpacity() != 1.0)
+ return false;
+ nsIntRegion childRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ childRegion.MoveBy(int32_t(childTransform._31), int32_t(childTransform._32));
+ childRegion.And(childRegion, rect);
+ if (l->GetClipRect()) {
+ childRegion.And(childRegion, l->GetClipRect()->ToUnknownRect() + offset);
+ }
+ nsIntRegion intersection;
+ intersection.And(covered, childRegion);
+ if (!intersection.IsEmpty())
+ return false;
+ covered.Or(covered, childRegion);
+ }
+
+ return covered.Contains(rect);
+}
+
+void
+BasicContainerLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ ReadbackProcessor readback;
+ if (BasicManager()->IsRetained()) {
+ readback.BuildUpdates(this);
+ }
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ BasicImplData* data = ToData(l);
+ data->Validate(aCallback, aCallbackData, &readback);
+ if (l->GetMaskLayer()) {
+ data = ToData(l->GetMaskLayer());
+ data->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+}
+
+already_AddRefed<ContainerLayer>
+BasicLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ContainerLayer> layer = new BasicContainerLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicContainerLayer.h b/system/graphics/layers/basic/BasicContainerLayer.h
new file mode 100644
index 000000000..c8227f6e9
--- /dev/null
+++ b/system/graphics/layers/basic/BasicContainerLayer.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICCONTAINERLAYER_H
+#define GFX_BASICCONTAINERLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicContainerLayer : public ContainerLayer, public BasicImplData {
+public:
+ explicit BasicContainerLayer(BasicLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+protected:
+ virtual ~BasicContainerLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::InsertAfter(aChild, aAfter);
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RemoveChild(aChild);
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RepositionChild(aChild, aAfter);
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
+
+ /**
+ * Returns true when:
+ * a) no (non-hidden) childrens' visible areas overlap in
+ * (aInRect intersected with this layer's visible region).
+ * b) the (non-hidden) childrens' visible areas cover
+ * (aInRect intersected with this layer's visible region).
+ * c) this layer and all (non-hidden) children have transforms that are translations
+ * by integers.
+ * aInRect is in the root coordinate system.
+ * Child layers with opacity do not contribute to the covered area in check b).
+ * This method can be conservative; it's OK to return false under any
+ * circumstances.
+ */
+ bool ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect);
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ /**
+ * We don't really have a hard restriction for max layer size, but we pick
+ * 4096 to avoid excessive memory usage.
+ */
+ virtual int32_t GetMaxLayerSize() override { return 4096; }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicImageLayer.cpp b/system/graphics/layers/basic/BasicImageLayer.cpp
new file mode 100644
index 000000000..0bb6b132d
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImageLayer.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer (ptr only), etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxPattern::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Point.h" // for IntSize
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicImageLayer : public ImageLayer, public BasicImplData {
+public:
+ explicit BasicImageLayer(BasicLayerManager* aLayerManager) :
+ ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mSize(-1, -1)
+ {
+ MOZ_COUNT_CTOR(BasicImageLayer);
+ }
+protected:
+ virtual ~BasicImageLayer()
+ {
+ MOZ_COUNT_DTOR(BasicImageLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+ virtual already_AddRefed<SourceSurface> GetAsSourceSurface() override;
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ gfx::IntSize mSize;
+};
+
+void
+BasicImageLayer::Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden() || !mContainer) {
+ return;
+ }
+
+ RefPtr<ImageFactory> originalIF = mContainer->GetImageFactory();
+ mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nullptr : BasicManager()->GetImageFactory());
+
+ AutoLockImage autoLock(mContainer);
+ Image *image = autoLock.GetImage();
+ if (!image) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+ RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
+ if (!surface || !surface->IsValid()) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+
+ gfx::IntSize size = mSize = surface->GetSize();
+ FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, size.width, size.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ mContainer->SetImageFactory(originalIF);
+}
+
+already_AddRefed<SourceSurface>
+BasicImageLayer::GetAsSourceSurface()
+{
+ if (!mContainer) {
+ return nullptr;
+ }
+
+ AutoLockImage lockImage(mContainer);
+ Image* image = lockImage.GetImage();
+ if (!image) {
+ return nullptr;
+ }
+ return image->GetAsSourceSurface();
+}
+
+already_AddRefed<ImageLayer>
+BasicLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ImageLayer> layer = new BasicImageLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicImages.cpp b/system/graphics/layers/basic/BasicImages.cpp
new file mode 100644
index 000000000..fc1be6e9a
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImages.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "cairo.h" // for cairo_user_data_key_t
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/CheckedInt.h"
+#include "mozilla/mozalloc.h" // for operator delete[], etc
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAutoRef.h" // for nsCountedRef
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "gfx2DGlue.h"
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+
+class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage
+{
+public:
+ BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
+ : RecyclingPlanarYCbCrImage(aRecycleBin)
+ , mScaleHint(aScaleHint)
+ , mStride(0)
+ , mDelayedConversion(false)
+ {
+ SetOffscreenFormat(aOffscreenFormat);
+ }
+
+ ~BasicPlanarYCbCrImage()
+ {
+ if (mDecodedBuffer) {
+ // Right now this only happens if the Image was never drawn, otherwise
+ // this will have been tossed away at surface destruction.
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+ }
+ }
+
+ virtual bool CopyData(const Data& aData) override;
+ virtual void SetDelayedConversion(bool aDelayed) override { mDelayedConversion = aDelayed; }
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ size_t size = RecyclingPlanarYCbCrImage::SizeOfExcludingThis(aMallocSizeOf);
+ size += aMallocSizeOf(mDecodedBuffer.get());
+ return size;
+ }
+
+private:
+ UniquePtr<uint8_t[]> mDecodedBuffer;
+ gfx::IntSize mScaleHint;
+ int mStride;
+ bool mDelayedConversion;
+};
+
+class BasicImageFactory : public ImageFactory
+{
+public:
+ BasicImageFactory() {}
+
+ virtual RefPtr<PlanarYCbCrImage>
+ CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin)
+ {
+ return new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
+ }
+};
+
+bool
+BasicPlanarYCbCrImage::CopyData(const Data& aData)
+{
+ RecyclingPlanarYCbCrImage::CopyData(aData);
+
+ if (mDelayedConversion) {
+ return false;
+ }
+
+ // Do some sanity checks to prevent integer overflow
+ if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image source width or height");
+ return false;
+ }
+
+ gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+
+ gfx::IntSize size(mScaleHint);
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ size.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return false;
+ }
+
+ gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format);
+ mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width);
+ mozilla::CheckedInt32 requiredBytes =
+ mozilla::CheckedInt32(size.height) * mozilla::CheckedInt32(mStride);
+ if (!requiredBytes.isValid()) {
+ // invalid size
+ return false;
+ }
+ mDecodedBuffer = AllocateBuffer(requiredBytes.value());
+ if (!mDecodedBuffer) {
+ // out of memory
+ return false;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride);
+ SetOffscreenFormat(iFormat);
+ mSize = size;
+
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+BasicPlanarYCbCrImage::GetAsSourceSurface()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
+
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ if (!mDecodedBuffer) {
+ return PlanarYCbCrImage::GetAsSourceSurface();
+ }
+
+ gfxImageFormat format = GetOffscreenFormat();
+
+ RefPtr<gfx::SourceSurface> surface;
+ {
+ // Create a DrawTarget so that we can own the data inside mDecodeBuffer.
+ // We create the target out of mDecodedBuffer, and get a snapshot from it.
+ // The draw target is destroyed on scope exit and the surface owns the data.
+ RefPtr<gfx::DrawTarget> drawTarget
+ = gfxPlatform::CreateDrawTargetForData(mDecodedBuffer.get(),
+ mSize,
+ mStride,
+ gfx::ImageFormatToSurfaceFormat(format));
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ surface = drawTarget->Snapshot();
+ }
+
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+
+ mSourceSurface = surface;
+ return surface.forget();
+}
+
+
+ImageFactory*
+BasicLayerManager::GetImageFactory()
+{
+ if (!mFactory) {
+ mFactory = new BasicImageFactory();
+ }
+
+ return mFactory.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicImplData.h b/system/graphics/layers/basic/BasicImplData.h
new file mode 100644
index 000000000..2ffa310a9
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImplData.h
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICIMPLDATA_H
+#define GFX_BASICIMPLDATA_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+/**
+ * This is the ImplData for all Basic layers. It also exposes methods
+ * private to the Basic implementation that are common to all Basic layer types.
+ * In particular, there is an internal Paint() method that we can use
+ * to paint the contents of non-PaintedLayers.
+ *
+ * The class hierarchy for Basic layers is like this:
+ * BasicImplData
+ * Layer | | |
+ * | | | |
+ * +-> ContainerLayer | | |
+ * | | | | |
+ * | +-> BasicContainerLayer <--+ | |
+ * | | |
+ * +-> PaintedLayer | |
+ * | | | |
+ * | +-> BasicPaintedLayer <---------+ |
+ * | |
+ * +-> ImageLayer |
+ * | |
+ * +-> BasicImageLayer <--------------+
+ */
+class BasicImplData {
+public:
+ BasicImplData() : mHidden(false),
+ mClipToVisibleRegion(false),
+ mDrawAtomically(false),
+ mOperator(gfx::CompositionOp::OP_OVER)
+ {
+ MOZ_COUNT_CTOR(BasicImplData);
+ }
+ virtual ~BasicImplData()
+ {
+ MOZ_COUNT_DTOR(BasicImplData);
+ }
+
+ /**
+ * Layers that paint themselves, such as ImageLayers, should paint
+ * in response to this method call. aContext will already have been
+ * set up to account for all the properties of the layer (transform,
+ * opacity, etc).
+ */
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) {}
+
+ /**
+ * Like Paint() but called for PaintedLayers with the additional parameters
+ * they need.
+ * If mClipToVisibleRegion is set, then the layer must clip to its
+ * effective visible region (snapped or unsnapped, it doesn't matter).
+ */
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMasklayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {}
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) {}
+
+ /**
+ * Layers will get this call when their layer manager is destroyed, this
+ * indicates they should clear resources they don't really need after their
+ * LayerManager ceases to exist.
+ */
+ virtual void ClearCachedResources() {}
+
+ /**
+ * This variable is set by MarkLayersHidden() before painting. It indicates
+ * that the layer should not be composited during this transaction.
+ */
+ void SetHidden(bool aCovered) { mHidden = aCovered; }
+ bool IsHidden() const { return false; }
+ /**
+ * This variable is set by MarkLayersHidden() before painting. This is
+ * the operator to be used when compositing the layer in this transaction. It must
+ * be OVER or SOURCE.
+ */
+ void SetOperator(gfx::CompositionOp aOperator)
+ {
+ NS_ASSERTION(aOperator == gfx::CompositionOp::OP_OVER ||
+ aOperator == gfx::CompositionOp::OP_SOURCE,
+ "Bad composition operator");
+ mOperator = aOperator;
+ }
+
+ gfx::CompositionOp GetOperator() const { return mOperator; }
+
+ /**
+ * Return a surface for this layer. Will use an existing surface, if
+ * possible, or may create a temporary surface. Implement this
+ * method for any layers that might be used as a mask. Should only
+ * return false if a surface cannot be created. If true is
+ * returned, only one of |aSurface| or |aDescriptor| is valid.
+ */
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
+
+ bool GetClipToVisibleRegion() { return mClipToVisibleRegion; }
+ void SetClipToVisibleRegion(bool aClip) { mClipToVisibleRegion = aClip; }
+
+ void SetDrawAtomically(bool aDrawAtomically) { mDrawAtomically = aDrawAtomically; }
+
+protected:
+ bool mHidden;
+ bool mClipToVisibleRegion;
+ bool mDrawAtomically;
+ gfx::CompositionOp mOperator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicLayerManager.cpp b/system/graphics/layers/basic/BasicLayerManager.cpp
new file mode 100644
index 000000000..41c37dc8e
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayerManager.cpp
@@ -0,0 +1,991 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint32_t
+#include <stdlib.h> // for rand, RAND_MAX
+#include <sys/types.h> // for int32_t
+#include <stack> // for stack
+#include "BasicContainerLayer.h" // for BasicContainerLayer
+#include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ImageContainer.h" // for ImageFactory
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "ReadbackProcessor.h" // for ReadbackProcessor
+#include "RenderTrace.h" // for RenderTraceLayers, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager, etc
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxMatrix.h" // for gfxMatrix
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/LayersTypes.h" // for BufferMode::BUFFER_NONE, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#include "nsTArray.h" // for AutoTArray
+#include "TreeTraversal.h" // for ForEachNode
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/**
+ * Clips to the smallest device-pixel-aligned rectangle containing aRect
+ * in user space.
+ * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
+ * aRect.
+ */
+static bool
+ClipToContain(gfxContext* aContext, const IntRect& aRect)
+{
+ gfxRect userRect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gfxRect deviceRect = aContext->UserToDevice(userRect);
+ deviceRect.RoundOut();
+
+ gfxMatrix currentMatrix = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ aContext->NewPath();
+ aContext->Rectangle(deviceRect);
+ aContext->Clip();
+ aContext->SetMatrix(currentMatrix);
+
+ return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
+}
+
+bool
+BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion, PushedGroup& aGroupResult)
+{
+ aGroupResult.mVisibleRegion = aRegion;
+ aGroupResult.mFinalTarget = aContext;
+ aGroupResult.mOperator = GetEffectiveOperator(aLayer);
+ aGroupResult.mOpacity = aLayer->GetEffectiveOpacity();
+
+ // If we need to call PushGroup, we should clip to the smallest possible
+ // area first to minimize the size of the temporary surface.
+ bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
+
+ bool canPushGroup = aGroupResult.mOperator == CompositionOp::OP_OVER ||
+ (aGroupResult.mOperator == CompositionOp::OP_SOURCE && (aLayer->CanUseOpaqueSurface() || aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+
+ if (!canPushGroup) {
+ aContext->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+
+ // PushGroup/PopGroup do not support non operator over.
+ gfxMatrix oldMat = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ gfxRect rect = aContext->GetClipExtents();
+ aContext->SetMatrix(oldMat);
+ rect.RoundOut();
+ IntRect surfRect;
+ ToRect(rect).ToIntRect(&surfRect);
+
+ if (!surfRect.IsEmpty()) {
+ RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ RefPtr<gfxContext> ctx =
+ gfxContext::CreateOrNull(dt, ToRect(rect).TopLeft());
+ if (!ctx) {
+ gfxCriticalNote << "BasicLayerManager context problem in PushGroupForLayer " << gfx::hexa(dt);
+ return false;
+ }
+ ctx->SetMatrix(oldMat);
+
+ aGroupResult.mGroupOffset = surfRect.TopLeft();
+ aGroupResult.mGroupTarget = ctx;
+
+ aGroupResult.mMaskSurface = GetMaskForLayer(aLayer, &aGroupResult.mMaskTransform);
+ return true;
+ }
+ aContext->Restore();
+ }
+
+ Matrix maskTransform;
+ RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
+
+ if (maskSurf) {
+ // The returned transform will transform the mask to device space on the
+ // destination. Since the User->Device space transform will be applied
+ // to the mask by PopGroupAndBlend we need to adjust the transform to
+ // transform the mask to user space.
+ Matrix currentTransform = ToMatrix(aGroupResult.mFinalTarget->CurrentMatrix());
+ currentTransform.Invert();
+ maskTransform = maskTransform * currentTransform;
+ }
+
+ if (aLayer->CanUseOpaqueSurface() &&
+ ((didCompleteClip && aRegion.GetNumRects() == 1) ||
+ !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
+ // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
+ // group. We need to make sure that only pixels inside the layer's visible
+ // region are copied back to the destination. Remember if we've already
+ // clipped precisely to the visible region.
+ aGroupResult.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
+ if (aGroupResult.mNeedsClipToVisibleRegion) {
+ aGroupResult.mFinalTarget->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+ }
+
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
+ aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ }
+ }
+
+ aGroupResult.mGroupTarget = aGroupResult.mFinalTarget;
+
+ return true;
+}
+
+void
+BasicLayerManager::PopGroupForLayer(PushedGroup &group)
+{
+ if (group.mFinalTarget == group.mGroupTarget) {
+ group.mFinalTarget->PopGroupAndBlend();
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Restore();
+ }
+ return;
+ }
+
+ DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
+ RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
+ group.mGroupTarget = nullptr;
+
+ RefPtr<SourceSurface> src = sourceDT->Snapshot();
+
+ if (group.mMaskSurface) {
+ Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+ dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+ Matrix surfTransform = group.mMaskTransform;
+ surfTransform.Invert();
+ dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
+ Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+ group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
+ } else {
+ // For now this is required since our group offset is in device space of the final target,
+ // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
+ // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
+ // always become null.
+ dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
+ Rect(0, 0, src->GetSize().width, src->GetSize().height), DrawSurfaceOptions(SamplingFilter::POINT), DrawOptions(group.mOpacity, group.mOperator));
+ }
+
+ if (group.mNeedsClipToVisibleRegion) {
+ dt->PopClip();
+ }
+
+ group.mFinalTarget->Restore();
+}
+
+static IntRect
+ToInsideIntRect(const gfxRect& aRect)
+{
+ return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+}
+
+// A context helper for BasicLayerManager::PaintLayer() that holds all the
+// painting context together in a data structure so it can be easily passed
+// around. It also uses ensures that the Transform and Opaque rect are restored
+// to their former state on destruction.
+
+class PaintLayerContext {
+public:
+ PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ : mTarget(aTarget)
+ , mTargetMatrixSR(aTarget)
+ , mLayer(aLayer)
+ , mCallback(aCallback)
+ , mCallbackData(aCallbackData)
+ , mPushedOpaqueRect(false)
+ {}
+
+ ~PaintLayerContext()
+ {
+ // Matrix is restored by mTargetMatrixSR
+ if (mPushedOpaqueRect)
+ {
+ ClearOpaqueRect();
+ }
+ }
+
+ // Gets the effective transform and returns true if it is a 2D
+ // transform.
+ bool Setup2DTransform()
+ {
+ // Will return an identity matrix for 3d transforms.
+ return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
+ }
+
+ // Applies the effective transform if it's 2D. If it's a 3D transform then
+ // it applies an identity.
+ void Apply2DTransform()
+ {
+ mTarget->SetMatrix(ThebesMatrix(mTransform));
+ }
+
+ // Set the opaque rect to match the bounds of the visible region.
+ void AnnotateOpaqueRect()
+ {
+ const nsIntRegion visibleRegion = mLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ const IntRect& bounds = visibleRegion.GetBounds();
+
+ DrawTarget *dt = mTarget->GetDrawTarget();
+ const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
+
+ // Try to annotate currentSurface with a region of pixels that have been
+ // (or will be) painted opaque, if no such region is currently set.
+ if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+ (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ !mTransform.HasNonAxisAlignedTransform()) {
+
+ gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
+ gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
+ opaqueRect.RoundIn();
+ IntRect intOpaqueRect;
+ if (opaqueRect.ToIntRect(&intOpaqueRect)) {
+ mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
+ mPushedOpaqueRect = true;
+ }
+ }
+ }
+
+ // Clear the Opaque rect. Although this doesn't really restore it to it's
+ // previous state it will happen on the exit path of the PaintLayer() so when
+ // painting is complete the opaque rect qill be clear.
+ void ClearOpaqueRect() {
+ mTarget->GetDrawTarget()->SetOpaqueRect(IntRect());
+ }
+
+ gfxContext* mTarget;
+ gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
+ Layer* mLayer;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+ Matrix mTransform;
+ bool mPushedOpaqueRect;
+};
+
+BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(BLM_WIDGET)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ NS_ASSERTION(aWidget, "Must provide a widget");
+}
+
+BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType)
+ : mPhase(PHASE_NONE)
+ , mWidget(nullptr)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(aType)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ MOZ_ASSERT(mType != BLM_WIDGET);
+}
+
+BasicLayerManager::~BasicLayerManager()
+{
+ NS_ASSERTION(!InTransaction(), "Died during transaction?");
+
+ ClearCachedResources();
+
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(BasicLayerManager);
+}
+
+void
+BasicLayerManager::SetDefaultTarget(gfxContext* aContext)
+{
+ NS_ASSERTION(!InTransaction(),
+ "Must set default target outside transaction");
+ mDefaultTarget = aContext;
+}
+
+void
+BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
+{
+ mDoubleBuffering = aDoubleBuffering;
+}
+
+bool
+BasicLayerManager::BeginTransaction()
+{
+ mInTransaction = true;
+ mUsingDefaultTarget = true;
+ return BeginTransactionWithTarget(mDefaultTarget);
+}
+
+bool
+BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ mInTransaction = true;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+ mTarget = aTarget;
+ return true;
+}
+
+static void
+TransformIntRect(IntRect& aRect, const Matrix& aMatrix,
+ IntRect (*aRoundMethod)(const gfxRect&))
+{
+ Rect gr = Rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gr = aMatrix.TransformBounds(gr);
+ aRect = (*aRoundMethod)(ThebesRect(gr));
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * It can't be used as is by accelerated layers because of intermediate surfaces.
+ * This must set the hidden flag to true or false on *all* layers in the subtree.
+ * It also sets the operator for all layers to "OVER", and call
+ * SetDrawAtomically(false).
+ * It clears mClipToVisibleRegion on all layers.
+ * @param aClipRect the cliprect, in the root coordinate system. We assume
+ * that any layer drawing is clipped to this rect. It is therefore not
+ * allowed to add to the opaque region outside that rect.
+ * @param aDirtyRect the dirty rect that will be painted, in the root
+ * coordinate system. Layers outside this rect should be hidden.
+ * @param aOpaqueRegion the opaque region covering aLayer, in the
+ * root coordinate system.
+ */
+enum {
+ ALLOW_OPAQUE = 0x01,
+};
+static void
+MarkLayersHidden(Layer* aLayer, const IntRect& aClipRect,
+ const IntRect& aDirtyRect,
+ nsIntRegion& aOpaqueRegion,
+ uint32_t aFlags)
+{
+ IntRect newClipRect(aClipRect);
+ uint32_t newFlags = aFlags;
+
+ // Allow aLayer or aLayer's descendants to cover underlying layers
+ // only if it's opaque.
+ if (aLayer->GetOpacity() != 1.0f) {
+ newFlags &= ~ALLOW_OPAQUE;
+ }
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ // Clip rect is applied after aLayer's transform, i.e., in the coordinate
+ // system of aLayer's parent.
+ TransformIntRect(cr, tr, ToInsideIntRect);
+ } else {
+ cr.SetRect(0, 0, 0, 0);
+ }
+ }
+ newClipRect.IntersectRect(newClipRect, cr);
+ }
+ }
+
+ BasicImplData* data = ToData(aLayer);
+ data->SetOperator(CompositionOp::OP_OVER);
+ data->SetClipToVisibleRegion(false);
+ data->SetDrawAtomically(false);
+
+ if (!aLayer->AsContainerLayer()) {
+ Matrix transform;
+ if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
+ data->SetHidden(false);
+ return;
+ }
+
+ nsIntRegion region = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ IntRect r = region.GetBounds();
+ TransformIntRect(r, transform, ToOutsideIntRect);
+ r.IntersectRect(r, aDirtyRect);
+ data->SetHidden(aOpaqueRegion.Contains(r));
+
+ // Allow aLayer to cover underlying layers only if aLayer's
+ // content is opaque
+ if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ (newFlags & ALLOW_OPAQUE)) {
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ r = iter.Get();
+ TransformIntRect(r, transform, ToInsideIntRect);
+
+ r.IntersectRect(r, newClipRect);
+ aOpaqueRegion.Or(aOpaqueRegion, r);
+ }
+ }
+ } else {
+ Layer* child = aLayer->GetLastChild();
+ bool allHidden = true;
+ for (; child; child = child->GetPrevSibling()) {
+ MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
+ if (!ToData(child)->IsHidden()) {
+ allHidden = false;
+ }
+ }
+ data->SetHidden(allHidden);
+ }
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * MarkLayersHidden must be called before calling this.
+ * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
+ * clipped and in the dirty rect), in the root coordinate system.
+ */
+static void
+ApplyDoubleBuffering(Layer* aLayer, const IntRect& aVisibleRect)
+{
+ BasicImplData* data = ToData(aLayer);
+ if (data->IsHidden())
+ return;
+
+ IntRect newVisibleRect(aVisibleRect);
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ NS_ASSERTION(!ThebesMatrix(tr).HasNonIntegerTranslation(),
+ "Parent can only have an integer translation");
+ cr += nsIntPoint(int32_t(tr._31), int32_t(tr._32));
+ } else {
+ NS_ERROR("Parent can only have an integer translation");
+ }
+ }
+ newVisibleRect.IntersectRect(newVisibleRect, cr);
+ }
+ }
+
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ // Layers that act as their own backbuffers should be drawn to the destination
+ // using OP_SOURCE to ensure that alpha values in a transparent window are
+ // cleared. This can also be faster than OP_OVER.
+ if (!container) {
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ data->SetDrawAtomically(true);
+ } else {
+ if (container->UseIntermediateSurface() ||
+ !container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
+ // We need to double-buffer this container.
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ container->ForceIntermediateSurface();
+ } else {
+ // Tell the children to clip to their visible regions so our assumption
+ // that they don't paint outside their visible regions is valid!
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ToData(child)->SetClipToVisibleRegion(true);
+ ApplyDoubleBuffering(child, newVisibleRect);
+ }
+ }
+ }
+}
+
+void
+BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+}
+
+void
+BasicLayerManager::AbortTransaction()
+{
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_NONE;
+ mUsingDefaultTarget = false;
+ mInTransaction = false;
+}
+
+bool
+BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ PROFILER_LABEL("BasicLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ RenderTraceLayers(mRoot, "FF00");
+
+ mTransactionIncomplete = false;
+
+ if (mRoot) {
+ if (aFlags & END_NO_COMPOSITE) {
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ mRoot->ApplyPendingUpdatesToSubtree();
+ }
+
+ // Need to do this before we call ApplyDoubleBuffering,
+ // which depends on correct effective transforms
+ if (mTarget) {
+ mSnapEffectiveTransforms =
+ !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
+ } else {
+ mSnapEffectiveTransforms = true;
+ }
+ mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(ToMatrix(mTarget->CurrentMatrix())) : Matrix4x4());
+
+ ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
+ if (mRoot->GetMaskLayer()) {
+ ToData(mRoot->GetMaskLayer())->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+
+ if (mTarget && mRoot &&
+ !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
+ !(aFlags & END_NO_COMPOSITE)) {
+ IntRect clipRect;
+
+ {
+ gfxContextMatrixAutoSaveRestore save(mTarget);
+ mTarget->SetMatrix(gfxMatrix());
+ clipRect = ToOutsideIntRect(mTarget->GetClipExtents());
+ }
+
+ if (IsRetained()) {
+ nsIntRegion region;
+ MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
+ if (mUsingDefaultTarget && mDoubleBuffering != BufferMode::BUFFER_NONE) {
+ ApplyDoubleBuffering(mRoot, clipRect);
+ }
+ }
+
+ PaintLayer(mTarget, mRoot, aCallback, aCallbackData);
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mTarget->GetDrawTarget()->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+ if (mWidget) {
+ FlashWidgetUpdateArea(mTarget);
+ }
+ RecordFrame();
+ PostPresent();
+
+ if (!mTransactionIncomplete) {
+ // Clear out target if we have a complete transaction.
+ mTarget = nullptr;
+ }
+ }
+
+ if (mRoot) {
+ mAnimationReadyTime = TimeStamp::Now();
+ mRoot->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ if (!mTransactionIncomplete) {
+ // This is still valid if the transaction was incomplete.
+ mUsingDefaultTarget = false;
+ }
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ // XXX - We should probably assert here that for an incomplete transaction
+ // out target is the default target.
+
+ return !mTransactionIncomplete;
+}
+
+void
+BasicLayerManager::FlashWidgetUpdateArea(gfxContext *aContext)
+{
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ aContext->SetColor(Color(r, g, b, 0.2f));
+ aContext->Paint();
+ }
+}
+
+bool
+BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot) {
+ return false;
+ }
+
+ return EndTransactionInternal(nullptr, nullptr, aFlags);
+}
+
+void
+BasicLayerManager::SetRoot(Layer* aLayer)
+{
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+}
+
+void
+BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
+ gfxContext* aGroupTarget)
+{
+ MOZ_ASSERT(aGroupTarget);
+ BasicImplData* data = ToData(aPaintContext.mLayer);
+
+ /* Only paint ourself, or our children - This optimization relies on this! */
+ Layer* child = aPaintContext.mLayer->GetFirstChild();
+ if (!child) {
+ if (aPaintContext.mLayer->AsPaintedLayer()) {
+ data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
+ aPaintContext.mCallback, aPaintContext.mCallbackData);
+ } else {
+ data->Paint(aGroupTarget->GetDrawTarget(),
+ aGroupTarget->GetDeviceOffset(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+ } else {
+ ContainerLayer* container =
+ static_cast<ContainerLayer*>(aPaintContext.mLayer);
+ AutoTArray<Layer*, 12> children;
+ container->SortChildrenBy3DZOrder(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* layer = children.ElementAt(i);
+ if (layer->IsBackfaceHidden()) {
+ continue;
+ }
+ if (!layer->AsContainerLayer() && !layer->IsVisible()) {
+ continue;
+ }
+
+ PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
+ aPaintContext.mCallbackData);
+ if (mTransactionIncomplete)
+ break;
+ }
+ }
+}
+
+void
+BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion)
+{
+ // If we're doing our own double-buffering, we need to avoid drawing
+ // the results of an incomplete transaction to the destination surface ---
+ // that could cause flicker. Double-buffering is implemented using a
+ // temporary surface for one or more container layers, so we need to stop
+ // those temporary surfaces from being composited to aTarget.
+ // ApplyDoubleBuffering guarantees that this container layer can't
+ // intersect any other leaf layers, so if the transaction is not yet marked
+ // incomplete, the contents of this container layer are the final contents
+ // for the window.
+ if (!mTransactionIncomplete) {
+ if (aNeedsClipToVisibleRegion) {
+ gfxUtils::ClipToRegion(aPaintContext.mTarget,
+ aPaintContext.mLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+
+ CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
+ AutoSetOperator setOperator(aPaintContext.mTarget, op);
+
+ PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+}
+
+/**
+ * Install the clip applied to the layer on the given gfxContext. The
+ * given gfxContext is the buffer that the layer will be painted to.
+ */
+static void
+InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer)
+{
+ const Maybe<ParentLayerIntRect> &clipRect = aLayer->GetLocalClipRect();
+
+ if (!clipRect) {
+ return;
+ }
+ MOZ_ASSERT(!aLayer->Extend3DContext() ||
+ !aLayer->Combines3DTransformWithAncestors(),
+ "Layers in a preserve 3D context have no clip"
+ " except leaves and the estabisher!");
+
+ Layer* parent = aLayer->GetParent();
+ Matrix4x4 transform3d =
+ parent && parent->Extend3DContext() ?
+ parent->GetEffectiveTransform() :
+ Matrix4x4();
+ Matrix transform;
+ if (!transform3d.CanDraw2D(&transform)) {
+ gfxDevCrash(LogReason::CannotDraw3D) << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
+ }
+ gfxMatrix oldTransform = aTarget->CurrentMatrix();
+ transform *= ToMatrix(oldTransform);
+ aTarget->SetMatrix(ThebesMatrix(transform));
+
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y,
+ clipRect->width, clipRect->height));
+ aTarget->Clip();
+
+ aTarget->SetMatrix(oldTransform);
+}
+
+void
+BasicLayerManager::PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ MOZ_ASSERT(aTarget);
+
+ PROFILER_LABEL("BasicLayerManager", "PaintLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback, aCallbackData);
+
+ // Don't attempt to paint layers with a singular transform, cairo will
+ // just throw an error.
+ if (aLayer->GetEffectiveTransform().IsSingular()) {
+ return;
+ }
+
+ RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
+
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ bool needsGroup = container && container->UseIntermediateSurface();
+ BasicImplData* data = ToData(aLayer);
+ bool needsClipToVisibleRegion =
+ data->GetClipToVisibleRegion() && !aLayer->AsPaintedLayer();
+ NS_ASSERTION(needsGroup || !container ||
+ container->GetOperator() == CompositionOp::OP_OVER,
+ "non-OVER operator should have forced UseIntermediateSurface");
+ NS_ASSERTION(!container || !aLayer->GetMaskLayer() ||
+ container->UseIntermediateSurface(),
+ "ContainerLayer with mask layer should force UseIntermediateSurface");
+
+ gfxContextAutoSaveRestore contextSR;
+ gfxMatrix transform;
+ // Will return an identity matrix for 3d transforms, and is handled separately below.
+ bool is2D = paintLayerContext.Setup2DTransform();
+ MOZ_ASSERT(is2D || needsGroup || !container ||
+ container->Extend3DContext() ||
+ container->Is3DContextLeaf(),
+ "Must PushGroup for 3d transforms!");
+
+ Layer* parent = aLayer->GetParent();
+ bool inPreserves3DChain = parent && parent->Extend3DContext();
+ bool needsSaveRestore =
+ needsGroup || clipRect || needsClipToVisibleRegion || !is2D ||
+ inPreserves3DChain;
+ if (needsSaveRestore) {
+ contextSR.SetContext(aTarget);
+
+ // The clips on ancestors on the preserved3d chain should be
+ // installed on the aTarget before painting the layer.
+ InstallLayerClipPreserves3D(aTarget, aLayer);
+ for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
+ InstallLayerClipPreserves3D(aTarget, l);
+ }
+ }
+
+ paintLayerContext.Apply2DTransform();
+
+ nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ // If needsGroup is true, we'll clip to the visible region after we've popped the group
+ if (needsClipToVisibleRegion && !needsGroup) {
+ gfxUtils::ClipToRegion(aTarget, visibleRegion);
+ // Don't need to clip to visible region again
+ needsClipToVisibleRegion = false;
+ }
+
+ if (is2D) {
+ paintLayerContext.AnnotateOpaqueRect();
+ }
+
+ bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
+ if (clipIsEmpty) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ if (is2D) {
+ if (needsGroup) {
+ PushedGroup pushedGroup;
+ if (PushGroupForLayer(aTarget, aLayer, aLayer->GetLocalVisibleRegion().ToUnknownRegion(), pushedGroup)) {
+ PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
+ PopGroupForLayer(pushedGroup);
+ }
+ } else {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ }
+ } else {
+ if (!needsGroup && container) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ IntRect bounds = visibleRegion.GetBounds();
+ // DrawTarget without the 3D transform applied:
+ RefPtr<DrawTarget> untransformedDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
+ SurfaceFormat::B8G8R8A8);
+ if (!untransformedDT || !untransformedDT->IsValid()) {
+ return;
+ }
+ untransformedDT->SetTransform(Matrix::Translation(-Point(bounds.x, bounds.y)));
+
+ RefPtr<gfxContext> groupTarget =
+ gfxContext::CreatePreservingTransformOrNull(untransformedDT);
+ MOZ_ASSERT(groupTarget); // already checked the target above
+
+ PaintSelfOrChildren(paintLayerContext, groupTarget);
+
+ // Temporary fast fix for bug 725886
+ // Revert these changes when 725886 is ready
+#ifdef DEBUG
+ if (aLayer->GetDebugColorIndex() != 0) {
+ Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
+ untransformedDT->FillRect(Rect(bounds), ColorPattern(color));
+ }
+#endif
+ Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
+ Rect xformBounds =
+ effectiveTransform.TransformAndClipBounds(Rect(bounds),
+ ToRect(aTarget->GetClipExtents()));
+ xformBounds.RoundOut();
+ effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
+
+ RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
+ RefPtr<DrawTarget> xformDT =
+ untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<SourceSurface> xformSurf;
+ if(xformDT && untransformedSurf &&
+ xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
+ xformSurf = xformDT->Snapshot();
+ }
+
+ if (xformSurf) {
+ aTarget->SetPattern(
+ new gfxPattern(xformSurf,
+ Matrix::Translation(xformBounds.TopLeft())));
+
+ // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
+ // of the source surface out to the current clip region, clip to
+ // the rectangle of the result surface now.
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(ThebesRect(xformBounds));
+ aTarget->Clip();
+ FlushGroup(paintLayerContext, needsClipToVisibleRegion);
+ }
+ }
+}
+
+void
+BasicLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+void
+BasicLayerManager::ClearLayer(Layer* aLayer)
+{
+ ToData(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+already_AddRefed<ReadbackLayer>
+BasicLayerManager::CreateReadbackLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicLayers.h b/system/graphics/layers/basic/BasicLayers.h
new file mode 100644
index 000000000..beb4357bb
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayers.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICLAYERS_H
+#define GFX_BASICLAYERS_H
+
+#include <stdint.h> // for INT32_MAX, int32_t
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxTypes.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ImageFactory;
+class ImageLayer;
+class PaintLayerContext;
+class ReadbackLayer;
+
+/**
+ * This is a cairo/Thebes-only, main-thread-only implementation of layers.
+ *
+ * In each transaction, the client sets up the layer tree and then during
+ * the drawing phase, each PaintedLayer is painted directly into the target
+ * context (with appropriate clipping and Push/PopGroups performed
+ * between layers).
+ */
+class BasicLayerManager final :
+ public LayerManager
+{
+public:
+ enum BasicLayerManagerType {
+ BLM_WIDGET,
+ BLM_OFFSCREEN,
+ BLM_INACTIVE
+ };
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will not
+ * be retained.
+ */
+ explicit BasicLayerManager(BasicLayerManagerType aType);
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will be
+ * retained; that is, we will try to retain the visible contents of
+ * PaintedLayers as cairo surfaces. We create PaintedLayer buffers by
+ * creating similar surfaces to the default target context, or to
+ * aWidget's GetThebesSurface if there is no default target context, or
+ * to the passed-in context if there is no widget and no default
+ * target context.
+ *
+ * This does not keep a strong reference to the widget, so the caller
+ * must ensure that the widget outlives the layer manager or call
+ * ClearWidget before the widget dies.
+ */
+ explicit BasicLayerManager(nsIWidget* aWidget);
+
+protected:
+ virtual ~BasicLayerManager();
+
+public:
+ BasicLayerManager* AsBasicLayerManager() override { return this; }
+
+ /**
+ * Set the default target context that will be used when BeginTransaction
+ * is called. This can only be called outside a transaction.
+ *
+ * aDoubleBuffering can request double-buffering for drawing to the
+ * default target. When BUFFERED, the layer manager avoids blitting
+ * temporary results to aContext and then overpainting them with final
+ * results, by using a temporary buffer when necessary. In BUFFERED
+ * mode we always completely overwrite the contents of aContext's
+ * destination surface (within the clip region) using OP_SOURCE.
+ */
+ void SetDefaultTarget(gfxContext* aContext);
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ gfxContext* GetDefaultTarget() { return mDefaultTarget; }
+
+ nsIWidget* GetRetainerWidget() { return mWidget; }
+ void ClearRetainerWidget() { mWidget = nullptr; }
+
+ virtual bool IsWidgetLayerManager() override { return mWidget != nullptr; }
+ virtual bool IsInactiveLayerManager() override { return mType == BLM_INACTIVE; }
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return IsWidgetLayerManager(); }
+
+ void AbortTransaction();
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual ImageFactory *GetImageFactory();
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_BASIC; }
+ virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("Basic"); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ gfxContext* GetTarget() { return mTarget; }
+ void SetTarget(gfxContext* aTarget) { mUsingDefaultTarget = false; mTarget = aTarget; }
+ bool IsRetained() { return mWidget != nullptr; }
+
+ virtual const char* Name() const override { return "Basic"; }
+
+ // Clear the cached contents of this layer tree.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+ bool IsTransactionIncomplete() { return mTransactionIncomplete; }
+
+ struct PushedGroup
+ {
+ PushedGroup() : mFinalTarget(nullptr), mNeedsClipToVisibleRegion(false), mOperator(gfx::CompositionOp::OP_COUNT), mOpacity(0.0f){}
+ gfxContext* mFinalTarget;
+ RefPtr<gfxContext> mGroupTarget;
+ nsIntRegion mVisibleRegion;
+ bool mNeedsClipToVisibleRegion;
+ gfx::IntPoint mGroupOffset;
+ gfx::CompositionOp mOperator;
+ gfx::Float mOpacity;
+ RefPtr<gfx::SourceSurface> mMaskSurface;
+ gfx::Matrix mMaskTransform;
+ };
+
+ // Construct a PushedGroup for a specific layer.
+ // Return false if it has some errors in PushGroupForLayer(). Then, the
+ // "aGroupResult" is unavailable for future using.
+ bool PushGroupForLayer(gfxContext* aContext, Layer* aLayerContext, const nsIntRegion& aRegion, PushedGroup& aGroupResult);
+
+ void PopGroupForLayer(PushedGroup& aGroup);
+
+ virtual bool IsCompositingCheap() override { return false; }
+ virtual int32_t GetMaxTextureSize() const override { return INT32_MAX; }
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+ // This is the main body of the PaintLayer routine which will if it has
+ // children, recurse into PaintLayer() otherwise it will paint using the
+ // underlying Paint() method of the Layer. It will not do both.
+ void PaintSelfOrChildren(PaintLayerContext& aPaintContext, gfxContext* aGroupTarget);
+
+ // Paint the group onto the underlying target. This is used by PaintLayer to
+ // flush the group to the underlying target.
+ void FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion);
+
+ // Paints aLayer to mTarget.
+ void PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ // Clear the contents of a layer
+ void ClearLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ void FlashWidgetUpdateArea(gfxContext* aContext);
+
+ // Widget whose surface should be used as the basis for PaintedLayer
+ // buffers.
+ nsIWidget* mWidget;
+ // The default context for BeginTransaction.
+ RefPtr<gfxContext> mDefaultTarget;
+ // The context to draw into.
+ RefPtr<gfxContext> mTarget;
+ // Image factory we use.
+ RefPtr<ImageFactory> mFactory;
+
+ BufferMode mDoubleBuffering;
+ BasicLayerManagerType mType;
+ bool mUsingDefaultTarget;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_BASICLAYERS_H */
diff --git a/system/graphics/layers/basic/BasicLayersImpl.cpp b/system/graphics/layers/basic/BasicLayersImpl.cpp
new file mode 100644
index 000000000..c2262c512
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayersImpl.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h"
+#include <new> // for operator new
+#include "Layers.h" // for Layer, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "AutoMaskData.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+bool
+GetMaskData(Layer* aMaskLayer,
+ const Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData)
+{
+ if (aMaskLayer) {
+ RefPtr<SourceSurface> surface =
+ static_cast<BasicImplData*>(aMaskLayer->ImplData())->GetAsSourceSurface();
+ if (surface) {
+ Matrix transform;
+ Matrix4x4 effectiveTransform = aMaskLayer->GetEffectiveTransform();
+ DebugOnly<bool> maskIs2D = effectiveTransform.CanDraw2D(&transform);
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+ transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
+ aMaskData->Construct(transform, surface);
+ return true;
+ }
+ }
+ return false;
+}
+
+already_AddRefed<SourceSurface>
+GetMaskForLayer(Layer* aLayer, Matrix* aMaskTransform)
+{
+ if (!aLayer->GetMaskLayer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aMaskTransform);
+
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aLayer->GetMaskLayer(), Point(), &mask)) {
+ *aMaskTransform = mask.GetTransform();
+ RefPtr<SourceSurface> surf = mask.GetSurface();
+ return surf.forget();
+ }
+
+ return nullptr;
+}
+
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, Point(), &mask)) {
+ aContext->SetMatrix(ThebesMatrix(mask.GetTransform()));
+ aContext->Mask(mask.GetSurface(), aOpacity);
+ return;
+ }
+
+ // if there is no mask, just paint normally
+ aContext->Paint(aOpacity);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(ColorPattern(aColor), aMaskSource, Point(), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect, ColorPattern(aColor), aOptions);
+}
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aColor, aOptions, mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aColor, aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ ExtendMode aExtendMode,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform,
+ const Matrix* aSurfaceTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ Matrix transform = oldTransform * inverseMask;
+ if (aSurfaceTransform) {
+ transform = (*aSurfaceTransform) * transform;
+ }
+
+ SurfacePattern source(aSurface, aExtendMode, transform, aSamplingFilter);
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect,
+ SurfacePattern(aSurface, aExtendMode,
+ aSurfaceTransform ? (*aSurfaceTransform) : Matrix(),
+ aSamplingFilter), aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP,
+ mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP);
+}
+
+BasicImplData*
+ToData(Layer* aLayer)
+{
+ return static_cast<BasicImplData*>(aLayer->ImplData());
+}
+
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer)
+{
+ CompositionOp op = aLayer->GetEffectiveMixBlendMode();
+
+ if (op != CompositionOp::OP_OVER) {
+ return op;
+ }
+
+ return ToData(aLayer)->GetOperator();
+}
+
+ShadowableLayer*
+ToShadowable(Layer* aLayer)
+{
+ return aLayer->AsShadowableLayer();
+}
+
+bool
+ShouldShadow(Layer* aLayer)
+{
+ if (!ToShadowable(aLayer)) {
+ MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_READBACK,
+ "Only expect not to shadow ReadbackLayers");
+ return false;
+ }
+ return true;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicLayersImpl.h b/system/graphics/layers/basic/BasicLayersImpl.h
new file mode 100644
index 000000000..5626fe329
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayersImpl.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICLAYERSIMPL_H
+#define GFX_BASICLAYERSIMPL_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "gfxContext.h" // for gfxContext, etc
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/Maybe.h" // for Maybe
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class AutoMoz2DMaskData;
+class Layer;
+
+class AutoSetOperator {
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+public:
+ AutoSetOperator(gfxContext* aContext, CompositionOp aOperator) {
+ if (aOperator != CompositionOp::OP_OVER) {
+ aContext->SetOp(aOperator);
+ mContext = aContext;
+ }
+ }
+ ~AutoSetOperator() {
+ if (mContext) {
+ mContext->SetOp(CompositionOp::OP_OVER);
+ }
+ }
+private:
+ RefPtr<gfxContext> mContext;
+};
+
+class BasicReadbackLayer : public ReadbackLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicReadbackLayer(BasicLayerManager* aLayerManager) :
+ ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicReadbackLayer);
+ }
+
+protected:
+ virtual ~BasicReadbackLayer()
+ {
+ MOZ_COUNT_DTOR(BasicReadbackLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ReadbackLayer::SetVisibleRegion(aRegion);
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+/*
+ * Extract a mask surface for a mask layer
+ * Returns true and through outparams a surface for the mask layer if
+ * a mask layer is present and has a valid surface and transform;
+ * false otherwise.
+ * The transform for the layer will be put in aMaskData
+ */
+bool
+GetMaskData(Layer* aMaskLayer,
+ const gfx::Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData);
+
+already_AddRefed<gfx::SourceSurface> GetMaskForLayer(Layer* aLayer, gfx::Matrix* aMaskTransform);
+
+// Paint the current source to a context using a mask, if present
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
+
+// Fill the rect with the source, using a mask and opacity, if present
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ gfx::ExtendMode aExtendMode,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr,
+ const gfx::Matrix* aSurfaceTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+
+BasicImplData*
+ToData(Layer* aLayer);
+
+/**
+ * Returns the operator to be used when blending and compositing this layer.
+ * Currently there is no way to specify both a blending and a compositing
+ * operator other than normal and source over respectively.
+ *
+ * If the layer has
+ * an effective blend mode operator other than normal, as returned by
+ * GetEffectiveMixBlendMode, this operator is used for blending, and source
+ * over is used for compositing.
+ * If the blend mode for this layer is normal, the compositing operator
+ * returned by GetOperator is used.
+ */
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicPaintedLayer.cpp b/system/graphics/layers/basic/BasicPaintedLayer.cpp
new file mode 100644
index 000000000..d2c456daa
--- /dev/null
+++ b/system/graphics/layers/basic/BasicPaintedLayer.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicPaintedLayer.h"
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "ReadbackProcessor.h" // for ReadbackProcessor::Update, etc
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "BasicLayersImpl.h" // for AutoMaskData, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "AutoMaskData.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static nsIntRegion
+IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
+{
+ gfxRect clip = aContext->GetClipExtents();
+ nsIntRegion result;
+ result.And(aRegion, IntRect::RoundOut(clip.X(), clip.Y(),
+ clip.Width(), clip.Height()));
+ return result;
+}
+
+void
+BasicPaintedLayer::PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ PROFILER_LABEL("BasicPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(BasicManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ float opacity = GetEffectiveOpacity();
+ CompositionOp effectiveOperator = GetEffectiveOperator(this);
+
+ if (!BasicManager()->IsRetained()) {
+ mValidRegion.SetEmpty();
+ mContentClient->Clear();
+
+ nsIntRegion toDraw = IntersectWithClip(GetLocalVisibleRegion().ToUnknownRegion(), aContext);
+
+ RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
+
+ if (!toDraw.IsEmpty() && !IsHidden()) {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ aContext->Save();
+
+ bool needsGroup = opacity != 1.0 ||
+ effectiveOperator != CompositionOp::OP_OVER ||
+ aMaskLayer;
+ RefPtr<gfxContext> context = nullptr;
+ BasicLayerManager::PushedGroup group;
+ bool availableGroup = false;
+
+ if (needsGroup) {
+ availableGroup =
+ BasicManager()->PushGroupForLayer(aContext, this, toDraw, group);
+ if (availableGroup) {
+ context = group.mGroupTarget;
+ }
+ } else {
+ context = aContext;
+ }
+ if (context) {
+ SetAntialiasingFlags(this, context->GetDrawTarget());
+ aCallback(this, context, toDraw, toDraw, DrawRegionClip::NONE,
+ nsIntRegion(), aCallbackData);
+ }
+ if (needsGroup && availableGroup) {
+ BasicManager()->PopGroupForLayer(group);
+ }
+
+ aContext->Restore();
+ }
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ return;
+ }
+
+ if (BasicManager()->IsTransactionIncomplete())
+ return;
+
+ gfxRect clipExtents;
+ clipExtents = aContext->GetClipExtents();
+
+ // Pull out the mask surface and transform here, because the mask
+ // is internal to basic layers
+ AutoMoz2DMaskData mask;
+ SourceSurface* maskSurface = nullptr;
+ Matrix maskTransform;
+ if (GetMaskData(aMaskLayer, aContext->GetDeviceOffset(), &mask)) {
+ maskSurface = mask.GetSurface();
+ maskTransform = mask.GetTransform();
+ }
+
+ if (!IsHidden() && !clipExtents.IsEmpty()) {
+ mContentClient->DrawTo(this, aContext->GetDrawTarget(), opacity,
+ effectiveOperator,
+ maskSurface, &maskTransform);
+ }
+}
+
+void
+BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ if (!mContentClient) {
+ // This client will have a null Forwarder, which means it will not have
+ // a ContentHost on the other side.
+ mContentClient = new ContentClientBasic(mBackend);
+ }
+
+ if (!BasicManager()->IsRetained()) {
+ return;
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ uint32_t flags = 0;
+ if (BasicManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+ if (mDrawAtomically) {
+ flags |= RotatedContentBuffer::PAINT_NO_ROTATION;
+ }
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
+ if (target && target->IsValid()) {
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+ SetAntialiasingFlags(this, target);
+
+ RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ PaintBuffer(ctx,
+ state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
+ state.mDidSelfCopy,
+ state.mClip,
+ aCallback, aCallbackData);
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this));
+ Mutated();
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ } else {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+ }
+
+ // It's possible that state.mRegionToInvalidate is nonempty here,
+ // if we are shrinking the valid region to nothing. So use mRegionToDraw
+ // instead.
+ NS_WARNING_ASSERTION(
+ state.mRegionToDraw.IsEmpty(),
+ "No context when we have something to draw, resource exhaustion?");
+ }
+
+ for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = readbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+ RefPtr<DrawTarget> dt =
+ update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
+ update.mSequenceCounter);
+ if (dt) {
+ NS_ASSERTION(GetEffectiveOpacity() == 1.0, "Should only read back opaque layers");
+ NS_ASSERTION(!GetMaskLayer(), "Should only read back layers without masks");
+ dt->SetTransform(dt->GetTransform().PreTranslate(offset.x, offset.y));
+ mContentClient->DrawTo(this, dt, 1.0, CompositionOp::OP_OVER,
+ nullptr, nullptr);
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+}
+
+already_AddRefed<PaintedLayer>
+BasicLayerManager::CreatePaintedLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+
+ BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
+
+ if (mDefaultTarget) {
+ backend = mDefaultTarget->GetDrawTarget()->GetBackendType();
+ } else if (mType == BLM_WIDGET) {
+ backend = gfxPlatform::GetPlatform()->GetContentBackendFor(LayersBackend::LAYERS_BASIC);
+ }
+
+ RefPtr<PaintedLayer> layer = new BasicPaintedLayer(this, backend);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicPaintedLayer.h b/system/graphics/layers/basic/BasicPaintedLayer.h
new file mode 100644
index 000000000..e616948e1
--- /dev/null
+++ b/system/graphics/layers/basic/BasicPaintedLayer.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICPAINTEDLAYER_H
+#define GFX_BASICPAINTEDLAYER_H
+
+#include "Layers.h" // for PaintedLayer, LayerManager, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/layers/ContentClient.h" // for ContentClientBasic
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+class BasicPaintedLayer : public PaintedLayer, public BasicImplData {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) :
+ PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mContentClient(nullptr)
+ , mBackend(aBackend)
+ {
+ MOZ_COUNT_CTOR(BasicPaintedLayer);
+ }
+
+protected:
+ virtual ~BasicPaintedLayer()
+ {
+ MOZ_COUNT_DTOR(BasicPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ if (!BasicManager()->IsRetained()) {
+ // Don't do any snapping of our transform, since we're just going to
+ // draw straight through without intermediate buffers.
+ mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
+ if (gfxPoint(0,0) != mResidualTranslation) {
+ mResidualTranslation = gfxPoint(0,0);
+ mValidRegion.SetEmpty();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ return;
+ }
+ PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+protected:
+ virtual void
+ PaintBuffer(gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aExtendedRegionToDraw,
+ const nsIntRegion& aRegionToInvalidate,
+ bool aDidSelfCopy,
+ DrawRegionClip aClip,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+ aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
+ aClip, aRegionToInvalidate, aCallbackData);
+ // Everything that's visible has been validated. Do this instead of just
+ // OR-ing with aRegionToDraw, since that can lead to a very complex region
+ // here (OR doesn't automatically simplify to the simplest possible
+ // representation of a region.)
+ nsIntRegion tmp;
+ tmp.Or(mVisibleRegion.ToUnknownRegion(), aExtendedRegionToDraw);
+ mValidRegion.Or(mValidRegion, tmp);
+ }
+
+ RefPtr<ContentClientBasic> mContentClient;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/TextureClientX11.cpp b/system/graphics/layers/basic/TextureClientX11.cpp
new file mode 100644
index 000000000..c79ab1a53
--- /dev/null
+++ b/system/graphics/layers/basic/TextureClientX11.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "mozilla/layers/TextureClientX11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+#include "mozilla/X11Util.h"
+#include <X11/Xlib.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+X11TextureData::X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface)
+: mSize(aSize)
+, mFormat(aFormat)
+, mSurface(aSurface)
+, mClientDeallocation(aClientDeallocation)
+, mIsCrossProcess(aIsCrossProcess)
+{
+ MOZ_ASSERT(mSurface);
+}
+
+bool
+X11TextureData::Lock(OpenMode aMode)
+{
+ return true;
+}
+
+void
+X11TextureData::Unlock()
+{
+ if (mSurface && mIsCrossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+}
+
+void
+X11TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+X11TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return false;
+ }
+
+ if (!mClientDeallocation) {
+ // Pass to the host the responsibility of freeing the pixmap. ReleasePixmap means
+ // the underlying pixmap will not be deallocated in mSurface's destructor.
+ // ToSurfaceDescriptor is at most called once per TextureClient.
+ mSurface->ReleasePixmap();
+ }
+
+ aOutDescriptor = SurfaceDescriptorX11(mSurface);
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+X11TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return nullptr;
+ }
+
+ IntSize size = mSurface->GetSize();
+ RefPtr<gfx::DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+
+ return dt.forget();
+}
+
+bool
+X11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+
+ return true;
+}
+
+void
+X11TextureData::Deallocate(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+TextureData*
+X11TextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return X11TextureData::Create(mSize, mFormat, aFlags, aAllocator);
+}
+
+X11TextureData*
+X11TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
+ if (aSize.width <= 0 || aSize.height <= 0 ||
+ aSize.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ aSize.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ gfxDebug() << "Asking for X11 surface of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aFormat);
+ RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, imageFormat);
+ if (!surface || surface->GetType() != gfxSurfaceType::Xlib) {
+ NS_ERROR("creating Xlib surface failed!");
+ return nullptr;
+ }
+
+ gfxXlibSurface* xlibSurface = static_cast<gfxXlibSurface*>(surface.get());
+
+ bool crossProcess = !aAllocator->IsSameProcess();
+ X11TextureData* texture = new X11TextureData(aSize, aFormat,
+ !!(aFlags & TextureFlags::DEALLOCATE_CLIENT),
+ crossProcess,
+ xlibSurface);
+ if (crossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+
+ return texture;
+}
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/basic/TextureClientX11.h b/system/graphics/layers/basic/TextureClientX11.h
new file mode 100644
index 000000000..084538ea1
--- /dev/null
+++ b/system/graphics/layers/basic/TextureClientX11.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_X11_H
+#define MOZILLA_GFX_TEXTURECLIENT_X11_H
+
+#include "mozilla/layers/TextureClient.h"
+#include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureData : public TextureData
+{
+public:
+ static X11TextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+protected:
+ X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface);
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<gfxXlibSurface> mSurface;
+ bool mClientDeallocation;
+ bool mIsCrossProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/TextureHostBasic.cpp b/system/graphics/layers/basic/TextureHostBasic.cpp
new file mode 100644
index 000000000..1d671729e
--- /dev/null
+++ b/system/graphics/layers/basic/TextureHostBasic.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHostBasic.h"
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<TextureHost>
+CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/TextureHostBasic.h b/system/graphics/layers/basic/TextureHostBasic.h
new file mode 100644
index 000000000..e08624f5a
--- /dev/null
+++ b/system/graphics/layers/basic/TextureHostBasic.h
@@ -0,0 +1,34 @@
+
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+#define MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+
+#include "CompositableHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A texture source interface that can be used by the software Compositor.
+ */
+class TextureSourceBasic
+{
+public:
+ TextureSourceBasic() : mFromYCBCR(false) {}
+ virtual ~TextureSourceBasic() {}
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) = 0;
+ virtual void SetBufferTextureHost(BufferTextureHost* aTexture) {}
+ bool mFromYCBCR; // we to track sources from YCBCR so we can use a less accurate fast path for video
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTUREHOSTBASIC_H_
diff --git a/system/graphics/layers/basic/X11BasicCompositor.cpp b/system/graphics/layers/basic/X11BasicCompositor.cpp
new file mode 100644
index 000000000..6f21d15d2
--- /dev/null
+++ b/system/graphics/layers/basic/X11BasicCompositor.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11BasicCompositor.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+bool
+X11DataTextureSourceBasic::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ // Reallocate our internal X11 surface if we don't have a DrawTarget yet,
+ // or if we changed surface size or format since last update.
+ if (!mBufferDrawTarget ||
+ (aSurface->GetSize() != mBufferDrawTarget->GetSize()) ||
+ (aSurface->GetFormat() != mBufferDrawTarget->GetFormat())) {
+
+ RefPtr<gfxASurface> surf;
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aSurface->GetFormat());
+ Display *display = DefaultXDisplay();
+ Screen *screen = DefaultScreenOfDisplay(display);
+ XRenderPictFormat *xrenderFormat =
+ gfxXlibSurface::FindRenderFormat(display, imageFormat);
+
+ if (xrenderFormat) {
+ surf = gfxXlibSurface::Create(screen, xrenderFormat,
+ aSurface->GetSize());
+ }
+
+ if (!surf) {
+ NS_WARNING("Couldn't create native surface, fallback to image surface");
+ surf = new gfxImageSurface(aSurface->GetSize(), imageFormat);
+ }
+
+ mBufferDrawTarget = gfxPlatform::GetPlatform()->
+ CreateDrawTargetForSurface(surf, aSurface->GetSize());
+ }
+
+ // Image contents have changed, upload to our DrawTarget
+ // If aDestRegion is null, means we're updating the whole surface
+ // Note : Incremental update with a source offset is only used on Mac.
+ NS_ASSERTION(!aSrcOffset, "SrcOffset should not be used with linux OMTC basic");
+
+ if (aDestRegion) {
+ for (auto iter = aDestRegion->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ IntRect srcRect(rect.x, rect.y, rect.width, rect.height);
+ IntPoint dstPoint(rect.x, rect.y);
+
+ // We're uploading regions to our buffer, so let's just copy contents over
+ mBufferDrawTarget->CopySurface(aSurface, srcRect, dstPoint);
+ }
+ } else {
+ // We're uploading the whole buffer, so let's just copy the full surface
+ IntSize size = aSurface->GetSize();
+ mBufferDrawTarget->CopySurface(aSurface, IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ }
+
+ return true;
+}
+
+TextureSourceBasic*
+X11DataTextureSourceBasic::AsSourceBasic()
+{
+ return this;
+}
+
+IntSize
+X11DataTextureSourceBasic::GetSize() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the size of an uninitialized TextureSource");
+ return IntSize(0, 0);
+ } else {
+ return mBufferDrawTarget->GetSize();
+ }
+}
+
+gfx::SurfaceFormat
+X11DataTextureSourceBasic::GetFormat() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the format of an uninitialized TextureSource");
+ return gfx::SurfaceFormat::UNKNOWN;
+ } else {
+ return mBufferDrawTarget->GetFormat();
+ }
+}
+
+SourceSurface*
+X11DataTextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ RefPtr<gfx::SourceSurface> surface;
+ if (mBufferDrawTarget) {
+ surface = mBufferDrawTarget->Snapshot();
+ return surface.get();
+ } else {
+ return nullptr;
+ }
+}
+
+void
+X11DataTextureSourceBasic::DeallocateDeviceData()
+{
+ mBufferDrawTarget = nullptr;
+}
+
+already_AddRefed<DataTextureSource>
+X11BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSource> result =
+ new X11DataTextureSourceBasic();
+ return result.forget();
+}
+
+void
+X11BasicCompositor::EndFrame()
+{
+ BasicCompositor::EndFrame();
+ XFlush(DefaultXDisplay());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/X11BasicCompositor.h b/system/graphics/layers/basic/X11BasicCompositor.h
new file mode 100644
index 000000000..2504e3f67
--- /dev/null
+++ b/system/graphics/layers/basic/X11BasicCompositor.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11BASICCOMPOSITOR_H
+#define MOZILLA_GFX_X11BASICCOMPOSITOR_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "gfxXlibSurface.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Image-backed surfaces.
+class X11DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ X11DataTextureSourceBasic() {};
+
+ virtual const char* Name() const override { return "X11DataTextureSourceBasic"; }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ virtual TextureSourceBasic* AsSourceBasic() override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+private:
+ // We are going to buffer layer content on this xlib draw target
+ RefPtr<mozilla::gfx::DrawTarget> mBufferDrawTarget;
+};
+
+class X11BasicCompositor : public BasicCompositor
+{
+public:
+ explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : BasicCompositor(aParent, aWidget)
+ {}
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
+ virtual void EndFrame() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
diff --git a/system/graphics/layers/basic/X11TextureSourceBasic.cpp b/system/graphics/layers/basic/X11TextureSourceBasic.cpp
new file mode 100644
index 000000000..24f21e5fc
--- /dev/null
+++ b/system/graphics/layers/basic/X11TextureSourceBasic.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11TextureSourceBasic.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceBasic::X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface)
+ : mCompositor(aCompositor),
+ mSurface(aSurface)
+{
+}
+
+IntSize
+X11TextureSourceBasic::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+SurfaceFormat
+X11TextureSourceBasic::GetFormat() const
+{
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+SourceSurface*
+X11TextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ if (!mSourceSurface) {
+ mSourceSurface =
+ Factory::CreateSourceSurfaceForCairoSurface(mSurface->CairoSurface(),
+ GetSize(), GetFormat());
+ }
+ return mSourceSurface;
+}
+
+void
+X11TextureSourceBasic::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertBasicCompositor(aCompositor);
+}
+
+SurfaceFormat
+X11TextureSourceBasic::ContentTypeToSurfaceFormat(gfxContentType aType)
+{
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::B8G8R8X8;
+ case gfxContentType::ALPHA:
+ return SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::B8G8R8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+}
+}
diff --git a/system/graphics/layers/basic/X11TextureSourceBasic.h b/system/graphics/layers/basic/X11TextureSourceBasic.h
new file mode 100644
index 000000000..f813560e0
--- /dev/null
+++ b/system/graphics/layers/basic/X11TextureSourceBasic.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+#define MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceBasic
+ : public TextureSourceBasic
+ , public X11TextureSource
+{
+public:
+ X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface);
+
+ virtual const char* Name() const override { return "X11TextureSourceBasic"; }
+
+ virtual X11TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override { }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void Updated() override { }
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
diff --git a/system/graphics/layers/client/CanvasClient.cpp b/system/graphics/layers/client/CanvasClient.cpp
new file mode 100644
index 000000000..40513984e
--- /dev/null
+++ b/system/graphics/layers/client/CanvasClient.cpp
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasClient.h"
+
+#include "ClientCanvasLayer.h" // for ClientCanvasLayer
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "ScopedGLHelpers.h"
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "nsDebug.h" // for printf_stderr, NS_ASSERTION
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TextureClientSharedSurface.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/* static */ already_AddRefed<CanvasClient>
+CanvasClient::CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ switch (aType) {
+ case CanvasClientTypeShSurf:
+ return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
+ case CanvasClientAsync:
+ return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
+ default:
+ return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
+ break;
+ }
+}
+
+void
+CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ if (!GetForwarder() || !mLayer || !aRenderer ||
+ !aRenderer->GetCanvasClient()) {
+ return;
+ }
+
+ uint64_t asyncID = aRenderer->GetCanvasClientAsyncID();
+ if (asyncID == 0 || mAsyncID == asyncID) {
+ return;
+ }
+
+ static_cast<ShadowLayerForwarder*>(GetForwarder())
+ ->AttachAsyncCompositable(asyncID, mLayer);
+ mAsyncID = asyncID;
+}
+
+void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+
+ if (!aTexture->IsSharedWithCompositor()) {
+ if (!AddTextureClient(aTexture)) {
+ return;
+ }
+ }
+
+ mBackBuffer = nullptr;
+ mFrontBuffer = nullptr;
+ mBufferProviderTexture = aTexture;
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = aTexture;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+ t->mFrameID = mFrameID;
+
+ GetForwarder()->UseTextures(this, textures);
+ aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
+CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ mBufferProviderTexture = nullptr;
+
+ AutoRemoveTexture autoRemove(this);
+ if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
+ autoRemove.mTexture = mBackBuffer;
+ mBackBuffer = nullptr;
+ }
+
+ bool bufferCreated = false;
+ if (!mBackBuffer) {
+ bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
+ gfxContentType contentType = isOpaque
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+ gfx::SurfaceFormat surfaceFormat
+ = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
+ if (!mBackBuffer) {
+ NS_WARNING("Failed to allocate the TextureClient");
+ return;
+ }
+ mBackBuffer->EnableReadLock();
+ MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
+
+ bufferCreated = true;
+ }
+
+ bool updated = false;
+ {
+ TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
+ if (target) {
+ if (!aLayer->UpdateTarget(target)) {
+ NS_WARNING("Failed to copy the canvas into a TextureClient.");
+ return;
+ }
+ updated = true;
+ }
+ }
+
+ if (bufferCreated && !AddTextureClient(mBackBuffer)) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ if (updated) {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mBackBuffer;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
+ t->mFrameID = mFrameID;
+ GetForwarder()->UseTextures(this, textures);
+ mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ mBackBuffer.swap(mFrontBuffer);
+}
+
+already_AddRefed<TextureClient>
+CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer)
+{
+ if (aLayer->IsGLLayer()) {
+ // We want a cairo backend here as we don't want to be copying into
+ // an accelerated backend and we like LockBits to work. This is currently
+ // the most effective way to make this work.
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, BackendType::CAIRO,
+ mTextureFlags | aFlags);
+ }
+
+#ifdef XP_WIN
+ return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags);
+#else
+ // XXX - We should use CreateTextureClientForDrawing, but we first need
+ // to use double buffering.
+ gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, backend,
+ mTextureFlags | aFlags);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+
+CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+{ }
+
+CanvasClientSharedSurface::~CanvasClientSharedSurface()
+{
+ ClearSurfaces();
+}
+
+////////////////////////////////////////
+// Readback
+
+// For formats compatible with R8G8B8A8.
+static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
+ // [RR, GG, BB, AA]
+ Swap(pixel[0], pixel[2]);
+}
+
+class TexClientFactory
+{
+ CompositableForwarder* const mAllocator;
+ const bool mHasAlpha;
+ const gfx::IntSize mSize;
+ const gfx::BackendType mBackendType;
+ const TextureFlags mBaseTexFlags;
+ const LayersBackend mLayersBackend;
+
+public:
+ TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
+ const gfx::IntSize& size, gfx::BackendType backendType,
+ TextureFlags baseTexFlags, LayersBackend layersBackend)
+ : mAllocator(allocator)
+ , mHasAlpha(hasAlpha)
+ , mSize(size)
+ , mBackendType(backendType)
+ , mBaseTexFlags(baseTexFlags)
+ , mLayersBackend(layersBackend)
+ {
+ }
+
+protected:
+ already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
+ return TextureClient::CreateForRawBufferAccess(mAllocator, format,
+ mSize, mBackendType,
+ mBaseTexFlags);
+ }
+
+public:
+ already_AddRefed<TextureClient> CreateB8G8R8AX8() {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ return Create(format);
+ }
+
+ already_AddRefed<TextureClient> CreateR8G8B8AX8() {
+ RefPtr<TextureClient> ret;
+
+ bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
+ if (!areRGBAFormatsBroken) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ ret = Create(format);
+ }
+
+ if (!ret) {
+ ret = CreateB8G8R8AX8();
+ if (ret) {
+ ret->AddFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return ret.forget();
+ }
+};
+
+static already_AddRefed<TextureClient>
+TexClientFromReadback(SharedSurface* src, CompositableForwarder* allocator,
+ TextureFlags baseFlags, LayersBackend layersBackend)
+{
+ auto backendType = gfx::BackendType::CAIRO;
+ TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
+ baseFlags, layersBackend);
+
+ RefPtr<TextureClient> texClient;
+
+ {
+ gl::ScopedReadbackFB autoReadback(src);
+
+ // We have a source FB, now we need a format.
+ GLenum destFormat = LOCAL_GL_BGRA;
+ GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
+ GLenum readFormat;
+ GLenum readType;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ auto gl = src->mGL;
+ GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
+
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA ||
+ readFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // With a format and type, we can create texClient.
+ if (readFormat == LOCAL_GL_BGRA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // 0xAARRGGBB
+ // In Lendian: [BB, GG, RR, AA]
+ texClient = factory.CreateB8G8R8AX8();
+
+ } else if (readFormat == LOCAL_GL_RGBA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // [RR, GG, BB, AA]
+ texClient = factory.CreateR8G8B8AX8();
+ } else {
+ MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
+ }
+
+ MOZ_ASSERT(texClient);
+ if (!texClient)
+ return nullptr;
+
+ // With a texClient, we can lock for writing.
+ TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
+ DebugOnly<bool> succeeded = autoLock.Succeeded();
+ MOZ_ASSERT(succeeded, "texture should have locked");
+
+ MappedTextureData mapped;
+ texClient->BorrowMappedData(mapped);
+
+ // ReadPixels from the current FB into mapped.data.
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+
+ {
+ ScopedPackState scopedPackState(gl);
+
+ MOZ_ASSERT(mapped.stride/4 == mapped.size.width);
+ gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data);
+ }
+
+ // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
+ // RB_SWAPPED doesn't work with Basic. (bug ???????)
+ // RB_SWAPPED doesn't work with D3D9. (bug ???????)
+ bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
+ layersBackend == LayersBackend::LAYERS_D3D9 ||
+ layersBackend == LayersBackend::LAYERS_D3D11;
+ if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
+ layersNeedsManualSwap)
+ {
+ size_t pixels = width * height;
+ uint8_t* itr = mapped.data;
+ for (size_t i = 0; i < pixels; i++) {
+ SwapRB_R8G8B8A8(itr);
+ itr += 4;
+ }
+
+ texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return texClient.forget();
+}
+
+////////////////////////////////////////
+
+static already_AddRefed<SharedSurfaceTextureClient>
+CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
+{
+ RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
+ if (!dest) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* destSurf = dest->Surf();
+
+ destSurf->ProducerAcquire();
+ SharedSurface::ProdCopy(src, dest->Surf(), factory);
+ destSurf->ProducerRelease();
+
+ return dest.forget();
+}
+
+void
+CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ Renderer renderer;
+ renderer.construct<ClientCanvasLayer*>(aLayer);
+ UpdateRenderer(aSize, renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ Renderer renderer;
+ renderer.construct<AsyncCanvasRenderer*>(aRenderer);
+ UpdateRenderer(aRenderer->GetSize(), renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer)
+{
+ GLContext* gl = nullptr;
+ ClientCanvasLayer* layer = nullptr;
+ AsyncCanvasRenderer* asyncRenderer = nullptr;
+ if (aRenderer.constructed<ClientCanvasLayer*>()) {
+ layer = aRenderer.ref<ClientCanvasLayer*>();
+ gl = layer->mGLContext;
+ } else {
+ asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
+ gl = asyncRenderer->mGLContext;
+ }
+ gl->MakeCurrent();
+
+ RefPtr<TextureClient> newFront;
+
+ if (layer && layer->mGLFrontbuffer) {
+ mShSurfClient = CloneSurface(layer->mGLFrontbuffer.get(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ gfxCriticalError() << "Invalid canvas front buffer";
+ return;
+ }
+ } else if (layer && layer->mIsMirror) {
+ mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ return;
+ }
+ } else {
+ mShSurfClient = gl->Screen()->Front();
+ if (mShSurfClient && mShSurfClient->GetAllocator() &&
+ mShSurfClient->GetAllocator() != GetForwarder()->GetTextureForwarder()) {
+ mShSurfClient = CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory());
+ }
+ if (!mShSurfClient) {
+ return;
+ }
+ }
+ MOZ_ASSERT(mShSurfClient);
+
+ newFront = mShSurfClient;
+
+ SharedSurface* surf = mShSurfClient->Surf();
+
+ // Readback if needed.
+ mReadbackClient = nullptr;
+
+ auto forwarder = GetForwarder();
+
+ bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
+ if (needsReadback) {
+ TextureFlags flags = TextureFlags::IMMUTABLE;
+
+ CompositableForwarder* shadowForwarder = nullptr;
+ if (layer) {
+ flags |= layer->Flags();
+ shadowForwarder = layer->ClientManager()->AsShadowForwarder();
+ } else {
+ MOZ_ASSERT(asyncRenderer);
+ flags |= mTextureFlags;
+ shadowForwarder = GetForwarder();
+ }
+
+ auto layersBackend = shadowForwarder->GetCompositorBackendType();
+ mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend);
+
+ newFront = mReadbackClient;
+ } else {
+ mReadbackClient = nullptr;
+ }
+
+ if (asyncRenderer) {
+ // If surface type is Basic, above codes will readback
+ // the GLContext to mReadbackClient in order to send frame to
+ // compositor. We copy from this TextureClient directly by
+ // calling CopyFromTextureClient().
+ // Therefore, if main-thread want the content of GLContext,
+ // it doesn't have to readback from GLContext again.
+ //
+ // Otherwise, if surface type isn't Basic, we will read from
+ // SharedSurface directly from main-thread. We still pass
+ // mReadbackClient which is nullptr here to tell
+ // AsyncCanvasRenderer reset some properties.
+ asyncRenderer->CopyFromTextureClient(mReadbackClient);
+ }
+
+ MOZ_ASSERT(newFront);
+ if (!newFront) {
+ // May happen in a release build in case of memory pressure.
+ gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
+ return;
+ }
+
+ mNewFront = newFront;
+}
+
+void
+CanvasClientSharedSurface::Updated()
+{
+ if (!mNewFront) {
+ return;
+ }
+
+ auto forwarder = GetForwarder();
+
+ mFront = mNewFront;
+ mNewFront = nullptr;
+
+ // Add the new TexClient.
+ MOZ_ALWAYS_TRUE( AddTextureClient(mFront) );
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mFront;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
+ t->mFrameID = mFrameID;
+ forwarder->UseTextures(this, textures);
+}
+
+void
+CanvasClientSharedSurface::OnDetach() {
+ ClearSurfaces();
+}
+
+void
+CanvasClientSharedSurface::ClearSurfaces()
+{
+ if (mFront) {
+ mFront->CancelWaitForRecycle();
+ }
+ mFront = nullptr;
+ mNewFront = nullptr;
+ mShSurfClient = nullptr;
+ mReadbackClient = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/CanvasClient.h b/system/graphics/layers/client/CanvasClient.h
new file mode 100644
index 000000000..cd88d02ab
--- /dev/null
+++ b/system/graphics/layers/client/CanvasClient.h
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_CANVASCLIENT_H
+#define MOZILLA_GFX_CANVASCLIENT_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/PersistentBufferProvider.h"
+
+// Fix X11 header brain damage that conflicts with MaybeOneOf::None
+#undef None
+#include "mozilla/MaybeOneOf.h"
+
+#include "mozilla/mozalloc.h" // for operator delete
+
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+
+namespace mozilla {
+namespace layers {
+
+class AsyncCanvasRenderer;
+class ClientCanvasLayer;
+class CompositableForwarder;
+class ShadowableLayer;
+class SharedSurfaceTextureClient;
+
+/**
+ * Compositable client for 2d and webgl canvas.
+ */
+class CanvasClient : public CompositableClient
+{
+public:
+ typedef MaybeOneOf<ClientCanvasLayer*, AsyncCanvasRenderer*> Renderer;
+
+ /**
+ * Creates, configures, and returns a new canvas client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ enum CanvasClientType {
+ CanvasClientSurface,
+ CanvasClientGLContext,
+ CanvasClientTypeShSurf,
+ CanvasClientAsync, // webgl on workers
+ };
+ static already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ CanvasClient(CompositableForwarder* aFwd, TextureFlags aFlags)
+ : CompositableClient(aFwd, aFlags)
+ , mFrameID(0)
+ {
+ mTextureFlags = aFlags;
+ }
+
+ virtual ~CanvasClient() {}
+
+ virtual void Clear() {};
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) = 0;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ ++mFrameID;
+ return CompositableClient::AddTextureClient(aTexture);
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
+
+ virtual void UpdateFromTexture(TextureClient* aTexture) {}
+
+ virtual void Updated() { }
+
+protected:
+ int32_t mFrameID;
+};
+
+// Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
+class CanvasClient2D : public CanvasClient
+{
+public:
+ CanvasClient2D(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE, mTextureFlags);
+ }
+
+ virtual void Clear() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
+
+ virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ return CanvasClient::AddTextureClient(aTexture);
+ }
+
+ virtual void OnDetach() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+private:
+ already_AddRefed<TextureClient>
+ CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer);
+
+ RefPtr<TextureClient> mBackBuffer;
+ RefPtr<TextureClient> mFrontBuffer;
+ // We store this texture separately to make sure it is not written into
+ // in Update() if for some silly reason we end up alternating between
+ // UpdateFromTexture and Update.
+ // This code is begging for a cleanup. The situation described above should
+ // not be made possible.
+ RefPtr<TextureClient> mBufferProviderTexture;
+};
+
+// Used for GL canvases where we don't need to do any readback, i.e., with a
+// GL backend.
+class CanvasClientSharedSurface : public CanvasClient
+{
+private:
+ RefPtr<SharedSurfaceTextureClient> mShSurfClient;
+ RefPtr<TextureClient> mReadbackClient;
+ RefPtr<TextureClient> mFront;
+ RefPtr<TextureClient> mNewFront;
+
+ void ClearSurfaces();
+
+public:
+ CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags);
+
+ ~CanvasClientSharedSurface();
+
+ virtual TextureInfo GetTextureInfo() const override {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Clear() override {
+ ClearSurfaces();
+ }
+
+ virtual void Update(gfx::IntSize aSize,
+ ClientCanvasLayer* aLayer) override;
+ void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ virtual void Updated() override;
+
+ virtual void OnDetach() override;
+};
+
+/**
+ * Used for OMT<canvas> uploads using the image bridge protocol.
+ * Actual CanvasClient is on the ImageBridgeChild thread, so we
+ * only forward its AsyncID here
+ */
+class CanvasClientBridge final : public CanvasClient
+{
+public:
+ CanvasClientBridge(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ , mAsyncID(0)
+ , mLayer(nullptr)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override
+ {
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ void SetLayer(ShadowableLayer* aLayer)
+ {
+ mLayer = aLayer;
+ }
+
+protected:
+ uint64_t mAsyncID;
+ ShadowableLayer* mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/ClientCanvasLayer.cpp b/system/graphics/layers/client/ClientCanvasLayer.cpp
new file mode 100644
index 000000000..7e9885ef1
--- /dev/null
+++ b/system/graphics/layers/client/ClientCanvasLayer.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientCanvasLayer.h"
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "SharedSurfaceEGL.h" // for SurfaceFactory_EGLImage
+#include "SharedSurfaceGL.h" // for SurfaceFactory_GLTexture, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "gfxPrefs.h" // for WebGLForceLayersReadback
+#include "gfxUtils.h"
+#include "TextureClientSharedSurface.h"
+
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+ClientCanvasLayer::~ClientCanvasLayer()
+{
+ MOZ_COUNT_DTOR(ClientCanvasLayer);
+ if (mCanvasClient) {
+ mCanvasClient->OnDetach();
+ mCanvasClient = nullptr;
+ }
+}
+
+void
+ClientCanvasLayer::Initialize(const Data& aData)
+{
+ CopyableCanvasLayer::Initialize(aData);
+
+ mCanvasClient = nullptr;
+
+ if (!mGLContext)
+ return;
+
+ GLScreenBuffer* screen = mGLContext->Screen();
+
+ SurfaceCaps caps;
+ if (mGLFrontbuffer) {
+ // The screen caps are irrelevant if we're using a separate frontbuffer.
+ caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA()
+ : SurfaceCaps::ForRGB();
+ } else {
+ MOZ_ASSERT(screen);
+ caps = screen->mCaps;
+ }
+ MOZ_ASSERT(caps.alpha == aData.mHasAlpha);
+
+ auto forwarder = ClientManager()->AsShadowForwarder();
+
+ mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+ if (!aData.mIsGLAlphaPremult) {
+ mFlags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags);
+
+ if (mGLFrontbuffer || aData.mIsMirror) {
+ // We're using a source other than the one in the default screen.
+ // (SkiaGL)
+ mFactory = Move(factory);
+ if (!mFactory) {
+ // Absolutely must have a factory here, so create a basic one
+ mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags);
+ }
+ } else {
+ if (factory)
+ screen->Morph(Move(factory));
+ }
+}
+
+void
+ClientCanvasLayer::RenderLayer()
+{
+ PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ RenderMaskLayers(this);
+
+ if (!mCanvasClient) {
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mOriginPos == gl::OriginPos::BottomLeft) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ if (!mIsAlphaPremultiplied) {
+ flags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mCanvasClient) {
+ return;
+ }
+ if (HasShadow()) {
+ if (mAsyncRenderer) {
+ static_cast<CanvasClientBridge*>(mCanvasClient.get())->SetLayer(this);
+ } else {
+ mCanvasClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this);
+ }
+ }
+ }
+
+ if (mCanvasClient && mAsyncRenderer) {
+ mCanvasClient->UpdateAsync(mAsyncRenderer);
+ }
+
+ if (!IsDirty()) {
+ return;
+ }
+ Painted();
+
+ FirePreTransactionCallback();
+ if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+ if (!mBufferProvider->SetForwarder(ClientManager()->AsShadowForwarder())) {
+ gfxCriticalNote << "BufferProvider::SetForwarder failed";
+ return;
+ }
+ mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+ } else {
+ mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+ }
+
+ FireDidTransactionCallback();
+
+ ClientManager()->Hold(this);
+ mCanvasClient->Updated();
+}
+
+bool
+ClientCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
+{
+ MOZ_ASSERT(aDestTarget);
+ if (!aDestTarget) {
+ return false;
+ }
+
+ RefPtr<SourceSurface> surface;
+
+ if (!mGLContext) {
+ AutoReturnSnapshot autoReturn;
+
+ if (mAsyncRenderer) {
+ surface = mAsyncRenderer->GetSurface();
+ } else if (mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ autoReturn.mSnapshot = &surface;
+ autoReturn.mBufferProvider = mBufferProvider;
+ }
+
+ MOZ_ASSERT(surface);
+ if (!surface) {
+ return false;
+ }
+
+ aDestTarget->CopySurface(surface,
+ IntRect(0, 0, mBounds.width, mBounds.height),
+ IntPoint(0, 0));
+ return true;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return false;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ // Try to read back directly into aDestTarget's output buffer
+ uint8_t* destData;
+ IntSize destSize;
+ int32_t destStride;
+ SurfaceFormat destFormat;
+ if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
+ if (destSize == readSize && destFormat == format) {
+ RefPtr<DataSourceSurface> data =
+ Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
+ mGLContext->Readback(frontbuffer, data);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(data, data);
+ }
+ aDestTarget->ReleaseBits(destData);
+ return true;
+ }
+ aDestTarget->ReleaseBits(destData);
+ }
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return false;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+
+ aDestTarget->CopySurface(resultSurf,
+ IntRect(0, 0, readSize.width, readSize.height),
+ IntPoint(0, 0));
+
+ return true;
+}
+
+CanvasClient::CanvasClientType
+ClientCanvasLayer::GetCanvasClientType()
+{
+ if (mAsyncRenderer) {
+ return CanvasClient::CanvasClientAsync;
+ }
+
+ if (mGLContext) {
+ return CanvasClient::CanvasClientTypeShSurf;
+ }
+ return CanvasClient::CanvasClientSurface;
+}
+
+already_AddRefed<CanvasLayer>
+ClientLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientCanvasLayer> layer =
+ new ClientCanvasLayer(this);
+ CREATE_SHADOW(Canvas);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientCanvasLayer.h b/system/graphics/layers/client/ClientCanvasLayer.h
new file mode 100644
index 000000000..9a655727c
--- /dev/null
+++ b/system/graphics/layers/client/ClientCanvasLayer.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTCANVASLAYER_H
+#define GFX_CLIENTCANVASLAYER_H
+
+#include "CanvasClient.h" // for CanvasClient, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayersMessages.h" // for CanvasLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gl {
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+
+class ClientCanvasLayer : public CopyableCanvasLayer,
+ public ClientLayer
+{
+ typedef CanvasClient::CanvasClientType CanvasClientType;
+public:
+ explicit ClientCanvasLayer(ClientLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientCanvasLayer);
+ }
+
+protected:
+ virtual ~ClientCanvasLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Initialize(const Data& aData) override;
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->Clear();
+ }
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = CanvasLayerAttributes(mSamplingFilter, mBounds);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ mCanvasClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mCanvasClient;
+ }
+
+ const TextureFlags& Flags() const { return mFlags; }
+
+protected:
+
+ bool UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CanvasClientType GetCanvasClientType();
+
+ RefPtr<CanvasClient> mCanvasClient;
+
+ UniquePtr<gl::SurfaceFactory> mFactory;
+
+ TextureFlags mFlags;
+
+ friend class CanvasClient2D;
+ friend class CanvasClientSharedSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/ClientColorLayer.cpp b/system/graphics/layers/client/ClientColorLayer.cpp
new file mode 100644
index 000000000..aa086f35a
--- /dev/null
+++ b/system/graphics/layers/client/ClientColorLayer.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/layers/LayersMessages.h" // for ColorLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientColorLayer : public ColorLayer,
+ public ClientLayer {
+public:
+ explicit ClientColorLayer(ClientLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientColorLayer);
+ }
+
+protected:
+ virtual ~ClientColorLayer()
+ {
+ MOZ_COUNT_DTOR(ClientColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer()
+ {
+ RenderMaskLayers(this);
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+ {
+ aAttrs = ColorLayerAttributes(GetColor(), GetBounds());
+ }
+
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+ClientLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientColorLayer> layer =
+ new ClientColorLayer(this);
+ CREATE_SHADOW(Color);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientContainerLayer.cpp b/system/graphics/layers/client/ClientContainerLayer.cpp
new file mode 100644
index 000000000..f72854af9
--- /dev/null
+++ b/system/graphics/layers/client/ClientContainerLayer.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientContainerLayer.h"
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<ContainerLayer>
+ClientLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientContainerLayer> layer =
+ new ClientContainerLayer(this);
+ CREATE_SHADOW(Container);
+ return layer.forget();
+}
+
+already_AddRefed<RefLayer>
+ClientLayerManager::CreateRefLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientRefLayer> layer =
+ new ClientRefLayer(this);
+ CREATE_SHADOW(Ref);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientContainerLayer.h b/system/graphics/layers/client/ClientContainerLayer.h
new file mode 100644
index 000000000..de10a55b4
--- /dev/null
+++ b/system/graphics/layers/client/ClientContainerLayer.h
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTCONTAINERLAYER_H
+#define GFX_CLIENTCONTAINERLAYER_H
+
+#include <stdint.h> // for uint32_t
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include "ReadbackProcessor.h"
+#include "ClientPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+
+class ClientContainerLayer : public ContainerLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientContainerLayer(ClientLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+
+protected:
+ virtual ~ClientContainerLayer()
+ {
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(ClientContainerLayer);
+ }
+
+public:
+ virtual void RenderLayer() override
+ {
+ RenderMaskLayers(this);
+
+ DefaultComputeSupportsComponentAlphaChildren();
+
+ AutoTArray<Layer*, 12> children;
+ SortChildrenBy3DZOrder(children);
+
+ ReadbackProcessor readback;
+ readback.BuildUpdates(this);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* child = children.ElementAt(i);
+
+ ToClientLayer(child)->RenderLayerWithReadback(&readback);
+
+ if (!ClientManager()->GetRepeatTransaction() &&
+ !child->GetInvalidRegion().IsEmpty()) {
+ child->Mutated();
+ }
+ }
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if(!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+
+ if (!ContainerLayer::InsertAfter(aChild, aAfter)) {
+ return false;
+ }
+
+ ClientManager()->AsShadowForwarder()->InsertAfter(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ // hold on to aChild before we remove it!
+ ShadowableLayer *heldChild = ClientManager()->Hold(aChild);
+ if (!ContainerLayer::RemoveChild(aChild)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this), heldChild);
+ return true;
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ if (!ContainerLayer::RepositionChild(aChild, aAfter)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RepositionChild(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+class ClientRefLayer : public RefLayer,
+ public ClientLayer {
+public:
+ explicit ClientRefLayer(ClientLayerManager* aManager) :
+ RefLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientRefLayer);
+ }
+
+protected:
+ virtual ~ClientRefLayer()
+ {
+ MOZ_COUNT_DTOR(ClientRefLayer);
+ }
+
+public:
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() { }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/ClientImageLayer.cpp b/system/graphics/layers/client/ClientImageLayer.cpp
new file mode 100644
index 000000000..8703f77a5
--- /dev/null
+++ b/system/graphics/layers/client/ClientImageLayer.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageClient.h" // for ImageClient, etc
+#include "mozilla/layers/LayersMessages.h" // for ImageLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientImageLayer : public ImageLayer,
+ public ClientLayer {
+public:
+ explicit ClientImageLayer(ClientLayerManager* aLayerManager)
+ : ImageLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ , mImageClientTypeContainer(CompositableType::UNKNOWN)
+ {
+ MOZ_COUNT_CTOR(ClientImageLayer);
+ }
+
+protected:
+ virtual ~ClientImageLayer()
+ {
+ DestroyBackBuffer();
+ MOZ_COUNT_DTOR(ClientImageLayer);
+ }
+
+ virtual void SetContainer(ImageContainer* aContainer) override
+ {
+ ImageLayer::SetContainer(aContainer);
+ mImageClientTypeContainer = CompositableType::UNKNOWN;
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mImageClient) {
+ mImageClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = ImageLayerAttributes(mSamplingFilter, mScaleToSize, mScaleMode);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ DestroyBackBuffer();
+ ClientLayer::Disconnect();
+ }
+
+ void DestroyBackBuffer()
+ {
+ if (mImageClient) {
+ mImageClient->SetLayer(nullptr);
+ mImageClient->OnDetach();
+ mImageClient = nullptr;
+ }
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mImageClient;
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CompositableType GetImageClientType()
+ {
+ if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
+ return mImageClientTypeContainer;
+ }
+
+ if (mContainer->IsAsync()) {
+ mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
+ return mImageClientTypeContainer;
+ }
+
+ AutoLockImage autoLock(mContainer);
+
+ mImageClientTypeContainer = autoLock.HasImage()
+ ? CompositableType::IMAGE : CompositableType::UNKNOWN;
+ return mImageClientTypeContainer;
+ }
+
+ RefPtr<ImageClient> mImageClient;
+ CompositableType mImageClientTypeContainer;
+};
+
+void
+ClientImageLayer::RenderLayer()
+{
+ RenderMaskLayers(this);
+
+ if (!mContainer) {
+ return;
+ }
+
+ if (!mImageClient ||
+ !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ CompositableType type = GetImageClientType();
+ if (type == CompositableType::UNKNOWN) {
+ return;
+ }
+ TextureFlags flags = TextureFlags::DEFAULT;
+ mImageClient = ImageClient::CreateImageClient(type,
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mImageClient) {
+ return;
+ }
+ mImageClient->SetLayer(this);
+ if (HasShadow() && !mContainer->IsAsync()) {
+ mImageClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
+ }
+ if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ return;
+ }
+ }
+ ClientManager()->Hold(this);
+}
+
+already_AddRefed<ImageLayer>
+ClientLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientImageLayer> layer =
+ new ClientImageLayer(this);
+ CREATE_SHADOW(Image);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientLayerManager.cpp b/system/graphics/layers/client/ClientLayerManager.cpp
new file mode 100644
index 000000000..ddca3ec3c
--- /dev/null
+++ b/system/graphics/layers/client/ClientLayerManager.cpp
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h"
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "gfxPrefs.h" // for gfxPrefs::LayersTile...
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Hal.h"
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/dom/TabChild.h" // for TabChild
+#include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/ContentClient.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayerChild.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "ClientReadbackLayer.h" // for ClientReadbackLayer
+#include "nsAString.h"
+#include "nsDisplayList.h"
+#include "nsIWidgetListener.h"
+#include "nsTArray.h" // for AutoTArray
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TiledLayerBuffer.h"
+#include "mozilla/dom/WindowBinding.h" // for Overfill Callback
+#include "FrameLayerBuilder.h" // for FrameLayerbuilder
+#ifdef XP_WIN
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientLayerManager::MemoryPressureObserver::Destroy()
+{
+ UnregisterMemoryPressureEvent();
+ mClientLayerManager = nullptr;
+}
+
+NS_IMETHODIMP
+ClientLayerManager::MemoryPressureObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aSomeData)
+{
+ if (!mClientLayerManager || strcmp(aTopic, "memory-pressure")) {
+ return NS_OK;
+ }
+
+ mClientLayerManager->HandleMemoryPressure();
+ return NS_OK;
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::RegisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ MOZ_ASSERT(observerService);
+
+ if (observerService) {
+ observerService->AddObserver(this, "memory-pressure", false);
+ }
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::UnregisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ observerService->RemoveObserver(this, "memory-pressure");
+ }
+}
+
+NS_IMPL_ISUPPORTS(ClientLayerManager::MemoryPressureObserver, nsIObserver)
+
+ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mLatestTransactionId(0)
+ , mLastPaintTime(TimeDuration::Forever())
+ , mTargetRotation(ROTATION_0)
+ , mRepeatTransaction(false)
+ , mIsRepeatTransaction(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+ , mNeedsComposite(false)
+ , mPaintSequenceNumber(0)
+ , mForwarder(new ShadowLayerForwarder(this))
+ , mDeviceCounter(gfxPlatform::GetPlatform()->GetDeviceCounter())
+{
+ MOZ_COUNT_CTOR(ClientLayerManager);
+ mMemoryPressureObserver = new MemoryPressureObserver(this);
+}
+
+
+ClientLayerManager::~ClientLayerManager()
+{
+ mMemoryPressureObserver->Destroy();
+ ClearCachedResources();
+ // Stop receiveing AsyncParentMessage at Forwarder.
+ // After the call, the message is directly handled by LayerTransactionChild.
+ // Basically this function should be called in ShadowLayerForwarder's
+ // destructor. But when the destructor is triggered by
+ // CompositorBridgeChild::Destroy(), the destructor can not handle it correctly.
+ // See Bug 1000525.
+ mForwarder->StopReceiveAsyncParentMessge();
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(ClientLayerManager);
+}
+
+void
+ClientLayerManager::Destroy()
+{
+ // It's important to call ClearCachedResource before Destroy because the
+ // former will early-return if the later has already run.
+ ClearCachedResources();
+ LayerManager::Destroy();
+
+ if (mTransactionIdAllocator) {
+ // Make sure to notify the refresh driver just in case it's waiting on a
+ // pending transaction. Do this at the top of the event loop so we don't
+ // cause a paint to occur during compositor shutdown.
+ RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
+ uint64_t id = mLatestTransactionId;
+
+ RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
+ allocator->NotifyTransactionCompleted(id);
+ });
+ NS_DispatchToMainThread(task.forget());
+ }
+
+ // Forget the widget pointer in case we outlive our owning widget.
+ mWidget = nullptr;
+}
+
+int32_t
+ClientLayerManager::GetMaxTextureSize() const
+{
+ return mForwarder->GetMaxTextureSize();
+}
+
+void
+ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
+ ScreenRotation aRotation)
+{
+ mTargetRotation = aRotation;
+ }
+
+void
+ClientLayerManager::SetRoot(Layer* aLayer)
+{
+ if (mRoot != aLayer) {
+ // Have to hold the old root and its children in order to
+ // maintain the same view of the layer tree in this process as
+ // the parent sees. Otherwise layers can be destroyed
+ // mid-transaction and bad things can happen (v. bug 612573)
+ if (mRoot) {
+ Hold(mRoot);
+ }
+ mForwarder->SetRoot(Hold(aLayer));
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+ }
+}
+
+void
+ClientLayerManager::Mutated(Layer* aLayer)
+{
+ LayerManager::Mutated(aLayer);
+
+ NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
+ mForwarder->Mutated(Hold(aLayer));
+}
+
+already_AddRefed<ReadbackLayer>
+ClientLayerManager::CreateReadbackLayer()
+{
+ RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
+ return layer.forget();
+}
+
+bool
+ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
+ if (!mForwarder->IPCOpen()) {
+ gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
+ return false;
+ }
+
+ mInTransaction = true;
+ mTransactionStart = TimeStamp::Now();
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+
+ if (DependsOnStaleDevice()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ mDeviceCounter = gfxPlatform::GetPlatform()->GetDeviceCounter();
+ }
+
+ MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
+
+ // If the last transaction was incomplete (a failed DoEmptyTransaction),
+ // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
+ // to the previous transaction.
+ dom::ScreenOrientationInternal orientation;
+ if (dom::TabChild* window = mWidget->GetOwningTabChild()) {
+ orientation = window->GetOrientation();
+ } else {
+ hal::ScreenConfiguration currentConfig;
+ hal::GetCurrentScreenConfiguration(&currentConfig);
+ orientation = currentConfig.orientation();
+ }
+ LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds();
+ targetBounds.x = targetBounds.y = 0;
+ mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation,
+ orientation);
+
+ // If we're drawing on behalf of a context with async pan/zoom
+ // enabled, then the entire buffer of painted layers might be
+ // composited (including resampling) asynchronously before we get
+ // a chance to repaint, so we have to ensure that it's all valid
+ // and not rotated.
+ //
+ // Desktop does not support async zoom yet, so we ignore this for those
+ // platforms.
+#if defined(MOZ_WIDGET_UIKIT)
+ if (mWidget && mWidget->GetOwningTabChild()) {
+ mCompositorMightResample = AsyncPanZoomEnabled();
+ }
+#endif
+
+ // If we have a non-default target, we need to let our shadow manager draw
+ // to it. This will happen at the end of the transaction.
+ if (aTarget && XRE_IsParentProcess()) {
+ mShadowTarget = aTarget;
+ } else {
+ NS_ASSERTION(!aTarget,
+ "Content-process ClientLayerManager::BeginTransactionWithTarget not supported");
+ }
+
+ // If this is a new paint, increment the paint sequence number.
+ if (!mIsRepeatTransaction) {
+ // Increment the paint sequence number even if test logging isn't
+ // enabled in this process; it may be enabled in the parent process,
+ // and the parent process expects unique sequence numbers.
+ ++mPaintSequenceNumber;
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewPaint(mPaintSequenceNumber);
+ }
+ }
+ return true;
+}
+
+bool
+ClientLayerManager::BeginTransaction()
+{
+ return BeginTransactionWithTarget(nullptr);
+}
+
+bool
+ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags)
+{
+ PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+ profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
+
+ mTransactionIncomplete = false;
+
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ GetRoot()->ApplyPendingUpdatesToSubtree();
+
+ mPaintedLayerCallback = aCallback;
+ mPaintedLayerCallbackData = aCallbackData;
+
+ GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
+
+ // Skip the painting if the device is in device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ TimeStamp start = TimeStamp::Now();
+ root->RenderLayer();
+ mLastPaintTime = TimeStamp::Now() - start;
+ } else {
+ root->RenderLayer();
+ }
+ } else {
+ gfxCriticalNote << "LayerManager::EndTransaction skip RenderLayer().";
+ }
+
+ if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
+ GetRoot()->Mutated();
+ }
+
+ if (!mIsRepeatTransaction) {
+ mAnimationReadyTime = TimeStamp::Now();
+ GetRoot()->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+ mPaintedLayerCallback = nullptr;
+ mPaintedLayerCallbackData = nullptr;
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ }
+
+ return !mTransactionIncomplete;
+}
+
+void
+ClientLayerManager::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations)
+{
+ if (mForwarder) {
+ mForwarder->StorePluginWidgetConfigurations(aConfigurations);
+ }
+}
+
+void
+ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ if (!mForwarder->IPCOpen()) {
+ mInTransaction = false;
+ return;
+ }
+
+ if (mWidget) {
+ mWidget->PrepareWindowEffects();
+ }
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+ ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
+
+ if (mRepeatTransaction) {
+ mRepeatTransaction = false;
+ mIsRepeatTransaction = true;
+ if (BeginTransaction()) {
+ ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
+ }
+ mIsRepeatTransaction = false;
+ } else {
+ MakeSnapshotIfRequired();
+ }
+
+ mInTransaction = false;
+ mTransactionStart = TimeStamp();
+}
+
+bool
+ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot || !mForwarder->IPCOpen()) {
+ return false;
+ }
+
+ if (!EndTransactionInternal(nullptr, nullptr, aFlags)) {
+ // Return without calling ForwardTransaction. This leaves the
+ // ShadowLayerForwarder transaction open; the following
+ // EndTransaction will complete it.
+ return false;
+ }
+ if (mWidget) {
+ mWidget->PrepareWindowEffects();
+ }
+ ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
+ MakeSnapshotIfRequired();
+ return true;
+}
+
+CompositorBridgeChild *
+ClientLayerManager::GetRemoteRenderer()
+{
+ if (!mWidget) {
+ return nullptr;
+ }
+
+ return mWidget->GetRemoteRenderer();
+}
+
+CompositorBridgeChild*
+ClientLayerManager::GetCompositorBridgeChild()
+{
+ if (!XRE_IsParentProcess()) {
+ return CompositorBridgeChild::Get();
+ }
+ return GetRemoteRenderer();
+}
+
+void
+ClientLayerManager::Composite()
+{
+ mForwarder->Composite();
+}
+
+void
+ClientLayerManager::DidComposite(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ MOZ_ASSERT(mWidget);
+
+ // |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
+ // layers transaction.
+ if (aTransactionId) {
+ nsIWidgetListener *listener = mWidget->GetWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ listener = mWidget->GetAttachedWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
+ }
+
+ // These observers fire whether or not we were in a transaction.
+ for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
+ mDidCompositeObservers[i]->DidComposite();
+ }
+}
+
+void
+ClientLayerManager::GetCompositorSideAPZTestData(APZTestData* aData) const
+{
+ if (mForwarder->HasShadowManager()) {
+ if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ }
+}
+
+float
+ClientLayerManager::RequestProperty(const nsAString& aProperty)
+{
+ if (mForwarder->HasShadowManager()) {
+ float value;
+ if (!mForwarder->GetShadowManager()->SendRequestProperty(nsString(aProperty), &value)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ return value;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber)
+{
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewRepaintRequest(aSequenceNumber);
+ }
+}
+
+void
+ClientLayerManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(), "Frame Uniformity only supported in parent process");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ child->SendGetFrameUniformity(aOutData);
+ return;
+ }
+
+ return LayerManager::GetFrameUniformity(aOutData);
+}
+
+bool
+ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback)
+{
+ MOZ_ASSERT(aCallback != nullptr);
+ MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ NS_ASSERTION(child, "Could not get CompositorBridgeChild");
+
+ child->AddOverfillObserver(this);
+ child->SendRequestOverfill();
+ mOverfillCallbacks.AppendElement(aCallback);
+ }
+
+ return true;
+}
+
+void
+ClientLayerManager::RunOverfillCallback(const uint32_t aOverfill)
+{
+ for (size_t i = 0; i < mOverfillCallbacks.Length(); i++) {
+ ErrorResult error;
+ mOverfillCallbacks[i]->Call(aOverfill, error);
+ }
+
+ mOverfillCallbacks.Clear();
+}
+
+void
+ClientLayerManager::MakeSnapshotIfRequired()
+{
+ if (!mShadowTarget) {
+ return;
+ }
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
+ // The compositor doesn't draw to a different sized surface
+ // when there's a rotation. Instead we rotate the result
+ // when drawing into dt
+ LayoutDeviceIntRect outerBounds = mWidget->GetBounds();
+
+ IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents());
+ if (mTargetRotation) {
+ bounds =
+ RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation);
+ }
+
+ SurfaceDescriptor inSnapshot;
+ if (!bounds.IsEmpty() &&
+ mForwarder->AllocSurfaceDescriptor(bounds.Size(),
+ gfxContentType::COLOR_ALPHA,
+ &inSnapshot)) {
+
+ // Make a copy of |inSnapshot| because the call to send it over IPC
+ // will call forget() on the Shmem inside, and zero it out.
+ SurfaceDescriptor outSnapshot = inSnapshot;
+
+ if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
+ RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot);
+ DrawTarget* dt = mShadowTarget->GetDrawTarget();
+
+ Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ Rect srcRect(0, 0, bounds.width, bounds.height);
+
+ gfx::Matrix rotate =
+ ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
+ mTargetRotation);
+
+ gfx::Matrix oldMatrix = dt->GetTransform();
+ dt->SetTransform(rotate * oldMatrix);
+ dt->DrawSurface(surf, dstRect, srcRect,
+ DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_OVER));
+ dt->SetTransform(oldMatrix);
+ }
+ mForwarder->DestroySurfaceDescriptor(&outSnapshot);
+ }
+ }
+ }
+ mShadowTarget = nullptr;
+}
+
+void
+ClientLayerManager::FlushRendering()
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendFlushRendering();
+ }
+ }
+}
+
+void
+ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
+{
+ mForwarder->IdentifyTextureHost(aNewIdentifier);
+}
+
+void
+ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendNotifyRegionInvalidated(aRegion);
+ }
+ }
+}
+
+uint32_t
+ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ uint32_t startIndex;
+ renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
+ return startIndex;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
+ }
+}
+
+void
+ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
+{
+ TimeStamp start = TimeStamp::Now();
+
+ // Skip the synchronization for buffer since we also skip the painting during
+ // device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (mForwarder->GetSyncObject() &&
+ mForwarder->GetSyncObject()->IsSyncObjectValid()) {
+ mForwarder->GetSyncObject()->FinalizeFrame();
+ }
+ }
+
+ mPhase = PHASE_FORWARD;
+
+ mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
+ TimeStamp transactionStart;
+ if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
+ transactionStart = mTransactionIdAllocator->GetTransactionStart();
+ } else {
+ transactionStart = mTransactionStart;
+ }
+
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
+ }
+
+ // forward this transaction's changeset to our LayerManagerComposite
+ bool sent;
+ AutoTArray<EditReply, 10> replies;
+ if (mForwarder->EndTransaction(&replies, mRegionToClear,
+ mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
+ mIsRepeatTransaction, transactionStart, &sent)) {
+ for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
+ const EditReply& reply = replies[i];
+
+ switch (reply.type()) {
+ case EditReply::TOpContentBufferSwap: {
+ MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
+
+ const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
+
+ RefPtr<CompositableClient> compositable =
+ CompositableClient::FromIPDLActor(obs.compositableChild());
+ ContentClientRemote* contentClient =
+ static_cast<ContentClientRemote*>(compositable.get());
+ MOZ_ASSERT(contentClient);
+
+ contentClient->SwapBuffers(obs.frontUpdatedRegion());
+
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+ }
+
+ if (sent) {
+ mNeedsComposite = false;
+ }
+ } else if (HasShadowManager()) {
+ NS_WARNING("failed to forward Layers transaction");
+ }
+
+ if (!sent) {
+ // Clear the transaction id so that it doesn't get returned
+ // unless we forwarded to somewhere that doesn't actually
+ // have a compositor.
+ mTransactionIdAllocator->RevokeTransactionId(mLatestTransactionId);
+ }
+
+ mPhase = PHASE_NONE;
+
+ // this may result in Layers being deleted, which results in
+ // PLayer::Send__delete__() and DeallocShmem()
+ mKeepAlive.Clear();
+
+ TabChild* window = mWidget ? mWidget->GetOwningTabChild() : nullptr;
+ if (window) {
+ TimeStamp end = TimeStamp::Now();
+ window->DidRequestComposite(start, end);
+ }
+}
+
+ShadowableLayer*
+ClientLayerManager::Hold(Layer* aLayer)
+{
+ MOZ_ASSERT(HasShadowManager(),
+ "top-level tree, no shadow tree to remote to");
+
+ ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer);
+ MOZ_ASSERT(shadowable, "trying to remote an unshadowable layer");
+
+ mKeepAlive.AppendElement(aLayer);
+ return shadowable;
+}
+
+bool
+ClientLayerManager::IsCompositingCheap()
+{
+ // Whether compositing is cheap depends on the parent backend.
+ return mForwarder->mShadowManager &&
+ LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
+}
+
+bool
+ClientLayerManager::AreComponentAlphaLayersEnabled()
+{
+ return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
+ AsShadowForwarder()->SupportsComponentAlpha() &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+void
+ClientLayerManager::SetIsFirstPaint()
+{
+ mForwarder->SetIsFirstPaint();
+}
+
+void
+ClientLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ if (mDestroyed) {
+ // ClearCachedResource was already called by ClientLayerManager::Destroy
+ return;
+ }
+ MOZ_ASSERT(!HasShadowManager() || !aSubtree);
+ mForwarder->ClearCachedResources();
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressure()
+{
+ if (mRoot) {
+ HandleMemoryPressureLayer(mRoot);
+ }
+
+ if (GetCompositorBridgeChild()) {
+ GetCompositorBridgeChild()->HandleMemoryPressure();
+ }
+}
+
+void
+ClientLayerManager::ClearLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ HandleMemoryPressureLayer(child);
+ }
+}
+
+void
+ClientLayerManager::GetBackendName(nsAString& aName)
+{
+ switch (mForwarder->GetCompositorBackendType()) {
+ case LayersBackend::LAYERS_NONE: aName.AssignLiteral("None"); return;
+ case LayersBackend::LAYERS_BASIC: aName.AssignLiteral("Basic"); return;
+ case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
+ case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
+ case LayersBackend::LAYERS_D3D11: {
+#ifdef XP_WIN
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ aName.AssignLiteral("Direct3D 11 WARP");
+ } else {
+ aName.AssignLiteral("Direct3D 11");
+ }
+#endif
+ return;
+ }
+ default: NS_RUNTIMEABORT("Invalid backend");
+ }
+}
+
+bool
+ClientLayerManager::AsyncPanZoomEnabled() const
+{
+ return mWidget && mWidget->AsyncPanZoomEnabled();
+}
+
+void
+ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
+{
+ mForwarder->SetPaintSyncId(aSyncId);
+}
+
+void
+ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+ mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+void
+ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ if (!mDidCompositeObservers.Contains(aObserver)) {
+ mDidCompositeObservers.AppendElement(aObserver);
+ }
+}
+
+void
+ClientLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ mDidCompositeObservers.RemoveElement(aObserver);
+}
+
+bool
+ClientLayerManager::DependsOnStaleDevice() const
+{
+ return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
+}
+
+
+already_AddRefed<PersistentBufferProvider>
+ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat)
+{
+ // Don't use a shared buffer provider if compositing is considered "not cheap"
+ // because the canvas will most likely be flattened into a thebes layer instead
+ // of being sent to the compositor, in which case rendering into shared memory
+ // is wasteful.
+ if (IsCompositingCheap() &&
+ gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+ RefPtr<PersistentBufferProvider> provider
+ = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+ if (provider) {
+ return provider.forget();
+ }
+ }
+
+ return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
+}
+
+
+ClientLayer::~ClientLayer()
+{
+ if (HasShadow()) {
+ PLayerChild::Send__delete__(GetShadow());
+ }
+ MOZ_COUNT_DTOR(ClientLayer);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientLayerManager.h b/system/graphics/layers/client/ClientLayerManager.h
new file mode 100644
index 000000000..5bcd5e412
--- /dev/null
+++ b/system/graphics/layers/client/ClientLayerManager.h
@@ -0,0 +1,422 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTLAYERMANAGER_H
+#define GFX_CLIENTLAYERMANAGER_H
+
+#include <stdint.h> // for int32_t
+#include "Layers.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/LinkedList.h" // For LinkedList
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/layers/APZTestData.h" // for APZTestData
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsIObserver.h" // for nsIObserver
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsAString
+#include "mozilla/layers/TransactionIdAllocator.h"
+#include "nsIWidget.h" // For plugin window configuration information structs
+
+namespace mozilla {
+namespace layers {
+
+class ClientPaintedLayer;
+class CompositorBridgeChild;
+class ImageLayer;
+class PLayerChild;
+class FrameUniformityData;
+
+class ClientLayerManager final : public LayerManager
+{
+ typedef nsTArray<RefPtr<Layer> > LayerRefArray;
+
+public:
+ explicit ClientLayerManager(nsIWidget* aWidget);
+
+ virtual void Destroy() override;
+
+protected:
+ virtual ~ClientLayerManager();
+
+public:
+ virtual ShadowLayerForwarder* AsShadowForwarder() override
+ {
+ return mForwarder;
+ }
+
+ virtual ClientLayerManager* AsClientLayerManager() override
+ {
+ return this;
+ }
+
+ virtual int32_t GetMaxTextureSize() const override;
+
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool BeginTransaction() override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_CLIENT; }
+ virtual LayersBackend GetCompositorBackendType() override
+ {
+ return AsShadowForwarder()->GetCompositorBackendType();
+ }
+ virtual void GetBackendName(nsAString& name) override;
+ virtual const char* Name() const override { return "Client"; }
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual void Mutated(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<RefLayer> CreateRefLayer() override;
+
+ void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier);
+ TextureFactoryIdentifier GetTextureFactoryIdentifier()
+ {
+ return AsShadowForwarder()->GetTextureFactoryIdentifier();
+ }
+
+ virtual void FlushRendering() override;
+ void SendInvalidRegion(const nsIntRegion& aRegion);
+
+ virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
+
+ virtual void StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals) override;
+
+ virtual bool NeedsWidgetInvalidation() override { return false; }
+
+ ShadowableLayer* Hold(Layer* aLayer);
+
+ bool HasShadowManager() const { return mForwarder->HasShadowManager(); }
+
+ virtual bool IsCompositingCheap() override;
+ virtual bool HasShadowManagerInternal() const override { return HasShadowManager(); }
+
+ virtual void SetIsFirstPaint() override;
+
+ /**
+ * Pass through call to the forwarder for nsPresContext's
+ * CollectPluginGeometryUpdates. Passes widget configuration information
+ * to the compositor for transmission to the chrome process. This
+ * configuration gets set when the window paints.
+ */
+ void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations) override;
+
+ // Drop cached resources and ask our shadow manager to do the same,
+ // if we have one.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void HandleMemoryPressure();
+
+ void SetRepeatTransaction() { mRepeatTransaction = true; }
+ bool GetRepeatTransaction() { return mRepeatTransaction; }
+
+ bool IsRepeatTransaction() { return mIsRepeatTransaction; }
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+
+ bool HasShadowTarget() { return !!mShadowTarget; }
+
+ void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
+
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+ DrawPaintedLayerCallback GetPaintedLayerCallback() const
+ { return mPaintedLayerCallback; }
+
+ void* GetPaintedLayerCallbackData() const
+ { return mPaintedLayerCallbackData; }
+
+ CompositorBridgeChild* GetRemoteRenderer();
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ // Disable component alpha layers with the software compositor.
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ void SetNeedsComposite(bool aNeedsComposite)
+ {
+ mNeedsComposite = aNeedsComposite;
+ }
+ bool NeedsComposite() const { return mNeedsComposite; }
+
+ virtual void Composite() override;
+ virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
+ virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
+ virtual void RunOverfillCallback(const uint32_t aOverfill) override;
+
+ void DidComposite(uint64_t aTransactionId,
+ const mozilla::TimeStamp& aCompositeStart,
+ const mozilla::TimeStamp& aCompositeEnd);
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ // Log APZ test data for the current paint. We supply the paint sequence
+ // number ourselves, and take care of calling APZTestData::StartNewPaint()
+ // when a new paint is started.
+ void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Log APZ test data for a repaint request. The sequence number must be
+ // passed in from outside, and APZTestData::StartNewRepaintRequest() needs
+ // to be called from the outside as well when a new repaint request is started.
+ void StartNewRepaintRequest(SequenceNumber aSequenceNumber);
+
+ // TODO(botond): When we start using this and write a wrapper similar to
+ // nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks
+ // gfxPrefs::APZTestLoggingEnabled().
+ void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
+ FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForRepaintRequest(aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Get the content-side APZ test data for reading. For writing, use the
+ // LogTestData...() functions.
+ const APZTestData& GetAPZTestData() const {
+ return mApzTestData;
+ }
+
+ // Get a copy of the compositor-side APZ test data for our layers ID.
+ void GetCompositorSideAPZTestData(APZTestData* aData) const;
+
+ void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
+
+ float RequestProperty(const nsAString& aProperty) override;
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void SetNextPaintSyncId(int32_t aSyncId);
+
+ void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
+ class DidCompositeObserver {
+ public:
+ virtual void DidComposite() = 0;
+ };
+
+ void AddDidCompositeObserver(DidCompositeObserver* aObserver);
+ void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
+
+ virtual already_AddRefed<PersistentBufferProvider>
+ CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+private:
+ // Listen memory-pressure event for ClientLayerManager
+ class MemoryPressureObserver final : public nsIObserver
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit MemoryPressureObserver(ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager)
+ {
+ RegisterMemoryPressureEvent();
+ }
+
+ void Destroy();
+
+ private:
+ virtual ~MemoryPressureObserver() {}
+ void RegisterMemoryPressureEvent();
+ void UnregisterMemoryPressureEvent();
+
+ ClientLayerManager* mClientLayerManager;
+ };
+
+ /**
+ * Forward transaction results to the parent context.
+ */
+ void ForwardTransaction(bool aScheduleComposite);
+
+ /**
+ * Take a snapshot of the parent context, and copy
+ * it into mShadowTarget.
+ */
+ void MakeSnapshotIfRequired();
+
+ void ClearLayer(Layer* aLayer);
+
+ void HandleMemoryPressureLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags);
+
+ bool DependsOnStaleDevice() const;
+
+ LayerRefArray mKeepAlive;
+
+ nsIWidget* mWidget;
+
+ /* PaintedLayer callbacks; valid at the end of a transaciton,
+ * while rendering */
+ DrawPaintedLayerCallback mPaintedLayerCallback;
+ void *mPaintedLayerCallbackData;
+
+ // When we're doing a transaction in order to draw to a non-default
+ // target, the layers transaction is only performed in order to send
+ // a PLayers:Update. We save the original non-default target to
+ // mShadowTarget, and then perform the transaction using
+ // mDummyTarget as the render target. After the transaction ends,
+ // we send a message to our remote side to capture the actual pixels
+ // being drawn to the default target, and then copy those pixels
+ // back to mShadowTarget.
+ RefPtr<gfxContext> mShadowTarget;
+
+ RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
+ uint64_t mLatestTransactionId;
+ TimeDuration mLastPaintTime;
+
+ // Sometimes we draw to targets that don't natively support
+ // landscape/portrait orientation. When we need to implement that
+ // ourselves, |mTargetRotation| describes the induced transform we
+ // need to apply when compositing content to our target.
+ ScreenRotation mTargetRotation;
+
+ // Used to repeat the transaction right away (to avoid rebuilding
+ // a display list) to support progressive drawing.
+ bool mRepeatTransaction;
+ bool mIsRepeatTransaction;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+ bool mNeedsComposite;
+
+ // An incrementing sequence number for paints.
+ // Incremented in BeginTransaction(), but not for repeat transactions.
+ uint32_t mPaintSequenceNumber;
+
+ APZTestData mApzTestData;
+
+ RefPtr<ShadowLayerForwarder> mForwarder;
+ AutoTArray<dom::OverfillCallback*,0> mOverfillCallbacks;
+ mozilla::TimeStamp mTransactionStart;
+
+ nsTArray<DidCompositeObserver*> mDidCompositeObservers;
+
+ RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
+ uint64_t mDeviceCounter;
+};
+
+class ClientLayer : public ShadowableLayer
+{
+public:
+ ClientLayer()
+ {
+ MOZ_COUNT_CTOR(ClientLayer);
+ }
+
+ ~ClientLayer();
+
+ void SetShadow(PLayerChild* aShadow)
+ {
+ MOZ_ASSERT(!mShadow, "can't have two shadows (yet)");
+ mShadow = aShadow;
+ }
+
+ virtual void Disconnect()
+ {
+ // This is an "emergency Disconnect()", called when the compositing
+ // process has died. |mShadow| and our Shmem buffers are
+ // automatically managed by IPDL, so we don't need to explicitly
+ // free them here (it's hard to get that right on emergency
+ // shutdown anyway).
+ mShadow = nullptr;
+ }
+
+ virtual void ClearCachedResources() { }
+
+ // Shrink memory usage.
+ // Called when "memory-pressure" is observed.
+ virtual void HandleMemoryPressure() { }
+
+ virtual void RenderLayer() = 0;
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
+
+ virtual ClientPaintedLayer* AsThebes() { return nullptr; }
+
+ static inline ClientLayer *
+ ToClientLayer(Layer* aLayer)
+ {
+ return static_cast<ClientLayer*>(aLayer->ImplData());
+ }
+
+ template <typename LayerType>
+ static inline void RenderMaskLayers(LayerType* aLayer) {
+ if (aLayer->GetMaskLayer()) {
+ ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
+ }
+ for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+ ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
+ }
+ }
+};
+
+// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
+// our layer tree to a parent process. Record the new layer creation
+// in the current open transaction as a side effect.
+template<typename CreatedMethod> void
+CreateShadowFor(ClientLayer* aLayer,
+ ClientLayerManager* aMgr,
+ CreatedMethod aMethod)
+{
+ PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
+ if (!shadow) {
+ return;
+ }
+
+ aLayer->SetShadow(shadow);
+ (aMgr->AsShadowForwarder()->*aMethod)(aLayer);
+ aMgr->Hold(aLayer->AsLayer());
+}
+
+#define CREATE_SHADOW(_type) \
+ CreateShadowFor(layer, this, \
+ &ShadowLayerForwarder::Created ## _type ## Layer)
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTLAYERMANAGER_H */
diff --git a/system/graphics/layers/client/ClientPaintedLayer.cpp b/system/graphics/layers/client/ClientPaintedLayer.cpp
new file mode 100644
index 000000000..150eadebf
--- /dev/null
+++ b/system/graphics/layers/client/ClientPaintedLayer.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientPaintedLayer.h"
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxContext.h" // for gfxContext
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/Preferences.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "gfx2DGlue.h"
+#include "ReadbackProcessor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientPaintedLayer::PaintThebes()
+{
+ PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(ClientManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
+ if (ClientManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+
+ bool didUpdate = false;
+ RotatedContentBuffer::DrawIterator iter;
+ while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
+ if (!target || !target->IsValid()) {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ }
+ continue;
+ }
+
+ SetAntialiasingFlags(this, target);
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ ClientManager()->GetPaintedLayerCallback()(this,
+ ctx,
+ iter.mDrawRegion,
+ iter.mDrawRegion,
+ state.mClip,
+ state.mRegionToInvalidate,
+ ClientManager()->GetPaintedLayerCallbackData());
+
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ didUpdate = true;
+ }
+
+ if (didUpdate) {
+ Mutated();
+
+ mValidRegion.Or(mValidRegion, state.mRegionToDraw);
+
+ ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
+ MOZ_ASSERT(contentClientRemote->GetIPDLActor());
+
+ // Hold(this) ensures this layer is kept alive through the current transaction
+ // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
+ // so deleting this Hold for whatever reason will break things.
+ ClientManager()->Hold(this);
+ contentClientRemote->Updated(state.mRegionToDraw,
+ mVisibleRegion.ToUnknownRegion(),
+ state.mDidSelfCopy);
+ }
+}
+
+void
+ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
+{
+ RenderMaskLayers(this);
+
+ if (!mContentClient) {
+ mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
+ if (!mContentClient) {
+ return;
+ }
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ nsIntRegion readbackRegion;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ IntPoint origin(mVisibleRegion.GetBounds().x, mVisibleRegion.GetBounds().y);
+ mContentClient->BeginPaint();
+ PaintThebes();
+ mContentClient->EndPaint(&readbackUpdates);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayer()
+{
+ return CreatePaintedLayerWithHint(NONE);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ if (gfxPrefs::LayersTilesEnabled()) {
+ RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ } else {
+ RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ }
+}
+
+void
+ClientPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientPaintedLayer.h b/system/graphics/layers/client/ClientPaintedLayer.h
new file mode 100644
index 000000000..949f746ae
--- /dev/null
+++ b/system/graphics/layers/client/ClientPaintedLayer.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTPAINTEDLAYER_H
+#define GFX_CLIENTPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/ContentClient.h" // for ContentClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+class ClientPaintedLayer : public PaintedLayer,
+ public ClientLayer {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit ClientPaintedLayer(ClientLayerManager* aLayerManager,
+ LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) :
+ PaintedLayer(aLayerManager, static_cast<ClientLayer*>(this), aCreationHint),
+ mContentClient(nullptr)
+ {
+ MOZ_COUNT_CTOR(ClientPaintedLayer);
+ }
+
+protected:
+ virtual ~ClientPaintedLayer()
+ {
+ if (mContentClient) {
+ mContentClient->OnDetach();
+ mContentClient = nullptr;
+ }
+ MOZ_COUNT_DTOR(ClientPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void RenderLayer() override { RenderLayerWithReadback(nullptr); }
+
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+ }
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mContentClient;
+ }
+
+ virtual void Disconnect() override
+ {
+ mContentClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ void PaintThebes();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void DestroyBackBuffer()
+ {
+ mContentClient = nullptr;
+ }
+
+ RefPtr<ContentClient> mContentClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/ClientReadbackLayer.h b/system/graphics/layers/client/ClientReadbackLayer.h
new file mode 100644
index 000000000..5f58486ca
--- /dev/null
+++ b/system/graphics/layers/client/ClientReadbackLayer.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTREADBACKLAYER_H
+#define GFX_CLIENTREADBACKLAYER_H
+
+#include "ClientLayerManager.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientReadbackLayer :
+ public ReadbackLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientReadbackLayer(ClientLayerManager *aManager)
+ : ReadbackLayer(aManager, nullptr)
+ {
+ mImplData = static_cast<ClientLayer*>(this);
+ }
+
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+ virtual Layer* AsLayer() override { return this; }
+ virtual void RenderLayer() override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTREADBACKLAYER_H */
diff --git a/system/graphics/layers/client/ClientTiledPaintedLayer.cpp b/system/graphics/layers/client/ClientTiledPaintedLayer.cpp
new file mode 100644
index 000000000..8936dad8f
--- /dev/null
+++ b/system/graphics/layers/client/ClientTiledPaintedLayer.cpp
@@ -0,0 +1,608 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientTiledPaintedLayer.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for ScreenIntRect, CSSPoint, etc
+#include "UnitTransforms.h" // for TransformTo
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Rect.h" // for Rect, RectTyped
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "LayersLogging.h"
+#include "mozilla/layers/SingleTiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::Rect;
+using gfx::IntRect;
+using gfx::IntSize;
+
+ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint)
+ : PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint)
+ , mContentClient()
+ , mHaveSingleTiledContentClient(false)
+{
+ MOZ_COUNT_CTOR(ClientTiledPaintedLayer);
+ mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
+ mPaintData.mFirstPaint = true;
+}
+
+ClientTiledPaintedLayer::~ClientTiledPaintedLayer()
+{
+ MOZ_COUNT_DTOR(ClientTiledPaintedLayer);
+}
+
+void
+ClientTiledPaintedLayer::ClearCachedResources()
+{
+ if (mContentClient) {
+ mContentClient->ClearCachedResources();
+ }
+ mValidRegion.SetEmpty();
+ mContentClient = nullptr;
+}
+
+void
+ClientTiledPaintedLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+}
+
+static Maybe<LayerRect>
+ApplyParentLayerToLayerTransform(const ParentLayerToLayerMatrix4x4& aTransform,
+ const ParentLayerRect& aParentLayerRect,
+ const LayerRect& aClip)
+{
+ return UntransformBy(aTransform, aParentLayerRect, aClip);
+}
+
+static LayerToParentLayerMatrix4x4
+GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor)
+{
+ gfx::Matrix4x4 transform;
+ const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent();
+ for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM);
+ ancestorParent ? iter != ancestorParent : iter.IsValid();
+ iter = iter.GetParent()) {
+ transform = transform * iter.GetTransform();
+
+ if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+ // When scrolling containers, layout adds a post-scale into the transform
+ // of the displayport-ancestor (which we pick up in GetTransform() above)
+ // to cancel out the pres shell resolution (for historical reasons). The
+ // compositor in turn cancels out this post-scale (i.e., scales by the
+ // pres shell resolution), and to get correct calculations, we need to do
+ // so here, too.
+ //
+ // With containerless scrolling, the offending post-scale is on the
+ // parent layer of the displayport-ancestor, which we don't reach in this
+ // loop, so we don't need to worry about it.
+ float presShellResolution = iter.GetPresShellResolution();
+ transform.PostScale(presShellResolution, presShellResolution, 1.0f);
+ }
+ }
+ return ViewAs<LayerToParentLayerMatrix4x4>(transform);
+}
+
+void
+ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation)
+{
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation = false;
+ for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
+ hasTransformAnimation |= ancestor.HasTransformAnimation();
+ const FrameMetrics& metrics = ancestor.Metrics();
+ if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
+ scrollAncestor = ancestor;
+ }
+ if (!metrics.GetDisplayPort().IsEmpty()) {
+ displayPortAncestor = ancestor;
+ // Any layer that has a displayport must be scrollable, so we can break
+ // here.
+ break;
+ }
+ }
+ if (aOutScrollAncestor) {
+ *aOutScrollAncestor = scrollAncestor;
+ }
+ if (aOutDisplayPortAncestor) {
+ *aOutDisplayPortAncestor = displayPortAncestor;
+ }
+ if (aOutHasTransformAnimation) {
+ *aOutHasTransformAnimation = hasTransformAnimation;
+ }
+}
+
+void
+ClientTiledPaintedLayer::BeginPaint()
+{
+ mPaintData.ResetPaintData();
+
+ if (!GetBaseTransform().Is2D()) {
+ // Give up if there is a complex CSS transform on the layer. We might
+ // eventually support these but for now it's too complicated to handle
+ // given that it's a pretty rare scenario.
+ return;
+ }
+
+ // Get the metrics of the nearest scrollable layer and the nearest layer
+ // with a displayport.
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation;
+ GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation);
+
+ if (!displayPortAncestor || !scrollAncestor) {
+ // No displayport or scroll ancestor, so we can't do progressive rendering.
+ return;
+ }
+
+ TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this,
+ scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation);
+
+ const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
+ const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();
+
+ // Calculate the transform required to convert ParentLayer space of our
+ // display port ancestor to the Layer space of this layer.
+ ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer =
+ GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse();
+
+ LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));
+
+ // Compute the critical display port that applies to this layer in the
+ // LayoutDevice space of this layer, but only if there is no OMT animation
+ // on this layer. If there is an OMT animation then we need to draw the whole
+ // visible region of this layer as determined by layout, because we don't know
+ // what parts of it might move into view in the compositor.
+ mPaintData.mHasTransformAnimation = hasTransformAnimation;
+ if (!mPaintData.mHasTransformAnimation &&
+ mContentClient->GetLowPrecisionTiledBuffer()) {
+ ParentLayerRect criticalDisplayPort =
+ (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
+ + displayportMetrics.GetCompositionBounds().TopLeft();
+ Maybe<LayerRect> criticalDisplayPortTransformed =
+ ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds);
+ if (criticalDisplayPortTransformed) {
+ mPaintData.mCriticalDisplayPort = Some(RoundedToInt(*criticalDisplayPortTransformed));
+ } else {
+ mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0));
+ }
+ }
+ TILING_LOG("TILING %p: Critical displayport %s\n", this,
+ mPaintData.mCriticalDisplayPort ?
+ Stringify(*mPaintData.mCriticalDisplayPort).c_str() : "not set");
+
+ // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
+ // before any async transforms have occurred, we can use the zoom for this.
+ mPaintData.mResolution = displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str());
+
+ // Store the applicable composition bounds in this layer's Layer units.
+ mPaintData.mTransformToCompBounds =
+ GetTransformToAncestorsParentLayer(this, scrollAncestor);
+ ParentLayerToLayerMatrix4x4 transformToBounds = mPaintData.mTransformToCompBounds.Inverse();
+ Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform(
+ transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds);
+ if (compositionBoundsTransformed) {
+ mPaintData.mCompositionBounds = *compositionBoundsTransformed;
+ } else {
+ mPaintData.mCompositionBounds.SetEmpty();
+ }
+ TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());
+
+ // Calculate the scroll offset since the last transaction
+ mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
+}
+
+bool
+ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics)
+{
+ CompositorBridgeChild* compositor = nullptr;
+ if (Manager() && Manager()->AsClientLayerManager()) {
+ compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ FrameMetrics compositorMetrics;
+ if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta
+ // is so small then we have nothing to gain from using paint heuristics.
+ float COORDINATE_EPSILON = 1.f;
+
+ return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x,
+ aParentMetrics.GetScrollOffset().x,
+ COORDINATE_EPSILON) ||
+ !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y,
+ aParentMetrics.GetScrollOffset().y,
+ COORDINATE_EPSILON);
+}
+
+bool
+ClientTiledPaintedLayer::UseProgressiveDraw() {
+ if (!gfxPrefs::ProgressivePaint()) {
+ // pref is disabled, so never do progressive
+ return false;
+ }
+
+ if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) {
+ return false;
+ }
+
+ if (ClientManager()->HasShadowTarget()) {
+ // This condition is true when we are in a reftest scenario. We don't want
+ // to draw progressively here because it can cause intermittent reftest
+ // failures because the harness won't wait for all the tiles to be drawn.
+ return false;
+ }
+
+ if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
+ // This layer is fixed-position and so even if it does have a scrolling
+ // ancestor it will likely be entirely on-screen all the time, so we
+ // should draw it all at once
+ return false;
+ }
+
+ if (mPaintData.mHasTransformAnimation) {
+ // The compositor is going to animate this somehow, so we want it all
+ // on the screen at once.
+ return false;
+ }
+
+ if (ClientManager()->AsyncPanZoomEnabled()) {
+ LayerMetricsWrapper scrollAncestor;
+ GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+ MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set
+ if (!scrollAncestor) {
+ return false;
+ }
+ const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
+ if (!IsScrollingOnCompositor(parentMetrics)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // If we have started drawing low-precision already, then we
+ // shouldn't do anything there.
+ if (mPaintData.mLowPrecisionPaintCount != 0) {
+ return false;
+ }
+
+ // Only draw progressively when there is something to paint and the
+ // resolution is unchanged
+ if (!aInvalidRegion.IsEmpty() &&
+ UseProgressiveDraw() &&
+ mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) {
+ // Store the old valid region, then clear it before painting.
+ // We clip the old valid region to the visible region, as it only gets
+ // used to decide stale content (currently valid and previously visible)
+ nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());
+
+ return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion,
+ oldValidRegion, &mPaintData, aCallback, aCallbackData);
+ }
+
+ // Otherwise do a non-progressive paint. We must do this even when
+ // the region to paint is empty as the valid region may have shrunk.
+
+ mValidRegion = aVisibleRegion;
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());
+
+ mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion,
+ aCallback, aCallbackData);
+ mPaintData.mPaintFinished = true;
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // Render the low precision buffer, if the visible region is larger than the
+ // critical display port.
+ if (!mPaintData.mCriticalDisplayPort ||
+ !nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()).Contains(aVisibleRegion)) {
+ nsIntRegion oldValidRegion = mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+
+ bool updatedBuffer = false;
+
+ // If the frame resolution or format have changed, invalidate the buffer
+ if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != mPaintData.mResolution ||
+ mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) {
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ updatedBuffer = true;
+ }
+ oldValidRegion.SetEmpty();
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ aInvalidRegion = aVisibleRegion;
+ }
+
+ // Invalidate previously valid content that is no longer visible
+ if (mPaintData.mLowPrecisionPaintCount == 1) {
+ mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion);
+ }
+ mPaintData.mLowPrecisionPaintCount++;
+
+ // Remove the valid high-precision region from the invalid low-precision
+ // region. We don't want to spend time drawing things twice.
+ aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str());
+
+ if (!aInvalidRegion.IsEmpty()) {
+ updatedBuffer = mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate(
+ mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
+ &mPaintData, aCallback, aCallbackData);
+ }
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+ return updatedBuffer;
+ }
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ TILING_LOG("TILING %p: Clearing low-precision buffer\n", this);
+ // Clear the low precision tiled buffer.
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ // Return true here so we send a Painted callback after clearing the valid
+ // region of the low precision buffer. This allows the shadow buffer's valid
+ // region to be updated and the associated resources to be freed.
+ return true;
+ }
+ return false;
+}
+
+void
+ClientTiledPaintedLayer::EndPaint()
+{
+ mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
+ mPaintData.mPaintFinished = true;
+ mPaintData.mFirstPaint = false;
+ TILING_LOG("TILING %p: Paint finished\n", this);
+}
+
+void
+ClientTiledPaintedLayer::RenderLayer()
+{
+ LayerManager::DrawPaintedLayerCallback callback =
+ ClientManager()->GetPaintedLayerCallback();
+ void *data = ClientManager()->GetPaintedLayerCallbackData();
+
+ IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
+ IntSize tileSize = gfx::gfxVars::TileSize();
+ bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 ||
+ layerSize.height <= tileSize.height / 2;
+
+ // Use single tile when layer is not scrollable, is smaller than one
+ // tile, or when more than half of the tiles' pixels in either
+ // dimension would be wasted.
+ bool wantSingleTiledContentClient =
+ (mCreationHint == LayerManager::NONE ||
+ layerSize <= tileSize ||
+ isHalfTileWidthOrHeight) &&
+ SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
+ gfxPrefs::LayersSingleTileEnabled();
+
+ if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) {
+ mContentClient = nullptr;
+ mValidRegion.SetEmpty();
+ }
+
+ if (!mContentClient) {
+ if (wantSingleTiledContentClient) {
+ mContentClient = new SingleTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = true;
+ } else {
+ mContentClient = new MultiTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = false;
+ }
+
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ if (mContentClient->GetTiledBuffer()->HasFormatChanged()) {
+ mValidRegion = nsIntRegion();
+ mContentClient->GetTiledBuffer()->ResetPaintedAndValidState();
+ }
+
+ TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
+ TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+
+ nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion();
+ // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
+ if (MayResample()) {
+ // If we're resampling then bilinear filtering can read up to 1 pixel
+ // outside of our texture coords. Make the visible region a single rect,
+ // and pad it out by 1 pixel (restricted to tile boundaries) so that
+ // we always have valid content or transparent pixels to sample from.
+ IntRect bounds = neededRegion.GetBounds();
+ IntRect wholeTiles = bounds;
+ wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize());
+ IntRect padded = bounds;
+ padded.Inflate(1);
+ padded.IntersectRect(padded, wholeTiles);
+ neededRegion = padded;
+ }
+
+ nsIntRegion invalidRegion;
+ invalidRegion.Sub(neededRegion, mValidRegion);
+ if (invalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (!callback) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ if (!ClientManager()->IsRepeatTransaction()) {
+ // Only paint the mask layers on the first transaction.
+ RenderMaskLayers(this);
+
+ // For more complex cases we need to calculate a bunch of metrics before we
+ // can do the paint.
+ BeginPaint();
+ if (mPaintData.mPaintFinished) {
+ return;
+ }
+
+ // Make sure that tiles that fall outside of the visible region or outside of the
+ // critical displayport are discarded on the first update. Also make sure that we
+ // only draw stuff inside the critical displayport on the first update.
+ mValidRegion.And(mValidRegion, neededRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ } else {
+ if (mPaintData.mCriticalDisplayPort) {
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+ TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ }
+
+ nsIntRegion lowPrecisionInvalidRegion;
+ if (mContentClient->GetLowPrecisionTiledBuffer()) {
+ // Calculate the invalid region for the low precision buffer. Make sure
+ // to remove the valid high-precision area so we don't double-paint it.
+ lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion);
+ lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
+ }
+ TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str());
+
+ bool updatedHighPrecision = RenderHighPrecision(invalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedHighPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more high-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If there is nothing to draw in low-precision, then we're done.
+ if (lowPrecisionInvalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (updatedHighPrecision) {
+ // If there are low precision updates, but we just did some high-precision
+ // updates, then mark the paint as unfinished and request a repeat transaction.
+ // This is so that we don't perform low-precision updates in the same transaction
+ // as high-precision updates.
+ TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this);
+ ClientManager()->SetRepeatTransaction();
+ mPaintData.mLowPrecisionPaintCount = 1;
+ mPaintData.mPaintFinished = false;
+ return;
+ }
+
+ bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedLowPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more low-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If we get here, we've done all the high- and low-precision
+ // paints we wanted to do, so we can finish the paint and chill.
+ EndPaint();
+}
+
+bool
+ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint)
+{
+ // The only creation hint is whether the layer is scrollable or not, and this
+ // is only respected on B2G and OSX, where it's used to determine whether to
+ // use a tiled content client or not.
+ // There are pretty nasty performance consequences for not using tiles on
+ // large, scrollable layers, so we want the layer to be recreated in this
+ // situation.
+ return aHint == GetCreationHint();
+}
+
+void
+ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ClientTiledPaintedLayer.h b/system/graphics/layers/client/ClientTiledPaintedLayer.h
new file mode 100644
index 000000000..9e1e003ee
--- /dev/null
+++ b/system/graphics/layers/client/ClientTiledPaintedLayer.h
@@ -0,0 +1,153 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTTILEDPAINTEDLAYER_H
+#define GFX_CLIENTTILEDPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayer, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/TiledContentClient.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+/**
+ * An implementation of PaintedLayer that ONLY supports remote
+ * composition that is backed by tiles. This painted layer implementation
+ * is better suited to mobile hardware to work around slow implementation
+ * of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
+ *
+ * Tiled PaintedLayers use a different protocol compared with other
+ * layers. A copy of the tiled buffer is made and sent to the compositing
+ * thread via the layers protocol. Tiles are uploaded by the buffers
+ * asynchonously without using IPC, that means they are not safe for cross-
+ * process use (bug 747811). Each tile has a TextureHost/Client pair but
+ * they communicate directly rather than using the Texture protocol.
+ *
+ * There is no ContentClient for tiled layers. There is a ContentHost, however.
+ */
+class ClientTiledPaintedLayer : public PaintedLayer,
+ public ClientLayer
+{
+ typedef PaintedLayer Base;
+
+public:
+ explicit ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE);
+
+protected:
+ ~ClientTiledPaintedLayer();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+public:
+ // Override name to distinguish it from ClientPaintedLayer in layer dumps
+ virtual const char* Name() const override { return "TiledPaintedLayer"; }
+
+ // PaintedLayer
+ virtual Layer* AsLayer() override { return this; }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override {
+ mInvalidRegion.Add(aRegion);
+ nsIntRegion invalidRegion = mInvalidRegion.GetRegion();
+ mValidRegion.Sub(mValidRegion, invalidRegion);
+ mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, invalidRegion);
+ }
+
+ // Shadow methods
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override;
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ /**
+ * Helper method to find the nearest ancestor layers which
+ * scroll and have a displayport. The parameters are out-params
+ * which hold the return values; the values passed in may be null.
+ */
+ void GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation);
+
+ virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) override;
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ /**
+ * For the initial PaintThebes of a transaction, calculates all the data
+ * needed for that paint and any repeated transactions.
+ */
+ void BeginPaint();
+
+ /**
+ * Check if the layer is being scrolled by APZ on the compositor.
+ */
+ bool IsScrollingOnCompositor(const FrameMetrics& aParentMetrics);
+
+ /**
+ * Check if we should use progressive draw on this layer. We will
+ * disable progressive draw based on a preference or if the layer
+ * is not being scrolled.
+ */
+ bool UseProgressiveDraw();
+
+ /**
+ * Helper function to do the high-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * Helper function to do the low-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * This causes the paint to be marked as finished, and updates any data
+ * necessary to persist until the next paint.
+ */
+ void EndPaint();
+
+ RefPtr<TiledContentClient> mContentClient;
+ // Flag to indicate if mContentClient is a SingleTiledContentClient. This is
+ // only valid when mContentClient is non-null.
+ bool mHaveSingleTiledContentClient;
+ nsIntRegion mLowPrecisionValidRegion;
+ BasicTiledLayerPaintData mPaintData;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/CompositableChild.cpp b/system/graphics/layers/client/CompositableChild.cpp
new file mode 100644
index 000000000..34a0e0696
--- /dev/null
+++ b/system/graphics/layers/client/CompositableChild.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableChild.h"
+#include "CompositableClient.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */ PCompositableChild*
+CompositableChild::CreateActor()
+{
+ CompositableChild* child = new CompositableChild();
+ child->AddRef();
+ return child;
+}
+
+/* static */ void
+CompositableChild::DestroyActor(PCompositableChild* aChild)
+{
+ static_cast<CompositableChild*>(aChild)->Release();
+}
+
+CompositableChild::CompositableChild()
+ : mCompositableClient(nullptr),
+ mAsyncID(0),
+ mCanSend(true)
+{
+ MOZ_COUNT_CTOR(CompositableChild);
+}
+
+CompositableChild::~CompositableChild()
+{
+ MOZ_COUNT_DTOR(CompositableChild);
+}
+
+bool
+CompositableChild::IsConnected() const
+{
+ return mCompositableClient && mCanSend;
+}
+
+void
+CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID)
+{
+ mCompositableClient = aCompositable;
+ mAsyncID = aAsyncID;
+}
+
+void
+CompositableChild::RevokeCompositableClient()
+{
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+CompositableChild::GetCompositableClient()
+{
+ return mCompositableClient;
+}
+
+void
+CompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mCanSend = false;
+
+ if (mCompositableClient) {
+ mCompositableClient->mCompositableChild = nullptr;
+ mCompositableClient = nullptr;
+ }
+}
+
+/* static */ PCompositableChild*
+AsyncCompositableChild::CreateActor()
+{
+ AsyncCompositableChild* child = new AsyncCompositableChild();
+ child->AddRef();
+ return child;
+}
+
+AsyncCompositableChild::AsyncCompositableChild()
+ : mLock("AsyncCompositableChild.mLock")
+{
+}
+
+AsyncCompositableChild::~AsyncCompositableChild()
+{
+}
+
+void
+AsyncCompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ mCanSend = false;
+
+ // We do not revoke CompositableClient::mCompositableChild here, since that
+ // could race with the main thread.
+ RevokeCompositableClient();
+}
+
+void
+AsyncCompositableChild::RevokeCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+AsyncCompositableChild::GetCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ return CompositableChild::GetCompositableClient();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/CompositableChild.h b/system/graphics/layers/client/CompositableChild.h
new file mode 100644
index 000000000..381d8051b
--- /dev/null
+++ b/system/graphics/layers/client/CompositableChild.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_layers_client_CompositableChild_h
+#define mozilla_gfx_layers_client_CompositableChild_h
+
+#include <stdint.h>
+#include "IPDLActor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/layers/PCompositableChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class AsyncCompositableChild;
+
+/**
+ * IPDL actor used by CompositableClient to match with its corresponding
+ * CompositableHost on the compositor side.
+ *
+ * CompositableChild is owned by a CompositableClient.
+ */
+class CompositableChild : public PCompositableChild
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild)
+
+ static PCompositableChild* CreateActor();
+ static void DestroyActor(PCompositableChild* aChild);
+
+ void Init(CompositableClient* aCompositable, uint64_t aAsyncID);
+ virtual void RevokeCompositableClient();
+
+ virtual void ActorDestroy(ActorDestroyReason) override;
+
+ virtual RefPtr<CompositableClient> GetCompositableClient();
+
+ virtual AsyncCompositableChild* AsAsyncCompositableChild() {
+ return nullptr;
+ }
+
+ uint64_t GetAsyncID() const {
+ return mAsyncID;
+ }
+
+ // These should only be called on the IPDL thread.
+ bool IsConnected() const;
+ bool CanSend() const {
+ return mCanSend;
+ }
+
+protected:
+ CompositableChild();
+ virtual ~CompositableChild();
+
+protected:
+ CompositableClient* mCompositableClient;
+ uint64_t mAsyncID;
+ bool mCanSend;
+};
+
+// This CompositableChild can be used off the main thread.
+class AsyncCompositableChild final : public CompositableChild
+{
+public:
+ static PCompositableChild* CreateActor();
+
+ void RevokeCompositableClient() override;
+ RefPtr<CompositableClient> GetCompositableClient() override;
+
+ void ActorDestroy(ActorDestroyReason) override;
+
+ AsyncCompositableChild* AsAsyncCompositableChild() override {
+ return this;
+ }
+
+protected:
+ AsyncCompositableChild();
+ ~AsyncCompositableChild() override;
+
+private:
+ Mutex mLock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_client_CompositableChild_h
diff --git a/system/graphics/layers/client/CompositableClient.cpp b/system/graphics/layers/client/CompositableClient.cpp
new file mode 100644
index 000000000..936ba7abc
--- /dev/null
+++ b/system/graphics/layers/client/CompositableClient.cpp
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositableClient.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/layers/PCompositableChild.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform
+#include "mozilla/layers/TextureD3D11.h"
+#endif
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
+{
+ MOZ_ASSERT(aActor);
+
+ mForwarder->AssertInForwarderThread();
+
+ mCompositableChild = static_cast<CompositableChild*>(aActor);
+ mCompositableChild->Init(this, aAsyncID);
+}
+
+/* static */ RefPtr<CompositableClient>
+CompositableClient::FromIPDLActor(PCompositableChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ RefPtr<CompositableClient> client = static_cast<CompositableChild*>(aActor)->GetCompositableClient();
+ if (!client) {
+ return nullptr;
+ }
+
+ client->mForwarder->AssertInForwarderThread();
+ return client;
+}
+
+CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
+ TextureFlags aTextureFlags)
+: mForwarder(aForwarder)
+, mTextureFlags(aTextureFlags)
+{
+ MOZ_COUNT_CTOR(CompositableClient);
+}
+
+CompositableClient::~CompositableClient()
+{
+ MOZ_COUNT_DTOR(CompositableClient);
+ Destroy();
+}
+
+LayersBackend
+CompositableClient::GetCompositorBackendType() const
+{
+ return mForwarder->GetCompositorBackendType();
+}
+
+PCompositableChild*
+CompositableClient::GetIPDLActor() const
+{
+ return mCompositableChild;
+}
+
+bool
+CompositableClient::Connect(ImageContainer* aImageContainer)
+{
+ MOZ_ASSERT(!mCompositableChild);
+ if (!GetForwarder() || GetIPDLActor()) {
+ return false;
+ }
+
+ GetForwarder()->AssertInForwarderThread();
+ GetForwarder()->Connect(this, aImageContainer);
+ return true;
+}
+
+bool
+CompositableClient::IsConnected() const
+{
+ // CanSend() is only reliable in the same thread as the IPDL channel.
+ mForwarder->AssertInForwarderThread();
+ return mCompositableChild && mCompositableChild->IsConnected();
+}
+
+void
+CompositableClient::Destroy()
+{
+ if (!mCompositableChild) {
+ return;
+ }
+
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->Destroy();
+ }
+
+ // Take away our IPDL's actor reference back to us.
+ mCompositableChild->RevokeCompositableClient();
+
+ // Schedule the IPDL actor to be destroyed on the forwarder's thread.
+ mForwarder->Destroy(mCompositableChild);
+ mCompositableChild = nullptr;
+}
+
+uint64_t
+CompositableClient::GetAsyncID() const
+{
+ if (mCompositableChild) {
+ return mCompositableChild->GetAsyncID();
+ }
+ return 0; // zero is always an invalid async ID
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags)
+{
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aTextureFlags | mTextureFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(GetForwarder(),
+ aFormat, aSize, aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateFromSurface(GetForwarder(),
+ aSurface,
+ aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+bool
+CompositableClient::AddTextureClient(TextureClient* aClient)
+{
+ if(!aClient) {
+ return false;
+ }
+ aClient->SetAddedToCompositableClient();
+ return aClient->InitIPDLActor(mForwarder);
+}
+
+void
+CompositableClient::ClearCachedResources()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::HandleMemoryPressure()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::RemoveTexture(TextureClient* aTexture)
+{
+ mForwarder->RemoveTextureFromCompositable(this, aTexture);
+}
+
+TextureClientRecycleAllocator*
+CompositableClient::GetTextureClientRecycler()
+{
+ if (mTextureClientRecycler) {
+ return mTextureClientRecycler;
+ }
+
+ if (!mForwarder) {
+ return nullptr;
+ }
+
+ if(!mForwarder->GetTextureForwarder()->UsesImageBridge()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ // Handle a case that mForwarder is ImageBridge
+
+ if (InImageBridgeChildThread()) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ ReentrantMonitor barrier("CompositableClient::GetTextureClientRecycler");
+ ReentrantMonitorAutoEnter mainThreadAutoMon(barrier);
+ bool done = false;
+
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ if (!mTextureClientRecycler) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ }
+ ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
+ done = true;
+ barrier.NotifyAll();
+ });
+
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
+
+ // should stop the thread until done.
+ while (!done) {
+ barrier.Wait();
+ }
+
+ return mTextureClientRecycler;
+}
+
+void
+CompositableClient::DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress)
+{
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ if (aCompress == TextureDumpMode::Compress) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ } else {
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+ }
+}
+
+AutoRemoveTexture::~AutoRemoveTexture()
+{
+ if (mCompositable && mTexture && mCompositable->IsConnected()) {
+ mCompositable->RemoveTexture(mTexture);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/CompositableClient.h b/system/graphics/layers/client/CompositableClient.h
new file mode 100644
index 000000000..07df59d59
--- /dev/null
+++ b/system/graphics/layers/client/CompositableClient.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BUFFERCLIENT_H
+#define MOZILLA_GFX_BUFFERCLIENT_H
+
+#include <stdint.h> // for uint64_t
+#include <vector> // for vector
+#include <map> // for map
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ImageBridgeChild;
+class ImageContainer;
+class CompositableForwarder;
+class CompositableChild;
+class PCompositableChild;
+class TextureClientRecycleAllocator;
+
+/**
+ * CompositableClient manages the texture-specific logic for composite layers,
+ * independently of the layer. It is the content side of a CompositableClient/
+ * CompositableHost pair.
+ *
+ * CompositableClient's purpose is to send texture data to the compositor side
+ * along with any extra information about how the texture is to be composited.
+ * Things like opacity or transformation belong to layer and not compositable.
+ *
+ * Since Compositables are independent of layers it is possible to create one,
+ * connect it to the compositor side, and start sending images to it. This alone
+ * is arguably not very useful, but it means that as long as a shadow layer can
+ * do the proper magic to find a reference to the right CompositableHost on the
+ * Compositor side, a Compositable client can be used outside of the main
+ * shadow layer forwarder machinery that is used on the main thread.
+ *
+ * The first step is to create a Compositable client and call Connect().
+ * Connect() creates the underlying IPDL actor (see CompositableChild) and the
+ * corresponding CompositableHost on the other side.
+ *
+ * To do in-transaction texture transfer (the default), call
+ * ShadowLayerForwarder::Attach(CompositableClient*, ShadowableLayer*). This
+ * will let the LayerComposite on the compositor side know which CompositableHost
+ * to use for compositing.
+ *
+ * To do async texture transfer (like async-video), the CompositableClient
+ * should be created with a different CompositableForwarder (like
+ * ImageBridgeChild) and attachment is done with
+ * CompositableForwarder::AttachAsyncCompositable that takes an identifier
+ * instead of a CompositableChild, since the CompositableClient is not managed
+ * by this layer forwarder (the matching uses a global map on the compositor side,
+ * see CompositableMap in ImageBridgeParent.cpp)
+ *
+ * Subclasses: Painted layers use ContentClients, ImageLayers use ImageClients,
+ * Canvas layers use CanvasClients (but ImageHosts). We have a different subclass
+ * where we have a different way of interfacing with the textures - in terms of
+ * drawing into the compositable and/or passing its contents to the compostior.
+ */
+class CompositableClient
+{
+protected:
+ virtual ~CompositableClient();
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableClient)
+
+ explicit CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = TextureFlags::NO_FLAGS);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) {};
+
+ virtual TextureInfo GetTextureInfo() const = 0;
+
+ LayersBackend GetCompositorBackendType() const;
+
+ already_AddRefed<TextureClient>
+ CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ /**
+ * Establishes the connection with compositor side through IPDL
+ */
+ virtual bool Connect(ImageContainer* aImageContainer = nullptr);
+
+ void Destroy();
+
+ bool IsConnected() const;
+
+ PCompositableChild* GetIPDLActor() const;
+
+ CompositableForwarder* GetForwarder() const
+ {
+ return mForwarder;
+ }
+
+ /**
+ * This identifier is what lets us attach async compositables with a shadow
+ * layer. It is not used if the compositable is used with the regular shadow
+ * layer forwarder.
+ *
+ * If this returns zero, it means the compositable is not async (it is used
+ * on the main thread).
+ */
+ uint64_t GetAsyncID() const;
+
+ /**
+ * Tells the Compositor to create a TextureHost for this TextureClient.
+ */
+ virtual bool AddTextureClient(TextureClient* aClient);
+
+ /**
+ * A hook for the when the Compositable is detached from it's layer.
+ */
+ virtual void OnDetach() {}
+
+ /**
+ * Clear any resources that are not immediately necessary. This may be called
+ * in low-memory conditions.
+ */
+ virtual void ClearCachedResources();
+
+ /**
+ * Shrink memory usage.
+ * Called when "memory-pressure" is observed.
+ */
+ virtual void HandleMemoryPressure();
+
+ /**
+ * Should be called when deataching a TextureClient from a Compositable, because
+ * some platforms need to do some extra book keeping when this happens (for
+ * example to properly keep track of fences on Gonk).
+ *
+ * See AutoRemoveTexture to automatically invoke this at the end of a scope.
+ */
+ virtual void RemoveTexture(TextureClient* aTexture);
+
+ static RefPtr<CompositableClient> FromIPDLActor(PCompositableChild* aActor);
+
+ void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
+
+ TextureFlags GetTextureFlags() const { return mTextureFlags; }
+
+ TextureClientRecycleAllocator* GetTextureClientRecycler();
+
+ bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
+
+ static void DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress);
+protected:
+ RefPtr<CompositableChild> mCompositableChild;
+ RefPtr<CompositableForwarder> mForwarder;
+ // Some layers may want to enforce some flags to all their textures
+ // (like disallowing tiling)
+ TextureFlags mTextureFlags;
+ RefPtr<TextureClientRecycleAllocator> mTextureClientRecycler;
+
+ friend class CompositableChild;
+};
+
+/**
+ * Helper to call RemoveTexture at the end of a scope.
+ */
+struct AutoRemoveTexture
+{
+ explicit AutoRemoveTexture(CompositableClient* aCompositable,
+ TextureClient* aTexture = nullptr)
+ : mTexture(aTexture)
+ , mCompositable(aCompositable)
+ {}
+
+ ~AutoRemoveTexture();
+
+ RefPtr<TextureClient> mTexture;
+private:
+ CompositableClient* mCompositable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/ContentClient.cpp b/system/graphics/layers/client/ContentClient.cpp
new file mode 100644
index 000000000..50e159a23
--- /dev/null
+++ b/system/graphics/layers/client/ContentClient.cpp
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentClient.h"
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxUtils.h" // for gfxUtils
+#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget, Factory
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "mozilla/layers/LayersTypes.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLayoutUtils.h"
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+#include "ReadbackLayer.h"
+
+#include <vector>
+
+using namespace std;
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
+{
+ TextureFlags result = TextureFlags::NO_FLAGS;
+
+ if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
+ result |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ return result;
+}
+
+/* static */ already_AddRefed<ContentClient>
+ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
+{
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (backend != LayersBackend::LAYERS_OPENGL &&
+ backend != LayersBackend::LAYERS_D3D9 &&
+ backend != LayersBackend::LAYERS_D3D11 &&
+ backend != LayersBackend::LAYERS_BASIC) {
+ return nullptr;
+ }
+
+ bool useDoubleBuffering = false;
+
+#ifdef XP_WIN
+ if (backend == LayersBackend::LAYERS_D3D11) {
+ useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
+ } else
+#endif
+#ifdef MOZ_WIDGET_GTK
+ // We can't use double buffering when using image content with
+ // Xrender support on Linux, as ContentHostDoubleBuffered is not
+ // suited for direct uploads to the server.
+ // FIXME: Even though the comment above suggests that double buffering
+ // is supposed to be disabled when Xrender support is being enabled
+ // (and used), it really wasn't. Historically,
+ // UseImageOffscreenSurfaces() was always false in GTK2 builds, thus
+ // triggering the check, regardless of UseXRender().
+ // Some time later, offscreen surfaces were always enabled, but the
+ // Xrender functionality broke due to not using Xlib-based surfaces.
+ // Using Xlib-based surfaces compatible with Xrender operations seems
+ // to lead to weird graphical artifacts (bars and stripes) on some
+ // hardware (Intel-based?) when displaying quickly-changing content,
+ // so contrary to the statement above we'd better enable double
+ // buffering - which also seems to not have any negative performance
+ // impact.
+ if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
+ gfxVars::UseXRender())
+#endif
+ {
+ useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() &&
+ backend != LayersBackend::LAYERS_D3D9) ||
+ backend == LayersBackend::LAYERS_BASIC;
+ }
+
+ if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
+ return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
+ }
+ return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
+}
+
+void
+ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+}
+
+void
+ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+// We pass a null pointer for the ContentClient Forwarder argument, which means
+// this client will not have a ContentHost on the other side.
+ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
+ : ContentClient(nullptr)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mBackend(aBackend)
+{}
+
+void
+ContentClientBasic::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
+ }
+
+ IntSize size(aRect.width, aRect.height);
+#ifdef XP_WIN
+ if (mBackend == BackendType::CAIRO &&
+ (aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) {
+ RefPtr<gfxASurface> surf =
+ new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 :
+ gfxImageFormat::A8R8G8B8_UINT32);
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
+
+ if (*aBlackDT) {
+ return;
+ }
+ }
+#endif
+
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
+ mBackend, size,
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+}
+
+void
+ContentClientRemoteBuffer::DestroyBuffers()
+{
+ if (!mTextureClient) {
+ return;
+ }
+
+ mOldTextures.AppendElement(mTextureClient);
+ mTextureClient = nullptr;
+ if (mTextureClientOnWhite) {
+ mOldTextures.AppendElement(mTextureClientOnWhite);
+ mTextureClientOnWhite = nullptr;
+ }
+
+ DestroyFrontBuffer();
+}
+
+class RemoteBufferReadbackProcessor : public TextureReadbackSink
+{
+public:
+ RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
+ const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
+ : mReadbackUpdates(*aReadbackUpdates)
+ , mBufferRect(aBufferRect)
+ , mBufferRotation(aBufferRotation)
+ {
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ mLayerRefs.push_back(mReadbackUpdates[i].mLayer);
+ }
+ }
+
+ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface)
+ {
+ SourceRotatedBuffer rotBuffer(aSourceSurface, nullptr, mBufferRect, mBufferRotation);
+
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = mReadbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+
+ ReadbackSink* sink = update.mLayer->GetSink();
+
+ if (!sink) {
+ continue;
+ }
+
+ if (!aSourceSurface) {
+ sink->SetUnknown(update.mSequenceCounter);
+ continue;
+ }
+
+ RefPtr<DrawTarget> dt =
+ sink->BeginUpdate(update.mUpdateRect + offset, update.mSequenceCounter);
+ if (!dt) {
+ continue;
+ }
+
+ dt->SetTransform(Matrix::Translation(offset.x, offset.y));
+
+ rotBuffer.DrawBufferWithRotation(dt, RotatedBuffer::BUFFER_BLACK);
+
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+
+private:
+ nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
+ // This array is used to keep the layers alive until the callback.
+ vector<RefPtr<Layer>> mLayerRefs;
+
+ IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+};
+
+void
+ContentClientRemoteBuffer::BeginPaint()
+{
+ EnsureBackBufferIfFrontBuffer();
+
+ // XXX: So we might not have a TextureClient yet.. because it will
+ // only be created by CreateBuffer.. which will deliver a locked surface!.
+ if (mTextureClient) {
+ SetBufferProvider(mTextureClient);
+ }
+ if (mTextureClientOnWhite) {
+ SetBufferProviderOnWhite(mTextureClientOnWhite);
+ }
+}
+
+void
+ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+ MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
+
+ // XXX: We might still not have a texture client if PaintThebes
+ // decided we didn't need one yet because the region to draw was empty.
+ SetBufferProvider(nullptr);
+ SetBufferProviderOnWhite(nullptr);
+ for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
+ if (mOldTextures[i]->IsLocked()) {
+ mOldTextures[i]->Unlock();
+ }
+ }
+ mOldTextures.Clear();
+
+ if (mTextureClient && mTextureClient->IsLocked()) {
+ if (aReadbackUpdates->Length() > 0) {
+ RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
+
+ mTextureClient->SetReadbackSink(readbackSink);
+ }
+
+ mTextureClient->Unlock();
+ mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
+ }
+ if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
+ mTextureClientOnWhite->Unlock();
+ mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject());
+ }
+
+ ContentClientRemote::EndPaint(aReadbackUpdates);
+}
+
+void
+ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
+ const IntRect& aRect,
+ uint32_t aFlags)
+{
+ // If we hit this assertion, then it might be due to an empty transaction
+ // followed by a real transaction. Our buffers should be created (but not
+ // painted in the empty transaction) and then painted (but not created) in the
+ // real transaction. That is kind of fragile, and this assert will catch
+ // circumstances where we screw that up, e.g., by unnecessarily recreating our
+ // buffers.
+ MOZ_ASSERT(!mIsNewBuffer,
+ "Bad! Did we create a buffer twice without painting?");
+
+ mIsNewBuffer = true;
+
+ DestroyBuffers();
+
+ mSurfaceFormat = aFormat;
+ mSize = IntSize(aRect.width, aRect.height);
+ mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
+
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ mTextureFlags |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ CreateBackBuffer(mBufferRect);
+}
+
+void
+ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
+{
+ // gfx::BackendType::NONE means fallback to the content backend
+ TextureAllocationFlags textureAllocFlags
+ = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
+
+ mTextureClient = CreateTextureClientForDrawing(
+ mSurfaceFormat, mSize, BackendSelector::Content,
+ mTextureFlags | ExtraTextureFlags(),
+ textureAllocFlags
+ );
+ if (!mTextureClient || !AddTextureClient(mTextureClient)) {
+ AbortTextureClientCreation();
+ return;
+ }
+
+ if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
+ mTextureClientOnWhite = mTextureClient->CreateSimilar(
+ mForwarder->GetCompositorBackendType(),
+ mTextureFlags | ExtraTextureFlags(),
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
+ );
+ if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
+ AbortTextureClientCreation();
+ return;
+ }
+ }
+}
+
+void
+ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
+ if (!mTextureClient) {
+ return;
+ }
+
+ // We just created the textures and we are about to get their draw targets
+ // so we have to lock them here.
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the TextureClient");
+
+ *aBlackDT = mTextureClient->BorrowDrawTarget();
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
+
+ *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
+ }
+}
+
+nsIntRegion
+ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion;
+ if (mIsNewBuffer || aDidSelfCopy) {
+ // A buffer reallocation clears both buffers. The front buffer has all the
+ // content by now, but the back buffer is still clear. Here, in effect, we
+ // are saying to copy all of the pixels of the front buffer to the back.
+ // Also when we self-copied in the buffer, the buffer space
+ // changes and some changed buffer content isn't reflected in the
+ // draw or invalidate region (on purpose!). When this happens, we
+ // need to read back the entire buffer too.
+ updatedRegion = aVisibleRegion.GetBounds();
+ mIsNewBuffer = false;
+ } else {
+ updatedRegion = aRegionToDraw;
+ }
+
+ NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
+ "Update outside of buffer rect!");
+ MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
+
+ return updatedRegion;
+}
+
+void
+ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
+ aVisibleRegion,
+ aDidSelfCopy);
+
+ MOZ_ASSERT(mTextureClient);
+ if (mTextureClientOnWhite) {
+ mForwarder->UseComponentAlphaTextures(this, mTextureClient,
+ mTextureClientOnWhite);
+ } else {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mTextureClient;
+ IntSize size = mTextureClient->GetSize();
+ t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
+ GetForwarder()->UseTextures(this, textures);
+ }
+ mForwarder->UpdateTextureRegion(this,
+ ThebesBufferData(BufferRect(),
+ BufferRotation()),
+ updatedRegion);
+}
+
+void
+ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontAndBackBufferDiffer = true;
+}
+
+void
+ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml, TextureDumpMode aCompress)
+{
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(aStream, mTextureClient, aCompress);
+}
+
+void
+ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml, TextureDumpMode aCompress)
+{
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(aStream, mFrontClient, aCompress);
+}
+
+void
+ContentClientDoubleBuffered::DestroyFrontBuffer()
+{
+ if (mFrontClient) {
+ mOldTextures.AppendElement(mFrontClient);
+ mFrontClient = nullptr;
+ }
+
+ if (mFrontClientOnWhite) {
+ mOldTextures.AppendElement(mFrontClientOnWhite);
+ mFrontClientOnWhite = nullptr;
+ }
+}
+
+void
+ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
+}
+
+void
+ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontUpdatedRegion = aFrontUpdatedRegion;
+
+ RefPtr<TextureClient> oldBack = mTextureClient;
+ mTextureClient = mFrontClient;
+ mFrontClient = oldBack;
+
+ oldBack = mTextureClientOnWhite;
+ mTextureClientOnWhite = mFrontClientOnWhite;
+ mFrontClientOnWhite = oldBack;
+
+ IntRect oldBufferRect = mBufferRect;
+ mBufferRect = mFrontBufferRect;
+ mFrontBufferRect = oldBufferRect;
+
+ nsIntPoint oldBufferRotation = mBufferRotation;
+ mBufferRotation = mFrontBufferRotation;
+ mFrontBufferRotation = oldBufferRotation;
+
+ MOZ_ASSERT(mFrontClient);
+
+ ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion);
+}
+
+void
+ContentClientDoubleBuffered::BeginPaint()
+{
+ ContentClientRemoteBuffer::BeginPaint();
+
+ mIsNewBuffer = false;
+
+ if (!mFrontAndBackBufferDiffer) {
+ return;
+ }
+
+ if (mDidSelfCopy) {
+ // We can't easily draw our front buffer into us, since we're going to be
+ // copying stuff around anyway it's easiest if we just move our situation
+ // to non-rotated while we're at it. If this situation occurs we'll have
+ // hit a self-copy path in PaintThebes before as well anyway.
+ mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
+ mBufferRotation = nsIntPoint();
+ return;
+ }
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+}
+
+// Sync front/back buffers content
+// After executing, the new back buffer has the same (interesting) pixels as
+// the new front buffer, and mValidRegion et al. are correct wrt the new
+// back buffer (i.e. as they were for the old back buffer)
+void
+ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+
+ if (!mFrontAndBackBufferDiffer) {
+ MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
+ return;
+ }
+ MOZ_ASSERT(mFrontClient);
+ if (!mFrontClient) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
+ this,
+ mFrontUpdatedRegion.GetBounds().x,
+ mFrontUpdatedRegion.GetBounds().y,
+ mFrontUpdatedRegion.GetBounds().width,
+ mFrontUpdatedRegion.GetBounds().height));
+
+ mFrontAndBackBufferDiffer = false;
+
+ nsIntRegion updateRegion = mFrontUpdatedRegion;
+ if (mDidSelfCopy) {
+ mDidSelfCopy = false;
+ updateRegion = mBufferRect;
+ }
+
+ // No point in sync'ing what we are going to draw over anyway. And if there is
+ // nothing to sync at all, there is nothing to do and we can go home early.
+ updateRegion.Sub(updateRegion, aRegionToDraw);
+ if (updateRegion.IsEmpty()) {
+ return;
+ }
+
+ // We need to ensure that we lock these two buffers in the same
+ // order as the compositor to prevent deadlocks.
+ TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
+ if (!frontLock.Succeeded()) {
+ return;
+ }
+ Maybe<TextureClientAutoLock> frontOnWhiteLock;
+ if (mFrontClientOnWhite) {
+ frontOnWhiteLock.emplace(mFrontClientOnWhite, OpenMode::OPEN_READ_ONLY);
+ if (!frontOnWhiteLock->Succeeded()) {
+ return;
+ }
+ }
+
+ // Restrict the DrawTargets and frontBuffer to a scope to make
+ // sure there is no more external references to the DrawTargets
+ // when we Unlock the TextureClients.
+ gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget();
+ gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr;
+ if (dt && dt->IsValid()) {
+ RefPtr<SourceSurface> surf = dt->Snapshot();
+ RefPtr<SourceSurface> surfOnWhite = dtw ? dtw->Snapshot() : nullptr;
+ SourceRotatedBuffer frontBuffer(surf,
+ surfOnWhite,
+ mFrontBufferRect,
+ mFrontBufferRotation);
+ UpdateDestinationFrom(frontBuffer, updateRegion);
+ } else {
+ // We know this can happen, but we want to track it somewhat, in case it leads
+ // to other problems.
+ gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw);
+ }
+}
+
+void
+ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
+{
+ if (!mTextureClient && mFrontClient) {
+ CreateBackBuffer(mFrontBufferRect);
+
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+ }
+}
+
+void
+ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion)
+{
+ DrawIterator iter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+
+ if (aSource.HaveBufferOnWhite()) {
+ MOZ_ASSERT(HaveBufferOnWhite());
+ DrawIterator whiteIter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+ }
+}
+
+void
+ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ContentClient.h b/system/graphics/layers/client/ContentClient.h
new file mode 100644
index 000000000..d26f01464
--- /dev/null
+++ b/system/graphics/layers/client/ContentClient.h
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_CONTENTCLIENT_H
+#define MOZILLA_GFX_CONTENTCLIENT_H
+
+#include <stdint.h> // for uint32_t
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "gfxTypes.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "ReadbackProcessor.h" // For ReadbackProcessor::Update
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class PaintedLayer;
+
+/**
+ * A compositable client for PaintedLayers. These are different to Image/Canvas
+ * clients due to sending a valid region across IPC and because we do a lot more
+ * optimisation work, encapsualted in RotatedContentBuffers.
+ *
+ * We use content clients for OMTC and non-OMTC, basic rendering so that
+ * BasicPaintedLayer has only one interface to deal with. We support single and
+ * double buffered flavours. For tiled layers, we do not use a ContentClient
+ * although we do have a ContentHost, and we do use texture clients and texture
+ * hosts.
+ *
+ * The interface presented by ContentClient is used by the BasicPaintedLayer
+ * methods - PaintThebes, which is the same for MT and OMTC, and PaintBuffer
+ * which is different (the OMTC one does a little more). The 'buffer' in the
+ * names of a lot of these method is actually the TextureClient. But, 'buffer'
+ * for the RotatedContentBuffer (as in SetBuffer) means a gfxSurface. See the
+ * comments for SetBuffer and SetBufferProvider in RotatedContentBuffer. To keep
+ * these mapped buffers alive, we store a pointer in mOldTextures if the
+ * RotatedContentBuffer's surface is not the one from our texture client, once we
+ * are done painting we unmap the surface/texture client and don't need to keep
+ * it alive anymore, so we clear mOldTextures.
+ *
+ * The sequence for painting is: call BeginPaint on the content client;
+ * call BeginPaintBuffer on the content client. That will initialise the buffer
+ * for painting, by calling RotatedContentBuffer::BeginPaint (usually) which
+ * will call back to ContentClient::FinalizeFrame to finalize update of the
+ * buffers before drawing (i.e., it finalizes the previous frame). Then call
+ * BorrowDrawTargetForPainting to get a DrawTarget to paint into. Then paint.
+ * Then return that DrawTarget using ReturnDrawTarget.
+ * Call EndPaint on the content client;
+ *
+ * SwapBuffers is called in response to the transaction reply from the compositor.
+ */
+class ContentClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new content client. If necessary, a
+ * message will be sent to the compositor to create a corresponding content
+ * host.
+ */
+ static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
+
+ explicit ContentClient(CompositableForwarder* aForwarder)
+ : CompositableClient(aForwarder)
+ {}
+ virtual ~ContentClient()
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Clear() = 0;
+ virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) = 0;
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
+
+ // Called as part of the layers transation reply. Conveys data about our
+ // buffer(s) from the compositor. If appropriate we should swap references
+ // to our buffers.
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
+
+ // call before and after painting into this content client
+ virtual void BeginPaint() {}
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
+};
+
+/**
+ * A ContentClient for use with OMTC.
+ */
+class ContentClientRemote : public ContentClient
+{
+public:
+ explicit ContentClientRemote(CompositableForwarder* aForwarder)
+ : ContentClient(aForwarder)
+ {}
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) = 0;
+};
+
+// thin wrapper around RotatedContentBuffer, for on-mtc
+class ContentClientBasic final : public ContentClient
+ , protected RotatedContentBuffer
+{
+public:
+ explicit ContentClientBasic(gfx::BackendType aBackend);
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override { RotatedContentBuffer::Clear(); }
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ void DrawTo(PaintedLayer* aLayer,
+ gfx::DrawTarget* aTarget,
+ float aOpacity,
+ gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform)
+ {
+ RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp,
+ aMask, aMaskTransform);
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ MOZ_CRASH("GFX: Should not be called on non-remote ContentClient");
+ }
+
+private:
+ gfx::BackendType mBackend;
+};
+
+/**
+ * A ContentClientRemote backed by a RotatedContentBuffer.
+ *
+ * When using a ContentClientRemote, SurfaceDescriptors are created on
+ * the rendering side and destroyed on the compositing side. They are only
+ * passed from one side to the other when the TextureClient/Hosts are created.
+ * *Ownership* of the SurfaceDescriptor moves from the rendering side to the
+ * compositing side with the create message (send from CreateBuffer) which
+ * tells the compositor that TextureClients have been created and that the
+ * compositor should assign the corresponding TextureHosts to our corresponding
+ * ContentHost.
+ *
+ * If the size or type of our buffer(s) change(s), then we simply destroy and
+ * create them.
+ */
+// Version using new texture clients
+class ContentClientRemoteBuffer : public ContentClientRemote
+ , protected RotatedContentBuffer
+{
+ using RotatedContentBuffer::BufferRect;
+ using RotatedContentBuffer::BufferRotation;
+public:
+ explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder)
+ : ContentClientRemote(aForwarder)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mIsNewBuffer(false)
+ , mFrontAndBackBufferDiffer(false)
+ , mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8)
+ {}
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override
+ {
+ RotatedContentBuffer::Clear();
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ /**
+ * Begin/End Paint map a gfxASurface from the texture client
+ * into the buffer of RotatedBuffer. The surface is only
+ * valid when the texture client is locked, so is mapped out
+ * of RotatedContentBuffer when we are done painting.
+ * None of the underlying buffer attributes (rect, rotation)
+ * are affected by mapping/unmapping.
+ */
+ virtual void BeginPaint() override;
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ // Expose these protected methods from the superclass.
+ virtual const gfx::IntRect& BufferRect() const
+ {
+ return RotatedContentBuffer::BufferRect();
+ }
+ virtual const nsIntPoint& BufferRotation() const
+ {
+ return RotatedContentBuffer::BufferRotation();
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureFlags ExtraTextureFlags() const
+ {
+ return TextureFlags::NO_FLAGS;
+ }
+
+protected:
+ void DestroyBuffers();
+
+ virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy);
+
+ void BuildTextureClients(gfx::SurfaceFormat aFormat,
+ const gfx::IntRect& aRect,
+ uint32_t aFlags);
+
+ void CreateBackBuffer(const gfx::IntRect& aBufferRect);
+
+ // Ensure we have a valid back buffer if we have a valid front buffer (i.e.
+ // if a backbuffer has been created.)
+ virtual void EnsureBackBufferIfFrontBuffer() {}
+
+ // Create the front buffer for the ContentClient/Host pair if necessary
+ // and notify the compositor that we have created the buffer(s).
+ virtual void DestroyFrontBuffer() {}
+
+ virtual void AbortTextureClientCreation()
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mIsNewBuffer = false;
+ }
+
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<TextureClient> mTextureClientOnWhite;
+ // keep a record of texture clients we have created and need to keep around
+ // (for RotatedBuffer to access), then unlock and remove them when we are done
+ // painting.
+ nsTArray<RefPtr<TextureClient> > mOldTextures;
+
+ bool mIsNewBuffer;
+ bool mFrontAndBackBufferDiffer;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mSurfaceFormat;
+};
+
+/**
+ * A double buffered ContentClient. mTextureClient is the back buffer, which
+ * we draw into. mFrontClient is the front buffer which we may read from, but
+ * not write to, when the compositor does not have the 'soft' lock. We can write
+ * into mTextureClient at any time.
+ *
+ * The ContentHost keeps a reference to both corresponding texture hosts, in
+ * response to our UpdateTextureRegion message, the compositor swaps its
+ * references. In response to the compositor's reply we swap our references
+ * (in SwapBuffers).
+ */
+class ContentClientDoubleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientDoubleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {}
+
+ virtual ~ContentClientDoubleBuffered() {}
+
+ virtual void Clear() override
+ {
+ ContentClientRemoteBuffer::Clear();
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ virtual void BeginPaint() override;
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual void EnsureBackBufferIfFrontBuffer() override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+protected:
+ virtual void DestroyFrontBuffer() override;
+
+private:
+ void UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion);
+
+ virtual void AbortTextureClientCreation() override
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ RefPtr<TextureClient> mFrontClient;
+ RefPtr<TextureClient> mFrontClientOnWhite;
+ nsIntRegion mFrontUpdatedRegion;
+ gfx::IntRect mFrontBufferRect;
+ nsIntPoint mFrontBufferRotation;
+};
+
+/**
+ * A single buffered ContentClient. We have a single TextureClient/Host
+ * which we update and then send a message to the compositor that we are
+ * done updating. It is not safe for the compositor to use the corresponding
+ * TextureHost's memory directly, it must upload it to video memory of some
+ * kind. We are free to modify the TextureClient once we receive reply from
+ * the compositor.
+ */
+class ContentClientSingleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientSingleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {
+ }
+ virtual ~ContentClientSingleBuffered() {}
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_SINGLE, mTextureFlags | ExtraTextureFlags());
+ }
+
+ virtual TextureFlags ExtraTextureFlags() const override
+ {
+ return TextureFlags::IMMEDIATE_UPLOAD;
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/GPUVideoTextureClient.cpp b/system/graphics/layers/client/GPUVideoTextureClient.cpp
new file mode 100644
index 000000000..10d2bbf38
--- /dev/null
+++ b/system/graphics/layers/client/GPUVideoTextureClient.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUVideoTextureClient.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+GPUVideoTextureData::GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize)
+ : mManager(aManager)
+ , mSD(aSD)
+ , mSize(aSize)
+{}
+
+GPUVideoTextureData::~GPUVideoTextureData()
+{
+}
+
+bool
+GPUVideoTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = mSD;
+ return true;
+}
+
+void
+GPUVideoTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ // TODO: We should probably try do better for this.
+ // layers::Image doesn't expose a format, so it's hard
+ // to figure out in VideoDecoderParent.
+ aInfo.format = SurfaceFormat::B8G8R8X8;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<SourceSurface>
+GPUVideoTextureData::GetAsSourceSurface()
+{
+ return mManager->Readback(mSD);
+}
+
+void
+GPUVideoTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ mManager->DeallocateSurfaceDescriptorGPUVideo(mSD);
+ mSD = SurfaceDescriptorGPUVideo();
+}
+
+void
+GPUVideoTextureData::Forget(LayersIPCChannel* aAllocator)
+{
+ // We always need to manually deallocate on the client side.
+ // Ideally we'd set up our TextureClient with the DEALLOCATE_CLIENT
+ // flag, but that forces texture destruction to be synchronous.
+ // Instead let's just deallocate from here as well.
+ Deallocate(aAllocator);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/GPUVideoTextureClient.h b/system/graphics/layers/client/GPUVideoTextureClient.h
new file mode 100644
index 000000000..a445e2a7d
--- /dev/null
+++ b/system/graphics/layers/client/GPUVideoTextureClient.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+#define MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+}
+namespace dom {
+class VideoDecoderManagerChild;
+}
+namespace layers {
+
+class GPUVideoTextureData : public TextureData
+{
+public:
+ GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize);
+ ~GPUVideoTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override { return true; };
+
+ virtual void Unlock() override {};
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ virtual void Forget(LayersIPCChannel* aAllocator) override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() override
+ {
+ return this;
+ }
+
+protected:
+ RefPtr<dom::VideoDecoderManagerChild> mManager;
+ SurfaceDescriptorGPUVideo mSD;
+ gfx::IntSize mSize;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
diff --git a/system/graphics/layers/client/ImageClient.cpp b/system/graphics/layers/client/ImageClient.cpp
new file mode 100644
index 000000000..e5a725c27
--- /dev/null
+++ b/system/graphics/layers/client/ImageClient.cpp
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageClient.h"
+
+#include <stdint.h> // for uint32_t
+
+#include "ClientLayerManager.h" // for ClientLayer
+#include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc
+#include "GLImages.h" // for SurfaceTextureImage::Data, etc
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h" // for SurfaceTextureClient
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/* static */ already_AddRefed<ImageClient>
+ImageClient::CreateImageClient(CompositableType aCompositableHostType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ RefPtr<ImageClient> result = nullptr;
+ switch (aCompositableHostType) {
+ case CompositableType::IMAGE:
+ result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
+ break;
+ case CompositableType::IMAGE_BRIDGE:
+ result = new ImageClientBridge(aForwarder, aFlags);
+ break;
+ case CompositableType::UNKNOWN:
+ result = nullptr;
+ break;
+ default:
+ MOZ_CRASH("GFX: unhandled program type image");
+ }
+
+ NS_ASSERTION(result, "Failed to create ImageClient");
+
+ return result.forget();
+}
+
+void
+ImageClient::RemoveTexture(TextureClient* aTexture)
+{
+ GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
+}
+
+ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType)
+ : ImageClient(aFwd, aFlags, aType)
+{
+}
+
+TextureInfo ImageClientSingle::GetTextureInfo() const
+{
+ return TextureInfo(CompositableType::IMAGE);
+}
+
+void
+ImageClientSingle::FlushAllImages()
+{
+ MOZ_ASSERT(GetForwarder()->GetTextureForwarder()->UsesImageBridge());
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.Clear();
+}
+
+/* static */ already_AddRefed<TextureClient>
+ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder)
+{
+ RefPtr<TextureClient> texture;
+ if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+ PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
+ const PlanarYCbCrData* data = ycbcr->GetData();
+ if (!data) {
+ return nullptr;
+ }
+ texture = TextureClient::CreateForYCbCr(aForwarder,
+ data->mYSize, data->mCbCrSize, data->mStereoMode,
+ data->mYUVColorSpace,
+ TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ bool status = UpdateYCbCrTextureClient(texture, *data);
+ MOZ_ASSERT(status);
+ if (!status) {
+ return nullptr;
+ }
+ } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
+ aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ gfx::IntSize size = aImage->GetSize();
+
+ if (aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ EGLImageImage* typedImage = aImage->AsEGLImageImage();
+ texture = EGLImageTextureData::CreateTextureClient(
+ typedImage, size, aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
+ } else {
+ MOZ_ASSERT(false, "Bad ImageFormat.");
+ }
+ } else {
+ RefPtr<gfx::SourceSurface> surface = aImage->GetAsSourceSurface();
+ MOZ_ASSERT(surface);
+ texture = TextureClient::CreateForDrawing(aForwarder, surface->GetFormat(), aImage->GetSize(),
+ BackendSelector::Content, TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(texture->CanExposeDrawTarget());
+
+ if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+ return nullptr;
+ }
+
+ {
+ // We must not keep a reference to the DrawTarget after it has been unlocked.
+ DrawTarget* dt = texture->BorrowDrawTarget();
+ if (!dt) {
+ gfxWarning() << "ImageClientSingle::UpdateImage failed in BorrowDrawTarget";
+ return nullptr;
+ }
+ MOZ_ASSERT(surface.get());
+ dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
+ }
+
+ texture->Unlock();
+ }
+ return texture.forget();
+}
+
+bool
+ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ AutoTArray<ImageContainer::OwningImage,4> images;
+ uint32_t generationCounter;
+ aContainer->GetCurrentImages(&images, &generationCounter);
+
+ if (mLastUpdateGenerationCounter == generationCounter) {
+ return true;
+ }
+ mLastUpdateGenerationCounter = generationCounter;
+
+ for (int32_t i = images.Length() - 1; i >= 0; --i) {
+ if (!images[i].mImage->IsValid()) {
+ // Don't try to update to an invalid image.
+ images.RemoveElementAt(i);
+ }
+ }
+ if (images.IsEmpty()) {
+ // This can happen if a ClearAllImages raced with SetCurrentImages from
+ // another thread and ClearImagesFromImageBridge ran after the
+ // SetCurrentImages call but before UpdateImageClientNow.
+ // This can also happen if all images in the list are invalid.
+ // We return true because the caller would attempt to recreate the
+ // ImageClient otherwise, and that isn't going to help.
+ return true;
+ }
+
+ nsTArray<Buffer> newBuffers;
+ AutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
+
+ for (auto& img : images) {
+ Image* image = img.mImage;
+
+ RefPtr<TextureClient> texture = image->GetTextureClient(GetForwarder());
+ const bool hasTextureClient = !!texture;
+
+ for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
+ if (mBuffers[i].mImageSerial == image->GetSerial()) {
+ if (hasTextureClient) {
+ MOZ_ASSERT(image->GetTextureClient(GetForwarder()) == mBuffers[i].mTextureClient);
+ } else {
+ texture = mBuffers[i].mTextureClient;
+ }
+ // Remove this element from mBuffers so mBuffers only contains
+ // images that aren't present in 'images'
+ mBuffers.RemoveElementAt(i);
+ }
+ }
+
+ if (!texture) {
+ // Slow path, we should not be hitting it very often and if we do it means
+ // we are using an Image class that is not backed by textureClient and we
+ // should fix it.
+ texture = CreateTextureClientForImage(image, GetForwarder());
+ }
+ if (!texture || !AddTextureClient(texture)) {
+ return false;
+ }
+
+
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = texture;
+ t->mTimeStamp = img.mTimeStamp;
+ t->mPictureRect = image->GetPictureRect();
+ t->mFrameID = img.mFrameID;
+ t->mProducerID = img.mProducerID;
+
+ Buffer* newBuf = newBuffers.AppendElement();
+ newBuf->mImageSerial = image->GetSerial();
+ newBuf->mTextureClient = texture;
+
+ texture->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ GetForwarder()->UseTextures(this, textures);
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.SwapElements(newBuffers);
+
+ return true;
+}
+
+bool
+ImageClientSingle::AddTextureClient(TextureClient* aTexture)
+{
+ MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
+ return CompositableClient::AddTextureClient(aTexture);
+}
+
+void
+ImageClientSingle::OnDetach()
+{
+ mBuffers.Clear();
+}
+
+ImageClient::ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType)
+: CompositableClient(aFwd, aFlags)
+, mLayer(nullptr)
+, mType(aType)
+, mLastUpdateGenerationCounter(0)
+{}
+
+ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags)
+: ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
+, mAsyncContainerID(0)
+{
+}
+
+bool
+ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ if (!GetForwarder() || !mLayer) {
+ return false;
+ }
+ if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
+ return true;
+ }
+ mAsyncContainerID = aContainer->GetAsyncContainerID();
+ static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/ImageClient.h b/system/graphics/layers/client/ImageClient.h
new file mode 100644
index 000000000..4c6e26400
--- /dev/null
+++ b/system/graphics/layers/client/ImageClient.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGECLIENT_H
+#define MOZILLA_GFX_IMAGECLIENT_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayer;
+class CompositableForwarder;
+class Image;
+class ImageContainer;
+class ShadowableLayer;
+class ImageClientSingle;
+
+/**
+ * Image clients are used by basic image layers on the content thread, they
+ * always match with an ImageHost on the compositor thread. See
+ * CompositableClient.h for information on connecting clients to hosts.
+ */
+class ImageClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new image client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ static already_AddRefed<ImageClient> CreateImageClient(CompositableType aImageHostType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual ~ImageClient() {}
+
+ /**
+ * Update this ImageClient from aContainer in aLayer
+ * returns false if this is the wrong kind of ImageClient for aContainer.
+ * Note that returning true does not necessarily imply success
+ */
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
+
+ void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
+ ClientLayer* GetLayer() const { return mLayer; }
+
+ /**
+ * asynchronously remove all the textures used by the image client.
+ *
+ */
+ virtual void FlushAllImages() {}
+
+ virtual void RemoveTexture(TextureClient* aTexture) override;
+
+ virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
+
+ static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
+
+protected:
+ ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType);
+
+ ClientLayer* mLayer;
+ CompositableType mType;
+ uint32_t mLastUpdateGenerationCounter;
+};
+
+/**
+ * An image client which uses a single texture client.
+ */
+class ImageClientSingle : public ImageClient
+{
+public:
+ ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+
+ virtual void OnDetach() override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override;
+
+ virtual TextureInfo GetTextureInfo() const override;
+
+ virtual void FlushAllImages() override;
+
+ ImageClientSingle* AsImageClientSingle() override { return this; }
+
+protected:
+ struct Buffer {
+ RefPtr<TextureClient> mTextureClient;
+ int32_t mImageSerial;
+ };
+ nsTArray<Buffer> mBuffers;
+};
+
+/**
+ * Image class to be used for async image uploads using the image bridge
+ * protocol.
+ * We store the ImageBridge id in the TextureClientIdentifier.
+ */
+class ImageClientBridge : public ImageClient
+{
+public:
+ ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+ virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(mType);
+ }
+
+protected:
+ uint64_t mAsyncContainerID;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/SingleTiledContentClient.cpp b/system/graphics/layers/client/SingleTiledContentClient.cpp
new file mode 100644
index 000000000..bcc8691cf
--- /dev/null
+++ b/system/graphics/layers/client/SingleTiledContentClient.cpp
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/SingleTiledContentClient.h"
+
+#include "ClientTiledPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+
+SingleTiledContentClient::SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Single")
+{
+ MOZ_COUNT_CTOR(SingleTiledContentClient);
+
+ mTiledBuffer = new ClientSingleTiledLayerBuffer(aPaintedLayer, *this, aManager);
+}
+
+void
+SingleTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer->DiscardBuffers();
+}
+
+void
+SingleTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ mForwarder->UseTiledLayerBuffer(this, mTiledBuffer->GetSurfaceDescriptorTiles());
+ mTiledBuffer->ClearPaintedRegion();
+}
+
+/* static */ bool
+SingleTiledContentClient::ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager)
+{
+ int32_t maxTextureSize = aManager->GetMaxTextureSize();
+ return aSize.width <= maxTextureSize && aSize.height <= maxTextureSize;
+}
+
+ClientSingleTiledLayerBuffer::ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mWasLastPaintProgressive(false)
+ , mFormat(gfx::SurfaceFormat::UNKNOWN)
+{
+}
+
+void
+ClientSingleTiledLayerBuffer::ReleaseTiles()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardBuffers();
+ }
+ mTile.SetTextureAllocator(nullptr);
+}
+
+void
+ClientSingleTiledLayerBuffer::DiscardBuffers()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardFrontBuffer();
+ mTile.DiscardBackBuffer();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientSingleTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ TileDescriptor tileDesc = mTile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ mTile.mUpdateRect = gfx::IntRect();
+
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTilingOrigin,
+ mSize,
+ 0, 0, 1, 1,
+ 1.0,
+ mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+already_AddRefed<TextureClient>
+ClientSingleTiledLayerBuffer::GetTextureClient()
+{
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN);
+ return mCompositableClient.CreateTextureClientForDrawing(
+ gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content,
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD);
+}
+
+void
+ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ mWasLastPaintProgressive = aIsProgressive;
+
+ // Compare layer valid region size to current backbuffer size, discard if not matching.
+ gfx::IntSize size = aNewValidRegion.GetBounds().Size();
+ gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
+ nsIntRegion paintRegion = aPaintRegion;
+
+ RefPtr<TextureClient> discardedFrontBuffer = nullptr;
+ RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
+ nsIntRegion discardedValidRegion;
+
+ if (mSize != size ||
+ mTilingOrigin != origin) {
+ discardedFrontBuffer = mTile.mFrontBuffer;
+ discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
+ discardedValidRegion = mValidRegion;
+
+ TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
+;
+ ResetPaintedAndValidState();
+ mSize = size;
+ mTilingOrigin = origin;
+ paintRegion = aNewValidRegion;
+ }
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);
+
+ if (mTile.IsPlaceholderTile()) {
+ mTile.SetTextureAllocator(this);
+ }
+
+ // The dirty region relative to the top-left of the tile.
+ nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ mTile.GetBackBuffer(mCompositableClient,
+ tileDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(mTilingOrigin);
+ extraPainted.And(extraPainted, aNewValidRegion);
+ mPaintedRegion.OrWith(paintRegion);
+ mPaintedRegion.OrWith(extraPainted);
+
+ if (!backBuffer) {
+ return;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<gfx::DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ }
+
+ if (mode != SurfaceMode::SURFACE_OPAQUE) {
+ for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect& rect = iter.Get();
+ if (dtOnWhite) {
+ dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
+ } else {
+ dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
+ }
+ }
+ }
+
+ // If the old frontbuffer was discarded then attempt to copy what we
+ // can from it to the new backbuffer.
+ if (discardedFrontBuffer) {
+ nsIntRegion copyableRegion;
+ copyableRegion.And(aNewValidRegion, discardedValidRegion);
+ copyableRegion.SubOut(aDirtyRegion);
+
+ if (!copyableRegion.IsEmpty()) {
+ TextureClientAutoLock frontLock(discardedFrontBuffer,
+ OpenMode::OPEN_READ);
+ if (frontLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+ discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
+ }
+ }
+
+ if (discardedFrontBufferOnWhite && backBufferOnWhite) {
+ TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
+ OpenMode::OPEN_READ);
+ if (frontOnWhiteLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+
+ discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
+ &rect, &dest);
+ }
+ }
+ }
+
+ TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
+
+ // We don't need to repaint valid content that was just copied.
+ paintRegion.SubOut(copyableRegion);
+ }
+ }
+
+ if (dtOnWhite) {
+ dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ dtOnWhite = nullptr;
+ }
+
+ {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
+ if (!ctx) {
+ gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+ return;
+ }
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
+
+ aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
+ }
+
+ // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
+ // now out of sync.
+ mTile.mInvalidFront.OrWith(tileDirtyRegion);
+
+ // The new buffer is now validated, remove the dirty region from it.
+ mTile.mInvalidBack.SubOut(tileDirtyRegion);
+
+ dt = nullptr;
+
+ mTile.Flip();
+ UnlockTile(mTile);
+
+ if (backBuffer->HasIntermediateBuffer()) {
+ // If our new buffer has an internal buffer, we don't want to keep another
+ // TextureClient around unnecessarily, so discard the back-buffer.
+ mTile.DiscardBackBuffer();
+ }
+
+ mValidRegion = aNewValidRegion;
+ mLastPaintSurfaceMode = mode;
+ mLastPaintContentType = content;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/SingleTiledContentClient.h b/system/graphics/layers/client/SingleTiledContentClient.h
new file mode 100644
index 000000000..e1706bd90
--- /dev/null
+++ b/system/graphics/layers/client/SingleTiledContentClient.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+
+#include "TiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientSingleTiledLayerBuffer
+ : public ClientTiledLayerBuffer
+ , public TextureClientAllocator
+{
+ virtual ~ClientSingleTiledLayerBuffer() {}
+public:
+ ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager);
+
+ // TextureClientAllocator
+ already_AddRefed<TextureClient> GetTextureClient() override;
+ void ReturnTextureClientDeferred(TextureClient* aClient) override {}
+ void ReportClientLost() override {}
+
+ // ClientTiledLayerBuffer
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ bool SupportsProgressiveUpdate() override { return false; }
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override
+ {
+ MOZ_ASSERT(false, "ProgressiveUpdate not supported!");
+ return false;
+ }
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTile.DiscardBuffers();
+ }
+
+ const nsIntRegion& GetValidRegion() override {
+ return mValidRegion;
+ }
+
+ bool IsLowPrecision() const override {
+ return false;
+ }
+
+ void ReleaseTiles();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void ClearPaintedRegion() {
+ mPaintedRegion.SetEmpty();
+ }
+
+private:
+ TileClient mTile;
+
+ nsIntRegion mPaintedRegion;
+ nsIntRegion mValidRegion;
+ bool mWasLastPaintProgressive;
+
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ gfx::IntSize mSize;
+ gfxImageFormat mFormat;
+};
+
+class SingleTiledContentClient : public TiledContentClient
+{
+public:
+ SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~SingleTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(SingleTiledContentClient);
+
+ mTiledBuffer->ReleaseTiles();
+ }
+
+public:
+ static bool ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager);
+
+ virtual void ClearCachedResources() override;
+
+ virtual void UpdatedBuffer(TiledBufferType aType) override;
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() override { return mTiledBuffer; }
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override { return nullptr; }
+
+private:
+ RefPtr<ClientSingleTiledLayerBuffer> mTiledBuffer;
+};
+
+}
+}
+
+#endif
diff --git a/system/graphics/layers/client/TextureClient.cpp b/system/graphics/layers/client/TextureClient.cpp
new file mode 100644
index 000000000..e8139e9a9
--- /dev/null
+++ b/system/graphics/layers/client/TextureClient.cpp
@@ -0,0 +1,1686 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/TextureClient.h"
+#include <stdint.h> // for uint8_t, uint32_t, etc
+#include "Layers.h" // for Layer, etc
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/Mutex.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "ImageContainer.h" // for PlanarYCbCrData, etc
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // for CreateDataSourceSurfaceByCloning
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "LayersLogging.h" // for AppendToString
+#include "gfxUtils.h" // for gfxUtils::GetAsLZ4Base64Str
+#include "IPDLActor.h"
+#include "BufferTexture.h"
+#include "gfxPrefs.h"
+#include "mozilla/layers/ShadowLayers.h"
+
+#ifdef XP_WIN
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureDIB.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#endif
+#ifdef MOZ_X11
+#include "mozilla/layers/TextureClientX11.h"
+#ifdef GL_PROVIDER_GLX
+#include "GLXLibrary.h"
+#endif
+#endif
+
+#if 0
+#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#define RECYCLE_LOG(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+struct TextureDeallocParams
+{
+ TextureData* data;
+ RefPtr<TextureChild> actor;
+ RefPtr<LayersIPCChannel> allocator;
+ bool clientDeallocation;
+ bool syncDeallocation;
+ bool workAroundSharedSurfaceOwnershipIssue;
+};
+
+void DeallocateTextureClient(TextureDeallocParams params);
+
+/**
+ * TextureChild is the content-side incarnation of the PTexture IPDL actor.
+ *
+ * TextureChild is used to synchronize a texture client and its corresponding
+ * TextureHost if needed (a TextureClient that is not shared with the compositor
+ * does not have a TextureChild)
+ *
+ * During the deallocation phase, a TextureChild may hold its recently destroyed
+ * TextureClient's data until the compositor side confirmed that it is safe to
+ * deallocte or recycle the it.
+ */
+class TextureChild final : PTextureChild
+{
+ ~TextureChild()
+ {
+ // We should have deallocated mTextureData in ActorDestroy
+ MOZ_ASSERT(!mTextureData);
+ MOZ_ASSERT_IF(!mOwnerCalledDestroy, !mTextureClient);
+ }
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
+
+ TextureChild()
+ : mCompositableForwarder(nullptr)
+ , mTextureForwarder(nullptr)
+ , mTextureClient(nullptr)
+ , mTextureData(nullptr)
+ , mDestroyed(false)
+ , mMainThreadOnly(false)
+ , mIPCOpen(false)
+ , mOwnsTextureData(false)
+ , mOwnerCalledDestroy(false)
+ {}
+
+ bool Recv__delete__() override { return true; }
+
+ LayersIPCChannel* GetAllocator() { return mTextureForwarder; }
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool IPCOpen() const { return mIPCOpen; }
+
+ void Lock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Enter(); } }
+
+ void Unlock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Leave(); } }
+
+private:
+
+ // AddIPDLReference and ReleaseIPDLReference are only to be called by CreateIPDLActor
+ // and DestroyIPDLActor, respectively. We intentionally make them private to prevent misuse.
+ // The purpose of these methods is to be aware of when the IPC system around this
+ // actor goes down: mIPCOpen is then set to false.
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+
+ /// The normal way to destroy the actor.
+ ///
+ /// This will asynchronously send a Destroy message to the parent actor, whom
+ /// will send the delete message.
+ void Destroy(const TextureDeallocParams& aParams);
+
+ /// The ugly and slow way to destroy the actor.
+ ///
+ /// This will block until the Parent actor has handled the Destroy message,
+ /// and then start the asynchronous handshake (and destruction will already
+ /// be done on the parent side, when the async part happens).
+ void DestroySynchronously(const TextureDeallocParams& aParams);
+
+ // This lock is used order to prevent several threads to access the
+ // TextureClient's data concurrently. In particular, it prevents shutdown
+ // code to destroy a texture while another thread is reading or writing into
+ // it.
+ // In most places, the lock is held in short and bounded scopes in which we
+ // don't block on any other resource. There are few exceptions to this, which
+ // are discussed below.
+ //
+ // The locking pattern of TextureClient may in some case upset deadlock detection
+ // tools such as TSan.
+ // Typically our tile rendering code will lock all of its tiles, render into them
+ // and unlock them all right after that, which looks something like:
+ //
+ // Lock tile A
+ // Lock tile B
+ // Lock tile C
+ // Apply drawing commands to tiles A, B and C
+ // Unlock tile A
+ // Unlock tile B
+ // Unlock tile C
+ //
+ // And later, we may end up rendering a tile buffer that has the same tiles,
+ // in a different order, for example:
+ //
+ // Lock tile B
+ // Lock tile A
+ // Lock tile D
+ // Apply drawing commands to tiles A, B and D
+ // Unlock tile B
+ // Unlock tile A
+ // Unlock tile D
+ //
+ // This is because textures being expensive to create, we recycle them as much
+ // as possible and they may reappear in the tile buffer in a different order.
+ //
+ // Unfortunately this is not very friendly to TSan's analysis, which will see
+ // that B was once locked while A was locked, and then A locked while B was
+ // locked. TSan identifies this as a potential dead-lock which would be the
+ // case if this kind of inconsistent and dependent locking order was happening
+ // concurrently.
+ // In the case of TextureClient, dependent locking only ever happens on the
+ // thread that draws into the texture (let's call it the producer thread). Other
+ // threads may call into a method that can lock the texture in a short and
+ // bounded scope inside of which it is not allowed to do anything that could
+ // cause the thread to block. A given texture can only have one producer thread.
+ //
+ // Another example of TSan-unfriendly locking pattern is when copying a texture
+ // into another, which also never happens outside of the producer thread.
+ // Copying A into B looks like this:
+ //
+ // Lock texture B
+ // Lock texture A
+ // Copy A into B
+ // Unlock A
+ // Unlock B
+ //
+ // In a given frame we may need to copy A into B and in another frame copy
+ // B into A. For example A and B can be the Front and Back buffers, alternating
+ // roles and the copy is needed to avoid the cost of re-drawing the valid
+ // region.
+ //
+ // The important rule is that all of the dependent locking must occur only
+ // in the texture's producer thread to avoid deadlocks.
+ mutable gfx::CriticalSection mLock;
+
+ RefPtr<CompositableForwarder> mCompositableForwarder;
+ RefPtr<TextureForwarder> mTextureForwarder;
+
+ TextureClient* mTextureClient;
+ TextureData* mTextureData;
+ Atomic<bool> mDestroyed;
+ bool mMainThreadOnly;
+ bool mIPCOpen;
+ bool mOwnsTextureData;
+ bool mOwnerCalledDestroy;
+
+ friend class TextureClient;
+ friend void DeallocateTextureClient(TextureDeallocParams params);
+};
+
+
+static void DestroyTextureData(TextureData* aTextureData, LayersIPCChannel* aAllocator,
+ bool aDeallocate, bool aMainThreadOnly)
+{
+ if (!aTextureData) {
+ return;
+ }
+
+ if (aMainThreadOnly && !NS_IsMainThread()) {
+ RefPtr<LayersIPCChannel> allocatorRef = aAllocator;
+ NS_DispatchToMainThread(NS_NewRunnableFunction([aTextureData, allocatorRef, aDeallocate]() -> void {
+ DestroyTextureData(aTextureData, allocatorRef, aDeallocate, true);
+ }));
+ return;
+ }
+
+ if (aDeallocate) {
+ aTextureData->Deallocate(aAllocator);
+ } else {
+ aTextureData->Forget(aAllocator);
+ }
+ delete aTextureData;
+}
+
+void
+TextureChild::ActorDestroy(ActorDestroyReason why)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+
+ if (mTextureData) {
+ DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly);
+ mTextureData = nullptr;
+ }
+}
+
+void
+TextureChild::Destroy(const TextureDeallocParams& aParams)
+{
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ // DestroyTextureData will be called by TextureChild::ActorDestroy
+ mTextureData = aParams.data;
+ mOwnsTextureData = aParams.clientDeallocation;
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, false))
+ {
+ this->SendDestroy();
+ }
+}
+
+void
+TextureChild::DestroySynchronously(const TextureDeallocParams& aParams)
+{
+ MOZ_PERFORMANCE_WARNING("gfx", "TextureClient/Host pair requires synchronous deallocation");
+
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ DestroyTextureData(
+ aParams.data,
+ aParams.allocator,
+ aParams.clientDeallocation,
+ mMainThreadOnly);
+
+ if (!IPCOpen()) {
+ return;
+ }
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, true))
+ {
+ this->SendDestroySync();
+ this->SendDestroy();
+ }
+}
+
+/* static */ Atomic<uint64_t> TextureClient::sSerialCounter(0);
+
+void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
+ ReentrantMonitor* aBarrier, bool* aDone)
+{
+ DeallocateTextureClient(params);
+ ReentrantMonitorAutoEnter autoMon(*aBarrier);
+ *aDone = true;
+ aBarrier->NotifyAll();
+}
+
+/// The logic for synchronizing a TextureClient's deallocation goes here.
+///
+/// This funciton takes care of dispatching work to the right thread using
+/// a synchronous proxy if needed, and handles client/host deallocation.
+void
+DeallocateTextureClient(TextureDeallocParams params)
+{
+ if (!params.actor && !params.data) {
+ // Nothing to do
+ return;
+ }
+
+ TextureChild* actor = params.actor;
+ MessageLoop* ipdlMsgLoop = nullptr;
+
+ if (params.allocator) {
+ ipdlMsgLoop = params.allocator->GetMessageLoop();
+ if (!ipdlMsgLoop) {
+ // An allocator with no message loop means we are too late in the shutdown
+ // sequence.
+ gfxCriticalError() << "Texture deallocated too late during shutdown";
+ return;
+ }
+ }
+
+ // First make sure that the work is happening on the IPDL thread.
+ if (ipdlMsgLoop && MessageLoop::current() != ipdlMsgLoop) {
+ if (params.syncDeallocation) {
+ bool done = false;
+ ReentrantMonitor barrier("DeallocateTextureClient");
+ ReentrantMonitorAutoEnter autoMon(barrier);
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClientSyncProxy,
+ params, &barrier, &done));
+ while (!done) {
+ barrier.Wait();
+ }
+ } else {
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClient,
+ params));
+ }
+ // The work has been forwarded to the IPDL thread, we are done.
+ return;
+ }
+
+ // Below this line, we are either in the IPDL thread or ther is no IPDL
+ // thread anymore.
+
+ if (!ipdlMsgLoop) {
+ // If we don't have a message loop we can't know for sure that we are in
+ // the IPDL thread and use the LayersIPCChannel.
+ // This should ideally not happen outside of gtest, but some shutdown raciness
+ // could put us in this situation.
+ params.allocator = nullptr;
+ }
+
+ if (!actor) {
+ // We don't have an IPDL actor, probably because we destroyed the TextureClient
+ // before sharing it with the compositor. It means the data cannot be owned by
+ // the TextureHost since we never created the TextureHost...
+ // ..except if the lovely mWorkaroundAnnoyingSharedSurfaceOwnershipIssues member
+ // is set to true. In this case we are in a special situation where this
+ // TextureClient is in wrapped into another TextureClient which assumes it owns
+ // our data. This is specific to the gralloc SharedSurface.
+ bool shouldDeallocate = !params.workAroundSharedSurfaceOwnershipIssue;
+ DestroyTextureData(params.data, params.allocator,
+ shouldDeallocate,
+ false); // main-thread deallocation
+ return;
+ }
+
+ if (params.syncDeallocation || !actor->IPCOpen()) {
+ actor->DestroySynchronously(params);
+ } else {
+ actor->Destroy(params);
+ }
+}
+
+void TextureClient::Destroy(bool aForceSync)
+{
+ if (mActor && !mIsLocked) {
+ mActor->Lock();
+ }
+
+ mBorrowedDrawTarget = nullptr;
+ mReadLock = nullptr;
+
+ RefPtr<TextureChild> actor = mActor;
+ mActor = nullptr;
+
+ if (actor && !actor->mDestroyed.compareExchange(false, true)) {
+ actor->Unlock();
+ actor = nullptr;
+ }
+
+ TextureData* data = mData;
+ if (!mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ mData = nullptr;
+ }
+
+ if (data || actor) {
+ TextureDeallocParams params;
+ params.actor = actor;
+ params.allocator = mAllocator;
+ params.clientDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
+ params.workAroundSharedSurfaceOwnershipIssue = mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+ if (mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ params.data = nullptr;
+ } else {
+ params.data = data;
+ }
+ // At the moment we always deallocate synchronously when deallocating on the
+ // client side, but having asynchronous deallocate in some of the cases will
+ // be a worthwhile optimization.
+ params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT) || aForceSync;
+
+ // Release the lock before calling DeallocateTextureClient because the latter
+ // may wait for the main thread which could create a dead-lock.
+
+ if (actor) {
+ actor->Unlock();
+ }
+
+ DeallocateTextureClient(params);
+ }
+}
+
+void
+TextureClient::LockActor() const
+{
+ if (mActor) {
+ mActor->Lock();
+ }
+}
+
+void
+TextureClient::UnlockActor() const
+{
+ if (mActor) {
+ mActor->Unlock();
+ }
+}
+
+bool
+TextureClient::IsReadLocked() const
+{
+ return mReadLock && mReadLock->GetReadCount() > 1;
+}
+
+bool
+TextureClient::Lock(OpenMode aMode)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(!mIsLocked);
+ if (!IsValid()) {
+ return false;
+ }
+ if (mIsLocked) {
+ return mOpenMode == aMode;
+ }
+
+ if (aMode & OpenMode::OPEN_WRITE && IsReadLocked()) {
+ NS_WARNING("Attempt to Lock a texture that is being read by the compositor!");
+ return false;
+ }
+
+ LockActor();
+
+ mIsLocked = mData->Lock(aMode);
+ mOpenMode = aMode;
+
+ auto format = GetFormat();
+ if (mIsLocked && CanExposeDrawTarget() &&
+ aMode == OpenMode::OPEN_READ_WRITE &&
+ NS_IsMainThread() &&
+ // the formats that we apparently expect, in the cairo backend. Any other
+ // format will trigger an assertion in GfxFormatToCairoFormat.
+ (format == SurfaceFormat::A8R8G8B8_UINT32 ||
+ format == SurfaceFormat::X8R8G8B8_UINT32 ||
+ format == SurfaceFormat::A8 ||
+ format == SurfaceFormat::R5G6B5_UINT16)) {
+ if (!BorrowDrawTarget()) {
+ // Failed to get a DrawTarget, means we won't be able to write into the
+ // texture, might as well fail now.
+ Unlock();
+ return false;
+ }
+ }
+
+ if (!mIsLocked) {
+ UnlockActor();
+ }
+
+ return mIsLocked;
+}
+
+void
+TextureClient::Unlock()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ if (!IsValid() || !mIsLocked) {
+ return;
+ }
+
+ if (mBorrowedDrawTarget) {
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mBorrowedDrawTarget->Flush();
+ if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
+ // Fallback implementation for reading back, because mData does not
+ // have a backend-specific implementation and returned false.
+ RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
+ mReadbackSink->ProcessReadback(dataSurf);
+ }
+ }
+
+ mBorrowedDrawTarget->DetachAllSnapshots();
+ // If this assertion is hit, it means something is holding a strong reference
+ // to our DrawTarget externally, which is not allowed.
+ MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
+
+ mBorrowedDrawTarget = nullptr;
+ }
+
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mUpdated = true;
+ }
+
+ if (mData) {
+ mData->Unlock();
+ }
+ mIsLocked = false;
+ mOpenMode = OpenMode::OPEN_NONE;
+
+ UnlockActor();
+}
+
+void
+TextureClient::EnableReadLock()
+{
+ if (!mReadLock) {
+ mReadLock = TextureReadLock::Create(mAllocator);
+ }
+}
+
+void
+TextureClient::SerializeReadLock(ReadLockDescriptor& aDescriptor)
+{
+ if (mReadLock && mUpdated) {
+ // Take a read lock on behalf of the TextureHost. The latter will unlock
+ // after the shared data is available again for drawing.
+ mReadLock->ReadLock();
+ mReadLock->Serialize(aDescriptor);
+ mUpdated = false;
+ } else {
+ aDescriptor = null_t();
+ }
+}
+
+TextureClient::~TextureClient()
+{
+ mReadLock = nullptr;
+ Destroy(false);
+}
+
+void
+TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ MOZ_ASSERT(aSurface);
+ // If you run into this assertion, make sure the texture was locked write-only
+ // rather than read-write.
+ MOZ_ASSERT(!mBorrowedDrawTarget);
+
+ // XXX - It would be better to first try the DrawTarget approach and fallback
+ // to the backend-specific implementation because the latter will usually do
+ // an expensive read-back + cpu-side copy if the texture is on the gpu.
+ // There is a bug with the DrawTarget approach, though specific to reading back
+ // from WebGL (where R and B channel end up inverted) to figure out first.
+ if (mData->UpdateFromSurface(aSurface)) {
+ return;
+ }
+ if (CanExposeDrawTarget() && NS_IsMainThread()) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ MOZ_ASSERT(dt);
+ if (dt) {
+ dt->CopySurface(aSurface,
+ gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
+ gfx::IntPoint(0, 0));
+ return;
+ }
+ }
+ NS_WARNING("TextureClient::UpdateFromSurface failed");
+}
+
+
+already_AddRefed<TextureClient>
+TextureClient::CreateSimilar(LayersBackend aLayersBackend, TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
+{
+ MOZ_ASSERT(IsValid());
+
+ MOZ_ASSERT(!mIsLocked);
+ if (mIsLocked) {
+ return nullptr;
+ }
+
+ LockActor();
+ TextureData* data = mData->CreateSimilar(mAllocator, aLayersBackend, aFlags, aAllocFlags);
+ UnlockActor();
+
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aFlags, mAllocator);
+}
+
+gfx::DrawTarget*
+TextureClient::BorrowDrawTarget()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ // TODO- We can't really assert that at the moment because there is code that Borrows
+ // the DrawTarget, just to get a snapshot, which is legit in term of OpenMode
+ // but we should have a way to get a SourceSurface directly instead.
+ //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
+
+ if (!IsValid() || !mIsLocked) {
+ return nullptr;
+ }
+
+ if (!NS_IsMainThread()) {
+ return nullptr;
+ }
+
+ if (!mBorrowedDrawTarget) {
+ mBorrowedDrawTarget = mData->BorrowDrawTarget();
+#ifdef DEBUG
+ mExpectedDtRefs = mBorrowedDrawTarget ? mBorrowedDrawTarget->refCount() : 0;
+#endif
+ }
+
+ return mBorrowedDrawTarget;
+}
+
+bool
+TextureClient::BorrowMappedData(MappedTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ // TODO - SharedRGBImage just accesses the buffer without properly locking
+ // the texture. It's bad.
+ //MOZ_ASSERT(mIsLocked);
+ //if (!mIsLocked) {
+ // return nullptr;
+ //}
+
+ return mData ? mData->BorrowMappedData(aMap) : false;
+}
+
+bool
+TextureClient::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->BorrowMappedYCbCrData(aMap) : false;
+}
+
+bool
+TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->Serialize(aOutDescriptor) : false;
+}
+
+// static
+PTextureChild*
+TextureClient::CreateIPDLActor()
+{
+ TextureChild* c = new TextureChild();
+ c->AddIPDLReference();
+ return c;
+}
+
+// static
+bool
+TextureClient::DestroyIPDLActor(PTextureChild* actor)
+{
+ static_cast<TextureChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::AsTextureClient(PTextureChild* actor)
+{
+ if (!actor) {
+ return nullptr;
+ }
+
+ TextureChild* tc = static_cast<TextureChild*>(actor);
+
+ tc->Lock();
+
+ // Since TextureClient may be destroyed asynchronously with respect to its
+ // IPDL actor, we must acquire a reference within a lock. The mDestroyed bit
+ // tells us whether or not the main thread has disconnected the TextureClient
+ // from its actor.
+ if (tc->mDestroyed) {
+ tc->Unlock();
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> texture = tc->mTextureClient;
+ tc->Unlock();
+
+ return texture.forget();
+}
+
+bool
+TextureClient::IsSharedWithCompositor() const {
+ return mActor && mActor->IPCOpen();
+}
+
+void
+TextureClient::AddFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags |= aFlags;
+}
+
+void
+TextureClient::RemoveFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags &= ~aFlags;
+}
+
+void
+TextureClient::RecycleTexture(TextureFlags aFlags)
+{
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(!mIsLocked);
+
+ mAddedToCompositableClient = false;
+ if (mFlags != aFlags) {
+ mFlags = aFlags;
+ }
+}
+
+void
+TextureClient::SetAddedToCompositableClient()
+{
+ if (!mAddedToCompositableClient) {
+ mAddedToCompositableClient = true;
+ if(!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+ MOZ_ASSERT(!mIsLocked);
+ LockActor();
+ if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+ mActor->SendRecycleTexture(mFlags);
+ }
+ UnlockActor();
+ }
+}
+
+void CancelTextureClientRecycle(uint64_t aTextureId, LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return;
+ }
+ MessageLoop* msgLoop = nullptr;
+ msgLoop = aAllocator->GetMessageLoop();
+ if (!msgLoop) {
+ return;
+ }
+ if (MessageLoop::current() == msgLoop) {
+ aAllocator->CancelWaitForRecycle(aTextureId);
+ } else {
+ msgLoop->PostTask(NewRunnableFunction(CancelTextureClientRecycle,
+ aTextureId, aAllocator));
+ }
+}
+
+void
+TextureClient::CancelWaitForRecycle()
+{
+ if (GetFlags() & TextureFlags::RECYCLE) {
+ CancelTextureClientRecycle(mSerial, GetAllocator());
+ return;
+ }
+}
+
+/* static */ void
+TextureClient::TextureClientRecycleCallback(TextureClient* aClient, void* aClosure)
+{
+ MOZ_ASSERT(aClient->GetRecycleAllocator());
+ aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
+}
+
+void
+TextureClient::SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator)
+{
+ mRecycleAllocator = aAllocator;
+ if (aAllocator) {
+ SetRecycleCallback(TextureClientRecycleCallback, nullptr);
+ } else {
+ ClearRecycleCallback();
+ }
+}
+
+bool
+TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+ if (currentFwd != aForwarder) {
+ // It's a bit iffy but right now ShadowLayerForwarder inherits TextureForwarder
+ // even though it should not. ShadowLayerForwarder::GetTextureForwarder actually
+ // returns a pointer to the CompositorBridgeChild.
+ // It's Ok for a texture to move from a ShadowLayerForwarder to another, but
+ // not form a CompositorBridgeChild to another (they use different channels).
+ if (currentTexFwd && currentTexFwd != aForwarder->GetTextureForwarder()) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel CF.";
+ return false;
+ }
+ if (currentFwd && currentFwd->GetCompositorBackendType() != aForwarder->GetCompositorBackendType()) {
+ gfxCriticalError() << "Attempt to move a texture to different compositor backend.";
+ return false;
+ }
+ mActor->mCompositableForwarder = aForwarder;
+ }
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mCompositableForwarder = aForwarder;
+ mActor->mTextureForwarder = aForwarder->GetTextureForwarder();
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+bool
+TextureClient::InitIPDLActor(KnowsCompositor* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ TextureForwarder* fwd = aForwarder->GetTextureForwarder();
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+
+ if (currentFwd) {
+ gfxCriticalError() << "Attempt to remove a texture from a CompositableForwarder.";
+ return false;
+ }
+
+ if (currentTexFwd && currentTexFwd != fwd) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel TF.";
+ return false;
+ }
+ mActor->mTextureForwarder = fwd;
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = fwd->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mTextureForwarder = fwd;
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+PTextureChild*
+TextureClient::GetIPDLActor()
+{
+ return mActor;
+}
+
+static inline gfx::BackendType
+BackendTypeForBackendSelector(LayersBackend aLayersBackend, BackendSelector aSelector)
+{
+ switch (aSelector) {
+ case BackendSelector::Canvas:
+ return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ case BackendSelector::Content:
+ return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
+ return gfx::BackendType::NONE;
+ }
+};
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ return TextureClient::CreateForDrawing(aAllocator->GetTextureForwarder(),
+ aFormat, aSize,
+ layersBackend,
+ aAllocator->GetMaxTextureSize(),
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector);
+
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+
+#ifdef XP_WIN
+ if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ aSize.width <= aMaxTextureSize &&
+ aSize.height <= aMaxTextureSize &&
+ !(aAllocFlags & ALLOC_UPDATE_FROM_SURFACE))
+ {
+ data = DXGITextureData::Create(aSize, aFormat, aAllocFlags);
+ }
+
+ if (!data && aFormat == SurfaceFormat::B8G8R8X8 &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ NS_IsMainThread()) {
+ data = DIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+#endif
+
+#ifdef MOZ_X11
+ gfxSurfaceType type =
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
+
+ if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ type == gfxSurfaceType::Xlib)
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#ifdef GL_PROVIDER_GLX
+ if (!data && aLayersBackend == LayersBackend::LAYERS_OPENGL &&
+ type == gfxSurfaceType::Xlib &&
+ aFormat != SurfaceFormat::A8 &&
+ gl::sGLXLibrary.UseTextureFromPixmap())
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#endif
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
+ }
+
+ if (moz2DBackend == BackendType::SKIA && aFormat == SurfaceFormat::B8G8R8X8) {
+ // Skia doesn't support RGBX, so ensure we clear the buffer for the proper alpha values.
+ aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER);
+ }
+
+ // Can't do any better than a buffer texture client.
+ return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
+ moz2DBackend, aLayersBackend,
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ gfx::IntSize size = aSurface->GetSize();
+
+ if (!gfx::Factory::AllowedSurfaceSize(size)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+#if defined(XP_WIN)
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(layersBackend, aSelector);
+
+ int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
+
+ if (layersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ size.width <= maxTextureSize &&
+ size.height <= maxTextureSize)
+ {
+ data = D3D11TextureData::Create(aSurface, aAllocFlags);
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator->GetTextureForwarder());
+ }
+
+ // Fall back to using UpdateFromSurface
+
+ TextureAllocationFlags allocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE);
+ RefPtr<TextureClient> client = CreateForDrawing(aAllocator, aSurface->GetFormat(), size,
+ aSelector, aTextureFlags, allocFlags);
+ if (!client) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(client, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ client->UpdateFromSurface(aSurface);
+ return client.forget();
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return CreateForRawBufferAccess(aAllocator->GetTextureForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aAllocator->GetCompositorBackendType(),
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (aAllocFlags & ALLOC_DISALLOW_BUFFERTEXTURECLIENT) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ // D2D backend does not support CreateDrawTargetForData(). Use CAIRO instead.
+ if (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
+ aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) {
+ aMoz2DBackend = gfx::BackendType::CAIRO;
+ }
+
+ TextureData* texData = BufferTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aTextureFlags,
+ aAllocFlags, aAllocator);
+ if (!texData) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
+ aStereoMode, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ TextureData* data =
+ BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+: AtomicRefCountedWithFinalize("TextureClient")
+, mAllocator(aAllocator)
+, mActor(nullptr)
+, mData(aData)
+, mFlags(aFlags)
+, mOpenMode(OpenMode::OPEN_NONE)
+#ifdef DEBUG
+, mExpectedDtRefs(0)
+#endif
+, mIsLocked(false)
+, mUpdated(false)
+, mAddedToCompositableClient(false)
+, mWorkaroundAnnoyingSharedSurfaceLifetimeIssues(false)
+, mWorkaroundAnnoyingSharedSurfaceOwnershipIssues(false)
+, mFwdTransactionId(0)
+, mSerial(++sSerialCounter)
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+, mPoolTracker(nullptr)
+#endif
+{
+ mData->FillInfo(mInfo);
+ mFlags |= mData->GetTextureFlags();
+}
+
+bool TextureClient::CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint)
+{
+ MOZ_ASSERT(IsLocked());
+ MOZ_ASSERT(aTarget->IsLocked());
+
+ if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) {
+ return false;
+ }
+
+ RefPtr<DrawTarget> destinationTarget = aTarget->BorrowDrawTarget();
+ if (!destinationTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<DrawTarget> sourceTarget = BorrowDrawTarget();
+ if (!sourceTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
+ destinationTarget->CopySurface(source,
+ aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
+ aPoint ? *aPoint : gfx::IntPoint(0, 0));
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+TextureClient::GetAsSurface()
+{
+ if (!Lock(OpenMode::OPEN_READ)) {
+ return nullptr;
+ }
+ RefPtr<gfx::DataSourceSurface> data;
+ { // scope so that the DrawTarget is destroyed before Unlock()
+ RefPtr<gfx::DrawTarget> dt = BorrowDrawTarget();
+ if (dt) {
+ RefPtr<gfx::SourceSurface> surf = dt->Snapshot();
+ if (surf) {
+ data = surf->GetDataSurface();
+ }
+ }
+ }
+ Unlock();
+ return data.forget();
+}
+
+void
+TextureClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TextureClient (0x%p)", this).get();
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ AppendToString(aStream, mFlags, " [flags=", "]");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+class MemoryTextureReadLock : public TextureReadLock {
+public:
+ MemoryTextureReadLock();
+
+ ~MemoryTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual LockType GetType() override { return TYPE_MEMORY; }
+
+ virtual bool IsValid() const override { return true; };
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ int32_t mReadCount;
+};
+
+// The cross-prcess implementation of TextureReadLock.
+//
+// Since we don't use cross-process reference counting for the ReadLock objects,
+// we use the lock's internal counter as a way to know when to deallocate the
+// underlying shmem section: when the counter is equal to 1, it means that the
+// lock is not "held" (the texture is writable), when the counter is equal to 0
+// it means that we can safely deallocate the shmem section without causing a race
+// condition with the other process.
+class ShmemTextureReadLock : public TextureReadLock {
+public:
+ struct ShmReadLockInfo {
+ int32_t readCount;
+ };
+
+ explicit ShmemTextureReadLock(LayersIPCChannel* aAllocator);
+
+ ~ShmemTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual bool IsValid() const override { return mAllocSuccess; };
+
+ virtual LockType GetType() override { return TYPE_SHMEM; }
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
+
+ explicit ShmemTextureReadLock(const mozilla::layers::ShmemSection& aShmemSection)
+ : mShmemSection(aShmemSection)
+ , mAllocSuccess(true)
+ {
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ }
+
+ ShmReadLockInfo* GetShmReadLockInfoPtr()
+ {
+ return reinterpret_cast<ShmReadLockInfo*>
+ (mShmemSection.shmem().get<char>() + mShmemSection.offset());
+ }
+
+ RefPtr<LayersIPCChannel> mClientAllocator;
+ mozilla::layers::ShmemSection mShmemSection;
+ bool mAllocSuccess;
+};
+
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator)
+{
+ switch (aDescriptor.type()) {
+ case ReadLockDescriptor::TShmemSection: {
+ const ShmemSection& section = aDescriptor.get_ShmemSection();
+ MOZ_RELEASE_ASSERT(section.shmem().IsReadable());
+ return MakeAndAddRef<ShmemTextureReadLock>(section);
+ }
+ case ReadLockDescriptor::Tuintptr_t: {
+ if (!aAllocator->IsSameProcess()) {
+ // Trying to use a memory based lock instead of a shmem based one in
+ // the cross-process case is a bad security violation.
+ NS_ERROR("A client process may be trying to peek at the host's address space!");
+ return nullptr;
+ }
+ RefPtr<TextureReadLock> lock = reinterpret_cast<MemoryTextureReadLock*>(
+ aDescriptor.get_uintptr_t()
+ );
+
+ MOZ_ASSERT(lock);
+ if (lock) {
+ // The corresponding AddRef is in MemoryTextureReadLock::Serialize
+ lock.get()->Release();
+ }
+
+ return lock.forget();
+ }
+ case ReadLockDescriptor::Tnull_t: {
+ return nullptr;
+ }
+ default: {
+ // Invalid descriptor.
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ }
+ }
+ return nullptr;
+}
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Create(LayersIPCChannel* aAllocator)
+{
+ if (aAllocator->IsSameProcess()) {
+ // If our compositor is in the same process, we can save some cycles by not
+ // using shared memory.
+ return MakeAndAddRef<MemoryTextureReadLock>();
+ }
+
+ return MakeAndAddRef<ShmemTextureReadLock>(aAllocator);
+}
+
+MemoryTextureReadLock::MemoryTextureReadLock()
+: mReadCount(1)
+{
+ MOZ_COUNT_CTOR(MemoryTextureReadLock);
+}
+
+MemoryTextureReadLock::~MemoryTextureReadLock()
+{
+ // One read count that is added in constructor.
+ MOZ_ASSERT(mReadCount == 1);
+ MOZ_COUNT_DTOR(MemoryTextureReadLock);
+}
+
+bool
+MemoryTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ // AddRef here and Release when receiving on the host side to make sure the
+ // reference count doesn't go to zero before the host receives the message.
+ // see TextureReadLock::Deserialize
+ this->AddRef();
+ aOutput = ReadLockDescriptor(uintptr_t(this));
+ return true;
+}
+
+int32_t
+MemoryTextureReadLock::ReadLock()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+
+ return PR_ATOMIC_INCREMENT(&mReadCount);
+}
+
+int32_t
+MemoryTextureReadLock::ReadUnlock()
+{
+ int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
+ MOZ_ASSERT(readCount >= 0);
+
+ return readCount;
+}
+
+int32_t
+MemoryTextureReadLock::GetReadCount()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+ return mReadCount;
+}
+
+ShmemTextureReadLock::ShmemTextureReadLock(LayersIPCChannel* aAllocator)
+ : mClientAllocator(aAllocator)
+ , mAllocSuccess(false)
+{
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ MOZ_ASSERT(mClientAllocator);
+#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
+ if (mClientAllocator->GetTileLockAllocator()->AllocShmemSection(
+ MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ info->readCount = 1;
+ mAllocSuccess = true;
+ }
+}
+
+ShmemTextureReadLock::~ShmemTextureReadLock()
+{
+ if (mClientAllocator) {
+ // Release one read count that is added in constructor.
+ // The count is kept for calling GetReadCount() by TextureClientPool.
+ ReadUnlock();
+ }
+ MOZ_COUNT_DTOR(ShmemTextureReadLock);
+}
+
+bool
+ShmemTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ aOutput = ReadLockDescriptor(GetShmemSection());
+ return true;
+}
+
+int32_t
+ShmemTextureReadLock::ReadLock() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return PR_ATOMIC_INCREMENT(&info->readCount);
+}
+
+int32_t
+ShmemTextureReadLock::ReadUnlock() {
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
+ MOZ_ASSERT(readCount >= 0);
+ if (readCount <= 0) {
+ if (mClientAllocator) {
+ mClientAllocator->GetTileLockAllocator()->DeallocShmemSection(mShmemSection);
+ } else {
+ // we are on the compositor process
+ FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mShmemSection);
+ }
+ }
+ return readCount;
+}
+
+int32_t
+ShmemTextureReadLock::GetReadCount() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return info->readCount;
+}
+
+bool
+UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData)
+{
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsLocked());
+ MOZ_ASSERT(aTexture->GetFormat() == gfx::SurfaceFormat::YUV, "This textureClient can only use YCbCr data");
+ MOZ_ASSERT(!aTexture->IsImmutable());
+ MOZ_ASSERT(aTexture->IsValid());
+ MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
+
+ MappedYCbCrTextureData mapped;
+ if (!aTexture->BorrowMappedYCbCrData(mapped)) {
+ NS_WARNING("Failed to extract YCbCr info!");
+ return false;
+ }
+
+ MappedYCbCrTextureData srcData;
+ srcData.y.data = aData.mYChannel;
+ srcData.y.size = aData.mYSize;
+ srcData.y.stride = aData.mYStride;
+ srcData.y.skip = aData.mYSkip;
+ srcData.cb.data = aData.mCbChannel;
+ srcData.cb.size = aData.mCbCrSize;
+ srcData.cb.stride = aData.mCbCrStride;
+ srcData.cb.skip = aData.mCbSkip;
+ srcData.cr.data = aData.mCrChannel;
+ srcData.cr.size = aData.mCbCrSize;
+ srcData.cr.stride = aData.mCbCrStride;
+ srcData.cr.skip = aData.mCrSkip;
+ srcData.metadata = nullptr;
+
+ if (!srcData.CopyInto(mapped)) {
+ NS_WARNING("Failed to copy image data!");
+ return false;
+ }
+
+ if (TextureRequiresLocking(aTexture->GetFlags())) {
+ // We don't have support for proper locking yet, so we'll
+ // have to be immutable instead.
+ aTexture->MarkImmutable();
+ }
+ return true;
+}
+
+already_AddRefed<SyncObject>
+SyncObject::CreateSyncObject(SyncHandle aHandle)
+{
+ if (!aHandle) {
+ return nullptr;
+ }
+
+#ifdef XP_WIN
+ return MakeAndAddRef<SyncObjectD3D11>(aHandle);
+#else
+ MOZ_ASSERT_UNREACHABLE();
+ return nullptr;
+#endif
+}
+
+already_AddRefed<TextureClient>
+TextureClient::CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ if (!aData) {
+ return nullptr;
+ }
+ return MakeAndAddRef<TextureClient>(aData, aFlags, aAllocator);
+}
+
+bool
+MappedYCbCrChannelData::CopyInto(MappedYCbCrChannelData& aDst)
+{
+ if (!data || !aDst.data || size != aDst.size) {
+ return false;
+ }
+
+ if (stride == aDst.stride) {
+ // fast path!
+ // We assume that the padding in the destination is there for alignment
+ // purposes and doesn't contain useful data.
+ memcpy(aDst.data, data, stride * size.height);
+ return true;
+ }
+
+ for (int32_t i = 0; i < size.height; ++i) {
+ if (aDst.skip == 0 && skip == 0) {
+ // fast-ish path
+ memcpy(aDst.data + i * aDst.stride,
+ data + i * stride,
+ size.width);
+ } else {
+ // slow path
+ uint8_t* src = data + i * stride;
+ uint8_t* dst = aDst.data + i * aDst.stride;
+ for (int32_t j = 0; j < size.width; ++j) {
+ *dst = *src;
+ src += 1 + skip;
+ dst += 1 + aDst.skip;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/TextureClient.h b/system/graphics/layers/client/TextureClient.h
new file mode 100644
index 000000000..d28f37cf5
--- /dev/null
+++ b/system/graphics/layers/client/TextureClient.h
@@ -0,0 +1,826 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_H
+#define MOZILLA_GFX_TEXTURECLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageTypes.h" // for StereoMode
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/gfx/CriticalSection.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "GfxTexturesReporter.h"
+#include "pratom.h"
+#include "nsThreadUtils.h"
+
+class gfxImageSurface;
+
+namespace mozilla {
+
+// When defined, we track which pool the tile came from and test for
+// any inconsistencies. This can be defined in release build as well.
+#ifdef DEBUG
+#define GFX_DEBUG_TRACK_CLIENTS_IN_POOL 1
+#endif
+
+namespace layers {
+
+class AsyncTransactionWaiter;
+class BufferTextureData;
+class CompositableForwarder;
+class KnowsCompositor;
+class LayersIPCChannel;
+class CompositableClient;
+struct PlanarYCbCrData;
+class Image;
+class PTextureChild;
+class TextureChild;
+class TextureData;
+class GPUVideoTextureData;
+struct RawTextureBuffer;
+class RawYCbCrTextureBuffer;
+class TextureClient;
+class ITextureClientRecycleAllocator;
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+class TextureClientPool;
+#endif
+class TextureForwarder;
+class KeepAlive;
+
+/**
+ * TextureClient is the abstraction that allows us to share data between the
+ * content and the compositor side.
+ */
+
+enum TextureAllocationFlags {
+ ALLOC_DEFAULT = 0,
+ ALLOC_CLEAR_BUFFER = 1 << 1, // Clear the buffer to whatever is best for the draw target
+ ALLOC_CLEAR_BUFFER_WHITE = 1 << 2, // explicit all white
+ ALLOC_CLEAR_BUFFER_BLACK = 1 << 3, // explicit all black
+ ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
+
+ // Allocate the texture for out-of-band content updates. This is mostly for
+ // TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
+ // surfaces when used on the main thread.
+ ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
+
+ // Disable any cross-device synchronization. This is also for TextureClientD3D11,
+ // and creates a texture without KeyedMutex.
+ ALLOC_MANUAL_SYNCHRONIZATION = 1 << 6,
+
+ // The texture is going to be updated using UpdateFromSurface and needs to support
+ // that call.
+ ALLOC_UPDATE_FROM_SURFACE = 1 << 7,
+};
+
+#ifdef XP_WIN
+typedef void* SyncHandle;
+#else
+typedef uintptr_t SyncHandle;
+#endif // XP_WIN
+
+class SyncObject : public RefCounted<SyncObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObject)
+ virtual ~SyncObject() { }
+
+ static already_AddRefed<SyncObject> CreateSyncObject(SyncHandle aHandle);
+
+ enum class SyncType {
+ D3D11,
+ };
+
+ virtual SyncType GetSyncType() = 0;
+ virtual void FinalizeFrame() = 0;
+ virtual bool IsSyncObjectValid() = 0;
+
+protected:
+ SyncObject() { }
+};
+
+/**
+ * This class may be used to asynchronously receive an update when the content
+ * drawn to this texture client is available for reading in CPU memory. This
+ * can only be used on texture clients that support draw target creation.
+ */
+class TextureReadbackSink
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadbackSink)
+public:
+ /**
+ * Callback function to implement in order to receive a DataSourceSurface
+ * containing the data read back from the texture client. This will always
+ * be called on the main thread, and this may not hold on to the
+ * DataSourceSurface beyond the execution of this function.
+ */
+ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0;
+
+protected:
+ virtual ~TextureReadbackSink() {}
+};
+
+enum class BackendSelector
+{
+ Content,
+ Canvas
+};
+
+/// Temporary object providing direct access to a Texture's memory.
+///
+/// see TextureClient::CanExposeMappedData() and TextureClient::BorrowMappedData().
+struct MappedTextureData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ gfx::SurfaceFormat format;
+};
+
+struct MappedYCbCrChannelData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ int32_t skip;
+
+ bool CopyInto(MappedYCbCrChannelData& aDst);
+};
+
+struct MappedYCbCrTextureData {
+ MappedYCbCrChannelData y;
+ MappedYCbCrChannelData cb;
+ MappedYCbCrChannelData cr;
+ // Sad but because of how SharedPlanarYCbCrData is used we have to expose this for now.
+ uint8_t* metadata;
+ StereoMode stereoMode;
+
+ bool CopyInto(MappedYCbCrTextureData& aDst)
+ {
+ return y.CopyInto(aDst.y)
+ && cb.CopyInto(aDst.cb)
+ && cr.CopyInto(aDst.cr);
+ }
+};
+
+class ReadLockDescriptor;
+
+// A class to help implement copy-on-write semantics for shared textures.
+//
+// A TextureClient/Host pair can opt into using a ReadLock by calling
+// TextureClient::EnableReadLock. This will equip the TextureClient with a
+// ReadLock object that will be automatically ReadLock()'ed by the texture itself
+// when it is written into (see TextureClient::Unlock).
+// A TextureReadLock's counter starts at 1 and is expected to be equal to 1 when the
+// lock is destroyed. See ShmemTextureReadLock for explanations about why we use
+// 1 instead of 0 as the initial state.
+// TextureReadLock is mostly internally managed by the TextureClient/Host pair,
+// and the compositable only has to forward it during updates. If an update message
+// contains a null_t lock, it means that the texture was not written into on the
+// content side, and there is no synchronization required on the compositor side
+// (or it means that the texture pair did not opt into using ReadLocks).
+// On the compositor side, the TextureHost can receive a ReadLock during a
+// transaction, and will both ReadUnlock() it and drop it as soon as the shared
+// data is available again for writing (the texture upload is done, or the compositor
+// not reading the texture anymore). The lock is dropped to make sure it is
+// ReadUnlock()'ed only once.
+class TextureReadLock {
+protected:
+ virtual ~TextureReadLock() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadLock)
+
+ virtual int32_t ReadLock() = 0;
+ virtual int32_t ReadUnlock() = 0;
+ virtual int32_t GetReadCount() = 0;
+ virtual bool IsValid() const = 0;
+
+ static already_AddRefed<TextureReadLock>
+ Create(LayersIPCChannel* aAllocator);
+
+ static already_AddRefed<TextureReadLock>
+ Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator);
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) = 0;
+
+ enum LockType {
+ TYPE_MEMORY,
+ TYPE_SHMEM
+ };
+ virtual LockType GetType() = 0;
+
+protected:
+ NS_DECL_OWNINGTHREAD
+};
+
+#ifdef XP_WIN
+class D3D11TextureData;
+#endif
+
+class TextureData {
+public:
+ struct Info {
+ gfx::IntSize size;
+ gfx::SurfaceFormat format;
+ bool hasIntermediateBuffer;
+ bool hasSynchronization;
+ bool supportsMoz2D;
+ bool canExposeMappedData;
+
+ Info()
+ : format(gfx::SurfaceFormat::UNKNOWN)
+ , hasIntermediateBuffer(false)
+ , hasSynchronization(false)
+ , supportsMoz2D(false)
+ , canExposeMappedData(false)
+ {}
+ };
+
+ TextureData() { MOZ_COUNT_CTOR(TextureData); }
+
+ virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
+
+ virtual void FillInfo(TextureData::Info& aInfo) const = 0;
+
+ virtual bool Lock(OpenMode aMode) = 0;
+
+ virtual void Unlock() = 0;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
+
+ virtual bool BorrowMappedData(MappedTextureData&) { return false; }
+
+ virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) = 0;
+
+ /// Depending on the texture's flags either Deallocate or Forget is called.
+ virtual void Forget(LayersIPCChannel* aAllocator) {}
+
+ virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
+
+ virtual bool ReadBack(TextureReadbackSink* aReadbackSink) { return false; }
+
+ virtual void SyncWithObject(SyncObject* aFence) {};
+
+ virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
+
+#ifdef XP_WIN
+ virtual D3D11TextureData* AsD3D11TextureData() {
+ return nullptr;
+ }
+#endif
+
+ virtual BufferTextureData* AsBufferTextureData() { return nullptr; }
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() { return nullptr; }
+};
+
+/**
+ * TextureClient is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * content-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureHost lives on the compositor-side.
+ *
+ * TextureClient's primary purpose is to present texture data in a way that is
+ * understood by the IPC system. There are two ways to use it:
+ * - Use it to serialize image data that is not IPC-friendly (most likely
+ * involving a copy into shared memory)
+ * - preallocate it and paint directly into it, which avoids copy but requires
+ * the painting code to be aware of TextureClient (or at least the underlying
+ * shared memory).
+ *
+ * There is always one and only one TextureClient per TextureHost, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * responsibility of the compositable (which would use two Texture pairs).
+ * In order to send several different buffers to the compositor side, use
+ * several TextureClients.
+ */
+class TextureClient
+ : public AtomicRefCountedWithFinalize<TextureClient>
+{
+public:
+ explicit TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual ~TextureClient();
+
+ static already_AddRefed<TextureClient>
+ CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ // Creates and allocates a TextureClient usable with Moz2D.
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ // Creates and allocates a TextureClient supporting the YCbCr format.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient (can be accessed through raw
+ // pointers).
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ // Creates and allocates a TextureClient (can beaccessed through raw
+ // pointers) with a certain buffer size. It's unfortunate that we need this.
+ // providing format and sizes could let us do more optimization.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient of the same type.
+ already_AddRefed<TextureClient>
+ CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
+
+ /**
+ * Locks the shared data, allowing the caller to get access to it.
+ *
+ * Please always lock/unlock when accessing the shared data.
+ * If Lock() returns false, you should not attempt to access the shared data.
+ */
+ bool Lock(OpenMode aMode);
+
+ void Unlock();
+
+ bool IsLocked() const { return mIsLocked; }
+
+ gfx::IntSize GetSize() const { return mInfo.size; }
+
+ gfx::SurfaceFormat GetFormat() const { return mInfo.format; }
+
+ /**
+ * Returns true if this texture has a synchronization mechanism (mutex, fence, etc.).
+ * Textures that do not implement synchronization should be immutable or should
+ * use immediate uploads (see TextureFlags in CompositorTypes.h)
+ * Even if a texture does not implement synchronization, Lock and Unlock need
+ * to be used appropriately since the latter are also there to map/numap data.
+ */
+ bool HasSynchronization() const { return mInfo.hasSynchronization; }
+
+ /**
+ * Indicates whether the TextureClient implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureClient does not contend with locking the texture on the host side.
+ */
+ bool HasIntermediateBuffer() const { return mInfo.hasIntermediateBuffer; }
+
+ bool CanExposeDrawTarget() const { return mInfo.supportsMoz2D; }
+
+ bool CanExposeMappedData() const { return mInfo.canExposeMappedData; }
+
+ /**
+ * Returns a DrawTarget to draw into the TextureClient.
+ * This function should never be called when not on the main thread!
+ *
+ * This must never be called on a TextureClient that is not sucessfully locked.
+ * When called several times within one Lock/Unlock pair, this method should
+ * return the same DrawTarget.
+ * The DrawTarget is automatically flushed by the TextureClient when the latter
+ * is unlocked, and the DrawTarget that will be returned within the next
+ * lock/unlock pair may or may not be the same object.
+ * Do not keep references to the DrawTarget outside of the lock/unlock pair.
+ *
+ * This is typically used as follows:
+ *
+ * if (!texture->Lock(OpenMode::OPEN_READ_WRITE)) {
+ * return false;
+ * }
+ * {
+ * // Restrict this code's scope to ensure all references to dt are gone
+ * // when Unlock is called.
+ * DrawTarget* dt = texture->BorrowDrawTarget();
+ * // use the draw target ...
+ * }
+ * texture->Unlock();
+ *
+ */
+ gfx::DrawTarget* BorrowDrawTarget();
+
+ /**
+ * Similar to BorrowDrawTarget but provides direct access to the texture's bits
+ * instead of a DrawTarget.
+ */
+ bool BorrowMappedData(MappedTextureData&);
+ bool BorrowMappedYCbCrData(MappedYCbCrTextureData&);
+
+ /**
+ * This function can be used to update the contents of the TextureClient
+ * off the main thread.
+ */
+ void UpdateFromSurface(gfx::SourceSurface* aSurface);
+
+ /**
+ * This method is strictly for debugging. It causes locking and
+ * needless copies.
+ */
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Copies a rectangle from this texture client to a position in aTarget.
+ * It is assumed that the necessary locks are in place; so this should at
+ * least have a read lock and aTarget should at least have a write lock.
+ */
+ bool CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint);
+
+ /**
+ * Allocate and deallocate a TextureChild actor.
+ *
+ * TextureChild is an implementation detail of TextureClient that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPextureChild and DeallocPTextureChild).
+ */
+ static PTextureChild* CreateIPDLActor();
+ static bool DestroyIPDLActor(PTextureChild* actor);
+
+ /**
+ * Get the TextureClient corresponding to the actor passed in parameter.
+ */
+ static already_AddRefed<TextureClient> AsTextureClient(PTextureChild* actor);
+
+ /**
+ * TextureFlags contain important information about various aspects
+ * of the texture, like how its liferime is managed, and how it
+ * should be displayed.
+ * See TextureFlags in CompositorTypes.h.
+ */
+ TextureFlags GetFlags() const { return mFlags; }
+
+ bool HasFlags(TextureFlags aFlags) const
+ {
+ return (mFlags & aFlags) == aFlags;
+ }
+
+ void AddFlags(TextureFlags aFlags);
+
+ void RemoveFlags(TextureFlags aFlags);
+
+ // Must not be called when TextureClient is in use by CompositableClient.
+ void RecycleTexture(TextureFlags aFlags);
+
+ /**
+ * After being shared with the compositor side, an immutable texture is never
+ * modified, it can only be read. It is safe to not Lock/Unlock immutable
+ * textures.
+ */
+ bool IsImmutable() const { return !!(mFlags & TextureFlags::IMMUTABLE); }
+
+ void MarkImmutable() { AddFlags(TextureFlags::IMMUTABLE); }
+
+ bool IsSharedWithCompositor() const;
+
+ /**
+ * If this method returns false users of TextureClient are not allowed
+ * to access the shared data.
+ */
+ bool IsValid() const { return !!mData; }
+
+ /**
+ * Called when TextureClient is added to CompositableClient.
+ */
+ void SetAddedToCompositableClient();
+
+ /**
+ * If this method retuns false, TextureClient is already added to CompositableClient,
+ * since its creation or recycling.
+ */
+ bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; }
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a CompositableForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(CompositableForwarder* aForwarder);
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a TextureForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(KnowsCompositor* aForwarder);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureChild* GetIPDLActor();
+
+ /**
+ * Triggers the destruction of the shared data and the corresponding TextureHost.
+ *
+ * If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction
+ * will be synchronously coordinated with the compositor side, otherwise it
+ * will be done asynchronously.
+ * If sync is true, the destruction will be synchronous regardless of the
+ * texture's flags (bad for performance, use with care).
+ */
+ void Destroy(bool sync = false);
+
+ /**
+ * Track how much of this texture is wasted.
+ * For example we might allocate a 256x256 tile but only use 10x10.
+ */
+ void SetWaste(int aWasteArea) {
+ mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
+ }
+
+ /**
+ * This sets the readback sink that this texture is to use. This will
+ * receive the data for this texture as soon as it becomes available after
+ * texture unlock.
+ */
+ virtual void SetReadbackSink(TextureReadbackSink* aReadbackSink) {
+ mReadbackSink = aReadbackSink;
+ }
+
+ void SyncWithObject(SyncObject* aFence) { mData->SyncWithObject(aFence); }
+
+ LayersIPCChannel* GetAllocator() { return mAllocator; }
+
+ ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
+ void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
+
+ /// If you add new code that uses this method, you are probably doing something wrong.
+ TextureData* GetInternalData() { return mData; }
+ const TextureData* GetInternalData() const { return mData; }
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ void CancelWaitForRecycle();
+
+ /**
+ * Set last transaction id of CompositableForwarder.
+ *
+ * Called when TextureClient has TextureFlags::RECYCLE flag.
+ * When CompositableForwarder forwards the TextureClient with
+ * TextureFlags::RECYCLE, it holds TextureClient's ref until host side
+ * releases it. The host side sends TextureClient release message.
+ * The id is used to check if the message is for the last TextureClient
+ * forwarding.
+ */
+ void SetLastFwdTransactionId(uint64_t aTransactionId)
+ {
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetLastFwdTransactionId() { return mFwdTransactionId; }
+
+ void EnableReadLock();
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ bool IsReadLocked() const;
+
+ void SerializeReadLock(ReadLockDescriptor& aDescriptor);
+
+private:
+ static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
+
+ // Internal helpers for creating texture clients using the actual forwarder instead
+ // of KnowsCompositor. TextureClientPool uses these to let it cache texture clients
+ // per-process instead of per ShadowLayerForwarder, but everyone else should
+ // use the public functions instead.
+ friend class TextureClientPool;
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ /**
+ * Called once, during the destruction of the Texture, on the thread in which
+ * texture's reference count reaches 0 (could be any thread).
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize() {}
+
+ friend class AtomicRefCountedWithFinalize<TextureClient>;
+protected:
+ /**
+ * Should only be called *once* per texture, in TextureClient::InitIPDLActor.
+ * Some texture implementations rely on the fact that the descriptor will be
+ * deserialized.
+ * Calling ToSurfaceDescriptor again after it has already returned true,
+ * or never constructing a TextureHost with aDescriptor may result in a memory
+ * leak (see TextureClientD3D9 for example).
+ */
+ bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
+
+ void LockActor() const;
+ void UnlockActor() const;
+
+ TextureData::Info mInfo;
+
+ RefPtr<LayersIPCChannel> mAllocator;
+ RefPtr<TextureChild> mActor;
+ RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
+ RefPtr<TextureReadLock> mReadLock;
+
+ TextureData* mData;
+ RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
+
+ TextureFlags mFlags;
+
+ gl::GfxTextureWasteTracker mWasteTracker;
+
+ OpenMode mOpenMode;
+#ifdef DEBUG
+ uint32_t mExpectedDtRefs;
+#endif
+ bool mIsLocked;
+ // This member tracks that the texture was written into until the update
+ // is sent to the compositor. We need this remember to lock mReadLock on
+ // behalf of the compositor just before sending the notification.
+ bool mUpdated;
+
+ // Used when TextureClient is recycled with TextureFlags::RECYCLE flag.
+ bool mAddedToCompositableClient;
+
+ bool mWorkaroundAnnoyingSharedSurfaceLifetimeIssues;
+ bool mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+
+ RefPtr<TextureReadbackSink> mReadbackSink;
+
+ uint64_t mFwdTransactionId;
+
+ // Serial id of TextureClient. It is unique in current process.
+ const uint64_t mSerial;
+ // Used to assign serial ids of TextureClient.
+ static mozilla::Atomic<uint64_t> sSerialCounter;
+
+ friend class TextureChild;
+ friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
+ friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+public:
+ // Pointer to the pool this tile came from.
+ TextureClientPool* mPoolTracker;
+#endif
+};
+
+/**
+ * Task that releases TextureClient pointer on a specified thread.
+ */
+class TextureClientReleaseTask : public Runnable
+{
+public:
+ explicit TextureClientReleaseTask(TextureClient* aClient)
+ : mTextureClient(aClient) {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mTextureClient = nullptr;
+ return NS_OK;
+ }
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+};
+
+// Automatically lock and unlock a texture. Since texture locking is fallible,
+// Succeeded() must be checked on the guard object before proceeding.
+class MOZ_RAII TextureClientAutoLock
+{
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+public:
+ TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mTexture(aTexture),
+ mSucceeded(false)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+ mSucceeded = mTexture->Lock(aMode);
+#ifdef DEBUG
+ mChecked = false;
+#endif
+ }
+ ~TextureClientAutoLock() {
+ MOZ_ASSERT(mChecked);
+ if (mSucceeded) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Succeeded() {
+#ifdef DEBUG
+ mChecked = true;
+#endif
+ return mSucceeded;
+ }
+
+private:
+ TextureClient* mTexture;
+#ifdef DEBUG
+ bool mChecked;
+#endif
+ bool mSucceeded;
+};
+
+class KeepAlive
+{
+public:
+ virtual ~KeepAlive() {}
+};
+
+template<typename T>
+class TKeepAlive : public KeepAlive
+{
+public:
+ explicit TKeepAlive(T* aData) : mData(aData) {}
+protected:
+ RefPtr<T> mData;
+};
+
+/// Convenience function to set the content of ycbcr texture.
+bool UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/client/TextureClientPool.cpp b/system/graphics/layers/client/TextureClientPool.cpp
new file mode 100644
index 000000000..c556a791e
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientPool.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureClientPool.h"
+#include "CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/TiledContentClient.h"
+
+#include "gfxPrefs.h"
+
+#include "nsComponentManagerUtils.h"
+
+#define TCP_LOG(...)
+//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
+
+namespace mozilla {
+namespace layers {
+
+// We want to shrink to our maximum size of N unused tiles
+// after a timeout to allow for short-term budget requirements
+static void
+ShrinkCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
+}
+
+// After a certain amount of inactivity, let's clear the pool so that
+// we don't hold onto tiles needlessly. In general, allocations are
+// cheap enough that re-allocating isn't an issue unless we're allocating
+// at an inopportune time (e.g. mid-animation).
+static void
+ClearCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->Clear();
+}
+
+TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator)
+ : mBackend(aLayersBackend)
+ , mMaxTextureSize(aMaxTextureSize)
+ , mFormat(aFormat)
+ , mSize(aSize)
+ , mFlags(aFlags)
+ , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+ , mClearTimeoutMsec(aClearTimeoutMsec)
+ , mInitialPoolSize(aInitialPoolSize)
+ , mPoolUnusedSize(aPoolUnusedSize)
+ , mOutstandingClients(0)
+ , mSurfaceAllocator(aAllocator)
+ , mDestroyed(false)
+{
+ TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
+ this, mInitialPoolSize);
+ mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1");
+ mClearTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
+ }
+}
+
+TextureClientPool::~TextureClientPool()
+{
+ mShrinkTimer->Cancel();
+ mClearTimer->Cancel();
+}
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+static bool TestClientPool(const char* what,
+ TextureClient* aClient,
+ TextureClientPool* aPool)
+{
+ if (!aClient || !aPool) {
+ return false;
+ }
+
+ TextureClientPool* actual = aClient->mPoolTracker;
+ bool ok = (actual == aPool);
+ if (ok) {
+ ok = (aClient->GetFormat() == aPool->GetFormat());
+ }
+
+ if (!ok) {
+ if (actual) {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", "
+ << actual << "-" << actual->GetFormat() << ", "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing with actual");
+ } else {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", nullptr, "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing without actual");
+ }
+ }
+ return ok;
+}
+#endif
+
+already_AddRefed<TextureClient>
+TextureClientPool::GetTextureClient()
+{
+ // Try to fetch a client from the pool
+ RefPtr<TextureClient> textureClient;
+
+ // We initially allocate mInitialPoolSize for our pool. If we run
+ // out of TextureClients, we allocate additional TextureClients to try and keep around
+ // mPoolUnusedSize
+ if (!mTextureClients.size()) {
+ AllocateTextureClient();
+ }
+
+ if (!mTextureClients.size()) {
+ // All our allocations failed, return nullptr
+ return nullptr;
+ }
+
+ mOutstandingClients++;
+ textureClient = mTextureClients.top();
+ mTextureClients.pop();
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ if (textureClient) {
+ textureClient->mPoolTracker = this;
+ }
+ DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
+ this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
+
+ return textureClient.forget();
+}
+
+void
+TextureClientPool::AllocateTextureClient()
+{
+ TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
+ this, mOutstandingClients);
+
+ RefPtr<TextureClient> newClient;
+ if (gfxPrefs::ForceShmemTiles()) {
+ // gfx::BackendType::NONE means use the content backend
+ newClient =
+ TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+ mFormat, mSize,
+ gfx::BackendType::NONE,
+ mBackend,
+ mFlags, ALLOC_DEFAULT);
+ } else {
+ newClient =
+ TextureClient::CreateForDrawing(mSurfaceAllocator,
+ mFormat, mSize,
+ mBackend,
+ mMaxTextureSize,
+ BackendSelector::Content,
+ mFlags);
+ }
+
+ if (newClient) {
+ mTextureClients.push(newClient);
+ }
+}
+
+void
+TextureClientPool::ResetTimers()
+{
+ // Shrink down if we're beyond our maximum size
+ if (mShrinkTimeoutMsec &&
+ mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
+ TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
+ mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+
+ // Clear pool after a period of inactivity to reduce memory consumption
+ if (mClearTimeoutMsec) {
+ TCP_LOG("TexturePool %p scheduling a clear\n", this);
+ mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+void
+TextureClientPool::ReturnTextureClient(TextureClient *aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("return", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ // Add the client to the pool:
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ mTextureClients.push(aClient);
+ TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
+ this, aClient, mTextureClients.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+ MOZ_ASSERT(aClient->GetReadLock());
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ mTextureClientsDeferred.push_back(aClient);
+ TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
+ this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ShrinkToMaximumSize()
+{
+ // We're over our desired maximum size, immediately shrink down to the
+ // maximum.
+ //
+ // We cull from the deferred TextureClients first, as we can't reuse those
+ // until they get returned.
+ uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
+
+ // If we have > mInitialPoolSize outstanding, then we want to keep around
+ // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
+ // outstanding, then keep around the entire initial pool size.
+ uint32_t targetUnusedClients;
+ if (mOutstandingClients > mInitialPoolSize) {
+ targetUnusedClients = mPoolUnusedSize;
+ } else {
+ targetUnusedClients = mInitialPoolSize;
+ }
+
+ TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
+ this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
+
+ while (totalUnusedTextureClients > targetUnusedClients) {
+ if (mTextureClientsDeferred.size()) {
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
+ this, mTextureClientsDeferred.front().get(),
+ mTextureClientsDeferred.size() - 1);
+ mTextureClientsDeferred.pop_front();
+ } else {
+ TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
+ this, mTextureClients.top().get(), mTextureClients.size() - 1);
+ mTextureClients.pop();
+ }
+ totalUnusedTextureClients--;
+ }
+}
+
+void
+TextureClientPool::ReturnDeferredClients()
+{
+ if (mTextureClientsDeferred.empty()) {
+ return;
+ }
+
+ TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+ this, mTextureClientsDeferred.size());
+
+ ReturnUnlockedClients();
+ ShrinkToMaximumSize();
+}
+
+void
+TextureClientPool::ReturnUnlockedClients()
+{
+ for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
+ MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
+ // Last count is held by the lock itself.
+ if (!(*it)->IsReadLocked()) {
+ mTextureClients.push(*it);
+ it = mTextureClientsDeferred.erase(it);
+
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ } else {
+ it++;
+ }
+ }
+}
+
+void
+TextureClientPool::ReportClientLost()
+{
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
+ this, mOutstandingClients);
+}
+
+void
+TextureClientPool::Clear()
+{
+ TCP_LOG("TexturePool %p getting cleared\n", this);
+ while (!mTextureClients.empty()) {
+ TCP_LOG("TexturePool %p releasing client %p\n",
+ this, mTextureClients.top().get());
+ mTextureClients.pop();
+ }
+ while (!mTextureClientsDeferred.empty()) {
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p releasing deferred client %p\n",
+ this, mTextureClientsDeferred.front().get());
+ mTextureClientsDeferred.pop_front();
+ }
+}
+
+void TextureClientPool::Destroy()
+{
+ Clear();
+ mDestroyed = true;
+ mInitialPoolSize = 0;
+ mPoolUnusedSize = 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/TextureClientPool.h b/system/graphics/layers/client/TextureClientPool.h
new file mode 100644
index 000000000..642c14bf5
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientPool.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTPOOL_H
+#define MOZILLA_GFX_TEXTURECLIENTPOOL_H
+
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "nsITimer.h"
+#include <stack>
+#include <list>
+
+namespace mozilla {
+namespace layers {
+
+class ISurfaceAllocator;
+class TextureForwarder;
+class TextureReadLock;
+
+class TextureClientAllocator
+{
+protected:
+ virtual ~TextureClientAllocator() {}
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator)
+
+ virtual already_AddRefed<TextureClient> GetTextureClient() = 0;
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ virtual void ReturnTextureClientDeferred(TextureClient *aClient) = 0;
+
+ virtual void ReportClientLost() = 0;
+};
+
+class TextureClientPool final : public TextureClientAllocator
+{
+ ~TextureClientPool();
+
+public:
+ TextureClientPool(LayersBackend aBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator);
+
+ /**
+ * Gets an allocated TextureClient of size and format that are determined
+ * by the initialisation parameters given to the pool. This will either be
+ * a cached client that was returned to the pool, or a newly allocated
+ * client if one isn't available.
+ *
+ * All clients retrieved by this method should be returned using the return
+ * functions, or reported lost so that the pool can manage its size correctly.
+ */
+ already_AddRefed<TextureClient> GetTextureClient() override;
+
+ /**
+ * Return a TextureClient that is no longer being used and is ready for
+ * immediate re-use or destruction.
+ */
+ void ReturnTextureClient(TextureClient *aClient);
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ void ReturnTextureClientDeferred(TextureClient *aClient) override;
+
+ /**
+ * Return any clients to the pool that were previously returned in
+ * ReturnTextureClientDeferred.
+ */
+ void ReturnDeferredClients();
+
+ /**
+ * Attempt to shrink the pool so that there are no more than
+ * mInitialPoolSize outstanding.
+ */
+ void ShrinkToMaximumSize();
+
+ /**
+ * Report that a client retrieved via GetTextureClient() has become
+ * unusable, so that it will no longer be tracked.
+ */
+ virtual void ReportClientLost() override;
+
+ /**
+ * Calling this will cause the pool to attempt to relinquish any unused
+ * clients.
+ */
+ void Clear();
+
+ LayersBackend GetBackend() const { return mBackend; }
+ int32_t GetMaxTextureSize() const { return mMaxTextureSize; }
+ gfx::SurfaceFormat GetFormat() { return mFormat; }
+ TextureFlags GetFlags() const { return mFlags; }
+
+ /**
+ * Clear the pool and put it in a state where it won't recycle any new texture.
+ */
+ void Destroy();
+
+private:
+ void ReturnUnlockedClients();
+
+ /// Allocate a single TextureClient to be returned from the pool.
+ void AllocateTextureClient();
+
+ /// Reset and/or initialise timers for shrinking/clearing the pool.
+ void ResetTimers();
+
+ /// Backend passed to the TextureClient for buffer creation.
+ LayersBackend mBackend;
+
+ // Max texture size passed to the TextureClient for buffer creation.
+ int32_t mMaxTextureSize;
+
+ /// Format is passed to the TextureClient for buffer creation.
+ gfx::SurfaceFormat mFormat;
+
+ /// The width and height of the tiles to be used.
+ gfx::IntSize mSize;
+
+ /// Flags passed to the TextureClient for buffer creation.
+ const TextureFlags mFlags;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to shrink the pool to its maximum size of mPoolUnusedSize.
+ uint32_t mShrinkTimeoutMsec;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to clear the pool.
+ uint32_t mClearTimeoutMsec;
+
+ // The initial number of unused texture clients to seed the pool with
+ // on construction
+ uint32_t mInitialPoolSize;
+
+ // How many unused texture clients to try and keep around if we go over
+ // the initial allocation
+ uint32_t mPoolUnusedSize;
+
+ /// This is a total number of clients in the wild and in the stack of
+ /// deferred clients (see below). So, the total number of clients in
+ /// existence is always mOutstandingClients + the size of mTextureClients.
+ uint32_t mOutstandingClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ std::stack<RefPtr<TextureClient> > mTextureClients;
+
+ std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
+ RefPtr<nsITimer> mShrinkTimer;
+ RefPtr<nsITimer> mClearTimer;
+ // This mSurfaceAllocator owns us, so no need to hold a ref to it
+ TextureForwarder* mSurfaceAllocator;
+
+ // Keep track of whether this pool has been destroyed or not. If it has,
+ // we won't accept returns of TextureClients anymore, and the refcounting
+ // should take care of their destruction.
+ bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENTPOOL_H */
diff --git a/system/graphics/layers/client/TextureClientRecycleAllocator.cpp b/system/graphics/layers/client/TextureClientRecycleAllocator.cpp
new file mode 100644
index 000000000..6a06d3f68
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientRecycleAllocator.cpp
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPlatform.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+// Used to keep TextureClient's reference count stable as not to disrupt recycling.
+class TextureClientHolder
+{
+ ~TextureClientHolder() {}
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
+
+ explicit TextureClientHolder(TextureClient* aClient)
+ : mTextureClient(aClient)
+ , mWillRecycle(true)
+ {}
+
+ TextureClient* GetTextureClient()
+ {
+ return mTextureClient;
+ }
+
+ bool WillRecycle()
+ {
+ return mWillRecycle;
+ }
+
+ void ClearWillRecycle()
+ {
+ mWillRecycle = false;
+ }
+
+ void ClearTextureClient() { mTextureClient = nullptr; }
+protected:
+ RefPtr<TextureClient> mTextureClient;
+ bool mWillRecycle;
+};
+
+class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : ITextureClientAllocationHelper(aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocationFlags)
+ , mAllocator(aAllocator)
+ {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override
+ {
+ if (aTextureClient->GetFormat() != mFormat ||
+ aTextureClient->GetSize() != mSize) {
+ return false;
+ }
+ return true;
+ }
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
+ {
+ return mAllocator->Allocate(mFormat,
+ mSize,
+ mSelector,
+ mTextureFlags,
+ mAllocationFlags);
+ }
+
+protected:
+ TextureClientRecycleAllocator* mAllocator;
+};
+
+YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
+ aData.mYSize,
+ BackendSelector::Content,
+ aTextureFlags,
+ ALLOC_DEFAULT)
+ , mData(aData)
+{
+}
+
+bool
+YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
+{
+ MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
+
+ BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
+ if (!bufferData ||
+ aTextureClient->GetSize() != mData.mYSize ||
+ bufferData->GetCbCrSize().isNothing() ||
+ bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
+ bufferData->GetYUVColorSpace().isNothing() ||
+ bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
+ bufferData->GetStereoMode().isNothing() ||
+ bufferData->GetStereoMode().ref() != mData.mStereoMode) {
+ return false;
+ }
+ return true;
+}
+
+already_AddRefed<TextureClient>
+YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
+{
+ return TextureClient::CreateForYCbCr(aAllocator,
+ mData.mYSize, mData.mCbCrSize,
+ mData.mStereoMode,
+ mData.mYUVColorSpace,
+ mTextureFlags);
+}
+
+TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
+ : mSurfaceAllocator(aAllocator)
+ , mMaxPooledSize(kMaxPooledSized)
+ , mLock("TextureClientRecycleAllocatorImp.mLock")
+ , mIsDestroyed(false)
+{
+}
+
+TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ MOZ_ASSERT(mInUseClients.empty());
+}
+
+void
+TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
+{
+ mMaxPooledSize = aMax;
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
+ DefaultTextureClientAllocationHelper helper(this,
+ aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+ return CreateOrRecycle(helper);
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
+{
+ MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
+
+ RefPtr<TextureClientHolder> textureHolder;
+
+ {
+ MutexAutoLock lock(mLock);
+ if (mIsDestroyed) {
+ return nullptr;
+ }
+ if (!mPooledClients.empty()) {
+ textureHolder = mPooledClients.top();
+ mPooledClients.pop();
+ // If a pooled TextureClient is not compatible, release it.
+ if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
+ // Release TextureClient.
+ RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
+ textureHolder->ClearTextureClient();
+ textureHolder = nullptr;
+ mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
+ } else {
+ textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
+ }
+ }
+ }
+
+ if (!textureHolder) {
+ // Allocate new TextureClient
+ RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
+ if (!texture) {
+ return nullptr;
+ }
+ textureHolder = new TextureClientHolder(texture);
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
+ // Register TextureClient
+ mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
+ }
+ RefPtr<TextureClient> client(textureHolder->GetTextureClient());
+
+ // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
+ // ref count drops to 1.
+ client->SetRecycleAllocator(this);
+ return client.forget();
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
+ aSelector, aTextureFlags, aAllocFlags);
+}
+
+void
+TextureClientRecycleAllocator::ShrinkToMinimumSize()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ // We can not clear using TextureClients safely.
+ // Just clear WillRecycle here.
+ std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
+ for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
+ RefPtr<TextureClientHolder> holder = it->second;
+ holder->ClearWillRecycle();
+ }
+}
+
+void
+TextureClientRecycleAllocator::Destroy()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ mIsDestroyed = true;
+}
+
+void
+TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
+{
+ // Clearing the recycle allocator drops a reference, so make sure we stay alive
+ // for the duration of this function.
+ RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
+ aClient->SetRecycleAllocator(nullptr);
+
+ RefPtr<TextureClientHolder> textureHolder;
+ {
+ MutexAutoLock lock(mLock);
+ if (mInUseClients.find(aClient) != mInUseClients.end()) {
+ textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
+ if (textureHolder->WillRecycle() &&
+ !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
+ mPooledClients.push(textureHolder);
+ }
+ mInUseClients.erase(aClient);
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/TextureClientRecycleAllocator.h b/system/graphics/layers/client/TextureClientRecycleAllocator.h
new file mode 100644
index 000000000..23ba78991
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientRecycleAllocator.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+#define MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+
+#include <map>
+#include <stack>
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureClientHolder;
+struct PlanarYCbCrData;
+
+class ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~ITextureClientRecycleAllocator() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ITextureClientRecycleAllocator)
+
+protected:
+ friend class TextureClient;
+ virtual void RecycleTextureClient(TextureClient* aClient) = 0;
+};
+
+class ITextureClientAllocationHelper
+{
+public:
+ ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : mFormat(aFormat)
+ , mSize(aSize)
+ , mSelector(aSelector)
+ , mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag
+ , mAllocationFlags(aAllocationFlags)
+ {}
+
+ virtual already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) = 0;
+ virtual bool IsCompatible(TextureClient* aTextureClient) = 0;
+
+ const gfx::SurfaceFormat mFormat;
+ const gfx::IntSize mSize;
+ const BackendSelector mSelector;
+ const TextureFlags mTextureFlags;
+ const TextureAllocationFlags mAllocationFlags;
+};
+
+class YCbCrTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags);
+
+ bool IsCompatible(TextureClient* aTextureClient) override;
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override;
+
+protected:
+ const PlanarYCbCrData& mData;
+};
+
+
+/**
+ * TextureClientRecycleAllocator provides TextureClients allocation and
+ * recycling capabilities. It expects allocations of same sizes and
+ * attributres. If a recycled TextureClient is different from
+ * requested one, the recycled one is dropped and new TextureClient is allocated.
+ *
+ * By default this uses TextureClient::CreateForDrawing to allocate new texture
+ * clients.
+ */
+class TextureClientRecycleAllocator : public ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~TextureClientRecycleAllocator();
+
+public:
+ explicit TextureClientRecycleAllocator(KnowsCompositor* aAllocator);
+
+ void SetMaxPoolSize(uint32_t aMax);
+
+ // Creates and allocates a TextureClient.
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(ITextureClientAllocationHelper& aHelper);
+
+ void ShrinkToMinimumSize();
+
+ void Destroy();
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ RefPtr<KnowsCompositor> mSurfaceAllocator;
+
+ friend class DefaultTextureClientAllocationHelper;
+ void RecycleTextureClient(TextureClient* aClient) override;
+
+ static const uint32_t kMaxPooledSized = 2;
+ uint32_t mMaxPooledSize;
+
+ std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ // stack is good from Graphics cache usage point of view.
+ std::stack<RefPtr<TextureClientHolder> > mPooledClients;
+ Mutex mLock;
+ bool mIsDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */
diff --git a/system/graphics/layers/client/TextureClientSharedSurface.cpp b/system/graphics/layers/client/TextureClientSharedSurface.cpp
new file mode 100644
index 000000000..b0cbfec21
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientSharedSurface.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureClientSharedSurface.h"
+
+#include "GLContext.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/Unused.h"
+#include "nsThreadUtils.h"
+#include "SharedSurface.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+
+SharedSurfaceTextureData::SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf)
+ : mSurf(Move(surf))
+{}
+
+SharedSurfaceTextureData::~SharedSurfaceTextureData()
+{}
+
+void
+SharedSurfaceTextureData::Deallocate(LayersIPCChannel*)
+{}
+
+void
+SharedSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSurf->mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+SharedSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ return mSurf->ToSurfaceDescriptor(&aOutDescriptor);
+}
+
+
+SharedSurfaceTextureClient::SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator)
+: TextureClient(aData, aFlags, aAllocator)
+{
+ mWorkaroundAnnoyingSharedSurfaceLifetimeIssues = true;
+}
+
+already_AddRefed<SharedSurfaceTextureClient>
+SharedSurfaceTextureClient::Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags)
+{
+ if (!surf) {
+ return nullptr;
+ }
+ TextureFlags flags = aFlags | TextureFlags::RECYCLE | surf->GetTextureFlags();
+ SharedSurfaceTextureData* data = new SharedSurfaceTextureData(Move(surf));
+ return MakeAndAddRef<SharedSurfaceTextureClient>(data, flags, aAllocator);
+}
+
+SharedSurfaceTextureClient::~SharedSurfaceTextureClient()
+{
+ // XXX - Things break when using the proper destruction handshake with
+ // SharedSurfaceTextureData because the TextureData outlives its gl
+ // context. Having a strong reference to the gl context creates a cycle.
+ // This needs to be fixed in a better way, though, because deleting
+ // the TextureData here can race with the compositor and cause flashing.
+ TextureData* data = mData;
+ mData = nullptr;
+
+ Destroy();
+
+ if (data) {
+ // Destroy mData right away without doing the proper deallocation handshake,
+ // because SharedSurface depends on things that may not outlive the texture's
+ // destructor so we can't wait until we know the compositor isn't using the
+ // texture anymore.
+ // It goes without saying that this is really bad and we should fix the bugs
+ // that block doing the right thing such as bug 1224199 sooner rather than
+ // later.
+ delete data;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/TextureClientSharedSurface.h b/system/graphics/layers/client/TextureClientSharedSurface.h
new file mode 100644
index 000000000..cfe5085f8
--- /dev/null
+++ b/system/graphics/layers/client/TextureClientSharedSurface.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+#define MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+
+#include <cstddef> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLContextTypes.h" // for GLContext (ptr only), etc
+#include "TextureClient.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class SharedSurface;
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class SharedSurfaceTextureClient;
+
+class SharedSurfaceTextureData : public TextureData
+{
+protected:
+ const UniquePtr<gl::SharedSurface> mSurf;
+
+ friend class SharedSurfaceTextureClient;
+
+ explicit SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf);
+public:
+
+ ~SharedSurfaceTextureData();
+
+ virtual bool Lock(OpenMode) override { return false; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ gl::SharedSurface* Surf() const { return mSurf.get(); }
+};
+
+class SharedSurfaceTextureClient : public TextureClient
+{
+public:
+ SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator);
+
+ ~SharedSurfaceTextureClient();
+
+ static already_AddRefed<SharedSurfaceTextureClient>
+ Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags);
+
+ gl::SharedSurface* Surf() const {
+ return static_cast<const SharedSurfaceTextureData*>(GetInternalData())->Surf();
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
diff --git a/system/graphics/layers/client/TiledContentClient.cpp b/system/graphics/layers/client/TiledContentClient.cpp
new file mode 100644
index 000000000..5dad3ec65
--- /dev/null
+++ b/system/graphics/layers/client/TiledContentClient.cpp
@@ -0,0 +1,1405 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/TiledContentClient.h"
+#include <math.h> // for ceil, ceilf, floor
+#include <algorithm>
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/MathAlgorithms.h" // for Abs
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Tools.h" // for BytesPerPixel
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "TextureClientPool.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsExpirationTracker.h" // for nsExpirationTracker
+#include "nsMathUtils.h" // for NS_lroundf
+#include "LayersLogging.h"
+#include "UnitTransforms.h" // for TransformTo
+#include "mozilla/UniquePtr.h"
+
+// This is the minimum area that we deem reasonable to copy from the front buffer to the
+// back buffer on tile updates. If the valid region is smaller than this, we just
+// redraw it and save on the copy (and requisite surface-locking involved).
+#define MINIMUM_TILE_COPY_AREA (1.f/16.f)
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+#include "cairo.h"
+#include <sstream>
+using mozilla::layers::Layer;
+static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height)
+{
+ gfxContext c(dt);
+
+ // Draw border
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(0, 0, width, height));
+ c.Stroke();
+
+ // Build tile description
+ std::stringstream ss;
+ ss << x << ", " << y;
+
+ // Draw text using cairo toy text API
+ // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
+ cairo_t* cr = gfxFont::RefCairo(dt);
+ cairo_set_font_size(cr, 25);
+ cairo_text_extents_t extents;
+ cairo_text_extents(cr, ss.str().c_str(), &extents);
+
+ int textWidth = extents.width + 6;
+
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Fill();
+
+ c.NewPath();
+ c.SetDeviceColor(Color(1.0, 0.0, 0.0));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Stroke();
+
+ c.NewPath();
+ cairo_move_to(cr, 4, 28);
+ cairo_show_text(cr, ss.str().c_str());
+
+}
+
+#endif
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+
+MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Multi")
+ , mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+ , mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+{
+ MOZ_COUNT_CTOR(MultiTiledContentClient);
+ mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
+ mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
+}
+
+void
+MultiTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+}
+
+void
+MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
+ ? &mLowPrecisionTiledBuffer
+ : &mTiledBuffer;
+
+ MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
+
+ mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
+ buffer->ClearPaintedRegion();
+}
+
+SharedFrameMetricsHelper::SharedFrameMetricsHelper()
+ : mLastProgressiveUpdateWasLowPrecision(false)
+ , mProgressiveUpdateWasInDanger(false)
+{
+ MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
+}
+
+SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
+{
+ MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
+}
+
+static inline bool
+FuzzyEquals(float a, float b) {
+ return (fabsf(a - b) < 1e-6);
+}
+
+static AsyncTransform
+ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aCompositorMetrics)
+{
+ // This is basically the same code as AsyncPanZoomController::GetCurrentAsyncTransform
+ // but with aContentMetrics used in place of mLastContentPaintMetrics, because they
+ // should be equivalent, modulo race conditions while transactions are inflight.
+
+ ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
+ * aCompositorMetrics.GetZoom();
+ return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
+}
+
+bool
+SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
+ const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform)
+{
+ MOZ_ASSERT(aLayer);
+
+ CompositorBridgeChild* compositor = nullptr;
+ if (aLayer.Manager() &&
+ aLayer.Manager()->AsClientLayerManager()) {
+ compositor = aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ const FrameMetrics& contentMetrics = aLayer.Metrics();
+ FrameMetrics compositorMetrics;
+
+ if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
+
+ // Reset the checkerboard risk flag when switching to low precision
+ // rendering.
+ if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
+ // Skip low precision rendering until we're at risk of checkerboarding.
+ if (!mProgressiveUpdateWasInDanger) {
+ TILING_LOG("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n");
+ return true;
+ }
+ mProgressiveUpdateWasInDanger = false;
+ }
+ mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
+
+ // Always abort updates if the resolution has changed. There's no use
+ // in drawing at the incorrect resolution.
+ if (!FuzzyEquals(compositorMetrics.GetZoom().xScale, contentMetrics.GetZoom().xScale) ||
+ !FuzzyEquals(compositorMetrics.GetZoom().yScale, contentMetrics.GetZoom().yScale)) {
+ TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
+ ToString(contentMetrics.GetZoom()).c_str(), ToString(compositorMetrics.GetZoom()).c_str());
+ return true;
+ }
+
+ // Never abort drawing if we can't be sure we've sent a more recent
+ // display-port. If we abort updating when we shouldn't, we can end up
+ // with blank regions on the screen and we open up the risk of entering
+ // an endless updating cycle.
+ if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
+ fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().x - compositorMetrics.GetDisplayPort().x) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().y - compositorMetrics.GetDisplayPort().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().width - compositorMetrics.GetDisplayPort().width) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().height - compositorMetrics.GetDisplayPort().height) <= 2) {
+ return false;
+ }
+
+ // When not a low precision pass and the page is in danger of checker boarding
+ // abort update.
+ if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
+ bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
+ contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
+ // If scrollUpdatePending is true, then that means the content-side
+ // metrics has a new scroll offset that is going to be forced into the
+ // compositor but it hasn't gotten there yet.
+ // Even though right now comparing the metrics might indicate we're
+ // about to checkerboard (and that's true), the checkerboarding will
+ // disappear as soon as the new scroll offset update is processed
+ // on the compositor side. To avoid leaving things in a low-precision
+ // paint, we need to detect and handle this case (bug 1026756).
+ if (!scrollUpdatePending && AboutToCheckerboard(contentMetrics, compositorMetrics)) {
+ mProgressiveUpdateWasInDanger = true;
+ return true;
+ }
+ }
+
+ // Abort drawing stale low-precision content if there's a more recent
+ // display-port in the pipeline.
+ if (aLowPrecision && !aHasPendingNewThebesContent) {
+ TILING_LOG("TILING: Aborting low-precision because of new pending content\n");
+ return true;
+ }
+
+ return false;
+}
+
+bool
+SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics)
+{
+ // The size of the painted area is originally computed in layer pixels in layout, but then
+ // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
+ // This process can introduce some rounding error, so we inflate the rect by one app unit
+ // to account for that.
+ CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
+ ? aContentMetrics.GetDisplayPort()
+ : aContentMetrics.GetCriticalDisplayPort())
+ + aContentMetrics.GetScrollOffset();
+ painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
+
+ // Inflate the rect by the danger zone. See the description of the danger zone prefs
+ // in AsyncPanZoomController.cpp for an explanation of this.
+ CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
+ aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
+ showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
+ / aCompositorMetrics.LayersPixelsPerCSSPixel());
+
+ // Clamp both rects to the scrollable rect, because having either of those
+ // exceed the scrollable rect doesn't make sense, and could lead to false
+ // positives.
+ painted = painted.Intersect(aContentMetrics.GetScrollableRect());
+ showing = showing.Intersect(aContentMetrics.GetScrollableRect());
+
+ if (!painted.Contains(showing)) {
+ TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
+ TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
+ return true;
+ }
+ return false;
+}
+
+ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mManager(aManager)
+ , mCallback(nullptr)
+ , mCallbackData(nullptr)
+ , mSharedFrameMetricsHelper(aHelper)
+{
+}
+
+bool
+ClientTiledLayerBuffer::HasFormatChanged() const
+{
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ return content != mLastPaintContentType ||
+ mode != mLastPaintSurfaceMode;
+}
+
+
+gfxContentType
+ClientTiledLayerBuffer::GetContentType(SurfaceMode* aMode) const
+{
+ gfxContentType content =
+ mPaintedLayer.CanUseOpaqueSurface() ? gfxContentType::COLOR :
+ gfxContentType::COLOR_ALPHA;
+ SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!mPaintedLayer.GetParent() ||
+ !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ content = gfxContentType::COLOR;
+ }
+ } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
+ if (mPaintedLayer.MayResample()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+ }
+
+ if (aMode) {
+ *aMode = mode;
+ }
+ return content;
+}
+
+class TileExpiry final : public nsExpirationTracker<TileClient, 3>
+{
+ public:
+ TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
+
+ static void AddTile(TileClient* aTile)
+ {
+ if (!sTileExpiry) {
+ sTileExpiry = MakeUnique<TileExpiry>();
+ }
+
+ sTileExpiry->AddObject(aTile);
+ }
+
+ static void RemoveTile(TileClient* aTile)
+ {
+ MOZ_ASSERT(sTileExpiry);
+ sTileExpiry->RemoveObject(aTile);
+ }
+
+ static void Shutdown() {
+ sTileExpiry = nullptr;
+ }
+ private:
+ virtual void NotifyExpired(TileClient* aTile) override
+ {
+ aTile->DiscardBackBuffer();
+ }
+
+ static UniquePtr<TileExpiry> sTileExpiry;
+};
+UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
+
+void ShutdownTileCache()
+{
+ TileExpiry::Shutdown();
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
+{
+ if (mBuffer) {
+ TileExpiry::RemoveTile(aContainer);
+ }
+ mBuffer = aNewValue;
+ if (mBuffer) {
+ TileExpiry::AddTile(aContainer);
+ }
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
+{
+ Set(aContainer, RefPtr<TextureClient>(aNewValue));
+}
+
+// Placeholder
+TileClient::TileClient()
+ : mWasPlaceholder(false)
+{
+}
+
+TileClient::~TileClient()
+{
+ if (mExpirationState.IsTracked()) {
+ MOZ_ASSERT(mBackBuffer);
+ TileExpiry::RemoveTile(this);
+ }
+}
+
+TileClient::TileClient(const TileClient& o)
+{
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+}
+
+TileClient&
+TileClient::operator=(const TileClient& o)
+{
+ if (this == &o) return *this;
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+ return *this;
+}
+
+void
+TileClient::Dump(std::stringstream& aStream)
+{
+ aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
+ if (mBackBufferOnWhite) {
+ aStream << " bbow=" << mBackBufferOnWhite.get();
+ }
+ if (mFrontBufferOnWhite) {
+ aStream << " fbow=" << mFrontBufferOnWhite.get();
+ }
+ aStream << ")";
+}
+
+void
+TileClient::Flip()
+{
+ RefPtr<TextureClient> frontBuffer = mFrontBuffer;
+ RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
+ mFrontBuffer = mBackBuffer;
+ mFrontBufferOnWhite = mBackBufferOnWhite;
+ mBackBuffer.Set(this, frontBuffer);
+ mBackBufferOnWhite = frontBufferOnWhite;
+ nsIntRegion invalidFront = mInvalidFront;
+ mInvalidFront = mInvalidBack;
+ mInvalidBack = invalidFront;
+}
+
+static bool
+CopyFrontToBack(TextureClient* aFront,
+ TextureClient* aBack,
+ const gfx::IntRect& aRectToCopy)
+{
+ TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
+ if (!frontLock.Succeeded()) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
+ return false;
+ }
+
+ if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
+ return false;
+ }
+
+ gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
+ aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+ return true;
+}
+
+void
+TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion)
+{
+ if (mBackBuffer && mFrontBuffer) {
+ gfx::IntSize tileSize = mFrontBuffer->GetSize();
+ const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
+
+ if (aDirtyRegion.Contains(tileRect)) {
+ // The dirty region means that we no longer need the front buffer, so
+ // discard it.
+ DiscardFrontBuffer();
+ } else {
+ // Region that needs copying.
+ nsIntRegion regionToCopy = mInvalidBack;
+
+ regionToCopy.Sub(regionToCopy, aDirtyRegion);
+
+ aAddPaintedRegion = regionToCopy;
+
+ if (regionToCopy.IsEmpty()) {
+ // Just redraw it all.
+ return;
+ }
+
+ // Copy the bounding rect of regionToCopy. As tiles are quite small, it
+ // is unlikely that we'd save much by copying each individual rect of the
+ // region, but we can reevaluate this if it becomes an issue.
+ const IntRect rectToCopy = regionToCopy.GetBounds();
+ gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height);
+ CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy);
+
+ if (mBackBufferOnWhite) {
+ MOZ_ASSERT(mFrontBufferOnWhite);
+ CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy);
+ }
+
+ mInvalidBack.SetEmpty();
+ }
+ }
+}
+
+void
+TileClient::DiscardFrontBuffer()
+{
+ if (mFrontBuffer) {
+ MOZ_ASSERT(mFrontBuffer->GetReadLock());
+
+ MOZ_ASSERT(mAllocator);
+ if (mAllocator) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
+ }
+ }
+
+ if (mFrontBuffer->IsLocked()) {
+ mFrontBuffer->Unlock();
+ }
+ if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
+ mFrontBufferOnWhite->Unlock();
+ }
+ mFrontBuffer = nullptr;
+ mFrontBufferOnWhite = nullptr;
+ }
+}
+
+static void
+DiscardTexture(TextureClient* aTexture, TextureClientAllocator* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ if (aTexture && aAllocator) {
+ MOZ_ASSERT(aTexture->GetReadLock());
+ if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ } else {
+ aAllocator->ReturnTextureClientDeferred(aTexture);
+ }
+ if (aTexture->IsLocked()) {
+ aTexture->Unlock();
+ }
+ }
+}
+
+void
+TileClient::DiscardBackBuffer()
+{
+ if (mBackBuffer) {
+ DiscardTexture(mBackBuffer, mAllocator);
+ mBackBuffer.Set(this, nullptr);
+ DiscardTexture(mBackBufferOnWhite, mAllocator);
+ mBackBufferOnWhite = nullptr;
+ }
+}
+
+static already_AddRefed<TextureClient>
+CreateBackBufferTexture(TextureClient* aCurrentTexture,
+ CompositableClient& aCompositable,
+ TextureClientAllocator* aAllocator)
+{
+ if (aCurrentTexture) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ }
+
+ RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
+
+ if (!texture) {
+ gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
+ return nullptr;
+ }
+
+ texture->EnableReadLock();
+
+ if (!aCompositable.AddTextureClient(texture)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
+ return nullptr;
+ }
+
+ return texture.forget();
+}
+
+TextureClient*
+TileClient::GetBackBuffer(CompositableClient& aCompositable,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent,
+ SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aBackBufferOnWhite)
+{
+ if (!mAllocator) {
+ gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
+ return nullptr;
+ }
+ if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ // It can happen that a component-alpha layer stops being on component alpha
+ // on the next frame, just drop the buffers on white if that happens.
+ if (mBackBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mBackBufferOnWhite = nullptr;
+ }
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mFrontBufferOnWhite = nullptr;
+ }
+ }
+
+ // Try to re-use the front-buffer if possible
+ if (mFrontBuffer &&
+ mFrontBuffer->HasIntermediateBuffer() &&
+ !mFrontBuffer->IsReadLocked() &&
+ (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
+ mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
+ // If we had a backbuffer we no longer care about it since we'll
+ // re-use the front buffer.
+ DiscardBackBuffer();
+ Flip();
+ } else {
+ if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
+ mBackBuffer.Set(this,
+ CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
+ );
+ if (!mBackBuffer) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
+ }
+
+ if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
+ && (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
+ mBackBufferOnWhite = CreateBackBufferTexture(
+ mBackBufferOnWhite, aCompositable, mAllocator
+ );
+ if (!mBackBufferOnWhite) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
+ }
+
+ ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
+ }
+
+ if (!mBackBuffer->IsLocked()) {
+ if (!mBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
+ if (!mBackBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ *aBackBufferOnWhite = mBackBufferOnWhite;
+ return mBackBuffer;
+}
+
+TileDescriptor
+TileClient::GetTileDescriptor()
+{
+ if (IsPlaceholderTile()) {
+ mWasPlaceholder = true;
+ return PlaceholderTileDescriptor();
+ }
+ bool wasPlaceholder = mWasPlaceholder;
+ mWasPlaceholder = false;
+
+ ReadLockDescriptor lock;
+ mFrontBuffer->SerializeReadLock(lock);
+
+ ReadLockDescriptor lockOnWhite = null_t();
+ if (mFrontBufferOnWhite) {
+ mFrontBufferOnWhite->SerializeReadLock(lockOnWhite);
+ }
+
+ return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+ mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+ mUpdateRect,
+ lock, lockOnWhite,
+ wasPlaceholder);
+}
+
+void
+ClientMultiTiledLayerBuffer::DiscardBuffers()
+{
+ for (TileClient& tile : mRetainedTiles) {
+ tile.DiscardBuffers();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ for (TileClient& tile : mRetainedTiles) {
+ TileDescriptor tileDesc = tile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ // Reset the update rect
+ tile.mUpdateRect = IntRect();
+ }
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTileOrigin, mTileSize,
+ mTiles.mFirst.x, mTiles.mFirst.y,
+ mTiles.mSize.width, mTiles.mSize.height,
+ mResolution, mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+void
+ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str());
+ TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str());
+
+ mCallback = aCallback;
+ mCallbackData = aCallbackData;
+ mWasLastPaintProgressive = aIsProgressive;
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ long start = PR_IntervalNow();
+#endif
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 30) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ if (aPaintRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ printf_stderr(" rect %i, %i, %i, %i\n",
+ rect.x, rect.y, rect.width, rect.height);
+ }
+ }
+ }
+ start = PR_IntervalNow();
+#endif
+
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "PaintThebesUpdate",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mNewValidRegion = aNewValidRegion;
+ Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 10) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+#endif
+
+ mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
+ mCallback = nullptr;
+ mCallbackData = nullptr;
+}
+
+void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion &region)
+{
+ struct LockedBits {
+ uint8_t *data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ static int clamp(int x, int min, int max)
+ {
+ if (x < min)
+ x = min;
+ if (x > max)
+ x = max;
+ return x;
+ }
+
+ static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
+ {
+ if (src + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long src memcpy");
+ }
+ if (src < bitmap) {
+ MOZ_CRASH("GFX: short src memcpy");
+ }
+ if (dst + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long dst mempcy");
+ }
+ if (dst < bitmap) {
+ MOZ_CRASH("GFX: short dst mempcy");
+ }
+ }
+
+ static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
+ LockedBits *lb = static_cast<LockedBits*>(closure);
+ uint8_t *bitmap = lb->data;
+ const int bpp = gfx::BytesPerPixel(lb->format);
+ const int stride = lb->stride;
+ const int width = lb->size.width;
+ const int height = lb->size.height;
+
+ if (side == VisitSide::TOP) {
+ if (y1 > 0) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ if (y1 < height) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::LEFT) {
+ if (x1 > 0) {
+ while (y1 != y2) {
+ memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ } else if (side == VisitSide::RIGHT) {
+ if (x1 < width) {
+ while (y1 != y2) {
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ }
+
+ }
+ } lb;
+
+ if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
+ // we can only pad software targets so if we can't lock the bits don't pad
+ region.VisitEdges(lb.visitor, &lb);
+ drawTarget->ReleaseBits(lb.data);
+ }
+}
+
+void
+ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
+{
+ // We locked the back buffer, and flipped so we now need to unlock the front
+ if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
+ aTile.mFrontBuffer->Unlock();
+ aTile.mFrontBuffer->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
+ aTile.mFrontBufferOnWhite->Unlock();
+ aTile.mFrontBufferOnWhite->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
+ aTile.mBackBuffer->Unlock();
+ }
+ if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
+ aTile.mBackBufferOnWhite->Unlock();
+ }
+}
+
+void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion)
+{
+ const IntSize scaledTileSize = GetScaledTileSize();
+ const gfx::IntRect newBounds = newValidRegion.GetBounds();
+
+ const TilesPlacement oldTiles = mTiles;
+ const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
+ floor_div(newBounds.y, scaledTileSize.height),
+ floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
+ + newBounds.width, scaledTileSize.width) + 1,
+ floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
+ + newBounds.height, scaledTileSize.height) + 1);
+
+ const size_t oldTileCount = mRetainedTiles.Length();
+ const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
+
+ nsTArray<TileClient> oldRetainedTiles;
+ mRetainedTiles.SwapElements(oldRetainedTiles);
+ mRetainedTiles.SetLength(newTileCount);
+
+ for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
+ const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
+ const size_t newIndex = newTiles.TileIndex(tilePosition);
+ // First, get the already existing tiles to the right place in the new array.
+ // Leave placeholders (default constructor) where there was no tile.
+ if (newTiles.HasTile(tilePosition)) {
+ mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
+ } else {
+ // release tiles that we are not going to reuse before allocating new ones
+ // to avoid allocating unnecessarily.
+ oldRetainedTiles[oldIndex].DiscardBuffers();
+ }
+ }
+
+ oldRetainedTiles.Clear();
+
+ if (!aPaintRegion.IsEmpty()) {
+ for (size_t i = 0; i < newTileCount; ++i) {
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ TileClient& tile = mRetainedTiles[i];
+ if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
+ gfxCriticalError() << "ValidateTile failed";
+ }
+ }
+
+ if (mMoz2DTiles.size() > 0) {
+ gfx::TileSet tileset;
+ for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
+ mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
+ }
+ tileset.mTiles = &mMoz2DTiles[0];
+ tileset.mTileCount = mMoz2DTiles.size();
+ RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+ if (!drawTarget || !drawTarget->IsValid()) {
+ gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
+ return;
+ }
+ drawTarget->SetTransform(Matrix());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+ MOZ_ASSERT(ctx); // already checked the draw target above
+ ctx->SetMatrix(
+ ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
+
+ mCallback(&mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+ DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+ mMoz2DTiles.clear();
+ // Reset:
+ mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ }
+
+ bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
+
+ for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
+ TileClient& tile = mRetainedTiles[i];
+
+ // Only worry about padding when not doing low-res because it simplifies
+ // the math and the artifacts won't be noticable
+ // Edge padding prevents sampling artifacts when compositing.
+ if (edgePaddingEnabled && mResolution == 1 &&
+ tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
+
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ // Strictly speakig we want the unscaled rect here, but it doesn't matter
+ // because we only run this code when the resolution is equal to 1.
+ IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
+ GetTileSize().width, GetTileSize().height);
+
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ nsIntRegion tileValidRegion = mValidRegion;
+ tileValidRegion.OrWith(tileDrawRegion);
+
+ // We only need to pad out if the tile has area that's not valid
+ if (!tileValidRegion.Contains(tileRect)) {
+ tileValidRegion = tileValidRegion.Intersect(tileRect);
+ // translate the region into tile space and pad
+ tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
+ RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
+ PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+ }
+ }
+ UnlockTile(tile);
+ }
+ }
+
+ mTiles = newTiles;
+ mValidRegion = newValidRegion;
+ mPaintedRegion.OrWith(aPaintRegion);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileOrigin,
+ const nsIntRegion& aDirtyRegion)
+{
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ValidateTile",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (aDirtyRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ }
+#endif
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+
+ if (!aTile.mAllocator) {
+ aTile.SetTextureAllocator(mManager->GetCompositorBridgeChild()->GetTexturePool(
+ mManager->AsShadowForwarder(),
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD));
+ MOZ_ASSERT(aTile.mAllocator);
+ }
+
+ nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
+ offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ aTile.GetBackBuffer(mCompositableClient,
+ offsetScaledDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(aTileOrigin);
+ extraPainted.And(extraPainted, mNewValidRegion);
+ mPaintedRegion.Or(mPaintedRegion, extraPainted);
+
+ if (!backBuffer) {
+ return false;
+ }
+
+ gfx::Tile moz2DTile;
+ RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ } else {
+ moz2DTile.mDrawTarget = dt;
+ }
+ moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+ if (!dt || (backBufferOnWhite && !dtOnWhite)) {
+ aTile.DiscardBuffers();
+ return false;
+ }
+
+ mMoz2DTiles.push_back(moz2DTile);
+ mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
+ mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
+
+ for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& dirtyRect = iter.Get();
+ gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
+ dirtyRect.y - aTileOrigin.y,
+ dirtyRect.width,
+ dirtyRect.height);
+ drawRect.Scale(mResolution);
+
+ // Mark the newly updated area as invalid in the front buffer
+ aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ } else if (content == gfxContentType::COLOR_ALPHA) {
+ dt->ClearRect(drawRect);
+ }
+ }
+
+ // The new buffer is now validated, remove the dirty region from it.
+ aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
+
+ aTile.Flip();
+
+ return true;
+}
+
+/**
+ * This function takes the transform stored in aTransformToCompBounds
+ * (which was generated in GetTransformToAncestorsParentLayer), and
+ * modifies it with the ViewTransform from the compositor side so that
+ * it reflects what the compositor is actually rendering. This operation
+ * basically adds in the layer's async transform.
+ * This function then returns the scroll ancestor's composition bounds,
+ * transformed into the painted layer's LayerPixel coordinates, accounting
+ * for the compositor state.
+ */
+static Maybe<LayerRect>
+GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor,
+ const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
+ const AsyncTransform& aAPZTransform,
+ const LayerRect& aClip)
+{
+ LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds *
+ AsyncTransformComponentMatrix(aAPZTransform);
+
+ return UntransformBy(transform.Inverse(),
+ aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated)
+{
+ aRegionToPaint = aInvalidRegion;
+
+ // If the composition bounds rect is empty, we can't make any sensible
+ // decision about how to update coherently. In this case, just update
+ // everything in one transaction.
+ if (aPaintData->mCompositionBounds.IsEmpty()) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ // If this is a low precision buffer, we force progressive updates. The
+ // assumption is that the contents is less important, so visual coherency
+ // is lower priority than speed.
+ bool drawingLowPrecision = IsLowPrecision();
+
+ // Find out if we have any non-stale content to update.
+ nsIntRegion staleRegion;
+ staleRegion.And(aInvalidRegion, aOldValidRegion);
+
+ TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer, Stringify(staleRegion).c_str());
+
+ LayerMetricsWrapper scrollAncestor;
+ mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+
+ // Find out the current view transform to determine which tiles to draw
+ // first, and see if we should just abort this paint. Aborting is usually
+ // caused by there being an incoming, more relevant paint.
+ AsyncTransform viewTransform;
+ MOZ_ASSERT(mSharedFrameMetricsHelper);
+
+ bool abortPaint =
+ mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
+ scrollAncestor,
+ !staleRegion.Contains(aInvalidRegion),
+ drawingLowPrecision,
+ viewTransform);
+
+ TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n",
+ &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint);
+
+ if (abortPaint) {
+ // We ignore if front-end wants to abort if this is the first,
+ // non-low-precision paint, as in that situation, we're about to override
+ // front-end's page/viewport metrics.
+ if (!aPaintData->mFirstPaint || drawingLowPrecision) {
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ComputeProgressiveUpdateRegion",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ aRegionToPaint.SetEmpty();
+ return aIsRepeated;
+ }
+ }
+
+ Maybe<LayerRect> transformedCompositionBounds =
+ GetCompositorSideCompositionBounds(scrollAncestor,
+ aPaintData->mTransformToCompBounds,
+ viewTransform,
+ ViewAs<LayerPixel>(Rect(mPaintedLayer.GetLayerBounds())));
+
+ if (!transformedCompositionBounds) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
+
+ // Compute a "coherent update rect" that we should paint all at once in a
+ // single transaction. This is to avoid rendering glitches on animated
+ // page content, and when layers change size/shape.
+ IntRect coherentUpdateRect(RoundedOut(*transformedCompositionBounds).ToUnknownRect());
+
+ TILING_LOG("TILING %p: Progressive update final coherency rect %s\n", &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
+
+ aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
+ aRegionToPaint.Or(aRegionToPaint, staleRegion);
+ bool drawingStale = !aRegionToPaint.IsEmpty();
+ if (!drawingStale) {
+ aRegionToPaint = aInvalidRegion;
+ }
+
+ // Prioritise tiles that are currently visible on the screen.
+ bool paintingVisible = false;
+ if (aRegionToPaint.Intersects(coherentUpdateRect)) {
+ aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
+ paintingVisible = true;
+ }
+
+ TILING_LOG("TILING %p: Progressive update final paint region %s\n", &mPaintedLayer, Stringify(aRegionToPaint).c_str());
+
+ // Paint area that's visible and overlaps previously valid content to avoid
+ // visible glitches in animated elements, such as gifs.
+ bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint);
+
+ TILING_LOG("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
+ &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction);
+
+ // The following code decides what order to draw tiles in, based on the
+ // current scroll direction of the primary scrollable layer.
+ NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
+ IntRect paintBounds = aRegionToPaint.GetBounds();
+
+ int startX, incX, startY, incY;
+ gfx::IntSize scaledTileSize = GetScaledTileSize();
+ if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
+ startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width);
+ incX = scaledTileSize.width;
+ } else {
+ startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
+ incX = -scaledTileSize.width;
+ }
+
+ if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
+ startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height);
+ incY = scaledTileSize.height;
+ } else {
+ startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
+ incY = -scaledTileSize.height;
+ }
+
+ // Find a tile to draw.
+ IntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height);
+ int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
+ int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
+ // This loop will always terminate, as there is at least one tile area
+ // along the first/last row/column intersecting with regionToPaint, or its
+ // bounds would have been smaller.
+ while (true) {
+ aRegionToPaint.And(aInvalidRegion, tileBounds);
+ if (!aRegionToPaint.IsEmpty()) {
+ if (mResolution != 1) {
+ // Paint the entire tile for low-res. This is aimed to fixing low-res resampling
+ // and to avoid doing costly region accurate painting for a small area.
+ aRegionToPaint = tileBounds;
+ }
+ break;
+ }
+ if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
+ tileBounds.x += incX;
+ } else {
+ tileBounds.y += incY;
+ }
+ }
+
+ if (!aRegionToPaint.Contains(aInvalidRegion)) {
+ // The region needed to paint is larger then our progressive chunk size
+ // therefore update what we want to paint and ask for a new paint transaction.
+
+ // If we need to draw more than one tile to maintain coherency, make
+ // sure it happens in the same transaction by requesting this work be
+ // repeated immediately.
+ // If this is unnecessary, the remaining work will be done tile-by-tile in
+ // subsequent transactions. The caller code is responsible for scheduling
+ // the subsequent transactions as long as we don't set the mPaintFinished
+ // flag to true.
+ return (!drawingLowPrecision && paintInSingleTransaction);
+ }
+
+ // We're not repeating painting and we've not requested a repeat transaction,
+ // so the paint is finished. If there's still a separate low precision
+ // paint to do, it will get marked as unfinished later.
+ aPaintData->mPaintFinished = true;
+ return false;
+}
+
+bool
+ClientMultiTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer, Stringify(aValidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update old valid region %s\n", &mPaintedLayer, Stringify(aOldValidRegion).c_str());
+
+ bool repeat = false;
+ bool isBufferChanged = false;
+ do {
+ // Compute the region that should be updated. Repeat as many times as
+ // is required.
+ nsIntRegion regionToPaint;
+ repeat = ComputeProgressiveUpdateRegion(aInvalidRegion,
+ aOldValidRegion,
+ regionToPaint,
+ aPaintData,
+ repeat);
+
+ TILING_LOG("TILING %p: Progressive update computed paint region %s repeat %d\n", &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
+
+ // There's no further work to be done.
+ if (regionToPaint.IsEmpty()) {
+ break;
+ }
+
+ isBufferChanged = true;
+
+ // Keep track of what we're about to refresh.
+ aValidRegion.Or(aValidRegion, regionToPaint);
+
+ // aValidRegion may have been altered by InvalidateRegion, but we still
+ // want to display stale content until it gets progressively updated.
+ // Create a region that includes stale content.
+ nsIntRegion validOrStale;
+ validOrStale.Or(aValidRegion, aOldValidRegion);
+
+ // Paint the computed region and subtract it from the invalid region.
+ PaintThebes(validOrStale, regionToPaint, aInvalidRegion,
+ aCallback, aCallbackData, true);
+ aInvalidRegion.Sub(aInvalidRegion, regionToPaint);
+ } while (repeat);
+
+ TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(aValidRegion).c_str(), isBufferChanged);
+ TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+
+ // Return false if nothing has been drawn, or give what has been drawn
+ // to the shadow layer to upload.
+ return isBufferChanged;
+}
+
+void
+TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+void
+TiledContentClient::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress)
+{
+ GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
+}
+
+void
+BasicTiledLayerPaintData::ResetPaintData()
+{
+ mLowPrecisionPaintCount = 0;
+ mPaintFinished = false;
+ mHasTransformAnimation = false;
+ mCompositionBounds.SetEmpty();
+ mCriticalDisplayPort = Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/client/TiledContentClient.h b/system/graphics/layers/client/TiledContentClient.h
new file mode 100644
index 000000000..cf55450f8
--- /dev/null
+++ b/system/graphics/layers/client/TiledContentClient.h
@@ -0,0 +1,545 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_TILEDCONTENTCLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t
+#include <algorithm> // for swap
+#include <limits>
+#include "Layers.h" // for LayerManager, etc
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer
+#include "Units.h" // for CSSPoint
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersMessages.h" // for TileDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientPool.h"
+#include "ClientLayerManager.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for MOZ_COUNT_DTOR
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsExpirationTracker.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Represent a single tile in tiled buffer. The buffer keeps tiles,
+ * each tile keeps a reference to a texture client and a read-lock. This
+ * read-lock is used to help implement a copy-on-write mechanism. The tile
+ * should be locked before being sent to the compositor. The compositor should
+ * unlock the read-lock as soon as it has finished with the buffer in the
+ * TextureHost to prevent more textures being created than is necessary.
+ * Ideal place to store per tile debug information.
+ */
+struct TileClient
+{
+ // Placeholder
+ TileClient();
+ ~TileClient();
+
+ TileClient(const TileClient& o);
+
+ TileClient& operator=(const TileClient& o);
+
+ bool operator== (const TileClient& o) const
+ {
+ return mFrontBuffer == o.mFrontBuffer;
+ }
+
+ bool operator!= (const TileClient& o) const
+ {
+ return mFrontBuffer != o.mFrontBuffer;
+ }
+
+ void SetTextureAllocator(TextureClientAllocator* aAllocator)
+ {
+ mAllocator = aAllocator;
+ }
+
+ bool IsPlaceholderTile() const
+ {
+ return mBackBuffer == nullptr && mFrontBuffer == nullptr;
+ }
+
+ void DiscardBuffers()
+ {
+ DiscardFrontBuffer();
+ DiscardBackBuffer();
+ }
+
+ nsExpirationState *GetExpirationState() { return &mExpirationState; }
+
+ TileDescriptor GetTileDescriptor();
+
+ /**
+ * For debugging.
+ */
+ void Dump(std::stringstream& aStream);
+
+ /**
+ * Swaps the front and back buffers.
+ */
+ void Flip();
+
+ void DumpTexture(std::stringstream& aStream, TextureDumpMode aCompress) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ CompositableClient::DumpTextureClient(aStream, mFrontBuffer, aCompress);
+ }
+
+ /**
+ * Returns an unlocked TextureClient that can be used for writing new
+ * data to the tile. This may flip the front-buffer to the back-buffer if
+ * the front-buffer is still locked by the host, or does not have an
+ * internal buffer (and so will always be locked).
+ *
+ * If getting the back buffer required copying pixels from the front buffer
+ * then the copied region is stored in aAddPaintedRegion so the host side
+ * knows to upload it.
+ *
+ * If nullptr is returned, aTextureClientOnWhite is undefined.
+ */
+ TextureClient* GetBackBuffer(CompositableClient&,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent, SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aTextureClientOnWhite);
+
+ void DiscardFrontBuffer();
+
+ void DiscardBackBuffer();
+
+ /* We wrap the back buffer in a class that disallows assignment
+ * so that we can track when ever it changes so that we can update
+ * the expiry tracker for expiring the back buffers */
+ class PrivateProtector {
+ public:
+ void Set(TileClient * container, RefPtr<TextureClient>);
+ void Set(TileClient * container, TextureClient*);
+ // Implicitly convert to TextureClient* because we can't chain
+ // implicit conversion that would happen on RefPtr<TextureClient>
+ operator TextureClient*() const { return mBuffer; }
+ RefPtr<TextureClient> operator ->() { return mBuffer; }
+ private:
+ PrivateProtector& operator=(const PrivateProtector &);
+ RefPtr<TextureClient> mBuffer;
+ } mBackBuffer;
+ RefPtr<TextureClient> mBackBufferOnWhite;
+ RefPtr<TextureClient> mFrontBuffer;
+ RefPtr<TextureClient> mFrontBufferOnWhite;
+ RefPtr<TextureClientAllocator> mAllocator;
+ gfx::IntRect mUpdateRect;
+ bool mWasPlaceholder;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ TimeStamp mLastUpdate;
+#endif
+ nsIntRegion mInvalidFront;
+ nsIntRegion mInvalidBack;
+ nsExpirationState mExpirationState;
+private:
+ // Copies dirty pixels from the front buffer into the back buffer,
+ // and records the copied region in aAddPaintedRegion.
+ void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion);
+};
+
+/**
+ * This struct stores all the data necessary to perform a paint so that it
+ * doesn't need to be recalculated on every repeated transaction.
+ */
+struct BasicTiledLayerPaintData {
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set.
+ */
+ ParentLayerPoint mScrollOffset;
+
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set, for the last
+ * layer update transaction.
+ */
+ ParentLayerPoint mLastScrollOffset;
+
+ /*
+ * The transform matrix to go from this layer's Layer units to
+ * the scroll ancestor's ParentLayer units. The "scroll ancestor" is
+ * the closest ancestor layer which scrolls, and is used to obtain
+ * the composition bounds that are relevant for this layer.
+ */
+ LayerToParentLayerMatrix4x4 mTransformToCompBounds;
+
+ /*
+ * The critical displayport of the content from the nearest ancestor layer
+ * that represents scrollable content with a display port set. isNothing()
+ * if a critical displayport is not set.
+ */
+ Maybe<LayerIntRect> mCriticalDisplayPort;
+
+ /*
+ * The render resolution of the document that the content this layer
+ * represents is in.
+ */
+ CSSToParentLayerScale2D mResolution;
+
+ /*
+ * The composition bounds of the layer, in Layer coordinates. This is
+ * used to make sure that tiled updates to regions that are visible to the
+ * user are grouped coherently.
+ */
+ LayerRect mCompositionBounds;
+
+ /*
+ * Low precision updates are always executed a tile at a time in repeated
+ * transactions. This counter is set to 1 on the first transaction of a low
+ * precision update, and incremented for each subsequent transaction.
+ */
+ uint16_t mLowPrecisionPaintCount;
+
+ /*
+ * Whether this is the first time this layer is painting
+ */
+ bool mFirstPaint : 1;
+
+ /*
+ * Whether there is further work to complete this paint. This is used to
+ * determine whether or not to repeat the transaction when painting
+ * progressively.
+ */
+ bool mPaintFinished : 1;
+
+ /*
+ * Whether or not there is an async transform animation active
+ */
+ bool mHasTransformAnimation : 1;
+
+ /*
+ * Initializes/clears data to prepare for paint action.
+ */
+ void ResetPaintData();
+};
+
+class SharedFrameMetricsHelper
+{
+public:
+ SharedFrameMetricsHelper();
+ ~SharedFrameMetricsHelper();
+
+ /**
+ * This is called by the BasicTileLayer to determine if it is still interested
+ * in the update of this display-port to continue. We can return true here
+ * to abort the current update and continue with any subsequent ones. This
+ * is useful for slow-to-render pages when the display-port starts lagging
+ * behind enough that continuing to draw it is wasted effort.
+ */
+ bool UpdateFromCompositorFrameMetrics(const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform);
+
+ /**
+ * Determines if the compositor's upcoming composition bounds has fallen
+ * outside of the contents display port. If it has then the compositor
+ * will start to checker board. Checker boarding is when the compositor
+ * tries to composite a tile and it is not available. Historically
+ * a tile with a checker board pattern was used. Now a blank tile is used.
+ */
+ bool AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics);
+private:
+ bool mLastProgressiveUpdateWasLowPrecision;
+ bool mProgressiveUpdateWasInDanger;
+};
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientTiledLayerBuffer
+{
+public:
+ ClientTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient)
+ : mPaintedLayer(aPaintedLayer)
+ , mCompositableClient(aCompositableClient)
+ , mLastPaintContentType(gfxContentType::COLOR)
+ , mLastPaintSurfaceMode(SurfaceMode::SURFACE_OPAQUE)
+ , mWasLastPaintProgressive(false)
+ {}
+
+ virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) = 0;
+
+ virtual bool SupportsProgressiveUpdate() = 0;
+ virtual bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) = 0;
+ virtual void ResetPaintedAndValidState() = 0;
+
+ virtual const nsIntRegion& GetValidRegion() = 0;
+
+ virtual bool IsLowPrecision() const = 0;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) {}
+
+ const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
+ void SetFrameResolution(const CSSToParentLayerScale2D& aResolution) { mFrameResolution = aResolution; }
+
+ bool HasFormatChanged() const;
+
+protected:
+ void UnlockTile(TileClient& aTile);
+ gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
+
+ ClientTiledPaintedLayer& mPaintedLayer;
+ CompositableClient& mCompositableClient;
+
+ gfxContentType mLastPaintContentType;
+ SurfaceMode mLastPaintSurfaceMode;
+ CSSToParentLayerScale2D mFrameResolution;
+
+ bool mWasLastPaintProgressive;
+};
+
+class ClientMultiTiledLayerBuffer
+ : public TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>
+ , public ClientTiledLayerBuffer
+{
+ friend class TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>;
+public:
+ ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper);
+
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ virtual bool SupportsProgressiveUpdate() override { return true; }
+ /**
+ * Performs a progressive update of a given tiled buffer.
+ * See ComputeProgressiveUpdateRegion below for parameter documentation.
+ */
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTiles.mSize.width = 0;
+ mTiles.mSize.height = 0;
+ DiscardBuffers();
+ mRetainedTiles.Clear();
+ }
+
+
+ const nsIntRegion& GetValidRegion() override {
+ return TiledLayerBuffer::GetValidRegion();
+ }
+
+ bool IsLowPrecision() const override {
+ return TiledLayerBuffer::IsLowPrecision();
+ }
+
+ void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) override {
+ TiledLayerBuffer::Dump(aStream, aPrefix, aDumpHtml, aCompress);
+ }
+
+ void ReadLock();
+
+ void Release();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void SetResolution(float aResolution) {
+ if (mResolution == aResolution) {
+ return;
+ }
+
+ Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
+ mResolution = aResolution;
+ }
+
+protected:
+ bool ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileRect,
+ const nsIntRegion& dirtyRect);
+
+ void Update(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion);
+
+ TileClient GetPlaceholderTile() const { return TileClient(); }
+
+private:
+ RefPtr<ClientLayerManager> mManager;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+
+ // The region that will be made valid during Update(). Once Update() is
+ // completed then this is identical to mValidRegion.
+ nsIntRegion mNewValidRegion;
+
+ SharedFrameMetricsHelper* mSharedFrameMetricsHelper;
+ // When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
+ std::vector<gfx::Tile> mMoz2DTiles;
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ /**
+ * Calculates the region to update in a single progressive update transaction.
+ * This employs some heuristics to update the most 'sensible' region to
+ * update at this point in time, and how large an update should be performed
+ * at once to maintain visual coherency.
+ *
+ * aInvalidRegion is the current invalid region.
+ * aOldValidRegion is the valid region of mTiledBuffer at the beginning of the
+ * current transaction.
+ * aRegionToPaint will be filled with the region to update. This may be empty,
+ * which indicates that there is no more work to do.
+ * aIsRepeated should be true if this function has already been called during
+ * this transaction.
+ *
+ * Returns true if it should be called again, false otherwise. In the case
+ * that aRegionToPaint is empty, this will return aIsRepeated for convenience.
+ */
+ bool ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated);
+};
+
+class TiledContentClient : public CompositableClient
+{
+public:
+ TiledContentClient(ClientLayerManager* aManager,
+ const char* aName = "")
+ : CompositableClient(aManager->AsShadowForwarder())
+ , mName(aName)
+ {}
+
+protected:
+ ~TiledContentClient()
+ {}
+
+public:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_TILED);
+ }
+
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() = 0;
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() = 0;
+
+ enum TiledBufferType {
+ TILED_BUFFER,
+ LOW_PRECISION_TILED_BUFFER
+ };
+ virtual void UpdatedBuffer(TiledBufferType aType) = 0;
+
+private:
+ const char* mName;
+};
+
+/**
+ * An implementation of TiledContentClient that supports
+ * multiple tiles and a low precision buffer.
+ */
+class MultiTiledContentClient : public TiledContentClient
+{
+public:
+ MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~MultiTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(MultiTiledContentClient);
+
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+ }
+
+public:
+ void ClearCachedResources() override;
+ void UpdatedBuffer(TiledBufferType aType) override;
+
+ ClientTiledLayerBuffer* GetTiledBuffer() override { return &mTiledBuffer; }
+ ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override {
+ if (mHasLowPrecision) {
+ return &mLowPrecisionTiledBuffer;
+ }
+ return nullptr;
+ }
+
+private:
+ SharedFrameMetricsHelper mSharedFrameMetricsHelper;
+ ClientMultiTiledLayerBuffer mTiledBuffer;
+ ClientMultiTiledLayerBuffer mLowPrecisionTiledBuffer;
+ bool mHasLowPrecision;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/AsyncCompositionManager.cpp b/system/graphics/layers/composite/AsyncCompositionManager.cpp
new file mode 100644
index 000000000..e1938392f
--- /dev/null
+++ b/system/graphics/layers/composite/AsyncCompositionManager.cpp
@@ -0,0 +1,1391 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include <stdint.h> // for uint32_t
+#include "apz/src/AsyncPanZoomController.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "gfxPoint.h" // for gfxPoint, gfxSize
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
+#include "mozilla/dom/KeyframeEffectReadOnly.h"
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
+#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsDeviceContext.h" // for nsDeviceContext
+#include "nsDisplayList.h" // for nsDisplayTransform, etc
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "UnitTransforms.h" // for TransformTo
+#include "gfxPrefs.h"
+#include "GeckoProfiler.h"
+#include "FrameUniformityData.h"
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
+#include "VsyncSource.h"
+
+struct nsCSSValueSharedList;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static bool
+IsSameDimension(dom::ScreenOrientationInternal o1, dom::ScreenOrientationInternal o2)
+{
+ bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary || o1 == dom::eScreenOrientation_PortraitSecondary);
+ bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary || o2 == dom::eScreenOrientation_PortraitSecondary);
+ return !(isO1portrait ^ isO2portrait);
+}
+
+static bool
+ContentMightReflowOnOrientationChange(const IntRect& rect)
+{
+ return rect.width != rect.height;
+}
+
+AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager)
+ : mLayerManager(aManager)
+ , mIsFirstPaint(true)
+ , mLayersUpdated(false)
+ , mPaintSyncId(0)
+ , mReadyForCompose(true)
+{
+}
+
+AsyncCompositionManager::~AsyncCompositionManager()
+{
+}
+
+void
+AsyncCompositionManager::ResolveRefLayers(CompositorBridgeParent* aCompositor,
+ bool* aHasRemoteContent,
+ bool* aResolvePlugins)
+{
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = false;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If valid *aResolvePlugins indicates if we need to update plugin geometry
+ // when we walk the tree.
+ bool resolvePlugins = (aCompositor && aResolvePlugins && *aResolvePlugins);
+#endif
+
+ if (!mLayerManager->GetRoot()) {
+ // Updated the return value since this result controls completing composition.
+ if (aResolvePlugins) {
+ *aResolvePlugins = false;
+ }
+ return;
+ }
+
+ mReadyForCompose = true;
+ bool hasRemoteContent = false;
+ bool didResolvePlugins = false;
+
+ ForEachNode<ForwardIterator>(
+ mLayerManager->GetRoot(),
+ [&](Layer* layer)
+ {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ hasRemoteContent = true;
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (!referent) {
+ return;
+ }
+
+ if (!refLayer->GetLocalVisibleRegion().IsEmpty()) {
+ dom::ScreenOrientationInternal chromeOrientation =
+ mTargetConfig.orientation();
+ dom::ScreenOrientationInternal contentOrientation =
+ state->mTargetConfig.orientation();
+ if (!IsSameDimension(chromeOrientation, contentOrientation) &&
+ ContentMightReflowOnOrientationChange(mTargetConfig.naturalBounds())) {
+ mReadyForCompose = false;
+ }
+ }
+
+ refLayer->ConnectReferentLayer(referent);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (resolvePlugins) {
+ didResolvePlugins |=
+ aCompositor->UpdatePluginWindowState(refLayer->GetReferentId());
+ }
+#endif
+ });
+
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = hasRemoteContent;
+ }
+ if (aResolvePlugins) {
+ *aResolvePlugins = didResolvePlugins;
+ }
+}
+
+void
+AsyncCompositionManager::DetachRefLayers()
+{
+ if (!mLayerManager->GetRoot()) {
+ return;
+ }
+
+ mReadyForCompose = false;
+
+ ForEachNodePostOrder<ForwardIterator>(mLayerManager->GetRoot(),
+ [&](Layer* layer)
+ {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (referent) {
+ refLayer->DetachReferentLayer(referent);
+ }
+ });
+}
+
+void
+AsyncCompositionManager::ComputeRotation()
+{
+ if (!mTargetConfig.naturalBounds().IsEmpty()) {
+ mWorldTransform =
+ ComputeTransformForRotation(mTargetConfig.naturalBounds(),
+ mTargetConfig.rotation());
+ }
+}
+
+#ifdef DEBUG
+static void
+GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform)
+{
+ // Start with the animated transform if there is one
+ *aTransform =
+ (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation()
+ ? aLayer->GetLocalTransform()
+ : aLayer->GetTransform());
+}
+#endif
+
+static void
+TransformClipRect(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+ if (clipRect) {
+ ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
+ aLayer->AsLayerComposite()->SetShadowClipRect(Some(transformed));
+ }
+}
+
+// Similar to TransformFixedClip(), but only transforms the fixed part of the
+// clip.
+static void
+TransformFixedClip(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform,
+ AsyncCompositionManager::ClipParts& aClipParts)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ if (aClipParts.mFixedClip) {
+ *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
+ aLayer->AsLayerComposite()->SetShadowClipRect(aClipParts.Intersect());
+ }
+}
+
+/**
+ * Set the given transform as the shadow transform on the layer, assuming
+ * that the given transform already has the pre- and post-scales applied.
+ * That is, this function cancels out the pre- and post-scales from aTransform
+ * before setting it as the shadow transform on the layer, so that when
+ * the layer's effective transform is computed, the pre- and post-scales will
+ * only be applied once.
+ */
+static void
+SetShadowTransform(Layer* aLayer, LayerToParentLayerMatrix4x4 aTransform)
+{
+ if (ContainerLayer* c = aLayer->AsContainerLayer()) {
+ aTransform.PreScale(1.0f / c->GetPreXScale(),
+ 1.0f / c->GetPreYScale(),
+ 1);
+ }
+ aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
+ 1.0f / aLayer->GetPostYScale(),
+ 1);
+ aLayer->AsLayerComposite()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
+}
+
+static void
+TranslateShadowLayer(Layer* aLayer,
+ const ParentLayerPoint& aTranslation,
+ bool aAdjustClipRect,
+ AsyncCompositionManager::ClipPartsCache* aClipPartsCache)
+{
+ // This layer might also be a scrollable layer and have an async transform.
+ // To make sure we don't clobber that, we start with the shadow transform.
+ // (i.e. GetLocalTransform() instead of GetTransform()).
+ // Note that the shadow transform is reset on every frame of composition so
+ // we don't have to worry about the adjustments compounding over successive
+ // frames.
+ LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
+
+ // Apply the translation to the layer transform.
+ layerTransform.PostTranslate(aTranslation);
+
+ SetShadowTransform(aLayer, layerTransform);
+ aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
+
+ if (aAdjustClipRect) {
+ auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
+ // If we're passed a clip parts cache, only transform the fixed part of
+ // the clip.
+ if (aClipPartsCache) {
+ auto iter = aClipPartsCache->find(aLayer);
+ MOZ_ASSERT(iter != aClipPartsCache->end());
+ TransformFixedClip(aLayer, transform, iter->second);
+ } else {
+ TransformClipRect(aLayer, transform);
+ }
+
+ // If a fixed- or sticky-position layer has a mask layer, that mask should
+ // move along with the layer, so apply the translation to the mask layer too.
+ if (Layer* maskLayer = aLayer->GetMaskLayer()) {
+ TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
+ }
+ }
+}
+
+#ifdef DEBUG
+static void
+AccumulateLayerTransforms(Layer* aLayer,
+ Layer* aAncestor,
+ Matrix4x4& aMatrix)
+{
+ // Accumulate the transforms between this layer and the subtree root layer.
+ for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
+ Matrix4x4 transform;
+ GetBaseTransform(l, &transform);
+ aMatrix *= transform;
+ }
+}
+#endif
+
+static LayerPoint
+GetLayerFixedMarginsOffset(Layer* aLayer,
+ const ScreenMargin& aFixedLayerMargins)
+{
+ // Work out the necessary translation, in root scrollable layer space.
+ // Because fixed layer margins are stored relative to the root scrollable
+ // layer, we can just take the difference between these values.
+ LayerPoint translation;
+ int32_t sides = aLayer->GetFixedPositionSides();
+
+ if ((sides & eSideBitsLeftRight) == eSideBitsLeftRight) {
+ translation.x += (aFixedLayerMargins.left - aFixedLayerMargins.right) / 2;
+ } else if (sides & eSideBitsRight) {
+ translation.x -= aFixedLayerMargins.right;
+ } else if (sides & eSideBitsLeft) {
+ translation.x += aFixedLayerMargins.left;
+ }
+
+ if ((sides & eSideBitsTopBottom) == eSideBitsTopBottom) {
+ translation.y += (aFixedLayerMargins.top - aFixedLayerMargins.bottom) / 2;
+ } else if (sides & eSideBitsBottom) {
+ translation.y -= aFixedLayerMargins.bottom;
+ } else if (sides & eSideBitsTop) {
+ translation.y += aFixedLayerMargins.top;
+ }
+
+ return translation;
+}
+
+static gfxFloat
+IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
+{
+ // Determine the amount of overlap between the 1D vector |aTranslation|
+ // and the interval [aMin, aMax].
+ if (aTranslation > 0) {
+ return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
+ } else {
+ return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
+ }
+}
+
+/**
+ * Finds the metrics on |aLayer| with scroll id |aScrollId|, and returns a
+ * LayerMetricsWrapper representing the (layer, metrics) pair, or the null
+ * LayerMetricsWrapper if no matching metrics could be found.
+ */
+static LayerMetricsWrapper
+FindMetricsWithScrollId(Layer* aLayer, FrameMetrics::ViewID aScrollId)
+{
+ for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) {
+ if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
+ return LayerMetricsWrapper(aLayer, i);
+ }
+ }
+ return LayerMetricsWrapper();
+}
+
+/**
+ * Checks whether the (layer, metrics) pair (aTransformedLayer, aTransformedMetrics)
+ * is on the path from |aFixedLayer| to the metrics with scroll id
+ * |aFixedWithRespectTo|, inclusive.
+ */
+static bool
+AsyncTransformShouldBeUnapplied(Layer* aFixedLayer,
+ FrameMetrics::ViewID aFixedWithRespectTo,
+ Layer* aTransformedLayer,
+ FrameMetrics::ViewID aTransformedMetrics)
+{
+ LayerMetricsWrapper transformed = FindMetricsWithScrollId(aTransformedLayer, aTransformedMetrics);
+ if (!transformed.IsValid()) {
+ return false;
+ }
+ // It's important to start at the bottom, because the fixed layer itself
+ // could have the transformed metrics, and they can be at the bottom.
+ LayerMetricsWrapper current(aFixedLayer, LayerMetricsWrapper::StartAt::BOTTOM);
+ bool encounteredTransformedLayer = false;
+ // The transformed layer is on the path from |aFixedLayer| to the fixed-to
+ // layer if as we walk up the (layer, metrics) tree starting from
+ // |aFixedLayer|, we *first* encounter the transformed layer, and *then* (or
+ // at the same time) the fixed-to layer.
+ while (current) {
+ if (!encounteredTransformedLayer && current == transformed) {
+ encounteredTransformedLayer = true;
+ }
+ if (current.Metrics().GetScrollId() == aFixedWithRespectTo) {
+ return encounteredTransformedLayer;
+ }
+ current = current.GetParent();
+ // It's possible that we reach a layers id boundary before we reach an
+ // ancestor with the scroll id |aFixedWithRespectTo| (this could happen
+ // e.g. if the scroll frame with that scroll id uses containerless
+ // scrolling). In such a case, stop the walk, as a new layers id could
+ // have a different layer with scroll id |aFixedWithRespectTo| which we
+ // don't intend to match.
+ if (current && current.AsRefLayer() != nullptr) {
+ break;
+ }
+ }
+ return false;
+}
+
+// If |aLayer| is fixed or sticky, returns the scroll id of the scroll frame
+// that it's fixed or sticky to. Otherwise, returns Nothing().
+static Maybe<FrameMetrics::ViewID>
+IsFixedOrSticky(Layer* aLayer)
+{
+ bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
+ !aLayer->GetParent()->GetIsFixedPosition();
+ if (isRootOfFixedSubtree) {
+ return Some(aLayer->GetFixedPositionScrollContainerId());
+ }
+ if (aLayer->GetIsStickyPosition()) {
+ return Some(aLayer->GetStickyScrollContainerId());
+ }
+ return Nothing();
+}
+
+void
+AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+ Layer* aStartTraversalAt,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache)
+{
+ // We're going to be inverting |aCurrentTransformForRoot|.
+ // If it's singular, there's nothing we can do.
+ if (aCurrentTransformForRoot.IsSingular()) {
+ return;
+ }
+
+ Layer* layer = aStartTraversalAt;
+ bool needsAsyncTransformUnapplied = false;
+ if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
+ needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(layer,
+ *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
+ }
+
+ // We want to process all the fixed and sticky descendants of
+ // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
+ // need to recurse any deeper because the adjustment to the fixed or sticky
+ // layer will apply to its subtree.
+ if (!needsAsyncTransformUnapplied) {
+ for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
+ aTransformScrollId, aPreviousTransformForRoot,
+ aCurrentTransformForRoot, aFixedLayerMargins, aClipPartsCache);
+ }
+ return;
+ }
+
+ // Insert a translation so that the position of the anchor point is the same
+ // before and after the change to the transform of aTransformedSubtreeRoot.
+
+ // A transform creates a containing block for fixed-position descendants,
+ // so there shouldn't be a transform in between the fixed layer and
+ // the subtree root layer.
+#ifdef DEBUG
+ Matrix4x4 ancestorTransform;
+ if (layer != aTransformedSubtreeRoot) {
+ AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
+ ancestorTransform);
+ }
+ ancestorTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(ancestorTransform.IsIdentity());
+#endif
+
+ // Since we create container layers for fixed layers, there shouldn't
+ // a local CSS or OMTA transform on the fixed layer, either (any local
+ // transform would go onto a descendant layer inside the container
+ // layer).
+#ifdef DEBUG
+ Matrix4x4 localTransform;
+ GetBaseTransform(layer, &localTransform);
+ localTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(localTransform.IsIdentity());
+#endif
+
+ // Now work out the translation necessary to make sure the layer doesn't
+ // move given the new sub-tree root transform.
+
+ // Get the layer's fixed anchor point, in the layer's local coordinate space
+ // (before any transform is applied).
+ LayerPoint anchor = layer->GetFixedPositionAnchor();
+
+ // Offset the layer's anchor point to make sure fixed position content
+ // respects content document fixed position margins.
+ LayerPoint offsetAnchor = anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
+
+ // Additionally transform the anchor to compensate for the change
+ // from the old transform to the new transform. We do
+ // this by using the old transform to take the offset anchor back into
+ // subtree root space, and then the inverse of the new transform
+ // to bring it back to layer space.
+ ParentLayerPoint offsetAnchorInSubtreeRootSpace =
+ aPreviousTransformForRoot.TransformPoint(offsetAnchor);
+ LayerPoint transformedAnchor = aCurrentTransformForRoot.Inverse()
+ .TransformPoint(offsetAnchorInSubtreeRootSpace);
+
+ // We want to translate the layer by the difference between
+ // |transformedAnchor| and |anchor|.
+ LayerPoint translation = transformedAnchor - anchor;
+
+ // A fixed layer will "consume" (be unadjusted by) the entire translation
+ // calculated above. A sticky layer may consume all, part, or none of it,
+ // depending on where we are relative to its sticky scroll range.
+ // The remainder of the translation (the unconsumed portion) needs to
+ // be propagated to descendant fixed/sticky layers.
+ LayerPoint unconsumedTranslation;
+
+ if (layer->GetIsStickyPosition()) {
+ // For sticky positioned layers, the difference between the two rectangles
+ // defines a pair of translation intervals in each dimension through which
+ // the layer should not move relative to the scroll container. To
+ // accomplish this, we limit each dimension of the |translation| to that
+ // part of it which overlaps those intervals.
+ const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
+ const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
+
+ LayerPoint originalTranslation = translation;
+ translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
+ IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
+ translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
+ IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
+ unconsumedTranslation = translation - originalTranslation;
+ }
+
+ // Finally, apply the translation to the layer transform. Note that in cases
+ // where the async transform on |aTransformedSubtreeRoot| affects this layer's
+ // clip rect, we need to apply the same translation to said clip rect, so
+ // that the effective transform on the clip rect takes it back to where it was
+ // originally, had there been no async scroll.
+ TranslateShadowLayer(layer, ViewAs<ParentLayerPixel>(translation,
+ PixelCastJustification::NoTransformOnLayer), true, aClipPartsCache);
+
+ // Propragate the unconsumed portion of the translation to descendant
+ // fixed/sticky layers.
+ if (unconsumedTranslation != LayerPoint()) {
+ // Take the computations we performed to derive |translation| from
+ // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
+ // quantities fixed, to come up with a new transform |newTransform| that
+ // would produce |unconsumedTranslation|.
+ LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
+ ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
+ aPreviousTransformForRoot.TransformPoint(newTransformedAnchor);
+ LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
+ newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
+ offsetAnchorInSubtreeRootSpace);
+
+ // Propagate this new transform to our descendants as the new value of
+ // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
+ // translation.
+ for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child, aTransformScrollId,
+ aPreviousTransformForRoot, newTransform, aFixedLayerMargins, aClipPartsCache);
+ }
+ }
+
+ return;
+}
+
+static void
+SampleValue(float aPortion, Animation& aAnimation,
+ const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
+ const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
+ Animatable* aValue, Layer* aLayer)
+{
+ NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
+ aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
+ aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
+ "Must have same unit");
+
+ StyleAnimationValue startValue = aStart;
+ StyleAnimationValue endValue = aEnd;
+ // Iteration composition for accumulate
+ if (static_cast<dom::IterationCompositeOperation>
+ (aAnimation.iterationComposite()) ==
+ dom::IterationCompositeOperation::Accumulate &&
+ aCurrentIteration > 0) {
+ // FIXME: Bug 1293492: Add a utility function to calculate both of
+ // below StyleAnimationValues.
+ DebugOnly<bool> accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ startValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ endValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ }
+
+ StyleAnimationValue interpolatedValue;
+ // This should never fail because we only pass transform and opacity values
+ // to the compositor and they should never fail to interpolate.
+ DebugOnly<bool> uncomputeResult =
+ StyleAnimationValue::Interpolate(aAnimation.property(),
+ startValue, endValue,
+ aPortion, interpolatedValue);
+ MOZ_ASSERT(uncomputeResult, "could not uncompute value");
+
+ if (aAnimation.property() == eCSSProperty_opacity) {
+ *aValue = interpolatedValue.GetFloatValue();
+ return;
+ }
+
+ nsCSSValueSharedList* interpolatedList =
+ interpolatedValue.GetCSSValueSharedListValue();
+
+ TransformData& data = aAnimation.data().get_TransformData();
+ nsPoint origin = data.origin();
+ // we expect all our transform data to arrive in device pixels
+ Point3D transformOrigin = data.transformOrigin();
+ nsDisplayTransform::FrameTransformProperties props(interpolatedList,
+ transformOrigin);
+
+ // If our parent layer is a perspective layer, then the offset into reference
+ // frame coordinates is already on that layer. If not, then we need to ask
+ // for it to be added here.
+ uint32_t flags = 0;
+ if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
+ flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
+ }
+
+ Matrix4x4 transform =
+ nsDisplayTransform::GetResultingTransformMatrix(props, origin,
+ data.appUnitsPerDevPixel(),
+ flags, &data.bounds());
+
+ InfallibleTArray<TransformFunction> functions;
+ functions.AppendElement(TransformMatrix(transform));
+ *aValue = functions;
+}
+
+static bool
+SampleAnimations(Layer* aLayer, TimeStamp aPoint)
+{
+ bool activeAnimations = false;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&activeAnimations, &aPoint] (Layer* layer)
+ {
+ AnimationArray& animations = layer->GetAnimations();
+ InfallibleTArray<AnimData>& animationData = layer->GetAnimationData();
+
+ // Process in order, since later animations override earlier ones.
+ for (size_t i = 0, iEnd = animations.Length(); i < iEnd; ++i) {
+ Animation& animation = animations[i];
+ AnimData& animData = animationData[i];
+
+ activeAnimations = true;
+
+ MOZ_ASSERT(!animation.startTime().IsNull(),
+ "Failed to resolve start time of pending animations");
+ TimeDuration elapsedDuration =
+ (aPoint - animation.startTime()).MultDouble(animation.playbackRate());
+ TimingParams timing;
+ timing.mDuration.emplace(animation.duration());
+ timing.mDelay = animation.delay();
+ timing.mIterations = animation.iterations();
+ timing.mIterationStart = animation.iterationStart();
+ timing.mDirection =
+ static_cast<dom::PlaybackDirection>(animation.direction());
+ timing.mFill = static_cast<dom::FillMode>(animation.fillMode());
+ timing.mFunction =
+ AnimationUtils::TimingFunctionToComputedTimingFunction(
+ animation.easingFunction());
+
+ ComputedTiming computedTiming =
+ dom::AnimationEffectReadOnly::GetComputedTimingAt(
+ Nullable<TimeDuration>(elapsedDuration), timing,
+ animation.playbackRate());
+
+ if (computedTiming.mProgress.IsNull()) {
+ continue;
+ }
+
+ uint32_t segmentIndex = 0;
+ size_t segmentSize = animation.segments().Length();
+ AnimationSegment* segment = animation.segments().Elements();
+ while (segment->endPortion() < computedTiming.mProgress.Value() &&
+ segmentIndex < segmentSize - 1) {
+ ++segment;
+ ++segmentIndex;
+ }
+
+ double positionInSegment =
+ (computedTiming.mProgress.Value() - segment->startPortion()) /
+ (segment->endPortion() - segment->startPortion());
+
+ double portion =
+ ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
+ positionInSegment,
+ computedTiming.mBeforeFlag);
+
+ // interpolate the property
+ Animatable interpolatedValue;
+ SampleValue(portion, animation,
+ animData.mStartValues[segmentIndex],
+ animData.mEndValues[segmentIndex],
+ animData.mEndValues.LastElement(),
+ computedTiming.mCurrentIteration,
+ &interpolatedValue, layer);
+ LayerComposite* layerComposite = layer->AsLayerComposite();
+ switch (animation.property()) {
+ case eCSSProperty_opacity:
+ {
+ layerComposite->SetShadowOpacity(interpolatedValue.get_float());
+ layerComposite->SetShadowOpacitySetByAnimation(true);
+ break;
+ }
+ case eCSSProperty_transform:
+ {
+ Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ matrix.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
+ }
+ layerComposite->SetShadowBaseTransform(matrix);
+ layerComposite->SetShadowTransformSetByAnimation(true);
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled animated property");
+ }
+ }
+ });
+ return activeAnimations;
+}
+
+static bool
+SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime)
+{
+ bool activeAnimations = false;
+
+ ForEachNodePostOrder<ForwardIterator>(aLayer,
+ [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics)
+ {
+ if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
+ apzc->ReportCheckerboard(aSampleTime);
+ activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
+ }
+ }
+ );
+
+ return activeAnimations;
+}
+
+void
+AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer)
+{
+ MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ ForEachNodePostOrder<ForwardIterator>(
+ aLayer,
+ [this] (Layer* layer)
+ {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
+ if (!apzc) {
+ continue;
+ }
+ gfx::Matrix4x4 shadowTransform = layer->AsLayerComposite()->GetShadowBaseTransform();
+ if (!shadowTransform.Is2D()) {
+ continue;
+ }
+
+ Matrix transform = shadowTransform.As2D();
+ if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
+ Point translation = transform.GetTranslation();
+ mLayerTransformRecorder.RecordTransform(layer, translation);
+ return;
+ }
+ }
+ });
+}
+
+static AsyncTransformComponentMatrix
+AdjustForClip(const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer)
+{
+ AsyncTransformComponentMatrix result = asyncTransform;
+
+ // Container layers start at the origin, but they are clipped to where they
+ // actually have content on the screen. The tree transform is meant to apply
+ // to the clipped area. If the tree transform includes a scale component,
+ // then applying it to container as-is will produce incorrect results. To
+ // avoid this, translate the layer so that the clip rect starts at the origin,
+ // apply the tree transform, and translate back.
+ if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
+ if (shadowClipRect->TopLeft() != ParentLayerIntPoint()) { // avoid a gratuitous change of basis
+ result.ChangeBasis(shadowClipRect->x, shadowClipRect->y, 0);
+ }
+ }
+ return result;
+}
+
+static void
+ExpandRootClipRect(Layer* aLayer, const ScreenMargin& aFixedLayerMargins)
+{
+ // For Fennec we want to expand the root scrollable layer clip rect based on
+ // the fixed position margins. In particular, we want this while the dynamic
+ // toolbar is in the process of sliding offscreen and the area of the
+ // LayerView visible to the user is larger than the viewport size that Gecko
+ // knows about (and therefore larger than the clip rect). We could also just
+ // clear the clip rect on aLayer entirely but this seems more precise.
+ Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+ if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
+ MOZ_ASSERT(false, "aFixedLayerMargins should be empty!");
+ ParentLayerRect rect(rootClipRect.value());
+ rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
+ PixelCastJustification::ScreenIsParentLayerForRoot));
+ aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
+ }
+}
+
+bool
+AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
+ bool* aOutFoundRoot)
+{
+ bool appliedTransform = false;
+ std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
+
+ // Maps layers to their ClipParts. The parts are not stored individually
+ // on the layer, but during AlignFixedAndStickyLayers we need access to
+ // the individual parts for descendant layers.
+ ClipPartsCache clipPartsCache;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&stackDeferredClips] (Layer* layer)
+ {
+ stackDeferredClips.push(Maybe<ParentLayerIntRect>());
+ },
+ [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform, &clipPartsCache] (Layer* layer)
+ {
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren = stackDeferredClips.top();
+ stackDeferredClips.pop();
+ LayerToParentLayerMatrix4x4 oldTransform = layer->GetTransformTyped() *
+ AsyncTransformMatrix();
+
+ AsyncTransformComponentMatrix combinedAsyncTransform;
+ bool hasAsyncTransform = false;
+ ScreenMargin fixedLayerMargins;
+
+ // Each layer has multiple clips:
+ // - Its local clip, which is fixed to the layer contents, i.e. it moves
+ // with those async transforms which the layer contents move with.
+ // - Its scrolled clip, which moves with all async transforms.
+ // - For each ScrollMetadata on the layer, a scroll clip. This includes
+ // the composition bounds and any other clips induced by layout. This
+ // moves with async transforms from ScrollMetadatas above it.
+ // In this function, these clips are combined into two shadow clip parts:
+ // - The fixed clip, which consists of the local clip only, initially
+ // transformed by all async transforms.
+ // - The scrolled clip, which consists of the other clips, transformed by
+ // the appropriate transforms.
+ // These two parts are kept separate for now, because for fixed layers, we
+ // need to adjust the fixed clip (to cancel out some async transforms).
+ // The parts are kept in a cache which is cleared at the beginning of every
+ // composite.
+ // The final shadow clip for the layer is the intersection of the (possibly
+ // adjusted) fixed clip and the scrolled clip.
+ ClipParts& clipParts = clipPartsCache[layer];
+ clipParts.mFixedClip = layer->GetClipRect();
+ clipParts.mScrolledClip = layer->GetScrolledClipRect();
+
+ // If we are a perspective transform ContainerLayer, apply the clip deferred
+ // from our child (if there is any) before we iterate over our frame metrics,
+ // because this clip is subject to all async transforms of this layer.
+ // Since this clip came from the a scroll clip on the child, it becomes part
+ // of our scrolled clip.
+ clipParts.mScrolledClip = IntersectMaybeRects(
+ clipDeferredFromChildren, clipParts.mScrolledClip);
+
+ // The transform of a mask layer is relative to the masked layer's parent
+ // layer. So whenever we apply an async transform to a layer, we need to
+ // apply that same transform to the layer's own mask layer.
+ // A layer can also have "ancestor" mask layers for any rounded clips from
+ // its ancestor scroll frames. A scroll frame mask layer only needs to be
+ // async transformed for async scrolls of this scroll frame's ancestor
+ // scroll frames, not for async scrolls of this scroll frame itself.
+ // In the loop below, we iterate over scroll frames from inside to outside.
+ // At each iteration, this array contains the layer's ancestor mask layers
+ // of all scroll frames inside the current one.
+ nsTArray<Layer*> ancestorMaskLayers;
+
+ // The layer's scrolled clip can have an ancestor mask layer as well,
+ // which is moved by all async scrolls on this layer.
+ if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
+ if (scrolledClip->GetMaskLayerIndex()) {
+ ancestorMaskLayers.AppendElement(
+ layer->GetAncestorMaskLayerAt(*scrolledClip->GetMaskLayerIndex()));
+ }
+ }
+
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* controller = layer->GetAsyncPanZoomController(i);
+ if (!controller) {
+ continue;
+ }
+
+ hasAsyncTransform = true;
+
+ AsyncTransform asyncTransformWithoutOverscroll =
+ controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix overscrollTransform =
+ controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix asyncTransform =
+ AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
+ * overscrollTransform;
+
+ if (!layer->IsScrollInfoLayer()) {
+ controller->MarkAsyncTransformAppliedToContent();
+ }
+
+ const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
+ const FrameMetrics& metrics = scrollMetadata.GetMetrics();
+
+ // We still care about this flag being cleared after
+ // the first call to TransformShadowTree().
+ mIsFirstPaint = false;
+
+ // Transform the current local clips by this APZC's async transform. If we're
+ // using containerful scrolling, then the clip is not part of the scrolled
+ // frame and should not be transformed.
+ if (!scrollMetadata.UsesContainerScrolling()) {
+ MOZ_ASSERT(asyncTransform.Is2D());
+ if (clipParts.mFixedClip) {
+ *clipParts.mFixedClip = TransformBy(asyncTransform, *clipParts.mFixedClip);
+ }
+ if (clipParts.mScrolledClip) {
+ *clipParts.mScrolledClip = TransformBy(asyncTransform, *clipParts.mScrolledClip);
+ }
+ }
+ // Note: we don't set the layer's shadow clip rect property yet;
+ // AlignFixedAndStickyLayers will use the clip parts from the clip parts
+ // cache.
+
+ combinedAsyncTransform *= asyncTransform;
+
+ // For the purpose of aligning fixed and sticky layers, we disregard
+ // the overscroll transform as well as any OMTA transform when computing the
+ // 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
+ // and OMTA transforms are not unapplied, and therefore that the visual
+ // effects apply to fixed and sticky layers. We do this by using
+ // GetTransform() as the base transform rather than GetLocalTransform(),
+ // which would include those factors.
+ LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
+ layer->GetTransformTyped()
+ * CompleteAsyncTransform(
+ AdjustForClip(asyncTransformWithoutOverscroll, layer));
+
+ AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(), oldTransform,
+ transformWithoutOverscrollOrOmta, fixedLayerMargins,
+ &clipPartsCache);
+
+ // Combine the local clip with the ancestor scrollframe clip. This is not
+ // included in the async transform above, since the ancestor clip should not
+ // move with this APZC.
+ if (scrollMetadata.HasScrollClip()) {
+ ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
+ if (layer->GetParent() && layer->GetParent()->GetTransformIsPerspective()) {
+ // If our parent layer has a perspective transform, we want to apply
+ // our scroll clip to it instead of to this layer (see bug 1168263).
+ // A layer with a perspective transform shouldn't have multiple
+ // children with FrameMetrics, nor a child with multiple FrameMetrics.
+ // (A child with multiple FrameMetrics would mean that there's *another*
+ // scrollable element between the one with the CSS perspective and the
+ // transformed element. But you'd have to use preserve-3d on the inner
+ // scrollable element in order to have the perspective apply to the
+ // transformed child, and preserve-3d is not supported on scrollable
+ // elements, so this case can't occur.)
+ MOZ_ASSERT(!stackDeferredClips.top());
+ stackDeferredClips.top().emplace(clip);
+ } else {
+ clipParts.mScrolledClip = IntersectMaybeRects(Some(clip),
+ clipParts.mScrolledClip);
+ }
+ }
+
+ // Do the same for the ancestor mask layers: ancestorMaskLayers contains
+ // the ancestor mask layers for scroll frames *inside* the current scroll
+ // frame, so these are the ones we need to shift by our async transform.
+ for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
+ SetShadowTransform(ancestorMaskLayer,
+ ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
+ }
+
+ // Append the ancestor mask layer for this scroll frame to ancestorMaskLayers.
+ if (scrollMetadata.HasScrollClip()) {
+ const LayerClip& scrollClip = scrollMetadata.ScrollClip();
+ if (scrollClip.GetMaskLayerIndex()) {
+ size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
+ Layer* ancestorMaskLayer = layer->GetAncestorMaskLayerAt(maskLayerIndex);
+ ancestorMaskLayers.AppendElement(ancestorMaskLayer);
+ }
+ }
+ }
+
+ bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
+ layer->GetScrolledClipRect());
+ if (clipChanged) {
+ // Intersect the two clip parts and apply them to the layer.
+ // During ApplyAsyncContentTransformTree on an ancestor layer,
+ // AlignFixedAndStickyLayers may overwrite this with a new clip it
+ // computes from the clip parts, but if that doesn't happen, this
+ // is the layer's final clip rect.
+ layer->AsLayerComposite()->SetShadowClipRect(clipParts.Intersect());
+ }
+
+ if (hasAsyncTransform) {
+ // Apply the APZ transform on top of GetLocalTransform() here (rather than
+ // GetTransform()) in case the OMTA code in SampleAnimations already set a
+ // shadow transform; in that case we want to apply ours on top of that one
+ // rather than clobber it.
+ SetShadowTransform(layer,
+ layer->GetLocalTransformTyped()
+ * AdjustForClip(combinedAsyncTransform, layer));
+
+ // Do the same for the layer's own mask layer, if it has one.
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowTransform(maskLayer,
+ maskLayer->GetLocalTransformTyped() * combinedAsyncTransform);
+ }
+
+ appliedTransform = true;
+ }
+
+ ExpandRootClipRect(layer, fixedLayerMargins);
+
+ if (layer->GetScrollbarDirection() != Layer::NONE) {
+ ApplyAsyncTransformToScrollbar(layer);
+ }
+ });
+
+ return appliedTransform;
+}
+
+static bool
+LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
+{
+ AsyncPanZoomController* apzc = aTarget.GetApzc();
+ if (!apzc) {
+ return false;
+ }
+ const FrameMetrics& metrics = aTarget.Metrics();
+ if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
+ return false;
+ }
+ return !aTarget.IsScrollInfoLayer();
+}
+
+static void
+ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
+ const LayerMetricsWrapper& aContent,
+ bool aScrollbarIsDescendant)
+{
+ // We only apply the transform if the scroll-target layer has non-container
+ // children (i.e. when it has some possibly-visible content). This is to
+ // avoid moving scroll-bars in the situation that only a scroll information
+ // layer has been built for a scroll frame, as this would result in a
+ // disparity between scrollbars and visible content.
+ if (aContent.IsScrollInfoLayer()) {
+ return;
+ }
+
+ const FrameMetrics& metrics = aContent.Metrics();
+ AsyncPanZoomController* apzc = aContent.GetApzc();
+ MOZ_RELEASE_ASSERT(apzc);
+
+ AsyncTransformComponentMatrix asyncTransform =
+ apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // |asyncTransform| represents the amount by which we have scrolled and
+ // zoomed since the last paint. Because the scrollbar was sized and positioned based
+ // on the painted content, we need to adjust it based on asyncTransform so that
+ // it reflects what the user is actually seeing now.
+ AsyncTransformComponentMatrix scrollbarTransform;
+ if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
+ const ParentLayerCoord asyncScrollY = asyncTransform._42;
+ const float asyncZoomY = asyncTransform._22;
+
+ // The scroll thumb needs to be scaled in the direction of scrolling by the
+ // inverse of the async zoom. This is because zooming in decreases the
+ // fraction of the whole srollable rect that is in view.
+ const float yScale = 1.f / asyncZoomY;
+
+ // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().yScale * asyncZoomY);
+
+ // Here we convert the scrollbar thumb ratio into a true unitless ratio by
+ // dividing out the conversion factor from the scrollframe's parent's space
+ // to the scrollframe's space.
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomY);
+ // The scroll thumb needs to be translated in opposite direction of the
+ // async scroll. This is because scrolling down, which translates the layer
+ // content up, should result in moving the scroll thumb down.
+ ParentLayerCoord yTranslation = -asyncScrollY * ratio;
+
+ // The scroll thumb additionally needs to be translated to compensate for
+ // the scale applied above. The origin with respect to which the scale is
+ // applied is the origin of the entire scrollbar, rather than the origin of
+ // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
+ // the composition bounds). This means that empty space above the thumb
+ // is scaled too, effectively translating the thumb. We undo that
+ // translation here.
+ // (One can think of the adjustment being done to the translation here as
+ // a change of basis. We have a method to help with that,
+ // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
+ // cleaner in this case).
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().y * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ yTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ // Scrollbar for the root are painted at the same resolution as the
+ // content. Since the coordinate space we apply this transform in includes
+ // the resolution, we need to adjust for it as well here. Note that in
+ // another metrics.IsRootContent() hunk below we apply a
+ // resolution-cancelling transform which ensures the scroll thumb isn't
+ // actually rendered at a larger scale.
+ yTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(1.f, yScale, 1.f);
+ scrollbarTransform.PostTranslate(0, yTranslation, 0);
+ }
+ if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
+ // See detailed comments under the VERTICAL case.
+
+ const ParentLayerCoord asyncScrollX = asyncTransform._41;
+ const float asyncZoomX = asyncTransform._11;
+
+ const float xScale = 1.f / asyncZoomX;
+
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().xScale * asyncZoomX);
+
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomX);
+ ParentLayerCoord xTranslation = -asyncScrollX * ratio;
+
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().x * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ xTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ xTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(xScale, 1.f, 1.f);
+ scrollbarTransform.PostTranslate(xTranslation, 0, 0);
+ }
+
+ LayerToParentLayerMatrix4x4 transform =
+ aScrollbar->GetLocalTransformTyped() * scrollbarTransform;
+
+ AsyncTransformComponentMatrix compensation;
+ // If the scrollbar layer is for the root then the content's resolution
+ // applies to the scrollbar as well. Since we don't actually want the scroll
+ // thumb's size to vary with the zoom (other than its length reflecting the
+ // fraction of the scrollable length that's in view, which is taken care of
+ // above), we apply a transform to cancel out this resolution.
+ if (metrics.IsRootContent()) {
+ compensation =
+ AsyncTransformComponentMatrix::Scaling(
+ metrics.GetPresShellResolution(),
+ metrics.GetPresShellResolution(),
+ 1.0f).Inverse();
+ }
+ // If the scrollbar layer is a child of the content it is a scrollbar for,
+ // then we need to adjust for any async transform (including an overscroll
+ // transform) on the content. This needs to be cancelled out because layout
+ // positions and sizes the scrollbar on the assumption that there is no async
+ // transform, and without this adjustment the scrollbar will end up in the
+ // wrong place.
+ //
+ // Note that since the async transform is applied on top of the content's
+ // regular transform, we need to make sure to unapply the async transform in
+ // the same coordinate space. This requires applying the content transform
+ // and then unapplying it after unapplying the async transform.
+ if (aScrollbarIsDescendant) {
+ AsyncTransformComponentMatrix overscroll =
+ apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
+ Matrix4x4 contentTransform = aContent.GetTransform();
+ Matrix4x4 contentUntransform = contentTransform.Inverse();
+
+ AsyncTransformComponentMatrix asyncCompensation =
+ ViewAs<AsyncTransformComponentMatrix>(
+ contentTransform
+ * asyncUntransform
+ * contentUntransform);
+
+ compensation = compensation * asyncCompensation;
+
+ // We also need to make a corresponding change on the clip rect of all the
+ // layers on the ancestor chain from the scrollbar layer up to but not
+ // including the layer with the async transform. Otherwise the scrollbar
+ // shifts but gets clipped and so appears to flicker.
+ for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
+ TransformClipRect(ancestor, asyncCompensation);
+ }
+ }
+ transform = transform * compensation;
+
+ SetShadowTransform(aScrollbar, transform);
+}
+
+static LayerMetricsWrapper
+FindScrolledLayerForScrollbar(Layer* aScrollbar, bool* aOutIsAncestor)
+{
+ // First check if the scrolled layer is an ancestor of the scrollbar layer.
+ LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
+ LayerMetricsWrapper prevAncestor(aScrollbar);
+ LayerMetricsWrapper scrolledLayer;
+
+ for (LayerMetricsWrapper ancestor(aScrollbar); ancestor; ancestor = ancestor.GetParent()) {
+ // Don't walk into remote layer trees; the scrollbar will always be in
+ // the same layer space.
+ if (ancestor.AsRefLayer()) {
+ root = prevAncestor;
+ break;
+ }
+ prevAncestor = ancestor;
+
+ if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
+ *aOutIsAncestor = true;
+ return ancestor;
+ }
+ }
+
+ // Search the entire layer space of the scrollbar.
+ ForEachNode<ForwardIterator>(
+ root,
+ [&root, &scrolledLayer, &aScrollbar](LayerMetricsWrapper aLayerMetrics)
+ {
+ // Do not recurse into RefLayers, since our initial aSubtreeRoot is the
+ // root (or RefLayer root) of a single layer space to search.
+ if (root != aLayerMetrics && aLayerMetrics.AsRefLayer()) {
+ return TraversalFlag::Skip;
+ }
+ if (LayerIsScrollbarTarget(aLayerMetrics, aScrollbar)) {
+ scrolledLayer = aLayerMetrics;
+ return TraversalFlag::Abort;
+ }
+ return TraversalFlag::Continue;
+ }
+ );
+ return scrolledLayer;
+}
+
+void
+AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer)
+{
+ // If this layer corresponds to a scrollbar, then there should be a layer that
+ // is a previous sibling or a parent that has a matching ViewID on its FrameMetrics.
+ // That is the content that this scrollbar is for. We pick up the transient
+ // async transform from that layer and use it to update the scrollbar position.
+ // Note that it is possible that the content layer is no longer there; in
+ // this case we don't need to do anything because there can't be an async
+ // transform on the content.
+ bool isAncestor = false;
+ const LayerMetricsWrapper& scrollTarget = FindScrolledLayerForScrollbar(aLayer, &isAncestor);
+ if (scrollTarget) {
+ ApplyAsyncTransformToScrollbarForContent(aLayer, scrollTarget, isAncestor);
+ }
+}
+
+void
+AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLayerTransformRecorder.EndTest(aOutData);
+}
+
+bool
+AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip)
+{
+ PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ Layer* root = mLayerManager->GetRoot();
+ if (!root) {
+ return false;
+ }
+
+ // First, compute and set the shadow transforms from OMT animations.
+ // NB: we must sample animations *before* sampling pan/zoom
+ // transforms.
+ // Use a previous vsync time to make main thread animations and compositor
+ // more in sync with each other.
+ // On the initial frame we use aVsyncTimestamp here so the timestamp on the
+ // second frame are the same as the initial frame, but it does not matter.
+ bool wantNextFrame = SampleAnimations(root,
+ !mPreviousFrameTimeStamp.IsNull() ?
+ mPreviousFrameTimeStamp : aCurrentFrame);
+
+ // Reset the previous time stamp if we don't already have any running
+ // animations to avoid using the time which is far behind for newly
+ // started animations.
+ mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame : TimeStamp();
+
+ if (!(aSkip & TransformsToSkip::APZ)) {
+ // FIXME/bug 775437: unify this interface with the ~native-fennec
+ // derived code
+ //
+ // Attempt to apply an async content transform to any layer that has
+ // an async pan zoom controller (which means that it is rendered
+ // async using Gecko). If this fails, fall back to transforming the
+ // primary scrollable layer. "Failing" here means that we don't
+ // find a frame that is async scrollable. Note that the fallback
+ // code also includes Fennec which is rendered async. Fennec uses
+ // its own platform-specific async rendering that is done partially
+ // in Gecko and partially in Java.
+ bool foundRoot = false;
+ if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+ // Fixed margin considerations should go here, but we never encounter
+ // those outside of mobile.
+ }
+
+ // Advance APZ animations to the next expected vsync timestamp, if we can
+ // get it.
+ TimeStamp nextFrame = aCurrentFrame;
+
+ MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
+ if (aVsyncRate != TimeDuration::Forever()) {
+ nextFrame += aVsyncRate;
+ }
+
+ wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
+ }
+
+ LayerComposite* rootComposite = root->AsLayerComposite();
+
+ gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
+ trans *= gfx::Matrix4x4::From2D(mWorldTransform);
+ rootComposite->SetShadowBaseTransform(trans);
+
+ if (gfxPrefs::CollectScrollTransforms()) {
+ RecordShadowTransforms(root);
+ }
+
+ return wantNextFrame;
+}
+
+void
+AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect)
+{
+ // ** STUB **
+}
+
+void
+AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins)
+{
+ // ** STUB **
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/AsyncCompositionManager.h b/system/graphics/layers/composite/AsyncCompositionManager.h
new file mode 100644
index 000000000..98aaa964e
--- /dev/null
+++ b/system/graphics/layers/composite/AsyncCompositionManager.h
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ASYNCCOMPOSITIONMANAGER_H
+#define GFX_ASYNCCOMPOSITIONMANAGER_H
+
+#include "Units.h" // for ScreenPoint, etc
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite
+#include "mozilla/Attributes.h" // for final, etc
+#include "mozilla/RefPtr.h" // for RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/FrameUniformityData.h" // For FrameUniformityData
+#include "mozilla/layers/LayersMessages.h" // for TargetConfig
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for LayerManager::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class Layer;
+class LayerManagerComposite;
+class AutoResolveRefLayers;
+class CompositorBridgeParent;
+
+// Represents async transforms consisting of a scale and a translation.
+struct AsyncTransform {
+ explicit AsyncTransform(LayerToParentLayerScale aScale = LayerToParentLayerScale(),
+ ParentLayerPoint aTranslation = ParentLayerPoint())
+ : mScale(aScale)
+ , mTranslation(aTranslation)
+ {}
+
+ operator AsyncTransformComponentMatrix() const
+ {
+ return AsyncTransformComponentMatrix::Scaling(mScale.scale, mScale.scale, 1)
+ .PostTranslate(mTranslation.x, mTranslation.y, 0);
+ }
+
+ bool operator==(const AsyncTransform& rhs) const {
+ return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
+ }
+
+ bool operator!=(const AsyncTransform& rhs) const {
+ return !(*this == rhs);
+ }
+
+ LayerToParentLayerScale mScale;
+ ParentLayerPoint mTranslation;
+};
+
+/**
+ * Manage async composition effects. This class is only used with OMTC and only
+ * lives on the compositor thread. It is a layer on top of the layer manager
+ * (LayerManagerComposite) which deals with elements of composition which are
+ * usually dealt with by dom or layout when main thread rendering, but which can
+ * short circuit that stuff to directly affect layers as they are composited,
+ * for example, off-main thread animation, async video, async pan/zoom.
+ */
+class AsyncCompositionManager final
+{
+ friend class AutoResolveRefLayers;
+ ~AsyncCompositionManager();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager)
+
+ explicit AsyncCompositionManager(LayerManagerComposite* aManager);
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint() { mIsFirstPaint = true; }
+
+ // Sample transforms for layer trees. Return true to request
+ // another animation frame.
+ enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
+ bool TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip = TransformsToSkip::NoneOfThem);
+
+ // Calculates the correct rotation and applies the transform to
+ // our layer manager
+ void ComputeRotation();
+
+ // Call after updating our layer tree.
+ void Updated(bool isFirstPaint, const TargetConfig& aTargetConfig,
+ int32_t aPaintSyncId)
+ {
+ mIsFirstPaint |= isFirstPaint;
+ mLayersUpdated = true;
+ mTargetConfig = aTargetConfig;
+ if (aPaintSyncId) {
+ mPaintSyncId = aPaintSyncId;
+ }
+ }
+
+ bool RequiresReorientation(mozilla::dom::ScreenOrientationInternal aOrientation) const
+ {
+ return mTargetConfig.orientation() != aOrientation;
+ }
+
+ // True if the underlying layer tree is ready to be composited.
+ bool ReadyForCompose() { return mReadyForCompose; }
+
+ // Returns true if the next composition will be the first for a
+ // particular document.
+ bool IsFirstPaint() { return mIsFirstPaint; }
+
+ // GetFrameUniformity will return the frame uniformity for each layer attached to an APZ
+ // from the recorded data in RecordShadowTransform
+ void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
+
+ // Stores the clip rect of a layer in two parts: a fixed part and a scrolled
+ // part. When a layer is fixed, the clip needs to be adjusted to account for
+ // async transforms. Only the fixed part needs to be adjusted, so we need
+ // to store the two parts separately.
+ struct ClipParts {
+ Maybe<ParentLayerIntRect> mFixedClip;
+ Maybe<ParentLayerIntRect> mScrolledClip;
+
+ Maybe<ParentLayerIntRect> Intersect() const {
+ return IntersectMaybeRects(mFixedClip, mScrolledClip);
+ }
+ };
+
+ typedef std::map<Layer*, ClipParts> ClipPartsCache;
+private:
+ // Return true if an AsyncPanZoomController content transform was
+ // applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
+ // one of the metrics on one of the layers was determined to be the "root"
+ // and its state was synced to the Java front-end. |aOutFoundRoot| must be
+ // non-null.
+ bool ApplyAsyncContentTransformToTree(Layer* aLayer,
+ bool* aOutFoundRoot);
+ /**
+ * Update the shadow transform for aLayer assuming that is a scrollbar,
+ * so that it stays in sync with the content that is being scrolled by APZ.
+ */
+ void ApplyAsyncTransformToScrollbar(Layer* aLayer);
+
+ void SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect);
+ void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins);
+
+ /**
+ * Adds a translation to the transform of any fixed position (whose parent
+ * layer is not fixed) or sticky position layer descendant of
+ * |aTransformedSubtreeRoot|. The translation is chosen so that the layer's
+ * anchor point relative to |aTransformedSubtreeRoot|'s parent layer is the same
+ * as it was when |aTransformedSubtreeRoot|'s GetLocalTransform() was
+ * |aPreviousTransformForRoot|. |aCurrentTransformForRoot| is
+ * |aTransformedSubtreeRoot|'s current GetLocalTransform() modulo any
+ * overscroll-related transform, which we don't want to adjust for.
+ * For sticky position layers, the translation is further intersected with
+ * the layer's sticky scroll ranges.
+ * This function will also adjust layers so that the given content document
+ * fixed position margins will be respected during asynchronous panning and
+ * zooming.
+ * |aTransformScrollId| is the scroll id of the scroll frame that scrolls
+ * |aTransformedSubtreeRoot|.
+ * |aClipPartsCache| optionally maps layers to separate fixed and scrolled
+ * clips, so we can only adjust the fixed portion.
+ * This function has a recursive implementation; aStartTraversalAt specifies
+ * where to start the current recursion of the traversal. For the initial
+ * call, it should be the same as aTrasnformedSubtreeRoot.
+ */
+ void AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+ Layer* aStartTraversalAt,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache);
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * For reach RefLayer in our layer tree, look up its referent and connect it
+ * to the layer tree, if found.
+ * aHasRemoteContent - indicates if the layer tree contains a remote reflayer.
+ * May be null.
+ * aResolvePlugins - incoming value indicates if plugin windows should be
+ * updated through a call on aCompositor's UpdatePluginWindowState. Applies
+ * to linux and windows only, may be null. On return value indicates
+ * if any updates occured.
+ */
+ void ResolveRefLayers(CompositorBridgeParent* aCompositor, bool* aHasRemoteContent,
+ bool* aResolvePlugins);
+
+ /**
+ * Detaches all referents resolved by ResolveRefLayers.
+ * Assumes that mLayerManager->GetRoot() and mTargetConfig have not changed
+ * since ResolveRefLayers was called.
+ */
+ void DetachRefLayers();
+
+ // Records the shadow transforms for the tree of layers rooted at the given layer
+ void RecordShadowTransforms(Layer* aLayer);
+
+ TargetConfig mTargetConfig;
+ CSSRect mContentRect;
+
+ RefPtr<LayerManagerComposite> mLayerManager;
+ // When this flag is set, the next composition will be the first for a
+ // particular document (i.e. the document displayed on the screen will change).
+ // This happens when loading a new page or switching tabs. We notify the
+ // front-end (e.g. Java on Android) about this so that it take the new page
+ // size and zoom into account when providing us with the next view transform.
+ bool mIsFirstPaint;
+
+ // This flag is set during a layers update, so that the first composition
+ // after a layers update has it set. It is cleared after that first composition.
+ bool mLayersUpdated;
+
+ int32_t mPaintSyncId;
+
+ bool mReadyForCompose;
+
+ gfx::Matrix mWorldTransform;
+ LayerTransformRecorder mLayerTransformRecorder;
+
+ TimeStamp mPreviousFrameTimeStamp;
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AsyncCompositionManager::TransformsToSkip)
+
+class MOZ_STACK_CLASS AutoResolveRefLayers {
+public:
+ explicit AutoResolveRefLayers(AsyncCompositionManager* aManager,
+ CompositorBridgeParent* aCompositor = nullptr,
+ bool* aHasRemoteContent = nullptr,
+ bool* aResolvePlugins = nullptr) :
+ mManager(aManager)
+ {
+ if (mManager) {
+ mManager->ResolveRefLayers(aCompositor, aHasRemoteContent, aResolvePlugins);
+ }
+ }
+
+ ~AutoResolveRefLayers()
+ {
+ if (mManager) {
+ mManager->DetachRefLayers();
+ }
+ }
+
+private:
+ AsyncCompositionManager* mManager;
+
+ AutoResolveRefLayers(const AutoResolveRefLayers&) = delete;
+ AutoResolveRefLayers& operator=(const AutoResolveRefLayers&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/CanvasLayerComposite.cpp b/system/graphics/layers/composite/CanvasLayerComposite.cpp
new file mode 100644
index 000000000..5ccbeeab9
--- /dev/null
+++ b/system/graphics/layers/composite/CanvasLayerComposite.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasLayerComposite.h"
+#include "composite/CompositableHost.h" // for CompositableHost
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CanvasLayerComposite::CanvasLayerComposite(LayerManagerComposite* aManager)
+ : CanvasLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mCompositableHost(nullptr)
+{
+ MOZ_COUNT_CTOR(CanvasLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+CanvasLayerComposite::~CanvasLayerComposite()
+{
+ MOZ_COUNT_DTOR(CanvasLayerComposite);
+
+ CleanupResources();
+}
+
+bool
+CanvasLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE: {
+ if (mCompositableHost && aHost != mCompositableHost) {
+ mCompositableHost->Detach(this);
+ }
+ mCompositableHost = aHost;
+ return true;
+ }
+ default:
+ return false;
+ }
+
+}
+
+Layer*
+CanvasLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mCompositableHost && mCompositor) {
+ mCompositableHost->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+CanvasLayerComposite::GetRenderState()
+{
+ if (mDestroyed || !mCompositableHost || !mCompositableHost->IsAttached()) {
+ return LayerRenderState();
+ }
+ return mCompositableHost->GetRenderState();
+}
+
+void
+CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ if (!mCompositableHost || !mCompositableHost->IsAttached()) {
+ return;
+ }
+
+ mCompositor->MakeCurrent();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mCompositableHost->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransform(),
+ GetSamplingFilter(),
+ clipRect);
+ });
+
+ mCompositableHost->BumpFlashCounter();
+}
+
+CompositableHost*
+CanvasLayerComposite::GetCompositableHost()
+{
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ return mCompositableHost.get();
+ }
+
+ return nullptr;
+}
+
+void
+CanvasLayerComposite::CleanupResources()
+{
+ if (mCompositableHost) {
+ mCompositableHost->Detach(this);
+ }
+ mCompositableHost = nullptr;
+}
+
+gfx::SamplingFilter
+CanvasLayerComposite::GetSamplingFilter()
+{
+ return mSamplingFilter;
+}
+
+void
+CanvasLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mCompositableHost->GenEffect(GetSamplingFilter());
+}
+
+void
+CanvasLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ CanvasLayer::PrintInfo(aStream, aPrefix);
+ aStream << "\n";
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mCompositableHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/CanvasLayerComposite.h b/system/graphics/layers/composite/CanvasLayerComposite.h
new file mode 100644
index 000000000..0042d9027
--- /dev/null
+++ b/system/graphics/layers/composite/CanvasLayerComposite.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CanvasLayerComposite_H
+#define GFX_CanvasLayerComposite_H
+
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+// Canvas layers use ImageHosts (but CanvasClients) because compositing a
+// canvas is identical to compositing an image.
+class ImageHost;
+
+class CanvasLayerComposite : public CanvasLayer,
+ public LayerComposite
+{
+public:
+ explicit CanvasLayerComposite(LayerManagerComposite* aManager);
+
+protected:
+ virtual ~CanvasLayerComposite();
+
+public:
+ // CanvasLayer impl
+ virtual void Initialize(const Data& aData) override
+ {
+ NS_RUNTIMEABORT("Incompatibe surface type");
+ }
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual void Disconnect() override
+ {
+ Destroy();
+ }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual Layer* GetLayer() override;
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ void SetBounds(gfx::IntRect aBounds) { mBounds = aBounds; }
+
+ virtual const char* Name() const override { return "CanvasLayerComposite"; }
+
+protected:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+private:
+ RefPtr<CompositableHost> mCompositableHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CanvasLayerComposite_H */
diff --git a/system/graphics/layers/composite/ColorLayerComposite.cpp b/system/graphics/layers/composite/ColorLayerComposite.cpp
new file mode 100644
index 000000000..4277a8f70
--- /dev/null
+++ b/system/graphics/layers/composite/ColorLayerComposite.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ColorLayerComposite.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::COLOR
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ Rect rect(GetBounds());
+ const Matrix4x4& transform = GetEffectiveTransform();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ GenEffectChain(effectChain);
+ mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
+ transform);
+ });
+
+ mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
+ transform);
+}
+
+void
+ColorLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = new EffectSolidColor(GetColor());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/ColorLayerComposite.h b/system/graphics/layers/composite/ColorLayerComposite.h
new file mode 100644
index 000000000..fb019f74f
--- /dev/null
+++ b/system/graphics/layers/composite/ColorLayerComposite.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ColorLayerComposite_H
+#define GFX_ColorLayerComposite_H
+
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+class ColorLayerComposite : public ColorLayer,
+ public LayerComposite
+{
+public:
+ explicit ColorLayerComposite(LayerManagerComposite *aManager)
+ : ColorLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ {
+ MOZ_COUNT_CTOR(ColorLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+ }
+
+protected:
+ ~ColorLayerComposite()
+ {
+ MOZ_COUNT_DTOR(ColorLayerComposite);
+ Destroy();
+ }
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ }
+
+ virtual void Destroy() override { mDestroyed = true; }
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void CleanupResources() override {};
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual const char* Name() const override { return "ColorLayerComposite"; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ColorLayerComposite_H */
diff --git a/system/graphics/layers/composite/CompositableHost.cpp b/system/graphics/layers/composite/CompositableHost.cpp
new file mode 100644
index 000000000..5ed3d3fe9
--- /dev/null
+++ b/system/graphics/layers/composite/CompositableHost.cpp
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableHost.h"
+#include <map> // for _Rb_tree_iterator, map, etc
+#include <utility> // for pair
+#include "ContentHost.h" // for ContentHostDoubleBuffered, etc
+#include "Effects.h" // for EffectMask, Effect, etc
+#include "gfxUtils.h"
+#include "ImageHost.h" // for ImageHostBuffered, etc
+#include "TiledContentHost.h" // for TiledContentHost
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/layers/PCompositableParent.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class Compositor;
+
+/**
+ * IPDL actor used by CompositableHost to match with its corresponding
+ * CompositableClient on the content side.
+ *
+ * CompositableParent is owned by the IPDL system. It's deletion is triggered
+ * by either the CompositableChild's deletion, or by the IPDL communication
+ * going down.
+ */
+class CompositableParent : public ParentActor<PCompositableParent>
+{
+public:
+ CompositableParent(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID = 0,
+ PImageContainerParent* aImageContainer = nullptr)
+ {
+ MOZ_COUNT_CTOR(CompositableParent);
+ mHost = CompositableHost::Create(aTextureInfo);
+ mHost->SetAsyncID(aID);
+ if (aID) {
+ CompositableMap::Set(aID, this);
+ }
+ if (aImageContainer) {
+ mHost->SetImageContainer(
+ static_cast<ImageContainerParent*>(aImageContainer));
+ }
+ }
+
+ ~CompositableParent()
+ {
+ MOZ_COUNT_DTOR(CompositableParent);
+ CompositableMap::Erase(mHost->GetAsyncID());
+ }
+
+ virtual void Destroy() override
+ {
+ if (mHost) {
+ mHost->Detach(nullptr, CompositableHost::FORCE_DETACH);
+ }
+ }
+
+ RefPtr<CompositableHost> mHost;
+};
+
+CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
+ : mTextureInfo(aTextureInfo)
+ , mAsyncID(0)
+ , mCompositorID(0)
+ , mCompositor(nullptr)
+ , mLayer(nullptr)
+ , mFlashCounter(0)
+ , mAttached(false)
+ , mKeepAttached(false)
+{
+ MOZ_COUNT_CTOR(CompositableHost);
+}
+
+CompositableHost::~CompositableHost()
+{
+ MOZ_COUNT_DTOR(CompositableHost);
+}
+
+PCompositableParent*
+CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID,
+ PImageContainerParent* aImageContainer)
+{
+ return new CompositableParent(aMgr, aTextureInfo, aID, aImageContainer);
+}
+
+bool
+CompositableHost::DestroyIPDLActor(PCompositableParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+CompositableHost*
+CompositableHost::FromIPDLActor(PCompositableParent* aActor)
+{
+ MOZ_ASSERT(aActor);
+ return static_cast<CompositableParent*>(aActor)->mHost;
+}
+
+void
+CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ if (GetCompositor()) {
+ for (auto& texture : aTextures) {
+ texture.mTexture->SetCompositor(GetCompositor());
+ }
+ }
+}
+
+void
+CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite)
+{
+ MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
+ if (GetCompositor()) {
+ aTextureOnBlack->SetCompositor(GetCompositor());
+ aTextureOnWhite->SetCompositor(GetCompositor());
+ }
+}
+
+void
+CompositableHost::RemoveTextureHost(TextureHost* aTexture)
+{}
+
+void
+CompositableHost::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ mCompositor = aCompositor;
+}
+
+bool
+CompositableHost::AddMaskEffect(EffectChain& aEffects,
+ const gfx::Matrix4x4& aTransform)
+{
+ CompositableTextureSourceRef source;
+ RefPtr<TextureHost> host = GetAsTextureHost();
+
+ if (!host) {
+ NS_WARNING("Using compositable with no valid TextureHost as mask");
+ return false;
+ }
+
+ if (!host->Lock()) {
+ NS_WARNING("Failed to lock the mask texture");
+ return false;
+ }
+
+ if (!host->BindTextureSource(source)) {
+ NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
+ host->Unlock();
+ return false;
+ }
+ MOZ_ASSERT(source);
+
+ RefPtr<EffectMask> effect = new EffectMask(source,
+ source->GetSize(),
+ aTransform);
+ aEffects.mSecondaryEffects[EffectTypes::MASK] = effect;
+ return true;
+}
+
+void
+CompositableHost::RemoveMaskEffect()
+{
+ RefPtr<TextureHost> host = GetAsTextureHost();
+ if (host) {
+ host->Unlock();
+ }
+}
+
+/* static */ already_AddRefed<CompositableHost>
+CompositableHost::Create(const TextureInfo& aTextureInfo)
+{
+ RefPtr<CompositableHost> result;
+ switch (aTextureInfo.mCompositableType) {
+ case CompositableType::IMAGE_BRIDGE:
+ NS_ERROR("Cannot create an image bridge compositable this way");
+ break;
+ case CompositableType::CONTENT_TILED:
+ result = new TiledContentHost(aTextureInfo);
+ break;
+ case CompositableType::IMAGE:
+ result = new ImageHost(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_SINGLE:
+ result = new ContentHostSingleBuffered(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_DOUBLE:
+ result = new ContentHostDoubleBuffered(aTextureInfo);
+ break;
+ default:
+ NS_ERROR("Unknown CompositableType");
+ }
+ return result.forget();
+}
+
+void
+CompositableHost::DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture)
+{
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+}
+
+void
+CompositableHost::ReceivedDestroy(PCompositableParent* aActor)
+{
+ static_cast<CompositableParent*>(aActor)->RecvDestroy();
+}
+
+namespace CompositableMap {
+
+typedef std::map<uint64_t, PCompositableParent*> CompositableMap_t;
+static CompositableMap_t* sCompositableMap = nullptr;
+bool IsCreated() {
+ return sCompositableMap != nullptr;
+}
+PCompositableParent* Get(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return nullptr;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it == sCompositableMap->end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+void Set(uint64_t aID, PCompositableParent* aParent)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ (*sCompositableMap)[aID] = aParent;
+}
+void Erase(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it != sCompositableMap->end()) {
+ sCompositableMap->erase(it);
+ }
+}
+void Clear()
+{
+ if (!IsCreated()) {
+ return;
+ }
+ sCompositableMap->clear();
+}
+void Create()
+{
+ if (sCompositableMap == nullptr) {
+ sCompositableMap = new CompositableMap_t;
+ }
+}
+void Destroy()
+{
+ Clear();
+ delete sCompositableMap;
+ sCompositableMap = nullptr;
+}
+
+} // namespace CompositableMap
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/CompositableHost.h b/system/graphics/layers/composite/CompositableHost.h
new file mode 100644
index 000000000..d8a967732
--- /dev/null
+++ b/system/graphics/layers/composite/CompositableHost.h
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BUFFERHOST_H
+#define MOZILLA_GFX_BUFFERHOST_H
+
+#include <stdint.h> // for uint64_t
+#include <stdio.h> // for FILE
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted, etc
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/Effects.h" // for Texture Effect
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+#include "Units.h" // for CSSToScreenScale
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Layer;
+class LayerComposite;
+class Compositor;
+class ImageContainerParent;
+class ThebesBufferData;
+class TiledContentHost;
+class CompositableParentManager;
+class PCompositableParent;
+struct EffectChain;
+
+/**
+ * The compositor-side counterpart to CompositableClient. Responsible for
+ * updating textures and data about textures from IPC and how textures are
+ * composited (tiling, double buffering, etc.).
+ *
+ * Update (for images/canvases) and UpdateThebes (for Thebes) are called during
+ * the layers transaction to update the Compositbale's textures from the
+ * content side. The actual update (and any syncronous upload) is done by the
+ * TextureHost, but it is coordinated by the CompositableHost.
+ *
+ * Composite is called by the owning layer when it is composited. CompositableHost
+ * will use its TextureHost(s) and call Compositor::DrawQuad to do the actual
+ * rendering.
+ */
+class CompositableHost
+{
+protected:
+ virtual ~CompositableHost();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(CompositableHost)
+ explicit CompositableHost(const TextureInfo& aTextureInfo);
+
+ static already_AddRefed<CompositableHost> Create(const TextureInfo& aTextureInfo);
+
+ virtual CompositableType GetType() = 0;
+
+ // If base class overrides, it should still call the parent implementation
+ virtual void SetCompositor(Compositor* aCompositor);
+
+ // composite the contents of this buffer host to the compositor's surface
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) = 0;
+
+ /**
+ * Update the content host.
+ * aUpdated is the region which should be updated
+ * aUpdatedRegionBack is the region in aNewBackResult which has been updated
+ */
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+ {
+ NS_ERROR("should be implemented or not used");
+ return false;
+ }
+
+ /**
+ * Returns the front buffer.
+ * *aPictureRect (if non-null, and the returned TextureHost is non-null)
+ * is set to the picture rect.
+ */
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) {
+ return nullptr;
+ }
+
+ virtual LayerRenderState GetRenderState() = 0;
+
+ virtual gfx::IntSize GetImageSize() const
+ {
+ MOZ_ASSERT(false, "Should have been overridden");
+ return gfx::IntSize();
+ }
+
+ /**
+ * Adds a mask effect using this texture as the mask, if possible.
+ * @return true if the effect was added, false otherwise.
+ */
+ bool AddMaskEffect(EffectChain& aEffects,
+ const gfx::Matrix4x4& aTransform);
+
+ void RemoveMaskEffect();
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ Layer* GetLayer() const { return mLayer; }
+ void SetLayer(Layer* aLayer) { mLayer = aLayer; }
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) {}
+
+ virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
+
+ typedef uint32_t AttachFlags;
+ static const AttachFlags NO_FLAGS = 0;
+ static const AttachFlags ALLOW_REATTACH = 1;
+ static const AttachFlags KEEP_ATTACHED = 2;
+ static const AttachFlags FORCE_DETACH = 2;
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS)
+ {
+ MOZ_ASSERT(aCompositor, "Compositor is required");
+ NS_ASSERTION(aFlags & ALLOW_REATTACH || !mAttached,
+ "Re-attaching compositables must be explicitly authorised");
+ SetCompositor(aCompositor);
+ SetLayer(aLayer);
+ mAttached = true;
+ mKeepAttached = aFlags & KEEP_ATTACHED;
+ }
+ // Detach this compositable host from its layer.
+ // If we are used for async video, then it is not safe to blindly detach since
+ // we might be re-attached to a different layer. aLayer is the layer which the
+ // caller expects us to be attached to, we will only detach if we are in fact
+ // attached to that layer. If we are part of a normal layer, then we will be
+ // detached in any case. if aLayer is null, then we will only detach if we are
+ // not async.
+ // Only force detach if the IPDL tree is being shutdown.
+ virtual void Detach(Layer* aLayer = nullptr, AttachFlags aFlags = NO_FLAGS)
+ {
+ if (!mKeepAttached ||
+ aLayer == mLayer ||
+ aFlags & FORCE_DETACH) {
+ SetLayer(nullptr);
+ mAttached = false;
+ mKeepAttached = false;
+ }
+ }
+ bool IsAttached() { return mAttached; }
+
+ static void
+ ReceivedDestroy(PCompositableParent* aActor);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) { }
+ static void DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture);
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() { return nullptr; }
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
+
+ struct TimedTexture {
+ CompositableTextureHostRef mTexture;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures);
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite);
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) { }
+
+ virtual void RemoveTextureHost(TextureHost* aTexture);
+
+ // Called every time this is composited
+ void BumpFlashCounter() {
+ mFlashCounter = mFlashCounter >= DIAGNOSTIC_FLASH_COUNTER_MAX
+ ? DIAGNOSTIC_FLASH_COUNTER_MAX : mFlashCounter + 1;
+ }
+
+ static PCompositableParent*
+ CreateIPDLActor(CompositableParentManager* mgr,
+ const TextureInfo& textureInfo,
+ uint64_t asyncID,
+ PImageContainerParent* aImageContainer = nullptr);
+
+ static bool DestroyIPDLActor(PCompositableParent* actor);
+
+ static CompositableHost* FromIPDLActor(PCompositableParent* actor);
+
+ uint64_t GetCompositorID() const { return mCompositorID; }
+
+ uint64_t GetAsyncID() const { return mAsyncID; }
+
+ void SetCompositorID(uint64_t aID) { mCompositorID = aID; }
+
+ void SetAsyncID(uint64_t aID) { mAsyncID = aID; }
+
+ virtual bool Lock() { return false; }
+
+ virtual void Unlock() { }
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) {
+ return nullptr;
+ }
+
+ /// Called when shutting down the layer tree.
+ /// This is a good place to clear all potential gpu resources before the widget
+ /// is is destroyed.
+ virtual void CleanupResources() {}
+
+protected:
+ TextureInfo mTextureInfo;
+ uint64_t mAsyncID;
+ uint64_t mCompositorID;
+ RefPtr<Compositor> mCompositor;
+ Layer* mLayer;
+ uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
+ bool mAttached;
+ bool mKeepAttached;
+};
+
+class AutoLockCompositableHost final
+{
+public:
+ explicit AutoLockCompositableHost(CompositableHost* aHost)
+ : mHost(aHost)
+ {
+ mSucceeded = (mHost && mHost->Lock());
+ }
+
+ ~AutoLockCompositableHost()
+ {
+ if (mSucceeded && mHost) {
+ mHost->Unlock();
+ }
+ }
+
+ bool Failed() const { return !mSucceeded; }
+
+private:
+ RefPtr<CompositableHost> mHost;
+ bool mSucceeded;
+};
+
+/**
+ * Global CompositableMap, to use in the compositor thread only.
+ *
+ * PCompositable and PLayer can, in the case of async textures, be managed by
+ * different top level protocols. In this case they don't share the same
+ * communication channel and we can't send an OpAttachCompositable (PCompositable,
+ * PLayer) message.
+ *
+ * In order to attach a layer and the right compositable if the the compositable
+ * is async, we store references to the async compositables in a CompositableMap
+ * that is accessed only on the compositor thread. During a layer transaction we
+ * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
+ * side we lookup the ID in the map and attach the corresponding compositable to
+ * the layer.
+ *
+ * CompositableMap must be global because the image bridge doesn't have any
+ * reference to whatever we have created with PLayerTransaction. So, the only way to
+ * actually connect these two worlds is to have something global that they can
+ * both query (in the same thread). The map is not allocated the map on the
+ * stack to avoid the badness of static initialization.
+ *
+ * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
+ * ImageBridge is used by all the existing compositors that have a video, so
+ * there isn't an instance or "something" that lives outside the boudaries of a
+ * given layer manager on the compositor thread except the image bridge and the
+ * thread itself.
+ */
+namespace CompositableMap {
+ void Create();
+ void Destroy();
+ PCompositableParent* Get(uint64_t aID);
+ void Set(uint64_t aID, PCompositableParent* aParent);
+ void Erase(uint64_t aID);
+ void Clear();
+} // namespace CompositableMap
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/ContainerLayerComposite.cpp b/system/graphics/layers/composite/ContainerLayerComposite.cpp
new file mode 100644
index 000000000..f25503532
--- /dev/null
+++ b/system/graphics/layers/composite/ContainerLayerComposite.cpp
@@ -0,0 +1,676 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContainerLayerComposite.h"
+#include <algorithm> // for min
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerRect, LayerPixel, etc
+#include "CompositableHost.h" // for CompositableHost
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point, IntPoint
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/Compositor.h" // for Compositor, etc
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include <stack>
+#include "TextRenderer.h" // for TextRenderer
+#include <vector>
+#include "GeckoProfiler.h" // for GeckoProfiler
+
+#define CULLING_LOG(...)
+// #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
+
+#define DUMP(...) do { if (gfxEnv::DumpDebug()) { printf_stderr(__VA_ARGS__); } } while(0)
+#define XYWH(k) (k).x, (k).y, (k).width, (k).height
+#define XY(k) (k).x, (k).y
+#define WH(k) (k).width, (k).height
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
+ LayerManagerComposite* aManager,
+ Layer* aLayer)
+{
+
+ if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
+ // XXX - should figure out a way to render this, but for now this
+ // is hard to do, since it will often get superimposed over the first
+ // child of the layer, which is bad.
+ return;
+ }
+
+ std::stringstream ss;
+ aLayer->PrintInfo(ss, "");
+
+ LayerIntRegion visibleRegion = aLayer->GetVisibleRegion();
+
+ uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
+
+ IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
+ aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
+ aLayer->GetEffectiveTransform(), 16,
+ maxWidth);
+}
+
+template<class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
+{
+ gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+ return surfaceRect;
+}
+
+static void PrintUniformityInfo(Layer* aLayer)
+{
+ /*** STUB ***/
+}
+
+/* all of the per-layer prepared data we need to maintain */
+struct PreparedLayer
+{
+ PreparedLayer(Layer *aLayer, RenderTargetIntRect aClipRect) :
+ mLayer(aLayer), mClipRect(aClipRect) {}
+ RefPtr<Layer> mLayer;
+ RenderTargetIntRect mClipRect;
+};
+
+/* all of the prepared data that we need in RenderLayer() */
+struct PreparedData
+{
+ RefPtr<CompositingRenderTarget> mTmpTarget;
+ AutoTArray<PreparedLayer, 12> mLayers;
+ bool mNeedsSurfaceCopy;
+};
+
+// ContainerPrepare is shared between RefLayer and ContainerLayer
+template<class ContainerT> void
+ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect)
+{
+ aContainer->mPrepared = MakeUnique<PreparedData>();
+ aContainer->mPrepared->mNeedsSurfaceCopy = false;
+
+ /**
+ * Determine which layers to draw.
+ */
+ AutoTArray<Layer*, 12> children;
+ aContainer->SortChildrenBy3DZOrder(children);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
+
+ RenderTargetIntRect clipRect = layerToRender->GetLayer()->
+ CalculateScissorRect(aClipRect);
+
+ if (layerToRender->GetLayer()->IsBackfaceHidden()) {
+ continue;
+ }
+
+ // We don't want to skip container layers because otherwise their mPrepared
+ // may be null which is not allowed.
+ if (!layerToRender->GetLayer()->AsContainerLayer()) {
+ if (!layerToRender->GetLayer()->IsVisible() &&
+ !layerToRender->NeedToDrawCheckerboarding(nullptr)) {
+ CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
+ continue;
+ }
+
+ if (clipRect.IsEmpty()) {
+ CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
+ continue;
+ }
+ }
+
+ CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
+
+ layerToRender->Prepare(clipRect);
+ aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender->GetLayer(),
+ clipRect));
+ }
+
+ CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
+
+ /**
+ * Setup our temporary surface for rendering the contents of this container.
+ */
+
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ bool surfaceCopyNeeded;
+ // DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call it unconditionally
+ aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
+ if (aContainer->UseIntermediateSurface()) {
+ if (!surfaceCopyNeeded) {
+ RefPtr<CompositingRenderTarget> surface = nullptr;
+
+ RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
+ if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ surface = lastSurf;
+ }
+
+ if (!surface) {
+ // If we don't need a copy we can render to the intermediate now to avoid
+ // unecessary render target switching. This brings a big perf boost on mobile gpus.
+ surface = CreateOrRecycleTarget(aContainer, aManager);
+
+ MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface rendering\n", aContainer);
+ RenderIntermediate(aContainer, aManager, aClipRect.ToUnknownRect(), surface);
+ aContainer->SetChildrenChanged(false);
+ }
+
+ aContainer->mPrepared->mTmpTarget = surface;
+ } else {
+ MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface copy\n", aContainer);
+ aContainer->mPrepared->mNeedsSurfaceCopy = true;
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+ } else {
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+}
+
+template<class ContainerT> void
+RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ if (aLayer->GetScrollMetadataCount() < 1) {
+ return;
+ }
+
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ if (!controller) {
+ return;
+ }
+
+ ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // Options
+ const int verticalPadding = 10;
+ const int horizontalPadding = 5;
+ gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
+ gfx::Color tileActiveColor(1, 1, 1, 0.4f);
+ gfx::Color tileBorderColor(0, 0, 0, 0.1f);
+ gfx::Color pageBorderColor(0, 0, 0);
+ gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
+ gfx::Color displayPortColor(0, 1.f, 0);
+ gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
+ gfx::Color visibilityColor(1.f, 0, 0);
+
+ // Rects
+ const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
+ ParentLayerRect compositionBounds = fm.GetCompositionBounds();
+ LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
+ LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
+ LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
+ Maybe<LayerRect> cdp;
+ if (!fm.GetCriticalDisplayPort().IsEmpty()) {
+ cdp = Some((fm.GetCriticalDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel());
+ }
+
+ // Don't render trivial minimap. They can show up from textboxes and other tiny frames.
+ if (viewRect.width < 64 && viewRect.height < 64) {
+ return;
+ }
+
+ // Compute a scale with an appropriate aspect ratio
+ // We allocate up to 100px of width and the height of this layer.
+ float scaleFactor;
+ float scaleFactorX;
+ float scaleFactorY;
+ Rect dest = Rect(aClipRect.ToUnknownRect());
+ if (aLayer->GetLocalClipRect()) {
+ dest = Rect(aLayer->GetLocalClipRect().value().ToUnknownRect());
+ } else {
+ dest = aContainer->GetEffectiveTransform().Inverse().TransformBounds(dest);
+ }
+ dest = dest.Intersect(compositionBounds.ToUnknownRect());
+ scaleFactorX = std::min(100.f, dest.width - (2 * horizontalPadding)) / scrollRect.width;
+ scaleFactorY = (dest.height - (2 * verticalPadding)) / scrollRect.height;
+ scaleFactor = std::min(scaleFactorX, scaleFactorY);
+ if (scaleFactor <= 0) {
+ return;
+ }
+
+ Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1);
+ transform.PostTranslate(horizontalPadding + dest.x, verticalPadding + dest.y, 0);
+
+ Rect transformedScrollRect = transform.TransformBounds(scrollRect.ToUnknownRect());
+
+ IntRect clipRect = RoundedOut(aContainer->GetEffectiveTransform().TransformBounds(transformedScrollRect));
+
+ // Render the scrollable area.
+ compositor->FillRect(transformedScrollRect, backgroundColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(transformedScrollRect, pageBorderColor, clipRect, aContainer->GetEffectiveTransform());
+
+ // If enabled, render information about visibility.
+ if (gfxPrefs::APZMinimapVisibilityEnabled()) {
+ // Retrieve the APZC scrollable layer guid, which we'll use to get the
+ // appropriate visibility information from the layer manager.
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ MOZ_ASSERT(controller);
+
+ ScrollableLayerGuid guid = controller->GetGuid();
+
+ // Get the approximately visible region.
+ static CSSIntRegion emptyRegion;
+ CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
+ if (!visibleRegion) {
+ visibleRegion = &emptyRegion;
+ }
+
+ // Iterate through and draw the rects in the region.
+ for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
+ !iterator.Done();
+ iterator.Next())
+ {
+ CSSIntRect rect = iterator.Get();
+ LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
+ Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
+ compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
+ }
+ }
+
+ // Render the displayport.
+ Rect r = transform.TransformBounds(dp.ToUnknownRect());
+ compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
+
+ // Render the critical displayport if there is one
+ if (cdp) {
+ r = transform.TransformBounds(cdp->ToUnknownRect());
+ compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
+ }
+
+ // Render the viewport.
+ r = transform.TransformBounds(viewRect.ToUnknownRect());
+ compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
+}
+
+
+template<class ContainerT> void
+RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
+ PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(preparedData.mLayer->ImplData());
+ const RenderTargetIntRect& clipRect = preparedData.mClipRect;
+ Layer* layer = layerToRender->GetLayer();
+
+ if (layerToRender->HasStaleCompositor()) {
+ continue;
+ }
+
+ if (gfxPrefs::LayersDrawFPS()) {
+ for (const auto& metadata : layer->GetAllScrollMetadata()) {
+ if (metadata.IsApzForceDisabled()) {
+ aManager->DisabledApzWarning();
+ break;
+ }
+ }
+ }
+
+ Color color;
+ if (layerToRender->NeedToDrawCheckerboarding(&color)) {
+ if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
+ color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
+ }
+ // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
+ // and only fill in that area. However the layer bounds takes into account the base translation
+ // for the painted layer whereas the checkerboard region does not. One does not simply
+ // intersect areas in different coordinate spaces. So we do this a little more permissively
+ // and only fill in the background when we know there is checkerboard, which in theory
+ // should only occur transiently.
+ gfx::IntRect layerBounds = layer->GetLayerBounds();
+ EffectChain effectChain(layer);
+ effectChain.mPrimaryEffect = new EffectSolidColor(color);
+ aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
+ clipRect.ToUnknownRect(),
+ effectChain, layer->GetEffectiveOpacity(),
+ layer->GetEffectiveTransform());
+ }
+
+ if (layerToRender->HasLayerBeenComposited()) {
+ // Composer2D will compose this layer so skip GPU composition
+ // this time. The flag will be reset for the next composition phase
+ // at the beginning of LayerManagerComposite::Rener().
+ gfx::IntRect clearRect = layerToRender->GetClearRect();
+ if (!clearRect.IsEmpty()) {
+ // Clear layer's visible rect on FrameBuffer with transparent pixels
+ gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
+ compositor->ClearRect(fbRect);
+ layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
+ }
+ } else {
+ layerToRender->RenderLayer(clipRect.ToUnknownRect());
+ }
+
+ if (gfxPrefs::UniformityInfo()) {
+ PrintUniformityInfo(layer);
+ }
+
+ if (gfxPrefs::DrawLayerInfo()) {
+ DrawLayerInfo(clipRect, aManager, layer);
+ }
+
+ // Draw a border around scrollable layers.
+ // A layer can be scrolled by multiple scroll frames. Draw a border
+ // for each.
+ // Within the list of scroll frames for a layer, the layer border for a
+ // scroll frame lower down is affected by the async transforms on scroll
+ // frames higher up, so loop from the top down, and accumulate an async
+ // transform as we go along.
+ Matrix4x4 asyncTransform;
+ for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) {
+ if (layer->GetFrameMetrics(i - 1).IsScrollable()) {
+ // Since the composition bounds are in the parent layer's coordinates,
+ // use the parent's effective transform rather than the layer's own.
+ ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
+ aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
+ compositionBounds.ToUnknownRect(),
+ aClipRect.ToUnknownRect(),
+ asyncTransform * aContainer->GetEffectiveTransform());
+ if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
+ asyncTransform =
+ apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix()
+ * asyncTransform;
+ }
+ }
+ }
+
+ if (gfxPrefs::APZMinimap()) {
+ RenderMinimap(aContainer, aManager, aClipRect, layer);
+ }
+
+ // invariant: our GL context should be current here, I don't think we can
+ // assert it though
+ }
+}
+
+template<class ContainerT> RefPtr<CompositingRenderTarget>
+CreateOrRecycleTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ SurfaceInitMode mode = INIT_MODE_CLEAR;
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (aContainer->GetLocalVisibleRegion().GetNumRects() == 1 &&
+ (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ mode = INIT_MODE_NONE;
+ }
+
+ RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
+ if (lastSurf && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ if (mode == INIT_MODE_CLEAR) {
+ lastSurf->ClearOnBind();
+ }
+
+ return lastSurf;
+ } else {
+ lastSurf = compositor->CreateRenderTarget(surfaceRect, mode);
+
+ return lastSurf;
+ }
+}
+
+template<class ContainerT> RefPtr<CompositingRenderTarget>
+CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ gfx::IntRect visibleRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+ RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
+ gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height);
+
+ gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
+
+ gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
+ DebugOnly<gfx::Matrix> transform2d;
+ MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
+ sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
+
+ sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin();
+
+ return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint);
+}
+
+template<class ContainerT> void
+RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
+
+ if (!surface) {
+ return;
+ }
+
+ compositor->SetRenderTarget(surface);
+ // pre-render all of the layers into our temporary
+ RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+ // Unbind the current surface and rebind the previous one.
+ compositor->SetRenderTarget(previousTarget);
+}
+
+template<class ContainerT> void
+ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect)
+{
+ MOZ_ASSERT(aContainer->mPrepared);
+
+ if (aContainer->UseIntermediateSurface()) {
+ RefPtr<CompositingRenderTarget> surface;
+
+ if (aContainer->mPrepared->mNeedsSurfaceCopy) {
+ // we needed to copy the background so we waited until now to render the intermediate
+ surface = CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager);
+ RenderIntermediate(aContainer, aManager,
+ aClipRect, surface);
+ } else {
+ surface = aContainer->mPrepared->mTmpTarget;
+ }
+
+ if (!surface) {
+ aContainer->mPrepared = nullptr;
+ return;
+ }
+
+ gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ RefPtr<Compositor> compositor = aManager->GetCompositor();
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
+ if (surf) {
+ WriteSnapshotToDumpFile(aContainer, surf);
+ }
+ }
+#endif
+
+ RefPtr<ContainerT> container = aContainer;
+ RenderWithAllMasks(aContainer, compositor, aClipRect,
+ [&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
+ effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
+ compositor->DrawQuad(visibleRect, clipRect, effectChain,
+ container->GetEffectiveOpacity(),
+ container->GetEffectiveTransform());
+ });
+ } else {
+ RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+ }
+ aContainer->mPrepared = nullptr;
+
+ // If it is a scrollable container layer with no child layers, and one of the APZCs
+ // attached to it has a nonempty async transform, then that transform is not applied
+ // to any visible content. Display a warning box (conditioned on the FPS display being
+ // enabled).
+ if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
+ // Since aContainer doesn't have any children we can just iterate from the top metrics
+ // on it down to the bottom using GetFirstChild and not worry about walking onto another
+ // underlying layer.
+ for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
+ if (AsyncPanZoomController* apzc = i.GetApzc()) {
+ if (!apzc->GetAsyncTransformAppliedToContent()
+ && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) {
+ aManager->UnusedApzTransformWarning();
+ break;
+ }
+ }
+ }
+ }
+}
+
+ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager)
+ : ContainerLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+{
+ MOZ_COUNT_CTOR(ContainerLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ContainerLayerComposite::~ContainerLayerComposite()
+{
+ MOZ_COUNT_DTOR(ContainerLayerComposite);
+
+ // We don't Destroy() on destruction here because this destructor
+ // can be called after remote content has crashed, and it may not be
+ // safe to free the IPC resources of our children. Those resources
+ // are automatically cleaned up by IPDL-generated code.
+ //
+ // In the common case of normal shutdown, either
+ // LayerManagerComposite::Destroy(), a parent
+ // *ContainerLayerComposite::Destroy(), or Disconnect() will trigger
+ // cleanup of our resources.
+ while (mFirstChild) {
+ RemoveChild(mFirstChild);
+ }
+}
+
+void
+ContainerLayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ while (mFirstChild) {
+ static_cast<LayerComposite*>(GetFirstChild()->ImplData())->Destroy();
+ RemoveChild(mFirstChild);
+ }
+ mDestroyed = true;
+ }
+}
+
+LayerComposite*
+ContainerLayerComposite::GetFirstChildComposite()
+{
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->ImplData());
+}
+
+void
+ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+void
+ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
+{
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void
+ContainerLayerComposite::CleanupResources()
+{
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ LayerComposite* layerToCleanup = static_cast<LayerComposite*>(l->ImplData());
+ layerToCleanup->CleanupResources();
+ }
+}
+
+RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
+ : RefLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+{
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+RefLayerComposite::~RefLayerComposite()
+{
+ Destroy();
+}
+
+void
+RefLayerComposite::Destroy()
+{
+ MOZ_ASSERT(!mFirstChild);
+ mDestroyed = true;
+}
+
+LayerComposite*
+RefLayerComposite::GetFirstChildComposite()
+{
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->ImplData());
+}
+
+void
+RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+void
+RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
+{
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void
+RefLayerComposite::CleanupResources()
+{
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/ContainerLayerComposite.h b/system/graphics/layers/composite/ContainerLayerComposite.h
new file mode 100644
index 000000000..5128b9d80
--- /dev/null
+++ b/system/graphics/layers/composite/ContainerLayerComposite.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ContainerLayerComposite_H
+#define GFX_ContainerLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+class CompositingRenderTarget;
+struct PreparedData;
+
+class ContainerLayerComposite : public ContainerLayer,
+ public LayerComposite
+{
+ template<class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateOrRecycleTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+
+ template<class ContainerT>
+ void RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer);
+public:
+ explicit ContainerLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ ~ContainerLayerComposite();
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ virtual void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // container layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ // If the layer is marked as scale-to-resolution, add a post-scale
+ // to the layer's transform equal to the pres shell resolution we're
+ // scaling to. This cancels out the post scale of '1 / resolution'
+ // added by Layout. TODO: It would be nice to get rid of both of these
+ // post-scales.
+ virtual float GetPostXScale() const override {
+ if (mScaleToResolution) {
+ return mPostXScale * mPresShellResolution;
+ }
+ return mPostXScale;
+ }
+ virtual float GetPostYScale() const override {
+ if (mScaleToResolution) {
+ return mPostYScale * mPresShellResolution;
+ }
+ return mPostYScale;
+ }
+
+ virtual const char* Name() const override { return "ContainerLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+class RefLayerComposite : public RefLayer,
+ public LayerComposite
+{
+ template<class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+
+public:
+ explicit RefLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ ~RefLayerComposite();
+
+public:
+ /** LayerOGL implementation */
+ Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // ref layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual const char* Name() const override { return "RefLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ContainerLayerComposite_H */
diff --git a/system/graphics/layers/composite/ContentHost.cpp b/system/graphics/layers/composite/ContentHost.cpp
new file mode 100644
index 000000000..86d33fdb6
--- /dev/null
+++ b/system/graphics/layers/composite/ContentHost.cpp
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentHost.h"
+#include "LayersLogging.h" // for AppendToString
+#include "gfx2DGlue.h" // for ContentForFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+
+namespace mozilla {
+using namespace gfx;
+
+namespace layers {
+
+ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo)
+ , mInitialised(false)
+{}
+
+ContentHostBase::~ContentHostBase()
+{
+}
+
+void
+ContentHostTexture::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const SamplingFilter aSamplingFilter,
+ const IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ NS_ASSERTION(aVisibleRegion, "Requires a visible region");
+
+ AutoLockCompositableHost lock(this);
+ if (lock.Failed()) {
+ return;
+ }
+
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return;
+ }
+ MOZ_ASSERT(mTextureSource.get());
+
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect = CreateTexturedEffect(mTextureSource.get(),
+ mTextureSourceOnWhite.get(),
+ aSamplingFilter, true,
+ GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ aEffectChain.mPrimaryEffect = effect;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion;
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ } else {
+ renderRegion = aVisibleRegion;
+ }
+
+ nsIntRegion region(*renderRegion);
+ nsIntPoint origin = GetOriginOffset();
+ // translate into TexImage space, buffer origin might not be at texture (0,0)
+ region.MoveBy(-origin);
+
+ // Figure out the intersecting draw region
+ gfx::IntSize texSize = mTextureSource->GetSize();
+ IntRect textureRect = IntRect(0, 0, texSize.width, texSize.height);
+ textureRect.MoveBy(region.GetBounds().TopLeft());
+ nsIntRegion subregion;
+ subregion.And(region, textureRect);
+ if (subregion.IsEmpty()) {
+ // Region is empty, nothing to draw
+ return;
+ }
+
+ nsIntRegion screenRects;
+ nsIntRegion regionRects;
+
+ // Collect texture/screen coordinates for drawing
+ for (auto iter = subregion.RectIter(); !iter.Done(); iter.Next()) {
+ IntRect regionRect = iter.Get();
+ IntRect screenRect = iter.Get();
+ screenRect.MoveBy(origin);
+
+ screenRects.Or(screenRects, screenRect);
+ regionRects.Or(regionRects, regionRect);
+ }
+
+ BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator();
+ BigImageIterator* iterOnWhite = nullptr;
+ if (bigImgIter) {
+ bigImgIter->BeginBigImageIteration();
+ }
+
+ if (mTextureSourceOnWhite) {
+ iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator();
+ MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
+ "Tile count mismatch on component alpha texture");
+ if (iterOnWhite) {
+ iterOnWhite->BeginBigImageIteration();
+ }
+ }
+
+ bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
+ do {
+ if (iterOnWhite && bigImgIter) {
+ MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
+ "component alpha textures should be the same size.");
+ }
+
+ IntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
+ : IntRect(0, 0,
+ texSize.width,
+ texSize.height);
+
+ // Draw texture. If we're using tiles, we do repeating manually, as texture
+ // repeat would cause each individual tile to repeat instead of the
+ // compound texture as a whole. This involves drawing at most 4 sections,
+ // 2 for each axis that has texture repeat.
+ for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
+ for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
+ IntRect currentTileRect(texRect);
+ currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
+
+ for (auto screenIter = screenRects.RectIter(),
+ regionIter = regionRects.RectIter();
+ !screenIter.Done() && !regionIter.Done();
+ screenIter.Next(), regionIter.Next()) {
+ const IntRect& screenRect = screenIter.Get();
+ const IntRect& regionRect = regionIter.Get();
+ IntRect tileScreenRect(screenRect);
+ IntRect tileRegionRect(regionRect);
+
+ // When we're using tiles, find the intersection between the tile
+ // rect and this region rect. Tiling is then handled by the
+ // outer for-loops and modifying the tile rect.
+ if (usingTiles) {
+ tileScreenRect.MoveBy(-origin);
+ tileScreenRect = tileScreenRect.Intersect(currentTileRect);
+ tileScreenRect.MoveBy(origin);
+
+ if (tileScreenRect.IsEmpty())
+ continue;
+
+ tileRegionRect = regionRect.Intersect(currentTileRect);
+ tileRegionRect.MoveBy(-currentTileRect.TopLeft());
+ }
+ gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
+ tileScreenRect.width, tileScreenRect.height);
+
+ effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
+ Float(tileRegionRect.y) / texRect.height,
+ Float(tileRegionRect.width) / texRect.width,
+ Float(tileRegionRect.height) / texRect.height);
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ if (usingTiles) {
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+ }
+ }
+
+ if (iterOnWhite) {
+ iterOnWhite->NextTile();
+ }
+ } while (usingTiles && bigImgIter->NextTile());
+
+ if (bigImgIter) {
+ bigImgIter->EndBigImageIteration();
+ }
+ if (iterOnWhite) {
+ iterOnWhite->EndBigImageIteration();
+ }
+
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
+ aTransform, mFlashCounter);
+}
+
+void
+ContentHostTexture::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ ContentHostBase::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() == 1);
+ const TimedTexture& t = aTextures[0];
+ MOZ_ASSERT(t.mPictureRect.IsEqualInterior(
+ nsIntRect(nsIntPoint(0, 0), nsIntSize(t.mTexture->GetSize()))),
+ "Only default picture rect supported");
+
+ if (t.mTexture != mTextureHost) {
+ mReceivedNewHost = true;
+ }
+
+ mTextureHost = t.mTexture;
+ mTextureHostOnWhite = nullptr;
+ mTextureSourceOnWhite = nullptr;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+}
+
+void
+ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite)
+{
+ ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite);
+ mTextureHost = aTextureOnBlack;
+ mTextureHostOnWhite = aTextureOnWhite;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->PrepareTextureSource(mTextureSourceOnWhite);
+ }
+}
+
+void
+ContentHostTexture::SetCompositor(Compositor* aCompositor)
+{
+ ContentHostBase::SetCompositor(aCompositor);
+ if (mTextureHost) {
+ mTextureHost->SetCompositor(aCompositor);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+}
+
+void
+ContentHostTexture::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+#ifdef MOZ_DUMP_PAINTING
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+ if (mTextureHost) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer: ";
+ }
+ DumpTextureHost(aStream, mTextureHost);
+ if (aDumpHtml) {
+ aStream << "> Front buffer </a></li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (mTextureHostOnWhite) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer on white: ";
+ }
+ DumpTextureHost(aStream, mTextureHostOnWhite);
+ if (aDumpHtml) {
+ aStream << "> Front buffer on white </a> </li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (aDumpHtml) {
+ aStream << "</ul>";
+ }
+#endif
+}
+
+static inline void
+AddWrappedRegion(const nsIntRegion& aInput, nsIntRegion& aOutput,
+ const IntSize& aSize, const nsIntPoint& aShift)
+{
+ nsIntRegion tempRegion;
+ tempRegion.And(IntRect(aShift, aSize), aInput);
+ tempRegion.MoveBy(-aShift);
+ aOutput.Or(aOutput, tempRegion);
+}
+
+bool
+ContentHostSingleBuffered::UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ aUpdatedRegionBack->SetEmpty();
+
+ if (!mTextureHost) {
+ mInitialised = false;
+ return true; // FIXME should we return false? Returning true for now
+ } // to preserve existing behavior of NOT causing IPC errors.
+
+ // updated is in screen coordinates. Convert it to buffer coordinates.
+ nsIntRegion destRegion(aUpdated);
+
+ if (mReceivedNewHost) {
+ destRegion.Or(destRegion, aOldValidRegionBack);
+ mReceivedNewHost = false;
+ }
+ destRegion.MoveBy(-aData.rect().TopLeft());
+
+ if (!aData.rect().Contains(aUpdated.GetBounds()) ||
+ aData.rotation().x > aData.rect().width ||
+ aData.rotation().y > aData.rect().height) {
+ NS_ERROR("Invalid update data");
+ return false;
+ }
+
+ // destRegion is now in logical coordinates relative to the buffer, but we
+ // need to account for rotation. We do that by moving the region to the
+ // rotation offset and then wrapping any pixels that extend off the
+ // bottom/right edges.
+
+ // Shift to the rotation point
+ destRegion.MoveBy(aData.rotation());
+
+ IntSize bufferSize = aData.rect().Size();
+
+ // Select only the pixels that are still within the buffer.
+ nsIntRegion finalRegion;
+ finalRegion.And(IntRect(IntPoint(), bufferSize), destRegion);
+
+ // For each of the overlap areas (right, bottom-right, bottom), select those
+ // pixels and wrap them around to the opposite edge of the buffer rect.
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, 0));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, aData.rect().height));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(0, aData.rect().height));
+
+ MOZ_ASSERT(IntRect(0, 0, aData.rect().width, aData.rect().height).Contains(finalRegion.GetBounds()));
+
+ mTextureHost->Updated(&finalRegion);
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated(&finalRegion);
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ return true;
+}
+
+bool
+ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ if (!mTextureHost) {
+ mInitialised = false;
+
+ *aUpdatedRegionBack = aUpdated;
+ return true;
+ }
+
+ // We don't need to calculate an update region because we assume that if we
+ // are using double buffering then we have render-to-texture and thus no
+ // upload to do.
+ mTextureHost->Updated();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated();
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ *aUpdatedRegionBack = aUpdated;
+
+ // Save the current valid region of our front buffer, because if
+ // we're double buffering, it's going to be the valid region for the
+ // next back buffer sent back to the renderer.
+ //
+ // NB: we rely here on the fact that mValidRegion is initialized to
+ // empty, and that the first time Swap() is called we don't have a
+ // valid front buffer that we're going to return to content.
+ mValidRegionForNextBackBuffer = aOldValidRegionBack;
+
+ return true;
+}
+
+void
+ContentHostTexture::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentHost (0x%p)", this).get();
+
+ AppendToString(aStream, mBufferRect, " [buffer-rect=", "]");
+ AppendToString(aStream, mBufferRotation, " [buffer-rotation=", "]");
+ if (PaintWillResample()) {
+ aStream << " [paint-will-resample]";
+ }
+
+ if (mTextureHost) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n";
+ mTextureHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+
+LayerRenderState
+ContentHostTexture::GetRenderState()
+{
+ if (!mTextureHost) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState result = mTextureHost->GetRenderState();
+
+ if (mBufferRotation != nsIntPoint()) {
+ result.mFlags |= LayerRenderStateFlags::BUFFER_ROTATION;
+ }
+ result.SetOffset(GetOriginOffset());
+ return result;
+}
+
+already_AddRefed<TexturedEffect>
+ContentHostTexture::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return nullptr;
+ }
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return nullptr;
+ }
+ return CreateTexturedEffect(mTextureSource.get(),
+ mTextureSourceOnWhite.get(),
+ aSamplingFilter, true,
+ GetRenderState());
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+ContentHostTexture::GetAsSurface()
+{
+ if (!mTextureHost) {
+ return nullptr;
+ }
+
+ return mTextureHost->GetAsSurface();
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/ContentHost.h b/system/graphics/layers/composite/ContentHost.h
new file mode 100644
index 000000000..9b7498415
--- /dev/null
+++ b/system/graphics/layers/composite/ContentHost.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CONTENTHOST_H
+#define GFX_CONTENTHOST_H
+
+#include <stdint.h> // for uint32_t
+#include <stdio.h> // for FILE
+#include "mozilla-config.h" // for MOZ_DUMP_PAINTING
+#include "CompositableHost.h" // for CompositableHost, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+class Compositor;
+class ThebesBufferData;
+struct EffectChain;
+
+struct TexturedEffect;
+
+/**
+ * ContentHosts are used for compositing Painted layers, always matched by a
+ * ContentClient of the same type.
+ *
+ * ContentHosts support only UpdateThebes(), not Update().
+ */
+class ContentHost : public CompositableHost
+{
+public:
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack) = 0;
+
+ virtual void SetPaintWillResample(bool aResample) { mPaintWillResample = aResample; }
+ bool PaintWillResample() { return mPaintWillResample; }
+
+ // We use this to allow TiledContentHost to invalidate regions where
+ // tiles are fading in.
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) { }
+
+protected:
+ explicit ContentHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo)
+ , mPaintWillResample(false)
+ {}
+
+ bool mPaintWillResample;
+};
+
+/**
+ * Base class for non-tiled ContentHosts.
+ *
+ * Ownership of the SurfaceDescriptor and the resources it represents is passed
+ * from the ContentClient to the ContentHost when the TextureClient/Hosts are
+ * created, that is recevied here by SetTextureHosts which assigns one or two
+ * texture hosts (for single and double buffering) to the ContentHost.
+ *
+ * It is the responsibility of the ContentHost to destroy its resources when
+ * they are recreated or the ContentHost dies.
+ */
+class ContentHostBase : public ContentHost
+{
+public:
+ typedef RotatedContentBuffer::ContentType ContentType;
+ typedef RotatedContentBuffer::PaintState PaintState;
+
+ explicit ContentHostBase(const TextureInfo& aTextureInfo);
+ virtual ~ContentHostBase();
+
+protected:
+ virtual nsIntPoint GetOriginOffset()
+ {
+ return mBufferRect.TopLeft() - mBufferRotation;
+ }
+
+
+ gfx::IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+ bool mInitialised;
+};
+
+/**
+ * Shared ContentHostBase implementation for content hosts that
+ * use up to two TextureHosts.
+ */
+class ContentHostTexture : public ContentHostBase
+{
+public:
+ explicit ContentHostTexture(const TextureInfo& aTextureInfo)
+ : ContentHostBase(aTextureInfo)
+ , mLocked(false)
+ , mReceivedNewHost(false)
+ { }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite) override;
+
+ virtual bool Lock() override {
+ MOZ_ASSERT(!mLocked);
+ if (!mTextureHost) {
+ return false;
+ }
+ if (!mTextureHost->Lock()) {
+ return false;
+ }
+
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->Lock()) {
+ return false;
+ }
+
+ mLocked = true;
+ return true;
+ }
+ virtual void Unlock() override {
+ MOZ_ASSERT(mLocked);
+ mTextureHost->Unlock();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Unlock();
+ }
+ mLocked = false;
+ }
+
+ LayerRenderState GetRenderState() override;
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+protected:
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ CompositableTextureSourceRef mTextureSource;
+ CompositableTextureSourceRef mTextureSourceOnWhite;
+ bool mLocked;
+ bool mReceivedNewHost;
+};
+
+/**
+ * Double buffering is implemented by swapping the front and back TextureHosts.
+ * We assume that whenever we use double buffering, then we have
+ * render-to-texture and thus no texture upload to do.
+ */
+class ContentHostDoubleBuffered : public ContentHostTexture
+{
+public:
+ explicit ContentHostDoubleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo)
+ {}
+
+ virtual ~ContentHostDoubleBuffered() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_DOUBLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+
+protected:
+ nsIntRegion mValidRegionForNextBackBuffer;
+};
+
+/**
+ * Single buffered, therefore we must synchronously upload the image from the
+ * TextureHost in the layers transaction (i.e., in UpdateThebes).
+ */
+class ContentHostSingleBuffered : public ContentHostTexture
+{
+public:
+ explicit ContentHostSingleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo)
+ {}
+ virtual ~ContentHostSingleBuffered() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_SINGLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/FPSCounter.cpp b/system/graphics/layers/composite/FPSCounter.cpp
new file mode 100644
index 000000000..b8e93eb97
--- /dev/null
+++ b/system/graphics/layers/composite/FPSCounter.cpp
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stddef.h> // for size_t
+#include "Units.h" // for ScreenIntRect
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsIFile.h" // for nsIFile
+#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
+#include "mozilla/Sprintf.h"
+#include "FPSCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+FPSCounter::FPSCounter(const char* aName)
+ : mWriteIndex(0)
+ , mIteratorIndex(-1)
+ , mFPSName(aName)
+{
+ Init();
+}
+
+FPSCounter::~FPSCounter() { }
+
+void
+FPSCounter::Init()
+{
+ for (int i = 0; i < kMaxFrames; i++) {
+ mFrameTimestamps.AppendElement(TimeStamp());
+ }
+ mLastInterval = TimeStamp::Now();
+}
+
+// Returns true if we captured a full interval of data
+bool
+FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
+ TimeDuration duration = aTimestamp - mLastInterval;
+ return duration.ToSeconds() >= kFpsDumpInterval;
+}
+
+void
+FPSCounter::AddFrame(TimeStamp aTimestamp) {
+ NS_ASSERTION(mWriteIndex < kMaxFrames, "We probably have a bug with the circular buffer");
+ NS_ASSERTION(mWriteIndex >= 0, "Circular Buffer index should never be negative");
+
+ int index = mWriteIndex++;
+ if (mWriteIndex == kMaxFrames) {
+ mWriteIndex = 0;
+ }
+
+ mFrameTimestamps[index] = aTimestamp;
+
+ if (CapturedFullInterval(aTimestamp)) {
+ PrintFPS();
+ WriteFrameTimeStamps();
+ mLastInterval = aTimestamp;
+ }
+}
+
+double
+FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
+ AddFrame(aTimestamp);
+ return GetFPS(aTimestamp);
+}
+
+int
+FPSCounter::GetLatestReadIndex()
+{
+ if (mWriteIndex == 0) {
+ return kMaxFrames - 1;
+ }
+
+ return mWriteIndex - 1;
+}
+
+TimeStamp
+FPSCounter::GetLatestTimeStamp()
+{
+ TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
+ MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
+ return timestamp;
+}
+
+// Returns true if we iterated over a full interval of data
+bool
+FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
+ MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
+ MOZ_ASSERT(mIteratorIndex < kMaxFrames, "Iterator index cannot be greater than kMaxFrames");
+
+ TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
+ TimeDuration duration = aTimestamp - currentStamp;
+ return duration.ToSeconds() >= aDuration;
+}
+
+void
+FPSCounter::ResetReverseIterator()
+{
+ mIteratorIndex = GetLatestReadIndex();
+}
+
+/***
+ * Returns true if we have another timestamp that is valid and
+ * is within the given duration that we're interested in.
+ * Duration is in seconds
+ */
+bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration)
+{
+ // Order of evaluation here has to stay the same
+ // otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
+ // be null
+ return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
+ && !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
+ && !IteratedFullInterval(aTimestamp, aDuration);
+}
+
+TimeStamp
+FPSCounter::GetNextTimeStamp()
+{
+ TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
+ MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
+
+ if (mIteratorIndex == -1) {
+ mIteratorIndex = kMaxFrames - 1;
+ }
+ return timestamp;
+}
+
+/**
+ * GetFPS calculates how many frames we've already composited from the current
+ * frame timestamp and we iterate from the latest timestamp we recorded,
+ * going back in time. When we hit a frame that is longer than the 1 second
+ * from the current composited frame, we return how many frames we've counted.
+ * Just a visualization:
+ *
+ * aTimestamp
+ * Frames: 1 2 3 4 5 6 7 8 9 10 11 12
+ * Time -------------------------->
+ *
+ * GetFPS iterates from aTimestamp, which is the current frame.
+ * Then starting at frame 12, going back to frame 11, 10, etc, we calculate
+ * the duration of the recorded frame timestamp from aTimestamp.
+ * Once duration is greater than 1 second, we return how many frames
+ * we composited.
+ */
+double
+FPSCounter::GetFPS(TimeStamp aTimestamp)
+{
+ int frameCount = 0;
+ int duration = 1.0; // Only care about the last 1s of data
+
+ ResetReverseIterator();
+ while (HasNext(aTimestamp, duration)) {
+ GetNextTimeStamp();
+ frameCount++;
+ }
+
+ return frameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+int
+FPSCounter::BuildHistogram(std::map<int, int>& aFpsData)
+{
+ TimeStamp currentIntervalStart = GetLatestTimeStamp();
+ TimeStamp currentTimeStamp = GetLatestTimeStamp();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ int frameCount = 0;
+ int totalFrameCount = 0;
+
+ ResetReverseIterator();
+ while (HasNext(startTimeStamp)) {
+ currentTimeStamp = GetNextTimeStamp();
+ TimeDuration interval = currentIntervalStart - currentTimeStamp;
+
+ if (interval.ToSeconds() >= 1.0 ) {
+ currentIntervalStart = currentTimeStamp;
+ aFpsData[frameCount]++;
+ frameCount = 0;
+ }
+
+ frameCount++;
+ totalFrameCount++;
+ }
+
+ TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
+ printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
+ frameCount, totalTime.ToMilliseconds(), mFPSName);
+ return totalFrameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+void
+FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd)
+{
+ const int bufferSize = 256;
+ char buffer[bufferSize];
+ int writtenCount = SprintfLiteral(buffer, "FPS Data for: %s\n", mFPSName);
+ MOZ_ASSERT(writtenCount < bufferSize);
+ if (writtenCount >= bufferSize) {
+ return;
+ }
+ PR_Write(fd, buffer, writtenCount);
+
+ ResetReverseIterator();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp previousSample = GetNextTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp nextTimeStamp = GetNextTimeStamp();
+
+ while (HasNext(startTimeStamp)) {
+ TimeDuration duration = previousSample - nextTimeStamp;
+ writtenCount = SprintfLiteral(buffer, "%f,\n", duration.ToMilliseconds());
+ MOZ_ASSERT(writtenCount < bufferSize);
+ if (writtenCount >= bufferSize) {
+ continue;
+ }
+ PR_Write(fd, buffer, writtenCount);
+
+ previousSample = nextTimeStamp;
+ nextTimeStamp = GetNextTimeStamp();
+ }
+}
+
+double
+FPSCounter::GetMean(std::map<int, int> aHistogram)
+{
+ double average = 0.0;
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ average += fps * count;
+ samples += count;
+ }
+
+ return average / samples;
+}
+
+double
+FPSCounter::GetStdDev(std::map<int, int> aHistogram)
+{
+ double sumOfDifferences = 0;
+ double average = GetMean(aHistogram);
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ double diff = ((double) fps) - average;
+ diff *= diff;
+
+ for (int i = 0; i < count; i++) {
+ sumOfDifferences += diff;
+ }
+ samples += count;
+ }
+
+ double stdDev = sumOfDifferences / samples;
+ return sqrt(stdDev);
+}
+
+void
+FPSCounter::PrintFPS()
+{
+ if (!gfxPrefs::FPSPrintHistogram()) {
+ return;
+ }
+
+ std::map<int, int> histogram;
+ int totalFrames = BuildHistogram(histogram);
+
+ TimeDuration measurementInterval = mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
+ printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
+ mFPSName, totalFrames, measurementInterval.ToSecondsSigDigits());
+
+ PrintHistogram(histogram);
+}
+
+void
+FPSCounter::PrintHistogram(std::map<int, int>& aHistogram)
+{
+ if (aHistogram.size() == 0) {
+ return;
+ }
+
+ int length = 0;
+ const int kBufferLength = 512;
+ int availableSpace = kBufferLength;
+ char buffer[kBufferLength];
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); iter++)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ int lengthRequired = snprintf(buffer + length, availableSpace,
+ "FPS: %d = %d. ", fps, count);
+ // Ran out of buffer space. Oh well - just print what we have.
+ if (lengthRequired > availableSpace) {
+ break;
+ }
+ length += lengthRequired;
+ availableSpace -= lengthRequired;
+ }
+
+ printf_stderr("%s\n", buffer);
+ printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram), GetStdDev(aHistogram));
+}
+
+// Write FPS timestamp data to a file only if
+// draw-fps.write-to-file is true
+nsresult
+FPSCounter::WriteFrameTimeStamps()
+{
+ if (!gfxPrefs::WriteFPSToFile()) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mWriteIndex == 0);
+
+ nsCOMPtr<nsIFile> resultFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
+ resultFile->Append(NS_LITERAL_STRING("fps.txt"));
+ } else {
+ resultFile->Append(NS_LITERAL_STRING("txn.txt"));
+ }
+
+ PRFileDesc* fd = nullptr;
+ int mode = 644;
+ int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
+ rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ WriteFrameTimeStamps(fd);
+ PR_Close(fd);
+
+ nsAutoCString path;
+ rv = resultFile->GetNativePath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf_stderr("Wrote FPS data to file: %s\n", path.get());
+ return NS_OK;
+}
+
+FPSState::FPSState()
+ : mCompositionFps("Compositor")
+ , mTransactionFps("LayerTransactions")
+{
+}
+
+// Size of the builtin font.
+static const float FontHeight = 7.f;
+static const float FontWidth = 4.f;
+
+// Scale the font when drawing it to the viewport for better readability.
+static const float FontScaleX = 2.f;
+static const float FontScaleY = 3.f;
+
+static void DrawDigits(unsigned int aValue,
+ int aOffsetX, int aOffsetY,
+ Compositor* aCompositor,
+ EffectChain& aEffectChain)
+{
+ if (aValue > 999) {
+ aValue = 999;
+ }
+
+ unsigned int divisor = 100;
+ float textureWidth = FontWidth * 10;
+ gfx::Float opacity = 1;
+ gfx::Matrix4x4 transform;
+ transform.PreScale(FontScaleX, FontScaleY, 1);
+
+ for (size_t n = 0; n < 3; ++n) {
+ unsigned int digit = aValue % (divisor * 10) / divisor;
+ divisor /= 10;
+
+ RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
+
+ Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
+ IntRect clipRect = IntRect(0, 0, 300, 100);
+ aCompositor->DrawQuad(drawRect, clipRect, aEffectChain, opacity, transform);
+ }
+}
+
+void FPSState::DrawFPS(TimeStamp aNow,
+ int aOffsetX, int aOffsetY,
+ unsigned int aFillRatio,
+ Compositor* aCompositor)
+{
+ if (!mFPSTextureSource) {
+ const char *text =
+ " "
+ " XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
+ " X X X X X X X X X X X X X X"
+ " X X X XXX XXX XXX XXX XXX X XXX XXX"
+ " X X X X X X X X X X X X X"
+ " XXX XXX XXX XXX X XXX XXX X XXX X"
+ " ";
+
+ // Convert the text encoding above to RGBA.
+ int w = FontWidth * 10;
+ int h = FontHeight;
+ uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ uint32_t purple = 0xfff000ff;
+ uint32_t white = 0xffffffff;
+ buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
+ }
+ }
+
+ int bytesPerPixel = 4;
+ RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mFPSTextureSource = aCompositor->CreateDataTextureSource();
+ mFPSTextureSource->Update(fpsSurface);
+ }
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8,
+ mFPSTextureSource,
+ SamplingFilter::POINT,
+ true);
+
+ unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
+ unsigned int txnFps = unsigned(mTransactionFps.GetFPS(aNow));
+
+ DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
+ DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
+ DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/system/graphics/layers/composite/FPSCounter.h b/system/graphics/layers/composite/FPSCounter.h
new file mode 100644
index 000000000..7a59267fc
--- /dev/null
+++ b/system/graphics/layers/composite/FPSCounter.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_opengl_FPSCounter_h_
+#define mozilla_layers_opengl_FPSCounter_h_
+
+#include <algorithm> // for min
+#include <stddef.h> // for size_t
+#include <map> // for std::map
+#include "GLDefs.h" // for GLuint
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsTArray.h" // for AutoTArray, nsTArray_Impl, etc
+#include "prio.h" // for NSPR file i/o
+
+namespace mozilla {
+namespace layers {
+
+class DataTextureSource;
+class Compositor;
+
+// Dump the FPS histogram every 10 seconds or kMaxFrameFPS
+const int kFpsDumpInterval = 10;
+
+// On desktop, we can have 240 hz monitors, so 10 seconds
+// times 240 frames = 2400
+const int kMaxFrames = 2400;
+
+/**
+ * The FPSCounter tracks how often we composite or have a layer transaction.
+ * At each composite / layer transaction, we record the timestamp.
+ * After kFpsDumpInterval number of composites / transactions, we calculate
+ * the average and standard deviation of frames composited. We dump a histogram,
+ * which allows for more statistically significant measurements. We also dump
+ * absolute frame composite times to a file on the device.
+ * The FPS counters displayed on screen are based on how many frames we
+ * composited within the last ~1 second. The more accurate measurement is to
+ * grab the histogram from stderr or grab the FPS timestamp dumps written to file.
+ *
+ * To enable dumping to file, enable
+ * layers.acceleration.draw-fps.write-to-file pref.
+
+ double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
+ AddFrame(aCurrentFrame);
+ return EstimateFps(aCurrentFrame);
+ }
+ * To enable printing histogram data to logcat,
+ * enable layers.acceleration.draw-fps.print-histogram
+ *
+ * Use the HasNext(), GetNextTimeStamp() like an iterator to read the data,
+ * backwards in time. This abstracts away the mechanics of reading the data.
+ */
+class FPSCounter {
+public:
+ explicit FPSCounter(const char* aName);
+ ~FPSCounter();
+
+ void AddFrame(TimeStamp aTimestamp);
+ double AddFrameAndGetFps(TimeStamp aTimestamp);
+ double GetFPS(TimeStamp aTimestamp);
+
+private:
+ void Init();
+ bool CapturedFullInterval(TimeStamp aTimestamp);
+
+ // Used while iterating backwards over the data
+ void ResetReverseIterator();
+ bool HasNext(TimeStamp aTimestamp, double aDuration = kFpsDumpInterval);
+ TimeStamp GetNextTimeStamp();
+ int GetLatestReadIndex();
+ TimeStamp GetLatestTimeStamp();
+ void WriteFrameTimeStamps(PRFileDesc* fd);
+ bool IteratedFullInterval(TimeStamp aTimestamp, double aDuration);
+
+ void PrintFPS();
+ int BuildHistogram(std::map<int, int>& aHistogram);
+ void PrintHistogram(std::map<int, int>& aHistogram);
+ double GetMean(std::map<int,int> aHistogram);
+ double GetStdDev(std::map<int, int> aHistogram);
+ nsresult WriteFrameTimeStamps();
+
+ /***
+ * mFrameTimestamps is a psuedo circular buffer
+ * Since we have a constant write time and don't
+ * read at an offset except our latest write
+ * we don't need an explicit read pointer.
+ */
+ AutoTArray<TimeStamp, kMaxFrames> mFrameTimestamps;
+ int mWriteIndex; // points to next open write slot
+ int mIteratorIndex; // used only when iterating
+ const char* mFPSName;
+ TimeStamp mLastInterval;
+};
+
+struct FPSState {
+ FPSState();
+ void DrawFPS(TimeStamp, int offsetX, int offsetY, unsigned, Compositor* aCompositor);
+ void NotifyShadowTreeTransaction() {
+ mTransactionFps.AddFrame(TimeStamp::Now());
+ }
+
+ FPSCounter mCompositionFps;
+ FPSCounter mTransactionFps;
+
+private:
+ RefPtr<DataTextureSource> mFPSTextureSource;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_FPSCounter_h_
diff --git a/system/graphics/layers/composite/FontData.h b/system/graphics/layers/composite/FontData.h
new file mode 100644
index 000000000..f8c73cc99
--- /dev/null
+++ b/system/graphics/layers/composite/FontData.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is explicitly not guarded as we want only 1 file to include this and
+// it's good if things break if someone else does.
+const unsigned char sFontPNG[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0xb, 0xfe, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0xd1, 0xb6, 0xe3, 0x20, 0x8, 0x74, 0x72, 0xf2, 0xff, 0xbf, 0x3c, 0xfb, 0xd0, 0xbb, 0xbd, 0x51, 0x8, 0x48, 0xd4, 0xc4, 0xde, 0xda, 0xb3, 0x67, 0x77, 0x6d, 0x13, 0x63, 0x10, 0x81, 0x41, 0x40, 0x30, 0x7d, 0xf7, 0x67, 0x4b, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x2f, 0xdc, 0x3, 0xc6, 0xda, 0x76, 0x67, 0x29, 0xa5, 0x94, 0xb8, 0x38, 0x20, 0x42, 0x33, 0xc0, 0x68, 0xaa, 0x37, 0x64, 0x14, 0x47, 0xc2, 0x67, 0x13, 0x80, 0x5, 0xbf, 0x91, 0xb0, 0x5f, 0x9f, 0x73, 0x71, 0x40, 0x39, 0x61, 0x1c, 0xcd, 0xff, 0x39, 0x7d, 0x8, 0x1e, 0x29, 0x72, 0x3f, 0x1, 0x90, 0x12, 0x8a, 0xf9, 0xc, 0x52, 0x20, 0x9f, 0x51, 0x2, 0x8e, 0xd0, 0x99, 0x8d, 0x3, 0x7a, 0x2f, 0x2, 0x7a, 0x4, 0xa4, 0x90, 0xba, 0x8, 0x68, 0x1, 0x28, 0x32, 0x1a, 0x87, 0x6f, 0x50, 0x71, 0x7f, 0x5f, 0x29, 0xf, 0x85, 0xa3, 0x18, 0x5b, 0x10, 0xc, 0x10, 0x80, 0xf2, 0x1d, 0x79, 0x4e, 0x5e, 0x8f, 0xfc, 0xaf, 0x1, 0xb0, 0x60, 0x50, 0x4, 0x17, 0x40, 0xc6, 0xd3, 0xf4, 0xd5, 0x2e, 0x3a, 0xdb, 0x1, 0x5d, 0x85, 0x78, 0x8a, 0xbd, 0x7e, 0x33, 0xc3, 0x24, 0xe4, 0x52, 0x70, 0xff, 0xbc, 0xf5, 0x8f, 0xf0, 0x82, 0xe2, 0x5c, 0x1c, 0xd0, 0x9b, 0x83, 0x1c, 0x7a, 0x50, 0xb4, 0x33, 0xa9, 0xf9, 0xf5, 0x58, 0x0, 0xb, 0xe, 0x2f, 0x2, 0x2c, 0x2, 0x7c, 0x3d, 0x1, 0xea, 0x8c, 0xb9, 0xc, 0x4f, 0x16, 0x70, 0x14, 0x21, 0xc5, 0x8e, 0x60, 0xfb, 0x1, 0xe, 0x80, 0xf5, 0xc2, 0x52, 0x8c, 0xb6, 0x9, 0x52, 0xdc, 0xfb, 0xc6, 0x35, 0x4, 0x40, 0xa, 0x81, 0x8d, 0x66, 0xbd, 0xce, 0xc9, 0x8, 0x80, 0x6f, 0x93, 0x1, 0x6d, 0xef, 0xcf, 0xf1, 0xf4, 0xc2, 0xc0, 0xf9, 0xd9, 0xdb, 0xe7, 0x7f, 0x30, 0x5, 0x50, 0x98, 0x6b, 0x48, 0xe8, 0x9, 0xaf, 0x77, 0x14, 0xf8, 0xbe, 0xf5, 0x7d, 0xa0, 0xfa, 0xb, 0x18, 0xa2, 0x6f, 0x3e, 0x9e, 0xc2, 0x5c, 0x65, 0x5f, 0xe3, 0x75, 0xa7, 0x30, 0x87, 0x3d, 0xa, 0xd8, 0x42, 0x8b, 0x8a, 0x3f, 0x81, 0xb6, 0x7f, 0xa1, 0xa6, 0x8f, 0x80, 0x7, 0xa2, 0x5d, 0x8, 0xd2, 0xd1, 0xfb, 0x7f, 0x4a, 0x4a, 0x6e, 0xfe, 0xc, 0x3c, 0xab, 0xa6, 0x9e, 0x31, 0x84, 0x4c, 0x9e, 0xa3, 0xfb, 0x45, 0x47, 0x7f, 0xc5, 0xc2, 0x2, 0xcb, 0x1f, 0xb0, 0x38, 0x60, 0x11, 0x60, 0x7a, 0x2, 0xe0, 0x6f, 0x11, 0x0, 0x1e, 0x5a, 0x85, 0x3, 0x97, 0x5f, 0xde, 0x1, 0x94, 0x5f, 0x9c, 0x93, 0x2b, 0xf2, 0x73, 0x77, 0x68, 0xd1, 0xce, 0x1, 0x60, 0x6e, 0x3b, 0x82, 0x44, 0x26, 0x5c, 0xe1, 0x6d, 0x5f, 0xd3, 0xb2, 0x43, 0x91, 0xb4, 0xcd, 0xb0, 0x27, 0x97, 0x0, 0x12, 0x4, 0x53, 0xc, 0x54, 0x25, 0x44, 0x6f, 0x4d, 0x95, 0x77, 0xe7, 0x13, 0x80, 0xd2, 0x30, 0xf4, 0x2d, 0x1f, 0xf0, 0xb4, 0x45, 0x47, 0x11, 0x13, 0xb7, 0xda, 0x49, 0x7b, 0xf7, 0x29, 0x22, 0x12, 0xc1, 0x23, 0xa2, 0x3, 0x1b, 0xec, 0xd, 0x30, 0xc7, 0x7f, 0x3f, 0x4b, 0x82, 0x9d, 0x18, 0xe0, 0x2, 0x1, 0xca, 0xdd, 0xd9, 0x32, 0xe4, 0xe2, 0x35, 0xb6, 0xdf, 0xab, 0xd0, 0x86, 0xaf, 0x59, 0xf0, 0x80, 0x4b, 0xcc, 0xe0, 0xd6, 0xe1, 0x7e, 0x41, 0x4, 0xb8, 0xf, 0x11, 0xf8, 0x9a, 0x37, 0x1a, 0x9c, 0xc1, 0x27, 0x6d, 0xd1, 0xe, 0x5e, 0x22, 0x80, 0x21, 0x99, 0xcc, 0x89, 0xc, 0xee, 0x72, 0x28, 0xbe, 0x1d, 0x10, 0x17, 0xd3, 0x60, 0x4a, 0x7c, 0x7, 0xee, 0x10, 0x0, 0x81, 0x71, 0x73, 0x1c, 0xdd, 0x86, 0xe8, 0xf, 0x86, 0x1a, 0xfb, 0x50, 0xc8, 0x77, 0xe3, 0xe3, 0xb7, 0xfb, 0x57, 0xdd, 0x5c, 0x78, 0x76, 0x86, 0xd5, 0x99, 0x8d, 0xe1, 0xee, 0x1, 0x2d, 0x7f, 0xc0, 0x82, 0xc3, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x15, 0xcb, 0x1a, 0x22, 0xb6, 0x94, 0x56, 0xbb, 0x94, 0x62, 0xa5, 0x62, 0x80, 0x68, 0x9a, 0xbf, 0xf7, 0xdd, 0xf9, 0xd2, 0xed, 0x82, 0x2c, 0x52, 0x54, 0x40, 0x15, 0xc8, 0xb7, 0xcb, 0xd0, 0x88, 0xb0, 0xc6, 0x6d, 0x6c, 0x5c, 0x5e, 0xcf, 0xe2, 0xe, 0xbf, 0xff, 0x36, 0x59, 0xe, 0x7b, 0x3c, 0x9b, 0x7b, 0x3, 0x3b, 0x68, 0xfd, 0x96, 0x94, 0x92, 0x8a, 0x0, 0x12, 0x33, 0x5c, 0xbc, 0xb4, 0x42, 0xe9, 0x81, 0xa1, 0xf0, 0xb, 0x96, 0x70, 0x4f, 0x2e, 0xa9, 0xd4, 0x64, 0xeb, 0x79, 0x33, 0x80, 0xa4, 0xf0, 0x63, 0xc6, 0x72, 0x16, 0x76, 0xdb, 0x2f, 0x30, 0x98, 0x83, 0x5, 0x29, 0x4d, 0x1b, 0x7b, 0x86, 0xc2, 0xd6, 0x7d, 0x88, 0x63, 0x93, 0x8c, 0xa7, 0x6, 0x63, 0x4, 0x90, 0x8f, 0x28, 0xd7, 0xac, 0x39, 0x0, 0xfa, 0xbd, 0x83, 0x85, 0x43, 0x25, 0x6, 0xc6, 0xfc, 0x68, 0xf1, 0x73, 0x7, 0xd5, 0xde, 0x24, 0x60, 0xfe, 0xcf, 0x57, 0xc8, 0x9, 0x1, 0xc6, 0x58, 0xde, 0x5f, 0x22, 0xd9, 0xe3, 0xc5, 0x12, 0x40, 0x63, 0xb0, 0xb4, 0x97, 0x14, 0xc7, 0xce, 0xc6, 0xfc, 0x85, 0xfb, 0x33, 0x96, 0xa4, 0xdb, 0x9f, 0x92, 0xae, 0x60, 0xe9, 0x69, 0x57, 0x6f, 0x2b, 0x5a, 0xd1, 0x6e, 0x33, 0xa5, 0xa6, 0xeb, 0x5b, 0xad, 0x84, 0x47, 0xc1, 0xd7, 0x74, 0xf8, 0x6d, 0xfb, 0xf2, 0xf7, 0x5f, 0x70, 0x78, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0xaf, 0xf7, 0x7, 0x28, 0xd1, 0x9c, 0xb4, 0xc0, 0x45, 0x91, 0x4c, 0xaa, 0xda, 0xd, 0x87, 0x6f, 0xdc, 0xfb, 0x99, 0x3f, 0x12, 0x34, 0xf1, 0xbb, 0x7b, 0xfd, 0xff, 0x76, 0xfe, 0x6f, 0xb9, 0xbd, 0xc8, 0x23, 0x1, 0xde, 0x9b, 0x78, 0xbf, 0xf0, 0x4e, 0xdb, 0xeb, 0xcb, 0x9e, 0x50, 0x98, 0x5e, 0x25, 0xe0, 0x37, 0xd1, 0x1b, 0x63, 0xda, 0x47, 0xcb, 0x2c, 0xb5, 0x6e, 0xff, 0x79, 0x63, 0xdb, 0x90, 0x7a, 0x7f, 0xb9, 0xfd, 0xfe, 0xbf, 0x12, 0x82, 0xbc, 0x2e, 0xf7, 0xf6, 0xb4, 0x83, 0xf7, 0x8f, 0x54, 0xde, 0x25, 0x5a, 0x67, 0xf6, 0xe5, 0xf6, 0x42, 0x63, 0xc7, 0x98, 0x87, 0x32, 0xe4, 0xe3, 0xd3, 0x6c, 0x21, 0x22, 0x42, 0xa2, 0xfd, 0x8d, 0x48, 0x6d, 0x8f, 0x4c, 0x4, 0xed, 0x29, 0x42, 0x41, 0x1, 0xfc, 0xbc, 0x84, 0xfe, 0xaf, 0xdc, 0x60, 0xee, 0xcd, 0xee, 0xa, 0x53, 0x6a, 0x3e, 0xbe, 0xd0, 0x9a, 0xad, 0xf0, 0x50, 0x1c, 0x1e, 0xc2, 0xe0, 0x4b, 0x81, 0x2e, 0xb, 0x50, 0x44, 0x30, 0xb8, 0x70, 0x18, 0x85, 0x2c, 0x9c, 0xd8, 0x44, 0x66, 0xd7, 0xfd, 0xf6, 0x2d, 0xfd, 0x6c, 0x60, 0x57, 0xf7, 0x49, 0x28, 0x33, 0x16, 0x61, 0x10, 0x84, 0x6e, 0x40, 0x8d, 0x60, 0xf3, 0x78, 0xfe, 0x78, 0x79, 0xf1, 0xfc, 0xfd, 0xcd, 0x84, 0xb9, 0xfa, 0x3c, 0xf4, 0x51, 0xae, 0x69, 0xc2, 0x5e, 0xe3, 0x5e, 0xcd, 0x8, 0x22, 0x22, 0x53, 0xa8, 0x6f, 0x13, 0x14, 0xe3, 0x63, 0x15, 0xd5, 0xb5, 0xe7, 0x1f, 0x63, 0x79, 0xbe, 0xd2, 0x12, 0xfc, 0x55, 0x0, 0x5f, 0x8a, 0x8b, 0x97, 0x3f, 0x60, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0x2f, 0xf7, 0x7, 0x54, 0xe1, 0xfd, 0xf3, 0x8d, 0x82, 0x12, 0xfe, 0x97, 0x96, 0xa4, 0x5, 0xcf, 0x7f, 0x77, 0xc5, 0x60, 0xf8, 0x7, 0xe4, 0xe3, 0x2d, 0xbf, 0x3e, 0x82, 0x7b, 0xd1, 0x7b, 0x1d, 0xde, 0x17, 0xe, 0x88, 0x77, 0xdb, 0xdb, 0xcd, 0x67, 0x79, 0x7d, 0xd1, 0x13, 0xe5, 0xde, 0x20, 0xa5, 0xbb, 0xc1, 0xc1, 0x16, 0x96, 0xf7, 0xc0, 0x89, 0xd3, 0x3d, 0x8b, 0xf, 0xf0, 0xf3, 0x67, 0xdb, 0x94, 0x2f, 0x8e, 0x86, 0x99, 0x48, 0x99, 0x8, 0xc4, 0x4e, 0xb6, 0x22, 0xf7, 0xf1, 0x32, 0x20, 0x6, 0xcf, 0x47, 0x18, 0x3a, 0x74, 0x6a, 0x89, 0x79, 0x78, 0xbf, 0xf8, 0x9d, 0x68, 0xe3, 0x80, 0x17, 0x58, 0x3d, 0x40, 0x56, 0x5, 0xba, 0x34, 0x6, 0xcf, 0xc6, 0x84, 0x60, 0x45, 0x3d, 0x38, 0xb0, 0xc5, 0x3f, 0x50, 0xb1, 0x1e, 0x22, 0xc1, 0xf7, 0x21, 0x77, 0x87, 0xcc, 0x37, 0xb8, 0xb4, 0x4, 0x1a, 0x53, 0x1e, 0x30, 0xb3, 0xc1, 0xbd, 0x55, 0xe1, 0xf5, 0x23, 0x5, 0xd0, 0xba, 0xe6, 0xf9, 0x2b, 0xfd, 0x6b, 0x16, 0x29, 0x38, 0x74, 0x7a, 0xf6, 0x3a, 0xbc, 0x7e, 0xe0, 0x33, 0x57, 0x6, 0x8, 0x4, 0x4f, 0xd7, 0x89, 0x6d, 0x5f, 0x31, 0x14, 0xaa, 0xea, 0x4f, 0x9e, 0x19, 0x23, 0x82, 0xad, 0x17, 0x54, 0xc8, 0x80, 0xbe, 0x19, 0x1e, 0x93, 0x9b, 0xc2, 0x3, 0xec, 0x9c, 0xc1, 0x2c, 0x60, 0x8e, 0xae, 0x32, 0x81, 0x86, 0xcb, 0x21, 0xb2, 0xd0, 0xe0, 0x22, 0xc0, 0xf, 0x1, 0xf0, 0xed, 0x4, 0xf0, 0xec, 0x88, 0xba, 0xd2, 0x4f, 0xd7, 0x3b, 0x40, 0xe7, 0x76, 0x7c, 0x9, 0xc, 0x16, 0x83, 0x51, 0x31, 0xdb, 0x23, 0x3c, 0xbf, 0x4d, 0x6, 0x88, 0x3, 0xb, 0x0, 0xf3, 0x77, 0x79, 0x79, 0x79, 0x9e, 0x81, 0xe8, 0xce, 0xea, 0xa0, 0x66, 0x86, 0x4d, 0xd3, 0x39, 0x1, 0xc7, 0x3e, 0x61, 0x8f, 0x7f, 0x57, 0x67, 0xcc, 0xa, 0x77, 0x57, 0x32, 0x3c, 0x60, 0xdc, 0x4f, 0x25, 0x40, 0x43, 0x44, 0x2c, 0x4, 0xb9, 0xc4, 0x45, 0x8f, 0x38, 0x42, 0x58, 0x7b, 0xfc, 0x15, 0xc9, 0xd3, 0x74, 0xd9, 0xb4, 0xad, 0xd4, 0x35, 0x3b, 0x2f, 0x2a, 0x25, 0xe1, 0x3e, 0x66, 0x9, 0xb6, 0xaa, 0x85, 0xd1, 0x6a, 0x65, 0x78, 0x41, 0x45, 0x36, 0x3e, 0x63, 0xf8, 0x89, 0x19, 0x5d, 0xcd, 0xd7, 0x8a, 0xa2, 0xaa, 0x70, 0x27, 0x1, 0x8d, 0x9a, 0xef, 0x82, 0x8b, 0x65, 0x20, 0x7, 0x10, 0x5, 0x8d, 0x8b, 0x9c, 0x94, 0x12, 0xee, 0x97, 0x29, 0x2e, 0x74, 0xf2, 0xfe, 0xa2, 0x29, 0x31, 0xe7, 0x3e, 0x95, 0xeb, 0xd4, 0xe3, 0xd3, 0xc8, 0x1f, 0x1c, 0x73, 0xed, 0x47, 0x60, 0x81, 0xc9, 0x4c, 0xef, 0x7, 0x38, 0x20, 0x96, 0x1a, 0xb, 0xfe, 0x39, 0x2, 0x2c, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x8, 0x70, 0xbb, 0x54, 0xb7, 0x4f, 0x33, 0x88, 0xfa, 0xb3, 0x3f, 0xef, 0x98, 0x1d, 0x17, 0xa, 0x79, 0xa5, 0xc6, 0x52, 0x51, 0x4e, 0x4f, 0xc1, 0xcb, 0x79, 0x81, 0x3, 0x17, 0xef, 0xcb, 0x19, 0xc1, 0x69, 0xf3, 0xdd, 0x86, 0x71, 0x7d, 0x51, 0x8f, 0x12, 0x23, 0xeb, 0x50, 0xed, 0x1a, 0x5e, 0x36, 0x2b, 0xe, 0x78, 0x78, 0xff, 0x82, 0x59, 0xb, 0xaf, 0x58, 0x39, 0xda, 0xd6, 0xa8, 0xc5, 0x15, 0xaa, 0x4b, 0x8c, 0x41, 0x2a, 0x6, 0x4d, 0x71, 0x58, 0xfb, 0xc5, 0xda, 0xde, 0x6a, 0x13, 0x8d, 0x41, 0x5a, 0x15, 0x26, 0xb6, 0xb, 0x6f, 0x0, 0x44, 0x5f, 0xb8, 0xd, 0xd9, 0x4, 0x93, 0x9d, 0x29, 0x18, 0xd8, 0x3a, 0xd3, 0xf2, 0x82, 0x16, 0xf0, 0xce, 0xf8, 0x8c, 0x4f, 0x11, 0x4c, 0x8a, 0xd9, 0x45, 0x57, 0x5b, 0x8d, 0xdb, 0x2d, 0x5c, 0xc8, 0x16, 0x35, 0xab, 0xce, 0x99, 0xf0, 0x88, 0xe0, 0xc0, 0xf0, 0x9a, 0xa2, 0x12, 0xef, 0x9b, 0x54, 0xd6, 0xfc, 0x1, 0xc9, 0xf4, 0xf, 0xb8, 0x27, 0x82, 0xd2, 0x3c, 0x42, 0x62, 0x6c, 0x7c, 0xc0, 0x9e, 0x3c, 0x17, 0xa7, 0xeb, 0x24, 0x55, 0xda, 0xf4, 0xae, 0xe7, 0x69, 0xb3, 0xa7, 0xc0, 0xad, 0x61, 0xcf, 0x9, 0xd, 0xa1, 0xb1, 0x3c, 0xcf, 0xd9, 0x2d, 0x41, 0xb7, 0x1e, 0xb, 0xbd, 0xf8, 0x0, 0x4e, 0x44, 0xee, 0x85, 0x6, 0x3b, 0x31, 0x5, 0xc6, 0xdd, 0xff, 0x11, 0x1c, 0x30, 0xb2, 0x32, 0xcf, 0xf3, 0x4, 0x78, 0x78, 0x4, 0xfb, 0x15, 0x3d, 0x1b, 0x14, 0x34, 0x66, 0x21, 0x20, 0x50, 0xad, 0x54, 0x86, 0xf3, 0x2, 0x5, 0xe7, 0x8a, 0x8d, 0xa7, 0xbf, 0x5a, 0xa9, 0xb3, 0xd1, 0x74, 0xfe, 0xce, 0x73, 0x46, 0x59, 0x6a, 0x2e, 0x87, 0xa7, 0x6e, 0x2a, 0xaf, 0x53, 0x80, 0xd0, 0x41, 0x93, 0xfb, 0x18, 0x6b, 0x23, 0x9f, 0x81, 0xb2, 0xd6, 0x17, 0x12, 0xa3, 0x24, 0x66, 0xcb, 0x74, 0x98, 0xd9, 0xe3, 0xf1, 0xc3, 0xdb, 0x82, 0xb1, 0xcf, 0x32, 0xd8, 0x7c, 0xaa, 0x52, 0xc8, 0x7b, 0xdc, 0xa1, 0x31, 0xb6, 0x54, 0xfa, 0xeb, 0x78, 0x2, 0xbc, 0x32, 0xec, 0x99, 0x64, 0x6c, 0x32, 0x4e, 0x57, 0x3a, 0xaf, 0x50, 0xec, 0xa, 0x7, 0x8c, 0x3e, 0x81, 0x13, 0xc4, 0xfb, 0xaf, 0x54, 0xe6, 0x27, 0x9c, 0x3c, 0xfd, 0xb2, 0xa2, 0xdb, 0x98, 0xc8, 0x4f, 0xb2, 0x6, 0x7b, 0x57, 0xf7, 0xd8, 0xaa, 0x41, 0x7e, 0x37, 0xa1, 0x8, 0xc7, 0x75, 0xad, 0xa0, 0x61, 0x8b, 0x2, 0x6e, 0x98, 0x9f, 0x8b, 0xc6, 0x11, 0xe4, 0x20, 0xe1, 0xb0, 0x53, 0x3c, 0x78, 0x99, 0x6a, 0x8a, 0x15, 0x4, 0x7c, 0xa9, 0xe5, 0x5f, 0x19, 0x30, 0xdc, 0xe, 0xe8, 0xae, 0x2, 0x59, 0x1, 0xf2, 0x3d, 0xbc, 0x7a, 0x2c, 0xe9, 0xd0, 0xe8, 0xa2, 0x65, 0x13, 0x83, 0x5c, 0x60, 0x80, 0xf, 0xfb, 0x80, 0xe3, 0xef, 0xf8, 0xd3, 0x40, 0x6b, 0xf9, 0x3, 0x16, 0x1, 0x3e, 0x90, 0xeb, 0x8f, 0xba, 0x75, 0x9b, 0x60, 0x38, 0x68, 0xbc, 0x20, 0xfa, 0x80, 0xcc, 0x6d, 0xed, 0x9f, 0x3d, 0xee, 0x9d, 0x3f, 0xd8, 0x38, 0x3e, 0x69, 0x46, 0xbc, 0x77, 0xa7, 0xd1, 0x7, 0x7b, 0x50, 0x3b, 0xc, 0x0, 0xe7, 0x4, 0x70, 0x2d, 0xa7, 0xbe, 0x72, 0x53, 0x3f, 0xc4, 0x17, 0xae, 0x7d, 0x13, 0xb5, 0x2c, 0x72, 0xcb, 0x11, 0x2c, 0xfc, 0x1, 0xd7, 0xd5, 0xc4, 0x18, 0x3b, 0x80, 0xe0, 0x7b, 0xcb, 0xd0, 0x3, 0x6b, 0x48, 0x66, 0x78, 0xaa, 0xb0, 0x4, 0xf3, 0xfd, 0x78, 0xf7, 0x7c, 0x1, 0x3a, 0x33, 0x46, 0xaf, 0xde, 0x7f, 0xf, 0x45, 0x6d, 0x55, 0xb3, 0x7f, 0x7b, 0xb4, 0xc, 0x9c, 0x88, 0x3b, 0xb5, 0x0, 0xba, 0x40, 0x2b, 0x9e, 0x1e, 0xd2, 0x78, 0x1, 0xe, 0xca, 0xd3, 0xef, 0x52, 0xaa, 0x5e, 0x2, 0x1d, 0xe8, 0x91, 0xbd, 0x82, 0x5f, 0xe, 0x84, 0x87, 0x3f, 0xa9, 0x36, 0x17, 0xfb, 0x72, 0xfe, 0xf9, 0x3e, 0x60, 0xf9, 0x9a, 0xf9, 0x1a, 0x57, 0x8a, 0xe7, 0x57, 0x84, 0x48, 0x5c, 0x77, 0x88, 0xc, 0x10, 0x60, 0x1c, 0xc1, 0x42, 0xfe, 0xfb, 0xe0, 0xaa, 0x3f, 0xe0, 0x3, 0xc, 0xb7, 0xae, 0x5a, 0xc0, 0x96, 0xa9, 0xb, 0xb, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x7c, 0xd3, 0x67, 0xff, 0xb6, 0x17, 0xbe, 0x52, 0x43, 0xc4, 0x2f, 0xd9, 0x27, 0x2e, 0x86, 0xb1, 0x97, 0x57, 0xe6, 0xed, 0x81, 0x96, 0xe2, 0x6e, 0x87, 0x56, 0xf6, 0xc6, 0x40, 0xcd, 0xe6, 0x72, 0x92, 0x25, 0xc, 0x3, 0xdb, 0xf5, 0xce, 0x79, 0x7f, 0x17, 0x28, 0x78, 0x5, 0x9, 0x9d, 0x76, 0xe2, 0x9f, 0x3c, 0xd, 0x27, 0x7d, 0x1c, 0x45, 0x82, 0x41, 0x4d, 0xe9, 0x30, 0xfb, 0xfd, 0x81, 0x63, 0xb0, 0x69, 0x23, 0xb4, 0x14, 0xfd, 0xb9, 0x4, 0xa0, 0x8e, 0xce, 0x2c, 0xe0, 0x1a, 0xf2, 0x89, 0xb8, 0xd1, 0xe6, 0xc8, 0xa2, 0xb1, 0x65, 0xd7, 0x71, 0xbc, 0x6d, 0x45, 0x77, 0xef, 0xe1, 0x45, 0x81, 0x82, 0x27, 0x50, 0x54, 0x7b, 0x87, 0x13, 0x41, 0x51, 0x75, 0xa4, 0x26, 0x4f, 0xfc, 0x37, 0x49, 0xd6, 0x5b, 0xa8, 0xa8, 0x8b, 0xdd, 0x78, 0xe0, 0xa2, 0xe5, 0x4e, 0x50, 0xc7, 0xe, 0xb6, 0xe1, 0xdf, 0x54, 0xd4, 0x14, 0x35, 0xdf, 0x5f, 0x1c, 0x4f, 0xa0, 0xbc, 0x2d, 0xfd, 0x18, 0x21, 0x1b, 0xdf, 0x9b, 0x24, 0x64, 0x99, 0x72, 0xe3, 0x48, 0x6e, 0x2f, 0xbb, 0xdc, 0xb9, 0xc0, 0x5d, 0x71, 0x3c, 0x19, 0x63, 0xb5, 0x16, 0x50, 0x17, 0x9d, 0xa1, 0x5, 0xf0, 0xa, 0x6a, 0x39, 0xed, 0x41, 0xf5, 0xdf, 0x44, 0xb4, 0x80, 0x26, 0xd6, 0x58, 0xdf, 0xa1, 0xd3, 0x5f, 0xd, 0x1, 0x9c, 0x73, 0x85, 0x9d, 0x3e, 0xa3, 0x6a, 0x30, 0x35, 0xd6, 0x2c, 0x6d, 0xb6, 0x3, 0x7a, 0x7b, 0x8, 0xbc, 0x83, 0x93, 0xbe, 0xcf, 0x14, 0x9e, 0xcb, 0x5, 0xb3, 0x4d, 0x3e, 0xbe, 0xf1, 0xd8, 0x60, 0xb9, 0xc4, 0x16, 0x1, 0x16, 0x1, 0xfa, 0x20, 0xec, 0xcf, 0x25, 0x80, 0x13, 0xb6, 0xe8, 0xd4, 0x92, 0xab, 0x88, 0x33, 0xb, 0xa6, 0x7f, 0x47, 0xb, 0x2, 0x94, 0xf9, 0xe6, 0x79, 0xfb, 0xff, 0xb3, 0x71, 0x2a, 0x4, 0xa5, 0x61, 0xe5, 0x5, 0x36, 0x22, 0x18, 0x5c, 0x1e, 0xb, 0x71, 0x80, 0x63, 0xfb, 0xeb, 0xd7, 0x83, 0x67, 0x6d, 0xc7, 0xf4, 0xaa, 0x58, 0x2, 0x22, 0x82, 0x83, 0xee, 0x84, 0xb4, 0xac, 0xa, 0x36, 0x5e, 0x2f, 0xec, 0x2e, 0xd5, 0xb6, 0x3f, 0x10, 0x80, 0x88, 0x15, 0x77, 0xe7, 0xc3, 0x87, 0x11, 0xaa, 0xee, 0x0, 0x83, 0x49, 0x49, 0x24, 0x3, 0xa0, 0x8a, 0x50, 0x59, 0x14, 0xc6, 0xaa, 0x80, 0xff, 0xd1, 0x39, 0x8a, 0xd2, 0xab, 0xf0, 0x26, 0x88, 0x3c, 0xc9, 0x12, 0xe, 0xe2, 0x10, 0x5d, 0xfc, 0xdb, 0x3e, 0x34, 0xff, 0x3, 0x66, 0xdd, 0xc9, 0xb6, 0x2b, 0x1c, 0x85, 0xce, 0xdb, 0xdb, 0x8, 0x49, 0x10, 0xf7, 0x81, 0x5, 0x5, 0xf8, 0x23, 0xc9, 0x90, 0xb5, 0x7f, 0x4f, 0x8d, 0x4b, 0xc5, 0xef, 0x8f, 0xdb, 0x1, 0xcd, 0xa6, 0x67, 0x9d, 0xd4, 0x36, 0xfc, 0xb2, 0x59, 0x73, 0x1b, 0xb4, 0x4c, 0x87, 0x1a, 0xef, 0x9a, 0x58, 0xb3, 0x95, 0x33, 0x4e, 0x4f, 0xa1, 0xdd, 0x4f, 0x84, 0x38, 0x5b, 0x5e, 0x9f, 0x23, 0x21, 0x6, 0xb5, 0xb2, 0x2e, 0x2c, 0xce, 0xd9, 0xcd, 0x24, 0x57, 0xf9, 0xfb, 0x60, 0x30, 0xa4, 0x79, 0x18, 0xa6, 0xca, 0x5d, 0xcd, 0x17, 0xc8, 0xf3, 0x5b, 0x63, 0xb7, 0xe7, 0x1b, 0xe4, 0x3e, 0xc7, 0x3b, 0x38, 0x60, 0x6a, 0x40, 0xbe, 0xfc, 0x1, 0xaa, 0xd4, 0xe, 0xc6, 0x4b, 0x77, 0x57, 0x1a, 0x5e, 0xd5, 0xda, 0x5e, 0x37, 0xff, 0x27, 0x0, 0x1d, 0x1d, 0x22, 0x4c, 0x19, 0x11, 0x7f, 0x8d, 0xc8, 0x33, 0x5d, 0x5b, 0xd1, 0x7b, 0x7e, 0x76, 0x6c, 0x73, 0xcb, 0xcd, 0x83, 0x96, 0xc0, 0x47, 0xe5, 0x51, 0xa9, 0xc9, 0xf3, 0xdd, 0xdb, 0xb9, 0x11, 0xa8, 0x9d, 0x67, 0x78, 0x7e, 0x80, 0xa1, 0xdb, 0x6e, 0x1b, 0xdc, 0xae, 0xb1, 0x74, 0xe7, 0x76, 0xa1, 0xea, 0xd4, 0xf3, 0xc, 0x3, 0x60, 0xa0, 0x2f, 0x77, 0x6d, 0x77, 0x71, 0x59, 0xd1, 0x9a, 0x66, 0x8d, 0x6c, 0xf2, 0xb0, 0xaf, 0xde, 0xed, 0xb9, 0x3f, 0xfb, 0x5d, 0x62, 0x26, 0x55, 0x7, 0xf3, 0x46, 0xdb, 0xed, 0x4, 0xe8, 0x7d, 0xcc, 0x89, 0xd6, 0xe6, 0x75, 0x7, 0xc0, 0x58, 0x19, 0xb0, 0xcb, 0x53, 0x5e, 0x47, 0xb4, 0xe7, 0x5d, 0x12, 0xf, 0x38, 0x44, 0x90, 0x66, 0xa2, 0xc8, 0xaf, 0xef, 0x6c, 0xe8, 0xbf, 0x98, 0xd6, 0xe, 0x78, 0xc4, 0x66, 0x9b, 0xc9, 0x50, 0x7c, 0x64, 0x6f, 0x70, 0x26, 0x2d, 0xf9, 0x8, 0x1, 0x66, 0xe2, 0x80, 0xe5, 0xf, 0xd0, 0x25, 0xa3, 0x3, 0x6f, 0xe1, 0x41, 0xf0, 0xd6, 0x7c, 0xef, 0x26, 0x88, 0x1f, 0xd2, 0x31, 0x5b, 0xd, 0x9c, 0x75, 0xfc, 0x5, 0x25, 0xe6, 0x76, 0xfd, 0x5, 0x51, 0x19, 0x11, 0x84, 0xf8, 0x33, 0x9d, 0xe1, 0xf2, 0x9, 0x58, 0x40, 0x98, 0xda, 0x22, 0xb0, 0x50, 0xc4, 0xcb, 0x37, 0xb6, 0x35, 0x86, 0x65, 0x51, 0x75, 0xa8, 0xb7, 0x65, 0x7a, 0x3a, 0x9c, 0x3d, 0xa9, 0xdb, 0xe9, 0x96, 0xcf, 0xa4, 0xb5, 0x5d, 0xe5, 0x93, 0x69, 0xd1, 0xb1, 0x8c, 0x3c, 0x6e, 0x93, 0xab, 0x4e, 0xdd, 0xcc, 0x3c, 0xc4, 0xef, 0xb7, 0xb6, 0xb, 0x21, 0xf5, 0x33, 0xa0, 0x7e, 0xa6, 0x41, 0x99, 0xb0, 0x60, 0x47, 0xa8, 0xdc, 0xbf, 0x31, 0xa2, 0x9d, 0x27, 0x30, 0xb6, 0x40, 0xdf, 0x99, 0x53, 0xe6, 0xc5, 0x1, 0x4a, 0x69, 0xb3, 0xc1, 0x86, 0xda, 0xf0, 0xf7, 0xf, 0xd5, 0xeb, 0xdf, 0xa8, 0x64, 0x7b, 0x73, 0xf0, 0xf6, 0xee, 0xd3, 0x96, 0xf0, 0xf1, 0x64, 0xf1, 0xfb, 0x97, 0x80, 0xac, 0xae, 0x3f, 0x58, 0x15, 0x2b, 0x1e, 0xa4, 0xc3, 0xf6, 0xf5, 0xa6, 0x8e, 0x41, 0xca, 0xc4, 0xdf, 0xb, 0x9a, 0xdb, 0x49, 0xd4, 0x34, 0x91, 0xd7, 0x67, 0x87, 0x98, 0xc4, 0xda, 0x72, 0xbe, 0x69, 0x95, 0x8c, 0x4, 0x9f, 0xb6, 0x3, 0x5e, 0x23, 0xb0, 0xc2, 0x90, 0xa2, 0x6d, 0xbb, 0xe0, 0xc4, 0xab, 0x82, 0xe2, 0x3a, 0x7c, 0xfd, 0x51, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x9, 0x80, 0x4f, 0x23, 0x40, 0xe7, 0x0, 0x11, 0xc4, 0x83, 0x63, 0x1b, 0x1c, 0x40, 0x3d, 0x22, 0x44, 0x9a, 0x3, 0x44, 0x10, 0x33, 0xc5, 0x9b, 0x1d, 0x40, 0xb0, 0xcc, 0xf6, 0xd0, 0xdd, 0xcb, 0xe, 0xf8, 0x39, 0x61, 0xa2, 0xc9, 0xf0, 0xaa, 0x68, 0x67, 0xb1, 0x79, 0x9d, 0xfb, 0x6f, 0x8e, 0x10, 0xd1, 0x22, 0x8b, 0x7b, 0xb7, 0x8f, 0x5f, 0xf6, 0xee, 0xbf, 0x59, 0x8, 0xb6, 0xee, 0x4e, 0x5f, 0xd9, 0xcd, 0xe6, 0x6c, 0x4b, 0xc0, 0x11, 0xfa, 0x1d, 0xda, 0xa8, 0x97, 0xe3, 0xd1, 0x76, 0x2b, 0x1, 0x40, 0xe9, 0xa3, 0xeb, 0xdd, 0xb6, 0xe1, 0x6a, 0x7, 0x96, 0x6a, 0x34, 0x84, 0x46, 0xf3, 0x80, 0x60, 0x2, 0xcc, 0xc3, 0x4, 0xa0, 0x88, 0xef, 0xef, 0xdf, 0x4e, 0x19, 0x4, 0x9f, 0x4b, 0xb, 0x3c, 0x61, 0x7, 0x4c, 0x65, 0x7b, 0x3c, 0x1, 0x86, 0xa6, 0xa, 0xa2, 0x7b, 0x82, 0x0, 0x53, 0x71, 0xc0, 0x72, 0x89, 0x2d, 0x7f, 0xc0, 0x22, 0x80, 0x62, 0x9a, 0xc5, 0x2, 0x44, 0x7a, 0x27, 0x94, 0xdc, 0x19, 0x20, 0xf2, 0x3a, 0x78, 0xd9, 0xc6, 0xfb, 0xbd, 0xfd, 0x5, 0x63, 0xfd, 0x3, 0xe2, 0x7a, 0x67, 0x7a, 0x3a, 0x63, 0xab, 0x9, 0x5, 0xaf, 0xf3, 0xb4, 0x4d, 0x46, 0xf8, 0x24, 0xa7, 0x66, 0x44, 0x6b, 0x5b, 0x24, 0x7e, 0xe6, 0x17, 0x88, 0xf8, 0x81, 0x58, 0x5b, 0xa9, 0x17, 0x21, 0xba, 0x3f, 0xfe, 0xfe, 0x8a, 0x16, 0x2f, 0xf7, 0x92, 0x46, 0xfa, 0x7, 0xb4, 0x19, 0xba, 0x3f, 0xd9, 0xd8, 0x40, 0x83, 0xe9, 0x86, 0xbd, 0x5a, 0xc5, 0x67, 0xd4, 0xcf, 0xcc, 0xb4, 0xb, 0x0, 0x94, 0xbf, 0xdf, 0xbe, 0x3d, 0xae, 0xd6, 0x96, 0x7b, 0xd0, 0x36, 0x9e, 0xc0, 0xe, 0xe8, 0x1e, 0x1f, 0x62, 0xf7, 0x58, 0xfc, 0xfe, 0x8, 0x16, 0xf0, 0x78, 0xa2, 0xaf, 0x7b, 0x0, 0xae, 0x84, 0x70, 0xaa, 0xe6, 0xf6, 0xf6, 0x12, 0x2b, 0xe3, 0x2b, 0xae, 0x2f, 0xea, 0x33, 0x6, 0xdb, 0x22, 0x20, 0xa, 0x91, 0xf0, 0x81, 0xbf, 0xf0, 0xf1, 0x2, 0x70, 0xc6, 0x97, 0xd0, 0x98, 0x5b, 0xa6, 0x38, 0x25, 0x34, 0x3e, 0xfe, 0xf5, 0x13, 0x43, 0xbf, 0x7f, 0xbd, 0x3f, 0xe0, 0x1f, 0x5f, 0x4e, 0x4b, 0x19, 0x56, 0xcb, 0xb5, 0x20, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 };
+const unsigned short sGlyphWidths[256] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 3, 5, 7, 7, 12, 9, 2, 4, 4, 5, 8, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 8, 8, 8, 7, 13, 9, 9, 9, 9, 9, 8, 10, 9, 3, 6, 9, 7, 11, 9, 10, 9, 10, 9, 9, 7, 9, 9, 13, 7, 9, 7, 4, 4, 4, 5, 7, 4, 7, 7, 7, 7, 7, 3, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7, 4, 7, 4, 7, 5, 9, 7, 7, 7, 4, 3, 4, 8, 10, 7, 10, 3, 7, 4, 13, 7, 7, 4, 14, 9, 4, 13, 10, 7, 10, 10, 3, 3, 4, 4, 5, 7, 13, 4, 13, 7, 4, 12, 10, 7, 9, 4, 3, 7, 7, 7, 7, 3, 7, 4, 10, 4, 7, 8, 4, 10, 7, 5, 7, 4, 4, 4, 7, 7, 4, 4, 4, 5, 7, 11, 11, 11, 8, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 3, 3, 3, 3, 9, 9, 10, 10, 10, 10, 10, 8, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 12, 7, 7, 7, 7, 7, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 };
+const unsigned int sTextureWidth = 256;
+const unsigned int sTextureHeight = 256;
+const unsigned int sCellWidth = 16;
+const unsigned int sCellHeight = 16;
diff --git a/system/graphics/layers/composite/FrameUniformityData.cpp b/system/graphics/layers/composite/FrameUniformityData.cpp
new file mode 100644
index 000000000..e8bab6adb
--- /dev/null
+++ b/system/graphics/layers/composite/FrameUniformityData.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameUniformityData.h"
+
+#include <map>
+
+#include "Units.h"
+#include "gfxPoint.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/APZTestDataBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+Point
+LayerTransforms::GetAverage()
+{
+ MOZ_ASSERT(!mTransforms.IsEmpty());
+
+ Point current = mTransforms[0];
+ Point average;
+ size_t length = mTransforms.Length();
+
+ for (size_t i = 1; i < length; i++) {
+ Point nextTransform = mTransforms[i];
+ Point movement = nextTransform - current;
+ average += Point(std::fabs(movement.x), std::fabs(movement.y));
+ current = nextTransform;
+ }
+
+ average = average / (float) length;
+ return average;
+}
+
+Point
+LayerTransforms::GetStdDev()
+{
+ Point average = GetAverage();
+ Point stdDev;
+ Point current = mTransforms[0];
+
+ for (size_t i = 1; i < mTransforms.Length(); i++) {
+ Point next = mTransforms[i];
+ Point move = next - current;
+ move.x = fabs(move.x);
+ move.y = fabs(move.y);
+
+ Point diff = move - average;
+ diff.x = diff.x * diff.x;
+ diff.y = diff.y * diff.y;
+ stdDev += diff;
+
+ current = next;
+ }
+
+ stdDev = stdDev / mTransforms.Length();
+ stdDev.x = sqrt(stdDev.x);
+ stdDev.y = sqrt(stdDev.y);
+ return stdDev;
+}
+
+LayerTransformRecorder::~LayerTransformRecorder()
+{
+ Reset();
+}
+
+void
+LayerTransformRecorder::RecordTransform(Layer* aLayer, const Point& aTransform)
+{
+ LayerTransforms* layerTransforms = GetLayerTransforms((uintptr_t) aLayer);
+ layerTransforms->mTransforms.AppendElement(aTransform);
+}
+
+void
+LayerTransformRecorder::EndTest(FrameUniformityData* aOutData)
+{
+ for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ uintptr_t layer = iter->first;
+ float uniformity = CalculateFrameUniformity(layer);
+
+ std::pair<uintptr_t,float> result(layer, uniformity);
+ aOutData->mUniformities.insert(result);
+ }
+
+ Reset();
+}
+
+LayerTransforms*
+LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer)
+{
+ if (!mFrameTransforms.count(aLayer)) {
+ LayerTransforms* newTransform = new LayerTransforms();
+ std::pair<uintptr_t, LayerTransforms*> newLayer(aLayer, newTransform);
+ mFrameTransforms.insert(newLayer);
+ }
+
+ return mFrameTransforms.find(aLayer)->second;
+}
+
+void
+LayerTransformRecorder::Reset()
+{
+ for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ LayerTransforms* layerTransforms = iter->second;
+ delete layerTransforms;
+ }
+
+ mFrameTransforms.clear();
+}
+
+float
+LayerTransformRecorder::CalculateFrameUniformity(uintptr_t aLayer)
+{
+ LayerTransforms* layerTransform = GetLayerTransforms(aLayer);
+ float yUniformity = -1;
+ if (!layerTransform->mTransforms.IsEmpty()) {
+ Point stdDev = layerTransform->GetStdDev();
+ yUniformity = stdDev.y;
+ }
+ return yUniformity;
+}
+
+bool
+FrameUniformityData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext)
+{
+ dom::FrameUniformityResults results;
+ dom::Sequence<dom::FrameUniformity>& layers = results.mLayerUniformities.Construct();
+
+ for (auto iter = mUniformities.begin(); iter != mUniformities.end(); ++iter) {
+ uintptr_t layerAddr = iter->first;
+ float uniformity = iter->second;
+
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(layers.AppendElement(fallible));
+ dom::FrameUniformity& entry = layers.LastElement();
+
+ entry.mLayerAddress.Construct() = layerAddr;
+ entry.mFrameUniformity.Construct() = uniformity;
+ }
+
+ return dom::ToJSValue(aContext, results, aOutValue);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/FrameUniformityData.h b/system/graphics/layers/composite/FrameUniformityData.h
new file mode 100644
index 000000000..3ff1bdc4a
--- /dev/null
+++ b/system/graphics/layers/composite/FrameUniformityData.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_FrameUniformityData_h_
+#define mozilla_layers_FrameUniformityData_h_
+
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+
+class FrameUniformityData {
+ friend struct IPC::ParamTraits<FrameUniformityData>;
+
+public:
+ bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext);
+ // Contains the calculated frame uniformities
+ std::map<uintptr_t,float> mUniformities;
+};
+
+struct LayerTransforms {
+ LayerTransforms() {}
+
+ gfx::Point GetAverage();
+ gfx::Point GetStdDev();
+
+ // 60 fps * 5 seconds worth of data
+ AutoTArray<gfx::Point, 300> mTransforms;
+};
+
+class LayerTransformRecorder {
+public:
+ LayerTransformRecorder() {}
+ ~LayerTransformRecorder();
+
+ void RecordTransform(Layer* aLayer, const gfx::Point& aTransform);
+ void Reset();
+ void EndTest(FrameUniformityData* aOutData);
+
+private:
+ float CalculateFrameUniformity(uintptr_t aLayer);
+ LayerTransforms* GetLayerTransforms(uintptr_t aLayer);
+ std::map<uintptr_t,LayerTransforms*> mFrameTransforms;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::layers::FrameUniformityData>
+{
+ typedef mozilla::layers::FrameUniformityData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mUniformities);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ParamTraitsStd<std::map<uintptr_t,float>>::Read(aMsg, aIter, &aResult->mUniformities);
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_FrameUniformityData_h_
diff --git a/system/graphics/layers/composite/GPUVideoTextureHost.cpp b/system/graphics/layers/composite/GPUVideoTextureHost.cpp
new file mode 100644
index 000000000..1e539d8ac
--- /dev/null
+++ b/system/graphics/layers/composite/GPUVideoTextureHost.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUVideoTextureHost.h"
+#include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+GPUVideoTextureHost::GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor)
+ : TextureHost(aFlags)
+{
+ MOZ_COUNT_CTOR(GPUVideoTextureHost);
+ mWrappedTextureHost = VideoBridgeParent::GetSingleton()->LookupTexture(aDescriptor.handle());
+}
+
+GPUVideoTextureHost::~GPUVideoTextureHost()
+{
+ MOZ_COUNT_DTOR(GPUVideoTextureHost);
+}
+
+bool
+GPUVideoTextureHost::Lock()
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->Lock();
+}
+
+bool
+GPUVideoTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->BindTextureSource(aTexture);
+}
+
+Compositor*
+GPUVideoTextureHost::GetCompositor()
+{
+ if (!mWrappedTextureHost) {
+ return nullptr;
+ }
+ return mWrappedTextureHost->GetCompositor();
+}
+
+void
+GPUVideoTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mWrappedTextureHost) {
+ mWrappedTextureHost->SetCompositor(aCompositor);
+ }
+}
+
+YUVColorSpace
+GPUVideoTextureHost::GetYUVColorSpace() const
+{
+ if (mWrappedTextureHost) {
+ return mWrappedTextureHost->GetYUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+gfx::IntSize
+GPUVideoTextureHost::GetSize() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::IntSize();
+ }
+ return mWrappedTextureHost->GetSize();
+}
+
+gfx::SurfaceFormat
+GPUVideoTextureHost::GetFormat() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mWrappedTextureHost->GetFormat();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/GPUVideoTextureHost.h b/system/graphics/layers/composite/GPUVideoTextureHost.h
new file mode 100644
index 000000000..fd6bdc3fb
--- /dev/null
+++ b/system/graphics/layers/composite/GPUVideoTextureHost.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+#define MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+class GPUVideoTextureHost : public TextureHost
+{
+public:
+ GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor);
+ virtual ~GPUVideoTextureHost();
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "GPUVideoTextureHost"; }
+#endif
+
+protected:
+ RefPtr<TextureHost> mWrappedTextureHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
diff --git a/system/graphics/layers/composite/ImageHost.cpp b/system/graphics/layers/composite/ImageHost.cpp
new file mode 100644
index 000000000..b1d77924b
--- /dev/null
+++ b/system/graphics/layers/composite/ImageHost.cpp
@@ -0,0 +1,739 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageHost.h"
+
+#include "LayersLogging.h" // for AppendToString
+#include "composite/CompositableHost.h" // for CompositableHost, etc
+#include "ipc/IPCMessageUtils.h" // for null_t
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayerManagerComposite.h" // for TexturedEffect, Effect, etc
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+
+#define BIAS_TIME_MS 1.0
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class ISurfaceAllocator;
+
+ImageHost::ImageHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo)
+ , mImageContainer(nullptr)
+ , mLastFrameID(-1)
+ , mLastProducerID(-1)
+ , mBias(BIAS_NONE)
+ , mLocked(false)
+{}
+
+ImageHost::~ImageHost()
+{
+ SetImageContainer(nullptr);
+}
+
+void
+ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() >= 1);
+
+ nsTArray<TimedImage> newImages;
+
+ for (uint32_t i = 0; i < aTextures.Length(); ++i) {
+ const TimedTexture& t = aTextures[i];
+ MOZ_ASSERT(t.mTexture);
+ if (i + 1 < aTextures.Length() &&
+ t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
+ // Ignore frames before a frame that we already composited. We don't
+ // ever want to display these frames. This could be important if
+ // the frame producer adjusts timestamps (e.g. to track the audio clock)
+ // and the new frame times are earlier.
+ continue;
+ }
+ TimedImage& img = *newImages.AppendElement();
+ img.mTextureHost = t.mTexture;
+ img.mTimeStamp = t.mTimeStamp;
+ img.mPictureRect = t.mPictureRect;
+ img.mFrameID = t.mFrameID;
+ img.mProducerID = t.mProducerID;
+ img.mTextureHost->SetCropRect(img.mPictureRect);
+ img.mTextureHost->Updated();
+ }
+
+ mImages.SwapElements(newImages);
+ newImages.Clear();
+
+ // If we only have one image we can upload it right away, otherwise we'll upload
+ // on-demand during composition after we have picked the proper timestamp.
+ if (mImages.Length() == 1) {
+ SetCurrentTextureHost(mImages[0].mTextureHost);
+ }
+
+ // Video producers generally send replacement images with the same frameID but
+ // slightly different timestamps in order to sync with the audio clock. This
+ // means that any CompositeUntil() call we made in Composite() may no longer
+ // guarantee that we'll composite until the next frame is ready. Fix that here.
+ if (GetCompositor() && mLastFrameID >= 0) {
+ for (size_t i = 0; i < mImages.Length(); ++i) {
+ bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
+ mImages[i].mProducerID != mLastProducerID;
+ if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
+ GetCompositor()->CompositeUntil(mImages[i].mTimeStamp +
+ TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ break;
+ }
+ }
+ }
+}
+
+void
+ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
+{
+ if (aTexture == mCurrentTextureHost.get()) {
+ return;
+ }
+
+ bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
+ && mCurrentTextureHost->HasIntermediateBuffer();
+
+ if (swapTextureSources) {
+ auto dataSource = mCurrentTextureSource->AsDataTextureSource();
+ if (dataSource) {
+ // The current textureHost has an internal buffer in the form of the
+ // DataTextureSource. Removing the ownership of the texture source
+ // will enable the next texture host we bind to the texture source to
+ // acquire it instead of creating a new one. This is desirable in
+ // ImageHost because the current texture won't be used again with the
+ // same content. It wouldn't be desirable with ContentHost for instance,
+ // because the latter reuses the texture's valid regions.
+ dataSource->SetOwner(nullptr);
+ }
+
+ RefPtr<TextureSource> tmp = mExtraTextureSource;
+ mExtraTextureSource = mCurrentTextureSource.get();
+ mCurrentTextureSource = tmp;
+ } else {
+ mExtraTextureSource = nullptr;
+ }
+
+ mCurrentTextureHost = aTexture;
+ mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
+}
+
+void
+ImageHost::CleanupResources()
+{
+ mExtraTextureSource = nullptr;
+ mCurrentTextureSource = nullptr;
+ mCurrentTextureHost = nullptr;
+}
+
+void
+ImageHost::RemoveTextureHost(TextureHost* aTexture)
+{
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::RemoveTextureHost(aTexture);
+
+ for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+ if (mImages[i].mTextureHost == aTexture) {
+ aTexture->UnbindTextureSource();
+ mImages.RemoveElementAt(i);
+ }
+ }
+}
+
+void
+ImageHost::UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect)
+{
+ if (ImageHostOverlay::IsValid(aOverlay)) {
+ if (!mImageHostOverlay) {
+ mImageHostOverlay = new ImageHostOverlay();
+ }
+ mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect);
+ } else {
+ mImageHostOverlay = nullptr;
+ }
+}
+
+static TimeStamp
+GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
+{
+ switch (aBias) {
+ case ImageHost::BIAS_NEGATIVE:
+ return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ case ImageHost::BIAS_POSITIVE:
+ return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ default:
+ return aInput;
+ }
+}
+
+static ImageHost::Bias
+UpdateBias(const TimeStamp& aCompositionTime,
+ const TimeStamp& aCompositedImageTime,
+ const TimeStamp& aNextImageTime, // may be null
+ ImageHost::Bias aBias)
+{
+ if (aCompositedImageTime.IsNull()) {
+ return ImageHost::BIAS_NONE;
+ }
+ TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
+ if (aCompositionTime - aCompositedImageTime < threshold &&
+ aCompositionTime - aCompositedImageTime > -threshold) {
+ // The chosen frame's time is very close to the composition time (probably
+ // just before the current composition time, but due to previously set
+ // negative bias, it could be just after the current composition time too).
+ // If the inter-frame time is almost exactly equal to (a multiple of)
+ // the inter-composition time, then we're in a dangerous situation because
+ // jitter might cause frames to fall one side or the other of the
+ // composition times, causing many frames to be skipped or duplicated.
+ // Try to prevent that by adding a negative bias to the frame times during
+ // the next composite; that should ensure the next frame's time is treated
+ // as falling just before a composite time.
+ return ImageHost::BIAS_NEGATIVE;
+ }
+ if (!aNextImageTime.IsNull() &&
+ aNextImageTime - aCompositionTime < threshold &&
+ aNextImageTime - aCompositionTime > -threshold) {
+ // The next frame's time is very close to our composition time (probably
+ // just after the current composition time, but due to previously set
+ // positive bias, it could be just before the current composition time too).
+ // We're in a dangerous situation because jitter might cause frames to
+ // fall one side or the other of the composition times, causing many frames
+ // to be skipped or duplicated.
+ // Try to prevent that by adding a negative bias to the frame times during
+ // the next composite; that should ensure the next frame's time is treated
+ // as falling just before a composite time.
+ return ImageHost::BIAS_POSITIVE;
+ }
+ return ImageHost::BIAS_NONE;
+}
+
+int ImageHost::ChooseImageIndex() const
+{
+ if (!GetCompositor() || mImages.IsEmpty()) {
+ return -1;
+ }
+ TimeStamp now = GetCompositor()->GetCompositionTime();
+
+ if (now.IsNull()) {
+ // Not in a composition, so just return the last image we composited
+ // (if it's one of the current images).
+ for (uint32_t i = 0; i < mImages.Length(); ++i) {
+ if (mImages[i].mFrameID == mLastFrameID &&
+ mImages[i].mProducerID == mLastProducerID) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ uint32_t result = 0;
+ while (result + 1 < mImages.Length() &&
+ GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
+ ++result;
+ }
+ return result;
+}
+
+const ImageHost::TimedImage* ImageHost::ChooseImage() const
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+ImageHost::TimedImage* ImageHost::ChooseImage()
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+TextureHost*
+ImageHost::GetAsTextureHost(IntRect* aPictureRect)
+{
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ }
+ if (aPictureRect && img) {
+ *aPictureRect = img->mPictureRect;
+ }
+ return img ? img->mTextureHost.get() : nullptr;
+}
+
+void ImageHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags)
+{
+ CompositableHost::Attach(aLayer, aCompositor, aFlags);
+ for (auto& img : mImages) {
+ if (GetCompositor()) {
+ img.mTextureHost->SetCompositor(GetCompositor());
+ }
+ img.mTextureHost->Updated();
+ }
+}
+
+void
+ImageHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ if (!GetCompositor()) {
+ // should only happen when a tab is dragged to another window and
+ // async-video is still sending frames but we haven't attached the
+ // set the new compositor yet.
+ return;
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->Composite(GetCompositor(),
+ mFlashCounter,
+ aLayer,
+ aEffectChain,
+ aOpacity,
+ aTransform,
+ aSamplingFilter,
+ aClipRect,
+ aVisibleRegion);
+ mBias = BIAS_NONE;
+ return;
+ }
+
+ int imageIndex = ChooseImageIndex();
+ if (imageIndex < 0) {
+ return;
+ }
+
+ if (uint32_t(imageIndex) + 1 < mImages.Length()) {
+ GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ }
+
+ TimedImage* img = &mImages[imageIndex];
+ img->mTextureHost->SetCompositor(GetCompositor());
+ SetCurrentTextureHost(img->mTextureHost);
+
+ {
+ AutoLockCompositableHost autoLock(this);
+ if (autoLock.Failed()) {
+ NS_WARNING("failed to lock front buffer");
+ return;
+ }
+
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return;
+ }
+
+ if (!mCurrentTextureSource) {
+ // BindTextureSource above should have returned false!
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ bool isAlphaPremultiplied =
+ !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+ RefPtr<TexturedEffect> effect =
+ CreateTexturedEffect(mCurrentTextureHost,
+ mCurrentTextureSource.get(), aSamplingFilter, isAlphaPremultiplied,
+ GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ if (!GetCompositor()->SupportsEffect(effect->mType)) {
+ return;
+ }
+
+ DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE;
+ if (effect->mType == EffectTypes::NV12) {
+ diagnosticFlags |= DiagnosticFlags::NV12;
+ } else if (effect->mType == EffectTypes::YCBCR) {
+ diagnosticFlags |= DiagnosticFlags::YCBCR;
+ }
+
+ if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
+ if (mImageContainer) {
+ aLayer->GetLayerManager()->
+ AppendImageCompositeNotification(ImageCompositeNotification(
+ mImageContainer, nullptr,
+ img->mTimeStamp, GetCompositor()->GetCompositionTime(),
+ img->mFrameID, img->mProducerID));
+ }
+ mLastFrameID = img->mFrameID;
+ mLastProducerID = img->mProducerID;
+ }
+ aEffectChain.mPrimaryEffect = effect;
+ gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
+ BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
+ if (it) {
+
+ // This iteration does not work if we have multiple texture sources here
+ // (e.g. 3 YCbCr textures). There's nothing preventing the different
+ // planes from having different resolutions or tile sizes. For example, a
+ // YCbCr frame could have Cb and Cr planes that are half the resolution of
+ // the Y plane, in such a way that the Y plane overflows the maximum
+ // texture size and the Cb and Cr planes do not. Then the Y plane would be
+ // split into multiple tiles and the Cb and Cr planes would just be one
+ // tile each.
+ // To handle the general case correctly, we'd have to create a grid of
+ // intersected tiles over all planes, and then draw each grid tile using
+ // the corresponding source tiles from all planes, with appropriate
+ // per-plane per-tile texture coords.
+ // DrawQuad currently assumes that all planes use the same texture coords.
+ MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
+ "Can't handle multi-plane BigImages");
+
+ it->BeginBigImageIteration();
+ do {
+ IntRect tileRect = it->GetTileRect();
+ gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
+ rect = rect.Intersect(pictureRect);
+ effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
+ Float(rect.y - tileRect.y) / tileRect.height,
+ Float(rect.width) / tileRect.width,
+ Float(rect.height) / tileRect.height);
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, mFlashCounter);
+ } while (it->NextTile());
+ it->EndBigImageIteration();
+ // layer border
+ GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
+ aClipRect, aTransform, mFlashCounter);
+ } else {
+ IntSize textureSize = mCurrentTextureSource->GetSize();
+ effect->mTextureCoords = Rect(Float(img->mPictureRect.x) / textureSize.width,
+ Float(img->mPictureRect.y) / textureSize.height,
+ Float(img->mPictureRect.width) / textureSize.width,
+ Float(img->mPictureRect.height) / textureSize.height);
+
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+
+ GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags,
+ pictureRect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+
+ // Update mBias last. This can change which frame ChooseImage(Index) would
+ // return, and we don't want to do that until we've finished compositing
+ // since callers of ChooseImage(Index) assume the same image will be chosen
+ // during a given composition. This must happen after autoLock's
+ // destructor!
+ mBias = UpdateBias(
+ GetCompositor()->GetCompositionTime(), mImages[imageIndex].mTimeStamp,
+ uint32_t(imageIndex + 1) < mImages.Length() ?
+ mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
+ mBias);
+}
+
+void
+ImageHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor != aCompositor) {
+ for (auto& img : mImages) {
+ img.mTextureHost->SetCompositor(aCompositor);
+ }
+ }
+ if (mImageHostOverlay) {
+ mImageHostOverlay->SetCompositor(aCompositor);
+ }
+ CompositableHost::SetCompositor(aCompositor);
+}
+
+void
+ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ for (auto& img : mImages) {
+ aStream << "\n";
+ img.mTextureHost->PrintInfo(aStream, pfx.get());
+ AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->PrintInfo(aStream, aPrefix);
+ }
+}
+
+void
+ImageHost::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+ for (auto& img : mImages) {
+ aStream << aPrefix;
+ aStream << (aDumpHtml ? "<ul><li>TextureHost: "
+ : "TextureHost: ");
+ DumpTextureHost(aStream, img.mTextureHost);
+ aStream << (aDumpHtml ? " </li></ul> " : " ");
+ }
+}
+
+LayerRenderState
+ImageHost::GetRenderState()
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->GetRenderState();
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ return img->mTextureHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+ImageHost::GetAsSurface()
+{
+ if (mImageHostOverlay) {
+ return nullptr;
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ return img->mTextureHost->GetAsSurface();
+ }
+ return nullptr;
+}
+
+bool
+ImageHost::Lock()
+{
+ MOZ_ASSERT(!mLocked);
+ TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ SetCurrentTextureHost(img->mTextureHost);
+
+ if (!mCurrentTextureHost->Lock()) {
+ return false;
+ }
+ mLocked = true;
+ return true;
+}
+
+void
+ImageHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+
+ if (mCurrentTextureHost) {
+ mCurrentTextureHost->Unlock();
+ }
+ mLocked = false;
+}
+
+IntSize
+ImageHost::GetImageSize() const
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->GetImageSize();
+ }
+
+ const TimedImage* img = ChooseImage();
+ if (img) {
+ return IntSize(img->mPictureRect.width, img->mPictureRect.height);
+ }
+ return IntSize();
+}
+
+bool
+ImageHost::IsOpaque()
+{
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ if (img->mPictureRect.width == 0 ||
+ img->mPictureRect.height == 0 ||
+ !img->mTextureHost) {
+ return false;
+ }
+
+ gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
+ if (gfx::IsOpaque(format)) {
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<TexturedEffect>
+ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ TimedImage* img = ChooseImage();
+ if (!img) {
+ return nullptr;
+ }
+ SetCurrentTextureHost(img->mTextureHost);
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return nullptr;
+ }
+ bool isAlphaPremultiplied = true;
+ if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
+ isAlphaPremultiplied = false;
+ }
+
+ return CreateTexturedEffect(mCurrentTextureHost,
+ mCurrentTextureSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ GetRenderState());
+}
+
+void
+ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
+{
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.RemoveElement(this);
+ }
+ mImageContainer = aImageContainer;
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.AppendElement(this);
+ }
+}
+
+ImageHostOverlay::ImageHostOverlay()
+{
+ MOZ_COUNT_CTOR(ImageHostOverlay);
+}
+
+ImageHostOverlay::~ImageHostOverlay()
+{
+ if (mCompositor) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ MOZ_COUNT_DTOR(ImageHostOverlay);
+}
+
+/* static */ bool
+ImageHostOverlay::IsValid(OverlaySource aOverlay)
+{
+ if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) &&
+ aOverlay.handle().get_int32_t() != INVALID_OVERLAY) {
+ return true;
+ } else if (aOverlay.handle().type() == OverlayHandle::TGonkNativeHandle) {
+ return true;
+ }
+ return false;
+}
+
+void
+ImageHostOverlay::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor && (mCompositor != aCompositor)) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ if (aCompositor) {
+ aCompositor->AddImageHostOverlay(this);
+ }
+ mCompositor = aCompositor;
+}
+
+void
+ImageHostOverlay::Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ MOZ_ASSERT(mCompositor == aCompositor);
+
+ if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
+ return;
+ }
+
+ Color hollow(0.0f, 0.0f, 0.0f, 0.0f);
+ aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow);
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE);
+
+ gfx::Rect rect;
+ gfx::Rect clipRect(aClipRect.x, aClipRect.y,
+ aClipRect.width, aClipRect.height);
+ rect.SetRect(mPictureRect.x, mPictureRect.y,
+ mPictureRect.width, mPictureRect.height);
+
+ aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, aFlashCounter);
+}
+
+LayerRenderState
+ImageHostOverlay::GetRenderState()
+{
+ LayerRenderState state;
+ return state;
+}
+
+void
+ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay,
+ const nsIntRect& aPictureRect)
+{
+ mOverlay = aOverlay;
+ mPictureRect = aPictureRect;
+}
+
+IntSize
+ImageHostOverlay::GetImageSize() const
+{
+ return IntSize(mPictureRect.width, mPictureRect.height);
+}
+
+void
+ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get();
+
+ AppendToString(aStream, mPictureRect, " [picture-rect=", "]");
+
+ if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ aStream << nsPrintfCString("Overlay: %d", mOverlay.handle().get_int32_t()).get();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/ImageHost.h b/system/graphics/layers/composite/ImageHost.h
new file mode 100644
index 000000000..b8d23afee
--- /dev/null
+++ b/system/graphics/layers/composite/ImageHost.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGEHOST_H
+#define MOZILLA_GFX_IMAGEHOST_H
+
+#include <stdio.h> // for FILE
+#include "CompositableHost.h" // for CompositableHost
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+struct EffectChain;
+class ImageContainerParent;
+class ImageHostOverlay;
+
+/**
+ * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ */
+class ImageHost : public CompositableHost
+{
+public:
+ explicit ImageHost(const TextureInfo& aTextureInfo);
+ ~ImageHost();
+
+ virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+
+ virtual void RemoveTextureHost(TextureHost* aTexture) override;
+
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) override;
+
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) override;
+
+ gfx::IntSize GetImageSize() const override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+ void SetCurrentTextureHost(TextureHost* aTexture);
+
+ virtual void CleanupResources() override;
+
+ int32_t GetFrameID()
+ {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mFrameID : -1;
+ }
+
+ int32_t GetProducerID()
+ {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mProducerID : -1;
+ }
+
+ int32_t GetLastFrameID() const { return mLastFrameID; }
+ int32_t GetLastProducerID() const { return mLastProducerID; }
+
+ enum Bias {
+ // Don't apply bias to frame times
+ BIAS_NONE,
+ // Apply a negative bias to frame times to keep them before the vsync time
+ BIAS_NEGATIVE,
+ // Apply a positive bias to frame times to keep them after the vsync time
+ BIAS_POSITIVE,
+ };
+
+ bool IsOpaque();
+
+protected:
+ struct TimedImage {
+ CompositableTextureHostRef mTextureHost;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+
+ // Use a simple RefPtr because the same texture is already held by a
+ // a CompositableTextureHostRef in the array of TimedImage.
+ // See the comment in CompositableTextureRef for more details.
+ RefPtr<TextureHost> mCurrentTextureHost;
+ CompositableTextureSourceRef mCurrentTextureSource;
+ // When doing texture uploads it's best to alternate between two (or three)
+ // texture sources so that the texture we upload to isn't being used by
+ // the GPU to composite the previous frame.
+ RefPtr<TextureSource> mExtraTextureSource;
+
+ /**
+ * ChooseImage is guaranteed to return the same TimedImage every time it's
+ * called during the same composition, up to the end of Composite() ---
+ * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
+ * mBias is updated at the end of Composite().
+ */
+ const TimedImage* ChooseImage() const;
+ TimedImage* ChooseImage();
+ int ChooseImageIndex() const;
+
+ nsTArray<TimedImage> mImages;
+ // Weak reference, will be null if mImageContainer has been destroyed.
+ ImageContainerParent* mImageContainer;
+ int32_t mLastFrameID;
+ int32_t mLastProducerID;
+ /**
+ * Bias to apply to the next frame.
+ */
+ Bias mBias;
+
+ bool mLocked;
+
+ RefPtr<ImageHostOverlay> mImageHostOverlay;
+};
+
+/**
+ * ImageHostOverlay handles OverlaySource compositing
+ */
+class ImageHostOverlay {
+protected:
+ virtual ~ImageHostOverlay();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay)
+ ImageHostOverlay();
+
+ static bool IsValid(OverlaySource aOverlay);
+
+ void SetCompositor(Compositor* aCompositor);
+
+ virtual void Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion);
+ virtual LayerRenderState GetRenderState();
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect);
+ virtual gfx::IntSize GetImageSize() const;
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+protected:
+ RefPtr<Compositor> mCompositor;
+ gfx::IntRect mPictureRect;
+ OverlaySource mOverlay;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/ImageLayerComposite.cpp b/system/graphics/layers/composite/ImageLayerComposite.cpp
new file mode 100644
index 000000000..6867aaa22
--- /dev/null
+++ b/system/graphics/layers/composite/ImageLayerComposite.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageLayerComposite.h"
+#include "CompositableHost.h" // for CompositableHost
+#include "Layers.h" // for WriteSnapshotToDumpFile, etc
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/ImageHost.h" // for ImageHost
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+ImageLayerComposite::ImageLayerComposite(LayerManagerComposite* aManager)
+ : ImageLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mImageHost(nullptr)
+{
+ MOZ_COUNT_CTOR(ImageLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ImageLayerComposite::~ImageLayerComposite()
+{
+ MOZ_COUNT_DTOR(ImageLayerComposite);
+ MOZ_ASSERT(mDestroyed);
+
+ CleanupResources();
+}
+
+bool
+ImageLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE: {
+ ImageHost* newImageHost = static_cast<ImageHost*>(aHost);
+ if (mImageHost && newImageHost != mImageHost) {
+ mImageHost->Detach(this);
+ }
+ mImageHost = newImageHost;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void
+ImageLayerComposite::Disconnect()
+{
+ Destroy();
+}
+
+LayerRenderState
+ImageLayerComposite::GetRenderState()
+{
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+Layer*
+ImageLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mImageHost) {
+ mImageHost->SetCompositor(mCompositor);
+ }
+}
+
+void
+ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ if (!mImageHost || !mImageHost->IsAttached()) {
+ return;
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ mCompositor->MakeCurrent();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mImageHost->SetCompositor(mCompositor);
+ mImageHost->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransformForBuffer(),
+ GetSamplingFilter(),
+ clipRect);
+ });
+ mImageHost->BumpFlashCounter();
+}
+
+void
+ImageLayerComposite::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+{
+ gfx::Matrix4x4 local = GetLocalTransform();
+
+ // Snap image edges to pixel boundaries
+ gfxRect sourceRect(0, 0, 0, 0);
+ if (mImageHost &&
+ mImageHost->IsAttached()) {
+ IntSize size = mImageHost->GetImageSize();
+ sourceRect.SizeTo(size.width, size.height);
+ }
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+
+ if (mScaleMode != ScaleMode::SCALE_NONE &&
+ sourceRect.width != 0.0 && sourceRect.height != 0.0) {
+ NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+ "No other scalemodes than stretch and none supported yet.");
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
+
+ mEffectiveTransformForBuffer =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ } else {
+ mEffectiveTransformForBuffer = mEffectiveTransform;
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool
+ImageLayerComposite::IsOpaque()
+{
+ if (!mImageHost ||
+ !mImageHost->IsAttached()) {
+ return false;
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ return mImageHost->IsOpaque();
+ }
+ return false;
+}
+
+nsIntRegion
+ImageLayerComposite::GetFullyRenderedRegion()
+{
+ if (!mImageHost ||
+ !mImageHost->IsAttached()) {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ nsIntRegion shadowVisibleRegion;
+ shadowVisibleRegion.And(GetShadowVisibleRegion().ToUnknownRegion(), nsIntRegion(gfx::IntRect(0, 0, mScaleToSize.width, mScaleToSize.height)));
+ return shadowVisibleRegion;
+ }
+
+ return GetShadowVisibleRegion().ToUnknownRegion();
+}
+
+CompositableHost*
+ImageLayerComposite::GetCompositableHost()
+{
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost.get();
+ }
+
+ return nullptr;
+}
+
+void
+ImageLayerComposite::CleanupResources()
+{
+ if (mImageHost) {
+ mImageHost->CleanupResources();
+ mImageHost->Detach(this);
+ }
+ mImageHost = nullptr;
+}
+
+gfx::SamplingFilter
+ImageLayerComposite::GetSamplingFilter()
+{
+ return mSamplingFilter;
+}
+
+void
+ImageLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mImageHost->GenEffect(GetSamplingFilter());
+}
+
+void
+ImageLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ ImageLayer::PrintInfo(aStream, aPrefix);
+ if (mImageHost && mImageHost->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mImageHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/ImageLayerComposite.h b/system/graphics/layers/composite/ImageLayerComposite.h
new file mode 100644
index 000000000..445917a75
--- /dev/null
+++ b/system/graphics/layers/composite/ImageLayerComposite.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ImageLayerComposite_H
+#define GFX_ImageLayerComposite_H
+
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "nscore.h" // for nsACString
+#include "CompositableHost.h" // for CompositableHost
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+class Layer;
+
+class ImageLayerComposite : public ImageLayer,
+ public LayerComposite
+{
+ typedef gl::TextureImage TextureImage;
+
+public:
+ explicit ImageLayerComposite(LayerManagerComposite* aManager);
+
+protected:
+ virtual ~ImageLayerComposite();
+
+public:
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void Disconnect() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
+
+ virtual void CleanupResources() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual const char* Name() const override { return "ImageLayerComposite"; }
+
+ virtual bool IsOpaque() override;
+
+ virtual nsIntRegion GetFullyRenderedRegion() override;
+
+protected:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+private:
+ RefPtr<ImageHost> mImageHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ImageLayerComposite_H */
diff --git a/system/graphics/layers/composite/LayerManagerComposite.cpp b/system/graphics/layers/composite/LayerManagerComposite.cpp
new file mode 100644
index 000000000..98bc777e4
--- /dev/null
+++ b/system/graphics/layers/composite/LayerManagerComposite.cpp
@@ -0,0 +1,1210 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerManagerComposite.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t, uint32_t
+#include "CanvasLayerComposite.h" // for CanvasLayerComposite
+#include "ColorLayerComposite.h" // for ColorLayerComposite
+#include "Composer2D.h" // for Composer2D
+#include "CompositableHost.h" // for CompositableHost
+#include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc
+#include "FPSCounter.h" // for FPSState, FPSCounter
+#include "FrameMetrics.h" // for FrameMetrics
+#include "GeckoProfiler.h" // for profiler_set_frame_number, etc
+#include "ImageLayerComposite.h" // for ImageLayerComposite
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "LayerScope.h" // for LayerScope Tool
+#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "TiledContentHost.h"
+#include "Units.h" // for ScreenIntRect
+#include "UnitTransforms.h" // for ViewAs
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for frame color util
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext
+#include "ipc/CompositorBench.h" // for CompositorBench
+#include "ipc/ShadowLayerUtils.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsAppRunner.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#include "GeckoProfiler.h"
+#include "TextRenderer.h" // for TextRenderer
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+#ifdef USE_SKIA
+#include "PaintCounter.h" // For PaintCounter
+#endif
+
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ImageLayer;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+static LayerComposite*
+ToLayerComposite(Layer* aLayer)
+{
+ return static_cast<LayerComposite*>(aLayer->ImplData());
+}
+
+static void ClearSubtree(Layer* aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ ToLayerComposite(layer)->CleanupResources();
+ });
+}
+
+void
+LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ Layer* subtree = aSubtree ? aSubtree : mRoot.get();
+ if (!subtree) {
+ return;
+ }
+
+ ClearSubtree(subtree);
+ // FIXME [bjacob]
+ // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
+ // Do we need that?
+}
+
+/**
+ * LayerManagerComposite
+ */
+LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
+: mWarningLevel(0.0f)
+, mUnusedApzTransformWarning(false)
+, mDisabledApzWarning(false)
+, mCompositor(aCompositor)
+, mInTransaction(false)
+, mIsCompositorReady(false)
+, mDebugOverlayWantsNextFrame(false)
+, mGeometryChanged(true)
+, mLastFrameMissedHWC(false)
+, mWindowOverlayChanged(false)
+, mLastPaintTime(TimeDuration::Forever())
+, mRenderStartTime(TimeStamp::Now())
+{
+ mTextRenderer = new TextRenderer(aCompositor);
+ MOZ_ASSERT(aCompositor);
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+}
+
+LayerManagerComposite::~LayerManagerComposite()
+{
+ Destroy();
+}
+
+
+void
+LayerManagerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mCompositor->GetWidget()->CleanupWindowEffects();
+ if (mRoot) {
+ RootLayer()->Destroy();
+ }
+ mRoot = nullptr;
+ mClonedLayerTreeProperties = nullptr;
+ mDestroyed = true;
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+ }
+}
+
+void
+LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect)
+{
+ mRenderBounds = aRect;
+}
+
+bool
+LayerManagerComposite::AreComponentAlphaLayersEnabled()
+{
+ return mCompositor->GetBackendType() != LayersBackend::LAYERS_BASIC &&
+ mCompositor->SupportsEffect(EffectTypes::COMPONENT_ALPHA) &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+bool
+LayerManagerComposite::BeginTransaction()
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return false;
+ }
+
+ mIsCompositorReady = true;
+ return true;
+}
+
+void
+LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const IntRect& aRect)
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return;
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ mIsCompositorReady = true;
+ mCompositor->SetTargetContext(aTarget, aRect);
+ mTarget = aTarget;
+ mTargetBounds = aRect;
+}
+
+/**
+ * Get accumulated transform of from the context creating layer to the
+ * given layer.
+ */
+static Matrix4x4
+GetAccTransformIn3DContext(Layer* aLayer) {
+ Matrix4x4 transform = aLayer->GetLocalTransform();
+ for (Layer* layer = aLayer->GetParent();
+ layer && layer->Extend3DContext();
+ layer = layer->GetParent()) {
+ transform = transform * layer->GetLocalTransform();
+ }
+ return transform;
+}
+
+void
+LayerManagerComposite::PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors)
+{
+ if (aLayer->Extend3DContext()) {
+ // For layers participating 3D rendering context, their visible
+ // region should be empty (invisible), so we pass through them
+ // without doing anything.
+
+ // Direct children of the establisher may have a clip, becaue the
+ // item containing it; ex. of nsHTMLScrollFrame, may give it one.
+ Maybe<ParentLayerIntRect> layerClip =
+ aLayer->AsLayerComposite()->GetShadowClipRect();
+ Maybe<ParentLayerIntRect> ancestorClipForChildren =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "Only direct children of the establisher could have a clip");
+
+ for (Layer* child = aLayer->GetLastChild();
+ child;
+ child = child->GetPrevSibling()) {
+ PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
+ ancestorClipForChildren);
+ }
+ return;
+ }
+
+ nsIntRegion localOpaque;
+ // Treat layers on the path to the root of the 3D rendering context as
+ // a giant layer if it is a leaf.
+ Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
+ Matrix transform2d;
+ Maybe<IntPoint> integerTranslation;
+ // If aLayer has a simple transform (only an integer translation) then we
+ // can easily convert aOpaqueRegion into pre-transform coordinates and include
+ // that region.
+ if (transform.Is2D(&transform2d)) {
+ if (transform2d.IsIntegerTranslation()) {
+ integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
+ localOpaque = aOpaqueRegion;
+ localOpaque.MoveBy(-*integerTranslation);
+ }
+ }
+
+ // Compute a clip that's the combination of our layer clip with the clip
+ // from our ancestors.
+ LayerComposite* composite = aLayer->AsLayerComposite();
+ Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "The layer with a clip should not participate "
+ "a 3D rendering context");
+ Maybe<ParentLayerIntRect> outsideClip =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+
+ // Convert the combined clip into our pre-transform coordinate space, so
+ // that it can later be intersected with our visible region.
+ // If our transform is a perspective, there's no meaningful insideClip rect
+ // we can compute (it would need to be a cone).
+ Maybe<LayerIntRect> insideClip;
+ if (outsideClip && !transform.HasPerspectiveComponent()) {
+ Matrix4x4 inverse = transform;
+ if (inverse.Invert()) {
+ Maybe<LayerRect> insideClipFloat =
+ UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse),
+ ParentLayerRect(*outsideClip),
+ LayerRect::MaxIntRect());
+ if (insideClipFloat) {
+ insideClipFloat->RoundOut();
+ LayerIntRect insideClipInt;
+ if (insideClipFloat->ToIntRect(&insideClipInt)) {
+ insideClip = Some(insideClipInt);
+ }
+ }
+ }
+ }
+
+ Maybe<ParentLayerIntRect> ancestorClipForChildren;
+ if (insideClip) {
+ ancestorClipForChildren =
+ Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
+ }
+
+ // Save the value of localOpaque, which currently stores the region obscured
+ // by siblings (and uncles and such), before our descendants contribute to it.
+ nsIntRegion obscured = localOpaque;
+
+ // Recurse on our descendants, in front-to-back order. In this process:
+ // - Occlusions are computed for them, and they contribute to localOpaque.
+ // - They recalculate their visible regions, taking ancestorClipForChildren
+ // into account, and accumulate them into descendantsVisibleRegion.
+ LayerIntRegion descendantsVisibleRegion;
+ bool hasPreserve3DChild = false;
+ for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+ PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
+ if (child->Extend3DContext()) {
+ hasPreserve3DChild = true;
+ }
+ }
+
+ // Recalculate our visible region.
+ LayerIntRegion visible = composite->GetShadowVisibleRegion();
+
+ // If we have descendants, throw away the visible region stored on this
+ // layer, and use the region accumulated by our descendants instead.
+ if (aLayer->GetFirstChild() && !hasPreserve3DChild) {
+ visible = descendantsVisibleRegion;
+ }
+
+ // Subtract any areas that we know to be opaque.
+ if (!obscured.IsEmpty()) {
+ visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
+ }
+
+ // Clip the visible region using the combined clip.
+ if (insideClip) {
+ visible.AndWith(*insideClip);
+ }
+ composite->SetShadowVisibleRegion(visible);
+
+ // Transform the newly calculated visible region into our parent's space,
+ // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
+ // for the caller to use.
+ ParentLayerIntRegion visibleParentSpace = TransformBy(
+ ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
+ if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
+ visibleParentSpace.AndWith(*clipRect);
+ }
+ aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
+ PixelCastJustification::MovingDownToChildren));
+
+ // If we have a simple transform, then we can add our opaque area into
+ // aOpaqueRegion.
+ if (integerTranslation &&
+ !aLayer->HasMaskLayers() &&
+ aLayer->IsOpaqueForVisibility()) {
+ if (aLayer->IsOpaque()) {
+ localOpaque.OrWith(composite->GetFullyRenderedRegion());
+ }
+ localOpaque.MoveBy(*integerTranslation);
+ if (layerClip) {
+ localOpaque.AndWith(layerClip->ToUnknownRect());
+ }
+ aOpaqueRegion.OrWith(localOpaque);
+ }
+}
+
+void
+LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags)
+{
+ NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
+ NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
+ "Shouldn't get END_NO_COMPOSITE here");
+ mInTransaction = false;
+ mRenderStartTime = TimeStamp::Now();
+
+ if (!mIsCompositorReady) {
+ return;
+ }
+ mIsCompositorReady = false;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ // Set composition timestamp here because we need it in
+ // ComputeEffectiveTransforms (so the correct video frame size is picked) and
+ // also to compute invalid regions properly.
+ mCompositor->SetCompositionTime(aTimeStamp);
+
+ if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
+ MOZ_ASSERT(!aTimeStamp.IsNull());
+ UpdateAndRender();
+ mCompositor->FlushPendingNotifyNotUsed();
+ } else {
+ // Modified the layer tree.
+ mGeometryChanged = true;
+ }
+
+ mCompositor->ClearTargetContext();
+ mTarget = nullptr;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+}
+
+void
+LayerManagerComposite::UpdateAndRender()
+{
+ nsIntRegion invalid;
+ bool didEffectiveTransforms = false;
+
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+
+ if (mClonedLayerTreeProperties) {
+ // Effective transforms are needed by ComputeDifferences().
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ didEffectiveTransforms = true;
+
+ // We need to compute layer tree differences even if we're not going to
+ // immediately use the resulting damage area, since ComputeDifferences
+ // is also responsible for invalidates intermediate surfaces in
+ // ContainerLayers.
+ nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
+
+ if (mTarget) {
+ // Since we're composing to an external target, we're not going to use
+ // the damage region from layers changes - we want to composite
+ // everything in the target bounds. Instead we accumulate the layers
+ // damage region for the next window composite.
+ mInvalidRegion.Or(mInvalidRegion, changed);
+ } else {
+ invalid = Move(changed);
+ }
+ }
+
+ if (mTarget) {
+ invalid.Or(invalid, mTargetBounds);
+ } else {
+ // If we didn't have a previous layer tree, invalidate the entire render
+ // area.
+ if (!mClonedLayerTreeProperties) {
+ invalid.Or(invalid, mRenderBounds);
+ }
+
+ // Add any additional invalid rects from the window manager or previous
+ // damage computed during ComposeToTarget().
+ invalid.Or(invalid, mInvalidRegion);
+ mInvalidRegion.SetEmpty();
+ }
+
+ if (invalid.IsEmpty() && !mWindowOverlayChanged) {
+ // Composition requested, but nothing has changed. Don't do any work.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+ return;
+ }
+
+ // We don't want our debug overlay to cause more frames to happen
+ // so we will invalidate after we've decided if something changed.
+ InvalidateDebugOverlay(invalid, mRenderBounds);
+
+ if (!didEffectiveTransforms) {
+ // The results of our drawing always go directly into a pixel buffer,
+ // so we don't need to pass any global transform here.
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ }
+
+ Render(invalid, opaque);
+ mGeometryChanged = false;
+ mWindowOverlayChanged = false;
+
+ // Update cached layer tree information.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+}
+
+already_AddRefed<DrawTarget>
+LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<PaintedLayer>
+LayerManagerComposite::CreatePaintedLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ContainerLayer>
+LayerManagerComposite::CreateContainerLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ImageLayer>
+LayerManagerComposite::CreateImageLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<ColorLayer>
+LayerManagerComposite::CreateColorLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ColorLayer> layer = new ColorLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<CanvasLayer>
+LayerManagerComposite::CreateCanvasLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+LayerComposite*
+LayerManagerComposite::RootLayer() const
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+
+ return ToLayerComposite(mRoot);
+}
+
+#ifdef MOZ_PROFILING
+// Only build the QR feature when profiling to avoid bloating
+// our data section.
+// This table was generated using qrencode and is a binary
+// encoding of the qrcodes 0-255.
+#include "qrcode_table.h"
+#endif
+
+void
+LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ if (drawFps || drawFrameCounter) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256));
+ }
+ if (drawFrameColorBars) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height));
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect()));
+ }
+#endif
+}
+
+#ifdef USE_SKIA
+void
+LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor)
+{
+ if (!mPaintCounter) {
+ mPaintCounter = new PaintCounter();
+ }
+
+ TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
+ mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
+}
+#endif
+
+static uint16_t sFrameCount = 0;
+void
+LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ TimeStamp now = TimeStamp::Now();
+
+ if (drawFps) {
+ if (!mFPS) {
+ mFPS = MakeUnique<FPSState>();
+ }
+
+ float alpha = 1;
+ float fillRatio = mCompositor->GetFillRatio();
+ mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 1, 2, unsigned(fillRatio), mCompositor);
+
+ if (mUnusedApzTransformWarning) {
+ // If we have an unused APZ transform on this composite, draw a 20x20 red box
+ // in the top-right corner
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mUnusedApzTransformWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+ if (mDisabledApzWarning) {
+ // If we have a disabled APZ on this composite, draw a 20x20 yellow box
+ // in the top-right corner, to the left of the unused-apz-transform
+ // warning box
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 40, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mDisabledApzWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+
+
+ // Each frame is invalidate by the previous frame for simplicity
+ } else {
+ mFPS = nullptr;
+ }
+
+ if (drawFrameColorBars) {
+ gfx::IntRect sideRect(0, 0, 10, aBounds.height);
+
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount));
+ mCompositor->DrawQuad(Rect(sideRect),
+ sideRect,
+ effects,
+ 1.0,
+ gfx::Matrix4x4());
+
+ }
+
+#ifdef MOZ_PROFILING
+ if (drawFrameCounter) {
+ profiler_set_frame_number(sFrameCount);
+ const char* qr = sQRCodeTable[sFrameCount%256];
+
+ int size = 21;
+ int padding = 2;
+ float opacity = 1.0;
+ const uint16_t bitWidth = 5;
+ gfx::IntRect clip(0,0, bitWidth*640, bitWidth*640);
+
+ // Draw the white squares at once
+ gfx::Color bitColor(1.0, 1.0, 1.0, 1.0);
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(bitColor);
+ int totalSize = (size + padding * 2) * bitWidth;
+ mCompositor->DrawQuad(gfx::Rect(0, 0, totalSize, totalSize),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+
+ // Draw a black square for every bit set in qr[index]
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1.0));
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ // Select the right bit from the binary encoding
+ int currBit = 128 >> ((x + y * 21) % 8);
+ int i = (x + y * 21) / 8;
+ if (qr[i] & currBit) {
+ mCompositor->DrawQuad(gfx::Rect(bitWidth * (x + padding),
+ bitWidth * (y + padding),
+ bitWidth, bitWidth),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+ }
+ }
+ }
+
+ }
+#endif
+
+ if (drawFrameColorBars || drawFrameCounter) {
+ // We intentionally overflow at 2^16.
+ sFrameCount++;
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ DrawPaintTimes(mCompositor);
+ }
+#endif
+}
+
+RefPtr<CompositingRenderTarget>
+LayerManagerComposite::PushGroupForLayerEffects()
+{
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(gfxPrefs::LayersEffectInvert() ||
+ gfxPrefs::LayersEffectGrayscale() ||
+ gfxPrefs::LayersEffectContrast() != 0.0);
+
+ RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget();
+ // make our render target the same size as the destination target
+ // so that we don't have to change size if the drawing area changes.
+ IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize());
+ // XXX: I'm not sure if this is true or not...
+ MOZ_ASSERT(rect.x == 0 && rect.y == 0);
+ if (!mTwoPassTmpTarget ||
+ mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() ||
+ mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) {
+ mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE);
+ }
+ MOZ_ASSERT(mTwoPassTmpTarget);
+ mCompositor->SetRenderTarget(mTwoPassTmpTarget);
+ return previousTarget;
+}
+void
+LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect)
+{
+ MOZ_ASSERT(mTwoPassTmpTarget);
+
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0);
+
+ mCompositor->SetRenderTarget(aPreviousTarget);
+
+ EffectChain effectChain(RootLayer());
+ Matrix5x4 effectMatrix;
+ if (aGrayscaleEffect) {
+ // R' = G' = B' = luminance
+ // R' = 0.2126*R + 0.7152*G + 0.0722*B
+ // G' = 0.2126*R + 0.7152*G + 0.0722*B
+ // B' = 0.2126*R + 0.7152*G + 0.0722*B
+ Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0,
+ 0.7152f, 0.7152f, 0.7152f, 0,
+ 0.0722f, 0.0722f, 0.0722f, 0,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0);
+ effectMatrix = grayscaleMatrix;
+ }
+
+ if (aInvertEffect) {
+ // R' = 1 - R
+ // G' = 1 - G
+ // B' = 1 - B
+ Matrix5x4 colorInvertMatrix(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1,
+ 1, 1, 1, 0);
+ effectMatrix = effectMatrix * colorInvertMatrix;
+ }
+
+ if (aContrastEffect != 0.0) {
+ // Multiplying with:
+ // R' = (1 + c) * (R - 0.5) + 0.5
+ // G' = (1 + c) * (G - 0.5) + 0.5
+ // B' = (1 + c) * (B - 0.5) + 0.5
+ float cP1 = aContrastEffect + 1;
+ float hc = 0.5*aContrastEffect;
+ Matrix5x4 contrastMatrix( cP1, 0, 0, 0,
+ 0, cP1, 0, 0,
+ 0, 0, cP1, 0,
+ 0, 0, 0, 1,
+ -hc, -hc, -hc, 0);
+ effectMatrix = effectMatrix * contrastMatrix;
+ }
+
+ effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget);
+ effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix);
+
+ mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), aClipRect, effectChain, 1.,
+ Matrix4x4());
+}
+
+// Used to clear the 'mLayerComposited' flag at the beginning of each Render().
+static void
+ClearLayerFlags(Layer* aLayer) {
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ if (layer->AsLayerComposite()) {
+ layer->AsLayerComposite()->SetLayerComposited(false);
+ }
+ });
+}
+
+void
+LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
+{
+ PROFILER_LABEL("LayerManagerComposite", "Render",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ ClearLayerFlags(mRoot);
+
+ // At this time, it doesn't really matter if these preferences change
+ // during the execution of the function; we should be safe in all
+ // permutations. However, may as well just get the values onces and
+ // then use them, just in case the consistency becomes important in
+ // the future.
+ bool invertVal = gfxPrefs::LayersEffectInvert();
+ bool grayscaleVal = gfxPrefs::LayersEffectGrayscale();
+ float contrastVal = gfxPrefs::LayersEffectContrast();
+ bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
+
+ // Set LayerScope begin/end frame
+ LayerScopeAutoFrame frame(PR_Now());
+
+ // Dump to console
+ if (gfxPrefs::LayersDump()) {
+ this->Dump(/* aSorted= */true);
+ } else if (profiler_feature_active("layersdump")) {
+ std::stringstream ss;
+ Dump(ss);
+ profiler_log(ss.str().c_str());
+ }
+
+ // Dump to LayerScope Viewer
+ if (LayerScope::CheckSendable()) {
+ // Create a LayersPacket, dump Layers into it and transfer the
+ // packet('s ownership) to LayerScope.
+ auto packet = MakeUnique<layerscope::Packet>();
+ layerscope::LayersPacket* layersPacket = packet->mutable_layers();
+ this->Dump(layersPacket);
+ LayerScope::SendLayerDump(Move(packet));
+ }
+
+ /** Our more efficient but less powerful alter ego, if one is available. */
+ RefPtr<Composer2D> composer2D;
+ composer2D = mCompositor->GetWidget()->GetComposer2D();
+
+ // We can't use composert2D if we have layer effects
+ if (!mTarget && !haveLayerEffects &&
+ gfxPrefs::Composer2DCompositionEnabled() &&
+ composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot,
+ mCompositor->GetWidget()->RealWidget(),
+ mGeometryChanged,
+ mCompositor->HasImageHostOverlays()))
+ {
+ LayerScope::SetHWComposed();
+ if (mFPS) {
+ double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
+ if (gfxPrefs::LayersDrawFPS()) {
+ printf_stderr("HWComposer: FPS is %g\n", fps);
+ }
+ }
+ mCompositor->EndFrameForExternalComposition(Matrix());
+ mLastFrameMissedHWC = false;
+ return;
+ } else if (!mTarget && !haveLayerEffects) {
+ mLastFrameMissedHWC = !!composer2D;
+ }
+
+ mozilla::widget::WidgetRenderingContext widgetContext;
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "PreRender",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
+ return;
+ }
+ }
+
+ ParentLayerIntRect clipRect;
+ IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
+ IntRect actualBounds;
+
+ CompositorBench(mCompositor, bounds);
+
+ MOZ_ASSERT(mRoot->GetOpacity() == 1);
+ if (mRoot->GetClipRect()) {
+ clipRect = *mRoot->GetClipRect();
+ IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
+ } else {
+ gfx::IntRect rect;
+ mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds);
+ clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ if (actualBounds.IsEmpty()) {
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return;
+ }
+
+ // Allow widget to render a custom background.
+ mCompositor->GetWidget()->DrawWindowUnderlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ RefPtr<CompositingRenderTarget> previousTarget;
+ if (haveLayerEffects) {
+ previousTarget = PushGroupForLayerEffects();
+ } else {
+ mTwoPassTmpTarget = nullptr;
+ }
+
+ // Render our layers.
+ RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
+ RootLayer()->RenderLayer(clipRect.ToUnknownRect());
+
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+
+ if (mTwoPassTmpTarget) {
+ MOZ_ASSERT(haveLayerEffects);
+ PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(),
+ grayscaleVal, invertVal, contrastVal);
+ }
+
+ // Allow widget to render a custom foreground.
+ mCompositor->GetWidget()->DrawWindowOverlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ // Debugging
+ RenderDebugOverlay(actualBounds);
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "EndFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mCompositor->EndFrame();
+
+ // Call after EndFrame()
+ mCompositor->SetDispAcquireFence(mRoot);
+ }
+
+ if (composer2D) {
+ composer2D->Render(mCompositor->GetWidget()->RealWidget());
+ }
+
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+
+ RecordFrame();
+}
+
+already_AddRefed<PaintedLayerComposite>
+LayerManagerComposite::CreatePaintedLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<PaintedLayerComposite>(new PaintedLayerComposite(this)).forget();
+}
+
+already_AddRefed<ContainerLayerComposite>
+LayerManagerComposite::CreateContainerLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
+}
+
+already_AddRefed<ImageLayerComposite>
+LayerManagerComposite::CreateImageLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
+}
+
+already_AddRefed<ColorLayerComposite>
+LayerManagerComposite::CreateColorLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
+}
+
+already_AddRefed<CanvasLayerComposite>
+LayerManagerComposite::CreateCanvasLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
+}
+
+already_AddRefed<RefLayerComposite>
+LayerManagerComposite::CreateRefLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
+}
+
+LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
+ EffectChain& aEffects)
+ : mCompositable(nullptr), mFailed(false)
+{
+ if (!aMaskLayer) {
+ return;
+ }
+
+ mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost();
+ if (!mCompositable) {
+ NS_WARNING("Mask layer with no compositable host");
+ mFailed = true;
+ return;
+ }
+
+ if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform())) {
+ mCompositable = nullptr;
+ mFailed = true;
+ }
+}
+
+LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect()
+{
+ if (!mCompositable) {
+ return;
+ }
+
+ mCompositable->RemoveMaskEffect();
+}
+
+void
+LayerManagerComposite::ChangeCompositor(Compositor* aNewCompositor)
+{
+ mCompositor = aNewCompositor;
+ mTextRenderer = new TextRenderer(aNewCompositor);
+ mTwoPassTmpTarget = nullptr;
+}
+
+LayerComposite::LayerComposite(LayerManagerComposite *aManager)
+ : mCompositeManager(aManager)
+ , mCompositor(aManager->GetCompositor())
+ , mShadowOpacity(1.0)
+ , mShadowTransformSetByAnimation(false)
+ , mShadowOpacitySetByAnimation(false)
+ , mDestroyed(false)
+ , mLayerComposited(false)
+{ }
+
+LayerComposite::~LayerComposite()
+{
+}
+
+void
+LayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void
+LayerComposite::AddBlendModeEffect(EffectChain& aEffectChain)
+{
+ gfx::CompositionOp blendMode = GetLayer()->GetEffectiveMixBlendMode();
+ if (blendMode == gfx::CompositionOp::OP_OVER) {
+ return;
+ }
+
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(blendMode);
+ return;
+}
+
+bool
+LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize)
+{
+ return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width,
+ aSize.height));
+}
+
+void
+LayerManagerComposite::NotifyShadowTreeTransaction()
+{
+ if (mFPS) {
+ mFPS->NotifyShadowTreeTransaction();
+ }
+}
+
+void
+LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ mCompositeManager = aManager;
+ mCompositor = aManager->GetCompositor();
+}
+
+bool
+LayerManagerComposite::AsyncPanZoomEnabled() const
+{
+ if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
+ return bridge->AsyncPanZoomEnabled();
+ }
+ return false;
+}
+
+nsIntRegion
+LayerComposite::GetFullyRenderedRegion() {
+ if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
+ : nullptr) {
+ nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion();
+ // Discard the region which hasn't been drawn yet when doing
+ // progressive drawing. Note that if the shadow visible region
+ // shrunk the tiled valig region may not have discarded this yet.
+ shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
+ return shadowVisibleRegion;
+ } else {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+}
+
+Matrix4x4
+LayerComposite::GetShadowTransform() {
+ Matrix4x4 transform = mShadowTransform;
+ Layer* layer = GetLayer();
+
+ transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
+ if (const ContainerLayer* c = layer->AsContainerLayer()) {
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ }
+
+ return transform;
+}
+
+bool
+LayerComposite::HasStaleCompositor() const
+{
+ return mCompositeManager->GetCompositor() != mCompositor;
+}
+
+static bool
+LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
+{
+ bool answer = false;
+ for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
+ if (!i.Metrics().IsScrollable()) {
+ continue;
+ }
+ if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
+ if (aOutColor) {
+ *aOutColor = i.Metadata().GetBackgroundColor();
+ }
+ answer = true;
+ break;
+ }
+ break;
+ }
+ return answer;
+}
+
+bool
+LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor)
+{
+ return GetLayer()->Manager()->AsyncPanZoomEnabled() &&
+ (GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ GetLayer()->IsOpaqueForVisibility() &&
+ LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor);
+}
+
+#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/LayerManagerComposite.h b/system/graphics/layers/composite/LayerManagerComposite.h
new file mode 100644
index 000000000..ea4b203ac
--- /dev/null
+++ b/system/graphics/layers/composite/LayerManagerComposite.h
@@ -0,0 +1,684 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LayerManagerComposite_H
+#define GFX_LayerManagerComposite_H
+
+#include <stdint.h> // for int32_t, uint32_t
+#include "GLDefs.h" // for GLenum
+#include "Layers.h"
+#include "Units.h" // for ParentLayerIntRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+#include "LayerTreeInvalidation.h"
+
+class gfxContext;
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CanvasLayerComposite;
+class ColorLayerComposite;
+class CompositableHost;
+class Compositor;
+class ContainerLayerComposite;
+struct EffectChain;
+class ImageLayer;
+class ImageLayerComposite;
+class LayerComposite;
+class RefLayerComposite;
+class PaintedLayerComposite;
+class TextRenderer;
+class CompositingRenderTarget;
+struct FPSState;
+class PaintCounter;
+
+static const int kVisualWarningDuration = 150; // ms
+
+class LayerManagerComposite final : public LayerManager
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+public:
+ explicit LayerManagerComposite(Compositor* aCompositor);
+ ~LayerManagerComposite();
+
+ virtual void Destroy() override;
+
+ /**
+ * Sets the clipping region for this layer manager. This is important on
+ * windows because using OGL we no longer have GDI's native clipping. Therefor
+ * widget must tell us what part of the screen is being invalidated,
+ * and we should clip to this.
+ *
+ * \param aClippingRegion Region to clip to. Setting an empty region
+ * will disable clipping.
+ */
+ void SetClippingRegion(const nsIntRegion& aClippingRegion)
+ {
+ mClippingRegion = aClippingRegion;
+ }
+
+ /**
+ * LayerManager implementation.
+ */
+ virtual LayerManagerComposite* AsLayerManagerComposite() override
+ {
+ return this;
+ }
+
+ void UpdateRenderBounds(const gfx::IntRect& aRect);
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override
+ {
+ MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
+ return false;
+ }
+ void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aRect);
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ return false;
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ }
+ void EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ virtual void SetRoot(Layer* aLayer) override { mRoot = aLayer; }
+
+ // XXX[nrc]: never called, we should move this logic to ClientLayerManager
+ // (bug 946926).
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
+
+ virtual int32_t GetMaxTextureSize() const override
+ {
+ MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
+ }
+
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ already_AddRefed<PaintedLayerComposite> CreatePaintedLayerComposite();
+ already_AddRefed<ContainerLayerComposite> CreateContainerLayerComposite();
+ already_AddRefed<ImageLayerComposite> CreateImageLayerComposite();
+ already_AddRefed<ColorLayerComposite> CreateColorLayerComposite();
+ already_AddRefed<CanvasLayerComposite> CreateCanvasLayerComposite();
+ already_AddRefed<RefLayerComposite> CreateRefLayerComposite();
+
+ virtual LayersBackend GetBackendType() override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+ virtual void GetBackendName(nsAString& name) override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalMaskDrawTarget(const IntSize &aSize) override;
+
+ virtual const char* Name() const override { return ""; }
+
+ /**
+ * Post-processes layers before composition. This performs the following:
+ *
+ * - Applies occlusion culling. This restricts the shadow visible region
+ * of layers that are covered with opaque content.
+ * |aOpaqueRegion| is the region already known to be covered with opaque
+ * content, in the post-transform coordinate space of aLayer.
+ *
+ * - Recomputes visible regions to account for async transforms.
+ * Each layer accumulates into |aVisibleRegion| its post-transform
+ * (including async transforms) visible region.
+ */
+ void PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors);
+
+ /**
+ * RAII helper class to add a mask effect with the compositable from aMaskLayer
+ * to the EffectChain aEffect and notify the compositable when we are done.
+ */
+ class AutoAddMaskEffect
+ {
+ public:
+ AutoAddMaskEffect(Layer* aMaskLayer,
+ EffectChain& aEffect);
+ ~AutoAddMaskEffect();
+
+ bool Failed() const { return mFailed; }
+ private:
+ CompositableHost* mCompositable;
+ bool mFailed;
+ };
+
+ /**
+ * returns true if PlatformAllocBuffer will return a buffer that supports
+ * direct texturing
+ */
+ static bool SupportsDirectTexturing();
+
+ static void PlatformSyncBeforeReplyUpdate();
+
+ void AddInvalidRegion(const nsIntRegion& aRegion)
+ {
+ mInvalidRegion.Or(mInvalidRegion, aRegion);
+ }
+
+ void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
+ const Maybe<uint32_t>& aPresShellId)
+ {
+ for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+ if (iter.Key().mLayersId == aLayersId &&
+ (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
+ iter.Remove();
+ }
+ }
+ }
+
+ void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+ {
+ CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
+ MOZ_ASSERT(regionForScrollFrame);
+
+ *regionForScrollFrame = aRegion;
+ }
+
+ CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
+ {
+ return mVisibleRegions.Get(aGuid);
+ }
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ // Called by CompositorBridgeParent when a new compositor has been created due
+ // to a device reset. The layer manager must clear any cached resources
+ // attached to the old compositor, and make a best effort at ignoring
+ // layer or texture updates against the old compositor.
+ void ChangeCompositor(Compositor* aNewCompositor);
+
+ /**
+ * LayerManagerComposite provides sophisticated debug overlays
+ * that can request a next frame.
+ */
+ bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+ void SetDebugOverlayWantsNextFrame(bool aVal)
+ { mDebugOverlayWantsNextFrame = aVal; }
+
+ void NotifyShadowTreeTransaction();
+
+ TextRenderer* GetTextRenderer() { return mTextRenderer; }
+
+ /**
+ * Add an on frame warning.
+ * @param severity ranges from 0 to 1. It's used to compute the warning color.
+ */
+ void VisualFrameWarning(float severity) {
+ mozilla::TimeStamp now = TimeStamp::Now();
+ if (mWarnTime.IsNull() ||
+ severity > mWarningLevel ||
+ mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) < now) {
+ mWarnTime = now;
+ mWarningLevel = severity;
+ }
+ }
+
+ void UnusedApzTransformWarning() {
+ mUnusedApzTransformWarning = true;
+ }
+ void DisabledApzWarning() {
+ mDisabledApzWarning = true;
+ }
+
+ bool LastFrameMissedHWC() { return mLastFrameMissedHWC; }
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void AppendImageCompositeNotification(const ImageCompositeNotification& aNotification)
+ {
+ // Only send composite notifications when we're drawing to the screen,
+ // because that's what they mean.
+ // Also when we're not drawing to the screen, DidComposite will not be
+ // called to extract and send these notifications, so they might linger
+ // and contain stale ImageContainerParent pointers.
+ if (!mCompositor->GetTargetContext()) {
+ mImageCompositeNotifications.AppendElement(aNotification);
+ }
+ }
+ void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+ {
+ aNotifications->AppendElements(Move(mImageCompositeNotifications));
+ }
+
+ // Indicate that we need to composite even if nothing in our layers has
+ // changed, so that the widget can draw something different in its window
+ // overlay.
+ void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+ void ForcePresent() { mCompositor->ForcePresent(); }
+
+ void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+
+private:
+ /** Region we're clipping our current drawing to. */
+ nsIntRegion mClippingRegion;
+ gfx::IntRect mRenderBounds;
+
+ /** Current root layer. */
+ LayerComposite* RootLayer() const;
+
+ /**
+ * Update the invalid region and render it.
+ */
+ void UpdateAndRender();
+
+ /**
+ * Render the current layer tree to the active target.
+ */
+ void Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion);
+
+ /**
+ * We need to know our invalid region before we're ready to render.
+ */
+ void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const gfx::IntRect& aBounds);
+
+ /**
+ * Render debug overlays such as the FPS/FrameCounter above the frame.
+ */
+ void RenderDebugOverlay(const gfx::IntRect& aBounds);
+
+
+ RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
+ void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ gfx::IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect);
+
+ void ChangeCompositorInternal(Compositor* aNewCompositor);
+
+ float mWarningLevel;
+ mozilla::TimeStamp mWarnTime;
+ bool mUnusedApzTransformWarning;
+ bool mDisabledApzWarning;
+ RefPtr<Compositor> mCompositor;
+ UniquePtr<LayerProperties> mClonedLayerTreeProperties;
+
+ nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
+
+ /**
+ * Context target, nullptr when drawing directly to our swap chain.
+ */
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ nsIntRegion mInvalidRegion;
+
+ typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
+ CSSIntRegion> VisibleRegions;
+ VisibleRegions mVisibleRegions;
+
+ UniquePtr<FPSState> mFPS;
+
+ bool mInTransaction;
+ bool mIsCompositorReady;
+ bool mDebugOverlayWantsNextFrame;
+
+ RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
+ RefPtr<TextRenderer> mTextRenderer;
+ bool mGeometryChanged;
+
+ // Testing property. If hardware composer is supported, this will return
+ // true if the last frame was deemed 'too complicated' to be rendered.
+ bool mLastFrameMissedHWC;
+
+ bool mWindowOverlayChanged;
+ TimeDuration mLastPaintTime;
+ TimeStamp mRenderStartTime;
+
+#ifdef USE_SKIA
+ /**
+ * Render paint and composite times above the frame.
+ */
+ void DrawPaintTimes(Compositor* aCompositor);
+ RefPtr<PaintCounter> mPaintCounter;
+#endif
+};
+
+/**
+ * Composite layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Basic layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree, all rendering is
+ * done by a Compositor (see Compositor.h). As such, composite layers are
+ * platform-independent and can be used on any platform for which there is a
+ * Compositor implementation.
+ *
+ * The composite layer tree reflects exactly the basic layer tree. To
+ * composite to screen, the layer manager walks the layer tree calling render
+ * methods which in turn call into their CompositableHosts' Composite methods.
+ * These call Compositor::DrawQuad to do the rendering.
+ *
+ * Mostly, layers are updated during the layers transaction. This is done from
+ * CompositableClient to CompositableHost without interacting with the layer.
+ *
+ * A reference to the Compositor is stored in LayerManagerComposite.
+ */
+class LayerComposite
+{
+public:
+ explicit LayerComposite(LayerManagerComposite* aManager);
+
+ virtual ~LayerComposite();
+
+ virtual LayerComposite* GetFirstChildComposite()
+ {
+ return nullptr;
+ }
+
+ /* Do NOT call this from the generic LayerComposite destructor. Only from the
+ * concrete class destructor
+ */
+ virtual void Destroy();
+
+ virtual Layer* GetLayer() = 0;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager);
+
+ LayerManagerComposite* GetLayerManager() const { return mCompositeManager; }
+
+ /**
+ * Perform a first pass over the layer tree to render all of the intermediate
+ * surfaces that we can. This allows us to avoid framebuffer switches in the
+ * middle of our render which is inefficient especially on mobile GPUs. This
+ * must be called before RenderLayer.
+ */
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
+
+ // TODO: This should also take RenderTargetIntRect like Prepare.
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
+
+ virtual bool SetCompositableHost(CompositableHost*)
+ {
+ // We must handle this gracefully, see bug 967824
+ NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
+ return false;
+ }
+ virtual CompositableHost* GetCompositableHost() = 0;
+
+ virtual void CleanupResources() = 0;
+
+ virtual void DestroyFrontBuffer() { }
+
+ void AddBlendModeEffect(EffectChain& aEffectChain);
+
+ virtual void GenEffectChain(EffectChain& aEffect) { }
+
+ /**
+ * The following methods are
+ *
+ * CONSTRUCTION PHASE ONLY
+ *
+ * They are analogous to the Layer interface.
+ */
+ void SetShadowVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ mShadowVisibleRegion = aRegion;
+ }
+
+ void SetShadowOpacity(float aOpacity)
+ {
+ mShadowOpacity = aOpacity;
+ }
+ void SetShadowOpacitySetByAnimation(bool aSetByAnimation)
+ {
+ mShadowOpacitySetByAnimation = aSetByAnimation;
+ }
+
+ void SetShadowClipRect(const Maybe<ParentLayerIntRect>& aRect)
+ {
+ mShadowClipRect = aRect;
+ }
+
+ void SetShadowBaseTransform(const gfx::Matrix4x4& aMatrix)
+ {
+ mShadowTransform = aMatrix;
+ }
+ void SetShadowTransformSetByAnimation(bool aSetByAnimation)
+ {
+ mShadowTransformSetByAnimation = aSetByAnimation;
+ }
+
+ void SetLayerComposited(bool value)
+ {
+ mLayerComposited = value;
+ }
+
+ void SetClearRect(const gfx::IntRect& aRect)
+ {
+ mClearRect = aRect;
+ }
+
+ // These getters can be used anytime.
+ float GetShadowOpacity() { return mShadowOpacity; }
+ const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
+ const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
+ const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
+ gfx::Matrix4x4 GetShadowTransform();
+ bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
+ bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
+ bool HasLayerBeenComposited() { return mLayerComposited; }
+ gfx::IntRect GetClearRect() { return mClearRect; }
+
+ // Returns false if the layer is attached to an older compositor.
+ bool HasStaleCompositor() const;
+
+ /**
+ * Return the part of the visible region that has been fully rendered.
+ * While progressive drawing is in progress this region will be
+ * a subset of the shadow visible region.
+ */
+ virtual nsIntRegion GetFullyRenderedRegion();
+
+ /**
+ * Return true if a checkerboarding background color needs to be drawn
+ * for this layer.
+ */
+ bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
+
+protected:
+ gfx::Matrix4x4 mShadowTransform;
+ LayerIntRegion mShadowVisibleRegion;
+ Maybe<ParentLayerIntRect> mShadowClipRect;
+ LayerManagerComposite* mCompositeManager;
+ RefPtr<Compositor> mCompositor;
+ float mShadowOpacity;
+ bool mShadowTransformSetByAnimation;
+ bool mShadowOpacitySetByAnimation;
+ bool mDestroyed;
+ bool mLayerComposited;
+ gfx::IntRect mClearRect;
+};
+
+// Render aLayer using aCompositor and apply all mask layers of aLayer: The
+// layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
+// layers.
+// If more than one mask layer needs to be applied, we use intermediate surfaces
+// (CompositingRenderTargets) for rendering, applying one mask layer at a time.
+// Callers need to provide a callback function aRenderCallback that does the
+// actual rendering of the source. It needs to have the following form:
+// void (EffectChain& effectChain, const Rect& clipRect)
+// aRenderCallback is called exactly once, inside this function, unless aLayer's
+// visible region is completely clipped out (in that case, aRenderCallback won't
+// be called at all).
+// This function calls aLayer->AsLayerComposite()->AddBlendModeEffect for the
+// final rendering pass.
+//
+// (This function should really live in LayerManagerComposite.cpp, but we
+// need to use templates for passing lambdas until bug 1164522 is resolved.)
+template<typename RenderCallbackType>
+void
+RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
+ const gfx::IntRect& aClipRect,
+ RenderCallbackType aRenderCallback)
+{
+ Layer* firstMask = nullptr;
+ size_t maskLayerCount = 0;
+ size_t nextAncestorMaskLayer = 0;
+
+ size_t ancestorMaskLayerCount = aLayer->GetAncestorMaskLayerCount();
+ if (Layer* ownMask = aLayer->GetMaskLayer()) {
+ firstMask = ownMask;
+ maskLayerCount = ancestorMaskLayerCount + 1;
+ nextAncestorMaskLayer = 0;
+ } else if (ancestorMaskLayerCount > 0) {
+ firstMask = aLayer->GetAncestorMaskLayerAt(0);
+ maskLayerCount = ancestorMaskLayerCount;
+ nextAncestorMaskLayer = 1;
+ } else {
+ // no mask layers at all
+ }
+
+ if (maskLayerCount <= 1) {
+ // This is the common case. Render in one pass and return.
+ EffectChain effectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ autoMaskEffect(firstMask, effectChain);
+ aLayer->AsLayerComposite()->AddBlendModeEffect(effectChain);
+ aRenderCallback(effectChain, aClipRect);
+ return;
+ }
+
+ // We have multiple mask layers.
+ // We split our list of mask layers into three parts:
+ // (1) The first mask
+ // (2) The list of intermediate masks (every mask except first and last)
+ // (3) The final mask.
+ // Part (2) can be empty.
+ // For parts (1) and (2) we need to allocate intermediate surfaces to render
+ // into. The final mask gets rendered into the original render target.
+
+ // Calculate the size of the intermediate surfaces.
+ gfx::Rect visibleRect(aLayer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
+ // TODO: Use RenderTargetIntRect and TransformBy here
+ gfx::IntRect surfaceRect =
+ RoundedOut(transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<CompositingRenderTarget> originalTarget =
+ aCompositor->GetCurrentRenderTarget();
+
+ RefPtr<CompositingRenderTarget> firstTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!firstTarget) {
+ return;
+ }
+
+ // Render the source while applying the first mask.
+ aCompositor->SetRenderTarget(firstTarget);
+ {
+ EffectChain firstEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ firstMaskEffect(firstMask, firstEffectChain);
+ aRenderCallback(firstEffectChain, aClipRect - surfaceRect.TopLeft());
+ // firstTarget now contains the transformed source with the first mask and
+ // opacity already applied.
+ }
+
+ // Apply the intermediate masks.
+ gfx::IntRect intermediateClip(surfaceRect - surfaceRect.TopLeft());
+ RefPtr<CompositingRenderTarget> previousTarget = firstTarget;
+ for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) {
+ Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i);
+ RefPtr<CompositingRenderTarget> intermediateTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!intermediateTarget) {
+ break;
+ }
+ aCompositor->SetRenderTarget(intermediateTarget);
+ EffectChain intermediateEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ intermediateMaskEffect(intermediateMask, intermediateEffectChain);
+ if (intermediateMaskEffect.Failed()) {
+ continue;
+ }
+ intermediateEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), intermediateClip,
+ intermediateEffectChain, 1.0, gfx::Matrix4x4());
+ previousTarget = intermediateTarget;
+ }
+
+ aCompositor->SetRenderTarget(originalTarget);
+
+ // Apply the final mask, rendering into originalTarget.
+ EffectChain finalEffectChain(aLayer);
+ finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+ Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
+
+ // The blend mode needs to be applied in this final step, because this is
+ // where we're blending with the actual background (which is in originalTarget).
+ aLayer->AsLayerComposite()->AddBlendModeEffect(finalEffectChain);
+ LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask, finalEffectChain);
+ if (!autoMaskEffect.Failed()) {
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), aClipRect,
+ finalEffectChain, 1.0, gfx::Matrix4x4());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LayerManagerComposite_H */
diff --git a/system/graphics/layers/composite/PaintCounter.cpp b/system/graphics/layers/composite/PaintCounter.cpp
new file mode 100644
index 000000000..56e57aab4
--- /dev/null
+++ b/system/graphics/layers/composite/PaintCounter.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "mozilla/Sprintf.h"
+
+#include "mozilla/gfx/HelpersSkia.h"
+#include "PaintCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+// Positioned below the chrome UI
+IntRect PaintCounter::mRect = IntRect(0, 175, 300, 60);
+
+PaintCounter::PaintCounter()
+{
+ mFormat = SurfaceFormat::B8G8R8A8;
+ mSurface = Factory::CreateDataSourceSurface(mRect.Size(), mFormat);
+ mStride = mSurface->Stride();
+
+ mCanvas.reset(
+ SkCanvas::NewRasterDirect(MakeSkiaImageInfo(mRect.Size(), mFormat),
+ mSurface->GetData(), mStride));
+ mCanvas->clear(SK_ColorWHITE);
+}
+
+PaintCounter::~PaintCounter()
+{
+ mSurface = nullptr;
+ mTextureSource = nullptr;
+ mTexturedEffect = nullptr;
+}
+
+void
+PaintCounter::Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime) {
+ char buffer[48];
+ SprintfLiteral(buffer, "P: %.2f C: %.2f",
+ aPaintTime.ToMilliseconds(),
+ aCompositeTime.ToMilliseconds());
+
+ SkPaint paint;
+ paint.setTextSize(32);
+ paint.setColor(SkColorSetRGB(0, 255, 0));
+ paint.setAntiAlias(true);
+
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ mCanvas->drawText(buffer, strlen(buffer), 10, 30, paint);
+ mCanvas->flush();
+
+ if (!mTextureSource) {
+ mTextureSource = aCompositor->CreateDataTextureSource();
+ mTexturedEffect = CreateTexturedEffect(mFormat, mTextureSource,
+ SamplingFilter::POINT, true);
+ mTexturedEffect->mTextureCoords = Rect(0, 0, 1.0f, 1.0f);
+ }
+
+ mTextureSource->Update(mSurface);
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = mTexturedEffect;
+
+ gfx::Matrix4x4 identity;
+ Rect rect(mRect.x, mRect.y, mRect.width, mRect.height);
+ aCompositor->DrawQuad(rect, mRect, effectChain, 1.0, identity);
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/system/graphics/layers/composite/PaintCounter.h b/system/graphics/layers/composite/PaintCounter.h
new file mode 100644
index 000000000..b5296939f
--- /dev/null
+++ b/system/graphics/layers/composite/PaintCounter.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PaintCounter_h_
+#define mozilla_layers_PaintCounter_h_
+
+#include <map> // for std::map
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "skia/include/core/SkCanvas.h"
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+// Keeps track and paints how long a full invalidation paint takes to rasterize
+// and composite.
+class PaintCounter {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PaintCounter)
+
+ PaintCounter();
+ void Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime);
+ static IntRect GetPaintRect() { return PaintCounter::mRect; }
+
+private:
+ virtual ~PaintCounter();
+
+ SurfaceFormat mFormat;
+ sk_sp<SkCanvas> mCanvas;
+ IntSize mSize;
+ int mStride;
+
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<TexturedEffect> mTexturedEffect;
+ static IntRect mRect;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_PaintCounter_h_
diff --git a/system/graphics/layers/composite/PaintedLayerComposite.cpp b/system/graphics/layers/composite/PaintedLayerComposite.cpp
new file mode 100644
index 000000000..232cc4ef4
--- /dev/null
+++ b/system/graphics/layers/composite/PaintedLayerComposite.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PaintedLayerComposite.h"
+#include "CompositableHost.h" // for TiledLayerProperties, etc
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for CSSRect, LayerPixel, etc
+#include "gfxEnv.h" // for gfxEnv
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter::LINEAR
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ContentHost.h" // for ContentHost
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsMathUtils.h" // for NS_lround
+#include "nsString.h" // for nsAutoCString
+#include "TextRenderer.h"
+#include "GeckoProfiler.h"
+
+namespace mozilla {
+namespace layers {
+
+PaintedLayerComposite::PaintedLayerComposite(LayerManagerComposite *aManager)
+ : PaintedLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mBuffer(nullptr)
+{
+ MOZ_COUNT_CTOR(PaintedLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+PaintedLayerComposite::~PaintedLayerComposite()
+{
+ MOZ_COUNT_DTOR(PaintedLayerComposite);
+ CleanupResources();
+}
+
+bool
+PaintedLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::CONTENT_TILED:
+ case CompositableType::CONTENT_SINGLE:
+ case CompositableType::CONTENT_DOUBLE: {
+ ContentHost* newBuffer = static_cast<ContentHost*>(aHost);
+ if (mBuffer && newBuffer != mBuffer) {
+ mBuffer->Detach(this);
+ }
+ mBuffer = newBuffer;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void
+PaintedLayerComposite::Disconnect()
+{
+ Destroy();
+}
+
+void
+PaintedLayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ CleanupResources();
+ mDestroyed = true;
+ }
+}
+
+Layer*
+PaintedLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mBuffer && mCompositor) {
+ mBuffer->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+PaintedLayerComposite::GetRenderState()
+{
+ if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
+ return LayerRenderState();
+ }
+ return mBuffer->GetRenderState();
+}
+
+void
+PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ if (!mBuffer || !mBuffer->IsAttached()) {
+ return;
+ }
+ PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ Compositor* compositor = mCompositeManager->GetCompositor();
+
+ MOZ_ASSERT(mBuffer->GetCompositor() == compositor &&
+ mBuffer->GetLayer() == this,
+ "buffer is corrupted");
+
+ const nsIntRegion visibleRegion = GetLocalVisibleRegion().ToUnknownRegion();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+
+ RenderWithAllMasks(this, compositor, aClipRect,
+ [&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
+ mBuffer->SetPaintWillResample(MayResample());
+
+ mBuffer->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransform(),
+ GetSamplingFilter(),
+ clipRect,
+ &visibleRegion);
+ });
+
+ mBuffer->BumpFlashCounter();
+
+ compositor->MakeCurrent();
+}
+
+CompositableHost*
+PaintedLayerComposite::GetCompositableHost()
+{
+ if (mBuffer && mBuffer->IsAttached()) {
+ return mBuffer.get();
+ }
+
+ return nullptr;
+}
+
+void
+PaintedLayerComposite::CleanupResources()
+{
+ if (mBuffer) {
+ mBuffer->Detach(this);
+ }
+ mBuffer = nullptr;
+}
+
+void
+PaintedLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mBuffer->GenEffect(GetSamplingFilter());
+}
+
+void
+PaintedLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mBuffer && mBuffer->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mBuffer->PrintInfo(aStream, pfx.get());
+ }
+}
+
+const gfx::TiledIntRegion&
+PaintedLayerComposite::GetInvalidRegion()
+{
+ if (mBuffer) {
+ nsIntRegion region = mInvalidRegion.GetRegion();
+ mBuffer->AddAnimationInvalidation(region);
+ }
+ return mInvalidRegion;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/PaintedLayerComposite.h b/system/graphics/layers/composite/PaintedLayerComposite.h
new file mode 100644
index 000000000..45a89eccf
--- /dev/null
+++ b/system/graphics/layers/composite/PaintedLayerComposite.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PaintedLayerComposite_H
+#define GFX_PaintedLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PaintedLayers use ContentHosts for their compsositable host.
+ * By using different ContentHosts, PaintedLayerComposite support tiled and
+ * non-tiled PaintedLayers and single or double buffering.
+ */
+
+class CompositableHost;
+class ContentHost;
+
+class PaintedLayerComposite : public PaintedLayer,
+ public LayerComposite
+{
+public:
+ explicit PaintedLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ virtual ~PaintedLayerComposite();
+
+public:
+ virtual void Disconnect() override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void Destroy() override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_RUNTIMEABORT("PaintedLayerComposites can't fill invalidated regions");
+ }
+
+ void SetValidRegion(const nsIntRegion& aRegion)
+ {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ValidRegion", this));
+ mValidRegion = aRegion;
+ Mutated();
+ }
+
+ const virtual gfx::TiledIntRegion& GetInvalidRegion() override;
+
+ MOZ_LAYER_DECL_NAME("PaintedLayerComposite", TYPE_PAINTED)
+
+protected:
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter() { return gfx::SamplingFilter::LINEAR; }
+
+private:
+ RefPtr<ContentHost> mBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_PaintedLayerComposite_H */
diff --git a/system/graphics/layers/composite/TextRenderer.cpp b/system/graphics/layers/composite/TextRenderer.cpp
new file mode 100644
index 000000000..be59cb246
--- /dev/null
+++ b/system/graphics/layers/composite/TextRenderer.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextRenderer.h"
+#include "FontData.h"
+#include "png.h"
+#include "mozilla/Base64.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/Effects.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace std;
+
+const Float sBackgroundOpacity = 0.6f;
+const SurfaceFormat sTextureFormat = SurfaceFormat::B8G8R8A8;
+
+static void PNGAPI info_callback(png_structp png_ptr, png_infop info_ptr)
+{
+ png_read_update_info(png_ptr, info_ptr);
+}
+
+static void PNGAPI row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
+{
+ MOZ_ASSERT(sTextureFormat == SurfaceFormat::B8G8R8A8);
+
+ DataSourceSurface::MappedSurface map = static_cast<TextRenderer*>(png_get_progressive_ptr(png_ptr))->GetSurfaceMap();
+
+ uint32_t* dst = (uint32_t*)(map.mData + map.mStride * row_num);
+
+ for (uint32_t x = 0; x < sTextureWidth; x++) {
+ // We blend to a transparent white background, this will make text readable
+ // even if it's on a dark background. Without hurting our ability to
+ // interact with the content behind the text.
+ Float alphaValue = Float(0xFF - new_row[x]) / 255.0f;
+ Float baseValue = sBackgroundOpacity * (1.0f - alphaValue);
+ Color pixelColor(baseValue, baseValue, baseValue, baseValue + alphaValue);
+ dst[x] = pixelColor.ToABGR();
+ }
+}
+
+TextRenderer::~TextRenderer()
+{
+ if (mGlyphBitmaps) {
+ mGlyphBitmaps->Unmap();
+ }
+}
+
+void
+TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin,
+ const Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth)
+{
+ EnsureInitialized();
+
+ // For now we only have a bitmap font with a 16px cell size, so we just
+ // scale it up if the user wants larger text.
+ Float scaleFactor = Float(aTextSize) / Float(sCellHeight);
+
+ aTargetPixelWidth /= scaleFactor;
+
+ uint32_t numLines = 1;
+ uint32_t maxWidth = 0;
+ uint32_t lineWidth = 0;
+ // Calculate the size of the surface needed to draw all the glyphs.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ // Insert a line break if we go past the TargetPixelWidth.
+ // XXX - this has the downside of overrunning the intended width, causing
+ // things at the edge of a window to be cut off.
+ if (aText[i] == '\n' || (aText[i] == ' ' && lineWidth > aTargetPixelWidth)) {
+ numLines++;
+ lineWidth = 0;
+ continue;
+ }
+
+ lineWidth += sGlyphWidths[uint32_t(aText[i])];
+ maxWidth = std::max(lineWidth, maxWidth);
+ }
+
+ // Create a surface to draw our glyphs to.
+ RefPtr<DataSourceSurface> textSurf =
+ Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
+ if (NS_WARN_IF(!textSurf)) {
+ return;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+ return;
+ }
+
+ // Initialize the surface to transparent white.
+ memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
+ numLines * sCellHeight * map.mStride);
+
+ uint32_t currentXPos = 0;
+ uint32_t currentYPos = 0;
+
+ // Copy our glyphs onto the surface.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ if (aText[i] == '\n' || (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) {
+ currentYPos += sCellHeight;
+ currentXPos = 0;
+ continue;
+ }
+
+ uint32_t glyphXOffset = aText[i] % (sTextureWidth / sCellWidth) * sCellWidth * BytesPerPixel(sTextureFormat);
+ uint32_t truncatedLine = aText[i] / (sTextureWidth / sCellWidth);
+ uint32_t glyphYOffset = truncatedLine * sCellHeight * mMap.mStride;
+
+ for (int y = 0; y < 16; y++) {
+ memcpy(map.mData + (y + currentYPos) * map.mStride + currentXPos * BytesPerPixel(sTextureFormat),
+ mMap.mData + glyphYOffset + y * mMap.mStride + glyphXOffset,
+ sGlyphWidths[uint32_t(aText[i])] * BytesPerPixel(sTextureFormat));
+ }
+
+ currentXPos += sGlyphWidths[uint32_t(aText[i])];
+ }
+
+ textSurf->Unmap();
+
+ RefPtr<DataTextureSource> src = mCompositor->CreateDataTextureSource();
+
+ if (!src->Update(textSurf)) {
+ // Upload failed.
+ return;
+ }
+
+ RefPtr<EffectRGB> effect = new EffectRGB(src, true, SamplingFilter::LINEAR);
+ EffectChain chain;
+ chain.mPrimaryEffect = effect;
+
+ Matrix4x4 transform = aTransform;
+ transform.PreScale(scaleFactor, scaleFactor, 1.0f);
+ mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
+ IntRect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
+}
+
+void
+TextRenderer::EnsureInitialized()
+{
+ if (mGlyphBitmaps) {
+ return;
+ }
+
+ mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
+ if (NS_WARN_IF(!mGlyphBitmaps)) {
+ return;
+ }
+
+ if (NS_WARN_IF(!mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
+ return;
+ }
+
+ png_structp png_ptr = NULL;
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+
+ png_set_progressive_read_fn(png_ptr, this, info_callback, row_callback, nullptr);
+ png_infop info_ptr = NULL;
+ info_ptr = png_create_info_struct(png_ptr);
+
+ png_process_data(png_ptr, info_ptr, (uint8_t*)sFontPNG, sizeof(sFontPNG));
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/TextRenderer.h b/system/graphics/layers/composite/TextRenderer.h
new file mode 100644
index 000000000..7665558eb
--- /dev/null
+++ b/system/graphics/layers/composite/TextRenderer.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TextRenderer_H
+#define GFX_TextRenderer_H
+
+#include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+class TextRenderer
+{
+ ~TextRenderer();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextRenderer)
+
+ explicit TextRenderer(Compositor *aCompositor)
+ : mCompositor(aCompositor), mMap({nullptr, 0})
+ {
+ }
+
+ void RenderText(const std::string& aText, const gfx::IntPoint& aOrigin,
+ const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth);
+
+ gfx::DataSourceSurface::MappedSurface& GetSurfaceMap() { return mMap; }
+
+private:
+
+ // Note that this may still fail to set mGlyphBitmaps to a valid value
+ // if the underlying CreateDataSourceSurface fails for some reason.
+ void EnsureInitialized();
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
+ gfx::DataSourceSurface::MappedSurface mMap;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/TextureHost.cpp b/system/graphics/layers/composite/TextureHost.cpp
new file mode 100644
index 000000000..d550945ef
--- /dev/null
+++ b/system/graphics/layers/composite/TextureHost.cpp
@@ -0,0 +1,1176 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "LayersLogging.h" // for AppendToString
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/GPUVideoTextureHost.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "mozilla/layers/PTextureParent.h"
+#include "mozilla/Unused.h"
+#include <limits>
+#include "../opengl/CompositorOGL.h"
+#include "gfxPrefs.h"
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+#ifdef MOZ_ENABLE_D3D10_LAYER
+#include "../d3d11/CompositorD3D11.h"
+#endif
+
+#ifdef MOZ_X11
+#include "mozilla/layers/X11TextureHost.h"
+#endif
+
+#ifdef XP_WIN
+#include "mozilla/layers/TextureDIB.h"
+#endif
+
+#if 0
+#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#define RECYCLE_LOG(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * TextureParent is the host-side IPDL glue between TextureClient and TextureHost.
+ * It is an IPDL actor just like LayerParent, CompositableParent, etc.
+ */
+class TextureParent : public ParentActor<PTextureParent>
+{
+public:
+ explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial);
+
+ ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) override;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ virtual void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ virtual bool RecvDestroySync() override {
+ DestroyIfNeeded();
+ return true;
+ }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent*
+TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ TextureParent* actor = new TextureParent(aAllocator, aSerial);
+ if (!actor->Init(aSharedData, aLayersBackend, aFlags)) {
+ actor->ActorDestroy(ipc::IProtocol::ActorDestroyReason::FailedConstructor);
+ delete actor;
+ return nullptr;
+ }
+ return actor;
+}
+
+// static
+bool
+TextureHost::DestroyIPDLActor(PTextureParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+// static
+bool
+TextureHost::SendDeleteIPDLActor(PTextureParent* actor)
+{
+ return PTextureParent::Send__delete__(actor);
+}
+
+// static
+TextureHost*
+TextureHost::AsTextureHost(PTextureParent* actor)
+{
+ if (!actor) {
+ return nullptr;
+ }
+ return static_cast<TextureParent*>(actor)->mTextureHost;
+}
+
+// static
+uint64_t
+TextureHost::GetTextureSerial(PTextureParent* actor)
+{
+ if (!actor) {
+ return UINT64_MAX;
+ }
+ return static_cast<TextureParent*>(actor)->mSerial;
+}
+
+PTextureParent*
+TextureHost::GetIPDLActor()
+{
+ return mActor;
+}
+
+void
+TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId)
+{
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+}
+
+// implemented in TextureHostOGL.cpp
+already_AddRefed<TextureHost> CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureHostBasic.cpp
+already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D11.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+already_AddRefed<TextureHost>
+TextureHost::Create(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags)
+{
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL) {
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR("A client process is trying to peek at our address space using a X11Texture!");
+ return nullptr;
+ }
+
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ return MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
+#endif
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+}
+
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ const SurfaceDescriptorBuffer& bufferDesc = aDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ switch (data.type()) {
+ case MemoryOrShmem::TShmem: {
+ const ipc::Shmem& shmem = data.get_Shmem();
+ const BufferDescriptor& desc = bufferDesc.desc();
+ if (!shmem.IsReadable()) {
+ // We failed to map the shmem so we can't verify its size. This
+ // should not be a fatal error, so just create the texture with
+ // nothing backing it.
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+
+ size_t bufSize = shmem.Size<char>();
+ size_t reqSize = SIZE_MAX;
+ switch (desc.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
+ reqSize =
+ ImageDataSerializer::ComputeYCbCrBufferSize(ycbcr.ySize(), ycbcr.cbCrSize(),
+ ycbcr.yOffset(), ycbcr.cbOffset(),
+ ycbcr.crOffset());
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = desc.get_RGBDescriptor();
+ reqSize = ImageDataSerializer::ComputeRGBBufferSize(rgb.size(), rgb.format());
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor " << (int)desc.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+
+ if (reqSize == 0 || bufSize < reqSize) {
+ NS_ERROR("A client process gave a shmem too small to fit for its descriptor!");
+ return nullptr;
+ }
+
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
+ return nullptr;
+ }
+
+ result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(),
+ aFlags);
+ break;
+ }
+ default:
+ gfxCriticalError() << "Failed texture host for backend " << (int)data.type();
+ MOZ_CRASH("GFX: No texture host for backend");
+ }
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
+ result = new GPUVideoTextureHost(aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR("A client process is trying to peek at our address space using a DIBTexture!");
+ return nullptr;
+ }
+
+ result = new DIBTextureHost(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping: {
+ result = new TextureHostFileMapping(aFlags, aDesc);
+ break;
+ }
+#endif
+ default: {
+ NS_WARNING("No backend independent TextureHost for this descriptor type");
+ }
+ }
+ return result.forget();
+}
+
+TextureHost::TextureHost(TextureFlags aFlags)
+ : AtomicRefCountedWithFinalize("TextureHost")
+ , mActor(nullptr)
+ , mFlags(aFlags)
+ , mCompositableCount(0)
+ , mFwdTransactionId(0)
+{
+}
+
+TextureHost::~TextureHost()
+{
+ // If we still have a ReadLock, unlock it. At this point we don't care about
+ // the texture client being written into on the other side since it should be
+ // destroyed by now. But we will hit assertions if we don't ReadUnlock before
+ // destroying the lock itself.
+ ReadUnlock();
+}
+
+void TextureHost::Finalize()
+{
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void
+TextureHost::UnbindTextureSource()
+{
+ if (mReadLock) {
+ auto compositor = GetCompositor();
+ // This TextureHost is not used anymore. Since most compositor backends are
+ // working asynchronously under the hood a compositor could still be using
+ // this texture, so it is generally best to wait until the end of the next
+ // composition before calling ReadUnlock. We ask the compositor to take care
+ // of that for us.
+ if (compositor) {
+ compositor->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ }
+ }
+}
+
+void
+TextureHost::RecycleTexture(TextureFlags aFlags)
+{
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(aFlags & TextureFlags::RECYCLE);
+ mFlags = aFlags;
+}
+
+void
+TextureHost::NotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+
+ // Do not need to call NotifyNotUsed() if TextureHost does not have
+ // TextureFlags::RECYCLE flag.
+ if (!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ auto compositor = GetCompositor();
+ // The following cases do not need to defer NotifyNotUsed until next Composite.
+ // - TextureHost does not have Compositor.
+ // - Compositor is BasicCompositor.
+ // - TextureHost has intermediate buffer.
+ // end of buffer usage.
+ if (!compositor ||
+ compositor->IsDestroyed() ||
+ compositor->AsBasicCompositor() ||
+ HasIntermediateBuffer()) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+
+ compositor->NotifyNotUsedAfterComposition(this);
+}
+
+void
+TextureHost::CallNotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+void
+TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ // Note: the TextureHost needs to be locked before it is safe to call
+ // GetSize() and GetFormat() on it.
+ if (Lock()) {
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ Unlock();
+ }
+ AppendToString(aStream, mFlags, " [flags=", "]");
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+void
+TextureHost::Updated(const nsIntRegion* aRegion)
+{
+ LayerScope::ContentChanged(this);
+ UpdatedInternal(aRegion);
+}
+
+TextureSource::TextureSource()
+: mCompositableCount(0)
+{
+ MOZ_COUNT_CTOR(TextureSource);
+}
+
+TextureSource::~TextureSource()
+{
+ MOZ_COUNT_DTOR(TextureSource);
+}
+
+const char*
+TextureSource::Name() const
+{
+ MOZ_CRASH("GFX: TextureSource without class name");
+ return "TextureSource";
+}
+
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: TextureHost(aFlags)
+, mCompositor(nullptr)
+, mUpdateSerial(1)
+, mLocked(false)
+, mNeedsFullUpdate(false)
+{
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.ySize();
+ mFormat = gfx::SurfaceFormat::YUV;
+ mHasIntermediateBuffer = ycbcr.hasIntermediateBuffer();
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+ mSize = rgb.size();
+ mFormat = rgb.format();
+ mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+ if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+ // One texture of a component alpha texture pair will start out all white.
+ // This hack allows us to easily make sure that white will be uploaded.
+ // See bug 1138934
+ mNeedsFullUpdate = true;
+ }
+}
+
+BufferTextureHost::~BufferTextureHost()
+{}
+
+void
+BufferTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ ++mUpdateSerial;
+ // If the last frame wasn't uploaded yet, and we -don't- have a partial update,
+ // we still need to update the full surface.
+ if (aRegion && !mNeedsFullUpdate) {
+ mMaybeUpdatedRegion.OrWith(*aRegion);
+ } else {
+ mNeedsFullUpdate = true;
+ }
+ if (GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) {
+ DebugOnly<bool> result = MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+ NS_WARNING_ASSERTION(result, "Failed to upload a texture");
+ }
+}
+
+void
+BufferTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ if (mCompositor == aCompositor) {
+ return;
+ }
+ if (aCompositor && mCompositor &&
+ aCompositor->GetBackendType() == mCompositor->GetBackendType()) {
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(aCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mCompositor = aCompositor;
+}
+
+void
+BufferTextureHost::DeallocateDeviceData()
+{
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ // WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost.
+ // When BufferTextureHost is destroyed, data of
+ // WrappingTextureSourceYCbCrBasic becomes invalid.
+ if (mFirstSource->AsWrappingTextureSourceYCbCrBasic() &&
+ mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ mFirstSource->DeallocateDeviceData();
+ }
+ return;
+ }
+
+ if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+ mFirstSource = nullptr;
+ return;
+ }
+
+ mFirstSource->SetOwner(nullptr);
+
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->DeallocateDeviceData();
+ it = it->GetNextSibling();
+ }
+}
+
+bool
+BufferTextureHost::Lock()
+{
+ MOZ_ASSERT(!mLocked);
+ if (!MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr)) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void
+BufferTextureHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void
+TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator)
+{
+ RefPtr<TextureReadLock> lock = TextureReadLock::Deserialize(aDesc, aAllocator);
+ if (!lock) {
+ return;
+ }
+
+ // If mReadLock is not null it means we haven't unlocked it yet and the content
+ // side should not have been able to write into this texture and send a new lock!
+ MOZ_ASSERT(!mReadLock);
+ mReadLock = lock.forget();
+}
+
+void
+TextureHost::ReadUnlock()
+{
+ if (mReadLock) {
+ mReadLock->ReadUnlock();
+ mReadLock = nullptr;
+ }
+}
+
+bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+ MOZ_ASSERT(!mHasIntermediateBuffer);
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ return true;
+ }
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mCompositor->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+ }
+
+ if (!mFirstSource) {
+ // BasicCompositor::CreateDataTextureSourceAround never returns null
+ // and we don't expect to take this branch if we are using another backend.
+ // Returning false is fine but if we get into this situation it probably
+ // means something fishy is going on, like a texture being used with
+ // several compositor backends.
+ NS_WARNING("Failed to use a BufferTextureHost without intermediate buffer");
+ return false;
+ }
+
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
+static
+bool IsCompatibleTextureSource(TextureSource* aTexture,
+ const BufferDescriptor& aDescriptor,
+ Compositor* aCompositor)
+{
+ if (!aCompositor) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
+ && aTexture->GetSize() == ycbcr.ySize();
+ }
+
+ if (aTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || aTexture->GetSize() != ycbcr.ySize()) {
+ return false;
+ }
+
+ auto cbTexture = aTexture->GetSubSource(1);
+ if (!cbTexture
+ || cbTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || cbTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ auto crTexture = aTexture->GetSubSource(2);
+ if (!crTexture
+ || crTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || crTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ return true;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
+ return aTexture->GetFormat() == rgb.format()
+ && aTexture->GetSize() == rgb.size();
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+void
+BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ // Reuse WrappingTextureSourceYCbCrBasic to reduce memory consumption.
+ if (mFormat == gfx::SurfaceFormat::YUV &&
+ !mHasIntermediateBuffer &&
+ aTexture.get() &&
+ aTexture->AsWrappingTextureSourceYCbCrBasic() &&
+ aTexture->NumCompositableRefs() <= 1 &&
+ aTexture->GetSize() == GetSize()) {
+ aTexture->AsSourceBasic()->SetBufferTextureHost(this);
+ aTexture->AsDataTextureSource()->SetOwner(this);
+ mFirstSource = aTexture->AsDataTextureSource();
+ mNeedsFullUpdate = true;
+ }
+
+ if (!mHasIntermediateBuffer) {
+ EnsureWrappingTextureSource();
+ }
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTexture = mFirstSource.get();
+ return;
+ }
+
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+
+ bool compatibleFormats = texture && IsCompatibleTextureSource(texture,
+ mDescriptor,
+ mCompositor);
+
+ bool shouldCreateTexture = !compatibleFormats
+ || texture->NumCompositableRefs() > 1
+ || texture->HasOwner();
+
+ if (!shouldCreateTexture) {
+ mFirstSource = texture;
+ mFirstSource->SetOwner(this);
+ mNeedsFullUpdate = true;
+
+ // It's possible that texture belonged to a different compositor,
+ // so make sure we update it (and all of its siblings) to the
+ // current one.
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(mCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool
+BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+void
+BufferTextureHost::UnbindTextureSource()
+{
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->Unbind();
+ }
+ // This texture is not used by any layer anymore.
+ // If the texture doesn't have an intermediate buffer, it means we are
+ // compositing synchronously on the CPU, so we don't need to wait until
+ // the end of the next composition to ReadUnlock (which other textures do
+ // by default).
+ // If the texture has an intermediate buffer we don't care either because
+ // texture uploads are also performed synchronously for BufferTextureHost.
+ ReadUnlock();
+}
+
+gfx::SurfaceFormat
+BufferTextureHost::GetFormat() const
+{
+ // mFormat is the format of the data that we share with the content process.
+ // GetFormat, on the other hand, expects the format that we present to the
+ // Compositor (it is used to choose the effect type).
+ // if the compositor does not support YCbCr effects, we give it a RGBX texture
+ // instead (see BufferTextureHost::Upload)
+ if (mFormat == gfx::SurfaceFormat::YUV &&
+ mCompositor &&
+ !mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+YUVColorSpace
+BufferTextureHost::GetYUVColorSpace() const
+{
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+bool
+BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
+{
+ auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+ if (serial == mUpdateSerial) {
+ return true;
+ }
+
+ if (serial == 0) {
+ // 0 means the source has no valid content
+ aRegion = nullptr;
+ }
+
+ if (!Upload(aRegion)) {
+ return false;
+ }
+
+ if (mHasIntermediateBuffer) {
+ // We just did the texture upload, the content side can now freely write
+ // into the shared buffer.
+ ReadUnlock();
+ }
+
+ // We no longer have an invalid region.
+ mNeedsFullUpdate = false;
+ mMaybeUpdatedRegion.SetEmpty();
+
+ // If upload returns true we know mFirstSource is not null
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ return true;
+}
+
+bool
+BufferTextureHost::Upload(nsIntRegion *aRegion)
+{
+ uint8_t* buf = GetBuffer();
+ if (!buf) {
+ // We don't have a buffer; a possible cause is that the IPDL actor
+ // is already dead. This inevitably happens as IPDL actors can die
+ // at any time, so we want to silently return in this case.
+ // another possible cause is that IPDL failed to map the shmem when
+ // deserializing it.
+ return false;
+ }
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ return true;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return false;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ mFirstSource->Update(surf, aRegion);
+ return true;
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ mFirstSource = srcY;
+ mFirstSource->SetOwner(this);
+ srcY->SetNextSibling(srcU);
+ srcU->SetNextSibling(srcV);
+ } else {
+ // mFormat never changes so if this was created as a YCbCr host and already
+ // contains a source it should already have 3 sources.
+ // BufferTextureHost only uses DataTextureSources so it is safe to assume
+ // all 3 sources are DataTextureSource.
+ MOZ_ASSERT(mFirstSource->GetNextSibling());
+ MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
+ srcY = mFirstSource;
+ srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
+ srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
+ }
+
+ RefPtr<gfx::DataSourceSurface> tempY =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
+ desc.ySize().width,
+ desc.ySize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ // We don't support partial updates for Y U V textures
+ NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
+ if (!tempY ||
+ !tempCb ||
+ !tempCr ||
+ !srcY->Update(tempY) ||
+ !srcU->Update(tempCb) ||
+ !srcV->Update(tempCr)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ } else {
+ // non-YCbCr case
+ nsIntRegion* regionToUpdate = aRegion;
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ // Update the full region the first time for component alpha textures.
+ regionToUpdate = nullptr;
+ }
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+
+ if (!mFirstSource->Update(surf.get(), regionToUpdate)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ }
+ MOZ_ASSERT(mFirstSource);
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+BufferTextureHost::GetAsSurface()
+{
+ RefPtr<gfx::DataSourceSurface> result;
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return nullptr;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ GetBuffer(), mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+ } else {
+ result =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ }
+ return result.forget();
+}
+
+ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+: BufferTextureHost(aDesc, aFlags)
+, mDeallocator(aDeallocator)
+{
+ if (aShmem.IsReadable()) {
+ mShmem = MakeUnique<ipc::Shmem>(aShmem);
+ } else {
+ // This can happen if we failed to map the shmem on this process, perhaps
+ // because it was big and we didn't have enough contiguous address space
+ // available, even though we did on the child process.
+ // As a result this texture will be in an invalid state and Lock will
+ // always fail.
+
+ gfxCriticalNote << "Failed to create a valid ShmemTextureHost";
+ }
+
+ MOZ_COUNT_CTOR(ShmemTextureHost);
+}
+
+ShmemTextureHost::~ShmemTextureHost()
+{
+ MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(ShmemTextureHost);
+}
+
+void
+ShmemTextureHost::DeallocateSharedData()
+{
+ if (mShmem) {
+ MOZ_ASSERT(mDeallocator,
+ "Shared memory would leak without a ISurfaceAllocator");
+ mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem);
+ mShmem = nullptr;
+ }
+}
+
+void
+ShmemTextureHost::ForgetSharedData()
+{
+ if (mShmem) {
+ mShmem = nullptr;
+ }
+}
+
+void
+ShmemTextureHost::OnShutdown()
+{
+ mShmem = nullptr;
+}
+
+uint8_t* ShmemTextureHost::GetBuffer()
+{
+ return mShmem ? mShmem->get<uint8_t>() : nullptr;
+}
+
+size_t ShmemTextureHost::GetBufferSize()
+{
+ return mShmem ? mShmem->Size<uint8_t>() : 0;
+}
+
+MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: BufferTextureHost(aDesc, aFlags)
+, mBuffer(aBuffer)
+{
+ MOZ_COUNT_CTOR(MemoryTextureHost);
+}
+
+MemoryTextureHost::~MemoryTextureHost()
+{
+ MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(MemoryTextureHost);
+}
+
+void
+MemoryTextureHost::DeallocateSharedData()
+{
+ if (mBuffer) {
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ }
+ delete[] mBuffer;
+ mBuffer = nullptr;
+}
+
+void
+MemoryTextureHost::ForgetSharedData()
+{
+ mBuffer = nullptr;
+}
+
+uint8_t* MemoryTextureHost::GetBuffer()
+{
+ return mBuffer;
+}
+
+size_t MemoryTextureHost::GetBufferSize()
+{
+ // MemoryTextureHost just trusts that the buffer size is large enough to read
+ // anything we need to. That's because MemoryTextureHost has to trust the buffer
+ // pointer anyway, so the security model here is just that MemoryTexture's
+ // are restricted to same-process clients.
+ return std::numeric_limits<size_t>::max();
+}
+
+TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, uint64_t aSerial)
+: mSurfaceAllocator(aSurfaceAllocator)
+, mSerial(aSerial)
+{
+ MOZ_COUNT_CTOR(TextureParent);
+}
+
+TextureParent::~TextureParent()
+{
+ MOZ_COUNT_DTOR(TextureParent);
+}
+
+void
+TextureParent::NotifyNotUsed(uint64_t aTransactionId)
+{
+ if (!mTextureHost) {
+ return;
+ }
+ mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
+}
+
+bool
+TextureParent::Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aBackend,
+ const TextureFlags& aFlags)
+{
+ mTextureHost = TextureHost::Create(aSharedData,
+ mSurfaceAllocator,
+ aBackend,
+ aFlags);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void
+TextureParent::Destroy()
+{
+ if (!mTextureHost) {
+ return;
+ }
+
+ // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+ // protocol that created it.
+ mTextureHost->ReadUnlock();
+
+ if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTextureHost->ForgetSharedData();
+ }
+
+ mTextureHost->mActor = nullptr;
+ mTextureHost = nullptr;
+}
+
+void
+TextureHost::ReceivedDestroy(PTextureParent* aActor)
+{
+ static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
+bool
+TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
+{
+ if (!mTextureHost) {
+ return true;
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/TextureHost.h b/system/graphics/layers/composite/TextureHost.h
new file mode 100644
index 000000000..778411225
--- /dev/null
+++ b/system/graphics/layers/composite/TextureHost.h
@@ -0,0 +1,898 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOST_H
+#define MOZILLA_GFX_TEXTUREHOST_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t, uint8_t
+#include "gfxTypes.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTraceRefcnt.h" // for MOZ_COUNT_CTOR, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class BufferDescriptor;
+class BufferTextureHost;
+class Compositor;
+class CompositableParentManager;
+class ReadLockDescriptor;
+class CompositorBridgeParent;
+class SurfaceDescriptor;
+class HostIPCAllocator;
+class ISurfaceAllocator;
+class TextureHostOGL;
+class TextureReadLock;
+class TextureSourceOGL;
+class TextureSourceD3D11;
+class TextureSourceBasic;
+class DataTextureSource;
+class PTextureParent;
+class TextureParent;
+class WrappingTextureSourceYCbCrBasic;
+
+/**
+ * A view on a TextureHost where the texture is internally represented as tiles
+ * (contrast with a tiled buffer, where each texture is a tile). For iteration by
+ * the texture's buffer host.
+ * This is only useful when the underlying surface is too big to fit in one
+ * device texture, which forces us to split it in smaller parts.
+ * Tiled Compositable is a different thing.
+ */
+class BigImageIterator
+{
+public:
+ virtual void BeginBigImageIteration() = 0;
+ virtual void EndBigImageIteration() {};
+ virtual gfx::IntRect GetTileRect() = 0;
+ virtual size_t GetTileCount() = 0;
+ virtual bool NextTile() = 0;
+};
+
+/**
+ * TextureSource is the interface for texture objects that can be composited
+ * by a given compositor backend. Since the drawing APIs are different
+ * between backends, the TextureSource interface is split into different
+ * interfaces (TextureSourceOGL, etc.), and TextureSource mostly provide
+ * access to these interfaces.
+ *
+ * This class is used on the compositor side.
+ */
+class TextureSource: public RefCounted<TextureSource>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureSource)
+
+ TextureSource();
+
+ virtual ~TextureSource();
+
+ virtual const char* Name() const = 0;
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+
+ /**
+ * Return the size of the texture in texels.
+ * If this is a tile iterator, GetSize must return the size of the current tile.
+ */
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Return the pixel format of this texture
+ */
+ virtual gfx::SurfaceFormat GetFormat() const { return gfx::SurfaceFormat::UNKNOWN; }
+
+ /**
+ * Cast to a TextureSource for for each backend..
+ */
+ virtual TextureSourceOGL* AsSourceOGL() {
+ gfxCriticalNote << "Failed to cast " << Name() << " into a TextureSourceOGL";
+ return nullptr;
+ }
+ virtual TextureSourceD3D11* AsSourceD3D11() { return nullptr; }
+ virtual TextureSourceBasic* AsSourceBasic() { return nullptr; }
+ /**
+ * Cast to a DataTextureSurce.
+ */
+ virtual DataTextureSource* AsDataTextureSource() { return nullptr; }
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() { return nullptr; }
+
+ /**
+ * Overload this if the TextureSource supports big textures that don't fit in
+ * one device texture and must be tiled internally.
+ */
+ virtual BigImageIterator* AsBigImageIterator() { return nullptr; }
+
+ virtual void SetCompositor(Compositor* aCompositor) {}
+
+ virtual void Unbind() {}
+
+ void SetNextSibling(TextureSource* aTexture) { mNextSibling = aTexture; }
+
+ TextureSource* GetNextSibling() const { return mNextSibling; }
+
+ /**
+ * In some rare cases we currently need to consider a group of textures as one
+ * TextureSource, that can be split in sub-TextureSources.
+ */
+ TextureSource* GetSubSource(int index)
+ {
+ switch (index) {
+ case 0: return this;
+ case 1: return GetNextSibling();
+ case 2: return GetNextSibling() ? GetNextSibling()->GetNextSibling() : nullptr;
+ }
+ return nullptr;
+ }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ void ReleaseCompositableRef() {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+protected:
+
+ RefPtr<TextureSource> mNextSibling;
+ int mCompositableCount;
+};
+
+/// Equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
+/// ReleaseCompositableRef in addition to the usual AddRef and Release.
+///
+/// The semantoics of these CompositableTextureRefs are important because they
+/// are used both as a synchronization/safety mechanism, and as an optimization
+/// mechanism. They are also tricky and subtle because we use them in a very
+/// implicit way (assigning to a CompositableTextureRef is less visible than
+/// explicitly calling a method or whatnot).
+/// It is Therefore important to be careful about the way we use this tool.
+///
+/// CompositableTextureRef is a mechanism that lets us count how many compositables
+/// are using a given texture (for TextureSource and TextureHost).
+/// We use it to run specific code when a texture is not used anymore, and also
+/// we trigger fast paths on some operations when we can see that the texture's
+/// CompositableTextureRef counter is equal to 1 (the texture is not shared
+/// between compositables).
+/// This means that it is important to observe the following rules:
+/// * CompositableHosts that receive UseTexture and similar messages *must* store
+/// all of the TextureHosts they receive in CompositableTextureRef slots for as
+/// long as they may be using them.
+/// * CompositableHosts must store each texture in a *single* CompositableTextureRef
+/// slot to ensure that the counter properly reflects how many compositables are
+/// using the texture.
+/// If a compositable needs to hold two references to a given texture (for example
+/// to have a pointer to the current texture in a list of textures that may be
+/// used), it can hold its extra references with RefPtr or whichever pointer type
+/// makes sense.
+template<typename T>
+class CompositableTextureRef {
+public:
+ CompositableTextureRef() {}
+
+ explicit CompositableTextureRef(const CompositableTextureRef& aOther)
+ {
+ *this = aOther;
+ }
+
+ explicit CompositableTextureRef(T* aOther)
+ {
+ *this = aOther;
+ }
+
+ ~CompositableTextureRef()
+ {
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ }
+
+ CompositableTextureRef& operator=(const CompositableTextureRef& aOther)
+ {
+ if (aOther.get()) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther.get();
+ return *this;
+ }
+
+ CompositableTextureRef& operator=(T* aOther)
+ {
+ if (aOther) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther;
+ return *this;
+ }
+
+ T* get() const { return mRef; }
+ operator T*() const { return mRef; }
+ T* operator->() const { return mRef; }
+ T& operator*() const { return *mRef; }
+
+private:
+ RefPtr<T> mRef;
+};
+
+typedef CompositableTextureRef<TextureSource> CompositableTextureSourceRef;
+typedef CompositableTextureRef<TextureHost> CompositableTextureHostRef;
+
+/**
+ * Interface for TextureSources that can be updated from a DataSourceSurface.
+ *
+ * All backend should implement at least one DataTextureSource.
+ */
+class DataTextureSource : public TextureSource
+{
+public:
+ DataTextureSource()
+ : mOwner(0)
+ , mUpdateSerial(0)
+ {}
+
+ virtual const char* Name() const override { return "DataTextureSource"; }
+
+ virtual DataTextureSource* AsDataTextureSource() override { return this; }
+
+ /**
+ * Upload a (portion of) surface to the TextureSource.
+ *
+ * The DataTextureSource doesn't own aSurface, although it owns and manage
+ * the device texture it uploads to internally.
+ */
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) = 0;
+
+ /**
+ * A facility to avoid reuploading when it is not necessary.
+ * The caller of Update can use GetUpdateSerial to see if the number has changed
+ * since last update, and call SetUpdateSerial after each successful update.
+ * The caller is responsible for managing the update serial except when the
+ * texture data is deallocated in which case the TextureSource should always
+ * reset the update serial to zero.
+ */
+ uint32_t GetUpdateSerial() const { return mUpdateSerial; }
+ void SetUpdateSerial(uint32_t aValue) { mUpdateSerial = aValue; }
+
+ // By default at least set the update serial to zero.
+ // overloaded versions should do that too.
+ virtual void DeallocateDeviceData() override
+ {
+ SetUpdateSerial(0);
+ }
+
+#ifdef DEBUG
+ /**
+ * Provide read access to the data as a DataSourceSurface.
+ *
+ * This is expected to be very slow and should be used for mostly debugging.
+ * XXX - implement everywhere and make it pure virtual.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() { return nullptr; };
+#endif
+
+ void SetOwner(TextureHost* aOwner)
+ {
+ auto newOwner = (uintptr_t)aOwner;
+ if (newOwner != mOwner) {
+ mOwner = newOwner;
+ SetUpdateSerial(0);
+ }
+ }
+
+ bool IsOwnedBy(TextureHost* aOwner) const { return mOwner == (uintptr_t)aOwner; }
+
+ bool HasOwner() const { return !IsOwnedBy(nullptr); }
+
+private:
+ // We store mOwner as an integer rather than as a pointer to make it clear
+ // it is not intended to be dereferenced.
+ uintptr_t mOwner;
+ uint32_t mUpdateSerial;
+};
+
+/**
+ * TextureHost is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * compositor-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureClient lives on the content-side.
+ *
+ * TextureHost only knows how to deserialize or synchronize generic image data
+ * (SurfaceDescriptor) and provide access to one or more TextureSource objects
+ * (these provide the necessary APIs for compositor backends to composite the
+ * image).
+ *
+ * A TextureHost implementation corresponds to one SurfaceDescriptor type, as
+ * opposed to TextureSource that corresponds to device textures.
+ * This means that for YCbCr planes, even though they are represented as
+ * 3 textures internally (3 TextureSources), we use 1 TextureHost and not 3,
+ * because the 3 planes are stored in the same buffer of shared memory, before
+ * they are uploaded separately.
+ *
+ * There is always one and only one TextureHost per TextureClient, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * reponsibility of the compositable (which would use two Texture pairs).
+ *
+ * The Lock/Unlock mecanism here mirrors Lock/Unlock in TextureClient.
+ *
+ */
+class TextureHost
+ : public AtomicRefCountedWithFinalize<TextureHost>
+{
+ /**
+ * Called once, just before the destructor.
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize();
+
+ friend class AtomicRefCountedWithFinalize<TextureHost>;
+public:
+ explicit TextureHost(TextureFlags aFlags);
+
+protected:
+ virtual ~TextureHost();
+
+public:
+ /**
+ * Factory method.
+ */
+ static already_AddRefed<TextureHost> Create(
+ const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags);
+
+ /**
+ * Lock the texture host for compositing.
+ */
+ virtual bool Lock() { return true; }
+ /**
+ * Unlock the texture host after compositing. Lock() and Unlock() should be
+ * called in pair.
+ */
+ virtual void Unlock() {}
+
+ /**
+ * Lock the texture host for compositing without using compositor.
+ */
+ virtual bool LockWithoutCompositor() { return true; }
+ /**
+ * Similar to Unlock(), but it should be called with LockWithoutCompositor().
+ */
+ virtual void UnlockWithoutCompositor() {}
+
+ /**
+ * Note that the texture host format can be different from its corresponding
+ * texture source's. For example a ShmemTextureHost can have the ycbcr
+ * format and produce 3 "alpha" textures sources.
+ */
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+ /**
+ * Return the format used for reading the texture.
+ * Apple's YCBCR_422 is R8G8B8X8.
+ */
+ virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); }
+
+ virtual YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; }
+
+ /**
+ * Called during the transaction. The TextureSource may or may not be composited.
+ *
+ * Note that this is called outside of lock/unlock.
+ */
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
+
+ /**
+ * Called at composition time, just before compositing the TextureSource composited.
+ *
+ * Note that this is called only withing lock/unlock.
+ */
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) = 0;
+
+ /**
+ * Called when another TextureHost will take over.
+ */
+ virtual void UnbindTextureSource();
+
+ /**
+ * Is called before compositing if the shared data has changed since last
+ * composition.
+ * This method should be overload in cases like when we need to do a texture
+ * upload for example.
+ *
+ * @param aRegion The region that has been changed, if nil, it means that the
+ * entire surface should be updated.
+ */
+ void Updated(const nsIntRegion* aRegion = nullptr);
+
+ /**
+ * Sets this TextureHost's compositor.
+ * A TextureHost can change compositor on certain occasions, in particular if
+ * it belongs to an async Compositable.
+ * aCompositor can be null, in which case the TextureHost must cleanup all
+ * of it's device textures.
+ */
+ virtual void SetCompositor(Compositor* aCompositor) {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is shared with
+ * the content side, such as shared memory.
+ */
+ virtual void DeallocateSharedData() {}
+
+ /**
+ * Should be overridden in order to force the TextureHost to drop all references
+ * to it's shared data.
+ *
+ * This is important to ensure the correctness of the deallocation protocol.
+ */
+ virtual void ForgetSharedData() {}
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Should be overridden if TextureHost supports crop rect.
+ */
+ virtual void SetCropRect(nsIntRect aCropRect) {}
+
+ /**
+ * Debug facility.
+ * XXX - cool kids use Moz2D. See bug 882113.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() = 0;
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void SetFlags(TextureFlags aFlags) { mFlags = aFlags; }
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void AddFlag(TextureFlags aFlag) { mFlags |= aFlag; }
+
+ TextureFlags GetFlags() { return mFlags; }
+
+ /**
+ * Allocate and deallocate a TextureParent actor.
+ *
+ * TextureParent< is an implementation detail of TextureHost that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPTextureParent and DeallocPTextureParent).
+ */
+ static PTextureParent* CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial);
+ static bool DestroyIPDLActor(PTextureParent* actor);
+
+ /**
+ * Destroy the TextureChild/Parent pair.
+ */
+ static bool SendDeleteIPDLActor(PTextureParent* actor);
+
+ static void ReceivedDestroy(PTextureParent* actor);
+
+ /**
+ * Get the TextureHost corresponding to the actor passed in parameter.
+ */
+ static TextureHost* AsTextureHost(PTextureParent* actor);
+
+ static uint64_t GetTextureSerial(PTextureParent* actor);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureParent* GetIPDLActor();
+
+ /**
+ * Specific to B2G's Composer2D
+ * XXX - more doc here
+ */
+ virtual LayerRenderState GetRenderState()
+ {
+ // By default we return an empty render state, this should be overridden
+ // by the TextureHost implementations that are used on B2G with Composer2D
+ return LayerRenderState();
+ }
+
+ // If a texture host holds a reference to shmem, it should override this method
+ // to forget about the shmem _without_ releasing it.
+ virtual void OnShutdown() {}
+
+ // Forget buffer actor. Used only for hacky fix for bug 966446.
+ virtual void ForgetBufferActor() {}
+
+ virtual const char *Name() { return "TextureHost"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Indicates whether the TextureHost implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureHost does not contend with locking the texture on the client side.
+ */
+ virtual bool HasIntermediateBuffer() const { return false; }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ void ReleaseCompositableRef()
+ {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ if (mCompositableCount == 0) {
+ UnbindTextureSource();
+ // Send mFwdTransactionId to client side if necessary.
+ NotifyNotUsed();
+ }
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+ void SetLastFwdTransactionId(uint64_t aTransactionId);
+
+ void DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator);
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ virtual Compositor* GetCompositor() = 0;
+
+ virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
+
+protected:
+ void ReadUnlock();
+
+ void RecycleTexture(TextureFlags aFlags);
+
+ virtual void UpdatedInternal(const nsIntRegion *Region) {}
+
+ /**
+ * Called when mCompositableCount becomes 0.
+ */
+ void NotifyNotUsed();
+
+ // for Compositor.
+ void CallNotifyNotUsed();
+
+ PTextureParent* mActor;
+ RefPtr<TextureReadLock> mReadLock;
+ TextureFlags mFlags;
+ int mCompositableCount;
+ uint64_t mFwdTransactionId;
+
+ friend class Compositor;
+ friend class TextureParent;
+ friend class TiledLayerBufferComposite;
+};
+
+/**
+ * TextureHost that wraps a random access buffer such as a Shmem or some raw
+ * memory.
+ *
+ * This TextureHost is backend-independent and the backend-specific bits are
+ * in the TextureSource.
+ * This class must be inherited to implement GetBuffer and DeallocSharedData
+ * (see ShmemTextureHost and MemoryTextureHost)
+ *
+ * Uploads happen when Lock is called.
+ *
+ * BufferTextureHost supports YCbCr and flavours of RGBA images (RGBX, A, etc.).
+ */
+class BufferTextureHost : public TextureHost
+{
+public:
+ BufferTextureHost(const BufferDescriptor& aDescriptor, TextureFlags aFlags);
+
+ ~BufferTextureHost();
+
+ virtual uint8_t* GetBuffer() = 0;
+
+ virtual size_t GetBufferSize() = 0;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void UnbindTextureSource() override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ /**
+ * Return the format that is exposed to the compositor when calling
+ * BindTextureSource.
+ *
+ * If the shared format is YCbCr and the compositor does not support it,
+ * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
+ */
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
+
+ virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
+
+ const BufferDescriptor& GetBufferDescriptor() const { return mDescriptor; }
+
+protected:
+ bool Upload(nsIntRegion *aRegion = nullptr);
+ bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+ bool EnsureWrappingTextureSource();
+
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ BufferDescriptor mDescriptor;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<DataTextureSource> mFirstSource;
+ nsIntRegion mMaybeUpdatedRegion;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ uint32_t mUpdateSerial;
+ bool mLocked;
+ bool mNeedsFullUpdate;
+ bool mHasIntermediateBuffer;
+
+ class DataTextureSourceYCbCrBasic;
+};
+
+/**
+ * TextureHost that wraps shared memory.
+ * the corresponding texture on the client side is ShmemTextureClient.
+ * This TextureHost is backend-independent.
+ */
+class ShmemTextureHost : public BufferTextureHost
+{
+public:
+ ShmemTextureHost(const mozilla::ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+protected:
+ ~ShmemTextureHost();
+
+public:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual const char *Name() override { return "ShmemTextureHost"; }
+
+ virtual void OnShutdown() override;
+
+protected:
+ UniquePtr<mozilla::ipc::Shmem> mShmem;
+ RefPtr<ISurfaceAllocator> mDeallocator;
+};
+
+/**
+ * TextureHost that wraps raw memory.
+ * The corresponding texture on the client side is MemoryTextureClient.
+ * Can obviously not be used in a cross process setup.
+ * This TextureHost is backend-independent.
+ */
+class MemoryTextureHost : public BufferTextureHost
+{
+public:
+ MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags);
+
+protected:
+ ~MemoryTextureHost();
+
+public:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual const char *Name() override { return "MemoryTextureHost"; }
+
+protected:
+ uint8_t* mBuffer;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHost
+{
+public:
+ explicit AutoLockTextureHost(TextureHost* aTexture)
+ : mTexture(aTexture)
+ {
+ mLocked = mTexture ? mTexture->Lock() : false;
+ }
+
+ ~AutoLockTextureHost()
+ {
+ if (mTexture && mLocked) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHostWithoutCompositor
+{
+public:
+ explicit AutoLockTextureHostWithoutCompositor(TextureHost* aTexture)
+ : mTexture(aTexture)
+ {
+ mLocked = mTexture ? mTexture->LockWithoutCompositor() : false;
+ }
+
+ ~AutoLockTextureHostWithoutCompositor()
+ {
+ if (mTexture && mLocked) {
+ mTexture->UnlockWithoutCompositor();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+/**
+ * This can be used as an offscreen rendering target by the compositor, and
+ * subsequently can be used as a source by the compositor.
+ */
+class CompositingRenderTarget: public TextureSource
+{
+public:
+
+ explicit CompositingRenderTarget(const gfx::IntPoint& aOrigin)
+ : mClearOnBind(false)
+ , mOrigin(aOrigin)
+ , mHasComplexProjection(false)
+ {}
+ virtual ~CompositingRenderTarget() {}
+
+ virtual const char* Name() const override { return "CompositingRenderTarget"; }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) { return nullptr; }
+#endif
+
+ /**
+ * Perform a clear when recycling a non opaque surface.
+ * The clear is deferred to when the render target is bound.
+ */
+ void ClearOnBind() {
+ mClearOnBind = true;
+ }
+
+ const gfx::IntPoint& GetOrigin() const { return mOrigin; }
+ gfx::IntRect GetRect() { return gfx::IntRect(GetOrigin(), GetSize()); }
+
+ /**
+ * If a Projection matrix is set, then it is used for rendering to
+ * this render target instead of generating one. If no explicit
+ * projection is set, Compositors are expected to generate an
+ * orthogonal maaping that maps 0..1 to the full size of the render
+ * target.
+ */
+ bool HasComplexProjection() const { return mHasComplexProjection; }
+ void ClearProjection() { mHasComplexProjection = false; }
+ void SetProjection(const gfx::Matrix4x4& aNewMatrix, bool aEnableDepthBuffer,
+ float aZNear, float aZFar)
+ {
+ mProjectionMatrix = aNewMatrix;
+ mEnableDepthBuffer = aEnableDepthBuffer;
+ mZNear = aZNear;
+ mZFar = aZFar;
+ mHasComplexProjection = true;
+ }
+ void GetProjection(gfx::Matrix4x4& aMatrix, bool& aEnableDepth, float& aZNear, float& aZFar)
+ {
+ MOZ_ASSERT(mHasComplexProjection);
+ aMatrix = mProjectionMatrix;
+ aEnableDepth = mEnableDepthBuffer;
+ aZNear = mZNear;
+ aZFar = mZFar;
+ }
+protected:
+ bool mClearOnBind;
+
+private:
+ gfx::IntPoint mOrigin;
+
+ gfx::Matrix4x4 mProjectionMatrix;
+ float mZNear, mZFar;
+ bool mHasComplexProjection;
+ bool mEnableDepthBuffer;
+};
+
+/**
+ * Creates a TextureHost that can be used with any of the existing backends
+ * Not all SurfaceDescriptor types are supported
+ */
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/TiledContentHost.cpp b/system/graphics/layers/composite/TiledContentHost.cpp
new file mode 100644
index 000000000..a80c47fb9
--- /dev/null
+++ b/system/graphics/layers/composite/TiledContentHost.cpp
@@ -0,0 +1,643 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TiledContentHost.h"
+#include "gfxPrefs.h" // for gfxPrefs
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/Compositor.h" // for Compositor
+//#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsPoint.h" // for IntPoint
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsRect.h" // for IntRect
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+using namespace gfx;
+namespace layers {
+
+class Layer;
+
+float
+TileHost::GetFadeInOpacity(float aOpacity)
+{
+ TimeStamp now = TimeStamp::Now();
+ if (!gfxPrefs::LayerTileFadeInEnabled() ||
+ mFadeStart.IsNull() ||
+ now < mFadeStart)
+ {
+ return aOpacity;
+ }
+
+ float duration = gfxPrefs::LayerTileFadeInDuration();
+ float elapsed = (now - mFadeStart).ToMilliseconds();
+ if (elapsed > duration) {
+ mFadeStart = TimeStamp();
+ return aOpacity;
+ }
+ return aOpacity * (elapsed / duration);
+}
+
+TiledLayerBufferComposite::TiledLayerBufferComposite()
+ : mFrameResolution()
+{}
+
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+ Clear();
+}
+
+void
+TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ for (TileHost& tile : mRetainedTiles) {
+ if (tile.IsPlaceholderTile()) continue;
+ tile.mTextureHost->SetCompositor(aCompositor);
+ if (tile.mTextureHostOnWhite) {
+ tile.mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+ }
+}
+
+void
+TiledLayerBufferComposite::AddAnimationInvalidation(nsIntRegion& aRegion)
+{
+ // We need to invalidate rects where we have a tile that is in the
+ // process of fading in.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ if (!mRetainedTiles[i].mFadeStart.IsNull()) {
+ TileIntPoint position = mTiles.TilePosition(i);
+ IntPoint offset = GetTileOffset(position);
+ nsIntRegion tileRegion = IntRect(offset, GetScaledTileSize());
+ aRegion.OrWith(tileRegion);
+ }
+ }
+}
+
+TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo)
+ , mTiledBuffer(TiledLayerBufferComposite())
+ , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
+{
+ MOZ_COUNT_CTOR(TiledContentHost);
+}
+
+TiledContentHost::~TiledContentHost()
+{
+ MOZ_COUNT_DTOR(TiledContentHost);
+}
+
+already_AddRefed<TexturedEffect>
+TiledContentHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ // If we can use hwc for this TiledContentHost, it implies that we have exactly
+ // one high precision tile. Please check TiledContentHost::GetRenderState() for
+ // all condition.
+ MOZ_ASSERT(mTiledBuffer.GetTileCount() == 1 && mLowPrecisionTiledBuffer.GetTileCount() == 0);
+ MOZ_ASSERT(mTiledBuffer.GetTile(0).mTextureHost);
+
+ TileHost& tile = mTiledBuffer.GetTile(0);
+ if (!tile.mTextureHost->BindTextureSource(tile.mTextureSource)) {
+ return nullptr;
+ }
+
+ return CreateTexturedEffect(tile.mTextureSource,
+ nullptr,
+ aSamplingFilter,
+ true,
+ tile.mTextureHost->GetRenderState());
+}
+
+void
+TiledContentHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags /* = NO_FLAGS */)
+{
+ CompositableHost::Attach(aLayer, aCompositor, aFlags);
+}
+
+void
+TiledContentHost::Detach(Layer* aLayer,
+ AttachFlags aFlags /* = NO_FLAGS */)
+{
+ if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
+ // Clear the TiledLayerBuffers, which will take care of releasing the
+ // copy-on-write locks.
+ mTiledBuffer.Clear();
+ mLowPrecisionTiledBuffer.Clear();
+ }
+ CompositableHost::Detach(aLayer,aFlags);
+}
+
+bool
+TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor)
+{
+ if (aTiledDescriptor.resolution() < 1) {
+ if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ } else {
+ if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+ CompositableTextureSourceRef& aTextureSource,
+ const IntRect& aUpdateRect,
+ Compositor* aCompositor)
+{
+ MOZ_ASSERT(aTexture);
+ if (!aTexture) {
+ return;
+ }
+
+ if (aCompositor) {
+ aTexture->SetCompositor(aCompositor);
+ }
+
+ if (!aUpdateRect.IsEmpty()) {
+ // For !HasIntermediateBuffer() textures, this is likely a no-op.
+ nsIntRegion region = aUpdateRect;
+ aTexture->Updated(&region);
+ }
+
+ aTexture->PrepareTextureSource(aTextureSource);
+}
+
+class TextureSourceRecycler
+{
+public:
+ explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet)
+ : mTiles(Move(aTileSet))
+ , mFirstPossibility(0)
+ {}
+
+ // Attempts to recycle a texture source that is already bound to the
+ // texture host for aTile.
+ void RecycleTextureSourceForTile(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ // Skip over existing tiles without a retained texture source
+ // and make sure we don't iterate them in the future.
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ // If this tile matches, then copy across the retained texture source (if
+ // any).
+ if (aTile.mTextureHost == mTiles[i].mTextureHost) {
+ aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ // Attempts to recycle any texture source to avoid needing to allocate
+ // a new one.
+ void RecycleTextureSource(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ if (mTiles[i].mTextureSource &&
+ mTiles[i].mTextureHost->GetFormat() == aTile.mTextureHost->GetFormat()) {
+ aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ void RecycleTileFading(TileHost& aTile) {
+ for (size_t i = 0; i < mTiles.Length(); i++) {
+ if (mTiles[i].mTextureHost == aTile.mTextureHost) {
+ aTile.mFadeStart = mTiles[i].mFadeStart;
+ }
+ }
+ }
+
+protected:
+ nsTArray<TileHost> mTiles;
+ size_t mFirstPossibility;
+};
+
+bool
+TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+ Compositor* aCompositor,
+ ISurfaceAllocator* aAllocator)
+{
+ if (mResolution != aTiles.resolution() ||
+ aTiles.tileSize() != mTileSize) {
+ Clear();
+ }
+ MOZ_ASSERT(aAllocator);
+ MOZ_ASSERT(aCompositor);
+ if (!aAllocator || !aCompositor) {
+ return false;
+ }
+
+ if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+ // There are divisions by mResolution so this protects the compositor process
+ // against malicious content processes and fuzzing.
+ return false;
+ }
+
+ TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
+ aTiles.retainedWidth(), aTiles.retainedHeight());
+
+ const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+ TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles));
+ mRetainedTiles.SetLength(tileDescriptors.Length());
+
+ // Step 1, deserialize the incoming set of tiles into mRetainedTiles, and attempt
+ // to recycle the TextureSource for any repeated tiles.
+ //
+ // Since we don't have any retained 'tile' object, we have to search for instances
+ // of the same TextureHost in the old tile set. The cost of binding a TextureHost
+ // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really
+ // high, so we avoid this whenever possible.
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+
+ TileHost& tile = mRetainedTiles[i];
+
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ NS_WARNING_ASSERTION(
+ tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor,
+ "Unrecognised tile descriptor type");
+ continue;
+ }
+
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+ tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent());
+ tile.mTextureHost->SetCompositor(aCompositor);
+ tile.mTextureHost->DeserializeReadLock(texturedDesc.sharedLock(), aAllocator);
+
+ if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+ tile.mTextureHostOnWhite = TextureHost::AsTextureHost(
+ texturedDesc.textureOnWhite().get_PTextureParent()
+ );
+ tile.mTextureHostOnWhite->DeserializeReadLock(
+ texturedDesc.sharedLockOnWhite(), aAllocator
+ );
+ }
+
+ tile.mTilePosition = newTiles.TilePosition(i);
+
+ // If this same tile texture existed in the old tile set then this will move the texture
+ // source into our new tile.
+ oldRetainedTiles.RecycleTextureSourceForTile(tile);
+
+ // If this tile is in the process of fading, we need to keep that going
+ oldRetainedTiles.RecycleTileFading(tile);
+
+ if (aTiles.isProgressive() &&
+ texturedDesc.wasPlaceholder())
+ {
+ // This is a progressive paint, and the tile used to be a placeholder.
+ // We need to begin fading it in (if enabled via layers.tiles.fade-in.enabled)
+ tile.mFadeStart = TimeStamp::Now();
+
+ aCompositor->CompositeUntil(tile.mFadeStart +
+ TimeDuration::FromMilliseconds(gfxPrefs::LayerTileFadeInDuration()));
+ }
+ }
+
+ // Step 2, attempt to recycle unused texture sources from the old tile set into new tiles.
+ //
+ // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way
+ // to ensure that any implicit locking on the old gralloc image is released.
+ for (TileHost& tile : mRetainedTiles) {
+ if (!tile.mTextureHost || tile.mTextureSource) {
+ continue;
+ }
+ oldRetainedTiles.RecycleTextureSource(tile);
+ }
+
+ // Step 3, handle the texture uploads, texture source binding and release the
+ // copy-on-write locks for textures with an internal buffer.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ TileHost& tile = mRetainedTiles[i];
+ if (!tile.mTextureHost) {
+ continue;
+ }
+
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+ UseTileTexture(tile.mTextureHost,
+ tile.mTextureSource,
+ texturedDesc.updateRect(),
+ aCompositor);
+
+ if (tile.mTextureHostOnWhite) {
+ UseTileTexture(tile.mTextureHostOnWhite,
+ tile.mTextureSourceOnWhite,
+ texturedDesc.updateRect(),
+ aCompositor);
+ }
+ }
+
+ mTiles = newTiles;
+ mTileSize = aTiles.tileSize();
+ mTileOrigin = aTiles.tileOrigin();
+ mValidRegion = aTiles.validRegion();
+ mResolution = aTiles.resolution();
+ mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+ aTiles.frameYResolution());
+
+ return true;
+}
+
+void
+TiledLayerBufferComposite::Clear()
+{
+ mRetainedTiles.Clear();
+ mTiles.mFirst = TileIntPoint();
+ mTiles.mSize = TileIntSize();
+ mValidRegion = nsIntRegion();
+ mResolution = 1.0;
+}
+
+void
+TiledContentHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion /* = nullptr */)
+{
+ MOZ_ASSERT(mCompositor);
+ // Reduce the opacity of the low-precision buffer to make it a
+ // little more subtle and less jarring. In particular, text
+ // rendered at low-resolution and scaled tends to look pretty
+ // heavy and this helps mitigate that. When we reduce the opacity
+ // we also make sure to draw the background color behind the
+ // reduced-opacity tile so that content underneath doesn't show
+ // through.
+ // However, in cases where the background is transparent, or the layer
+ // already has some opacity, we want to skip this behaviour. Otherwise
+ // we end up changing the expected overall transparency of the content,
+ // and it just looks wrong.
+ Color backgroundColor;
+ if (aOpacity == 1.0f && gfxPrefs::LowPrecisionOpacity() < 1.0f) {
+ // Background colors are only stored on scrollable layers. Grab
+ // the one from the nearest scrollable ancestor layer.
+ for (LayerMetricsWrapper ancestor(GetLayer(), LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
+ if (ancestor.Metrics().IsScrollable()) {
+ backgroundColor = ancestor.Metadata().GetBackgroundColor();
+ break;
+ }
+ }
+ }
+ float lowPrecisionOpacityReduction =
+ (aOpacity == 1.0f && backgroundColor.a == 1.0f)
+ ? gfxPrefs::LowPrecisionOpacity() : 1.0f;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion = aVisibleRegion;
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ }
+
+ // Render the low and high precision buffers.
+ RenderLayerBuffer(mLowPrecisionTiledBuffer,
+ lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
+ aEffectChain, lowPrecisionOpacityReduction * aOpacity,
+ aSamplingFilter, aClipRect, *renderRegion, aTransform);
+ RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
+ aClipRect, *renderRegion, aTransform);
+}
+
+
+void
+TiledContentHost::RenderTile(TileHost& aTile,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion,
+ const IntPoint& aTextureOffset,
+ const IntSize& aTextureBounds,
+ const gfx::Rect& aVisibleRect)
+{
+ MOZ_ASSERT(!aTile.IsPlaceholderTile());
+
+ AutoLockTextureHost autoLock(aTile.mTextureHost);
+ AutoLockTextureHost autoLockOnWhite(aTile.mTextureHostOnWhite);
+ if (autoLock.Failed() ||
+ autoLockOnWhite.Failed()) {
+ NS_WARNING("Failed to lock tile");
+ return;
+ }
+
+ if (!aTile.mTextureHost->BindTextureSource(aTile.mTextureSource)) {
+ return;
+ }
+
+ if (aTile.mTextureHostOnWhite && !aTile.mTextureHostOnWhite->BindTextureSource(aTile.mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect =
+ CreateTexturedEffect(aTile.mTextureSource,
+ aTile.mTextureSourceOnWhite,
+ aSamplingFilter,
+ true,
+ aTile.mTextureHost->GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ float opacity = aTile.GetFadeInOpacity(aOpacity);
+ aEffectChain.mPrimaryEffect = effect;
+
+ for (auto iter = aScreenRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
+ Rect textureRect(rect.x - aTextureOffset.x, rect.y - aTextureOffset.y,
+ rect.width, rect.height);
+
+ effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
+ textureRect.y / aTextureBounds.height,
+ textureRect.width / aTextureBounds.width,
+ textureRect.height / aTextureBounds.height);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
+ }
+ DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
+ if (aTile.mTextureHostOnWhite) {
+ flags |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ mCompositor->DrawDiagnostics(flags,
+ aScreenRegion, aClipRect, aTransform, mFlashCounter);
+}
+
+void
+TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aVisibleRegion,
+ gfx::Matrix4x4 aTransform)
+{
+ if (!mCompositor) {
+ NS_WARNING("Can't render tiled content host - no compositor");
+ return;
+ }
+ float resolution = aLayerBuffer.GetResolution();
+ gfx::Size layerScale(1, 1);
+
+ // We assume that the current frame resolution is the one used in our high
+ // precision layer buffer. Compensate for a changing frame resolution when
+ // rendering the low precision buffer.
+ if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
+ const CSSToParentLayerScale2D& layerResolution = aLayerBuffer.GetFrameResolution();
+ const CSSToParentLayerScale2D& localResolution = mTiledBuffer.GetFrameResolution();
+ layerScale.width = layerResolution.xScale / localResolution.xScale;
+ layerScale.height = layerResolution.yScale / localResolution.yScale;
+ aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure we don't render at low resolution where we have valid high
+ // resolution content, to avoid overdraw and artifacts with semi-transparent
+ // layers.
+ nsIntRegion maskRegion;
+ if (resolution != mTiledBuffer.GetResolution()) {
+ maskRegion = mTiledBuffer.GetValidRegion();
+ // XXX This should be ScaleRoundIn, but there is no such function on
+ // nsIntRegion.
+ maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure the resolution and difference in frame resolution are accounted
+ // for in the layer transform.
+ aTransform.PreScale(1/(resolution * layerScale.width),
+ 1/(resolution * layerScale.height), 1);
+
+ DiagnosticFlags componentAlphaDiagnostic = DiagnosticFlags::NO_DIAGNOSTIC;
+
+ nsIntRegion compositeRegion = aLayerBuffer.GetValidRegion();
+ compositeRegion.AndWith(aVisibleRegion);
+ compositeRegion.SubOut(maskRegion);
+
+ IntRect visibleRect = aVisibleRegion.GetBounds();
+
+ if (compositeRegion.IsEmpty()) {
+ return;
+ }
+
+ if (aBackgroundColor) {
+ nsIntRegion backgroundRegion = compositeRegion;
+ backgroundRegion.ScaleRoundOut(resolution, resolution);
+ EffectChain effect;
+ effect.mPrimaryEffect = new EffectSolidColor(*aBackgroundColor);
+ for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
+ }
+ }
+
+ for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
+ TileHost& tile = aLayerBuffer.GetTile(i);
+ if (tile.IsPlaceholderTile()) {
+ continue;
+ }
+
+ TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i);
+ // A sanity check that catches a lot of mistakes.
+ MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y);
+
+ IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
+ tileDrawRegion.AndWith(compositeRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ tileDrawRegion.ScaleRoundOut(resolution, resolution);
+ RenderTile(tile, aEffectChain, aOpacity,
+ aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
+ tileOffset * resolution, aLayerBuffer.GetTileSize(),
+ gfx::Rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height));
+ if (tile.mTextureHostOnWhite) {
+ componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ }
+
+ gfx::Rect rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height);
+ GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTENT | componentAlphaDiagnostic,
+ rect, aClipRect, aTransform, mFlashCounter);
+}
+
+void
+TiledContentHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get();
+
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+void
+TiledContentHost::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+ mTiledBuffer.Dump(aStream, aPrefix, aDumpHtml,
+ TextureDumpMode::DoNotCompress /* compression not supported on host side */);
+}
+
+void
+TiledContentHost::AddAnimationInvalidation(nsIntRegion& aRegion)
+{
+ return mTiledBuffer.AddAnimationInvalidation(aRegion);
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/composite/TiledContentHost.h b/system/graphics/layers/composite/TiledContentHost.h
new file mode 100644
index 000000000..4b52394de
--- /dev/null
+++ b/system/graphics/layers/composite/TiledContentHost.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TILEDCONTENTHOST_H
+#define GFX_TILEDCONTENTHOST_H
+
+#include <stdint.h> // for uint16_t
+#include <stdio.h> // for FILE
+#include <algorithm> // for swap
+#include "ContentHost.h" // for ContentHost
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer, etc
+#include "CompositableHost.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+
+namespace layers {
+
+class Compositor;
+class ISurfaceAllocator;
+class Layer;
+class ThebesBufferData;
+class TextureReadLock;
+struct EffectChain;
+
+
+class TileHost {
+public:
+ // Constructs a placeholder TileHost. See the comments above
+ // TiledLayerBuffer for more information on what this is used for;
+ // essentially, this is a sentinel used to represent an invalid or blank
+ // tile.
+ TileHost()
+ {}
+
+ // Constructs a TileHost from a TextureReadLock and TextureHost.
+ TileHost(TextureReadLock* aSharedLock,
+ TextureHost* aTextureHost,
+ TextureHost* aTextureHostOnWhite,
+ TextureSource* aSource,
+ TextureSource* aSourceOnWhite)
+ : mTextureHost(aTextureHost)
+ , mTextureHostOnWhite(aTextureHostOnWhite)
+ , mTextureSource(aSource)
+ , mTextureSourceOnWhite(aSourceOnWhite)
+ {}
+
+ TileHost(const TileHost& o) {
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTilePosition = o.mTilePosition;
+ }
+ TileHost& operator=(const TileHost& o) {
+ if (this == &o) {
+ return *this;
+ }
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTilePosition = o.mTilePosition;
+ return *this;
+ }
+
+ bool operator== (const TileHost& o) const {
+ return mTextureHost == o.mTextureHost;
+ }
+ bool operator!= (const TileHost& o) const {
+ return mTextureHost != o.mTextureHost;
+ }
+
+ bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
+
+ void Dump(std::stringstream& aStream) {
+ aStream << "TileHost(...)"; // fill in as needed
+ }
+
+ void DumpTexture(std::stringstream& aStream, TextureDumpMode /* aCompress, ignored for host tiles */) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ CompositableHost::DumpTextureHost(aStream, mTextureHost);
+ }
+
+ /**
+ * This does a linear tween of the passed opacity (which is assumed
+ * to be between 0.0 and 1.0). The duration of the fade is controlled
+ * by the 'layers.tiles.fade-in.duration-ms' preference. It is enabled
+ * via 'layers.tiles.fade-in.enabled'
+ */
+ float GetFadeInOpacity(float aOpacity);
+
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ mutable CompositableTextureSourceRef mTextureSource;
+ mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+ // This is not strictly necessary but makes debugging whole lot easier.
+ TileIntPoint mTilePosition;
+ TimeStamp mFadeStart;
+};
+
+class TiledLayerBufferComposite
+ : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
+{
+ friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
+
+public:
+ TiledLayerBufferComposite();
+ ~TiledLayerBufferComposite();
+
+ bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+ Compositor* aCompositor,
+ ISurfaceAllocator* aAllocator);
+
+ void Clear();
+
+ TileHost GetPlaceholderTile() const { return TileHost(); }
+
+ // Stores the absolute resolution of the containing frame, calculated
+ // by the sum of the resolutions of all parent layers' FrameMetrics.
+ const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
+
+ void SetCompositor(Compositor* aCompositor);
+
+ void AddAnimationInvalidation(nsIntRegion& aRegion);
+protected:
+
+ CSSToParentLayerScale2D mFrameResolution;
+};
+
+/**
+ * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
+ * flakes, we have a unique update process. All the textures that back the
+ * tiles are added in the usual way, but Updated is called on the host side
+ * in response to a message that describes the transaction for every tile.
+ * Composition happens in the normal way.
+ *
+ * TiledContentHost has a TiledLayerBufferComposite which keeps hold of the tiles.
+ * Each tile has a reference to a texture host. During the layers transaction, we
+ * receive a list of descriptors for the client-side tile buffer tiles
+ * (UseTiledLayerBuffer). If we receive two transactions before a composition,
+ * we immediately unlock and discard the unused buffer.
+ *
+ * When the content host is composited, we first validate the TiledLayerBuffer
+ * (Upload), which calls Updated on each tile's texture host to make sure the
+ * texture data has been uploaded. For single-buffered tiles, we unlock at this
+ * point, for double-buffered tiles we unlock and discard the last composited
+ * buffer after compositing a new one. Rendering takes us to RenderTile which
+ * is similar to Composite for non-tiled ContentHosts.
+ */
+class TiledContentHost : public ContentHost
+{
+public:
+ explicit TiledContentHost(const TextureInfo& aTextureInfo);
+
+protected:
+ ~TiledContentHost();
+
+public:
+ virtual LayerRenderState GetRenderState() override
+ {
+ // If we have exactly one high precision tile, then we can support hwc.
+ if (mTiledBuffer.GetTileCount() == 1 &&
+ mLowPrecisionTiledBuffer.GetTileCount() == 0) {
+ TextureHost* host = mTiledBuffer.GetTile(0).mTextureHost;
+ if (host) {
+ MOZ_ASSERT(!mTiledBuffer.GetTile(0).mTextureHostOnWhite, "Component alpha not supported!");
+
+ gfx::IntPoint offset = mTiledBuffer.GetTileOffset(mTiledBuffer.GetPlacement().TilePosition(0));
+
+ // Don't try to use HWC if the content doesn't start at the top-left of the tile.
+ if (offset != GetValidRegion().GetBounds().TopLeft()) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState state = host->GetRenderState();
+ state.SetOffset(offset);
+ return state;
+ }
+ }
+ return LayerRenderState();
+ }
+
+ // Generate effect for layerscope when using hwc.
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack) override
+ {
+ NS_ERROR("N/A for tiled layers");
+ return false;
+ }
+
+ const nsIntRegion& GetValidLowPrecisionRegion() const
+ {
+ return mLowPrecisionTiledBuffer.GetValidRegion();
+ }
+
+ const nsIntRegion& GetValidRegion() const
+ {
+ return mTiledBuffer.GetValidRegion();
+ }
+
+ virtual void SetCompositor(Compositor* aCompositor) override
+ {
+ MOZ_ASSERT(aCompositor);
+ CompositableHost::SetCompositor(aCompositor);
+ mTiledBuffer.SetCompositor(aCompositor);
+ mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+ }
+
+ bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor);
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
+
+ virtual TiledContentHost* AsTiledContentHost() override { return this; }
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Detach(Layer* aLayer = nullptr,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) override;
+
+private:
+
+ void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const gfx::Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aMaskRegion,
+ gfx::Matrix4x4 aTransform);
+
+ // Renders a single given tile.
+ void RenderTile(TileHost& aTile,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion,
+ const gfx::IntPoint& aTextureOffset,
+ const gfx::IntSize& aTextureBounds,
+ const gfx::Rect& aVisibleRect);
+
+ void EnsureTileStore() {}
+
+ TiledLayerBufferComposite mTiledBuffer;
+ TiledLayerBufferComposite mLowPrecisionTiledBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/composite/X11TextureHost.cpp b/system/graphics/layers/composite/X11TextureHost.cpp
new file mode 100644
index 000000000..92b6f8e91
--- /dev/null
+++ b/system/graphics/layers/composite/X11TextureHost.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11TextureHost.h"
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#ifdef GL_PROVIDER_GLX
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/X11TextureSourceOGL.h"
+#endif
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureHost::X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor)
+ : TextureHost(aFlags)
+{
+ mSurface = aDescriptor.OpenForeign();
+
+ if (mSurface && !(aFlags & TextureFlags::DEALLOCATE_CLIENT)) {
+ mSurface->TakePixmap();
+ }
+}
+
+bool
+X11TextureHost::Lock()
+{
+ if (!mCompositor || !mSurface) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ switch (mCompositor->GetBackendType()) {
+ case LayersBackend::LAYERS_BASIC:
+ mTextureSource =
+ new X11TextureSourceBasic(mCompositor->AsBasicCompositor(), mSurface);
+ break;
+#ifdef GL_PROVIDER_GLX
+ case LayersBackend::LAYERS_OPENGL:
+ mTextureSource =
+ new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface);
+ break;
+#endif
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+X11TextureHost::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+SurfaceFormat
+X11TextureHost::GetFormat() const
+{
+ if (!mSurface) {
+ return SurfaceFormat::UNKNOWN;
+ }
+ gfxContentType type = mSurface->GetContentType();
+#ifdef GL_PROVIDER_GLX
+ if (mCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+ }
+#endif
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+IntSize
+X11TextureHost::GetSize() const
+{
+ if (!mSurface) {
+ return IntSize();
+ }
+ return mSurface->GetSize();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+X11TextureHost::GetAsSurface()
+{
+ if (!mTextureSource || !mTextureSource->AsSourceBasic()) {
+ return nullptr;
+ }
+ RefPtr<DrawTarget> tempDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ GetSize(), GetFormat());
+ if (!tempDT) {
+ return nullptr;
+ }
+ RefPtr<SourceSurface> surf = mTextureSource->AsSourceBasic()->GetSurface(tempDT);
+ if (!surf) {
+ return nullptr;
+ }
+ return surf->GetDataSurface();
+}
+
+}
+}
diff --git a/system/graphics/layers/composite/X11TextureHost.h b/system/graphics/layers/composite/X11TextureHost.h
new file mode 100644
index 000000000..1f1d34409
--- /dev/null
+++ b/system/graphics/layers/composite/X11TextureHost.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTUREHOST__H
+#define MOZILLA_GFX_X11TEXTUREHOST__H
+
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/gfx/Types.h"
+
+#include "gfxXlibSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureSource : public TextureSource
+{
+public:
+ // Called when the underlying X surface has been changed.
+ // Useful for determining whether to rebind a GLXPixmap to a texture.
+ virtual void Updated() = 0;
+
+ virtual const char* Name() const override { return "X11TextureSource"; }
+};
+
+// TextureHost for Xlib-backed TextureSources.
+class X11TextureHost : public TextureHost
+{
+public:
+ X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "X11TextureHost"; }
+#endif
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion*) override
+ {
+ if (mTextureSource)
+ mTextureSource->Updated();
+ }
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<X11TextureSource> mTextureSource;
+ RefPtr<gfxXlibSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTUREHOST__H
diff --git a/system/graphics/layers/composite/qrcode_table.h b/system/graphics/layers/composite/qrcode_table.h
new file mode 100644
index 000000000..553be28c1
--- /dev/null
+++ b/system/graphics/layers/composite/qrcode_table.h
@@ -0,0 +1,259 @@
+const char * const sQRCodeTable[] = {
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB2\xC7\x11\x94\xE3\x6\x63\xA0\xF1\x74\xAA\x80\x78\xE3\xFA\x94\xD0\x43\xBA\xBA\x73\xFD\xD4\x83\xEE\x8A\x23\x5\x4D\x6F\xEF\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x20\xA5\x11\xB4\xF2\x22\x6A\x84\x62\xF0\xAB\x80\x5A\xAB\xFA\x5D\xF0\x53\xBA\xBA\x97\x6D\xD2\x11\xAE\xA2\x23\x5\x84\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB7\xE4\x11\xAB\xCB\x7\x9B\xA0\xF8\xB6\xAA\x0\x48\xE3\xFB\x94\xD0\x4F\xBA\xBA\x13\xFD\xD7\x83\xEE\x8A\x23\x5\xD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x25\x86\x11\x8B\xDA\x23\x92\x84\x6B\x32\xAB\x0\x6A\xAB\xFB\x5D\xF0\x5F\xBA\xBA\xF7\x6D\xD1\x11\xAE\xA2\x23\x5\xC4\x4F\xE8\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB1\xAD\x11\xAF\xC3\x7\xC8\x20\xFB\x6C\xAA\x80\x68\xEB\xFB\x14\xD0\x4F\xBA\xBA\x13\xFD\xD4\x83\xEE\x82\x23\x5\x4D\x6F\xE9\x8E\x0",
+ "\xFE\xFB\xFC\x15\x50\x6E\xA0\xBB\x75\xE5\xDB\xA0\xAE\xC1\x69\x7\xFA\xAF\xE0\xF\x0\xCE\x51\x7E\x69\xCA\xE2\xE4\xF9\x53\x4D\x4C\x7A\xE2\x80\x67\x17\xF8\xB0\x50\x5B\x28\xBA\xD3\xFD\xD0\xCA\xEE\x9C\xF9\x5\xCD\x6F\xEE\xE3\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x8E\x11\x82\xA2\x22\x79\x4\x62\xAE\xAA\x0\x4A\xA3\xFA\x5D\xF0\x53\xBA\xBA\xD7\x6D\xD3\x11\xAE\xA2\x23\x5\x44\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x26\xEC\x11\xB0\xFA\x22\x39\x4\x61\x2A\xAB\x0\x7A\xA3\xFA\xDD\xF0\x53\xBA\xBA\x97\x6D\xD1\x11\xAE\xAA\x23\x5\xC4\x4F\xEE\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB6\x2A\x11\x81\x83\x7\x7A\xE0\xFE\xEC\xAA\x80\x58\xE7\xFA\x14\xD0\x43\xBA\xBA\x13\xFD\xD6\x83\xEE\x8A\x23\x5\xCD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x48\x11\xA1\x92\x23\x73\xC4\x6D\x68\xAB\x80\x7A\xAF\xFA\xDD\xF0\x53\xBA\xBA\xF7\x6D\xD0\x11\xAE\xA2\x23\x5\x4\x4F\xE8\xAA\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xB\xAA\x82\xDD\xCA\xAB\x3B\x86\xED\x73\x80\x71\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAE\xAA\xA5\xB5\xC9\xFB\x3B\x82\x23\x73\x80\x61\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x24\xAA\xBC\xD5\xCB\xFB\xFB\xB4\x69\x73\x80\x61\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x81\xAA\x89\xF4\xEC\xE2\xDF\x20\xA7\x73\x80\x63\x53\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6E\xAA\xA7\xC5\xCA\x1B\xBB\x94\xAF\x73\x80\x41\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCB\xAA\x80\xAD\xC9\x4B\xBB\x90\x61\x73\x80\x51\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x0\xAA\x94\xBD\xCA\xF3\x7B\xAC\x6D\x72\x0\x51\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA5\xAA\xB3\xD5\xC9\xA3\x7B\xA8\xA3\x72\x0\x41\x1B\xFB\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4A\xAA\x8F\xAD\xCB\x13\x3B\x8C\xAB\x72\x0\x71\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEF\xAA\xA8\xC5\xC8\x43\x3B\x88\x65\x72\x0\x61\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x65\xAA\xB1\xA5\xCA\x43\xFB\xBE\x2F\x72\x0\x61\x1B\xFB\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC0\xAA\x96\xCD\xC9\x13\xFB\xBA\xE1\x72\x0\x71\x1B\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2F\xAA\xAA\xB5\xCB\xA3\xBB\x9E\xE9\x72\x0\x41\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8A\xAA\x8D\xDD\xC8\xF3\xBB\x9A\x27\x72\x0\x51\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x62\xAA\xA6\xE5\xCA\xB3\x7B\xAF\xE9\x73\x0\x61\x1B\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xC7\xAA\x93\xC4\xED\xAA\x5F\x3B\x27\x73\x0\x63\x53\xFB\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x28\xAA\xBD\xF5\xCB\x53\x3B\x8F\x2F\x73\x0\x41\x1B\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8D\xAA\x9A\x9D\xC8\x3\x3B\x8B\xE1\x73\x0\x51\x1B\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x7\xAA\x83\xFD\xCA\x3\xFB\xBD\xAB\x73\x0\x51\x1B\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA2\xAA\xA4\x95\xC9\x53\xFB\xB9\x65\x73\x0\x41\x1B\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4D\xAA\x98\xED\xCB\xE3\xBB\x9D\x6D\x73\x0\x71\x1B\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE8\xAA\xBF\x85\xC8\xB3\xBB\x99\xA3\x73\x0\x61\x1B\xFB\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x90\xB5\xCA\xA0\xFB\xAF\xB7\x72\x80\x71\x13\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x50\xC4\x49\x39\xE5\x2A\x7A\xC3\x48\xF7\x4A\x0\x6F\x2B\xFA\x2C\x30\x4B\xC8\xBA\xE9\x25\xD7\x49\x2E\xB5\x39\x5\x83\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x8B\xA5\xCB\x40\xBB\x8F\x71\x72\x80\x51\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA6\xAA\xAC\xCD\xC8\x10\xBB\x8B\xBF\x72\x80\x41\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xB5\xAD\xCA\x10\x7B\xBD\xF5\x72\x80\x41\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x92\xC5\xC9\x40\x7B\xB9\x3B\x72\x80\x51\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xBC\xF4\xEF\xB9\x1F\xD\x33\x72\x80\x73\x5B\xFA\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x89\xD5\xC8\xA0\x3B\x99\xFD\x72\x80\x71\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2B\xAA\xA2\xED\xCA\xE0\xFB\xAC\x33\x73\x80\x41\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x85\x85\xC9\xB0\xFB\xA8\xFD\x73\x80\x51\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x61\xAA\xAB\xB4\xEF\x49\x9F\x1C\xF5\x73\x80\x73\x5B\xFB\x6B\x30\x44\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC4\xAA\x9E\x95\xC8\x50\xBB\x88\x3B\x73\x80\x71\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4E\xAA\x87\xF5\xCA\x50\x7B\xBE\x71\x73\x80\x71\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xEB\xAA\xB2\xD4\xED\x49\x5F\x2A\xBF\x73\x80\x73\x5B\xFA\xEB\x30\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4\xAA\x8E\xAC\xEF\xF9\x1F\xE\xB7\x73\x80\x43\x5B\xFA\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xA1\xAA\xA9\xC4\xEC\xA9\x1F\xA\x79\x73\x80\x53\x5B\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6A\xAA\xAF\x9D\xCB\x58\xFB\xA6\x75\x72\x0\x41\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCF\xAA\x88\xF5\xC8\x8\xFB\xA2\xBB\x72\x0\x51\x13\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x20\xAA\xB4\x8D\xCA\xB8\xBB\x86\xB3\x72\x0\x61\x13\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\xC\x6A\x55\x54\xF9\xB8\x5A\x60\xE7\x28\x27\x0\x5C\xA7\xF8\x3E\x70\x47\xB9\xBA\xFB\x6D\xD5\xC7\x2E\x88\x8F\x5\x8D\x5F\xEB\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xF\xAA\x8A\x85\xCB\xE8\x7B\xB4\x37\x72\x0\x71\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAA\xAA\xAD\xED\xC8\xB8\x7B\xB0\xF9\x72\x0\x61\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x7B\xFC\x12\x90\x6E\x90\xBB\x75\xE5\xDB\xAB\xAE\xC1\x15\x7\xFA\xAF\xE0\xF\x0\xC7\x48\xC0\xAA\x55\x5F\xAD\x29\xA2\x81\x71\xA4\x27\x0\x7F\xAB\xFA\xAC\x30\x5B\xB9\xBA\x9\x25\xD1\x41\x2E\x98\x8F\x5\x81\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xE0\xAA\xB6\xFD\xC9\x58\x3B\x90\x3F\x72\x0\x41\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8\xAA\x9D\xC5\xCB\x18\xFB\xA5\xF1\x73\x0\x71\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAD\xAA\xBA\xAD\xC8\x48\xFB\xA1\x3F\x73\x0\x61\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x42\xAA\x86\xD5\xCA\xF8\xBB\x85\x37\x73\x0\x51\x13\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE7\xAA\xA1\xBD\xC9\xA8\xBB\x81\xF9\x73\x0\x41\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x6D\xAA\xAA\x94\xEF\xE1\x5F\x27\xB3\x73\x0\x53\x5B\xFA\x6B\x30\x48\x46\xBA\x6E\x3D\xD7\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xC8\xAA\x8D\xFC\xEC\xB1\x5F\x23\x7D\x73\x0\x43\x5B\xFB\xEB\x30\x48\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x27\xAA\xA3\xCD\xCA\x48\x3B\x97\x75\x73\x0\x61\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x82\xAA\x84\xA5\xC9\x18\x3B\x93\xBB\x73\x0\x71\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xBE\xF5\xCA\x12\x3B\xAA\x37\x72\x80\x41\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x8B\xD4\xED\xB\x1F\x3E\xF9\x72\x80\x43\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xA5\xE5\xCB\xF2\x7B\x8A\xF1\x72\x80\x61\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x57\x9\x49\xC\xB5\x2B\x28\x43\x6D\xB1\x4A\x0\x7F\x27\xFA\xAC\x30\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\xC3\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x89\xA4\xEE\xEB\x9F\x28\x75\x72\x80\x63\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE\xAA\xAE\xCC\xED\xBB\x9F\x2C\xBB\x72\x80\x73\x57\xFB\x6B\x30\x48\x46\xBA\xE\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE1\xAA\x80\xFD\xCB\x42\xFB\x98\xB3\x72\x80\x51\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xA7\x95\xC8\x12\xFB\x9C\x7D\x72\x80\x41\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xAC\xAA\x8C\xAD\xCA\x52\x3B\xA9\xB3\x73\x80\x71\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xAB\xC5\xC9\x2\x3B\xAD\x7D\x73\x80\x61\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x97\xBD\xCB\xB2\x7B\x89\x75\x73\x80\x51\x1F\xFA\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xB0\xD5\xC8\xE2\x7B\x8D\xBB\x73\x80\x41\x1F\xFB\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC9\xAA\xA9\xB5\xCA\xE2\xBB\xBB\xF1\x73\x80\x41\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6C\xAA\x8E\xDD\xC9\xB2\xBB\xBF\x3F\x73\x80\x51\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xB2\xA5\xCB\x2\xFB\x9B\x37\x73\x80\x61\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\x8\xC9\x55\x52\xD1\xB9\xE0\x20\xFA\xAC\x26\x80\x5C\xAB\xF9\x3E\x70\x4B\xB9\xBA\xFB\x6D\xD7\xC7\x2E\x90\x8F\x5\xD\x5F\xE9\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x81\xDD\xCB\xEA\x3B\xA3\xF5\x72\x0\x71\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x48\xAA\xA6\xB5\xC8\xBA\x3B\xA7\x3B\x72\x0\x61\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x9A\xCD\xCA\xA\x7B\x83\x33\x72\x0\x51\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2\xAA\xAF\xEC\xED\x13\x5F\x17\xFD\x72\x0\x53\x57\xFA\x6B\x30\x44\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xF2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x88\xAA\xA4\xC5\xCB\x5A\xBB\xB1\xB7\x72\x0\x41\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2D\xAA\x83\xAD\xC8\xA\xBB\xB5\x79\x72\x0\x51\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xC2\xAA\xAD\x9C\xEE\xF3\xDF\x1\x71\x72\x0\x73\x57\xFA\x6B\x30\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x67\xAA\x8A\xF4\xED\xA3\xDF\x5\xBF\x72\x0\x63\x57\xFB\xEB\x30\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x8F\xAA\xA1\xCC\xEF\xE3\x1F\x30\x71\x73\x0\x53\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x2A\xAA\x94\xED\xC8\xFA\x3B\xA4\xBF\x73\x0\x51\x1F\xFB\x22\x10\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xC5\xAA\xA8\x95\xCA\x4A\x7B\x80\xB7\x73\x0\x61\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x60\xAA\x8F\xFD\xC9\x1A\x7B\x84\x79\x73\x0\x71\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEA\xAA\x96\x9D\xCB\x1A\xBB\xB2\x33\x73\x0\x71\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x4F\xAA\xA3\xBC\xEC\x3\x9F\x26\xFD\x73\x0\x73\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA0\xAA\x8D\x8D\xCA\xFA\xFB\x92\xF5\x73\x0\x51\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x5\xAA\xAA\xE5\xC9\xAA\xFB\x96\x3B\x73\x0\x41\x1F\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA4\xAA\x85\xD5\xCB\xB9\xBB\xA0\x2F\x72\x80\x51\x17\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x1\xAA\xA2\xBD\xC8\xE9\xBB\xA4\xE1\x72\x80\x41\x17\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEE\xAA\x9E\xC5\xCA\x59\xFB\x80\xE9\x72\x80\x71\x17\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7F\xED\x71\xD4\x9B\x13\x9B\xB2\xA0\xB5\x3B\x80\x4C\xA3\xF9\xCF\xB0\x50\xD4\xBA\xAE\x3D\xD1\x71\xEE\x8B\x1\x5\xB2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x27\xAA\xA4\xCC\xEA\xB2\xDF\x2D\x23\x73\x80\x73\x53\xFB\xEB\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x64\xAA\x3D\xBD\xCE\x79\x3B\xB3\x79\x73\x80\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x4C\xAA\xF3\xED\xCE\x6A\xFB\xB0\x2F\x73\x80\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xF\xAA\x6A\x9C\xEA\xA1\x1F\x2E\x75\x73\x80\x43\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x9F\xBD\xC9\x3B\x3B\xB5\x7B\x73\x80\x41\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xAE\xAA\x14\x85\xC9\xB9\xFB\xBB\x21\x73\x80\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x86\xAA\xDA\xD5\xC9\xAA\x3B\xB8\x77\x73\x80\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xC5\xAA\x51\xED\xC9\x28\xFB\xB6\x2D\x73\x80\x71\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x53\xAA\x49\x1F\xD5\x2E\x21\xC3\x5A\x63\x4B\x0\x7F\x23\xFB\xAC\x70\x4B\xC8\xBA\xE9\x25\xD4\x49\x2E\xBD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xC1\xAA\x1A\xD5\xCD\x29\x3B\xB7\xB7\x73\x80\x61\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xE9\xAA\xC6\xCC\xE9\x73\xDF\x24\xE1\x73\x80\x43\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x85\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAA\xAA\x5F\xBD\xCD\xB8\x3B\xBA\xBB\x73\x80\x41\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2\xAA\xA3\xC5\xCB\x8B\x7B\x91\x73\x73\x80\x71\x1B\xFB\x22\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x41\xAA\x28\xFD\xCB\x9\xBB\x9F\x29\x73\x80\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x69\xAA\xE6\xAD\xCB\x1A\x7B\x9C\x7F\x73\x80\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2A\xAA\x6D\x95\xCB\x98\xBB\x92\x25\x73\x80\x41\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x6D\xAA\xAD\x95\xCF\x1B\xBB\x9D\xE5\x73\x80\x41\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x2E\xAA\x26\xAD\xCF\x99\x7B\x93\xBF\x73\x80\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6\xAA\xE8\xFD\xCF\x8A\xBB\x90\xE9\x73\x80\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x45\xAA\x63\xC5\xCF\x8\x7B\x9E\xB3\x73\x80\x71\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x84\xAD\xC8\xDB\x7B\x95\xBD\x73\x80\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xE4\xAA\x1D\xDC\xEC\x10\x9F\xB\xE7\x73\x80\x63\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xCC\xAA\xC1\xC5\xC8\x4A\x7B\x98\xB1\x73\x80\x41\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x8F\xAA\x4A\xFD\xC8\xC8\xBB\x96\xEB\x73\x80\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xC8\xAA\x8A\xFD\xCC\x4B\xBB\x99\x2B\x73\x80\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x2D\x71\x6C\xF3\x16\x5B\x32\xB3\xE3\x3A\x80\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\xAE\x3D\xD2\x71\xEE\x8B\x1\x5\xF2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xA3\xAA\xCF\x95\xCC\xDA\xBB\x94\x27\x73\x80\x71\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE0\xAA\x44\xAD\xCC\x58\x7B\x9A\x7D\x73\x80\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x6C\xAA\x82\xF4\xEF\x2A\x9F\x39\xB1\x72\x0\x73\x53\xFA\xEB\x70\x40\x46\xBA\x4E\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x2F\xAA\x9\xCC\xEF\xA8\x5F\x37\xEB\x72\x0\x63\x53\xFB\x6B\x70\x44\x46\xBA\x2E\x3D\xD7\x38\xEE\x85\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x7\xAA\xC7\x9C\xEF\xBB\x9F\x34\xBD\x72\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x44\xAA\x4C\xA4\xEF\x39\x5F\x3A\xE7\x72\x0\x43\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x9E\xED\xCF\xF3\x7B\xA5\x27\x72\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x40\xAA\x15\xD5\xCF\x71\xBB\xAB\x7D\x72\x0\x41\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x68\xAA\xDB\x85\xCF\x62\x7B\xA8\x2B\x72\x0\x71\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2B\xAA\x50\xBD\xCF\xE0\xBB\xA6\x71\x72\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC9\xAA\xA5\x9C\xEC\x7A\x9F\x3D\x7F\x72\x0\x63\x53\xFB\x6B\x70\x40\x46\xBA\xE\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8A\xAA\x3C\xED\xC8\xB1\x7B\xA3\x25\x72\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA2\xAA\xF2\xBD\xC8\xA2\xBB\xA0\x73\x72\x0\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x47\x71\x14\xB3\x12\xB2\x32\x8A\xBB\x3B\x0\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\x8E\x3D\xD2\x71\xEE\x9B\x1\x5\x72\xAF\xEF\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA6\xAA\xAB\xCC\xE8\xEA\x5F\x31\xE9\x72\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE5\xAA\x32\xBD\xCC\x21\xBB\xAF\xB3\x72\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCD\xAA\xFC\xED\xCC\x32\x7B\xAC\xE5\x72\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8E\xAA\x77\xD5\xCC\xB0\xBB\xA2\xBF\x72\x0\x71\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x26\xAA\x99\xE4\xEE\xCA\xDF\x19\x77\x72\x0\x53\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x65\xAA\x12\xDC\xEE\x48\x1F\x17\x2D\x72\x0\x43\x53\xFA\xEB\x70\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x4D\xAA\xCE\xC5\xCA\x12\xFB\x84\x7B\x72\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE\xAA\x45\xFD\xCA\x90\x3B\x8A\x21\x72\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x85\xFD\xCE\x13\x3B\x85\xE1\x72\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA\xAA\xE\xC5\xCE\x91\xFB\x8B\xBB\x72\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x22\xAA\xD2\xDC\xEA\xCB\x1F\x18\xED\x72\x0\x43\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x54\x49\x49\xC5\x95\x2D\x8A\xC3\x65\x39\x4A\x80\x4F\x23\xFA\xAC\x70\x47\xC8\xBA\xC9\x25\xD7\x49\x2E\xBD\x39\x5\x43\x6F\xEB\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xAC\xC5\xC9\xD3\xFB\x8D\xB9\x72\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC0\xAA\x35\xB4\xED\x18\x1F\x13\xE3\x72\x0\x53\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE8\xAA\xE9\xAD\xC9\x42\xFB\x80\xB5\x72\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x83\x49\xEC\xAD\x2A\x4A\x3\x6D\x61\x4A\x80\x6F\x23\xFA\x2C\x70\x4F\xC8\xBA\xA9\x25\xD6\x49\x2E\xA5\x39\x5\xC3\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEC\xAA\xA2\x95\xCD\x43\x3B\x81\x2F\x72\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x56\x87\x49\xA7\x95\x2E\x4B\xC3\x6C\xFB\x4A\x80\x7F\x23\xFB\xAC\x70\x43\xC8\xBA\xE9\x25\xD4\x49\x2E\xB5\x39\x5\x83\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x87\xAA\xE7\xFD\xCD\xD2\x3B\x8C\x23\x72\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC4\xAA\x6C\xC5\xCD\x50\xFB\x82\x79\x72\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xB5\xA5\xCB\xD3\x3B\xBB\xF3\x72\x0\x51\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x4A\xAA\x2C\xD4\xEF\x18\xDF\x25\xA9\x72\x0\x53\x53\xFA\xEB\x70\x48\x46\xBA\xE\x3D\xD6\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x62\xAA\xF0\xCD\xCB\x42\x3B\xB6\xFF\x72\x0\x71\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x51\x9\x49\xF5\xCD\x28\x4A\xC3\x5B\x2B\x4A\x80\x6F\x23\xFB\xAC\x70\x4F\xC8\xBA\xA9\x25\xD5\x49\x2E\xA5\x39\x5\x3\x6F\xED\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xA9\xBC\xEB\xA\xDF\x27\x65\x72\x0\x73\x53\xFA\x6B\x70\x44\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x25\xAA\x30\xCD\xCF\xC1\x3B\xB9\x3F\x72\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xD\xAA\xFE\x9D\xCF\xD2\xFB\xBA\x69\x72\x0\x41\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x4E\xAA\x67\xEC\xEB\x19\x1F\x24\x33\x72\x0\x43\x53\xFB\x6B\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\xAC\xAA\x80\x84\xEC\xCA\x1F\x2F\x3D\x72\x0\x53\x53\xFA\xEB\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xEF\xAA\xB\xBC\xEC\x48\xDF\x21\x67\x72\x0\x43\x53\xFB\x6B\x70\x48\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC7\xAA\xC5\xEC\xEC\x5B\x1F\x22\x31\x72\x0\x73\x53\xFA\x6B\x70\x48\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x84\xAA\x4E\xD4\xEC\xD9\xDF\x2C\x6B\x72\x0\x63\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x9C\x9D\xCC\x13\xFB\xB3\xAB\x72\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x80\xAA\x17\xA5\xCC\x91\x3B\xBD\xF1\x72\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA8\xAA\xD9\xF5\xCC\x82\xFB\xBE\xA7\x72\x0\x51\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEB\xAA\x52\xCD\xCC\x0\x3B\xB0\xFD\x72\x0\x41\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xAE\xB5\xCA\x33\x7B\x9B\x35\x72\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x0\xAA\x25\x8D\xCA\xB1\xBB\x95\x6F\x72\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x28\xAA\xEB\xDD\xCA\xA2\x7B\x96\x39\x72\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6B\xAA\x60\xE5\xCA\x20\xBB\x98\x63\x72\x0\x41\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xA0\xE5\xCE\xA3\xBB\x97\xA3\x72\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x6F\xAA\x39\x94\xEA\x68\x5F\x9\xF9\x72\x0\x43\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD5\x38\xEE\x9D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x47\xAA\xE5\x8D\xCE\x32\xBB\x9A\xAF\x72\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4\xAA\x6E\xB5\xCE\xB0\x7B\x94\xF5\x72\x0\x71\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x89\xDD\xC9\x63\x7B\x9F\xFB\x72\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA5\xAA\x2\xE5\xC9\xE1\xBB\x91\xA1\x72\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8D\xAA\xCC\xB5\xC9\xF2\x7B\x92\xF7\x72\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xCE\xAA\x47\x8D\xC9\x70\xBB\x9C\xAD\x72\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x87\x8D\xCD\xF3\xBB\x93\x6D\x72\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCA\xAA\xC\xB5\xCD\x71\x7B\x9D\x37\x72\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE2\xAA\xC2\xE5\xCD\x62\xBB\x9E\x61\x72\x0\x71\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA1\xAA\x49\xDD\xCD\xE0\x7B\x90\x3B\x72\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE\xAA\xA2\xE5\xCB\x23\xBB\xAA\x35\x73\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4D\xAA\x29\xDD\xCB\xA1\x7B\xA4\x6F\x73\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x65\xAA\xE7\x8D\xCB\xB2\xBB\xA7\x39\x73\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x26\xAA\x6C\xB5\xCB\x30\x7B\xA9\x63\x73\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x61\xAA\xAC\xB5\xCF\xB3\x7B\xA6\xA3\x73\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x22\xAA\x35\xC4\xEB\x78\x9F\x38\xF9\x73\x0\x63\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA\xAA\xE9\xDD\xCF\x22\x7B\xAB\xAF\x73\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x49\xAA\x62\xE5\xCF\xA0\xBB\xA5\xF5\x73\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x97\xC4\xEC\x3A\x9F\x3E\xFB\x73\x0\x53\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD7\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE8\xAA\xE\xB5\xC8\xF1\x7B\xA0\xA1\x73\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC0\xAA\xC0\xE5\xC8\xE2\xBB\xA3\xF7\x73\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x83\xAA\x4B\xDD\xC8\x60\x7B\xAD\xAD\x73\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\xC4\xAA\x99\x94\xE8\xAA\x5F\x32\x6D\x73\x0\x63\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x87\xAA\x0\xE5\xCC\x61\xBB\xAC\x37\x73\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xAF\xAA\xCE\xB5\xCC\x72\x7B\xAF\x61\x73\x0\x51\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEC\xAA\x45\x8D\xCC\xF0\xBB\xA1\x3B\x73\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xB9\xF5\xCA\xC3\xFB\x8A\xF3\x73\x0\x71\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x16\xD0\x6E\x9C\xBB\x74\xB5\xDB\xA8\xAE\xC1\x51\x7\xFA\xAF\xE0\x1E\x0\xE6\xFF\x99\xAD\x0\xA7\x98\x9E\xEB\x91\x21\xFC\x26\x0\x4B\xB3\xF9\x77\x10\x52\xEC\xBA\x7F\xFD\xD1\x0\x2E\xA8\x8F\x5\x11\x2F\xEA\x23\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x2F\xAA\xFC\x9D\xCA\x52\xFB\x87\xFF\x73\x0\x51\x1B\xFB\x22\x50\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x6C\xAA\x77\xA5\xCA\xD0\x3B\x89\xA5\x73\x0\x41\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2B\xAA\xA5\xEC\xEA\x1A\x1F\x16\x65\x73\x0\x53\x53\xFA\xEB\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x68\xAA\x2E\xD4\xEA\x98\xDF\x18\x3F\x73\x0\x43\x53\xFB\x6B\x70\x40\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x40\xAA\xF2\xCD\xCE\xC2\x3B\x8B\x69\x73\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x3\xAA\x79\xF5\xCE\x40\xFB\x85\x33\x73\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE1\xAA\x8C\xD4\xED\xDA\xDF\x1E\x3D\x73\x0\x73\x53\xFA\x6B\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x9D\xDB\x5\x32\xAF\xEC\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x8A\x49\x9B\x9D\x2A\x9B\x3\x63\xE9\x4B\x80\x7F\x23\xFA\x2C\x70\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8A\xAA\xDB\xF5\xC9\x2\xFB\x83\x31\x73\x0\x41\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC9\xAA\x42\x84\xED\xC9\x1F\x1D\x6B\x73\x0\x43\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x90\xCD\xCD\x3\x3B\x82\xAB\x73\x0\x51\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xCD\xAA\x9\xBC\xE9\xC8\xDF\x1C\xF1\x73\x0\x53\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE5\xAA\xD5\xA5\xCD\x92\x3B\x8F\xA7\x73\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA6\xAA\x5E\x9D\xCD\x10\xFB\x81\xFD\x73\x0\x61\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x95\xB4\xEF\xDA\x1F\x28\x77\x73\x0\x73\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD6\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x28\xAA\xC\xC5\xCB\x11\xFB\xB6\x2D\x73\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x0\xAA\xC2\x95\xCB\x2\x3B\xB5\x7B\x73\x0\x41\x1B\xFB\x22\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x43\xAA\x49\xAD\xCB\x80\xFB\xBB\x21\x73\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4\xAA\x89\xAD\xCF\x3\xFB\xB4\xE1\x73\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x47\xAA\x2\x95\xCF\x81\x3B\xBA\xBB\x73\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x6F\xAA\xCC\xC5\xCF\x92\xFB\xB9\xED\x73\x0\x71\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2C\xAA\x47\xFD\xCF\x10\x3B\xB7\xB7\x73\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xA0\x95\xC8\xC3\x3B\xBC\xB9\x73\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8D\xAA\x2B\xAD\xC8\x41\xFB\xB2\xE3\x73\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xA5\xAA\xE5\xFD\xC8\x52\x3B\xB1\xB5\x73\x0\x51\x1B\xFA\xA2\x50\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE6\xAA\x6E\xC5\xC8\xD0\xFB\xBF\xEF\x73\x0\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA1\xAA\xAE\xC5\xCC\x53\xFB\xB0\x2F\x73\x0\x41\x1B\xFB\x22\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE2\xAA\x25\xFD\xCC\xD1\x3B\xBE\x75\x73\x0\x51\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xCA\xAA\xEB\xAD\xCC\xC2\xFB\xBD\x23\x73\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x89\xAA\x60\x95\xCC\x40\x3B\xB3\x79\x73\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x21\xAA\x9C\xED\xCA\x73\x7B\x98\xB1\x73\x0\x41\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x62\xAA\x5\x9C\xEE\xB8\x9F\x6\xEB\x73\x0\x43\x53\xFB\xEB\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x85\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4A\xAA\xD9\x85\xCA\xE2\x7B\x95\xBD\x73\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x9\xAA\x52\xBD\xCA\x60\xBB\x9B\xE7\x73\x0\x71\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4E\xAA\x80\xF4\xEA\xAA\x9F\x4\x27\x73\x0\x63\x53\xFB\x6B\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xD\xAA\x19\x85\xCE\x61\x7B\x9A\x7D\x73\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x25\xAA\xD7\xD5\xCE\x72\xBB\x99\x2B\x73\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x66\xAA\x4E\xA4\xEA\xB9\x5F\x7\x71\x73\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xBB\x85\xC9\x23\x7B\x9C\x7F\x73\x0\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC7\xAA\x22\xF4\xED\xE8\x9F\x2\x25\x73\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xEF\xAA\xFE\xED\xC9\xB2\x7B\x91\x73\x73\x0\x71\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xAC\xAA\x75\xD5\xC9\x30\xBB\x9F\x29\x73\x0\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEB\xAA\xB5\xD5\xCD\xB3\xBB\x90\xE9\x73\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA8\xAA\x2C\xA4\xE9\x78\x5F\xE\xB3\x73\x0\x63\x53\xFB\x6B\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x80\xAA\xE2\xF4\xE9\x6B\x9F\xD\xE5\x73\x0\x53\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC3\xAA\x7B\x85\xCD\xA0\x7B\x93\xBF\x73\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x25\xAA\x94\xB5\xCB\x30\x3B\xAA\x6B\x72\x80\x41\x13\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+};
diff --git a/system/graphics/layers/d3d11/BlendShaderConstants.h b/system/graphics/layers/d3d11/BlendShaderConstants.h
new file mode 100644
index 000000000..d89240c18
--- /dev/null
+++ b/system/graphics/layers/d3d11/BlendShaderConstants.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+#define MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+
+// These constants are shared between CompositorD3D11 and the blend pixel shader.
+#define PS_LAYER_RGB 0
+#define PS_LAYER_RGBA 1
+#define PS_LAYER_YCBCR 2
+#define PS_LAYER_COLOR 3
+
+// These must be in the same order as the Mask enum.
+#define PS_MASK_NONE 0
+#define PS_MASK 1
+
+// These must be in the same order as CompositionOp.
+#define PS_BLEND_MULTIPLY 0
+#define PS_BLEND_SCREEN 1
+#define PS_BLEND_OVERLAY 2
+#define PS_BLEND_DARKEN 3
+#define PS_BLEND_LIGHTEN 4
+#define PS_BLEND_COLOR_DODGE 5
+#define PS_BLEND_COLOR_BURN 6
+#define PS_BLEND_HARD_LIGHT 7
+#define PS_BLEND_SOFT_LIGHT 8
+#define PS_BLEND_DIFFERENCE 9
+#define PS_BLEND_EXCLUSION 10
+#define PS_BLEND_HUE 11
+#define PS_BLEND_SATURATION 12
+#define PS_BLEND_COLOR 13
+#define PS_BLEND_LUMINOSITY 14
+
+#if defined(__cplusplus)
+namespace mozilla {
+namespace layers {
+
+static inline int
+BlendOpToShaderConstant(gfx::CompositionOp aOp) {
+ return int(aOp) - int(gfx::CompositionOp::OP_MULTIPLY);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+// Sanity checks.
+namespace {
+static inline void BlendShaderConstantAsserts() {
+ static_assert(PS_MASK_NONE == int(mozilla::layers::MaskType::MaskNone), "shader constant is out of sync");
+ static_assert(PS_MASK == int(mozilla::layers::MaskType::Mask), "shader constant is out of sync");
+ static_assert(int(mozilla::gfx::CompositionOp::OP_LUMINOSITY) - int(mozilla::gfx::CompositionOp::OP_MULTIPLY) == 14,
+ "shader constants are out of sync");
+}
+} // anonymous namespace
+#endif
+
+#endif // MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
diff --git a/system/graphics/layers/d3d11/BlendingHelpers.hlslh b/system/graphics/layers/d3d11/BlendingHelpers.hlslh
new file mode 100644
index 000000000..57d27b23b
--- /dev/null
+++ b/system/graphics/layers/d3d11/BlendingHelpers.hlslh
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Helper functions.
+float hardlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest * (2.0 * src);
+ } else {
+ // Note: we substitute (2*src-1) into the screen formula below.
+ return 2.0 * dest + 2.0 * src - 1.0 - 2.0 * dest * src;
+ }
+}
+
+float dodge(float dest, float src) {
+ if (dest == 0.0) {
+ return 0.0;
+ } else if (src == 1.0) {
+ return 1.0;
+ } else {
+ return min(1.0, dest / (1.0 - src));
+ }
+}
+
+float burn(float dest, float src) {
+ if (dest == 1.0) {
+ return 1.0;
+ } else if (src == 0.0) {
+ return 0.0;
+ } else {
+ return 1.0 - min(1.0, (1.0 - dest) / src);
+ }
+}
+
+float darken(float dest) {
+ if (dest <= 0.25) {
+ return ((16.0 * dest - 12.0) * dest + 4.0) * dest;
+ } else {
+ return sqrt(dest);
+ }
+}
+
+float softlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);
+ } else {
+ return dest + (2.0 * src - 1.0) * (darken(dest) - dest);
+ }
+}
+
+float Lum(float3 c) {
+ return dot(float3(0.3, 0.59, 0.11), c);
+}
+
+float3 ClipColor(float3 c) {
+ float L = Lum(c);
+ float n = min(min(c.r, c.g), c.b);
+ float x = max(max(c.r, c.g), c.b);
+ if (n < 0.0) {
+ c = L + (((c - L) * L) / (L - n));
+ }
+ if (x > 1.0) {
+ c = L + (((c - L) * (1.0 - L)) / (x - L));
+ }
+ return c;
+}
+
+float3 SetLum(float3 c, float L) {
+ float d = L - Lum(c);
+ return ClipColor(float3(
+ c.r + d,
+ c.g + d,
+ c.b + d));
+}
+
+float Sat(float3 c) {
+ return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);
+}
+
+// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+float3 SetSatInner(float3 c, float s) {
+ if (c.b > c.r) {
+ c.g = (((c.g - c.r) * s) / (c.b - c.r));
+ c.b = s;
+ } else {
+ c.gb = float2(0.0, 0.0);
+ }
+ return float3(0.0, c.g, c.b);
+}
+
+float3 SetSat(float3 c, float s) {
+ if (c.r <= c.g) {
+ if (c.g <= c.b) {
+ c.rgb = SetSatInner(c.rgb, s);
+ } else if (c.r <= c.b) {
+ c.rbg = SetSatInner(c.rbg, s);
+ } else {
+ c.brg = SetSatInner(c.brg, s);
+ }
+ } else if (c.r <= c.b) {
+ c.grb = SetSatInner(c.grb, s);
+ } else if (c.g <= c.b) {
+ c.gbr = SetSatInner(c.gbr, s);
+ } else {
+ c.bgr = SetSatInner(c.bgr, s);
+ }
+ return c;
+}
+
+float3 BlendMultiply(float3 dest, float3 src) {
+ return dest * src;
+}
+
+float3 BlendScreen(float3 dest, float3 src) {
+ return dest + src - (dest * src);
+}
+
+float3 BlendOverlay(float3 dest, float3 src) {
+ return float3(
+ hardlight(src.r, dest.r),
+ hardlight(src.g, dest.g),
+ hardlight(src.b, dest.b));
+}
+
+float3 BlendDarken(float3 dest, float3 src) {
+ return min(dest, src);
+}
+
+float3 BlendLighten(float3 dest, float3 src) {
+ return max(dest, src);
+}
+
+float3 BlendColorDodge(float3 dest, float3 src) {
+ return float3(
+ dodge(dest.r, src.r),
+ dodge(dest.g, src.g),
+ dodge(dest.b, src.b));
+}
+
+float3 BlendColorBurn(float3 dest, float3 src) {
+ return float3(
+ burn(dest.r, src.r),
+ burn(dest.g, src.g),
+ burn(dest.b, src.b));
+}
+
+float3 BlendHardLight(float3 dest, float3 src) {
+ return float3(
+ hardlight(dest.r, src.r),
+ hardlight(dest.g, src.g),
+ hardlight(dest.b, src.b));
+}
+
+float3 BlendSoftLight(float3 dest, float3 src) {
+ return float3(
+ softlight(dest.r, src.r),
+ softlight(dest.g, src.g),
+ softlight(dest.b, src.b));
+}
+
+float3 BlendDifference(float3 dest, float3 src) {
+ return abs(dest - src);
+}
+
+float3 BlendExclusion(float3 dest, float3 src) {
+ return dest + src - 2.0 * dest * src;
+}
+
+float3 BlendHue(float3 dest, float3 src) {
+ return SetLum(SetSat(src, Sat(dest)), Lum(dest));
+}
+
+float3 BlendSaturation(float3 dest, float3 src) {
+ return SetLum(SetSat(dest, Sat(src)), Lum(dest));
+}
+
+float3 BlendColor(float3 dest, float3 src) {
+ return SetLum(src, Lum(dest));
+}
+
+float3 BlendLuminosity(float3 dest, float3 src) {
+ return SetLum(dest, Lum(src));
+}
diff --git a/system/graphics/layers/d3d11/CompositorD3D11.cpp b/system/graphics/layers/d3d11/CompositorD3D11.cpp
new file mode 100644
index 000000000..7655d097b
--- /dev/null
+++ b/system/graphics/layers/d3d11/CompositorD3D11.cpp
@@ -0,0 +1,1577 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorD3D11.h"
+
+#include "TextureD3D11.h"
+#include "CompositorD3D11Shaders.h"
+
+#include "gfxWindowsPlatform.h"
+#include "nsIWidget.h"
+#include "mozilla/gfx/D3D11Checks.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/ContentHost.h"
+#include "mozilla/layers/Effects.h"
+#include "nsWindowsHelpers.h"
+#include "gfxPrefs.h"
+#include "gfxConfig.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/StackArray.h"
+#include "mozilla/Services.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+
+#include "mozilla/EnumeratedArray.h"
+#include "BlendShaderConstants.h"
+
+#include "D3D11ShareHandleImage.h"
+
+#include <dxgi1_2.h>
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static bool CanUsePartialPresents(ID3D11Device* aDevice);
+
+struct Vertex
+{
+ float position[2];
+};
+
+// {1E4D7BEB-D8EC-4A0B-BF0A-63E6DE129425}
+static const GUID sDeviceAttachmentsD3D11 =
+{ 0x1e4d7beb, 0xd8ec, 0x4a0b, { 0xbf, 0xa, 0x63, 0xe6, 0xde, 0x12, 0x94, 0x25 } };
+// {88041664-C835-4AA8-ACB8-7EC832357ED8}
+static const GUID sLayerManagerCount =
+{ 0x88041664, 0xc835, 0x4aa8, { 0xac, 0xb8, 0x7e, 0xc8, 0x32, 0x35, 0x7e, 0xd8 } };
+
+const FLOAT sBlendFactor[] = { 0, 0, 0, 0 };
+
+namespace TexSlot {
+ static const int RGB = 0;
+ static const int Y = 1;
+ static const int Cb = 2;
+ static const int Cr = 3;
+ static const int RGBWhite = 4;
+ static const int Mask = 5;
+ static const int Backdrop = 6;
+}
+
+struct DeviceAttachmentsD3D11
+{
+ DeviceAttachmentsD3D11(ID3D11Device* device)
+ : mSyncHandle(0),
+ mDevice(device),
+ mInitOkay(true)
+ {}
+
+ bool CreateShaders();
+ bool InitBlendShaders();
+ bool InitSyncObject();
+
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11VertexShader>>
+ VertexShaderArray;
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11PixelShader>>
+ PixelShaderArray;
+
+ RefPtr<ID3D11InputLayout> mInputLayout;
+ RefPtr<ID3D11Buffer> mVertexBuffer;
+
+ VertexShaderArray mVSQuadShader;
+ VertexShaderArray mVSQuadBlendShader;
+ PixelShaderArray mSolidColorShader;
+ PixelShaderArray mRGBAShader;
+ PixelShaderArray mRGBShader;
+ PixelShaderArray mYCbCrShader;
+ PixelShaderArray mComponentAlphaShader;
+ PixelShaderArray mBlendShader;
+ RefPtr<ID3D11Buffer> mPSConstantBuffer;
+ RefPtr<ID3D11Buffer> mVSConstantBuffer;
+ RefPtr<ID3D11RasterizerState> mRasterizerState;
+ RefPtr<ID3D11SamplerState> mLinearSamplerState;
+ RefPtr<ID3D11SamplerState> mPointSamplerState;
+ RefPtr<ID3D11BlendState> mPremulBlendState;
+ RefPtr<ID3D11BlendState> mNonPremulBlendState;
+ RefPtr<ID3D11BlendState> mComponentBlendState;
+ RefPtr<ID3D11BlendState> mDisabledBlendState;
+ RefPtr<IDXGIResource> mSyncTexture;
+ HANDLE mSyncHandle;
+
+private:
+ void InitVertexShader(const ShaderBytes& aShader, VertexShaderArray& aArray, MaskType aMaskType) {
+ InitVertexShader(aShader, getter_AddRefs(aArray[aMaskType]));
+ }
+ void InitPixelShader(const ShaderBytes& aShader, PixelShaderArray& aArray, MaskType aMaskType) {
+ InitPixelShader(aShader, getter_AddRefs(aArray[aMaskType]));
+ }
+ void InitVertexShader(const ShaderBytes& aShader, ID3D11VertexShader** aOut) {
+ if (!mInitOkay) {
+ return;
+ }
+ if (Failed(mDevice->CreateVertexShader(aShader.mData, aShader.mLength, nullptr, aOut), "create vs")) {
+ mInitOkay = false;
+ }
+ }
+ void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut) {
+ if (!mInitOkay) {
+ return;
+ }
+ if (Failed(mDevice->CreatePixelShader(aShader.mData, aShader.mLength, nullptr, aOut), "create ps")) {
+ mInitOkay = false;
+ }
+ }
+
+ bool Failed(HRESULT hr, const char* aContext) {
+ if (SUCCEEDED(hr))
+ return false;
+
+ gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr);
+ return true;
+ }
+
+ // Only used during initialization.
+ RefPtr<ID3D11Device> mDevice;
+ bool mInitOkay;
+};
+
+CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mAttachments(nullptr)
+ , mHwnd(nullptr)
+ , mDisableSequenceForNextFrame(false)
+ , mAllowPartialPresents(false)
+ , mVerifyBuffersFailed(false)
+{
+}
+
+CompositorD3D11::~CompositorD3D11()
+{
+ if (mDevice) {
+ int referenceCount = 0;
+ UINT size = sizeof(referenceCount);
+ HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+ NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device.");
+ referenceCount--;
+ mDevice->SetPrivateData(sLayerManagerCount,
+ sizeof(referenceCount),
+ &referenceCount);
+
+ if (!referenceCount) {
+ DeviceAttachmentsD3D11 *attachments;
+ size = sizeof(attachments);
+ mDevice->GetPrivateData(sDeviceAttachmentsD3D11, &size, &attachments);
+ // No LayerManagers left for this device. Clear out interfaces stored
+ // which hold a reference to the device.
+ mDevice->SetPrivateData(sDeviceAttachmentsD3D11, 0, nullptr);
+
+ delete attachments;
+ }
+ }
+}
+
+bool
+CompositorD3D11::Initialize(nsCString* const out_failureReason)
+{
+ MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
+
+ HRESULT hr;
+
+ mDevice = DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!mDevice) {
+ gfxCriticalNote << "[D3D11] failed to get compositor device.";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE";
+ return false;
+ }
+
+ mDevice->GetImmediateContext(getter_AddRefs(mContext));
+ if (!mContext) {
+ gfxCriticalNote << "[D3D11] failed to get immediate context";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT";
+ return false;
+ }
+
+ mFeatureLevel = mDevice->GetFeatureLevel();
+
+ mHwnd = mWidget->AsWindows()->GetHwnd();
+
+ memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
+
+ int referenceCount = 0;
+ UINT size = sizeof(referenceCount);
+ // If this isn't there yet it'll fail, count will remain 0, which is correct.
+ mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+ referenceCount++;
+ mDevice->SetPrivateData(sLayerManagerCount,
+ sizeof(referenceCount),
+ &referenceCount);
+
+ size = sizeof(DeviceAttachmentsD3D11*);
+ if (FAILED(mDevice->GetPrivateData(sDeviceAttachmentsD3D11,
+ &size,
+ &mAttachments))) {
+ mAttachments = new DeviceAttachmentsD3D11(mDevice);
+ mDevice->SetPrivateData(sDeviceAttachmentsD3D11,
+ sizeof(mAttachments),
+ &mAttachments);
+
+ D3D11_INPUT_ELEMENT_DESC layout[] =
+ {
+ { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ };
+
+ hr = mDevice->CreateInputLayout(layout,
+ sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
+ LayerQuadVS,
+ sizeof(LayerQuadVS),
+ getter_AddRefs(mAttachments->mInputLayout));
+
+ if (Failed(hr, "CreateInputLayout")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_INPUT_LAYOUT";
+ return false;
+ }
+
+ Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} };
+ CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
+ D3D11_SUBRESOURCE_DATA data;
+ data.pSysMem = (void*)vertices;
+
+ hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mAttachments->mVertexBuffer));
+
+ if (Failed(hr, "create vertex buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_VERTEX_BUFFER";
+ return false;
+ }
+
+ if (!mAttachments->CreateShaders()) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_CREATE_SHADERS";
+ return false;
+ }
+
+ CD3D11_BUFFER_DESC cBufferDesc(sizeof(VertexShaderConstants),
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE);
+
+ hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVSConstantBuffer));
+ if (Failed(hr, "create vs buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_VS_BUFFER";
+ return false;
+ }
+
+ cBufferDesc.ByteWidth = sizeof(PixelShaderConstants);
+ hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mPSConstantBuffer));
+ if (Failed(hr, "create ps buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_PS_BUFFER";
+ return false;
+ }
+
+ CD3D11_RASTERIZER_DESC rastDesc(D3D11_DEFAULT);
+ rastDesc.CullMode = D3D11_CULL_NONE;
+ rastDesc.ScissorEnable = TRUE;
+
+ hr = mDevice->CreateRasterizerState(&rastDesc, getter_AddRefs(mAttachments->mRasterizerState));
+ if (Failed(hr, "create rasterizer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_RASTERIZER";
+ return false;
+ }
+
+ CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
+ hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mLinearSamplerState));
+ if (Failed(hr, "create linear sampler")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_LINEAR_SAMPLER";
+ return false;
+ }
+
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+ hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mPointSamplerState));
+ if (Failed(hr, "create point sampler")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_POINT_SAMPLER";
+ return false;
+ }
+
+ CD3D11_BLEND_DESC blendDesc(D3D11_DEFAULT);
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendPremul = {
+ TRUE,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendPremul;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mPremulBlendState));
+ if (Failed(hr, "create pm blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_PM_BLENDER";
+ return false;
+ }
+
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendNonPremul = {
+ TRUE,
+ D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendNonPremul;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mNonPremulBlendState));
+ if (Failed(hr, "create npm blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NPM_BLENDER";
+ return false;
+ }
+
+ if (gfxPrefs::ComponentAlphaEnabled()) {
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendComponent = {
+ TRUE,
+ D3D11_BLEND_ONE,
+ D3D11_BLEND_INV_SRC1_COLOR,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE,
+ D3D11_BLEND_INV_SRC_ALPHA,
+ D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendComponent;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mComponentBlendState));
+ if (Failed(hr, "create component blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_COMP_BLENDER";
+ return false;
+ }
+ }
+
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendDisabled = {
+ FALSE,
+ D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendDisabled;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mDisabledBlendState));
+ if (Failed(hr, "create null blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NULL_BLENDER";
+ return false;
+ }
+
+ if (!mAttachments->InitSyncObject()) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_OBJ_SYNC";
+ return false;
+ }
+ }
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> dxgiAdapter;
+
+ mDevice->QueryInterface(dxgiDevice.StartAssignment());
+ dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
+
+ {
+ RefPtr<IDXGIFactory> dxgiFactory;
+ dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));
+
+ DXGI_SWAP_CHAIN_DESC swapDesc;
+ ::ZeroMemory(&swapDesc, sizeof(swapDesc));
+ swapDesc.BufferDesc.Width = 0;
+ swapDesc.BufferDesc.Height = 0;
+ swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapDesc.BufferDesc.RefreshRate.Numerator = 60;
+ swapDesc.BufferDesc.RefreshRate.Denominator = 1;
+ swapDesc.SampleDesc.Count = 1;
+ swapDesc.SampleDesc.Quality = 0;
+ swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapDesc.BufferCount = 1;
+ swapDesc.OutputWindow = mHwnd;
+ swapDesc.Windowed = TRUE;
+ swapDesc.Flags = 0;
+ swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+
+
+ /**
+ * Create a swap chain, this swap chain will contain the backbuffer for
+ * the window we draw to. The front buffer is the full screen front
+ * buffer.
+ */
+ hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain));
+ if (Failed(hr, "create swap chain")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN";
+ return false;
+ }
+
+ // We need this because we don't want DXGI to respond to Alt+Enter.
+ dxgiFactory->MakeWindowAssociation(swapDesc.OutputWindow,
+ DXGI_MWA_NO_WINDOW_CHANGES);
+ }
+
+ if (!mWidget->InitCompositor(this)) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR";
+ return false;
+ }
+
+ mAllowPartialPresents = CanUsePartialPresents(mDevice);
+
+ return true;
+}
+
+static bool
+CanUsePartialPresents(ID3D11Device* aDevice)
+{
+ if (gfxPrefs::PartialPresent() > 0) {
+ return true;
+ }
+ if (gfxPrefs::PartialPresent() < 0) {
+ return false;
+ }
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (!D3D11Checks::GetDxgiDesc(aDevice, &desc)) {
+ return false;
+ }
+
+ // We have to disable partial presents on NVIDIA (bug 1189940).
+ if (desc.VendorId == 0x10de) {
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorD3D11::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN,
+ this, aFlags);
+ return result.forget();
+}
+
+TextureFactoryIdentifier
+CompositorD3D11::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident;
+ ident.mMaxTextureSize = GetMaxTextureSize();
+ ident.mParentProcessType = XRE_GetProcessType();
+ ident.mParentBackend = LayersBackend::LAYERS_D3D11;
+ ident.mSyncHandle = mAttachments->mSyncHandle;
+ return ident;
+}
+
+bool
+CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize)
+{
+ int32_t maxTextureSize = GetMaxTextureSize();
+
+ if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
+ return false;
+ }
+
+ return true;
+}
+
+int32_t
+CompositorD3D11::GetMaxTextureSize() const
+{
+ return GetMaxTextureSizeForFeatureLevel(mFeatureLevel);
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
+ SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<CompositingRenderTargetD3D11> rt = new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
+ rt->SetSize(IntSize(aRect.width, aRect.height));
+
+ if (aInit == INIT_MODE_CLEAR) {
+ FLOAT clear[] = { 0, 0, 0, 0 };
+ mContext->ClearRenderTargetView(rt->mRTView, clear);
+ }
+
+ return rt.forget();
+}
+
+RefPtr<ID3D11Texture2D>
+CompositorD3D11::CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ aRect.width, aRect.height, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed in CreateRenderTargetFromSource " << hexa(hr);
+ HandleError(hr);
+ return nullptr;
+ }
+
+ if (aSource) {
+ const CompositingRenderTargetD3D11* sourceD3D11 =
+ static_cast<const CompositingRenderTargetD3D11*>(aSource);
+
+ const IntSize& srcSize = sourceD3D11->GetSize();
+ MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0,
+ "render targets should have nonnegative sizes");
+
+ IntRect srcRect(IntPoint(), srcSize);
+ IntRect copyRect(aSourcePoint, aRect.Size());
+ if (!srcRect.Contains(copyRect)) {
+ NS_WARNING("Could not copy the whole copy rect from the render target");
+ }
+
+ copyRect = copyRect.Intersect(srcRect);
+
+ if (!copyRect.IsEmpty()) {
+ D3D11_BOX copyBox;
+ copyBox.front = 0;
+ copyBox.back = 1;
+ copyBox.left = copyRect.x;
+ copyBox.top = copyRect.y;
+ copyBox.right = copyRect.XMost();
+ copyBox.bottom = copyRect.YMost();
+
+ mContext->CopySubresourceRegion(texture, 0,
+ 0, 0, 0,
+ sourceD3D11->GetD3D11Texture(), 0,
+ &copyBox);
+ }
+ }
+
+ return texture;
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint &aSourcePoint)
+{
+ RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, aSource, aSourcePoint);
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<CompositingRenderTargetD3D11> rt =
+ new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
+ rt->SetSize(aRect.Size());
+
+ return rt.forget();
+}
+
+bool
+CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect,
+ RefPtr<ID3D11Texture2D>* aOutTexture,
+ RefPtr<ID3D11ShaderResourceView>* aOutView)
+{
+ RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, mCurrentRT, aRect.TopLeft());
+ if (!texture) {
+ return false;
+ }
+
+ CD3D11_SHADER_RESOURCE_VIEW_DESC desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM);
+
+ RefPtr<ID3D11ShaderResourceView> srv;
+ HRESULT hr = mDevice->CreateShaderResourceView(texture, &desc, getter_AddRefs(srv));
+ if (FAILED(hr) || !srv) {
+ return false;
+ }
+
+ *aOutTexture = texture.forget();
+ *aOutView = srv.forget();
+ return true;
+}
+
+void
+CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget);
+ CompositingRenderTargetD3D11* newRT =
+ static_cast<CompositingRenderTargetD3D11*>(aRenderTarget);
+ if (mCurrentRT != newRT) {
+ mCurrentRT = newRT;
+ mCurrentRT->BindRenderTarget(mContext);
+ }
+
+ if (newRT->HasComplexProjection()) {
+ gfx::Matrix4x4 projection;
+ bool depthEnable;
+ float zNear, zFar;
+ newRT->GetProjection(projection, depthEnable, zNear, zFar);
+ PrepareViewport(newRT->GetSize(), projection, zNear, zFar);
+ } else {
+ PrepareViewport(newRT->GetSize());
+ }
+}
+
+ID3D11PixelShader*
+CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType)
+{
+ switch (aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ return mAttachments->mSolidColorShader[aMaskType];
+ case EffectTypes::RENDER_TARGET:
+ return mAttachments->mRGBAShader[aMaskType];
+ case EffectTypes::RGB: {
+ SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
+ return (format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::R8G8B8A8)
+ ? mAttachments->mRGBAShader[aMaskType]
+ : mAttachments->mRGBShader[aMaskType];
+ }
+ case EffectTypes::YCBCR:
+ return mAttachments->mYCbCrShader[aMaskType];
+ case EffectTypes::COMPONENT_ALPHA:
+ return mAttachments->mComponentAlphaShader[aMaskType];
+ default:
+ NS_WARNING("No shader to load");
+ return nullptr;
+ }
+}
+
+void
+CompositorD3D11::ClearRect(const gfx::Rect& aRect)
+{
+ mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF);
+
+ Matrix4x4 identity;
+ memcpy(&mVSConstants.layerTransform, &identity._11, 64);
+
+ mVSConstants.layerQuad = aRect;
+ mVSConstants.renderTargetOffset[0] = 0;
+ mVSConstants.renderTargetOffset[1] = 0;
+ mPSConstants.layerOpacity[0] = 1.0f;
+
+ D3D11_RECT scissor;
+ scissor.left = aRect.x;
+ scissor.right = aRect.XMost();
+ scissor.top = aRect.y;
+ scissor.bottom = aRect.YMost();
+ mContext->RSSetScissorRects(1, &scissor);
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], nullptr, 0);
+
+ mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], nullptr, 0);
+ mPSConstants.layerColor[0] = 0;
+ mPSConstants.layerColor[1] = 0;
+ mPSConstants.layerColor[2] = 0;
+ mPSConstants.layerColor[3] = 0;
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ return;
+ }
+
+ mContext->Draw(4, 0);
+
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+}
+
+static inline bool
+EffectHasPremultipliedAlpha(Effect* aEffect)
+{
+ if (aEffect->mType == EffectTypes::RGB) {
+ return static_cast<TexturedEffect*>(aEffect)->mPremultiplied;
+ }
+ return true;
+}
+
+static inline int
+EffectToBlendLayerType(Effect* aEffect)
+{
+ switch (aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ return PS_LAYER_COLOR;
+ case EffectTypes::RGB: {
+ gfx::SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
+ return (format == gfx::SurfaceFormat::B8G8R8A8 || format == gfx::SurfaceFormat::R8G8B8A8)
+ ? PS_LAYER_RGBA
+ : PS_LAYER_RGB;
+ }
+ case EffectTypes::RENDER_TARGET:
+ return PS_LAYER_RGBA;
+ case EffectTypes::YCBCR:
+ return PS_LAYER_YCBCR;
+ default:
+ MOZ_ASSERT_UNREACHABLE("blending not supported for this layer type");
+ return 0;
+ }
+}
+
+void
+CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ if (mCurrentClip.IsEmpty()) {
+ return;
+ }
+
+ MOZ_ASSERT(mCurrentRT, "No render target");
+
+ memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
+ IntPoint origin = mCurrentRT->GetOrigin();
+ mVSConstants.renderTargetOffset[0] = origin.x;
+ mVSConstants.renderTargetOffset[1] = origin.y;
+
+ mPSConstants.layerOpacity[0] = aOpacity;
+
+ bool restoreBlendMode = false;
+
+ MaskType maskType = MaskType::MaskNone;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ maskType = MaskType::Mask;
+
+ EffectMask* maskEffect =
+ static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();
+
+ if (!source) {
+ NS_WARNING("Missing texture source!");
+ return;
+ }
+
+ ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
+ mContext->PSSetShaderResources(TexSlot::Mask, 1, &srView);
+
+ const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskEffect->mSize));
+
+ mVSConstants.maskQuad = maskTransform.As2D().TransformBounds(bounds);
+ }
+
+ D3D11_RECT scissor;
+
+ IntRect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
+ if (mCurrentRT == mDefaultRT) {
+ clipRect = clipRect.Intersect(mCurrentClip);
+ }
+
+ if (clipRect.IsEmpty()) {
+ return;
+ }
+
+ scissor.left = clipRect.x;
+ scissor.right = clipRect.XMost();
+ scissor.top = clipRect.y;
+ scissor.bottom = clipRect.YMost();
+
+ RefPtr<ID3D11VertexShader> vertexShader = mAttachments->mVSQuadShader[maskType];
+ RefPtr<ID3D11PixelShader> pixelShader = GetPSForEffect(aEffectChain.mPrimaryEffect, maskType);
+
+ RefPtr<ID3D11Texture2D> mixBlendBackdrop;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+
+ // If the blend operation needs to read from the backdrop, copy the
+ // current render target into a new texture and bind it now.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ gfx::Matrix4x4 backdropTransform;
+ gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &backdropTransform);
+
+ RefPtr<ID3D11ShaderResourceView> srv;
+ if (CopyBackdrop(rect, &mixBlendBackdrop, &srv) &&
+ mAttachments->InitBlendShaders())
+ {
+ vertexShader = mAttachments->mVSQuadBlendShader[maskType];
+ pixelShader = mAttachments->mBlendShader[MaskType::MaskNone];
+
+ ID3D11ShaderResourceView* srView = srv.get();
+ mContext->PSSetShaderResources(TexSlot::Backdrop, 1, &srView);
+
+ memcpy(&mVSConstants.backdropTransform, &backdropTransform._11, 64);
+
+ mPSConstants.blendConfig[0] = EffectToBlendLayerType(aEffectChain.mPrimaryEffect);
+ mPSConstants.blendConfig[1] = int(maskType);
+ mPSConstants.blendConfig[2] = BlendOpToShaderConstant(blendMode);
+ mPSConstants.blendConfig[3] = EffectHasPremultipliedAlpha(aEffectChain.mPrimaryEffect);
+ }
+ }
+ }
+
+ mContext->RSSetScissorRects(1, &scissor);
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->VSSetShader(vertexShader, nullptr, 0);
+ mContext->PSSetShader(pixelShader, nullptr, 0);
+
+ const Rect* pTexCoordRect = nullptr;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ Color color =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
+ mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
+ mPSConstants.layerColor[1] = color.g * color.a * aOpacity;
+ mPSConstants.layerColor[2] = color.b * color.a * aOpacity;
+ mPSConstants.layerColor[3] = color.a * aOpacity;
+ }
+ break;
+ case EffectTypes::RGB:
+ case EffectTypes::RENDER_TARGET:
+ {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ pTexCoordRect = &texturedEffect->mTextureCoords;
+
+ TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
+
+ if (!source) {
+ NS_WARNING("Missing texture source!");
+ return;
+ }
+
+ ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
+ mContext->PSSetShaderResources(TexSlot::RGB, 1, &srView);
+
+ if (!texturedEffect->mPremultiplied) {
+ mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+ restoreBlendMode = true;
+ }
+
+ SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
+ }
+ break;
+ case EffectTypes::YCBCR: {
+ EffectYCbCr* ycbcrEffect =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+
+ SetSamplerForSamplingFilter(SamplingFilter::LINEAR);
+
+ pTexCoordRect = &ycbcrEffect->mTextureCoords;
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSource* source = ycbcrEffect->mTexture;
+
+ if (!source) {
+ NS_WARNING("No texture to composite");
+ return;
+ }
+
+ if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || !source->GetSubSource(Cr)) {
+ // This can happen if we failed to upload the textures, most likely
+ // because of unsupported dimensions (we don't tile YCbCr textures).
+ return;
+ }
+
+ float* yuvToRgb = gfxUtils::Get4x3YuvColorMatrix(ycbcrEffect->mYUVColorSpace);
+ memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, sizeof(mPSConstants.yuvColorMatrix));
+
+ TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11();
+ TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
+ TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
+
+ ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(),
+ sourceCb->GetShaderResourceView(),
+ sourceCr->GetShaderResourceView() };
+ mContext->PSSetShaderResources(TexSlot::Y, 3, srViews);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ MOZ_ASSERT(mAttachments->mComponentBlendState);
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+
+ TextureSourceD3D11* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D11();
+ TextureSourceD3D11* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D11();
+
+ if (!sourceOnWhite || !sourceOnBlack) {
+ NS_WARNING("Missing texture source(s)!");
+ return;
+ }
+
+ SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);
+
+ pTexCoordRect = &effectComponentAlpha->mTextureCoords;
+
+ ID3D11ShaderResourceView* srViews[2] = { sourceOnBlack->GetShaderResourceView(),
+ sourceOnWhite->GetShaderResourceView() };
+ mContext->PSSetShaderResources(TexSlot::RGB, 1, &srViews[0]);
+ mContext->PSSetShaderResources(TexSlot::RGBWhite, 1, &srViews[1]);
+
+ mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
+ restoreBlendMode = true;
+ }
+ break;
+ default:
+ NS_WARNING("Unknown shader type");
+ return;
+ }
+
+ if (pTexCoordRect) {
+ Rect layerRects[4];
+ Rect textureRects[4];
+ size_t rects = DecomposeIntoNoRepeatRects(aRect,
+ *pTexCoordRect,
+ &layerRects,
+ &textureRects);
+ for (size_t i = 0; i < rects; i++) {
+ mVSConstants.layerQuad = layerRects[i];
+ mVSConstants.textureCoords = textureRects[i];
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ break;
+ }
+ mContext->Draw(4, 0);
+ }
+ } else {
+ mVSConstants.layerQuad = aRect;
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ } else {
+ mContext->Draw(4, 0);
+ }
+ }
+
+ if (restoreBlendMode) {
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+ }
+}
+
+void
+CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect* aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect* aClipRectOut,
+ IntRect* aRenderBoundsOut)
+{
+ // Don't composite if we are minimised. Other than for the sake of efficency,
+ // this is important because resizing our buffers when mimised will fail and
+ // cause a crash when we're restored.
+ NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
+ if (::IsIconic(mHwnd)) {
+ // We are not going to render, and not going to call EndFrame so we have to
+ // read-unlock our textures to prevent them from accumulating.
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed.";
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+
+ // If we are in the GPU process then the main process doesn't
+ // know that a device reset has happened and needs to be informed
+ if (XRE_IsGPUProcess()) {
+ GPUParent::GetSingleton()->NotifyDeviceReset();
+ }
+
+ return;
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+
+ // Failed to create a render target or the view.
+ if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
+ mSize.width <= 0 || mSize.height <= 0) {
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ IntRect intRect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize());
+ // Sometimes the invalid region is larger than we want to draw.
+ nsIntRegion invalidRegionSafe;
+
+ if (mSize != oldSize) {
+ invalidRegionSafe = intRect;
+ } else {
+ invalidRegionSafe.And(aInvalidRegion, intRect);
+ }
+
+ IntRect invalidRect = invalidRegionSafe.GetBounds();
+
+ IntRect clipRect = invalidRect;
+ if (aClipRectIn) {
+ clipRect.IntersectRect(clipRect, IntRect(aClipRectIn->x, aClipRectIn->y, aClipRectIn->width, aClipRectIn->height));
+ }
+
+ if (clipRect.IsEmpty()) {
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ mContext->IASetInputLayout(mAttachments->mInputLayout);
+
+ ID3D11Buffer* buffer = mAttachments->mVertexBuffer;
+ UINT size = sizeof(Vertex);
+ UINT offset = 0;
+ mContext->IASetVertexBuffers(0, 1, &buffer, &size, &offset);
+
+ mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
+ mInvalidRegion = invalidRegionSafe;
+
+ if (aClipRectOut) {
+ *aClipRectOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+
+ mCurrentClip = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+
+ mContext->RSSetState(mAttachments->mRasterizerState);
+
+ SetRenderTarget(mDefaultRT);
+
+ // ClearRect will set the correct blend state for us.
+ ClearRect(Rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height));
+
+ if (mAttachments->mSyncTexture) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ mAttachments->mSyncTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+ MOZ_ASSERT(mutex);
+ {
+ HRESULT hr;
+ AutoTextureLock lock(mutex, hr, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ hr = mDevice->GetDeviceRemovedReason();
+ if (hr == S_OK) {
+ // There is no driver-removed event. Crash with this timeout.
+ MOZ_CRASH("GFX: D3D11 normal status timeout");
+ }
+
+ // Since the timeout is related to the driver-removed, clear the
+ // render-bounding size to skip this frame.
+ gfxCriticalNote << "GFX: D3D11 timeout with device-removed:" << gfx::hexa(hr);
+ *aRenderBoundsOut = IntRect();
+ return;
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 abandoned sync";
+ }
+ }
+ }
+}
+
+void
+CompositorD3D11::EndFrame()
+{
+ if (!mDefaultRT) {
+ Compositor::EndFrame();
+ return;
+ }
+
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
+ Compositor::EndFrame();
+ mCurrentRT = nullptr;
+ return;
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+ EnsureSize();
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ Compositor::EndFrame();
+ return;
+ }
+
+ RefPtr<ID3D11Query> query;
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+ mDevice->CreateQuery(&desc, getter_AddRefs(query));
+ if (query) {
+ mContext->End(query);
+ }
+
+ UINT presentInterval = 0;
+
+ bool isWARP = DeviceManagerDx::Get()->IsWARP();
+ if (isWARP) {
+ // When we're using WARP we cannot present immediately as it causes us
+ // to tear when rendering. When not using WARP it appears the DWM takes
+ // care of tearing for us.
+ presentInterval = 1;
+ }
+
+ if (oldSize == mSize) {
+ RefPtr<IDXGISwapChain1> chain;
+ HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain));
+
+ if (SUCCEEDED(hr) && chain && mAllowPartialPresents) {
+ DXGI_PRESENT_PARAMETERS params;
+ PodZero(&params);
+ params.DirtyRectsCount = mInvalidRegion.GetNumRects();
+ StackArray<RECT, 4> rects(params.DirtyRectsCount);
+
+ uint32_t i = 0;
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ rects[i].left = r.x;
+ rects[i].top = r.y;
+ rects[i].bottom = r.YMost();
+ rects[i].right = r.XMost();
+ i++;
+ }
+
+ params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
+ chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
+ } else {
+ hr = mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
+ HandleError(hr);
+ }
+ }
+ mDisableSequenceForNextFrame = false;
+ if (mTarget) {
+ PaintToTarget();
+ }
+ }
+
+ // Block until the previous frame's work has been completed.
+ if (mQuery) {
+ TimeStamp start = TimeStamp::Now();
+ BOOL result;
+ while (mContext->GetData(mQuery, &result, sizeof(BOOL), 0) != S_OK) {
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ break;
+ }
+ if ((TimeStamp::Now() - start) > TimeDuration::FromSeconds(2)) {
+ break;
+ }
+ Sleep(0);
+ }
+ }
+ // Store the query for this frame so we can flush it next time.
+ mQuery = query;
+
+ Compositor::EndFrame();
+
+ mCurrentRT = nullptr;
+}
+
+void
+CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
+{
+ // This view matrix translates coordinates from 0..width and 0..height to
+ // -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate)
+ Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+
+ Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
+ projection._33 = 0.0f;
+
+ PrepareViewport(aSize, projection, 0.0f, 1.0f);
+}
+
+void
+CompositorD3D11::ForcePresent()
+{
+ LayoutDeviceIntSize size = mWidget->GetClientSize();
+
+ DXGI_SWAP_CHAIN_DESC desc;
+ mSwapChain->GetDesc(&desc);
+
+ if (desc.BufferDesc.Width == size.width && desc.BufferDesc.Height == size.height) {
+ mSwapChain->Present(0, 0);
+ }
+}
+
+void
+CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
+ const gfx::Matrix4x4& aProjection,
+ float aZNear, float aZFar)
+{
+ D3D11_VIEWPORT viewport;
+ viewport.MaxDepth = aZFar;
+ viewport.MinDepth = aZNear;
+ viewport.Width = aSize.width;
+ viewport.Height = aSize.height;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+
+ mContext->RSSetViewports(1, &viewport);
+
+ memcpy(&mVSConstants.projection, &aProjection._11, sizeof(mVSConstants.projection));
+}
+
+void
+CompositorD3D11::EnsureSize()
+{
+ mSize = mWidget->GetClientSize();
+}
+
+bool
+CompositorD3D11::VerifyBufferSize()
+{
+ DXGI_SWAP_CHAIN_DESC swapDesc;
+ HRESULT hr;
+
+ hr = mSwapChain->GetDesc(&swapDesc);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+
+ if (((swapDesc.BufferDesc.Width == mSize.width &&
+ swapDesc.BufferDesc.Height == mSize.height) ||
+ mSize.width <= 0 || mSize.height <= 0) &&
+ !mVerifyBuffersFailed) {
+ return true;
+ }
+
+ ID3D11RenderTargetView* view = nullptr;
+ mContext->OMSetRenderTargets(1, &view, nullptr);
+
+ if (mDefaultRT) {
+ RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView;
+ RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV;
+
+ // Make sure the texture, which belongs to the swapchain, is destroyed
+ // before resizing the swapchain.
+ if (mCurrentRT == mDefaultRT) {
+ mCurrentRT = nullptr;
+ }
+ MOZ_ASSERT(mDefaultRT->hasOneRef());
+ mDefaultRT = nullptr;
+
+ RefPtr<ID3D11Resource> resource;
+ rtView->GetResource(getter_AddRefs(resource));
+
+ ULONG newRefCnt = rtView.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "mRTView not destroyed on final release! RefCnt: " << newRefCnt;
+ }
+
+ if (srView) {
+ newRefCnt = srView.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "mSRV not destroyed on final release! RefCnt: " << newRefCnt;
+ }
+ }
+
+ newRefCnt = resource.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "Unexpecting lingering references to backbuffer! RefCnt: " << newRefCnt;
+ }
+ }
+
+ hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ 0);
+
+ mVerifyBuffersFailed = FAILED(hr);
+ if (mVerifyBuffersFailed) {
+ gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " << mSize;
+ HandleError(hr);
+ }
+
+ return !mVerifyBuffersFailed;
+}
+
+bool
+CompositorD3D11::UpdateRenderTarget()
+{
+ EnsureSize();
+ if (!VerifyBufferSize()) {
+ gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " << mSize;
+ return false;
+ }
+
+ if (mDefaultRT) {
+ return true;
+ }
+
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", " << (int)mVerifyBuffersFailed;
+ return false;
+ }
+
+ HRESULT hr;
+
+ RefPtr<ID3D11Texture2D> backBuf;
+
+ hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ // This happens on some GPUs/drivers when there's a TDR.
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalError() << "GetBuffer returned invalid call! " << mSize << ", " << (int)mVerifyBuffersFailed;
+ return false;
+ }
+ }
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+
+ mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
+ mDefaultRT->SetSize(mSize.ToUnknownSize());
+
+ return true;
+}
+
+bool
+DeviceAttachmentsD3D11::InitSyncObject()
+{
+ // Sync object is not supported on WARP.
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ // It's okay to do this on Windows 8. But for now we'll just bail
+ // whenever we're using WARP.
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 1, 1, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE |
+ D3D11_BIND_RENDER_TARGET);
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (Failed(hr, "create sync texture")) {
+ return false;
+ }
+
+ hr = texture->QueryInterface((IDXGIResource**)getter_AddRefs(mSyncTexture));
+ if (Failed(hr, "QI sync texture")) {
+ return false;
+ }
+
+ hr = mSyncTexture->GetSharedHandle(&mSyncHandle);
+ if (FAILED(hr) || !mSyncHandle) {
+ gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: "
+ << hexa(hr);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DeviceAttachmentsD3D11::InitBlendShaders()
+{
+ if (!mVSQuadBlendShader[MaskType::MaskNone]) {
+ InitVertexShader(sLayerQuadBlendVS, mVSQuadBlendShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadBlendMaskVS, mVSQuadBlendShader, MaskType::Mask);
+ }
+ if (!mBlendShader[MaskType::MaskNone]) {
+ InitPixelShader(sBlendShader, mBlendShader, MaskType::MaskNone);
+ }
+ return mInitOkay;
+}
+
+bool
+DeviceAttachmentsD3D11::CreateShaders()
+{
+ InitVertexShader(sLayerQuadVS, mVSQuadShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadMaskVS, mVSQuadShader, MaskType::Mask);
+
+ InitPixelShader(sSolidColorShader, mSolidColorShader, MaskType::MaskNone);
+ InitPixelShader(sSolidColorShaderMask, mSolidColorShader, MaskType::Mask);
+ InitPixelShader(sRGBShader, mRGBShader, MaskType::MaskNone);
+ InitPixelShader(sRGBShaderMask, mRGBShader, MaskType::Mask);
+ InitPixelShader(sRGBAShader, mRGBAShader, MaskType::MaskNone);
+ InitPixelShader(sRGBAShaderMask, mRGBAShader, MaskType::Mask);
+ InitPixelShader(sYCbCrShader, mYCbCrShader, MaskType::MaskNone);
+ InitPixelShader(sYCbCrShaderMask, mYCbCrShader, MaskType::Mask);
+ if (gfxPrefs::ComponentAlphaEnabled()) {
+ InitPixelShader(sComponentAlphaShader, mComponentAlphaShader, MaskType::MaskNone);
+ InitPixelShader(sComponentAlphaShaderMask, mComponentAlphaShader, MaskType::Mask);
+ }
+
+ return mInitOkay;
+}
+
+bool
+CompositorD3D11::UpdateConstantBuffers()
+{
+ HRESULT hr;
+ D3D11_MAPPED_SUBRESOURCE resource;
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr) || !resource.pData) {
+ gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+ *(VertexShaderConstants*)resource.pData = mVSConstants;
+ mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr) || !resource.pData) {
+ gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+ *(PixelShaderConstants*)resource.pData = mPSConstants;
+ mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
+
+ ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
+
+ mContext->VSSetConstantBuffers(0, 1, &buffer);
+
+ buffer = mAttachments->mPSConstantBuffer;
+ mContext->PSSetConstantBuffers(0, 1, &buffer);
+ return true;
+}
+
+void
+CompositorD3D11::SetSamplerForSamplingFilter(SamplingFilter aSamplingFilter)
+{
+ ID3D11SamplerState *sampler;
+ switch (aSamplingFilter) {
+ case SamplingFilter::POINT:
+ sampler = mAttachments->mPointSamplerState;
+ break;
+ case SamplingFilter::LINEAR:
+ default:
+ sampler = mAttachments->mLinearSamplerState;
+ break;
+ }
+
+ mContext->PSSetSamplers(0, 1, &sampler);
+}
+
+void
+CompositorD3D11::PaintToTarget()
+{
+ RefPtr<ID3D11Texture2D> backBuf;
+ HRESULT hr;
+
+ hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 1";
+ HandleError(hr);
+ return;
+ }
+
+ D3D11_TEXTURE2D_DESC bbDesc;
+ backBuf->GetDesc(&bbDesc);
+
+ CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
+ softDesc.MipLevels = 1;
+ softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ softDesc.Usage = D3D11_USAGE_STAGING;
+ softDesc.BindFlags = 0;
+
+ RefPtr<ID3D11Texture2D> readTexture;
+
+ hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 2";
+ HandleError(hr);
+ return;
+ }
+ mContext->CopyResource(readTexture, backBuf);
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 3";
+ HandleError(hr);
+ return;
+ }
+ RefPtr<DataSourceSurface> sourceSurface =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)map.pData,
+ map.RowPitch,
+ IntSize(bbDesc.Width, bbDesc.Height),
+ SurfaceFormat::B8G8R8A8);
+ mTarget->CopySurface(sourceSurface,
+ IntRect(0, 0, bbDesc.Width, bbDesc.Height),
+ IntPoint(-mTargetBounds.x, -mTargetBounds.y));
+
+ mTarget->Flush();
+ mContext->Unmap(readTexture, 0);
+}
+
+bool
+CompositorD3D11::Failed(HRESULT hr, const char* aContext)
+{
+ if (SUCCEEDED(hr))
+ return false;
+
+ gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ return true;
+}
+
+void
+CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
+{
+ if (SUCCEEDED(hr)) {
+ return;
+ }
+
+ if (aSeverity == Critical) {
+ MOZ_CRASH("GFX: Unrecoverable D3D11 error");
+ }
+
+ if (mDevice && DeviceManagerDx::Get()->GetCompositorDevice() != mDevice) {
+ gfxCriticalNote << "Out of sync D3D11 devices in HandleError, " << (int)mVerifyBuffersFailed;
+ }
+
+ HRESULT hrOnReset = S_OK;
+ bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
+
+ if (deviceRemoved && mDevice) {
+ hrOnReset = mDevice->GetDeviceRemovedReason();
+ } else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) {
+ hrOnReset = mDevice->GetDeviceRemovedReason();
+ if (hrOnReset != S_OK) {
+ deviceRemoved = true;
+ }
+ }
+
+ // Device reset may not be an error on our side, but can mess things up so
+ // it's useful to see it in the reports.
+ gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
+ << (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
+ : "[CompositorD3D11] error code: ")
+ << hexa(hr) << ", " << hexa(hrOnReset) << ", " << (int)mVerifyBuffersFailed;
+
+ // Crash if we are making invalid calls outside of device removal
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ gfxDevCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved : LogReason::D3D11InvalidCall) << "Invalid D3D11 api call";
+ }
+
+ if (aSeverity == Recoverable) {
+ NS_WARNING("Encountered a recoverable D3D11 error");
+ }
+}
+
+}
+}
diff --git a/system/graphics/layers/d3d11/CompositorD3D11.h b/system/graphics/layers/d3d11/CompositorD3D11.h
new file mode 100644
index 000000000..7a1a5cc7d
--- /dev/null
+++ b/system/graphics/layers/d3d11/CompositorD3D11.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITORD3D11_H
+#define MOZILLA_GFX_COMPOSITORD3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "gfx2DGlue.h"
+#include "mozilla/layers/Compositor.h"
+#include "TextureD3D11.h"
+#include <d3d11.h>
+
+class nsWidget;
+
+namespace mozilla {
+namespace layers {
+
+#define LOGD3D11(param)
+
+struct VertexShaderConstants
+{
+ float layerTransform[4][4];
+ float projection[4][4];
+ float renderTargetOffset[4];
+ gfx::Rect textureCoords;
+ gfx::Rect layerQuad;
+ gfx::Rect maskQuad;
+ float backdropTransform[4][4];
+};
+
+struct PixelShaderConstants
+{
+ float layerColor[4];
+ float layerOpacity[4];
+ int blendConfig[4];
+ float yuvColorMatrix[3][4];
+};
+
+struct DeviceAttachmentsD3D11;
+
+class CompositorD3D11 : public Compositor
+{
+public:
+ CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+ ~CompositorD3D11();
+
+ virtual CompositorD3D11* AsCompositorD3D11() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual TextureFactoryIdentifier
+ GetTextureFactoryIdentifier() override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override;
+ virtual int32_t GetMaxTextureSize() const final;
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override {}
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect,
+ SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget* aSurface) override;
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mCurrentRT;
+ }
+
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
+
+ /**
+ * Declare an offset to use when rendering layers. This will be ignored when
+ * rendering to a target instead of the screen.
+ */
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override
+ {
+ if (aOffset.x || aOffset.y) {
+ NS_RUNTIMEABORT("SetScreenRenderOffset not supported by CompositorD3D11.");
+ }
+ // If the offset is 0, 0 that's okay.
+ }
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void DrawQuad(const gfx::Rect &aRect,
+ const gfx::IntRect &aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ /**
+ * Start a new frame. If aClipRectIn is null, sets *aClipRectOut to the
+ * screen dimensions.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+
+ /**
+ * Flush the current frame to the screen.
+ */
+ virtual void EndFrame() override;
+
+ /**
+ * Post rendering stuff if the rendering is outside of this Compositor
+ * e.g., by Composer2D
+ */
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override {}
+
+ /**
+ * Setup the viewport and projection matrix for rendering
+ * to a window of the given dimensions.
+ */
+ virtual void PrepareViewport(const gfx::IntSize& aSize);
+ virtual void PrepareViewport(const gfx::IntSize& aSize, const gfx::Matrix4x4& aProjection,
+ float aZNear, float aZFar);
+
+ virtual bool SupportsPartialTextureUpdate() override { return true; }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Direct3D 11"; }
+#endif
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_D3D11;
+ }
+
+ virtual void ForcePresent();
+
+ ID3D11Device* GetDevice() { return mDevice; }
+
+ ID3D11DeviceContext* GetDC() { return mContext; }
+
+private:
+ enum Severity {
+ Recoverable,
+ DebugAssert,
+ Critical,
+ };
+
+ void HandleError(HRESULT hr, Severity aSeverity = DebugAssert);
+
+ // Same as Failed(), except the severity is critical (with no abort) and
+ // a string prefix must be provided.
+ bool Failed(HRESULT hr, const char* aContext);
+
+ // ensure mSize is up to date with respect to mWidget
+ void EnsureSize();
+ bool VerifyBufferSize();
+ bool UpdateRenderTarget();
+ bool UpdateConstantBuffers();
+ void SetSamplerForSamplingFilter(gfx::SamplingFilter aSamplingFilter);
+ ID3D11PixelShader* GetPSForEffect(Effect *aEffect, MaskType aMaskType);
+ void PaintToTarget();
+ RefPtr<ID3D11Texture2D> CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint);
+ bool CopyBackdrop(const gfx::IntRect& aRect,
+ RefPtr<ID3D11Texture2D>* aOutTexture,
+ RefPtr<ID3D11ShaderResourceView>* aOutView);
+
+ RefPtr<ID3D11DeviceContext> mContext;
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<IDXGISwapChain> mSwapChain;
+ RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
+ RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
+
+ RefPtr<ID3D11Query> mQuery;
+
+ DeviceAttachmentsD3D11* mAttachments;
+
+ LayoutDeviceIntSize mSize;
+
+ HWND mHwnd;
+
+ D3D_FEATURE_LEVEL mFeatureLevel;
+
+ VertexShaderConstants mVSConstants;
+ PixelShaderConstants mPSConstants;
+ bool mDisableSequenceForNextFrame;
+ bool mAllowPartialPresents;
+
+ gfx::IntRect mInvalidRect;
+ // This is the clip rect applied to the default DrawTarget (i.e. the window)
+ gfx::IntRect mCurrentClip;
+ nsIntRegion mInvalidRegion;
+
+ bool mVerifyBuffersFailed;
+};
+
+}
+}
+
+#endif
diff --git a/system/graphics/layers/d3d11/CompositorD3D11.hlsl b/system/graphics/layers/d3d11/CompositorD3D11.hlsl
new file mode 100644
index 000000000..21175704b
--- /dev/null
+++ b/system/graphics/layers/d3d11/CompositorD3D11.hlsl
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BlendingHelpers.hlslh"
+#include "BlendShaderConstants.h"
+
+typedef float4 rect;
+
+float4x4 mLayerTransform : register(vs, c0);
+float4x4 mProjection : register(vs, c4);
+float4 vRenderTargetOffset : register(vs, c8);
+rect vTextureCoords : register(vs, c9);
+rect vLayerQuad : register(vs, c10);
+rect vMaskQuad : register(vs, c11);
+float4x4 mBackdropTransform : register(vs, c12);
+
+float4 fLayerColor : register(ps, c0);
+float fLayerOpacity : register(ps, c1);
+
+// x = layer type
+// y = mask type
+// z = blend op
+// w = is premultiplied
+uint4 iBlendConfig : register(ps, c2);
+
+row_major float3x3 mYuvColorMatrix : register(ps, c3);
+
+sampler sSampler : register(ps, s0);
+
+// The mix-blend mega shader uses all variables, so we have to make sure they
+// are assigned fixed slots.
+Texture2D tRGB : register(ps, t0);
+Texture2D tY : register(ps, t1);
+Texture2D tCb : register(ps, t2);
+Texture2D tCr : register(ps, t3);
+Texture2D tRGBWhite : register(ps, t4);
+Texture2D tMask : register(ps, t5);
+Texture2D tBackdrop : register(ps, t6);
+
+struct VS_INPUT {
+ float2 vPosition : POSITION;
+};
+
+struct VS_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+};
+
+struct VS_MASK_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+};
+
+// Combined struct for the mix-blend compatible vertex shaders.
+struct VS_BLEND_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+ float2 vBackdropCoords : TEXCOORD2;
+};
+
+struct PS_OUTPUT {
+ float4 vSrc;
+ float4 vAlpha;
+};
+
+float2 TexCoords(const float2 aPosition)
+{
+ float2 result;
+ const float2 size = vTextureCoords.zw;
+ result.x = vTextureCoords.x + aPosition.x * size.x;
+ result.y = vTextureCoords.y + aPosition.y * size.y;
+
+ return result;
+}
+
+SamplerState LayerTextureSamplerLinear
+{
+ Filter = MIN_MAG_MIP_LINEAR;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+float4 TransformedPosition(float2 aInPosition)
+{
+ // the current vertex's position on the quad
+ // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform
+ float4 position = float4(0, 0, 0, 1);
+
+ // We use 4 component floats to uniquely describe a rectangle, by the structure
+ // of x, y, width, height. This allows us to easily generate the 4 corners
+ // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+ // stream source for our LayerQuad vertex shader. We do this by doing:
+ // Xout = x + Xin * width
+ // Yout = y + Yin * height
+ float2 size = vLayerQuad.zw;
+ position.x = vLayerQuad.x + aInPosition.x * size.x;
+ position.y = vLayerQuad.y + aInPosition.y * size.y;
+
+ position = mul(mLayerTransform, position);
+
+ return position;
+}
+
+float4 VertexPosition(float4 aTransformedPosition)
+{
+ float4 result;
+ result.w = aTransformedPosition.w;
+ result.xyz = aTransformedPosition.xyz / aTransformedPosition.w;
+ result -= vRenderTargetOffset;
+ result.xyz *= result.w;
+
+ result = mul(mProjection, result);
+
+ return result;
+}
+
+float2 BackdropPosition(float4 aPosition)
+{
+ // Move the position from clip space (-1,1) into 0..1 space.
+ float2 pos;
+ pos.x = (aPosition.x + 1.0) / 2.0;
+ pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;
+
+ return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
+}
+
+VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT outp;
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ outp.vPosition = VertexPosition(position);
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+
+ return outp;
+}
+
+VS_MASK_OUTPUT LayerQuadMaskVS(const VS_INPUT aVertex)
+{
+ VS_MASK_OUTPUT outp;
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ outp.vPosition = VertexPosition(position);
+
+ // calculate the position on the mask texture
+ outp.vMaskCoords.x = (position.x - vMaskQuad.x) / vMaskQuad.z;
+ outp.vMaskCoords.y = (position.y - vMaskQuad.y) / vMaskQuad.w;
+ // We use the w coord to do non-perspective correct interpolation:
+ // the quad might be transformed in 3D, in which case it will have some
+ // perspective. The graphics card will do perspective-correct interpolation
+ // of the texture, but our mask is already transformed and so we require
+ // linear interpolation. Therefore, we must correct the interpolation
+ // ourselves, we do this by multiplying all coords by w here, and dividing by
+ // w in the pixel shader (post-interpolation), we pass w in outp.vMaskCoords.z.
+ // See http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness
+ outp.vMaskCoords.z = 1;
+ outp.vMaskCoords *= position.w;
+
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+
+ return outp;
+}
+
+float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return result * mask;
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+float4 CalculateYCbCrColor(const float2 aTexCoords)
+{
+ float3 yuv;
+ float4 color;
+
+ yuv.x = tY.Sample(sSampler, aTexCoords).r - 0.06275;
+ yuv.y = tCb.Sample(sSampler, aTexCoords).r - 0.50196;
+ yuv.z = tCr.Sample(sSampler, aTexCoords).r - 0.50196;
+
+ color.rgb = mul(mYuvColorMatrix, yuv);
+ color.a = 1.0f;
+
+ return color;
+}
+
+float4 YCbCrShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+PS_OUTPUT ComponentAlphaShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ result.vSrc *= fLayerOpacity * mask;
+ result.vAlpha *= fLayerOpacity * mask;
+
+ return result;
+}
+
+float4 SolidColorShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return fLayerColor * mask;
+}
+
+/*
+ * Un-masked versions
+ *************************************************************
+ */
+float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+}
+
+float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+ return result;
+}
+
+float4 YCbCrShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity;
+}
+
+PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+ result.vSrc *= fLayerOpacity;
+ result.vAlpha *= fLayerOpacity;
+ return result;
+}
+
+float4 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return fLayerColor;
+}
+
+// Mix-blend compatible vertex shaders.
+VS_BLEND_OUTPUT LayerQuadBlendVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT v = LayerQuadVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = float3(0, 0, 0);
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+VS_BLEND_OUTPUT LayerQuadBlendMaskVS(const VS_INPUT aVertex)
+{
+ VS_MASK_OUTPUT v = LayerQuadMaskVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = v.vMaskCoords;
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+// The layer type and mask type are specified as constants. We use these to
+// call the correct pixel shader to determine the source color for blending.
+// Unfortunately this also requires some boilerplate to convert VS_BLEND_OUTPUT
+// to a compatible pixel shader input.
+float4 ComputeBlendSourceColor(const VS_BLEND_OUTPUT aVertex)
+{
+ if (iBlendConfig.y == PS_MASK_NONE) {
+ VS_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShader(tmp);
+ }
+ return SolidColorShader(tmp);
+ } else if (iBlendConfig.y == PS_MASK) {
+ VS_MASK_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ tmp.vMaskCoords = aVertex.vMaskCoords;
+
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShaderMask(tmp);
+ }
+ return SolidColorShaderMask(tmp);
+ }
+
+ return float4(0.0, 0.0, 0.0, 1.0);
+}
+
+float3 ChooseBlendFunc(float3 dest, float3 src)
+{
+ [flatten] switch (iBlendConfig.z) {
+ case PS_BLEND_MULTIPLY:
+ return BlendMultiply(dest, src);
+ case PS_BLEND_SCREEN:
+ return BlendScreen(dest, src);
+ case PS_BLEND_OVERLAY:
+ return BlendOverlay(dest, src);
+ case PS_BLEND_DARKEN:
+ return BlendDarken(dest, src);
+ case PS_BLEND_LIGHTEN:
+ return BlendLighten(dest, src);
+ case PS_BLEND_COLOR_DODGE:
+ return BlendColorDodge(dest, src);
+ case PS_BLEND_COLOR_BURN:
+ return BlendColorBurn(dest, src);
+ case PS_BLEND_HARD_LIGHT:
+ return BlendHardLight(dest, src);
+ case PS_BLEND_SOFT_LIGHT:
+ return BlendSoftLight(dest, src);
+ case PS_BLEND_DIFFERENCE:
+ return BlendDifference(dest, src);
+ case PS_BLEND_EXCLUSION:
+ return BlendExclusion(dest, src);
+ case PS_BLEND_HUE:
+ return BlendHue(dest, src);
+ case PS_BLEND_SATURATION:
+ return BlendSaturation(dest, src);
+ case PS_BLEND_COLOR:
+ return BlendColor(dest, src);
+ case PS_BLEND_LUMINOSITY:
+ return BlendLuminosity(dest, src);
+ default:
+ return float3(0, 0, 0);
+ }
+}
+
+float4 BlendShader(const VS_BLEND_OUTPUT aVertex) : SV_Target
+{
+ float4 backdrop = tBackdrop.Sample(sSampler, aVertex.vBackdropCoords.xy);
+ float4 source = ComputeBlendSourceColor(aVertex);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ backdrop.rgb /= backdrop.a;
+ if (iBlendConfig.w) {
+ source.rgb /= source.a;
+ }
+
+ float4 result;
+ result.rgb = ChooseBlendFunc(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
diff --git a/system/graphics/layers/d3d11/CompositorD3D11Shaders.h b/system/graphics/layers/d3d11/CompositorD3D11Shaders.h
new file mode 100644
index 000000000..084dc667a
--- /dev/null
+++ b/system/graphics/layers/d3d11/CompositorD3D11Shaders.h
@@ -0,0 +1,9434 @@
+struct ShaderBytes { const void* mData; size_t mLength; };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64 [unused]
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 8 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ dcl_texcoord v0
+ mad oT0.xy, v0, c9.zwzw, c9
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c2
+ mad r0, c1, r0.x, r1
+ add r0, r0, c3
+ rcp r1.x, r0.w
+ mul r0.xyz, r0, r1.x
+ add r0, r0, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 15 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[11], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_temps 2
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r0.xyz, r0.xyzx, r0.wwww
+add r0.xyzw, r0.xyzw, -cb0[8].xyzw
+mul r0.xyz, r0.wwww, r0.xyzx
+mul r1.xyzw, r0.yyyy, cb0[5].xyzw
+mad r1.xyzw, cb0[4].xyzw, r0.xxxx, r1.xyzw
+mad r1.xyzw, cb0[6].xyzw, r0.zzzz, r1.xyzw
+mad o0.xyzw, cb0[7].xyzw, r0.wwww, r1.xyzw
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+ret
+// Approximately 13 instruction slots used
+#endif
+
+const BYTE LayerQuadVS[] =
+{
+ 68, 88, 66, 67, 250, 65,
+ 94, 205, 254, 155, 52, 90,
+ 43, 147, 203, 201, 141, 74,
+ 80, 143, 1, 0, 0, 0,
+ 68, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 152, 1, 0, 0, 160, 3,
+ 0, 0, 28, 4, 0, 0,
+ 184, 6, 0, 0, 236, 6,
+ 0, 0, 65, 111, 110, 57,
+ 88, 1, 0, 0, 88, 1,
+ 0, 0, 0, 2, 254, 255,
+ 24, 1, 0, 0, 64, 0,
+ 0, 0, 2, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 0, 0, 36, 0,
+ 1, 0, 60, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 8, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 10, 0, 238, 160,
+ 10, 0, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 1, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 0, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 8, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 255, 128, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 85, 128, 5, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 4, 0, 228, 160,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 15, 128, 6, 0,
+ 228, 160, 0, 0, 170, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 7, 0, 228, 160, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 3, 192, 0, 0, 255, 128,
+ 0, 0, 228, 160, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 12, 192, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 0, 2,
+ 0, 0, 64, 0, 1, 0,
+ 128, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 166, 10, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 13, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 12, 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, 82, 68, 69, 70,
+ 148, 2, 0, 0, 1, 0,
+ 0, 0, 72, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 108, 2,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 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, 36, 71,
+ 108, 111, 98, 97, 108, 115,
+ 0, 171, 171, 171, 60, 0,
+ 0, 0, 11, 0, 0, 0,
+ 96, 0, 0, 0, 96, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 104, 1,
+ 0, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 2, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 136, 1,
+ 0, 0, 64, 0, 0, 0,
+ 64, 0, 0, 0, 2, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 148, 1,
+ 0, 0, 128, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 168, 1, 0, 0,
+ 0, 0, 0, 0, 184, 1,
+ 0, 0, 144, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 216, 1,
+ 0, 0, 160, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 227, 1,
+ 0, 0, 176, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 237, 1,
+ 0, 0, 192, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 1, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 168, 1, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 16, 1, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 28, 2, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 32, 1, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 60, 2, 0, 0,
+ 0, 0, 0, 0, 76, 2,
+ 0, 0, 48, 1, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 84, 101, 120, 116, 117,
+ 114, 101, 67, 111, 111, 114,
+ 100, 115, 0, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 76, 97, 121,
+ 101, 114, 81, 117, 97, 100,
+ 0, 118, 77, 97, 115, 107,
+ 81, 117, 97, 100, 0, 109,
+ 66, 97, 99, 107, 100, 114,
+ 111, 112, 84, 114, 97, 110,
+ 115, 102, 111, 114, 109, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 49,
+ 48, 46, 49, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 80, 0, 0, 0, 2, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171
+};
+ShaderBytes sLayerQuadVS = { LayerQuadVS, sizeof(LayerQuadVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ mov oC0, c0
+
+// approximately 1 instruction slot used
+ps_4_0
+dcl_constantbuffer CB0[1], immediateIndexed
+dcl_output o0.xyzw
+mov o0.xyzw, cb0[0].xyzw
+ret
+// Approximately 2 instruction slots used
+#endif
+
+const BYTE SolidColorShader[] =
+{
+ 68, 88, 66, 67, 181, 3,
+ 20, 0, 202, 78, 164, 59,
+ 210, 171, 118, 253, 118, 104,
+ 133, 184, 1, 0, 0, 0,
+ 112, 4, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 132, 0, 0, 0, 204, 0,
+ 0, 0, 72, 1, 0, 0,
+ 228, 3, 0, 0, 60, 4,
+ 0, 0, 65, 111, 110, 57,
+ 68, 0, 0, 0, 68, 0,
+ 0, 0, 0, 2, 255, 255,
+ 20, 0, 0, 0, 48, 0,
+ 0, 0, 1, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 48, 0, 0, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 16, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 6,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 2, 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,
+ 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,
+ 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, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 116, 1,
+ 0, 0, 0, 0, 0, 0,
+ 132, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 148, 1,
+ 0, 0, 0, 0, 0, 0,
+ 164, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 180, 1,
+ 0, 0, 0, 0, 0, 0,
+ 196, 1, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 212, 1,
+ 0, 0, 0, 0, 0, 0,
+ 228, 1, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 116, 1,
+ 0, 0, 0, 0, 0, 0,
+ 36, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 79, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 89, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sSolidColorShader = { SolidColorShader, sizeof(SolidColorShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mul r0.xyz, r0, c0.x
+ mov r0.w, c0.x
+ mov oC0, r0
+
+// approximately 4 instruction slots used (1 texture, 3 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 1
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul o0.xyz, r0.xyzx, cb0[1].xxxx
+mov o0.w, cb0[1].x
+ret
+// Approximately 4 instruction slots used
+#endif
+
+const BYTE RGBShader[] =
+{
+ 68, 88, 66, 67, 181, 57,
+ 113, 191, 104, 206, 206, 65,
+ 235, 158, 87, 241, 179, 224,
+ 69, 235, 1, 0, 0, 0,
+ 120, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 204, 0, 0, 0, 136, 1,
+ 0, 0, 4, 2, 0, 0,
+ 236, 4, 0, 0, 68, 5,
+ 0, 0, 65, 111, 110, 57,
+ 140, 0, 0, 0, 140, 0,
+ 0, 0, 0, 2, 255, 255,
+ 88, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 180, 0,
+ 0, 0, 64, 0, 0, 0,
+ 45, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 114, 32, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 6, 130, 32, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 1, 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,
+ 1, 0, 0, 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,
+ 82, 68, 69, 70, 224, 2,
+ 0, 0, 1, 0, 0, 0,
+ 148, 0, 0, 0, 3, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 184, 2, 0, 0,
+ 124, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 133, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 138, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 138, 0, 0, 0, 11, 0,
+ 0, 0, 172, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 208, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
+ 0, 0, 0, 0, 0, 0,
+ 240, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 0, 0, 0, 0,
+ 48, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 80, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 112, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 144, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 155, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 165, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sRGBShader = { RGBShader, sizeof(RGBShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 3 instruction slots used (1 texture, 2 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 1
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul o0.xyzw, r0.xyzw, cb0[1].xxxx
+ret
+// Approximately 3 instruction slots used
+#endif
+
+const BYTE RGBAShader[] =
+{
+ 68, 88, 66, 67, 0, 64,
+ 93, 222, 73, 216, 128, 20,
+ 42, 69, 82, 179, 209, 122,
+ 136, 190, 1, 0, 0, 0,
+ 84, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 192, 0, 0, 0, 100, 1,
+ 0, 0, 224, 1, 0, 0,
+ 200, 4, 0, 0, 32, 5,
+ 0, 0, 65, 111, 110, 57,
+ 128, 0, 0, 0, 128, 0,
+ 0, 0, 0, 2, 255, 255,
+ 76, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 156, 0,
+ 0, 0, 64, 0, 0, 0,
+ 39, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 1, 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,
+ 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,
+ 82, 68, 69, 70, 224, 2,
+ 0, 0, 1, 0, 0, 0,
+ 148, 0, 0, 0, 3, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 184, 2, 0, 0,
+ 124, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 133, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 138, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 138, 0, 0, 0, 11, 0,
+ 0, 0, 172, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 208, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
+ 0, 0, 0, 0, 0, 0,
+ 240, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 0, 0, 0, 0,
+ 48, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 80, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 112, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 144, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 155, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 165, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sRGBAShader = { RGBAShader, sizeof(RGBAShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tRGBWhite texture float4 2d t4 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+// SV_Target 1 xyzw 1 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t4
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t0, s0
+ texld r1, t0, s1
+ add r1, r0, -r1
+ add r1, r1, c1.x
+ mov r0.w, r1.y
+ mul r1, r1, c0.x
+ mov oC1, r1
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 9 instruction slots used (2 texture, 7 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t4
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_output o1.xyzw
+dcl_temps 2
+sample r0.xyzw, v1.xyxx, t4.xyzw, s0
+sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+add r0.xyzw, -r0.xyzw, r1.xyzw
+add r0.xyzw, r0.xyzw, l(1.000000, 1.000000, 1.000000, 1.000000)
+mov r1.w, r0.y
+mul o1.xyzw, r0.xyzw, cb0[1].xxxx
+mul o0.xyzw, r1.xyzw, cb0[1].xxxx
+ret
+// Approximately 8 instruction slots used
+#endif
+
+const BYTE ComponentAlphaShader[] =
+{
+ 68, 88, 66, 67, 168, 127,
+ 203, 56, 125, 182, 211, 23,
+ 166, 215, 189, 218, 181, 48,
+ 227, 73, 1, 0, 0, 0,
+ 212, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 64, 1, 0, 0, 160, 2,
+ 0, 0, 28, 3, 0, 0,
+ 48, 6, 0, 0, 136, 6,
+ 0, 0, 65, 111, 110, 57,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 2, 255, 255,
+ 200, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 4, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 1, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 2, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 0, 228, 129, 2, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 85, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 8,
+ 15, 128, 1, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 0, 0, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 88, 1, 0, 0, 64, 0,
+ 0, 0, 86, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 4, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 1, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 4, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 54, 0, 0, 5,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 56, 0, 0, 8, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 4, 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, 2, 0,
+ 0, 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, 82, 68,
+ 69, 70, 12, 3, 0, 0,
+ 1, 0, 0, 0, 192, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 228, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 180, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 82, 71, 66,
+ 87, 104, 105, 116, 101, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 180, 0, 0, 0, 11, 0,
+ 0, 0, 216, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 0, 0, 0, 0,
+ 252, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 28, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 60, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 76, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 0, 0, 0, 0,
+ 156, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 188, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 199, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 209, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sComponentAlphaShader = { ComponentAlphaShader, sizeof(ComponentAlphaShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+// c1 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t1
+// s1 s0 t2
+// s2 s0 t3
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ mov r0.w, c4.z
+ texld r1, t0, s1
+ texld r2, t0, s0
+ add r2.x, r2.x, c4.x
+ add r2.y, r1.x, c4.y
+ texld r1, t0, s2
+ add r2.z, r1.x, c4.y
+ dp3 r0.x, c1, r2
+ dp3 r0.y, c2, r2
+ dp3 r0.z, c3, r2
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 12 instruction slots used (3 texture, 9 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 3
+mov r0.w, l(1.000000)
+sample r1.xyzw, v1.xyxx, t1.xyzw, s0
+add r1.x, r1.x, l(-0.062750)
+sample r2.xyzw, v1.xyxx, t2.xyzw, s0
+add r1.y, r2.x, l(-0.501960)
+sample r2.xyzw, v1.xyxx, t3.xyzw, s0
+add r1.z, r2.x, l(-0.501960)
+dp3 r0.x, cb0[3].xyzx, r1.xyzx
+dp3 r0.y, cb0[4].xyzx, r1.xyzx
+dp3 r0.z, cb0[5].xyzx, r1.xyzx
+mul o0.xyzw, r0.xyzw, cb0[1].xxxx
+ret
+// Approximately 12 instruction slots used
+#endif
+
+const BYTE YCbCrShader[] =
+{
+ 68, 88, 66, 67, 56, 199,
+ 91, 5, 215, 233, 204, 14,
+ 193, 166, 163, 11, 246, 123,
+ 165, 88, 1, 0, 0, 0,
+ 156, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 144, 1, 0, 0, 100, 3,
+ 0, 0, 224, 3, 0, 0,
+ 16, 7, 0, 0, 104, 7,
+ 0, 0, 65, 111, 110, 57,
+ 80, 1, 0, 0, 80, 1,
+ 0, 0, 0, 2, 255, 255,
+ 8, 1, 0, 0, 72, 0,
+ 0, 0, 2, 0, 48, 0,
+ 0, 0, 72, 0, 0, 0,
+ 72, 0, 3, 0, 36, 0,
+ 0, 0, 72, 0, 1, 0,
+ 0, 0, 2, 0, 1, 0,
+ 3, 0, 2, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 18, 131,
+ 128, 189, 115, 128, 0, 191,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 4, 0,
+ 170, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 1, 128,
+ 2, 0, 0, 128, 4, 0,
+ 0, 160, 2, 0, 0, 3,
+ 2, 0, 2, 128, 1, 0,
+ 0, 128, 4, 0, 85, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 2, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 4, 128,
+ 1, 0, 0, 128, 4, 0,
+ 85, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 228, 160, 2, 0, 228, 128,
+ 8, 0, 0, 3, 0, 0,
+ 2, 128, 2, 0, 228, 160,
+ 2, 0, 228, 128, 8, 0,
+ 0, 3, 0, 0, 4, 128,
+ 3, 0, 228, 160, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 0, 0, 0, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 204, 1, 0, 0,
+ 64, 0, 0, 0, 115, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 2, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 3, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 3, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 18, 131,
+ 128, 189, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 2, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 115, 128, 0, 191,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 3, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 16, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 8, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 12, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 7, 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, 3, 0, 0, 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, 82, 68, 69, 70,
+ 40, 3, 0, 0, 1, 0,
+ 0, 0, 220, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 197, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 200, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 2, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 204, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 208, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 89, 0,
+ 116, 67, 98, 0, 116, 67,
+ 114, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 208, 0, 0, 0,
+ 11, 0, 0, 0, 244, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 252, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 164, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 216, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 227, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 237, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 80, 0, 0, 0, 2, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sYCbCrShader = { YCbCrShader, sizeof(YCbCrShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64 [unused]
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 9 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c12, 1, 0, 0, 0
+ dcl_texcoord v0
+ mov r0.z, c12.x
+ rcp r0.w, c11.z
+ mad r1.xy, v0, c10.zwzw, c10
+ mul r2, r1.y, c2
+ mad r1, c1, r1.x, r2
+ add r1, r1, c3
+ add r2.xy, r1, -c11
+ mul r0.x, r0.w, r2.x
+ rcp r0.w, c11.w
+ mul r0.y, r0.w, r2.y
+ mul oT1.xyz, r0, r1.w
+ mad oT0.xy, v0, c9.zwzw, c9
+ rcp r0.x, r1.w
+ mul r1.xyz, r0.x, r1
+ add r0, r1, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 22 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[12], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o2.xyz
+dcl_temps 4
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r1.xyz, r0.xyzx, r0.wwww
+mov r1.w, r0.w
+add r2.xyzw, r1.xyzw, -cb0[8].xyzw
+mul r1.xyz, r2.wwww, r2.xyzx
+mul r3.xyzw, r1.yyyy, cb0[5].xyzw
+mad r3.xyzw, cb0[4].xyzw, r1.xxxx, r3.xyzw
+mad r3.xyzw, cb0[6].xyzw, r1.zzzz, r3.xyzw
+mad o0.xyzw, cb0[7].xyzw, r2.wwww, r3.xyzw
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+add r0.xy, r0.xyxx, -cb0[11].xyxx
+div r0.xy, r0.xyxx, cb0[11].zwzz
+mov r0.z, l(1.000000)
+mul o2.xyz, r1.wwww, r0.xyzx
+ret
+// Approximately 18 instruction slots used
+#endif
+
+const BYTE LayerQuadMaskVS[] =
+{
+ 68, 88, 66, 67, 47, 28,
+ 196, 228, 98, 79, 27, 152,
+ 192, 25, 215, 128, 59, 234,
+ 245, 240, 1, 0, 0, 0,
+ 108, 8, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 20, 2, 0, 0, 176, 4,
+ 0, 0, 44, 5, 0, 0,
+ 200, 7, 0, 0, 252, 7,
+ 0, 0, 65, 111, 110, 57,
+ 212, 1, 0, 0, 212, 1,
+ 0, 0, 0, 2, 254, 255,
+ 148, 1, 0, 0, 64, 0,
+ 0, 0, 2, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 0, 0, 36, 0,
+ 1, 0, 60, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 9, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 12, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 12, 0, 0, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 11, 0, 170, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 0, 0, 228, 144,
+ 10, 0, 238, 160, 10, 0,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 15, 128, 1, 0,
+ 85, 128, 2, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 1, 0, 228, 160,
+ 1, 0, 0, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 2, 0, 0, 3, 2, 0,
+ 3, 128, 1, 0, 228, 128,
+ 11, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 11, 0,
+ 255, 160, 5, 0, 0, 3,
+ 0, 0, 2, 128, 0, 0,
+ 255, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 224, 0, 0, 228, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 9, 0,
+ 238, 160, 9, 0, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 1, 128, 1, 0, 255, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 128, 8, 0,
+ 228, 161, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 255, 128, 0, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 85, 128,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 1, 0, 15, 128,
+ 4, 0, 228, 160, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 6, 0, 228, 160,
+ 0, 0, 170, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 7, 0,
+ 228, 160, 0, 0, 255, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 3, 192,
+ 0, 0, 255, 128, 0, 0,
+ 228, 160, 0, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 148, 2, 0, 0,
+ 64, 0, 1, 0, 165, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 12, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 50, 0, 0, 11, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 10, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 10, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 5, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 6, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 166, 10, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 10, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 11, 0, 0, 0,
+ 14, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 18, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 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,
+ 2, 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, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadMaskVS = { LayerQuadMaskVS, sizeof(LayerQuadMaskVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t1.xyz
+ dcl_2d s0
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s0
+ mul r0, r0.x, c0
+ mov oC0, r0
+
+// approximately 5 instruction slots used (1 texture, 4 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[1], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 1
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+mul o0.xyzw, r0.xxxx, cb0[0].xyzw
+ret
+// Approximately 4 instruction slots used
+#endif
+
+const BYTE SolidColorShaderMask[] =
+{
+ 68, 88, 66, 67, 11, 0,
+ 43, 127, 123, 42, 253, 228,
+ 4, 220, 7, 130, 11, 94,
+ 213, 177, 1, 0, 0, 0,
+ 164, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 220, 0, 0, 0, 156, 1,
+ 0, 0, 24, 2, 0, 0,
+ 0, 5, 0, 0, 112, 5,
+ 0, 0, 65, 111, 110, 57,
+ 156, 0, 0, 0, 156, 0,
+ 0, 0, 0, 2, 255, 255,
+ 104, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 170, 176,
+ 5, 0, 0, 3, 0, 0,
+ 3, 128, 0, 0, 255, 128,
+ 1, 0, 228, 176, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 184, 0, 0, 0,
+ 64, 0, 0, 0, 46, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 5, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 114, 16, 16, 0,
+ 2, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 1, 0, 0, 0,
+ 14, 0, 0, 7, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 4, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 2, 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, 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, 82, 68, 69, 70,
+ 224, 2, 0, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 184, 2,
+ 0, 0, 124, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 133, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 139, 0, 0, 0, 0, 0,
+ 0, 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, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 77, 97, 115, 107, 0, 36,
+ 71, 108, 111, 98, 97, 108,
+ 115, 0, 139, 0, 0, 0,
+ 11, 0, 0, 0, 172, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 180, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 192, 1, 0, 0, 0, 0,
+ 0, 0, 208, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 240, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 0, 0,
+ 0, 0, 48, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 80, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 192, 1, 0, 0, 0, 0,
+ 0, 0, 112, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 144, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 155, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 165, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sSolidColorShaderMask = { SolidColorShaderMask, sizeof(SolidColorShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1.xyz, r1, c0.x
+ mov r1.w, c0.x
+ mul r0, r0.x, r1
+ mov oC0, r0
+
+// approximately 8 instruction slots used (2 texture, 6 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 2
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul r0.xyz, r0.xyzx, cb0[1].xxxx
+div r1.xy, v2.xyxx, v2.zzzz
+sample r1.xyzw, r1.xyxx, t5.xyzw, s0
+mov r0.w, cb0[1].x
+mul o0.xyzw, r0.xyzw, r1.xxxx
+ret
+// Approximately 7 instruction slots used
+#endif
+
+const BYTE RGBShaderMask[] =
+{
+ 68, 88, 66, 67, 89, 221,
+ 15, 22, 232, 140, 114, 122,
+ 200, 15, 217, 125, 153, 18,
+ 224, 0, 1, 0, 0, 0,
+ 136, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 36, 1, 0, 0, 88, 2,
+ 0, 0, 212, 2, 0, 0,
+ 228, 5, 0, 0, 84, 6,
+ 0, 0, 65, 111, 110, 57,
+ 228, 0, 0, 0, 228, 0,
+ 0, 0, 0, 2, 255, 255,
+ 172, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 5, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 7, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 5, 0,
+ 0, 3, 1, 0, 7, 128,
+ 1, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 1, 0, 8, 128, 0, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 44, 1, 0, 0,
+ 64, 0, 0, 0, 75, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 14, 0, 0, 7, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 6, 130, 0, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 7, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 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, 2, 0,
+ 0, 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, 82, 68,
+ 69, 70, 8, 3, 0, 0,
+ 1, 0, 0, 0, 188, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 224, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 176, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 176, 0, 0, 0,
+ 11, 0, 0, 0, 212, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 248, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 132, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 195, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 205, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sRGBShaderMask = { RGBShaderMask, sizeof(RGBShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1, r1, c0.x
+ mul r0, r0.x, r1
+ mov oC0, r0
+
+// approximately 7 instruction slots used (2 texture, 5 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 2
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+mul r1.xyzw, r1.xyzw, cb0[1].xxxx
+mul o0.xyzw, r0.xxxx, r1.xyzw
+ret
+// Approximately 6 instruction slots used
+#endif
+
+const BYTE RGBAShaderMask[] =
+{
+ 68, 88, 66, 67, 195, 236,
+ 129, 118, 244, 48, 247, 117,
+ 155, 208, 5, 31, 9, 224,
+ 75, 19, 1, 0, 0, 0,
+ 100, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 24, 1, 0, 0, 52, 2,
+ 0, 0, 176, 2, 0, 0,
+ 192, 5, 0, 0, 48, 6,
+ 0, 0, 65, 111, 110, 57,
+ 216, 0, 0, 0, 216, 0,
+ 0, 0, 0, 2, 255, 255,
+ 160, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 5, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 7, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 20, 1, 0, 0,
+ 64, 0, 0, 0, 69, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 6, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 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, 2, 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, 82, 68,
+ 69, 70, 8, 3, 0, 0,
+ 1, 0, 0, 0, 188, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 224, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 176, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 176, 0, 0, 0,
+ 11, 0, 0, 0, 212, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 248, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 132, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 195, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 205, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sRGBAShaderMask = { RGBAShaderMask, sizeof(RGBAShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+// c1 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t1
+// s1 s0 t2
+// s2 s0 t3
+// s3 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ dcl_2d s3
+ mov r0.w, c4.z
+ texld r1, t0, s1
+ texld r2, t0, s0
+ add r2.x, r2.x, c4.x
+ add r2.y, r1.x, c4.y
+ rcp r2.w, t1.z
+ mul r1.xy, r2.w, t1
+ texld r3, t0, s2
+ texld r1, r1, s3
+ add r2.z, r3.x, c4.y
+ dp3 r0.x, c1, r2
+ dp3 r0.y, c2, r2
+ dp3 r0.z, c3, r2
+ mul r0, r0, c0.x
+ mul r0, r1.x, r0
+ mov oC0, r0
+
+// approximately 16 instruction slots used (4 texture, 12 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 3
+mov r0.w, l(1.000000)
+sample r1.xyzw, v1.xyxx, t1.xyzw, s0
+add r1.x, r1.x, l(-0.062750)
+sample r2.xyzw, v1.xyxx, t2.xyzw, s0
+add r1.y, r2.x, l(-0.501960)
+sample r2.xyzw, v1.xyxx, t3.xyzw, s0
+add r1.z, r2.x, l(-0.501960)
+dp3 r0.x, cb0[3].xyzx, r1.xyzx
+dp3 r0.y, cb0[4].xyzx, r1.xyzx
+dp3 r0.z, cb0[5].xyzx, r1.xyzx
+mul r0.xyzw, r0.xyzw, cb0[1].xxxx
+div r1.xy, v2.xyxx, v2.zzzz
+sample r1.xyzw, r1.xyxx, t5.xyzw, s0
+mul o0.xyzw, r0.xyzw, r1.xxxx
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE YCbCrShaderMask[] =
+{
+ 68, 88, 66, 67, 239, 174,
+ 189, 163, 31, 16, 244, 108,
+ 86, 227, 23, 8, 28, 147,
+ 43, 62, 1, 0, 0, 0,
+ 168, 8, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 232, 1, 0, 0, 52, 4,
+ 0, 0, 176, 4, 0, 0,
+ 4, 8, 0, 0, 116, 8,
+ 0, 0, 65, 111, 110, 57,
+ 168, 1, 0, 0, 168, 1,
+ 0, 0, 0, 2, 255, 255,
+ 92, 1, 0, 0, 76, 0,
+ 0, 0, 2, 0, 52, 0,
+ 0, 0, 76, 0, 0, 0,
+ 76, 0, 4, 0, 36, 0,
+ 0, 0, 76, 0, 1, 0,
+ 0, 0, 2, 0, 1, 0,
+ 3, 0, 2, 0, 5, 0,
+ 3, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 18, 131, 128, 189,
+ 115, 128, 0, 191, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 3, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 170, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 2, 0, 0, 3,
+ 2, 0, 1, 128, 2, 0,
+ 0, 128, 4, 0, 0, 160,
+ 2, 0, 0, 3, 2, 0,
+ 2, 128, 1, 0, 0, 128,
+ 4, 0, 85, 160, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 1, 0, 3, 128,
+ 2, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 3, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 3, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 4, 128,
+ 3, 0, 0, 128, 4, 0,
+ 85, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 228, 160, 2, 0, 228, 128,
+ 8, 0, 0, 3, 0, 0,
+ 2, 128, 2, 0, 228, 160,
+ 2, 0, 228, 128, 8, 0,
+ 0, 3, 0, 0, 4, 128,
+ 3, 0, 228, 160, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 1, 0, 0, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 68, 2, 0, 0, 64, 0,
+ 0, 0, 145, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 2, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 3, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 18, 131, 128, 189, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 2, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 34, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 3, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 66, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 115, 128, 0, 191,
+ 16, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 8,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 14, 0, 0, 7,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 2, 0, 0, 0, 166, 26,
+ 16, 0, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 5, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 15, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 9, 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,
+ 4, 0, 0, 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,
+ 82, 68, 69, 70, 76, 3,
+ 0, 0, 1, 0, 0, 0,
+ 0, 1, 0, 0, 6, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 36, 3, 0, 0,
+ 220, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 229, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 232, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 236, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 3, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 240, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 246, 0,
+ 0, 0, 0, 0, 0, 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,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 89, 0,
+ 116, 67, 98, 0, 116, 67,
+ 114, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 246, 0, 0, 0, 11, 0,
+ 0, 0, 24, 1, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 60, 2, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 76, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 2, 0, 0, 0, 140, 2,
+ 0, 0, 0, 0, 0, 0,
+ 156, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 188, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 220, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 17, 3, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 7, 7, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sYCbCrShaderMask = { YCbCrShaderMask, sizeof(YCbCrShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tRGBWhite texture float4 2d t4 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+// SV_Target 1 xyzw 1 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t4
+// s2 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s2
+ mul r0.x, r0.x, c0.x
+ texld r1, t0, s0
+ texld r2, t0, s1
+ add r2, r1, -r2
+ add r2, r2, c1.x
+ mov r1.w, r2.y
+ mul r2, r0.x, r2
+ mul r0, r0.x, r1
+ mov oC0, r0
+ mov oC1, r2
+
+// approximately 13 instruction slots used (3 texture, 10 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t4
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_output o1.xyzw
+dcl_temps 3
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+mul r0.x, r0.x, cb0[1].x
+sample r1.xyzw, v1.xyxx, t4.xyzw, s0
+sample r2.xyzw, v1.xyxx, t0.xyzw, s0
+add r1.xyzw, -r1.xyzw, r2.xyzw
+add r1.xyzw, r1.xyzw, l(1.000000, 1.000000, 1.000000, 1.000000)
+mov r2.w, r1.y
+mul o1.xyzw, r0.xxxx, r1.xyzw
+mul o0.xyzw, r0.xxxx, r2.xyzw
+ret
+// Approximately 11 instruction slots used
+#endif
+
+const BYTE ComponentAlphaShaderMask[] =
+{
+ 68, 88, 66, 67, 53, 1,
+ 100, 182, 2, 181, 247, 136,
+ 91, 215, 208, 183, 243, 6,
+ 78, 16, 1, 0, 0, 0,
+ 220, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 152, 1, 0, 0, 108, 3,
+ 0, 0, 232, 3, 0, 0,
+ 32, 7, 0, 0, 144, 7,
+ 0, 0, 65, 111, 110, 57,
+ 88, 1, 0, 0, 88, 1,
+ 0, 0, 0, 2, 255, 255,
+ 28, 1, 0, 0, 60, 0,
+ 0, 0, 1, 0, 48, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 3, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 4, 0, 1, 0,
+ 5, 0, 2, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 2, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 0, 0,
+ 0, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 15, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 129, 2, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 0, 128,
+ 2, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 8, 15, 128, 2, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 204, 1,
+ 0, 0, 64, 0, 0, 0,
+ 115, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 4, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 1, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 4, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 0, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 11, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 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, 3, 0,
+ 0, 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, 82, 68,
+ 69, 70, 48, 3, 0, 0,
+ 1, 0, 0, 0, 228, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 8, 3, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 197, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 202, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 212, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 218, 0, 0, 0, 0, 0,
+ 0, 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, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 82, 71, 66, 0, 116, 82,
+ 71, 66, 87, 104, 105, 116,
+ 101, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 218, 0, 0, 0, 11, 0,
+ 0, 0, 252, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 48, 2,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 80, 2,
+ 0, 0, 0, 0, 0, 0,
+ 96, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 112, 2,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 160, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 172, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 192, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 224, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 235, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 245, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 7, 7, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sComponentAlphaShaderMask = { ComponentAlphaShaderMask, sizeof(ComponentAlphaShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 8 ( FLT, FLT, FLT, FLT)
+// c11 cb0 12 2 ( FLT, FLT, FLT, FLT)
+// c13 cb0 15 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c14, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c9.zwzw, c9
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c2
+ mad r0, c1, r0.x, r1
+ add r0, r0, c3
+ rcp r1.x, r0.w
+ mul r0.xyz, r0, r1.x
+ add r0, r0, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ add r1.xy, r0, c14.x
+ mad r1.y, r1.y, -c14.y, c14.x
+ mul r1.x, r1.x, c14.y
+ mul r1.yz, r1.y, c12.xyxw
+ mad r1.xy, c11.yxzw, r1.x, r1.yzzw
+ add oT0.zw, r1.xyxy, c13.xyyx
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+ mov oT1.xyz, c14.z
+
+// approximately 22 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[16], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o1.zw
+dcl_output o2.xyz
+dcl_temps 2
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r0.xyz, r0.xyzx, r0.wwww
+add r0.xyzw, r0.xyzw, -cb0[8].xyzw
+mul r0.xyz, r0.wwww, r0.xyzx
+mul r1.xyzw, r0.yyyy, cb0[5].xyzw
+mad r1.xyzw, cb0[4].xyzw, r0.xxxx, r1.xyzw
+mad r1.xyzw, cb0[6].xyzw, r0.zzzz, r1.xyzw
+mad r0.xyzw, cb0[7].xyzw, r0.wwww, r1.xyzw
+mov o0.xyzw, r0.xyzw
+add r0.xy, r0.xyxx, l(1.000000, 1.000000, 0.000000, 0.000000)
+mad r0.y, -r0.y, l(0.500000), l(1.000000)
+mul r0.x, r0.x, l(0.500000)
+mul r0.yz, r0.yyyy, cb0[13].xxyx
+mad r0.xy, cb0[12].xyxx, r0.xxxx, r0.yzyy
+add o1.zw, r0.xxxy, cb0[15].xxxy
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+mov o2.xyz, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE LayerQuadBlendVS[] =
+{
+ 68, 88, 66, 67, 36, 1,
+ 251, 17, 122, 90, 56, 20,
+ 13, 210, 38, 20, 162, 170,
+ 120, 203, 1, 0, 0, 0,
+ 56, 9, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 60, 2, 0, 0, 100, 5,
+ 0, 0, 224, 5, 0, 0,
+ 124, 8, 0, 0, 176, 8,
+ 0, 0, 65, 111, 110, 57,
+ 252, 1, 0, 0, 252, 1,
+ 0, 0, 0, 2, 254, 255,
+ 164, 1, 0, 0, 88, 0,
+ 0, 0, 4, 0, 36, 0,
+ 0, 0, 84, 0, 0, 0,
+ 84, 0, 0, 0, 36, 0,
+ 1, 0, 84, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 8, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 12, 0, 2, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 1, 0, 13, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 14, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 10, 0, 238, 160,
+ 10, 0, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 1, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 0, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 8, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 255, 128, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 85, 128, 5, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 4, 0, 228, 160,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 15, 128, 6, 0,
+ 228, 160, 0, 0, 170, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 7, 0, 228, 160, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 3, 128, 0, 0, 228, 128,
+ 14, 0, 0, 160, 4, 0,
+ 0, 4, 1, 0, 2, 128,
+ 1, 0, 85, 128, 14, 0,
+ 85, 161, 14, 0, 0, 160,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 1, 0, 0, 128,
+ 14, 0, 85, 160, 5, 0,
+ 0, 3, 1, 0, 6, 128,
+ 1, 0, 85, 128, 12, 0,
+ 196, 160, 4, 0, 0, 4,
+ 1, 0, 3, 128, 11, 0,
+ 225, 160, 1, 0, 0, 128,
+ 1, 0, 233, 128, 2, 0,
+ 0, 3, 0, 0, 12, 224,
+ 1, 0, 68, 128, 13, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 192, 0, 0,
+ 255, 128, 0, 0, 228, 160,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 7, 224,
+ 14, 0, 170, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 32, 3, 0, 0, 64, 0,
+ 1, 0, 200, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 0, 0, 0, 0, 103, 0,
+ 0, 4, 242, 32, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 5, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 10,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 56, 0, 0, 8,
+ 98, 0, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 6, 129,
+ 32, 0, 0, 0, 0, 0,
+ 13, 0, 0, 0, 50, 0,
+ 0, 10, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 150, 5, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 6, 4, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 54, 0, 0, 8, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 21, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 18, 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,
+ 2, 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, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 128, 0, 0, 0,
+ 4, 0, 0, 0, 8, 0,
+ 0, 0, 104, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 116, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 116, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadBlendVS = { LayerQuadBlendVS, sizeof(LayerQuadBlendVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 11 ( FLT, FLT, FLT, FLT)
+// c14 cb0 15 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c15, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mov r0.z, c15.x
+ rcp r0.w, c11.z
+ mad r1.xy, v0, c10.zwzw, c10
+ mul r2, r1.y, c2
+ mad r1, c1, r1.x, r2
+ add r1, r1, c3
+ add r2.xy, r1, -c11
+ mul r0.x, r0.w, r2.x
+ rcp r0.w, c11.w
+ mul r0.y, r0.w, r2.y
+ mul oT1.xyz, r0, r1.w
+ mad oT0.xy, v0, c9.zwzw, c9
+ rcp r0.x, r1.w
+ mul r1.xyz, r0.x, r1
+ add r0, r1, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ add r1.xy, r0, c15.x
+ mad r1.y, r1.y, -c15.y, c15.x
+ mul r1.x, r1.x, c15.y
+ mul r1.yz, r1.y, c13.xyxw
+ mad r1.xy, c12.yxzw, r1.x, r1.yzzw
+ add oT0.zw, r1.xyxy, c14.xyyx
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 28 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[16], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o1.zw
+dcl_output o2.xyz
+dcl_temps 4
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r1.xyz, r0.xyzx, r0.wwww
+mov r1.w, r0.w
+add r2.xyzw, r1.xyzw, -cb0[8].xyzw
+mul r1.xyz, r2.wwww, r2.xyzx
+mul r3.xyzw, r1.yyyy, cb0[5].xyzw
+mad r3.xyzw, cb0[4].xyzw, r1.xxxx, r3.xyzw
+mad r3.xyzw, cb0[6].xyzw, r1.zzzz, r3.xyzw
+mad r2.xyzw, cb0[7].xyzw, r2.wwww, r3.xyzw
+mov o0.xyzw, r2.xyzw
+add r0.zw, r2.xxxy, l(0.000000, 0.000000, 1.000000, 1.000000)
+mad r0.w, -r0.w, l(0.500000), l(1.000000)
+mul r0.z, r0.z, l(0.500000)
+mul r1.xy, r0.wwww, cb0[13].xyxx
+mad r0.zw, cb0[12].xxxy, r0.zzzz, r1.xxxy
+add o1.zw, r0.zzzw, cb0[15].xxxy
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+add r0.xy, r0.xyxx, -cb0[11].xyxx
+div r0.xy, r0.xyxx, cb0[11].zwzz
+mov r0.z, l(1.000000)
+mul o2.xyz, r1.wwww, r0.xyzx
+ret
+// Approximately 25 instruction slots used
+#endif
+
+const BYTE LayerQuadBlendMaskVS[] =
+{
+ 68, 88, 66, 67, 206, 205,
+ 172, 45, 15, 157, 207, 85,
+ 247, 28, 223, 137, 10, 58,
+ 17, 237, 1, 0, 0, 0,
+ 236, 9, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 136, 2, 0, 0, 24, 6,
+ 0, 0, 148, 6, 0, 0,
+ 48, 9, 0, 0, 100, 9,
+ 0, 0, 65, 111, 110, 57,
+ 72, 2, 0, 0, 72, 2,
+ 0, 0, 0, 2, 254, 255,
+ 252, 1, 0, 0, 76, 0,
+ 0, 0, 3, 0, 36, 0,
+ 0, 0, 72, 0, 0, 0,
+ 72, 0, 0, 0, 36, 0,
+ 1, 0, 72, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 11, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 1, 0, 14, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 15, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 15, 0, 0, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 11, 0, 170, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 0, 0, 228, 144,
+ 10, 0, 238, 160, 10, 0,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 15, 128, 1, 0,
+ 85, 128, 2, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 1, 0, 228, 160,
+ 1, 0, 0, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 2, 0, 0, 3, 2, 0,
+ 3, 128, 1, 0, 228, 128,
+ 11, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 11, 0,
+ 255, 160, 5, 0, 0, 3,
+ 0, 0, 2, 128, 0, 0,
+ 255, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 224, 0, 0, 228, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 9, 0,
+ 238, 160, 9, 0, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 1, 128, 1, 0, 255, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 128, 8, 0,
+ 228, 161, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 255, 128, 0, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 85, 128,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 1, 0, 15, 128,
+ 4, 0, 228, 160, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 6, 0, 228, 160,
+ 0, 0, 170, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 7, 0,
+ 228, 160, 0, 0, 255, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 3, 128,
+ 0, 0, 228, 128, 15, 0,
+ 0, 160, 4, 0, 0, 4,
+ 1, 0, 2, 128, 1, 0,
+ 85, 128, 15, 0, 85, 161,
+ 15, 0, 0, 160, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 1, 0, 0, 128, 15, 0,
+ 85, 160, 5, 0, 0, 3,
+ 1, 0, 6, 128, 1, 0,
+ 85, 128, 13, 0, 196, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 12, 0, 225, 160,
+ 1, 0, 0, 128, 1, 0,
+ 233, 128, 2, 0, 0, 3,
+ 0, 0, 12, 224, 1, 0,
+ 68, 128, 14, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 192, 0, 0, 255, 128,
+ 0, 0, 228, 160, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 12, 192, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 136, 3,
+ 0, 0, 64, 0, 1, 0,
+ 226, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 4, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 5,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 166, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 0, 0, 0, 10,
+ 194, 0, 16, 0, 0, 0,
+ 0, 0, 6, 4, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 50, 0, 0, 10, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 66, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 56, 0, 0, 8,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 13, 0, 0, 0, 50, 0,
+ 0, 10, 194, 0, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 6, 4, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 166, 14, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 11, 0, 0, 0,
+ 14, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 25, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 21, 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,
+ 3, 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, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 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,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 128, 0, 0, 0,
+ 4, 0, 0, 0, 8, 0,
+ 0, 0, 104, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 116, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 116, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadBlendMaskVS = { LayerQuadBlendMaskVS, sizeof(LayerQuadBlendMaskVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// tMask texture float4 2d t5 1
+// tBackdrop texture float4 2d t6 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c2 cb0 2 1 (UINT,UINT,UINT,UINT)
+// c3 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t1
+// s2 s0 t2
+// s3 s0 t3
+// s4 s0 t5
+// s5 s0 t6
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c6, -1, -2, -0.0627499968, -0.50195998
+ def c7, -2, -3, -4, -5
+ def c8, -6, -7, -8, -9
+ def c9, 0.5, 1, 0.25, -2
+ def c10, 16, -12, -14, 0
+ def c11, -10, -11, -12, -13
+ def c12, 0.300000012, 0.589999974, 0.109999999, 0
+ def c13, -1, -0, 0, 1
+ dcl t0
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ dcl_2d s3
+ dcl_2d s4
+ dcl_2d s5
+ mov r0.x, c13.z
+ mov r1.x, c13.z
+ mov r2.z, c13.z
+ mov r3.w, -c6.x
+ texld r4, t0, s2
+ texld r5, t0, s1
+ add r5.x, r5.x, c6.z
+ add r5.y, r4.x, c6.w
+ rcp r0.w, t1.z
+ mul r4.xy, r0.w, t1
+ texld r6, t0, s3
+ texld r4, r4, s4
+ add r5.z, r6.x, c6.w
+ dp3 r3.x, c3, r5
+ dp3 r3.y, c4, r5
+ dp3 r3.z, c5, r5
+ mul r3, r3, c1.x
+ mul r5, r4.x, r3
+ mov r6.xy, t0.wzzw
+ texld r7, t0, s0
+ texld r6, r6, s5
+ mul r7, r7, c1.x
+ mul r8, r4.x, r7
+ mov r9.xy, c6
+ add r10, r9.xyxx, c2.xxyz
+ mul r10, r10, r10
+ cmp r5, -r10.x, r8, r5
+ cmp r3, -r10.x, r7, r3
+ mov r7.w, c1.x
+ mul r8, r4.x, r7
+ cmp r3, -c2.x, r7, r3
+ mul r4, r4.x, c0
+ cmp r5, -c2.x, r8, r5
+ cmp r7.xy, -r10.yzzw, c13.x, c13.y
+ cmp r0.w, -r10.x, c6.x, r7.x
+ cmp r1.w, -c2.y, r9.x, r7.y
+ cmp r0.w, -c2.x, r9.x, r0.w
+ cmp r4, r0.w, r4, r5
+ cmp r3, r0.w, c0, r3
+ cmp r3, -c2.y, r3, r4
+ cmp r3, r1.w, c13.zzzw, r3
+ rcp r0.w, r3.w
+ mul r4.xyz, r0.w, r3
+ cmp r4.xyz, -c2.w, r3, r4
+ add r5.xy, -r4.yzzw, r4
+ cmp r5.zw, r5.x, r4.xyxy, r4.xyyx
+ max r0.w, r5.z, r4.z
+ min r1.w, r4.z, r5.w
+ add r7.w, r0.w, -r1.w
+ rcp r0.w, r6.w
+ mul r8.xyz, r0.w, r6
+ mad r5.zw, r6.xyzy, r0.w, -r8.xyxz
+ mul r9.xy, r7.w, r5.zwzw
+ mad r11, r6.yxxz, r0.w, -r8.xzyy
+ rcp r1.w, r11.x
+ mul r7.y, r1.w, r9.x
+ cmp r1.yz, r11.z, c13.z, r7.xwyw
+ mul r12, r7.w, r11
+ rcp r1.w, r5.w
+ mul r7.x, r1.w, r12.y
+ cmp r2.xy, r11.w, c13.z, r7.xwzw
+ cmp r1.xyz, r5.z, r1, r2
+ rcp r1.w, r5.z
+ mul r7.z, r1.w, r12.x
+ cmp r0.yz, r11.y, c13.z, r7.xzww
+ cmp r0.xyz, r11.w, r0, r1
+ mov r1.y, c13.z
+ mov r2.y, c13.z
+ mov r10.z, c13.z
+ rcp r1.w, r11.z
+ mul r7.y, r1.w, r12.w
+ cmp r2.xz, r11.x, c13.z, r7.wyyw
+ rcp r1.w, r11.y
+ mul r7.x, r1.w, r9.y
+ cmp r10.xy, r5.z, c13.z, r7.wxzw
+ cmp r2.xyz, r11.w, r2, r10
+ rcp r1.w, r11.w
+ mul r7.z, r1.w, r12.z
+ cmp r1.xz, r5.w, c13.z, r7.zyww
+ cmp r1.xyz, r5.z, r1, r2
+ cmp r0.xyz, r11.x, r0, r1
+ cmp r1.xy, r11.z, r8, r8.yxzw
+ dp3 r4.w, c12, r0
+ dp3 r8.w, c12, r8
+ add r4.w, -r4.w, r8.w
+ add r0.xyz, r0, r4.w
+ add r4.w, -r0.y, r0.x
+ cmp r1.zw, r4.w, r0.xyyx, r0.xyxy
+ min r4.w, r0.z, r1.z
+ max r2.x, r1.w, r0.z
+ dp3 r1.z, c12, r0
+ add r1.w, -r4.w, r1.z
+ rcp r1.w, r1.w
+ add r2.yzw, r0.xxyz, -r1.z
+ mul r2.yzw, r1.z, r2
+ mad r2.yzw, r2, r1.w, r1.z
+ cmp r0.xyz, r4.w, r0, r2.yzww
+ add r2.yzw, -r1.z, r0.xxyz
+ add r1.w, -r1.z, -c6.x
+ mul r2.yzw, r1.w, r2
+ add r1.w, -r1.z, r2.x
+ add r4.w, -r2.x, -c6.x
+ rcp r1.w, r1.w
+ mad r2.xyz, r2.yzww, r1.w, r1.z
+ cmp r0.xyz, r4.w, r0, r2
+ mov r4.w, c2.z
+ add r1.z, r4.w, c10.z
+ mul r1.z, r1.z, r1.z
+ dp3 r1.w, c12, r4
+ add r2.x, -r8.w, r1.w
+ add r1.w, -r1.w, r8.w
+ add r2.yzw, r1.w, r4.xxyz
+ mad r7.xyz, r6, r0.w, r2.x
+ add r1.w, -r7.y, r7.x
+ cmp r5.zw, r1.w, r7.xyyx, r7.xyxy
+ min r1.w, r7.z, r5.z
+ max r2.x, r5.w, r7.z
+ dp3 r7.w, c12, r7
+ add r5.z, -r1.w, r7.w
+ rcp r5.z, r5.z
+ add r9.xyz, -r7.w, r7
+ mul r9.xyz, r7.w, r9
+ mad r9.xyz, r9, r5.z, r7.w
+ cmp r7.xyz, r1.w, r7, r9
+ add r9.xyz, -r7.w, r7
+ add r1.w, -r7.w, -c6.x
+ mul r9.xyz, r1.w, r9
+ add r1.w, r2.x, -r7.w
+ add r9.w, -r2.x, -c6.x
+ rcp r1.w, r1.w
+ mad r9.xyz, r9, r1.w, r7.w
+ cmp r7.xyz, r9.w, r7, r9
+ cmp r7.xyz, -r1.z, r7, c13.z
+ add r7.w, -r2.z, r2.y
+ cmp r1.zw, r7.w, r2.xyzy, r2.xyyz
+ min r7.w, r2.w, r1.z
+ max r5.z, r1.w, r2.w
+ dp3 r5.w, c12, r2.yzww
+ add r1.z, -r7.w, r5.w
+ rcp r1.z, r1.z
+ add r9.xyz, r2.yzww, -r5.w
+ mul r9.xyz, r5.w, r9
+ mad r9.xyz, r9, r1.z, r5.w
+ cmp r2.xyz, r7.w, r2.yzww, r9
+ add r9.xyz, -r5.w, r2
+ add r2.w, -r5.w, -c6.x
+ mul r9.xyz, r2.w, r9
+ add r2.w, -r5.w, r5.z
+ add r7.w, -r5.z, -c6.x
+ rcp r2.w, r2.w
+ mad r9.xyz, r9, r2.w, r5.w
+ cmp r2.xyz, r7.w, r2, r9
+ add r9, r4.w, c11
+ mul r9, r9, r9
+ cmp r2.xyz, -r9.w, r2, r7
+ cmp r0.xyz, -r9.z, r0, r2
+ add r2, -r4.xxzy, r4.yzxz
+ mov r7.y, c13.z
+ mov r10.y, c13.z
+ mov r11.z, c13.z
+ rcp r7.w, r2.z
+ max r11.w, r1.x, r8.z
+ min r5.z, r8.z, r1.y
+ add r1.w, -r5.z, r11.w
+ mul r5.zw, r1.w, r5.xyxy
+ mul r1.x, r7.w, r5.w
+ cmp r11.xy, r2.y, c13.z, r1.wxzw
+ rcp r5.w, r5.x
+ mul r12, r1.w, r2
+ mul r1.y, r5.w, r12.w
+ cmp r10.xz, r2.x, c13.z, r1.wyyw
+ cmp r10.xyz, r2.w, r10, r11
+ rcp r5.w, r2.w
+ mul r1.z, r5.w, r5.z
+ cmp r7.xz, r5.y, c13.z, r1.zyww
+ cmp r7.xyz, r2.y, r7, r10
+ mov r10.x, c13.z
+ mov r11.x, c13.z
+ mov r13.z, c13.z
+ rcp r7.w, r2.x
+ mul r1.y, r7.w, r12.y
+ cmp r11.yz, r5.x, c13.z, r1.xwyw
+ rcp r7.w, r5.y
+ mul r1.x, r7.w, r12.z
+ cmp r13.xy, r2.w, c13.z, r1.xwzw
+ cmp r5.xyz, r2.y, r11, r13
+ rcp r5.w, r2.y
+ mul r1.z, r5.w, r12.x
+ cmp r10.yz, r2.z, c13.z, r1.xzww
+ cmp r1.xyz, r2.w, r10, r5
+ cmp r1.xyz, r2.x, r1, r7
+ dp3 r1.w, c12, r1
+ add r1.w, -r1.w, r8.w
+ add r1.xyz, r1.w, r1
+ add r1.w, -r1.y, r1.x
+ cmp r2.xy, r1.w, r1.yxzw, r1
+ min r8.w, r1.z, r2.x
+ max r5.x, r2.y, r1.z
+ dp3 r1.w, c12, r1
+ add r2.x, -r8.w, r1.w
+ rcp r2.x, r2.x
+ add r2.yzw, -r1.w, r1.xxyz
+ mul r2.yzw, r1.w, r2
+ mad r2.xyz, r2.yzww, r2.x, r1.w
+ cmp r1.xyz, r8.w, r1, r2
+ add r2.xyz, -r1.w, r1
+ add r2.w, -r1.w, -c6.x
+ mul r2.xyz, r2.w, r2
+ add r2.w, -r1.w, r5.x
+ add r8.w, -r5.x, -c6.x
+ rcp r2.w, r2.w
+ mad r2.xyz, r2, r2.w, r1.w
+ cmp r1.xyz, r8.w, r1, r2
+ cmp r0.xyz, -r9.y, r1, r0
+ mad r1.xyz, r6, r0.w, r4
+ mul r2.xyz, r4, r8
+ mad r5.xyz, r2, c6.y, r1
+ mad r1.xyz, r8, -r4, r1
+ cmp r0.xyz, -r9.x, r5, r0
+ mad r5.xyz, r6, r0.w, -r4
+ abs r5.xyz, r5
+ add r7, r4.w, c8
+ mul r7, r7, r7
+ cmp r0.xyz, -r7.w, r5, r0
+ add r5.xy, -r4.yzzw, c9.x
+ mad r9.xyz, r4, -c9.w, -c9.y
+ mad r1.w, r6.z, -r0.w, c9.z
+ mad r10.xyz, r8, c10.x, c10.y
+ mad r10.xyz, r10, r8, -c7.z
+ mul r10.xyz, r8, r10
+ rsq r2.w, r8.z
+ rcp r2.w, r2.w
+ cmp r1.w, r1.w, r10.z, r2.w
+ mad r1.w, r6.z, -r0.w, r1.w
+ mad r1.w, r9.z, r1.w, r8.z
+ mad r11.xyz, r4, c6.y, -c6.x
+ mul r11.xyz, r8, r11
+ mad r12, r6.yzxy, -r0.w, c9.yyzz
+ mad r5.zw, r11.xyyz, -r12.xyxy, r8.xyyz
+ cmp r13.z, r5.y, r5.w, r1.w
+ rsq r1.w, r8.y
+ rcp r1.w, r1.w
+ cmp r1.w, r12.w, r10.y, r1.w
+ mad r1.w, r6.y, -r0.w, r1.w
+ mad r1.w, r9.y, r1.w, r8.y
+ cmp r13.y, r5.x, r5.z, r1.w
+ add r14, -r4.xyzx, c9.yyyx
+ rsq r1.w, r8.x
+ rcp r1.w, r1.w
+ cmp r1.w, r12.z, r10.x, r1.w
+ mad r1.w, r6.x, -r0.w, r1.w
+ mad r1.w, r9.x, r1.w, r8.x
+ mad r9, r6.xyzx, -r0.w, c9.xxxy
+ mad r6.xyz, r6, r0.w, c6.x
+ mul r6.xyz, r6, r6
+ mad r0.w, r11.x, -r9.w, r8.x
+ cmp r13.x, r14.w, r0.w, r1.w
+ cmp r0.xyz, -r7.z, r13, r0
+ add r10.xyz, r8, r8
+ mad r11.xyz, r4, -c6.y, r10
+ add r11.xyz, r11, c6.x
+ mad r13.xyz, r4, -r10, r11
+ mul r10.xyz, r4, r10
+ add r15.xyz, r4, r4
+ mul r16.xyz, r8, r15
+ mad r11.xyz, r15, -r8, r11
+ cmp r9.xyz, r9, r10, r11
+ cmp r5.yz, r5.xxyw, r16, r13
+ cmp r5.x, r14.w, r16.x, r13.x
+ cmp r0.xyz, -r7.y, r5, r0
+ rcp r0.w, r4.x
+ mad r0.w, r9.w, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ mul r5.xyz, r4, r4
+ cmp r0.w, -r5.x, c13.z, r1.w
+ cmp r10.x, -r6.x, -c6.x, r0.w
+ rcp r0.w, r4.y
+ mad r0.w, r12.x, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ cmp r0.w, -r5.y, c13.z, r1.w
+ cmp r10.y, -r6.y, -c6.x, r0.w
+ rcp r0.w, r4.z
+ mad r0.w, r12.y, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ cmp r0.w, -r5.z, c13.z, r1.w
+ cmp r10.z, -r6.z, -c6.x, r0.w
+ cmp r0.xyz, -r7.x, r10, r0
+ add r5, r4.w, c7
+ mul r5, r5, r5
+ add r6.xyz, r4, c6.x
+ mul r6.xyz, r6, r6
+ rcp r0.w, r14.x
+ mul r0.w, r0.w, r8.x
+ min r1.w, r0.w, -c6.x
+ cmp r0.w, -r6.x, -c6.x, r1.w
+ mul r7.xyz, r8, r8
+ cmp r10.x, -r7.x, c13.z, r0.w
+ rcp r0.w, r14.y
+ rcp r1.w, r14.z
+ mul r1.w, r1.w, r8.z
+ min r2.w, r1.w, -c6.x
+ cmp r1.w, -r6.z, -c6.x, r2.w
+ cmp r10.z, -r7.z, c13.z, r1.w
+ mul r0.w, r0.w, r8.y
+ min r1.w, r0.w, -c6.x
+ cmp r0.w, -r6.y, -c6.x, r1.w
+ cmp r10.y, -r7.y, c13.z, r0.w
+ cmp r0.xyz, -r5.w, r10, r0
+ max r6.xyz, r8, r4
+ min r7.xyz, r4, r8
+ cmp r0.xyz, -r5.z, r6, r0
+ cmp r0.xyz, -r5.y, r7, r0
+ cmp r0.xyz, -r5.x, r9, r0
+ cmp r0.xyz, -r10.w, r1, r0
+ cmp r0.xyz, -c2.z, r2, r0
+ lrp r1.xyz, r6.w, r0, r4
+ mul r1.w, r6.w, r6.w
+ mul r0.xyz, r3.w, r1
+ mul r1.x, r3.w, r3.w
+ mov r0.w, r3.w
+ cmp r0, -r1.x, c13.z, r0
+ cmp r0, -r1.w, r3, r0
+ mov oC0, r0
+
+// approximately 323 instruction slots used (6 texture, 317 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_resource_texture2d (float,float,float,float) t6
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v1.zw
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 22
+sample r0.xyzw, v1.zwzz, t6.xyzw, s0
+if_z cb0[2].y
+ if_z cb0[2].x
+ sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r1.xyz, r1.xyzx, cb0[1].xxxx
+ mov r1.w, cb0[1].x
+ mov r2.x, l(-1)
+ else
+ ieq r2.y, l(1), cb0[2].x
+ if_nz r2.y
+ sample r3.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r1.xyzw, r3.xyzw, cb0[1].xxxx
+ mov r2.x, l(-1)
+ else
+ ieq r2.x, l(2), cb0[2].x
+ if_nz r2.x
+ sample r3.xyzw, v1.xyxx, t1.xyzw, s0
+ add r3.x, r3.x, l(-0.062750)
+ sample r4.xyzw, v1.xyxx, t2.xyzw, s0
+ add r3.y, r4.x, l(-0.501960)
+ sample r4.xyzw, v1.xyxx, t3.xyzw, s0
+ add r3.z, r4.x, l(-0.501960)
+ dp3 r4.x, cb0[3].xyzx, r3.xyzx
+ dp3 r4.y, cb0[4].xyzx, r3.xyzx
+ dp3 r4.z, cb0[5].xyzx, r3.xyzx
+ mov r4.w, l(1.000000)
+ mul r1.xyzw, r4.xyzw, cb0[1].xxxx
+ endif
+ endif
+ endif
+ movc r1.xyzw, r2.xxxx, r1.xyzw, cb0[0].xyzw
+ mov r2.x, l(-1)
+else
+ ieq r2.x, l(1), cb0[2].y
+ if_nz r2.x
+ if_z cb0[2].x
+ sample r3.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r3.xyz, r3.xyzx, cb0[1].xxxx
+ div r2.yz, v2.xxyx, v2.zzzz
+ sample r4.xyzw, r2.yzyy, t5.xyzw, s0
+ mov r3.w, cb0[1].x
+ mul r1.xyzw, r3.xyzw, r4.xxxx
+ mov r2.y, l(-1)
+ else
+ ieq r2.z, l(1), cb0[2].x
+ if_nz r2.z
+ div r2.zw, v2.xxxy, v2.zzzz
+ sample r3.xyzw, r2.zwzz, t5.xyzw, s0
+ sample r4.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r4.xyzw, r4.xyzw, cb0[1].xxxx
+ mul r1.xyzw, r3.xxxx, r4.xyzw
+ mov r2.y, l(-1)
+ else
+ ieq r2.y, l(2), cb0[2].x
+ if_nz r2.y
+ div r2.zw, v2.xxxy, v2.zzzz
+ sample r3.xyzw, r2.zwzz, t5.xyzw, s0
+ sample r4.xyzw, v1.xyxx, t1.xyzw, s0
+ add r4.x, r4.x, l(-0.062750)
+ sample r5.xyzw, v1.xyxx, t2.xyzw, s0
+ add r4.y, r5.x, l(-0.501960)
+ sample r5.xyzw, v1.xyxx, t3.xyzw, s0
+ add r4.z, r5.x, l(-0.501960)
+ dp3 r5.x, cb0[3].xyzx, r4.xyzx
+ dp3 r5.y, cb0[4].xyzx, r4.xyzx
+ dp3 r5.z, cb0[5].xyzx, r4.xyzx
+ mov r5.w, l(1.000000)
+ mul r4.xyzw, r5.xyzw, cb0[1].xxxx
+ mul r1.xyzw, r3.xxxx, r4.xyzw
+ endif
+ endif
+ endif
+ if_z r2.y
+ div r2.yz, v2.xxyx, v2.zzzz
+ sample r3.xyzw, r2.yzyy, t5.xyzw, s0
+ mul r1.xyzw, r3.xxxx, cb0[0].xyzw
+ endif
+ endif
+endif
+movc r1.xyzw, r2.xxxx, r1.xyzw, l(0,0,0,1.000000)
+eq r2.x, r0.w, l(0.000000)
+if_nz r2.x
+ mov o0.xyzw, r1.xyzw
+ ret
+endif
+eq r2.x, r1.w, l(0.000000)
+if_nz r2.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+endif
+div r0.xyz, r0.xyzx, r0.wwww
+div r2.xyz, r1.xyzx, r1.wwww
+movc r1.xyz, cb0[2].wwww, r2.xyzx, r1.xyzx
+mul r2.xyz, r0.xyzx, r1.xyzx
+add r3.xyz, r0.xyzx, r1.xyzx
+mad r4.xyz, -r0.xyzx, r1.xyzx, r3.xyzx
+ge r5.xyzw, l(0.500000, 0.500000, 0.500000, 0.250000), r0.xyzx
+add r6.xyz, r0.xyzx, r0.xyzx
+mul r7.xyz, r1.xyzx, r6.xyzx
+add r8.xyz, r1.xyzx, r1.xyzx
+mad r9.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r6.xyzx
+add r9.xyz, r9.xyzx, l(-1.000000, -1.000000, -1.000000, 0.000000)
+mul r10.xyz, r0.xyzx, r8.xyzx
+mad r8.xyz, -r8.xyzx, r0.xyzx, r9.xyzx
+movc r5.xyz, r5.xyzx, r7.xyzx, r8.xyzx
+min r7.xyz, r0.xyzx, r1.xyzx
+ieq r8.xyzw, l(1, 2, 3, 4), cb0[2].zzzz
+max r11.xyz, r0.xyzx, r1.xyzx
+eq r12.xyzw, r0.xyzx, l(0.000000, 0.000000, 0.000000, 1.000000)
+eq r13.xyzw, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+add r14.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+div r14.xyz, r0.xyzx, r14.xyzx
+min r14.xyz, r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+movc r13.xyz, r13.xyzx, l(1.000000,1.000000,1.000000,0), r14.xyzx
+movc r12.xyz, r12.xyzx, l(0,0,0,0), r13.xyzx
+add r13.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+div r14.xyz, r13.xyzx, r1.xyzx
+min r14.xyz, r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+add r14.xyz, -r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+movc r2.w, r13.w, l(0), r14.x
+movc r15.x, r12.w, l(1.000000), r2.w
+eq r14.xw, r0.yyyz, l(1.000000, 0.000000, 0.000000, 1.000000)
+eq r16.xy, r1.yzyy, l(0.000000, 0.000000, 0.000000, 0.000000)
+movc r14.yz, r16.xxyx, l(0,0,0,0), r14.yyzy
+movc r15.yz, r14.xxwx, l(0,1.000000,1.000000,0), r14.yyzy
+ge r14.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r1.xyzx
+mad r6.xyz, -r1.xyzx, r6.xyzx, r9.xyzx
+movc r6.xyz, r14.xyzx, r10.xyzx, r6.xyzx
+ieq r9.xyzw, l(5, 6, 7, 8), cb0[2].zzzz
+mad r10.xyz, -r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(1.000000, 1.000000, 1.000000, 0.000000)
+mul r10.xyz, r0.xyzx, r10.xyzx
+mad r10.xyz, -r10.xyzx, r13.xyzx, r0.xyzx
+mad r13.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+mad r16.xyz, r0.xyzx, l(16.000000, 16.000000, 16.000000, 0.000000), l(-12.000000, -12.000000, -12.000000, 0.000000)
+mad r16.xyz, r16.xyzx, r0.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000)
+mul r16.xyz, r0.xyzx, r16.xyzx
+sqrt r17.xyz, r0.xyzx
+movc r2.w, r5.w, r16.x, r17.x
+add r2.w, -r0.x, r2.w
+mad r2.w, r13.x, r2.w, r0.x
+movc r18.x, r14.x, r10.x, r2.w
+ge r10.xw, l(0.250000, 0.000000, 0.000000, 0.250000), r0.yyyz
+movc r10.xw, r10.xxxw, r16.yyyz, r17.yyyz
+add r10.xw, -r0.yyyz, r10.xxxw
+mad r10.xw, r13.yyyz, r10.xxxw, r0.yyyz
+movc r18.yz, r14.yyzy, r10.yyzy, r10.xxwx
+add r10.xyz, r0.xyzx, -r1.xyzx
+mad r3.xyz, -r2.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r3.xyzx
+max r2.w, r0.y, r0.x
+max r2.w, r0.z, r2.w
+min r3.w, r0.y, r0.x
+min r3.w, r0.z, r3.w
+add r13.w, r2.w, -r3.w
+ge r2.w, r1.y, r1.x
+if_nz r2.w
+ lt r14.xyz, r1.xxzx, r1.zyyz
+ add r16.xyzw, -r1.xxzz, r1.yzxy
+ mul r17.xyz, r13.wwww, r16.xyzx
+ div r13.xyz, r17.xyzx, r16.yxwy
+ and r16.yz, r13.xxwx, r14.xxxx
+ ge r14.xw, r1.zzzz, r1.yyyx
+ and r17.yz, r13.wwyw, r14.yyyy
+ and r19.xy, r13.zwzz, r14.zzzz
+ mov r17.x, l(0)
+ mov r19.z, l(0)
+ movc r14.yzw, r14.wwww, r17.xxyz, r19.xxyz
+ mov r16.x, l(0)
+ movc r14.xyz, r14.xxxx, r16.xyzx, r14.yzwy
+else
+ lt r16.xyz, r1.yyzy, r1.zxxz
+ add r17.xyzw, -r1.yyzz, r1.xzyx
+ mul r19.xyz, r13.wwww, r17.xyzx
+ div r13.xyz, r19.xyzx, r17.yxwy
+ and r17.xz, r13.xxwx, r16.xxxx
+ ge r16.xw, r1.zzzz, r1.xxxy
+ and r19.xz, r13.wwyw, r16.yyyy
+ and r13.xy, r13.wzww, r16.zzzz
+ mov r19.y, l(0)
+ mov r13.z, l(0)
+ movc r13.xyz, r16.wwww, r19.xyzx, r13.xyzx
+ mov r17.y, l(0)
+ movc r14.xyz, r16.xxxx, r17.xyzx, r13.xyzx
+endif
+dp3 r2.w, l(0.300000, 0.590000, 0.110000, 0.000000), r0.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r14.xyzx
+add r3.w, r2.w, -r3.w
+add r13.xyz, r3.wwww, r14.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r13.xyzx
+min r4.w, r13.y, r13.x
+min r4.w, r13.z, r4.w
+max r5.w, r13.y, r13.x
+max r5.w, r13.z, r5.w
+lt r6.w, r4.w, l(0.000000)
+add r14.xyz, -r3.wwww, r13.xyzx
+mul r14.xyz, r3.wwww, r14.xyzx
+add r4.w, r3.w, -r4.w
+div r14.xyz, r14.xyzx, r4.wwww
+add r14.xyz, r3.wwww, r14.xyzx
+movc r13.xyz, r6.wwww, r14.xyzx, r13.xyzx
+lt r4.w, l(1.000000), r5.w
+add r14.xyz, -r3.wwww, r13.xyzx
+add r6.w, -r3.w, l(1.000000)
+mul r14.xyz, r6.wwww, r14.xyzx
+add r5.w, -r3.w, r5.w
+div r14.xyz, r14.xyzx, r5.wwww
+add r14.xyz, r3.wwww, r14.xyzx
+movc r13.xyz, r4.wwww, r14.xyzx, r13.xyzx
+ieq r14.xyzw, l(9, 10, 11, 12), cb0[2].zzzz
+max r3.w, r1.y, r1.x
+max r3.w, r1.z, r3.w
+min r4.w, r1.y, r1.x
+min r4.w, r1.z, r4.w
+add r16.w, r3.w, -r4.w
+ge r3.w, r0.y, r0.x
+if_nz r3.w
+ lt r17.xyz, r0.xxzx, r0.zyyz
+ add r19.xyzw, -r0.xxzz, r0.yzxy
+ mul r20.xyz, r16.wwww, r19.xyzx
+ div r16.xyz, r20.xyzx, r19.yxwy
+ and r19.yz, r16.xxwx, r17.xxxx
+ ge r17.xw, r0.zzzz, r0.yyyx
+ and r20.yz, r16.wwyw, r17.yyyy
+ and r21.xy, r16.zwzz, r17.zzzz
+ mov r20.x, l(0)
+ mov r21.z, l(0)
+ movc r17.yzw, r17.wwww, r20.xxyz, r21.xxyz
+ mov r19.x, l(0)
+ movc r17.xyz, r17.xxxx, r19.xyzx, r17.yzwy
+else
+ lt r19.xyz, r0.yyzy, r0.zxxz
+ add r20.xyzw, -r0.yyzz, r0.xzyx
+ mul r21.xyz, r16.wwww, r20.xyzx
+ div r16.xyz, r21.xyzx, r20.yxwy
+ and r20.xz, r16.xxwx, r19.xxxx
+ ge r19.xw, r0.zzzz, r0.xxxy
+ and r21.xz, r16.wwyw, r19.yyyy
+ and r16.xy, r16.wzww, r19.zzzz
+ mov r21.y, l(0)
+ mov r16.z, l(0)
+ movc r16.xyz, r19.wwww, r21.xyzx, r16.xyzx
+ mov r20.y, l(0)
+ movc r17.xyz, r19.xxxx, r20.xyzx, r16.xyzx
+endif
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r17.xyzx
+add r3.w, r2.w, -r3.w
+add r16.xyz, r3.wwww, r17.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r16.xyzx
+min r4.w, r16.y, r16.x
+min r4.w, r16.z, r4.w
+max r5.w, r16.y, r16.x
+max r5.w, r16.z, r5.w
+lt r6.w, r4.w, l(0.000000)
+add r17.xyz, -r3.wwww, r16.xyzx
+mul r17.xyz, r3.wwww, r17.xyzx
+add r4.w, r3.w, -r4.w
+div r17.xyz, r17.xyzx, r4.wwww
+add r17.xyz, r3.wwww, r17.xyzx
+movc r16.xyz, r6.wwww, r17.xyzx, r16.xyzx
+lt r4.w, l(1.000000), r5.w
+add r17.xyz, -r3.wwww, r16.xyzx
+add r6.w, -r3.w, l(1.000000)
+mul r17.xyz, r6.wwww, r17.xyzx
+add r5.w, -r3.w, r5.w
+div r17.xyz, r17.xyzx, r5.wwww
+add r17.xyz, r3.wwww, r17.xyzx
+movc r16.xyz, r4.wwww, r17.xyzx, r16.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r1.xyzx
+add r4.w, r2.w, -r3.w
+add r17.xyz, r1.xyzx, r4.wwww
+dp3 r4.w, l(0.300000, 0.590000, 0.110000, 0.000000), r17.xyzx
+min r5.w, r17.y, r17.x
+min r5.w, r17.z, r5.w
+max r6.w, r17.y, r17.x
+max r6.w, r17.z, r6.w
+lt r7.w, r5.w, l(0.000000)
+add r19.xyz, -r4.wwww, r17.xyzx
+mul r19.xyz, r4.wwww, r19.xyzx
+add r5.w, r4.w, -r5.w
+div r19.xyz, r19.xyzx, r5.wwww
+add r19.xyz, r4.wwww, r19.xyzx
+movc r17.xyz, r7.wwww, r19.xyzx, r17.xyzx
+lt r5.w, l(1.000000), r6.w
+add r19.xyz, -r4.wwww, r17.xyzx
+add r7.w, -r4.w, l(1.000000)
+mul r19.xyz, r7.wwww, r19.xyzx
+add r6.w, -r4.w, r6.w
+div r19.xyz, r19.xyzx, r6.wwww
+add r19.xyz, r4.wwww, r19.xyzx
+movc r17.xyz, r5.wwww, r19.xyzx, r17.xyzx
+ieq r19.xy, l(13, 14, 0, 0), cb0[2].zzzz
+add r2.w, -r2.w, r3.w
+add r0.xyz, r0.xyzx, r2.wwww
+dp3 r2.w, l(0.300000, 0.590000, 0.110000, 0.000000), r0.xyzx
+min r3.w, r0.y, r0.x
+min r3.w, r0.z, r3.w
+max r4.w, r0.y, r0.x
+max r4.w, r0.z, r4.w
+lt r5.w, r3.w, l(0.000000)
+add r20.xyz, r0.xyzx, -r2.wwww
+mul r20.xyz, r2.wwww, r20.xyzx
+add r3.w, r2.w, -r3.w
+div r20.xyz, r20.xyzx, r3.wwww
+add r20.xyz, r2.wwww, r20.xyzx
+movc r0.xyz, r5.wwww, r20.xyzx, r0.xyzx
+lt r3.w, l(1.000000), r4.w
+add r20.xyz, -r2.wwww, r0.xyzx
+add r5.w, -r2.w, l(1.000000)
+mul r20.xyz, r5.wwww, r20.xyzx
+add r4.w, -r2.w, r4.w
+div r20.xyz, r20.xyzx, r4.wwww
+add r20.xyz, r2.wwww, r20.xyzx
+movc r0.xyz, r3.wwww, r20.xyzx, r0.xyzx
+and r0.xyz, r0.xyzx, r19.yyyy
+movc r0.xyz, r19.xxxx, r17.xyzx, r0.xyzx
+movc r0.xyz, r14.wwww, r16.xyzx, r0.xyzx
+movc r0.xyz, r14.zzzz, r13.xyzx, r0.xyzx
+movc r0.xyz, r14.yyyy, r3.xyzx, r0.xyzx
+movc r0.xyz, r14.xxxx, |r10.xyzx|, r0.xyzx
+movc r0.xyz, r9.wwww, r18.xyzx, r0.xyzx
+movc r0.xyz, r9.zzzz, r6.xyzx, r0.xyzx
+movc r0.xyz, r9.yyyy, r15.xyzx, r0.xyzx
+movc r0.xyz, r9.xxxx, r12.xyzx, r0.xyzx
+movc r0.xyz, r8.wwww, r11.xyzx, r0.xyzx
+movc r0.xyz, r8.zzzz, r7.xyzx, r0.xyzx
+movc r0.xyz, r8.yyyy, r5.xyzx, r0.xyzx
+movc r0.xyz, r8.xxxx, r4.xyzx, r0.xyzx
+movc r0.xyz, cb0[2].zzzz, r0.xyzx, r2.xyzx
+add r2.x, -r0.w, l(1.000000)
+mul r0.xyz, r0.xyzx, r0.wwww
+mad r0.xyz, r2.xxxx, r1.xyzx, r0.xyzx
+mul o0.xyz, r1.wwww, r0.xyzx
+mov o0.w, r1.w
+ret
+// Approximately 333 instruction slots used
+#endif
+
+const BYTE BlendShader[] =
+{
+ 68, 88, 66, 67, 28, 114,
+ 244, 41, 206, 5, 116, 244,
+ 79, 130, 118, 154, 72, 188,
+ 36, 32, 1, 0, 0, 0,
+ 172, 66, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 16, 23, 0, 0, 208, 61,
+ 0, 0, 76, 62, 0, 0,
+ 240, 65, 0, 0, 120, 66,
+ 0, 0, 65, 111, 110, 57,
+ 208, 22, 0, 0, 208, 22,
+ 0, 0, 0, 2, 255, 255,
+ 112, 22, 0, 0, 96, 0,
+ 0, 0, 3, 0, 60, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 0, 6, 0, 36, 0,
+ 0, 0, 96, 0, 0, 0,
+ 0, 0, 1, 0, 1, 0,
+ 2, 0, 2, 0, 3, 0,
+ 3, 0, 5, 0, 4, 0,
+ 6, 0, 5, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 1, 0, 2, 0,
+ 3, 3, 3, 3, 0, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 6, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 192,
+ 18, 131, 128, 189, 115, 128,
+ 0, 191, 81, 0, 0, 5,
+ 7, 0, 15, 160, 0, 0,
+ 0, 192, 0, 0, 64, 192,
+ 0, 0, 128, 192, 0, 0,
+ 160, 192, 81, 0, 0, 5,
+ 8, 0, 15, 160, 0, 0,
+ 192, 192, 0, 0, 224, 192,
+ 0, 0, 0, 193, 0, 0,
+ 16, 193, 81, 0, 0, 5,
+ 9, 0, 15, 160, 0, 0,
+ 0, 63, 0, 0, 128, 63,
+ 0, 0, 128, 62, 0, 0,
+ 0, 192, 81, 0, 0, 5,
+ 10, 0, 15, 160, 0, 0,
+ 128, 65, 0, 0, 64, 193,
+ 0, 0, 96, 193, 0, 0,
+ 0, 0, 81, 0, 0, 5,
+ 11, 0, 15, 160, 0, 0,
+ 32, 193, 0, 0, 48, 193,
+ 0, 0, 64, 193, 0, 0,
+ 80, 193, 81, 0, 0, 5,
+ 12, 0, 15, 160, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 81, 0, 0, 5,
+ 13, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 128,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 3, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 4, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 5, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 1, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 1, 0, 1, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 2, 0, 4, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 3, 0, 8, 128, 6, 0,
+ 0, 161, 66, 0, 0, 3,
+ 4, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 66, 0, 0, 3, 5, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 2, 0,
+ 0, 3, 5, 0, 1, 128,
+ 5, 0, 0, 128, 6, 0,
+ 170, 160, 2, 0, 0, 3,
+ 5, 0, 2, 128, 4, 0,
+ 0, 128, 6, 0, 255, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 170, 176,
+ 5, 0, 0, 3, 4, 0,
+ 3, 128, 0, 0, 255, 128,
+ 1, 0, 228, 176, 66, 0,
+ 0, 3, 6, 0, 15, 128,
+ 0, 0, 228, 176, 3, 8,
+ 228, 160, 66, 0, 0, 3,
+ 4, 0, 15, 128, 4, 0,
+ 228, 128, 4, 8, 228, 160,
+ 2, 0, 0, 3, 5, 0,
+ 4, 128, 6, 0, 0, 128,
+ 6, 0, 255, 160, 8, 0,
+ 0, 3, 3, 0, 1, 128,
+ 3, 0, 228, 160, 5, 0,
+ 228, 128, 8, 0, 0, 3,
+ 3, 0, 2, 128, 4, 0,
+ 228, 160, 5, 0, 228, 128,
+ 8, 0, 0, 3, 3, 0,
+ 4, 128, 5, 0, 228, 160,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 3, 0, 15, 128,
+ 3, 0, 228, 128, 1, 0,
+ 0, 160, 5, 0, 0, 3,
+ 5, 0, 15, 128, 4, 0,
+ 0, 128, 3, 0, 228, 128,
+ 1, 0, 0, 2, 6, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 7, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 6, 0, 15, 128,
+ 6, 0, 228, 128, 5, 8,
+ 228, 160, 5, 0, 0, 3,
+ 7, 0, 15, 128, 7, 0,
+ 228, 128, 1, 0, 0, 160,
+ 5, 0, 0, 3, 8, 0,
+ 15, 128, 4, 0, 0, 128,
+ 7, 0, 228, 128, 1, 0,
+ 0, 2, 9, 0, 3, 128,
+ 6, 0, 228, 160, 2, 0,
+ 0, 3, 10, 0, 15, 128,
+ 9, 0, 4, 128, 2, 0,
+ 144, 160, 5, 0, 0, 3,
+ 10, 0, 15, 128, 10, 0,
+ 228, 128, 10, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 15, 128, 10, 0, 0, 129,
+ 8, 0, 228, 128, 5, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 10, 0,
+ 0, 129, 7, 0, 228, 128,
+ 3, 0, 228, 128, 1, 0,
+ 0, 2, 7, 0, 8, 128,
+ 1, 0, 0, 160, 5, 0,
+ 0, 3, 8, 0, 15, 128,
+ 4, 0, 0, 128, 7, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 2, 0,
+ 0, 161, 7, 0, 228, 128,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 15, 128,
+ 4, 0, 0, 128, 0, 0,
+ 228, 160, 88, 0, 0, 4,
+ 5, 0, 15, 128, 2, 0,
+ 0, 161, 8, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 7, 0, 3, 128,
+ 10, 0, 233, 129, 13, 0,
+ 0, 160, 13, 0, 85, 160,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 10, 0, 0, 129,
+ 6, 0, 0, 160, 7, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 8, 128, 2, 0,
+ 85, 161, 9, 0, 0, 128,
+ 7, 0, 85, 128, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 2, 0, 0, 161, 9, 0,
+ 0, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 15, 128, 0, 0, 255, 128,
+ 4, 0, 228, 128, 5, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 0, 0,
+ 255, 128, 0, 0, 228, 160,
+ 3, 0, 228, 128, 88, 0,
+ 0, 4, 3, 0, 15, 128,
+ 2, 0, 85, 161, 3, 0,
+ 228, 128, 4, 0, 228, 128,
+ 88, 0, 0, 4, 3, 0,
+ 15, 128, 1, 0, 255, 128,
+ 13, 0, 234, 160, 3, 0,
+ 228, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 3,
+ 4, 0, 7, 128, 0, 0,
+ 255, 128, 3, 0, 228, 128,
+ 88, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 255, 161,
+ 3, 0, 228, 128, 4, 0,
+ 228, 128, 2, 0, 0, 3,
+ 5, 0, 3, 128, 4, 0,
+ 233, 129, 4, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 12, 128, 5, 0, 0, 128,
+ 4, 0, 68, 128, 4, 0,
+ 20, 128, 11, 0, 0, 3,
+ 0, 0, 8, 128, 5, 0,
+ 170, 128, 4, 0, 170, 128,
+ 10, 0, 0, 3, 1, 0,
+ 8, 128, 4, 0, 170, 128,
+ 5, 0, 255, 128, 2, 0,
+ 0, 3, 7, 0, 8, 128,
+ 0, 0, 255, 128, 1, 0,
+ 255, 129, 6, 0, 0, 2,
+ 0, 0, 8, 128, 6, 0,
+ 255, 128, 5, 0, 0, 3,
+ 8, 0, 7, 128, 0, 0,
+ 255, 128, 6, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 12, 128, 6, 0, 100, 128,
+ 0, 0, 255, 128, 8, 0,
+ 132, 129, 5, 0, 0, 3,
+ 9, 0, 3, 128, 7, 0,
+ 255, 128, 5, 0, 238, 128,
+ 4, 0, 0, 4, 11, 0,
+ 15, 128, 6, 0, 129, 128,
+ 0, 0, 255, 128, 8, 0,
+ 88, 129, 6, 0, 0, 2,
+ 1, 0, 8, 128, 11, 0,
+ 0, 128, 5, 0, 0, 3,
+ 7, 0, 2, 128, 1, 0,
+ 255, 128, 9, 0, 0, 128,
+ 88, 0, 0, 4, 1, 0,
+ 6, 128, 11, 0, 170, 128,
+ 13, 0, 170, 160, 7, 0,
+ 220, 128, 5, 0, 0, 3,
+ 12, 0, 15, 128, 7, 0,
+ 255, 128, 11, 0, 228, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 5, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 1, 128, 1, 0, 255, 128,
+ 12, 0, 85, 128, 88, 0,
+ 0, 4, 2, 0, 3, 128,
+ 11, 0, 255, 128, 13, 0,
+ 170, 160, 7, 0, 236, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 5, 0, 170, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 6, 0, 0, 2,
+ 1, 0, 8, 128, 5, 0,
+ 170, 128, 5, 0, 0, 3,
+ 7, 0, 4, 128, 1, 0,
+ 255, 128, 12, 0, 0, 128,
+ 88, 0, 0, 4, 0, 0,
+ 6, 128, 11, 0, 85, 128,
+ 13, 0, 170, 160, 7, 0,
+ 248, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 11, 0,
+ 255, 128, 0, 0, 228, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 2, 128,
+ 13, 0, 170, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 13, 0, 170, 160, 1, 0,
+ 0, 2, 10, 0, 4, 128,
+ 13, 0, 170, 160, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 11, 0, 170, 128, 5, 0,
+ 0, 3, 7, 0, 2, 128,
+ 1, 0, 255, 128, 12, 0,
+ 255, 128, 88, 0, 0, 4,
+ 2, 0, 5, 128, 11, 0,
+ 0, 128, 13, 0, 170, 160,
+ 7, 0, 215, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 11, 0, 85, 128, 5, 0,
+ 0, 3, 7, 0, 1, 128,
+ 1, 0, 255, 128, 9, 0,
+ 85, 128, 88, 0, 0, 4,
+ 10, 0, 3, 128, 5, 0,
+ 170, 128, 13, 0, 170, 160,
+ 7, 0, 227, 128, 88, 0,
+ 0, 4, 2, 0, 7, 128,
+ 11, 0, 255, 128, 2, 0,
+ 228, 128, 10, 0, 228, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 11, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 4, 128, 1, 0, 255, 128,
+ 12, 0, 170, 128, 88, 0,
+ 0, 4, 1, 0, 5, 128,
+ 5, 0, 255, 128, 13, 0,
+ 170, 160, 7, 0, 246, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 5, 0, 170, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 11, 0,
+ 0, 128, 0, 0, 228, 128,
+ 1, 0, 228, 128, 88, 0,
+ 0, 4, 1, 0, 3, 128,
+ 11, 0, 170, 128, 8, 0,
+ 228, 128, 8, 0, 225, 128,
+ 8, 0, 0, 3, 4, 0,
+ 8, 128, 12, 0, 228, 160,
+ 0, 0, 228, 128, 8, 0,
+ 0, 3, 8, 0, 8, 128,
+ 12, 0, 228, 160, 8, 0,
+ 228, 128, 2, 0, 0, 3,
+ 4, 0, 8, 128, 4, 0,
+ 255, 129, 8, 0, 255, 128,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 3, 4, 0, 8, 128,
+ 0, 0, 85, 129, 0, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 12, 128, 4, 0,
+ 255, 128, 0, 0, 20, 128,
+ 0, 0, 68, 128, 10, 0,
+ 0, 3, 4, 0, 8, 128,
+ 0, 0, 170, 128, 1, 0,
+ 170, 128, 11, 0, 0, 3,
+ 2, 0, 1, 128, 1, 0,
+ 255, 128, 0, 0, 170, 128,
+ 8, 0, 0, 3, 1, 0,
+ 4, 128, 12, 0, 228, 160,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 4, 0, 255, 129, 1, 0,
+ 170, 128, 6, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 2, 0, 0, 3,
+ 2, 0, 14, 128, 0, 0,
+ 144, 128, 1, 0, 170, 129,
+ 5, 0, 0, 3, 2, 0,
+ 14, 128, 1, 0, 170, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 4, 2, 0, 14, 128,
+ 2, 0, 228, 128, 1, 0,
+ 255, 128, 1, 0, 170, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 4, 0, 255, 128,
+ 0, 0, 228, 128, 2, 0,
+ 249, 128, 2, 0, 0, 3,
+ 2, 0, 14, 128, 1, 0,
+ 170, 129, 0, 0, 144, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 170, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 1, 0,
+ 170, 129, 2, 0, 0, 128,
+ 2, 0, 0, 3, 4, 0,
+ 8, 128, 2, 0, 0, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 2, 0, 7, 128,
+ 2, 0, 249, 128, 1, 0,
+ 255, 128, 1, 0, 170, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 4, 0, 255, 128,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 4, 0, 8, 128, 2, 0,
+ 170, 160, 2, 0, 0, 3,
+ 1, 0, 4, 128, 4, 0,
+ 255, 128, 10, 0, 170, 160,
+ 5, 0, 0, 3, 1, 0,
+ 4, 128, 1, 0, 170, 128,
+ 1, 0, 170, 128, 8, 0,
+ 0, 3, 1, 0, 8, 128,
+ 12, 0, 228, 160, 4, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 1, 128, 8, 0,
+ 255, 129, 1, 0, 255, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 129,
+ 8, 0, 255, 128, 2, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 128, 4, 0,
+ 144, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 0, 0, 255, 128,
+ 2, 0, 0, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 7, 0, 85, 129, 7, 0,
+ 0, 128, 88, 0, 0, 4,
+ 5, 0, 12, 128, 1, 0,
+ 255, 128, 7, 0, 20, 128,
+ 7, 0, 68, 128, 10, 0,
+ 0, 3, 1, 0, 8, 128,
+ 7, 0, 170, 128, 5, 0,
+ 170, 128, 11, 0, 0, 3,
+ 2, 0, 1, 128, 5, 0,
+ 255, 128, 7, 0, 170, 128,
+ 8, 0, 0, 3, 7, 0,
+ 8, 128, 12, 0, 228, 160,
+ 7, 0, 228, 128, 2, 0,
+ 0, 3, 5, 0, 4, 128,
+ 1, 0, 255, 129, 7, 0,
+ 255, 128, 6, 0, 0, 2,
+ 5, 0, 4, 128, 5, 0,
+ 170, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 7, 0,
+ 255, 129, 7, 0, 228, 128,
+ 5, 0, 0, 3, 9, 0,
+ 7, 128, 7, 0, 255, 128,
+ 9, 0, 228, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 5, 0,
+ 170, 128, 7, 0, 255, 128,
+ 88, 0, 0, 4, 7, 0,
+ 7, 128, 1, 0, 255, 128,
+ 7, 0, 228, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 7, 0,
+ 255, 129, 7, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 7, 0, 255, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 9, 0, 7, 128,
+ 1, 0, 255, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 2, 0,
+ 0, 128, 7, 0, 255, 129,
+ 2, 0, 0, 3, 9, 0,
+ 8, 128, 2, 0, 0, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 1, 0,
+ 255, 128, 7, 0, 255, 128,
+ 88, 0, 0, 4, 7, 0,
+ 7, 128, 9, 0, 255, 128,
+ 7, 0, 228, 128, 9, 0,
+ 228, 128, 88, 0, 0, 4,
+ 7, 0, 7, 128, 1, 0,
+ 170, 129, 7, 0, 228, 128,
+ 13, 0, 170, 160, 2, 0,
+ 0, 3, 7, 0, 8, 128,
+ 2, 0, 170, 129, 2, 0,
+ 85, 128, 88, 0, 0, 4,
+ 1, 0, 12, 128, 7, 0,
+ 255, 128, 2, 0, 100, 128,
+ 2, 0, 148, 128, 10, 0,
+ 0, 3, 7, 0, 8, 128,
+ 2, 0, 255, 128, 1, 0,
+ 170, 128, 11, 0, 0, 3,
+ 5, 0, 4, 128, 1, 0,
+ 255, 128, 2, 0, 255, 128,
+ 8, 0, 0, 3, 5, 0,
+ 8, 128, 12, 0, 228, 160,
+ 2, 0, 249, 128, 2, 0,
+ 0, 3, 1, 0, 4, 128,
+ 7, 0, 255, 129, 5, 0,
+ 255, 128, 6, 0, 0, 2,
+ 1, 0, 4, 128, 1, 0,
+ 170, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 2, 0,
+ 249, 128, 5, 0, 255, 129,
+ 5, 0, 0, 3, 9, 0,
+ 7, 128, 5, 0, 255, 128,
+ 9, 0, 228, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 1, 0,
+ 170, 128, 5, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 7, 0, 255, 128,
+ 2, 0, 249, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 5, 0,
+ 255, 129, 2, 0, 228, 128,
+ 2, 0, 0, 3, 2, 0,
+ 8, 128, 5, 0, 255, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 9, 0, 7, 128,
+ 2, 0, 255, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 8, 128, 5, 0,
+ 255, 129, 5, 0, 170, 128,
+ 2, 0, 0, 3, 7, 0,
+ 8, 128, 5, 0, 170, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 2, 0,
+ 255, 128, 5, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 7, 0, 255, 128,
+ 2, 0, 228, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 15, 128, 4, 0,
+ 255, 128, 11, 0, 228, 160,
+ 5, 0, 0, 3, 9, 0,
+ 15, 128, 9, 0, 228, 128,
+ 9, 0, 228, 128, 88, 0,
+ 0, 4, 2, 0, 7, 128,
+ 9, 0, 255, 129, 2, 0,
+ 228, 128, 7, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 9, 0, 170, 129,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 15, 128, 4, 0,
+ 96, 129, 4, 0, 137, 128,
+ 1, 0, 0, 2, 7, 0,
+ 2, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 10, 0,
+ 2, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 11, 0,
+ 4, 128, 13, 0, 170, 160,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 2, 0, 170, 128,
+ 11, 0, 0, 3, 11, 0,
+ 8, 128, 1, 0, 0, 128,
+ 8, 0, 170, 128, 10, 0,
+ 0, 3, 5, 0, 4, 128,
+ 8, 0, 170, 128, 1, 0,
+ 85, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 5, 0,
+ 170, 129, 11, 0, 255, 128,
+ 5, 0, 0, 3, 5, 0,
+ 12, 128, 1, 0, 255, 128,
+ 5, 0, 68, 128, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 7, 0, 255, 128, 5, 0,
+ 255, 128, 88, 0, 0, 4,
+ 11, 0, 3, 128, 2, 0,
+ 85, 128, 13, 0, 170, 160,
+ 1, 0, 227, 128, 6, 0,
+ 0, 2, 5, 0, 8, 128,
+ 5, 0, 0, 128, 5, 0,
+ 0, 3, 12, 0, 15, 128,
+ 1, 0, 255, 128, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 2, 128, 5, 0,
+ 255, 128, 12, 0, 255, 128,
+ 88, 0, 0, 4, 10, 0,
+ 5, 128, 2, 0, 0, 128,
+ 13, 0, 170, 160, 1, 0,
+ 215, 128, 88, 0, 0, 4,
+ 10, 0, 7, 128, 2, 0,
+ 255, 128, 10, 0, 228, 128,
+ 11, 0, 228, 128, 6, 0,
+ 0, 2, 5, 0, 8, 128,
+ 2, 0, 255, 128, 5, 0,
+ 0, 3, 1, 0, 4, 128,
+ 5, 0, 255, 128, 5, 0,
+ 170, 128, 88, 0, 0, 4,
+ 7, 0, 5, 128, 5, 0,
+ 85, 128, 13, 0, 170, 160,
+ 1, 0, 246, 128, 88, 0,
+ 0, 4, 7, 0, 7, 128,
+ 2, 0, 85, 128, 7, 0,
+ 228, 128, 10, 0, 228, 128,
+ 1, 0, 0, 2, 10, 0,
+ 1, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 11, 0,
+ 1, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 13, 0,
+ 4, 128, 13, 0, 170, 160,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 2, 0, 0, 128,
+ 5, 0, 0, 3, 1, 0,
+ 2, 128, 7, 0, 255, 128,
+ 12, 0, 85, 128, 88, 0,
+ 0, 4, 11, 0, 6, 128,
+ 5, 0, 0, 128, 13, 0,
+ 170, 160, 1, 0, 220, 128,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 5, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 7, 0, 255, 128,
+ 12, 0, 170, 128, 88, 0,
+ 0, 4, 13, 0, 3, 128,
+ 2, 0, 255, 128, 13, 0,
+ 170, 160, 1, 0, 236, 128,
+ 88, 0, 0, 4, 5, 0,
+ 7, 128, 2, 0, 85, 128,
+ 11, 0, 228, 128, 13, 0,
+ 228, 128, 6, 0, 0, 2,
+ 5, 0, 8, 128, 2, 0,
+ 85, 128, 5, 0, 0, 3,
+ 1, 0, 4, 128, 5, 0,
+ 255, 128, 12, 0, 0, 128,
+ 88, 0, 0, 4, 10, 0,
+ 6, 128, 2, 0, 170, 128,
+ 13, 0, 170, 160, 1, 0,
+ 248, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 2, 0,
+ 255, 128, 10, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 1, 0, 7, 128,
+ 2, 0, 0, 128, 1, 0,
+ 228, 128, 7, 0, 228, 128,
+ 8, 0, 0, 3, 1, 0,
+ 8, 128, 12, 0, 228, 160,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 129, 8, 0,
+ 255, 128, 2, 0, 0, 3,
+ 1, 0, 7, 128, 1, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 85, 129,
+ 1, 0, 0, 128, 88, 0,
+ 0, 4, 2, 0, 3, 128,
+ 1, 0, 255, 128, 1, 0,
+ 225, 128, 1, 0, 228, 128,
+ 10, 0, 0, 3, 8, 0,
+ 8, 128, 1, 0, 170, 128,
+ 2, 0, 0, 128, 11, 0,
+ 0, 3, 5, 0, 1, 128,
+ 2, 0, 85, 128, 1, 0,
+ 170, 128, 8, 0, 0, 3,
+ 1, 0, 8, 128, 12, 0,
+ 228, 160, 1, 0, 228, 128,
+ 2, 0, 0, 3, 2, 0,
+ 1, 128, 8, 0, 255, 129,
+ 1, 0, 255, 128, 6, 0,
+ 0, 2, 2, 0, 1, 128,
+ 2, 0, 0, 128, 2, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 129, 1, 0,
+ 144, 128, 5, 0, 0, 3,
+ 2, 0, 14, 128, 1, 0,
+ 255, 128, 2, 0, 228, 128,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 2, 0, 249, 128,
+ 2, 0, 0, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 2, 0, 7, 128,
+ 1, 0, 255, 129, 1, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 8, 128, 1, 0,
+ 255, 129, 6, 0, 0, 161,
+ 5, 0, 0, 3, 2, 0,
+ 7, 128, 2, 0, 255, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 129, 5, 0,
+ 0, 128, 2, 0, 0, 3,
+ 8, 0, 8, 128, 5, 0,
+ 0, 129, 6, 0, 0, 161,
+ 6, 0, 0, 2, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 2, 0, 228, 128,
+ 2, 0, 255, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 9, 0, 85, 129, 1, 0,
+ 228, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 2, 0, 7, 128, 4, 0,
+ 228, 128, 8, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 2, 0, 228, 128,
+ 6, 0, 85, 160, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 228, 128, 4, 0, 228, 129,
+ 1, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 9, 0, 0, 129, 5, 0,
+ 228, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 4, 0,
+ 228, 129, 35, 0, 0, 2,
+ 5, 0, 7, 128, 5, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 15, 128, 4, 0,
+ 255, 128, 8, 0, 228, 160,
+ 5, 0, 0, 3, 7, 0,
+ 15, 128, 7, 0, 228, 128,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 255, 129, 5, 0,
+ 228, 128, 0, 0, 228, 128,
+ 2, 0, 0, 3, 5, 0,
+ 3, 128, 4, 0, 233, 129,
+ 9, 0, 0, 160, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 4, 0, 228, 128, 9, 0,
+ 255, 161, 9, 0, 85, 161,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 170, 128,
+ 0, 0, 255, 129, 9, 0,
+ 170, 160, 4, 0, 0, 4,
+ 10, 0, 7, 128, 8, 0,
+ 228, 128, 10, 0, 0, 160,
+ 10, 0, 85, 160, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 10, 0, 228, 128, 8, 0,
+ 228, 128, 7, 0, 170, 161,
+ 5, 0, 0, 3, 10, 0,
+ 7, 128, 8, 0, 228, 128,
+ 10, 0, 228, 128, 7, 0,
+ 0, 2, 2, 0, 8, 128,
+ 8, 0, 170, 128, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 1, 0, 255, 128, 10, 0,
+ 170, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 170, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 170, 128, 1, 0, 255, 128,
+ 8, 0, 170, 128, 4, 0,
+ 0, 4, 11, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 85, 160, 6, 0, 0, 161,
+ 5, 0, 0, 3, 11, 0,
+ 7, 128, 8, 0, 228, 128,
+ 11, 0, 228, 128, 4, 0,
+ 0, 4, 12, 0, 15, 128,
+ 6, 0, 73, 128, 0, 0,
+ 255, 129, 9, 0, 165, 160,
+ 4, 0, 0, 4, 5, 0,
+ 12, 128, 11, 0, 148, 128,
+ 12, 0, 68, 129, 8, 0,
+ 148, 128, 88, 0, 0, 4,
+ 13, 0, 4, 128, 5, 0,
+ 85, 128, 5, 0, 255, 128,
+ 1, 0, 255, 128, 7, 0,
+ 0, 2, 1, 0, 8, 128,
+ 8, 0, 85, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 12, 0, 255, 128, 10, 0,
+ 85, 128, 1, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 85, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 85, 128, 1, 0, 255, 128,
+ 8, 0, 85, 128, 88, 0,
+ 0, 4, 13, 0, 2, 128,
+ 5, 0, 0, 128, 5, 0,
+ 170, 128, 1, 0, 255, 128,
+ 2, 0, 0, 3, 14, 0,
+ 15, 128, 4, 0, 36, 129,
+ 9, 0, 21, 160, 7, 0,
+ 0, 2, 1, 0, 8, 128,
+ 8, 0, 0, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 12, 0, 170, 128, 10, 0,
+ 0, 128, 1, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 0, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 0, 128, 1, 0, 255, 128,
+ 8, 0, 0, 128, 4, 0,
+ 0, 4, 9, 0, 15, 128,
+ 6, 0, 36, 128, 0, 0,
+ 255, 129, 9, 0, 64, 160,
+ 4, 0, 0, 4, 6, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 6, 0,
+ 0, 160, 5, 0, 0, 3,
+ 6, 0, 7, 128, 6, 0,
+ 228, 128, 6, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 8, 128, 11, 0, 0, 128,
+ 9, 0, 255, 129, 8, 0,
+ 0, 128, 88, 0, 0, 4,
+ 13, 0, 1, 128, 14, 0,
+ 255, 128, 0, 0, 255, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 170, 129, 13, 0,
+ 228, 128, 0, 0, 228, 128,
+ 2, 0, 0, 3, 10, 0,
+ 7, 128, 8, 0, 228, 128,
+ 8, 0, 228, 128, 4, 0,
+ 0, 4, 11, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 85, 161, 10, 0, 228, 128,
+ 2, 0, 0, 3, 11, 0,
+ 7, 128, 11, 0, 228, 128,
+ 6, 0, 0, 160, 4, 0,
+ 0, 4, 13, 0, 7, 128,
+ 4, 0, 228, 128, 10, 0,
+ 228, 129, 11, 0, 228, 128,
+ 5, 0, 0, 3, 10, 0,
+ 7, 128, 4, 0, 228, 128,
+ 10, 0, 228, 128, 2, 0,
+ 0, 3, 15, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 16, 0, 7, 128, 8, 0,
+ 228, 128, 15, 0, 228, 128,
+ 4, 0, 0, 4, 11, 0,
+ 7, 128, 15, 0, 228, 128,
+ 8, 0, 228, 129, 11, 0,
+ 228, 128, 88, 0, 0, 4,
+ 9, 0, 7, 128, 9, 0,
+ 228, 128, 10, 0, 228, 128,
+ 11, 0, 228, 128, 88, 0,
+ 0, 4, 5, 0, 6, 128,
+ 5, 0, 208, 128, 16, 0,
+ 228, 128, 13, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 1, 128, 14, 0, 255, 128,
+ 16, 0, 0, 128, 13, 0,
+ 0, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 85, 129, 5, 0, 228, 128,
+ 0, 0, 228, 128, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 4, 0, 0, 128, 4, 0,
+ 0, 4, 0, 0, 8, 128,
+ 9, 0, 255, 128, 0, 0,
+ 255, 129, 6, 0, 0, 161,
+ 11, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 13, 0, 170, 160, 5, 0,
+ 0, 3, 5, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 8, 128, 5, 0,
+ 0, 129, 13, 0, 170, 160,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 1, 128,
+ 6, 0, 0, 129, 6, 0,
+ 0, 161, 0, 0, 255, 128,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 85, 128,
+ 4, 0, 0, 4, 0, 0,
+ 8, 128, 12, 0, 0, 128,
+ 0, 0, 255, 129, 6, 0,
+ 0, 161, 11, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 255, 128, 13, 0, 170, 160,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 5, 0, 85, 129,
+ 13, 0, 170, 160, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 10, 0, 2, 128, 6, 0,
+ 85, 129, 6, 0, 0, 161,
+ 0, 0, 255, 128, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 4, 0, 170, 128, 4, 0,
+ 0, 4, 0, 0, 8, 128,
+ 12, 0, 85, 128, 0, 0,
+ 255, 129, 6, 0, 0, 161,
+ 11, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 13, 0, 170, 160, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 5, 0, 170, 129, 13, 0,
+ 170, 160, 1, 0, 255, 128,
+ 88, 0, 0, 4, 10, 0,
+ 4, 128, 6, 0, 170, 129,
+ 6, 0, 0, 161, 0, 0,
+ 255, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 0, 129, 10, 0, 228, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 5, 0, 15, 128,
+ 4, 0, 255, 128, 7, 0,
+ 228, 160, 5, 0, 0, 3,
+ 5, 0, 15, 128, 5, 0,
+ 228, 128, 5, 0, 228, 128,
+ 2, 0, 0, 3, 6, 0,
+ 7, 128, 4, 0, 228, 128,
+ 6, 0, 0, 160, 5, 0,
+ 0, 3, 6, 0, 7, 128,
+ 6, 0, 228, 128, 6, 0,
+ 228, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 14, 0,
+ 0, 128, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 8, 0, 0, 128,
+ 10, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 6, 0, 0, 161, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 6, 0, 0, 129, 6, 0,
+ 0, 161, 1, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 7, 128, 8, 0, 228, 128,
+ 8, 0, 228, 128, 88, 0,
+ 0, 4, 10, 0, 1, 128,
+ 7, 0, 0, 129, 13, 0,
+ 170, 160, 0, 0, 255, 128,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 14, 0, 85, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 14, 0, 170, 128,
+ 5, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 8, 0, 170, 128, 10, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 6, 0,
+ 0, 161, 88, 0, 0, 4,
+ 1, 0, 8, 128, 6, 0,
+ 170, 129, 6, 0, 0, 161,
+ 2, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 4, 128,
+ 7, 0, 170, 129, 13, 0,
+ 170, 160, 1, 0, 255, 128,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 8, 0, 85, 128, 10, 0,
+ 0, 3, 1, 0, 8, 128,
+ 0, 0, 255, 128, 6, 0,
+ 0, 161, 88, 0, 0, 4,
+ 0, 0, 8, 128, 6, 0,
+ 85, 129, 6, 0, 0, 161,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 2, 128,
+ 7, 0, 85, 129, 13, 0,
+ 170, 160, 0, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 5, 0, 255, 129,
+ 10, 0, 228, 128, 0, 0,
+ 228, 128, 11, 0, 0, 3,
+ 6, 0, 7, 128, 8, 0,
+ 228, 128, 4, 0, 228, 128,
+ 10, 0, 0, 3, 7, 0,
+ 7, 128, 4, 0, 228, 128,
+ 8, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 5, 0, 170, 129, 6, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 5, 0, 85, 129,
+ 7, 0, 228, 128, 0, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 5, 0,
+ 0, 129, 9, 0, 228, 128,
+ 0, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 10, 0, 255, 129, 1, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 2, 0, 170, 161,
+ 2, 0, 228, 128, 0, 0,
+ 228, 128, 18, 0, 0, 4,
+ 1, 0, 7, 128, 6, 0,
+ 255, 128, 0, 0, 228, 128,
+ 4, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 6, 0, 255, 128, 6, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 3, 0,
+ 255, 128, 1, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 3, 0, 255, 128,
+ 3, 0, 255, 128, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 3, 0, 255, 128, 88, 0,
+ 0, 4, 0, 0, 15, 128,
+ 1, 0, 0, 129, 13, 0,
+ 170, 160, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 15, 128, 1, 0, 255, 129,
+ 3, 0, 228, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 184, 38,
+ 0, 0, 64, 0, 0, 0,
+ 174, 9, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 2, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 3, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 5, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 6, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 194, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 22, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 230, 26,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 6, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 4, 26, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 31, 0, 0, 4,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 54, 0, 0, 6,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 54, 0, 0, 5, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 255, 255,
+ 255, 255, 18, 0, 0, 1,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 3, 0,
+ 0, 0, 10, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 18, 131, 128, 189,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 2, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 3, 0, 0, 0,
+ 10, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 4, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 3, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 66, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 16, 0, 0, 8,
+ 18, 0, 16, 0, 4, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 8, 34, 0, 16, 0,
+ 4, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 21, 0, 0, 1, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 55, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 255, 255, 255, 255,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 18, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 1, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 31, 0, 0, 4, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 14, 0, 0, 7, 98, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 17, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 4, 0, 0, 0, 150, 5,
+ 16, 0, 2, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 6, 130, 0, 16, 0,
+ 3, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 6, 0, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 66, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 42, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 194, 0, 16, 0,
+ 2, 0, 0, 0, 6, 20,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 14, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 194, 0, 16, 0,
+ 2, 0, 0, 0, 6, 20,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 4, 0,
+ 0, 0, 10, 0, 16, 0,
+ 4, 0, 0, 0, 1, 64,
+ 0, 0, 18, 131, 128, 189,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 2, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 4, 0, 0, 0,
+ 10, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 5, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 3, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 66, 0, 16, 0,
+ 4, 0, 0, 0, 10, 0,
+ 16, 0, 5, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 16, 0, 0, 8,
+ 18, 0, 16, 0, 5, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 8, 34, 0, 16, 0,
+ 5, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 4, 0, 0, 0, 70, 14,
+ 16, 0, 5, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 3, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 21, 0, 0, 1, 31, 0,
+ 0, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 98, 0, 16, 0,
+ 2, 0, 0, 0, 6, 17,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 150, 5, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 21, 0, 0, 1, 55, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 24, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 21, 0, 0, 1,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 10, 0, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 21, 0,
+ 0, 1, 14, 0, 0, 7,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 10, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 143,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 29, 0, 0, 10,
+ 242, 0, 16, 0, 5, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 128, 62, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 114, 0,
+ 16, 0, 9, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 6, 0, 0, 0,
+ 0, 0, 0, 10, 114, 0,
+ 16, 0, 9, 0, 0, 0,
+ 70, 2, 16, 0, 9, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 8, 0,
+ 0, 0, 50, 0, 0, 10,
+ 114, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 9, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 8, 0, 0, 0,
+ 51, 0, 0, 7, 114, 0,
+ 16, 0, 7, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 32, 0,
+ 0, 11, 242, 0, 16, 0,
+ 8, 0, 0, 0, 2, 64,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 3, 0,
+ 0, 0, 4, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 52, 0, 0, 7, 114, 0,
+ 16, 0, 11, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 24, 0,
+ 0, 10, 242, 0, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 24, 0, 0, 10,
+ 242, 0, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 51, 0, 0, 10, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 12, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 51, 0,
+ 0, 10, 114, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 11,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 13, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 9, 18, 0, 16, 0,
+ 15, 0, 0, 0, 58, 0,
+ 16, 0, 12, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 2, 0, 0, 0, 24, 0,
+ 0, 10, 146, 0, 16, 0,
+ 14, 0, 0, 0, 86, 9,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 24, 0, 0, 10,
+ 50, 0, 16, 0, 16, 0,
+ 0, 0, 150, 5, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 12, 98, 0,
+ 16, 0, 14, 0, 0, 0,
+ 6, 1, 16, 0, 16, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 86, 6,
+ 16, 0, 14, 0, 0, 0,
+ 55, 0, 0, 12, 98, 0,
+ 16, 0, 15, 0, 0, 0,
+ 6, 3, 16, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 86, 6,
+ 16, 0, 14, 0, 0, 0,
+ 29, 0, 0, 10, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 9, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 32, 0, 0, 11,
+ 242, 0, 16, 0, 9, 0,
+ 0, 0, 2, 64, 0, 0,
+ 5, 0, 0, 0, 6, 0,
+ 0, 0, 7, 0, 0, 0,
+ 8, 0, 0, 0, 166, 138,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 16, 114, 0, 16, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 10, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 10, 0, 0, 0,
+ 50, 0, 0, 10, 114, 0,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 65, 0, 0, 128, 65,
+ 0, 0, 128, 65, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 64, 193, 0, 0,
+ 64, 193, 0, 0, 64, 193,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 64, 0, 0,
+ 128, 64, 0, 0, 128, 64,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 75, 0, 0, 5,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 10, 0, 16, 0, 16, 0,
+ 0, 0, 10, 0, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 13, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 18, 0,
+ 16, 0, 18, 0, 0, 0,
+ 10, 0, 16, 0, 14, 0,
+ 0, 0, 10, 0, 16, 0,
+ 10, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 29, 0, 0, 10, 146, 0,
+ 16, 0, 10, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 62, 86, 9, 16, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 146, 0, 16, 0,
+ 10, 0, 0, 0, 6, 12,
+ 16, 0, 10, 0, 0, 0,
+ 86, 9, 16, 0, 16, 0,
+ 0, 0, 86, 9, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 146, 0, 16, 0,
+ 10, 0, 0, 0, 86, 9,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 6, 12,
+ 16, 0, 10, 0, 0, 0,
+ 50, 0, 0, 9, 146, 0,
+ 16, 0, 10, 0, 0, 0,
+ 86, 9, 16, 0, 13, 0,
+ 0, 0, 6, 12, 16, 0,
+ 10, 0, 0, 0, 86, 9,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 98, 0,
+ 16, 0, 18, 0, 0, 0,
+ 86, 6, 16, 0, 14, 0,
+ 0, 0, 86, 6, 16, 0,
+ 10, 0, 0, 0, 6, 3,
+ 16, 0, 10, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 13,
+ 114, 0, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 2, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 13, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 29, 0, 0, 7, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 49, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 6, 2,
+ 16, 0, 1, 0, 0, 0,
+ 102, 9, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 16, 0,
+ 0, 0, 6, 10, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 150, 4, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 22, 7,
+ 16, 0, 16, 0, 0, 0,
+ 1, 0, 0, 7, 98, 0,
+ 16, 0, 16, 0, 0, 0,
+ 6, 3, 16, 0, 13, 0,
+ 0, 0, 6, 0, 16, 0,
+ 14, 0, 0, 0, 29, 0,
+ 0, 7, 146, 0, 16, 0,
+ 14, 0, 0, 0, 166, 10,
+ 16, 0, 1, 0, 0, 0,
+ 86, 1, 16, 0, 1, 0,
+ 0, 0, 1, 0, 0, 7,
+ 98, 0, 16, 0, 17, 0,
+ 0, 0, 246, 13, 16, 0,
+ 13, 0, 0, 0, 86, 5,
+ 16, 0, 14, 0, 0, 0,
+ 1, 0, 0, 7, 50, 0,
+ 16, 0, 19, 0, 0, 0,
+ 230, 10, 16, 0, 13, 0,
+ 0, 0, 166, 10, 16, 0,
+ 14, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 17, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 19, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 226, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 14, 0, 0, 0, 6, 9,
+ 16, 0, 17, 0, 0, 0,
+ 6, 9, 16, 0, 19, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 16, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 14, 0, 0, 0, 6, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 150, 7, 16, 0,
+ 14, 0, 0, 0, 18, 0,
+ 0, 1, 49, 0, 0, 7,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 86, 6, 16, 0,
+ 1, 0, 0, 0, 38, 8,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 17, 0, 0, 0,
+ 86, 10, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 134, 1, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 22, 7, 16, 0,
+ 17, 0, 0, 0, 1, 0,
+ 0, 7, 82, 0, 16, 0,
+ 17, 0, 0, 0, 6, 3,
+ 16, 0, 13, 0, 0, 0,
+ 6, 0, 16, 0, 16, 0,
+ 0, 0, 29, 0, 0, 7,
+ 146, 0, 16, 0, 16, 0,
+ 0, 0, 166, 10, 16, 0,
+ 1, 0, 0, 0, 6, 4,
+ 16, 0, 1, 0, 0, 0,
+ 1, 0, 0, 7, 82, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 13, 16, 0, 13, 0,
+ 0, 0, 86, 5, 16, 0,
+ 16, 0, 0, 0, 1, 0,
+ 0, 7, 50, 0, 16, 0,
+ 13, 0, 0, 0, 182, 15,
+ 16, 0, 13, 0, 0, 0,
+ 166, 10, 16, 0, 16, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 19, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 13, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 246, 15, 16, 0, 16, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 17, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 6, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 21, 0, 0, 1,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 13, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 16, 0, 0, 10,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 2, 64, 0, 0,
+ 154, 153, 153, 62, 61, 10,
+ 23, 63, 174, 71, 225, 61,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 26, 0, 16, 0, 13, 0,
+ 0, 0, 10, 0, 16, 0,
+ 13, 0, 0, 0, 51, 0,
+ 0, 7, 130, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 13, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 26, 0, 16, 0,
+ 13, 0, 0, 0, 10, 0,
+ 16, 0, 13, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 13, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 49, 0,
+ 0, 7, 130, 0, 16, 0,
+ 6, 0, 0, 0, 58, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 4, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 246, 15, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 6, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 58, 0, 16, 0, 5, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 0, 5, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 13, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 32, 0,
+ 0, 11, 242, 0, 16, 0,
+ 14, 0, 0, 0, 2, 64,
+ 0, 0, 9, 0, 0, 0,
+ 10, 0, 0, 0, 11, 0,
+ 0, 0, 12, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 3, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 3, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 16, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 29, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 3, 0, 0, 0, 49, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 6, 2,
+ 16, 0, 0, 0, 0, 0,
+ 102, 9, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 19, 0,
+ 0, 0, 6, 10, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 150, 4, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 22, 7,
+ 16, 0, 19, 0, 0, 0,
+ 1, 0, 0, 7, 98, 0,
+ 16, 0, 19, 0, 0, 0,
+ 6, 3, 16, 0, 16, 0,
+ 0, 0, 6, 0, 16, 0,
+ 17, 0, 0, 0, 29, 0,
+ 0, 7, 146, 0, 16, 0,
+ 17, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 86, 1, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 7,
+ 98, 0, 16, 0, 20, 0,
+ 0, 0, 246, 13, 16, 0,
+ 16, 0, 0, 0, 86, 5,
+ 16, 0, 17, 0, 0, 0,
+ 1, 0, 0, 7, 50, 0,
+ 16, 0, 21, 0, 0, 0,
+ 230, 10, 16, 0, 16, 0,
+ 0, 0, 166, 10, 16, 0,
+ 17, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 20, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 21, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 226, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 17, 0, 0, 0, 6, 9,
+ 16, 0, 20, 0, 0, 0,
+ 6, 9, 16, 0, 21, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 19, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 17, 0, 0, 0, 6, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 150, 7, 16, 0,
+ 17, 0, 0, 0, 18, 0,
+ 0, 1, 49, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 86, 6, 16, 0,
+ 0, 0, 0, 0, 38, 8,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 20, 0, 0, 0,
+ 86, 10, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 134, 1, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 21, 0,
+ 0, 0, 246, 15, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 21, 0,
+ 0, 0, 22, 7, 16, 0,
+ 20, 0, 0, 0, 1, 0,
+ 0, 7, 82, 0, 16, 0,
+ 20, 0, 0, 0, 6, 3,
+ 16, 0, 16, 0, 0, 0,
+ 6, 0, 16, 0, 19, 0,
+ 0, 0, 29, 0, 0, 7,
+ 146, 0, 16, 0, 19, 0,
+ 0, 0, 166, 10, 16, 0,
+ 0, 0, 0, 0, 6, 4,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 7, 82, 0,
+ 16, 0, 21, 0, 0, 0,
+ 246, 13, 16, 0, 16, 0,
+ 0, 0, 86, 5, 16, 0,
+ 19, 0, 0, 0, 1, 0,
+ 0, 7, 50, 0, 16, 0,
+ 16, 0, 0, 0, 182, 15,
+ 16, 0, 16, 0, 0, 0,
+ 166, 10, 16, 0, 19, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 21, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 16, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 19, 0,
+ 0, 0, 70, 2, 16, 0,
+ 21, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 20, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 6, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 21, 0, 0, 1,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 3, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 16, 0, 0, 0, 10, 0,
+ 16, 0, 16, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 16, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 5, 0, 0, 0, 26, 0,
+ 16, 0, 16, 0, 0, 0,
+ 10, 0, 16, 0, 16, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 42, 0, 16, 0,
+ 16, 0, 0, 0, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 4, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 246, 15, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 49, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 4, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 26, 0, 16, 0,
+ 17, 0, 0, 0, 10, 0,
+ 16, 0, 17, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 17, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 6, 0, 0, 0, 26, 0,
+ 16, 0, 17, 0, 0, 0,
+ 10, 0, 16, 0, 17, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 42, 0, 16, 0,
+ 17, 0, 0, 0, 58, 0,
+ 16, 0, 6, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 5, 0, 0, 0, 58, 0,
+ 16, 0, 4, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 49, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 58, 0,
+ 16, 0, 6, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 7, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 0, 7, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 4, 0,
+ 0, 0, 58, 0, 16, 0,
+ 6, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 0, 6, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 32, 0, 0, 11, 50, 0,
+ 16, 0, 19, 0, 0, 0,
+ 2, 64, 0, 0, 13, 0,
+ 0, 0, 14, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 10,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 154, 153, 153, 62, 61, 10,
+ 23, 63, 174, 71, 225, 61,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 51, 0,
+ 0, 7, 130, 0, 16, 0,
+ 3, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 3, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 49, 0,
+ 0, 7, 130, 0, 16, 0,
+ 5, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 20, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 246, 15, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 20, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 19, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 166, 10, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 10,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 128, 129, 0, 0, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 18, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 10, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 15, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 11, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 10, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 10, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 8, 18, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 9, 114, 0, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 77, 1,
+ 0, 0, 22, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 191, 0, 0, 0,
+ 9, 0, 0, 0, 13, 0,
+ 0, 0, 13, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 0,
+ 45, 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,
+ 82, 68, 69, 70, 156, 3,
+ 0, 0, 1, 0, 0, 0,
+ 80, 1, 0, 0, 8, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 116, 3, 0, 0,
+ 28, 1, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 37, 1, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 42, 1,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 45, 1, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 2, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 49, 1, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 53, 1,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 59, 1, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 6, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 69, 1, 0, 0,
+ 0, 0, 0, 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, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 82, 71, 66, 0,
+ 116, 89, 0, 116, 67, 98,
+ 0, 116, 67, 114, 0, 116,
+ 77, 97, 115, 107, 0, 116,
+ 66, 97, 99, 107, 100, 114,
+ 111, 112, 0, 36, 71, 108,
+ 111, 98, 97, 108, 115, 0,
+ 171, 171, 69, 1, 0, 0,
+ 11, 0, 0, 0, 104, 1,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 112, 2, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 124, 2, 0, 0, 0, 0,
+ 0, 0, 140, 2, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 156, 2, 0, 0, 0, 0,
+ 0, 0, 172, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 188, 2, 0, 0, 0, 0,
+ 0, 0, 204, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 220, 2, 0, 0, 0, 0,
+ 0, 0, 236, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 12, 3, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 24, 3, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 0, 0,
+ 0, 0, 44, 3, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 76, 3, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 87, 3, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 97, 3, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 128, 0, 0, 0, 4, 0,
+ 0, 0, 8, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 116, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 12, 0, 0,
+ 116, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sBlendShader = { BlendShader, sizeof(BlendShader) };
diff --git a/system/graphics/layers/d3d11/ReadbackManagerD3D11.cpp b/system/graphics/layers/d3d11/ReadbackManagerD3D11.cpp
new file mode 100644
index 000000000..88d75869d
--- /dev/null
+++ b/system/graphics/layers/d3d11/ReadbackManagerD3D11.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ReadbackManagerD3D11.h"
+#include "ReadbackProcessor.h"
+#include "ReadbackLayer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/gfx/2D.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+// Structure that contains the information required to execute a readback task,
+// the only member accessed off the main thread here is mReadbackTexture. Since
+// mSink may be released only on the main thread this object should always be
+// destroyed on the main thread!
+struct ReadbackTask {
+ // The texture that we copied the contents of the paintedlayer to.
+ RefPtr<ID3D10Texture2D> mReadbackTexture;
+ // The sink that we're trying to read back to.
+ RefPtr<TextureReadbackSink> mSink;
+};
+
+// This class is created and dispatched from the Readback thread but it must be
+// destroyed by the main thread.
+class ReadbackResultWriterD3D11 final : public nsIRunnable
+{
+ ~ReadbackResultWriterD3D11() {}
+ NS_DECL_THREADSAFE_ISUPPORTS
+public:
+ ReadbackResultWriterD3D11(ReadbackTask *aTask) : mTask(aTask) {}
+
+ NS_IMETHOD Run() override
+ {
+ D3D10_TEXTURE2D_DESC desc;
+ mTask->mReadbackTexture->GetDesc(&desc);
+
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ // Unless there is an error this map should succeed immediately, as we've
+ // recently mapped (and unmapped) this copied data on our task thread.
+ HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+
+ if (FAILED(hr)) {
+ mTask->mSink->ProcessReadback(nullptr);
+ return NS_OK;
+ }
+
+ {
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)mappedTex.pData, mappedTex.RowPitch,
+ IntSize(desc.Width, desc.Height),
+ SurfaceFormat::B8G8R8A8);
+
+ mTask->mSink->ProcessReadback(surf);
+
+ MOZ_ASSERT(surf->hasOneRef());
+ }
+
+ mTask->mReadbackTexture->Unmap(0);
+
+ return NS_OK;
+ }
+
+private:
+ nsAutoPtr<ReadbackTask> mTask;
+};
+
+NS_IMPL_ISUPPORTS(ReadbackResultWriterD3D11, nsIRunnable)
+
+DWORD WINAPI ReadbackManagerD3D11::StartTaskThread(void *aManager)
+{
+ static_cast<ReadbackManagerD3D11*>(aManager)->ProcessTasks();
+
+ return 0;
+}
+
+ReadbackManagerD3D11::ReadbackManagerD3D11()
+ : mRefCnt(0)
+{
+ ::InitializeCriticalSection(&mTaskMutex);
+ mShutdownEvent = ::CreateEventA(nullptr, FALSE, FALSE, nullptr);
+ mTaskSemaphore = ::CreateSemaphoreA(nullptr, 0, 1000000, nullptr);
+ mTaskThread = ::CreateThread(nullptr, 0, StartTaskThread, this, 0, 0);
+}
+
+ReadbackManagerD3D11::~ReadbackManagerD3D11()
+{
+ ::SetEvent(mShutdownEvent);
+
+ // This shouldn't take longer than 5 seconds, if it does we're going to choose
+ // to leak the thread and its synchronisation in favor of crashing or freezing
+ DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
+ if (result != WAIT_TIMEOUT) {
+ ::DeleteCriticalSection(&mTaskMutex);
+ ::CloseHandle(mShutdownEvent);
+ ::CloseHandle(mTaskSemaphore);
+ ::CloseHandle(mTaskThread);
+ } else {
+ NS_RUNTIMEABORT("ReadbackManager: Task thread did not shutdown in 5 seconds.");
+ }
+}
+
+void
+ReadbackManagerD3D11::PostTask(ID3D10Texture2D *aTexture, TextureReadbackSink* aSink)
+{
+ ReadbackTask *task = new ReadbackTask;
+ task->mReadbackTexture = aTexture;
+ task->mSink = aSink;
+
+ ::EnterCriticalSection(&mTaskMutex);
+ mPendingReadbackTasks.AppendElement(task);
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ ::ReleaseSemaphore(mTaskSemaphore, 1, nullptr);
+}
+
+void
+ReadbackManagerD3D11::ProcessTasks()
+{
+ HANDLE handles[] = { mTaskSemaphore, mShutdownEvent };
+
+ while (true) {
+ DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (result != WAIT_OBJECT_0) {
+ return;
+ }
+
+ ::EnterCriticalSection(&mTaskMutex);
+ if (mPendingReadbackTasks.Length() == 0) {
+ NS_RUNTIMEABORT("Trying to read from an empty array, bad bad bad");
+ }
+ ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
+ mPendingReadbackTasks.RemoveElementAt(0);
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ // We want to block here until the texture contents are available, the
+ // easiest thing is to simply map and unmap.
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+ nextReadbackTask->mReadbackTexture->Unmap(0);
+
+ // We can only send the update to the sink on the main thread, so post an
+ // event there to do so. Ownership of the task is passed from
+ // mPendingReadbackTasks to ReadbackResultWriter here.
+ nsCOMPtr<nsIThread> thread = do_GetMainThread();
+ thread->Dispatch(new ReadbackResultWriterD3D11(nextReadbackTask),
+ nsIEventTarget::DISPATCH_NORMAL);
+ }
+}
+
+}
+}
diff --git a/system/graphics/layers/d3d11/ReadbackManagerD3D11.h b/system/graphics/layers/d3d11/ReadbackManagerD3D11.h
new file mode 100644
index 000000000..d15346fd8
--- /dev/null
+++ b/system/graphics/layers/d3d11/ReadbackManagerD3D11.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKMANAGERD3D11_H
+#define GFX_READBACKMANAGERD3D11_H
+
+#include <windows.h>
+#include <d3d10_1.h>
+
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureReadbackSink;
+struct ReadbackTask;
+
+class ReadbackManagerD3D11 final
+{
+ NS_INLINE_DECL_REFCOUNTING(ReadbackManagerD3D11)
+public:
+ ReadbackManagerD3D11();
+
+ /**
+ * Tell the readback manager to post a readback task.
+ *
+ * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that
+ * was readback.
+ * @param aSink TextureReadbackSink that the resulting DataSourceSurface
+ * should be dispatched to.
+ */
+ void PostTask(ID3D10Texture2D* aTexture, TextureReadbackSink* aSink);
+
+private:
+ ~ReadbackManagerD3D11();
+
+ static DWORD WINAPI StartTaskThread(void *aManager);
+
+ void ProcessTasks();
+
+ // The invariant maintained by |mTaskSemaphore| is that the readback thread
+ // will awaken from WaitForMultipleObjects() at least once per readback
+ // task enqueued by the main thread. Since the readback thread processes
+ // exactly one task per wakeup (with one exception), no tasks are lost. The
+ // exception is when the readback thread is shut down, which orphans the
+ // remaining tasks, on purpose.
+ HANDLE mTaskSemaphore;
+ // Event signaled when the task thread should shutdown
+ HANDLE mShutdownEvent;
+ // Handle to the task thread
+ HANDLE mTaskThread;
+
+ // FiFo list of readback tasks that are to be executed. Access is synchronized
+ // by mTaskMutex.
+ CRITICAL_SECTION mTaskMutex;
+ nsTArray<nsAutoPtr<ReadbackTask>> mPendingReadbackTasks;
+};
+
+}
+}
+
+#endif /* GFX_READBACKMANAGERD3D11_H */
diff --git a/system/graphics/layers/d3d11/TextureD3D11.cpp b/system/graphics/layers/d3d11/TextureD3D11.cpp
new file mode 100644
index 000000000..628ca1288
--- /dev/null
+++ b/system/graphics/layers/d3d11/TextureD3D11.cpp
@@ -0,0 +1,1286 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureD3D11.h"
+#include "CompositorD3D11.h"
+#include "gfxContext.h"
+#include "Effects.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxPrefs.h"
+#include "ReadbackManagerD3D11.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static const GUID sD3D11TextureUsage =
+{ 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
+
+/* This class gets its lifetime tied to a D3D texture
+ * and increments memory usage on construction and decrements
+ * on destruction */
+class TextureMemoryMeasurer : public IUnknown
+{
+public:
+ TextureMemoryMeasurer(size_t aMemoryUsed)
+ {
+ mMemoryUsed = aMemoryUsed;
+ gfxWindowsPlatform::sD3D11SharedTextures += mMemoryUsed;
+ mRefCnt = 0;
+ }
+ STDMETHODIMP_(ULONG) AddRef() {
+ mRefCnt++;
+ return mRefCnt;
+ }
+ STDMETHODIMP QueryInterface(REFIID riid,
+ void **ppvObject)
+ {
+ IUnknown *punk = nullptr;
+ if (riid == IID_IUnknown) {
+ punk = this;
+ }
+ *ppvObject = punk;
+ if (punk) {
+ punk->AddRef();
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ STDMETHODIMP_(ULONG) Release() {
+ int refCnt = --mRefCnt;
+ if (refCnt == 0) {
+ gfxWindowsPlatform::sD3D11SharedTextures -= mMemoryUsed;
+ delete this;
+ }
+ return refCnt;
+ }
+private:
+ int mRefCnt;
+ int mMemoryUsed;
+};
+
+static DXGI_FORMAT
+SurfaceFormatToDXGIFormat(gfx::SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::B8G8R8X8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::R8G8B8A8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::R8G8B8X8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::A8:
+ return DXGI_FORMAT_R8_UNORM;
+ default:
+ MOZ_ASSERT(false, "unsupported format");
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+static uint32_t
+GetRequiredTilesD3D11(uint32_t aSize, uint32_t aMaxSize)
+{
+ uint32_t requiredTiles = aSize / aMaxSize;
+ if (aSize % aMaxSize) {
+ requiredTiles++;
+ }
+ return requiredTiles;
+}
+
+static IntRect
+GetTileRectD3D11(uint32_t aID, IntSize aSize, uint32_t aMaxSize)
+{
+ uint32_t horizontalTiles = GetRequiredTilesD3D11(aSize.width, aMaxSize);
+ uint32_t verticalTiles = GetRequiredTilesD3D11(aSize.height, aMaxSize);
+
+ uint32_t verticalTile = aID / horizontalTiles;
+ uint32_t horizontalTile = aID % horizontalTiles;
+
+ return IntRect(horizontalTile * aMaxSize,
+ verticalTile * aMaxSize,
+ horizontalTile < (horizontalTiles - 1) ? aMaxSize : aSize.width % aMaxSize,
+ verticalTile < (verticalTiles - 1) ? aMaxSize : aSize.height % aMaxSize);
+}
+
+AutoTextureLock::AutoTextureLock(IDXGIKeyedMutex* aMutex,
+ HRESULT& aResult,
+ uint32_t aTimeout)
+{
+ mMutex = aMutex;
+ mResult = mMutex->AcquireSync(0, aTimeout);
+ aResult = mResult;
+}
+
+AutoTextureLock::~AutoTextureLock()
+{
+ if (!FAILED(mResult) && mResult != WAIT_TIMEOUT &&
+ mResult != WAIT_ABANDONED) {
+ mMutex->ReleaseSync(0);
+ }
+}
+
+ID3D11ShaderResourceView*
+TextureSourceD3D11::GetShaderResourceView()
+{
+ MOZ_ASSERT(mTexture == GetD3D11Texture(), "You need to override GetShaderResourceView if you're overriding GetD3D11Texture!");
+
+ if (!mSRV && mTexture) {
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ // see comment in CompositingRenderTargetD3D11 constructor
+ CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D, mFormatOverride);
+ D3D11_SHADER_RESOURCE_VIEW_DESC *desc = mFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &srvDesc;
+
+ HRESULT hr = device->CreateShaderResourceView(mTexture, desc, getter_AddRefs(mSRV));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "[D3D11] TextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mSRV;
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(SurfaceFormat aFormat,
+ CompositorD3D11* aCompositor,
+ TextureFlags aFlags)
+ : mCompositor(aCompositor)
+ , mFormat(aFormat)
+ , mFlags(aFlags)
+ , mCurrentTile(0)
+ , mIsTiled(false)
+ , mIterating(false)
+ , mAllowTextureUploads(true)
+{
+ MOZ_COUNT_CTOR(DataTextureSourceD3D11);
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(SurfaceFormat aFormat,
+ CompositorD3D11* aCompositor,
+ ID3D11Texture2D* aTexture)
+: mCompositor(aCompositor)
+, mFormat(aFormat)
+, mFlags(TextureFlags::NO_FLAGS)
+, mCurrentTile(0)
+, mIsTiled(false)
+, mIterating(false)
+, mAllowTextureUploads(false)
+{
+ MOZ_COUNT_CTOR(DataTextureSourceD3D11);
+
+ mTexture = aTexture;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+
+ mSize = IntSize(desc.Width, desc.Height);
+}
+
+
+
+DataTextureSourceD3D11::~DataTextureSourceD3D11()
+{
+ MOZ_COUNT_DTOR(DataTextureSourceD3D11);
+}
+
+
+template<typename T> // ID3D10Texture2D or ID3D11Texture2D
+static bool LockD3DTexture(T* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ // Textures created by the DXVA decoders don't have a mutex for synchronization
+ if (mutex) {
+ HRESULT hr = mutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ return false;
+ }
+ }
+ return true;
+}
+
+template<typename T>
+static bool HasKeyedMutex(T* aTexture)
+{
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ return !!mutex;
+}
+
+template<typename T> // ID3D10Texture2D or ID3D11Texture2D
+static void UnlockD3DTexture(T* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ if (mutex) {
+ HRESULT hr = mutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+}
+
+DXGITextureData::DXGITextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent)
+: mSize(aSize)
+, mFormat(aFormat)
+, mNeedsClear(aNeedsClear)
+, mNeedsClearWhite(aNeedsClearWhite)
+, mHasSynchronization(false)
+, mIsForOutOfBandContent(aIsForOutOfBandContent)
+{}
+
+D3D11TextureData::D3D11TextureData(ID3D11Texture2D* aTexture,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent)
+: DXGITextureData(aSize, aFormat, aNeedsClear, aNeedsClearWhite, aIsForOutOfBandContent)
+, mTexture(aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ mHasSynchronization = HasKeyedMutex(aTexture);
+}
+
+D3D11TextureData::~D3D11TextureData()
+{
+#ifdef DEBUG
+ // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
+ // when it calls EndDraw. This EndDraw should not execute anything so it
+ // shouldn't -really- need the lock but the debug layer chokes on this.
+ if (mDrawTarget) {
+ Lock(OpenMode::OPEN_NONE);
+ mDrawTarget = nullptr;
+ Unlock();
+ }
+#endif
+}
+
+bool
+D3D11TextureData::Lock(OpenMode aMode)
+{
+ if (!LockD3DTexture(mTexture.get())) {
+ return false;
+ }
+
+ if (NS_IsMainThread() && !mIsForOutOfBandContent) {
+ if (!PrepareDrawTargetInLock(aMode)) {
+ Unlock();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+DXGITextureData::PrepareDrawTargetInLock(OpenMode aMode)
+{
+ // Make sure that successful write-lock means we will have a DrawTarget to
+ // write into.
+ if (!mDrawTarget && (aMode & OpenMode::OPEN_WRITE || mNeedsClear || mNeedsClearWhite)) {
+ mDrawTarget = BorrowDrawTarget();
+ if (!mDrawTarget) {
+ return false;
+ }
+ }
+
+ if (mNeedsClear) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mNeedsClear = false;
+ }
+ if (mNeedsClearWhite) {
+ mDrawTarget->FillRect(Rect(0, 0, mSize.width, mSize.height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ mNeedsClearWhite = false;
+ }
+
+ return true;
+}
+
+void
+D3D11TextureData::Unlock()
+{
+ UnlockD3DTexture(mTexture.get());
+}
+
+
+void
+DXGITextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = mHasSynchronization;
+}
+
+void
+D3D11TextureData::SyncWithObject(SyncObject* aSyncObject)
+{
+ if (!aSyncObject || !NS_IsMainThread() || mIsForOutOfBandContent) {
+ // When off the main thread we sync using a keyed mutex per texture.
+ return;
+ }
+
+ MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11);
+ SyncObjectD3D11* sync = static_cast<SyncObjectD3D11*>(aSyncObject);
+ sync->RegisterTexture(mTexture);
+}
+
+bool
+DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ RefPtr<IDXGIResource> resource;
+ GetDXGIResource((IDXGIResource**)getter_AddRefs(resource));
+ if (!resource) {
+ return false;
+ }
+ HANDLE sharedHandle;
+ HRESULT hr = resource->GetSharedHandle(&sharedHandle);
+ if (FAILED(hr)) {
+ LOGD3D11("Error getting shared handle for texture.");
+ return false;
+ }
+
+ aOutDescriptor = SurfaceDescriptorD3D10((WindowsHandle)sharedHandle, mFormat, mSize);
+ return true;
+}
+
+DXGITextureData*
+DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags)
+{
+ if (aFormat == SurfaceFormat::A8) {
+ // Currently we don't support A8 surfaces. Fallback.
+ return nullptr;
+ }
+
+ return D3D11TextureData::Create(aSize, aFormat, aFlags);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat, SourceSurface* aSurface,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ // Just grab any device. We never use the immediate context, so the devices are fine
+ // to use from any thread.
+ RefPtr<ID3D11Device> device = aDevice;
+ if (!device) {
+ device = DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return nullptr;
+ }
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ aSize.width, aSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ if (!NS_IsMainThread() || !!(aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT)) {
+ // On the main thread we use the syncobject to handle synchronization.
+ if (!(aFlags & ALLOC_MANUAL_SYNCHRONIZATION)) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+ }
+
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX &&
+ !DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ D3D11_SUBRESOURCE_DATA uploadData;
+ D3D11_SUBRESOURCE_DATA* uploadDataPtr = nullptr;
+ RefPtr<DataSourceSurface> srcSurf;
+ if (aSurface) {
+ srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in D3D11TextureData::Create";
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for D3D11TextureData::Create";
+ return nullptr;
+ }
+
+ uploadData.pSysMem = sourceMap.mData;
+ uploadData.SysMemPitch = sourceMap.mStride;
+ uploadData.SysMemSlicePitch = 0; // unused
+
+ uploadDataPtr = &uploadData;
+ }
+
+ RefPtr<ID3D11Texture2D> texture11;
+ HRESULT hr = device->CreateTexture2D(&newDesc, uploadDataPtr, getter_AddRefs(texture11));
+ if (srcSurf) {
+ srcSurf->Unmap();
+ }
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
+ << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
+ return nullptr;
+ }
+
+ // If we created the texture with a keyed mutex, then we expect all operations
+ // on it to be synchronized using it. If we did an initial upload using aSurface
+ // then bizarely this isn't covered, so we insert a manual lock/unlock pair
+ // to force this.
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) {
+ if (!LockD3DTexture(texture11.get())) {
+ return nullptr;
+ }
+ UnlockD3DTexture(texture11.get());
+ }
+ texture11->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4));
+ return new D3D11TextureData(texture11, aSize, aFormat,
+ aFlags & ALLOC_CLEAR_BUFFER,
+ aFlags & ALLOC_CLEAR_BUFFER_WHITE,
+ aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ return D3D11TextureData::Create(aSize, aFormat, nullptr, aFlags, aDevice);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(SourceSurface* aSurface,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ // Currently we don't support A8 surfaces. Fallback.
+ return nullptr;
+ }
+
+ return D3D11TextureData::Create(aSurface->GetSize(), aSurface->GetFormat(),
+ aSurface, aFlags, aDevice);
+}
+
+void
+D3D11TextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ mDrawTarget = nullptr;
+ mTexture = nullptr;
+}
+
+already_AddRefed<TextureClient>
+CreateD3D11TextureClientWithDevice(IntSize aSize, SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator)
+{
+ TextureData* data = D3D11TextureData::Create(aSize, aFormat, aAllocFlags, aDevice);
+ if (!data) {
+ return nullptr;
+ }
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
+}
+
+TextureData*
+D3D11TextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return D3D11TextureData::Create(mSize, mFormat, aAllocFlags);
+}
+
+void
+D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource)
+{
+ mTexture->QueryInterface(aOutResource);
+}
+
+DXGIYCbCrTextureData*
+DXGIYCbCrTextureData::Create(TextureFlags aFlags,
+ IUnknown* aTextureY,
+ IUnknown* aTextureCb,
+ IUnknown* aTextureCr,
+ HANDLE aHandleY,
+ HANDLE aHandleCb,
+ HANDLE aHandleCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr)
+{
+ if (!aHandleY || !aHandleCb || !aHandleCr ||
+ !aTextureY || !aTextureCb || !aTextureCr) {
+ return nullptr;
+ }
+
+ DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData();
+ texture->mHandles[0] = aHandleY;
+ texture->mHandles[1] = aHandleCb;
+ texture->mHandles[2] = aHandleCr;
+ texture->mHoldRefs[0] = aTextureY;
+ texture->mHoldRefs[1] = aTextureCb;
+ texture->mHoldRefs[2] = aTextureCr;
+ texture->mSize = aSize;
+ texture->mSizeY = aSizeY;
+ texture->mSizeCbCr = aSizeCbCr;
+
+ return texture;
+}
+
+DXGIYCbCrTextureData*
+DXGIYCbCrTextureData::Create(TextureFlags aFlags,
+ ID3D11Texture2D* aTextureY,
+ ID3D11Texture2D* aTextureCb,
+ ID3D11Texture2D* aTextureCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr)
+{
+ if (!aTextureY || !aTextureCb || !aTextureCr) {
+ return nullptr;
+ }
+
+ aTextureY->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSize.width * aSize.height));
+ aTextureCb->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+ aTextureCr->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+
+ RefPtr<IDXGIResource> resource;
+
+ aTextureY->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleY;
+ HRESULT hr = resource->GetSharedHandle(&handleY);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCb->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleCb;
+ hr = resource->GetSharedHandle(&handleCb);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCr->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+ HANDLE handleCr;
+ hr = resource->GetSharedHandle(&handleCr);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ return DXGIYCbCrTextureData::Create(aFlags,
+ aTextureY, aTextureCb, aTextureCr,
+ handleY, handleCb, handleCr,
+ aSize, aSizeY, aSizeCbCr);
+}
+
+void
+DXGIYCbCrTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::YUV;
+ aInfo.supportsMoz2D = false;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+}
+
+bool
+DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorDXGIYCbCr(
+ (WindowsHandle)mHandles[0], (WindowsHandle)mHandles[1], (WindowsHandle)mHandles[2],
+ mSize, mSizeY, mSizeCbCr
+ );
+ return true;
+}
+
+void
+DXGIYCbCrTextureData::Deallocate(LayersIPCChannel*)
+{
+ mHoldRefs[0] = nullptr;
+ mHoldRefs[1] = nullptr;
+ mHoldRefs[2] = nullptr;
+}
+
+already_AddRefed<TextureHost>
+CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+ result = new DXGITextureHostD3D11(aFlags,
+ aDesc.get_SurfaceDescriptorD3D10());
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
+ result = new DXGIYCbCrTextureHostD3D11(aFlags,
+ aDesc.get_SurfaceDescriptorDXGIYCbCr());
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Unsupported SurfaceDescriptor type");
+ }
+ }
+ return result.forget();
+}
+
+
+already_AddRefed<DrawTarget>
+D3D11TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mDrawTarget && mTexture) {
+ // This may return a null DrawTarget
+ mDrawTarget = Factory::CreateDrawTargetForD3D11Texture(mTexture, mFormat);
+ if (!mDrawTarget) {
+ gfxCriticalNote << "Could not borrow DrawTarget (D3D11) " << (int)mFormat;
+ }
+ }
+
+ RefPtr<DrawTarget> result = mDrawTarget;
+ return result.forget();
+}
+
+bool
+D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ // Supporting texture updates after creation requires an ID3D11DeviceContext and those
+ // aren't threadsafe. We'd need to either lock, or have a device for whatever thread
+ // this runs on and we're trying to avoid extra devices (bug 1284672).
+ MOZ_ASSERT(false, "UpdateFromSurface not supported for D3D11! Use CreateFromSurface instead");
+ return false;
+}
+
+DXGITextureHostD3D11::DXGITextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor)
+ : TextureHost(aFlags)
+ , mSize(aDescriptor.size())
+ , mHandle(aDescriptor.handle())
+ , mFormat(aDescriptor.format())
+ , mIsLocked(false)
+{
+}
+
+bool
+DXGITextureHostD3D11::OpenSharedHandle()
+{
+ if (!GetDevice()) {
+ return false;
+ }
+
+ HRESULT hr = GetDevice()->OpenSharedResource((HANDLE)mHandle,
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mTexture));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture");
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ mTexture->GetDesc(&desc);
+ mSize = IntSize(desc.Width, desc.Height);
+ return true;
+}
+
+RefPtr<ID3D11Device>
+DXGITextureHostD3D11::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ return DeviceManagerDx::Get()->GetCompositorDevice();
+}
+
+static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor)
+{
+ CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11()
+ : nullptr;
+ if (!compositor) {
+ gfxCriticalNote << "[D3D11] Attempt to set an incompatible compositor";
+ }
+ return compositor;
+}
+
+void
+DXGITextureHostD3D11::SetCompositor(Compositor* aCompositor)
+{
+ CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+ if (!d3dCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = d3dCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+Compositor*
+DXGITextureHostD3D11::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+DXGITextureHostD3D11::Lock()
+{
+ if (!mCompositor) {
+ // Make an early return here if we call SetCompositor() with an incompatible
+ // compositor. This check tries to prevent the problem where we use that
+ // incompatible compositor to compose this texture.
+ return false;
+ }
+
+ return LockInternal();
+}
+
+bool
+DXGITextureHostD3D11::LockWithoutCompositor()
+{
+ // Unlike the normal Lock() function, this function may be called when
+ // mCompositor is nullptr such as during WebVR frame submission. So, there is
+ // no 'mCompositor' checking here.
+ return LockInternal();
+}
+
+void
+DXGITextureHostD3D11::Unlock()
+{
+ UnlockInternal();
+}
+
+void
+DXGITextureHostD3D11::UnlockWithoutCompositor()
+{
+ UnlockInternal();
+}
+
+bool
+DXGITextureHostD3D11::LockInternal()
+{
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+
+ if (!mTextureSource) {
+ if (!mTexture && !OpenSharedHandle()) {
+ DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
+ return false;
+ }
+
+ mTextureSource = new DataTextureSourceD3D11(mFormat, mCompositor, mTexture);
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+void
+DXGITextureHostD3D11::UnlockInternal()
+{
+ UnlockD3DTexture(mTextureSource->GetD3D11Texture());
+}
+
+bool
+DXGITextureHostD3D11::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSource);
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor)
+ : TextureHost(aFlags)
+ , mSize(aDescriptor.size())
+ , mIsLocked(false)
+{
+ mHandles[0] = aDescriptor.handleY();
+ mHandles[1] = aDescriptor.handleCb();
+ mHandles[2] = aDescriptor.handleCr();
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::OpenSharedHandle()
+{
+ RefPtr<ID3D11Device> device = GetDevice();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<ID3D11Texture2D> textures[3];
+
+ HRESULT hr = device->OpenSharedResource((HANDLE)mHandles[0],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[0]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Y Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource((HANDLE)mHandles[1],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[1]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cb Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource((HANDLE)mHandles[2],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[2]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cr Plane");
+ return false;
+ }
+
+ mTextures[0] = textures[0].forget();
+ mTextures[1] = textures[1].forget();
+ mTextures[2] = textures[2].forget();
+
+ return true;
+}
+
+RefPtr<ID3D11Device>
+DXGIYCbCrTextureHostD3D11::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ return DeviceManagerDx::Get()->GetCompositorDevice();
+}
+
+void
+DXGIYCbCrTextureHostD3D11::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertD3D11Compositor(aCompositor);
+ if (!mCompositor) {
+ mTextureSources[0] = nullptr;
+ mTextureSources[1] = nullptr;
+ mTextureSources[2] = nullptr;
+ return;
+ }
+
+ if (mTextureSources[0]) {
+ mTextureSources[0]->SetCompositor(aCompositor);
+ }
+}
+
+Compositor*
+DXGIYCbCrTextureHostD3D11::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::Lock()
+{
+ if (!mCompositor) {
+ NS_WARNING("no suitable compositor");
+ return false;
+ }
+
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+ if (!mTextureSources[0]) {
+ if (!mTextures[0] && !OpenSharedHandle()) {
+ return false;
+ }
+
+ MOZ_ASSERT(mTextures[1] && mTextures[2]);
+
+ mTextureSources[0] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[0]);
+ mTextureSources[1] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[1]);
+ mTextureSources[2] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[2]);
+ mTextureSources[0]->SetNextSibling(mTextureSources[1]);
+ mTextureSources[1]->SetNextSibling(mTextureSources[2]);
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSources[0]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[1]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+void
+DXGIYCbCrTextureHostD3D11::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ UnlockD3DTexture(mTextureSources[0]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[1]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+ mIsLocked = false;
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSources[0] && mTextureSources[1] && mTextureSources[2]);
+ aTexture = mTextureSources[0].get();
+ return !!aTexture;
+}
+
+bool
+DataTextureSourceD3D11::Update(DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ IntPoint* aSrcOffset)
+{
+ // Incremental update with a source offset is only used on Mac so it is not
+ // clear that we ever will need to support it for D3D.
+ MOZ_ASSERT(!aSrcOffset);
+ MOZ_ASSERT(aSurface);
+
+ MOZ_ASSERT(mAllowTextureUploads);
+ if (!mAllowTextureUploads) {
+ return false;
+ }
+
+ HRESULT hr;
+
+ if (!mCompositor || !mCompositor->GetDevice()) {
+ return false;
+ }
+
+ uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+ DXGI_FORMAT dxgiFormat = SurfaceFormatToDXGIFormat(aSurface->GetFormat());
+
+ mSize = aSurface->GetSize();
+ mFormat = aSurface->GetFormat();
+
+ CD3D11_TEXTURE2D_DESC desc(dxgiFormat, mSize.width, mSize.height, 1, 1);
+
+ int32_t maxSize = mCompositor->GetMaxTextureSize();
+ if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
+ (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+
+ if (mTexture) {
+ D3D11_TEXTURE2D_DESC currentDesc;
+ mTexture->GetDesc(&currentDesc);
+
+ // Make sure there's no size mismatch, if there is, recreate.
+ if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height ||
+ currentDesc.Format != dxgiFormat) {
+ mTexture = nullptr;
+ // Make sure we upload the whole surface.
+ aDestRegion = nullptr;
+ }
+ }
+
+ nsIntRegion *regionToUpdate = aDestRegion;
+ if (!mTexture) {
+ hr = mCompositor->GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture));
+ mIsTiled = false;
+ if (FAILED(hr) || !mTexture) {
+ Reset();
+ return false;
+ }
+
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ regionToUpdate = nullptr;
+ }
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ gfxCriticalError() << "Failed to map surface.";
+ Reset();
+ return false;
+ }
+
+ if (regionToUpdate) {
+ for (auto iter = regionToUpdate->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = rect.x;
+ box.top = rect.y;
+ box.right = rect.XMost();
+ box.bottom = rect.YMost();
+
+ void* data = map.mData + map.mStride * rect.y + BytesPerPixel(aSurface->GetFormat()) * rect.x;
+
+ mCompositor->GetDC()->UpdateSubresource(mTexture, 0, &box, data, map.mStride, map.mStride * rect.height);
+ }
+ } else {
+ mCompositor->GetDC()->UpdateSubresource(mTexture, 0, nullptr, aSurface->GetData(),
+ aSurface->Stride(), aSurface->Stride() * mSize.height);
+ }
+
+ aSurface->Unmap();
+ } else {
+ mIsTiled = true;
+ uint32_t tileCount = GetRequiredTilesD3D11(mSize.width, maxSize) *
+ GetRequiredTilesD3D11(mSize.height, maxSize);
+
+ mTileTextures.resize(tileCount);
+ mTileSRVs.resize(tileCount);
+ mTexture = nullptr;
+
+ for (uint32_t i = 0; i < tileCount; i++) {
+ IntRect tileRect = GetTileRect(i);
+
+ desc.Width = tileRect.width;
+ desc.Height = tileRect.height;
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+
+ D3D11_SUBRESOURCE_DATA initData;
+ initData.pSysMem = aSurface->GetData() +
+ tileRect.y * aSurface->Stride() +
+ tileRect.x * bpp;
+ initData.SysMemPitch = aSurface->Stride();
+
+ hr = mCompositor->GetDevice()->CreateTexture2D(&desc, &initData, getter_AddRefs(mTileTextures[i]));
+ if (FAILED(hr) || !mTileTextures[i]) {
+ Reset();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ID3D11Texture2D*
+DataTextureSourceD3D11::GetD3D11Texture() const
+{
+ return mIterating ? mTileTextures[mCurrentTile]
+ : mTexture;
+}
+
+ID3D11ShaderResourceView*
+DataTextureSourceD3D11::GetShaderResourceView()
+{
+ if (mIterating) {
+ if (!mTileSRVs[mCurrentTile]) {
+ if (!mTileTextures[mCurrentTile]) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ mTileTextures[mCurrentTile]->GetDevice(getter_AddRefs(device));
+ HRESULT hr = device->CreateShaderResourceView(mTileTextures[mCurrentTile], nullptr, getter_AddRefs(mTileSRVs[mCurrentTile]));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "[D3D11] DataTextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mTileSRVs[mCurrentTile];
+ }
+
+ return TextureSourceD3D11::GetShaderResourceView();
+}
+
+void
+DataTextureSourceD3D11::Reset()
+{
+ mTexture = nullptr;
+ mTileSRVs.resize(0);
+ mTileTextures.resize(0);
+ mIsTiled = false;
+ mSize.width = 0;
+ mSize.height = 0;
+}
+
+IntRect
+DataTextureSourceD3D11::GetTileRect(uint32_t aIndex) const
+{
+ return GetTileRectD3D11(aIndex, mSize, mCompositor->GetMaxTextureSize());
+}
+
+IntRect
+DataTextureSourceD3D11::GetTileRect()
+{
+ IntRect rect = GetTileRect(mCurrentTile);
+ return IntRect(rect.x, rect.y, rect.width, rect.height);
+}
+
+void
+DataTextureSourceD3D11::SetCompositor(Compositor* aCompositor)
+{
+ CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+ if (!d3dCompositor) {
+ return;
+ }
+ mCompositor = d3dCompositor;
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+CompositingRenderTargetD3D11::CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
+ const gfx::IntPoint& aOrigin,
+ DXGI_FORMAT aFormatOverride)
+ : CompositingRenderTarget(aOrigin)
+{
+ MOZ_ASSERT(aTexture);
+
+ mTexture = aTexture;
+
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ mFormatOverride = aFormatOverride;
+
+ // If we happen to have a typeless underlying DXGI surface, we need to be explicit
+ // about the format here. (Such a surface could come from an external source)
+ CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D, mFormatOverride);
+ D3D11_RENDER_TARGET_VIEW_DESC *desc = aFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &rtvDesc;
+
+ HRESULT hr = device->CreateRenderTargetView(mTexture, desc, getter_AddRefs(mRTView));
+
+ if (FAILED(hr)) {
+ LOGD3D11("Failed to create RenderTargetView.");
+ }
+}
+
+void
+CompositingRenderTargetD3D11::BindRenderTarget(ID3D11DeviceContext* aContext)
+{
+ if (mClearOnBind) {
+ FLOAT clear[] = { 0, 0, 0, 0 };
+ aContext->ClearRenderTargetView(mRTView, clear);
+ mClearOnBind = false;
+ }
+ ID3D11RenderTargetView* view = mRTView;
+ aContext->OMSetRenderTargets(1, &view, nullptr);
+}
+
+IntSize
+CompositingRenderTargetD3D11::GetSize() const
+{
+ return TextureSourceD3D11::GetSize();
+}
+
+SyncObjectD3D11::SyncObjectD3D11(SyncHandle aHandle)
+{
+ MOZ_ASSERT(aHandle);
+ mD3D11Device = DeviceManagerDx::Get()->GetContentDevice();
+ mHandle = aHandle;
+}
+
+void
+SyncObjectD3D11::RegisterTexture(ID3D11Texture2D* aTexture)
+{
+ mD3D11SyncedTextures.push_back(aTexture);
+}
+
+bool
+SyncObjectD3D11::IsSyncObjectValid()
+{
+ RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
+ if (!dev || (dev != mD3D11Device)) {
+ return false;
+ }
+ return true;
+}
+
+void
+SyncObjectD3D11::FinalizeFrame()
+{
+ HRESULT hr;
+
+ if (!mD3D11Texture && mD3D11SyncedTextures.size()) {
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetContentDevice();
+
+ hr = device->OpenSharedResource(mHandle, __uuidof(ID3D11Texture2D), (void**)(ID3D11Texture2D**)getter_AddRefs(mD3D11Texture));
+
+ if (FAILED(hr) || !mD3D11Texture) {
+ gfxCriticalError() << "Failed to D3D11 OpenSharedResource for frame finalization: " << hexa(hr);
+
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ return;
+ }
+
+ gfxDevCrash(LogReason::D3D11FinalizeFrame) << "Without device reset: " << hexa(hr);
+ }
+
+ // test QI
+ RefPtr<IDXGIKeyedMutex> mutex;
+ hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+ if (FAILED(hr) || !mutex) {
+ // Leave both the critical error and MOZ_CRASH for now; the critical error lets
+ // us "save" the hr value. We will probably eventuall replace this with gfxDevCrash.
+ gfxCriticalError() << "Failed to get KeyedMutex (2): " << hexa(hr);
+ MOZ_CRASH("GFX: Cannot get D3D11 KeyedMutex");
+ }
+ }
+
+ if (mD3D11SyncedTextures.size()) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ {
+ AutoTextureLock lock(mutex, hr, 20000);
+
+ if (hr == WAIT_TIMEOUT) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ gfxWarning() << "AcquireSync timed out because of device reset.";
+ return;
+ }
+ gfxDevCrash(LogReason::D3D11SyncLock) << "Timeout on the D3D11 sync lock";
+ }
+
+ D3D11_BOX box;
+ box.front = box.top = box.left = 0;
+ box.back = box.bottom = box.right = 1;
+
+ RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
+ if (!dev) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ return;
+ }
+ MOZ_CRASH("GFX: Invalid D3D11 content device");
+ }
+
+ RefPtr<ID3D11DeviceContext> ctx;
+ dev->GetImmediateContext(getter_AddRefs(ctx));
+
+ for (auto iter = mD3D11SyncedTextures.begin(); iter != mD3D11SyncedTextures.end(); iter++) {
+ ctx->CopySubresourceRegion(mD3D11Texture, 0, 0, 0, 0, *iter, 0, &box);
+ }
+ }
+
+ mD3D11SyncedTextures.clear();
+ }
+}
+
+}
+}
diff --git a/system/graphics/layers/d3d11/TextureD3D11.h b/system/graphics/layers/d3d11/TextureD3D11.h
new file mode 100644
index 000000000..831342aa2
--- /dev/null
+++ b/system/graphics/layers/d3d11/TextureD3D11.h
@@ -0,0 +1,455 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURED3D11_H
+#define MOZILLA_GFX_TEXTURED3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/GfxMessageUtils.h"
+#include <d3d11.h>
+#include "d3d9.h"
+#include <vector>
+
+namespace mozilla {
+namespace layers {
+
+class MOZ_RAII AutoTextureLock
+{
+public:
+ AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
+ uint32_t aTimeout = 0);
+ ~AutoTextureLock();
+
+private:
+ RefPtr<IDXGIKeyedMutex> mMutex;
+ HRESULT mResult;
+};
+
+class CompositorD3D11;
+
+class DXGITextureData : public TextureData
+{
+public:
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
+
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
+
+protected:
+ bool PrepareDrawTargetInLock(OpenMode aMode);
+
+ DXGITextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent);
+
+ virtual void GetDXGIResource(IDXGIResource** aOutResource) = 0;
+
+ // Hold on to the DrawTarget because it is expensive to create one each ::Lock.
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ bool mNeedsClear;
+ bool mNeedsClearWhite;
+ bool mHasSynchronization;
+ bool mIsForOutOfBandContent;
+};
+
+class D3D11TextureData : public DXGITextureData
+{
+public:
+ // If aDevice is null, use one provided by gfxWindowsPlatform.
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+ static DXGITextureData*
+ Create(gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const override;
+
+ virtual void SyncWithObject(SyncObject* aSync) override;
+
+ ID3D11Texture2D* GetD3D11Texture() { return mTexture; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ D3D11TextureData* AsD3D11TextureData() override {
+ return this;
+ }
+
+ ~D3D11TextureData();
+protected:
+ D3D11TextureData(ID3D11Texture2D* aTexture,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent);
+
+ virtual void GetDXGIResource(IDXGIResource** aOutResource) override;
+
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ RefPtr<ID3D11Texture2D> mTexture;
+};
+
+already_AddRefed<TextureClient>
+CreateD3D11extureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator);
+
+class DXGIYCbCrTextureData : public TextureData
+{
+public:
+ static DXGIYCbCrTextureData*
+ Create(TextureFlags aFlags,
+ IUnknown* aTextureY,
+ IUnknown* aTextureCb,
+ IUnknown* aTextureCr,
+ HANDLE aHandleY,
+ HANDLE aHandleCb,
+ HANDLE aHandleCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr);
+
+ static DXGIYCbCrTextureData*
+ Create(TextureFlags aFlags,
+ ID3D11Texture2D* aTextureCb,
+ ID3D11Texture2D* aTextureY,
+ ID3D11Texture2D* aTextureCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr);
+
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override { return nullptr; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface*) override { return false; }
+
+ virtual TextureFlags GetTextureFlags() const override
+ {
+ return TextureFlags::DEALLOCATE_MAIN_THREAD;
+ }
+
+protected:
+ RefPtr<IUnknown> mHoldRefs[3];
+ HANDLE mHandles[3];
+ gfx::IntSize mSize;
+ gfx::IntSize mSizeY;
+ gfx::IntSize mSizeCbCr;
+};
+
+/**
+ * TextureSource that provides with the necessary APIs to be composited by a
+ * CompositorD3D11.
+ */
+class TextureSourceD3D11
+{
+public:
+ TextureSourceD3D11() : mFormatOverride(DXGI_FORMAT_UNKNOWN) {}
+ virtual ~TextureSourceD3D11() {}
+
+ virtual ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
+ virtual ID3D11ShaderResourceView* GetShaderResourceView();
+protected:
+ virtual gfx::IntSize GetSize() const { return mSize; }
+
+ gfx::IntSize mSize;
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID3D11ShaderResourceView> mSRV;
+ DXGI_FORMAT mFormatOverride;
+};
+
+/**
+ * A TextureSource that implements the DataTextureSource interface.
+ * it can be used without a TextureHost and is able to upload texture data
+ * from a gfx::DataSourceSurface.
+ */
+class DataTextureSourceD3D11 : public DataTextureSource
+ , public TextureSourceD3D11
+ , public BigImageIterator
+{
+public:
+ /// Constructor allowing the texture to perform texture uploads.
+ ///
+ /// The texture can be used as an actual DataTextureSource.
+ DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
+ TextureFlags aFlags);
+
+ /// Constructor for textures created around DXGI shared handles, disallowing
+ /// texture uploads.
+ ///
+ /// The texture CANNOT be used as a DataTextureSource.
+ DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
+ ID3D11Texture2D* aTexture);
+
+ virtual ~DataTextureSourceD3D11();
+
+ virtual const char* Name() const override { return "DataTextureSourceD3D11"; }
+
+ // DataTextureSource
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ // TextureSource
+
+ virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
+
+ virtual ID3D11Texture2D* GetD3D11Texture() const override;
+
+ virtual ID3D11ShaderResourceView* GetShaderResourceView() override;
+
+ // Returns nullptr if this texture was created by a DXGI TextureHost.
+ virtual DataTextureSource* AsDataTextureSource() override { return mAllowTextureUploads ? this : false; }
+
+ virtual void DeallocateDeviceData() override { mTexture = nullptr; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ // BigImageIterator
+
+ virtual BigImageIterator* AsBigImageIterator() override { return mIsTiled ? this : nullptr; }
+
+ virtual size_t GetTileCount() override { return mTileTextures.size(); }
+
+ virtual bool NextTile() override { return (++mCurrentTile < mTileTextures.size()); }
+
+ virtual gfx::IntRect GetTileRect() override;
+
+ virtual void EndBigImageIteration() override { mIterating = false; }
+
+ virtual void BeginBigImageIteration() override
+ {
+ mIterating = true;
+ mCurrentTile = 0;
+ }
+
+protected:
+ gfx::IntRect GetTileRect(uint32_t aIndex) const;
+
+ void Reset();
+
+ std::vector< RefPtr<ID3D11Texture2D> > mTileTextures;
+ std::vector< RefPtr<ID3D11ShaderResourceView> > mTileSRVs;
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::SurfaceFormat mFormat;
+ TextureFlags mFlags;
+ uint32_t mCurrentTile;
+ bool mIsTiled;
+ bool mIterating;
+ // Sadly, the code was originally organized so that this class is used both in
+ // the cases where we want to perform texture uploads through the DataTextureSource
+ // interface, and the cases where we wrap the texture around an existing DXGI
+ // handle in which case we should not use it as a DataTextureSource.
+ // This member differentiates the two scenarios. When it is false the texture
+ // "pretends" to not be a DataTextureSource.
+ bool mAllowTextureUploads;
+};
+
+already_AddRefed<TextureClient>
+CreateD3D11TextureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator);
+
+
+/**
+ * A TextureHost for shared D3D11 textures.
+ */
+class DXGITextureHostD3D11 : public TextureHost
+{
+public:
+ DXGITextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual bool Lock() override;
+ virtual void Unlock() override;
+
+ virtual bool LockWithoutCompositor() override;
+ virtual void UnlockWithoutCompositor() override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+protected:
+ bool LockInternal();
+ void UnlockInternal();
+
+ RefPtr<ID3D11Device> GetDevice();
+
+ bool OpenSharedHandle();
+
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<DataTextureSourceD3D11> mTextureSource;
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::IntSize mSize;
+ WindowsHandle mHandle;
+ gfx::SurfaceFormat mFormat;
+ bool mIsLocked;
+};
+
+class DXGIYCbCrTextureHostD3D11 : public TextureHost
+{
+public:
+ DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override{}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override{ return gfx::SurfaceFormat::YUV; }
+
+ // Bug 1305906 fixes YUVColorSpace handling
+ virtual YUVColorSpace GetYUVColorSpace() const override { return YUVColorSpace::BT601; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+protected:
+ RefPtr<ID3D11Device> GetDevice();
+
+ bool OpenSharedHandle();
+
+ RefPtr<ID3D11Texture2D> mTextures[3];
+ RefPtr<DataTextureSourceD3D11> mTextureSources[3];
+
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::IntSize mSize;
+ WindowsHandle mHandles[3];
+ bool mIsLocked;
+};
+
+class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
+ public TextureSourceD3D11
+{
+public:
+ CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
+ const gfx::IntPoint& aOrigin,
+ DXGI_FORMAT aFormatOverride = DXGI_FORMAT_UNKNOWN);
+
+ virtual const char* Name() const override { return "CompositingRenderTargetD3D11"; }
+
+ virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
+
+ void BindRenderTarget(ID3D11DeviceContext* aContext);
+
+ virtual gfx::IntSize GetSize() const override;
+
+ void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }
+
+private:
+ friend class CompositorD3D11;
+ RefPtr<ID3D11RenderTargetView> mRTView;
+};
+
+class SyncObjectD3D11 : public SyncObject
+{
+public:
+ SyncObjectD3D11(SyncHandle aSyncHandle);
+ virtual void FinalizeFrame();
+ virtual bool IsSyncObjectValid();
+
+ virtual SyncType GetSyncType() { return SyncType::D3D11; }
+
+ void RegisterTexture(ID3D11Texture2D* aTexture);
+
+private:
+ RefPtr<ID3D11Texture2D> mD3D11Texture;
+ RefPtr<ID3D11Device> mD3D11Device;
+ std::vector<ID3D11Texture2D*> mD3D11SyncedTextures;
+ SyncHandle mHandle;
+};
+
+inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel)
+{
+ int32_t maxTextureSize;
+ switch (aFeatureLevel) {
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ maxTextureSize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ case D3D_FEATURE_LEVEL_9_3:
+ maxTextureSize = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ default:
+ maxTextureSize = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ }
+ return maxTextureSize;
+}
+
+}
+}
+
+#endif /* MOZILLA_GFX_TEXTURED3D11_H */
diff --git a/system/graphics/layers/d3d11/genshaders.sh b/system/graphics/layers/d3d11/genshaders.sh
new file mode 100644
index 000000000..0928e3f49
--- /dev/null
+++ b/system/graphics/layers/d3d11/genshaders.sh
@@ -0,0 +1,50 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+tempfile=tmpShaderHeader
+
+FXC_DEBUG_FLAGS="-Zi -Fd shaders.pdb"
+FXC_FLAGS=""
+
+# If DEBUG is in the environment, then rebuild with debug info
+if [ "$DEBUG" != "" ] ; then
+ FXC_FLAGS="$FXC_DEBUG_FLAGS"
+fi
+
+makeShaderVS() {
+ fxc -nologo $FXC_FLAGS -Tvs_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
+ echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
+ cat $tempfile >> $DEST
+}
+
+makeShaderPS() {
+ fxc -nologo $FXC_FLAGS -Tps_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
+ echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
+ cat $tempfile >> $DEST
+}
+
+SRC=CompositorD3D11.hlsl
+DEST=CompositorD3D11Shaders.h
+
+rm -f $DEST
+echo "struct ShaderBytes { const void* mData; size_t mLength; };" >> $DEST;
+makeShaderVS LayerQuadVS
+makeShaderPS SolidColorShader
+makeShaderPS RGBShader
+makeShaderPS RGBAShader
+makeShaderPS ComponentAlphaShader
+makeShaderPS YCbCrShader
+makeShaderVS LayerQuadMaskVS
+makeShaderPS SolidColorShaderMask
+makeShaderPS RGBShaderMask
+makeShaderPS RGBAShaderMask
+makeShaderPS YCbCrShaderMask
+makeShaderPS ComponentAlphaShaderMask
+
+# Mix-blend shaders
+makeShaderVS LayerQuadBlendVS
+makeShaderVS LayerQuadBlendMaskVS
+makeShaderPS BlendShader
+
+rm $tempfile
diff --git a/system/graphics/layers/ipc/APZCTreeManagerChild.cpp b/system/graphics/layers/ipc/APZCTreeManagerChild.cpp
new file mode 100644
index 000000000..a2623f875
--- /dev/null
+++ b/system/graphics/layers/ipc/APZCTreeManagerChild.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerChild.h"
+
+#include "InputData.h" // for InputData
+#include "mozilla/dom/TabParent.h" // for TabParent
+#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper
+#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerChild::APZCTreeManagerChild()
+ : mCompositorSession(nullptr)
+{
+}
+
+void
+APZCTreeManagerChild::SetCompositorSession(RemoteCompositorSession* aSession)
+{
+ // Exactly one of mCompositorSession and aSession must be null (i.e. either
+ // we're setting mCompositorSession or we're clearing it).
+ MOZ_ASSERT(!mCompositorSession ^ !aSession);
+ mCompositorSession = aSession;
+}
+
+nsEventStatus
+APZCTreeManagerChild::ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ MultiTouchInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveMultiTouchInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case MOUSE_INPUT: {
+ MouseInput& event = aEvent.AsMouseInput();
+ MouseInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveMouseInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput& event = aEvent.AsPanGestureInput();
+ PanGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceivePanGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput& event = aEvent.AsPinchGestureInput();
+ PinchGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceivePinchGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput& event = aEvent.AsTapGestureInput();
+ TapGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveTapGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ ScrollWheelInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveScrollWheelInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+}
+
+void
+APZCTreeManagerChild::ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags)
+{
+ SendZoomToRect(aGuid, aRect, aFlags);
+}
+
+void
+APZCTreeManagerChild::ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault)
+{
+ SendContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void
+APZCTreeManagerChild::SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ SendSetTargetAPZC(aInputBlockId, aTargets);
+}
+
+void
+APZCTreeManagerChild::UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ SendUpdateZoomConstraints(aGuid, aConstraints);
+}
+
+void
+APZCTreeManagerChild::CancelAnimation(const ScrollableLayerGuid &aGuid)
+{
+ SendCancelAnimation(aGuid);
+}
+
+void
+APZCTreeManagerChild::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ SendAdjustScrollForSurfaceShift(aShift);
+}
+
+void
+APZCTreeManagerChild::SetDPI(float aDpiValue)
+{
+ SendSetDPI(aDpiValue);
+}
+
+void
+APZCTreeManagerChild::SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues)
+{
+ SendSetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void
+APZCTreeManagerChild::StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ SendStartScrollbarDrag(aGuid, aDragMetrics);
+}
+
+void
+APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled)
+{
+ SendSetLongTapEnabled(aTapGestureEnabled);
+}
+
+void
+APZCTreeManagerChild::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+ SendProcessTouchVelocity(aTimestampMs, aSpeedY);
+}
+
+void
+APZCTreeManagerChild::UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage)
+{
+ SendUpdateWheelTransaction(aRefPoint, aEventMessage);
+}
+
+void APZCTreeManagerChild::TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ SendTransformEventRefPoint(*aRefPoint, aRefPoint, aOutTargetGuid);
+}
+
+bool
+APZCTreeManagerChild::RecvHandleTap(const TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mCompositorSession &&
+ mCompositorSession->RootLayerTreeId() == aGuid.mLayersId &&
+ mCompositorSession->GetContentController()) {
+ mCompositorSession->GetContentController()->HandleTap(aType, aPoint,
+ aModifiers, aGuid, aInputBlockId);
+ return true;
+ }
+ dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+ return true;
+}
+
+bool
+APZCTreeManagerChild::RecvNotifyPinchGesture(const PinchGestureType& aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers)
+{
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We want to handle it in this process regardless of what the target guid
+ // of the pinch is. This may change in the future.
+ if (mCompositorSession &&
+ mCompositorSession->GetWidget()) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mCompositorSession->GetWidget());
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/APZCTreeManagerChild.h b/system/graphics/layers/ipc/APZCTreeManagerChild.h
new file mode 100644
index 000000000..5374c797b
--- /dev/null
+++ b/system/graphics/layers/ipc/APZCTreeManagerChild.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerChild_h
+#define mozilla_layers_APZCTreeManagerChild_h
+
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/PAPZCTreeManagerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class RemoteCompositorSession;
+
+class APZCTreeManagerChild
+ : public IAPZCTreeManager
+ , public PAPZCTreeManagerChild
+{
+public:
+ APZCTreeManagerChild();
+
+ void SetCompositorSession(RemoteCompositorSession* aSession);
+
+ nsEventStatus
+ ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ void
+ ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ void
+ ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ void
+ SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ void
+ UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ void
+ CancelAnimation(const ScrollableLayerGuid &aGuid) override;
+
+ void
+ AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ void
+ SetDPI(float aDpiValue) override;
+
+ void
+ SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ void
+ StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ void
+ SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ void
+ ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
+
+ void
+ TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void
+ UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) override;
+
+protected:
+ bool RecvHandleTap(const TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ bool RecvNotifyPinchGesture(const PinchGestureType& aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers) override;
+
+ virtual
+ ~APZCTreeManagerChild() { }
+
+private:
+ MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerChild_h
diff --git a/system/graphics/layers/ipc/APZCTreeManagerParent.cpp b/system/graphics/layers/ipc/APZCTreeManagerParent.cpp
new file mode 100644
index 000000000..30a0d81ac
--- /dev/null
+++ b/system/graphics/layers/ipc/APZCTreeManagerParent.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerParent.h"
+
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerParent::APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager)
+ : mLayersId(aLayersId)
+ , mTreeManager(aAPZCTreeManager)
+{
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+}
+
+APZCTreeManagerParent::~APZCTreeManagerParent()
+{
+}
+
+void
+APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager)
+{
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+ mTreeManager = aAPZCTreeManager;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MultiTouchInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ MultiTouchInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MouseInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ MouseInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PanGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ PanGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PinchGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ PinchGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ TapGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ TapGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent,
+ nsEventStatus* aOutStatus,
+ ScrollWheelInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ ScrollWheelInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t& aFlags)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
+ return false;
+ }
+
+ mTreeManager->ZoomToRect(aGuid, aRect, aFlags);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId,
+ const bool& aPreventDefault)
+{
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableMethod<uint64_t, bool>(mTreeManager,
+ &IAPZCTreeManager::ContentReceivedInputBlock,
+ aInputBlockId,
+ aPreventDefault));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets)
+{
+ for (size_t i = 0; i < aTargets.Length(); i++) {
+ if (aTargets[i].mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvSetTargetAPZC; dropping message...");
+ return false;
+ }
+ }
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod
+ <uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>
+ (mTreeManager, &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvUpdateZoomConstraints; dropping message...");
+ return false;
+ }
+
+ mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvCancelAnimation(const ScrollableLayerGuid& aGuid)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvCancelAnimation; dropping message...");
+ return false;
+ }
+
+ mTreeManager->CancelAnimation(aGuid);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ mTreeManager->AdjustScrollForSurfaceShift(aShift);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue)
+{
+ mTreeManager->SetDPI(aDpiValue);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId,
+ nsTArray<TouchBehaviorFlags>&& aValues)
+{
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod
+ <uint64_t,
+ StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>
+ (mTreeManager,
+ &IAPZCTreeManager::SetAllowedTouchBehavior,
+ aInputBlockId, Move(aValues)));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
+ return false;
+ }
+
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+ mTreeManager,
+ &IAPZCTreeManager::StartScrollbarDrag,
+ aGuid, aDragMetrics));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aTapGestureEnabled)
+{
+ mTreeManager->SetLongTapEnabled(aTapGestureEnabled);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvProcessTouchVelocity(
+ const uint32_t& aTimestampMs,
+ const float& aSpeedY)
+{
+ mTreeManager->ProcessTouchVelocity(aTimestampMs, aSpeedY);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint,
+ const EventMessage& aEventMessage)
+{
+ mTreeManager->UpdateWheelTransaction(aRefPoint, aEventMessage);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvTransformEventRefPoint(
+ const LayoutDeviceIntPoint& aRefPoint,
+ LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ LayoutDeviceIntPoint refPoint = aRefPoint;
+ mTreeManager->TransformEventRefPoint(&refPoint, aOutTargetGuid);
+ *aOutRefPoint = refPoint;
+
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/APZCTreeManagerParent.h b/system/graphics/layers/ipc/APZCTreeManagerParent.h
new file mode 100644
index 000000000..8e44babbd
--- /dev/null
+++ b/system/graphics/layers/ipc/APZCTreeManagerParent.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerParent_h
+#define mozilla_layers_APZCTreeManagerParent_h
+
+#include "mozilla/layers/PAPZCTreeManagerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZCTreeManager;
+
+class APZCTreeManagerParent
+ : public PAPZCTreeManagerParent
+{
+public:
+
+ explicit APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager);
+ virtual ~APZCTreeManagerParent();
+
+ uint64_t LayersId() const { return mLayersId; }
+
+ /**
+ * Called when the layer tree that this protocol is connected to
+ * is adopted by another compositor, and we need to switch APZCTreeManagers.
+ */
+ void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager);
+
+ bool
+ RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MultiTouchInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MouseInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PanGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PinchGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ TapGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent,
+ nsEventStatus* aOutStatus,
+ ScrollWheelInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t& aFlags) override;
+
+ bool
+ RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId,
+ const bool& aPreventDefault) override;
+
+ bool
+ RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ bool
+ RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints) override;
+
+ bool
+ RecvCancelAnimation(const ScrollableLayerGuid& aGuid) override;
+
+ bool
+ RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ bool
+ RecvSetDPI(const float& aDpiValue) override;
+
+ bool
+ RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId,
+ nsTArray<TouchBehaviorFlags>&& aValues) override;
+
+ bool
+ RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ bool
+ RecvSetLongTapEnabled(const bool& aTapGestureEnabled) override;
+
+ bool
+ RecvProcessTouchVelocity(
+ const uint32_t& aTimestampMs,
+ const float& aSpeedY) override;
+
+ bool
+ RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint,
+ const EventMessage& aEventMessage) override;
+
+ bool
+ RecvTransformEventRefPoint(
+ const LayoutDeviceIntPoint& aRefPoint,
+ LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void
+ ActorDestroy(ActorDestroyReason aWhy) override { }
+
+private:
+ uint64_t mLayersId;
+ RefPtr<APZCTreeManager> mTreeManager;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerParent_h
diff --git a/system/graphics/layers/ipc/APZChild.cpp b/system/graphics/layers/ipc/APZChild.cpp
new file mode 100644
index 000000000..d8d0fb241
--- /dev/null
+++ b/system/graphics/layers/ipc/APZChild.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/GeckoContentController.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+APZChild::APZChild(RefPtr<GeckoContentController> aController)
+ : mController(aController)
+{
+ MOZ_ASSERT(mController);
+}
+
+APZChild::~APZChild()
+{
+ if (mController) {
+ mController->Destroy();
+ mController = nullptr;
+ }
+}
+
+bool
+APZChild::RecvRequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->RequestContentRepaint(aFrameMetrics);
+ return true;
+}
+
+bool
+APZChild::RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent)
+{
+ mController->UpdateOverscrollVelocity(aX, aY, aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent)
+{
+ mController->UpdateOverscrollOffset(aX, aY, aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvSetScrollingRootContent(const bool& aIsRootContent)
+{
+ mController->SetScrollingRootContent(aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ mController->NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ const APZStateChange& aChange,
+ const int& aArg)
+{
+ mController->NotifyAPZStateChange(aGuid, aChange, aArg);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyFlushComplete()
+{
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->NotifyFlushComplete();
+ return true;
+}
+
+bool
+APZChild::RecvDestroy()
+{
+ // mController->Destroy will be called in the destructor
+ PAPZChild::Send__delete__(this);
+ return true;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/APZChild.h b/system/graphics/layers/ipc/APZChild.h
new file mode 100644
index 000000000..990e41ada
--- /dev/null
+++ b/system/graphics/layers/ipc/APZChild.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZChild_h
+#define mozilla_layers_APZChild_h
+
+#include "mozilla/layers/PAPZChild.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class GeckoContentController;
+
+/**
+ * APZChild implements PAPZChild and is used to remote a GeckoContentController
+ * that lives in a different process than where APZ lives.
+ */
+class APZChild final : public PAPZChild
+{
+public:
+ explicit APZChild(RefPtr<GeckoContentController> aController);
+ ~APZChild();
+
+ bool RecvRequestContentRepaint(const FrameMetrics& frame) override;
+
+ bool RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent) override;
+
+ bool RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent) override;
+
+ bool RecvSetScrollingRootContent(const bool& aIsRootContent) override;
+
+ bool RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ bool RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ const APZStateChange& aChange,
+ const int& aArg) override;
+
+ bool RecvNotifyFlushComplete() override;
+
+ bool RecvDestroy() override;
+
+private:
+ RefPtr<GeckoContentController> mController;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_APZChild_h
diff --git a/system/graphics/layers/ipc/CompositableForwarder.cpp b/system/graphics/layers/ipc/CompositableForwarder.cpp
new file mode 100644
index 000000000..2ca4f1675
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositableForwarder.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableForwarder.h"
+#include "mozilla/layers/CompositableChild.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+CompositableForwarder::Destroy(CompositableChild* aCompositable)
+{
+ AssertInForwarderThread();
+
+ if (!aCompositable->CanSend()) {
+ return;
+ }
+
+ if (!DestroyInTransaction(aCompositable, false)) {
+ aCompositable->SendDestroy();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/CompositableForwarder.h b/system/graphics/layers/ipc/CompositableForwarder.h
new file mode 100644
index 000000000..8cee41534
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositableForwarder.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+#define MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Rect.h"
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ImageContainer;
+class SurfaceDescriptor;
+class SurfaceDescriptorTiles;
+class ThebesBufferData;
+class PTextureChild;
+
+/**
+ * A transaction is a set of changes that happenned on the content side, that
+ * should be sent to the compositor side.
+ * CompositableForwarder is an interface to manage a transaction of
+ * compositable objetcs.
+ *
+ * ShadowLayerForwarder is an example of a CompositableForwarder (that can
+ * additionally forward modifications of the Layer tree).
+ * ImageBridgeChild is another CompositableForwarder.
+ *
+ * CompositableForwarder implements KnowsCompositor for simplicity as all
+ * implementations of CompositableForwarder currently also implement KnowsCompositor.
+ * This dependency could be split if we add new use cases.
+ */
+class CompositableForwarder : public KnowsCompositor
+{
+public:
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer = nullptr) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side what TiledLayerBuffer to
+ * use for the next composition.
+ */
+ virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by
+ * aCompositable and aIdentifier has been updated to aThebesBuffer.
+ */
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) = 0;
+
+ virtual void Destroy(CompositableChild* aCompositable);
+
+ virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0;
+ virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side to remove the texture
+ * from the CompositableHost.
+ * This function does not delete the TextureHost corresponding to the
+ * TextureClient passed in parameter.
+ * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
+ * the transaction becomes synchronous.
+ */
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) = 0;
+
+ struct TimedTextureClient {
+ TimedTextureClient()
+ : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
+
+ TextureClient* mTextureClient;
+ TimeStamp mTimeStamp;
+ nsIntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ /**
+ * Tell the CompositableHost on the compositor side what textures to use for
+ * the next composition.
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) = 0;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) = 0;
+
+ virtual void UpdateFwdTransactionId() = 0;
+ virtual uint64_t GetFwdTransactionId() = 0;
+
+ virtual bool InForwarderThread() = 0;
+
+ void AssertInForwarderThread() {
+ MOZ_ASSERT(InForwarderThread());
+ }
+
+protected:
+ nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
+ nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/CompositableTransactionParent.cpp b/system/graphics/layers/ipc/CompositableTransactionParent.cpp
new file mode 100644
index 000000000..135011101
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositableTransactionParent.cpp
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableTransactionParent.h"
+#include "CompositableHost.h" // for CompositableParent, etc
+#include "CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "GLContext.h" // for GLContext
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ContentHost.h" // for ContentHostBase
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/TiledContentHost.h"
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledLayerBuffer;
+class Compositor;
+
+// This function can in some cases fail and return false without it being a bug.
+// This can theoretically happen if the ImageBridge sends frames before
+// we created the layer tree. Since we can't enforce that the layer
+// tree is already created before ImageBridge operates, there isn't much
+// we can do about it, but in practice it is very rare.
+// Typically when a tab with a video is dragged from a window to another,
+// there can be a short time when the video is still sending frames
+// asynchonously while the layer tree is not reconstructed. It's not a
+// big deal.
+// Note that Layers transactions do not need to call this because they always
+// schedule the composition, in LayerManagerComposite::EndTransaction.
+static bool
+ScheduleComposition(CompositableHost* aCompositable)
+{
+ uint64_t id = aCompositable->GetCompositorID();
+ if (!id) {
+ return false;
+ }
+ CompositorBridgeParent* cp = CompositorBridgeParent::GetCompositorBridgeParent(id);
+ if (!cp) {
+ return false;
+ }
+ cp->ScheduleComposition();
+ return true;
+}
+
+bool
+CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation& aEdit,
+ EditReplyVector& replyv)
+{
+ // Ignore all operations on compositables created on stale compositors. We
+ // return true because the child is unable to handle errors.
+ CompositableHost* compositable = CompositableHost::FromIPDLActor(aEdit.compositableParent());
+ if (compositable->GetCompositor() && !compositable->GetCompositor()->IsValid()) {
+ return true;
+ }
+
+ switch (aEdit.detail().type()) {
+ case CompositableOperationDetail::TOpPaintTextureRegion: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
+
+ const OpPaintTextureRegion& op = aEdit.detail().get_OpPaintTextureRegion();
+ Layer* layer = compositable->GetLayer();
+ if (!layer || layer->GetType() != Layer::TYPE_PAINTED) {
+ return false;
+ }
+ PaintedLayerComposite* thebes = static_cast<PaintedLayerComposite*>(layer);
+
+ const ThebesBufferData& bufferData = op.bufferData();
+
+ RenderTraceInvalidateStart(thebes, "FF00FF", op.updatedRegion().GetBounds());
+
+ nsIntRegion frontUpdatedRegion;
+ if (!compositable->UpdateThebes(bufferData,
+ op.updatedRegion(),
+ thebes->GetValidRegion(),
+ &frontUpdatedRegion))
+ {
+ return false;
+ }
+ replyv.push_back(
+ OpContentBufferSwap(aEdit.compositableParent(), nullptr, frontUpdatedRegion));
+
+ RenderTraceInvalidateEnd(thebes, "FF00FF");
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTiledLayerBuffer: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
+ const OpUseTiledLayerBuffer& op = aEdit.detail().get_OpUseTiledLayerBuffer();
+ TiledContentHost* tiledHost = compositable->AsTiledContentHost();
+
+ NS_ASSERTION(tiledHost, "The compositable is not tiled");
+
+ const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
+
+ bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc);
+
+ const InfallibleTArray<TileDescriptor>& tileDescriptors = tileDesc.tiles();
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ continue;
+ }
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(texturedDesc.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+ texture = TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+ if (!success) {
+ return false;
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpRemoveTexture: {
+ const OpRemoveTexture& op = aEdit.detail().get_OpRemoveTexture();
+
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
+
+ MOZ_ASSERT(tex.get());
+ compositable->RemoveTextureHost(tex);
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTexture: {
+ const OpUseTexture& op = aEdit.detail().get_OpUseTexture();
+
+ AutoTArray<CompositableHost::TimedTexture,4> textures;
+ for (auto& timedTexture : op.textures()) {
+ CompositableHost::TimedTexture* t = textures.AppendElement();
+ t->mTexture =
+ TextureHost::AsTextureHost(timedTexture.textureParent());
+ MOZ_ASSERT(t->mTexture);
+ t->mTimeStamp = timedTexture.timeStamp();
+ t->mPictureRect = timedTexture.picture();
+ t->mFrameID = timedTexture.frameID();
+ t->mProducerID = timedTexture.producerID();
+ t->mTexture->DeserializeReadLock(timedTexture.sharedLock(), this);
+ }
+ if (textures.Length() > 0) {
+ compositable->UseTextureHost(textures);
+
+ for (auto& timedTexture : op.textures()) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(timedTexture.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+
+ if (UsesImageBridge() && compositable->GetLayer()) {
+ ScheduleComposition(compositable);
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpUseComponentAlphaTextures: {
+ const OpUseComponentAlphaTextures& op = aEdit.detail().get_OpUseComponentAlphaTextures();
+ RefPtr<TextureHost> texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent());
+ RefPtr<TextureHost> texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent());
+ texOnBlack->DeserializeReadLock(op.sharedLockBlack(), this);
+ texOnWhite->DeserializeReadLock(op.sharedLockWhite(), this);
+
+ MOZ_ASSERT(texOnBlack && texOnWhite);
+ compositable->UseComponentAlphaTextures(texOnBlack, texOnWhite);
+
+ if (texOnBlack) {
+ texOnBlack->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnBlack->NumCompositableRefs() > 0);
+ }
+
+ if (texOnWhite) {
+ texOnWhite->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnWhite->NumCompositableRefs() > 0);
+ }
+
+ if (UsesImageBridge()) {
+ ScheduleComposition(compositable);
+ }
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "bad type");
+ }
+ }
+
+ return true;
+}
+
+void
+CompositableParentManager::DestroyActor(const OpDestroy& aOp)
+{
+ switch (aOp.type()) {
+ case OpDestroy::TPTextureParent: {
+ auto actor = aOp.get_PTextureParent();
+ TextureHost::ReceivedDestroy(actor);
+ break;
+ }
+ case OpDestroy::TPCompositableParent: {
+ auto actor = aOp.get_PCompositableParent();
+ CompositableHost::ReceivedDestroy(actor);
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "unsupported type");
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/system/graphics/layers/ipc/CompositableTransactionParent.h b/system/graphics/layers/ipc/CompositableTransactionParent.h
new file mode 100644
index 000000000..ca676c115
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositableTransactionParent.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+
+#include <vector> // for vector
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
+
+// Since PCompositble has two potential manager protocols, we can't just call
+// the Manager() method usually generated when there's one manager protocol,
+// so both manager protocols implement this and we keep a reference to them
+// through this interface.
+class CompositableParentManager : public HostIPCAllocator
+{
+public:
+ CompositableParentManager() {}
+
+ void DestroyActor(const OpDestroy& aOp);
+
+ void UpdateFwdTransactionId(uint64_t aTransactionId)
+ {
+ MOZ_ASSERT(mFwdTransactionId < aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+protected:
+ /**
+ * Handle the IPDL messages that affect PCompositable actors.
+ */
+ bool ReceiveCompositableUpdate(const CompositableOperation& aEdit,
+ EditReplyVector& replyv);
+
+ uint64_t mFwdTransactionId = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/CompositorBench.cpp b/system/graphics/layers/ipc/CompositorBench.cpp
new file mode 100644
index 000000000..945adafc1
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBench.cpp
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorBench.h"
+
+#ifdef MOZ_COMPOSITOR_BENCH
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/TimeStamp.h"
+#include "gfxPrefs.h"
+#include <math.h>
+#include "GeckoProfiler.h"
+
+#define TEST_STEPS 1000
+#define DURATION_THRESHOLD 30
+#define THRESHOLD_ABORT_COUNT 5
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static float SimplePseudoRandom(int aStep, int aCount) {
+ srand(aStep * 1000 + aCount);
+ return static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+}
+
+class BenchTest {
+public:
+ BenchTest(const char* aTestName)
+ : mTestName(aTestName)
+ {}
+
+ virtual ~BenchTest() {}
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {}
+ virtual void Teardown(Compositor* aCompositor) {}
+ virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) = 0;
+
+ const char* ToString() { return mTestName; }
+private:
+ const char* mTestName;
+};
+
+static void
+DrawFrameTrivialQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+ (int)(i / aScreenRect.height),
+ 1, 1);
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+static void
+DrawFrameStressQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects)
+{
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+ aScreenRect.height * SimplePseudoRandom(i, 1),
+ aScreenRect.width * SimplePseudoRandom(i, 2),
+ aScreenRect.height * SimplePseudoRandom(i, 3));
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+ transform2d = transform2d.PreRotate(SimplePseudoRandom(i, 4) * 70.f);
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+class EffectSolidColorBench : public BenchTest {
+public:
+ EffectSolidColorBench()
+ : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ float tmp;
+ float red = modff(aStep * 0.03f, &tmp);
+ EffectChain effects;
+ effects.mPrimaryEffect =
+ new EffectSolidColor(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+
+ const gfx::Rect& rect = aScreenRect;
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix4x4 transform;
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+};
+
+class EffectSolidColorTrivialBench : public BenchTest {
+public:
+ EffectSolidColorTrivialBench()
+ : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class EffectSolidColorStressBench : public BenchTest {
+public:
+ EffectSolidColorStressBench()
+ : BenchTest("EffectSolidColorStressBench (10s various EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class UploadBench : public BenchTest {
+public:
+ UploadBench()
+ : BenchTest("Upload Bench (10s 256x256 upload)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ int w = 256;
+ int h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ mTexture->Update(mSurface);
+ }
+ }
+};
+
+class TrivialTexturedQuadBench : public BenchTest {
+public:
+ TrivialTexturedQuadBench()
+ : BenchTest("Trvial Textured Quad (10s 256x256 quads)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+public:
+ StressTexturedQuadBench()
+ : BenchTest("Stress Textured Quad (10s 256x256 quads)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ virtual already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect)
+{
+ std::vector<BenchTest*> tests;
+
+ tests.push_back(new EffectSolidColorBench());
+ tests.push_back(new UploadBench());
+ tests.push_back(new EffectSolidColorTrivialBench());
+ tests.push_back(new EffectSolidColorStressBench());
+ tests.push_back(new TrivialTexturedQuadBench());
+ tests.push_back(new StressTexturedQuadBench());
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ BenchTest* test = tests[i];
+ std::vector<TimeDuration> results;
+ int testsOverThreshold = 0;
+ PROFILER_MARKER(test->ToString());
+ for (size_t j = 0; j < TEST_STEPS; j++) {
+ test->Setup(aCompositor, j);
+
+ TimeStamp start = TimeStamp::Now();
+ IntRect screenRect(aScreenRect.x, aScreenRect.y,
+ aScreenRect.width, aScreenRect.height);
+ aCompositor->BeginFrame(
+ IntRect(screenRect.x, screenRect.y,
+ screenRect.width, screenRect.height),
+ nullptr, aScreenRect, nullptr, nullptr);
+
+ test->DrawFrame(aCompositor, aScreenRect, j);
+
+ aCompositor->EndFrame();
+ results.push_back(TimeStamp::Now() - start);
+
+ if (results[j].ToMilliseconds() > DURATION_THRESHOLD) {
+ testsOverThreshold++;
+ if (testsOverThreshold == THRESHOLD_ABORT_COUNT) {
+ test->Teardown(aCompositor);
+ break;
+ }
+ } else {
+ testsOverThreshold = 0;
+ }
+ test->Teardown(aCompositor);
+ }
+
+ printf_stderr("%s\n", test->ToString());
+ printf_stderr("Run step, Time (ms)\n");
+ for (size_t j = 0; j < results.size(); j++) {
+ printf_stderr("%i,%f\n", j, (float)results[j].ToMilliseconds());
+ }
+ printf_stderr("\n", test->ToString());
+ }
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ delete tests[i];
+ }
+}
+
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect)
+{
+ static bool sRanBenchmark = false;
+ bool wantBenchmark = gfxPrefs::LayersBenchEnabled();
+ if (wantBenchmark && !sRanBenchmark) {
+ RunCompositorBench(aCompositor, aScreenRect);
+ }
+ sRanBenchmark = wantBenchmark;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
diff --git a/system/graphics/layers/ipc/CompositorBench.h b/system/graphics/layers/ipc/CompositorBench.h
new file mode 100644
index 000000000..afe31bca9
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBench.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBench_h
+#define mozilla_layers_CompositorBench_h
+
+#include "mozilla/gfx/Rect.h" // for Rect
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+// Uncomment this line to rebuild with compositor bench.
+// #define MOZ_COMPOSITOR_BENCH
+
+#ifdef MOZ_COMPOSITOR_BENCH
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect);
+#else
+static inline void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect) {}
+#endif
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
+
diff --git a/system/graphics/layers/ipc/CompositorBridgeChild.cpp b/system/graphics/layers/ipc/CompositorBridgeChild.cpp
new file mode 100644
index 000000000..f0a1b861a
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBridgeChild.cpp
@@ -0,0 +1,1149 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include <stddef.h> // for size_t
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "gfxPrefs.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/layers/TextureClient.h"// for TextureClient
+#include "mozilla/layers/TextureClientPool.h"// for TextureClientPool
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsAutoPtr.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsIObserver.h" // for nsIObserver
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop, etc
+#include "FrameLayerBuilder.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#if defined(XP_WIN)
+#include "WinUtils.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
+#include "VsyncSource.h"
+
+using mozilla::layers::LayerTransactionChild;
+using mozilla::dom::TabChildBase;
+using mozilla::Unused;
+using mozilla::gfx::GPUProcessManager;
+
+namespace mozilla {
+namespace layers {
+
+static int sShmemCreationCounter = 0;
+
+static void ResetShmemCounter()
+{
+ sShmemCreationCounter = 0;
+}
+
+static void ShmemAllocated(CompositorBridgeChild* aProtocol)
+{
+ sShmemCreationCounter++;
+ if (sShmemCreationCounter > 256) {
+ aProtocol->SendSyncWithCompositor();
+ ResetShmemCounter();
+ MOZ_PERFORMANCE_WARNING("gfx", "The number of shmem allocations is too damn high!");
+ }
+}
+
+static StaticRefPtr<CompositorBridgeChild> sCompositorBridge;
+
+Atomic<int32_t> KnowsCompositor::sSerialCounter(0);
+
+CompositorBridgeChild::CompositorBridgeChild(LayerManager *aLayerManager)
+ : mLayerManager(aLayerManager)
+ , mCanSend(false)
+ , mFwdTransactionId(0)
+ , mMessageLoop(MessageLoop::current())
+ , mSectionAllocator(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorBridgeChild::~CompositorBridgeChild()
+{
+ if (mCanSend) {
+ gfxCriticalError() << "CompositorBridgeChild was not deinitialized";
+ }
+}
+
+bool
+CompositorBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+static void DeferredDestroyCompositor(RefPtr<CompositorBridgeParent> aCompositorBridgeParent,
+ RefPtr<CompositorBridgeChild> aCompositorBridgeChild)
+{
+ aCompositorBridgeChild->Close();
+
+ if (sCompositorBridge == aCompositorBridgeChild) {
+ sCompositorBridge = nullptr;
+ }
+}
+
+void
+CompositorBridgeChild::Destroy()
+{
+ // This must not be called from the destructor!
+ mTexturesWaitingRecycled.Clear();
+
+ if (!mCanSend) {
+ return;
+ }
+
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Destroy();
+ }
+
+ if (mSectionAllocator) {
+ delete mSectionAllocator;
+ mSectionAllocator = nullptr;
+ }
+
+ // Destroying the layer manager may cause all sorts of things to happen, so
+ // let's make sure there is still a reference to keep this alive whatever
+ // happens.
+ RefPtr<CompositorBridgeChild> selfRef = this;
+
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ }
+
+ AutoTArray<PLayerTransactionChild*, 16> transactions;
+ ManagedPLayerTransactionChild(transactions);
+ for (int i = transactions.Length() - 1; i >= 0; --i) {
+ RefPtr<LayerTransactionChild> layers =
+ static_cast<LayerTransactionChild*>(transactions[i]);
+ layers->Destroy();
+ }
+
+ const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
+ for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<TextureClient> texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
+
+ if (texture) {
+ texture->Destroy();
+ }
+ }
+
+ SendWillClose();
+ mCanSend = false;
+
+ // We no longer care about unexpected shutdowns, in the remote process case.
+ mProcessToken = 0;
+
+ // The call just made to SendWillClose can result in IPC from the
+ // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the destruction
+ // of shared memory). We need to ensure this gets processed by the
+ // CompositorBridgeChild before it gets destroyed. It suffices to ensure that
+ // events already in the MessageLoop get processed before the
+ // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to
+ // handle compositor desctruction.
+
+ // From now on we can't send any message message.
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef));
+}
+
+// static
+void
+CompositorBridgeChild::ShutDown()
+{
+ if (sCompositorBridge) {
+ sCompositorBridge->Destroy();
+ do {
+ NS_ProcessNextEvent(nullptr, true);
+ } while (sCompositorBridge);
+ }
+}
+
+bool
+CompositorBridgeChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId,
+ FrameMetrics& aFrame)
+{
+ SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
+ if (data) {
+ data->CopyFrameMetrics(&aFrame);
+ return true;
+ }
+ return false;
+}
+
+/* static */ bool
+CompositorBridgeChild::InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ // There's only one compositor per child process.
+ MOZ_ASSERT(!sCompositorBridge);
+
+ RefPtr<CompositorBridgeChild> child(new CompositorBridgeChild(nullptr));
+ if (!aEndpoint.Bind(child)) {
+ NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
+ return false;
+ }
+ child->InitIPDL();
+
+ // We release this ref in DeferredDestroyCompositor.
+ sCompositorBridge = child;
+ return true;
+}
+
+/* static */ bool
+CompositorBridgeChild::ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) {
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get
+ // a false result and a MsgDropped processing error. This is okay.
+ old->Destroy();
+ }
+
+ return InitForContent(Move(aEndpoint));
+}
+
+CompositorBridgeParent*
+CompositorBridgeChild::InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurface,
+ const gfx::IntSize& aSurfaceSize)
+{
+ TimeDuration vsyncRate =
+ gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
+
+ mCompositorBridgeParent =
+ new CompositorBridgeParent(aScale, vsyncRate, aUseExternalSurface, aSurfaceSize);
+
+ bool ok = Open(mCompositorBridgeParent->GetIPCChannel(),
+ CompositorThreadHolder::Loop(),
+ ipc::ChildSide);
+ MOZ_RELEASE_ASSERT(ok);
+
+ InitIPDL();
+ mCompositorBridgeParent->InitSameProcess(aWidget, aLayerTreeId, aUseAPZ);
+ return mCompositorBridgeParent;
+}
+
+/* static */ RefPtr<CompositorBridgeChild>
+CompositorBridgeChild::CreateRemote(const uint64_t& aProcessToken,
+ LayerManager* aLayerManager,
+ Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager);
+ if (!aEndpoint.Bind(child)) {
+ return nullptr;
+ }
+ child->InitIPDL();
+ child->mProcessToken = aProcessToken;
+ return child;
+}
+
+void
+CompositorBridgeChild::InitIPDL()
+{
+ mCanSend = true;
+ AddRef();
+}
+
+void
+CompositorBridgeChild::DeallocPCompositorBridgeChild()
+{
+ Release();
+}
+
+/*static*/ CompositorBridgeChild*
+CompositorBridgeChild::Get()
+{
+ // This is only expected to be used in child processes.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ return sCompositorBridge;
+}
+
+// static
+bool
+CompositorBridgeChild::ChildProcessHasCompositorBridge()
+{
+ return sCompositorBridge != nullptr;
+}
+
+PLayerTransactionChild*
+CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier*,
+ bool*)
+{
+ LayerTransactionChild* c = new LayerTransactionChild(aId);
+ c->AddIPDLReference();
+ return c;
+}
+
+bool
+CompositorBridgeChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor)
+{
+ uint64_t childId = static_cast<LayerTransactionChild*>(actor)->GetId();
+
+ for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<SharedFrameMetricsData>& data = iter.Data();
+ if (data->GetLayersId() == childId) {
+ iter.Remove();
+ }
+ }
+ static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvInvalidateLayers(const uint64_t& aLayersId)
+{
+ if (mLayerManager) {
+ MOZ_ASSERT(aLayersId == 0);
+ FrameLayerBuilder::InvalidateAllLayers(mLayerManager);
+ } else if (aLayersId != 0) {
+ if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
+ child->InvalidateLayers();
+ }
+ }
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId,
+ const TextureFactoryIdentifier& aNewIdentifier)
+{
+ if (mLayerManager) {
+ // This case is handled directly by nsBaseWidget.
+ MOZ_ASSERT(aLayersId == 0);
+ } else if (aLayersId != 0) {
+ if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
+ child->CompositorUpdated(aNewIdentifier);
+
+ // If we still get device reset here, something must wrong when creating
+ // d3d11 devices.
+ if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ gfxCriticalError() << "Unexpected reset device processing when \
+ updating compositor.";
+ }
+ }
+ if (!mCanSend) {
+ return true;
+ }
+ SendAcknowledgeCompositorUpdate(aLayersId);
+ }
+ return true;
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+static void CalculatePluginClip(const LayoutDeviceIntRect& aBounds,
+ const nsTArray<LayoutDeviceIntRect>& aPluginClipRects,
+ const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<LayoutDeviceIntRect>& aResult,
+ LayoutDeviceIntRect& aVisibleBounds,
+ bool& aPluginIsVisible)
+{
+ aPluginIsVisible = true;
+ LayoutDeviceIntRegion contentVisibleRegion;
+ // aPluginClipRects (plugin widget origin) - contains *visible* rects
+ for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) {
+ LayoutDeviceIntRect rect = aPluginClipRects[idx];
+ // shift to content origin
+ rect.MoveBy(aBounds.x, aBounds.y);
+ // accumulate visible rects
+ contentVisibleRegion.OrWith(rect);
+ }
+ // apply layers clip (window origin)
+ LayoutDeviceIntRegion region = aParentLayerVisibleRegion;
+ region.MoveBy(-aContentOffset.x, -aContentOffset.y);
+ contentVisibleRegion.AndWith(region);
+ if (contentVisibleRegion.IsEmpty()) {
+ aPluginIsVisible = false;
+ return;
+ }
+ // shift to plugin widget origin
+ contentVisibleRegion.MoveBy(-aBounds.x, -aBounds.y);
+ for (auto iter = contentVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& rect = iter.Get();
+ aResult.AppendElement(rect);
+ aVisibleBounds.UnionRect(aVisibleBounds, rect);
+ }
+}
+#endif
+
+bool
+CompositorBridgeChild::RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins)
+{
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ NS_NOTREACHED("CompositorBridgeChild::RecvUpdatePluginConfigurations calls "
+ "unexpected on this platform.");
+ return false;
+#else
+ // Now that we are on the main thread, update plugin widget config.
+ // This should happen a little before we paint to the screen assuming
+ // the main thread is running freely.
+ DebugOnly<nsresult> rv;
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Tracks visible plugins we update, so we can hide any plugins we don't.
+ nsTArray<uintptr_t> visiblePluginIds;
+ nsIWidget* parent = nullptr;
+ for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
+ nsIWidget* widget =
+ nsIWidget::LookupRegisteredPluginWindow(aPlugins[pluginsIdx].windowId());
+ if (!widget) {
+ NS_WARNING("Unexpected, plugin id not found!");
+ continue;
+ }
+ if (!parent) {
+ parent = widget->GetParent();
+ }
+ bool isVisible = aPlugins[pluginsIdx].visible();
+ if (widget && !widget->Destroyed()) {
+ LayoutDeviceIntRect bounds;
+ LayoutDeviceIntRect visibleBounds;
+ // If the plugin is visible update it's geometry.
+ if (isVisible) {
+ // Set bounds (content origin)
+ bounds = aPlugins[pluginsIdx].bounds();
+ nsTArray<LayoutDeviceIntRect> rectsOut;
+ // This call may change the value of isVisible
+ CalculatePluginClip(bounds, aPlugins[pluginsIdx].clip(),
+ aContentOffset,
+ aParentLayerVisibleRegion,
+ rectsOut, visibleBounds, isVisible);
+ // content clipping region (widget origin)
+ rv = widget->SetWindowClipRegion(rectsOut, false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+ // This will trigger a browser window paint event for areas uncovered
+ // by a child window move, and will call invalidate on the plugin
+ // parent window which the browser owns. The latter gets picked up in
+ // our OnPaint handler and forwarded over to the plugin process async.
+ rv = widget->Resize(aContentOffset.x + bounds.x,
+ aContentOffset.y + bounds.y,
+ bounds.width, bounds.height, true);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+ }
+
+ rv = widget->Enable(isVisible);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+
+ // visible state - updated after clipping, prior to invalidating
+ rv = widget->Show(isVisible);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+
+ // Handle invalidation, this can be costly, avoid if it is not needed.
+ if (isVisible) {
+ // invalidate region (widget origin)
+#if defined(XP_WIN)
+ // Work around for flash's crummy sandbox. See bug 762948. This call
+ // digs down into the window hirearchy, invalidating regions on
+ // windows owned by other processes.
+ mozilla::widget::WinUtils::InvalidatePluginAsWorkaround(
+ widget, visibleBounds);
+#else
+ rv = widget->Invalidate(visibleBounds);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+#endif
+ visiblePluginIds.AppendElement(aPlugins[pluginsIdx].windowId());
+ }
+ }
+ }
+ // Any plugins we didn't update need to be hidden, as they are
+ // not associated with visible content.
+ nsIWidget::UpdateRegisteredPluginWindowVisibility((uintptr_t)parent, visiblePluginIds);
+ if (!mCanSend) {
+ return true;
+ }
+ SendRemotePluginsReady();
+ return true;
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+#if defined(XP_WIN)
+static void
+ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis, MessageLoop* aLoop)
+{
+ aLoop->PostTask(NewNonOwningRunnableMethod(
+ aThis, &CompositorBridgeChild::SendAllPluginsCaptured));
+}
+#endif
+
+bool
+CompositorBridgeChild::RecvCaptureAllPlugins(const uintptr_t& aParentWidget)
+{
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+ nsIWidget::CaptureRegisteredPlugins(aParentWidget);
+
+ // Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild loop,
+ // to make sure that the image updates on that thread have been processed.
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableFunction(&ScheduleSendAllPluginsCaptured, this,
+ MessageLoop::current()));
+ return true;
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected.");
+ return false;
+#endif
+}
+
+bool
+CompositorBridgeChild::RecvHideAllPlugins(const uintptr_t& aParentWidget)
+{
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ NS_NOTREACHED("CompositorBridgeChild::RecvHideAllPlugins calls "
+ "unexpected on this platform.");
+ return false;
+#else
+ MOZ_ASSERT(NS_IsMainThread());
+ nsTArray<uintptr_t> list;
+ nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list);
+ if (!mCanSend) {
+ return true;
+ }
+ SendRemotePluginsReady();
+ return true;
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+bool
+CompositorBridgeChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ // Hold a reference to keep texture pools alive. See bug 1387799
+ AutoTArray<RefPtr<TextureClientPool>,2> texturePools = mTexturePools;
+
+ if (mLayerManager) {
+ MOZ_ASSERT(aId == 0);
+ RefPtr<ClientLayerManager> m = mLayerManager->AsClientLayerManager();
+ MOZ_ASSERT(m);
+ m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ } else if (aId != 0) {
+ RefPtr<dom::TabChild> child = dom::TabChild::GetFrom(aId);
+ if (child) {
+ child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ }
+
+ for (size_t i = 0; i < texturePools.Length(); i++) {
+ texturePools[i]->ReturnDeferredClients();
+ }
+
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvOverfill(const uint32_t &aOverfill)
+{
+ for (size_t i = 0; i < mOverfillObservers.Length(); i++) {
+ mOverfillObservers[i]->RunOverfillCallback(aOverfill);
+ }
+ mOverfillObservers.Clear();
+ return true;
+}
+
+void
+CompositorBridgeChild::AddOverfillObserver(ClientLayerManager* aLayerManager)
+{
+ MOZ_ASSERT(aLayerManager);
+ mOverfillObservers.AppendElement(aLayerManager);
+}
+
+bool
+CompositorBridgeChild::RecvClearCachedResources(const uint64_t& aId)
+{
+ dom::TabChild* child = dom::TabChild::GetFrom(aId);
+ if (child) {
+ child->ClearCachedResources();
+ }
+ return true;
+}
+
+void
+CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (aWhy == AbnormalShutdown) {
+ // If the parent side runs into a problem then the actor will be destroyed.
+ // There is nothing we can do in the child side, here sets mCanSend as false.
+ gfxCriticalNote << "Receive IPC close with reason=AbnormalShutdown";
+ }
+
+ mCanSend = false;
+
+ if (mProcessToken && XRE_IsParentProcess()) {
+ GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+}
+
+bool
+CompositorBridgeChild::RecvSharedCompositorFrameMetrics(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId)
+{
+ SharedFrameMetricsData* data = new SharedFrameMetricsData(
+ metrics, handle, aLayersId, aAPZCId);
+ mFrameMetricsTable.Put(data->GetViewID(), data);
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics(
+ const ViewID& aId,
+ const uint32_t& aAPZCId)
+{
+ SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
+ // The SharedFrameMetricsData may have been removed previously if
+ // a SharedFrameMetricsData with the same ViewID but later APZCId had
+ // been store and over wrote it.
+ if (data && (data->GetAPZCId() == aAPZCId)) {
+ mFrameMetricsTable.Remove(aId);
+ }
+ return true;
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::SharedFrameMetricsData(
+ const ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId)
+ : mMutex(nullptr)
+ , mLayersId(aLayersId)
+ , mAPZCId(aAPZCId)
+{
+ mBuffer = new ipc::SharedMemoryBasic;
+ mBuffer->SetHandle(metrics);
+ mBuffer->Map(sizeof(FrameMetrics));
+ mMutex = new CrossProcessMutex(handle);
+ MOZ_COUNT_CTOR(SharedFrameMetricsData);
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::~SharedFrameMetricsData()
+{
+ // When the hash table deletes the class, delete
+ // the shared memory and mutex.
+ delete mMutex;
+ mBuffer = nullptr;
+ MOZ_COUNT_DTOR(SharedFrameMetricsData);
+}
+
+void
+CompositorBridgeChild::SharedFrameMetricsData::CopyFrameMetrics(FrameMetrics* aFrame)
+{
+ FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ mMutex->Lock();
+ *aFrame = *frame;
+ mMutex->Unlock();
+}
+
+FrameMetrics::ViewID
+CompositorBridgeChild::SharedFrameMetricsData::GetViewID()
+{
+ FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ // Not locking to read of mScrollId since it should not change after being
+ // initially set.
+ return frame->GetScrollId();
+}
+
+uint64_t
+CompositorBridgeChild::SharedFrameMetricsData::GetLayersId() const
+{
+ return mLayersId;
+}
+
+uint32_t
+CompositorBridgeChild::SharedFrameMetricsData::GetAPZCId()
+{
+ return mAPZCId;
+}
+
+
+bool
+CompositorBridgeChild::RecvRemotePaintIsReady()
+{
+ // Used on the content thread, this bounces the message to the
+ // TabParent (via the TabChild) if the notification was previously requested.
+ // XPCOM gives a soup of compiler errors when trying to do_QueryReference
+ // so I'm using static_cast<>
+ MOZ_LAYERS_LOG(("[RemoteGfx] CompositorBridgeChild received RemotePaintIsReady"));
+ RefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild));
+ if (!iTabChildBase) {
+ MOZ_LAYERS_LOG(("[RemoteGfx] Note: TabChild was released before RemotePaintIsReady. "
+ "MozAfterRemotePaint will not be sent to listener."));
+ return true;
+ }
+ TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get());
+ TabChild* tabChild = static_cast<TabChild*>(tabChildBase);
+ MOZ_ASSERT(tabChild);
+ Unused << tabChild->SendRemotePaintIsReady();
+ mWeakTabChild = nullptr;
+ return true;
+}
+
+
+void
+CompositorBridgeChild::RequestNotifyAfterRemotePaint(TabChild* aTabChild)
+{
+ MOZ_ASSERT(aTabChild, "NULL TabChild not allowed in CompositorBridgeChild::RequestNotifyAfterRemotePaint");
+ mWeakTabChild = do_GetWeakReference( static_cast<dom::TabChildBase*>(aTabChild) );
+ if (!mCanSend) {
+ return;
+ }
+ Unused << SendRequestNotifyAfterRemotePaint();
+}
+
+void
+CompositorBridgeChild::CancelNotifyAfterRemotePaint(TabChild* aTabChild)
+{
+ RefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild));
+ if (!iTabChildBase) {
+ return;
+ }
+ TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get());
+ TabChild* tabChild = static_cast<TabChild*>(tabChildBase);
+ if (tabChild == aTabChild) {
+ mWeakTabChild = nullptr;
+ }
+}
+
+bool
+CompositorBridgeChild::SendWillClose()
+{
+ MOZ_RELEASE_ASSERT(mCanSend);
+ return PCompositorBridgeChild::SendWillClose();
+}
+
+bool
+CompositorBridgeChild::SendPause()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendPause();
+}
+
+bool
+CompositorBridgeChild::SendResume()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendResume();
+}
+
+bool
+CompositorBridgeChild::SendNotifyChildCreated(const uint64_t& id)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyChildCreated(id);
+}
+
+bool
+CompositorBridgeChild::SendAdoptChild(const uint64_t& id)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAdoptChild(id);
+}
+
+bool
+CompositorBridgeChild::SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect);
+}
+
+bool
+CompositorBridgeChild::SendFlushRendering()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendFlushRendering();
+}
+
+bool
+CompositorBridgeChild::SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize, startIndex);
+}
+
+bool
+CompositorBridgeChild::SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex, intervals);
+}
+
+bool
+CompositorBridgeChild::SendNotifyRegionInvalidated(const nsIntRegion& region)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyRegionInvalidated(region);
+}
+
+bool
+CompositorBridgeChild::SendRequestNotifyAfterRemotePaint()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
+}
+
+bool
+CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
+ uint32_t aPresShellId)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
+ aPresShellId);
+}
+
+bool
+CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+}
+
+bool
+CompositorBridgeChild::SendAllPluginsCaptured()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAllPluginsCaptured();
+}
+
+PTextureChild*
+CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t&,
+ const uint64_t& aSerial)
+{
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+bool
+CompositorBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
+{
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvObserveLayerUpdate(const uint64_t& aLayersId,
+ const uint64_t& aEpoch,
+ const bool& aActive)
+{
+ // This message is sent via the window compositor, not the tab compositor -
+ // however it still has a layers id.
+ MOZ_ASSERT(aLayersId);
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (RefPtr<dom::TabParent> tab = dom::TabParent::GetTabParentFromLayersId(aLayersId)) {
+ tab->LayerTreeUpdate(aEpoch, aActive);
+ }
+ return true;
+}
+
+void
+CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
+{
+ if (!aClient) {
+ return;
+ }
+
+ if (!(aClient->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient);
+}
+
+void
+CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ if (aFwdTransactionId < client->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+void
+CompositorBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+TextureClientPool*
+CompositorBridgeChild::GetTexturePool(KnowsCompositor* aAllocator,
+ SurfaceFormat aFormat,
+ TextureFlags aFlags)
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ if (mTexturePools[i]->GetBackend() == aAllocator->GetCompositorBackendType() &&
+ mTexturePools[i]->GetMaxTextureSize() == aAllocator->GetMaxTextureSize() &&
+ mTexturePools[i]->GetFormat() == aFormat &&
+ mTexturePools[i]->GetFlags() == aFlags) {
+ return mTexturePools[i];
+ }
+ }
+
+ mTexturePools.AppendElement(
+ new TextureClientPool(aAllocator->GetCompositorBackendType(),
+ aAllocator->GetMaxTextureSize(),
+ aFormat,
+ gfx::gfxVars::TileSize(),
+ aFlags,
+ gfxPrefs::LayersTilePoolShrinkTimeout(),
+ gfxPrefs::LayersTilePoolClearTimeout(),
+ gfxPrefs::LayersTileInitialPoolSize(),
+ gfxPrefs::LayersTilePoolUnusedSize(),
+ this));
+
+ return mTexturePools.LastElement();
+}
+
+void
+CompositorBridgeChild::HandleMemoryPressure()
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+void
+CompositorBridgeChild::ClearTexturePool()
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+FixedSizeSmallShmemSectionAllocator*
+CompositorBridgeChild::GetTileLockAllocator()
+{
+ MOZ_ASSERT(IPCOpen());
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!mSectionAllocator) {
+ mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this);
+ }
+ return mSectionAllocator;
+}
+
+
+PTextureChild*
+CompositorBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ return PCompositorBridgeChild::SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, 0 /* FIXME? */, aSerial);
+}
+
+bool
+CompositorBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::DeallocShmem(aShmem);
+}
+
+widget::PCompositorWidgetChild*
+CompositorBridgeChild::AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData)
+{
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool
+CompositorBridgeChild::DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor)
+{
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+RefPtr<IAPZCTreeManager>
+CompositorBridgeChild::GetAPZCTreeManager(uint64_t aLayerTreeId)
+{
+ bool apzEnabled = false;
+ Unused << SendAsyncPanZoomEnabled(aLayerTreeId, &apzEnabled);
+
+ if (!apzEnabled) {
+ return nullptr;
+ }
+
+ PAPZCTreeManagerChild* child = SendPAPZCTreeManagerConstructor(aLayerTreeId);
+ if (!child) {
+ return nullptr;
+ }
+ APZCTreeManagerChild* parent = static_cast<APZCTreeManagerChild*>(child);
+
+ return RefPtr<IAPZCTreeManager>(parent);
+}
+
+PAPZCTreeManagerChild*
+CompositorBridgeChild::AllocPAPZCTreeManagerChild(const uint64_t& aLayersId)
+{
+ APZCTreeManagerChild* child = new APZCTreeManagerChild();
+ child->AddRef();
+ return child;
+}
+
+PAPZChild*
+CompositorBridgeChild::AllocPAPZChild(const uint64_t& aLayersId)
+{
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool
+CompositorBridgeChild::DeallocPAPZChild(PAPZChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+CompositorBridgeChild::DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor)
+{
+ APZCTreeManagerChild* parent = static_cast<APZCTreeManagerChild*>(aActor);
+ parent->Release();
+ return true;
+}
+
+void
+CompositorBridgeChild::ProcessingError(Result aCode, const char* aReason)
+{
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode);
+ }
+}
+
+void
+CompositorBridgeChild::WillEndTransaction()
+{
+ ResetShmemCounter();
+}
+
+void
+CompositorBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/system/graphics/layers/ipc/CompositorBridgeChild.h b/system/graphics/layers/ipc/CompositorBridgeChild.h
new file mode 100644
index 000000000..e5a4906b3
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBridgeChild.h
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeChild_h
+#define mozilla_layers_CompositorBridgeChild_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PCompositorBridgeChild.h"
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "nsClassHashtable.h" // for nsClassHashtable
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsHashKeys.h" // for nsUint64HashKey
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+
+namespace dom {
+class TabChild;
+} // namespace dom
+
+namespace widget {
+class CompositorWidget;
+} // namespace widget
+
+namespace layers {
+
+using mozilla::dom::TabChild;
+
+class IAPZCTreeManager;
+class APZCTreeManagerChild;
+class ClientLayerManager;
+class CompositorBridgeParent;
+class TextureClient;
+class TextureClientPool;
+struct FrameMetrics;
+
+class CompositorBridgeChild final : public PCompositorBridgeChild,
+ public TextureForwarder
+{
+ typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override);
+
+ explicit CompositorBridgeChild(LayerManager *aLayerManager);
+
+ void Destroy();
+
+ /**
+ * Lookup the FrameMetrics shared by the compositor process with the
+ * associated FrameMetrics::ViewID. The returned FrameMetrics is used
+ * in progressive paint calculations.
+ */
+ bool LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, FrameMetrics&);
+
+ /**
+ * Initialize the singleton compositor bridge for a content process.
+ */
+ static bool InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
+ static bool ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
+
+ static RefPtr<CompositorBridgeChild> CreateRemote(
+ const uint64_t& aProcessToken,
+ LayerManager* aLayerManager,
+ Endpoint<PCompositorBridgeChild>&& aEndpoint);
+
+ /**
+ * Initialize the CompositorBridgeChild, create CompositorBridgeParent, and
+ * open a same-process connection.
+ */
+ CompositorBridgeParent* InitSameProcess(
+ widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurface,
+ const gfx::IntSize& aSurfaceSize);
+
+ static CompositorBridgeChild* Get();
+
+ static bool ChildProcessHasCompositorBridge();
+
+ void AddOverfillObserver(ClientLayerManager* aLayerManager);
+
+ virtual bool
+ RecvClearCachedResources(const uint64_t& id) override;
+
+ virtual bool
+ RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd) override;
+
+ virtual bool
+ RecvInvalidateLayers(const uint64_t& aLayersId) override;
+
+ virtual bool
+ RecvCompositorUpdated(const uint64_t& aLayersId,
+ const TextureFactoryIdentifier& aNewIdentifier) override;
+
+ virtual bool
+ RecvOverfill(const uint32_t &aOverfill) override;
+
+ virtual bool
+ RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins) override;
+
+ virtual bool
+ RecvCaptureAllPlugins(const uintptr_t& aParentWidget) override;
+
+ virtual bool
+ RecvHideAllPlugins(const uintptr_t& aParentWidget) override;
+
+ virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+
+ virtual bool DeallocPTextureChild(PTextureChild* actor) override;
+
+ virtual bool
+ RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
+ virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+ /**
+ * Request that the parent tell us when graphics are ready on GPU.
+ * When we get that message, we bounce it to the TabParent via
+ * the TabChild
+ * @param tabChild The object to bounce the note to. Non-NULL.
+ */
+ void RequestNotifyAfterRemotePaint(TabChild* aTabChild);
+
+ void CancelNotifyAfterRemotePaint(TabChild* aTabChild);
+
+ // Beware that these methods don't override their super-class equivalent (which
+ // are not virtual), they just overload them.
+ // All of these Send* methods just add a sanity check (that it is not too late
+ // send a message) and forward the call to the super-class's equivalent method.
+ // This means that it is correct to call directly the super-class methods, but
+ // you won't get the extra safety provided here.
+ bool SendWillClose();
+ bool SendPause();
+ bool SendResume();
+ bool SendNotifyChildCreated(const uint64_t& id);
+ bool SendAdoptChild(const uint64_t& id);
+ bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect);
+ bool SendFlushRendering();
+ bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
+ bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex);
+ bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals);
+ bool SendNotifyRegionInvalidated(const nsIntRegion& region);
+ bool SendRequestNotifyAfterRemotePaint();
+ bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
+ bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const mozilla::CSSIntRegion& aRegion);
+ bool SendAllPluginsCaptured();
+ bool IsSameProcess() const override;
+
+ virtual bool IPCOpen() const override { return mCanSend; }
+
+ static void ShutDown();
+
+ void UpdateFwdTransactionId() { ++mFwdTransactionId; }
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set.
+ * Host side's usage is checked via CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
+
+ TextureClientPool* GetTexturePool(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags);
+ void ClearTexturePool();
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override;
+
+ void HandleMemoryPressure();
+
+ virtual MessageLoop* GetMessageLoop() const override { return mMessageLoop; }
+
+ virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PCompositorWidgetChild* AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData) override;
+ bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor) override;
+
+ RefPtr<IAPZCTreeManager> GetAPZCTreeManager(uint64_t aLayerTreeId);
+
+ PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const uint64_t& aLayersId) override;
+ bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor) override;
+
+ PAPZChild* AllocPAPZChild(const uint64_t& aLayersId) override;
+ bool DeallocPAPZChild(PAPZChild* aActor) override;
+
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ void WillEndTransaction();
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeChild();
+
+ void InitIPDL();
+ void DeallocPCompositorBridgeChild() override;
+
+ virtual PLayerTransactionChild*
+ AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool* aSuccess) override;
+
+ virtual bool DeallocPLayerTransactionChild(PLayerTransactionChild *aChild) override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvSharedCompositorFrameMetrics(const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId) override;
+
+ virtual bool RecvReleaseSharedCompositorFrameMetrics(const ViewID& aId,
+ const uint32_t& aAPZCId) override;
+
+ virtual bool
+ RecvRemotePaintIsReady() override;
+
+ bool RecvObserveLayerUpdate(const uint64_t& aLayersId,
+ const uint64_t& aEpoch,
+ const bool& aActive) override;
+
+ // Class used to store the shared FrameMetrics, mutex, and APZCId in a hash table
+ class SharedFrameMetricsData {
+ public:
+ SharedFrameMetricsData(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId);
+
+ ~SharedFrameMetricsData();
+
+ void CopyFrameMetrics(FrameMetrics* aFrame);
+ FrameMetrics::ViewID GetViewID();
+ uint64_t GetLayersId() const;
+ uint32_t GetAPZCId();
+
+ private:
+ // Pointer to the class that allows access to the shared memory that contains
+ // the shared FrameMetrics
+ RefPtr<mozilla::ipc::SharedMemoryBasic> mBuffer;
+ CrossProcessMutex* mMutex;
+ uint64_t mLayersId;
+ // Unique ID of the APZC that is sharing the FrameMetrics
+ uint32_t mAPZCId;
+ };
+
+ RefPtr<LayerManager> mLayerManager;
+ // When not multi-process, hold a reference to the CompositorBridgeParent to keep it
+ // alive. This reference should be null in multi-process.
+ RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+
+ // The ViewID of the FrameMetrics is used as the key for this hash table.
+ // While this should be safe to use since the ViewID is unique
+ nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
+
+ // Weakly hold the TabChild that made a request to be alerted when
+ // the transaction has been received.
+ nsWeakPtr mWeakTabChild; // type is TabChild
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild);
+
+ // When we receive overfill numbers, notify these client layer managers
+ AutoTArray<ClientLayerManager*,0> mOverfillObservers;
+
+ // True until the beginning of the two-step shutdown sequence of this actor.
+ bool mCanSend;
+
+ /**
+ * Transaction id of ShadowLayerForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
+
+ MessageLoop* mMessageLoop;
+
+ AutoTArray<RefPtr<TextureClientPool>,2> mTexturePools;
+
+ uint64_t mProcessToken;
+
+ FixedSizeSmallShmemSectionAllocator* mSectionAllocator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBrigedChild_h
diff --git a/system/graphics/layers/ipc/CompositorBridgeParent.cpp b/system/graphics/layers/ipc/CompositorBridgeParent.cpp
new file mode 100644
index 000000000..e650b2dcf
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBridgeParent.cpp
@@ -0,0 +1,2378 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#ifdef MOZ_WIDGET_GTK
+#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#ifdef XP_WIN
+#include "mozilla/layers/CompositorD3D11.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace std;
+
+using base::ProcessId;
+using base::Thread;
+
+ProcessId
+CompositorBridgeParentBase::GetChildProcessId()
+{
+ return OtherPid();
+}
+
+void
+CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(
+ OpNotifyNotUsed(textureId, aTransactionId));
+}
+
+void
+CompositorBridgeParentBase::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ Unused << SendParentAsyncMessages(aMessage);
+}
+
+bool
+CompositorBridgeParentBase::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem)
+{
+ PCompositorBridgeParent::DeallocShmem(aShmem);
+}
+
+base::ProcessId
+CompositorBridgeParentBase::RemotePid()
+{
+ return OtherPid();
+}
+
+bool
+CompositorBridgeParentBase::StartSharingMetrics(ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId)
+{
+ return PCompositorBridgeParent::SendSharedCompositorFrameMetrics(
+ aHandle, aMutexHandle, aLayersId, aApzcId);
+}
+
+bool
+CompositorBridgeParentBase::StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId)
+{
+ return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics(
+ aScrollId, aApzcId);
+}
+
+CompositorBridgeParent::LayerTreeState::LayerTreeState()
+ : mApzcTreeManagerParent(nullptr)
+ , mParent(nullptr)
+ , mLayerManager(nullptr)
+ , mCrossProcessParent(nullptr)
+ , mLayerTree(nullptr)
+ , mUpdatedPluginDataAvailable(false)
+ , mPendingCompositorUpdates(0)
+{
+}
+
+CompositorBridgeParent::LayerTreeState::~LayerTreeState()
+{
+ if (mController) {
+ mController->Destroy();
+ }
+}
+
+typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+static LayerTreeMap sIndirectLayerTrees;
+static StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+static void EnsureLayerTreeMapReady()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sIndirectLayerTreesLock) {
+ sIndirectLayerTreesLock = new Monitor("IndirectLayerTree");
+ mozilla::ClearOnShutdown(&sIndirectLayerTreesLock);
+ }
+}
+
+template <typename Lambda>
+inline void
+CompositorBridgeParent::ForEachIndirectLayerTree(const Lambda& aCallback)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); it++) {
+ LayerTreeState* state = &it->second;
+ if (state->mParent == this) {
+ aCallback(state, it->first);
+ }
+ }
+}
+
+/**
+ * A global map referencing each compositor by ID.
+ *
+ * This map is used by the ImageBridge protocol to trigger
+ * compositions without having to keep references to the
+ * compositor
+ */
+typedef map<uint64_t,CompositorBridgeParent*> CompositorMap;
+static StaticAutoPtr<CompositorMap> sCompositorMap;
+
+void
+CompositorBridgeParent::Setup()
+{
+ EnsureLayerTreeMapReady();
+
+ MOZ_ASSERT(!sCompositorMap);
+ sCompositorMap = new CompositorMap;
+}
+
+void
+CompositorBridgeParent::Shutdown()
+{
+ MOZ_ASSERT(sCompositorMap);
+ MOZ_ASSERT(sCompositorMap->empty());
+ sCompositorMap = nullptr;
+}
+
+void
+CompositorBridgeParent::FinishShutdown()
+{
+ // TODO: this should be empty by now...
+ sIndirectLayerTrees.clear();
+}
+
+static void SetThreadPriority()
+{
+ hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
+}
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+static int32_t
+CalculateCompositionFrameRate()
+{
+ // Used when layout.frame_rate is -1. Needs to be kept in sync with
+ // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
+ // TODO: This should actually return the vsync rate.
+ const int32_t defaultFrameRate = 60;
+ int32_t compositionFrameRatePref = gfxPrefs::LayersCompositionFrameRate();
+ if (compositionFrameRatePref < 0) {
+ // Use the same frame rate for composition as for layout.
+ int32_t layoutFrameRatePref = gfxPrefs::LayoutFrameRate();
+ if (layoutFrameRatePref < 0) {
+ // TODO: The main thread frame scheduling code consults the actual
+ // monitor refresh rate in this case. We should do the same.
+ return defaultFrameRate;
+ }
+ return layoutFrameRatePref;
+ }
+ return compositionFrameRatePref;
+}
+#endif
+
+CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
+ : mMutex("CompositorVsyncScheduler.Observer.Mutex")
+ , mOwner(aOwner)
+{
+}
+
+CompositorVsyncScheduler::Observer::~Observer()
+{
+ MOZ_ASSERT(!mOwner);
+}
+
+bool
+CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+ MutexAutoLock lock(mMutex);
+ if (!mOwner) {
+ return false;
+ }
+ return mOwner->NotifyVsync(aVsyncTimestamp);
+}
+
+void
+CompositorVsyncScheduler::Observer::Destroy()
+{
+ MutexAutoLock lock(mMutex);
+ mOwner = nullptr;
+}
+
+CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
+ widget::CompositorWidget* aWidget)
+ : mCompositorBridgeParent(aCompositorBridgeParent)
+ , mLastCompose(TimeStamp::Now())
+ , mIsObservingVsync(false)
+ , mNeedsComposite(0)
+ , mVsyncNotificationsSkipped(0)
+ , mWidget(aWidget)
+ , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
+ , mCurrentCompositeTask(nullptr)
+ , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
+ , mSetNeedsCompositeTask(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU);
+ mVsyncObserver = new Observer(this);
+
+ // mAsapScheduling is set on the main thread during init,
+ // but is only accessed after on the compositor thread.
+ mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
+ gfxPlatform::IsInLayoutAsapMode();
+}
+
+CompositorVsyncScheduler::~CompositorVsyncScheduler()
+{
+ MOZ_ASSERT(!mIsObservingVsync);
+ MOZ_ASSERT(!mVsyncObserver);
+ // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
+ mCompositorBridgeParent = nullptr;
+}
+
+void
+CompositorVsyncScheduler::Destroy()
+{
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ UnobserveVsync();
+ mVsyncObserver->Destroy();
+ mVsyncObserver = nullptr;
+
+ CancelCurrentSetNeedsCompositeTask();
+ CancelCurrentCompositeTask();
+}
+
+void
+CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
+{
+ // can be called from the compositor or vsync thread
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<TimeStamp>(this, &CompositorVsyncScheduler::Composite,
+ aCompositeTimestamp);
+ mCurrentCompositeTask = task;
+ ScheduleTask(task.forget(), 0);
+ }
+}
+
+void
+CompositorVsyncScheduler::ScheduleComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mAsapScheduling) {
+ // Used only for performance testing purposes
+ PostCompositeTask(TimeStamp::Now());
+ } else {
+ SetNeedsComposite();
+ }
+}
+
+void
+CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ if (mSetNeedsCompositeTask) {
+ mSetNeedsCompositeTask->Cancel();
+ mSetNeedsCompositeTask = nullptr;
+ }
+ mNeedsComposite = 0;
+}
+
+/**
+ * TODO Potential performance heuristics:
+ * If a composite takes 17 ms, do we composite ASAP or wait until next vsync?
+ * If a layer transaction comes after vsync, do we composite ASAP or wait until
+ * next vsync?
+ * How many skipped vsync events until we stop listening to vsync events?
+ */
+void
+CompositorVsyncScheduler::SetNeedsComposite()
+{
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite);
+ mSetNeedsCompositeTask = task;
+ ScheduleTask(task.forget(), 0);
+ return;
+ } else {
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ mSetNeedsCompositeTask = nullptr;
+ }
+
+ mNeedsComposite++;
+ if (!mIsObservingVsync && mNeedsComposite) {
+ ObserveVsync();
+ }
+}
+
+bool
+CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+ // Called from the vsync dispatch thread. When in the GPU Process, that's
+ // the same as the compositor thread.
+ MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(!NS_IsMainThread());
+ PostCompositeTask(aVsyncTimestamp);
+ return true;
+}
+
+void
+CompositorVsyncScheduler::CancelCurrentCompositeTask()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask) {
+ mCurrentCompositeTask->Cancel();
+ mCurrentCompositeTask = nullptr;
+ }
+}
+
+void
+CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ {
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ mCurrentCompositeTask = nullptr;
+ }
+
+ if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
+ // We can sometimes get vsync timestamps that are in the past
+ // compared to the last compose with force composites.
+ // In those cases, wait until the next vsync;
+ return;
+ }
+
+ MOZ_ASSERT(mCompositorBridgeParent);
+ if (!mAsapScheduling && mCompositorBridgeParent->IsPendingComposite()) {
+ // If previous composite is still on going, finish it and does a next
+ // composite in a next vsync.
+ mCompositorBridgeParent->FinishPendingComposite();
+ return;
+ }
+
+ DispatchTouchEvents(aVsyncTimestamp);
+
+ if (mNeedsComposite || mAsapScheduling) {
+ mNeedsComposite = 0;
+ mLastCompose = aVsyncTimestamp;
+ ComposeToTarget(nullptr);
+ mVsyncNotificationsSkipped = 0;
+ } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
+ UnobserveVsync();
+ }
+}
+
+void
+CompositorVsyncScheduler::OnForceComposeToTarget()
+{
+ /**
+ * bug 1138502 - There are cases such as during long-running window resizing events
+ * where we receive many sync RecvFlushComposites. We also get vsync notifications which
+ * will increment mVsyncNotificationsSkipped because a composite just occurred. After
+ * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
+ * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
+ * force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
+ * free and this oscillating behavior causes a performance hit. In order to avoid this problem,
+ * we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
+ */
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mVsyncNotificationsSkipped = 0;
+}
+
+void
+CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ OnForceComposeToTarget();
+ mLastCompose = TimeStamp::Now();
+ ComposeToTarget(aTarget, aRect);
+}
+
+bool
+CompositorVsyncScheduler::NeedsComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mNeedsComposite;
+}
+
+void
+CompositorVsyncScheduler::ObserveVsync()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(mVsyncObserver);
+ mIsObservingVsync = true;
+}
+
+void
+CompositorVsyncScheduler::UnobserveVsync()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(nullptr);
+ mIsObservingVsync = false;
+}
+
+void
+CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
+{
+}
+
+void
+CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
+ int aTime)
+{
+ MOZ_ASSERT(CompositorThreadHolder::Loop());
+ MOZ_ASSERT(aTime >= 0);
+ CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime);
+}
+
+void
+CompositorVsyncScheduler::ResumeComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLastCompose = TimeStamp::Now();
+ ComposeToTarget(nullptr);
+}
+
+void
+CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(mCompositorBridgeParent);
+ mCompositorBridgeParent->CompositeToTarget(aTarget, aRect);
+}
+
+static inline MessageLoop*
+CompositorLoop()
+{
+ return CompositorThreadHolder::Loop();
+}
+
+CompositorBridgeParent::CompositorBridgeParent(CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize)
+ : mWidget(nullptr)
+ , mScale(aScale)
+ , mVsyncRate(aVsyncRate)
+ , mIsTesting(false)
+ , mPendingTransaction(0)
+ , mPaused(false)
+ , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
+ , mEGLSurfaceSize(aSurfaceSize)
+ , mPauseCompositionMonitor("PauseCompositionMonitor")
+ , mResumeCompositionMonitor("ResumeCompositionMonitor")
+ , mResetCompositorMonitor("ResetCompositorMonitor")
+ , mRootLayerTreeID(0)
+ , mOverrideComposeReadiness(false)
+ , mForceCompositionTask(nullptr)
+ , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
+ , mCompositorScheduler(nullptr)
+ , mPaintTime(TimeDuration::Forever())
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ , mLastPluginUpdateLayerTreeId(0)
+ , mDeferPluginWindows(false)
+ , mPluginWindowsHidden(false)
+#endif
+{
+ // Always run destructor on the main thread
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ bool aUseAPZ)
+{
+ mWidget = aWidget;
+ mRootLayerTreeID = aLayerTreeId;
+ if (aUseAPZ) {
+ mApzcTreeManager = new APZCTreeManager();
+ }
+
+ // IPDL initialization. mSelfRef is cleared in DeferredDestroy.
+ SetOtherProcessId(base::GetCurrentProcId());
+ mSelfRef = this;
+
+ Initialize();
+}
+
+bool
+CompositorBridgeParent::Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ return false;
+ }
+ mSelfRef = this;
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvInitialize(const uint64_t& aRootLayerTreeId)
+{
+ mRootLayerTreeID = aRootLayerTreeId;
+
+ Initialize();
+ return true;
+}
+
+void
+CompositorBridgeParent::Initialize()
+{
+ MOZ_ASSERT(CompositorThread(),
+ "The compositor thread must be Initialized before instanciating a CompositorBridgeParent.");
+
+ mCompositorID = 0;
+ // FIXME: This holds on the the fact that right now the only thing that
+ // can destroy this instance is initialized on the compositor thread after
+ // this task has been processed.
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableFunction(&AddCompositor,
+ this, &mCompositorID));
+
+ CompositorLoop()->PostTask(NewRunnableFunction(SetThreadPriority));
+
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
+ }
+
+ LayerScope::SetPixelScale(mScale.scale);
+
+ mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+}
+
+bool
+CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier)
+{
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ ResetCompositorTask(aBackendHints, &newIdentifier);
+
+ if (newIdentifier) {
+ *aResult = true;
+ *aOutIdentifier = newIdentifier.value();
+ } else {
+ *aResult = false;
+ }
+
+ return true;
+}
+
+uint64_t
+CompositorBridgeParent::RootLayerTreeId()
+{
+ MOZ_ASSERT(mRootLayerTreeID);
+ return mRootLayerTreeID;
+}
+
+CompositorBridgeParent::~CompositorBridgeParent()
+{
+ InfallibleTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ // We expect all textures to be destroyed by now.
+ MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+}
+
+void
+CompositorBridgeParent::ForceIsFirstPaint()
+{
+ mCompositionManager->ForceIsFirstPaint();
+}
+
+void
+CompositorBridgeParent::StopAndClearResources()
+{
+ if (mForceCompositionTask) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ }
+
+ mPaused = true;
+
+ // Ensure that the layer manager is destroyed before CompositorBridgeChild.
+ if (mLayerManager) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([this] (LayerTreeState* lts, uint64_t) -> void {
+ mLayerManager->ClearCachedResources(lts->mRoot);
+ lts->mLayerManager = nullptr;
+ lts->mParent = nullptr;
+ });
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ mCompositionManager = nullptr;
+ }
+
+ if (mCompositor) {
+ mCompositor->DetachWidget();
+ mCompositor->Destroy();
+ mCompositor = nullptr;
+ }
+
+ // This must be destroyed now since it accesses the widget.
+ if (mCompositorScheduler) {
+ mCompositorScheduler->Destroy();
+ mCompositorScheduler = nullptr;
+ }
+
+ // After this point, it is no longer legal to access the widget.
+ mWidget = nullptr;
+}
+
+bool
+CompositorBridgeParent::RecvWillClose()
+{
+ StopAndClearResources();
+ return true;
+}
+
+void CompositorBridgeParent::DeferredDestroy()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(mCompositorThreadHolder);
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+bool
+CompositorBridgeParent::RecvPause()
+{
+ PauseComposition();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvResume()
+{
+ ResumeComposition();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect)
+{
+ RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
+ MOZ_ASSERT(target);
+ if (!target) {
+ // We kill the content process rather than have it continue with an invalid
+ // snapshot, that may be too harsh and we could decide to return some sort
+ // of error to the child process and let it deal with it...
+ return false;
+ }
+ ForceComposeToTarget(target, &aRect);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvFlushRendering()
+{
+ if (mCompositorScheduler->NeedsComposite())
+ {
+ CancelCurrentCompositeTask();
+ ForceComposeToTarget(nullptr);
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvForcePresent()
+{
+ // During the shutdown sequence mLayerManager may be null
+ if (mLayerManager) {
+ mLayerManager->ForcePresent();
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
+{
+ if (mLayerManager) {
+ mLayerManager->AddInvalidRegion(aRegion);
+ }
+ return true;
+}
+
+void
+CompositorBridgeParent::Invalidate()
+{
+ if (mLayerManager && mLayerManager->GetRoot()) {
+ mLayerManager->AddInvalidRegion(
+ mLayerManager->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+}
+
+bool
+CompositorBridgeParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
+{
+ if (mLayerManager) {
+ *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
+ } else {
+ *aOutStartIndex = 0;
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex,
+ InfallibleTArray<float>* intervals)
+{
+ if (mLayerManager) {
+ mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId)
+{
+ ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+ return true;
+}
+
+void
+CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const Maybe<uint32_t>& aPresShellId)
+{
+ if (mLayerManager) {
+ mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId);
+
+ // We need to recomposite to update the minimap.
+ ScheduleComposition();
+ }
+}
+
+bool
+CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ if (mLayerManager) {
+ mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion);
+
+ // We need to recomposite to update the minimap.
+ ScheduleComposition();
+ }
+ return true;
+}
+
+void
+CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
+{
+ StopAndClearResources();
+
+ RemoveCompositor(mCompositorID);
+
+ mCompositionManager = nullptr;
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ClearTree();
+ mApzcTreeManager = nullptr;
+ }
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mRootLayerTreeID);
+ }
+
+ // There are chances that the ref count reaches zero on the main thread shortly
+ // after this function returns while some ipdl code still needs to run on
+ // this thread.
+ // We must keep the compositor parent alive untill the code handling message
+ // reception is finished on this thread.
+ mSelfRef = this;
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy));
+}
+
+void
+CompositorBridgeParent::ScheduleRenderOnCompositorThread()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ScheduleComposition));
+}
+
+void
+CompositorBridgeParent::InvalidateOnCompositorThread()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::Invalidate));
+}
+
+void
+CompositorBridgeParent::PauseComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "PauseComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mPauseCompositionMonitor);
+
+ if (!mPaused) {
+ mPaused = true;
+
+ mCompositor->Pause();
+
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+
+ // if anyone's waiting to make sure that composition really got paused, tell them
+ lock.NotifyAll();
+}
+
+void
+CompositorBridgeParent::ResumeComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "ResumeComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ if (!mCompositor->Resume()) {
+ lock.NotifyAll();
+ return;
+ }
+
+ mPaused = false;
+
+ Invalidate();
+ mCompositorScheduler->ResumeComposition();
+
+ // if anyone's waiting to make sure that composition really got resumed, tell them
+ lock.NotifyAll();
+}
+
+void
+CompositorBridgeParent::ForceComposition()
+{
+ // Cancel the orientation changed state to force composition
+ mForceCompositionTask = nullptr;
+ ScheduleRenderOnCompositorThread();
+}
+
+void
+CompositorBridgeParent::CancelCurrentCompositeTask()
+{
+ mCompositorScheduler->CancelCurrentCompositeTask();
+}
+
+void
+CompositorBridgeParent::SetEGLSurfaceSize(int width, int height)
+{
+ NS_ASSERTION(mUseExternalSurfaceSize, "Compositor created without UseExternalSurfaceSize provided");
+ mEGLSurfaceSize.SizeTo(width, height);
+ if (mCompositor) {
+ mCompositor->SetDestinationSurfaceSize(gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height));
+ }
+}
+
+void
+CompositorBridgeParent::ResumeCompositionAndResize(int width, int height)
+{
+ SetEGLSurfaceSize(width, height);
+ ResumeComposition();
+}
+
+/*
+ * This will execute a pause synchronously, waiting to make sure that the compositor
+ * really is paused.
+ */
+void
+CompositorBridgeParent::SchedulePauseOnCompositorThread()
+{
+ MonitorAutoLock lock(mPauseCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::PauseComposition));
+
+ // Wait until the pause has actually been processed by the compositor thread
+ lock.Wait();
+}
+
+bool
+CompositorBridgeParent::ScheduleResumeOnCompositorThread()
+{
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ResumeComposition));
+
+ // Wait until the resume has actually been processed by the compositor thread
+ lock.Wait();
+
+ return !mPaused;
+}
+
+bool
+CompositorBridgeParent::ScheduleResumeOnCompositorThread(int width, int height)
+{
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod
+ <int, int>(this,
+ &CompositorBridgeParent::ResumeCompositionAndResize,
+ width, height));
+
+ // Wait until the resume has actually been processed by the compositor thread
+ lock.Wait();
+
+ return !mPaused;
+}
+
+void
+CompositorBridgeParent::ScheduleTask(already_AddRefed<CancelableRunnable> task, int time)
+{
+ if (time == 0) {
+ MessageLoop::current()->PostTask(Move(task));
+ } else {
+ MessageLoop::current()->PostDelayedTask(Move(task), time);
+ }
+}
+
+void
+CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime)
+{
+ // We get a lot of paint timings for things with empty transactions.
+ if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) {
+ return;
+ }
+
+ mLayerManager->SetPaintTime(aPaintTime);
+}
+
+void
+CompositorBridgeParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, bool aHitTestUpdate)
+{
+ if (!aIsRepeatTransaction &&
+ mLayerManager &&
+ mLayerManager->GetRoot()) {
+ // Process plugin data here to give time for them to update before the next
+ // composition.
+ bool pluginsUpdatedFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this, nullptr,
+ &pluginsUpdatedFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If plugins haven't been updated, stop waiting.
+ if (!pluginsUpdatedFlag) {
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ }
+#endif
+
+ if (mApzcTreeManager && aHitTestUpdate) {
+ mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID,
+ mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
+ }
+
+ mLayerManager->NotifyShadowTreeTransaction();
+ }
+ if (aScheduleComposite) {
+ ScheduleComposition();
+ }
+}
+
+void
+CompositorBridgeParent::ScheduleComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ mCompositorScheduler->ScheduleComposition();
+}
+
+// Go down the composite layer tree, setting properties to match their
+// content-side counterparts.
+/* static */ void
+CompositorBridgeParent::SetShadowProperties(Layer* aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer *layer)
+ {
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowProperties(maskLayer);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ SetShadowProperties(layer->GetAncestorMaskLayerAt(i));
+ }
+
+ // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
+ LayerComposite* layerComposite = layer->AsLayerComposite();
+ // Set the layerComposite's base transform to the layer's base transform.
+ layerComposite->SetShadowBaseTransform(layer->GetBaseTransform());
+ layerComposite->SetShadowTransformSetByAnimation(false);
+ layerComposite->SetShadowVisibleRegion(layer->GetVisibleRegion());
+ layerComposite->SetShadowClipRect(layer->GetClipRect());
+ layerComposite->SetShadowOpacity(layer->GetOpacity());
+ layerComposite->SetShadowOpacitySetByAnimation(false);
+ }
+ );
+}
+
+void
+CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
+{
+ profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
+ PROFILER_LABEL("CompositorBridgeParent", "Composite",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "Composite can only be called on the compositor thread");
+ TimeStamp start = TimeStamp::Now();
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeDuration scheduleDelta = TimeStamp::Now() - mCompositorScheduler->GetExpectedComposeStartTime();
+ if (scheduleDelta > TimeDuration::FromMilliseconds(2) ||
+ scheduleDelta < TimeDuration::FromMilliseconds(-2)) {
+ printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n",
+ scheduleDelta.ToMilliseconds());
+ }
+#endif
+
+ if (!CanComposite()) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(start, end);
+ return;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (!mWaitForPluginsUntil.IsNull() &&
+ mWaitForPluginsUntil > start) {
+ mHaveBlockedForPlugins = true;
+ ScheduleComposition();
+ return;
+ }
+#endif
+
+ /*
+ * AutoResolveRefLayers handles two tasks related to Windows and Linux
+ * plugin window management:
+ * 1) calculating if we have remote content in the view. If we do not have
+ * remote content, all plugin windows for this CompositorBridgeParent (window)
+ * can be hidden since we do not support plugins in chrome when running
+ * under e10s.
+ * 2) Updating plugin position, size, and clip. We do this here while the
+ * remote layer tree is hooked up to to chrome layer tree. This is needed
+ * since plugin clipping can depend on chrome (for example, due to tab modal
+ * prompts). Updates in step 2 are applied via an async ipc message sent
+ * to the main thread.
+ */
+ bool hasRemoteContent = false;
+ bool updatePluginsFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this,
+ &hasRemoteContent,
+ &updatePluginsFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // We do not support plugins in local content. When switching tabs
+ // to local pages, hide every plugin associated with the window.
+ if (!hasRemoteContent && gfxVars::BrowserTabsRemoteAutostart() &&
+ mCachedPluginData.Length()) {
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ mCachedPluginData.Clear();
+ }
+#endif
+
+ if (aTarget) {
+ mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
+ } else {
+ mLayerManager->BeginTransaction();
+ }
+
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ if (mForceCompositionTask && !mOverrideComposeReadiness) {
+ if (mCompositionManager->ReadyForCompose()) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ } else {
+ return;
+ }
+ }
+
+ mCompositionManager->ComputeRotation();
+
+ TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
+ bool requestNextFrame = mCompositionManager->TransformShadowTree(time, mVsyncRate);
+ if (requestNextFrame) {
+ ScheduleComposition();
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If we have visible windowed plugins then we need to wait for content (and
+ // then the plugins) to have been updated by the active animation.
+ if (!mPluginWindowsHidden && mCachedPluginData.Length()) {
+ mWaitForPluginsUntil = mCompositorScheduler->GetLastComposeTime() + (mVsyncRate * 2);
+ }
+#endif
+ }
+
+ RenderTraceLayers(mLayerManager->GetRoot(), "0000");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::DumpHostLayers()) {
+ printf_stderr("Painting --- compositing layer tree:\n");
+ mLayerManager->Dump(/* aSorted = */ true);
+ }
+#endif
+ mLayerManager->SetDebugOverlayWantsNextFrame(false);
+ mLayerManager->EndTransaction(time);
+
+ if (!aTarget) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(start, end);
+ }
+
+ // We're not really taking advantage of the stored composite-again-time here.
+ // We might be able to skip the next few composites altogether. However,
+ // that's a bit complex to implement and we'll get most of the advantage
+ // by skipping compositing when we detect there's nothing invalid. This is why
+ // we do "composite until" rather than "composite again at".
+ if (!mCompositor->GetCompositeUntilTime().IsNull() ||
+ mLayerManager->DebugOverlayWantsNextFrame()) {
+ ScheduleComposition();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeDuration executionTime = TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime();
+ TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
+ int32_t frameRate = CalculateCompositionFrameRate();
+ if (frameRate > 0) {
+ frameBudget = TimeDuration::FromSeconds(1.0 / frameRate);
+ }
+ if (executionTime > frameBudget) {
+ printf_stderr("Compositor: Composite execution took %4.1f ms\n",
+ executionTime.ToMilliseconds());
+ }
+#endif
+
+ // 0 -> Full-tilt composite
+ if (gfxPrefs::LayersCompositionFrameRate() == 0
+ || mLayerManager->GetCompositor()->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS) {
+ // Special full-tilt composite mode for performance testing
+ ScheduleComposition();
+ }
+ mCompositor->SetCompositionTime(TimeStamp());
+
+ profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
+}
+
+bool
+CompositorBridgeParent::RecvRemotePluginsReady()
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ mWaitForPluginsUntil = TimeStamp();
+ if (mHaveBlockedForPlugins) {
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ } else {
+ ScheduleComposition();
+ }
+ return true;
+#else
+ NS_NOTREACHED("CompositorBridgeParent::RecvRemotePluginsReady calls "
+ "unexpected on this platform.");
+ return false;
+#endif
+}
+
+void
+CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
+{
+ PROFILER_LABEL("CompositorBridgeParent", "ForceComposeToTarget",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ AutoRestore<bool> override(mOverrideComposeReadiness);
+ mOverrideComposeReadiness = true;
+ mCompositorScheduler->ForceComposeToTarget(aTarget, aRect);
+}
+
+PAPZCTreeManagerParent*
+CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+
+ // This message doubles as initialization
+ MOZ_ASSERT(!mApzcTreeManager);
+ mApzcTreeManager = new APZCTreeManager();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_ASSERT(state.mParent);
+ MOZ_ASSERT(!state.mApzcTreeManagerParent);
+ state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager());
+
+ return state.mApzcTreeManagerParent;
+}
+
+bool
+CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PAPZParent*
+CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool
+CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
+{
+ RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+ *aHasAPZ = AsyncPanZoomEnabled();
+ return true;
+}
+
+RefPtr<APZCTreeManager>
+CompositorBridgeParent::GetAPZCTreeManager()
+{
+ return mApzcTreeManager;
+}
+
+bool
+CompositorBridgeParent::CanComposite()
+{
+ return mLayerManager &&
+ mLayerManager->GetRoot() &&
+ !mPaused;
+}
+
+void
+CompositorBridgeParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
+ bool aIsFirstPaint)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (!aIsFirstPaint &&
+ !mCompositionManager->IsFirstPaint() &&
+ mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) {
+ if (mForceCompositionTask != nullptr) {
+ mForceCompositionTask->Cancel();
+ }
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &CompositorBridgeParent::ForceComposition);
+ mForceCompositionTask = task;
+ ScheduleTask(task.forget(), gfxPrefs::OrientationSyncMillis());
+ }
+}
+
+void
+CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aUnused,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate)
+{
+ ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
+
+ // Instruct the LayerManager to update its render bounds now. Since all the orientation
+ // change, dimension change would be done at the stage, update the size here is free of
+ // race condition.
+ mLayerManager->UpdateRenderBounds(aTargetConfig.naturalBounds());
+ mLayerManager->SetRegionToClear(aTargetConfig.clearRegion());
+ mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation());
+
+ mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId);
+ Layer* root = aLayerTree->GetRoot();
+ mLayerManager->SetRoot(root);
+
+ if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+
+ mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, root, aIsFirstPaint,
+ mRootLayerTreeID, aPaintSequenceNumber);
+ }
+
+ // The transaction ID might get reset to 1 if the page gets reloaded, see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
+ // Otherwise, it should be continually increasing.
+ MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransaction);
+ mPendingTransaction = aTransactionId;
+
+ if (root) {
+ SetShadowProperties(root);
+ }
+ if (aScheduleComposite) {
+ ScheduleComposition();
+ if (mPaused) {
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+ mLayerManager->NotifyShadowTreeTransaction();
+}
+
+void
+CompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
+{
+ ScheduleComposition();
+}
+
+bool
+CompositorBridgeParent::SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime)
+{
+ if (aTime.IsNull()) {
+ return false;
+ }
+
+ mIsTesting = true;
+ mTestTime = aTime;
+
+ bool testComposite = mCompositionManager &&
+ mCompositorScheduler->NeedsComposite();
+
+ // Update but only if we were already scheduled to animate
+ if (testComposite) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime, mVsyncRate);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is wating for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+
+ return true;
+}
+
+void
+CompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
+{
+ mIsTesting = false;
+}
+
+void
+CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+{
+ // NOTE: This should only be used for testing. For example, when mIsTesting is
+ // true or when called from test-only methods like
+ // LayerTransactionParent::RecvGetAnimationTransform.
+
+ // Synchronously update the layer tree
+ if (aLayerTree->GetRoot()) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
+ bool requestNextFrame =
+ mCompositionManager->TransformShadowTree(time, mVsyncRate,
+ AsyncCompositionManager::TransformsToSkip::APZ);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is waiting for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+}
+
+bool
+CompositorBridgeParent::RecvGetFrameUniformity(FrameUniformityData* aOutData)
+{
+ mCompositionManager->GetFrameUniformity(aOutData);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvRequestOverfill()
+{
+ uint32_t overfillRatio = mCompositor->GetFillRatio();
+ Unused << SendOverfill(overfillRatio);
+ return true;
+}
+
+void
+CompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree)
+{
+ MOZ_ASSERT(mApzcTreeManager);
+ uint64_t layersId = aLayerTree->GetId();
+ if (layersId == 0) {
+ // The request is coming from the parent-process layer tree, so we should
+ // use the compositor's root layer tree id.
+ layersId = mRootLayerTreeID;
+ }
+ RefPtr<CompositorBridgeParent> self = this;
+ APZThreadUtils::RunOnControllerThread(NS_NewRunnableFunction([=] () {
+ self->mApzcTreeManager->FlushApzRepaints(layersId);
+ }));
+}
+
+void
+CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ *aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
+}
+
+void
+CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ if (!mApzcTreeManager) {
+ return;
+ }
+ // Need to specifically bind this since it's overloaded.
+ void (APZCTreeManager::*setTargetApzcFunc)
+ (uint64_t, const nsTArray<ScrollableLayerGuid>&) =
+ &APZCTreeManager::SetTargetAPZC;
+ RefPtr<Runnable> task = NewRunnableMethod
+ <uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>>
+ (mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
+ APZThreadUtils::RunOnControllerThread(task.forget());
+
+}
+
+void
+CompositorBridgeParent::InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints)
+{
+ NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager");
+ NS_ASSERTION(!mCompositor, "Already initialised mCompositor");
+
+ mCompositor = NewCompositor(aBackendHints);
+ if (!mCompositor) {
+ return;
+ }
+
+ mLayerManager = new LayerManagerComposite(mCompositor);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager;
+}
+
+RefPtr<Compositor>
+CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHints)
+{
+ for (size_t i = 0; i < aBackendHints.Length(); ++i) {
+ RefPtr<Compositor> compositor;
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
+ compositor = new CompositorOGL(this,
+ mWidget,
+ mEGLSurfaceSize.width,
+ mEGLSurfaceSize.height,
+ mUseExternalSurfaceSize);
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
+#ifdef MOZ_WIDGET_GTK
+ if (gfxVars::UseXRender()) {
+ compositor = new X11BasicCompositor(this, mWidget);
+ } else
+#endif
+ {
+ compositor = new BasicCompositor(this, mWidget);
+ }
+#ifdef XP_WIN
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
+ compositor = new CompositorD3D11(this, mWidget);
+#endif
+ }
+ nsCString failureReason;
+ if (compositor && compositor->Initialize(&failureReason)) {
+ if (failureReason.IsEmpty()){
+ failureReason = "SUCCESS";
+ }
+
+ compositor->SetCompositorID(mCompositorID);
+ return compositor;
+ }
+
+ // report any failure reasons here
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL){
+ gfxCriticalNote << "[OPENGL] Failed to init compositor with reason: "
+ << failureReason.get();
+ }
+#ifdef XP_WIN
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){
+ gfxCriticalNote << "[D3D11] Failed to init compositor with reason: "
+ << failureReason.get();
+ }
+#endif
+ }
+
+ return nullptr;
+}
+
+PLayerTransactionParent*
+CompositorBridgeParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess)
+{
+ MOZ_ASSERT(aId == 0);
+
+ InitializeLayerManager(aBackendHints);
+
+ if (!mLayerManager) {
+ NS_WARNING("Failed to initialise Compositor");
+ *aSuccess = false;
+ LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0);
+ p->AddIPDLReference();
+ return p;
+ }
+
+ mCompositionManager = new AsyncCompositionManager(mLayerManager);
+ *aSuccess = true;
+
+ *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier();
+ LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool
+CompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor)
+{
+ static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent(uint64_t id)
+{
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ return it != sCompositorMap->end() ? it->second : nullptr;
+}
+
+void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor, uint64_t* outID)
+{
+ static uint64_t sNextID = 1;
+
+ ++sNextID;
+ (*sCompositorMap)[sNextID] = compositor;
+ *outID = sNextID;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id)
+{
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ if (it == sCompositorMap->end()) {
+ return nullptr;
+ }
+ CompositorBridgeParent *retval = it->second;
+ sCompositorMap->erase(it);
+ return retval;
+}
+
+void
+CompositorBridgeParent::NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end())
+ return;
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget)
+ return;
+
+ RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
+ if (!obs)
+ return;
+
+ obs->NotifyVsync(aTimeStamp);
+}
+
+bool
+CompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvNotifyChildRecreated(const uint64_t& aChild)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) {
+ // Invalid to register the same layer tree twice.
+ return false;
+ }
+
+ NotifyChildCreated(aChild);
+ return true;
+}
+
+void
+CompositorBridgeParent::NotifyChildCreated(uint64_t aChild)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ sIndirectLayerTrees[aChild].mParent = this;
+ sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
+}
+
+bool
+CompositorBridgeParent::RecvAdoptChild(const uint64_t& child)
+{
+ APZCTreeManagerParent* parent;
+ {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ if (sIndirectLayerTrees[child].mLayerTree) {
+ sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager);
+ }
+ parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
+ }
+
+ if (mApzcTreeManager && parent) {
+ parent->ChildAdopted(mApzcTreeManager);
+ }
+ return true;
+}
+
+static void
+EraseLayerState(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ auto iter = sIndirectLayerTrees.find(aId);
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent* parent = iter->second.mParent;
+ if (parent) {
+ parent->ClearApproximatelyVisibleRegions(aId, Nothing());
+ }
+
+ sIndirectLayerTrees.erase(iter);
+ }
+}
+
+/*static*/ void
+CompositorBridgeParent::DeallocateLayerTreeId(uint64_t aId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // Here main thread notifies compositor to remove an element from
+ // sIndirectLayerTrees. This removed element might be queried soon.
+ // Checking the elements of sIndirectLayerTrees exist or not before using.
+ if (!CompositorLoop()) {
+ gfxCriticalError() << "Attempting to post to a invalid Compositor Loop";
+ return;
+ }
+ CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId));
+}
+
+static void
+UpdateControllerForLayersId(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ // Adopt ref given to us by SetControllerForLayerTree()
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mController =
+ already_AddRefed<GeckoContentController>(aController);
+}
+
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
+ uint64_t aLayersId,
+ Layer* aRoot,
+ GeckoContentController* aController)
+ : mLayersId(aLayersId)
+{
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mRoot = aRoot;
+ sIndirectLayerTrees[aLayersId].mController = aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mLayersId);
+}
+
+/*static*/ void
+CompositorBridgeParent::SetControllerForLayerTree(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ // This ref is adopted by UpdateControllerForLayersId().
+ aController->AddRef();
+ CompositorLoop()->PostTask(NewRunnableFunction(&UpdateControllerForLayersId,
+ aLayersId,
+ aController));
+}
+
+/*static*/ already_AddRefed<APZCTreeManager>
+CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId)
+{
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ LayerTreeState* lts = &cit->second;
+
+ RefPtr<APZCTreeManager> apzctm = lts->mParent
+ ? lts->mParent->mApzcTreeManager.get()
+ : nullptr;
+ return apzctm.forget();
+}
+
+static void
+InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+ /*** STUB ***/
+}
+
+/*static */ void
+CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+ // Called in the vsync thread
+ if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
+ CompositorLoop()->PostTask(
+ NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
+ }
+}
+
+widget::PCompositorWidgetParent*
+CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ if (mWidget) {
+ // Should not create two widgets on the same compositor.
+ return nullptr;
+ }
+
+ widget::CompositorWidgetParent* widget =
+ new widget::CompositorWidgetParent(aInitData);
+ widget->AddRef();
+
+ // Sending the constructor acts as initialization as well.
+ mWidget = widget;
+ return widget;
+#else
+ return nullptr;
+#endif
+}
+
+bool
+CompositorBridgeParent::DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ static_cast<widget::CompositorWidgetParent*>(aActor)->Release();
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+CompositorBridgeParent::IsPendingComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return false;
+ }
+ return mCompositor->IsPendingComposite();
+}
+
+void
+CompositorBridgeParent::FinishPendingComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return;
+ }
+ return mCompositor->FinishPendingComposite();
+}
+
+CompositorController*
+CompositorBridgeParent::LayerTreeState::GetCompositorController() const
+{
+ return mParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const
+{
+ return mCrossProcessParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::InProcessSharingController() const
+{
+ return mParent;
+}
+
+void
+CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd)
+{
+ Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd);
+ mPendingTransaction = 0;
+
+ if (mLayerManager) {
+ nsTArray<ImageCompositeNotification> notifications;
+ mLayerManager->ExtractImageCompositeNotifications(&notifications);
+ if (!notifications.IsEmpty()) {
+ Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+ }
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
+ if (lts->mCrossProcessParent && lts->mParent == this) {
+ CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
+ cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd);
+ }
+ });
+}
+
+void
+CompositorBridgeParent::InvalidateRemoteLayers()
+{
+ MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
+
+ Unused << PCompositorBridgeParent::SendInvalidateLayers(0);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
+ if (lts->mCrossProcessParent) {
+ CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
+ Unused << cpcp->SendInvalidateLayers(aLayersId);
+ }
+ });
+}
+
+bool
+CompositorBridgeParent::ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier)
+{
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ {
+ MonitorAutoLock lock(mResetCompositorMonitor);
+
+ CompositorLoop()->PostTask(NewRunnableMethod
+ <StoreCopyPassByConstLRef<nsTArray<LayersBackend>>,
+ Maybe<TextureFactoryIdentifier>*>(this,
+ &CompositorBridgeParent::ResetCompositorTask,
+ aBackendHints,
+ &newIdentifier));
+
+ mResetCompositorMonitor.Wait();
+ }
+
+ if (!newIdentifier) {
+ return false;
+ }
+
+ *aOutIdentifier = newIdentifier.value();
+ return true;
+}
+
+// Invoked on the compositor thread. The main thread is waiting on the given
+// monitor.
+void
+CompositorBridgeParent::ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+ Maybe<TextureFactoryIdentifier>* aOutNewIdentifier)
+{
+ // Perform the reset inside a lock, so the main thread can wake up as soon as
+ // possible. We notify child processes (if necessary) outside the lock.
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ {
+ MonitorAutoLock lock(mResetCompositorMonitor);
+
+ newIdentifier = ResetCompositorImpl(aBackendHints);
+ *aOutNewIdentifier = newIdentifier;
+
+ mResetCompositorMonitor.NotifyAll();
+ }
+
+ // NOTE: |aBackendHints|, and |aOutNewIdentifier| are now all invalid since
+ // they are allocated on ResetCompositor's stack on the main thread, which
+ // is no longer waiting on the lock.
+
+ if (!newIdentifier) {
+ // No compositor change; nothing to do.
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void {
+ if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) {
+ Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value());
+
+ if (LayerTransactionParent* ltp = lts->mLayerTree) {
+ ltp->AddPendingCompositorUpdate();
+ }
+ lts->mPendingCompositorUpdates++;
+ }
+ });
+}
+
+Maybe<TextureFactoryIdentifier>
+CompositorBridgeParent::ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints)
+{
+ if (!mLayerManager) {
+ return Nothing();
+ }
+
+ RefPtr<Compositor> compositor = NewCompositor(aBackendHints);
+ if (!compositor) {
+ MOZ_RELEASE_ASSERT(compositor, "Failed to reset compositor.");
+ }
+
+ // Don't bother changing from basic->basic.
+ if (mCompositor &&
+ mCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC &&
+ compositor->GetBackendType() == LayersBackend::LAYERS_BASIC)
+ {
+ return Nothing();
+ }
+
+ if (mCompositor) {
+ mCompositor->SetInvalid();
+ }
+ mCompositor = compositor;
+ mLayerManager->ChangeCompositor(compositor);
+
+ return Some(compositor->GetTextureFactoryIdentifier());
+}
+
+static void
+OpenCompositor(RefPtr<CrossProcessCompositorBridgeParent> aCompositor,
+ Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ aCompositor->Bind(Move(aEndpoint));
+}
+
+/* static */ bool
+CompositorBridgeParent::CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ gfxPlatform::InitLayersIPC();
+
+ RefPtr<CrossProcessCompositorBridgeParent> cpcp =
+ new CrossProcessCompositorBridgeParent();
+
+ CompositorLoop()->PostTask(NewRunnableFunction(OpenCompositor, cpcp, Move(aEndpoint)));
+ return true;
+}
+
+static void
+UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aId].mRoot = aRoot;
+ sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
+}
+
+/* static */ CompositorBridgeParent::LayerTreeState*
+CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ return &cit->second;
+}
+
+static CompositorBridgeParent::LayerTreeState*
+GetStateForRoot(uint64_t aContentLayersId, const MonitorAutoLock& aProofOfLock)
+{
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ // |state| is the state for the content process, but we want the APZCTMParent
+ // for the parent process owning that content process. So we have to jump to
+ // the LayerTreeState for the root layer tree id for that layer tree, and use
+ // the mApzcTreeManagerParent from that. This should also work with nested
+ // content processes, because RootLayerTreeId() will bypass any intermediate
+ // processes' ids and go straight to the root.
+ if (state) {
+ uint64_t rootLayersId = state->mParent->RootLayerTreeId();
+ itr = sIndirectLayerTrees.find(rootLayersId);
+ state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr;
+ }
+
+ return state;
+}
+
+/* static */ APZCTreeManagerParent*
+CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mApzcTreeManagerParent : nullptr;
+}
+
+/* static */ GeckoContentController*
+CompositorBridgeParent::GetGeckoContentControllerForRoot(uint64_t aContentLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mController.get() : nullptr;
+}
+
+PTextureParent*
+CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial)
+{
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool
+CompositorBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+//#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__);
+// printf_stderr(__VA_ARGS__);
+// printf_stderr("\n");
+#define PLUGINS_LOG(...)
+
+bool
+CompositorBridgeParent::UpdatePluginWindowState(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& lts = sIndirectLayerTrees[aId];
+ if (!lts.mParent) {
+ PLUGINS_LOG("[%" PRIu64 "] layer tree compositor parent pointer is null", aId);
+ return false;
+ }
+
+ // Check if this layer tree has received any shadow layer updates
+ if (!lts.mUpdatedPluginDataAvailable) {
+ PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId);
+ return false;
+ }
+
+ // pluginMetricsChanged tracks whether we need to send plugin update
+ // data to the main thread. If we do we'll have to block composition,
+ // which we want to avoid if at all possible.
+ bool pluginMetricsChanged = false;
+
+ // Same layer tree checks
+ if (mLastPluginUpdateLayerTreeId == aId) {
+ // no plugin data and nothing has changed, bail.
+ if (!mCachedPluginData.Length() && !lts.mPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] no data, no changes", aId);
+ return false;
+ }
+
+ if (mCachedPluginData.Length() == lts.mPluginData.Length()) {
+ // check for plugin data changes
+ for (uint32_t idx = 0; idx < lts.mPluginData.Length(); idx++) {
+ if (!(mCachedPluginData[idx] == lts.mPluginData[idx])) {
+ pluginMetricsChanged = true;
+ break;
+ }
+ }
+ } else {
+ // array lengths don't match, need to update
+ pluginMetricsChanged = true;
+ }
+ } else {
+ // exchanging layer trees, we need to update
+ pluginMetricsChanged = true;
+ }
+
+ // Check if plugin windows are currently hidden due to scrolling
+ if (mDeferPluginWindows) {
+ PLUGINS_LOG("[%" PRIu64 "] suppressing", aId);
+ return false;
+ }
+
+ // If the plugin windows were hidden but now are not, we need to force
+ // update the metrics to make sure they are visible again.
+ if (mPluginWindowsHidden) {
+ PLUGINS_LOG("[%" PRIu64 "] re-showing", aId);
+ mPluginWindowsHidden = false;
+ pluginMetricsChanged = true;
+ }
+
+ if (!lts.mPluginData.Length()) {
+ // Don't hide plugins if the previous remote layer tree didn't contain any.
+ if (!mCachedPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId);
+ return false;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ // We will pass through here in cases where the previous shadow layer
+ // tree contained visible plugins and the new tree does not. All we need
+ // to do here is hide the plugins for the old tree, so don't waste time
+ // calculating clipping.
+ mPluginsLayerOffset = nsIntPoint(0,0);
+ mPluginsLayerVisibleRegion.SetEmpty();
+ Unused << lts.mParent->SendHideAllPlugins(parentWidget);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] hide all", aId);
+ } else {
+ // Retrieve the offset and visible region of the layer that hosts
+ // the plugins, CompositorBridgeChild needs these in calculating proper
+ // plugin clipping.
+ LayerTransactionParent* layerTree = lts.mLayerTree;
+ Layer* contentRoot = layerTree->GetRoot();
+ if (contentRoot) {
+ nsIntPoint offset;
+ nsIntRegion visibleRegion;
+ if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion,
+ &offset)) {
+ // Check to see if these values have changed, if so we need to
+ // update plugin window position within the window.
+ if (!pluginMetricsChanged &&
+ mPluginsLayerVisibleRegion == visibleRegion &&
+ mPluginsLayerOffset == offset) {
+ PLUGINS_LOG("[%" PRIu64 "] no change", aId);
+ return false;
+ }
+ mPluginsLayerOffset = offset;
+ mPluginsLayerVisibleRegion = visibleRegion;
+ Unused << lts.mParent->SendUpdatePluginConfigurations(
+ LayoutDeviceIntPoint::FromUnknownPoint(offset),
+ LayoutDeviceIntRegion::FromUnknownRegion(visibleRegion),
+ lts.mPluginData);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] updated", aId);
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId);
+ return false;
+ }
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no content root", aId);
+ return false;
+ }
+ }
+
+ mLastPluginUpdateLayerTreeId = aId;
+ mCachedPluginData = lts.mPluginData;
+ return true;
+}
+
+void
+CompositorBridgeParent::ScheduleShowAllPluginWindows()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ShowAllPluginWindows));
+}
+
+void
+CompositorBridgeParent::ShowAllPluginWindows()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ mDeferPluginWindows = false;
+ ScheduleComposition();
+}
+
+void
+CompositorBridgeParent::ScheduleHideAllPluginWindows()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::HideAllPluginWindows));
+}
+
+void
+CompositorBridgeParent::HideAllPluginWindows()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ // No plugins in the cache implies no plugins to manage
+ // in this content.
+ if (!mCachedPluginData.Length() || mDeferPluginWindows) {
+ return;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ mDeferPluginWindows = true;
+ mPluginWindowsHidden = true;
+
+#if defined(XP_WIN)
+ // We will get an async reply that this has happened and then send hide.
+ mWaitForPluginsUntil = TimeStamp::Now() + mVsyncRate;
+ Unused << SendCaptureAllPlugins(parentWidget);
+#else
+ Unused << SendHideAllPlugins(parentWidget);
+ ScheduleComposition();
+#endif
+}
+#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+
+bool
+CompositorBridgeParent::RecvAllPluginsCaptured()
+{
+#if defined(XP_WIN)
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ return true;
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected.");
+ return false;
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/CompositorBridgeParent.h b/system/graphics/layers/ipc/CompositorBridgeParent.h
new file mode 100644
index 000000000..d4f2da54c
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorBridgeParent.h
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeParent_h
+#define mozilla_layers_CompositorBridgeParent_h
+
+// Enable this pref to turn on compositor performance warning.
+// This will print warnings if the compositor isn't meeting
+// its responsiveness objectives:
+// 1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
+// 2) Unless a frame was composited within the throttle threshold in
+// which the deadline will be 15ms + throttle threshold
+//#define COMPOSITOR_PERFORMANCE_WARNING
+
+#include <stdint.h> // for uint64_t
+#include "Layers.h" // for Layer
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/CompositorController.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator
+#include "mozilla/layers/LayersMessages.h" // for TargetConfig
+#include "mozilla/layers/MetricsSharingController.h"
+#include "mozilla/layers/PCompositorBridgeParent.h"
+#include "mozilla/layers/APZTestData.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "mozilla/VsyncDispatcher.h"
+
+class MessageLoop;
+class nsIWidget;
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace gfx {
+class DrawTarget;
+class GPUProcessManager;
+class GPUParent;
+} // namespace gfx
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class APZCTreeManager;
+class APZCTreeManagerParent;
+class AsyncCompositionManager;
+class Compositor;
+class CompositorBridgeParent;
+class LayerManagerComposite;
+class LayerTransactionParent;
+class PAPZParent;
+class CrossProcessCompositorBridgeParent;
+class CompositorThreadHolder;
+class InProcessCompositorSession;
+
+struct ScopedLayerTreeRegistration
+{
+ ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
+ uint64_t aLayersId,
+ Layer* aRoot,
+ GeckoContentController* aController);
+ ~ScopedLayerTreeRegistration();
+
+private:
+ uint64_t mLayersId;
+};
+
+/**
+ * Manages the vsync (de)registration and tracking on behalf of the
+ * compositor when it need to paint.
+ * Turns vsync notifications into scheduled composites.
+ **/
+class CompositorVsyncScheduler
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
+
+public:
+ explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
+ widget::CompositorWidget* aWidget);
+
+ bool NotifyVsync(TimeStamp aVsyncTimestamp);
+ void SetNeedsComposite();
+ void OnForceComposeToTarget();
+
+ void ScheduleTask(already_AddRefed<CancelableRunnable>, int);
+ void ResumeComposition();
+ void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+ void PostCompositeTask(TimeStamp aCompositeTimestamp);
+ void Destroy();
+ void ScheduleComposition();
+ void CancelCurrentCompositeTask();
+ bool NeedsComposite();
+ void Composite(TimeStamp aVsyncTimestamp);
+ void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect);
+
+ const TimeStamp& GetLastComposeTime()
+ {
+ return mLastCompose;
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ const TimeStamp& GetExpectedComposeStartTime()
+ {
+ return mExpectedComposeStartTime;
+ }
+#endif
+
+private:
+ virtual ~CompositorVsyncScheduler();
+
+ void NotifyCompositeTaskExecuted();
+ void ObserveVsync();
+ void UnobserveVsync();
+ void DispatchTouchEvents(TimeStamp aVsyncTimestamp);
+ void DispatchVREvents(TimeStamp aVsyncTimestamp);
+ void CancelCurrentSetNeedsCompositeTask();
+
+ class Observer final : public VsyncObserver
+ {
+ public:
+ explicit Observer(CompositorVsyncScheduler* aOwner);
+ virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+ void Destroy();
+ private:
+ virtual ~Observer();
+
+ Mutex mMutex;
+ // Hold raw pointer to avoid mutual reference.
+ CompositorVsyncScheduler* mOwner;
+ };
+
+ CompositorBridgeParent* mCompositorBridgeParent;
+ TimeStamp mLastCompose;
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeStamp mExpectedComposeStartTime;
+#endif
+
+ bool mAsapScheduling;
+ bool mIsObservingVsync;
+ uint32_t mNeedsComposite;
+ int32_t mVsyncNotificationsSkipped;
+ widget::CompositorWidget* mWidget;
+ RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
+
+ mozilla::Monitor mCurrentCompositeTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentCompositeTask;
+
+ mozilla::Monitor mSetNeedsCompositeMonitor;
+ RefPtr<CancelableRunnable> mSetNeedsCompositeTask;
+};
+
+class CompositorBridgeParentBase : public PCompositorBridgeParent,
+ public HostIPCAllocator,
+ public ShmemAllocator,
+ public MetricsSharingController
+{
+public:
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate) = 0;
+
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) { return nullptr; }
+
+ virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { }
+
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) { }
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) { return true; }
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { }
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) { }
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+ virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual bool RecvSyncWithCompositor() override { return true; }
+
+ // HostIPCAllocator
+ virtual base::ProcessId GetChildProcessId() override;
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ShmemAllocator
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // MetricsSharingController
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return HostIPCAllocator::AddRef(); }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { return HostIPCAllocator::Release(); }
+ base::ProcessId RemotePid() override;
+ bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId) override;
+ bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId) override;
+};
+
+class CompositorBridgeParent final : public CompositorBridgeParentBase
+ , public CompositorController
+{
+ friend class CompositorVsyncScheduler;
+ friend class CompositorThreadHolder;
+ friend class InProcessCompositorSession;
+ friend class gfx::GPUProcessManager;
+ friend class gfx::GPUParent;
+
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return CompositorBridgeParentBase::AddRef(); }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { return CompositorBridgeParentBase::Release(); }
+
+ explicit CompositorBridgeParent(CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ // Must only be called by CompositorBridgeChild. After invoking this, the
+ // IPC channel is active and RecvWillStop/ActorDestroy must be called to
+ // free the compositor.
+ void InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ bool aUseAPZ);
+
+ // Must only be called by GPUParent. After invoking this, the IPC channel
+ // is active and RecvWillStop/ActorDestroy must be called to free the
+ // compositor.
+ bool Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint);
+
+ virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override;
+ virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override;
+ virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override;
+ virtual bool RecvRequestOverfill() override;
+ virtual bool RecvWillClose() override;
+ virtual bool RecvPause() override;
+ virtual bool RecvResume() override;
+ virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
+ virtual bool RecvNotifyChildRecreated(const uint64_t& child) override;
+ virtual bool RecvAdoptChild(const uint64_t& child) override;
+ virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override;
+ virtual bool RecvFlushRendering() override;
+ virtual bool RecvForcePresent() override;
+
+ virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override {
+ MOZ_ASSERT_UNREACHABLE("This message is only sent cross-process");
+ return true;
+ }
+
+ virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override;
+ virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
+ virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override;
+
+ // Unused for chrome <-> compositor communication (which this class does).
+ // @see CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint
+ virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; };
+
+ virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId) override;
+ void ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const Maybe<uint32_t>& aPresShellId);
+ virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion) override;
+
+ virtual bool RecvAllPluginsCaptured() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate) override;
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) override;
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) override;
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override;
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+ override;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) override;
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) override { return mCompositionManager; }
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ virtual bool IsSameProcess() const override;
+
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
+ bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
+
+ /**
+ * Request that the compositor be recreated due to a shared device reset.
+ * This must be called on the main thread, and blocks until a task posted
+ * to the compositor thread has completed.
+ *
+ * Note that this posts a task directly, rather than using synchronous
+ * IPDL, and waits on a monitor notification from the compositor thread.
+ * We do this as a best-effort attempt to jump any IPDL messages that
+ * have not yet been posted (and are sitting around in the IO pipe), to
+ * minimize the amount of time the main thread is blocked.
+ */
+ bool ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier);
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint();
+
+ static void SetShadowProperties(Layer* aLayer);
+
+ void NotifyChildCreated(uint64_t aChild);
+
+ void AsyncRender();
+
+ // Can be called from any thread
+ void ScheduleRenderOnCompositorThread() override;
+ void SchedulePauseOnCompositorThread();
+ void InvalidateOnCompositorThread();
+ /**
+ * Returns true if a surface was obtained and the resume succeeded; false
+ * otherwise.
+ */
+ bool ScheduleResumeOnCompositorThread();
+ bool ScheduleResumeOnCompositorThread(int width, int height);
+
+ virtual void ScheduleComposition();
+ void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, bool aHitTestUpdate);
+
+ void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) override;
+
+ /**
+ * Check rotation info and schedule a rendering task if needed.
+ * Only can be called from compositor thread.
+ */
+ void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, bool aIsFirstPaint);
+
+ /**
+ * Returns the unique layer tree identifier that corresponds to the root
+ * tree of this compositor.
+ */
+ uint64_t RootLayerTreeId();
+
+ /**
+ * Notify local and remote layer trees connected to this compositor that
+ * the compositor's local device is being reset. All layers must be
+ * invalidated to clear any cached TextureSources.
+ *
+ * This must be called on the compositor thread.
+ */
+ void InvalidateRemoteLayers();
+
+ /**
+ * Returns a pointer to the CompositorBridgeParent corresponding to the given ID.
+ */
+ static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id);
+
+ /**
+ * Notify the compositor for the given layer tree that vsync has occurred.
+ */
+ static void NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId);
+
+ /**
+ * Set aController as the pan/zoom callback for the subtree referred
+ * to by aLayersId.
+ *
+ * Must run on content main thread.
+ */
+ static void SetControllerForLayerTree(uint64_t aLayersId,
+ GeckoContentController* aController);
+
+ /**
+ * A new child process has been configured to push transactions
+ * directly to us. Transport is to its thread context.
+ */
+ static bool
+ CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint);
+
+ struct LayerTreeState {
+ LayerTreeState();
+ ~LayerTreeState();
+ RefPtr<Layer> mRoot;
+ RefPtr<GeckoContentController> mController;
+ APZCTreeManagerParent* mApzcTreeManagerParent;
+ CompositorBridgeParent* mParent;
+ LayerManagerComposite* mLayerManager;
+ // Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share
+ // their FrameMetrics with the corresponding child process that holds
+ // the PCompositorBridgeChild
+ CrossProcessCompositorBridgeParent* mCrossProcessParent;
+ TargetConfig mTargetConfig;
+ APZTestData mApzTestData;
+ LayerTransactionParent* mLayerTree;
+ nsTArray<PluginWindowData> mPluginData;
+ bool mUpdatedPluginDataAvailable;
+
+ // Number of times the compositor has been reset without having been
+ // acknowledged by the child.
+ uint32_t mPendingCompositorUpdates;
+
+ CompositorController* GetCompositorController() const;
+ MetricsSharingController* CrossProcessSharingController() const;
+ MetricsSharingController* InProcessSharingController() const;
+ };
+
+ /**
+ * Lookup the indirect shadow tree for |aId| and return it if it
+ * exists. Otherwise null is returned. This must only be called on
+ * the compositor thread.
+ */
+ static LayerTreeState* GetIndirectShadowTree(uint64_t aId);
+
+ /**
+ * Given the layers id for a content process, get the APZCTreeManagerParent
+ * for the corresponding *root* layers id. That is, the APZCTreeManagerParent,
+ * if one is found, will always be connected to the parent process rather
+ * than a content process. Note that unless the compositor process is
+ * separated this is expected to return null, because if the compositor is
+ * living in the gecko parent process then there is no APZCTreeManagerParent
+ * for the parent process.
+ */
+ static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot(
+ uint64_t aContentLayersId);
+ /**
+ * Same as the GetApzcTreeManagerParentForRoot function, but returns
+ * the GeckoContentController for the parent process.
+ */
+ static GeckoContentController* GetGeckoContentControllerForRoot(
+ uint64_t aContentLayersId);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ /**
+ * Calculates and requests the main thread update plugin positioning, clip,
+ * and visibility via ipc.
+ */
+ bool UpdatePluginWindowState(uint64_t aId);
+
+ /**
+ * Plugin visibility helpers for the apz (main thread) and compositor
+ * thread.
+ */
+ void ScheduleShowAllPluginWindows() override;
+ void ScheduleHideAllPluginWindows() override;
+ void ShowAllPluginWindows();
+ void HideAllPluginWindows();
+#else
+ void ScheduleShowAllPluginWindows() override {}
+ void ScheduleHideAllPluginWindows() override {}
+#endif
+
+ /**
+ * Main thread response for a plugin visibility request made by the
+ * compositor thread.
+ */
+ virtual bool RecvRemotePluginsReady() override;
+
+ /**
+ * Used by the profiler to denote when a vsync occured
+ */
+ static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
+
+ widget::CompositorWidget* GetWidget() { return mWidget; }
+
+ void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override;
+
+ RefPtr<APZCTreeManager> GetAPZCTreeManager();
+
+ bool AsyncPanZoomEnabled() const {
+ return !!mApzcTreeManager;
+ }
+
+private:
+
+ void Initialize();
+
+ /**
+ * Called during destruction in order to release resources as early as possible.
+ */
+ void StopAndClearResources();
+
+ /**
+ * This returns a reference to the APZCTreeManager to which
+ * pan/zoom-related events can be sent.
+ */
+ static already_AddRefed<APZCTreeManager> GetAPZCTreeManager(uint64_t aLayersId);
+
+ /**
+ * Release compositor-thread resources referred to by |aID|.
+ *
+ * Must run on the content main thread.
+ */
+ static void DeallocateLayerTreeId(uint64_t aId);
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ virtual PLayerTransactionParent*
+ AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool* aSuccess) override;
+ virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
+ virtual void ScheduleTask(already_AddRefed<CancelableRunnable>, int);
+ void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+
+ void SetEGLSurfaceSize(int width, int height);
+
+ void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
+ void PauseComposition();
+ void ResumeComposition();
+ void ResumeCompositionAndResize(int width, int height);
+ void ForceComposition();
+ void CancelCurrentCompositeTask();
+ void Invalidate();
+ bool IsPendingComposite();
+ void FinishPendingComposite();
+
+ RefPtr<Compositor> NewCompositor(const nsTArray<LayersBackend>& aBackendHints);
+ void ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+ Maybe<TextureFactoryIdentifier>* aOutNewIdentifier);
+ Maybe<TextureFactoryIdentifier> ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints);
+
+ /**
+ * Add a compositor to the global compositor map.
+ */
+ static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
+ /**
+ * Remove a compositor from the global compositor map.
+ */
+ static CompositorBridgeParent* RemoveCompositor(uint64_t id);
+
+ /**
+ * Creates the global compositor map.
+ */
+ static void Setup();
+
+ /**
+ * Destroys the compositor thread and global compositor map.
+ */
+ static void Shutdown();
+
+ /**
+ * Finish the shutdown operation on the compositor thread.
+ */
+ static void FinishShutdown();
+
+ /**
+ * Return true if current state allows compositing, that is
+ * finishing a layers transaction.
+ */
+ bool CanComposite();
+
+ void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
+ template <typename Lambda>
+ inline void ForEachIndirectLayerTree(const Lambda& aCallback);
+
+ RefPtr<LayerManagerComposite> mLayerManager;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<AsyncCompositionManager> mCompositionManager;
+ widget::CompositorWidget* mWidget;
+ TimeStamp mTestTime;
+ CSSToLayoutDeviceScale mScale;
+ TimeDuration mVsyncRate;
+ bool mIsTesting;
+
+ uint64_t mPendingTransaction;
+
+ bool mPaused;
+
+ bool mUseExternalSurfaceSize;
+ gfx::IntSize mEGLSurfaceSize;
+
+ mozilla::Monitor mPauseCompositionMonitor;
+ mozilla::Monitor mResumeCompositionMonitor;
+ mozilla::Monitor mResetCompositorMonitor;
+
+ uint64_t mCompositorID;
+ uint64_t mRootLayerTreeID;
+
+ bool mOverrideComposeReadiness;
+ RefPtr<CancelableRunnable> mForceCompositionTask;
+
+ RefPtr<APZCTreeManager> mApzcTreeManager;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+ RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
+ // This makes sure the compositorParent is not destroyed before receiving
+ // confirmation that the channel is closed.
+ // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
+ RefPtr<CompositorBridgeParent> mSelfRef;
+
+ TimeDuration mPaintTime;
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // cached plugin data used to reduce the number of updates we request.
+ uint64_t mLastPluginUpdateLayerTreeId;
+ nsIntPoint mPluginsLayerOffset;
+ nsIntRegion mPluginsLayerVisibleRegion;
+ nsTArray<PluginWindowData> mCachedPluginData;
+ // Time until which we will block composition to wait for plugin updates.
+ TimeStamp mWaitForPluginsUntil;
+ // Indicates that we have actually blocked a composition waiting for plugins.
+ bool mHaveBlockedForPlugins = false;
+ // indicates if plugin window visibility and metric updates are currently
+ // being defered due to a scroll operation.
+ bool mDeferPluginWindows;
+ // indicates if the plugin windows were hidden, and need to be made
+ // visible again even if their geometry has not changed.
+ bool mPluginWindowsHidden;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeParent);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBridgeParent_h
diff --git a/system/graphics/layers/ipc/CompositorThread.cpp b/system/graphics/layers/ipc/CompositorThread.cpp
new file mode 100644
index 000000000..878fabbb8
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorThread.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorThread.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "CompositorBridgeParent.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/media/MediaSystemResourceService.h"
+
+namespace mozilla {
+
+namespace layers {
+
+static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
+static bool sFinishedCompositorShutDown = false;
+
+// See ImageBridgeChild.cpp
+void ReleaseImageBridgeParentSingleton();
+
+CompositorThreadHolder* GetCompositorThreadHolder()
+{
+ return sCompositorThreadHolder;
+}
+
+base::Thread*
+CompositorThread()
+{
+ return sCompositorThreadHolder
+ ? sCompositorThreadHolder->GetCompositorThread()
+ : nullptr;
+}
+
+/* static */ MessageLoop*
+CompositorThreadHolder::Loop()
+{
+ return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
+}
+
+CompositorThreadHolder*
+CompositorThreadHolder::GetSingleton()
+{
+ return sCompositorThreadHolder;
+}
+
+CompositorThreadHolder::CompositorThreadHolder()
+ : mCompositorThread(CreateCompositorThread())
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_CTOR(CompositorThreadHolder);
+}
+
+CompositorThreadHolder::~CompositorThreadHolder()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_COUNT_DTOR(CompositorThreadHolder);
+
+ DestroyCompositorThread(mCompositorThread);
+}
+
+/* static */ void
+CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
+
+ CompositorBridgeParent::Shutdown();
+ delete aCompositorThread;
+ sFinishedCompositorShutDown = true;
+}
+
+/* static */ base::Thread*
+CompositorThreadHolder::CreateCompositorThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+ base::Thread* compositorThread = new base::Thread("Compositor");
+
+ base::Thread::Options options;
+ /* Timeout values are powers-of-two to enable us get better data.
+ 128ms is chosen for transient hangs because 8Hz should be the minimally
+ acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
+ options.transient_hang_timeout = 128; // milliseconds
+ /* 2048ms is chosen for permanent hangs because it's longer than most
+ * Compositor hangs seen in the wild, but is short enough to not miss getting
+ * native hang stacks. */
+ options.permanent_hang_timeout = 2048; // milliseconds
+#if defined(_WIN32)
+ /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As
+ * such the thread is a gui thread, and must process a windows message queue or
+ * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */
+ options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+
+ if (!compositorThread->StartWithOptions(options)) {
+ delete compositorThread;
+ return nullptr;
+ }
+
+ CompositorBridgeParent::Setup();
+ ImageBridgeParent::Setup();
+
+ return compositorThread;
+}
+
+void
+CompositorThreadHolder::Start()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+ sCompositorThreadHolder = new CompositorThreadHolder();
+}
+
+void
+CompositorThreadHolder::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
+
+ ReleaseImageBridgeParentSingleton();
+ MediaSystemResourceService::Shutdown();
+
+ sCompositorThreadHolder = nullptr;
+
+ // No locking is needed around sFinishedCompositorShutDown because it is only
+ // ever accessed on the main thread.
+ while (!sFinishedCompositorShutDown) {
+ NS_ProcessNextEvent(nullptr, true);
+ }
+
+ CompositorBridgeParent::FinishShutdown();
+}
+
+/* static */ bool
+CompositorThreadHolder::IsInCompositorThread()
+{
+ return CompositorThread() &&
+ CompositorThread()->thread_id() == PlatformThread::CurrentId();
+}
+
+} // namespace mozilla
+} // namespace layers
diff --git a/system/graphics/layers/ipc/CompositorThread.h b/system/graphics/layers/ipc/CompositorThread.h
new file mode 100644
index 000000000..0e1999fab
--- /dev/null
+++ b/system/graphics/layers/ipc/CompositorThread.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_layers_CompositorThread_h
+#define mozilla_layers_CompositorThread_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "base/platform_thread.h" // for PlatformThreadId
+#include "base/thread.h" // for Thread
+#include "base/message_loop.h"
+#include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorThreadHolder final
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder)
+
+public:
+ CompositorThreadHolder();
+
+ base::Thread* GetCompositorThread() const {
+ return mCompositorThread;
+ }
+
+ static CompositorThreadHolder* GetSingleton();
+
+ static bool IsActive() {
+ return !!GetSingleton();
+ }
+
+ /**
+ * Creates the compositor thread and the global compositor map.
+ */
+ static void Start();
+
+ /*
+ * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and
+ * releases compositor-thread owned resources.
+ */
+ static void Shutdown();
+
+ static MessageLoop* Loop();
+
+ // Returns true if the calling thread is the compositor thread.
+ static bool IsInCompositorThread();
+
+private:
+ ~CompositorThreadHolder();
+
+ base::Thread* const mCompositorThread;
+
+ static base::Thread* CreateCompositorThread();
+ static void DestroyCompositorThread(base::Thread* aCompositorThread);
+
+ friend class CompositorBridgeParent;
+};
+
+base::Thread* CompositorThread();
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorThread_h
diff --git a/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.cpp b/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.cpp
new file mode 100644
index 000000000..623bf9e5d
--- /dev/null
+++ b/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -0,0 +1,571 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#ifdef MOZ_WIDGET_GTK
+#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#ifdef XP_WIN
+#include "mozilla/layers/CompositorD3D11.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace std;
+
+// defined in CompositorBridgeParent.cpp
+typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+extern LayerTreeMap sIndirectLayerTrees;
+extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+bool
+CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint()
+{
+ mNotifyAfterRemotePaint = true;
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // We must keep this object alive untill the code handling message
+ // reception is finished on this thread.
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy));
+}
+
+PLayerTransactionParent*
+CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>&,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess)
+{
+ MOZ_ASSERT(aId != 0);
+
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPLayerTransactionParent; dropping message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ if (state && state->mLayerManager) {
+ state->mCrossProcessParent = this;
+ LayerManagerComposite* lm = state->mLayerManager;
+ *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
+ *aSuccess = true;
+ LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
+ p->AddIPDLReference();
+ sIndirectLayerTrees[aId].mLayerTree = p;
+ p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates);
+ return p;
+ }
+
+ NS_WARNING("Created child without a matching parent?");
+ *aSuccess = false;
+ LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers)
+{
+ LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers);
+ EraseLayerState(slp->GetId());
+ static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in RecvAsyncPanZoomEnabled; dropping message...");
+ return false;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ *aHasAPZ = state.mParent ? state.mParent->AsyncPanZoomEnabled() : false;
+ return true;
+}
+
+PAPZCTreeManagerParent*
+CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+ MOZ_ASSERT(state.mParent);
+ MOZ_ASSERT(!state.mApzcTreeManagerParent);
+ state.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, state.mParent->GetAPZCTreeManager());
+
+ return state.mApzcTreeManagerParent;
+}
+bool
+CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
+{
+ APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto iter = sIndirectLayerTrees.find(parent->LayersId());
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent::LayerTreeState& state = iter->second;
+ MOZ_ASSERT(state.mApzcTreeManagerParent == parent);
+ state.mApzcTreeManagerParent = nullptr;
+ }
+
+ delete parent;
+
+ return true;
+}
+
+PAPZParent*
+CrossProcessCompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPAPZParent; dropping message...");
+ return nullptr;
+ }
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+ MOZ_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
+{
+ RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
+ it != sIndirectLayerTrees.end(); it++) {
+ CompositorBridgeParent::LayerTreeState* lts = &it->second;
+ if (lts->mParent && lts->mCrossProcessParent == this) {
+ lts->mParent->NotifyChildCreated(child);
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CrossProcessCompositorBridgeParent::ShadowLayersUpdated(
+ LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t /*aPaintSyncId: unused*/,
+ bool aHitTestUpdate)
+{
+ uint64_t id = aLayerTree->GetId();
+
+ MOZ_ASSERT(id != 0);
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
+
+ Layer* shadowRoot = aLayerTree->GetRoot();
+ if (shadowRoot) {
+ CompositorBridgeParent::SetShadowProperties(shadowRoot);
+ }
+ UpdateIndirectTree(id, shadowRoot, aTargetConfig);
+
+ // Cache the plugin data for this remote layer tree
+ state->mPluginData = aPlugins;
+ state->mUpdatedPluginDataAvailable = true;
+
+ state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction, aHitTestUpdate);
+
+ // Send the 'remote paint ready' message to the content thread if it has already asked.
+ if(mNotifyAfterRemotePaint) {
+ Unused << SendRemotePaintIsReady();
+ mNotifyAfterRemotePaint = false;
+ }
+
+ if (aLayerTree->ShouldParentObserveEpoch()) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), true);
+ }
+
+ aLayerTree->SetPendingTransactionId(aTransactionId);
+}
+
+void
+CrossProcessCompositorBridgeParent::DidComposite(
+ uint64_t aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) {
+ Unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd);
+ layerTree->SetPendingTransactionId(0);
+ }
+}
+
+void
+CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[id].mParent;
+ }
+ if (parent) {
+ parent->ForceComposite(aLayerTree);
+ }
+}
+
+void
+CrossProcessCompositorBridgeParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (state && state->mParent) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), false);
+ }
+}
+
+bool
+CrossProcessCompositorBridgeParent::SetTestSampleTime(
+ LayerTransactionParent* aLayerTree, const TimeStamp& aTime)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return false;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->SetTestSampleTime(aLayerTree, aTime);
+}
+
+void
+CrossProcessCompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->LeaveTestMode(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::ApplyAsyncProperties(
+ LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ApplyAsyncProperties(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->FlushApzRepaints(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::GetAPZTestData(
+ const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ *aOutData = sIndirectLayerTrees[id].mApzTestData;
+}
+
+void
+CrossProcessCompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->SetConfirmedTargetAPZC(aLayerTree, aInputBlockId, aTargets);
+}
+
+AsyncCompositionManager*
+CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->GetCompositionManager(aLayerTree);
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ if (LayerTransactionParent* ltp = state.mLayerTree) {
+ ltp->AcknowledgeCompositorUpdate();
+ }
+ MOZ_ASSERT(state.mPendingCompositorUpdates > 0);
+ state.mPendingCompositorUpdates--;
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::DeferredDestroy()
+{
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+CrossProcessCompositorBridgeParent::~CrossProcessCompositorBridgeParent()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop());
+ MOZ_ASSERT(IToplevelProtocol::GetTransport());
+}
+
+PTextureParent*
+CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial)
+{
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ TextureFlags flags = aFlags;
+
+ if (!state || state->mPendingCompositorUpdates) {
+ // The compositor was recreated, and we're receiving layers updates for a
+ // a layer manager that will soon be discarded or invalidated. We can't
+ // return null because this will mess up deserialization later and we'll
+ // kill the content process. Instead, we signal that the underlying
+ // TextureHost should not attempt to access the compositor.
+ flags |= TextureFlags::INVALID_COMPOSITOR;
+ } else if (state->mLayerManager && state->mLayerManager->GetCompositor() &&
+ aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) {
+ gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong";
+ }
+
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool
+CrossProcessCompositorBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId)
+{
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[aLayersId].mParent;
+ }
+ if (parent) {
+ parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+ }
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[aGuid.mLayersId].mParent;
+ }
+ if (parent) {
+ return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+ }
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->UpdatePaintTime(aLayerTree, aPaintTime);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.h b/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.h
new file mode 100644
index 000000000..76a90f71b
--- /dev/null
+++ b/system/graphics/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CrossProcessCompositorBridgeParent_h
+#define mozilla_layers_CrossProcessCompositorBridgeParent_h
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class handles layer updates pushed directly from child processes to
+ * the compositor thread. It's associated with a CompositorBridgeParent on the
+ * compositor thread. While it uses the PCompositorBridge protocol to manage
+ * these updates, it doesn't actually drive compositing itself. For that it
+ * hands off work to the CompositorBridgeParent it's associated with.
+ */
+class CrossProcessCompositorBridgeParent final : public CompositorBridgeParentBase
+{
+ friend class CompositorBridgeParent;
+
+public:
+ explicit CrossProcessCompositorBridgeParent()
+ : mNotifyAfterRemotePaint(false)
+ , mDestroyCalled(false)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ void Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+ mSelfRef = this;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // FIXME/bug 774388: work out what shutdown protocol we need.
+ virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override { return false; }
+ virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override { return false; }
+ virtual bool RecvRequestOverfill() override { return true; }
+ virtual bool RecvWillClose() override { return true; }
+ virtual bool RecvPause() override { return true; }
+ virtual bool RecvResume() override { return true; }
+ virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
+ virtual bool RecvNotifyChildRecreated(const uint64_t& child) override { return false; }
+ virtual bool RecvAdoptChild(const uint64_t& child) override { return false; }
+ virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override
+ { return true; }
+ virtual bool RecvFlushRendering() override { return true; }
+ virtual bool RecvForcePresent() override { return true; }
+ virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
+ virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
+ virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override { return true; }
+
+ virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId) override;
+
+ virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion) override;
+
+ virtual bool RecvAllPluginsCaptured() override { return true; }
+
+ virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override
+ {
+ // Don't support calculating frame uniformity on the child process and
+ // this is just a stub for now.
+ MOZ_ASSERT(false);
+ return true;
+ }
+
+ /**
+ * Tells this CompositorBridgeParent to send a message when the compositor has received the transaction.
+ */
+ virtual bool RecvRequestNotifyAfterRemotePaint() override;
+
+ virtual PLayerTransactionParent*
+ AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess) override;
+
+ virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
+
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t /*aPaintSyncId: unused*/,
+ bool aHitTestUpdate) override;
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) override;
+ virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) override;
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override;
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+ override;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) override;
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
+ virtual bool RecvRemotePluginsReady() override { return false; }
+ virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override;
+
+ void DidComposite(uint64_t aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd);
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ virtual bool IsSameProcess() const override;
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override {
+ // Not allowed.
+ return nullptr;
+ }
+ bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override {
+ // Not allowed.
+ return false;
+ }
+
+ virtual bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override;
+
+ virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override;
+ virtual bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ virtual PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
+ virtual bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override;
+
+protected:
+ void OnChannelConnected(int32_t pid) override {
+ mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+ }
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CrossProcessCompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ // There can be many CPCPs, and IPDL-generated code doesn't hold a
+ // reference to top-level actors. So we hold a reference to
+ // ourself. This is released (deferred) in ActorDestroy().
+ RefPtr<CrossProcessCompositorBridgeParent> mSelfRef;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+ // If true, we should send a RemotePaintIsReady message when the layer transaction
+ // is received
+ bool mNotifyAfterRemotePaint;
+ bool mDestroyCalled;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CrossProcessCompositorBridgeParent_h
diff --git a/system/graphics/layers/ipc/GonkNativeHandle.cpp b/system/graphics/layers/ipc/GonkNativeHandle.cpp
new file mode 100644
index 000000000..8f808e623
--- /dev/null
+++ b/system/graphics/layers/ipc/GonkNativeHandle.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <unistd.h>
+
+#include "GonkNativeHandle.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+namespace layers {
+
+GonkNativeHandle::GonkNativeHandle()
+ : mNhObj(new NhObj())
+{
+}
+
+GonkNativeHandle::GonkNativeHandle(NhObj* aNhObj)
+ : mNhObj(aNhObj)
+{
+ MOZ_ASSERT(aNhObj);
+}
+
+
+void
+GonkNativeHandle::TransferToAnother(GonkNativeHandle& aHandle)
+{
+ aHandle.mNhObj = this->GetAndResetNhObj();
+}
+
+already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::GetAndResetNhObj()
+{
+ RefPtr<NhObj> nhObj = mNhObj;
+ mNhObj = new NhObj();
+ return nhObj.forget();
+}
+
+already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::GetDupNhObj()
+{
+ if (!IsValid()) {
+ return GonkNativeHandle::CreateDupNhObj(nullptr);
+ }
+ return GonkNativeHandle::CreateDupNhObj(mNhObj->mHandle);
+}
+
+/* static */ already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::CreateDupNhObj(native_handle_t* aHandle)
+{
+ RefPtr<NhObj> nhObj;
+ if (aHandle) {
+ native_handle* nativeHandle =
+ native_handle_create(aHandle->numFds, aHandle->numInts);
+ if (!nativeHandle) {
+ nhObj = new GonkNativeHandle::NhObj();
+ return nhObj.forget();
+ }
+
+ for (int i = 0; i < aHandle->numFds; ++i) {
+ nativeHandle->data[i] = dup(aHandle->data[i]);
+ }
+
+ memcpy(nativeHandle->data + nativeHandle->numFds,
+ aHandle->data + aHandle->numFds,
+ sizeof(int) * aHandle->numInts);
+
+ nhObj = new GonkNativeHandle::NhObj(nativeHandle);
+ } else {
+ nhObj = new GonkNativeHandle::NhObj();
+ }
+ return nhObj.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/GonkNativeHandle.h b/system/graphics/layers/ipc/GonkNativeHandle.h
new file mode 100644
index 000000000..6afaea540
--- /dev/null
+++ b/system/graphics/layers/ipc/GonkNativeHandle.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_GonkNativeHandle_h
+#define IPC_GonkNativeHandle_h
+
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace layers {
+
+struct GonkNativeHandle {
+ bool operator==(const GonkNativeHandle&) const { return false; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // IPC_GonkNativeHandle_h
diff --git a/system/graphics/layers/ipc/GonkNativeHandleUtils.cpp b/system/graphics/layers/ipc/GonkNativeHandleUtils.cpp
new file mode 100644
index 000000000..fb2efecb3
--- /dev/null
+++ b/system/graphics/layers/ipc/GonkNativeHandleUtils.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GonkNativeHandleUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla::layers;
+
+namespace IPC {
+
+namespace {
+
+class native_handle_Delete
+{
+public:
+ void operator()(native_handle* aNativeHandle) const
+ {
+ native_handle_close(aNativeHandle); // closes file descriptors
+ native_handle_delete(aNativeHandle);
+ }
+};
+
+} // anonymous namespace
+
+void
+ParamTraits<GonkNativeHandle>::Write(Message* aMsg,
+ const paramType& aParam)
+{
+ GonkNativeHandle handle = aParam;
+ MOZ_ASSERT(handle.IsValid());
+
+ RefPtr<GonkNativeHandle::NhObj> nhObj = handle.GetAndResetNhObj();
+ native_handle_t* nativeHandle = nhObj->GetAndResetNativeHandle();
+
+ size_t nbytes = nativeHandle->numInts * sizeof(int);
+ aMsg->WriteSize(nbytes);
+ aMsg->WriteBytes((nativeHandle->data + nativeHandle->numFds), nbytes);
+
+ for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) {
+ aMsg->WriteFileDescriptor(base::FileDescriptor(nativeHandle->data[i], true));
+ }
+}
+
+bool
+ParamTraits<GonkNativeHandle>::Read(const Message* aMsg,
+ PickleIterator* aIter, paramType* aResult)
+{
+ size_t nbytes;
+ if (!aMsg->ReadSize(aIter, &nbytes)) {
+ return false;
+ }
+
+ if (nbytes % sizeof(int) != 0) {
+ return false;
+ }
+
+ size_t numInts = nbytes / sizeof(int);
+ size_t numFds = aMsg->num_fds();
+ mozilla::UniquePtr<native_handle, native_handle_Delete> nativeHandle(
+ native_handle_create(numFds, numInts));
+ if (!nativeHandle) {
+ return false;
+ }
+
+ auto data =
+ reinterpret_cast<char*>(nativeHandle->data + nativeHandle->numFds);
+ if (!aMsg->ReadBytesInto(aIter, data, nbytes)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < numFds; ++i) {
+ base::FileDescriptor fd;
+ if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
+ return false;
+ }
+ nativeHandle->data[i] = fd.fd;
+ nativeHandle->numFds = i + 1; // set number of valid file descriptors
+ }
+
+ GonkNativeHandle handle(new GonkNativeHandle::NhObj(nativeHandle.get()));
+ handle.TransferToAnother(*aResult);
+
+ mozilla::Unused << nativeHandle.release();
+
+ return true;
+}
+
+} // namespace IPC
diff --git a/system/graphics/layers/ipc/GonkNativeHandleUtils.h b/system/graphics/layers/ipc/GonkNativeHandleUtils.h
new file mode 100644
index 000000000..d91792c95
--- /dev/null
+++ b/system/graphics/layers/ipc/GonkNativeHandleUtils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_GonkNativeHandleUtils_h
+#define IPC_GonkNativeHandleUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "GonkNativeHandle.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::GonkNativeHandle> {
+ typedef mozilla::layers::GonkNativeHandle paramType;
+ static void Write(Message*, const paramType&) {}
+ static bool Read(const Message*, PickleIterator*, paramType*) { return false; }
+};
+
+} // namespace IPC
+
+#endif // IPC_GonkNativeHandleUtils_h
diff --git a/system/graphics/layers/ipc/ISurfaceAllocator.cpp b/system/graphics/layers/ipc/ISurfaceAllocator.cpp
new file mode 100644
index 000000000..57da4d9cd
--- /dev/null
+++ b/system/graphics/layers/ipc/ISurfaceAllocator.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ISurfaceAllocator.h"
+
+#include "gfxPrefs.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
+
+mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType()
+{
+ return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+}
+
+void
+HostIPCAllocator::SendPendingAsyncMessages()
+{
+ if (mPendingAsyncMessage.empty()) {
+ return;
+ }
+
+ // Some type of AsyncParentMessageData message could have
+ // one file descriptor (e.g. OpDeliverFence).
+ // A number of file descriptors per gecko ipc message have a limitation
+ // on OS_POSIX (MACOSX or LINUX).
+#if defined(OS_POSIX)
+ static const uint32_t kMaxMessageNumber = FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+ // default number that works everywhere else
+ static const uint32_t kMaxMessageNumber = 250;
+#endif
+
+ InfallibleTArray<AsyncParentMessageData> messages;
+ messages.SetCapacity(mPendingAsyncMessage.size());
+ for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) {
+ messages.AppendElement(mPendingAsyncMessage[i]);
+ // Limit maximum number of messages.
+ if (messages.Length() >= kMaxMessageNumber) {
+ SendAsyncMessage(messages);
+ // Initialize Messages.
+ messages.Clear();
+ }
+ }
+
+ if (messages.Length() > 0) {
+ SendAsyncMessage(messages);
+ }
+ mPendingAsyncMessage.clear();
+}
+
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+
+#ifdef DEBUG
+const uint32_t sSupportedBlockSize = 4;
+#endif
+
+FixedSizeSmallShmemSectionAllocator::FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider)
+: mShmProvider(aShmProvider)
+{
+ MOZ_ASSERT(mShmProvider);
+}
+
+FixedSizeSmallShmemSectionAllocator::~FixedSizeSmallShmemSectionAllocator()
+{
+ ShrinkShmemSectionHeap();
+}
+
+bool
+FixedSizeSmallShmemSectionAllocator::IPCOpen() const
+{
+ return mShmProvider->IPCOpen();
+}
+
+bool
+FixedSizeSmallShmemSectionAllocator::AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection)
+{
+ // For now we only support sizes of 4. If we want to support different sizes
+ // some more complicated bookkeeping should be added.
+ MOZ_ASSERT(aSize == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection);
+
+ if (!IPCOpen()) {
+ gfxCriticalError() << "Attempt to allocate a ShmemSection after shutdown.";
+ return false;
+ }
+
+ uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+ for (size_t i = 0; i < mUsedShmems.size(); i++) {
+ ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if ((header->mAllocatedBlocks + 1) * allocationSize + sizeof(ShmemSectionHeapHeader) < sShmemPageSize) {
+ aShmemSection->shmem() = mUsedShmems[i];
+ MOZ_ASSERT(mUsedShmems[i].IsWritable());
+ break;
+ }
+ }
+
+ if (!aShmemSection->shmem().IsWritable()) {
+ ipc::Shmem tmp;
+ if (!mShmProvider->AllocUnsafeShmem(sShmemPageSize, OptimalShmemType(), &tmp)) {
+ return false;
+ }
+
+ ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+ header->mTotalBlocks = 0;
+ header->mAllocatedBlocks = 0;
+
+ mUsedShmems.push_back(tmp);
+ aShmemSection->shmem() = tmp;
+ }
+
+ MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+ ShmemSectionHeapHeader* header = aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+ uint8_t* heap = aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+ ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+ if (header->mTotalBlocks > header->mAllocatedBlocks) {
+ // Search for the first available block.
+ for (size_t i = 0; i < header->mTotalBlocks; i++) {
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+ if (allocHeader->mStatus == STATUS_FREED) {
+ break;
+ }
+ heap += allocationSize;
+ }
+ MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+ MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+ } else {
+ heap += header->mTotalBlocks * allocationSize;
+
+ header->mTotalBlocks++;
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+ allocHeader->mSize = aSize;
+ }
+
+ MOZ_ASSERT(allocHeader);
+ header->mAllocatedBlocks++;
+ allocHeader->mStatus = STATUS_ALLOCATED;
+
+ aShmemSection->size() = aSize;
+ aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get<uint8_t>();
+ ShrinkShmemSectionHeap();
+ return true;
+}
+
+void
+FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+ MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+ if (!aShmemSection.shmem().IsWritable()) {
+ return;
+ }
+
+ ShmemSectionHeapAllocation* allocHeader =
+ reinterpret_cast<ShmemSectionHeapAllocation*>(aShmemSection.shmem().get<char>() +
+ aShmemSection.offset() -
+ sizeof(ShmemSectionHeapAllocation));
+
+ MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+ DebugOnly<bool> success = allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+ // If this fails something really weird is going on.
+ MOZ_ASSERT(success);
+
+ ShmemSectionHeapHeader* header = aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+ header->mAllocatedBlocks--;
+}
+
+void
+FixedSizeSmallShmemSectionAllocator::DeallocShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+ if (!IPCOpen()) {
+ gfxCriticalNote << "Attempt to dealloc a ShmemSections after shutdown.";
+ return;
+ }
+
+ FreeShmemSection(aShmemSection);
+ ShrinkShmemSectionHeap();
+}
+
+
+void
+FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap()
+{
+ if (!IPCOpen()) {
+ mUsedShmems.clear();
+ return;
+ }
+
+ // The loop will terminate as we either increase i, or decrease size
+ // every time through.
+ size_t i = 0;
+ while (i < mUsedShmems.size()) {
+ ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if (header->mAllocatedBlocks == 0) {
+ mShmProvider->DeallocShmem(mUsedShmems[i]);
+ // We don't particularly care about order, move the last one in the array
+ // to this position.
+ if (i < mUsedShmems.size() - 1) {
+ mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1];
+ }
+ mUsedShmems.pop_back();
+ } else {
+ i++;
+ }
+ }
+}
+
+int32_t
+ClientIPCAllocator::GetMaxTextureSize() const
+{
+ return gfxPrefs::MaxTextureSize();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ISurfaceAllocator.h b/system/graphics/layers/ipc/ISurfaceAllocator.h
new file mode 100644
index 000000000..cd38b7e8b
--- /dev/null
+++ b/system/graphics/layers/ipc/ISurfaceAllocator.h
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_ISURFACEDEALLOCATOR
+#define GFX_LAYERS_ISURFACEDEALLOCATOR
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t
+#include "gfxTypes.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/RefPtr.h"
+#include "nsIMemoryReporter.h" // for nsIMemoryReporter
+#include "mozilla/Atomics.h" // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+class IShmemAllocator;
+} // namespace ipc
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class CompositableForwarder;
+class TextureForwarder;
+
+class ShmemAllocator;
+class ShmemSectionAllocator;
+class LegacySurfaceDescriptorAllocator;
+class ClientIPCAllocator;
+class HostIPCAllocator;
+class LayersIPCChannel;
+
+enum BufferCapabilities {
+ DEFAULT_BUFFER_CAPS = 0,
+ /**
+ * The allocated buffer must be efficiently mappable as a DataSourceSurface.
+ */
+ MAP_AS_IMAGE_SURFACE = 1 << 0,
+ /**
+ * The allocated buffer will be used for GL rendering only
+ */
+ USING_GL_RENDERING_ONLY = 1 << 1
+};
+
+class SurfaceDescriptor;
+
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType();
+
+/**
+ * An interface used to create and destroy surfaces that are shared with the
+ * Compositor process (using shmem, or gralloc, or other platform specific memory)
+ *
+ * Most of the methods here correspond to methods that are implemented by IPDL
+ * actors without a common polymorphic interface.
+ * These methods should be only called in the ipdl implementor's thread, unless
+ * specified otherwise in the implementing class.
+ */
+class ISurfaceAllocator
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ISurfaceAllocator)
+
+ ISurfaceAllocator() {}
+
+ // down-casting
+
+ virtual ShmemAllocator* AsShmemAllocator() { return nullptr; }
+
+ virtual ShmemSectionAllocator* AsShmemSectionAllocator() { return nullptr; }
+
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
+
+ virtual TextureForwarder* GetTextureForwarder() { return nullptr; }
+
+ virtual ClientIPCAllocator* AsClientAllocator() { return nullptr; }
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() { return nullptr; }
+
+ virtual LegacySurfaceDescriptorAllocator*
+ AsLegacySurfaceDescriptorAllocator() { return nullptr; }
+
+ // ipc info
+
+ virtual bool IPCOpen() const { return true; }
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+protected:
+ void Finalize() {}
+
+ virtual ~ISurfaceAllocator() {}
+};
+
+/// Methods that are specific to the client/child side.
+class ClientIPCAllocator : public ISurfaceAllocator
+{
+public:
+ ClientIPCAllocator() {}
+
+ virtual ClientIPCAllocator* AsClientAllocator() override { return this; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop * GetMessageLoop() const = 0;
+
+ virtual int32_t GetMaxTextureSize() const;
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
+};
+
+/// Methods that are specific to the host/parent side.
+class HostIPCAllocator : public ISurfaceAllocator
+{
+public:
+ HostIPCAllocator() {}
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() override { return this; }
+
+ /**
+ * Get child side's process Id.
+ */
+ virtual base::ProcessId GetChildProcessId() = 0;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) = 0;
+
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) = 0;
+
+ virtual void SendPendingAsyncMessages();
+
+ virtual void SetAboutToSendAsyncMessages()
+ {
+ mAboutToSendAsyncMessages = true;
+ }
+
+ bool IsAboutToSendAsyncMessages()
+ {
+ return mAboutToSendAsyncMessages;
+ }
+
+protected:
+ std::vector<AsyncParentMessageData> mPendingAsyncMessage;
+ bool mAboutToSendAsyncMessages = false;
+};
+
+/// An allocator can provide shared memory.
+///
+/// The allocated shmems can be deallocated on either process, as long as they
+/// belong to the same channel.
+class ShmemAllocator
+{
+public:
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
+};
+
+/// An allocator that can group allocations in bigger chunks of shared memory.
+///
+/// The allocated shmem sections can only be deallocated by the same allocator
+/// instance (and only in the child process).
+class ShmemSectionAllocator
+{
+public:
+ virtual bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) = 0;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) = 0;
+
+ virtual void MemoryPressure() {}
+};
+
+/// Some old stuff that's still around and used for screenshots.
+///
+/// New code should not need this (see TextureClient).
+class LegacySurfaceDescriptorAllocator
+{
+public:
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) = 0;
+};
+
+bool
+IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface);
+
+already_AddRefed<gfx::DrawTarget>
+GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend);
+
+already_AddRefed<gfx::DataSourceSurface>
+GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor);
+
+uint8_t*
+GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor);
+
+void
+DestroySurfaceDescriptor(mozilla::ipc::IShmemAllocator* aAllocator, SurfaceDescriptor* aSurface);
+
+class GfxMemoryImageReporter final : public nsIMemoryReporter
+{
+ ~GfxMemoryImageReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ GfxMemoryImageReporter()
+ {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount|
+ // being static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+
+ static void DidAlloc(void* aPointer)
+ {
+ sAmount += MallocSizeOfOnAlloc(aPointer);
+ }
+
+ static void WillFree(void* aPointer)
+ {
+ sAmount -= MallocSizeOfOnFree(aPointer);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount,
+ "Heap memory shared between threads by texture clients and hosts.");
+
+ return NS_OK;
+ }
+
+private:
+ // Typically we use |size_t| in memory reporters, but in the past this
+ // variable has sometimes gone negative due to missing DidAlloc() calls.
+ // Therefore, we use a signed type so that any such negative values show up
+ // as negative in about:memory, rather than as enormous positive numbers.
+ static mozilla::Atomic<ptrdiff_t> sAmount;
+};
+
+/// A simple shmem section allocator that can only allocate small
+/// fixed size elements (only intended to be used to store tile
+/// copy-on-write locks for now).
+class FixedSizeSmallShmemSectionAllocator final : public ShmemSectionAllocator
+{
+public:
+ enum AllocationStatus
+ {
+ STATUS_ALLOCATED,
+ STATUS_FREED
+ };
+
+ struct ShmemSectionHeapHeader
+ {
+ Atomic<uint32_t> mTotalBlocks;
+ Atomic<uint32_t> mAllocatedBlocks;
+ };
+
+ struct ShmemSectionHeapAllocation
+ {
+ Atomic<uint32_t> mStatus;
+ uint32_t mSize;
+ };
+
+ explicit FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider);
+
+ ~FixedSizeSmallShmemSectionAllocator();
+
+ virtual bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) override;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) override;
+
+ virtual void MemoryPressure() override { ShrinkShmemSectionHeap(); }
+
+ // can be called on the compositor process.
+ static void FreeShmemSection(ShmemSection& aShmemSection);
+
+ void ShrinkShmemSectionHeap();
+
+ bool IPCOpen() const;
+
+protected:
+ std::vector<mozilla::ipc::Shmem> mUsedShmems;
+ LayersIPCChannel* mShmProvider;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/ImageBridgeChild.cpp b/system/graphics/layers/ipc/ImageBridgeChild.cpp
new file mode 100644
index 000000000..efd7a0162
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageBridgeChild.cpp
@@ -0,0 +1,1225 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeChild.h"
+#include <vector> // for vector
+#include "ImageBridgeParent.h" // for ImageBridgeParent
+#include "ImageContainer.h" // for ImageContainer
+#include "Layers.h" // for Layer, etc
+#include "ShadowLayers.h" // for ShadowLayerForwarder
+#include "base/message_loop.h" // for MessageLoop
+#include "base/platform_thread.h" // for PlatformThread
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for NewRunnableFunction, etc
+#include "base/thread.h" // for Thread
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
+#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/ImageContainerChild.h"
+#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
+#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mtransport/runnable_utils.h"
+#include "nsContentUtils.h"
+#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsTArrayForwardDeclare.h" // for AutoTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/layers/TextureClient.h"
+#include "SynchronousTask.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using base::Thread;
+using base::ProcessId;
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+typedef std::vector<CompositableOperation> OpVector;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+namespace {
+class ImageBridgeThread : public Thread {
+public:
+
+ ImageBridgeThread() : Thread("ImageBridgeChild") {
+ }
+
+protected:
+
+ MOZ_IS_CLASS_INIT
+ void Init() {
+ }
+
+ void CleanUp() {
+ }
+};
+}
+
+struct CompositableTransaction
+{
+ CompositableTransaction()
+ : mSwapRequired(false)
+ , mFinished(true)
+ {}
+ ~CompositableTransaction()
+ {
+ End();
+ }
+ bool Finished() const
+ {
+ return mFinished;
+ }
+ void Begin()
+ {
+ MOZ_ASSERT(mFinished);
+ mFinished = false;
+ }
+ void End()
+ {
+ mFinished = true;
+ mSwapRequired = false;
+ mOperations.clear();
+ mDestroyedActors.Clear();
+ }
+ bool IsEmpty() const
+ {
+ return mOperations.empty() && mDestroyedActors.IsEmpty();
+ }
+ void AddNoSwapEdit(const CompositableOperation& op)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mOperations.push_back(op);
+ }
+ void AddEdit(const CompositableOperation& op)
+ {
+ AddNoSwapEdit(op);
+ MarkSyncTransaction();
+ }
+ void MarkSyncTransaction()
+ {
+ mSwapRequired = true;
+ }
+
+ OpVector mOperations;
+ OpDestroyVector mDestroyedActors;
+ bool mSwapRequired;
+ bool mFinished;
+};
+
+struct AutoEndTransaction {
+ explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
+ ~AutoEndTransaction() { mTxn->End(); }
+ CompositableTransaction* mTxn;
+};
+
+void
+ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aCompositable->GetIPDLActor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+
+ AutoTArray<TimedTexture,4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+
+ if (!t.mTextureClient->IsSharedWithCompositor()) {
+ return;
+ }
+
+ ReadLockDescriptor readLock;
+ t.mTextureClient->SerializeReadLock(readLock);
+
+ textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
+ readLock,
+ t.mTimeStamp, t.mPictureRect,
+ t.mFrameID, t.mProducerID));
+
+ // Wait end of usage on host side if TextureFlags::RECYCLE is set or GrallocTextureData case
+ HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTexture(textures)));
+}
+
+void
+ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aTextureOnWhite);
+ MOZ_ASSERT(aTextureOnBlack);
+ MOZ_ASSERT(aCompositable->IsConnected());
+ MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
+
+ ReadLockDescriptor readLockW;
+ ReadLockDescriptor readLockB;
+ aTextureOnBlack->SerializeReadLock(readLockB);
+ aTextureOnWhite->SerializeReadLock(readLockW);
+
+ HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
+ HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
+
+ mTxn->AddNoSwapEdit(
+ CompositableOperation(
+ nullptr,
+ aCompositable->GetIPDLActor(),
+ OpUseComponentAlphaTextures(
+ nullptr, aTextureOnBlack->GetIPDLActor(),
+ nullptr, aTextureOnWhite->GetIPDLActor(),
+ readLockB, readLockW
+ )
+ )
+ );
+}
+
+void
+ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
+{
+ // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge.
+ if (!aClient ||
+ !(aClient->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient);
+}
+
+void
+ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ if (aFwdTransactionId < client->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+void
+ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+// Singleton
+static StaticMutex sImageBridgeSingletonLock;
+static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
+static Thread *sImageBridgeChildThread = nullptr;
+
+// dispatched function
+void
+ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask)
+{
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ MediaSystemResourceManager::Shutdown();
+
+ // Force all managed protocols to shut themselves down cleanly
+ InfallibleTArray<PCompositableChild*> compositables;
+ ManagedPCompositableChild(compositables);
+ for (int i = compositables.Length() - 1; i >= 0; --i) {
+ auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
+ if (compositable) {
+ compositable->Destroy();
+ }
+ }
+ InfallibleTArray<PTextureChild*> textures;
+ ManagedPTextureChild(textures);
+ for (int i = textures.Length() - 1; i >= 0; --i) {
+ RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+ if (client) {
+ client->Destroy();
+ }
+ }
+
+ if (mCanSend) {
+ SendWillClose();
+ }
+ MarkShutDown();
+
+ // From now on, no message can be sent through the image bridge from the
+ // client side except the final Stop message.
+}
+
+// dispatched function
+void
+ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
+{
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ if (!mCalledClose) {
+ Close();
+ mCalledClose = true;
+ }
+}
+
+void
+ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+ mCalledClose = true;
+}
+
+void
+ImageBridgeChild::DeallocPImageBridgeChild()
+{
+ this->Release();
+}
+
+void
+ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ AutoCompleteTask complete(aTask);
+ *result = CreateImageClientNow(aType, aImageContainer, aContainerChild);
+}
+
+// dispatched function
+void
+ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult)
+{
+ AutoCompleteTask complete(aTask);
+ *outResult = CreateCanvasClientNow(aType, aFlags);
+}
+
+ImageBridgeChild::ImageBridgeChild()
+ : mCanSend(false)
+ , mCalledClose(false)
+ , mFwdTransactionId(0)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTxn = new CompositableTransaction();
+}
+
+ImageBridgeChild::~ImageBridgeChild()
+{
+ delete mTxn;
+}
+
+void
+ImageBridgeChild::MarkShutDown()
+{
+ mTexturesWaitingRecycled.Clear();
+
+ mCanSend = false;
+}
+
+void
+ImageBridgeChild::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(CanSend());
+
+ uint64_t id = 0;
+
+ PImageContainerChild* imageContainerChild = nullptr;
+ if (aImageContainer)
+ imageContainerChild = aImageContainer->GetPImageContainerChild();
+
+ PCompositableChild* child =
+ SendPCompositableConstructor(aCompositable->GetTextureInfo(),
+ imageContainerChild, &id);
+ if (!child) {
+ return;
+ }
+ aCompositable->InitIPDLActor(child, id);
+}
+
+PCompositableChild*
+ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo,
+ PImageContainerChild* aChild, uint64_t* aID)
+{
+ MOZ_ASSERT(CanSend());
+ return AsyncCompositableChild::CreateActor();
+}
+
+bool
+ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
+{
+ AsyncCompositableChild::DestroyActor(aActor);
+ return true;
+}
+
+
+Thread* ImageBridgeChild::GetThread() const
+{
+ return sImageBridgeChildThread;
+}
+
+/* static */ RefPtr<ImageBridgeChild>
+ImageBridgeChild::GetSingleton()
+{
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ return sImageBridgeChildSingleton;
+}
+
+void
+ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild)
+{
+ if (!aChild) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ReleaseImageContainer,
+ aChild);
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+
+ aChild->SendAsyncDelete();
+}
+
+void
+ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ RELEASE_MANUALLY(aClient);
+}
+
+/* static */ void
+ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
+{
+ if (!aClient) {
+ return;
+ }
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (!imageBridge) {
+ // TextureClient::Release should normally happen in the ImageBridgeChild
+ // thread because it usually generate some IPDL messages.
+ // However, if we take this branch it means that the ImageBridgeChild
+ // has already shut down, along with the TextureChild, which means no
+ // message will be sent and it is safe to run this code from any thread.
+ MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
+ RELEASE_MANUALLY(aClient);
+ return;
+ }
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ imageBridge,
+ &ImageBridgeChild::ReleaseTextureClientNow,
+ aClient);
+ imageBridge->GetMessageLoop()->PostTask(runnable.forget());
+}
+
+void
+ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer)
+{
+ if (!aClient || !aContainer) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateImageClient,
+ aClient,
+ aContainer);
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ // If the client has become disconnected before this event was dispatched,
+ // early return now.
+ if (!aClient->IsConnected()) {
+ return;
+ }
+
+ BeginTransaction();
+ aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
+{
+ AutoCompleteTask complete(aTask);
+
+ UpdateAsyncCanvasRendererNow(aWrapper);
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
+{
+ aWrapper->GetCanvasClient()->UpdateAsync(aWrapper);
+
+ if (InImageBridgeChildThread()) {
+ UpdateAsyncCanvasRendererNow(aWrapper);
+ return;
+ }
+
+ SynchronousTask task("UpdateAsyncCanvasRenderer Lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateAsyncCanvasRendererSync,
+ &task,
+ aWrapper);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
+{
+ MOZ_ASSERT(aWrapper);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ BeginTransaction();
+ aWrapper->GetCanvasClient()->Updated();
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ MOZ_ASSERT(aClient);
+ BeginTransaction();
+ if (aContainer) {
+ aContainer->ClearImagesFromImageBridge();
+ }
+ aClient->FlushAllImages();
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer)
+{
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ if (InImageBridgeChildThread()) {
+ NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
+ return;
+ }
+
+ SynchronousTask task("FlushAllImages Lock");
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::FlushAllImagesSync,
+ &task,
+ aClient,
+ aContainer);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+}
+
+void
+ImageBridgeChild::BeginTransaction()
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin();
+}
+
+void
+ImageBridgeChild::EndTransaction()
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ AutoEndTransaction _(mTxn);
+
+ if (mTxn->IsEmpty()) {
+ return;
+ }
+
+ AutoTArray<CompositableOperation, 10> cset;
+ cset.SetCapacity(mTxn->mOperations.size());
+ if (!mTxn->mOperations.empty()) {
+ cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
+ }
+
+ if (!IsSameProcess()) {
+ ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+ }
+
+ AutoTArray<EditReply, 10> replies;
+
+ if (mTxn->mSwapRequired) {
+ if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId(), &replies)) {
+ NS_WARNING("could not send async texture transaction");
+ return;
+ }
+ } else {
+ // If we don't require a swap we can call SendUpdateNoSwap which
+ // assumes that aReplies is empty (DEBUG assertion)
+ if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
+ NS_WARNING("could not send async texture transaction (no swap)");
+ return;
+ }
+ }
+ for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
+ NS_RUNTIMEABORT("not reached");
+ }
+}
+
+void
+ImageBridgeChild::SendImageBridgeThreadId()
+{
+}
+
+bool
+ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gfxPlatform::GetPlatform();
+
+ if (!sImageBridgeChildThread) {
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->Start()) {
+ return false;
+ }
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+ RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child,
+ &ImageBridgeChild::Bind,
+ Move(aEndpoint));
+ child->GetMessageLoop()->PostTask(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get a
+ // false result and a MsgDropped processing error. This is okay.
+ ShutdownSingleton();
+
+ return InitForContent(Move(aEndpoint));
+}
+
+void
+ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
+}
+
+void
+ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
+{
+ MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
+ ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
+ Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
+}
+
+/* static */ void
+ImageBridgeChild::ShutDown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ShutdownSingleton();
+
+ delete sImageBridgeChildThread;
+ sImageBridgeChildThread = nullptr;
+}
+
+/* static */ void
+ImageBridgeChild::ShutdownSingleton()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->WillShutdown();
+
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = nullptr;
+ }
+}
+
+void
+ImageBridgeChild::WillShutdown()
+{
+ {
+ SynchronousTask task("ImageBridge ShutdownStep1 lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep1,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ }
+
+ {
+ SynchronousTask task("ImageBridge ShutdownStep2 lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep2,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ }
+}
+
+void
+ImageBridgeChild::InitSameProcess()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->IsRunning()) {
+ sImageBridgeChildThread->Start();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+ RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ child,
+ &ImageBridgeChild::BindSameProcess,
+ parent);
+ child->GetMessageLoop()->PostTask(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+/* static */ void
+ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->IsRunning()) {
+ sImageBridgeChildThread->Start();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+ MessageLoop* loop = child->GetMessageLoop();
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child, &ImageBridgeChild::Bind, Move(aEndpoint)));
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+bool InImageBridgeChildThread()
+{
+ return sImageBridgeChildThread &&
+ sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
+}
+
+MessageLoop * ImageBridgeChild::GetMessageLoop() const
+{
+ return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr;
+}
+
+/* static */ void
+ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
+{
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->IdentifyTextureHost(aIdentifier);
+ }
+}
+
+RefPtr<ImageClient>
+ImageBridgeChild::CreateImageClient(CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ if (InImageBridgeChildThread()) {
+ return CreateImageClientNow(aType, aImageContainer, aContainerChild);
+ }
+
+ SynchronousTask task("CreateImageClient Lock");
+
+ RefPtr<ImageClient> result = nullptr;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateImageClientSync,
+ &task,
+ &result,
+ aType,
+ aImageContainer,
+ aContainerChild);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return result;
+}
+
+RefPtr<ImageClient>
+ImageBridgeChild::CreateImageClientNow(CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (!CanSend()) {
+ return nullptr;
+ }
+
+ if (aImageContainer) {
+ aContainerChild->RegisterWithIPDL();
+ if (!SendPImageContainerConstructor(aContainerChild)) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
+ MOZ_ASSERT(client, "failed to create ImageClient");
+ if (client) {
+ client->Connect(aImageContainer);
+ }
+ return client;
+}
+
+already_AddRefed<CanvasClient>
+ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag)
+{
+ if (InImageBridgeChildThread()) {
+ return CreateCanvasClientNow(aType, aFlag);
+ }
+
+ SynchronousTask task("CreateCanvasClient Lock");
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<CanvasClient> result = nullptr;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateCanvasClientSync,
+ &task,
+ aType,
+ aFlag,
+ &result);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return result.forget();
+}
+
+already_AddRefed<CanvasClient>
+ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag)
+{
+ RefPtr<CanvasClient> client
+ = CanvasClient::CreateCanvasClient(aType, this, aFlag);
+ MOZ_ASSERT(client, "failed to create CanvasClient");
+ if (client) {
+ client->Connect();
+ }
+ return client.forget();
+}
+
+bool
+ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+ImageBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+// NewRunnableFunction accepts a limited number of parameters so we need a
+// struct here
+struct AllocShmemParams {
+ size_t mSize;
+ ipc::SharedMemory::SharedMemoryType mType;
+ ipc::Shmem* mShmem;
+ bool mUnsafe;
+ bool mSuccess;
+};
+
+void
+ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aParams->mUnsafe) {
+ ok = AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem);
+ } else {
+ ok = AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem);
+ }
+ aParams->mSuccess = ok;
+}
+
+bool
+ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem,
+ bool aUnsafe)
+{
+ SynchronousTask task("AllocatorProxy alloc");
+
+ AllocShmemParams params = {
+ aSize, aType, aShmem, aUnsafe, false
+ };
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyAllocShmemNow,
+ &task,
+ &params);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return params.mSuccess;
+}
+
+void
+ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem,
+ bool* aResult)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool
+ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (InImageBridgeChildThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::DeallocShmem(aShmem);
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyDeallocShmemNow,
+ &task,
+ &aShmem,
+ &result);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild*
+ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t& aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerChild*
+ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
+{
+ MOZ_ASSERT(CanSend());
+ return new mozilla::media::MediaSystemResourceManagerChild();
+}
+
+bool
+ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
+ return true;
+}
+
+PImageContainerChild*
+ImageBridgeChild::AllocPImageContainerChild()
+{
+ // we always use the "power-user" ctor
+ NS_RUNTIMEABORT("not reached");
+ return nullptr;
+}
+
+bool
+ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor)
+{
+ static_cast<ImageContainerChild*>(actor)->UnregisterFromIPDL();
+ return true;
+}
+
+bool
+ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
+{
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
+{
+ for (auto& n : aNotifications) {
+ ImageContainerChild* child =
+ static_cast<ImageContainerChild*>(n.imageContainerChild());
+ if (child) {
+ child->NotifyComposite(n);
+ }
+ }
+ return true;
+}
+
+PTextureChild*
+ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+static bool
+IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+ if (aTxn->Finished()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+
+ if (synchronously) {
+ aTxn->MarkSyncTransaction();
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+ return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+ return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
+
+void
+ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture)
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsSharedWithCompositor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+ if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
+ return;
+ }
+
+ CompositableOperation op(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor()));
+
+ if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTxn->AddEdit(op);
+ } else {
+ mTxn->AddNoSwapEdit(op);
+ }
+}
+
+bool ImageBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+ImageBridgeChild::Destroy(CompositableChild* aCompositable)
+{
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::Destroy,
+ RefPtr<CompositableChild>(aCompositable));
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+ CompositableForwarder::Destroy(aCompositable);
+}
+
+bool
+ImageBridgeChild::CanSend() const
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ return mCanSend;
+}
+
+void
+ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ImageBridgeChild.h b/system/graphics/layers/ipc/ImageBridgeChild.h
new file mode 100644
index 000000000..f068149f7
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageBridgeChild.h
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H
+#define MOZILLA_GFX_IMAGEBRIDGECHILD_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/PImageBridgeChild.h"
+#include "mozilla/Mutex.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsIObserver.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class AsyncCanvasRenderer;
+class ImageClient;
+class ImageContainer;
+class ImageContainerChild;
+class ImageBridgeParent;
+class CompositableClient;
+struct CompositableTransaction;
+class Image;
+class TextureClient;
+class SynchronousTask;
+struct AllocShmemParams;
+
+/**
+ * Returns true if the current thread is the ImageBrdigeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+bool InImageBridgeChildThread();
+
+/**
+ * The ImageBridge protocol is meant to allow ImageContainers to forward images
+ * directly to the compositor thread/process without using the main thread.
+ *
+ * ImageBridgeChild is a CompositableForwarder just like ShadowLayerForwarder.
+ * This means it also does transactions with the compositor thread/process,
+ * except that the transactions are restricted to operations on the Compositables
+ * and cannot contain messages affecting layers directly.
+ *
+ * ImageBridgeChild is also a ISurfaceAllocator. It can be used to allocate or
+ * deallocate data that is shared with the compositor. The main differerence
+ * with other ISurfaceAllocators is that some of its overriden methods can be
+ * invoked from any thread.
+ *
+ * There are three important phases in the ImageBridge protocol. These three steps
+ * can do different things depending if (A) the ImageContainer uses ImageBridge
+ * or (B) it does not use ImageBridge:
+ *
+ * - When an ImageContainer calls its method SetCurrentImage:
+ * - (A) The image is sent directly to the compositor process through the
+ * ImageBridge IPDL protocol.
+ * On the compositor side the image is stored in a global table that associates
+ * the image with an ID corresponding to the ImageContainer, and a composition is
+ * triggered.
+ * - (B) Since it does not have an ImageBridge, the image is not sent yet.
+ * instead the will be sent to the compositor during the next layer transaction
+ * (on the main thread).
+ *
+ * - During a Layer transaction:
+ * - (A) The ImageContainer uses ImageBridge. The image is already available to the
+ * compositor process because it has been sent with SetCurrentImage. Yet, the
+ * CompositableHost on the compositor side will needs the ID referring to the
+ * ImageContainer to access the Image. So during the Swap operation that happens
+ * in the transaction, we swap the container ID rather than the image data.
+ * - (B) Since the ImageContainer does not use ImageBridge, the image data is swaped.
+ *
+ * - During composition:
+ * - (A) The CompositableHost has an AsyncID, it looks up the ID in the
+ * global table to see if there is an image. If there is no image, nothing is rendered.
+ * - (B) The CompositableHost has image data rather than an ID (meaning it is not
+ * using ImageBridge), then it just composites the image data normally.
+ *
+ * This means that there might be a possibility for the ImageBridge to send the first
+ * frame before the first layer transaction that will pass the container ID to the
+ * CompositableHost happens. In this (unlikely) case the layer is not composited
+ * until the layer transaction happens. This means this scenario is not harmful.
+ *
+ * Since sending an image through imageBridge triggers compositing, the main thread is
+ * not used at all (except for the very first transaction that provides the
+ * CompositableHost with an AsyncID).
+ */
+class ImageBridgeChild final : public PImageBridgeChild
+ , public CompositableForwarder
+ , public TextureForwarder
+{
+ friend class ImageContainer;
+
+ typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageBridgeChild, override);
+
+ TextureForwarder* GetTextureForwarder() override { return this; }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ /**
+ * Creates the image bridge with a dedicated thread for ImageBridgeChild.
+ *
+ * We may want to use a specifi thread in the future. In this case, use
+ * CreateWithThread instead.
+ */
+ static void InitSameProcess();
+
+ static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint);
+ static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
+ static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
+
+ /**
+ * Destroys the image bridge by calling DestroyBridge, and destroys the
+ * ImageBridge's thread.
+ *
+ * If you don't want to destroy the thread, call DestroyBridge directly
+ * instead.
+ */
+ static void ShutDown();
+
+ /**
+ * returns the singleton instance.
+ *
+ * can be called from any thread.
+ */
+ static RefPtr<ImageBridgeChild> GetSingleton();
+
+
+ static void IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ void BeginTransaction();
+ void EndTransaction();
+
+ /**
+ * Returns the ImageBridgeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+ base::Thread * GetThread() const;
+
+ /**
+ * Returns the ImageBridgeChild's message loop.
+ *
+ * Can be called from any thread.
+ */
+ virtual MessageLoop * GetMessageLoop() const override;
+
+ virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo,
+ PImageContainerChild* aChild, uint64_t* aID) override;
+ bool DeallocPCompositableChild(PCompositableChild* aActor) override;
+
+ virtual PTextureChild*
+ AllocPTextureChild(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aSerial) override;
+
+ virtual bool
+ DeallocPTextureChild(PTextureChild* actor) override;
+
+ PMediaSystemResourceManagerChild*
+ AllocPMediaSystemResourceManagerChild() override;
+ bool
+ DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) override;
+
+ virtual PImageContainerChild*
+ AllocPImageContainerChild() override;
+ virtual bool
+ DeallocPImageContainerChild(PImageContainerChild* actor) override;
+
+ virtual bool
+ RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
+
+ virtual bool
+ RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) override;
+
+ // Create an ImageClient from any thread.
+ RefPtr<ImageClient> CreateImageClient(
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ // Create an ImageClient from the ImageBridge thread.
+ RefPtr<ImageClient> CreateImageClientNow(
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag);
+ void ReleaseImageContainer(RefPtr<ImageContainerChild> aChild);
+ void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
+ void UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer);
+ static void DispatchReleaseTextureClient(TextureClient* aClient);
+
+ /**
+ * Flush all Images sent to CompositableHost.
+ */
+ void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+ virtual bool IPCOpen() const override { return mCanSend; }
+
+private:
+
+ /**
+ * This must be called by the static function DeleteImageBridgeSync defined
+ * in ImageBridgeChild.cpp ONLY.
+ */
+ ~ImageBridgeChild();
+
+ // Helpers for dispatching.
+ already_AddRefed<CanvasClient> CreateCanvasClientNow(
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags);
+ void CreateCanvasClientSync(
+ SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult);
+
+ void CreateImageClientSync(
+ SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ void ReleaseTextureClientNow(TextureClient* aClient);
+
+ void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
+ void UpdateAsyncCanvasRendererSync(
+ SynchronousTask* aTask,
+ AsyncCanvasRenderer* aWrapper);
+
+ void FlushAllImagesSync(
+ SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer);
+
+ void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, Shmem* aShmem, bool* aResult);
+
+public:
+ // CompositableForwarder
+
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ virtual bool UsesImageBridge() const override { return true; }
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ void Destroy(CompositableChild* aCompositable) override;
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set.
+ * Host side's usage is checked via CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
+
+ virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+ virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override
+ {
+ NS_RUNTIMEABORT("should not be called");
+ }
+
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override {
+ NS_RUNTIMEABORT("should not be called");
+ }
+
+ // ISurfaceAllocator
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ virtual bool IsSameProcess() const override;
+
+ virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
+ virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
+
+ bool InForwarderThread() override {
+ return InImageBridgeChildThread();
+ }
+
+ virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+protected:
+ ImageBridgeChild();
+ bool DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ Shmem* aShmem,
+ bool aUnsafe);
+
+ void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+ void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+ void SendImageBridgeThreadId();
+
+ void WillShutdown();
+ void ShutdownStep1(SynchronousTask* aTask);
+ void ShutdownStep2(SynchronousTask* aTask);
+ void MarkShutDown();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPImageBridgeChild() override;
+
+ bool CanSend() const;
+
+ static void ShutdownSingleton();
+
+private:
+ CompositableTransaction* mTxn;
+
+ bool mCanSend;
+ bool mCalledClose;
+
+ /**
+ * Transaction id of CompositableForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/ImageBridgeParent.cpp b/system/graphics/layers/ipc/ImageBridgeParent.cpp
new file mode 100644
index 000000000..4944dfc84
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageBridgeParent.cpp
@@ -0,0 +1,448 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeParent.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableHost.h" // for CompositableParent, Create
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, DeleteTask, etc
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority()
+#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
+#include "mozilla/layers/CompositableTransactionParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply
+#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent
+#include "mozilla/layers/PCompositableParent.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT, etc
+#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/layers/TextureHost.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeParent::sImageBridges;
+
+StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
+
+// defined in CompositorBridgeParent.cpp
+CompositorThreadHolder* GetCompositorThreadHolder();
+
+/* static */ void
+ImageBridgeParent::Setup()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sImageBridgesLock) {
+ sImageBridgesLock = new Monitor("ImageBridges");
+ mozilla::ClearOnShutdown(&sImageBridgesLock);
+ }
+}
+
+ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
+ ProcessId aChildProcessId)
+ : mMessageLoop(aLoop)
+ , mSetChildThreadPriority(false)
+ , mClosed(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // creates the map only if it has not been created already, so it is safe
+ // with several bridges
+ CompositableMap::Create();
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges[aChildProcessId] = this;
+ }
+ SetOtherProcessId(aChildProcessId);
+}
+
+ImageBridgeParent::~ImageBridgeParent()
+{
+ nsTArray<PImageContainerParent*> parents;
+ ManagedPImageContainerParent(parents);
+ for (PImageContainerParent* p : parents) {
+ delete p;
+ }
+}
+
+static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
+
+void ReleaseImageBridgeParentSingleton() {
+ sImageBridgeParentSingleton = nullptr;
+}
+
+/* static */ ImageBridgeParent*
+ImageBridgeParent::CreateSameProcess()
+{
+ RefPtr<ImageBridgeParent> parent =
+ new ImageBridgeParent(CompositorThreadHolder::Loop(), base::GetCurrentProcId());
+ parent->mSelfRef = parent;
+
+ sImageBridgeParentSingleton = parent;
+ return parent;
+}
+
+/* static */ bool
+ImageBridgeParent::CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+ RefPtr<ImageBridgeParent> parent = new ImageBridgeParent(loop, aEndpoint.OtherPid());
+
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ parent, &ImageBridgeParent::Bind, Move(aEndpoint)));
+
+ sImageBridgeParentSingleton = parent;
+ return true;
+}
+
+void
+ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges.erase(OtherPid());
+ }
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy));
+
+ // It is very important that this method gets called at shutdown (be it a clean
+ // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef.
+ // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent
+ // is leaked which causes the CompositorThreadHolder to be leaked and
+ // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting
+ // for the compositor thread to terminate.
+}
+
+bool
+ImageBridgeParent::RecvImageBridgeThreadId(const PlatformThreadId& aThreadId)
+{
+ MOZ_ASSERT(!mSetChildThreadPriority);
+ if (mSetChildThreadPriority) {
+ return false;
+ }
+ mSetChildThreadPriority = true;
+ return true;
+}
+
+class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender
+{
+public:
+ explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge,
+ InfallibleTArray<OpDestroy>* aToDestroy = nullptr)
+ : mImageBridge(aImageBridge)
+ , mToDestroy(aToDestroy)
+ {
+ mImageBridge->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoImageBridgeParentAsyncMessageSender()
+ {
+ mImageBridge->SendPendingAsyncMessages();
+ if (mToDestroy) {
+ for (const auto& op : *mToDestroy) {
+ mImageBridge->DestroyActor(op);
+ }
+ }
+ }
+private:
+ ImageBridgeParent* mImageBridge;
+ InfallibleTArray<OpDestroy>* mToDestroy;
+};
+
+bool
+ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ EditReplyArray* aReply)
+{
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ EditReplyVector replyv;
+ for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) {
+ if (!ReceiveCompositableUpdate(aEdits[i], replyv)) {
+ return false;
+ }
+ }
+
+ aReply->SetCapacity(replyv.size());
+ if (replyv.size() > 0) {
+ aReply->AppendElements(&replyv.front(), replyv.size());
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeParent::RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId)
+{
+ InfallibleTArray<EditReply> noReplies;
+ bool success = RecvUpdate(Move(aEdits), Move(aToDestroy), aFwdTransactionId, &noReplies);
+ MOZ_ASSERT(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits");
+ return success;
+}
+
+/* static */ bool
+ImageBridgeParent::CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+
+ RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aEndpoint.OtherPid());
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ bridge, &ImageBridgeParent::Bind, Move(aEndpoint)));
+
+ return true;
+}
+
+void
+ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this))
+ return;
+ mSelfRef = this;
+}
+
+bool ImageBridgeParent::RecvWillClose()
+{
+ // If there is any texture still alive we have to force it to deallocate the
+ // device data (GL textures, etc.) now because shortly after SenStop() returns
+ // on the child side the widget will be destroyed along with it's associated
+ // GL context.
+ InfallibleTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+ return true;
+}
+
+static uint64_t GenImageContainerID() {
+ static uint64_t sNextImageID = 1;
+
+ ++sNextImageID;
+ return sNextImageID;
+}
+
+PCompositableParent*
+ImageBridgeParent::AllocPCompositableParent(const TextureInfo& aInfo,
+ PImageContainerParent* aImageContainer,
+ uint64_t* aID)
+{
+ uint64_t id = GenImageContainerID();
+ *aID = id;
+ return CompositableHost::CreateIPDLActor(this, aInfo, id, aImageContainer);
+}
+
+bool ImageBridgeParent::DeallocPCompositableParent(PCompositableParent* aActor)
+{
+ return CompositableHost::DestroyIPDLActor(aActor);
+}
+
+PTextureParent*
+ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial)
+{
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerParent*
+ImageBridgeParent::AllocPMediaSystemResourceManagerParent()
+{
+ return new mozilla::media::MediaSystemResourceManagerParent();
+}
+
+bool
+ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor)
+{
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor);
+ return true;
+}
+
+PImageContainerParent*
+ImageBridgeParent::AllocPImageContainerParent()
+{
+ return new ImageContainerParent();
+}
+
+bool
+ImageBridgeParent::DeallocPImageContainerParent(PImageContainerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ mozilla::Unused << SendParentAsyncMessages(aMessage);
+}
+
+class ProcessIdComparator
+{
+public:
+ bool Equals(const ImageCompositeNotification& aA,
+ const ImageCompositeNotification& aB) const
+ {
+ return aA.imageContainerParent()->OtherPid() == aB.imageContainerParent()->OtherPid();
+ }
+ bool LessThan(const ImageCompositeNotification& aA,
+ const ImageCompositeNotification& aB) const
+ {
+ return aA.imageContainerParent()->OtherPid() < aB.imageContainerParent()->OtherPid();
+ }
+};
+
+/* static */ bool
+ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications)
+{
+ // Group the notifications by destination process ID and then send the
+ // notifications in one message per group.
+ aNotifications.Sort(ProcessIdComparator());
+ uint32_t i = 0;
+ bool ok = true;
+ while (i < aNotifications.Length()) {
+ AutoTArray<ImageCompositeNotification,1> notifications;
+ notifications.AppendElement(aNotifications[i]);
+ uint32_t end = i + 1;
+ MOZ_ASSERT(aNotifications[i].imageContainerParent());
+ ProcessId pid = aNotifications[i].imageContainerParent()->OtherPid();
+ while (end < aNotifications.Length() &&
+ aNotifications[end].imageContainerParent()->OtherPid() == pid) {
+ notifications.AppendElement(aNotifications[end]);
+ ++end;
+ }
+ GetInstance(pid)->SendPendingAsyncMessages();
+ if (!GetInstance(pid)->SendDidComposite(notifications)) {
+ ok = false;
+ }
+ i = end;
+ }
+ return ok;
+}
+
+void
+ImageBridgeParent::DeferredDestroy()
+{
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr; // "this" ImageBridge may get deleted here.
+}
+
+RefPtr<ImageBridgeParent>
+ImageBridgeParent::GetInstance(ProcessId aId)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(*sImageBridgesLock);
+ NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process");
+ return sImageBridges[aId];
+}
+
+void
+ImageBridgeParent::OnChannelConnected(int32_t aPid)
+{
+ mCompositorThreadHolder = GetCompositorThreadHolder();
+}
+
+
+bool
+ImageBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+ImageBridgeParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (mClosed) {
+ return;
+ }
+ PImageBridgeParent::DeallocShmem(aShmem);
+}
+
+bool ImageBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(
+ OpNotifyNotUsed(textureId, aTransactionId));
+
+ if (!IsAboutToSendAsyncMessages()) {
+ SendPendingAsyncMessages();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ImageBridgeParent.h b/system/graphics/layers/ipc/ImageBridgeParent.h
new file mode 100644
index 000000000..2dc705691
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageBridgeParent.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_ImageBridgeParent_h_
+#define gfx_layers_ipc_ImageBridgeParent_h_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "CompositableTransactionParent.h"
+#include "ImageContainerParent.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "nsISupportsImpl.h"
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+/**
+ * ImageBridgeParent is the manager Protocol of ImageContainerParent.
+ * It's purpose is mainly to setup the IPDL connection. Most of the
+ * interesting stuff is in ImageContainerParent.
+ */
+class ImageBridgeParent final : public PImageBridgeParent,
+ public CompositableParentManager,
+ public ShmemAllocator
+{
+public:
+ typedef InfallibleTArray<CompositableOperation> EditArray;
+ typedef InfallibleTArray<OpDestroy> OpDestroyArray;
+ typedef InfallibleTArray<EditReply> EditReplyArray;
+
+protected:
+ ImageBridgeParent(MessageLoop* aLoop, ProcessId aChildProcessId);
+
+public:
+ ~ImageBridgeParent();
+
+ /**
+ * Creates the globals of ImageBridgeParent.
+ */
+ static void Setup();
+
+ static ImageBridgeParent* CreateSameProcess();
+ static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint);
+ static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // CompositableParentManager
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+
+ virtual base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+
+ // PImageBridge
+ virtual bool RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
+ virtual bool RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ EditReplyArray* aReply) override;
+ virtual bool RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId) override;
+
+ PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo,
+ PImageContainerParent* aImageContainer,
+ uint64_t*) override;
+ bool DeallocPCompositableParent(PCompositableParent* aActor) override;
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override;
+ bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override;
+ virtual PImageContainerParent* AllocPImageContainerParent() override;
+ virtual bool DeallocPImageContainerParent(PImageContainerParent* actor) override;
+
+ // Shutdown step 1
+ virtual bool RecvWillClose() override;
+
+ MessageLoop* GetMessageLoop() const { return mMessageLoop; }
+
+ // ShmemAllocator
+
+ virtual bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual void DeallocShmem(ipc::Shmem& aShmem) override;
+
+ virtual bool IsSameProcess() const override;
+
+ static RefPtr<ImageBridgeParent> GetInstance(ProcessId aId);
+
+ static bool NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications);
+
+ virtual bool UsesImageBridge() const override { return true; }
+
+ virtual bool IPCOpen() const override { return !mClosed; }
+
+protected:
+ void OnChannelConnected(int32_t pid) override;
+
+ void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+private:
+ void DeferredDestroy();
+ MessageLoop* mMessageLoop;
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<ImageBridgeParent> mSelfRef;
+
+ bool mSetChildThreadPriority;
+ bool mClosed;
+
+ /**
+ * Map of all living ImageBridgeParent instances
+ */
+ static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_ImageBridgeParent_h_
diff --git a/system/graphics/layers/ipc/ImageContainerChild.cpp b/system/graphics/layers/ipc/ImageContainerChild.cpp
new file mode 100644
index 000000000..c54eb2c41
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageContainerChild.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainerChild.h"
+#include "ImageContainer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageContainerChild::ImageContainerChild(ImageContainer* aImageContainer)
+ : mLock("ImageContainerChild")
+ , mImageContainer(aImageContainer)
+ , mIPCOpen(false)
+{
+}
+
+void
+ImageContainerChild::ForgetImageContainer()
+{
+ MutexAutoLock lock(mLock);
+ mImageContainer = nullptr;
+}
+
+void
+ImageContainerChild::NotifyComposite(const ImageCompositeNotification& aNotification)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ MutexAutoLock lock(mLock);
+ if (mImageContainer) {
+ mImageContainer->NotifyCompositeInternal(aNotification);
+ }
+}
+
+void
+ImageContainerChild::RegisterWithIPDL()
+{
+ MOZ_ASSERT(!mIPCOpen);
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ AddRef();
+ mIPCOpen = true;
+}
+
+void
+ImageContainerChild::UnregisterFromIPDL()
+{
+ MOZ_ASSERT(mIPCOpen);
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ mIPCOpen = false;
+ Release();
+}
+
+void
+ImageContainerChild::SendAsyncDelete()
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ if (mIPCOpen) {
+ PImageContainerChild::SendAsyncDelete();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ImageContainerChild.h b/system/graphics/layers/ipc/ImageContainerChild.h
new file mode 100644
index 000000000..839540411
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageContainerChild.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_layers_ImageContainerChild_h
+#define mozilla_gfx_layers_ImageContainerChild_h
+
+#include "mozilla/Mutex.h"
+#include "mozilla/layers/PImageContainerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageContainer;
+class ImageCompositeNotification;
+
+/**
+ * The child side of PImageContainer. It's best to avoid ImageContainer filling
+ * this role since IPDL objects should be associated with a single thread and
+ * ImageContainer definitely isn't. This object belongs to (and is always
+ * destroyed on) the ImageBridge thread, except when we need to destroy it
+ * during shutdown.
+ * An ImageContainer owns one of these; we have a weak reference to our
+ * ImageContainer.
+ */
+class ImageContainerChild final : public PImageContainerChild
+{
+public:
+ explicit ImageContainerChild(ImageContainer* aImageContainer);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainerChild)
+
+ void RegisterWithIPDL();
+ void UnregisterFromIPDL();
+ void SendAsyncDelete();
+
+ void NotifyComposite(const ImageCompositeNotification& aNotification);
+ void ForgetImageContainer();
+
+private:
+ ~ImageContainerChild()
+ {}
+
+private:
+ Mutex mLock;
+ ImageContainer* mImageContainer;
+
+ // If mIPCOpen is false, it means the IPDL code tried to deallocate the actor
+ // before the ImageContainer released it. When this happens we don't actually
+ // delete the actor right away because the ImageContainer has a reference to
+ // it. In this case the actor will be deleted when the ImageContainer lets go
+ // of it.
+ // mIPCOpen must not be accessed off the ImageBridgeChild thread.
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_ImageContainerChild_h
diff --git a/system/graphics/layers/ipc/ImageContainerParent.cpp b/system/graphics/layers/ipc/ImageContainerParent.cpp
new file mode 100644
index 000000000..0dc0d6d32
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageContainerParent.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainerParent.h"
+
+#include "nsThreadUtils.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageContainerParent::~ImageContainerParent()
+{
+ while (!mImageHosts.IsEmpty()) {
+ mImageHosts[mImageHosts.Length() - 1]->SetImageContainer(nullptr);
+ }
+}
+
+bool ImageContainerParent::RecvAsyncDelete()
+{
+ Unused << PImageContainerParent::Send__delete__(this);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ImageContainerParent.h b/system/graphics/layers/ipc/ImageContainerParent.h
new file mode 100644
index 000000000..849bcb44f
--- /dev/null
+++ b/system/graphics/layers/ipc/ImageContainerParent.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ImageContainerParent_h
+#define mozilla_layers_ImageContainerParent_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PImageContainerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+
+class ImageContainerParent : public PImageContainerParent
+{
+public:
+ ImageContainerParent() {}
+ ~ImageContainerParent();
+
+ virtual bool RecvAsyncDelete() override;
+
+ AutoTArray<ImageHost*,1> mImageHosts;
+
+private:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ImageContainerParent_h
diff --git a/system/graphics/layers/ipc/KnowsCompositor.h b/system/graphics/layers/ipc/KnowsCompositor.h
new file mode 100644
index 000000000..940038ecf
--- /dev/null
+++ b/system/graphics/layers/ipc/KnowsCompositor.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_KNOWSCOMPOSITOR
+#define MOZILLA_LAYERS_KNOWSCOMPOSITOR
+
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/CompositorTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+class SyncObject;
+class TextureForwarder;
+class LayersIPCActor;
+
+/**
+ * An abstract interface for classes that are tied to a specific Compositor across
+ * IPDL and uses TextureFactoryIdentifier to describe this Compositor.
+ */
+class KnowsCompositor {
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ KnowsCompositor();
+ ~KnowsCompositor();
+
+ void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ SyncObject* GetSyncObject() { return mSyncObject; }
+
+ int32_t GetMaxTextureSize() const
+ {
+ return mTextureFactoryIdentifier.mMaxTextureSize;
+ }
+
+ /**
+ * Returns the type of backend that is used off the main thread.
+ * We only don't allow changing the backend type at runtime so this value can
+ * be queried once and will not change until Gecko is restarted.
+ */
+ LayersBackend GetCompositorBackendType() const
+ {
+ return mTextureFactoryIdentifier.mParentBackend;
+ }
+
+ bool SupportsTextureBlitting() const
+ {
+ return mTextureFactoryIdentifier.mSupportsTextureBlitting;
+ }
+
+ bool SupportsPartialUploads() const
+ {
+ return mTextureFactoryIdentifier.mSupportsPartialUploads;
+ }
+
+ bool SupportsComponentAlpha() const
+ {
+ return mTextureFactoryIdentifier.mSupportsComponentAlpha;
+ }
+
+ const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
+ {
+ return mTextureFactoryIdentifier;
+ }
+
+ int32_t GetSerial() { return mSerial; }
+
+ /**
+ * Helpers for finding other related interface. These are infallible.
+ */
+ virtual TextureForwarder* GetTextureForwarder() = 0;
+ virtual LayersIPCActor* GetLayersIPCActor() = 0;
+
+protected:
+ TextureFactoryIdentifier mTextureFactoryIdentifier;
+ RefPtr<SyncObject> mSyncObject;
+
+ const int32_t mSerial;
+ static mozilla::Atomic<int32_t> sSerialCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/LayerAnimationUtils.cpp b/system/graphics/layers/ipc/LayerAnimationUtils.cpp
new file mode 100644
index 000000000..dde601cfc
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerAnimationUtils.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerAnimationUtils.h"
+#include "mozilla/ComputedTimingFunction.h" // For ComputedTimingFunction
+#include "mozilla/layers/LayersMessages.h" // For TimingFunction etc.
+
+namespace mozilla {
+namespace layers {
+
+/* static */ Maybe<ComputedTimingFunction>
+AnimationUtils::TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction)
+{
+ switch (aTimingFunction.type()) {
+ case TimingFunction::Tnull_t:
+ return Nothing();
+ case TimingFunction::TCubicBezierFunction: {
+ ComputedTimingFunction result;
+ CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction();
+ result.Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2()));
+ return Some(result);
+ }
+ case TimingFunction::TStepFunction: {
+ StepFunction sf = aTimingFunction.get_StepFunction();
+ nsTimingFunction::Type type = sf.type() == 1 ?
+ nsTimingFunction::Type::StepStart :
+ nsTimingFunction::Type::StepEnd;
+ ComputedTimingFunction result;
+ result.Init(nsTimingFunction(type, sf.steps()));
+ return Some(result);
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "Function must be null, bezier or step");
+ break;
+ }
+ return Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/LayerAnimationUtils.h b/system/graphics/layers/ipc/LayerAnimationUtils.h
new file mode 100644
index 000000000..bab004d66
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerAnimationUtils.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerAnimationUtils_h
+#define mozilla_layers_LayerAnimationUtils_h
+
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class ComputedTimingFunction;
+
+namespace layers {
+
+class TimingFunction;
+
+class AnimationUtils
+{
+public:
+ static Maybe<ComputedTimingFunction> TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerAnimationUtils_h
diff --git a/system/graphics/layers/ipc/LayerTransactionChild.cpp b/system/graphics/layers/ipc/LayerTransactionChild.cpp
new file mode 100644
index 000000000..f07e2c27f
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTransactionChild.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionChild.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+#include "mozilla/layers/PImageContainerChild.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT, etc
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+
+void
+LayerTransactionChild::Destroy()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ // mDestroyed is used to prevent calling Send__delete__() twice.
+ // When this function is called from CompositorBridgeChild::Destroy(),
+ // under Send__delete__() call, this function is called from
+ // ShadowLayerForwarder's destructor.
+ // When it happens, IPCOpen() is still true.
+ // See bug 1004191.
+ mDestroyed = true;
+
+ SendShutdown();
+}
+
+
+PLayerChild*
+LayerTransactionChild::AllocPLayerChild()
+{
+ // we always use the "power-user" ctor
+ NS_RUNTIMEABORT("not reached");
+ return nullptr;
+}
+
+bool
+LayerTransactionChild::DeallocPLayerChild(PLayerChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+PCompositableChild*
+LayerTransactionChild::AllocPCompositableChild(const TextureInfo& aInfo)
+{
+ MOZ_ASSERT(!mDestroyed);
+ return CompositableChild::CreateActor();
+}
+
+bool
+LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor)
+{
+ CompositableChild::DestroyActor(actor);
+ return true;
+}
+
+void
+LayerTransactionChild::ActorDestroy(ActorDestroyReason why)
+{
+ mDestroyed = true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/LayerTransactionChild.h b/system/graphics/layers/ipc/LayerTransactionChild.h
new file mode 100644
index 000000000..3d56399f4
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTransactionChild.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+namespace layout {
+class RenderFrameChild;
+} // namespace layout
+
+namespace layers {
+
+class ShadowLayerForwarder;
+
+class LayerTransactionChild : public PLayerTransactionChild
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild)
+ /**
+ * Clean this up, finishing with SendShutDown() which will cause __delete__
+ * to be sent from the parent side.
+ *
+ * It is expected (checked with an assert) that all shadow layers
+ * created by this have already been destroyed and
+ * Send__delete__()d by the time this method is called.
+ */
+ void Destroy();
+
+ bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
+ bool IsDestroyed() const { return mDestroyed; }
+
+ void SetForwarder(ShadowLayerForwarder* aForwarder)
+ {
+ mForwarder = aForwarder;
+ }
+
+ uint64_t GetId() const { return mId; }
+
+protected:
+ explicit LayerTransactionChild(const uint64_t& aId)
+ : mForwarder(nullptr)
+ , mIPCOpen(false)
+ , mDestroyed(false)
+ , mId(aId)
+ {}
+ ~LayerTransactionChild() { }
+
+ virtual PLayerChild* AllocPLayerChild() override;
+ virtual bool DeallocPLayerChild(PLayerChild* actor) override;
+
+ virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) override;
+ virtual bool DeallocPCompositableChild(PCompositableChild* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeChild;
+ friend class layout::RenderFrameChild;
+
+ ShadowLayerForwarder* mForwarder;
+ bool mIPCOpen;
+ bool mDestroyed;
+ uint64_t mId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
diff --git a/system/graphics/layers/ipc/LayerTransactionParent.cpp b/system/graphics/layers/ipc/LayerTransactionParent.cpp
new file mode 100644
index 000000000..c30ccee5b
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTransactionParent.cpp
@@ -0,0 +1,1098 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionParent.h"
+#include <vector> // for vector
+#include "apz/src/AsyncPanZoomController.h"
+#include "CompositableHost.h" // for CompositableParent, Get, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ShadowLayerParent.h" // for ShadowLayerParent
+#include "CompositableTransactionParent.h" // for EditReplyVector
+#include "CompositorBridgeParent.h"
+#include "gfxPrefs.h"
+#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
+#include "mozilla/layers/CanvasLayerComposite.h"
+#include "mozilla/layers/ColorLayerComposite.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ContainerLayerComposite.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/ImageLayerComposite.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/PCompositableParent.h"
+#include "mozilla/layers/PLayerParent.h" // for PLayerParent
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/Unused.h"
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsLayoutUtils.h" // for nsLayoutUtils
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "TreeTraversal.h" // for ForEachNode
+#include "GeckoProfiler.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/AsyncCompositionManager.h"
+
+typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
+
+using mozilla::layout::RenderFrameParent;
+
+namespace mozilla {
+namespace layers {
+
+//--------------------------------------------------
+// Convenience accessors
+static ShadowLayerParent*
+cast(const PLayerParent* in)
+{
+ return const_cast<ShadowLayerParent*>(
+ static_cast<const ShadowLayerParent*>(in));
+}
+
+template<class OpCreateT>
+static ShadowLayerParent*
+AsLayerComposite(const OpCreateT& op)
+{
+ return cast(op.layerParent());
+}
+
+static ShadowLayerParent*
+AsLayerComposite(const OpSetRoot& op)
+{
+ return cast(op.rootParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpInsertAfter& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpInsertAfter& op)
+{
+ return cast(op.childLayerParent());
+}
+static ShadowLayerParent*
+ShadowAfter(const OpInsertAfter& op)
+{
+ return cast(op.afterParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpPrependChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpPrependChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRemoveChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRemoveChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRepositionChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRepositionChild& op)
+{
+ return cast(op.childLayerParent());
+}
+static ShadowLayerParent*
+ShadowAfter(const OpRepositionChild& op)
+{
+ return cast(op.afterParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRaiseToTopChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRaiseToTopChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+//--------------------------------------------------
+// LayerTransactionParent
+LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
+ CompositorBridgeParentBase* aBridge,
+ uint64_t aId)
+ : mLayerManager(aManager)
+ , mCompositorBridge(aBridge)
+ , mId(aId)
+ , mChildEpoch(0)
+ , mParentEpoch(0)
+ , mPendingTransaction(0)
+ , mPendingCompositorUpdates(0)
+ , mDestroyed(false)
+ , mIPCOpen(false)
+{
+}
+
+LayerTransactionParent::~LayerTransactionParent()
+{
+}
+
+void
+LayerTransactionParent::SetLayerManager(LayerManagerComposite* aLayerManager)
+{
+ mLayerManager = aLayerManager;
+ const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent();
+ for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) {
+ ShadowLayerParent* slp =
+ static_cast<ShadowLayerParent*>(iter.Get()->GetKey());
+ if (slp->AsLayer() && slp->AsLayer()->AsLayerComposite()) {
+ slp->AsLayer()->AsLayerComposite()->SetLayerManager(aLayerManager);
+ }
+ }
+}
+
+bool
+LayerTransactionParent::RecvShutdown()
+{
+ Destroy();
+ return Send__delete__(this);
+}
+
+void
+LayerTransactionParent::Destroy()
+{
+ const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent();
+ for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) {
+ ShadowLayerParent* slp =
+ static_cast<ShadowLayerParent*>(iter.Get()->GetKey());
+ slp->Destroy();
+ }
+ mDestroyed = true;
+}
+
+bool
+LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray<Edit>&& cset,
+ InfallibleTArray<OpDestroy>&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId)
+{
+ return RecvUpdate(Move(cset), Move(aToDestroy), aFwdTransactionId,
+ aTransactionId, targetConfig, Move(aPlugins), isFirstPaint,
+ scheduleComposite, paintSequenceNumber, isRepeatTransaction,
+ aTransactionStart, aPaintSyncId, nullptr);
+}
+
+class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender
+{
+public:
+ explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction,
+ InfallibleTArray<OpDestroy>* aDestroyActors = nullptr)
+ : mLayerTransaction(aLayerTransaction)
+ , mActorsToDestroy(aDestroyActors)
+ {
+ mLayerTransaction->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoLayerTransactionParentAsyncMessageSender()
+ {
+ mLayerTransaction->SendPendingAsyncMessages();
+ if (mActorsToDestroy) {
+ // Destroy the actors after sending the async messages because the latter may contain
+ // references to some actors.
+ for (const auto& op : *mActorsToDestroy) {
+ mLayerTransaction->DestroyActor(op);
+ }
+ }
+ }
+private:
+ LayerTransactionParent* mLayerTransaction;
+ InfallibleTArray<OpDestroy>* mActorsToDestroy;
+};
+
+bool
+LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
+ const TimeDuration& aPaintTime)
+{
+ mCompositorBridge->UpdatePaintTime(this, aPaintTime);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
+ InfallibleTArray<OpDestroy>&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId,
+ InfallibleTArray<EditReply>* reply)
+{
+ profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_START);
+ PROFILER_LABEL("LayerTransactionParent", "RecvUpdate",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeStamp updateStart = TimeStamp::Now();
+#endif
+
+ MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
+
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ for (const auto& op : aToDestroy) {
+ DestroyActor(op);
+ }
+ return true;
+ }
+
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
+ EditReplyVector replyv;
+
+ {
+ AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
+ layer_manager()->BeginTransaction();
+ }
+
+ // not all edits require an update to the hit testing tree
+ bool updateHitTestingTree = false;
+
+ for (EditArray::index_type i = 0; i < cset.Length(); ++i) {
+ const Edit& edit = cset[i];
+
+ switch (edit.type()) {
+ // Create* ops
+ case Edit::TOpCreatePaintedLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer"));
+
+ RefPtr<PaintedLayerComposite> layer =
+ layer_manager()->CreatePaintedLayerComposite();
+ AsLayerComposite(edit.get_OpCreatePaintedLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateContainerLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer"));
+
+ RefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayerComposite();
+ AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateImageLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer"));
+
+ RefPtr<ImageLayerComposite> layer =
+ layer_manager()->CreateImageLayerComposite();
+ AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateColorLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer"));
+
+ RefPtr<ColorLayerComposite> layer = layer_manager()->CreateColorLayerComposite();
+ AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateCanvasLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
+
+ RefPtr<CanvasLayerComposite> layer =
+ layer_manager()->CreateCanvasLayerComposite();
+ AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateRefLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer"));
+
+ RefPtr<RefLayerComposite> layer =
+ layer_manager()->CreateRefLayerComposite();
+ AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+
+ // Attributes
+ case Edit::TOpSetLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
+
+ const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
+ ShadowLayerParent* layerParent = AsLayerComposite(osla);
+ Layer* layer = layerParent->AsLayer();
+ if (!layer) {
+ return false;
+ }
+ const LayerAttributes& attrs = osla.attrs();
+
+ const CommonLayerAttributes& common = attrs.common();
+ layer->SetLayerBounds(common.layerBounds());
+ layer->SetVisibleRegion(common.visibleRegion());
+ layer->SetEventRegions(common.eventRegions());
+ layer->SetContentFlags(common.contentFlags());
+ layer->SetOpacity(common.opacity());
+ layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing());
+ layer->SetScrolledClip(common.scrolledClip());
+ layer->SetBaseTransform(common.transform().value());
+ layer->SetTransformIsPerspective(common.transformIsPerspective());
+ layer->SetPostScale(common.postXScale(), common.postYScale());
+ layer->SetIsFixedPosition(common.isFixedPosition());
+ if (common.isFixedPosition()) {
+ layer->SetFixedPositionData(common.fixedPositionScrollContainerId(),
+ common.fixedPositionAnchor(),
+ common.fixedPositionSides());
+ }
+ if (common.isStickyPosition()) {
+ layer->SetStickyPositionData(common.stickyScrollContainerId(),
+ common.stickyScrollRangeOuter(),
+ common.stickyScrollRangeInner());
+ }
+ layer->SetScrollbarData(common.scrollbarTargetContainerId(),
+ static_cast<Layer::ScrollDirection>(common.scrollbarDirection()),
+ common.scrollbarThumbRatio());
+ if (common.isScrollbarContainer()) {
+ layer->SetIsScrollbarContainer();
+ }
+ layer->SetMixBlendMode((gfx::CompositionOp)common.mixBlendMode());
+ layer->SetForceIsolatedGroup(common.forceIsolatedGroup());
+ if (PLayerParent* maskLayer = common.maskLayerParent()) {
+ layer->SetMaskLayer(cast(maskLayer)->AsLayer());
+ } else {
+ layer->SetMaskLayer(nullptr);
+ }
+ layer->SetAnimations(common.animations());
+ layer->SetScrollMetadata(common.scrollMetadata());
+ layer->SetDisplayListLog(common.displayListLog().get());
+
+ // The updated invalid region is added to the existing one, since we can
+ // update multiple times before the next composite.
+ layer->AddInvalidRegion(common.invalidRegion());
+
+ nsTArray<RefPtr<Layer>> maskLayers;
+ for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
+ Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
+ maskLayers.AppendElement(maskLayer);
+ }
+ layer->SetAncestorMaskLayers(maskLayers);
+
+ typedef SpecificLayerAttributes Specific;
+ const SpecificLayerAttributes& specific = attrs.specific();
+ switch (specific.type()) {
+ case Specific::Tnull_t:
+ break;
+
+ case Specific::TPaintedLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] painted layer"));
+
+ PaintedLayerComposite* paintedLayer = layerParent->AsPaintedLayerComposite();
+ if (!paintedLayer) {
+ return false;
+ }
+ const PaintedLayerAttributes& attrs =
+ specific.get_PaintedLayerAttributes();
+
+ paintedLayer->SetValidRegion(attrs.validRegion());
+
+ break;
+ }
+ case Specific::TContainerLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] container layer"));
+
+ ContainerLayerComposite* containerLayer = layerParent->AsContainerLayerComposite();
+ if (!containerLayer) {
+ return false;
+ }
+ const ContainerLayerAttributes& attrs =
+ specific.get_ContainerLayerAttributes();
+ containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
+ containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
+ containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
+ attrs.presShellResolution());
+ containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
+
+ break;
+ }
+ case Specific::TColorLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] color layer"));
+
+ ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
+ if (!colorLayer) {
+ return false;
+ }
+ colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value());
+ colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TCanvasLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] canvas layer"));
+
+ CanvasLayerComposite* canvasLayer = layerParent->AsCanvasLayerComposite();
+ if (!canvasLayer) {
+ return false;
+ }
+ canvasLayer->SetSamplingFilter(specific.get_CanvasLayerAttributes().samplingFilter());
+ canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TRefLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] ref layer"));
+
+ RefLayerComposite* refLayer = layerParent->AsRefLayerComposite();
+ if (!refLayer) {
+ return false;
+ }
+ refLayer->SetReferentId(specific.get_RefLayerAttributes().id());
+ refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride());
+ break;
+ }
+ case Specific::TImageLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] image layer"));
+
+ ImageLayerComposite* imageLayer = layerParent->AsImageLayerComposite();
+ if (!imageLayer) {
+ return false;
+ }
+ const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes();
+ imageLayer->SetSamplingFilter(attrs.samplingFilter());
+ imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode());
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpSetDiagnosticTypes: {
+ mLayerManager->GetCompositor()->SetDiagnosticTypes(
+ edit.get_OpSetDiagnosticTypes().diagnostics());
+ break;
+ }
+ case Edit::TOpWindowOverlayChanged: {
+ mLayerManager->SetWindowOverlayChanged();
+ break;
+ }
+ // Tree ops
+ case Edit::TOpSetRoot: {
+ MOZ_LAYERS_LOG(("[ParentSide] SetRoot"));
+
+ Layer* newRoot = AsLayerComposite(edit.get_OpSetRoot())->AsLayer();
+ if (!newRoot) {
+ return false;
+ }
+ if (newRoot->GetParent()) {
+ // newRoot is not a root!
+ return false;
+ }
+ mRoot = newRoot;
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpInsertAfter: {
+ MOZ_LAYERS_LOG(("[ParentSide] InsertAfter"));
+
+ const OpInsertAfter& oia = edit.get_OpInsertAfter();
+ Layer* child = ShadowChild(oia)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(oia)->AsContainerLayerComposite();
+ if (!container ||
+ !container->InsertAfter(child, ShadowAfter(oia)->AsLayer()))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpPrependChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] PrependChild"));
+
+ const OpPrependChild& oac = edit.get_OpPrependChild();
+ Layer* child = ShadowChild(oac)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(oac)->AsContainerLayerComposite();
+ if (!container ||
+ !container->InsertAfter(child, nullptr))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRemoveChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RemoveChild"));
+
+ const OpRemoveChild& orc = edit.get_OpRemoveChild();
+ Layer* childLayer = ShadowChild(orc)->AsLayer();
+ if (!childLayer) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RemoveChild(childLayer))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRepositionChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RepositionChild"));
+
+ const OpRepositionChild& orc = edit.get_OpRepositionChild();
+ Layer* child = ShadowChild(orc)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RepositionChild(child, ShadowAfter(orc)->AsLayer()))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRaiseToTopChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild"));
+
+ const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild();
+ Layer* child = ShadowChild(rtc)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(rtc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RepositionChild(child, nullptr))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TCompositableOperation: {
+ if (!ReceiveCompositableUpdate(edit.get_CompositableOperation(),
+ replyv)) {
+ return false;
+ }
+ break;
+ }
+ case Edit::TOpAttachCompositable: {
+ const OpAttachCompositable& op = edit.get_OpAttachCompositable();
+ CompositableHost* host = CompositableHost::FromIPDLActor(op.compositableParent());
+ if (mPendingCompositorUpdates) {
+ // Do not attach compositables from old layer trees. Return true since
+ // content cannot handle errors.
+ return true;
+ }
+ if (!Attach(cast(op.layerParent()), host, false)) {
+ return false;
+ }
+ host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+ break;
+ }
+ case Edit::TOpAttachAsyncCompositable: {
+ const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
+ PCompositableParent* compositableParent = CompositableMap::Get(op.containerID());
+ if (!compositableParent) {
+ NS_ERROR("CompositableParent not found in the map");
+ return false;
+ }
+ if (mPendingCompositorUpdates) {
+ // Do not attach compositables from old layer trees. Return true since
+ // content cannot handle errors.
+ return true;
+ }
+ CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent);
+ if (!Attach(cast(op.layerParent()), host, true)) {
+ return false;
+ }
+ host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+ }
+
+ mCompositorBridge->ShadowLayersUpdated(this, aTransactionId, targetConfig,
+ aPlugins, isFirstPaint, scheduleComposite,
+ paintSequenceNumber, isRepeatTransaction,
+ aPaintSyncId, updateHitTestingTree);
+
+ {
+ AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
+ layer_manager()->EndTransaction(TimeStamp(), LayerManager::END_NO_IMMEDIATE_REDRAW);
+ }
+
+ if (reply) {
+ reply->SetCapacity(replyv.size());
+ if (replyv.size() > 0) {
+ reply->AppendElements(&replyv.front(), replyv.size());
+ }
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ int compositeTime = (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds();
+ if (compositeTime > 15) {
+ printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", compositeTime);
+ }
+#endif
+
+ // Enable visual warning for long transaction when draw FPS option is enabled
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ if (drawFps) {
+ uint32_t visualWarningTrigger = gfxPrefs::LayerTransactionWarning();
+ // The default theshold is 200ms to trigger, hit red when it take 4 times longer
+ TimeDuration latency = TimeStamp::Now() - aTransactionStart;
+ if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) {
+ float severity = (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)).ToMilliseconds() /
+ (4 * visualWarningTrigger);
+ if (severity > 1.f) {
+ severity = 1.f;
+ }
+ mLayerManager->VisualFrameWarning(severity);
+ PR_LogPrint("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms",
+ OtherPid(),
+ latency.ToMilliseconds());
+ }
+ }
+
+ profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_END);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
+{
+ mChildEpoch = aLayerObserverEpoch;
+ return true;
+}
+
+bool
+LayerTransactionParent::ShouldParentObserveEpoch()
+{
+ if (mParentEpoch == mChildEpoch) {
+ return false;
+ }
+
+ mParentEpoch = mChildEpoch;
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime)
+{
+ return mCompositorBridge->SetTestSampleTime(this, aTime);
+}
+
+bool
+LayerTransactionParent::RecvLeaveTestMode()
+{
+ mCompositorBridge->LeaveTestMode(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAnimationOpacity(PLayerParent* aParent,
+ float* aOpacity,
+ bool* aHasAnimationOpacity)
+{
+ *aHasAnimationOpacity = false;
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ Layer* layer = cast(aParent)->AsLayer();
+ if (!layer) {
+ return false;
+ }
+
+ mCompositorBridge->ApplyAsyncProperties(this);
+
+ if (!layer->AsLayerComposite()->GetShadowOpacitySetByAnimation()) {
+ return true;
+ }
+
+ *aOpacity = layer->GetLocalOpacity();
+ *aHasAnimationOpacity = true;
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
+ MaybeTransform* aTransform)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ Layer* layer = cast(aParent)->AsLayer();
+ if (!layer) {
+ return false;
+ }
+
+ // Make sure we apply the latest animation style or else we can end up with
+ // a race between when we temporarily clear the animation transform (in
+ // CompositorBridgeParent::SetShadowProperties) and when animation recalculates
+ // the value.
+ mCompositorBridge->ApplyAsyncProperties(this);
+
+ // This method is specific to transforms applied by animation.
+ // This is because this method uses the information stored with an animation
+ // such as the origin of the reference frame corresponding to the layer, to
+ // recover the untranslated transform from the shadow transform. For
+ // transforms that are not set by animation we don't have this information
+ // available.
+ if (!layer->AsLayerComposite()->GetShadowTransformSetByAnimation()) {
+ *aTransform = mozilla::void_t();
+ return true;
+ }
+
+ // The following code recovers the untranslated transform
+ // from the shadow transform by undoing the translations in
+ // AsyncCompositionManager::SampleValue.
+
+ Matrix4x4 transform = layer->AsLayerComposite()->GetShadowBaseTransform();
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ // Undo the scale transform applied by AsyncCompositionManager::SampleValue
+ transform.PostScale(1.0f/c->GetInheritedXScale(),
+ 1.0f/c->GetInheritedYScale(),
+ 1.0f);
+ }
+ float scale = 1;
+ Point3D scaledOrigin;
+ Point3D transformOrigin;
+ for (uint32_t i=0; i < layer->GetAnimations().Length(); i++) {
+ if (layer->GetAnimations()[i].data().type() == AnimationData::TTransformData) {
+ const TransformData& data = layer->GetAnimations()[i].data().get_TransformData();
+ scale = data.appUnitsPerDevPixel();
+ scaledOrigin =
+ Point3D(NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)),
+ NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)),
+ 0.0f);
+ transformOrigin = data.transformOrigin();
+ break;
+ }
+ }
+
+ // If our parent isn't a perspective layer, then the offset into reference
+ // frame coordinates will have been applied to us. Add an inverse translation
+ // to cancel it out.
+ if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
+ transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+ }
+
+ // Undo the rebasing applied by
+ // nsDisplayTransform::GetResultingTransformMatrixInternal
+ transform.ChangeBasis(-transformOrigin);
+
+ // Convert to CSS pixels (this undoes the operations performed by
+ // nsStyleTransformMatrix::ProcessTranslatePart which is called from
+ // nsDisplayTransform::GetResultingTransformMatrix)
+ double devPerCss =
+ double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
+ transform._41 *= devPerCss;
+ transform._42 *= devPerCss;
+ transform._43 *= devPerCss;
+
+ *aTransform = transform;
+ return true;
+}
+
+static AsyncPanZoomController*
+GetAPZCForViewID(Layer* aLayer, FrameMetrics::ViewID aScrollID)
+{
+ AsyncPanZoomController* resultApzc = nullptr;
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [aScrollID, &resultApzc] (Layer* layer)
+ {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ if (layer->GetFrameMetrics(i).GetScrollId() == aScrollID) {
+ resultApzc = layer->GetAsyncPanZoomController(i);
+ return TraversalFlag::Abort;
+ }
+ }
+ return TraversalFlag::Continue;
+ });
+ return resultApzc;
+}
+
+bool
+LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollID,
+ const float& aX, const float& aY)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
+ if (!controller) {
+ return false;
+ }
+ controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY));
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID,
+ const float& aValue)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
+ if (!controller) {
+ return false;
+ }
+ controller->SetTestAsyncZoom(LayerToParentLayerScale(aValue));
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvFlushApzRepaints()
+{
+ mCompositorBridge->FlushApzRepaints(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
+{
+ mCompositorBridge->GetAPZTestData(this, aOutData);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvRequestProperty(const nsString& aProperty, float* aValue)
+{
+ if (aProperty.Equals(NS_LITERAL_STRING("overdraw"))) {
+ *aValue = layer_manager()->GetCompositor()->GetFillRatio();
+ } else if (aProperty.Equals(NS_LITERAL_STRING("missed_hwc"))) {
+ *aValue = layer_manager()->LastFrameMissedHWC() ? 1 : 0;
+ } else {
+ *aValue = -1;
+ }
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets)
+{
+ mCompositorBridge->SetConfirmedTargetAPZC(this, aBlockId, aTargets);
+ return true;
+}
+
+bool
+LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent,
+ CompositableHost* aCompositable,
+ bool aIsAsync)
+{
+ if (!aCompositable) {
+ return false;
+ }
+
+ Layer* baselayer = aLayerParent->AsLayer();
+ if (!baselayer) {
+ return false;
+ }
+ LayerComposite* layer = baselayer->AsLayerComposite();
+ if (!layer) {
+ return false;
+ }
+
+ Compositor* compositor
+ = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
+
+ if (!layer->SetCompositableHost(aCompositable)) {
+ // not all layer types accept a compositable, see bug 967824
+ return false;
+ }
+ aCompositable->Attach(aLayerParent->AsLayer(),
+ compositor,
+ aIsAsync
+ ? CompositableHost::ALLOW_REATTACH
+ | CompositableHost::KEEP_ATTACHED
+ : CompositableHost::NO_FLAGS);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvClearCachedResources()
+{
+ if (mRoot) {
+ // NB: |mRoot| here is the *child* context's root. In this parent
+ // context, it's just a subtree root. We need to scope the clear
+ // of resources to exactly that subtree, so we specify it here.
+ mLayerManager->ClearCachedResources(mRoot);
+ }
+ mCompositorBridge->NotifyClearCachedResources(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvForceComposite()
+{
+ mCompositorBridge->ForceComposite(this);
+ return true;
+}
+
+PLayerParent*
+LayerTransactionParent::AllocPLayerParent()
+{
+ return new ShadowLayerParent();
+}
+
+bool
+LayerTransactionParent::DeallocPLayerParent(PLayerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PCompositableParent*
+LayerTransactionParent::AllocPCompositableParent(const TextureInfo& aInfo)
+{
+ return CompositableHost::CreateIPDLActor(this, aInfo, 0);
+}
+
+bool
+LayerTransactionParent::DeallocPCompositableParent(PCompositableParent* aActor)
+{
+ return CompositableHost::DestroyIPDLActor(aActor);
+}
+
+void
+LayerTransactionParent::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+bool
+LayerTransactionParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+ return PLayerTransactionParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+LayerTransactionParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+
+ return PLayerTransactionParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return;
+ }
+ PLayerTransactionParent::DeallocShmem(aShmem);
+}
+
+bool LayerTransactionParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+LayerTransactionParent::SendPendingAsyncMessages()
+{
+ mCompositorBridge->SendPendingAsyncMessages();
+}
+
+void
+LayerTransactionParent::SetAboutToSendAsyncMessages()
+{
+ mCompositorBridge->SetAboutToSendAsyncMessages();
+}
+
+void
+LayerTransactionParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/LayerTransactionParent.h b/system/graphics/layers/ipc/LayerTransactionParent.h
new file mode 100644
index 000000000..d92aa0358
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTransactionParent.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableTransactionParent.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+
+namespace mozilla {
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layout {
+class RenderFrameParent;
+} // namespace layout
+
+namespace layers {
+
+class Layer;
+class LayerManagerComposite;
+class ShadowLayerParent;
+class CompositableParent;
+class CompositorBridgeParentBase;
+
+class LayerTransactionParent final : public PLayerTransactionParent,
+ public CompositableParentManager,
+ public ShmemAllocator
+{
+ typedef mozilla::layout::RenderFrameParent RenderFrameParent;
+ typedef InfallibleTArray<Edit> EditArray;
+ typedef InfallibleTArray<OpDestroy> OpDestroyArray;
+ typedef InfallibleTArray<EditReply> EditReplyArray;
+ typedef InfallibleTArray<PluginWindowData> PluginsArray;
+
+public:
+ LayerTransactionParent(LayerManagerComposite* aManager,
+ CompositorBridgeParentBase* aBridge,
+ uint64_t aId);
+
+protected:
+ ~LayerTransactionParent();
+
+public:
+ void Destroy();
+
+ LayerManagerComposite* layer_manager() const { return mLayerManager; }
+
+ void SetLayerManager(LayerManagerComposite* aLayerManager);
+
+ uint64_t GetId() const { return mId; }
+ Layer* GetRoot() const { return mRoot; }
+
+ uint64_t GetChildEpoch() const { return mChildEpoch; }
+ bool ShouldParentObserveEpoch();
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual void DeallocShmem(ipc::Shmem& aShmem) override;
+
+ virtual bool IsSameProcess() const override;
+
+ const uint64_t& GetPendingTransactionId() { return mPendingTransaction; }
+ void SetPendingTransactionId(uint64_t aId) { mPendingTransaction = aId; }
+
+ // CompositableParentManager
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ virtual void SendPendingAsyncMessages() override;
+
+ virtual void SetAboutToSendAsyncMessages() override;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+
+ virtual base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+
+ void AddPendingCompositorUpdate() {
+ mPendingCompositorUpdates++;
+ }
+ void SetPendingCompositorUpdates(uint32_t aCount) {
+ // Only called after construction.
+ MOZ_ASSERT(mPendingCompositorUpdates == 0);
+ mPendingCompositorUpdates = aCount;
+ }
+ void AcknowledgeCompositorUpdate() {
+ MOZ_ASSERT(mPendingCompositorUpdates > 0);
+ mPendingCompositorUpdates--;
+ }
+
+protected:
+ virtual bool RecvShutdown() override;
+
+ virtual bool RecvPaintTime(const uint64_t& aTransactionId,
+ const TimeDuration& aPaintTime) override;
+
+ virtual bool RecvUpdate(EditArray&& cset,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId,
+ EditReplyArray* reply) override;
+
+ virtual bool RecvUpdateNoSwap(EditArray&& cset,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId) override;
+
+ virtual bool RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
+
+ virtual bool RecvClearCachedResources() override;
+ virtual bool RecvForceComposite() override;
+ virtual bool RecvSetTestSampleTime(const TimeStamp& aTime) override;
+ virtual bool RecvLeaveTestMode() override;
+ virtual bool RecvGetAnimationOpacity(PLayerParent* aParent,
+ float* aOpacity,
+ bool* aHasAnimationOpacity) override;
+ virtual bool RecvGetAnimationTransform(PLayerParent* aParent,
+ MaybeTransform* aTransform)
+ override;
+ virtual bool RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId,
+ const float& aX, const float& aY) override;
+ virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId,
+ const float& aValue) override;
+ virtual bool RecvFlushApzRepaints() override;
+ virtual bool RecvGetAPZTestData(APZTestData* aOutData) override;
+ virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override;
+ virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ virtual PLayerParent* AllocPLayerParent() override;
+ virtual bool DeallocPLayerParent(PLayerParent* actor) override;
+
+ virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) override;
+ virtual bool DeallocPCompositableParent(PCompositableParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool Attach(ShadowLayerParent* aLayerParent,
+ CompositableHost* aCompositable,
+ bool aIsAsyncVideo);
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeParent;
+ friend class CrossProcessCompositorBridgeParent;
+ friend class layout::RenderFrameParent;
+
+private:
+ RefPtr<LayerManagerComposite> mLayerManager;
+ CompositorBridgeParentBase* mCompositorBridge;
+ // Hold the root because it might be grafted under various
+ // containers in the "real" layer tree
+ RefPtr<Layer> mRoot;
+ // When this is nonzero, it refers to a layer tree owned by the
+ // compositor thread. It is always true that
+ // mId != 0 => mRoot == null
+ // because the "real tree" is owned by the compositor.
+ uint64_t mId;
+
+ // These fields keep track of the latest epoch values in the child and the
+ // parent. mChildEpoch is the latest epoch value received from the child.
+ // mParentEpoch is the latest epoch value that we have told TabParent about
+ // (via ObserveLayerUpdate).
+ uint64_t mChildEpoch;
+ uint64_t mParentEpoch;
+
+ uint64_t mPendingTransaction;
+
+ // Number of compositor updates we're waiting for the child to
+ // acknowledge.
+ uint32_t mPendingCompositorUpdates;
+
+ // When the widget/frame/browser stuff in this process begins its
+ // destruction process, we need to Disconnect() all the currently
+ // live shadow layers, because some of them might be orphaned from
+ // the layer tree. This happens in Destroy() above. After we
+ // Destroy() ourself, there's a window in which that information
+ // hasn't yet propagated back to the child side and it might still
+ // send us layer transactions. We want to ignore those transactions
+ // because they refer to "zombie layers" on this side. So, we track
+ // that state with |mDestroyed|. This is similar to, but separate
+ // from, |mLayerManager->IsDestroyed()|; we might have had Destroy()
+ // called on us but the mLayerManager might not be destroyed, or
+ // vice versa. In both cases though, we want to ignore shadow-layer
+ // transactions posted by the child.
+
+ bool mDestroyed;
+
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
diff --git a/system/graphics/layers/ipc/LayerTreeOwnerTracker.cpp b/system/graphics/layers/ipc/LayerTreeOwnerTracker.cpp
new file mode 100644
index 000000000..2f4c041ee
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTreeOwnerTracker.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeOwnerTracker.h"
+
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/dom/ContentParent.h" // for ContentParent
+#include "mozilla/gfx/GPUChild.h" // for GPUChild
+#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager
+
+#include <utility> // for std::make_pair
+
+namespace mozilla {
+namespace layers {
+
+static StaticAutoPtr<LayerTreeOwnerTracker> sSingleton;
+
+LayerTreeOwnerTracker::LayerTreeOwnerTracker() :
+ mLayerIdsLock("LayerTreeOwnerTrackerLock")
+{
+}
+
+void
+LayerTreeOwnerTracker::Initialize()
+{
+ MOZ_ASSERT(!sSingleton);
+ sSingleton = new LayerTreeOwnerTracker();
+}
+
+void
+LayerTreeOwnerTracker::Shutdown()
+{
+ sSingleton = nullptr;
+}
+
+LayerTreeOwnerTracker*
+LayerTreeOwnerTracker::Get()
+{
+ return sSingleton;
+}
+
+void
+LayerTreeOwnerTracker::Map(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ // Add the mapping to the list
+ mLayerIds[aLayersId] = aProcessId;
+}
+
+void
+LayerTreeOwnerTracker::Unmap(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId);
+ mLayerIds.erase(aLayersId);
+}
+
+bool
+LayerTreeOwnerTracker::IsMapped(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ auto iter = mLayerIds.find(aLayersId);
+ return iter != mLayerIds.end() && iter->second == aProcessId;
+}
+
+void
+LayerTreeOwnerTracker::Iterate(function<void(uint64_t aLayersId, base::ProcessId aProcessId)> aCallback)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ for (const auto& iter : mLayerIds) {
+ aCallback(iter.first, iter.second);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/LayerTreeOwnerTracker.h b/system/graphics/layers/ipc/LayerTreeOwnerTracker.h
new file mode 100644
index 000000000..348115b46
--- /dev/null
+++ b/system/graphics/layers/ipc/LayerTreeOwnerTracker.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerTreeOwnerTracker_h
+#define mozilla_layers_LayerTreeOwnerTracker_h
+
+#include "base/process.h" // for base::ProcessId
+#include "mozilla/Mutex.h" // for mozilla::Mutex
+#include "mozilla/Function.h"
+
+#include <map>
+
+namespace mozilla {
+
+namespace dom {
+ class ContentParent;
+}
+
+namespace layers {
+
+/**
+ * A utility class for tracking which content processes should be allowed
+ * to access which layer trees.
+ *
+ * ProcessId's are used to track which content process can access the layer
+ * tree, and in the case of nested browser's we use the top level content
+ * processes' ProcessId.
+ *
+ * This class is only available in the main process and gpu process. Mappings
+ * are synced from main process to the gpu process. The actual syncing happens
+ * in GPUProcessManager, and so this class should not be used directly.
+ */
+class LayerTreeOwnerTracker final
+{
+public:
+ static void Initialize();
+ static void Shutdown();
+ static LayerTreeOwnerTracker* Get();
+
+ /**
+ * Map aLayersId and aProcessId together so that that process
+ * can access that layer tree.
+ */
+ void Map(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Remove an existing mapping.
+ */
+ void Unmap(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Checks whether it is okay for aProcessId to access aLayersId.
+ */
+ bool IsMapped(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ void Iterate(function<void(uint64_t aLayersId, base::ProcessId aProcessId)> aCallback);
+
+private:
+ LayerTreeOwnerTracker();
+
+ mozilla::Mutex mLayerIdsLock;
+ std::map<uint64_t, base::ProcessId> mLayerIds;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerTreeOwnerTracker_h
diff --git a/system/graphics/layers/ipc/LayersMessages.ipdlh b/system/graphics/layers/ipc/LayersMessages.ipdlh
new file mode 100644
index 000000000..dbbb3649a
--- /dev/null
+++ b/system/graphics/layers/ipc/LayersMessages.ipdlh
@@ -0,0 +1,499 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PCompositable;
+include protocol PCompositorBridge;
+include protocol PLayer;
+include protocol PImageContainer;
+include protocol PRenderFrame;
+include protocol PTexture;
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "ImageLayers.h";
+
+using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::Color from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using nscoord from "nsCoord.h";
+using struct nsRect from "nsRect.h";
+using struct nsPoint from "nsPoint.h";
+using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
+using nsCSSPropertyID from "nsCSSPropertyID.h";
+using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::LayerMargin from "Units.h";
+using mozilla::LayerPoint from "Units.h";
+using mozilla::LayerRect from "Units.h";
+using mozilla::LayerIntRegion from "Units.h";
+using mozilla::ParentLayerIntRect from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
+
+namespace mozilla {
+namespace layers {
+
+struct TargetConfig {
+ IntRect naturalBounds;
+ ScreenRotation rotation;
+ ScreenOrientationInternal orientation;
+ nsIntRegion clearRegion;
+};
+
+// Create a shadow layer for |layer|
+struct OpCreatePaintedLayer { PLayer layer; };
+struct OpCreateContainerLayer { PLayer layer; };
+struct OpCreateImageLayer { PLayer layer; };
+struct OpCreateColorLayer { PLayer layer; };
+struct OpCreateCanvasLayer { PLayer layer; };
+struct OpCreateRefLayer { PLayer layer; };
+
+struct OpAttachCompositable {
+ PLayer layer;
+ PCompositable compositable;
+};
+
+struct OpAttachAsyncCompositable {
+ PLayer layer;
+ uint64_t containerID;
+};
+
+struct ThebesBufferData {
+ IntRect rect;
+ IntPoint rotation;
+};
+
+struct CubicBezierFunction {
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct StepFunction {
+ int steps;
+ // 1 = nsTimingFunction::StepStart, 2 = nsTimingFunction::StepEnd
+ int type;
+};
+
+union TimingFunction {
+ null_t;
+ CubicBezierFunction;
+ StepFunction;
+};
+
+// Send the angle with units rather than sending all angles in radians
+// to avoid having floating point error introduced by unit switching.
+struct CSSAngle {
+ float value;
+ int unit; // an nsCSSUnit that is valid for angles
+};
+
+struct LayerColor { Color value; };
+struct Perspective { float value; };
+struct RotationX { CSSAngle angle; };
+struct RotationY { CSSAngle angle; };
+struct RotationZ { CSSAngle angle; };
+struct Rotation { CSSAngle angle; };
+struct Rotation3D {
+ float x;
+ float y;
+ float z;
+ CSSAngle angle;
+};
+struct Scale {
+ float x;
+ float y;
+ float z;
+};
+struct Skew { CSSAngle x; CSSAngle y; };
+struct SkewX { CSSAngle x; };
+struct SkewY { CSSAngle y; };
+struct TransformMatrix { Matrix4x4 value; };
+struct Translation {
+ float x;
+ float y;
+ float z;
+};
+
+union TransformFunction {
+ Perspective;
+ RotationX;
+ RotationY;
+ RotationZ;
+ Rotation;
+ Rotation3D;
+ Scale;
+ Skew;
+ SkewX;
+ SkewY;
+ Translation;
+ TransformMatrix;
+};
+
+union Animatable {
+ float;
+ TransformFunction[];
+};
+
+struct AnimationSegment {
+ Animatable startState;
+ Animatable endState;
+ float startPortion;
+ float endPortion;
+ TimingFunction sampleFn;
+};
+
+// Transforms need extra information to correctly convert the list of transform
+// functions to a Matrix4x4 that can be applied directly to the layer.
+struct TransformData {
+ // the origin of the frame being transformed in app units
+ nsPoint origin;
+ // the transform-origin property for the transform in device pixels
+ Point3D transformOrigin;
+ nsRect bounds;
+ int32_t appUnitsPerDevPixel;
+};
+
+union AnimationData {
+ null_t;
+ TransformData;
+};
+
+struct Animation {
+ TimeStamp startTime;
+ TimeDuration delay;
+ // The value of the animation's current time at the moment it was created.
+ // For animations that are waiting to start, their startTime will be null.
+ // Once the animation is ready to start, we calculate an appropriate value
+ // of startTime such that we begin playback from initialCurrentTime.
+ TimeDuration initialCurrentTime;
+ TimeDuration duration;
+ // For each frame, the interpolation point is computed based on the
+ // startTime, the direction, the duration, and the current time.
+ // The segments must uniquely cover the portion from 0.0 to 1.0
+ AnimationSegment[] segments;
+ // Number of times to repeat the animation, including positive infinity.
+ // Values <= 0 mean the animation will not play (although events are still
+ // dispatched on the main thread).
+ float iterations;
+ float iterationStart;
+ // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
+ uint8_t direction;
+ // This uses dom::FillMode.
+ uint8_t fillMode;
+ nsCSSPropertyID property;
+ AnimationData data;
+ float playbackRate;
+ // This is used in the transformed progress calculation.
+ TimingFunction easingFunction;
+ uint8_t iterationComposite;
+};
+
+// Change a layer's attributes
+struct CommonLayerAttributes {
+ IntRect layerBounds;
+ LayerIntRegion visibleRegion;
+ EventRegions eventRegions;
+ TransformMatrix transform;
+ bool transformIsPerspective;
+ float postXScale;
+ float postYScale;
+ uint32_t contentFlags;
+ float opacity;
+ bool useClipRect;
+ ParentLayerIntRect clipRect;
+ MaybeLayerClip scrolledClip;
+ bool isFixedPosition;
+ uint64_t fixedPositionScrollContainerId;
+ LayerPoint fixedPositionAnchor;
+ int32_t fixedPositionSides;
+ bool isStickyPosition;
+ uint64_t stickyScrollContainerId;
+ LayerRect stickyScrollRangeOuter;
+ LayerRect stickyScrollRangeInner;
+ uint64_t scrollbarTargetContainerId;
+ uint32_t scrollbarDirection;
+ float scrollbarThumbRatio;
+ bool isScrollbarContainer;
+ int8_t mixBlendMode;
+ bool forceIsolatedGroup;
+ nullable PLayer maskLayer;
+ PLayer[] ancestorMaskLayers;
+ // Animated colors will only honored for ColorLayers.
+ Animation[] animations;
+ nsIntRegion invalidRegion;
+ ScrollMetadata[] scrollMetadata;
+ nsCString displayListLog;
+};
+
+struct PaintedLayerAttributes {
+ nsIntRegion validRegion;
+};
+struct ContainerLayerAttributes {
+ float preXScale;
+ float preYScale;
+ float inheritedXScale;
+ float inheritedYScale;
+ float presShellResolution;
+ bool scaleToResolution;
+ EventRegionsOverride eventRegionsOverride;
+};
+struct ColorLayerAttributes { LayerColor color; IntRect bounds; };
+struct CanvasLayerAttributes { SamplingFilter samplingFilter; IntRect bounds; };
+struct RefLayerAttributes {
+ int64_t id;
+ // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override
+ // explicitly here.
+ EventRegionsOverride eventRegionsOverride;
+};
+struct ImageLayerAttributes { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; };
+
+union SpecificLayerAttributes {
+ null_t;
+ PaintedLayerAttributes;
+ ContainerLayerAttributes;
+ ColorLayerAttributes;
+ CanvasLayerAttributes;
+ RefLayerAttributes;
+ ImageLayerAttributes;
+};
+
+struct LayerAttributes {
+ CommonLayerAttributes common;
+ SpecificLayerAttributes specific;
+};
+
+// See nsIWidget Configurations
+struct PluginWindowData {
+ uintptr_t windowId;
+ LayoutDeviceIntRect[] clip;
+ LayoutDeviceIntRect bounds;
+ bool visible;
+};
+
+struct OpSetLayerAttributes {
+ PLayer layer;
+ LayerAttributes attrs;
+};
+
+// Monkey with the tree structure
+struct OpSetRoot { PLayer root; };
+struct OpInsertAfter { PLayer container; PLayer childLayer; PLayer after; };
+struct OpPrependChild { PLayer container; PLayer childLayer; };
+struct OpRemoveChild { PLayer container; PLayer childLayer; };
+struct OpRepositionChild { PLayer container; PLayer childLayer; PLayer after; };
+struct OpRaiseToTopChild { PLayer container; PLayer childLayer; };
+
+struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
+struct OpWindowOverlayChanged { };
+
+struct ShmemSection {
+ Shmem shmem;
+ uint32_t offset;
+ size_t size;
+};
+
+union ReadLockDescriptor {
+ ShmemSection;
+ uintptr_t;
+ null_t;
+};
+
+union MaybeTexture {
+ PTexture;
+ null_t;
+};
+
+struct TexturedTileDescriptor {
+ PTexture texture;
+ MaybeTexture textureOnWhite;
+ IntRect updateRect;
+ ReadLockDescriptor sharedLock;
+ ReadLockDescriptor sharedLockOnWhite;
+ bool wasPlaceholder;
+};
+
+struct PlaceholderTileDescriptor {
+};
+
+union TileDescriptor {
+ TexturedTileDescriptor;
+ PlaceholderTileDescriptor;
+};
+
+struct SurfaceDescriptorTiles {
+ nsIntRegion validRegion;
+ TileDescriptor[] tiles;
+ IntPoint tileOrigin;
+ IntSize tileSize;
+ int firstTileX;
+ int firstTileY;
+ int retainedWidth;
+ int retainedHeight;
+ float resolution;
+ float frameXResolution;
+ float frameYResolution;
+ bool isProgressive;
+};
+
+struct OpUseTiledLayerBuffer {
+ SurfaceDescriptorTiles tileLayerDescriptor;
+};
+
+struct OpUseOverlaySource {
+ OverlaySource overlay;
+ IntRect picture;
+};
+
+struct OpPaintTextureRegion {
+ ThebesBufferData bufferData;
+ nsIntRegion updatedRegion;
+};
+
+/**
+ * Tells the CompositableHost to remove the corresponding TextureHost
+ */
+struct OpRemoveTexture {
+ PTexture texture;
+};
+
+struct TimedTexture {
+ PTexture texture;
+ ReadLockDescriptor sharedLock;
+ TimeStamp timeStamp;
+ IntRect picture;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+/**
+ * Tells the compositor-side which textures to use (for example, as front buffer
+ * if there are several textures for double buffering).
+ * This provides a list of textures with timestamps, ordered by timestamp.
+ * The newest texture whose timestamp is <= the current time is rendered
+ * (where null is considered less than every other timestamp). If there is no
+ * such texture, the first texture is rendered.
+ * The first timestamp value can be null, but the others must not be.
+ * The list must not be empty.
+ */
+struct OpUseTexture {
+ TimedTexture[] textures;
+};
+
+struct OpUseComponentAlphaTextures {
+ PTexture textureOnBlack;
+ PTexture textureOnWhite;
+ ReadLockDescriptor sharedLockBlack;
+ ReadLockDescriptor sharedLockWhite;
+};
+
+union MaybeRegion {
+ nsIntRegion;
+ null_t;
+};
+
+struct OpNotifyNotUsed {
+ uint64_t TextureId;
+ uint64_t fwdTransactionId;
+};
+
+union CompositableOperationDetail {
+ OpPaintTextureRegion;
+
+ OpUseTiledLayerBuffer;
+
+ OpRemoveTexture;
+
+ OpUseTexture;
+ OpUseComponentAlphaTextures;
+ OpUseOverlaySource;
+};
+
+struct CompositableOperation {
+ PCompositable compositable;
+ CompositableOperationDetail detail;
+};
+
+// A unit of a changeset; a set of these comprise a changeset
+// If adding a new edit type that requires the hit testing tree to be updated,
+// set the updateHitTestingTree flag to true in RecvUpdate()
+union Edit {
+ OpCreatePaintedLayer;
+ OpCreateContainerLayer;
+ OpCreateImageLayer;
+ OpCreateColorLayer;
+ OpCreateCanvasLayer;
+ OpCreateRefLayer;
+
+ OpSetLayerAttributes;
+ OpSetDiagnosticTypes;
+ OpWindowOverlayChanged;
+
+ OpSetRoot;
+ OpInsertAfter;
+ OpPrependChild;
+ OpRemoveChild;
+ OpRepositionChild;
+ OpRaiseToTopChild;
+
+ OpAttachCompositable;
+ OpAttachAsyncCompositable;
+
+ CompositableOperation;
+};
+
+// Operations related to destroying resources, always handled after the other
+// operations for safety.
+union OpDestroy {
+ PTexture;
+ PCompositable;
+};
+
+// Replies to operations
+
+struct OpContentBufferSwap {
+ PCompositable compositable;
+ nsIntRegion frontUpdatedRegion;
+};
+
+/**
+ * An ImageCompositeNotification is sent the first time a particular
+ * image is composited by an ImageHost.
+ */
+struct ImageCompositeNotification {
+ PImageContainer imageContainer;
+ TimeStamp imageTimeStamp;
+ TimeStamp firstCompositeTimeStamp;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+// Unit of a "changeset reply". This is a weird abstraction, probably
+// only to be used for buffer swapping.
+union EditReply {
+ OpContentBufferSwap;
+};
+
+union AsyncParentMessageData {
+ OpNotifyNotUsed;
+};
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/ipc/LayersSurfaces.ipdlh b/system/graphics/layers/ipc/LayersSurfaces.ipdlh
new file mode 100644
index 000000000..16689fdcc
--- /dev/null
+++ b/system/graphics/layers/ipc/LayersSurfaces.ipdlh
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using struct gfxPoint from "gfxPoint.h";
+using nsIntRegion from "nsRegion.h";
+using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
+using mozilla::StereoMode from "ImageTypes.h";
+using mozilla::YUVColorSpace from "ImageTypes.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using gfxImageFormat from "gfxTypes.h";
+using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+union OverlayHandle {
+ int32_t;
+ GonkNativeHandle;
+ null_t;
+};
+
+struct OverlaySource {
+ OverlayHandle handle;
+ IntSize size;
+};
+
+struct SurfaceDescriptorFileMapping {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+};
+
+struct SurfaceDescriptorDIB {
+ // gfxWindowsSurface*
+ uintptr_t surface;
+};
+
+struct SurfaceDescriptorD3D10 {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+};
+
+struct SurfaceDescriptorDXGIYCbCr {
+ WindowsHandle handleY;
+ WindowsHandle handleCb;
+ WindowsHandle handleCr;
+ IntSize size;
+ IntSize sizeY;
+ IntSize sizeCbCr;
+};
+
+struct SurfaceDescriptorMacIOSurface {
+ uint32_t surfaceId;
+ double scaleFactor;
+ bool isOpaque;
+};
+
+struct SurfaceTextureDescriptor {
+ uintptr_t surfTex;
+ IntSize size;
+};
+
+struct EGLImageDescriptor {
+ uintptr_t image; // `EGLImage` is a `void*`.
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+struct SurfaceDescriptorSharedGLTexture {
+ uint32_t texture;
+ uint32_t target;
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+struct SurfaceDescriptorGPUVideo {
+ uint64_t handle;
+};
+
+struct RGBDescriptor {
+ IntSize size;
+ SurfaceFormat format;
+ bool hasIntermediateBuffer;
+};
+
+struct YCbCrDescriptor {
+ IntSize ySize;
+ IntSize cbCrSize;
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ StereoMode stereoMode;
+ YUVColorSpace yUVColorSpace;
+ bool hasIntermediateBuffer;
+};
+
+union BufferDescriptor {
+ RGBDescriptor;
+ YCbCrDescriptor;
+};
+
+union MemoryOrShmem {
+ uintptr_t;
+ Shmem;
+};
+
+struct SurfaceDescriptorBuffer {
+ BufferDescriptor desc;
+ MemoryOrShmem data;
+};
+
+union SurfaceDescriptor {
+ SurfaceDescriptorBuffer;
+ SurfaceDescriptorDIB;
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorFileMapping;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorX11;
+ SurfaceTextureDescriptor;
+ EGLImageDescriptor;
+ SurfaceDescriptorMacIOSurface;
+ SurfaceDescriptorSharedGLTexture;
+ SurfaceDescriptorGPUVideo;
+ null_t;
+};
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/ipc/PAPZ.ipdl b/system/graphics/layers/ipc/PAPZ.ipdl
new file mode 100644
index 000000000..e321b9ea0
--- /dev/null
+++ b/system/graphics/layers/ipc/PAPZ.ipdl
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using class nsRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
+ * PCompositorBridge protocol which either connects to the compositor thread
+ * in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
+ * GeckoContentController lives (generally the main thread of the main or content process).
+ * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
+ *
+ * PAPZ is always used for ContentProcessController and only used for ChromeProcessController
+ * when there is a gpu process, otherwhise ChromeProcessController is used directly on the
+ * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
+ * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
+ * must be updated to handle it.
+ */
+sync protocol PAPZ
+{
+ manager PCompositorBridge;
+
+parent:
+
+ async __delete__();
+
+child:
+
+ async RequestContentRepaint(FrameMetrics frame);
+
+ async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent);
+
+ async UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent);
+
+ async SetScrollingRootContent(bool aIsRootContent);
+
+ async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
+
+ async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg);
+
+ async NotifyFlushComplete();
+
+ async Destroy();
+};
+
+} // layers
+} // mozilla
diff --git a/system/graphics/layers/ipc/PAPZCTreeManager.ipdl b/system/graphics/layers/ipc/PAPZCTreeManager.ipdl
new file mode 100644
index 000000000..21d899f91
--- /dev/null
+++ b/system/graphics/layers/ipc/PAPZCTreeManager.ipdl
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "ipc/nsGUIEventIPC.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using LayoutDeviceCoord from "Units.h";
+using LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using ScreenPoint from "Units.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
+
+using nsEventStatus from "mozilla/EventForwards.h";
+using EventMessage from "mozilla/EventForwards.h";
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using class mozilla::WidgetInputEvent from "mozilla/BasicEvents.h";
+using class mozilla::WidgetMouseEventBase from "mozilla/MouseEvents.h";
+using mozilla::WidgetMouseEvent::Reason from "mozilla/MouseEvents.h";
+using class mozilla::WidgetTouchEvent from "mozilla/TouchEvents.h";
+using class mozilla::WidgetWheelEvent from "mozilla/MouseEvents.h";
+using class mozilla::InputData from "InputData.h";
+using class mozilla::MultiTouchInput from "InputData.h";
+using class mozilla::MouseInput from "InputData.h";
+using class mozilla::PanGestureInput from "InputData.h";
+using class mozilla::PinchGestureInput from "InputData.h";
+using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
+using class mozilla::TapGestureInput from "InputData.h";
+using class mozilla::ScrollWheelInput from "InputData.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
+ * lives on the PCompositorBridge protocol which either connects to the compositor
+ * thread in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
+ * lives in the main thread of the main or the content process. APZCTreeManagerParent
+ * and APZCTreeManagerChild implement this protocol.
+ */
+sync protocol PAPZCTreeManager
+{
+manager PCompositorBridge;
+
+parent:
+
+ // These messages correspond to the methods
+ // on the IAPZCTreeManager interface
+
+ async ZoomToRect(ScrollableLayerGuid aGuid, CSSRect aRect, uint32_t Flags);
+
+ async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault);
+
+ async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets);
+
+ async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints aConstraints);
+
+ async CancelAnimation(ScrollableLayerGuid aGuid);
+
+ async AdjustScrollForSurfaceShift(ScreenPoint aShift);
+
+ async SetDPI(float aDpiValue);
+
+ async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues);
+
+ async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics);
+
+ async SetLongTapEnabled(bool aTapGestureEnabled);
+
+ async ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY);
+
+ // The following messages are used to
+ // implement the ReceiveInputEvent methods
+
+ sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ MultiTouchInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveMouseInputEvent(MouseInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ MouseInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceivePanGestureInputEvent(PanGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ PanGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ PinchGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveTapGestureInputEvent(TapGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ TapGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ ScrollWheelInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage);
+
+ sync TransformEventRefPoint(LayoutDeviceIntPoint aRefPoint)
+ returns (LayoutDeviceIntPoint aOutRefPoint,
+ ScrollableLayerGuid aOutTargetGuid);
+
+ async __delete__();
+
+child:
+
+ async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+
+ async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid,
+ LayoutDeviceCoord aSpanChange, Modifiers aModifiers);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/PCompositable.ipdl b/system/graphics/layers/ipc/PCompositable.ipdl
new file mode 100644
index 000000000..d7754cd95
--- /dev/null
+++ b/system/graphics/layers/ipc/PCompositable.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PLayerTransaction;
+include protocol PImageBridge;
+include protocol PCompositorBridge;
+
+namespace mozilla {
+namespace layers {
+
+async protocol PCompositable
+{
+ manager PImageBridge or PLayerTransaction;
+child:
+ async __delete__();
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+};
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/ipc/PCompositorBridge.ipdl b/system/graphics/layers/ipc/PCompositorBridge.ipdl
new file mode 100644
index 000000000..03a353506
--- /dev/null
+++ b/system/graphics/layers/ipc/PCompositorBridge.ipdl
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include PlatformWidgetTypes;
+include protocol PAPZ;
+include protocol PAPZCTreeManager;
+include protocol PBrowser;
+include protocol PCompositable;
+include protocol PCompositorWidget;
+include protocol PImageContainer;
+include protocol PLayer;
+include protocol PLayerTransaction;
+include protocol PTexture;
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::CSSIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDeviceIntRegion from "Units.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * The PCompositorBridge protocol is used to manage communication between
+ * the main thread and the compositor thread context. It's primary
+ * purpose is to manage the PLayerTransaction sub protocol.
+ */
+sync protocol PCompositorBridge
+{
+ manages PAPZ;
+ manages PAPZCTreeManager;
+ // A Compositor manages a single Layer Manager (PLayerTransaction)
+ manages PLayerTransaction;
+ manages PTexture;
+ manages PCompositorWidget;
+
+child:
+ // The child should invalidate retained layers. This is used for local
+ // compositor device resets, such as in CompositorD3D9, and ensures that
+ // TextureSources are recreated.
+ async InvalidateLayers(uint64_t layersId);
+
+ // The compositor type or device has changed, and a new texture factory
+ // identifier is available. Layers must be invalidated and the new identifier
+ // must be propagated.
+ async CompositorUpdated(uint64_t layersId, TextureFactoryIdentifier newIdentifier);
+
+ // The compositor completed a layers transaction. id is the layers id
+ // of the child layer tree that was composited (or 0 when notifying
+ // the root layer tree).
+ // transactionId is the id of the transaction before this composite, or 0
+ // if there was no transaction since the last composite.
+ async DidComposite(uint64_t id, uint64_t transactionId,
+ TimeStamp compositeStart, TimeStamp compositeEnd);
+
+ // The parent sends the child the requested fill ratio numbers.
+ async Overfill(uint32_t aOverfill);
+
+ /**
+ * Parent informs the child that the graphics objects are ready for
+ * compositing. This usually means that the graphics objects (textures
+ * and the like) are available on the GPU. This is used for chrome UI.
+ * @see RequestNotifyAfterRemotePaint
+ * @see PBrowser
+ */
+ async RemotePaintIsReady();
+
+ /**
+ * Bounce plugin widget configurations over to the main thread for
+ * application on the widgets. Used on Windows and Linux in managing
+ * plugin widgets.
+ */
+ async UpdatePluginConfigurations(LayoutDeviceIntPoint aContentOffset,
+ LayoutDeviceIntRegion aVisibleRegion,
+ PluginWindowData[] aPlugins);
+
+ /**
+ * Captures an image for all visible child plugins of a given widget for use
+ * during scrolling.
+ * @param aParentWidget parent of widgets to be captured
+ */
+ async CaptureAllPlugins(uintptr_t aParentWidget);
+
+ /**
+ * Hides all registered plugin widgets associated with a particular chrome
+ * widget.
+ */
+ async HideAllPlugins(uintptr_t aParentWidget);
+
+ /**
+ * Drop any buffers that might be retained on the child compositor
+ * side.
+ */
+ async ClearCachedResources(uint64_t id);
+
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive);
+
+parent:
+ // Must be called before Initialize().
+ async PCompositorWidget(CompositorWidgetInitData aInitData);
+
+ // When out-of-process, this must be called to finish initialization.
+ sync Initialize(uint64_t rootLayerTreeId);
+ sync Reset(LayersBackend[] aBackendHints) returns (bool aResult, TextureFactoryIdentifier aOutIdentifier);
+
+ // Returns whether this Compositor has APZ enabled or not.
+ sync AsyncPanZoomEnabled(uint64_t layersId) returns (bool aHasAPZ);
+
+ // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true.
+ async PAPZ(uint64_t layersId);
+ async PAPZCTreeManager(uint64_t layersId);
+
+ /**
+ * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins.
+ */
+ async RemotePluginsReady();
+
+ // Confirmation that the child has invalidated all its layers, and will not
+ // request layers against an old compositor.
+ async AcknowledgeCompositorUpdate(uint64_t id);
+
+ // Child sends the parent a request for fill ratio numbers.
+ async RequestOverfill();
+
+ // Child requests frame uniformity measurements
+ sync GetFrameUniformity() returns (FrameUniformityData data);
+
+ // The child is about to be destroyed, so perform any necessary cleanup.
+ sync WillClose();
+
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume();
+
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync NotifyChildCreated(uint64_t id);
+
+ async AdoptChild(uint64_t id);
+
+ // Same as NotifyChildCreated, but used when child processes need to
+ // reassociate layers. This must be synchronous to ensure that the
+ // association happens before PLayerTransactions are sent over the
+ // cross-process bridge.
+ sync NotifyChildRecreated(uint64_t id);
+
+ // Make a snapshot of the content that would have been drawn to our
+ // render target at the time this message is received. If the size
+ // or format of |inSnapshot| doesn't match our render target,
+ // results are undefined.
+ //
+ // NB: this message will result in animations, transforms, effects,
+ // and so forth being interpolated. That's what we want to happen.
+ sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect);
+
+ // Make sure any pending composites are started immediately and
+ // block until they are completed.
+ sync FlushRendering();
+
+ // Force an additional frame presentation to be executed. This is used to
+ // work around a windows presentation bug (See Bug 1232042)
+ async ForcePresent();
+
+ sync StartFrameTimeRecording(int32_t bufferSize)
+ returns (uint32_t startIndex);
+
+ sync StopFrameTimeRecording(uint32_t startIndex)
+ returns (float[] intervals);
+
+ // layersBackendHints is an ordered list of preffered backends where
+ // layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE
+ // that hint is ignored.
+ sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id)
+ returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success);
+
+ // Notify the compositor that a region of the screen has been invalidated.
+ async NotifyRegionInvalidated(nsIntRegion region);
+
+ /**
+ * The child (content/chrome thread) requests that the parent inform it when
+ * the graphics objects are ready to display.
+ * @see PBrowser
+ * @see RemotePaintIsReady
+ */
+ async RequestNotifyAfterRemotePaint();
+
+ // The child clears the 'approximately visible' regions associated with the
+ // provided layers ID and pres shell ID (i.e., the regions for all view IDs
+ // associated with those IDs).
+ async ClearApproximatelyVisibleRegions(uint64_t layersId, uint32_t presShellId);
+
+ // The child sends a region containing rects associated with the provided
+ // scrollable layer GUID that the child considers 'approximately visible'.
+ // We visualize this information in the APZ minimap.
+ async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region);
+
+ /**
+ * Sent when the child has finished CaptureAllPlugins.
+ */
+ async AllPluginsCaptured();
+
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
+
+ sync SyncWithCompositor();
+
+child:
+ // Send back Compositor Frame Metrics from APZCs so tiled layers can
+ // update progressively.
+ async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint64_t aLayersId, uint32_t aAPZCId);
+ async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
+};
+
+} // layers
+} // mozilla
diff --git a/system/graphics/layers/ipc/PImageBridge.ipdl b/system/graphics/layers/ipc/PImageBridge.ipdl
new file mode 100644
index 000000000..3bed222d0
--- /dev/null
+++ b/system/graphics/layers/ipc/PImageBridge.ipdl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PCompositable;
+include protocol PImageContainer;
+include protocol PLayer;
+include protocol PTexture;
+include ProtocolTypes;
+include protocol PMediaSystemResourceManager;
+
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+using PlatformThreadId from "base/platform_thread.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PImageBridge protocol is used to allow isolated threads or processes to push
+ * frames directly to the compositor thread/process without relying on the main thread
+ * which might be too busy dealing with content script.
+ */
+sync protocol PImageBridge
+{
+ manages PCompositable;
+ manages PTexture;
+ manages PMediaSystemResourceManager;
+ manages PImageContainer;
+
+child:
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async DidComposite(ImageCompositeNotification[] aNotifications);
+
+parent:
+ async ImageBridgeThreadId(PlatformThreadId aTreahdId);
+
+ sync Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId)
+ returns (EditReply[] reply);
+
+ async UpdateNoSwap(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
+
+ // First step of the destruction sequence. This puts ImageBridge
+ // in a state in which it can't send asynchronous messages
+ // so as to not race with the channel getting closed.
+ // In the child side, the Closing the channel does not happen right after WillClose,
+ // it is scheduled in the ImageBridgeChild's message queue in order to ensure
+ // that all of the messages from the parent side have been received and processed
+ // before sending closing the channel.
+ sync WillClose();
+
+ sync PCompositable(TextureInfo aInfo,
+ nullable PImageContainer aImageContainer) returns (uint64_t id);
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial);
+ async PMediaSystemResourceManager();
+ async PImageContainer();
+
+};
+
+
+} // namespace
+} // namespace
+
diff --git a/system/graphics/layers/ipc/PImageContainer.ipdl b/system/graphics/layers/ipc/PImageContainer.ipdl
new file mode 100644
index 000000000..7ae567a88
--- /dev/null
+++ b/system/graphics/layers/ipc/PImageContainer.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PImageBridge;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PImageContainer represents an ImageContainer.
+ */
+
+async protocol PImageContainer {
+ manager PImageBridge;
+parent:
+ /**
+ * The child effectively owns the parent. When the child should be
+ * destroyed, it sends an AsyncDelete to the parent but does not die
+ * because we could still have messages in flight from the compositor
+ * mentioning the child. The parent handles AsyncDelete by destroying
+ * itself and sending __delete__ to the child to clean it up.
+ */
+ async AsyncDelete();
+child:
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/system/graphics/layers/ipc/PLayer.ipdl b/system/graphics/layers/ipc/PLayer.ipdl
new file mode 100644
index 000000000..4a46b56d6
--- /dev/null
+++ b/system/graphics/layers/ipc/PLayer.ipdl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PLayerTransaction;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PLayer represents a layer shared across thread contexts.
+ */
+
+async protocol PLayer {
+ manager PLayerTransaction;
+
+ /**
+ * OWNERSHIP MODEL
+ *
+ * Roughly speaking, the child side "actually owns" a Layer. This
+ * is because the parent side is the "shadow"; when the child
+ * releases a Layer, the parent's shadow is no longer meaningful.
+ *
+ * To implement this model, the concrete PLayerParent keeps a
+ * strong ref to its Layer, so the Layer's lifetime is bound to
+ * the PLayerParent's. Then, when the Layer's refcount hits 0 on
+ * the child side, we send __delete__() from the child to parent.
+ * The parent then releases its Layer, which results in the Layer
+ * being deleted "soon" (usually immediately).
+ */
+parent:
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/system/graphics/layers/ipc/PLayerTransaction.ipdl b/system/graphics/layers/ipc/PLayerTransaction.ipdl
new file mode 100644
index 000000000..d669b1d65
--- /dev/null
+++ b/system/graphics/layers/ipc/PLayerTransaction.ipdl
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PCompositable;
+include protocol PCompositorBridge;
+include protocol PLayer;
+include protocol PRenderFrame;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+
+/**
+ * The layers protocol is spoken between thread contexts that manage
+ * layer (sub)trees. The protocol comprises atomically publishing
+ * layer subtrees to a "shadow" thread context (which grafts the
+ * subtree into its own tree), and atomically updating a published
+ * subtree. ("Atomic" in this sense is wrt painting.)
+ */
+
+namespace mozilla {
+namespace layers {
+
+union MaybeTransform {
+ Matrix4x4;
+ void_t;
+};
+
+sync protocol PLayerTransaction {
+ manager PCompositorBridge;
+ manages PLayer;
+ manages PCompositable;
+
+parent:
+ async PLayer();
+ async PCompositable(TextureInfo aTextureInfo);
+
+ // The isFirstPaint flag can be used to indicate that this is the first update
+ // for a particular document.
+ sync Update(Edit[] cset, OpDestroy[] toDestroy,
+ uint64_t fwdTransactionId,
+ uint64_t id, TargetConfig targetConfig,
+ PluginWindowData[] plugins, bool isFirstPaint,
+ bool scheduleComposite, uint32_t paintSequenceNumber,
+ bool isRepeatTransaction, TimeStamp transactionStart,
+ int32_t paintSyncId)
+ returns (EditReply[] reply);
+
+ async PaintTime(uint64_t id, TimeDuration paintTime);
+
+ // We don't need to send a sync transaction if
+ // no transaction operate require a swap.
+ async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy,
+ uint64_t fwdTransactionId,
+ uint64_t id, TargetConfig targetConfig,
+ PluginWindowData[] plugins, bool isFirstPaint,
+ bool scheduleComposite, uint32_t paintSequenceNumber,
+ bool isRepeatTransaction, TimeStamp transactionStart,
+ int32_t paintSyncId);
+
+ async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
+
+ // Testing APIs
+
+ // Enter test mode, set the sample time to sampleTime, and resample
+ // animations. sampleTime must not be null.
+ sync SetTestSampleTime(TimeStamp sampleTime);
+ // Leave test mode and resume normal compositing
+ sync LeaveTestMode();
+
+ // Returns the value of the opacity applied to the layer by animation.
+ // |hasAnimationOpacity| is true if the layer has an opacity value
+ // specified by animation. If it's false, |opacity| value is indefinite.
+ sync GetAnimationOpacity(PLayer layer) returns (float opacity,
+ bool hasAnimationOpacity);
+
+ // Returns the value of the transform applied to the layer by animation after
+ // factoring out translation components introduced to account for the offset
+ // of the corresponding frame and transform origin and after converting to CSS
+ // pixels. If the layer is not transformed by animation, the return value will
+ // be void_t.
+ sync GetAnimationTransform(PLayer layer) returns (MaybeTransform transform);
+
+ // The next time the layer tree is composited, add this async scroll offset in
+ // CSS pixels for the given ViewID.
+ // Useful for testing rendering of async scrolling.
+ sync SetAsyncScrollOffset(ViewID id, float x, float y);
+
+ // The next time the layer tree is composited, include this async zoom in
+ // for the given ViewID.
+ // Useful for testing rendering of async zooming.
+ sync SetAsyncZoom(ViewID id, float zoom);
+
+ // Flush any pending APZ repaints to the main thread.
+ async FlushApzRepaints();
+
+ // Drop any front buffers that might be retained on the compositor
+ // side.
+ async ClearCachedResources();
+
+ // Schedule a composite if one isn't already scheduled.
+ async ForceComposite();
+
+ // Get a copy of the compositor-side APZ test data instance for this
+ // layers id.
+ sync GetAPZTestData() returns (APZTestData data);
+
+ // Query a named property from the last frame
+ sync RequestProperty(nsString property) returns (float value);
+
+ // Tell the compositor to notify APZ that a layer has been confirmed for an
+ // input event.
+ async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+
+ async Shutdown();
+child:
+ async __delete__();
+};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/PTexture.ipdl b/system/graphics/layers/ipc/PTexture.ipdl
new file mode 100644
index 000000000..7c1979bf0
--- /dev/null
+++ b/system/graphics/layers/ipc/PTexture.ipdl
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PLayerTransaction;
+include protocol PCompositorBridge;
+include protocol PImageBridge;
+include protocol PVideoBridge;
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PTexture is the IPDL glue between a TextureClient and a TextureHost.
+ */
+sync protocol PTexture {
+ manager PImageBridge or PCompositorBridge or PVideoBridge;
+
+child:
+ async __delete__();
+
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+
+ /**
+ * Synchronously tell the compositor side to remove the texture.
+ */
+ sync DestroySync();
+
+ async RecycleTexture(TextureFlags aTextureFlags);
+};
+
+} // layers
+} // mozilla
diff --git a/system/graphics/layers/ipc/PVideoBridge.ipdl b/system/graphics/layers/ipc/PVideoBridge.ipdl
new file mode 100644
index 000000000..3fca7ac75
--- /dev/null
+++ b/system/graphics/layers/ipc/PVideoBridge.ipdl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PVideoBridge protocol is used to share textures from the video decoders
+ * to the compositor.
+ */
+sync protocol PVideoBridge
+{
+ manages PTexture;
+
+parent:
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend,
+ TextureFlags aTextureFlags, uint64_t aSerial);
+};
+
+} // namespace
+} // namespace
+
diff --git a/system/graphics/layers/ipc/RemoteContentController.cpp b/system/graphics/layers/ipc/RemoteContentController.cpp
new file mode 100644
index 000000000..d71476a78
--- /dev/null
+++ b/system/graphics/layers/ipc/RemoteContentController.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/RemoteContentController.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "MainThreadUtils.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/Unused.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+RemoteContentController::RemoteContentController()
+ : mCompositorThread(MessageLoop::current())
+ , mCanSend(true)
+{
+}
+
+RemoteContentController::~RemoteContentController()
+{
+}
+
+void
+RemoteContentController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendRequestContentRepaint(aFrameMetrics);
+ }
+}
+
+void
+RemoteContentController::HandleTapOnMainThread(TapType aTapType,
+ LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+}
+
+void
+RemoteContentController::HandleTap(TapType aTapType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ MOZ_ASSERT(MessageLoop::current() == mCompositorThread);
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (NS_IsMainThread()) {
+ HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ } else {
+ // We don't want to get the TabParent or call TabParent::SendHandleTap() from a non-main thread
+ NS_DispatchToMainThread(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, ScrollableLayerGuid, uint64_t>
+ (this, &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, aModifiers, aGuid, aInputBlockId));
+ }
+}
+
+void
+RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // For now we only ever want to handle this NotifyPinchGesture message in
+ // the parent process, even if the APZ is sending it to a content process.
+
+ // If we're in the GPU process, try to find a handle to the parent process
+ // and send it there.
+ if (XRE_IsGPUProcess()) {
+ MOZ_ASSERT(MessageLoop::current() == mCompositorThread);
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
+ return;
+ }
+ }
+
+ // If we're in the parent process, handle it directly. We don't have a handle
+ // to the widget though, so we fish out the ChromeProcessController and
+ // delegate to that instead.
+ if (XRE_IsParentProcess()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId);
+ if (rootController) {
+ rootController->NotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
+ }
+ }
+}
+
+void
+RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
+{
+ (MessageLoop::current() ? MessageLoop::current() : mCompositorThread)->
+ PostDelayedTask(Move(aTask), aDelayMs);
+}
+
+bool
+RemoteContentController::IsRepaintThread()
+{
+ return MessageLoop::current() == mCompositorThread;
+}
+
+void
+RemoteContentController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ mCompositorThread->PostTask(Move(aTask));
+}
+
+void
+RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid,
+ APZStateChange,
+ int>(this,
+ &RemoteContentController::NotifyAPZStateChange,
+ aGuid, aChange, aArg));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg);
+ }
+}
+
+void
+RemoteContentController::UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<float,
+ float, bool>(this,
+ &RemoteContentController::UpdateOverscrollVelocity,
+ aX, aY, aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendUpdateOverscrollVelocity(aX, aY, aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<float,
+ float, bool>(this,
+ &RemoteContentController::UpdateOverscrollOffset,
+ aX, aY, aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendUpdateOverscrollOffset(aX, aY, aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::SetScrollingRootContent(bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<bool>(this,
+ &RemoteContentController::SetScrollingRootContent,
+ aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendSetScrollingRootContent(aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID,
+ nsString>(this,
+ &RemoteContentController::NotifyMozMouseScrollEvent,
+ aScrollId, aEvent));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void
+RemoteContentController::NotifyFlushComplete()
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendNotifyFlushComplete();
+ }
+}
+
+void
+RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // This controller could possibly be kept alive longer after this
+ // by a RefPtr, but it is no longer valid to send messages.
+ mCanSend = false;
+}
+
+void
+RemoteContentController::Destroy()
+{
+ if (mCanSend) {
+ mCanSend = false;
+ Unused << SendDestroy();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/RemoteContentController.h b/system/graphics/layers/ipc/RemoteContentController.h
new file mode 100644
index 000000000..d015ada4f
--- /dev/null
+++ b/system/graphics/layers/ipc/RemoteContentController.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_RemoteContentController_h
+#define mozilla_layers_RemoteContentController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/PAPZParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+}
+
+namespace layers {
+
+/**
+ * RemoteContentController implements PAPZChild and is used to access a
+ * GeckoContentController that lives in a different process.
+ *
+ * RemoteContentController lives on the compositor thread. All methods can
+ * be called off the compositor thread and will get dispatched to the right
+ * thread, with the exception of RequestContentRepaint and NotifyFlushComplete,
+ * which must be called on the repaint thread, which in this case is the compositor
+ * thread.
+ */
+class RemoteContentController : public GeckoContentController
+ , public PAPZParent
+{
+ using GeckoContentController::TapType;
+ using GeckoContentController::APZStateChange;
+
+public:
+ RemoteContentController();
+
+ virtual ~RemoteContentController();
+
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+
+ virtual void HandleTap(TapType aTapType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
+
+ virtual bool IsRepaintThread() override;
+
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+
+ virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override;
+
+ virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override;
+
+ virtual void SetScrollingRootContent(bool aIsRootContent) override;
+
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ virtual void NotifyFlushComplete() override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual void Destroy() override;
+
+private:
+ MessageLoop* mCompositorThread;
+ bool mCanSend;
+
+ void HandleTapOnMainThread(TapType aType,
+ LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_RemoteContentController_h
diff --git a/system/graphics/layers/ipc/ShadowLayerChild.cpp b/system/graphics/layers/ipc/ShadowLayerChild.cpp
new file mode 100644
index 000000000..62d260838
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerChild.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerChild.h"
+#include "Layers.h" // for Layer
+#include "ShadowLayers.h" // for ShadowableLayer
+
+namespace mozilla {
+namespace layers {
+
+ShadowLayerChild::ShadowLayerChild()
+ : mLayer(nullptr)
+{ }
+
+ShadowLayerChild::~ShadowLayerChild()
+{ }
+
+void
+ShadowLayerChild::SetShadowableLayer(ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(!mLayer);
+ mLayer = aLayer;
+}
+
+void
+ShadowLayerChild::ActorDestroy(ActorDestroyReason why)
+{
+ MOZ_ASSERT(AncestorDeletion != why,
+ "shadowable layer should have been cleaned up by now");
+
+ if (AbnormalShutdown == why && mLayer) {
+ // This is last-ditch emergency shutdown. Just have the layer
+ // forget its IPDL resources; IPDL-generated code will clean up
+ // automatically in this case.
+ mLayer->AsLayer()->Disconnect();
+ mLayer = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ShadowLayerChild.h b/system/graphics/layers/ipc/ShadowLayerChild.h
new file mode 100644
index 000000000..38a7f9500
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerChild.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerChild_h
+#define mozilla_layers_ShadowLayerChild_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+
+class ShadowLayerChild : public PLayerChild
+{
+public:
+ ShadowLayerChild();
+ virtual ~ShadowLayerChild();
+
+ void SetShadowableLayer(ShadowableLayer* aLayer);
+ ShadowableLayer* layer() const { return mLayer; }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ ShadowableLayer* mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayerChild_h
diff --git a/system/graphics/layers/ipc/ShadowLayerParent.cpp b/system/graphics/layers/ipc/ShadowLayerParent.cpp
new file mode 100644
index 000000000..e0898715d
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerParent.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerParent.h"
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/layers/CanvasLayerComposite.h"
+#include "mozilla/layers/ColorLayerComposite.h"
+#include "mozilla/layers/ImageLayerComposite.h"
+#include "mozilla/layers/ContainerLayerComposite.h"
+
+namespace mozilla {
+namespace layers {
+
+ShadowLayerParent::ShadowLayerParent() : mLayer(nullptr)
+{
+}
+
+ShadowLayerParent::~ShadowLayerParent()
+{
+ Disconnect();
+}
+
+void
+ShadowLayerParent::Disconnect()
+{
+ if (mLayer) {
+ mLayer->Disconnect();
+ mLayer = nullptr;
+ }
+}
+
+void
+ShadowLayerParent::Bind(Layer* layer)
+{
+ if (mLayer != layer) {
+ Disconnect();
+ mLayer = layer;
+ }
+}
+
+void
+ShadowLayerParent::Destroy()
+{
+ // It's possible for Destroy() to come in just after this has been
+ // created, but just before the transaction in which Bind() would
+ // have been called. In that case, we'll ignore shadow-layers
+ // transactions from there on and never get a layer here.
+ Disconnect();
+}
+
+ContainerLayerComposite*
+ShadowLayerParent::AsContainerLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_CONTAINER
+ ? static_cast<ContainerLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+CanvasLayerComposite*
+ShadowLayerParent::AsCanvasLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_CANVAS
+ ? static_cast<CanvasLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+ColorLayerComposite*
+ShadowLayerParent::AsColorLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_COLOR
+ ? static_cast<ColorLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+ImageLayerComposite*
+ShadowLayerParent::AsImageLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE
+ ? static_cast<ImageLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+RefLayerComposite*
+ShadowLayerParent::AsRefLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_REF
+ ? static_cast<RefLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+PaintedLayerComposite*
+ShadowLayerParent::AsPaintedLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED
+ ? static_cast<PaintedLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+void
+ShadowLayerParent::ActorDestroy(ActorDestroyReason why)
+{
+ switch (why) {
+ case AncestorDeletion:
+ NS_RUNTIMEABORT("shadow layer deleted out of order!");
+ return; // unreached
+
+ case Deletion:
+ // See comment near Destroy() above.
+ Disconnect();
+ break;
+
+ case AbnormalShutdown:
+ Disconnect();
+ break;
+
+ case NormalShutdown:
+ // let IPDL-generated code automatically clean up Shmems and so
+ // forth; our channel is disconnected anyway
+ break;
+
+ case FailedConstructor:
+ NS_RUNTIMEABORT("FailedConstructor isn't possible in PLayerTransaction");
+ return; // unreached
+ }
+
+ mLayer = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ShadowLayerParent.h b/system/graphics/layers/ipc/ShadowLayerParent.h
new file mode 100644
index 000000000..d40cf34ff
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerParent.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerParent_h
+#define mozilla_layers_ShadowLayerParent_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerParent.h" // for PLayerParent
+
+namespace mozilla {
+namespace layers {
+
+class ContainerLayer;
+class Layer;
+
+class CanvasLayerComposite;
+class ColorLayerComposite;
+class ContainerLayerComposite;
+class ImageLayerComposite;
+class RefLayerComposite;
+class PaintedLayerComposite;
+
+class ShadowLayerParent : public PLayerParent
+{
+public:
+ ShadowLayerParent();
+
+ virtual ~ShadowLayerParent();
+
+ void Bind(Layer* layer);
+ void Destroy();
+
+ Layer* AsLayer() const { return mLayer; }
+
+ ContainerLayerComposite* AsContainerLayerComposite() const;
+ CanvasLayerComposite* AsCanvasLayerComposite() const;
+ ColorLayerComposite* AsColorLayerComposite() const;
+ ImageLayerComposite* AsImageLayerComposite() const;
+ RefLayerComposite* AsRefLayerComposite() const;
+ PaintedLayerComposite* AsPaintedLayerComposite() const;
+
+private:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ void Disconnect();
+
+ RefPtr<Layer> mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayerParent_h
diff --git a/system/graphics/layers/ipc/ShadowLayerUtils.h b/system/graphics/layers/ipc/ShadowLayerUtils.h
new file mode 100644
index 000000000..faa5041e3
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerUtils.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_ShadowLayerUtils_h
+#define IPC_ShadowLayerUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "GLContextTypes.h"
+#include "SurfaceTypes.h"
+#include "mozilla/WidgetUtils.h"
+
+#if defined(MOZ_X11)
+# include "mozilla/layers/ShadowLayerUtilsX11.h"
+#else
+namespace mozilla { namespace layers {
+struct SurfaceDescriptorX11 {
+ bool operator==(const SurfaceDescriptorX11&) const { return false; }
+};
+} // namespace layers
+} // namespace mozilla
+#endif
+
+namespace IPC {
+
+#if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11)
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+ static void Write(Message*, const paramType&) {}
+ static bool Read(const Message*, PickleIterator*, paramType*) { return false; }
+};
+#endif // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11)
+
+template <>
+struct ParamTraits<mozilla::ScreenRotation>
+ : public ContiguousEnumSerializer<
+ mozilla::ScreenRotation,
+ mozilla::ROTATION_0,
+ mozilla::ROTATION_COUNT>
+{};
+
+} // namespace IPC
+
+#endif // IPC_ShadowLayerUtils_h
diff --git a/system/graphics/layers/ipc/ShadowLayerUtilsX11.cpp b/system/graphics/layers/ipc/ShadowLayerUtilsX11.cpp
new file mode 100644
index 000000000..6b9660054
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerUtilsX11.h"
+#include <X11/X.h> // for Drawable, XID
+#include <X11/Xlib.h> // for Display, Visual, etc
+#include <X11/extensions/Xrender.h> // for XRenderPictFormat, etc
+#include <X11/extensions/render.h> // for PictFormat
+#include "cairo-xlib.h"
+#include "X11UndefineNone.h"
+#include <stdint.h> // for uint32_t
+#include "GLDefs.h" // for GLenum
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxXlibSurface.h" // for gfxXlibSurface
+#include "gfx2DGlue.h" // for Moz2D transistion helpers
+#include "mozilla/X11Util.h" // for DefaultXDisplay, FinishX, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "gfxEnv.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class TextureImage;
+}
+
+namespace layers {
+
+// Return true if we're likely compositing using X and so should use
+// Xlib surfaces in shadow layers.
+static bool
+UsingXCompositing()
+{
+ if (!gfxEnv::LayersEnableXlibSurfaces()) {
+ return false;
+ }
+ return (gfxSurfaceType::Xlib ==
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType());
+}
+
+// LookReturn a pointer to |aFormat| that lives in the Xrender library.
+// All code using render formats assumes it doesn't need to copy.
+static XRenderPictFormat*
+GetXRenderPictFormatFromId(Display* aDisplay, PictFormat aFormatId)
+{
+ XRenderPictFormat tmplate;
+ tmplate.id = aFormatId;
+ return XRenderFindFormat(aDisplay, PictFormatID, &tmplate, 0);
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf,
+ bool aForwardGLX)
+ : mId(aSurf->XDrawable())
+ , mSize(aSurf->GetSize())
+ , mGLXPixmap(X11None)
+{
+ const XRenderPictFormat *pictFormat = aSurf->XRenderFormat();
+ if (pictFormat) {
+ mFormat = pictFormat->id;
+ } else {
+ mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid;
+ }
+
+#ifdef GL_PROVIDER_GLX
+ if (aForwardGLX) {
+ mGLXPixmap = aSurf->GetGLXPixmap();
+ }
+#endif
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize)
+ : mId(aDrawable)
+ , mFormat(aFormatID)
+ , mSize(aSize)
+ , mGLXPixmap(X11None)
+{ }
+
+already_AddRefed<gfxXlibSurface>
+SurfaceDescriptorX11::OpenForeign() const
+{
+ Display* display = DefaultXDisplay();
+ Screen* screen = DefaultScreenOfDisplay(display);
+
+ RefPtr<gfxXlibSurface> surf;
+ XRenderPictFormat* pictFormat = GetXRenderPictFormatFromId(display, mFormat);
+ if (pictFormat) {
+ surf = new gfxXlibSurface(screen, mId, pictFormat, mSize);
+ } else {
+ Visual* visual;
+ int depth;
+ FindVisualAndDepth(display, mFormat, &visual, &depth);
+ if (!visual)
+ return nullptr;
+
+ surf = new gfxXlibSurface(display, mId, visual, mSize);
+ }
+
+#ifdef GL_PROVIDER_GLX
+ if (mGLXPixmap)
+ surf->BindGLXPixmap(mGLXPixmap);
+#endif
+
+ return surf->CairoStatus() ? nullptr : surf.forget();
+}
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, then we need to finish all pending
+ // operations on the back buffers before handing them to the
+ // parent, otherwise the surface might be used by the parent's
+ // Display in between two operations queued by our Display.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, we need to finish all pending
+ // operations on the *front buffers* before handing them back to
+ // the child, even though they will be read operations.
+ // Otherwise, the child might start scribbling on new back buffers
+ // that are still participating in requests as old front buffers.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ShadowLayerUtilsX11.h b/system/graphics/layers/ipc/ShadowLayerUtilsX11.h
new file mode 100644
index 000000000..bf64b09ff
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayerUtilsX11.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerUtilsX11_h
+#define mozilla_layers_ShadowLayerUtilsX11_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+
+#define MOZ_HAVE_SURFACEDESCRIPTORX11
+#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+typedef unsigned long XID;
+typedef XID Drawable;
+
+class gfxXlibSurface;
+
+namespace IPC {
+class Message;
+}
+
+namespace mozilla {
+namespace layers {
+
+struct SurfaceDescriptorX11 {
+ SurfaceDescriptorX11()
+ { }
+
+ explicit SurfaceDescriptorX11(gfxXlibSurface* aSurf, bool aForwardGLX = false);
+
+ SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize);
+
+ // Default copy ctor and operator= are OK
+
+ bool operator==(const SurfaceDescriptorX11& aOther) const {
+ // Define == as two descriptors having the same XID for now,
+ // ignoring size and render format. If the two indeed refer to
+ // the same valid XID, then size/format are "actually" the same
+ // anyway, regardless of the values of the fields in
+ // SurfaceDescriptorX11.
+ return mId == aOther.mId;
+ }
+
+ already_AddRefed<gfxXlibSurface> OpenForeign() const;
+
+ MOZ_INIT_OUTSIDE_CTOR Drawable mId;
+ MOZ_INIT_OUTSIDE_CTOR XID mFormat; // either a PictFormat or VisualID
+ MOZ_INIT_OUTSIDE_CTOR gfx::IntSize mSize;
+ MOZ_INIT_OUTSIDE_CTOR Drawable mGLXPixmap; // used to prevent multiple bindings to the same GLXPixmap in-process
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mId);
+ WriteParam(aMsg, aParam.mSize);
+ WriteParam(aMsg, aParam.mFormat);
+ WriteParam(aMsg, aParam.mGLXPixmap);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mId) &&
+ ReadParam(aMsg, aIter, &aResult->mSize) &&
+ ReadParam(aMsg, aIter, &aResult->mFormat) &&
+ ReadParam(aMsg, aIter, &aResult->mGLXPixmap)
+ );
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_ShadowLayerUtilsX11_h
diff --git a/system/graphics/layers/ipc/ShadowLayers.cpp b/system/graphics/layers/ipc/ShadowLayers.cpp
new file mode 100644
index 000000000..88b88bde0
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayers.cpp
@@ -0,0 +1,1044 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "ShadowLayers.h"
+#include <set> // for _Rb_tree_const_iterator, etc
+#include <vector> // for vector
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceScope
+#include "ShadowLayerChild.h" // for ShadowLayerChild
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "gfxPlatform.h" // for gfxImageFormat, gfxPlatform
+//#include "gfxSharedImageSurface.h" // for gfxSharedImageSurface
+#include "ipc/IPCMessageUtils.h" // for gfxContentType, null_t
+#include "IPDLActor.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/LayersMessages.h" // for Edit, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/PCompositableChild.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "ShadowLayerUtils.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "mozilla/ReentrantMonitor.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+using namespace mozilla::ipc;
+
+class ClientTiledLayerBuffer;
+
+typedef nsTArray<SurfaceDescriptor> BufferArray;
+typedef std::vector<Edit> EditVector;
+typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+class Transaction
+{
+public:
+ Transaction()
+ : mTargetRotation(ROTATION_0)
+ , mSwapRequired(false)
+ , mOpen(false)
+ , mRotationChanged(false)
+ {}
+
+ void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation,
+ dom::ScreenOrientationInternal aOrientation)
+ {
+ mOpen = true;
+ mTargetBounds = aTargetBounds;
+ if (aRotation != mTargetRotation) {
+ // the first time this is called, mRotationChanged will be false if
+ // aRotation is 0, but we should be OK because for the first transaction
+ // we should only compose if it is non-empty. See the caller(s) of
+ // RotationChanged.
+ mRotationChanged = true;
+ }
+ mTargetRotation = aRotation;
+ mTargetOrientation = aOrientation;
+ }
+ void MarkSyncTransaction()
+ {
+ mSwapRequired = true;
+ }
+ void AddEdit(const Edit& aEdit)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mCset.push_back(aEdit);
+ }
+ void AddEdit(const CompositableOperation& aEdit)
+ {
+ AddEdit(Edit(aEdit));
+ }
+ void AddPaint(const Edit& aPaint)
+ {
+ AddNoSwapPaint(aPaint);
+ mSwapRequired = true;
+ }
+ void AddPaint(const CompositableOperation& aPaint)
+ {
+ AddNoSwapPaint(Edit(aPaint));
+ mSwapRequired = true;
+ }
+
+ void AddNoSwapPaint(const Edit& aPaint)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mPaints.push_back(aPaint);
+ }
+ void AddNoSwapPaint(const CompositableOperation& aPaint)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mPaints.push_back(Edit(aPaint));
+ }
+ void AddMutant(ShadowableLayer* aLayer)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mMutants.PutEntry(aLayer);
+ }
+ void End()
+ {
+ mCset.clear();
+ mPaints.clear();
+ mMutants.Clear();
+ mDestroyedActors.Clear();
+ mOpen = false;
+ mSwapRequired = false;
+ mRotationChanged = false;
+ }
+
+ bool Empty() const {
+ return mCset.empty() && mPaints.empty() && mMutants.IsEmpty()
+ && mDestroyedActors.IsEmpty();
+ }
+ bool RotationChanged() const {
+ return mRotationChanged;
+ }
+ bool Finished() const { return !mOpen && Empty(); }
+
+ bool Opened() const { return mOpen; }
+
+ EditVector mCset;
+ EditVector mPaints;
+ OpDestroyVector mDestroyedActors;
+ ShadowableLayerSet mMutants;
+ gfx::IntRect mTargetBounds;
+ ScreenRotation mTargetRotation;
+ dom::ScreenOrientationInternal mTargetOrientation;
+ bool mSwapRequired;
+
+private:
+ bool mOpen;
+ bool mRotationChanged;
+
+ // disabled
+ Transaction(const Transaction&);
+ Transaction& operator=(const Transaction&);
+};
+struct AutoTxnEnd {
+ explicit AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {}
+ ~AutoTxnEnd() { mTxn->End(); }
+ Transaction* mTxn;
+};
+
+void
+KnowsCompositor::IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier)
+{
+ mTextureFactoryIdentifier = aIdentifier;
+
+ mSyncObject = SyncObject::CreateSyncObject(aIdentifier.mSyncHandle);
+}
+
+KnowsCompositor::KnowsCompositor()
+ : mSerial(++sSerialCounter)
+{}
+
+KnowsCompositor::~KnowsCompositor()
+{}
+
+ShadowLayerForwarder::ShadowLayerForwarder(ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager)
+ , mMessageLoop(MessageLoop::current())
+ , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
+ , mIsFirstPaint(false)
+ , mWindowOverlayChanged(false)
+ , mPaintSyncId(0)
+{
+ mTxn = new Transaction();
+ mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(1000, "CompositableForwarder");
+}
+
+template<typename T>
+struct ReleaseOnMainThreadTask : public Runnable
+{
+ UniquePtr<T> mObj;
+
+ explicit ReleaseOnMainThreadTask(UniquePtr<T>& aObj)
+ : mObj(Move(aObj))
+ {}
+
+ NS_IMETHOD Run() override {
+ mObj = nullptr;
+ return NS_OK;
+ }
+};
+
+ShadowLayerForwarder::~ShadowLayerForwarder()
+{
+ MOZ_ASSERT(mTxn->Finished(), "unfinished transaction?");
+ delete mTxn;
+ if (mShadowManager) {
+ mShadowManager->SetForwarder(nullptr);
+ mShadowManager->Destroy();
+ }
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(
+ new ReleaseOnMainThreadTask<ActiveResourceTracker>(mActiveResourceTracker));
+ }
+}
+
+void
+ShadowLayerForwarder::BeginTransaction(const gfx::IntRect& aTargetBounds,
+ ScreenRotation aRotation,
+ dom::ScreenOrientationInternal aOrientation)
+{
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin(aTargetBounds, aRotation, aOrientation);
+}
+
+static PLayerChild*
+Shadow(ShadowableLayer* aLayer)
+{
+ return aLayer->GetShadow();
+}
+
+template<typename OpCreateT>
+static void
+CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer)
+{
+ aTxn->AddEdit(OpCreateT(nullptr, Shadow(aLayer)));
+}
+
+void
+ShadowLayerForwarder::CreatedPaintedLayer(ShadowableLayer* aThebes)
+{
+ CreatedLayer<OpCreatePaintedLayer>(mTxn, aThebes);
+}
+void
+ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer)
+{
+ CreatedLayer<OpCreateContainerLayer>(mTxn, aContainer);
+}
+void
+ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage)
+{
+ CreatedLayer<OpCreateImageLayer>(mTxn, aImage);
+}
+void
+ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor)
+{
+ CreatedLayer<OpCreateColorLayer>(mTxn, aColor);
+}
+void
+ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
+{
+ CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
+}
+void
+ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef)
+{
+ CreatedLayer<OpCreateRefLayer>(mTxn, aRef);
+}
+
+void
+ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant)
+{
+mTxn->AddMutant(aMutant);
+}
+
+void
+ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot)
+{
+ mTxn->AddEdit(OpSetRoot(nullptr, Shadow(aRoot)));
+}
+void
+ShadowLayerForwarder::InsertAfter(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter)
+{
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling() ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() : nullptr;
+ }
+
+ if (aAfter) {
+ mTxn->AddEdit(OpInsertAfter(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild),
+ nullptr, Shadow(aAfter)));
+ } else {
+ mTxn->AddEdit(OpPrependChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+ }
+}
+void
+ShadowLayerForwarder::RemoveChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild)
+{
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRemoveChild container=%p child=%p\n",
+ aContainer->AsLayer(), aChild->AsLayer()));
+
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ mTxn->AddEdit(OpRemoveChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+}
+void
+ShadowLayerForwarder::RepositionChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter)
+{
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling() ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() : nullptr;
+ }
+
+ if (aAfter) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRepositionChild container=%p child=%p after=%p",
+ aContainer->AsLayer(), aChild->AsLayer(), aAfter->AsLayer()));
+ mTxn->AddEdit(OpRepositionChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild),
+ nullptr, Shadow(aAfter)));
+ } else {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRaiseToTopChild container=%p child=%p",
+ aContainer->AsLayer(), aChild->AsLayer()));
+ mTxn->AddEdit(OpRaiseToTopChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+ }
+}
+
+
+#ifdef DEBUG
+void
+ShadowLayerForwarder::CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const
+{
+ if (!aDescriptor) {
+ return;
+ }
+
+ if (aDescriptor->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aDescriptor->get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::TShmem) {
+ const Shmem& shmem = aDescriptor->get_SurfaceDescriptorBuffer().data().get_Shmem();
+ shmem.AssertInvariants();
+ MOZ_ASSERT(mShadowManager &&
+ mShadowManager->IsTrackingSharedMemory(shmem.mSegment));
+ }
+}
+#endif
+
+void
+ShadowLayerForwarder::UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapPaint(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTiledLayerBuffer(aTileLayerDescriptor)));
+}
+
+void
+ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddPaint(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
+}
+
+void
+ShadowLayerForwarder::UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ AutoTArray<TimedTexture,4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ ReadLockDescriptor readLock;
+ t.mTextureClient->SerializeReadLock(readLock);
+ textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
+ readLock,
+ t.mTimeStamp, t.mPictureRect,
+ t.mFrameID, t.mProducerID));
+ if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
+ && t.mTextureClient->HasIntermediateBuffer()) {
+
+ // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
+ // race with updates on the main thread. In this case we want the transaction
+ // to be synchronous.
+ mTxn->MarkSyncTransaction();
+ }
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTexture(textures)));
+}
+
+void
+ShadowLayerForwarder::UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ MOZ_ASSERT(aTextureOnWhite);
+ MOZ_ASSERT(aTextureOnBlack);
+ MOZ_ASSERT(aCompositable->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
+ MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+
+ ReadLockDescriptor readLockW;
+ ReadLockDescriptor readLockB;
+ aTextureOnBlack->SerializeReadLock(readLockB);
+ aTextureOnWhite->SerializeReadLock(readLockW);
+
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
+
+ mTxn->AddEdit(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpUseComponentAlphaTextures(
+ nullptr, aTextureOnBlack->GetIPDLActor(),
+ nullptr, aTextureOnWhite->GetIPDLActor(),
+ readLockB, readLockW)
+ )
+ );
+}
+
+static bool
+AddOpDestroy(Transaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+ if (!aTxn->Opened()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+ if (synchronously) {
+ aTxn->MarkSyncTransaction();
+ }
+
+ return true;
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+ return AddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+ return AddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
+void
+ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
+ // We don't have an actor anymore, don't try to use it!
+ return;
+ }
+
+ mTxn->AddEdit(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+ if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTxn->MarkSyncTransaction();
+ }
+}
+
+bool
+ShadowLayerForwarder::InWorkerThread()
+{
+ return MessageLoop::current() && (GetTextureForwarder()->GetMessageLoop()->id() == MessageLoop::current()->id());
+}
+
+void
+ShadowLayerForwarder::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations)
+{
+ // Cache new plugin widget configs here until we call update, at which
+ // point this data will get shipped over to chrome.
+ mPluginWindowData.Clear();
+ for (uint32_t idx = 0; idx < aConfigurations.Length(); idx++) {
+ const nsIWidget::Configuration& configuration = aConfigurations[idx];
+ mPluginWindowData.AppendElement(PluginWindowData(configuration.mWindowID,
+ configuration.mClipRegion,
+ configuration.mBounds,
+ configuration.mVisible));
+ }
+}
+
+void
+ShadowLayerForwarder::SendPaintTime(uint64_t aId, TimeDuration aPaintTime)
+{
+ if (!IPCOpen() ||
+ !mShadowManager->SendPaintTime(aId, aPaintTime)) {
+ NS_WARNING("Could not send paint times over IPC");
+ }
+}
+
+bool
+ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
+ const nsIntRegion& aRegionToClear,
+ uint64_t aId,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ bool* aSent)
+{
+ *aSent = false;
+
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ if (!IPCOpen()) {
+ return false;
+ }
+
+ GetCompositorBridgeChild()->WillEndTransaction();
+
+ MOZ_ASSERT(aId);
+
+ PROFILER_LABEL("ShadowLayerForwarder", "EndTransaction",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ RenderTraceScope rendertrace("Foward Transaction", "000091");
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ DiagnosticTypes diagnostics = gfxPlatform::GetPlatform()->GetLayerDiagnosticTypes();
+ if (mDiagnosticTypes != diagnostics) {
+ mDiagnosticTypes = diagnostics;
+ mTxn->AddEdit(OpSetDiagnosticTypes(diagnostics));
+ }
+ if (mWindowOverlayChanged) {
+ mTxn->AddEdit(OpWindowOverlayChanged());
+ }
+
+ AutoTxnEnd _(mTxn);
+
+ if (mTxn->Empty() && !mTxn->RotationChanged()) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] 0-length cset (?) and no rotation event, skipping Update()"));
+ return true;
+ }
+
+ if (!mTxn->mPaints.empty()) {
+ // With some platforms, telling the drawing backend that there will be no more
+ // drawing for this frame helps with preventing command queues from spanning
+ // across multiple frames.
+ gfxPlatform::GetPlatform()->FlushContentDrawing();
+ }
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] destroying buffers..."));
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] building transaction..."));
+
+ // We purposely add attribute-change ops to the final changeset
+ // before we add paint ops. This allows layers to record the
+ // attribute changes before new pixels arrive, which can be useful
+ // for setting up back/front buffers.
+ RenderTraceScope rendertrace2("Foward Transaction", "000092");
+ for (ShadowableLayerSet::Iterator it(&mTxn->mMutants);
+ !it.Done(); it.Next()) {
+ ShadowableLayer* shadow = it.Get()->GetKey();
+
+ if (!shadow->HasShadow()) {
+ continue;
+ }
+ Layer* mutant = shadow->AsLayer();
+ MOZ_ASSERT(!!mutant, "unshadowable layer?");
+
+ LayerAttributes attrs;
+ CommonLayerAttributes& common = attrs.common();
+ common.layerBounds() = mutant->GetLayerBounds();
+ common.visibleRegion() = mutant->GetVisibleRegion();
+ common.eventRegions() = mutant->GetEventRegions();
+ common.postXScale() = mutant->GetPostXScale();
+ common.postYScale() = mutant->GetPostYScale();
+ common.transform() = mutant->GetBaseTransform();
+ common.transformIsPerspective() = mutant->GetTransformIsPerspective();
+ common.contentFlags() = mutant->GetContentFlags();
+ common.opacity() = mutant->GetOpacity();
+ common.useClipRect() = !!mutant->GetClipRect();
+ common.clipRect() = (common.useClipRect() ?
+ *mutant->GetClipRect() : ParentLayerIntRect());
+ common.scrolledClip() = mutant->GetScrolledClip();
+ common.isFixedPosition() = mutant->GetIsFixedPosition();
+ if (mutant->GetIsFixedPosition()) {
+ common.fixedPositionScrollContainerId() = mutant->GetFixedPositionScrollContainerId();
+ common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor();
+ common.fixedPositionSides() = mutant->GetFixedPositionSides();
+ }
+ common.isStickyPosition() = mutant->GetIsStickyPosition();
+ if (mutant->GetIsStickyPosition()) {
+ common.stickyScrollContainerId() = mutant->GetStickyScrollContainerId();
+ common.stickyScrollRangeOuter() = mutant->GetStickyScrollRangeOuter();
+ common.stickyScrollRangeInner() = mutant->GetStickyScrollRangeInner();
+ } else {
+#ifdef MOZ_VALGRIND
+ // Initialize these so that Valgrind doesn't complain when we send them
+ // to another process.
+ common.stickyScrollContainerId() = 0;
+ common.stickyScrollRangeOuter() = LayerRect();
+ common.stickyScrollRangeInner() = LayerRect();
+#endif
+ }
+ common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId();
+ common.scrollbarDirection() = mutant->GetScrollbarDirection();
+ common.scrollbarThumbRatio() = mutant->GetScrollbarThumbRatio();
+ common.isScrollbarContainer() = mutant->IsScrollbarContainer();
+ common.mixBlendMode() = (int8_t)mutant->GetMixBlendMode();
+ common.forceIsolatedGroup() = mutant->GetForceIsolatedGroup();
+ if (Layer* maskLayer = mutant->GetMaskLayer()) {
+ common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
+ } else {
+ common.maskLayerChild() = nullptr;
+ }
+ common.maskLayerParent() = nullptr;
+ common.animations() = mutant->GetAnimations();
+ common.invalidRegion() = mutant->GetInvalidRegion().GetRegion();
+ common.scrollMetadata() = mutant->GetAllScrollMetadata();
+ for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) {
+ auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer());
+ common.ancestorMaskLayersChild().AppendElement(layer);
+ }
+ nsCString log;
+ mutant->GetDisplayListLog(log);
+ common.displayListLog() = log;
+
+ attrs.specific() = null_t();
+ mutant->FillSpecificAttributes(attrs.specific());
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
+
+ mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
+ }
+
+ AutoTArray<Edit, 10> cset;
+ size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size();
+ if (nCsets == 0 && !mTxn->RotationChanged()) {
+ return true;
+ }
+
+ cset.SetCapacity(nCsets);
+ if (!mTxn->mCset.empty()) {
+ cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size());
+ }
+ // Paints after non-paint ops, including attribute changes. See
+ // above.
+ if (!mTxn->mPaints.empty()) {
+ cset.AppendElements(&mTxn->mPaints.front(), mTxn->mPaints.size());
+ }
+
+ mWindowOverlayChanged = false;
+
+ TargetConfig targetConfig(mTxn->mTargetBounds,
+ mTxn->mTargetRotation,
+ mTxn->mTargetOrientation,
+ aRegionToClear);
+
+ if (!GetTextureForwarder()->IsSameProcess()) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
+ PlatformSyncBeforeUpdate();
+ }
+
+ profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END);
+ if (mTxn->mSwapRequired) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
+ RenderTraceScope rendertrace3("Forward Transaction", "000093");
+ if (!mShadowManager->SendUpdate(cset, mTxn->mDestroyedActors,
+ GetFwdTransactionId(),
+ aId, targetConfig, mPluginWindowData,
+ mIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction,
+ aTransactionStart, mPaintSyncId, aReplies)) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+ return false;
+ }
+ } else {
+ // If we don't require a swap we can call SendUpdateNoSwap which
+ // assumes that aReplies is empty (DEBUG assertion)
+ MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
+ RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
+ if (!mShadowManager->SendUpdateNoSwap(cset, mTxn->mDestroyedActors,
+ GetFwdTransactionId(),
+ aId, targetConfig, mPluginWindowData,
+ mIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction,
+ aTransactionStart, mPaintSyncId)) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+ return false;
+ }
+ }
+
+ *aSent = true;
+ mIsFirstPaint = false;
+ mPaintSyncId = 0;
+ MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
+ return true;
+}
+
+void
+ShadowLayerForwarder::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+bool
+ShadowLayerForwarder::IPCOpen() const
+{
+ return HasShadowManager() && mShadowManager->IPCOpen();
+}
+
+/**
+ * We bail out when we have no shadow manager. That can happen when the
+ * layer manager is created by the preallocated process.
+ * See bug 914843 for details.
+ */
+PLayerChild*
+ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ ShadowLayerChild* child = new ShadowLayerChild();
+ if (!mShadowManager->SendPLayerConstructor(child)) {
+ return nullptr;
+ }
+
+ child->SetShadowableLayer(aLayer);
+ return child;
+}
+
+#if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+void
+ShadowLayerForwarder::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer)
+{
+#ifdef GFX_COMPOSITOR_LOGGING
+ printf("ShadowLayerForwarder::Connect(Compositable)\n");
+#endif
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(mShadowManager);
+ if (!IPCOpen()) {
+ return;
+ }
+ PCompositableChild* actor =
+ mShadowManager->SendPCompositableConstructor(aCompositable->GetTextureInfo());
+ if (!actor) {
+ return;
+ }
+ aCompositable->InitIPDLActor(actor);
+}
+
+void ShadowLayerForwarder::Attach(CompositableClient* aCompositable,
+ ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aCompositable);
+ mTxn->AddEdit(OpAttachCompositable(nullptr, Shadow(aLayer),
+ nullptr, aCompositable->GetIPDLActor()));
+}
+
+void ShadowLayerForwarder::AttachAsyncCompositable(uint64_t aCompositableID,
+ ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aCompositableID != 0); // zero is always an invalid compositable id.
+ mTxn->AddEdit(OpAttachAsyncCompositable(nullptr, Shadow(aLayer),
+ aCompositableID));
+}
+
+void ShadowLayerForwarder::SetShadowManager(PLayerTransactionChild* aShadowManager)
+{
+ mShadowManager = static_cast<LayerTransactionChild*>(aShadowManager);
+ mShadowManager->SetForwarder(this);
+}
+
+void ShadowLayerForwarder::StopReceiveAsyncParentMessge()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SetForwarder(nullptr);
+}
+
+void ShadowLayerForwarder::ClearCachedResources()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendClearCachedResources();
+}
+
+void ShadowLayerForwarder::Composite()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendForceComposite();
+}
+
+bool
+IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
+{
+ return aSurface.type() != SurfaceDescriptor::T__None &&
+ aSurface.type() != SurfaceDescriptor::Tnull_t;
+}
+
+uint8_t*
+GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+ MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor));
+ MOZ_RELEASE_ASSERT(aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer, "GFX: surface descriptor is not the right type.");
+
+ auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data();
+ if (memOrShmem.type() == MemoryOrShmem::TShmem) {
+ return memOrShmem.get_Shmem().get<uint8_t>();
+ } else {
+ return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+ if (aDescriptor.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ return nullptr;
+ }
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(),
+ rgb.format());
+}
+
+already_AddRefed<gfx::DrawTarget>
+GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend)
+{
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
+ data, rgb.size(),
+ stride, rgb.format());
+}
+
+void
+DestroySurfaceDescriptor(IShmemAllocator* aAllocator, SurfaceDescriptor* aSurface)
+{
+ MOZ_ASSERT(aSurface);
+
+ SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer();
+ switch (desc.data().type()) {
+ case MemoryOrShmem::TShmem: {
+ aAllocator->DeallocShmem(desc.data().get_Shmem());
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t();
+ GfxMemoryImageReporter::WillFree(ptr);
+ delete [] ptr;
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("surface type not implemented!");
+ }
+ *aSurface = SurfaceDescriptor();
+}
+
+bool
+ShadowLayerForwarder::AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer)
+{
+ if (!IPCOpen()) {
+ return false;
+ }
+ return AllocSurfaceDescriptorWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS, aBuffer);
+}
+
+bool
+ShadowLayerForwarder::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer)
+{
+ if (!IPCOpen()) {
+ return false;
+ }
+ gfx::SurfaceFormat format =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent);
+ size_t size = ImageDataSerializer::ComputeRGBBufferSize(aSize, format);
+ if (!size) {
+ return false;
+ }
+
+ MemoryOrShmem bufferDesc;
+ if (GetTextureForwarder()->IsSameProcess()) {
+ uint8_t* data = new (std::nothrow) uint8_t[size];
+ if (!data) {
+ return false;
+ }
+ GfxMemoryImageReporter::DidAlloc(data);
+ memset(data, 0, size);
+ bufferDesc = reinterpret_cast<uintptr_t>(data);
+ } else {
+
+ mozilla::ipc::Shmem shmem;
+ if (!GetTextureForwarder()->AllocUnsafeShmem(size, OptimalShmemType(), &shmem)) {
+ return false;
+ }
+
+ bufferDesc = shmem;
+ }
+
+ // Use an intermediate buffer by default. Skipping the intermediate buffer is
+ // only possible in certain configurations so let's keep it simple here for now.
+ const bool hasIntermediateBuffer = true;
+ *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+ bufferDesc);
+
+ return true;
+}
+
+/* static */ bool
+ShadowLayerForwarder::IsShmem(SurfaceDescriptor* aSurface)
+{
+ return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
+ && (aSurface->get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::TShmem);
+}
+
+void
+ShadowLayerForwarder::DestroySurfaceDescriptor(SurfaceDescriptor* aSurface)
+{
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(IPCOpen());
+ if (!IPCOpen() || !aSurface) {
+ return;
+ }
+
+ ::mozilla::layers::DestroySurfaceDescriptor(GetTextureForwarder(), aSurface);
+}
+
+void
+ShadowLayerForwarder::UpdateFwdTransactionId()
+{
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge) {
+ compositorBridge->UpdateFwdTransactionId();
+ }
+}
+
+uint64_t
+ShadowLayerForwarder::GetFwdTransactionId()
+{
+ auto compositorBridge = GetCompositorBridgeChild();
+ MOZ_DIAGNOSTIC_ASSERT(compositorBridge);
+ return compositorBridge ? compositorBridge->GetFwdTransactionId() : 0;
+}
+
+CompositorBridgeChild*
+ShadowLayerForwarder::GetCompositorBridgeChild()
+{
+ if (mCompositorBridgeChild) {
+ return mCompositorBridgeChild;
+ }
+ if (!mShadowManager) {
+ return nullptr;
+ }
+ mCompositorBridgeChild = static_cast<CompositorBridgeChild*>(mShadowManager->Manager());
+ return mCompositorBridgeChild;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/ShadowLayers.h b/system/graphics/layers/ipc/ShadowLayers.h
new file mode 100644
index 000000000..8b207eb1a
--- /dev/null
+++ b/system/graphics/layers/ipc/ShadowLayers.h
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayers_h
+#define mozilla_layers_ShadowLayers_h 1
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nsIWidget.h"
+#include <vector>
+#include "nsExpirationTracker.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayerManager;
+class CompositorBridgeChild;
+class EditReply;
+class FixedSizeSmallShmemSectionAllocator;
+class ImageContainer;
+class Layer;
+class PLayerChild;
+class PLayerTransactionChild;
+class LayerTransactionChild;
+class ShadowableLayer;
+class SurfaceDescriptor;
+class TextureClient;
+class ThebesBuffer;
+class ThebesBufferData;
+class Transaction;
+
+/**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource
+{
+public:
+ virtual void NotifyInactive() = 0;
+ nsExpirationState* GetExpirationState() { return &mExpirationState; }
+ bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+private:
+ nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3>
+{
+public:
+ ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName)
+ : nsExpirationTracker(aExpirationCycle, aName)
+ {}
+
+ virtual void NotifyExpired(ActiveResource* aResource) override
+ {
+ RemoveObject(aResource);
+ aResource->NotifyInactive();
+ }
+};
+
+/**
+ * We want to share layer trees across thread contexts and address
+ * spaces for several reasons; chief among them
+ *
+ * - a parent process can paint a child process's layer tree while
+ * the child process is blocked, say on content script. This is
+ * important on mobile devices where UI responsiveness is key.
+ *
+ * - a dedicated "compositor" process can asynchronously (wrt the
+ * browser process) composite and animate layer trees, allowing a
+ * form of pipeline parallelism between compositor/browser/content
+ *
+ * - a dedicated "compositor" process can take all responsibility for
+ * accessing the GPU, which is desirable on systems with
+ * buggy/leaky drivers because the compositor process can die while
+ * browser and content live on (and failover mechanisms can be
+ * installed to quickly bring up a replacement compositor)
+ *
+ * The Layers model has a crisply defined API, which makes it easy to
+ * safely "share" layer trees. The ShadowLayers API extends Layers to
+ * allow a remote, parent process to access a child process's layer
+ * tree.
+ *
+ * ShadowLayerForwarder publishes a child context's layer tree to a
+ * parent context. This comprises recording layer-tree modifications
+ * into atomic transactions and pushing them over IPC.
+ *
+ * LayerManagerComposite grafts layer subtrees published by child-context
+ * ShadowLayerForwarder(s) into a parent-context layer tree.
+ *
+ * (Advanced note: because our process tree may have a height >2, a
+ * non-leaf subprocess may both receive updates from child processes
+ * and publish them to parent processes. Put another way,
+ * LayerManagers may be both LayerManagerComposites and
+ * ShadowLayerForwarders.)
+ *
+ * There are only shadow types for layers that have different shadow
+ * vs. not-shadow behavior. ColorLayers and ContainerLayers behave
+ * the same way in both regimes (so far).
+ *
+ *
+ * The mecanism to shadow the layer tree on the compositor through IPC works as
+ * follows:
+ * The layer tree is managed on the content thread, and shadowed in the compositor
+ * thread. The shadow layer tree is only kept in sync with whatever happens in
+ * the content thread. To do this we use IPDL protocols. IPDL is a domain
+ * specific language that describes how two processes or thread should
+ * communicate. C++ code is generated from .ipdl files to implement the message
+ * passing, synchronization and serialization logic. To use the generated code
+ * we implement classes that inherit the generated IPDL actor. the ipdl actors
+ * of a protocol PX are PXChild or PXParent (the generated class), and we
+ * conventionally implement XChild and XParent. The Parent side of the protocol
+ * is the one that lives on the compositor thread. Think of IPDL actors as
+ * endpoints of communication. they are useful to send messages and also to
+ * dispatch the message to the right actor on the other side. One nice property
+ * of an IPDL actor is that when an actor, say PXChild is sent in a message, the
+ * PXParent comes out in the other side. we use this property a lot to dispatch
+ * messages to the right layers and compositable, each of which have their own
+ * ipdl actor on both side.
+ *
+ * Most of the synchronization logic happens in layer transactions and
+ * compositable transactions.
+ * A transaction is a set of changes to the layers and/or the compositables
+ * that are sent and applied together to the compositor thread to keep the
+ * LayerComposite in a coherent state.
+ * Layer transactions maintain the shape of the shadow layer tree, and
+ * synchronize the texture data held by compositables. Layer transactions
+ * are always between the content thread and the compositor thread.
+ * Compositable transactions are subset of a layer transaction with which only
+ * compositables and textures can be manipulated, and does not always originate
+ * from the content thread. (See CompositableForwarder.h and ImageBridgeChild.h)
+ */
+
+class ShadowLayerForwarder final : public LayersIPCActor
+ , public CompositableForwarder
+ , public LegacySurfaceDescriptorAllocator
+{
+ friend class ClientLayerManager;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ShadowLayerForwarder, override);
+
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ /**
+ * Adds an edit in the layers transaction in order to attach
+ * the corresponding compositable and layer on the compositor side.
+ * Connect must have been called on aCompositable beforehand.
+ */
+ void Attach(CompositableClient* aCompositable,
+ ShadowableLayer* aLayer);
+
+ /**
+ * Adds an edit in the transaction in order to attach a Compositable that
+ * is not managed by this ShadowLayerForwarder (for example, by ImageBridge
+ * in the case of async-video).
+ * Since the compositable is not managed by this forwarder, we can't use
+ * the compositable or it's IPDL actor here, so we use an ID instead, that
+ * is matched on the compositor side.
+ */
+ void AttachAsyncCompositable(uint64_t aCompositableID,
+ ShadowableLayer* aLayer);
+
+ /**
+ * Begin recording a transaction to be forwarded atomically to a
+ * LayerManagerComposite.
+ */
+ void BeginTransaction(const gfx::IntRect& aTargetBounds,
+ ScreenRotation aRotation,
+ mozilla::dom::ScreenOrientationInternal aOrientation);
+
+ /**
+ * The following methods may only be called after BeginTransaction()
+ * but before EndTransaction(). They mirror the LayerManager
+ * interface in Layers.h.
+ */
+
+ /**
+ * Notify the shadow manager that a new, "real" layer has been
+ * created, and a corresponding shadow layer should be created in
+ * the compositing process.
+ */
+ void CreatedPaintedLayer(ShadowableLayer* aThebes);
+ void CreatedContainerLayer(ShadowableLayer* aContainer);
+ void CreatedImageLayer(ShadowableLayer* aImage);
+ void CreatedColorLayer(ShadowableLayer* aColor);
+ void CreatedCanvasLayer(ShadowableLayer* aCanvas);
+ void CreatedRefLayer(ShadowableLayer* aRef);
+
+ /**
+ * At least one attribute of |aMutant| has changed, and |aMutant|
+ * needs to sync to its shadow layer. This initial implementation
+ * forwards all attributes when any is mutated.
+ */
+ void Mutated(ShadowableLayer* aMutant);
+
+ void SetRoot(ShadowableLayer* aRoot);
+ /**
+ * Insert |aChild| after |aAfter| in |aContainer|. |aAfter| can be
+ * nullptr to indicated that |aChild| should be appended to the end of
+ * |aContainer|'s child list.
+ */
+ void InsertAfter(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+ void RemoveChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild);
+ void RepositionChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+
+ /**
+ * Set aMaskLayer as the mask on aLayer.
+ * Note that only image layers are properly supported
+ * LayerTransactionParent::UpdateMask and accompanying ipdl
+ * will need changing to update properties for other kinds
+ * of mask layer.
+ */
+ void SetMask(ShadowableLayer* aLayer,
+ ShadowableLayer* aMaskLayer);
+
+ /**
+ * See CompositableForwarder::UseTiledLayerBuffer
+ */
+ void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
+
+ bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+ bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by aLayer
+ * and aIdentifier has been updated to aThebesBuffer.
+ */
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override;
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ /**
+ * Used for debugging to tell the compositor how long this frame took to paint.
+ */
+ void SendPaintTime(uint64_t aId, TimeDuration aPaintTime);
+
+ /**
+ * End the current transaction and forward it to LayerManagerComposite.
+ * |aReplies| are directions from the LayerManagerComposite to the
+ * caller of EndTransaction().
+ */
+ bool EndTransaction(InfallibleTArray<EditReply>* aReplies,
+ const nsIntRegion& aRegionToClear,
+ uint64_t aId,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ bool* aSent);
+
+ /**
+ * Set an actor through which layer updates will be pushed.
+ */
+ void SetShadowManager(PLayerTransactionChild* aShadowManager);
+
+ /**
+ * Layout calls here to cache current plugin widget configuration
+ * data. We ship this across with the rest of the layer updates when
+ * we update. Chrome handles applying these changes.
+ */
+ void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations);
+
+ void StopReceiveAsyncParentMessge();
+
+ void ClearCachedResources();
+
+ void Composite();
+
+ /**
+ * True if this is forwarding to a LayerManagerComposite.
+ */
+ bool HasShadowManager() const { return !!mShadowManager; }
+ LayerTransactionChild* GetShadowManager() const { return mShadowManager.get(); }
+
+ virtual void WindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+ /**
+ * The following Alloc/Open/Destroy interfaces abstract over the
+ * details of working with surfaces that are shared across
+ * processes. They provide the glue between C++ Layers and the
+ * LayerComposite IPC system.
+ *
+ * The basic lifecycle is
+ *
+ * - a Layer needs a buffer. Its ShadowableLayer subclass calls
+ * AllocBuffer(), then calls one of the Created*Buffer() methods
+ * above to transfer the (temporary) front buffer to its
+ * LayerComposite in the other process. The Layer needs a
+ * gfxASurface to paint, so the ShadowableLayer uses
+ * OpenDescriptor(backBuffer) to get that surface, and hands it
+ * out to the Layer.
+ *
+ * - a Layer has painted new pixels. Its ShadowableLayer calls one
+ * of the Painted*Buffer() methods above with the back buffer
+ * descriptor. This notification is forwarded to the LayerComposite,
+ * which uses OpenDescriptor() to access the newly-painted pixels.
+ * The LayerComposite then updates its front buffer in a Layer- and
+ * platform-dependent way, and sends a surface descriptor back to
+ * the ShadowableLayer that becomes its new back back buffer.
+ *
+ * - a Layer wants to destroy its buffers. Its ShadowableLayer
+ * calls Destroyed*Buffer(), which gives up control of the back
+ * buffer descriptor. The actual back buffer surface is then
+ * destroyed using DestroySharedSurface() just before notifying
+ * the parent process. When the parent process is notified, the
+ * LayerComposite also calls DestroySharedSurface() on its front
+ * buffer, and the double-buffer pair is gone.
+ */
+
+ virtual bool IPCOpen() const override;
+
+ /**
+ * Construct a shadow of |aLayer| on the "other side", at the
+ * LayerManagerComposite.
+ */
+ PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer);
+
+ /**
+ * Flag the next paint as the first for a document.
+ */
+ void SetIsFirstPaint() { mIsFirstPaint = true; }
+
+ void SetPaintSyncId(int32_t aSyncId) { mPaintSyncId = aSyncId; }
+
+ void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
+ static void PlatformSyncBeforeUpdate();
+
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) override;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) override;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override;
+
+ virtual void UpdateFwdTransactionId() override;
+ virtual uint64_t GetFwdTransactionId() override;
+
+ bool InForwarderThread() override {
+ return NS_IsMainThread();
+ }
+
+ // Returns true if aSurface wraps a Shmem.
+ static bool IsShmem(SurfaceDescriptor* aSurface);
+
+ TextureForwarder* GetTextureForwarder() override { return GetCompositorBridgeChild(); }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); }
+protected:
+ virtual ~ShadowLayerForwarder();
+
+ explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager);
+
+#ifdef DEBUG
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const;
+#else
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {}
+#endif
+
+ bool InWorkerThread();
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ RefPtr<LayerTransactionChild> mShadowManager;
+ RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+
+private:
+
+ ClientLayerManager* mClientLayerManager;
+ Transaction* mTxn;
+ MessageLoop* mMessageLoop;
+ DiagnosticTypes mDiagnosticTypes;
+ bool mIsFirstPaint;
+ bool mWindowOverlayChanged;
+ int32_t mPaintSyncId;
+ InfallibleTArray<PluginWindowData> mPluginWindowData;
+ UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
+};
+
+class CompositableClient;
+
+/**
+ * A ShadowableLayer is a Layer can be shared with a parent context
+ * through a ShadowLayerForwarder. A ShadowableLayer maps to a
+ * Shadow*Layer in a parent context.
+ *
+ * Note that ShadowLayers can themselves be ShadowableLayers.
+ */
+class ShadowableLayer
+{
+public:
+ virtual ~ShadowableLayer() {}
+
+ virtual Layer* AsLayer() = 0;
+
+ /**
+ * True if this layer has a shadow in a parent process.
+ */
+ bool HasShadow() { return !!mShadow; }
+
+ /**
+ * Return the IPC handle to a Shadow*Layer referring to this if one
+ * exists, nullptr if not.
+ */
+ PLayerChild* GetShadow() { return mShadow; }
+
+ virtual CompositableClient* GetCompositableClient() { return nullptr; }
+protected:
+ ShadowableLayer() : mShadow(nullptr) {}
+
+ PLayerChild* mShadow;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayers_h
diff --git a/system/graphics/layers/ipc/SharedPlanarYCbCrImage.cpp b/system/graphics/layers/ipc/SharedPlanarYCbCrImage.cpp
new file mode 100644
index 000000000..6a1bbcac0
--- /dev/null
+++ b/system/graphics/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedPlanarYCbCrImage.h"
+#include <stddef.h> // for size_t
+#include <stdio.h> // for printf
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Types.h" // for SurfaceFormat::SurfaceFormat::YUV
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for Image::AddRef
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+
+SharedPlanarYCbCrImage::SharedPlanarYCbCrImage(ImageClient* aCompositable)
+: mCompositable(aCompositable)
+{
+ MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
+}
+
+SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() {
+ MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
+
+ if (mCompositable->GetAsyncID() != 0 &&
+ !InImageBridgeChildThread()) {
+ if (mTextureClient) {
+ ADDREF_MANUALLY(mTextureClient);
+ ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
+ mTextureClient = nullptr;
+ }
+ }
+}
+
+size_t
+SharedPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // NB: Explicitly skipping mTextureClient, the memory is already reported
+ // at time of allocation in GfxMemoryImageReporter.
+ // Not owned:
+ // - mCompositable
+ return 0;
+}
+
+TextureClient*
+SharedPlanarYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient.get();
+}
+
+uint8_t*
+SharedPlanarYCbCrImage::GetBuffer()
+{
+ // This should never be used
+ MOZ_ASSERT(false);
+ return nullptr;
+}
+
+already_AddRefed<gfx::SourceSurface>
+SharedPlanarYCbCrImage::GetAsSourceSurface()
+{
+ if (!IsValid()) {
+ NS_WARNING("Can't get as surface");
+ return nullptr;
+ }
+ return PlanarYCbCrImage::GetAsSourceSurface();
+}
+
+bool
+SharedPlanarYCbCrImage::CopyData(const PlanarYCbCrData& aData)
+{
+ // If mTextureClient has not already been allocated (through Allocate(aData))
+ // allocate it. This code path is slower than the one used when Allocate has
+ // been called since it will trigger a full copy.
+ PlanarYCbCrData data = aData;
+ if (!mTextureClient && !Allocate(data)) {
+ return false;
+ }
+
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ MOZ_ASSERT(false, "Failed to lock the texture.");
+ return false;
+ }
+
+ if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
+ MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
+ return false;
+ }
+ mTextureClient->MarkImmutable();
+ return true;
+}
+
+// needs to be overriden because the parent class sets mBuffer which we
+// do not want to happen.
+uint8_t*
+SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
+{
+ MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
+ size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize);
+ if (!size) {
+ return nullptr;
+ }
+
+ // XXX Add YUVColorSpace handling. Use YUVColorSpace::BT601 for now.
+ mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(),
+ size,
+ YUVColorSpace::BT601,
+ mCompositable->GetTextureFlags());
+
+ // get new buffer _without_ setting mBuffer.
+ if (!mTextureClient) {
+ return nullptr;
+ }
+
+ // update buffer size
+ mBufferSize = size;
+
+ MappedYCbCrTextureData mapped;
+ if (mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ // The caller expects a pointer to the beginning of the writable part of the
+ // buffer which is where the y channel starts by default.
+ return mapped.y.data;
+ } else {
+ MOZ_CRASH("GFX: Cannot borrow mapped YCbCr data");
+ }
+}
+
+bool
+SharedPlanarYCbCrImage::AdoptData(const Data &aData)
+{
+ // AdoptData is used to update YUV plane offsets without (re)allocating
+ // memory previously allocated with AllocateAndGetNewBuffer().
+
+ MOZ_ASSERT(mTextureClient, "This Image should have already allocated data");
+ if (!mTextureClient) {
+ return false;
+ }
+ mData = aData;
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+
+ uint8_t *base = GetBuffer();
+ uint32_t yOffset = aData.mYChannel - base;
+ uint32_t cbOffset = aData.mCbChannel - base;
+ uint32_t crOffset = aData.mCrChannel - base;
+
+ auto fwd = mCompositable->GetForwarder();
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ fwd->GetCompositorBackendType());
+
+ static_cast<BufferTextureData*>(mTextureClient->GetInternalData())->SetDesciptor(
+ YCbCrDescriptor(aData.mYSize, aData.mCbCrSize, yOffset, cbOffset, crOffset,
+ aData.mStereoMode, aData.mYUVColorSpace, hasIntermediateBuffer)
+ );
+
+ return true;
+}
+
+bool
+SharedPlanarYCbCrImage::IsValid() {
+ return mTextureClient && mTextureClient->IsValid();
+}
+
+bool
+SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData)
+{
+ MOZ_ASSERT(!mTextureClient,
+ "This image already has allocated data");
+ static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
+
+ if (!mCompositable->HasTextureClientRecycler()) {
+ // Initialize TextureClientRecycler
+ mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
+ }
+
+ {
+ YCbCrTextureClientAllocationHelper helper(aData, mCompositable->GetTextureFlags());
+ mTextureClient = mCompositable->GetTextureClientRecycler()->CreateOrRecycle(helper);
+ }
+
+ if (!mTextureClient) {
+ NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
+ return false;
+ }
+
+ MappedYCbCrTextureData mapped;
+ // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
+ // pointers out of the TextureClient and keeps them around, which works only
+ // because the underlyin BufferTextureData is always mapped in memory even outside
+ // of the lock/unlock interval. That's sad and new code should follow this example.
+ if (!mTextureClient->Lock(OpenMode::OPEN_READ) || !mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ MOZ_CRASH("GFX: Cannot lock or borrow mapped YCbCr");
+ }
+
+ aData.mYChannel = mapped.y.data;
+ aData.mCbChannel = mapped.cb.data;
+ aData.mCrChannel = mapped.cr.data;
+
+ // copy some of aData's values in mData (most of them)
+ mData.mYChannel = aData.mYChannel;
+ mData.mCbChannel = aData.mCbChannel;
+ mData.mCrChannel = aData.mCrChannel;
+ mData.mYSize = aData.mYSize;
+ mData.mCbCrSize = aData.mCbCrSize;
+ mData.mPicX = aData.mPicX;
+ mData.mPicY = aData.mPicY;
+ mData.mPicSize = aData.mPicSize;
+ mData.mStereoMode = aData.mStereoMode;
+ mData.mYUVColorSpace = aData.mYUVColorSpace;
+ // those members are not always equal to aData's, due to potentially different
+ // packing.
+ mData.mYSkip = 0;
+ mData.mCbSkip = 0;
+ mData.mCrSkip = 0;
+ mData.mYStride = mData.mYSize.width;
+ mData.mCbCrStride = mData.mCbCrSize.width;
+
+ // do not set mBuffer like in PlanarYCbCrImage because the later
+ // will try to manage this memory without knowing it belongs to a
+ // shmem.
+ mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(mData.mYSize, mData.mCbCrSize);
+ mSize = mData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+
+ mTextureClient->Unlock();
+
+ return mBufferSize > 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/SharedPlanarYCbCrImage.h b/system/graphics/layers/ipc/SharedPlanarYCbCrImage.h
new file mode 100644
index 000000000..0c1b6e9c8
--- /dev/null
+++ b/system/graphics/layers/ipc/SharedPlanarYCbCrImage.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+
+#ifndef MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+#define MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+
+class SharedPlanarYCbCrImage : public PlanarYCbCrImage
+{
+public:
+ explicit SharedPlanarYCbCrImage(ImageClient* aCompositable);
+
+protected:
+ ~SharedPlanarYCbCrImage();
+
+public:
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+ virtual uint8_t* GetBuffer() override;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual bool CopyData(const PlanarYCbCrData& aData) override;
+ virtual bool AdoptData(const Data &aData) override;
+
+ virtual bool Allocate(PlanarYCbCrData& aData);
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
+
+ virtual bool IsValid() override;
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ImageClient> mCompositable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/SharedRGBImage.cpp b/system/graphics/layers/ipc/SharedRGBImage.cpp
new file mode 100644
index 000000000..bb3bb968c
--- /dev/null
+++ b/system/graphics/layers/ipc/SharedRGBImage.cpp
@@ -0,0 +1,113 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedRGBImage.h"
+#include "ImageTypes.h" // for ImageFormat::SHARED_RGB, etc
+#include "Shmem.h" // for Shmem
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "mozilla/gfx/Point.h" // for IntSIze
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h" // for BufferTextureClient, etc
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+// Just big enough for a 1080p RGBA32 frame
+#define MAX_FRAME_SIZE (16 * 1024 * 1024)
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<Image>
+CreateSharedRGBImage(ImageContainer *aImageContainer,
+ gfx::IntSize aSize,
+ gfxImageFormat aImageFormat)
+{
+ NS_ASSERTION(aImageFormat == gfx::SurfaceFormat::A8R8G8B8_UINT32 ||
+ aImageFormat == gfx::SurfaceFormat::X8R8G8B8_UINT32 ||
+ aImageFormat == gfx::SurfaceFormat::R5G6B5_UINT16,
+ "RGB formats supported only");
+
+ if (!aImageContainer) {
+ NS_WARNING("No ImageContainer to allocate SharedRGBImage");
+ return nullptr;
+ }
+
+ RefPtr<SharedRGBImage> rgbImage = aImageContainer->CreateSharedRGBImage();
+ if (!rgbImage) {
+ NS_WARNING("Failed to create SharedRGBImage");
+ return nullptr;
+ }
+ if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
+ NS_WARNING("Failed to allocate a shared image");
+ return nullptr;
+ }
+ return rgbImage.forget();
+}
+
+SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
+: Image(nullptr, ImageFormat::SHARED_RGB)
+, mCompositable(aCompositable)
+{
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::~SharedRGBImage()
+{
+ MOZ_COUNT_DTOR(SharedRGBImage);
+
+ if (mCompositable->GetAsyncID() != 0 &&
+ !InImageBridgeChildThread()) {
+ ADDREF_MANUALLY(mTextureClient);
+ ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
+ mTextureClient = nullptr;
+ }
+}
+
+bool
+SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+{
+ mSize = aSize;
+ mTextureClient = mCompositable->CreateBufferTextureClient(aFormat, aSize,
+ gfx::BackendType::NONE,
+ TextureFlags::DEFAULT);
+ return !!mTextureClient;
+}
+
+uint8_t*
+SharedRGBImage::GetBuffer()
+{
+ MappedTextureData mapped;
+ if (mTextureClient && mTextureClient->BorrowMappedData(mapped)) {
+ return mapped.data;
+ }
+ return 0;
+}
+
+gfx::IntSize
+SharedRGBImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+SharedRGBImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient.get();
+}
+
+already_AddRefed<gfx::SourceSurface>
+SharedRGBImage::GetAsSourceSurface()
+{
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/SharedRGBImage.h b/system/graphics/layers/ipc/SharedRGBImage.h
new file mode 100644
index 000000000..2c6009c19
--- /dev/null
+++ b/system/graphics/layers/ipc/SharedRGBImage.h
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHAREDRGBIMAGE_H_
+#define SHAREDRGBIMAGE_H_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint8_t
+#include "ImageContainer.h" // for ISharedImage, Image, etc
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "nsCOMPtr.h" // for already_AddRefed
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+
+already_AddRefed<Image> CreateSharedRGBImage(ImageContainer* aImageContainer,
+ gfx::IntSize aSize,
+ gfxImageFormat aImageFormat);
+
+/**
+ * Stores RGB data in shared memory
+ * It is assumed that the image width and stride are equal
+ */
+class SharedRGBImage : public Image
+{
+public:
+ explicit SharedRGBImage(ImageClient* aCompositable);
+
+protected:
+ ~SharedRGBImage();
+
+public:
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ gfx::IntSize GetSize() override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+private:
+ gfx::IntSize mSize;
+ RefPtr<ImageClient> mCompositable;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/SynchronousTask.h b/system/graphics/layers/ipc/SynchronousTask.h
new file mode 100644
index 000000000..fc20e2843
--- /dev/null
+++ b/system/graphics/layers/ipc/SynchronousTask.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SYNCHRONOUSTASK_H
+#define MOZILLA_GFX_SYNCHRONOUSTASK_H
+
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+namespace mozilla {
+namespace layers {
+
+// Helper that creates a monitor and a "done" flag, then enters the monitor.
+// This can go away when we switch ImageBridge to an XPCOM thread.
+class MOZ_STACK_CLASS SynchronousTask
+{
+ friend class AutoCompleteTask;
+
+public:
+ explicit SynchronousTask(const char* name)
+ : mMonitor(name),
+ mAutoEnter(mMonitor),
+ mDone(false)
+ {}
+
+ void Wait() {
+ while (!mDone) {
+ mMonitor.Wait();
+ }
+ }
+
+private:
+ void Complete() {
+ mDone = true;
+ mMonitor.NotifyAll();
+ }
+
+private:
+ ReentrantMonitor mMonitor;
+ ReentrantMonitorAutoEnter mAutoEnter;
+ bool mDone;
+};
+
+class MOZ_STACK_CLASS AutoCompleteTask
+{
+public:
+ explicit AutoCompleteTask(SynchronousTask* aTask)
+ : mTask(aTask),
+ mAutoEnter(aTask->mMonitor)
+ {
+ }
+ ~AutoCompleteTask() {
+ mTask->Complete();
+ }
+
+private:
+ SynchronousTask* mTask;
+ ReentrantMonitorAutoEnter mAutoEnter;
+};
+
+}
+}
+
+#endif
diff --git a/system/graphics/layers/ipc/TextureForwarder.h b/system/graphics/layers/ipc/TextureForwarder.h
new file mode 100644
index 000000000..2b6579932
--- /dev/null
+++ b/system/graphics/layers/ipc/TextureForwarder.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER
+#define MOZILLA_LAYERS_TEXTUREFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/KnowsCompositor.h"
+
+namespace mozilla {
+namespace ipc {
+class IShmemAllocator;
+}
+namespace layers {
+
+/**
+ * An abstract interface for classes that implement the autogenerated
+ * IPDL actor class. Lets us check if they are still valid for IPC.
+ */
+class LayersIPCActor {
+public:
+ virtual bool IPCOpen() const { return true; }
+};
+
+/**
+ * An abstract interface for LayersIPCActors that implement a top-level
+ * IPDL protocol so also have their own channel.
+ * Has their own MessageLoop for message dispatch, and can allocate
+ * shmem.
+ */
+class LayersIPCChannel : public LayersIPCActor
+ , public mozilla::ipc::IShmemAllocator {
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop* GetMessageLoop() const = 0;
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() { return nullptr; }
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
+
+protected:
+ virtual ~LayersIPCChannel() {}
+};
+
+/**
+ * An abstract interface for classes that can allocate PTexture objects
+ * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity
+ * since all our implementations use both, but could be independant if needed.
+ */
+class TextureForwarder : public LayersIPCChannel {
+public:
+ /**
+ * Create a TextureChild/Parent pair as as well as the TextureHost on the parent side.
+ */
+ virtual PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h b/system/graphics/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
new file mode 100644
index 000000000..e64705478
--- /dev/null
+++ b/system/graphics/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
+#define THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
+
+#include "base/message_loop.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+inline MessageLoop* GetMainLoopAssertingMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return MessageLoop::current();
+}
+
+inline MessageLoop* GetMainLoop()
+{
+ static MessageLoop* sMainLoop = GetMainLoopAssertingMainThread();
+ return sMainLoop;
+}
+
+struct HelperForMainThreadDestruction
+{
+ HelperForMainThreadDestruction()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ GetMainLoop();
+ }
+
+ ~HelperForMainThreadDestruction()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+};
+
+template<typename T>
+struct DeleteOnMainThreadTask : public Runnable
+{
+ T* mToDelete;
+ explicit DeleteOnMainThreadTask(T* aToDelete) : mToDelete(aToDelete) {}
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ mToDelete->DeleteToBeCalledOnMainThread();
+ return NS_OK;
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(_class) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return (nsrefcnt) count; \
+ } \
+ void DeleteToBeCalledOnMainThread() { \
+ MOZ_ASSERT(NS_IsMainThread()); \
+ NS_LOG_RELEASE(this, 0, #_class); \
+ delete this; \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ if (count == 0) { \
+ if (NS_IsMainThread()) { \
+ DeleteToBeCalledOnMainThread(); \
+ } else { \
+ NS_DispatchToMainThread( \
+ new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
+ } \
+ } else { \
+ NS_LOG_RELEASE(this, count, #_class); \
+ } \
+ return count; \
+ } \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+private: \
+ ::mozilla::layers::HelperForMainThreadDestruction mHelperForMainThreadDestruction; \
+public:
+
+#endif
diff --git a/system/graphics/layers/ipc/VideoBridgeChild.cpp b/system/graphics/layers/ipc/VideoBridgeChild.cpp
new file mode 100644
index 000000000..9651c563e
--- /dev/null
+++ b/system/graphics/layers/ipc/VideoBridgeChild.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeChild.h"
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+
+namespace mozilla {
+namespace layers {
+
+StaticRefPtr<VideoBridgeChild> sVideoBridgeChildSingleton;
+
+/* static */ void
+VideoBridgeChild::Startup()
+{
+ sVideoBridgeChildSingleton = new VideoBridgeChild();
+ RefPtr<VideoBridgeParent> parent = new VideoBridgeParent();
+
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+
+ sVideoBridgeChildSingleton->Open(parent->GetIPCChannel(),
+ loop,
+ ipc::ChildSide);
+ sVideoBridgeChildSingleton->mIPDLSelfRef = sVideoBridgeChildSingleton;
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+}
+
+/* static */ void
+VideoBridgeChild::Shutdown()
+{
+ if (sVideoBridgeChildSingleton) {
+ sVideoBridgeChildSingleton->Close();
+ sVideoBridgeChildSingleton = nullptr;
+ }
+}
+
+VideoBridgeChild::VideoBridgeChild()
+ : mMessageLoop(MessageLoop::current())
+ , mCanSend(true)
+{
+}
+
+VideoBridgeChild::~VideoBridgeChild()
+{
+}
+
+VideoBridgeChild*
+VideoBridgeChild::GetSingleton()
+{
+ return sVideoBridgeChildSingleton;
+}
+
+bool
+VideoBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PVideoBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ MOZ_ASSERT(CanSend());
+ return PVideoBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ return PVideoBridgeChild::DeallocShmem(aShmem);
+}
+
+PTextureChild*
+VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t& aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+void
+VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+}
+
+void
+VideoBridgeChild::DeallocPVideoBridgeChild()
+{
+ mIPDLSelfRef = nullptr;
+}
+
+PTextureChild*
+VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool VideoBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/VideoBridgeChild.h b/system/graphics/layers/ipc/VideoBridgeChild.h
new file mode 100644
index 000000000..f5677008e
--- /dev/null
+++ b/system/graphics/layers/ipc/VideoBridgeChild.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H
+#define MOZILLA_GFX_VIDEOBRIDGECHILD_H
+
+#include "mozilla/layers/PVideoBridgeChild.h"
+#include "ISurfaceAllocator.h"
+#include "TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+class VideoBridgeChild final : public PVideoBridgeChild
+ , public TextureForwarder
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override);
+
+ static void Startup();
+ static void Shutdown();
+
+ static VideoBridgeChild* GetSingleton();
+
+ // PVideoBridgeChild
+ PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ bool DeallocPTextureChild(PTextureChild* actor) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPVideoBridgeChild() override;
+
+
+ // ISurfaceAllocator
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // TextureForwarder
+ PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ // ClientIPCAllocator
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+ MessageLoop * GetMessageLoop() const override { return mMessageLoop; }
+ void CancelWaitForRecycle(uint64_t aTextureId) override { MOZ_ASSERT(false, "NO RECYCLING HERE"); }
+
+ // ISurfaceAllocator
+ bool IsSameProcess() const override;
+
+ bool CanSend() { return mCanSend; }
+
+private:
+ VideoBridgeChild();
+ ~VideoBridgeChild();
+
+ RefPtr<VideoBridgeChild> mIPDLSelfRef;
+ MessageLoop* mMessageLoop;
+ bool mCanSend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/ipc/VideoBridgeParent.cpp b/system/graphics/layers/ipc/VideoBridgeParent.cpp
new file mode 100644
index 000000000..bdab24766
--- /dev/null
+++ b/system/graphics/layers/ipc/VideoBridgeParent.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeParent.h"
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+
+
+static VideoBridgeParent* sVideoBridgeSingleton;
+
+VideoBridgeParent::VideoBridgeParent()
+ : mClosed(false)
+{
+ mSelfRef = this;
+ sVideoBridgeSingleton = this;
+}
+
+VideoBridgeParent::~VideoBridgeParent()
+{
+ sVideoBridgeSingleton = nullptr;
+}
+
+/* static */ VideoBridgeParent*
+VideoBridgeParent::GetSingleton()
+{
+ return sVideoBridgeSingleton;
+}
+
+TextureHost*
+VideoBridgeParent::LookupTexture(uint64_t aSerial)
+{
+ return TextureHost::AsTextureHost(mTextureMap[aSerial]);
+}
+
+void
+VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+}
+
+void
+VideoBridgeParent::DeallocPVideoBridgeParent()
+{
+ mSelfRef = nullptr;
+}
+
+PTextureParent*
+VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial)
+{
+ PTextureParent* parent =
+ TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+ mTextureMap[aSerial] = parent;
+ return parent;
+}
+
+bool
+VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ mTextureMap.erase(TextureHost::GetTextureSerial(actor));
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+void
+VideoBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ MOZ_ASSERT(false, "AsyncMessages not supported");
+}
+
+bool
+VideoBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (mClosed) {
+ return;
+ }
+ PVideoBridgeParent::DeallocShmem(aShmem);
+}
+
+bool
+VideoBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+VideoBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/ipc/VideoBridgeParent.h b/system/graphics/layers/ipc/VideoBridgeParent.h
new file mode 100644
index 000000000..bde18b632
--- /dev/null
+++ b/system/graphics/layers/ipc/VideoBridgeParent.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_VideoBridgeParent_h_
+#define gfx_layers_ipc_VideoBridgeParent_h_
+
+#include "mozilla/layers/PVideoBridgeParent.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class VideoBridgeParent final : public PVideoBridgeParent,
+ public HostIPCAllocator,
+ public ShmemAllocator
+{
+public:
+ VideoBridgeParent();
+ ~VideoBridgeParent();
+
+ static VideoBridgeParent* GetSingleton();
+ TextureHost* LookupTexture(uint64_t aSerial);
+
+ // PVideoBridgeParent
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+ void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+ void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ISurfaceAllocator
+ ShmemAllocator* AsShmemAllocator() override { return this; }
+ bool IsSameProcess() const override;
+ bool IPCOpen() const override { return !mClosed; }
+
+ // ShmemAllocator
+ bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ void DeallocShmem(ipc::Shmem& aShmem) override;
+
+private:
+ void DeallocPVideoBridgeParent() override;
+
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<VideoBridgeParent> mSelfRef;
+
+ std::map<uint64_t, PTextureParent*> mTextureMap;
+
+ bool mClosed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_VideoBridgeParent_h_
diff --git a/system/graphics/layers/layerviewer/hide.png b/system/graphics/layers/layerviewer/hide.png
new file mode 100644
index 000000000..9a92e2c1b
--- /dev/null
+++ b/system/graphics/layers/layerviewer/hide.png
Binary files differ
diff --git a/system/graphics/layers/layerviewer/index.html b/system/graphics/layers/layerviewer/index.html
new file mode 100644
index 000000000..3ad835df5
--- /dev/null
+++ b/system/graphics/layers/layerviewer/index.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>GFX Display List & Layer Visualizer</title>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="tree.css">
+ <script src="layerTreeView.js"></script>
+ <style>
+
+.csstooltip
+{
+ z-index: 5;
+ background: white;
+ border: solid 1px black;
+ position: absolute;
+ padding: 5px;
+ margin: 5px;
+ max-width: 300px;
+}
+ </style>
+ </head>
+ <body>
+ <h1>GFX Layers dump visualizer:</h1>
+ Paste your display list or layers dump into this textarea:<br>
+ <textarea id="input_layers_dump" style="width:100%; height: 80%;" cols="80" rows="10">
+ClientLayerManager (0x1264f5000)
+ ClientContainerLayer (0x1263fe200) [visible=< (x=0, y=0, w=1457, h=1163); >] [opaqueContent] [metrics0={ [cb=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [sr=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [s=(0,0)] [dp=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [cdp=(x=0.000000, y=0.000000, w=0.000000, h=0.000000)] [color=rgba(0, 0, 0, 0.000000)] [scrollId=3] [z=1] }]
+ ClientTiledPaintedLayer (0x1263b3600) [bounds=(x=-1, y=0, w=1458, h=1163)] [visible=< (x=0, y=0, w=1457, h=79); >] { hitregion=< (x=0, y=0, w=1457, h=47); (x=-1, y=47, w=1458, h=24); (x=0, y=71, w=1457, h=1092); > dispatchtocontentregion=< (x=68, y=9, w=1375, h=31); (x=944, y=47, w=280, h=24); >} [opaqueContent] [valid=< (x=0, y=0, w=1457, h=79); >]
+ SingleTiledContentClient (0x126f80680)
+ ClientContainerLayer (0x122a33f00) [clip=(x=0, y=79, w=1457, h=1084)] [visible=< (x=0, y=79, w=1457, h=1084); >] [opaqueContent]
+ ClientTiledPaintedLayer (0x11e11a700) [bounds=(x=0, y=79, w=1457, h=1084)] [visible=< (x=0, y=79, w=1457, h=1084); >] { hitregion=< (x=0, y=79, w=1457, h=1084); > dispatchtocontentregion=< (x=0, y=125, w=1457, h=1034); >} [opaqueContent] [valid=< (x=0, y=79, w=1457, h=1084); >]
+ SingleTiledContentClient (0x1226d52c0)
+ </textarea>
+ <br>
+ <input type="button" value="Process pasted log" onclick="log_pasted()" />
+ <br>
+ <br>
+ Help: To get a layers dump go to about:config and set layout.display-list.dump;true or layers.dump;true.
+ <script>
+function log_pasted() {
+ var container = parseMultiLineDump(document.getElementById("input_layers_dump").value);
+ document.body.innerHTML = "";
+ document.body.appendChild(container);
+}
+ </script>
+ </body>
+</html>
diff --git a/system/graphics/layers/layerviewer/layerTreeView.js b/system/graphics/layers/layerviewer/layerTreeView.js
new file mode 100644
index 000000000..e5ecac251
--- /dev/null
+++ b/system/graphics/layers/layerviewer/layerTreeView.js
@@ -0,0 +1,885 @@
+function toFixed(num, fixed) {
+ fixed = fixed || 0;
+ fixed = Math.pow(10, fixed);
+ return Math.floor(num * fixed) / fixed;
+}
+function createElement(name, props) {
+ var el = document.createElement(name);
+
+ for (var key in props) {
+ if (key === "style") {
+ for (var styleName in props.style) {
+ el.style[styleName] = props.style[styleName];
+ }
+ } else {
+ el[key] = props[key];
+ }
+ }
+
+ return el;
+}
+
+function parseDisplayList(lines) {
+ var root = {
+ line: "DisplayListRoot 0",
+ name: "DisplayListRoot",
+ address: "0x0",
+ frame: "Root",
+ children: [],
+ };
+
+ var objectAtIndentation = {
+ "-1": root,
+ };
+
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+
+ var layerObject = {
+ line: line,
+ children: [],
+ }
+ if (!root) {
+ root = layerObject;
+ }
+
+ var matches = line.match("(\\s*)(\\w+)\\sp=(\\w+)\\sf=(.*?)\\((.*?)\\)\\s(z=(\\w+)\\s)?(.*?)?( layer=(\\w+))?$");
+ if (!matches) {
+ dump("Failed to match: " + line + "\n");
+ continue;
+ }
+
+ var indentation = Math.floor(matches[1].length / 2);
+ objectAtIndentation[indentation] = layerObject;
+ var parent = objectAtIndentation[indentation - 1];
+ if (parent) {
+ parent.children.push(layerObject);
+ }
+
+ layerObject.name = matches[2];
+ layerObject.address = matches[3]; // Use 0x prefix to be consistent with layer dump
+ layerObject.frame = matches[4];
+ layerObject.contentDescriptor = matches[5];
+ layerObject.z = matches[7];
+ var rest = matches[8];
+ if (matches[10]) { // WrapList don't provide a layer
+ layerObject.layer = matches[10];
+ }
+ layerObject.rest = rest;
+
+ // the content node name doesn't have a prefix, this makes the parsing easier
+ rest = "content" + rest;
+
+ var fields = {};
+ var nesting = 0;
+ var startIndex;
+ var lastSpace = -1;
+ var lastFieldStart = -1;
+ for (var j = 0; j < rest.length; j++) {
+ if (rest.charAt(j) == '(') {
+ nesting++;
+ if (nesting == 1) {
+ startIndex = j;
+ }
+ } else if (rest.charAt(j) == ')') {
+ nesting--;
+ if (nesting == 0) {
+ var name = rest.substring(lastSpace + 1, startIndex);
+ var value = rest.substring(startIndex + 1, j);
+
+ var rectMatches = value.match("^(.*?),(.*?),(.*?),(.*?)$")
+ if (rectMatches) {
+ layerObject[name] = [
+ parseFloat(rectMatches[1]),
+ parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]),
+ parseFloat(rectMatches[4]),
+ ];
+ } else {
+ layerObject[name] = value;
+ }
+ }
+ } else if (nesting == 0 && rest.charAt(j) == ' ') {
+ lastSpace = j;
+ }
+ }
+ //dump("FIELDS: " + JSON.stringify(fields) + "\n");
+ }
+ return root;
+}
+
+function trim(s){
+ return ( s || '' ).replace( /^\s+|\s+$/g, '' );
+}
+
+function getDataURI(str) {
+ if (str.indexOf("data:image/png;base64,") == 0) {
+ return str;
+ }
+
+ var matches = str.match("data:image/lz4bgra;base64,([0-9]+),([0-9]+),([0-9]+),(.*)");
+ if (!matches)
+ return null;
+
+ var canvas = document.createElement("canvas");
+ var w = parseInt(matches[1]);
+ var stride = parseInt(matches[2]);
+ var h = parseInt(matches[3]);
+ canvas.width = w;
+ canvas.height = h;
+
+ // TODO handle stride
+
+ var binary_string = window.atob(matches[4]);
+ var len = binary_string.length;
+ var bytes = new Uint8Array(len);
+ var decoded = new Uint8Array(stride * h);
+ for (var i = 0; i < len; i++) {
+ var ascii = binary_string.charCodeAt(i);
+ bytes[i] = ascii;
+ }
+
+ var ctxt = canvas.getContext("2d");
+ var out = ctxt.createImageData(w, h);
+ buffer = LZ4_uncompressChunk(bytes, decoded);
+
+ for (var x = 0; x < w; x++) {
+ for (var y = 0; y < h; y++) {
+ out.data[4 * x + 4 * y * w + 0] = decoded[4 * x + y * stride + 2];
+ out.data[4 * x + 4 * y * w + 1] = decoded[4 * x + y * stride + 1];
+ out.data[4 * x + 4 * y * w + 2] = decoded[4 * x + y * stride + 0];
+ out.data[4 * x + 4 * y * w + 3] = decoded[4 * x + y * stride + 3];
+ }
+ }
+
+ ctxt.putImageData(out, 0, 0);
+ return canvas.toDataURL();
+}
+
+function parseLayers(layersDumpLines) {
+ function parseMatrix2x3(str) {
+ str = trim(str);
+
+ // Something like '[ 1 0; 0 1; 0 158; ]'
+ var matches = str.match("^\\[ (.*?) (.*?); (.*?) (.*?); (.*?) (.*?); \\]$");
+ if (!matches) {
+ return null;
+ }
+
+ var matrix = [
+ [parseFloat(matches[1]), parseFloat(matches[2])],
+ [parseFloat(matches[3]), parseFloat(matches[4])],
+ [parseFloat(matches[5]), parseFloat(matches[6])],
+ ];
+
+ return matrix;
+ }
+ function parseColor(str) {
+ str = trim(str);
+
+ // Something like 'rgba(0, 0, 0, 0)'
+ var colorMatches = str.match("^rgba\\((.*), (.*), (.*), (.*)\\)$");
+ if (!colorMatches) {
+ return null;
+ }
+
+ var color = {
+ r: colorMatches[1],
+ g: colorMatches[2],
+ b: colorMatches[3],
+ a: colorMatches[4],
+ };
+ return color;
+ }
+ function parseFloat_cleo(str) {
+ str = trim(str);
+
+ // Something like 2.000
+ if (parseFloat(str) == str) {
+ return parseFloat(str);
+ }
+
+ return null;
+ }
+ function parseRect2D(str) {
+ str = trim(str);
+
+ // Something like '(x=0, y=0, w=2842, h=158)'
+ var rectMatches = str.match("^\\(x=(.*?), y=(.*?), w=(.*?), h=(.*?)\\)$");
+ if (!rectMatches) {
+ return null;
+ }
+
+ var rect = [
+ parseFloat(rectMatches[1]), parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]), parseFloat(rectMatches[4]),
+ ];
+ return rect;
+ }
+ function parseRegion(str) {
+ str = trim(str);
+
+ // Something like '< (x=0, y=0, w=2842, h=158); (x=0, y=1718, w=2842, h=500); >'
+ if (str.charAt(0) != '<' || str.charAt(str.length - 1) != '>') {
+ return null;
+ }
+
+ var region = [];
+ str = trim(str.substring(1, str.length - 1));
+ while (str != "") {
+ var rectMatches = str.match("^\\(x=(.*?), y=(.*?), w=(.*?), h=(.*?)\\);(.*)$");
+ if (!rectMatches) {
+ return null;
+ }
+
+ var rect = [
+ parseFloat(rectMatches[1]), parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]), parseFloat(rectMatches[4]),
+ ];
+ str = trim(rectMatches[5]);
+ region.push(rect);
+ }
+ return region;
+ }
+
+ var LAYERS_LINE_REGEX = "(\\s*)(\\w+)\\s\\((\\w+)\\)(.*)";
+
+ var root;
+ var objectAtIndentation = [];
+ for (var i = 0; i < layersDumpLines.length; i++) {
+ // Something like 'ThebesLayerComposite (0x12104cc00) [shadow-visible=< (x=0, y=0, w=1920, h=158); >] [visible=< (x=0, y=0, w=1920, h=158); >] [opaqueContent] [valid=< (x=0, y=0, w=1920, h=2218); >]'
+ var line = layersDumpLines[i].name || layersDumpLines[i];
+
+ var tileMatches = line.match("(\\s*)Tile \\(x=(.*), y=(.*)\\): (.*)");
+ if (tileMatches) {
+ var indentation = Math.floor(matches[1].length / 2);
+ var x = tileMatches[2];
+ var y = tileMatches[3];
+ var dataUri = tileMatches[4];
+ var parent = objectAtIndentation[indentation - 1];
+ var tiles = parent.tiles || {};
+
+ tiles[x] = tiles[x] || {};
+ tiles[x][y] = dataUri;
+
+ parent.tiles = tiles;
+
+ continue;
+ }
+
+ var surfaceMatches = line.match("(\\s*)Surface: (.*)");
+ if (surfaceMatches) {
+ var indentation = Math.floor(matches[1].length / 2);
+ var parent = objectAtIndentation[indentation - 1] || objectAtIndentation[indentation - 2];
+
+ var surfaceURI = surfaceMatches[2];
+ if (parent.surfaceURI != null) {
+ console.log("error: surfaceURI already set for this layer " + parent.line);
+ }
+ parent.surfaceURI = surfaceURI;
+
+ // Look for the buffer-rect offset
+ var contentHostLine = layersDumpLines[i - 2].name || layersDumpLines[i - 2];
+ var matches = contentHostLine.match(LAYERS_LINE_REGEX);
+ if (matches) {
+ var contentHostRest = matches[4];
+ parent.contentHostProp = {};
+ parseProperties(contentHostRest, parent.contentHostProp);
+ }
+
+ continue;
+ }
+
+ var layerObject = {
+ line: line,
+ children: [],
+ }
+ if (!root) {
+ root = layerObject;
+ }
+
+ var matches = line.match(LAYERS_LINE_REGEX);
+ if (!matches) {
+ continue; // Something like a texturehost dump. Safe to ignore
+ }
+
+ if (matches[2].indexOf("TiledContentHost") != -1 ||
+ matches[2].indexOf("GrallocTextureHostOGL") != -1 ||
+ matches[2].indexOf("ContentHost") != -1 ||
+ matches[2].indexOf("ContentClient") != -1 ||
+ matches[2].indexOf("MemoryTextureHost") != -1 ||
+ matches[2].indexOf("ImageHost") != -1) {
+ continue; // We're already pretty good at visualizing these
+ }
+
+ var indentation = Math.floor(matches[1].length / 2);
+ objectAtIndentation[indentation] = layerObject;
+ for (var c = indentation + 1; c < objectAtIndentation.length; c++) {
+ objectAtIndentation[c] = null;
+ }
+ if (indentation > 0) {
+ var parent = objectAtIndentation[indentation - 1];
+ while (!parent) {
+ indentation--;
+ parent = objectAtIndentation[indentation - 1];
+ }
+
+ parent.children.push(layerObject);
+ }
+
+ layerObject.name = matches[2];
+ layerObject.address = matches[3];
+
+ var rest = matches[4];
+
+ function parseProperties(rest, layerObject) {
+ var fields = [];
+ var nesting = 0;
+ var startIndex;
+ for (var j = 0; j < rest.length; j++) {
+ if (rest.charAt(j) == '[') {
+ nesting++;
+ if (nesting == 1) {
+ startIndex = j;
+ }
+ } else if (rest.charAt(j) == ']') {
+ nesting--;
+ if (nesting == 0) {
+ fields.push(rest.substring(startIndex + 1, j));
+ }
+ }
+ }
+
+ for (var j = 0; j < fields.length; j++) {
+ // Something like 'valid=< (x=0, y=0, w=1920, h=2218); >' or 'opaqueContent'
+ var field = fields[j];
+ //dump("FIELD: " + field + "\n");
+ var parts = field.split("=", 2);
+ var fieldName = parts[0];
+ var rest = field.substring(fieldName.length + 1);
+ if (parts.length == 1) {
+ layerObject[fieldName] = "true";
+ layerObject[fieldName].type = "bool";
+ continue;
+ }
+ var float = parseFloat_cleo(rest);
+ if (float) {
+ layerObject[fieldName] = float;
+ layerObject[fieldName].type = "float";
+ continue;
+ }
+ var region = parseRegion(rest);
+ if (region) {
+ layerObject[fieldName] = region;
+ layerObject[fieldName].type = "region";
+ continue;
+ }
+ var rect = parseRect2D(rest);
+ if (rect) {
+ layerObject[fieldName] = rect;
+ layerObject[fieldName].type = "rect2d";
+ continue;
+ }
+ var matrix = parseMatrix2x3(rest);
+ if (matrix) {
+ layerObject[fieldName] = matrix;
+ layerObject[fieldName].type = "matrix2x3";
+ continue;
+ }
+ var color = parseColor(rest);
+ if (color) {
+ layerObject[fieldName] = color;
+ layerObject[fieldName].type = "color";
+ continue;
+ }
+ if (rest[0] == '{' && rest[rest.length - 1] == '}') {
+ var object = {};
+ parseProperties(rest.substring(1, rest.length - 2).trim(), object);
+ layerObject[fieldName] = object;
+ layerObject[fieldName].type = "object";
+ continue;
+ }
+ fieldName = fieldName.split(" ")[0];
+ layerObject[fieldName] = rest[0];
+ layerObject[fieldName].type = "string";
+ }
+ }
+ parseProperties(rest, layerObject);
+
+ if (!layerObject['shadow-transform']) {
+ // No shadow transform = identify
+ layerObject['shadow-transform'] = [[1, 0], [0, 1], [0, 0]];
+ }
+
+ // Compute screenTransformX/screenTransformY
+ // TODO Fully support transforms
+ if (layerObject['shadow-transform'] && layerObject['transform']) {
+ layerObject['screen-transform'] = [layerObject['shadow-transform'][2][0], layerObject['shadow-transform'][2][1]];
+ var currIndentation = indentation - 1;
+ while (currIndentation >= 0) {
+ var transform = objectAtIndentation[currIndentation]['shadow-transform'] || objectAtIndentation[currIndentation]['transform'];
+ if (transform) {
+ layerObject['screen-transform'][0] += transform[2][0];
+ layerObject['screen-transform'][1] += transform[2][1];
+ }
+ currIndentation--;
+ }
+ }
+
+ //dump("Fields: " + JSON.stringify(fields) + "\n");
+ }
+ root.compositeTime = layersDumpLines.compositeTime;
+ //dump("OBJECTS: " + JSON.stringify(root) + "\n");
+ return root;
+}
+function populateLayers(root, displayList, pane, previewParent, hasSeenRoot, contentScale, rootPreviewParent) {
+
+ contentScale = contentScale || 1;
+ rootPreviewParent = rootPreviewParent || previewParent;
+
+ function getDisplayItemForLayer(displayList) {
+ var items = [];
+ if (!displayList) {
+ return items;
+ }
+ if (displayList.layer == root.address) {
+ items.push(displayList);
+ }
+ for (var i = 0; i < displayList.children.length; i++) {
+ var subDisplayItems = getDisplayItemForLayer(displayList.children[i]);
+ for (var j = 0; j < subDisplayItems.length; j++) {
+ items.push(subDisplayItems[j]);
+ }
+ }
+ return items;
+ }
+ var elem = createElement("div", {
+ className: "layerObjectDescription",
+ textContent: root.line,
+ style: {
+ whiteSpace: "pre",
+ },
+ onmouseover: function() {
+ if (this.layerViewport) {
+ this.layerViewport.classList.add("layerHover");
+ }
+ },
+ onmouseout: function() {
+ if (this.layerViewport) {
+ this.layerViewport.classList.remove("layerHover");
+ }
+ },
+ });
+ var icon = createElement("img", {
+ src: "show.png",
+ style: {
+ width: "12px",
+ height: "12px",
+ marginLeft: "4px",
+ marginRight: "4px",
+ cursor: "pointer",
+ },
+ onclick: function() {
+ if (this.layerViewport) {
+ if (this.layerViewport.style.visibility == "hidden") {
+ this.layerViewport.style.visibility = "";
+ this.src = "show.png"
+ } else {
+ this.layerViewport.style.visibility = "hidden";
+ this.src = "hide.png"
+ }
+ }
+ }
+ });
+ elem.insertBefore(icon, elem.firstChild);
+ pane.appendChild(elem);
+
+ if (root["shadow-visible"] || root["visible"]) {
+ var visibleRegion = root["shadow-visible"] || root["visible"];
+ var layerViewport = createElement("div", {
+ id: root.address + "_viewport",
+ style: {
+ position: "absolute",
+ pointerEvents: "none",
+ },
+ });
+ elem.layerViewport = layerViewport;
+ icon.layerViewport = layerViewport;
+ var layerViewportMatrix = [1, 0, 0, 1, 0, 0];
+ if (root["shadow-clip"] || root["clip"]) {
+ var clip = root["shadow-clip"] || root["clip"]
+ var clipElem = createElement("div", {
+ id: root.address + "_clip",
+ style: {
+ left: clip[0]+"px",
+ top: clip[1]+"px",
+ width: clip[2]+"px",
+ height: clip[3]+"px",
+ position: "absolute",
+ overflow: "hidden",
+ pointerEvents: "none",
+ },
+ });
+ layerViewportMatrix[4] += -clip[0];
+ layerViewportMatrix[5] += -clip[1];
+ layerViewport.style.transform = "translate(-" + clip[0] + "px, -" + clip[1] + "px" + ")";
+ }
+ if (root["shadow-transform"] || root["transform"]) {
+ var matrix = root["shadow-transform"] || root["transform"];
+ layerViewportMatrix[0] = matrix[0][0];
+ layerViewportMatrix[1] = matrix[0][1];
+ layerViewportMatrix[2] = matrix[1][0];
+ layerViewportMatrix[3] = matrix[1][1];
+ layerViewportMatrix[4] += matrix[2][0];
+ layerViewportMatrix[5] += matrix[2][1];
+ }
+ layerViewport.style.transform = "matrix(" + layerViewportMatrix[0] + "," + layerViewportMatrix[1] + "," + layerViewportMatrix[2] + "," + layerViewportMatrix[3] + "," + layerViewportMatrix[4] + "," + layerViewportMatrix[5] + ")";
+ if (!hasSeenRoot) {
+ hasSeenRoot = true;
+ layerViewport.style.transform = "scale(" + 1/contentScale + "," + 1/contentScale + ")";
+ }
+ if (clipElem) {
+ previewParent.appendChild(clipElem);
+ clipElem.appendChild(layerViewport);
+ } else {
+ previewParent.appendChild(layerViewport);
+ }
+ previewParent = layerViewport;
+ for (var i = 0; i < visibleRegion.length; i++) {
+ var rect2d = visibleRegion[i];
+ var layerPreview = createElement("div", {
+ id: root.address + "_visible_part" + i + "-" + visibleRegion.length,
+ className: "layerPreview",
+ style: {
+ position: "absolute",
+ left: rect2d[0] + "px",
+ top: rect2d[1] + "px",
+ width: rect2d[2] + "px",
+ height: rect2d[3] + "px",
+ overflow: "hidden",
+ border: "solid 1px black",
+ background: 'url("noise.png"), linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2))',
+ },
+ });
+ layerViewport.appendChild(layerPreview);
+
+ function isInside(rect1, rect2) {
+ if (rect1[0] + rect1[2] < rect2[0] && rect2[0] + rect2[2] < rect1[0] &&
+ rect1[1] + rect1[3] < rect2[1] && rect2[1] + rect2[3] < rect1[1]) {
+ return true;
+ }
+ return true;
+ }
+
+ var hasImg = false;
+ // Add tile img objects for this part
+ var previewOffset = rect2d;
+
+ if (root.tiles) {
+ hasImg = true;
+ for (var x in root.tiles) {
+ for (var y in root.tiles[x]) {
+ if (isInside(rect2d, [x, y, 512, 512])) {
+ var tileImgElem = createElement("img", {
+ src: getDataURI(root.tiles[x][y]),
+ style: {
+ position: "absolute",
+ left: (x - previewOffset[0]) + "px",
+ top: (y - previewOffset[1]) + "px",
+ pointerEvents: "auto",
+ },
+ });
+ layerPreview.appendChild(tileImgElem);
+ }
+ }
+ }
+ layerPreview.style.background = "";
+ } else if (root.surfaceURI) {
+ hasImg = true;
+ var offsetX = 0;
+ var offsetY = 0;
+ if (root.contentHostProp && root.contentHostProp['buffer-rect']) {
+ offsetX = root.contentHostProp['buffer-rect'][0];
+ offsetY = root.contentHostProp['buffer-rect'][1];
+ }
+ var surfaceImgElem = createElement("img", {
+ src: getDataURI(root.surfaceURI),
+ style: {
+ position: "absolute",
+ left: (offsetX - previewOffset[0]) + "px",
+ top: (offsetY - previewOffset[1]) + "px",
+ pointerEvents: "auto",
+ },
+ });
+ layerPreview.appendChild(surfaceImgElem);
+ layerPreview.style.background = "";
+ } else if (root.color) {
+ hasImg = true;
+ layerPreview.style.background = "rgba(" + root.color.r + ", " + root.color.g + ", " + root.color.b + ", " + root.color.a + ")";
+ }
+
+ if (hasImg || true) {
+ layerPreview.mouseoverElem = elem;
+ layerPreview.onmouseenter = function() {
+ this.mouseoverElem.onmouseover();
+ }
+ layerPreview.onmouseout = function() {
+ this.mouseoverElem.onmouseout();
+ }
+ }
+ }
+
+ var layerDisplayItems = getDisplayItemForLayer(displayList);
+ for (var i = 0; i < layerDisplayItems.length; i++) {
+ var displayItem = layerDisplayItems[i];
+ var displayElem = createElement("div", {
+ className: "layerObjectDescription",
+ textContent: " " + trim(displayItem.line),
+ style: {
+ whiteSpace: "pre",
+ },
+ displayItem: displayItem,
+ layerViewport: layerViewport,
+ onmouseover: function() {
+ if (this.diPreview) {
+ this.diPreview.classList.add("displayHover");
+
+ var description = "";
+ if (this.displayItem.contentDescriptor) {
+ description += "Content: " + this.displayItem.contentDescriptor;
+ } else {
+ description += "Content: Unknown";
+ }
+ description += "<br>Item: " + this.displayItem.name + " (" + this.displayItem.address + ")";
+ description += "<br>Layer: " + root.name + " (" + root.address + ")";
+ if (this.displayItem.frame) {
+ description += "<br>Frame: " + this.displayItem.frame;
+ }
+ if (this.displayItem.layerBounds) {
+ description += "<br>Bounds: [" + toFixed(this.displayItem.layerBounds[0] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[1] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[2] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[3] / 60, 2) + "] (CSS Pixels)";
+ }
+ if (this.displayItem.z) {
+ description += "<br>Z: " + this.displayItem.z;
+ }
+ // At the end
+ if (this.displayItem.rest) {
+ description += "<br>" + this.displayItem.rest;
+ }
+
+ var box = this.diPreview.getBoundingClientRect();
+ var pageBox = document.body.getBoundingClientRect();
+ this.diPreview.tooltip = createElement("div", {
+ className: "csstooltip",
+ innerHTML: description,
+ style: {
+ top: Math.min(box.bottom, document.documentElement.clientHeight - 150) + "px",
+ left: box.left + "px",
+ }
+ });
+
+ document.body.appendChild(this.diPreview.tooltip);
+ }
+ },
+ onmouseout: function() {
+ if (this.diPreview) {
+ this.diPreview.classList.remove("displayHover");
+ document.body.removeChild(this.diPreview.tooltip);
+ }
+ },
+ });
+
+ var icon = createElement("img", {
+ style: {
+ width: "12px",
+ height: "12px",
+ marginLeft: "4px",
+ marginRight: "4px",
+ }
+ });
+ displayElem.insertBefore(icon, displayElem.firstChild);
+ pane.appendChild(displayElem);
+ // bounds doesn't adjust for within the layer. It's not a bad fallback but
+ // will have the wrong offset
+ var rect2d = displayItem.layerBounds || displayItem.bounds;
+ if (rect2d) { // This doesn't place them corectly
+ var appUnitsToPixels = 60 / contentScale;
+ diPreview = createElement("div", {
+ id: "displayitem_" + displayItem.content + "_" + displayItem.address,
+ className: "layerPreview",
+ style: {
+ position: "absolute",
+ left: rect2d[0]/appUnitsToPixels + "px",
+ top: rect2d[1]/appUnitsToPixels + "px",
+ width: rect2d[2]/appUnitsToPixels + "px",
+ height: rect2d[3]/appUnitsToPixels + "px",
+ border: "solid 1px gray",
+ pointerEvents: "auto",
+ },
+ displayElem: displayElem,
+ onmouseover: function() {
+ this.displayElem.onmouseover();
+ },
+ onmouseout: function() {
+ this.displayElem.onmouseout();
+ },
+ });
+
+ layerViewport.appendChild(diPreview);
+ displayElem.diPreview = diPreview;
+ }
+ }
+ }
+
+ for (var i = 0; i < root.children.length; i++) {
+ populateLayers(root.children[i], displayList, pane, previewParent, hasSeenRoot, contentScale, rootPreviewParent);
+ }
+}
+
+// This function takes a stdout snippet and finds the frames
+function parseMultiLineDump(log) {
+ var lines = log.split("\n");
+
+ var container = createElement("div", {
+ style: {
+ height: "100%",
+ position: "relative",
+ },
+ });
+
+ var layerManagerFirstLine = "[a-zA-Z]*LayerManager \\(.*$\n";
+ var nextLineStartWithSpace = "([ \\t].*$\n)*";
+ var layersRegex = "(" + layerManagerFirstLine + nextLineStartWithSpace + ")";
+
+ var startLine = "Painting --- after optimization:\n";
+ var endLine = "Painting --- layer tree:"
+ var displayListRegex = "(" + startLine + "(.*\n)*?" + endLine + ")";
+
+ var regex = new RegExp(layersRegex + "|" + displayListRegex, "gm");
+ var matches = log.match(regex);
+ console.log(matches);
+ window.matches = matches;
+
+ var matchList = createElement("span", {
+ style: {
+ height: "95%",
+ width: "10%",
+ position: "relative",
+ border: "solid black 2px",
+ display: "inline-block",
+ float: "left",
+ overflow: "auto",
+ },
+ });
+ container.appendChild(matchList);
+ var contents = createElement("span", {
+ style: {
+ height: "95%",
+ width: "88%",
+ display: "inline-block",
+ },
+ textContent: "Click on a frame on the left to view the layer tree",
+ });
+ container.appendChild(contents);
+
+ var lastDisplayList = null;
+ var frameID = 1;
+ for (let i = 0; i < matches.length; i++) {
+ var currMatch = matches[i];
+
+ if (currMatch.indexOf(startLine) == 0) {
+ // Display list match
+ var matchLines = matches[i].split("\n")
+ lastDisplayList = parseDisplayList(matchLines);
+ } else {
+ // Layer tree match:
+ let displayList = lastDisplayList;
+ lastDisplayList = null;
+ var currFrameDiv = createElement("a", {
+ style: {
+ padding: "3px",
+ display: "block",
+ },
+ href: "#",
+ textContent: "LayerTree " + (frameID++),
+ onclick: function() {
+ contents.innerHTML = "";
+ var matchLines = matches[i].split("\n")
+ var dumpDiv = parseDump(matchLines, displayList);
+ contents.appendChild(dumpDiv);
+ }
+ });
+ matchList.appendChild(currFrameDiv);
+ }
+ }
+
+ return container;
+}
+
+function parseDump(log, displayList, compositeTitle, compositeTime) {
+ compositeTitle |= "";
+ compositeTime |= 0
+
+ var container = createElement("div", {
+ style: {
+ background: "white",
+ height: "100%",
+ position: "relative",
+ },
+ });
+
+ if (compositeTitle == null && compositeTime == null) {
+ var titleDiv = createElement("div", {
+ className: "treeColumnHeader",
+ style: {
+ width: "100%",
+ },
+ textContent: compositeTitle + (compositeTitle ? " (near " + compositeTime.toFixed(0) + " ms)" : ""),
+ });
+ container.appendChild(titleDiv);
+ }
+
+ var mainDiv = createElement("div", {
+ style: {
+ position: "absolute",
+ top: "16px",
+ left: "0px",
+ right: "0px",
+ bottom: "0px",
+ },
+ });
+ container.appendChild(mainDiv);
+
+ var layerListPane = createElement("div", {
+ style: {
+ cssFloat: "left",
+ height: "100%",
+ width: "300px",
+ overflowY: "scroll",
+ },
+ });
+ mainDiv.appendChild(layerListPane);
+
+ var previewDiv = createElement("div", {
+ style: {
+ position: "absolute",
+ left: "300px",
+ right: "0px",
+ top: "0px",
+ bottom: "0px",
+ overflow: "auto",
+ },
+ });
+ mainDiv.appendChild(previewDiv);
+
+ var root = parseLayers(log);
+ populateLayers(root, displayList, layerListPane, previewDiv);
+ return container;
+}
+
+function tab_showLayersDump(layersDumpLines, compositeTitle, compositeTime) {
+ var container = parseDump(layersDumpLines, compositeTitle, compositeTime);
+
+ gTabWidget.addTab("LayerTree", container);
+ gTabWidget.selectTab("LayerTree");
+}
+
diff --git a/system/graphics/layers/layerviewer/noise.png b/system/graphics/layers/layerviewer/noise.png
new file mode 100644
index 000000000..01d340aaa
--- /dev/null
+++ b/system/graphics/layers/layerviewer/noise.png
Binary files differ
diff --git a/system/graphics/layers/layerviewer/show.png b/system/graphics/layers/layerviewer/show.png
new file mode 100644
index 000000000..7038b660c
--- /dev/null
+++ b/system/graphics/layers/layerviewer/show.png
Binary files differ
diff --git a/system/graphics/layers/layerviewer/tree.css b/system/graphics/layers/layerviewer/tree.css
new file mode 100644
index 000000000..6b26d729b
--- /dev/null
+++ b/system/graphics/layers/layerviewer/tree.css
@@ -0,0 +1,36 @@
+html, body {
+ height: 100%;
+ overflow: hidden;
+}
+.layerObjectDescription:hover {
+ background-color: #E8E8E8;
+}
+
+.layerHover > .layerPreview::after {
+ position: absolute;
+ top: 0; right: 0; bottom: 0; left: 0;
+ background-color: inherit;
+ content: "";
+ background-color: rgba(0,0,0,0.2);
+ box-shadow: -2px 2px 0 #FFF;
+}
+
+@keyframes layerHoverAnimation {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.2); }
+ 100% { transform: scale(1); }
+}
+
+.displayHover {
+ background: rgba(0, 128, 0, 0.8);
+}
+
+.layerHover > .layerPreview {
+ animation: layerHoverAnimation 200ms;
+ animation-transform-origin: 50% 50%;
+ background: gold !important;
+ box-shadow: 10px 10px 5px #888888;
+ border-color: blue !important;
+ z-index: 10;
+}
+
diff --git a/system/graphics/layers/moz.build b/system/graphics/layers/moz.build
new file mode 100644
index 000000000..11d6ee7a6
--- /dev/null
+++ b/system/graphics/layers/moz.build
@@ -0,0 +1,400 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('apz/**'):
+ BUG_COMPONENT = ('Core', 'Panning and Zooming')
+
+EXPORTS += [
+ 'basic/BasicCanvasLayer.h',
+ 'basic/BasicImplData.h',
+ 'basic/BasicLayers.h',
+ 'basic/BasicLayersImpl.h',
+ 'basic/BasicPaintedLayer.h',
+ 'client/ClientCanvasLayer.h',
+ 'client/ClientContainerLayer.h',
+ 'client/ClientLayerManager.h',
+ 'client/ClientPaintedLayer.h',
+ 'client/ClientTiledPaintedLayer.h',
+ 'composite/CompositableHost.h',
+ 'composite/ImageHost.h',
+ 'CompositorTypes.h',
+ 'CopyableCanvasLayer.h',
+ 'D3D9SurfaceImage.h',
+ 'FrameMetrics.h',
+ 'GLImages.h',
+ 'GPUVideoImage.h',
+ 'ImageContainer.h',
+ 'ImageLayers.h',
+ 'ImageTypes.h',
+ 'IMFYCbCrImage.h',
+ 'ipc/ThreadSafeRefcountingWithMainThreadDestruction.h',
+ 'Layers.h',
+ 'LayerScope.h',
+ 'LayersLogging.h',
+ 'LayerSorter.h',
+ 'LayersTypes.h',
+ 'LayerTreeInvalidation.h',
+ 'LayerUserData.h',
+ 'opengl/Composer2D.h',
+ 'opengl/OGLShaderProgram.h',
+ 'opengl/TexturePoolOGL.h',
+ 'protobuf/LayerScopePacket.pb.h',
+ 'ReadbackLayer.h',
+ 'TiledLayerBuffer.h',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'D3D11ShareHandleImage.cpp',
+ 'D3D9SurfaceImage.cpp',
+ 'IMFYCbCrImage.cpp',
+ 'TextureDIB.cpp',
+ ]
+ EXPORTS.mozilla.layers += [
+ 'TextureDIB.h',
+ ]
+ if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
+ EXPORTS.mozilla.layers += [
+ 'd3d11/CompositorD3D11.h',
+ 'd3d11/ReadbackManagerD3D11.h',
+ 'd3d11/TextureD3D11.h',
+ ]
+ SOURCES += [
+ 'd3d11/CompositorD3D11.cpp',
+ 'd3d11/ReadbackManagerD3D11.cpp',
+ 'd3d11/TextureD3D11.cpp',
+ ]
+
+EXPORTS.gfxipc += [
+ 'ipc/ShadowLayerUtils.h',
+]
+
+EXPORTS.mozilla.dom += [
+ 'apz/util/CheckerboardReportService.h',
+]
+
+EXPORTS.mozilla.layers += [
+ 'apz/public/CompositorController.h',
+ 'apz/public/GeckoContentController.h',
+ 'apz/public/IAPZCTreeManager.h',
+ 'apz/public/MetricsSharingController.h',
+ # exporting things from apz/src is temporary until we extract a
+ # proper interface for the code there
+ 'apz/src/APZCTreeManager.h',
+ 'apz/src/APZUtils.h',
+ 'apz/src/AsyncDragMetrics.h',
+ 'apz/src/AsyncPanZoomAnimation.h',
+ 'apz/src/TouchCounter.h',
+ 'apz/testutil/APZTestData.h',
+ 'apz/util/ActiveElementManager.h',
+ 'apz/util/APZCCallbackHelper.h',
+ 'apz/util/APZEventState.h',
+ 'apz/util/APZThreadUtils.h',
+ 'apz/util/ChromeProcessController.h',
+ 'apz/util/ContentProcessController.h',
+ 'apz/util/DoubleTapToZoom.h',
+ 'apz/util/InputAPZContext.h',
+ 'apz/util/ScrollInputMethods.h',
+ 'apz/util/ScrollLinkedEffectDetector.h',
+ 'apz/util/TouchActionHelper.h',
+ 'AsyncCanvasRenderer.h',
+ 'AtomicRefCountedWithFinalize.h',
+ 'AxisPhysicsModel.h',
+ 'AxisPhysicsMSDModel.h',
+ 'basic/BasicCompositor.h',
+ 'basic/TextureHostBasic.h',
+ 'BSPTree.h',
+ 'BufferTexture.h',
+ 'client/CanvasClient.h',
+ 'client/CompositableChild.h',
+ 'client/CompositableClient.h',
+ 'client/ContentClient.h',
+ 'client/GPUVideoTextureClient.h',
+ 'client/ImageClient.h',
+ 'client/SingleTiledContentClient.h',
+ 'client/TextureClient.h',
+ 'client/TextureClientPool.h',
+ 'client/TextureClientRecycleAllocator.h',
+ 'client/TextureClientSharedSurface.h',
+ 'client/TiledContentClient.h',
+ 'composite/AsyncCompositionManager.h',
+ 'composite/CanvasLayerComposite.h',
+ 'composite/ColorLayerComposite.h',
+ 'composite/ContainerLayerComposite.h',
+ 'composite/ContentHost.h',
+ 'composite/FrameUniformityData.h',
+ 'composite/GPUVideoTextureHost.h',
+ 'composite/ImageHost.h',
+ 'composite/ImageLayerComposite.h',
+ 'composite/LayerManagerComposite.h',
+ 'composite/PaintedLayerComposite.h',
+ 'composite/TextureHost.h',
+ 'composite/TiledContentHost.h',
+ 'Compositor.h',
+ 'CompositorTypes.h',
+ 'D3D11ShareHandleImage.h',
+ 'D3D9SurfaceImage.h',
+ 'Effects.h',
+ 'ImageDataSerializer.h',
+ 'ipc/APZChild.h',
+ 'ipc/APZCTreeManagerChild.h',
+ 'ipc/APZCTreeManagerParent.h',
+ 'ipc/CompositableForwarder.h',
+ 'ipc/CompositableTransactionParent.h',
+ 'ipc/CompositorBridgeChild.h',
+ 'ipc/CompositorBridgeParent.h',
+ 'ipc/CompositorThread.h',
+ 'ipc/CrossProcessCompositorBridgeParent.h',
+ 'ipc/GonkNativeHandle.h',
+ 'ipc/GonkNativeHandleUtils.h',
+ 'ipc/ImageBridgeChild.h',
+ 'ipc/ImageBridgeParent.h',
+ 'ipc/ImageContainerChild.h',
+ 'ipc/ImageContainerParent.h',
+ 'ipc/ISurfaceAllocator.h',
+ 'ipc/KnowsCompositor.h',
+ 'ipc/LayerAnimationUtils.h',
+ 'ipc/LayerTransactionChild.h',
+ 'ipc/LayerTransactionParent.h',
+ 'ipc/LayerTreeOwnerTracker.h',
+ 'ipc/RemoteContentController.h',
+ 'ipc/ShadowLayerChild.h',
+ 'ipc/ShadowLayers.h',
+ 'ipc/SharedPlanarYCbCrImage.h',
+ 'ipc/SharedRGBImage.h',
+ 'ipc/SynchronousTask.h',
+ 'ipc/TextureForwarder.h',
+ 'ipc/VideoBridgeChild.h',
+ 'ipc/VideoBridgeParent.h',
+ 'LayerMetricsWrapper.h',
+ 'LayersTypes.h',
+ 'opengl/CompositingRenderTargetOGL.h',
+ 'opengl/CompositorOGL.h',
+ 'opengl/TextureClientOGL.h',
+ 'opengl/TextureHostOGL.h',
+ 'PersistentBufferProvider.h',
+ 'RenderTrace.h',
+ 'TextureWrapperImage.h',
+ 'TransactionIdAllocator.h',
+]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS.mozilla.layers += [
+ 'basic/TextureClientX11.h',
+ 'basic/X11TextureSourceBasic.h',
+ 'composite/X11TextureHost.h',
+ 'ipc/ShadowLayerUtilsX11.h',
+ 'opengl/X11TextureSourceOGL.h',
+ ]
+ SOURCES += [
+ 'basic/TextureClientX11.cpp',
+ 'basic/X11BasicCompositor.cpp',
+ 'basic/X11TextureSourceBasic.cpp',
+ 'composite/X11TextureHost.cpp',
+ 'ipc/ShadowLayerUtilsX11.cpp',
+ 'opengl/X11TextureSourceOGL.cpp',
+ ]
+
+SOURCES += [
+ 'apz/public/IAPZCTreeManager.cpp',
+ 'apz/src/APZCTreeManager.cpp',
+ 'apz/src/AsyncPanZoomController.cpp',
+ 'apz/src/Axis.cpp',
+ 'apz/src/CheckerboardEvent.cpp',
+ 'apz/src/DragTracker.cpp',
+ 'apz/src/GestureEventListener.cpp',
+ 'apz/src/HitTestingTreeNode.cpp',
+ 'apz/src/InputBlockState.cpp',
+ 'apz/src/InputQueue.cpp',
+ 'apz/src/OverscrollHandoffState.cpp',
+ 'apz/src/PotentialCheckerboardDurationTracker.cpp',
+ 'apz/src/QueuedInput.cpp',
+ 'apz/src/TouchCounter.cpp',
+ 'apz/src/WheelScrollAnimation.cpp',
+ 'apz/testutil/APZTestData.cpp',
+ 'apz/util/ActiveElementManager.cpp',
+ 'apz/util/APZCCallbackHelper.cpp',
+ 'apz/util/APZEventState.cpp',
+ 'apz/util/APZThreadUtils.cpp',
+ 'apz/util/CheckerboardReportService.cpp',
+ 'apz/util/ChromeProcessController.cpp',
+ 'apz/util/ContentProcessController.cpp',
+ 'apz/util/DoubleTapToZoom.cpp',
+ 'apz/util/InputAPZContext.cpp',
+ 'apz/util/ScrollLinkedEffectDetector.cpp',
+ 'apz/util/TouchActionHelper.cpp',
+ 'AsyncCanvasRenderer.cpp',
+ 'AxisPhysicsModel.cpp',
+ 'AxisPhysicsMSDModel.cpp',
+ 'basic/BasicCanvasLayer.cpp',
+ 'basic/BasicColorLayer.cpp',
+ 'basic/BasicCompositor.cpp',
+ 'basic/BasicContainerLayer.cpp',
+ 'basic/BasicImageLayer.cpp',
+ 'basic/BasicImages.cpp',
+ 'basic/BasicLayerManager.cpp',
+ 'basic/BasicLayersImpl.cpp',
+ 'basic/BasicPaintedLayer.cpp',
+ 'basic/TextureHostBasic.cpp',
+ 'BSPTree.cpp',
+ 'BufferTexture.cpp',
+ 'BufferUnrotate.cpp',
+ 'client/CanvasClient.cpp',
+ 'client/ClientCanvasLayer.cpp',
+ 'client/ClientColorLayer.cpp',
+ 'client/ClientContainerLayer.cpp',
+ 'client/ClientImageLayer.cpp',
+ 'client/ClientLayerManager.cpp',
+ 'client/ClientPaintedLayer.cpp',
+ 'client/ClientTiledPaintedLayer.cpp',
+ 'client/CompositableChild.cpp',
+ 'client/CompositableClient.cpp',
+ 'client/ContentClient.cpp',
+ 'client/GPUVideoTextureClient.cpp',
+ 'client/ImageClient.cpp',
+ 'client/SingleTiledContentClient.cpp',
+ 'client/TextureClient.cpp',
+ 'client/TextureClientPool.cpp',
+ 'client/TextureClientRecycleAllocator.cpp',
+ 'client/TextureClientSharedSurface.cpp',
+ 'client/TiledContentClient.cpp',
+ 'composite/AsyncCompositionManager.cpp',
+ 'composite/CanvasLayerComposite.cpp',
+ 'composite/ColorLayerComposite.cpp',
+ 'composite/CompositableHost.cpp',
+ 'composite/ContainerLayerComposite.cpp',
+ 'composite/ContentHost.cpp',
+ 'composite/FPSCounter.cpp',
+ 'composite/FrameUniformityData.cpp',
+ 'composite/GPUVideoTextureHost.cpp',
+ 'composite/ImageHost.cpp',
+ 'composite/ImageLayerComposite.cpp',
+ 'composite/LayerManagerComposite.cpp',
+ 'composite/PaintedLayerComposite.cpp',
+ 'composite/TextRenderer.cpp',
+ 'composite/TextureHost.cpp',
+ 'composite/TiledContentHost.cpp',
+ 'Compositor.cpp',
+ 'CopyableCanvasLayer.cpp',
+ 'Effects.cpp',
+ 'FrameMetrics.cpp',
+ 'GLImages.cpp',
+ 'ImageContainer.cpp',
+ 'ImageDataSerializer.cpp',
+ 'ImageLayers.cpp',
+ 'ipc/APZChild.cpp',
+ 'ipc/APZCTreeManagerChild.cpp',
+ 'ipc/APZCTreeManagerParent.cpp',
+ 'ipc/CompositableForwarder.cpp',
+ 'ipc/CompositableTransactionParent.cpp',
+ 'ipc/CompositorBench.cpp',
+ 'ipc/CompositorBridgeChild.cpp',
+ 'ipc/CompositorThread.cpp',
+ 'ipc/ImageBridgeChild.cpp',
+ 'ipc/ImageBridgeParent.cpp',
+ 'ipc/ImageContainerChild.cpp',
+ 'ipc/ImageContainerParent.cpp',
+ 'ipc/ISurfaceAllocator.cpp',
+ 'ipc/LayerAnimationUtils.cpp',
+ 'ipc/LayerTransactionChild.cpp',
+ 'ipc/LayerTransactionParent.cpp',
+ 'ipc/LayerTreeOwnerTracker.cpp',
+ 'ipc/RemoteContentController.cpp',
+ 'ipc/ShadowLayerChild.cpp',
+ 'ipc/ShadowLayerParent.cpp',
+ 'ipc/ShadowLayers.cpp',
+ 'ipc/SharedPlanarYCbCrImage.cpp',
+ 'ipc/SharedRGBImage.cpp',
+ 'ipc/VideoBridgeChild.cpp',
+ 'ipc/VideoBridgeParent.cpp',
+ 'Layers.cpp',
+ 'LayerScope.cpp',
+ 'LayersLogging.cpp',
+ 'LayerSorter.cpp',
+ 'LayersTypes.cpp',
+ 'LayerTreeInvalidation.cpp',
+ 'opengl/CompositingRenderTargetOGL.cpp',
+ 'opengl/CompositorOGL.cpp',
+ 'opengl/GLBlitTextureImageHelper.cpp',
+ 'opengl/OGLShaderProgram.cpp',
+ 'opengl/TextureClientOGL.cpp',
+ 'opengl/TextureHostOGL.cpp',
+ 'opengl/TexturePoolOGL.cpp',
+ 'PersistentBufferProvider.cpp',
+ 'protobuf/LayerScopePacket.pb.cc',
+ 'ReadbackProcessor.cpp',
+ 'RenderTrace.cpp',
+ 'RotatedBuffer.cpp',
+ 'TextureWrapperImage.cpp',
+]
+
+# Implementation in CBP.cpp of things used in CPCBP.cpp
+# EraseLayerState, UpdateIndirectTree and map<uint64_t, CompositorBridgeParent::LayerTreeState>
+UNIFIED_SOURCES += [
+ 'ipc/CompositorBridgeParent.cpp',
+ 'ipc/CrossProcessCompositorBridgeParent.cpp',
+]
+
+# Disable RTTI in google protocol buffer
+DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
+
+# Workaround compiler bug (Bug 795594)
+if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64':
+ for src in [
+ 'Layers.cpp',
+ 'LayerTreeInvalidation.cpp',
+ ]:
+ SOURCES[src].no_pgo = True
+
+IPDL_SOURCES = [
+ 'ipc/LayersMessages.ipdlh',
+ 'ipc/LayersSurfaces.ipdlh',
+ 'ipc/PAPZ.ipdl',
+ 'ipc/PAPZCTreeManager.ipdl',
+ 'ipc/PCompositable.ipdl',
+ 'ipc/PCompositorBridge.ipdl',
+ 'ipc/PImageBridge.ipdl',
+ 'ipc/PImageContainer.ipdl',
+ 'ipc/PLayer.ipdl',
+ 'ipc/PLayerTransaction.ipdl',
+ 'ipc/PTexture.ipdl',
+ 'ipc/PVideoBridge.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/layout/base', # for TouchManager.h
+ '/layout/generic', # for nsTextFrame.h
+ '/libs/libyuv/include', # for libyuv.h
+ '/system/docshell/base', # for nsDocShell.h
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_DEBUG']:
+ DEFINES['D3D_DEBUG_INFO'] = True
+
+if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
+ DEFINES['MOZ_ENABLE_D3D10_LAYER'] = True
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['apz/test/gtest']
+
+MOCHITEST_MANIFESTS += ['apz/test/mochitest/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['apz/test/mochitest/chrome.ini']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'composite/PaintCounter.cpp',
+ ]
diff --git a/system/graphics/layers/opengl/Composer2D.h b/system/graphics/layers/opengl/Composer2D.h
new file mode 100644
index 000000000..a7cdcea36
--- /dev/null
+++ b/system/graphics/layers/opengl/Composer2D.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Composer2D_h
+#define mozilla_layers_Composer2D_h
+
+#include "gfxTypes.h"
+#include "nsISupportsImpl.h"
+
+/**
+ * Many platforms have dedicated hardware for simple composition.
+ * This hardware is usually faster or more power efficient than the
+ * GPU. However, in exchange for this better performance, generality
+ * has to be sacrificed: no 3d transforms, no intermediate surfaces,
+ * no special shader effects, loss of other goodies depending on the
+ * platform.
+ *
+ * Composer2D is a very simple interface to this class of hardware
+ * that allows an implementation to "try rendering" with the fast
+ * path. If the given layer tree requires more generality than the
+ * hardware provides, the implementation should bail and have the
+ * layer manager fall back on full GPU composition.
+ */
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+class Composer2D {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Composer2D)
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Composer2D() {}
+
+public:
+ /**
+ * Return true if |aRoot| met the implementation's criteria for fast
+ * composition and the render was successful. Return false to fall
+ * back on the GPU.
+ *
+ * Currently, when TryRender() returns true, the entire framebuffer
+ * must have been rendered.
+ */
+ virtual bool TryRenderWithHwc(Layer* aRoot,
+ nsIWidget* aWidget,
+ bool aGeometryChanged,
+ bool aHasImageHostOverlays) = 0;
+
+ /**
+ * Return true if Composer2D does composition. Return false if Composer2D
+ * failed the composition.
+ */
+ virtual bool Render(nsIWidget* aWidget) = 0;
+
+ /**
+ * Return true if Composer2D has a fast composition hardware.
+ * Return false if Composer2D does not have a fast composition hardware.
+ */
+ virtual bool HasHwc() = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_Composer2D_h
diff --git a/system/graphics/layers/opengl/CompositingRenderTargetOGL.cpp b/system/graphics/layers/opengl/CompositingRenderTargetOGL.cpp
new file mode 100644
index 000000000..a1521c56d
--- /dev/null
+++ b/system/graphics/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositingRenderTargetOGL.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+CompositingRenderTargetOGL::~CompositingRenderTargetOGL()
+{
+ if (mGL && mGL->MakeCurrent()) {
+ mGL->fDeleteTextures(1, &mTextureHandle);
+ mGL->fDeleteFramebuffers(1, &mFBO);
+ }
+}
+
+void
+CompositingRenderTargetOGL::BindTexture(GLenum aTextureUnit, GLenum aTextureTarget)
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ MOZ_ASSERT(mTextureHandle != 0);
+ mGL->fActiveTexture(aTextureUnit);
+ mGL->fBindTexture(aTextureTarget, mTextureHandle);
+}
+
+void
+CompositingRenderTargetOGL::BindRenderTarget()
+{
+ bool needsClear = false;
+
+ if (mInitParams.mStatus != InitParams::INITIALIZED) {
+ InitializeImpl();
+ if (mInitParams.mInit == INIT_MODE_CLEAR) {
+ needsClear = true;
+ mClearOnBind = false;
+ }
+ } else {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ // The main framebuffer (0) of non-offscreen contexts
+ // might be backed by a EGLSurface that needs to be renewed.
+ if (mFBO == 0 && !mGL->IsOffscreen()) {
+ mGL->RenewSurface(mCompositor->GetWidget()->RealWidget());
+ result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ }
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Framebuffer not complete -- CheckFramebufferStatus returned 0x%x, "
+ "GLContext=%p, IsOffscreen()=%d, mFBO=%d, aFBOTextureTarget=0x%x, "
+ "aRect.width=%d, aRect.height=%d",
+ result, mGL.get(), mGL->IsOffscreen(), mFBO, mInitParams.mFBOTextureTarget,
+ mInitParams.mSize.width, mInitParams.mSize.height);
+ NS_WARNING(msg.get());
+ }
+ }
+
+ needsClear = mClearOnBind;
+ }
+
+ if (needsClear) {
+ ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGL, 0, 0, mInitParams.mSize.width,
+ mInitParams.mSize.height);
+ mGL->fClearColor(0.0, 0.0, 0.0, 0.0);
+ mGL->fClearDepth(0.0);
+ mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+#ifdef MOZ_DUMP_PAINTING
+already_AddRefed<DataSourceSurface>
+CompositingRenderTargetOGL::Dump(Compositor* aCompositor)
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL();
+ return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat());
+}
+#endif
+
+void
+CompositingRenderTargetOGL::InitializeImpl()
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
+
+ //TODO: call mGL->GetBackbufferFB(), use that
+ GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ mInitParams.mFBOTextureTarget,
+ mTextureHandle,
+ 0);
+
+ // Making this call to fCheckFramebufferStatus prevents a crash on
+ // PowerVR. See bug 695246.
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Framebuffer not complete -- error 0x%x, aFBOTextureTarget 0x%x, mFBO %d, mTextureHandle %d, aRect.width %d, aRect.height %d",
+ result, mInitParams.mFBOTextureTarget, mFBO, mTextureHandle, mInitParams.mSize.width, mInitParams.mSize.height);
+ NS_ERROR(msg.get());
+ }
+
+ mInitParams.mStatus = InitParams::INITIALIZED;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/CompositingRenderTargetOGL.h b/system/graphics/layers/opengl/CompositingRenderTargetOGL.h
new file mode 100644
index 000000000..071dc5cac
--- /dev/null
+++ b/system/graphics/layers/opengl/CompositingRenderTargetOGL.h
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+#define MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_FRAMEBUFFER, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize, IntSizeTyped
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_WARNING
+#include "nsString.h" // for nsAutoCString
+
+
+namespace mozilla {
+namespace gl {
+ class BindableTexture;
+} // namespace gl
+namespace gfx {
+ class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class TextureSource;
+
+class CompositingRenderTargetOGL : public CompositingRenderTarget
+{
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class CompositorOGL;
+
+ // For lazy initialisation of the GL stuff
+ struct InitParams
+ {
+ InitParams() : mStatus(NO_PARAMS) {}
+ InitParams(const gfx::IntSize& aSize,
+ const gfx::IntSize& aPhySize,
+ GLenum aFBOTextureTarget,
+ SurfaceInitMode aInit)
+ : mStatus(READY)
+ , mSize(aSize)
+ , mPhySize(aPhySize)
+ , mFBOTextureTarget(aFBOTextureTarget)
+ , mInit(aInit)
+ {}
+
+ enum {
+ NO_PARAMS,
+ READY,
+ INITIALIZED
+ } mStatus;
+ /*
+ * Users of render target would draw in logical size, but it is
+ * actually drawn to a surface in physical size. GL surfaces have
+ * a limitation on their size, a smaller surface would be
+ * allocated for the render target if the caller requests in a
+ * size too big.
+ */
+ gfx::IntSize mSize; // Logical size, the expected by callers.
+ gfx::IntSize mPhySize; // Physical size, the real size of the surface.
+ GLenum mFBOTextureTarget;
+ SurfaceInitMode mInit;
+ };
+
+public:
+ CompositingRenderTargetOGL(CompositorOGL* aCompositor, const gfx::IntPoint& aOrigin,
+ GLuint aTexure, GLuint aFBO)
+ : CompositingRenderTarget(aOrigin)
+ , mInitParams()
+ , mCompositor(aCompositor)
+ , mGL(aCompositor->gl())
+ , mTextureHandle(aTexure)
+ , mFBO(aFBO)
+ {
+ MOZ_ASSERT(mGL);
+ }
+
+ ~CompositingRenderTargetOGL();
+
+ virtual const char* Name() const override { return "CompositingRenderTargetOGL"; }
+
+ /**
+ * Create a render target around the default FBO, for rendering straight to
+ * the window.
+ */
+ static already_AddRefed<CompositingRenderTargetOGL>
+ RenderTargetForWindow(CompositorOGL* aCompositor,
+ const gfx::IntSize& aSize)
+ {
+ RefPtr<CompositingRenderTargetOGL> result
+ = new CompositingRenderTargetOGL(aCompositor, gfx::IntPoint(), 0, 0);
+ result->mInitParams = InitParams(aSize, aSize, 0, INIT_MODE_NONE);
+ result->mInitParams.mStatus = InitParams::INITIALIZED;
+ return result.forget();
+ }
+
+ /**
+ * Some initialisation work on the backing FBO and texture.
+ * We do this lazily so that when we first set this render target on the
+ * compositor we do not have to re-bind the FBO after unbinding it, or
+ * alternatively leave the FBO bound after creation.
+ */
+ void Initialize(const gfx::IntSize& aSize,
+ const gfx::IntSize& aPhySize,
+ GLenum aFBOTextureTarget,
+ SurfaceInitMode aInit)
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::NO_PARAMS, "Initialized twice?");
+ // postpone initialization until we actually want to use this render target
+ mInitParams = InitParams(aSize, aPhySize, aFBOTextureTarget, aInit);
+ }
+
+ void BindTexture(GLenum aTextureUnit, GLenum aTextureTarget);
+
+ /**
+ * Call when we want to draw into our FBO
+ */
+ void BindRenderTarget();
+
+ bool IsWindow() { return GetFBO() == 0; }
+
+ GLuint GetFBO() const
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ return mFBO;
+ }
+
+ GLuint GetTextureHandle() const
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ return mTextureHandle;
+ }
+
+ // TextureSourceOGL
+ TextureSourceOGL* AsSourceOGL() override
+ {
+ // XXX - Bug 900770
+ MOZ_ASSERT(false, "CompositingRenderTargetOGL should not be used as a TextureSource");
+ return nullptr;
+ }
+ gfx::IntSize GetSize() const override
+ {
+ return mInitParams.mSize;
+ }
+
+ gfx::SurfaceFormat GetFormat() const override
+ {
+ // XXX - Should it be implemented ? is the above assert true ?
+ MOZ_ASSERT(false, "Not implemented");
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) override;
+#endif
+
+ const gfx::IntSize& GetInitSize() const {
+ return mInitParams.mSize;
+ }
+
+private:
+ /**
+ * Actually do the initialisation. Note that we leave our FBO bound, and so
+ * calling this method is only suitable when about to use this render target.
+ */
+ void InitializeImpl();
+
+ InitParams mInitParams;
+ /**
+ * There is temporary a cycle between the compositor and the render target,
+ * each having a strong ref to the other. The compositor's reference to
+ * the target is always cleared at the end of a frame.
+ */
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLContext> mGL;
+ GLuint mTextureHandle;
+ GLuint mFBO;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SURFACEOGL_H */
diff --git a/system/graphics/layers/opengl/CompositorOGL.cpp b/system/graphics/layers/opengl/CompositorOGL.cpp
new file mode 100644
index 000000000..6e5a01100
--- /dev/null
+++ b/system/graphics/layers/opengl/CompositorOGL.cpp
@@ -0,0 +1,1890 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorOGL.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t
+#include <stdlib.h> // for free, malloc
+#include "GLContextProvider.h" // for GLContextProvider
+#include "GLContext.h" // for GLContext
+#include "GLUploadHelpers.h"
+#include "Layers.h" // for WriteSnapshotToDumpFile
+#include "LayerScope.h" // for LayerScope
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils, etc
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/gfxVars.h" // for gfxVars
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/CompositingRenderTargetOGL.h"
+#include "mozilla/layers/Effects.h" // for EffectChain, TexturedEffect, etc
+#include "mozilla/layers/TextureHost.h" // for TextureSource, etc
+#include "mozilla/layers/TextureHostOGL.h" // for TextureSourceOGL, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAppRunner.h"
+#include "nsAString.h"
+#include "nsIConsoleService.h" // for nsIConsoleService, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLiteralString.h" // for NS_LITERAL_STRING
+#include "nsMathUtils.h" // for NS_roundf
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsServiceManagerUtils.h" // for do_GetService
+#include "nsString.h" // for nsString, nsAutoCString, etc
+#include "ScopedGLHelpers.h"
+#include "GLReadTexImageHelper.h"
+#include "GLBlitTextureImageHelper.h"
+#include "HeapCopyOfStackArray.h"
+
+#include "GeckoProfiler.h"
+
+namespace mozilla {
+
+using namespace std;
+using namespace gfx;
+
+namespace layers {
+
+using namespace mozilla::gl;
+
+static const GLuint kCoordinateAttributeIndex = 0;
+static const GLuint kTexCoordinateAttributeIndex = 1;
+
+static void
+BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
+ GLenum aTexUnit, const gfx::Matrix4x4& aTransform)
+{
+ MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31);
+ aSourceMask->BindTexture(aTexUnit, gfx::SamplingFilter::LINEAR);
+ aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
+ aProgram->SetMaskLayerTransform(aTransform);
+}
+
+void
+CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit)
+{
+ MOZ_ASSERT(aBackdrop);
+
+ mGLContext->fActiveTexture(aTexUnit);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
+ mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+ aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
+}
+
+CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget,
+ int aSurfaceWidth, int aSurfaceHeight,
+ bool aUseExternalSurfaceSize)
+ : Compositor(aWidget, aParent)
+ , mWidgetSize(-1, -1)
+ , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
+ , mHasBGRA(0)
+ , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
+ , mFrameInProgress(false)
+ , mDestroyed(false)
+ , mViewportSize(0, 0)
+ , mCurrentProgram(nullptr)
+{
+ MOZ_COUNT_CTOR(CompositorOGL);
+}
+
+CompositorOGL::~CompositorOGL()
+{
+ MOZ_COUNT_DTOR(CompositorOGL);
+ Destroy();
+}
+
+already_AddRefed<mozilla::gl::GLContext>
+CompositorOGL::CreateContext()
+{
+ RefPtr<GLContext> context;
+
+ // Used by mock widget to create an offscreen context
+ nsIWidget* widget = mWidget->RealWidget();
+ void* widgetOpenGLContext = widget ? widget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT) : nullptr;
+ if (widgetOpenGLContext) {
+ GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
+ return already_AddRefed<GLContext>(alreadyRefed);
+ }
+
+#ifdef XP_WIN
+ if (gfxEnv::LayersPreferEGL()) {
+ printf_stderr("Trying GL layers...\n");
+ context = gl::GLContextProviderEGL::CreateForCompositorWidget(mWidget, false);
+ }
+#endif
+
+ // Allow to create offscreen GL context for main Layer Manager
+ if (!context && gfxEnv::LayersPreferOffscreen()) {
+ SurfaceCaps caps = SurfaceCaps::ForRGB();
+ caps.preserve = false;
+ caps.bpp16 = gfxVars::OffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
+
+ nsCString discardFailureId;
+ context = GLContextProvider::CreateOffscreen(mSurfaceSize,
+ caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+ &discardFailureId);
+ }
+
+ if (!context) {
+ context = gl::GLContextProvider::CreateForCompositorWidget(mWidget,
+ gfxVars::RequiresAcceleratedGLContextForCompositorOGL());
+ }
+
+ if (!context) {
+ NS_WARNING("Failed to create CompositorOGL context");
+ }
+
+ return context.forget();
+}
+
+void
+CompositorOGL::Destroy()
+{
+ Compositor::Destroy();
+
+ if (mTexturePool) {
+ mTexturePool->Clear();
+ mTexturePool = nullptr;
+ }
+
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void
+CompositorOGL::CleanupResources()
+{
+ if (!mGLContext)
+ return;
+
+ RefPtr<GLContext> ctx = mGLContext->GetSharedContext();
+ if (!ctx) {
+ ctx = mGLContext;
+ }
+
+ if (!ctx->MakeCurrent()) {
+ // Leak resources!
+ mQuadVBO = 0;
+ mTriangleVBO = 0;
+ mGLContext = nullptr;
+ mPrograms.clear();
+ return;
+ }
+
+ for (std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.begin();
+ iter != mPrograms.end();
+ iter++) {
+ delete iter->second;
+ }
+ mPrograms.clear();
+
+ ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mQuadVBO) {
+ ctx->fDeleteBuffers(1, &mQuadVBO);
+ mQuadVBO = 0;
+ }
+
+ if (mTriangleVBO) {
+ ctx->fDeleteBuffers(1, &mTriangleVBO);
+ mTriangleVBO = 0;
+ }
+
+ mGLContext->MakeCurrent();
+
+ mBlitTextureImageHelper = nullptr;
+
+ mContextStateTracker.DestroyOGL(mGLContext);
+
+ // On the main thread the Widget will be destroyed soon and calling MakeCurrent
+ // after that could cause a crash (at least with GLX, see bug 1059793), unless
+ // context is marked as destroyed.
+ // There may be some textures still alive that will try to call MakeCurrent on
+ // the context so let's make sure it is marked destroyed now.
+ mGLContext->MarkDestroyed();
+
+ mGLContext = nullptr;
+}
+
+bool
+CompositorOGL::Initialize(nsCString* const out_failureReason)
+{
+ // Do not allow double initialization
+ MOZ_ASSERT(mGLContext == nullptr, "Don't reinitialize CompositorOGL");
+
+ mGLContext = CreateContext();
+
+ if (!mGLContext){
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_CREATE_CONTEXT";
+ return false;
+ }
+
+ MakeCurrent();
+
+ mHasBGRA =
+ mGLContext->IsExtensionSupported(gl::GLContext::EXT_texture_format_BGRA8888) ||
+ mGLContext->IsExtensionSupported(gl::GLContext::EXT_bgra);
+
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ // initialise a common shader to check that we can actually compile a shader
+ RefPtr<EffectSolidColor> effect = new EffectSolidColor(Color(0, 0, 0, 0));
+ ShaderConfigOGL config = GetShaderConfigFor(effect);
+ if (!GetShaderProgramFor(config)) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_COMPILE_SHADER";
+ return false;
+ }
+
+ if (mGLContext->WorkAroundDriverBugs()) {
+ /**
+ * We'll test the ability here to bind NPOT textures to a framebuffer, if
+ * this fails we'll try ARB_texture_rectangle.
+ */
+
+ GLenum textureTargets[] = {
+ LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_NONE
+ };
+
+ if (!mGLContext->IsGLES()) {
+ // No TEXTURE_RECTANGLE_ARB available on ES2
+ textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
+ }
+
+ mFBOTextureTarget = LOCAL_GL_NONE;
+
+ GLuint testFBO = 0;
+ mGLContext->fGenFramebuffers(1, &testFBO);
+ GLuint testTexture = 0;
+
+ for (uint32_t i = 0; i < ArrayLength(textureTargets); i++) {
+ GLenum target = textureTargets[i];
+ if (!target)
+ continue;
+
+ mGLContext->fGenTextures(1, &testTexture);
+ mGLContext->fBindTexture(target, testTexture);
+ mGLContext->fTexParameteri(target,
+ LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexParameteri(target,
+ LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexImage2D(target,
+ 0,
+ LOCAL_GL_RGBA,
+ 5, 3, /* sufficiently NPOT */
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+
+ // unbind this texture, in preparation for binding it to the FBO
+ mGLContext->fBindTexture(target, 0);
+
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, testFBO);
+ mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ target,
+ testTexture,
+ 0);
+
+ if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) ==
+ LOCAL_GL_FRAMEBUFFER_COMPLETE)
+ {
+ mFBOTextureTarget = target;
+ mGLContext->fDeleteTextures(1, &testTexture);
+ break;
+ }
+
+ mGLContext->fDeleteTextures(1, &testTexture);
+ }
+
+ if (testFBO) {
+ mGLContext->fDeleteFramebuffers(1, &testFBO);
+ }
+
+ if (mFBOTextureTarget == LOCAL_GL_NONE) {
+ /* Unable to find a texture target that works with FBOs and NPOT textures */
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_TEXTURE_TARGET";
+ return false;
+ }
+ } else {
+ // not trying to work around driver bugs, so TEXTURE_2D should just work
+ mFBOTextureTarget = LOCAL_GL_TEXTURE_2D;
+ }
+
+ // back to default framebuffer, to avoid confusion
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
+ /* If we're using TEXTURE_RECTANGLE, then we must have the ARB
+ * extension -- the EXT variant does not provide support for
+ * texture rectangle access inside GLSL (sampler2DRect,
+ * texture2DRect).
+ */
+ if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle)){
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_ARB_EXT";
+ return false;
+ }
+ }
+
+ // Create a VBO for triangle vertices.
+ mGLContext->fGenBuffers(1, &mTriangleVBO);
+
+ /* Create a simple quad VBO */
+ mGLContext->fGenBuffers(1, &mQuadVBO);
+
+ // 4 quads, with the number of the quad (vertexID) encoded in w.
+ GLfloat vertices[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f, 0.0f,
+
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 0.0f, 1.0f,
+
+ 0.0f, 0.0f, 0.0f, 2.0f,
+ 1.0f, 0.0f, 0.0f, 2.0f,
+ 0.0f, 1.0f, 0.0f, 2.0f,
+ 1.0f, 0.0f, 0.0f, 2.0f,
+ 0.0f, 1.0f, 0.0f, 2.0f,
+ 1.0f, 1.0f, 0.0f, 2.0f,
+
+ 0.0f, 0.0f, 0.0f, 3.0f,
+ 1.0f, 0.0f, 0.0f, 3.0f,
+ 0.0f, 1.0f, 0.0f, 3.0f,
+ 1.0f, 0.0f, 0.0f, 3.0f,
+ 0.0f, 1.0f, 0.0f, 3.0f,
+ 1.0f, 1.0f, 0.0f, 3.0f,
+ };
+ HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+ verticesOnHeap.ByteLength(),
+ verticesOnHeap.Data(),
+ LOCAL_GL_STATIC_DRAW);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ nsCOMPtr<nsIConsoleService>
+ console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+ if (console) {
+ nsString msg;
+ msg +=
+ NS_LITERAL_STRING("OpenGL compositor Initialized Succesfully.\nVersion: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VERSION)));
+ msg += NS_LITERAL_STRING("\nVendor: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VENDOR)));
+ msg += NS_LITERAL_STRING("\nRenderer: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_RENDERER)));
+ msg += NS_LITERAL_STRING("\nFBO Texture Target: ");
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D)
+ msg += NS_LITERAL_STRING("TEXTURE_2D");
+ else
+ msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
+ console->LogStringMessage(msg.get());
+ }
+
+ return true;
+}
+
+/*
+ * Returns a size that is equal to, or larger than and closest to,
+ * aSize where both width and height are powers of two.
+ * If the OpenGL setup is capable of using non-POT textures,
+ * then it will just return aSize.
+ */
+static IntSize
+CalculatePOTSize(const IntSize& aSize, GLContext* gl)
+{
+ if (CanUploadNonPowerOfTwo(gl))
+ return aSize;
+
+ return IntSize(RoundUpPow2(aSize.width), RoundUpPow2(aSize.height));
+}
+
+gfx::Rect
+CompositorOGL::GetTextureCoordinates(gfx::Rect textureRect, TextureSource* aTexture)
+{
+ // If the OpenGL setup does not support non-power-of-two textures then the
+ // texture's width and height will have been increased to the next
+ // power-of-two (unless already a power of two). In that case we must scale
+ // the texture coordinates to account for that.
+ if (!CanUploadNonPowerOfTwo(mGLContext)) {
+ const IntSize& textureSize = aTexture->GetSize();
+ const IntSize potSize = CalculatePOTSize(textureSize, mGLContext);
+ if (potSize != textureSize) {
+ const float xScale = (float)textureSize.width / (float)potSize.width;
+ const float yScale = (float)textureSize.height / (float)potSize.height;
+ textureRect.Scale(xScale, yScale);
+ }
+ }
+
+ return textureRect;
+}
+
+void
+CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget);
+ // Logical surface size.
+ const gfx::IntSize& size = aRenderTarget->mInitParams.mSize;
+ // Physical surface size.
+ const gfx::IntSize& phySize = aRenderTarget->mInitParams.mPhySize;
+
+ // Set the viewport correctly.
+ mGLContext->fViewport(0, 0, phySize.width, phySize.height);
+
+ mViewportSize = size;
+
+ if (!aRenderTarget->HasComplexProjection()) {
+ // We flip the view matrix around so that everything is right-side up; we're
+ // drawing directly into the window's back buffer, so this keeps things
+ // looking correct.
+ // XXX: We keep track of whether the window size changed, so we could skip
+ // this update if it hadn't changed since the last call.
+
+ // Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
+ // 2, 2) and flip the contents.
+ Matrix viewMatrix;
+ if (mGLContext->IsOffscreen() && !gIsGtest) {
+ // In case of rendering via GL Offscreen context, disable Y-Flipping
+ viewMatrix.PreTranslate(-1.0, -1.0);
+ viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
+ } else {
+ viewMatrix.PreTranslate(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+ }
+
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+ // If we're drawing directly to the window then we want to offset
+ // drawing by the render offset.
+ if (!mTarget && mCurrentRenderTarget->IsWindow()) {
+ viewMatrix.PreTranslate(mRenderOffset.x, mRenderOffset.y);
+ }
+
+ Matrix4x4 matrix3d = Matrix4x4::From2D(viewMatrix);
+ matrix3d._33 = 0.0f;
+ mProjMatrix = matrix3d;
+ mGLContext->fDepthRange(0.0f, 1.0f);
+ } else {
+ // XXX take into account mRenderOffset
+ bool depthEnable;
+ float zNear, zFar;
+ aRenderTarget->GetProjection(mProjMatrix, depthEnable, zNear, zFar);
+ mGLContext->fDepthRange(zNear, zFar);
+ }
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorOGL::CreateRenderTarget(const IntRect &aRect, SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!gl()) {
+ // CompositingRenderTargetOGL does not work without a gl context.
+ return nullptr;
+ }
+
+ GLuint tex = 0;
+ GLuint fbo = 0;
+ IntRect rect = aRect;
+ IntSize FBOSize;
+ CreateFBOWithTexture(rect, false, 0, &fbo, &tex, &FBOSize);
+ RefPtr<CompositingRenderTargetOGL> surface
+ = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo);
+ surface->Initialize(aRect.Size(), FBOSize, mFBOTextureTarget, aInit);
+ return surface.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorOGL::CreateRenderTargetFromSource(const IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const IntPoint &aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!gl()) {
+ return nullptr;
+ }
+
+ GLuint tex = 0;
+ GLuint fbo = 0;
+ const CompositingRenderTargetOGL* sourceSurface
+ = static_cast<const CompositingRenderTargetOGL*>(aSource);
+ IntRect sourceRect(aSourcePoint, aRect.Size());
+ if (aSource) {
+ CreateFBOWithTexture(sourceRect, true, sourceSurface->GetFBO(),
+ &fbo, &tex);
+ } else {
+ CreateFBOWithTexture(sourceRect, true, 0,
+ &fbo, &tex);
+ }
+
+ RefPtr<CompositingRenderTargetOGL> surface
+ = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo);
+ surface->Initialize(aRect.Size(),
+ sourceRect.Size(),
+ mFBOTextureTarget,
+ INIT_MODE_NONE);
+ return surface.forget();
+}
+
+void
+CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
+{
+ MOZ_ASSERT(aSurface);
+ CompositingRenderTargetOGL* surface
+ = static_cast<CompositingRenderTargetOGL*>(aSurface);
+ if (mCurrentRenderTarget != surface) {
+ mCurrentRenderTarget = surface;
+ if (mCurrentRenderTarget) {
+ mContextStateTracker.PopOGLSection(gl(), "Frame");
+ }
+ mContextStateTracker.PushOGLSection(gl(), "Frame");
+ surface->BindRenderTarget();
+ }
+
+ PrepareViewport(mCurrentRenderTarget);
+}
+
+CompositingRenderTarget*
+CompositorOGL::GetCurrentRenderTarget() const
+{
+ return mCurrentRenderTarget;
+}
+
+static GLenum
+GetFrameBufferInternalFormat(GLContext* gl,
+ GLuint aFrameBuffer,
+ mozilla::widget::CompositorWidget* aWidget)
+{
+ if (aFrameBuffer == 0) { // default framebuffer
+ return aWidget->GetGLFrameBufferFormat();
+ }
+ return LOCAL_GL_RGBA;
+}
+
+void
+CompositorOGL::ClearRect(const gfx::Rect& aRect)
+{
+ // Map aRect to OGL coordinates, origin:bottom-left
+ GLint y = mViewportSize.height - (aRect.y + aRect.height);
+
+ ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGLContext, aRect.x, y, aRect.width, aRect.height);
+ mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
+ mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+}
+
+void
+CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect *aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect *aClipRectOut,
+ IntRect *aRenderBoundsOut)
+{
+ PROFILER_LABEL("CompositorOGL", "BeginFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame");
+
+ gfx::IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = gfx::IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ rect = gfx::IntRect(aRenderBounds.x, aRenderBounds.y, aRenderBounds.width, aRenderBounds.height);
+ }
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = rect;
+ }
+
+ GLint width = rect.width;
+ GLint height = rect.height;
+
+ // We can't draw anything to something with no area
+ // so just return
+ if (width == 0 || height == 0)
+ return;
+
+ // We're about to actually draw a frame.
+ mFrameInProgress = true;
+
+ // If the widget size changed, we have to force a MakeCurrent
+ // to make sure that GL sees the updated widget size.
+ if (mWidgetSize.width != width ||
+ mWidgetSize.height != height)
+ {
+ MakeCurrent(ForceMakeCurrent);
+
+ mWidgetSize.width = width;
+ mWidgetSize.height = height;
+ } else {
+ MakeCurrent();
+ }
+
+ mPixelsPerFrame = width * height;
+ mPixelsFilled = 0;
+
+ // Default blend function implements "OVER"
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ RefPtr<CompositingRenderTargetOGL> rt =
+ CompositingRenderTargetOGL::RenderTargetForWindow(this,
+ IntSize(width, height));
+ SetRenderTarget(rt);
+
+#ifdef DEBUG
+ mWindowRenderTarget = mCurrentRenderTarget;
+#endif
+
+ if (aClipRectOut && !aClipRectIn) {
+ aClipRectOut->SetRect(0, 0, width, height);
+ }
+
+ mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b, mClearColor.a);
+ mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+}
+
+void
+CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect,
+ bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ GLuint *aFBO, GLuint *aTexture,
+ gfx::IntSize* aAllocSize)
+{
+ *aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer,
+ aAllocSize);
+ mGLContext->fGenFramebuffers(1, aFBO);
+}
+
+GLuint
+CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer, IntSize* aAllocSize)
+{
+ // we're about to create a framebuffer backed by textures to use as an intermediate
+ // surface. What to do if its size (as given by aRect) would exceed the
+ // maximum texture size supported by the GL? The present code chooses the compromise
+ // of just clamping the framebuffer's size to the max supported size.
+ // This gives us a lower resolution rendering of the intermediate surface (children layers).
+ // See bug 827170 for a discussion.
+ IntRect clampedRect = aRect;
+ int32_t maxTexSize = GetMaxTextureSize();
+ clampedRect.width = std::min(clampedRect.width, maxTexSize);
+ clampedRect.height = std::min(clampedRect.height, maxTexSize);
+
+ GLuint tex;
+
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+ mGLContext->fGenTextures(1, &tex);
+ mGLContext->fBindTexture(mFBOTextureTarget, tex);
+
+ if (aCopyFromSource) {
+ GLuint curFBO = mCurrentRenderTarget->GetFBO();
+ if (curFBO != aSourceFrameBuffer) {
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aSourceFrameBuffer);
+ }
+
+ // We're going to create an RGBA temporary fbo. But to
+ // CopyTexImage() from the current framebuffer, the framebuffer's
+ // format has to be compatible with the new texture's. So we
+ // check the format of the framebuffer here and take a slow path
+ // if it's incompatible.
+ GLenum format =
+ GetFrameBufferInternalFormat(gl(), aSourceFrameBuffer, mWidget);
+
+ bool isFormatCompatibleWithRGBA
+ = gl()->IsGLES() ? (format == LOCAL_GL_RGBA)
+ : true;
+
+ if (isFormatCompatibleWithRGBA) {
+ mGLContext->fCopyTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.x, FlipY(clampedRect.y + clampedRect.height),
+ clampedRect.width, clampedRect.height,
+ 0);
+ } else {
+ // Curses, incompatible formats. Take a slow path.
+
+ // RGBA
+ size_t bufferSize = clampedRect.width * clampedRect.height * 4;
+ auto buf = MakeUnique<uint8_t[]>(bufferSize);
+
+ mGLContext->fReadPixels(clampedRect.x, clampedRect.y,
+ clampedRect.width, clampedRect.height,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ buf.get());
+ mGLContext->fTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.width, clampedRect.height,
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ buf.get());
+ }
+
+ GLenum error = mGLContext->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Texture initialization failed! -- error 0x%x, Source %d, Source format %d, RGBA Compat %d",
+ error, aSourceFrameBuffer, format, isFormatCompatibleWithRGBA);
+ NS_ERROR(msg.get());
+ }
+ } else {
+ mGLContext->fTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.width, clampedRect.height,
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+ }
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fBindTexture(mFBOTextureTarget, 0);
+
+ if (aAllocSize) {
+ aAllocSize->width = clampedRect.width;
+ aAllocSize->height = clampedRect.height;
+ }
+
+ return tex;
+}
+
+ShaderConfigOGL
+CompositorOGL::GetShaderConfigFor(Effect *aEffect,
+ MaskType aMask,
+ gfx::CompositionOp aOp,
+ bool aColorMatrix,
+ bool aDEAAEnabled) const
+{
+ ShaderConfigOGL config;
+
+ switch(aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ config.SetRenderColor(true);
+ break;
+ case EffectTypes::YCBCR:
+ config.SetYCbCr(true);
+ break;
+ case EffectTypes::NV12:
+ config.SetNV12(true);
+ config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ config.SetComponentAlpha(true);
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffect);
+ gfx::SurfaceFormat format = effectComponentAlpha->mOnWhite->GetFormat();
+ config.SetRBSwap(format == gfx::SurfaceFormat::B8G8R8A8 ||
+ format == gfx::SurfaceFormat::B8G8R8X8);
+ TextureSourceOGL* source = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ config.SetTextureTarget(source->GetTextureTarget());
+ break;
+ }
+ case EffectTypes::RENDER_TARGET:
+ config.SetTextureTarget(mFBOTextureTarget);
+ break;
+ default:
+ {
+ MOZ_ASSERT(aEffect->mType == EffectTypes::RGB);
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffect);
+ TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
+ MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8);
+ MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16 ||
+ source->GetFormat() == gfx::SurfaceFormat::YUV422 );
+ config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
+ source->GetFormat());
+ if (!texturedEffect->mPremultiplied) {
+ config.SetNoPremultipliedAlpha();
+ }
+ break;
+ }
+ }
+ config.SetColorMatrix(aColorMatrix);
+ config.SetMask(aMask == MaskType::Mask);
+ config.SetDEAA(aDEAAEnabled);
+ config.SetCompositionOp(aOp);
+ return config;
+}
+
+ShaderProgramOGL*
+CompositorOGL::GetShaderProgramFor(const ShaderConfigOGL &aConfig)
+{
+ std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.find(aConfig);
+ if (iter != mPrograms.end())
+ return iter->second;
+
+ ProgramProfileOGL profile = ProgramProfileOGL::GetProfileFor(aConfig);
+ ShaderProgramOGL *shader = new ShaderProgramOGL(gl(), profile);
+ if (!shader->Initialize()) {
+ delete shader;
+ return nullptr;
+ }
+
+ mPrograms[aConfig] = shader;
+ return shader;
+}
+
+void
+CompositorOGL::ActivateProgram(ShaderProgramOGL* aProg)
+{
+ if (mCurrentProgram != aProg) {
+ gl()->fUseProgram(aProg->GetProgram());
+ mCurrentProgram = aProg;
+ }
+}
+
+void
+CompositorOGL::ResetProgram()
+{
+ mCurrentProgram = nullptr;
+}
+
+static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true)
+{
+ if (BlendOpIsMixBlendMode(aBlendMode)) {
+ // Mix-blend modes require an extra step (or more) that cannot be expressed
+ // in the fixed-function blending capabilities of opengl. We handle them
+ // separately in shaders, and the shaders assume we will use our default
+ // blend function for compositing (premultiplied OP_OVER).
+ return false;
+ }
+ if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
+ return false;
+ }
+
+ GLenum srcBlend;
+ GLenum dstBlend;
+ GLenum srcAlphaBlend = LOCAL_GL_ONE;
+ GLenum dstAlphaBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+
+ switch (aBlendMode) {
+ case gfx::CompositionOp::OP_OVER:
+ MOZ_ASSERT(!aIsPremultiplied);
+ srcBlend = LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case gfx::CompositionOp::OP_SOURCE:
+ srcBlend = aIsPremultiplied ? LOCAL_GL_ONE : LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ZERO;
+ srcAlphaBlend = LOCAL_GL_ONE;
+ dstAlphaBlend = LOCAL_GL_ZERO;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported blend mode!");
+ return false;
+ }
+
+ aGL->fBlendFuncSeparate(srcBlend, dstBlend,
+ srcAlphaBlend, dstAlphaBlend);
+ return true;
+}
+
+gfx::Point3D
+CompositorOGL::GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2)
+{
+ // Return standard coefficients for a line between aPoint1 and aPoint2
+ // for standard line equation:
+ //
+ // Ax + By + C = 0
+ //
+ // A = (p1.y – p2.y)
+ // B = (p2.x – p1.x)
+ // C = (p1.x * p2.y) – (p2.x * p1.y)
+
+ gfx::Point3D coeffecients;
+ coeffecients.x = aPoint1.y - aPoint2.y;
+ coeffecients.y = aPoint2.x - aPoint1.x;
+ coeffecients.z = aPoint1.x * aPoint2.y - aPoint2.x * aPoint1.y;
+
+ coeffecients *= 1.0f / sqrtf(coeffecients.x * coeffecients.x +
+ coeffecients.y * coeffecients.y);
+
+ // Offset outwards by 0.5 pixel as the edge is considered to be 1 pixel
+ // wide and included within the interior of the polygon
+ coeffecients.z += 0.5f;
+
+ return coeffecients;
+}
+
+void
+CompositorOGL::DrawQuad(const Rect& aRect,
+ const IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ PROFILER_LABEL("CompositorOGL", "DrawQuad",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ DrawGeometry(aRect, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+}
+
+void
+CompositorOGL::DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ PROFILER_LABEL("CompositorOGL", "DrawTriangle",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ DrawGeometry(aTriangle, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+}
+
+template<typename Geometry>
+void
+CompositorOGL::DrawGeometry(const Geometry& aGeometry,
+ const IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ MOZ_ASSERT(mFrameInProgress, "frame not started");
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+
+ MakeCurrent();
+
+ IntPoint offset = mCurrentRenderTarget->GetOrigin();
+ IntSize size = mCurrentRenderTarget->GetSize();
+
+ Rect renderBound(0, 0, size.width, size.height);
+ renderBound.IntersectRect(renderBound, Rect(aClipRect));
+ renderBound.MoveBy(offset);
+
+ Rect destRect = aTransform.TransformAndClipBounds(aGeometry, renderBound);
+
+ // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
+ // quads. Fix me.
+ mPixelsFilled += destRect.width * destRect.height;
+
+ // Do a simple culling if this rect is out of target buffer.
+ // Inflate a small size to avoid some numerical imprecision issue.
+ destRect.Inflate(1, 1);
+ destRect.MoveBy(-offset);
+ renderBound = Rect(0, 0, size.width, size.height);
+ if (!renderBound.Intersects(destRect)) {
+ return;
+ }
+
+ LayerScope::DrawBegin();
+
+ IntRect clipRect = aClipRect;
+ // aClipRect is in destination coordinate space (after all
+ // transforms and offsets have been applied) so if our
+ // drawing is going to be shifted by mRenderOffset then we need
+ // to shift the clip rect by the same amount.
+ if (!mTarget && mCurrentRenderTarget->IsWindow()) {
+ clipRect.MoveBy(mRenderOffset.x, mRenderOffset.y);
+ }
+
+ ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGLContext, clipRect.x, FlipY(clipRect.y + clipRect.height),
+ clipRect.width, clipRect.height);
+
+ MaskType maskType;
+ EffectMask* effectMask;
+ TextureSourceOGL* sourceMask = nullptr;
+ gfx::Matrix4x4 maskQuadTransform;
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ sourceMask = effectMask->mMaskTexture->AsSourceOGL();
+
+ // NS_ASSERTION(textureMask->IsAlpha(),
+ // "OpenGL mask layers must be backed by alpha surfaces");
+
+ // We're assuming that the gl backend won't cheat and use NPOT
+ // textures when glContext says it can't (which seems to happen
+ // on a mac when you force POT textures)
+ IntSize maskSize = CalculatePOTSize(effectMask->mSize, mGLContext);
+
+ const gfx::Matrix4x4& maskTransform = effectMask->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskSize));
+ bounds = maskTransform.As2D().TransformBounds(bounds);
+
+ maskQuadTransform._11 = 1.0f/bounds.width;
+ maskQuadTransform._22 = 1.0f/bounds.height;
+ maskQuadTransform._41 = float(-bounds.x)/bounds.width;
+ maskQuadTransform._42 = float(-bounds.y)/bounds.height;
+
+ maskType = MaskType::Mask;
+ } else {
+ maskType = MaskType::MaskNone;
+ }
+
+ // Determine the color if this is a color shader and fold the opacity into
+ // the color since color shaders don't have an opacity uniform.
+ Color color;
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::SOLID_COLOR) {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+ color = effectSolidColor->mColor;
+
+ Float opacity = aOpacity * color.a;
+ color.r *= opacity;
+ color.g *= opacity;
+ color.b *= opacity;
+ color.a = opacity;
+
+ // We can fold opacity into the color, so no need to consider it further.
+ aOpacity = 1.f;
+ }
+
+ bool createdMixBlendBackdropTexture = false;
+ GLuint mixBlendBackdrop = 0;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+ }
+
+ // Only apply DEAA to quads that have been transformed such that aliasing
+ // could be visible
+ bool bEnableAA = gfxPrefs::LayersDEAAEnabled() &&
+ !aTransform.Is2DIntegerTranslation();
+
+ bool colorMatrix = aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX];
+ ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect,
+ maskType, blendMode, colorMatrix,
+ bEnableAA);
+
+ config.SetOpacity(aOpacity != 1.f);
+ ApplyPrimitiveConfig(config, aGeometry);
+
+ ShaderProgramOGL *program = GetShaderProgramFor(config);
+ ActivateProgram(program);
+ program->SetProjectionMatrix(mProjMatrix);
+ program->SetLayerTransform(aTransform);
+ LayerScope::SetLayerTransform(aTransform);
+
+ if (colorMatrix) {
+ EffectColorMatrix* effectColorMatrix =
+ static_cast<EffectColorMatrix*>(aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX].get());
+ program->SetColorMatrix(effectColorMatrix->mColorMatrix);
+ }
+
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ gfx::Matrix4x4 backdropTransform;
+
+ if (gl()->IsExtensionSupported(GLContext::NV_texture_barrier)) {
+ // The NV_texture_barrier extension lets us read directly from the
+ // backbuffer. Let's do that.
+ // We need to tell OpenGL about this, so that it can make sure everything
+ // on the GPU is happening in the right order.
+ gl()->fTextureBarrier();
+ mixBlendBackdrop = mCurrentRenderTarget->GetTextureHandle();
+ } else {
+ gfx::IntRect rect = ComputeBackdropCopyRect(aGeometry, aClipRect,
+ aTransform, &backdropTransform);
+ mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
+ createdMixBlendBackdropTexture = true;
+ }
+ program->SetBackdropTransform(backdropTransform);
+ }
+
+ program->SetRenderOffset(offset.x, offset.y);
+ LayerScope::SetRenderOffset(offset.x, offset.y);
+
+ if (aOpacity != 1.f)
+ program->SetLayerOpacity(aOpacity);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ TextureSourceOGL* source = nullptr;
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::COMPONENT_ALPHA) {
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ source = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ } else {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ source = texturedEffect->mTexture->AsSourceOGL();
+ }
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
+ program->SetTexCoordMultiplier(source->GetSize().width, source->GetSize().height);
+ }
+
+ // XXX kip - These calculations could be performed once per layer rather than
+ // for every tile. This might belong in Compositor.cpp once DEAA
+ // is implemented for DirectX.
+ if (bEnableAA) {
+ // Calculate the transformed vertices of aVisibleRect in screen space
+ // pixels, mirroring the calculations in the vertex shader
+ Matrix4x4 flatTransform = aTransform;
+ flatTransform.PostTranslate(-offset.x, -offset.y, 0.0f);
+ flatTransform *= mProjMatrix;
+
+ Rect viewportClip = Rect(-1.0f, -1.0f, 2.0f, 2.0f);
+ size_t edgeCount = 0;
+ Point3D coefficients[4];
+
+ Point points[Matrix4x4::kTransformAndClipRectMaxVerts];
+ size_t pointCount = flatTransform.TransformAndClipRect(aVisibleRect, viewportClip, points);
+ for (size_t i = 0; i < pointCount; i++) {
+ points[i] = Point((points[i].x * 0.5f + 0.5f) * mViewportSize.width,
+ (points[i].y * 0.5f + 0.5f) * mViewportSize.height);
+ }
+ if (pointCount > 2) {
+ // Use shoelace formula on a triangle in the clipped quad to determine if
+ // winding order is reversed. Iterate through the triangles until one is
+ // found with a non-zero area.
+ float winding = 0.0f;
+ size_t wp = 0;
+ while (winding == 0.0f && wp < pointCount) {
+ int wp1 = (wp + 1) % pointCount;
+ int wp2 = (wp + 2) % pointCount;
+ winding = (points[wp1].x - points[wp].x) * (points[wp1].y + points[wp].y) +
+ (points[wp2].x - points[wp1].x) * (points[wp2].y + points[wp1].y) +
+ (points[wp].x - points[wp2].x) * (points[wp].y + points[wp2].y);
+ wp++;
+ }
+ bool frontFacing = winding >= 0.0f;
+
+ // Calculate the line coefficients used by the DEAA shader to determine the
+ // sub-pixel coverage of the edge pixels
+ for (size_t i=0; i<pointCount; i++) {
+ const Point& p1 = points[i];
+ const Point& p2 = points[(i + 1) % pointCount];
+ // Create a DEAA edge for any non-straight lines, to a maximum of 4
+ if (p1.x != p2.x && p1.y != p2.y && edgeCount < 4) {
+ if (frontFacing) {
+ coefficients[edgeCount++] = GetLineCoefficients(p2, p1);
+ } else {
+ coefficients[edgeCount++] = GetLineCoefficients(p1, p2);
+ }
+ }
+ }
+ }
+
+ // The coefficients that are not needed must not cull any fragments.
+ // We fill these unused coefficients with a clipping plane that has no
+ // effect.
+ for (size_t i = edgeCount; i < 4; i++) {
+ coefficients[i] = Point3D(0.0f, 1.0f, mViewportSize.height);
+ }
+
+ // Set uniforms required by DEAA shader
+ Matrix4x4 transformInverted = aTransform;
+ transformInverted.Invert();
+ program->SetLayerTransformInverse(transformInverted);
+ program->SetDEAAEdges(coefficients);
+ program->SetVisibleCenter(aVisibleRect.Center());
+ program->SetViewportSize(mViewportSize);
+ }
+
+ bool didSetBlendMode = false;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ program->SetRenderColor(color);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
+ }
+
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+
+ BindAndDrawGeometry(program, aGeometry);
+ }
+ break;
+
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource *source = texturedEffect->mTexture;
+
+ didSetBlendMode = SetBlendMode(gl(), blendMode, texturedEffect->mPremultiplied);
+
+ gfx::SamplingFilter samplingFilter = texturedEffect->mSamplingFilter;
+
+ source->AsSourceOGL()->BindTexture(LOCAL_GL_TEXTURE0, samplingFilter);
+
+ program->SetTextureUnit(0);
+
+ Matrix4x4 textureTransform = source->AsSourceOGL()->GetTextureTransform();
+ program->SetTextureTransform(textureTransform);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
+ }
+
+ BindAndDrawGeometryWithTextureRect(program, aGeometry,
+ texturedEffect->mTextureCoords, source);
+ }
+ break;
+ case EffectTypes::YCBCR: {
+ EffectYCbCr* effectYCbCr =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceYCbCr = effectYCbCr->mTexture;
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
+ TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCb || !sourceCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectYCbCr->mSamplingFilter);
+ sourceCb->BindTexture(LOCAL_GL_TEXTURE1, effectYCbCr->mSamplingFilter);
+ sourceCr->BindTexture(LOCAL_GL_TEXTURE2, effectYCbCr->mSamplingFilter);
+
+ program->SetYCbCrTextureUnits(Y, Cb, Cr);
+ program->SetTextureTransform(Matrix4x4());
+ program->SetYUVColorSpace(effectYCbCr->mYUVColorSpace);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
+ }
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectYCbCr->mTextureCoords,
+ sourceYCbCr->GetSubSource(Y));
+ }
+ break;
+ case EffectTypes::NV12: {
+ EffectNV12* effectNV12 =
+ static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceNV12 = effectNV12->mTexture;
+ const int Y = 0, CbCr = 1;
+ TextureSourceOGL* sourceY = sourceNV12->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCbCr = sourceNV12->GetSubSource(CbCr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCbCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectNV12->mSamplingFilter);
+ sourceCbCr->BindTexture(LOCAL_GL_TEXTURE1, effectNV12->mSamplingFilter);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
+ program->SetCbCrTexCoordMultiplier(sourceCbCr->GetSize().width, sourceCbCr->GetSize().height);
+ }
+
+ program->SetNV12TextureUnits(Y, CbCr);
+ program->SetTextureTransform(Matrix4x4());
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
+ }
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectNV12->mTextureCoords,
+ sourceNV12->GetSubSource(Y));
+ }
+ break;
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<CompositingRenderTargetOGL> surface
+ = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
+
+ surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget);
+
+ // Drawing is always flipped, but when copying between surfaces we want to avoid
+ // this, so apply a flip here to cancel the other one out.
+ Matrix transform;
+ transform.PreTranslate(0.0, 1.0);
+ transform.PreScale(1.0f, -1.0f);
+ program->SetTextureTransform(Matrix4x4::From2D(transform));
+ program->SetTextureUnit(0);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
+ }
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // 2DRect case, get the multiplier right for a sampler2DRect
+ program->SetTexCoordMultiplier(surface->GetSize().width,
+ surface->GetSize().height);
+ }
+
+ // Drawing is always flipped, but when copying between surfaces we want to avoid
+ // this. Pass true for the flip parameter to introduce a second flip
+ // that cancels the other one out.
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometry(program, aGeometry);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA: {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ MOZ_ASSERT(blendMode == gfx::CompositionOp::OP_OVER, "Can't support blend modes with component alpha!");
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceOGL* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ TextureSourceOGL* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceOGL();
+
+ if (!sourceOnBlack->IsValid() ||
+ !sourceOnWhite->IsValid()) {
+ NS_WARNING("Invalid layer texture for component alpha");
+ return;
+ }
+
+ sourceOnBlack->BindTexture(LOCAL_GL_TEXTURE0, effectComponentAlpha->mSamplingFilter);
+ sourceOnWhite->BindTexture(LOCAL_GL_TEXTURE1, effectComponentAlpha->mSamplingFilter);
+
+ program->SetBlackTextureUnit(0);
+ program->SetWhiteTextureUnit(1);
+ program->SetTextureTransform(Matrix4x4());
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
+ }
+ // Pass 1.
+ gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR,
+ LOCAL_GL_ONE, LOCAL_GL_ONE);
+ program->SetTexturePass2(false);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectComponentAlpha->mTextureCoords,
+ effectComponentAlpha->mOnBlack);
+
+ // Pass 2.
+ gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE,
+ LOCAL_GL_ONE, LOCAL_GL_ONE);
+ program->SetTexturePass2(true);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectComponentAlpha->mTextureCoords,
+ effectComponentAlpha->mOnBlack);
+
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ }
+ break;
+ default:
+ MOZ_ASSERT(false, "Unhandled effect type");
+ break;
+ }
+
+ if (didSetBlendMode) {
+ gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ }
+ if (createdMixBlendBackdropTexture) {
+ gl()->fDeleteTextures(1, &mixBlendBackdrop);
+ }
+
+ // in case rendering has used some other GL context
+ MakeCurrent();
+
+ LayerScope::DrawEnd(mGLContext, aEffectChain,
+ aGeometry.width, aGeometry.height);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTextureRect)
+{
+ BindAndDrawQuad(aProgram, aRect, aTextureRect);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTextureRect)
+{
+ NS_ASSERTION(aProgram->HasInitialized(), "Shader program not correctly initialized");
+
+ const gfx::TexturedTriangle& t = aTriangle;
+ const gfx::Triangle& tex = t.textureCoords;
+
+ GLfloat vertices[] = {
+ t.p1.x, t.p1.y, 0.0f, 1.0f, tex.p1.x, tex.p1.y,
+ t.p2.x, t.p2.y, 0.0f, 1.0f, tex.p2.x, tex.p2.y,
+ t.p3.x, t.p3.y, 0.0f, 1.0f, tex.p3.x, tex.p3.y
+ };
+
+ HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTriangleVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+ verticesOnHeap.ByteLength(),
+ verticesOnHeap.Data(),
+ LOCAL_GL_STREAM_DRAW);
+
+ const GLsizei stride = 6 * sizeof(GLfloat);
+ InitializeVAO(kCoordinateAttributeIndex, 4, stride, 0);
+ InitializeVAO(kTexCoordinateAttributeIndex, 2, stride, 4 * sizeof(GLfloat));
+
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 3);
+
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fDisableVertexAttribArray(kTexCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+}
+
+// |aRect| is the rectangle we want to draw to. We will draw it with
+// up to 4 draw commands if necessary to avoid wrapping.
+// |aTexCoordRect| is the rectangle from the texture that we want to
+// draw using the given program.
+// |aTexture| is the texture we are drawing. Its actual size can be
+// larger than the rectangle given by |texCoordRect|.
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const Rect& aRect,
+ const Rect& aTexCoordRect,
+ TextureSource *aTexture)
+{
+ Rect scaledTexCoordRect = GetTextureCoordinates(aTexCoordRect, aTexture);
+ Rect layerRects[4];
+ Rect textureRects[4];
+ size_t rects = DecomposeIntoNoRepeatRects(aRect,
+ scaledTexCoordRect,
+ &layerRects,
+ &textureRects);
+
+ BindAndDrawQuads(aProg, rects, layerRects, textureRects);
+}
+
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture)
+{
+ BindAndDrawGeometry(aProg, aTriangle,
+ GetTextureCoordinates(aTexCoordRect, aTexture));
+}
+
+void
+CompositorOGL::BindAndDrawQuads(ShaderProgramOGL *aProg,
+ int aQuads,
+ const Rect* aLayerRects,
+ const Rect* aTextureRects)
+{
+ NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ InitializeVAO(kCoordinateAttributeIndex, 4, 0, 0);
+
+ aProg->SetLayerRects(aLayerRects);
+ if (aProg->GetTextureCount() > 0) {
+ aProg->SetTextureRects(aTextureRects);
+ }
+
+ // We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
+ // process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+ LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects);
+}
+
+void
+CompositorOGL::InitializeVAO(const GLuint aAttrib, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset)
+{
+ mGLContext->fVertexAttribPointer(aAttrib, aComponents, LOCAL_GL_FLOAT,
+ LOCAL_GL_FALSE, aStride,
+ reinterpret_cast<GLvoid*>(aOffset));
+ mGLContext->fEnableVertexAttribArray(aAttrib);
+}
+
+void
+CompositorOGL::EndFrame()
+{
+ PROFILER_LABEL("CompositorOGL", "EndFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ LayoutDeviceIntSize size;
+ if (mUseExternalSurfaceSize) {
+ size = LayoutDeviceIntSize(mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ size = mWidget->GetClientSize();
+ }
+ RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8);
+ if (target) {
+ CopyToTarget(target, nsIntPoint(), Matrix());
+ WriteSnapshotToDumpFile(this, target);
+ }
+ }
+#endif
+
+ mContextStateTracker.PopOGLSection(gl(), "Frame");
+
+ mFrameInProgress = false;
+
+ if (mTarget) {
+ CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+ mCurrentRenderTarget = nullptr;
+ Compositor::EndFrame();
+ return;
+ }
+
+ mCurrentRenderTarget = nullptr;
+
+ if (mTexturePool) {
+ mTexturePool->EndFrame();
+ }
+
+ mGLContext->SwapBuffers();
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ // Unbind all textures
+ for (GLuint i = 0; i <= 4; i++) {
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+ if (!mGLContext->IsGLES()) {
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+ }
+ }
+
+ Compositor::EndFrame();
+}
+
+void
+CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(!mTarget);
+ if (mTexturePool) {
+ mTexturePool->EndFrame();
+ }
+}
+
+void
+CompositorOGL::SetDestinationSurfaceSize(const IntSize& aSize)
+{
+ mSurfaceSize.width = aSize.width;
+ mSurfaceSize.height = aSize.height;
+}
+
+void
+CompositorOGL::CopyToTarget(DrawTarget* aTarget, const nsIntPoint& aTopLeft, const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(aTarget);
+ IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ rect = IntRect(0, 0, mWidgetSize.width, mWidgetSize.height);
+ }
+ GLint width = rect.width;
+ GLint height = rect.height;
+
+ if ((int64_t(width) * int64_t(height) * int64_t(4)) > INT32_MAX) {
+ NS_ERROR("Widget size too big - integer overflow!");
+ return;
+ }
+
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (!mGLContext->IsGLES()) {
+ // GLES2 promises that binding to any custom FBO will attach
+ // to GL_COLOR_ATTACHMENT0 attachment point.
+ mGLContext->fReadBuffer(LOCAL_GL_BACK);
+ }
+
+ RefPtr<DataSourceSurface> source =
+ Factory::CreateDataSourceSurface(rect.Size(), gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!source)) {
+ return;
+ }
+
+ ReadPixelsIntoDataSurface(mGLContext, source);
+
+ // Map from GL space to Cairo space and reverse the world transform.
+ Matrix glToCairoTransform = aTransform;
+ glToCairoTransform.Invert();
+ glToCairoTransform.PreScale(1.0, -1.0);
+ glToCairoTransform.PreTranslate(0.0, -height);
+
+ glToCairoTransform.PostTranslate(-aTopLeft.x, -aTopLeft.y);
+
+ Matrix oldMatrix = aTarget->GetTransform();
+ aTarget->SetTransform(glToCairoTransform);
+ Rect floatRect = Rect(rect.x, rect.y, rect.width, rect.height);
+ aTarget->DrawSurface(source, floatRect, floatRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ aTarget->SetTransform(oldMatrix);
+ aTarget->Flush();
+}
+
+void
+CompositorOGL::Pause()
+{
+ // This was only used on Android
+}
+
+bool
+CompositorOGL::Resume()
+{
+#if defined(MOZ_WIDGET_UIKIT)
+ if (!gl() || gl()->IsDestroyed())
+ return false;
+
+ // RenewSurface internally calls MakeCurrent.
+ return gl()->RenewSurface(GetWidget()->RealWidget());
+#endif
+ return true;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorOGL::CreateDataTextureSource(TextureFlags aFlags)
+{
+ return MakeAndAddRef<TextureImageTextureSourceOGL>(this, aFlags);
+}
+
+bool
+CompositorOGL::SupportsPartialTextureUpdate()
+{
+ return CanUploadSubTextures(mGLContext);
+}
+
+int32_t
+CompositorOGL::GetMaxTextureSize() const
+{
+ MOZ_ASSERT(mGLContext);
+ GLint texSize = 0;
+ mGLContext->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
+ &texSize);
+ MOZ_ASSERT(texSize != 0);
+ return texSize;
+}
+
+void
+CompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+ mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
+}
+
+GLBlitTextureImageHelper*
+CompositorOGL::BlitTextureImageHelper()
+{
+ if (!mBlitTextureImageHelper) {
+ mBlitTextureImageHelper = MakeUnique<GLBlitTextureImageHelper>(this);
+ }
+
+ return mBlitTextureImageHelper.get();
+}
+
+
+
+GLuint
+CompositorOGL::GetTemporaryTexture(GLenum aTarget, GLenum aUnit)
+{
+ if (!mTexturePool) {
+ mTexturePool = new PerUnitTexturePoolOGL(gl());
+ }
+ return mTexturePool->GetTexture(aTarget, aUnit);
+}
+
+GLuint
+PerUnitTexturePoolOGL::GetTexture(GLenum aTarget, GLenum aTextureUnit)
+{
+ if (mTextureTarget == 0) {
+ mTextureTarget = aTarget;
+ }
+ MOZ_ASSERT(mTextureTarget == aTarget);
+
+ size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
+ // lazily grow the array of temporary textures
+ if (mTextures.Length() <= index) {
+ size_t prevLength = mTextures.Length();
+ mTextures.SetLength(index + 1);
+ for(unsigned int i = prevLength; i <= index; ++i) {
+ mTextures[i] = 0;
+ }
+ }
+ // lazily initialize the temporary textures
+ if (!mTextures[index]) {
+ if (!mGL->MakeCurrent()) {
+ return 0;
+ }
+ mGL->fGenTextures(1, &mTextures[index]);
+ mGL->fBindTexture(aTarget, mTextures[index]);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ }
+ return mTextures[index];
+}
+
+void
+PerUnitTexturePoolOGL::DestroyTextures()
+{
+ if (mGL && mGL->MakeCurrent()) {
+ if (mTextures.Length() > 0) {
+ mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
+ }
+ }
+ mTextures.SetLength(0);
+}
+
+void
+PerFrameTexturePoolOGL::DestroyTextures()
+{
+ if (!mGL->MakeCurrent()) {
+ return;
+ }
+
+ if (mUnusedTextures.Length() > 0) {
+ mGL->fDeleteTextures(mUnusedTextures.Length(), &mUnusedTextures[0]);
+ mUnusedTextures.Clear();
+ }
+
+ if (mCreatedTextures.Length() > 0) {
+ mGL->fDeleteTextures(mCreatedTextures.Length(), &mCreatedTextures[0]);
+ mCreatedTextures.Clear();
+ }
+}
+
+GLuint
+PerFrameTexturePoolOGL::GetTexture(GLenum aTarget, GLenum)
+{
+ if (mTextureTarget == 0) {
+ mTextureTarget = aTarget;
+ }
+
+ // The pool should always use the same texture target because it is illegal
+ // to change the target of an already exisiting gl texture.
+ // If we need to use several targets, a pool with several sub-pools (one per
+ // target) will have to be implemented.
+ // At the moment this pool is only used with tiling on b2g so we always need
+ // the same target.
+ MOZ_ASSERT(mTextureTarget == aTarget);
+
+ GLuint texture = 0;
+
+ if (!mUnusedTextures.IsEmpty()) {
+ // Try to reuse one from the unused pile first
+ texture = mUnusedTextures[0];
+ mUnusedTextures.RemoveElementAt(0);
+ } else if (mGL->MakeCurrent()) {
+ // There isn't one to reuse, create one.
+ mGL->fGenTextures(1, &texture);
+ mGL->fBindTexture(aTarget, texture);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ }
+
+ if (texture) {
+ mCreatedTextures.AppendElement(texture);
+ }
+
+ return texture;
+}
+
+void
+PerFrameTexturePoolOGL::EndFrame()
+{
+ if (!mGL->MakeCurrent()) {
+ // this means the context got destroyed underneith us somehow, and the driver
+ // already has destroyed the textures.
+ mCreatedTextures.Clear();
+ mUnusedTextures.Clear();
+ return;
+ }
+
+ // Some platforms have issues unlocking Gralloc buffers even when they're
+ // rebound.
+ if (gfxPrefs::OverzealousGrallocUnlocking()) {
+ mUnusedTextures.AppendElements(mCreatedTextures);
+ mCreatedTextures.Clear();
+ }
+
+ // Delete unused textures
+ for (size_t i = 0; i < mUnusedTextures.Length(); i++) {
+ GLuint texture = mUnusedTextures[i];
+ mGL->fDeleteTextures(1, &texture);
+ }
+ mUnusedTextures.Clear();
+
+ // Move all created textures into the unused pile
+ mUnusedTextures.AppendElements(mCreatedTextures);
+ mCreatedTextures.Clear();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/CompositorOGL.h b/system/graphics/layers/opengl/CompositorOGL.h
new file mode 100644
index 000000000..da204b12e
--- /dev/null
+++ b/system/graphics/layers/opengl/CompositorOGL.h
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITOROGL_H
+#define MOZILLA_GFX_COMPOSITOROGL_H
+
+#include "ContextStateTracker.h"
+#include "gfx2DGlue.h"
+#include "GLContextTypes.h" // for GLContext, etc
+#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc
+#include "OGLShaderProgram.h" // for ShaderProgramOGL, etc
+#include "Units.h" // for ScreenPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override, final
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefPtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/Types.h" // for Float, SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, Compositor, etc
+#include "mozilla/layers/CompositorTypes.h" // for MaskType::MaskType::NumMaskTypes, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsThreadUtils.h" // for nsRunnable
+#include "nsXULAppAPI.h" // for XRE_GetProcessType
+#include "nscore.h" // for NS_IMETHOD
+
+class nsIWidget;
+
+namespace mozilla {
+
+namespace layers {
+
+class CompositingRenderTarget;
+class CompositingRenderTargetOGL;
+class DataTextureSource;
+class GLManagerCompositor;
+class TextureSource;
+struct Effect;
+struct EffectChain;
+class GLBlitTextureImageHelper;
+
+/**
+ * Interface for pools of temporary gl textures for the compositor.
+ * The textures are fully owned by the pool, so the latter is responsible
+ * calling fDeleteTextures accordingly.
+ * Users of GetTexture receive a texture that is only valid for the duration
+ * of the current frame.
+ * This is primarily intended for direct texturing APIs that need to attach
+ * shared objects (such as an EGLImage) to a gl texture.
+ */
+class CompositorTexturePoolOGL
+{
+protected:
+ virtual ~CompositorTexturePoolOGL() {}
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(CompositorTexturePoolOGL)
+
+ virtual void Clear() = 0;
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aEnum) = 0;
+
+ virtual void EndFrame() = 0;
+};
+
+/**
+ * Agressively reuses textures. One gl texture per texture unit in total.
+ * So far this hasn't shown the best results on b2g.
+ */
+class PerUnitTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+ explicit PerUnitTexturePoolOGL(gl::GLContext* aGL)
+ : mTextureTarget(0) // zero is never a valid texture target
+ , mGL(aGL)
+ {}
+
+ virtual ~PerUnitTexturePoolOGL()
+ {
+ DestroyTextures();
+ }
+
+ virtual void Clear() override
+ {
+ DestroyTextures();
+ }
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override;
+
+ virtual void EndFrame() override {}
+
+protected:
+ void DestroyTextures();
+
+ GLenum mTextureTarget;
+ nsTArray<GLuint> mTextures;
+ RefPtr<gl::GLContext> mGL;
+};
+
+/**
+ * Reuse gl textures from a pool of textures that haven't yet been
+ * used during the current frame.
+ * All the textures that are not used at the end of a frame are
+ * deleted.
+ * This strategy seems to work well with gralloc textures because destroying
+ * unused textures which are bound to gralloc buffers let drivers know that it
+ * can unlock the gralloc buffers.
+ */
+class PerFrameTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+ explicit PerFrameTexturePoolOGL(gl::GLContext* aGL)
+ : mTextureTarget(0) // zero is never a valid texture target
+ , mGL(aGL)
+ {}
+
+ virtual ~PerFrameTexturePoolOGL()
+ {
+ DestroyTextures();
+ }
+
+ virtual void Clear() override
+ {
+ DestroyTextures();
+ }
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override;
+
+ virtual void EndFrame() override;
+
+protected:
+ void DestroyTextures();
+
+ GLenum mTextureTarget;
+ RefPtr<gl::GLContext> mGL;
+ nsTArray<GLuint> mCreatedTextures;
+ nsTArray<GLuint> mUnusedTextures;
+};
+
+// If you want to make this class not final, first remove calls to virtual
+// methods (Destroy) that are made in the destructor.
+class CompositorOGL final : public Compositor
+{
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class GLManagerCompositor;
+ friend class CompositingRenderTargetOGL;
+
+ std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms;
+public:
+ explicit CompositorOGL(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget,
+ int aSurfaceWidth = -1, int aSurfaceHeight = -1,
+ bool aUseExternalSurfaceSize = false);
+
+protected:
+ virtual ~CompositorOGL();
+
+public:
+ virtual CompositorOGL* AsCompositorOGL() override { return this; }
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual void Destroy() override;
+
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
+ {
+ TextureFactoryIdentifier result =
+ TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL,
+ XRE_GetProcessType(),
+ GetMaxTextureSize(),
+ mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
+ SupportsPartialTextureUpdate());
+ return result;
+ }
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override;
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override;
+
+ virtual void DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void EndFrame() override;
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
+
+ virtual bool SupportsPartialTextureUpdate() override;
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
+ {
+ if (!mGLContext)
+ return false;
+ int32_t maxSize = GetMaxTextureSize();
+ return aSize <= gfx::IntSize(maxSize, maxSize);
+ }
+
+ virtual int32_t GetMaxTextureSize() const override;
+
+ /**
+ * Set the size of the EGL surface we're rendering to, if we're rendering to
+ * an EGL surface.
+ */
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override;
+
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
+ mRenderOffset = aOffset;
+ }
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override;
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "OGL"; }
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_OPENGL;
+ }
+
+ virtual void Pause() override;
+ virtual bool Resume() override;
+
+ virtual bool HasImageHostOverlays() override
+ {
+ return false;
+ }
+
+ virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) override
+ {
+ }
+
+ virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) override
+ {
+ }
+
+ GLContext* gl() const { return mGLContext; }
+ /**
+ * Clear the program state. This must be called
+ * before operating on the GLContext directly. */
+ void ResetProgram();
+
+ gfx::SurfaceFormat GetFBOFormat() const {
+ return gfx::SurfaceFormat::R8G8B8A8;
+ }
+
+ GLBlitTextureImageHelper* BlitTextureImageHelper();
+
+ /**
+ * The compositor provides with temporary textures for use with direct
+ * textruing like gralloc texture.
+ * Doing so lets us use gralloc the way it has been designed to be used
+ * (see https://wiki.mozilla.org/Platform/GFX/Gralloc)
+ */
+ GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit);
+
+ const gfx::Matrix4x4& GetProjMatrix() const {
+ return mProjMatrix;
+ }
+
+ void SetProjMatrix(const gfx::Matrix4x4& aProjMatrix) {
+ mProjMatrix = aProjMatrix;
+ }
+
+ const gfx::IntSize GetDestinationSurfaceSize() const {
+ return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height);
+ }
+
+ const ScreenPoint& GetScreenRenderOffset() const {
+ return mRenderOffset;
+ }
+
+private:
+ template<typename Geometry>
+ void DrawGeometry(const Geometry& aGeometry,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect);
+
+ void PrepareViewport(CompositingRenderTargetOGL *aRenderTarget);
+
+ /** Widget associated with this compositor */
+ LayoutDeviceIntSize mWidgetSize;
+ RefPtr<GLContext> mGLContext;
+ UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
+ gfx::Matrix4x4 mProjMatrix;
+
+ /** The size of the surface we are rendering to */
+ gfx::IntSize mSurfaceSize;
+
+ ScreenPoint mRenderOffset;
+
+ already_AddRefed<mozilla::gl::GLContext> CreateContext();
+
+ /** Texture target to use for FBOs */
+ GLenum mFBOTextureTarget;
+
+ /** Currently bound render target */
+ RefPtr<CompositingRenderTargetOGL> mCurrentRenderTarget;
+#ifdef DEBUG
+ CompositingRenderTargetOGL* mWindowRenderTarget;
+#endif
+
+ /**
+ * VBO that has some basics in it for a textured quad, including vertex
+ * coords and texcoords.
+ */
+ GLuint mQuadVBO;
+
+
+ /**
+ * VBO that stores dynamic triangle geometry.
+ */
+ GLuint mTriangleVBO;
+
+ bool mHasBGRA;
+
+ /**
+ * When rendering to some EGL surfaces (e.g. on Android), we rely on being told
+ * about size changes (via SetSurfaceSize) rather than pulling this information
+ * from the widget.
+ */
+ bool mUseExternalSurfaceSize;
+
+ /**
+ * Have we had DrawQuad calls since the last frame was rendered?
+ */
+ bool mFrameInProgress;
+
+ /*
+ * Clear aRect on current render target.
+ */
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ /* Start a new frame. If aClipRectIn is null and aClipRectOut is non-null,
+ * sets *aClipRectOut to the screen dimensions.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+
+ ShaderConfigOGL GetShaderConfigFor(Effect *aEffect,
+ MaskType aMask = MaskType::MaskNone,
+ gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER,
+ bool aColorMatrix = false,
+ bool aDEAAEnabled = false) const;
+
+ ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig);
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+ const gfx::Rect&)
+ {
+ aConfig.SetDynamicGeometry(false);
+ }
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+ const gfx::TexturedTriangle&)
+ {
+ aConfig.SetDynamicGeometry(true);
+ }
+
+ /**
+ * Create a FBO backed by a texture.
+ * Note that the texture target type will be
+ * of the type returned by FBOTextureTarget; different
+ * shaders are required to sample from the different
+ * texture types.
+ */
+ void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ GLuint *aFBO, GLuint *aTexture,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ gfx::Point3D GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2);
+
+ void ActivateProgram(ShaderProgramOGL *aProg);
+
+ void CleanupResources();
+
+ void BindAndDrawQuads(ShaderProgramOGL *aProg,
+ int aQuads,
+ const gfx::Rect* aLayerRect,
+ const gfx::Rect* aTextureRect);
+
+ void BindAndDrawQuad(ShaderProgramOGL *aProg,
+ const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f))
+ {
+ gfx::Rect layerRects[4];
+ gfx::Rect textureRects[4];
+ layerRects[0] = aLayerRect;
+ textureRects[0] = aTextureRect;
+ BindAndDrawQuads(aProg, 1, layerRects, textureRects);
+ }
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture);
+
+ void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture);
+
+ void InitializeVAO(const GLuint aAttribIndex, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset);
+
+ gfx::Rect GetTextureCoordinates(gfx::Rect textureRect,
+ TextureSource* aTexture);
+
+ /**
+ * Bind the texture behind the current render target as the backdrop for a
+ * mix-blend shader.
+ */
+ void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
+
+ /**
+ * Copies the content of our backbuffer to the set transaction target.
+ * Does not restore the target FBO, so only call from EndFrame.
+ */
+ void CopyToTarget(gfx::DrawTarget* aTarget, const nsIntPoint& aTopLeft, const gfx::Matrix& aWorldMatrix);
+
+ /**
+ * Implements the flipping of the y-axis to convert from layers/compositor
+ * coordinates to OpenGL coordinates.
+ *
+ * Indeed, the only coordinate system that OpenGL knows has the y-axis
+ * pointing upwards, but the layers/compositor coordinate system has the
+ * y-axis pointing downwards, for good reason as Web pages are typically
+ * scrolled downwards. So, some flipping has to take place; FlippedY does it.
+ */
+ GLint FlipY(GLint y) const { return mViewportSize.height - y; }
+
+ RefPtr<CompositorTexturePoolOGL> mTexturePool;
+
+ ContextStateTrackerOGL mContextStateTracker;
+
+ bool mDestroyed;
+
+ /**
+ * Size of the OpenGL context's primary framebuffer in pixels. Used by
+ * FlipY for the y-flipping calculation and by the DEAA shader.
+ */
+ gfx::IntSize mViewportSize;
+
+ ShaderProgramOGL *mCurrentProgram;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COMPOSITOROGL_H */
diff --git a/system/graphics/layers/opengl/EGLImageHelpers.cpp b/system/graphics/layers/opengl/EGLImageHelpers.cpp
new file mode 100644
index 000000000..57e09c266
--- /dev/null
+++ b/system/graphics/layers/opengl/EGLImageHelpers.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "EGLImageHelpers.h"
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla
+{
+namespace layers {
+
+using namespace gl;
+
+EGLImage
+EGLImageCreateFromNativeBuffer(GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize)
+{
+ EGLint attrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
+ LOCAL_EGL_NONE, LOCAL_EGL_NONE,
+ };
+
+ bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0);
+ EGLint* usedAttrs = attrs;
+
+ return sEGLLibrary.fCreateImage(sEGLLibrary.Display(),
+ EGL_NO_CONTEXT,
+ LOCAL_EGL_NATIVE_BUFFER_ANDROID,
+ aBuffer, usedAttrs);
+}
+
+void
+EGLImageDestroy(GLContext* aGL, EGLImage aImage)
+{
+ sEGLLibrary.fDestroyImage(sEGLLibrary.Display(), aImage);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/EGLImageHelpers.h b/system/graphics/layers/opengl/EGLImageHelpers.h
new file mode 100644
index 000000000..c2271dc76
--- /dev/null
+++ b/system/graphics/layers/opengl/EGLImageHelpers.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef EGLIMAGEHELPERS_H_
+#define EGLIMAGEHELPERS_H_
+
+#include "mozilla/gfx/Point.h"
+
+typedef void* EGLImage;
+
+namespace mozilla {
+namespace gl {
+ class GLContext;
+}
+
+namespace layers {
+
+EGLImage EGLImageCreateFromNativeBuffer(gl::GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize);
+void EGLImageDestroy(gl::GLContext* aGL, EGLImage aImage);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // EGLIMAGEHELPERS_H_
diff --git a/system/graphics/layers/opengl/GLBlitTextureImageHelper.cpp b/system/graphics/layers/opengl/GLBlitTextureImageHelper.cpp
new file mode 100644
index 000000000..76ebd8bd0
--- /dev/null
+++ b/system/graphics/layers/opengl/GLBlitTextureImageHelper.cpp
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLBlitTextureImageHelper.h"
+#include "GLUploadHelpers.h"
+#include "DecomposeIntoNoRepeatTriangles.h"
+#include "GLContext.h"
+#include "GLTextureImage.h"
+#include "ScopedGLHelpers.h"
+#include "nsRect.h"
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "CompositorOGL.h"
+#include "mozilla/gfx/Point.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+GLBlitTextureImageHelper::GLBlitTextureImageHelper(CompositorOGL* aCompositor)
+ : mCompositor(aCompositor)
+ , mBlitProgram(0)
+ , mBlitFramebuffer(0)
+
+{
+}
+
+GLBlitTextureImageHelper::~GLBlitTextureImageHelper()
+{
+ GLContext *gl = mCompositor->gl();
+ // Likely used by OGL Layers.
+ gl->fDeleteProgram(mBlitProgram);
+ gl->fDeleteFramebuffers(1, &mBlitFramebuffer);
+}
+
+void
+GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const gfx::IntRect& aSrcRect,
+ TextureImage *aDst, const gfx::IntRect& aDstRect)
+{
+ GLContext *gl = mCompositor->gl();
+
+ if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty())
+ return;
+
+ int savedFb = 0;
+ gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb);
+
+ ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false);
+ ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false);
+
+ // 2.0 means scale up by two
+ float blitScaleX = float(aDstRect.width) / float(aSrcRect.width);
+ float blitScaleY = float(aDstRect.height) / float(aSrcRect.height);
+
+ // We start iterating over all destination tiles
+ aDst->BeginBigImageIteration();
+ do {
+ // calculate portion of the tile that is going to be painted to
+ gfx::IntRect dstSubRect;
+ gfx::IntRect dstTextureRect = aDst->GetTileRect();
+ dstSubRect.IntersectRect(aDstRect, dstTextureRect);
+
+ // this tile is not part of the destination rectangle aDstRect
+ if (dstSubRect.IsEmpty())
+ continue;
+
+ // (*) transform the rect of this tile into the rectangle defined by aSrcRect...
+ gfx::IntRect dstInSrcRect(dstSubRect);
+ dstInSrcRect.MoveBy(-aDstRect.TopLeft());
+ // ...which might be of different size, hence scale accordingly
+ dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY);
+ dstInSrcRect.MoveBy(aSrcRect.TopLeft());
+
+ SetBlitFramebufferForDestTexture(aDst->GetTextureID());
+ UseBlitProgram();
+
+ aSrc->BeginBigImageIteration();
+ // now iterate over all tiles in the source Image...
+ do {
+ // calculate portion of the source tile that is in the source rect
+ gfx::IntRect srcSubRect;
+ gfx::IntRect srcTextureRect = aSrc->GetTileRect();
+ srcSubRect.IntersectRect(aSrcRect, srcTextureRect);
+
+ // this tile is not part of the source rect
+ if (srcSubRect.IsEmpty()) {
+ continue;
+ }
+ // calculate intersection of source rect with destination rect
+ srcSubRect.IntersectRect(srcSubRect, dstInSrcRect);
+ // this tile does not overlap the current destination tile
+ if (srcSubRect.IsEmpty()) {
+ continue;
+ }
+ // We now have the intersection of
+ // the current source tile
+ // and the desired source rectangle
+ // and the destination tile
+ // and the desired destination rectange
+ // in destination space.
+ // We need to transform this back into destination space, inverting the transform from (*)
+ gfx::IntRect srcSubInDstRect(srcSubRect);
+ srcSubInDstRect.MoveBy(-aSrcRect.TopLeft());
+ srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY);
+ srcSubInDstRect.MoveBy(aDstRect.TopLeft());
+
+ // we transform these rectangles to be relative to the current src and dst tiles, respectively
+ gfx::IntSize srcSize = srcTextureRect.Size();
+ gfx::IntSize dstSize = dstTextureRect.Size();
+ srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y);
+ srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y);
+
+ float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f;
+ float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f;
+ float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
+ float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
+ ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height);
+
+ RectTriangles rects;
+
+ gfx::IntSize realTexSize = srcSize;
+ if (!CanUploadNonPowerOfTwo(gl)) {
+ realTexSize = gfx::IntSize(RoundUpPow2(srcSize.width),
+ RoundUpPow2(srcSize.height));
+ }
+
+ if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
+ rects.addRect(/* dest rectangle */
+ dx0, dy0, dx1, dy1,
+ /* tex coords */
+ srcSubRect.x / float(realTexSize.width),
+ srcSubRect.y / float(realTexSize.height),
+ srcSubRect.XMost() / float(realTexSize.width),
+ srcSubRect.YMost() / float(realTexSize.height));
+ } else {
+ DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);
+
+ // now put the coords into the d[xy]0 .. d[xy]1 coordinate space
+ // from the 0..1 that it comes out of decompose
+ InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords();
+
+ for (unsigned int i = 0; i < coords.Length(); ++i) {
+ coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0;
+ coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0;
+ }
+ }
+
+ ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0);
+ ScopedBindTexture autoTex(gl, aSrc->GetTextureID());
+ ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements());
+ ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements());
+
+ gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
+
+ } while (aSrc->NextTile());
+ } while (aDst->NextTile());
+
+ // unbind the previous texture from the framebuffer
+ SetBlitFramebufferForDestTexture(0);
+
+ gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb);
+}
+
+void
+GLBlitTextureImageHelper::SetBlitFramebufferForDestTexture(GLuint aTexture)
+{
+ GLContext *gl = mCompositor->gl();
+ if (!mBlitFramebuffer) {
+ gl->fGenFramebuffers(1, &mBlitFramebuffer);
+ }
+
+ gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer);
+ gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D,
+ aTexture,
+ 0);
+
+ GLenum result = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
+ nsAutoCString msg;
+ msg.AppendLiteral("Framebuffer not complete -- error 0x");
+ msg.AppendInt(result, 16);
+ // Note: if you are hitting this, it is likely that
+ // your texture is not texture complete -- that is, you
+ // allocated a texture name, but didn't actually define its
+ // size via a call to TexImage2D.
+ NS_RUNTIMEABORT(msg.get());
+ }
+}
+
+void
+GLBlitTextureImageHelper::UseBlitProgram()
+{
+ // XXX: GLBlitTextureImageHelper doesn't use ShaderProgramOGL
+ // so we need to Reset the program
+ mCompositor->ResetProgram();
+
+ GLContext *gl = mCompositor->gl();
+ if (mBlitProgram) {
+ gl->fUseProgram(mBlitProgram);
+ return;
+ }
+
+ mBlitProgram = gl->fCreateProgram();
+
+ GLuint shaders[2];
+ shaders[0] = gl->fCreateShader(LOCAL_GL_VERTEX_SHADER);
+ shaders[1] = gl->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
+
+ const char *blitVSSrc =
+ "attribute vec2 aVertex;"
+ "attribute vec2 aTexCoord;"
+ "varying vec2 vTexCoord;"
+ "void main() {"
+ " vTexCoord = aTexCoord;"
+ " gl_Position = vec4(aVertex, 0.0, 1.0);"
+ "}";
+ const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n"
+ "uniform sampler2D uSrcTexture;"
+ "varying vec2 vTexCoord;"
+ "void main() {"
+ " gl_FragColor = texture2D(uSrcTexture, vTexCoord);"
+ "}";
+
+ gl->fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr);
+ gl->fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr);
+
+ for (int i = 0; i < 2; ++i) {
+ GLint success, len = 0;
+
+ gl->fCompileShader(shaders[i]);
+ gl->fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success);
+ NS_ASSERTION(success, "Shader compilation failed!");
+
+ if (!success) {
+ nsAutoCString log;
+ gl->fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ log.SetCapacity(len);
+ gl->fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ printf_stderr("Shader %d compilation failed:\n%s\n", i, log.get());
+ return;
+ }
+
+ gl->fAttachShader(mBlitProgram, shaders[i]);
+ gl->fDeleteShader(shaders[i]);
+ }
+
+ gl->fBindAttribLocation(mBlitProgram, 0, "aVertex");
+ gl->fBindAttribLocation(mBlitProgram, 1, "aTexCoord");
+
+ gl->fLinkProgram(mBlitProgram);
+
+ GLint success, len = 0;
+ gl->fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success);
+ NS_ASSERTION(success, "Shader linking failed!");
+
+ if (!success) {
+ nsAutoCString log;
+ gl->fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ log.SetCapacity(len);
+ gl->fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ printf_stderr("Program linking failed:\n%s\n", log.get());
+ return;
+ }
+
+ gl->fUseProgram(mBlitProgram);
+ gl->fUniform1i(gl->fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/GLBlitTextureImageHelper.h b/system/graphics/layers/opengl/GLBlitTextureImageHelper.h
new file mode 100644
index 000000000..de5ee1696
--- /dev/null
+++ b/system/graphics/layers/opengl/GLBlitTextureImageHelper.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLBLITTEXTUREIMAGEHELPER_H_
+#define GLBLITTEXTUREIMAGEHELPER_H_
+
+#include "mozilla/Attributes.h"
+#include "GLContextTypes.h"
+#include "GLConsts.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace gl {
+ class GLContext;
+ class TextureImage;
+} // namespace gl
+namespace layers {
+
+class CompositorOGL;
+
+class GLBlitTextureImageHelper final
+{
+ // The GLContext is the sole owner of the GLBlitTextureImageHelper.
+ CompositorOGL* mCompositor;
+
+ // lazy-initialized things
+ GLuint mBlitProgram, mBlitFramebuffer;
+ void UseBlitProgram();
+ void SetBlitFramebufferForDestTexture(GLuint aTexture);
+
+public:
+
+ explicit GLBlitTextureImageHelper(CompositorOGL *gl);
+ ~GLBlitTextureImageHelper();
+
+ /**
+ * Copy a rectangle from one TextureImage into another. The
+ * source and destination are given in integer coordinates, and
+ * will be converted to texture coordinates.
+ *
+ * For the source texture, the wrap modes DO apply -- it's valid
+ * to use REPEAT or PAD and expect appropriate behaviour if the source
+ * rectangle extends beyond its bounds.
+ *
+ * For the destination texture, the wrap modes DO NOT apply -- the
+ * destination will be clipped by the bounds of the texture.
+ *
+ * Note: calling this function will cause the following OpenGL state
+ * to be changed:
+ *
+ * - current program
+ * - framebuffer binding
+ * - viewport
+ * - blend state (will be enabled at end)
+ * - scissor state (will be enabled at end)
+ * - vertex attrib 0 and 1 (pointer and enable state [enable state will be disabled at exit])
+ * - array buffer binding (will be 0)
+ * - active texture (will be 0)
+ * - texture 0 binding
+ */
+ void BlitTextureImage(gl::TextureImage *aSrc, const gfx::IntRect& aSrcRect,
+ gl::TextureImage *aDst, const gfx::IntRect& aDstRect);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GLBLITTEXTUREIMAGEHELPER_H_
diff --git a/system/graphics/layers/opengl/OGLShaderProgram.cpp b/system/graphics/layers/opengl/OGLShaderProgram.cpp
new file mode 100644
index 000000000..c06dc52dd
--- /dev/null
+++ b/system/graphics/layers/opengl/OGLShaderProgram.cpp
@@ -0,0 +1,974 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OGLShaderProgram.h"
+#include <stdint.h> // for uint32_t
+#include <sstream> // for ostringstream
+#include "gfxEnv.h"
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h"
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/Compositor.h" // for BlendOpIsMixBlendMode
+#include "nsAString.h"
+#include "nsString.h" // for nsAutoCString
+#include "Layers.h"
+#include "GLContext.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace std;
+
+#define GAUSSIAN_KERNEL_HALF_WIDTH 11
+#define GAUSSIAN_KERNEL_STEP 0.2
+
+void
+AddUniforms(ProgramProfileOGL& aProfile)
+{
+ // This needs to be kept in sync with the KnownUniformName enum
+ static const char *sKnownUniformNames[] = {
+ "uLayerTransform",
+ "uLayerTransformInverse",
+ "uMaskTransform",
+ "uBackdropTransform",
+ "uLayerRects",
+ "uMatrixProj",
+ "uTextureTransform",
+ "uTextureRects",
+ "uRenderTargetOffset",
+ "uLayerOpacity",
+ "uTexture",
+ "uYTexture",
+ "uCbTexture",
+ "uCrTexture",
+ "uBlackTexture",
+ "uWhiteTexture",
+ "uMaskTexture",
+ "uBackdropTexture",
+ "uRenderColor",
+ "uTexCoordMultiplier",
+ "uCbCrTexCoordMultiplier",
+ "uTexturePass2",
+ "uColorMatrix",
+ "uColorMatrixVector",
+ "uBlurRadius",
+ "uBlurOffset",
+ "uBlurAlpha",
+ "uBlurGaussianKernel",
+ "uSSEdges",
+ "uViewportSize",
+ "uVisibleCenter",
+ "uYuvColorMatrix",
+ nullptr
+ };
+
+ for (int i = 0; sKnownUniformNames[i] != nullptr; ++i) {
+ aProfile.mUniforms[i].mNameString = sKnownUniformNames[i];
+ aProfile.mUniforms[i].mName = (KnownUniform::KnownUniformName) i;
+ }
+}
+
+void
+ShaderConfigOGL::SetRenderColor(bool aEnabled)
+{
+ SetFeature(ENABLE_RENDER_COLOR, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetTextureTarget(GLenum aTarget)
+{
+ SetFeature(ENABLE_TEXTURE_EXTERNAL | ENABLE_TEXTURE_RECT, false);
+ switch (aTarget) {
+ case LOCAL_GL_TEXTURE_EXTERNAL:
+ SetFeature(ENABLE_TEXTURE_EXTERNAL, true);
+ break;
+ case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+ SetFeature(ENABLE_TEXTURE_RECT, true);
+ break;
+ }
+}
+
+void
+ShaderConfigOGL::SetRBSwap(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_RB_SWAP, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetNoAlpha(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_NO_ALPHA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetOpacity(bool aEnabled)
+{
+ SetFeature(ENABLE_OPACITY, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetYCbCr(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_NV12));
+}
+
+void
+ShaderConfigOGL::SetNV12(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_NV12, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR));
+}
+
+void
+ShaderConfigOGL::SetComponentAlpha(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_COMPONENT_ALPHA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetColorMatrix(bool aEnabled)
+{
+ SetFeature(ENABLE_COLOR_MATRIX, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetBlur(bool aEnabled)
+{
+ SetFeature(ENABLE_BLUR, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetMask(bool aEnabled)
+{
+ SetFeature(ENABLE_MASK, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetNoPremultipliedAlpha()
+{
+ SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
+}
+
+void
+ShaderConfigOGL::SetDEAA(bool aEnabled)
+{
+ SetFeature(ENABLE_DEAA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetCompositionOp(gfx::CompositionOp aOp)
+{
+ mCompositionOp = aOp;
+}
+
+void
+ShaderConfigOGL::SetDynamicGeometry(bool aEnabled)
+{
+ SetFeature(ENABLE_DYNAMIC_GEOMETRY, aEnabled);
+}
+
+/* static */ ProgramProfileOGL
+ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
+{
+ ProgramProfileOGL result;
+ ostringstream fs, vs;
+
+ AddUniforms(result);
+
+ gfx::CompositionOp blendOp = aConfig.mCompositionOp;
+
+ vs << "#ifdef GL_ES" << endl;
+ vs << "#define EDGE_PRECISION mediump" << endl;
+ vs << "#else" << endl;
+ vs << "#define EDGE_PRECISION" << endl;
+ vs << "#endif" << endl;
+ vs << "uniform mat4 uMatrixProj;" << endl;
+ vs << "uniform vec4 uLayerRects[4];" << endl;
+ vs << "uniform mat4 uLayerTransform;" << endl;
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ vs << "uniform mat4 uLayerTransformInverse;" << endl;
+ vs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ vs << "uniform vec2 uVisibleCenter;" << endl;
+ vs << "uniform vec2 uViewportSize;" << endl;
+ }
+ vs << "uniform vec2 uRenderTargetOffset;" << endl;
+ vs << "attribute vec4 aCoord;" << endl;
+ result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aCoord", 0});
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << "uniform mat4 uTextureTransform;" << endl;
+ vs << "uniform vec4 uTextureRects[4];" << endl;
+ vs << "varying vec2 vTexCoord;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << "attribute vec2 aTexCoord;" << endl;
+ result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aTexCoord", 1});
+ }
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ vs << "uniform mat4 uBackdropTransform;" << endl;
+ vs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << "uniform mat4 uMaskTransform;" << endl;
+ vs << "varying vec3 vMaskCoord;" << endl;
+ }
+
+ vs << "void main() {" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vec4 finalPosition = vec4(aCoord.xy, 0.0, 1.0);" << endl;
+ } else {
+ vs << " int vertexID = int(aCoord.w);" << endl;
+ vs << " vec4 layerRect = uLayerRects[vertexID];" << endl;
+ vs << " vec4 finalPosition = vec4(aCoord.xy * layerRect.zw + layerRect.xy, 0.0, 1.0);" << endl;
+ }
+
+ vs << " finalPosition = uLayerTransform * finalPosition;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // XXX kip - The DEAA shader could be made simpler if we switch to
+ // using dynamic vertex buffers instead of sending everything
+ // in through uniforms. This would enable passing information
+ // about how to dilate each vertex explicitly and eliminate the
+ // need to extrapolate this with the sub-pixel coverage
+ // calculation in the vertex shader.
+
+ // Calculate the screen space position of this vertex, in screen pixels
+ vs << " vec4 ssPos = finalPosition;" << endl;
+ vs << " ssPos.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " ssPos = uMatrixProj * ssPos;" << endl;
+ vs << " ssPos.xy = ((ssPos.xy/ssPos.w)*0.5+0.5)*uViewportSize;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_MASK ||
+ !(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << " vec4 coordAdjusted;" << endl;
+ vs << " coordAdjusted.xy = aCoord.xy;" << endl;
+ }
+
+ // It is necessary to dilate edges away from uVisibleCenter to ensure that
+ // fragments with less than 50% sub-pixel coverage will be shaded.
+ // This offset is applied when the sub-pixel coverage of the vertex is
+ // less than 100%. Expanding by 0.5 pixels in screen space is sufficient
+ // to include these pixels.
+ vs << " if (dot(uSSEdges[0], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[1], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[2], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[3], vec3(ssPos.xy, 1.0)) < 1.5) {" << endl;
+ // If the shader reaches this branch, then this vertex is on the edge of
+ // the layer's visible rect and should be dilated away from the center of
+ // the visible rect. We don't want to hit this for inner facing
+ // edges between tiles, as the pixels may be covered twice without clipping
+ // against uSSEdges. If all edges were dilated, it would result in
+ // artifacts visible within semi-transparent layers with multiple tiles.
+ vs << " vec4 visibleCenter = uLayerTransform * vec4(uVisibleCenter, 0.0, 1.0);" << endl;
+ vs << " vec2 dilateDir = finalPosition.xy / finalPosition.w - visibleCenter.xy / visibleCenter.w;" << endl;
+ vs << " vec2 offset = sign(dilateDir) * 0.5;" << endl;
+ vs << " finalPosition.xy += offset * finalPosition.w;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ // We must adjust the texture coordinates to compensate for the dilation
+ vs << " coordAdjusted = uLayerTransformInverse * finalPosition;" << endl;
+ vs << " coordAdjusted /= coordAdjusted.w;" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY)) {
+ vs << " coordAdjusted.xy -= layerRect.xy;" << endl;
+ vs << " coordAdjusted.xy /= layerRect.zw;" << endl;
+ }
+ }
+ vs << " }" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = coordAdjusted.xy * textureRect.zw + textureRect.xy;" << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
+ }
+ }
+ } else if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = aCoord.xy * textureRect.zw + textureRect.xy;" << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
+ }
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << " vMaskCoord.xy = (uMaskTransform * (finalPosition / finalPosition.w)).xy;" << endl;
+ // correct for perspective correct interpolation, see comment in D3D11 shader
+ vs << " vMaskCoord.z = 1.0;" << endl;
+ vs << " vMaskCoord *= finalPosition.w;" << endl;
+ }
+ vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Translate from clip space (-1, 1) to (0..1), apply the backdrop
+ // transform, then invert the y-axis.
+ vs << " vBackdropCoord.x = (finalPosition.x + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord.y = 1.0 - (finalPosition.y + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord = (uBackdropTransform * vec4(vBackdropCoord.xy, 0.0, 1.0)).xy;" << endl;
+ vs << " vBackdropCoord.y = 1.0 - vBackdropCoord.y;" << endl;
+ }
+ vs << " gl_Position = finalPosition;" << endl;
+ vs << "}" << endl;
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "#extension GL_ARB_texture_rectangle : require" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ fs << "#extension GL_OES_EGL_image_external : require" << endl;
+ }
+ fs << "#ifdef GL_ES" << endl;
+ fs << "precision mediump float;" << endl;
+ fs << "#define COLOR_PRECISION lowp" << endl;
+ fs << "#define EDGE_PRECISION mediump" << endl;
+ fs << "#else" << endl;
+ fs << "#define COLOR_PRECISION" << endl;
+ fs << "#define EDGE_PRECISION" << endl;
+ fs << "#endif" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << "uniform COLOR_PRECISION vec4 uRenderColor;" << endl;
+ } else {
+ // for tiling, texcoord can be greater than the lowfp range
+ fs << "varying vec2 vTexCoord;" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "uniform bool uBlurAlpha;" << endl;
+ fs << "uniform vec2 uBlurRadius;" << endl;
+ fs << "uniform vec2 uBlurOffset;" << endl;
+ fs << "uniform float uBlurGaussianKernel[" << GAUSSIAN_KERNEL_HALF_WIDTH << "];" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << "uniform mat4 uColorMatrix;" << endl;
+ fs << "uniform vec4 uColorMatrixVector;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
+ }
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ const char *sampler2D = "sampler2D";
+ const char *texture2D = "texture2D";
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "uniform vec2 uTexCoordMultiplier;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform vec2 uCbCrTexCoordMultiplier;" << endl;
+ }
+ sampler2D = "sampler2DRect";
+ texture2D = "texture2DRect";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ sampler2D = "samplerExternalOES";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ fs << "uniform sampler2D uYTexture;" << endl;
+ fs << "uniform sampler2D uCbTexture;" << endl;
+ fs << "uniform sampler2D uCrTexture;" << endl;
+ fs << "uniform mat3 uYuvColorMatrix;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform " << sampler2D << " uYTexture;" << endl;
+ fs << "uniform " << sampler2D << " uCbTexture;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ fs << "uniform " << sampler2D << " uBlackTexture;" << endl;
+ fs << "uniform " << sampler2D << " uWhiteTexture;" << endl;
+ fs << "uniform bool uTexturePass2;" << endl;
+ } else {
+ fs << "uniform " << sampler2D << " uTexture;" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Component alpha should be flattened away inside blend containers.
+ MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
+
+ fs << "uniform sampler2D uBackdropTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << "varying vec3 vMaskCoord;" << endl;
+ fs << "uniform sampler2D uMaskTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ BuildMixBlender(aConfig, fs);
+ }
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ fs << "vec4 sample(vec2 coord) {" << endl;
+ fs << " vec4 color;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = texture2D(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = texture2D(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cr = texture2D(uCrTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float y = texture2D(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = texture2D(uCbTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cr = texture2D(uCrTexture, coord).r;" << endl;
+ }
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).a;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord).a;" << endl;
+ }
+ }
+
+ fs << " y = y - 0.06275;" << endl;
+ fs << " cb = cb - 0.50196;" << endl;
+ fs << " cr = cr - 0.50196;" << endl;
+ fs << " vec3 yuv = vec3(y, cb, cr);" << endl;
+ fs << " color.rgb = uYuvColorMatrix * yuv;" << endl;
+ fs << " color.a = 1.0;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D << "(uWhiteTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ } else {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D << "(uWhiteTexture, coord).rgb;" << endl;
+ }
+ fs << " COLOR_PRECISION vec4 alphas = (1.0 - onWhite + onBlack).rgbg;" << endl;
+ fs << " if (uTexturePass2)" << endl;
+ fs << " color = vec4(onBlack, alphas.a);" << endl;
+ fs << " else" << endl;
+ fs << " color = alphas;" << endl;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " color = " << texture2D << "(uTexture, coord * uTexCoordMultiplier);" << endl;
+ } else {
+ fs << " color = " << texture2D << "(uTexture, coord);" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RB_SWAP) {
+ fs << " color = color.bgra;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_NO_ALPHA) {
+ fs << " color = vec4(color.rgb, 1.0);" << endl;
+ }
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "vec4 sampleAtRadius(vec2 coord, float radius) {" << endl;
+ fs << " coord += uBlurOffset;" << endl;
+ fs << " coord += radius * uBlurRadius;" << endl;
+ fs << " if (coord.x < 0. || coord.y < 0. || coord.x > 1. || coord.y > 1.)" << endl;
+ fs << " return vec4(0, 0, 0, 0);" << endl;
+ fs << " return sample(coord);" << endl;
+ fs << "}" << endl;
+ fs << "vec4 blur(vec4 color, vec2 coord) {" << endl;
+ fs << " vec4 total = color * uBlurGaussianKernel[0];" << endl;
+ fs << " for (int i = 1; i < " << GAUSSIAN_KERNEL_HALF_WIDTH << "; ++i) {" << endl;
+ fs << " float r = float(i) * " << GAUSSIAN_KERNEL_STEP << ";" << endl;
+ fs << " float k = uBlurGaussianKernel[i];" << endl;
+ fs << " total += sampleAtRadius(coord, r) * k;" << endl;
+ fs << " total += sampleAtRadius(coord, -r) * k;" << endl;
+ fs << " }" << endl;
+ fs << " if (uBlurAlpha) {" << endl;
+ fs << " color *= total.a;" << endl;
+ fs << " } else {" << endl;
+ fs << " color = total;" << endl;
+ fs << " }" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ }
+ }
+ fs << "void main() {" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << " vec4 color = uRenderColor;" << endl;
+ } else {
+ fs << " vec4 color = sample(vTexCoord);" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << " color = blur(color, vTexCoord);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << " color = uColorMatrix * vec4(color.rgb / color.a, color.a) + uColorMatrixVector;" << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << " color *= uLayerOpacity;" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // Calculate the sub-pixel coverage of the pixel and modulate its opacity
+ // by that amount to perform DEAA.
+ fs << " vec3 ssPos = vec3(gl_FragCoord.xy, 1.0);" << endl;
+ fs << " float deaaCoverage = clamp(dot(uSSEdges[0], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[1], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[2], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
+ fs << " color *= deaaCoverage;" << endl;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);" << endl;
+ fs << " color = mixAndBlend(backdrop, color);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
+ fs << " COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
+ fs << " color *= mask;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float mask = 1.0;" << endl;
+ fs << " color *= mask;" << endl;
+ }
+ fs << " gl_FragColor = color;" << endl;
+ fs << "}" << endl;
+
+ result.mVertexShaderString = vs.str();
+ result.mFragmentShaderString = fs.str();
+
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ result.mTextureCount = 0;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ result.mTextureCount = 3;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ result.mTextureCount = 2;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ result.mTextureCount = 2;
+ } else {
+ result.mTextureCount = 1;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ result.mTextureCount = 1;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ result.mTextureCount += 1;
+ }
+
+ return result;
+}
+
+void
+ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs)
+{
+ // From the "Compositing and Blending Level 1" spec.
+ // Generate helper functions first.
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_OVERLAY:
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ // Note: we substitute (2*src-1) into the screen formula below.
+ fs << "float hardlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest * (2.0 * src);" << endl;
+ fs << " } else {" << endl;
+ fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << "float dodge(float dest, float src) {" << endl;
+ fs << " if (dest == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else if (src == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return min(1.0, dest / (1.0 - src));" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << "float burn(float dest, float src) {" << endl;
+ fs << " if (dest == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else if (src == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << "float darken(float dest) {" << endl;
+ fs << " if (dest <= 0.25) {" << endl;
+ fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
+ fs << " } else {" << endl;
+ fs << " return sqrt(dest);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ fs << "float softlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);" << endl;
+ fs << " } else {" << endl;
+ fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ case gfx::CompositionOp::OP_SATURATION:
+ case gfx::CompositionOp::OP_COLOR:
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << "float Lum(vec3 c) {" << endl;
+ fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
+ fs << "}" << endl;
+ fs << "vec3 ClipColor(vec3 c) {" << endl;
+ fs << " float L = Lum(c);" << endl;
+ fs << " float n = min(min(c.r, c.g), c.b);" << endl;
+ fs << " float x = max(max(c.r, c.g), c.b);" << endl;
+ fs << " if (n < 0.0) {" << endl;
+ fs << " c = L + (((c - L) * L) / (L - n));" << endl;
+ fs << " }" << endl;
+ fs << " if (x > 1.0) {" << endl;
+ fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ fs << "vec3 SetLum(vec3 c, float L) {" << endl;
+ fs << " float d = L - Lum(c);" << endl;
+ fs << " return ClipColor(vec3(" << endl;
+ fs << " c.r + d," << endl;
+ fs << " c.g + d," << endl;
+ fs << " c.b + d));" << endl;
+ fs << "}" << endl;
+ fs << "float Sat(vec3 c) {" << endl;
+ fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);" << endl;
+ fs << "}" << endl;
+
+ // To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+ fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
+ fs << " if (c.b > c.r) {" << endl;
+ fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
+ fs << " c.b = s;" << endl;
+ fs << " } else {" << endl;
+ fs << " c.gb = vec2(0.0, 0.0);" << endl;
+ fs << " }" << endl;
+ fs << " return vec3(0.0, c.gb);" << endl;
+ fs << "}" << endl;
+
+ fs << "vec3 SetSat(vec3 c, float s) {" << endl;
+ fs << " if (c.r <= c.g) {" << endl;
+ fs << " if (c.g <= c.b) {" << endl;
+ fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.brg = SetSatInner(c.brg, s);" << endl;
+ fs << " }" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.grb = SetSatInner(c.grb, s);" << endl;
+ fs << " } else if (c.g <= c.b) {" << endl;
+ fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ break;
+ default:
+ break;
+ }
+
+ // Generate the main blending helper.
+ fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ fs << " return dest * src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_SCREEN:
+ fs << " return dest + src - (dest * src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_OVERLAY:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(src.r, dest.r)," << endl;
+ fs << " hardlight(src.g, dest.g)," << endl;
+ fs << " hardlight(src.b, dest.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DARKEN:
+ fs << " return min(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_LIGHTEN:
+ fs << " return max(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << " return vec3(" << endl;
+ fs << " dodge(dest.r, src.r)," << endl;
+ fs << " dodge(dest.g, src.g)," << endl;
+ fs << " dodge(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << " return vec3(" << endl;
+ fs << " burn(dest.r, src.r)," << endl;
+ fs << " burn(dest.g, src.g)," << endl;
+ fs << " burn(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(dest.r, src.r)," << endl;
+ fs << " hardlight(dest.g, src.g)," << endl;
+ fs << " hardlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " softlight(dest.r, src.r)," << endl;
+ fs << " softlight(dest.g, src.g)," << endl;
+ fs << " softlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ fs << " return abs(dest - src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_EXCLUSION:
+ fs << " return dest + src - 2.0*dest*src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SATURATION:
+ fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR:
+ fs << " return SetLum(src, Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << " return SetLum(dest, Lum(src));" << endl;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown blend mode");
+ }
+ fs << "}" << endl;
+
+ // Generate the mix-blend function the fragment shader will call.
+ fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // Infinity into the blend function and return incorrect results.
+ fs << " if (backdrop.a == 0.0) {" << endl;
+ fs << " return color;" << endl;
+ fs << " }" << endl;
+ fs << " if (color.a == 0.0) {" << endl;
+ fs << " return vec4(0.0, 0.0, 0.0, 0.0);" << endl;
+ fs << " }" << endl;
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ fs << " backdrop.rgb /= backdrop.a;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
+ fs << " color.rgb /= color.a;" << endl;
+ }
+ fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
+ fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * blended.rgb;" << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+}
+
+ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
+ : mGL(aGL)
+ , mProgram(0)
+ , mProfile(aProfile)
+ , mProgramState(STATE_NEW)
+{
+}
+
+ShaderProgramOGL::~ShaderProgramOGL()
+{
+ if (mProgram <= 0) {
+ return;
+ }
+
+ RefPtr<GLContext> ctx = mGL->GetSharedContext();
+ if (!ctx) {
+ ctx = mGL;
+ }
+ ctx->MakeCurrent();
+ ctx->fDeleteProgram(mProgram);
+}
+
+bool
+ShaderProgramOGL::Initialize()
+{
+ NS_ASSERTION(mProgramState == STATE_NEW, "Shader program has already been initialised");
+
+ ostringstream vs, fs;
+ for (uint32_t i = 0; i < mProfile.mDefines.Length(); ++i) {
+ vs << mProfile.mDefines[i] << endl;
+ fs << mProfile.mDefines[i] << endl;
+ }
+ vs << mProfile.mVertexShaderString << endl;
+ fs << mProfile.mFragmentShaderString << endl;
+
+ if (!CreateProgram(vs.str().c_str(), fs.str().c_str())) {
+ mProgramState = STATE_ERROR;
+ return false;
+ }
+
+ mProgramState = STATE_OK;
+
+ for (uint32_t i = 0; i < KnownUniform::KnownUniformCount; ++i) {
+ mProfile.mUniforms[i].mLocation =
+ mGL->fGetUniformLocation(mProgram, mProfile.mUniforms[i].mNameString);
+ }
+
+ return true;
+}
+
+GLint
+ShaderProgramOGL::CreateShader(GLenum aShaderType, const char *aShaderSource)
+{
+ GLint success, len = 0;
+
+ GLint sh = mGL->fCreateShader(aShaderType);
+ mGL->fShaderSource(sh, 1, (const GLchar**)&aShaderSource, nullptr);
+ mGL->fCompileShader(sh);
+ mGL->fGetShaderiv(sh, LOCAL_GL_COMPILE_STATUS, &success);
+ mGL->fGetShaderiv(sh, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ /* Even if compiling is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::DebugShaders())
+#endif
+ )
+ {
+ nsAutoCString log;
+ log.SetCapacity(len);
+ mGL->fGetShaderInfoLog(sh, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ if (!success) {
+ printf_stderr("=== SHADER COMPILATION FAILED ===\n");
+ } else {
+ printf_stderr("=== SHADER COMPILATION WARNINGS ===\n");
+ }
+
+ printf_stderr("=== Source:\n%s\n", aShaderSource);
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+
+ if (!success) {
+ mGL->fDeleteShader(sh);
+ return 0;
+ }
+ }
+
+ return sh;
+}
+
+bool
+ShaderProgramOGL::CreateProgram(const char *aVertexShaderString,
+ const char *aFragmentShaderString)
+{
+ GLuint vertexShader = CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString);
+ GLuint fragmentShader = CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString);
+
+ if (!vertexShader || !fragmentShader)
+ return false;
+
+ GLint result = mGL->fCreateProgram();
+ mGL->fAttachShader(result, vertexShader);
+ mGL->fAttachShader(result, fragmentShader);
+
+ for (Pair<nsCString, GLuint>& attribute : mProfile.mAttributes) {
+ mGL->fBindAttribLocation(result, attribute.second(),
+ attribute.first().get());
+ }
+
+ mGL->fLinkProgram(result);
+
+ GLint success, len;
+ mGL->fGetProgramiv(result, LOCAL_GL_LINK_STATUS, &success);
+ mGL->fGetProgramiv(result, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ /* Even if linking is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::DebugShaders())
+#endif
+ )
+ {
+ nsAutoCString log;
+ log.SetCapacity(len);
+ mGL->fGetProgramInfoLog(result, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ if (!success) {
+ printf_stderr("=== PROGRAM LINKING FAILED ===\n");
+ } else {
+ printf_stderr("=== PROGRAM LINKING WARNINGS ===\n");
+ }
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+ }
+
+ // We can mark the shaders for deletion; they're attached to the program
+ // and will remain attached.
+ mGL->fDeleteShader(vertexShader);
+ mGL->fDeleteShader(fragmentShader);
+
+ if (!success) {
+ mGL->fDeleteProgram(result);
+ return false;
+ }
+
+ mProgram = result;
+ return true;
+}
+
+GLuint
+ShaderProgramOGL::GetProgram()
+{
+ if (mProgramState == STATE_NEW) {
+ if (!Initialize()) {
+ NS_WARNING("Shader could not be initialised");
+ }
+ }
+ MOZ_ASSERT(HasInitialized(), "Attempting to get a program that's not been initialized!");
+ return mProgram;
+}
+
+void
+ShaderProgramOGL::SetBlurRadius(float aRX, float aRY)
+{
+ float f[] = {aRX, aRY};
+ SetUniform(KnownUniform::BlurRadius, 2, f);
+
+ float gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH];
+ float sum = 0.0f;
+ for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
+ float x = i * GAUSSIAN_KERNEL_STEP;
+ float sigma = 1.0f;
+ gaussianKernel[i] = exp(-x * x / (2 * sigma * sigma)) / sqrt(2 * M_PI * sigma * sigma);
+ sum += gaussianKernel[i] * (i == 0 ? 1 : 2);
+ }
+ for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
+ gaussianKernel[i] /= sum;
+ }
+ SetArrayUniform(KnownUniform::BlurGaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel);
+}
+
+void
+ShaderProgramOGL::SetYUVColorSpace(YUVColorSpace aYUVColorSpace)
+{
+ float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(aYUVColorSpace);
+ SetMatrix3fvUniform(KnownUniform::YuvColorMatrix, yuvToRgb);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/OGLShaderProgram.h b/system/graphics/layers/opengl/OGLShaderProgram.h
new file mode 100644
index 000000000..ff4fb825f
--- /dev/null
+++ b/system/graphics/layers/opengl/OGLShaderProgram.h
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_OGLSHADERPROGRAM_H
+#define GFX_OGLSHADERPROGRAM_H
+
+#include "GLContext.h" // for fast inlines of glUniform*
+#include "gfxTypes.h"
+#include "ImageTypes.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Pair.h" // for Pair
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/CompositorTypes.h"
+
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+enum ShaderFeatures {
+ ENABLE_RENDER_COLOR=0x01,
+ ENABLE_TEXTURE_RECT=0x02,
+ ENABLE_TEXTURE_EXTERNAL=0x04,
+ ENABLE_TEXTURE_YCBCR=0x08,
+ ENABLE_TEXTURE_NV12=0x10,
+ ENABLE_TEXTURE_COMPONENT_ALPHA=0x20,
+ ENABLE_TEXTURE_NO_ALPHA=0x40,
+ ENABLE_TEXTURE_RB_SWAP=0x80,
+ ENABLE_OPACITY=0x100,
+ ENABLE_BLUR=0x200,
+ ENABLE_COLOR_MATRIX=0x400,
+ ENABLE_MASK=0x800,
+ ENABLE_NO_PREMUL_ALPHA=0x1000,
+ ENABLE_DEAA=0x2000,
+ ENABLE_DYNAMIC_GEOMETRY=0x4000
+};
+
+class KnownUniform {
+public:
+ // this needs to be kept in sync with strings in 'AddUniforms'
+ enum KnownUniformName {
+ NotAKnownUniform = -1,
+
+ LayerTransform = 0,
+ LayerTransformInverse,
+ MaskTransform,
+ BackdropTransform,
+ LayerRects,
+ MatrixProj,
+ TextureTransform,
+ TextureRects,
+ RenderTargetOffset,
+ LayerOpacity,
+ Texture,
+ YTexture,
+ CbTexture,
+ CrTexture,
+ BlackTexture,
+ WhiteTexture,
+ MaskTexture,
+ BackdropTexture,
+ RenderColor,
+ TexCoordMultiplier,
+ CbCrTexCoordMultiplier,
+ TexturePass2,
+ ColorMatrix,
+ ColorMatrixVector,
+ BlurRadius,
+ BlurOffset,
+ BlurAlpha,
+ BlurGaussianKernel,
+ SSEdges,
+ ViewportSize,
+ VisibleCenter,
+ YuvColorMatrix,
+
+ KnownUniformCount
+ };
+
+ KnownUniform()
+ {
+ mName = NotAKnownUniform;
+ mNameString = nullptr;
+ mLocation = -1;
+ memset(&mValue, 0, sizeof(mValue));
+ }
+
+ bool UpdateUniform(int32_t i1) {
+ if (mLocation == -1) return false;
+ if (mValue.i1 != i1) {
+ mValue.i1 = i1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1) {
+ if (mLocation == -1) return false;
+ if (mValue.f1 != f1) {
+ mValue.f1 = f1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 ||
+ mValue.f16v[1] != f2)
+ {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2, float f3, float f4) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 ||
+ mValue.f16v[1] != f2 ||
+ mValue.f16v[2] != f3 ||
+ mValue.f16v[3] != f4)
+ {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ mValue.f16v[2] = f3;
+ mValue.f16v[3] = f4;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(int cnt, const float *fp) {
+ if (mLocation == -1) return false;
+ switch (cnt) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 9:
+ case 16:
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ NS_NOTREACHED("cnt must be 1 2 3 4 9 or 16");
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const float *fp) {
+ if (mLocation == -1) return false;
+ if (cnt > 16) {
+ return false;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const gfx::Point3D* points) {
+ if (mLocation == -1) return false;
+ if (cnt > 4) {
+ return false;
+ }
+
+ float fp[12];
+ float *d = fp;
+ for(int i=0; i < cnt; i++) {
+ // Note: Do not want to make assumptions about .x, .y, .z member packing.
+ // If gfx::Point3D is updated to make this guarantee, SIMD optimizations
+ // may be possible
+ *d++ = points[i].x;
+ *d++ = points[i].y;
+ *d++ = points[i].z;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt * 3) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt * 3);
+ return true;
+ }
+ return false;
+ }
+
+ KnownUniformName mName;
+ const char *mNameString;
+ int32_t mLocation;
+
+ union {
+ int i1;
+ float f1;
+ float f16v[16];
+ } mValue;
+};
+
+class ShaderConfigOGL
+{
+public:
+ ShaderConfigOGL() :
+ mFeatures(0),
+ mCompositionOp(gfx::CompositionOp::OP_OVER)
+ {}
+
+ void SetRenderColor(bool aEnabled);
+ void SetTextureTarget(GLenum aTarget);
+ void SetRBSwap(bool aEnabled);
+ void SetNoAlpha(bool aEnabled);
+ void SetOpacity(bool aEnabled);
+ void SetYCbCr(bool aEnabled);
+ void SetNV12(bool aEnabled);
+ void SetComponentAlpha(bool aEnabled);
+ void SetColorMatrix(bool aEnabled);
+ void SetBlur(bool aEnabled);
+ void SetMask(bool aEnabled);
+ void SetDEAA(bool aEnabled);
+ void SetCompositionOp(gfx::CompositionOp aOp);
+ void SetNoPremultipliedAlpha();
+ void SetDynamicGeometry(bool aEnabled);
+
+ bool operator< (const ShaderConfigOGL& other) const {
+ return mFeatures < other.mFeatures ||
+ (mFeatures == other.mFeatures &&
+ (int)mCompositionOp < (int)other.mCompositionOp);
+ }
+
+public:
+ void SetFeature(int aBitmask, bool aState) {
+ if (aState)
+ mFeatures |= aBitmask;
+ else
+ mFeatures &= (~aBitmask);
+ }
+
+ int mFeatures;
+ gfx::CompositionOp mCompositionOp;
+};
+
+static inline ShaderConfigOGL
+ShaderConfigFromTargetAndFormat(GLenum aTarget,
+ gfx::SurfaceFormat aFormat)
+{
+ ShaderConfigOGL config;
+ config.SetTextureTarget(aTarget);
+ config.SetRBSwap(aFormat == gfx::SurfaceFormat::B8G8R8A8 ||
+ aFormat == gfx::SurfaceFormat::B8G8R8X8);
+ config.SetNoAlpha(aFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+ aFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ aFormat == gfx::SurfaceFormat::R5G6B5_UINT16);
+ return config;
+}
+
+/**
+ * This struct represents the shaders that make up a program and the uniform
+ * and attribute parmeters that those shaders take.
+ * It is used by ShaderProgramOGL.
+ * Use the factory method GetProfileFor to create instances.
+ */
+struct ProgramProfileOGL
+{
+ /**
+ * Factory method; creates an instance of this class for the given
+ * ShaderConfigOGL
+ */
+ static ProgramProfileOGL GetProfileFor(ShaderConfigOGL aConfig);
+
+ // the source code for the program's shaders
+ std::string mVertexShaderString;
+ std::string mFragmentShaderString;
+
+ // the vertex attributes
+ nsTArray<Pair<nsCString, GLuint>> mAttributes;
+
+ KnownUniform mUniforms[KnownUniform::KnownUniformCount];
+ nsTArray<const char *> mDefines;
+ size_t mTextureCount;
+
+ ProgramProfileOGL() :
+ mTextureCount(0)
+ {}
+
+ private:
+ static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs);
+};
+
+
+#if defined(DEBUG)
+#define CHECK_CURRENT_PROGRAM 1
+#define ASSERT_THIS_PROGRAM \
+ do { \
+ GLuint currentProgram; \
+ mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram); \
+ MOZ_ASSERT(currentProgram == mProgram, \
+ "SetUniform with wrong program active!"); \
+ } while (0)
+#else
+#define ASSERT_THIS_PROGRAM \
+ do { } while (0)
+#endif
+
+/**
+ * Represents an OGL shader program. The details of a program are represented
+ * by a ProgramProfileOGL
+ */
+class ShaderProgramOGL
+{
+public:
+ typedef mozilla::gl::GLContext GLContext;
+
+ ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile);
+
+ ~ShaderProgramOGL();
+
+ bool HasInitialized() {
+ NS_ASSERTION(mProgramState != STATE_OK || mProgram > 0, "Inconsistent program state");
+ return mProgramState == STATE_OK;
+ }
+
+ GLuint GetProgram();
+
+ bool Initialize();
+
+ GLint CreateShader(GLenum aShaderType, const char *aShaderSource);
+
+ /**
+ * Creates a program and stores its id.
+ */
+ bool CreateProgram(const char *aVertexShaderString,
+ const char *aFragmentShaderString);
+
+ /**
+ * The following set of methods set a uniform argument to the shader program.
+ * Not all uniforms may be set for all programs, and such uses will throw
+ * an assertion.
+ */
+ void SetLayerTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransform, aMatrix);
+ }
+
+ void SetLayerTransformInverse(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransformInverse, aMatrix);
+ }
+
+ void SetMaskLayerTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::MaskTransform, aMatrix);
+ }
+
+ void SetBackdropTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::BackdropTransform, aMatrix);
+ }
+
+ void SetDEAAEdges(const gfx::Point3D* aEdges) {
+ SetArrayUniform(KnownUniform::SSEdges, 4, aEdges);
+ }
+
+ void SetViewportSize(const gfx::IntSize& aSize) {
+ float vals[2] = { (float)aSize.width, (float)aSize.height };
+ SetUniform(KnownUniform::ViewportSize, 2, vals);
+ }
+
+ void SetVisibleCenter(const gfx::Point& aVisibleCenter) {
+ float vals[2] = { aVisibleCenter.x, aVisibleCenter.y };
+ SetUniform(KnownUniform::VisibleCenter, 2, vals);
+ }
+
+ void SetLayerRects(const gfx::Rect* aRects) {
+ float vals[16] = { aRects[0].x, aRects[0].y, aRects[0].width, aRects[0].height,
+ aRects[1].x, aRects[1].y, aRects[1].width, aRects[1].height,
+ aRects[2].x, aRects[2].y, aRects[2].width, aRects[2].height,
+ aRects[3].x, aRects[3].y, aRects[3].width, aRects[3].height };
+ SetUniform(KnownUniform::LayerRects, 16, vals);
+ }
+
+ void SetProjectionMatrix(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::MatrixProj, aMatrix);
+ }
+
+ // sets this program's texture transform, if it uses one
+ void SetTextureTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::TextureTransform, aMatrix);
+ }
+
+ void SetTextureRects(const gfx::Rect* aRects) {
+ float vals[16] = { aRects[0].x, aRects[0].y, aRects[0].width, aRects[0].height,
+ aRects[1].x, aRects[1].y, aRects[1].width, aRects[1].height,
+ aRects[2].x, aRects[2].y, aRects[2].width, aRects[2].height,
+ aRects[3].x, aRects[3].y, aRects[3].width, aRects[3].height };
+ SetUniform(KnownUniform::TextureRects, 16, vals);
+ }
+
+ void SetRenderOffset(const nsIntPoint& aOffset) {
+ float vals[4] = { float(aOffset.x), float(aOffset.y) };
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetRenderOffset(float aX, float aY) {
+ float vals[2] = { aX, aY };
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetLayerOpacity(float aOpacity) {
+ SetUniform(KnownUniform::LayerOpacity, aOpacity);
+ }
+
+ void SetTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::Texture, aUnit);
+ }
+ void SetYTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::YTexture, aUnit);
+ }
+
+ void SetCbTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CbTexture, aUnit);
+ }
+
+ void SetCrTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CrTexture, aUnit);
+ }
+
+ void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbUnit);
+ SetUniform(KnownUniform::CrTexture, aCrUnit);
+ }
+
+ void SetNV12TextureUnits(GLint aYUnit, GLint aCbCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbCrUnit);
+ }
+
+ void SetBlackTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::BlackTexture, aUnit);
+ }
+
+ void SetWhiteTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::WhiteTexture, aUnit);
+ }
+
+ void SetMaskTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::MaskTexture, aUnit);
+ }
+
+ void SetBackdropTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::BackdropTexture, aUnit);
+ }
+
+ void SetRenderColor(const gfx::Color& aColor) {
+ SetUniform(KnownUniform::RenderColor, aColor);
+ }
+
+ void SetColorMatrix(const gfx::Matrix5x4& aColorMatrix)
+ {
+ SetMatrixUniform(KnownUniform::ColorMatrix, &aColorMatrix._11);
+ SetUniform(KnownUniform::ColorMatrixVector, 4, &aColorMatrix._51);
+ }
+
+ void SetTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::TexCoordMultiplier, 2, f);
+ }
+
+ void SetCbCrTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::CbCrTexCoordMultiplier, 2, f);
+ }
+
+ void SetYUVColorSpace(YUVColorSpace aYUVColorSpace);
+
+ // Set whether we want the component alpha shader to return the color
+ // vector (pass 1, false) or the alpha vector (pass2, true). With support
+ // for multiple render targets we wouldn't need two passes here.
+ void SetTexturePass2(bool aFlag) {
+ SetUniform(KnownUniform::TexturePass2, aFlag ? 1 : 0);
+ }
+
+ void SetBlurRadius(float aRX, float aRY);
+
+ void SetBlurAlpha(float aAlpha) {
+ SetUniform(KnownUniform::BlurAlpha, aAlpha);
+ }
+
+ void SetBlurOffset(float aOffsetX, float aOffsetY) {
+ float f[] = {aOffsetX, aOffsetY};
+ SetUniform(KnownUniform::BlurOffset, 2, f);
+ }
+
+ size_t GetTextureCount() const {
+ return mProfile.mTextureCount;
+ }
+
+protected:
+ RefPtr<GLContext> mGL;
+ // the OpenGL id of the program
+ GLuint mProgram;
+ ProgramProfileOGL mProfile;
+ enum {
+ STATE_NEW,
+ STATE_OK,
+ STATE_ERROR
+ } mProgramState;
+
+#ifdef CHECK_CURRENT_PROGRAM
+ static int sCurrentProgramKey;
+#endif
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, float aFloatValue)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aFloatValue)) {
+ mGL->fUniform1f(ku.mLocation, aFloatValue);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Color& aColor) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) {
+ mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, const float *aFloatValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aLength, aFloatValues)) {
+ switch (aLength) {
+ case 1: mGL->fUniform1fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 2: mGL->fUniform2fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 3: mGL->fUniform3fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 4: mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 16: mGL->fUniform4fv(ku.mLocation, 4, ku.mValue.f16v); break;
+ default:
+ NS_NOTREACHED("Bogus aLength param");
+ }
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, float *aFloatValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aFloatValues)) {
+ mGL->fUniform1fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, const gfx::Point3D *aPointValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aPointValues)) {
+ mGL->fUniform3fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, GLint aIntValue) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aIntValue)) {
+ mGL->fUniform1i(ku.mLocation, aIntValue);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const float *aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(16, aFloatValues)) {
+ mGL->fUniformMatrix4fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrix3fvUniform(KnownUniform::KnownUniformName aKnownUniform, const float *aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(9, aFloatValues)) {
+ mGL->fUniformMatrix3fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(aKnownUniform, &aMatrix._11);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_OGLSHADERPROGRAM_H */
diff --git a/system/graphics/layers/opengl/TextureClientOGL.cpp b/system/graphics/layers/opengl/TextureClientOGL.cpp
new file mode 100644
index 000000000..1e337717b
--- /dev/null
+++ b/system/graphics/layers/opengl/TextureClientOGL.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContext.h" // for GLContext, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "GLLibraryEGL.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+class CompositableForwarder;
+
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+EGLImageTextureData::EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize)
+: mImage(aImage)
+, mSize(aSize)
+{
+ MOZ_ASSERT(aImage);
+}
+
+already_AddRefed<TextureClient>
+EGLImageTextureData::CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "Can't pass an `EGLImage` between processes.");
+
+ if (!aImage || !XRE_IsParentProcess()) {
+ return nullptr;
+ }
+
+ // XXX - This is quite sad and slow.
+ aFlags |= TextureFlags::DEALLOCATE_CLIENT;
+
+ if (aImage->GetOriginPos() == gl::OriginPos::BottomLeft) {
+ aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ return TextureClient::CreateWithData(
+ new EGLImageTextureData(aImage, aSize),
+ aFlags, aAllocator
+ );
+}
+
+void
+EGLImageTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+EGLImageTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ const bool hasAlpha = true;
+ aOutDescriptor =
+ EGLImageDescriptor((uintptr_t)mImage->GetImage(),
+ (uintptr_t)mImage->GetSync(),
+ mImage->GetSize(), hasAlpha);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/TextureClientOGL.h b/system/graphics/layers/opengl/TextureClientOGL.h
new file mode 100644
index 000000000..b996f7eba
--- /dev/null
+++ b/system/graphics/layers/opengl/TextureClientOGL.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTOGL_H
+#define MOZILLA_GFX_TEXTURECLIENTOGL_H
+
+#include "GLContextTypes.h" // for SharedTextureHandle, etc
+#include "GLImages.h"
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+
+namespace mozilla {
+
+namespace layers {
+
+class EGLImageTextureData : public TextureData
+{
+public:
+
+ static already_AddRefed<TextureClient>
+ CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags);
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override { mImage = nullptr; }
+
+ virtual void Forget(LayersIPCChannel*) override { mImage = nullptr; }
+
+ // Unused functions.
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+protected:
+ EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize);
+
+ RefPtr<EGLImageImage> mImage;
+ const gfx::IntSize mSize;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/opengl/TextureHostOGL.cpp b/system/graphics/layers/opengl/TextureHostOGL.cpp
new file mode 100644
index 000000000..35c83686a
--- /dev/null
+++ b/system/graphics/layers/opengl/TextureHostOGL.cpp
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHostOGL.h"
+
+#include "EGLUtils.h"
+#include "GLContext.h" // for GLContext, etc
+#include "GLLibraryEGL.h" // for GLLibraryEGL
+#include "GLUploadHelpers.h"
+#include "GLReadTexImageHelper.h"
+#include "gfx2DGlue.h" // for ContentForFormat, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Logging.h" // for gfxCriticalError
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "GfxTexturesReporter.h" // for GfxTexturesReporter
+#include "GLBlitTextureImageHelper.h"
+#include "GeckoProfiler.h"
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+already_AddRefed<TextureHost>
+CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TEGLImageDescriptor: {
+ const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor();
+ result = new EGLImageTextureHost(aFlags,
+ (EGLImage)desc.image(),
+ (EGLSync)desc.fence(),
+ desc.size(),
+ desc.hasAlpha());
+ break;
+ }
+
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
+ const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
+ result = new GLTextureHost(aFlags, desc.texture(),
+ desc.target(),
+ (GLsync)desc.fence(),
+ desc.size(),
+ desc.hasAlpha());
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Unsupported SurfaceDescriptor type");
+ break;
+ }
+ }
+ return result.forget();
+}
+
+static gl::TextureImage::Flags
+FlagsToGLFlags(TextureFlags aFlags)
+{
+ uint32_t result = TextureImage::NoFlags;
+
+ if (aFlags & TextureFlags::USE_NEAREST_FILTER)
+ result |= TextureImage::UseNearestFilter;
+ if (aFlags & TextureFlags::ORIGIN_BOTTOM_LEFT)
+ result |= TextureImage::OriginBottomLeft;
+ if (aFlags & TextureFlags::DISALLOW_BIGIMAGE)
+ result |= TextureImage::DisallowBigImage;
+
+ return static_cast<gl::TextureImage::Flags>(result);
+}
+
+bool
+TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ GLContext *gl = mCompositor->gl();
+ MOZ_ASSERT(gl);
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("trying to update TextureImageTextureSourceOGL without a GLContext");
+ return false;
+ }
+ if (!aSurface) {
+ gfxCriticalError() << "Invalid surface for OGL update";
+ return false;
+ }
+ MOZ_ASSERT(aSurface);
+
+ IntSize size = aSurface->GetSize();
+ if (!mTexImage ||
+ (mTexImage->GetSize() != size && !aSrcOffset) ||
+ mTexImage->GetContentType() != gfx::ContentForFormat(aSurface->GetFormat())) {
+ if (mFlags & TextureFlags::DISALLOW_BIGIMAGE) {
+ GLint maxTextureSize;
+ gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ if (size.width > maxTextureSize || size.height > maxTextureSize) {
+ NS_WARNING("Texture exceeds maximum texture size, refusing upload");
+ return false;
+ }
+ // Explicitly use CreateBasicTextureImage instead of CreateTextureImage,
+ // because CreateTextureImage might still choose to create a tiled
+ // texture image.
+ mTexImage = CreateBasicTextureImage(gl, size,
+ gfx::ContentForFormat(aSurface->GetFormat()),
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags));
+ } else {
+ // XXX - clarify which size we want to use. IncrementalContentHost will
+ // require the size of the destination surface to be different from
+ // the size of aSurface.
+ // See bug 893300 (tracks the implementation of ContentHost for new textures).
+ mTexImage = CreateTextureImage(gl,
+ size,
+ gfx::ContentForFormat(aSurface->GetFormat()),
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags),
+ SurfaceFormatToImageFormat(aSurface->GetFormat()));
+ }
+ ClearCachedFilter();
+
+ if (aDestRegion &&
+ !aSrcOffset &&
+ !aDestRegion->IsEqual(gfx::IntRect(0, 0, size.width, size.height))) {
+ // UpdateFromDataSource will ignore our specified aDestRegion since the texture
+ // hasn't been allocated with glTexImage2D yet. Call Resize() to force the
+ // allocation (full size, but no upload), and then we'll only upload the pixels
+ // we care about below.
+ mTexImage->Resize(size);
+ }
+ }
+
+ mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset);
+
+ return true;
+}
+
+void
+TextureImageTextureSourceOGL::EnsureBuffer(const IntSize& aSize,
+ gfxContentType aContentType)
+{
+ if (!mTexImage ||
+ mTexImage->GetSize() != aSize ||
+ mTexImage->GetContentType() != aContentType) {
+ mTexImage = CreateTextureImage(mCompositor->gl(),
+ aSize,
+ aContentType,
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags));
+ }
+ mTexImage->Resize(aSize);
+}
+
+void
+TextureImageTextureSourceOGL::CopyTo(const gfx::IntRect& aSourceRect,
+ DataTextureSource *aDest,
+ const gfx::IntRect& aDestRect)
+{
+ MOZ_ASSERT(aDest->AsSourceOGL(), "Incompatible destination type!");
+ TextureImageTextureSourceOGL *dest =
+ aDest->AsSourceOGL()->AsTextureImageTextureSource();
+ MOZ_ASSERT(dest, "Incompatible destination type!");
+
+ mCompositor->BlitTextureImageHelper()->BlitTextureImage(mTexImage, aSourceRect,
+ dest->mTexImage, aDestRect);
+ dest->mTexImage->MarkValid();
+}
+
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* compositor = aCompositor ? aCompositor->AsCompositorOGL()
+ : nullptr;
+ MOZ_ASSERT(!!compositor);
+ return compositor;
+}
+
+void
+TextureImageTextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ DeallocateDeviceData();
+ return;
+ }
+ if (mCompositor != glCompositor) {
+ DeallocateDeviceData();
+ mCompositor = glCompositor;
+ }
+}
+
+gfx::IntSize
+TextureImageTextureSourceOGL::GetSize() const
+{
+ if (mTexImage) {
+ if (mIterating) {
+ return mTexImage->GetTileRect().Size();
+ }
+ return mTexImage->GetSize();
+ }
+ NS_WARNING("Trying to query the size of an empty TextureSource.");
+ return gfx::IntSize(0, 0);
+}
+
+gfx::SurfaceFormat
+TextureImageTextureSourceOGL::GetFormat() const
+{
+ if (mTexImage) {
+ return mTexImage->GetTextureFormat();
+ }
+ NS_WARNING("Trying to query the format of an empty TextureSource.");
+ return gfx::SurfaceFormat::UNKNOWN;
+}
+
+gfx::IntRect TextureImageTextureSourceOGL::GetTileRect()
+{
+ return mTexImage->GetTileRect();
+}
+
+void
+TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ MOZ_ASSERT(mTexImage,
+ "Trying to bind a TextureSource that does not have an underlying GL texture.");
+ mTexImage->BindTexture(aTextureUnit);
+ SetSamplingFilter(mCompositor->gl(), aSamplingFilter);
+}
+
+////////////////////////////////////////////////////////////////////////
+// GLTextureSource
+
+GLTextureSource::GLTextureSource(CompositorOGL* aCompositor,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ bool aExternallyOwned)
+ : mCompositor(aCompositor)
+ , mTextureHandle(aTextureHandle)
+ , mTextureTarget(aTarget)
+ , mSize(aSize)
+ , mFormat(aFormat)
+ , mExternallyOwned(aExternallyOwned)
+{
+ MOZ_COUNT_CTOR(GLTextureSource);
+}
+
+GLTextureSource::~GLTextureSource()
+{
+ MOZ_COUNT_DTOR(GLTextureSource);
+ if (!mExternallyOwned) {
+ DeleteTextureHandle();
+ }
+}
+
+void
+GLTextureSource::DeallocateDeviceData()
+{
+ if (!mExternallyOwned) {
+ DeleteTextureHandle();
+ }
+}
+
+void
+GLTextureSource::DeleteTextureHandle()
+{
+ GLContext* gl = this->gl();
+ if (mTextureHandle != 0 && gl && gl->MakeCurrent()) {
+ gl->fDeleteTextures(1, &mTextureHandle);
+ }
+ mTextureHandle = 0;
+}
+
+void
+GLTextureSource::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ MOZ_ASSERT(mTextureHandle != 0);
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return;
+ }
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(mTextureTarget, mTextureHandle);
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
+}
+
+void
+GLTextureSource::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ return;
+ }
+
+ if (mCompositor && mCompositor != glCompositor) {
+ gfxCriticalError() << "GLTextureSource does not support changing compositors";
+ }
+ mCompositor = glCompositor;
+
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+bool
+GLTextureSource::IsValid() const
+{
+ return !!gl() && mTextureHandle != 0;
+}
+
+gl::GLContext*
+GLTextureSource::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+EGLImageTextureSource::EGLImageTextureSource(CompositorOGL* aCompositor,
+ EGLImage aImage,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize)
+ : mCompositor(aCompositor)
+ , mImage(aImage)
+ , mFormat(aFormat)
+ , mTextureTarget(aTarget)
+ , mWrapMode(aWrapMode)
+ , mSize(aSize)
+{
+ MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
+ mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
+}
+
+void
+EGLImageTextureSource::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("Trying to bind a texture without a GLContext");
+ return;
+ }
+
+ MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl),
+ "EGLImage not supported or disabled in runtime");
+
+ GLuint tex = mCompositor->GetTemporaryTexture(mTextureTarget, aTextureUnit);
+
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(mTextureTarget, tex);
+
+ gl->fEGLImageTargetTexture2D(mTextureTarget, mImage);
+
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
+}
+
+void
+EGLImageTextureSource::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertGLCompositor(aCompositor);
+}
+
+bool
+EGLImageTextureSource::IsValid() const
+{
+ return !!gl();
+}
+
+gl::GLContext*
+EGLImageTextureSource::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+gfx::Matrix4x4
+EGLImageTextureSource::GetTextureTransform()
+{
+ gfx::Matrix4x4 ret;
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+EGLImageTextureHost::EGLImageTextureHost(TextureFlags aFlags,
+ EGLImage aImage,
+ EGLSync aSync,
+ gfx::IntSize aSize,
+ bool hasAlpha)
+ : TextureHost(aFlags)
+ , mImage(aImage)
+ , mSync(aSync)
+ , mSize(aSize)
+ , mHasAlpha(hasAlpha)
+ , mCompositor(nullptr)
+{}
+
+EGLImageTextureHost::~EGLImageTextureHost()
+{}
+
+gl::GLContext*
+EGLImageTextureHost::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+EGLImageTextureHost::Lock()
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return false;
+ }
+
+ EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+
+ if (mSync) {
+ MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
+ status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), mSync, 0, LOCAL_EGL_FOREVER);
+ }
+
+ if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+ MOZ_ASSERT(status != 0,
+ "ClientWaitSync generated an error. Has mSync already been destroyed?");
+ return false;
+ }
+
+ if (!mTextureSource) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ GLenum target = gl->GetPreferredEGLImageTextureTarget();
+ GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
+ mTextureSource = new EGLImageTextureSource(mCompositor,
+ mImage,
+ format,
+ target,
+ wrapMode,
+ mSize);
+ }
+
+ return true;
+}
+
+void
+EGLImageTextureHost::Unlock()
+{
+}
+
+void
+EGLImageTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = glCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(glCompositor);
+ }
+}
+
+gfx::SurfaceFormat
+EGLImageTextureHost::GetFormat() const
+{
+ MOZ_ASSERT(mTextureSource);
+ return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+}
+
+//
+
+GLTextureHost::GLTextureHost(TextureFlags aFlags,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ GLsync aSync,
+ gfx::IntSize aSize,
+ bool aHasAlpha)
+ : TextureHost(aFlags)
+ , mTexture(aTextureHandle)
+ , mTarget(aTarget)
+ , mSync(aSync)
+ , mSize(aSize)
+ , mHasAlpha(aHasAlpha)
+ , mCompositor(nullptr)
+{}
+
+GLTextureHost::~GLTextureHost()
+{}
+
+gl::GLContext*
+GLTextureHost::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+GLTextureHost::Lock()
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return false;
+ }
+
+ if (mSync) {
+ if (!gl->MakeCurrent()) {
+ return false;
+ }
+ gl->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED);
+ gl->fDeleteSync(mSync);
+ mSync = 0;
+ }
+
+ if (!mTextureSource) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ mTextureSource = new GLTextureSource(mCompositor,
+ mTexture,
+ mTarget,
+ mSize,
+ format,
+ false /* owned by the client */);
+ }
+
+ return true;
+}
+void
+GLTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = glCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(glCompositor);
+ }
+}
+
+gfx::SurfaceFormat
+GLTextureHost::GetFormat() const
+{
+ MOZ_ASSERT(mTextureSource);
+ return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/TextureHostOGL.h b/system/graphics/layers/opengl/TextureHostOGL.h
new file mode 100644
index 000000000..cac58ee65
--- /dev/null
+++ b/system/graphics/layers/opengl/TextureHostOGL.h
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREOGL_H
+#define MOZILLA_GFX_TEXTUREOGL_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "CompositableHost.h"
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_CLAMP_TO_EDGE, etc
+#include "GLTextureImage.h" // for TextureImage
+#include "gfxTypes.h"
+#include "mozilla/GfxMessageUtils.h" // for gfxContentType
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for TextureImage::Release, etc
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "OGLShaderProgram.h" // for ShaderProgramType, etc
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Compositor;
+class CompositorOGL;
+class TextureImageTextureSourceOGL;
+class GLTextureSource;
+
+inline void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL,
+ gfx::SamplingFilter aSamplingFilter,
+ GLuint aTarget = LOCAL_GL_TEXTURE_2D)
+{
+ GLenum filter =
+ (aSamplingFilter == gfx::SamplingFilter::POINT ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR);
+
+ aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, filter);
+ aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, filter);
+}
+
+/*
+ * TextureHost implementations for the OpenGL backend.
+ *
+ * Note that it is important to be careful about the ownership model with
+ * the OpenGL backend, due to some widget limitation on Linux: before
+ * the nsBaseWidget associated with our OpenGL context has been completely
+ * deleted, every resource belonging to the OpenGL context MUST have been
+ * released. At the moment the teardown sequence happens in the middle of
+ * the nsBaseWidget's destructor, meaning that at a given moment we must be
+ * able to easily find and release all the GL resources.
+ * The point is: be careful about the ownership model and limit the number
+ * of objects sharing references to GL resources to make the tear down
+ * sequence as simple as possible.
+ */
+
+/**
+ * TextureSourceOGL provides the necessary API for CompositorOGL to composite
+ * a TextureSource.
+ */
+class TextureSourceOGL
+{
+public:
+ TextureSourceOGL()
+ : mHasCachedSamplingFilter(false)
+ {}
+
+ virtual bool IsValid() const = 0;
+
+ virtual void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) = 0;
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; }
+
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+
+ virtual GLenum GetWrapMode() const = 0;
+
+ virtual gfx::Matrix4x4 GetTextureTransform() { return gfx::Matrix4x4(); }
+
+ virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; }
+
+ virtual GLTextureSource* AsGLTextureSource() { return nullptr; }
+
+ void SetSamplingFilter(gl::GLContext* aGL, gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mHasCachedSamplingFilter &&
+ mCachedSamplingFilter == aSamplingFilter) {
+ return;
+ }
+ mHasCachedSamplingFilter = true;
+ mCachedSamplingFilter = aSamplingFilter;
+ ApplySamplingFilterToBoundTexture(aGL, aSamplingFilter, GetTextureTarget());
+ }
+
+ void ClearCachedFilter() { mHasCachedSamplingFilter = false; }
+
+private:
+ gfx::SamplingFilter mCachedSamplingFilter;
+ bool mHasCachedSamplingFilter;
+};
+
+/**
+ * A TextureSource backed by a TextureImage.
+ *
+ * Depending on the underlying TextureImage, may support texture tiling, so
+ * make sure to check AsBigImageIterator() and use the texture accordingly.
+ *
+ * This TextureSource can be used without a TextureHost and manage it's own
+ * GL texture(s).
+ */
+class TextureImageTextureSourceOGL final : public DataTextureSource
+ , public TextureSourceOGL
+ , public BigImageIterator
+{
+public:
+ explicit TextureImageTextureSourceOGL(CompositorOGL *aCompositor,
+ TextureFlags aFlags = TextureFlags::DEFAULT)
+ : mCompositor(aCompositor)
+ , mFlags(aFlags)
+ , mIterating(false)
+ {}
+
+ virtual const char* Name() const override { return "TextureImageTextureSourceOGL"; }
+ // DataTextureSource
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ void EnsureBuffer(const gfx::IntSize& aSize,
+ gfxContentType aContentType);
+
+ void CopyTo(const gfx::IntRect& aSourceRect,
+ DataTextureSource* aDest,
+ const gfx::IntRect& aDestRect);
+
+ virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() override { return this; }
+
+ // TextureSource
+
+ virtual void DeallocateDeviceData() override
+ {
+ mTexImage = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool IsValid() const override { return !!mTexImage; }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual GLenum GetWrapMode() const override
+ {
+ return mTexImage->GetWrapMode();
+ }
+
+ // BigImageIterator
+
+ virtual BigImageIterator* AsBigImageIterator() override { return this; }
+
+ virtual void BeginBigImageIteration() override
+ {
+ mTexImage->BeginBigImageIteration();
+ mIterating = true;
+ }
+
+ virtual void EndBigImageIteration() override
+ {
+ mIterating = false;
+ }
+
+ virtual gfx::IntRect GetTileRect() override;
+
+ virtual size_t GetTileCount() override
+ {
+ return mTexImage->GetTileCount();
+ }
+
+ virtual bool NextTile() override
+ {
+ return mTexImage->NextTile();
+ }
+
+protected:
+ RefPtr<gl::TextureImage> mTexImage;
+ RefPtr<CompositorOGL> mCompositor;
+ TextureFlags mFlags;
+ bool mIterating;
+};
+
+/**
+ * A texture source for GL textures.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ *
+ * The shared texture handle is owned by the TextureHost.
+ */
+class GLTextureSource : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ GLTextureSource(CompositorOGL* aCompositor,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ bool aExternallyOwned = false);
+
+ ~GLTextureSource();
+
+ virtual const char* Name() const override { return "GLTextureSource"; }
+
+ virtual GLTextureSource* AsGLTextureSource() override { return this; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ void SetSize(gfx::IntSize aSize) { mSize = aSize; }
+
+ void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; }
+
+ GLuint GetTextureHandle() const { return mTextureHandle; }
+
+ gl::GLContext* gl() const;
+
+protected:
+ void DeleteTextureHandle();
+
+ RefPtr<CompositorOGL> mCompositor;
+ GLuint mTextureHandle;
+ GLenum mTextureTarget;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ // If the texture is externally owned, the gl handle will not be deleted
+ // in the destructor.
+ bool mExternallyOwned;
+};
+
+class GLTextureHost : public TextureHost
+{
+public:
+ GLTextureHost(TextureFlags aFlags,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ GLsync aSync,
+ gfx::IntSize aSize,
+ bool aHasAlpha);
+
+ virtual ~GLTextureHost();
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override {}
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual const char* Name() override { return "GLTextureHost"; }
+
+protected:
+ const GLuint mTexture;
+ const GLenum mTarget;
+ GLsync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLTextureSource> mTextureSource;
+};
+
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+class EGLImageTextureSource : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ EGLImageTextureSource(CompositorOGL* aCompositor,
+ EGLImage aImage,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize);
+
+ virtual const char* Name() const override { return "EGLImageTextureSource"; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::Matrix4x4 GetTextureTransform() override;
+
+ virtual GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ virtual GLenum GetWrapMode() const override { return mWrapMode; }
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ gl::GLContext* gl() const;
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ const EGLImage mImage;
+ const gfx::SurfaceFormat mFormat;
+ const GLenum mTextureTarget;
+ const GLenum mWrapMode;
+ const gfx::IntSize mSize;
+};
+
+class EGLImageTextureHost : public TextureHost
+{
+public:
+ EGLImageTextureHost(TextureFlags aFlags,
+ EGLImage aImage,
+ EGLSync aSync,
+ gfx::IntSize aSize,
+ bool hasAlpha);
+
+ virtual ~EGLImageTextureHost();
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual const char* Name() override { return "EGLImageTextureHost"; }
+
+protected:
+ const EGLImage mImage;
+ const EGLSync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<EGLImageTextureSource> mTextureSource;
+};
+
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTUREOGL_H */
diff --git a/system/graphics/layers/opengl/TexturePoolOGL.cpp b/system/graphics/layers/opengl/TexturePoolOGL.cpp
new file mode 100644
index 000000000..8ee3b4cbb
--- /dev/null
+++ b/system/graphics/layers/opengl/TexturePoolOGL.cpp
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TexturePoolOGL.h"
+#include <stdlib.h> // for malloc
+#include "GLContext.h" // for GLContext
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_ASSERTION, NS_ERROR, etc
+#include "nsDeque.h" // for nsDeque
+
+#define TEXTURE_POOL_SIZE 10
+
+namespace mozilla {
+namespace gl {
+
+static GLContext* sActiveContext = nullptr;
+
+static Monitor* sMonitor = nullptr;
+static nsDeque* sTextures = nullptr;
+
+GLuint TexturePoolOGL::AcquireTexture()
+{
+ NS_ASSERTION(sMonitor, "not initialized");
+
+ MonitorAutoLock lock(*sMonitor);
+
+ if (!sActiveContext) {
+ // Wait for a context
+ sMonitor->Wait();
+
+ if (!sActiveContext)
+ return 0;
+ }
+
+ GLuint texture = 0;
+ if (sActiveContext->IsOwningThreadCurrent()) {
+ sActiveContext->MakeCurrent();
+
+ sActiveContext->fGenTextures(1, &texture);
+ } else {
+ while (sTextures->GetSize() == 0) {
+ NS_WARNING("Waiting for texture");
+ sMonitor->Wait();
+ }
+
+ GLuint* popped = (GLuint*) sTextures->Pop();
+ if (!popped) {
+ NS_ERROR("Failed to pop texture pool item");
+ return 0;
+ }
+
+ texture = *popped;
+ delete popped;
+
+ NS_ASSERTION(texture, "Failed to retrieve texture from pool");
+ }
+
+ return texture;
+}
+
+static void Clear()
+{
+ if (!sActiveContext)
+ return;
+
+ sActiveContext->MakeCurrent();
+
+ GLuint* item;
+ while (sTextures->GetSize()) {
+ item = (GLuint*)sTextures->Pop();
+ sActiveContext->fDeleteTextures(1, item);
+ delete item;
+ }
+}
+
+void TexturePoolOGL::Fill(GLContext* aContext)
+{
+ NS_ASSERTION(aContext, "NULL GLContext");
+ NS_ASSERTION(sMonitor, "not initialized");
+
+ MonitorAutoLock lock(*sMonitor);
+
+ if (sActiveContext != aContext) {
+ Clear();
+ sActiveContext = aContext;
+ }
+
+ if (sTextures->GetSize() == TEXTURE_POOL_SIZE)
+ return;
+
+ sActiveContext->MakeCurrent();
+
+ GLuint* texture = nullptr;
+ while (sTextures->GetSize() < TEXTURE_POOL_SIZE) {
+ texture = (GLuint*)malloc(sizeof(GLuint));
+ sActiveContext->fGenTextures(1, texture);
+ sTextures->Push((void*) texture);
+ }
+
+ sMonitor->NotifyAll();
+}
+
+GLContext* TexturePoolOGL::GetGLContext()
+{
+ return sActiveContext;
+}
+
+void TexturePoolOGL::Init()
+{
+ sMonitor = new Monitor("TexturePoolOGL.sMonitor");
+ sTextures = new nsDeque();
+}
+
+void TexturePoolOGL::Shutdown()
+{
+ delete sMonitor;
+ delete sTextures;
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/system/graphics/layers/opengl/TexturePoolOGL.h b/system/graphics/layers/opengl/TexturePoolOGL.h
new file mode 100644
index 000000000..136364e8c
--- /dev/null
+++ b/system/graphics/layers/opengl/TexturePoolOGL.h
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEXTUREPOOLOGL_H
+#define GFX_TEXTUREPOOLOGL_H
+
+#include "GLContextTypes.h" // for GLContext, GLuint
+
+namespace mozilla {
+namespace gl {
+
+// A texture pool for for the on-screen GLContext. The main purpose of this class
+// is to provide the ability to easily allocate an on-screen texture from the
+// content thread. The unfortunate nature of the SurfaceTexture API (see AndroidSurfaceTexture)
+// necessitates this.
+class TexturePoolOGL
+{
+public:
+ // Get a new texture from the pool. Will block
+ // and wait for one to be created if necessary
+ static GLuint AcquireTexture();
+
+ // Called by the active LayerManagerOGL to fill
+ // the pool
+ static void Fill(GLContext* aContext);
+
+ static GLContext* GetGLContext();
+
+ // Initializes the pool, but does not fill it. Called by gfxPlatform init.
+ static void Init();
+
+ // Clears all internal data structures in preparation for shutdown
+ static void Shutdown();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GFX_TEXTUREPOOLOGL_H
diff --git a/system/graphics/layers/opengl/X11TextureSourceOGL.cpp b/system/graphics/layers/opengl/X11TextureSourceOGL.cpp
new file mode 100644
index 000000000..dbed66b61
--- /dev/null
+++ b/system/graphics/layers/opengl/X11TextureSourceOGL.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef GL_PROVIDER_GLX
+
+#include "X11TextureSourceOGL.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceOGL::X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+ , mTexture(0)
+ , mUpdated(false)
+{
+}
+
+X11TextureSourceOGL::~X11TextureSourceOGL()
+{
+ DeallocateDeviceData();
+}
+
+void
+X11TextureSourceOGL::DeallocateDeviceData()
+{
+ if (mTexture) {
+ if (gl() && gl()->MakeCurrent()) {
+ gl::sGLXLibrary.ReleaseTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ gl()->fDeleteTextures(1, &mTexture);
+ mTexture = 0;
+ }
+ }
+}
+
+void
+X11TextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ gl()->fActiveTexture(aTextureUnit);
+
+ if (!mTexture) {
+ gl()->fGenTextures(1, &mTexture);
+
+ gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+
+ gl::sGLXLibrary.BindTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ } else {
+ gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+ if (mUpdated) {
+ gl::sGLXLibrary.UpdateTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ mUpdated = false;
+ }
+ }
+
+ ApplySamplingFilterToBoundTexture(gl(), aSamplingFilter, LOCAL_GL_TEXTURE_2D);
+}
+
+IntSize
+X11TextureSourceOGL::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+SurfaceFormat
+X11TextureSourceOGL::GetFormat() const {
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+}
+
+void
+X11TextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (mCompositor == glCompositor) {
+ return;
+ }
+ DeallocateDeviceData();
+ if (glCompositor) {
+ mCompositor = glCompositor;
+ }
+}
+
+gl::GLContext*
+X11TextureSourceOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+SurfaceFormat
+X11TextureSourceOGL::ContentTypeToSurfaceFormat(gfxContentType aType)
+{
+ // X11 uses a switched format and the OGL compositor
+ // doesn't support ALPHA / A8.
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::R8G8B8X8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::R8G8B8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+}
+}
+
+#endif
diff --git a/system/graphics/layers/opengl/X11TextureSourceOGL.h b/system/graphics/layers/opengl/X11TextureSourceOGL.h
new file mode 100644
index 000000000..505847403
--- /dev/null
+++ b/system/graphics/layers/opengl/X11TextureSourceOGL.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEOGL__H
+#define MOZILLA_GFX_X11TEXTURESOURCEOGL__H
+
+#ifdef GL_PROVIDER_GLX
+
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceOGL
+ : public TextureSourceOGL
+ , public X11TextureSource
+{
+public:
+ X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface);
+ ~X11TextureSourceOGL();
+
+ virtual X11TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual bool IsValid() const override { return !!gl(); } ;
+
+ virtual void BindTexture(GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_2D; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void Updated() override { mUpdated = true; }
+
+ gl::GLContext* gl() const;
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+ GLuint mTexture;
+ bool mUpdated;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEOGL__H
diff --git a/system/graphics/layers/protobuf/LayerScopePacket.pb.cc b/system/graphics/layers/protobuf/LayerScopePacket.pb.cc
new file mode 100644
index 000000000..c9a1bd94e
--- /dev/null
+++ b/system/graphics/layers/protobuf/LayerScopePacket.pb.cc
@@ -0,0 +1,6802 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: LayerScopePacket.proto
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "LayerScopePacket.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+
+namespace mozilla {
+namespace layers {
+namespace layerscope {
+
+void protobuf_ShutdownFile_LayerScopePacket_2eproto() {
+ delete FramePacket::default_instance_;
+ delete ColorPacket::default_instance_;
+ delete TexturePacket::default_instance_;
+ delete TexturePacket_Rect::default_instance_;
+ delete TexturePacket_Size::default_instance_;
+ delete TexturePacket_Matrix::default_instance_;
+ delete TexturePacket_EffectMask::default_instance_;
+ delete LayersPacket::default_instance_;
+ delete LayersPacket_Layer::default_instance_;
+ delete LayersPacket_Layer_Size::default_instance_;
+ delete LayersPacket_Layer_Rect::default_instance_;
+ delete LayersPacket_Layer_Region::default_instance_;
+ delete LayersPacket_Layer_Matrix::default_instance_;
+ delete LayersPacket_Layer_Shadow::default_instance_;
+ delete MetaPacket::default_instance_;
+ delete DrawPacket::default_instance_;
+ delete DrawPacket_Rect::default_instance_;
+ delete Packet::default_instance_;
+ delete CommandPacket::default_instance_;
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+void protobuf_AddDesc_LayerScopePacket_2eproto_impl() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#else
+void protobuf_AddDesc_LayerScopePacket_2eproto() {
+ static bool already_here = false;
+ if (already_here) return;
+ already_here = true;
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#endif
+ FramePacket::default_instance_ = new FramePacket();
+ ColorPacket::default_instance_ = new ColorPacket();
+ TexturePacket::default_instance_ = new TexturePacket();
+ TexturePacket_Rect::default_instance_ = new TexturePacket_Rect();
+ TexturePacket_Size::default_instance_ = new TexturePacket_Size();
+ TexturePacket_Matrix::default_instance_ = new TexturePacket_Matrix();
+ TexturePacket_EffectMask::default_instance_ = new TexturePacket_EffectMask();
+ LayersPacket::default_instance_ = new LayersPacket();
+ LayersPacket_Layer::default_instance_ = new LayersPacket_Layer();
+ LayersPacket_Layer_Size::default_instance_ = new LayersPacket_Layer_Size();
+ LayersPacket_Layer_Rect::default_instance_ = new LayersPacket_Layer_Rect();
+ LayersPacket_Layer_Region::default_instance_ = new LayersPacket_Layer_Region();
+ LayersPacket_Layer_Matrix::default_instance_ = new LayersPacket_Layer_Matrix();
+ LayersPacket_Layer_Shadow::default_instance_ = new LayersPacket_Layer_Shadow();
+ MetaPacket::default_instance_ = new MetaPacket();
+ DrawPacket::default_instance_ = new DrawPacket();
+ DrawPacket_Rect::default_instance_ = new DrawPacket_Rect();
+ Packet::default_instance_ = new Packet();
+ CommandPacket::default_instance_ = new CommandPacket();
+ FramePacket::default_instance_->InitAsDefaultInstance();
+ ColorPacket::default_instance_->InitAsDefaultInstance();
+ TexturePacket::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Rect::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Size::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Matrix::default_instance_->InitAsDefaultInstance();
+ TexturePacket_EffectMask::default_instance_->InitAsDefaultInstance();
+ LayersPacket::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Size::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Rect::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Region::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Matrix::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Shadow::default_instance_->InitAsDefaultInstance();
+ MetaPacket::default_instance_->InitAsDefaultInstance();
+ DrawPacket::default_instance_->InitAsDefaultInstance();
+ DrawPacket_Rect::default_instance_->InitAsDefaultInstance();
+ Packet::default_instance_->InitAsDefaultInstance();
+ CommandPacket::default_instance_->InitAsDefaultInstance();
+ ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_LayerScopePacket_2eproto);
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_LayerScopePacket_2eproto_once_);
+void protobuf_AddDesc_LayerScopePacket_2eproto() {
+ ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_LayerScopePacket_2eproto_once_,
+ &protobuf_AddDesc_LayerScopePacket_2eproto_impl);
+}
+#else
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_LayerScopePacket_2eproto {
+ StaticDescriptorInitializer_LayerScopePacket_2eproto() {
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+ }
+} static_descriptor_initializer_LayerScopePacket_2eproto_;
+#endif
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int FramePacket::kValueFieldNumber;
+const int FramePacket::kScaleFieldNumber;
+#endif // !_MSC_VER
+
+FramePacket::FramePacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.FramePacket)
+}
+
+void FramePacket::InitAsDefaultInstance() {
+}
+
+FramePacket::FramePacket(const FramePacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.FramePacket)
+}
+
+void FramePacket::SharedCtor() {
+ _cached_size_ = 0;
+ value_ = GOOGLE_ULONGLONG(0);
+ scale_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+FramePacket::~FramePacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.FramePacket)
+ SharedDtor();
+}
+
+void FramePacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void FramePacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const FramePacket& FramePacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+FramePacket* FramePacket::default_instance_ = NULL;
+
+FramePacket* FramePacket::New() const {
+ return new FramePacket;
+}
+
+void FramePacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<FramePacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(value_, scale_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool FramePacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.FramePacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional uint64 value = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &value_)));
+ set_has_value();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_scale;
+ break;
+ }
+
+ // optional float scale = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_scale:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &scale_)));
+ set_has_scale();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.FramePacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.FramePacket)
+ return false;
+#undef DO_
+}
+
+void FramePacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.FramePacket)
+ // optional uint64 value = 1;
+ if (has_value()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->value(), output);
+ }
+
+ // optional float scale = 2;
+ if (has_scale()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->scale(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.FramePacket)
+}
+
+int FramePacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional uint64 value = 1;
+ if (has_value()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->value());
+ }
+
+ // optional float scale = 2;
+ if (has_scale()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void FramePacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const FramePacket*>(&from));
+}
+
+void FramePacket::MergeFrom(const FramePacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_value()) {
+ set_value(from.value());
+ }
+ if (from.has_scale()) {
+ set_scale(from.scale());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void FramePacket::CopyFrom(const FramePacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FramePacket::IsInitialized() const {
+
+ return true;
+}
+
+void FramePacket::Swap(FramePacket* other) {
+ if (other != this) {
+ std::swap(value_, other->value_);
+ std::swap(scale_, other->scale_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string FramePacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.FramePacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int ColorPacket::kLayerrefFieldNumber;
+const int ColorPacket::kWidthFieldNumber;
+const int ColorPacket::kHeightFieldNumber;
+const int ColorPacket::kColorFieldNumber;
+#endif // !_MSC_VER
+
+ColorPacket::ColorPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.ColorPacket)
+}
+
+void ColorPacket::InitAsDefaultInstance() {
+}
+
+ColorPacket::ColorPacket(const ColorPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.ColorPacket)
+}
+
+void ColorPacket::SharedCtor() {
+ _cached_size_ = 0;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ width_ = 0u;
+ height_ = 0u;
+ color_ = 0u;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+ColorPacket::~ColorPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.ColorPacket)
+ SharedDtor();
+}
+
+void ColorPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void ColorPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ColorPacket& ColorPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+ColorPacket* ColorPacket::default_instance_ = NULL;
+
+ColorPacket* ColorPacket::New() const {
+ return new ColorPacket;
+}
+
+void ColorPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<ColorPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(layerref_, color_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool ColorPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.ColorPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required uint64 layerref = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_width;
+ break;
+ }
+
+ // optional uint32 width = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_width:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &width_)));
+ set_has_width();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_height;
+ break;
+ }
+
+ // optional uint32 height = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_height:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &height_)));
+ set_has_height();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_color;
+ break;
+ }
+
+ // optional uint32 color = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_color:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &color_)));
+ set_has_color();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.ColorPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.ColorPacket)
+ return false;
+#undef DO_
+}
+
+void ColorPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.ColorPacket)
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output);
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output);
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output);
+ }
+
+ // optional uint32 color = 4;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->color(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.ColorPacket)
+}
+
+int ColorPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->width());
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->height());
+ }
+
+ // optional uint32 color = 4;
+ if (has_color()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->color());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void ColorPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const ColorPacket*>(&from));
+}
+
+void ColorPacket::MergeFrom(const ColorPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ if (from.has_width()) {
+ set_width(from.width());
+ }
+ if (from.has_height()) {
+ set_height(from.height());
+ }
+ if (from.has_color()) {
+ set_color(from.color());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void ColorPacket::CopyFrom(const ColorPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ColorPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void ColorPacket::Swap(ColorPacket* other) {
+ if (other != this) {
+ std::swap(layerref_, other->layerref_);
+ std::swap(width_, other->width_);
+ std::swap(height_, other->height_);
+ std::swap(color_, other->color_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string ColorPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.ColorPacket";
+}
+
+
+// ===================================================================
+
+bool TexturePacket_Filter_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const TexturePacket_Filter TexturePacket::GOOD;
+const TexturePacket_Filter TexturePacket::LINEAR;
+const TexturePacket_Filter TexturePacket::POINT;
+const TexturePacket_Filter TexturePacket::Filter_MIN;
+const TexturePacket_Filter TexturePacket::Filter_MAX;
+const int TexturePacket::Filter_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int TexturePacket_Rect::kXFieldNumber;
+const int TexturePacket_Rect::kYFieldNumber;
+const int TexturePacket_Rect::kWFieldNumber;
+const int TexturePacket_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Rect::TexturePacket_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+void TexturePacket_Rect::InitAsDefaultInstance() {
+}
+
+TexturePacket_Rect::TexturePacket_Rect(const TexturePacket_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+void TexturePacket_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Rect::~TexturePacket_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Rect)
+ SharedDtor();
+}
+
+void TexturePacket_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Rect& TexturePacket_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Rect* TexturePacket_Rect::default_instance_ = NULL;
+
+TexturePacket_Rect* TexturePacket_Rect::New() const {
+ return new TexturePacket_Rect;
+}
+
+void TexturePacket_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional float x = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_y;
+ break;
+ }
+
+ // optional float y = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_w;
+ break;
+ }
+
+ // optional float w = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(37)) goto parse_h;
+ break;
+ }
+
+ // optional float h = 4;
+ case 4: {
+ if (tag == 37) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Rect)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Rect)
+ // optional float x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->x(), output);
+ }
+
+ // optional float y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->y(), output);
+ }
+
+ // optional float w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->w(), output);
+ }
+
+ // optional float h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+int TexturePacket_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional float x = 1;
+ if (has_x()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float y = 2;
+ if (has_y()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float w = 3;
+ if (has_w()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float h = 4;
+ if (has_h()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Rect*>(&from));
+}
+
+void TexturePacket_Rect::MergeFrom(const TexturePacket_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Rect::CopyFrom(const TexturePacket_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Rect::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Rect::Swap(TexturePacket_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_Size::kWFieldNumber;
+const int TexturePacket_Size::kHFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Size::TexturePacket_Size()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+void TexturePacket_Size::InitAsDefaultInstance() {
+}
+
+TexturePacket_Size::TexturePacket_Size(const TexturePacket_Size& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+void TexturePacket_Size::SharedCtor() {
+ _cached_size_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Size::~TexturePacket_Size() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Size)
+ SharedDtor();
+}
+
+void TexturePacket_Size::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Size::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Size& TexturePacket_Size::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Size* TexturePacket_Size::default_instance_ = NULL;
+
+TexturePacket_Size* TexturePacket_Size::New() const {
+ return new TexturePacket_Size;
+}
+
+void TexturePacket_Size::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Size*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(w_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Size::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Size)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 w = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Size)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Size)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Size::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Size)
+ // optional int32 w = 1;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->w(), output);
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+int TexturePacket_Size::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 w = 1;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Size::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Size*>(&from));
+}
+
+void TexturePacket_Size::MergeFrom(const TexturePacket_Size& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Size::CopyFrom(const TexturePacket_Size& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Size::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Size::Swap(TexturePacket_Size* other) {
+ if (other != this) {
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Size::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Size";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_Matrix::kIs2DFieldNumber;
+const int TexturePacket_Matrix::kIsIdFieldNumber;
+const int TexturePacket_Matrix::kMFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Matrix::TexturePacket_Matrix()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+void TexturePacket_Matrix::InitAsDefaultInstance() {
+}
+
+TexturePacket_Matrix::TexturePacket_Matrix(const TexturePacket_Matrix& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+void TexturePacket_Matrix::SharedCtor() {
+ _cached_size_ = 0;
+ is2d_ = false;
+ isid_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Matrix::~TexturePacket_Matrix() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+ SharedDtor();
+}
+
+void TexturePacket_Matrix::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Matrix::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Matrix& TexturePacket_Matrix::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Matrix* TexturePacket_Matrix::default_instance_ = NULL;
+
+TexturePacket_Matrix* TexturePacket_Matrix::New() const {
+ return new TexturePacket_Matrix;
+}
+
+void TexturePacket_Matrix::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Matrix*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(is2d_, isid_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ m_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Matrix::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Matrix)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool is2D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &is2d_)));
+ set_has_is2d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_isId;
+ break;
+ }
+
+ // optional bool isId = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_isId:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &isid_)));
+ set_has_isid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ break;
+ }
+
+ // repeated float m = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_m:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_m())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_m())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Matrix)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Matrix)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Matrix::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Matrix)
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->is2d(), output);
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->isid(), output);
+ }
+
+ // repeated float m = 3;
+ for (int i = 0; i < this->m_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->m(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+int TexturePacket_Matrix::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ // repeated float m = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->m_size();
+ total_size += 1 * this->m_size() + data_size;
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Matrix::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Matrix*>(&from));
+}
+
+void TexturePacket_Matrix::MergeFrom(const TexturePacket_Matrix& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ m_.MergeFrom(from.m_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_is2d()) {
+ set_is2d(from.is2d());
+ }
+ if (from.has_isid()) {
+ set_isid(from.isid());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Matrix::CopyFrom(const TexturePacket_Matrix& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Matrix::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Matrix::Swap(TexturePacket_Matrix* other) {
+ if (other != this) {
+ std::swap(is2d_, other->is2d_);
+ std::swap(isid_, other->isid_);
+ m_.Swap(&other->m_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Matrix::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Matrix";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_EffectMask::kMIs3DFieldNumber;
+const int TexturePacket_EffectMask::kMSizeFieldNumber;
+const int TexturePacket_EffectMask::kMMaskTransformFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_EffectMask::TexturePacket_EffectMask()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+void TexturePacket_EffectMask::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ msize_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Size*>(
+ ::mozilla::layers::layerscope::TexturePacket_Size::internal_default_instance());
+#else
+ msize_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Size*>(&::mozilla::layers::layerscope::TexturePacket_Size::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mmasktransform_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Matrix*>(
+ ::mozilla::layers::layerscope::TexturePacket_Matrix::internal_default_instance());
+#else
+ mmasktransform_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Matrix*>(&::mozilla::layers::layerscope::TexturePacket_Matrix::default_instance());
+#endif
+}
+
+TexturePacket_EffectMask::TexturePacket_EffectMask(const TexturePacket_EffectMask& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+void TexturePacket_EffectMask::SharedCtor() {
+ _cached_size_ = 0;
+ mis3d_ = false;
+ msize_ = NULL;
+ mmasktransform_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_EffectMask::~TexturePacket_EffectMask() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ SharedDtor();
+}
+
+void TexturePacket_EffectMask::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete msize_;
+ delete mmasktransform_;
+ }
+}
+
+void TexturePacket_EffectMask::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_EffectMask& TexturePacket_EffectMask::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_EffectMask* TexturePacket_EffectMask::default_instance_ = NULL;
+
+TexturePacket_EffectMask* TexturePacket_EffectMask::New() const {
+ return new TexturePacket_EffectMask;
+}
+
+void TexturePacket_EffectMask::Clear() {
+ if (_has_bits_[0 / 32] & 7) {
+ mis3d_ = false;
+ if (has_msize()) {
+ if (msize_ != NULL) msize_->::mozilla::layers::layerscope::TexturePacket_Size::Clear();
+ }
+ if (has_mmasktransform()) {
+ if (mmasktransform_ != NULL) mmasktransform_->::mozilla::layers::layerscope::TexturePacket_Matrix::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_EffectMask::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool mIs3D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &mis3d_)));
+ set_has_mis3d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_mSize;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_mSize:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_msize()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_mMaskTransform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_mMaskTransform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mmasktransform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_EffectMask::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ // optional bool mIs3D = 1;
+ if (has_mis3d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->mis3d(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ if (has_msize()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->msize(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ if (has_mmasktransform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->mmasktransform(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+int TexturePacket_EffectMask::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool mIs3D = 1;
+ if (has_mis3d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ if (has_msize()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->msize());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ if (has_mmasktransform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mmasktransform());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_EffectMask::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_EffectMask*>(&from));
+}
+
+void TexturePacket_EffectMask::MergeFrom(const TexturePacket_EffectMask& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_mis3d()) {
+ set_mis3d(from.mis3d());
+ }
+ if (from.has_msize()) {
+ mutable_msize()->::mozilla::layers::layerscope::TexturePacket_Size::MergeFrom(from.msize());
+ }
+ if (from.has_mmasktransform()) {
+ mutable_mmasktransform()->::mozilla::layers::layerscope::TexturePacket_Matrix::MergeFrom(from.mmasktransform());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_EffectMask::CopyFrom(const TexturePacket_EffectMask& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_EffectMask::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_EffectMask::Swap(TexturePacket_EffectMask* other) {
+ if (other != this) {
+ std::swap(mis3d_, other->mis3d_);
+ std::swap(msize_, other->msize_);
+ std::swap(mmasktransform_, other->mmasktransform_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_EffectMask::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.EffectMask";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket::kLayerrefFieldNumber;
+const int TexturePacket::kWidthFieldNumber;
+const int TexturePacket::kHeightFieldNumber;
+const int TexturePacket::kStrideFieldNumber;
+const int TexturePacket::kNameFieldNumber;
+const int TexturePacket::kTargetFieldNumber;
+const int TexturePacket::kDataformatFieldNumber;
+const int TexturePacket::kGlcontextFieldNumber;
+const int TexturePacket::kDataFieldNumber;
+const int TexturePacket::kMTextureCoordsFieldNumber;
+const int TexturePacket::kMPremultipliedFieldNumber;
+const int TexturePacket::kMFilterFieldNumber;
+const int TexturePacket::kIsMaskFieldNumber;
+const int TexturePacket::kMaskFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket::TexturePacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket)
+}
+
+void TexturePacket::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mtexturecoords_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Rect*>(
+ ::mozilla::layers::layerscope::TexturePacket_Rect::internal_default_instance());
+#else
+ mtexturecoords_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Rect*>(&::mozilla::layers::layerscope::TexturePacket_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mask_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_EffectMask*>(
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask::internal_default_instance());
+#else
+ mask_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_EffectMask*>(&::mozilla::layers::layerscope::TexturePacket_EffectMask::default_instance());
+#endif
+}
+
+TexturePacket::TexturePacket(const TexturePacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket)
+}
+
+void TexturePacket::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ width_ = 0u;
+ height_ = 0u;
+ stride_ = 0u;
+ name_ = 0u;
+ target_ = 0u;
+ dataformat_ = 0u;
+ glcontext_ = GOOGLE_ULONGLONG(0);
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ mtexturecoords_ = NULL;
+ mpremultiplied_ = false;
+ mfilter_ = 0;
+ ismask_ = false;
+ mask_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket::~TexturePacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket)
+ SharedDtor();
+}
+
+void TexturePacket::SharedDtor() {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete data_;
+ }
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete mtexturecoords_;
+ delete mask_;
+ }
+}
+
+void TexturePacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket& TexturePacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket* TexturePacket::default_instance_ = NULL;
+
+TexturePacket* TexturePacket::New() const {
+ return new TexturePacket;
+}
+
+void TexturePacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 255) {
+ ZR_(layerref_, glcontext_);
+ }
+ if (_has_bits_[8 / 32] & 16128) {
+ ZR_(mfilter_, ismask_);
+ if (has_data()) {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_->clear();
+ }
+ }
+ if (has_mtexturecoords()) {
+ if (mtexturecoords_ != NULL) mtexturecoords_->::mozilla::layers::layerscope::TexturePacket_Rect::Clear();
+ }
+ if (has_mask()) {
+ if (mask_ != NULL) mask_->::mozilla::layers::layerscope::TexturePacket_EffectMask::Clear();
+ }
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(16383);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required uint64 layerref = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_width;
+ break;
+ }
+
+ // optional uint32 width = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_width:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &width_)));
+ set_has_width();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_height;
+ break;
+ }
+
+ // optional uint32 height = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_height:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &height_)));
+ set_has_height();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_stride;
+ break;
+ }
+
+ // optional uint32 stride = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_stride:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &stride_)));
+ set_has_stride();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(40)) goto parse_name;
+ break;
+ }
+
+ // optional uint32 name = 5;
+ case 5: {
+ if (tag == 40) {
+ parse_name:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &name_)));
+ set_has_name();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(48)) goto parse_target;
+ break;
+ }
+
+ // optional uint32 target = 6;
+ case 6: {
+ if (tag == 48) {
+ parse_target:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &target_)));
+ set_has_target();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_dataformat;
+ break;
+ }
+
+ // optional uint32 dataformat = 7;
+ case 7: {
+ if (tag == 56) {
+ parse_dataformat:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &dataformat_)));
+ set_has_dataformat();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(64)) goto parse_glcontext;
+ break;
+ }
+
+ // optional uint64 glcontext = 8;
+ case 8: {
+ if (tag == 64) {
+ parse_glcontext:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &glcontext_)));
+ set_has_glcontext();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(74)) goto parse_data;
+ break;
+ }
+
+ // optional bytes data = 9;
+ case 9: {
+ if (tag == 74) {
+ parse_data:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+ input, this->mutable_data()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(82)) goto parse_mTextureCoords;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ case 10: {
+ if (tag == 82) {
+ parse_mTextureCoords:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mtexturecoords()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(88)) goto parse_mPremultiplied;
+ break;
+ }
+
+ // optional bool mPremultiplied = 11;
+ case 11: {
+ if (tag == 88) {
+ parse_mPremultiplied:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &mpremultiplied_)));
+ set_has_mpremultiplied();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(96)) goto parse_mFilter;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ case 12: {
+ if (tag == 96) {
+ parse_mFilter:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::TexturePacket_Filter_IsValid(value)) {
+ set_mfilter(static_cast< ::mozilla::layers::layerscope::TexturePacket_Filter >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(160)) goto parse_isMask;
+ break;
+ }
+
+ // optional bool isMask = 20;
+ case 20: {
+ if (tag == 160) {
+ parse_isMask:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &ismask_)));
+ set_has_ismask();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(170)) goto parse_mask;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ case 21: {
+ if (tag == 170) {
+ parse_mask:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mask()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket)
+ return false;
+#undef DO_
+}
+
+void TexturePacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket)
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output);
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output);
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output);
+ }
+
+ // optional uint32 stride = 4;
+ if (has_stride()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->stride(), output);
+ }
+
+ // optional uint32 name = 5;
+ if (has_name()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(5, this->name(), output);
+ }
+
+ // optional uint32 target = 6;
+ if (has_target()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(6, this->target(), output);
+ }
+
+ // optional uint32 dataformat = 7;
+ if (has_dataformat()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(7, this->dataformat(), output);
+ }
+
+ // optional uint64 glcontext = 8;
+ if (has_glcontext()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(8, this->glcontext(), output);
+ }
+
+ // optional bytes data = 9;
+ if (has_data()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
+ 9, this->data(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ if (has_mtexturecoords()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 10, this->mtexturecoords(), output);
+ }
+
+ // optional bool mPremultiplied = 11;
+ if (has_mpremultiplied()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(11, this->mpremultiplied(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ if (has_mfilter()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 12, this->mfilter(), output);
+ }
+
+ // optional bool isMask = 20;
+ if (has_ismask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(20, this->ismask(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ if (has_mask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 21, this->mask(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket)
+}
+
+int TexturePacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->width());
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->height());
+ }
+
+ // optional uint32 stride = 4;
+ if (has_stride()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->stride());
+ }
+
+ // optional uint32 name = 5;
+ if (has_name()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->name());
+ }
+
+ // optional uint32 target = 6;
+ if (has_target()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->target());
+ }
+
+ // optional uint32 dataformat = 7;
+ if (has_dataformat()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->dataformat());
+ }
+
+ // optional uint64 glcontext = 8;
+ if (has_glcontext()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->glcontext());
+ }
+
+ }
+ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ // optional bytes data = 9;
+ if (has_data()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::BytesSize(
+ this->data());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ if (has_mtexturecoords()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mtexturecoords());
+ }
+
+ // optional bool mPremultiplied = 11;
+ if (has_mpremultiplied()) {
+ total_size += 1 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ if (has_mfilter()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->mfilter());
+ }
+
+ // optional bool isMask = 20;
+ if (has_ismask()) {
+ total_size += 2 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ if (has_mask()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mask());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket*>(&from));
+}
+
+void TexturePacket::MergeFrom(const TexturePacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ if (from.has_width()) {
+ set_width(from.width());
+ }
+ if (from.has_height()) {
+ set_height(from.height());
+ }
+ if (from.has_stride()) {
+ set_stride(from.stride());
+ }
+ if (from.has_name()) {
+ set_name(from.name());
+ }
+ if (from.has_target()) {
+ set_target(from.target());
+ }
+ if (from.has_dataformat()) {
+ set_dataformat(from.dataformat());
+ }
+ if (from.has_glcontext()) {
+ set_glcontext(from.glcontext());
+ }
+ }
+ if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ if (from.has_data()) {
+ set_data(from.data());
+ }
+ if (from.has_mtexturecoords()) {
+ mutable_mtexturecoords()->::mozilla::layers::layerscope::TexturePacket_Rect::MergeFrom(from.mtexturecoords());
+ }
+ if (from.has_mpremultiplied()) {
+ set_mpremultiplied(from.mpremultiplied());
+ }
+ if (from.has_mfilter()) {
+ set_mfilter(from.mfilter());
+ }
+ if (from.has_ismask()) {
+ set_ismask(from.ismask());
+ }
+ if (from.has_mask()) {
+ mutable_mask()->::mozilla::layers::layerscope::TexturePacket_EffectMask::MergeFrom(from.mask());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket::CopyFrom(const TexturePacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void TexturePacket::Swap(TexturePacket* other) {
+ if (other != this) {
+ std::swap(layerref_, other->layerref_);
+ std::swap(width_, other->width_);
+ std::swap(height_, other->height_);
+ std::swap(stride_, other->stride_);
+ std::swap(name_, other->name_);
+ std::swap(target_, other->target_);
+ std::swap(dataformat_, other->dataformat_);
+ std::swap(glcontext_, other->glcontext_);
+ std::swap(data_, other->data_);
+ std::swap(mtexturecoords_, other->mtexturecoords_);
+ std::swap(mpremultiplied_, other->mpremultiplied_);
+ std::swap(mfilter_, other->mfilter_);
+ std::swap(ismask_, other->ismask_);
+ std::swap(mask_, other->mask_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket";
+}
+
+
+// ===================================================================
+
+bool LayersPacket_Layer_LayerType_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_LayerType LayersPacket_Layer::UnknownLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerManager;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ContainerLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::PaintedLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::CanvasLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ImageLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ColorLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::RefLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ReadbackLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerType_MIN;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerType_MAX;
+const int LayersPacket_Layer::LayerType_ARRAYSIZE;
+#endif // _MSC_VER
+bool LayersPacket_Layer_ScrollingDirect_IsValid(int value) {
+ switch(value) {
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::VERTICAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::HORIZONTAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::ScrollingDirect_MIN;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::ScrollingDirect_MAX;
+const int LayersPacket_Layer::ScrollingDirect_ARRAYSIZE;
+#endif // _MSC_VER
+bool LayersPacket_Layer_Filter_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_FAST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GOOD;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BEST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_NEAREST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BILINEAR;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GAUSSIAN;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_SENTINEL;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_LINEAR;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_POINT;
+const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MIN;
+const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MAX;
+const int LayersPacket_Layer::Filter_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Size::kWFieldNumber;
+const int LayersPacket_Layer_Size::kHFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Size::LayersPacket_Layer_Size()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+void LayersPacket_Layer_Size::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Size::LayersPacket_Layer_Size(const LayersPacket_Layer_Size& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+void LayersPacket_Layer_Size::SharedCtor() {
+ _cached_size_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Size::~LayersPacket_Layer_Size() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Size::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Size::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Size& LayersPacket_Layer_Size::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Size* LayersPacket_Layer_Size::default_instance_ = NULL;
+
+LayersPacket_Layer_Size* LayersPacket_Layer_Size::New() const {
+ return new LayersPacket_Layer_Size;
+}
+
+void LayersPacket_Layer_Size::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Size*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(w_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Size::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 w = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Size::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ // optional int32 w = 1;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->w(), output);
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+int LayersPacket_Layer_Size::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 w = 1;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Size::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Size*>(&from));
+}
+
+void LayersPacket_Layer_Size::MergeFrom(const LayersPacket_Layer_Size& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Size::CopyFrom(const LayersPacket_Layer_Size& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Size::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Size::Swap(LayersPacket_Layer_Size* other) {
+ if (other != this) {
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Size::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Size";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Rect::kXFieldNumber;
+const int LayersPacket_Layer_Rect::kYFieldNumber;
+const int LayersPacket_Layer_Rect::kWFieldNumber;
+const int LayersPacket_Layer_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Rect::LayersPacket_Layer_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+void LayersPacket_Layer_Rect::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Rect::LayersPacket_Layer_Rect(const LayersPacket_Layer_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+void LayersPacket_Layer_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Rect::~LayersPacket_Layer_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Rect& LayersPacket_Layer_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Rect* LayersPacket_Layer_Rect::default_instance_ = NULL;
+
+LayersPacket_Layer_Rect* LayersPacket_Layer_Rect::New() const {
+ return new LayersPacket_Layer_Rect;
+}
+
+void LayersPacket_Layer_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 x = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_y;
+ break;
+ }
+
+ // optional int32 y = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_w;
+ break;
+ }
+
+ // optional int32 w = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ // optional int32 x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->x(), output);
+ }
+
+ // optional int32 y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->y(), output);
+ }
+
+ // optional int32 w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->w(), output);
+ }
+
+ // optional int32 h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+int LayersPacket_Layer_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 x = 1;
+ if (has_x()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->x());
+ }
+
+ // optional int32 y = 2;
+ if (has_y()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->y());
+ }
+
+ // optional int32 w = 3;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 4;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Rect*>(&from));
+}
+
+void LayersPacket_Layer_Rect::MergeFrom(const LayersPacket_Layer_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Rect::CopyFrom(const LayersPacket_Layer_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Rect::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Rect::Swap(LayersPacket_Layer_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Region::kRFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Region::LayersPacket_Layer_Region()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+void LayersPacket_Layer_Region::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Region::LayersPacket_Layer_Region(const LayersPacket_Layer_Region& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+void LayersPacket_Layer_Region::SharedCtor() {
+ _cached_size_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Region::~LayersPacket_Layer_Region() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Region::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Region::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Region& LayersPacket_Layer_Region::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Region* LayersPacket_Layer_Region::default_instance_ = NULL;
+
+LayersPacket_Layer_Region* LayersPacket_Layer_Region::New() const {
+ return new LayersPacket_Layer_Region;
+}
+
+void LayersPacket_Layer_Region::Clear() {
+ r_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Region::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ case 1: {
+ if (tag == 10) {
+ parse_r:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_r()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_r;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Region::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ for (int i = 0; i < this->r_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->r(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+int LayersPacket_Layer_Region::ByteSize() const {
+ int total_size = 0;
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ total_size += 1 * this->r_size();
+ for (int i = 0; i < this->r_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->r(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Region::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Region*>(&from));
+}
+
+void LayersPacket_Layer_Region::MergeFrom(const LayersPacket_Layer_Region& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ r_.MergeFrom(from.r_);
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Region::CopyFrom(const LayersPacket_Layer_Region& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Region::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Region::Swap(LayersPacket_Layer_Region* other) {
+ if (other != this) {
+ r_.Swap(&other->r_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Region::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Region";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Matrix::kIs2DFieldNumber;
+const int LayersPacket_Layer_Matrix::kIsIdFieldNumber;
+const int LayersPacket_Layer_Matrix::kMFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Matrix::LayersPacket_Layer_Matrix()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+void LayersPacket_Layer_Matrix::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Matrix::LayersPacket_Layer_Matrix(const LayersPacket_Layer_Matrix& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+void LayersPacket_Layer_Matrix::SharedCtor() {
+ _cached_size_ = 0;
+ is2d_ = false;
+ isid_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Matrix::~LayersPacket_Layer_Matrix() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Matrix::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Matrix::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Matrix& LayersPacket_Layer_Matrix::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Matrix* LayersPacket_Layer_Matrix::default_instance_ = NULL;
+
+LayersPacket_Layer_Matrix* LayersPacket_Layer_Matrix::New() const {
+ return new LayersPacket_Layer_Matrix;
+}
+
+void LayersPacket_Layer_Matrix::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Matrix*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(is2d_, isid_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ m_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Matrix::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool is2D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &is2d_)));
+ set_has_is2d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_isId;
+ break;
+ }
+
+ // optional bool isId = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_isId:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &isid_)));
+ set_has_isid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ break;
+ }
+
+ // repeated float m = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_m:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_m())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_m())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Matrix::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->is2d(), output);
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->isid(), output);
+ }
+
+ // repeated float m = 3;
+ for (int i = 0; i < this->m_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->m(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+int LayersPacket_Layer_Matrix::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ // repeated float m = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->m_size();
+ total_size += 1 * this->m_size() + data_size;
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Matrix::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Matrix*>(&from));
+}
+
+void LayersPacket_Layer_Matrix::MergeFrom(const LayersPacket_Layer_Matrix& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ m_.MergeFrom(from.m_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_is2d()) {
+ set_is2d(from.is2d());
+ }
+ if (from.has_isid()) {
+ set_isid(from.isid());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Matrix::CopyFrom(const LayersPacket_Layer_Matrix& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Matrix::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Matrix::Swap(LayersPacket_Layer_Matrix* other) {
+ if (other != this) {
+ std::swap(is2d_, other->is2d_);
+ std::swap(isid_, other->isid_);
+ m_.Swap(&other->m_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Matrix::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Matrix";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Shadow::kClipFieldNumber;
+const int LayersPacket_Layer_Shadow::kTransformFieldNumber;
+const int LayersPacket_Layer_Shadow::kVRegionFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Shadow::LayersPacket_Layer_Shadow()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+void LayersPacket_Layer_Shadow::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect::internal_default_instance());
+#else
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::internal_default_instance());
+#else
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+}
+
+LayersPacket_Layer_Shadow::LayersPacket_Layer_Shadow(const LayersPacket_Layer_Shadow& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+void LayersPacket_Layer_Shadow::SharedCtor() {
+ _cached_size_ = 0;
+ clip_ = NULL;
+ transform_ = NULL;
+ vregion_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Shadow::~LayersPacket_Layer_Shadow() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Shadow::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete clip_;
+ delete transform_;
+ delete vregion_;
+ }
+}
+
+void LayersPacket_Layer_Shadow::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Shadow& LayersPacket_Layer_Shadow::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Shadow* LayersPacket_Layer_Shadow::default_instance_ = NULL;
+
+LayersPacket_Layer_Shadow* LayersPacket_Layer_Shadow::New() const {
+ return new LayersPacket_Layer_Shadow;
+}
+
+void LayersPacket_Layer_Shadow::Clear() {
+ if (_has_bits_[0 / 32] & 7) {
+ if (has_clip()) {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ }
+ if (has_transform()) {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ }
+ if (has_vregion()) {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Shadow::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_clip()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_transform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_transform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_transform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_vRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_vRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Shadow::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ if (has_clip()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->clip(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ if (has_transform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->transform(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ if (has_vregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->vregion(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+int LayersPacket_Layer_Shadow::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ if (has_clip()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->clip());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ if (has_transform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->transform());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ if (has_vregion()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vregion());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Shadow::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Shadow*>(&from));
+}
+
+void LayersPacket_Layer_Shadow::MergeFrom(const LayersPacket_Layer_Shadow& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_clip()) {
+ mutable_clip()->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::MergeFrom(from.clip());
+ }
+ if (from.has_transform()) {
+ mutable_transform()->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::MergeFrom(from.transform());
+ }
+ if (from.has_vregion()) {
+ mutable_vregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vregion());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Shadow::CopyFrom(const LayersPacket_Layer_Shadow& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Shadow::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Shadow::Swap(LayersPacket_Layer_Shadow* other) {
+ if (other != this) {
+ std::swap(clip_, other->clip_);
+ std::swap(transform_, other->transform_);
+ std::swap(vregion_, other->vregion_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Shadow::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Shadow";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer::kTypeFieldNumber;
+const int LayersPacket_Layer::kPtrFieldNumber;
+const int LayersPacket_Layer::kParentPtrFieldNumber;
+const int LayersPacket_Layer::kClipFieldNumber;
+const int LayersPacket_Layer::kTransformFieldNumber;
+const int LayersPacket_Layer::kVRegionFieldNumber;
+const int LayersPacket_Layer::kShadowFieldNumber;
+const int LayersPacket_Layer::kOpacityFieldNumber;
+const int LayersPacket_Layer::kCOpaqueFieldNumber;
+const int LayersPacket_Layer::kCAlphaFieldNumber;
+const int LayersPacket_Layer::kDirectFieldNumber;
+const int LayersPacket_Layer::kBarIDFieldNumber;
+const int LayersPacket_Layer::kMaskFieldNumber;
+const int LayersPacket_Layer::kHitRegionFieldNumber;
+const int LayersPacket_Layer::kDispatchRegionFieldNumber;
+const int LayersPacket_Layer::kNoActionRegionFieldNumber;
+const int LayersPacket_Layer::kHPanRegionFieldNumber;
+const int LayersPacket_Layer::kVPanRegionFieldNumber;
+const int LayersPacket_Layer::kValidFieldNumber;
+const int LayersPacket_Layer::kColorFieldNumber;
+const int LayersPacket_Layer::kFilterFieldNumber;
+const int LayersPacket_Layer::kRefIDFieldNumber;
+const int LayersPacket_Layer::kSizeFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogLengthFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer::LayersPacket_Layer()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+void LayersPacket_Layer::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect::internal_default_instance());
+#else
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::internal_default_instance());
+#else
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ shadow_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::internal_default_instance());
+#else
+ shadow_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ hitregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ hitregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ dispatchregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ dispatchregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ noactionregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ noactionregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ hpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ hpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ valid_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ valid_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ size_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Size*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size::internal_default_instance());
+#else
+ size_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Size*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Size::default_instance());
+#endif
+}
+
+LayersPacket_Layer::LayersPacket_Layer(const LayersPacket_Layer& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+void LayersPacket_Layer::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ type_ = 0;
+ ptr_ = GOOGLE_ULONGLONG(0);
+ parentptr_ = GOOGLE_ULONGLONG(0);
+ clip_ = NULL;
+ transform_ = NULL;
+ vregion_ = NULL;
+ shadow_ = NULL;
+ opacity_ = 0;
+ copaque_ = false;
+ calpha_ = false;
+ direct_ = 1;
+ barid_ = GOOGLE_ULONGLONG(0);
+ mask_ = GOOGLE_ULONGLONG(0);
+ hitregion_ = NULL;
+ dispatchregion_ = NULL;
+ noactionregion_ = NULL;
+ hpanregion_ = NULL;
+ vpanregion_ = NULL;
+ valid_ = NULL;
+ color_ = 0u;
+ filter_ = 0;
+ refid_ = GOOGLE_ULONGLONG(0);
+ size_ = NULL;
+ displaylistloglength_ = 0u;
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer::~LayersPacket_Layer() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer)
+ SharedDtor();
+}
+
+void LayersPacket_Layer::SharedDtor() {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete displaylistlog_;
+ }
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete clip_;
+ delete transform_;
+ delete vregion_;
+ delete shadow_;
+ delete hitregion_;
+ delete dispatchregion_;
+ delete noactionregion_;
+ delete hpanregion_;
+ delete vpanregion_;
+ delete valid_;
+ delete size_;
+ }
+}
+
+void LayersPacket_Layer::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer& LayersPacket_Layer::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer* LayersPacket_Layer::default_instance_ = NULL;
+
+LayersPacket_Layer* LayersPacket_Layer::New() const {
+ return new LayersPacket_Layer;
+}
+
+void LayersPacket_Layer::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 255) {
+ ZR_(ptr_, parentptr_);
+ ZR_(type_, opacity_);
+ if (has_clip()) {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ }
+ if (has_transform()) {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ }
+ if (has_vregion()) {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_shadow()) {
+ if (shadow_ != NULL) shadow_->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::Clear();
+ }
+ }
+ if (_has_bits_[8 / 32] & 65280) {
+ ZR_(copaque_, calpha_);
+ ZR_(barid_, mask_);
+ direct_ = 1;
+ if (has_hitregion()) {
+ if (hitregion_ != NULL) hitregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_dispatchregion()) {
+ if (dispatchregion_ != NULL) dispatchregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_noactionregion()) {
+ if (noactionregion_ != NULL) noactionregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ }
+ if (_has_bits_[16 / 32] & 16711680) {
+ ZR_(color_, refid_);
+ if (has_hpanregion()) {
+ if (hpanregion_ != NULL) hpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_vpanregion()) {
+ if (vpanregion_ != NULL) vpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_valid()) {
+ if (valid_ != NULL) valid_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_size()) {
+ if (size_ != NULL) size_->::mozilla::layers::layerscope::LayersPacket_Layer_Size::Clear();
+ }
+ displaylistloglength_ = 0u;
+ }
+ if (has_displaylistlog()) {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_->clear();
+ }
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(16383);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_LayerType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_ptr;
+ break;
+ }
+
+ // required uint64 ptr = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_ptr:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &ptr_)));
+ set_has_ptr();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_parentPtr;
+ break;
+ }
+
+ // required uint64 parentPtr = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_parentPtr:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &parentptr_)));
+ set_has_parentptr();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(82)) goto parse_clip;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ case 10: {
+ if (tag == 82) {
+ parse_clip:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_clip()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(90)) goto parse_transform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ case 11: {
+ if (tag == 90) {
+ parse_transform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_transform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(98)) goto parse_vRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ case 12: {
+ if (tag == 98) {
+ parse_vRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(106)) goto parse_shadow;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ case 13: {
+ if (tag == 106) {
+ parse_shadow:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_shadow()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(117)) goto parse_opacity;
+ break;
+ }
+
+ // optional float opacity = 14;
+ case 14: {
+ if (tag == 117) {
+ parse_opacity:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &opacity_)));
+ set_has_opacity();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(120)) goto parse_cOpaque;
+ break;
+ }
+
+ // optional bool cOpaque = 15;
+ case 15: {
+ if (tag == 120) {
+ parse_cOpaque:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &copaque_)));
+ set_has_copaque();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(128)) goto parse_cAlpha;
+ break;
+ }
+
+ // optional bool cAlpha = 16;
+ case 16: {
+ if (tag == 128) {
+ parse_cAlpha:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &calpha_)));
+ set_has_calpha();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(136)) goto parse_direct;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ case 17: {
+ if (tag == 136) {
+ parse_direct:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect_IsValid(value)) {
+ set_direct(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(144)) goto parse_barID;
+ break;
+ }
+
+ // optional uint64 barID = 18;
+ case 18: {
+ if (tag == 144) {
+ parse_barID:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &barid_)));
+ set_has_barid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(152)) goto parse_mask;
+ break;
+ }
+
+ // optional uint64 mask = 19;
+ case 19: {
+ if (tag == 152) {
+ parse_mask:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &mask_)));
+ set_has_mask();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(162)) goto parse_hitRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ case 20: {
+ if (tag == 162) {
+ parse_hitRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_hitregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(170)) goto parse_dispatchRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ case 21: {
+ if (tag == 170) {
+ parse_dispatchRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_dispatchregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(178)) goto parse_noActionRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ case 22: {
+ if (tag == 178) {
+ parse_noActionRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_noactionregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(186)) goto parse_hPanRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ case 23: {
+ if (tag == 186) {
+ parse_hPanRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_hpanregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(194)) goto parse_vPanRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ case 24: {
+ if (tag == 194) {
+ parse_vPanRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vpanregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(802)) goto parse_valid;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ case 100: {
+ if (tag == 802) {
+ parse_valid:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_valid()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(808)) goto parse_color;
+ break;
+ }
+
+ // optional uint32 color = 101;
+ case 101: {
+ if (tag == 808) {
+ parse_color:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &color_)));
+ set_has_color();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(816)) goto parse_filter;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ case 102: {
+ if (tag == 816) {
+ parse_filter:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_Filter_IsValid(value)) {
+ set_filter(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Filter >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(824)) goto parse_refID;
+ break;
+ }
+
+ // optional uint64 refID = 103;
+ case 103: {
+ if (tag == 824) {
+ parse_refID:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &refid_)));
+ set_has_refid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(834)) goto parse_size;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ case 104: {
+ if (tag == 834) {
+ parse_size:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_size()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(840)) goto parse_displayListLogLength;
+ break;
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ case 105: {
+ if (tag == 840) {
+ parse_displayListLogLength:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &displaylistloglength_)));
+ set_has_displaylistloglength();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(850)) goto parse_displayListLog;
+ break;
+ }
+
+ // optional bytes displayListLog = 106;
+ case 106: {
+ if (tag == 850) {
+ parse_displayListLog:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+ input, this->mutable_displaylistlog()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer)
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // required uint64 ptr = 2;
+ if (has_ptr()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(2, this->ptr(), output);
+ }
+
+ // required uint64 parentPtr = 3;
+ if (has_parentptr()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(3, this->parentptr(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ if (has_clip()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 10, this->clip(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ if (has_transform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 11, this->transform(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ if (has_vregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 12, this->vregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ if (has_shadow()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 13, this->shadow(), output);
+ }
+
+ // optional float opacity = 14;
+ if (has_opacity()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(14, this->opacity(), output);
+ }
+
+ // optional bool cOpaque = 15;
+ if (has_copaque()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(15, this->copaque(), output);
+ }
+
+ // optional bool cAlpha = 16;
+ if (has_calpha()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(16, this->calpha(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ if (has_direct()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 17, this->direct(), output);
+ }
+
+ // optional uint64 barID = 18;
+ if (has_barid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(18, this->barid(), output);
+ }
+
+ // optional uint64 mask = 19;
+ if (has_mask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(19, this->mask(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ if (has_hitregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 20, this->hitregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ if (has_dispatchregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 21, this->dispatchregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ if (has_noactionregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 22, this->noactionregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ if (has_hpanregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 23, this->hpanregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ if (has_vpanregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 24, this->vpanregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ if (has_valid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 100, this->valid(), output);
+ }
+
+ // optional uint32 color = 101;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(101, this->color(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ if (has_filter()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 102, this->filter(), output);
+ }
+
+ // optional uint64 refID = 103;
+ if (has_refid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(103, this->refid(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ if (has_size()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 104, this->size(), output);
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ if (has_displaylistloglength()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(105, this->displaylistloglength(), output);
+ }
+
+ // optional bytes displayListLog = 106;
+ if (has_displaylistlog()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
+ 106, this->displaylistlog(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+int LayersPacket_Layer::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // required uint64 ptr = 2;
+ if (has_ptr()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->ptr());
+ }
+
+ // required uint64 parentPtr = 3;
+ if (has_parentptr()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->parentptr());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ if (has_clip()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->clip());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ if (has_transform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->transform());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ if (has_vregion()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ if (has_shadow()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->shadow());
+ }
+
+ // optional float opacity = 14;
+ if (has_opacity()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ // optional bool cOpaque = 15;
+ if (has_copaque()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool cAlpha = 16;
+ if (has_calpha()) {
+ total_size += 2 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ if (has_direct()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->direct());
+ }
+
+ // optional uint64 barID = 18;
+ if (has_barid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->barid());
+ }
+
+ // optional uint64 mask = 19;
+ if (has_mask()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->mask());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ if (has_hitregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->hitregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ if (has_dispatchregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->dispatchregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ if (has_noactionregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->noactionregion());
+ }
+
+ }
+ if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ if (has_hpanregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->hpanregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ if (has_vpanregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vpanregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ if (has_valid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->valid());
+ }
+
+ // optional uint32 color = 101;
+ if (has_color()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->color());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ if (has_filter()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->filter());
+ }
+
+ // optional uint64 refID = 103;
+ if (has_refid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->refid());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ if (has_size()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->size());
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ if (has_displaylistloglength()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->displaylistloglength());
+ }
+
+ }
+ if (_has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+ // optional bytes displayListLog = 106;
+ if (has_displaylistlog()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::BytesSize(
+ this->displaylistlog());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer*>(&from));
+}
+
+void LayersPacket_Layer::MergeFrom(const LayersPacket_Layer& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_ptr()) {
+ set_ptr(from.ptr());
+ }
+ if (from.has_parentptr()) {
+ set_parentptr(from.parentptr());
+ }
+ if (from.has_clip()) {
+ mutable_clip()->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::MergeFrom(from.clip());
+ }
+ if (from.has_transform()) {
+ mutable_transform()->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::MergeFrom(from.transform());
+ }
+ if (from.has_vregion()) {
+ mutable_vregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vregion());
+ }
+ if (from.has_shadow()) {
+ mutable_shadow()->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::MergeFrom(from.shadow());
+ }
+ if (from.has_opacity()) {
+ set_opacity(from.opacity());
+ }
+ }
+ if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ if (from.has_copaque()) {
+ set_copaque(from.copaque());
+ }
+ if (from.has_calpha()) {
+ set_calpha(from.calpha());
+ }
+ if (from.has_direct()) {
+ set_direct(from.direct());
+ }
+ if (from.has_barid()) {
+ set_barid(from.barid());
+ }
+ if (from.has_mask()) {
+ set_mask(from.mask());
+ }
+ if (from.has_hitregion()) {
+ mutable_hitregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.hitregion());
+ }
+ if (from.has_dispatchregion()) {
+ mutable_dispatchregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.dispatchregion());
+ }
+ if (from.has_noactionregion()) {
+ mutable_noactionregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.noactionregion());
+ }
+ }
+ if (from._has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+ if (from.has_hpanregion()) {
+ mutable_hpanregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.hpanregion());
+ }
+ if (from.has_vpanregion()) {
+ mutable_vpanregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vpanregion());
+ }
+ if (from.has_valid()) {
+ mutable_valid()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.valid());
+ }
+ if (from.has_color()) {
+ set_color(from.color());
+ }
+ if (from.has_filter()) {
+ set_filter(from.filter());
+ }
+ if (from.has_refid()) {
+ set_refid(from.refid());
+ }
+ if (from.has_size()) {
+ mutable_size()->::mozilla::layers::layerscope::LayersPacket_Layer_Size::MergeFrom(from.size());
+ }
+ if (from.has_displaylistloglength()) {
+ set_displaylistloglength(from.displaylistloglength());
+ }
+ }
+ if (from._has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+ if (from.has_displaylistlog()) {
+ set_displaylistlog(from.displaylistlog());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer::CopyFrom(const LayersPacket_Layer& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000007) != 0x00000007) return false;
+
+ return true;
+}
+
+void LayersPacket_Layer::Swap(LayersPacket_Layer* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(ptr_, other->ptr_);
+ std::swap(parentptr_, other->parentptr_);
+ std::swap(clip_, other->clip_);
+ std::swap(transform_, other->transform_);
+ std::swap(vregion_, other->vregion_);
+ std::swap(shadow_, other->shadow_);
+ std::swap(opacity_, other->opacity_);
+ std::swap(copaque_, other->copaque_);
+ std::swap(calpha_, other->calpha_);
+ std::swap(direct_, other->direct_);
+ std::swap(barid_, other->barid_);
+ std::swap(mask_, other->mask_);
+ std::swap(hitregion_, other->hitregion_);
+ std::swap(dispatchregion_, other->dispatchregion_);
+ std::swap(noactionregion_, other->noactionregion_);
+ std::swap(hpanregion_, other->hpanregion_);
+ std::swap(vpanregion_, other->vpanregion_);
+ std::swap(valid_, other->valid_);
+ std::swap(color_, other->color_);
+ std::swap(filter_, other->filter_);
+ std::swap(refid_, other->refid_);
+ std::swap(size_, other->size_);
+ std::swap(displaylistloglength_, other->displaylistloglength_);
+ std::swap(displaylistlog_, other->displaylistlog_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket::kLayerFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket::LayersPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket)
+}
+
+void LayersPacket::InitAsDefaultInstance() {
+}
+
+LayersPacket::LayersPacket(const LayersPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket)
+}
+
+void LayersPacket::SharedCtor() {
+ _cached_size_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket::~LayersPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket)
+ SharedDtor();
+}
+
+void LayersPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket& LayersPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket* LayersPacket::default_instance_ = NULL;
+
+LayersPacket* LayersPacket::New() const {
+ return new LayersPacket;
+}
+
+void LayersPacket::Clear() {
+ layer_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ case 1: {
+ if (tag == 10) {
+ parse_layer:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_layer()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_layer;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket)
+ return false;
+#undef DO_
+}
+
+void LayersPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket)
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ for (int i = 0; i < this->layer_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->layer(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket)
+}
+
+int LayersPacket::ByteSize() const {
+ int total_size = 0;
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ total_size += 1 * this->layer_size();
+ for (int i = 0; i < this->layer_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layer(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket*>(&from));
+}
+
+void LayersPacket::MergeFrom(const LayersPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ layer_.MergeFrom(from.layer_);
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket::CopyFrom(const LayersPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket::IsInitialized() const {
+
+ if (!::google::protobuf::internal::AllAreInitialized(this->layer())) return false;
+ return true;
+}
+
+void LayersPacket::Swap(LayersPacket* other) {
+ if (other != this) {
+ layer_.Swap(&other->layer_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int MetaPacket::kComposedByHwcFieldNumber;
+#endif // !_MSC_VER
+
+MetaPacket::MetaPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.MetaPacket)
+}
+
+void MetaPacket::InitAsDefaultInstance() {
+}
+
+MetaPacket::MetaPacket(const MetaPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.MetaPacket)
+}
+
+void MetaPacket::SharedCtor() {
+ _cached_size_ = 0;
+ composedbyhwc_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+MetaPacket::~MetaPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.MetaPacket)
+ SharedDtor();
+}
+
+void MetaPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void MetaPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const MetaPacket& MetaPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+MetaPacket* MetaPacket::default_instance_ = NULL;
+
+MetaPacket* MetaPacket::New() const {
+ return new MetaPacket;
+}
+
+void MetaPacket::Clear() {
+ composedbyhwc_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool MetaPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.MetaPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool composedByHwc = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &composedbyhwc_)));
+ set_has_composedbyhwc();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.MetaPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.MetaPacket)
+ return false;
+#undef DO_
+}
+
+void MetaPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.MetaPacket)
+ // optional bool composedByHwc = 1;
+ if (has_composedbyhwc()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->composedbyhwc(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.MetaPacket)
+}
+
+int MetaPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool composedByHwc = 1;
+ if (has_composedbyhwc()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void MetaPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const MetaPacket*>(&from));
+}
+
+void MetaPacket::MergeFrom(const MetaPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_composedbyhwc()) {
+ set_composedbyhwc(from.composedbyhwc());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void MetaPacket::CopyFrom(const MetaPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool MetaPacket::IsInitialized() const {
+
+ return true;
+}
+
+void MetaPacket::Swap(MetaPacket* other) {
+ if (other != this) {
+ std::swap(composedbyhwc_, other->composedbyhwc_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string MetaPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.MetaPacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int DrawPacket_Rect::kXFieldNumber;
+const int DrawPacket_Rect::kYFieldNumber;
+const int DrawPacket_Rect::kWFieldNumber;
+const int DrawPacket_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+DrawPacket_Rect::DrawPacket_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+void DrawPacket_Rect::InitAsDefaultInstance() {
+}
+
+DrawPacket_Rect::DrawPacket_Rect(const DrawPacket_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+void DrawPacket_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+DrawPacket_Rect::~DrawPacket_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.DrawPacket.Rect)
+ SharedDtor();
+}
+
+void DrawPacket_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void DrawPacket_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const DrawPacket_Rect& DrawPacket_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+DrawPacket_Rect* DrawPacket_Rect::default_instance_ = NULL;
+
+DrawPacket_Rect* DrawPacket_Rect::New() const {
+ return new DrawPacket_Rect;
+}
+
+void DrawPacket_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<DrawPacket_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool DrawPacket_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.DrawPacket.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required float x = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_y;
+ break;
+ }
+
+ // required float y = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_w;
+ break;
+ }
+
+ // required float w = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(37)) goto parse_h;
+ break;
+ }
+
+ // required float h = 4;
+ case 4: {
+ if (tag == 37) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.DrawPacket.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.DrawPacket.Rect)
+ return false;
+#undef DO_
+}
+
+void DrawPacket_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.DrawPacket.Rect)
+ // required float x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->x(), output);
+ }
+
+ // required float y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->y(), output);
+ }
+
+ // required float w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->w(), output);
+ }
+
+ // required float h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+int DrawPacket_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required float x = 1;
+ if (has_x()) {
+ total_size += 1 + 4;
+ }
+
+ // required float y = 2;
+ if (has_y()) {
+ total_size += 1 + 4;
+ }
+
+ // required float w = 3;
+ if (has_w()) {
+ total_size += 1 + 4;
+ }
+
+ // required float h = 4;
+ if (has_h()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void DrawPacket_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const DrawPacket_Rect*>(&from));
+}
+
+void DrawPacket_Rect::MergeFrom(const DrawPacket_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void DrawPacket_Rect::CopyFrom(const DrawPacket_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool DrawPacket_Rect::IsInitialized() const {
+ if ((_has_bits_[0] & 0x0000000f) != 0x0000000f) return false;
+
+ return true;
+}
+
+void DrawPacket_Rect::Swap(DrawPacket_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string DrawPacket_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.DrawPacket.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int DrawPacket::kOffsetXFieldNumber;
+const int DrawPacket::kOffsetYFieldNumber;
+const int DrawPacket::kMvMatrixFieldNumber;
+const int DrawPacket::kTotalRectsFieldNumber;
+const int DrawPacket::kLayerRectFieldNumber;
+const int DrawPacket::kLayerrefFieldNumber;
+const int DrawPacket::kTexIDsFieldNumber;
+const int DrawPacket::kTextureRectFieldNumber;
+#endif // !_MSC_VER
+
+DrawPacket::DrawPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.DrawPacket)
+}
+
+void DrawPacket::InitAsDefaultInstance() {
+}
+
+DrawPacket::DrawPacket(const DrawPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.DrawPacket)
+}
+
+void DrawPacket::SharedCtor() {
+ _cached_size_ = 0;
+ offsetx_ = 0;
+ offsety_ = 0;
+ totalrects_ = 0u;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+DrawPacket::~DrawPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.DrawPacket)
+ SharedDtor();
+}
+
+void DrawPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void DrawPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const DrawPacket& DrawPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+DrawPacket* DrawPacket::default_instance_ = NULL;
+
+DrawPacket* DrawPacket::New() const {
+ return new DrawPacket;
+}
+
+void DrawPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<DrawPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 43) {
+ ZR_(offsetx_, offsety_);
+ totalrects_ = 0u;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ mvmatrix_.Clear();
+ layerrect_.Clear();
+ texids_.Clear();
+ texturerect_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool DrawPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.DrawPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required float offsetX = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &offsetx_)));
+ set_has_offsetx();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_offsetY;
+ break;
+ }
+
+ // required float offsetY = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_offsetY:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &offsety_)));
+ set_has_offsety();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_mvMatrix;
+ break;
+ }
+
+ // repeated float mvMatrix = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_mvMatrix:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_mvmatrix())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_mvmatrix())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_mvMatrix;
+ if (input->ExpectTag(32)) goto parse_totalRects;
+ break;
+ }
+
+ // required uint32 totalRects = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_totalRects:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &totalrects_)));
+ set_has_totalrects();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layerRect;
+ break;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ case 5: {
+ if (tag == 42) {
+ parse_layerRect:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_layerrect()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layerRect;
+ if (input->ExpectTag(48)) goto parse_layerref;
+ break;
+ }
+
+ // required uint64 layerref = 6;
+ case 6: {
+ if (tag == 48) {
+ parse_layerref:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_texIDs;
+ break;
+ }
+
+ // repeated uint32 texIDs = 7;
+ case 7: {
+ if (tag == 56) {
+ parse_texIDs:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ 1, 56, input, this->mutable_texids())));
+ } else if (tag == 58) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, this->mutable_texids())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_texIDs;
+ if (input->ExpectTag(66)) goto parse_textureRect;
+ break;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ case 8: {
+ if (tag == 66) {
+ parse_textureRect:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_texturerect()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(66)) goto parse_textureRect;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.DrawPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.DrawPacket)
+ return false;
+#undef DO_
+}
+
+void DrawPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.DrawPacket)
+ // required float offsetX = 1;
+ if (has_offsetx()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->offsetx(), output);
+ }
+
+ // required float offsetY = 2;
+ if (has_offsety()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->offsety(), output);
+ }
+
+ // repeated float mvMatrix = 3;
+ for (int i = 0; i < this->mvmatrix_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->mvmatrix(i), output);
+ }
+
+ // required uint32 totalRects = 4;
+ if (has_totalrects()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->totalrects(), output);
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ for (int i = 0; i < this->layerrect_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 5, this->layerrect(i), output);
+ }
+
+ // required uint64 layerref = 6;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(6, this->layerref(), output);
+ }
+
+ // repeated uint32 texIDs = 7;
+ for (int i = 0; i < this->texids_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(
+ 7, this->texids(i), output);
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ for (int i = 0; i < this->texturerect_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 8, this->texturerect(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.DrawPacket)
+}
+
+int DrawPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required float offsetX = 1;
+ if (has_offsetx()) {
+ total_size += 1 + 4;
+ }
+
+ // required float offsetY = 2;
+ if (has_offsety()) {
+ total_size += 1 + 4;
+ }
+
+ // required uint32 totalRects = 4;
+ if (has_totalrects()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->totalrects());
+ }
+
+ // required uint64 layerref = 6;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ }
+ // repeated float mvMatrix = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->mvmatrix_size();
+ total_size += 1 * this->mvmatrix_size() + data_size;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ total_size += 1 * this->layerrect_size();
+ for (int i = 0; i < this->layerrect_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layerrect(i));
+ }
+
+ // repeated uint32 texIDs = 7;
+ {
+ int data_size = 0;
+ for (int i = 0; i < this->texids_size(); i++) {
+ data_size += ::google::protobuf::internal::WireFormatLite::
+ UInt32Size(this->texids(i));
+ }
+ total_size += 1 * this->texids_size() + data_size;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ total_size += 1 * this->texturerect_size();
+ for (int i = 0; i < this->texturerect_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->texturerect(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void DrawPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const DrawPacket*>(&from));
+}
+
+void DrawPacket::MergeFrom(const DrawPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ mvmatrix_.MergeFrom(from.mvmatrix_);
+ layerrect_.MergeFrom(from.layerrect_);
+ texids_.MergeFrom(from.texids_);
+ texturerect_.MergeFrom(from.texturerect_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_offsetx()) {
+ set_offsetx(from.offsetx());
+ }
+ if (from.has_offsety()) {
+ set_offsety(from.offsety());
+ }
+ if (from.has_totalrects()) {
+ set_totalrects(from.totalrects());
+ }
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void DrawPacket::CopyFrom(const DrawPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool DrawPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x0000002b) != 0x0000002b) return false;
+
+ if (!::google::protobuf::internal::AllAreInitialized(this->layerrect())) return false;
+ if (!::google::protobuf::internal::AllAreInitialized(this->texturerect())) return false;
+ return true;
+}
+
+void DrawPacket::Swap(DrawPacket* other) {
+ if (other != this) {
+ std::swap(offsetx_, other->offsetx_);
+ std::swap(offsety_, other->offsety_);
+ mvmatrix_.Swap(&other->mvmatrix_);
+ std::swap(totalrects_, other->totalrects_);
+ layerrect_.Swap(&other->layerrect_);
+ std::swap(layerref_, other->layerref_);
+ texids_.Swap(&other->texids_);
+ texturerect_.Swap(&other->texturerect_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string DrawPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.DrawPacket";
+}
+
+
+// ===================================================================
+
+bool Packet_DataType_IsValid(int value) {
+ switch(value) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const Packet_DataType Packet::FRAMESTART;
+const Packet_DataType Packet::FRAMEEND;
+const Packet_DataType Packet::COLOR;
+const Packet_DataType Packet::TEXTURE;
+const Packet_DataType Packet::LAYERS;
+const Packet_DataType Packet::META;
+const Packet_DataType Packet::DRAW;
+const Packet_DataType Packet::DataType_MIN;
+const Packet_DataType Packet::DataType_MAX;
+const int Packet::DataType_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int Packet::kTypeFieldNumber;
+const int Packet::kFrameFieldNumber;
+const int Packet::kColorFieldNumber;
+const int Packet::kTextureFieldNumber;
+const int Packet::kLayersFieldNumber;
+const int Packet::kMetaFieldNumber;
+const int Packet::kDrawFieldNumber;
+#endif // !_MSC_VER
+
+Packet::Packet()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.Packet)
+}
+
+void Packet::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ frame_ = const_cast< ::mozilla::layers::layerscope::FramePacket*>(
+ ::mozilla::layers::layerscope::FramePacket::internal_default_instance());
+#else
+ frame_ = const_cast< ::mozilla::layers::layerscope::FramePacket*>(&::mozilla::layers::layerscope::FramePacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ color_ = const_cast< ::mozilla::layers::layerscope::ColorPacket*>(
+ ::mozilla::layers::layerscope::ColorPacket::internal_default_instance());
+#else
+ color_ = const_cast< ::mozilla::layers::layerscope::ColorPacket*>(&::mozilla::layers::layerscope::ColorPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ texture_ = const_cast< ::mozilla::layers::layerscope::TexturePacket*>(
+ ::mozilla::layers::layerscope::TexturePacket::internal_default_instance());
+#else
+ texture_ = const_cast< ::mozilla::layers::layerscope::TexturePacket*>(&::mozilla::layers::layerscope::TexturePacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ layers_ = const_cast< ::mozilla::layers::layerscope::LayersPacket*>(
+ ::mozilla::layers::layerscope::LayersPacket::internal_default_instance());
+#else
+ layers_ = const_cast< ::mozilla::layers::layerscope::LayersPacket*>(&::mozilla::layers::layerscope::LayersPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ meta_ = const_cast< ::mozilla::layers::layerscope::MetaPacket*>(
+ ::mozilla::layers::layerscope::MetaPacket::internal_default_instance());
+#else
+ meta_ = const_cast< ::mozilla::layers::layerscope::MetaPacket*>(&::mozilla::layers::layerscope::MetaPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ draw_ = const_cast< ::mozilla::layers::layerscope::DrawPacket*>(
+ ::mozilla::layers::layerscope::DrawPacket::internal_default_instance());
+#else
+ draw_ = const_cast< ::mozilla::layers::layerscope::DrawPacket*>(&::mozilla::layers::layerscope::DrawPacket::default_instance());
+#endif
+}
+
+Packet::Packet(const Packet& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.Packet)
+}
+
+void Packet::SharedCtor() {
+ _cached_size_ = 0;
+ type_ = 1;
+ frame_ = NULL;
+ color_ = NULL;
+ texture_ = NULL;
+ layers_ = NULL;
+ meta_ = NULL;
+ draw_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+Packet::~Packet() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.Packet)
+ SharedDtor();
+}
+
+void Packet::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete frame_;
+ delete color_;
+ delete texture_;
+ delete layers_;
+ delete meta_;
+ delete draw_;
+ }
+}
+
+void Packet::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const Packet& Packet::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+Packet* Packet::default_instance_ = NULL;
+
+Packet* Packet::New() const {
+ return new Packet;
+}
+
+void Packet::Clear() {
+ if (_has_bits_[0 / 32] & 127) {
+ type_ = 1;
+ if (has_frame()) {
+ if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear();
+ }
+ if (has_color()) {
+ if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear();
+ }
+ if (has_texture()) {
+ if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear();
+ }
+ if (has_layers()) {
+ if (layers_ != NULL) layers_->::mozilla::layers::layerscope::LayersPacket::Clear();
+ }
+ if (has_meta()) {
+ if (meta_ != NULL) meta_->::mozilla::layers::layerscope::MetaPacket::Clear();
+ }
+ if (has_draw()) {
+ if (draw_ != NULL) draw_->::mozilla::layers::layerscope::DrawPacket::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool Packet::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.Packet)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::Packet_DataType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::Packet_DataType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_frame;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_frame:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_frame()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_color;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_color:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_color()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(34)) goto parse_texture;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ case 4: {
+ if (tag == 34) {
+ parse_texture:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_texture()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layers;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ case 5: {
+ if (tag == 42) {
+ parse_layers:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_layers()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(50)) goto parse_meta;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ case 6: {
+ if (tag == 50) {
+ parse_meta:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_meta()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(58)) goto parse_draw;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ case 7: {
+ if (tag == 58) {
+ parse_draw:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_draw()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.Packet)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.Packet)
+ return false;
+#undef DO_
+}
+
+void Packet::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.Packet)
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ if (has_frame()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->frame(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->color(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ if (has_texture()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 4, this->texture(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ if (has_layers()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 5, this->layers(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ if (has_meta()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 6, this->meta(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ if (has_draw()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 7, this->draw(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.Packet)
+}
+
+int Packet::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ if (has_frame()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->frame());
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ if (has_color()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->color());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ if (has_texture()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->texture());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ if (has_layers()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layers());
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ if (has_meta()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->meta());
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ if (has_draw()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->draw());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void Packet::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const Packet*>(&from));
+}
+
+void Packet::MergeFrom(const Packet& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_frame()) {
+ mutable_frame()->::mozilla::layers::layerscope::FramePacket::MergeFrom(from.frame());
+ }
+ if (from.has_color()) {
+ mutable_color()->::mozilla::layers::layerscope::ColorPacket::MergeFrom(from.color());
+ }
+ if (from.has_texture()) {
+ mutable_texture()->::mozilla::layers::layerscope::TexturePacket::MergeFrom(from.texture());
+ }
+ if (from.has_layers()) {
+ mutable_layers()->::mozilla::layers::layerscope::LayersPacket::MergeFrom(from.layers());
+ }
+ if (from.has_meta()) {
+ mutable_meta()->::mozilla::layers::layerscope::MetaPacket::MergeFrom(from.meta());
+ }
+ if (from.has_draw()) {
+ mutable_draw()->::mozilla::layers::layerscope::DrawPacket::MergeFrom(from.draw());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void Packet::CopyFrom(const Packet& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Packet::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ if (has_color()) {
+ if (!this->color().IsInitialized()) return false;
+ }
+ if (has_texture()) {
+ if (!this->texture().IsInitialized()) return false;
+ }
+ if (has_layers()) {
+ if (!this->layers().IsInitialized()) return false;
+ }
+ if (has_draw()) {
+ if (!this->draw().IsInitialized()) return false;
+ }
+ return true;
+}
+
+void Packet::Swap(Packet* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(frame_, other->frame_);
+ std::swap(color_, other->color_);
+ std::swap(texture_, other->texture_);
+ std::swap(layers_, other->layers_);
+ std::swap(meta_, other->meta_);
+ std::swap(draw_, other->draw_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string Packet::GetTypeName() const {
+ return "mozilla.layers.layerscope.Packet";
+}
+
+
+// ===================================================================
+
+bool CommandPacket_CmdType_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const CommandPacket_CmdType CommandPacket::NO_OP;
+const CommandPacket_CmdType CommandPacket::LAYERS_TREE;
+const CommandPacket_CmdType CommandPacket::LAYERS_BUFFER;
+const CommandPacket_CmdType CommandPacket::CmdType_MIN;
+const CommandPacket_CmdType CommandPacket::CmdType_MAX;
+const int CommandPacket::CmdType_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int CommandPacket::kTypeFieldNumber;
+const int CommandPacket::kValueFieldNumber;
+#endif // !_MSC_VER
+
+CommandPacket::CommandPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.CommandPacket)
+}
+
+void CommandPacket::InitAsDefaultInstance() {
+}
+
+CommandPacket::CommandPacket(const CommandPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.CommandPacket)
+}
+
+void CommandPacket::SharedCtor() {
+ _cached_size_ = 0;
+ type_ = 0;
+ value_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+CommandPacket::~CommandPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.CommandPacket)
+ SharedDtor();
+}
+
+void CommandPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void CommandPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const CommandPacket& CommandPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+CommandPacket* CommandPacket::default_instance_ = NULL;
+
+CommandPacket* CommandPacket::New() const {
+ return new CommandPacket;
+}
+
+void CommandPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<CommandPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(type_, value_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool CommandPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.CommandPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_value;
+ break;
+ }
+
+ // optional bool value = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_value:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &value_)));
+ set_has_value();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.CommandPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.CommandPacket)
+ return false;
+#undef DO_
+}
+
+void CommandPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.CommandPacket)
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // optional bool value = 2;
+ if (has_value()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->value(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.CommandPacket)
+}
+
+int CommandPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // optional bool value = 2;
+ if (has_value()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void CommandPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const CommandPacket*>(&from));
+}
+
+void CommandPacket::MergeFrom(const CommandPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_value()) {
+ set_value(from.value());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void CommandPacket::CopyFrom(const CommandPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool CommandPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void CommandPacket::Swap(CommandPacket* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(value_, other->value_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string CommandPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.CommandPacket";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace layerscope
+} // namespace layers
+} // namespace mozilla
+
+// @@protoc_insertion_point(global_scope)
diff --git a/system/graphics/layers/protobuf/LayerScopePacket.pb.h b/system/graphics/layers/protobuf/LayerScopePacket.pb.h
new file mode 100644
index 000000000..1a850c03d
--- /dev/null
+++ b/system/graphics/layers/protobuf/LayerScopePacket.pb.h
@@ -0,0 +1,5779 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: LayerScopePacket.proto
+
+#ifndef PROTOBUF_LayerScopePacket_2eproto__INCLUDED
+#define PROTOBUF_LayerScopePacket_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 2006000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+// @@protoc_insertion_point(includes)
+
+namespace mozilla {
+namespace layers {
+namespace layerscope {
+
+// Internal implementation detail -- do not call these.
+void protobuf_AddDesc_LayerScopePacket_2eproto();
+void protobuf_AssignDesc_LayerScopePacket_2eproto();
+void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+class FramePacket;
+class ColorPacket;
+class TexturePacket;
+class TexturePacket_Rect;
+class TexturePacket_Size;
+class TexturePacket_Matrix;
+class TexturePacket_EffectMask;
+class LayersPacket;
+class LayersPacket_Layer;
+class LayersPacket_Layer_Size;
+class LayersPacket_Layer_Rect;
+class LayersPacket_Layer_Region;
+class LayersPacket_Layer_Matrix;
+class LayersPacket_Layer_Shadow;
+class MetaPacket;
+class DrawPacket;
+class DrawPacket_Rect;
+class Packet;
+class CommandPacket;
+
+enum TexturePacket_Filter {
+ TexturePacket_Filter_GOOD = 0,
+ TexturePacket_Filter_LINEAR = 1,
+ TexturePacket_Filter_POINT = 2
+};
+bool TexturePacket_Filter_IsValid(int value);
+const TexturePacket_Filter TexturePacket_Filter_Filter_MIN = TexturePacket_Filter_GOOD;
+const TexturePacket_Filter TexturePacket_Filter_Filter_MAX = TexturePacket_Filter_POINT;
+const int TexturePacket_Filter_Filter_ARRAYSIZE = TexturePacket_Filter_Filter_MAX + 1;
+
+enum LayersPacket_Layer_LayerType {
+ LayersPacket_Layer_LayerType_UnknownLayer = 0,
+ LayersPacket_Layer_LayerType_LayerManager = 1,
+ LayersPacket_Layer_LayerType_ContainerLayer = 2,
+ LayersPacket_Layer_LayerType_PaintedLayer = 3,
+ LayersPacket_Layer_LayerType_CanvasLayer = 4,
+ LayersPacket_Layer_LayerType_ImageLayer = 5,
+ LayersPacket_Layer_LayerType_ColorLayer = 6,
+ LayersPacket_Layer_LayerType_RefLayer = 7,
+ LayersPacket_Layer_LayerType_ReadbackLayer = 8
+};
+bool LayersPacket_Layer_LayerType_IsValid(int value);
+const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MIN = LayersPacket_Layer_LayerType_UnknownLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MAX = LayersPacket_Layer_LayerType_ReadbackLayer;
+const int LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE = LayersPacket_Layer_LayerType_LayerType_MAX + 1;
+
+enum LayersPacket_Layer_ScrollingDirect {
+ LayersPacket_Layer_ScrollingDirect_VERTICAL = 1,
+ LayersPacket_Layer_ScrollingDirect_HORIZONTAL = 2
+};
+bool LayersPacket_Layer_ScrollingDirect_IsValid(int value);
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MIN = LayersPacket_Layer_ScrollingDirect_VERTICAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX = LayersPacket_Layer_ScrollingDirect_HORIZONTAL;
+const int LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE = LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX + 1;
+
+enum LayersPacket_Layer_Filter {
+ LayersPacket_Layer_Filter_FILTER_FAST = 0,
+ LayersPacket_Layer_Filter_FILTER_GOOD = 1,
+ LayersPacket_Layer_Filter_FILTER_BEST = 2,
+ LayersPacket_Layer_Filter_FILTER_NEAREST = 3,
+ LayersPacket_Layer_Filter_FILTER_BILINEAR = 4,
+ LayersPacket_Layer_Filter_FILTER_GAUSSIAN = 5,
+ LayersPacket_Layer_Filter_FILTER_SENTINEL = 6,
+ LayersPacket_Layer_Filter_FILTER_LINEAR = 7,
+ LayersPacket_Layer_Filter_FILTER_POINT = 8
+};
+bool LayersPacket_Layer_Filter_IsValid(int value);
+const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MIN = LayersPacket_Layer_Filter_FILTER_FAST;
+const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MAX = LayersPacket_Layer_Filter_FILTER_POINT;
+const int LayersPacket_Layer_Filter_Filter_ARRAYSIZE = LayersPacket_Layer_Filter_Filter_MAX + 1;
+
+enum Packet_DataType {
+ Packet_DataType_FRAMESTART = 1,
+ Packet_DataType_FRAMEEND = 2,
+ Packet_DataType_COLOR = 3,
+ Packet_DataType_TEXTURE = 4,
+ Packet_DataType_LAYERS = 5,
+ Packet_DataType_META = 6,
+ Packet_DataType_DRAW = 7
+};
+bool Packet_DataType_IsValid(int value);
+const Packet_DataType Packet_DataType_DataType_MIN = Packet_DataType_FRAMESTART;
+const Packet_DataType Packet_DataType_DataType_MAX = Packet_DataType_DRAW;
+const int Packet_DataType_DataType_ARRAYSIZE = Packet_DataType_DataType_MAX + 1;
+
+enum CommandPacket_CmdType {
+ CommandPacket_CmdType_NO_OP = 0,
+ CommandPacket_CmdType_LAYERS_TREE = 1,
+ CommandPacket_CmdType_LAYERS_BUFFER = 2
+};
+bool CommandPacket_CmdType_IsValid(int value);
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MIN = CommandPacket_CmdType_NO_OP;
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MAX = CommandPacket_CmdType_LAYERS_BUFFER;
+const int CommandPacket_CmdType_CmdType_ARRAYSIZE = CommandPacket_CmdType_CmdType_MAX + 1;
+
+// ===================================================================
+
+class FramePacket : public ::google::protobuf::MessageLite {
+ public:
+ FramePacket();
+ virtual ~FramePacket();
+
+ FramePacket(const FramePacket& from);
+
+ inline FramePacket& operator=(const FramePacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const FramePacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const FramePacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(FramePacket* other);
+
+ // implements Message ----------------------------------------------
+
+ FramePacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const FramePacket& from);
+ void MergeFrom(const FramePacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional uint64 value = 1;
+ inline bool has_value() const;
+ inline void clear_value();
+ static const int kValueFieldNumber = 1;
+ inline ::google::protobuf::uint64 value() const;
+ inline void set_value(::google::protobuf::uint64 value);
+
+ // optional float scale = 2;
+ inline bool has_scale() const;
+ inline void clear_scale();
+ static const int kScaleFieldNumber = 2;
+ inline float scale() const;
+ inline void set_scale(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.FramePacket)
+ private:
+ inline void set_has_value();
+ inline void clear_has_value();
+ inline void set_has_scale();
+ inline void clear_has_scale();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 value_;
+ float scale_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static FramePacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class ColorPacket : public ::google::protobuf::MessageLite {
+ public:
+ ColorPacket();
+ virtual ~ColorPacket();
+
+ ColorPacket(const ColorPacket& from);
+
+ inline ColorPacket& operator=(const ColorPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const ColorPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const ColorPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(ColorPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ ColorPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const ColorPacket& from);
+ void MergeFrom(const ColorPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // required uint64 layerref = 1;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 1;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // optional uint32 width = 2;
+ inline bool has_width() const;
+ inline void clear_width();
+ static const int kWidthFieldNumber = 2;
+ inline ::google::protobuf::uint32 width() const;
+ inline void set_width(::google::protobuf::uint32 value);
+
+ // optional uint32 height = 3;
+ inline bool has_height() const;
+ inline void clear_height();
+ static const int kHeightFieldNumber = 3;
+ inline ::google::protobuf::uint32 height() const;
+ inline void set_height(::google::protobuf::uint32 value);
+
+ // optional uint32 color = 4;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 4;
+ inline ::google::protobuf::uint32 color() const;
+ inline void set_color(::google::protobuf::uint32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.ColorPacket)
+ private:
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+ inline void set_has_width();
+ inline void clear_has_width();
+ inline void set_has_height();
+ inline void clear_has_height();
+ inline void set_has_color();
+ inline void clear_has_color();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::uint32 width_;
+ ::google::protobuf::uint32 height_;
+ ::google::protobuf::uint32 color_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static ColorPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Rect : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Rect();
+ virtual ~TexturePacket_Rect();
+
+ TexturePacket_Rect(const TexturePacket_Rect& from);
+
+ inline TexturePacket_Rect& operator=(const TexturePacket_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Rect& from);
+ void MergeFrom(const TexturePacket_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional float x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline float x() const;
+ inline void set_x(float value);
+
+ // optional float y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline float y() const;
+ inline void set_y(float value);
+
+ // optional float w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline float w() const;
+ inline void set_w(float value);
+
+ // optional float h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline float h() const;
+ inline void set_h(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float x_;
+ float y_;
+ float w_;
+ float h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Size : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Size();
+ virtual ~TexturePacket_Size();
+
+ TexturePacket_Size(const TexturePacket_Size& from);
+
+ inline TexturePacket_Size& operator=(const TexturePacket_Size& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Size& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Size* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Size* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Size* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Size& from);
+ void MergeFrom(const TexturePacket_Size& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 w = 1;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 1;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 2;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 2;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Size)
+ private:
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Size* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Matrix : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Matrix();
+ virtual ~TexturePacket_Matrix();
+
+ TexturePacket_Matrix(const TexturePacket_Matrix& from);
+
+ inline TexturePacket_Matrix& operator=(const TexturePacket_Matrix& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Matrix& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Matrix* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Matrix* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Matrix* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Matrix& from);
+ void MergeFrom(const TexturePacket_Matrix& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool is2D = 1;
+ inline bool has_is2d() const;
+ inline void clear_is2d();
+ static const int kIs2DFieldNumber = 1;
+ inline bool is2d() const;
+ inline void set_is2d(bool value);
+
+ // optional bool isId = 2;
+ inline bool has_isid() const;
+ inline void clear_isid();
+ static const int kIsIdFieldNumber = 2;
+ inline bool isid() const;
+ inline void set_isid(bool value);
+
+ // repeated float m = 3;
+ inline int m_size() const;
+ inline void clear_m();
+ static const int kMFieldNumber = 3;
+ inline float m(int index) const;
+ inline void set_m(int index, float value);
+ inline void add_m(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ m() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_m();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Matrix)
+ private:
+ inline void set_has_is2d();
+ inline void clear_has_is2d();
+ inline void set_has_isid();
+ inline void clear_has_isid();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedField< float > m_;
+ bool is2d_;
+ bool isid_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Matrix* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_EffectMask : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_EffectMask();
+ virtual ~TexturePacket_EffectMask();
+
+ TexturePacket_EffectMask(const TexturePacket_EffectMask& from);
+
+ inline TexturePacket_EffectMask& operator=(const TexturePacket_EffectMask& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_EffectMask& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_EffectMask* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_EffectMask* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_EffectMask* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_EffectMask& from);
+ void MergeFrom(const TexturePacket_EffectMask& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool mIs3D = 1;
+ inline bool has_mis3d() const;
+ inline void clear_mis3d();
+ static const int kMIs3DFieldNumber = 1;
+ inline bool mis3d() const;
+ inline void set_mis3d(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ inline bool has_msize() const;
+ inline void clear_msize();
+ static const int kMSizeFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Size& msize() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Size* mutable_msize();
+ inline ::mozilla::layers::layerscope::TexturePacket_Size* release_msize();
+ inline void set_allocated_msize(::mozilla::layers::layerscope::TexturePacket_Size* msize);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ inline bool has_mmasktransform() const;
+ inline void clear_mmasktransform();
+ static const int kMMaskTransformFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Matrix& mmasktransform() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Matrix* mutable_mmasktransform();
+ inline ::mozilla::layers::layerscope::TexturePacket_Matrix* release_mmasktransform();
+ inline void set_allocated_mmasktransform(::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ private:
+ inline void set_has_mis3d();
+ inline void clear_has_mis3d();
+ inline void set_has_msize();
+ inline void clear_has_msize();
+ inline void set_has_mmasktransform();
+ inline void clear_has_mmasktransform();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::TexturePacket_Size* msize_;
+ ::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform_;
+ bool mis3d_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_EffectMask* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket();
+ virtual ~TexturePacket();
+
+ TexturePacket(const TexturePacket& from);
+
+ inline TexturePacket& operator=(const TexturePacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket& from);
+ void MergeFrom(const TexturePacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef TexturePacket_Rect Rect;
+ typedef TexturePacket_Size Size;
+ typedef TexturePacket_Matrix Matrix;
+ typedef TexturePacket_EffectMask EffectMask;
+
+ typedef TexturePacket_Filter Filter;
+ static const Filter GOOD = TexturePacket_Filter_GOOD;
+ static const Filter LINEAR = TexturePacket_Filter_LINEAR;
+ static const Filter POINT = TexturePacket_Filter_POINT;
+ static inline bool Filter_IsValid(int value) {
+ return TexturePacket_Filter_IsValid(value);
+ }
+ static const Filter Filter_MIN =
+ TexturePacket_Filter_Filter_MIN;
+ static const Filter Filter_MAX =
+ TexturePacket_Filter_Filter_MAX;
+ static const int Filter_ARRAYSIZE =
+ TexturePacket_Filter_Filter_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required uint64 layerref = 1;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 1;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // optional uint32 width = 2;
+ inline bool has_width() const;
+ inline void clear_width();
+ static const int kWidthFieldNumber = 2;
+ inline ::google::protobuf::uint32 width() const;
+ inline void set_width(::google::protobuf::uint32 value);
+
+ // optional uint32 height = 3;
+ inline bool has_height() const;
+ inline void clear_height();
+ static const int kHeightFieldNumber = 3;
+ inline ::google::protobuf::uint32 height() const;
+ inline void set_height(::google::protobuf::uint32 value);
+
+ // optional uint32 stride = 4;
+ inline bool has_stride() const;
+ inline void clear_stride();
+ static const int kStrideFieldNumber = 4;
+ inline ::google::protobuf::uint32 stride() const;
+ inline void set_stride(::google::protobuf::uint32 value);
+
+ // optional uint32 name = 5;
+ inline bool has_name() const;
+ inline void clear_name();
+ static const int kNameFieldNumber = 5;
+ inline ::google::protobuf::uint32 name() const;
+ inline void set_name(::google::protobuf::uint32 value);
+
+ // optional uint32 target = 6;
+ inline bool has_target() const;
+ inline void clear_target();
+ static const int kTargetFieldNumber = 6;
+ inline ::google::protobuf::uint32 target() const;
+ inline void set_target(::google::protobuf::uint32 value);
+
+ // optional uint32 dataformat = 7;
+ inline bool has_dataformat() const;
+ inline void clear_dataformat();
+ static const int kDataformatFieldNumber = 7;
+ inline ::google::protobuf::uint32 dataformat() const;
+ inline void set_dataformat(::google::protobuf::uint32 value);
+
+ // optional uint64 glcontext = 8;
+ inline bool has_glcontext() const;
+ inline void clear_glcontext();
+ static const int kGlcontextFieldNumber = 8;
+ inline ::google::protobuf::uint64 glcontext() const;
+ inline void set_glcontext(::google::protobuf::uint64 value);
+
+ // optional bytes data = 9;
+ inline bool has_data() const;
+ inline void clear_data();
+ static const int kDataFieldNumber = 9;
+ inline const ::std::string& data() const;
+ inline void set_data(const ::std::string& value);
+ inline void set_data(const char* value);
+ inline void set_data(const void* value, size_t size);
+ inline ::std::string* mutable_data();
+ inline ::std::string* release_data();
+ inline void set_allocated_data(::std::string* data);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ inline bool has_mtexturecoords() const;
+ inline void clear_mtexturecoords();
+ static const int kMTextureCoordsFieldNumber = 10;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Rect& mtexturecoords() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Rect* mutable_mtexturecoords();
+ inline ::mozilla::layers::layerscope::TexturePacket_Rect* release_mtexturecoords();
+ inline void set_allocated_mtexturecoords(::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords);
+
+ // optional bool mPremultiplied = 11;
+ inline bool has_mpremultiplied() const;
+ inline void clear_mpremultiplied();
+ static const int kMPremultipliedFieldNumber = 11;
+ inline bool mpremultiplied() const;
+ inline void set_mpremultiplied(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ inline bool has_mfilter() const;
+ inline void clear_mfilter();
+ static const int kMFilterFieldNumber = 12;
+ inline ::mozilla::layers::layerscope::TexturePacket_Filter mfilter() const;
+ inline void set_mfilter(::mozilla::layers::layerscope::TexturePacket_Filter value);
+
+ // optional bool isMask = 20;
+ inline bool has_ismask() const;
+ inline void clear_ismask();
+ static const int kIsMaskFieldNumber = 20;
+ inline bool ismask() const;
+ inline void set_ismask(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ inline bool has_mask() const;
+ inline void clear_mask();
+ static const int kMaskFieldNumber = 21;
+ inline const ::mozilla::layers::layerscope::TexturePacket_EffectMask& mask() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* mutable_mask();
+ inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* release_mask();
+ inline void set_allocated_mask(::mozilla::layers::layerscope::TexturePacket_EffectMask* mask);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket)
+ private:
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+ inline void set_has_width();
+ inline void clear_has_width();
+ inline void set_has_height();
+ inline void clear_has_height();
+ inline void set_has_stride();
+ inline void clear_has_stride();
+ inline void set_has_name();
+ inline void clear_has_name();
+ inline void set_has_target();
+ inline void clear_has_target();
+ inline void set_has_dataformat();
+ inline void clear_has_dataformat();
+ inline void set_has_glcontext();
+ inline void clear_has_glcontext();
+ inline void set_has_data();
+ inline void clear_has_data();
+ inline void set_has_mtexturecoords();
+ inline void clear_has_mtexturecoords();
+ inline void set_has_mpremultiplied();
+ inline void clear_has_mpremultiplied();
+ inline void set_has_mfilter();
+ inline void clear_has_mfilter();
+ inline void set_has_ismask();
+ inline void clear_has_ismask();
+ inline void set_has_mask();
+ inline void clear_has_mask();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::uint32 width_;
+ ::google::protobuf::uint32 height_;
+ ::google::protobuf::uint32 stride_;
+ ::google::protobuf::uint32 name_;
+ ::google::protobuf::uint32 target_;
+ ::google::protobuf::uint32 dataformat_;
+ ::google::protobuf::uint64 glcontext_;
+ ::std::string* data_;
+ ::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords_;
+ int mfilter_;
+ bool mpremultiplied_;
+ bool ismask_;
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask* mask_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Size : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Size();
+ virtual ~LayersPacket_Layer_Size();
+
+ LayersPacket_Layer_Size(const LayersPacket_Layer_Size& from);
+
+ inline LayersPacket_Layer_Size& operator=(const LayersPacket_Layer_Size& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Size& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Size* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Size* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Size* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Size& from);
+ void MergeFrom(const LayersPacket_Layer_Size& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 w = 1;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 1;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 2;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 2;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ private:
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Size* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Rect : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Rect();
+ virtual ~LayersPacket_Layer_Rect();
+
+ LayersPacket_Layer_Rect(const LayersPacket_Layer_Rect& from);
+
+ inline LayersPacket_Layer_Rect& operator=(const LayersPacket_Layer_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Rect& from);
+ void MergeFrom(const LayersPacket_Layer_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline ::google::protobuf::int32 x() const;
+ inline void set_x(::google::protobuf::int32 value);
+
+ // optional int32 y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline ::google::protobuf::int32 y() const;
+ inline void set_y(::google::protobuf::int32 value);
+
+ // optional int32 w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 x_;
+ ::google::protobuf::int32 y_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Region : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Region();
+ virtual ~LayersPacket_Layer_Region();
+
+ LayersPacket_Layer_Region(const LayersPacket_Layer_Region& from);
+
+ inline LayersPacket_Layer_Region& operator=(const LayersPacket_Layer_Region& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Region& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Region* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Region* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Region* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Region& from);
+ void MergeFrom(const LayersPacket_Layer_Region& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ inline int r_size() const;
+ inline void clear_r();
+ static const int kRFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& r(int index) const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_r(int index);
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* add_r();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >&
+ r() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >*
+ mutable_r();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ private:
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect > r_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Region* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Matrix : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Matrix();
+ virtual ~LayersPacket_Layer_Matrix();
+
+ LayersPacket_Layer_Matrix(const LayersPacket_Layer_Matrix& from);
+
+ inline LayersPacket_Layer_Matrix& operator=(const LayersPacket_Layer_Matrix& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Matrix& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Matrix* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Matrix* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Matrix* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Matrix& from);
+ void MergeFrom(const LayersPacket_Layer_Matrix& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool is2D = 1;
+ inline bool has_is2d() const;
+ inline void clear_is2d();
+ static const int kIs2DFieldNumber = 1;
+ inline bool is2d() const;
+ inline void set_is2d(bool value);
+
+ // optional bool isId = 2;
+ inline bool has_isid() const;
+ inline void clear_isid();
+ static const int kIsIdFieldNumber = 2;
+ inline bool isid() const;
+ inline void set_isid(bool value);
+
+ // repeated float m = 3;
+ inline int m_size() const;
+ inline void clear_m();
+ static const int kMFieldNumber = 3;
+ inline float m(int index) const;
+ inline void set_m(int index, float value);
+ inline void add_m(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ m() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_m();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ private:
+ inline void set_has_is2d();
+ inline void clear_has_is2d();
+ inline void set_has_isid();
+ inline void clear_has_isid();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedField< float > m_;
+ bool is2d_;
+ bool isid_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Matrix* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Shadow : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Shadow();
+ virtual ~LayersPacket_Layer_Shadow();
+
+ LayersPacket_Layer_Shadow(const LayersPacket_Layer_Shadow& from);
+
+ inline LayersPacket_Layer_Shadow& operator=(const LayersPacket_Layer_Shadow& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Shadow& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Shadow* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Shadow* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Shadow* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Shadow& from);
+ void MergeFrom(const LayersPacket_Layer_Shadow& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ inline bool has_clip() const;
+ inline void clear_clip();
+ static const int kClipFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& clip() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_clip();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* release_clip();
+ inline void set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ inline bool has_transform() const;
+ inline void clear_transform();
+ static const int kTransformFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& transform() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* mutable_transform();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* release_transform();
+ inline void set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ inline bool has_vregion() const;
+ inline void clear_vregion();
+ static const int kVRegionFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vregion();
+ inline void set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ private:
+ inline void set_has_clip();
+ inline void clear_has_clip();
+ inline void set_has_transform();
+ inline void clear_has_transform();
+ inline void set_has_vregion();
+ inline void clear_has_vregion();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Shadow* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer();
+ virtual ~LayersPacket_Layer();
+
+ LayersPacket_Layer(const LayersPacket_Layer& from);
+
+ inline LayersPacket_Layer& operator=(const LayersPacket_Layer& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer& from);
+ void MergeFrom(const LayersPacket_Layer& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef LayersPacket_Layer_Size Size;
+ typedef LayersPacket_Layer_Rect Rect;
+ typedef LayersPacket_Layer_Region Region;
+ typedef LayersPacket_Layer_Matrix Matrix;
+ typedef LayersPacket_Layer_Shadow Shadow;
+
+ typedef LayersPacket_Layer_LayerType LayerType;
+ static const LayerType UnknownLayer = LayersPacket_Layer_LayerType_UnknownLayer;
+ static const LayerType LayerManager = LayersPacket_Layer_LayerType_LayerManager;
+ static const LayerType ContainerLayer = LayersPacket_Layer_LayerType_ContainerLayer;
+ static const LayerType PaintedLayer = LayersPacket_Layer_LayerType_PaintedLayer;
+ static const LayerType CanvasLayer = LayersPacket_Layer_LayerType_CanvasLayer;
+ static const LayerType ImageLayer = LayersPacket_Layer_LayerType_ImageLayer;
+ static const LayerType ColorLayer = LayersPacket_Layer_LayerType_ColorLayer;
+ static const LayerType RefLayer = LayersPacket_Layer_LayerType_RefLayer;
+ static const LayerType ReadbackLayer = LayersPacket_Layer_LayerType_ReadbackLayer;
+ static inline bool LayerType_IsValid(int value) {
+ return LayersPacket_Layer_LayerType_IsValid(value);
+ }
+ static const LayerType LayerType_MIN =
+ LayersPacket_Layer_LayerType_LayerType_MIN;
+ static const LayerType LayerType_MAX =
+ LayersPacket_Layer_LayerType_LayerType_MAX;
+ static const int LayerType_ARRAYSIZE =
+ LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE;
+
+ typedef LayersPacket_Layer_ScrollingDirect ScrollingDirect;
+ static const ScrollingDirect VERTICAL = LayersPacket_Layer_ScrollingDirect_VERTICAL;
+ static const ScrollingDirect HORIZONTAL = LayersPacket_Layer_ScrollingDirect_HORIZONTAL;
+ static inline bool ScrollingDirect_IsValid(int value) {
+ return LayersPacket_Layer_ScrollingDirect_IsValid(value);
+ }
+ static const ScrollingDirect ScrollingDirect_MIN =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MIN;
+ static const ScrollingDirect ScrollingDirect_MAX =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX;
+ static const int ScrollingDirect_ARRAYSIZE =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE;
+
+ typedef LayersPacket_Layer_Filter Filter;
+ static const Filter FILTER_FAST = LayersPacket_Layer_Filter_FILTER_FAST;
+ static const Filter FILTER_GOOD = LayersPacket_Layer_Filter_FILTER_GOOD;
+ static const Filter FILTER_BEST = LayersPacket_Layer_Filter_FILTER_BEST;
+ static const Filter FILTER_NEAREST = LayersPacket_Layer_Filter_FILTER_NEAREST;
+ static const Filter FILTER_BILINEAR = LayersPacket_Layer_Filter_FILTER_BILINEAR;
+ static const Filter FILTER_GAUSSIAN = LayersPacket_Layer_Filter_FILTER_GAUSSIAN;
+ static const Filter FILTER_SENTINEL = LayersPacket_Layer_Filter_FILTER_SENTINEL;
+ static const Filter FILTER_LINEAR = LayersPacket_Layer_Filter_FILTER_LINEAR;
+ static const Filter FILTER_POINT = LayersPacket_Layer_Filter_FILTER_POINT;
+ static inline bool Filter_IsValid(int value) {
+ return LayersPacket_Layer_Filter_IsValid(value);
+ }
+ static const Filter Filter_MIN =
+ LayersPacket_Layer_Filter_Filter_MIN;
+ static const Filter Filter_MAX =
+ LayersPacket_Layer_Filter_Filter_MAX;
+ static const int Filter_ARRAYSIZE =
+ LayersPacket_Layer_Filter_Filter_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType type() const;
+ inline void set_type(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType value);
+
+ // required uint64 ptr = 2;
+ inline bool has_ptr() const;
+ inline void clear_ptr();
+ static const int kPtrFieldNumber = 2;
+ inline ::google::protobuf::uint64 ptr() const;
+ inline void set_ptr(::google::protobuf::uint64 value);
+
+ // required uint64 parentPtr = 3;
+ inline bool has_parentptr() const;
+ inline void clear_parentptr();
+ static const int kParentPtrFieldNumber = 3;
+ inline ::google::protobuf::uint64 parentptr() const;
+ inline void set_parentptr(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ inline bool has_clip() const;
+ inline void clear_clip();
+ static const int kClipFieldNumber = 10;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& clip() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_clip();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* release_clip();
+ inline void set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ inline bool has_transform() const;
+ inline void clear_transform();
+ static const int kTransformFieldNumber = 11;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& transform() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* mutable_transform();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* release_transform();
+ inline void set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ inline bool has_vregion() const;
+ inline void clear_vregion();
+ static const int kVRegionFieldNumber = 12;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vregion();
+ inline void set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ inline bool has_shadow() const;
+ inline void clear_shadow();
+ static const int kShadowFieldNumber = 13;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow& shadow() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* mutable_shadow();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* release_shadow();
+ inline void set_allocated_shadow(::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow);
+
+ // optional float opacity = 14;
+ inline bool has_opacity() const;
+ inline void clear_opacity();
+ static const int kOpacityFieldNumber = 14;
+ inline float opacity() const;
+ inline void set_opacity(float value);
+
+ // optional bool cOpaque = 15;
+ inline bool has_copaque() const;
+ inline void clear_copaque();
+ static const int kCOpaqueFieldNumber = 15;
+ inline bool copaque() const;
+ inline void set_copaque(bool value);
+
+ // optional bool cAlpha = 16;
+ inline bool has_calpha() const;
+ inline void clear_calpha();
+ static const int kCAlphaFieldNumber = 16;
+ inline bool calpha() const;
+ inline void set_calpha(bool value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ inline bool has_direct() const;
+ inline void clear_direct();
+ static const int kDirectFieldNumber = 17;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect direct() const;
+ inline void set_direct(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect value);
+
+ // optional uint64 barID = 18;
+ inline bool has_barid() const;
+ inline void clear_barid();
+ static const int kBarIDFieldNumber = 18;
+ inline ::google::protobuf::uint64 barid() const;
+ inline void set_barid(::google::protobuf::uint64 value);
+
+ // optional uint64 mask = 19;
+ inline bool has_mask() const;
+ inline void clear_mask();
+ static const int kMaskFieldNumber = 19;
+ inline ::google::protobuf::uint64 mask() const;
+ inline void set_mask(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ inline bool has_hitregion() const;
+ inline void clear_hitregion();
+ static const int kHitRegionFieldNumber = 20;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& hitregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_hitregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_hitregion();
+ inline void set_allocated_hitregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ inline bool has_dispatchregion() const;
+ inline void clear_dispatchregion();
+ static const int kDispatchRegionFieldNumber = 21;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& dispatchregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_dispatchregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_dispatchregion();
+ inline void set_allocated_dispatchregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ inline bool has_noactionregion() const;
+ inline void clear_noactionregion();
+ static const int kNoActionRegionFieldNumber = 22;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& noactionregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_noactionregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_noactionregion();
+ inline void set_allocated_noactionregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ inline bool has_hpanregion() const;
+ inline void clear_hpanregion();
+ static const int kHPanRegionFieldNumber = 23;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& hpanregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_hpanregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_hpanregion();
+ inline void set_allocated_hpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ inline bool has_vpanregion() const;
+ inline void clear_vpanregion();
+ static const int kVPanRegionFieldNumber = 24;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vpanregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vpanregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vpanregion();
+ inline void set_allocated_vpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ inline bool has_valid() const;
+ inline void clear_valid();
+ static const int kValidFieldNumber = 100;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& valid() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_valid();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_valid();
+ inline void set_allocated_valid(::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid);
+
+ // optional uint32 color = 101;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 101;
+ inline ::google::protobuf::uint32 color() const;
+ inline void set_color(::google::protobuf::uint32 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ inline bool has_filter() const;
+ inline void clear_filter();
+ static const int kFilterFieldNumber = 102;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Filter filter() const;
+ inline void set_filter(::mozilla::layers::layerscope::LayersPacket_Layer_Filter value);
+
+ // optional uint64 refID = 103;
+ inline bool has_refid() const;
+ inline void clear_refid();
+ static const int kRefIDFieldNumber = 103;
+ inline ::google::protobuf::uint64 refid() const;
+ inline void set_refid(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ inline bool has_size() const;
+ inline void clear_size();
+ static const int kSizeFieldNumber = 104;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Size& size() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* mutable_size();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* release_size();
+ inline void set_allocated_size(::mozilla::layers::layerscope::LayersPacket_Layer_Size* size);
+
+ // optional uint32 displayListLogLength = 105;
+ inline bool has_displaylistloglength() const;
+ inline void clear_displaylistloglength();
+ static const int kDisplayListLogLengthFieldNumber = 105;
+ inline ::google::protobuf::uint32 displaylistloglength() const;
+ inline void set_displaylistloglength(::google::protobuf::uint32 value);
+
+ // optional bytes displayListLog = 106;
+ inline bool has_displaylistlog() const;
+ inline void clear_displaylistlog();
+ static const int kDisplayListLogFieldNumber = 106;
+ inline const ::std::string& displaylistlog() const;
+ inline void set_displaylistlog(const ::std::string& value);
+ inline void set_displaylistlog(const char* value);
+ inline void set_displaylistlog(const void* value, size_t size);
+ inline ::std::string* mutable_displaylistlog();
+ inline ::std::string* release_displaylistlog();
+ inline void set_allocated_displaylistlog(::std::string* displaylistlog);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_ptr();
+ inline void clear_has_ptr();
+ inline void set_has_parentptr();
+ inline void clear_has_parentptr();
+ inline void set_has_clip();
+ inline void clear_has_clip();
+ inline void set_has_transform();
+ inline void clear_has_transform();
+ inline void set_has_vregion();
+ inline void clear_has_vregion();
+ inline void set_has_shadow();
+ inline void clear_has_shadow();
+ inline void set_has_opacity();
+ inline void clear_has_opacity();
+ inline void set_has_copaque();
+ inline void clear_has_copaque();
+ inline void set_has_calpha();
+ inline void clear_has_calpha();
+ inline void set_has_direct();
+ inline void clear_has_direct();
+ inline void set_has_barid();
+ inline void clear_has_barid();
+ inline void set_has_mask();
+ inline void clear_has_mask();
+ inline void set_has_hitregion();
+ inline void clear_has_hitregion();
+ inline void set_has_dispatchregion();
+ inline void clear_has_dispatchregion();
+ inline void set_has_noactionregion();
+ inline void clear_has_noactionregion();
+ inline void set_has_hpanregion();
+ inline void clear_has_hpanregion();
+ inline void set_has_vpanregion();
+ inline void clear_has_vpanregion();
+ inline void set_has_valid();
+ inline void clear_has_valid();
+ inline void set_has_color();
+ inline void clear_has_color();
+ inline void set_has_filter();
+ inline void clear_has_filter();
+ inline void set_has_refid();
+ inline void clear_has_refid();
+ inline void set_has_size();
+ inline void clear_has_size();
+ inline void set_has_displaylistloglength();
+ inline void clear_has_displaylistloglength();
+ inline void set_has_displaylistlog();
+ inline void clear_has_displaylistlog();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 ptr_;
+ ::google::protobuf::uint64 parentptr_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip_;
+ int type_;
+ float opacity_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow_;
+ bool copaque_;
+ bool calpha_;
+ int direct_;
+ ::google::protobuf::uint64 barid_;
+ ::google::protobuf::uint64 mask_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid_;
+ ::google::protobuf::uint32 color_;
+ int filter_;
+ ::google::protobuf::uint64 refid_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size* size_;
+ ::std::string* displaylistlog_;
+ ::google::protobuf::uint32 displaylistloglength_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket();
+ virtual ~LayersPacket();
+
+ LayersPacket(const LayersPacket& from);
+
+ inline LayersPacket& operator=(const LayersPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket& from);
+ void MergeFrom(const LayersPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef LayersPacket_Layer Layer;
+
+ // accessors -------------------------------------------------------
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ inline int layer_size() const;
+ inline void clear_layer();
+ static const int kLayerFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer& layer(int index) const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer* mutable_layer(int index);
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer* add_layer();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >&
+ layer() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >*
+ mutable_layer();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket)
+ private:
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer > layer_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class MetaPacket : public ::google::protobuf::MessageLite {
+ public:
+ MetaPacket();
+ virtual ~MetaPacket();
+
+ MetaPacket(const MetaPacket& from);
+
+ inline MetaPacket& operator=(const MetaPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const MetaPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const MetaPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(MetaPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ MetaPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const MetaPacket& from);
+ void MergeFrom(const MetaPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool composedByHwc = 1;
+ inline bool has_composedbyhwc() const;
+ inline void clear_composedbyhwc();
+ static const int kComposedByHwcFieldNumber = 1;
+ inline bool composedbyhwc() const;
+ inline void set_composedbyhwc(bool value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.MetaPacket)
+ private:
+ inline void set_has_composedbyhwc();
+ inline void clear_has_composedbyhwc();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ bool composedbyhwc_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static MetaPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class DrawPacket_Rect : public ::google::protobuf::MessageLite {
+ public:
+ DrawPacket_Rect();
+ virtual ~DrawPacket_Rect();
+
+ DrawPacket_Rect(const DrawPacket_Rect& from);
+
+ inline DrawPacket_Rect& operator=(const DrawPacket_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const DrawPacket_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const DrawPacket_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(DrawPacket_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ DrawPacket_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const DrawPacket_Rect& from);
+ void MergeFrom(const DrawPacket_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // required float x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline float x() const;
+ inline void set_x(float value);
+
+ // required float y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline float y() const;
+ inline void set_y(float value);
+
+ // required float w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline float w() const;
+ inline void set_w(float value);
+
+ // required float h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline float h() const;
+ inline void set_h(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.DrawPacket.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float x_;
+ float y_;
+ float w_;
+ float h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static DrawPacket_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class DrawPacket : public ::google::protobuf::MessageLite {
+ public:
+ DrawPacket();
+ virtual ~DrawPacket();
+
+ DrawPacket(const DrawPacket& from);
+
+ inline DrawPacket& operator=(const DrawPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const DrawPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const DrawPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(DrawPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ DrawPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const DrawPacket& from);
+ void MergeFrom(const DrawPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef DrawPacket_Rect Rect;
+
+ // accessors -------------------------------------------------------
+
+ // required float offsetX = 1;
+ inline bool has_offsetx() const;
+ inline void clear_offsetx();
+ static const int kOffsetXFieldNumber = 1;
+ inline float offsetx() const;
+ inline void set_offsetx(float value);
+
+ // required float offsetY = 2;
+ inline bool has_offsety() const;
+ inline void clear_offsety();
+ static const int kOffsetYFieldNumber = 2;
+ inline float offsety() const;
+ inline void set_offsety(float value);
+
+ // repeated float mvMatrix = 3;
+ inline int mvmatrix_size() const;
+ inline void clear_mvmatrix();
+ static const int kMvMatrixFieldNumber = 3;
+ inline float mvmatrix(int index) const;
+ inline void set_mvmatrix(int index, float value);
+ inline void add_mvmatrix(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ mvmatrix() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_mvmatrix();
+
+ // required uint32 totalRects = 4;
+ inline bool has_totalrects() const;
+ inline void clear_totalrects();
+ static const int kTotalRectsFieldNumber = 4;
+ inline ::google::protobuf::uint32 totalrects() const;
+ inline void set_totalrects(::google::protobuf::uint32 value);
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ inline int layerrect_size() const;
+ inline void clear_layerrect();
+ static const int kLayerRectFieldNumber = 5;
+ inline const ::mozilla::layers::layerscope::DrawPacket_Rect& layerrect(int index) const;
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* mutable_layerrect(int index);
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* add_layerrect();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+ layerrect() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+ mutable_layerrect();
+
+ // required uint64 layerref = 6;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 6;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // repeated uint32 texIDs = 7;
+ inline int texids_size() const;
+ inline void clear_texids();
+ static const int kTexIDsFieldNumber = 7;
+ inline ::google::protobuf::uint32 texids(int index) const;
+ inline void set_texids(int index, ::google::protobuf::uint32 value);
+ inline void add_texids(::google::protobuf::uint32 value);
+ inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+ texids() const;
+ inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+ mutable_texids();
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ inline int texturerect_size() const;
+ inline void clear_texturerect();
+ static const int kTextureRectFieldNumber = 8;
+ inline const ::mozilla::layers::layerscope::DrawPacket_Rect& texturerect(int index) const;
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* mutable_texturerect(int index);
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* add_texturerect();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+ texturerect() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+ mutable_texturerect();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.DrawPacket)
+ private:
+ inline void set_has_offsetx();
+ inline void clear_has_offsetx();
+ inline void set_has_offsety();
+ inline void clear_has_offsety();
+ inline void set_has_totalrects();
+ inline void clear_has_totalrects();
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float offsetx_;
+ float offsety_;
+ ::google::protobuf::RepeatedField< float > mvmatrix_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect > layerrect_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > texids_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect > texturerect_;
+ ::google::protobuf::uint32 totalrects_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static DrawPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class Packet : public ::google::protobuf::MessageLite {
+ public:
+ Packet();
+ virtual ~Packet();
+
+ Packet(const Packet& from);
+
+ inline Packet& operator=(const Packet& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const Packet& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const Packet* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(Packet* other);
+
+ // implements Message ----------------------------------------------
+
+ Packet* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const Packet& from);
+ void MergeFrom(const Packet& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef Packet_DataType DataType;
+ static const DataType FRAMESTART = Packet_DataType_FRAMESTART;
+ static const DataType FRAMEEND = Packet_DataType_FRAMEEND;
+ static const DataType COLOR = Packet_DataType_COLOR;
+ static const DataType TEXTURE = Packet_DataType_TEXTURE;
+ static const DataType LAYERS = Packet_DataType_LAYERS;
+ static const DataType META = Packet_DataType_META;
+ static const DataType DRAW = Packet_DataType_DRAW;
+ static inline bool DataType_IsValid(int value) {
+ return Packet_DataType_IsValid(value);
+ }
+ static const DataType DataType_MIN =
+ Packet_DataType_DataType_MIN;
+ static const DataType DataType_MAX =
+ Packet_DataType_DataType_MAX;
+ static const int DataType_ARRAYSIZE =
+ Packet_DataType_DataType_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::Packet_DataType type() const;
+ inline void set_type(::mozilla::layers::layerscope::Packet_DataType value);
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ inline bool has_frame() const;
+ inline void clear_frame();
+ static const int kFrameFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::FramePacket& frame() const;
+ inline ::mozilla::layers::layerscope::FramePacket* mutable_frame();
+ inline ::mozilla::layers::layerscope::FramePacket* release_frame();
+ inline void set_allocated_frame(::mozilla::layers::layerscope::FramePacket* frame);
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::ColorPacket& color() const;
+ inline ::mozilla::layers::layerscope::ColorPacket* mutable_color();
+ inline ::mozilla::layers::layerscope::ColorPacket* release_color();
+ inline void set_allocated_color(::mozilla::layers::layerscope::ColorPacket* color);
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ inline bool has_texture() const;
+ inline void clear_texture();
+ static const int kTextureFieldNumber = 4;
+ inline const ::mozilla::layers::layerscope::TexturePacket& texture() const;
+ inline ::mozilla::layers::layerscope::TexturePacket* mutable_texture();
+ inline ::mozilla::layers::layerscope::TexturePacket* release_texture();
+ inline void set_allocated_texture(::mozilla::layers::layerscope::TexturePacket* texture);
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ inline bool has_layers() const;
+ inline void clear_layers();
+ static const int kLayersFieldNumber = 5;
+ inline const ::mozilla::layers::layerscope::LayersPacket& layers() const;
+ inline ::mozilla::layers::layerscope::LayersPacket* mutable_layers();
+ inline ::mozilla::layers::layerscope::LayersPacket* release_layers();
+ inline void set_allocated_layers(::mozilla::layers::layerscope::LayersPacket* layers);
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ inline bool has_meta() const;
+ inline void clear_meta();
+ static const int kMetaFieldNumber = 6;
+ inline const ::mozilla::layers::layerscope::MetaPacket& meta() const;
+ inline ::mozilla::layers::layerscope::MetaPacket* mutable_meta();
+ inline ::mozilla::layers::layerscope::MetaPacket* release_meta();
+ inline void set_allocated_meta(::mozilla::layers::layerscope::MetaPacket* meta);
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ inline bool has_draw() const;
+ inline void clear_draw();
+ static const int kDrawFieldNumber = 7;
+ inline const ::mozilla::layers::layerscope::DrawPacket& draw() const;
+ inline ::mozilla::layers::layerscope::DrawPacket* mutable_draw();
+ inline ::mozilla::layers::layerscope::DrawPacket* release_draw();
+ inline void set_allocated_draw(::mozilla::layers::layerscope::DrawPacket* draw);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.Packet)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_frame();
+ inline void clear_has_frame();
+ inline void set_has_color();
+ inline void clear_has_color();
+ inline void set_has_texture();
+ inline void clear_has_texture();
+ inline void set_has_layers();
+ inline void clear_has_layers();
+ inline void set_has_meta();
+ inline void clear_has_meta();
+ inline void set_has_draw();
+ inline void clear_has_draw();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::FramePacket* frame_;
+ ::mozilla::layers::layerscope::ColorPacket* color_;
+ ::mozilla::layers::layerscope::TexturePacket* texture_;
+ ::mozilla::layers::layerscope::LayersPacket* layers_;
+ ::mozilla::layers::layerscope::MetaPacket* meta_;
+ ::mozilla::layers::layerscope::DrawPacket* draw_;
+ int type_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static Packet* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class CommandPacket : public ::google::protobuf::MessageLite {
+ public:
+ CommandPacket();
+ virtual ~CommandPacket();
+
+ CommandPacket(const CommandPacket& from);
+
+ inline CommandPacket& operator=(const CommandPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const CommandPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const CommandPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(CommandPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ CommandPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const CommandPacket& from);
+ void MergeFrom(const CommandPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef CommandPacket_CmdType CmdType;
+ static const CmdType NO_OP = CommandPacket_CmdType_NO_OP;
+ static const CmdType LAYERS_TREE = CommandPacket_CmdType_LAYERS_TREE;
+ static const CmdType LAYERS_BUFFER = CommandPacket_CmdType_LAYERS_BUFFER;
+ static inline bool CmdType_IsValid(int value) {
+ return CommandPacket_CmdType_IsValid(value);
+ }
+ static const CmdType CmdType_MIN =
+ CommandPacket_CmdType_CmdType_MIN;
+ static const CmdType CmdType_MAX =
+ CommandPacket_CmdType_CmdType_MAX;
+ static const int CmdType_ARRAYSIZE =
+ CommandPacket_CmdType_CmdType_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::CommandPacket_CmdType type() const;
+ inline void set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value);
+
+ // optional bool value = 2;
+ inline bool has_value() const;
+ inline void clear_value();
+ static const int kValueFieldNumber = 2;
+ inline bool value() const;
+ inline void set_value(bool value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.CommandPacket)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_value();
+ inline void clear_has_value();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ int type_;
+ bool value_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static CommandPacket* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+// FramePacket
+
+// optional uint64 value = 1;
+inline bool FramePacket::has_value() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void FramePacket::set_has_value() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void FramePacket::clear_has_value() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void FramePacket::clear_value() {
+ value_ = GOOGLE_ULONGLONG(0);
+ clear_has_value();
+}
+inline ::google::protobuf::uint64 FramePacket::value() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.FramePacket.value)
+ return value_;
+}
+inline void FramePacket::set_value(::google::protobuf::uint64 value) {
+ set_has_value();
+ value_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.FramePacket.value)
+}
+
+// optional float scale = 2;
+inline bool FramePacket::has_scale() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void FramePacket::set_has_scale() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void FramePacket::clear_has_scale() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void FramePacket::clear_scale() {
+ scale_ = 0;
+ clear_has_scale();
+}
+inline float FramePacket::scale() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.FramePacket.scale)
+ return scale_;
+}
+inline void FramePacket::set_scale(float value) {
+ set_has_scale();
+ scale_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.FramePacket.scale)
+}
+
+// -------------------------------------------------------------------
+
+// ColorPacket
+
+// required uint64 layerref = 1;
+inline bool ColorPacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void ColorPacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void ColorPacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void ColorPacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 ColorPacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.layerref)
+ return layerref_;
+}
+inline void ColorPacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.layerref)
+}
+
+// optional uint32 width = 2;
+inline bool ColorPacket::has_width() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void ColorPacket::set_has_width() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void ColorPacket::clear_has_width() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void ColorPacket::clear_width() {
+ width_ = 0u;
+ clear_has_width();
+}
+inline ::google::protobuf::uint32 ColorPacket::width() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.width)
+ return width_;
+}
+inline void ColorPacket::set_width(::google::protobuf::uint32 value) {
+ set_has_width();
+ width_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.width)
+}
+
+// optional uint32 height = 3;
+inline bool ColorPacket::has_height() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void ColorPacket::set_has_height() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void ColorPacket::clear_has_height() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void ColorPacket::clear_height() {
+ height_ = 0u;
+ clear_has_height();
+}
+inline ::google::protobuf::uint32 ColorPacket::height() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.height)
+ return height_;
+}
+inline void ColorPacket::set_height(::google::protobuf::uint32 value) {
+ set_has_height();
+ height_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.height)
+}
+
+// optional uint32 color = 4;
+inline bool ColorPacket::has_color() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void ColorPacket::set_has_color() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void ColorPacket::clear_has_color() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void ColorPacket::clear_color() {
+ color_ = 0u;
+ clear_has_color();
+}
+inline ::google::protobuf::uint32 ColorPacket::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.color)
+ return color_;
+}
+inline void ColorPacket::set_color(::google::protobuf::uint32 value) {
+ set_has_color();
+ color_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.color)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Rect
+
+// optional float x = 1;
+inline bool TexturePacket_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline float TexturePacket_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.x)
+ return x_;
+}
+inline void TexturePacket_Rect::set_x(float value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.x)
+}
+
+// optional float y = 2;
+inline bool TexturePacket_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline float TexturePacket_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.y)
+ return y_;
+}
+inline void TexturePacket_Rect::set_y(float value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.y)
+}
+
+// optional float w = 3;
+inline bool TexturePacket_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline float TexturePacket_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.w)
+ return w_;
+}
+inline void TexturePacket_Rect::set_w(float value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.w)
+}
+
+// optional float h = 4;
+inline bool TexturePacket_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void TexturePacket_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void TexturePacket_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void TexturePacket_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline float TexturePacket_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.h)
+ return h_;
+}
+inline void TexturePacket_Rect::set_h(float value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Size
+
+// optional int32 w = 1;
+inline bool TexturePacket_Size::has_w() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Size::set_has_w() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Size::clear_has_w() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Size::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 TexturePacket_Size::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Size.w)
+ return w_;
+}
+inline void TexturePacket_Size::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Size.w)
+}
+
+// optional int32 h = 2;
+inline bool TexturePacket_Size::has_h() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Size::set_has_h() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Size::clear_has_h() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Size::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 TexturePacket_Size::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Size.h)
+ return h_;
+}
+inline void TexturePacket_Size::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Size.h)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Matrix
+
+// optional bool is2D = 1;
+inline bool TexturePacket_Matrix::has_is2d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Matrix::set_has_is2d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Matrix::clear_has_is2d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Matrix::clear_is2d() {
+ is2d_ = false;
+ clear_has_is2d();
+}
+inline bool TexturePacket_Matrix::is2d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.is2D)
+ return is2d_;
+}
+inline void TexturePacket_Matrix::set_is2d(bool value) {
+ set_has_is2d();
+ is2d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.is2D)
+}
+
+// optional bool isId = 2;
+inline bool TexturePacket_Matrix::has_isid() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Matrix::set_has_isid() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Matrix::clear_has_isid() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Matrix::clear_isid() {
+ isid_ = false;
+ clear_has_isid();
+}
+inline bool TexturePacket_Matrix::isid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.isId)
+ return isid_;
+}
+inline void TexturePacket_Matrix::set_isid(bool value) {
+ set_has_isid();
+ isid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.isId)
+}
+
+// repeated float m = 3;
+inline int TexturePacket_Matrix::m_size() const {
+ return m_.size();
+}
+inline void TexturePacket_Matrix::clear_m() {
+ m_.Clear();
+}
+inline float TexturePacket_Matrix::m(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return m_.Get(index);
+}
+inline void TexturePacket_Matrix::set_m(int index, float value) {
+ m_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+}
+inline void TexturePacket_Matrix::add_m(float value) {
+ m_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+TexturePacket_Matrix::m() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return m_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+TexturePacket_Matrix::mutable_m() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return &m_;
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_EffectMask
+
+// optional bool mIs3D = 1;
+inline bool TexturePacket_EffectMask::has_mis3d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_mis3d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_EffectMask::clear_has_mis3d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_EffectMask::clear_mis3d() {
+ mis3d_ = false;
+ clear_has_mis3d();
+}
+inline bool TexturePacket_EffectMask::mis3d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mIs3D)
+ return mis3d_;
+}
+inline void TexturePacket_EffectMask::set_mis3d(bool value) {
+ set_has_mis3d();
+ mis3d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.EffectMask.mIs3D)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+inline bool TexturePacket_EffectMask::has_msize() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_msize() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_EffectMask::clear_has_msize() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_EffectMask::clear_msize() {
+ if (msize_ != NULL) msize_->::mozilla::layers::layerscope::TexturePacket_Size::Clear();
+ clear_has_msize();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Size& TexturePacket_EffectMask::msize() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return msize_ != NULL ? *msize_ : *default_instance().msize_;
+#else
+ return msize_ != NULL ? *msize_ : *default_instance_->msize_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Size* TexturePacket_EffectMask::mutable_msize() {
+ set_has_msize();
+ if (msize_ == NULL) msize_ = new ::mozilla::layers::layerscope::TexturePacket_Size;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+ return msize_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Size* TexturePacket_EffectMask::release_msize() {
+ clear_has_msize();
+ ::mozilla::layers::layerscope::TexturePacket_Size* temp = msize_;
+ msize_ = NULL;
+ return temp;
+}
+inline void TexturePacket_EffectMask::set_allocated_msize(::mozilla::layers::layerscope::TexturePacket_Size* msize) {
+ delete msize_;
+ msize_ = msize;
+ if (msize) {
+ set_has_msize();
+ } else {
+ clear_has_msize();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+inline bool TexturePacket_EffectMask::has_mmasktransform() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_mmasktransform() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket_EffectMask::clear_has_mmasktransform() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket_EffectMask::clear_mmasktransform() {
+ if (mmasktransform_ != NULL) mmasktransform_->::mozilla::layers::layerscope::TexturePacket_Matrix::Clear();
+ clear_has_mmasktransform();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Matrix& TexturePacket_EffectMask::mmasktransform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mmasktransform_ != NULL ? *mmasktransform_ : *default_instance().mmasktransform_;
+#else
+ return mmasktransform_ != NULL ? *mmasktransform_ : *default_instance_->mmasktransform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Matrix* TexturePacket_EffectMask::mutable_mmasktransform() {
+ set_has_mmasktransform();
+ if (mmasktransform_ == NULL) mmasktransform_ = new ::mozilla::layers::layerscope::TexturePacket_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+ return mmasktransform_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Matrix* TexturePacket_EffectMask::release_mmasktransform() {
+ clear_has_mmasktransform();
+ ::mozilla::layers::layerscope::TexturePacket_Matrix* temp = mmasktransform_;
+ mmasktransform_ = NULL;
+ return temp;
+}
+inline void TexturePacket_EffectMask::set_allocated_mmasktransform(::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform) {
+ delete mmasktransform_;
+ mmasktransform_ = mmasktransform;
+ if (mmasktransform) {
+ set_has_mmasktransform();
+ } else {
+ clear_has_mmasktransform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket
+
+// required uint64 layerref = 1;
+inline bool TexturePacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 TexturePacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.layerref)
+ return layerref_;
+}
+inline void TexturePacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.layerref)
+}
+
+// optional uint32 width = 2;
+inline bool TexturePacket::has_width() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket::set_has_width() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket::clear_has_width() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket::clear_width() {
+ width_ = 0u;
+ clear_has_width();
+}
+inline ::google::protobuf::uint32 TexturePacket::width() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.width)
+ return width_;
+}
+inline void TexturePacket::set_width(::google::protobuf::uint32 value) {
+ set_has_width();
+ width_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.width)
+}
+
+// optional uint32 height = 3;
+inline bool TexturePacket::has_height() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket::set_has_height() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket::clear_has_height() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket::clear_height() {
+ height_ = 0u;
+ clear_has_height();
+}
+inline ::google::protobuf::uint32 TexturePacket::height() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.height)
+ return height_;
+}
+inline void TexturePacket::set_height(::google::protobuf::uint32 value) {
+ set_has_height();
+ height_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.height)
+}
+
+// optional uint32 stride = 4;
+inline bool TexturePacket::has_stride() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void TexturePacket::set_has_stride() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void TexturePacket::clear_has_stride() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void TexturePacket::clear_stride() {
+ stride_ = 0u;
+ clear_has_stride();
+}
+inline ::google::protobuf::uint32 TexturePacket::stride() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.stride)
+ return stride_;
+}
+inline void TexturePacket::set_stride(::google::protobuf::uint32 value) {
+ set_has_stride();
+ stride_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.stride)
+}
+
+// optional uint32 name = 5;
+inline bool TexturePacket::has_name() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void TexturePacket::set_has_name() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void TexturePacket::clear_has_name() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void TexturePacket::clear_name() {
+ name_ = 0u;
+ clear_has_name();
+}
+inline ::google::protobuf::uint32 TexturePacket::name() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.name)
+ return name_;
+}
+inline void TexturePacket::set_name(::google::protobuf::uint32 value) {
+ set_has_name();
+ name_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.name)
+}
+
+// optional uint32 target = 6;
+inline bool TexturePacket::has_target() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void TexturePacket::set_has_target() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void TexturePacket::clear_has_target() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void TexturePacket::clear_target() {
+ target_ = 0u;
+ clear_has_target();
+}
+inline ::google::protobuf::uint32 TexturePacket::target() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.target)
+ return target_;
+}
+inline void TexturePacket::set_target(::google::protobuf::uint32 value) {
+ set_has_target();
+ target_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.target)
+}
+
+// optional uint32 dataformat = 7;
+inline bool TexturePacket::has_dataformat() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void TexturePacket::set_has_dataformat() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void TexturePacket::clear_has_dataformat() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void TexturePacket::clear_dataformat() {
+ dataformat_ = 0u;
+ clear_has_dataformat();
+}
+inline ::google::protobuf::uint32 TexturePacket::dataformat() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.dataformat)
+ return dataformat_;
+}
+inline void TexturePacket::set_dataformat(::google::protobuf::uint32 value) {
+ set_has_dataformat();
+ dataformat_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.dataformat)
+}
+
+// optional uint64 glcontext = 8;
+inline bool TexturePacket::has_glcontext() const {
+ return (_has_bits_[0] & 0x00000080u) != 0;
+}
+inline void TexturePacket::set_has_glcontext() {
+ _has_bits_[0] |= 0x00000080u;
+}
+inline void TexturePacket::clear_has_glcontext() {
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline void TexturePacket::clear_glcontext() {
+ glcontext_ = GOOGLE_ULONGLONG(0);
+ clear_has_glcontext();
+}
+inline ::google::protobuf::uint64 TexturePacket::glcontext() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.glcontext)
+ return glcontext_;
+}
+inline void TexturePacket::set_glcontext(::google::protobuf::uint64 value) {
+ set_has_glcontext();
+ glcontext_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.glcontext)
+}
+
+// optional bytes data = 9;
+inline bool TexturePacket::has_data() const {
+ return (_has_bits_[0] & 0x00000100u) != 0;
+}
+inline void TexturePacket::set_has_data() {
+ _has_bits_[0] |= 0x00000100u;
+}
+inline void TexturePacket::clear_has_data() {
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline void TexturePacket::clear_data() {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_->clear();
+ }
+ clear_has_data();
+}
+inline const ::std::string& TexturePacket::data() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.data)
+ return *data_;
+}
+inline void TexturePacket::set_data(const ::std::string& value) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline void TexturePacket::set_data(const char* value) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(value);
+ // @@protoc_insertion_point(field_set_char:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline void TexturePacket::set_data(const void* value, size_t size) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline ::std::string* TexturePacket::mutable_data() {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.data)
+ return data_;
+}
+inline ::std::string* TexturePacket::release_data() {
+ clear_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = data_;
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void TexturePacket::set_allocated_data(::std::string* data) {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete data_;
+ }
+ if (data) {
+ set_has_data();
+ data_ = data;
+ } else {
+ clear_has_data();
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.data)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+inline bool TexturePacket::has_mtexturecoords() const {
+ return (_has_bits_[0] & 0x00000200u) != 0;
+}
+inline void TexturePacket::set_has_mtexturecoords() {
+ _has_bits_[0] |= 0x00000200u;
+}
+inline void TexturePacket::clear_has_mtexturecoords() {
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline void TexturePacket::clear_mtexturecoords() {
+ if (mtexturecoords_ != NULL) mtexturecoords_->::mozilla::layers::layerscope::TexturePacket_Rect::Clear();
+ clear_has_mtexturecoords();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Rect& TexturePacket::mtexturecoords() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mtexturecoords_ != NULL ? *mtexturecoords_ : *default_instance().mtexturecoords_;
+#else
+ return mtexturecoords_ != NULL ? *mtexturecoords_ : *default_instance_->mtexturecoords_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Rect* TexturePacket::mutable_mtexturecoords() {
+ set_has_mtexturecoords();
+ if (mtexturecoords_ == NULL) mtexturecoords_ = new ::mozilla::layers::layerscope::TexturePacket_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+ return mtexturecoords_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Rect* TexturePacket::release_mtexturecoords() {
+ clear_has_mtexturecoords();
+ ::mozilla::layers::layerscope::TexturePacket_Rect* temp = mtexturecoords_;
+ mtexturecoords_ = NULL;
+ return temp;
+}
+inline void TexturePacket::set_allocated_mtexturecoords(::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords) {
+ delete mtexturecoords_;
+ mtexturecoords_ = mtexturecoords;
+ if (mtexturecoords) {
+ set_has_mtexturecoords();
+ } else {
+ clear_has_mtexturecoords();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+}
+
+// optional bool mPremultiplied = 11;
+inline bool TexturePacket::has_mpremultiplied() const {
+ return (_has_bits_[0] & 0x00000400u) != 0;
+}
+inline void TexturePacket::set_has_mpremultiplied() {
+ _has_bits_[0] |= 0x00000400u;
+}
+inline void TexturePacket::clear_has_mpremultiplied() {
+ _has_bits_[0] &= ~0x00000400u;
+}
+inline void TexturePacket::clear_mpremultiplied() {
+ mpremultiplied_ = false;
+ clear_has_mpremultiplied();
+}
+inline bool TexturePacket::mpremultiplied() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mPremultiplied)
+ return mpremultiplied_;
+}
+inline void TexturePacket::set_mpremultiplied(bool value) {
+ set_has_mpremultiplied();
+ mpremultiplied_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.mPremultiplied)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+inline bool TexturePacket::has_mfilter() const {
+ return (_has_bits_[0] & 0x00000800u) != 0;
+}
+inline void TexturePacket::set_has_mfilter() {
+ _has_bits_[0] |= 0x00000800u;
+}
+inline void TexturePacket::clear_has_mfilter() {
+ _has_bits_[0] &= ~0x00000800u;
+}
+inline void TexturePacket::clear_mfilter() {
+ mfilter_ = 0;
+ clear_has_mfilter();
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Filter TexturePacket::mfilter() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mFilter)
+ return static_cast< ::mozilla::layers::layerscope::TexturePacket_Filter >(mfilter_);
+}
+inline void TexturePacket::set_mfilter(::mozilla::layers::layerscope::TexturePacket_Filter value) {
+ assert(::mozilla::layers::layerscope::TexturePacket_Filter_IsValid(value));
+ set_has_mfilter();
+ mfilter_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.mFilter)
+}
+
+// optional bool isMask = 20;
+inline bool TexturePacket::has_ismask() const {
+ return (_has_bits_[0] & 0x00001000u) != 0;
+}
+inline void TexturePacket::set_has_ismask() {
+ _has_bits_[0] |= 0x00001000u;
+}
+inline void TexturePacket::clear_has_ismask() {
+ _has_bits_[0] &= ~0x00001000u;
+}
+inline void TexturePacket::clear_ismask() {
+ ismask_ = false;
+ clear_has_ismask();
+}
+inline bool TexturePacket::ismask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.isMask)
+ return ismask_;
+}
+inline void TexturePacket::set_ismask(bool value) {
+ set_has_ismask();
+ ismask_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.isMask)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+inline bool TexturePacket::has_mask() const {
+ return (_has_bits_[0] & 0x00002000u) != 0;
+}
+inline void TexturePacket::set_has_mask() {
+ _has_bits_[0] |= 0x00002000u;
+}
+inline void TexturePacket::clear_has_mask() {
+ _has_bits_[0] &= ~0x00002000u;
+}
+inline void TexturePacket::clear_mask() {
+ if (mask_ != NULL) mask_->::mozilla::layers::layerscope::TexturePacket_EffectMask::Clear();
+ clear_has_mask();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_EffectMask& TexturePacket::mask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mask)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mask_ != NULL ? *mask_ : *default_instance().mask_;
+#else
+ return mask_ != NULL ? *mask_ : *default_instance_->mask_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* TexturePacket::mutable_mask() {
+ set_has_mask();
+ if (mask_ == NULL) mask_ = new ::mozilla::layers::layerscope::TexturePacket_EffectMask;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.mask)
+ return mask_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* TexturePacket::release_mask() {
+ clear_has_mask();
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask* temp = mask_;
+ mask_ = NULL;
+ return temp;
+}
+inline void TexturePacket::set_allocated_mask(::mozilla::layers::layerscope::TexturePacket_EffectMask* mask) {
+ delete mask_;
+ mask_ = mask;
+ if (mask) {
+ set_has_mask();
+ } else {
+ clear_has_mask();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.mask)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Size
+
+// optional int32 w = 1;
+inline bool LayersPacket_Layer_Size::has_w() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Size::set_has_w() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Size::clear_has_w() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Size::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Size::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Size.w)
+ return w_;
+}
+inline void LayersPacket_Layer_Size::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Size.w)
+}
+
+// optional int32 h = 2;
+inline bool LayersPacket_Layer_Size::has_h() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Size::set_has_h() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Size::clear_has_h() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Size::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Size::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Size.h)
+ return h_;
+}
+inline void LayersPacket_Layer_Size::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Size.h)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Rect
+
+// optional int32 x = 1;
+inline bool LayersPacket_Layer_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.x)
+ return x_;
+}
+inline void LayersPacket_Layer_Rect::set_x(::google::protobuf::int32 value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.x)
+}
+
+// optional int32 y = 2;
+inline bool LayersPacket_Layer_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.y)
+ return y_;
+}
+inline void LayersPacket_Layer_Rect::set_y(::google::protobuf::int32 value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.y)
+}
+
+// optional int32 w = 3;
+inline bool LayersPacket_Layer_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.w)
+ return w_;
+}
+inline void LayersPacket_Layer_Rect::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.w)
+}
+
+// optional int32 h = 4;
+inline bool LayersPacket_Layer_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void LayersPacket_Layer_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.h)
+ return h_;
+}
+inline void LayersPacket_Layer_Rect::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Region
+
+// repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+inline int LayersPacket_Layer_Region::r_size() const {
+ return r_.size();
+}
+inline void LayersPacket_Layer_Region::clear_r() {
+ r_.Clear();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer_Region::r(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Get(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Region::mutable_r(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Region::add_r() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >&
+LayersPacket_Layer_Region::r() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >*
+LayersPacket_Layer_Region::mutable_r() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return &r_;
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Matrix
+
+// optional bool is2D = 1;
+inline bool LayersPacket_Layer_Matrix::has_is2d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Matrix::set_has_is2d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Matrix::clear_has_is2d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Matrix::clear_is2d() {
+ is2d_ = false;
+ clear_has_is2d();
+}
+inline bool LayersPacket_Layer_Matrix::is2d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.is2D)
+ return is2d_;
+}
+inline void LayersPacket_Layer_Matrix::set_is2d(bool value) {
+ set_has_is2d();
+ is2d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.is2D)
+}
+
+// optional bool isId = 2;
+inline bool LayersPacket_Layer_Matrix::has_isid() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Matrix::set_has_isid() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Matrix::clear_has_isid() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Matrix::clear_isid() {
+ isid_ = false;
+ clear_has_isid();
+}
+inline bool LayersPacket_Layer_Matrix::isid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.isId)
+ return isid_;
+}
+inline void LayersPacket_Layer_Matrix::set_isid(bool value) {
+ set_has_isid();
+ isid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.isId)
+}
+
+// repeated float m = 3;
+inline int LayersPacket_Layer_Matrix::m_size() const {
+ return m_.size();
+}
+inline void LayersPacket_Layer_Matrix::clear_m() {
+ m_.Clear();
+}
+inline float LayersPacket_Layer_Matrix::m(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return m_.Get(index);
+}
+inline void LayersPacket_Layer_Matrix::set_m(int index, float value) {
+ m_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+}
+inline void LayersPacket_Layer_Matrix::add_m(float value) {
+ m_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+LayersPacket_Layer_Matrix::m() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return m_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+LayersPacket_Layer_Matrix::mutable_m() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return &m_;
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Shadow
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+inline bool LayersPacket_Layer_Shadow::has_clip() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_clip() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_clip() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Shadow::clear_clip() {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ clear_has_clip();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer_Shadow::clip() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return clip_ != NULL ? *clip_ : *default_instance().clip_;
+#else
+ return clip_ != NULL ? *clip_ : *default_instance_->clip_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Shadow::mutable_clip() {
+ set_has_clip();
+ if (clip_ == NULL) clip_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+ return clip_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Shadow::release_clip() {
+ clear_has_clip();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* temp = clip_;
+ clip_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip) {
+ delete clip_;
+ clip_ = clip;
+ if (clip) {
+ set_has_clip();
+ } else {
+ clear_has_clip();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+inline bool LayersPacket_Layer_Shadow::has_transform() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_transform() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_transform() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Shadow::clear_transform() {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ clear_has_transform();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& LayersPacket_Layer_Shadow::transform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return transform_ != NULL ? *transform_ : *default_instance().transform_;
+#else
+ return transform_ != NULL ? *transform_ : *default_instance_->transform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer_Shadow::mutable_transform() {
+ set_has_transform();
+ if (transform_ == NULL) transform_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+ return transform_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer_Shadow::release_transform() {
+ clear_has_transform();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* temp = transform_;
+ transform_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform) {
+ delete transform_;
+ transform_ = transform;
+ if (transform) {
+ set_has_transform();
+ } else {
+ clear_has_transform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+inline bool LayersPacket_Layer_Shadow::has_vregion() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_vregion() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_vregion() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer_Shadow::clear_vregion() {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer_Shadow::vregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vregion_ != NULL ? *vregion_ : *default_instance().vregion_;
+#else
+ return vregion_ != NULL ? *vregion_ : *default_instance_->vregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer_Shadow::mutable_vregion() {
+ set_has_vregion();
+ if (vregion_ == NULL) vregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+ return vregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer_Shadow::release_vregion() {
+ clear_has_vregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vregion_;
+ vregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion) {
+ delete vregion_;
+ vregion_ = vregion;
+ if (vregion) {
+ set_has_vregion();
+ } else {
+ clear_has_vregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer
+
+// required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+inline bool LayersPacket_Layer::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer::clear_type() {
+ type_ = 0;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType LayersPacket_Layer::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.type)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType >(type_);
+}
+inline void LayersPacket_Layer::set_type(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.type)
+}
+
+// required uint64 ptr = 2;
+inline bool LayersPacket_Layer::has_ptr() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer::set_has_ptr() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer::clear_has_ptr() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer::clear_ptr() {
+ ptr_ = GOOGLE_ULONGLONG(0);
+ clear_has_ptr();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::ptr() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.ptr)
+ return ptr_;
+}
+inline void LayersPacket_Layer::set_ptr(::google::protobuf::uint64 value) {
+ set_has_ptr();
+ ptr_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.ptr)
+}
+
+// required uint64 parentPtr = 3;
+inline bool LayersPacket_Layer::has_parentptr() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer::set_has_parentptr() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer::clear_has_parentptr() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer::clear_parentptr() {
+ parentptr_ = GOOGLE_ULONGLONG(0);
+ clear_has_parentptr();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::parentptr() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.parentPtr)
+ return parentptr_;
+}
+inline void LayersPacket_Layer::set_parentptr(::google::protobuf::uint64 value) {
+ set_has_parentptr();
+ parentptr_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.parentPtr)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+inline bool LayersPacket_Layer::has_clip() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void LayersPacket_Layer::set_has_clip() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void LayersPacket_Layer::clear_has_clip() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void LayersPacket_Layer::clear_clip() {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ clear_has_clip();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer::clip() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return clip_ != NULL ? *clip_ : *default_instance().clip_;
+#else
+ return clip_ != NULL ? *clip_ : *default_instance_->clip_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer::mutable_clip() {
+ set_has_clip();
+ if (clip_ == NULL) clip_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+ return clip_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer::release_clip() {
+ clear_has_clip();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* temp = clip_;
+ clip_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip) {
+ delete clip_;
+ clip_ = clip;
+ if (clip) {
+ set_has_clip();
+ } else {
+ clear_has_clip();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+inline bool LayersPacket_Layer::has_transform() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void LayersPacket_Layer::set_has_transform() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void LayersPacket_Layer::clear_has_transform() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void LayersPacket_Layer::clear_transform() {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ clear_has_transform();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& LayersPacket_Layer::transform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return transform_ != NULL ? *transform_ : *default_instance().transform_;
+#else
+ return transform_ != NULL ? *transform_ : *default_instance_->transform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer::mutable_transform() {
+ set_has_transform();
+ if (transform_ == NULL) transform_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+ return transform_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer::release_transform() {
+ clear_has_transform();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* temp = transform_;
+ transform_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform) {
+ delete transform_;
+ transform_ = transform;
+ if (transform) {
+ set_has_transform();
+ } else {
+ clear_has_transform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+inline bool LayersPacket_Layer::has_vregion() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void LayersPacket_Layer::set_has_vregion() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void LayersPacket_Layer::clear_has_vregion() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void LayersPacket_Layer::clear_vregion() {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::vregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vregion_ != NULL ? *vregion_ : *default_instance().vregion_;
+#else
+ return vregion_ != NULL ? *vregion_ : *default_instance_->vregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_vregion() {
+ set_has_vregion();
+ if (vregion_ == NULL) vregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+ return vregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_vregion() {
+ clear_has_vregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vregion_;
+ vregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion) {
+ delete vregion_;
+ vregion_ = vregion;
+ if (vregion) {
+ set_has_vregion();
+ } else {
+ clear_has_vregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+inline bool LayersPacket_Layer::has_shadow() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void LayersPacket_Layer::set_has_shadow() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void LayersPacket_Layer::clear_has_shadow() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void LayersPacket_Layer::clear_shadow() {
+ if (shadow_ != NULL) shadow_->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::Clear();
+ clear_has_shadow();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow& LayersPacket_Layer::shadow() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return shadow_ != NULL ? *shadow_ : *default_instance().shadow_;
+#else
+ return shadow_ != NULL ? *shadow_ : *default_instance_->shadow_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* LayersPacket_Layer::mutable_shadow() {
+ set_has_shadow();
+ if (shadow_ == NULL) shadow_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+ return shadow_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* LayersPacket_Layer::release_shadow() {
+ clear_has_shadow();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* temp = shadow_;
+ shadow_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_shadow(::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow) {
+ delete shadow_;
+ shadow_ = shadow;
+ if (shadow) {
+ set_has_shadow();
+ } else {
+ clear_has_shadow();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+}
+
+// optional float opacity = 14;
+inline bool LayersPacket_Layer::has_opacity() const {
+ return (_has_bits_[0] & 0x00000080u) != 0;
+}
+inline void LayersPacket_Layer::set_has_opacity() {
+ _has_bits_[0] |= 0x00000080u;
+}
+inline void LayersPacket_Layer::clear_has_opacity() {
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline void LayersPacket_Layer::clear_opacity() {
+ opacity_ = 0;
+ clear_has_opacity();
+}
+inline float LayersPacket_Layer::opacity() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.opacity)
+ return opacity_;
+}
+inline void LayersPacket_Layer::set_opacity(float value) {
+ set_has_opacity();
+ opacity_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.opacity)
+}
+
+// optional bool cOpaque = 15;
+inline bool LayersPacket_Layer::has_copaque() const {
+ return (_has_bits_[0] & 0x00000100u) != 0;
+}
+inline void LayersPacket_Layer::set_has_copaque() {
+ _has_bits_[0] |= 0x00000100u;
+}
+inline void LayersPacket_Layer::clear_has_copaque() {
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline void LayersPacket_Layer::clear_copaque() {
+ copaque_ = false;
+ clear_has_copaque();
+}
+inline bool LayersPacket_Layer::copaque() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.cOpaque)
+ return copaque_;
+}
+inline void LayersPacket_Layer::set_copaque(bool value) {
+ set_has_copaque();
+ copaque_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.cOpaque)
+}
+
+// optional bool cAlpha = 16;
+inline bool LayersPacket_Layer::has_calpha() const {
+ return (_has_bits_[0] & 0x00000200u) != 0;
+}
+inline void LayersPacket_Layer::set_has_calpha() {
+ _has_bits_[0] |= 0x00000200u;
+}
+inline void LayersPacket_Layer::clear_has_calpha() {
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline void LayersPacket_Layer::clear_calpha() {
+ calpha_ = false;
+ clear_has_calpha();
+}
+inline bool LayersPacket_Layer::calpha() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.cAlpha)
+ return calpha_;
+}
+inline void LayersPacket_Layer::set_calpha(bool value) {
+ set_has_calpha();
+ calpha_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.cAlpha)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+inline bool LayersPacket_Layer::has_direct() const {
+ return (_has_bits_[0] & 0x00000400u) != 0;
+}
+inline void LayersPacket_Layer::set_has_direct() {
+ _has_bits_[0] |= 0x00000400u;
+}
+inline void LayersPacket_Layer::clear_has_direct() {
+ _has_bits_[0] &= ~0x00000400u;
+}
+inline void LayersPacket_Layer::clear_direct() {
+ direct_ = 1;
+ clear_has_direct();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::direct() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.direct)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect >(direct_);
+}
+inline void LayersPacket_Layer::set_direct(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect_IsValid(value));
+ set_has_direct();
+ direct_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.direct)
+}
+
+// optional uint64 barID = 18;
+inline bool LayersPacket_Layer::has_barid() const {
+ return (_has_bits_[0] & 0x00000800u) != 0;
+}
+inline void LayersPacket_Layer::set_has_barid() {
+ _has_bits_[0] |= 0x00000800u;
+}
+inline void LayersPacket_Layer::clear_has_barid() {
+ _has_bits_[0] &= ~0x00000800u;
+}
+inline void LayersPacket_Layer::clear_barid() {
+ barid_ = GOOGLE_ULONGLONG(0);
+ clear_has_barid();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::barid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.barID)
+ return barid_;
+}
+inline void LayersPacket_Layer::set_barid(::google::protobuf::uint64 value) {
+ set_has_barid();
+ barid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.barID)
+}
+
+// optional uint64 mask = 19;
+inline bool LayersPacket_Layer::has_mask() const {
+ return (_has_bits_[0] & 0x00001000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_mask() {
+ _has_bits_[0] |= 0x00001000u;
+}
+inline void LayersPacket_Layer::clear_has_mask() {
+ _has_bits_[0] &= ~0x00001000u;
+}
+inline void LayersPacket_Layer::clear_mask() {
+ mask_ = GOOGLE_ULONGLONG(0);
+ clear_has_mask();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::mask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.mask)
+ return mask_;
+}
+inline void LayersPacket_Layer::set_mask(::google::protobuf::uint64 value) {
+ set_has_mask();
+ mask_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.mask)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+inline bool LayersPacket_Layer::has_hitregion() const {
+ return (_has_bits_[0] & 0x00002000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_hitregion() {
+ _has_bits_[0] |= 0x00002000u;
+}
+inline void LayersPacket_Layer::clear_has_hitregion() {
+ _has_bits_[0] &= ~0x00002000u;
+}
+inline void LayersPacket_Layer::clear_hitregion() {
+ if (hitregion_ != NULL) hitregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_hitregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::hitregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return hitregion_ != NULL ? *hitregion_ : *default_instance().hitregion_;
+#else
+ return hitregion_ != NULL ? *hitregion_ : *default_instance_->hitregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_hitregion() {
+ set_has_hitregion();
+ if (hitregion_ == NULL) hitregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+ return hitregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_hitregion() {
+ clear_has_hitregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = hitregion_;
+ hitregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_hitregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion) {
+ delete hitregion_;
+ hitregion_ = hitregion;
+ if (hitregion) {
+ set_has_hitregion();
+ } else {
+ clear_has_hitregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+inline bool LayersPacket_Layer::has_dispatchregion() const {
+ return (_has_bits_[0] & 0x00004000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_dispatchregion() {
+ _has_bits_[0] |= 0x00004000u;
+}
+inline void LayersPacket_Layer::clear_has_dispatchregion() {
+ _has_bits_[0] &= ~0x00004000u;
+}
+inline void LayersPacket_Layer::clear_dispatchregion() {
+ if (dispatchregion_ != NULL) dispatchregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_dispatchregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::dispatchregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return dispatchregion_ != NULL ? *dispatchregion_ : *default_instance().dispatchregion_;
+#else
+ return dispatchregion_ != NULL ? *dispatchregion_ : *default_instance_->dispatchregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_dispatchregion() {
+ set_has_dispatchregion();
+ if (dispatchregion_ == NULL) dispatchregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+ return dispatchregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_dispatchregion() {
+ clear_has_dispatchregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = dispatchregion_;
+ dispatchregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_dispatchregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion) {
+ delete dispatchregion_;
+ dispatchregion_ = dispatchregion;
+ if (dispatchregion) {
+ set_has_dispatchregion();
+ } else {
+ clear_has_dispatchregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+inline bool LayersPacket_Layer::has_noactionregion() const {
+ return (_has_bits_[0] & 0x00008000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_noactionregion() {
+ _has_bits_[0] |= 0x00008000u;
+}
+inline void LayersPacket_Layer::clear_has_noactionregion() {
+ _has_bits_[0] &= ~0x00008000u;
+}
+inline void LayersPacket_Layer::clear_noactionregion() {
+ if (noactionregion_ != NULL) noactionregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_noactionregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::noactionregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return noactionregion_ != NULL ? *noactionregion_ : *default_instance().noactionregion_;
+#else
+ return noactionregion_ != NULL ? *noactionregion_ : *default_instance_->noactionregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_noactionregion() {
+ set_has_noactionregion();
+ if (noactionregion_ == NULL) noactionregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+ return noactionregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_noactionregion() {
+ clear_has_noactionregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = noactionregion_;
+ noactionregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_noactionregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion) {
+ delete noactionregion_;
+ noactionregion_ = noactionregion;
+ if (noactionregion) {
+ set_has_noactionregion();
+ } else {
+ clear_has_noactionregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+inline bool LayersPacket_Layer::has_hpanregion() const {
+ return (_has_bits_[0] & 0x00010000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_hpanregion() {
+ _has_bits_[0] |= 0x00010000u;
+}
+inline void LayersPacket_Layer::clear_has_hpanregion() {
+ _has_bits_[0] &= ~0x00010000u;
+}
+inline void LayersPacket_Layer::clear_hpanregion() {
+ if (hpanregion_ != NULL) hpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_hpanregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::hpanregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return hpanregion_ != NULL ? *hpanregion_ : *default_instance().hpanregion_;
+#else
+ return hpanregion_ != NULL ? *hpanregion_ : *default_instance_->hpanregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_hpanregion() {
+ set_has_hpanregion();
+ if (hpanregion_ == NULL) hpanregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+ return hpanregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_hpanregion() {
+ clear_has_hpanregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = hpanregion_;
+ hpanregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_hpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion) {
+ delete hpanregion_;
+ hpanregion_ = hpanregion;
+ if (hpanregion) {
+ set_has_hpanregion();
+ } else {
+ clear_has_hpanregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+inline bool LayersPacket_Layer::has_vpanregion() const {
+ return (_has_bits_[0] & 0x00020000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_vpanregion() {
+ _has_bits_[0] |= 0x00020000u;
+}
+inline void LayersPacket_Layer::clear_has_vpanregion() {
+ _has_bits_[0] &= ~0x00020000u;
+}
+inline void LayersPacket_Layer::clear_vpanregion() {
+ if (vpanregion_ != NULL) vpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vpanregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::vpanregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vpanregion_ != NULL ? *vpanregion_ : *default_instance().vpanregion_;
+#else
+ return vpanregion_ != NULL ? *vpanregion_ : *default_instance_->vpanregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_vpanregion() {
+ set_has_vpanregion();
+ if (vpanregion_ == NULL) vpanregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+ return vpanregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_vpanregion() {
+ clear_has_vpanregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vpanregion_;
+ vpanregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_vpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion) {
+ delete vpanregion_;
+ vpanregion_ = vpanregion;
+ if (vpanregion) {
+ set_has_vpanregion();
+ } else {
+ clear_has_vpanregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+inline bool LayersPacket_Layer::has_valid() const {
+ return (_has_bits_[0] & 0x00040000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_valid() {
+ _has_bits_[0] |= 0x00040000u;
+}
+inline void LayersPacket_Layer::clear_has_valid() {
+ _has_bits_[0] &= ~0x00040000u;
+}
+inline void LayersPacket_Layer::clear_valid() {
+ if (valid_ != NULL) valid_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_valid();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::valid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return valid_ != NULL ? *valid_ : *default_instance().valid_;
+#else
+ return valid_ != NULL ? *valid_ : *default_instance_->valid_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_valid() {
+ set_has_valid();
+ if (valid_ == NULL) valid_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+ return valid_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_valid() {
+ clear_has_valid();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = valid_;
+ valid_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_valid(::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid) {
+ delete valid_;
+ valid_ = valid;
+ if (valid) {
+ set_has_valid();
+ } else {
+ clear_has_valid();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+}
+
+// optional uint32 color = 101;
+inline bool LayersPacket_Layer::has_color() const {
+ return (_has_bits_[0] & 0x00080000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_color() {
+ _has_bits_[0] |= 0x00080000u;
+}
+inline void LayersPacket_Layer::clear_has_color() {
+ _has_bits_[0] &= ~0x00080000u;
+}
+inline void LayersPacket_Layer::clear_color() {
+ color_ = 0u;
+ clear_has_color();
+}
+inline ::google::protobuf::uint32 LayersPacket_Layer::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.color)
+ return color_;
+}
+inline void LayersPacket_Layer::set_color(::google::protobuf::uint32 value) {
+ set_has_color();
+ color_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.color)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+inline bool LayersPacket_Layer::has_filter() const {
+ return (_has_bits_[0] & 0x00100000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_filter() {
+ _has_bits_[0] |= 0x00100000u;
+}
+inline void LayersPacket_Layer::clear_has_filter() {
+ _has_bits_[0] &= ~0x00100000u;
+}
+inline void LayersPacket_Layer::clear_filter() {
+ filter_ = 0;
+ clear_has_filter();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Filter LayersPacket_Layer::filter() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.filter)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Filter >(filter_);
+}
+inline void LayersPacket_Layer::set_filter(::mozilla::layers::layerscope::LayersPacket_Layer_Filter value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_Filter_IsValid(value));
+ set_has_filter();
+ filter_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.filter)
+}
+
+// optional uint64 refID = 103;
+inline bool LayersPacket_Layer::has_refid() const {
+ return (_has_bits_[0] & 0x00200000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_refid() {
+ _has_bits_[0] |= 0x00200000u;
+}
+inline void LayersPacket_Layer::clear_has_refid() {
+ _has_bits_[0] &= ~0x00200000u;
+}
+inline void LayersPacket_Layer::clear_refid() {
+ refid_ = GOOGLE_ULONGLONG(0);
+ clear_has_refid();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::refid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.refID)
+ return refid_;
+}
+inline void LayersPacket_Layer::set_refid(::google::protobuf::uint64 value) {
+ set_has_refid();
+ refid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.refID)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+inline bool LayersPacket_Layer::has_size() const {
+ return (_has_bits_[0] & 0x00400000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_size() {
+ _has_bits_[0] |= 0x00400000u;
+}
+inline void LayersPacket_Layer::clear_has_size() {
+ _has_bits_[0] &= ~0x00400000u;
+}
+inline void LayersPacket_Layer::clear_size() {
+ if (size_ != NULL) size_->::mozilla::layers::layerscope::LayersPacket_Layer_Size::Clear();
+ clear_has_size();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Size& LayersPacket_Layer::size() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.size)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return size_ != NULL ? *size_ : *default_instance().size_;
+#else
+ return size_ != NULL ? *size_ : *default_instance_->size_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* LayersPacket_Layer::mutable_size() {
+ set_has_size();
+ if (size_ == NULL) size_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Size;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.size)
+ return size_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* LayersPacket_Layer::release_size() {
+ clear_has_size();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size* temp = size_;
+ size_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_size(::mozilla::layers::layerscope::LayersPacket_Layer_Size* size) {
+ delete size_;
+ size_ = size;
+ if (size) {
+ set_has_size();
+ } else {
+ clear_has_size();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.size)
+}
+
+// optional uint32 displayListLogLength = 105;
+inline bool LayersPacket_Layer::has_displaylistloglength() const {
+ return (_has_bits_[0] & 0x00800000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_displaylistloglength() {
+ _has_bits_[0] |= 0x00800000u;
+}
+inline void LayersPacket_Layer::clear_has_displaylistloglength() {
+ _has_bits_[0] &= ~0x00800000u;
+}
+inline void LayersPacket_Layer::clear_displaylistloglength() {
+ displaylistloglength_ = 0u;
+ clear_has_displaylistloglength();
+}
+inline ::google::protobuf::uint32 LayersPacket_Layer::displaylistloglength() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.displayListLogLength)
+ return displaylistloglength_;
+}
+inline void LayersPacket_Layer::set_displaylistloglength(::google::protobuf::uint32 value) {
+ set_has_displaylistloglength();
+ displaylistloglength_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.displayListLogLength)
+}
+
+// optional bytes displayListLog = 106;
+inline bool LayersPacket_Layer::has_displaylistlog() const {
+ return (_has_bits_[0] & 0x01000000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_displaylistlog() {
+ _has_bits_[0] |= 0x01000000u;
+}
+inline void LayersPacket_Layer::clear_has_displaylistlog() {
+ _has_bits_[0] &= ~0x01000000u;
+}
+inline void LayersPacket_Layer::clear_displaylistlog() {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_->clear();
+ }
+ clear_has_displaylistlog();
+}
+inline const ::std::string& LayersPacket_Layer::displaylistlog() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+ return *displaylistlog_;
+}
+inline void LayersPacket_Layer::set_displaylistlog(const ::std::string& value) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline void LayersPacket_Layer::set_displaylistlog(const char* value) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(value);
+ // @@protoc_insertion_point(field_set_char:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline void LayersPacket_Layer::set_displaylistlog(const void* value, size_t size) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline ::std::string* LayersPacket_Layer::mutable_displaylistlog() {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+ return displaylistlog_;
+}
+inline ::std::string* LayersPacket_Layer::release_displaylistlog() {
+ clear_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = displaylistlog_;
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void LayersPacket_Layer::set_allocated_displaylistlog(::std::string* displaylistlog) {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete displaylistlog_;
+ }
+ if (displaylistlog) {
+ set_has_displaylistlog();
+ displaylistlog_ = displaylistlog;
+ } else {
+ clear_has_displaylistlog();
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket
+
+// repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+inline int LayersPacket::layer_size() const {
+ return layer_.size();
+}
+inline void LayersPacket::clear_layer() {
+ layer_.Clear();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer& LayersPacket::layer(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Get(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer* LayersPacket::mutable_layer(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer* LayersPacket::add_layer() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >&
+LayersPacket::layer() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >*
+LayersPacket::mutable_layer() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.layer)
+ return &layer_;
+}
+
+// -------------------------------------------------------------------
+
+// MetaPacket
+
+// optional bool composedByHwc = 1;
+inline bool MetaPacket::has_composedbyhwc() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void MetaPacket::set_has_composedbyhwc() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void MetaPacket::clear_has_composedbyhwc() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void MetaPacket::clear_composedbyhwc() {
+ composedbyhwc_ = false;
+ clear_has_composedbyhwc();
+}
+inline bool MetaPacket::composedbyhwc() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.MetaPacket.composedByHwc)
+ return composedbyhwc_;
+}
+inline void MetaPacket::set_composedbyhwc(bool value) {
+ set_has_composedbyhwc();
+ composedbyhwc_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.MetaPacket.composedByHwc)
+}
+
+// -------------------------------------------------------------------
+
+// DrawPacket_Rect
+
+// required float x = 1;
+inline bool DrawPacket_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void DrawPacket_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void DrawPacket_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void DrawPacket_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline float DrawPacket_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.x)
+ return x_;
+}
+inline void DrawPacket_Rect::set_x(float value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.x)
+}
+
+// required float y = 2;
+inline bool DrawPacket_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void DrawPacket_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void DrawPacket_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void DrawPacket_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline float DrawPacket_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.y)
+ return y_;
+}
+inline void DrawPacket_Rect::set_y(float value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.y)
+}
+
+// required float w = 3;
+inline bool DrawPacket_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void DrawPacket_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void DrawPacket_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void DrawPacket_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline float DrawPacket_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.w)
+ return w_;
+}
+inline void DrawPacket_Rect::set_w(float value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.w)
+}
+
+// required float h = 4;
+inline bool DrawPacket_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void DrawPacket_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void DrawPacket_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void DrawPacket_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline float DrawPacket_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.h)
+ return h_;
+}
+inline void DrawPacket_Rect::set_h(float value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// DrawPacket
+
+// required float offsetX = 1;
+inline bool DrawPacket::has_offsetx() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void DrawPacket::set_has_offsetx() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void DrawPacket::clear_has_offsetx() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void DrawPacket::clear_offsetx() {
+ offsetx_ = 0;
+ clear_has_offsetx();
+}
+inline float DrawPacket::offsetx() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.offsetX)
+ return offsetx_;
+}
+inline void DrawPacket::set_offsetx(float value) {
+ set_has_offsetx();
+ offsetx_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.offsetX)
+}
+
+// required float offsetY = 2;
+inline bool DrawPacket::has_offsety() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void DrawPacket::set_has_offsety() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void DrawPacket::clear_has_offsety() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void DrawPacket::clear_offsety() {
+ offsety_ = 0;
+ clear_has_offsety();
+}
+inline float DrawPacket::offsety() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.offsetY)
+ return offsety_;
+}
+inline void DrawPacket::set_offsety(float value) {
+ set_has_offsety();
+ offsety_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.offsetY)
+}
+
+// repeated float mvMatrix = 3;
+inline int DrawPacket::mvmatrix_size() const {
+ return mvmatrix_.size();
+}
+inline void DrawPacket::clear_mvmatrix() {
+ mvmatrix_.Clear();
+}
+inline float DrawPacket::mvmatrix(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return mvmatrix_.Get(index);
+}
+inline void DrawPacket::set_mvmatrix(int index, float value) {
+ mvmatrix_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+}
+inline void DrawPacket::add_mvmatrix(float value) {
+ mvmatrix_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+DrawPacket::mvmatrix() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return mvmatrix_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+DrawPacket::mutable_mvmatrix() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return &mvmatrix_;
+}
+
+// required uint32 totalRects = 4;
+inline bool DrawPacket::has_totalrects() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void DrawPacket::set_has_totalrects() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void DrawPacket::clear_has_totalrects() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void DrawPacket::clear_totalrects() {
+ totalrects_ = 0u;
+ clear_has_totalrects();
+}
+inline ::google::protobuf::uint32 DrawPacket::totalrects() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.totalRects)
+ return totalrects_;
+}
+inline void DrawPacket::set_totalrects(::google::protobuf::uint32 value) {
+ set_has_totalrects();
+ totalrects_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.totalRects)
+}
+
+// repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+inline int DrawPacket::layerrect_size() const {
+ return layerrect_.size();
+}
+inline void DrawPacket::clear_layerrect() {
+ layerrect_.Clear();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket_Rect& DrawPacket::layerrect(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Get(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::mutable_layerrect(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::add_layerrect() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+DrawPacket::layerrect() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+DrawPacket::mutable_layerrect() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return &layerrect_;
+}
+
+// required uint64 layerref = 6;
+inline bool DrawPacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void DrawPacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void DrawPacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void DrawPacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 DrawPacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.layerref)
+ return layerref_;
+}
+inline void DrawPacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.layerref)
+}
+
+// repeated uint32 texIDs = 7;
+inline int DrawPacket::texids_size() const {
+ return texids_.size();
+}
+inline void DrawPacket::clear_texids() {
+ texids_.Clear();
+}
+inline ::google::protobuf::uint32 DrawPacket::texids(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return texids_.Get(index);
+}
+inline void DrawPacket::set_texids(int index, ::google::protobuf::uint32 value) {
+ texids_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.texIDs)
+}
+inline void DrawPacket::add_texids(::google::protobuf::uint32 value) {
+ texids_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.texIDs)
+}
+inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+DrawPacket::texids() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return texids_;
+}
+inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+DrawPacket::mutable_texids() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return &texids_;
+}
+
+// repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+inline int DrawPacket::texturerect_size() const {
+ return texturerect_.size();
+}
+inline void DrawPacket::clear_texturerect() {
+ texturerect_.Clear();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket_Rect& DrawPacket::texturerect(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Get(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::mutable_texturerect(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::add_texturerect() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+DrawPacket::texturerect() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+DrawPacket::mutable_texturerect() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return &texturerect_;
+}
+
+// -------------------------------------------------------------------
+
+// Packet
+
+// required .mozilla.layers.layerscope.Packet.DataType type = 1;
+inline bool Packet::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void Packet::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void Packet::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void Packet::clear_type() {
+ type_ = 1;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::Packet_DataType Packet::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.type)
+ return static_cast< ::mozilla::layers::layerscope::Packet_DataType >(type_);
+}
+inline void Packet::set_type(::mozilla::layers::layerscope::Packet_DataType value) {
+ assert(::mozilla::layers::layerscope::Packet_DataType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.Packet.type)
+}
+
+// optional .mozilla.layers.layerscope.FramePacket frame = 2;
+inline bool Packet::has_frame() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void Packet::set_has_frame() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void Packet::clear_has_frame() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void Packet::clear_frame() {
+ if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear();
+ clear_has_frame();
+}
+inline const ::mozilla::layers::layerscope::FramePacket& Packet::frame() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.frame)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return frame_ != NULL ? *frame_ : *default_instance().frame_;
+#else
+ return frame_ != NULL ? *frame_ : *default_instance_->frame_;
+#endif
+}
+inline ::mozilla::layers::layerscope::FramePacket* Packet::mutable_frame() {
+ set_has_frame();
+ if (frame_ == NULL) frame_ = new ::mozilla::layers::layerscope::FramePacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.frame)
+ return frame_;
+}
+inline ::mozilla::layers::layerscope::FramePacket* Packet::release_frame() {
+ clear_has_frame();
+ ::mozilla::layers::layerscope::FramePacket* temp = frame_;
+ frame_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_frame(::mozilla::layers::layerscope::FramePacket* frame) {
+ delete frame_;
+ frame_ = frame;
+ if (frame) {
+ set_has_frame();
+ } else {
+ clear_has_frame();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.frame)
+}
+
+// optional .mozilla.layers.layerscope.ColorPacket color = 3;
+inline bool Packet::has_color() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void Packet::set_has_color() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void Packet::clear_has_color() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void Packet::clear_color() {
+ if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear();
+ clear_has_color();
+}
+inline const ::mozilla::layers::layerscope::ColorPacket& Packet::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.color)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return color_ != NULL ? *color_ : *default_instance().color_;
+#else
+ return color_ != NULL ? *color_ : *default_instance_->color_;
+#endif
+}
+inline ::mozilla::layers::layerscope::ColorPacket* Packet::mutable_color() {
+ set_has_color();
+ if (color_ == NULL) color_ = new ::mozilla::layers::layerscope::ColorPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.color)
+ return color_;
+}
+inline ::mozilla::layers::layerscope::ColorPacket* Packet::release_color() {
+ clear_has_color();
+ ::mozilla::layers::layerscope::ColorPacket* temp = color_;
+ color_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_color(::mozilla::layers::layerscope::ColorPacket* color) {
+ delete color_;
+ color_ = color;
+ if (color) {
+ set_has_color();
+ } else {
+ clear_has_color();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.color)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+inline bool Packet::has_texture() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void Packet::set_has_texture() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void Packet::clear_has_texture() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void Packet::clear_texture() {
+ if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear();
+ clear_has_texture();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket& Packet::texture() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.texture)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return texture_ != NULL ? *texture_ : *default_instance().texture_;
+#else
+ return texture_ != NULL ? *texture_ : *default_instance_->texture_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket* Packet::mutable_texture() {
+ set_has_texture();
+ if (texture_ == NULL) texture_ = new ::mozilla::layers::layerscope::TexturePacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.texture)
+ return texture_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket* Packet::release_texture() {
+ clear_has_texture();
+ ::mozilla::layers::layerscope::TexturePacket* temp = texture_;
+ texture_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_texture(::mozilla::layers::layerscope::TexturePacket* texture) {
+ delete texture_;
+ texture_ = texture;
+ if (texture) {
+ set_has_texture();
+ } else {
+ clear_has_texture();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.texture)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+inline bool Packet::has_layers() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void Packet::set_has_layers() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void Packet::clear_has_layers() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void Packet::clear_layers() {
+ if (layers_ != NULL) layers_->::mozilla::layers::layerscope::LayersPacket::Clear();
+ clear_has_layers();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket& Packet::layers() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.layers)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return layers_ != NULL ? *layers_ : *default_instance().layers_;
+#else
+ return layers_ != NULL ? *layers_ : *default_instance_->layers_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket* Packet::mutable_layers() {
+ set_has_layers();
+ if (layers_ == NULL) layers_ = new ::mozilla::layers::layerscope::LayersPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.layers)
+ return layers_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket* Packet::release_layers() {
+ clear_has_layers();
+ ::mozilla::layers::layerscope::LayersPacket* temp = layers_;
+ layers_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_layers(::mozilla::layers::layerscope::LayersPacket* layers) {
+ delete layers_;
+ layers_ = layers;
+ if (layers) {
+ set_has_layers();
+ } else {
+ clear_has_layers();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.layers)
+}
+
+// optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+inline bool Packet::has_meta() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void Packet::set_has_meta() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void Packet::clear_has_meta() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void Packet::clear_meta() {
+ if (meta_ != NULL) meta_->::mozilla::layers::layerscope::MetaPacket::Clear();
+ clear_has_meta();
+}
+inline const ::mozilla::layers::layerscope::MetaPacket& Packet::meta() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.meta)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return meta_ != NULL ? *meta_ : *default_instance().meta_;
+#else
+ return meta_ != NULL ? *meta_ : *default_instance_->meta_;
+#endif
+}
+inline ::mozilla::layers::layerscope::MetaPacket* Packet::mutable_meta() {
+ set_has_meta();
+ if (meta_ == NULL) meta_ = new ::mozilla::layers::layerscope::MetaPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.meta)
+ return meta_;
+}
+inline ::mozilla::layers::layerscope::MetaPacket* Packet::release_meta() {
+ clear_has_meta();
+ ::mozilla::layers::layerscope::MetaPacket* temp = meta_;
+ meta_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_meta(::mozilla::layers::layerscope::MetaPacket* meta) {
+ delete meta_;
+ meta_ = meta;
+ if (meta) {
+ set_has_meta();
+ } else {
+ clear_has_meta();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.meta)
+}
+
+// optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+inline bool Packet::has_draw() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void Packet::set_has_draw() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void Packet::clear_has_draw() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void Packet::clear_draw() {
+ if (draw_ != NULL) draw_->::mozilla::layers::layerscope::DrawPacket::Clear();
+ clear_has_draw();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket& Packet::draw() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.draw)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return draw_ != NULL ? *draw_ : *default_instance().draw_;
+#else
+ return draw_ != NULL ? *draw_ : *default_instance_->draw_;
+#endif
+}
+inline ::mozilla::layers::layerscope::DrawPacket* Packet::mutable_draw() {
+ set_has_draw();
+ if (draw_ == NULL) draw_ = new ::mozilla::layers::layerscope::DrawPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.draw)
+ return draw_;
+}
+inline ::mozilla::layers::layerscope::DrawPacket* Packet::release_draw() {
+ clear_has_draw();
+ ::mozilla::layers::layerscope::DrawPacket* temp = draw_;
+ draw_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_draw(::mozilla::layers::layerscope::DrawPacket* draw) {
+ delete draw_;
+ draw_ = draw;
+ if (draw) {
+ set_has_draw();
+ } else {
+ clear_has_draw();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.draw)
+}
+
+// -------------------------------------------------------------------
+
+// CommandPacket
+
+// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+inline bool CommandPacket::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void CommandPacket::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void CommandPacket::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void CommandPacket::clear_type() {
+ type_ = 0;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::CommandPacket_CmdType CommandPacket::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.CommandPacket.type)
+ return static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(type_);
+}
+inline void CommandPacket::set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value) {
+ assert(::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.CommandPacket.type)
+}
+
+// optional bool value = 2;
+inline bool CommandPacket::has_value() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void CommandPacket::set_has_value() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void CommandPacket::clear_has_value() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void CommandPacket::clear_value() {
+ value_ = false;
+ clear_has_value();
+}
+inline bool CommandPacket::value() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.CommandPacket.value)
+ return value_;
+}
+inline void CommandPacket::set_value(bool value) {
+ set_has_value();
+ value_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.CommandPacket.value)
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace layerscope
+} // namespace layers
+} // namespace mozilla
+
+// @@protoc_insertion_point(global_scope)
+
+#endif // PROTOBUF_LayerScopePacket_2eproto__INCLUDED
diff --git a/system/graphics/layers/protobuf/LayerScopePacket.proto b/system/graphics/layers/protobuf/LayerScopePacket.proto
new file mode 100644
index 000000000..9b4306b2a
--- /dev/null
+++ b/system/graphics/layers/protobuf/LayerScopePacket.proto
@@ -0,0 +1,218 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+
+option optimize_for = LITE_RUNTIME;
+
+package mozilla.layers.layerscope;
+
+// ===============================
+// Server to Client messages
+// ===============================
+message FramePacket {
+ optional uint64 value = 1;
+ optional float scale = 2;
+}
+
+message ColorPacket {
+ required uint64 layerref = 1;
+ optional uint32 width = 2;
+ optional uint32 height = 3;
+ optional uint32 color = 4;
+}
+
+message TexturePacket {
+ enum Filter {
+ GOOD = 0;
+ LINEAR = 1;
+ POINT = 2;
+ }
+ message Rect {
+ optional float x = 1;
+ optional float y = 2;
+ optional float w = 3;
+ optional float h = 4;
+ }
+ message Size {
+ optional int32 w = 1;
+ optional int32 h = 2;
+ }
+ message Matrix {
+ optional bool is2D = 1;
+ optional bool isId = 2;
+ repeated float m = 3;
+ }
+ message EffectMask {
+ optional bool mIs3D = 1;
+ optional Size mSize = 2;
+ optional Matrix mMaskTransform = 3;
+ }
+
+ // Basic info
+ required uint64 layerref = 1;
+ optional uint32 width = 2;
+ optional uint32 height = 3;
+ optional uint32 stride = 4;
+ optional uint32 name = 5;
+ optional uint32 target = 6;
+ optional uint32 dataformat = 7;
+ optional uint64 glcontext = 8;
+ optional bytes data = 9;
+
+ // TextureEffect attributes
+ optional Rect mTextureCoords = 10;
+ optional bool mPremultiplied = 11;
+ optional Filter mFilter = 12;
+
+ // Mask attributes
+ optional bool isMask = 20;
+ optional EffectMask mask = 21;
+}
+
+message LayersPacket {
+ message Layer {
+ enum LayerType {
+ UnknownLayer = 0;
+ LayerManager = 1;
+ ContainerLayer = 2;
+ PaintedLayer = 3;
+ CanvasLayer = 4;
+ ImageLayer = 5;
+ ColorLayer = 6;
+ RefLayer = 7;
+ ReadbackLayer = 8;
+ }
+ enum ScrollingDirect {
+ VERTICAL = 1;
+ HORIZONTAL = 2;
+ }
+ enum Filter {
+ FILTER_FAST = 0; // deprecated
+ FILTER_GOOD = 1;
+ FILTER_BEST = 2; // deprecated
+ FILTER_NEAREST = 3; //deprecated
+ FILTER_BILINEAR = 4; //deprecated
+ FILTER_GAUSSIAN = 5; //deprecated
+ FILTER_SENTINEL = 6; //deprecated
+ FILTER_LINEAR = 7;
+ FILTER_POINT = 8;
+ }
+ message Size {
+ optional int32 w = 1;
+ optional int32 h = 2;
+ }
+ message Rect {
+ optional int32 x = 1;
+ optional int32 y = 2;
+ optional int32 w = 3;
+ optional int32 h = 4;
+ }
+ message Region {
+ repeated Rect r = 1;
+ }
+ message Matrix {
+ optional bool is2D = 1;
+ optional bool isId = 2;
+ repeated float m = 3;
+ }
+ message Shadow {
+ optional Rect clip = 1;
+ optional Matrix transform = 2;
+ optional Region vRegion = 3;
+ }
+
+ // Basic info
+ // Note: Parent's pointer is used to recontruct the layer tree
+ required LayerType type = 1;
+ required uint64 ptr = 2;
+ required uint64 parentPtr = 3;
+
+ // Common info (10 to 99)
+ optional Rect clip = 10;
+ optional Matrix transform = 11;
+ optional Region vRegion = 12; // visible region
+ optional Shadow shadow = 13; // shadow info
+ optional float opacity = 14;
+ optional bool cOpaque = 15; // content opaque
+ optional bool cAlpha = 16; // component alpha
+ optional ScrollingDirect direct = 17;
+ optional uint64 barID = 18;
+ optional uint64 mask = 19; // mask layer
+ optional Region hitRegion = 20;
+ optional Region dispatchRegion = 21;
+ optional Region noActionRegion = 22;
+ optional Region hPanRegion = 23;
+ optional Region vPanRegion = 24;
+
+ // Specific info (100 to max)
+ // Painted Layer
+ optional Region valid = 100;
+ // Color Layer
+ optional uint32 color = 101;
+ // Canvas & Image Layer
+ optional Filter filter = 102;
+ // Ref Layer
+ optional uint64 refID = 103;
+ // Readback Layer
+ optional Size size = 104;
+ optional uint32 displayListLogLength = 105;
+ optional bytes displayListLog = 106;
+ }
+ repeated Layer layer = 1;
+}
+
+message MetaPacket {
+ optional bool composedByHwc = 1;
+}
+
+message DrawPacket {
+ message Rect {
+ required float x = 1;
+ required float y = 2;
+ required float w = 3;
+ required float h = 4;
+ }
+
+ required float offsetX = 1;
+ required float offsetY = 2;
+ repeated float mvMatrix = 3;
+ required uint32 totalRects = 4;
+ repeated Rect layerRect = 5;
+ required uint64 layerref = 6;
+ repeated uint32 texIDs = 7;
+ repeated Rect textureRect = 8;
+}
+
+// We only need to use this Packet.
+// Other packet definitions are just type defines
+message Packet {
+ enum DataType {
+ FRAMESTART = 1;
+ FRAMEEND = 2;
+ COLOR = 3;
+ TEXTURE = 4;
+ LAYERS = 5;
+ META = 6;
+ DRAW = 7;
+ }
+ required DataType type = 1;
+
+ optional FramePacket frame = 2;
+ optional ColorPacket color = 3;
+ optional TexturePacket texture = 4;
+ optional LayersPacket layers = 5;
+ optional MetaPacket meta = 6;
+ optional DrawPacket draw = 7;
+}
+
+
+// ===============================
+// Client to Server messages
+// ===============================
+message CommandPacket {
+ enum CmdType {
+ NO_OP = 0;
+ LAYERS_TREE = 1;
+ LAYERS_BUFFER = 2;
+ }
+ required CmdType type = 1;
+ optional bool value = 2;
+}
diff --git a/system/graphics/moz.build b/system/graphics/moz.build
new file mode 100644
index 000000000..7914cea6b
--- /dev/null
+++ b/system/graphics/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ '2d',
+ 'src',
+ 'gl',
+ 'layers',
+ 'thebes',
+ 'ipc',
+ 'config',
+]
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['tests/gtest']
+
+TEST_DIRS += ['tests']
+
+SPHINX_TREES['gfx'] = 'docs'
diff --git a/system/graphics/src/AppUnits.h b/system/graphics/src/AppUnits.h
new file mode 100644
index 000000000..e0ad6dc52
--- /dev/null
+++ b/system/graphics/src/AppUnits.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AppUnits_h
+#define mozilla_AppUnits_h
+
+#include <stdint.h>
+
+namespace mozilla {
+inline int32_t AppUnitsPerCSSPixel() { return 60; }
+inline int32_t AppUnitsPerCSSInch() { return 96 * AppUnitsPerCSSPixel(); }
+} // namespace mozilla
+#endif /* _NS_APPUNITS_H_ */
diff --git a/system/graphics/src/ArrayView.h b/system/graphics/src/ArrayView.h
new file mode 100644
index 000000000..3c6704f43
--- /dev/null
+++ b/system/graphics/src/ArrayView.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_ARRAY_VIEW_H_
+#define MOZILLA_GFX_ARRAY_VIEW_H_
+
+#include "nsTArray.h"
+
+/* This is similar to mfbt/Range.h but has implicit conversion
+ * from nsTArray and less bounds checking.
+ * For now, prefer Range over ArrayView */
+
+namespace mozilla {
+namespace gfx {
+
+template<typename T>
+class ArrayView
+{
+ public:
+ MOZ_IMPLICIT ArrayView(const nsTArray<T>& aData) :
+ mData(aData.Elements()), mLength(aData.Length())
+ {
+ }
+ ArrayView(const T* aData, const size_t aLength) :
+ mData(aData), mLength(aLength)
+ {
+ }
+ const T& operator[](const size_t aIdx) const
+ {
+ return mData[aIdx];
+ }
+ size_t Length() const
+ {
+ return mLength;
+ }
+ const T* Data() const
+ {
+ return mData;
+ }
+ private:
+ const T* mData;
+ const size_t mLength;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_ARRAY_VIEW_H_ */
diff --git a/system/graphics/src/DriverCrashGuard.cpp b/system/graphics/src/DriverCrashGuard.cpp
new file mode 100644
index 000000000..3b69d387c
--- /dev/null
+++ b/system/graphics/src/DriverCrashGuard.cpp
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "DriverCrashGuard.h"
+#include "gfxEnv.h"
+#include "gfxPrefs.h"
+#include "gfxConfig.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/dom/ContentChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const size_t NUM_CRASH_GUARD_TYPES = size_t(CrashGuardType::NUM_TYPES);
+static const char* sCrashGuardNames[] = {
+ "d3d11layers",
+ "d3d9video",
+ "glcontext",
+ "d3d11video",
+};
+static_assert(MOZ_ARRAY_LENGTH(sCrashGuardNames) == NUM_CRASH_GUARD_TYPES,
+ "CrashGuardType updated without a name string");
+
+static inline void
+BuildCrashGuardPrefName(CrashGuardType aType, nsCString& aOutPrefName)
+{
+ MOZ_ASSERT(aType < CrashGuardType::NUM_TYPES);
+ MOZ_ASSERT(sCrashGuardNames[size_t(aType)]);
+
+ aOutPrefName.Assign("gfx.crash-guard.status.");
+ aOutPrefName.Append(sCrashGuardNames[size_t(aType)]);
+}
+
+DriverCrashGuard::DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent)
+ : mType(aType)
+ , mMode(aContentParent ? Mode::Proxy : Mode::Normal)
+ , mInitialized(false)
+ , mGuardActivated(false)
+ , mCrashDetected(false)
+{
+ BuildCrashGuardPrefName(aType, mStatusPref);
+}
+
+void
+DriverCrashGuard::InitializeIfNeeded()
+{
+ if (mInitialized) {
+ return;
+ }
+
+ mInitialized = true;
+ Initialize();
+}
+
+static inline bool
+AreCrashGuardsEnabled()
+{
+ // Crash guard isn't supported in the GPU process since the entire
+ // process is basically a crash guard.
+ if (XRE_IsGPUProcess()) {
+ return false;
+ }
+#ifdef NIGHTLY_BUILD
+ // We only use the crash guard on non-nightly channels, since the nightly
+ // channel is for development and having graphics features perma-disabled
+ // is rather annoying. Unless the user forces is with an environment
+ // variable, which comes in handy for testing.
+ return gfxEnv::ForceCrashGuardNightly();
+#else
+ // Check to see if all guards have been disabled through the environment.
+ if (gfxEnv::DisableCrashGuard()) {
+ return false;
+ }
+ return true;
+#endif
+}
+
+void
+DriverCrashGuard::Initialize()
+{
+ if (!AreCrashGuardsEnabled()) {
+ return;
+ }
+
+ // Using DriverCrashGuard off the main thread currently does not work. Under
+ // e10s it could conceivably work by dispatching the IPC calls via the main
+ // thread. In the parent process this would be harder. For now, we simply
+ // exit early instead.
+ if (!NS_IsMainThread()) {
+ return;
+ }
+
+ mGfxInfo = services::GetGfxInfo();
+
+ if (XRE_IsContentProcess()) {
+ // Ask the parent whether or not activating the guard is okay. The parent
+ // won't bother if it detected a crash.
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ cc->SendBeginDriverCrashGuard(uint32_t(mType), &mCrashDetected);
+ if (mCrashDetected) {
+ LogFeatureDisabled();
+ return;
+ }
+
+ ActivateGuard();
+ return;
+ }
+
+ // Always check whether or not the lock file exists. For example, we could
+ // have crashed creating a D3D9 device in the parent process, and on restart
+ // are now requesting one in the child process. We catch everything here.
+ if (RecoverFromCrash()) {
+ mCrashDetected = true;
+ return;
+ }
+
+ // If the environment has changed, we always activate the guard. In the
+ // parent process this performs main-thread disk I/O. Child process guards
+ // only incur an IPC cost, so if we're proxying for a child process, we
+ // play it safe and activate the guard as long as we don't expect it to
+ // crash.
+ if (CheckOrRefreshEnvironment() ||
+ (mMode == Mode::Proxy && GetStatus() != DriverInitStatus::Crashed))
+ {
+ ActivateGuard();
+ return;
+ }
+
+ // If we got here and our status is "crashed", then the environment has not
+ // updated and we do not want to attempt to use the driver again.
+ if (GetStatus() == DriverInitStatus::Crashed) {
+ mCrashDetected = true;
+ LogFeatureDisabled();
+ }
+}
+
+DriverCrashGuard::~DriverCrashGuard()
+{
+ if (!mGuardActivated) {
+ return;
+ }
+
+ if (XRE_IsParentProcess()) {
+ if (mGuardFile) {
+ mGuardFile->Remove(false);
+ }
+
+ // If during our initialization, no other process encountered a crash, we
+ // proceed to mark the status as okay.
+ if (GetStatus() != DriverInitStatus::Crashed) {
+ SetStatus(DriverInitStatus::Okay);
+ }
+ } else {
+ dom::ContentChild::GetSingleton()->SendEndDriverCrashGuard(uint32_t(mType));
+ }
+}
+
+bool
+DriverCrashGuard::Crashed()
+{
+ InitializeIfNeeded();
+
+ // Note, we read mCrashDetected instead of GetStatus(), since in child
+ // processes we're not guaranteed that the prefs have been synced in
+ // time.
+ return mCrashDetected;
+}
+
+nsCOMPtr<nsIFile>
+DriverCrashGuard::GetGuardFile()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ nsCString filename;
+ filename.Assign(sCrashGuardNames[size_t(mType)]);
+ filename.Append(".guard");
+
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(file));
+ if (!file) {
+ return nullptr;
+ }
+ if (!NS_SUCCEEDED(file->AppendNative(filename))) {
+ return nullptr;
+ }
+ return file;
+}
+
+void
+DriverCrashGuard::ActivateGuard()
+{
+ mGuardActivated = true;
+
+ // If we're in the content process, the rest of the guarding is handled
+ // in the parent.
+ if (XRE_IsContentProcess()) {
+ return;
+ }
+
+ SetStatus(DriverInitStatus::Attempting);
+
+ if (mMode != Mode::Proxy) {
+ // In parent process guards, we use two tombstones to detect crashes: a
+ // preferences and a zero-byte file on the filesystem.
+ FlushPreferences();
+
+ // Create a temporary tombstone/lockfile.
+ FILE* fp = nullptr;
+ mGuardFile = GetGuardFile();
+ if (!mGuardFile || !NS_SUCCEEDED(mGuardFile->OpenANSIFileDesc("w", &fp))) {
+ return;
+ }
+ fclose(fp);
+ }
+}
+
+void
+DriverCrashGuard::NotifyCrashed()
+{
+ CheckOrRefreshEnvironment();
+ SetStatus(DriverInitStatus::Crashed);
+ FlushPreferences();
+ LogCrashRecovery();
+}
+
+bool
+DriverCrashGuard::RecoverFromCrash()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ nsCOMPtr<nsIFile> file = GetGuardFile();
+ bool exists;
+ if ((file &&
+ NS_SUCCEEDED(file->Exists(&exists)) &&
+ exists) ||
+ (GetStatus() == DriverInitStatus::Attempting))
+ {
+ // If we get here, we've just recovered from a crash. Disable acceleration
+ // until the environment changes.
+ if (file) {
+ file->Remove(false);
+ }
+ NotifyCrashed();
+ return true;
+ }
+ return false;
+}
+
+// Return true if the caller should proceed to guard for crashes. False if
+// the environment has not changed. We persist the "changed" status across
+// calls, so that after an environment changes, all guards for the new
+// session are activated rather than just the first.
+bool
+DriverCrashGuard::CheckOrRefreshEnvironment()
+{
+ // Our result can be cached statically since we don't check live prefs.
+ static bool sBaseInfoChanged = false;
+ static bool sBaseInfoChecked = false;
+
+ if (!sBaseInfoChecked) {
+ // None of the prefs we care about, so we cache the result statically.
+ sBaseInfoChecked = true;
+ sBaseInfoChanged = UpdateBaseEnvironment();
+ }
+
+ // Always update the full environment, even if the base info didn't change.
+ return UpdateEnvironment() ||
+ sBaseInfoChanged ||
+ GetStatus() == DriverInitStatus::Unknown;
+}
+
+bool
+DriverCrashGuard::UpdateBaseEnvironment()
+{
+ bool changed = false;
+ if (mGfxInfo) {
+ nsString value;
+
+ // Driver properties.
+ mGfxInfo->GetAdapterDriverVersion(value);
+ changed |= CheckAndUpdatePref("driverVersion", value);
+ mGfxInfo->GetAdapterDeviceID(value);
+ changed |= CheckAndUpdatePref("deviceID", value);
+ }
+
+ // Firefox properties.
+ changed |= CheckAndUpdatePref("appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION));
+
+ return changed;
+}
+
+bool
+DriverCrashGuard::FeatureEnabled(int aFeature, bool aDefault)
+{
+ if (!mGfxInfo) {
+ return aDefault;
+ }
+ int32_t status;
+ nsCString discardFailureId;
+ if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
+ return false;
+ }
+ return status == nsIGfxInfo::FEATURE_STATUS_OK;
+}
+
+bool
+DriverCrashGuard::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue)
+{
+ std::string pref = GetFullPrefName(aPrefName);
+
+ bool oldValue;
+ if (NS_SUCCEEDED(Preferences::GetBool(pref.c_str(), &oldValue)) &&
+ oldValue == aCurrentValue)
+ {
+ return false;
+ }
+ Preferences::SetBool(pref.c_str(), aCurrentValue);
+ return true;
+}
+
+bool
+DriverCrashGuard::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue)
+{
+ std::string pref = GetFullPrefName(aPrefName);
+
+ nsAdoptingString oldValue = Preferences::GetString(pref.c_str());
+ if (oldValue == aCurrentValue) {
+ return false;
+ }
+ Preferences::SetString(pref.c_str(), aCurrentValue);
+ return true;
+}
+
+std::string
+DriverCrashGuard::GetFullPrefName(const char* aPref)
+{
+ return std::string("gfx.crash-guard.") +
+ std::string(sCrashGuardNames[uint32_t(mType)]) +
+ std::string(".") +
+ std::string(aPref);
+}
+
+DriverInitStatus
+DriverCrashGuard::GetStatus() const
+{
+ return (DriverInitStatus)Preferences::GetInt(mStatusPref.get(), 0);
+}
+
+void
+DriverCrashGuard::SetStatus(DriverInitStatus aStatus)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ Preferences::SetInt(mStatusPref.get(), int32_t(aStatus));
+}
+
+void
+DriverCrashGuard::FlushPreferences()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (nsIPrefService* prefService = Preferences::GetService()) {
+ prefService->SavePrefFile(nullptr);
+ }
+}
+
+void
+DriverCrashGuard::ForEachActiveCrashGuard(const CrashGuardCallback& aCallback)
+{
+ if (!AreCrashGuardsEnabled()) {
+ // Even if guards look active (via prefs), they can be ignored if globally
+ // disabled.
+ return;
+ }
+
+ for (size_t i = 0; i < NUM_CRASH_GUARD_TYPES; i++) {
+ CrashGuardType type = static_cast<CrashGuardType>(i);
+
+ nsCString prefName;
+ BuildCrashGuardPrefName(type, prefName);
+
+ auto status =
+ static_cast<DriverInitStatus>(Preferences::GetInt(prefName.get(), 0));
+ if (status != DriverInitStatus::Crashed) {
+ continue;
+ }
+
+ aCallback(sCrashGuardNames[i], prefName.get());
+ }
+}
+
+D3D11LayersCrashGuard::D3D11LayersCrashGuard(dom::ContentParent* aContentParent)
+ : DriverCrashGuard(CrashGuardType::D3D11Layers, aContentParent)
+{
+}
+
+void
+D3D11LayersCrashGuard::Initialize()
+{
+ if (!XRE_IsParentProcess()) {
+ // We assume the parent process already performed crash detection for
+ // graphics devices.
+ return;
+ }
+
+ DriverCrashGuard::Initialize();
+}
+
+bool
+D3D11LayersCrashGuard::UpdateEnvironment()
+{
+ // Our result can be cached statically since we don't check live prefs.
+ static bool checked = false;
+ static bool changed = false;
+
+ if (checked) {
+ return changed;
+ }
+
+ checked = true;
+
+ // Feature status.
+#if defined(XP_WIN)
+ bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() ||
+ (!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D));
+ changed |= CheckAndUpdateBoolPref("feature-d2d", d2dEnabled);
+
+ bool d3d11Enabled = gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING);
+ changed |= CheckAndUpdateBoolPref("feature-d3d11", d3d11Enabled);
+#endif
+
+ if (!changed) {
+ return false;
+ }
+
+ return true;
+}
+
+void
+D3D11LayersCrashGuard::LogCrashRecovery()
+{
+ gfxCriticalNote << "D3D11 layers just crashed; D3D11 will be disabled.";
+}
+
+void
+D3D11LayersCrashGuard::LogFeatureDisabled()
+{
+ gfxCriticalNote << "D3D11 layers disabled due to a prior crash.";
+}
+
+D3D9VideoCrashGuard::D3D9VideoCrashGuard(dom::ContentParent* aContentParent)
+ : DriverCrashGuard(CrashGuardType::D3D9Video, aContentParent)
+{
+}
+
+bool
+D3D9VideoCrashGuard::UpdateEnvironment()
+{
+ // We don't care about any extra preferences here.
+ return false;
+}
+
+void
+D3D9VideoCrashGuard::LogCrashRecovery()
+{
+ gfxCriticalNote << "DXVA2D3D9 just crashed; hardware video will be disabled.";
+}
+
+void
+D3D9VideoCrashGuard::LogFeatureDisabled()
+{
+ gfxCriticalNote << "DXVA2D3D9 video decoding is disabled due to a previous crash.";
+}
+
+D3D11VideoCrashGuard::D3D11VideoCrashGuard(dom::ContentParent* aContentParent)
+ : DriverCrashGuard(CrashGuardType::D3D11Video, aContentParent)
+{
+}
+
+bool
+D3D11VideoCrashGuard::UpdateEnvironment()
+{
+ // We don't care about any extra preferences here.
+ return false;
+}
+
+void
+D3D11VideoCrashGuard::LogCrashRecovery()
+{
+ gfxCriticalNote << "DXVA2D3D11 just crashed; hardware video will be disabled.";
+}
+
+void
+D3D11VideoCrashGuard::LogFeatureDisabled()
+{
+ gfxCriticalNote << "DXVA2D3D11 video decoding is disabled due to a previous crash.";
+}
+
+GLContextCrashGuard::GLContextCrashGuard(dom::ContentParent* aContentParent)
+ : DriverCrashGuard(CrashGuardType::GLContext, aContentParent)
+{
+}
+
+void
+GLContextCrashGuard::Initialize()
+{
+ if (XRE_IsContentProcess()) {
+ // Disable the GL crash guard in content processes, since we're not going
+ // to lose the entire browser and we don't want to hinder WebGL availability.
+ return;
+ }
+
+ DriverCrashGuard::Initialize();
+}
+
+bool
+GLContextCrashGuard::UpdateEnvironment()
+{
+ static bool checked = false;
+ static bool changed = false;
+
+ if (checked) {
+ return changed;
+ }
+
+ checked = true;
+
+#if defined(XP_WIN)
+ changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-d3d11",
+ gfxPrefs::WebGLANGLEForceD3D11());
+ changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-try-d3d11",
+ gfxPrefs::WebGLANGLETryD3D11());
+ changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-warp",
+ gfxPrefs::WebGLANGLEForceWARP());
+ changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle",
+ FeatureEnabled(nsIGfxInfo::FEATURE_WEBGL_ANGLE, false));
+ changed |= CheckAndUpdateBoolPref("gfx.driver-init.direct3d11-angle",
+ FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, false));
+#endif
+
+ return changed;
+}
+
+void
+GLContextCrashGuard::LogCrashRecovery()
+{
+ gfxCriticalNote << "GLContext just crashed.";
+}
+
+void
+GLContextCrashGuard::LogFeatureDisabled()
+{
+ gfxCriticalNote << "GLContext remains enabled despite a previous crash.";
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/src/DriverCrashGuard.h b/system/graphics/src/DriverCrashGuard.h
new file mode 100644
index 000000000..8db9287c9
--- /dev/null
+++ b/system/graphics/src/DriverCrashGuard.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef gfx_src_DriverCrashGuard_h__
+#define gfx_src_DriverCrashGuard_h__
+
+#include "nsCOMPtr.h"
+#include "nsIGfxInfo.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "mozilla/Function.h"
+#include <string>
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+namespace gfx {
+
+enum class DriverInitStatus
+{
+ // Drivers have not been initialized yet.
+ Unknown,
+
+ // We're attempting to initialize drivers.
+ Attempting,
+
+ // Drivers were successfully initialized last run.
+ Okay,
+
+ // We crashed during driver initialization, and have restarted.
+ Crashed
+};
+
+enum class CrashGuardType : uint32_t
+{
+ D3D11Layers,
+ D3D9Video,
+ GLContext,
+ D3D11Video,
+ // Add new entries above this line, update the name array in
+ // DriverCrashGuard.cpp, and make sure to add an entry in
+ // ContentParent.cpp.
+
+ NUM_TYPES
+};
+
+// DriverCrashGuard is used to detect crashes at graphics driver callsites.
+//
+// If the graphics environment is unrecognized or has changed since the last
+// session, the crash guard will activate and will detect any crashes within
+// the scope of the guard object.
+//
+// If a callsite has a previously encountered crash, and the environment has
+// not changed since the last session, then the guard will set a status flag
+// indicating that the driver should not be used.
+class DriverCrashGuard
+{
+public:
+ DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent);
+ virtual ~DriverCrashGuard();
+
+ bool Crashed();
+ void NotifyCrashed();
+
+ enum class Mode {
+ // Normal operation.
+ Normal,
+
+ // Acting as a proxy between the parent and child process.
+ Proxy
+ };
+
+ typedef mozilla::function<void(const char* aName, const char* aPrefName)>
+ CrashGuardCallback;
+ static void ForEachActiveCrashGuard(const CrashGuardCallback& aCallback);
+
+protected:
+ virtual void Initialize();
+ virtual bool UpdateEnvironment() = 0;
+ virtual void LogCrashRecovery() = 0;
+ virtual void LogFeatureDisabled() = 0;
+
+ // Helper functions.
+ bool FeatureEnabled(int aFeature, bool aDefault=true);
+ bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue);
+ bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue);
+ std::string GetFullPrefName(const char* aPref);
+
+private:
+ // Either process.
+ void InitializeIfNeeded();
+ bool CheckOrRefreshEnvironment();
+ bool UpdateBaseEnvironment();
+ DriverInitStatus GetStatus() const;
+
+ // Parent process only.
+ nsCOMPtr<nsIFile> GetGuardFile();
+ bool RecoverFromCrash();
+ void ActivateGuard();
+ void FlushPreferences();
+ void SetStatus(DriverInitStatus aStatus);
+
+private:
+ CrashGuardType mType;
+ Mode mMode;
+ bool mInitialized;
+ bool mGuardActivated;
+ bool mCrashDetected;
+ nsCOMPtr<nsIFile> mGuardFile;
+
+protected:
+ nsCString mStatusPref;
+ nsCOMPtr<nsIGfxInfo> mGfxInfo;
+};
+
+class D3D11LayersCrashGuard final : public DriverCrashGuard
+{
+ public:
+ explicit D3D11LayersCrashGuard(dom::ContentParent* aContentParent = nullptr);
+
+ protected:
+ void Initialize() override;
+ bool UpdateEnvironment() override;
+ void LogCrashRecovery() override;
+ void LogFeatureDisabled() override;
+};
+
+class D3D9VideoCrashGuard final : public DriverCrashGuard
+{
+ public:
+ explicit D3D9VideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
+
+ protected:
+ bool UpdateEnvironment() override;
+ void LogCrashRecovery() override;
+ void LogFeatureDisabled() override;
+};
+
+class D3D11VideoCrashGuard final : public DriverCrashGuard
+{
+ public:
+ explicit D3D11VideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
+
+ protected:
+ bool UpdateEnvironment() override;
+ void LogCrashRecovery() override;
+ void LogFeatureDisabled() override;
+};
+
+class GLContextCrashGuard final : public DriverCrashGuard
+{
+ public:
+ explicit GLContextCrashGuard(dom::ContentParent* aContentParent = nullptr);
+ void Initialize() override;
+
+ protected:
+ bool UpdateEnvironment() override;
+ void LogCrashRecovery() override;
+ void LogFeatureDisabled() override;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // gfx_src_DriverCrashGuard_h__
+
diff --git a/system/graphics/src/FilterSupport.cpp b/system/graphics/src/FilterSupport.cpp
new file mode 100644
index 000000000..fed7b6879
--- /dev/null
+++ b/system/graphics/src/FilterSupport.cpp
@@ -0,0 +1,2185 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilterSupport.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Filters.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/PodOperations.h"
+
+#include "gfxContext.h"
+#include "gfxPattern.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+
+#include "nsMargin.h"
+
+// c = n / 255
+// c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f
+static const float glinearRGBTosRGBMap[256] = {
+ 0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f,
+ 0.194f, 0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f,
+ 0.278f, 0.286f, 0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f,
+ 0.339f, 0.346f, 0.352f, 0.359f, 0.365f, 0.371f, 0.378f, 0.383f,
+ 0.389f, 0.395f, 0.401f, 0.406f, 0.412f, 0.417f, 0.422f, 0.427f,
+ 0.433f, 0.438f, 0.443f, 0.448f, 0.452f, 0.457f, 0.462f, 0.466f,
+ 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f, 0.498f, 0.502f,
+ 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f, 0.534f,
+ 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f,
+ 0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f,
+ 0.596f, 0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f,
+ 0.622f, 0.625f, 0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f,
+ 0.647f, 0.650f, 0.653f, 0.656f, 0.659f, 0.662f, 0.665f, 0.668f,
+ 0.671f, 0.674f, 0.677f, 0.680f, 0.683f, 0.685f, 0.688f, 0.691f,
+ 0.694f, 0.697f, 0.699f, 0.702f, 0.705f, 0.708f, 0.710f, 0.713f,
+ 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f, 0.731f, 0.734f,
+ 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f, 0.754f,
+ 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f,
+ 0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f,
+ 0.795f, 0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f,
+ 0.814f, 0.816f, 0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f,
+ 0.832f, 0.834f, 0.836f, 0.838f, 0.840f, 0.843f, 0.845f, 0.847f,
+ 0.849f, 0.851f, 0.853f, 0.855f, 0.857f, 0.860f, 0.862f, 0.864f,
+ 0.866f, 0.868f, 0.870f, 0.872f, 0.874f, 0.876f, 0.878f, 0.880f,
+ 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f, 0.894f, 0.896f,
+ 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f, 0.912f,
+ 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f,
+ 0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f,
+ 0.945f, 0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f,
+ 0.959f, 0.961f, 0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f,
+ 0.974f, 0.975f, 0.977f, 0.979f, 0.981f, 0.983f, 0.984f, 0.986f,
+ 0.988f, 0.990f, 0.991f, 0.993f, 0.995f, 0.997f, 0.998f, 1.000f
+};
+
+// c = n / 255
+// c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f)
+static const float gsRGBToLinearRGBMap[256] = {
+ 0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f,
+ 0.002f, 0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f,
+ 0.005f, 0.006f, 0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f,
+ 0.009f, 0.010f, 0.010f, 0.011f, 0.012f, 0.012f, 0.013f, 0.014f,
+ 0.014f, 0.015f, 0.016f, 0.017f, 0.018f, 0.019f, 0.019f, 0.020f,
+ 0.021f, 0.022f, 0.023f, 0.024f, 0.025f, 0.026f, 0.027f, 0.028f,
+ 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f, 0.037f, 0.038f,
+ 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f, 0.050f,
+ 0.051f, 0.053f, 0.054f, 0.056f, 0.058f, 0.060f, 0.061f, 0.063f,
+ 0.065f, 0.067f, 0.068f, 0.070f, 0.072f, 0.074f, 0.076f, 0.078f,
+ 0.080f, 0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f,
+ 0.098f, 0.100f, 0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f,
+ 0.117f, 0.120f, 0.122f, 0.125f, 0.127f, 0.130f, 0.133f, 0.136f,
+ 0.138f, 0.141f, 0.144f, 0.147f, 0.150f, 0.153f, 0.156f, 0.159f,
+ 0.162f, 0.165f, 0.168f, 0.171f, 0.175f, 0.178f, 0.181f, 0.184f,
+ 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f, 0.209f, 0.212f,
+ 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f, 0.242f,
+ 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f,
+ 0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f,
+ 0.314f, 0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f,
+ 0.352f, 0.356f, 0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f,
+ 0.392f, 0.397f, 0.402f, 0.407f, 0.413f, 0.418f, 0.423f, 0.429f,
+ 0.434f, 0.440f, 0.445f, 0.451f, 0.456f, 0.462f, 0.468f, 0.474f,
+ 0.479f, 0.485f, 0.491f, 0.497f, 0.503f, 0.509f, 0.515f, 0.521f,
+ 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f, 0.565f, 0.571f,
+ 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f, 0.624f,
+ 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f,
+ 0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f,
+ 0.745f, 0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f,
+ 0.807f, 0.815f, 0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f,
+ 0.871f, 0.880f, 0.888f, 0.896f, 0.905f, 0.913f, 0.922f, 0.930f,
+ 0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f
+};
+
+namespace mozilla {
+namespace gfx {
+
+// Some convenience FilterNode creation functions.
+
+namespace FilterWrappers {
+
+ static already_AddRefed<FilterNode>
+ Unpremultiply(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
+ if (filter) {
+ filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Premultiply(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::PREMULTIPLY);
+ if (filter) {
+ filter->SetInput(IN_PREMULTIPLY_IN, aInput);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ LinearRGBToSRGB(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ SRGBToLinearRGB(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Crop(DrawTarget* aDT, FilterNode* aInputFilter, const IntRect& aRect)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CROP);
+ if (filter) {
+ filter->SetAttribute(ATT_CROP_RECT, Rect(aRect));
+ filter->SetInput(IN_CROP_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Offset(DrawTarget* aDT, FilterNode* aInputFilter, const IntPoint& aOffset)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (filter) {
+ filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(aOffset.x, aOffset.y));
+ filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation)
+ {
+ float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
+ float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
+ if (stdX == stdY) {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
+ if (filter) {
+ filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
+ filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+ RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+ RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+ if (filterH && filterV) {
+ filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
+ filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
+ filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
+ filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
+ filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
+ filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
+ return filterV.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Clear(DrawTarget* aDT)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
+ if (filter) {
+ filter->SetAttribute(ATT_FLOOD_COLOR, Color(0, 0, 0, 0));
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ ForSurface(DrawTarget* aDT, SourceSurface* aSurface,
+ const IntPoint& aSurfacePosition)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (filter) {
+ filter->SetAttribute(ATT_TRANSFORM_MATRIX,
+ Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
+ filter->SetInput(IN_TRANSFORM_IN, aSurface);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ ToAlpha(DrawTarget* aDT, FilterNode* aInput)
+ {
+ float zero = 0.0f;
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+} // namespace FilterWrappers
+
+// A class that wraps a FilterNode and handles conversion between different
+// color models. Create FilterCachedColorModels with your original filter and
+// the color model that this filter outputs in natively, and then call
+// ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
+// the specified colorModel.
+// Internally, this is achieved by wrapping the original FilterNode with
+// conversion FilterNodes. These filter nodes are cached in such a way that no
+// repeated or back-and-forth conversions happen.
+class FilterCachedColorModels
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
+ // aFilter can be null. In that case, ForColorModel will return a non-null
+ // completely transparent filter for all color models.
+ FilterCachedColorModels(DrawTarget* aDT,
+ FilterNode* aFilter,
+ ColorModel aOriginalColorModel);
+
+ // Get a FilterNode for the specified color model, guaranteed to be non-null.
+ already_AddRefed<FilterNode> ForColorModel(ColorModel aColorModel);
+
+ AlphaModel OriginalAlphaModel() const { return mOriginalColorModel.mAlphaModel; }
+
+private:
+ // Create the required FilterNode that will be cached by ForColorModel.
+ already_AddRefed<FilterNode> WrapForColorModel(ColorModel aColorModel);
+
+ RefPtr<DrawTarget> mDT;
+ ColorModel mOriginalColorModel;
+
+ // This array is indexed by ColorModel::ToIndex.
+ RefPtr<FilterNode> mFilterForColorModel[4];
+
+ ~FilterCachedColorModels() {}
+};
+
+FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT,
+ FilterNode* aFilter,
+ ColorModel aOriginalColorModel)
+ : mDT(aDT)
+ , mOriginalColorModel(aOriginalColorModel)
+{
+ if (aFilter) {
+ mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter;
+ } else {
+ RefPtr<FilterNode> clear = FilterWrappers::Clear(aDT);
+ mFilterForColorModel[0] = clear;
+ mFilterForColorModel[1] = clear;
+ mFilterForColorModel[2] = clear;
+ mFilterForColorModel[3] = clear;
+ }
+}
+
+already_AddRefed<FilterNode>
+FilterCachedColorModels::ForColorModel(ColorModel aColorModel)
+{
+ if (aColorModel == mOriginalColorModel) {
+ // Make sure to not call WrapForColorModel if our original filter node was
+ // null, because then we'd get an infinite recursion.
+ RefPtr<FilterNode> filter = mFilterForColorModel[mOriginalColorModel.ToIndex()];
+ return filter.forget();
+ }
+
+ if (!mFilterForColorModel[aColorModel.ToIndex()]) {
+ mFilterForColorModel[aColorModel.ToIndex()] = WrapForColorModel(aColorModel);
+ }
+ RefPtr<FilterNode> filter(mFilterForColorModel[aColorModel.ToIndex()]);
+ return filter.forget();
+}
+
+already_AddRefed<FilterNode>
+FilterCachedColorModels::WrapForColorModel(ColorModel aColorModel)
+{
+ // Convert one aspect at a time and recurse.
+ // Conversions between premultiplied / unpremultiplied color channels for the
+ // same color space can happen directly.
+ // Conversions between different color spaces can only happen on
+ // unpremultiplied color channels.
+
+ if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) {
+ RefPtr<FilterNode> unpre =
+ ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied));
+ return FilterWrappers::Premultiply(mDT, unpre);
+ }
+
+ MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied);
+ if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) {
+ RefPtr<FilterNode> premultiplied =
+ ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied));
+ return FilterWrappers::Unpremultiply(mDT, premultiplied);
+ }
+
+ RefPtr<FilterNode> unpremultipliedOriginal =
+ ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
+ if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
+ return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
+ }
+ return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
+}
+
+static const float identityMatrix[] =
+ { 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+// When aAmount == 0, the identity matrix is returned.
+// When aAmount == 1, aToMatrix is returned.
+// When aAmount > 1, an exaggerated version of aToMatrix is returned. This can
+// be useful in certain cases, such as producing a color matrix to oversaturate
+// an image.
+//
+// This function is a shortcut of a full matrix addition and a scalar multiply,
+// and it assumes that the following elements in aToMatrix are 0 and 1:
+// x x x 0 0
+// x x x 0 0
+// x x x 0 0
+// 0 0 0 1 0
+static void
+InterpolateFromIdentityMatrix(const float aToMatrix[20], float aAmount,
+ float aOutMatrix[20])
+{
+ PodCopy(aOutMatrix, identityMatrix, 20);
+
+ float oneMinusAmount = 1 - aAmount;
+
+ aOutMatrix[0] = aAmount * aToMatrix[0] + oneMinusAmount;
+ aOutMatrix[1] = aAmount * aToMatrix[1];
+ aOutMatrix[2] = aAmount * aToMatrix[2];
+
+ aOutMatrix[5] = aAmount * aToMatrix[5];
+ aOutMatrix[6] = aAmount * aToMatrix[6] + oneMinusAmount;
+ aOutMatrix[7] = aAmount * aToMatrix[7];
+
+ aOutMatrix[10] = aAmount * aToMatrix[10];
+ aOutMatrix[11] = aAmount * aToMatrix[11];
+ aOutMatrix[12] = aAmount * aToMatrix[12] + oneMinusAmount;
+}
+
+// Create a 4x5 color matrix for the different ways to specify color matrices
+// in SVG.
+static nsresult
+ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
+ float aOutMatrix[20])
+{
+ // Luminance coefficients.
+ static const float lumR = 0.2126f;
+ static const float lumG = 0.7152f;
+ static const float lumB = 0.0722f;
+
+ static const float oneMinusLumR = 1 - lumR;
+ static const float oneMinusLumG = 1 - lumG;
+ static const float oneMinusLumB = 1 - lumB;
+
+ static const float luminanceToAlphaMatrix[] =
+ { 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ lumR, lumG, lumB, 0, 0 };
+
+ static const float saturateMatrix[] =
+ { lumR, lumG, lumB, 0, 0,
+ lumR, lumG, lumB, 0, 0,
+ lumR, lumG, lumB, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+ static const float sepiaMatrix[] =
+ { 0.393f, 0.769f, 0.189f, 0, 0,
+ 0.349f, 0.686f, 0.168f, 0, 0,
+ 0.272f, 0.534f, 0.131f, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+ // Hue rotate specific coefficients.
+ static const float hueRotateR = 0.143f;
+ static const float hueRotateG = 0.140f;
+ static const float hueRotateB = 0.283f;
+
+ switch (aColorMatrixType) {
+
+ case SVG_FECOLORMATRIX_TYPE_MATRIX:
+ {
+ if (aValues.Length() != 20) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PodCopy(aOutMatrix, aValues.Elements(), 20);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_SATURATE:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ float s = aValues[0];
+
+ if (s < 0)
+ return NS_ERROR_FAILURE;
+
+ InterpolateFromIdentityMatrix(saturateMatrix, 1 - s, aOutMatrix);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ PodCopy(aOutMatrix, identityMatrix, 20);
+
+ float hueRotateValue = aValues[0];
+
+ float c = static_cast<float>(cos(hueRotateValue * M_PI / 180));
+ float s = static_cast<float>(sin(hueRotateValue * M_PI / 180));
+
+ aOutMatrix[0] = lumR + oneMinusLumR * c - lumR * s;
+ aOutMatrix[1] = lumG - lumG * c - lumG * s;
+ aOutMatrix[2] = lumB - lumB * c + oneMinusLumB * s;
+
+ aOutMatrix[5] = lumR - lumR * c + hueRotateR * s;
+ aOutMatrix[6] = lumG + oneMinusLumG * c + hueRotateG * s;
+ aOutMatrix[7] = lumB - lumB * c - hueRotateB * s;
+
+ aOutMatrix[10] = lumR - lumR * c - oneMinusLumR * s;
+ aOutMatrix[11] = lumG - lumG * c + lumG * s;
+ aOutMatrix[12] = lumB + oneMinusLumB * c + lumB * s;
+
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA:
+ {
+ PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_SEPIA:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ float amount = aValues[0];
+
+ if (amount < 0 || amount > 1)
+ return NS_ERROR_FAILURE;
+
+ InterpolateFromIdentityMatrix(sepiaMatrix, amount, aOutMatrix);
+ break;
+ }
+
+ default:
+ return NS_ERROR_FAILURE;
+
+ }
+
+ return NS_OK;
+}
+
+static void
+DisableAllTransfers(FilterNode* aTransferFilterNode)
+{
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true);
+}
+
+// Called for one channel at a time.
+// This function creates the required FilterNodes on demand and tries to
+// merge conversions of different channels into the same FilterNode if
+// possible.
+// There's a mismatch between the way SVG and the Moz2D API handle transfer
+// functions: In SVG, it's possible to specify a different transfer function
+// type for each color channel, but in Moz2D, a given transfer function type
+// applies to all color channels.
+//
+// @param aFunctionAttributes The attributes of the transfer function for this
+// channel.
+// @param aChannel The color channel that this function applies to, where
+// 0 = red, 1 = green, 2 = blue, 3 = alpha
+// @param aDT The DrawTarget that the FilterNodes should be created for.
+// @param aTableTransfer Existing FilterNode holders (which may still be
+// null) that the resulting FilterNodes from this
+// function will be stored in.
+//
+static void
+ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes,
+ int32_t aChannel,
+ DrawTarget* aDT,
+ RefPtr<FilterNode>& aTableTransfer,
+ RefPtr<FilterNode>& aDiscreteTransfer,
+ RefPtr<FilterNode>& aLinearTransfer,
+ RefPtr<FilterNode>& aGammaTransfer)
+{
+ static const TransferAtts disableAtt[4] = {
+ ATT_TRANSFER_DISABLE_R,
+ ATT_TRANSFER_DISABLE_G,
+ ATT_TRANSFER_DISABLE_B,
+ ATT_TRANSFER_DISABLE_A
+ };
+
+ RefPtr<FilterNode> filter;
+
+ uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType);
+
+ switch (type) {
+ case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 2)
+ return;
+
+ if (!aTableTransfer) {
+ aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
+ if (!aTableTransfer) {
+ return;
+ }
+ DisableAllTransfers(aTableTransfer);
+ }
+ filter = aTableTransfer;
+ static const TableTransferAtts tableAtt[4] = {
+ ATT_TABLE_TRANSFER_TABLE_R,
+ ATT_TABLE_TRANSFER_TABLE_G,
+ ATT_TABLE_TRANSFER_TABLE_B,
+ ATT_TABLE_TRANSFER_TABLE_A
+ };
+ filter->SetAttribute(disableAtt[aChannel], false);
+ filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 1)
+ return;
+
+ if (!aDiscreteTransfer) {
+ aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (!aDiscreteTransfer) {
+ return;
+ }
+ DisableAllTransfers(aDiscreteTransfer);
+ }
+ filter = aDiscreteTransfer;
+ static const DiscreteTransferAtts tableAtt[4] = {
+ ATT_DISCRETE_TRANSFER_TABLE_R,
+ ATT_DISCRETE_TRANSFER_TABLE_G,
+ ATT_DISCRETE_TRANSFER_TABLE_B,
+ ATT_DISCRETE_TRANSFER_TABLE_A
+ };
+ filter->SetAttribute(disableAtt[aChannel], false);
+ filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
+ {
+ static const LinearTransferAtts slopeAtt[4] = {
+ ATT_LINEAR_TRANSFER_SLOPE_R,
+ ATT_LINEAR_TRANSFER_SLOPE_G,
+ ATT_LINEAR_TRANSFER_SLOPE_B,
+ ATT_LINEAR_TRANSFER_SLOPE_A
+ };
+ static const LinearTransferAtts interceptAtt[4] = {
+ ATT_LINEAR_TRANSFER_INTERCEPT_R,
+ ATT_LINEAR_TRANSFER_INTERCEPT_G,
+ ATT_LINEAR_TRANSFER_INTERCEPT_B,
+ ATT_LINEAR_TRANSFER_INTERCEPT_A
+ };
+ if (!aLinearTransfer) {
+ aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
+ if (!aLinearTransfer) {
+ return;
+ }
+ DisableAllTransfers(aLinearTransfer);
+ }
+ filter = aLinearTransfer;
+ filter->SetAttribute(disableAtt[aChannel], false);
+ float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope);
+ float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
+ filter->SetAttribute(slopeAtt[aChannel], slope);
+ filter->SetAttribute(interceptAtt[aChannel], intercept);
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
+ {
+ static const GammaTransferAtts amplitudeAtt[4] = {
+ ATT_GAMMA_TRANSFER_AMPLITUDE_R,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_G,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_B,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_A
+ };
+ static const GammaTransferAtts exponentAtt[4] = {
+ ATT_GAMMA_TRANSFER_EXPONENT_R,
+ ATT_GAMMA_TRANSFER_EXPONENT_G,
+ ATT_GAMMA_TRANSFER_EXPONENT_B,
+ ATT_GAMMA_TRANSFER_EXPONENT_A
+ };
+ static const GammaTransferAtts offsetAtt[4] = {
+ ATT_GAMMA_TRANSFER_OFFSET_R,
+ ATT_GAMMA_TRANSFER_OFFSET_G,
+ ATT_GAMMA_TRANSFER_OFFSET_B,
+ ATT_GAMMA_TRANSFER_OFFSET_A
+ };
+ if (!aGammaTransfer) {
+ aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
+ if (!aGammaTransfer) {
+ return;
+ }
+ DisableAllTransfers(aGammaTransfer);
+ }
+ filter = aGammaTransfer;
+ filter->SetAttribute(disableAtt[aChannel], false);
+ float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude);
+ float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent);
+ float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
+ filter->SetAttribute(amplitudeAtt[aChannel], amplitude);
+ filter->SetAttribute(exponentAtt[aChannel], exponent);
+ filter->SetAttribute(offsetAtt[aChannel], offset);
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ default:
+ break;
+ }
+}
+
+const int32_t kMorphologyMaxRadius = 100000;
+
+// Handle the different primitive description types and create the necessary
+// FilterNode(s) for each.
+// Returns nullptr for invalid filter primitives. This should be interpreted as
+// transparent black by the caller.
+// aSourceRegions contains the filter primitive subregions of the source
+// primitives; only needed for eTile primitives.
+// aInputImages carries additional surfaces that are used by eImage primitives.
+static already_AddRefed<FilterNode>
+FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription,
+ DrawTarget* aDT,
+ nsTArray<RefPtr<FilterNode> >& aSources,
+ nsTArray<IntRect>& aSourceRegions,
+ nsTArray<RefPtr<SourceSurface>>& aInputImages)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ return nullptr;
+
+ case PrimitiveType::Blend:
+ {
+ uint32_t mode = atts.GetUint(eBlendBlendmode);
+ RefPtr<FilterNode> filter;
+ if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
+ return nullptr;
+ }
+ if (mode == SVG_FEBLEND_MODE_NORMAL) {
+ filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ } else {
+ filter = aDT->CreateFilter(FilterType::BLEND);
+ if (!filter) {
+ return nullptr;
+ }
+ static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
+ 0,
+ 0,
+ BLEND_MODE_MULTIPLY,
+ BLEND_MODE_SCREEN,
+ BLEND_MODE_DARKEN,
+ BLEND_MODE_LIGHTEN,
+ BLEND_MODE_OVERLAY,
+ BLEND_MODE_COLOR_DODGE,
+ BLEND_MODE_COLOR_BURN,
+ BLEND_MODE_HARD_LIGHT,
+ BLEND_MODE_SOFT_LIGHT,
+ BLEND_MODE_DIFFERENCE,
+ BLEND_MODE_EXCLUSION,
+ BLEND_MODE_HUE,
+ BLEND_MODE_SATURATION,
+ BLEND_MODE_COLOR,
+ BLEND_MODE_LUMINOSITY
+ };
+ filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
+ // The correct input order for both software and D2D filters is flipped
+ // from our source order, so flip here.
+ filter->SetInput(IN_BLEND_IN, aSources[1]);
+ filter->SetInput(IN_BLEND_IN2, aSources[0]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::ColorMatrix:
+ {
+ float colorMatrix[20];
+ uint32_t type = atts.GetUint(eColorMatrixType);
+ const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
+ if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix)) ||
+ PodEqual(colorMatrix, identityMatrix)) {
+ RefPtr<FilterNode> filter(aSources[0]);
+ return filter.forget();
+ }
+ Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15],
+ colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16],
+ colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17],
+ colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18],
+ colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COLOR_MATRIX);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
+ filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT);
+ filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = radii.width;
+ int32_t ry = radii.height;
+ if (rx < 0 || ry < 0) {
+ // XXX SVGContentUtils::ReportToConsole()
+ return nullptr;
+ }
+ if (rx == 0 && ry == 0) {
+ return nullptr;
+ }
+
+ // Clamp radii to prevent completely insane values:
+ rx = std::min(rx, kMorphologyMaxRadius);
+ ry = std::min(ry, kMorphologyMaxRadius);
+
+ MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ?
+ MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE;
+
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::MORPHOLOGY);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
+ filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
+ filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Flood:
+ {
+ Color color = atts.GetColor(eFloodColor);
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_FLOOD_COLOR, color);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Tile:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TILE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]);
+ filter->SetInput(IN_TILE_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::ComponentTransfer:
+ {
+ RefPtr<FilterNode> filters[4]; // one for each FILTER_*_TRANSFER type
+ static const AttributeName componentFunctionNames[4] = {
+ eComponentTransferFunctionR,
+ eComponentTransferFunctionG,
+ eComponentTransferFunctionB,
+ eComponentTransferFunctionA
+ };
+ for (int32_t i = 0; i < 4; i++) {
+ AttributeMap functionAttributes =
+ atts.GetAttributeMap(componentFunctionNames[i]);
+ ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT,
+ filters[0], filters[1], filters[2], filters[3]);
+ }
+
+ // Connect all used filters nodes.
+ RefPtr<FilterNode> lastFilter = aSources[0];
+ for (int32_t i = 0; i < 4; i++) {
+ if (filters[i]) {
+ filters[i]->SetInput(0, lastFilter);
+ lastFilter = filters[i];
+ }
+ }
+
+ return lastFilter.forget();
+ }
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize));
+ const nsTArray<float>& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix);
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX,
+ matrix.Elements(), matrix.Length());
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
+ atts.GetFloat(eConvolveMatrixDivisor));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS,
+ atts.GetFloat(eConvolveMatrixBias));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET,
+ atts.GetIntPoint(eConvolveMatrixTarget));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT,
+ aSourceRegions[0]);
+ uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode);
+ static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = {
+ EDGE_MODE_NONE, // SVG_EDGEMODE_UNKNOWN
+ EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE
+ EDGE_MODE_WRAP, // SVG_EDGEMODE_WRAP
+ EDGE_MODE_NONE // SVG_EDGEMODE_NONE
+ };
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]);
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
+ atts.GetSize(eConvolveMatrixKernelUnitLength));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
+ atts.GetBool(eConvolveMatrixPreserveAlpha));
+ filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Offset:
+ {
+ return FilterWrappers::Offset(aDT, aSources[0],
+ atts.GetIntPoint(eOffsetOffset));
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE,
+ atts.GetFloat(eDisplacementMapScale));
+ static const uint8_t channel[SVG_CHANNEL_A+1] = {
+ COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN
+ COLOR_CHANNEL_R, // SVG_CHANNEL_R
+ COLOR_CHANNEL_G, // SVG_CHANNEL_G
+ COLOR_CHANNEL_B, // SVG_CHANNEL_B
+ COLOR_CHANNEL_A // SVG_CHANNEL_A
+ };
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
+ (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]);
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
+ (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]);
+ filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]);
+ filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Turbulence:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TURBULENCE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
+ atts.GetSize(eTurbulenceBaseFrequency));
+ filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES,
+ atts.GetUint(eTurbulenceNumOctaves));
+ filter->SetAttribute(ATT_TURBULENCE_STITCHABLE,
+ atts.GetBool(eTurbulenceStitchable));
+ filter->SetAttribute(ATT_TURBULENCE_SEED,
+ (uint32_t)atts.GetFloat(eTurbulenceSeed));
+ static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = {
+ TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN
+ TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE
+ TURBULENCE_TYPE_TURBULENCE // SVG_TURBULENCE_TYPE_TURBULENCE
+ };
+ filter->SetAttribute(ATT_TURBULENCE_TYPE,
+ (uint32_t)type[atts.GetUint(eTurbulenceType)]);
+ filter->SetAttribute(ATT_TURBULENCE_RECT,
+ aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset));
+ return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
+ }
+
+ case PrimitiveType::Composite:
+ {
+ RefPtr<FilterNode> filter;
+ uint32_t op = atts.GetUint(eCompositeOperator);
+ if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
+ const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+ static const float allZero[4] = { 0, 0, 0, 0 };
+ filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
+ // All-zero coefficients sometimes occur in junk filters.
+ if (!filter ||
+ (coefficients.Length() == ArrayLength(allZero) &&
+ PodEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
+ coefficients.Elements(), coefficients.Length());
+ filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
+ filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
+ } else {
+ filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = {
+ COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
+ COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER
+ COMPOSITE_OPERATOR_IN, // SVG_FECOMPOSITE_OPERATOR_IN
+ COMPOSITE_OPERATOR_OUT, // SVG_FECOMPOSITE_OPERATOR_OUT
+ COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP
+ COMPOSITE_OPERATOR_XOR // SVG_FECOMPOSITE_OPERATOR_XOR
+ };
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
+ filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::Merge:
+ {
+ if (aSources.Length() == 0) {
+ return nullptr;
+ }
+ if (aSources.Length() == 1) {
+ RefPtr<FilterNode> filter(aSources[0]);
+ return filter.forget();
+ }
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
+ for (size_t i = 0; i < aSources.Length(); i++) {
+ filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ return FilterWrappers::GaussianBlur(aDT, aSources[0],
+ atts.GetSize(eGaussianBlurStdDeviation));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
+ RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
+ atts.GetSize(eDropShadowStdDeviation));
+ RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
+ atts.GetIntPoint(eDropShadowOffset));
+ RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
+ if (!flood) {
+ return nullptr;
+ }
+ Color color = atts.GetColor(eDropShadowColor);
+ if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
+ color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
+ gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
+ gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
+ color.a);
+ }
+ flood->SetAttribute(ATT_FLOOD_COLOR, color);
+
+ RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!composite) {
+ return nullptr;
+ }
+ composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
+ composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
+ composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
+
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
+ filter->SetInput(IN_COMPOSITE_IN_START, composite);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ bool isSpecular =
+ aDescription.Type() == PrimitiveType::SpecularLighting;
+
+ AttributeMap lightAttributes = atts.GetAttributeMap(eLightingLight);
+
+ if (lightAttributes.GetUint(eLightType) == eLightTypeNone) {
+ return nullptr;
+ }
+
+ enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
+
+ switch (lightAttributes.GetUint(eLightType)) {
+ case eLightTypePoint: lightType = POINT; break;
+ case eLightTypeSpot: lightType = SPOT; break;
+ case eLightTypeDistant: lightType = DISTANT; break;
+ }
+
+ static const FilterType filterType[2][DISTANT+1] = {
+ { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE },
+ { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR }
+ };
+ RefPtr<FilterNode> filter =
+ aDT->CreateFilter(filterType[isSpecular][lightType]);
+ if (!filter) {
+ return nullptr;
+ }
+
+ filter->SetAttribute(ATT_LIGHTING_COLOR,
+ atts.GetColor(eLightingColor));
+ filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE,
+ atts.GetFloat(eLightingSurfaceScale));
+ filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ atts.GetSize(eLightingKernelUnitLength));
+
+ if (isSpecular) {
+ filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ atts.GetFloat(eSpecularLightingSpecularConstant));
+ filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
+ atts.GetFloat(eSpecularLightingSpecularExponent));
+ } else {
+ filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
+ atts.GetFloat(eDiffuseLightingDiffuseConstant));
+ }
+
+ switch (lightType) {
+ case POINT:
+ filter->SetAttribute(ATT_POINT_LIGHT_POSITION,
+ lightAttributes.GetPoint3D(ePointLightPosition));
+ break;
+ case SPOT:
+ filter->SetAttribute(ATT_SPOT_LIGHT_POSITION,
+ lightAttributes.GetPoint3D(eSpotLightPosition));
+ filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT,
+ lightAttributes.GetPoint3D(eSpotLightPointsAt));
+ filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
+ lightAttributes.GetFloat(eSpotLightFocus));
+ filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ lightAttributes.GetFloat(eSpotLightLimitingConeAngle));
+ break;
+ case DISTANT:
+ filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH,
+ lightAttributes.GetFloat(eDistantLightAzimuth));
+ filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION,
+ lightAttributes.GetFloat(eDistantLightElevation));
+ break;
+ }
+
+ filter->SetInput(IN_LIGHTING_IN, aSources[0]);
+
+ return filter.forget();
+ }
+
+ case PrimitiveType::Image:
+ {
+ Matrix TM = atts.GetMatrix(eImageTransform);
+ if (!TM.Determinant()) {
+ return nullptr;
+ }
+
+ // Pull the image from the additional image list using the index that's
+ // stored in the primitive description.
+ RefPtr<SourceSurface> inputImage =
+ aInputImages[atts.GetUint(eImageInputIndex)];
+
+ RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (!transform) {
+ return nullptr;
+ }
+ transform->SetInput(IN_TRANSFORM_IN, inputImage);
+ transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
+ transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
+ return transform.forget();
+ }
+
+ case PrimitiveType::ToAlpha:
+ {
+ return FilterWrappers::ToAlpha(aDT, aSources[0]);
+ }
+
+ default:
+ return nullptr;
+ }
+}
+
+template<typename T>
+static const T&
+ElementForIndex(int32_t aIndex,
+ const nsTArray<T>& aPrimitiveElements,
+ const T& aSourceGraphicElement,
+ const T& aFillPaintElement,
+ const T& aStrokePaintElement)
+{
+ switch (aIndex) {
+ case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
+ case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
+ return aSourceGraphicElement;
+ case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
+ return aFillPaintElement;
+ case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
+ return aStrokePaintElement;
+ default:
+ MOZ_ASSERT(aIndex >= 0, "bad index");
+ return aPrimitiveElements[aIndex];
+ }
+}
+
+static AlphaModel
+InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
+ int32_t aInputIndex,
+ AlphaModel aOriginalAlphaModel)
+{
+ switch (aDescr.Type()) {
+ case PrimitiveType::Tile:
+ case PrimitiveType::Offset:
+ case PrimitiveType::ToAlpha:
+ return aOriginalAlphaModel;
+
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ return AlphaModel::Unpremultiplied;
+
+ case PrimitiveType::DisplacementMap:
+ return aInputIndex == 0 ?
+ AlphaModel::Premultiplied : AlphaModel::Unpremultiplied;
+
+ case PrimitiveType::ConvolveMatrix:
+ return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ?
+ AlphaModel::Unpremultiplied : AlphaModel::Premultiplied;
+
+ default:
+ return AlphaModel::Premultiplied;
+ }
+}
+
+static AlphaModel
+OutputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
+ const nsTArray<AlphaModel>& aInputAlphaModels)
+{
+ if (aInputAlphaModels.Length()) {
+ // For filters with inputs, the output is premultiplied if and only if the
+ // first input is premultiplied.
+ return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
+ }
+
+ // All filters without inputs produce premultiplied alpha.
+ return AlphaModel::Premultiplied;
+}
+
+// Returns the output FilterNode, in premultiplied sRGB space.
+static already_AddRefed<FilterNode>
+FilterNodeGraphFromDescription(DrawTarget* aDT,
+ const FilterDescription& aFilter,
+ const Rect& aResultNeededRect,
+ SourceSurface* aSourceGraphic,
+ const IntRect& aSourceGraphicRect,
+ SourceSurface* aFillPaint,
+ const IntRect& aFillPaintRect,
+ SourceSurface* aStrokePaint,
+ const IntRect& aStrokePaintRect,
+ nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+
+ RefPtr<FilterCachedColorModels> sourceFilters[4];
+ nsTArray<RefPtr<FilterCachedColorModels> > primitiveFilters;
+
+ for (size_t i = 0; i < primitives.Length(); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+
+ nsTArray<RefPtr<FilterNode> > inputFilterNodes;
+ nsTArray<IntRect> inputSourceRects;
+ nsTArray<AlphaModel> inputAlphaModels;
+
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ if (inputIndex < 0) {
+ inputSourceRects.AppendElement(descr.FilterSpaceBounds());
+ } else {
+ inputSourceRects.AppendElement(primitives[inputIndex].PrimitiveSubregion());
+ }
+
+ RefPtr<FilterCachedColorModels> inputFilter;
+ if (inputIndex >= 0) {
+ MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!");
+ inputFilter = primitiveFilters[inputIndex];
+ MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?");
+ } else {
+ int32_t sourceIndex = -inputIndex - 1;
+ MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
+ MOZ_ASSERT(sourceIndex < 4, "invalid source index");
+ inputFilter = sourceFilters[sourceIndex];
+ if (!inputFilter) {
+ RefPtr<FilterNode> sourceFilterNode;
+
+ nsTArray<SourceSurface*> primitiveSurfaces;
+ nsTArray<IntRect> primitiveSurfaceRects;
+ RefPtr<SourceSurface> surf =
+ ElementForIndex(inputIndex, primitiveSurfaces,
+ aSourceGraphic, aFillPaint, aStrokePaint);
+ IntRect surfaceRect =
+ ElementForIndex(inputIndex, primitiveSurfaceRects,
+ aSourceGraphicRect, aFillPaintRect, aStrokePaintRect);
+ if (surf) {
+ IntPoint offset = surfaceRect.TopLeft();
+ sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset);
+
+ // Clip the original SourceGraphic to the first filter region if the
+ // surface isn't already sized appropriately.
+ if ((inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic ||
+ inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) &&
+ !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) {
+ sourceFilterNode =
+ FilterWrappers::Crop(aDT, sourceFilterNode, descr.FilterSpaceBounds());
+ }
+
+ if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) {
+ sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode);
+ }
+ }
+
+ inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode,
+ ColorModel::PremulSRGB());
+ sourceFilters[sourceIndex] = inputFilter;
+ }
+ }
+ MOZ_ASSERT(inputFilter);
+
+ AlphaModel inputAlphaModel =
+ InputAlphaModelForPrimitive(descr, j, inputFilter->OriginalAlphaModel());
+ inputAlphaModels.AppendElement(inputAlphaModel);
+ ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel);
+ inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel));
+ }
+
+ RefPtr<FilterNode> primitiveFilterNode =
+ FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes,
+ inputSourceRects, aAdditionalImages);
+
+ if (primitiveFilterNode) {
+ primitiveFilterNode =
+ FilterWrappers::Crop(aDT, primitiveFilterNode, descr.PrimitiveSubregion());
+ }
+
+ ColorModel outputColorModel(descr.OutputColorSpace(),
+ OutputAlphaModelForPrimitive(descr, inputAlphaModels));
+ RefPtr<FilterCachedColorModels> primitiveFilter =
+ new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
+
+ primitiveFilters.AppendElement(primitiveFilter);
+ }
+
+ MOZ_RELEASE_ASSERT(!primitiveFilters.IsEmpty());
+ return primitiveFilters.LastElement()->ForColorModel(ColorModel::PremulSRGB());
+}
+
+// FilterSupport
+
+void
+FilterSupport::RenderFilterDescription(DrawTarget* aDT,
+ const FilterDescription& aFilter,
+ const Rect& aRenderRect,
+ SourceSurface* aSourceGraphic,
+ const IntRect& aSourceGraphicRect,
+ SourceSurface* aFillPaint,
+ const IntRect& aFillPaintRect,
+ SourceSurface* aStrokePaint,
+ const IntRect& aStrokePaintRect,
+ nsTArray<RefPtr<SourceSurface>>& aAdditionalImages,
+ const Point& aDestPoint,
+ const DrawOptions& aOptions)
+{
+ RefPtr<FilterNode> resultFilter =
+ FilterNodeGraphFromDescription(aDT, aFilter, aRenderRect,
+ aSourceGraphic, aSourceGraphicRect, aFillPaint, aFillPaintRect,
+ aStrokePaint, aStrokePaintRect, aAdditionalImages);
+ if (!resultFilter) {
+ gfxWarning() << "Filter is NULL.";
+ return;
+ }
+ aDT->DrawFilter(resultFilter, aRenderRect, aDestPoint, aOptions);
+}
+
+static nsIntRegion
+UnionOfRegions(const nsTArray<nsIntRegion>& aRegions)
+{
+ nsIntRegion result;
+ for (size_t i = 0; i < aRegions.Length(); i++) {
+ result.Or(result, aRegions[i]);
+ }
+ return result;
+}
+
+static int32_t
+InflateSizeForBlurStdDev(float aStdDev)
+{
+ double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
+ return uint32_t(floor(size + 0.5));
+}
+
+static nsIntRegion
+ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsTArray<nsIntRegion>& aInputChangeRegions)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ case PrimitiveType::Flood:
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ return nsIntRegion();
+
+ case PrimitiveType::Blend:
+ case PrimitiveType::Composite:
+ case PrimitiveType::Merge:
+ return UnionOfRegions(aInputChangeRegions);
+
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ case PrimitiveType::ToAlpha:
+ return aInputChangeRegions[0];
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ case PrimitiveType::Tile:
+ return aDescription.PrimitiveSubregion();
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ if (atts.GetUint(eConvolveMatrixEdgeMode) != EDGE_MODE_NONE) {
+ return aDescription.PrimitiveSubregion();
+ }
+ Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
+ IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
+ IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+ nsIntMargin m(ceil(kernelUnitLength.width * (target.x)),
+ ceil(kernelUnitLength.height * (target.y)),
+ ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
+ ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
+ return aInputChangeRegions[0].Inflated(m);
+ }
+
+ case PrimitiveType::Offset:
+ {
+ IntPoint offset = atts.GetIntPoint(eOffsetOffset);
+ return aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
+ return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+ nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+ Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ blurRegion.Or(blurRegion, aInputChangeRegions[0]);
+ return blurRegion;
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+ int32_t dx = ceil(kernelUnitLength.width);
+ int32_t dy = ceil(kernelUnitLength.height);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ default:
+ return nsIntRegion();
+ }
+}
+
+/* static */ nsIntRegion
+FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicChange,
+ const nsIntRegion& aFillPaintChange,
+ const nsIntRegion& aStrokePaintChange)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+
+ nsTArray<nsIntRegion> resultChangeRegions;
+
+ for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+
+ nsTArray<nsIntRegion> inputChangeRegions;
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion inputChangeRegion =
+ ElementForIndex(inputIndex, resultChangeRegions,
+ aSourceGraphicChange, aFillPaintChange,
+ aStrokePaintChange);
+ inputChangeRegions.AppendElement(inputChangeRegion);
+ }
+ nsIntRegion changeRegion =
+ ResultChangeRegionForPrimitive(descr, inputChangeRegions);
+ changeRegion.And(changeRegion, descr.PrimitiveSubregion());
+ resultChangeRegions.AppendElement(changeRegion);
+ }
+
+ MOZ_RELEASE_ASSERT(!resultChangeRegions.IsEmpty());
+ return resultChangeRegions[resultChangeRegions.Length() - 1];
+}
+
+static float
+ResultOfZeroUnderTransferFunction(const AttributeMap& aFunctionAttributes)
+{
+ switch (aFunctionAttributes.GetUint(eComponentTransferFunctionType)) {
+ case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 2) {
+ return 0.0f;
+ }
+ return tableValues[0];
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 1) {
+ return 0.0f;
+ }
+ return tableValues[0];
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
+ return aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
+ return aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ default:
+ return 0.0f;
+ }
+}
+
+nsIntRegion
+FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsTArray<nsIntRegion>& aInputExtents)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ return IntRect();
+
+ case PrimitiveType::Composite:
+ {
+ uint32_t op = atts.GetUint(eCompositeOperator);
+ if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
+ // The arithmetic composite primitive can draw outside the bounding
+ // box of its source images.
+ const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+ MOZ_ASSERT(coefficients.Length() == 4);
+
+ // The calculation is:
+ // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
+ nsIntRegion region;
+ if (coefficients[0] > 0.0f) {
+ region = aInputExtents[0].Intersect(aInputExtents[1]);
+ }
+ if (coefficients[1] > 0.0f) {
+ region.Or(region, aInputExtents[0]);
+ }
+ if (coefficients[2] > 0.0f) {
+ region.Or(region, aInputExtents[1]);
+ }
+ if (coefficients[3] > 0.0f) {
+ region = aDescription.PrimitiveSubregion();
+ }
+ return region;
+ }
+ if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
+ return aInputExtents[0].Intersect(aInputExtents[1]);
+ }
+ return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+ }
+
+ case PrimitiveType::Flood:
+ {
+ if (atts.GetColor(eFloodColor).a == 0.0f) {
+ return IntRect();
+ }
+ return aDescription.PrimitiveSubregion();
+ }
+
+ case PrimitiveType::ColorMatrix:
+ {
+ if (atts.GetUint(eColorMatrixType) == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
+ const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
+ if (values.Length() == 20 && values[19] > 0.0f) {
+ return aDescription.PrimitiveSubregion();
+ }
+ }
+ return aInputExtents[0];
+ }
+
+ case PrimitiveType::ComponentTransfer:
+ {
+ AttributeMap functionAttributes =
+ atts.GetAttributeMap(eComponentTransferFunctionA);
+ if (ResultOfZeroUnderTransferFunction(functionAttributes) > 0.0f) {
+ return aDescription.PrimitiveSubregion();
+ }
+ return aInputExtents[0];
+ }
+
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ return aDescription.PrimitiveSubregion();
+ }
+
+ case PrimitiveType::Morphology:
+ {
+ uint32_t op = atts.GetUint(eMorphologyOperator);
+ if (op == SVG_OPERATOR_ERODE) {
+ return aInputExtents[0];
+ }
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ default:
+ return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+ }
+}
+
+/* static */ nsIntRegion
+FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicExtents)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+ nsTArray<nsIntRegion> postFilterExtents;
+
+ for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+ nsIntRegion filterSpace = descr.FilterSpaceBounds();
+
+ nsTArray<nsIntRegion> inputExtents;
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion inputExtent =
+ ElementForIndex(inputIndex, postFilterExtents,
+ aSourceGraphicExtents, filterSpace, filterSpace);
+ inputExtents.AppendElement(inputExtent);
+ }
+ nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
+ extent.And(extent, descr.PrimitiveSubregion());
+ postFilterExtents.AppendElement(extent);
+ }
+
+ MOZ_RELEASE_ASSERT(!postFilterExtents.IsEmpty());
+ return postFilterExtents[postFilterExtents.Length() - 1];
+}
+
+static nsIntRegion
+SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsIntRegion& aResultNeededRegion,
+ int32_t aInputIndex)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Flood:
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
+ return nsIntRegion();
+
+ case PrimitiveType::Empty:
+ return nsIntRegion();
+
+ case PrimitiveType::Blend:
+ case PrimitiveType::Composite:
+ case PrimitiveType::Merge:
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ case PrimitiveType::ToAlpha:
+ return aResultNeededRegion;
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ case PrimitiveType::Tile:
+ return IntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
+ IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
+ IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+ nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
+ ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
+ ceil(kernelUnitLength.width * (target.x)),
+ ceil(kernelUnitLength.height * (target.y)));
+ return aResultNeededRegion.Inflated(m);
+ }
+
+ case PrimitiveType::Offset:
+ {
+ IntPoint offset = atts.GetIntPoint(eOffsetOffset);
+ return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ if (aInputIndex == 1) {
+ return aResultNeededRegion;
+ }
+ int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
+ return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+ nsIntRegion offsetRegion =
+ aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+ Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ blurRegion.Or(blurRegion, aResultNeededRegion);
+ return blurRegion;
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+ int32_t dx = ceil(kernelUnitLength.width);
+ int32_t dy = ceil(kernelUnitLength.height);
+ return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ default:
+ return nsIntRegion();
+ }
+
+}
+
+/* static */ void
+FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter,
+ const nsIntRegion& aResultNeededRegion,
+ nsIntRegion& aSourceGraphicNeededRegion,
+ nsIntRegion& aFillPaintNeededRegion,
+ nsIntRegion& aStrokePaintNeededRegion)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_ASSERT(!primitives.IsEmpty());
+ if (primitives.IsEmpty()) {
+ return;
+ }
+
+ nsTArray<nsIntRegion> primitiveNeededRegions;
+ primitiveNeededRegions.AppendElements(primitives.Length());
+
+ primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion;
+
+ for (int32_t i = primitives.Length() - 1; i >= 0; --i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+ nsIntRegion neededRegion = primitiveNeededRegions[i];
+ neededRegion.And(neededRegion, descr.PrimitiveSubregion());
+
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion* inputNeededRegion = const_cast<nsIntRegion*>(
+ &ElementForIndex(inputIndex, primitiveNeededRegions,
+ aSourceGraphicNeededRegion,
+ aFillPaintNeededRegion, aStrokePaintNeededRegion));
+ inputNeededRegion->Or(*inputNeededRegion,
+ SourceNeededRegionForPrimitive(descr, neededRegion, j));
+ }
+ }
+
+ // Clip original SourceGraphic to first filter region.
+ const FilterPrimitiveDescription& firstDescr = primitives[0];
+ aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
+ firstDescr.FilterSpaceBounds());
+}
+
+// FilterPrimitiveDescription
+
+FilterPrimitiveDescription::FilterPrimitiveDescription()
+ : mType(PrimitiveType::Empty)
+ , mOutputColorSpace(ColorSpace::SRGB)
+ , mIsTainted(false)
+{
+}
+
+FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType)
+ : mType(aType)
+ , mOutputColorSpace(ColorSpace::SRGB)
+ , mIsTainted(false)
+{
+}
+
+FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
+ : mType(aOther.mType)
+ , mAttributes(aOther.mAttributes)
+ , mInputPrimitives(aOther.mInputPrimitives)
+ , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion)
+ , mFilterSpaceBounds(aOther.mFilterSpaceBounds)
+ , mInputColorSpaces(aOther.mInputColorSpaces)
+ , mOutputColorSpace(aOther.mOutputColorSpace)
+ , mIsTainted(aOther.mIsTainted)
+{
+}
+
+FilterPrimitiveDescription&
+FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther)
+{
+ if (this != &aOther) {
+ mType = aOther.mType;
+ mAttributes = aOther.mAttributes;
+ mInputPrimitives = aOther.mInputPrimitives;
+ mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion;
+ mFilterSpaceBounds = aOther.mFilterSpaceBounds;
+ mInputColorSpaces = aOther.mInputColorSpaces;
+ mOutputColorSpace = aOther.mOutputColorSpace;
+ mIsTainted = aOther.mIsTainted;
+ }
+ return *this;
+}
+
+bool
+FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const
+{
+ return mType == aOther.mType &&
+ mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) &&
+ mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
+ mOutputColorSpace == aOther.mOutputColorSpace &&
+ mIsTainted == aOther.mIsTainted &&
+ mInputPrimitives == aOther.mInputPrimitives &&
+ mInputColorSpaces == aOther.mInputColorSpaces &&
+ mAttributes == aOther.mAttributes;
+}
+
+// FilterDescription
+
+bool
+FilterDescription::operator==(const FilterDescription& aOther) const
+{
+ return mPrimitives == aOther.mPrimitives;
+}
+
+// AttributeMap
+
+// A class that wraps different types for easy storage in a hashtable. Only
+// used by AttributeMap.
+struct FilterAttribute {
+ FilterAttribute(const FilterAttribute& aOther);
+ ~FilterAttribute();
+
+ bool operator==(const FilterAttribute& aOther) const;
+ bool operator!=(const FilterAttribute& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ AttributeType Type() const { return mType; }
+
+#define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel) \
+ explicit FilterAttribute(type aValue) \
+ : mType(AttributeType::e##typeLabel), m##typeLabel(aValue) \
+ {} \
+ type As##typeLabel() { \
+ MOZ_ASSERT(mType == AttributeType::e##typeLabel); \
+ return m##typeLabel; \
+ }
+
+#define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className) \
+ explicit FilterAttribute(const className& aValue) \
+ : mType(AttributeType::e##className), m##className(new className(aValue)) \
+ {} \
+ className As##className() { \
+ MOZ_ASSERT(mType == AttributeType::e##className); \
+ return *m##className; \
+ }
+
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(AttributeMap)
+
+#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC
+#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS
+
+ FilterAttribute(const float* aValue, uint32_t aLength)
+ : mType(AttributeType::eFloats)
+ {
+ mFloats = new nsTArray<float>();
+ mFloats->AppendElements(aValue, aLength);
+ }
+
+ const nsTArray<float>& AsFloats() const {
+ MOZ_ASSERT(mType == AttributeType::eFloats);
+ return *mFloats;
+ }
+
+private:
+ const AttributeType mType;
+
+ union {
+ bool mBool;
+ uint32_t mUint;
+ float mFloat;
+ Size* mSize;
+ IntSize* mIntSize;
+ IntPoint* mIntPoint;
+ Matrix* mMatrix;
+ Matrix5x4* mMatrix5x4;
+ Point3D* mPoint3D;
+ Color* mColor;
+ AttributeMap* mAttributeMap;
+ nsTArray<float>* mFloats;
+ };
+};
+
+FilterAttribute::FilterAttribute(const FilterAttribute& aOther)
+ : mType(aOther.mType)
+{
+ switch (mType) {
+ case AttributeType::eBool:
+ mBool = aOther.mBool;
+ break;
+ case AttributeType::eUint:
+ mUint = aOther.mUint;
+ break;
+ case AttributeType::eFloat:
+ mFloat = aOther.mFloat;
+ break;
+
+#define HANDLE_CLASS(className) \
+ case AttributeType::e##className: \
+ m##className = new className(*aOther.m##className); \
+ break;
+
+ HANDLE_CLASS(Size)
+ HANDLE_CLASS(IntSize)
+ HANDLE_CLASS(IntPoint)
+ HANDLE_CLASS(Matrix)
+ HANDLE_CLASS(Matrix5x4)
+ HANDLE_CLASS(Point3D)
+ HANDLE_CLASS(Color)
+ HANDLE_CLASS(AttributeMap)
+
+#undef HANDLE_CLASS
+
+ case AttributeType::eFloats:
+ mFloats = new nsTArray<float>(*aOther.mFloats);
+ break;
+ case AttributeType::Max:
+ break;
+ }
+}
+
+FilterAttribute::~FilterAttribute() {
+ switch (mType) {
+ case AttributeType::Max:
+ case AttributeType::eBool:
+ case AttributeType::eUint:
+ case AttributeType::eFloat:
+ break;
+
+#define HANDLE_CLASS(className) \
+ case AttributeType::e##className: \
+ delete m##className; \
+ break;
+
+ HANDLE_CLASS(Size)
+ HANDLE_CLASS(IntSize)
+ HANDLE_CLASS(IntPoint)
+ HANDLE_CLASS(Matrix)
+ HANDLE_CLASS(Matrix5x4)
+ HANDLE_CLASS(Point3D)
+ HANDLE_CLASS(Color)
+ HANDLE_CLASS(AttributeMap)
+
+#undef HANDLE_CLASS
+
+ case AttributeType::eFloats:
+ delete mFloats;
+ break;
+ }
+}
+
+bool
+FilterAttribute::operator==(const FilterAttribute& aOther) const
+{
+ if (mType != aOther.mType) {
+ return false;
+ }
+
+ switch (mType) {
+
+#define HANDLE_TYPE(typeName) \
+ case AttributeType::e##typeName: \
+ return m##typeName == aOther.m##typeName;
+
+ HANDLE_TYPE(Bool)
+ HANDLE_TYPE(Uint)
+ HANDLE_TYPE(Float)
+ HANDLE_TYPE(Size)
+ HANDLE_TYPE(IntSize)
+ HANDLE_TYPE(IntPoint)
+ HANDLE_TYPE(Matrix)
+ HANDLE_TYPE(Matrix5x4)
+ HANDLE_TYPE(Point3D)
+ HANDLE_TYPE(Color)
+ HANDLE_TYPE(AttributeMap)
+ HANDLE_TYPE(Floats)
+
+#undef HANDLE_TYPE
+
+ default:
+ return false;
+ }
+}
+
+typedef FilterAttribute Attribute;
+
+AttributeMap::AttributeMap()
+{
+}
+
+AttributeMap::~AttributeMap()
+{
+}
+
+AttributeMap::AttributeMap(const AttributeMap& aOther)
+{
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ mMap.Put(attributeName, new Attribute(*attribute));
+ }
+}
+
+AttributeMap&
+AttributeMap::operator=(const AttributeMap& aOther)
+{
+ if (this != &aOther) {
+ mMap.Clear();
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ mMap.Put(attributeName, new Attribute(*attribute));
+ }
+ }
+ return *this;
+}
+
+bool
+AttributeMap::operator==(const AttributeMap& aOther) const
+{
+ if (mMap.Count() != aOther.mMap.Count()) {
+ return false;
+ }
+
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ Attribute* matchingAttribute = mMap.Get(attributeName);
+ if (!matchingAttribute || *matchingAttribute != *attribute) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+uint32_t
+AttributeMap::Count() const
+{
+ return mMap.Count();
+}
+
+nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator
+AttributeMap::ConstIter() const
+{
+ return mMap.ConstIter();
+}
+
+/* static */ AttributeType
+AttributeMap::GetType(FilterAttribute* aAttribute)
+{
+ return aAttribute->Type();
+}
+
+#define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
+ type \
+ AttributeMap::Get##typeLabel(AttributeName aName) const { \
+ Attribute* value = mMap.Get(aName); \
+ return value ? value->As##typeLabel() : defaultValue; \
+ } \
+ void \
+ AttributeMap::Set(AttributeName aName, type aValue) { \
+ mMap.Remove(aName); \
+ mMap.Put(aName, new Attribute(aValue)); \
+ }
+
+#define MAKE_ATTRIBUTE_HANDLERS_CLASS(className) \
+ className \
+ AttributeMap::Get##className(AttributeName aName) const { \
+ Attribute* value = mMap.Get(aName); \
+ return value ? value->As##className() : className(); \
+ } \
+ void \
+ AttributeMap::Set(AttributeName aName, const className& aValue) { \
+ mMap.Remove(aName); \
+ mMap.Put(aName, new Attribute(aValue)); \
+ }
+
+MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false)
+MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0)
+MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Size)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Color)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(AttributeMap)
+
+#undef MAKE_ATTRIBUTE_HANDLERS_BASIC
+#undef MAKE_ATTRIBUTE_HANDLERS_CLASS
+
+const nsTArray<float>&
+AttributeMap::GetFloats(AttributeName aName) const
+{
+ Attribute* value = mMap.Get(aName);
+ if (!value) {
+ value = new Attribute(nullptr, 0);
+ mMap.Put(aName, value);
+ }
+ return value->AsFloats();
+}
+
+void
+AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength)
+{
+ mMap.Remove(aName);
+ mMap.Put(aName, new Attribute(aValues, aLength));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/src/FilterSupport.h b/system/graphics/src/FilterSupport.h
new file mode 100644
index 000000000..96a43d7cb
--- /dev/null
+++ b/system/graphics/src/FilterSupport.h
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __FilterSupport_h
+#define __FilterSupport_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/2D.h"
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+#include "nsRegion.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Morphology Operators
+const unsigned short SVG_OPERATOR_UNKNOWN = 0;
+const unsigned short SVG_OPERATOR_ERODE = 1;
+const unsigned short SVG_OPERATOR_DILATE = 2;
+
+// ColorMatrix types
+const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
+const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
+const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
+const unsigned short SVG_FECOLORMATRIX_TYPE_HUE_ROTATE = 3;
+const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA = 4;
+// ColorMatrix types for CSS filters
+const unsigned short SVG_FECOLORMATRIX_TYPE_SEPIA = 5;
+
+// ComponentTransfer types
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0;
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE = 2;
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4;
+const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5;
+
+// Blend Mode Values
+const unsigned short SVG_FEBLEND_MODE_UNKNOWN = 0;
+const unsigned short SVG_FEBLEND_MODE_NORMAL = 1;
+const unsigned short SVG_FEBLEND_MODE_MULTIPLY = 2;
+const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
+const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
+const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
+const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
+const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
+const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
+const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
+const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
+const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
+const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
+const unsigned short SVG_FEBLEND_MODE_HUE = 13;
+const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
+const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
+const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
+
+// Edge Mode Values
+const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
+const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
+const unsigned short SVG_EDGEMODE_WRAP = 2;
+const unsigned short SVG_EDGEMODE_NONE = 3;
+
+// Channel Selectors
+const unsigned short SVG_CHANNEL_UNKNOWN = 0;
+const unsigned short SVG_CHANNEL_R = 1;
+const unsigned short SVG_CHANNEL_G = 2;
+const unsigned short SVG_CHANNEL_B = 3;
+const unsigned short SVG_CHANNEL_A = 4;
+
+// Turbulence Types
+const unsigned short SVG_TURBULENCE_TYPE_UNKNOWN = 0;
+const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
+const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
+
+// Composite Operators
+const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
+const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
+
+enum AttributeName {
+ eBlendBlendmode = 0,
+ eMorphologyRadii,
+ eMorphologyOperator,
+ eColorMatrixType,
+ eColorMatrixValues,
+ eFloodColor,
+ eTileSourceRect,
+ eComponentTransferFunctionR,
+ eComponentTransferFunctionG,
+ eComponentTransferFunctionB,
+ eComponentTransferFunctionA,
+ eComponentTransferFunctionType,
+ eComponentTransferFunctionTableValues,
+ eComponentTransferFunctionSlope,
+ eComponentTransferFunctionIntercept,
+ eComponentTransferFunctionAmplitude,
+ eComponentTransferFunctionExponent,
+ eComponentTransferFunctionOffset,
+ eConvolveMatrixKernelSize,
+ eConvolveMatrixKernelMatrix,
+ eConvolveMatrixDivisor,
+ eConvolveMatrixBias,
+ eConvolveMatrixTarget,
+ eConvolveMatrixEdgeMode,
+ eConvolveMatrixKernelUnitLength,
+ eConvolveMatrixPreserveAlpha,
+ eOffsetOffset,
+ eDropShadowStdDeviation,
+ eDropShadowOffset,
+ eDropShadowColor,
+ eDisplacementMapScale,
+ eDisplacementMapXChannel,
+ eDisplacementMapYChannel,
+ eTurbulenceOffset,
+ eTurbulenceBaseFrequency,
+ eTurbulenceNumOctaves,
+ eTurbulenceSeed,
+ eTurbulenceStitchable,
+ eTurbulenceType,
+ eCompositeOperator,
+ eCompositeCoefficients,
+ eGaussianBlurStdDeviation,
+ eLightingLight,
+ eLightingSurfaceScale,
+ eLightingKernelUnitLength,
+ eLightingColor,
+ eDiffuseLightingDiffuseConstant,
+ eSpecularLightingSpecularConstant,
+ eSpecularLightingSpecularExponent,
+ eLightType,
+ eLightTypeNone,
+ eLightTypePoint,
+ eLightTypeSpot,
+ eLightTypeDistant,
+ ePointLightPosition,
+ eSpotLightPosition,
+ eSpotLightPointsAt,
+ eSpotLightFocus,
+ eSpotLightLimitingConeAngle,
+ eDistantLightAzimuth,
+ eDistantLightElevation,
+ eImageInputIndex,
+ eImageFilter,
+ eImageNativeSize,
+ eImageSubregion,
+ eImageTransform,
+ eLastAttributeName
+};
+
+class DrawTarget;
+class SourceSurface;
+struct FilterAttribute;
+
+enum class AttributeType {
+ eBool,
+ eUint,
+ eFloat,
+ eSize,
+ eIntSize,
+ eIntPoint,
+ eMatrix,
+ eMatrix5x4,
+ ePoint3D,
+ eColor,
+ eAttributeMap,
+ eFloats,
+ Max
+};
+
+// Limits
+const float kMaxStdDeviation = 500;
+
+// A class that stores values of different types, keyed by an attribute name.
+// The Get*() methods assert that they're called for the same type that the
+// attribute was Set() with.
+// AttributeMaps can be nested because AttributeMap is a valid attribute type.
+class AttributeMap final {
+public:
+ AttributeMap();
+ AttributeMap(const AttributeMap& aOther);
+ AttributeMap& operator=(const AttributeMap& aOther);
+ bool operator==(const AttributeMap& aOther) const;
+ bool operator!=(const AttributeMap& aOther) const
+ {
+ return !(*this == aOther);
+ }
+ ~AttributeMap();
+
+ void Set(AttributeName aName, bool aValue);
+ void Set(AttributeName aName, uint32_t aValue);
+ void Set(AttributeName aName, float aValue);
+ void Set(AttributeName aName, const Size& aValue);
+ void Set(AttributeName aName, const IntSize& aValue);
+ void Set(AttributeName aName, const IntPoint& aValue);
+ void Set(AttributeName aName, const Matrix& aValue);
+ void Set(AttributeName aName, const Matrix5x4& aValue);
+ void Set(AttributeName aName, const Point3D& aValue);
+ void Set(AttributeName aName, const Color& aValue);
+ void Set(AttributeName aName, const AttributeMap& aValue);
+ void Set(AttributeName aName, const float* aValues, int32_t aLength);
+
+ bool GetBool(AttributeName aName) const;
+ uint32_t GetUint(AttributeName aName) const;
+ float GetFloat(AttributeName aName) const;
+ Size GetSize(AttributeName aName) const;
+ IntSize GetIntSize(AttributeName aName) const;
+ IntPoint GetIntPoint(AttributeName aName) const;
+ Matrix GetMatrix(AttributeName aName) const;
+ Matrix5x4 GetMatrix5x4(AttributeName aName) const;
+ Point3D GetPoint3D(AttributeName aName) const;
+ Color GetColor(AttributeName aName) const;
+ AttributeMap GetAttributeMap(AttributeName aName) const;
+ const nsTArray<float>& GetFloats(AttributeName aName) const;
+
+ uint32_t Count() const;
+
+ nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator ConstIter() const;
+
+ static AttributeType GetType(FilterAttribute* aAttribute);
+
+private:
+ mutable nsClassHashtable<nsUint32HashKey, FilterAttribute> mMap;
+};
+
+enum class ColorSpace {
+ SRGB,
+ LinearRGB,
+ Max
+};
+
+enum class AlphaModel {
+ Unpremultiplied,
+ Premultiplied
+};
+
+class ColorModel {
+public:
+ static ColorModel PremulSRGB()
+ {
+ return ColorModel(ColorSpace::SRGB, AlphaModel::Premultiplied);
+ }
+
+ ColorModel(ColorSpace aColorSpace, AlphaModel aAlphaModel) :
+ mColorSpace(aColorSpace), mAlphaModel(aAlphaModel) {}
+ ColorModel() :
+ mColorSpace(ColorSpace::SRGB), mAlphaModel(AlphaModel::Premultiplied) {}
+ bool operator==(const ColorModel& aOther) const {
+ return mColorSpace == aOther.mColorSpace &&
+ mAlphaModel == aOther.mAlphaModel;
+ }
+
+ // Used to index FilterCachedColorModels::mFilterForColorModel.
+ uint8_t ToIndex() const
+ {
+ return (uint8_t(mColorSpace) << 1) + uint8_t(mAlphaModel);
+ }
+
+ ColorSpace mColorSpace;
+ AlphaModel mAlphaModel;
+};
+
+enum class PrimitiveType {
+ Empty = 0,
+ Blend,
+ Morphology,
+ ColorMatrix,
+ Flood,
+ Tile,
+ ComponentTransfer,
+ ConvolveMatrix,
+ Offset,
+ DisplacementMap,
+ Turbulence,
+ Composite,
+ Merge,
+ Image,
+ GaussianBlur,
+ DropShadow,
+ DiffuseLighting,
+ SpecularLighting,
+ ToAlpha,
+ Max
+};
+
+/**
+ * A data structure to carry attributes for a given primitive that's part of a
+ * filter. Will be serializable via IPDL, so it must not contain complex
+ * functionality.
+ * Used as part of a FilterDescription.
+ */
+class FilterPrimitiveDescription final {
+public:
+ enum {
+ kPrimitiveIndexSourceGraphic = -1,
+ kPrimitiveIndexSourceAlpha = -2,
+ kPrimitiveIndexFillPaint = -3,
+ kPrimitiveIndexStrokePaint = -4
+ };
+
+ FilterPrimitiveDescription();
+ explicit FilterPrimitiveDescription(PrimitiveType aType);
+ FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther);
+ FilterPrimitiveDescription& operator=(const FilterPrimitiveDescription& aOther);
+
+ PrimitiveType Type() const { return mType; }
+ void SetType(PrimitiveType aType) { mType = aType; }
+ const AttributeMap& Attributes() const { return mAttributes; }
+ AttributeMap& Attributes() { return mAttributes; }
+
+ IntRect PrimitiveSubregion() const { return mFilterPrimitiveSubregion; }
+ IntRect FilterSpaceBounds() const { return mFilterSpaceBounds; }
+ bool IsTainted() const { return mIsTainted; }
+
+ size_t NumberOfInputs() const { return mInputPrimitives.Length(); }
+ int32_t InputPrimitiveIndex(size_t aInputIndex) const
+ {
+ return aInputIndex < mInputPrimitives.Length() ?
+ mInputPrimitives[aInputIndex] : 0;
+ }
+
+ ColorSpace InputColorSpace(size_t aInputIndex) const
+ {
+ return aInputIndex < mInputColorSpaces.Length() ?
+ mInputColorSpaces[aInputIndex] : ColorSpace();
+ }
+
+ ColorSpace OutputColorSpace() const { return mOutputColorSpace; }
+
+ void SetPrimitiveSubregion(const IntRect& aRect)
+ {
+ mFilterPrimitiveSubregion = aRect;
+ }
+
+ void SetFilterSpaceBounds(const IntRect& aRect)
+ {
+ mFilterSpaceBounds = aRect;
+ }
+
+ void SetIsTainted(bool aIsTainted)
+ {
+ mIsTainted = aIsTainted;
+ }
+
+ void SetInputPrimitive(size_t aInputIndex, int32_t aInputPrimitiveIndex)
+ {
+ mInputPrimitives.EnsureLengthAtLeast(aInputIndex + 1);
+ mInputPrimitives[aInputIndex] = aInputPrimitiveIndex;
+ }
+
+ void SetInputColorSpace(size_t aInputIndex, ColorSpace aColorSpace)
+ {
+ mInputColorSpaces.EnsureLengthAtLeast(aInputIndex + 1);
+ mInputColorSpaces[aInputIndex] = aColorSpace;
+ }
+
+ void SetOutputColorSpace(const ColorSpace& aColorSpace)
+ {
+ mOutputColorSpace = aColorSpace;
+ }
+
+ bool operator==(const FilterPrimitiveDescription& aOther) const;
+ bool operator!=(const FilterPrimitiveDescription& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+private:
+ PrimitiveType mType;
+ AttributeMap mAttributes;
+ nsTArray<int32_t> mInputPrimitives;
+ IntRect mFilterPrimitiveSubregion;
+ IntRect mFilterSpaceBounds;
+ nsTArray<ColorSpace> mInputColorSpaces;
+ ColorSpace mOutputColorSpace;
+ bool mIsTainted;
+};
+
+/**
+ * A data structure that contains one or more FilterPrimitiveDescriptions.
+ * Designed to be serializable via IPDL, so it must not contain complex
+ * functionality.
+ */
+struct FilterDescription final {
+ FilterDescription() {}
+ explicit FilterDescription(const nsTArray<FilterPrimitiveDescription>& aPrimitives)
+ : mPrimitives(aPrimitives)
+ {}
+
+ bool operator==(const FilterDescription& aOther) const;
+ bool operator!=(const FilterDescription& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ nsTArray<FilterPrimitiveDescription> mPrimitives;
+};
+
+/**
+ * The methods of this class are not on FilterDescription because
+ * FilterDescription is designed as a simple value holder that can be used
+ * on any thread.
+ */
+class FilterSupport {
+public:
+
+ /**
+ * Draw the filter described by aFilter. All rect parameters are in filter
+ * space coordinates. aRenderRect specifies the part of the filter output
+ * that will be drawn at (0, 0) into the draw target aDT, subject to the
+ * current transform on aDT but with no additional scaling.
+ * The source surfaces must match their corresponding rect in size.
+ * aAdditionalImages carries the images that are referenced by the
+ * eImageInputIndex attribute on any image primitives in the filter.
+ */
+ static void
+ RenderFilterDescription(DrawTarget* aDT,
+ const FilterDescription& aFilter,
+ const Rect& aRenderRect,
+ SourceSurface* aSourceGraphic,
+ const IntRect& aSourceGraphicRect,
+ SourceSurface* aFillPaint,
+ const IntRect& aFillPaintRect,
+ SourceSurface* aStrokePaint,
+ const IntRect& aStrokePaintRect,
+ nsTArray<RefPtr<SourceSurface>>& aAdditionalImages,
+ const Point& aDestPoint,
+ const DrawOptions& aOptions = DrawOptions());
+
+ /**
+ * Computes the region that changes in the filter output due to a change in
+ * input. This is primarily needed when an individual piece of content inside
+ * a filtered container element changes.
+ */
+ static nsIntRegion
+ ComputeResultChangeRegion(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicChange,
+ const nsIntRegion& aFillPaintChange,
+ const nsIntRegion& aStrokePaintChange);
+
+ /**
+ * Computes the regions that need to be supplied in the filter inputs when
+ * painting aResultNeededRegion of the filter output.
+ */
+ static void
+ ComputeSourceNeededRegions(const FilterDescription& aFilter,
+ const nsIntRegion& aResultNeededRegion,
+ nsIntRegion& aSourceGraphicNeededRegion,
+ nsIntRegion& aFillPaintNeededRegion,
+ nsIntRegion& aStrokePaintNeededRegion);
+
+ /**
+ * Computes the size of the filter output.
+ */
+ static nsIntRegion
+ ComputePostFilterExtents(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicExtents);
+
+ /**
+ * Computes the size of a single FilterPrimitiveDescription's output given a
+ * set of input extents.
+ */
+ static nsIntRegion
+ PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsTArray<nsIntRegion>& aInputExtents);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // __FilterSupport_h
diff --git a/system/graphics/src/PingPongRegion.h b/system/graphics/src/PingPongRegion.h
new file mode 100644
index 000000000..d3bdcae1b
--- /dev/null
+++ b/system/graphics/src/PingPongRegion.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PingPongRegion_h__
+#define PingPongRegion_h__
+
+/* This class uses a pair of regions and swaps between them while
+ * accumulating to avoid the heap allocations associated with
+ * modifying a region in place.
+ *
+ * It is sizeof(T)*2 + sizeof(T*) and can use end up using
+ * approximately double the amount of memory as using single
+ * region so use it sparingly.
+ */
+
+template <typename T>
+class PingPongRegion
+{
+ typedef typename T::RectType RectType;
+public:
+ PingPongRegion()
+ {
+ rgn = &rgn1;
+ }
+
+ void SubOut(const RectType& aOther)
+ {
+ T* nextRgn = nextRegion();
+ nextRgn->Sub(*rgn, aOther);
+ rgn = nextRgn;
+ }
+
+ void OrWith(const RectType& aOther)
+ {
+ T* nextRgn = nextRegion();
+ nextRgn->Or(*rgn, aOther);
+ rgn = nextRgn;
+ }
+
+ T& Region()
+ {
+ return *rgn;
+ }
+
+private:
+
+ T* nextRegion()
+ {
+ if (rgn == &rgn1) {
+ return &rgn2;
+ } else {
+ return &rgn1;
+ }
+ }
+
+ T* rgn;
+ T rgn1;
+ T rgn2;
+};
+
+#endif
diff --git a/system/graphics/src/RegionBuilder.h b/system/graphics/src/RegionBuilder.h
new file mode 100644
index 000000000..c8bcd21b1
--- /dev/null
+++ b/system/graphics/src/RegionBuilder.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RegionBuilder_h__
+#define RegionBuilder_h__
+
+#include <nsTArray.h>
+
+template <typename RegionType>
+class RegionBuilder
+{
+public:
+ typedef typename RegionType::RectType RectType;
+
+ RegionBuilder()
+ {}
+
+ void OrWith(const RectType& aRect) {
+ pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
+ mRects.AppendElement(box);
+ }
+
+ RegionType ToRegion() const {
+ return RegionType(mRects);
+ }
+
+private:
+ nsTArray<pixman_box32_t> mRects;
+};
+
+#endif // RegionBuilder_h__
diff --git a/system/graphics/src/TiledRegion.cpp b/system/graphics/src/TiledRegion.cpp
new file mode 100644
index 000000000..1d3d50a02
--- /dev/null
+++ b/system/graphics/src/TiledRegion.cpp
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TiledRegion.h"
+
+#include <algorithm>
+
+#include "mozilla/fallible.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const int32_t kTileSize = 256;
+static const size_t kMaxTiles = 1000;
+
+/**
+ * TiledRegionImpl stores an array of non-empty rectangles (pixman_box32_ts) to
+ * represent the region. Each rectangle is contained in a single tile;
+ * rectangles never cross tile boundaries. The rectangles are sorted by their
+ * tile's origin in top-to-bottom, left-to-right order.
+ * (Note that this can mean that a rectangle r1 can come before another
+ * rectangle r2 even if r2.y1 < r1.y1, as long as the two rects are in the same
+ * row of tiles and r1.x1 < r2.x1.)
+ * Empty tiles take up no space in the array - there is no rectangle stored for
+ * them. As a result, any algorithm that needs to deal with empty tiles will
+ * iterate through the mRects array and compare the positions of two
+ * consecutive rects to figure out whether there are any empty tiles between
+ * them.
+ */
+
+static pixman_box32_t
+IntersectionOfNonEmptyBoxes(const pixman_box32_t& aBox1,
+ const pixman_box32_t& aBox2)
+{
+ return pixman_box32_t {
+ std::max(aBox1.x1, aBox2.x1),
+ std::max(aBox1.y1, aBox2.y1),
+ std::min(aBox1.x2, aBox2.x2),
+ std::min(aBox1.y2, aBox2.y2)
+ };
+}
+
+// A TileIterator points to a specific tile inside a certain tile range, or to
+// the end of the tile range. Advancing a TileIterator will move to the next
+// tile inside the range (or to the range end). The next tile is either the
+// tile to the right of the current one, or the first tile of the next tile
+// row if the current tile is already the last tile in the row.
+class TileIterator {
+public:
+ TileIterator(const pixman_box32_t& aTileBounds, const IntPoint& aPosition)
+ : mTileBounds(aTileBounds)
+ , mPos(aPosition)
+ {}
+
+ bool operator!=(const TileIterator& aOther) { return mPos != aOther.mPos; }
+ bool operator==(const TileIterator& aOther) { return mPos == aOther.mPos; }
+
+ IntPoint operator*() const { return mPos; }
+
+ const TileIterator& operator++() {
+ mPos.x += kTileSize;
+ if (mPos.x >= mTileBounds.x2) {
+ mPos.x = mTileBounds.x1;
+ mPos.y += kTileSize;
+ }
+ return *this;
+ }
+
+ TileIterator& operator=(const IntPoint& aPosition)
+ {
+ mPos = aPosition;
+ return *this;
+ }
+
+ bool IsBeforeTileContainingPoint(const IntPoint& aPoint) const
+ {
+ return (mPos.y + kTileSize) <= aPoint.y ||
+ (mPos.y <= aPoint.y && (mPos.x + kTileSize) <= aPoint.x);
+ }
+
+ bool IsAtTileContainingPoint(const IntPoint& aPoint) const
+ {
+ return mPos.y <= aPoint.y && aPoint.y < (mPos.y + kTileSize) &&
+ mPos.x <= aPoint.x && aPoint.x < (mPos.x + kTileSize);
+
+ }
+
+ pixman_box32_t IntersectionWith(const pixman_box32_t& aRect) const
+ {
+ pixman_box32_t tile = { mPos.x, mPos.y,
+ mPos.x + kTileSize, mPos.y + kTileSize };
+ return IntersectionOfNonEmptyBoxes(tile, aRect);
+ }
+
+private:
+ const pixman_box32_t& mTileBounds;
+ IntPoint mPos;
+};
+
+// A TileRange describes a range of tiles contained inside a certain tile
+// bounds (which is a rectangle that includes all tiles that you're
+// interested in). The tile range can start and end at any point inside a
+// tile row.
+// The tile range end is described by the tile that starts at the bottom
+// left corner of the tile bounds, i.e. the first tile under the tile
+// bounds.
+class TileRange {
+public:
+ // aTileBounds, aStart and aEnd need to be aligned with the tile grid.
+ TileRange(const pixman_box32_t& aTileBounds,
+ const IntPoint& aStart, const IntPoint& aEnd)
+ : mTileBounds(aTileBounds)
+ , mStart(aStart)
+ , mEnd(aEnd)
+ {}
+ // aTileBounds needs to be aligned with the tile grid.
+ explicit TileRange(const pixman_box32_t& aTileBounds)
+ : mTileBounds(aTileBounds)
+ , mStart(mTileBounds.x1, mTileBounds.y1)
+ , mEnd(mTileBounds.x1, mTileBounds.y2)
+ {}
+
+ TileIterator Begin() const { return TileIterator(mTileBounds, mStart); }
+ TileIterator End() const { return TileIterator(mTileBounds, mEnd); }
+
+ // The number of tiles in this tile range.
+ size_t Length() const
+ {
+ if (mEnd.y == mStart.y) {
+ return (mEnd.x - mStart.x) / kTileSize;
+ }
+ int64_t numberOfFullRows = (((int64_t)mEnd.y - (int64_t)mStart.y) / kTileSize) - 1;
+ int64_t tilesInFirstRow = ((int64_t)mTileBounds.x2 - (int64_t)mStart.x) / kTileSize;
+ int64_t tilesInLastRow = ((int64_t)mEnd.x - (int64_t)mTileBounds.x1) / kTileSize;
+ int64_t tilesInFullRow = ((int64_t)mTileBounds.x2 - (int64_t)mTileBounds.x1) / kTileSize;
+ int64_t total = tilesInFirstRow + (tilesInFullRow * numberOfFullRows) + tilesInLastRow;
+ MOZ_ASSERT(total > 0);
+ // The total may be larger than what fits in a size_t, so clamp it to
+ // SIZE_MAX in that case.
+ return ((uint64_t)total > (uint64_t)SIZE_MAX) ? SIZE_MAX : (size_t)total;
+ }
+
+ // If aTileOrigin does not describe a tile inside our tile bounds, move it
+ // to the next tile that you'd encounter by "advancing" a tile iterator
+ // inside these tile bounds. If aTileOrigin is after the last tile inside
+ // our tile bounds, move it to the range end tile.
+ // The result of this method is a valid end tile for a tile range with our
+ // tile bounds.
+ IntPoint MoveIntoBounds(const IntPoint& aTileOrigin) const
+ {
+ IntPoint p = aTileOrigin;
+ if (p.x < mTileBounds.x1) {
+ p.x = mTileBounds.x1;
+ } else if (p.x >= mTileBounds.x2) {
+ p.x = mTileBounds.x1;
+ p.y += kTileSize;
+ }
+ if (p.y < mTileBounds.y1) {
+ p.y = mTileBounds.y1;
+ p.x = mTileBounds.x1;
+ } else if (p.y >= mTileBounds.y2) {
+ // There's only one valid state after the end of the tile range, and that's
+ // the bottom left point of the tile bounds.
+ p.x = mTileBounds.x1;
+ p.y = mTileBounds.y2;
+ }
+ return p;
+ }
+
+private:
+ const pixman_box32_t& mTileBounds;
+ const IntPoint mStart;
+ const IntPoint mEnd;
+};
+
+static IntPoint
+TileContainingPoint(const IntPoint& aPoint)
+{
+ return IntPoint(RoundDownToMultiple(aPoint.x, kTileSize),
+ RoundDownToMultiple(aPoint.y, kTileSize));
+}
+
+enum class IterationAction : uint8_t {
+ CONTINUE,
+ STOP
+};
+
+enum class IterationEndReason : uint8_t {
+ NOT_STOPPED,
+ STOPPED
+};
+
+template<
+ typename HandleEmptyTilesFunction,
+ typename HandleNonEmptyTileFunction,
+ typename RectArrayT>
+IterationEndReason ProcessIntersectedTiles(const pixman_box32_t& aRect,
+ RectArrayT& aRectArray,
+ HandleEmptyTilesFunction aHandleEmptyTiles,
+ HandleNonEmptyTileFunction aHandleNonEmptyTile)
+{
+ pixman_box32_t tileBounds = {
+ RoundDownToMultiple(aRect.x1, kTileSize),
+ RoundDownToMultiple(aRect.y1, kTileSize),
+ RoundUpToMultiple(aRect.x2, kTileSize),
+ RoundUpToMultiple(aRect.y2, kTileSize)
+ };
+ if (tileBounds.x2 < tileBounds.x1 || tileBounds.y2 < tileBounds.y1) {
+ // RoundUpToMultiple probably overflowed. Bail out.
+ return IterationEndReason::STOPPED;
+ }
+
+ TileRange tileRange(tileBounds);
+ TileIterator rangeEnd = tileRange.End();
+
+ // tileIterator points to the next tile in tileRange, or to rangeEnd if we're
+ // done.
+ TileIterator tileIterator = tileRange.Begin();
+
+ // We iterate over the rectangle array. Depending on the position of the
+ // rectangle we encounter, we may need to advance tileIterator by zero, one,
+ // or more tiles:
+ // - Zero if the rectangle we encountered is outside the tiles that
+ // intersect aRect.
+ // - One if the rectangle is in the exact tile that we're interested in next
+ // (i.e. the tile that tileIterator points at).
+ // - More than one if the encountered rectangle is in a tile that's further
+ // to the right or to the bottom than tileIterator. In that case there is
+ // at least one empty tile between the last rectangle we encountered and
+ // the current one.
+ for (size_t i = 0; i < aRectArray.Length() && tileIterator != rangeEnd; i++) {
+ MOZ_ASSERT(aRectArray[i].x1 < aRectArray[i].x2 && aRectArray[i].y1 < aRectArray[i].y2, "empty rect");
+ IntPoint rectOrigin(aRectArray[i].x1, aRectArray[i].y1);
+ if (tileIterator.IsBeforeTileContainingPoint(rectOrigin)) {
+ IntPoint tileOrigin = TileContainingPoint(rectOrigin);
+ IntPoint afterEmptyTiles = tileRange.MoveIntoBounds(tileOrigin);
+ TileRange emptyTiles(tileBounds, *tileIterator, afterEmptyTiles);
+ if (aHandleEmptyTiles(aRectArray, i, emptyTiles) == IterationAction::STOP) {
+ return IterationEndReason::STOPPED;
+ }
+ tileIterator = afterEmptyTiles;
+ if (tileIterator == rangeEnd) {
+ return IterationEndReason::NOT_STOPPED;
+ }
+ }
+ if (tileIterator.IsAtTileContainingPoint(rectOrigin)) {
+ pixman_box32_t rectIntersection = tileIterator.IntersectionWith(aRect);
+ if (aHandleNonEmptyTile(aRectArray, i, rectIntersection) == IterationAction::STOP) {
+ return IterationEndReason::STOPPED;
+ }
+ ++tileIterator;
+ }
+ }
+
+ if (tileIterator != rangeEnd) {
+ // We've looked at all of our existing rectangles but haven't covered all
+ // of the tiles that we're interested in yet. So we need to deal with the
+ // remaining tiles now.
+ size_t endIndex = aRectArray.Length();
+ TileRange emptyTiles(tileBounds, *tileIterator, *rangeEnd);
+ if (aHandleEmptyTiles(aRectArray, endIndex, emptyTiles) == IterationAction::STOP) {
+ return IterationEndReason::STOPPED;
+ }
+ }
+ return IterationEndReason::NOT_STOPPED;
+}
+
+static pixman_box32_t
+UnionBoundsOfNonEmptyBoxes(const pixman_box32_t& aBox1,
+ const pixman_box32_t& aBox2)
+{
+ return { std::min(aBox1.x1, aBox2.x1),
+ std::min(aBox1.y1, aBox2.y1),
+ std::max(aBox1.x2, aBox2.x2),
+ std::max(aBox1.y2, aBox2.y2) };
+}
+
+// Returns true when adding the rectangle was successful, and false if
+// allocation failed.
+// When this returns false, our internal state might not be consistent and we
+// need to be cleared.
+bool
+TiledRegionImpl::AddRect(const pixman_box32_t& aRect)
+{
+ // We are adding a rectangle that can span multiple tiles.
+ // For each empty tile that aRect intersects, we need to add the intersection
+ // of aRect with that tile to mRects, respecting the order of mRects.
+ // For each tile that already has a rectangle, we need to enlarge that
+ // existing rectangle to include the intersection of aRect with the tile.
+ return ProcessIntersectedTiles(aRect, mRects,
+ [&aRect](nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
+ CheckedInt<size_t> newLength(rects.Length());
+ newLength += emptyTiles.Length();
+ if (!newLength.isValid() || newLength.value() >= kMaxTiles ||
+ !rects.InsertElementsAt(rectIndex, emptyTiles.Length(), fallible)) {
+ return IterationAction::STOP;
+ }
+ for (TileIterator tileIt = emptyTiles.Begin();
+ tileIt != emptyTiles.End();
+ ++tileIt, ++rectIndex) {
+ rects[rectIndex] = tileIt.IntersectionWith(aRect);
+ }
+ return IterationAction::CONTINUE;
+ },
+ [](nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
+ rects[rectIndex] =
+ UnionBoundsOfNonEmptyBoxes(rects[rectIndex], rectIntersectionWithTile);
+ return IterationAction::CONTINUE;
+ }) == IterationEndReason::NOT_STOPPED;
+}
+
+static bool
+NonEmptyBoxesIntersect(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2)
+{
+ return aBox1.x1 < aBox2.x2 && aBox2.x1 < aBox1.x2 &&
+ aBox1.y1 < aBox2.y2 && aBox2.y1 < aBox1.y2;
+}
+
+bool
+TiledRegionImpl::Intersects(const pixman_box32_t& aRect) const
+{
+ // aRect intersects this region if it intersects any of our rectangles.
+ return ProcessIntersectedTiles(aRect, mRects,
+ [](const nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
+ // Ignore empty tiles and keep on iterating.
+ return IterationAction::CONTINUE;
+ },
+ [](const nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
+ if (NonEmptyBoxesIntersect(rects[rectIndex], rectIntersectionWithTile)) {
+ // Found an intersecting rectangle, so aRect intersects this region.
+ return IterationAction::STOP;
+ }
+ return IterationAction::CONTINUE;
+ }) == IterationEndReason::STOPPED;
+}
+
+static bool
+NonEmptyBoxContainsNonEmptyBox(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2)
+{
+ return aBox1.x1 <= aBox2.x1 && aBox2.x2 <= aBox1.x2 &&
+ aBox1.y1 <= aBox2.y1 && aBox2.y2 <= aBox1.y2;
+}
+
+bool
+TiledRegionImpl::Contains(const pixman_box32_t& aRect) const
+{
+ // aRect is contained in this region if aRect does not intersect any empty
+ // tiles and, for each non-empty tile, if the intersection of aRect with that
+ // tile is contained in the existing rectangle we have in that tile.
+ return ProcessIntersectedTiles(aRect, mRects,
+ [](const nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
+ // Found an empty tile that intersects aRect, so aRect is not contained
+ // in this region.
+ return IterationAction::STOP;
+ },
+ [](const nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
+ if (!NonEmptyBoxContainsNonEmptyBox(rects[rectIndex], rectIntersectionWithTile)) {
+ // Our existing rectangle in this tile does not cover the part of aRect that
+ // intersects this tile, so aRect is not contained in this region.
+ return IterationAction::STOP;
+ }
+ return IterationAction::CONTINUE;
+ }) == IterationEndReason::NOT_STOPPED;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/src/TiledRegion.h b/system/graphics/src/TiledRegion.h
new file mode 100644
index 000000000..e0254ddff
--- /dev/null
+++ b/system/graphics/src/TiledRegion.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TILEDREGION_H_
+#define MOZILLA_GFX_TILEDREGION_H_
+
+#include "mozilla/ArrayView.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/Move.h"
+#include "nsRegion.h"
+#include "pixman.h"
+
+namespace mozilla {
+namespace gfx {
+
+// See TiledRegion.cpp for documentation on TiledRegionImpl.
+class TiledRegionImpl {
+public:
+ void Clear() { mRects.Clear(); }
+ bool AddRect(const pixman_box32_t& aRect);
+ bool Intersects(const pixman_box32_t& aRect) const;
+ bool Contains(const pixman_box32_t& aRect) const;
+ operator ArrayView<pixman_box32_t>() const { return ArrayView<pixman_box32_t>(mRects); }
+
+private:
+ nsTArray<pixman_box32_t> mRects;
+};
+
+/**
+ * A auto-simplifying region type that supports one rectangle per tile.
+ * The virtual tile grid is anchored at (0, 0) and has quadratic tiles whose
+ * size is hard-coded as kTileSize in TiledRegion.cpp.
+ * A TiledRegion starts out empty. You can add rectangles or (regular) regions
+ * into it by calling Add(). Add() is a mutating union operation (similar to
+ * OrWith on nsRegion) that's *not* exact, because it will enlarge the region as
+ * necessary to satisfy the "one rectangle per tile" requirement.
+ * Tiled regions convert implicitly to the underlying regular region type.
+ * The only way to remove parts from a TiledRegion is by calling SetEmpty().
+ */
+template<typename RegionT>
+class TiledRegion {
+public:
+ typedef typename RegionT::RectType RectT;
+
+ TiledRegion()
+ : mCoversBounds(false)
+ {}
+
+ TiledRegion(const TiledRegion& aOther)
+ : mBounds(aOther.mBounds)
+ , mImpl(aOther.mImpl)
+ , mCoversBounds(false)
+ {}
+
+ TiledRegion(TiledRegion&& aOther)
+ : mBounds(aOther.mBounds)
+ , mImpl(Move(aOther.mImpl))
+ , mCoversBounds(false)
+ {}
+
+ RegionT GetRegion() const
+ {
+ if (mBounds.IsEmpty()) {
+ return RegionT();
+ }
+ if (mCoversBounds) {
+ // Rect limit hit or allocation failed, treat as 1 rect.
+ return RegionT(mBounds);
+ }
+ return RegionT(mImpl);
+ }
+
+ TiledRegion& operator=(const TiledRegion& aOther)
+ {
+ if (&aOther != this) {
+ mBounds = aOther.mBounds;
+ mImpl = aOther.mImpl;
+ mCoversBounds = aOther.mCoversBounds;
+ }
+ return *this;
+ }
+
+ void Add(const RectT& aRect)
+ {
+ if (aRect.IsEmpty()) {
+ return;
+ }
+
+ Maybe<RectT> newBounds = mBounds.SafeUnion(aRect);
+ if (!newBounds) {
+ return;
+ }
+ mBounds = newBounds.value();
+ MOZ_ASSERT(!mBounds.Overflows());
+
+ if (mCoversBounds) {
+ return;
+ }
+
+ if (!mImpl.AddRect(RectToBox(aRect))) {
+ FallBackToBounds();
+ }
+ }
+
+ void Add(const RegionT& aRegion)
+ {
+ Maybe<RectT> newBounds = mBounds.SafeUnion(aRegion.GetBounds());
+ if (!newBounds) {
+ return;
+ }
+ mBounds = newBounds.value();
+ MOZ_ASSERT(!mBounds.Overflows());
+
+ if (mCoversBounds) {
+ return;
+ }
+
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ RectT r = iter.Get();
+ if (r.IsEmpty() || r.Overflows()) {
+ // This can happen if e.g. a negative-width rect was wrapped into a
+ // region. Treat it the same as we would if such a rect was passed to
+ // the Add(const RectT&) function.
+ continue;
+ }
+ if (!mImpl.AddRect(RectToBox(r))) {
+ FallBackToBounds();
+ return;
+ }
+ }
+ }
+
+ bool IsEmpty() const { return mBounds.IsEmpty(); }
+
+ void SetEmpty()
+ {
+ mBounds.SetEmpty();
+ mImpl.Clear();
+ mCoversBounds = false;
+ }
+
+ RectT GetBounds() const { return mBounds; }
+ bool CoversBounds() const { return mCoversBounds; }
+
+ bool Intersects(const RectT& aRect) const
+ {
+ if (aRect.IsEmpty()) {
+ return true;
+ }
+ if (aRect.Overflows() || !mBounds.Intersects(aRect)) {
+ return false;
+ }
+ if (mCoversBounds) {
+ return true;
+ }
+
+ return mImpl.Intersects(RectToBox(aRect));
+ }
+
+ bool Contains(const RectT& aRect) const
+ {
+ if (aRect.IsEmpty()) {
+ return true;
+ }
+ if (aRect.Overflows() || !mBounds.Contains(aRect)) {
+ return false;
+ }
+ if (mCoversBounds) {
+ return true;
+ }
+ return mImpl.Contains(RectToBox(aRect));
+ }
+
+private:
+
+ void FallBackToBounds()
+ {
+ mCoversBounds = true;
+ mImpl.Clear();
+ }
+
+ static pixman_box32_t RectToBox(const RectT& aRect)
+ {
+ MOZ_ASSERT(!aRect.IsEmpty());
+ MOZ_ASSERT(!aRect.Overflows());
+ return { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
+ }
+
+ RectT mBounds;
+ TiledRegionImpl mImpl;
+
+ // mCoversBounds is true if we bailed out due to a large number of tiles.
+ // mCoversBounds being true means that this TiledRegion is just a simple
+ // rectangle (our mBounds).
+ // Once set to true, the TiledRegion will stay in this state until SetEmpty
+ // is called.
+ bool mCoversBounds;
+};
+
+typedef TiledRegion<IntRegion> TiledIntRegion;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TILEDREGION_H_ */
diff --git a/system/graphics/src/X11UndefineNone.h b/system/graphics/src/X11UndefineNone.h
new file mode 100644
index 000000000..826227eb9
--- /dev/null
+++ b/system/graphics/src/X11UndefineNone.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The header <X11/X.h> defines "None" as a macro that expands to "0L".
+// This is terrible because many enumerations have an enumerator named "None".
+// To work around this, we undefine the macro "None", and define a replacement
+// macro named "X11None".
+// Include this header after including X11 headers, where necessary.
+#ifdef None
+# undef None
+# define X11None 0L
+// <X11/X.h> also defines "RevertToNone" as a macro that expands to "(int)None".
+// Since we are undefining "None", that stops working. To keep it working,
+// we undefine "RevertToNone" and redefine it in terms of "X11None".
+# ifdef RevertToNone
+# undef RevertToNone
+# define RevertToNone (int)X11None
+# endif
+#endif
+
diff --git a/system/graphics/src/X11Util.cpp b/system/graphics/src/X11Util.cpp
new file mode 100644
index 000000000..7dd02d2fd
--- /dev/null
+++ b/system/graphics/src/X11Util.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11Util.h"
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "MainThreadUtils.h" // for NS_IsMainThread
+
+namespace mozilla {
+
+void
+FindVisualAndDepth(Display* aDisplay, VisualID aVisualID,
+ Visual** aVisual, int* aDepth)
+{
+ const Screen* screen = DefaultScreenOfDisplay(aDisplay);
+
+ for (int d = 0; d < screen->ndepths; d++) {
+ Depth *d_info = &screen->depths[d];
+ for (int v = 0; v < d_info->nvisuals; v++) {
+ Visual* visual = &d_info->visuals[v];
+ if (visual->visualid == aVisualID) {
+ *aVisual = visual;
+ *aDepth = d_info->depth;
+ return;
+ }
+ }
+ }
+
+ NS_ASSERTION(aVisualID == X11None, "VisualID not on Screen.");
+ *aVisual = nullptr;
+ *aDepth = 0;
+ return;
+}
+
+void
+FinishX(Display* aDisplay)
+{
+ unsigned long lastRequest = NextRequest(aDisplay) - 1;
+ if (lastRequest == LastKnownRequestProcessed(aDisplay))
+ return;
+
+ XSync(aDisplay, False);
+}
+
+ScopedXErrorHandler::ErrorEvent* ScopedXErrorHandler::sXErrorPtr;
+
+int
+ScopedXErrorHandler::ErrorHandler(Display *, XErrorEvent *ev)
+{
+ // only record the error if no error was previously recorded.
+ // this means that in case of multiple errors, it's the first error that we report.
+ if (!sXErrorPtr->mError.error_code)
+ sXErrorPtr->mError = *ev;
+ return 0;
+}
+
+ScopedXErrorHandler::ScopedXErrorHandler(bool aAllowOffMainThread)
+{
+ if (!aAllowOffMainThread) {
+ // Off main thread usage is not safe in general, but OMTC GL layers uses this
+ // with the main thread blocked, which makes it safe.
+ NS_WARNING_ASSERTION(
+ NS_IsMainThread(),
+ "ScopedXErrorHandler being called off main thread, may cause issues");
+ }
+ // let sXErrorPtr point to this object's mXError object, but don't reset this mXError object!
+ // think of the case of nested ScopedXErrorHandler's.
+ mOldXErrorPtr = sXErrorPtr;
+ sXErrorPtr = &mXError;
+ mOldErrorHandler = XSetErrorHandler(ErrorHandler);
+}
+
+ScopedXErrorHandler::~ScopedXErrorHandler()
+{
+ sXErrorPtr = mOldXErrorPtr;
+ XSetErrorHandler(mOldErrorHandler);
+}
+
+bool
+ScopedXErrorHandler::SyncAndGetError(Display *dpy, XErrorEvent *ev)
+{
+ FinishX(dpy);
+
+ bool retval = mXError.mError.error_code != 0;
+ if (ev)
+ *ev = mXError.mError;
+ mXError = ErrorEvent(); // reset
+ return retval;
+}
+
+} // namespace mozilla
diff --git a/system/graphics/src/X11Util.h b/system/graphics/src/X11Util.h
new file mode 100644
index 000000000..e04913342
--- /dev/null
+++ b/system/graphics/src/X11Util.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_X11Util_h
+#define mozilla_X11Util_h
+
+// Utilities common to all X clients, regardless of UI toolkit.
+
+#if defined(MOZ_WIDGET_GTK)
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+# include "X11UndefineNone.h"
+#else
+# error Unknown toolkit
+#endif
+
+#include <string.h> // for memset
+#include "mozilla/Scoped.h" // for SCOPED_TEMPLATE
+
+namespace mozilla {
+
+/**
+ * Return the default X Display created and used by the UI toolkit.
+ */
+inline Display*
+DefaultXDisplay()
+{
+#if defined(MOZ_WIDGET_GTK)
+ return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+#endif
+}
+
+/**
+ * Sets *aVisual to point to aDisplay's Visual struct corresponding to
+ * aVisualID, and *aDepth to its depth. When aVisualID is None, these are set
+ * to nullptr and 0 respectively. Both out-parameter pointers are assumed
+ * non-nullptr.
+ */
+void
+FindVisualAndDepth(Display* aDisplay, VisualID aVisualID,
+ Visual** aVisual, int* aDepth);
+
+
+/**
+ * Ensure that all X requests have been processed.
+ *
+ * This is similar to XSync, but doesn't need a round trip if the previous
+ * request was synchronous or if events have been received since the last
+ * request. Subsequent FinishX calls will be noops if there have been no
+ * intermediate requests.
+ */
+
+void
+FinishX(Display* aDisplay);
+
+/**
+ * Invoke XFree() on a pointer to memory allocated by Xlib (if the
+ * pointer is nonnull) when this class goes out of scope.
+ */
+template <typename T>
+struct ScopedXFreePtrTraits
+{
+ typedef T *type;
+ static T *empty() { return nullptr; }
+ static void release(T *ptr) { if (ptr != nullptr) XFree(ptr); }
+};
+SCOPED_TEMPLATE(ScopedXFree, ScopedXFreePtrTraits)
+
+/**
+ * On construction, set a graceful X error handler that doesn't crash the application and records X errors.
+ * On destruction, restore the X error handler to what it was before construction.
+ *
+ * The SyncAndGetError() method allows to know whether a X error occurred, optionally allows to get the full XErrorEvent,
+ * and resets the recorded X error state so that a single X error will be reported only once.
+ *
+ * Nesting is correctly handled: multiple nested ScopedXErrorHandler's don't interfere with each other's state. However,
+ * if SyncAndGetError is not called on the nested ScopedXErrorHandler, then any X errors caused by X calls made while the nested
+ * ScopedXErrorHandler was in place may then be caught by the other ScopedXErrorHandler. This is just a result of X being
+ * asynchronous and us not doing any implicit syncing: the only method in this class what causes syncing is SyncAndGetError().
+ *
+ * This class is not thread-safe at all. It is assumed that only one thread is using any ScopedXErrorHandler's. Given that it's
+ * not used on Mac, it should be easy to make it thread-safe by using thread-local storage with __thread.
+ */
+class ScopedXErrorHandler
+{
+public:
+ // trivial wrapper around XErrorEvent, just adding ctor initializing by zero.
+ struct ErrorEvent
+ {
+ XErrorEvent mError;
+
+ ErrorEvent()
+ {
+ memset(this, 0, sizeof(ErrorEvent));
+ }
+ };
+
+private:
+
+ // this ScopedXErrorHandler's ErrorEvent object
+ ErrorEvent mXError;
+
+ // static pointer for use by the error handler
+ static ErrorEvent* sXErrorPtr;
+
+ // what to restore sXErrorPtr to on destruction
+ ErrorEvent* mOldXErrorPtr;
+
+ // what to restore the error handler to on destruction
+ int (*mOldErrorHandler)(Display *, XErrorEvent *);
+
+public:
+
+ static int
+ ErrorHandler(Display *, XErrorEvent *ev);
+
+ /**
+ * @param aAllowOffMainThread whether to warn if used off main thread
+ */
+ explicit ScopedXErrorHandler(bool aAllowOffMainThread = false);
+
+ ~ScopedXErrorHandler();
+
+ /** \returns true if a X error occurred since the last time this method was called on this ScopedXErrorHandler object,
+ * or since the creation of this ScopedXErrorHandler object if this method was never called on it.
+ *
+ * \param ev this optional parameter, if set, will be filled with the XErrorEvent object. If multiple errors occurred,
+ * the first one will be returned.
+ */
+ bool SyncAndGetError(Display *dpy, XErrorEvent *ev = nullptr);
+};
+
+class OffMainThreadScopedXErrorHandler : public ScopedXErrorHandler
+{
+public:
+ OffMainThreadScopedXErrorHandler()
+ : ScopedXErrorHandler(true)
+ {
+ }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_X11Util_h
diff --git a/system/graphics/src/gfxTelemetry.cpp b/system/graphics/src/gfxTelemetry.cpp
new file mode 100644
index 000000000..b027cde39
--- /dev/null
+++ b/system/graphics/src/gfxTelemetry.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "gfxTelemetry.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+const char*
+FeatureStatusToString(FeatureStatus aStatus)
+{
+ switch (aStatus) {
+ case FeatureStatus::Unused:
+ return "unused";
+ case FeatureStatus::Unavailable:
+ return "unavailable";
+ case FeatureStatus::CrashedInHandler:
+ return "crashed";
+ case FeatureStatus::Blocked:
+ return "blocked";
+ case FeatureStatus::Blacklisted:
+ return "blacklisted";
+ case FeatureStatus::Failed:
+ return "failed";
+ case FeatureStatus::Disabled:
+ return "disabled";
+ case FeatureStatus::Available:
+ return "available";
+ case FeatureStatus::ForceEnabled:
+ return "force_enabled";
+ case FeatureStatus::CrashedOnStartup:
+ return "crashed_on_startup";
+ case FeatureStatus::Broken:
+ return "broken";
+ default:
+ MOZ_ASSERT_UNREACHABLE("missing status case");
+ return "unknown";
+ }
+}
+
+bool
+IsFeatureStatusFailure(FeatureStatus aStatus)
+{
+ return !(aStatus == FeatureStatus::Unused ||
+ aStatus == FeatureStatus::Available ||
+ aStatus == FeatureStatus::ForceEnabled);
+}
+
+bool
+IsFeatureStatusSuccess(FeatureStatus aStatus)
+{
+ return aStatus == FeatureStatus::Available ||
+ aStatus == FeatureStatus::ForceEnabled;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/src/gfxTelemetry.h b/system/graphics/src/gfxTelemetry.h
new file mode 100644
index 000000000..c6d13c86a
--- /dev/null
+++ b/system/graphics/src/gfxTelemetry.h
@@ -0,0 +1,64 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef gfx_src_gfxTelemetry_h__
+#define gfx_src_gfxTelemetry_h__
+
+namespace mozilla {
+namespace gfx {
+
+// Describes the status of a graphics feature, in terms of whether or not we've
+// attempted to initialize the feature, and if so, whether or not it succeeded
+// (and if not, why).
+enum class FeatureStatus
+{
+ // This feature has not been requested.
+ Unused,
+
+ // This feature is unavailable due to Safe Mode, not being included with
+ // the operating system, or a dependent feature being disabled.
+ Unavailable,
+
+ // This feature crashed immediately when we tried to initialize it, but we
+ // were able to recover via SEH (or something similar).
+ CrashedInHandler,
+
+ // This feature was blocked for reasons outside the blacklist, such as a
+ // runtime test failing.
+ Blocked,
+
+ // This feature has been blocked by the graphics blacklist.
+ Blacklisted,
+
+ // This feature was attempted but failed to activate.
+ Failed,
+
+ // This feature was explicitly disabled by the user.
+ Disabled,
+
+ // This feature is available for use.
+ Available,
+
+ // This feature was explicitly force-enabled by the user.
+ ForceEnabled,
+
+ // This feature was disabled due to the startup crash guard.
+ CrashedOnStartup,
+
+ // This feature was attempted but later determined to be broken.
+ Broken,
+
+ // Add new entries above here.
+ LAST
+};
+
+const char* FeatureStatusToString(FeatureStatus aStatus);
+bool IsFeatureStatusFailure(FeatureStatus aStatus);
+bool IsFeatureStatusSuccess(FeatureStatus aStatus);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // gfx_src_gfxTelemetry_h__
diff --git a/system/graphics/src/moz.build b/system/graphics/src/moz.build
new file mode 100644
index 000000000..993d4bde2
--- /dev/null
+++ b/system/graphics/src/moz.build
@@ -0,0 +1,87 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIFontEnumerator.idl',
+ 'nsIScriptableRegion.idl',
+]
+
+XPIDL_MODULE = 'gfx'
+
+DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
+
+EXPORTS += [
+ 'DriverCrashGuard.h',
+ 'FilterSupport.h',
+ 'gfxTelemetry.h',
+ 'nsBoundingMetrics.h',
+ 'nsColor.h',
+ 'nsColorNameList.h',
+ 'nsColorNames.h',
+ 'nsCoord.h',
+ 'nsDeviceContext.h',
+ 'nsFont.h',
+ 'nsFontMetrics.h',
+ 'nsGfxCIID.h',
+ 'nsITheme.h',
+ 'nsMargin.h',
+ 'nsPoint.h',
+ 'nsRect.h',
+ 'nsRegion.h',
+ 'nsRegionFwd.h',
+ 'nsRenderingContext.h',
+ 'nsSize.h',
+ 'nsThemeConstants.h',
+ 'nsTransform2D.h',
+ 'PingPongRegion.h',
+ 'RegionBuilder.h',
+ 'X11UndefineNone.h'
+]
+
+EXPORTS.mozilla += [
+ 'AppUnits.h',
+ 'ArrayView.h',
+]
+
+EXPORTS.mozilla.gfx += [
+ 'TiledRegion.h',
+]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS.mozilla += ['X11Util.h']
+ SOURCES += [
+ 'X11Util.cpp',
+ ]
+
+SOURCES += [
+ 'DriverCrashGuard.cpp',
+ 'FilterSupport.cpp',
+ 'gfxTelemetry.cpp',
+ 'nsColor.cpp',
+ 'nsDeviceContext.cpp',
+ 'nsFont.cpp',
+ 'nsFontMetrics.cpp',
+ 'nsRect.cpp',
+ 'nsRegion.cpp',
+ 'nsScriptableRegion.cpp',
+ 'nsThebesFontEnumerator.cpp',
+ 'nsThebesGfxFactory.cpp',
+ 'nsTransform2D.cpp',
+ 'TiledRegion.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/dom/ipc', # for ContentChild.h
+]
+
+FINAL_LIBRARY = 'xul'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
diff --git a/system/graphics/src/nsBoundingMetrics.h b/system/graphics/src/nsBoundingMetrics.h
new file mode 100644
index 000000000..4665db24f
--- /dev/null
+++ b/system/graphics/src/nsBoundingMetrics.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsBoundingMetrics_h
+#define __nsBoundingMetrics_h
+
+#include "nsCoord.h"
+#include <algorithm>
+
+/* Struct used for accurate measurements of a string, in order to
+ * allow precise positioning when processing MathML. This is in its
+ * own header file because some very-widely-included headers need it
+ * but not the rest of nsFontMetrics, or vice versa.
+ */
+
+struct nsBoundingMetrics {
+
+ ///////////
+ // Metrics that _exactly_ enclose the text:
+
+ // The character coordinate system is the one used on X Windows:
+ // 1. The origin is located at the intersection of the baseline
+ // with the left of the character's cell.
+ // 2. All horizontal bearings are oriented from left to right.
+ // 2. All horizontal bearings are oriented from left to right.
+ // 3. The ascent is oriented from bottom to top (being 0 at the orgin).
+ // 4. The descent is oriented from top to bottom (being 0 at the origin).
+
+ // Note that Win32/Mac/PostScript use a different convention for
+ // the descent (all vertical measurements are oriented from bottom
+ // to top on these palatforms). Make sure to flip the sign of the
+ // descent on these platforms for cross-platform compatibility.
+
+ // Any of the following member variables listed here can have
+ // positive or negative value.
+
+ nscoord leftBearing;
+ /* The horizontal distance from the origin of the drawing
+ operation to the left-most part of the drawn string. */
+
+ nscoord rightBearing;
+ /* The horizontal distance from the origin of the drawing
+ operation to the right-most part of the drawn string.
+ The _exact_ width of the string is therefore:
+ rightBearing - leftBearing */
+
+ nscoord ascent;
+ /* The vertical distance from the origin of the drawing
+ operation to the top-most part of the drawn string. */
+
+ nscoord descent;
+ /* The vertical distance from the origin of the drawing
+ operation to the bottom-most part of the drawn string.
+ The _exact_ height of the string is therefore:
+ ascent + descent */
+
+ nscoord width;
+ /* The horizontal distance from the origin of the drawing
+ operation to the correct origin for drawing another string
+ to follow the current one. Depending on the font, this
+ could be greater than or less than the right bearing. */
+
+ nsBoundingMetrics() : leftBearing(0), rightBearing(0),
+ ascent(0), descent(0), width(0)
+ {}
+
+ void
+ operator += (const nsBoundingMetrics& bm) {
+ if (ascent + descent == 0 && rightBearing - leftBearing == 0) {
+ ascent = bm.ascent;
+ descent = bm.descent;
+ leftBearing = width + bm.leftBearing;
+ rightBearing = width + bm.rightBearing;
+ }
+ else {
+ if (ascent < bm.ascent) ascent = bm.ascent;
+ if (descent < bm.descent) descent = bm.descent;
+ leftBearing = std::min(leftBearing, width + bm.leftBearing);
+ rightBearing = std::max(rightBearing, width + bm.rightBearing);
+ }
+ width += bm.width;
+ }
+};
+
+#endif // __nsBoundingMetrics_h
diff --git a/system/graphics/src/nsColor.cpp b/system/graphics/src/nsColor.cpp
new file mode 100644
index 000000000..359f9fde4
--- /dev/null
+++ b/system/graphics/src/nsColor.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/MathAlgorithms.h"
+
+#include "nsColor.h"
+#include <sys/types.h> // for int32_t
+#include "nsColorNames.h" // for nsColorNames
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsStaticNameTable.h"
+#include "nsString.h" // for nsAutoCString, nsString, etc
+#include "nscore.h" // for nsAString, etc
+
+using namespace mozilla;
+
+// define an array of all color names
+#define GFX_COLOR(_name, _value) #_name,
+static const char* const kColorNames[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+// define an array of all color name values
+#define GFX_COLOR(_name, _value) _value,
+static const nscolor kColors[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+#define eColorName_COUNT (ArrayLength(kColorNames))
+#define eColorName_UNKNOWN (-1)
+
+static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr;
+
+void nsColorNames::AddRefTable(void)
+{
+ NS_ASSERTION(!gColorTable, "pre existing array!");
+ if (!gColorTable) {
+ gColorTable =
+ new nsStaticCaseInsensitiveNameTable(kColorNames, eColorName_COUNT);
+ }
+}
+
+void nsColorNames::ReleaseTable(void)
+{
+ if (gColorTable) {
+ delete gColorTable;
+ gColorTable = nullptr;
+ }
+}
+
+static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc)
+{
+ int component = 0;
+ int index = (color * dpc);
+ if (2 < dpc) {
+ dpc = 2;
+ }
+ while (--dpc >= 0) {
+ char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0');
+ if (('0' <= ch) && (ch <= '9')) {
+ component = (component * 16) + (ch - '0');
+ } else if ((('a' <= ch) && (ch <= 'f')) ||
+ (('A' <= ch) && (ch <= 'F'))) {
+ // "ch&7" handles lower and uppercase hex alphabetics
+ component = (component * 16) + (ch & 7) + 9;
+ }
+ else { // not a hex digit, treat it like 0
+ component = (component * 16);
+ }
+ }
+ return component;
+}
+
+bool
+NS_HexToRGBA(const nsAString& aColorSpec, nsHexColorType aType,
+ nscolor* aResult)
+{
+ const char16_t* buffer = aColorSpec.BeginReading();
+
+ int nameLen = aColorSpec.Length();
+ bool hasAlpha = false;
+ if (nameLen != 3 && nameLen != 6) {
+ if ((nameLen != 4 && nameLen != 8) || aType == nsHexColorType::NoAlpha) {
+ // Improperly formatted color value
+ return false;
+ }
+ hasAlpha = true;
+ }
+
+ // Make sure the digits are legal
+ for (int i = 0; i < nameLen; i++) {
+ char16_t ch = buffer[i];
+ if (((ch >= '0') && (ch <= '9')) ||
+ ((ch >= 'a') && (ch <= 'f')) ||
+ ((ch >= 'A') && (ch <= 'F'))) {
+ // Legal character
+ continue;
+ }
+ // Whoops. Illegal character.
+ return false;
+ }
+
+ // Convert the ascii to binary
+ int dpc = ((nameLen <= 4) ? 1 : 2);
+ // Translate components from hex to binary
+ int r = ComponentValue(buffer, nameLen, 0, dpc);
+ int g = ComponentValue(buffer, nameLen, 1, dpc);
+ int b = ComponentValue(buffer, nameLen, 2, dpc);
+ int a;
+ if (hasAlpha) {
+ a = ComponentValue(buffer, nameLen, 3, dpc);
+ } else {
+ a = (dpc == 1) ? 0xf : 0xff;
+ }
+ if (dpc == 1) {
+ // Scale single digit component to an 8 bit value. Replicate the
+ // single digit to compute the new value.
+ r = (r << 4) | r;
+ g = (g << 4) | g;
+ b = (b << 4) | b;
+ a = (a << 4) | a;
+ }
+ NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
+ NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
+ NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
+ NS_ASSERTION((a >= 0) && (a <= 255), "bad a");
+ *aResult = NS_RGBA(r, g, b, a);
+ return true;
+}
+
+// This implements part of the algorithm for legacy behavior described in
+// http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
+bool NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
+{
+ if (aColorSpec.EqualsLiteral("transparent")) {
+ return false;
+ }
+
+ int nameLen = aColorSpec.Length();
+ const char16_t* colorSpec = aColorSpec.get();
+ if (nameLen > 128) {
+ nameLen = 128;
+ }
+
+ if ('#' == colorSpec[0]) {
+ ++colorSpec;
+ --nameLen;
+ }
+
+ // digits per component
+ int dpc = (nameLen + 2) / 3;
+ int newdpc = dpc;
+
+ // Use only the rightmost 8 characters of each component.
+ if (newdpc > 8) {
+ nameLen -= newdpc - 8;
+ colorSpec += newdpc - 8;
+ newdpc = 8;
+ }
+
+ // And then keep trimming characters at the left until we'd trim one
+ // that would leave a nonzero value, but not past 2 characters per
+ // component.
+ while (newdpc > 2) {
+ bool haveNonzero = false;
+ for (int c = 0; c < 3; ++c) {
+ MOZ_ASSERT(c * dpc < nameLen,
+ "should not pass end of string while newdpc > 2");
+ char16_t ch = colorSpec[c * dpc];
+ if (('1' <= ch && ch <= '9') ||
+ ('A' <= ch && ch <= 'F') ||
+ ('a' <= ch && ch <= 'f')) {
+ haveNonzero = true;
+ break;
+ }
+ }
+ if (haveNonzero) {
+ break;
+ }
+ --newdpc;
+ --nameLen;
+ ++colorSpec;
+ }
+
+ // Translate components from hex to binary
+ int r = ComponentValue(colorSpec, nameLen, 0, dpc);
+ int g = ComponentValue(colorSpec, nameLen, 1, dpc);
+ int b = ComponentValue(colorSpec, nameLen, 2, dpc);
+ NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
+ NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
+ NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
+
+ *aResult = NS_RGB(r, g, b);
+ return true;
+}
+
+bool NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
+{
+ if (!gColorTable) return false;
+
+ int32_t id = gColorTable->Lookup(aColorName);
+ if (eColorName_UNKNOWN < id) {
+ NS_ASSERTION(uint32_t(id) < eColorName_COUNT,
+ "gColorTable->Lookup messed up");
+ if (aResult) {
+ *aResult = kColors[id];
+ }
+ return true;
+ }
+ return false;
+}
+
+// Returns kColorNames, an array of all possible color names, and sets
+// *aSizeArray to the size of that array. Do NOT call free() on this array.
+const char * const * NS_AllColorNames(size_t *aSizeArray)
+{
+ *aSizeArray = ArrayLength(kColorNames);
+ return kColorNames;
+}
+
+// Macro to blend two colors
+//
+// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
+#define MOZ_BLEND(target, bg, fg, fgalpha) \
+ FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
+
+nscolor
+NS_ComposeColors(nscolor aBG, nscolor aFG)
+{
+ // This function uses colors that are non premultiplied alpha.
+ int r, g, b, a;
+
+ int bgAlpha = NS_GET_A(aBG);
+ int fgAlpha = NS_GET_A(aFG);
+
+ // Compute the final alpha of the blended color
+ // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255;
+ FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha));
+ a = fgAlpha + a;
+ int blendAlpha;
+ if (a == 0) {
+ // In this case the blended color is totally trasparent,
+ // we preserve the color information of the foreground color.
+ blendAlpha = 255;
+ } else {
+ blendAlpha = (fgAlpha*255)/a;
+ }
+ MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha);
+ MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha);
+ MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha);
+
+ return NS_RGBA(r, g, b, a);
+}
+
+namespace mozilla {
+
+static uint32_t
+BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
+{
+ return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
+}
+
+nscolor
+LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio)
+{
+ // Common case that either pure background or pure foreground
+ if (aFgRatio == 0) {
+ return aBg;
+ }
+ if (aFgRatio == 255) {
+ return aFg;
+ }
+ // Common case that alpha channel is equal (usually both are opaque)
+ if (NS_GET_A(aBg) == NS_GET_A(aFg)) {
+ auto r = BlendColorComponent(NS_GET_R(aBg), NS_GET_R(aFg), aFgRatio);
+ auto g = BlendColorComponent(NS_GET_G(aBg), NS_GET_G(aFg), aFgRatio);
+ auto b = BlendColorComponent(NS_GET_B(aBg), NS_GET_B(aFg), aFgRatio);
+ return NS_RGBA(r, g, b, NS_GET_A(aFg));
+ }
+
+ constexpr float kFactor = 1.0f / 255.0f;
+
+ float p1 = kFactor * (255 - aFgRatio);
+ float a1 = kFactor * NS_GET_A(aBg);
+ float r1 = a1 * NS_GET_R(aBg);
+ float g1 = a1 * NS_GET_G(aBg);
+ float b1 = a1 * NS_GET_B(aBg);
+
+ float p2 = 1.0f - p1;
+ float a2 = kFactor * NS_GET_A(aFg);
+ float r2 = a2 * NS_GET_R(aFg);
+ float g2 = a2 * NS_GET_G(aFg);
+ float b2 = a2 * NS_GET_B(aFg);
+
+ float a = p1 * a1 + p2 * a2;
+ if (a == 0.0) {
+ return NS_RGBA(0, 0, 0, 0);
+ }
+
+ auto r = ClampColor((p1 * r1 + p2 * r2) / a);
+ auto g = ClampColor((p1 * g1 + p2 * g2) / a);
+ auto b = ClampColor((p1 * b1 + p2 * b2) / a);
+ return NS_RGBA(r, g, b, NSToIntRound(a * 255));
+}
+
+} // namespace mozilla
+
+// Functions to convert from HSL color space to RGB color space.
+// This is the algorithm described in the CSS3 specification
+
+// helper
+static float
+HSL_HueToRGB(float m1, float m2, float h)
+{
+ if (h < 0.0f)
+ h += 1.0f;
+ if (h > 1.0f)
+ h -= 1.0f;
+ if (h < (float)(1.0/6.0))
+ return m1 + (m2 - m1)*h*6.0f;
+ if (h < (float)(1.0/2.0))
+ return m2;
+ if (h < (float)(2.0/3.0))
+ return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f;
+ return m1;
+}
+
+// The float parameters are all expected to be in the range 0-1
+nscolor
+NS_HSL2RGB(float h, float s, float l)
+{
+ uint8_t r, g, b;
+ float m1, m2;
+ if (l <= 0.5f) {
+ m2 = l*(s+1);
+ } else {
+ m2 = l + s - l*s;
+ }
+ m1 = l*2 - m2;
+ r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f));
+ g = uint8_t(255 * HSL_HueToRGB(m1, m2, h));
+ b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
+ return NS_RGB(r, g, b);
+}
+
+const char*
+NS_RGBToColorName(nscolor aColor)
+{
+ for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
+ if (kColors[idx] == aColor) {
+ return kColorNames[idx];
+ }
+ }
+
+ return nullptr;
+}
diff --git a/system/graphics/src/nsColor.h b/system/graphics/src/nsColor.h
new file mode 100644
index 000000000..2f21c91bf
--- /dev/null
+++ b/system/graphics/src/nsColor.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsColor_h___
+#define nsColor_h___
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint8_t, uint32_t
+#include "nscore.h" // for nsAString
+#include "nsCoord.h" // for NSToIntRound
+
+class nsAString;
+class nsString;
+
+// A color is a 32 bit unsigned integer with four components: R, G, B
+// and A.
+typedef uint32_t nscolor;
+
+// Make a color out of r,g,b values. This assumes that the r,g,b values are
+// properly constrained to 0-255. This also assumes that a is 255.
+#define NS_RGB(_r,_g,_b) \
+ ((nscolor) ((255 << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
+
+// Make a color out of r,g,b,a values. This assumes that the r,g,b,a
+// values are properly constrained to 0-255.
+#define NS_RGBA(_r,_g,_b,_a) \
+ ((nscolor) (((_a) << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
+
+// Extract color components from nscolor
+#define NS_GET_R(_rgba) ((uint8_t) ((_rgba) & 0xff))
+#define NS_GET_G(_rgba) ((uint8_t) (((_rgba) >> 8) & 0xff))
+#define NS_GET_B(_rgba) ((uint8_t) (((_rgba) >> 16) & 0xff))
+#define NS_GET_A(_rgba) ((uint8_t) (((_rgba) >> 24) & 0xff))
+
+namespace mozilla {
+
+template<typename T>
+inline uint8_t ClampColor(T aColor)
+{
+ if (aColor >= 255) {
+ return 255;
+ }
+ if (aColor <= 0) {
+ return 0;
+ }
+ return NSToIntRound(aColor);
+}
+
+} // namespace mozilla
+
+// Fast approximate division by 255. It has the property that
+// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
+// But it only uses two adds and two shifts instead of an
+// integer division (which is expensive on many processors).
+//
+// equivalent to target=v/255
+#define FAST_DIVIDE_BY_255(target,v) \
+ PR_BEGIN_MACRO \
+ unsigned tmp_ = v; \
+ target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
+ PR_END_MACRO
+
+enum class nsHexColorType : uint8_t {
+ NoAlpha, // 3 or 6 digit hex colors only
+ AllowAlpha, // 3, 4, 6, or 8 digit hex colors
+};
+
+// Translate a hex string to a color. Return true if it parses ok,
+// otherwise return false.
+// This accepts the number of digits specified by aType.
+bool
+NS_HexToRGBA(const nsAString& aBuf, nsHexColorType aType, nscolor* aResult);
+
+// Compose one NS_RGB color onto another. The result is what
+// you get if you draw aFG on top of aBG with operator OVER.
+nscolor NS_ComposeColors(nscolor aBG, nscolor aFG);
+
+namespace mozilla {
+
+inline uint32_t RoundingDivideBy255(uint32_t n)
+{
+ // There is an approximate alternative: ((n << 8) + n + 32896) >> 16
+ // But that is actually slower than this simple expression on a modern
+ // machine with a modern compiler.
+ return (n + 127) / 255;
+}
+
+// Blend one RGBA color with another based on a given ratio.
+// It is a linear interpolation on each channel with alpha premultipled.
+nscolor LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio);
+
+} // namespace mozilla
+
+// Translate a hex string to a color. Return true if it parses ok,
+// otherwise return false.
+// This version accepts 1 to 9 digits (missing digits are 0)
+bool NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
+
+// There is no function to translate a color to a hex string, because
+// the hex-string syntax does not support transparency.
+
+// Translate a color name to a color. Return true if it parses ok,
+// otherwise return false.
+bool NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
+
+// Returns an array of all possible color names, and sets
+// *aSizeArray to the size of that array. Do NOT call |free()| on this array.
+const char * const * NS_AllColorNames(size_t *aSizeArray);
+
+// function to convert from HSL color space to RGB color space
+// the float parameters are all expected to be in the range 0-1
+nscolor NS_HSL2RGB(float h, float s, float l);
+
+// Return a color name for the given nscolor. If there is no color
+// name for it, returns null. If there are multiple possible color
+// names for the given color, the first one in nsColorNameList.h
+// (which is generally the first one in alphabetical order) will be
+// returned.
+const char* NS_RGBToColorName(nscolor aColor);
+
+#endif /* nsColor_h___ */
diff --git a/system/graphics/src/nsColorNameList.h b/system/graphics/src/nsColorNameList.h
new file mode 100644
index 000000000..b679b981d
--- /dev/null
+++ b/system/graphics/src/nsColorNameList.h
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/******
+
+ This file contains the list of all named colors
+ See nsCSSColorNames.h for access to the enum values for colors
+
+ It is designed to be used as inline input to nsCSSColorNames.cpp *only*
+ through the magic of C preprocessing.
+
+ All entries must be enclosed in the macro GFX_COLOR which will have cruel
+ and unusual things done to it
+
+ It is recommended (but not strictly necessary) to keep all entries
+ in alphabetical order
+
+ The first argument to GFX_COLOR is both the enum identifier of the color
+ and the string value
+ The second argument is the sRGBA value for the named color
+
+ 'name' entries *must* use only lowercase characters.
+
+ ** Break these invarient and bad things will happen. **
+
+
+ ******/
+
+
+GFX_COLOR(aliceblue, NS_RGB(240, 248, 255))
+GFX_COLOR(antiquewhite, NS_RGB(250, 235, 215))
+GFX_COLOR(aqua, NS_RGB( 0, 255, 255))
+GFX_COLOR(aquamarine, NS_RGB(127, 255, 212))
+GFX_COLOR(azure, NS_RGB(240, 255, 255))
+GFX_COLOR(beige, NS_RGB(245, 245, 220))
+GFX_COLOR(bisque, NS_RGB(255, 228, 196))
+GFX_COLOR(black, NS_RGB( 0, 0, 0))
+GFX_COLOR(blanchedalmond, NS_RGB(255, 235, 205))
+GFX_COLOR(blue, NS_RGB( 0, 0, 255))
+GFX_COLOR(blueviolet, NS_RGB(138, 43, 226))
+GFX_COLOR(brown, NS_RGB(165, 42, 42))
+GFX_COLOR(burlywood, NS_RGB(222, 184, 135))
+GFX_COLOR(cadetblue, NS_RGB( 95, 158, 160))
+GFX_COLOR(chartreuse, NS_RGB(127, 255, 0))
+GFX_COLOR(chocolate, NS_RGB(210, 105, 30))
+GFX_COLOR(coral, NS_RGB(255, 127, 80))
+GFX_COLOR(cornflowerblue, NS_RGB(100, 149, 237))
+GFX_COLOR(cornsilk, NS_RGB(255, 248, 220))
+GFX_COLOR(crimson, NS_RGB(220, 20, 60))
+GFX_COLOR(cyan, NS_RGB( 0, 255, 255))
+GFX_COLOR(darkblue, NS_RGB( 0, 0, 139))
+GFX_COLOR(darkcyan, NS_RGB( 0, 139, 139))
+GFX_COLOR(darkgoldenrod, NS_RGB(184, 134, 11))
+GFX_COLOR(darkgray, NS_RGB(169, 169, 169))
+GFX_COLOR(darkgreen, NS_RGB( 0, 100, 0))
+GFX_COLOR(darkgrey, NS_RGB(169, 169, 169))
+GFX_COLOR(darkkhaki, NS_RGB(189, 183, 107))
+GFX_COLOR(darkmagenta, NS_RGB(139, 0, 139))
+GFX_COLOR(darkolivegreen, NS_RGB( 85, 107, 47))
+GFX_COLOR(darkorange, NS_RGB(255, 140, 0))
+GFX_COLOR(darkorchid, NS_RGB(153, 50, 204))
+GFX_COLOR(darkred, NS_RGB(139, 0, 0))
+GFX_COLOR(darksalmon, NS_RGB(233, 150, 122))
+GFX_COLOR(darkseagreen, NS_RGB(143, 188, 143))
+GFX_COLOR(darkslateblue, NS_RGB( 72, 61, 139))
+GFX_COLOR(darkslategray, NS_RGB( 47, 79, 79))
+GFX_COLOR(darkslategrey, NS_RGB( 47, 79, 79))
+GFX_COLOR(darkturquoise, NS_RGB( 0, 206, 209))
+GFX_COLOR(darkviolet, NS_RGB(148, 0, 211))
+GFX_COLOR(deeppink, NS_RGB(255, 20, 147))
+GFX_COLOR(deepskyblue, NS_RGB( 0, 191, 255))
+GFX_COLOR(dimgray, NS_RGB(105, 105, 105))
+GFX_COLOR(dimgrey, NS_RGB(105, 105, 105))
+GFX_COLOR(dodgerblue, NS_RGB( 30, 144, 255))
+GFX_COLOR(firebrick, NS_RGB(178, 34, 34))
+GFX_COLOR(floralwhite, NS_RGB(255, 250, 240))
+GFX_COLOR(forestgreen, NS_RGB( 34, 139, 34))
+GFX_COLOR(fuchsia, NS_RGB(255, 0, 255))
+GFX_COLOR(gainsboro, NS_RGB(220, 220, 220))
+GFX_COLOR(ghostwhite, NS_RGB(248, 248, 255))
+GFX_COLOR(gold, NS_RGB(255, 215, 0))
+GFX_COLOR(goldenrod, NS_RGB(218, 165, 32))
+GFX_COLOR(gray, NS_RGB(128, 128, 128))
+GFX_COLOR(grey, NS_RGB(128, 128, 128))
+GFX_COLOR(green, NS_RGB( 0, 128, 0))
+GFX_COLOR(greenyellow, NS_RGB(173, 255, 47))
+GFX_COLOR(honeydew, NS_RGB(240, 255, 240))
+GFX_COLOR(hotpink, NS_RGB(255, 105, 180))
+GFX_COLOR(indianred, NS_RGB(205, 92, 92))
+GFX_COLOR(indigo, NS_RGB( 75, 0, 130))
+GFX_COLOR(ivory, NS_RGB(255, 255, 240))
+GFX_COLOR(khaki, NS_RGB(240, 230, 140))
+GFX_COLOR(lavender, NS_RGB(230, 230, 250))
+GFX_COLOR(lavenderblush, NS_RGB(255, 240, 245))
+GFX_COLOR(lawngreen, NS_RGB(124, 252, 0))
+GFX_COLOR(lemonchiffon, NS_RGB(255, 250, 205))
+GFX_COLOR(lightblue, NS_RGB(173, 216, 230))
+GFX_COLOR(lightcoral, NS_RGB(240, 128, 128))
+GFX_COLOR(lightcyan, NS_RGB(224, 255, 255))
+GFX_COLOR(lightgoldenrodyellow, NS_RGB(250, 250, 210))
+GFX_COLOR(lightgray, NS_RGB(211, 211, 211))
+GFX_COLOR(lightgreen, NS_RGB(144, 238, 144))
+GFX_COLOR(lightgrey, NS_RGB(211, 211, 211))
+GFX_COLOR(lightpink, NS_RGB(255, 182, 193))
+GFX_COLOR(lightsalmon, NS_RGB(255, 160, 122))
+GFX_COLOR(lightseagreen, NS_RGB( 32, 178, 170))
+GFX_COLOR(lightskyblue, NS_RGB(135, 206, 250))
+GFX_COLOR(lightslategray, NS_RGB(119, 136, 153))
+GFX_COLOR(lightslategrey, NS_RGB(119, 136, 153))
+GFX_COLOR(lightsteelblue, NS_RGB(176, 196, 222))
+GFX_COLOR(lightyellow, NS_RGB(255, 255, 224))
+GFX_COLOR(lime, NS_RGB( 0, 255, 0))
+GFX_COLOR(limegreen, NS_RGB( 50, 205, 50))
+GFX_COLOR(linen, NS_RGB(250, 240, 230))
+GFX_COLOR(magenta, NS_RGB(255, 0, 255))
+GFX_COLOR(maroon, NS_RGB(128, 0, 0))
+GFX_COLOR(mediumaquamarine, NS_RGB(102, 205, 170))
+GFX_COLOR(mediumblue, NS_RGB( 0, 0, 205))
+GFX_COLOR(mediumorchid, NS_RGB(186, 85, 211))
+GFX_COLOR(mediumpurple, NS_RGB(147, 112, 219))
+GFX_COLOR(mediumseagreen, NS_RGB( 60, 179, 113))
+GFX_COLOR(mediumslateblue, NS_RGB(123, 104, 238))
+GFX_COLOR(mediumspringgreen, NS_RGB( 0, 250, 154))
+GFX_COLOR(mediumturquoise, NS_RGB( 72, 209, 204))
+GFX_COLOR(mediumvioletred, NS_RGB(199, 21, 133))
+GFX_COLOR(midnightblue, NS_RGB( 25, 25, 112))
+GFX_COLOR(mintcream, NS_RGB(245, 255, 250))
+GFX_COLOR(mistyrose, NS_RGB(255, 228, 225))
+GFX_COLOR(moccasin, NS_RGB(255, 228, 181))
+GFX_COLOR(navajowhite, NS_RGB(255, 222, 173))
+GFX_COLOR(navy, NS_RGB( 0, 0, 128))
+GFX_COLOR(oldlace, NS_RGB(253, 245, 230))
+GFX_COLOR(olive, NS_RGB(128, 128, 0))
+GFX_COLOR(olivedrab, NS_RGB(107, 142, 35))
+GFX_COLOR(orange, NS_RGB(255, 165, 0))
+GFX_COLOR(orangered, NS_RGB(255, 69, 0))
+GFX_COLOR(orchid, NS_RGB(218, 112, 214))
+GFX_COLOR(palegoldenrod, NS_RGB(238, 232, 170))
+GFX_COLOR(palegreen, NS_RGB(152, 251, 152))
+GFX_COLOR(paleturquoise, NS_RGB(175, 238, 238))
+GFX_COLOR(palevioletred, NS_RGB(219, 112, 147))
+GFX_COLOR(papayawhip, NS_RGB(255, 239, 213))
+GFX_COLOR(peachpuff, NS_RGB(255, 218, 185))
+GFX_COLOR(peru, NS_RGB(205, 133, 63))
+GFX_COLOR(pink, NS_RGB(255, 192, 203))
+GFX_COLOR(plum, NS_RGB(221, 160, 221))
+GFX_COLOR(powderblue, NS_RGB(176, 224, 230))
+GFX_COLOR(purple, NS_RGB(128, 0, 128))
+GFX_COLOR(rebeccapurple, NS_RGB(102, 51, 153))
+GFX_COLOR(red, NS_RGB(255, 0, 0))
+GFX_COLOR(rosybrown, NS_RGB(188, 143, 143))
+GFX_COLOR(royalblue, NS_RGB( 65, 105, 225))
+GFX_COLOR(saddlebrown, NS_RGB(139, 69, 19))
+GFX_COLOR(salmon, NS_RGB(250, 128, 114))
+GFX_COLOR(sandybrown, NS_RGB(244, 164, 96))
+GFX_COLOR(seagreen, NS_RGB( 46, 139, 87))
+GFX_COLOR(seashell, NS_RGB(255, 245, 238))
+GFX_COLOR(sienna, NS_RGB(160, 82, 45))
+GFX_COLOR(silver, NS_RGB(192, 192, 192))
+GFX_COLOR(skyblue, NS_RGB(135, 206, 235))
+GFX_COLOR(slateblue, NS_RGB(106, 90, 205))
+GFX_COLOR(slategray, NS_RGB(112, 128, 144))
+GFX_COLOR(slategrey, NS_RGB(112, 128, 144))
+GFX_COLOR(snow, NS_RGB(255, 250, 250))
+GFX_COLOR(springgreen, NS_RGB( 0, 255, 127))
+GFX_COLOR(steelblue, NS_RGB( 70, 130, 180))
+GFX_COLOR(tan, NS_RGB(210, 180, 140))
+GFX_COLOR(teal, NS_RGB( 0, 128, 128))
+GFX_COLOR(thistle, NS_RGB(216, 191, 216))
+GFX_COLOR(tomato, NS_RGB(255, 99, 71))
+GFX_COLOR(transparent, NS_RGBA(0, 0, 0, 0))
+GFX_COLOR(turquoise, NS_RGB( 64, 224, 208))
+GFX_COLOR(violet, NS_RGB(238, 130, 238))
+GFX_COLOR(wheat, NS_RGB(245, 222, 179))
+GFX_COLOR(white, NS_RGB(255, 255, 255))
+GFX_COLOR(whitesmoke, NS_RGB(245, 245, 245))
+GFX_COLOR(yellow, NS_RGB(255, 255, 0))
+GFX_COLOR(yellowgreen, NS_RGB(154, 205, 50))
+
diff --git a/system/graphics/src/nsColorNames.h b/system/graphics/src/nsColorNames.h
new file mode 100644
index 000000000..388789710
--- /dev/null
+++ b/system/graphics/src/nsColorNames.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsColorNames_h___
+#define nsColorNames_h___
+
+
+class nsColorNames {
+public:
+ static void AddRefTable(void);
+ static void ReleaseTable(void);
+};
+
+#endif /* nsColorNames_h___ */
diff --git a/system/graphics/src/nsCoord.h b/system/graphics/src/nsCoord.h
new file mode 100644
index 000000000..bb53611cf
--- /dev/null
+++ b/system/graphics/src/nsCoord.h
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSCOORD_H
+#define NSCOORD_H
+
+#include "nsAlgorithm.h"
+#include "nscore.h"
+#include "nsMathUtils.h"
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+
+#include "nsDebug.h"
+#include <algorithm>
+
+/*
+ * Basic type used for the geometry classes.
+ *
+ * Normally all coordinates are maintained in an app unit coordinate
+ * space. An app unit is 1/60th of a CSS device pixel, which is, in turn
+ * an integer number of device pixels, such at the CSS DPI is as close to
+ * 96dpi as possible.
+ */
+
+// This controls whether we're using integers or floats for coordinates. We
+// want to eventually use floats.
+//#define NS_COORD_IS_FLOAT
+
+inline float NS_IEEEPositiveInfinity() {
+ union { uint32_t mPRUint32; float mFloat; } pun;
+ pun.mPRUint32 = 0x7F800000;
+ return pun.mFloat;
+}
+inline bool NS_IEEEIsNan(float aF) {
+ union { uint32_t mBits; float mFloat; } pun;
+ pun.mFloat = aF;
+ return (pun.mBits & 0x7F800000) == 0x7F800000 &&
+ (pun.mBits & 0x007FFFFF) != 0;
+}
+
+#ifdef NS_COORD_IS_FLOAT
+typedef float nscoord;
+#define nscoord_MAX NS_IEEEPositiveInfinity()
+#else
+typedef int32_t nscoord;
+#define nscoord_MAX nscoord(1 << 30)
+#endif
+
+#define nscoord_MIN (-nscoord_MAX)
+
+inline void VERIFY_COORD(nscoord aCoord) {
+#ifdef NS_COORD_IS_FLOAT
+ NS_ASSERTION(floorf(aCoord) == aCoord,
+ "Coords cannot have fractions");
+#endif
+}
+
+/**
+ * Divide aSpace by aN. Assign the resulting quotient to aQuotient and
+ * return the remainder.
+ */
+inline nscoord NSCoordDivRem(nscoord aSpace, size_t aN, nscoord* aQuotient)
+{
+#ifdef NS_COORD_IS_FLOAT
+ *aQuotient = aSpace / aN;
+ return 0.0f;
+#else
+ div_t result = div(aSpace, aN);
+ *aQuotient = nscoord(result.quot);
+ return nscoord(result.rem);
+#endif
+}
+
+inline nscoord NSCoordMulDiv(nscoord aMult1, nscoord aMult2, nscoord aDiv) {
+#ifdef NS_COORD_IS_FLOAT
+ return (aMult1 * aMult2 / aDiv);
+#else
+ return (int64_t(aMult1) * int64_t(aMult2) / int64_t(aDiv));
+#endif
+}
+
+inline nscoord NSToCoordRound(float aValue)
+{
+#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
+ return NS_lroundup30(aValue);
+#else
+ return nscoord(floorf(aValue + 0.5f));
+#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
+}
+
+inline nscoord NSToCoordRound(double aValue)
+{
+#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
+ return NS_lroundup30((float)aValue);
+#else
+ return nscoord(floor(aValue + 0.5f));
+#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
+}
+
+inline nscoord NSToCoordRoundWithClamp(float aValue)
+{
+#ifndef NS_COORD_IS_FLOAT
+ // Bounds-check before converting out of float, to avoid overflow
+ if (aValue >= nscoord_MAX) {
+ return nscoord_MAX;
+ }
+ if (aValue <= nscoord_MIN) {
+ return nscoord_MIN;
+ }
+#endif
+ return NSToCoordRound(aValue);
+}
+
+/**
+ * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
+ * appropriate for the signs of aCoord and aScale. If requireNotNegative is
+ * true, this method will enforce that aScale is not negative; use that
+ * parametrization to get a check of that fact in debug builds.
+ */
+inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale,
+ bool requireNotNegative) {
+ VERIFY_COORD(aCoord);
+ if (requireNotNegative) {
+ MOZ_ASSERT(aScale >= 0.0f,
+ "negative scaling factors must be handled manually");
+ }
+#ifdef NS_COORD_IS_FLOAT
+ return floorf(aCoord * aScale);
+#else
+ float product = aCoord * aScale;
+ if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0))
+ return NSToCoordRoundWithClamp(std::min<float>(nscoord_MAX, product));
+ return NSToCoordRoundWithClamp(std::max<float>(nscoord_MIN, product));
+#endif
+}
+
+/**
+ * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
+ * appropriate for the sign of aCoord. This method requires aScale to not be
+ * negative; use this method when you know that aScale should never be
+ * negative to get a sanity check of that invariant in debug builds.
+ */
+inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord, float aScale) {
+ return _nscoordSaturatingMultiply(aCoord, aScale, true);
+}
+
+/**
+ * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
+ * appropriate for the signs of aCoord and aScale.
+ */
+inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) {
+ return _nscoordSaturatingMultiply(aCoord, aScale, false);
+}
+
+/**
+ * Returns a + b, capping the sum to nscoord_MAX.
+ *
+ * This function assumes that neither argument is nscoord_MIN.
+ *
+ * Note: If/when we start using floats for nscoords, this function won't be as
+ * necessary. Normal float addition correctly handles adding with infinity,
+ * assuming we aren't adding nscoord_MIN. (-infinity)
+ */
+inline nscoord
+NSCoordSaturatingAdd(nscoord a, nscoord b)
+{
+ VERIFY_COORD(a);
+ VERIFY_COORD(b);
+
+#ifdef NS_COORD_IS_FLOAT
+ // Float math correctly handles a+b, given that neither is -infinity.
+ return a + b;
+#else
+ if (a == nscoord_MAX || b == nscoord_MAX) {
+ // infinity + anything = anything + infinity = infinity
+ return nscoord_MAX;
+ } else {
+ // a + b = a + b
+ // Cap the result, just in case we're dealing with numbers near nscoord_MAX
+ return std::min(nscoord_MAX, a + b);
+ }
+#endif
+}
+
+/**
+ * Returns a - b, gracefully handling cases involving nscoord_MAX.
+ * This function assumes that neither argument is nscoord_MIN.
+ *
+ * The behavior is as follows:
+ *
+ * a) infinity - infinity -> infMinusInfResult
+ * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED)
+ * c) infinity - N -> infinity
+ * d) N1 - N2 -> N1 - N2
+ *
+ * Note: For float nscoords, cases (c) and (d) are handled by normal float
+ * math. We still need to explicitly specify the behavior for cases (a)
+ * and (b), though. (Under normal float math, those cases would return NaN
+ * and -infinity, respectively.)
+ */
+inline nscoord
+NSCoordSaturatingSubtract(nscoord a, nscoord b,
+ nscoord infMinusInfResult)
+{
+ VERIFY_COORD(a);
+ VERIFY_COORD(b);
+
+ if (b == nscoord_MAX) {
+ if (a == nscoord_MAX) {
+ // case (a)
+ return infMinusInfResult;
+ } else {
+ // case (b)
+ NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
+ return 0;
+ }
+ } else {
+#ifdef NS_COORD_IS_FLOAT
+ // case (c) and (d) for floats. (float math handles both)
+ return a - b;
+#else
+ if (a == nscoord_MAX) {
+ // case (c) for integers
+ return nscoord_MAX;
+ } else {
+ // case (d) for integers
+ // Cap the result, in case we're dealing with numbers near nscoord_MAX
+ return std::min(nscoord_MAX, a - b);
+ }
+#endif
+ }
+}
+
+inline float NSCoordToFloat(nscoord aCoord) {
+ VERIFY_COORD(aCoord);
+#ifdef NS_COORD_IS_FLOAT
+ NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion");
+#endif
+ return (float)aCoord;
+}
+
+/*
+ * Coord Rounding Functions
+ */
+inline nscoord NSToCoordFloor(float aValue)
+{
+ return nscoord(floorf(aValue));
+}
+
+inline nscoord NSToCoordFloor(double aValue)
+{
+ return nscoord(floor(aValue));
+}
+
+inline nscoord NSToCoordFloorClamped(float aValue)
+{
+#ifndef NS_COORD_IS_FLOAT
+ // Bounds-check before converting out of float, to avoid overflow
+ if (aValue >= nscoord_MAX) {
+ return nscoord_MAX;
+ }
+ if (aValue <= nscoord_MIN) {
+ return nscoord_MIN;
+ }
+#endif
+ return NSToCoordFloor(aValue);
+}
+
+inline nscoord NSToCoordCeil(float aValue)
+{
+ return nscoord(ceilf(aValue));
+}
+
+inline nscoord NSToCoordCeil(double aValue)
+{
+ return nscoord(ceil(aValue));
+}
+
+inline nscoord NSToCoordCeilClamped(double aValue)
+{
+#ifndef NS_COORD_IS_FLOAT
+ // Bounds-check before converting out of double, to avoid overflow
+ if (aValue >= nscoord_MAX) {
+ return nscoord_MAX;
+ }
+ if (aValue <= nscoord_MIN) {
+ return nscoord_MIN;
+ }
+#endif
+ return NSToCoordCeil(aValue);
+}
+
+// The NSToCoordTrunc* functions remove the fractional component of
+// aValue, and are thus equivalent to NSToCoordFloor* for positive
+// values and NSToCoordCeil* for negative values.
+
+inline nscoord NSToCoordTrunc(float aValue)
+{
+ // There's no need to use truncf() since it matches the default
+ // rules for float to integer conversion.
+ return nscoord(aValue);
+}
+
+inline nscoord NSToCoordTrunc(double aValue)
+{
+ // There's no need to use trunc() since it matches the default
+ // rules for float to integer conversion.
+ return nscoord(aValue);
+}
+
+inline nscoord NSToCoordTruncClamped(float aValue)
+{
+#ifndef NS_COORD_IS_FLOAT
+ // Bounds-check before converting out of float, to avoid overflow
+ if (aValue >= nscoord_MAX) {
+ return nscoord_MAX;
+ }
+ if (aValue <= nscoord_MIN) {
+ return nscoord_MIN;
+ }
+#endif
+ return NSToCoordTrunc(aValue);
+}
+
+inline nscoord NSToCoordTruncClamped(double aValue)
+{
+#ifndef NS_COORD_IS_FLOAT
+ // Bounds-check before converting out of double, to avoid overflow
+ if (aValue >= nscoord_MAX) {
+ return nscoord_MAX;
+ }
+ if (aValue <= nscoord_MIN) {
+ return nscoord_MIN;
+ }
+#endif
+ return NSToCoordTrunc(aValue);
+}
+
+/*
+ * Int Rounding Functions
+ */
+inline int32_t NSToIntFloor(float aValue)
+{
+ return int32_t(floorf(aValue));
+}
+
+inline int32_t NSToIntCeil(float aValue)
+{
+ return int32_t(ceilf(aValue));
+}
+
+inline int32_t NSToIntRound(float aValue)
+{
+ return NS_lroundf(aValue);
+}
+
+inline int32_t NSToIntRound(double aValue)
+{
+ return NS_lround(aValue);
+}
+
+inline int32_t NSToIntRoundUp(double aValue)
+{
+ return int32_t(floor(aValue + 0.5));
+}
+
+/*
+ * App Unit/Pixel conversions
+ */
+inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel)
+{
+ return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel);
+}
+
+inline nscoord NSIntPixelsToAppUnits(int32_t aPixels, int32_t aAppUnitsPerPixel)
+{
+ // The cast to nscoord makes sure we don't overflow if we ever change
+ // nscoord to float
+ nscoord r = aPixels * (nscoord)aAppUnitsPerPixel;
+ VERIFY_COORD(r);
+ return r;
+}
+
+inline float NSAppUnitsToFloatPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
+{
+ return (float(aAppUnits) / aAppUnitsPerPixel);
+}
+
+inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, double aAppUnitsPerPixel)
+{
+ return (double(aAppUnits) / aAppUnitsPerPixel);
+}
+
+inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
+{
+ return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel);
+}
+
+inline float NSCoordScale(nscoord aCoord, int32_t aFromAPP, int32_t aToAPP)
+{
+ return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP;
+}
+
+/// handy constants
+#define TWIPS_PER_POINT_INT 20
+#define TWIPS_PER_POINT_FLOAT 20.0f
+#define POINTS_PER_INCH_INT 72
+#define POINTS_PER_INCH_FLOAT 72.0f
+#define CM_PER_INCH_FLOAT 2.54f
+#define MM_PER_INCH_FLOAT 25.4f
+
+/*
+ * Twips/unit conversions
+ */
+inline float NSUnitsToTwips(float aValue, float aPointsPerUnit)
+{
+ return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT;
+}
+
+inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint)
+{
+ return (aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT));
+}
+
+/// Unit conversion macros
+//@{
+#define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
+#define NS_INCHES_TO_TWIPS(x) NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch
+
+#define NS_MILLIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
+
+#define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
+#define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
+
+#define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
+
+#define NS_TWIPS_TO_MILLIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
+//@}
+
+#endif /* NSCOORD_H */
diff --git a/system/graphics/src/nsDeviceContext.cpp b/system/graphics/src/nsDeviceContext.cpp
new file mode 100644
index 000000000..e4288e47e
--- /dev/null
+++ b/system/graphics/src/nsDeviceContext.cpp
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDeviceContext.h"
+#include <algorithm> // for max
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h"
+#include "gfxFont.h" // for gfxFontGroup
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxPoint.h" // for gfxSize
+#include "mozilla/Attributes.h" // for final
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/PrintTarget.h"
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/Services.h" // for GetObserverService
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCRT.h" // for nsCRT
+#include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc
+#include "nsFont.h" // for nsFont
+#include "nsFontMetrics.h" // for nsFontMetrics
+#include "nsIAtom.h" // for nsIAtom, NS_Atomize
+#include "nsID.h"
+#include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
+#include "nsILanguageAtomService.h" // for nsILanguageAtomService, etc
+#include "nsIObserver.h" // for nsIObserver, etc
+#include "nsIObserverService.h" // for nsIObserverService
+#include "nsIScreen.h" // for nsIScreen
+#include "nsIScreenManager.h" // for nsIScreenManager
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
+#include "nsRect.h" // for nsRect
+#include "nsServiceManagerUtils.h" // for do_GetService
+#include "nsString.h" // for nsDependentString
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Logging.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::services::GetObserverService;
+
+class nsFontCache final : public nsIObserver
+{
+public:
+ nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ void Init(nsDeviceContext* aContext);
+ void Destroy();
+
+ already_AddRefed<nsFontMetrics> GetMetricsFor(
+ const nsFont& aFont, const nsFontMetrics::Params& aParams);
+
+ void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
+ void Compact();
+ void Flush();
+
+protected:
+ ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); }
+
+ nsDeviceContext* mContext; // owner
+ nsCOMPtr<nsIAtom> mLocaleLanguage;
+ nsTArray<nsFontMetrics*> mFontMetrics;
+};
+
+NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
+
+// The Init and Destroy methods are necessary because it's not
+// safe to call AddObserver from a constructor or RemoveObserver
+// from a destructor. That should be fixed.
+void
+nsFontCache::Init(nsDeviceContext* aContext)
+{
+ mContext = aContext;
+ // register as a memory-pressure observer to free font resources
+ // in low-memory situations.
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs)
+ obs->AddObserver(this, "memory-pressure", false);
+
+ nsCOMPtr<nsILanguageAtomService> langService;
+ langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+ if (langService) {
+ mLocaleLanguage = langService->GetLocaleLanguage();
+ }
+ if (!mLocaleLanguage) {
+ mLocaleLanguage = NS_Atomize("x-western");
+ }
+}
+
+void
+nsFontCache::Destroy()
+{
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs)
+ obs->RemoveObserver(this, "memory-pressure");
+ Flush();
+}
+
+NS_IMETHODIMP
+nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
+{
+ if (!nsCRT::strcmp(aTopic, "memory-pressure"))
+ Compact();
+ return NS_OK;
+}
+
+already_AddRefed<nsFontMetrics>
+nsFontCache::GetMetricsFor(const nsFont& aFont,
+ const nsFontMetrics::Params& aParams)
+{
+ nsIAtom* language = aParams.language ? aParams.language
+ : mLocaleLanguage.get();
+
+ // First check our cache
+ // start from the end, which is where we put the most-recent-used element
+
+ int32_t n = mFontMetrics.Length() - 1;
+ for (int32_t i = n; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ if (fm->Font().Equals(aFont) &&
+ fm->GetUserFontSet() == aParams.userFontSet &&
+ fm->Language() == language &&
+ fm->Orientation() == aParams.orientation) {
+ if (i != n) {
+ // promote it to the end of the cache
+ mFontMetrics.RemoveElementAt(i);
+ mFontMetrics.AppendElement(fm);
+ }
+ fm->GetThebesFontGroup()->UpdateUserFonts();
+ return do_AddRef(fm);
+ }
+ }
+
+ // It's not in the cache. Get font metrics and then cache them.
+
+ nsFontMetrics::Params params = aParams;
+ params.language = language;
+ RefPtr<nsFontMetrics> fm = new nsFontMetrics(aFont, params, mContext);
+ // the mFontMetrics list has the "head" at the end, because append
+ // is cheaper than insert
+ mFontMetrics.AppendElement(do_AddRef(fm.get()).take());
+ return fm.forget();
+}
+
+void
+nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
+{
+ mFontMetrics.RemoveElement(aFontMetrics);
+}
+
+void
+nsFontCache::Compact()
+{
+ // Need to loop backward because the running element can be removed on
+ // the way
+ for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ nsFontMetrics* oldfm = fm;
+ // Destroy() isn't here because we want our device context to be
+ // notified
+ NS_RELEASE(fm); // this will reset fm to nullptr
+ // if the font is really gone, it would have called back in
+ // FontMetricsDeleted() and would have removed itself
+ if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
+ // nope, the font is still there, so let's hold onto it too
+ NS_ADDREF(oldfm);
+ }
+ }
+}
+
+void
+nsFontCache::Flush()
+{
+ for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ // Destroy() will unhook our device context from the fm so that we
+ // won't waste time in triggering the notification of
+ // FontMetricsDeleted() in the subsequent release
+ fm->Destroy();
+ NS_RELEASE(fm);
+ }
+ mFontMetrics.Clear();
+}
+
+nsDeviceContext::nsDeviceContext()
+ : mWidth(0), mHeight(0), mDepth(0),
+ mAppUnitsPerDevPixel(-1), mAppUnitsPerDevPixelAtUnitFullZoom(-1),
+ mAppUnitsPerPhysicalInch(-1),
+ mFullZoom(1.0f), mPrintingScale(1.0f)
+#ifdef DEBUG
+ , mIsInitialized(false)
+#endif
+{
+ MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
+}
+
+nsDeviceContext::~nsDeviceContext()
+{
+ if (mFontCache) {
+ mFontCache->Destroy();
+ }
+}
+
+already_AddRefed<nsFontMetrics>
+nsDeviceContext::GetMetricsFor(const nsFont& aFont,
+ const nsFontMetrics::Params& aParams)
+{
+ if (!mFontCache) {
+ mFontCache = new nsFontCache();
+ mFontCache->Init(this);
+ }
+
+ return mFontCache->GetMetricsFor(aFont, aParams);
+}
+
+nsresult
+nsDeviceContext::FlushFontCache(void)
+{
+ if (mFontCache)
+ mFontCache->Flush();
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
+{
+ if (mFontCache) {
+ mFontCache->FontMetricsDeleted(aFontMetrics);
+ }
+ return NS_OK;
+}
+
+bool
+nsDeviceContext::IsPrinterContext()
+{
+ return mPrintTarget != nullptr;
+}
+
+void
+nsDeviceContext::SetDPI(double* aScale)
+{
+ float dpi = -1.0f;
+
+ // Use the printing DC to determine DPI values, if we have one.
+ if (mDeviceContextSpec) {
+ dpi = mDeviceContextSpec->GetDPI();
+ mPrintingScale = mDeviceContextSpec->GetPrintingScale();
+ mAppUnitsPerDevPixelAtUnitFullZoom =
+ NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
+ } else {
+ // A value of -1 means use the maximum of 96 and the system DPI.
+ // A value of 0 means use the system DPI. A positive value is used as the DPI.
+ // This sets the physical size of a device pixel and thus controls the
+ // interpretation of physical units.
+ int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
+
+ if (prefDPI > 0) {
+ dpi = prefDPI;
+ } else if (mWidget) {
+ dpi = mWidget->GetDPI();
+
+ if (prefDPI < 0) {
+ dpi = std::max(96.0f, dpi);
+ }
+ } else {
+ dpi = 96.0f;
+ }
+
+ double devPixelsPerCSSPixel;
+ if (aScale && *aScale > 0.0) {
+ // if caller provided a scale, we just use it
+ devPixelsPerCSSPixel = *aScale;
+ } else {
+ // otherwise get from the widget, and return it in aScale for
+ // the caller to pass to child contexts if needed
+ CSSToLayoutDeviceScale scale =
+ mWidget ? mWidget->GetDefaultScale()
+ : CSSToLayoutDeviceScale(1.0);
+ devPixelsPerCSSPixel = scale.scale;
+ if (aScale) {
+ *aScale = devPixelsPerCSSPixel;
+ }
+ }
+
+ mAppUnitsPerDevPixelAtUnitFullZoom =
+ std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
+ }
+
+ NS_ASSERTION(dpi != -1.0, "no dpi set");
+
+ mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
+ UpdateAppUnitsForFullZoom();
+}
+
+nsresult
+nsDeviceContext::Init(nsIWidget *aWidget)
+{
+#ifdef DEBUG
+ // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
+ // re-initializes nsDeviceContext objects. We can only assert in
+ // InitForPrinting (below).
+ mIsInitialized = true;
+#endif
+
+ nsresult rv = NS_OK;
+ if (mScreenManager && mWidget == aWidget)
+ return rv;
+
+ mWidget = aWidget;
+ SetDPI();
+
+ if (mScreenManager)
+ return rv;
+
+ mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
+
+ return rv;
+}
+
+// XXX This is only for printing. We should make that obvious in the name.
+already_AddRefed<gfxContext>
+nsDeviceContext::CreateRenderingContext()
+{
+ return CreateRenderingContextCommon(/* not a reference context */ false);
+}
+
+already_AddRefed<gfxContext>
+nsDeviceContext::CreateReferenceRenderingContext()
+{
+ return CreateRenderingContextCommon(/* a reference context */ true);
+}
+
+already_AddRefed<gfxContext>
+nsDeviceContext::CreateRenderingContextCommon(bool aWantReferenceContext)
+{
+ MOZ_ASSERT(IsPrinterContext());
+ MOZ_ASSERT(mWidth > 0 && mHeight > 0);
+
+ RefPtr<PrintTarget> printingTarget = mPrintTarget;
+
+ // This will usually be null, depending on the pref print.print_via_parent.
+ RefPtr<DrawEventRecorder> recorder;
+ mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
+
+ RefPtr<gfx::DrawTarget> dt;
+ if (aWantReferenceContext) {
+ dt = printingTarget->GetReferenceDrawTarget(recorder);
+ } else {
+ dt = printingTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
+ }
+
+ if (!dt || !dt->IsValid()) {
+ gfxCriticalNote
+ << "Failed to create draw target in device context sized "
+ << mWidth << "x" << mHeight << " and pointers "
+ << hexa(mPrintTarget) << " and " << hexa(printingTarget);
+ return nullptr;
+ }
+
+ dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
+
+ RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(pContext); // already checked draw target above
+
+ gfxMatrix transform;
+ if (printingTarget->RotateNeededForLandscape()) {
+ // Rotate page 90 degrees to draw landscape page on portrait paper
+ IntSize size = printingTarget->GetSize();
+ transform.Translate(gfxPoint(0, size.width));
+ gfxMatrix rotate(0, -1,
+ 1, 0,
+ 0, 0);
+ transform = rotate * transform;
+ }
+ transform.Scale(mPrintingScale, mPrintingScale);
+
+ pContext->SetMatrix(transform);
+ return pContext.forget();
+}
+
+nsresult
+nsDeviceContext::GetDepth(uint32_t& aDepth)
+{
+ if (mDepth == 0 && mScreenManager) {
+ nsCOMPtr<nsIScreen> primaryScreen;
+ mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
+ primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth));
+ }
+
+ aDepth = mDepth;
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
+{
+ if (IsPrinterContext()) {
+ aWidth = mWidth;
+ aHeight = mHeight;
+ } else {
+ nsRect area;
+ ComputeFullAreaUsingScreen(&area);
+ aWidth = area.width;
+ aHeight = area.height;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::GetRect(nsRect &aRect)
+{
+ if (IsPrinterContext()) {
+ aRect.x = 0;
+ aRect.y = 0;
+ aRect.width = mWidth;
+ aRect.height = mHeight;
+ } else
+ ComputeFullAreaUsingScreen ( &aRect );
+
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::GetClientRect(nsRect &aRect)
+{
+ if (IsPrinterContext()) {
+ aRect.x = 0;
+ aRect.y = 0;
+ aRect.width = mWidth;
+ aRect.height = mHeight;
+ }
+ else
+ ComputeClientRectUsingScreen(&aRect);
+
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
+{
+ NS_ENSURE_ARG_POINTER(aDevice);
+
+ MOZ_ASSERT(!mIsInitialized,
+ "Only initialize once, immediately after construction");
+
+ // We don't set mIsInitialized here. The Init() call below does that.
+
+ mPrintTarget = aDevice->MakePrintTarget();
+ if (!mPrintTarget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mDeviceContextSpec = aDevice;
+
+ Init(nullptr);
+
+ if (!CalcPrintingSize()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDeviceContext::BeginDocument(const nsAString& aTitle,
+ const nsAString& aPrintToFileName,
+ int32_t aStartPage,
+ int32_t aEndPage)
+{
+ nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName);
+
+ if (NS_SUCCEEDED(rv) && mDeviceContextSpec) {
+ rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
+ aStartPage, aEndPage);
+ }
+
+ return rv;
+}
+
+
+nsresult
+nsDeviceContext::EndDocument(void)
+{
+ nsresult rv = NS_OK;
+
+ if (mPrintTarget) {
+ rv = mPrintTarget->EndPrinting();
+ if (NS_SUCCEEDED(rv))
+ mPrintTarget->Finish();
+ }
+
+ if (mDeviceContextSpec)
+ mDeviceContextSpec->EndDocument();
+
+ return rv;
+}
+
+
+nsresult
+nsDeviceContext::AbortDocument(void)
+{
+ nsresult rv = mPrintTarget->AbortPrinting();
+
+ if (mDeviceContextSpec)
+ mDeviceContextSpec->EndDocument();
+
+ return rv;
+}
+
+
+nsresult
+nsDeviceContext::BeginPage(void)
+{
+ nsresult rv = NS_OK;
+
+ if (mDeviceContextSpec)
+ rv = mDeviceContextSpec->BeginPage();
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mPrintTarget->BeginPage();
+
+ return rv;
+}
+
+nsresult
+nsDeviceContext::EndPage(void)
+{
+ nsresult rv = mPrintTarget->EndPage();
+
+ if (mDeviceContextSpec)
+ mDeviceContextSpec->EndPage();
+
+ return rv;
+}
+
+void
+nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
+{
+ // we always need to recompute the clientRect
+ // because the window may have moved onto a different screen. In the single
+ // monitor case, we only need to do the computation if we haven't done it
+ // once already, and remember that we have because we're assured it won't change.
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen (getter_AddRefs(screen));
+ if (screen) {
+ int32_t x, y, width, height;
+ screen->GetAvailRect(&x, &y, &width, &height);
+
+ // convert to device units
+ outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
+ outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
+ outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
+ outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
+ }
+}
+
+void
+nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
+{
+ // if we have more than one screen, we always need to recompute the clientRect
+ // because the window may have moved onto a different screen. In the single
+ // monitor case, we only need to do the computation if we haven't done it
+ // once already, and remember that we have because we're assured it won't change.
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen ( getter_AddRefs(screen) );
+ if ( screen ) {
+ int32_t x, y, width, height;
+ screen->GetRect ( &x, &y, &width, &height );
+
+ // convert to device units
+ outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
+ outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
+ outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
+ outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
+
+ mWidth = outRect->width;
+ mHeight = outRect->height;
+ }
+}
+
+//
+// FindScreen
+//
+// Determines which screen intersects the largest area of the given surface.
+//
+void
+nsDeviceContext::FindScreen(nsIScreen** outScreen)
+{
+ if (!mWidget || !mScreenManager) {
+ return;
+ }
+
+ CheckDPIChange();
+
+ if (mWidget->GetOwningTabChild()) {
+ mScreenManager->ScreenForNativeWidget((void *)mWidget->GetOwningTabChild(),
+ outScreen);
+ }
+ else if (mWidget->GetNativeData(NS_NATIVE_WINDOW)) {
+ mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
+ outScreen);
+ }
+
+ if (!(*outScreen)) {
+ mScreenManager->GetPrimaryScreen(outScreen);
+ }
+}
+
+bool
+nsDeviceContext::CalcPrintingSize()
+{
+ if (!mPrintTarget) {
+ return (mWidth > 0 && mHeight > 0);
+ }
+
+ gfxSize size = mPrintTarget->GetSize();
+ // For printing, CSS inches and physical inches are identical
+ // so it doesn't matter which we use here
+ mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch()
+ / POINTS_PER_INCH_FLOAT);
+ mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch()
+ / POINTS_PER_INCH_FLOAT);
+
+ return (mWidth > 0 && mHeight > 0);
+}
+
+bool nsDeviceContext::CheckDPIChange(double* aScale)
+{
+ int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
+ int32_t oldInches = mAppUnitsPerPhysicalInch;
+
+ SetDPI(aScale);
+
+ return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
+ oldInches != mAppUnitsPerPhysicalInch;
+}
+
+bool
+nsDeviceContext::SetFullZoom(float aScale)
+{
+ if (aScale <= 0) {
+ NS_NOTREACHED("Invalid full zoom value");
+ return false;
+ }
+ int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
+ mFullZoom = aScale;
+ UpdateAppUnitsForFullZoom();
+ return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
+}
+
+void
+nsDeviceContext::UpdateAppUnitsForFullZoom()
+{
+ mAppUnitsPerDevPixel =
+ std::max(1, NSToIntRound(float(mAppUnitsPerDevPixelAtUnitFullZoom) / mFullZoom));
+ // adjust mFullZoom to reflect appunit rounding
+ mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
+}
+
+DesktopToLayoutDeviceScale
+nsDeviceContext::GetDesktopToDeviceScale()
+{
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen(getter_AddRefs(screen));
+
+ if (screen) {
+ double scale;
+ screen->GetContentsScaleFactor(&scale);
+ return DesktopToLayoutDeviceScale(scale);
+ }
+
+ return DesktopToLayoutDeviceScale(1.0);
+}
diff --git a/system/graphics/src/nsDeviceContext.h b/system/graphics/src/nsDeviceContext.h
new file mode 100644
index 000000000..edb3f5d6a
--- /dev/null
+++ b/system/graphics/src/nsDeviceContext.h
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NS_DEVICECONTEXT_H_
+#define _NS_DEVICECONTEXT_H_
+
+#include <stdint.h> // for uint32_t
+#include <sys/types.h> // for int32_t
+#include "gfxTypes.h" // for gfxFloat
+#include "gfxFont.h" // for gfxFont::Orientation
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsCoord.h" // for nscoord
+#include "nsError.h" // for nsresult
+#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsMathUtils.h" // for NS_round
+#include "nscore.h" // for char16_t, nsAString
+#include "mozilla/AppUnits.h" // for AppUnits
+#include "nsFontMetrics.h" // for nsFontMetrics::Params
+
+class gfxContext;
+class gfxTextPerfMetrics;
+class gfxUserFontSet;
+struct nsFont;
+class nsFontCache;
+class nsIAtom;
+class nsIDeviceContextSpec;
+class nsIScreen;
+class nsIScreenManager;
+class nsIWidget;
+struct nsRect;
+
+namespace mozilla {
+namespace gfx {
+class PrintTarget;
+}
+}
+
+class nsDeviceContext final
+{
+public:
+ typedef mozilla::gfx::PrintTarget PrintTarget;
+
+ nsDeviceContext();
+
+ NS_INLINE_DECL_REFCOUNTING(nsDeviceContext)
+
+ /**
+ * Initialize the device context from a widget
+ * @param aWidget a widget to initialize the device context from
+ * @return error status
+ */
+ nsresult Init(nsIWidget *aWidget);
+
+ /**
+ * Initialize the device context from a device context spec
+ * @param aDevSpec the specification of the printing device
+ * @return error status
+ */
+ nsresult InitForPrinting(nsIDeviceContextSpec *aDevSpec);
+
+ /**
+ * Create a rendering context and initialize it. Only call this
+ * method on device contexts that were initialized for printing.
+ *
+ * @return the new rendering context (guaranteed to be non-null)
+ */
+ already_AddRefed<gfxContext> CreateRenderingContext();
+
+ /**
+ * Create a reference rendering context and initialize it. Only call this
+ * method on device contexts that were initialized for printing.
+ *
+ * @return the new rendering context.
+ */
+ already_AddRefed<gfxContext> CreateReferenceRenderingContext();
+
+ /**
+ * Gets the number of app units in one CSS pixel; this number is global,
+ * not unique to each device context.
+ */
+ static int32_t AppUnitsPerCSSPixel() { return mozilla::AppUnitsPerCSSPixel(); }
+
+ /**
+ * Gets the number of app units in one device pixel; this number
+ * is usually a factor of AppUnitsPerCSSPixel(), although that is
+ * not guaranteed.
+ */
+ int32_t AppUnitsPerDevPixel() const { return mAppUnitsPerDevPixel; }
+
+ /**
+ * Convert device pixels which is used for gfx/thebes to nearest
+ * (rounded) app units
+ */
+ nscoord GfxUnitsToAppUnits(gfxFloat aGfxUnits) const
+ { return nscoord(NS_round(aGfxUnits * AppUnitsPerDevPixel())); }
+
+ /**
+ * Convert app units to device pixels which is used for gfx/thebes.
+ */
+ gfxFloat AppUnitsToGfxUnits(nscoord aAppUnits) const
+ { return gfxFloat(aAppUnits) / AppUnitsPerDevPixel(); }
+
+ /**
+ * Gets the number of app units in one physical inch; this is the
+ * device's DPI times AppUnitsPerDevPixel().
+ */
+ int32_t AppUnitsPerPhysicalInch() const
+ { return mAppUnitsPerPhysicalInch; }
+
+ /**
+ * Gets the number of app units in one CSS inch; this is
+ * 96 times AppUnitsPerCSSPixel.
+ */
+ static int32_t AppUnitsPerCSSInch() { return mozilla::AppUnitsPerCSSInch(); }
+
+ /**
+ * Get the ratio of app units to dev pixels that would be used at unit
+ * (100%) full zoom.
+ */
+ int32_t AppUnitsPerDevPixelAtUnitFullZoom() const
+ { return mAppUnitsPerDevPixelAtUnitFullZoom; }
+
+ /**
+ * Get the nsFontMetrics that describe the properties of
+ * an nsFont.
+ * @param aFont font description to obtain metrics for
+ */
+ already_AddRefed<nsFontMetrics> GetMetricsFor(
+ const nsFont& aFont, const nsFontMetrics::Params& aParams);
+
+ /**
+ * Notification when a font metrics instance created for this device is
+ * about to be deleted
+ */
+ nsresult FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
+
+ /**
+ * Attempt to free up resources by flushing out any fonts no longer
+ * referenced by anything other than the font cache itself.
+ * @return error status
+ */
+ nsresult FlushFontCache();
+
+ /**
+ * Return the bit depth of the device.
+ */
+ nsresult GetDepth(uint32_t& aDepth);
+
+ /**
+ * Get the size of the displayable area of the output device
+ * in app units.
+ * @param aWidth out parameter for width
+ * @param aHeight out parameter for height
+ * @return error status
+ */
+ nsresult GetDeviceSurfaceDimensions(nscoord& aWidth, nscoord& aHeight);
+
+ /**
+ * Get the size of the content area of the output device in app
+ * units. This corresponds on a screen device, for instance, to
+ * the entire screen.
+ * @param aRect out parameter for full rect. Position (x,y) will
+ * be (0,0) or relative to the primary monitor if
+ * this is not the primary.
+ * @return error status
+ */
+ nsresult GetRect(nsRect& aRect);
+
+ /**
+ * Get the size of the content area of the output device in app
+ * units. This corresponds on a screen device, for instance, to
+ * the area reported by GetDeviceSurfaceDimensions, minus the
+ * taskbar (Windows) or menubar (Macintosh).
+ * @param aRect out parameter for client rect. Position (x,y) will
+ * be (0,0) adjusted for any upper/left non-client
+ * space if present or relative to the primary
+ * monitor if this is not the primary.
+ * @return error status
+ */
+ nsresult GetClientRect(nsRect& aRect);
+
+ /**
+ * Inform the output device that output of a document is beginning
+ * Used for print related device contexts. Must be matched 1:1 with
+ * EndDocument() or AbortDocument().
+ *
+ * @param aTitle - title of Document
+ * @param aPrintToFileName - name of file to print to, if empty then don't
+ * print to file
+ * @param aStartPage - starting page number (must be greater than zero)
+ * @param aEndPage - ending page number (must be less than or
+ * equal to number of pages)
+ *
+ * @return error status
+ */
+ nsresult BeginDocument(const nsAString& aTitle,
+ const nsAString& aPrintToFileName,
+ int32_t aStartPage,
+ int32_t aEndPage);
+
+ /**
+ * Inform the output device that output of a document is ending.
+ * Used for print related device contexts. Must be matched 1:1 with
+ * BeginDocument()
+ * @return error status
+ */
+ nsresult EndDocument();
+
+ /**
+ * Inform the output device that output of a document is being aborted.
+ * Must be matched 1:1 with BeginDocument()
+ * @return error status
+ */
+ nsresult AbortDocument();
+
+ /**
+ * Inform the output device that output of a page is beginning
+ * Used for print related device contexts. Must be matched 1:1 with
+ * EndPage() and within a BeginDocument()/EndDocument() pair.
+ * @return error status
+ */
+ nsresult BeginPage();
+
+ /**
+ * Inform the output device that output of a page is ending
+ * Used for print related device contexts. Must be matched 1:1 with
+ * BeginPage() and within a BeginDocument()/EndDocument() pair.
+ * @return error status
+ */
+ nsresult EndPage();
+
+ /**
+ * Check to see if the DPI has changed, or impose a new DPI scale value.
+ * @param aScale - If non-null, the default (unzoomed) CSS to device pixel
+ * scale factor will be returned here; and if it is > 0.0
+ * on input, the given value will be used instead of
+ * getting it from the widget (if any). This is used to
+ * allow subdocument contexts to inherit the resolution
+ * setting of their parent.
+ * @return whether there was actually a change in the DPI (whether
+ * AppUnitsPerDevPixel() or AppUnitsPerPhysicalInch()
+ * changed)
+ */
+ bool CheckDPIChange(double* aScale = nullptr);
+
+ /**
+ * Set the full zoom factor: all lengths are multiplied by this factor
+ * when we convert them to device pixels. Returns whether the ratio of
+ * app units to dev pixels changed because of the zoom factor.
+ */
+ bool SetFullZoom(float aScale);
+
+ /**
+ * Returns the page full zoom factor applied.
+ */
+ float GetFullZoom() const { return mFullZoom; }
+
+ /**
+ * True if this device context was created for printing.
+ */
+ bool IsPrinterContext();
+
+ mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale();
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~nsDeviceContext();
+
+ /**
+ * Implementation shared by CreateRenderingContext and
+ * CreateReferenceRenderingContext.
+ */
+ already_AddRefed<gfxContext>
+ CreateRenderingContextCommon(bool aWantReferenceContext);
+
+ void SetDPI(double* aScale = nullptr);
+ void ComputeClientRectUsingScreen(nsRect *outRect);
+ void ComputeFullAreaUsingScreen(nsRect *outRect);
+ void FindScreen(nsIScreen **outScreen);
+
+ // Return false if the surface is not right
+ bool CalcPrintingSize();
+ void UpdateAppUnitsForFullZoom();
+
+ nscoord mWidth;
+ nscoord mHeight;
+ uint32_t mDepth;
+ int32_t mAppUnitsPerDevPixel;
+ int32_t mAppUnitsPerDevPixelAtUnitFullZoom;
+ int32_t mAppUnitsPerPhysicalInch;
+ float mFullZoom;
+ float mPrintingScale;
+
+ RefPtr<nsFontCache> mFontCache;
+ nsCOMPtr<nsIWidget> mWidget;
+ nsCOMPtr<nsIScreenManager> mScreenManager;
+ nsCOMPtr<nsIDeviceContextSpec> mDeviceContextSpec;
+ RefPtr<PrintTarget> mPrintTarget;
+#ifdef DEBUG
+ bool mIsInitialized;
+#endif
+};
+
+#endif /* _NS_DEVICECONTEXT_H_ */
diff --git a/system/graphics/src/nsFont.cpp b/system/graphics/src/nsFont.cpp
new file mode 100644
index 000000000..c5b1f09f0
--- /dev/null
+++ b/system/graphics/src/nsFont.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFont.h"
+#include "gfxFont.h" // for gfxFontStyle
+#include "gfxFontConstants.h" // for NS_FONT_KERNING_AUTO, etc
+#include "gfxFontFeatures.h" // for gfxFontFeature, etc
+#include "gfxFontUtils.h" // for TRUETYPE_TAG
+#include "nsCRT.h" // for nsCRT
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupports.h"
+#include "nsUnicharUtils.h"
+#include "nscore.h" // for char16_t
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace mozilla;
+
+nsFont::nsFont(const FontFamilyList& aFontlist, nscoord aSize)
+ : fontlist(aFontlist)
+{
+ Init();
+ size = aSize;
+}
+
+nsFont::nsFont(FontFamilyType aGenericType, nscoord aSize)
+ : fontlist(aGenericType)
+{
+ Init();
+ size = aSize;
+}
+
+void
+nsFont::Init()
+{
+ style = NS_FONT_STYLE_NORMAL;
+ weight = NS_FONT_WEIGHT_NORMAL;
+ stretch = NS_FONT_STRETCH_NORMAL;
+ systemFont = false;
+ smoothing = NS_FONT_SMOOTHING_AUTO;
+ sizeAdjust = -1.0f;
+ kerning = NS_FONT_KERNING_AUTO;
+ synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE;
+
+ variantAlternates = 0;
+ variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+ variantEastAsian = 0;
+ variantLigatures = 0;
+ variantNumeric = 0;
+ variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
+ variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
+}
+
+nsFont::nsFont(const nsFont& aOther) = default;
+
+nsFont::nsFont()
+{
+}
+
+nsFont::~nsFont()
+{
+}
+
+bool nsFont::Equals(const nsFont& aOther) const
+{
+ if ((style == aOther.style) &&
+ (systemFont == aOther.systemFont) &&
+ (weight == aOther.weight) &&
+ (stretch == aOther.stretch) &&
+ (size == aOther.size) &&
+ (sizeAdjust == aOther.sizeAdjust) &&
+ (fontlist == aOther.fontlist) &&
+ (kerning == aOther.kerning) &&
+ (synthesis == aOther.synthesis) &&
+ (fontFeatureSettings == aOther.fontFeatureSettings) &&
+ (languageOverride == aOther.languageOverride) &&
+ (variantAlternates == aOther.variantAlternates) &&
+ (variantCaps == aOther.variantCaps) &&
+ (variantEastAsian == aOther.variantEastAsian) &&
+ (variantLigatures == aOther.variantLigatures) &&
+ (variantNumeric == aOther.variantNumeric) &&
+ (variantPosition == aOther.variantPosition) &&
+ (variantWidth == aOther.variantWidth) &&
+ (alternateValues == aOther.alternateValues) &&
+ (featureValueLookup == aOther.featureValueLookup) &&
+ (smoothing == aOther.smoothing)) {
+ return true;
+ }
+ return false;
+}
+
+nsFont& nsFont::operator=(const nsFont& aOther) = default;
+
+void
+nsFont::CopyAlternates(const nsFont& aOther)
+{
+ variantAlternates = aOther.variantAlternates;
+ alternateValues = aOther.alternateValues;
+ featureValueLookup = aOther.featureValueLookup;
+}
+
+// mapping from bitflag to font feature tag/value pair
+//
+// these need to be kept in sync with the constants listed
+// in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78)
+
+// NS_FONT_VARIANT_EAST_ASIAN_xxx values
+const gfxFontFeature eastAsianDefaults[] = {
+ { TRUETYPE_TAG('j','p','7','8'), 1 },
+ { TRUETYPE_TAG('j','p','8','3'), 1 },
+ { TRUETYPE_TAG('j','p','9','0'), 1 },
+ { TRUETYPE_TAG('j','p','0','4'), 1 },
+ { TRUETYPE_TAG('s','m','p','l'), 1 },
+ { TRUETYPE_TAG('t','r','a','d'), 1 },
+ { TRUETYPE_TAG('f','w','i','d'), 1 },
+ { TRUETYPE_TAG('p','w','i','d'), 1 },
+ { TRUETYPE_TAG('r','u','b','y'), 1 }
+};
+
+static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults) ==
+ eFeatureEastAsian_numFeatures,
+ "eFeatureEastAsian_numFeatures should be correct");
+
+// NS_FONT_VARIANT_LIGATURES_xxx values
+const gfxFontFeature ligDefaults[] = {
+ { TRUETYPE_TAG('l','i','g','a'), 0 }, // none value means all off
+ { TRUETYPE_TAG('l','i','g','a'), 1 },
+ { TRUETYPE_TAG('l','i','g','a'), 0 },
+ { TRUETYPE_TAG('d','l','i','g'), 1 },
+ { TRUETYPE_TAG('d','l','i','g'), 0 },
+ { TRUETYPE_TAG('h','l','i','g'), 1 },
+ { TRUETYPE_TAG('h','l','i','g'), 0 },
+ { TRUETYPE_TAG('c','a','l','t'), 1 },
+ { TRUETYPE_TAG('c','a','l','t'), 0 }
+};
+
+static_assert(MOZ_ARRAY_LENGTH(ligDefaults) ==
+ eFeatureLigatures_numFeatures,
+ "eFeatureLigatures_numFeatures should be correct");
+
+// NS_FONT_VARIANT_NUMERIC_xxx values
+const gfxFontFeature numericDefaults[] = {
+ { TRUETYPE_TAG('l','n','u','m'), 1 },
+ { TRUETYPE_TAG('o','n','u','m'), 1 },
+ { TRUETYPE_TAG('p','n','u','m'), 1 },
+ { TRUETYPE_TAG('t','n','u','m'), 1 },
+ { TRUETYPE_TAG('f','r','a','c'), 1 },
+ { TRUETYPE_TAG('a','f','r','c'), 1 },
+ { TRUETYPE_TAG('z','e','r','o'), 1 },
+ { TRUETYPE_TAG('o','r','d','n'), 1 }
+};
+
+static_assert(MOZ_ARRAY_LENGTH(numericDefaults) ==
+ eFeatureNumeric_numFeatures,
+ "eFeatureNumeric_numFeatures should be correct");
+
+static void
+AddFontFeaturesBitmask(uint32_t aValue, uint32_t aMin, uint32_t aMax,
+ const gfxFontFeature aFeatureDefaults[],
+ nsTArray<gfxFontFeature>& aFeaturesOut)
+
+{
+ uint32_t i, m;
+
+ for (i = 0, m = aMin; m <= aMax; i++, m <<= 1) {
+ if (m & aValue) {
+ const gfxFontFeature& feature = aFeatureDefaults[i];
+ aFeaturesOut.AppendElement(feature);
+ }
+ }
+}
+
+static uint32_t
+FontFeatureTagForVariantWidth(uint32_t aVariantWidth)
+{
+ switch (aVariantWidth) {
+ case NS_FONT_VARIANT_WIDTH_FULL:
+ return TRUETYPE_TAG('f','w','i','d');
+ case NS_FONT_VARIANT_WIDTH_HALF:
+ return TRUETYPE_TAG('h','w','i','d');
+ case NS_FONT_VARIANT_WIDTH_THIRD:
+ return TRUETYPE_TAG('t','w','i','d');
+ case NS_FONT_VARIANT_WIDTH_QUARTER:
+ return TRUETYPE_TAG('q','w','i','d');
+ default:
+ return 0;
+ }
+}
+
+void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const
+{
+ // add in font-variant features
+ gfxFontFeature setting;
+
+ // -- kerning
+ setting.mTag = TRUETYPE_TAG('k','e','r','n');
+ switch (kerning) {
+ case NS_FONT_KERNING_NONE:
+ setting.mValue = 0;
+ aStyle->featureSettings.AppendElement(setting);
+ break;
+ case NS_FONT_KERNING_NORMAL:
+ setting.mValue = 1;
+ aStyle->featureSettings.AppendElement(setting);
+ break;
+ default:
+ // auto case implies use user agent default
+ break;
+ }
+
+ // -- alternates
+ if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_HISTORICAL) {
+ setting.mValue = 1;
+ setting.mTag = TRUETYPE_TAG('h','i','s','t');
+ aStyle->featureSettings.AppendElement(setting);
+ }
+
+ // -- copy font-specific alternate info into style
+ // (this will be resolved after font-matching occurs)
+ aStyle->alternateValues.AppendElements(alternateValues);
+ aStyle->featureValueLookup = featureValueLookup;
+
+ // -- caps
+ aStyle->variantCaps = variantCaps;
+
+ // -- east-asian
+ if (variantEastAsian) {
+ AddFontFeaturesBitmask(variantEastAsian,
+ NS_FONT_VARIANT_EAST_ASIAN_JIS78,
+ NS_FONT_VARIANT_EAST_ASIAN_RUBY,
+ eastAsianDefaults, aStyle->featureSettings);
+ }
+
+ // -- ligatures
+ if (variantLigatures) {
+ AddFontFeaturesBitmask(variantLigatures,
+ NS_FONT_VARIANT_LIGATURES_NONE,
+ NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL,
+ ligDefaults, aStyle->featureSettings);
+
+ if (variantLigatures & NS_FONT_VARIANT_LIGATURES_COMMON) {
+ // liga already enabled, need to enable clig also
+ setting.mTag = TRUETYPE_TAG('c','l','i','g');
+ setting.mValue = 1;
+ aStyle->featureSettings.AppendElement(setting);
+ } else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NO_COMMON) {
+ // liga already disabled, need to disable clig also
+ setting.mTag = TRUETYPE_TAG('c','l','i','g');
+ setting.mValue = 0;
+ aStyle->featureSettings.AppendElement(setting);
+ } else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NONE) {
+ // liga already disabled, need to disable dlig, hlig, calt, clig
+ setting.mValue = 0;
+ setting.mTag = TRUETYPE_TAG('d','l','i','g');
+ aStyle->featureSettings.AppendElement(setting);
+ setting.mTag = TRUETYPE_TAG('h','l','i','g');
+ aStyle->featureSettings.AppendElement(setting);
+ setting.mTag = TRUETYPE_TAG('c','a','l','t');
+ aStyle->featureSettings.AppendElement(setting);
+ setting.mTag = TRUETYPE_TAG('c','l','i','g');
+ aStyle->featureSettings.AppendElement(setting);
+ }
+ }
+
+ // -- numeric
+ if (variantNumeric) {
+ AddFontFeaturesBitmask(variantNumeric,
+ NS_FONT_VARIANT_NUMERIC_LINING,
+ NS_FONT_VARIANT_NUMERIC_ORDINAL,
+ numericDefaults, aStyle->featureSettings);
+ }
+
+ // -- position
+ aStyle->variantSubSuper = variantPosition;
+
+ // -- width
+ setting.mTag = FontFeatureTagForVariantWidth(variantWidth);
+ if (setting.mTag) {
+ setting.mValue = 1;
+ aStyle->featureSettings.AppendElement(setting);
+ }
+
+ // indicate common-path case when neither variantCaps or variantSubSuper are set
+ aStyle->noFallbackVariantFeatures =
+ (aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL) &&
+ (variantPosition == NS_FONT_VARIANT_POSITION_NORMAL);
+
+ // add in features from font-feature-settings
+ aStyle->featureSettings.AppendElements(fontFeatureSettings);
+
+ // enable grayscale antialiasing for text
+ if (smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
+ aStyle->useGrayscaleAntialiasing = true;
+ }
+}
diff --git a/system/graphics/src/nsFont.h b/system/graphics/src/nsFont.h
new file mode 100644
index 000000000..d21ce2593
--- /dev/null
+++ b/system/graphics/src/nsFont.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsFont_h___
+#define nsFont_h___
+
+#include <stdint.h> // for uint8_t, uint16_t
+#include <sys/types.h> // for int16_t
+#include "gfxFontFamilyList.h"
+#include "gfxFontFeatures.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsCoord.h" // for nscoord
+#include "nsStringFwd.h" // for nsSubstring
+#include "nsString.h" // for nsString
+#include "nsTArray.h" // for nsTArray
+
+struct gfxFontStyle;
+
+// XXX we need a method to enumerate all of the possible fonts on the
+// system across family, weight, style, size, etc. But not here!
+
+// Enumerator callback function. Return false to stop
+typedef bool (*nsFontFamilyEnumFunc)(const nsString& aFamily, bool aGeneric, void *aData);
+
+// IDs for generic fonts
+// NOTE: 0, 1 are reserved for the special IDs of the default variable
+// and fixed fonts in the presentation context, see nsPresContext.h
+const uint8_t kGenericFont_NONE = 0x00;
+// Special
+const uint8_t kGenericFont_moz_variable = 0x00; // for the default variable width font
+const uint8_t kGenericFont_moz_fixed = 0x01; // our special "use the user's fixed font"
+// CSS
+const uint8_t kGenericFont_serif = 0x02;
+const uint8_t kGenericFont_sans_serif = 0x04;
+const uint8_t kGenericFont_monospace = 0x08;
+const uint8_t kGenericFont_cursive = 0x10;
+const uint8_t kGenericFont_fantasy = 0x20;
+
+// Font structure.
+struct nsFont {
+
+ // list of font families, either named or generic
+ mozilla::FontFamilyList fontlist;
+
+ // The style of font (normal, italic, oblique; see gfxFontConstants.h)
+ uint8_t style;
+
+ // Force this font to not be considered a 'generic' font, even if
+ // the name is the same as a CSS generic font family.
+ bool systemFont;
+
+ // Variant subproperties
+ uint8_t variantCaps;
+ uint8_t variantNumeric;
+ uint8_t variantPosition;
+ uint8_t variantWidth;
+
+ uint16_t variantLigatures;
+ uint16_t variantEastAsian;
+
+ // Some font-variant-alternates property values require
+ // font-specific settings defined via @font-feature-values rules.
+ // These are resolved *after* font matching occurs.
+
+ // -- bitmask for both enumerated and functional propvals
+ uint16_t variantAlternates;
+
+ // Smoothing - controls subpixel-antialiasing (currently OSX only)
+ uint8_t smoothing;
+
+ // The weight of the font; see gfxFontConstants.h.
+ uint16_t weight;
+
+ // The stretch of the font (the sum of various NS_FONT_STRETCH_*
+ // constants; see gfxFontConstants.h).
+ int16_t stretch;
+
+ // Kerning
+ uint8_t kerning;
+
+ // Synthesis setting, controls use of fake bolding/italics
+ uint8_t synthesis;
+
+ // The logical size of the font, in nscoord units
+ nscoord size;
+
+ // The aspect-value (ie., the ratio actualsize:actualxheight) that any
+ // actual physical font created from this font structure must have when
+ // rendering or measuring a string. A value of -1.0 means no adjustment
+ // needs to be done; otherwise the value must be nonnegative.
+ float sizeAdjust;
+
+ // -- list of value tags for font-specific alternate features
+ nsTArray<gfxAlternateValue> alternateValues;
+
+ // -- object used to look these up once the font is matched
+ RefPtr<gfxFontFeatureValueSet> featureValueLookup;
+
+ // Font features from CSS font-feature-settings
+ nsTArray<gfxFontFeature> fontFeatureSettings;
+
+ // Language system tag, to override document language;
+ // this is an OpenType "language system" tag represented as a 32-bit integer
+ // (see http://www.microsoft.com/typography/otspec/languagetags.htm).
+ nsString languageOverride;
+
+ // initialize the font with a fontlist
+ nsFont(const mozilla::FontFamilyList& aFontlist, nscoord aSize);
+
+ // initialize the font with a single generic
+ nsFont(mozilla::FontFamilyType aGenericType, nscoord aSize);
+
+ // Make a copy of the given font
+ nsFont(const nsFont& aFont);
+
+ // leave members uninitialized
+ nsFont();
+
+ ~nsFont();
+
+ bool operator==(const nsFont& aOther) const {
+ return Equals(aOther);
+ }
+
+ bool operator!=(const nsFont& aOther) const {
+ return !Equals(aOther);
+ }
+
+ bool Equals(const nsFont& aOther) const;
+
+ nsFont& operator=(const nsFont& aOther);
+
+ void CopyAlternates(const nsFont& aOther);
+
+ // Add featureSettings into style
+ void AddFontFeaturesToStyle(gfxFontStyle *aStyle) const;
+
+protected:
+ void Init(); // helper method for initialization
+};
+
+#define NS_FONT_VARIANT_NORMAL 0
+#define NS_FONT_VARIANT_SMALL_CAPS 1
+
+#endif /* nsFont_h___ */
diff --git a/system/graphics/src/nsFontMetrics.cpp b/system/graphics/src/nsFontMetrics.cpp
new file mode 100644
index 000000000..062bc73a2
--- /dev/null
+++ b/system/graphics/src/nsFontMetrics.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFontMetrics.h"
+#include <math.h> // for floor, ceil
+#include <algorithm> // for max
+#include "gfxFontConstants.h" // for NS_FONT_SYNTHESIS_*
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPoint.h" // for gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfxTypes.h" // for gfxFloat
+#include "nsBoundingMetrics.h" // for nsBoundingMetrics
+#include "nsDebug.h" // for NS_ERROR
+#include "nsDeviceContext.h" // for nsDeviceContext
+#include "nsIAtom.h" // for nsIAtom
+#include "nsMathUtils.h" // for NS_round
+#include "nsRenderingContext.h" // for nsRenderingContext
+#include "nsString.h" // for nsString
+#include "nsStyleConsts.h" // for NS_STYLE_HYPHENS_NONE
+#include "mozilla/Assertions.h" // for MOZ_ASSERT
+#include "mozilla/UniquePtr.h" // for UniquePtr
+
+class gfxUserFontSet;
+using namespace mozilla;
+
+namespace {
+
+class AutoTextRun {
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
+ const char* aString, int32_t aLength)
+ {
+ mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
+ reinterpret_cast<const uint8_t*>(aString), aLength,
+ aDrawTarget,
+ aMetrics->AppUnitsPerDevPixel(),
+ ComputeFlags(aMetrics),
+ nullptr);
+ }
+
+ AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
+ const char16_t* aString, int32_t aLength)
+ {
+ mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
+ aString, aLength,
+ aDrawTarget,
+ aMetrics->AppUnitsPerDevPixel(),
+ ComputeFlags(aMetrics),
+ nullptr);
+ }
+
+ gfxTextRun *get() { return mTextRun.get(); }
+ gfxTextRun *operator->() { return mTextRun.get(); }
+
+private:
+ static uint32_t ComputeFlags(nsFontMetrics* aMetrics) {
+ uint32_t flags = 0;
+ if (aMetrics->GetTextRunRTL()) {
+ flags |= gfxTextRunFactory::TEXT_IS_RTL;
+ }
+ if (aMetrics->GetVertical()) {
+ switch (aMetrics->GetTextOrientation()) {
+ case NS_STYLE_TEXT_ORIENTATION_MIXED:
+ flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED;
+ break;
+ case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
+ flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+ break;
+ case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
+ flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+ break;
+ }
+ }
+ return flags;
+ }
+
+ RefPtr<gfxTextRun> mTextRun;
+};
+
+class StubPropertyProvider : public gfxTextRun::PropertyProvider {
+public:
+ virtual void GetHyphenationBreaks(gfxTextRun::Range aRange,
+ bool* aBreakBefore) {
+ NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
+ }
+ virtual int8_t GetHyphensOption() {
+ NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
+ return NS_STYLE_HYPHENS_NONE;
+ }
+ virtual gfxFloat GetHyphenWidth() {
+ NS_ERROR("This shouldn't be called because we never enable hyphens");
+ return 0;
+ }
+ virtual already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() {
+ NS_ERROR("This shouldn't be called because we never enable hyphens");
+ return nullptr;
+ }
+ virtual uint32_t GetAppUnitsPerDevUnit() {
+ NS_ERROR("This shouldn't be called because we never enable hyphens");
+ return 60;
+ }
+ virtual void GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) {
+ NS_ERROR("This shouldn't be called because we never enable spacing");
+ }
+};
+
+} // namespace
+
+nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
+ nsDeviceContext *aContext)
+ : mFont(aFont)
+ , mLanguage(aParams.language)
+ , mDeviceContext(aContext)
+ , mP2A(aContext->AppUnitsPerDevPixel())
+ , mOrientation(aParams.orientation)
+ , mTextRunRTL(false)
+ , mVertical(false)
+ , mTextOrientation(0)
+{
+ gfxFontStyle style(aFont.style,
+ aFont.weight,
+ aFont.stretch,
+ gfxFloat(aFont.size) / mP2A,
+ aParams.language,
+ aParams.explicitLanguage,
+ aFont.sizeAdjust,
+ aFont.systemFont,
+ mDeviceContext->IsPrinterContext(),
+ aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
+ aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
+ aFont.languageOverride);
+
+ aFont.AddFontFeaturesToStyle(&style);
+
+ gfxFloat devToCssSize = gfxFloat(mP2A) /
+ gfxFloat(mDeviceContext->AppUnitsPerCSSPixel());
+ mFontGroup = gfxPlatform::GetPlatform()->
+ CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
+ aParams.userFontSet, devToCssSize);
+}
+
+nsFontMetrics::~nsFontMetrics()
+{
+ if (mDeviceContext) {
+ mDeviceContext->FontMetricsDeleted(this);
+ }
+}
+
+void
+nsFontMetrics::Destroy()
+{
+ mDeviceContext = nullptr;
+}
+
+// XXXTODO get rid of this macro
+#define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
+#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
+
+const gfxFont::Metrics&
+nsFontMetrics::GetMetrics(gfxFont::Orientation aOrientation) const
+{
+ return mFontGroup->GetFirstValidFont()->GetMetrics(aOrientation);
+}
+
+nscoord
+nsFontMetrics::XHeight()
+{
+ return ROUND_TO_TWIPS(GetMetrics().xHeight);
+}
+
+nscoord
+nsFontMetrics::CapHeight()
+{
+ return ROUND_TO_TWIPS(GetMetrics().capHeight);
+}
+
+nscoord
+nsFontMetrics::SuperscriptOffset()
+{
+ return ROUND_TO_TWIPS(GetMetrics().emHeight *
+ NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
+}
+
+nscoord
+nsFontMetrics::SubscriptOffset()
+{
+ return ROUND_TO_TWIPS(GetMetrics().emHeight *
+ NS_FONT_SUBSCRIPT_OFFSET_RATIO);
+}
+
+void
+nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
+{
+ aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset);
+ aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize);
+}
+
+void
+nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
+{
+ aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
+ aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
+}
+
+// GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
+// text-decoration lines drawable area. See bug 421353.
+// BE CAREFUL for rounding each values. The logic MUST be same as
+// nsCSSRendering::GetTextDecorationRectInternal's.
+
+static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
+ gfxFontGroup* aFontGroup)
+{
+ gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
+ gfxFloat size = NS_round(aMetrics.underlineSize);
+ gfxFloat minDescent = offset + size;
+ return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
+}
+
+static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
+{
+ return floor(aMetrics.maxAscent + 0.5);
+}
+
+nscoord
+nsFontMetrics::InternalLeading()
+{
+ return ROUND_TO_TWIPS(GetMetrics().internalLeading);
+}
+
+nscoord
+nsFontMetrics::ExternalLeading()
+{
+ return ROUND_TO_TWIPS(GetMetrics().externalLeading);
+}
+
+nscoord
+nsFontMetrics::EmHeight()
+{
+ return ROUND_TO_TWIPS(GetMetrics().emHeight);
+}
+
+nscoord
+nsFontMetrics::EmAscent()
+{
+ return ROUND_TO_TWIPS(GetMetrics().emAscent);
+}
+
+nscoord
+nsFontMetrics::EmDescent()
+{
+ return ROUND_TO_TWIPS(GetMetrics().emDescent);
+}
+
+nscoord
+nsFontMetrics::MaxHeight()
+{
+ return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
+ CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
+}
+
+nscoord
+nsFontMetrics::MaxAscent()
+{
+ return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
+}
+
+nscoord
+nsFontMetrics::MaxDescent()
+{
+ return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
+}
+
+nscoord
+nsFontMetrics::MaxAdvance()
+{
+ return CEIL_TO_TWIPS(GetMetrics().maxAdvance);
+}
+
+nscoord
+nsFontMetrics::AveCharWidth()
+{
+ // Use CEIL instead of ROUND for consistency with GetMaxAdvance
+ return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
+}
+
+nscoord
+nsFontMetrics::SpaceWidth()
+{
+ // For vertical text with mixed or sideways orientation, we want the
+ // width of a horizontal space (even if we're using vertical line-spacing
+ // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
+ return CEIL_TO_TWIPS(
+ GetMetrics(mVertical &&
+ mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT
+ ? gfxFont::eVertical
+ : gfxFont::eHorizontal).spaceWidth);
+}
+
+int32_t
+nsFontMetrics::GetMaxStringLength()
+{
+ const gfxFont::Metrics& m = GetMetrics();
+ const double x = 32767.0 / m.maxAdvance;
+ int32_t len = (int32_t)floor(x);
+ return std::max(1, len);
+}
+
+nscoord
+nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
+ DrawTarget* aDrawTarget)
+{
+ if (aLength == 0)
+ return 0;
+
+ if (aLength == 1 && aString[0] == ' ')
+ return SpaceWidth();
+
+ StubPropertyProvider provider;
+ AutoTextRun textRun(this, aDrawTarget, aString, aLength);
+ if (textRun.get()) {
+ return NSToCoordRound(
+ textRun->GetAdvanceWidth(Range(0, aLength), &provider));
+ }
+ return 0;
+}
+
+nscoord
+nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
+ DrawTarget* aDrawTarget)
+{
+ if (aLength == 0)
+ return 0;
+
+ if (aLength == 1 && aString[0] == ' ')
+ return SpaceWidth();
+
+ StubPropertyProvider provider;
+ AutoTextRun textRun(this, aDrawTarget, aString, aLength);
+ if (textRun.get()) {
+ return NSToCoordRound(
+ textRun->GetAdvanceWidth(Range(0, aLength), &provider));
+ }
+ return 0;
+}
+
+// Draw a string using this font handle on the surface passed in.
+void
+nsFontMetrics::DrawString(const char *aString, uint32_t aLength,
+ nscoord aX, nscoord aY,
+ nsRenderingContext *aContext)
+{
+ if (aLength == 0)
+ return;
+
+ StubPropertyProvider provider;
+ AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
+ if (!textRun.get()) {
+ return;
+ }
+ gfxPoint pt(aX, aY);
+ Range range(0, aLength);
+ if (mTextRunRTL) {
+ if (mVertical) {
+ pt.y += textRun->GetAdvanceWidth(range, &provider);
+ } else {
+ pt.x += textRun->GetAdvanceWidth(range, &provider);
+ }
+ }
+ gfxTextRun::DrawParams params(aContext->ThebesContext());
+ params.provider = &provider;
+ textRun->Draw(range, pt, params);
+}
+
+void
+nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
+ nscoord aX, nscoord aY,
+ nsRenderingContext *aContext,
+ DrawTarget* aTextRunConstructionDrawTarget)
+{
+ if (aLength == 0)
+ return;
+
+ StubPropertyProvider provider;
+ AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
+ if (!textRun.get()) {
+ return;
+ }
+ gfxPoint pt(aX, aY);
+ Range range(0, aLength);
+ if (mTextRunRTL) {
+ if (mVertical) {
+ pt.y += textRun->GetAdvanceWidth(range, &provider);
+ } else {
+ pt.x += textRun->GetAdvanceWidth(range, &provider);
+ }
+ }
+ gfxTextRun::DrawParams params(aContext->ThebesContext());
+ params.provider = &provider;
+ textRun->Draw(range, pt, params);
+}
+
+static nsBoundingMetrics
+GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString,
+ uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget,
+ gfxFont::BoundingBoxType aType)
+{
+ if (aLength == 0)
+ return nsBoundingMetrics();
+
+ StubPropertyProvider provider;
+ AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
+ nsBoundingMetrics m;
+ if (textRun.get()) {
+ gfxTextRun::Metrics theMetrics = textRun->MeasureText(
+ gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
+
+ m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X());
+ m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost());
+ m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y());
+ m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost());
+ m.width = NSToCoordRound( theMetrics.mAdvanceWidth);
+ }
+ return m;
+}
+
+nsBoundingMetrics
+nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength,
+ DrawTarget* aDrawTarget)
+{
+ return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
+ gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
+}
+
+nsBoundingMetrics
+nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength,
+ DrawTarget* aDrawTarget)
+{
+ return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
+ gfxFont::LOOSE_INK_EXTENTS);
+}
+
diff --git a/system/graphics/src/nsFontMetrics.h b/system/graphics/src/nsFontMetrics.h
new file mode 100644
index 000000000..f8a2c5b93
--- /dev/null
+++ b/system/graphics/src/nsFontMetrics.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSFONTMETRICS__H__
+#define NSFONTMETRICS__H__
+
+#include <stdint.h> // for uint32_t
+#include <sys/types.h> // for int32_t
+#include "gfxTextRun.h" // for gfxFont, gfxFontGroup
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsCoord.h" // for nscoord
+#include "nsError.h" // for nsresult
+#include "nsFont.h" // for nsFont
+#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nscore.h" // for char16_t
+
+class gfxUserFontSet;
+class gfxTextPerfMetrics;
+class nsDeviceContext;
+class nsIAtom;
+class nsRenderingContext;
+struct nsBoundingMetrics;
+
+/**
+ * Font metrics
+ *
+ * This class may be somewhat misnamed. A better name might be
+ * nsFontList. The style system uses the nsFont struct for various
+ * font properties, one of which is font-family, which can contain a
+ * *list* of font names. The nsFont struct is "realized" by asking the
+ * device context to cough up an nsFontMetrics object, which contains
+ * a list of real font handles, one for each font mentioned in
+ * font-family (and for each fallback when we fall off the end of that
+ * list).
+ *
+ * The style system needs to have access to certain metrics, such as
+ * the em height (for the CSS "em" unit), and we use the first Western
+ * font's metrics for that purpose. The platform-specific
+ * implementations are expected to select non-Western fonts that "fit"
+ * reasonably well with the Western font that is loaded at Init time.
+ */
+class nsFontMetrics final
+{
+public:
+ typedef gfxTextRun::Range Range;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ struct Params
+ {
+ nsIAtom* language = nullptr;
+ bool explicitLanguage = false;
+ gfxFont::Orientation orientation = gfxFont::eHorizontal;
+ gfxUserFontSet* userFontSet = nullptr;
+ gfxTextPerfMetrics* textPerf = nullptr;
+ };
+
+ nsFontMetrics(const nsFont& aFont, const Params& aParams,
+ nsDeviceContext *aContext);
+
+ NS_INLINE_DECL_REFCOUNTING(nsFontMetrics)
+
+ /**
+ * Destroy this font metrics. This breaks the association between
+ * the font metrics and the device context.
+ */
+ void Destroy();
+
+ /**
+ * Return the font's x-height.
+ */
+ nscoord XHeight();
+
+ /**
+ * Return the font's cap-height.
+ */
+ nscoord CapHeight();
+
+ /**
+ * Return the font's superscript offset (the distance from the
+ * baseline to where a superscript's baseline should be placed).
+ * The value returned will be positive.
+ */
+ nscoord SuperscriptOffset();
+
+ /**
+ * Return the font's subscript offset (the distance from the
+ * baseline to where a subscript's baseline should be placed).
+ * The value returned will be positive.
+ */
+ nscoord SubscriptOffset();
+
+ /**
+ * Return the font's strikeout offset (the distance from the
+ * baseline to where a strikeout should be placed) and size.
+ * Positive values are above the baseline, negative below.
+ */
+ void GetStrikeout(nscoord& aOffset, nscoord& aSize);
+
+ /**
+ * Return the font's underline offset (the distance from the
+ * baseline to where a underline should be placed) and size.
+ * Positive values are above the baseline, negative below.
+ */
+ void GetUnderline(nscoord& aOffset, nscoord& aSize);
+
+ /**
+ * Returns the amount of internal leading for the font.
+ * This is normally the difference between the max ascent
+ * and the em ascent.
+ */
+ nscoord InternalLeading();
+
+ /**
+ * Returns the amount of external leading for the font.
+ * em ascent(?) plus external leading is the font designer's
+ * recommended line-height for this font.
+ */
+ nscoord ExternalLeading();
+
+ /**
+ * Returns the height of the em square.
+ * This is em ascent plus em descent.
+ */
+ nscoord EmHeight();
+
+ /**
+ * Returns the ascent part of the em square.
+ */
+ nscoord EmAscent();
+
+ /**
+ * Returns the descent part of the em square.
+ */
+ nscoord EmDescent();
+
+ /**
+ * Returns the height of the bounding box.
+ * This is max ascent plus max descent.
+ */
+ nscoord MaxHeight();
+
+ /**
+ * Returns the maximum distance characters in this font extend
+ * above the base line.
+ */
+ nscoord MaxAscent();
+
+ /**
+ * Returns the maximum distance characters in this font extend
+ * below the base line.
+ */
+ nscoord MaxDescent();
+
+ /**
+ * Returns the maximum character advance for the font.
+ */
+ nscoord MaxAdvance();
+
+ /**
+ * Returns the average character width
+ */
+ nscoord AveCharWidth();
+
+ /**
+ * Returns the often needed width of the space character
+ */
+ nscoord SpaceWidth();
+
+ /**
+ * Returns the font associated with these metrics. The return value
+ * is only defined after Init() has been called.
+ */
+ const nsFont &Font() const { return mFont; }
+
+ /**
+ * Returns the language associated with these metrics
+ */
+ nsIAtom* Language() const { return mLanguage; }
+
+ /**
+ * Returns the orientation (horizontal/vertical) of these metrics.
+ */
+ gfxFont::Orientation Orientation() const { return mOrientation; }
+
+ int32_t GetMaxStringLength();
+
+ // Get the width for this string. aWidth will be updated with the
+ // width in points, not twips. Callers must convert it if they
+ // want it in another format.
+ nscoord GetWidth(const char* aString, uint32_t aLength,
+ DrawTarget* aDrawTarget);
+ nscoord GetWidth(const char16_t* aString, uint32_t aLength,
+ DrawTarget* aDrawTarget);
+
+ // Draw a string using this font handle on the surface passed in.
+ void DrawString(const char *aString, uint32_t aLength,
+ nscoord aX, nscoord aY,
+ nsRenderingContext *aContext);
+ void DrawString(const char16_t* aString, uint32_t aLength,
+ nscoord aX, nscoord aY,
+ nsRenderingContext *aContext,
+ DrawTarget* aTextRunConstructionDrawTarget);
+
+ nsBoundingMetrics GetBoundingMetrics(const char16_t *aString,
+ uint32_t aLength,
+ DrawTarget* aDrawTarget);
+
+ // Returns the LOOSE_INK_EXTENTS bounds of the text for determing the
+ // overflow area of the string.
+ nsBoundingMetrics GetInkBoundsForVisualOverflow(const char16_t *aString,
+ uint32_t aLength,
+ DrawTarget* aDrawTarget);
+
+ void SetTextRunRTL(bool aIsRTL) { mTextRunRTL = aIsRTL; }
+ bool GetTextRunRTL() const { return mTextRunRTL; }
+
+ void SetVertical(bool aVertical) { mVertical = aVertical; }
+ bool GetVertical() const { return mVertical; }
+
+ void SetTextOrientation(uint8_t aTextOrientation)
+ {
+ mTextOrientation = aTextOrientation;
+ }
+ uint8_t GetTextOrientation() const { return mTextOrientation; }
+
+ gfxFontGroup* GetThebesFontGroup() const { return mFontGroup; }
+ gfxUserFontSet* GetUserFontSet() const
+ {
+ return mFontGroup->GetUserFontSet();
+ }
+
+ int32_t AppUnitsPerDevPixel() const { return mP2A; }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~nsFontMetrics();
+
+ const gfxFont::Metrics& GetMetrics() const {
+ return GetMetrics(mOrientation);
+ }
+
+ const gfxFont::Metrics&
+ GetMetrics(const gfxFont::Orientation aFontOrientation) const;
+
+ nsFont mFont;
+ RefPtr<gfxFontGroup> mFontGroup;
+ nsCOMPtr<nsIAtom> mLanguage;
+ nsDeviceContext* mDeviceContext;
+ int32_t mP2A;
+
+ // The font orientation (horizontal or vertical) for which these metrics
+ // have been initialized. This determines which line metrics (ascent and
+ // descent) they will return.
+ gfxFont::Orientation mOrientation;
+
+ // These fields may be set by clients to control the behavior of methods
+ // like GetWidth and DrawString according to the writing mode, direction
+ // and text-orientation desired.
+ bool mTextRunRTL;
+ bool mVertical;
+ uint8_t mTextOrientation;
+};
+
+#endif /* NSFONTMETRICS__H__ */
diff --git a/system/graphics/src/nsGfxCIID.h b/system/graphics/src/nsGfxCIID.h
new file mode 100644
index 000000000..37cc27d0d
--- /dev/null
+++ b/system/graphics/src/nsGfxCIID.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsGfxCIID_h__
+#define nsGfxCIID_h__
+
+#define NS_FONT_ENUMERATOR_CID \
+{ 0xa6cf9115, 0x15b3, 0x11d2, \
+{ 0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
+
+#define NS_SCRIPTABLE_REGION_CID \
+{ 0xda5b130a, 0x1dd1, 0x11b2, \
+{ 0xad, 0x47, 0xf4, 0x55, 0xb1, 0x81, 0x4a, 0x78 } }
+
+#define NS_GFX_INITIALIZATION_CID \
+{ 0x67c41576, 0x9664, 0x4ed5, \
+{ 0x90, 0xc1, 0xf6, 0x68, 0x3f, 0xd5, 0x2c, 0x8f } }
+
+#endif
diff --git a/system/graphics/src/nsIFontEnumerator.idl b/system/graphics/src/nsIFontEnumerator.idl
new file mode 100644
index 000000000..f5f4c6468
--- /dev/null
+++ b/system/graphics/src/nsIFontEnumerator.idl
@@ -0,0 +1,61 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(924d98d9-3518-4cb4-8708-c74fe8e3ec3c)]
+interface nsIFontEnumerator : nsISupports
+{
+ /**
+ * Return a sorted array of the names of all installed fonts.
+ *
+ * @param aCount returns number of names returned
+ * @param aResult returns array of names
+ * @return void
+ */
+ void EnumerateAllFonts(out uint32_t aCount,
+ [retval, array, size_is(aCount)] out wstring aResult);
+
+ /**
+ * Return a sorted array of names of fonts that support the given language
+ * group and are suitable for use as the given CSS generic font.
+ *
+ * @param aLangGroup language group
+ * @param aGeneric CSS generic font
+ * @param aCount returns number of names returned
+ * @param aResult returns array of names
+ * @return void
+ */
+ void EnumerateFonts(in string aLangGroup, in string aGeneric,
+ out uint32_t aCount, [retval, array, size_is(aCount)] out wstring aResult);
+
+ /**
+ @param aLangGroup language group
+ @return bool do we have a font for this language group
+ */
+ void HaveFontFor(in string aLangGroup, [retval] out boolean aResult);
+
+ /**
+ * @param aLangGroup language group
+ * @param aGeneric CSS generic font
+ * @return suggested default font for this language group and generic family
+ */
+ wstring getDefaultFont(in string aLangGroup, in string aGeneric);
+
+ /**
+ * update the global font list
+ * return true if font list is changed
+ */
+ boolean updateFontList();
+
+ /**
+ * get the standard family name on the system from given family
+ * @param aName family name which may be alias
+ * @return the standard family name on the system, if given name does not
+ * exist, returns empty string
+ */
+ wstring getStandardFamilyName(in wstring aName);
+};
diff --git a/system/graphics/src/nsIScriptableRegion.idl b/system/graphics/src/nsIScriptableRegion.idl
new file mode 100644
index 000000000..1e32fdd1b
--- /dev/null
+++ b/system/graphics/src/nsIScriptableRegion.idl
@@ -0,0 +1,176 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "nsRegionFwd.h"
+%}
+
+native nsIntRegion(nsIntRegion);
+
+
+[scriptable, uuid(a5f44cc7-2820-489b-b817-ae8a08506ff6)]
+interface nsIScriptableRegion : nsISupports
+{
+ void init ( ) ;
+
+ /**
+ * copy operator equivalent that takes another region
+ *
+ * @param region to copy
+ * @return void
+ *
+ **/
+
+ void setToRegion ( in nsIScriptableRegion aRegion );
+
+ /**
+ * copy operator equivalent that takes a rect
+ *
+ * @param aX xoffset of rect to set region to
+ * @param aY yoffset of rect to set region to
+ * @param aWidth width of rect to set region to
+ * @param aHeight height of rect to set region to
+ * @return void
+ *
+ **/
+
+ void setToRect ( in long aX, in long aY, in long aWidth, in long aHeight );
+
+ /**
+ * destructively intersect another region with this one
+ *
+ * @param region to intersect
+ * @return void
+ *
+ **/
+
+ void intersectRegion ( in nsIScriptableRegion aRegion ) ;
+
+ /**
+ * destructively intersect a rect with this region
+ *
+ * @param aX xoffset of rect to intersect with region
+ * @param aY yoffset of rect to intersect with region
+ * @param aWidth width of rect to intersect with region
+ * @param aHeight height of rect to intersect with region
+ * @return void
+ *
+ **/
+
+ void intersectRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
+
+ /**
+ * destructively union another region with this one
+ *
+ * @param region to union
+ * @return void
+ *
+ **/
+
+ void unionRegion ( in nsIScriptableRegion aRegion ) ;
+
+ /**
+ * destructively union a rect with this region
+ *
+ * @param aX xoffset of rect to union with region
+ * @param aY yoffset of rect to union with region
+ * @param aWidth width of rect to union with region
+ * @param aHeight height of rect to union with region
+ * @return void
+ *
+ **/
+
+ void unionRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
+
+ /**
+ * destructively subtract another region with this one
+ *
+ * @param region to subtract
+ * @return void
+ *
+ **/
+
+ void subtractRegion ( in nsIScriptableRegion aRegion ) ;
+
+ /**
+ * destructively subtract a rect from this region
+ *
+ * @param aX xoffset of rect to subtract with region
+ * @param aY yoffset of rect to subtract with region
+ * @param aWidth width of rect to subtract with region
+ * @param aHeight height of rect to subtract with region
+ * @return void
+ *
+ **/
+
+ void subtractRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
+
+ /**
+ * is this region empty? i.e. does it contain any pixels
+ *
+ * @param none
+ * @return returns whether the region is empty
+ *
+ **/
+
+ boolean isEmpty ( ) ;
+
+ /**
+ * == operator equivalent i.e. do the regions contain exactly
+ * the same pixels
+ *
+ * @param region to compare
+ * @return whether the regions are identical
+ *
+ **/
+
+ boolean isEqualRegion ( in nsIScriptableRegion aRegion ) ;
+
+ /**
+ * returns the bounding box of the region i.e. the smallest
+ * rectangle that completely contains the region.
+ *
+ * @param aX out parameter for xoffset of bounding rect for region
+ * @param aY out parameter for yoffset of bounding rect for region
+ * @param aWidth out parameter for width of bounding rect for region
+ * @param aHeight out parameter for height of bounding rect for region
+ * @return void
+ *
+ **/
+ void getBoundingBox ( out long aX, out long aY, out long aWidth, out long aHeight ) ;
+
+ /**
+ * offsets the region in x and y
+ *
+ * @param xoffset pixel offset in x
+ * @param yoffset pixel offset in y
+ * @return void
+ *
+ **/
+ void offset ( in long aXOffset, in long aYOffset ) ;
+
+ /**
+ * @return null if there are no rects,
+ * @return flat array of rects,ie [x1,y1,width1,height1,x2...].
+ * The result will contain bogus data if values don't fit in 31 bit
+ **/
+ [implicit_jscontext] jsval getRects();
+
+ /**
+ * does the region intersect the rectangle?
+ *
+ * @param rect to check for containment
+ * @return true if the region intersects the rect
+ *
+ **/
+
+ boolean containsRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
+
+ [noscript] readonly attribute nsIntRegion region;
+
+};
diff --git a/system/graphics/src/nsITheme.h b/system/graphics/src/nsITheme.h
new file mode 100644
index 000000000..9456a7394
--- /dev/null
+++ b/system/graphics/src/nsITheme.h
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* service providing platform-specific native rendering for widgets */
+
+#ifndef nsITheme_h_
+#define nsITheme_h_
+
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include "nsColor.h"
+#include "Units.h"
+
+struct nsRect;
+class nsAttrValue;
+class nsPresContext;
+class nsRenderingContext;
+class nsDeviceContext;
+class nsIFrame;
+class nsIAtom;
+class nsIWidget;
+
+// IID for the nsITheme interface
+// {7329f760-08cb-450f-8225-dae729096dec}
+ #define NS_ITHEME_IID \
+{ 0x7329f760, 0x08cb, 0x450f, \
+ { 0x82, 0x25, 0xda, 0xe7, 0x29, 0x09, 0x6d, 0xec } }
+// {0ae05515-cf7a-45a8-9e02-6556de7685b1}
+#define NS_THEMERENDERER_CID \
+{ 0x0ae05515, 0xcf7a, 0x45a8, \
+ { 0x9e, 0x02, 0x65, 0x56, 0xde, 0x76, 0x85, 0xb1 } }
+
+/**
+ * nsITheme is a service that provides platform-specific native
+ * rendering for widgets. In other words, it provides the necessary
+ * operations to draw a rendering object (an nsIFrame) as a native
+ * widget.
+ *
+ * All the methods on nsITheme take a rendering context or device
+ * context, a frame (the rendering object), and a widget type (one of
+ * the constants in nsThemeConstants.h).
+ */
+class nsITheme: public nsISupports {
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITHEME_IID)
+
+ /**
+ * Draw the actual theme background.
+ * @param aContext the context to draw into
+ * @param aFrame the frame for the widget that we're drawing
+ * @param aWidgetType the -moz-appearance value to draw
+ * @param aRect the rectangle defining the area occupied by the widget
+ * @param aDirtyRect the rectangle that needs to be drawn
+ */
+ NS_IMETHOD DrawWidgetBackground(nsRenderingContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ const nsRect& aRect,
+ const nsRect& aDirtyRect) = 0;
+
+ /**
+ * Get the computed CSS border for the widget, in pixels.
+ */
+ NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult)=0;
+
+ /**
+ * This method can return false to indicate that the CSS padding
+ * value should be used. Otherwise, it will fill in aResult with the
+ * computed padding, in pixels, and return true.
+ *
+ * XXXldb This ought to be required to return true for non-containers
+ * so that we don't let specified padding that has no effect change
+ * the computed padding and potentially the size.
+ */
+ virtual bool GetWidgetPadding(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult) = 0;
+
+ /**
+ * On entry, *aResult is positioned at 0,0 and sized to the new size
+ * of aFrame (aFrame->GetSize() may be stale and should not be used).
+ * This method can return false to indicate that no special
+ * overflow area is required by the native widget. Otherwise it will
+ * fill in aResult with the desired overflow area, in appunits, relative
+ * to the frame origin, and return true.
+ *
+ * This overflow area is used to determine what area needs to be
+ * repainted when the widget changes. However, it does not affect the
+ * widget's size or what area is reachable by scrollbars. (In other
+ * words, in layout terms, it affects visual overflow but not
+ * scrollable overflow.)
+ */
+ virtual bool GetWidgetOverflow(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ /*INOUT*/ nsRect* aOverflowRect)
+ { return false; }
+
+ /**
+ * Get the minimum border-box size of a widget, in *pixels* (in
+ * |aResult|). If |aIsOverridable| is set to true, this size is a
+ * minimum size; if false, this size is the only valid size for the
+ * widget.
+ */
+ NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ mozilla::LayoutDeviceIntSize* aResult,
+ bool* aIsOverridable)=0;
+
+
+ enum Transparency {
+ eOpaque = 0,
+ eTransparent,
+ eUnknownTransparency
+ };
+
+ /**
+ * Returns what we know about the transparency of the widget.
+ */
+ virtual Transparency GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
+ { return eUnknownTransparency; }
+
+ NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
+ nsIAtom* aAttribute, bool* aShouldRepaint,
+ const nsAttrValue* aOldValue)=0;
+
+ NS_IMETHOD ThemeChanged()=0;
+
+ virtual bool WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
+ { return false; }
+
+ virtual bool NeedToClearBackgroundBehindWidget(nsIFrame* aFrame,
+ uint8_t aWidgetType)
+ { return false; }
+
+ virtual bool WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame,
+ uint8_t aWidgetType, nscolor* aColor)
+ { return false; }
+
+ /**
+ * ThemeGeometryType values are used for describing themed nsIFrames in
+ * calls to nsIWidget::UpdateThemeGeometries. We don't simply pass the
+ * -moz-appearance value ("widget type") of the frame because the widget may
+ * want to treat different frames with the same -moz-appearance differently
+ * based on other properties of the frame. So we give the theme a first look
+ * at the frame in nsITheme::ThemeGeometryTypeForWidget and pass the
+ * returned ThemeGeometryType along to the widget.
+ * Each theme backend defines the ThemeGeometryType values it needs in its
+ * own nsITheme subclass. eThemeGeometryTypeUnknown is the only value that's
+ * shared between backends.
+ */
+ typedef uint8_t ThemeGeometryType;
+ enum {
+ eThemeGeometryTypeUnknown = 0
+ };
+
+ /**
+ * Returns the theme geometry type that should be used in the ThemeGeometry
+ * array that's passed to the widget using nsIWidget::UpdateThemeGeometries.
+ * A return value of eThemeGeometryTypeUnknown means that this frame will
+ * not be included in the ThemeGeometry array.
+ */
+ virtual ThemeGeometryType ThemeGeometryTypeForWidget(nsIFrame* aFrame,
+ uint8_t aWidgetType)
+ { return eThemeGeometryTypeUnknown; }
+
+ /**
+ * Can the nsITheme implementation handle this widget?
+ */
+ virtual bool ThemeSupportsWidget(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType)=0;
+
+ virtual bool WidgetIsContainer(uint8_t aWidgetType)=0;
+
+ /**
+ * Does the nsITheme implementation draw its own focus ring for this widget?
+ */
+ virtual bool ThemeDrawsFocusForWidget(uint8_t aWidgetType)=0;
+
+ /**
+ * Should we insert a dropmarker inside of combobox button?
+ */
+ virtual bool ThemeNeedsComboboxDropmarker()=0;
+
+ /**
+ * Should we hide scrollbars?
+ */
+ virtual bool ShouldHideScrollbars()
+ { return false; }
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITheme, NS_ITHEME_IID)
+
+// Creator function
+extern nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+#endif
diff --git a/system/graphics/src/nsMargin.h b/system/graphics/src/nsMargin.h
new file mode 100644
index 000000000..d9b0b8bd9
--- /dev/null
+++ b/system/graphics/src/nsMargin.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSMARGIN_H
+#define NSMARGIN_H
+
+#include "nsCoord.h"
+#include "nsPoint.h"
+#include "mozilla/gfx/BaseMargin.h"
+#include "mozilla/gfx/Rect.h"
+
+struct nsMargin : public mozilla::gfx::BaseMargin<nscoord, nsMargin> {
+ typedef mozilla::gfx::BaseMargin<nscoord, nsMargin> Super;
+
+ // Constructors
+ nsMargin() : Super() {}
+ nsMargin(const nsMargin& aMargin) : Super(aMargin) {}
+ nsMargin(nscoord aTop, nscoord aRight, nscoord aBottom, nscoord aLeft)
+ : Super(aTop, aRight, aBottom, aLeft) {}
+};
+
+typedef mozilla::gfx::IntMargin nsIntMargin;
+
+#endif /* NSMARGIN_H */
diff --git a/system/graphics/src/nsPoint.h b/system/graphics/src/nsPoint.h
new file mode 100644
index 000000000..b377eb5a5
--- /dev/null
+++ b/system/graphics/src/nsPoint.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSPOINT_H
+#define NSPOINT_H
+
+#include "nsCoord.h"
+#include "mozilla/gfx/BaseSize.h"
+#include "mozilla/gfx/BasePoint.h"
+#include "nsSize.h"
+#include "mozilla/gfx/Point.h"
+
+// nsIntPoint represents a point in one of the types of pixels.
+// Uses of nsIntPoint should eventually be converted to CSSIntPoint,
+// LayoutDeviceIntPoint, etc. (see layout/base/Units.h).
+typedef mozilla::gfx::IntPoint nsIntPoint;
+
+// nsPoint represents a point in app units.
+
+struct nsPoint : public mozilla::gfx::BasePoint<nscoord, nsPoint> {
+ typedef mozilla::gfx::BasePoint<nscoord, nsPoint> Super;
+
+ nsPoint() : Super() {}
+ nsPoint(const nsPoint& aPoint) : Super(aPoint) {}
+ nsPoint(nscoord aX, nscoord aY) : Super(aX, aY) {}
+
+ inline nsIntPoint ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const;
+ inline nsIntPoint ToNearestPixels(nscoord aAppUnitsPerPixel) const;
+
+ /**
+ * Return this point scaled to a different appunits per pixel (APP) ratio.
+ * @param aFromAPP the APP to scale from
+ * @param aToAPP the APP to scale to
+ */
+ MOZ_MUST_USE inline nsPoint
+ ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
+
+ MOZ_MUST_USE inline nsPoint
+ RemoveResolution(const float resolution) const;
+ MOZ_MUST_USE inline nsPoint
+ ApplyResolution(const float resolution) const;
+};
+
+inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel);
+
+inline nsIntPoint
+nsPoint::ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const
+{
+ return nsIntPoint(
+ NSToIntRoundUp(NSAppUnitsToDoublePixels(x, aAppUnitsPerPixel) * aXScale),
+ NSToIntRoundUp(NSAppUnitsToDoublePixels(y, aAppUnitsPerPixel) * aYScale));
+}
+
+inline nsIntPoint
+nsPoint::ToNearestPixels(nscoord aAppUnitsPerPixel) const
+{
+ return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
+}
+
+inline nsPoint
+nsPoint::ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP != aToAPP) {
+ nsPoint point;
+ point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP));
+ point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP));
+ return point;
+ }
+ return *this;
+}
+
+inline nsPoint
+nsPoint::RemoveResolution(const float resolution) const {
+ if (resolution != 1.0f) {
+ nsPoint point;
+ point.x = NSToCoordRound(NSCoordToFloat(x) / resolution);
+ point.y = NSToCoordRound(NSCoordToFloat(y) / resolution);
+ return point;
+ }
+ return *this;
+}
+
+inline nsPoint
+nsPoint::ApplyResolution(const float resolution) const {
+ if (resolution != 1.0f) {
+ nsPoint point;
+ point.x = NSToCoordRound(NSCoordToFloat(x) * resolution);
+ point.y = NSToCoordRound(NSCoordToFloat(y) * resolution);
+ return point;
+ }
+ return *this;
+}
+
+// app units are integer multiples of pixels, so no rounding needed
+inline nsPoint
+ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel)
+{
+ return nsPoint(NSIntPixelsToAppUnits(aPoint.x, aAppUnitsPerPixel),
+ NSIntPixelsToAppUnits(aPoint.y, aAppUnitsPerPixel));
+}
+
+#endif /* NSPOINT_H */
diff --git a/system/graphics/src/nsRect.cpp b/system/graphics/src/nsRect.cpp
new file mode 100644
index 000000000..c17c249b2
--- /dev/null
+++ b/system/graphics/src/nsRect.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsRect.h"
+#include "mozilla/gfx/Types.h" // for NS_SIDE_BOTTOM, etc
+#include "mozilla/CheckedInt.h" // for CheckedInt
+#include "nsDeviceContext.h" // for nsDeviceContext
+#include "nsString.h" // for nsAutoString, etc
+#include "nsMargin.h" // for nsMargin
+
+static_assert((int(NS_SIDE_TOP) == 0) &&
+ (int(NS_SIDE_RIGHT) == 1) &&
+ (int(NS_SIDE_BOTTOM) == 2) &&
+ (int(NS_SIDE_LEFT) == 3),
+ "The mozilla::css::Side sequence must match the nsMargin nscoord sequence");
+
+const mozilla::gfx::IntRect& GetMaxSizedIntRect() {
+ static const mozilla::gfx::IntRect r(0, 0, INT32_MAX, INT32_MAX);
+ return r;
+}
+
+
+bool nsRect::Overflows() const {
+#ifdef NS_COORD_IS_FLOAT
+ return false;
+#else
+ mozilla::CheckedInt<int32_t> xMost = this->x;
+ xMost += this->width;
+ mozilla::CheckedInt<int32_t> yMost = this->y;
+ yMost += this->height;
+ return !xMost.isValid() || !yMost.isValid();
+#endif
+}
+
+#ifdef DEBUG
+// Diagnostics
+
+FILE* operator<<(FILE* out, const nsRect& rect)
+{
+ nsAutoString tmp;
+
+ // Output the coordinates in fractional pixels so they're easier to read
+ tmp.Append('{');
+ tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.x,
+ nsDeviceContext::AppUnitsPerCSSPixel()));
+ tmp.AppendLiteral(", ");
+ tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.y,
+ nsDeviceContext::AppUnitsPerCSSPixel()));
+ tmp.AppendLiteral(", ");
+ tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.width,
+ nsDeviceContext::AppUnitsPerCSSPixel()));
+ tmp.AppendLiteral(", ");
+ tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.height,
+ nsDeviceContext::AppUnitsPerCSSPixel()));
+ tmp.Append('}');
+ fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
+ return out;
+}
+
+#endif // DEBUG
diff --git a/system/graphics/src/nsRect.h b/system/graphics/src/nsRect.h
new file mode 100644
index 000000000..267f5849c
--- /dev/null
+++ b/system/graphics/src/nsRect.h
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef NSRECT_H
+#define NSRECT_H
+
+#include <stdio.h> // for FILE
+#include <stdint.h> // for int32_t, int64_t
+#include <algorithm> // for min/max
+#include "mozilla/Likely.h" // for MOZ_UNLIKELY
+#include "mozilla/gfx/Rect.h"
+#include "nsCoord.h" // for nscoord, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsPoint.h" // for nsIntPoint, nsPoint
+#include "nsMargin.h" // for nsIntMargin, nsMargin
+#include "nsSize.h" // for IntSize, nsSize
+#include "nscore.h" // for NS_BUILD_REFCNT_LOGGING
+
+typedef mozilla::gfx::IntRect nsIntRect;
+
+struct nsRect :
+ public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> {
+ typedef mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> Super;
+
+ static void VERIFY_COORD(nscoord aValue) { ::VERIFY_COORD(aValue); }
+
+ // Constructors
+ nsRect() : Super()
+ {
+ MOZ_COUNT_CTOR(nsRect);
+ }
+ nsRect(const nsRect& aRect) : Super(aRect)
+ {
+ MOZ_COUNT_CTOR(nsRect);
+ }
+ nsRect(const nsPoint& aOrigin, const nsSize &aSize) : Super(aOrigin, aSize)
+ {
+ MOZ_COUNT_CTOR(nsRect);
+ }
+ nsRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) :
+ Super(aX, aY, aWidth, aHeight)
+ {
+ MOZ_COUNT_CTOR(nsRect);
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ ~nsRect() {
+ MOZ_COUNT_DTOR(nsRect);
+ }
+#endif
+
+ // We have saturating versions of all the Union methods. These avoid
+ // overflowing nscoord values in the 'width' and 'height' fields by
+ // clamping the width and height values to nscoord_MAX if necessary.
+
+ MOZ_MUST_USE nsRect SaturatingUnion(const nsRect& aRect) const
+ {
+ if (IsEmpty()) {
+ return aRect;
+ } else if (aRect.IsEmpty()) {
+ return *static_cast<const nsRect*>(this);
+ } else {
+ return SaturatingUnionEdges(aRect);
+ }
+ }
+
+ MOZ_MUST_USE nsRect SaturatingUnionEdges(const nsRect& aRect) const
+ {
+#ifdef NS_COORD_IS_FLOAT
+ return UnionEdges(aRect);
+#else
+ nsRect result;
+ result.x = std::min(aRect.x, x);
+ int64_t w = std::max(int64_t(aRect.x) + aRect.width, int64_t(x) + width) - result.x;
+ if (MOZ_UNLIKELY(w > nscoord_MAX)) {
+ // Clamp huge negative x to nscoord_MIN / 2 and try again.
+ result.x = std::max(result.x, nscoord_MIN / 2);
+ w = std::max(int64_t(aRect.x) + aRect.width, int64_t(x) + width) - result.x;
+ if (MOZ_UNLIKELY(w > nscoord_MAX)) {
+ w = nscoord_MAX;
+ }
+ }
+ result.width = nscoord(w);
+
+ result.y = std::min(aRect.y, y);
+ int64_t h = std::max(int64_t(aRect.y) + aRect.height, int64_t(y) + height) - result.y;
+ if (MOZ_UNLIKELY(h > nscoord_MAX)) {
+ // Clamp huge negative y to nscoord_MIN / 2 and try again.
+ result.y = std::max(result.y, nscoord_MIN / 2);
+ h = std::max(int64_t(aRect.y) + aRect.height, int64_t(y) + height) - result.y;
+ if (MOZ_UNLIKELY(h > nscoord_MAX)) {
+ h = nscoord_MAX;
+ }
+ }
+ result.height = nscoord(h);
+ return result;
+#endif
+ }
+
+#ifndef NS_COORD_IS_FLOAT
+ // Make all nsRect Union methods be saturating.
+ MOZ_MUST_USE nsRect UnionEdges(const nsRect& aRect) const
+ {
+ return SaturatingUnionEdges(aRect);
+ }
+ void UnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ *this = aRect1.UnionEdges(aRect2);
+ }
+ MOZ_MUST_USE nsRect Union(const nsRect& aRect) const
+ {
+ return SaturatingUnion(aRect);
+ }
+ void UnionRect(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ *this = aRect1.Union(aRect2);
+ }
+#endif
+
+ void SaturatingUnionRect(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ *this = aRect1.SaturatingUnion(aRect2);
+ }
+ void SaturatingUnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ *this = aRect1.SaturatingUnionEdges(aRect2);
+ }
+
+ // Return whether this rect's right or bottom edge overflow int32.
+ bool Overflows() const;
+
+ /**
+ * Return this rect scaled to a different appunits per pixel (APP) ratio.
+ * In the RoundOut version we make the rect the smallest rect containing the
+ * unrounded result. In the RoundIn version we make the rect the largest rect
+ * contained in the unrounded result.
+ * @param aFromAPP the APP to scale from
+ * @param aToAPP the APP to scale to
+ * @note this can turn an empty rectangle into a non-empty rectangle
+ */
+ MOZ_MUST_USE inline nsRect
+ ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const;
+ MOZ_MUST_USE inline nsRect
+ ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const;
+
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const;
+
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ToNearestPixels(nscoord aAppUnitsPerPixel) const;
+
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ScaleToOutsidePixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const;
+
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ToOutsidePixels(nscoord aAppUnitsPerPixel) const;
+
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ScaleToInsidePixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const;
+
+ MOZ_MUST_USE inline mozilla::gfx::IntRect
+ ToInsidePixels(nscoord aAppUnitsPerPixel) const;
+
+ // This is here only to keep IPDL-generated code happy. DO NOT USE.
+ bool operator==(const nsRect& aRect) const
+ {
+ return IsEqualEdges(aRect);
+ }
+
+ MOZ_MUST_USE inline nsRect RemoveResolution(const float aResolution) const;
+};
+
+/*
+ * App Unit/Pixel conversions
+ */
+
+inline nsRect
+nsRect::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRect rect;
+ nscoord right = NSToCoordCeil(NSCoordScale(XMost(), aFromAPP, aToAPP));
+ nscoord bottom = NSToCoordCeil(NSCoordScale(YMost(), aFromAPP, aToAPP));
+ rect.x = NSToCoordFloor(NSCoordScale(x, aFromAPP, aToAPP));
+ rect.y = NSToCoordFloor(NSCoordScale(y, aFromAPP, aToAPP));
+ rect.width = (right - rect.x);
+ rect.height = (bottom - rect.y);
+
+ return rect;
+}
+
+inline nsRect
+nsRect::ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRect rect;
+ nscoord right = NSToCoordFloor(NSCoordScale(XMost(), aFromAPP, aToAPP));
+ nscoord bottom = NSToCoordFloor(NSCoordScale(YMost(), aFromAPP, aToAPP));
+ rect.x = NSToCoordCeil(NSCoordScale(x, aFromAPP, aToAPP));
+ rect.y = NSToCoordCeil(NSCoordScale(y, aFromAPP, aToAPP));
+ rect.width = (right - rect.x);
+ rect.height = (bottom - rect.y);
+
+ return rect;
+}
+
+// scale the rect but round to preserve centers
+inline mozilla::gfx::IntRect
+nsRect::ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const
+{
+ mozilla::gfx::IntRect rect;
+ rect.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(x, aAppUnitsPerPixel) * aXScale);
+ rect.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(y, aAppUnitsPerPixel) * aYScale);
+ // Avoid negative widths and heights due to overflow
+ rect.width = std::max(0, NSToIntRoundUp(NSAppUnitsToDoublePixels(XMost(),
+ aAppUnitsPerPixel) * aXScale) - rect.x);
+ rect.height = std::max(0, NSToIntRoundUp(NSAppUnitsToDoublePixels(YMost(),
+ aAppUnitsPerPixel) * aYScale) - rect.y);
+ return rect;
+}
+
+// scale the rect but round to smallest containing rect
+inline mozilla::gfx::IntRect
+nsRect::ScaleToOutsidePixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const
+{
+ mozilla::gfx::IntRect rect;
+ rect.x = NSToIntFloor(NSAppUnitsToFloatPixels(x, float(aAppUnitsPerPixel)) * aXScale);
+ rect.y = NSToIntFloor(NSAppUnitsToFloatPixels(y, float(aAppUnitsPerPixel)) * aYScale);
+ // Avoid negative widths and heights due to overflow
+ rect.width = std::max(0, NSToIntCeil(NSAppUnitsToFloatPixels(XMost(),
+ float(aAppUnitsPerPixel)) * aXScale) - rect.x);
+ rect.height = std::max(0, NSToIntCeil(NSAppUnitsToFloatPixels(YMost(),
+ float(aAppUnitsPerPixel)) * aYScale) - rect.y);
+ return rect;
+}
+
+// scale the rect but round to largest contained rect
+inline mozilla::gfx::IntRect
+nsRect::ScaleToInsidePixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const
+{
+ mozilla::gfx::IntRect rect;
+ rect.x = NSToIntCeil(NSAppUnitsToFloatPixels(x, float(aAppUnitsPerPixel)) * aXScale);
+ rect.y = NSToIntCeil(NSAppUnitsToFloatPixels(y, float(aAppUnitsPerPixel)) * aYScale);
+ // Avoid negative widths and heights due to overflow
+ rect.width = std::max(0, NSToIntFloor(NSAppUnitsToFloatPixels(XMost(),
+ float(aAppUnitsPerPixel)) * aXScale) - rect.x);
+ rect.height = std::max(0, NSToIntFloor(NSAppUnitsToFloatPixels(YMost(),
+ float(aAppUnitsPerPixel)) * aYScale) - rect.y);
+ return rect;
+}
+
+inline mozilla::gfx::IntRect
+nsRect::ToNearestPixels(nscoord aAppUnitsPerPixel) const
+{
+ return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
+}
+
+inline mozilla::gfx::IntRect
+nsRect::ToOutsidePixels(nscoord aAppUnitsPerPixel) const
+{
+ return ScaleToOutsidePixels(1.0f, 1.0f, aAppUnitsPerPixel);
+}
+
+inline mozilla::gfx::IntRect
+nsRect::ToInsidePixels(nscoord aAppUnitsPerPixel) const
+{
+ return ScaleToInsidePixels(1.0f, 1.0f, aAppUnitsPerPixel);
+}
+
+inline nsRect
+nsRect::RemoveResolution(const float aResolution) const
+{
+ MOZ_ASSERT(aResolution > 0.0f);
+ nsRect rect;
+ rect.x = NSToCoordRound(NSCoordToFloat(x) / aResolution);
+ rect.y = NSToCoordRound(NSCoordToFloat(y) / aResolution);
+ // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
+ // rect as well instead of possibly rounding the width or height to zero.
+ if (width == 1 && height == 1) {
+ rect.width = rect.height = 1;
+ } else {
+ rect.width = NSToCoordCeil(NSCoordToFloat(width) / aResolution);
+ rect.height = NSToCoordCeil(NSCoordToFloat(height) / aResolution);
+ }
+
+ return rect;
+}
+
+const mozilla::gfx::IntRect& GetMaxSizedIntRect();
+
+// app units are integer multiples of pixels, so no rounding needed
+template<class units>
+nsRect
+ToAppUnits(const mozilla::gfx::IntRectTyped<units>& aRect, nscoord aAppUnitsPerPixel)
+{
+ return nsRect(NSIntPixelsToAppUnits(aRect.x, aAppUnitsPerPixel),
+ NSIntPixelsToAppUnits(aRect.y, aAppUnitsPerPixel),
+ NSIntPixelsToAppUnits(aRect.width, aAppUnitsPerPixel),
+ NSIntPixelsToAppUnits(aRect.height, aAppUnitsPerPixel));
+}
+
+#ifdef DEBUG
+// Diagnostics
+extern FILE* operator<<(FILE* out, const nsRect& rect);
+#endif // DEBUG
+
+#endif /* NSRECT_H */
diff --git a/system/graphics/src/nsRegion.cpp b/system/graphics/src/nsRegion.cpp
new file mode 100644
index 000000000..ed86704e8
--- /dev/null
+++ b/system/graphics/src/nsRegion.cpp
@@ -0,0 +1,1147 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsRegion.h"
+#include "nsTArray.h"
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/ToString.h"
+
+bool nsRegion::Contains(const nsRegion& aRgn) const
+{
+ // XXX this could be made faster by iterating over
+ // both regions at the same time some how
+ for (auto iter = aRgn.RectIter(); !iter.Done(); iter.Next()) {
+ if (!Contains(iter.Get())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool nsRegion::Intersects(const nsRect& aRect) const
+{
+ // XXX this could be made faster by using pixman_region32_contains_rect
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ if (iter.Get().Intersects(aRect)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void nsRegion::Inflate(const nsMargin& aMargin)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.Inflate(aMargin);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+}
+
+void nsRegion::SimplifyOutward (uint32_t aMaxRects)
+{
+ MOZ_ASSERT(aMaxRects >= 1, "Invalid max rect count");
+
+ if (GetNumRects() <= aMaxRects)
+ return;
+
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // Try combining rects in horizontal bands into a single rect
+ int dest = 0;
+ for (int src = 1; src < n; src++)
+ {
+ // The goal here is to try to keep groups of rectangles that are vertically
+ // discontiguous as separate rectangles in the final region. This is
+ // simple and fast to implement and page contents tend to vary more
+ // vertically than horizontally (which is why our rectangles are stored
+ // sorted by y-coordinate, too).
+ //
+ // Note: if boxes share y1 because of the canonical representation they
+ // will share y2
+ while ((src < (n)) && boxes[dest].y1 == boxes[src].y1) {
+ // merge box[i] and box[i+1]
+ boxes[dest].x2 = boxes[src].x2;
+ src++;
+ }
+ if (src < n) {
+ dest++;
+ boxes[dest] = boxes[src];
+ }
+ }
+
+ uint32_t reducedCount = dest+1;
+ // pixman has a special representation for
+ // regions of 1 rectangle. So just use the
+ // bounds in that case
+ if (reducedCount > 1 && reducedCount <= aMaxRects) {
+ // reach into pixman and lower the number
+ // of rects stored in data.
+ mImpl.data->numRects = reducedCount;
+ } else {
+ *this = GetBounds();
+ }
+}
+
+// compute the covered area difference between two rows.
+// by iterating over both rows simultaneously and adding up
+// the additional increase in area caused by extending each
+// of the rectangles to the combined height of both rows
+static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects,
+ pixman_box32_t *topRectsEnd,
+ pixman_box32_t *bottomRects,
+ pixman_box32_t *bottomRectsEnd)
+{
+ uint32_t totalArea = 0;
+ struct pt {
+ int32_t x, y;
+ };
+
+
+ pt *i = (pt*)topRects;
+ pt *end_i = (pt*)topRectsEnd;
+ pt *j = (pt*)bottomRects;
+ pt *end_j = (pt*)bottomRectsEnd;
+ bool top = false;
+ bool bottom = false;
+
+ int cur_x = i->x;
+ bool top_next = top;
+ bool bottom_next = bottom;
+ //XXX: we could probably simplify this condition and perhaps move it into the loop below
+ if (j->x < cur_x) {
+ cur_x = j->x;
+ j++;
+ bottom_next = !bottom;
+ } else if (j->x == cur_x) {
+ i++;
+ top_next = !top;
+ bottom_next = !bottom;
+ j++;
+ } else {
+ top_next = !top;
+ i++;
+ }
+
+ int topRectsHeight = topRects->y2 - topRects->y1;
+ int bottomRectsHeight = bottomRects->y2 - bottomRects->y1;
+ int inbetweenHeight = bottomRects->y1 - topRects->y2;
+ int width = cur_x;
+ // top and bottom are the in-status to the left of cur_x
+ do {
+ if (top && !bottom) {
+ totalArea += (inbetweenHeight+bottomRectsHeight)*width;
+ } else if (bottom && !top) {
+ totalArea += (inbetweenHeight+topRectsHeight)*width;
+ } else if (bottom && top) {
+ totalArea += (inbetweenHeight)*width;
+ }
+ top = top_next;
+ bottom = bottom_next;
+ // find the next edge
+ if (i->x < j->x) {
+ top_next = !top;
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ } else if (j->x < i->x) {
+ bottom_next = !bottom;
+ width = j->x - cur_x;
+ cur_x = j->x;
+ j++;
+ } else { // i->x == j->x
+ top_next = !top;
+ bottom_next = !bottom;
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ j++;
+ }
+ } while (i < end_i && j < end_j);
+
+ // handle any remaining rects
+ while (i < end_i) {
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ if (top)
+ totalArea += (inbetweenHeight+bottomRectsHeight)*width;
+ top = !top;
+ }
+
+ while (j < end_j) {
+ width = j->x - cur_x;
+ cur_x = j->x;
+ j++;
+ if (bottom)
+ totalArea += (inbetweenHeight+topRectsHeight)*width;
+ bottom = !bottom;
+ }
+ return totalArea;
+}
+
+static pixman_box32_t *
+CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end)
+{
+ // XXX: std::copy
+ pixman_box32_t *src_it = src_start;
+ while (src_it < src_end) {
+ *dest_it++ = *src_it++;
+ }
+ return dest_it;
+}
+
+
+#define WRITE_RECT(x1, x2, y1, y2) \
+ do { \
+ tmpRect->x1 = x1; \
+ tmpRect->x2 = x2; \
+ tmpRect->y1 = y1; \
+ tmpRect->y2 = y2; \
+ tmpRect++; \
+ } while (0)
+
+/* If 'r' overlaps the current rect, then expand the current rect to include
+ * it. Otherwise write the current rect out to tmpRect, and set r as the
+ * updated current rect. */
+#define MERGE_RECT(r) \
+ do { \
+ if (r->x1 <= x2) { \
+ if (x2 < r->x2) \
+ x2 = r->x2; \
+ } else { \
+ WRITE_RECT(x1, x2, y1, y2); \
+ x1 = r->x1; \
+ x2 = r->x2; \
+ } \
+ r++; \
+ } while (0)
+
+
+/* Can we merge two sets of rects without extra space?
+ * Yes, but not easily. We can even do it stably
+ * but we don't need that property.
+ *
+ * This is written in the style of pixman_region_union_o */
+static pixman_box32_t *
+MergeRects(pixman_box32_t *r1,
+ pixman_box32_t *r1_end,
+ pixman_box32_t *r2,
+ pixman_box32_t *r2_end,
+ pixman_box32_t *tmpRect)
+{
+ /* This routine works by maintaining the current
+ * rectangle in x1,x2,y1,y2 and either merging
+ * in the left most rectangle if it overlaps or
+ * outputing the current rectangle and setting
+ * it to the the left most one */
+ const int y1 = r1->y1;
+ const int y2 = r2->y2;
+ int x1;
+ int x2;
+
+ /* Find the left-most edge */
+ if (r1->x1 < r2->x1) {
+ x1 = r1->x1;
+ x2 = r1->x2;
+ r1++;
+ } else {
+ x1 = r2->x1;
+ x2 = r2->x2;
+ r2++;
+ }
+
+ while (r1 != r1_end && r2 != r2_end) {
+ /* Find and merge the left-most rectangle */
+ if (r1->x1 < r2->x1)
+ MERGE_RECT (r1);
+ else
+ MERGE_RECT (r2);
+ }
+
+ /* Finish up any left overs */
+ if (r1 != r1_end) {
+ do {
+ MERGE_RECT (r1);
+ } while (r1 != r1_end);
+ } else if (r2 != r2_end) {
+ do {
+ MERGE_RECT(r2);
+ } while (r2 != r2_end);
+ }
+
+ /* Finish up the last rectangle */
+ WRITE_RECT(x1, x2, y1, y2);
+
+ return tmpRect;
+}
+
+void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
+{
+
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // if we have no rectangles then we're done
+ if (!n)
+ return;
+
+ pixman_box32_t *end = boxes + n;
+ pixman_box32_t *topRectsEnd = boxes+1;
+ pixman_box32_t *topRects = boxes;
+
+ // we need some temporary storage for merging both rows of rectangles
+ AutoTArray<pixman_box32_t, 10> tmpStorage;
+ tmpStorage.SetCapacity(n);
+ pixman_box32_t *tmpRect = tmpStorage.Elements();
+
+ pixman_box32_t *destRect = boxes;
+ pixman_box32_t *rect = tmpRect;
+ // find the end of the first span of rectangles
+ while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
+ topRectsEnd++;
+ }
+
+ // if we only have one row we are done
+ if (topRectsEnd == end)
+ return;
+
+ pixman_box32_t *bottomRects = topRectsEnd;
+ pixman_box32_t *bottomRectsEnd = bottomRects+1;
+ do {
+ // find the end of the bottom span of rectangles
+ while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
+ bottomRectsEnd++;
+ }
+ uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd,
+ bottomRects, bottomRectsEnd);
+
+ if (totalArea <= aThreshold) {
+ // merge the rects into tmpRect
+ rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect);
+
+ // set topRects to where the newly merged rects will be so that we use them
+ // as our next set of topRects
+ topRects = destRect;
+ // copy the merged rects back into the destination
+ topRectsEnd = CopyRow(destRect, tmpRect, rect);
+ } else {
+ // copy the unmerged rects
+ destRect = CopyRow(destRect, topRects, topRectsEnd);
+
+ topRects = bottomRects;
+ topRectsEnd = bottomRectsEnd;
+ if (bottomRectsEnd == end) {
+ // copy the last row when we are done
+ topRectsEnd = CopyRow(destRect, topRects, topRectsEnd);
+ }
+ }
+ bottomRects = bottomRectsEnd;
+ } while (bottomRectsEnd != end);
+
+
+ uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n);
+ // pixman has a special representation for
+ // regions of 1 rectangle. So just use the
+ // bounds in that case
+ if (reducedCount > 1) {
+ // reach into pixman and lower the number
+ // of rects stored in data.
+ this->mImpl.data->numRects = reducedCount;
+ } else {
+ *this = GetBounds();
+ }
+}
+
+
+typedef void (*visit_fn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
+
+static bool VisitNextEdgeBetweenRect(visit_fn visit, void *closure, VisitSide side,
+ pixman_box32_t *&r1, pixman_box32_t *&r2, const int y, int &x1)
+{
+ // check for overlap
+ if (r1->x2 >= r2->x1) {
+ MOZ_ASSERT(r2->x1 >= x1);
+ visit(closure, side, x1, y, r2->x1, y);
+
+ // find the rect that ends first or always drop the one that comes first?
+ if (r1->x2 < r2->x2) {
+ x1 = r1->x2;
+ r1++;
+ } else {
+ x1 = r2->x2;
+ r2++;
+ }
+ return true;
+ } else {
+ MOZ_ASSERT(r1->x2 < r2->x2);
+ // we handle the corners by just extending the top and bottom edges
+ visit(closure, side, x1, y, r1->x2+1, y);
+ r1++;
+ // we assign x1 because we can assume that x1 <= r2->x1 - 1
+ // However the caller may know better and if so, may update
+ // x1 to r1->x1
+ x1 = r2->x1 - 1;
+ return false;
+ }
+}
+
+//XXX: if we need to this can compute the end of the row
+static void
+VisitSides(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ // XXX: we can drop LEFT/RIGHT and just use the orientation
+ // of the line if it makes sense
+ while (r != r_end) {
+ visit(closure, VisitSide::LEFT, r->x1, r->y1, r->x1, r->y2);
+ visit(closure, VisitSide::RIGHT, r->x2, r->y1, r->x2, r->y2);
+ r++;
+ }
+}
+
+static void
+VisitAbove(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ while (r != r_end) {
+ visit(closure, VisitSide::TOP, r->x1-1, r->y1, r->x2+1, r->y1);
+ r++;
+ }
+}
+
+static void
+VisitBelow(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ while (r != r_end) {
+ visit(closure, VisitSide::BOTTOM, r->x1-1, r->y2, r->x2+1, r->y2);
+ r++;
+ }
+}
+
+static pixman_box32_t *
+VisitInbetween(visit_fn visit, void *closure, pixman_box32_t *r1,
+ pixman_box32_t *r1_end,
+ pixman_box32_t *r2,
+ pixman_box32_t *r2_end)
+{
+ const int y = r1->y2;
+ int x1;
+
+ bool overlap = false;
+ while (r1 != r1_end && r2 != r2_end) {
+ if (!overlap) {
+ /* Find the left-most edge */
+ if (r1->x1 < r2->x1) {
+ x1 = r1->x1 - 1;
+ } else {
+ x1 = r2->x1 - 1;
+ }
+ }
+
+ MOZ_ASSERT((x1 >= (r1->x1 - 1)) || (x1 >= (r2->x1 - 1)));
+ if (r1->x1 < r2->x1) {
+ overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::BOTTOM, r1, r2, y, x1);
+ } else {
+ overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::TOP, r2, r1, y, x1);
+ }
+ }
+
+ /* Finish up which ever row has remaining rects*/
+ if (r1 != r1_end) {
+ // top row
+ do {
+ visit(closure, VisitSide::BOTTOM, x1, y, r1->x2 + 1, y);
+ r1++;
+ if (r1 == r1_end)
+ break;
+ x1 = r1->x1 - 1;
+ } while (true);
+ } else if (r2 != r2_end) {
+ // bottom row
+ do {
+ visit(closure, VisitSide::TOP, x1, y, r2->x2 + 1, y);
+ r2++;
+ if (r2 == r2_end)
+ break;
+ x1 = r2->x1 - 1;
+ } while (true);
+ }
+
+ return 0;
+}
+
+void nsRegion::VisitEdges (visit_fn visit, void *closure)
+{
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // if we have no rectangles then we're done
+ if (!n)
+ return;
+
+ pixman_box32_t *end = boxes + n;
+ pixman_box32_t *topRectsEnd = boxes + 1;
+ pixman_box32_t *topRects = boxes;
+
+ // find the end of the first span of rectangles
+ while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
+ topRectsEnd++;
+ }
+
+ // In order to properly handle convex corners we always visit the sides first
+ // that way when we visit the corners we can pad using the value from the sides
+ VisitSides(visit, closure, topRects, topRectsEnd);
+
+ VisitAbove(visit, closure, topRects, topRectsEnd);
+
+ pixman_box32_t *bottomRects = topRects;
+ pixman_box32_t *bottomRectsEnd = topRectsEnd;
+ if (topRectsEnd != end) {
+ do {
+ // find the next row of rects
+ bottomRects = topRectsEnd;
+ bottomRectsEnd = topRectsEnd + 1;
+ while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
+ bottomRectsEnd++;
+ }
+
+ VisitSides(visit, closure, bottomRects, bottomRectsEnd);
+
+ if (topRects->y2 == bottomRects->y1) {
+ VisitInbetween(visit, closure, topRects, topRectsEnd,
+ bottomRects, bottomRectsEnd);
+ } else {
+ VisitBelow(visit, closure, topRects, topRectsEnd);
+ VisitAbove(visit, closure, bottomRects, bottomRectsEnd);
+ }
+
+ topRects = bottomRects;
+ topRectsEnd = bottomRectsEnd;
+ } while (bottomRectsEnd != end);
+ }
+
+ // the bottom of the region doesn't touch anything else so we
+ // can always visit it at the end
+ VisitBelow(visit, closure, bottomRects, bottomRectsEnd);
+}
+
+
+void nsRegion::SimplifyInward (uint32_t aMaxRects)
+{
+ NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
+
+ if (GetNumRects() <= aMaxRects)
+ return;
+
+ SetEmpty();
+}
+
+uint64_t nsRegion::Area () const
+{
+ uint64_t area = 0;
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ area += uint64_t(rect.width) * rect.height;
+ }
+ return area;
+}
+
+nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale)
+{
+ if (mozilla::gfx::FuzzyEqual(aXScale, 1.0f) &&
+ mozilla::gfx::FuzzyEqual(aYScale, 1.0f)) {
+ return *this;
+ }
+
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.ScaleRoundOut(aXScale, aYScale);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+nsRegion& nsRegion::ScaleInverseRoundOut (float aXScale, float aYScale)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.ScaleInverseRoundOut(aXScale, aYScale);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+static mozilla::gfx::IntRect
+TransformRect(const mozilla::gfx::IntRect& aRect, const mozilla::gfx::Matrix4x4& aTransform)
+{
+ if (aRect.IsEmpty()) {
+ return mozilla::gfx::IntRect();
+ }
+
+ mozilla::gfx::RectDouble rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ rect = aTransform.TransformAndClipBounds(rect, mozilla::gfx::RectDouble::MaxIntRect());
+ rect.RoundOut();
+
+ mozilla::gfx::IntRect intRect;
+ if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
+ return mozilla::gfx::IntRect();
+ }
+
+ return intRect;
+}
+
+nsRegion& nsRegion::Transform (const mozilla::gfx::Matrix4x4 &aTransform)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ boxes[i] = RectToBox(nsIntRegion::ToRect(TransformRect(nsIntRegion::FromRect(rect), aTransform)));
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+
+nsRegion nsRegion::ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect = rect.ScaleToOtherAppUnitsRoundOut(aFromAPP, aToAPP);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t pixmanRegion;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&pixmanRegion, boxes, n);
+
+ pixman_region32_fini(&region.mImpl);
+ region.mImpl = pixmanRegion;
+ return region;
+}
+
+nsRegion nsRegion::ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect = rect.ScaleToOtherAppUnitsRoundIn(aFromAPP, aToAPP);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t pixmanRegion;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&pixmanRegion, boxes, n);
+
+ pixman_region32_fini(&region.mImpl);
+ region.mImpl = pixmanRegion;
+ return region;
+}
+
+nsIntRegion nsRegion::ToPixels (nscoord aAppUnitsPerPixel, bool aOutsidePixels) const
+{
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ mozilla::gfx::IntRect deviceRect;
+ if (aOutsidePixels)
+ deviceRect = rect.ToOutsidePixels(aAppUnitsPerPixel);
+ else
+ deviceRect = rect.ToNearestPixels(aAppUnitsPerPixel);
+
+ boxes[i] = RectToBox(deviceRect);
+ }
+
+ nsIntRegion intRegion;
+ pixman_region32_fini(&intRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
+
+ return intRegion;
+}
+
+nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
+{
+ return ToPixels(aAppUnitsPerPixel, true);
+}
+
+nsIntRegion nsRegion::ToNearestPixels (nscoord aAppUnitsPerPixel) const
+{
+ return ToPixels(aAppUnitsPerPixel, false);
+}
+
+nsIntRegion nsRegion::ScaleToNearestPixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ nsIntRegion result;
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ mozilla::gfx::IntRect deviceRect =
+ iter.Get().ScaleToNearestPixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+ result.Or(result, deviceRect);
+ }
+ return result;
+}
+
+nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ // make a copy of the region so that we can mutate it inplace
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ mozilla::gfx::IntRect irect = rect.ScaleToOutsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+ boxes[i] = RectToBox(irect);
+ }
+
+ nsIntRegion iRegion;
+ // clear out the initial pixman_region so that we can replace it below
+ pixman_region32_fini(&iRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&iRegion.mImpl.mImpl, boxes, n);
+
+ return iRegion;
+}
+
+nsIntRegion nsRegion::ScaleToInsidePixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ /* When scaling a rect, walk forward through the rect list up until the y value is greater
+ * than the current rect's YMost() value.
+ *
+ * For each rect found, check if the rects have a touching edge (in unscaled coordinates),
+ * and if one edge is entirely contained within the other.
+ *
+ * If it is, then the contained edge can be moved (in scaled pixels) to ensure that no
+ * gap exists.
+ *
+ * Since this could be potentially expensive - O(n^2), we only attempt this algorithm
+ * for the first rect.
+ */
+
+ // make a copy of this region so that we can mutate it in place
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+
+ nsIntRegion intRegion;
+ if (n) {
+ nsRect first = BoxToRect(boxes[0]);
+ mozilla::gfx::IntRect firstDeviceRect =
+ first.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+
+ for (int i=1; i<n; i++) {
+ nsRect rect = nsRect(boxes[i].x1, boxes[i].y1,
+ boxes[i].x2 - boxes[i].x1,
+ boxes[i].y2 - boxes[i].y1);
+ mozilla::gfx::IntRect deviceRect =
+ rect.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+
+ if (rect.y <= first.YMost()) {
+ if (rect.XMost() == first.x && rect.YMost() <= first.YMost()) {
+ // rect is touching on the left edge of the first rect and contained within
+ // the length of its left edge
+ deviceRect.SetRightEdge(firstDeviceRect.x);
+ } else if (rect.x == first.XMost() && rect.YMost() <= first.YMost()) {
+ // rect is touching on the right edge of the first rect and contained within
+ // the length of its right edge
+ deviceRect.SetLeftEdge(firstDeviceRect.XMost());
+ } else if (rect.y == first.YMost()) {
+ // The bottom of the first rect is on the same line as the top of rect, but
+ // they aren't necessarily contained.
+ if (rect.x <= first.x && rect.XMost() >= first.XMost()) {
+ // The top of rect contains the bottom of the first rect
+ firstDeviceRect.SetBottomEdge(deviceRect.y);
+ } else if (rect.x >= first.x && rect.XMost() <= first.XMost()) {
+ // The bottom of the first contains the top of rect
+ deviceRect.SetTopEdge(firstDeviceRect.YMost());
+ }
+ }
+ }
+
+ boxes[i] = RectToBox(deviceRect);
+ }
+
+ boxes[0] = RectToBox(firstDeviceRect);
+
+ pixman_region32_fini(&intRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
+ }
+ return intRegion;
+
+}
+
+// A cell's "value" is a pair consisting of
+// a) the area of the subrectangle it corresponds to, if it's in
+// aContainingRect and in the region, 0 otherwise
+// b) the area of the subrectangle it corresponds to, if it's in the region,
+// 0 otherwise
+// Addition, subtraction and identity are defined on these values in the
+// obvious way. Partial order is lexicographic.
+// A "large negative value" is defined with large negative numbers for both
+// fields of the pair. This negative value has the property that adding any
+// number of non-negative values to it always results in a negative value.
+//
+// The GetLargestRectangle algorithm works in three phases:
+// 1) Convert the region into a grid by adding vertical/horizontal lines for
+// each edge of each rectangle in the region.
+// 2) For each rectangle in the region, for each cell it contains, set that
+// cells's value as described above.
+// 3) Calculate the submatrix with the largest sum such that none of its cells
+// contain any 0s (empty regions). The rectangle represented by the
+// submatrix is the largest rectangle in the region.
+//
+// Let k be the number of rectangles in the region.
+// Let m be the height of the grid generated in step 1.
+// Let n be the width of the grid generated in step 1.
+//
+// Step 1 is O(k) in time and O(m+n) in space for the sparse grid.
+// Step 2 is O(mn) in time and O(mn) in additional space for the full grid.
+// Step 3 is O(m^2 n) in time and O(mn) in additional space
+//
+// The implementation of steps 1 and 2 are rather straightforward. However our
+// implementation of step 3 uses dynamic programming to achieve its efficiency.
+//
+// Psuedo code for step 3 is as follows where G is the grid from step 1 and A
+// is the array from step 2:
+// Phase3 = function (G, A, m, n) {
+// let (t,b,l,r,_) = MaxSum2D(A,m,n)
+// return rect(G[t],G[l],G[r],G[b]);
+// }
+// MaxSum2D = function (A, m, n) {
+// S = array(m+1,n+1)
+// S[0][i] = 0 for i in [0,n]
+// S[j][0] = 0 for j in [0,m]
+// S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
+// + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
+//
+// // top, bottom, left, right, area
+// var maxRect = (-1, -1, -1, -1, 0);
+//
+// for all (m',m'') in [0, m]^2 {
+// let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
+// let ((l,r),area) = MaxSum1D(B,n+1)
+// if (area > maxRect.area) {
+// maxRect := (m', m'', l, r, area)
+// }
+// }
+//
+// return maxRect;
+// }
+//
+// Originally taken from Improved algorithms for the k-maximum subarray problem
+// for small k - SE Bae, T Takaoka but modified to show the explicit tracking
+// of indices and we already have the prefix sums from our one call site so
+// there's no need to construct them.
+// MaxSum1D = function (A,n) {
+// var minIdx = 0;
+// var min = 0;
+// var maxIndices = (0,0);
+// var max = 0;
+// for i in range(n) {
+// let cand = A[i] - min;
+// if (cand > max) {
+// max := cand;
+// maxIndices := (minIdx, i)
+// }
+// if (min > A[i]) {
+// min := A[i];
+// minIdx := i;
+// }
+// }
+// return (minIdx, maxIdx, max);
+// }
+
+namespace {
+ // This class represents a partitioning of an axis delineated by coordinates.
+ // It internally maintains a sorted array of coordinates.
+ class AxisPartition {
+ public:
+ // Adds a new partition at the given coordinate to this partitioning. If
+ // the coordinate is already present in the partitioning, this does nothing.
+ void InsertCoord(nscoord c) {
+ uint32_t i = mStops.IndexOfFirstElementGt(c);
+ if (i == 0 || mStops[i-1] != c) {
+ mStops.InsertElementAt(i, c);
+ }
+ }
+
+ // Returns the array index of the given partition point. The partition
+ // point must already be present in the partitioning.
+ int32_t IndexOf(nscoord p) const {
+ return mStops.BinaryIndexOf(p);
+ }
+
+ // Returns the partition at the given index which must be non-zero and
+ // less than the number of partitions in this partitioning.
+ nscoord StopAt(int32_t index) const {
+ return mStops[index];
+ }
+
+ // Returns the size of the gap between the partition at the given index and
+ // the next partition in this partitioning. If the index is the last index
+ // in the partitioning, the result is undefined.
+ nscoord StopSize(int32_t index) const {
+ return mStops[index+1] - mStops[index];
+ }
+
+ // Returns the number of partitions in this partitioning.
+ int32_t GetNumStops() const { return mStops.Length(); }
+
+ private:
+ nsTArray<nscoord> mStops;
+ };
+
+ const int64_t kVeryLargeNegativeNumber = 0xffff000000000000ll;
+
+ struct SizePair {
+ int64_t mSizeContainingRect;
+ int64_t mSize;
+
+ SizePair() : mSizeContainingRect(0), mSize(0) {}
+
+ static SizePair VeryLargeNegative() {
+ SizePair result;
+ result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
+ return result;
+ }
+ bool operator<(const SizePair& aOther) const {
+ if (mSizeContainingRect < aOther.mSizeContainingRect)
+ return true;
+ if (mSizeContainingRect > aOther.mSizeContainingRect)
+ return false;
+ return mSize < aOther.mSize;
+ }
+ bool operator>(const SizePair& aOther) const {
+ return aOther.operator<(*this);
+ }
+ SizePair operator+(const SizePair& aOther) const {
+ SizePair result = *this;
+ result.mSizeContainingRect += aOther.mSizeContainingRect;
+ result.mSize += aOther.mSize;
+ return result;
+ }
+ SizePair operator-(const SizePair& aOther) const {
+ SizePair result = *this;
+ result.mSizeContainingRect -= aOther.mSizeContainingRect;
+ result.mSize -= aOther.mSize;
+ return result;
+ }
+ };
+
+ // Returns the sum and indices of the subarray with the maximum sum of the
+ // given array (A,n), assuming the array is already in prefix sum form.
+ SizePair MaxSum1D(const nsTArray<SizePair> &A, int32_t n,
+ int32_t *minIdx, int32_t *maxIdx) {
+ // The min/max indicies of the largest subarray found so far
+ SizePair min, max;
+ int32_t currentMinIdx = 0;
+
+ *minIdx = 0;
+ *maxIdx = 0;
+
+ // Because we're given the array in prefix sum form, we know the first
+ // element is 0
+ for(int32_t i = 1; i < n; i++) {
+ SizePair cand = A[i] - min;
+ if (cand > max) {
+ max = cand;
+ *minIdx = currentMinIdx;
+ *maxIdx = i;
+ }
+ if (min > A[i]) {
+ min = A[i];
+ currentMinIdx = i;
+ }
+ }
+
+ return max;
+ }
+} // namespace
+
+nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
+ nsRect bestRect;
+
+ if (GetNumRects() <= 1) {
+ bestRect = GetBounds();
+ return bestRect;
+ }
+
+ AxisPartition xaxis, yaxis;
+
+ // Step 1: Calculate the grid lines
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ xaxis.InsertCoord(rect.x);
+ xaxis.InsertCoord(rect.XMost());
+ yaxis.InsertCoord(rect.y);
+ yaxis.InsertCoord(rect.YMost());
+ }
+ if (!aContainingRect.IsEmpty()) {
+ xaxis.InsertCoord(aContainingRect.x);
+ xaxis.InsertCoord(aContainingRect.XMost());
+ yaxis.InsertCoord(aContainingRect.y);
+ yaxis.InsertCoord(aContainingRect.YMost());
+ }
+
+ // Step 2: Fill out the grid with the areas
+ // Note: due to the ordering of rectangles in the region, it is not always
+ // possible to combine steps 2 and 3 so we don't try to be clever.
+ int32_t matrixHeight = yaxis.GetNumStops() - 1;
+ int32_t matrixWidth = xaxis.GetNumStops() - 1;
+ int32_t matrixSize = matrixHeight * matrixWidth;
+ nsTArray<SizePair> areas(matrixSize);
+ areas.SetLength(matrixSize);
+
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ int32_t xstart = xaxis.IndexOf(rect.x);
+ int32_t xend = xaxis.IndexOf(rect.XMost());
+ int32_t y = yaxis.IndexOf(rect.y);
+ int32_t yend = yaxis.IndexOf(rect.YMost());
+
+ for (; y < yend; y++) {
+ nscoord height = yaxis.StopSize(y);
+ for (int32_t x = xstart; x < xend; x++) {
+ nscoord width = xaxis.StopSize(x);
+ int64_t size = width*int64_t(height);
+ if (rect.Intersects(aContainingRect)) {
+ areas[y*matrixWidth+x].mSizeContainingRect = size;
+ }
+ areas[y*matrixWidth+x].mSize = size;
+ }
+ }
+ }
+
+ // Step 3: Find the maximum submatrix sum that does not contain a rectangle
+ {
+ // First get the prefix sum array
+ int32_t m = matrixHeight + 1;
+ int32_t n = matrixWidth + 1;
+ nsTArray<SizePair> pareas(m*n);
+ pareas.SetLength(m*n);
+ for (int32_t y = 1; y < m; y++) {
+ for (int32_t x = 1; x < n; x++) {
+ SizePair area = areas[(y-1)*matrixWidth+x-1];
+ if (!area.mSize) {
+ area = SizePair::VeryLargeNegative();
+ }
+ area = area + pareas[ y*n+x-1]
+ + pareas[(y-1)*n+x ]
+ - pareas[(y-1)*n+x-1];
+ pareas[y*n+x] = area;
+ }
+ }
+
+ // No longer need the grid
+ areas.SetLength(0);
+
+ SizePair bestArea;
+ struct {
+ int32_t left, top, right, bottom;
+ } bestRectIndices = { 0, 0, 0, 0 };
+ for (int32_t m1 = 0; m1 < m; m1++) {
+ for (int32_t m2 = m1+1; m2 < m; m2++) {
+ nsTArray<SizePair> B;
+ B.SetLength(n);
+ for (int32_t i = 0; i < n; i++) {
+ B[i] = pareas[m2*n+i] - pareas[m1*n+i];
+ }
+ int32_t minIdx, maxIdx;
+ SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
+ if (area > bestArea) {
+ bestRectIndices.left = minIdx;
+ bestRectIndices.top = m1;
+ bestRectIndices.right = maxIdx;
+ bestRectIndices.bottom = m2;
+ bestArea = area;
+ }
+ }
+ }
+
+ bestRect.MoveTo(xaxis.StopAt(bestRectIndices.left),
+ yaxis.StopAt(bestRectIndices.top));
+ bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
+ yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
+ }
+
+ return bestRect;
+}
+
+std::ostream& operator<<(std::ostream& stream, const nsRegion& m) {
+ stream << "[";
+
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&m.mImpl), &n);
+ for (int i=0; i<n; i++) {
+ if (i != 0) {
+ stream << "; ";
+ }
+ stream << boxes[i].x1 << "," << boxes[i].y1 << "," << boxes[i].x2 << "," << boxes[i].y2;
+ }
+
+ stream << "]";
+ return stream;
+}
+
+nsCString
+nsRegion::ToString() const {
+ return nsCString(mozilla::ToString(*this).c_str());
+}
diff --git a/system/graphics/src/nsRegion.h b/system/graphics/src/nsRegion.h
new file mode 100644
index 000000000..e4ad5fc8a
--- /dev/null
+++ b/system/graphics/src/nsRegion.h
@@ -0,0 +1,867 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef nsRegion_h__
+#define nsRegion_h__
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include <ostream> // for std::ostream
+#include "nsCoord.h" // for nscoord
+#include "nsError.h" // for nsresult
+#include "nsPoint.h" // for nsIntPoint, nsPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect, nsRect
+#include "nsMargin.h" // for nsIntMargin
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nsStringGlue.h" // for nsCString
+#include "xpcom-config.h" // for CPP_THROW_NEW
+#include "mozilla/ArrayView.h" // for ArrayView
+#include "mozilla/Move.h" // for mozilla::Move
+#include "mozilla/gfx/MatrixFwd.h" // for mozilla::gfx::Matrix4x4
+
+#include "pixman.h"
+
+/* For information on the internal representation look at pixman-region.c
+ *
+ * This replaces an older homebrew implementation of nsRegion. The
+ * representation used here may use more rectangles than nsRegion however, the
+ * representation is canonical. This means that there's no need for an
+ * Optimize() method because for a paticular region there is only one
+ * representation. This means that nsIntRegion will have more predictable
+ * performance characteristics than the old nsRegion and should not become
+ * degenerate.
+ *
+ * The pixman region code originates from X11 which has spread to a variety of
+ * projects including Qt, Gtk, Wine. It should perform reasonably well.
+ */
+
+enum class VisitSide {
+ TOP,
+ BOTTOM,
+ LEFT,
+ RIGHT
+};
+
+class nsRegion
+{
+public:
+ typedef nsRect RectType;
+ typedef nsPoint PointType;
+ typedef nsMargin MarginType;
+
+ nsRegion () { pixman_region32_init(&mImpl); }
+ MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl,
+ aRect.x,
+ aRect.y,
+ aRect.width,
+ aRect.height); }
+ explicit nsRegion (mozilla::gfx::ArrayView<pixman_box32_t> aRects)
+ {
+ pixman_region32_init_rects(&mImpl, aRects.Data(), aRects.Length());
+ }
+ nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); }
+ nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); }
+ nsRegion& operator = (nsRegion&& aRegion) {
+ pixman_region32_fini(&mImpl);
+ mImpl = aRegion.mImpl;
+ pixman_region32_init(&aRegion.mImpl);
+ return *this;
+ }
+ ~nsRegion () { pixman_region32_fini(&mImpl); }
+ nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; }
+ nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; }
+ bool operator==(const nsRegion& aRgn) const
+ {
+ return IsEqual(aRgn);
+ }
+ bool operator!=(const nsRegion& aRgn) const
+ {
+ return !(*this == aRgn);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
+
+ void Swap(nsRegion* aOther)
+ {
+ pixman_region32_t tmp = mImpl;
+ mImpl = aOther->mImpl;
+ aOther->mImpl = tmp;
+ }
+
+ static
+ nsresult InitStatic()
+ {
+ return NS_OK;
+ }
+
+ static
+ void ShutdownStatic() {}
+
+ void AndWith(const nsRegion& aOther)
+ {
+ And(*this, aOther);
+ }
+ void AndWith(const nsRect& aOther)
+ {
+ And(*this, aOther);
+ }
+ nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2)
+ {
+ pixman_region32_intersect(&mImpl, aRgn1.Impl(), aRgn2.Impl());
+ return *this;
+ }
+ nsRegion& And(const nsRect& aRect, const nsRegion& aRegion)
+ {
+ return And(aRegion, aRect);
+ }
+ nsRegion& And(const nsRegion& aRegion, const nsRect& aRect)
+ {
+ pixman_region32_intersect_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height);
+ return *this;
+ }
+ nsRegion& And(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ nsRect TmpRect;
+
+ TmpRect.IntersectRect(aRect1, aRect2);
+ return Copy(TmpRect);
+ }
+
+ nsRegion& OrWith(const nsRegion& aOther)
+ {
+ return Or(*this, aOther);
+ }
+ nsRegion& OrWith(const nsRect& aOther)
+ {
+ return Or(*this, aOther);
+ }
+ nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2)
+ {
+ pixman_region32_union(&mImpl, aRgn1.Impl(), aRgn2.Impl());
+ return *this;
+ }
+ nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect)
+ {
+ pixman_region32_union_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height);
+ return *this;
+ }
+ nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion)
+ {
+ return Or(aRegion, aRect);
+ }
+ nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ Copy (aRect1);
+ return Or (*this, aRect2);
+ }
+
+ nsRegion& XorWith(const nsRegion& aOther)
+ {
+ return Xor(*this, aOther);
+ }
+ nsRegion& XorWith(const nsRect& aOther)
+ {
+ return Xor(*this, aOther);
+ }
+ nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2)
+ {
+ // this could be implemented better if pixman had direct
+ // support for xoring regions.
+ nsRegion p;
+ p.Sub(aRgn1, aRgn2);
+ nsRegion q;
+ q.Sub(aRgn2, aRgn1);
+ return Or(p, q);
+ }
+ nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect)
+ {
+ return Xor(aRegion, nsRegion(aRect));
+ }
+ nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion)
+ {
+ return Xor(nsRegion(aRect), aRegion);
+ }
+ nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ return Xor(nsRegion(aRect1), nsRegion(aRect2));
+ }
+
+ nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const;
+
+ nsRegion& SubOut(const nsRegion& aOther)
+ {
+ return Sub(*this, aOther);
+ }
+ nsRegion& SubOut(const nsRect& aOther)
+ {
+ return Sub(*this, aOther);
+ }
+ nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2)
+ {
+ pixman_region32_subtract(&mImpl, aRgn1.Impl(), aRgn2.Impl());
+ return *this;
+ }
+ nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect)
+ {
+ return Sub(aRegion, nsRegion(aRect));
+ }
+ nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion)
+ {
+ return Sub(nsRegion(aRect), aRegion);
+ }
+ nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2)
+ {
+ Copy(aRect1);
+ return Sub(*this, aRect2);
+ }
+
+ /**
+ * Returns true iff the given point is inside the region. A region
+ * created from a rect (x=0, y=0, w=100, h=100) will NOT contain
+ * the point x=100, y=100.
+ */
+ bool Contains (int aX, int aY) const
+ {
+ return pixman_region32_contains_point(Impl(), aX, aY, nullptr);
+ }
+ bool Contains (const nsRect& aRect) const
+ {
+ pixman_box32_t box = RectToBox(aRect);
+ return pixman_region32_contains_rectangle(Impl(), &box) == PIXMAN_REGION_IN;
+ }
+ bool Contains (const nsRegion& aRgn) const;
+ bool Intersects (const nsRect& aRect) const;
+
+ void MoveBy (int32_t aXOffset, int32_t aYOffset)
+ {
+ MoveBy (nsPoint (aXOffset, aYOffset));
+ }
+ void MoveBy (nsPoint aPt) { pixman_region32_translate(&mImpl, aPt.x, aPt.y); }
+ void SetEmpty ()
+ {
+ pixman_region32_clear(&mImpl);
+ }
+
+ nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const
+ {
+ return MovedBy(nsPoint(aXOffset, aYOffset));
+ }
+ nsRegion MovedBy(const nsPoint& aPt) const
+ {
+ nsRegion copy(*this);
+ copy.MoveBy(aPt);
+ return copy;
+ }
+
+ nsRegion Intersect(const nsRegion& aOther) const
+ {
+ nsRegion intersection;
+ intersection.And(*this, aOther);
+ return intersection;
+ }
+
+ void Inflate(const nsMargin& aMargin);
+
+ nsRegion Inflated(const nsMargin& aMargin) const
+ {
+ nsRegion copy(*this);
+ copy.Inflate(aMargin);
+ return copy;
+ }
+
+ bool IsEmpty () const { return !pixman_region32_not_empty(Impl()); }
+ bool IsComplex () const { return GetNumRects() > 1; }
+ bool IsEqual (const nsRegion& aRegion) const
+ {
+ return pixman_region32_equal(Impl(), aRegion.Impl());
+ }
+ uint32_t GetNumRects () const
+ {
+ // Work around pixman bug. Sometimes pixman creates regions with 1 rect
+ // that's empty.
+ uint32_t result = pixman_region32_n_rects(Impl());
+ return (result == 1 && GetBounds().IsEmpty()) ? 0 : result;
+ }
+ const nsRect GetBounds () const { return BoxToRect(mImpl.extents); }
+ uint64_t Area () const;
+
+ /**
+ * Return this region scaled to a different appunits per pixel (APP) ratio.
+ * This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the region.
+ * @param aFromAPP the APP to scale from
+ * @param aToAPP the APP to scale to
+ * @note this can turn an empty region into a non-empty region
+ */
+ MOZ_MUST_USE nsRegion
+ ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const;
+ MOZ_MUST_USE nsRegion
+ ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const;
+ nsRegion& ScaleRoundOut(float aXScale, float aYScale);
+ nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale);
+ nsRegion& Transform (const mozilla::gfx::Matrix4x4 &aTransform);
+ nsIntRegion ScaleToOutsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
+ nsIntRegion ScaleToInsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
+ nsIntRegion ScaleToNearestPixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
+ nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
+ nsIntRegion ToNearestPixels (nscoord aAppUnitsPerPixel) const;
+
+ /**
+ * Gets the largest rectangle contained in the region.
+ * @param aContainingRect if non-empty, we choose a rectangle that
+ * maximizes the area intersecting with aContainingRect (and break ties by
+ * then choosing the largest rectangle overall)
+ */
+ nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const;
+
+ /**
+ * Make sure the region has at most aMaxRects by adding area to it
+ * if necessary. The simplified region will be a superset of the
+ * original region. The simplified region's bounding box will be
+ * the same as for the current region.
+ */
+ void SimplifyOutward (uint32_t aMaxRects);
+ /**
+ * Simplify the region by adding at most aThreshold area between spans of
+ * rects. The simplified region will be a superset of the original region.
+ * The simplified region's bounding box will be the same as for the current
+ * region.
+ */
+ void SimplifyOutwardByArea(uint32_t aThreshold);
+ /**
+ * Make sure the region has at most aMaxRects by removing area from
+ * it if necessary. The simplified region will be a subset of the
+ * original region.
+ */
+ void SimplifyInward (uint32_t aMaxRects);
+
+ /**
+ * VisitEdges is a weird kind of function that we use for padding
+ * out surfaces to prevent texture filtering artifacts.
+ * It calls the visitFn callback for each of the exterior edges of
+ * the regions. The top and bottom edges will be expanded 1 pixel
+ * to the left and right if there's an outside corner. The order
+ * the edges are visited is not guaranteed.
+ *
+ * visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT
+ * and specifies which kind of edge is being visited. x1, y1, x2, y2
+ * are the coordinates of the line. (x1 == x2) || (y1 == y2)
+ */
+ typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
+ void VisitEdges(visitFn, void *closure);
+
+ nsCString ToString() const;
+
+ class RectIterator
+ {
+ int mCurrent; // Index of the current entry
+ int mLimit; // Index one past the final entry.
+ mutable nsRect mTmp; // The most recently gotten rectangle.
+ pixman_box32_t *mBoxes;
+
+ public:
+ explicit RectIterator(const nsRegion& aRegion)
+ {
+ mCurrent = 0;
+ mBoxes = pixman_region32_rectangles(aRegion.Impl(), &mLimit);
+ // Work around pixman bug. Sometimes pixman creates regions with 1 rect
+ // that's empty.
+ if (mLimit == 1 && nsRegion::BoxToRect(mBoxes[0]).IsEmpty()) {
+ mLimit = 0;
+ }
+ }
+
+ bool Done() const { return mCurrent == mLimit; }
+
+ const nsRect& Get() const
+ {
+ MOZ_ASSERT(!Done());
+ mTmp = nsRegion::BoxToRect(mBoxes[mCurrent]);
+ NS_ASSERTION(!mTmp.IsEmpty(), "Shouldn't return empty rect");
+ return mTmp;
+ }
+
+ void Next()
+ {
+ MOZ_ASSERT(!Done());
+ mCurrent++;
+ }
+ };
+
+ RectIterator RectIter() const { return RectIterator(*this); }
+
+private:
+ pixman_region32_t mImpl;
+
+#ifndef MOZ_TREE_PIXMAN
+ // For compatibility with pixman versions older than 0.25.2.
+ static inline void
+ pixman_region32_clear(pixman_region32_t *region)
+ {
+ pixman_region32_fini(region);
+ pixman_region32_init(region);
+ }
+#endif
+
+ nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const;
+
+ nsRegion& Copy (const nsRegion& aRegion)
+ {
+ pixman_region32_copy(&mImpl, aRegion.Impl());
+ return *this;
+ }
+
+ nsRegion& Copy (const nsRect& aRect)
+ {
+ // pixman needs to distinguish between an empty region and a region
+ // with one rect so that it can return a different number of rectangles.
+ // Empty rect: data = empty_box
+ // 1 rect: data = null
+ // >1 rect: data = rects
+ if (aRect.IsEmpty()) {
+ pixman_region32_clear(&mImpl);
+ } else {
+ pixman_box32_t box = RectToBox(aRect);
+ pixman_region32_reset(&mImpl, &box);
+ }
+ return *this;
+ }
+
+ static inline pixman_box32_t RectToBox(const nsRect &aRect)
+ {
+ pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
+ return box;
+ }
+
+ static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
+ {
+ pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
+ return box;
+ }
+
+
+ static inline nsRect BoxToRect(const pixman_box32_t &aBox)
+ {
+ return nsRect(aBox.x1, aBox.y1,
+ aBox.x2 - aBox.x1,
+ aBox.y2 - aBox.y1);
+ }
+
+ pixman_region32_t* Impl() const
+ {
+ return const_cast<pixman_region32_t*>(&mImpl);
+ }
+};
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * BaseIntRegions use int32_t coordinates.
+ */
+template <typename Derived, typename Rect, typename Point, typename Margin>
+class BaseIntRegion
+{
+ friend class ::nsRegion;
+
+ // Give access to all specializations of IntRegionTyped, not just ones that
+ // derive from this specialization of BaseIntRegion.
+ template <typename units>
+ friend class IntRegionTyped;
+
+public:
+ typedef Rect RectType;
+ typedef Point PointType;
+ typedef Margin MarginType;
+
+ BaseIntRegion () {}
+ MOZ_IMPLICIT BaseIntRegion (const Rect& aRect) : mImpl (ToRect(aRect)) {}
+ explicit BaseIntRegion (mozilla::gfx::ArrayView<pixman_box32_t> aRects) : mImpl (aRects) {}
+ BaseIntRegion (const BaseIntRegion& aRegion) : mImpl (aRegion.mImpl) {}
+ BaseIntRegion (BaseIntRegion&& aRegion) : mImpl (mozilla::Move(aRegion.mImpl)) {}
+ Derived& operator = (const Rect& aRect) { mImpl = ToRect (aRect); return This(); }
+ Derived& operator = (const Derived& aRegion) { mImpl = aRegion.mImpl; return This(); }
+ Derived& operator = (Derived&& aRegion) { mImpl = mozilla::Move(aRegion.mImpl); return This(); }
+
+ bool operator==(const Derived& aRgn) const
+ {
+ return IsEqual(aRgn);
+ }
+ bool operator!=(const Derived& aRgn) const
+ {
+ return !(*this == aRgn);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const Derived& m) {
+ return stream << m.mImpl;
+ }
+
+ void Swap(Derived* aOther)
+ {
+ mImpl.Swap(&aOther->mImpl);
+ }
+
+ void AndWith(const Derived& aOther)
+ {
+ And(This(), aOther);
+ }
+ void AndWith(const Rect& aOther)
+ {
+ And(This(), aOther);
+ }
+ Derived& And (const Derived& aRgn1, const Derived& aRgn2)
+ {
+ mImpl.And (aRgn1.mImpl, aRgn2.mImpl);
+ return This();
+ }
+ Derived& And (const Derived& aRegion, const Rect& aRect)
+ {
+ mImpl.And (aRegion.mImpl, ToRect (aRect));
+ return This();
+ }
+ Derived& And (const Rect& aRect, const Derived& aRegion)
+ {
+ return And (aRegion, aRect);
+ }
+ Derived& And (const Rect& aRect1, const Rect& aRect2)
+ {
+ Rect TmpRect;
+
+ TmpRect.IntersectRect (aRect1, aRect2);
+ mImpl = ToRect (TmpRect);
+ return This();
+ }
+
+ Derived& OrWith(const Derived& aOther)
+ {
+ return Or(This(), aOther);
+ }
+ Derived& OrWith(const Rect& aOther)
+ {
+ return Or(This(), aOther);
+ }
+ Derived& Or (const Derived& aRgn1, const Derived& aRgn2)
+ {
+ mImpl.Or (aRgn1.mImpl, aRgn2.mImpl);
+ return This();
+ }
+ Derived& Or (const Derived& aRegion, const Rect& aRect)
+ {
+ mImpl.Or (aRegion.mImpl, ToRect (aRect));
+ return This();
+ }
+ Derived& Or (const Rect& aRect, const Derived& aRegion)
+ {
+ return Or (aRegion, aRect);
+ }
+ Derived& Or (const Rect& aRect1, const Rect& aRect2)
+ {
+ mImpl = ToRect (aRect1);
+ return Or (This(), aRect2);
+ }
+
+ Derived& XorWith(const Derived& aOther)
+ {
+ return Xor(This(), aOther);
+ }
+ Derived& XorWith(const Rect& aOther)
+ {
+ return Xor(This(), aOther);
+ }
+ Derived& Xor (const Derived& aRgn1, const Derived& aRgn2)
+ {
+ mImpl.Xor (aRgn1.mImpl, aRgn2.mImpl);
+ return This();
+ }
+ Derived& Xor (const Derived& aRegion, const Rect& aRect)
+ {
+ mImpl.Xor (aRegion.mImpl, ToRect (aRect));
+ return This();
+ }
+ Derived& Xor (const Rect& aRect, const Derived& aRegion)
+ {
+ return Xor (aRegion, aRect);
+ }
+ Derived& Xor (const Rect& aRect1, const Rect& aRect2)
+ {
+ mImpl = ToRect (aRect1);
+ return Xor (This(), aRect2);
+ }
+
+ Derived& SubOut(const Derived& aOther)
+ {
+ return Sub(This(), aOther);
+ }
+ Derived& SubOut(const Rect& aOther)
+ {
+ return Sub(This(), aOther);
+ }
+ Derived& Sub (const Derived& aRgn1, const Derived& aRgn2)
+ {
+ mImpl.Sub (aRgn1.mImpl, aRgn2.mImpl);
+ return This();
+ }
+ Derived& Sub (const Derived& aRegion, const Rect& aRect)
+ {
+ mImpl.Sub (aRegion.mImpl, ToRect (aRect));
+ return This();
+ }
+ Derived& Sub (const Rect& aRect, const Derived& aRegion)
+ {
+ return Sub (Derived (aRect), aRegion);
+ }
+ Derived& Sub (const Rect& aRect1, const Rect& aRect2)
+ {
+ mImpl = ToRect (aRect1);
+ return Sub (This(), aRect2);
+ }
+
+ /**
+ * Returns true iff the given point is inside the region. A region
+ * created from a rect (x=0, y=0, w=100, h=100) will NOT contain
+ * the point x=100, y=100.
+ */
+ bool Contains (int aX, int aY) const
+ {
+ return mImpl.Contains(aX, aY);
+ }
+ bool Contains (const Rect& aRect) const
+ {
+ return mImpl.Contains (ToRect (aRect));
+ }
+ bool Contains (const Derived& aRgn) const
+ {
+ return mImpl.Contains (aRgn.mImpl);
+ }
+ bool Intersects (const Rect& aRect) const
+ {
+ return mImpl.Intersects (ToRect (aRect));
+ }
+
+ void MoveBy (int32_t aXOffset, int32_t aYOffset)
+ {
+ MoveBy (Point (aXOffset, aYOffset));
+ }
+ void MoveBy (Point aPt)
+ {
+ mImpl.MoveBy (aPt.x, aPt.y);
+ }
+ Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const
+ {
+ return MovedBy(Point(aXOffset, aYOffset));
+ }
+ Derived MovedBy(const Point& aPt) const
+ {
+ Derived copy(This());
+ copy.MoveBy(aPt);
+ return copy;
+ }
+
+ Derived Intersect(const Derived& aOther) const
+ {
+ Derived intersection;
+ intersection.And(This(), aOther);
+ return intersection;
+ }
+
+ void Inflate(const Margin& aMargin)
+ {
+ mImpl.Inflate(nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left));
+ }
+ Derived Inflated(const Margin& aMargin) const
+ {
+ Derived copy(This());
+ copy.Inflate(aMargin);
+ return copy;
+ }
+
+ void SetEmpty ()
+ {
+ mImpl.SetEmpty ();
+ }
+
+ bool IsEmpty () const { return mImpl.IsEmpty (); }
+ bool IsComplex () const { return mImpl.IsComplex (); }
+ bool IsEqual (const Derived& aRegion) const
+ {
+ return mImpl.IsEqual (aRegion.mImpl);
+ }
+ uint32_t GetNumRects () const { return mImpl.GetNumRects (); }
+ Rect GetBounds () const { return FromRect (mImpl.GetBounds ()); }
+ uint64_t Area () const { return mImpl.Area(); }
+ nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const
+ {
+ nsRegion result;
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel);
+ result.Or(result, appRect);
+ }
+ return result;
+ }
+ Rect GetLargestRectangle (const Rect& aContainingRect = Rect()) const
+ {
+ return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) ));
+ }
+
+ Derived& ScaleRoundOut (float aXScale, float aYScale)
+ {
+ mImpl.ScaleRoundOut(aXScale, aYScale);
+ return This();
+ }
+
+ Derived& ScaleInverseRoundOut (float aXScale, float aYScale)
+ {
+ mImpl.ScaleInverseRoundOut(aXScale, aYScale);
+ return This();
+ }
+
+ // Prefer using TransformBy(matrix, region) from UnitTransforms.h,
+ // as applying the transform should typically change the unit system.
+ // TODO(botond): Move this to IntRegionTyped and disable it for
+ // unit != UnknownUnits.
+ Derived& Transform (const mozilla::gfx::Matrix4x4 &aTransform)
+ {
+ mImpl.Transform(aTransform);
+ return This();
+ }
+
+ /**
+ * Make sure the region has at most aMaxRects by adding area to it
+ * if necessary. The simplified region will be a superset of the
+ * original region. The simplified region's bounding box will be
+ * the same as for the current region.
+ */
+ void SimplifyOutward (uint32_t aMaxRects)
+ {
+ mImpl.SimplifyOutward (aMaxRects);
+ }
+ void SimplifyOutwardByArea (uint32_t aThreshold)
+ {
+ mImpl.SimplifyOutwardByArea (aThreshold);
+ }
+ /**
+ * Make sure the region has at most aMaxRects by removing area from
+ * it if necessary. The simplified region will be a subset of the
+ * original region.
+ */
+ void SimplifyInward (uint32_t aMaxRects)
+ {
+ mImpl.SimplifyInward (aMaxRects);
+ }
+
+ typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
+ void VisitEdges (visitFn visit, void *closure)
+ {
+ mImpl.VisitEdges (visit, closure);
+ }
+
+ nsCString ToString() const { return mImpl.ToString(); }
+
+ class RectIterator
+ {
+ nsRegion::RectIterator mImpl; // The underlying iterator.
+ mutable Rect mTmp; // The most recently gotten rectangle.
+
+ public:
+ explicit RectIterator(const BaseIntRegion& aRegion)
+ : mImpl(aRegion.mImpl)
+ {}
+
+ bool Done() const { return mImpl.Done(); }
+
+ const Rect& Get() const
+ {
+ mTmp = FromRect(mImpl.Get());
+ return mTmp;
+ }
+
+ void Next() { mImpl.Next(); }
+ };
+
+ RectIterator RectIter() const { return RectIterator(*this); }
+
+protected:
+ // Expose enough to derived classes from them to define conversions
+ // between different types of BaseIntRegions.
+ explicit BaseIntRegion(const nsRegion& aImpl) : mImpl(aImpl) {}
+ const nsRegion& Impl() const { return mImpl; }
+private:
+ nsRegion mImpl;
+
+ static nsRect ToRect(const Rect& aRect)
+ {
+ return nsRect (aRect.x, aRect.y, aRect.width, aRect.height);
+ }
+ static Rect FromRect(const nsRect& aRect)
+ {
+ return Rect (aRect.x, aRect.y, aRect.width, aRect.height);
+ }
+
+ Derived& This()
+ {
+ return *static_cast<Derived*>(this);
+ }
+ const Derived& This() const
+ {
+ return *static_cast<const Derived*>(this);
+ }
+};
+
+template <class units>
+class IntRegionTyped :
+ public BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>>
+{
+ typedef BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>> Super;
+
+ // Make other specializations of IntRegionTyped friends.
+ template <typename OtherUnits>
+ friend class IntRegionTyped;
+
+ static_assert(IsPixel<units>::value, "'units' must be a coordinate system tag");
+
+public:
+ typedef IntRectTyped<units> RectType;
+ typedef IntPointTyped<units> PointType;
+ typedef IntMarginTyped<units> MarginType;
+
+ // Forward constructors.
+ IntRegionTyped() {}
+ MOZ_IMPLICIT IntRegionTyped(const IntRectTyped<units>& aRect) : Super(aRect) {}
+ IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {}
+ explicit IntRegionTyped(mozilla::gfx::ArrayView<pixman_box32_t> aRects) : Super(aRects) {}
+ IntRegionTyped(IntRegionTyped&& aRegion) : Super(mozilla::Move(aRegion)) {}
+
+ // Assignment operators need to be forwarded as well, otherwise the compiler
+ // will declare deleted ones.
+ IntRegionTyped& operator=(const IntRegionTyped& aRegion)
+ {
+ return Super::operator=(aRegion);
+ }
+ IntRegionTyped& operator=(IntRegionTyped&& aRegion)
+ {
+ return Super::operator=(mozilla::Move(aRegion));
+ }
+
+ static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion)
+ {
+ return IntRegionTyped(aRegion.Impl());
+ }
+ IntRegion ToUnknownRegion() const
+ {
+ // Need |this->| because Impl() is defined in a dependent base class.
+ return IntRegion(this->Impl());
+ }
+private:
+ // This is deliberately private, so calling code uses FromUnknownRegion().
+ explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+typedef mozilla::gfx::IntRegion nsIntRegion;
+
+#endif
diff --git a/system/graphics/src/nsRegionFwd.h b/system/graphics/src/nsRegionFwd.h
new file mode 100644
index 000000000..6d5345d14
--- /dev/null
+++ b/system/graphics/src/nsRegionFwd.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef nsRegionFwd_h__
+#define nsRegionFwd_h__
+
+// Forward declare enough things to define the typedef |nsIntRegion|.
+
+namespace mozilla {
+namespace gfx {
+
+struct UnknownUnits;
+
+template <class units>
+class IntRegionTyped;
+
+typedef IntRegionTyped<UnknownUnits> IntRegion;
+
+} // namespace gfx
+} // namespace mozilla
+
+typedef mozilla::gfx::IntRegion nsIntRegion;
+
+#endif
diff --git a/system/graphics/src/nsRenderingContext.h b/system/graphics/src/nsRenderingContext.h
new file mode 100644
index 000000000..02bb397d6
--- /dev/null
+++ b/system/graphics/src/nsRenderingContext.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSRENDERINGCONTEXT__H__
+#define NSRENDERINGCONTEXT__H__
+
+#include "gfxContext.h"
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+} // namespace mozilla
+
+class MOZ_STACK_CLASS nsRenderingContext final
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+public:
+ explicit nsRenderingContext(gfxContext* aThebesContext)
+ : mThebes(aThebesContext)
+ {}
+
+ explicit nsRenderingContext(already_AddRefed<gfxContext>&& aThebesContext)
+ : mThebes(aThebesContext)
+ {}
+
+ // These accessors will never return null.
+ gfxContext *ThebesContext() { return mThebes; }
+ DrawTarget *GetDrawTarget() { return mThebes->GetDrawTarget(); }
+
+private:
+ RefPtr<gfxContext> mThebes;
+};
+
+#endif // NSRENDERINGCONTEXT__H__
diff --git a/system/graphics/src/nsScriptableRegion.cpp b/system/graphics/src/nsScriptableRegion.cpp
new file mode 100644
index 000000000..f0afff8f4
--- /dev/null
+++ b/system/graphics/src/nsScriptableRegion.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsScriptableRegion.h"
+#include <stdint.h> // for uint32_t
+#include <sys/types.h> // for int32_t
+#include "js/RootingAPI.h" // for Rooted
+#include "js/Value.h" // for INT_TO_JSVAL, etc
+#include "jsapi.h" // for JS_DefineElement, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc
+#include "nsID.h"
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nscore.h" // for NS_IMETHODIMP
+
+class JSObject;
+struct JSContext;
+
+nsScriptableRegion::nsScriptableRegion()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsScriptableRegion, nsIScriptableRegion)
+
+NS_IMETHODIMP nsScriptableRegion::Init()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::SetToRegion(nsIScriptableRegion *aRegion)
+{
+ aRegion->GetRegion(&mRegion);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::SetToRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+{
+ mRegion = mozilla::gfx::IntRect(aX, aY, aWidth, aHeight);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::IntersectRegion(nsIScriptableRegion *aRegion)
+{
+ nsIntRegion region;
+ aRegion->GetRegion(&region);
+ mRegion.And(mRegion, region);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::IntersectRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+{
+ mRegion.And(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::UnionRegion(nsIScriptableRegion *aRegion)
+{
+ nsIntRegion region;
+ aRegion->GetRegion(&region);
+ mRegion.Or(mRegion, region);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::UnionRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+{
+ mRegion.Or(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::SubtractRegion(nsIScriptableRegion *aRegion)
+{
+ nsIntRegion region;
+ aRegion->GetRegion(&region);
+ mRegion.Sub(mRegion, region);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::SubtractRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+{
+ mRegion.Sub(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::IsEmpty(bool *isEmpty)
+{
+ *isEmpty = mRegion.IsEmpty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::IsEqualRegion(nsIScriptableRegion *aRegion, bool *isEqual)
+{
+ nsIntRegion region;
+ aRegion->GetRegion(&region);
+ *isEqual = mRegion.IsEqual(region);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::GetBoundingBox(int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
+{
+ mozilla::gfx::IntRect boundRect = mRegion.GetBounds();
+ *aX = boundRect.x;
+ *aY = boundRect.y;
+ *aWidth = boundRect.width;
+ *aHeight = boundRect.height;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::Offset(int32_t aXOffset, int32_t aYOffset)
+{
+ mRegion.MoveBy(aXOffset, aYOffset);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::ContainsRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool *containsRect)
+{
+ *containsRect = mRegion.Contains(mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsScriptableRegion::GetRegion(nsIntRegion* outRgn)
+{
+ *outRgn = mRegion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsScriptableRegion::GetRects(JSContext* aCx, JS::MutableHandle<JS::Value> aRects)
+{
+ uint32_t numRects = mRegion.GetNumRects();
+
+ if (!numRects) {
+ aRects.setNull();
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> destArray(aCx, JS_NewArrayObject(aCx, numRects * 4));
+ if (!destArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ aRects.setObject(*destArray);
+
+ uint32_t n = 0;
+ for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const mozilla::gfx::IntRect& rect = iter.Get();
+ if (!JS_DefineElement(aCx, destArray, n, rect.x, JSPROP_ENUMERATE) ||
+ !JS_DefineElement(aCx, destArray, n + 1, rect.y, JSPROP_ENUMERATE) ||
+ !JS_DefineElement(aCx, destArray, n + 2, rect.width, JSPROP_ENUMERATE) ||
+ !JS_DefineElement(aCx, destArray, n + 3, rect.height, JSPROP_ENUMERATE)) {
+ return NS_ERROR_FAILURE;
+ }
+ n += 4;
+ }
+
+ return NS_OK;
+}
diff --git a/system/graphics/src/nsScriptableRegion.h b/system/graphics/src/nsScriptableRegion.h
new file mode 100644
index 000000000..094093c75
--- /dev/null
+++ b/system/graphics/src/nsScriptableRegion.h
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsScriptableRegion_h
+#define nsScriptableRegion_h
+
+#include "nsIScriptableRegion.h"
+#include "nsISupports.h"
+#include "nsRegion.h"
+#include "mozilla/Attributes.h"
+
+class nsScriptableRegion final : public nsIScriptableRegion {
+public:
+ nsScriptableRegion();
+
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSISCRIPTABLEREGION
+
+private:
+ ~nsScriptableRegion() {}
+ nsIntRegion mRegion;
+};
+
+#endif
diff --git a/system/graphics/src/nsSize.h b/system/graphics/src/nsSize.h
new file mode 100644
index 000000000..f27c478f9
--- /dev/null
+++ b/system/graphics/src/nsSize.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSSIZE_H
+#define NSSIZE_H
+
+#include "nsCoord.h"
+#include "mozilla/gfx/BaseSize.h"
+#include "mozilla/gfx/Point.h"
+
+// Maximum allowable size
+#define NS_MAXSIZE nscoord_MAX
+
+typedef mozilla::gfx::IntSize nsIntSize;
+
+struct nsSize : public mozilla::gfx::BaseSize<nscoord, nsSize> {
+ typedef mozilla::gfx::BaseSize<nscoord, nsSize> Super;
+
+ nsSize() : Super() {}
+ nsSize(nscoord aWidth, nscoord aHeight) : Super(aWidth, aHeight) {}
+
+ inline mozilla::gfx::IntSize ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const;
+ inline mozilla::gfx::IntSize ToNearestPixels(nscoord aAppUnitsPerPixel) const;
+
+ /**
+ * Return this size scaled to a different appunits per pixel (APP) ratio.
+ * @param aFromAPP the APP to scale from
+ * @param aToAPP the APP to scale to
+ */
+ MOZ_MUST_USE inline nsSize
+ ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
+};
+
+inline mozilla::gfx::IntSize
+nsSize::ScaleToNearestPixels(float aXScale, float aYScale,
+ nscoord aAppUnitsPerPixel) const
+{
+ return mozilla::gfx::IntSize(
+ NSToIntRoundUp(NSAppUnitsToDoublePixels(width, aAppUnitsPerPixel) * aXScale),
+ NSToIntRoundUp(NSAppUnitsToDoublePixels(height, aAppUnitsPerPixel) * aYScale));
+}
+
+inline mozilla::gfx::IntSize
+nsSize::ToNearestPixels(nscoord aAppUnitsPerPixel) const
+{
+ return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
+}
+
+inline nsSize
+nsSize::ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const {
+ if (aFromAPP != aToAPP) {
+ nsSize size;
+ size.width = NSToCoordRound(NSCoordScale(width, aFromAPP, aToAPP));
+ size.height = NSToCoordRound(NSCoordScale(height, aFromAPP, aToAPP));
+ return size;
+ }
+ return *this;
+}
+
+inline nsSize
+IntSizeToAppUnits(mozilla::gfx::IntSize aSize, nscoord aAppUnitsPerPixel)
+{
+ return nsSize(NSIntPixelsToAppUnits(aSize.width, aAppUnitsPerPixel),
+ NSIntPixelsToAppUnits(aSize.height, aAppUnitsPerPixel));
+}
+
+#endif /* NSSIZE_H */
diff --git a/system/graphics/src/nsThebesFontEnumerator.cpp b/system/graphics/src/nsThebesFontEnumerator.cpp
new file mode 100644
index 000000000..3ef3f443b
--- /dev/null
+++ b/system/graphics/src/nsThebesFontEnumerator.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThebesFontEnumerator.h"
+#include <stdint.h> // for uint32_t
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsDebug.h" // for NS_ENSURE_ARG_POINTER
+#include "nsError.h" // for NS_OK, NS_FAILED, nsresult
+#include "nsIAtom.h" // for nsIAtom, NS_Atomize
+#include "nsID.h"
+#include "nsMemory.h" // for nsMemory
+#include "nsString.h" // for nsAutoCString, nsAutoString, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nscore.h" // for char16_t, NS_IMETHODIMP
+
+NS_IMPL_ISUPPORTS(nsThebesFontEnumerator, nsIFontEnumerator)
+
+nsThebesFontEnumerator::nsThebesFontEnumerator()
+{
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::EnumerateAllFonts(uint32_t *aCount,
+ char16_t ***aResult)
+{
+ return EnumerateFonts (nullptr, nullptr, aCount, aResult);
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::EnumerateFonts(const char *aLangGroup,
+ const char *aGeneric,
+ uint32_t *aCount,
+ char16_t ***aResult)
+{
+ NS_ENSURE_ARG_POINTER(aCount);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsTArray<nsString> fontList;
+
+ nsAutoCString generic;
+ if (aGeneric)
+ generic.Assign(aGeneric);
+ else
+ generic.SetIsVoid(true);
+
+ nsCOMPtr<nsIAtom> langGroupAtom;
+ if (aLangGroup) {
+ nsAutoCString lowered;
+ lowered.Assign(aLangGroup);
+ ToLowerCase(lowered);
+ langGroupAtom = NS_Atomize(lowered);
+ }
+
+ nsresult rv = gfxPlatform::GetPlatform()->GetFontList(langGroupAtom, generic, fontList);
+
+ if (NS_FAILED(rv)) {
+ *aCount = 0;
+ *aResult = nullptr;
+ /* XXX in this case, do we want to return the CSS generics? */
+ return NS_OK;
+ }
+
+ char16_t **fs = static_cast<char16_t **>
+ (moz_xmalloc(fontList.Length() * sizeof(char16_t*)));
+ for (uint32_t i = 0; i < fontList.Length(); i++) {
+ fs[i] = ToNewUnicode(fontList[i]);
+ }
+
+ *aResult = fs;
+ *aCount = fontList.Length();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::HaveFontFor(const char *aLangGroup,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::GetDefaultFont(const char *aLangGroup,
+ const char *aGeneric,
+ char16_t **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::UpdateFontList(bool *_retval)
+{
+ gfxPlatform::GetPlatform()->UpdateFontList();
+ *_retval = false; // always return false for now
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::GetStandardFamilyName(const char16_t *aName,
+ char16_t **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aName);
+
+ nsAutoString name(aName);
+ if (name.IsEmpty()) {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+
+ nsAutoString family;
+ nsresult rv = gfxPlatform::GetPlatform()->
+ GetStandardFamilyName(nsDependentString(aName), family);
+ if (NS_FAILED(rv) || family.IsEmpty()) {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+ *aResult = ToNewUnicode(family);
+ return NS_OK;
+}
diff --git a/system/graphics/src/nsThebesFontEnumerator.h b/system/graphics/src/nsThebesFontEnumerator.h
new file mode 100644
index 000000000..666663e3a
--- /dev/null
+++ b/system/graphics/src/nsThebesFontEnumerator.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NSTHEBESFONTENUMERATOR_H_
+#define _NSTHEBESFONTENUMERATOR_H_
+
+#include "mozilla/Attributes.h" // for final
+#include "nsIFontEnumerator.h" // for NS_DECL_NSIFONTENUMERATOR, etc
+#include "nsISupports.h" // for NS_DECL_ISUPPORTS
+
+class nsThebesFontEnumerator final : public nsIFontEnumerator
+{
+ ~nsThebesFontEnumerator() {}
+public:
+ nsThebesFontEnumerator();
+
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIFONTENUMERATOR
+};
+
+#endif /* _NSTHEBESFONTENUMERATOR_H_ */
diff --git a/system/graphics/src/nsThebesGfxFactory.cpp b/system/graphics/src/nsThebesGfxFactory.cpp
new file mode 100644
index 000000000..8e1e18d5c
--- /dev/null
+++ b/system/graphics/src/nsThebesGfxFactory.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for final
+#include "mozilla/Module.h" // for Module, Module::CIDEntry, etc
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsError.h" // for NS_ERROR_NO_AGGREGATION, etc
+#include "nsGfxCIID.h" // for NS_FONT_ENUMERATOR_CID, etc
+#include "nsID.h" // for NS_DEFINE_NAMED_CID, etc
+#include "nsIScriptableRegion.h" // for nsIScriptableRegion
+#include "nsISupports.h" // for NS_DECL_ISUPPORTS, etc
+#include "nsScriptableRegion.h" // for nsScriptableRegion
+#include "nsThebesFontEnumerator.h" // for nsThebesFontEnumerator
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesFontEnumerator)
+
+static nsresult
+nsScriptableRegionConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIScriptableRegion> scriptableRgn = new nsScriptableRegion();
+ return scriptableRgn->QueryInterface(aIID, aResult);
+}
+
+NS_DEFINE_NAMED_CID(NS_FONT_ENUMERATOR_CID);
+NS_DEFINE_NAMED_CID(NS_SCRIPTABLE_REGION_CID);
+
+static const mozilla::Module::CIDEntry kThebesCIDs[] = {
+ { &kNS_FONT_ENUMERATOR_CID, false, nullptr, nsThebesFontEnumeratorConstructor },
+ { &kNS_SCRIPTABLE_REGION_CID, false, nullptr, nsScriptableRegionConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kThebesContracts[] = {
+ { "@mozilla.org/gfx/fontenumerator;1", &kNS_FONT_ENUMERATOR_CID },
+ { "@mozilla.org/gfx/region;1", &kNS_SCRIPTABLE_REGION_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kThebesModule = {
+ mozilla::Module::kVersion,
+ kThebesCIDs,
+ kThebesContracts,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+NSMODULE_DEFN(nsGfxModule) = &kThebesModule;
diff --git a/system/graphics/src/nsThemeConstants.h b/system/graphics/src/nsThemeConstants.h
new file mode 100644
index 000000000..7825b9c6f
--- /dev/null
+++ b/system/graphics/src/nsThemeConstants.h
@@ -0,0 +1,296 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// No appearance at all.
+#define NS_THEME_NONE 0
+
+// A typical dialog button.
+#define NS_THEME_BUTTON 1
+
+// A radio element within a radio group.
+#define NS_THEME_RADIO 2
+
+// A checkbox element.
+#define NS_THEME_CHECKBOX 3
+
+// A rectangular button that contains complex content
+// like images (e.g. HTML <button> elements)
+#define NS_THEME_BUTTON_BEVEL 7
+
+// A themed focus outline (for outline:auto)
+#define NS_THEME_FOCUS_OUTLINE 8
+
+// The toolbox that contains the toolbars.
+#define NS_THEME_TOOLBOX 11
+
+// A toolbar in an application window.
+#define NS_THEME_TOOLBAR 12
+
+// A single toolbar button (with no associated dropdown)
+#define NS_THEME_TOOLBARBUTTON 13
+
+// A dual toolbar button (e.g., a Back button with a dropdown)
+#define NS_THEME_DUALBUTTON 14
+
+// The dropdown portion of a toolbar button
+#define NS_THEME_TOOLBARBUTTON_DROPDOWN 15
+
+// Various arrows that go in buttons
+#define NS_THEME_BUTTON_ARROW_UP 16
+#define NS_THEME_BUTTON_ARROW_DOWN 17
+#define NS_THEME_BUTTON_ARROW_NEXT 18
+#define NS_THEME_BUTTON_ARROW_PREVIOUS 19
+
+// A separator. Can be horizontal or vertical.
+#define NS_THEME_SEPARATOR 20
+
+// The gripper for a toolbar.
+#define NS_THEME_TOOLBARGRIPPER 21
+
+// A splitter. Can be horizontal or vertical.
+#define NS_THEME_SPLITTER 22
+
+// A status bar in a main application window.
+#define NS_THEME_STATUSBAR 23
+
+// A single pane of a status bar.
+#define NS_THEME_STATUSBARPANEL 24
+
+// The resizer background area in a status bar
+// for the resizer widget in the corner of a window.
+#define NS_THEME_RESIZERPANEL 25
+
+// The resizer itself.
+#define NS_THEME_RESIZER 26
+
+// List boxes
+#define NS_THEME_LISTBOX 31
+
+// A listbox item
+#define NS_THEME_LISTITEM 32
+
+// A tree widget
+#define NS_THEME_TREEVIEW 41
+
+// A tree item
+#define NS_THEME_TREEITEM 42
+
+// A tree widget twisty
+#define NS_THEME_TREETWISTY 43
+
+// A tree widget branch line
+#define NS_THEME_TREELINE 44
+
+// A listbox or tree widget header
+#define NS_THEME_TREEHEADER 45
+
+// An individual header cell
+#define NS_THEME_TREEHEADERCELL 46
+
+// The sort arrow for a header.
+#define NS_THEME_TREEHEADERSORTARROW 47
+
+// Open tree widget twisty
+#define NS_THEME_TREETWISTYOPEN 48
+
+// A horizontal progress bar.
+#define NS_THEME_PROGRESSBAR 51
+
+// The progress bar's progress indicator
+#define NS_THEME_PROGRESSCHUNK 52
+
+// A vertical progress bar.
+#define NS_THEME_PROGRESSBAR_VERTICAL 53
+
+// A vertical progress chunk
+#define NS_THEME_PROGRESSCHUNK_VERTICAL 54
+
+// A horizontal meter bar.
+#define NS_THEME_METERBAR 55
+
+// The meter bar's meter indicator
+#define NS_THEME_METERCHUNK 56
+
+// A single tab in a tab widget.
+#define NS_THEME_TAB 61
+
+// A single pane (inside the tabpanels container)
+#define NS_THEME_TABPANEL 62
+
+// The tab panels container.
+#define NS_THEME_TABPANELS 65
+
+// The tabs scroll arrows (left/right)
+#define NS_THEME_TAB_SCROLL_ARROW_BACK 66
+#define NS_THEME_TAB_SCROLL_ARROW_FORWARD 67
+
+// A tooltip
+#define NS_THEME_TOOLTIP 71
+
+// A spin control (up/down control for time/date pickers)
+#define NS_THEME_SPINNER 72
+
+// The up button of a spin control
+#define NS_THEME_SPINNER_UPBUTTON 73
+
+// The down button of a spin control
+#define NS_THEME_SPINNER_DOWNBUTTON 74
+
+// The textfield of a spin control
+#define NS_THEME_SPINNER_TEXTFIELD 75
+
+// For HTML's <input type=number>
+#define NS_THEME_NUMBER_INPUT 76
+
+// A scrollbar.
+#define NS_THEME_SCROLLBAR 80
+
+// A small scrollbar.
+#define NS_THEME_SCROLLBAR_SMALL 81
+
+// The scrollbar slider
+#define NS_THEME_SCROLLBAR_HORIZONTAL 82
+#define NS_THEME_SCROLLBAR_VERTICAL 83
+
+// A scrollbar button (up/down/left/right)
+#define NS_THEME_SCROLLBARBUTTON_UP 84
+#define NS_THEME_SCROLLBARBUTTON_DOWN 85
+#define NS_THEME_SCROLLBARBUTTON_LEFT 86
+#define NS_THEME_SCROLLBARBUTTON_RIGHT 87
+
+// The scrollbar track
+#define NS_THEME_SCROLLBARTRACK_HORIZONTAL 88
+#define NS_THEME_SCROLLBARTRACK_VERTICAL 89
+
+// The scrollbar thumb
+#define NS_THEME_SCROLLBARTHUMB_HORIZONTAL 90
+#define NS_THEME_SCROLLBARTHUMB_VERTICAL 91
+
+// A non-disappearing scrollbar.
+#define NS_THEME_SCROLLBAR_NON_DISAPPEARING 92
+
+// A textfield or text area
+#define NS_THEME_TEXTFIELD 95
+
+// The caret of a text area
+#define NS_THEME_CARET 96
+
+// A multiline text field
+#define NS_THEME_TEXTFIELD_MULTILINE 97
+
+// A searchfield
+#define NS_THEME_SEARCHFIELD 98
+
+// A dropdown list.
+#define NS_THEME_MENULIST 101
+
+// The dropdown button(s) that open up a dropdown list.
+#define NS_THEME_MENULIST_BUTTON 102
+
+// The text part of a dropdown list, to left of button
+#define NS_THEME_MENULIST_TEXT 103
+
+// An editable textfield with a dropdown list (a combobox)
+#define NS_THEME_MENULIST_TEXTFIELD 104
+
+// A slider
+#define NS_THEME_SCALE_HORIZONTAL 111
+#define NS_THEME_SCALE_VERTICAL 112
+
+// A slider's thumb
+#define NS_THEME_SCALETHUMB_HORIZONTAL 113
+#define NS_THEME_SCALETHUMB_VERTICAL 114
+
+// If the platform supports it, the left/right chunks
+// of the slider thumb
+#define NS_THEME_SCALETHUMBSTART 115
+#define NS_THEME_SCALETHUMBEND 116
+
+// The ticks for a slider.
+#define NS_THEME_SCALETHUMBTICK 117
+
+// nsRangeFrame and its subparts
+#define NS_THEME_RANGE 120
+#define NS_THEME_RANGE_THUMB 121
+
+// A groupbox
+#define NS_THEME_GROUPBOX 149
+
+// A generic container that always repaints on state
+// changes. This is a hack to make checkboxes and
+// radio buttons work.
+#define NS_THEME_CHECKBOX_CONTAINER 150
+#define NS_THEME_RADIO_CONTAINER 151
+
+// The label part of a checkbox or radio button, used for painting
+// a focus outline.
+#define NS_THEME_CHECKBOX_LABEL 152
+#define NS_THEME_RADIO_LABEL 153
+
+// The focus outline box inside of a button
+#define NS_THEME_BUTTON_FOCUS 154
+
+// Window and dialog backgrounds
+#define NS_THEME_WINDOW 200
+#define NS_THEME_DIALOG 201
+
+// Menu Bar background
+#define NS_THEME_MENUBAR 210
+// Menu Popup background
+#define NS_THEME_MENUPOPUP 211
+// <menu> and <menuitem> appearances
+#define NS_THEME_MENUITEM 212
+#define NS_THEME_CHECKMENUITEM 213
+#define NS_THEME_RADIOMENUITEM 214
+
+// menu checkbox/radio appearances
+#define NS_THEME_MENUCHECKBOX 215
+#define NS_THEME_MENURADIO 216
+#define NS_THEME_MENUSEPARATOR 217
+#define NS_THEME_MENUARROW 218
+// An image in the menu gutter, like in bookmarks or history
+#define NS_THEME_MENUIMAGE 219
+// For text on non-iconic menuitems only
+#define NS_THEME_MENUITEMTEXT 220
+
+// Vista Rebars
+#define NS_THEME_WIN_COMMUNICATIONS_TOOLBOX 221
+#define NS_THEME_WIN_MEDIA_TOOLBOX 222
+#define NS_THEME_WIN_BROWSERTABBAR_TOOLBOX 223
+
+// Titlebar elements on the Mac
+#define NS_THEME_MAC_FULLSCREEN_BUTTON 226
+
+// Mac help button
+#define NS_THEME_MAC_HELP_BUTTON 227
+
+// Vista glass
+#define NS_THEME_WIN_BORDERLESS_GLASS 229
+#define NS_THEME_WIN_GLASS 230
+
+// Windows themed window frame elements
+#define NS_THEME_WINDOW_TITLEBAR 231
+#define NS_THEME_WINDOW_TITLEBAR_MAXIMIZED 232
+#define NS_THEME_WINDOW_FRAME_LEFT 233
+#define NS_THEME_WINDOW_FRAME_RIGHT 234
+#define NS_THEME_WINDOW_FRAME_BOTTOM 235
+#define NS_THEME_WINDOW_BUTTON_CLOSE 236
+#define NS_THEME_WINDOW_BUTTON_MINIMIZE 237
+#define NS_THEME_WINDOW_BUTTON_MAXIMIZE 238
+#define NS_THEME_WINDOW_BUTTON_RESTORE 239
+#define NS_THEME_WINDOW_BUTTON_BOX 240
+#define NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED 241
+
+// moz-apperance style used in setting proper glass margins
+#define NS_THEME_WIN_EXCLUDE_GLASS 242
+
+#define NS_THEME_MAC_VIBRANCY_LIGHT 243
+#define NS_THEME_MAC_VIBRANCY_DARK 244
+#define NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN 245
+#define NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED 246
+
+#define NS_THEME_GTK_INFO_BAR 247
+#define NS_THEME_MAC_SOURCE_LIST 248
+#define NS_THEME_MAC_SOURCE_LIST_SELECTION 249
+#define NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION 250
diff --git a/system/graphics/src/nsTransform2D.cpp b/system/graphics/src/nsTransform2D.cpp
new file mode 100644
index 000000000..becea4392
--- /dev/null
+++ b/system/graphics/src/nsTransform2D.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsTransform2D.h"
+
+void nsTransform2D :: TransformCoord(nscoord *ptX, nscoord *ptY) const
+{
+ *ptX = NSToCoordRound(*ptX * m00 + m20);
+ *ptY = NSToCoordRound(*ptY * m11 + m21);
+}
+
+void nsTransform2D :: TransformCoord(nscoord *aX, nscoord *aY, nscoord *aWidth, nscoord *aHeight) const
+{
+ nscoord x2 = *aX + *aWidth;
+ nscoord y2 = *aY + *aHeight;
+ TransformCoord(aX, aY);
+ TransformCoord(&x2, &y2);
+ *aWidth = x2 - *aX;
+ *aHeight = y2 - *aY;
+}
diff --git a/system/graphics/src/nsTransform2D.h b/system/graphics/src/nsTransform2D.h
new file mode 100644
index 000000000..8999b2cf3
--- /dev/null
+++ b/system/graphics/src/nsTransform2D.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTransform2D_h___
+#define nsTransform2D_h___
+
+#include "nsCoord.h"
+
+class nsTransform2D
+{
+private:
+ /**
+ * This represents the following matrix (note that the order of row/column
+ * indices is opposite to usual notation)
+ *
+ * / m00 0 m20 \
+ * M = | 0 m11 m21 |
+ * \ 0 0 1 /
+ *
+ * Transformation of a coordinate (x, y) is obtained by setting
+ * v = (x, y, 1)^T and evaluating M . v
+ **/
+
+ float m00, m11, m20, m21;
+
+public:
+ nsTransform2D(void) { m20 = m21 = 0.0f; m00 = m11 = 1.0f; }
+
+ ~nsTransform2D(void) { }
+
+ /**
+ * set this transform to a translation
+ *
+ * @param tx, x translation
+ * @param ty, y translation
+ **/
+
+ void SetToTranslate(float tx, float ty) { m00 = m11 = 1.0f; m20 = tx; m21 = ty; }
+
+ /**
+ * get the translation portion of this transform
+ *
+ * @param pt, Point to return translation values in
+ **/
+
+ void GetTranslationCoord(nscoord *ptX, nscoord *ptY) const { *ptX = NSToCoordRound(m20); *ptY = NSToCoordRound(m21); }
+
+ /**
+ * apply matrix to vector
+ *
+ * @param pt Point to transform
+ **/
+
+ void TransformCoord(nscoord *ptX, nscoord *ptY) const;
+
+ /**
+ * apply matrix to rect
+ *
+ * @param rect Rect to transform
+ **/
+
+ void TransformCoord(nscoord *aX, nscoord *aY, nscoord *aWidth, nscoord *aHeight) const;
+
+ /**
+ * add a scale to a Transform via x, y pair
+ *
+ * @param ptX x value to add as x scale
+ * @param ptY y value to add as y scale
+ **/
+
+ void AddScale(float ptX, float ptY) { m00 *= ptX; m11 *= ptY; }
+
+ /**
+ * Set the scale (overriding any previous calls to AddScale, but leaving
+ * any existing translation).
+ *
+ * @param ptX x value to add as x scale
+ * @param ptY y value to add as y scale
+ **/
+
+ void SetScale(float ptX, float ptY) { m00 = ptX; m11 = ptY; }
+};
+
+#endif
diff --git a/system/graphics/tests/browser/browser.ini b/system/graphics/tests/browser/browser.ini
new file mode 100644
index 000000000..0a1902f0e
--- /dev/null
+++ b/system/graphics/tests/browser/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+support-files =
+
+[browser_windowless_troubleshoot_crash.js]
diff --git a/system/graphics/tests/browser/browser_windowless_troubleshoot_crash.js b/system/graphics/tests/browser/browser_windowless_troubleshoot_crash.js
new file mode 100644
index 000000000..f78a91c72
--- /dev/null
+++ b/system/graphics/tests/browser/browser_windowless_troubleshoot_crash.js
@@ -0,0 +1,45 @@
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+add_task(function* test_windowlessBrowserTroubleshootCrash() {
+ let webNav = Services.appShell.createWindowlessBrowser(false);
+
+ let onLoaded = new Promise((resolve, reject) => {
+ let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell);
+ let listener = {
+ observe(contentWindow, topic, data) {
+ let observedDocShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .sameTypeRootTreeItem
+ .QueryInterface(Ci.nsIDocShell);
+ if (docShell === observedDocShell) {
+ Services.obs.removeObserver(listener, "content-document-global-created", false);
+ resolve();
+ }
+ }
+ }
+ Services.obs.addObserver(listener, "content-document-global-created", false);
+ });
+ webNav.loadURI("about:blank", 0, null, null, null);
+
+ yield onLoaded;
+
+ let winUtils = webNav.document.defaultView.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+ is(winUtils.layerManagerType, "None", "windowless browser's layerManagerType should be 'None'");
+
+ ok(true, "not crashed");
+
+ var Troubleshoot = Cu.import("resource://gre/modules/Troubleshoot.jsm", {}).Troubleshoot;
+ var data = yield new Promise((resolve, reject) => {
+ Troubleshoot.snapshot((data) => {
+ resolve(data);
+ });
+ });
+
+ ok(data.graphics.windowLayerManagerType !== "None", "windowless browser window should not set windowLayerManagerType to 'None'");
+
+ webNav.close();
+});
diff --git a/system/graphics/tests/crashtests/1034403-1.html b/system/graphics/tests/crashtests/1034403-1.html
new file mode 100644
index 000000000..714994d3e
--- /dev/null
+++ b/system/graphics/tests/crashtests/1034403-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body style="font-variant: small-caps; font-family: 'Times New Roman';">
+<div>x&#xE0131;</div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/1134549-1.svg b/system/graphics/tests/crashtests/1134549-1.svg
new file mode 100644
index 000000000..1d0d5484a
--- /dev/null
+++ b/system/graphics/tests/crashtests/1134549-1.svg
@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400" height="400"
+ viewBox="694400 -179730 8000 8000">
+
+ <defs>
+ <path id="tp_crash" d="M 698430.938,-174861.922 C 699068.125,-175493.781 699593.562,-176022.531 699499,-177727" />
+ </defs>
+
+ <text font-size="4000">
+ <textPath xlink:href="#tp_crash">Eisack</textPath>
+ </text>
+ <use xlink:href="#tp_crash" fill="none" stroke="green" stroke-width="200"></use>
+</svg>
diff --git a/system/graphics/tests/crashtests/1205900.html b/system/graphics/tests/crashtests/1205900.html
new file mode 100644
index 000000000..5e1f47cae
--- /dev/null
+++ b/system/graphics/tests/crashtests/1205900.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body>
+<canvas id="canvas0"></canvas>
+
+<script>
+ var canvas0=document.getElementById("canvas0");
+ var ctx=canvas0.getContext("2d");
+ canvas0.addEventListener("DOMAttrModified",
+ function(event) {
+ canvas0.toBlob(function(){},"image/jpeg",1);
+ }, true);
+ canvas0.setAttribute("height",470)
+</script>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/1216832-1.html b/system/graphics/tests/crashtests/1216832-1.html
new file mode 100644
index 000000000..0f02a9280
--- /dev/null
+++ b/system/graphics/tests/crashtests/1216832-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+</head>
+<body>
+ <div style="transform-style: preserve-3d; border-bottom-style: outset; width: 4787550px;">
+ <div style="margin-left: 4787550px;"></div>
+ <div style="will-change: contents, transform;"></div>
+ </div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/1225125-1.html b/system/graphics/tests/crashtests/1225125-1.html
new file mode 100644
index 000000000..6632dff28
--- /dev/null
+++ b/system/graphics/tests/crashtests/1225125-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="mix-blend-mode: saturation; margin-right: 1187px; transform: translateX(2px);">
+ <div style="border-style: outset; mix-blend-mode: color-dodge; display: inherit; padding: 1760px; float: right;"></div>
+ <div style="height: 2000px; overflow: auto;"></div>
+</div>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/122875-1.html b/system/graphics/tests/crashtests/122875-1.html
new file mode 100644
index 000000000..6241666b0
--- /dev/null
+++ b/system/graphics/tests/crashtests/122875-1.html
@@ -0,0 +1 @@
+<html> <head> <meta http-equiv="content-type" content="text/html; charset=Shift_JIS"> </head> <body> @@ </body> </html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/1308394.html b/system/graphics/tests/crashtests/1308394.html
new file mode 100644
index 000000000..fd7570801
--- /dev/null
+++ b/system/graphics/tests/crashtests/1308394.html
@@ -0,0 +1,23 @@
+<html class="reftest-print"><head>
+<svg xmlns="http://www.w3.org/2000/svg" width="90" height="20">
+ <linearGradient id="b" x2="0" y2="100%">
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+ <stop offset="1" stop-opacity=".1"/>
+ </linearGradient>
+ <mask id="a">
+ <rect width="90" height="20" rx="3" fill="#fff"/>
+ </mask>
+ <g mask="url(#a)">
+ <path fill="#555" d="M0 0h33v20H0z"/>
+ <path fill="#007ec6" d="M33 0h57v20H33z"/>
+ <path fill="url(#b)" d="M0 0h90v20H0z"/>
+ </g>
+ <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
+ <text x="16.5" y="15" fill="#010101" fill-opacity=".3">AUR</text>
+ <text x="16.5" y="14">AUR</text>
+ <text x="60.5" y="15" fill="#010101" fill-opacity=".3">v2.0.4-1</text>
+ <text x="60.5" y="14">v2.0.4-1</text>
+ </g>
+</svg>
+</head>
+<body>
diff --git a/system/graphics/tests/crashtests/1317403-1.html b/system/graphics/tests/crashtests/1317403-1.html
new file mode 100644
index 000000000..1a972c0e1
--- /dev/null
+++ b/system/graphics/tests/crashtests/1317403-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<style>
+#o {
+ padding: 27660vw;
+ outline: thick dotted;
+ mask: subtract url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=), linear-gradient(#FFF, #555);
+}
+</style>
+<script>
+document.addEventListener("DOMContentLoaded", function(){
+ o.appendChild(document.createElement("div"));
+ o.appendChild(document.createElement("frame"));
+ o.appendChild(document.createElement("div"));
+});
+</script>
+<span id=o />
+</html>
diff --git a/system/graphics/tests/crashtests/1325159-1.html b/system/graphics/tests/crashtests/1325159-1.html
new file mode 100644
index 000000000..2441e9f2f
--- /dev/null
+++ b/system/graphics/tests/crashtests/1325159-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+#o_1 {
+ position: absolute;
+ top: 0px;
+ width: 100px;
+ height: 100px;
+ background: red;
+}
+#o_2 {
+ height: 10000px;
+}
+</style>
+<script>
+function boom(){
+ let doc = document.documentElement;
+ o_2.style.MozBorderEndStyle = "dotted";
+ doc.style.MozPerspective = "24.5pt";
+ o_0.style.MozTransformStyle = "preserve-3d";
+ doc.style.overflow = "-moz-scrollbars-horizontal";
+ doc.style.textOverflow = "''";
+ o_0.style.offsetInlineStart = "calc(3*25px)";
+ doc.style.paddingTop = "calc(67108864%)";
+ doc.style.width = "3e-0%";
+ o_0.style.display = "-moz-stack";
+ o_0.style.position = "relative";
+}
+addEventListener("DOMContentLoaded", boom);
+</script>
+</head>
+<body id=o_0><div id=o_1></div><div id=o_2></div></body>
+</html>
diff --git a/system/graphics/tests/crashtests/156882-1.html b/system/graphics/tests/crashtests/156882-1.html
new file mode 100644
index 000000000..c861e0a83
--- /dev/null
+++ b/system/graphics/tests/crashtests/156882-1.html
@@ -0,0 +1,205 @@
+<HTML>
+<HEAD>
+<META http-equiv="Content-Type" content="charset=x-user-defined">
+<TITLE>Testcase</TITLE>
+</HEAD>
+<BODY>
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/157320-1.html b/system/graphics/tests/crashtests/157320-1.html
new file mode 100644
index 000000000..77119b6aa
--- /dev/null
+++ b/system/graphics/tests/crashtests/157320-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title>foobidu</title>
+<meta http-equiv="Content-Type" content="text/html; charset=big5">
+
+</head>
+<body bgcolor="#C2C2C2" text="#000000" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" >
+è° <!-- mozilla doesn't like that! //-->
+°è <!-- but this works!!! //-->
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/199379-1.html b/system/graphics/tests/crashtests/199379-1.html
new file mode 100644
index 000000000..ba4afe0b5
--- /dev/null
+++ b/system/graphics/tests/crashtests/199379-1.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>bug 199379</title>
+ </head>
+ <body>
+ <form>
+ <input style="font-size:1000; width:32; height:34;" TYPE="SUBMIT" VALUE="link=basket&prod=96&tariff_id=999&desc=Fast Gate BASIC (ISDN 64K) &tariff=74,82&cp_id=5555&rating_tariff_id=0&parent_tariff_id=978&parent_product_id=&sItemDesc=Fast Gate BASIC (ISDN 64K) &package_id=0">
+ </form>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/206561-1.html b/system/graphics/tests/crashtests/206561-1.html
new file mode 100644
index 000000000..f17cf1121
--- /dev/null
+++ b/system/graphics/tests/crashtests/206561-1.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>bug 206561</title>
+ </head>
+ <body>
+ <div style="height: 100%; opacity: 0.8;"></div>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/248518-1.html b/system/graphics/tests/crashtests/248518-1.html
new file mode 100644
index 000000000..b15c78612
--- /dev/null
+++ b/system/graphics/tests/crashtests/248518-1.html
@@ -0,0 +1,7 @@
+<html><head>
+ <style type="text/css">
+ <!--
+ body{font: 10pt lucida;}
+ -->
+ </style>
+</head><body>Hello.</body></html>
diff --git a/system/graphics/tests/crashtests/306649-1.xml b/system/graphics/tests/crashtests/306649-1.xml
new file mode 100644
index 000000000..c9861807c
--- /dev/null
+++ b/system/graphics/tests/crashtests/306649-1.xml
@@ -0,0 +1 @@
+<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg"> <g transform="scale(1e10)"> <rect x="0" y="0" width="400" height="200" rx="50" ry="50" fill="none" stroke="purple" stroke-width="30"/> </g> </svg> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/306902-1.xml b/system/graphics/tests/crashtests/306902-1.xml
new file mode 100644
index 000000000..24e8c068a
--- /dev/null
+++ b/system/graphics/tests/crashtests/306902-1.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0'?>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<body>
+
+<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'><msup>
+
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+</msup></math>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/333861-1.html b/system/graphics/tests/crashtests/333861-1.html
new file mode 100644
index 000000000..694b06b3b
--- /dev/null
+++ b/system/graphics/tests/crashtests/333861-1.html
@@ -0,0 +1,18 @@
+<html><head>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!--
+Copyright Georgi Guninski
+-->
+
+ <style>
+ button
+ {
+ font-size: 131131px;
+ }
+ </style>
+ </head><body>
+
+ <button>f00</button>
+</body></html>
diff --git a/system/graphics/tests/crashtests/334735-1.html b/system/graphics/tests/crashtests/334735-1.html
new file mode 100644
index 000000000..f1a2c04a5
--- /dev/null
+++ b/system/graphics/tests/crashtests/334735-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>demo 1</title>
+</head><body>
+
+These unusual characters, &#1048713;,
+cause Firefox to crash &#x100089;
+
+
+</body></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/345576-1.html b/system/graphics/tests/crashtests/345576-1.html
new file mode 100644
index 000000000..547d4606c
--- /dev/null
+++ b/system/graphics/tests/crashtests/345576-1.html
@@ -0,0 +1,6 @@
+<html>
+<head>
+</head>
+<body>
+<font
+font-weight=5555555555555555555555555555555555555555555555555555555555555555555555 \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/345629-1.html b/system/graphics/tests/crashtests/345629-1.html
new file mode 100644
index 000000000..0706b9d0d
--- /dev/null
+++ b/system/graphics/tests/crashtests/345629-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<font font-weight="467591">x</font>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/348462-1.html b/system/graphics/tests/crashtests/348462-1.html
new file mode 100644
index 000000000..e49b89158
--- /dev/null
+++ b/system/graphics/tests/crashtests/348462-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<style>.classname { font-size: 2666.3423423423423424 }</style>
+<title>bla crash</title>
+</head>
+
+<body>
+<p class="classname">crash!</p>
+</body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/348462-2.html b/system/graphics/tests/crashtests/348462-2.html
new file mode 100644
index 000000000..5efbaa5b0
--- /dev/null
+++ b/system/graphics/tests/crashtests/348462-2.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom() {
+ document.body.appendChild(document.createTextNode(String.fromCharCode(25261)))
+ document.body.style.fontSize = '8388608px';
+}
+
+</script>
+</head>
+<body onload="boom()"></body>
+</html>
diff --git a/system/graphics/tests/crashtests/366643.html b/system/graphics/tests/crashtests/366643.html
new file mode 100644
index 000000000..b8ce73a1f
--- /dev/null
+++ b/system/graphics/tests/crashtests/366643.html
@@ -0,0 +1,7 @@
+<html><head>
+ <title>Uniscribe::Itemize crash</title>
+</head><body>
+
+x.&#8205;x.&#8205;x
+
+</body></html>
diff --git a/system/graphics/tests/crashtests/369688-1.html b/system/graphics/tests/crashtests/369688-1.html
new file mode 100644
index 000000000..f71c8b091
--- /dev/null
+++ b/system/graphics/tests/crashtests/369688-1.html
@@ -0,0 +1,19 @@
+<html class="reftest-wait">
+
+<head>
+<script>
+function boom()
+{
+ document.body.style.fontSizeAdjust = 0xffffffff;
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+
+<body onload="setTimeout(boom, 30);">
+
+<p>Foo</p>
+
+</body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/369947-1.html b/system/graphics/tests/crashtests/369947-1.html
new file mode 100644
index 000000000..efb0aed9c
--- /dev/null
+++ b/system/graphics/tests/crashtests/369947-1.html
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+</head>
+
+<body>
+
+<pre>bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar &rho; foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo </pre>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/372094-1.xhtml b/system/graphics/tests/crashtests/372094-1.xhtml
new file mode 100644
index 000000000..714e510af
--- /dev/null
+++ b/system/graphics/tests/crashtests/372094-1.xhtml
@@ -0,0 +1,45 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+<![CDATA[
+
+function init()
+{
+ setTimeout(function()
+ {
+ targetWindow = window.frames[0];
+ targetDocument = targetWindow.document;
+ targetDocument.body.appendChild(targetDocument.importNode(document.getElementById('rootish'), true));
+ targetDocument.designMode = 'on';
+ setTimeout(boom, 30);
+ }, 30);
+}
+
+function boom()
+{
+ var r = targetDocument.createRange();
+ r.setStart(targetDocument.getElementById("bar"), 0);
+ r.setEnd(targetDocument.getElementById("baz").firstChild, 0);
+ targetWindow.getSelection().addRange(r);
+
+ targetDocument.execCommand("indent", false, null);
+
+ document.documentElement.removeAttribute("class");
+}
+
+]]>
+</script>
+</head>
+
+<body onload="init()">
+
+<iframe src="data:text/html," style="width: 95%; height: 500px;"/>
+
+<div id="rootish">
+ <div>Foo</div>
+ <div id="bar">Bar</div>
+ <div><select><option id="baz">baz</option></select></div>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/376627-1.html b/system/graphics/tests/crashtests/376627-1.html
new file mode 100644
index 000000000..44319efed
--- /dev/null
+++ b/system/graphics/tests/crashtests/376627-1.html
@@ -0,0 +1,3 @@
+<html>
+<body>
+&#01;
diff --git a/system/graphics/tests/crashtests/377231-1.html b/system/graphics/tests/crashtests/377231-1.html
new file mode 100644
index 000000000..c3e52284c
--- /dev/null
+++ b/system/graphics/tests/crashtests/377231-1.html
@@ -0,0 +1 @@
+<div><span>&#1741;</span><span>&#8232;</span><span>&#1994;</span></div>
diff --git a/system/graphics/tests/crashtests/377232-1.xhtml b/system/graphics/tests/crashtests/377232-1.xhtml
new file mode 100644
index 000000000..4ab81eca0
--- /dev/null
+++ b/system/graphics/tests/crashtests/377232-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="font-family: arial">
+<div>&#1050;&#769;</div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/377461-1.xhtml b/system/graphics/tests/crashtests/377461-1.xhtml
new file mode 100644
index 000000000..adeaaaccf
--- /dev/null
+++ b/system/graphics/tests/crashtests/377461-1.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.textContent = "\uFDDE\r\uFDDE";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<div id="div"></div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/383473-1.html b/system/graphics/tests/crashtests/383473-1.html
new file mode 100644
index 000000000..12d6d0f5b
--- /dev/null
+++ b/system/graphics/tests/crashtests/383473-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<span style="font-size:72px; font-size-adjust:24">X</span>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/383872-1.svg b/system/graphics/tests/crashtests/383872-1.svg
new file mode 100644
index 000000000..2d32753f7
--- /dev/null
+++ b/system/graphics/tests/crashtests/383872-1.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<html:style>
+
+#para {
+ width: 30px;
+ height: 0.5px;
+ background: url("data:image/gif;base64,R0lGODlhMQA8ANX%2FAJ22hRgcFI6keHqNZzpDMZ%2B2hqO4iR8jGpythSgsIa28kwoLCGJrULzCoFBQPsfFqb20hRMRDMq4is62hbeidjMuJNC3iJ%2BMaIJyVdK6jdi%2Fm6KUgNfJtSYhGkM5LsetluvGtebLvq2PiPHLwsDAwPbMx7%2BbmdKpqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACQALAAAAAAxADwAAAb%2FQJJwSCwaj8TSqIRsOkmlKNNZCj0UBoPiEZo%2BjVUO5%2FFoNB6cUGgJJYYaBoAcUDCcQ99hiQMvzP91CmYNHEwjDwZ%2Bf3J1HHkkHApzA5SVAweYBwQMCGdwAgwVCaOjFQwABnhPIw1%2BAwkEsbEHC6dyA7F1BaAMAosCDg4ADV8ccbgDiwnJiw62zouTCQAKXkclDaAEzH%2FL0QMOoguk24soqCNOIwoM5dHei%2B22kwTm6E4hAvDR%2FJfc3fYUpGsSAhq%2Fg7gOTvtzTkEXggsP8hPw748wAJXO2SmEJEREiSAPOiCwYFsdA46OeAzJEiSyLCmLHPrYsuYcZzBlRjLw0UFF%2F5sICTRKgkhOxEu%2BgIbENXSIsTmmfAVTypIpyiHr5DhAwVXcT6qLEiqIGeJYvW7zwPJjcNGhkLIANoUlkBSjWgan8CYt8EAIBzkH6s5hm%2FSAWgEBEjgQgIJZNUiARRoGEEAwUMa3ygmEDGByPzls1QKAlzAVJD%2BeQV4Eu8%2FwVbipRUvEPMcwXxJZ3ck%2BCCzt5AZRGgBgQHP3n9B%2FbG9%2BOqCycXM0BSjHUwJRAQYHAmD6%2BrwdHbe4r%2FjR%2FfyXu81C9kgiXx60dzrEiBZg3z4WM9NFytIH4JNRgf8A1qRbfDIpsF85BkCg4IJZKPIHRXMIQBcqDQxkxAOERbONARtAMP%2FBhx9aIAEKE8oBTIl4afGAhUaEYOBqtQmggDMg1ojCVKAFk4kDCHDABj4IjMTNKwloQIEHNYKIggQJkGhfFiqy%2BISL7WQnSwUaTFBBkh%2BiMIEGDWywgQlqqCFlHmFcIeYGHTBwwZZcennmI1R4cYIIb3I5gZd09nmElnry6eegQgAapwWEEmpokihkkOigi9qYwZyPNhGpkpNW%2BsgIRwZqAaWaFqEBXoFOAEKoX1DgAAWlnopqEyBg4ACIFtRYwQUavHrECCCoesGHF9R66wRvWuDqoCcMdIIJzDbL7AUOYPAhBB182EECrLJ1gasjNHvCF6FUUEEw5JLrQQSzTiCRa7odYIDkBB5cK%2B64wVTgwb34eoBBHrzCGwGxFUTwLrzQ%2FkpBBRiAYM2jxHXQgb72YvAmq%2Bo6AC2rBX84AqiriJCAh9B6cEEGGViAQQK%2FQiDyyfe66eGHmfqZpQYlW6wBG7y%2BeauswF4gKwrzTmBBrroKMYIFFyQgsdAhZqDBmhpocGzRJIDwQQdOU%2B2nCZUGAQA7");
+}
+
+</html:style>
+
+<foreignObject width="100" height="100" x="100" y="100" transform="scale(.7,.7)">
+ <html:div>
+ <html:p id="para">Foo</html:p>
+ </html:div>
+</foreignObject>
+
+</svg>
diff --git a/system/graphics/tests/crashtests/385228-1.svg b/system/graphics/tests/crashtests/385228-1.svg
new file mode 100644
index 000000000..ec448b5b3
--- /dev/null
+++ b/system/graphics/tests/crashtests/385228-1.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 10);" class="reftest-wait">
+
+<script type="text/javascript">
+
+function boom()
+{
+ var ttt = document.getElementById("ttt");
+ ttt.appendChild(document.createTextNode("Pattern on stroke"));
+ ttt.appendChild(document.createTextNode("\n"));
+ ttt.appendChild(document.createTextNode("\n"));
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+
+<text id="ttt" stroke="url(#pat2)" x="25" y="275"></text>
+
+<pattern id="pat2" width="20" height="1000"><g/></pattern>
+
+</svg>
diff --git a/system/graphics/tests/crashtests/385228-2.svg b/system/graphics/tests/crashtests/385228-2.svg
new file mode 100644
index 000000000..82586fcfa
--- /dev/null
+++ b/system/graphics/tests/crashtests/385228-2.svg
@@ -0,0 +1,20 @@
+<svg version="1.1" baseProfile="basic" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 480 360" onload="yaa();" class="reftest-wait">
+
+<g id="g">
+ <rect width="440" height="340" fill="url(#pat1)"/>
+ <pattern id="pat1" width="20" height="20"><rect/></pattern>
+</g>
+
+<script type="text/javascript">
+
+function yaa()
+{
+ var g = document.getElementById("g");
+ document.documentElement.removeChild(g);
+ document.documentElement.appendChild(g);
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</svg>
diff --git a/system/graphics/tests/crashtests/385289-1.xhtml b/system/graphics/tests/crashtests/385289-1.xhtml
new file mode 100644
index 000000000..666756c01
--- /dev/null
+++ b/system/graphics/tests/crashtests/385289-1.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var mss = document.getElementById("mss");
+
+ var j = document.createTextNode("j");
+ var comb = document.createTextNode("\u0302");
+
+ mss.appendChild(j);
+ mss.appendChild(comb);
+}
+</script>
+</head>
+
+<body onload="boom()">
+
+<div>
+ <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
+ <msub id="mss">
+ <mi>v</mi>
+ <mn>1</mn>
+ </msub>
+ </math>
+</div>
+
+</body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/385417-1.html b/system/graphics/tests/crashtests/385417-1.html
new file mode 100644
index 000000000..0bf70ec34
--- /dev/null
+++ b/system/graphics/tests/crashtests/385417-1.html
@@ -0,0 +1 @@
+<html><body>&#x22A3;&zwnj;</body></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/385417-2.html b/system/graphics/tests/crashtests/385417-2.html
new file mode 100644
index 000000000..f94f21e69
--- /dev/null
+++ b/system/graphics/tests/crashtests/385417-2.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+
+<div><span style="text-transform: capitalize; font-variant: small-caps;">
+x</span></div>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/385423-1.html b/system/graphics/tests/crashtests/385423-1.html
new file mode 100644
index 000000000..748776c30
--- /dev/null
+++ b/system/graphics/tests/crashtests/385423-1.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div")
+ div.appendChild(document.createTextNode(String.fromCharCode(0x076F) + String.fromCharCode(13) + String.fromCharCode(0x076F)));
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+<body onload="setTimeout(boom, 10);">
+<div style="text-transform: lowercase" id="div">&#x76F;&#13;&#x76F;</div>
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/385423-2.html b/system/graphics/tests/crashtests/385423-2.html
new file mode 100644
index 000000000..7de8bdaaf
--- /dev/null
+++ b/system/graphics/tests/crashtests/385423-2.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div")
+ div.appendChild(document.createTextNode(String.fromCharCode(0x076F) + String.fromCharCode(13) + String.fromCharCode(0x076F)));
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+<body onload="setTimeout(boom, 10);">
+<div style="text-transform: lowercase" id="div"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/385719-1.html b/system/graphics/tests/crashtests/385719-1.html
new file mode 100644
index 000000000..3081dcc04
--- /dev/null
+++ b/system/graphics/tests/crashtests/385719-1.html
@@ -0,0 +1 @@
+<div>-<span>&zwj;</span>&#xB235;</div> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/389326-1-inner.xhtml b/system/graphics/tests/crashtests/389326-1-inner.xhtml
new file mode 100644
index 000000000..30236cf8b
--- /dev/null
+++ b/system/graphics/tests/crashtests/389326-1-inner.xhtml
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+function boom()
+{
+ var f = document.getElementById("f");
+ var s = document.getElementById("s");
+ s.insertBefore(f, s.firstChild);
+
+ setTimeout(rel, 200);
+}
+
+function rel()
+{
+ location.reload();
+}
+
+]]>
+</script>
+
+</head>
+
+<body onload="boom();">
+<font id="f"><b>2</b>"</font>
+<b>"<span id="s">="<b></b></span></b>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/389326-1.html b/system/graphics/tests/crashtests/389326-1.html
new file mode 100644
index 000000000..2d9451596
--- /dev/null
+++ b/system/graphics/tests/crashtests/389326-1.html
@@ -0,0 +1,9 @@
+<html class="reftest-wait">
+<head>
+<script>
+setTimeout('document.documentElement.className = ""', 1000);
+</script>
+<body>
+<iframe src="389326-1-inner.xhtml"></iframe>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/390476.html b/system/graphics/tests/crashtests/390476.html
new file mode 100644
index 000000000..069369fe7
--- /dev/null
+++ b/system/graphics/tests/crashtests/390476.html
@@ -0,0 +1,13 @@
+<html><head>
+<title>Crash [@ _moz_cairo_win32_scaled_font_select_font] with negative font-size in canvas</title>
+<script>
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.translate(100, 100);
+ ctx.mozTextStyle = "-14pt sans serif";
+ ctx.mozDrawText('text');
+</script>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/393746-1.xhtml b/system/graphics/tests/crashtests/393746-1.xhtml
new file mode 100644
index 000000000..feba6a3c0
--- /dev/null
+++ b/system/graphics/tests/crashtests/393746-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var text = document.createTextNode("t");
+ document.getElementById("foo").appendChild(text);
+}
+</script>
+</head>
+
+<body onload="boom();" dir="rtl"><b id="foo"></b> x </body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/393749-1.html b/system/graphics/tests/crashtests/393749-1.html
new file mode 100644
index 000000000..edaba0eb2
--- /dev/null
+++ b/system/graphics/tests/crashtests/393749-1.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ var het = document.createTextNode(String.fromCharCode(0x05D7)); // hebrew letter het
+ document.getElementById("s").appendChild(het);
+}
+</script>
+</head>
+
+<body onload="boom();" dir="rtl">
+
+<div><span id="s">A</span><span>&nbsp;</span>B</div>
+
+</body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/393822-1.html b/system/graphics/tests/crashtests/393822-1.html
new file mode 100644
index 000000000..e763632df
--- /dev/null
+++ b/system/graphics/tests/crashtests/393822-1.html
@@ -0,0 +1,32 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+
+var ff;
+var jj;
+
+function boom()
+{
+ ff = document.createElement("font");
+ ff.setAttribute("size", "-2");
+ ff.appendChild(document.createTextNode("G"));
+
+ jj = document.getElementById("jj");
+
+ jj.appendChild(ff);
+ setTimeout(boom2, 30);
+}
+
+function boom2()
+{
+ jj.removeChild(ff);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();" dir="rtl"><div id="jj">h </div></body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/394246-1.html b/system/graphics/tests/crashtests/394246-1.html
new file mode 100644
index 000000000..e09facb8b
--- /dev/null
+++ b/system/graphics/tests/crashtests/394246-1.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.innerHTML = div.innerHTML.slice(1);
+}
+</script>
+</head>
+<body onload="boom();">
+
+<div id="div">t
+&#769;</div>
+
+</body></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/394246-2.html b/system/graphics/tests/crashtests/394246-2.html
new file mode 100644
index 000000000..e5fe8c175
--- /dev/null
+++ b/system/graphics/tests/crashtests/394246-2.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.innerHTML = div.innerHTML.slice(1);
+ setTimeout(c, 30);
+}
+
+function c()
+{
+ div.innerHTML = div.innerHTML.slice(1);
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="boom();">
+
+<div id="div">t
+&#769;</div>
+
+</body></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/394384-1.html b/system/graphics/tests/crashtests/394384-1.html
new file mode 100644
index 000000000..279c473a0
--- /dev/null
+++ b/system/graphics/tests/crashtests/394384-1.html
@@ -0,0 +1,26 @@
+<html class="reftest-wait">
+<head>
+<script>
+
+function boom()
+{
+ var t1 = document.createTextNode(String.fromCharCode(0x2011) + String.fromCharCode(13));
+ document.body.appendChild(t1);
+
+ setTimeout(boom2, 30);
+}
+
+function boom2()
+{
+ var letterA = document.createTextNode(String.fromCharCode(65));
+ document.body.appendChild(letterA);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(boom, 50);"></body>
+
+</html>
diff --git a/system/graphics/tests/crashtests/394751.xhtml b/system/graphics/tests/crashtests/394751.xhtml
new file mode 100644
index 000000000..35a65af09
--- /dev/null
+++ b/system/graphics/tests/crashtests/394751.xhtml
@@ -0,0 +1,3 @@
+<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">XML Parsing Error: not well-formed
+Location: file:///C:/Documents%20and%20Settings/mw/Bureaublad/crashzone/crash1.xhtml
+Line Number 3, Column 42964:<sourcetext>Line Number 3, Column 42134:&lt;sourcetext&gt;Line Number 3, Column 41312:&amp;lt;sourcetext&amp;gt;Line Number 3, Column 40498:&amp;amp;lt;sourcetext&amp;amp;gt;Line Number 3, Column 39692:&amp;amp;amp;lt;sourcetext&amp;amp;amp;gt;Line Number 3, Column 38894:&amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;gt;Line Number 3, Column 38104:&amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;gt;Line Number 3, Column 37322:&amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 36548:&amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 35782:&amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 35024:&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 34274:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 33532:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 32798:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 32072:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 31354:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 30644:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 29942:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 29248:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 28562:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 27884:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 27214:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 26552:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 25898:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 25252:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 24614:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 23984:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 23362:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 22748:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 22142:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 21544:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 20954:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 20372:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 19798:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 19232:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 18674:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 18124:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 17582:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 17048:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 16522:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 16004:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 15494:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14992:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14498:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14012:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 13534:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 13064:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 12602:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 12148:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 11702:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 11264:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 10834:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 10412:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9999:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9594:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9197:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8808:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8427:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8054:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 7689:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 7332:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6983:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6642:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6309:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5984:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5667:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5358:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5057:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4764:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4479:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4202:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3933:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3672:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3419:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3174:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2937:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2708:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2487:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2274:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2069:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1872:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1683:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1502:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1329:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1164:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1007:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 859:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 1, Column 743:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:wairole="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#" xmlns:aaa="http://www.w3.org/2005/07/aaa" xmlns:xforms="http://www.w3.org/2002/xforms"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;9&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;svg:foreignObject x="0" y="0" width="100%" height="100%"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;wbr style="" id="a" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;ý&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;area style="" id="b" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/area&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;marquee style="" id="b" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;ý&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/marquee&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/wbr&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/svg:foreignObject&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;script style="" id="a" name="c"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;^&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;hx style="" id="b" name="c"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;ý&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/hx&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a style="" id="b" name="d"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;aa </sourcetext></parsererror> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/395335-1.xhtml b/system/graphics/tests/crashtests/395335-1.xhtml
new file mode 100644
index 000000000..d9b73edc1
--- /dev/null
+++ b/system/graphics/tests/crashtests/395335-1.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("tree").firstChild.data = "xyz";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<xul:tree id="tree">a</xul:tree>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/395458-1.html b/system/graphics/tests/crashtests/395458-1.html
new file mode 100644
index 000000000..80fc55178
--- /dev/null
+++ b/system/graphics/tests/crashtests/395458-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="direction: rtl;">
+&#30;
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/396321-1.svg b/system/graphics/tests/crashtests/396321-1.svg
new file mode 100644
index 000000000..603292134
--- /dev/null
+++ b/system/graphics/tests/crashtests/396321-1.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<text>&#x202B;x</text>
+
+</svg>
diff --git a/system/graphics/tests/crashtests/398042-1.xhtml b/system/graphics/tests/crashtests/398042-1.xhtml
new file mode 100644
index 000000000..33c18a605
--- /dev/null
+++ b/system/graphics/tests/crashtests/398042-1.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style id="s">
+maligngroup { white-space: pre; }
+</style>
+</head>
+
+<body onload="document.getElementById('s').disabled = true;">
+<ms xmlns="http://www.w3.org/1998/Math/MathML"><maligngroup>
+
+ </maligngroup></ms>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/398042-2.xhtml b/system/graphics/tests/crashtests/398042-2.xhtml
new file mode 100644
index 000000000..49b40673a
--- /dev/null
+++ b/system/graphics/tests/crashtests/398042-2.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style id="s">
+maligngroup { white-space: pre; }
+</style>
+</head>
+
+<body onload="document.getElementById('s').disabled = true;">
+<ms xmlns="http://www.w3.org/1998/Math/MathML"><maligngroup>
+
+</maligngroup></ms>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/402307-1.html b/system/graphics/tests/crashtests/402307-1.html
new file mode 100644
index 000000000..0f9c2941d
--- /dev/null
+++ b/system/graphics/tests/crashtests/402307-1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body style="font-size: 15000px; word-spacing: 10px;">
+
+ABCDE&#xA2C9;
+&zwj;&#x9BB8;&#x9143;
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/403464-1.html b/system/graphics/tests/crashtests/403464-1.html
new file mode 100644
index 000000000..459486539
--- /dev/null
+++ b/system/graphics/tests/crashtests/403464-1.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript">
+</script>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
+<style type="text/css">
+body { margin: 0 4em; font-family: sans-serif; font-size:xx-large;}
+#content { width: 800px; margin: 0 auto; padding: 10em 0;}
+</style>
+
+</head>
+<body>
+
+<div id="content">
+
+uÌ€Ì̂̃̄̅̆̇̈̉̊̋̌ÌÌŽÌÌ̛̖̗̘̙̜̑̒̓̔̕̚Ì̴̵̶̷̸̡̢̧̨̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼̽̾̿̀Í͇͈͉͂̓̈́͆͊͋͌ͅÍÍŽÍÍ͓͔͕͖͙͚͑͒͗͛͘͜Íͣ͟͢͞͠͡
+</div><!-- #content -->
+
+
+<pre>
+u LATIN SMALL LETTER U
+Ì€ COMBINING GRAVE ACCENT
+Ì COMBINING ACUTE ACCENT
+Ì‚ COMBINING CIRCUMFLEX ACCENT
+̃ COMBINING TILDE
+Ì„ COMBINING MACRON
+Ì… COMBINING OVERLINE
+̆ COMBINING BREVE
+̇ COMBINING DOT ABOVE
+̈ COMBINING DIAERESIS
+̉ COMBINING HOOK ABOVE
+ÌŠ COMBINING RING ABOVE
+Ì‹ COMBINING DOUBLE ACUTE ACCENT
+̌ COMBINING CARON
+Ì COMBINING VERTICAL LINE ABOVE
+ÌŽ COMBINING DOUBLE VERTICAL LINE ABOVE
+Ì COMBINING DOUBLE GRAVE ACCENT
+Ì COMBINING CANDRABINDU
+Ì‘ COMBINING INVERTED BREVE
+Ì’ COMBINING TURNED COMMA ABOVE
+Ì“ COMBINING COMMA ABOVE
+Ì” COMBINING REVERSED COMMA ABOVE
+Ì• COMBINING COMMA ABOVE RIGHT
+Ì– COMBINING GRAVE ACCENT BELOW
+Ì— COMBINING ACUTE ACCENT BELOW
+̘ COMBINING LEFT TACK BELOW
+Ì™ COMBINING RIGHT TACK BELOW
+Ìš COMBINING LEFT ANGLE ABOVE
+Ì› COMBINING HORN
+̜ COMBINING LEFT HALF RING BELOW
+Ì COMBINING UP TACK BELOW
+Ìž COMBINING DOWN TACK BELOW
+ÌŸ COMBINING PLUS SIGN BELOW
+Ì  COMBINING MINUS SIGN BELOW
+Ì¡ COMBINING PALATALIZED HOOK BELOW
+Ì¢ COMBINING RETROFLEX HOOK BELOW
+Ì£ COMBINING DOT BELOW
+̤ COMBINING DIAERESIS BELOW
+Ì¥ COMBINING RING BELOW
+̦ COMBINING COMMA BELOW
+̧ COMBINING CEDILLA
+̨ COMBINING OGONEK
+Ì© COMBINING VERTICAL LINE BELOW
+̪ COMBINING BRIDGE BELOW
+Ì« COMBINING INVERTED DOUBLE ARCH BELOW
+̬ COMBINING CARON BELOW
+Ì­ COMBINING CIRCUMFLEX ACCENT BELOW
+Ì® COMBINING BREVE BELOW
+̯ COMBINING INVERTED BREVE BELOW
+Ì° COMBINING TILDE BELOW
+̱ COMBINING MACRON BELOW
+̲ COMBINING LOW LINE
+̳ COMBINING DOUBLE LOW LINE
+Ì´ COMBINING TILDE OVERLAY
+̵ COMBINING SHORT STROKE OVERLAY
+̶ COMBINING LONG STROKE OVERLAY
+Ì· COMBINING SHORT SOLIDUS OVERLAY
+̸ COMBINING LONG SOLIDUS OVERLAY
+̹ COMBINING RIGHT HALF RING BELOW
+̺ COMBINING INVERTED BRIDGE BELOW
+Ì» COMBINING SQUARE BELOW
+̼ COMBINING SEAGULL BELOW
+̽ COMBINING X ABOVE
+̾ COMBINING VERTICAL TILDE
+Ì¿ COMBINING DOUBLE OVERLINE
+Í€ COMBINING GRAVE TONE MARK
+Í COMBINING ACUTE TONE MARK
+Í‚ COMBINING GREEK PERISPOMENI
+̓ COMBINING GREEK KORONIS
+Í„ COMBINING GREEK DIALYTIKA TONOS
+Í… COMBINING GREEK YPOGEGRAMMENI
+͆ COMBINING BRIDGE ABOVE
+͇ COMBINING EQUALS SIGN BELOW
+͈ COMBINING DOUBLE VERTICAL LINE BELOW
+͉ COMBINING LEFT ANGLE BELOW
+ÍŠ COMBINING NOT TILDE ABOVE
+Í‹ COMBINING HOMOTHETIC ABOVE
+͌ COMBINING ALMOST EQUAL TO ABOVE
+Í COMBINING LEFT RIGHT ARROW BELOW
+ÍŽ COMBINING UPWARDS ARROW BELOW
+Í COMBINING GRAPHEME JOINER
+Í COMBINING RIGHT ARROWHEAD ABOVE
+Í‘ COMBINING LEFT HALF RING ABOVE
+Í’ COMBINING FERMATA
+Í“ COMBINING X BELOW
+Í” COMBINING LEFT ARROWHEAD BELOW
+Í• COMBINING RIGHT ARROWHEAD BELOW
+Í– COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW
+Í— COMBINING RIGHT HALF RING ABOVE
+͘ COMBINING DOT ABOVE RIGHT
+Í™ COMBINING ASTERISK BELOW
+Íš COMBINING DOUBLE RING BELOW
+Í› COMBINING ZIGZAG ABOVE
+͜ COMBINING DOUBLE BREVE BELOW
+Í COMBINING DOUBLE BREVE
+Íž COMBINING DOUBLE MACRON
+ÍŸ COMBINING DOUBLE MACRON BELOW
+Í  COMBINING DOUBLE TILDE
+Í¡ COMBINING DOUBLE INVERTED BREVE
+Í¢ COMBINING DOUBLE RIGHTWARDS ARROW BELOW
+Í£ COMBINING LATIN SMALL LETTER A
+
+</pre>
+<p>Friends and neighbors, this terrifying piece of Unicode technology was created by Mr SBP of <a href="http://inamidst.com/odds/supercombiner">inamidst.com</a></p>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/404112-1.html b/system/graphics/tests/crashtests/404112-1.html
new file mode 100644
index 000000000..aa5d3fea6
--- /dev/null
+++ b/system/graphics/tests/crashtests/404112-1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Google</title>
+</head>
+<style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#3366cc}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{float:left;font-weight:bold;height:22px;padding-left:2px}#gbh{border-top:1px solid #c9d7f1;font-size:0;height:0;position:absolute;right:0;top:24px;width:200%}#gbi{background:#fff;border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;font-size:13px;top:24px;z-index:1000}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}}#gbi,.gb2{display:none;position:absolute;width:8em}.gb2{z-index:1001}#gbar a,#gbar a:active,#gbar a:visited{color:#00c;font-weight:normal}.gb2 a,.gb3 a{text-decoration:none}.gb2 a{display:block;padding:.2em .5em}#gbar .gb2 a:hover{background:#36c;color:#fff}
+</style>
+<body>
+<div id="gbh"></div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/404112-2.html b/system/graphics/tests/crashtests/404112-2.html
new file mode 100644
index 000000000..a3cb47fa4
--- /dev/null
+++ b/system/graphics/tests/crashtests/404112-2.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>bug 404112</title>
+</head>
+<body>
+<div style="font-size: 0"></div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/405268-1.xhtml b/system/graphics/tests/crashtests/405268-1.xhtml
new file mode 100644
index 000000000..ecb6ecf21
--- /dev/null
+++ b/system/graphics/tests/crashtests/405268-1.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title></title>
+<script type="text/javascript">
+//<![CDATA[
+function boom()
+{
+ var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ document.body.appendChild(div);
+ div.style.fontSize = '68719476736px';
+ div.appendChild(document.createTextNode(String.fromCharCode(0)));
+}
+//]]>
+</script>
+</head>
+
+<body onload="boom()">
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/407761-1.html b/system/graphics/tests/crashtests/407761-1.html
new file mode 100644
index 000000000..96e9597f7
--- /dev/null
+++ b/system/graphics/tests/crashtests/407761-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<title></title>
+</head>
+<body>
+<span style="font-family: Verdana">IIIIIiiii'm dumb. ಠ_ಠ</span>
+</body> </html>
diff --git a/system/graphics/tests/crashtests/407842.html b/system/graphics/tests/crashtests/407842.html
new file mode 100644
index 000000000..a90c13b3d
--- /dev/null
+++ b/system/graphics/tests/crashtests/407842.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<title>Gecko Crash Demo</title>
+</head>
+
+<body>
+
+<div>
+ <span style="font-size:463.25em"><a href="#">Hello World!</a></span>
+</div>
+
+</body>
+</html>
+
diff --git a/system/graphics/tests/crashtests/408754-1.html b/system/graphics/tests/crashtests/408754-1.html
new file mode 100644
index 000000000..4e716d682
--- /dev/null
+++ b/system/graphics/tests/crashtests/408754-1.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style>
+ .a { width: 17895680}
+ .b { width: 10}
+</style>
+</head>
+<body>
+<table><tr>
+<td class="a">a</td><td class="b">b</td>
+</tr></table>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/410728-1.xml b/system/graphics/tests/crashtests/410728-1.xml
new file mode 100644
index 000000000..deaeb8fc5
--- /dev/null
+++ b/system/graphics/tests/crashtests/410728-1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"
+ "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd"
+[
+ <!ENTITY mathml "http://www.w3.org/1998/Math/MathML">
+]>
+<math display="block" xmlns="&mathml;">
+ <mi>f &#x0332;</mi>
+ <mi>-</mi>
+ <mo>+</mo>
+ <mo> -</mo>
+ <mo>&#x0332;</mo>
+</math>
diff --git a/system/graphics/tests/crashtests/416637-1.html b/system/graphics/tests/crashtests/416637-1.html
new file mode 100644
index 000000000..48323a744
--- /dev/null
+++ b/system/graphics/tests/crashtests/416637-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="font-size: 10000%">&#x2029;&#x0301;</body>
+</html>
diff --git a/system/graphics/tests/crashtests/419095-1.html b/system/graphics/tests/crashtests/419095-1.html
new file mode 100644
index 000000000..9f47af262
--- /dev/null
+++ b/system/graphics/tests/crashtests/419095-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x202E)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x000D)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x200D)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0xD75A)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0xD63F)));
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/419255-1.html b/system/graphics/tests/crashtests/419255-1.html
new file mode 100644
index 000000000..acde950dd
--- /dev/null
+++ b/system/graphics/tests/crashtests/419255-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body><div style="text-transform: capitalize">&#x5426; &#x200C;</body>
+</html>
diff --git a/system/graphics/tests/crashtests/420945-1.html b/system/graphics/tests/crashtests/420945-1.html
new file mode 100644
index 000000000..a6eb2d57c
--- /dev/null
+++ b/system/graphics/tests/crashtests/420945-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body dir="rtl"><div>&#x200C;&#x2028;</div></body>
+</html>
diff --git a/system/graphics/tests/crashtests/420962-1.html b/system/graphics/tests/crashtests/420962-1.html
new file mode 100644
index 000000000..f8cf8b453
--- /dev/null
+++ b/system/graphics/tests/crashtests/420962-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body><div>&#x0301;&#x2029;</div></body>
+</html>
diff --git a/system/graphics/tests/crashtests/421393-1.html b/system/graphics/tests/crashtests/421393-1.html
new file mode 100644
index 000000000..82b697467
--- /dev/null
+++ b/system/graphics/tests/crashtests/421393-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.appendChild(document.createTextNode("\uCEDD\u5C76\u000D\uCA34"));
+}
+
+</script>
+</head>
+
+<body onload="boom();"><span>&#x202E;&#x05BC;</span></body>
+</html>
diff --git a/system/graphics/tests/crashtests/421813-1.html b/system/graphics/tests/crashtests/421813-1.html
new file mode 100644
index 000000000..885408b1b
--- /dev/null
+++ b/system/graphics/tests/crashtests/421813-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body onload="document.body.appendChild(document.createTextNode('y'));">x&#x2028;&#x200D;&#x202D;<span>&#x0643;</span></body>
+</html>
diff --git a/system/graphics/tests/crashtests/423110-1.xhtml b/system/graphics/tests/crashtests/423110-1.xhtml
new file mode 100644
index 000000000..9633ff868
--- /dev/null
+++ b/system/graphics/tests/crashtests/423110-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="min-width: -moz-max-content; float: left;"><head style="padding: 200%; display: -moz-inline-box; float: inherit;"></head></html>
diff --git a/system/graphics/tests/crashtests/423270-1.html b/system/graphics/tests/crashtests/423270-1.html
new file mode 100644
index 000000000..e7c1d606b
--- /dev/null
+++ b/system/graphics/tests/crashtests/423270-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<p style="font-family: DejaVu Sans">&#814;</p>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/428633.html b/system/graphics/tests/crashtests/428633.html
new file mode 100644
index 000000000..cbf601a4a
--- /dev/null
+++ b/system/graphics/tests/crashtests/428633.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>A&#x101747;AAAAAAA&#x4f47;AAAAA&#xe33;AAAAAA</body>
+</html>
diff --git a/system/graphics/tests/crashtests/429899-1.html b/system/graphics/tests/crashtests/429899-1.html
new file mode 100644
index 000000000..11cccd686
--- /dev/null
+++ b/system/graphics/tests/crashtests/429899-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><body><span>&#x1104</span><span>&#x1104</span><span>&#x116A</span></body></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/441360.html b/system/graphics/tests/crashtests/441360.html
new file mode 100644
index 000000000..0f06414e7
--- /dev/null
+++ b/system/graphics/tests/crashtests/441360.html
@@ -0,0 +1,39 @@
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug </title>
+ <style type="text/css">
+
+ html,body {
+ color:black; background-color:white; font-size:16px; padding:0; margin:0;
+ }
+
+
+ </style>
+</head>
+<body>
+
+<div id="Image"></div>
+
+<script>
+var v;
+function insertImage() {
+ var img_node = document.createElement('iframe');
+ img_node.src = "441360_data.gif";
+ var image_div = document.getElementById('Image');
+ image_div.appendChild(img_node);
+}
+
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+
+</script>
+
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/441360_data.gif b/system/graphics/tests/crashtests/441360_data.gif
new file mode 100644
index 000000000..96532dc21
--- /dev/null
+++ b/system/graphics/tests/crashtests/441360_data.gif
Binary files differ
diff --git a/system/graphics/tests/crashtests/445711.html b/system/graphics/tests/crashtests/445711.html
new file mode 100644
index 000000000..6ebd37eef
--- /dev/null
+++ b/system/graphics/tests/crashtests/445711.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/REC-html401-19991224/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Tamil testcase</title>
+ </head>
+ <body>
+ <p>&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;</p>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/463307-1.html b/system/graphics/tests/crashtests/463307-1.html
new file mode 100644
index 000000000..2d8eca317
--- /dev/null
+++ b/system/graphics/tests/crashtests/463307-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html style="position: relative; bottom: 4449920388in; margin: 200px;">
+<head></head>
+<body style="background: url(../../../layout/reftests/bugs/repeatable-diagonal-gradient.png);"></body>
+</html>
diff --git a/system/graphics/tests/crashtests/467703-1.xhtml b/system/graphics/tests/crashtests/467703-1.xhtml
new file mode 100644
index 000000000..e315baa32
--- /dev/null
+++ b/system/graphics/tests/crashtests/467703-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="margin: 78504em; background: url(../../../testing/crashtest/images/tree.gif); font-size: 305203ch; position: relative; left: 65em;"><head></head><body></body></html>
diff --git a/system/graphics/tests/crashtests/467873-1.html b/system/graphics/tests/crashtests/467873-1.html
new file mode 100644
index 000000000..ac4a6cdd0
--- /dev/null
+++ b/system/graphics/tests/crashtests/467873-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('q').appendChild(document.createTextNode('C'));">
+<div style="white-space: pre; direction: rtl;">A<div id="q" style="text-transform: capitalize;">B
+<div></div></div></div>
+</body></html>
diff --git a/system/graphics/tests/crashtests/470418-1.html b/system/graphics/tests/crashtests/470418-1.html
new file mode 100644
index 000000000..7f13b8735
--- /dev/null
+++ b/system/graphics/tests/crashtests/470418-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body><div style="text-transform: capitalize">&#x06CD;A</div></body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/474410-1.html b/system/graphics/tests/crashtests/474410-1.html
new file mode 100644
index 000000000..c7ea8e7b2
--- /dev/null
+++ b/system/graphics/tests/crashtests/474410-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div { -moz-transform: matrix(3, 1, 16.8, 5.6, 0, 0); }
+div > div { border: 1px dashed #9ab; }
+
+</style>
+</head>
+<body>
+
+<div><div></div></div>
+
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/483120-1.xhtml b/system/graphics/tests/crashtests/483120-1.xhtml
new file mode 100644
index 000000000..4a15e725b
--- /dev/null
+++ b/system/graphics/tests/crashtests/483120-1.xhtml
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: monospace;">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="w"><content><div xmlns="http://www.w3.org/1999/xhtml"><children xmlns="http://www.mozilla.org/xbl"/></div></content></binding>
+ <binding id="empty"><content/></binding>
+</bindings>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.createElementNS("http://www.w3.org/2000/svg", "rect");
+ document.getElementById("d").previousSibling.data += "C";
+ document.getElementById("d").style.MozBinding = "url('#empty')";
+}
+
+</script>
+
+</head>
+
+<body onload="boom();"><span>A&#x202E;<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
+</html>
diff --git a/system/graphics/tests/crashtests/483120-2.xhtml b/system/graphics/tests/crashtests/483120-2.xhtml
new file mode 100644
index 000000000..a9e67fe70
--- /dev/null
+++ b/system/graphics/tests/crashtests/483120-2.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: monospace;">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="w"><content><div xmlns="http://www.w3.org/1999/xhtml"><children xmlns="http://www.mozilla.org/xbl"/></div></content></binding>
+ <binding id="empty"><content/></binding>
+</bindings>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("d").previousSibling.data += "C";
+ document.getElementById("d").style.MozBinding = "url('#empty')";
+}
+
+</script>
+
+</head>
+
+<body onload="boom();"><span>A&#x202E;<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
+</html>
diff --git a/system/graphics/tests/crashtests/487549-1.html b/system/graphics/tests/crashtests/487549-1.html
new file mode 100644
index 000000000..c20cb483f
--- /dev/null
+++ b/system/graphics/tests/crashtests/487549-1.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Bad kern table - bug 487549</title>
+<!--
+ The font used here has an invalid 'kern' table that will crash ATSUI
+ if we attempt to use it.
+ See https://bugzilla.mozilla.org/show_bug.cgi?id=487549
+-->
+<style>
+@font-face {
+ font-family: bad-kern-font;
+ src: url(487549-bad_kern_table.ttf) format("truetype");
+}
+
+body {
+ font-family: bad-kern-font;
+}
+</style>
+</head>
+<body>
+ABC abc 123
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/487549-bad_kern_table.ttf b/system/graphics/tests/crashtests/487549-bad_kern_table.ttf
new file mode 100644
index 000000000..d8da04a3a
--- /dev/null
+++ b/system/graphics/tests/crashtests/487549-bad_kern_table.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/487724-1.html b/system/graphics/tests/crashtests/487724-1.html
new file mode 100644
index 000000000..abc158b43
--- /dev/null
+++ b/system/graphics/tests/crashtests/487724-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var w = document.getElementById("w");
+ var q = document.getElementById("q");
+ var e = document.createTextNode("");
+ document.documentElement.appendChild(document.body);
+ w.appendChild(e);
+ document.documentElement.offsetHeight;
+ w.removeChild(q);
+ document.documentElement.offsetHeight;
+ e.data += " x ";
+}
+
+</script>
+</head>
+
+<body onload="boom();"><span id="w">s&#x202E;<span id="q"></span><div></div><span>a</span></span></body>
+</html>
diff --git a/system/graphics/tests/crashtests/490777-1.html b/system/graphics/tests/crashtests/490777-1.html
new file mode 100644
index 000000000..f7e42b54b
--- /dev/null
+++ b/system/graphics/tests/crashtests/490777-1.html
@@ -0,0 +1,9 @@
+<!-- This crashed on Mac OS X with the modified ATSUI font backend implemented in
+ bug 481948. Crash occurs due to an unpaired low surrogate in text with the
+ right-to-left direction override; this cannot occur in direct HTML content
+ because the unpaired surrogate will be replaced with U+FFFD, but it can be
+ generated from Javascript. -->
+<html>
+<body onload="document.body.appendChild(document.createTextNode('\u202E\u4839\uDC1D'));">
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/516512-1.html b/system/graphics/tests/crashtests/516512-1.html
new file mode 100644
index 000000000..028e4507a
--- /dev/null
+++ b/system/graphics/tests/crashtests/516512-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="font-size: 70368744177663%; -moz-background-size: 2199023255552em 809464690865px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4%2F58BAAT%2FAf9jgNErAAAAAElFTkSuQmCC);">
+<body>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/532726-1.html b/system/graphics/tests/crashtests/532726-1.html
new file mode 100644
index 000000000..d89a52475
--- /dev/null
+++ b/system/graphics/tests/crashtests/532726-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="word-spacing: 10px">
+<div>X &#x0301;&#x0000;</div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/538065-1.html b/system/graphics/tests/crashtests/538065-1.html
new file mode 100644
index 000000000..ff6669bee
--- /dev/null
+++ b/system/graphics/tests/crashtests/538065-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<title>Testcase for bug 538065</title>
+<style type="text/css">
+span.test { background: #ff0; }
+</style>
+</head>
+<body>
+<p>U+FEFF: <span class="test">&#xfeff;</span></p>
+<p>U+FFF9: <span class="test">&#xfff9;</span></p>
+<p>U+FFFA: <span class="test">&#xfffa;</span></p>
+<p>U+FFFB: <span class="test">&#xfffb;</span></p>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/546870-1.html b/system/graphics/tests/crashtests/546870-1.html
new file mode 100644
index 000000000..bc83d90e0
--- /dev/null
+++ b/system/graphics/tests/crashtests/546870-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('button').value='';">
+<input id="button" type="button" value="Should not crash" style="width: 1px; white-space: normal;">
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/557348-1.html b/system/graphics/tests/crashtests/557348-1.html
new file mode 100644
index 000000000..8248500ce
--- /dev/null
+++ b/system/graphics/tests/crashtests/557348-1.html
@@ -0,0 +1 @@
+<html style="background: -moz-repeating-radial-gradient(left center , circle closest-side, red, white 100px, black); width: 300px; height: 1px;"></html>
diff --git a/system/graphics/tests/crashtests/563740-1.html b/system/graphics/tests/crashtests/563740-1.html
new file mode 100644
index 000000000..8873cfa3a
--- /dev/null
+++ b/system/graphics/tests/crashtests/563740-1.html
@@ -0,0 +1,2 @@
+<html><body style="font-size-adjust: -18446744073709552000; font-weight: bold;">&#x9385;</body></html>
+
diff --git a/system/graphics/tests/crashtests/580100-1.html b/system/graphics/tests/crashtests/580100-1.html
new file mode 100644
index 000000000..41e4a474f
--- /dev/null
+++ b/system/graphics/tests/crashtests/580100-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580100-bad_hhea_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/system/graphics/tests/crashtests/580100-bad_hhea_table.ttf b/system/graphics/tests/crashtests/580100-bad_hhea_table.ttf
new file mode 100644
index 000000000..229767423
--- /dev/null
+++ b/system/graphics/tests/crashtests/580100-bad_hhea_table.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/580212-1.html b/system/graphics/tests/crashtests/580212-1.html
new file mode 100644
index 000000000..b5af08ddc
--- /dev/null
+++ b/system/graphics/tests/crashtests/580212-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580212-bad_loca_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/system/graphics/tests/crashtests/580212-bad_loca_table.ttf b/system/graphics/tests/crashtests/580212-bad_loca_table.ttf
new file mode 100644
index 000000000..ec303a922
--- /dev/null
+++ b/system/graphics/tests/crashtests/580212-bad_loca_table.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/580233-1.html b/system/graphics/tests/crashtests/580233-1.html
new file mode 100644
index 000000000..07d4abdcb
--- /dev/null
+++ b/system/graphics/tests/crashtests/580233-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580233-bad_gpos_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/system/graphics/tests/crashtests/580233-bad_gpos_table.ttf b/system/graphics/tests/crashtests/580233-bad_gpos_table.ttf
new file mode 100644
index 000000000..c753040ca
--- /dev/null
+++ b/system/graphics/tests/crashtests/580233-bad_gpos_table.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/580719-1.html b/system/graphics/tests/crashtests/580719-1.html
new file mode 100644
index 000000000..29fe9f73d
--- /dev/null
+++ b/system/graphics/tests/crashtests/580719-1.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<style>
+@font-face {
+ font-family: t;
+ src: url(580719-bad_head_table.ttf);
+}
+
+p.t {
+ font-size: 40px;
+ font-family: t;
+}
+</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/580719-bad_head_table.ttf b/system/graphics/tests/crashtests/580719-bad_head_table.ttf
new file mode 100644
index 000000000..9ad4896b0
--- /dev/null
+++ b/system/graphics/tests/crashtests/580719-bad_head_table.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/593526.html b/system/graphics/tests/crashtests/593526.html
new file mode 100644
index 000000000..f0de065dc
--- /dev/null
+++ b/system/graphics/tests/crashtests/593526.html
@@ -0,0 +1 @@
+<html style="max-width: 51079px; width: 730.549in; -moz-appearance: checkbox; box-shadow: 0.2em 0.2em rgb(204, 204, 204);">
diff --git a/system/graphics/tests/crashtests/593526.xul b/system/graphics/tests/crashtests/593526.xul
new file mode 100644
index 000000000..d1da4c3d8
--- /dev/null
+++ b/system/graphics/tests/crashtests/593526.xul
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<box style="max-width: 51079px; width: 730.549in; -moz-appearance: checkbox; box-shadow: 0.2em 0.2em rgb(204, 204, 204);"></box>
+</window>
+
diff --git a/system/graphics/tests/crashtests/594654-1.xhtml b/system/graphics/tests/crashtests/594654-1.xhtml
new file mode 100644
index 000000000..4ab2ce6e7
--- /dev/null
+++ b/system/graphics/tests/crashtests/594654-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.getElementById('x').appendChild(document.createElementNS('http://www.w3.org/1998/Math/MathML', 'mrow'));">
+<div style="position: fixed;"><div><msubsup xmlns="http://www.w3.org/1998/Math/MathML" id="x"/></div><menclose xmlns="http://www.w3.org/1998/Math/MathML"/></div><mroot xmlns="http://www.w3.org/1998/Math/MathML"/>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/595042-1.html b/system/graphics/tests/crashtests/595042-1.html
new file mode 100644
index 000000000..0cc2b2247
--- /dev/null
+++ b/system/graphics/tests/crashtests/595042-1.html
@@ -0,0 +1 @@
+<html style="-moz-box-shadow: 0 0 0.2em blue; -moz-appearance: button;"><body style="padding: 113in;"></body></html>
diff --git a/system/graphics/tests/crashtests/595727-1.html b/system/graphics/tests/crashtests/595727-1.html
new file mode 100644
index 000000000..3b5d242aa
--- /dev/null
+++ b/system/graphics/tests/crashtests/595727-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+
+function doTest()
+{
+ var r = document.documentElement;
+ while(r.firstChild) { r.removeChild(r.firstChild); }
+ var body = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
+ r.appendChild(body);
+ body.contentEditable = "true";
+ document.execCommand("inserthtml", false, "<span style=\"position:relative;left:0.8px\">a<select></select>a</span>");
+
+ document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+</head>
+
+<body></body>
+</html>
diff --git a/system/graphics/tests/crashtests/624198.xhtml b/system/graphics/tests/crashtests/624198.xhtml
new file mode 100644
index 000000000..67d207b7a
--- /dev/null
+++ b/system/graphics/tests/crashtests/624198.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><msqrt xmlns="http://www.w3.org/1998/Math/MathML"><mpadded depth="+98774970791px"/></msqrt></html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/633322-1.html b/system/graphics/tests/crashtests/633322-1.html
new file mode 100644
index 000000000..89b0aaa61
--- /dev/null
+++ b/system/graphics/tests/crashtests/633322-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><body><div style="text-transform: uppercase">&#xA6F8;&#xDF;&#x200B;</div></body></html>
diff --git a/system/graphics/tests/crashtests/633453-1.html b/system/graphics/tests/crashtests/633453-1.html
new file mode 100644
index 000000000..2b08776c0
--- /dev/null
+++ b/system/graphics/tests/crashtests/633453-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+<p>Откуда: ×ֶרֶץ יִשְׂרָ×ֵל‎</p>
+</body>
+</html>
+
diff --git a/system/graphics/tests/crashtests/662467-1.html b/system/graphics/tests/crashtests/662467-1.html
new file mode 100644
index 000000000..ccf3c7d8e
--- /dev/null
+++ b/system/graphics/tests/crashtests/662467-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html><html style="min-height: 1540095em; opacity: 0.2; -moz-appearance: toolbar"><body></body></html>
+
diff --git a/system/graphics/tests/crashtests/665218.html b/system/graphics/tests/crashtests/665218.html
new file mode 100644
index 000000000..944b341bf
--- /dev/null
+++ b/system/graphics/tests/crashtests/665218.html
@@ -0,0 +1,8 @@
+<html class="reftest-print"><head>
+<style id="e"> @font-face {
+ font-family: "aaa";
+ src: url("doesnotexist.TTF");
+ }
+ * { font-family: "aaa"; }</style>
+</head>
+<body>
diff --git a/system/graphics/tests/crashtests/675550-1.html b/system/graphics/tests/crashtests/675550-1.html
new file mode 100644
index 000000000..d6c25b59e
--- /dev/null
+++ b/system/graphics/tests/crashtests/675550-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+.justify {
+ width: 400px;
+ text-align: justify;
+ background: lightgreen;
+}
+
+.force {
+ width: 400px;
+ display: inline-block;
+ height: 3px;
+ background: yellow;
+}
+
+</style>
+</head>
+<body onload="document.getElementById('s').textContent='\u202E\0 \u1DCEz'; /* cannot express \0 in html markup using amp escape */">
+<div class="justify"><span class="force"></span><span id="s"></span><span class="force"></span></div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/686190-1.html b/system/graphics/tests/crashtests/686190-1.html
new file mode 100644
index 000000000..26cda094f
--- /dev/null
+++ b/system/graphics/tests/crashtests/686190-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<head>
+<style type="text/css">
+@font-face {
+ font-family: foo;
+ src: url(Prototype.ttf);
+}
+body {
+ font-family: foo;
+ font-size: 2000px;
+ font-weight: 900;
+}
+</style>
+</head>
+<body>
+xyzzy
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/691581-1.html b/system/graphics/tests/crashtests/691581-1.html
new file mode 100644
index 000000000..ce2f35eaf
--- /dev/null
+++ b/system/graphics/tests/crashtests/691581-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="font-size: 0.03rem;">
+<body>
+ Test Text
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/693143-1.html b/system/graphics/tests/crashtests/693143-1.html
new file mode 100644
index 000000000..80b1e891a
--- /dev/null
+++ b/system/graphics/tests/crashtests/693143-1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Tiny ugly fonts</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<style type="text/css">
+
+body {
+ margin: 50px;
+}
+
+p, div {
+ margin: 0;
+}
+
+#test {
+ font-size: 1px;
+}
+
+#test p {
+ font-size: 8.3%;
+}
+
+#f1 { font-family: Terminal; }
+#f2 { font-family: FixedSys; }
+#f3 { font-family: Script; }
+#f4 { font-family: Roman; }
+
+</style>
+
+</head>
+<body>
+
+<h4>No text should show below this line</h4>
+<div id="test">
+<p id="f1">ugly font</p>
+<p id="f2">ugly font</p>
+<p id="f3">ugly font</p>
+<p id="f4">ugly font</p>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/696936-1.html b/system/graphics/tests/crashtests/696936-1.html
new file mode 100644
index 000000000..0ce95ddd6
--- /dev/null
+++ b/system/graphics/tests/crashtests/696936-1.html
@@ -0,0 +1,2 @@
+<!-- quirks mode only -->
+<body style="-moz-transform: perspective(-3);"></body>
diff --git a/system/graphics/tests/crashtests/699563-1.html b/system/graphics/tests/crashtests/699563-1.html
new file mode 100644
index 000000000..e73968d71
--- /dev/null
+++ b/system/graphics/tests/crashtests/699563-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html><html style="width: 16px; height: 16px; -moz-transform: matrix3d(1, 2, 300, 4, 5, 6000, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); filter: url(&quot;#none&quot;);"><div style="height: 100px; width: 100px;"></div></html>
+
diff --git a/system/graphics/tests/crashtests/710149-1.html b/system/graphics/tests/crashtests/710149-1.html
new file mode 100644
index 000000000..930ef9551
--- /dev/null
+++ b/system/graphics/tests/crashtests/710149-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<script>
+
+function boom()
+{
+ var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ d.style.setProperty("-moz-transform", "translate(0pt, 10px)", "");
+ d.style.setProperty("opacity", "0.8", "");
+ d.style.setProperty("background-color", "gray", "");
+ var c = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ (d).appendChild(c);
+ (document.body).appendChild(d);
+ c.getContext("2d");
+}
+
+</script>
+
+<body onload="setTimeout(boom, 100);"></body>
diff --git a/system/graphics/tests/crashtests/766452-1.html b/system/graphics/tests/crashtests/766452-1.html
new file mode 100644
index 000000000..abb696979
--- /dev/null
+++ b/system/graphics/tests/crashtests/766452-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+
+document.createElement('canvas').getContext('2d').measureText("\u0CC4\u0CA7\u200C");
+
+</script>
diff --git a/system/graphics/tests/crashtests/766452-2.html b/system/graphics/tests/crashtests/766452-2.html
new file mode 100644
index 000000000..b70d6f813
--- /dev/null
+++ b/system/graphics/tests/crashtests/766452-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="font-family:Arial Unicode MS">&#x0CC4;&#x0CA7;&#x200C;</div>
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/768079-1.html b/system/graphics/tests/crashtests/768079-1.html
new file mode 100644
index 000000000..b61e6607e
--- /dev/null
+++ b/system/graphics/tests/crashtests/768079-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="-moz-perspective: 7000px; overflow: hidden;">
+<body style="overflow: hidden; -moz-transform: rotateX(30deg); border-radius: 1px; -moz-columns: 2 10px; visibility: collapse;">X</body>
+</html>
diff --git a/system/graphics/tests/crashtests/783041-1.html b/system/graphics/tests/crashtests/783041-1.html
new file mode 100644
index 000000000..0e57a7315
--- /dev/null
+++ b/system/graphics/tests/crashtests/783041-1.html
@@ -0,0 +1,63 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el7 {
+ font-size:.92em
+ }
+ #el0 {
+ height: 200px ! important;
+ margin: 0px;
+ display: table;
+ font-size:.92em
+ }
+
+ #el3 {
+ line-height: 0.5px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ display: table-row-group;
+ transform: translate3d(-3px, -300px, 0px);
+ }
+
+ #el5 {
+ height:1em;
+ display:block;
+ }
+
+ .c4 {
+ margin: 1em;
+ padding: 0.5em;
+ }
+ </style>
+ <script>
+ onload = function() {
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ document.body.appendChild(el7)
+
+ el0=document.createElement('span')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+ el0.appendChild(document.createTextNode('A'))
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ document.body.offsetTop
+ el0.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/783041-2.html b/system/graphics/tests/crashtests/783041-2.html
new file mode 100644
index 000000000..6d5745b81
--- /dev/null
+++ b/system/graphics/tests/crashtests/783041-2.html
@@ -0,0 +1,63 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ height: 1em;
+ width: 1em;
+ padding: 5px;
+ display: table;
+ -moz-transform:translate3d(0, 80px, 0);
+ }
+ #el0:before {
+ display: -moz-grid;
+ content: counter(c, hiragana) attr(id);
+ counter-increment: c 694;
+ }
+ #el0:after {
+ counter-reset: c 694;
+ content: counter(c, cjk-ideographic) attr(id);
+ }
+ #el1 {
+ text-shadow: 0px 20px 0px, 0px -20px 10px;
+ line-height: 4px;
+ transform: translate3d(0px, -300px, 0px);
+ display: table-row-group;
+ border-spacing: 7px;
+ }
+ #el1:after {
+ counter-reset: c;
+ display: -moz-box;
+ content: counter(c, cjk-ideographic) attr(id);
+ counter-increment: c 694;
+ }
+ #el2 {
+ display: table-row-group;
+ -moz-transform:translate3d(0, 80px, 0);
+ }
+ #el2:after {
+ content: counter(c, cjk-ideographic) attr(id);
+ }
+ </style>
+ <script>
+ onload = function() {
+ el0=document.createElement('div')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+ el1=document.createElement('div')
+ el1.setAttribute('id','el1')
+ el0.appendChild(el1)
+ el2=document.createElement('q')
+ el2.setAttribute('id','el2')
+ el1.appendChild(el2)
+ el0.appendChild(document.createTextNode('A'))
+ setTimeout("location.reload()", 100)
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/783041-3.html b/system/graphics/tests/crashtests/783041-3.html
new file mode 100644
index 000000000..efdbced5e
--- /dev/null
+++ b/system/graphics/tests/crashtests/783041-3.html
@@ -0,0 +1,71 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ padding: 4px;
+ white-space: pre-wrap;
+ margin-top: 0px;
+ display: table;
+ }
+ #el3 {
+ line-height: 3px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ }
+ #el3:before { display: inline-block; }
+ #el3:first-of-type { transform: translate3d(-30px, -300px, 0px); }
+ #el3:first-child { display: table-row-group; }
+ #el5:before { -moz-margin-before:1em; display: list-item; content: counter(c, ethiopic-halehame-tig) attr(id); counter-increment: c 810; }
+ #el5 { list-style-type:lower-greek }
+ #el5 { background:red;margin:0;height:1em;display:block }
+ #el5:nth-child(3) { -moz-svg-shadow:5px 5px 5px red; display: link; content: counter(c, asterisks) attr(id); counter-increment: c 266; }
+
+ .c4 {
+ margin: 1em; }
+ #el7 { font-size:.92em }
+ #el3 { font-size:.92em }
+ .c4 { padding:0.4em 0.5em 0.4em 2.5em }
+ .c4:after { margin:2px; display: -moz-box; content: counter(c, octal) attr(id); counter-increment: c 171; }
+ .c4:before { -moz-user-drag:none; display: -moz-inline-flexbox; content: counter(c, hiragana-iroha) attr(id); counter-increment: c 209; }
+ .c4:only-of-type { -moz-logical-height:50px; display: -moz-box; content: counter(c, binary) attr(id); counter-increment: c 871; }
+ </style>
+ <script>
+ onload = function() {
+ el0=document.createElement('ul')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+
+ el1=document.createElement('canvas')
+ document.body.appendChild(el1)
+
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el4=document.createElement('progress')
+ el4.setAttribute('id','el4')
+ document.body.appendChild(el4)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ el0.parentNode.insertBefore(el7, el0)
+ el0.setAttribute('class', 'c4');
+ el3.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ setTimeout("window.close()", 5000)
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/783041-4.html b/system/graphics/tests/crashtests/783041-4.html
new file mode 100644
index 000000000..1538bfdf0
--- /dev/null
+++ b/system/graphics/tests/crashtests/783041-4.html
@@ -0,0 +1,82 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ padding: 4px;
+ white-space: pre-wrap;
+ margin-top: 0px;
+ display: table;
+ font-size:.92em
+ }
+
+ #el0:before {
+ content: counter(c, hiragana-iroha);
+ }
+
+ #el3 {
+ line-height: 3px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ display: table-row-group;
+ transform: translate3d(-30px, -300px, 0px);
+ }
+
+ #el3:before {
+ display: inline-block;
+ }
+
+ #el5 {
+ height:1em;
+ display:block;
+ }
+
+ #el5:before {
+ display: list-item;
+ }
+
+ #el7 {
+ font-size:.92em
+ }
+ .c4 {
+ margin: 1em;
+ padding:0.4em 0.5em 0.4em 2.5em;
+ counter-increment: c;
+ }
+
+ .c4:after {
+ margin: 2px;
+ display: inline-block;
+ }
+ </style>
+ <script>
+ onload = function() {
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ document.body.appendChild(el7)
+
+ el0=document.createElement('ul')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ el0.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/system/graphics/tests/crashtests/798853.html b/system/graphics/tests/crashtests/798853.html
new file mode 100644
index 000000000..a0b16f77c
--- /dev/null
+++ b/system/graphics/tests/crashtests/798853.html
@@ -0,0 +1,3 @@
+<p style="font-size-adjust: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; "><style>
+@-moz-keyframes cfpulse1 { 0% { opacity: 0.7301; font-size-adjust: 0.7684; } }
+* { -moz-animation-name: cfpulse1; -moz-animation-duration: 3s; \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/805760-1.html b/system/graphics/tests/crashtests/805760-1.html
new file mode 100644
index 000000000..9f89d96e7
--- /dev/null
+++ b/system/graphics/tests/crashtests/805760-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face{
+ font-family:t; src:url(805760.ttf);
+}
+p.t{
+ font-size:40px;
+ font-family:t;
+}
+span.t {
+ font-size:10px;
+ font-family:t;
+}
+</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p>
+<span class="t">Lorem ipsum</span>
+</body></html>
+
diff --git a/system/graphics/tests/crashtests/805760.ttf b/system/graphics/tests/crashtests/805760.ttf
new file mode 100644
index 000000000..e02a76955
--- /dev/null
+++ b/system/graphics/tests/crashtests/805760.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/815489.html b/system/graphics/tests/crashtests/815489.html
new file mode 100644
index 000000000..3bc4d3eee
--- /dev/null
+++ b/system/graphics/tests/crashtests/815489.html
@@ -0,0 +1,17 @@
+><textarea></textarea><audio id=test1 style="cue-after: none; margin: 8508em -189em 182; ">><style>
+* { azimuth: behind; -moz-transform: skewy(86deg); }
+@-moz-keyframes cfpulse0 { 0% { opacity: 0.3175; transform: rotatex(67166.5665591deg); box-shadow: 167px 0px 8px ivory; quotes: "" "‹" "›"; } }
+* { -moz-animation-name: cfpulse0; -moz-animation-duration: 5s; background-color: -moz-buttonhoverface;</style><script>
+var docElement = document.body ? document.body : document.documentElement;
+function initCF() {
+try { test2 = document.createElementNS("http://www.w3.org/1999/xhtml", "tbody"); } catch(e) {}
+try { docElement.appendChild(test2); } catch(e) {}
+setTimeout("CFcrash()", 192);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+function CFcrash() {
+try { test1.style.display = "table-column" } catch(e) {}
+setTimeout('try { var x = new XSLTProcessor(); x.transformToDocument(test1); } catch(e) {}', 205);
+setTimeout('try { docElement.appendChild(test1); } catch(e) {}', 391);
+try { test2.classList.add("not-exist"); } catch(e) {}
+}</script>> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/836225-1.html b/system/graphics/tests/crashtests/836225-1.html
new file mode 100644
index 000000000..18f023f7c
--- /dev/null
+++ b/system/graphics/tests/crashtests/836225-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style type="text/css">
+@font-face {
+ font-family: foo;
+ src: url(PigLatin_Plane15.ttf);
+}
+body {
+ font-family: foo;
+ font-size: 24px;
+}
+</style>
+</head>
+<body>
+&#xf0050;&#xf0069;&#xf0067; &#xf004c;&#xf0061;&#xf0074;&#xf0069;&#xf006e;
+</body>
+</html>
diff --git a/system/graphics/tests/crashtests/839745-1.html b/system/graphics/tests/crashtests/839745-1.html
new file mode 100644
index 000000000..f45b2f41d
--- /dev/null
+++ b/system/graphics/tests/crashtests/839745-1.html
@@ -0,0 +1,20 @@
+<html>>><svg width="38.500000cm"> fill="#740000" height="64787">><rect height="182px" stroke="gray" width="39904px"></rect>
+<text font-size="52em" x="145 15 162 245 31261 251 143 3 2556045634 67 30 164 16925">k20n01 (21.00)</text>
+><rect height="44" stroke="gray" width="482.6"></rect>
+<text font-size="0.941166">3V97 T*2W t
+fSs
+_9 oCFRVeS
+@
+[^2h A3Y
+4
+ u
+vd`
+8%&amp;./h}*y|Z`6&amp;7 2?D4PoB|
+~#6b|/ak;sf?MaHHLAck Aee6fI*pU_i}5N%q? Qk7 uBJ l4;x7LlsrDu~:U=+P
+ *e#{z
+
+) n|NRXS:N
+ J p
+ZFfSc!W %rTL)#D+6Cd}0$
+k20n03 (22.00)</text>
+><text font-size="102em">> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/856784-1.html b/system/graphics/tests/crashtests/856784-1.html
new file mode 100644
index 000000000..b048f8d0e
--- /dev/null
+++ b/system/graphics/tests/crashtests/856784-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML><html><head>
+<style>
+@font-face {
+ font-family: test;
+ src: url(data:font/opentype;base64,AAEAAAANAIAAAwBQT1MvMkm5zgUAAADcAAAAYGNtYXAMOQzXAAABPAAAASpjdnQg7x+UzAAAAmgAAAc6ZnBnbQjouigAAAmkAAAF12dseWZb0FHdAAAPfAAAOyhoZWFkzwIhqAAASqQAAAA2aGhlYRJ+DK0AAErcAAAAJGhtdHjugxS+AABLAAAAAOBsb2NhAAX85gAAS+AAAADkbWF4cAcxEgMAAEzEAAAAIG5hbWVqLhwrAABM5AAAAihwb3N0AAMAAAAATwwAAAAgcHJlcEEhZAMAAE8sAAAQbwADAiQB9AAFAAACigK7AAAAjAKKArsAAAHfADEBAgAAAAAGAAAAAAAAAIAAAAEQAAACAAAAAAAAAAAqMjEqAAAAIP//Bz7+TgBkCBUDAwAAAAAAAAAAAAACywAAACAAAwAAAAEAAwABAAAADAAEAR4AAAAoACAABAAIACAAKgA6AEMARQBHAEkATgBQAFYAYgBmAGkAbwB1AHggrOAC//7//wAAACAAKAAtAEEARQBHAEkASwBQAFIAYQBkAGgAbAByAHggrOAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAKAAoACwARgBKAEoASgBKAFAAUABYAFoAXgBgAGYAbABsAGwAcAAAAAMAHwAgADEABAAiABQAGwAdABkAHAA0ADMANgAyABoAIwAhACQABQAuACcAJgARADUAKAAsAA4ALQApACsACgAXACoACwAWACUACAANABUABgAHABAAEgAPAAwAGAAJABMALwAwAAIAHgA3AAEAAAW6ABwFugAcBacAHAQmABwAAP/kAAD/5AAA/+T+af/kBboAHP5p/+QC6gAAAR0AAAEdAAAAAAAAAAAAsgCsANcBKAEgALMB+gAXAPgBGQExAEkABAD3AAMArwD9AJUAFABUAJYBEgAkABYAVQBJAQQBGQErAIwBm/92/+kAPQCSAKL/twGC/6oAFgCPAMYA+AAcAN4EAQA3AE4AVQBVAGUA6QPlAFn/mgAIAIcACwA7AFIBFgBhANYA1gD1AAAAkwCUAL4BfP/4AAQAFACCAJIAPABBAEH/wf/8ACoAjASQBdgJtQCRALsBBv9j/2kAHgAiAIoCK//W/98AJgBZAKMArAEEASsBwARIACEAawCFAJgBGQPGAGsAlQCkAP4BDAJdA0MFvwAAAEkAVgBuAHcAigCqAMoBEgFQBdgF8P97/+cABgATACgAYQBpAOkBNQFNAqUEDP8+/9oAWwC5AMgBGQEZARkBwARbBKcFW/4//53/wgAVALcBCgG8AcEFMgWO/YH/of+uAAwAJgAxAD0ATgBWAGIAgwDBAMkA8QDyAn//fwBIAFMAdwDFAR0BIAEmASgB1gIZAn4CfgPTAC4AQQBdAGsAdQCfALAAsgC6ALsAvQDWANsA4ADlARQBGwFKAWIBkQHyAgwCZALPA5sDtAPUBAEEqQAWACMAJQAqAHQApQC2AMwAzQDPAQUBIAEwAVABagFvAZcBnQHgArAC7AL3BAgEgwT7BP0FJv7g/vv/Tv/1ABgAGgBMAHoAfwCRAKMAswC0AM4A1QDyAPMA9gEQATgBaAGhAbAB4AHsAgkCIgJPAnAClgKlAq0DTgORA8EENQRCBGsEzQTaBYYFiwdhB/78pv6T/q3+0f+3/9EAAwAOABgAJgBGAGkAgQCPAKUAvwDTANUA2QDdAOIBGQErATgBOwFaAV4BaAFzAYgBlAGtAcUB0QHqAfICAAIAAgACIgI7AkQCTwJvAnICfgKCApMClAKlAs8CzwLQAtoC3QLrAvUDBQMiAzYDcQOhA7ADuAPQA+YEEAQmBC4EMQRPBFoE/wUyBTIFRwVTBagFqwXCBfAGPAZkBnAG6AeCB4QIzP0q/d7+AP5o/rD+s/+qAAgAWQB6AJEAngCiAK8AtAC7AMoAzADOANkA4AD0ARQBGgEhAScBKwE5AUYBSwFNAVcBXAFlAYIBhwGSAZgBmwGiAa4BxQHFAdECBwIiAisCQQJTAmECZQKEAocCjQK0ArQCugLJAtYC2ALtAvUDFwMjAysDMQNJA1oDWwNuA3EDdAN+A4QDkQORA6oDzwPTA+cD6APtBAgEFwQeBHUEegSZBKcEtATRBUwFbQVtBaIFvwXABdEF/AX8BgIGGgYcBi8GagaoBuIHBgc2B1AHiQfUB/MIcAEcASoBGgEgAAAAAAAAAAAAAAAAAhkACwAeAqoCFAR/Ae0AAAAdAQQADwCRACsBiAFTARIB8wA/A/4BaAEOBH8B7QNuAxUCGQQTAAAAAAZABLAAAAJ0AbsANQHFAH8GAgMBAAAE4ACyAdwC4ATDAj0A1QFgARkEpwNuBcoCIQCrBCYAkAK8ArsBQgC0AjwCVgKcAwAB5QGoAOUAawB4AJQBawFzAKsB7QE6AX0BNwF/ANQCFgNTAYQAPP+iAgQBCQFJAfAAbgMVAIEEZABeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATkA3ADp/p4EDQR8ASsAuACWAFkArADfAakA+gEF/+wAFwADAFUAYQAEAIwAowCFACgBIABdANYAfwEmARkBBAFsBs8AtAEGAAAHNwY+BHoA8AD5AOkFugQmBEIAAP/n/mkEngTj/zf/LQEgAQUBIACoAHQAaABHAPIA5QDZAL0AqABoAEcAXABIAAoAKAAyAEEAUABaAGQAfQCHAJH/sP+c/4P/ef9vAMsBIAD6ASwB+gGgANUAuABcADwAyADIAI8A2QGLALMARwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP5kAMAA6gEYASUBMgOwA+0FdgWQBaoFtAW+Bc0GMQB4AIQAmwDMAOIA9AEKASABYwDRAOoA9wEIAUIAGQAsADQAQQA4AEgAWABsAlkDvQBDARoAcADTACgANwBCAFAAWgBkAHMAeACCAIwAnAClAL0AzgDwARABXAC+ANgBAgEXASwBYwDqAQgAQQBLAFUAXwBzAKYBCQGDAbMAQQBkAB4AKgDrAPoBDgE4AnQALABAAIIAlgC2AMAAzADcAOYA8AD/AQoBIAEsATsBRAFWAWMA9wBXAGQBEAE2AFABsQAA/7YAOQBOAEQDzADlACQBEABCASIBpADwAGAA4AAOAB0AOQXjAQIALP5O/zgCaQO9ARYA/wAOAKAAVAAbAD0BcQBBAA8AUAD9ABUBTwA1/lIALADTAABAQVRAPz49PDs6OTg3NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEALEUjRmAgsCZgsAQmI0hILSxFI0YjYSCwJmGwBCYjSEgtLEUjRmCwIGEgsEZgsAQmI0hILSxFI0YjYbAgYCCwJmGwIGGwBCYjSEgtLEUjRmCwQGEgsGZgsAQmI0hILSxFI0YjYbBAYCCwJmGwQGGwBCYjSEgtLAEQIDwAPC0sIEUjILDNRCMguAFaUVgjILCNRCNZILDtUVgjILBNRCNZILCQUVgjILANRCNZISEtLCAgRRhoRCCwAWAgRbBGdmiKRWBELSwBsQsKQyNDZQotLACxCgtDI0MLLSwAsBcjcLEBFz4BsBcjcLECF0U6sQIACA0tLEWwGiNERbAZI0QtLCBFsAMlRWFksFBRWEVEGyEhWS0ssAFDYyNisAAjQrAPKy0sIEWwAENgRC0sAbAGQ7AHQ2UKLSwgabBAYbAAiyCxLMCKjLgQAGJgKwxkI2RhXFiwA2FZLSxFsBErsBcjRLAXeuQYLSxFsBErsBcjRC0ssBJDWIdFsBErsBcjRLAXeuQbA4pFGGkgsBcjRIqKhyCwwFFYsBErsBcjRLAXeuQbIbAXeuRZWRgtLC0ssAIlRmCKRrBAYYxILSxLUyBcWLAChVlYsAGFWS0sILADJUWwGSNERbAaI0RFZSNFILADJWBqILAJI0IjaIpqYGEgsBqKsABSeSGyGhpAuf/gABpFIIpUWCMhsD8bI1lhRByxFACKUnmzGUAgGUUgilRYIyGwPxsjWWFELSyxEBFDI0MLLSyxDg9DI0MLLSyxDA1DI0MLLSyxDA1DI0NlCy0ssQ4PQyNDZQstLLEQEUMjQ2ULLSxLUlhFRBshIVktLAEgsAMlI0mwQGCwIGMgsABSWCOwAiU4I7ACJWU4AIpjOBshISEhIVkBLSxLsGRRWEVpsAlDYIoQOhshISFZLSwBsAUlECMgivUAsAFgI+3sLSwBsAUlECMgivUAsAFhI+3sLSwBsAYlEPUA7ewtLCCwAWABECA8ADwtLCCwAWEBECA8ADwtLLArK7AqKi0sALAHQ7AGQwstLD6wKiotLDUtLHa4AjYjcBAguAI2RSCwAFBYsAFhWTovGC0sISEMZCNki7hAAGItLCGwgFFYDGQjZIu4IABiG7IAQC8rWbACYC0sIbDAUVgMZCNki7gVVWIbsgCALytZsAJgLSwMZCNki7hAAGJgIyEtLLQAAQAAABWwCCawCCawCCawCCYPEBYTRWg6sAEWLSy0AAEAAAAVsAgmsAgmsAgmsAgmDxAWE0VoZTqwARYtLEtTI0tRWlggRYpgRBshIVktLEtUWCBFimBEGyEhWS0sS1MjS1FaWDgbISFZLSxLVFg4GyEhWS0ssBNDWAMbAlktLLATQ1gCGwNZLSxLVLASQ1xaWDgbISFZLSywEkNcWAywBCWwBCUGDGQjZGFksANRWLAEJbAEJQEgRrAQYEggRrAQYEhZCiEhGyEhWS0ssBJDXFgMsAQlsAQlBgxkI2RhZLgHCFFYsAQlsAQlASBGuP/wYEggRrj/8GBIWQohIRshIVktLEtTI0tRWliwOisbISFZLSxLUyNLUVpYsDsrGyEhWS0sS1MjS1FasBJDXFpYOBshIVktLAyKA0tUsAQmAktUWoqKCrASQ1xaWDgbISFZLSxGI0ZgiopGIyBGimCKYbj/gGIjIBAjirkCpwKninBFYCCwAFBYsAFhuP+6ixuwRoxZsBBgaAE6LSyxAgBCsSMBiFGxQAGIU1pYuRAAACCIVFiyAgECQ2BCWbEkAYhRWLkgAABAiFRYsgICAkNgQlmxJAGIVFiyAiACQ2BCAEsBS1JYsgIIAkNgQlkbuUAAAICIVFiyAgQCQ2BCWblAAACAY7gBAIhUWLICCAJDYEJZuUAAAQBjuAIAiFRYsgIQAkNgQllZWVktAAACAQAAAAUABQAAAwAHAEK0AgH+Bge4Aj9AEwAFBP4DAAoHBP4BABkIBgX+AgO8ASYACQGwARgAGCsQ9jz9PE4Q9DxN/TwAPzz9PBD8PP08MTAhESERJSERIQEABAD8IAPA/EAFAPsAIATAAAABAEEBhwJpAqAAAwAyQCEwAUABAgE3AAADEAMgAzADBANEBRAAIAAwAAMAGQSlbBgrThDkXU0Q5l0AL+1dMTATESERQQIoAYcBGf7nAAADAJYAAAViBboAEwAgACwA2EA/dyoBaA54KuYE9gQECSEVCQYoLCElFhIfFU8VAjAVrxUCFRUUIyIlEhMIIBQlAQACGydwBoAGAgZLKCevDAEMuP/AswkLNAy4AoxAITAuQC5QLmAucC6ALpAuoC4IIC4wLgIuFCIgACATMBMCE7gCi7MtMVMYK04Q9F08Tf08TRBdcfYrcU3t9F3tAD88/Tw/PP08ETkvXXFDXFi5ABX/gLIdORW4/8CyGjkVuP+AsRM5KysrWTz9PAEREjkAERI5MTABS7ALU0uwD1FaWLEKIDhZAV0AXRMhMh4CFRQGBxYWFRQGBgcGBSEBETMyNzY2NTQmJyYjAxEhMjc2NjU0JiYjlgJKrquHWm9fhpBdoXZK/uX+DQEowq0qTFdLSizRqgESoCtCU0B5ygW6HVyZX2esKye8f2S9cQ0IAgTG/q0FCVdHRFUJBf25/ngJDF1OQlwqAAACAJMAAAGsBboAAwAHAHe5AAn/wEA/EQo/QAlQCQKACbAJwAnQCe8JBR8JYAl/CaAJsAkFAwYHAAUEAw8AAUAA0ADgAAMAXQIBAAYFBgcECgIHJgEEuP/AQAkhJDQEGQg/PBgrThD0KzxN/TwAPzw/PD88/V1xPAMFEDw8EDw8MTABcV1yKxMRIREBESERkwEZ/ucBGQS2AQT+/PtKBCb72gABAJMAAAGsBboAAwBTuQAF/8BAKREKP0AFUAUCgAWwBcAF0AXvBQUfBWAFfwWgBbAFBQIBAAMACgIDJgEAuP/AQAkhJDQAGQQ/PBgrThD0KzxN/TwAPzw/PDEwAXFdciszESERkwEZBbr6RgACAEH/6AQnBD4AFAAcAaO5ABD/+EBGCzmZCZoNlhCoBacKuwm7DbgaCAgUAUgCRwZGCk8eqA22BrYaxwrIDNYK2Az4B/cNDRwPHBVAGx00FUAOETQPFb8VzxUDFbj/wLMPHj8VuP/Asw4XPxW4Ao1ADA8OElAOYA4CDhgSAbj/wLYZGzQBMxIAuP/Asx0gNAC4/8CzIik0ALj/wLMrLTQAuP/AsxgcNAC4/8BADw4PNKAAAQAAEAACAF8SErgCdLMECxIYuAJ0QCQLBwAhAS8VIU8OAQ4aLx5fHm8enx4EHg8hCEANDzQIGR1pQRgrThD0K03tThBd9l1N7fTtAD/tQ1xYQBQYQCgUPxhAHg8/GEAbED8YQBwRPysrKytZP/1DXFi5ABL/wLMoFD8SuP/Asx4PPxK4/8CzGxA/Erj/wLIcET8rKysrWfRdcSsrKysrQ1xYuQAA/8CyEjkAuP/Ashc5ALj/sLMJCj4AuP/AskEhPysAKysrWeQrERI5XUNcWEAUDkAPHj8OQBwRPw5AGxA/DkAOFz8AKysrK1kvPP0rK3IrKzwBETMxMAFdcQBdKwEFBgYjICcmNRAAMzIAAyEWFjMyNhMmJiMiBwYXAvoBGDbpr/7rhWkBFNPtARIG/UADgmFCWicDeFZcPDwBAVIvmqG1kd0BCAEr/sf+vX2LSAFsen9DQ3MAAAEAH//oApEFnQAZAM1AKSAAIAEjCikPOg5KDlkPBxkVABgDFhUAFxITFAEXEgIUARgDCQcKBwwYuAEBQA8AF6AXsBcDYBegF8AXAxe4AQSyFQEUuAJ0swAVBge4AnRADgwLCS8KLwAALwFfAQIBuAEEQCgYAyYXElUVPxSfFK8UA2AUgBSQFNAU8BQFABQQFCAUMBQEFBkaeKAYK04Q9F1xckuwN1NLsDtRWli5ABT/wDhZPE38PP089F08EPQZ5AAYP+0/PP08EPRdceQREjkRMw8PDw8xMAFdARUjERQWFjMyNxcGIyImJicmNREjNTM1JRECesALJxwnShhifEx6OQsJgYEBGgQm4P5UgiscG9oqM1FFMZUBz+DTpP6JAAEALAAABLkFugAHAHJAIy8JMAQwBVAJcAmACZAJBwYBBQIlBAMCBwAICRcXGgR/BQEFuAEtQAoGByABMAB/AAIAuAEtQBEDDlACcAKAApACBAIZCP2sGCtOEPRdS1FYsQJAOFk8TfRdPP089F08RWVE5AA/PD88/Tw8PDEwAV0hESE1IRUhEQHf/k0Ejf5OBML4+Ps+AAIASf/oBC4EPgAjADIBcUBoBxoIHAUdFhpKG0gcSSXbEN8RCTYZRhlXJmYZZyaGJpIZkxqmGrkbxxrIGwwGBg0VFgYZFicGKRVZGXcChgKmBrUGxgYMvzTZEAIdJDIxESwNJEArLjQkQCIoNCRAGR00byT8JAIkRh24/8BAMA4PND0dAQAdEB2wHfkdBB0dLAEzAEAODzQPAB8AAgBVIUAcET8hQBsQPyFAGBo0IbgCdLUEBwwNCiy4/8CzHBE/LLj/wLMbED8suP/AsxgaNCy4AnRAQBQLHjEmCCkJKA1ZHwyfDAIfDAH/DAEMQA4WNAwaTzQBNGAAAQCOMAEBATMpIV8XAd8XAU8XXxdvFwMXGTNpQRgrThD0XXFyTe30ce1dThBd9itdcXJN7fTk/TwAP+0rKys/PD/9Kysr9F0r5BI5L11xK7EGAkNUWLIvHQFxWe2xBgJDVFi5ACT/wLcbHTRUJGQkAl0rWV0rKysREjkDDhA8PDwxMAFxXQBdcQEnNjYzMhYWFQMUFhchJicmJwYGIyImNTQ2Njc2NzU0JiMiBgEGBgcGFRQWMzI3Njc2NQFl/yvSz7y4SwMbJf7qCxAHA0ikXaS9VpuSxUxQb0tUAV426iQ3WERMRTMQCwLiLpqUWYm3/riMhUwcNxkIRkayiFqNSxwlIBxRRTv+0hIyGCc8O1YyJjckZQABAIcAAAM3BD4AEACoQCiXBQEJDgFTBWYFdQUDLxJYDmgOcBIECgkPDB8MAo8M/wwCPwxPDAIMuAJ3QCkHBwEACgMCBgooAAkQCTAJcAkECRp/Ep8SAl8SfxKvEtASBBIQACYBA7gBKUALAgKAAaABAgEZET+5ARwAGCtOEPRxPE0Q7RD9PE4QXXH2XRlN5AAYPzw/PD/tXXFyOTIxMAFdAF1xAEuwF1NLsDVRWlixCjI4WQBdISERIRU2NjMyFwcmIyIGBhEBoP7nAQVDa0RgWVdHPTtSLwQml2tENfUuQar+8QABABgAAALmBdMAFgC4QDI2BAEqBCAQIBFZBIAYBQgEvxgCFRYRAhQSFhEOEw8AEA4TAQAQAhQJCA8LAV8L/wsCC7gCdEAKBgERDxYB/xYBFrgCdEAeEAAAAfAAAQAGExQKCTM/CE8IUAgDCCgQLxFfEQIRuAEEQA0OEyYCFF8AoBbAFgIWuP/AtgkMNBYZF3i5AmkAGCtOEPQrcTxN/Dz9PPxdPPRdGeQAGD88P11xPP1dcTw//V1xOTIPDw8PMTABcV0AXRMzNTQ2NjMyFwcmIyIGFRUzFSMRIREjGJw5mXV4cyZDPj010tL+55wEJlCGhFMkxBA5UUvd/LcDSQAAAQCYAAAFIwW6AAkBzkAOCQMGCBkDFwgEEggCAwO4/wCzEgs/A7j/wLNbXTQDuP/AQCpTVDQDMgcIFAcHCAMIAgIHAwkEAgIJBwgDBEBbXTQEQFNUNAQyBs8FAQW4AotAGUALUAtgCwNwC4ALAqALwAsCIAswCwILCAm4/8CzW100Cbj/wEAOU1M0CTIBIAAwAMAAAwC4AouzCjF1GCtOEPRdPE39Kys8TRBdXXFy9l08Tf0rKzwAPzw/PAEREjk5ABI5OYcuKysrK4d9xLEGAkNUWLkAA//gQAkOJzQIIA4nNAO4/8C3CQ00CEAJDTQAKysrK1kxMENYuQAD/4C2CzUIgAs1A7j/wEA9Gi40CFMaLjQFAxYDMgNAAwRGA4UIkAigCLII5AMGxAPPCNoIAyADLwg0AzsITwiSA58IoAOvCLADvwgLB7j/wEAJMzU0AkAzNTQHuP/gQA0vMjQCIC8yNAIHFDUHuP+XQAkhLjQCVCEuNAe4/8BARh4gNAJUHiA0CAIHBxgCAxcHLAInBzsCMwdOAkAHXAJWBwkUAhsHTQJFB5oHqwfLAtkC6ALnB/kCCycCKAdKB3gHiAesAgYBXXFycisrKysrKysrKwBdXXFyKysrK1kAXTMRIQERIREhARGYASACWAET/tf9sQW6/C0D0/pGA7z8RAAAAgBS/+gEmgQ+AA0AGQCXQEjoAecI9xP3FQTHAugFAhIZBRkJAlkQVhNWFlkYlwKYBpgIlwy4CdUC2wXcCdUM5wXnBugNEKcIywLMBsMIxgwFdQiJBoQIAxG4AnSyCgsXuAJ0QBQEBxQ5BxpgG3AbAhsOIQAZGlhBGCtOEPRN7U4QcfZN7QA/7T/tMTAAcV0BXXFDWEAJaRBmEmYWaRgEAV1ZAF0BXRM0EjYzMgAVFAAjIiQmJRQWMzI2NTQmIyIGUor9nPEBNP7J7JL+94oBIJZubpWVbm6WAiKMAQaK/sfv8f7DhP+onqiooJyoqAAAAQB+AAAGmAQ+ACcBO7kAKf/AQF0RCj8FBgYMFQYWDDQDNAg0GDQjRAJFCEUYRCMMIAMvKVMJYCmAKZ8ppAanB6YMtQa1DLAp0CngKQ4AKS8pUCmfKb8p3ykGKUAaHDQ/KVApgCnQKeApBQchBAcaHRa4AnSyCgchuAJ0QB4EBxAREScbHBwmJwoBAAYPECYSEUBaNWARAW8RARG4AkZADxobJh0cQFo1bxwBYBwBHLgCRrQlJiYnAbgBKbIAACe4/8CzDwk/J7j/wEA2EQo/J0BaNSdAQTUnQDw1J0AkJzQnQDo9NC8nzyffJwMPJx8ngCcDACcgJzAn/ycEJxko4zwYK04Q9F1xcisrKysrKys8TRDtEP089l1xKzz9PPZxXSs8/TwAPzw/PDwQPBA8EDw/7T/tARESOQAREjkxMAFyK3FdAF0BKxMhFTYzMhYXNjYzMhYXFhURIRE0JyYjIgYGFREhETQmJiMiBgYVESF+AQOLwGaWMEaiXHWiKB3+5x0nUTtoLv7nHj82QWgt/ucEJpGpVFVVVF9cRJj9WQJfni48SIuW/gICRptaLEaEmf38AAEAjAAAAbQFugADAG+5AAX/wLMyNDQFuP/AsyMlNAW4/8BAPxQXNAAFQAVQBYAF4AUFHwVgBXAF8AUEgAUBAgECAwAIAgPZAQAAsADgAAPAAPAAAiAAMADQAOAABABuBDGfGCtOEPRdcXI8Tf08AD88PzwxMAFdcXIrKyszESERjAEoBbr6RgABAJEAAARZBD4AFgCfQBgHExcTWghoCAS4BAE0CDQQRAhED+kQBQa4AnRAHREHDg0GDAsLAQAKAgEmFgBAICQ0rwAB/wABABoYuP/AQBYiJDSQGKAYAnAY8BgC7xgBGAoLJgwOuAEpsg0NDLj/wEAPICQ0oAwB8AwBDBkXPzwYK04Q9HFyKzxNEO0Q/TxOEF1xciv2cXIrPE39PAA/PDwQPD88P+0xMABdAXFdISERNCYmIyIGBhURIREhFTYzMh4CFQRZ/uckUTlJdCv+5wEFi9Ndmk8fAh6sZThQhLL+HwQmnLRDaIR7AAABAI3/6ARTBCYAFgCcQBdXEWcRlgUDCQYZBjwCPBFLAksR5wIHD7gCdEARBAsWAAoVFBQKCQYTFCYVFQC4ASlADhZAICQ0rxYB/xYBFhoYuP/AQBYiJDSQGKAYAnAY8BgC7xgBGAoLJgkIuP/AQA8gJDSgCAHwCAEIGRc/PBgrThD0cXIrPE39PE4QXXFyK/ZxcitN7TwQ/TwAPzw8EDw/PD/tMTAAXQFdITUGBiMiJiY1ESERFBYWMzI2NjURIREDTjq9aWuqTAEZH1I/SHIqARmfVWJeqpYCoP4Y4GU7T3XkAcD72gAAAf/9/+cCOwXTAAMAOEAdAAEBSQIDFAICAwIBAAMACgHrAhoFA+sAGQSTbBgrThD0Te1OEPZN7QA/PD88hwUuK30QxDEwBwEzAQMBa9P+kRkF7PoUAAABAJIAAARZBboAFgCyQCsPAR8BOQEzAjMQQgFCEd4B+QEJBwUWBSQCWBFoEQUBAQITFBESEwMUAgEPuAJ0QB0DBwkKChQVChYAAAsKJggJQCAkNK8JAf8JAQkaGLj/wEAWIiQ0kBigGAJwGPAYAu8YARgAFCYWFbj/wEAPICQ0oBUB8BUBFRkXPzwYK04Q9HFyKzxN/TxOEF1xciv2cXIrPE39PAA/PD88PBA8P+05OREXOQMOEDwIPDEwAV0AXQERNjMyHgIVESERNCYmIyIGBhURIREBq4i9YZxPHf7nIFE9Rm4z/ucFuv3ln0hwiI/9kQIxp1o1RImG/ewFugAAAgCH/+gElAW6AA8AHACduQAS//hAMQs5NxtHGwISVgZWClYWVhhZHPcHBjUEOw07EzUbRQRLDUsTRRuUB5kJCgwOAQIBABq4AnSyBQcUuAJ0QBMLCw8AChchCBpwHgEeECkCAyYPuAEpQAwBcACAAAIAGR0/QRgrThD0cTxN7f085k4QcfZN7QA/PD/tP+0/PDEwAHFdAV1DWEALZgZmCmYWZhhpHAVdWQBdKzMRIRE2MzISERAAIyImJxUTFBcWMzI2NTQmIyIGhwEZgrLC/v79uVuxQBI0SXldg4RnZYYFuv3wlP7n/vn+8P7aW1mcAiqlT3Cfq7ahnQAAAQCT/+cFJAW6ABkAikA4BwgHCQcQFwgWCUcIRwkHVwlWEJYQlxGYFZsWpxC3FtcV5Qb2BgsNDAwBAAIHJRMJDAsgDc8OAQ64AotAIkAbUBtgGwNwG4AbAiAbMBugG8AbBBsBAiAAIBkwGcAZAxm4AouzGjF1GCtOEPRdPE39PE0QXXFy9l08Tf08AD/tPzw8EDwxMAFdcRMhERQXFhYzMjY2NREhERAOAiMiJiYnJjWTASgLE498foAaASgwgdiu0tl+FB0FuvzmvThabWeWrgMr/P7++NqWWWGbVX72AAABADD/6AQQBD4AKgLCQMAGEQYjCCcXERcjmBKYFJcnlSoJBxRGFAISuw25Dsch5SP4DfYiBikNVQ1lDZULlxKnIrkMB0EjQCREJmciZCaHEocUhiKDJAk3JkUGRgtKDU8PRiFCIgciJCcmNww1ITUiNSM1JAcGCgURCSEYDScMIiIiIwckIkAscwx4FHkVdil1KogVhCqaFZUqtCK0Iw2AAY8XjBiZKqkqsCwGFyEWQCEjNBZAHB80HxYB3xYBFjMIIVAljyUCJUAYHTQlGiy4/8BAFxEKP1AsATAsAS8sASweITAQARAzASEAuP/Asw8JPwC4/8CzEQo/ALj/wEAJCQ00ABkreLgYK04Q/CsrK03t9HHtThBdcXIr9itxTe30cXIrK+0AsQYCQ1RYQDUGAQEGARYBJiI2IUYhVAFZF2QBaRf2AQoBFwIEGiEiAigTDQwCKBpfBAEERigLUBoBGkYTBz/9XT/9XRESFzkREhc5ERIXOV1xG7kAIv/LsygqNCG4/8uzKCo0Irj/4LMeJDQhuP/gsx8kNCK4/+CzGRo0Ibj/4EAbGRo0aw0BNiJGIpgNlCLEItQiBiEiDA0EBBoAuP/AtRkbNAAzAbj/wLMXLT8BuP+wswkKPgG4/8CzIiU0Abj/wEAdGhw0AAEwAUABUAEEYAGAAfABAwABEAFQAWABBAG4/8CzExY0AbgBAUBNAAQBXwTwBAIERigLFkAZGzQWMxdAFy0/F0AJCj4XQDU3NBdAKy40F0AlKTQXQBocNA8XHxdfF28XBBdVGkAiJDQPGgFQGv8aAhpGEwc//V1xK/RdKysrKysr5Cs//V1x9CtdcXIrKysr5CsREhc5XXErKysrKytZMTABcV0AcXFxcV1dQ1xYuQAk/8lACQsSPw8oCxI/Ibj/7LYNOQwUDDkhuP/ssgw5Irj/6rELOQArKysrASsrWQBxXRMlFhYzMjc2NTQnJickJyY1NDYzMhYXBSYmIyIHBhUUFxYEFxYVFAYjIiYwARoSbmNtNyUUFUn+rFt+2uXa1Cj+9xFfWG8wIBwmAcFZWPTv2f0BLytSVSgcLyAVFBFLPlaZiryOizE+Qh8WIx4VHGZKS4aS0rAAAQAzAAAEDAXAAB0BP0BftRi2GrkbygTHGNAY0BnQGghDG0McQx1WGZsElRiqBKYcCAYaIAAoBjcaSARDGEMZQxoIJBgkGSQaAxYmBFYEiBicG5wcnB2qHKodCBIAHRAdIB0xHXYdhB2QHdYdCB24/8BAFhQVNB0CEAwPHRAAIAACIAAwAEAAAwC4/8CzEhY0ALgCobMCAQwPuAFWQCMfDC8MAgxAEhY0DKYTBQnYFhYBTwABABofD9gQdwIZHtPCGCtOEPRN9O1OEPZxPDxNEO0AP/0rceQ/PP0rXXE8ERI5AREzK11DXFi5AB3/wLIROR24/8CyDzkduP/AQA4QOQQIEDkFCBE5BAgROSsrKysrK1mxBgJDVFhACwkbGRsCGxMBBBMAABESORESOV1ZMTABXUtRWL0AG//gABz/4AAd/+A4ODhZAXFdXV0BESE2Ejc2NzY1NCYjIgYHJTYkMzIWFRQGBwYEBgcEDPwnEKDsvis6ZVlYaAj+6BkBCMbZ+EdNM/72RxYBBf77lAEJ27E/V1VeZWp7HOjK6q5js2JB9FAmAAADAFP/5gQXBcAAGAAkADABDbUwCB0fNCa4//hAbB0fNMcRxxPXBdcHBHUQdhSEEAMmACoMNgA7DEYATAxuBGMIZxFoFXcnhyeXDZgYpA2pGKkaph6nJ6YsqTC5GrceF3cThhOGFIcnBJcMAQyXAAEAHC6YDAEMKwmXAAEAJQMuQBIWND8uTy4CLroCjgAc/8BAEBYYNHAcgBwCoBwBHBwGEii4/8BACRIWNDAoQCgCKLgCjkANEg0/Ik8iAiJAEhY0IrgCjkAaBgUf2Al3K9hPDwEPGjIZ2AN3JdgWGTHTwhgrThD0Te307U4Q9nFN7fTtAD/tK10//V0rEBE5L11xK+1dKwEREjldERI5XQAREjldOV0xMAFxXQBxXSsrASYmNTQ2MzIWFRQGBxYWFRQEIyInJjU0NhMUFjMyNjU0JiMiBgMUFjMyNjU0JiMiBgFIbWPl09HnamB6f/7918iFnXa5X09QYF9OUWAad1lXcnRZZ2UDFy6hYKTW1qRmnyoxvHvL/ml82HfHAVFUXl9UT19g/T10gn12Z32OAAACAFb/5wQOBcAADgAgAJNAS3gKiAqnAaoHqgmnDrcJyAkIVhFZFlkaVh9nEWgWaBpnHwg5AjkGNgk2DUkCSQZFCUYNpwnLAskGxAnEDdkC2wbUCdQNERAYIBgCGLj/wEAlEhY0GKYIDR8PLw8CD0ASFjQPpgAFHdhPBAEEGiIU2AsZIdPCGCtOEPRN7U4Q9nFN7QA/7StxP+0rcTEwAV0AXV0BMhcWERAHBiMiABEQNzYXIgYHBhEQFhYzMjY3NhEQJiYCMtV4j5B31db++pB31TNQFh00TzMzUBYdNE8FwJi0/l/+YLaWAUkBpgGetpbpQVRt/v7+/sFAQVRsAQIBAsFBAAEATf/nBBsFwAApANlAMocVyRUCexyLHAKmA6kFpxS2A7oFthTaGN0ZCBYUAY0WjRcCIQoNAAQBFxMWIR8NEAwKuAEkQAxPDQFADY8NAg0NARa4AQJADx8TLxMCE0ASFjQTphsFAbgBVrUQBCAEAgS4/8BAMhIWNASmJw2wDMAMAgwMFhDYfx+PH58frx+/HwUf4AfYTyQBJBorFtgXdwHYABkq08IYK04Q9E3t9O1OEPZxTe30Xe0ROS9dAD/9K3HkP/0rceQROS9dce0BERI5ETkAERI5ERI5ERI5XTEwAXFdXQBdEyUWFjMyNjU0JiMiBzcWNjU0JiMiBgclPgIzMhcWFRQHFhYVFAAjIiRNARANclFXd3JSNksfcnhYSUhmC/79G23Dec99Z9N+l/7m0sf++gGFIWhuhHBqfBXlA2lXSlhkYCyFn1uEbIjBcxu8hcH+8OUAAQCiAAADJgXAAAkAVkAJawJ7AosCAwIEuAEps18FAQW4Al63CAkFAQAMCQC7AVgAAgABAl1ADQUABB8EIASwBAQEGQq6AacBoAAYK04Q9F08TfY8/TwAPzw/PPRd7TkxMABdISERBgc1NiQ3MwMm/uea0W4BAjDkBCOQRf8kyYYA//8AQf/oBCcF0wImAAgAAAEHADcA6AAAADFAEQLgIPAgAiAgTyAC4CDwIAIguP/AQAsOETQgCyhIKwIBILkCNQApACsBKytdcXE1AAABAGv+UQJoBdMAEABGQA4oD6cDAggJAQAJEAASCL0BIgAJAAEBIgAAApRACgnzDJsgBDAEAgS4ApazEaVrGCsQ9l3t/fTtEO0APz8QPBA8MTABXQEjJgI1EBM2NzMCAhUUEhcWAmXBmaBjVoTAiWc9NSP+UecB8ukBIQEC4L3+0f5X7qT+qJtmAAABAEP+UQJABdMAEABJQBgnAicKZwJnCpcCpwKoDgcJCBAACBAAEhC9ASIAAAAJASIAAAKUtAjzBZsMuAKWsxJqQxgrEPbt/eTtEO0APz8QPBA8MTABXRM+AzU0AgMzFhIVFAcCA0VTRDocZom/l6dCS6z+UbK++N917gGpAS/X/h740e/+9P77AAACAMkAAAHiBCYAAwAHAC5AGwM4AQYGOAQKCa8DAgIGJgEgBTAFAgWvCM2rGCsQ9l08/TwQPPYAP+0/7TEwExEhEQERIRHJARn+5wEZAw0BGf7n/PMBGf7nAAABAJMAAAGsARkAAwAkQBUCOAAKAiYPAB8AIAAwAAQAGQRndhgrThD0XU39AD9N7TEwMxEhEZMBGQEZ/ucAAAIAQf/mBBQFwAAXACMA0EBYOxFLEWULegiJCKkFqQimDqYTtQC5A7UOuBG0E8UAyhHAExE0E1YLWQ1fEVITYBMGGQV3FpkX3RDfFAVoEwEABAEHGBIfGy8bAhtAEhY0G6YPBRAhICECIbj/wEAQEhY0IaYwCQFPCd8J8AkDCbgBT7OgAQEBuAEhtRAEIAQCBLj/wEAbEhY0BKYVDRjYTxIBEholAdgAdx7YDBkk08IYK04Q9E3t9O1OEPZxTe0AP/0rcfRd9l1x7StxP+0rcQEREjkAERI5MTABcV0AcV0TJRYWMzI2NwYjIgI1NAAzMgAREAAjIiYBNCYjIgYVFBYzMjZdARAKVEVXehFqn637AQnN3wEe/tbvrNQCXn1STmdwVFFvAVMeU1Cg/HsBC9bfARH+p/51/m7+nLcDHIiWe4yOhYAAAgAAAAAFvwW6AAcACgFBuQAH/9hACTc5NAYoNzk0B7j/wEAJKDU0BkAoNTQHuP/YQFAhJzQGKCEnNCkAKgQqBSgKLww4ADcFPwxqAGoCZQNmBWgIZwroAw9KBgECCAkBAwoJCQQHCQEBIAAHFAAABwYJBAQgBQYUBQUGCApAGh0+Crj/wEALGh00CiUCAwMGBAm4AbxADgYHAgUEBAEACAwXFxoAuAJhQAsfAQEgATABgAEDAbgCJEAJHwkBMAmACQIJugIkAAQCYUAJIAUBBRkLXmMYK04Q9F1N/Rn2XXH0XXEY/U5FZUTmAD88PBA8PzxN7RESOS88/SsrPIcFLiuHfcSHLhgrh33EBxA8PIfExLEGAkNUWLQJNAkNNAArWTEwAUuwC1NLsB5RWli5AAP//rIIBAq6//4AB//8sQYEODg4ODhZAXFdKysrKysrISEDIQMhASETAwMFv/6+gP22ef7GAjsBOSrKxgFN/rMFuvyKAiD94AAAAgBU/+gEYQW6AA8AHACQQC0SWQZZClkSVhZWGFkcmAeZCfgJCXAegB4COgM0DDoVNBlKA0QMShVEGZkJCRS4AnSyBQsauAJ0QA4LBw4PAAEAChcpDg0mAbgBKUAPDwAajx4BHhAhCBkdWDwYK04Q9E3tThBx9jxN7f085gA/PD88P+0/7TEwAF0BcV1DWEANaQZpCmkSZhZmGGkcBl1ZISE1BgYjIgAREBIzMhcRIQEUFxYzMjY1NCYjIgYEYf77QbFat/77/sKyggEZ/RIvRHphiIRnZIecW1kBJwEIAQ4BGZQCEPxwqkxupaS3oZ8AAQBi/+cFvQXTACAA2EBGOB5LHlYHdgh2DIUIhAyFF4QbCQYXBhsSFxIbKBEoGCgaKB4ISAtbBFQJWgtqBHsEehh0GrYOthDHDcYQ1xDnEA4DHAYgALj/wEAfGjkfAAEAJQIBARYcLQYJEkAOEjQSS08TARNAFRg0E7gBKEAqFi0PAwABASAZXxMBEycSVgIfICADAhogIjAiAiIZJ6AKAQ8KHwowCgMKuAKMsyF+nxgrThD0XXFN7U4QXfY8Tf08EPTtcRESOS88AD/99Ctd5Cs/7RE5Lzz9cSs8ERI5MTAAXQFdXQE1IREGBCMiJAI1NBI3NjMgBBcFJiYjIgYVEBIzMjY3NQM/An5d/p+15v6qrMC5jdIBEQEzLP7aH6uAwuXovF27QwIb9/24WonBAWfT5QFkX0nlyjdsffby/vv++0k0ugABAJUAAATwBboACwCQQD0IBQQHCCUGEh8FATAFrwUCBQUJAwQlAgECCgklCwAIBwZLAwJICgALAQsaIA0wDUANAw0ECSABIAAwAAIAuAKLswwxUxgrThD0XTxN/TxOEF32XTxN9Dz0PAA/PP08Pzz9PBE5L11xQ1xYuQAF/8CyHTkFuP+Asho5Bbj/gLETOSsrK1k8/TwDBRA8PDEwMxEhFSERIRUhESEVlQQ//OkC4P0gAzMFuvj+u/f+cfcAAQCdAAAEpQWuAAUAPUAaUAcBAgECBAMlBQAIBAUaBwIDIAEgADAAAgC4AouzBjG5GCtOEPRdPE39PE4Q/jwAPzxN/Tw/PDEwAV0zESERIRWdASgC4AWu+0n3AAACAJYAAAW8BboAFQAhAPhAgjkPSQ9XB2oLagyqCacOoCO2DtgJCgYIBgoXCBYKNg5GDkYPBwgQCREUDhQPFBA2DjYPRw91DnkQ0woLeAl4GXYdiAmIGYYdBgkWFAkMDw5TDnUOhA6UDqMOBQ4gDQwUDQ0MDwwVDRcWJRMQFAFgFKAUAhQUACAhJQIBAg0ODhUACA64AbxAJwANEA0CDdQbJ6AGsAbABtAGBAaHcCMBICMwIwIjIRUgASAAMAACALgCi7MiMWMYK04Q9F08Tf08EF1x9l3t9F3tAD88PBA8Pzz9PBI5L11xPP08ARESOTmHLitdDn0QxAEROQAREjkxMABdAXFdXTMRITIWFhUUBgcWFhcTIQMuAiMjEREzMjY2NTQmJyYjI5YCb+vVgMLBYH1qs/6e1nJUXmY829VqPE9IJLTnBbpPyoKl1xw4hqv+4gE/q1kh/ZwDTiRYQkpbDAUAAAH//wAABVQFugAGAQ6zAAMBCLj/gLISOQO4/4BAVBo6NMAI1gHWAtkE2QUFdgJ5BHgFlwGWApkEmAWfCAgDAAsGLwhnAmgEYAh3AQcZABYGKQAmBkkARwZXAAcAAwICIAEAFAEBAAYDBAQgBQYUBQUGA7gCYkALBgAIBQQEAgIBAgW6AmEABP/AQA8SOQsEAX8EgATfBOAEBAS4AQtACn8DgAPfA+ADBAO4/8C1EjkLAwEDugELAAICYUAJMAEBARkHXmMYK04Q9F1N/Rn0XStd9F1dKxjtAD88EDwQPD887YcFLiuHfcSHLhgrh33EsQYCQ1RYuQAD/8CzECc0A7j/wLUJDTRUAwEAXSsrWTEwAXFdXV0AKwErAF0hASEBASEBAgv99AFBAXMBZwE6/fMFuvvDBD36RgAAAQBK/+YE8gXTACwB0EA9uRG4HbYoxiwEBxMHFRcTFxUYK2UFZSh0BngNdCjZDNYjDFkKVQ5VIlkjaAxmEmchaShnLHcdhh2WIQwSI7j/4LMeHzQjuP/gQGUZGjRRIlEjwSLBIwRxInEjgSKBI+Ei4SMGKwoqDSQiJCM5DTQjSwpLDUQiQyNqDWUjeQ16IokNiiKmCqcNqCITCQoJDQYiBiMZChkNFiIHIiMKDQQBF1YYQBkgNG8YAW8YnxgCGLoCZQAb/8BADBo5HxsBGy0UAwBIAbj/wEBJGiA0MAFAAVABYAGQAaABsAHAAQgB7gRAGjkQBAEELSoJGO//FwEXQBMXNBdLBycmGi4fJ6AQsBACEEsB7xEgADAAAgAZLdJTGCtOEPRdS1NYsQBAOFlN7fRd7U4Q9k3t9Cty7QA//XEr9F0r5D/9cSv0XXIr5BIXOV1dcXIrK0NcWLkAIv/gsxsdPiO4/9CzGx0+I7j/47ITOSK4/+CyEzkjuP/JshI5Irj/0EAPEjkNIBI5CiASOQogDzkiuP/oQA4MOQ0gDTkKGA05ChgTOSsrKysrKysrKysrKytZsQYCQ1RYQBU6CjoNNSI1I0sKSQ1DIkYjpgqpIgoAXVkxMABdcQFdEyUWFjMyNjU0JicmJyYnJjU0NjYzIAQXBSYmIyIHBhUUFxYEFhYVFAYEIyAASgEgGp+Hj5E9TDS57mCHf++pARQBFwf+2BN9fYFJLyw4AbDPdYz/AL/+6v7WAd0ckYh5UTRJGxIuO1Z5rnDDZvLKDXFjNSI5NCUvZm29i37cawEBAAABAJEAAAYZBboADAIYQAsLAyYIJgsDBAMBA7j/gEAJHDo0CiA6OzQJuP/gszo7NAm4/+BApBwuNAogHC40BgkICuMJ7AoEBAkKChMCHAQQCR8KIwIsBCAJLwpnAmgEZQlqCncCeASkCaoKtQm6CvYJ+goWnwSQCZ8KxgnJCtcC2ATWCdkK5wLoBOUJ6goNdwl4CoMCjASDCYwKkAIHWAtlAmoEZwloCnYCeQQHRAJLBEQJSwpXCFcJWAoHGAovDjQCOgQ0CTsKPw4HAwIMBAYJCQoVAhoEFwkHsQYCQ1RYQB8CBAMKCQUMBwcyBg4MMgAAAxADAghQCA00C1AIDTQDuP+AQA4LDTQIQA4nNAtADic0A7j/nEAQDic0AwsIAwEABAECBwoACAA/PDw/PBESFzkrKysrKytdAS/tENTtERIXORu4/ztALQMKCSAECAkJMgMEFAMDBAILCgoyAwIUAwMCCwgDAwwEAgIMCgoJCQcIHw4BDrgBDbMHBgUEugI4AAX/wLNbXTQFuP/AQBdTVDQFMgdAB38IAQi9fwMBA70LIAsMArgCOEASAQAAQFtdNABAU1Q0ADIfDAEMuAENsw0xdRgrEPRx7SsrEDzuEDwaGRD9cf1xPBoYEP0rK+4QPBDkcQA/PBA8EDw/PBIXOYcFLiuHfcSHLhgrh33EK1kxMAFLsBNTWLkACP/gsQsgODhZAV1dXV1dXXFyKysrKwArcV0zESEBASERIREBIQERkQG7AQoBBwG8/u3+3f7j/t4FuvwYA+j6RgSC+34Egvt+AAIAlQAABPgFugAPABsAd0AlBgW5FLkYA0cFAWcF1gUCEhElDQ4OABsQJQIBAg8ACBYnrwcBB7j/wLMJCzQHuAKMQBYfHTAdYB1wHYAdBR0QDyABIAAwAAIAuAKLsxwxUxgrThD0XTxN/TxNEHH2K3FN7QA/PD88/TwSOS88/TwxMABdcQFdMxEhIBcWFhUUBgYHBiMjGQIzMjY2NTQmJyYjlQHbAQ5Sfqpil05qycGir3ZDXkg1oAW6FiHdr4e4aREV/dcEwv5gLmJBUGgNCgABAGH/5wVeBdMAGgDWQE6GCYkUiRafAJgGxwnUA9QL9QMJJQkoDCgNKRQpFnUFdQmGBQgHEwcXFxMXFykCKgMlBQcoBZkFlwnJA8ULBT8BTwECAVIQAAHgAPAAAgC4/8CzERg0ALj/wLMKDTQAuAFaQBcYLQQIDkAOEjQOS18PAU8PAQ9AFRg0D7gBKEAiEi0KAw/vDlYA7wABTwECARowHAEcFSegBwEPBx8HMAcDB7gCjLMbflMYK04Q9F1xTe1OEF32XU3t9O0AP/30K11x5Cs//fQrK11x5F0xMABdAV1dXQEFBgQjIAAREAAhIBcWFwUmJiMiBhEQFjMyNgQ/AR9C/s3s/tz+iAF6ATQBDahkMv7bGqV2o8vIoHaqAhtb8OkBjwFaAW4BlZ9esEZyhOr++v7q7JYAAAEADAAABGAEJgALAZhAiygHyAQCmAe5AdwB1QfwDQUIBxoEGAYoBjcAOAhIAVkBfAF1BwomASsHNgE6B0YBSgeYC/gG+AcJJQQmByoKNAQ6CkMETgrDBAhMClQEWQpkBG0KeAF9CpQElge6CtUE3Ar8Cg0DBAcHCQoWBCAEKgozBD8KRgQJFQQZCjoKTgprCqcEtwTJCvYECQe4//CzEhg0BLj/2LMVFzQEuP/gQBUMETQBBAoHBAACAQQKBwQIAAkIAwO4/+C2KS00/wMBA7j/4EAPFiQ0AyYCCRQCAgkFBgsLuP/gtiktNPALAQu4/+BAFxYkNAsmAAUUAAAFBgUFAwIGCAkJCwAFuAJtsgYzCbgCbUALCGVPDZ8N4A0DDQO4Am2yAjMLuAJtQBLwAAEAABAAIAAwAAQAZQzEoBgrGU4Q9F1xGE3t9O0ZTRBd9hhN7fTtAC88PBA8Pzw8EDyHBS4rK3Erh33Ehy4YKytxK4d9xAEREhc5ABESFzmxBgJDVFi1CiAJITQEuP/gsgkhNAArK1kxMAArKytdcXFyAV1xcXIzAQEhExMhAQEhAwMMAX/+kQFXvMYBSv6YAYn+p9jaAiMCA/7cAST+Cf3RAUn+twAAAf/g/+cEMwXTAC0BCkBuBysBAlUcAdUcAQMIGwESAhUTdROFEwOaBboFygUDCAVIBQIeAyoIHyE0pxMB0RMBAx4RIRgOAfgOAQ4KKAclKApYCgIKCgMVGBoHFxcXAgkXFS0aDQAsCAEBDgABAy0sAxAPCUAICAwXASAAFxe4/8BAMwkONBcvEQcMJyNAdR6FHtUeA1cepx4CAx4oJyhnKAICKAgjGCMCCQAjHyYmQAkTNCYgJy88zSsBEDwYL19eXTNfXRDFX11dGhBN/cQyEMYrARgQ1hoZzRESORgvGs3UzQA/Tf3GX15dETk//cZeXRE5ERI5L1083TwQ1l1xPN08AV9dMTBdKwBfXl1dXV9eXV9dcV9dAQMmIyIHBgchByEGFRQXIQchFhcWMzI3EQYjICcmJyM3MyY1NDcjNzM2NzYhMgQzO1Wjo2U5GAImIP3mAQIB/SD+OBk0ZKDAcoKt/ty8gCmUIGIBAoMgdyp9vQE0qAWW/utVdUJjmxckIyabaz12dv7OQciIy5sYGy4jm8eGygAAAQAcAxgC8QXTAB4Ay0AliRMBVR1WHgJFHUYeAh0MCwUYBQ8OCwwKGAUdAxYAG4YcKwCGAbgBJkAUCRblFQnlQBUrsAoBCogPDgAVPha4/8BAJxETNBY+G3IgHEAaHDRPHF8cAhw+D+VADkAaHDRPDl8OAg4+AHIgAbj/wEAVERM0AT4KPg8JAT8JjwkCCRkfonwYK04Q9F1xGU3kGPQrGhn99F0rGhj9GfRdKxr9GPQrGeQAGD889F3kGuwQ7RkQ9vQY9BnkERIXORE5OQEREhc5MTAAXV1dEyc2NzY3JicmJzcWFyY1MxQHNjc2NxcGBxcWFwcnBvWXSE4fCBl2VRs7g2cYshsUQlpMNW+SeCQVmYc9Axh1UUoeCAQdFQqwNUCjZ0nDCB8pHbUZGIcpGmXfbAABAFcAAAQYBaYACwCGuQAE/+BAMQ8RNAoLGgs6BDgKSAVWC6oLvgvNC9kLCiELAQsDBwAfCy8LAi8LPwtPCwMLQBIWNAu6AqAAAwGstwICAQQHCAwIuAFYsy8HAQe4AmBADgJPAwEDGg0BABkM08IYK04Q9DwQ9nE8TfRd/QA/PD88EO39K11xPAEREjldMTABXSsTESEVBgICFyESEjdXA8F39oEB/vEH7cYEoQEFzHX+Sv4TwgEwAnj5AAEAW//nBDUFpgAdARBAKQgOIAw3EkUSSRmZDZ4OlxLaDgkSERMSIREjEoUSBQAEAQ0KDAwNEhERuAKgQBYODRQODg0SChQgATABQAEDUAGQAQIBuAFWtRAEIAQCBLj/wLcSFjQEphsNDLgCWkANHwovCgIKQBIWNAqmFLj/wEALFBY0IBQwFEAUAxS4AatAFBERHxAvEAIvED8QTxADEEASFjQQuAKgQBMPDw4EDxDgB9jQFwFAFwEXGh8OuAEhQBINdwG80AABQACfAK8AAwAZHtO5AUcAGCtOEPRdcU3t9OROEPZdcU3t9DwAPzwQ/StdcTwQ9l0r/Stx5D/9K3HkXXEREjmHBS4rDn0QxAEROQAREjkREjkxMAFxXRMlFhYzMjY1NCYjIgcnEyERIQc2MzIAFRQHBiMiJFsBGAx2TVh6eWF5YOSQAuf97ixeYrsBBGmP/sv/AAF5HV9vj5CHh2shAvv++fkv/vDZtY7C2gACACYAAAREBcAACgANAN9AOQwgDTkJDBkMKwxTDGsM4gwG7Q0BBgQWBCUEKA1IDVsNpw23DcYNCQECCAAMBg0HBQoLDQcADAwNDbgBrkAaAwQUAwMEAwIMBA0DDQIECgAHQA3ADdANAw27ASgACAACAbS2AAQEAAwMALgBWLQFjwoBCrgBAkASEAefB78HAwcaDz8CfwICAhkOugFMAUgAGCtOEORxEPZdTfRdPP08AD8/EPQ8/V08ARESORI5OQAREjkSOYcFLisEfRDEDw8PsQYCQ1RYQAstDD0MTQzNDN0MBQBdWTEwAV1dAF0rIREhNQEzETMVIxEBEQECfv2oAnzstrb+8P6vASf2A6P8Xvf+2QIeAfX+CwABAJkAAAXDBboACwGRQBoIBgESEgoKBQMCAwQGBgcJCgkICgUJCAkKCLgBt0ArBwYUBwcGAwQEIAUKFAUFCgoJAwMGCgMJAwgLBgYHBQQEAgECAAsLCAcIBLgCZLIFSAi4AmRAEgcaIA0wDQINAgsgASAAMAACALgCi7MMMWMYK04Q9F08Tf08GU4QXfYYTe307QA/PDwQPD88PBA8GRI5LwEREhc5ABIXOYcFLhgrBH0QxIcFLhgrCH0QxIcIEDwIxAMIEDwIPLEGAkNUWLUJIAsNNAO4/8qyCCc0ACsrWTEwAENYQBkmBicJkASYBqAEsATABAeEBqgE6AT2BQQJuP/gszdSNAm4/8BAJDdSNCUGPQp0A4YDmQOZCZoKqgO6A8kDCsED0AP8CgM9CkIDAnJxXSsBK3FdWUNcWLkABv/osxILPwa4/+hAEw8LPwQwDRY/BDAMFD8EIAsSPwO4/9CzDxk/A7j/0LMOFz8DuP/Qsw0WPwO4/9CzDBQ/A7j/0LMLEj8DuP/Qsg4TPwArKysrKysBKysrKytZAV0zESERASEBASEBBxGZASgCVgGO/dgCRv6B/m3wBbr9dQKL/cX8gQKw9f5FAAIAV//nBCoFwAAXACMAvUA7agt1CIcIlxmnBacIqQ6qE7kOthG9E8ARzxMNFQU2EUQQeha1AtIQ0BQHuwDPAAIABAEHGBIQGyAbAhu4/8BAHhIWNBumDw0fIS8hAiFAEhY0IaY/CQFACdAJ/wkDCbgBT7OvAQEBuAEhQCIfBC8EAgRAEhY0BKYVBQHYAHce2E8MAQwaJRjYEhkk08IYK04Q9E3tThD2cU3t9O0AP/0rcfRd9l1x7StxP+0rcQEREjkAERI5XTEwAV0AXQEFJiYjIgYHNjMyEhUUACMiABEQADMyFgEUFjMyNjU0JiMiBgQP/vAKVENZexBpnLD7/vjP3v7iASrup9v9oX5RTmhwVFFwBFMeVFCg/Xz+9NTh/vABWQGJAZMBZLv86YmVeouPhX8AAQC7BKgCgAXTAAMAQ0ATASASFTQBIB4kNFADAQADEAMCA7gBWUAJAQAD5QAQAgECuAJhtwFuABkE5nwYK04Q9E307V0Q7QA/7V1xMTABKysTEyEBu4oBO/7tBKgBK/7VAAABAAAAAuZnAAAAAF8PPPUIOQgAAAAAAKLjPB0AAAAAudW1E/r6/P0QAAgVAAEACQABAAEAAAAAAAEAAAc+/k4AQxAA+vr+JhAAAAEAAAAAAAAAAAAAAAAAAAA4BgABAAAAAAACOQAAAjkAAAKqAHMFxwCWAjkAkwI5AJMEcwBBAqoAHwTjACwEcwBJAx0AhwKqABgFxwCYBOMAUgcdAH4COQCMBOMAkQTjAI0COf/9BOMAkgTjAIcFxwCTBHMAMARzADMEcwBTBHMAVgRzAE0EcwCiBHMAQQKqAGsCqgBDAqoAyQI5AJMEcwBBBccAAATjAFQGOQBiBVYAlQTjAJ0FxwCWBVb//wVWAEoGqgCRBVYAlQXHAGEEcwAMBHP/4AMdABwEcwBXBHMAWwRzACYFxwCZBHMAVwKqALsAAAAAAAAAbAAAAGwAAABsAAAAbAAAALoAAAIiAAACxAAAAzAAAAU8AAAGXAAABvIAAAkAAAAJ5AAACuQAAAziAAAN0gAAD4YAABAOAAAQ+AAAEeAAABI2AAATNgAAFDIAABUSAAAYWAAAGf4AABucAAAcoAAAHfgAAB56AAAexgAAH1AAAB/aAAAgNAAAIHIAACG4AAAjMgAAJCIAACVoAAAmJgAAJoIAACfmAAApIAAAK4AAAC3UAAAupAAAL94AADG0AAAzTAAANIAAADU8AAA2rgAAN8gAADmSAAA6xgAAOygAAQAAADgA8gA8AG8ABgACABAALwBVAAAGThBvAAMAAgAAABQA9gABAAAAAAAAABAAAAABAAAAAAABABMAEAABAAAAAAACAAcAIwABAAAAAAADAAgAKgABAAAAAAAEABMAMgABAAAAAAAFAAwARQABAAAAAAAGAAAAUQABAAAAAAAHAAcAUQABAAAAAAAIAAcAWAABAAAAAAAJAAcAXwADAAEECQAAACAAZgADAAEECQABACYAhgADAAEECQACAA4ArAADAAEECQADABAAugADAAEECQAEACYAygADAAEECQAFABgA8AADAAEECQAGAAABCAADAAEECQAHAA4BCAADAAEECQAIAA4BFgADAAEECQAJAA4BJE9yaWdpbmFsIGxpY2VuY2VFRkpFRkUrQXJpYWwtQm9sZE1UVW5rbm93bnVuaXF1ZUlERUZKRUZFK0FyaWFsLUJvbGRNVFZlcnNpb24gMC4xMVVua25vd25Vbmtub3duVW5rbm93bgBPAHIAaQBnAGkAbgBhAGwAIABsAGkAYwBlAG4AYwBlAEUARgBKAEUARgBFACsAQQByAGkAYQBsAC0AQgBvAGwAZABNAFQAVQBuAGsAbgBvAHcAbgB1AG4AaQBxAHUAZQBJAEQARQBGAEoARQBGAEUAKwBBAHIAaQBhAGwALQBCAG8AbABkAE0AVABWAGUAcgBzAGkAbwBuACAAMAAuADEAMQBVAG4AawBuAG8AdwBuAFUAbgBrAG4AbwB3AG4AVQBuAGsAbgBvAHcAbgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvgBUA5oADwEBAB//wAOYsxAUMkC4A5mzDxMyQEEQA5UAUAOVAAIAsANNAMADTQACAG8DkQB/A5EAAv/AA0uyLTEyuf/AA0uzCg4yEEEQA4sAIAOLAIADiwADAKADiwABACADiwBAA4sAAv/AA4uzExYyQLgDg7IPETK5/8ADe7IwNDK5/8ADe7MQGDJQQRQDeAABA2UDbgAjAB8DfgNuAB4AHwNjA24AHQAfA2IDZAANAB//wANAsw8QMoBBEAM/AAEDPwMVACkAHwNBAxYAMgAfA0QDGgAbAB//wAN1sg4RMrn/wAN1sigqMkEKA0MDGAAyAB8DDwMNADQAHwMIAweyMh8guwNAAAEAQAOIswkLMkC4A4iyEBUyvQOFAwcAFAAfA4ADB7IXHw+9AwoALwMKAAL/wANUswkNMpBBDANUAKADVAACAB8DbgABAJ8DbgABAEADbrIJCzJBCgNFAxwAFgAfA2sDHQAVAB8DRgMeshUfwLsDkwABAEADkrMJDTJAuAM+sggzQLgDPrMNDjLAQQkDPgABALADjgDAA44AAv/AA5CzJjgyAEEmAygAMAMoAAIAIAN/ADADfwACABADigAwA4oAUAOKAG8DigB/A4oAnwOKAAYAAAOJADADiQACAC8DegBwA3cAkAN3AJ8DegAE/8ADFbIPEDK5/8ADFbIkKDK5AxkDGLIyHxC7AxoAAf/AAxqzCQ4yQLgDGLISEzK5/8ADGLMMDjI/vQNzAE8DcwACAEADdLMXGDJvuwMqAAEAQAMssxgbMkC4A3CyCQwyvQMXAxYAMgAf/8ADFrIOETK9AxwDHgAWAB8DHQMeshUfsEEfAx4AAQAPAx8AAQLKAtAAFQAfAtMC1QANAB8CzwLQAA0AHwLLAtAADQAfAs0C0AANAB8CzgLQAA0AH//AAtCzCQwyQLgC0rMJDDLgQRwC5QABAF8C3QCfAuUAAgK7AsMAMAAfAtoCuAAyAB8C2QK5AD8AHwLYArgAZAAfArkCuAAzAB8CurIhyB+4ArizIcgfQLgDm7INFjK5/8ACw7IrLzK5/8ACw7IfJTK5/8ACw7IXGzK5/8ACw7ISFjJBJQLCAsEAHAAfAtcCwQAkAB8CwQLAACIAHwK/AsAAGAAfAsACdADIAB8CtQI1ADsAHwK0AjUAOwAfAsQCvAAeAB8CtwK2ADgAHwKzsg7IH7gCsLIHyB+4Aq+yBsgfuAKusgDIH7gCr7JQLx+8Aq4CqwAaAB8CrbImGh+4AqizJiQfD7sCNQABAqUCdLIdHxJBCgKhAVgB9AAfAqAA2AH0AB8AEgKisjfIH7gCkLK8IB+5ApACkEAYN0AlQC1ApgMwJTAtMKYDICUgLSA3IKYgQRACjgAFAJ8CiwABAosCiwA3ACACiQAwAokAQAKJAJACibIEN7BB/QJ0AMACdAACAIACdACgAnQAAgBgAnQAcAJ0AAIAAAJ0ABACdAACAIACdADwAnQAAgA/AoUATwKFAAIAkAJ+AJACfwCQAoAAkAKBAAQAkAJ6AJACewCQAnwAkAJ9AAQAkAJ0AJACdQCQAncAAwBwAn4AcAJ/AHACgABwAoEABABwAnoAcAJ7AHACfABwAn0ABABwAnQAcAJ1AHACdwADAGACfgBgAn8AYAKAAGACgQAEAGACegBgAnsAYAJ8AGACfQAEAGACdABgAnUAYAJ3AAMAUAJ+AFACfwBQAoAAUAKBAAQAUAJ6AFACewBQAnwAUAJ9AAQAUAJ0AFACdQBQAncAAwBAAn4AQAJ/AEACgABAAoEABABAAnoAQAJ7AEACfABAAn0ABABAAnQAQAJ1AEACdwADADACfgAwAn8AMAKAADACgQAEADACegAwAnsAMAJ8ADACfQAEADACdAAwAnUAMAJ3AAMAIAJ+ACACfwAgAoAAIAKBAAQAIAJ6ACACewAgAnwAIAJ9AAQAIAJ0ACACdQAgAncAAwAQAn4AEAJ/ABACgAAQAoEABAAQAnoAEAJ7ABACfAAQAn0ABAAQAnQAEAJ1ABACdwADAOACfgDgAn8A4AKAAOACgQAEAOACegDgAnsA4AJ8AOACfQAEAOACdADgAnUA4AJ3sQPQQcUCfgDQAn8A0AKAANACgQAEANACegDQAnsA0AJ8ANACfQAEANACdADQAnUA0AJ3AAMAMAJ0AEACdAACAMACfgDAAn8AwAKAAMACgQAEAMACegDAAnsAwAJ8AMACfQAEAMACdADAAnUAwAJ3AAMAsAJ+ALACfwCwAoAAsAKBAAQAsAJ6ALACewCwAnwAsAJ9AAQAsAJ0ALACdQCwAncAAwCgAn4AoAJ/AKACgACgAoEABACgAnoAoAJ7AKACfACgAn0ABACgAnQAoAJ1AKACdwADAJACfgCQAn8AkAKAAJACgQAEAJACegCQAnsAkAJ8AJACfQAEAJACdACQAnUAkAJ3AAMAIAJ+ACACfwAgAoAAIAKBAAQAIAJ6ACACewAgAnwAIAJ9AAQAIAJ0ACACdQAgAncAAwKBAVgIAQAfAoABKQgBAB8CfwDsCAEAHwJ+ANgIAQAfAn0AsQgBAB8CfACmCAEAHwJ7AIIIAQAfAnoANwgBAB8CdwAmCAEAHwJ1ACAIAQAfAnQAHwgBsh83D0EWAjUATwI1AF8CNQBvAjUAnwI1AK8CNQC/AjUABwCvAjUAzwI1AN8CNQD/AjVAIgQPB08HnwevB78HBa8H4AcCDwZPBp8Grwa/BgWvBuAGAiBBGwINAAEAXwI1AAEAjwI1AAEAfwI1AO8CNQACAC8CNQA/AjUAAgA/AjQATwI0AAICNQI1AjQCNEAR7SDvKgHPKgG/KgGvKgGPKgFBCQJHAQQAHgAfAiAANwIBAB8BWEAMJj4f2CY+HzcmJz4fuAKOtuwXH7ImNh+4AbyyJjYfuAEpQCsmNh/sJjYfsSY2H6YmNh+CJjYfNyY2HzImNh8tJjYfJSY2Hx8mNh83JiofuAFYQCImPh/YJj4fvCY+HycmPh8hJj4fICY+HzcAFhYAAAASEQhAuQINAaazxQ0ACbgBvLInKB+4AbuyJzAfuAG4sidPH7gBt7InYh9BCQG2ACcBAQAfAbUAIAKrAB8Br7If5B+4Aa2yH+QfuAGssh+7H7gBqLIfNB+4AV2yJy4fuAFbsifNH0ENAVUAHwQBAB8BVAAfBAEAHwFTAB8CAQAfAVKyH1YfuAFRsh8pH7gBK7InJh9BDQEqACcBJQAfASkBWADkAB8BJQAfBAEAHwEksh/kH7gBI7IfOx+4ASKyHzkfQQ0BCAAnCAEAHwEGAC0BAQAfAQUAHwEBAB8BA7Mfux/vuQFYBAFACx/tH5Mf7B/kH+sfuAIBsh/ZILgEAbIfzyW4AVZACh+8LZ4fux9BH7JBCgFYBAEAHwCxAVgEAQAfALABWAQBtR+mJYkfm7kBWAElth+ZHy4fji24CAG1H40fKR+JuQFYBAGyH4IguAKrQBMfgB8wH3Qt5B9zH0ofYR9SH10luAKrsh9cH7wIAQAfAFkBWAKrth9QJYkfSR+4ASWyH0cluAQBQAsfRh95H0AfJx85ILwCqwAfADgBWAQBsh83LbwBJQAfADIBWAElth8sHzQfKiW4CAGyH1U3uAERQCoH8AeQB1sHQgc7ByMHIgceBx0HFAgSCBAIDggMCAoICAgGCAQIAggACBS4/+BAKwAAAQAUBhAAAAEABgQAAAEABBAAAAEAEAIAAAEAAgAAAAEAAAIBCAIASgCwEwNLAktTQgFLsMBjAEtiILD2UyO4AQpRWrAFI0IBsBJLAEtUQrA4K0u4B/9SsDcrS7AHUFtYsQEBjlmwOCuwAoi4AQBUWLgB/7EBAY6FG7ASQ1ixAQCFjRu5AAEBGYWNWVkAGBZ2Pxg/Ej4ROUZEPhE5RkQ+ETlGRD4ROUZEPhE5RmBEPhE5RmBEKysrKysrKysrKysYKysrKysrKysrKxgrHbCWS1NYsKodWbAyS1NYsP8dWUuwgVMgXFi5Ag8CDUVEuQIOAg1FRFlYuQRwAg9FUli5Ag8EcERZWUuw5FMgXFi5ACACDkVEuQAnAg5FRFlYuQhCACBFUli5ACAIQkRZWUu4ASVTIFxYuQAmAg9FRLkAIQIPRURZWLkKDQAmRVJYuQAmCg1EWVlLuAQBUyBcWLHYIEVEsSAgRURZWLklAADYRVJYuQDYJQBEWVlLuAQBUyBcWLkBWAAmRUSxJiZFRFlYuSMgAVhFUli5AVgjIERZWUuwKVMgXFixHx9FRLEtH0VEWVi5AQ0AH0VSWLkAHwENRFlZS7AvUyBcWLEfH0VEsSUfRURZWLkBNQAfRVJYuQAfATVEWVlLuAMBUyBcWLEfH0VEsR8fRURZWLkUKAAfRVJYuQAfFChEWVkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrZUIrAbMxdX7DRWUjRWAjRWVgI0VgsIt2aBiwgGIgILF+dUVlI0UgsAMmYGJjaCCwAyZhZbB1I2VEsH4jRCCxMcNFZSNFILADJmBiY2ggsAMmYWWwwyNlRLAxI0SxAMNFVFixw0BlRLIxQDFFI2FEWbM/PFhBRWUjRWAjRWVgI0VgsIl2aBiwgGIgILFYPEVlI0UgsAMmYGJjaCCwAyZhZbA8I2VEsFgjRCCxP0FFZSNFILADJmBiY2ggsAMmYWWwQSNlRLA/I0SxAEFFVFixQUBlRLI/QD9FI2FEWUVpU0IBS1BYsQgAQllDXFixCABCWbMCCwoSQ1hgGyFZQhYQcD6wEkNYuTshGH4bugQAAagACytZsAwjQrANI0KwEkNYuS1BLUEbugQABAAACytZsA4jQrAPI0KwEkNYuRh+OyEbugGoBAAACytZsBAjQrARI0IAKysrKysrKysAsBJDWEuwNVFLsCFTWlixJiZFsEBhRFlZKysrKysrKysrKysrKysrKysrK3Nzc3NzRbBAYUQYAEVpREVpRHNzc3Rzc3N0c3RzdCsrKysrKysrKysrKwBzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NzdHR0dHR0dHR0dHR0dHR0dHR0dHR0dXV1c3R1dXV1K3MAAEuwKlNLsDZRWlixBwdFsEBgRFkAS7AuU0uwNlFaWLEDA0WwQGBEsQkJRbj/wGBEWStFaUQBdABzc3MrRWlEKwErQ1xYQAoABgAHAqAGoAcCuf/AAnSzGh0yb70CdwB/AncAAv/AAneyLzEyuf/AAnezIiUyQLgCdLMvNTJAuAJ0sygqMkC4AnSyGiEyuP/AszcaHTK4/8CzJRodMrj/wEARLRodMpAlkC2QN6AloC2gNwa4/8C2phodMh+mH7gCjrIvpgMAdCtzKysrKysrKyt0K3N0WQArK0NcWLn/wAKhshwdMrn/wAKgshwdMisrWStzASsrKysAKysrKysrKysrKysrKysrKysrASsrKysrKytzdCsrKysrKysrc3MrKysrKytzK3MrKyt0Kysrc3Nzc3Mrc3MrKytzACsrKytzdHMrcysrKyt1KysrKysrKyt1KysrKytzKysrK3N0dSsrc3NzKysrKwA=);
+}
+body { font-family: test; }
+</style>
+</head>
+<body>A</body>
+</html> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/893572-1.html b/system/graphics/tests/crashtests/893572-1.html
new file mode 100644
index 000000000..58807459e
--- /dev/null
+++ b/system/graphics/tests/crashtests/893572-1.html
@@ -0,0 +1,11 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.strokeRect(1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.strokeRect(34.323262543409996, 1.7976931348623157e+308, 0.651, 8);
+o1.strokeRect(8, 34.323262543409996, 1.7976931348623157e+308, 0.651);
+o1.strokeRect(0.651, 8, 34.323262543409996, 1.7976931348623157e+308);
+
+</script>
diff --git a/system/graphics/tests/crashtests/893572-2.html b/system/graphics/tests/crashtests/893572-2.html
new file mode 100644
index 000000000..354c15613
--- /dev/null
+++ b/system/graphics/tests/crashtests/893572-2.html
@@ -0,0 +1,30 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.rect(1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.stroke()
+
+o1.rect(-1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.stroke()
+
+o1.rect(34.323262543409996, 1.7976931348623157e+308, 0.651, 8);
+o1.stroke()
+
+o1.rect(34.323262543409996, -1.7976931348623157e+308, 0.651, 8);
+o1.stroke()
+
+o1.rect(8, 34.323262543409996, 1.7976931348623157e+308, 0.651);
+o1.stroke()
+
+o1.rect(8, 34.323262543409996, -1.7976931348623157e+308, 0.651);
+o1.stroke()
+
+o1.rect(0.651, 8, 34.323262543409996, 1.7976931348623157e+308);
+o1.stroke()
+
+o1.rect(0.651, 8, 34.323262543409996, -1.7976931348623157e+308);
+o1.stroke()
+
+</script>
diff --git a/system/graphics/tests/crashtests/893572-3.html b/system/graphics/tests/crashtests/893572-3.html
new file mode 100644
index 000000000..d40f3c997
--- /dev/null
+++ b/system/graphics/tests/crashtests/893572-3.html
@@ -0,0 +1,44 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,34.323262543409996);
+o1.arcTo(1.7976931348623157e+308, 150, 20, 150, 70);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,34.323262543409996);
+o1.arcTo(70, 1.7976931348623157e+308, 150, 20, 150);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, 1.7976931348623157e+308);
+o1.arcTo(150, 70, 1.7976931348623157e+308, 150, 20);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, -1.7976931348623157e+308);
+o1.arcTo(20, 150, 70,1.7976931348623157e+308, 150);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(20, 20);
+o1.lineTo(100, 20);
+o1.arcTo(150, 20, 150, 70, 1.7976931348623157e+308);
+o1.lineTo(150, 120);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(20, 20);
+o1.lineTo(100, 20);
+o1.arcTo(150, 20, 150, 70, -1.7976931348623157e+308);
+o1.lineTo(150, 120);
+o1.stroke();
+
+</script>
diff --git a/system/graphics/tests/crashtests/893572-4.html b/system/graphics/tests/crashtests/893572-4.html
new file mode 100644
index 000000000..b6d8212e7
--- /dev/null
+++ b/system/graphics/tests/crashtests/893572-4.html
@@ -0,0 +1,38 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,44.323262543409996);
+o1.lineTo(10.0,44.323262543409996);
+o1.lineTo(8,34.323262543409996);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, 1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 10.0);
+o1.lineTo(34.323262543409996, 8);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,44.323262543409996);
+o1.lineTo(10.0,44.323262543409996);
+o1.lineTo(8,34.323262543409996);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, -1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, -1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 10.0);
+o1.lineTo(34.323262543409996, 8);
+o1.stroke();
+
+</script>
diff --git a/system/graphics/tests/crashtests/914457-1.html b/system/graphics/tests/crashtests/914457-1.html
new file mode 100644
index 000000000..9b9193de3
--- /dev/null
+++ b/system/graphics/tests/crashtests/914457-1.html
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html style="transform: skewY(0.08turn); overflow: -moz-hidden-unscrollable; position: absolute;">
+ <body style="border-bottom: thick solid; transform: skewX(30grad);">
+ <table style="visibility: collapse; display: list-item;"></table>
+ </body>
+</html>
+
diff --git a/system/graphics/tests/crashtests/944579.html b/system/graphics/tests/crashtests/944579.html
new file mode 100644
index 000000000..3667f6024
--- /dev/null
+++ b/system/graphics/tests/crashtests/944579.html
@@ -0,0 +1 @@
+><svg><filter width=60cm height=190.339893225in id=morphology><feMorphology operator=dilate width=140 radius=10><img src=944579.png style="-moz-filter: url(#morphology); filter: url(#morphology);">
diff --git a/system/graphics/tests/crashtests/944579.png b/system/graphics/tests/crashtests/944579.png
new file mode 100644
index 000000000..68641b767
--- /dev/null
+++ b/system/graphics/tests/crashtests/944579.png
Binary files differ
diff --git a/system/graphics/tests/crashtests/944579.svg b/system/graphics/tests/crashtests/944579.svg
new file mode 100644
index 000000000..cefe73c9f
--- /dev/null
+++ b/system/graphics/tests/crashtests/944579.svg
@@ -0,0 +1,26 @@
+<html><head></head><body><svg xmlns="http://www.w3.org/2000/svg" width="500">
+
+<filter>
+ <feMorphology></feMorphology>
+</filter>
+<g><rect></rect>
+</g>
+
+<filter>
+<feMorphology></feMorphology>
+</filter>
+<g>
+ <rect></rect>
+</g>
+
+<filter id="f3" primitiveUnits="objectBoundingBox">
+ <feMorphology operator="dilate" radius="32542"></feMorphology>
+</filter>
+<g filter="url(#f3)"><rect width="4294967217ex" height="100" x="29%"></rect>
+</g>
+
+<filter></filter>
+<g></g>
+
+</svg>
+</body><style>></style></html><!-- --> \ No newline at end of file
diff --git a/system/graphics/tests/crashtests/950000.html b/system/graphics/tests/crashtests/950000.html
new file mode 100644
index 000000000..dfd51c52b
--- /dev/null
+++ b/system/graphics/tests/crashtests/950000.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style id="s">
+
+@font-face {
+ font-family: x;
+ src: url(bogus-font.ttf);
+}
+
+@font-face {
+ font-family: y;
+ src: url(bogus-font.ttf);
+}
+
+</style>
+
+<script>
+
+function boom()
+{
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.font = 'normal 20px x';
+
+ document.getElementById("s").remove();
+
+ setTimeout(function() {
+ ctx.measureText("A");
+ }, 50);
+}
+
+window.addEventListener("DOMContentLoaded", boom, false)
+
+</script>
+</head>
+
+<body><div style="font-family: y;">y</div></body>
+</html>
diff --git a/system/graphics/tests/crashtests/PigLatin_Plane15.ttf b/system/graphics/tests/crashtests/PigLatin_Plane15.ttf
new file mode 100644
index 000000000..ab8329603
--- /dev/null
+++ b/system/graphics/tests/crashtests/PigLatin_Plane15.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/Prototype.ttf b/system/graphics/tests/crashtests/Prototype.ttf
new file mode 100644
index 000000000..c70bf00f4
--- /dev/null
+++ b/system/graphics/tests/crashtests/Prototype.ttf
Binary files differ
diff --git a/system/graphics/tests/crashtests/balinese-letter-spacing.html b/system/graphics/tests/crashtests/balinese-letter-spacing.html
new file mode 100644
index 000000000..90a2c06a0
--- /dev/null
+++ b/system/graphics/tests/crashtests/balinese-letter-spacing.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><body style="letter-spacing: 300px"><div>&#x6978;</div></body></html>
diff --git a/system/graphics/tests/crashtests/crashtests.list b/system/graphics/tests/crashtests/crashtests.list
new file mode 100644
index 000000000..6800c7384
--- /dev/null
+++ b/system/graphics/tests/crashtests/crashtests.list
@@ -0,0 +1,135 @@
+load 122875-1.html
+load 156882-1.html
+load 157320-1.html
+load 199379-1.html
+load 206561-1.html
+load 248518-1.html
+load 306649-1.xml
+load 306902-1.xml
+load 333861-1.html
+load 334735-1.html
+load 345576-1.html
+load 345629-1.html
+load 348462-1.html
+load 348462-2.html
+load 366643.html
+load 369688-1.html
+load 369947-1.html
+load 372094-1.xhtml
+load 376627-1.html
+load 377231-1.html
+load 377232-1.xhtml
+load 377461-1.xhtml
+load 383473-1.html
+load 383872-1.svg
+skip-if(Android&&smallScreen) load 385228-1.svg # bug 523255 / bug 385228, nexus-s Android 2.3.6
+skip load 385228-2.svg # bug 523255 / bug 385228
+load 385289-1.xhtml
+load 385417-1.html
+load 385417-2.html
+load 385423-1.html
+load 385423-2.html
+load 385719-1.html
+load 389326-1.html
+load 390476.html
+load 393746-1.xhtml
+load 393749-1.html
+load 393822-1.html
+load 394384-1.html
+load 394246-1.html
+load 394246-2.html
+skip-if(Android) load 394751.xhtml # bug 922976
+load 395335-1.xhtml
+load 395458-1.html
+load 396321-1.svg
+load 398042-1.xhtml
+load 398042-2.xhtml
+load 402307-1.html
+load 403464-1.html
+load 404112-1.html
+load 404112-2.html
+load 405268-1.xhtml
+load 407761-1.html
+load 407842.html
+load 408754-1.html
+load 410728-1.xml
+load 416637-1.html
+load 419095-1.html
+load 419255-1.html
+load 420945-1.html
+load 420962-1.html
+load 421393-1.html
+load 421813-1.html
+load 423110-1.xhtml
+load 423270-1.html
+load 428633.html
+load 429899-1.html
+load 441360.html
+load 445711.html
+load 463307-1.html
+load 467703-1.xhtml
+load 467873-1.html
+load 470418-1.html
+load 474410-1.html
+load 483120-1.xhtml
+load 483120-2.xhtml
+load 487549-1.html
+load 487724-1.html
+load 490777-1.html
+load 516512-1.html
+load 532726-1.html
+load 538065-1.html
+load 546870-1.html
+load 557348-1.html
+load 563740-1.html
+load 580100-1.html
+load 580212-1.html
+load 580233-1.html
+load 580719-1.html
+load 593526.html
+load 593526.xul
+load 594654-1.xhtml
+load 595042-1.html
+load 595727-1.html
+load 624198.xhtml
+load 633322-1.html
+load 633453-1.html
+load 662467-1.html
+load 665218.html
+load 675550-1.html
+load 686190-1.html
+load 691581-1.html
+load 693143-1.html
+load 696936-1.html
+load 699563-1.html
+load 710149-1.html
+load 766452-1.html
+load 766452-2.html
+load 768079-1.html
+load 783041-1.html
+load 783041-2.html
+load 783041-3.html
+load 783041-4.html
+load 798853.html # bug 868792
+load 805760-1.html
+skip-if(Android) load 815489.html # bug 1216304
+load 836225-1.html
+load 839745-1.html
+load 856784-1.html
+load 893572-1.html
+load 893572-2.html
+load 893572-3.html
+load 893572-4.html
+pref(layers.force-active,true) load 914457-1.html
+load 944579.svg
+load 944579.html
+pref(security.fileuri.strict_origin_policy,false) load 950000.html
+load 1034403-1.html
+load 1205900.html
+load 1134549-1.svg
+load balinese-letter-spacing.html
+load 1216832-1.html
+load 1225125-1.html
+load 1308394.html
+load 1317403-1.html
+load 1325159-1.html
diff --git a/system/graphics/tests/gtest/PolygonTestUtils.cpp b/system/graphics/tests/gtest/PolygonTestUtils.cpp
new file mode 100644
index 000000000..24479cc3a
--- /dev/null
+++ b/system/graphics/tests/gtest/PolygonTestUtils.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PolygonTestUtils.h"
+
+#include <cmath>
+
+namespace mozilla {
+namespace gfx {
+
+const float kEpsilon = 0.001f;
+
+// Compares two points while allowing some numerical inaccuracy.
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs)
+{
+ const Point3D d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon &&
+ std::abs(d.y) < kEpsilon &&
+ std::abs(d.z) < kEpsilon;
+}
+
+bool FuzzyEquals(const Point& lhs, const Point& rhs)
+{
+ const Point d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon &&
+ std::abs(d.y) < kEpsilon;
+}
+
+bool operator==(const Triangle& lhs, const Triangle& rhs)
+{
+ return FuzzyEquals(lhs.p1, rhs.p1) &&
+ FuzzyEquals(lhs.p2, rhs.p2) &&
+ FuzzyEquals(lhs.p3, rhs.p3);
+}
+
+// Compares the points of two polygons and ensures
+// that the points are in the same winding order.
+bool operator==(const Polygon3D& lhs, const Polygon3D& rhs)
+{
+ const nsTArray<Point3D>& left = lhs.GetPoints();
+ const nsTArray<Point3D>& right = rhs.GetPoints();
+
+ // Polygons do not have the same amount of points.
+ if (left.Length() != right.Length()) {
+ return false;
+ }
+
+ const size_t pointCount = left.Length();
+
+ // Find the first vertex of the first polygon from the second polygon.
+ // This assumes that the polygons do not contain duplicate vertices.
+ int start = -1;
+ for (size_t i = 0; i < pointCount; ++i) {
+ if (FuzzyEquals(left[0], right[i])) {
+ start = i;
+ break;
+ }
+ }
+
+ // Found at least one different vertex.
+ if (start == -1) {
+ return false;
+ }
+
+ // Verify that the polygons have the same points.
+ for (size_t i = 0; i < pointCount; ++i) {
+ size_t j = (start + i) % pointCount;
+
+ if (!FuzzyEquals(left[i], right[j])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+
+TEST(PolygonTestUtils, TestSanity)
+{
+ EXPECT_TRUE(FuzzyEquals(Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 0.0f, 0.0f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.00001f, 0.00001f, 0.00001f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point3D(0.00001f, 0.00001f, 0.00001f),
+ Point3D(0.0f, 0.0f, 0.0f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.01f, 0.01f, 0.01f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point3D(0.01f, 0.01f, 0.01f),
+ Point3D(0.0f, 0.0f, 0.0f)));
+
+ Polygon3D p1 {
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f)
+ };
+
+ // Same points as above shifted forward by one position.
+ Polygon3D shifted {
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f)
+ };
+
+ Polygon3D p2 {
+ Point3D(0.00001f, 0.00001f, 1.00001f),
+ Point3D(1.00001f, 0.00001f, 1.00001f),
+ Point3D(1.00001f, 1.00001f, 1.00001f),
+ Point3D(0.00001f, 1.00001f, 1.00001f)
+ };
+
+ Polygon3D p3 {
+ Point3D(0.01f, 0.01f, 1.01f),
+ Point3D(1.01f, 0.01f, 1.01f),
+ Point3D(1.01f, 1.01f, 1.01f),
+ Point3D(0.01f, 1.01f, 1.01f)
+ };
+
+ // Trivial equals
+ EXPECT_TRUE(p1 == p1);
+ EXPECT_TRUE(p2 == p2);
+ EXPECT_TRUE(p3 == p3);
+ EXPECT_TRUE(shifted == shifted);
+
+ // Polygons with the same point order
+ EXPECT_TRUE(p1 == p2);
+ EXPECT_TRUE(p1 == shifted);
+
+ // Polygons containing different points
+ EXPECT_FALSE(p1 == p3);
+ EXPECT_FALSE(p2 == p3);
+ EXPECT_FALSE(shifted == p3);
+
+ const nsTArray<Triangle> t1 {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f))
+ };
+
+ const nsTArray<Triangle> t2 {
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
+ };
+
+ const nsTArray<Triangle> t3 {
+ Triangle(
+ Point(0.00001f, 0.00001f),
+ Point(0.00001f, 1.00001f),
+ Point(1.00001f, 1.00001f)
+ )
+ };
+
+ EXPECT_TRUE(t1[0] == t1[0]);
+ EXPECT_TRUE(t2[0] == t2[0]);
+ EXPECT_TRUE(t3[0] == t1[0]);
+
+ EXPECT_FALSE(t1[0] == t2[0]);
+ EXPECT_FALSE(t2[0] == t3[0]);
+
+ AssertArrayEQ(t1, t1);
+ AssertArrayEQ(t2, t2);
+} \ No newline at end of file
diff --git a/system/graphics/tests/gtest/PolygonTestUtils.h b/system/graphics/tests/gtest/PolygonTestUtils.h
new file mode 100644
index 000000000..38609acfd
--- /dev/null
+++ b/system/graphics/tests/gtest/PolygonTestUtils.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEST_POLYGONUTILS_H
+#define GFX_TEST_POLYGONUTILS_H
+
+#include "gtest/gtest.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+namespace mozilla {
+namespace gfx {
+
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs);
+bool FuzzyEquals(const Point& lhs, const Point& rhs);
+
+bool operator==(const Triangle& lhs, const Triangle& rhs);
+bool operator==(const Polygon3D& lhs, const Polygon3D& rhs);
+
+// Compares two arrays with the equality operator.
+template<typename T>
+void AssertArrayEQ(const nsTArray<T>& rhs, const nsTArray<T>& lhs)
+{
+ ASSERT_EQ(lhs.Length(), rhs.Length());
+
+ for (size_t i = 0; i < lhs.Length(); ++i) {
+ EXPECT_TRUE(lhs[i] == rhs[i]);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_TEST_POLYGONUTILS_H */
diff --git a/system/graphics/tests/gtest/TestArena.cpp b/system/graphics/tests/gtest/TestArena.cpp
new file mode 100644
index 000000000..4822b32f9
--- /dev/null
+++ b/system/graphics/tests/gtest/TestArena.cpp
@@ -0,0 +1,188 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/gfx/IterableArena.h"
+#include <string>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#ifdef A
+#undef A
+#endif
+
+#ifdef B
+#undef B
+#endif
+
+// to avoid having symbols that collide easily like A and B in the global namespace
+namespace test_arena {
+
+class A;
+class B;
+
+class Base {
+public:
+ virtual ~Base() {}
+ virtual A* AsA() { return nullptr; }
+ virtual B* AsB() { return nullptr; }
+};
+
+static int sDtorItemA = 0;
+static int sDtorItemB = 0;
+
+class A : public Base {
+public:
+ virtual A* AsA() override { return this; }
+
+ explicit A(uint64_t val) : mVal(val) {}
+ ~A() { ++sDtorItemA; }
+
+ uint64_t mVal;
+};
+
+class B : public Base {
+public:
+ virtual B* AsB() override { return this; }
+
+ explicit B(const string& str) : mVal(str) {}
+ ~B() { ++sDtorItemB; }
+
+ std::string mVal;
+};
+
+struct BigStruct {
+ uint64_t mVal;
+ uint8_t data[120];
+
+ explicit BigStruct(uint64_t val) : mVal(val) {}
+};
+
+void TestArenaAlloc(IterableArena::ArenaType aType)
+{
+ sDtorItemA = 0;
+ sDtorItemB = 0;
+ IterableArena arena(aType, 256);
+
+ // An empty arena has no items to iterate over.
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item){
+ iterations++;
+ });
+ ASSERT_EQ(iterations, 0);
+ }
+
+ auto a1 = arena.Alloc<A>(42);
+ auto b1 = arena.Alloc<B>("Obladi oblada");
+ auto a2 = arena.Alloc<A>(1337);
+ auto b2 = arena.Alloc<B>("Yellow submarine");
+ auto b3 = arena.Alloc<B>("She's got a ticket to ride");
+
+ // Alloc returns a non-negative offset if the allocation succeeded.
+ ASSERT_TRUE(a1 >= 0);
+ ASSERT_TRUE(a2 >= 0);
+ ASSERT_TRUE(b1 >= 0);
+ ASSERT_TRUE(b2 >= 0);
+ ASSERT_TRUE(b3 >= 0);
+
+ ASSERT_TRUE(arena.GetStorage(a1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(a2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b3) != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(a1))->AsA() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(a2))->AsA() != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(b1))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b2))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b3))->AsB() != nullptr);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(a1))->AsA()->mVal, (uint64_t)42);
+ ASSERT_EQ(((Base*)arena.GetStorage(a2))->AsA()->mVal, (uint64_t)1337);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(b1))->AsB()->mVal, std::string("Obladi oblada"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b2))->AsB()->mVal, std::string("Yellow submarine"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b3))->AsB()->mVal, std::string("She's got a ticket to ride"));
+
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item){
+ iterations++;
+ });
+ ASSERT_EQ(iterations, 5);
+ }
+
+ // Typically, running the destructors of the elements in the arena will is done
+ // manually like this:
+ arena.ForEach([](void* item){
+ ((Base*)item)->~Base();
+ });
+ arena.Clear();
+ ASSERT_EQ(sDtorItemA, 2);
+ ASSERT_EQ(sDtorItemB, 3);
+
+ // An empty arena has no items to iterate over (we just cleared it).
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item){
+ iterations++;
+ });
+ ASSERT_EQ(iterations, 0);
+ }
+
+}
+
+void TestArenaLimit(IterableArena::ArenaType aType, bool aShouldReachLimit)
+{
+ IterableArena arena(aType, 128);
+
+ // A non-growable arena should return a negative offset when running out
+ // of space, without crashing.
+ // We should not run out of space with a growable arena (unless the os is
+ // running out of memory but this isn't expected for this test).
+ bool reachedLimit = false;
+ for (int i = 0; i < 100; ++i) {
+ auto offset = arena.Alloc<A>(42);
+ if (offset < 0) {
+ reachedLimit = true;
+ break;
+ }
+ }
+ ASSERT_EQ(reachedLimit, aShouldReachLimit);
+}
+
+} // namespace test_arena
+
+using namespace test_arena;
+
+TEST(Moz2D, FixedArena) {
+ TestArenaAlloc(IterableArena::FIXED_SIZE);
+ TestArenaLimit(IterableArena::FIXED_SIZE, true);
+}
+
+TEST(Moz2D, GrowableArena) {
+ TestArenaAlloc(IterableArena::GROWABLE);
+ TestArenaLimit(IterableArena::GROWABLE, false);
+
+ IterableArena arena(IterableArena::GROWABLE, 16);
+ // sizeof(BigStruct) is more than twice the initial capacity, make sure that
+ // this doesn't blow everything up, since the arena doubles its storage size each
+ // time it grows (until it finds a size that fits).
+ auto a = arena.Alloc<BigStruct>(1);
+ auto b = arena.Alloc<BigStruct>(2);
+ auto c = arena.Alloc<BigStruct>(3);
+
+ // Offsets should also still point to the appropriate values after reallocation.
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(a))->mVal, (uint64_t)1);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(b))->mVal, (uint64_t)2);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(c))->mVal, (uint64_t)3);
+
+ arena.Clear();
+}
diff --git a/system/graphics/tests/gtest/TestArrayView.cpp b/system/graphics/tests/gtest/TestArrayView.cpp
new file mode 100644
index 000000000..39ed1e63f
--- /dev/null
+++ b/system/graphics/tests/gtest/TestArrayView.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "mozilla/ArrayView.h"
+
+using namespace mozilla::gfx;
+
+TEST(Gfx, ArrayView) {
+ nsTArray<int> p = {5, 6};
+ ArrayView<int> pv(p);
+ ASSERT_EQ(pv[1], 6);
+ ASSERT_EQ(*pv.Data(), 5);
+}
diff --git a/system/graphics/tests/gtest/TestBSPTree.cpp b/system/graphics/tests/gtest/TestBSPTree.cpp
new file mode 100644
index 000000000..1a84e37b6
--- /dev/null
+++ b/system/graphics/tests/gtest/TestBSPTree.cpp
@@ -0,0 +1,959 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BSPTree.h"
+#include "Polygon.h"
+#include "PolygonTestUtils.h"
+
+#include <deque>
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+namespace {
+
+static void RunTest(std::deque<Polygon3D> aPolygons,
+ std::deque<Polygon3D> aExpected)
+{
+ std::deque<LayerPolygon> layers;
+ for (Polygon3D& polygon : aPolygons) {
+ layers.push_back(LayerPolygon(nullptr, Move(polygon)));
+ }
+
+ const BSPTree tree(layers);
+ const nsTArray<LayerPolygon> order = tree.GetDrawOrder();
+
+ EXPECT_EQ(aExpected.size(), order.Length());
+
+ for (size_t i = 0; i < order.Length(); ++i) {
+ EXPECT_TRUE(aExpected[i] == *order[i].geometry);
+ }
+}
+
+} // namespace
+
+
+TEST(BSPTree, SameNode)
+{
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f)
+ },
+ Polygon3D {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f)
+ }
+ };
+
+ ::RunTest(polygons, polygons);
+}
+
+TEST(BSPTree, OneChild)
+{
+ const Polygon3D p1 {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f)
+ };
+
+ const Polygon3D p2 {
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f)
+ };
+
+ ::RunTest({p1, p2}, {p1, p2});
+ ::RunTest({p2, p1}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge1)
+{
+ Polygon3D p1 {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f)
+ };
+
+ Polygon3D p2 {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(2.0f, 2.0f, 1.0f),
+ Point3D(2.0f, 0.0f, 1.0f)
+ };
+
+ ::RunTest({p1, p2}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge2)
+{
+ Polygon3D p1 {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f)
+ };
+
+ Polygon3D p2 {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(2.0f, 2.0f, 0.0f),
+ Point3D(2.0f, 0.0f, 0.0f)
+ };
+
+ ::RunTest({p1, p2}, {p2, p1});
+}
+
+TEST(BSPTree, SplitSharedEdge)
+{
+ Polygon3D p1 {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f)
+ };
+
+ Polygon3D p2 {
+ Point3D(1.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 1.0f, 2.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f)
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 1.0f)
+ },
+ Polygon3D {
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f)
+ },
+ Polygon3D {
+ Point3D(1.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 1.0f, 2.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f)
+ }
+ };
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple1)
+{
+ Polygon3D p1 {
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f)
+ };
+
+ Polygon3D p2 {
+ Point3D(0.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f)
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(0.0f, 1.0f, 0.0f),
+ Point3D(0.0f, 0.5f, 1.0f),
+ Point3D(1.0f, 0.5f, 1.0f),
+ Point3D(1.0f, 1.0f, 0.0f)
+ },
+ p1,
+ Polygon3D {
+ Point3D(0.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 0.0f, 2.0f),
+ Point3D(1.0f, 0.5f, 1.0f),
+ Point3D(0.0f, 0.5f, 1.0f)
+ }
+ };
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple2) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-5.00000f, -5.00000f, 0.00000f),
+ Point3D(-5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, -5.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, -5.00000f, -5.00000f),
+ Point3D(0.00000f, 5.00000f, -5.00000f),
+ Point3D(0.00000f, 5.00000f, 5.00000f),
+ Point3D(0.00000f, -5.00000f, 5.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(0.00000f, -5.00000f, 0.00000f),
+ Point3D(0.00000f, -5.00000f, -5.00000f),
+ Point3D(0.00000f, 5.00000f, -5.00000f),
+ Point3D(0.00000f, 5.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(-5.00000f, -5.00000f, 0.00000f),
+ Point3D(-5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, -5.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 5.00000f, 0.00000f),
+ Point3D(0.00000f, 5.00000f, 5.00000f),
+ Point3D(0.00000f, -5.00000f, 5.00000f),
+ Point3D(0.00000f, -5.00000f, 0.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit1) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, 0.00000f),
+ Point3D(0.00000f, 0.00000f, 0.00000f),
+ Point3D(10.00000f, 0.00000f, 0.00000f),
+ Point3D(10.00000f, 10.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, -5.00000f),
+ Point3D(0.00000f, 0.00000f, -5.00000f),
+ Point3D(10.00000f, 0.00000f, -5.00000f),
+ Point3D(10.00000f, 10.00000f, -5.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, 5.00000f),
+ Point3D(0.00000f, 0.00000f, 5.00000f),
+ Point3D(10.00000f, 0.00000f, 5.00000f),
+ Point3D(10.00000f, 10.00000f, 5.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, -5.00000f),
+ Point3D(0.00000f, 0.00000f, -5.00000f),
+ Point3D(10.00000f, 0.00000f, -5.00000f),
+ Point3D(10.00000f, 10.00000f, -5.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, 0.00000f),
+ Point3D(0.00000f, 0.00000f, 0.00000f),
+ Point3D(10.00000f, 0.00000f, 0.00000f),
+ Point3D(10.00000f, 10.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 10.00000f, 5.00000f),
+ Point3D(0.00000f, 0.00000f, 5.00000f),
+ Point3D(10.00000f, 0.00000f, 5.00000f),
+ Point3D(10.00000f, 10.00000f, 5.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit2) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-5.00000f, -5.00000f, 0.00000f),
+ Point3D(-5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, -5.00000f, 0.00000f)
+ },
+ Polygon3D {
+ Point3D(0.00000f, 5.00000f, -15.00000f),
+ Point3D(0.00000f, -5.00000f, -15.00000f),
+ Point3D(0.00000f, -5.00000f, -10.00000f),
+ Point3D(0.00000f, 5.00000f, -10.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(0.00000f, 5.00000f, -15.00000f),
+ Point3D(0.00000f, -5.00000f, -15.00000f),
+ Point3D(0.00000f, -5.00000f, -10.00000f),
+ Point3D(0.00000f, 5.00000f, -10.00000f)
+ },
+ Polygon3D {
+ Point3D(-5.00000f, -5.00000f, 0.00000f),
+ Point3D(-5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, 5.00000f, 0.00000f),
+ Point3D(5.00000f, -5.00000f, 0.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate0degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 2.00000f, 2.00000f),
+ Point3D(-0.00000f, -2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, -2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00000f, 2.00000f),
+ Point3D(2.00000f, -0.00000f, -2.00000f),
+ Point3D(-2.00000f, 0.00000f, -2.00000f),
+ Point3D(-2.00000f, 0.00010f, 2.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, 0.00000f, 2.00000f),
+ Point3D(2.00000f, -0.00000f, -2.00000f),
+ Point3D(-2.00000f, 0.00000f, -2.00000f),
+ Point3D(-2.00000f, 0.00010f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 2.00000f, 2.00000f),
+ Point3D(-0.00000f, -2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, -2.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate20degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate40degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -0.73200f, 2.73210f),
+ Point3D(-0.00000f, -2.73200f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, 0.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, -0.73200f, 2.73210f),
+ Point3D(-0.00000f, -2.73200f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, 0.73210f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate60degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -2.73200f, 0.73210f),
+ Point3D(-0.00000f, -0.73200f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, 2.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-2.00000f, 1.26793f, 0.73210f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, 1.26793f, 0.73210f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, -2.73200f, 0.73210f),
+ Point3D(-0.00000f, -0.73200f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, 2.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.26793f, 0.73210f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.26793f, 0.73210f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate80degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate100degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 2.73210f, -0.73200f),
+ Point3D(-0.00000f, 0.73210f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, -2.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -1.26783f, -0.73200f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.26783f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 2.73210f, -0.73200f),
+ Point3D(-0.00000f, 0.73210f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, -2.73200f)
+ },
+ Polygon3D {
+ Point3D(-2.00000f, -1.26783f, -0.73200f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, -1.26783f, -0.73200f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate120degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -0.73200f, 2.73210f),
+ Point3D(-0.00000f, -2.73200f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, 0.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, -0.73200f, 2.73210f),
+ Point3D(-0.00000f, -2.73200f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, 0.73210f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate140degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate160degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 2.00000f, 2.00000f),
+ Point3D(-0.00000f, -2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, -2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 2.00000f, 2.00000f),
+ Point3D(-0.00000f, -2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, -2.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate180degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate200degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate220degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 0.73210f, -2.73200f),
+ Point3D(-0.00000f, 2.73210f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, 0.73210f, -2.73200f),
+ Point3D(-0.00000f, 2.73210f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate240degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -2.73200f, 0.73210f),
+ Point3D(-0.00000f, -0.73200f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, 2.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-2.00000f, 1.26793f, 0.73210f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(2.00000f, 1.26793f, 0.73210f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, -2.73200f, 0.73210f),
+ Point3D(-0.00000f, -0.73200f, -2.73200f),
+ Point3D(0.00010f, 2.73210f, -0.73200f),
+ Point3D(0.00010f, 0.73210f, 2.73210f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.26793f, 0.73210f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(-2.00000f, 1.26793f, 0.73210f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate260degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 1.19540f, 2.56350f),
+ Point3D(-0.00000f, -2.56340f, 1.19540f),
+ Point3D(0.00010f, -1.19530f, -2.56340f),
+ Point3D(0.00010f, 2.56350f, -1.19530f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate280degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 2.73210f, -0.73200f),
+ Point3D(-0.00000f, 0.73210f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, -2.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(2.00000f, -1.26783f, -0.73200f),
+ Point3D(2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.73200f, -1.00000f),
+ Point3D(-2.00000f, -1.26783f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(-0.00000f, 2.73210f, -0.73200f),
+ Point3D(-0.00000f, 0.73210f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, -2.73200f)
+ },
+ Polygon3D {
+ Point3D(-2.00000f, -1.26783f, -0.73200f),
+ Point3D(-2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, 1.73210f, 1.00010f),
+ Point3D(2.00000f, -1.26783f, -0.73200f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate300degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, 0.73210f, -2.73200f),
+ Point3D(-0.00000f, 2.73210f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, 0.73210f, -2.73200f),
+ Point3D(-0.00000f, 2.73210f, 0.73210f),
+ Point3D(0.00010f, -0.73200f, 2.73210f),
+ Point3D(0.00010f, -2.73200f, -0.73200f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 1.73210f, -0.99990f),
+ Point3D(2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, -1.73200f, 1.00000f),
+ Point3D(-2.00000f, 1.73210f, -0.99990f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate320degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -1.19530f, -2.56340f),
+ Point3D(-0.00000f, 2.56350f, -1.19530f),
+ Point3D(0.00010f, 1.19540f, 2.56350f),
+ Point3D(0.00010f, -2.56340f, 1.19540f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.68410f, -1.87930f),
+ Point3D(2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, -0.68400f, 1.87940f),
+ Point3D(-2.00000f, 0.68410f, -1.87930f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate340degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate360degrees) {
+ const std::deque<Polygon3D> polygons {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+
+ const std::deque<Polygon3D> expected {
+ Polygon3D {
+ Point3D(-0.00000f, -2.00000f, -2.00000f),
+ Point3D(-0.00000f, 2.00000f, -2.00000f),
+ Point3D(0.00010f, 2.00000f, 2.00000f),
+ Point3D(0.00010f, -2.00000f, 2.00000f)
+ },
+ Polygon3D {
+ Point3D(2.00000f, 0.00010f, -2.00000f),
+ Point3D(2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, -0.00000f, 2.00000f),
+ Point3D(-2.00000f, 0.00010f, -2.00000f)
+ }
+ };
+ ::RunTest(polygons, expected);
+}
diff --git a/system/graphics/tests/gtest/TestBufferRotation.cpp b/system/graphics/tests/gtest/TestBufferRotation.cpp
new file mode 100644
index 000000000..9fb972adb
--- /dev/null
+++ b/system/graphics/tests/gtest/TestBufferRotation.cpp
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BufferUnrotate.h"
+
+static unsigned char* GenerateBuffer(int bytesPerPixel,
+ int width, int height,
+ int stride, int xBoundary, int yBoundary)
+{
+ unsigned char* buffer = new unsigned char[stride*height];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ buffer[pos+i] = (x+y+i*2)%256;
+ }
+ }
+ }
+ return buffer;
+}
+
+static bool CheckBuffer(unsigned char* buffer, int bytesPerPixel,
+ int width, int height, int stride)
+{
+ int xBoundary = 0;
+ int yBoundary = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ if (buffer[pos+i] != (x+y+i*2)%256) {
+ printf("Buffer differs at %i, %i, is %i\n", x, y, (int)buffer[pos+i]);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+TEST(Gfx, BufferUnrotateHorizontal) {
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2,4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer = GenerateBuffer(bytesPerPixel,
+ width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer,
+ width[testId] * bytesPerPixel, height[testId], stride,
+ xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
+ width[testId], height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateVertical) {
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2,4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer = GenerateBuffer(bytesPerPixel,
+ width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel,
+ height[testId], stride,
+ xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
+ width[testId], height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+
+TEST(Gfx, BufferUnrotateBoth) {
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2,4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99, 100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer = GenerateBuffer(bytesPerPixel,
+ width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer,
+ width[testId] * bytesPerPixel, height[testId], stride,
+ xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
+ width[testId], height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateUneven) {
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2,4};
+ int width[NUM_OF_TESTS] = {10, 100, 99, 39, 100, 40, 99, 39, 100, 50, 39, 99, 74, 60, 99, 39};
+ int height[NUM_OF_TESTS] = {100, 39, 10, 99, 10, 99, 40, 99, 73, 39, 100, 39, 67, 99, 84, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 30, 30, 99, 31, 0, 31, 30, 30, 30, 30, 31, 31, 31, 38};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 0, 30, 0, 30, 0, 30, 31, 31, 31, 31, 31, 31, 31, 98};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer = GenerateBuffer(bytesPerPixel,
+ width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer,
+ width[testId]*bytesPerPixel, height[testId], stride,
+ xBoundary[testId]*bytesPerPixel, yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId], height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+
diff --git a/system/graphics/tests/gtest/TestColorNames.cpp b/system/graphics/tests/gtest/TestColorNames.cpp
new file mode 100644
index 000000000..3ce702dba
--- /dev/null
+++ b/system/graphics/tests/gtest/TestColorNames.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include <string.h>
+#include "nsColor.h"
+#include "nsColorNames.h"
+#include "mozilla/Sprintf.h"
+#include "nsString.h"
+#include "mozilla/ArrayUtils.h"
+
+// define an array of all color names
+#define GFX_COLOR(_name, _value) #_name,
+static const char* const kColorNames[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+// define an array of all color name values
+#define GFX_COLOR(_name, _value) _value,
+static const nscolor kColors[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+using namespace mozilla;
+
+static const char* kJunkNames[] = {
+ nullptr,
+ "",
+ "123",
+ "backgroundz",
+ "zzzzzz",
+ "#@$&@#*@*$@$#"
+};
+
+static
+void RunColorTests() {
+ nscolor rgb;
+ // First make sure we can find all of the tags that are supposed to
+ // be in the table. Futz with the case to make sure any case will
+ // work
+
+ for (uint32_t index = 0 ; index < ArrayLength(kColorNames); index++) {
+ // Lookup color by name and make sure it has the right id
+ nsCString tagName(kColorNames[index]);
+
+ // Check that color lookup by name gets the right rgb value
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb)) <<
+ "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index])) <<
+ "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // fiddle with the case to make sure we can still find it
+ tagName.SetCharAt(tagName.CharAt(0) - 32, 0);
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb)) <<
+ "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index])) <<
+ "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // Check that parsing an RGB value in hex gets the right values
+ uint8_t r = NS_GET_R(rgb);
+ uint8_t g = NS_GET_G(rgb);
+ uint8_t b = NS_GET_B(rgb);
+ uint8_t a = NS_GET_A(rgb);
+ char cbuf[50];
+ if (a != UINT8_MAX) {
+ SprintfLiteral(cbuf, "%02x%02x%02x%02x", r, g, b, a);
+ } else {
+ SprintfLiteral(cbuf, "%02x%02x%02x", r, g, b);
+ }
+ nscolor hexrgb;
+ ASSERT_TRUE(NS_HexToRGBA(NS_ConvertASCIItoUTF16(cbuf),
+ nsHexColorType::AllowAlpha, &hexrgb)) <<
+ "hex conversion to color of '" << cbuf << "'";
+ ASSERT_TRUE(hexrgb == rgb);
+ }
+}
+
+static
+void RunJunkColorTests() {
+ nscolor rgb;
+ // Now make sure we don't find some garbage
+ for (uint32_t i = 0; i < ArrayLength(kJunkNames); i++) {
+ nsCString tag(kJunkNames[i]);
+ ASSERT_FALSE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tag), &rgb)) <<
+ "Failed at junk color " << kJunkNames[i];
+ }
+}
+
+TEST(Gfx, ColorNames) {
+ RunColorTests();
+}
+
+TEST(Gfx, JunkColorNames) {
+ RunJunkColorTests();
+}
diff --git a/system/graphics/tests/gtest/TestCompositor.cpp b/system/graphics/tests/gtest/TestCompositor.cpp
new file mode 100644
index 000000000..7a2556ae0
--- /dev/null
+++ b/system/graphics/tests/gtest/TestCompositor.cpp
@@ -0,0 +1,284 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gfxPrefs.h"
+#include "gfxUtils.h"
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+#include "TestLayers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/widget/InProcessCompositorWidget.h"
+#include "nsBaseWidget.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include <vector>
+
+const int gCompWidth = 256;
+const int gCompHeight = 256;
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+class MockWidget : public nsBaseWidget
+{
+public:
+ MockWidget() {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual LayoutDeviceIntRect GetClientBounds() override {
+ return LayoutDeviceIntRect(0, 0, gCompWidth, gCompHeight);
+ }
+ virtual LayoutDeviceIntRect GetBounds() override {
+ return GetClientBounds();
+ }
+
+ void* GetNativeData(uint32_t aDataType) override {
+ if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
+ mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
+ caps.preserve = false;
+ caps.bpp16 = false;
+ nsCString discardFailureId;
+ RefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
+ IntSize(gCompWidth, gCompHeight), caps,
+ CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+ &discardFailureId);
+ return context.forget().take();
+ }
+ return nullptr;
+ }
+
+ virtual nsresult Create(nsIWidget* aParent,
+ nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override { return NS_OK; }
+ virtual nsresult Create(nsIWidget* aParent,
+ nsNativeWidget aNativeParent,
+ const DesktopIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override { return NS_OK; }
+ NS_IMETHOD Show(bool aState) override { return NS_OK; }
+ virtual bool IsVisible() const override { return true; }
+ NS_IMETHOD Move(double aX, double aY) override { return NS_OK; }
+ NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override { return NS_OK; }
+ NS_IMETHOD Resize(double aX, double aY,
+ double aWidth, double aHeight, bool aRepaint) override { return NS_OK; }
+
+ NS_IMETHOD Enable(bool aState) override { return NS_OK; }
+ virtual bool IsEnabled() const override { return true; }
+ NS_IMETHOD SetFocus(bool aRaise) override { return NS_OK; }
+ virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override { return NS_OK; }
+ NS_IMETHOD Invalidate(const LayoutDeviceIntRect& aRect) override { return NS_OK; }
+ NS_IMETHOD SetTitle(const nsAString& title) override { return NS_OK; }
+ virtual LayoutDeviceIntPoint WidgetToScreenOffset() override { return LayoutDeviceIntPoint(0, 0); }
+ NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus& aStatus) override { return NS_OK; }
+ NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
+ const InputContextAction& aAction) override {}
+ NS_IMETHOD_(InputContext) GetInputContext() override { abort(); }
+
+private:
+ ~MockWidget() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
+
+struct LayerManagerData {
+ RefPtr<MockWidget> mWidget;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<widget::CompositorWidget> mCompositorWidget;
+ RefPtr<LayerManagerComposite> mLayerManager;
+
+ LayerManagerData(Compositor* compositor,
+ MockWidget* widget,
+ widget::CompositorWidget* aWidget,
+ LayerManagerComposite* layerManager)
+ : mWidget(widget)
+ , mCompositor(compositor)
+ , mCompositorWidget(aWidget)
+ , mLayerManager(layerManager)
+ {}
+};
+
+static already_AddRefed<Compositor> CreateTestCompositor(LayersBackend backend, widget::CompositorWidget* widget)
+{
+ gfxPrefs::GetSingleton();
+
+ RefPtr<Compositor> compositor;
+
+ if (backend == LayersBackend::LAYERS_OPENGL) {
+ compositor = new CompositorOGL(nullptr,
+ widget,
+ gCompWidth,
+ gCompHeight,
+ true);
+ compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
+ } else if (backend == LayersBackend::LAYERS_BASIC) {
+ compositor = new BasicCompositor(nullptr, widget);
+#ifdef XP_WIN
+ } else if (backend == LayersBackend::LAYERS_D3D11) {
+ //compositor = new CompositorD3D11();
+ MOZ_CRASH(); // No support yet
+ } else if (backend == LayersBackend::LAYERS_D3D9) {
+ //compositor = new CompositorD3D9(this, mWidget);
+ MOZ_CRASH(); // No support yet
+#endif
+ }
+ nsCString failureReason;
+ if (!compositor || !compositor->Initialize(&failureReason)) {
+ printf_stderr("Failed to construct layer manager for the requested backend\n");
+ abort();
+ }
+
+ return compositor.forget();
+}
+
+/**
+ * Get a list of layers managers for the platform to run the test on.
+ */
+static std::vector<LayerManagerData> GetLayerManagers(std::vector<LayersBackend> aBackends)
+{
+ std::vector<LayerManagerData> managers;
+
+ for (size_t i = 0; i < aBackends.size(); i++) {
+ auto backend = aBackends[i];
+
+ RefPtr<MockWidget> widget = new MockWidget();
+ RefPtr<widget::CompositorWidget> proxy = new widget::InProcessCompositorWidget(widget);
+ RefPtr<Compositor> compositor = CreateTestCompositor(backend, proxy);
+
+ RefPtr<LayerManagerComposite> layerManager = new LayerManagerComposite(compositor);
+
+ managers.push_back(LayerManagerData(compositor, widget, proxy, layerManager));
+ }
+
+ return managers;
+}
+
+/**
+ * This will return the default list of backends that
+ * units test should run against.
+ */
+static std::vector<LayersBackend> GetPlatformBackends()
+{
+ std::vector<LayersBackend> backends;
+
+ // For now we only support Basic for gtest
+ backends.push_back(LayersBackend::LAYERS_BASIC);
+
+#ifdef XP_MACOSX
+ backends.push_back(LayersBackend::LAYERS_OPENGL);
+#endif
+
+ // TODO Support OGL/D3D backends with unit test
+ return backends;
+}
+
+static already_AddRefed<DrawTarget> CreateDT()
+{
+ return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ IntSize(gCompWidth, gCompHeight), SurfaceFormat::B8G8R8A8);
+}
+
+static bool CompositeAndCompare(RefPtr<LayerManagerComposite> layerManager, DrawTarget* refDT)
+{
+ RefPtr<DrawTarget> drawTarget = CreateDT();
+
+ layerManager->BeginTransactionWithDrawTarget(drawTarget, IntRect(0, 0, gCompWidth, gCompHeight));
+ layerManager->EndTransaction(TimeStamp::Now());
+
+ RefPtr<SourceSurface> ss = drawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dss = ss->GetDataSurface();
+ uint8_t* bitmap = dss->GetData();
+
+ RefPtr<SourceSurface> ssRef = refDT->Snapshot();
+ RefPtr<DataSourceSurface> dssRef = ssRef->GetDataSurface();
+ uint8_t* bitmapRef = dssRef->GetData();
+
+ for (int y = 0; y < gCompHeight; y++) {
+ for (int x = 0; x < gCompWidth; x++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ uint8_t bit = bitmap[y * dss->Stride() + x * 4 + channel];
+ uint8_t bitRef = bitmapRef[y * dss->Stride() + x * 4 + channel];
+ if (bit != bitRef) {
+ printf("Layer Tree:\n");
+ layerManager->Dump();
+ printf("Original:\n");
+ gfxUtils::DumpAsDataURI(drawTarget);
+ printf("\n\n");
+
+ printf("Reference:\n");
+ gfxUtils::DumpAsDataURI(refDT);
+ printf("\n\n");
+
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+TEST(Gfx, CompositorConstruct)
+{
+ auto layerManagers = GetLayerManagers(GetPlatformBackends());
+}
+
+static void CompositorSimpleTree() {
+ const int benchmarkRepeatCount = 30;
+
+ RefPtr<DrawTarget> refDT = CreateDT();
+ refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
+ refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
+ refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
+
+ auto layerManagers = GetLayerManagers(GetPlatformBackends());
+ for (size_t i = 0; i < layerManagers.size(); i++) {
+ // Benchmark n composites
+ for (size_t n = 0; n < benchmarkRepeatCount; n++) {
+ RefPtr<LayerManagerComposite> layerManager = layerManagers[i].mLayerManager;
+ RefPtr<LayerManager> lmBase = layerManager.get();
+ nsTArray<RefPtr<Layer>> layers;
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 100)),
+ };
+ RefPtr<Layer> root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+
+ { // background
+ ColorLayer* colorLayer = layers[1]->AsColorLayer();
+ colorLayer->SetColor(Color(1.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[2]->AsColorLayer();
+ colorLayer->SetColor(Color(1.f, 0.f, 0.f, 1.f));
+ colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[3]->AsColorLayer();
+ colorLayer->SetColor(Color(0.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+
+ EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
+ }
+ }
+};
+
+MOZ_GTEST_BENCH(GfxBench, CompositorSimpleTree, &CompositorSimpleTree);
+
diff --git a/system/graphics/tests/gtest/TestGfxPrefs.cpp b/system/graphics/tests/gtest/TestGfxPrefs.cpp
new file mode 100644
index 000000000..4e3b6037e
--- /dev/null
+++ b/system/graphics/tests/gtest/TestGfxPrefs.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "gfxPrefs.h"
+#ifdef GFX_DECL_PREF
+#error "This is not supposed to be defined outside of gfxPrefs.h"
+#endif
+
+// If the default values for any of these preferences change,
+// just modify the test to match. We are only testing against
+// a particular value to make sure we receive the correct
+// result through this API.
+
+TEST(GfxPrefs, Singleton) {
+ gfxPrefs::GetSingleton();
+ ASSERT_TRUE(gfxPrefs::SingletonExists());
+}
+
+TEST(GfxPrefs, LiveValues) {
+ gfxPrefs::GetSingleton();
+ ASSERT_TRUE(gfxPrefs::SingletonExists());
+
+ // Live boolean, default false
+ ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
+
+ // Live int32_t, default 23456
+ ASSERT_TRUE(gfxPrefs::LayerScopePort() == 23456);
+
+ // Live uint32_t, default 2
+ ASSERT_TRUE(gfxPrefs::MSAALevel() == 2);
+}
+
+TEST(GfxPrefs, OnceValues) {
+ gfxPrefs::GetSingleton();
+ ASSERT_TRUE(gfxPrefs::SingletonExists());
+
+ // Once boolean, default true
+ ASSERT_TRUE(gfxPrefs::WorkAroundDriverBugs());
+
+ // Once boolean, default false
+ ASSERT_FALSE(gfxPrefs::LayersDump());
+
+ // Once int32_t, default 95
+ ASSERT_TRUE(gfxPrefs::CanvasSkiaGLCacheSize() == 96);
+
+ // Once uint32_t, default 5
+ ASSERT_TRUE(gfxPrefs::APZMaxVelocityQueueSize() == 5);
+
+ // Once float, default -1 (should be OK with ==)
+ ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == -1.0f);
+}
+
+TEST(GfxPrefs, Set) {
+ gfxPrefs::GetSingleton();
+ ASSERT_TRUE(gfxPrefs::SingletonExists());
+
+ // Once boolean, default false
+ ASSERT_FALSE(gfxPrefs::LayersDump());
+ gfxPrefs::SetLayersDump(true);
+ ASSERT_TRUE(gfxPrefs::LayersDump());
+ gfxPrefs::SetLayersDump(false);
+ ASSERT_FALSE(gfxPrefs::LayersDump());
+
+ // Live boolean, default false
+ ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
+ gfxPrefs::SetLayersDumpTexture(true);
+ ASSERT_TRUE(gfxPrefs::LayersDumpTexture());
+ gfxPrefs::SetLayersDumpTexture(false);
+ ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
+
+ // Once float, default -1
+ ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == -1.0f);
+ gfxPrefs::SetAPZMaxVelocity(1.75f);
+ ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == 1.75f);
+ gfxPrefs::SetAPZMaxVelocity(-1.0f);
+ ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == -1.0f);
+}
+
diff --git a/system/graphics/tests/gtest/TestGfxWidgets.cpp b/system/graphics/tests/gtest/TestGfxWidgets.cpp
new file mode 100644
index 000000000..830204e05
--- /dev/null
+++ b/system/graphics/tests/gtest/TestGfxWidgets.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "GfxDriverInfo.h"
+#include "nsVersionComparator.h"
+
+using namespace mozilla::widget;
+
+TEST(GfxWidgets, Split) {
+ char aStr[8], bStr[8], cStr[8], dStr[8];
+
+ ASSERT_TRUE(SplitDriverVersion("33.4.3.22", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 33 && atoi(bStr) == 4 && atoi(cStr) == 3 && atoi(dStr) == 22);
+
+ ASSERT_TRUE(SplitDriverVersion("28.74.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 74 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("132.0.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 132 && atoi(bStr) == 0 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("2.3.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 2 && atoi(bStr) == 3 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("25.4.0.8", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 && atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.84437.3", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8443 && atoi(dStr) == 3);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 && atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.8.3143243", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 && atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8..", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 && atoi(dStr) == 8);
+
+ ASSERT_FALSE(SplitDriverVersion("424.143.8.3143243.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 && atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.13", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 && atoi(dStr) == 8);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 && atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("28...74", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 0 && atoi(cStr) == 0 && atoi(dStr) == 74);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 && atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("35..42.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 35 && atoi(bStr) == 0 && atoi(cStr) == 42 && atoi(dStr) == 0);
+}
+
+TEST(GfxWidgets, Versioning) {
+ ASSERT_TRUE(mozilla::Version("0") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("39.0.5b7") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("18.0.5b7") < mozilla::Version("18.2"));
+ ASSERT_TRUE(mozilla::Version("30.0.5b7") < mozilla::Version("41.0b9"));
+ ASSERT_TRUE(mozilla::Version("100") > mozilla::Version("43.0a1"));
+ ASSERT_FALSE(mozilla::Version("42.0") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0.5") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.1") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("42.0b7") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("") == mozilla::Version("0"));
+
+ // Note these two; one would expect for 42.0b1 and 42b1 to compare the
+ // same, but they do not. If this ever changes, we want to know, so
+ // leave the test here to fail.
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0b2"));
+ ASSERT_FALSE(mozilla::Version("42.0a1") < mozilla::Version("42b2"));
+}
+
diff --git a/system/graphics/tests/gtest/TestJobScheduler.cpp b/system/graphics/tests/gtest/TestJobScheduler.cpp
new file mode 100644
index 000000000..7f22cbfcc
--- /dev/null
+++ b/system/graphics/tests/gtest/TestJobScheduler.cpp
@@ -0,0 +1,245 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/gfx/JobScheduler.h"
+
+#ifndef WIN32
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include <stdlib.h>
+#include <time.h>
+
+namespace test_scheduler {
+
+using namespace mozilla::gfx;
+using namespace mozilla;
+
+// Artificially cause threads to yield randomly in an attempt to make racy
+// things more apparent (if any).
+void MaybeYieldThread()
+{
+#ifndef WIN32
+ if (rand() % 5 == 0) {
+ sched_yield();
+ }
+#endif
+}
+
+/// Used by the TestCommand to check that tasks are processed in the right order.
+struct SanityChecker {
+ std::vector<uint64_t> mAdvancements;
+ mozilla::gfx::CriticalSection mSection;
+
+ explicit SanityChecker(uint64_t aNumCmdBuffers)
+ {
+ for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
+ mAdvancements.push_back(0);
+ }
+ }
+
+ virtual void Check(uint64_t aJobId, uint64_t aCmdId)
+ {
+ MaybeYieldThread();
+ CriticalSectionAutoEnter lock(&mSection);
+ MOZ_RELEASE_ASSERT(mAdvancements[aJobId] == aCmdId-1);
+ mAdvancements[aJobId] = aCmdId;
+ }
+};
+
+/// Run checks that are specific to TestSchulerJoin.
+struct JoinTestSanityCheck : public SanityChecker {
+ bool mSpecialJobHasRun;
+
+ explicit JoinTestSanityCheck(uint64_t aNumCmdBuffers)
+ : SanityChecker(aNumCmdBuffers)
+ , mSpecialJobHasRun(false)
+ {}
+
+ virtual void Check(uint64_t aJobId, uint64_t aCmdId) override
+ {
+ // Job 0 is the special task executed when everything is joined after task 1
+ if (aCmdId == 0) {
+ MOZ_RELEASE_ASSERT(!mSpecialJobHasRun, "GFX: A special task has been executed.");
+ mSpecialJobHasRun = true;
+ for (auto advancement : mAdvancements) {
+ // Because of the synchronization point (beforeFilter), all
+ // task buffers should have run task 1 when task 0 is run.
+ MOZ_RELEASE_ASSERT(advancement == 1, "GFX: task buffer has not run task 1.");
+ }
+ } else {
+ // This check does not apply to task 0.
+ SanityChecker::Check(aJobId, aCmdId);
+ }
+
+ if (aCmdId == 2) {
+ MOZ_RELEASE_ASSERT(mSpecialJobHasRun, "GFX: Special job has not run.");
+ }
+ }
+};
+
+class TestJob : public Job
+{
+public:
+ TestJob(uint64_t aCmdId, uint64_t aJobId, SanityChecker* aChecker,
+ SyncObject* aStart, SyncObject* aCompletion)
+ : Job(aStart, aCompletion, nullptr)
+ , mCmdId(aCmdId)
+ , mCmdBufferId(aJobId)
+ , mSanityChecker(aChecker)
+ {}
+
+ JobStatus Run()
+ {
+ MaybeYieldThread();
+ mSanityChecker->Check(mCmdBufferId, mCmdId);
+ MaybeYieldThread();
+ return JobStatus::Complete;
+ }
+
+ uint64_t mCmdId;
+ uint64_t mCmdBufferId;
+ SanityChecker* mSanityChecker;
+};
+
+/// This test creates aNumCmdBuffers task buffers with sync objects set up
+/// so that all tasks will join after command 5 before a task buffer runs
+/// a special task (task 0) after which all task buffers fork again.
+/// This simulates the kind of scenario where all tiles must join at
+/// a certain point to execute, say, a filter, and fork again after the filter
+/// has been processed.
+/// The main thread is only blocked when waiting for the completion of the entire
+/// task stream (it doesn't have to wait at the filter's sync points to orchestrate it).
+void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
+{
+ JoinTestSanityCheck check(aNumCmdBuffers);
+
+ RefPtr<SyncObject> beforeFilter = new SyncObject(aNumCmdBuffers);
+ RefPtr<SyncObject> afterFilter = new SyncObject();
+ RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
+
+
+ for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
+ Job* t1 = new TestJob(1, i, &check, nullptr, beforeFilter);
+ JobScheduler::SubmitJob(t1);
+ MaybeYieldThread();
+ }
+ beforeFilter->FreezePrerequisites();
+
+ // This task buffer is executed when all other tasks have joined after task 1
+ JobScheduler::SubmitJob(
+ new TestJob(0, 0, &check, beforeFilter, afterFilter)
+ );
+ afterFilter->FreezePrerequisites();
+
+ for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
+ Job* t2 = new TestJob(2, i, &check, afterFilter, completion);
+ JobScheduler::SubmitJob(t2);
+ MaybeYieldThread();
+ }
+ completion->FreezePrerequisites();
+
+ JobScheduler::Join(completion);
+
+ MaybeYieldThread();
+
+ for (auto advancement : check.mAdvancements) {
+ EXPECT_TRUE(advancement == 2);
+ }
+}
+
+/// This test creates several chains of 10 task, tasks of a given chain are executed
+/// sequentially, and chains are exectuted in parallel.
+/// This simulates the typical scenario where we want to process sequences of drawing
+/// commands for several tiles in parallel.
+void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
+{
+ SanityChecker check(aNumCmdBuffers);
+
+ RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
+
+ uint32_t numJobs = 10;
+
+ for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
+
+ std::vector<RefPtr<SyncObject>> syncs;
+ std::vector<Job*> tasks;
+ syncs.reserve(numJobs);
+ tasks.reserve(numJobs);
+
+ for (uint32_t t = 0; t < numJobs-1; ++t) {
+ syncs.push_back(new SyncObject());
+ tasks.push_back(new TestJob(t+1, i, &check, t == 0 ? nullptr
+ : syncs[t-1].get(),
+ syncs[t]));
+ syncs.back()->FreezePrerequisites();
+ }
+
+ tasks.push_back(new TestJob(numJobs, i, &check, syncs.back(), completion));
+
+ if (i % 2 == 0) {
+ // submit half of the tasks in order
+ for (Job* task : tasks) {
+ JobScheduler::SubmitJob(task);
+ MaybeYieldThread();
+ }
+ } else {
+ // ... and submit the other half in reverse order
+ for (int32_t reverse = numJobs-1; reverse >= 0; --reverse) {
+ JobScheduler::SubmitJob(tasks[reverse]);
+ MaybeYieldThread();
+ }
+ }
+ }
+ completion->FreezePrerequisites();
+
+ JobScheduler::Join(completion);
+
+ for (auto advancement : check.mAdvancements) {
+ EXPECT_TRUE(advancement == numJobs);
+ }
+}
+
+} // namespace test_scheduler
+
+TEST(Moz2D, JobScheduler_Shutdown) {
+ srand(time(nullptr));
+ for (uint32_t threads = 1; threads < 16; ++threads) {
+ for (uint32_t i = 1; i < 1000; ++i) {
+ mozilla::gfx::JobScheduler::Init(threads, threads);
+ mozilla::gfx::JobScheduler::ShutDown();
+ }
+ }
+}
+
+TEST(Moz2D, JobScheduler_Join) {
+ srand(time(nullptr));
+ for (uint32_t threads = 1; threads < 8; ++threads) {
+ for (uint32_t queues = 1; queues < threads; ++queues) {
+ for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
+ mozilla::gfx::JobScheduler::Init(threads, queues);
+ test_scheduler::TestSchedulerJoin(threads, buffers);
+ mozilla::gfx::JobScheduler::ShutDown();
+ }
+ }
+ }
+}
+
+TEST(Moz2D, JobScheduler_Chain) {
+ srand(time(nullptr));
+ for (uint32_t threads = 1; threads < 8; ++threads) {
+ for (uint32_t queues = 1; queues < threads; ++queues) {
+ for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
+ mozilla::gfx::JobScheduler::Init(threads, queues);
+ test_scheduler::TestSchedulerChain(threads, buffers);
+ mozilla::gfx::JobScheduler::ShutDown();
+ }
+ }
+ }
+}
diff --git a/system/graphics/tests/gtest/TestLayers.cpp b/system/graphics/tests/gtest/TestLayers.cpp
new file mode 100644
index 000000000..fb9fd27be
--- /dev/null
+++ b/system/graphics/tests/gtest/TestLayers.cpp
@@ -0,0 +1,488 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "TestLayers.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "LayerUserData.h"
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+class TestContainerLayer: public ContainerLayer {
+public:
+ explicit TestContainerLayer(LayerManager* aManager)
+ : ContainerLayer(aManager, nullptr)
+ {}
+
+ virtual const char* Name() const {
+ return "TestContainerLayer";
+ }
+
+ virtual LayerType GetType() const {
+ return TYPE_CONTAINER;
+ }
+
+ virtual void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+};
+
+class TestPaintedLayer: public PaintedLayer {
+public:
+ explicit TestPaintedLayer(LayerManager* aManager)
+ : PaintedLayer(aManager, nullptr)
+ {}
+
+ virtual const char* Name() const {
+ return "TestPaintedLayer";
+ }
+
+ virtual LayerType GetType() const {
+ return TYPE_PAINTED;
+ }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) {
+ MOZ_CRASH();
+ }
+};
+
+class TestLayerManager: public LayerManager {
+public:
+ TestLayerManager()
+ : LayerManager()
+ {}
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; }
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() {
+ RefPtr<ContainerLayer> layer = new TestContainerLayer(this);
+ return layer.forget();
+ }
+ virtual void GetBackendName(nsAString& aName) {}
+ virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
+ virtual bool BeginTransaction() { return true; }
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() {
+ NS_RUNTIMEABORT("Not implemented.");
+ return nullptr;
+ }
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
+ RefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
+ return layer.forget();
+ }
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() {
+ NS_RUNTIMEABORT("Not implemented.");
+ return nullptr;
+ }
+ virtual void SetRoot(Layer* aLayer) {}
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) { return true; }
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
+ NS_RUNTIMEABORT("Not implemented.");
+ return nullptr;
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) {}
+ virtual int32_t GetMaxTextureSize() const { return 0; }
+};
+
+class TestUserData: public LayerUserData {
+public:
+ MOCK_METHOD0(Die, void());
+ virtual ~TestUserData() { Die(); }
+};
+
+
+TEST(Layers, LayerConstructor) {
+ TestContainerLayer layer(nullptr);
+}
+
+TEST(Layers, Defaults) {
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(1.0, layer.GetOpacity());
+ ASSERT_EQ(1.0f, layer.GetPostXScale());
+ ASSERT_EQ(1.0f, layer.GetPostYScale());
+
+ ASSERT_EQ(nullptr, layer.GetNextSibling());
+ ASSERT_EQ(nullptr, layer.GetPrevSibling());
+ ASSERT_EQ(nullptr, layer.GetFirstChild());
+ ASSERT_EQ(nullptr, layer.GetLastChild());
+}
+
+TEST(Layers, Transform) {
+ TestContainerLayer layer(nullptr);
+
+ Matrix4x4 identity;
+ ASSERT_EQ(true, identity.IsIdentity());
+
+ ASSERT_EQ(identity, layer.GetTransform());
+}
+
+TEST(Layers, Type) {
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(nullptr, layer.AsPaintedLayer());
+ ASSERT_EQ(nullptr, layer.AsRefLayer());
+ ASSERT_EQ(nullptr, layer.AsColorLayer());
+}
+
+TEST(Layers, UserData) {
+ UniquePtr<TestContainerLayer> layerPtr(new TestContainerLayer(nullptr));
+ TestContainerLayer& layer = *layerPtr;
+
+ void* key1 = (void*)1;
+ void* key2 = (void*)2;
+ void* key3 = (void*)3;
+
+ ASSERT_EQ(nullptr, layer.GetUserData(key1));
+ ASSERT_EQ(nullptr, layer.GetUserData(key2));
+ ASSERT_EQ(nullptr, layer.GetUserData(key3));
+
+ TestUserData* data1 = new TestUserData;
+ TestUserData* data2 = new TestUserData;
+ TestUserData* data3 = new TestUserData;
+
+ layer.SetUserData(key1, data1);
+ layer.SetUserData(key2, data2);
+ layer.SetUserData(key3, data3);
+
+ // Also checking that the user data is returned but not free'd
+ UniquePtr<LayerUserData> d1(layer.RemoveUserData(key1));
+ UniquePtr<LayerUserData> d2(layer.RemoveUserData(key2));
+ UniquePtr<LayerUserData> d3(layer.RemoveUserData(key3));
+ ASSERT_EQ(data1, d1.get());
+ ASSERT_EQ(data2, d2.get());
+ ASSERT_EQ(data3, d3.get());
+
+ layer.SetUserData(key1, d1.release());
+ layer.SetUserData(key2, d2.release());
+ layer.SetUserData(key3, d3.release());
+
+ // Layer has ownership of data1-3, check that they are destroyed
+ EXPECT_CALL(*data1, Die());
+ EXPECT_CALL(*data2, Die());
+ EXPECT_CALL(*data3, Die());
+}
+
+static
+already_AddRefed<Layer> CreateLayer(char aLayerType, LayerManager* aManager) {
+ RefPtr<Layer> layer = nullptr;
+ if (aLayerType == 'c') {
+ layer = aManager->CreateContainerLayer();
+ } else if (aLayerType == 't') {
+ layer = aManager->CreatePaintedLayer();
+ } else if (aLayerType == 'o') {
+ layer = aManager->CreateColorLayer();
+ }
+ return layer.forget();
+}
+
+already_AddRefed<Layer> CreateLayerTree(
+ const char* aLayerTreeDescription,
+ nsIntRegion* aVisibleRegions,
+ const Matrix4x4* aTransforms,
+ RefPtr<LayerManager>& manager,
+ nsTArray<RefPtr<Layer> >& aLayersOut) {
+
+ aLayersOut.Clear();
+
+ if (!manager) {
+ manager = new TestLayerManager();
+ }
+
+ RefPtr<Layer> rootLayer = nullptr;
+ RefPtr<ContainerLayer> parentContainerLayer = nullptr;
+ RefPtr<Layer> lastLayer = nullptr;
+ int layerNumber = 0;
+ for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) {
+ if (aLayerTreeDescription[i] == '(') {
+ if (!lastLayer) {
+ printf("Syntax error, likely '(' character isn't preceded by a container.\n");
+ MOZ_CRASH();
+ }
+ parentContainerLayer = lastLayer->AsContainerLayer();
+ if (!parentContainerLayer) {
+ printf("Layer before '(' must be a container.\n");
+ MOZ_CRASH();
+ }
+ } else if (aLayerTreeDescription[i] == ')') {
+ parentContainerLayer = parentContainerLayer->GetParent();
+ lastLayer = nullptr;
+ } else {
+ RefPtr<Layer> layer = CreateLayer(aLayerTreeDescription[i], manager.get());
+ if (aVisibleRegions) {
+ layer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(aVisibleRegions[layerNumber]));
+ layer->SetEventRegions(EventRegions(aVisibleRegions[layerNumber]));
+ }
+ if (aTransforms) {
+ layer->SetBaseTransform(aTransforms[layerNumber]);
+ }
+ aLayersOut.AppendElement(layer);
+ layerNumber++;
+ if (rootLayer && !parentContainerLayer) {
+ MOZ_CRASH();
+ }
+ if (!rootLayer) {
+ rootLayer = layer;
+ }
+ if (parentContainerLayer) {
+ parentContainerLayer->InsertAfter(layer, parentContainerLayer->GetLastChild());
+ layer->SetParent(parentContainerLayer);
+ }
+ lastLayer = layer;
+ }
+ }
+ if (rootLayer) {
+ rootLayer->ComputeEffectiveTransforms(Matrix4x4());
+ manager->SetRoot(rootLayer);
+ if (rootLayer->AsLayerComposite()) {
+ // Only perform this for LayerManagerComposite
+ CompositorBridgeParent::SetShadowProperties(rootLayer);
+ }
+ }
+ return rootLayer.forget();
+}
+
+TEST(Layers, LayerTree) {
+ const char* layerTreeSyntax = "c(c(tt))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(10,10,20,20)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ nsTArray<RefPtr<Layer> > layers;
+
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
+
+ // B2G g++ doesn't like ASSERT_NE with nullptr directly. It thinks it's
+ // an int.
+ Layer* nullLayer = nullptr;
+ ASSERT_NE(nullLayer, layers[0]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[1]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[2]->AsPaintedLayer());
+ ASSERT_NE(nullLayer, layers[3]->AsPaintedLayer());
+}
+
+static void ValidateTreePointers(Layer* aLayer) {
+ if (aLayer->GetNextSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetNextSibling()->GetPrevSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetLastChild());
+ }
+ if (aLayer->GetPrevSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetPrevSibling()->GetNextSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetFirstChild());
+ }
+ if (aLayer->GetFirstChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetFirstChild()->GetParent());
+ }
+ if (aLayer->GetLastChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetLastChild()->GetParent());
+ }
+}
+
+static void ValidateTreePointers(nsTArray<RefPtr<Layer> >& aLayers) {
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ ValidateTreePointers(aLayers[i]);
+ }
+}
+
+TEST(Layers, RepositionChild) {
+ const char* layerTreeSyntax = "c(ttt)";
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ ContainerLayer* parent = root->AsContainerLayer();
+ ValidateTreePointers(layers);
+
+ // tree is currently like this (using indexes into layers):
+ // 0
+ // 1 2 3
+ ASSERT_EQ(layers[2], layers[1]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[1], layers[3]);
+ ValidateTreePointers(layers);
+
+ // now the tree is like this:
+ // 0
+ // 2 3 1
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[2]);
+ ValidateTreePointers(layers);
+
+ // no change
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[1]);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 2 1 3
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[1]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], nullptr);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 3 2 1
+ ASSERT_EQ(layers[2], layers[3]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+}
+
+class LayerMetricsWrapperTester : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ // This ensures ScrollMetadata::sNullMetadata is initialized.
+ gfxPlatform::GetPlatform();
+ }
+};
+
+TEST_F(LayerMetricsWrapperTester, SimpleTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+ LayerMetricsWrapper wrapper(root);
+
+ ASSERT_EQ(root.get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[3].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetFirstChild().IsValid());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[4].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetLastChild();
+ ASSERT_EQ(layers[6].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ LayerMetricsWrapper layer5 = wrapper;
+ wrapper = wrapper.GetPrevSibling();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_TRUE(layer5 == wrapper.GetLastChild());
+ LayerMetricsWrapper rootWrapper(root);
+ ASSERT_TRUE(rootWrapper == wrapper.GetParent());
+}
+
+static ScrollMetadata
+MakeMetadata(FrameMetrics::ViewID aId) {
+ ScrollMetadata metadata;
+ metadata.GetMetrics().SetScrollId(aId);
+ return metadata;
+}
+
+TEST_F(LayerMetricsWrapperTester, MultiFramemetricsTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+
+ nsTArray<ScrollMetadata> metadata;
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 0)); // topmost of root layer
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID));
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 1));
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 2));
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID));
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID)); // bottom of root layer
+ root->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 3));
+ layers[1]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID));
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 4));
+ layers[2]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 5));
+ layers[4]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 6));
+ layers[5]->SetScrollMetadata(metadata);
+
+ LayerMetricsWrapper wrapper(root, LayerMetricsWrapper::StartAt::TOP);
+ nsTArray<Layer*> expectedLayers;
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[1].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[3].get());
+ nsTArray<FrameMetrics::ViewID> expectedIds;
+ expectedIds.AppendElement(FrameMetrics::START_SCROLL_ID + 0);
+ expectedIds.AppendElement(FrameMetrics::NULL_SCROLL_ID);
+ expectedIds.AppendElement(FrameMetrics::START_SCROLL_ID + 1);
+ expectedIds.AppendElement(FrameMetrics::START_SCROLL_ID + 2);
+ expectedIds.AppendElement(FrameMetrics::NULL_SCROLL_ID);
+ expectedIds.AppendElement(FrameMetrics::NULL_SCROLL_ID);
+ expectedIds.AppendElement(FrameMetrics::START_SCROLL_ID + 3);
+ expectedIds.AppendElement(FrameMetrics::NULL_SCROLL_ID);
+ expectedIds.AppendElement(FrameMetrics::START_SCROLL_ID + 4);
+ expectedIds.AppendElement(FrameMetrics::NULL_SCROLL_ID);
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper = LayerMetricsWrapper(root, LayerMetricsWrapper::StartAt::BOTTOM);
+ for (int i = 5; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper = LayerMetricsWrapper(layers[4], LayerMetricsWrapper::StartAt::BOTTOM);
+ ASSERT_EQ(FrameMetrics::START_SCROLL_ID + 5, wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(FrameMetrics::START_SCROLL_ID + 4, wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(FrameMetrics::NULL_SCROLL_ID, wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(FrameMetrics::START_SCROLL_ID + 6, wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+}
diff --git a/system/graphics/tests/gtest/TestLayers.h b/system/graphics/tests/gtest/TestLayers.h
new file mode 100644
index 000000000..fc6b750f1
--- /dev/null
+++ b/system/graphics/tests/gtest/TestLayers.h
@@ -0,0 +1,52 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#ifndef GFX_TEST_LAYERS_H
+#define GFX_TEST_LAYERS_H
+
+#include "Layers.h"
+#include "nsTArray.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class TestSurfaceAllocator final : public ISurfaceAllocator
+{
+public:
+ TestSurfaceAllocator() {}
+ ~TestSurfaceAllocator() override {}
+
+ bool IsSameProcess() const override { return true; }
+};
+
+} // layers
+} // mozilla
+
+/* Create layer tree from a simple layer tree description syntax.
+ * Each index is either the first letter of the layer type or
+ * a '(',')' to indicate the start/end of the child layers.
+ * The aim of this function is to remove hard to read
+ * layer tree creation code.
+ *
+ * Example "c(c(c(tt)t))" would yield:
+ * c
+ * |
+ * c
+ * / \
+ * c t
+ * / \
+ * t t
+ */
+already_AddRefed<mozilla::layers::Layer> CreateLayerTree(
+ const char* aLayerTreeDescription,
+ nsIntRegion* aVisibleRegions,
+ const mozilla::gfx::Matrix4x4* aTransforms,
+ RefPtr<mozilla::layers::LayerManager>& aLayerManager,
+ nsTArray<RefPtr<mozilla::layers::Layer> >& aLayersOut);
+
+
+#endif
+
diff --git a/system/graphics/tests/gtest/TestMatrix.cpp b/system/graphics/tests/gtest/TestMatrix.cpp
new file mode 100644
index 000000000..1d0b831a4
--- /dev/null
+++ b/system/graphics/tests/gtest/TestMatrix.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/gfx/Matrix.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+static Rect NudgedToInt(const Rect& aRect) {
+ Rect r(aRect);
+ r.NudgeToIntegers();
+ return r;
+}
+
+TEST(Matrix, TransformAndClipRect)
+{
+ Rect c(100, 100, 100, 100);
+ Matrix4x4 m;
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 250, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 20, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 250, 100, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 150, 20, 100), c).IsEmpty());
+
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 50, 100, 100), c))
+ .IsEqualInterior(Rect(100, 100, 50, 50)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(150, 50, 100, 100), c))
+ .IsEqualInterior(Rect(150, 100, 50, 50)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(150, 150, 100, 100), c))
+ .IsEqualInterior(Rect(150, 150, 50, 50)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 150, 100, 100), c))
+ .IsEqualInterior(Rect(100, 150, 50, 50)));
+
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(110, 110, 80, 80), c))
+ .IsEqualInterior(Rect(110, 110, 80, 80)));
+
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 50, 200, 200), c))
+ .IsEqualInterior(Rect(100, 100, 100, 100)));
+
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 50, 200, 100), c))
+ .IsEqualInterior(Rect(100, 100, 100, 50)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 150, 200, 100), c))
+ .IsEqualInterior(Rect(100, 150, 100, 50)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(50, 50, 100, 200), c))
+ .IsEqualInterior(Rect(100, 100, 50, 100)));
+ EXPECT_TRUE(NudgedToInt(m.TransformAndClipBounds(Rect(150, 50, 100, 200), c))
+ .IsEqualInterior(Rect(150, 100, 50, 100)));
+}
diff --git a/system/graphics/tests/gtest/TestMoz2D.cpp b/system/graphics/tests/gtest/TestMoz2D.cpp
new file mode 100644
index 000000000..3385c7ed6
--- /dev/null
+++ b/system/graphics/tests/gtest/TestMoz2D.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "TestBase.h"
+#include "TestPoint.h"
+#include "TestScaling.h"
+#include "TestBugs.h"
+
+TEST(Moz2D, Bugs) {
+ TestBugs* test = new TestBugs();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Point) {
+ TestBase* test = new TestPoint();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Scaling) {
+ TestBase* test = new TestScaling();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
diff --git a/system/graphics/tests/gtest/TestPolygon.cpp b/system/graphics/tests/gtest/TestPolygon.cpp
new file mode 100644
index 000000000..962722619
--- /dev/null
+++ b/system/graphics/tests/gtest/TestPolygon.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "PolygonTestUtils.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+using namespace mozilla::gfx;
+
+TEST(Polygon3D, TriangulateRectangle)
+{
+ const Polygon3D p {
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f)
+ };
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
+ };
+
+ AssertArrayEQ(triangles, expected);
+}
+
+TEST(Polygon3D, TriangulatePentagon)
+{
+ const Polygon3D p {
+ Point3D(0.0f, 0.0f, 1.0f),
+ Point3D(0.0f, 1.0f, 1.0f),
+ Point3D(0.5f, 1.5f, 1.0f),
+ Point3D(1.0f, 1.0f, 1.0f),
+ Point3D(1.0f, 0.0f, 1.0f)
+ };
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(0.5f, 1.5f)),
+ Triangle(Point(0.0f, 0.0f), Point(0.5f, 1.5f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
+ };
+
+ AssertArrayEQ(triangles, expected);
+}
+
+TEST(Polygon3D, ClipRectangle)
+{
+ Polygon3D clipped, expected;
+
+ Polygon3D polygon {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 0.0f, 0.0f)
+ };
+
+ clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
+ EXPECT_TRUE(clipped == polygon);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
+ expected = Polygon3D {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.0f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
+ expected = Polygon3D {
+ Point3D(0.2f, 0.2f, 0.0f),
+ Point3D(0.2f, 1.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 0.2f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
+ expected = Polygon3D {
+ Point3D(0.2f, 0.2f, 0.0f),
+ Point3D(0.2f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.2f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+}
+
+TEST(Polygon3D, ClipTriangle)
+{
+ Polygon3D clipped, expected;
+ const Polygon3D polygon {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f)
+ };
+
+ clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
+ expected = Polygon3D {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 1.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
+ expected = Polygon3D {
+ Point3D(0.0f, 0.0f, 0.0f),
+ Point3D(0.0f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.8f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
+ expected = Polygon3D {
+ Point3D(0.2f, 0.2f, 0.0f),
+ Point3D(0.2f, 1.0f, 0.0f),
+ Point3D(1.0f, 1.0f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+
+
+ clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
+ expected = Polygon3D {
+ Point3D(0.2f, 0.2f, 0.0f),
+ Point3D(0.2f, 0.8f, 0.0f),
+ Point3D(0.8f, 0.8f, 0.0f)
+ };
+ EXPECT_TRUE(clipped == expected);
+}
diff --git a/system/graphics/tests/gtest/TestQcms.cpp b/system/graphics/tests/gtest/TestQcms.cpp
new file mode 100644
index 000000000..9a0cff019
--- /dev/null
+++ b/system/graphics/tests/gtest/TestQcms.cpp
@@ -0,0 +1,168 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "qcms.h"
+#include "transform_util.h"
+
+const size_t allGBSize = 1 * 256 * 256 * 4;
+static unsigned char* createAllGB() {
+ unsigned char* buff = (unsigned char*)malloc(allGBSize);
+ int pos = 0;
+ for (int r = 0; r < 1; r++) { // Skip all R values for speed
+ for (int g = 0; g < 256; g++) {
+ for (int b = 0; b < 256; b++) {
+ buff[pos * 4 + 0] = r;
+ buff[pos * 4 + 1] = g;
+ buff[pos * 4 + 2] = b;
+ buff[pos * 4 + 3] = 0x80;
+ pos++;
+ }
+ }
+ }
+
+ return buff;
+}
+
+TEST(GfxQcms, Identity) {
+ // XXX: This means that we can't have qcms v2 unit test
+ // without changing the qcms API.
+ qcms_enable_iccv4();
+
+ qcms_profile* input_profile = qcms_profile_sRGB();
+ qcms_profile* output_profile = qcms_profile_sRGB();
+
+ EXPECT_FALSE(qcms_profile_is_bogus(input_profile));
+ EXPECT_FALSE(qcms_profile_is_bogus(output_profile));
+
+ const qcms_intent intent = QCMS_INTENT_DEFAULT;
+ qcms_data_type input_type = QCMS_DATA_RGBA_8;
+ qcms_data_type output_type = QCMS_DATA_RGBA_8;
+
+ qcms_transform* transform = qcms_transform_create(input_profile, input_type,
+ output_profile, output_type,
+ intent);
+
+ unsigned char *data_in = createAllGB();;
+ unsigned char *data_out = (unsigned char*)malloc(allGBSize);
+ qcms_transform_data(transform, data_in, data_out, allGBSize / 4);
+
+ qcms_profile_release(input_profile);
+ qcms_profile_release(output_profile);
+ qcms_transform_release(transform);
+
+ free(data_in);
+ free(data_out);
+}
+
+TEST(GfxQcms, LutInverseCrash) {
+ uint16_t lutTable1[] = {
+ 0x0000, 0x0000, 0x0000, 0x8000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ };
+ uint16_t lutTable2[] = {
+ 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ };
+
+ // Crash/Assert test
+ lut_inverse_interp16((uint16_t)5, lutTable1, (int)mozilla::ArrayLength(lutTable1));
+ lut_inverse_interp16((uint16_t)5, lutTable2, (int)mozilla::ArrayLength(lutTable2));
+}
+
+TEST(GfxQcms, LutInverse) {
+ // mimic sRGB_v4_ICC mBA Output
+ //
+ // XXXX
+ // X
+ // X
+ // XXXX
+ uint16_t value;
+ uint16_t lutTable[256];
+
+ for (int i = 0; i < 20; i++) {
+ lutTable[i] = 0;
+ }
+
+ for (int i = 20; i < 200; i++) {
+ lutTable[i] = (i - 20) * 0xFFFF / (200 - 20);
+ }
+
+ for (int i = 200; i < (int)mozilla::ArrayLength(lutTable); i++) {
+ lutTable[i] = 0xFFFF;
+ }
+
+ for (uint16_t i = 0; i < 65535; i++) {
+ lut_inverse_interp16(i, lutTable, (int)mozilla::ArrayLength(lutTable));
+ }
+
+ // Lookup the interesting points
+
+ value = lut_inverse_interp16(0, lutTable, (int)mozilla::ArrayLength(lutTable));
+ EXPECT_LE(value, 20 * 256);
+
+ value = lut_inverse_interp16(1, lutTable, (int)mozilla::ArrayLength(lutTable));
+ EXPECT_GT(value, 20 * 256);
+
+ value = lut_inverse_interp16(65535, lutTable, (int)mozilla::ArrayLength(lutTable));
+ EXPECT_LT(value, 201 * 256);
+}
+
+TEST(GfxQcms, LutInverseNonMonotonic) {
+ // Make sure we behave sanely for non monotic functions
+ // X X X
+ // X X X
+ // X X X
+ uint16_t lutTable[256];
+
+ for (int i = 0; i < 100; i++) {
+ lutTable[i] = (i - 0) * 0xFFFF / (100 - 0);
+ }
+
+ for (int i = 100; i < 200; i++) {
+ lutTable[i] = (i - 100) * 0xFFFF / (200 - 100);
+ }
+
+ for (int i = 200; i < 256; i++) {
+ lutTable[i] = (i - 200) * 0xFFFF / (256 - 200);
+ }
+
+ for (uint16_t i = 0; i < 65535; i++) {
+ lut_inverse_interp16(i, lutTable, (int)mozilla::ArrayLength(lutTable));
+ }
+
+ // Make sure we don't crash, hang or let sanitizers do their magic
+}
diff --git a/system/graphics/tests/gtest/TestRect.cpp b/system/graphics/tests/gtest/TestRect.cpp
new file mode 100644
index 000000000..056469507
--- /dev/null
+++ b/system/graphics/tests/gtest/TestRect.cpp
@@ -0,0 +1,450 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "nsRect.h"
+#include "gfxRect.h"
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+template <class RectType>
+static bool
+TestConstructors()
+{
+ // Create a rectangle
+ RectType rect1(10, 20, 30, 40);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect1.x == 10 && rect1.y == 20 &&
+ rect1.width == 30 && rect1.height == 40) <<
+ "[1] Make sure the rectangle was properly initialized with constructor";
+
+ // Create a second rect using the copy constructor
+ RectType rect2(rect1);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect2.x == rect1.x && rect2.y == rect2.y &&
+ rect2.width == rect2.width && rect2.height == rect2.height) <<
+ "[2] Make sure the rectangle was properly initialized with copy constructor";
+
+
+ EXPECT_TRUE(!rect1.IsEmpty() && rect1.IsFinite() &&
+ !rect2.IsEmpty() && rect2.IsFinite()) <<
+ "[3] These rectangles are not empty and are finite";
+
+ return true;
+}
+
+template <class RectType>
+static bool
+TestEqualityOperator()
+{
+ RectType rect1(10, 20, 30, 40);
+ RectType rect2(rect1);
+
+ // Test the equality operator
+ EXPECT_TRUE(rect1 == rect2) <<
+ "[1] Test the equality operator";
+
+ EXPECT_FALSE(!rect1.IsEqualInterior(rect2)) <<
+ "[2] Test the inequality operator";
+
+ // Make sure that two empty rects are equal
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ EXPECT_TRUE(rect1 == rect2) <<
+ "[3] Make sure that two empty rects are equal";
+
+ return true;
+}
+
+template <class RectType>
+static bool
+TestContainment()
+{
+ RectType rect1(10, 10, 50, 50);
+
+ // Test the point containment methods
+ //
+
+ // Basic test of a point in the middle of the rect
+ EXPECT_FALSE(!rect1.Contains(rect1.x + rect1.width/2, rect1.y + rect1.height/2)) <<
+ "[1] Basic test of a point in the middle of the rect";
+
+ // Test against a point at the left/top edges
+ EXPECT_FALSE(!rect1.Contains(rect1.x, rect1.y)) <<
+ "[2] Test against a point at the left/top edges";
+
+ // Test against a point at the right/bottom extents
+ EXPECT_FALSE(rect1.Contains(rect1.XMost(), rect1.YMost())) <<
+ "[3] Test against a point at the right/bottom extents";
+
+ // Test the rect containment methods
+ //
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Contains(rect2)) <<
+ "[4] Test against a rect that's the same as rect1";
+
+ // Test against a rect whose left edge (only) is outside of rect1
+ rect2.x--;
+ EXPECT_FALSE(rect1.Contains(rect2)) <<
+ "[5] Test against a rect whose left edge (only) is outside of rect1";
+ rect2.x++;
+
+ // Test against a rect whose top edge (only) is outside of rect1
+ rect2.y--;
+ EXPECT_FALSE(rect1.Contains(rect2)) <<
+ "[6] Test against a rect whose top edge (only) is outside of rect1";
+ rect2.y++;
+
+ // Test against a rect whose right edge (only) is outside of rect1
+ rect2.x++;
+ EXPECT_FALSE(rect1.Contains(rect2)) <<
+ "[7] Test against a rect whose right edge (only) is outside of rect1";
+ rect2.x--;
+
+ // Test against a rect whose bottom edge (only) is outside of rect1
+ rect2.y++;
+ EXPECT_FALSE(rect1.Contains(rect2)) <<
+ "[8] Test against a rect whose bottom edge (only) is outside of rect1";
+ rect2.y--;
+
+ return true;
+}
+
+// Test the method that returns a boolean result but doesn't return a
+// a rectangle
+template <class RectType>
+static bool
+TestIntersects()
+{
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Intersects(rect2)) <<
+ "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!rect1.Contains(rect2) || !rect1.Intersects(rect2)) <<
+ "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Make sure inflate and deflate worked correctly
+ EXPECT_TRUE(rect1.IsEqualInterior(rect2)) <<
+ "[3] Make sure inflate and deflate worked correctly";
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.x--;
+ EXPECT_FALSE(!rect1.Intersects(rect2)) <<
+ "[4] Test against a rect that overlaps the left edge of rect1";
+ rect2.x++;
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.x -= rect2.width;
+ EXPECT_FALSE(rect1.Intersects(rect2)) <<
+ "[5] Test against a rect that's outside of rect1 on the left";
+ rect2.x += rect2.width;
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.y--;
+ EXPECT_FALSE(!rect1.Intersects(rect2)) <<
+ "[6] Test against a rect that overlaps the top edge of rect1";
+ rect2.y++;
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.y -= rect2.height;
+ EXPECT_FALSE(rect1.Intersects(rect2)) <<
+ "[7] Test against a rect that's outside of rect1 on the top";
+ rect2.y += rect2.height;
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.x++;
+ EXPECT_FALSE(!rect1.Intersects(rect2)) <<
+ "[8] Test against a rect that overlaps the right edge of rect1";
+ rect2.x--;
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.x += rect2.width;
+ EXPECT_FALSE(rect1.Intersects(rect2)) <<
+ "[9] Test against a rect that's outside of rect1 on the right";
+ rect2.x -= rect2.width;
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.y++;
+ EXPECT_FALSE(!rect1.Intersects(rect2)) <<
+ "[10] Test against a rect that overlaps the bottom edge of rect1";
+ rect2.y--;
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.y += rect2.height;
+ EXPECT_FALSE(rect1.Intersects(rect2)) <<
+ "[11] Test against a rect that's outside of rect1 on the bottom";
+ rect2.y -= rect2.height;
+
+ return true;
+}
+
+// Test the method that returns a boolean result and an intersection rect
+template <class RectType>
+static bool
+TestIntersection()
+{
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+ RectType dest;
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) || !(dest.IsEqualInterior(rect1))) <<
+ "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) || !(dest.IsEqualInterior(rect2))) <<
+ "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.x--;
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.x, rect1.y, rect1.width - 1, rect1.height)))) <<
+ "[3] Test against a rect that overlaps the left edge of rect1";
+ rect2.x++;
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.x -= rect2.width;
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2)) <<
+ "[4] Test against a rect that's outside of rect1 on the left";
+ // Make sure an empty rect is returned
+ EXPECT_FALSE(!dest.IsEmpty()) <<
+ "[4] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+ rect2.x += rect2.width;
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.y--;
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.x, rect1.y, rect1.width, rect1.height - 1)))) <<
+ "[5] Test against a rect that overlaps the top edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+ rect2.y++;
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.y -= rect2.height;
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2)) <<
+ "[6] Test against a rect that's outside of rect1 on the top";
+ // Make sure an empty rect is returned
+ EXPECT_FALSE(!dest.IsEmpty()) <<
+ "[6] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[6b] Should be finite";
+ rect2.y += rect2.height;
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.x++;
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.x + 1, rect1.y, rect1.width - 1, rect1.height)))) <<
+ "[7] Test against a rect that overlaps the right edge of rect1";
+ rect2.x--;
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.x += rect2.width;
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2)) <<
+ "[8] Test against a rect that's outside of rect1 on the right";
+ // Make sure an empty rect is returned
+ EXPECT_FALSE(!dest.IsEmpty()) <<
+ "[8] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[8b] Should be finite";
+ rect2.x -= rect2.width;
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.y++;
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.x, rect1.y + 1, rect1.width, rect1.height - 1)))) <<
+ "[9] Test against a rect that overlaps the bottom edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[9b] Should be finite";
+ rect2.y--;
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.y += rect2.height;
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2)) <<
+ "[10] Test against a rect that's outside of rect1 on the bottom";
+ // Make sure an empty rect is returned
+ EXPECT_FALSE(!dest.IsEmpty()) <<
+ "[10] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[10b] Should be finite";
+ rect2.y -= rect2.height;
+
+ // Test against a rect with zero width or height
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(150, 100, 0, 100);
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2) || !dest.IsEmpty()) <<
+ "[11] Intersection of rects with zero width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[11b] Should be finite";
+
+ // Tests against a rect with negative width or height
+ //
+
+ // Test against a rect with negative width
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(100, 100, -100, 100);
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2) || !dest.IsEmpty()) <<
+ "[12] Intersection of rects with negative width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[12b] Should be finite";
+
+ // Those two rects exactly overlap in some way...
+ // but we still want to return an empty rect
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(200, 200, -100, -100);
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2) || !dest.IsEmpty()) <<
+ "[13] Intersection of rects with negative width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[13b] Should be finite";
+
+ // Test against two identical rects with negative height
+ rect1.SetRect(100, 100, 100, -100);
+ rect2.SetRect(100, 100, 100, -100);
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2) || !dest.IsEmpty()) <<
+ "[14] Intersection of rects with negative width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[14b] Should be finite";
+
+ return true;
+}
+
+template <class RectType>
+static bool
+TestUnion()
+{
+ RectType rect1;
+ RectType rect2(10, 10, 50, 50);
+ RectType dest;
+
+ // Check the case where the receiver is an empty rect
+ rect1.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_FALSE(dest.IsEmpty() || !dest.IsEqualInterior(rect2)) <<
+ "[1] Check the case where the receiver is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[1b] Should be finite";
+
+ // Check the case where the source rect is an empty rect
+ rect1 = rect2;
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_FALSE(dest.IsEmpty() || !dest.IsEqualInterior(rect1)) <<
+ "[2] Check the case where the source rect is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[2b] Should be finite";
+
+ // Test the case where both rects are empty
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_FALSE(!dest.IsEmpty()) <<
+ "[3] Test the case where both rects are empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[3b] Should be finite";
+
+ // Test union case where the two rects don't overlap at all
+ rect1.SetRect(10, 10, 50, 50);
+ rect2.SetRect(100, 100, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_FALSE(dest.IsEmpty() ||
+ !(dest.IsEqualInterior(RectType(rect1.x, rect1.y, rect2.XMost() - rect1.x, rect2.YMost() - rect1.y)))) <<
+ "[4] Test union case where the two rects don't overlap at all";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+
+ // Test union case where the two rects overlap
+ rect1.SetRect(30, 30, 50, 50);
+ rect2.SetRect(10, 10, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_FALSE(dest.IsEmpty() ||
+ !(dest.IsEqualInterior(RectType(rect2.x, rect2.y, rect1.XMost() - rect2.x, rect1.YMost() - rect2.y)))) <<
+ "[5] Test union case where the two rects overlap";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+
+ return true;
+}
+
+static bool
+TestFiniteGfx()
+{
+ float posInf = std::numeric_limits<float>::infinity();
+ float negInf = -std::numeric_limits<float>::infinity();
+ float justNaN = std::numeric_limits<float>::quiet_NaN();
+
+ gfxFloat values[4] = {5.0, 10.0, 15.0, 20.0};
+
+ // Try the "non-finite" values for x, y, width, height, one at a time
+ for (int i=0; i<4; i+=1) {
+ values[i] = posInf;
+ gfxRect rectPosInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectPosInf.IsFinite()) << "For +inf (" << values[0] << "," << values[1] << "," << values[2] << "," << values[3] << ")";
+
+ values[i] = negInf;
+ gfxRect rectNegInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNegInf.IsFinite()) << "For -inf (" << values[0] << "," << values[1] << "," << values[2] << "," << values[3] << ")";
+
+ values[i] = justNaN;
+ gfxRect rectNaN(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNaN.IsFinite()) << "For NaN (" << values[0] << "," << values[1] << "," << values[2] << "," << values[3] << ")";
+
+ // Reset to a finite value...
+ values[i] = 5.0*i;
+ }
+
+ return true;
+}
+
+// We want to test nsRect values that are still in range but where
+// the implementation is at risk of overflowing
+template <class RectType>
+static bool
+TestBug1135677()
+{
+ RectType rect1(1073741344, 1073741344, 1073756696, 1073819936);
+ RectType rect2(1073741820, 1073741820, 14400, 77640);
+ RectType dest;
+
+ dest = rect1.Intersect(rect2);
+
+ EXPECT_TRUE(dest.x == 1073741820 && dest.y == 1073741820 &&
+ dest.width == 14400 && dest.height == 77640) <<
+ "[1] Operation should not overflow internally.";
+
+ return true;
+}
+
+TEST(Gfx, nsRect) {
+ TestConstructors<nsRect>();
+ TestEqualityOperator<nsRect>();
+ TestContainment<nsRect>();
+ TestIntersects<nsRect>();
+ TestIntersection<nsRect>();
+ TestUnion<nsRect>();
+ TestBug1135677<nsRect>();
+}
+
+TEST(Gfx, nsIntRect) {
+ TestConstructors<nsIntRect>();
+ TestEqualityOperator<nsIntRect>();
+ TestContainment<nsIntRect>();
+ TestIntersects<nsIntRect>();
+ TestIntersection<nsIntRect>();
+ TestUnion<nsIntRect>();
+ TestBug1135677<nsIntRect>();
+}
+
+TEST(Gfx, gfxRect) {
+ TestConstructors<gfxRect>();
+ // Skip TestEqualityOperator<gfxRect>(); as gfxRect::operator== is private
+ TestContainment<gfxRect>();
+ TestIntersects<gfxRect>();
+ TestIntersection<gfxRect>();
+ TestUnion<gfxRect>();
+ TestBug1135677<gfxRect>();
+ TestFiniteGfx();
+}
diff --git a/system/graphics/tests/gtest/TestRegion.cpp b/system/graphics/tests/gtest/TestRegion.cpp
new file mode 100644
index 000000000..ea8e323f9
--- /dev/null
+++ b/system/graphics/tests/gtest/TestRegion.cpp
@@ -0,0 +1,837 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+
+#include "PingPongRegion.h"
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+#include "nsRect.h"
+#include "nsRegion.h"
+#include "RegionBuilder.h"
+#include "mozilla/gfx/TiledRegion.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace std;
+using namespace mozilla::gfx;
+
+class TestLargestRegion {
+public:
+ static void TestSingleRect(nsRect r) {
+ nsRegion region(r);
+ EXPECT_TRUE(region.GetLargestRectangle().IsEqualInterior(r));
+ }
+ // Construct a rectangle, remove part of it, then check the remainder
+ static void TestNonRectangular() {
+ nsRegion r(nsRect(0, 0, 30, 30));
+
+ const int nTests = 19;
+ struct {
+ nsRect rect;
+ int64_t expectedArea;
+ } tests[nTests] = {
+ // Remove a 20x10 chunk from the square
+ { nsRect(0, 0, 20, 10), 600 },
+ { nsRect(10, 0, 20, 10), 600 },
+ { nsRect(10, 20, 20, 10), 600 },
+ { nsRect(0, 20, 20, 10), 600 },
+ // Remove a 10x20 chunk from the square
+ { nsRect(0, 0, 10, 20), 600 },
+ { nsRect(20, 0, 10, 20), 600 },
+ { nsRect(20, 10, 10, 20), 600 },
+ { nsRect(0, 10, 10, 20), 600 },
+ // Remove the center 10x10
+ { nsRect(10, 10, 10, 10), 300 },
+ // Remove the middle column
+ { nsRect(10, 0, 10, 30), 300 },
+ // Remove the middle row
+ { nsRect(0, 10, 30, 10), 300 },
+ // Remove the corners 10x10
+ { nsRect(0, 0, 10, 10), 600 },
+ { nsRect(20, 20, 10, 10), 600 },
+ { nsRect(20, 0, 10, 10), 600 },
+ { nsRect(0, 20, 10, 10), 600 },
+ // Remove the corners 20x20
+ { nsRect(0, 0, 20, 20), 300 },
+ { nsRect(10, 10, 20, 20), 300 },
+ { nsRect(10, 0, 20, 20), 300 },
+ { nsRect(0, 10, 20, 20), 300 }
+ };
+
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+ r2.Sub(r, tests[i].rect);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.width * largest.height == tests[i].expectedArea) <<
+ "Did not successfully find largest rectangle in non-rectangular region on iteration " << i;
+ }
+
+ }
+ static void TwoRectTest() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ const int nTests = 4;
+ struct {
+ nsRect rect1, rect2;
+ int64_t expectedArea;
+ } tests[nTests] = {
+ { nsRect(0, 0, 75, 40), nsRect(0, 60, 75, 40), 2500 },
+ { nsRect(25, 0, 75, 40), nsRect(25, 60, 75, 40), 2500 },
+ { nsRect(25, 0, 75, 40), nsRect(0, 60, 75, 40), 2000 },
+ { nsRect(0, 0, 75, 40), nsRect(25, 60, 75, 40), 2000 },
+ };
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+
+ r2.Sub(r, tests[i].rect1);
+ r2.Sub(r2, tests[i].rect2);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.width * largest.height == tests[i].expectedArea) <<
+ "Did not successfully find largest rectangle in two-rect-subtract region on iteration " << i;
+ }
+ }
+ static void TestContainsSpecifiedRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 300, 10, 10)).IsEqualInterior(nsRect(0, 300, 50, 50))) <<
+ "Chose wrong rectangle";
+ }
+ static void TestContainsSpecifiedOverflowingRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 290, 10, 20)).IsEqualInterior(nsRect(0, 300, 50, 50))) <<
+ "Chose wrong rectangle";
+ }
+};
+
+TEST(Gfx, RegionSingleRect) {
+ TestLargestRegion::TestSingleRect(nsRect(0, 52, 720, 480));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 50, 20));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-20, -40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-10, -10, 20, 20));
+}
+
+TEST(Gfx, RegionNonRectangular) {
+ TestLargestRegion::TestNonRectangular();
+}
+
+TEST(Gfx, RegionTwoRectTest) {
+ TestLargestRegion::TwoRectTest();
+}
+
+TEST(Gfx, RegionContainsSpecifiedRect) {
+ TestLargestRegion::TestContainsSpecifiedRect();
+}
+
+TEST(Gfx, RegionTestContainsSpecifiedOverflowingRect) {
+ TestLargestRegion::TestContainsSpecifiedOverflowingRect();
+}
+
+TEST(Gfx, RegionScaleToInside) {
+ { // no rectangles
+ nsRegion r;
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result;
+
+ EXPECT_TRUE(result.IsEqual(scaled)) <<
+ "scaled result incorrect";
+ }
+
+ { // one rectangle
+ nsRegion r(nsRect(0,44760,19096,264));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0,746,318,4));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) <<
+ "scaled result incorrect";
+ }
+
+
+ { // the first rectangle gets adjusted
+ nsRegion r(nsRect(0,44760,19096,264));
+ r.Or(r, nsRect(0,45024,19360,1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0,746,318,5));
+ result.Or(result, mozilla::gfx::IntRect(0,751,322,17));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) <<
+ "scaled result incorrect";
+ }
+
+ { // the second rectangle gets adjusted
+ nsRegion r(nsRect(0,44760,19360,264));
+ r.Or(r, nsRect(0,45024,19096,1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0,746,322,4));
+ result.Or(result, mozilla::gfx::IntRect(0,750,318,18));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) <<
+ "scaled result incorrect";
+ }
+
+}
+
+
+TEST(Gfx, RegionSimplify) {
+ { // ensure simplify works on a single rect
+ nsRegion r(nsRect(0,100,200,100));
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,200,100));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not the same";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,300,300));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // two rectangle on the first span
+ // one on the second
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+ r.Or(r, nsRect(250,100,50,100));
+
+ EXPECT_TRUE(r.GetNumRects() == 3) <<
+ "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,300,300));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+ r.Or(r, nsRect(250,100,50,100));
+ r.Sub(r, nsRect(200,200,40,200));
+
+ EXPECT_TRUE(r.GetNumRects() == 4) <<
+ "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,300,300));
+ result.Sub(result, nsRect(200,100,40,300));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // three spans of rectangles
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+ r.Or(r, nsRect(250,100,50,50));
+ r.Sub(r, nsRect(200,200,40,200));
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,300,300));
+ result.Sub(result, nsRect(200,100,40,300));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // three spans of rectangles and an unmerged rectangle
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+ r.Or(r, nsRect(250,100,50,50));
+ r.Sub(r, nsRect(200,200,40,200));
+ r.Or(r, nsRect(250,900,150,50));
+
+ r.SimplifyOutwardByArea(100*100);
+
+ nsRegion result(nsRect(0,100,300,300));
+ result.Sub(result, nsRect(200,100,40,300));
+ result.Or(result, nsRect(250,900,150,50));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // unmerged regions
+ nsRegion r(nsRect(0,100,200,100));
+ r.Or(r, nsRect(0,200,300,200));
+
+ r.SimplifyOutwardByArea(100);
+
+ nsRegion result(nsRect(0,100,200,100));
+ result.Or(result, nsRect(0,200,300,200));
+
+ EXPECT_TRUE(r.IsEqual(result)) <<
+ "regions not merged";
+ }
+
+ { // empty region
+ // just make sure this doesn't crash.
+ nsRegion r;
+ r.SimplifyOutwardByArea(100);
+ }
+}
+
+TEST(Gfx, RegionContains)
+{
+ { // ensure Contains works on a simple region
+ nsRegion r(nsRect(0, 0, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(0, 99));
+ EXPECT_TRUE(r.Contains(99, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+
+ EXPECT_FALSE(r.Contains(-1, 50));
+ EXPECT_FALSE(r.Contains(100, 50));
+ EXPECT_FALSE(r.Contains(50, -1));
+ EXPECT_FALSE(r.Contains(50, 100));
+
+ EXPECT_TRUE(r.Contains(nsRect(0, 0, 100, 100)));
+ EXPECT_TRUE(r.Contains(nsRect(99, 99, 1, 1)));
+
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ }
+
+ { // empty regions contain nothing
+ nsRegion r(nsRect(100, 100, 0, 0));
+
+ EXPECT_FALSE(r.Contains(0, 0));
+ EXPECT_FALSE(r.Contains(100, 100));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ }
+
+ { // complex region contain tests
+ // The region looks like this, with two squares that overlap.
+ // (hard to do accurately with ASCII art)
+ // +------+
+ // | |
+ // | +--+
+ // | |
+ // +--+ |
+ // | |
+ // +------+
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.OrWith(nsRect(50, 50, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+ EXPECT_TRUE(r.Contains(50, 100));
+ EXPECT_TRUE(r.Contains(100, 50));
+ EXPECT_TRUE(r.Contains(149, 149));
+
+ EXPECT_FALSE(r.Contains(49, 100));
+ EXPECT_FALSE(r.Contains(100, 49));
+ EXPECT_FALSE(r.Contains(150, 150));
+
+ EXPECT_TRUE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(49, 99, 2, 2)));
+ }
+
+ { // region with a hole
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.SubOut(nsRect(40, 40, 10, 10));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(39, 39));
+ EXPECT_FALSE(r.Contains(40, 40));
+ EXPECT_FALSE(r.Contains(49, 49));
+ EXPECT_TRUE(r.Contains(50, 50));
+
+ EXPECT_FALSE(r.Contains(nsRect(40, 40, 10, 10)));
+ EXPECT_FALSE(r.Contains(nsRect(39, 39, 2, 2)));
+ }
+}
+
+#define DILATE_VALUE 0x88
+#define REGION_VALUE 0xff
+
+struct RegionBitmap {
+ RegionBitmap(unsigned char *bitmap, int width, int height) : bitmap(bitmap), width(width), height(height) {}
+
+ void clear() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ bitmap[x + y * width] = 0;
+ }
+ }
+ }
+
+ void set(nsRegion &region) {
+ clear();
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& r = iter.Get();
+ for (int y = r.y; y < r.YMost(); y++) {
+ for (int x = r.x; x < r.XMost(); x++) {
+ bitmap[x + y * width] = REGION_VALUE;
+ }
+ }
+ }
+ }
+
+ void dilate() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (bitmap[x + y * width] == REGION_VALUE) {
+ for (int yn = max(y - 1, 0); yn <= min(y + 1, height - 1); yn++) {
+ for (int xn = max(x - 1, 0); xn <= min(x + 1, width - 1); xn++) {
+ if (bitmap[xn + yn * width] == 0)
+ bitmap[xn + yn * width] = DILATE_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ void compare(RegionBitmap &reference) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ EXPECT_EQ(bitmap[x + y * width], reference.bitmap[x + y * width]);
+ }
+ }
+ }
+
+ unsigned char *bitmap;
+ int width;
+ int height;
+};
+
+void VisitEdge(void *closure, VisitSide side, int x1, int y1, int x2, int y2)
+{
+ EXPECT_GE(x2, x1);
+ RegionBitmap *visitor = static_cast<RegionBitmap*>(closure);
+ unsigned char *bitmap = visitor->bitmap;
+ const int width = visitor->width;
+
+ if (side == VisitSide::TOP) {
+ while (x1 != x2) {
+ bitmap[x1 + (y1 - 1) * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ while (x1 != x2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::LEFT) {
+ while (y1 != y2) {
+ bitmap[x1 - 1 + y1 *width] = DILATE_VALUE;
+ y1++;
+ }
+ } else if (side == VisitSide::RIGHT) {
+ while (y1 != y2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ y1++;
+ }
+ }
+}
+
+void TestVisit(nsRegion &r)
+{
+ auto reference = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ auto result = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ RegionBitmap ref(reference.get(), 600, 600);
+ RegionBitmap res(result.get(), 600, 600);
+
+ ref.set(r);
+ ref.dilate();
+
+ res.set(r);
+ r.VisitEdges(VisitEdge, &res);
+ res.compare(ref);
+}
+
+TEST(Gfx, RegionVisitEdges) {
+ { // visit edges
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(20, 120, 200, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 1 pixel inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 2 pixels inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(122, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ {
+ // only corner of the rects are touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // corners are 1 pixel away
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // not touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(130, 120, 100, 100));
+ r.Or(r, nsRect(240, 20, 100, 100));
+
+ TestVisit(r);
+ }
+
+ { // rect with a hole in it
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Sub(r, nsRect(40, 40, 10, 10));
+
+ TestVisit(r);
+ }
+ {
+ // left overs
+ nsRegion r(nsRect(20, 20, 10, 10));
+ r.Or(r, nsRect(50, 20, 10, 10));
+ r.Or(r, nsRect(90, 20, 10, 10));
+ r.Or(r, nsRect(24, 30, 10, 10));
+ r.Or(r, nsRect(20, 40, 15, 10));
+ r.Or(r, nsRect(50, 40, 15, 10));
+ r.Or(r, nsRect(90, 40, 15, 10));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // two upper rects followed by a lower one
+ // on the same line
+ nsRegion r(nsRect(5, 5, 50, 50));
+ r.Or(r, nsRect(100, 5, 50, 50));
+ r.Or(r, nsRect(200, 50, 50, 50));
+
+ TestVisit(r);
+ }
+
+ {
+ // bug 1130978.
+ nsRegion r(nsRect(4, 1, 61, 49));
+ r.Or(r, nsRect(115, 1, 99, 49));
+ r.Or(r, nsRect(115, 49, 99, 1));
+ r.Or(r, nsRect(12, 50, 11, 5));
+ r.Or(r, nsRect(25, 50, 28, 5));
+ r.Or(r, nsRect(115, 50, 99, 5));
+ r.Or(r, nsRect(115, 55, 99, 12));
+
+ TestVisit(r);
+ }
+}
+
+TEST(Gfx, PingPongRegion) {
+ nsRect rects[] = {
+ nsRect(4, 1, 61, 49),
+ nsRect(115, 1, 99, 49),
+ nsRect(115, 49, 99, 1),
+ nsRect(12, 50, 11, 5),
+ nsRect(25, 50, 28, 5),
+ nsRect(115, 50, 99, 5),
+ nsRect(115, 55, 99, 12),
+ };
+
+ // Test accumulations of various sizes to make sure
+ // the ping-pong behavior of PingPongRegion is working.
+ for (size_t size = 0; size < mozilla::ArrayLength(rects); size++) {
+ // bug 1130978.
+ nsRegion r;
+ PingPongRegion<nsRegion> ar;
+ for (size_t i = 0; i < size; i++) {
+ r.Or(r, rects[i]);
+ ar.OrWith(rects[i]);
+ EXPECT_TRUE(ar.Region().IsEqual(r));
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ ar.SubOut(rects[i]);
+ r.SubOut(rects[i]);
+ EXPECT_TRUE(ar.Region().IsEqual(r));
+ }
+ }
+}
+
+// The TiledRegion tests use nsIntRect / IntRegion because nsRect doesn't have
+// InflateToMultiple which is required by TiledRegion.
+TEST(Gfx, TiledRegionNoSimplification2Rects) {
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionNoSimplification1Region) {
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionWithSimplification3Rects) {
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionWithSimplification1Region) {
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+ region.OrWith(nsIntRect(250, 70, 10, 10));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionContains) {
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 50, 50)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(50, 50, 301, 50)));
+}
+
+TEST(Gfx, TiledRegionIntersects) {
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(200, 10, 10, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 301, 50)));
+ EXPECT_FALSE(tiledRegion.Intersects(nsIntRect(0, 0, 50, 500)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions1) {
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+
+ // This causes the tiledRegion.mBounds to overflow, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+
+ // Verify that the tiledRegion contains only things we expect
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions2) {
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+
+ // As with TiledRegionBoundaryConditions1, this overflows, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBigRects) {
+ TiledIntRegion tiledRegion;
+ // Super wide region, forces simplification into bounds mode
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, INT_MAX, 100)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 99, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, INT_MIN + 99, 1, 1)));
+
+ // Add another rect, verify that simplification caused the entire bounds
+ // to expand by a lot more.
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN + 200, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 200, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 201, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryOverflow) {
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRegion(nsIntRect(100, 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, 1, 1)));
+
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try that again as a rect, it will also get ignored
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 1, 1));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try with a bigger overflowing rect
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 500, 500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 10, 10)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 100, 100)));
+
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionNegativeRect) {
+ TiledIntRegion tiledRegion;
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(0, 0, -500, -500)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-50, -50, 1, 1)));
+ // Rects with negative widths/heights are treated as empty and ignored
+ tiledRegion.Add(nsIntRect(0, 0, -500, -500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, -1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+ // Empty rects are always contained
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(0, 0, -1, -1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, -1, -1)));
+}
+
+MOZ_GTEST_BENCH(GfxBench, RegionOr, []{
+ const int size = 5000;
+
+ nsRegion r;
+ for (int i = 0; i < size; i++) {
+ r = r.Or(r, nsRect(i, i, i + 10, i + 10));
+ }
+
+ nsIntRegion rInt;
+ for (int i = 0; i < size; i++) {
+ rInt = rInt.Or(rInt, nsIntRect(i, i, i + 10, i + 10));
+ }
+});
+
+MOZ_GTEST_BENCH(GfxBench, RegionAnd, []{
+ const int size = 5000;
+ nsRegion r(nsRect(0, 0, size, size));
+ for (int i = 0; i < size; i++) {
+ nsRegion rMissingPixel(nsRect(0, 0, size, size));
+ rMissingPixel = rMissingPixel.Sub(rMissingPixel, nsRect(i, i, 1, 1));
+ r = r.And(r, rMissingPixel);
+ }
+});
+
+void BenchRegionBuilderOr() {
+ const int size = 5000;
+
+ RegionBuilder<nsRegion> r;
+ for (int i = 0; i < size; i++) {
+ r.OrWith(nsRect(i, i, i + 10, i + 10));
+ }
+ r.ToRegion();
+
+ RegionBuilder<nsIntRegion> rInt;
+ for (int i = 0; i < size; i++) {
+ rInt.OrWith(nsIntRect(i, i, i + 10, i + 10));
+ }
+ rInt.ToRegion();
+}
+
+MOZ_GTEST_BENCH(GfxBench, RegionBuilderOr, []{
+ BenchRegionBuilderOr();
+});
+
+void BenchPingPongRegionOr() {
+ const int size = 5000;
+
+ PingPongRegion<nsRegion> r;
+ for (int i = 0; i < size; i++) {
+ r.OrWith(nsRect(i, i, i + 10, i + 10));
+ }
+ r.Region();
+
+ PingPongRegion<nsIntRegion> rInt;
+ for (int i = 0; i < size; i++) {
+ rInt.OrWith(nsIntRect(i, i, i + 10, i + 10));
+ }
+ rInt.Region();
+}
+
+MOZ_GTEST_BENCH(GfxBench, PingPongRegionOr, []{
+ BenchPingPongRegionOr();
+});
+
diff --git a/system/graphics/tests/gtest/TestSkipChars.cpp b/system/graphics/tests/gtest/TestSkipChars.cpp
new file mode 100644
index 000000000..65c96f417
--- /dev/null
+++ b/system/graphics/tests/gtest/TestSkipChars.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "gfxSkipChars.h"
+#include "mozilla/ArrayUtils.h"
+
+static bool
+TestConstructor()
+{
+ gfxSkipChars skipChars;
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 0) <<
+ "[1] Make sure the gfxSkipChars was properly initialized with constructor";
+
+ return true;
+}
+
+static bool
+TestLength()
+{
+ gfxSkipChars skipChars;
+
+ skipChars.KeepChars(100);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 100) <<
+ "[1] Check length after keeping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 150) <<
+ "[2] Check length after skipping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 200) <<
+ "[3] Check length after skipping more chars";
+
+ skipChars.KeepChar();
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 201) <<
+ "[4] Check length after keeping a final char";
+
+ return true;
+}
+
+static bool
+TestIterator()
+{
+ // Test a gfxSkipChars that starts with kept chars
+ gfxSkipChars skipChars1;
+
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+
+ EXPECT_TRUE(skipChars1.GetOriginalCharCount() == 29) <<
+ "[1] Check length";
+
+ gfxSkipCharsIterator iter1(skipChars1);
+
+ EXPECT_TRUE(iter1.GetOriginalOffset() == 0) <<
+ "[2] Check initial original offset";
+ EXPECT_TRUE(iter1.GetSkippedOffset() == 0) <<
+ "[3] Check initial skipped offset";
+
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == false) <<
+ "[3a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped1[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) {
+ EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i]) <<
+ "[4] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal1[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28 };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) {
+ EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i]) <<
+ "[5] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped1[] =
+ { false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false
+ };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped1); i++) {
+ iter1.SetOriginalOffset(i);
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == expectIsOriginalSkipped1[i]) <<
+ "[5.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ // Test a gfxSkipChars that starts with skipped chars
+ gfxSkipChars skipChars2;
+
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+
+ EXPECT_TRUE(skipChars2.GetOriginalCharCount() == 29) <<
+ "[6] Check length";
+
+ gfxSkipCharsIterator iter2(skipChars2);
+
+ EXPECT_TRUE(iter2.GetOriginalOffset() == 0) <<
+ "[7] Check initial original offset";
+ EXPECT_TRUE(iter2.GetSkippedOffset() == 0) <<
+ "[8] Check initial skipped offset";
+
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == true) <<
+ "[8a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped2[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) {
+ EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i]) <<
+ "[9] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal2[] = { 9, 19, 29 };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
+ EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) <<
+ "[10] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped2[] =
+ { true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true
+ };
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped2); i++) {
+ iter2.SetOriginalOffset(i);
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == expectIsOriginalSkipped2[i]) <<
+ "[10.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ return true;
+}
+
+TEST(Gfx, gfxSkipChars) {
+ TestConstructor();
+ TestLength();
+ TestIterator();
+}
diff --git a/system/graphics/tests/gtest/TestTextures.cpp b/system/graphics/tests/gtest/TestTextures.cpp
new file mode 100644
index 000000000..8f413cb3b
--- /dev/null
+++ b/system/graphics/tests/gtest/TestTextures.cpp
@@ -0,0 +1,300 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "TestLayers.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/RefPtr.h"
+#include "gfx2DGlue.h"
+#include "gfxImageSurface.h"
+#include "gfxTypes.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+/*
+ * This test performs the following actions:
+ * - creates a surface
+ * - initialize a texture client with it
+ * - serilaizes the texture client
+ * - deserializes the data into a texture host
+ * - reads the surface from the texture host.
+ *
+ * The surface in the end should be equal to the inital one.
+ * This test is run for different combinations of texture types and
+ * image formats.
+ */
+
+namespace mozilla {
+namespace layers {
+
+// fills the surface with values betwee 0 and 100.
+void SetupSurface(gfxImageSurface* surface) {
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format());
+ int stride = surface->Stride();
+ uint8_t val = 0;
+ uint8_t* data = surface->Data();
+ for (int y = 0; y < surface->Height(); ++y) {
+ for (int x = 0; x < surface->Height(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ data[y*stride + x*bpp + b] = val;
+ if (val == 100) {
+ val = 0;
+ } else {
+ ++val;
+ }
+ }
+ }
+ }
+}
+
+// return true if two surfaces contain the same data
+void AssertSurfacesEqual(gfxImageSurface* surface1,
+ gfxImageSurface* surface2)
+{
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->Format(), surface2->Format());
+
+ uint8_t* data1 = surface1->Data();
+ uint8_t* data2 = surface2->Data();
+ int stride1 = surface1->Stride();
+ int stride2 = surface2->Stride();
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format());
+
+ for (int y = 0; y < surface1->Height(); ++y) {
+ for (int x = 0; x < surface1->Width(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y*stride1 + x*bpp + b],
+ data2[y*stride2 + x*bpp + b]);
+ }
+ }
+ }
+}
+
+void AssertSurfacesEqual(SourceSurface* surface1,
+ SourceSurface* surface2)
+{
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat());
+
+ RefPtr<DataSourceSurface> dataSurface1 = surface1->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurface2 = surface2->GetDataSurface();
+ DataSourceSurface::MappedSurface map1;
+ DataSourceSurface::MappedSurface map2;
+ if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) {
+ return;
+ }
+ if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) {
+ dataSurface1->Unmap();
+ return;
+ }
+ uint8_t* data1 = map1.mData;
+ uint8_t* data2 = map2.mData;
+ int stride1 = map1.mStride;
+ int stride2 = map2.mStride;
+ int bpp = BytesPerPixel(surface1->GetFormat());
+ int width = surface1->GetSize().width;
+ int height = surface1->GetSize().height;
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y*stride1 + x*bpp + b],
+ data2[y*stride2 + x*bpp + b]);
+ }
+ }
+ }
+
+ dataSurface1->Unmap();
+ dataSurface2->Unmap();
+}
+
+// Run the test for a texture client and a surface
+void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) {
+
+ // client allocation
+ ASSERT_TRUE(texture->CanExposeDrawTarget());
+
+ ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
+ // client painting
+ RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
+ RefPtr<SourceSurface> source =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
+ dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
+
+ RefPtr<SourceSurface> snapshot = dt->Snapshot();
+
+ AssertSurfacesEqual(snapshot, source);
+
+ dt = nullptr; // drop reference before calling Unlock()
+ texture->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(descriptor, deallocator,
+ texture->GetFlags());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), texture->GetFlags());
+
+ // host read
+
+ // XXX - this can fail because lock tries to upload the texture but it needs a
+ // Compositor to do that. We could add a DummyComposior for testing but I am
+ // not sure it'll be worth it. Maybe always test against a BasicCompositor,
+ // but the latter needs a widget...
+ if (host->Lock()) {
+ RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface = host->GetAsSurface();
+
+ RefPtr<gfxImageSurface> hostSurface =
+ new gfxImageSurface(hostDataSurface->GetData(),
+ hostDataSurface->GetSize(),
+ hostDataSurface->Stride(),
+ SurfaceFormatToImageFormat(hostDataSurface->GetFormat()));
+ AssertSurfacesEqual(surface, hostSurface.get());
+ host->Unlock();
+ }
+}
+
+// Same as above, for YCbCr surfaces
+void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) {
+ client->Lock(OpenMode::OPEN_READ_WRITE);
+ UpdateYCbCrTextureClient(client, ycbcrData);
+ client->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_EQ(descriptor.type(), SurfaceDescriptor::TSurfaceDescriptorBuffer);
+ auto bufferDesc = descriptor.get_SurfaceDescriptorBuffer();
+ ASSERT_EQ(bufferDesc.desc().type(), BufferDescriptor::TYCbCrDescriptor);
+ auto ycbcrDesc = bufferDesc.desc().get_YCbCrDescriptor();
+ ASSERT_EQ(ycbcrDesc.ySize(), ycbcrData.mYSize);
+ ASSERT_EQ(ycbcrDesc.cbCrSize(), ycbcrData.mCbCrSize);
+ ASSERT_EQ(ycbcrDesc.stereoMode(), ycbcrData.mStereoMode);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(descriptor, deallocator,
+ client->GetFlags());
+
+ RefPtr<BufferTextureHost> host = static_cast<BufferTextureHost*>(textureHost.get());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), client->GetFlags());
+
+ // host read
+
+ if (host->Lock()) {
+ // This will work iff the compositor is not BasicCompositor
+ ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV);
+ host->Unlock();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+TEST(Layers, TextureSerialization) {
+ // the test is run on all the following image formats
+ gfxImageFormat formats[3] = {
+ SurfaceFormat::A8R8G8B8_UINT32,
+ SurfaceFormat::X8R8G8B8_UINT32,
+ SurfaceFormat::A8,
+ };
+
+ for (int f = 0; f < 3; ++f) {
+ RefPtr<gfxImageSurface> surface = new gfxImageSurface(IntSize(400,300), formats[f]);
+ SetupSurface(surface.get());
+ AssertSurfacesEqual(surface, surface);
+
+ auto texData = BufferTextureData::Create(surface->GetSize(),
+ gfx::ImageFormatToSurfaceFormat(surface->Format()),
+ gfx::BackendType::CAIRO, LayersBackend::LAYERS_NONE,
+ TextureFlags::DEALLOCATE_CLIENT, ALLOC_DEFAULT, nullptr
+ );
+ ASSERT_TRUE(!!texData);
+
+ RefPtr<TextureClient> client = new TextureClient(
+ texData, TextureFlags::DEALLOCATE_CLIENT, nullptr
+ );
+
+ TestTextureClientSurface(client, surface);
+
+ // XXX - Test more texture client types.
+ }
+}
+
+TEST(Layers, TextureYCbCrSerialization) {
+ RefPtr<gfxImageSurface> ySurface = new gfxImageSurface(IntSize(400,300), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> cbSurface = new gfxImageSurface(IntSize(200,150), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> crSurface = new gfxImageSurface(IntSize(200,150), SurfaceFormat::A8);
+ SetupSurface(ySurface.get());
+ SetupSurface(cbSurface.get());
+ SetupSurface(crSurface.get());
+
+ PlanarYCbCrData clientData;
+ clientData.mYChannel = ySurface->Data();
+ clientData.mCbChannel = cbSurface->Data();
+ clientData.mCrChannel = crSurface->Data();
+ clientData.mYSize = ySurface->GetSize();
+ clientData.mPicSize = ySurface->GetSize();
+ clientData.mCbCrSize = cbSurface->GetSize();
+ clientData.mYStride = ySurface->Stride();
+ clientData.mCbCrStride = cbSurface->Stride();
+ clientData.mStereoMode = StereoMode::MONO;
+ clientData.mYUVColorSpace = YUVColorSpace::BT601;
+ clientData.mYSkip = 0;
+ clientData.mCbSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mPicX = 0;
+ clientData.mPicX = 0;
+
+ ImageBridgeChild::InitSameProcess();
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ static int retry = 5;
+ while(!imageBridge->IPCOpen() && retry) {
+ // IPDL connection takes time especially in slow testing environment, like
+ // VM machines. Here we added retry mechanism to wait for IPDL connnection.
+#ifdef XP_WIN
+ Sleep(1);
+#else
+ sleep(1);
+#endif
+ retry--;
+ }
+
+ // Skip this testing if IPDL connection is not ready
+ if (!retry && !imageBridge->IPCOpen()) {
+ return;
+ }
+
+ RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mCbCrSize,
+ StereoMode::MONO, YUVColorSpace::BT601,
+ TextureFlags::DEALLOCATE_CLIENT);
+
+ TestTextureClientYCbCr(client, clientData);
+
+ // XXX - Test more texture client types.
+}
diff --git a/system/graphics/tests/gtest/TestTiledLayerBuffer.cpp b/system/graphics/tests/gtest/TestTiledLayerBuffer.cpp
new file mode 100644
index 000000000..c5e9b0f41
--- /dev/null
+++ b/system/graphics/tests/gtest/TestTiledLayerBuffer.cpp
@@ -0,0 +1,64 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "TiledLayerBuffer.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+namespace layers {
+
+TEST(TiledLayerBuffer, TileStart) {
+ ASSERT_EQ(RoundDownToTileEdge(10, 256), 0);
+ ASSERT_EQ(RoundDownToTileEdge(-10, 256), -256);
+}
+
+TEST(TiledLayerBuffer, TilesPlacement) {
+ for (int firstY = -10; firstY < 10; ++firstY) {
+ for (int firstX = -10; firstX < 10; ++firstX) {
+ for (int height = 1; height < 10; ++height) {
+ for (int width = 1; width < 10; ++width) {
+
+ const TilesPlacement p1 = TilesPlacement(firstX, firstY, width, height);
+ // Check that HasTile returns false with some positions that we know
+ // not to be in the rectangle of the TilesPlacement.
+ ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX - 1, 0)));
+ ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY - 1)));
+ ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX + width + 1, 0)));
+ ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY + height + 1)));
+
+ // Verify that all positions within the rect that defines the
+ // TilesPlacement map to indices between 0 and width*height.
+ for (int y = firstY; y < (firstY+height); ++y) {
+ for (int x = firstX; x < (firstX+width); ++x) {
+ ASSERT_TRUE(p1.HasTile(TileIntPoint(x,y)));
+ ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) >= 0);
+ ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) < width * height);
+ }
+ }
+
+ // XXX - This causes some versions of gcc to warn that it optimizes
+ // away the test, which gets caught in -WError in PGO builds.
+ // The lazy thing to do is to just comment this out since this specific
+ // test isn't critically important, but we should remove the warning instead.
+ // cf. bug 1179287
+ //
+ // Verify that indices map to positions that are within the rect that
+ // defines the TilesPlacement.
+ // for (int i = 0; i < width * height; ++i) {
+ // ASSERT_TRUE(p1.TilePosition(i).x >= firstX);
+ // ASSERT_TRUE(p1.TilePosition(i).x < firstX + width);
+ // ASSERT_TRUE(p1.TilePosition(i).y >= firstY);
+ // ASSERT_TRUE(p1.TilePosition(i).y < firstY + height);
+ // }
+
+ }
+ }
+ }
+ }
+}
+
+}
+}
diff --git a/system/graphics/tests/gtest/TestTreeTraversal.cpp b/system/graphics/tests/gtest/TestTreeTraversal.cpp
new file mode 100644
index 000000000..043e28fd5
--- /dev/null
+++ b/system/graphics/tests/gtest/TestTreeTraversal.cpp
@@ -0,0 +1,2225 @@
+#include <vector>
+#include "mozilla/RefPtr.h"
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+#include "nsRegion.h"
+#include "nsRect.h"
+#include "TreeTraversal.h"
+#include <stack>
+#include <queue>
+
+const int PERFORMANCE_TREE_DEPTH = 20;
+const int PERFORMANCE_TREE_CHILD_COUNT = 2;
+const int PERFORMANCE_TREE_LEAF_COUNT = 1048576; // 2 ** 20
+const int PERFORMANCE_REGION_XWRAP = 1024;
+
+using namespace mozilla::layers;
+using namespace mozilla;
+
+enum class SearchNodeType {Needle, Hay};
+enum class ForEachNodeType {Continue, Skip};
+
+template <class T>
+class TestNodeBase {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(TestNodeBase<T>);
+ explicit TestNodeBase(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeBase();
+ void SetActualTraversalRank(int aRank);
+ void SetValue(int aValue);
+ void SetType(T aType);
+ void SetRegion(nsRegion aRegion);
+ int GetExpectedTraversalRank();
+ int GetActualTraversalRank();
+ int GetValue();
+ T GetType();
+ nsRegion GetRegion();
+ virtual bool IsLeaf() = 0;
+ private:
+ MOZ_INIT_OUTSIDE_CTOR int mExpectedTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mActualTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mValue;
+ MOZ_INIT_OUTSIDE_CTOR nsRegion mRegion;
+ MOZ_INIT_OUTSIDE_CTOR T mType;
+ protected:
+ virtual ~TestNodeBase<T>() {};
+};
+
+template <class T>
+class TestNodeReverse : public TestNodeBase<T> {
+ public:
+ explicit TestNodeReverse(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeReverse();
+ void AddChild(RefPtr<TestNodeReverse<T>> aNode);
+ TestNodeReverse<T>* GetLastChild();
+ TestNodeReverse<T>* GetPrevSibling();
+ bool IsLeaf();
+ private:
+ void SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeReverse<T>> aNode);
+ RefPtr<TestNodeReverse<T>> mSiblingNode;
+ RefPtr<TestNodeReverse<T>> mLastChildNode;
+ ~TestNodeReverse<T>() {};
+};
+
+template <class T>
+class TestNodeForward : public TestNodeBase<T> {
+ public:
+ explicit TestNodeForward(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeForward();
+ void AddChild(RefPtr<TestNodeForward<T>> aNode);
+ TestNodeForward<T>* GetFirstChild();
+ TestNodeForward<T>* GetNextSibling();
+ bool IsLeaf();
+ private:
+ void SetNextSibling(RefPtr<TestNodeForward<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeForward<T>> aNode);
+ void SetFirstChild(RefPtr<TestNodeForward<T>> aNode);
+ RefPtr<TestNodeForward<T>> mSiblingNode = nullptr;
+ RefPtr<TestNodeForward<T>> mFirstChildNode = nullptr;
+ // Track last child to facilitate appending children
+ RefPtr<TestNodeForward<T>> mLastChildNode = nullptr;
+ ~TestNodeForward<T>() {};
+};
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse(T aType, int aExpectedTraversalRank) :
+ TestNodeBase<T>(aType, aExpectedTraversalRank)
+{
+
+}
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse() :
+ TestNodeBase<T>()
+{
+
+}
+
+template <class T>
+void TestNodeReverse<T>::SetLastChild(RefPtr<TestNodeReverse<T>> aNode)
+{
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeReverse<T>::AddChild(RefPtr<TestNodeReverse<T>> aNode)
+{
+ aNode->SetPrevSibling(mLastChildNode);
+ SetLastChild(aNode);
+}
+
+template <class T>
+void TestNodeReverse<T>::SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode)
+{
+ mSiblingNode = aNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetLastChild()
+{
+ return mLastChildNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetPrevSibling()
+{
+ return mSiblingNode;
+}
+
+template <class T>
+bool TestNodeReverse<T>::IsLeaf()
+{
+ return !mLastChildNode;
+}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward(T aType, int aExpectedTraversalRank) :
+ TestNodeBase<T>(aType, aExpectedTraversalRank)
+{
+
+}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward() :
+ TestNodeBase<T>()
+{
+
+}
+
+template <class T>
+void TestNodeForward<T>::AddChild(RefPtr<TestNodeForward<T>> aNode)
+{
+ if (mFirstChildNode == nullptr) {
+ SetFirstChild(aNode);
+ SetLastChild(aNode);
+ }
+ else {
+ mLastChildNode->SetNextSibling(aNode);
+ SetLastChild(aNode);
+ }
+}
+
+template <class T>
+void TestNodeForward<T>::SetLastChild(RefPtr<TestNodeForward<T>> aNode)
+{
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetFirstChild(RefPtr<TestNodeForward<T>> aNode)
+{
+ mFirstChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetNextSibling(RefPtr<TestNodeForward<T>> aNode)
+{
+ mSiblingNode = aNode;
+}
+
+template <class T>
+bool TestNodeForward<T>::IsLeaf()
+{
+ return !mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetFirstChild()
+{
+ return mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetNextSibling()
+{
+ return mSiblingNode;
+}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase(T aType, int aExpectedTraversalRank):
+ mExpectedTraversalRank(aExpectedTraversalRank),
+ mActualTraversalRank(-1),
+ mType(aType)
+{
+}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase()
+{
+}
+
+template <class T>
+int TestNodeBase<T>::GetActualTraversalRank()
+{
+ return mActualTraversalRank;
+}
+
+template <class T>
+void TestNodeBase<T>::SetActualTraversalRank(int aRank)
+{
+ mActualTraversalRank = aRank;
+}
+
+template <class T>
+int TestNodeBase<T>::GetExpectedTraversalRank()
+{
+ return mExpectedTraversalRank;
+}
+
+template <class T>
+T TestNodeBase<T>::GetType()
+{
+ return mType;
+}
+
+template <class T>
+void TestNodeBase<T>::SetType(T aType)
+{
+ mType = aType;
+}
+
+template <class T>
+nsRegion TestNodeBase<T>::GetRegion()
+{
+ return mRegion;
+}
+
+template <class T>
+void TestNodeBase<T>::SetRegion(nsRegion aRegion)
+{
+ mRegion = aRegion;
+}
+
+template <class T>
+int TestNodeBase<T>::GetValue()
+{
+ return mValue;
+}
+
+template <class T>
+void TestNodeBase<T>::SetValue(int aValue)
+{
+ mValue = aValue;
+}
+
+typedef TestNodeBase<SearchNodeType> SearchTestNode;
+typedef TestNodeBase<ForEachNodeType> ForEachTestNode;
+typedef TestNodeReverse<SearchNodeType> SearchTestNodeReverse;
+typedef TestNodeReverse<ForEachNodeType> ForEachTestNodeReverse;
+typedef TestNodeForward<SearchNodeType> SearchTestNodeForward;
+typedef TestNodeForward<ForEachNodeType> ForEachTestNodeForward;
+
+template <typename Node, typename Action>
+void CreateBenchmarkTreeRecursive(RefPtr<Node> aNode, int aDepth, int aChildrenCount, Action aAction)
+{
+ if (aDepth > 0) {
+ for (int i = 0; i < aChildrenCount; i++) {
+ RefPtr<Node> newNode = new Node();
+ aNode->AddChild(newNode);
+ CreateBenchmarkTreeRecursive(newNode, aDepth-1, aChildrenCount, aAction);
+ }
+ }
+ aAction(aNode);
+}
+
+template <typename Node, typename Action>
+RefPtr<Node> CreateBenchmarkTree(int aDepth, int aChildrenCount, Action aAction)
+{
+ RefPtr<Node> rootNode = new Node();
+ CreateBenchmarkTreeRecursive(rootNode, aDepth, aChildrenCount, aAction);
+ return rootNode;
+}
+
+TEST(TreeTraversal, DepthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result = DepthFirstSearch<layers::ReverseIterator>(nullNode.get(),
+ [](SearchTestNodeReverse* aNode)
+ {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearch<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root = new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1= new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result = DepthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearch<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result = DepthFirstSearchPostOrder<layers::ReverseIterator>(nullNode.get(),
+ [](SearchTestNodeReverse* aNode)
+ {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearchPostOrder<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearchPostOrder<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root = new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1= new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result = DepthFirstSearchPostOrder<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearchPostOrder<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearchPostOrder<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result = BreadthFirstSearch<layers::ReverseIterator>(nullNode.get(),
+ [](SearchTestNodeReverse* aNode)
+ {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root = new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1= new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result = BreadthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode = BreadthFirstSearch<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (size_t i = 0; i < 10; i++)
+ {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode = BreadthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+
+ RefPtr<SearchTestNodeForward> foundNode = BreadthFirstSearch<layers::ForwardIterator>(root.get(),
+ [&visitCount](SearchTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+
+ RefPtr<SearchTestNodeReverse> foundNode = BreadthFirstSearch<layers::ReverseIterator>(root.get(),
+ [&visitCount](SearchTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, ForEachNodeNullStillRuns)
+{
+ RefPtr<ForEachTestNodeReverse> nullNode;
+ ForEachNode<layers::ReverseIterator>(nullNode.get(),
+ [](ForEachTestNodeReverse* aNode)
+ {
+ return TraversalFlag::Continue;
+ });
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligible)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue,i));
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+
+ ForEachNode<layers::ForwardIterator>(root.get(),
+ [&visitCount](ForEachTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligibleReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue,i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&visitCount](ForEachTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodes)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeForward> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+
+ ForEachNode<layers::ForwardIterator>(root.get(),
+ [&visitCount](ForEachTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++)
+ {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++)
+ {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodesReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeReverse> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&visitCount](ForEachTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++)
+ {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++)
+ {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeIneligibleRoot)
+{
+ int visitCount = 0;
+
+ RefPtr<ForEachTestNodeReverse> root = new ForEachTestNodeReverse(ForEachNodeType::Skip, 0);
+ RefPtr<ForEachTestNodeReverse> childNode1 = new ForEachTestNodeReverse(ForEachNodeType::Continue);
+ RefPtr<ForEachTestNodeReverse> chlidNode2 = new ForEachTestNodeReverse(ForEachNodeType::Skip);
+
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&visitCount](ForEachTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Root was hit out of order.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank())
+ << "Eligible child was still hit.";
+ ASSERT_EQ(chlidNode2->GetExpectedTraversalRank(), chlidNode2->GetActualTraversalRank())
+ << "Ineligible child was still hit.";
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligible)
+{
+
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ ForEachNode<layers::ForwardIterator>(root.get(),
+ [&visitCount](ForEachTestNodeForward* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligibleReverse)
+{
+
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&visitCount](ForEachTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLambdaReturnsVoid)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Continue,i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&visitCount](ForEachTestNodeReverse* aNode)
+ {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++)
+ {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+struct AssignSearchNodeTypesWithLastLeafAsNeedle {
+ RefPtr<SearchTestNodeForward>& node;
+ void operator()(SearchTestNodeForward* aNode) {
+ aNode->SetType(SearchNodeType::Hay);
+ if (aNode->IsLeaf()) {
+ node = aNode;
+ }
+ }
+};
+
+bool FindNeedle(SearchTestNode* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+}
+
+struct AssignSearchNodeTypesAllHay
+{
+ void operator()(SearchTestNode* aNode){
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeTypesWithFirstLeafAsNeedle
+{
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode){
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesReverse
+{
+ int falseValue;
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode){
+ aNode->SetValue(falseValue);
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesForward
+{
+ int falseValue;
+ RefPtr<SearchTestNodeForward>& needleNode;
+ void operator()(SearchTestNodeForward* aNode){
+ aNode->SetValue(falseValue);
+ needleNode = aNode;
+ }
+};
+
+struct AllocateUnitRegionsToLeavesOnly
+{
+ int& xWrap;
+ int& squareCount;
+ void operator()(ForEachTestNode* aNode) {
+ if (aNode->IsLeaf()) {
+ int x = squareCount % xWrap;
+ int y = squareCount / xWrap;
+ aNode->SetRegion(nsRegion(nsRect(x, y, 1, 1)));
+ squareCount++;
+ }
+ }
+};
+
+void ForEachNodeDoNothing(ForEachTestNode* aNode) {}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchForwardRecursive(RefPtr<Node> aNode)
+{
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ForwardDepthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchForwardRecursive<SearchTestNodeForward>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchPerformance, &Plain_ForwardDepthFirstSearchPerformance);
+
+static void TreeTraversal_ForwardDepthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearch<layers::ForwardIterator>(root.get(), &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchPerformance, &TreeTraversal_ForwardDepthFirstSearchPerformance);
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesForwardRecursive(RefPtr<Node> aNode,
+ int a, int b, int c, int d, int e, int f,
+ int g, int h, int i, int j, int k, int l,
+ int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x,
+ int& y, int& z)
+{
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchCaptureVariablesForwardRecursive(node,
+ a, b, c, d, e, f, g, h, i, j, k, l, m,
+ n, o, p, q, r, s, t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ForwardDepthFirstSearchCaptureVariablesPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1;
+ int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1;
+ int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1;
+ int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1;
+ int y = 1; int z = 1;
+ int needleTotal = a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z;
+ int hayTotal = 0;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeValuesAllFalseValuesForward{hayTotal, needleNode});
+ needleNode->SetValue(needleTotal);
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchCaptureVariablesForwardRecursive<SearchTestNodeForward>(root.get(),
+ a, b, c, d, e, f, g, h, i, j, k, l, m,
+ n, o, p, q, r, s, t, u, v, w, x, y, z);
+ ASSERT_EQ(foundNode->GetValue(), needleTotal);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchCaptureVariablesPerformance, &Plain_ForwardDepthFirstSearchCaptureVariablesPerformance);
+
+static void TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1;
+ int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1;
+ int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1;
+ int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1;
+ int y = 1; int z = 1;
+ int needleTotal = a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z;
+ int hayTotal = 0;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeValuesAllFalseValuesForward{hayTotal, needleNode});
+ needleNode->SetValue(needleTotal);
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearch<layers::ForwardIterator>(root.get(),
+ [a, b, c, d, e, f, g, h, i, j, k, l, m,
+ &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z]
+ (SearchTestNodeForward* aNode) {
+ return aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z;
+ });
+ ASSERT_EQ(foundNode->GetValue(), needleTotal);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance, &TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance);
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderForwardRecursive(RefPtr<Node> aNode)
+{
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchPostOrderForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+static void Plain_ForwardDepthFirstSearchPostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesAllHay{});
+ root->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchPostOrderForwardRecursive<SearchTestNodeForward>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(root, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchPostOrderPerformance, &Plain_ForwardDepthFirstSearchPostOrderPerformance);
+
+static void TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesAllHay{});
+ root->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode = DepthFirstSearchPostOrder<layers::ForwardIterator>(root.get(), &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(root, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance, &TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance);
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchForwardQueue(RefPtr<Node> aNode)
+{
+ queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while(!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetFirstChild();
+ childNode != nullptr;
+ childNode = childNode->GetNextSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ForwardBreadthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode =
+ BreadthFirstSearchForwardQueue<SearchTestNodeForward>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardBreadthFirstSearchPerformance, &Plain_ForwardBreadthFirstSearchPerformance);
+
+static void TreeTraversal_ForwardBreadthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeForward> needleNode;
+ RefPtr<SearchTestNodeForward> root = CreateBenchmarkTree<SearchTestNodeForward>(depth, childrenCount,
+ AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeForward> foundNode = BreadthFirstSearch<layers::ForwardIterator>(root.get(), &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardBreadthFirstSearchPerformance, &TreeTraversal_ForwardBreadthFirstSearchPerformance);
+
+// This test ((Plain|TreeTraversal)_ForwardForEachNodePostOrderPerformance)
+// uses the following benchmark:
+//
+// Starting with a tree whose leaves only are augmented with region data
+// (arranged as a series of 1x1 blocks stacked in rows of 100000), calculate
+// each ancestor's region as the union of its child regions.
+template <typename Node>
+static void ForEachNodePostOrderForwardRecursive(RefPtr<Node> aNode)
+{
+ if (!aNode->IsLeaf()) {
+ nsRegion newRegion;
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ ForEachNodePostOrderForwardRecursive(node);
+ nsRegion childRegion = node->GetRegion();
+ newRegion.OrWith(childRegion);
+ }
+ aNode->SetRegion(newRegion);
+ }
+}
+
+static void Plain_ForwardForEachNodePostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int squareCount = 0;
+ int xWrap = PERFORMANCE_REGION_XWRAP;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ AllocateUnitRegionsToLeavesOnly{xWrap, squareCount});
+ ForEachNodePostOrderForwardRecursive(root);
+ ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP)));
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodePostOrderPerformance, &Plain_ForwardForEachNodePostOrderPerformance);
+
+static void TreeTraversal_ForwardForEachNodePostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int squareCount = 0;
+ int xWrap = PERFORMANCE_REGION_XWRAP;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ AllocateUnitRegionsToLeavesOnly{xWrap, squareCount});
+ ForEachNodePostOrder<layers::ForwardIterator>(root.get(),
+ [](ForEachTestNodeForward* aNode) {
+ if (!aNode->IsLeaf()) {
+ nsRegion newRegion;
+ for (RefPtr<ForEachTestNodeForward> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ nsRegion childRegion = node->GetRegion();
+ newRegion.OrWith(childRegion);
+ }
+ aNode->SetRegion(newRegion);
+ }
+ });
+ ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP)));
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodePostOrderPerformance, &TreeTraversal_ForwardForEachNodePostOrderPerformance);
+
+// This test ((Plain|TreeTraversal)_ForwardForEachNodePerformance) uses the
+// following benchmark:
+//
+// Starting with a tree whose root has a rectangular region of size
+// PERFORMANCE_TREE_LEAF_COUNT x 1, for each node, split the region into
+// PERFORMANCE_TREE_CHILD_COUNT separate regions of equal width and assign to
+// each child left-to-right. In the end, every node's region should equal the
+// sum of its childrens' regions, and each level of depth's regions should sum
+// to the root's region.
+template <typename Node>
+static void ForEachNodeForwardRecursive(RefPtr<Node> aNode)
+{
+ if (!aNode->IsLeaf()) {
+ int nChildren = 0;
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ nChildren++;
+ }
+ nsRect bounds = aNode->GetRegion().GetBounds();
+ int childWidth = bounds.width / nChildren;
+ int x = bounds.x;
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1)));
+ ForEachNodeForwardRecursive(node);
+ x += childWidth;
+ }
+ }
+}
+
+static void Plain_ForwardForEachNodePerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNodeForwardRecursive(root);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodePerformance, &Plain_ForwardForEachNodePerformance);
+
+static void TreeTraversal_ForwardForEachNodePerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNode<layers::ForwardIterator>(root.get(),
+ [](ForEachTestNodeForward* aNode) {
+ if (!aNode->IsLeaf()) {
+ int nChildren = 0;
+ for (RefPtr<ForEachTestNodeForward> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ nChildren++;
+ }
+ nsRect bounds = aNode->GetRegion().GetBounds();
+ int childWidth = bounds.width / nChildren;
+ int x = bounds.x;
+ for (RefPtr<ForEachTestNodeForward> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1)));
+ x += childWidth;
+ }
+ }
+ });
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodePerformance, &TreeTraversal_ForwardForEachNodePerformance);
+
+// This test ((Plain|TreeTraversal)_ForwardForEachNodeStackPerformance) uses
+// the following benchmark:
+//
+// Starting with an unattached region equal to PERFORMANCE_TREE_LEAF_COUNT x 1,
+// a starting width of PERFORMANCE_TREE_LEAF_COUNT, and an empty tree, create a
+// tree with the same conditions as
+// ((Plain|TreeTraversal)_ForwardForEachNodePerformance) by assigning regions
+// of the current width, starting from the min x and min y coordinates. For
+// each level of depth, decrease the current width by a factor of
+// PERFORMANCE_TREE_CHILD_COUNT, and maintain a stack of ancestor regions.
+// Use the stack to track the portion of each region still available to assign
+// to children, which determines the aforementioned min x and min y coordinates.
+// Compare this to using the program stack.
+template <typename Node>
+static void ForEachNodeForwardStackRecursive(RefPtr<Node> aNode, int& aRectangleWidth, nsRegion aRegion, int aChildrenCount)
+{
+ nsRect parentRect = aRegion.GetBounds();
+ nsRect newRectangle(parentRect.x, parentRect.y, aRectangleWidth, 1);
+ nsRegion newRegion(newRectangle);
+ aNode->SetRegion(nsRegion(newRegion));
+
+ aRectangleWidth /= aChildrenCount;
+
+ for (RefPtr<Node> node = aNode->GetFirstChild();
+ node != nullptr;
+ node = node->GetNextSibling()) {
+ ForEachNodeForwardStackRecursive(node, aRectangleWidth, newRegion, aChildrenCount);
+ newRegion.SubOut(node->GetRegion());
+ }
+
+ // Handle case where rectangle width is truncated if power falls below 0,
+ // so we dont lose the regions in future iterations
+ if (aRectangleWidth == 0) {
+ aRectangleWidth = 1;
+ }
+ else {
+ aRectangleWidth *= aChildrenCount;
+ }
+}
+
+static void Plain_ForwardForEachNodeStackPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ nsRegion startRegion(nsRect(0, 0, rectangleWidth, 1));
+ ForEachNodeForwardStackRecursive(root, rectangleWidth, startRegion, childrenCount);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodeStackPerformance, &Plain_ForwardForEachNodeStackPerformance);
+
+static void TreeTraversal_ForwardForEachNodeStackPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ stack<nsRegion> regionStack;
+ RefPtr<ForEachTestNodeForward> root = CreateBenchmarkTree<ForEachTestNodeForward>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ regionStack.push(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNode<layers::ForwardIterator>(root.get(),
+ [&regionStack, &rectangleWidth, childrenCount](ForEachTestNodeForward* aNode) {
+ nsRegion parentRegion = regionStack.top();
+ nsRect parentRect = parentRegion.GetBounds();
+ nsRect newRect(parentRect.x, parentRect.y, rectangleWidth, 1);
+ nsRegion newRegion(newRect);
+ aNode->SetRegion(newRegion);
+ regionStack.top().SubOut(newRegion);
+ regionStack.push(newRegion);
+ rectangleWidth /= childrenCount;
+ },
+ [&regionStack, &rectangleWidth, childrenCount](ForEachTestNodeForward* aNode) {
+ regionStack.pop();
+ // Handle case where rectangle width is truncated if power falls below 0,
+ // so we dont lose the regions in future iterations
+ if (rectangleWidth == 0) {
+ rectangleWidth = 1;
+ }
+ else {
+ rectangleWidth *= childrenCount;
+ }
+ });
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodeStackPerformance, &TreeTraversal_ForwardForEachNodeStackPerformance);
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchReverseRecursive(RefPtr<Node> aNode)
+{
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ReverseDepthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchReverseRecursive<SearchTestNodeReverse>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchPerformance, &Plain_ReverseDepthFirstSearchPerformance);
+
+static void TreeTraversal_ReverseDepthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearch<layers::ReverseIterator>(root.get(),
+ &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchPerformance, &TreeTraversal_ReverseDepthFirstSearchPerformance);
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesReverseRecursive(RefPtr<Node> aNode,
+ int a, int b, int c, int d, int e, int f,
+ int g, int h, int i, int j, int k, int l,
+ int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x,
+ int& y, int& z)
+{
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l +
+ m + n + o + p + q + r + s + t + u + v + w + x + y + z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchCaptureVariablesReverseRecursive(node,
+ a, b, c, d, e, f, g, h, i, j, k, l, m,
+ n, o, p, q, r, s, t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ReverseDepthFirstSearchCaptureVariablesPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1;
+ int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1;
+ int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1;
+ int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1;
+ int y = 1; int z = 1;
+ int needleTotal = a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z;
+ int hayTotal = 0;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeValuesAllFalseValuesReverse{hayTotal, needleNode});
+ needleNode->SetValue(needleTotal);
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchCaptureVariablesReverseRecursive<SearchTestNodeReverse>(root.get(),
+ a, b, c, d, e, f, g, h, i, j, k, l, m,
+ n, o, p, q, r, s, t, u, v, w, x, y, z);
+ ASSERT_EQ(foundNode->GetValue(), needleTotal);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchCaptureVariablesPerformance, &Plain_ReverseDepthFirstSearchCaptureVariablesPerformance);
+
+static void TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1;
+ int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1;
+ int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1;
+ int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1;
+ int y = 1; int z = 1;
+ int needleTotal = a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y + z;
+ int hayTotal = 0;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeValuesAllFalseValuesReverse{hayTotal, needleNode});
+ needleNode->SetValue(needleTotal);
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearch<layers::ReverseIterator>(root.get(),
+ [a, b, c, d, e, f, g, h, i, j, k, l, m,
+ &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z] (SearchTestNodeReverse* aNode) {
+ return aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l +
+ m + n + o + p + q + r + s + t + u + v + w + x + y + z;
+ });
+ ASSERT_EQ(foundNode->GetValue(), needleTotal);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance, &TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance);
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderReverseRecursive(RefPtr<Node> aNode)
+{
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchPostOrderReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+static void Plain_ReverseDepthFirstSearchPostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesAllHay{});
+ root->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchPostOrderReverseRecursive<SearchTestNodeReverse>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(root, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchPostOrderPerformance, &Plain_ReverseDepthFirstSearchPostOrderPerformance);
+
+static void TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesAllHay{});
+ root->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode = DepthFirstSearchPostOrder<layers::ReverseIterator>(root.get(), &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(root, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance, &TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance);
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchReverseQueue(RefPtr<Node> aNode)
+{
+ queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while(!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetLastChild();
+ childNode != nullptr;
+ childNode = childNode->GetPrevSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
+
+static void Plain_ReverseBreadthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode =
+ BreadthFirstSearchReverseQueue<SearchTestNodeReverse>(root.get());
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseBreadthFirstSearchPerformance, &Plain_ReverseBreadthFirstSearchPerformance);
+
+static void TreeTraversal_ReverseBreadthFirstSearchPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ RefPtr<SearchTestNodeReverse> root = CreateBenchmarkTree<SearchTestNodeReverse>(depth, childrenCount,
+ AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode});
+ needleNode->SetType(SearchNodeType::Needle);
+ RefPtr<SearchTestNodeReverse> foundNode = BreadthFirstSearch<layers::ReverseIterator>(root.get(), &FindNeedle);
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle);
+ ASSERT_EQ(needleNode, foundNode);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseBreadthFirstSearchPerformance, &TreeTraversal_ReverseBreadthFirstSearchPerformance);
+
+// This test ((Plain|TreeTraversal)_ReverseForEachNodePostOrderPerformance)
+// uses the following benchmark:
+//
+// Starting with a tree whose leaves only are augmented with region data
+// (arranged as a series of 1x1 blocks stacked in rows of 100000), calculate
+// each ancestor's region as the union of its child regions.
+template <typename Node>
+static void ForEachNodePostOrderReverseRecursive(RefPtr<Node> aNode)
+{
+ if (!aNode->IsLeaf()) {
+ nsRegion newRegion;
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ ForEachNodePostOrderReverseRecursive(node);
+ nsRegion childRegion = node->GetRegion();
+ newRegion.OrWith(childRegion);
+ }
+ aNode->SetRegion(newRegion);
+ }
+}
+
+static void Plain_ReverseForEachNodePostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int squareCount = 0;
+ int xWrap = PERFORMANCE_REGION_XWRAP;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ AllocateUnitRegionsToLeavesOnly{xWrap, squareCount});
+ ForEachNodePostOrderReverseRecursive(root);
+ ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP)));
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodePostOrderPerformance, &Plain_ReverseForEachNodePostOrderPerformance);
+
+static void TreeTraversal_ReverseForEachNodePostOrderPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int squareCount = 0;
+ int xWrap = PERFORMANCE_REGION_XWRAP;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ AllocateUnitRegionsToLeavesOnly{xWrap, squareCount});
+ ForEachNodePostOrder<layers::ReverseIterator>(root.get(),
+ [](ForEachTestNodeReverse* aNode) {
+ if (!aNode->IsLeaf()) {
+ nsRegion newRegion;
+ for (RefPtr<ForEachTestNodeReverse> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ nsRegion childRegion = node->GetRegion();
+ newRegion.OrWith(childRegion);
+ }
+ aNode->SetRegion(newRegion);
+ }
+ });
+ ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP)));
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodePostOrderPerformance, &TreeTraversal_ReverseForEachNodePostOrderPerformance);
+
+// This test ((Plain|TreeTraversal)_ReverseForEachNodePerformance) uses the
+// following benchmark:
+//
+// Starting with a tree whose root has a rectangular region of size
+// PERFORMANCE_TREE_LEAF_COUNT x 1, for each node, split the region into
+// PERFORMANCE_TREE_CHILD_COUNT separate regions of equal width and assign to
+// each child left-to-right. In the end, every node's region should equal the
+// sum of its childrens' regions, and each level of depth's regions should sum
+// to the root's region.
+template <typename Node>
+static void ForEachNodeReverseRecursive(RefPtr<Node> aNode)
+{
+ if (!aNode->IsLeaf()) {
+ int nChildren = 0;
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ nChildren++;
+ }
+ nsRect bounds = aNode->GetRegion().GetBounds();
+ int childWidth = bounds.width / nChildren;
+ int x = bounds.x;
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1)));
+ ForEachNodeReverseRecursive(node);
+ x += childWidth;
+ }
+ }
+}
+
+static void Plain_ReverseForEachNodePerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNodeReverseRecursive(root);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodePerformance, &Plain_ReverseForEachNodePerformance);
+
+static void TreeTraversal_ReverseForEachNodePerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [](ForEachTestNodeReverse* aNode) {
+ if (!aNode->IsLeaf()) {
+ int nChildren = 0;
+ for (RefPtr<ForEachTestNodeReverse> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ nChildren++;
+ }
+ nsRect bounds = aNode->GetRegion().GetBounds();
+ int childWidth = bounds.width / nChildren;
+ int x = bounds.x;
+ for (RefPtr<ForEachTestNodeReverse> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1)));
+ x += childWidth;
+ }
+ }
+ });
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodePerformance, &TreeTraversal_ReverseForEachNodePerformance);
+
+// This test ((Plain|TreeTraversal)_ReverseForEachNodeStackPerformance) uses
+// the following benchmark:
+//
+// Starting with an unattached region equal to PERFORMANCE_TREE_LEAF_COUNT x 1,
+// a starting width of PERFORMANCE_TREE_LEAF_COUNT, and an empty tree, create a
+// tree with the same conditions as
+// ((Plain|TreeTraversal)_ReverseForEachNodePerformance) by assigning regions
+// of the current width, starting from the min x and min y coordinates. For
+// each level of depth, decrease the current width by a factor of
+// PERFORMANCE_TREE_CHILD_COUNT, and maintain a stack of ancestor regions.
+// Use the stack to track the portion of each region still available to assign
+// to children, which determines the aforementioned min x and min y coordinates.
+// Compare this to using the program stack.
+template <typename Node>
+static void ForEachNodeReverseStackRecursive(RefPtr<Node> aNode, int& aRectangleWidth, nsRegion aRegion, int aChildrenCount)
+{
+ nsRect parentRect = aRegion.GetBounds();
+ nsRect newRectangle(parentRect.x, parentRect.y, aRectangleWidth, 1);
+ nsRegion newRegion(newRectangle);
+ aNode->SetRegion(nsRegion(newRegion));
+
+ aRectangleWidth /= aChildrenCount;
+
+ for (RefPtr<Node> node = aNode->GetLastChild();
+ node != nullptr;
+ node = node->GetPrevSibling()) {
+ ForEachNodeReverseStackRecursive(node, aRectangleWidth, newRegion, aChildrenCount);
+ newRegion.SubOut(node->GetRegion());
+ }
+ // Handle case where rectangle width is truncated if power falls below 0,
+ // so we dont lose the regions in future iterations
+ if (aRectangleWidth == 0) {
+ aRectangleWidth = 1;
+ }
+ else {
+ aRectangleWidth *= aChildrenCount;
+ }
+}
+
+static void Plain_ReverseForEachNodeStackPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ nsRegion startRegion(nsRect(0, 0, rectangleWidth, 1));
+ ForEachNodeReverseStackRecursive(root, rectangleWidth, startRegion, childrenCount);
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodeStackPerformance, &Plain_ReverseForEachNodeStackPerformance);
+
+static void TreeTraversal_ReverseForEachNodeStackPerformance()
+{
+ int depth = PERFORMANCE_TREE_DEPTH;
+ int childrenCount = PERFORMANCE_TREE_CHILD_COUNT;
+ int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT;
+ stack<nsRegion> regionStack;
+ RefPtr<ForEachTestNodeReverse> root = CreateBenchmarkTree<ForEachTestNodeReverse>(depth, childrenCount,
+ &ForEachNodeDoNothing);
+ regionStack.push(nsRegion(nsRect(0, 0, rectangleWidth, 1)));
+ ForEachNode<layers::ReverseIterator>(root.get(),
+ [&regionStack, &rectangleWidth, childrenCount](ForEachTestNodeReverse* aNode) {
+ nsRegion parentRegion = regionStack.top();
+ nsRect parentRect = parentRegion.GetBounds();
+ nsRect newRect(parentRect.x, parentRect.y, rectangleWidth, 1);
+ nsRegion newRegion(newRect);
+ aNode->SetRegion(newRegion);
+ regionStack.top().SubOut(newRegion);
+ regionStack.push(newRegion);
+ rectangleWidth /= childrenCount;
+ },
+ [&regionStack, &rectangleWidth, childrenCount](ForEachTestNodeReverse* aNode) {
+ regionStack.pop();
+ // Handle case where rectangle width is truncated if power falls below 0,
+ // so we dont lose the regions in future iterations
+ if (rectangleWidth == 0) {
+ rectangleWidth = 1;
+ }
+ else {
+ rectangleWidth *= childrenCount;
+ }
+ });
+}
+
+MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodeStackPerformance, &TreeTraversal_ReverseForEachNodeStackPerformance);
diff --git a/system/graphics/tests/gtest/TestVsync.cpp b/system/graphics/tests/gtest/TestVsync.cpp
new file mode 100644
index 000000000..c9cd97ae7
--- /dev/null
+++ b/system/graphics/tests/gtest/TestVsync.cpp
@@ -0,0 +1,205 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "MainThreadUtils.h"
+#include "nsIThread.h"
+#include "mozilla/RefPtr.h"
+#include "SoftwareVsyncSource.h"
+#include "VsyncSource.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/VsyncDispatcher.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+
+// Timeout for vsync events to occur in milliseconds
+// Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
+const int kVsyncTimeoutMS = 80;
+
+class TestVsyncObserver : public VsyncObserver {
+public:
+ TestVsyncObserver()
+ : mDidGetVsyncNotification(false)
+ , mVsyncMonitor("VsyncMonitor")
+ {
+ }
+
+ virtual bool NotifyVsync(TimeStamp aVsyncTimeStamp) override {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = true;
+ mVsyncMonitor.Notify();
+ return true;
+ }
+
+ void WaitForVsyncNotification()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (DidGetVsyncNotification()) {
+ return;
+ }
+
+ { // scope lock
+ MonitorAutoLock lock(mVsyncMonitor);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(kVsyncTimeoutMS);
+ lock.Wait(timeout);
+ }
+ }
+
+ bool DidGetVsyncNotification()
+ {
+ MonitorAutoLock lock(mVsyncMonitor);
+ return mDidGetVsyncNotification;
+ }
+
+ void ResetVsyncNotification()
+ {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = false;
+ }
+
+private:
+ bool mDidGetVsyncNotification;
+
+private:
+ Monitor mVsyncMonitor;
+};
+
+class VsyncTester : public ::testing::Test {
+protected:
+ explicit VsyncTester()
+ {
+ gfxPlatform::GetPlatform();
+ gfxPrefs::GetSingleton();
+ mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
+ MOZ_RELEASE_ASSERT(mVsyncSource, "GFX: Vsync source not found.");
+ }
+
+ virtual ~VsyncTester()
+ {
+ mVsyncSource = nullptr;
+ }
+
+ RefPtr<VsyncSource> mVsyncSource;
+};
+
+static void
+FlushMainThreadLoop()
+{
+ // Some tasks are pushed onto the main thread when adding vsync observers
+ // This function will ensure all tasks are executed on the main thread
+ // before returning.
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = NS_OK;
+ bool processed = true;
+ while (processed && NS_SUCCEEDED(rv)) {
+ rv = mainThread->ProcessNextEvent(false, &processed);
+ }
+}
+
+// Tests that we can enable/disable vsync notifications
+TEST_F(VsyncTester, EnableVsync)
+{
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.EnableVsync();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the display should get vsync notifications
+TEST_F(VsyncTester, CompositorGetVsyncNotifications)
+{
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<CompositorVsyncDispatcher> vsyncDispatcher = new CompositorVsyncDispatcher();
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+
+ vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
+ FlushMainThreadLoop();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+}
+
+// Test that if we have vsync enabled, the parent refresh driver should get notifications
+TEST_F(VsyncTester, ParentRefreshDriverGetVsyncNotifications)
+{
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher = globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->SetParentRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+ vsyncDispatcher->SetParentRefreshTimer(nullptr);
+
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+}
+
+// Test that child refresh vsync observers get vsync notifications
+TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications)
+{
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher = globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->AddChildRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher->RemoveChildRefreshTimer(testVsyncObserver);
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+}
+
+// Test that we can read the vsync rate
+TEST_F(VsyncTester, VsyncSourceHasVsyncRate)
+{
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
+ ASSERT_NE(vsyncRate, TimeDuration::Forever());
+ ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
+}
diff --git a/system/graphics/tests/gtest/gfxFontSelectionTest.cpp b/system/graphics/tests/gtest/gfxFontSelectionTest.cpp
new file mode 100644
index 000000000..f00aa9e9e
--- /dev/null
+++ b/system/graphics/tests/gtest/gfxFontSelectionTest.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsDependentString.h"
+
+#include "mozilla/Preferences.h"
+
+#include "gfxContext.h"
+#include "gfxFont.h"
+#include "gfxPlatform.h"
+
+#include "gfxFontTest.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+enum {
+ S_UTF8 = 0,
+ S_ASCII = 1
+};
+
+class FrameTextRunCache;
+
+struct LiteralArray {
+ LiteralArray (unsigned long l1) {
+ data.AppendElement(l1);
+ }
+ LiteralArray (unsigned long l1, unsigned long l2) {
+ data.AppendElement(l1);
+ data.AppendElement(l2);
+ }
+ LiteralArray (unsigned long l1, unsigned long l2, unsigned long l3) {
+ data.AppendElement(l1);
+ data.AppendElement(l2);
+ data.AppendElement(l3);
+ }
+ LiteralArray (unsigned long l1, unsigned long l2, unsigned long l3, unsigned long l4) {
+ data.AppendElement(l1);
+ data.AppendElement(l2);
+ data.AppendElement(l3);
+ data.AppendElement(l4);
+ }
+ LiteralArray (unsigned long l1, unsigned long l2, unsigned long l3, unsigned long l4, unsigned long l5) {
+ data.AppendElement(l1);
+ data.AppendElement(l2);
+ data.AppendElement(l3);
+ data.AppendElement(l4);
+ data.AppendElement(l5);
+ }
+
+ LiteralArray (const LiteralArray& other) {
+ data = other.data;
+ }
+
+ nsTArray<unsigned long> data;
+};
+
+#define GLYPHS LiteralArray
+
+struct TestEntry {
+ TestEntry (const char *aUTF8FamilyString,
+ const gfxFontStyle& aFontStyle,
+ const char *aString)
+ : utf8FamilyString(aUTF8FamilyString),
+ fontStyle(aFontStyle),
+ stringType(S_ASCII),
+ string(aString),
+ isRTL(false)
+ {
+ }
+
+ TestEntry (const char *aUTF8FamilyString,
+ const gfxFontStyle& aFontStyle,
+ int stringType,
+ const char *aString)
+ : utf8FamilyString(aUTF8FamilyString),
+ fontStyle(aFontStyle),
+ stringType(stringType),
+ string(aString),
+ isRTL(false)
+ {
+ }
+
+ struct ExpectItem {
+ ExpectItem(const nsCString& aFontName,
+ const LiteralArray& aGlyphs)
+ : fontName(aFontName), glyphs(aGlyphs)
+ { }
+
+ bool Compare(const nsCString& aFontName,
+ cairo_glyph_t *aGlyphs,
+ int num_glyphs)
+ {
+ // bit that allowed for empty fontname to match all is commented
+ // out
+ if (/*!fontName.IsEmpty() &&*/ !fontName.Equals(aFontName))
+ return false;
+
+ if (num_glyphs != int(glyphs.data.Length()))
+ return false;
+
+ for (int j = 0; j < num_glyphs; j++) {
+ if (glyphs.data[j] != aGlyphs[j].index)
+ return false;
+ }
+
+ return true;
+ }
+
+ nsCString fontName;
+ LiteralArray glyphs;
+ };
+
+ void SetRTL()
+ {
+ isRTL = true;
+ }
+
+ // Empty/nullptr fontName means ignore font name
+ void Expect (const char *platform,
+ const char *fontName,
+ const LiteralArray& glyphs)
+ {
+ if (fontName)
+ Expect (platform, nsDependentCString(fontName), glyphs);
+ else
+ Expect (platform, nsCString(), glyphs);
+ }
+
+ void Expect (const char *platform,
+ const nsCString& fontName,
+ const LiteralArray& glyphs)
+ {
+#if defined(XP_WIN)
+ if (strcmp(platform, "win32"))
+ return;
+#elif defined(XP_MACOSX)
+ if (strcmp(platform, "macosx"))
+ return;
+#elif defined(XP_UNIX)
+ if (strcmp(platform, "gtk2-pango"))
+ return;
+#else
+ return;
+#endif
+
+ expectItems.AppendElement(ExpectItem(fontName, glyphs));
+ }
+
+ bool Check (gfxFontTestStore *store) {
+ if (expectItems.Length() == 0 ||
+ store->items.Length() != expectItems.Length())
+ {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < expectItems.Length(); i++) {
+ if (!expectItems[i].Compare(store->items[i].platformFont,
+ store->items[i].glyphs,
+ store->items[i].num_glyphs))
+ return false;
+ }
+
+ return true;
+ }
+
+ const char *utf8FamilyString;
+ gfxFontStyle fontStyle;
+
+ int stringType;
+ const char *string;
+ bool isRTL;
+
+ nsTArray<ExpectItem> expectItems;
+};
+
+static already_AddRefed<gfxContext>
+MakeContext ()
+{
+ const int size = 200;
+
+ RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
+ CreateOffscreenContentDrawTarget(IntSize(size, size),
+ SurfaceFormat::B8G8R8X8);
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+ if (!ctx) {
+ MOZ_CRASH("gfxContext creation failed");
+ }
+
+ return ctx.forget();
+}
+
+TestEntry*
+AddTest (nsTArray<TestEntry>& testList,
+ const char *utf8FamilyString,
+ const gfxFontStyle& fontStyle,
+ int stringType,
+ const char *string)
+{
+ TestEntry te (utf8FamilyString,
+ fontStyle,
+ stringType,
+ string);
+
+ testList.AppendElement(te);
+
+ return &(testList[testList.Length()-1]);
+}
+
+void
+DumpStore (gfxFontTestStore *store) {
+ if (store->items.Length() == 0) {
+ printf ("(empty)\n");
+ }
+
+ for (uint32_t i = 0;
+ i < store->items.Length();
+ i++)
+ {
+ printf ("Run[% 2d]: '%s' ", i, store->items[i].platformFont.BeginReading());
+
+ for (int j = 0; j < store->items[i].num_glyphs; j++)
+ printf ("%d ", int(store->items[i].glyphs[j].index));
+
+ printf ("\n");
+ }
+}
+
+void
+DumpTestExpect (TestEntry *test) {
+ for (uint32_t i = 0; i < test->expectItems.Length(); i++) {
+ printf ("Run[% 2d]: '%s' ", i, test->expectItems[i].fontName.BeginReading());
+ for (uint32_t j = 0; j < test->expectItems[i].glyphs.data.Length(); j++)
+ printf ("%d ", int(test->expectItems[i].glyphs.data[j]));
+
+ printf ("\n");
+ }
+}
+
+void SetupTests(nsTArray<TestEntry>& testList);
+
+static bool
+RunTest (TestEntry *test, gfxContext *ctx) {
+ RefPtr<gfxFontGroup> fontGroup;
+
+ fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(NS_ConvertUTF8toUTF16(test->utf8FamilyString), &test->fontStyle, nullptr, nullptr, 1.0);
+
+ RefPtr<gfxTextRun> textRun;
+ gfxTextRunFactory::Parameters params = {
+ ctx, nullptr, nullptr, nullptr, 0, 60
+ };
+ uint32_t flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
+ if (test->isRTL) {
+ flags |= gfxTextRunFactory::TEXT_IS_RTL;
+ }
+ uint32_t length;
+ if (test->stringType == S_ASCII) {
+ flags |= gfxTextRunFactory::TEXT_IS_ASCII | gfxTextRunFactory::TEXT_IS_8BIT;
+ length = strlen(test->string);
+ textRun = fontGroup->MakeTextRun(
+ reinterpret_cast<const uint8_t*>(test->string), length, &params, flags);
+ } else {
+ NS_ConvertUTF8toUTF16 str(nsDependentCString(test->string));
+ length = str.Length();
+ textRun = fontGroup->MakeTextRun(str.get(), length, &params, flags);
+ }
+
+ gfxFontTestStore::NewStore();
+ textRun->Draw(ctx, gfxPoint(0,0), DrawMode::GLYPH_FILL, 0, length, nullptr, nullptr, nullptr);
+ gfxFontTestStore *s = gfxFontTestStore::CurrentStore();
+
+ if (!test->Check(s)) {
+ DumpStore(s);
+ printf (" expected:\n");
+ DumpTestExpect(test);
+ gfxFontTestStore::DeleteStore();
+ return false;
+ }
+
+ gfxFontTestStore::DeleteStore();
+
+ return true;
+}
+
+TEST(Gfx, FontSelection) {
+ int passed = 0;
+ int failed = 0;
+
+ // set up the tests
+ nsTArray<TestEntry> testList;
+ SetupTests(testList);
+
+ RefPtr<gfxContext> context = MakeContext();
+
+ for (uint32_t test = 0;
+ test < testList.Length();
+ test++)
+ {
+ bool result = RunTest (&testList[test], context);
+ if (result) {
+ passed++;
+ } else {
+ printf ("Test %d failed\n", test);
+ failed++;
+ }
+ }
+}
+
+// The tests themselves
+
+#include "gfxFontSelectionTests.h"
diff --git a/system/graphics/tests/gtest/gfxFontSelectionTests.h b/system/graphics/tests/gtest/gfxFontSelectionTests.h
new file mode 100644
index 000000000..ce1816df8
--- /dev/null
+++ b/system/graphics/tests/gtest/gfxFontSelectionTests.h
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * This file is #included directly by gfxFontSelectionTest.cpp, and as
+ * such does not need any #include files or similar. (However, should
+ * any extra ones be required, it should be ok to do so, as well as
+ * defining new functions, etc.
+ *
+ * To add a new test, call AddTest with the following arguments: the
+ * CSS font-family string, the gfxFontStyle, an enum (either S_ASCII
+ * or S_UTF8) indicating the string type, and then the text string
+ * itself as a string literal. Unfortunately there is no good way to
+ * embed UTF8 directly into C code, so hex literals will need to be
+ * placed in the string. Because of the way \x is parsed things like
+ * "\xabcd" won't work -- you have to do "\xab""cd". "\xab\x01\x03"
+ * will work fine, though.
+ *
+ * The result of AddTest should be assigned to the variable t; after
+ * AddTest, one or more calls to t->Expect() should be added to define
+ * the expected result. Multiple Expect() calls in a row for the same
+ * platform mean that the resulting glyph/font selection items needs
+ * to have as many items as there are Expect() calls. (See below for
+ * examples.)
+ *
+ * The arguments to Expect are:
+ *
+ * platform - a string identifying the platform.
+ * Valid strings are "win32", "macosx", and "gtk2-pango".
+ * font - a string (UTF8) giving the unique name of the font.
+ * See below for how the unique name is constructed.
+ * glyphs - a set of glyph IDs that are expected.
+ * This array is constructed using a GLYPHS() macro.
+ *
+ * GLYPHS() is just a #define for LiteralArray, which is defined
+ * in gfxFontSelectionTest.cpp -- if you need more array elements
+ * than available, just extend LiteralArray with a new constructor
+ * with the required number of unsigned longs.
+ *
+ * The unique font name is a platform-specific constructed string for
+ * (mostly) identifying a font. On Mac, it's created by taking the
+ * Postscript name of the font. On Windows, it's created by taking
+ * the family name, and then appending attributes such as ":Bold",
+ * ":Italic", etc.
+ *
+ * The easiest way to create a test is to add a call to AddTest, and
+ * then run the test. The output will include a list like:
+ *
+ * ==== Test 1
+ * expected:
+ * Run[ 0]: 'Verdana' 73 82 82
+ * Run[ 1]: 'MS UI Gothic' 19401
+ * Run[ 2]: 'Verdana' 69 68 85
+ * Test 1 failed
+ *
+ * This gives you the information needed for the calls to Expect() --
+ * the unique name, and the glyphs. Appropriate calls to expect for
+ * the above would be:
+ *
+ * t->Expect ("win32", "Verdana", GLYPHS(73, 82, 82));
+ * t->Expect ("win32", "MS UI Gothic", GLYPHS(19401));
+ * t->Expect ("win32", "Verdana", GLYPHS(69, 68, 85));
+ *
+ */
+
+
+void
+SetupTests(nsTArray<TestEntry>& testList)
+{
+ TestEntry *t;
+
+ /* some common styles */
+ gfxFontStyle style_western_normal_16 (mozilla::gfx::FontStyle::NORMAL,
+ 400,
+ 0,
+ 16.0,
+ NS_Atomize(NS_LITERAL_STRING("en")),
+ 0.0,
+ false, false,
+ NS_LITERAL_STRING(""));
+
+ gfxFontStyle style_western_bold_16 (mozilla::gfx::FontStyle::NORMAL,
+ 700,
+ 0,
+ 16.0,
+ NS_Atomize(NS_LITERAL_STRING("en")),
+ 0.0,
+ false, false,
+ NS_LITERAL_STRING(""));
+
+ /* Test 0 */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_ASCII,
+ "ABCD");
+
+ t->Expect ("win32", "Arial", GLYPHS(36, 37, 38, 39));
+ t->Expect ("macosx", "Helvetica", GLYPHS(36, 37, 38, 39));
+ t->Expect ("gtk2-pango", "Albany AMT", GLYPHS(36, 37, 38, 39));
+
+ /* Test 1 */
+ t = AddTest (testList, "verdana,sans-serif",
+ style_western_normal_16,
+ S_UTF8,
+ "foo\xe2\x80\x91""bar");
+
+ t->Expect ("win32", "Verdana", GLYPHS(73, 82, 82));
+ t->Expect ("win32", "Arial Unicode MS", GLYPHS(3236));
+ t->Expect ("win32", "Verdana", GLYPHS(69, 68, 85));
+
+ t->Expect ("macosx", "Verdana", GLYPHS(73, 82, 82));
+ t->Expect ("macosx", "Helvetica", GLYPHS(587));
+ t->Expect ("macosx", "Verdana", GLYPHS(69, 68, 85));
+
+ /* Test 2 */
+ t = AddTest (testList, "sans-serif",
+ style_western_bold_16,
+ S_ASCII,
+ "ABCD");
+
+ t->Expect ("win32", "Arial:700", GLYPHS(36, 37, 38, 39));
+ t->Expect ("macosx", "Helvetica-Bold", GLYPHS(36, 37, 38, 39));
+ t->Expect ("gtk2-pango", "Albany AMT Bold", GLYPHS(36, 37, 38, 39));
+
+ /* Test 3: RTL Arabic with a ligature and leading and trailing whitespace */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_UTF8,
+ " \xd8\xaa\xd9\x85 ");
+ t->SetRTL();
+ t->Expect ("macosx", "Helvetica", GLYPHS(3));
+ t->Expect ("macosx", "ArialMT", GLYPHS(919, 993));
+ t->Expect ("macosx", "Helvetica", GLYPHS(3));
+ t->Expect ("win32", "Arial", GLYPHS(3, 919, 994, 3));
+
+ /* Test 4: LTR Arabic with leading and trailing whitespace */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_UTF8,
+ " \xd9\x85\xd8\xaa ");
+ t->Expect ("macosx", "Helvetica", GLYPHS(3));
+ t->Expect ("macosx", "ArialMT", GLYPHS(993, 919));
+ t->Expect ("macosx", "Helvetica", GLYPHS(3));
+ t->Expect ("win32", "Arial", GLYPHS(3, 994, 919, 3));
+
+ /* Test 5: RTL ASCII with leading whitespace */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_ASCII,
+ " ab");
+ t->SetRTL();
+ t->Expect ("macosx", "Helvetica", GLYPHS(3, 68, 69));
+ t->Expect ("win32", "Arial", GLYPHS(3, 68, 69));
+ t->Expect ("gtk2-pango", "Albany AMT", GLYPHS(3, 68, 69));
+
+ /* Test 6: RTL ASCII with trailing whitespace */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_ASCII,
+ "ab ");
+ t->SetRTL();
+ t->Expect ("macosx", "Helvetica", GLYPHS(68, 69, 3));
+ t->Expect ("win32", "Arial", GLYPHS(68, 69, 3));
+ t->Expect ("gtk2-pango", "Albany AMT", GLYPHS(68, 69, 3));
+
+ /* Test 7: Simple ASCII ligature */
+ /* Do we have a Windows font with ligatures? Can we use DejaVu Sans? */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_ASCII,
+ "fi");
+ t->Expect ("macosx", "Helvetica", GLYPHS(192));
+ t->Expect ("win32", "Arial", GLYPHS(73, 76));
+
+ /* Test 8: DEVANAGARI VOWEL I reordering */
+ /* The glyph for DEVANAGARI VOWEL I 2367 (101) is displayed before the glyph for 2361 (99) */
+ t = AddTest (testList, "sans-serif",
+ style_western_normal_16,
+ S_UTF8,
+ "\xe0\xa4\x9a\xe0\xa4\xbe\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\x8f"); // 2330 2366 2361 2367 2319
+ t->Expect ("macosx", "DevanagariMT", GLYPHS(71, 100, 101, 99, 60));
+ t->Expect ("win32", "Mangal", GLYPHS(133, 545, 465, 161, 102));
+
+ // Disabled Test 9 & 10 because these appear to vary on mac
+
+ /* Test 9: NWJ test */
+ //t = AddTest (testList, "Kartika",
+ // style_western_normal_16,
+ // S_UTF8,
+ // "\xe0\xb4\xb3\xe0\xb5\x8d\xe2\x80\x8d");
+ //t->Expect ("macosx", "MalayalamMN", GLYPHS(360));
+ //t->Expect ("win32", "Kartika", GLYPHS(332));
+
+ /* Test 10: NWJ fallback test */
+ /* it isn't clear what we should actually do in this case. Ideally
+ we would have the same results as the previous test, but because
+ we use sans-serif (i.e. Arial) CSS says we should should really
+ use Arial for U+200D.
+ */
+ //t = AddTest (testList, "sans-serif",
+ // style_western_normal_16,
+ // S_UTF8,
+ // "\xe0\xb4\xb3\xe0\xb5\x8d\xe2\x80\x8d");
+ // Disabled because these appear to vary
+ //t->Expect ("macosx", "MalayalamMN", GLYPHS(360));
+ //t->Expect ("win32", "Kartika", GLYPHS(332));
+}
diff --git a/system/graphics/tests/gtest/gfxSurfaceRefCountTest.cpp b/system/graphics/tests/gtest/gfxSurfaceRefCountTest.cpp
new file mode 100644
index 000000000..0bbd2361d
--- /dev/null
+++ b/system/graphics/tests/gtest/gfxSurfaceRefCountTest.cpp
@@ -0,0 +1,151 @@
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+
+#include "cairo.h"
+
+int
+GetASurfaceRefCount(gfxASurface *s) {
+ NS_ADDREF(s);
+ return s->Release();
+}
+
+int
+CheckInt (int value, int expected) {
+ if (value != expected) {
+ fprintf (stderr, "Expected %d got %d\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+CheckPointer (void *value, void *expected) {
+ if (value != expected) {
+ fprintf (stderr, "Expected %p got %p\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static cairo_user_data_key_t destruction_key;
+void
+SurfaceDestroyNotifier (void *data) {
+ *(int *)data = 1;
+}
+
+int
+TestNewSurface () {
+ int failures = 0;
+ int destroyed = 0;
+
+ RefPtr<gfxASurface> s = new gfxImageSurface (mozilla::gfx::IntSize(10, 10), SurfaceFormat::A8R8G8B8_UINT32);
+ cairo_surface_t *cs = s->CairoSurface();
+
+ cairo_surface_set_user_data (cs, &destruction_key, &destroyed, SurfaceDestroyNotifier);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt (destroyed, 0);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt (destroyed, 0);
+
+ gfxASurface *savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt (destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer (s.get(), savedWrapper);
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt (destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt (destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt (destroyed, 1);
+
+ return failures;
+}
+
+int
+TestExistingSurface () {
+ int failures = 0;
+ int destroyed = 0;
+
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 10, 10);
+
+ cairo_surface_set_user_data (cs, &destruction_key, &destroyed, SurfaceDestroyNotifier);
+
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt (destroyed, 0);
+
+ RefPtr<gfxASurface> s = gfxASurface::Wrap(cs);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 2);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt (destroyed, 0);
+
+ gfxASurface *savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt (destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer (s.get(), savedWrapper);
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt (destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt (GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt (destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt (cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt (destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt (destroyed, 1);
+
+ return failures;
+}
+
+TEST(Gfx, SurfaceRefCount) {
+ int fail;
+
+ fail = TestNewSurface();
+ EXPECT_TRUE(fail == 0) << "TestNewSurface: " << fail << " failures";
+ fail = TestExistingSurface();
+ EXPECT_TRUE(fail == 0) << "TestExistingSurface: " << fail << " failures";
+}
+
diff --git a/system/graphics/tests/gtest/gfxTextRunPerfTest.cpp b/system/graphics/tests/gtest/gfxTextRunPerfTest.cpp
new file mode 100644
index 000000000..603c13ddb
--- /dev/null
+++ b/system/graphics/tests/gtest/gfxTextRunPerfTest.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsDependentString.h"
+
+#include "prinrval.h"
+
+#include "gfxContext.h"
+#include "gfxFont.h"
+#include "gfxPlatform.h"
+
+#include "gfxFontTest.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+struct TestEntry {
+ const char* mFamilies;
+ const char* mString;
+};
+
+TestEntry testList[] = {
+#include "per-word-runs.h"
+{ nullptr, nullptr } // terminator
+};
+
+static already_AddRefed<gfxContext>
+MakeContext ()
+{
+ const int size = 200;
+
+ RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
+ CreateOffscreenContentDrawTarget(IntSize(size, size),
+ SurfaceFormat::B8G8R8X8);
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+ if (!ctx) {
+ MOZ_CRASH("gfxContext creation failed");
+ }
+
+ return ctx.forget();
+}
+
+const char* lastFamilies = nullptr;
+
+static void
+RunTest (TestEntry *test, gfxContext *ctx) {
+ RefPtr<gfxFontGroup> fontGroup;
+ if (!lastFamilies || strcmp(lastFamilies, test->mFamilies)) {
+ gfxFontStyle style_western_normal_16 (mozilla::gfx::FontStyle::NORMAL,
+ 400,
+ 0,
+ 16.0,
+ NS_Atomize(NS_LITERAL_STRING("en")),
+ 0.0,
+ false, false,
+ NS_LITERAL_STRING(""));
+
+ fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(NS_ConvertUTF8toUTF16(test->mFamilies), &style_western_normal_16, nullptr, nullptr, 1.0);
+ }
+
+ RefPtr<gfxTextRun> textRun;
+ uint32_t i;
+ bool isASCII = true;
+ for (i = 0; test->mString[i]; ++i) {
+ if (test->mString[i] & 0x80) {
+ isASCII = false;
+ }
+ }
+ gfxTextRunFactory::Parameters params = {
+ ctx, nullptr, nullptr, nullptr, 0, 60
+ };
+ uint32_t flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
+ uint32_t length;
+ gfxFontTestStore::NewStore();
+ if (isASCII) {
+ flags |= gfxTextRunFactory::TEXT_IS_ASCII |
+ gfxTextRunFactory::TEXT_IS_8BIT;
+ length = strlen(test->mString);
+ textRun = fontGroup->MakeTextRun(
+ reinterpret_cast<const uint8_t*>(test->mString), length, &params, flags);
+ } else {
+ NS_ConvertUTF8toUTF16 str(nsDependentCString(test->mString));
+ length = str.Length();
+ textRun = fontGroup->MakeTextRun(str.get(), length, &params, flags);
+ }
+
+ // Should we test drawing?
+ // textRun->Draw(ctx, gfxPoint(0,0), 0, length, nullptr, nullptr, nullptr);
+
+ textRun->GetAdvanceWidth(0, length, nullptr);
+ gfxFontTestStore::DeleteStore();
+}
+
+uint32_t iterations = 1;
+
+TEST(Gfx, TextRunPref) {
+ RefPtr<gfxContext> context = MakeContext();
+
+ // Start timing
+ PRIntervalTime start = PR_IntervalNow();
+
+ for (uint32_t i = 0; i < iterations; ++i) {
+ for (uint test = 0;
+ test < ArrayLength(testList) - 1;
+ test++)
+ {
+ RunTest(&testList[test], context);
+ }
+ }
+
+ PRIntervalTime end = PR_IntervalNow();
+
+ printf("Elapsed time (ms): %d\n", PR_IntervalToMilliseconds(end - start));
+
+}
diff --git a/system/graphics/tests/gtest/moz.build b/system/graphics/tests/gtest/moz.build
new file mode 100644
index 000000000..66851abc7
--- /dev/null
+++ b/system/graphics/tests/gtest/moz.build
@@ -0,0 +1,59 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'gfxSurfaceRefCountTest.cpp',
+ 'PolygonTestUtils.cpp',
+ 'TestArena.cpp',
+ 'TestArrayView.cpp',
+ 'TestBSPTree.cpp',
+ 'TestBufferRotation.cpp',
+ 'TestColorNames.cpp',
+ 'TestCompositor.cpp',
+ 'TestGfxPrefs.cpp',
+ 'TestGfxWidgets.cpp',
+ 'TestJobScheduler.cpp',
+ 'TestLayers.cpp',
+ 'TestMatrix.cpp',
+ 'TestMoz2D.cpp',
+ 'TestPolygon.cpp',
+ 'TestQcms.cpp',
+ 'TestRect.cpp',
+ 'TestRegion.cpp',
+ 'TestSkipChars.cpp',
+ # Hangs on linux in ApplyGdkScreenFontOptions
+ #'gfxFontSelectionTest.cpp',
+ 'TestTextures.cpp',
+ 'TestTreeTraversal.cpp',
+ # Test works but it doesn't assert anything
+ #'gfxTextRunPerfTest.cpp',
+ # Bug 1179287 - PGO bustage on Linux
+ #'TestTiledLayerBuffer.cpp',
+ 'TestVsync.cpp',
+]
+
+UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [
+ 'TestBase.cpp',
+ 'TestBugs.cpp',
+ 'TestCairo.cpp',
+ 'TestPoint.cpp',
+ 'TestScaling.cpp',
+]]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/gfx/2d',
+ '/gfx/2d/unittest',
+ '/gfx/layers',
+ '/gfx/qcms',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/system/graphics/tests/gtest/per-word-runs.h b/system/graphics/tests/gtest/per-word-runs.h
new file mode 100644
index 000000000..b17ab42d8
--- /dev/null
+++ b/system/graphics/tests/gtest/per-word-runs.h
@@ -0,0 +1,58528 @@
+{ "Lucida Grande",
+ "Tbbtyr" },
+{ "Lucida Grande",
+ "Tbbtyr" },
+{ "Lucida Grande",
+ "Trggvat Fgnegrq" },
+{ "Lucida Grande",
+ "Yngrfg Urnqyvarf" },
+{ "Lucida Grande",
+ "svyr:///Hfref/ebp/Gc2/cres/fgneg.ugzy" },
+{ "Lucida Grande",
+ "svyr:///Hfref/ebp/Gc2/cres/fgneg.ugzy" },
+{ "serif",
+ "Plpyrf:" },
+{ "serif",
+ "Cntrf:" },
+{ "serif",
+ "V18A:" },
+{ "serif",
+ "(ahzore" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "gvzrf" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "ybbc" },
+{ "serif",
+ "bire" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "cntrf)" },
+{ "serif",
+ "(bayl" },
+{ "serif",
+ "svefg" },
+{ "serif",
+ "A" },
+{ "serif",
+ "cntrf;" },
+{ "serif",
+ "yrnir" },
+{ "serif",
+ "oynax" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "(frg" },
+{ "serif",
+ "\"1\"" },
+{ "serif",
+ "vapyhqr" },
+{ "serif",
+ "v18a" },
+{ "sans-serif",
+ "5" },
+{ "sans-serif",
+ "0" },
+{ "sans-serif",
+ "Ortva Grfg" },
+{ "Lucida Grande",
+ "Qbar" },
+{ "sans-serif",
+ "5" },
+{ "serif",
+ " (ahzore bs gvzrf gb ybbc bire gur cntrf)" },
+{ "serif",
+ " (bayl ybbc bire gur svefg A cntrf; yrnir oynax gb ybbc bire nyy cntrf)" },
+{ "sans-serif",
+ "0" },
+{ "serif",
+ " (frg gb \"1\" gb vapyhqr v18a cntrf)" },
+{ "sans-serif",
+ "Ortva Grfg" },
+{ "sans-serif",
+ "1" },
+{ "sans-serif",
+ "1" },
+{ "Lucida Grande",
+ "1" },
+{ "Lucida Grande",
+ "10" },
+{ "Lucida Grande",
+ "100" },
+{ "Lucida Grande",
+ "svyr:///Hfref/ebp/Gc2/cres/plpyre.ugzy?plpyrf=1&cntrf=&v18a=0" },
+{ "Lucida Grande",
+ "svyr:///Hfref/ebp/Gc2/cres/plpyre.ugzy?plpyrf=1&cntrf=&v18a=0" },
+{ "serif",
+ "Ohtmvyyn" },
+{ "serif",
+ "irefvba" },
+{ "serif",
+ "2.11" },
+{ "serif",
+ "Dhrel" },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "cntr" },
+{ "serif",
+ "yrgf" },
+{ "serif",
+ "lbh" },
+{ "serif",
+ "frnepu" },
+{ "serif",
+ "qngnonfr" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "erpbeqrq" },
+{ "serif",
+ "ohtf." },
+{ "serif",
+ "Fgnghf" },
+{ "serif",
+ ":" },
+{ "Lucida Grande",
+ "HAPBASVEZRQ" },
+{ "Lucida Grande",
+ "ARJ" },
+{ "Lucida Grande",
+ "NFFVTARQ" },
+{ "Lucida Grande",
+ "ERBCRARQ" },
+{ "Lucida Grande",
+ "ERFBYIRQ" },
+{ "Lucida Grande",
+ "IREVSVRQ" },
+{ "Lucida Grande",
+ "PYBFRQ" },
+{ "serif",
+ "Erfbyhgvba" },
+{ "Lucida Grande",
+ "SVKRQ" },
+{ "Lucida Grande",
+ "VAINYVQ" },
+{ "Lucida Grande",
+ "JBAGSVK" },
+{ "Lucida Grande",
+ "YNGRE" },
+{ "Lucida Grande",
+ "ERZVAQ" },
+{ "Lucida Grande",
+ "QHCYVPNGR" },
+{ "Lucida Grande",
+ "JBEXFSBEZR" },
+{ "Lucida Grande",
+ "ZBIRQ" },
+{ "Lucida Grande",
+ "---" },
+{ "serif",
+ "Cyngsbez" },
+{ "Lucida Grande",
+ "Nyy" },
+{ "Lucida Grande",
+ "QRP" },
+{ "Lucida Grande",
+ "UC" },
+{ "Lucida Grande",
+ "Znpvagbfu" },
+{ "Lucida Grande",
+ "CP" },
+{ "Lucida Grande",
+ "FTV" },
+{ "Lucida Grande",
+ "Fha" },
+{ "Lucida Grande",
+ "Bgure" },
+{ "serif",
+ "BcFlf" },
+{ "Lucida Grande",
+ "Jvaqbjf" },
+{ "Lucida Grande",
+ "3.1" },
+{ "Lucida Grande",
+ "95" },
+{ "Lucida Grande",
+ "98" },
+{ "Lucida Grande",
+ "ZR" },
+{ "Lucida Grande",
+ "2000" },
+{ "Lucida Grande",
+ "AG" },
+{ "Lucida Grande",
+ "Znp" },
+{ "Lucida Grande",
+ "Flfgrz" },
+{ "Lucida Grande",
+ "7" },
+{ "Lucida Grande",
+ "7.5" },
+{ "Lucida Grande",
+ "7.6.1" },
+{ "Lucida Grande",
+ "8.0" },
+{ "Lucida Grande",
+ "8.5" },
+{ "Lucida Grande",
+ "8.6" },
+{ "Lucida Grande",
+ "9.k" },
+{ "Lucida Grande",
+ "ZnpBF" },
+{ "Lucida Grande",
+ "K" },
+{ "Lucida Grande",
+ "Yvahk" },
+{ "Lucida Grande",
+ "OFQV" },
+{ "Lucida Grande",
+ "SerrOFQ" },
+{ "Lucida Grande",
+ "ArgOFQ" },
+{ "Lucida Grande",
+ "BcraOFQ" },
+{ "Lucida Grande",
+ "NVK" },
+{ "Lucida Grande",
+ "OrBF" },
+{ "Lucida Grande",
+ "UC-HK" },
+{ "Lucida Grande",
+ "VEVK" },
+{ "Lucida Grande",
+ "Arhgevab" },
+{ "Lucida Grande",
+ "BcraIZF" },
+{ "Lucida Grande",
+ "BF/2" },
+{ "Lucida Grande",
+ "BFS/1" },
+{ "Lucida Grande",
+ "Fbynevf" },
+{ "Lucida Grande",
+ "FhaBF" },
+{ "Lucida Grande",
+ "bgure" },
+{ "serif",
+ "Cevbevgl" },
+{ "Lucida Grande",
+ "C1" },
+{ "Lucida Grande",
+ "C2" },
+{ "Lucida Grande",
+ "C3" },
+{ "Lucida Grande",
+ "C4" },
+{ "Lucida Grande",
+ "C5" },
+{ "serif",
+ "Frirevgl" },
+{ "Lucida Grande",
+ "oybpxre" },
+{ "Lucida Grande",
+ "pevgvpny" },
+{ "Lucida Grande",
+ "znwbe" },
+{ "Lucida Grande",
+ "abezny" },
+{ "Lucida Grande",
+ "zvabe" },
+{ "Lucida Grande",
+ "gevivny" },
+{ "Lucida Grande",
+ "raunaprzrag" },
+{ "serif",
+ "Punatrq" },
+{ "serif",
+ "va" },
+{ "serif",
+ "ynfg" },
+{ "serif",
+ "qnlf." },
+{ "serif",
+ "Ng" },
+{ "serif",
+ "yrnfg" },
+{ "serif",
+ "ibgrf." },
+{ "serif",
+ "Rznvy:" },
+{ "serif",
+ " zngpuvat" },
+{ "serif",
+ "nf" },
+{ "Lucida Grande",
+ "ertrkc" },
+{ "Lucida Grande",
+ "abg" },
+{ "Lucida Grande",
+ "fhofgevat" },
+{ "Lucida Grande",
+ "rknpg" },
+{ "serif",
+ "(Jvyy" },
+{ "serif",
+ "zngpu" },
+{ "serif",
+ "nal" },
+{ "serif",
+ "fryrpgrq" },
+{ "serif",
+ "svryqf)" },
+{ "serif",
+ "Nffvtarq" },
+{ "serif",
+ "Gb" },
+{ "serif",
+ "Ercbegre" },
+{ "serif",
+ "DN" },
+{ "serif",
+ "Pbagnpg" },
+{ "serif",
+ "PP" },
+{ "serif",
+ "Nqqrq" },
+{ "serif",
+ "pbzzrag" },
+{ "Lucida Grande",
+ "Bayl" },
+{ "Lucida Grande",
+ "Rkpyhqr" },
+{ "serif",
+ "ohtf" },
+{ "serif",
+ "ahzorerq:" },
+{ "serif",
+ "Jurer" },
+{ "serif",
+ "svryq(f)" },
+{ "Lucida Grande",
+ "[Oht" },
+{ "Lucida Grande",
+ "perngvba]" },
+{ "Lucida Grande",
+ "nffvtarq_gb" },
+{ "Lucida Grande",
+ "oht_svyr_ybp" },
+{ "Lucida Grande",
+ "oht_frirevgl" },
+{ "Lucida Grande",
+ "oht_fgnghf" },
+{ "Lucida Grande",
+ "pbzcbarag" },
+{ "Lucida Grande",
+ "rirepbasvezrq" },
+{ "Lucida Grande",
+ "tebhcfrg" },
+{ "Lucida Grande",
+ "xrljbeqf" },
+{ "Lucida Grande",
+ "bc_flf" },
+{ "Lucida Grande",
+ "cevbevgl" },
+{ "Lucida Grande",
+ "cebqhpg" },
+{ "Lucida Grande",
+ "dn_pbagnpg" },
+{ "Lucida Grande",
+ "erc_cyngsbez" },
+{ "Lucida Grande",
+ "ercbegre" },
+{ "Lucida Grande",
+ "erfbyhgvba" },
+{ "Lucida Grande",
+ "fubeg_qrfp" },
+{ "Lucida Grande",
+ "fgnghf_juvgrobneq" },
+{ "Lucida Grande",
+ "gnetrg_zvyrfgbar" },
+{ "Lucida Grande",
+ "irefvba" },
+{ "Lucida Grande",
+ "ibgrf" },
+{ "serif",
+ "punatrq." },
+{ "serif",
+ "qngrf" },
+{ "serif",
+ "punatrq" },
+{ "serif",
+ "inyhr" },
+{ "serif",
+ "(bcgvbany)" },
+{ "Lucida Grande",
+ "Abj" },
+{ "serif",
+ "Cebtenz:" },
+{ "Lucida Grande",
+ "Oebjfre" },
+{ "Lucida Grande",
+ "Ybpnyvmngvbaf" },
+{ "Lucida Grande",
+ "Pnyraqne" },
+{ "Lucida Grande",
+ "PPX" },
+{ "Lucida Grande",
+ "Qrevingvirf" },
+{ "Lucida Grande",
+ "Qverpgbel" },
+{ "Lucida Grande",
+ "Qbphzragngvba" },
+{ "Lucida Grande",
+ "Teraqry" },
+{ "Lucida Grande",
+ "ZnvyArjf" },
+{ "Lucida Grande",
+ "zbmvyyn.bet" },
+{ "Lucida Grande",
+ "ZbmvyynPynffvp" },
+{ "Lucida Grande",
+ "AFCE" },
+{ "Lucida Grande",
+ "AFF" },
+{ "Lucida Grande",
+ "CFZ" },
+{ "Lucida Grande",
+ "Euvab" },
+{ "Lucida Grande",
+ "Jrogbbyf" },
+{ "serif",
+ "Irefvba:" },
+{ "Lucida Grande",
+ "1.01" },
+{ "Lucida Grande",
+ "1.1" },
+{ "Lucida Grande",
+ "1.2" },
+{ "Lucida Grande",
+ "1.3" },
+{ "Lucida Grande",
+ "1.4" },
+{ "Lucida Grande",
+ "1.5" },
+{ "Lucida Grande",
+ "1998-03-31" },
+{ "Lucida Grande",
+ "1998-04-08" },
+{ "Lucida Grande",
+ "1998-04-29" },
+{ "Lucida Grande",
+ "1998-06-03" },
+{ "Lucida Grande",
+ "1998-07-28" },
+{ "Lucida Grande",
+ "1998-09-04" },
+{ "Lucida Grande",
+ "2.0" },
+{ "Lucida Grande",
+ "2.1" },
+{ "Lucida Grande",
+ "3.0" },
+{ "Lucida Grande",
+ "3.0.1" },
+{ "Lucida Grande",
+ "3.1.1" },
+{ "Lucida Grande",
+ "3.1.2" },
+{ "Lucida Grande",
+ "3.1.3" },
+{ "Lucida Grande",
+ "3.2" },
+{ "Lucida Grande",
+ "3.5" },
+{ "Lucida Grande",
+ "3.5.1" },
+{ "Lucida Grande",
+ "4.0" },
+{ "Lucida Grande",
+ "4.0.1" },
+{ "Lucida Grande",
+ "4.0.2" },
+{ "Lucida Grande",
+ "4.1" },
+{ "Lucida Grande",
+ "hafcrpvsvrq" },
+{ "serif",
+ "Pbzcbarag:" },
+{ "Lucida Grande",
+ "Nppbhag" },
+{ "Lucida Grande",
+ "Znantre" },
+{ "Lucida Grande",
+ "NpgvirK" },
+{ "Lucida Grande",
+ "Jenccre" },
+{ "Lucida Grande",
+ "Nqqerff" },
+{ "Lucida Grande",
+ "Obbx" },
+{ "Lucida Grande",
+ "Nqqerffobbx/YQNC" },
+{ "Lucida Grande",
+ "(aba-HV)" },
+{ "Lucida Grande",
+ "NF-Juvgrobk" },
+{ "Lucida Grande",
+ "Nheben/EQS" },
+{ "Lucida Grande",
+ "OR" },
+{ "Lucida Grande",
+ "SR" },
+{ "Lucida Grande",
+ "Nhgbsvyy" },
+{ "Lucida Grande",
+ "Orexryrl" },
+{ "Lucida Grande",
+ "QO" },
+{ "Lucida Grande",
+ "Obafnv" },
+{ "Lucida Grande",
+ "Obbxznexf" },
+{ "Lucida Grande",
+ "Obfavna/of-ON" },
+{ "Lucida Grande",
+ "Ubbxf" },
+{ "Lucida Grande",
+ "Oebjfre-Trareny" },
+{ "Lucida Grande",
+ "Ohtmvyyn" },
+{ "Lucida Grande",
+ "Ohvyq" },
+{ "Lucida Grande",
+ "Pbasvt" },
+{ "Lucida Grande",
+ "Ohytnevna/ot-OT" },
+{ "Lucida Grande",
+ "PPX-PQYnlbhg" },
+{ "Lucida Grande",
+ "PPX-PhfgbzFuryy" },
+{ "Lucida Grande",
+ "PPX-Vafgnyyngvba" },
+{ "Lucida Grande",
+ "PPX-Furyy" },
+{ "Lucida Grande",
+ "PPX-Juvgrobk" },
+{ "Lucida Grande",
+ "PPX-Jvmneq" },
+{ "Lucida Grande",
+ "pungmvyyn" },
+{ "Lucida Grande",
+ "Pyvrag" },
+{ "Lucida Grande",
+ "Yvoenel" },
+{ "Lucida Grande",
+ "Pbzcvyre" },
+{ "Lucida Grande",
+ "Pbzcbfre" },
+{ "Lucida Grande",
+ "Pbzcbfvgvba" },
+{ "Lucida Grande",
+ "Pbzcbfvgbe" },
+{ "Lucida Grande",
+ "pbasvt" },
+{ "Lucida Grande",
+ "Pbbxvrf" },
+{ "Lucida Grande",
+ "Pber" },
+{ "Lucida Grande",
+ "Qnrzba" },
+{ "Lucida Grande",
+ "Qnavfu/qn-QX" },
+{ "Lucida Grande",
+ "Qrfcbg" },
+{ "Lucida Grande",
+ "Qvnyhc-Nppbhag" },
+{ "Lucida Grande",
+ "Frghc" },
+{ "Lucida Grande",
+ "Qvnyhc-Vafgnyy" },
+{ "Lucida Grande",
+ "Qvnyhc-Zhc/Zhp" },
+{ "Lucida Grande",
+ "Qvnyhc-Hctenqr" },
+{ "Lucida Grande",
+ "QBZ" },
+{ "Lucida Grande",
+ "Yriry" },
+{ "Lucida Grande",
+ "0" },
+{ "Lucida Grande",
+ "1" },
+{ "Lucida Grande",
+ "2" },
+{ "Lucida Grande",
+ "gb" },
+{ "Lucida Grande",
+ "Grkg" },
+{ "Lucida Grande",
+ "Pbairefvba" },
+{ "Lucida Grande",
+ "Ivrjre" },
+{ "Lucida Grande",
+ "Qhgpu/ay" },
+{ "Lucida Grande",
+ "Rqvgbe" },
+{ "Lucida Grande",
+ "Rzorqqvat" },
+{ "Lucida Grande",
+ "NCVf" },
+{ "Lucida Grande",
+ "Rzorqqvat:" },
+{ "Lucida Grande",
+ "Qbpfuryy" },
+{ "Lucida Grande",
+ "Rinatryvfz" },
+{ "Lucida Grande",
+ "Rirag" },
+{ "Lucida Grande",
+ "Unaqyvat" },
+{ "Lucida Grande",
+ "Svygref" },
+{ "Lucida Grande",
+ "SbagYvo" },
+{ "Lucida Grande",
+ "Sbez" },
+{ "Lucida Grande",
+ "Fhozvffvba" },
+{ "Lucida Grande",
+ "Serapu/se-SE" },
+{ "Lucida Grande",
+ "Trezna-Nhfgevn/qr-NG" },
+{ "Lucida Grande",
+ "TGX" },
+{ "Lucida Grande",
+ "Jvqtrg" },
+{ "Lucida Grande",
+ "Uryc" },
+{ "Lucida Grande",
+ "Uvfgbel" },
+{ "Lucida Grande",
+ "UGZY" },
+{ "Lucida Grande",
+ "Ryrzrag" },
+{ "Lucida Grande",
+ "Pbagebyf" },
+{ "Lucida Grande",
+ "Grkg/CbfgFpevcg" },
+{ "Lucida Grande",
+ "Genafyngvba" },
+{ "Lucida Grande",
+ "UGZYSenzrf" },
+{ "Lucida Grande",
+ "UGZYGnoyrf" },
+{ "Lucida Grande",
+ "Vzntr" },
+{ "Lucida Grande",
+ "VzntrYvo" },
+{ "Lucida Grande",
+ "Vafgnyy" },
+{ "Lucida Grande",
+ "Vafgnyyre" },
+{ "Lucida Grande",
+ "Vafgnyyre:" },
+{ "Lucida Grande",
+ "KCV" },
+{ "Lucida Grande",
+ "Cnpxntrf" },
+{ "Lucida Grande",
+ "KCVafgnyy" },
+{ "Lucida Grande",
+ "Ratvar" },
+{ "Lucida Grande",
+ "Vagreangvbanyvmngvba" },
+{ "Lucida Grande",
+ "Vgnyvna/vg-VG" },
+{ "Lucida Grande",
+ "Wnin" },
+{ "Lucida Grande",
+ "sbe" },
+{ "Lucida Grande",
+ "JroFuryy" },
+{ "Lucida Grande",
+ "Fghof" },
+{ "Lucida Grande",
+ "KCPBZ" },
+{ "Lucida Grande",
+ "Oevqtr" },
+{ "Lucida Grande",
+ "Wnin-Vzcyrzragrq" },
+{ "Lucida Grande",
+ "Cyhtvaf" },
+{ "Lucida Grande",
+ "WninFpevcg" },
+{ "Lucida Grande",
+ "Qrohttre" },
+{ "Lucida Grande",
+ "Wninfpevcg" },
+{ "Lucida Grande",
+ "WCRT" },
+{ "Lucida Grande",
+ "Xrlobneq" },
+{ "Lucida Grande",
+ "Anivtngvba" },
+{ "Lucida Grande",
+ "Ynlbhg" },
+{ "Lucida Grande",
+ "YQNC" },
+{ "Lucida Grande",
+ "P" },
+{ "Lucida Grande",
+ "FQX" },
+{ "Lucida Grande",
+ "Gbbyf" },
+{ "Lucida Grande",
+ "YvoZbpun" },
+{ "Lucida Grande",
+ "Yvoenevrf" },
+{ "Lucida Grande",
+ "Yvir" },
+{ "Lucida Grande",
+ "Pbaarpg" },
+{ "Lucida Grande",
+ "Ybpnyvmngvba" },
+{ "Lucida Grande",
+ "YKE" },
+{ "Lucida Grande",
+ "Znvy" },
+{ "Lucida Grande",
+ "Onpx" },
+{ "Lucida Grande",
+ "Raq" },
+{ "Lucida Grande",
+ "Qngnonfr" },
+{ "Lucida Grande",
+ "Jvaqbj" },
+{ "Lucida Grande",
+ "Sebag" },
+{ "Lucida Grande",
+ "ZnguZY" },
+{ "Lucida Grande",
+ "ZVZR" },
+{ "Lucida Grande",
+ "ZVZRYvo" },
+{ "Lucida Grande",
+ "Zvfpryynarbhf" },
+{ "Lucida Grande",
+ "Zbmobg" },
+{ "Lucida Grande",
+ "Zbmvyyn" },
+{ "Lucida Grande",
+ "Qrirybcre" },
+{ "Lucida Grande",
+ "ZbmvyynGenafyngbe" },
+{ "Lucida Grande",
+ "Arpxb" },
+{ "Lucida Grande",
+ "ArgYvo" },
+{ "Lucida Grande",
+ "Argfpncr" },
+{ "Lucida Grande",
+ "6" },
+{ "Lucida Grande",
+ "Argjbexvat" },
+{ "Lucida Grande",
+ "-" },
+{ "Lucida Grande",
+ "Trareny" },
+{ "Lucida Grande",
+ "VZNC" },
+{ "Lucida Grande",
+ "Arjf" },
+{ "Lucida Grande",
+ "CBC" },
+{ "Lucida Grande",
+ "FZGC" },
+{ "Lucida Grande",
+ "Argjbexvat:" },
+{ "Lucida Grande",
+ "Pnpur" },
+{ "Lucida Grande",
+ "AYF" },
+{ "Lucida Grande",
+ "Abejrtvna/aab-ab" },
+{ "Lucida Grande",
+ "BWV" },
+{ "Lucida Grande",
+ "Cnefre" },
+{ "Lucida Grande",
+ "CreYQNC" },
+{ "Lucida Grande",
+ "CVPF" },
+{ "Lucida Grande",
+ "Cyht-vaf" },
+{ "Lucida Grande",
+ "CAT" },
+{ "Lucida Grande",
+ "Cersreraprf" },
+{ "Lucida Grande",
+ "Cersreraprf:" },
+{ "Lucida Grande",
+ "Onpxraq" },
+{ "Lucida Grande",
+ "Cevagvat" },
+{ "Lucida Grande",
+ "Cebsvyr" },
+{ "Lucida Grande",
+ "OnpxRaq" },
+{ "Lucida Grande",
+ "SebagRaq" },
+{ "Lucida Grande",
+ "Zvtengvba" },
+{ "Lucida Grande",
+ "Cebgbpbyf" },
+{ "Lucida Grande",
+ "EQS" },
+{ "Lucida Grande",
+ "Ertvfgel" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "Lucida Grande",
+ "Frphevgl" },
+{ "Lucida Grande",
+ "Frphevgl:" },
+{ "Lucida Grande",
+ "PNCF" },
+{ "Lucida Grande",
+ "Pelcgb" },
+{ "Lucida Grande",
+ "Fryrpgvba" },
+{ "Lucida Grande",
+ "Freire" },
+{ "Lucida Grande",
+ "Bcrengvbaf" },
+{ "Lucida Grande",
+ "Fvqrone" },
+{ "Lucida Grande",
+ "Fvatyr" },
+{ "Lucida Grande",
+ "Fvtaba" },
+{ "Lucida Grande",
+ "Fxvanovyvgl" },
+{ "Lucida Grande",
+ "FghoSR" },
+{ "Lucida Grande",
+ "Fglyr" },
+{ "Lucida Grande",
+ "Fhofpevor" },
+{ "Lucida Grande",
+ "FIT" },
+{ "Lucida Grande",
+ "Gnyxonpx" },
+{ "Lucida Grande",
+ "Grfg" },
+{ "Lucida Grande",
+ "Grfgmvyyn" },
+{ "Lucida Grande",
+ "Gurzrf" },
+{ "Lucida Grande",
+ "Guernqvat" },
+{ "Lucida Grande",
+ "Gvaqreobk" },
+{ "Lucida Grande",
+ "Genpxvat" },
+{ "Lucida Grande",
+ "HV" },
+{ "Lucida Grande",
+ "Hfre" },
+{ "Lucida Grande",
+ "vagresnpr" },
+{ "Lucida Grande",
+ "Vagresnpr:" },
+{ "Lucida Grande",
+ "Qrfvta" },
+{ "Lucida Grande",
+ "Srrqonpx" },
+{ "Lucida Grande",
+ "Ncc" },
+{ "Lucida Grande",
+ "Ivrjf" },
+{ "Lucida Grande",
+ "Jro" },
+{ "Lucida Grande",
+ "KOY" },
+{ "Lucida Grande",
+ "KSR" },
+{ "Lucida Grande",
+ "KZY" },
+{ "Lucida Grande",
+ "KC" },
+{ "Lucida Grande",
+ "Nccf" },
+{ "Lucida Grande",
+ "Nccf:" },
+{ "Lucida Grande",
+ "Pzq-yvar" },
+{ "Lucida Grande",
+ "Srngherf" },
+{ "Lucida Grande",
+ "THV" },
+{ "Lucida Grande",
+ "Zvfpryynal" },
+{ "Lucida Grande",
+ "Gbbyxvg/Jvqtrgf" },
+{ "Lucida Grande",
+ "Gbbyxvg/Jvqtrgf:" },
+{ "Lucida Grande",
+ "Zrahf" },
+{ "Lucida Grande",
+ "Gerrf" },
+{ "Lucida Grande",
+ "KHY" },
+{ "Lucida Grande",
+ "Hgvyvgvrf" },
+{ "Lucida Grande",
+ "KCPbaarpg" },
+{ "Lucida Grande",
+ "KCSP" },
+{ "Lucida Grande",
+ "kcvqy" },
+{ "Lucida Grande",
+ "KFYG" },
+{ "serif",
+ "Gnetrg" },
+{ "serif",
+ "Zvyrfgbar:" },
+{ "Lucida Grande",
+ "Shgher" },
+{ "Lucida Grande",
+ "Z1" },
+{ "Lucida Grande",
+ "Z2" },
+{ "Lucida Grande",
+ "Z3" },
+{ "Lucida Grande",
+ "Z4" },
+{ "Lucida Grande",
+ "Z5" },
+{ "Lucida Grande",
+ "Z6" },
+{ "Lucida Grande",
+ "Z7" },
+{ "Lucida Grande",
+ "Z8" },
+{ "Lucida Grande",
+ "Z9" },
+{ "Lucida Grande",
+ "Z10" },
+{ "Lucida Grande",
+ "Z11" },
+{ "Lucida Grande",
+ "Z12" },
+{ "Lucida Grande",
+ "Z13" },
+{ "Lucida Grande",
+ "Z14" },
+{ "Lucida Grande",
+ "Z15" },
+{ "Lucida Grande",
+ "Z16" },
+{ "Lucida Grande",
+ "Z17" },
+{ "Lucida Grande",
+ "Z18" },
+{ "Lucida Grande",
+ "zbmvyyn0.6" },
+{ "Lucida Grande",
+ "zbmvyyn0.9" },
+{ "Lucida Grande",
+ "zbmvyyn0.9.1" },
+{ "Lucida Grande",
+ "zbmvyyn1.0" },
+{ "Lucida Grande",
+ "zbmvyyn1.0.1" },
+{ "Lucida Grande",
+ "zbmvyyn1.1" },
+{ "Lucida Grande",
+ "zbmvyyn1.2" },
+{ "Lucida Grande",
+ "Z19" },
+{ "Lucida Grande",
+ "Z20" },
+{ "Lucida Grande",
+ "Z21" },
+{ "Lucida Grande",
+ "Z22" },
+{ "Lucida Grande",
+ "Z23" },
+{ "Lucida Grande",
+ "Z24" },
+{ "Lucida Grande",
+ "Z25" },
+{ "Lucida Grande",
+ "Z26" },
+{ "Lucida Grande",
+ "Z27" },
+{ "Lucida Grande",
+ "Z28" },
+{ "Lucida Grande",
+ "Z29" },
+{ "Lucida Grande",
+ "Z30" },
+{ "Lucida Grande",
+ "3.0.2" },
+{ "Lucida Grande",
+ "3.3" },
+{ "Lucida Grande",
+ "4.1.1" },
+{ "Lucida Grande",
+ "4.2" },
+{ "serif",
+ "Fhzznel:" },
+{ "serif",
+ "N" },
+{ "serif",
+ "qrfpevcgvba" },
+{ "serif",
+ "ragel:" },
+{ "serif",
+ "HEY:" },
+{ "serif",
+ "Fgnghf" },
+{ "serif",
+ "juvgrobneq:" },
+{ "serif",
+ "Xrljbeqf" },
+{ "serif",
+ ":" },
+{ "Lucida Grande",
+ "pnfr-vafrafvgvir" },
+{ "Lucida Grande",
+ "pnfr-frafvgvir" },
+{ "Lucida Grande",
+ "nyy" },
+{ "Lucida Grande",
+ "jbeqf" },
+{ "Lucida Grande",
+ "nal" },
+{ "Lucida Grande",
+ "erthyne" },
+{ "Lucida Grande",
+ "rkcerffvba" },
+{ "Lucida Grande",
+ "(" },
+{ "Lucida Grande",
+ ")" },
+{ "Lucida Grande",
+ "Nal" },
+{ "Lucida Grande",
+ "bs" },
+{ "Lucida Grande",
+ "gur" },
+{ "Lucida Grande",
+ "yvfgrq" },
+{ "Lucida Grande",
+ "frg" },
+{ "Lucida Grande",
+ "Abar" },
+{ "Lucida Grande",
+ "Oht" },
+{ "Lucida Grande",
+ "#" },
+{ "Lucida Grande",
+ "Fhzznel" },
+{ "Lucida Grande",
+ "Cebqhpg" },
+{ "Lucida Grande",
+ "Irefvba" },
+{ "Lucida Grande",
+ "Cyngsbez" },
+{ "Lucida Grande",
+ "HEY" },
+{ "Lucida Grande",
+ "BF/Irefvba" },
+{ "Lucida Grande",
+ "Fgnghf" },
+{ "Lucida Grande",
+ "Juvgrobneq" },
+{ "Lucida Grande",
+ "Xrljbeqf" },
+{ "Lucida Grande",
+ "Erfbyhgvba" },
+{ "Lucida Grande",
+ "Frirevgl" },
+{ "Lucida Grande",
+ "Cevbevgl" },
+{ "Lucida Grande",
+ "Pbzcbarag" },
+{ "Lucida Grande",
+ "NffvtarqGb" },
+{ "Lucida Grande",
+ "ErcbegrqOl" },
+{ "Lucida Grande",
+ "Ibgrf" },
+{ "Lucida Grande",
+ "DNPbagnpg" },
+{ "Lucida Grande",
+ "PP" },
+{ "Lucida Grande",
+ "OhtfGuvfQrcraqfBa" },
+{ "Lucida Grande",
+ "BgureOhtfQrcraqvatBaGuvf" },
+{ "Lucida Grande",
+ "Nggnpuzrag" },
+{ "Lucida Grande",
+ "qrfpevcgvba" },
+{ "Lucida Grande",
+ "qngn" },
+{ "Lucida Grande",
+ "zvzr" },
+{ "Lucida Grande",
+ "glcr" },
+{ "Lucida Grande",
+ "vf" },
+{ "Lucida Grande",
+ "cngpu" },
+{ "Lucida Grande",
+ "Gnetrg" },
+{ "Lucida Grande",
+ "Zvyrfgbar" },
+{ "Lucida Grande",
+ "Ynfg" },
+{ "Lucida Grande",
+ "punatrq" },
+{ "Lucida Grande",
+ "qngr" },
+{ "Lucida Grande",
+ "Qnlf" },
+{ "Lucida Grande",
+ "fvapr" },
+{ "Lucida Grande",
+ "oht" },
+{ "Lucida Grande",
+ "Pbzzrag" },
+{ "Lucida Grande",
+ "rdhny" },
+{ "Lucida Grande",
+ "pbagnvaf" },
+{ "Lucida Grande",
+ "(pnfr-frafvgvir)" },
+{ "Lucida Grande",
+ "(pnfr-vafrafvgvir)" },
+{ "Lucida Grande",
+ "qbrf" },
+{ "Lucida Grande",
+ "pbagnva" },
+{ "Lucida Grande",
+ "yrff" },
+{ "Lucida Grande",
+ "guna" },
+{ "Lucida Grande",
+ "terngre" },
+{ "Lucida Grande",
+ "abar" },
+{ "Lucida Grande",
+ "orsber" },
+{ "Lucida Grande",
+ "nsgre" },
+{ "Lucida Grande",
+ "ol" },
+{ "Lucida Grande",
+ "Naq" },
+{ "serif",
+ " " },
+{ "Lucida Grande",
+ "Nqq nabgure obbyrna puneg" },
+{ "serif",
+ "Jung" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "guvf" },
+{ "serif",
+ "fghss?" },
+{ "Lucida Grande",
+ "Be" },
+{ "serif",
+ "Eha" },
+{ "serif",
+ "dhrel" },
+{ "serif",
+ "Ybnq" },
+{ "serif",
+ "erzrzorerq" },
+{ "serif",
+ "dhrel:" },
+{ "serif",
+ "Sbetrg" },
+{ "Lucida Grande",
+ "nfqs" },
+{ "Lucida Grande",
+ "nfqs2" },
+{ "serif",
+ "Erzrzore" },
+{ "serif",
+ "qrsnhyg" },
+{ "serif",
+ "dhrel," },
+{ "serif",
+ "naq" },
+{ "serif",
+ "anzr" },
+{ "serif",
+ "vg:" },
+{ "serif",
+ "Fbeg" },
+{ "serif",
+ "Ol:" },
+{ "Lucida Grande",
+ "Ahzore" },
+{ "Lucida Grande",
+ "'Vzcbegnapr'" },
+{ "Lucida Grande",
+ "Nffvtarr" },
+{ "Lucida Grande",
+ "Fhozvg dhrel" },
+{ "Lucida Grande",
+ "Erfrg onpx gb gur qrsnhyg dhrel" },
+{ "serif",
+ "Tvir" },
+{ "serif",
+ "zr" },
+{ "serif",
+ "n" },
+{ "serif",
+ "pyhr" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "ubj" },
+{ "serif",
+ "hfr" },
+{ "serif",
+ "sbez." },
+{ "serif",
+ "Ybt" },
+{ "serif",
+ "fbzrbar" },
+{ "serif",
+ "orfvqrf" },
+{ "serif",
+ "sbbonm@zbmvyyn.bet" },
+{ "serif",
+ "Punatr" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "cnffjbeq" },
+{ "serif",
+ "be" },
+{ "serif",
+ "cersreraprf." },
+{ "serif",
+ "Ercbeg" },
+{ "serif",
+ "arj" },
+{ "serif",
+ "oht." },
+{ "serif",
+ "Bcra" },
+{ "serif",
+ "Ohtmvyyn" },
+{ "serif",
+ "nppbhag" },
+{ "serif",
+ "Oht" },
+{ "serif",
+ "ercbegf" },
+{ "serif",
+ "Ohtmvyyn" },
+{ "serif",
+ "Zbmvyyn" },
+{ "serif",
+ "oht" },
+{ "serif",
+ "flfgrz." },
+{ "serif",
+ "Sbe" },
+{ "serif",
+ "zber" },
+{ "serif",
+ "vasbezngvba" },
+{ "serif",
+ "jung" },
+{ "serif",
+ "vg" },
+{ "serif",
+ "pna" },
+{ "serif",
+ "qb," },
+{ "serif",
+ "frr" },
+{ "serif",
+ "zbmvyyn.bet" },
+{ "serif",
+ "'f" },
+{ "serif",
+ "oht" },
+{ "serif",
+ "cntrf" },
+{ "serif",
+ "." },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "|" },
+{ "serif",
+ "Dhrel" },
+{ "Lucida Grande",
+ "Svaq" },
+{ "serif",
+ "#" },
+{ "serif",
+ "Ercbegf" },
+{ "serif",
+ "Zl" },
+{ "serif",
+ "ibgrf" },
+{ "serif",
+ "Rqvg" },
+{ "serif",
+ "cersf" },
+{ "serif",
+ "bhg" },
+{ "serif",
+ "sbbonm@zbmvyyn.bet" },
+{ "serif",
+ "Ohtmvyyn irefvba 2.11" },
+{ "serif",
+ " " },
+{ "serif",
+ "Guvf cntr yrgf lbh frnepu gur qngnonfr sbe erpbeqrq ohtf." },
+{ "Lucida Grande",
+ "Jvaqbjf 3.1" },
+{ "Lucida Grande",
+ "Jvaqbjf 95" },
+{ "Lucida Grande",
+ "Jvaqbjf 98" },
+{ "Lucida Grande",
+ "Jvaqbjf ZR" },
+{ "Lucida Grande",
+ "Jvaqbjf 2000" },
+{ "Lucida Grande",
+ "Jvaqbjf AG" },
+{ "Lucida Grande",
+ "Znp Flfgrz 7" },
+{ "serif",
+ " zngpuvat nf " },
+{ "serif",
+ "Nffvtarq Gb" },
+{ "serif",
+ "Ercbegre" },
+{ "serif",
+ "DN Pbagnpg" },
+{ "serif",
+ "(Jvyy zngpu nal bs gur fryrpgrq svryqf)" },
+{ "serif",
+ "PP" },
+{ "serif",
+ "Nqqrq pbzzrag" },
+{ "serif",
+ " ohtf ahzorerq: " },
+{ "serif",
+ "Punatrq va gur " },
+{ "serif",
+ "ynfg " },
+{ "serif",
+ " qnlf." },
+{ "serif",
+ "Ng " },
+{ "serif",
+ "yrnfg " },
+{ "serif",
+ " ibgrf." },
+{ "serif",
+ "Jurer gur svryq(f)" },
+{ "Lucida Grande",
+ "[Oht perngvba]" },
+{ "serif",
+ "punatrq." },
+{ "serif",
+ "qngrf " },
+{ "serif",
+ "gb " },
+{ "Lucida Grande",
+ "Abj" },
+{ "serif",
+ "punatrq gb inyhr " },
+{ "serif",
+ " (bcgvbany)" },
+{ "serif",
+ "1," },
+{ "serif",
+ "ohtmvyyn.zbmvyyn.bet," },
+{ "serif",
+ "558" },
+{ "serif",
+ "1, 1, ohtmvyyn.zbmvyyn.bet, 558" },
+{ "serif",
+ "Gnetrg Zvyrfgbar:" },
+{ "serif",
+ "Pyvpx" },
+{ "serif",
+ "urer" },
+{ "serif",
+ "vs" },
+{ "serif",
+ "lbh'er" },
+{ "serif",
+ "univat" },
+{ "serif",
+ "ceboyrzf" },
+{ "serif",
+ "jvgu" },
+{ "serif",
+ "cntr." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fhes'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gnzcn" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Onl'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jneera" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fncc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "rnearq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbhegu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgenvtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ceb" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Objy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "vaivgr." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Hc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gvgnaf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ohpf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vs" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gvgnaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ohppnarref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "qba'g" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ernpu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fhcre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Objy," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ernfba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jba'g" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "or" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ynpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gnyrag." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Obgu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "pyhof" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jvyy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fraq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "rvtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "cynlref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gb" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "2001" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "NSP-ASP" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ceb" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Objy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jvgu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gnzcn" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Onl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ynaqvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "n" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "yrnthr-uvtu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fvk" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgnegref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "va" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ASP." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qbabina" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZpAnoo" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Phegvf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Znegva" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ner" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "nzbat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "zbfg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "abgvprnoyr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bzvffvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Sro." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "4" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "tnzr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ubabyhyh." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "NSP" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ebfgre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "|" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ASP" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Hfre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "srrqonpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ahzore" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pehapuvat:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arj" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "oybbq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Hc" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "FcbegfPragre," },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "11" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "RG" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "Ceb" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "Objy" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "vaivgrrf" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "naq" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "fahorrf." },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "Jngpu" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "FcbegfPragre." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "URNQYVARF" },
+{ "times,serif",
+ "Synzrq" },
+{ "times,serif",
+ "Qhpx:" },
+{ "times,serif",
+ "Fgehttyvat" },
+{ "times,serif",
+ "Nanurvz" },
+{ "times,serif",
+ "sverf" },
+{ "times,serif",
+ "Unegfohet" },
+{ "times,serif",
+ "Pneqvanyf" },
+{ "times,serif",
+ "hctenqr" },
+{ "times,serif",
+ "cvgpuvat," },
+{ "times,serif",
+ "genqr" },
+{ "times,serif",
+ "Gngvf" },
+{ "times,serif",
+ "sbe" },
+{ "times,serif",
+ "Ureznafba" },
+{ "times,serif",
+ " " },
+{ "times,serif",
+ "Enatref" },
+{ "times,serif",
+ "qrny" },
+{ "times,serif",
+ "Pynlgba" },
+{ "times,serif",
+ "HFP" },
+{ "times,serif",
+ "pnyyf" },
+{ "times,serif",
+ "va" },
+{ "times,serif",
+ "Pneebyy" },
+{ "times,serif",
+ "gb" },
+{ "times,serif",
+ "qvfphff" },
+{ "times,serif",
+ "pbnpuvat" },
+{ "times,serif",
+ "inpnapl" },
+{ "times,serif",
+ "Pbnpuvat" },
+{ "times,serif",
+ "ehzbe" },
+{ "times,serif",
+ "prageny" },
+{ "times,serif",
+ "Onlab" },
+{ "times,serif",
+ "envfrf" },
+{ "times,serif",
+ "HAYI" },
+{ "times,serif",
+ "fgnxrf," },
+{ "times,serif",
+ "zvtug" },
+{ "times,serif",
+ "gnxr" },
+{ "times,serif",
+ "yrtny" },
+{ "times,serif",
+ "npgvba" },
+{ "times,serif",
+ "Zrgf" },
+{ "times,serif",
+ "ercbegrqyl" },
+{ "times,serif",
+ "gelvat" },
+{ "times,serif",
+ "znxr" },
+{ "times,serif",
+ "n" },
+{ "times,serif",
+ "qrny" },
+{ "times,serif",
+ "Jryyf" },
+{ "times,serif",
+ "Ynfg" },
+{ "times,serif",
+ "qnapr?" },
+{ "times,serif",
+ "Rvtug" },
+{ "times,serif",
+ "Enzf" },
+{ "times,serif",
+ "svarq" },
+{ "times,serif",
+ "Obo" },
+{ "times,serif",
+ "'A" },
+{ "times,serif",
+ "Jrnir" },
+{ "times,serif",
+ "Zbhearef" },
+{ "times,serif",
+ "znex" },
+{ "times,serif",
+ "naavirefnel" },
+{ "times,serif",
+ "juvyr" },
+{ "times,serif",
+ "Pneehgu" },
+{ "times,serif",
+ "gevny" },
+{ "times,serif",
+ "zbirf" },
+{ "times,serif",
+ "ba" },
+{ "times,serif",
+ "Nagyrl" },
+{ "times,serif",
+ "unq" },
+{ "times,serif",
+ "phgf," },
+{ "times,serif",
+ "oehvfrf;" },
+{ "times,serif",
+ "qehtf" },
+{ "times,serif",
+ "qvfpbirerq" },
+{ "times,serif",
+ "arneol" },
+{ "times,serif",
+ "Tnyyrel:" },
+{ "times,serif",
+ "Jrqarfqnl'f" },
+{ "times,serif",
+ "orfg" },
+{ "times,serif",
+ "Jver" },
+{ "times,serif",
+ "|" },
+{ "times,serif",
+ "Zber" },
+{ "times,serif",
+ "Arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "GUR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "TNZR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "JBA'G" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OERNX" },
+{ "verdana,geneva,sans-serif,serif",
+ "Jung" },
+{ "verdana,geneva,sans-serif,serif",
+ "qbrf" },
+{ "verdana,geneva,sans-serif,serif",
+ "Nyrk" },
+{ "verdana,geneva,sans-serif,serif",
+ "Ebqevthrm'f" },
+{ "verdana,geneva,sans-serif,serif",
+ "zbafgre" },
+{ "verdana,geneva,sans-serif,serif",
+ "pbagenpg" },
+{ "verdana,geneva,sans-serif,serif",
+ "zrna" },
+{ "verdana,geneva,sans-serif,serif",
+ "sbe" },
+{ "verdana,geneva,sans-serif,serif",
+ "gur" },
+{ "verdana,geneva,sans-serif,serif",
+ "shgher" },
+{ "verdana,geneva,sans-serif,serif",
+ "bs" },
+{ "verdana,geneva,sans-serif,serif",
+ "tnzr?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zber..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgnex:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tenaq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "byq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tnzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "jvyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fheivir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ORNEPNGF" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ORJNER" },
+{ "verdana,geneva,sans-serif,serif",
+ "Va" },
+{ "verdana,geneva,sans-serif,serif",
+ "Pvapvaangv," },
+{ "verdana,geneva,sans-serif,serif",
+ "vg'f" },
+{ "verdana,geneva,sans-serif,serif",
+ "Tbq," },
+{ "verdana,geneva,sans-serif,serif",
+ "pbhagel" },
+{ "verdana,geneva,sans-serif,serif",
+ "naq" },
+{ "verdana,geneva,sans-serif,serif",
+ "Pebffgbja" },
+{ "verdana,geneva,sans-serif,serif",
+ "Fubbgbhg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fubbgbhg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "oevatf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bhg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "orfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "va" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Knivre." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NFX" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NJNL" },
+{ "verdana,geneva,sans-serif,serif",
+ "Qvpxvr" },
+{ "verdana,geneva,sans-serif,serif",
+ "I" },
+{ "verdana,geneva,sans-serif,serif",
+ "nafjref" },
+{ "verdana,geneva,sans-serif,serif",
+ "lbhe" },
+{ "verdana,geneva,sans-serif,serif",
+ "dhrevrf" },
+{ "verdana,geneva,sans-serif,serif",
+ "ba" },
+{ "verdana,geneva,sans-serif,serif",
+ "Obo" },
+{ "verdana,geneva,sans-serif,serif",
+ "Xavtug," },
+{ "verdana,geneva,sans-serif,serif",
+ "HAYI" },
+{ "verdana,geneva,sans-serif,serif",
+ "zber." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ivgnyr:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lbh'ir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tbg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "dhrfgvbaf...?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " GBQNL'F" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ORFG" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " VA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ARJF" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qnyynf'" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Uvpxf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ab" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "qhzzl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bjare" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Enatref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "penml" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "cnlvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nyrk" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebqevthrm?" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Abg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "vs" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "lbh" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ybbx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ng" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jung" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "qvq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgnef." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Znl:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Va" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qraire," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "raq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "vf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "arne" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vffry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qna" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vffry'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ahttrgf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgvax" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "nccneragyl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "yvxr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvz," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ohg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vffry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "znqr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "guvf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "zrff" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvzfrys." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Enggb:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZW'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bcgvbaf?" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cevpryrff" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zvpunry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Wbeqna" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "unf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "cneg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jvmneqf." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Abj" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pncvgnyf." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gung" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "tbbq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "yvivat," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "be" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jung?" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgrebvq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fzhttyvat:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pevzr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ohg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "chavfuzrag" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Sbezre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ASY" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ehaavat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "onpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Trbetr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Wbarf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "vf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "havdhr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "--" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jnf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "obgu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "neerfgrq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "cebfrphgrq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgrebvqf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fzhttyvat." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zber" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Srngherf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbyhzaf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnvyl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " YVIR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "BA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA.pbz" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "RFCA'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ercbegref:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cnegvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fubgf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qvpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fpunnc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pb." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "raq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "rnpu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ercbegref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cnegvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fubg." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yvfgra" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gurz" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "urer." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cbyy:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cvpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "FcbegfPragre'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fubjpnfr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvtuyvtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "FcbegfPragre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "RFCA.pbz" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "grnzvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "hc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "trg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "lbhe" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "vachg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvtuyvtugf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "PYNFFVP" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgratry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "znqr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "onfronyy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sha" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pnfrl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgratry," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jub" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "frira" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jbeyq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Frevrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "nf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Lnaxrrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "znantre," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "cebsvyrq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Guhefqnl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "8" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "c.z." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "zvqavtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "RG." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Znevf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jnf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "hayvxryl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ubzr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "eha" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "xvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebtre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Znevf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "oebxr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ehgu'f" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "erpbeq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ol" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "uvggvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "61" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ubzref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "1961," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Sevqnl." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " FCRPVNY" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FRPGVBAF" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nguyrgrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Punaary:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Trg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "hc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "pybfr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jvgu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Naan," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "N-Ebq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Trg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "npprff" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Naan" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Xbheavxbin," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebqevthrm" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "qbmraf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bgure" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ceb" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "nguyrgrf'" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "crefbany" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jro" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fvgrf." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pbagrfgf:" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zbaqnl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Avtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pbhagqbja" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Punyyratr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Svaq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bhg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbbgonyy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gevivn" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fxvyyf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "dhvpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "rabhtu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "orng" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "pybpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "pbhyq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "jva" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gevc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "RFCA" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fghqvbf." },
+{ "times,serif",
+ "2-Zvahgr" },
+{ "times,serif",
+ "Qevyy" },
+{ "times,serif",
+ "Jvyy" },
+{ "times,serif",
+ "guvf" },
+{ "times,serif",
+ "pbagrfgnag" },
+{ "times,serif",
+ "ernpu" },
+{ "times,serif",
+ "gur" },
+{ "times,serif",
+ "svanyf" },
+{ "times,serif",
+ "bs" },
+{ "times,serif",
+ "RFCA'f" },
+{ "times,serif",
+ "2-Zvahgr" },
+{ "times,serif",
+ "Qevyy?" },
+{ "times,serif",
+ "Jngpu" },
+{ "times,serif",
+ "naq" },
+{ "times,serif",
+ "svaq" },
+{ "times,serif",
+ "bhg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Erny:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "28.8" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uvtuyvtugf:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nhqvb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ivqrb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " NHQVB/IVQRB" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\xe2""\x80""\xa2"" Sevqnl," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RG" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obo" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yrl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cerivrjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "yngrfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bhgfvqr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yvarf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "pbyyrtr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ubbc" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "theh" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Wbr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yhaneqv" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvaarfbgn" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jvyq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "punvezna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Anrtryr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbzcyrgr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pung" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fpurqhyr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " PUNG" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sebz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCAZNT.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "n" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "yvsg?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnivq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Syrzvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tbrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fubccvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "n" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "arj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "wbpxfgenc" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uhtr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Hcfvqr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ovyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jnygba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "guvaxf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lnb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "unf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "hayvzvgrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cbgragvny" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " ASY.PBZ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jrrx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "16" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "zngpuhcf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ibgr:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgncyrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbnpu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lrne" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA.pbz:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "URYC" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NQIREGVFRE" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "VASB" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "PBAGNPG" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "HF" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "GBBYF" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FVGR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZNC" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbclevtug" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\xc2""\xa9""2000" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vagrearg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Iragherf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Grezf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Hfr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cevinpl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cbyvpl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fnsrgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vasbezngvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ner" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "nccyvpnoyr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "guvf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fvgr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rzcyblzrag" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bccbeghavgvrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ng" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA.pbz." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "BA-NVE:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nfx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ubbcf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "dhrfgvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "IBGR:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FcbegfPragre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jrrx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ONFRONYY:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "arjrfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ehzbe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Prageny" },
+{ "Lucida Grande",
+ "RFCA" },
+{ "Lucida Grande",
+ "Xrljbeq" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "Lucida Grande",
+ "RFCA.pbz" },
+{ "Lucida Grande",
+ "Gur" },
+{ "Lucida Grande",
+ "Jro" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Argjbex:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ASY.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AON.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AUY.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ANFPNE" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NOPFcbegf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RKCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "SNAGNFL" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " Fcbeg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Frpgvbaf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ASY" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fpberf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbyyrtr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "SO" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " | " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Objyf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AON" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Z" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OO" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AUY" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tbys" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bayvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "J" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Onfronyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Graavf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zbgbef" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obkvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubefr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Enpvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rkgerzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fbppre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fbpprearg.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCAqrcbegrf" },
+{ "Lucida Grande",
+ "Zber" },
+{ "Lucida Grande",
+ "Fcbegf" },
+{ "Lucida Grande",
+ "OO-zvabef." },
+{ "Lucida Grande",
+ "Bylzcvpf" },
+{ "Lucida Grande",
+ "Svt." },
+{ "Lucida Grande",
+ "fxngvat" },
+{ "Lucida Grande",
+ "Uvtu" },
+{ "Lucida Grande",
+ "Fpubby" },
+{ "Lucida Grande",
+ "Ubpxrl-zva." },
+{ "Lucida Grande",
+ "Bhgqbbef" },
+{ "Lucida Grande",
+ "JAON" },
+{ "Lucida Grande",
+ "fcbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " FcbegfZnyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " Fubc" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "@" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AvxrGbja" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "GrnzFgber" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvfarl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgber" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " Pbzzhavgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fvta-Va/Ubzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zrffntr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obneqf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Guvf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "vf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FcbegfPragre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Enqvb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "GI" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yvfgvatf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Crefbanyvgvrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nfx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cngevpx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Crgre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzzbaf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Puevf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zbegrafra" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvpx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ivgnyr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zry" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Xvcre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "We." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fcrpvny" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Frpgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nguyrgrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Punaary" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "FcbegfSvtherf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Genvavat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebbz" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ivqrb" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Tnzr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Erivrjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbagrfgf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zba." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Avtug" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Punyyratr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ovt" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Purrfr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jrrx" },
+{ "verdana,geneva,sans-serif,serif",
+ "NOPARJF.pbz" },
+{ "verdana,geneva,sans-serif,serif",
+ "TB.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " RFCA Argjbex: " },
+{ "Arial,Helvetica,sans-serif,serif",
+ " Fcbeg Frpgvbaf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbyyrtr SO" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " | " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Z Pbyyrtr OO" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tbys Bayvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "J Pbyyrtr OO" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbyyrtr Fcbegf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubefr Enpvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rkgerzr Fcbegf" },
+{ "Lucida Grande",
+ "Zber Fcbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " FcbegfZnyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " Fubc @ RFCA.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvfarl Fgber" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " Pbzzhavgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zrffntr Obneqf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " RFCA" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Guvf vf FcbegfPragre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA Enqvb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RFCA GI Yvfgvatf" },
+{ "Verdana, Geneva, Arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "Verdana, Geneva, Arial,serif",
+ "Ohyyqbmre" },
+{ "Verdana, Geneva, Arial,serif",
+ "Ercbeg" },
+{ "Verdana, Geneva, Arial,serif",
+ "Arire" },
+{ "Verdana, Geneva, Arial,serif",
+ "oevat" },
+{ "Verdana, Geneva, Arial,serif",
+ "n" },
+{ "Verdana, Geneva, Arial,serif",
+ "xavsr" },
+{ "Verdana, Geneva, Arial,serif",
+ "gb" },
+{ "Verdana, Geneva, Arial,serif",
+ "tha" },
+{ "Verdana, Geneva, Arial,serif",
+ "svtug" },
+{ "Verdana, Geneva, Arial,serif",
+ "--" },
+{ "Verdana, Geneva, Arial,serif",
+ "naq" },
+{ "Verdana, Geneva, Arial,serif",
+ "bgure" },
+{ "Verdana, Geneva, Arial,serif",
+ "SSY" },
+{ "Verdana, Geneva, Arial,serif",
+ "yrffbaf." },
+{ "Verdana, Geneva, Arial,serif",
+ "Objy" },
+{ "Verdana, Geneva, Arial,serif",
+ "Cvpx'rz" },
+{ "Verdana, Geneva, Arial,serif",
+ "RFCA'f" },
+{ "Verdana, Geneva, Arial,serif",
+ "serr" },
+{ "Verdana, Geneva, Arial,serif",
+ "Pbyyrtr" },
+{ "Verdana, Geneva, Arial,serif",
+ "unf" },
+{ "Verdana, Geneva, Arial,serif",
+ "nqqrq" },
+{ "Verdana, Geneva, Arial,serif",
+ "gjvfg" },
+{ "Verdana, Geneva, Arial,serif",
+ "guvf" },
+{ "Verdana, Geneva, Arial,serif",
+ "ubyvqnl" },
+{ "Verdana, Geneva, Arial,serif",
+ "frnfba." },
+{ "Verdana, Geneva, Arial,serif",
+ "Purpx" },
+{ "Verdana, Geneva, Arial,serif",
+ "vg" },
+{ "Verdana, Geneva, Arial,serif",
+ "bhg." },
+{ "Verdana, Geneva, Arial,serif",
+ "Cynl" },
+{ "Verdana, Geneva, Arial,serif",
+ "3Cynl" },
+{ "Verdana, Geneva, Arial,serif",
+ "Cerfragrq" },
+{ "Verdana, Geneva, Arial,serif",
+ "ol" },
+{ "Verdana, Geneva, Arial,serif",
+ "Abxvn," },
+{ "Verdana, Geneva, Arial,serif",
+ "FOP" },
+{ "Verdana, Geneva, Arial,serif",
+ "&" },
+{ "Verdana, Geneva, Arial,serif",
+ "OryyFbhgu" },
+{ "Verdana, Geneva, Arial,serif",
+ " " },
+{ "Verdana, Geneva, Arial,serif",
+ "Gnxr" },
+{ "Verdana, Geneva, Arial,serif",
+ "gur" },
+{ "Verdana, Geneva, Arial,serif",
+ "gbhe" },
+{ "Verdana, Geneva, Arial,serif",
+ "Jveryrff" },
+{ "Verdana, Geneva, Arial,serif",
+ "3Cynl" },
+{ "Verdana, Geneva, Arial,serif",
+ "SNAGNFL" },
+{ "Verdana, Geneva, Arial,serif",
+ "TNZRF" },
+{ "Verdana, Geneva, Arial,serif",
+ "FCBEGFZNYY" },
+{ "Verdana, Geneva, Arial,serif",
+ "Fvzcyl" },
+{ "Verdana, Geneva, Arial,serif",
+ "Eribygvat" },
+{ "Verdana, Geneva, Arial,serif",
+ "Qe." },
+{ "Verdana, Geneva, Arial,serif",
+ "Wnpx" },
+{ "Verdana, Geneva, Arial,serif",
+ "qvfphffrf" },
+{ "Verdana, Geneva, Arial,serif",
+ "Ahttrgf'" },
+{ "Verdana, Geneva, Arial,serif",
+ "arne" },
+{ "Verdana, Geneva, Arial,serif",
+ "zhgval." },
+{ "Verdana, Geneva, Arial,serif",
+ "Chg" },
+{ "Verdana, Geneva, Arial,serif",
+ "Zr" },
+{ "Verdana, Geneva, Arial,serif",
+ "Va," },
+{ "Verdana, Geneva, Arial,serif",
+ "Pbnpu" },
+{ "Verdana, Geneva, Arial,serif",
+ "Ernqref" },
+{ "Verdana, Geneva, Arial,serif",
+ "funer" },
+{ "Verdana, Geneva, Arial,serif",
+ "zrzbevrf" },
+{ "Verdana, Geneva, Arial,serif",
+ "bs" },
+{ "Verdana, Geneva, Arial,serif",
+ "gurve" },
+{ "Verdana, Geneva, Arial,serif",
+ "pbnpurf." },
+{ "Verdana, Geneva, Arial,serif",
+ "Enycu" },
+{ "Verdana, Geneva, Arial,serif",
+ "Jvyrl" },
+{ "Verdana, Geneva, Arial,serif",
+ "Gurer" },
+{ "Verdana, Geneva, Arial,serif",
+ "ner" },
+{ "Verdana, Geneva, Arial,serif",
+ "gjb" },
+{ "Verdana, Geneva, Arial,serif",
+ "glcrf" },
+{ "Verdana, Geneva, Arial,serif",
+ "DOf:" },
+{ "Verdana, Geneva, Arial,serif",
+ "syrrg" },
+{ "Verdana, Geneva, Arial,serif",
+ "qrnq" },
+{ "Verdana, Geneva, Arial,serif",
+ "zrng" },
+{ "Verdana, Geneva, Arial,serif",
+ "." },
+{ "Verdana, Geneva, Arial,serif",
+ "CNTR" },
+{ "Verdana, Geneva, Arial,serif",
+ "2" },
+{ "TimesRoman,serif",
+ "Jub" },
+{ "TimesRoman,serif",
+ "jnf" },
+{ "TimesRoman,serif",
+ "gur" },
+{ "TimesRoman,serif",
+ "ovttrfg" },
+{ "TimesRoman,serif",
+ "bzvffvba" },
+{ "TimesRoman,serif",
+ "sebz" },
+{ "TimesRoman,serif",
+ "2001" },
+{ "TimesRoman,serif",
+ "Ceb" },
+{ "TimesRoman,serif",
+ "Objy?" },
+{ "TimesRoman,serif",
+ "Zvxr" },
+{ "TimesRoman,serif",
+ "Naqrefba" },
+{ "TimesRoman,serif",
+ "Gvz" },
+{ "TimesRoman,serif",
+ "Oebja" },
+{ "TimesRoman,serif",
+ "Phegvf" },
+{ "TimesRoman,serif",
+ "Znegva" },
+{ "TimesRoman,serif",
+ "Rq" },
+{ "TimesRoman,serif",
+ "ZpPnsserl" },
+{ "TimesRoman,serif",
+ "Qbabina" },
+{ "TimesRoman,serif",
+ "ZpAnoo" },
+{ "TimesRoman,serif",
+ "Funaaba" },
+{ "TimesRoman,serif",
+ "Funecr" },
+{ "TimesRoman,serif",
+ "Serq" },
+{ "TimesRoman,serif",
+ "Gnlybe" },
+{ "Lucida Grande",
+ "Fhozvg Ibgr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gnzcn Onl'f Jneera Fncc rnearq uvf sbhegu " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgenvtug Ceb Objy vaivgr." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fhes'f Hc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "sbe Gvgnaf, Ohpf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vs gur Gvgnaf naq " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ohppnarref qba'g ernpu " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur Fhcre Objy, gur " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ernfba jba'g or ynpx bs " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gnyrag. Obgu pyhof jvyy " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fraq rvtug cynlref gb gur " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "2001 NSP-ASP Ceb " },
+{ "Arial,Helvetica,sans-serif,serif",
+ ", jvgu Gnzcn Onl " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ynaqvat n yrnthr-uvtu fvk " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "fgnegref va gur ASP. " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qbabina ZpAnoo naq " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Phegvf Znegva ner nzbat " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur zbfg abgvprnoyr " },
+{ "Arial,Helvetica,sans-serif,serif",
+ " sbe gur Sro. 4 " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "tnzr va Ubabyhyh." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "NSP ebfgre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " | " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ASP ebfgre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Hfre srrqonpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ahzore Pehapuvat: Arj Ceb Objy oybbq" },
+{ "serif",
+ " " },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "FcbegfPragre, 11 RG" },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "Ceb Objy vaivgrrf naq " },
+{ "verdana, arial, helvetica, sans sarif,serif",
+ "fahorrf. Jngpu" },
+{ "times,serif",
+ "Synzrq Qhpx: Fgehttyvat " },
+{ "times,serif",
+ "Nanurvz sverf Unegfohet" },
+{ "times,serif",
+ " " },
+{ "times,serif",
+ "Pneqvanyf hctenqr cvgpuvat, " },
+{ "times,serif",
+ "genqr Gngvf sbe Ureznafba" },
+{ "times,serif",
+ " " },
+{ "times,serif",
+ "Enatref qrny Pynlgba " },
+{ "times,serif",
+ "HFP pnyyf va Pneebyy gb " },
+{ "times,serif",
+ "qvfphff pbnpuvat inpnapl" },
+{ "times,serif",
+ "Pbnpuvat ehzbe prageny" },
+{ "times,serif",
+ "Onlab envfrf HAYI fgnxrf, " },
+{ "times,serif",
+ "zvtug gnxr yrtny npgvba" },
+{ "times,serif",
+ "Zrgf ercbegrqyl gelvat gb " },
+{ "times,serif",
+ "znxr n qrny sbe Jryyf" },
+{ "times,serif",
+ "Ynfg qnapr? Rvtug Enzf " },
+{ "times,serif",
+ "svarq sbe Obo 'A Jrnir" },
+{ "times,serif",
+ "Zbhearef znex naavirefnel " },
+{ "times,serif",
+ "juvyr Pneehgu gevny zbirf ba" },
+{ "times,serif",
+ "Nagyrl unq phgf, oehvfrf; " },
+{ "times,serif",
+ "qehtf qvfpbirerq arneol" },
+{ "times,serif",
+ "Tnyyrel: " },
+{ "times,serif",
+ "Jrqarfqnl'f orfg" },
+{ "times,serif",
+ " | " },
+{ "times,serif",
+ "Zber Arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "GUR TNZR JBA'G OERNX" },
+{ "verdana,geneva,sans-serif,serif",
+ "Jung qbrf Nyrk Ebqevthrm'f " },
+{ "verdana,geneva,sans-serif,serif",
+ "zbafgre pbagenpg zrna sbe gur " },
+{ "verdana,geneva,sans-serif,serif",
+ "shgher bs gur tnzr?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ORNEPNGF ORJNER" },
+{ "verdana,geneva,sans-serif,serif",
+ "Va Pvapvaangv, vg'f Tbq, pbhagel " },
+{ "verdana,geneva,sans-serif,serif",
+ "naq gur Pebffgbja Fubbgbhg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fubbgbhg oevatf bhg gur orfg va" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NFX NJNL" },
+{ "verdana,geneva,sans-serif,serif",
+ "Qvpxvr I nafjref lbhe dhrevrf " },
+{ "verdana,geneva,sans-serif,serif",
+ "ba Obo Xavtug, HAYI naq " },
+{ "Lucida Grande",
+ "RFCA Xrljbeq" },
+{ "Lucida Grande",
+ "Svaq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "BA-NVE: " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nfx n pbyyrtr ubbcf dhrfgvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "IBGR: " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FcbegfPragre Tnzr bs gur Jrrx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ONFRONYY: " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gur arjrfg Ehzbe Prageny" },
+{ "serif",
+ "1," },
+{ "serif",
+ "2," },
+{ "serif",
+ "rfca.tb.pbz," },
+{ "serif",
+ "989" },
+{ "serif",
+ "1, 2, rfca.tb.pbz, 989" },
+{ "Verdana, Geneva, Arial,serif",
+ "CNTR 2" },
+{ "Verdana, Geneva, Arial,serif",
+ "Fvzcyl Eribygvat" },
+{ "Verdana, Geneva, Arial,serif",
+ "Qe. Wnpx qvfphffrf" },
+{ "Verdana, Geneva, Arial,serif",
+ " gur " },
+{ "Verdana, Geneva, Arial,serif",
+ "Ahttrgf' arne zhgval." },
+{ "Verdana, Geneva, Arial,serif",
+ "Chg Zr Va, Pbnpu" },
+{ "Verdana, Geneva, Arial,serif",
+ "Ernqref funer " },
+{ "Verdana, Geneva, Arial,serif",
+ " bs gurve " },
+{ "Verdana, Geneva, Arial,serif",
+ "Enycu Jvyrl" },
+{ "Verdana, Geneva, Arial,serif",
+ "Gurer ner " },
+{ "Verdana, Geneva, Arial,serif",
+ "gjb glcrf bs " },
+{ "Verdana, Geneva, Arial,serif",
+ "DOf: syrrg naq qrnq " },
+{ "Verdana, Geneva, Arial,serif",
+ "SNAGNFL TNZRF" },
+{ "Verdana, Geneva, Arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Verdana, Geneva, Arial,serif",
+ "Ohyyqbmre Ercbeg" },
+{ "Verdana, Geneva, Arial,serif",
+ "Arire oevat n xavsr gb n " },
+{ "Verdana, Geneva, Arial,serif",
+ "tha svtug -- naq bgure " },
+{ "Verdana, Geneva, Arial,serif",
+ "SSY yrffbaf." },
+{ "Verdana, Geneva, Arial,serif",
+ "Objy Cvpx'rz" },
+{ "Verdana, Geneva, Arial,serif",
+ "RFCA'f serr Pbyyrtr " },
+{ "Verdana, Geneva, Arial,serif",
+ "Objy Cvpx'rz unf " },
+{ "Verdana, Geneva, Arial,serif",
+ "nqqrq n gjvfg guvf " },
+{ "Verdana, Geneva, Arial,serif",
+ "ubyvqnl frnfba. Purpx " },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubyvqnl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tvsg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fanpxf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pynffvsvrqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrpvfvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "Thvqrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qverpgbel" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tnzrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbo" },
+{ "Verdana,Geneva,Arial,serif",
+ "Frnepu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zncf/Qverpgvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zbivr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Yvfgvatf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Juvgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cntrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Geniry" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrnyf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lryybj" },
+{ "Lucida Grande",
+ "Argfpncr" },
+{ "Lucida Grande",
+ "NygnIvfgn" },
+{ "Lucida Grande",
+ "Nfx" },
+{ "Lucida Grande",
+ "Wrrirf" },
+{ "Lucida Grande",
+ "Rkpvgr" },
+{ "Lucida Grande",
+ "Tbbtyr" },
+{ "Lucida Grande",
+ "TbGb" },
+{ "Lucida Grande",
+ "UbgObg" },
+{ "Lucida Grande",
+ "YbbxFzneg" },
+{ "Lucida Grande",
+ "Ylpbf" },
+{ "serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gbc" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cvpxf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arj:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Argfpncr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obbx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Syvtug" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ohvyq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "n" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jro" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fvgr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbafhzre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Thvqrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tvsg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vqrnf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yngrfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fbsgjner" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ybir@Argfpncr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bayvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jva" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "$5X" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ercnvef" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubebfpbcr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qrcnegzragf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nhgbf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ohf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pnerref" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbzchgvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Urnygu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubhfr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vagreangvbany" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yvsrfglyrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ybpny" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zhfvp" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Enqvb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Erfrnepu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yrnea" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fubccvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Geniry" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fnyr!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Puevfgznf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Purff" },
+{ "Verdana,Geneva,Arial,serif",
+ "frg" },
+{ "Verdana,Geneva,Arial,serif",
+ "$47.99." },
+{ "Verdana,Geneva,Arial,serif",
+ "Purpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jrngure " },
+{ "Verdana,Geneva,Arial,serif",
+ " Tb" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ragre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Mvc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbqr" },
+{ "Verdana,Geneva,Arial,serif",
+ "be" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pvgl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NC" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tber" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ohfu" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ohpunana" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Anqre" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "262" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "246" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "0" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "270" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ibgrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "arrqrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jva." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ryrpgbeny" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ibgrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhaqnl," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Abirzore" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "12," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "2000" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Onggyr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Urnqf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pbheg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Trbetr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "J." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ohfu'f" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "nggrzcg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fgbc" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "unaq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhag" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "bs" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "onyybgf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "va" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "sbhe" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sybevqn" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhagvrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "zbirf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "n" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "srqreny" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhegebbz" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Zbaqnl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "zbeavat." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Qrzbpengf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "yngr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhaqnl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "svyrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gurve" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erfcbafr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "25-cntr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "oevrs," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "nfxvat" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fhvg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "or" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erwrpgrq." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Na" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Nffbpvngrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cerff" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gnyyl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fubjrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "yrnqvat" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ol" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "288" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ibgrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhaqnl." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ernq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fgbel" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "|" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yngrfg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cbyvgvpny" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Erfhygf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cbyy" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ ":" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fubhyq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbaprqr?" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Lrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ab" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ibgr" },
+{ "Verdana,serif",
+ "YNGRFG" },
+{ "Verdana,serif",
+ "ARJF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sybevqn" },
+{ "Verdana,Geneva,Arial,serif",
+ "erpbhag" },
+{ "Verdana,Geneva,Arial,serif",
+ "onggyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "zbirf" },
+{ "Verdana,Geneva,Arial,serif",
+ "gb" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbhegf" },
+{ "Verdana,serif",
+ "Arjf" },
+{ "Verdana,serif",
+ "Erfphref" },
+{ "Verdana,serif",
+ "Frnepu" },
+{ "Verdana,serif",
+ "sbe" },
+{ "Verdana,serif",
+ "155" },
+{ "Verdana,serif",
+ "Ghaary" },
+{ "Verdana,serif",
+ "Ivpgvzf" },
+{ "Verdana,serif",
+ "Fcbegf" },
+{ "Verdana,serif",
+ "Eniraf" },
+{ "Verdana,serif",
+ "Gbc" },
+{ "Verdana,serif",
+ "Gvgnaf," },
+{ "Verdana,serif",
+ "Enzf" },
+{ "Verdana,serif",
+ "Orng" },
+{ "Verdana,serif",
+ "Tvnagf" },
+{ "Verdana,serif",
+ "Zbivrf" },
+{ "Verdana,serif",
+ "'Natryf'" },
+{ "Verdana,serif",
+ "Obk" },
+{ "Verdana,serif",
+ "Bssvpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZNEXRG" },
+{ "Verdana,Geneva,Arial,serif",
+ "PRAGRE" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fgbpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "Dhbgrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ol" },
+{ "Verdana,Geneva,Arial,serif",
+ "FNYBZBA" },
+{ "Verdana,Geneva,Arial,serif",
+ "FZVGU" },
+{ "Verdana,Geneva,Arial,serif",
+ "ONEARL" },
+{ "Verdana,Geneva,Arial,serif",
+ "Dhbgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Flzoby" },
+{ "Verdana,Geneva,Arial,serif",
+ "Anzr" },
+{ "Verdana,Geneva,Arial,serif",
+ "POF" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZnexrgJngpu" },
+{ "Verdana,Geneva,Arial,serif",
+ ":" },
+{ "Verdana,Geneva,Arial,serif",
+ "N" },
+{ "Verdana,Geneva,Arial,serif",
+ "oehvfrq" },
+{ "Verdana,Geneva,Arial,serif",
+ "znexrg" },
+{ "Verdana,Geneva,Arial,serif",
+ "ybbxf" },
+{ "Verdana,Geneva,Arial,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,serif",
+ "eryvrs" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erhgref" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cnpvsvp" },
+{ "Verdana,Geneva,Arial,serif",
+ "Evz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cenvfr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Genqr," },
+{ "Verdana,Geneva,Arial,serif",
+ "Nibvq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qvivfvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Rf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fh" },
+{ "Verdana,Geneva,Arial,serif",
+ "pnfn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zvpebfbsg'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "pnfn?" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbhoyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "fnynel," },
+{ "Verdana,Geneva,Arial,serif",
+ "qbhoyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "sha" },
+{ "Verdana,Geneva,Arial,serif",
+ "jvgu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zbafgre.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Snfuvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cnenabvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fcrpvny" },
+{ "Verdana,Geneva,Arial,serif",
+ "srngherf" },
+{ "Verdana,Geneva,Arial,serif",
+ "jvgu" },
+{ "Verdana,Geneva,Arial,serif",
+ "n" },
+{ "Verdana,Geneva,Arial,serif",
+ "qvssrerag" },
+{ "Verdana,Geneva,Arial,serif",
+ "gnxr" },
+{ "Verdana,Geneva,Arial,serif",
+ "ba" },
+{ "Verdana,Geneva,Arial,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "jbexcynpr." },
+{ "Verdana,Geneva,Arial,serif",
+ "Bssvpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uneqjner" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cbjre-hc" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "arj" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbzchgref" },
+{ "Verdana,Geneva,Arial,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,serif",
+ "npprffbevrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "Cresrpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbo" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gnxr" },
+{ "Verdana,Geneva,Arial,serif",
+ "dhvpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "dhvm" },
+{ "Verdana,Geneva,Arial,serif",
+ "svaq" },
+{ "Verdana,Geneva,Arial,serif",
+ "bhg" },
+{ "Verdana,Geneva,Arial,serif",
+ "juvpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "pnerre" },
+{ "Verdana,Geneva,Arial,serif",
+ "vf" },
+{ "Verdana,Geneva,Arial,serif",
+ "evtug" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbh." },
+{ "Verdana,Geneva,Arial,serif",
+ "Trg" },
+{ "Verdana,Geneva,Arial,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jbeq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bhg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrirybc" },
+{ "Verdana,Geneva,Arial,serif",
+ "choyvpvgl-ohvyqvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "zngrevnyf" },
+{ "Verdana,Geneva,Arial,serif",
+ "va" },
+{ "Verdana,Geneva,Arial,serif",
+ "nf" },
+{ "Verdana,Geneva,Arial,serif",
+ "yvggyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "30" },
+{ "Verdana,Geneva,Arial,serif",
+ "zvahgrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "Znantr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fuvccvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arrqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Xrrc" },
+{ "Verdana,Geneva,Arial,serif",
+ "genpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,serif",
+ "fuvczragf" },
+{ "Verdana,Geneva,Arial,serif",
+ "rnfvyl." },
+{ "Verdana,Geneva,Arial,serif",
+ "Erfrnepu," },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbzcner" },
+{ "Verdana,Geneva,Arial,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Trg" },
+{ "Verdana,Geneva,Arial,serif",
+ "orfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "cebqhpgf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fznyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "ng" },
+{ "Verdana,Geneva,Arial,serif",
+ "cevpr." },
+{ "Verdana,Geneva,Arial,serif",
+ "zber" },
+{ "Verdana,Geneva,Arial,serif",
+ "ohfvarff >" },
+{ "Verdana,serif",
+ "FLZOBY" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "QWVN" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Anfqnd" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "F&C" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "500" },
+{ "Verdana,serif",
+ "CEVPR" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "10,602.90" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "3,028.99" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "1,365.98" },
+{ "Verdana,serif",
+ "PUNATR" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-231.30" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-171.36" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-34.16" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Nf" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "bs" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Abi." },
+{ "sans-serif,Arial,Helvetica,serif",
+ "13," },
+{ "sans-serif,Arial,Helvetica,serif",
+ "2000" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "2:52" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "nz" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "RG" },
+{ "Verdana,Geneva,Arial,serif",
+ "Svanapvny" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cynaavat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nqivpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "svaqvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "svanapvny" },
+{ "Verdana,Geneva,Arial,serif",
+ "cynaare." },
+{ "Verdana,Geneva,Arial,serif",
+ "Vairfgvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Onfvpf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Na" },
+{ "Verdana,Geneva,Arial,serif",
+ "8-fgrc" },
+{ "Verdana,Geneva,Arial,serif",
+ "cevzre" },
+{ "Verdana,Geneva,Arial,serif",
+ "dhrfgvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bayvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "Onaxvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Svaq" },
+{ "Verdana,Geneva,Arial,serif",
+ "bayvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "onax" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cbegsbyvb" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sberpnfgre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jvyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbh" },
+{ "Verdana,Geneva,Arial,serif",
+ "znxr" },
+{ "Verdana,Geneva,Arial,serif",
+ "tbnyf?" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ergverzrag" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gvcf" },
+{ "Verdana,Geneva,Arial,serif",
+ "401(x)" },
+{ "Verdana,Geneva,Arial,serif",
+ "nppbhagf," },
+{ "Verdana,Geneva,Arial,serif",
+ "ebyybiref." },
+{ "Verdana,Geneva,Arial,serif",
+ "FhcreFgne" },
+{ "Verdana,Geneva,Arial,serif",
+ "Shaqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zhghny" },
+{ "Verdana,Geneva,Arial,serif",
+ "shaqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "gung" },
+{ "Verdana,Geneva,Arial,serif",
+ "ner" },
+{ "Verdana,Geneva,Arial,serif",
+ "ubg." },
+{ "Verdana,Geneva,Arial,serif",
+ "VAFHENAPR" },
+{ "Verdana,Geneva,Arial,serif",
+ "dhbgrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "nyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "vafhenapr" },
+{ "Verdana,Geneva,Arial,serif",
+ "arrqf:" },
+{ "Verdana,Geneva,Arial,serif",
+ "nhgb," },
+{ "Verdana,Geneva,Arial,serif",
+ "yvsr," },
+{ "Verdana,Geneva,Arial,serif",
+ "ubzrbjare," },
+{ "Verdana,Geneva,Arial,serif",
+ "eragre." },
+{ "Verdana,Geneva,Arial,serif",
+ "crefbany" },
+{ "Verdana,Geneva,Arial,serif",
+ "svanapr >" },
+{ "Verdana,Geneva,Arial,serif",
+ "OHVYQ" },
+{ "Verdana,Geneva,Arial,serif",
+ "LBHE" },
+{ "Verdana,Geneva,Arial,serif",
+ "OHFVARFF" },
+{ "Verdana,Geneva,Arial,serif",
+ "BAYVAR" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "arrqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ernpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "phfgbzref." },
+{ "Verdana,Geneva,Arial,serif",
+ "gbbyf" },
+{ "Verdana,Geneva,Arial,serif",
+ "arrq" },
+{ "Verdana,Geneva,Arial,serif",
+ "trg" },
+{ "Verdana,Geneva,Arial,serif",
+ "fgnegrq." },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Rzcyblzrag" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bayvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "Znantr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Znexrg" },
+{ "Verdana,Geneva,Arial,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fryy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Argjbex" },
+{ "Verdana,Geneva,Arial,serif",
+ "OHL" },
+{ "Verdana,Geneva,Arial,serif",
+ "Znxr" },
+{ "Verdana,Geneva,Arial,serif",
+ "fher" },
+{ "Verdana,Geneva,Arial,serif",
+ "ohl" },
+{ "Verdana,Geneva,Arial,serif",
+ "cebqhpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "PBAARPG" },
+{ "Verdana,Geneva,Arial,serif",
+ "Funer," },
+{ "Verdana,Geneva,Arial,serif",
+ "pbaarpg," },
+{ "Verdana,Geneva,Arial,serif",
+ "vagrenpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "bgure" },
+{ "Verdana,Geneva,Arial,serif",
+ "bjaref." },
+{ "Verdana,Geneva,Arial,serif",
+ "CEBZBGR" },
+{ "Verdana,Geneva,Arial,serif",
+ "Yrirentr" },
+{ "Verdana,Geneva,Arial,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "cbjre" },
+{ "Verdana,Geneva,Arial,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vagrearg" },
+{ "Verdana,Geneva,Arial,serif",
+ "jurgure" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbh\xe2""\x80""\x99""er" },
+{ "Verdana,Geneva,Arial,serif",
+ "bayvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "be" },
+{ "Verdana,Geneva,Arial,serif",
+ "abg." },
+{ "Verdana,Geneva,Arial,serif",
+ "argohfvarff >" },
+{ "Verdana,Geneva,Arial,serif",
+ "BznunFgrnxf.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jva" },
+{ "Verdana,Geneva,Arial,serif",
+ "$1000" },
+{ "Verdana,Geneva,Arial,serif",
+ "jbegu" },
+{ "Verdana,Geneva,Arial,serif",
+ "fgrnxf." },
+{ "Verdana,Geneva,Arial,serif",
+ "Nagvdhrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cevagref" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lbhat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jbzra'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbzchgref" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sbbq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cubgb" },
+{ "Verdana,Geneva,Arial,serif",
+ "SYBJREF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gunaxftvivat" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbzcyrgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "serfu" },
+{ "Verdana,Geneva,Arial,serif",
+ "sybjref." },
+{ "Verdana,Geneva,Arial,serif",
+ "TVSGF" },
+{ "Verdana,Geneva,Arial,serif",
+ "ERTVFGEVRF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubeg" },
+{ "Verdana,Geneva,Arial,serif",
+ "gvzr?" },
+{ "Verdana,Geneva,Arial,serif",
+ "tvsg" },
+{ "Verdana,Geneva,Arial,serif",
+ "gung'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "hajenccvat." },
+{ "Verdana,Geneva,Arial,serif",
+ "LBHAT" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZRA'F" },
+{ "Verdana,Geneva,Arial,serif",
+ "yngrfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "geraqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "gvgnavhz" },
+{ "Verdana,Geneva,Arial,serif",
+ "jngpurf" },
+{ "Verdana,Geneva,Arial,serif",
+ "uvtu" },
+{ "Verdana,Geneva,Arial,serif",
+ "grpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "fubrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "fubccvat >" },
+{ "Verdana,Geneva,Arial,serif",
+ "Frphevgl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pragre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Yngrfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "frphevgl" },
+{ "Verdana,Geneva,Arial,serif",
+ "gbbyf," },
+{ "Verdana,Geneva,Arial,serif",
+ "arjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "genvavat." },
+{ "Verdana,Geneva,Arial,serif",
+ "Fbsgjner" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uneqjner" },
+{ "Verdana,Geneva,Arial,serif",
+ "Argfpncr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Punaary" },
+{ "Verdana,Geneva,Arial,serif",
+ "PQ" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erivrjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Grpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "QRIRYBCRE" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jnag" },
+{ "Verdana,Geneva,Arial,serif",
+ "or" },
+{ "Verdana,Geneva,Arial,serif",
+ "na" },
+{ "Verdana,Geneva,Arial,serif",
+ "KZY" },
+{ "Verdana,Geneva,Arial,serif",
+ "rkcreg?" },
+{ "Verdana,Geneva,Arial,serif",
+ "QBJAYBNQ" },
+{ "Verdana,Geneva,Arial,serif",
+ "NG" },
+{ "Verdana,Geneva,Arial,serif",
+ "FREIVPR" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sebz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Oebjfre," },
+{ "Verdana,Geneva,Arial,serif",
+ "FznegHcqngr," },
+{ "Verdana,Geneva,Arial,serif",
+ "Cyht-Vaf." },
+{ "Verdana,Geneva,Arial,serif",
+ "CEVAG" },
+{ "Verdana,Geneva,Arial,serif",
+ "PRAGENY" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbjaybnq" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbby" },
+{ "Verdana,Geneva,Arial,serif",
+ "pneqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fgngvbarel" },
+{ "Verdana,Geneva,Arial,serif",
+ "grzcyngrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "grpuabybtl >" },
+{ "Verdana,Geneva,Arial,serif",
+ "CERGGL" },
+{ "Verdana,Geneva,Arial,serif",
+ "FGNGHR" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fur'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "ornhgvshy," },
+{ "Verdana,Geneva,Arial,serif",
+ "fur'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "gnyragrq," },
+{ "Verdana,Geneva,Arial,serif",
+ "fur" },
+{ "Verdana,Geneva,Arial,serif",
+ "unf" },
+{ "Verdana,Geneva,Arial,serif",
+ "tbetrbhf" },
+{ "Verdana,Geneva,Arial,serif",
+ "oblsevraq--naq" },
+{ "Verdana,Geneva,Arial,serif",
+ "znxrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "$20" },
+{ "Verdana,Geneva,Arial,serif",
+ "zvyyvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "cvpgher." },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Whyvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "jnagf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fuval" },
+{ "Verdana,Geneva,Arial,serif",
+ "zna." },
+{ "Verdana,Geneva,Arial,serif",
+ "Pryroevgvrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ragregnvazrag" },
+{ "Verdana,Geneva,Arial,serif",
+ "Snzvyl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sha" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zhfvp" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fcbegf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fpberf" },
+{ "Verdana,Geneva,Arial,serif",
+ "OBBXF" },
+{ "Verdana,Geneva,Arial,serif",
+ "fuvccvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "beqref" },
+{ "Verdana,Geneva,Arial,serif",
+ "bire" },
+{ "Verdana,Geneva,Arial,serif",
+ "$100." },
+{ "Verdana,Geneva,Arial,serif",
+ "UBZR" },
+{ "Verdana,Geneva,Arial,serif",
+ "VZCEBIRZRAG" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubzr" },
+{ "Verdana,Geneva,Arial,serif",
+ "ubyvqnlf?" },
+{ "Verdana,Geneva,Arial,serif",
+ "vg" },
+{ "Verdana,Geneva,Arial,serif",
+ "fcnexyr." },
+{ "Verdana,Geneva,Arial,serif",
+ "FCBEGF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pngpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "fpberf" },
+{ "Verdana,Geneva,Arial,serif",
+ "sebz" },
+{ "Verdana,Geneva,Arial,serif",
+ "ASY." },
+{ "Verdana,Geneva,Arial,serif",
+ "sha >" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cnegaref " },
+{ "Verdana,Geneva,Arial,serif",
+ "SrqRk" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fuvccvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,serif",
+ "Baivn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fznyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uryc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Djrfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "5\xc2""\xa2""" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pnyyf" },
+{ "Verdana,Geneva,Arial,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,serif",
+ "FbsgjnerOhlf.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Terng" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nhfgenyvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Oenmvy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pnanqn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Puvan" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qraznex" },
+{ "Verdana,Geneva,Arial,serif",
+ "Senapr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Treznal" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Xbat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vgnyl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wncna" },
+{ "Verdana,Geneva,Arial,serif",
+ "Xbern" },
+{ "Verdana,Geneva,Arial,serif",
+ "Yngva" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nzrevpn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Argureynaqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fcnva" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fjrqra" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gnvjna" },
+{ "Verdana,Geneva,Arial,serif",
+ "HX" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zber..." },
+{ "Verdana,Geneva,Arial,serif",
+ "Nqiregvfr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jvgu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Hf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nssvyvngr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cebtenz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Oebjfre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qvfgevohgvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "VCynarg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbof" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vasb" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fvgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Znc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jung'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arj" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbby" },
+{ "Verdana,Geneva,Arial,serif",
+ "\xc2""\xa9"" 2000" },
+{ "Verdana,Geneva,Arial,serif",
+ "Argfpncr." },
+{ "Verdana,Geneva,Arial,serif",
+ "Nyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Evtugf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erfreirq." },
+{ "Verdana,Geneva,Arial,serif",
+ "Grezf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Freivpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cevinpl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cbyvpl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Guvf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fvgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "cbjrerq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Freiref" },
+{ "Verdana,Geneva,Arial,serif",
+ "." },
+{ "Verdana,Geneva,Arial,serif",
+ "Pyvpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "urer!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pyvpx urer!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubyvqnl Tvsg Fanpxf" },
+{ "Lucida Grande",
+ "Argfpncr Frnepu" },
+{ "serif",
+ " " },
+{ "sans-serif",
+ "Fhozvg" },
+{ "sans-serif",
+ "Fhozvg " },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrpvfvba Thvqrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbo Frnepu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zbivr Yvfgvatf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Juvgr Cntrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Geniry Qrnyf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lryybj Cntrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Onggyr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Urnqf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Onggyr " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pbheg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Urnqf gb " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NC" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ryrpgbeny Ibgrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "270 ibgrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "arrqrq gb jva." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhaqnl, Abirzore 12, 2000" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Onggyr Urnqf gb Pbheg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Trbetr J. Ohfu'f nggrzcg gb fgbc gur unaq pbhag bs" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "onyybgf va sbhe Sybevqn pbhagvrf zbirf gb n srqreny " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhegebbz Zbaqnl zbeavat. Qrzbpengf yngr Fhaqnl " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "svyrq gurve erfcbafr va n 25-cntr oevrs, nfxvat gur " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fhvg gb or erwrpgrq. Na Nffbpvngrq Cerff gnyyl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fubjrq Ohfu yrnqvat Tber ol 288 ibgrf Fhaqnl." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ernq gur Fgbel" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " | " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yngrfg Cbyvgvpny Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " | " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ ": Fubhyq Tber pbaprqr?" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Lrf " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ab " },
+{ "Verdana,Geneva,Arial,serif",
+ "Ibgr" },
+{ "Verdana,serif",
+ "YNGRFG ARJF" },
+{ "Verdana,Geneva,Arial,serif",
+ "PAA.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ "Sybevqn erpbhag onggyr zbirf gb pbhegf" },
+{ "Verdana,serif",
+ " " },
+{ "Verdana,serif",
+ " " },
+{ "Verdana,serif",
+ "Erfphref Frnepu sbe 155 Ghaary Ivpgvzf" },
+{ "Verdana,serif",
+ "Eniraf Gbc Gvgnaf, Enzf Orng Tvnagf" },
+{ "Verdana,serif",
+ "'Natryf' Gbc Obk Bssvpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fnyr! Puevfgznf Purff frg " },
+{ "Verdana,Geneva,Arial,serif",
+ "Purpx Lbhe Jrngure " },
+{ "Verdana,Geneva,Arial,serif",
+ " Tb" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ragre Mvc Pbqr be Pvgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gbc Cvpxf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arj: Argfpncr 6" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obbx Lbhe Syvtug" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ohvyq n Jro Fvgr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbafhzre Thvqrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tvsg Vqrnf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yngrfg Fbsgjner" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bayvar Tnzrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jva $5X sbe Ercnvef" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lbhe Ubebfpbcr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubhfr & Ubzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ohf. & Pnerref" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Erfrnepu & Yrnea" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zhfvp & Enqvb" },
+{ "serif",
+ "Ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZNEXRG PRAGRE" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fgbpx Dhbgrf ol" },
+{ "Verdana,Geneva,Arial,serif",
+ "FNYBZBA FZVGU ONEARL" },
+{ "Verdana,Geneva,Arial,serif",
+ "Dhbgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Flzoby " },
+{ "Verdana,Geneva,Arial,serif",
+ "Anzr" },
+{ "Verdana,Geneva,Arial,serif",
+ "POF ZnexrgJngpu" },
+{ "Verdana,Geneva,Arial,serif",
+ ": N oehvfrq " },
+{ "Verdana,Geneva,Arial,serif",
+ "znexrg ybbxf sbe eryvrs" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Cnpvsvp Evz gb Cenvfr " },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr Genqr, Nibvq Qvivfvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Rf fh pnfn Zvpebfbsg'f " },
+{ "Verdana,Geneva,Arial,serif",
+ "pnfn?" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbhoyr lbhe fnynel, qbhoyr " },
+{ "Verdana,Geneva,Arial,serif",
+ "lbhe sha jvgu Zbafgre.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Snfuvba naq Cnenabvn" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Fcrpvny " },
+{ "Verdana,Geneva,Arial,serif",
+ "srngherf jvgu n qvssrerag gnxr ba " },
+{ "Verdana,Geneva,Arial,serif",
+ "gur jbexcynpr." },
+{ "Verdana,Geneva,Arial,serif",
+ "Bssvpr Uneqjner" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Cbjre-hc lbhe " },
+{ "Verdana,Geneva,Arial,serif",
+ "ohfvarff jvgu arj pbzchgref naq " },
+{ "Verdana,Geneva,Arial,serif",
+ "Cresrpg Wbo" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Gnxr n dhvpx dhvm gb " },
+{ "Verdana,Geneva,Arial,serif",
+ "svaq bhg juvpu pnerre vf evtug sbe " },
+{ "Verdana,Geneva,Arial,serif",
+ "Trg gur Jbeq Bhg" },
+{ "Verdana,Geneva,Arial,serif",
+ "choyvpvgl-ohvyqvat zngrevnyf va nf " },
+{ "Verdana,Geneva,Arial,serif",
+ "yvggyr nf 30 zvahgrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "Znantr Lbhe Fuvccvat Arrqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Xrrc genpx bs lbhe fuvczragf " },
+{ "Verdana,Geneva,Arial,serif",
+ "Erfrnepu, Pbzcner & Ohl" },
+{ "Verdana,Geneva,Arial,serif",
+ "gur orfg cebqhpgf sbe lbhe fznyy " },
+{ "Verdana,Geneva,Arial,serif",
+ "ohfvarff ng gur evtug cevpr." },
+{ "Verdana,Geneva,Arial,serif",
+ "zber ohfvarff >" },
+{ "serif",
+ "Crefbany" },
+{ "serif",
+ "Svanapr" },
+{ "serif",
+ "Crefbany Svanapr" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "F&C 500" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Nf bs Abi. 13, 2000 2:52 nz RG" },
+{ "Verdana,Geneva,Arial,serif",
+ "Svanapvny Cynaavat" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Nqivpr ba " },
+{ "Verdana,Geneva,Arial,serif",
+ "svaqvat n svanapvny cynaare." },
+{ "Verdana,Geneva,Arial,serif",
+ "Vairfgvat Onfvpf" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Na 8-fgrc " },
+{ "Verdana,Geneva,Arial,serif",
+ "cevzre sbe lbhe dhrfgvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bayvar Onaxvat" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Svaq gur orfg " },
+{ "Verdana,Geneva,Arial,serif",
+ "bayvar onax sbe lbh." },
+{ "Verdana,Geneva,Arial,serif",
+ "Cbegsbyvb Sberpnfgre" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Jvyy lbh " },
+{ "Verdana,Geneva,Arial,serif",
+ "znxr lbhe svanapvny tbnyf?" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Gvcf sbe 401(x) " },
+{ "Verdana,Geneva,Arial,serif",
+ "nppbhagf, ebyybiref." },
+{ "Verdana,Geneva,Arial,serif",
+ "FhcreFgne Shaqf" },
+{ "Verdana,Geneva,Arial,serif",
+ ": Zhghny shaqf " },
+{ "Verdana,Geneva,Arial,serif",
+ "gung ner ubg." },
+{ "Verdana,Geneva,Arial,serif",
+ "VAFHENAPR PRAGRE" },
+{ "Verdana,Geneva,Arial,serif",
+ "Trg dhbgrf sbe nyy lbhe vafhenapr " },
+{ "Verdana,Geneva,Arial,serif",
+ "arrqf: nhgb, yvsr, ubzrbjare, " },
+{ "Verdana,Geneva,Arial,serif",
+ ": Qrirybc " },
+{ "Verdana,Geneva,Arial,serif",
+ ": " },
+{ "Verdana,Geneva,Arial,serif",
+ ": Trg " },
+{ "serif",
+ "3," },
+{ "serif",
+ "ubzr.argfpncr.pbz," },
+{ "serif",
+ "796" },
+{ "serif",
+ "1, 3, ubzr.argfpncr.pbz, 796" },
+{ "Arial, Helvetica,serif",
+ "Frnepu: " },
+{ "Lucida Grande",
+ "Jverq" },
+{ "Lucida Grande",
+ "Arjf" },
+{ "Lucida Grande",
+ "Jrozbaxrl" },
+{ "Lucida Grande",
+ "Thvqrf" },
+{ "Lucida Grande",
+ "UbgJverq" },
+{ "Lucida Grande",
+ "Nepuvirf" },
+{ "Lucida Grande",
+ "Zntnmvar" },
+{ "Lucida Grande",
+ "Navzngvba" },
+{ "Lucida Grande",
+ "Rkcerff" },
+{ "Lucida Grande",
+ "Fhpx.pbz" },
+{ "Lucida Grande",
+ "->" },
+{ "Arial, Helvetica,serif",
+ " sbe " },
+{ "Lucida Grande",
+ "tb" },
+{ "Verdana, Arial, Helvetica,serif",
+ "<nqiregvfrzrag>" },
+{ "Verdana, Arial, Helvetica,serif",
+ "</nqiregvfrzrag>" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jrozbaxrl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Xvqf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Yrffbaf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "naq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "cebwrpgf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "sbe" },
+{ "Verdana, Arial, Helvetica,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica,serif",
+ "arkg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "trarengvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "bs" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ohvyqref" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zntnmvar" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Oebjfr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ol" },
+{ "Verdana, Arial, Helvetica,serif",
+ "vffhr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "be" },
+{ "Verdana, Arial, Helvetica,serif",
+ "gbcvp" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nepuvirf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Sbhe" },
+{ "Verdana, Arial, Helvetica,serif",
+ "lrnef" },
+{ "Verdana, Arial, Helvetica,serif",
+ "UbgJverq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "UbgObg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro'f" },
+{ "Verdana, Arial, Helvetica,serif",
+ "#1" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Frnepu" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ratvar" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arrq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "uryc?" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fraq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "hf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "srrqonpx" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qvtvgny" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fgnss" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbagvahbhf" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Pheerag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ubb-Un:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jveryrff" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jbeyq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Tnqtrgf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Tvmzbf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ZC3" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ebpxf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jbzra" },
+{ "Verdana, Arial, Helvetica,serif",
+ "va" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grpu" },
+{ "Verdana, Arial, Helvetica,serif",
+ "VCB" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bhgybbx" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rkrphgvir" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fhzznel" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Enqvb" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vaqrk" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hcqngrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "sebz" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qvtvgny" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Sebag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "02:00" },
+{ "Verdana, Arial, Helvetica,serif",
+ "nz " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fnsre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pnyyf," },
+{ "Verdana, Arial, Helvetica,serif",
+ "be" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fryyvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Srne?" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Va" },
+{ "Verdana, Arial, Helvetica,serif",
+ "jung" },
+{ "Verdana, Arial, Helvetica,serif",
+ "znl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "or" },
+{ "Verdana, Arial, Helvetica,serif",
+ "nabgure" },
+{ "Verdana, Arial, Helvetica,serif",
+ "choyvp" },
+{ "Verdana, Arial, Helvetica,serif",
+ "eryngvbaf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "punyyratr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pryyhyne" },
+{ "Verdana, Arial, Helvetica,serif",
+ "cubar" },
+{ "Verdana, Arial, Helvetica,serif",
+ "vaqhfgel," },
+{ "Verdana, Arial, Helvetica,serif",
+ "n" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pbzcnal" },
+{ "Verdana, Arial, Helvetica,serif",
+ "vf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "znexrgvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "qrivpr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "vg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fnlf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "jvyy" },
+{ "Verdana, Arial, Helvetica,serif",
+ "cerirag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pryy" },
+{ "Verdana, Arial, Helvetica,serif",
+ "cubarf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "unezvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "obql." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ol" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ryvfn" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ongvfgn." },
+{ "Verdana, Arial, Helvetica,serif",
+ "11:00" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ohvyqvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "n" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Orggre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Onyybg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Obk" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hcqngrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "sebz" },
+{ "Verdana, Arial, Helvetica,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qvtvgny" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Sebag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Penfu" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbhefrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "va:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Navzngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vasb." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nepuvgrpgher" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrfvta" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ortvaavat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "WninFpevcg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nqinaprq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fvgr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bcgvzvmngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qlanzvp" },
+{ "Verdana, Arial, Helvetica,serif",
+ "UGZY" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qernzjrnire" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrirybcre'f" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Erfbhepr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "CUC4" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vafgnyyngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bireivrj" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jurgure" },
+{ "Verdana, Arial, Helvetica,serif",
+ "lbh'er" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jvaqbjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Aba-Jvaqbjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "glcr," },
+{ "Verdana, Arial, Helvetica,serif",
+ "Whyvr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fubjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "lbh" },
+{ "Verdana, Arial, Helvetica,serif",
+ "rirelguvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "arrq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica,serif",
+ "xabj" },
+{ "Verdana, Arial, Helvetica,serif",
+ "trg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "CUC4" },
+{ "Verdana, Arial, Helvetica,serif",
+ "hc" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ehaavat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "hfvat" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ncnpur" },
+{ "Verdana, Arial, Helvetica,serif",
+ "nf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "onfr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "freire" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fbsgjner." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Yrnea" },
+{ "Verdana, Arial, Helvetica,serif",
+ "nobhg:" },
+{ "Lucida Grande",
+ "Nhgubevat" },
+{ "Lucida Grande",
+ "Qrfvta" },
+{ "Lucida Grande",
+ "Zhygvzrqvn" },
+{ "Lucida Grande",
+ "R-Ohfvarff" },
+{ "Lucida Grande",
+ "Cebtenzzvat" },
+{ "Lucida Grande",
+ "Onpxraq" },
+{ "Lucida Grande",
+ "Wbof" },
+{ "sans-serif",
+ "Tb!" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrirybcre'f" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Erfbhepr" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Nqqvgvbany" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rkuvovgf:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "GRKG.HER" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ubhef" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ybpngvba," },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ybpngvba..." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Eryvfgra:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cyrnfr!" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vzchyfr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Sernx" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cebgbglcr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "19" },
+{ "Verdana, Arial, Helvetica,serif",
+ "[qraqevgr]" },
+{ "Verdana, Arial, Helvetica,serif",
+ "49,682,923" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fgbevrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "..." },
+{ "Verdana, Arial, Helvetica,serif",
+ " Navzngvbaf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zber:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nyy" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Navzngvbaf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cyht-va" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grfgre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fhozvffvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Thvqryvarf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Dhvpxvrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arjfyrggre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Obql" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Uhegf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "TVPX" },
+{ "Verdana, Arial, Helvetica,serif",
+ "VV" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Tneqra" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Srrqonpx" },
+{ "serif",
+ "|" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Uryc" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Nobhg" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Hf" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Wbof" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Nqiregvfr" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Cevinpl" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Fgngrzrag" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Grezf" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "bs" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Freivpr" },
+{ "Verdana, Arial, Geneva,serif",
+ "Pbclevtug" },
+{ "Verdana, Arial, Geneva,serif",
+ "\xc2""\xa9""" },
+{ "Verdana, Arial, Geneva,serif",
+ "1994-2000" },
+{ "Verdana, Arial, Geneva,serif",
+ "Jverq" },
+{ "Verdana, Arial, Geneva,serif",
+ "Qvtvgny" },
+{ "Verdana, Arial, Geneva,serif",
+ "Vap.," },
+{ "Verdana, Arial, Geneva,serif",
+ "n" },
+{ "Verdana, Arial, Geneva,serif",
+ "Ylpbf" },
+{ "Verdana, Arial, Geneva,serif",
+ "Argjbex" },
+{ "Verdana, Arial, Geneva,serif",
+ "fvgr." },
+{ "Verdana, Arial, Geneva,serif",
+ "Nyy" },
+{ "Verdana, Arial, Geneva,serif",
+ "evtugf" },
+{ "Verdana, Arial, Geneva,serif",
+ "erfreirq." },
+{ "serif",
+ "4," },
+{ "serif",
+ "ubgjverq.ylpbf.pbz," },
+{ "serif",
+ "430" },
+{ "serif",
+ "1, 4, ubgjverq.ylpbf.pbz, 430" },
+{ "Arial, Helvetica,serif",
+ "Frnepu: " },
+{ "Lucida Grande",
+ "Jverq Arjf" },
+{ "Arial, Helvetica,serif",
+ " sbe " },
+{ "Lucida Grande",
+ "tb" },
+{ "Verdana, Arial, Helvetica,serif",
+ "<nqiregvfrzrag>" },
+{ "Verdana, Arial, Helvetica,serif",
+ "</nqiregvfrzrag>" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jrozbaxrl Xvqf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Yrffbaf naq cebwrpgf sbe gur " },
+{ "Verdana, Arial, Helvetica,serif",
+ "arkg trarengvba bs Jro " },
+{ "Verdana, Arial, Helvetica,serif",
+ " Zntnmvar" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Oebjfr ol vffhr be gbcvp" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Sbhe lrnef bs UbgJverq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur Jro'f #1 Frnepu Ratvar" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arrq uryc?" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Pheerag Ubb-Un:" },
+{ "Verdana, Arial, Helvetica,serif",
+ " " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur Jveryrff Jbeyq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Tnqtrgf naq Tvmzbf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ZC3 Ebpxf gur Jro" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jbzra va Grpu" },
+{ "Verdana, Arial, Helvetica,serif",
+ "VCB Bhgybbx" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rkrphgvir Fhzznel" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jverq Arjf Enqvb" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur Jverq Vaqrk" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Penfu Pbhefrf va:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vasb. Nepuvgrpgher" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro Qrfvta" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ortvaavat WninFpevcg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nqinaprq WninFpevcg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fvgr Bcgvzvmngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qlanzvp UGZY" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbagvahbhf Hcqngrf sebz gur Qvtvgny Sebag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "02:00 nz " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fnsre Pnyyf, be Fryyvat Srne?" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Va jung znl or nabgure choyvp eryngvbaf punyyratr " },
+{ "Verdana, Arial, Helvetica,serif",
+ "sbe gur pryyhyne cubar vaqhfgel, n pbzcnal vf " },
+{ "Verdana, Arial, Helvetica,serif",
+ "znexrgvat n qrivpr vg fnlf jvyy cerirag pryy cubarf " },
+{ "Verdana, Arial, Helvetica,serif",
+ "sebz unezvat gur obql. Ol Ryvfn Ongvfgn." },
+{ "Verdana, Arial, Helvetica,serif",
+ "11:00 nz " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ohvyqvat n Orggre Onyybg Obk" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Navzngvbaf naq Zber:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Oebjfr Nyy Navzngvbaf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cyht-va Grfgre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fhozvffvba Thvqryvarf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Navzngvba Arjfyrggre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zl Obql Uhegf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "TVPX VV" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur Tneqra" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gur Jro Qrirybcre'f Erfbhepr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "CUC4 Vafgnyyngvba Bireivrj" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jurgure lbh'er n Jvaqbjf be Aba-Jvaqbjf glcr, " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Whyvr fubjf lbh rirelguvat lbh arrq gb xabj gb trg " },
+{ "Verdana, Arial, Helvetica,serif",
+ "CUC4 hc naq ehaavat hfvat Ncnpur nf gur onfr Jro " },
+{ "Verdana, Arial, Helvetica,serif",
+ "freire fbsgjner. " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Yrnea nobhg:" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Nqqvgvbany Rkuvovgf:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fvgr bs Ubhef" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ybpngvba, Ybpngvba..." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Eryvfgra: Cyrnfr!" },
+{ "serif",
+ "Zbmvyyn" },
+{ "serif",
+ "Pebff" },
+{ "serif",
+ "Ersrerapr:" },
+{ "serif",
+ "frnzbaxrl" },
+{ "serif",
+ "zbmvyyn" },
+{ "serif",
+ "/" },
+{ "serif",
+ "kcpbz" },
+{ "serif",
+ "qf" },
+{ "serif",
+ "afIbvqOGerr.pcc" },
+{ "serif",
+ "PIF" },
+{ "serif",
+ "Ybt" },
+{ "serif",
+ "Oynzr" },
+{ "serif",
+ "punatrf" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "guvf" },
+{ "serif",
+ "svyr" },
+{ "serif",
+ "va" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "ynfg:" },
+{ "serif",
+ "qnl" },
+{ "serif",
+ "jrrx" },
+{ "serif",
+ "zbagu" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "1" },
+{ "monospace",
+ "/* -*- Zbqr: P++; gno-jvqgu: 8; vaqrag-gnof-zbqr: avy; p-onfvp-bssfrg: 4 -*- */" },
+{ "monospace",
+ "2" },
+{ "monospace",
+ "/*" },
+{ "monospace",
+ "3" },
+{ "monospace",
+ " * Gur pbagragf bs guvf svyr ner fhowrpg gb gur Zbmvyyn Choyvp Yvprafr" },
+{ "monospace",
+ "4" },
+{ "monospace",
+ " * Irefvba 1.1 (gur \"ZCY\"); lbh znl abg hfr guvf svyr rkprcg va" },
+{ "monospace",
+ "5" },
+{ "monospace",
+ " * pbzcyvnapr jvgu gur ZCY. Lbh znl bognva n pbcl bs gur ZCY ng" },
+{ "monospace",
+ "6" },
+{ "monospace",
+ " * " },
+{ "monospace",
+ "uggc://jjj.zbmvyyn.bet/ZCY/" },
+{ "monospace",
+ "7" },
+{ "monospace",
+ " *" },
+{ "monospace",
+ "8" },
+{ "monospace",
+ " * Fbsgjner qvfgevohgrq haqre gur ZCY vf qvfgevohgrq ba na \"NF VF\" onfvf," },
+{ "monospace",
+ "9" },
+{ "monospace",
+ " * JVGUBHG JNEENAGL BS NAL XVAQ, rvgure rkcerff be vzcyvrq. Frr gur ZCY" },
+{ "monospace",
+ "10" },
+{ "monospace",
+ " * sbe gur fcrpvsvp ynathntr tbireavat evtugf naq yvzvgngvbaf haqre gur" },
+{ "monospace",
+ "11" },
+{ "monospace",
+ " * ZCY." },
+{ "monospace",
+ "12" },
+{ "monospace",
+ "13" },
+{ "monospace",
+ " * Gur Vavgvny Qrirybcre bs guvf pbqr haqre gur ZCY vf Argfpncr" },
+{ "monospace",
+ "14" },
+{ "monospace",
+ " * Pbzzhavpngvbaf Pbecbengvba. Cbegvbaf perngrq ol" },
+{ "monospace",
+ " * Pbzzhavpngvbaf Pbecbengvba. Cbegvbaf perngrq ol Argfpncr ner" },
+{ "monospace",
+ "15" },
+{ "monospace",
+ " * Pbclevtug (P) 1999 Argfpncr Pbzzhavpngvbaf Pbecbengvba. Nyy Evtugf" },
+{ "monospace",
+ "16" },
+{ "monospace",
+ " * Erfreirq." },
+{ "monospace",
+ "17" },
+{ "monospace",
+ "18" },
+{ "monospace",
+ " * Bevtvany Nhgube:" },
+{ "monospace",
+ "19" },
+{ "monospace",
+ " * Puevf Jngrefba <jngrefba@argfpncr.pbz>" },
+{ "monospace",
+ "20" },
+{ "monospace",
+ " */" },
+{ "monospace",
+ "21" },
+{ "monospace",
+ "22" },
+{ "monospace",
+ " #vapyhqr \"" },
+{ "monospace",
+ "afIbvqOGerr.u" },
+{ "monospace",
+ "\"" },
+{ "monospace",
+ "23" },
+{ "monospace",
+ "24" },
+{ "monospace",
+ " #vsqrs " },
+{ "monospace",
+ "QROHT" },
+{ "monospace",
+ "25" },
+{ "monospace",
+ " #vapyhqr <" },
+{ "monospace",
+ "fgqvb.u" },
+{ "monospace",
+ ">" },
+{ "monospace",
+ "26" },
+{ "monospace",
+ " #raqvs" },
+{ "monospace",
+ "27" },
+{ "monospace",
+ "28" },
+{ "monospace",
+ "// Frg guvf gb sbepr gur gerr gb or irevsvrq nsgre rirel vafregvba naq" },
+{ "monospace",
+ "29" },
+{ "monospace",
+ "// erzbiny." },
+{ "monospace",
+ "30" },
+{ "monospace",
+ "//#qrsvar CNENABVQ 1" },
+{ "monospace",
+ "31" },
+{ "monospace",
+ "32" },
+{ "monospace",
+ "33" },
+{ "monospace",
+ "//----------------------------------------------------------------------" },
+{ "monospace",
+ "34" },
+{ "monospace",
+ "// afIbvqOGerr::Abqr" },
+{ "monospace",
+ "35" },
+{ "monospace",
+ "//" },
+{ "monospace",
+ "36" },
+{ "monospace",
+ "// Vzcyrzragngvba zrgubqf" },
+{ "monospace",
+ "37" },
+{ "monospace",
+ "38" },
+{ "monospace",
+ "39" },
+{ "monospace",
+ "aferfhyg" },
+{ "monospace",
+ "40" },
+{ "monospace",
+ "afIbvqOGerr" },
+{ "monospace",
+ "::" },
+{ "monospace",
+ "Abqr" },
+{ "monospace",
+ "Perngr" },
+{ "monospace",
+ "(" },
+{ "monospace",
+ "Glcr" },
+{ "monospace",
+ "nGlcr" },
+{ "monospace",
+ ", " },
+{ "monospace",
+ "CEVag32" },
+{ "monospace",
+ " nPncnpvgl, " },
+{ "monospace",
+ "** " },
+{ "monospace",
+ "nErfhyg" },
+{ "monospace",
+ ")" },
+{ "monospace",
+ "41" },
+{ "monospace",
+ " {" },
+{ "monospace",
+ "42" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "// Fb jr bayl rire unir gb qb bar nyybpngvba sbe n Abqr, jr qb n" },
+{ "monospace",
+ "43" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "// \"anxrq\" urnc nyybpngvba, pbzchgvat gur fvmr bs gur abqr naq" },
+{ "monospace",
+ "44" },
+{ "monospace",
+ "// \"cnqqvat\" vg bhg fb gung vg pna ubyq nPncnpvgl fybgf." },
+{ "monospace",
+ "45" },
+{ "monospace",
+ " pune* " },
+{ "monospace",
+ "olgrf" },
+{ "monospace",
+ " = " },
+{ "monospace",
+ "arj" },
+{ "monospace",
+ " pune[fvmrbs(" },
+{ "monospace",
+ ") + (nPncnpvgl - 1) * fvmrbs(ibvq*)];" },
+{ "monospace",
+ "46" },
+{ "monospace",
+ " vs (! " },
+{ "monospace",
+ "47" },
+{ "monospace",
+ " erghea " },
+{ "monospace",
+ "AF_REEBE_BHG_BS_ZRZBEL" },
+{ "monospace",
+ ";" },
+{ "monospace",
+ "48" },
+{ "monospace",
+ "49" },
+{ "monospace",
+ "* " },
+{ "monospace",
+ "erfhyg" },
+{ "monospace",
+ "AF_ERVAGRECERG_PNFG" },
+{ "monospace",
+ "*, " },
+{ "monospace",
+ ");" },
+{ "monospace",
+ "50" },
+{ "monospace",
+ "->zOvgf = 0;" },
+{ "monospace",
+ "51" },
+{ "monospace",
+ "->" },
+{ "monospace",
+ "FrgGlcr" },
+{ "monospace",
+ "52" },
+{ "monospace",
+ "53" },
+{ "monospace",
+ " *" },
+{ "monospace",
+ "54" },
+{ "monospace",
+ " erghea " },
+{ "monospace",
+ "AF_BX" },
+{ "monospace",
+ "55" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "56" },
+{ "monospace",
+ "57" },
+{ "monospace",
+ "58" },
+{ "monospace",
+ "Qrfgebl" },
+{ "monospace",
+ "nAbqr" },
+{ "monospace",
+ "59" },
+{ "monospace",
+ "60" },
+{ "monospace",
+ " pune* " },
+{ "monospace",
+ "(pune*, " },
+{ "monospace",
+ "61" },
+{ "monospace",
+ "qryrgr" },
+{ "monospace",
+ "[] " },
+{ "monospace",
+ "62" },
+{ "monospace",
+ "63" },
+{ "monospace",
+ "64" },
+{ "monospace",
+ "65" },
+{ "monospace",
+ " ibvq" },
+{ "monospace",
+ "66" },
+{ "monospace",
+ "VafregRyrzragNg" },
+{ "monospace",
+ "(ibvq* " },
+{ "monospace",
+ "nRyrzrag" },
+{ "monospace",
+ "nVaqrk" },
+{ "monospace",
+ "67" },
+{ "monospace",
+ "68" },
+{ "monospace",
+ "AF_CERPBAQVGVBA" },
+{ "monospace",
+ " >= 0 && " },
+{ "monospace",
+ " <= " },
+{ "monospace",
+ "TrgPbhag" },
+{ "monospace",
+ "(), " },
+{ "monospace",
+ "\"onq vaqrk\"" },
+{ "monospace",
+ "69" },
+{ "monospace",
+ "70" },
+{ "monospace",
+ "pbhag" },
+{ "monospace",
+ "();" },
+{ "monospace",
+ "71" },
+{ "monospace",
+ " FrgPbhag(" },
+{ "monospace",
+ " + 1);" },
+{ "monospace",
+ "72" },
+{ "monospace",
+ "73" },
+{ "monospace",
+ " juvyr (" },
+{ "monospace",
+ " > " },
+{ "monospace",
+ ") {" },
+{ "monospace",
+ "74" },
+{ "monospace",
+ " zQngn[" },
+{ "monospace",
+ "] = zQngn[" },
+{ "monospace",
+ " - 1];" },
+{ "monospace",
+ "75" },
+{ "monospace",
+ " --" },
+{ "monospace",
+ "76" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "77" },
+{ "monospace",
+ "78" },
+{ "monospace",
+ " zQngn[" },
+{ "monospace",
+ "] = " },
+{ "monospace",
+ "79" },
+{ "monospace",
+ "80" },
+{ "monospace",
+ "81" },
+{ "monospace",
+ "82" },
+{ "monospace",
+ "ErzbirRyrzragNg" },
+{ "monospace",
+ "83" },
+{ "monospace",
+ "84" },
+{ "monospace",
+ " < " },
+{ "monospace",
+ "85" },
+{ "monospace",
+ "86" },
+{ "monospace",
+ "87" },
+{ "monospace",
+ " - 1);" },
+{ "monospace",
+ "88" },
+{ "monospace",
+ "89" },
+{ "monospace",
+ "90" },
+{ "monospace",
+ " + 1];" },
+{ "monospace",
+ "91" },
+{ "monospace",
+ " ++" },
+{ "monospace",
+ "92" },
+{ "monospace",
+ "93" },
+{ "monospace",
+ "94" },
+{ "monospace",
+ "95" },
+{ "monospace",
+ "96" },
+{ "monospace",
+ "97" },
+{ "monospace",
+ "98" },
+{ "monospace",
+ "// afIbvqOGerr::Cngu" },
+{ "monospace",
+ "99" },
+{ "monospace",
+ "100" },
+{ "monospace",
+ "101" },
+{ "monospace",
+ "102" },
+{ "monospace",
+ "103" },
+{ "monospace",
+ "Cngu" },
+{ "monospace",
+ "(pbafg " },
+{ "monospace",
+ "& " },
+{ "monospace",
+ "nBgure" },
+{ "monospace",
+ "104" },
+{ "monospace",
+ " : zGbc(" },
+{ "monospace",
+ ".zGbc)" },
+{ "monospace",
+ "105" },
+{ "monospace",
+ "106" },
+{ "monospace",
+ " sbe (" },
+{ "monospace",
+ "v" },
+{ "monospace",
+ " = 0; " },
+{ "monospace",
+ " < zGbc; ++" },
+{ "monospace",
+ "107" },
+{ "monospace",
+ " zYvax[" },
+{ "monospace",
+ ".zYvax[" },
+{ "monospace",
+ "];" },
+{ "monospace",
+ "108" },
+{ "monospace",
+ "109" },
+{ "monospace",
+ "110" },
+{ "monospace",
+ "&" },
+{ "monospace",
+ "111" },
+{ "monospace",
+ "::bcrengbe=(pbafg " },
+{ "monospace",
+ "112" },
+{ "monospace",
+ "113" },
+{ "monospace",
+ " zGbc = " },
+{ "monospace",
+ ".zGbc;" },
+{ "monospace",
+ "114" },
+{ "monospace",
+ "115" },
+{ "monospace",
+ "116" },
+{ "monospace",
+ " erghea *" },
+{ "monospace",
+ "guvf" },
+{ "monospace",
+ "117" },
+{ "monospace",
+ "118" },
+{ "monospace",
+ "119" },
+{ "monospace",
+ " vayvar " },
+{ "monospace",
+ "120" },
+{ "monospace",
+ "Chfu" },
+{ "monospace",
+ "121" },
+{ "monospace",
+ "122" },
+{ "monospace",
+ "// KKK Vs lbh biresybj guvf guvat, guvax nobhg znxvat ynetre vaqrk" },
+{ "monospace",
+ "123" },
+{ "monospace",
+ "// be qngn abqrf. Lbh pna cnpx n _ybg_ bs qngn vagb n cerggl syng" },
+{ "monospace",
+ "124" },
+{ "monospace",
+ "// gerr." },
+{ "monospace",
+ "125" },
+{ "monospace",
+ "(zGbc <= xZnkQrcgu, " },
+{ "monospace",
+ "\"biresybj\"" },
+{ "monospace",
+ "126" },
+{ "monospace",
+ " vs (zGbc > xZnkQrcgu)" },
+{ "monospace",
+ "127" },
+{ "monospace",
+ "128" },
+{ "monospace",
+ "129" },
+{ "monospace",
+ " zYvax[zGbc].zAbqr = " },
+{ "monospace",
+ "130" },
+{ "monospace",
+ " zYvax[zGbc]." },
+{ "monospace",
+ "zVaqrk" },
+{ "monospace",
+ "131" },
+{ "monospace",
+ " ++zGbc;" },
+{ "monospace",
+ "132" },
+{ "monospace",
+ "133" },
+{ "monospace",
+ "134" },
+{ "monospace",
+ "135" },
+{ "monospace",
+ "136" },
+{ "monospace",
+ "137" },
+{ "monospace",
+ " vayvar ibvq" },
+{ "monospace",
+ "138" },
+{ "monospace",
+ "Cbc" },
+{ "monospace",
+ "139" },
+{ "monospace",
+ "140" },
+{ "monospace",
+ " --zGbc;" },
+{ "monospace",
+ "141" },
+{ "monospace",
+ " = zYvax[zGbc].zAbqr;" },
+{ "monospace",
+ "142" },
+{ "monospace",
+ " = zYvax[zGbc]." },
+{ "monospace",
+ "143" },
+{ "monospace",
+ "144" },
+{ "monospace",
+ "145" },
+{ "monospace",
+ "146" },
+{ "monospace",
+ "147" },
+{ "monospace",
+ "// afIbvqOGerr zrgubqf" },
+{ "monospace",
+ "148" },
+{ "monospace",
+ "149" },
+{ "monospace",
+ "150" },
+{ "monospace",
+ "151" },
+{ "monospace",
+ "152" },
+{ "monospace",
+ " PbafgVgrengbe " },
+{ "monospace",
+ "ynfg" },
+{ "monospace",
+ "." },
+{ "monospace",
+ "Ynfg" },
+{ "monospace",
+ "153" },
+{ "monospace",
+ " sbe (PbafgVgrengbe ryrzrag = " },
+{ "monospace",
+ "Svefg" },
+{ "monospace",
+ "(); ryrzrag != " },
+{ "monospace",
+ "; ++ryrzrag)" },
+{ "monospace",
+ "154" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "NccraqRyrzrag" },
+{ "monospace",
+ "(*ryrzrag);" },
+{ "monospace",
+ "155" },
+{ "monospace",
+ "156" },
+{ "monospace",
+ "157" },
+{ "monospace",
+ "158" },
+{ "monospace",
+ "159" },
+{ "monospace",
+ "160" },
+{ "monospace",
+ "Pyrne" },
+{ "monospace",
+ "161" },
+{ "monospace",
+ "162" },
+{ "monospace",
+ "163" },
+{ "monospace",
+ "164" },
+{ "monospace",
+ "165" },
+{ "monospace",
+ "166" },
+{ "monospace",
+ "167" },
+{ "monospace",
+ "168" },
+{ "monospace",
+ "Pbhag" },
+{ "monospace",
+ "() pbafg" },
+{ "monospace",
+ "169" },
+{ "monospace",
+ "170" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ "VfRzcgl" },
+{ "monospace",
+ "())" },
+{ "monospace",
+ "171" },
+{ "monospace",
+ " erghea 0;" },
+{ "monospace",
+ "172" },
+{ "monospace",
+ "173" },
+{ "monospace",
+ " vs (VfFvatyrRyrzrag())" },
+{ "monospace",
+ "174" },
+{ "monospace",
+ " erghea 1;" },
+{ "monospace",
+ "175" },
+{ "monospace",
+ "176" },
+{ "monospace",
+ "* ebbg = " },
+{ "monospace",
+ "*, zEbbg & xEbbg_CbvagreZnfx);" },
+{ "monospace",
+ "177" },
+{ "monospace",
+ " erghea ebbg->TrgFhoGerrFvmr();" },
+{ "monospace",
+ "178" },
+{ "monospace",
+ "179" },
+{ "monospace",
+ "180" },
+{ "monospace",
+ " ibvq*" },
+{ "monospace",
+ "181" },
+{ "monospace",
+ "RyrzragNg" },
+{ "monospace",
+ ") pbafg" },
+{ "monospace",
+ "182" },
+{ "monospace",
+ "183" },
+{ "monospace",
+ " < 0 || " },
+{ "monospace",
+ " >= " },
+{ "monospace",
+ "184" },
+{ "monospace",
+ "afahyy" },
+{ "monospace",
+ "185" },
+{ "monospace",
+ "186" },
+{ "monospace",
+ "187" },
+{ "monospace",
+ "(ibvq*, zEbbg & xEbbg_CbvagreZnfx);" },
+{ "monospace",
+ "188" },
+{ "monospace",
+ "189" },
+{ "monospace",
+ "pheerag" },
+{ "monospace",
+ "190" },
+{ "monospace",
+ "TrgGlcr" },
+{ "monospace",
+ "() != " },
+{ "monospace",
+ "::rGlcr_Qngn) {" },
+{ "monospace",
+ "191" },
+{ "monospace",
+ "// Jr'er fgvyy va gur vaqrk. Svaq gur evtug yrns." },
+{ "monospace",
+ "192" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "arkg" },
+{ "monospace",
+ "193" },
+{ "monospace",
+ "194" },
+{ "monospace",
+ "195" },
+{ "monospace",
+ " sbe (" },
+{ "monospace",
+ "; ++" },
+{ "monospace",
+ "196" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "* puvyq = " },
+{ "monospace",
+ "TrgRyrzragNg" },
+{ "monospace",
+ "));" },
+{ "monospace",
+ "197" },
+{ "monospace",
+ "198" },
+{ "monospace",
+ " puvyqpbhag = puvyq->TrgFhoGerrFvmr();" },
+{ "monospace",
+ "199" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ ") < puvyqpbhag) {" },
+{ "monospace",
+ "200" },
+{ "monospace",
+ " " },
+{ "monospace",
+ " = puvyq;" },
+{ "monospace",
+ "201" },
+{ "monospace",
+ " oernx;" },
+{ "monospace",
+ "202" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "203" },
+{ "monospace",
+ "204" },
+{ "monospace",
+ " -= puvyqpbhag;" },
+{ "monospace",
+ "205" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "206" },
+{ "monospace",
+ "207" },
+{ "monospace",
+ " vs (! " },
+{ "monospace",
+ "208" },
+{ "monospace",
+ "AF_REEBE" },
+{ "monospace",
+ "\"pbeehcgrq\"" },
+{ "monospace",
+ "209" },
+{ "monospace",
+ " erghea " },
+{ "monospace",
+ "210" },
+{ "monospace",
+ "211" },
+{ "monospace",
+ "212" },
+{ "monospace",
+ "213" },
+{ "monospace",
+ "214" },
+{ "monospace",
+ "215" },
+{ "monospace",
+ "216" },
+{ "monospace",
+ "217" },
+{ "monospace",
+ "218" },
+{ "monospace",
+ "219" },
+{ "monospace",
+ "220" },
+{ "monospace",
+ "VaqrkBs" },
+{ "monospace",
+ "(ibvq* nCbffvoyrRyrzrag) pbafg" },
+{ "monospace",
+ "221" },
+{ "monospace",
+ "222" },
+{ "monospace",
+ "((" },
+{ "monospace",
+ "CEJbeq" },
+{ "monospace",
+ "(nCbffvoyrRyrzrag) & ~xEbbg_CbvagreZnfx) == 0," },
+{ "monospace",
+ "223" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "\"hu bu, fbzrbar jnagf gb hfr gur cbvagre ovgf\"" },
+{ "monospace",
+ "224" },
+{ "monospace",
+ "225" },
+{ "monospace",
+ "(nCbffvoyrRyrzrag != " },
+{ "monospace",
+ "\"afIbvqOGerr pna'g unaqyr ahyy ryrzragf\"" },
+{ "monospace",
+ "226" },
+{ "monospace",
+ " vs (nCbffvoyrRyrzrag == " },
+{ "monospace",
+ "227" },
+{ "monospace",
+ " erghea -1;" },
+{ "monospace",
+ "228" },
+{ "monospace",
+ "229" },
+{ "monospace",
+ " = 0;" },
+{ "monospace",
+ "230" },
+{ "monospace",
+ "231" },
+{ "monospace",
+ "; ++ryrzrag, ++" },
+{ "monospace",
+ "232" },
+{ "monospace",
+ " vs (nCbffvoyrRyrzrag == *ryrzrag)" },
+{ "monospace",
+ "233" },
+{ "monospace",
+ "234" },
+{ "monospace",
+ "235" },
+{ "monospace",
+ "236" },
+{ "monospace",
+ " erghea -1;" },
+{ "monospace",
+ "237" },
+{ "monospace",
+ "238" },
+{ "monospace",
+ "239" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "240" },
+{ "monospace",
+ "CEObby" },
+{ "monospace",
+ "241" },
+{ "monospace",
+ "242" },
+{ "monospace",
+ "243" },
+{ "monospace",
+ ") & ~xEbbg_CbvagreZnfx) == 0," },
+{ "monospace",
+ "244" },
+{ "monospace",
+ "245" },
+{ "monospace",
+ "246" },
+{ "monospace",
+ " vs ((" },
+{ "monospace",
+ ") & ~xEbbg_CbvagreZnfx) != 0)" },
+{ "monospace",
+ "247" },
+{ "monospace",
+ "CE_SNYFR" },
+{ "monospace",
+ "248" },
+{ "monospace",
+ "249" },
+{ "monospace",
+ " != " },
+{ "monospace",
+ "250" },
+{ "monospace",
+ " == " },
+{ "monospace",
+ "251" },
+{ "monospace",
+ "252" },
+{ "monospace",
+ "253" },
+{ "monospace",
+ "254" },
+{ "monospace",
+ "255" },
+{ "monospace",
+ "256" },
+{ "monospace",
+ "257" },
+{ "monospace",
+ "258" },
+{ "monospace",
+ "ei" },
+{ "monospace",
+ "259" },
+{ "monospace",
+ "260" },
+{ "monospace",
+ " vs (VfFvatyrRyrzrag()) {" },
+{ "monospace",
+ "261" },
+{ "monospace",
+ "// Jr'er bayl n fvatyr ryrzrag ubyqre, naq unira'g lrg" },
+{ "monospace",
+ "262" },
+{ "monospace",
+ "// \"snhygrq\" gb perngr gur ogerr." },
+{ "monospace",
+ "263" },
+{ "monospace",
+ "264" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ " == 0) {" },
+{ "monospace",
+ "265" },
+{ "monospace",
+ "// Vs jr unir *ab* ryrzragf, gura whfg frg gur ebbg" },
+{ "monospace",
+ "266" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "// cbvagre naq jr'er qbar." },
+{ "monospace",
+ "267" },
+{ "monospace",
+ " zEbbg = " },
+{ "monospace",
+ "268" },
+{ "monospace",
+ "CE_GEHR" },
+{ "monospace",
+ "269" },
+{ "monospace",
+ "270" },
+{ "monospace",
+ "271" },
+{ "monospace",
+ "// Vs jr nyernql unq na ryrzrag, naq abj jr'er nqqvat" },
+{ "monospace",
+ "272" },
+{ "monospace",
+ "// nabgure. Snhyg naq fgneg perngvat gur ogerr." },
+{ "monospace",
+ "273" },
+{ "monospace",
+ " ibvq* ryrzrag = " },
+{ "monospace",
+ "274" },
+{ "monospace",
+ "275" },
+{ "monospace",
+ "* arjebbg;" },
+{ "monospace",
+ "276" },
+{ "monospace",
+ "::rGlcr_Qngn, xQngnPncnpvgl, &arjebbg);" },
+{ "monospace",
+ "277" },
+{ "monospace",
+ "AF_SNVYRQ" },
+{ "monospace",
+ ")) erghea " },
+{ "monospace",
+ "278" },
+{ "monospace",
+ "279" },
+{ "monospace",
+ " arjebbg->" },
+{ "monospace",
+ "(ryrzrag, 0);" },
+{ "monospace",
+ "280" },
+{ "monospace",
+ " arjebbg->FrgFhoGerrFvmr(1);" },
+{ "monospace",
+ "281" },
+{ "monospace",
+ " FrgEbbg(arjebbg);" },
+{ "monospace",
+ "282" },
+{ "monospace",
+ "283" },
+{ "monospace",
+ "284" },
+{ "monospace",
+ "cngu" },
+{ "monospace",
+ "285" },
+{ "monospace",
+ "286" },
+{ "monospace",
+ "287" },
+{ "monospace",
+ "288" },
+{ "monospace",
+ "289" },
+{ "monospace",
+ "290" },
+{ "monospace",
+ "291" },
+{ "monospace",
+ "292" },
+{ "monospace",
+ "293" },
+{ "monospace",
+ "294" },
+{ "monospace",
+ "295" },
+{ "monospace",
+ "296" },
+{ "monospace",
+ ") <= puvyqpbhag) {" },
+{ "monospace",
+ "297" },
+{ "monospace",
+ "298" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ "299" },
+{ "monospace",
+ "300" },
+{ "monospace",
+ "301" },
+{ "monospace",
+ "302" },
+{ "monospace",
+ "303" },
+{ "monospace",
+ "304" },
+{ "monospace",
+ "305" },
+{ "monospace",
+ "306" },
+{ "monospace",
+ "307" },
+{ "monospace",
+ "308" },
+{ "monospace",
+ "309" },
+{ "monospace",
+ "310" },
+{ "monospace",
+ "311" },
+{ "monospace",
+ "312" },
+{ "monospace",
+ "313" },
+{ "monospace",
+ "314" },
+{ "monospace",
+ "315" },
+{ "monospace",
+ "() >= xQngnPncnpvgl) {" },
+{ "monospace",
+ "316" },
+{ "monospace",
+ "// Jr whfg oyrj gur qngn abqr'f ohssre. Perngr nabgure" },
+{ "monospace",
+ "317" },
+{ "monospace",
+ "// qngnabqr naq fcyvg." },
+{ "monospace",
+ "318" },
+{ "monospace",
+ "Fcyvg" },
+{ "monospace",
+ "319" },
+{ "monospace",
+ "320" },
+{ "monospace",
+ "321" },
+{ "monospace",
+ " ryfr {" },
+{ "monospace",
+ "322" },
+{ "monospace",
+ "323" },
+{ "monospace",
+ "->FrgFhoGerrFvmr(" },
+{ "monospace",
+ "->TrgFhoGerrFvmr() + 1);" },
+{ "monospace",
+ "324" },
+{ "monospace",
+ "325" },
+{ "monospace",
+ "326" },
+{ "monospace",
+ "Yratgu" },
+{ "monospace",
+ "() > 0) {" },
+{ "monospace",
+ "327" },
+{ "monospace",
+ "vaqrk" },
+{ "monospace",
+ "328" },
+{ "monospace",
+ "(&" },
+{ "monospace",
+ ", &" },
+{ "monospace",
+ "329" },
+{ "monospace",
+ "330" },
+{ "monospace",
+ "331" },
+{ "monospace",
+ "332" },
+{ "monospace",
+ "CNENABVQ" },
+{ "monospace",
+ "333" },
+{ "monospace",
+ "Irevsl" },
+{ "monospace",
+ "*, zEbbg & xEbbg_CbvagreZnfx));" },
+{ "monospace",
+ "334" },
+{ "monospace",
+ "335" },
+{ "monospace",
+ "336" },
+{ "monospace",
+ "337" },
+{ "monospace",
+ "338" },
+{ "monospace",
+ "339" },
+{ "monospace",
+ "340" },
+{ "monospace",
+ "ErcynprRyrzragNg" },
+{ "monospace",
+ "341" },
+{ "monospace",
+ "342" },
+{ "monospace",
+ "343" },
+{ "monospace",
+ "344" },
+{ "monospace",
+ "345" },
+{ "monospace",
+ "346" },
+{ "monospace",
+ "347" },
+{ "monospace",
+ "348" },
+{ "monospace",
+ "349" },
+{ "monospace",
+ "350" },
+{ "monospace",
+ "351" },
+{ "monospace",
+ "352" },
+{ "monospace",
+ "353" },
+{ "monospace",
+ "354" },
+{ "monospace",
+ "355" },
+{ "monospace",
+ "356" },
+{ "monospace",
+ " zEbbg = " },
+{ "monospace",
+ "357" },
+{ "monospace",
+ "358" },
+{ "monospace",
+ "359" },
+{ "monospace",
+ "360" },
+{ "monospace",
+ "361" },
+{ "monospace",
+ "362" },
+{ "monospace",
+ "363" },
+{ "monospace",
+ "364" },
+{ "monospace",
+ "365" },
+{ "monospace",
+ "366" },
+{ "monospace",
+ "367" },
+{ "monospace",
+ "368" },
+{ "monospace",
+ "369" },
+{ "monospace",
+ "370" },
+{ "monospace",
+ "371" },
+{ "monospace",
+ "372" },
+{ "monospace",
+ "373" },
+{ "monospace",
+ "374" },
+{ "monospace",
+ "375" },
+{ "monospace",
+ "376" },
+{ "monospace",
+ "377" },
+{ "monospace",
+ "378" },
+{ "monospace",
+ "379" },
+{ "monospace",
+ "380" },
+{ "monospace",
+ "381" },
+{ "monospace",
+ "382" },
+{ "monospace",
+ "383" },
+{ "monospace",
+ "384" },
+{ "monospace",
+ "385" },
+{ "monospace",
+ "386" },
+{ "monospace",
+ "FrgRyrzragNg" },
+{ "monospace",
+ "387" },
+{ "monospace",
+ "388" },
+{ "monospace",
+ "389" },
+{ "monospace",
+ "390" },
+{ "monospace",
+ "391" },
+{ "monospace",
+ "ErzbirRyrzrag" },
+{ "monospace",
+ "392" },
+{ "monospace",
+ "393" },
+{ "monospace",
+ "394" },
+{ "monospace",
+ " erghea (" },
+{ "monospace",
+ " >= 0) ? " },
+{ "monospace",
+ ") : " },
+{ "monospace",
+ "395" },
+{ "monospace",
+ "396" },
+{ "monospace",
+ "397" },
+{ "monospace",
+ "398" },
+{ "monospace",
+ "399" },
+{ "monospace",
+ "400" },
+{ "monospace",
+ "401" },
+{ "monospace",
+ "402" },
+{ "monospace",
+ "403" },
+{ "monospace",
+ "404" },
+{ "monospace",
+ "405" },
+{ "monospace",
+ "406" },
+{ "monospace",
+ "// Jr'er erzbivat gur bar naq bayl ryrzrag" },
+{ "monospace",
+ "407" },
+{ "monospace",
+ " zEbbg = 0;" },
+{ "monospace",
+ "408" },
+{ "monospace",
+ "409" },
+{ "monospace",
+ "410" },
+{ "monospace",
+ "411" },
+{ "monospace",
+ "// Jr'ir tbg zber guna bar ryrzrag, naq jr'er erzbivat vg." },
+{ "monospace",
+ "412" },
+{ "monospace",
+ "413" },
+{ "monospace",
+ "414" },
+{ "monospace",
+ "415" },
+{ "monospace",
+ "416" },
+{ "monospace",
+ "417" },
+{ "monospace",
+ " = ebbg;" },
+{ "monospace",
+ "418" },
+{ "monospace",
+ "419" },
+{ "monospace",
+ "420" },
+{ "monospace",
+ "421" },
+{ "monospace",
+ "422" },
+{ "monospace",
+ "423" },
+{ "monospace",
+ "424" },
+{ "monospace",
+ "425" },
+{ "monospace",
+ "426" },
+{ "monospace",
+ "427" },
+{ "monospace",
+ "428" },
+{ "monospace",
+ "429" },
+{ "monospace",
+ "430" },
+{ "monospace",
+ "431" },
+{ "monospace",
+ "432" },
+{ "monospace",
+ "433" },
+{ "monospace",
+ "434" },
+{ "monospace",
+ "435" },
+{ "monospace",
+ "436" },
+{ "monospace",
+ "437" },
+{ "monospace",
+ "438" },
+{ "monospace",
+ "439" },
+{ "monospace",
+ "440" },
+{ "monospace",
+ "441" },
+{ "monospace",
+ "442" },
+{ "monospace",
+ "443" },
+{ "monospace",
+ "444" },
+{ "monospace",
+ "445" },
+{ "monospace",
+ "446" },
+{ "monospace",
+ "447" },
+{ "monospace",
+ "448" },
+{ "monospace",
+ " juvyr ((" },
+{ "monospace",
+ "() == 0) && (" },
+{ "monospace",
+ " != ebbg)) {" },
+{ "monospace",
+ "449" },
+{ "monospace",
+ "qbbzrq" },
+{ "monospace",
+ "450" },
+{ "monospace",
+ "451" },
+{ "monospace",
+ "452" },
+{ "monospace",
+ "453" },
+{ "monospace",
+ "454" },
+{ "monospace",
+ "455" },
+{ "monospace",
+ "456" },
+{ "monospace",
+ "457" },
+{ "monospace",
+ "458" },
+{ "monospace",
+ "->TrgFhoGerrFvmr() - 1);" },
+{ "monospace",
+ "459" },
+{ "monospace",
+ "460" },
+{ "monospace",
+ "461" },
+{ "monospace",
+ "462" },
+{ "monospace",
+ "463" },
+{ "monospace",
+ "464" },
+{ "monospace",
+ "465" },
+{ "monospace",
+ "466" },
+{ "monospace",
+ " juvyr ((ebbg->" },
+{ "monospace",
+ "() == " },
+{ "monospace",
+ "::rGlcr_Vaqrk) && (ebbg->" },
+{ "monospace",
+ "() == 1)) {" },
+{ "monospace",
+ "467" },
+{ "monospace",
+ "468" },
+{ "monospace",
+ " ebbg = " },
+{ "monospace",
+ "*, ebbg->" },
+{ "monospace",
+ "(0));" },
+{ "monospace",
+ "469" },
+{ "monospace",
+ " FrgEbbg(ebbg);" },
+{ "monospace",
+ "470" },
+{ "monospace",
+ "471" },
+{ "monospace",
+ "472" },
+{ "monospace",
+ "473" },
+{ "monospace",
+ "474" },
+{ "monospace",
+ "(ebbg);" },
+{ "monospace",
+ "475" },
+{ "monospace",
+ "476" },
+{ "monospace",
+ "477" },
+{ "monospace",
+ "478" },
+{ "monospace",
+ "479" },
+{ "monospace",
+ "480" },
+{ "monospace",
+ "481" },
+{ "monospace",
+ "(ibvq)" },
+{ "monospace",
+ "482" },
+{ "monospace",
+ "483" },
+{ "monospace",
+ "484" },
+{ "monospace",
+ " erghea;" },
+{ "monospace",
+ "485" },
+{ "monospace",
+ "486" },
+{ "monospace",
+ " vs (! VfFvatyrRyrzrag()) {" },
+{ "monospace",
+ "487" },
+{ "monospace",
+ "488" },
+{ "monospace",
+ "489" },
+{ "monospace",
+ "490" },
+{ "monospace",
+ "Qhzc" },
+{ "monospace",
+ "(ebbg, 0);" },
+{ "monospace",
+ "491" },
+{ "monospace",
+ "492" },
+{ "monospace",
+ "493" },
+{ "monospace",
+ "QrfgeblFhogerr" },
+{ "monospace",
+ "494" },
+{ "monospace",
+ "495" },
+{ "monospace",
+ "496" },
+{ "monospace",
+ " zEbbg = 0;" },
+{ "monospace",
+ "497" },
+{ "monospace",
+ "498" },
+{ "monospace",
+ "499" },
+{ "monospace",
+ "500" },
+{ "monospace",
+ "501" },
+{ "monospace",
+ "Pbzcnpg" },
+{ "monospace",
+ "502" },
+{ "monospace",
+ "503" },
+{ "monospace",
+ "// KKK Jr pbhyq tb guebhtu naq gel gb zretr qngnabqrf." },
+{ "monospace",
+ "504" },
+{ "monospace",
+ "}" },
+{ "monospace",
+ "505" },
+{ "monospace",
+ "506" },
+{ "monospace",
+ "507" },
+{ "monospace",
+ "RahzrengrSbejneqf" },
+{ "monospace",
+ "(RahzShap " },
+{ "monospace",
+ "nShap" },
+{ "monospace",
+ ", ibvq* " },
+{ "monospace",
+ "nQngn" },
+{ "monospace",
+ "508" },
+{ "monospace",
+ "509" },
+{ "monospace",
+ " ehaavat = " },
+{ "monospace",
+ "510" },
+{ "monospace",
+ "511" },
+{ "monospace",
+ "512" },
+{ "monospace",
+ "(); ehaavat && ryrzrag != " },
+{ "monospace",
+ "513" },
+{ "monospace",
+ " ehaavat = (*" },
+{ "monospace",
+ ")(*ryrzrag, " },
+{ "monospace",
+ "514" },
+{ "monospace",
+ "515" },
+{ "monospace",
+ " erghea ehaavat;" },
+{ "monospace",
+ "516" },
+{ "monospace",
+ "517" },
+{ "monospace",
+ "518" },
+{ "monospace",
+ "519" },
+{ "monospace",
+ "RahzrengrOnpxjneqf" },
+{ "monospace",
+ "520" },
+{ "monospace",
+ "521" },
+{ "monospace",
+ "522" },
+{ "monospace",
+ "523" },
+{ "monospace",
+ " PbafgVgrengbe ryrzrag = " },
+{ "monospace",
+ "524" },
+{ "monospace",
+ "svefg" },
+{ "monospace",
+ "525" },
+{ "monospace",
+ "526" },
+{ "monospace",
+ " vs (ryrzrag != " },
+{ "monospace",
+ "527" },
+{ "monospace",
+ " qb {" },
+{ "monospace",
+ "528" },
+{ "monospace",
+ " ehaavat = (*" },
+{ "monospace",
+ ")(*--ryrzrag, " },
+{ "monospace",
+ "529" },
+{ "monospace",
+ " } juvyr (ehaavat && ryrzrag != " },
+{ "monospace",
+ "530" },
+{ "monospace",
+ "531" },
+{ "monospace",
+ "532" },
+{ "monospace",
+ "533" },
+{ "monospace",
+ "534" },
+{ "monospace",
+ "535" },
+{ "monospace",
+ "536" },
+{ "monospace",
+ "537" },
+{ "monospace",
+ "FvmrBs" },
+{ "monospace",
+ "afVFvmrBsUnaqyre" },
+{ "monospace",
+ "nUnaqyre" },
+{ "monospace",
+ "CEHvag32" },
+{ "monospace",
+ "538" },
+{ "monospace",
+ "539" },
+{ "monospace",
+ "540" },
+{ "monospace",
+ "541" },
+{ "monospace",
+ "542" },
+{ "monospace",
+ " = fvmrbs(*" },
+{ "monospace",
+ "543" },
+{ "monospace",
+ "544" },
+{ "monospace",
+ "545" },
+{ "monospace",
+ "546" },
+{ "monospace",
+ "547" },
+{ "monospace",
+ "548" },
+{ "monospace",
+ "*, zEbbg & xEbbg_CbvagreZnfx), 0);" },
+{ "monospace",
+ "549" },
+{ "monospace",
+ "550" },
+{ "monospace",
+ "()) {" },
+{ "monospace",
+ "551" },
+{ "monospace",
+ "552" },
+{ "monospace",
+ "553" },
+{ "monospace",
+ "554" },
+{ "monospace",
+ "555" },
+{ "monospace",
+ "556" },
+{ "monospace",
+ " *" },
+{ "monospace",
+ " += fvmrbs(" },
+{ "monospace",
+ ") + (fvmrbs(ibvq*) * (xQngnPncnpvgl - 1));" },
+{ "monospace",
+ "557" },
+{ "monospace",
+ "558" },
+{ "monospace",
+ " ryfr {" },
+{ "monospace",
+ "559" },
+{ "monospace",
+ ") + (fvmrbs(ibvq*) * (xVaqrkPncnpvgl - 1));" },
+{ "monospace",
+ "560" },
+{ "monospace",
+ "561" },
+{ "monospace",
+ "// Vs jr'er va na vaqrk abqr, naq gurer ner fgvyy xvqf gb" },
+{ "monospace",
+ "562" },
+{ "monospace",
+ "// genirefr, jryy, genirefr 'rz." },
+{ "monospace",
+ "563" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ "564" },
+{ "monospace",
+ "565" },
+{ "monospace",
+ "AF_FGNGVP_PNFG" },
+{ "monospace",
+ ")), 0);" },
+{ "monospace",
+ "566" },
+{ "monospace",
+ "567" },
+{ "monospace",
+ "568" },
+{ "monospace",
+ "569" },
+{ "monospace",
+ "570" },
+{ "monospace",
+ "571" },
+{ "monospace",
+ "572" },
+{ "monospace",
+ "573" },
+{ "monospace",
+ "574" },
+{ "monospace",
+ "* nByqAbqr, ibvq* nRyrzragGbVafreg, " },
+{ "monospace",
+ " nFcyvgVaqrk)" },
+{ "monospace",
+ "575" },
+{ "monospace",
+ "576" },
+{ "monospace",
+ "577" },
+{ "monospace",
+ "578" },
+{ "monospace",
+ " pncnpvgl = (nByqAbqr->" },
+{ "monospace",
+ "::rGlcr_Qngn) ? xQngnPncnpvgl : xVaqrkPncnpvgl;" },
+{ "monospace",
+ "579" },
+{ "monospace",
+ "qrygn" },
+{ "monospace",
+ "580" },
+{ "monospace",
+ "581" },
+{ "monospace",
+ "582" },
+{ "monospace",
+ "* arjabqr;" },
+{ "monospace",
+ "583" },
+{ "monospace",
+ "(nByqAbqr->" },
+{ "monospace",
+ "(), pncnpvgl, &arjabqr);" },
+{ "monospace",
+ "584" },
+{ "monospace",
+ "585" },
+{ "monospace",
+ "586" },
+{ "monospace",
+ " vs (nFcyvgVaqrk == pncnpvgl) {" },
+{ "monospace",
+ "587" },
+{ "monospace",
+ "// Vs nFcyvgVaqrk vf gur fnzr nf gur pncnpvgl bs gur abqr," },
+{ "monospace",
+ "588" },
+{ "monospace",
+ "// gura gurer'yy or abguvat gb pbcl sebz gur byq abqr gb gur" },
+{ "monospace",
+ "589" },
+{ "monospace",
+ "// arj abqr, naq gur ryrzrag vf ernyyl zrnag gb or vafregrq va" },
+{ "monospace",
+ "590" },
+{ "monospace",
+ "// gur arjabqr. Va gung pnfr, qb vg _abj_ fb gung arjabqr'f" },
+{ "monospace",
+ "591" },
+{ "monospace",
+ "// fhogerr fvmr jvyy or pbeerpg." },
+{ "monospace",
+ "592" },
+{ "monospace",
+ " arjabqr->" },
+{ "monospace",
+ "(nRyrzragGbVafreg, 0);" },
+{ "monospace",
+ "593" },
+{ "monospace",
+ "594" },
+{ "monospace",
+ " vs (arjabqr->" },
+{ "monospace",
+ "595" },
+{ "monospace",
+ " arjabqr->FrgFhoGerrFvmr(1);" },
+{ "monospace",
+ "596" },
+{ "monospace",
+ "597" },
+{ "monospace",
+ "598" },
+{ "monospace",
+ "*, nRyrzragGbVafreg);" },
+{ "monospace",
+ "599" },
+{ "monospace",
+ " arjabqr->FrgFhoGerrFvmr(puvyq->TrgFhoGerrFvmr());" },
+{ "monospace",
+ "600" },
+{ "monospace",
+ "601" },
+{ "monospace",
+ "602" },
+{ "monospace",
+ "603" },
+{ "monospace",
+ "// Jr'er zrnag gb vafreg gur ryrzrag vagb gur byqabqr ng" },
+{ "monospace",
+ "604" },
+{ "monospace",
+ "// nFcyvgVaqrk. Pbcl qngn sebz nByqAbqr gb gur arjabqr ohg" },
+{ "monospace",
+ "605" },
+{ "monospace",
+ "// _qba'g_ vafreg arjabqr lrg. Jr znl arrq gb erphefviryl" },
+{ "monospace",
+ "606" },
+{ "monospace",
+ "// fcyvg cneragf, na bcrengvba gung nyybpf, naq urapr, znl" },
+{ "monospace",
+ "607" },
+{ "monospace",
+ "// snvy. Vs vg qbrf snvy, jr jna'g gb abg fperj hc gur" },
+{ "monospace",
+ "608" },
+{ "monospace",
+ "// rkvfgvat qngnfgehpgher." },
+{ "monospace",
+ "609" },
+{ "monospace",
+ "610" },
+{ "monospace",
+ "// Abgr gung vg fubhyq or gur pnfr gung pbhag == pncnpvgl, ohg" },
+{ "monospace",
+ "611" },
+{ "monospace",
+ "// jub xabjf, jr znl qrpvqr ng fbzr cbvag gb cerzngheryl fcyvg" },
+{ "monospace",
+ "612" },
+{ "monospace",
+ "// abqrf sbe fbzr ernfba be nabgure." },
+{ "monospace",
+ "613" },
+{ "monospace",
+ " = nByqAbqr->" },
+{ "monospace",
+ "614" },
+{ "monospace",
+ " = nFcyvgVaqrk;" },
+{ "monospace",
+ "615" },
+{ "monospace",
+ "w" },
+{ "monospace",
+ "616" },
+{ "monospace",
+ "617" },
+{ "monospace",
+ " arjabqr->FrgPbhag(" },
+{ "monospace",
+ " - nFcyvgVaqrk);" },
+{ "monospace",
+ "618" },
+{ "monospace",
+ " juvyr (" },
+{ "monospace",
+ "619" },
+{ "monospace",
+ " vs (nByqAbqr->" },
+{ "monospace",
+ "620" },
+{ "monospace",
+ " ++" },
+{ "monospace",
+ "621" },
+{ "monospace",
+ "622" },
+{ "monospace",
+ " ryfr {" },
+{ "monospace",
+ "623" },
+{ "monospace",
+ "* zvtengvat = " },
+{ "monospace",
+ "*, nByqAbqr->" },
+{ "monospace",
+ "624" },
+{ "monospace",
+ " += zvtengvat->TrgFhoGerrFvmr();" },
+{ "monospace",
+ "625" },
+{ "monospace",
+ "626" },
+{ "monospace",
+ "627" },
+{ "monospace",
+ " arjabqr->" },
+{ "monospace",
+ "), " },
+{ "monospace",
+ "628" },
+{ "monospace",
+ " ++" },
+{ "monospace",
+ "629" },
+{ "monospace",
+ "630" },
+{ "monospace",
+ "631" },
+{ "monospace",
+ " arjabqr->FrgFhoGerrFvmr(" },
+{ "monospace",
+ "632" },
+{ "monospace",
+ "633" },
+{ "monospace",
+ "634" },
+{ "monospace",
+ "// Abj jr fcyvg gur abqr." },
+{ "monospace",
+ "635" },
+{ "monospace",
+ "636" },
+{ "monospace",
+ "() == 0) {" },
+{ "monospace",
+ "637" },
+{ "monospace",
+ "// Jr znqr vg nyy gur jnl hc gb gur ebbg! Bx, fb, perngr n arj" },
+{ "monospace",
+ "638" },
+{ "monospace",
+ "// ebbg" },
+{ "monospace",
+ "639" },
+{ "monospace",
+ "640" },
+{ "monospace",
+ "::rGlcr_Vaqrk, xVaqrkPncnpvgl, &arjebbg);" },
+{ "monospace",
+ "641" },
+{ "monospace",
+ "642" },
+{ "monospace",
+ "643" },
+{ "monospace",
+ " arjebbg->FrgPbhag(2);" },
+{ "monospace",
+ "644" },
+{ "monospace",
+ "(nByqAbqr, 0);" },
+{ "monospace",
+ "645" },
+{ "monospace",
+ "(arjabqr, 1);" },
+{ "monospace",
+ "646" },
+{ "monospace",
+ " arjebbg->FrgFhoGerrFvmr(nByqAbqr->TrgFhoGerrFvmr() + 1);" },
+{ "monospace",
+ "647" },
+{ "monospace",
+ "648" },
+{ "monospace",
+ "649" },
+{ "monospace",
+ "650" },
+{ "monospace",
+ "// Bgurejvfr, hfr gur \"cngu\" gb cbc bss gur arkg guvat nobir hf." },
+{ "monospace",
+ "651" },
+{ "monospace",
+ "cnerag" },
+{ "monospace",
+ "652" },
+{ "monospace",
+ " vaqk;" },
+{ "monospace",
+ "653" },
+{ "monospace",
+ ", &vaqk);" },
+{ "monospace",
+ "654" },
+{ "monospace",
+ "655" },
+{ "monospace",
+ "() >= xVaqrkPncnpvgl) {" },
+{ "monospace",
+ "656" },
+{ "monospace",
+ "// Cnerag vf shyy, gbb. Erphefviryl fcyvg vg." },
+{ "monospace",
+ "657" },
+{ "monospace",
+ ", arjabqr, vaqk);" },
+{ "monospace",
+ "658" },
+{ "monospace",
+ ")) {" },
+{ "monospace",
+ "659" },
+{ "monospace",
+ "(arjabqr);" },
+{ "monospace",
+ "660" },
+{ "monospace",
+ " erghea " },
+{ "monospace",
+ "661" },
+{ "monospace",
+ "662" },
+{ "monospace",
+ "663" },
+{ "monospace",
+ "664" },
+{ "monospace",
+ "// Ebbz va gur cnerag, fb whfg fznpx vg ba hc gurer." },
+{ "monospace",
+ "665" },
+{ "monospace",
+ "(arjabqr, vaqk);" },
+{ "monospace",
+ "666" },
+{ "monospace",
+ "667" },
+{ "monospace",
+ "668" },
+{ "monospace",
+ "669" },
+{ "monospace",
+ "670" },
+{ "monospace",
+ "// Abj, fvapr nyy bhe bcrengvbaf gung zvtug snvy unir svavfurq, jr" },
+{ "monospace",
+ "671" },
+{ "monospace",
+ "// pna tb nurnq naq zbaxrl jvgu gur byq abqr." },
+{ "monospace",
+ "672" },
+{ "monospace",
+ "673" },
+{ "monospace",
+ "674" },
+{ "monospace",
+ " abqrfybfg = arjabqr->TrgFhoGerrFvmr() - 1;" },
+{ "monospace",
+ "675" },
+{ "monospace",
+ " fhogerrfvmr = nByqAbqr->TrgFhoGerrFvmr() - abqrfybfg;" },
+{ "monospace",
+ "676" },
+{ "monospace",
+ " nByqAbqr->FrgFhoGerrFvmr(fhogerrfvmr);" },
+{ "monospace",
+ "677" },
+{ "monospace",
+ "678" },
+{ "monospace",
+ "679" },
+{ "monospace",
+ " nByqAbqr->FrgPbhag(nFcyvgVaqrk);" },
+{ "monospace",
+ "680" },
+{ "monospace",
+ " nByqAbqr->" },
+{ "monospace",
+ "(nRyrzragGbVafreg, nFcyvgVaqrk);" },
+{ "monospace",
+ "681" },
+{ "monospace",
+ " fhogerrfvmr = nByqAbqr->TrgFhoGerrFvmr() - " },
+{ "monospace",
+ " + 1;" },
+{ "monospace",
+ "682" },
+{ "monospace",
+ "683" },
+{ "monospace",
+ "684" },
+{ "monospace",
+ "685" },
+{ "monospace",
+ "686" },
+{ "monospace",
+ "687" },
+{ "monospace",
+ "688" },
+{ "monospace",
+ "689" },
+{ "monospace",
+ "690" },
+{ "monospace",
+ "691" },
+{ "monospace",
+ "692" },
+{ "monospace",
+ "// Fnavgl purpx gur gerr ol irevslvat gung gur fhogerr fvmrf nyy" },
+{ "monospace",
+ "693" },
+{ "monospace",
+ "// nqq hc pbeerpgyl." },
+{ "monospace",
+ "694" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ "695" },
+{ "monospace",
+ "AF_NFFREGVBA" },
+{ "monospace",
+ "->TrgFhoGerrFvmr(), " },
+{ "monospace",
+ "696" },
+{ "monospace",
+ "697" },
+{ "monospace",
+ "698" },
+{ "monospace",
+ "699" },
+{ "monospace",
+ " puvyqpbhag = 0;" },
+{ "monospace",
+ "700" },
+{ "monospace",
+ "(); ++" },
+{ "monospace",
+ "701" },
+{ "monospace",
+ "702" },
+{ "monospace",
+ " puvyqpbhag += " },
+{ "monospace",
+ "(puvyq);" },
+{ "monospace",
+ "703" },
+{ "monospace",
+ "704" },
+{ "monospace",
+ "705" },
+{ "monospace",
+ "(puvyqpbhag == " },
+{ "monospace",
+ "706" },
+{ "monospace",
+ " erghea puvyqpbhag;" },
+{ "monospace",
+ "707" },
+{ "monospace",
+ "708" },
+{ "monospace",
+ "709" },
+{ "monospace",
+ "710" },
+{ "monospace",
+ "711" },
+{ "monospace",
+ "712" },
+{ "monospace",
+ "713" },
+{ "monospace",
+ "() - 1;" },
+{ "monospace",
+ "714" },
+{ "monospace",
+ " >= 0) {" },
+{ "monospace",
+ "715" },
+{ "monospace",
+ "::rGlcr_Vaqrk)" },
+{ "monospace",
+ "716" },
+{ "monospace",
+ ")));" },
+{ "monospace",
+ "717" },
+{ "monospace",
+ "718" },
+{ "monospace",
+ "719" },
+{ "monospace",
+ "720" },
+{ "monospace",
+ "721" },
+{ "monospace",
+ "722" },
+{ "monospace",
+ "723" },
+{ "monospace",
+ "724" },
+{ "monospace",
+ "725" },
+{ "monospace",
+ "726" },
+{ "monospace",
+ "nVaqrag" },
+{ "monospace",
+ "727" },
+{ "monospace",
+ "728" },
+{ "monospace",
+ "729" },
+{ "monospace",
+ "cevags" },
+{ "monospace",
+ "\" \"" },
+{ "monospace",
+ "730" },
+{ "monospace",
+ "731" },
+{ "monospace",
+ "732" },
+{ "monospace",
+ "\"qngn(%q/%q)\\a\"" },
+{ "monospace",
+ "->TrgFhoGerrFvmr());" },
+{ "monospace",
+ "733" },
+{ "monospace",
+ "734" },
+{ "monospace",
+ "735" },
+{ "monospace",
+ "\"vaqrk(%q/%q)\\a\"" },
+{ "monospace",
+ "736" },
+{ "monospace",
+ "737" },
+{ "monospace",
+ ")), " },
+{ "monospace",
+ "738" },
+{ "monospace",
+ "739" },
+{ "monospace",
+ "740" },
+{ "monospace",
+ "741" },
+{ "monospace",
+ "742" },
+{ "monospace",
+ "743" },
+{ "monospace",
+ "744" },
+{ "monospace",
+ "// afIbvqOGerr::PbafgVgrengbe naq Vgrengbe zrgubqf" },
+{ "monospace",
+ "745" },
+{ "monospace",
+ "746" },
+{ "monospace",
+ "747" },
+{ "monospace",
+ " ibvq* " },
+{ "monospace",
+ "xQhzzlYnfg" },
+{ "monospace",
+ "748" },
+{ "monospace",
+ "749" },
+{ "monospace",
+ "750" },
+{ "monospace",
+ "::PbafgVgrengbe::" },
+{ "monospace",
+ "Arkg" },
+{ "monospace",
+ "()" },
+{ "monospace",
+ "751" },
+{ "monospace",
+ "752" },
+{ "monospace",
+ " vs (zVfFvatyrgba) {" },
+{ "monospace",
+ "753" },
+{ "monospace",
+ " zVfRkunhfgrq = " },
+{ "monospace",
+ "754" },
+{ "monospace",
+ "755" },
+{ "monospace",
+ "756" },
+{ "monospace",
+ "757" },
+{ "monospace",
+ "// Bgurejvfr jr'er n erny o-gerr vgrengbe, naq jr arrq gb chyy naq" },
+{ "monospace",
+ "758" },
+{ "monospace",
+ "// cbc bhe cngu fgnpx nccebcevngryl gb tlengr vagb gur evtug" },
+{ "monospace",
+ "759" },
+{ "monospace",
+ "// cbfvgvba." },
+{ "monospace",
+ "760" },
+{ "monospace",
+ " juvyr (1) {" },
+{ "monospace",
+ "761" },
+{ "monospace",
+ "762" },
+{ "monospace",
+ "763" },
+{ "monospace",
+ " zCngu." },
+{ "monospace",
+ "764" },
+{ "monospace",
+ "765" },
+{ "monospace",
+ "766" },
+{ "monospace",
+ "767" },
+{ "monospace",
+ "\"ena bss gur raq, cny\"" },
+{ "monospace",
+ "768" },
+{ "monospace",
+ "769" },
+{ "monospace",
+ " vs (++" },
+{ "monospace",
+ "770" },
+{ "monospace",
+ "// KKKjngrefba Bu, guvf vf fb htyl. V jvfu V jnf fzneg" },
+{ "monospace",
+ "771" },
+{ "monospace",
+ "// rabhtu gb svther bhg n cerggvre jnl gb qb vg." },
+{ "monospace",
+ "772" },
+{ "monospace",
+ "773" },
+{ "monospace",
+ "// Frr vs jr'ir whfg vgrengrq cnfg gur ynfg ryrzrag va gur" },
+{ "monospace",
+ "774" },
+{ "monospace",
+ "// o-gerr, naq abj arrq gb yrnir bhefryirf va gur zntvpny" },
+{ "monospace",
+ "775" },
+{ "monospace",
+ "// fgngr gung vf rdhny gb afIbvqOGerr::Ynfg()." },
+{ "monospace",
+ "776" },
+{ "monospace",
+ "777" },
+{ "monospace",
+ " evtugzbfg = " },
+{ "monospace",
+ "778" },
+{ "monospace",
+ " sbe (" },
+{ "monospace",
+ " fybg = zCngu.zGbc - 1; fybg >= 0; --fybg) {" },
+{ "monospace",
+ "779" },
+{ "monospace",
+ " pbafg " },
+{ "monospace",
+ "Yvax" },
+{ "monospace",
+ "yvax" },
+{ "monospace",
+ " = zCngu.zYvax[fybg];" },
+{ "monospace",
+ "780" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ ".zAbqr->" },
+{ "monospace",
+ "() - 1) {" },
+{ "monospace",
+ "781" },
+{ "monospace",
+ " evtugzbfg = " },
+{ "monospace",
+ "782" },
+{ "monospace",
+ " oernx;" },
+{ "monospace",
+ "783" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "784" },
+{ "monospace",
+ " }" },
+{ "monospace",
+ "785" },
+{ "monospace",
+ "786" },
+{ "monospace",
+ " vs (evtugzbfg) {" },
+{ "monospace",
+ "787" },
+{ "monospace",
+ "// Vg'f gur ynfg bar. Znxr gur cngu ybbx rknpgyl" },
+{ "monospace",
+ "788" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "// yvxr afIbvqOGerr::Ynfg()." },
+{ "monospace",
+ "789" },
+{ "monospace",
+ " zCngu." },
+{ "monospace",
+ "790" },
+{ "monospace",
+ " erghea;" },
+{ "monospace",
+ "791" },
+{ "monospace",
+ "792" },
+{ "monospace",
+ "793" },
+{ "monospace",
+ "794" },
+{ "monospace",
+ "// Bgurejvfr, jr whfg ena bss gur raq bs n \"zvqqyvat\"" },
+{ "monospace",
+ "795" },
+{ "monospace",
+ "// abqr. Ybbc nebhaq, gb cbc onpx hc gur o-gerr gb vgf" },
+{ "monospace",
+ "796" },
+{ "monospace",
+ "// cnerag." },
+{ "monospace",
+ "797" },
+{ "monospace",
+ " pbagvahr;" },
+{ "monospace",
+ "798" },
+{ "monospace",
+ "799" },
+{ "monospace",
+ "800" },
+{ "monospace",
+ "// Jr'er fbzrjurer va gur zvqqyr. Chfu gur arj ybpngvba bagb" },
+{ "monospace",
+ "801" },
+{ "monospace",
+ "// gur fgnpx." },
+{ "monospace",
+ "802" },
+{ "monospace",
+ " zCngu." },
+{ "monospace",
+ "803" },
+{ "monospace",
+ "804" },
+{ "monospace",
+ "// Vs jr'er va n qngn abqr, jr'er qbar: oernx bhg bs gur ybbc" },
+{ "monospace",
+ "805" },
+{ "monospace",
+ "// urer yrnivat gur gbc bs gur fgnpx cbvagvat gb gur arkg qngn" },
+{ "monospace",
+ "806" },
+{ "monospace",
+ "// ryrzrag va gur o-gerr." },
+{ "monospace",
+ "807" },
+{ "monospace",
+ " vs (" },
+{ "monospace",
+ "::rGlcr_Qngn)" },
+{ "monospace",
+ "808" },
+{ "monospace",
+ " oernx;" },
+{ "monospace",
+ "809" },
+{ "monospace",
+ "810" },
+{ "monospace",
+ "// Bgurejvfr, jr'er fgvyy va na vaqrk abqr. Chfu arkg abqr" },
+{ "monospace",
+ "811" },
+{ "monospace",
+ "// qbja bagb gur fgnpx, fgnegvat \"bar bss\" gb gur yrsg, naq" },
+{ "monospace",
+ "812" },
+{ "monospace",
+ "// pbagvahr nebhaq." },
+{ "monospace",
+ "813" },
+{ "monospace",
+ ")), -1);" },
+{ "monospace",
+ "814" },
+{ "monospace",
+ "815" },
+{ "monospace",
+ "816" },
+{ "monospace",
+ "817" },
+{ "monospace",
+ "818" },
+{ "monospace",
+ "Ceri" },
+{ "monospace",
+ "819" },
+{ "monospace",
+ "820" },
+{ "monospace",
+ "821" },
+{ "monospace",
+ "822" },
+{ "monospace",
+ "823" },
+{ "monospace",
+ "824" },
+{ "monospace",
+ "825" },
+{ "monospace",
+ "826" },
+{ "monospace",
+ "827" },
+{ "monospace",
+ "// cbfvgvba. Guvf vf whfg yvxr afIbvqOGerr::PbafgVgrengbe::Arkg()," },
+{ "monospace",
+ "828" },
+{ "monospace",
+ "// ohg va erirefr." },
+{ "monospace",
+ "829" },
+{ "monospace",
+ "830" },
+{ "monospace",
+ "831" },
+{ "monospace",
+ "832" },
+{ "monospace",
+ "833" },
+{ "monospace",
+ "834" },
+{ "monospace",
+ " >= 0, " },
+{ "monospace",
+ "\"ena bss gur sebag, cny\"" },
+{ "monospace",
+ "835" },
+{ "monospace",
+ "836" },
+{ "monospace",
+ " vs (--" },
+{ "monospace",
+ " < 0)" },
+{ "monospace",
+ "837" },
+{ "monospace",
+ " pbagvahr;" },
+{ "monospace",
+ "838" },
+{ "monospace",
+ "839" },
+{ "monospace",
+ "840" },
+{ "monospace",
+ "841" },
+{ "monospace",
+ "842" },
+{ "monospace",
+ "843" },
+{ "monospace",
+ "844" },
+{ "monospace",
+ "845" },
+{ "monospace",
+ "());" },
+{ "monospace",
+ "846" },
+{ "monospace",
+ "847" },
+{ "monospace",
+ "848" },
+{ "monospace",
+ "849" },
+{ "monospace",
+ " pbafg " },
+{ "monospace",
+ "850" },
+{ "monospace",
+ "::YrsgZbfgCngu() pbafg" },
+{ "monospace",
+ "851" },
+{ "monospace",
+ "852" },
+{ "monospace",
+ "853" },
+{ "monospace",
+ "854" },
+{ "monospace",
+ "855" },
+{ "monospace",
+ " juvyr (1) {" },
+{ "monospace",
+ "856" },
+{ "monospace",
+ ", 0);" },
+{ "monospace",
+ "857" },
+{ "monospace",
+ "858" },
+{ "monospace",
+ "859" },
+{ "monospace",
+ "860" },
+{ "monospace",
+ "861" },
+{ "monospace",
+ "862" },
+{ "monospace",
+ "863" },
+{ "monospace",
+ "864" },
+{ "monospace",
+ "865" },
+{ "monospace",
+ "866" },
+{ "monospace",
+ "867" },
+{ "monospace",
+ "868" },
+{ "monospace",
+ "869" },
+{ "monospace",
+ "::EvtugZbfgCngu() pbafg" },
+{ "monospace",
+ "870" },
+{ "monospace",
+ "871" },
+{ "monospace",
+ "872" },
+{ "monospace",
+ "873" },
+{ "monospace",
+ "874" },
+{ "monospace",
+ "875" },
+{ "monospace",
+ "876" },
+{ "monospace",
+ "877" },
+{ "monospace",
+ "878" },
+{ "monospace",
+ "879" },
+{ "monospace",
+ "880" },
+{ "monospace",
+ "881" },
+{ "monospace",
+ "882" },
+{ "monospace",
+ "883" },
+{ "monospace",
+ " - 1));" },
+{ "monospace",
+ "884" },
+{ "monospace",
+ "885" },
+{ "monospace",
+ "886" },
+{ "monospace",
+ "887" },
+{ "monospace",
+ "888" },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "cntr" },
+{ "serif",
+ "jnf" },
+{ "serif",
+ "nhgbzngvpnyyl" },
+{ "serif",
+ "trarengrq" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "YKE" },
+{ "serif",
+ "." },
+{ "serif",
+ "Zbmvyyn Pebff Ersrerapr:" },
+{ "serif",
+ " " },
+{ "serif",
+ "/ " },
+{ "serif",
+ "PIF Ybt" },
+{ "serif",
+ "PIF Oynzr" },
+{ "serif",
+ "punatrf gb" },
+{ "serif",
+ "guvf svyr va" },
+{ "serif",
+ "gur ynfg:" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "1" },
+{ "monospace",
+ " " },
+{ "monospace",
+ "/* -*- Zbqr: P++; gno-jvqgu: 8; vaqrag-gnof-zbqr: avy; p-onfvp-bssfrg: 4 -*- */" },
+{ "monospace",
+ "2" },
+{ "monospace",
+ "/*" },
+{ "monospace",
+ "3" },
+{ "monospace",
+ " * Gur pbagragf bs guvf svyr ner fhowrpg gb gur Zbmvyyn Choyvp Yvprafr" },
+{ "monospace",
+ "4" },
+{ "monospace",
+ " * Irefvba 1.1 (gur \"ZCY\"); lbh znl abg hfr guvf svyr rkprcg va" },
+{ "monospace",
+ "5" },
+{ "monospace",
+ " * pbzcyvnapr jvgu gur ZCY. Lbh znl bognva n pbcl bs gur ZCY ng" },
+{ "monospace",
+ "6" },
+{ "monospace",
+ " * " },
+{ "monospace",
+ "uggc://jjj.zbmvyyn.bet/ZCY/" },
+{ "monospace",
+ "7" },
+{ "monospace",
+ " *" },
+{ "monospace",
+ "8" },
+{ "monospace",
+ " * Fbsgjner qvfgevohgrq haqre gur ZCY vf qvfgevohgrq ba na \"NF VF\" onfvf," },
+{ "monospace",
+ "9" },
+{ "monospace",
+ " * JVGUBHG JNEENAGL BS NAL XVAQ, rvgure rkcerff be vzcyvrq. Frr gur ZCY" },
+{ "monospace",
+ "10" },
+{ "monospace",
+ " * sbe gur fcrpvsvp ynathntr tbireavat evtugf naq yvzvgngvbaf haqre gur" },
+{ "monospace",
+ "11" },
+{ "monospace",
+ " * ZCY." },
+{ "monospace",
+ "12" },
+{ "monospace",
+ "13" },
+{ "monospace",
+ " * Gur Vavgvny Qrirybcre bs guvf pbqr haqre gur ZCY vf Argfpncr" },
+{ "monospace",
+ "14" },
+{ "monospace",
+ " * Pbzzhavpngvbaf Pbecbengvba. Cbegvbaf perngrq ol Argfpncr ner" },
+{ "monospace",
+ "15" },
+{ "monospace",
+ " * Pbclevtug (P) 1999 Argfpncr Pbzzhavpngvbaf Pbecbengvba. Nyy Evtugf" },
+{ "monospace",
+ "16" },
+{ "monospace",
+ " * Erfreirq." },
+{ "monospace",
+ "17" },
+{ "monospace",
+ "18" },
+{ "monospace",
+ " * Bevtvany Nhgube:" },
+{ "monospace",
+ "19" },
+{ "monospace",
+ " * Puevf Jngrefba <jngrefba@argfpncr.pbz>" },
+{ "monospace",
+ "20" },
+{ "monospace",
+ " */" },
+{ "monospace",
+ "21" },
+{ "monospace",
+ "22" },
+{ "monospace",
+ " #vapyhqr \"" },
+{ "monospace",
+ "afIbvqOGerr.u" },
+{ "monospace",
+ "\"" },
+{ "monospace",
+ "23" },
+{ "monospace",
+ "24" },
+{ "monospace",
+ " #vsqrs " },
+{ "monospace",
+ "QROHT" },
+{ "monospace",
+ "25" },
+{ "monospace",
+ " #vapyhqr <" },
+{ "monospace",
+ "fgqvb.u" },
+{ "monospace",
+ ">" },
+{ "monospace",
+ "26" },
+{ "monospace",
+ " #raqvs" },
+{ "monospace",
+ "27" },
+{ "monospace",
+ "28" },
+{ "monospace",
+ "// Frg guvf gb sbepr gur gerr gb or irevsvrq nsgre rirel vafregvba naq" },
+{ "monospace",
+ "29" },
+{ "monospace",
+ "// erzbiny." },
+{ "monospace",
+ "30" },
+{ "monospace",
+ "//#qrsvar CNENABVQ 1" },
+{ "monospace",
+ "31" },
+{ "serif",
+ "5," },
+{ "serif",
+ "yke.zbmvyyn.bet," },
+{ "serif",
+ "554" },
+{ "serif",
+ "1, 5, yke.zbmvyyn.bet, 554" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argfpncr" },
+{ "Helvetica,serif",
+ "Frnepu" },
+{ "Helvetica,serif",
+ " - " },
+{ "Helvetica,serif",
+ "JroZnvy" },
+{ "Helvetica,serif",
+ "Zl" },
+{ "Helvetica,serif",
+ "Argfpncr" },
+{ "Helvetica,serif",
+ "Ohqql" },
+{ "Helvetica,serif",
+ "Pung" },
+{ "Helvetica,serif",
+ "Uryc" },
+{ "Helvetica,serif",
+ "Qbjaybnq" },
+{ "Helvetica,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "Ubzr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Uryc" },
+{ "helvetica,arial,sans-serif,serif",
+ "|" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fvta Va" },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "Zl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argfpncr" },
+{ "helvetica,arial,sans-serif,serif",
+ " Fngheqnl" },
+{ "helvetica,arial,sans-serif,serif",
+ "-" },
+{ "helvetica,arial,sans-serif,serif",
+ "Abirzore" },
+{ "helvetica,arial,sans-serif,serif",
+ "11," },
+{ "helvetica,arial,sans-serif,serif",
+ "2000" },
+{ "helvetica,arial,sans-serif,serif",
+ "5:20" },
+{ "helvetica,arial,sans-serif,serif",
+ "NZ" },
+{ "helvetica,arial,sans-serif,serif",
+ " (RQG)" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jrypbzr!" },
+{ "helvetica,arial,sans-serif,serif",
+ "V" },
+{ "helvetica,arial,sans-serif,serif",
+ "unir" },
+{ "helvetica,arial,sans-serif,serif",
+ "nyernql" },
+{ "helvetica,arial,sans-serif,serif",
+ "phfgbzvmrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "zl" },
+{ "helvetica,arial,sans-serif,serif",
+ "cntr." },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Vg!" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znxr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Guvf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ubzrcntr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "uryc?" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gel" },
+{ "helvetica,arial,sans-serif,serif",
+ "gur" },
+{ "helvetica,arial,sans-serif,serif",
+ "SND" },
+{ "helvetica,arial,sans-serif,serif",
+ "." },
+{ "helvetica,arial,sans-serif,serif",
+ "Fgbpxf" },
+{ "helvetica,arial,sans-serif,serif",
+ "FLZOBY" },
+{ "helvetica,arial,sans-serif,serif",
+ "CEVPR" },
+{ "helvetica,arial,sans-serif,serif",
+ "PUNATR" },
+{ "sans-serif,Arial,Helvetica,serif",
+ " " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "QWVN" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Anfqnd" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "F&C" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "500" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "NBY" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "10,602.90 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "3,028.99 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "1,365.98 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "50.50 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-231.30" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-171.36" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-34.16" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "-2.18" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "0" },
+{ "sans-serif,Arial,Helvetica,serif",
+ " Cbegsbyvb" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Gbgny:" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Zl" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Cbegsbyvb" },
+{ "Lucida Grande",
+ "Dhbgr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Flzoby" },
+{ "helvetica,arial,sans-serif,serif",
+ "Anzr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Dhbgrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "qrynlrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "20" },
+{ "helvetica,arial,sans-serif,serif",
+ "zvaf." },
+{ "helvetica,arial,sans-serif,serif",
+ "*" },
+{ "helvetica,arial,sans-serif,serif",
+ "Vaqvpngrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Uv" },
+{ "helvetica,arial,sans-serif,serif",
+ "be" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yb" },
+{ "helvetica,arial,sans-serif,serif",
+ "zbirzrag." },
+{ "helvetica,arial,sans-serif,serif",
+ "Qvfpynvzre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fcbegf" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Zber..." },
+{ "helvetica,arial,sans-serif,serif",
+ "L" },
+{ "helvetica,arial,sans-serif,serif",
+ "R" },
+{ "helvetica,arial,sans-serif,serif",
+ "F" },
+{ "helvetica,arial,sans-serif,serif",
+ "G" },
+{ "helvetica,arial,sans-serif,serif",
+ "E" },
+{ "helvetica,arial,sans-serif,serif",
+ "Q" },
+{ "helvetica,arial,sans-serif,serif",
+ "N" },
+{ "helvetica,arial,sans-serif,serif",
+ "B" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gbqnl'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "Srngherf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NC" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fngheqnl," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Abirzore" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "11," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "2000" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sybevqn" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fgnaqbss" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jrrx'f" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "raq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "snvyrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "oevat" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "na" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pnzcnvta" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pubbfr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Nzrevpn'f" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "arkg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "cerfvqrag." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sevqnl," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ohfu" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "naq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tber" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pnzcnvtaf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pynfurq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "bire" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ibgr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhag" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "va" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sybevqn," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jurer" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erpbhag" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbagvahrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "guebhtu" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jrrxraq." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Svany" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erfhygf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jrer" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "abg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "rkcrpgrq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "hagvy" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ghrfqnl," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "nofragrr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "onyybgf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ynjfhvgf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pbhyq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qrynl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qrpvfvba" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "rira" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "shegure." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yngrfg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cbyvgvpny" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Frnepu" },
+{ "helvetica,arial,sans-serif,serif",
+ "gur" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jro" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "helvetica,arial,sans-serif,serif",
+ "Frnepu Gvcf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yrnea zber" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jung'f Arj" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jung'f Pbby" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arg Frnepu Cntr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Ohfvarff" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Zrzore" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Crefba" },
+{ "helvetica,arial,sans-serif,serif",
+ "Negf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ohfvarff" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbzchgref" },
+{ "helvetica,arial,sans-serif,serif",
+ "Tnzrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Urnygu" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ubzr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arjf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erperngvba" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ersrerapr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ertvbany" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fpvrapr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fubccvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fbpvrgl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fcbegf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jbeyq" },
+{ "helvetica,arial,sans-serif,serif",
+ "P" },
+{ "helvetica,arial,sans-serif,serif",
+ "U" },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "T" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znexrg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arjf" },
+{ "helvetica,arial,sans-serif,serif",
+ "ol" },
+{ "helvetica,arial,sans-serif,serif",
+ "POF" },
+{ "helvetica,arial,sans-serif,serif",
+ "ZnexrgJngpu" },
+{ "helvetica,arial,sans-serif,serif",
+ "GBQNL'F" },
+{ "helvetica,arial,sans-serif,serif",
+ "ZNEXRG" },
+{ "helvetica,arial,sans-serif,serif",
+ "ARJF" },
+{ "helvetica,arial,sans-serif,serif",
+ "(Abirzore" },
+{ "helvetica,arial,sans-serif,serif",
+ "11," },
+{ "helvetica,arial,sans-serif,serif",
+ "5:03" },
+{ "helvetica,arial,sans-serif,serif",
+ "NZ)" },
+{ "helvetica,arial,sans-serif,serif",
+ "oehvfrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "znexrg" },
+{ "helvetica,arial,sans-serif,serif",
+ "ybbxf" },
+{ "helvetica,arial,sans-serif,serif",
+ "sbe" },
+{ "helvetica,arial,sans-serif,serif",
+ "eryvrs" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nsgre-ubhef" },
+{ "helvetica,arial,sans-serif,serif",
+ "genqvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "uvg" },
+{ "helvetica,arial,sans-serif,serif",
+ "ol" },
+{ "helvetica,arial,sans-serif,serif",
+ "grpu" },
+{ "helvetica,arial,sans-serif,serif",
+ "jrnxarff" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qryy" },
+{ "helvetica,arial,sans-serif,serif",
+ "funerf" },
+{ "helvetica,arial,sans-serif,serif",
+ "cnl" },
+{ "helvetica,arial,sans-serif,serif",
+ "cevpr" },
+{ "helvetica,arial,sans-serif,serif",
+ "fybjre" },
+{ "helvetica,arial,sans-serif,serif",
+ "fnyrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "sberpnfg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Urnqyvar" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gbc" },
+{ "helvetica,arial,sans-serif,serif",
+ "(Abi" },
+{ "helvetica,arial,sans-serif,serif",
+ "11" },
+{ "helvetica,arial,sans-serif,serif",
+ "2000" },
+{ "helvetica,arial,sans-serif,serif",
+ "3:15NZ)" },
+{ "helvetica,arial,sans-serif,serif",
+ " ol Erhgref" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ohfu" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gnxrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yrnq" },
+{ "helvetica,arial,sans-serif,serif",
+ "va" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arj" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zrkvpb" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erpbhag" },
+{ "helvetica,arial,sans-serif,serif",
+ "Sybevqn" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbhagvrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "gb" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ortva" },
+{ "helvetica,arial,sans-serif,serif",
+ "Unaq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbhagvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "Onyybgf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ohfu'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znetva" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qebcf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ryrpgvba" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qenzn" },
+{ "helvetica,arial,sans-serif,serif",
+ "Bssorng" },
+{ "helvetica,arial,sans-serif,serif",
+ "10" },
+{ "helvetica,arial,sans-serif,serif",
+ "8:04NZ)" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fzbxvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fglyr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Tvirf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Shtvgvir" },
+{ "helvetica,arial,sans-serif,serif",
+ "Njnl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zvyyvbaf" },
+{ "helvetica,arial,sans-serif,serif",
+ "bs" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qbyynef" },
+{ "helvetica,arial,sans-serif,serif",
+ "Sbhaq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Haqre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Sybbe" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gjragl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Oyvaq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pevpxrg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Snaf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Trg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbearnf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "helvetica,arial,sans-serif,serif",
+ "1:24NZ)" },
+{ "helvetica,arial,sans-serif,serif",
+ " ol NC" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cyntvnevfz" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fhvg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svyrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Bire" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znek" },
+{ "helvetica,arial,sans-serif,serif",
+ "Errir" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cebhq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fcvany" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erfrnepu" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zbarl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Oebxnj" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ebfr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cnenqr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Tenaq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znefuny" },
+{ "helvetica,arial,sans-serif,serif",
+ "Grpu" },
+{ "helvetica,arial,sans-serif,serif",
+ "MQArg" },
+{ "helvetica,arial,sans-serif,serif",
+ "GRPU" },
+{ "helvetica,arial,sans-serif,serif",
+ "OL" },
+{ "helvetica,arial,sans-serif,serif",
+ "5:05" },
+{ "helvetica,arial,sans-serif,serif",
+ "Rf" },
+{ "helvetica,arial,sans-serif,serif",
+ "fh" },
+{ "helvetica,arial,sans-serif,serif",
+ "pnfn" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zvpebfbsg'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "pnfn?" },
+{ "helvetica,arial,sans-serif,serif",
+ "VOZ'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "uvtu-erm" },
+{ "helvetica,arial,sans-serif,serif",
+ "fperra" },
+{ "helvetica,arial,sans-serif,serif",
+ "vf" },
+{ "helvetica,arial,sans-serif,serif",
+ "cncre" },
+{ "helvetica,arial,sans-serif,serif",
+ "cresrpg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znp" },
+{ "helvetica,arial,sans-serif,serif",
+ "BF" },
+{ "helvetica,arial,sans-serif,serif",
+ "K" },
+{ "helvetica,arial,sans-serif,serif",
+ "pbzcrgvgbef" },
+{ "helvetica,arial,sans-serif,serif",
+ "ybool" },
+{ "helvetica,arial,sans-serif,serif",
+ "Treznal" },
+{ "helvetica,arial,sans-serif,serif",
+ "GI" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yvfgvatf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Thvqr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Sbe" },
+{ "helvetica,arial,sans-serif,serif",
+ "ybpny" },
+{ "helvetica,arial,sans-serif,serif",
+ "GI" },
+{ "helvetica,arial,sans-serif,serif",
+ "yvfgvatf," },
+{ "helvetica,arial,sans-serif,serif",
+ "cyrnfr" },
+{ "helvetica,arial,sans-serif,serif",
+ "ragre" },
+{ "helvetica,arial,sans-serif,serif",
+ "lbhe" },
+{ "helvetica,arial,sans-serif,serif",
+ "MVC" },
+{ "helvetica,arial,sans-serif,serif",
+ "pbqr" },
+{ "Lucida Grande",
+ "Trg Yvfgvatf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gbc" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zbivrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "ba" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fbncf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Bgure" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yvfgvatf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ubebfpbcrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nevrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Frr" },
+{ "helvetica,arial,sans-serif,serif",
+ "nyy" },
+{ "helvetica,arial,sans-serif,serif",
+ "fvtaf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbh" },
+{ "helvetica,arial,sans-serif,serif",
+ "znl" },
+{ "helvetica,arial,sans-serif,serif",
+ "or" },
+{ "helvetica,arial,sans-serif,serif",
+ "birejuryzrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "ng" },
+{ "helvetica,arial,sans-serif,serif",
+ "rabezbhf" },
+{ "helvetica,arial,sans-serif,serif",
+ "erfcbafvovyvgvrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "lbh" },
+{ "helvetica,arial,sans-serif,serif",
+ "snpr" },
+{ "helvetica,arial,sans-serif,serif",
+ "choyvp" },
+{ "helvetica,arial,sans-serif,serif",
+ "yvsr." },
+{ "helvetica,arial,sans-serif,serif",
+ "abg" },
+{ "helvetica,arial,sans-serif,serif",
+ "qrcerffrq." },
+{ "helvetica,arial,sans-serif,serif",
+ "Vs" },
+{ "helvetica,arial,sans-serif,serif",
+ "gnyx" },
+{ "helvetica,arial,sans-serif,serif",
+ "jvgu" },
+{ "helvetica,arial,sans-serif,serif",
+ "snzvyl" },
+{ "helvetica,arial,sans-serif,serif",
+ "zrzoref" },
+{ "helvetica,arial,sans-serif,serif",
+ "nobhg" },
+{ "helvetica,arial,sans-serif,serif",
+ "cerffherf" },
+{ "helvetica,arial,sans-serif,serif",
+ "naq" },
+{ "helvetica,arial,sans-serif,serif",
+ "srne" },
+{ "helvetica,arial,sans-serif,serif",
+ "snvyher," },
+{ "helvetica,arial,sans-serif,serif",
+ "jvyy" },
+{ "helvetica,arial,sans-serif,serif",
+ "ernyvmr" },
+{ "helvetica,arial,sans-serif,serif",
+ "terng" },
+{ "helvetica,arial,sans-serif,serif",
+ "fhccbeg" },
+{ "helvetica,arial,sans-serif,serif",
+ "ninvynoyr" },
+{ "helvetica,arial,sans-serif,serif",
+ "sebz" },
+{ "helvetica,arial,sans-serif,serif",
+ "gubfr" },
+{ "helvetica,arial,sans-serif,serif",
+ "ybir." },
+{ "helvetica,arial,sans-serif,serif",
+ "Va" },
+{ "helvetica,arial,sans-serif,serif",
+ "snpg," },
+{ "helvetica,arial,sans-serif,serif",
+ "vs" },
+{ "helvetica,arial,sans-serif,serif",
+ "nfx" },
+{ "helvetica,arial,sans-serif,serif",
+ "zber" },
+{ "helvetica,arial,sans-serif,serif",
+ "ernffhenapr," },
+{ "helvetica,arial,sans-serif,serif",
+ "lbh'yy" },
+{ "helvetica,arial,sans-serif,serif",
+ "frr" },
+{ "helvetica,arial,sans-serif,serif",
+ "ubj" },
+{ "helvetica,arial,sans-serif,serif",
+ "zhpu" },
+{ "helvetica,arial,sans-serif,serif",
+ "ybir" },
+{ "helvetica,arial,sans-serif,serif",
+ "erfcrpg" },
+{ "helvetica,arial,sans-serif,serif",
+ "tvira." },
+{ "helvetica,arial,sans-serif,serif",
+ "Vg'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "bayl" },
+{ "helvetica,arial,sans-serif,serif",
+ "angheny" },
+{ "helvetica,arial,sans-serif,serif",
+ "grfg" },
+{ "helvetica,arial,sans-serif,serif",
+ "lbhefrys" },
+{ "helvetica,arial,sans-serif,serif",
+ "gvzr" },
+{ "helvetica,arial,sans-serif,serif",
+ "gvzr." },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbclevtug" },
+{ "helvetica,arial,sans-serif,serif",
+ "2000," },
+{ "helvetica,arial,sans-serif,serif",
+ "Wrnaar" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nirel" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nfgebarg," },
+{ "helvetica,arial,sans-serif,serif",
+ "Jbzra.pbz" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argjbexf," },
+{ "helvetica,arial,sans-serif,serif",
+ "YYP" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argpragre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nccf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Purpx" },
+{ "helvetica,arial,sans-serif,serif",
+ "JroZnvy" },
+{ "helvetica,arial,sans-serif,serif",
+ "Serr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Crefbany" },
+{ "helvetica,arial,sans-serif,serif",
+ "Rznvy!" },
+{ "helvetica,arial,sans-serif,serif",
+ "Vafgnag" },
+{ "helvetica,arial,sans-serif,serif",
+ "Zrffratre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pung" },
+{ "helvetica,arial,sans-serif,serif",
+ "sevraqf." },
+{ "helvetica,arial,sans-serif,serif",
+ "JroPnyraqne" },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbhe" },
+{ "helvetica,arial,sans-serif,serif",
+ "JroPnyraqne." },
+{ "helvetica,arial,sans-serif,serif",
+ "Jrngure" },
+{ "helvetica,arial,sans-serif,serif",
+ "44\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "S" },
+{ "helvetica,arial,sans-serif,serif",
+ "/" },
+{ "helvetica,arial,sans-serif,serif",
+ "7\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "P" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cnegyl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pybhql" },
+{ "helvetica,arial,sans-serif,serif",
+ "51\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "11\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yvtug" },
+{ "helvetica,arial,sans-serif,serif",
+ "Enva" },
+{ "helvetica,arial,sans-serif,serif",
+ "54\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "12\xc2""\xb0""" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ybaqba," },
+{ "helvetica,arial,sans-serif,serif",
+ "Havgrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "Xvatqbz" },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbex," },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbex" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fna" },
+{ "helvetica,arial,sans-serif,serif",
+ "Senapvfpb," },
+{ "helvetica,arial,sans-serif,serif",
+ "Pnyvsbeavn" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ragre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pvgl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Mvc" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pbqr" },
+{ "Lucida Grande",
+ "Trg Sberpnfg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Obbxznexf" },
+{ "helvetica,arial,sans-serif,serif",
+ " 4nalguvat.pbz" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qverpgbevrf" },
+{ "helvetica,arial,sans-serif,serif",
+ " Qvfpbirel" },
+{ "helvetica,arial,sans-serif,serif",
+ " QvGrpu.pbz" },
+{ "helvetica,arial,sans-serif,serif",
+ " Ubbire'f" },
+{ "helvetica,arial,sans-serif,serif",
+ "Bayvar" },
+{ "helvetica,arial,sans-serif,serif",
+ " Vafvtug-Pbzchgvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cebqhpgf" },
+{ "helvetica,arial,sans-serif,serif",
+ " jbeyqylvairfgbe.pbz" },
+{ "helvetica,arial,sans-serif,serif",
+ "Fubj" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nyy" },
+{ "helvetica,arial,sans-serif,serif",
+ "Obbxznexf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Vzcbeg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbhe" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ivrj" },
+{ "helvetica,arial,sans-serif,serif",
+ "n" },
+{ "helvetica,arial,sans-serif,serif",
+ "cevagnoyr" },
+{ "helvetica,arial,sans-serif,serif",
+ "irefvba" },
+{ "helvetica,arial,sans-serif,serif",
+ "guvf" },
+{ "helvetica,arial,sans-serif,serif",
+ "\xc2""\xa9""" },
+{ "helvetica,arial,sans-serif,serif",
+ "1999,2000" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argfpncr," },
+{ "helvetica,arial,sans-serif,serif",
+ "Nyy" },
+{ "helvetica,arial,sans-serif,serif",
+ "Evtugf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erfreirq." },
+{ "helvetica,arial,sans-serif,serif",
+ "Yrtny" },
+{ "helvetica,arial,sans-serif,serif",
+ "&" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cevinpl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Abgvprf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Dhbgr" },
+{ "helvetica,arial,sans-serif,serif",
+ "grpuabybtl" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gvopb" },
+{ "helvetica,arial,sans-serif,serif",
+ "hfvat" },
+{ "helvetica,arial,sans-serif,serif",
+ "GVO/NpgvirRagrecevfr" },
+{ "helvetica,arial,sans-serif,serif",
+ "fbsgjner." },
+{ "helvetica,arial,sans-serif,serif",
+ "cebivqrq" },
+{ "helvetica,arial,sans-serif,serif",
+ "F&C" },
+{ "helvetica,arial,sans-serif,serif",
+ "PbzFgbpx" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cbegvbaf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erhgref" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ygq." },
+{ "helvetica,arial,sans-serif,serif",
+ "," },
+{ "helvetica,arial,sans-serif,serif",
+ "FcbegfGvpxre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Ragrecevfrf" },
+{ "helvetica,arial,sans-serif,serif",
+ "YC." },
+{ "helvetica,arial,sans-serif,serif",
+ "Nyy" },
+{ "helvetica,arial,sans-serif,serif",
+ "Evtugf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Erfreirq." },
+{ "helvetica,arial,sans-serif,serif",
+ "fvgr" },
+{ "helvetica,arial,sans-serif,serif",
+ "cbjrerq" },
+{ "helvetica,arial,sans-serif,serif",
+ "FhvgrFcbg" },
+{ "helvetica,arial,sans-serif,serif",
+ "freiref" },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "Ragre Pvgl Anzr be Mvc Pbqr" },
+{ "serif",
+ "6," },
+{ "serif",
+ "zl.argfpncr.pbz," },
+{ "serif",
+ "217" },
+{ "serif",
+ "1, 6, zl.argfpncr.pbz, 217" },
+{ "Helvetica,serif",
+ " - " },
+{ "Helvetica,serif",
+ "Zl Argfpncr" },
+{ "Helvetica,serif",
+ "Ohqql Pung" },
+{ "Helvetica,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "Fvta Va" },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "Zl Argfpncr" },
+{ "helvetica,arial,sans-serif,serif",
+ " Fngheqnl - Abirzore 11, 2000 - 5:20 NZ" },
+{ "helvetica,arial,sans-serif,serif",
+ " (RQG)" },
+{ "helvetica,arial,sans-serif,serif",
+ "V unir nyernql phfgbzvmrq zl " },
+{ "helvetica,arial,sans-serif,serif",
+ "cntr. Svaq Vg!" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znxr Guvf Zl Ubzrcntr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arrq uryc? Gel gur " },
+{ "helvetica,arial,sans-serif,serif",
+ "." },
+{ "sans-serif,Arial,Helvetica,serif",
+ "Zl Cbegsbyvb" },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "sans-serif,Arial,Helvetica,serif",
+ " " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "10,602.90 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ " " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "3,028.99 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "F&C 500" },
+{ "sans-serif,Arial,Helvetica,serif",
+ "1,365.98 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ "50.50 " },
+{ "sans-serif,Arial,Helvetica,serif",
+ " Cbegsbyvb Gbgny:" },
+{ "Lucida Grande",
+ "Dhbgr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Flzoby " },
+{ "helvetica,arial,sans-serif,serif",
+ "Anzr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Dhbgrf qrynlrq 20 zvaf." },
+{ "helvetica,arial,sans-serif,serif",
+ "* Vaqvpngrf Uv be Yb zbirzrag." },
+{ "helvetica,arial,sans-serif,serif",
+ "G B Q N L" },
+{ "helvetica,arial,sans-serif,serif",
+ "L R F G R E Q N L" },
+{ "helvetica,arial,sans-serif,serif",
+ "Gbqnl'f Srngherf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NC" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fngheqnl, Abirzore 11, 2000" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sybevqn Fgnaqbss" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gur jrrx'f raq snvyrq gb oevat na raq gb gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "pnzcnvta gb pubbfr Nzrevpn'f arkg cerfvqrag. " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sevqnl, gur Ohfu naq Tber pnzcnvtaf pynfurq " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "bire gur ibgr pbhag va Sybevqn, jurer gur " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erpbhag pbagvahrq guebhtu gur jrrxraq. Svany " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "erfhygf jrer abg rkcrpgrq hagvy Ghrfqnl, naq " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "nofragrr onyybgf naq ynjfhvgf pbhyq qrynl gur " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qrpvfvba rira shegure." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yngrfg Cbyvgvpny Arjf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Frnepu gur Jro" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Ohfvarff" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Zrzore" },
+{ "helvetica,arial,sans-serif,serif",
+ "Svaq n Crefba" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jung'f Arj" },
+{ "helvetica,arial,sans-serif,serif",
+ "Jung'f Pbby" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arg Frnepu Cntr" },
+{ "helvetica,arial,sans-serif,serif",
+ "Frnepu Gvcf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yrnea zber" },
+{ "helvetica,arial,sans-serif,serif",
+ "F R N E P U P N G R T B E V R F" },
+{ "helvetica,arial,sans-serif,serif",
+ "Znexrg Arjf ol POF ZnexrgJngpu" },
+{ "helvetica,arial,sans-serif,serif",
+ "GBQNL'F ZNEXRG ARJF" },
+{ "helvetica,arial,sans-serif,serif",
+ "(Abirzore 11, 5:03 NZ)" },
+{ "helvetica,arial,sans-serif,serif",
+ "N oehvfrq znexrg ybbxf sbe eryvrs" },
+{ "helvetica,arial,sans-serif,serif",
+ "Nsgre-ubhef genqvat uvg ol grpu jrnxarff" },
+{ "helvetica,arial,sans-serif,serif",
+ "Qryy funerf cnl cevpr sbe fybjre fnyrf sberpnfg" },
+{ "helvetica,arial,sans-serif,serif",
+ "Argpragre Nccf" },
+{ "helvetica,arial,sans-serif,serif",
+ "Purpx JroZnvy" },
+{ "helvetica,arial,sans-serif,serif",
+ "Serr Crefbany Rznvy!" },
+{ "helvetica,arial,sans-serif,serif",
+ "Vafgnag Zrffratre" },
+{ "helvetica,arial,sans-serif,serif",
+ "Pung jvgu lbhe sevraqf." },
+{ "helvetica,arial,sans-serif,serif",
+ "Purpx JroPnyraqne" },
+{ "helvetica,arial,sans-serif,serif",
+ "Lbhe Serr JroPnyraqne." },
+{ "helvetica,arial,sans-serif,serif",
+ "Fna Senapvfpb," },
+{ "helvetica,arial,sans-serif,serif",
+ " " },
+{ "helvetica,arial,sans-serif,serif",
+ "44\xc2""\xb0"" S / 7\xc2""\xb0"" P" },
+{ "helvetica,arial,sans-serif,serif",
+ "Cnegyl Pybhql" },
+{ "helvetica,arial,sans-serif,serif",
+ "Arj Lbex," },
+{ "helvetica,arial,sans-serif,serif",
+ "Arj Lbex" },
+{ "helvetica,arial,sans-serif,serif",
+ "51\xc2""\xb0"" S / 11\xc2""\xb0"" P" },
+{ "helvetica,arial,sans-serif,serif",
+ "Yvtug Enva" },
+{ "helvetica,arial,sans-serif,serif",
+ "Havgrq Xvatqbz" },
+{ "helvetica,arial,sans-serif,serif",
+ "54\xc2""\xb0"" S / 12\xc2""\xb0"" P" },
+{ "Lucida Grande",
+ "Trg Sberpnfg" },
+{ "helvetica,arial,sans-serif,serif",
+ " 4nalguvat.pbz Qverpgbevrf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ " " },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "PARG" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "|" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Arjf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Uneqjner" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Qbjaybnqf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Ohvyqre" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Tnzrf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Wbof" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Nhpgvbaf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Cevprf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Grpu" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Uryc" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ "Serr" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ "Rznvy" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ " " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zrna" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ohfvarff" },
+{ "Arial, Helvetica,serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "PARG" },
+{ "Arial, Helvetica,serif",
+ ":" },
+{ "Arial, Helvetica,serif",
+ "Arjf" },
+{ "arial,helvetica,serif",
+ "Ynfg" },
+{ "arial,helvetica,serif",
+ "Hcqngrq:" },
+{ "arial,helvetica,serif",
+ "Qrp" },
+{ "arial,helvetica,serif",
+ "13," },
+{ "arial,helvetica,serif",
+ "10:15" },
+{ "arial,helvetica,serif",
+ "c.z." },
+{ "arial,helvetica,serif",
+ "CG" },
+{ "Arial, Helvetica,serif",
+ "Serr" },
+{ "Arial, Helvetica,serif",
+ "Erny-Gvzr" },
+{ "Arial, Helvetica,serif",
+ "Dhbgrf" },
+{ "arial,helvetica,serif",
+ "Frnepu" },
+{ "serif",
+ " " },
+{ "Lucida Grande",
+ "Va" },
+{ "Lucida Grande",
+ "Nyy" },
+{ "Lucida Grande",
+ "PARG " },
+{ "Lucida Grande",
+ "Tb!" },
+{ "Arial, Helvetica,serif",
+ "\xc2""\xb7"" " },
+{ "Arial, Helvetica,serif",
+ "Nqinaprq" },
+{ "Arial, Helvetica,serif",
+ "Arjf.pbz" },
+{ "Arial, Helvetica,serif",
+ "va" },
+{ "Arial, Helvetica,serif",
+ "lbhe" },
+{ "Arial, Helvetica,serif",
+ "cnyz" },
+{ "arial,helvetica,serif",
+ "Svanapr" },
+{ "arial,helvetica,serif",
+ "naq" },
+{ "arial,helvetica,serif",
+ "Vairfgvat" },
+{ "arial,helvetica,serif",
+ " |" },
+{ "arial,helvetica,serif",
+ "Zl" },
+{ "arial,helvetica,serif",
+ "Cbegsbyvb" },
+{ "Arial, Helvetica,serif",
+ "Pbafhzref" },
+{ "Arial, Helvetica,serif",
+ "fcraq" },
+{ "Arial, Helvetica,serif",
+ "erpbeq" },
+{ "Arial, Helvetica,serif",
+ "nzbhagf" },
+{ "Arial, Helvetica,serif",
+ "bayvar" },
+{ "serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "Zbivrf" },
+{ "Arial, Helvetica,serif",
+ "bire" },
+{ "Arial, Helvetica,serif",
+ "pryy" },
+{ "Arial, Helvetica,serif",
+ "cubarf" },
+{ "Arial, Helvetica,serif",
+ "abg" },
+{ "Arial, Helvetica,serif",
+ "lrg" },
+{ "Arial, Helvetica,serif",
+ "sbphf" },
+{ "Arial, Helvetica,serif",
+ "Jvyy" },
+{ "Arial, Helvetica,serif",
+ "gur" },
+{ "Arial, Helvetica,serif",
+ "arkg" },
+{ "Arial, Helvetica,serif",
+ "cerfvqrag" },
+{ "Arial, Helvetica,serif",
+ "nygre" },
+{ "Arial, Helvetica,serif",
+ "jbeyq?" },
+{ "arial, helvetica,serif",
+ "Whqtr" },
+{ "arial, helvetica,serif",
+ "ershfrf" },
+{ "arial, helvetica,serif",
+ "gb" },
+{ "arial, helvetica,serif",
+ "oybpx" },
+{ "arial, helvetica,serif",
+ "JbeyqPbz," },
+{ "arial, helvetica,serif",
+ "Vagrezrqvn" },
+{ "arial, helvetica,serif",
+ "zretre" },
+{ "Arial, Helvetica,serif",
+ "N" },
+{ "Arial, Helvetica,serif",
+ "Qrynjner" },
+{ "Arial, Helvetica,serif",
+ "whqtr" },
+{ "Arial, Helvetica,serif",
+ "ershfrf" },
+{ "Arial, Helvetica,serif",
+ "gb" },
+{ "Arial, Helvetica,serif",
+ "oybpx" },
+{ "Arial, Helvetica,serif",
+ "gur" },
+{ "Arial, Helvetica,serif",
+ "pbzzhavpngvbaf" },
+{ "Arial, Helvetica,serif",
+ "zretre" },
+{ "Arial, Helvetica,serif",
+ "nf" },
+{ "Arial, Helvetica,serif",
+ "erdhrfgrq" },
+{ "Arial, Helvetica,serif",
+ "ol" },
+{ "Arial, Helvetica,serif",
+ "zvabevgl" },
+{ "Arial, Helvetica,serif",
+ "funerubyqref" },
+{ "Arial, Helvetica,serif",
+ "bs" },
+{ "Arial, Helvetica,serif",
+ "Qvtrk," },
+{ "Arial, Helvetica,serif",
+ "juvpu" },
+{ "Arial, Helvetica,serif",
+ "vf" },
+{ "Arial, Helvetica,serif",
+ "pbagebyyrq" },
+{ "Arial, Helvetica,serif",
+ "Vagrezrqvn." },
+{ "Arial, Helvetica,serif",
+ "Qrprzore" },
+{ "Arial, Helvetica,serif",
+ "13," },
+{ "Arial, Helvetica,serif",
+ "10:15" },
+{ "Arial, Helvetica,serif",
+ "c.z." },
+{ "Arial, Helvetica,serif",
+ "CG" },
+{ "Arial, Helvetica,serif",
+ "Pbzzhavpngvbaf" },
+{ "arial, helvetica,serif",
+ "VOZ" },
+{ "arial, helvetica,serif",
+ "fuvc" },
+{ "arial, helvetica,serif",
+ "raunaprq" },
+{ "arial, helvetica,serif",
+ "znvasenzr," },
+{ "arial, helvetica,serif",
+ "fgbentr" },
+{ "arial, helvetica,serif",
+ "flfgrzf" },
+{ "Arial, Helvetica,serif",
+ "Ovt" },
+{ "Arial, Helvetica,serif",
+ "Oyhr" },
+{ "Arial, Helvetica,serif",
+ "fpurqhyrq" },
+{ "Arial, Helvetica,serif",
+ "fgneg" },
+{ "Arial, Helvetica,serif",
+ "fuvccvat" },
+{ "Arial, Helvetica,serif",
+ "vgf" },
+{ "Arial, Helvetica,serif",
+ "svefg" },
+{ "Arial, Helvetica,serif",
+ "T7" },
+{ "Arial, Helvetica,serif",
+ "znvasenzr," },
+{ "Arial, Helvetica,serif",
+ "pbqr-anzrq" },
+{ "Arial, Helvetica,serif",
+ "Serrjnl," },
+{ "Arial, Helvetica,serif",
+ "jryy" },
+{ "Arial, Helvetica,serif",
+ "na" },
+{ "Arial, Helvetica,serif",
+ "raunaprq" },
+{ "Arial, Helvetica,serif",
+ "irefvba" },
+{ "Arial, Helvetica,serif",
+ "Funex" },
+{ "Arial, Helvetica,serif",
+ "fgbentr" },
+{ "Arial, Helvetica,serif",
+ "flfgrz." },
+{ "Arial, Helvetica,serif",
+ "9:50" },
+{ "Arial, Helvetica,serif",
+ "Ragrecevfr" },
+{ "Arial, Helvetica,serif",
+ "Pbzchgvat" },
+{ "arial, helvetica,serif",
+ "IN" },
+{ "arial, helvetica,serif",
+ "obyfgref" },
+{ "arial, helvetica,serif",
+ "chfu" },
+{ "arial, helvetica,serif",
+ "sbe" },
+{ "arial, helvetica,serif",
+ "ovt" },
+{ "arial, helvetica,serif",
+ "ohfvarff" },
+{ "arial, helvetica,serif",
+ "phfgbzref" },
+{ "Arial, Helvetica,serif",
+ "IN" },
+{ "Arial, Helvetica,serif",
+ "Yvahk" },
+{ "Arial, Helvetica,serif",
+ "Flfgrzf," },
+{ "Arial, Helvetica,serif",
+ "ubcvat" },
+{ "Arial, Helvetica,serif",
+ "vg'yy" },
+{ "Arial, Helvetica,serif",
+ "or" },
+{ "Arial, Helvetica,serif",
+ "pneevrq" },
+{ "Arial, Helvetica,serif",
+ "nybat" },
+{ "Arial, Helvetica,serif",
+ "va" },
+{ "Arial, Helvetica,serif",
+ "qensg" },
+{ "Arial, Helvetica,serif",
+ "VOZ'f" },
+{ "Arial, Helvetica,serif",
+ "rzobyqrarq" },
+{ "Arial, Helvetica,serif",
+ "chfu," },
+{ "Arial, Helvetica,serif",
+ "nqwhfgvat" },
+{ "Arial, Helvetica,serif",
+ "ohfvarff" },
+{ "Arial, Helvetica,serif",
+ "gel" },
+{ "Arial, Helvetica,serif",
+ "jbb" },
+{ "Arial, Helvetica,serif",
+ "yrff" },
+{ "Arial, Helvetica,serif",
+ "ninagr-tneqr" },
+{ "Arial, Helvetica,serif",
+ "phfgbzref." },
+{ "Arial, Helvetica,serif",
+ "9:20" },
+{ "Arial, Helvetica,serif",
+ "\xe2""\x80""\xa2""" },
+{ "Arial, Helvetica,serif",
+ "Ragrecevfr" },
+{ "Arial, Helvetica,serif",
+ "Pbzchgvat" },
+{ "Arial, Helvetica,serif",
+ "R-Ohfvarff" },
+{ "Arial, Helvetica,serif",
+ "Pbzzhavpngvbaf" },
+{ "Arial, Helvetica,serif",
+ "Ragregnvazrag" },
+{ "Arial, Helvetica,serif",
+ "&" },
+{ "Arial, Helvetica,serif",
+ "Zrqvn" },
+{ "Arial, Helvetica,serif",
+ "Crefbany" },
+{ "Arial, Helvetica,serif",
+ "Grpuabybtl" },
+{ "Arial, Helvetica,serif",
+ "Fcrpvny" },
+{ "Arial, Helvetica,serif",
+ "Ercbegf" },
+{ "Arial, Helvetica,serif",
+ "PARG" },
+{ "Arial, Helvetica,serif",
+ "Vairfgbe" },
+{ "Arial, Helvetica,serif",
+ "GI" },
+{ "Arial, Helvetica,serif",
+ "Enqvb" },
+{ "Arial, Helvetica,serif",
+ "Crefcrpgvirf" },
+{ "Arial, Helvetica,serif",
+ "Arjfznxref" },
+{ "Arial, Helvetica,serif",
+ "Ehzbe" },
+{ "Arial, Helvetica,serif",
+ "Zvyy" },
+{ "Arial, Helvetica,serif",
+ "Bar" },
+{ "Arial, Helvetica,serif",
+ "Jrrx" },
+{ "Arial, Helvetica,serif",
+ "Ivrj" },
+{ "Arial, Helvetica,serif",
+ "Vafgnag" },
+{ "Arial, Helvetica,serif",
+ "Nyregf" },
+{ "Arial, Helvetica,serif",
+ "Pbeerpgvbaf" },
+{ "Arial, Helvetica,serif",
+ " PARG" },
+{ "Arial, Helvetica,serif",
+ "Arjfyrggref" },
+{ "Arial, Helvetica,serif",
+ "Arjf.pbz" },
+{ "Arial, Helvetica,serif",
+ "Qvfcngpu" },
+{ "Arial, Helvetica,serif",
+ "Vairfgbe" },
+{ "Lucida Grande",
+ "Fhofpevor" },
+{ "Arial, Helvetica,serif",
+ "Nyy" },
+{ "Lucida Grande",
+ "Lbhe rznvy urer" },
+{ "Arial, Helvetica,serif",
+ "Xrl" },
+{ "Arial, Helvetica,serif",
+ "rkrp" },
+{ "Arial, Helvetica,serif",
+ "fnlf" },
+{ "Arial, Helvetica,serif",
+ "pbecbengr" },
+{ "Arial, Helvetica,serif",
+ "chfu" },
+{ "Arial, Helvetica,serif",
+ "cnlvat" },
+{ "Arial, Helvetica,serif",
+ "bss" },
+{ "arial, helvetica,serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "SGP" },
+{ "Arial, Helvetica,serif",
+ "gb" },
+{ "Arial, Helvetica,serif",
+ "ibgr" },
+{ "Arial, Helvetica,serif",
+ "ba" },
+{ "Arial, Helvetica,serif",
+ "NBY" },
+{ "Arial, Helvetica,serif",
+ "Gvzr" },
+{ "Arial, Helvetica,serif",
+ "Jneare;" },
+{ "Arial, Helvetica,serif",
+ "SPP" },
+{ "Arial, Helvetica,serif",
+ "erivrj" },
+{ "Arial, Helvetica,serif",
+ "ybbzf" },
+{ "Arial, Helvetica,serif",
+ "Pybfrf" },
+{ "Arial, Helvetica,serif",
+ "VOZ" },
+{ "Arial, Helvetica,serif",
+ "freire" },
+{ "Arial, Helvetica,serif",
+ "znexrg" },
+{ "arial, helvetica,serif",
+ "Cnyz" },
+{ "arial, helvetica,serif",
+ "unf" },
+{ "arial, helvetica,serif",
+ "zhfvp" },
+{ "arial, helvetica,serif",
+ "ba" },
+{ "arial, helvetica,serif",
+ "vgf" },
+{ "arial, helvetica,serif",
+ "zvaq" },
+{ "Arial, Helvetica,serif",
+ "ubfg" },
+{ "Arial, Helvetica,serif",
+ "arj" },
+{ "Arial, Helvetica,serif",
+ "srngherf" },
+{ "Arial, Helvetica,serif",
+ "znl" },
+{ "Arial, Helvetica,serif",
+ "ninvynoyr" },
+{ "Arial, Helvetica,serif",
+ "sbe" },
+{ "Arial, Helvetica,serif",
+ "Cnyz" },
+{ "Arial, Helvetica,serif",
+ "2001," },
+{ "Arial, Helvetica,serif",
+ "n" },
+{ "Arial, Helvetica,serif",
+ "pbzcnal" },
+{ "Arial, Helvetica,serif",
+ "rkrphgvir" },
+{ "Arial, Helvetica,serif",
+ "fnlf," },
+{ "Arial, Helvetica,serif",
+ "ohg" },
+{ "Arial, Helvetica,serif",
+ "abg" },
+{ "Arial, Helvetica,serif",
+ "vs" },
+{ "Arial, Helvetica,serif",
+ "gurl" },
+{ "Arial, Helvetica,serif",
+ "pbzcebzvfr" },
+{ "Arial, Helvetica,serif",
+ "cyngsbez'f" },
+{ "Arial, Helvetica,serif",
+ "ryrtnapr" },
+{ "Arial, Helvetica,serif",
+ "naq" },
+{ "Arial, Helvetica,serif",
+ "fvzcyvpvgl." },
+{ "Arial, Helvetica,serif",
+ "8:40" },
+{ "Arial, Helvetica,serif",
+ "Crefbany" },
+{ "Arial, Helvetica,serif",
+ "Grpuabybtl" },
+{ "arial, helvetica,serif",
+ "Tber" },
+{ "arial, helvetica,serif",
+ "fheeraqref" },
+{ "arial, helvetica,serif",
+ "uvf" },
+{ "arial, helvetica,serif",
+ "Juvgr" },
+{ "arial, helvetica,serif",
+ "Ubhfr" },
+{ "arial, helvetica,serif",
+ "ovq" },
+{ "arial,helvetica,serif",
+ "hcqngr" },
+{ "Arial, Helvetica,serif",
+ "Ny" },
+{ "Arial, Helvetica,serif",
+ "Tber" },
+{ "Arial, Helvetica,serif",
+ "fheeraqref" },
+{ "Arial, Helvetica,serif",
+ "uvf" },
+{ "Arial, Helvetica,serif",
+ "onggyr" },
+{ "Arial, Helvetica,serif",
+ "Juvgr" },
+{ "Arial, Helvetica,serif",
+ "Ubhfr," },
+{ "Arial, Helvetica,serif",
+ "npprcgvat" },
+{ "Arial, Helvetica,serif",
+ "Trbetr" },
+{ "Arial, Helvetica,serif",
+ "J." },
+{ "Arial, Helvetica,serif",
+ "Ohfu" },
+{ "Arial, Helvetica,serif",
+ "43eq" },
+{ "Arial, Helvetica,serif",
+ "cerfvqrag" },
+{ "Arial, Helvetica,serif",
+ "Havgrq" },
+{ "Arial, Helvetica,serif",
+ "Fgngrf." },
+{ "Arial, Helvetica,serif",
+ "6:35" },
+{ "Arial, Helvetica,serif",
+ "Ragregnvazrag" },
+{ "Arial, Helvetica,serif",
+ "&" },
+{ "Arial, Helvetica,serif",
+ "Zrqvn" },
+{ "arial, helvetica,serif",
+ "Arg" },
+{ "arial, helvetica,serif",
+ "pbafhygnapl" },
+{ "arial, helvetica,serif",
+ "Betnavp" },
+{ "arial, helvetica,serif",
+ "fynfurf" },
+{ "arial, helvetica,serif",
+ "jbex" },
+{ "arial, helvetica,serif",
+ "sbepr" },
+{ "Arial, Helvetica,serif",
+ "Gur" },
+{ "Arial, Helvetica,serif",
+ "Vagrearg" },
+{ "Arial, Helvetica,serif",
+ "pbafhygvat" },
+{ "Arial, Helvetica,serif",
+ "cynaf" },
+{ "Arial, Helvetica,serif",
+ "phg" },
+{ "Arial, Helvetica,serif",
+ "270" },
+{ "Arial, Helvetica,serif",
+ "wbof" },
+{ "Arial, Helvetica,serif",
+ "pybfr" },
+{ "Arial, Helvetica,serif",
+ "gjb" },
+{ "Arial, Helvetica,serif",
+ "bssvprf" },
+{ "Arial, Helvetica,serif",
+ "fnir" },
+{ "Arial, Helvetica,serif",
+ "rkcrpgrq" },
+{ "Arial, Helvetica,serif",
+ "$25" },
+{ "Arial, Helvetica,serif",
+ "zvyyvba" },
+{ "Arial, Helvetica,serif",
+ "lrne." },
+{ "Arial, Helvetica,serif",
+ "5:00" },
+{ "Arial, Helvetica,serif",
+ "R-Ohfvarff" },
+{ "arial, helvetica,serif",
+ "Perngvir" },
+{ "arial, helvetica,serif",
+ "Cynarg" },
+{ "arial, helvetica,serif",
+ "phgf" },
+{ "arial, helvetica,serif",
+ "fgnss" },
+{ "arial, helvetica,serif",
+ "qrfcvgr" },
+{ "arial, helvetica,serif",
+ "shaqvat" },
+{ "Arial, Helvetica,serif",
+ "bayvar" },
+{ "Arial, Helvetica,serif",
+ "ragregnvazrag" },
+{ "Arial, Helvetica,serif",
+ "fhccyvre" },
+{ "Arial, Helvetica,serif",
+ "ynlf" },
+{ "Arial, Helvetica,serif",
+ "bss" },
+{ "Arial, Helvetica,serif",
+ "70" },
+{ "Arial, Helvetica,serif",
+ "rzcyblrrf," },
+{ "Arial, Helvetica,serif",
+ "whfg" },
+{ "Arial, Helvetica,serif",
+ "srj" },
+{ "Arial, Helvetica,serif",
+ "jrrxf" },
+{ "Arial, Helvetica,serif",
+ "nsgre" },
+{ "Arial, Helvetica,serif",
+ "vg" },
+{ "Arial, Helvetica,serif",
+ "frpherq" },
+{ "Arial, Helvetica,serif",
+ "$30" },
+{ "Arial, Helvetica,serif",
+ "shaqvat." },
+{ "Arial, Helvetica,serif",
+ "4:45" },
+{ "Arial, Helvetica,serif",
+ "Pna" },
+{ "Arial, Helvetica,serif",
+ "800.pbz" },
+{ "Arial, Helvetica,serif",
+ "oevat" },
+{ "Arial, Helvetica,serif",
+ "onpx" },
+{ "Arial, Helvetica,serif",
+ "vgf" },
+{ "Arial, Helvetica,serif",
+ "oyhr-evooba" },
+{ "Arial, Helvetica,serif",
+ "qnlf?" },
+{ "Arial, Helvetica,serif",
+ "Ohfvarff" },
+{ "Arial, Helvetica,serif",
+ "Jrrx" },
+{ "Arial, Helvetica,serif",
+ "H.X." },
+{ "Arial, Helvetica,serif",
+ "tnf" },
+{ "Arial, Helvetica,serif",
+ "fgngvbaf" },
+{ "Arial, Helvetica,serif",
+ "bssre" },
+{ "Arial, Helvetica,serif",
+ "npprff" },
+{ "Arial, Helvetica,serif",
+ "OOP" },
+{ "Arial, Helvetica,serif",
+ "Bayvar" },
+{ "Arial, Helvetica,serif",
+ "Gur" },
+{ "Arial, Helvetica,serif",
+ "qnl" },
+{ "Arial, Helvetica,serif",
+ "V" },
+{ "Arial, Helvetica,serif",
+ "xvyyrq" },
+{ "Arial, Helvetica,serif",
+ "zl" },
+{ "Arial, Helvetica,serif",
+ "qbg-pbz" },
+{ "Arial, Helvetica,serif",
+ "Fnyba" },
+{ "Arial, Helvetica,serif",
+ "Grpu" },
+{ "Arial, Helvetica,serif",
+ "svezf" },
+{ "Arial, Helvetica,serif",
+ "qenva" },
+{ "Arial, Helvetica,serif",
+ "cbjre" },
+{ "Arial, Helvetica,serif",
+ "tevq" },
+{ "Arial, Helvetica,serif",
+ "Ybf" },
+{ "Arial, Helvetica,serif",
+ "Natryrf" },
+{ "Arial, Helvetica,serif",
+ "Gvzrf" },
+{ "Arial, Helvetica,serif",
+ "Jnvgvat" },
+{ "Arial, Helvetica,serif",
+ "sbe" },
+{ "Arial, Helvetica,serif",
+ "fzneg" },
+{ "Arial, Helvetica,serif",
+ "pneq" },
+{ "Arial, Helvetica,serif",
+ "snyy" },
+{ "Arial, Helvetica,serif",
+ "Sbeorf" },
+{ "Arial, Helvetica,serif",
+ "Zber" },
+{ "Arial, Helvetica,serif",
+ "Sebz" },
+{ "Arial, Helvetica,serif",
+ "Nebhaq" },
+{ "Arial, Helvetica,serif",
+ "Jro" },
+{ "arial,helvetica,serif",
+ " " },
+{ "arial,helvetica,serif",
+ "Zber" },
+{ "arial,helvetica,serif",
+ "arjf" },
+{ "arial,helvetica,serif",
+ "sebz" },
+{ "arial,helvetica,serif",
+ "nebhaq" },
+{ "arial,helvetica,serif",
+ "gur" },
+{ "arial,helvetica,serif",
+ "Jro" },
+{ "Arial, Helvetica,serif",
+ "VOZ" },
+{ "Arial, Helvetica,serif",
+ "fuvc" },
+{ "Arial, Helvetica,serif",
+ "flfgrzf" },
+{ "Arial, Helvetica,serif",
+ "obyfgref" },
+{ "Arial, Helvetica,serif",
+ "chfu" },
+{ "Arial, Helvetica,serif",
+ "ovt" },
+{ "Arial, Helvetica,serif",
+ "phfgbzref" },
+{ "Arial, Helvetica,serif",
+ "znantre" },
+{ "Arial, Helvetica,serif",
+ "pbaivpgrq" },
+{ "Arial, Helvetica,serif",
+ "xvpxonpx" },
+{ "Arial, Helvetica,serif",
+ "fpurzr" },
+{ "Arial, Helvetica,serif",
+ "Ubhfr" },
+{ "Arial, Helvetica,serif",
+ "ovq" },
+{ "Arial, Helvetica,serif",
+ "Perngvir" },
+{ "Arial, Helvetica,serif",
+ "Cynarg" },
+{ "Arial, Helvetica,serif",
+ "phgf" },
+{ "Arial, Helvetica,serif",
+ "fgnss" },
+{ "Arial, Helvetica,serif",
+ "qrfcvgr" },
+{ "Arial, Helvetica,serif",
+ "shaqvat" },
+{ "Arial, Helvetica,serif",
+ "ErnyAnzrf" },
+{ "Arial, Helvetica,serif",
+ "frrf" },
+{ "Arial, Helvetica,serif",
+ "ynlbssf" },
+{ "Arial, Helvetica,serif",
+ "fvtany" },
+{ "Arial, Helvetica,serif",
+ "fhpprff" },
+{ "Arial, Helvetica,serif",
+ "Arg" },
+{ "Arial, Helvetica,serif",
+ "pbafhygnapl" },
+{ "Arial, Helvetica,serif",
+ "Betnavp" },
+{ "Arial, Helvetica,serif",
+ "fynfurf" },
+{ "Arial, Helvetica,serif",
+ "jbex" },
+{ "Arial, Helvetica,serif",
+ "sbepr" },
+{ "Arial, Helvetica,serif",
+ "Pbbyfnivatf" },
+{ "Arial, Helvetica,serif",
+ "frggyrf" },
+{ "Arial, Helvetica,serif",
+ "cngrag" },
+{ "Arial, Helvetica,serif",
+ "fhvg" },
+{ "Arial, Helvetica,serif",
+ "Grpu" },
+{ "Arial, Helvetica,serif",
+ "fgbpxf" },
+{ "Arial, Helvetica,serif",
+ "avpxrq" },
+{ "Arial, Helvetica,serif",
+ "rneavatf" },
+{ "Arial, Helvetica,serif",
+ "jneavatf" },
+{ "Arial, Helvetica,serif",
+ "Whqtr" },
+{ "Arial, Helvetica,serif",
+ "JbeyqPbz," },
+{ "Arial, Helvetica,serif",
+ "Vagrezrqvn" },
+{ "Arial, Helvetica,serif",
+ "Funerubyqref" },
+{ "Arial, Helvetica,serif",
+ "fhr" },
+{ "Arial, Helvetica,serif",
+ "Fcevag" },
+{ "Arial, Helvetica,serif",
+ "bire" },
+{ "Arial, Helvetica,serif",
+ "$1.7" },
+{ "Arial, Helvetica,serif",
+ "ovyyvba" },
+{ "Arial, Helvetica,serif",
+ "bcgvbaf" },
+{ "Arial, Helvetica,serif",
+ "Yriry" },
+{ "Arial, Helvetica,serif",
+ "3" },
+{ "Arial, Helvetica,serif",
+ "obbfg" },
+{ "Arial, Helvetica,serif",
+ "fnyrf" },
+{ "Arial, Helvetica,serif",
+ "rfgvzngr," },
+{ "Arial, Helvetica,serif",
+ "nanylfg" },
+{ "Arial, Helvetica,serif",
+ "fnlf" },
+{ "Arial, Helvetica,serif",
+ "unf" },
+{ "Arial, Helvetica,serif",
+ "zhfvp" },
+{ "Arial, Helvetica,serif",
+ "ba" },
+{ "Arial, Helvetica,serif",
+ "zvaq" },
+{ "Arial, Helvetica,serif",
+ "Avagraqb" },
+{ "Arial, Helvetica,serif",
+ "24" },
+{ "Arial, Helvetica,serif",
+ "Tnzr" },
+{ "Arial, Helvetica,serif",
+ "Oblf" },
+{ "Arial, Helvetica,serif",
+ "cebzvfrf" },
+{ "Arial, Helvetica,serif",
+ "jbeyq" },
+{ "arial,helvetica,serif",
+ "Qb" },
+{ "arial,helvetica,serif",
+ "lbh" },
+{ "arial,helvetica,serif",
+ "jnag" },
+{ "arial,helvetica,serif",
+ "gb" },
+{ "arial,helvetica,serif",
+ "xabj" },
+{ "arial,helvetica,serif",
+ "zber?" },
+{ "arial,helvetica,serif",
+ "Pyvpx" },
+{ "arial,helvetica,serif",
+ "urer" },
+{ "arial,helvetica,serif",
+ "sbe" },
+{ "arial,helvetica,serif",
+ "n" },
+{ "arial,helvetica,serif",
+ "jrrx'f" },
+{ "arial,helvetica,serif",
+ "jbegu" },
+{ "arial,helvetica,serif",
+ "bs" },
+{ "arial,helvetica,serif",
+ "urnqyvarf." },
+{ "arial,helvetica,serif",
+ "Yngrfg" },
+{ "arial,helvetica,serif",
+ "Arjf.pbz" },
+{ "Arial, Helvetica,serif",
+ "Lnat" },
+{ "Arial, Helvetica,serif",
+ "jnagf" },
+{ "Arial, Helvetica,serif",
+ "fgernzvat" },
+{ "Arial, Helvetica,serif",
+ "znqr" },
+{ "Arial, Helvetica,serif",
+ "rnfl" },
+{ "Arial, Helvetica,serif",
+ "Wreel" },
+{ "Arial, Helvetica,serif",
+ "Lnat," },
+{ "Arial, Helvetica,serif",
+ "Lnubb" },
+{ "Arial, Helvetica,serif",
+ "pb-sbhaqre" },
+{ "Arial, Helvetica,serif",
+ "snfuvba:" },
+{ "Arial, Helvetica,serif",
+ "Pynhqvn" },
+{ "Arial, Helvetica,serif",
+ "Fpuvssre" },
+{ "Arial, Helvetica,serif",
+ "gbhgf" },
+{ "Arial, Helvetica,serif",
+ "oyhr" },
+{ "Arial, Helvetica,serif",
+ "unaquryq" },
+{ "Arial, Helvetica,serif",
+ "Pynhqvn" },
+{ "Arial, Helvetica,serif",
+ "Fpuvssre," },
+{ "Arial, Helvetica,serif",
+ "fhcrezbqry" },
+{ "Arial, Helvetica,serif",
+ "ivqrb" },
+{ "Arial, Helvetica,serif",
+ "SOV" },
+{ "Arial, Helvetica,serif",
+ "chyyf" },
+{ "Arial, Helvetica,serif",
+ "qbja" },
+{ "Arial, Helvetica,serif",
+ "rkcbfrq" },
+{ "Arial, Helvetica,serif",
+ "pneq" },
+{ "Arial, Helvetica,serif",
+ "ahzoref;" },
+{ "Arial, Helvetica,serif",
+ "oernpu" },
+{ "Arial, Helvetica,serif",
+ "svkrq" },
+{ "Arial, Helvetica,serif",
+ "Vagry" },
+{ "Arial, Helvetica,serif",
+ "cbfgcbar" },
+{ "Arial, Helvetica,serif",
+ "puvc" },
+{ "Arial, Helvetica,serif",
+ "snpgbel" },
+{ "Arial, Helvetica,serif",
+ "Verynaq" },
+{ "Arial, Helvetica,serif",
+ "Pbzcnd" },
+{ "Arial, Helvetica,serif",
+ "zhfg" },
+{ "Arial, Helvetica,serif",
+ "gnc" },
+{ "Arial, Helvetica,serif",
+ "fnyrf," },
+{ "Arial, Helvetica,serif",
+ "freivprf" },
+{ "Arial, Helvetica,serif",
+ "cebsvgf" },
+{ "Arial, Helvetica,serif",
+ "NG&G" },
+{ "Arial, Helvetica,serif",
+ "rntre" },
+{ "Arial, Helvetica,serif",
+ "rkpunatr" },
+{ "Arial, Helvetica,serif",
+ "pbageby" },
+{ "Arial, Helvetica,serif",
+ "pncvgny" },
+{ "Arial, Helvetica,serif",
+ "xvaqre," },
+{ "Arial, Helvetica,serif",
+ "tragyre" },
+{ "Arial, Helvetica,serif",
+ "Jnyy" },
+{ "Arial, Helvetica,serif",
+ "Fgerrg?" },
+{ "Arial, Helvetica,serif",
+ "Zvpebfbsg" },
+{ "Arial, Helvetica,serif",
+ "fhcrezbqry" },
+{ "Arial, Helvetica,serif",
+ "uvg" },
+{ "Arial, Helvetica,serif",
+ "pvephvg" },
+{ "Arial, Helvetica,serif",
+ "Dhbgr" },
+{ "Arial, Helvetica,serif",
+ "Fancfubg" },
+{ "ms sans serif, geneva,serif",
+ "ANFQND" },
+{ "ms sans serif, geneva,serif",
+ "QWVN" },
+{ "ms sans serif, geneva,serif",
+ "F&C" },
+{ "ms sans serif, geneva,serif",
+ "500" },
+{ "ms sans serif, geneva,serif",
+ "2822.77" },
+{ "ms sans serif, geneva,serif",
+ "10794.44" },
+{ "ms sans serif, geneva,serif",
+ "1359.99" },
+{ "ms sans serif,geneva,serif",
+ "-109.00" },
+{ "ms sans serif,geneva,serif",
+ "+26.17" },
+{ "ms sans serif,geneva,serif",
+ "-11.19" },
+{ "serif",
+ "\xc2""\xb7"" " },
+{ "ms sans serif,geneva,helvetica,serif",
+ "Erny-Gvzr" },
+{ "ms sans serif,geneva,helvetica,serif",
+ "Dhbgrf" },
+{ "arial,helvetica,serif",
+ "Ragre" },
+{ "arial,helvetica,serif",
+ "flzoby:" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ "Flzoby Ybbxhc" },
+{ "arial,helvetica,serif",
+ "Zber" },
+{ "arial,helvetica,serif",
+ "sebz" },
+{ "arial,helvetica,serif",
+ "PARG" },
+{ "arial,helvetica,serif",
+ "Vairfgbe" },
+{ "ms sans serif,geneva,helvetica,serif",
+ "Dhbgrf" },
+{ "ms sans serif,geneva,helvetica,serif",
+ "qrynlrq" },
+{ "ms sans serif,geneva,helvetica,serif",
+ "20+" },
+{ "ms sans serif,geneva,helvetica,serif",
+ "zvahgrf" },
+{ "Arial, Helvetica,serif",
+ "Rtturnq.pbz" },
+{ "geneva, ms sans serif,serif",
+ "Gur" },
+{ "geneva, ms sans serif,serif",
+ "Pbzchgre" },
+{ "geneva, ms sans serif,serif",
+ "Fgber" },
+{ "geneva, ms sans serif,serif",
+ "Cnvag" },
+{ "geneva, ms sans serif,serif",
+ "Fubc" },
+{ "geneva, ms sans serif,serif",
+ "Ceb" },
+{ "geneva, ms sans serif,serif",
+ "7" },
+{ "geneva, ms sans serif,serif",
+ "CYRKJEVGRE" },
+{ "geneva, ms sans serif,serif",
+ "12K10K32" },
+{ "geneva, ms sans serif,serif",
+ "IvrjFbavp" },
+{ "geneva, ms sans serif,serif",
+ "17\"" },
+{ "geneva, ms sans serif,serif",
+ "Zbavgbe" },
+{ "geneva, arial,serif",
+ "FCBAFBERQ" },
+{ "geneva, arial,serif",
+ "YVAXF" },
+{ "Arial, Helvetica,serif",
+ "Terng" },
+{ "Arial, Helvetica,serif",
+ "Qrnyf!" },
+{ "Arial, Helvetica,serif",
+ "Qvtvgny" },
+{ "Arial, Helvetica,serif",
+ "Pnzrenf" },
+{ "Arial, Helvetica,serif",
+ "Pbzchgref" },
+{ "Arial, Helvetica,serif",
+ "Bcgvbaf:" },
+{ "Arial, Helvetica,serif",
+ "Bar" },
+{ "Arial, Helvetica,serif",
+ "Jrrx" },
+{ "Arial, Helvetica,serif",
+ "Ivrj" },
+{ "Arial, Helvetica,serif",
+ " |" },
+{ "Arial, Helvetica,serif",
+ "Fraq" },
+{ "Arial, Helvetica,serif",
+ "hf" },
+{ "Arial, Helvetica,serif",
+ "lbhe" },
+{ "Arial, Helvetica,serif",
+ "arjf" },
+{ "Arial, Helvetica,serif",
+ "gvcf" },
+{ "Arial, Helvetica,serif",
+ "Arjf" },
+{ "Arial, Helvetica,serif",
+ "Tb" },
+{ "Arial, Helvetica,serif",
+ "Nqinaprq" },
+{ "Arial, Helvetica,serif",
+ "Frnepu" },
+{ "Arial, Helvetica,serif",
+ "Pbeerpgvbaf" },
+{ "ms sans serif, geneva,serif",
+ "Pbagnpg" },
+{ "ms sans serif, geneva,serif",
+ "hf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "PARG" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Freivprf:" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Nhpgvbaf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "\xc2""\xb7""" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Purpx Yngrfg Cevprf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Qngn Freivprf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Qbjaybnqf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Ragrecevfr Ohfvarff" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Svaq n Jro Ubfg" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Svaq na VFC" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Serr Arjfyrggref" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Tnqtrgf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Tnzrf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Uneqjner" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Uryc & Ubj-Gbf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Yngrfg CPf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Zrqvn Cebqhpgvbaf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Arjf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Choyvfu Lbhe Bcvavba" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Frnepu" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Fgbpx Dhbgrf" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Grpu Wbof" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Jro Ohvyqvat" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Nyy Freivprf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Pbeerpgvbaf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Ubj" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "gb" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Nqiregvfr" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Wbva" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "PARG'f" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Nssvyvngr" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Cebtenz" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Fhccbeg" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Nobhg" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "PARG" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Onpx" },
+{ "Arial, Geneva, Helvetica,serif",
+ "gb" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Gbc" },
+{ "Arial, Geneva, Helvetica,serif",
+ "Wbva" },
+{ "Arial, Geneva, Helvetica,serif",
+ "PARG," },
+{ "Arial, Geneva, Helvetica,serif",
+ "jr'er" },
+{ "Arial, Geneva, Helvetica,serif",
+ "uvevat" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Pbclevtug" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "\xc2""\xa9""1995-2000" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "PARG" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Argjbexf," },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Vap." },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Nyy" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "evtugf" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "erfreirq." },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Cevinpl" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "cbyvpl" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "." },
+{ "Arial, Helvetica,serif",
+ "Arjf.pbz Qvfcngpu" },
+{ "Lucida Grande",
+ "Lbhe rznvy urer" },
+{ "serif",
+ "7," },
+{ "serif",
+ "arjf.parg.pbz," },
+{ "serif",
+ "327" },
+{ "serif",
+ "1, 7, arjf.parg.pbz, 327" },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ " " },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ " | " },
+{ "Geneva, MS Sans Serif, Helvetica,serif",
+ "Grpu Uryc" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ "Serr Rznvy" },
+{ "MS Sans Serif, Geneva, Helvetica,serif",
+ " " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jr Zrna Ohfvarff" },
+{ "Arial, Helvetica,serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ " : " },
+{ "arial,helvetica,serif",
+ "Ynfg Hcqngrq: " },
+{ "arial,helvetica,serif",
+ "Qrp 13, 10:15 c.z. CG" },
+{ "serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "Serr Erny-Gvzr Dhbgrf" },
+{ "serif",
+ " " },
+{ "Lucida Grande",
+ "Va Arjf" },
+{ "Lucida Grande",
+ "Tb!" },
+{ "Arial, Helvetica,serif",
+ "Arjf.pbz va lbhe cnyz" },
+{ "arial,helvetica,serif",
+ "Svanapr naq Vairfgvat" },
+{ "arial,helvetica,serif",
+ " | " },
+{ "arial,helvetica,serif",
+ "Zl Cbegsbyvb" },
+{ "Arial, Helvetica,serif",
+ "Pbafhzref fcraq erpbeq " },
+{ "Arial, Helvetica,serif",
+ "nzbhagf bayvar" },
+{ "serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "Zbivrf bire pryy cubarf abg " },
+{ "Arial, Helvetica,serif",
+ "lrg va sbphf" },
+{ "Arial, Helvetica,serif",
+ "Jvyy gur arkg cerfvqrag nygre " },
+{ "Arial, Helvetica,serif",
+ "gur bayvar jbeyq?" },
+{ "arial, helvetica,serif",
+ "Whqtr ershfrf gb oybpx JbeyqPbz, Vagrezrqvn zretre" },
+{ "Arial, Helvetica,serif",
+ "N Qrynjner whqtr ershfrf gb oybpx gur pbzzhavpngvbaf zretre nf erdhrfgrq ol zvabevgl funerubyqref bs " },
+{ "Arial, Helvetica,serif",
+ "Qvtrk, juvpu vf pbagebyyrq ol Vagrezrqvn." },
+{ "Arial, Helvetica,serif",
+ "Qrprzore 13, 10:15 c.z. CG " },
+{ "Arial, Helvetica,serif",
+ " " },
+{ "arial, helvetica,serif",
+ "VOZ gb fuvc raunaprq znvasenzr, fgbentr flfgrzf" },
+{ "Arial, Helvetica,serif",
+ "Ovt Oyhr vf fpurqhyrq gb fgneg fuvccvat vgf svefg T7 znvasenzr, pbqr-anzrq Serrjnl, nf jryy nf na " },
+{ "Arial, Helvetica,serif",
+ "raunaprq irefvba bs gur Funex fgbentr flfgrz." },
+{ "Arial, Helvetica,serif",
+ "Qrprzore 13, 9:50 c.z. CG " },
+{ "Arial, Helvetica,serif",
+ "Ragrecevfr Pbzchgvat" },
+{ "arial, helvetica,serif",
+ "IN obyfgref chfu sbe ovt ohfvarff phfgbzref" },
+{ "Arial, Helvetica,serif",
+ "IN Yvahk Flfgrzf, ubcvat vg'yy or pneevrq nybat va gur qensg bs VOZ'f rzobyqrarq Yvahk chfu, vf nqwhfgvat " },
+{ "Arial, Helvetica,serif",
+ "vgf ohfvarff gb gel jbb yrff ninagr-tneqr phfgbzref." },
+{ "Arial, Helvetica,serif",
+ "Qrprzore 13, 9:20 c.z. CG " },
+{ "Arial, Helvetica,serif",
+ " " },
+{ "Arial, Helvetica,serif",
+ "Ragrecevfr Pbzchgvat" },
+{ "Arial, Helvetica,serif",
+ "Ragregnvazrag & Zrqvn" },
+{ "Arial, Helvetica,serif",
+ "Crefbany Grpuabybtl" },
+{ "Arial, Helvetica,serif",
+ "Fcrpvny Ercbegf" },
+{ "Arial, Helvetica,serif",
+ "PARG Vairfgbe" },
+{ "Arial, Helvetica,serif",
+ "PARG Arjf.pbz GI" },
+{ "Arial, Helvetica,serif",
+ "PARG Enqvb" },
+{ "Arial, Helvetica,serif",
+ "Ehzbe Zvyy" },
+{ "Arial, Helvetica,serif",
+ "Bar Jrrx Ivrj" },
+{ "Arial, Helvetica,serif",
+ "Vafgnag Arjf Nyregf" },
+{ "Arial, Helvetica,serif",
+ " PARG Arjfyrggref" },
+{ "Arial, Helvetica,serif",
+ "Vairfgbe Qvfcngpu" },
+{ "Lucida Grande",
+ "Fhofpevor" },
+{ "Arial, Helvetica,serif",
+ "Nyy Arjfyrggref" },
+{ "serif",
+ "snd" },
+{ "serif",
+ "pbqr" },
+{ "serif",
+ "bfqa" },
+{ "serif",
+ "njneqf" },
+{ "serif",
+ "cevinpl" },
+{ "serif",
+ "fynfuARG" },
+{ "serif",
+ "byqre" },
+{ "serif",
+ "fghss" },
+{ "serif",
+ "ebo'f" },
+{ "serif",
+ "cntr" },
+{ "serif",
+ "cersreraprf" },
+{ "serif",
+ "fhozvg" },
+{ "serif",
+ "fgbel" },
+{ "serif",
+ "nqiregvfvat" },
+{ "serif",
+ "fhccbegref" },
+{ "serif",
+ "cnfg" },
+{ "serif",
+ "cbyyf" },
+{ "serif",
+ "gbcvpf" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "wbof" },
+{ "serif",
+ "ubs" },
+{ "serif",
+ "Frpgvbaf" },
+{ "serif",
+ "10/28" },
+{ "serif",
+ "ncnpur" },
+{ "serif",
+ "(5)" },
+{ "serif",
+ "nfxfynfuqbg" },
+{ "serif",
+ "1/27" },
+{ "serif",
+ "10/26" },
+{ "serif",
+ "obbxf" },
+{ "serif",
+ "10/27" },
+{ "serif",
+ "ofq" },
+{ "serif",
+ "srngherf" },
+{ "serif",
+ "vagreivrjf" },
+{ "serif",
+ "enqvb" },
+{ "serif",
+ "(2)" },
+{ "serif",
+ "fpvrapr" },
+{ "serif",
+ "(6)" },
+{ "serif",
+ "leb" },
+{ "serif",
+ "BFQA" },
+{ "serif",
+ "Serfuzrng" },
+{ "serif",
+ "Yvahk.pbz" },
+{ "serif",
+ "FbheprSbetr" },
+{ "serif",
+ "GuvaxTrrx" },
+{ "serif",
+ "Dhrfgvba" },
+{ "serif",
+ " Rkpunatr" },
+{ "arial,helvetica,serif",
+ "Frtn" },
+{ "arial,helvetica,serif",
+ "Gb" },
+{ "arial,helvetica,serif",
+ "Sbez" },
+{ "arial,helvetica,serif",
+ "Wbvag" },
+{ "arial,helvetica,serif",
+ "Pbzcnal" },
+{ "arial,helvetica,serif",
+ "Jvgu" },
+{ "arial,helvetica,serif",
+ "Avagraqb?" },
+{ "serif",
+ "Cbfgrq" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "gvzbgul" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "Fhaqnl" },
+{ "serif",
+ "Bpgbore" },
+{ "serif",
+ "29," },
+{ "serif",
+ "@12:50NZ" },
+{ "serif",
+ "sebz" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "fgenatr-orrcvat-orqsryybjf" },
+{ "serif",
+ "qrcg." },
+{ "serif",
+ "WnlObapv" },
+{ "serif",
+ "jevgrf:" },
+{ "serif",
+ "\"Frtn" },
+{ "serif",
+ "unf" },
+{ "serif",
+ "unq" },
+{ "serif",
+ "n" },
+{ "serif",
+ "ybg" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "pbzcrgvgvba" },
+{ "serif",
+ "yngryl," },
+{ "serif",
+ "naq" },
+{ "serif",
+ "juvyr" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "Qernzpnfg" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "irel" },
+{ "serif",
+ "pbby," },
+{ "serif",
+ "vg" },
+{ "serif",
+ "fbzr" },
+{ "serif",
+ "uneq" },
+{ "serif",
+ "gvzrf" },
+{ "serif",
+ "yngryl." },
+{ "serif",
+ "VTA" },
+{ "serif",
+ "oevatf" },
+{ "serif",
+ "hc" },
+{ "serif",
+ "guvf" },
+{ "serif",
+ "cvrpr" },
+{ "serif",
+ "arjf" },
+{ "serif",
+ "sebz" },
+{ "serif",
+ "bhg" },
+{ "serif",
+ "oyhr" },
+{ "serif",
+ "gung" },
+{ "serif",
+ "Frtn" },
+{ "serif",
+ "zlfgrevbhfyl" },
+{ "serif",
+ "grnzvat" },
+{ "serif",
+ "jvgu" },
+{ "serif",
+ "Avagraqb" },
+{ "serif",
+ "." },
+{ "serif",
+ "Jul" },
+{ "serif",
+ "jbhyq" },
+{ "serif",
+ "fhpu" },
+{ "serif",
+ "ybat" },
+{ "serif",
+ "gvzr" },
+{ "serif",
+ "evinyf" },
+{ "serif",
+ "grnz" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "fhqqra?" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "negvpyr" },
+{ "serif",
+ "qbrf" },
+{ "serif",
+ "abg" },
+{ "serif",
+ "tvir" },
+{ "serif",
+ "zhpu" },
+{ "serif",
+ "va" },
+{ "serif",
+ "jnl" },
+{ "serif",
+ "qrgnvyf," },
+{ "serif",
+ "rkprcg" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "zragvba" },
+{ "serif",
+ "tbvat" },
+{ "serif",
+ "snyy" },
+{ "serif",
+ "fubeg" },
+{ "serif",
+ "vgf" },
+{ "serif",
+ "rneavatf." },
+{ "serif",
+ "Ubj" },
+{ "serif",
+ "punatr" },
+{ "serif",
+ "tnzvat" },
+{ "serif",
+ "vaqhfgel" },
+{ "serif",
+ "gjb" },
+{ "serif",
+ "znwbe" },
+{ "serif",
+ "cynlref" },
+{ "serif",
+ "jbexvat" },
+{ "serif",
+ "gbtrgure?\"" },
+{ "serif",
+ "(" },
+{ "serif",
+ "Ernq" },
+{ "serif",
+ "Zber..." },
+{ "serif",
+ "|" },
+{ "serif",
+ "9" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "18" },
+{ "serif",
+ "pbzzragf" },
+{ "serif",
+ ")" },
+{ "arial,helvetica,serif",
+ "Gur" },
+{ "arial,helvetica,serif",
+ "Vzcnpg" },
+{ "arial,helvetica,serif",
+ "ba" },
+{ "arial,helvetica,serif",
+ "Bcra" },
+{ "arial,helvetica,serif",
+ "Fbhepr" },
+{ "arial,helvetica,serif",
+ "bs" },
+{ "arial,helvetica,serif",
+ "Fgbyra" },
+{ "arial,helvetica,serif",
+ "Zvpebfbsg" },
+{ "arial,helvetica,serif",
+ "Pbqr" },
+{ "serif",
+ "Fngheqnl" },
+{ "serif",
+ "28," },
+{ "serif",
+ "@07:58CZ" },
+{ "serif",
+ "urpx-pnirng-*rirelobql*!" },
+{ "serif",
+ "Pnony" },
+{ "serif",
+ "\"V" },
+{ "serif",
+ "erpragyl" },
+{ "serif",
+ "pnzr" },
+{ "serif",
+ "npebff" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "Yvahk" },
+{ "serif",
+ "Wbheany" },
+{ "serif",
+ "Vg" },
+{ "serif",
+ "qvfphffrf" },
+{ "serif",
+ "zber" },
+{ "serif",
+ "vagrerfgvat" },
+{ "serif",
+ "yrtny" },
+{ "serif",
+ "enzvsvpngvbaf" },
+{ "serif",
+ "gursg" },
+{ "serif",
+ "Zvpebfbsg'f" },
+{ "serif",
+ "fbhepr" },
+{ "serif",
+ "pbqr" },
+{ "serif",
+ "V" },
+{ "serif",
+ "unqa'g" },
+{ "serif",
+ "rira" },
+{ "serif",
+ "gubhtug" },
+{ "serif",
+ "vg'f" },
+{ "serif",
+ "rssrpg" },
+{ "serif",
+ "bcra-fbhepr" },
+{ "serif",
+ "cebwrpgf." },
+{ "serif",
+ "Onfvpnyyl," },
+{ "serif",
+ "fnlvat" },
+{ "serif",
+ "qba'g" },
+{ "serif",
+ "tb" },
+{ "serif",
+ "arne" },
+{ "serif",
+ "nal" },
+{ "serif",
+ "pynvzvat" },
+{ "serif",
+ "or" },
+{ "serif",
+ "fgbyra" },
+{ "serif",
+ "ZF," },
+{ "serif",
+ "tbbq" },
+{ "serif",
+ "ernfba," },
+{ "serif",
+ "vapyhqvat" },
+{ "serif",
+ "dhbgngvbaf" },
+{ "serif",
+ "Fnzon" },
+{ "serif",
+ "cebwrpg." },
+{ "serif",
+ "Purpx" },
+{ "serif",
+ "bhg," },
+{ "serif",
+ "ernq.\"" },
+{ "serif",
+ "205" },
+{ "serif",
+ "372" },
+{ "arial,helvetica,serif",
+ "NBY" },
+{ "arial,helvetica,serif",
+ "6.0" },
+{ "arial,helvetica,serif",
+ "Pyvrag:" },
+{ "arial,helvetica,serif",
+ "Jr'yy" },
+{ "arial,helvetica,serif",
+ "Or" },
+{ "arial,helvetica,serif",
+ "Lbhe" },
+{ "arial,helvetica,serif",
+ "Ubzr" },
+{ "arial,helvetica,serif",
+ "Cntr," },
+{ "arial,helvetica,serif",
+ "Gunaxf" },
+{ "serif",
+ "@04:49CZ" },
+{ "serif",
+ "oboovat-sbe-rlronyyf" },
+{ "serif",
+ "Znfrz" },
+{ "serif",
+ "\"Nppbeqvat" },
+{ "serif",
+ "fgbel" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "PArg" },
+{ "serif",
+ "," },
+{ "serif",
+ "yngrfg" },
+{ "serif",
+ "irefvba" },
+{ "serif",
+ "NBY" },
+{ "serif",
+ "6.0'f" },
+{ "serif",
+ "Jro" },
+{ "serif",
+ "oebjfre" },
+{ "serif",
+ "nyybj" },
+{ "serif",
+ "hfre" },
+{ "serif",
+ "frg" },
+{ "serif",
+ "ubzr" },
+{ "serif",
+ "cntr," },
+{ "serif",
+ "abe" },
+{ "serif",
+ "pyvpx" },
+{ "serif",
+ "ohggba" },
+{ "serif",
+ "cntr." },
+{ "serif",
+ "Vafgrnq," },
+{ "serif",
+ "sbeprq" },
+{ "serif",
+ "fgneg" },
+{ "serif",
+ "guebhtu" },
+{ "serif",
+ "NBY'f" },
+{ "serif",
+ "pynvzf" },
+{ "serif",
+ "jnf" },
+{ "serif",
+ "na" },
+{ "serif",
+ "nrfgurgvp" },
+{ "serif",
+ "zbir" },
+{ "serif",
+ "cebzcgrq" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "vachg," },
+{ "serif",
+ "ohg" },
+{ "serif",
+ "znal" },
+{ "serif",
+ "ner" },
+{ "serif",
+ "pelvat" },
+{ "serif",
+ "sbhy" },
+{ "serif",
+ "yvtug" },
+{ "serif",
+ "cebcbfrq" },
+{ "serif",
+ "GJ/NBY" },
+{ "serif",
+ "zretre;" },
+{ "serif",
+ "tvirf" },
+{ "serif",
+ "gbb" },
+{ "serif",
+ "rqtr" },
+{ "serif",
+ "pbagrag" },
+{ "serif",
+ "pbageby." },
+{ "serif",
+ "Ng" },
+{ "serif",
+ "yrnfg" },
+{ "serif",
+ "gurl" },
+{ "serif",
+ "pbhyq" },
+{ "serif",
+ "unir" },
+{ "serif",
+ "yrsg" },
+{ "serif",
+ "nqinaprq" },
+{ "serif",
+ "qvnybt" },
+{ "serif",
+ "obk" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "rkcrevraprq" },
+{ "serif",
+ "hfref" },
+{ "serif",
+ "...\"" },
+{ "serif",
+ "158" },
+{ "serif",
+ "239" },
+{ "arial,helvetica,serif",
+ "Nfx" },
+{ "arial,helvetica,serif",
+ "Fynfuqbg" },
+{ "arial,helvetica,serif",
+ ":" },
+{ "arial,helvetica,serif",
+ "Unf" },
+{ "arial,helvetica,serif",
+ "Q.N.E.R" },
+{ "arial,helvetica,serif",
+ "Orra" },
+{ "arial,helvetica,serif",
+ "Rssrpgvir?" },
+{ "serif",
+ "Pyvss" },
+{ "serif",
+ "@02:29CZ" },
+{ "serif",
+ "engr-gur-cebtenz" },
+{ "serif",
+ "znpavtry" },
+{ "serif",
+ "nfxf:" },
+{ "serif",
+ "\"V'z" },
+{ "serif",
+ "rqvgbe" },
+{ "serif",
+ "zl" },
+{ "serif",
+ "fpubby'f" },
+{ "serif",
+ "arjfcncre" },
+{ "serif",
+ "cyna" },
+{ "serif",
+ "jevgr" },
+{ "serif",
+ "pbzzragnel" },
+{ "serif",
+ "rssrpgvirarff" },
+{ "serif",
+ "Q.N.E.R" },
+{ "serif",
+ "cebtenz." },
+{ "serif",
+ "yvxr" },
+{ "serif",
+ "urne" },
+{ "serif",
+ "jung" },
+{ "serif",
+ "Fynfuqbg'f" },
+{ "serif",
+ "nhqvrapr" },
+{ "serif",
+ "fnl" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "Qeht" },
+{ "serif",
+ "Nohfr" },
+{ "serif",
+ "Erfvfgnapr" },
+{ "serif",
+ "Rqhpngvba" },
+{ "serif",
+ "cebtenz" },
+{ "serif",
+ "gung'f" },
+{ "serif",
+ "cynpr" },
+{ "serif",
+ "zbfg" },
+{ "serif",
+ "fpubbyf" },
+{ "serif",
+ "nebhaq" },
+{ "serif",
+ "pbhagel." },
+{ "serif",
+ "Pbzzragf" },
+{ "serif",
+ "rkcrevraprf" },
+{ "serif",
+ "jrypbzr." },
+{ "serif",
+ "envfr" },
+{ "serif",
+ "dhrfgvba" },
+{ "serif",
+ "snveyl" },
+{ "serif",
+ "erprag" },
+{ "serif",
+ "fghql" },
+{ "serif",
+ "Uneineq" },
+{ "serif",
+ "Havirefvgl." },
+{ "serif",
+ "tbrf" },
+{ "serif",
+ "pynvz" },
+{ "serif",
+ "fhprff" },
+{ "serif",
+ "arj" },
+{ "serif",
+ "nccebnpu" },
+{ "serif",
+ "ceboyrz" },
+{ "serif",
+ "ovatr" },
+{ "serif",
+ "qevaxvat." },
+{ "serif",
+ "Fubhyq" },
+{ "serif",
+ "gel" },
+{ "serif",
+ "nccebnpurf" },
+{ "serif",
+ "ceboyrz?" },
+{ "serif",
+ "Pna" },
+{ "serif",
+ "rire" },
+{ "serif",
+ "ubcr" },
+{ "serif",
+ "vzcnpg" },
+{ "serif",
+ "qeht" },
+{ "serif",
+ "nohfr" },
+{ "serif",
+ "nzbat" },
+{ "serif",
+ "lbhgu?\"" },
+{ "serif",
+ "411" },
+{ "serif",
+ "645" },
+{ "serif",
+ "Nfx" },
+{ "serif",
+ "Fynfuqbg" },
+{ "arial,helvetica,serif",
+ "Ivfhny" },
+{ "arial,helvetica,serif",
+ "Nanylfvf" },
+{ "arial,helvetica,serif",
+ "Bs" },
+{ "arial,helvetica,serif",
+ "Zc3" },
+{ "arial,helvetica,serif",
+ "Rapbqref" },
+{ "serif",
+ "@01:12CZ" },
+{ "serif",
+ "pna-n-phr-png-ernq-gurfr?" },
+{ "serif",
+ "Puevf" },
+{ "serif",
+ "Wbuafba" },
+{ "serif",
+ "\"V'ir" },
+{ "serif",
+ "whfg" },
+{ "serif",
+ "svavfurq" },
+{ "serif",
+ "fpvragvsvp" },
+{ "serif",
+ "nanylfvf" },
+{ "serif",
+ "frireny" },
+{ "serif",
+ "zc3" },
+{ "serif",
+ "rapbqref" },
+{ "serif",
+ "svaqvatf" },
+{ "serif",
+ "Jro." },
+{ "serif",
+ "cebprff" },
+{ "serif",
+ "vaibyirf" },
+{ "serif",
+ "qvssrerapvat" },
+{ "serif",
+ "'fbabtenz'" },
+{ "serif",
+ "vzntr" },
+{ "serif",
+ "rapbqrq" },
+{ "serif",
+ "grfg" },
+{ "serif",
+ "fvtany" },
+{ "serif",
+ "bevtvany" },
+{ "serif",
+ "fvtany," },
+{ "serif",
+ "gura" },
+{ "serif",
+ "cebqhpvat" },
+{ "serif",
+ "erfcbafr" },
+{ "serif",
+ "pheirf" },
+{ "serif",
+ "fubjvat" },
+{ "serif",
+ "qvfcnevgl" },
+{ "serif",
+ "qverpg" },
+{ "serif",
+ "ibyhzr," },
+{ "serif",
+ "bire" },
+{ "serif",
+ "gvzr." },
+{ "serif",
+ "Hzz" },
+{ "serif",
+ "juvpu" },
+{ "serif",
+ "cebonoyl" },
+{ "serif",
+ "evtbebhf" },
+{ "serif",
+ "naljurer" },
+{ "serif",
+ "jro," },
+{ "serif",
+ "trrxl" },
+{ "serif",
+ "(va" },
+{ "serif",
+ "jnl)." },
+{ "serif",
+ "YNZR" },
+{ "serif",
+ "pneevrf" },
+{ "serif",
+ "qnl," },
+{ "serif",
+ "OynqrRap" },
+{ "serif",
+ "fubjf" },
+{ "serif",
+ "pbzcyrgryl" },
+{ "serif",
+ "qvfgvapgvir" },
+{ "serif",
+ "fbavp" },
+{ "serif",
+ "nccebnpu-" },
+{ "serif",
+ "Senhaubsre" },
+{ "serif",
+ "cebirf" },
+{ "serif",
+ "hanpprcgnoyr" },
+{ "serif",
+ "grfgrq)" },
+{ "serif",
+ "nhqvbcuvyr" },
+{ "serif",
+ "hfr," },
+{ "serif",
+ "gubhtu" },
+{ "serif",
+ "haorngnoyr" },
+{ "serif",
+ "ybj" },
+{ "serif",
+ "ovg" },
+{ "serif",
+ "engrf." },
+{ "serif",
+ "Frr" },
+{ "serif",
+ "jul." },
+{ "serif",
+ "\"" },
+{ "serif",
+ "Gehgu" },
+{ "serif",
+ "nqiregvfvat" },
+{ "serif",
+ "--" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "n" },
+{ "serif",
+ "pbby" },
+{ "serif",
+ "rknzcyr" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "ubj" },
+{ "serif",
+ "ivfhny" },
+{ "serif",
+ "vasbezngvba" },
+{ "serif",
+ "pna" },
+{ "serif",
+ "pbairl" },
+{ "serif",
+ "zber" },
+{ "serif",
+ "guna" },
+{ "serif",
+ "lbh'q" },
+{ "serif",
+ "rkcrpg" },
+{ "serif",
+ "vg" },
+{ "serif",
+ "gb." },
+{ "serif",
+ "113" },
+{ "serif",
+ "177" },
+{ "arial,helvetica,serif",
+ "NcnpurPba" },
+{ "arial,helvetica,serif",
+ "/" },
+{ "arial,helvetica,serif",
+ "2000" },
+{ "arial,helvetica,serif",
+ "RH:" },
+{ "arial,helvetica,serif",
+ "Frphevgl" },
+{ "arial,helvetica,serif",
+ "Fbyhgvbaf" },
+{ "arial,helvetica,serif",
+ "FFY" },
+{ "serif",
+ "Urzbf" },
+{ "serif",
+ "@12:35CZ" },
+{ "serif",
+ "vagrerfgvat-ernqvat-sbe-n-fngheqnl" },
+{ "serif",
+ "Enys" },
+{ "serif",
+ "F." },
+{ "serif",
+ "Ratryfpunyy" },
+{ "serif",
+ "\"Gur" },
+{ "serif",
+ "fyvqr" },
+{ "serif",
+ "cerfragngvba," },
+{ "serif",
+ "'Frphevgl" },
+{ "serif",
+ "Fbyhgvbaf" },
+{ "serif",
+ "FFY,'" },
+{ "serif",
+ "tnir" },
+{ "serif",
+ "NcnpurPba" },
+{ "serif",
+ "2000/RH" },
+{ "serif",
+ "Ybaqba" },
+{ "serif",
+ "ynfg" },
+{ "serif",
+ "Zbaqnl" },
+{ "serif",
+ "ninvynoyr" },
+{ "serif",
+ "bayvar" },
+{ "serif",
+ "haqre" },
+{ "serif",
+ "uggc://jjj.zbqffy.bet/qbpf/ncnpurpba2000/" },
+{ "serif",
+ "vs" },
+{ "serif",
+ "lbh" },
+{ "serif",
+ "nggraqrq" },
+{ "serif",
+ "arireguryrff" },
+{ "serif",
+ "vagrerfgrq" },
+{ "serif",
+ "Ncnpur" },
+{ "serif",
+ "FFY." },
+{ "serif",
+ "N" },
+{ "serif",
+ "CAT-pncnoyr" },
+{ "serif",
+ "erdhverq" },
+{ "serif",
+ "ivrj.\"" },
+{ "serif",
+ "6" },
+{ "serif",
+ "21" },
+{ "serif",
+ "Ncnpur" },
+{ "arial,helvetica,serif",
+ "AL'f" },
+{ "arial,helvetica,serif",
+ "Fvyvpba" },
+{ "arial,helvetica,serif",
+ "Nyyrl" },
+{ "arial,helvetica,serif",
+ "Srryf" },
+{ "arial,helvetica,serif",
+ "Pehapu" },
+{ "serif",
+ "@11:03NZ" },
+{ "serif",
+ "onq-gvzrf-sbe-gur-ovt-nccyr" },
+{ "serif",
+ "rcuenvz" },
+{ "serif",
+ "\"Qhevat" },
+{ "serif",
+ "lrne," },
+{ "serif",
+ "jbexrq" },
+{ "serif",
+ "qbg-pbzf" },
+{ "serif",
+ "ALP." },
+{ "serif",
+ "Nobhg" },
+{ "serif",
+ "sbhe" },
+{ "serif",
+ "zbaguf" },
+{ "serif",
+ "ntb," },
+{ "serif",
+ "ortna" },
+{ "serif",
+ "srry" },
+{ "serif",
+ "qbjaghea" },
+{ "serif",
+ "vzzvarag," },
+{ "serif",
+ "fb" },
+{ "serif",
+ "wbva" },
+{ "serif",
+ "genqvgvbany" },
+{ "serif",
+ "pbzcnal." },
+{ "serif",
+ "Sne" },
+{ "serif",
+ "pbzcnavrf" },
+{ "serif",
+ "pbafhygrq" },
+{ "serif",
+ "frrzrq" },
+{ "serif",
+ "frevbhf" },
+{ "serif",
+ "vagreany" },
+{ "serif",
+ "ceboyrzf" },
+{ "serif",
+ "nccrnerq" },
+{ "serif",
+ "cerpyhqr" },
+{ "serif",
+ "gurz" },
+{ "serif",
+ "znxvat" },
+{ "serif",
+ "erny" },
+{ "serif",
+ "zbarl" },
+{ "serif",
+ "bapr" },
+{ "serif",
+ "gurve" },
+{ "serif",
+ "iragher" },
+{ "serif",
+ "pncvgny" },
+{ "serif",
+ "qevrq" },
+{ "serif",
+ "hc." },
+{ "serif",
+ "gheaf" },
+{ "serif",
+ "znqr" },
+{ "serif",
+ "evtug" },
+{ "serif",
+ "zbir." },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "AL" },
+{ "serif",
+ "Gvzrf" },
+{ "serif",
+ "fvgr" },
+{ "serif",
+ "(serr" },
+{ "serif",
+ "ertvfgengvba" },
+{ "serif",
+ "erdhverq)" },
+{ "serif",
+ "trareny" },
+{ "serif",
+ "geraqf" },
+{ "serif",
+ "vaqhfgel," },
+{ "serif",
+ "nabgure" },
+{ "serif",
+ "ybbxf" },
+{ "serif",
+ "curabzraba" },
+{ "serif",
+ "rlrf" },
+{ "serif",
+ "bar" },
+{ "serif",
+ "rzcyblrr" },
+{ "serif",
+ "qbbzrq" },
+{ "serif",
+ "Jung" },
+{ "serif",
+ "ur" },
+{ "serif",
+ "fnlf" },
+{ "serif",
+ "crbcyr" },
+{ "serif",
+ "fvggvat" },
+{ "serif",
+ "qbvat" },
+{ "serif",
+ "abguvat" },
+{ "serif",
+ "gehr" },
+{ "serif",
+ "dhvgr" },
+{ "serif",
+ "srj" },
+{ "serif",
+ "gurfr" },
+{ "serif",
+ "pbzcnavrf." },
+{ "serif",
+ "Juvyr" },
+{ "serif",
+ "gurer" },
+{ "serif",
+ "fgvyy" },
+{ "serif",
+ "cyragl" },
+{ "serif",
+ "wbof" },
+{ "serif",
+ "grpuabybtl" },
+{ "serif",
+ "AL," },
+{ "serif",
+ "gubfr" },
+{ "serif",
+ "ybbxvat" },
+{ "serif",
+ "jbex" },
+{ "serif",
+ "fubhyq" },
+{ "serif",
+ "ortva" },
+{ "serif",
+ "nfx" },
+{ "serif",
+ "qvfpreavat" },
+{ "serif",
+ "dhrfgvbaf" },
+{ "serif",
+ "cebfcrpgvir" },
+{ "serif",
+ "rzcyblref.\"" },
+{ "serif",
+ "95" },
+{ "serif",
+ "143" },
+{ "arial,helvetica,serif",
+ "Evtugf" },
+{ "arial,helvetica,serif",
+ "Bayvar" },
+{ "arial,helvetica,serif",
+ "VPNAA" },
+{ "arial,helvetica,serif",
+ "Obneq" },
+{ "arial,helvetica,serif",
+ "Zrzoref" },
+{ "arial,helvetica,serif",
+ "Fdhng" },
+{ "serif",
+ "zvpunry" },
+{ "serif",
+ "@06:27NZ" },
+{ "serif",
+ "xvpx-gur-ohzf-bhg" },
+{ "serif",
+ "Cebsrffbe" },
+{ "serif",
+ "Sebbzxva" },
+{ "serif",
+ "unf" },
+{ "serif",
+ "jevggra" },
+{ "serif",
+ "fgebat" },
+{ "serif",
+ "pevgvpvfz" },
+{ "serif",
+ "VPNAA" },
+{ "serif",
+ "vavgvny" },
+{ "serif",
+ "obneq," },
+{ "serif",
+ "juvpu" },
+{ "serif",
+ "rkgraqrq" },
+{ "serif",
+ "vgf" },
+{ "serif",
+ "haryrpgrq" },
+{ "serif",
+ "bar-lrne" },
+{ "serif",
+ "grez" },
+{ "serif",
+ "na" },
+{ "serif",
+ "nfgbavfuvat" },
+{ "serif",
+ "sbhe" },
+{ "serif",
+ "lrnef," },
+{ "serif",
+ "jvgu" },
+{ "serif",
+ "ab" },
+{ "serif",
+ "raq" },
+{ "serif",
+ "fvtug." },
+{ "serif",
+ "Nppbeqvat" },
+{ "serif",
+ "VPNAA'f" },
+{ "serif",
+ "pheerag" },
+{ "serif",
+ "olynjf," },
+{ "serif",
+ "gubfr" },
+{ "serif",
+ "obneq" },
+{ "serif",
+ "zrzoref" },
+{ "serif",
+ "ner" },
+{ "serif",
+ "freivat" },
+{ "serif",
+ "yvsr" },
+{ "serif",
+ "-" },
+{ "serif",
+ "gurve" },
+{ "serif",
+ "grezf" },
+{ "serif",
+ "arire" },
+{ "serif",
+ "rkcver." },
+{ "serif",
+ "V" },
+{ "serif",
+ "fgebatyl" },
+{ "serif",
+ "hetr" },
+{ "serif",
+ "ernqref" },
+{ "serif",
+ "Pnyvsbeavn" },
+{ "serif",
+ "znxr" },
+{ "serif",
+ "gvzr" },
+{ "serif",
+ "nggraq" },
+{ "serif",
+ "arkg" },
+{ "serif",
+ "zrrgvat" },
+{ "serif",
+ "Abirzore" },
+{ "serif",
+ "43" },
+{ "serif",
+ "68" },
+{ "serif",
+ "Lbhe" },
+{ "serif",
+ "Evtugf" },
+{ "serif",
+ "Bayvar" },
+{ "arial,helvetica,serif",
+ "ClgubaYnof" },
+{ "arial,helvetica,serif",
+ "zbirf" },
+{ "arial,helvetica,serif",
+ "gb" },
+{ "arial,helvetica,serif",
+ "Qvtvgny" },
+{ "arial,helvetica,serif",
+ "Perngvbaf" },
+{ "serif",
+ "@02:45NZ" },
+{ "serif",
+ "zbivat-ba" },
+{ "serif",
+ "fabggehr" },
+{ "serif",
+ "jevgrf" },
+{ "serif",
+ "\"Guvf" },
+{ "serif",
+ "pbzc.ynat.clguba" },
+{ "serif",
+ "-" },
+{ "serif",
+ "ClgubaYnof" },
+{ "serif",
+ "zbivat" },
+{ "serif",
+ "Qvtvgny" },
+{ "serif",
+ "Perngvbaf" },
+{ "serif",
+ "(gur" },
+{ "serif",
+ "Mbcr" },
+{ "serif",
+ "crbcyr).\"" },
+{ "serif",
+ "Ina" },
+{ "serif",
+ "Ebffhz'f" },
+{ "serif",
+ "zrffntr" },
+{ "serif",
+ "vasbezngvir." },
+{ "serif",
+ "25" },
+{ "serif",
+ "64" },
+{ "arial,helvetica,serif",
+ "Yvahk" },
+{ "arial,helvetica,serif",
+ "Fperrafubgf" },
+{ "arial,helvetica,serif",
+ "Yriry" },
+{ "arial,helvetica,serif",
+ "9" },
+{ "serif",
+ "@12:17NZ" },
+{ "serif",
+ "obbo-ghor" },
+{ "serif",
+ "oenqvcb" },
+{ "serif",
+ "jngpuvat" },
+{ "serif",
+ "Yriry" },
+{ "serif",
+ "9" },
+{ "serif",
+ "svefg" },
+{ "serif",
+ "fnj" },
+{ "serif",
+ "tyvzcfr" },
+{ "serif",
+ "yvahk" },
+{ "serif",
+ "qrfxgbc," },
+{ "serif",
+ "xrcg" },
+{ "serif",
+ "jngpuvat." },
+{ "serif",
+ "Fher" },
+{ "serif",
+ "rabhtu," },
+{ "serif",
+ "jrer" },
+{ "serif",
+ "hfvat" },
+{ "serif",
+ "nf" },
+{ "serif",
+ "pbzchgre" },
+{ "serif",
+ "pbhcyr" },
+{ "serif",
+ "xvqf" },
+{ "serif",
+ "ivrj" },
+{ "serif",
+ "ANFN" },
+{ "serif",
+ "qbphzragf," },
+{ "serif",
+ "rgp..." },
+{ "serif",
+ "pncgherq" },
+{ "serif",
+ "avsgl" },
+{ "serif",
+ "gi" },
+{ "serif",
+ "pncgher" },
+{ "serif",
+ "pneq." },
+{ "serif",
+ "zr" },
+{ "serif",
+ "Rayvtugrazrag" },
+{ "serif",
+ "be" },
+{ "serif",
+ "JvaqbjZnxre" },
+{ "serif",
+ "cbffvoyl" },
+{ "serif",
+ "obgu" },
+{ "serif",
+ "gbtrgure.\"" },
+{ "serif",
+ "232" },
+{ "serif",
+ "391" },
+{ "arial,helvetica,serif",
+ "Vagreivrjf" },
+{ "serif",
+ "Znex" },
+{ "serif",
+ "Rqry" },
+{ "serif",
+ "Nafjref" },
+{ "serif",
+ "Cebwrpg" },
+{ "serif",
+ "Yrnqrefuvc" },
+{ "serif",
+ "Dhrfgvbaf" },
+{ "serif",
+ "Cerfvqragvny" },
+{ "serif",
+ "Nafjref," },
+{ "serif",
+ "Ebhaq" },
+{ "serif",
+ "Bar" },
+{ "serif",
+ "Vaqerzn'f" },
+{ "serif",
+ "Wbua" },
+{ "serif",
+ "Tvyqerq" },
+{ "serif",
+ "Lbhe" },
+{ "serif",
+ "Yrnqvat" },
+{ "serif",
+ "N" },
+{ "serif",
+ "Ybj-Cebsvyr" },
+{ "serif",
+ "Serr" },
+{ "serif",
+ "Fbsgjner" },
+{ "serif",
+ "Nfx" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "Pnaqvqngrf" },
+{ "serif",
+ "sebz" },
+{ "serif",
+ "Pneaviber" },
+{ "serif",
+ "Erivrjre" },
+{ "serif",
+ "Urael" },
+{ "serif",
+ "U." },
+{ "serif",
+ "Creevg," },
+{ "serif",
+ "We." },
+{ "serif",
+ "Gnyx" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "Puvrs" },
+{ "serif",
+ "Erivrjref" },
+{ "serif",
+ "Nobhg" },
+{ "serif",
+ "Vaqerzn" },
+{ "serif",
+ "Naq" },
+{ "serif",
+ "Yvahk" },
+{ "serif",
+ "Tnzvat" },
+{ "serif",
+ "IbbqbbRkgerzr" },
+{ "serif",
+ "Vagreivrj" },
+{ "serif",
+ "Jvgu" },
+{ "serif",
+ "Pneznpx" },
+{ "serif",
+ "Zvpunry" },
+{ "serif",
+ "Rgurggba" },
+{ "serif",
+ "-" },
+{ "serif",
+ "Fcrpvny" },
+{ "serif",
+ "Thrfg" },
+{ "serif",
+ "va" },
+{ "serif",
+ "#Cnyz" },
+{ "arial,helvetica,serif",
+ "Ybtva" },
+{ "serif",
+ "Avpxanzr:" },
+{ "serif",
+ "Cnffjbeq:" },
+{ "Lucida Grande",
+ "hfreybtva" },
+{ "serif",
+ "Qba'g" },
+{ "serif",
+ "unir" },
+{ "serif",
+ "na" },
+{ "serif",
+ "nppbhag" },
+{ "serif",
+ "lrg?" },
+{ "serif",
+ "Tb" },
+{ "serif",
+ "Perngr" },
+{ "serif",
+ "." },
+{ "serif",
+ "hfre" },
+{ "serif",
+ "jvyy" },
+{ "serif",
+ "nyybj" },
+{ "serif",
+ "lbh" },
+{ "serif",
+ "phfgbzvmr" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "gurfr" },
+{ "serif",
+ "ahggl" },
+{ "serif",
+ "yvggyr" },
+{ "serif",
+ "obkrf" },
+{ "serif",
+ "," },
+{ "serif",
+ "gnvybe" },
+{ "serif",
+ "fgbevrf" },
+{ "serif",
+ "frr," },
+{ "serif",
+ "nf" },
+{ "serif",
+ "jryy" },
+{ "serif",
+ "erzrzore" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "pbzzrag" },
+{ "serif",
+ "ivrjvat" },
+{ "serif",
+ "cersreraprf." },
+{ "arial,helvetica,serif",
+ "Byqre" },
+{ "arial,helvetica,serif",
+ "Fghss" },
+{ "serif",
+ "Sevqnl" },
+{ "serif",
+ "Bpgbore" },
+{ "serif",
+ "27" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "Vzntrf" },
+{ "serif",
+ "Tnyvyrb" },
+{ "serif",
+ "(84)" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "CF2" },
+{ "serif",
+ "Rkcrevrapr" },
+{ "serif",
+ "(284)" },
+{ "serif",
+ "Pehfbr" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "Orapuznexf" },
+{ "serif",
+ "(196)" },
+{ "serif",
+ "6" },
+{ "serif",
+ "Znef" },
+{ "serif",
+ "Zvffvbaf" },
+{ "serif",
+ "(188)" },
+{ "serif",
+ "Vf" },
+{ "serif",
+ "Abiryy" },
+{ "serif",
+ "Qbbzrq?" },
+{ "serif",
+ "(378)" },
+{ "serif",
+ "RH" },
+{ "serif",
+ "Fghql" },
+{ "serif",
+ "Ybbxf" },
+{ "serif",
+ "Ng" },
+{ "serif",
+ "Cngragf" },
+{ "serif",
+ "(136)" },
+{ "serif",
+ "(59)" },
+{ "serif",
+ "Sbe" },
+{ "serif",
+ "TABZR" },
+{ "serif",
+ "Sbhaqngvba" },
+{ "serif",
+ "Ryrpgvbaf" },
+{ "serif",
+ "Naabhaprq" },
+{ "serif",
+ "(70)" },
+{ "serif",
+ "YnfreZNZR:" },
+{ "serif",
+ "Cynlvat" },
+{ "serif",
+ "Grzcrfg" },
+{ "serif",
+ "Va" },
+{ "serif",
+ "Jubyr" },
+{ "serif",
+ "Yvtug" },
+{ "serif",
+ "(179)" },
+{ "serif",
+ "Zvpebfbsg" },
+{ "serif",
+ "Guerngraf" },
+{ "serif",
+ "Benpyr" },
+{ "serif",
+ "Bire" },
+{ "serif",
+ "(265)" },
+{ "serif",
+ "Ploreqrzbpenpl" },
+{ "serif",
+ "Choyvp" },
+{ "serif",
+ "Fcurer" },
+{ "serif",
+ "(149)" },
+{ "serif",
+ "Hctenqr" },
+{ "serif",
+ "Cragvhz'f" },
+{ "serif",
+ "Zvpebpbqr" },
+{ "serif",
+ "(171)" },
+{ "serif",
+ "Penpxrq" },
+{ "serif",
+ "(1030)" },
+{ "serif",
+ "Chfu" },
+{ "serif",
+ "Haqrejnl" },
+{ "serif",
+ "Ynathvfuvat" },
+{ "serif",
+ "HPVGN" },
+{ "serif",
+ "(124)" },
+{ "serif",
+ "Ohl" },
+{ "serif",
+ "Lbhefrys" },
+{ "serif",
+ "Ehffvna" },
+{ "serif",
+ "Fcnpr" },
+{ "serif",
+ "Pncfhyr" },
+{ "serif",
+ "(97)" },
+{ "serif",
+ "Guhefqnl" },
+{ "serif",
+ "26" },
+{ "serif",
+ "Yvivat" },
+{ "serif",
+ "Pbybe" },
+{ "serif",
+ "(162)" },
+{ "serif",
+ "Cynlfgngvba" },
+{ "serif",
+ "VV" },
+{ "serif",
+ "Ynhapu" },
+{ "serif",
+ "Abgrf" },
+{ "serif",
+ "Sebz" },
+{ "serif",
+ "Svryq" },
+{ "serif",
+ "(246)" },
+{ "serif",
+ "Fbal" },
+{ "serif",
+ "2" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "$1x" },
+{ "serif",
+ "[Hcqngrq" },
+{ "serif",
+ "--" },
+{ "serif",
+ "$5" },
+{ "serif",
+ "X]" },
+{ "serif",
+ "(362)" },
+{ "serif",
+ "Q&Q" },
+{ "serif",
+ "Genvyre" },
+{ "serif",
+ "(435)" },
+{ "serif",
+ "ARNE" },
+{ "serif",
+ "fxvegf" },
+{ "serif",
+ "Rebf" },
+{ "serif",
+ "fhesnpr" },
+{ "serif",
+ "(165)" },
+{ "serif",
+ "Byqre" },
+{ "serif",
+ "Negvpyrf" },
+{ "serif",
+ "Lrfgreqnl'f" },
+{ "serif",
+ "Rqvgvba" },
+{ "arial,helvetica,serif",
+ "Cbyy" },
+{ "serif",
+ "Zl" },
+{ "serif",
+ "Fpebyy" },
+{ "serif",
+ "Ybpx" },
+{ "serif",
+ "Vf" },
+{ "serif",
+ "Ba" },
+{ "serif",
+ "Bss" },
+{ "serif",
+ "Erznccrq" },
+{ "serif",
+ "Urzbf" },
+{ "Lucida Grande",
+ "Ibgr" },
+{ "serif",
+ "[" },
+{ "serif",
+ "Erfhygf" },
+{ "serif",
+ "|" },
+{ "serif",
+ "Cbyyf" },
+{ "serif",
+ "]" },
+{ "serif",
+ "Pbzzragf:" },
+{ "serif",
+ "424" },
+{ "serif",
+ "Ibgrf:" },
+{ "serif",
+ "33028" },
+{ "arial,helvetica,serif",
+ "Obbx" },
+{ "arial,helvetica,serif",
+ "Erivrjf" },
+{ "serif",
+ "Crgre" },
+{ "serif",
+ "Jnlare" },
+{ "serif",
+ "unf" },
+{ "serif",
+ "jevggra" },
+{ "serif",
+ "Nyy" },
+{ "serif",
+ "n" },
+{ "serif",
+ "obbx" },
+{ "serif",
+ "gung" },
+{ "serif",
+ "rkcyberf" },
+{ "serif",
+ "Fbsgjner," },
+{ "serif",
+ "uvfgbel," },
+{ "serif",
+ "jurer" },
+{ "serif",
+ "vg'f" },
+{ "serif",
+ "tbvat." },
+{ "serif",
+ "Vs" },
+{ "serif",
+ "ner" },
+{ "serif",
+ "gelvat" },
+{ "serif",
+ "yrnea" },
+{ "serif",
+ "vqrnf" },
+{ "serif",
+ "oruvaq" },
+{ "serif",
+ "qvtvgny" },
+{ "serif",
+ "frphevgl," },
+{ "serif",
+ "purpx" },
+{ "serif",
+ "bhg" },
+{ "serif",
+ "Frpergf" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Yvrf" },
+{ "serif",
+ "yngrfg" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "Oehpr" },
+{ "serif",
+ "Fpuarvre." },
+{ "serif",
+ "Qnaal" },
+{ "serif",
+ "Lrr" },
+{ "serif",
+ "qvq" },
+{ "serif",
+ "erivrjf" },
+{ "serif",
+ "pbhcyr" },
+{ "serif",
+ "CUC" },
+{ "serif",
+ "obbxf." },
+{ "serif",
+ "fb" },
+{ "serif",
+ "znal" },
+{ "serif",
+ "crbcyr" },
+{ "serif",
+ "hfvat" },
+{ "serif",
+ "CUC," },
+{ "serif",
+ "vg" },
+{ "serif",
+ "nyjnlf" },
+{ "serif",
+ "cnlf" },
+{ "serif",
+ "xabj" },
+{ "serif",
+ "zber." },
+{ "serif",
+ "Ynfgyl," },
+{ "serif",
+ "Wba" },
+{ "serif",
+ "Ynffre" },
+{ "serif",
+ "Guvax" },
+{ "serif",
+ "Havk" },
+{ "serif",
+ "qrfvtarq" },
+{ "serif",
+ "nebhaq" },
+{ "serif",
+ "znxvat" },
+{ "serif",
+ "haqrefgnaq" },
+{ "serif",
+ "pbaprcgf" },
+{ "serif",
+ "Havk." },
+{ "serif",
+ "Ivfvg" },
+{ "serif",
+ "Bhe" },
+{ "serif",
+ "Obbx" },
+{ "serif",
+ "Erivrjf" },
+{ "serif",
+ "Frpgvba" },
+{ "serif",
+ "Hcqngr:" },
+{ "serif",
+ "9/21" },
+{ "serif",
+ "13:19" },
+{ "serif",
+ "U" },
+{ "serif",
+ ":" },
+{ "arial,helvetica,serif",
+ "Dhvpx" },
+{ "arial,helvetica,serif",
+ "Yvaxf" },
+{ "serif",
+ "Pbby" },
+{ "serif",
+ "Fvgrf:" },
+{ "serif",
+ "Svygul" },
+{ "serif",
+ "Pevgvp" },
+{ "serif",
+ "(Ur" },
+{ "serif",
+ "Ungrf" },
+{ "serif",
+ "Rirelguvat)" },
+{ "serif",
+ "Rirelguvat" },
+{ "serif",
+ "(Oybj" },
+{ "serif",
+ "Zvaq)" },
+{ "serif",
+ "Ns" },
+{ "serif",
+ "gre" },
+{ "serif",
+ "L2x" },
+{ "serif",
+ "(" },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "Cbfg-Ncbpnylcgvp?)" },
+{ "serif",
+ "Byq" },
+{ "serif",
+ "Zna" },
+{ "serif",
+ "Zheenl" },
+{ "serif",
+ "(Tnzrf," },
+{ "serif",
+ "Fbegn)" },
+{ "serif",
+ "Gurzrf.bet" },
+{ "serif",
+ "(Znxr" },
+{ "serif",
+ "K" },
+{ "serif",
+ "Cregl)" },
+{ "serif",
+ "Fhccbeg" },
+{ "serif",
+ "Fynfuqbg:" },
+{ "serif",
+ "(Pybgur" },
+{ "serif",
+ "Fynfuqbg)" },
+{ "arial,helvetica,serif",
+ "Serfuzrng" },
+{ "serif",
+ "Bpgbore" },
+{ "serif",
+ "28gu" },
+{ "serif",
+ "2000" },
+{ "serif",
+ "PC/Z?" },
+{ "serif",
+ "fynat" },
+{ "serif",
+ "1.4.2" },
+{ "serif",
+ "tvzc-cevag" },
+{ "serif",
+ "4.0.0" },
+{ "serif",
+ "PlorePragre" },
+{ "serif",
+ "Bayvar" },
+{ "serif",
+ "Grfgvat" },
+{ "serif",
+ "Pbzcbarag" },
+{ "serif",
+ "0.11o" },
+{ "serif",
+ "Wnpbzzn" },
+{ "serif",
+ "0.9.1" },
+{ "serif",
+ "fpebg" },
+{ "serif",
+ "0.3" },
+{ "serif",
+ "Mra" },
+{ "serif",
+ "0.1.1" },
+{ "serif",
+ "Xehfnqre" },
+{ "serif",
+ "0.69" },
+{ "serif",
+ "Fpjz" },
+{ "serif",
+ "0.99.6.2" },
+{ "serif",
+ "Obgarg" },
+{ "serif",
+ "Freivprf" },
+{ "serif",
+ "1.0.0" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Serfuzrng:" },
+{ "serif",
+ "Zber" },
+{ "serif",
+ "Zrng..." },
+{ "arial,helvetica,serif",
+ "UBJ" },
+{ "arial,helvetica,serif",
+ "LBH" },
+{ "arial,helvetica,serif",
+ "PNA" },
+{ "arial,helvetica,serif",
+ "GRYY" },
+{ "arial,helvetica,serif",
+ "GUNG" },
+{ "arial,helvetica,serif",
+ "VG'F" },
+{ "arial,helvetica,serif",
+ "TBVAT" },
+{ "arial,helvetica,serif",
+ "GB" },
+{ "arial,helvetica,serif",
+ "OR" },
+{ "arial,helvetica,serif",
+ "N" },
+{ "arial,helvetica,serif",
+ "EBGGRA" },
+{ "arial,helvetica,serif",
+ "QNL:" },
+{ "arial,helvetica,serif",
+ "#15" },
+{ "arial,helvetica,serif",
+ "Lbhe" },
+{ "arial,helvetica,serif",
+ "crg" },
+{ "arial,helvetica,serif",
+ "ebpx" },
+{ "arial,helvetica,serif",
+ "fancf" },
+{ "arial,helvetica,serif",
+ "ng" },
+{ "arial,helvetica,serif",
+ "lbh." },
+{ "arial,helvetica,serif",
+ "Nyy" },
+{ "arial,helvetica,serif",
+ "genqrznexf" },
+{ "arial,helvetica,serif",
+ "naq" },
+{ "arial,helvetica,serif",
+ "pbclevtugf" },
+{ "arial,helvetica,serif",
+ "ba" },
+{ "arial,helvetica,serif",
+ "guvf" },
+{ "arial,helvetica,serif",
+ "cntr" },
+{ "arial,helvetica,serif",
+ "ner" },
+{ "arial,helvetica,serif",
+ "bjarq" },
+{ "arial,helvetica,serif",
+ "ol" },
+{ "arial,helvetica,serif",
+ "gurve" },
+{ "arial,helvetica,serif",
+ "erfcrpgvir" },
+{ "arial,helvetica,serif",
+ "bjaref." },
+{ "arial,helvetica,serif",
+ "Pbzzragf" },
+{ "arial,helvetica,serif",
+ "gur" },
+{ "arial,helvetica,serif",
+ "Cbfgre." },
+{ "arial,helvetica,serif",
+ "Gur" },
+{ "arial,helvetica,serif",
+ "Erfg" },
+{ "arial,helvetica,serif",
+ "\xc2""\xa9""" },
+{ "arial,helvetica,serif",
+ "1997-2000" },
+{ "arial,helvetica,serif",
+ "BFQA" },
+{ "arial,helvetica,serif",
+ "." },
+{ "serif",
+ "ubzr" },
+{ "serif",
+ "njneqf" },
+{ "serif",
+ "fhccbegref" },
+{ "serif",
+ "ebo'f" },
+{ "serif",
+ "ubzrcntr" },
+{ "serif",
+ "pbagevohgr" },
+{ "serif",
+ "fgbel" },
+{ "serif",
+ "byqre" },
+{ "serif",
+ "negvpyrf" },
+{ "serif",
+ "BFQA" },
+{ "serif",
+ "nqiregvfvat" },
+{ "serif",
+ "cnfg" },
+{ "serif",
+ "cbyyf" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "snd" },
+{ "serif",
+ "8," },
+{ "serif",
+ "fynfuqbg.bet," },
+{ "serif",
+ "209" },
+{ "serif",
+ "1, 8, fynfuqbg.bet, 209" },
+{ "serif",
+ " " },
+{ "serif",
+ "byqre fghss" },
+{ "serif",
+ "ebo'f cntr" },
+{ "serif",
+ "fhozvg fgbel" },
+{ "serif",
+ "cnfg cbyyf" },
+{ "serif",
+ "10/28 (5)" },
+{ "serif",
+ "1/27" },
+{ "serif",
+ "10/26" },
+{ "serif",
+ "10/27" },
+{ "arial,helvetica,serif",
+ "Frtn Gb Sbez Wbvag Pbzcnal Jvgu Avagraqb?" },
+{ "serif",
+ "Cbfgrq ol " },
+{ "serif",
+ " ba Fhaqnl Bpgbore 29," },
+{ "serif",
+ "sebz gur fgenatr-orrcvat-orqsryybjf qrcg." },
+{ "serif",
+ " jevgrf: " },
+{ "serif",
+ "\"Frtn unf unq n ybg bs pbzcrgvgvba " },
+{ "serif",
+ "yngryl, naq juvyr gur Qernzpnfg vf irel pbby, vg unf unq " },
+{ "serif",
+ "fbzr uneq gvzrf yngryl. VTA oevatf hc guvf cvrpr bs arjf sebz bhg bs gur " },
+{ "serif",
+ "oyhr gung " },
+{ "serif",
+ "Frtn vf zlfgrevbhfyl grnzvat hc jvgu Avagraqb" },
+{ "serif",
+ ". Jul jbhyq fhpu" },
+{ "serif",
+ "ybat gvzr evinyf grnz hc nyy bs n fhqqra? Gur " },
+{ "serif",
+ " negvpyr qbrf abg tvir " },
+{ "serif",
+ "zhpu va gur jnl bs qrgnvyf, rkprcg gb zragvba gung Frtn vf tbvat gb snyy " },
+{ "serif",
+ "fubeg bs vgf rneavatf. Ubj jbhyq guvf punatr gur tnzvat vaqhfgel jvgu" },
+{ "serif",
+ "gjb znwbe cynlref jbexvat gbtrgure?\"" },
+{ "serif",
+ "( " },
+{ "serif",
+ "Ernq Zber..." },
+{ "serif",
+ " | " },
+{ "serif",
+ " bs " },
+{ "serif",
+ " " },
+{ "serif",
+ "pbzzragf " },
+{ "arial,helvetica,serif",
+ "Gur Vzcnpg ba Bcra Fbhepr bs Fgbyra Zvpebfbsg Pbqr" },
+{ "serif",
+ " ba Fngheqnl Bpgbore 28," },
+{ "serif",
+ "sebz gur urpx-pnirng-*rirelobql*! qrcg." },
+{ "serif",
+ "\"V erpragyl pnzr npebff " },
+{ "serif",
+ "guvf negvpyr" },
+{ "serif",
+ " ba " },
+{ "serif",
+ "Yvahk Wbheany" },
+{ "serif",
+ ". Vg qvfphffrf fbzr bs gur zber vagrerfgvat" },
+{ "serif",
+ "yrtny enzvsvpngvbaf bs gur gursg bs " },
+{ "serif",
+ " fbhepr pbqr gung V unqa'g " },
+{ "serif",
+ "rira gubhtug bs naq vg'f rssrpg ba bcra-fbhepr cebwrpgf. Onfvpnyyl, vg'f" },
+{ "serif",
+ "fnlvat qba'g tb arne nal pbqr pynvzvat gb or fgbyra sebz ZF, naq jvgu " },
+{ "serif",
+ "tbbq ernfba, vapyhqvat dhbgngvbaf sebz gur " },
+{ "serif",
+ " cebwrpg. Purpx vg bhg," },
+{ "serif",
+ "vg'f n tbbq ernq.\"" },
+{ "serif",
+ "Znex Rqry Nafjref Cebwrpg " },
+{ "serif",
+ "Yrnqrefuvc Dhrfgvbaf" },
+{ "serif",
+ "Cerfvqragvny Nafjref, Ebhaq Bar" },
+{ "serif",
+ "Vaqerzn'f Wbua Tvyqerq Nafjref " },
+{ "serif",
+ "Lbhe Dhrfgvbaf" },
+{ "serif",
+ "Yrnqvat N Ybj-Cebsvyr Serr " },
+{ "serif",
+ "Fbsgjner Cebwrpg" },
+{ "serif",
+ "Nfx gur Cerfvqragvny Pnaqvqngrf" },
+{ "serif",
+ "Nafjref sebz Pneaviber Erivrjre" },
+{ "serif",
+ "Urael U. Creevg, We." },
+{ "serif",
+ "Gnyx gb Bar bs gur Puvrs Pneaviber " },
+{ "serif",
+ "Nfx Wbua Tvyqerq Nobhg Vaqerzn " },
+{ "serif",
+ "Naq Yvahk Tnzvat" },
+{ "serif",
+ "IbbqbbRkgerzr Vagreivrj Jvgu " },
+{ "serif",
+ "Wbua Pneznpx" },
+{ "serif",
+ "Zvpunry Rgurggba - Fcrpvny Thrfg " },
+{ "serif",
+ "va #Cnyz" },
+{ "arial,helvetica,serif",
+ "Fynfuqbg Ybtva" },
+{ "Lucida Grande",
+ "hfreybtva" },
+{ "serif",
+ "Qba'g unir na nppbhag lrg? " },
+{ "serif",
+ "Tb Perngr" },
+{ "serif",
+ ". N hfre nppbhag jvyy nyybj lbh" },
+{ "serif",
+ "gb phfgbzvmr nyy gurfr " },
+{ "serif",
+ "ahggl yvggyr " },
+{ "serif",
+ ", gnvybe gur fgbevrf lbh frr, nf" },
+{ "serif",
+ "jryy nf erzrzore lbhe pbzzrag " },
+{ "serif",
+ "Whfg" },
+{ "serif",
+ "fbzr" },
+{ "serif",
+ "irel" },
+{ "serif",
+ "cynva" },
+{ "serif",
+ "UGZY." },
+{ "serif",
+ "Whfg fbzr irel cynva UGZY. Whfg fbzr irel cynva UGZY. Whfg fbzr irel cynva UGZY. Whfg fbzr irel cynva UGZY." },
+{ "serif",
+ "Whfg fbzr irel cynva UGZY. Whfg fbzr irel cynva UGZY. Whfg fbzr irel cynva UGZY." },
+{ "serif",
+ "9," },
+{ "serif",
+ "inavyyn-cntr," },
+{ "serif",
+ "58" },
+{ "serif",
+ "1, 9, inavyyn-cntr, 58" },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "Ubzr" },
+{ "arial,sans-serif,serif",
+ "Pbzcnevfba" },
+{ "arial,sans-serif,serif",
+ "Fubc" },
+{ "arial,sans-serif,serif",
+ "Punaaryf" },
+{ "arial,sans-serif,serif",
+ "Erjneqf" },
+{ "arial,sans-serif,serif",
+ "Rznvy" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "Gbbyf" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn\xe2""\x80""\x99""f" },
+{ "arial,sans-serif,serif",
+ "ynfg" },
+{ "arial,sans-serif,serif",
+ "zvahgr" },
+{ "arial,sans-serif,serif",
+ "tvsg" },
+{ "arial,sans-serif,serif",
+ "vqrnf!" },
+{ "arial,sans-serif,serif",
+ "Fvta" },
+{ "arial,sans-serif,serif",
+ "Hc!" },
+{ "arial,sans-serif,serif",
+ " | " },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ "Zrzoref" },
+{ "arial,sans-serif,serif",
+ "Fvta" },
+{ "arial,sans-serif,serif",
+ "Va" },
+{ "arial,sans-serif,serif",
+ "Zrzore" },
+{ "arial,sans-serif,serif",
+ "Pragre" },
+{ "arial,sans-serif,serif",
+ "Zl" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ "Jro" },
+{ "arial,sans-serif,serif",
+ "Cntr" },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "Znva" },
+{ "arial,sans-serif,serif",
+ "Nqinaprq" },
+{ "arial,sans-serif,serif",
+ "Zhygvzrqvn" },
+{ "arial,sans-serif,serif",
+ "Vzntrf" },
+{ "arial,sans-serif,serif",
+ "ZC3/Nhqvb" },
+{ "arial,sans-serif,serif",
+ "Ivqrb" },
+{ "arial,sans-serif,serif",
+ "ol" },
+{ "arial,sans-serif,serif",
+ "Gbcvp" },
+{ "arial,sans-serif,serif",
+ "Cebqhpgf" },
+{ "arial,sans-serif,serif",
+ "Arjf" },
+{ "arial,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "arial,sans-serif,serif",
+ "Ohfvarff" },
+{ "arial,sans-serif,serif",
+ "Rqhpngvba" },
+{ "arial,sans-serif,serif",
+ "Tbireazrag" },
+{ "arial,sans-serif,serif",
+ "Qbjaybnq" },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "Ratvar" },
+{ "arial,sans-serif,serif",
+ "3.0" },
+{ "arial,sans-serif,serif",
+ "Yrnea" },
+{ "arial,sans-serif,serif",
+ "Zber" },
+{ "arial,sans-serif,serif",
+ "Nobhg" },
+{ "arial,sans-serif,serif",
+ "Ohfvarff" },
+{ "arial,sans-serif,serif",
+ "Fbyhgvbaf" },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr" },
+{ "arial,sans-serif,serif",
+ "jvgu" },
+{ "arial,sans-serif,serif",
+ "Hf" },
+{ "arial,sans-serif,serif",
+ "Yvfg" },
+{ "arial,sans-serif,serif",
+ "Lbhe" },
+{ "arial,sans-serif,serif",
+ "Cebqhpgf" },
+{ "arial,sans-serif,serif",
+ "Fhozvg" },
+{ "arial,sans-serif,serif",
+ "n" },
+{ "arial,sans-serif,serif",
+ "Fvgr" },
+{ "arial,sans-serif,serif",
+ "Wbva" },
+{ "arial,sans-serif,serif",
+ "gur" },
+{ "arial,sans-serif,serif",
+ "Nssvyvngr" },
+{ "arial,sans-serif,serif",
+ "Argjbex" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "JUNG'F" },
+{ "arial,sans-serif,serif",
+ "PBBY" },
+{ "arial,sans-serif,serif",
+ "BA" },
+{ "arial,sans-serif,serif",
+ "GUR" },
+{ "arial,sans-serif,serif",
+ "JRO:" },
+{ "arial,sans-serif,serif",
+ "Yrg" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn'f" },
+{ "arial,sans-serif,serif",
+ "Arj" },
+{ "arial,sans-serif,serif",
+ "Srngher" },
+{ "arial,sans-serif,serif",
+ "Or" },
+{ "arial,sans-serif,serif",
+ "Lbhe" },
+{ "arial,sans-serif,serif",
+ "Thvqr" },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "sbe:" },
+{ "arial,sans-serif,serif",
+ "Rknzcyr:" },
+{ "arial,sans-serif,serif",
+ "+fxvvat" },
+{ "arial,sans-serif,serif",
+ "-fabjobneqvat" },
+{ "arial,sans-serif,serif",
+ "Uryc" },
+{ "arial,sans-serif,serif",
+ "Phfgbzvmr" },
+{ "arial,sans-serif,serif",
+ "Frggvatf" },
+{ "arial,sans-serif,serif",
+ "Snzvyl" },
+{ "arial,sans-serif,serif",
+ "Svygre" },
+{ "arial,sans-serif,serif",
+ "vf" },
+{ "arial,sans-serif,serif",
+ "bss" },
+{ "arial,sans-serif,serif",
+ "Zber" },
+{ "arial,sans-serif,serif",
+ "Bcgvbaf" },
+{ "Lucida Grande",
+ "nal" },
+{ "Lucida Grande",
+ "ynathntr" },
+{ "Lucida Grande",
+ "Ratyvfu" },
+{ "Lucida Grande",
+ "Puvarfr" },
+{ "Lucida Grande",
+ "Pmrpu" },
+{ "Lucida Grande",
+ "Qnavfu" },
+{ "Lucida Grande",
+ "Qhgpu" },
+{ "Lucida Grande",
+ "Rfgbavna" },
+{ "Lucida Grande",
+ "Svaavfu" },
+{ "Lucida Grande",
+ "Serapu" },
+{ "Lucida Grande",
+ "Trezna" },
+{ "Lucida Grande",
+ "Terrx" },
+{ "Lucida Grande",
+ "Uroerj" },
+{ "Lucida Grande",
+ "Uhatnevna" },
+{ "Lucida Grande",
+ "Vprynaqvp" },
+{ "Lucida Grande",
+ "Vgnyvna" },
+{ "Lucida Grande",
+ "Wncnarfr" },
+{ "Lucida Grande",
+ "Xberna" },
+{ "Lucida Grande",
+ "Yngivna" },
+{ "Lucida Grande",
+ "Yvguhnavna" },
+{ "Lucida Grande",
+ "Abejrtvna" },
+{ "Lucida Grande",
+ "Cbyvfu" },
+{ "Lucida Grande",
+ "Cbeghthrfr" },
+{ "Lucida Grande",
+ "Ebznavna" },
+{ "Lucida Grande",
+ "Ehffvna" },
+{ "Lucida Grande",
+ "Fcnavfu" },
+{ "Lucida Grande",
+ "Fjrqvfu" },
+{ "arial,sans-serif,serif",
+ "Ohfu" },
+{ "arial,sans-serif,serif",
+ "Zbirf" },
+{ "arial,sans-serif,serif",
+ "Dhvpxyl" },
+{ "arial,sans-serif,serif",
+ "gb" },
+{ "arial,sans-serif,serif",
+ "Urny" },
+{ "arial,sans-serif,serif",
+ "Jbhaqf" },
+{ "arial,sans-serif,serif",
+ "SGP" },
+{ "arial,sans-serif,serif",
+ "Nccebirf" },
+{ "arial,sans-serif,serif",
+ "NBY/Gvzr" },
+{ "arial,sans-serif,serif",
+ "Jneare" },
+{ "arial,sans-serif,serif",
+ "Qrny" },
+{ "arial,sans-serif,serif",
+ "Vfenryvf" },
+{ "arial,sans-serif,serif",
+ "Zrrg" },
+{ "arial,sans-serif,serif",
+ "Nensng," },
+{ "arial,sans-serif,serif",
+ "Crnpr" },
+{ "arial,sans-serif,serif",
+ "Gnyxf" },
+{ "arial,sans-serif,serif",
+ "Znl" },
+{ "arial,sans-serif,serif",
+ "Erfhzr" },
+{ "arial,sans-serif,serif",
+ "Arjf" },
+{ "arial,sans-serif,serif",
+ ">>" },
+{ "arial,sans-serif,serif",
+ "Svaq " },
+{ "arial,sans-serif,serif",
+ "Gur" },
+{ "arial,sans-serif,serif",
+ "ubyvqnl'f" },
+{ "arial,sans-serif,serif",
+ "ubggrfg" },
+{ "arial,sans-serif,serif",
+ "tvsgf" },
+{ "arial,sans-serif,serif",
+ "Trg " },
+{ "arial,sans-serif,serif",
+ "Yhkhel" },
+{ "arial,sans-serif,serif",
+ "vgrzf" },
+{ "arial,sans-serif,serif",
+ "sebz" },
+{ "arial,sans-serif,serif",
+ "qrfvtare" },
+{ "arial,sans-serif,serif",
+ "oenaqf" },
+{ "arial,sans-serif,serif",
+ "Jva " },
+{ "arial,sans-serif,serif",
+ "N" },
+{ "arial,sans-serif,serif",
+ "gevc" },
+{ "arial,sans-serif,serif",
+ "Q.P." },
+{ "arial,sans-serif,serif",
+ "naq" },
+{ "arial,sans-serif,serif",
+ "$10,000" },
+{ "arial,sans-serif,serif",
+ "pnfu" },
+{ "arial,sans-serif,serif",
+ "Fubccvat" },
+{ "arial,sans-serif,serif",
+ "Fcvpr" },
+{ "arial,sans-serif,serif",
+ "Hc" },
+{ "arial,sans-serif,serif",
+ "Frk" },
+{ "arial,sans-serif,serif",
+ "Yvsr" },
+{ "arial,sans-serif,serif",
+ "Trg" },
+{ "arial,sans-serif,serif",
+ "Ubebfpbcr" },
+{ "arial,sans-serif,serif",
+ "Snfuvba," },
+{ "arial,sans-serif,serif",
+ "Ornhgl" },
+{ "arial,sans-serif,serif",
+ "Gvcf" },
+{ "arial,sans-serif,serif",
+ "Jbzra" },
+{ "arial,sans-serif,serif",
+ "Snfg" },
+{ "arial,sans-serif,serif",
+ "Svaqf:" },
+{ "arial,sans-serif,serif",
+ "Erny" },
+{ "arial,sans-serif,serif",
+ "Rfgngr" },
+{ "arial,sans-serif,serif",
+ "," },
+{ "arial,sans-serif,serif",
+ "Nhgbf" },
+{ "arial,sans-serif,serif",
+ "Fpberf" },
+{ "arial,sans-serif,serif",
+ "Ubebfpbcrf" },
+{ "arial,sans-serif,serif",
+ "Zncf" },
+{ "arial,sans-serif,serif",
+ "Lryybj" },
+{ "arial,sans-serif,serif",
+ "Cntrf" },
+{ "arial,sans-serif,serif",
+ "Crbcyr" },
+{ "arial,sans-serif,serif",
+ "Svaqre" },
+{ "arial,sans-serif,serif",
+ "Ubyvqnl" },
+{ "arial,sans-serif,serif",
+ "Tvsgf" },
+{ "arial,sans-serif,serif",
+ "Svaq" },
+{ "arial,sans-serif,serif",
+ "n" },
+{ "arial,sans-serif,serif",
+ "Ynjlre" },
+{ "arial,sans-serif,serif",
+ "Ebznapr" },
+{ "arial,sans-serif,serif",
+ "Freivprf:" },
+{ "arial,sans-serif,serif",
+ "Genafyngr" },
+{ "arial,sans-serif,serif",
+ "Nhgb" },
+{ "arial,sans-serif,serif",
+ "Svanapvat" },
+{ "arial,sans-serif,serif",
+ "Yrtny" },
+{ "arial,sans-serif,serif",
+ "Geniry" },
+{ "arial,sans-serif,serif",
+ "ZlCevzrGvzr" },
+{ "arial,sans-serif,serif",
+ "Vafhenapr" },
+{ "arial,sans-serif,serif",
+ "Qbznva" },
+{ "arial,sans-serif,serif",
+ "Anzr" },
+{ "arial,sans-serif,serif",
+ "Ertvfgengvba" },
+{ "arial,sans-serif,serif",
+ "Negf" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "arial,sans-serif,serif",
+ "Negf" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "Phygher" },
+{ "arial,sans-serif,serif",
+ "Pryroevgvrf" },
+{ "arial,sans-serif,serif",
+ "Zbivrf" },
+{ "arial,sans-serif,serif",
+ "..." },
+{ "arial,sans-serif,serif",
+ "Nhgbf" },
+{ "arial,sans-serif,serif",
+ "Ohl" },
+{ "arial,sans-serif,serif",
+ "Fryy" },
+{ "arial,sans-serif,serif",
+ "Pynffvp" },
+{ "arial,sans-serif,serif",
+ "Zbgbeplpyrf" },
+{ "arial,sans-serif,serif",
+ "Pbzchgvat" },
+{ "arial,sans-serif,serif",
+ "Uneqjner" },
+{ "arial,sans-serif,serif",
+ "Vagrearg" },
+{ "arial,sans-serif,serif",
+ "Fbsgjner" },
+{ "arial,sans-serif,serif",
+ "Tnzrf" },
+{ "arial,sans-serif,serif",
+ "Tnzoyvat" },
+{ "arial,sans-serif,serif",
+ "Ebyr" },
+{ "arial,sans-serif,serif",
+ "Cynlvat" },
+{ "arial,sans-serif,serif",
+ "Ivqrb" },
+{ "arial,sans-serif,serif",
+ "Urnygu" },
+{ "arial,sans-serif,serif",
+ "Svgarff" },
+{ "arial,sans-serif,serif",
+ "Pbaqvgvbaf" },
+{ "arial,sans-serif,serif",
+ "Zrqvpvar" },
+{ "arial,sans-serif,serif",
+ "Frkhny" },
+{ "arial,sans-serif,serif",
+ "Urnygu" },
+{ "arial,sans-serif,serif",
+ "Wbof" },
+{ "arial,sans-serif,serif",
+ "Ol" },
+{ "arial,sans-serif,serif",
+ "Ertvba" },
+{ "arial,sans-serif,serif",
+ "Bpphcngvbaf" },
+{ "arial,sans-serif,serif",
+ "Fnynel" },
+{ "arial,sans-serif,serif",
+ "Yvoenel" },
+{ "arial,sans-serif,serif",
+ "Erfbheprf" },
+{ "arial,sans-serif,serif",
+ "Rqhpngvba" },
+{ "arial,sans-serif,serif",
+ "Uhznavgvrf" },
+{ "arial,sans-serif,serif",
+ "Ersrerapr" },
+{ "arial,sans-serif,serif",
+ "Yvsrfglyr" },
+{ "arial,sans-serif,serif",
+ "Snfuvba" },
+{ "arial,sans-serif,serif",
+ "Uboovrf" },
+{ "arial,sans-serif,serif",
+ "Crgf" },
+{ "arial,sans-serif,serif",
+ "Zhfvp" },
+{ "arial,sans-serif,serif",
+ "Negvfgf" },
+{ "arial,sans-serif,serif",
+ "Traerf" },
+{ "arial,sans-serif,serif",
+ "ZC3" },
+{ "arial,sans-serif,serif",
+ "Crbcyr" },
+{ "arial,sans-serif,serif",
+ "Pung" },
+{ "arial,sans-serif,serif",
+ "Pung" },
+{ "arial,sans-serif,serif",
+ "Rznvy" },
+{ "arial,sans-serif,serif",
+ "Crefbanyf" },
+{ "arial,sans-serif,serif",
+ "Crefbany" },
+{ "arial,sans-serif,serif",
+ "Vagvznpl" },
+{ "arial,sans-serif,serif",
+ "Eryngvbafuvcf" },
+{ "arial,sans-serif,serif",
+ "Geniry" },
+{ "arial,sans-serif,serif",
+ "Npgvivgvrf" },
+{ "arial,sans-serif,serif",
+ "Qrfgvangvbaf" },
+{ "arial,sans-serif,serif",
+ "Erfreingvbaf" },
+{ "arial,sans-serif,serif",
+ "Fubccvat" },
+{ "arial,sans-serif,serif",
+ "Nhpgvbaf" },
+{ "arial,sans-serif,serif",
+ "Obbxf" },
+{ "arial,sans-serif,serif",
+ "JJJ" },
+{ "arial,sans-serif,serif",
+ "Fubcf" },
+{ "arial,sans-serif,serif",
+ "Fcbegf" },
+{ "arial,sans-serif,serif",
+ "Sbbgonyy" },
+{ "arial,sans-serif,serif",
+ "Arjf" },
+{ "arial,sans-serif,serif",
+ "Fbppre" },
+{ "arial,sans-serif,serif",
+ "Jbex" },
+{ "arial,sans-serif,serif",
+ "Zbarl" },
+{ "arial,sans-serif,serif",
+ "Pbzcnavrf" },
+{ "arial,sans-serif,serif",
+ "Vairfgvat" },
+{ "arial,sans-serif,serif",
+ "Fznyy" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ "Nebhaq" },
+{ "arial,sans-serif,serif",
+ "gur" },
+{ "arial,sans-serif,serif",
+ "Jbeyq:" },
+{ "arial,sans-serif,serif",
+ "Nhfgenyvn" },
+{ "arial,sans-serif,serif",
+ "Oenmvy" },
+{ "arial,sans-serif,serif",
+ "Qraznex" },
+{ "arial,sans-serif,serif",
+ "Senapr" },
+{ "arial,sans-serif,serif",
+ "Treznal" },
+{ "arial,sans-serif,serif",
+ "Vaqvn" },
+{ "arial,sans-serif,serif",
+ "Verynaq" },
+{ "arial,sans-serif,serif",
+ "Vgnyl" },
+{ "arial,sans-serif,serif",
+ "Argureynaqf" },
+{ "arial,sans-serif,serif",
+ "Fcnva" },
+{ "arial,sans-serif,serif",
+ "Fjrqra" },
+{ "arial,sans-serif,serif",
+ "HX" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ "Cnegaref:" },
+{ "arial,sans-serif,serif",
+ "Pnef.pbz" },
+{ "arial,sans-serif,serif",
+ "SBKFCBEGF.PBZ" },
+{ "arial,sans-serif,serif",
+ "Zbir.pbz" },
+{ "arial,sans-serif,serif",
+ "Jbzra.pbz" },
+{ "arial,sans-serif,serif",
+ "HFYnj.pbz" },
+{ "arial,sans-serif,serif",
+ "rovk.pbz" },
+{ "arial,sans-serif,serif",
+ "hOvq.pbz" },
+{ "arial,sans-serif,serif",
+ "Cersref" },
+{ "arial,sans-serif,serif",
+ "|" },
+{ "arial,sans-serif,serif",
+ "Grezf" },
+{ "arial,sans-serif,serif",
+ "bs" },
+{ "arial,sans-serif,serif",
+ "Hfr" },
+{ "arial,sans-serif,serif",
+ "Cevinpl" },
+{ "arial,sans-serif,serif",
+ "Cbyvpl" },
+{ "arial,sans-serif,serif",
+ "Pbagnpg" },
+{ "arial,sans-serif,serif",
+ "hf" },
+{ "arial,sans-serif,serif",
+ "Grkg" },
+{ "arial,sans-serif,serif",
+ "Bayl" },
+{ "arial,sans-serif,serif",
+ "Fhozvg" },
+{ "arial,sans-serif,serif",
+ "Fvgr" },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr" },
+{ "arial,sans-serif,serif",
+ "Jvgu" },
+{ "arial,sans-serif,serif",
+ "Hf" },
+{ "arial,sans-serif,serif",
+ "Wbof" },
+{ "arial,sans-serif,serif",
+ "PZTV" },
+{ "arial,sans-serif,serif",
+ "Pbzcnal" },
+{ "arial,sans-serif,serif",
+ "\xc2""\xa9""" },
+{ "arial,sans-serif,serif",
+ "2000" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ "Pbzcnal." },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn\xc2""\xae""" },
+{ "arial,sans-serif,serif",
+ "vf" },
+{ "arial,sans-serif,serif",
+ "n" },
+{ "arial,sans-serif,serif",
+ "ertvfgrerq" },
+{ "arial,sans-serif,serif",
+ "genqrznex" },
+{ "arial,sans-serif,serif",
+ "naq" },
+{ "arial,sans-serif,serif",
+ "Fzneg" },
+{ "arial,sans-serif,serif",
+ "Vf" },
+{ "arial,sans-serif,serif",
+ "Ornhgvshy" },
+{ "arial,sans-serif,serif",
+ "gur" },
+{ "arial,sans-serif,serif",
+ "ybtb" },
+{ "arial,sans-serif,serif",
+ "ner" },
+{ "arial,sans-serif,serif",
+ "genqrznexf" },
+{ "arial,sans-serif,serif",
+ "bs" },
+{ "arial,sans-serif,serif",
+ "." },
+{ "serif",
+ "10," },
+{ "serif",
+ "jjj.nygnivfgn.pbz," },
+{ "serif",
+ "237" },
+{ "serif",
+ "1, 10, jjj.nygnivfgn.pbz, 237" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " Jro Cntr Frnepu" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Zhygvzrqvn Frnepu" },
+{ "arial,sans-serif,serif",
+ "Frnepu ol Gbcvp" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Qbjaybnq NygnIvfgn " },
+{ "arial,sans-serif,serif",
+ "Frnepu Ratvar 3.0" },
+{ "arial,sans-serif,serif",
+ "Yrnea Zber Nobhg " },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn Ohfvarff " },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr jvgu Hf" },
+{ "arial,sans-serif,serif",
+ "Yvfg Lbhe Cebqhpgf" },
+{ "arial,sans-serif,serif",
+ "Fhozvg n Fvgr" },
+{ "arial,sans-serif,serif",
+ "Wbva gur Nssvyvngr Argjbex" },
+{ "arial,sans-serif,serif",
+ "Frnepu Ubzr" },
+{ "arial,sans-serif,serif",
+ "Pbzcnevfba Fubc" },
+{ "arial,sans-serif,serif",
+ "Rznvy & Gbbyf" },
+{ "arial,sans-serif,serif",
+ "NygnIvfgn\xe2""\x80""\x99""f ynfg zvahgr tvsg vqrnf!" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Fvta Hc!" },
+{ "arial,sans-serif,serif",
+ " | NygnIvfgn Zrzoref " },
+{ "arial,sans-serif,serif",
+ "Fvta Va" },
+{ "arial,sans-serif,serif",
+ " | " },
+{ "arial,sans-serif,serif",
+ "Zrzore Pragre" },
+{ "arial,sans-serif,serif",
+ "Zl NygnIvfgn" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "JUNG'F PBBY BA GUR JRO:" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Yrg NygnIvfgn'f Arj Srngher Or Lbhe Thvqr" },
+{ "arial,sans-serif,serif",
+ "Frnepu sbe:" },
+{ "arial,sans-serif,serif",
+ "Phfgbzvmr Frggvatf" },
+{ "arial,sans-serif,serif",
+ "Snzvyl Svygre vf " },
+{ "Lucida Grande",
+ "nal ynathntr" },
+{ "arial,sans-serif,serif",
+ "Rknzcyr: " },
+{ "arial,sans-serif,serif",
+ "+fxvvat -fabjobneqvat" },
+{ "arial,sans-serif,serif",
+ "Zber Frnepu Bcgvbaf" },
+{ "arial,sans-serif,serif",
+ "Ohfu Zbirf Dhvpxyl gb Urny Jbhaqf" },
+{ "arial,sans-serif,serif",
+ "SGP Nccebirf NBY/Gvzr Jneare Qrny" },
+{ "arial,sans-serif,serif",
+ "Vfenryvf Zrrg Nensng, Crnpr Gnyxf Znl " },
+{ "arial,sans-serif,serif",
+ "Frnepu Arjf" },
+{ "arial,sans-serif,serif",
+ " >>" },
+{ "arial,sans-serif,serif",
+ "Svaq " },
+{ "arial,sans-serif,serif",
+ "Gur ubyvqnl'f ubggrfg tvsgf" },
+{ "arial,sans-serif,serif",
+ "Trg " },
+{ "arial,sans-serif,serif",
+ "Yhkhel vgrzf sebz qrfvtare oenaqf" },
+{ "arial,sans-serif,serif",
+ "Jva " },
+{ "arial,sans-serif,serif",
+ "N gevc gb Q.P. naq $10,000 pnfu" },
+{ "arial,sans-serif,serif",
+ "Frnepu Fubccvat" },
+{ "arial,sans-serif,serif",
+ "Fcvpr Hc Lbhe Frk Yvsr" },
+{ "arial,sans-serif,serif",
+ "Trg Lbhe Ubebfpbcr" },
+{ "arial,sans-serif,serif",
+ "Trg Snfuvba, Ornhgl Gvcf" },
+{ "arial,sans-serif,serif",
+ "Frnepu Jbzra" },
+{ "arial,sans-serif,serif",
+ "Snfg Svaqf:" },
+{ "arial,sans-serif,serif",
+ "Erny Rfgngr" },
+{ "arial,sans-serif,serif",
+ ", " },
+{ "arial,sans-serif,serif",
+ "Lryybj Cntrf" },
+{ "arial,sans-serif,serif",
+ "Crbcyr Svaqre" },
+{ "arial,sans-serif,serif",
+ "Ubyvqnl Tvsgf" },
+{ "arial,sans-serif,serif",
+ "Svaq n Ynjlre" },
+{ "arial,sans-serif,serif",
+ "Ebznapr Gvcf" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Nhgb Svanapvat" },
+{ "arial,sans-serif,serif",
+ "Yrtny Uryc" },
+{ "arial,sans-serif,serif",
+ "Qbznva Anzr Ertvfgengvba" },
+{ "arial,sans-serif,serif",
+ "Negf & Ragregnvazrag" },
+{ "arial,sans-serif,serif",
+ "Negf & Phygher" },
+{ "arial,sans-serif,serif",
+ "Ohl & Fryy" },
+{ "arial,sans-serif,serif",
+ "Ebyr Cynlvat" },
+{ "arial,sans-serif,serif",
+ "Urnygu & Svgarff" },
+{ "arial,sans-serif,serif",
+ "Frkhny Urnygu" },
+{ "arial,sans-serif,serif",
+ "..." },
+{ "arial,sans-serif,serif",
+ "Ol Ertvba" },
+{ "arial,sans-serif,serif",
+ "Yvoenel & Erfbheprf" },
+{ "arial,sans-serif,serif",
+ "Crbcyr & Pung" },
+{ "arial,sans-serif,serif",
+ "Vagvznpl & Frk" },
+{ "arial,sans-serif,serif",
+ "Ohl Obbxf" },
+{ "arial,sans-serif,serif",
+ "JJJ Fubcf" },
+{ "arial,sans-serif,serif",
+ "Arjf & Fpberf" },
+{ "arial,sans-serif,serif",
+ "Jbex & Zbarl" },
+{ "arial,sans-serif,serif",
+ "Fznyy Ohfvarff" },
+{ "verdana,arial,helvetica,serif",
+ "Trg" },
+{ "verdana,arial,helvetica,serif",
+ "gvcf" },
+{ "verdana,arial,helvetica,serif",
+ "ba" },
+{ "verdana,arial,helvetica,serif",
+ "frnepuvat" },
+{ "verdana,arial,helvetica,serif",
+ "naq" },
+{ "verdana,arial,helvetica,serif",
+ "beqrevat!" },
+{ "verdana,arial,helvetica,serif",
+ "Frnepu" },
+{ "verdana,arial,helvetica,serif",
+ "bhe" },
+{ "verdana,arial,helvetica,serif",
+ "Uryc" },
+{ "verdana,arial,helvetica,serif",
+ "qrcnegzrag" },
+{ "verdana,arial,helvetica,serif",
+ "." },
+{ "Lucida Grande",
+ "Nyy" },
+{ "Lucida Grande",
+ "Cebqhpgf" },
+{ "Lucida Grande",
+ "Obbxf" },
+{ "Lucida Grande",
+ "Cbchyne" },
+{ "Lucida Grande",
+ "Zhfvp" },
+{ "Lucida Grande",
+ "Pynffvpny" },
+{ "Lucida Grande",
+ "QIQ" },
+{ "Lucida Grande",
+ "IUF" },
+{ "Lucida Grande",
+ "Gblf" },
+{ "Lucida Grande",
+ "Ivqrb" },
+{ "Lucida Grande",
+ "Tnzrf" },
+{ "Lucida Grande",
+ "Ryrpgebavpf" },
+{ "Lucida Grande",
+ "Pnzren" },
+{ "Lucida Grande",
+ "&" },
+{ "Lucida Grande",
+ "Cubgb" },
+{ "Lucida Grande",
+ "Fbsgjner" },
+{ "Lucida Grande",
+ "Gbbyf" },
+{ "Lucida Grande",
+ "Uneqjner" },
+{ "Lucida Grande",
+ "Ynja" },
+{ "Lucida Grande",
+ "Cngvb" },
+{ "Lucida Grande",
+ "Xvgpura" },
+{ "Lucida Grande",
+ "Arj" },
+{ "Lucida Grande",
+ "Pnef" },
+{ "Lucida Grande",
+ "Jveryrff" },
+{ "Lucida Grande",
+ "Cubarf" },
+{ "Lucida Grande",
+ "Nhpgvbaf" },
+{ "Lucida Grande",
+ "mFubcf" },
+{ "verdana,arial,helvetica,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "verdana,arial,helvetica,serif",
+ "Obbxf" },
+{ "verdana,arial,helvetica,serif",
+ "Ryrpgebavpf" },
+{ "verdana,arial,helvetica,serif",
+ "Gblf" },
+{ "verdana,arial,helvetica,serif",
+ "Xvgpura" },
+{ "verdana,arial,helvetica,serif",
+ "QIQ" },
+{ "verdana,arial,helvetica,serif",
+ "Ivqrb" },
+{ "verdana,arial,helvetica,serif",
+ "Zhfvp" },
+{ "verdana,arial,helvetica,serif",
+ "Tvsgf" },
+{ "verdana,arial,helvetica,serif",
+ "Urnygu" },
+{ "verdana,arial,helvetica,serif",
+ "&" },
+{ "verdana,arial,helvetica,serif",
+ "Ornhgl" },
+{ "verdana,arial,helvetica,serif",
+ "Jveryrff" },
+{ "verdana,arial,helvetica,serif",
+ "Cubarf" },
+{ "verdana,arial,helvetica,serif",
+ "Pnzren" },
+{ "verdana,arial,helvetica,serif",
+ "Cubgb" },
+{ "verdana,arial,helvetica,serif",
+ "Pbzchgre" },
+{ "verdana,arial,helvetica,serif",
+ "Tnzrf" },
+{ "verdana,arial,helvetica,serif",
+ "Fbsgjner" },
+{ "verdana,arial,helvetica,serif",
+ "Gbbyf" },
+{ "verdana,arial,helvetica,serif",
+ "Uneqjner" },
+{ "verdana,arial,helvetica,serif",
+ "Ynja" },
+{ "verdana,arial,helvetica,serif",
+ "Cngvb" },
+{ "verdana,arial,helvetica,serif",
+ "Arj" },
+{ "verdana,arial,helvetica,serif",
+ "Pnef" },
+{ "verdana,arial,helvetica,serif",
+ "Nhpgvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "mFubcf" },
+{ "verdana,arial,helvetica,serif",
+ "Serr" },
+{ "verdana,arial,helvetica,serif",
+ "r-Pneqf" },
+{ "verdana,arial,helvetica,serif",
+ "Fcrpvny" },
+{ "verdana,arial,helvetica,serif",
+ "Srngherf" },
+{ "verdana,arial,helvetica,serif",
+ "\xe2""\x80""\xa2""" },
+{ "verdana,arial,helvetica,serif",
+ "Rnea" },
+{ "verdana,arial,helvetica,serif",
+ "Tvsg" },
+{ "verdana,arial,helvetica,serif",
+ "Pregvsvpngrf" },
+{ "verdana,arial,helvetica,serif",
+ "Chepunfr" },
+{ "verdana,arial,helvetica,serif",
+ "Pvepyrf" },
+{ "verdana,arial,helvetica,serif",
+ "Nznmba.pbz" },
+{ "verdana,arial,helvetica,serif",
+ "Naljurer" },
+{ "verdana,arial,helvetica,serif",
+ "Trg" },
+{ "verdana,arial,helvetica,serif",
+ "vasb" },
+{ "verdana,arial,helvetica,serif",
+ "ba" },
+{ "verdana,arial,helvetica,serif",
+ "arj" },
+{ "verdana,arial,helvetica,serif",
+ "eryrnfrf" },
+{ "verdana,arial,helvetica,serif",
+ "jvgu" },
+{ "verdana,arial,helvetica,serif",
+ "Nyregf" },
+{ "verdana,arial,helvetica,serif",
+ "Nffbpvngrf" },
+{ "verdana,arial,helvetica,serif",
+ "Fryy" },
+{ "verdana,arial,helvetica,serif",
+ "obbxf," },
+{ "verdana,arial,helvetica,serif",
+ "zhfvp," },
+{ "verdana,arial,helvetica,serif",
+ "ivqrbf," },
+{ "verdana,arial,helvetica,serif",
+ "zber" },
+{ "verdana,arial,helvetica,serif",
+ "sebz" },
+{ "verdana,arial,helvetica,serif",
+ "lbhe" },
+{ "verdana,arial,helvetica,serif",
+ "Jro" },
+{ "verdana,arial,helvetica,serif",
+ "fvgr." },
+{ "verdana,arial,helvetica,serif",
+ "Fgneg" },
+{ "verdana,arial,helvetica,serif",
+ "rneavat" },
+{ "verdana,arial,helvetica,serif",
+ "gbqnl" },
+{ "verdana,arial,helvetica,serif",
+ "!" },
+{ "serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Uryyb." },
+{ "verdana,arial,helvetica,serif",
+ " \xe2""\x80""\xa2""" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvg" },
+{ "verdana,arial,helvetica,serif",
+ "Tvsgf" },
+{ "verdana,arial,helvetica,serif",
+ "Gel" },
+{ "verdana,arial,helvetica,serif",
+ "gur" },
+{ "verdana,arial,helvetica,serif",
+ "Jvmneq" },
+{ "verdana,arial,helvetica,serif",
+ "Ohl" },
+{ "verdana,arial,helvetica,serif",
+ "n" },
+{ "verdana,arial,helvetica,serif",
+ "tvsg" },
+{ "verdana,arial,helvetica,serif",
+ "pregvsvpngr" },
+{ "verdana,arial,helvetica,serif",
+ "Ivrj" },
+{ "verdana,arial,helvetica,serif",
+ "phfgbzre'f" },
+{ "verdana,arial,helvetica,serif",
+ "Jvfu" },
+{ "verdana,arial,helvetica,serif",
+ "Yvfg" },
+{ "verdana,arial,helvetica,serif",
+ "be" },
+{ "verdana,arial,helvetica,serif",
+ "hcqngr" },
+{ "verdana,arial,helvetica,serif",
+ "bja" },
+{ "verdana,arial,helvetica,serif",
+ "Frr" },
+{ "verdana,arial,helvetica,serif",
+ "beqrevat" },
+{ "verdana,arial,helvetica,serif",
+ "qrnqyvarf" },
+{ "verdana,arial,helvetica,serif",
+ "27,336,632" },
+{ "verdana,arial,helvetica,serif",
+ "Nyernql" },
+{ "verdana,arial,helvetica,serif",
+ "n" },
+{ "verdana,arial,helvetica,serif",
+ "phfgbzre?" },
+{ "verdana,arial,helvetica,serif",
+ "Fvta" },
+{ "verdana,arial,helvetica,serif",
+ "va" },
+{ "verdana,arial,helvetica,serif",
+ "gb" },
+{ "verdana,arial,helvetica,serif",
+ "frr" },
+{ "verdana,arial,helvetica,serif",
+ "jung'f" },
+{ "verdana,arial,helvetica,serif",
+ "sbe" },
+{ "verdana,arial,helvetica,serif",
+ "Lbh" },
+{ "verdana,arial,helvetica,serif",
+ "Yvgrengher" },
+{ "verdana,arial,helvetica,serif",
+ "&" },
+{ "verdana,arial,helvetica,serif",
+ "Svpgvba" },
+{ "verdana,arial,helvetica,serif",
+ "Cbc" },
+{ "verdana,arial,helvetica,serif",
+ "QIQ" },
+{ "verdana,arial,helvetica,serif",
+ "Qenzn" },
+{ "verdana,arial,helvetica,serif",
+ "Tebjahcf" },
+{ "verdana,arial,helvetica,serif",
+ "Ubzr" },
+{ "verdana,arial,helvetica,serif",
+ "Nhqvb" },
+{ "verdana,arial,helvetica,serif",
+ "Zber" },
+{ "verdana,arial,helvetica,serif",
+ "Eryrnfrf" },
+{ "verdana,arial,helvetica,serif",
+ "5,622%" },
+{ "verdana,arial,helvetica,serif",
+ "Oyrffvatf" },
+{ "verdana,arial,helvetica,serif",
+ "Bgure" },
+{ "verdana,arial,helvetica,serif",
+ "Fvqr" },
+{ "verdana,arial,helvetica,serif",
+ "ol" },
+{ "verdana,arial,helvetica,serif",
+ "Flyivn" },
+{ "verdana,arial,helvetica,serif",
+ "Oebjar," },
+{ "verdana,arial,helvetica,serif",
+ "Yvaqfnl" },
+{ "verdana,arial,helvetica,serif",
+ "Uneevfba(Pbagevohgbe)" },
+{ "verdana,arial,helvetica,serif",
+ "795%" },
+{ "verdana,arial,helvetica,serif",
+ "Unaqfcevat" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfbe" },
+{ "verdana,arial,helvetica,serif",
+ "Qryhkr" },
+{ "verdana,arial,helvetica,serif",
+ "(Oyhr)" },
+{ "verdana,arial,helvetica,serif",
+ "Frr" },
+{ "verdana,arial,helvetica,serif",
+ "nyy" },
+{ "verdana,arial,helvetica,serif",
+ "gur" },
+{ "verdana,arial,helvetica,serif",
+ "Zbiref" },
+{ "verdana,arial,helvetica,serif",
+ "Funxref" },
+{ "verdana,arial,helvetica,serif",
+ "trg" },
+{ "verdana,arial,helvetica,serif",
+ "erpbzzraqngvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "Frtn" },
+{ "verdana,arial,helvetica,serif",
+ "Qernzpnfg" },
+{ "verdana,arial,helvetica,serif",
+ "pbafbyr" },
+{ "verdana,arial,helvetica,serif",
+ "$50" },
+{ "verdana,arial,helvetica,serif",
+ "gbjneq" },
+{ "verdana,arial,helvetica,serif",
+ "nal" },
+{ "verdana,arial,helvetica,serif",
+ "ivqrb" },
+{ "verdana,arial,helvetica,serif",
+ "tnzr" },
+{ "verdana,arial,helvetica,serif",
+ "fbsgjner" },
+{ "verdana,arial,helvetica,serif",
+ "npprffbel--guebhtu" },
+{ "verdana,arial,helvetica,serif",
+ "Qrprzore" },
+{ "verdana,arial,helvetica,serif",
+ "18" },
+{ "verdana,arial,helvetica,serif",
+ "bayl!" },
+{ "verdana,arial,helvetica,serif",
+ "Gbc" },
+{ "verdana,arial,helvetica,serif",
+ "Fryyref" },
+{ "verdana,arial,helvetica,serif",
+ "va" },
+{ "verdana,arial,helvetica,serif",
+ "Gur" },
+{ "verdana,arial,helvetica,serif",
+ "Fvzf" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "vqrn" },
+{ "serif",
+ "oruvaq" },
+{ "serif",
+ "Fvzf" },
+{ "serif",
+ "guvf:" },
+{ "serif",
+ "hygvzngr" },
+{ "serif",
+ "tbny" },
+{ "serif",
+ "npuvrir" },
+{ "serif",
+ "unccvarff," },
+{ "serif",
+ "naq" },
+{ "serif",
+ "jnl" },
+{ "serif",
+ "unccvarff" },
+{ "serif",
+ "fngvfsl" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "Fvzf'" },
+{ "serif",
+ "arrqf." },
+{ "serif",
+ "Gung'f" },
+{ "serif",
+ "evtug," },
+{ "serif",
+ "nyybjf" },
+{ "serif",
+ "lbh" },
+{ "serif",
+ "perngr," },
+{ "serif",
+ "qverpg," },
+{ "serif",
+ "znantr" },
+{ "serif",
+ "yvirf" },
+{ "serif",
+ "FvzPvgl" },
+{ "serif",
+ "'f" },
+{ "serif",
+ "erfvqragf." },
+{ "serif",
+ "Rnpu" },
+{ "serif",
+ "arrq" },
+{ "serif",
+ "(Uhatre," },
+{ "serif",
+ "Pbzsbeg," },
+{ "serif",
+ "Ultvrar," },
+{ "serif",
+ "Oynqqre,..." },
+{ "serif",
+ "Ernq" },
+{ "serif",
+ "zber" },
+{ "verdana,arial,helvetica,serif",
+ "Fryyref:" },
+{ "serif",
+ "Grxab" },
+{ "serif",
+ "Ebobgvp" },
+{ "serif",
+ "Chccl" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "Znayrl" },
+{ "serif",
+ "Cnyz" },
+{ "serif",
+ "VVVkr" },
+{ "serif",
+ "Unaquryq" },
+{ "serif",
+ "CNYZ" },
+{ "serif",
+ "Inph" },
+{ "serif",
+ "Iva" },
+{ "serif",
+ "Jvar" },
+{ "serif",
+ "Fnire" },
+{ "serif",
+ "Tvsg" },
+{ "serif",
+ "Cnpx," },
+{ "serif",
+ "Juvgr" },
+{ "serif",
+ "Va" },
+{ "serif",
+ "Urnygu" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Ornhgl" },
+{ "verdana,arial,helvetica,serif",
+ "Orngyrf" },
+{ "verdana,arial,helvetica,serif",
+ "Nagubybtl" },
+{ "serif",
+ "Nsgre" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "gurfr" },
+{ "serif",
+ "gurer" },
+{ "serif",
+ "nalguvat" },
+{ "serif",
+ "arj" },
+{ "serif",
+ "fnl" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "Orngyrf?" },
+{ "serif",
+ "Bayl" },
+{ "serif",
+ "vs" },
+{ "serif",
+ "tb" },
+{ "serif",
+ "fgenvtug" },
+{ "serif",
+ "thlf" },
+{ "serif",
+ "jub" },
+{ "serif",
+ "unira'g" },
+{ "serif",
+ "unq" },
+{ "serif",
+ "shyy" },
+{ "serif",
+ "fnl:" },
+{ "serif",
+ "Orngyrf" },
+{ "serif",
+ "gurzfryirf" },
+{ "serif",
+ "pybfrfg" },
+{ "serif",
+ "pbasvqnagf." },
+{ "serif",
+ "Orngyrf" },
+{ "serif",
+ "Nagubybtl" },
+{ "serif",
+ "xnyrvqbfpbcr" },
+{ "serif",
+ "rlrf--naq" },
+{ "serif",
+ "creprcgvir" },
+{ "serif",
+ "barf" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "gung." },
+{ "serif",
+ "obbx" },
+{ "serif",
+ "snfpvangvat" },
+{ "serif",
+ "ba..." },
+{ "verdana,arial,helvetica,serif",
+ "Eryrnfrf:" },
+{ "serif",
+ "Zhfvp" },
+{ "serif",
+ "QIQ" },
+{ "serif",
+ "Gblf" },
+{ "serif",
+ "Tnzrf" },
+{ "serif",
+ "Ryrpgebavpf" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "Pnef" },
+{ "verdana,arial,helvetica,serif",
+ "Furq" },
+{ "verdana,arial,helvetica,serif",
+ "Fbzr" },
+{ "verdana,arial,helvetica,serif",
+ "Fjrrg" },
+{ "verdana,arial,helvetica,serif",
+ "Yvtug" },
+{ "serif",
+ "Svyy" },
+{ "serif",
+ "ubzr" },
+{ "serif",
+ "fprag" },
+{ "serif",
+ "cbzrtenangr." },
+{ "serif",
+ "Ivfvg" },
+{ "serif",
+ "qehtfgber.pbz" },
+{ "serif",
+ "ubyvqnl" },
+{ "serif",
+ "fgber" },
+{ "serif",
+ "," },
+{ "serif",
+ "jurer" },
+{ "serif",
+ "lbh'yy" },
+{ "serif",
+ "svaq" },
+{ "serif",
+ "tybjvat" },
+{ "serif",
+ "tvsgf" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "rirelbar" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "yvfg." },
+{ "serif",
+ "Jr'yy" },
+{ "serif",
+ "rira" },
+{ "serif",
+ "gerng" },
+{ "serif",
+ "serr" },
+{ "serif",
+ "cbzrtenangr" },
+{ "serif",
+ "pnaqyr-va-n-gva" },
+{ "serif",
+ "nal" },
+{ "serif",
+ "abacerfpevcgvba" },
+{ "serif",
+ "chepunfr." },
+{ "serif",
+ "Yvtug" },
+{ "serif",
+ "Zber" },
+{ "serif",
+ "Jnlf" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "Znvagnva" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "Ubyvqnl" },
+{ "serif",
+ "Ngzbfcurer:" },
+{ "serif",
+ "\xe2""\x80""\xa2""" },
+{ "serif",
+ "Purreshy" },
+{ "serif",
+ "gevzzvatf" },
+{ "serif",
+ "Wblshy" },
+{ "serif",
+ "zhfvp" },
+{ "verdana,arial,helvetica,serif",
+ "Gblfehf.pbz" },
+{ "verdana,arial,helvetica,serif",
+ "Vf" },
+{ "verdana,arial,helvetica,serif",
+ "Urer!" },
+{ "serif",
+ "Jurgure" },
+{ "serif",
+ "lbh'er" },
+{ "serif",
+ "fubccvat" },
+{ "serif",
+ "lbhat" },
+{ "serif",
+ "be" },
+{ "serif",
+ "urneg," },
+{ "serif",
+ "or" },
+{ "serif",
+ "fher" },
+{ "serif",
+ "ivfvg" },
+{ "serif",
+ "bhe" },
+{ "serif",
+ "Nznmba.pbz" },
+{ "serif",
+ "grnzrq" },
+{ "serif",
+ "hc" },
+{ "serif",
+ "Gblfehf.pbz" },
+{ "serif",
+ "oevat" },
+{ "serif",
+ "orfg" },
+{ "serif",
+ "fryrpgvba," },
+{ "serif",
+ "ubggrfg" },
+{ "serif",
+ "gblf" },
+{ "serif",
+ "frnfba," },
+{ "serif",
+ "greevsvp" },
+{ "serif",
+ "qrnyf." },
+{ "serif",
+ "Sebz" },
+{ "serif",
+ "yngrfg" },
+{ "serif",
+ "Oneovr" },
+{ "serif",
+ "mvccvrfg" },
+{ "serif",
+ "fpbbgre" },
+{ "serif",
+ "gehfgrq" },
+{ "serif",
+ "oenaqf" },
+{ "serif",
+ "fhpu" },
+{ "serif",
+ "nf" },
+{ "serif",
+ "Svfure-Cevpr" },
+{ "serif",
+ "Yvggyr" },
+{ "serif",
+ "Gvxrf" },
+{ "serif",
+ "YRTB" },
+{ "serif",
+ "jr" },
+{ "serif",
+ "unir" },
+{ "serif",
+ "pbirerq." },
+{ "serif",
+ "Rkcyber:" },
+{ "serif",
+ "Arrq" },
+{ "serif",
+ "vqrnf?" },
+{ "serif",
+ "Gel" },
+{ "serif",
+ "bhe" },
+{ "serif",
+ "Jvmneq" },
+{ "serif",
+ "xvqf" },
+{ "serif",
+ "Tbg" },
+{ "serif",
+ "tnzref?" },
+{ "serif",
+ "Fgbc" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "snzvyl" },
+{ "serif",
+ "ivqrb" },
+{ "serif",
+ "tnzrf" },
+{ "verdana,arial,helvetica,serif",
+ "gb" },
+{ "verdana,arial,helvetica,serif",
+ "Rkcyber" },
+{ "verdana,arial,helvetica,serif",
+ "Nfusbeq.pbz" },
+{ "verdana,arial,helvetica,serif",
+ "Svar" },
+{ "verdana,arial,helvetica,serif",
+ "crefbany" },
+{ "verdana,arial,helvetica,serif",
+ "npprffbevrf." },
+{ "verdana,arial,helvetica,serif",
+ "ArkgPneq" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfn" },
+{ "verdana,arial,helvetica,serif",
+ "erjneq" },
+{ "verdana,arial,helvetica,serif",
+ "cbvagf" },
+{ "verdana,arial,helvetica,serif",
+ "pregvsvpngrf!" },
+{ "verdana,arial,helvetica,serif",
+ "VZQo.pbz" },
+{ "verdana,arial,helvetica,serif",
+ "Njneq-jvaavat" },
+{ "verdana,arial,helvetica,serif",
+ "zbivr" },
+{ "verdana,arial,helvetica,serif",
+ "Jurer'f" },
+{ "verdana,arial,helvetica,serif",
+ "Zl" },
+{ "verdana,arial,helvetica,serif",
+ "Fghss?" },
+{ "verdana,arial,helvetica,serif",
+ "Genpx" },
+{ "verdana,arial,helvetica,serif",
+ "lbhe" },
+{ "verdana,arial,helvetica,serif",
+ "erprag" },
+{ "verdana,arial,helvetica,serif",
+ "beqref" },
+{ "verdana,arial,helvetica,serif",
+ "Ivrj" },
+{ "verdana,arial,helvetica,serif",
+ "be" },
+{ "verdana,arial,helvetica,serif",
+ "punatr" },
+{ "verdana,arial,helvetica,serif",
+ "beqref" },
+{ "verdana,arial,helvetica,serif",
+ "va" },
+{ "verdana,arial,helvetica,serif",
+ "Lbhe" },
+{ "verdana,arial,helvetica,serif",
+ "Nppbhag" },
+{ "verdana,arial,helvetica,serif",
+ "Fuvccvat" },
+{ "verdana,arial,helvetica,serif",
+ "Ergheaf" },
+{ "verdana,arial,helvetica,serif",
+ "Frr" },
+{ "verdana,arial,helvetica,serif",
+ "bhe" },
+{ "verdana,arial,helvetica,serif",
+ "fuvccvat" },
+{ "verdana,arial,helvetica,serif",
+ "engrf" },
+{ "verdana,arial,helvetica,serif",
+ "cbyvpvrf" },
+{ "verdana,arial,helvetica,serif",
+ "Erghea" },
+{ "verdana,arial,helvetica,serif",
+ "na" },
+{ "verdana,arial,helvetica,serif",
+ "vgrz" },
+{ "verdana,arial,helvetica,serif",
+ "(urer'f" },
+{ "verdana,arial,helvetica,serif",
+ "Ergheaf" },
+{ "verdana,arial,helvetica,serif",
+ "Cbyvpl" },
+{ "verdana,arial,helvetica,serif",
+ ")." },
+{ "verdana,arial,helvetica,serif",
+ "Tvsg" },
+{ "verdana,arial,helvetica,serif",
+ "Pregvsvpngrf" },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl" },
+{ "verdana,arial,helvetica,serif",
+ "Erqrrz" },
+{ "verdana,arial,helvetica,serif",
+ "ohl" },
+{ "verdana,arial,helvetica,serif",
+ "pregvsvpngrf." },
+{ "verdana,arial,helvetica,serif",
+ "Fgvyy" },
+{ "verdana,arial,helvetica,serif",
+ "unir" },
+{ "verdana,arial,helvetica,serif",
+ "dhrfgvba?" },
+{ "verdana,arial,helvetica,serif",
+ "Frnepu " },
+{ "verdana,arial,helvetica,serif",
+ " sbe " },
+{ "verdana,arial,helvetica,serif",
+ "Grkg" },
+{ "verdana,arial,helvetica,serif",
+ "Bayl" },
+{ "verdana,arial,helvetica,serif",
+ "Gbc" },
+{ "verdana,arial,helvetica,serif",
+ "bs" },
+{ "verdana,arial,helvetica,serif",
+ "Cntr" },
+{ "verdana,arial,helvetica,serif",
+ "Qverpgbel" },
+{ "verdana,arial,helvetica,serif",
+ "Nyy" },
+{ "verdana,arial,helvetica,serif",
+ "Fgberf" },
+{ "verdana,arial,helvetica,serif",
+ " | " },
+{ "verdana,arial,helvetica,serif",
+ "Bhe" },
+{ "verdana,arial,helvetica,serif",
+ "Vagreangvbany" },
+{ "verdana,arial,helvetica,serif",
+ "Fvgrf" },
+{ "verdana,arial,helvetica,serif",
+ "Fubccvat" },
+{ "verdana,arial,helvetica,serif",
+ "Pneg" },
+{ "verdana,arial,helvetica,serif",
+ "Vgrzf" },
+{ "verdana,arial,helvetica,serif",
+ "1-Pyvpx" },
+{ "verdana,arial,helvetica,serif",
+ "Frggvatf" },
+{ "verdana,arial,helvetica,serif",
+ "Fvgr" },
+{ "verdana,arial,helvetica,serif",
+ "Thvqr" },
+{ "verdana,arial,helvetica,serif",
+ "Nobhg" },
+{ "verdana,arial,helvetica,serif",
+ "Wbva" },
+{ "verdana,arial,helvetica,serif",
+ "Fgnss" },
+{ "verdana,arial,helvetica,serif",
+ "Nffbpvngrf" },
+{ "verdana,arial,helvetica,serif",
+ "Nqinagntr" },
+{ "verdana,arial,helvetica,serif",
+ "Pbaqvgvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "bs" },
+{ "verdana,arial,helvetica,serif",
+ "Hfr" },
+{ "verdana,arial,helvetica,serif",
+ " | " },
+{ "verdana,arial,helvetica,serif",
+ "Cevinpl" },
+{ "verdana,arial,helvetica,serif",
+ "Abgvpr" },
+{ "verdana,arial,helvetica,serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "\xc2""\xa9""" },
+{ "verdana,arial,helvetica,serif",
+ "1996-2000," },
+{ "verdana,arial,helvetica,serif",
+ "Nznmba.pbz," },
+{ "verdana,arial,helvetica,serif",
+ "Vap." },
+{ "Lucida Grande",
+ "Nyy Cebqhpgf" },
+{ "serif",
+ "Uryyb." },
+{ "serif",
+ "11," },
+{ "serif",
+ "jjj.nznmba.pbz," },
+{ "serif",
+ "231" },
+{ "serif",
+ "1, 11, jjj.nznmba.pbz, 231" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvg " },
+{ "verdana,arial,helvetica,serif",
+ "Gel gur " },
+{ "verdana,arial,helvetica,serif",
+ "Tvsg Jvmneq" },
+{ "verdana,arial,helvetica,serif",
+ "Ohl n " },
+{ "verdana,arial,helvetica,serif",
+ "tvsg pregvsvpngr" },
+{ "verdana,arial,helvetica,serif",
+ "Ivrj n phfgbzre'f " },
+{ "verdana,arial,helvetica,serif",
+ "Jvfu Yvfg" },
+{ "verdana,arial,helvetica,serif",
+ " be hcqngr " },
+{ "verdana,arial,helvetica,serif",
+ "lbhe bja" },
+{ "verdana,arial,helvetica,serif",
+ "Frr " },
+{ "verdana,arial,helvetica,serif",
+ "beqrevat " },
+{ "verdana,arial,helvetica,serif",
+ "27,336,632" },
+{ "verdana,arial,helvetica,serif",
+ "Nyernql n phfgbzre?" },
+{ "verdana,arial,helvetica,serif",
+ "Fvta va" },
+{ "verdana,arial,helvetica,serif",
+ " gb frr jung'f " },
+{ "verdana,arial,helvetica,serif",
+ "Arj sbe Lbh" },
+{ "verdana,arial,helvetica,serif",
+ "Yvgrengher & Svpgvba" },
+{ "serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Trg gvcf ba frnepuvat naq beqrevat! Frnepu bhe " },
+{ "verdana,arial,helvetica,serif",
+ "Uryc qrcnegzrag" },
+{ "verdana,arial,helvetica,serif",
+ "." },
+{ "verdana,arial,helvetica,serif",
+ "Urnygu & Ornhgl" },
+{ "verdana,arial,helvetica,serif",
+ "Jveryrff Cubarf" },
+{ "verdana,arial,helvetica,serif",
+ "Pnzren & Cubgb" },
+{ "verdana,arial,helvetica,serif",
+ "Pbzchgre &" },
+{ "verdana,arial,helvetica,serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Ivqrb Tnzrf" },
+{ "verdana,arial,helvetica,serif",
+ "Gbbyf &" },
+{ "verdana,arial,helvetica,serif",
+ "Ynja & Cngvb" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Pnef" },
+{ "serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Fvta va gb " },
+{ "verdana,arial,helvetica,serif",
+ "trg erpbzzraqngvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "Ohl n Frtn Qernzpnfg pbafbyr naq trg n " },
+{ "verdana,arial,helvetica,serif",
+ "$50 " },
+{ "verdana,arial,helvetica,serif",
+ " gbjneq nal Frtn ivqrb tnzr fbsgjner be " },
+{ "verdana,arial,helvetica,serif",
+ "npprffbel--guebhtu Qrprzore 18 bayl!" },
+{ "serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Gbc Fryyref va Pbzchgre & Ivqrb Tnzrf" },
+{ "verdana,arial,helvetica,serif",
+ "Gur Fvzf" },
+{ "serif",
+ "Gur vqrn oruvaq " },
+{ "serif",
+ "Gur Fvzf" },
+{ "serif",
+ " vf guvf: gur hygvzngr " },
+{ "serif",
+ "tbny bs yvsr vf gb npuvrir unccvarff, naq gur " },
+{ "serif",
+ "jnl gb npuvrir unccvarff vf gb fngvfsl lbhe " },
+{ "serif",
+ "Fvzf' arrqf. Gung'f evtug, " },
+{ "serif",
+ " nyybjf lbh " },
+{ "serif",
+ "gb perngr, qverpg, naq znantr gur yvirf bs " },
+{ "serif",
+ "'f erfvqragf. Rnpu arrq (Uhatre, " },
+{ "serif",
+ "Pbzsbeg, Ultvrar, Oynqqre,... " },
+{ "serif",
+ "Ernq zber" },
+{ "verdana,arial,helvetica,serif",
+ "Zber Gbc Fryyref:" },
+{ "serif",
+ "Grxab gur Ebobgvp Chccl" },
+{ "serif",
+ " ol Znayrl" },
+{ "serif",
+ "Cnyz VVVkr Unaquryq" },
+{ "serif",
+ " ol CNYZ" },
+{ "serif",
+ "Inph Iva Jvar Fnire Tvsg Cnpx, Juvgr" },
+{ "serif",
+ "Va Urnygu & Ornhgl" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Eryrnfrf va Obbxf" },
+{ "verdana,arial,helvetica,serif",
+ "Gur Orngyrf Nagubybtl" },
+{ "serif",
+ "Nsgre nyy gurfr lrnef, vf gurer nalguvat arj gb " },
+{ "serif",
+ "fnl nobhg gur Orngyrf? Bayl vs lbh tb fgenvtug " },
+{ "serif",
+ "gb gur thlf jub unira'g unq gurve shyy fnl: gur " },
+{ "serif",
+ "Orngyrf gurzfryirf naq gurve pybfrfg pbasvqnagf. " },
+{ "serif",
+ "Gur Orngyrf Nagubybtl" },
+{ "serif",
+ " unf xnyrvqbfpbcr " },
+{ "verdana, arial, geneva,serif",
+ "Gel" },
+{ "verdana, arial, geneva,serif",
+ "NBY" },
+{ "verdana, arial, geneva,serif",
+ "6.0" },
+{ "verdana, arial, geneva,serif",
+ "Jvgu" },
+{ "verdana, arial, geneva,serif",
+ "Bhe" },
+{ "verdana, arial, geneva,serif",
+ "Orfg" },
+{ "verdana, arial, geneva,serif",
+ "Bssre" },
+{ "verdana, arial, geneva,serif",
+ "Lrg" },
+{ "verdana, arial, geneva,serif",
+ "--" },
+{ "verdana, arial, geneva,serif",
+ "Hc" },
+{ "verdana, arial, geneva,serif",
+ "Gb" },
+{ "verdana, arial, geneva,serif",
+ "700" },
+{ "verdana, arial, geneva,serif",
+ "Ubhef" },
+{ "verdana, arial, geneva,serif",
+ "SERR" },
+{ "verdana, arial, geneva,serif",
+ "Va" },
+{ "verdana, arial, geneva,serif",
+ "N" },
+{ "verdana, arial, geneva,serif",
+ "Zbagu!" },
+{ "verdana, arial, geneva,serif",
+ " Fvta" },
+{ "verdana, arial, geneva,serif",
+ "ABJ!" },
+{ "verdana, arial, geneva,serif",
+ "NVZ" },
+{ "verdana, arial, geneva,serif",
+ "Rkcerff" },
+{ "verdana, arial, geneva,serif",
+ "Cbegsbyvb" },
+{ "verdana, arial, geneva,serif",
+ "Zl" },
+{ "verdana, arial, geneva,serif",
+ "Pnyraqne" },
+{ "verdana, arial, geneva,serif",
+ "Ragre" },
+{ "verdana, arial, geneva,serif",
+ "Fperra" },
+{ "verdana, arial, geneva,serif",
+ "Anzr:" },
+{ "verdana, arial, geneva,serif",
+ "Cnffjbeq:" },
+{ "verdana, arial, geneva,serif",
+ "Purpx" },
+{ "verdana, arial, geneva,serif",
+ "lbhe" },
+{ "verdana, arial, geneva,serif",
+ "NBY" },
+{ "verdana, arial, geneva,serif",
+ "Znvy" },
+{ "arial,geneva,serif",
+ "Frnepu" },
+{ "verdana, arial, geneva,serif",
+ "Juvgr" },
+{ "verdana, arial, geneva,serif",
+ "Cntrf" },
+{ "verdana, arial, geneva,serif",
+ "|" },
+{ "verdana, arial, geneva,serif",
+ "Lryybj" },
+{ "verdana, arial, geneva,serif",
+ "Pngrtbevrf" },
+{ "verdana, arial, geneva,serif",
+ "R-znvy" },
+{ "verdana, arial, geneva,serif",
+ "Svaqre" },
+{ "verdana, arial, geneva,serif",
+ "Zncf" },
+{ "verdana, arial, geneva,serif",
+ "Crbcyr" },
+{ "verdana, arial, geneva,serif",
+ "Qverpgbel" },
+{ "verdana, arial, geneva,serif",
+ "Ubzr" },
+{ "verdana, arial, geneva,serif",
+ "Xvqf" },
+{ "verdana, arial, geneva,serif",
+ "Bayl" },
+{ "verdana, arial, geneva,serif",
+ "Genqr" },
+{ "verdana, arial, geneva,serif",
+ "lbhe" },
+{ "verdana, arial, geneva,serif",
+ "Obevat" },
+{ "verdana, arial, geneva,serif",
+ "Perqvg" },
+{ "verdana, arial, geneva,serif",
+ "Pneq" },
+{ "verdana, arial, geneva,serif",
+ "sbe" },
+{ "verdana, arial, geneva,serif",
+ "IVFN" },
+{ "verdana, arial, geneva,serif",
+ "gbqnl!" },
+{ "verdana, arial, geneva,serif",
+ "Cynlfgngvba" },
+{ "verdana, arial, geneva,serif",
+ "2," },
+{ "verdana, arial, geneva,serif",
+ "or" },
+{ "verdana, arial, geneva,serif",
+ "n" },
+{ "verdana, arial, geneva,serif",
+ "jvaare" },
+{ "verdana, arial, geneva,serif",
+ "ng" },
+{ "verdana, arial, geneva,serif",
+ "Oybpxohfgre.pbz." },
+{ "verdana, arial, geneva,serif",
+ "Tebhcf@NBY" },
+{ "verdana, arial, geneva,serif",
+ "Ubg" },
+{ "verdana, arial, geneva,serif",
+ "Pungf" },
+{ "verdana, arial, geneva,serif",
+ "Ybir@NBY" },
+{ "verdana, arial, geneva,serif",
+ "Ohvyq" },
+{ "verdana, arial, geneva,serif",
+ "Serr" },
+{ "verdana, arial, geneva,serif",
+ "Fhaqnl," },
+{ "verdana, arial, geneva,serif",
+ "Bpgbore" },
+{ "verdana, arial, geneva,serif",
+ "29," },
+{ "verdana, arial, geneva,serif",
+ "2000" },
+{ "verdana, arial, geneva,serif",
+ "GBC" },
+{ "verdana, arial, geneva,serif",
+ "ARJF:" },
+{ "geneva,arial,serif",
+ "Pyvagba" },
+{ "geneva,arial,serif",
+ "Pnyyf" },
+{ "geneva,arial,serif",
+ "sbe" },
+{ "geneva,arial,serif",
+ "Ohqtrg" },
+{ "geneva,arial,serif",
+ "Gnyxf" },
+{ "geneva,arial,serif",
+ "Fnyhgrf" },
+{ "geneva,arial,serif",
+ "Vgnyvna-Nzrevpnaf" },
+{ "verdana, arial, geneva,serif",
+ " " },
+{ "verdana, arial, geneva,serif",
+ "Pyvpx" },
+{ "verdana, arial, geneva,serif",
+ "urer" },
+{ "verdana, arial, geneva,serif",
+ "gb" },
+{ "verdana, arial, geneva,serif",
+ "trg" },
+{ "verdana, arial, geneva,serif",
+ "vg" },
+{ "verdana, arial, geneva,serif",
+ "abj!" },
+{ "verdana, arial, geneva,serif",
+ "Vg'f" },
+{ "verdana, arial, geneva,serif",
+ "rnfvre" },
+{ "verdana, arial, geneva,serif",
+ "guna" },
+{ "verdana, arial, geneva,serif",
+ "rire" },
+{ "verdana, arial, geneva,serif",
+ "gb" },
+{ "verdana, arial, geneva,serif",
+ "fgnl" },
+{ "verdana, arial, geneva,serif",
+ "pbaarpgrq" },
+{ "verdana, arial, geneva,serif",
+ "sevraqf" },
+{ "verdana, arial, geneva,serif",
+ "naq" },
+{ "verdana, arial, geneva,serif",
+ "snzvyl" },
+{ "verdana, arial, geneva,serif",
+ "vasbezngvba" },
+{ "verdana, arial, geneva,serif",
+ "ba" },
+{ "verdana, arial, geneva,serif",
+ "NBY." },
+{ "verdana, arial, geneva,serif",
+ "Jnvgvat" },
+{ "verdana, arial, geneva,serif",
+ "na" },
+{ "verdana, arial, geneva,serif",
+ "vzcbegnag" },
+{ "verdana, arial, geneva,serif",
+ "r-znvy?" },
+{ "verdana, arial, geneva,serif",
+ "Hfr" },
+{ "verdana, arial, geneva,serif",
+ "lbhe" },
+{ "verdana, arial, geneva,serif",
+ "pryy" },
+{ "verdana, arial, geneva,serif",
+ "cubar" },
+{ "verdana, arial, geneva,serif",
+ "frr" },
+{ "verdana, arial, geneva,serif",
+ "vs" },
+{ "verdana, arial, geneva,serif",
+ "vg'f" },
+{ "verdana, arial, geneva,serif",
+ "gurer," },
+{ "verdana, arial, geneva,serif",
+ "jvgu" },
+{ "verdana, arial, geneva,serif",
+ "NBY" },
+{ "verdana, arial, geneva,serif",
+ "Zbovyr" },
+{ "verdana, arial, geneva,serif",
+ "." },
+{ "verdana, arial, geneva,serif",
+ "Pryy" },
+{ "verdana, arial, geneva,serif",
+ "CQN/Unaquryq" },
+{ "verdana, arial, geneva,serif",
+ "GI" },
+{ "verdana, arial, geneva,serif",
+ "ol" },
+{ "verdana, arial, geneva,serif",
+ "Cubar" },
+{ "verdana, arial, geneva,serif",
+ "Pbzzhavpngbe" },
+{ "verdana, arial, geneva,serif",
+ "Cubgb" },
+{ "verdana, arial, geneva,serif",
+ "Srngherf" },
+{ "arial, geneva,serif",
+ "-" },
+{ "arial, geneva,serif",
+ "Jbeyq" },
+{ "arial, geneva,serif",
+ "Frevrf" },
+{ "arial, geneva,serif",
+ "Vzntrf" },
+{ "arial, geneva,serif",
+ "Cvpgherf" },
+{ "arial, geneva,serif",
+ "Bs" },
+{ "arial, geneva,serif",
+ "Gur" },
+{ "arial, geneva,serif",
+ "Jrrx" },
+{ "arial, geneva,serif",
+ "Zber" },
+{ "arial, geneva,serif",
+ "Cubgb" },
+{ "arial, geneva,serif",
+ "Tnyyrevrf" },
+{ "verdana,arial,geneva,serif",
+ "Trg Dhbgr" },
+{ "verdana, arial, geneva,serif",
+ "FGBPX" },
+{ "verdana, arial, geneva,serif",
+ "DHBGR:" },
+{ "arial, geneva,serif",
+ " " },
+{ "arial, geneva,serif",
+ "Flzoby" },
+{ "arial, geneva,serif",
+ "Ybbxhc" },
+{ "verdana, arial, geneva,serif",
+ "Fcbegf" },
+{ "verdana, arial, geneva,serif",
+ "Fpberf" },
+{ "verdana, arial, geneva,serif",
+ "Serr" },
+{ "verdana, arial, geneva,serif",
+ "Terrgvatf" },
+{ "verdana, arial, geneva,serif",
+ "Yvfgvatf" },
+{ "verdana, arial, geneva,serif",
+ "Vaivgngvbaf" },
+{ "verdana, arial, geneva,serif",
+ "Ubzrgbja" },
+{ "verdana, arial, geneva,serif",
+ "Zvyrf" },
+{ "verdana, arial, geneva,serif",
+ "ba" },
+{ "verdana, arial, geneva,serif",
+ "Ubebfpbcrf" },
+{ "verdana, arial, geneva,serif",
+ "Jrngure" },
+{ "verdana, arial, geneva,serif",
+ "Zbivr" },
+{ "verdana, arial, geneva,serif",
+ "Gvk" },
+{ "verdana, arial, geneva,serif",
+ "Pynffvsvrqf" },
+{ "verdana, arial, geneva,serif",
+ "Tnzrf" },
+{ "verdana, arial, geneva,serif",
+ "Gel" },
+{ "verdana, arial, geneva,serif",
+ "SERR" },
+{ "verdana, arial, geneva,serif",
+ "Nhgbf" },
+{ "verdana, arial, geneva,serif",
+ "Ohfvarff&" },
+{ "verdana, arial, geneva,serif",
+ "Pnerref" },
+{ "verdana, arial, geneva,serif",
+ "Pbzchgvat" },
+{ "verdana, arial, geneva,serif",
+ "Ragregnvazrag" },
+{ "verdana, arial, geneva,serif",
+ "Sbbq" },
+{ "verdana, arial, geneva,serif",
+ "&" },
+{ "verdana, arial, geneva,serif",
+ "Pbbxvat" },
+{ "verdana, arial, geneva,serif",
+ "Tnzrf" },
+{ "verdana, arial, geneva,serif",
+ "Tbireazrag" },
+{ "verdana, arial, geneva,serif",
+ "Urnygu" },
+{ "verdana, arial, geneva,serif",
+ "Tneqra" },
+{ "verdana, arial, geneva,serif",
+ "Yrtny" },
+{ "verdana, arial, geneva,serif",
+ "Ybpny" },
+{ "verdana, arial, geneva,serif",
+ "Cneragvat" },
+{ "verdana, arial, geneva,serif",
+ "Crefbany" },
+{ "verdana, arial, geneva,serif",
+ "Svanapr" },
+{ "verdana, arial, geneva,serif",
+ "Erny" },
+{ "verdana, arial, geneva,serif",
+ "Rfgngr" },
+{ "verdana, arial, geneva,serif",
+ "Erfrnepu" },
+{ "verdana, arial, geneva,serif",
+ "Yrnea" },
+{ "verdana, arial, geneva,serif",
+ "Fcbegf" },
+{ "verdana, arial, geneva,serif",
+ "Geniry" },
+{ "verdana, arial, geneva,serif",
+ "Jbzra" },
+{ "verdana, arial, geneva,serif",
+ "Wbof" },
+{ "verdana, arial, geneva,serif",
+ "|" },
+{ "verdana, arial, geneva,serif",
+ "Hfrq" },
+{ "verdana, arial, geneva,serif",
+ "Pnef" },
+{ "verdana, arial, geneva,serif",
+ "Fbsgjner" },
+{ "verdana, arial, geneva,serif",
+ "Inpngvbaf" },
+{ "verdana, arial, geneva,serif",
+ "Qbjaybnq" },
+{ "verdana, arial, geneva,serif",
+ "6.0" },
+{ "verdana, arial, geneva,serif",
+ "Cevpvat" },
+{ "verdana, arial, geneva,serif",
+ "Cynaf" },
+{ "verdana, arial, geneva,serif",
+ "Npprff" },
+{ "verdana, arial, geneva,serif",
+ "Ahzoref" },
+{ "verdana, arial, geneva,serif",
+ "Nssvyvngr" },
+{ "verdana, arial, geneva,serif",
+ "Argjbex" },
+{ "verdana, arial, geneva,serif",
+ "Nobhg" },
+{ "verdana, arial, geneva,serif",
+ "Naljurer" },
+{ "verdana, arial, geneva,serif",
+ "NBY@FPUBBY" },
+{ "verdana, arial, geneva,serif",
+ "Srrqonpx" },
+{ "verdana, arial, geneva,serif",
+ "Pnerref@NBY" },
+{ "verdana, arial, geneva,serif",
+ "Yvax" },
+{ "verdana, arial, geneva,serif",
+ "Hf" },
+{ "verdana, arial, geneva,serif",
+ "Nqiregvfr" },
+{ "verdana, arial, geneva,serif",
+ "Jroznfgre" },
+{ "verdana, arial, geneva,serif",
+ "Vasb" },
+{ "verdana, arial, geneva,serif",
+ "Uryc" },
+{ "verdana, arial, geneva,serif",
+ "Fvgr" },
+{ "verdana, arial, geneva,serif",
+ "Vaqrk" },
+{ "geneva, arial,serif",
+ "Pbclevtug" },
+{ "geneva, arial,serif",
+ "\xc2""\xa9""" },
+{ "geneva, arial,serif",
+ "2000" },
+{ "geneva, arial,serif",
+ "Nzrevpn" },
+{ "geneva, arial,serif",
+ "Bayvar," },
+{ "geneva, arial,serif",
+ "Vap." },
+{ "geneva, arial,serif",
+ "Nyy" },
+{ "geneva, arial,serif",
+ "evtugf" },
+{ "geneva, arial,serif",
+ "erfreirq." },
+{ "geneva, arial,serif",
+ "Yrtny" },
+{ "geneva, arial,serif",
+ "Abgvprf" },
+{ "geneva, arial,serif",
+ "Cevinpl" },
+{ "geneva, arial,serif",
+ "Cbyvpl" },
+{ "geneva, arial,serif",
+ "Gel" },
+{ "geneva, arial,serif",
+ "NBY" },
+{ "geneva, arial,serif",
+ "6.0" },
+{ "verdana, arial, geneva,serif",
+ "Nzrevpn" },
+{ "verdana, arial, geneva,serif",
+ "Bayvar" },
+{ "verdana, arial, geneva,serif",
+ "Pbecbengr" },
+{ "verdana, arial, geneva,serif",
+ "Ynhapurf" },
+{ "verdana, arial, geneva,serif",
+ "Npprffvovyvgl" },
+{ "verdana, arial, geneva,serif",
+ "Cbyvpl" },
+{ "verdana, arial, geneva,serif",
+ "Xrrc" },
+{ "verdana, arial, geneva,serif",
+ "Lbhe" },
+{ "verdana, arial, geneva,serif",
+ "Xvqf" },
+{ "verdana, arial, geneva,serif",
+ "Fnsr" },
+{ "verdana, arial, geneva,serif",
+ "va" },
+{ "verdana, arial, geneva,serif",
+ "Plorefcnpr" },
+{ "verdana, arial, geneva,serif",
+ "," },
+{ "verdana, arial, geneva,serif",
+ "TrgArgJvfr" },
+{ "verdana, arial, geneva,serif",
+ "Cevinpl" },
+{ "verdana, arial, geneva,serif",
+ "Cebgrpgvba" },
+{ "verdana, arial, geneva,serif",
+ "naq" },
+{ "verdana, arial, geneva,serif",
+ "NBY'f" },
+{ "verdana, arial, geneva,serif",
+ "Hafbyvpvgrq" },
+{ "verdana, arial, geneva,serif",
+ "Ohyx" },
+{ "verdana, arial, geneva,serif",
+ "R-znvy" },
+{ "verdana, arial, geneva,serif",
+ "Jbeyqjvqr" },
+{ "verdana, arial, geneva,serif",
+ "Freivprf" },
+{ "verdana, arial, geneva,serif",
+ ":" },
+{ "verdana, arial, geneva,serif",
+ "Fcrpvny" },
+{ "verdana, arial, geneva,serif",
+ "irefvbaf" },
+{ "verdana, arial, geneva,serif",
+ "bs" },
+{ "verdana, arial, geneva,serif",
+ "sbe" },
+{ "verdana, arial, geneva,serif",
+ "erfvqragf" },
+{ "verdana, arial, geneva,serif",
+ "Netragvan" },
+{ "verdana, arial, geneva,serif",
+ "Nhfgenyvn" },
+{ "verdana, arial, geneva,serif",
+ "Oenmvy" },
+{ "verdana, arial, geneva,serif",
+ "Pnanqn" },
+{ "verdana, arial, geneva,serif",
+ "Senapr" },
+{ "verdana, arial, geneva,serif",
+ "Treznal" },
+{ "verdana, arial, geneva,serif",
+ "Ubat" },
+{ "verdana, arial, geneva,serif",
+ "Xbat" },
+{ "verdana, arial, geneva,serif",
+ "Wncna" },
+{ "verdana, arial, geneva,serif",
+ "Zrkvpb" },
+{ "verdana, arial, geneva,serif",
+ "Fjrqra" },
+{ "verdana, arial, geneva,serif",
+ "gur" },
+{ "verdana, arial, geneva,serif",
+ "Havgrq" },
+{ "verdana, arial, geneva,serif",
+ "Xvatqbz" },
+{ "arial,serif",
+ "Tb" },
+{ "verdana, arial, geneva,serif",
+ "Purpx lbhe NBY Znvy" },
+{ "verdana, arial, geneva,serif",
+ "Ragre Fperra Anzr:" },
+{ "verdana, arial, geneva,serif",
+ "Ragre Cnffjbeq:" },
+{ "arial,serif",
+ "Tb" },
+{ "verdana, arial, geneva,serif",
+ "NVZ" },
+{ "verdana, arial, geneva,serif",
+ "Rkcerff" },
+{ "verdana, arial, geneva,serif",
+ "NVZ " },
+{ "verdana, arial, geneva,serif",
+ "Pnyraqne" },
+{ "verdana, arial, geneva,serif",
+ "Cbegsbyvb" },
+{ "verdana, arial, geneva,serif",
+ "Zl" },
+{ "verdana, arial, geneva,serif",
+ "NBY" },
+{ "verdana, arial, geneva,serif",
+ "Zl " },
+{ "serif",
+ "NBY" },
+{ "serif",
+ "Vafgnag" },
+{ "serif",
+ "Zrffratre" },
+{ "serif",
+ "NBY Vafgnag " },
+{ "verdana, arial, geneva,serif",
+ " " },
+{ "verdana, arial, geneva,serif",
+ "Pyvpx urer gb trg vg abj!" },
+{ "verdana, arial, geneva,serif",
+ "Fhaqnl, Bpgbore 29, 2000" },
+{ "serif",
+ "Fubegphgf" },
+{ "verdana, arial, geneva,serif",
+ "GBC ARJF:" },
+{ "geneva,arial,serif",
+ "Pyvagba Pnyyf sbe Ohqtrg Gnyxf" },
+{ "geneva,arial,serif",
+ "Pyvagba Fnyhgrf Vgnyvna-Nzrevpnaf" },
+{ "verdana, arial, geneva,serif",
+ "Fcbegf Fpberf" },
+{ "verdana, arial, geneva,serif",
+ "Serr Terrgvatf" },
+{ "verdana, arial, geneva,serif",
+ "GI Yvfgvatf" },
+{ "verdana, arial, geneva,serif",
+ "Zbivr Gvk" },
+{ "verdana, arial, geneva,serif",
+ "Serr Zvyrf ba NBY" },
+{ "verdana, arial, geneva,serif",
+ "Gel NBY SERR" },
+{ "verdana, arial, geneva,serif",
+ "FGBPX DHBGR:" },
+{ "arial, geneva,serif",
+ " " },
+{ "arial, geneva,serif",
+ "Flzoby Ybbxhc" },
+{ "verdana,arial,geneva,serif",
+ "Trg Dhbgr" },
+{ "verdana, arial, geneva,serif",
+ "Gel NBY 6.0 Jvgu Bhe Orfg Bssre Lrg -- Hc Gb 700 " },
+{ "verdana, arial, geneva,serif",
+ "Ubhef SERR Va N Zbagu! Fvta Hc ABJ!" },
+{ "verdana, arial, geneva,serif",
+ "NVZ Rkcerff" },
+{ "verdana, arial, geneva,serif",
+ "Zl Pnyraqne" },
+{ "verdana, arial, geneva,serif",
+ "Zl NBY" },
+{ "serif",
+ "Frnepu" },
+{ "arial,geneva,serif",
+ "Frnepu" },
+{ "serif",
+ "12," },
+{ "serif",
+ "jjj.nby.pbz," },
+{ "serif",
+ "253" },
+{ "serif",
+ "1, 12, jjj.nby.pbz, 253" },
+{ "verdana, arial, geneva,serif",
+ "Cubgb Srngherf" },
+{ "arial, geneva,serif",
+ "- " },
+{ "arial, geneva,serif",
+ "Jbeyq Frevrf Vzntrf" },
+{ "arial, geneva,serif",
+ "Cvpgherf Bs Gur Jrrx" },
+{ "arial, geneva,serif",
+ "Zber Cubgb Tnyyrevrf" },
+{ "verdana, arial, geneva,serif",
+ "Vg'f rnfvre guna rire" },
+{ "verdana, arial, geneva,serif",
+ "gb fgnl pbaarpgrq gb" },
+{ "verdana, arial, geneva,serif",
+ "sevraqf naq snzvyl" },
+{ "verdana, arial, geneva,serif",
+ "naq lbhe vasbezngvba ba NBY." },
+{ "verdana, arial, geneva,serif",
+ "Pryy cubar" },
+{ "verdana, arial, geneva,serif",
+ "NBY GI" },
+{ "verdana, arial, geneva,serif",
+ "NBY ol Cubar" },
+{ "verdana, arial, geneva,serif",
+ "Zbovyr Pbzzhavpngbe" },
+{ "verdana, arial, geneva,serif",
+ "Jnvgvat sbe na vzcbegnag r-znvy?" },
+{ "verdana, arial, geneva,serif",
+ "Hfr lbhe pryy cubar gb frr vs vg'f" },
+{ "verdana, arial, geneva,serif",
+ "gurer, jvgu " },
+{ "verdana, arial, geneva,serif",
+ "NBY Zbovyr" },
+{ "verdana, arial, geneva,serif",
+ "Juvgr Cntrf" },
+{ "verdana, arial, geneva,serif",
+ " |" },
+{ "verdana, arial, geneva,serif",
+ "Lryybj Cntrf" },
+{ "verdana, arial, geneva,serif",
+ " | " },
+{ "verdana, arial, geneva,serif",
+ "R-znvy Svaqre" },
+{ "verdana, arial, geneva,serif",
+ " | " },
+{ "verdana, arial, geneva,serif",
+ "Crbcyr Qverpgbel" },
+{ "verdana, arial, geneva,serif",
+ "Ubzr Cntrf" },
+{ "verdana, arial, geneva,serif",
+ "Xvqf Bayl" },
+{ "verdana, arial, geneva,serif",
+ " " },
+{ "verdana, arial, geneva,serif",
+ "Genqr lbhe Obevat Perqvg Pneq sbe NBY IVFN gbqnl!" },
+{ "verdana, arial, geneva,serif",
+ "Cynlfgngvba 2, or n jvaare ng Oybpxohfgre.pbz." },
+{ "verdana, arial, geneva,serif",
+ "Ubg Pungf" },
+{ "verdana, arial, geneva,serif",
+ "Ohvyq Serr Ubzr Cntrf" },
+{ "Geneva, Helvetica, Arial,serif",
+ "Znepu" },
+{ "Geneva, Helvetica, Arial,serif",
+ "16," },
+{ "Geneva, Helvetica, Arial,serif",
+ "2007" },
+{ "Geneva, Helvetica, Arial,serif",
+ "2:36" },
+{ "Geneva, Helvetica, Arial,serif",
+ "CZ" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Fvgr" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Znc" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ " | " },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Frnepu" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Gvcf" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Bcgvbaf" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Svaq" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Wbo" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Bccbeghavgvrf" },
+{ "Geneva,Helvetica,Arial,serif",
+ "Ivfvg" },
+{ "Geneva,Helvetica,Arial,serif",
+ "bgure" },
+{ "Geneva,Helvetica,Arial,serif",
+ "Nccyr" },
+{ "Geneva,Helvetica,Arial,serif",
+ "fvgrf" },
+{ "Geneva,Helvetica,Arial,serif",
+ "nebhaq" },
+{ "Geneva,Helvetica,Arial,serif",
+ "gur" },
+{ "Geneva,Helvetica,Arial,serif",
+ "jbeyq: " },
+{ "Lucida Grande",
+ "Pubbfr..." },
+{ "Lucida Grande",
+ "Nfvn" },
+{ "Lucida Grande",
+ "Nhfgenyvn" },
+{ "Lucida Grande",
+ "Nhfgevn" },
+{ "Lucida Grande",
+ "Orytvhz" },
+{ "Lucida Grande",
+ "Oenmvy" },
+{ "Lucida Grande",
+ "Ohytnevn" },
+{ "Lucida Grande",
+ "Pnanqn" },
+{ "Lucida Grande",
+ "Pebngvn" },
+{ "Lucida Grande",
+ "Erchoyvp" },
+{ "Lucida Grande",
+ "Qraznex" },
+{ "Lucida Grande",
+ "Rhebcr" },
+{ "Lucida Grande",
+ "Svaynaq" },
+{ "Lucida Grande",
+ "Senapr" },
+{ "Lucida Grande",
+ "Trbetvn" },
+{ "Lucida Grande",
+ "Treznal" },
+{ "Lucida Grande",
+ "Terrpr" },
+{ "Lucida Grande",
+ "Uhatnel" },
+{ "Lucida Grande",
+ "Vprynaq" },
+{ "Lucida Grande",
+ "Verynaq" },
+{ "Lucida Grande",
+ "Vfenry" },
+{ "Lucida Grande",
+ "Vgnyl" },
+{ "Lucida Grande",
+ "Wncna" },
+{ "Lucida Grande",
+ "Xbern" },
+{ "Lucida Grande",
+ "Yngva" },
+{ "Lucida Grande",
+ "Nzrevpn" },
+{ "Lucida Grande",
+ "Znygn" },
+{ "Lucida Grande",
+ "Zrkvpb" },
+{ "Lucida Grande",
+ "Argureynaqf" },
+{ "Lucida Grande",
+ "Abejnl" },
+{ "Lucida Grande",
+ "Cbynaq" },
+{ "Lucida Grande",
+ "Cbeghtny" },
+{ "Lucida Grande",
+ "Ehffvn" },
+{ "Lucida Grande",
+ "Fvatncber" },
+{ "Lucida Grande",
+ "Fybiravn" },
+{ "Lucida Grande",
+ "Fbhgu" },
+{ "Lucida Grande",
+ "Nsevpn" },
+{ "Lucida Grande",
+ "Fcnva" },
+{ "Lucida Grande",
+ "Fjrqra" },
+{ "Lucida Grande",
+ "Fjvgmreynaq" },
+{ "Lucida Grande",
+ "Gnvjna" },
+{ "Lucida Grande",
+ "Ghexrl" },
+{ "Lucida Grande",
+ "HX" },
+{ "Lucida Grande",
+ "Hxenvar" },
+{ "Lucida Grande",
+ "Havgrq" },
+{ "Lucida Grande",
+ "Neno" },
+{ "Lucida Grande",
+ "Rzvengrf" },
+{ "Lucida Grande",
+ "Fgngrf" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Pbagnpg" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Hf" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Cevinpl" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Abgvpr" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Pbclevtug" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "\xc2""\xa9""" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "2000" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Nccyr" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Pbzchgre," },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Vap." },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "Nyy" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "evtugf" },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "erfreirq." },
+{ "Geneva,Arial,Helvetica,Swiss,SunSans-Regular,serif",
+ "1-800-ZL-NCCYR" },
+{ "serif",
+ "Cbjrerq" },
+{ "serif",
+ "ZnpBFKFreire" },
+{ "Geneva, Helvetica, Arial,serif",
+ "Znepu 16, 2007 2:36 CZ" },
+{ "serif",
+ "13," },
+{ "serif",
+ "jjj.nccyr.pbz," },
+{ "serif",
+ "477" },
+{ "serif",
+ "1, 13, jjj.nccyr.pbz, 477" },
+{ "verdana,ARIAL,sans-serif,serif",
+ "Pyvpx" },
+{ "verdana,ARIAL,sans-serif,serif",
+ "Urer" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAA.pbz" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAAFV.pbz" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAAsa.pbz" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Gur" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Jro" },
+{ "Lucida Grande",
+ "Svaq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Frnepu" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAA" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Fvgrf" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Rhebcr" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAAsa" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAAFV" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "zlPAA" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Rqhpngvba" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "NyyCbyvgvpf" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Ynathntrf" },
+{ "helvetica,arial,verdana,serif",
+ "ZNVACNTR" },
+{ "helvetica,arial,verdana,serif",
+ "R-ZNVY:" },
+{ "helvetica,arial,verdana,serif",
+ "Fhofpevor" },
+{ "helvetica,arial,verdana,serif",
+ "gb" },
+{ "helvetica,arial,verdana,serif",
+ "bar" },
+{ "helvetica,arial,verdana,serif",
+ "bs" },
+{ "helvetica,arial,verdana,serif",
+ "bhe" },
+{ "helvetica,arial,verdana,serif",
+ "arjf" },
+{ "helvetica,arial,verdana,serif",
+ "r-znvy" },
+{ "helvetica,arial,verdana,serif",
+ "yvfgf" },
+{ "helvetica,arial,verdana,serif",
+ "." },
+{ "helvetica,arial,verdana,serif",
+ "Ragre" },
+{ "helvetica,arial,verdana,serif",
+ "lbhe" },
+{ "helvetica,arial,verdana,serif",
+ "nqqerff:" },
+{ "Lucida Grande",
+ "tb" },
+{ "helvetica,arial,verdana,serif",
+ "QVFPHFFVBA:" },
+{ "helvetica,arial,verdana,serif",
+ "zrffntr" },
+{ "helvetica,arial,verdana,serif",
+ "obneqf" },
+{ "helvetica,arial,verdana,serif",
+ "pung" },
+{ "helvetica,arial,verdana,serif",
+ "srrqonpx" },
+{ "helvetica,arial,verdana,serif",
+ "ivqrb" },
+{ "helvetica,arial,verdana,serif",
+ "nhqvb" },
+{ "helvetica,arial,verdana,serif",
+ "zhygvzrqvn" },
+{ "helvetica,arial,verdana,serif",
+ "fubjpnfr" },
+{ "helvetica,arial,verdana,serif",
+ "dhvm" },
+{ "helvetica,arial,verdana,serif",
+ "zber" },
+{ "helvetica,arial,verdana,serif",
+ "freivprf" },
+{ "helvetica,arial,verdana,serif",
+ "ZHYGVZRQVN:" },
+{ "helvetica,arial,verdana,serif",
+ "PAA.pbz" },
+{ "helvetica,arial,verdana,serif",
+ "Rhebcr" },
+{ "helvetica,arial,verdana,serif",
+ "punatr" },
+{ "helvetica,arial,verdana,serif",
+ "qrsnhyg" },
+{ "helvetica,arial,verdana,serif",
+ "rqvgvba" },
+{ "helvetica,arial,verdana,serif",
+ "RQVGVBAF:" },
+{ "helvetica,arial,verdana,serif",
+ "JBEYQ" },
+{ "helvetica,arial,verdana,serif",
+ "H.F." },
+{ "helvetica,arial,verdana,serif",
+ "JRNGURE" },
+{ "helvetica,arial,verdana,serif",
+ "OHFVARFF" },
+{ "helvetica,arial,verdana,serif",
+ "FCBEGF" },
+{ "helvetica,arial,verdana,serif",
+ "GRPUABYBTL" },
+{ "helvetica,arial,verdana,serif",
+ "FCNPR" },
+{ "helvetica,arial,verdana,serif",
+ "URNYGU" },
+{ "helvetica,arial,verdana,serif",
+ "RAGREGNVAZRAG" },
+{ "helvetica,arial,verdana,serif",
+ "CBYVGVPF" },
+{ "helvetica,arial,verdana,serif",
+ "YNJ" },
+{ "helvetica,arial,verdana,serif",
+ "PNERRE" },
+{ "helvetica,arial,verdana,serif",
+ "GENIRY" },
+{ "helvetica,arial,verdana,serif",
+ "SBBQ" },
+{ "helvetica,arial,verdana,serif",
+ "NEGF" },
+{ "helvetica,arial,verdana,serif",
+ "&" },
+{ "helvetica,arial,verdana,serif",
+ "FGLYR" },
+{ "helvetica,arial,verdana,serif",
+ "OBBXF" },
+{ "helvetica,arial,verdana,serif",
+ "ANGHER" },
+{ "helvetica,arial,verdana,serif",
+ "VA-QRCGU" },
+{ "helvetica,arial,verdana,serif",
+ "NANYLFVF" },
+{ "helvetica,arial,verdana,serif",
+ "YBPNY" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag-ryrpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnyyf" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbe" },
+{ "arial,helvetica,sans-serif,serif",
+ "'fcvevg" },
+{ "arial,helvetica,sans-serif,serif",
+ "bs" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbbcrengvba'" },
+{ "arial,helvetica,sans-serif,serif",
+ "Trbetr" },
+{ "arial,helvetica,sans-serif,serif",
+ "J." },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu" },
+{ "arial,helvetica,sans-serif,serif",
+ "nqqerffrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "angvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbe" },
+{ "arial,helvetica,sans-serif,serif",
+ "svefg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gvzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "gbavtug" },
+{ "arial,helvetica,sans-serif,serif",
+ "nf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cerfvqrag-ryrpg," },
+{ "arial,helvetica,sans-serif,serif",
+ "cyrqtvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "gb" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbex" },
+{ "arial,helvetica,sans-serif,serif",
+ "jvgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrzbpengvp" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnaqvqngr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tber" },
+{ "arial,helvetica,sans-serif,serif",
+ "\"urny" },
+{ "arial,helvetica,sans-serif,serif",
+ "bhe" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbhagel\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "naq" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnyyvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "n" },
+{ "arial,helvetica,sans-serif,serif",
+ "\"fcvevg" },
+{ "arial,helvetica,sans-serif,serif",
+ "bs" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbbcrengvba\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "va" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jnfuvatgba." },
+{ "arial,helvetica,sans-serif,serif",
+ "nyfb" },
+{ "arial,helvetica,sans-serif,serif",
+ "ernpurq" },
+{ "arial,helvetica,sans-serif,serif",
+ "bhg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gubfr" },
+{ "arial,helvetica,sans-serif,serif",
+ "jub" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvqa'g" },
+{ "arial,helvetica,sans-serif,serif",
+ "fhccbeg" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvz," },
+{ "arial,helvetica,sans-serif,serif",
+ "fnlvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "\"gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "cerfvqrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Havgrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fgngrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "vf" },
+{ "arial,helvetica,sans-serif,serif",
+ "rirel" },
+{ "arial,helvetica,sans-serif,serif",
+ "fvatyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nzrevpna," },
+{ "arial,helvetica,sans-serif,serif",
+ "enpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "onpxtebhaq.\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "erznexf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "nobhg" },
+{ "arial,helvetica,sans-serif,serif",
+ "na" },
+{ "arial,helvetica,sans-serif,serif",
+ "ubhe" },
+{ "arial,helvetica,sans-serif,serif",
+ "nsgre" },
+{ "arial,helvetica,sans-serif,serif",
+ "raqrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "svir-jrrx" },
+{ "arial,helvetica,sans-serif,serif",
+ "cbyvgvpny" },
+{ "arial,helvetica,sans-serif,serif",
+ "yrtny" },
+{ "arial,helvetica,sans-serif,serif",
+ "onggyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "cerfvqrapl" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnyyrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "ba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nzrevpnaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "havgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "oruvaq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu." },
+{ "arial,helvetica,sans-serif,serif",
+ "\"V" },
+{ "arial,helvetica,sans-serif,serif",
+ "fnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag-ryrpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gung" },
+{ "arial,helvetica,sans-serif,serif",
+ "jung" },
+{ "arial,helvetica,sans-serif,serif",
+ "erznvaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cnegvfna" },
+{ "arial,helvetica,sans-serif,serif",
+ "enapbe" },
+{ "arial,helvetica,sans-serif,serif",
+ "zhfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "abj" },
+{ "arial,helvetica,sans-serif,serif",
+ "or" },
+{ "arial,helvetica,sans-serif,serif",
+ "chg" },
+{ "arial,helvetica,sans-serif,serif",
+ "nfvqr," },
+{ "arial,helvetica,sans-serif,serif",
+ "znl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tbq" },
+{ "arial,helvetica,sans-serif,serif",
+ "oyrff" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgrjneqfuvc" },
+{ "arial,helvetica,sans-serif,serif",
+ "guvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbhagel.\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "QRIRYBCVAT" },
+{ "arial,helvetica,sans-serif,serif",
+ "FGBEL" },
+{ "arial,helvetica,sans-serif,serif",
+ "Trbetr" },
+{ "arial,helvetica,sans-serif,serif",
+ "J." },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgngrzrag:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Genafpevcg" },
+{ "arial,helvetica,sans-serif,serif",
+ "|" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivqrb" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tber" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ernq" },
+{ "arial,helvetica,sans-serif,serif",
+ "gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhcerzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbheg" },
+{ "arial,helvetica,sans-serif,serif",
+ "bcvavba" },
+{ "arial,helvetica,sans-serif,serif",
+ "(SvaqYnj)" },
+{ "arial,helvetica,sans-serif,serif",
+ " \xe2""\x80""\xa2"" " },
+{ "arial,helvetica,sans-serif,serif",
+ "CQS" },
+{ "arial,helvetica,sans-serif,serif",
+ "*" },
+{ "arial,helvetica,sans-serif,serif",
+ "UGZY" },
+{ "arial,helvetica,sans-serif,serif",
+ "Rkprecgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "sebz" },
+{ "arial,helvetica,sans-serif,serif",
+ "bcvavba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zber" },
+{ "arial,helvetica,sans-serif,serif",
+ "ryrpgvba-eryngrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "qbphzragf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnfrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "erdhver" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nqbor\xc2""\xae""" },
+{ "arial,helvetica,sans-serif,serif",
+ "Npebong\xc2""\xae""" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ernqre" },
+{ "arial,helvetica,sans-serif,serif",
+ "\xe2""\x84""\xa2""" },
+{ "arial,helvetica,sans-serif,serif",
+ "Va-Qrcgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcrpvny:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erivrjvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "ibgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yvfgra" },
+{ "arial,helvetica,sans-serif,serif",
+ "abj:" },
+{ "arial,helvetica,sans-serif,serif",
+ "PAA" },
+{ "arial,helvetica,sans-serif,serif",
+ "ryrpgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbirentr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erny" },
+{ "arial,helvetica,sans-serif,serif",
+ "be" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvaqbjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zrqvn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yngrfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "arjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "naq" },
+{ "arial,helvetica,sans-serif,serif",
+ "nanylfvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ubj" },
+{ "arial,helvetica,sans-serif,serif",
+ "jr" },
+{ "arial,helvetica,sans-serif,serif",
+ "tbg" },
+{ "arial,helvetica,sans-serif,serif",
+ "urer:" },
+{ "arial,helvetica,sans-serif,serif",
+ "N" },
+{ "arial,helvetica,sans-serif,serif",
+ "gvzryvar" },
+{ "arial,helvetica,sans-serif,serif",
+ "Sybevqn" },
+{ "arial,helvetica,sans-serif,serif",
+ "erpbhag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbaterffvbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrzbpengf" },
+{ "arial,helvetica,sans-serif,serif",
+ "gurl'yy" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbyvgvpny" },
+{ "arial,helvetica,sans-serif,serif",
+ "tevqybpx" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrrgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jnyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fgerrg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pung" },
+{ "arial,helvetica,sans-serif,serif",
+ "Genafpevcg:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Wbr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ybpxuneg" },
+{ "arial,helvetica,sans-serif,serif",
+ "rinyhngrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tber'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbaprffvba" },
+{ "arial,helvetica,sans-serif,serif",
+ " \xe2""\x80""\xa2"" Tnyyrel:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqragvny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Genafvgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "hetrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbbcrengvba," },
+{ "arial,helvetica,sans-serif,serif",
+ "pbafrafhf" },
+{ "arial,helvetica,sans-serif,serif",
+ "genafvgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "pna" },
+{ "arial,helvetica,sans-serif,serif",
+ "fuvsg" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvtu" },
+{ "arial,helvetica,sans-serif,serif",
+ "trne" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "jvaare" },
+{ "arial,helvetica,sans-serif,serif",
+ "qrpvqrq," },
+{ "arial,helvetica,sans-serif,serif",
+ "unccraf" },
+{ "arial,helvetica,sans-serif,serif",
+ "abj?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbeyq" },
+{ "arial,helvetica,sans-serif,serif",
+ "ernpgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbyy:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Znwbevgl" },
+{ "arial,helvetica,sans-serif,serif",
+ "npprcg" },
+{ "arial,helvetica,sans-serif,serif",
+ "yrtvgvzngr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vagrenpgvir:" },
+{ "arial,helvetica,sans-serif,serif",
+ "snpgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tnyyrel:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ryrpgbeny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ibgrf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "271," },
+{ "arial,helvetica,sans-serif,serif",
+ "267" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbzcyrgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "erfhygf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nanylfvf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivpgbel" },
+{ "arial,helvetica,sans-serif,serif",
+ "fraqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "gerzbef" },
+{ "arial,helvetica,sans-serif,serif",
+ "guebhtu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Rhebcr" },
+{ "arial,helvetica,sans-serif,serif",
+ "erfgberf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qlanfgl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jnfuvatgba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fpubynef" },
+{ "arial,helvetica,sans-serif,serif",
+ "nethr" },
+{ "arial,helvetica,sans-serif,serif",
+ "ehyvat;" },
+{ "arial,helvetica,sans-serif,serif",
+ "nterr" },
+{ "arial,helvetica,sans-serif,serif",
+ "vzcnpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yrtny" },
+{ "arial,helvetica,sans-serif,serif",
+ "vffhrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "yvatre" },
+{ "arial,helvetica,sans-serif,serif",
+ "fubjqbja" },
+{ "arial,helvetica,sans-serif,serif",
+ "H.F." },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhcerzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbheg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "whfgvprf" },
+{ "arial,helvetica,sans-serif,serif",
+ "trg" },
+{ "arial,helvetica,sans-serif,serif",
+ "nybat" },
+{ "arial,helvetica,sans-serif,serif",
+ "i." },
+{ "arial,helvetica,sans-serif,serif",
+ "nsgrezngu?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yngrfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "ivqrb" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zrffntr" },
+{ "arial,helvetica,sans-serif,serif",
+ "obneq:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "lbhe" },
+{ "arial,helvetica,verdana,serif",
+ "BA" },
+{ "arial,helvetica,verdana,serif",
+ "GUR" },
+{ "arial,helvetica,verdana,serif",
+ "FPRAR" },
+{ "arial,helvetica,verdana,serif",
+ "Puneyrf" },
+{ "arial,helvetica,verdana,serif",
+ "Ovreonhre" },
+{ "arial,helvetica,verdana,serif",
+ "ba" },
+{ "arial,helvetica,verdana,serif",
+ "gur" },
+{ "arial,helvetica,verdana,serif",
+ "vzcnpg" },
+{ "arial,helvetica,verdana,serif",
+ "bs" },
+{ "arial,helvetica,verdana,serif",
+ "Ohfu" },
+{ "arial,helvetica,verdana,serif",
+ "i." },
+{ "arial,helvetica,verdana,serif",
+ "Tber" },
+{ "arial,helvetica,verdana,serif",
+ "Ghpxre" },
+{ "arial,helvetica,verdana,serif",
+ "Pneyfba:" },
+{ "arial,helvetica,verdana,serif",
+ "Raq" },
+{ "arial,helvetica,verdana,serif",
+ "Pyvagba" },
+{ "arial,helvetica,verdana,serif",
+ "ren" },
+{ "arial,helvetica,verdana,serif",
+ "vf" },
+{ "arial,helvetica,verdana,serif",
+ "avtu" },
+{ "arial,helvetica,verdana,serif",
+ "Ovyy" },
+{ "arial,helvetica,verdana,serif",
+ "Fpuarvqre" },
+{ "arial,helvetica,verdana,serif",
+ "ryrpgvba" },
+{ "arial,helvetica,verdana,serif",
+ "nsgrezngu" },
+{ "arial,helvetica,verdana,serif",
+ "Wbanguna" },
+{ "arial,helvetica,verdana,serif",
+ "Xney:" },
+{ "arial,helvetica,verdana,serif",
+ "Obgu" },
+{ "arial,helvetica,verdana,serif",
+ "Ohfu" },
+{ "arial,helvetica,verdana,serif",
+ "naq" },
+{ "arial,helvetica,verdana,serif",
+ "Tber" },
+{ "arial,helvetica,verdana,serif",
+ "snpr" },
+{ "arial,helvetica,verdana,serif",
+ "arj" },
+{ "arial,helvetica,verdana,serif",
+ "punyyratrf" },
+{ "arial,helvetica,verdana,serif",
+ "abj" },
+{ "arial,helvetica,verdana,serif",
+ "gung" },
+{ "arial,helvetica,verdana,serif",
+ "bire" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbaivpgrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "fcl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbcr" },
+{ "arial,helvetica,sans-serif,serif",
+ "eryrnfrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvagre" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgbez" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvfehcgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "syvtugf," },
+{ "arial,helvetica,sans-serif,serif",
+ "cnenylmrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "geniryref" },
+{ "arial,helvetica,sans-serif,serif",
+ "Srqreny" },
+{ "arial,helvetica,sans-serif,serif",
+ "cnary" },
+{ "arial,helvetica,sans-serif,serif",
+ "fnlf" },
+{ "arial,helvetica,sans-serif,serif",
+ "rkpyhqvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbagenprcgvir" },
+{ "arial,helvetica,sans-serif,serif",
+ "vyyrtny" },
+{ "arial,helvetica,sans-serif,serif",
+ "fubjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nynfxn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nveyvarf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cvybgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgehttyrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "fnir" },
+{ "arial,helvetica,sans-serif,serif",
+ "cynar" },
+{ "arial,helvetica,sans-serif,serif",
+ "RH" },
+{ "arial,helvetica,sans-serif,serif",
+ "fcrnx" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nensng," },
+{ "arial,helvetica,sans-serif,serif",
+ "Onenx" },
+{ "arial,helvetica,sans-serif,serif",
+ "Va" },
+{ "arial,helvetica,sans-serif,serif",
+ "Bgure" },
+{ "arial,helvetica,sans-serif,serif",
+ "Arjf:" },
+{ "serif",
+ " " },
+{ "arial,helvetica,sans-serif,serif",
+ "Pna" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbssrr" },
+{ "arial,helvetica,sans-serif,serif",
+ "cebgrpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "fzbxref" },
+{ "arial,helvetica,sans-serif,serif",
+ "ntnvafg" },
+{ "arial,helvetica,sans-serif,serif",
+ "oynqqre" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnapre?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fghql:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ergnvy" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jro" },
+{ "arial,helvetica,sans-serif,serif",
+ "fvgrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "snyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "fubeg" },
+{ "arial,helvetica,sans-serif,serif",
+ "phfgbzre" },
+{ "arial,helvetica,sans-serif,serif",
+ "freivpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yvfgra" },
+{ "arial,helvetica,sans-serif,serif",
+ "fbhaq" },
+{ "arial,helvetica,sans-serif,serif",
+ "snyyvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrgrbef" },
+{ "arial,helvetica,sans-serif,serif",
+ "Srngherf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrprzore" },
+{ "arial,helvetica,sans-serif,serif",
+ "14," },
+{ "arial,helvetica,sans-serif,serif",
+ "2000" },
+{ "arial,helvetica,sans-serif,serif",
+ "--" },
+{ "arial,helvetica,sans-serif,serif",
+ "Hcqngrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "03:32" },
+{ "arial,helvetica,sans-serif,serif",
+ "n.z." },
+{ "arial,helvetica,sans-serif,serif",
+ "RFG," },
+{ "arial,helvetica,sans-serif,serif",
+ "0832" },
+{ "arial,helvetica,sans-serif,serif",
+ "TZG" },
+{ "arial,helvetica,verdana,serif",
+ "Rqvgvbaf" },
+{ "arial,helvetica,verdana,serif",
+ " | " },
+{ "arial,helvetica,verdana,serif",
+ "zlPAA" },
+{ "arial,helvetica,verdana,serif",
+ "Ivqrb" },
+{ "arial,helvetica,verdana,serif",
+ "Nhqvb" },
+{ "arial,helvetica,verdana,serif",
+ "Urnqyvar" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Argjbexf" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Vagreangvbany" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Urnqyvar" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Arjf" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Enqvb" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Nvecbeg" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "Argjbex" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Oevrs" },
+{ "arial,helvetica,verdana,serif",
+ "Serr" },
+{ "arial,helvetica,verdana,serif",
+ "R-znvy" },
+{ "arial,helvetica,verdana,serif",
+ "Srrqonpx" },
+{ "arial,helvetica,verdana,serif",
+ "Arjf" },
+{ "arial,helvetica,verdana,serif",
+ "Oevrs" },
+{ "Lucida Grande",
+ "PAA" },
+{ "Lucida Grande",
+ "Fvgrf" },
+{ "Lucida Grande",
+ "PAAsa" },
+{ "Lucida Grande",
+ "PAAFV" },
+{ "Lucida Grande",
+ "zlPAA" },
+{ "Lucida Grande",
+ "PAAslv" },
+{ "Lucida Grande",
+ "NyyCbyvgvpf" },
+{ "Lucida Grande",
+ "Ynathntrf" },
+{ "arial,helvetica,sans-serif,serif",
+ " Frnepu" },
+{ "Lucida Grande",
+ "PAA.pbz" },
+{ "Lucida Grande",
+ "PAAFV.pbz" },
+{ "Lucida Grande",
+ "PAAsa.pbz" },
+{ "Lucida Grande",
+ "Gur" },
+{ "Lucida Grande",
+ "Jro" },
+{ "arial,helvetica,sans-serif,serif",
+ " Gbc" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Arjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ng " },
+{ "arial,helvetica,sans-serif,serif",
+ "Gbc" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfvarff" },
+{ "arial,helvetica,sans-serif,serif",
+ "ng: " },
+{ "arial,helvetica,sans-serif,serif",
+ "lvryqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gevcyr-qvtvg" },
+{ "arial,helvetica,sans-serif,serif",
+ "ybff" },
+{ "arial,helvetica,sans-serif,serif",
+ "Anfqnd" },
+{ "arial,helvetica,sans-serif,serif",
+ "Benpyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "rneavatf" },
+{ "arial,helvetica,sans-serif,serif",
+ "frra" },
+{ "arial,helvetica,sans-serif,serif",
+ "evfvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "Znexrg" },
+{ "arial,helvetica,sans-serif,serif",
+ "arjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "nanylfvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "QWVN" },
+{ "arial,helvetica,sans-serif,serif",
+ "ANF" },
+{ "arial,helvetica,sans-serif,serif",
+ "F&C" },
+{ "arial,helvetica,sans-serif,serif",
+ "26.20" },
+{ "arial,helvetica,sans-serif,serif",
+ "109.00" },
+{ "arial,helvetica,sans-serif,serif",
+ "11.19" },
+{ "arial,helvetica,sans-serif,serif",
+ "4:31cz" },
+{ "arial,helvetica,sans-serif,serif",
+ "RG," },
+{ "arial,helvetica,sans-serif,serif",
+ "12/13" },
+{ "arial,helvetica,sans-serif,serif",
+ " ZNEXRGF" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jrngure:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivfvg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "PAA.pbz" },
+{ "arial,helvetica,sans-serif,serif",
+ "jrngure" },
+{ "arial,helvetica,sans-serif,serif",
+ "frpgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "," },
+{ "arial,helvetica,sans-serif,serif",
+ "be" },
+{ "arial,helvetica,sans-serif,serif",
+ "ragre" },
+{ "arial,helvetica,sans-serif,serif",
+ "lbhe" },
+{ "arial,helvetica,sans-serif,serif",
+ "H.F." },
+{ "arial,helvetica,sans-serif,serif",
+ "Mvc" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbqr:" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "tb" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pyvpx" },
+{ "arial,helvetica,sans-serif,serif",
+ "urer" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbe" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgngrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbeyq" },
+{ "arial,helvetica,sans-serif,serif",
+ "pvgvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "AON:" },
+{ "arial,helvetica,sans-serif,serif",
+ "92" },
+{ "arial,helvetica,sans-serif,serif",
+ "69" },
+{ "arial,helvetica,sans-serif,serif",
+ "Svany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ngynagn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Unjxf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrgebvg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cvfgbaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Trg" },
+{ "arial,helvetica,sans-serif,serif",
+ "nyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "fpberf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oynmref" },
+{ "arial,helvetica,sans-serif,serif",
+ "xrrc" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ynxref" },
+{ "arial,helvetica,sans-serif,serif",
+ "erryvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "96-86" },
+{ "arial,helvetica,sans-serif,serif",
+ "ivpgbel" },
+{ "arial,helvetica,sans-serif,serif",
+ "Evpr," },
+{ "arial,helvetica,sans-serif,serif",
+ "Avaref" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvibepr" },
+{ "arial,helvetica,sans-serif,serif",
+ "frrzf" },
+{ "arial,helvetica,sans-serif,serif",
+ "yvxryl" },
+{ "arial,helvetica,sans-serif,serif",
+ "frnfba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhaf'" },
+{ "arial,helvetica,sans-serif,serif",
+ "Uneqnjnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "snprf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qbzrfgvp" },
+{ "arial,helvetica,sans-serif,serif",
+ "ivbyrapr" },
+{ "arial,helvetica,sans-serif,serif",
+ "punetr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Enzverm" },
+{ "arial,helvetica,sans-serif,serif",
+ "bssvpvnyyl" },
+{ "arial,helvetica,sans-serif,serif",
+ "fvtaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qrny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fbk" },
+{ "helvetica,arial,verdana,serif",
+ "PAA" },
+{ "helvetica,arial,verdana,serif",
+ "JRO" },
+{ "helvetica,arial,verdana,serif",
+ "FVGRF:" },
+{ "helvetica,arial,verdana,serif",
+ "PAAslv.pbz" },
+{ "helvetica,arial,verdana,serif",
+ "NfvnAbj" },
+{ "helvetica,arial,verdana,serif",
+ "Fcnavfu" },
+{ "helvetica,arial,verdana,serif",
+ "Cbeghthrfr" },
+{ "helvetica,arial,verdana,serif",
+ "Trezna" },
+{ "helvetica,arial,verdana,serif",
+ "Vgnyvna" },
+{ "helvetica,arial,verdana,serif",
+ "Fjrqvfu" },
+{ "helvetica,arial,verdana,serif",
+ "Abejrtvna" },
+{ "helvetica,arial,verdana,serif",
+ "Qnavfu" },
+{ "helvetica,arial,verdana,serif",
+ "Wncnarfr" },
+{ "helvetica,arial,verdana,serif",
+ "Puvarfr" },
+{ "helvetica,arial,verdana,serif",
+ "Urnqyvarf" },
+{ "helvetica,arial,verdana,serif",
+ "GVZR" },
+{ "helvetica,arial,verdana,serif",
+ "VAP." },
+{ "Lucida Grande",
+ "Tb" },
+{ "Lucida Grande",
+ "Gb" },
+{ "Lucida Grande",
+ "..." },
+{ "Lucida Grande",
+ "Gvzr.pbz" },
+{ "Lucida Grande",
+ "Crbcyr" },
+{ "Lucida Grande",
+ "Zbarl" },
+{ "Lucida Grande",
+ "Sbeghar" },
+{ "Lucida Grande",
+ "RJ" },
+{ "helvetica,arial,verdana,serif",
+ "ARGJBEXF:" },
+{ "helvetica,arial,verdana,serif",
+ "PAA" },
+{ "helvetica,arial,verdana,serif",
+ "napubef" },
+{ "helvetica,arial,verdana,serif",
+ "genafpevcgf" },
+{ "helvetica,arial,verdana,serif",
+ "Gheare" },
+{ "helvetica,arial,verdana,serif",
+ "qvfgevohgvba" },
+{ "helvetica,arial,verdana,serif",
+ "FVGR" },
+{ "helvetica,arial,verdana,serif",
+ "VASB:" },
+{ "helvetica,arial,verdana,serif",
+ "uryc" },
+{ "helvetica,arial,verdana,serif",
+ "pbagragf" },
+{ "helvetica,arial,verdana,serif",
+ "frnepu" },
+{ "helvetica,arial,verdana,serif",
+ "nq" },
+{ "helvetica,arial,verdana,serif",
+ "vasb" },
+{ "helvetica,arial,verdana,serif",
+ "wbof" },
+{ "helvetica,arial,verdana,serif",
+ "FREIVPRF:" },
+{ "helvetica,arial,verdana,serif",
+ "r-fgber" },
+{ "arial,helvetica,sans-serif,serif",
+ " DHVPX" },
+{ "arial,helvetica,sans-serif,serif",
+ "IBGR" },
+{ "arial,helvetica,sans-serif,serif",
+ " OVTTRFG" },
+{ "arial,helvetica,sans-serif,serif",
+ "GERR" },
+{ "arial,helvetica,sans-serif,serif",
+ " 3-Q" },
+{ "arial,helvetica,sans-serif,serif",
+ "CUBGBF" },
+{ "arial,helvetica,sans-serif,serif",
+ "CVPGHER" },
+{ "arial,helvetica,sans-serif,serif",
+ "BS" },
+{ "arial,helvetica,sans-serif,serif",
+ "GUR" },
+{ "arial,helvetica,sans-serif,serif",
+ "QNL" },
+{ "arial,helvetica,sans-serif,serif",
+ " PAAslv.pbz:" },
+{ "arial,helvetica,sans-serif,serif",
+ "SBE" },
+{ "arial,helvetica,sans-serif,serif",
+ "FGHQRAGF" },
+{ "arial,helvetica,sans-serif,serif",
+ "GRNPUREF" },
+{ "arial,helvetica,sans-serif,serif",
+ "YVAX" },
+{ "arial,helvetica,sans-serif,serif",
+ " OBBXF" },
+{ "arial,helvetica,sans-serif,serif",
+ " VAGREIVRJ" },
+{ "arial,helvetica,sans-serif,serif",
+ "PERJ" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nzrevpna" },
+{ "arial,helvetica,sans-serif,serif",
+ "snzvyvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "jub" },
+{ "arial,helvetica,sans-serif,serif",
+ "unir" },
+{ "arial,helvetica,sans-serif,serif",
+ "nqbcgrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "puvyqera" },
+{ "arial,helvetica,sans-serif,serif",
+ "sebz" },
+{ "arial,helvetica,sans-serif,serif",
+ "Puvan" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbex" },
+{ "arial,helvetica,sans-serif,serif",
+ "gb" },
+{ "arial,helvetica,sans-serif,serif",
+ "znvagnva" },
+{ "arial,helvetica,sans-serif,serif",
+ "gurve" },
+{ "arial,helvetica,sans-serif,serif",
+ "puvyqera'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "gvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "jvgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Puvarfr" },
+{ "arial,helvetica,sans-serif,serif",
+ "phygher" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ibvprf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jung" },
+{ "arial,helvetica,sans-serif,serif",
+ "guvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "arj" },
+{ "arial,helvetica,sans-serif,serif",
+ "fubhyq" },
+{ "arial,helvetica,sans-serif,serif",
+ "qb?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcva" },
+{ "arial,helvetica,sans-serif,serif",
+ "ivrjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ryrpgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "2000" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ner" },
+{ "arial,helvetica,sans-serif,serif",
+ "lbh" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Jurry" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gvzr'" },
+{ "arial,helvetica,sans-serif,serif",
+ "nsvpvbanqb?" },
+{ "arial,helvetica,sans-serif,serif",
+ "nccrny?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Funer" },
+{ "arial,helvetica,sans-serif,serif",
+ "frnfbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "tvsg" },
+{ "arial,helvetica,sans-serif,serif",
+ "pryroengvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "vqrnf" },
+{ "serif",
+ " " },
+{ "arial,helvetica,sans-serif,serif",
+ "QVFPHFFVBA" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ovqqvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "ebpxrgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Uvyynel" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pyvagba" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrzbve" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erivrj:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Znhcva'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Avtug" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yvfgrare'" },
+{ "arial,helvetica,sans-serif,serif",
+ "Arj" },
+{ "arial,helvetica,sans-serif,serif",
+ "zhfvpny" },
+{ "arial,helvetica,sans-serif,serif",
+ "irefvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oebagr" },
+{ "arial,helvetica,sans-serif,serif",
+ "gnyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oebnqjnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Erny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yvsr" },
+{ "arial,helvetica,sans-serif,serif",
+ "ng" },
+{ "arial,helvetica,sans-serif,serif",
+ "Juvgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ubhfr'" },
+{ "arial,helvetica,sans-serif,serif",
+ "tbrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "phegnvaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "OBBXF" },
+{ "arial,helvetica,sans-serif,serif",
+ "Raqnatrerq" },
+{ "arial,helvetica,sans-serif,serif",
+ "ornhgvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tvatreoernq" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbbxvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "erpnyyrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "cbgcbheev" },
+{ "arial,helvetica,sans-serif,serif",
+ "hahfhny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ubyvqnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "tvsgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "snibevgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "purs" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ahgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ahg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erpvcrf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Bprna" },
+{ "arial,helvetica,sans-serif,serif",
+ "Crepu" },
+{ "arial,helvetica,sans-serif,serif",
+ "&" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cvar" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erpvcr" },
+{ "arial,helvetica,sans-serif,serif",
+ "," },
+{ "arial,helvetica,sans-serif,serif",
+ "Nccyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oynpx" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pheenag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Percrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "SBBQ" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fpvragvfgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbzcyrgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "trargvp" },
+{ "arial,helvetica,sans-serif,serif",
+ "znc" },
+{ "arial,helvetica,sans-serif,serif",
+ "cynag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nf" },
+{ "arial,helvetica,sans-serif,serif",
+ "sberfgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "snyy," },
+{ "arial,helvetica,sans-serif,serif",
+ "zna" },
+{ "arial,helvetica,sans-serif,serif",
+ "ornfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrrg" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbasyvpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Znynlfvn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbafreingvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "funqbjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qrirybczrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Phon" },
+{ "arial,helvetica,sans-serif,serif",
+ "H.A." },
+{ "arial,helvetica,sans-serif,serif",
+ "raivebazrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "obql" },
+{ "arial,helvetica,sans-serif,serif",
+ "rlrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnivne" },
+{ "arial,helvetica,sans-serif,serif",
+ "rkcbeg" },
+{ "arial,helvetica,sans-serif,serif",
+ "dhbgnf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ANGHER" },
+{ "arial,helvetica,sans-serif,serif",
+ "N" },
+{ "arial,helvetica,sans-serif,serif",
+ "cubgbtencul" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jro" },
+{ "arial,helvetica,sans-serif,serif",
+ "fvgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "abj" },
+{ "arial,helvetica,sans-serif,serif",
+ "nyybjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "crbcyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "oyraq" },
+{ "arial,helvetica,sans-serif,serif",
+ "3-Q" },
+{ "arial,helvetica,sans-serif,serif",
+ "punenpgref" },
+{ "arial,helvetica,sans-serif,serif",
+ "cvpgherf." },
+{ "arial,helvetica,sans-serif,serif",
+ "H.X." },
+{ "arial,helvetica,sans-serif,serif",
+ "nagvivehf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbzcnal" },
+{ "arial,helvetica,sans-serif,serif",
+ "enaxf" },
+{ "arial,helvetica,sans-serif,serif",
+ "gbc" },
+{ "arial,helvetica,sans-serif,serif",
+ "10" },
+{ "arial,helvetica,sans-serif,serif",
+ "ivehfrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Unpxre" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgrnyf" },
+{ "arial,helvetica,sans-serif,serif",
+ "uhtr" },
+{ "arial,helvetica,sans-serif,serif",
+ "perqvg" },
+{ "arial,helvetica,sans-serif,serif",
+ "pneq" },
+{ "arial,helvetica,sans-serif,serif",
+ "qngnonfr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zvpebfbsg" },
+{ "arial,helvetica,sans-serif,serif",
+ "rayvfgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ryrpgebavp" },
+{ "arial,helvetica,sans-serif,serif",
+ "Negf" },
+{ "arial,helvetica,sans-serif,serif",
+ "znxr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Kobk" },
+{ "arial,helvetica,sans-serif,serif",
+ "tnzrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Snfgre" },
+{ "arial,helvetica,sans-serif,serif",
+ "fhesvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "fxvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Va-Qrcgu:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Uvtu-grpu" },
+{ "arial,helvetica,sans-serif,serif",
+ "thvqr" },
+{ "arial,helvetica,sans-serif,serif",
+ "PBZCHGVAT" },
+{ "arial,helvetica,sans-serif,serif",
+ "Svanapvny" },
+{ "arial,helvetica,sans-serif,serif",
+ "cynaavat" },
+{ "arial,helvetica,sans-serif,serif",
+ "gvcf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cerfvqragvny" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jrvtuvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "nqinagntrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "401(x)" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ebgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "VEN" },
+{ "arial,helvetica,sans-serif,serif",
+ "ibvpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "natryf?" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgnl-ng-ubzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "fnivatf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cyna" },
+{ "arial,helvetica,sans-serif,serif",
+ "nfcvevat" },
+{ "arial,helvetica,sans-serif,serif",
+ "zbgure" },
+{ "arial,helvetica,sans-serif,serif",
+ "jevgre" },
+{ "arial,helvetica,sans-serif,serif",
+ "CREFBANY" },
+{ "arial,helvetica,sans-serif,serif",
+ "SVANAPR" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jbeyq'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "ynetrfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gerr" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvfpbiref" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbhagnva" },
+{ "arial,helvetica,sans-serif,serif",
+ "bs" },
+{ "arial,helvetica,sans-serif,serif",
+ "lbhgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "A.L." },
+{ "arial,helvetica,sans-serif,serif",
+ "svyz" },
+{ "arial,helvetica,sans-serif,serif",
+ "pevgvpf" },
+{ "arial,helvetica,sans-serif,serif",
+ "anzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Genssvp'" },
+{ "arial,helvetica,sans-serif,serif",
+ "lrne'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "orfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ornpu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oblf," },
+{ "arial,helvetica,sans-serif,serif",
+ "Oraargg," },
+{ "arial,helvetica,sans-serif,serif",
+ "Zneyrl" },
+{ "arial,helvetica,sans-serif,serif",
+ "erprvir" },
+{ "arial,helvetica,sans-serif,serif",
+ "yvsrgvzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "npuvrirzrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tenzzlf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fubjohmm:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pneerl," },
+{ "arial,helvetica,sans-serif,serif",
+ "Mryyjrtre" },
+{ "arial,helvetica,sans-serif,serif",
+ "pnyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "vg" },
+{ "arial,helvetica,sans-serif,serif",
+ "dhvgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zry" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tvofba" },
+{ "arial,helvetica,sans-serif,serif",
+ "cbaqref" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Jung" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jbzra" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jnag'" },
+{ "arial,helvetica,sans-serif,serif",
+ "RAGREGNVAZRAG" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbaprqrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "enpr;" },
+{ "arial,helvetica,sans-serif,serif",
+ "orpbzrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cerfvqrag-ryrpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Grpuabybtl" },
+{ "arial,helvetica,sans-serif,serif",
+ "cynlf" },
+{ "arial,helvetica,sans-serif,serif",
+ "prageny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ebyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "cbfg-Ryrpgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "CBYVGVPF" },
+{ "arial,helvetica,sans-serif,serif",
+ "erfgberq" },
+{ "arial,helvetica,sans-serif,serif",
+ "zl" },
+{ "arial,helvetica,sans-serif,serif",
+ "snvgu" },
+{ "arial,helvetica,sans-serif,serif",
+ "va" },
+{ "arial,helvetica,sans-serif,serif",
+ "gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "H.F." },
+{ "arial,helvetica,sans-serif,serif",
+ "tbireazrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvzvavfurq" },
+{ "arial,helvetica,sans-serif,serif",
+ "unq" },
+{ "arial,helvetica,sans-serif,serif",
+ "ab" },
+{ "arial,helvetica,sans-serif,serif",
+ "rssrpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "ba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivrj" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erfhygf" },
+{ "Lucida Grande",
+ "ibgr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gur" },
+{ "arial,helvetica,sans-serif,serif",
+ "bhgpbzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "guvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ryrpgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ ":" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ehffvna" },
+{ "arial,helvetica,sans-serif,serif",
+ "Chgva" },
+{ "arial,helvetica,sans-serif,serif",
+ "erarjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "gvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "urnqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Eblny" },
+{ "arial,helvetica,sans-serif,serif",
+ "qngr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gjb" },
+{ "arial,helvetica,sans-serif,serif",
+ "obngf" },
+{ "arial,helvetica,sans-serif,serif",
+ "160" },
+{ "arial,helvetica,sans-serif,serif",
+ "zvffvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "bss" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nhfgenyvn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Mvzonojr'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zhtnor" },
+{ "arial,helvetica,sans-serif,serif",
+ "grfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vfenry" },
+{ "arial,helvetica,sans-serif,serif",
+ "2001" },
+{ "arial,helvetica,sans-serif,serif",
+ "Frnepu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tybony" },
+{ "arial,helvetica,sans-serif,serif",
+ "Whfgvpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "JBEYQ" },
+{ "arial,helvetica,sans-serif,serif",
+ "Punyyratr" },
+{ "arial,helvetica,sans-serif,serif",
+ "fghqragf" },
+{ "arial,helvetica,sans-serif,serif",
+ "nanylmr" },
+{ "arial,helvetica,sans-serif,serif",
+ "punenpgre" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fybobqna" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zvybfrivp" },
+{ "arial,helvetica,sans-serif,serif",
+ "," },
+{ "arial,helvetica,sans-serif,serif",
+ "fnlf" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvf" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbafpvrapr" },
+{ "arial,helvetica,sans-serif,serif",
+ "vf" },
+{ "arial,helvetica,sans-serif,serif",
+ "\"pbzcyrgryl" },
+{ "arial,helvetica,sans-serif,serif",
+ "pyrne\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ovbtencul:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu," },
+{ "arial,helvetica,sans-serif,serif",
+ "Pnzcnvta" },
+{ "arial,helvetica,sans-serif,serif",
+ "Svanaprf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvfgbel" },
+{ "arial,helvetica,sans-serif,serif",
+ "fpubby" },
+{ "arial,helvetica,sans-serif,serif",
+ "ibhpuref" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbzcrgvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "'ovyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "evtugf'" },
+{ "arial,helvetica,sans-serif,serif",
+ "cebcbfnyf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ybttreurnqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "QRZBPENPL" },
+{ "arial,helvetica,sans-serif,serif",
+ "VA" },
+{ "arial,helvetica,sans-serif,serif",
+ "NZREVPN" },
+{ "arial,helvetica,sans-serif,serif",
+ "'Ryrpgebphgrq'" },
+{ "arial,helvetica,sans-serif,serif",
+ "evfrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qrnq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvaqbj-fubccref" },
+{ "arial,helvetica,sans-serif,serif",
+ "orjner!" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fgber" },
+{ "arial,helvetica,sans-serif,serif",
+ "bjare" },
+{ "arial,helvetica,sans-serif,serif",
+ "trgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "gbhtu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gebhoyrfbzr" },
+{ "arial,helvetica,sans-serif,serif",
+ "punqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ninvynoyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgreyvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "fvyire" },
+{ "arial,helvetica,sans-serif,serif",
+ "Angvba'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "pncfhyr:" },
+{ "arial,helvetica,sans-serif,serif",
+ "qbt" },
+{ "arial,helvetica,sans-serif,serif",
+ "gntf," },
+{ "arial,helvetica,sans-serif,serif",
+ "pryy" },
+{ "arial,helvetica,sans-serif,serif",
+ "cubar" },
+{ "arial,helvetica,sans-serif,serif",
+ "qernzf" },
+{ "arial,helvetica,sans-serif,serif",
+ "SEVATR" },
+{ "arial,helvetica,sans-serif,serif",
+ "Baobneq" },
+{ "arial,helvetica,sans-serif,serif",
+ "vagrearg" },
+{ "arial,helvetica,sans-serif,serif",
+ "pbaarpgvbaf!" },
+{ "arial,helvetica,sans-serif,serif",
+ "Snfgre" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhesvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "uvgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "fxvrf." },
+{ "arial,helvetica,sans-serif,serif",
+ "Orarggba" },
+{ "arial,helvetica,sans-serif,serif",
+ "bcraf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gbxlb" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrtnfgber" },
+{ "arial,helvetica,sans-serif,serif",
+ "Erzoenaqg" },
+{ "arial,helvetica,sans-serif,serif",
+ "fryyf" },
+{ "arial,helvetica,sans-serif,serif",
+ "erpbeq" },
+{ "arial,helvetica,sans-serif,serif",
+ "cevpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cynaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "nsbbg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ybf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Natryrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "zber" },
+{ "arial,helvetica,sans-serif,serif",
+ "crqrfgevna-sevraqyl" },
+{ "arial,helvetica,sans-serif,serif",
+ "RYYR:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhcrezbqry" },
+{ "arial,helvetica,sans-serif,serif",
+ "qbaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "$15" },
+{ "arial,helvetica,sans-serif,serif",
+ "zvyyvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "oen" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgber" },
+{ "arial,helvetica,sans-serif,serif",
+ "bcravat" },
+{ "arial,helvetica,sans-serif,serif",
+ "NEGF" },
+{ "arial,helvetica,sans-serif,serif",
+ "&" },
+{ "arial,helvetica,sans-serif,serif",
+ "FGLYR" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zrpunavpny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ceboyrz" },
+{ "arial,helvetica,sans-serif,serif",
+ "qrynlf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Abegujrfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "cnffratref" },
+{ "arial,helvetica,sans-serif,serif",
+ "avar" },
+{ "arial,helvetica,sans-serif,serif",
+ "ubhef" },
+{ "arial,helvetica,sans-serif,serif",
+ "syvtug" },
+{ "arial,helvetica,sans-serif,serif",
+ "qviregrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Napubentr" },
+{ "arial,helvetica,sans-serif,serif",
+ "gheohyrapr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Oevgvfu" },
+{ "arial,helvetica,sans-serif,serif",
+ "ehajnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "erbcraf" },
+{ "arial,helvetica,sans-serif,serif",
+ "wrg" },
+{ "arial,helvetica,sans-serif,serif",
+ "serrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "zhq" },
+{ "arial,helvetica,sans-serif,serif",
+ "GENIRY" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yrffbaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbe" },
+{ "arial,helvetica,sans-serif,serif",
+ "yvivat" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ubj" },
+{ "arial,helvetica,sans-serif,serif",
+ "uryc" },
+{ "arial,helvetica,sans-serif,serif",
+ "n" },
+{ "arial,helvetica,sans-serif,serif",
+ "sevraq" },
+{ "arial,helvetica,sans-serif,serif",
+ "jura" },
+{ "arial,helvetica,sans-serif,serif",
+ "ybirq" },
+{ "arial,helvetica,sans-serif,serif",
+ "bar" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ZvePbec" },
+{ "arial,helvetica,sans-serif,serif",
+ "cynaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ohvyq" },
+{ "arial,helvetica,sans-serif,serif",
+ "fcnprfuvc" },
+{ "arial,helvetica,sans-serif,serif",
+ "gbhevfgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Whcvgre'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcbg" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgnerf" },
+{ "arial,helvetica,sans-serif,serif",
+ "qbja" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vb" },
+{ "arial,helvetica,sans-serif,serif",
+ "sylol" },
+{ "arial,helvetica,sans-serif,serif",
+ "cvpf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Puvan" },
+{ "arial,helvetica,sans-serif,serif",
+ "znaarq" },
+{ "arial,helvetica,sans-serif,serif",
+ "fcnpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "5" },
+{ "arial,helvetica,sans-serif,serif",
+ "lrnef" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vagreangvbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcnpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fgngvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "FCNPR" },
+{ "arial,helvetica,sans-serif,serif",
+ "Geraqf:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zra" },
+{ "arial,helvetica,sans-serif,serif",
+ "guna" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbzra:" },
+{ "arial,helvetica,sans-serif,serif",
+ "fgehttyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "rdhny-cnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "jbexcynpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "wbo:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ubfcvgny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ovbzrqvpny" },
+{ "arial,helvetica,sans-serif,serif",
+ "ratvarrevat" },
+{ "arial,helvetica,sans-serif,serif",
+ "qverpgbe:" },
+{ "arial,helvetica,sans-serif,serif",
+ "\xe2""\x80""\x98""Gurl" },
+{ "arial,helvetica,sans-serif,serif",
+ "jnag" },
+{ "arial,helvetica,sans-serif,serif",
+ "vg" },
+{ "arial,helvetica,sans-serif,serif",
+ "abj\xe2""\x80""\x99""" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ernqvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "hc:" },
+{ "arial,helvetica,sans-serif,serif",
+ "\xe2""\x80""\x99""Theh" },
+{ "arial,helvetica,sans-serif,serif",
+ "Thvqr" },
+{ "arial,helvetica,sans-serif,serif",
+ "gb" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ragercerarhefuvc\xe2""\x80""\x99""" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pnerre" },
+{ "arial,helvetica,sans-serif,serif",
+ "pragre:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Wbo" },
+{ "arial,helvetica,sans-serif,serif",
+ "vagreivrj" },
+{ "arial,helvetica,sans-serif,serif",
+ "zvfgnxrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "PNERRE" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tbg" },
+{ "arial,helvetica,sans-serif,serif",
+ "dhrfgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "perj" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vagreangvbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fcnpr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fgngvba?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fraq" },
+{ "arial,helvetica,sans-serif,serif",
+ "vg" },
+{ "arial,helvetica,sans-serif,serif",
+ "gb:" },
+{ "arial,helvetica,sans-serif,serif",
+ "zvyrf.boevra@paa.pbz" },
+{ "arial,helvetica,sans-serif,serif",
+ "." },
+{ "arial,helvetica,sans-serif,serif",
+ "Fryrpgrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "dhrfgvbaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "jvyy" },
+{ "arial,helvetica,sans-serif,serif",
+ "or" },
+{ "arial,helvetica,sans-serif,serif",
+ "hfrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "qhevat" },
+{ "arial,helvetica,sans-serif,serif",
+ "yvir" },
+{ "arial,helvetica,sans-serif,serif",
+ "vagreivrj" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jropnfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Guhefqnl," },
+{ "arial,helvetica,sans-serif,serif",
+ "7" },
+{ "arial,helvetica,sans-serif,serif",
+ "n.z." },
+{ "arial,helvetica,sans-serif,serif",
+ "RG." },
+{ "arial,helvetica,sans-serif,serif",
+ "Nagv-GO" },
+{ "arial,helvetica,sans-serif,serif",
+ "rssbegf" },
+{ "arial,helvetica,sans-serif,serif",
+ "sbphf" },
+{ "arial,helvetica,sans-serif,serif",
+ "vzzvtenagf," },
+{ "arial,helvetica,sans-serif,serif",
+ "fghql" },
+{ "arial,helvetica,sans-serif,serif",
+ "fhttrfgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Znal" },
+{ "arial,helvetica,sans-serif,serif",
+ "'haurnygul'" },
+{ "arial,helvetica,sans-serif,serif",
+ "tveyf" },
+{ "arial,helvetica,sans-serif,serif",
+ "URNYGU" },
+{ "arial,helvetica,sans-serif,serif",
+ "qvfnterr" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrevgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ehyvat," },
+{ "arial,helvetica,sans-serif,serif",
+ "ohg" },
+{ "arial,helvetica,sans-serif,serif",
+ "vgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ehyrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "znahny" },
+{ "arial,helvetica,sans-serif,serif",
+ "erpbhagf" },
+{ "arial,helvetica,sans-serif,serif",
+ "hapbafgvghgvbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Whqtr" },
+{ "arial,helvetica,sans-serif,serif",
+ "nssvezf" },
+{ "arial,helvetica,sans-serif,serif",
+ "nssvezngvir" },
+{ "arial,helvetica,sans-serif,serif",
+ "npgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zvpuvtna" },
+{ "arial,helvetica,sans-serif,serif",
+ "nqzvffvbaf" },
+{ "arial,helvetica,sans-serif,serif",
+ "cbyvpl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tergn@Ynj:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jngpu" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jro-rkpyhfvir" },
+{ "arial,helvetica,sans-serif,serif",
+ "vagreivrjf" },
+{ "arial,helvetica,sans-serif,serif",
+ "ubfgrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "ol" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tergn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ina" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fhfgrera" },
+{ "arial,helvetica,sans-serif,serif",
+ "YNJ" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbire" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbafgvghgvbany" },
+{ "arial,helvetica,sans-serif,serif",
+ "Avtugzner" },
+{ "arial,helvetica,sans-serif,serif",
+ "Orsber" },
+{ "arial,helvetica,sans-serif,serif",
+ "Puevfgznf?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zntnmvar" },
+{ "arial,helvetica,sans-serif,serif",
+ "GVZR" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vairfgvtngvba:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Svryqf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Sver" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jro" },
+{ "arial,helvetica,sans-serif,serif",
+ "Srngher" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qvtvgny'f" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ubyvqnl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Tvsg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Vqrnf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Orfg" },
+{ "arial,helvetica,sans-serif,serif",
+ "2000" },
+{ "arial,helvetica,sans-serif,serif",
+ "Zber" },
+{ "arial,helvetica,sans-serif,serif",
+ "ng" },
+{ "arial,helvetica,sans-serif,serif",
+ "GVZR.pbz" },
+{ "arial,helvetica,sans-serif,serif",
+ "HFF" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbyr" },
+{ "arial,helvetica,sans-serif,serif",
+ "neevirf" },
+{ "arial,helvetica,sans-serif,serif",
+ "onpx" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nve" },
+{ "arial,helvetica,sans-serif,serif",
+ "Sbepr" },
+{ "arial,helvetica,sans-serif,serif",
+ "S-16" },
+{ "arial,helvetica,sans-serif,serif",
+ "svtugre" },
+{ "arial,helvetica,sans-serif,serif",
+ "cnaunaqyr," },
+{ "arial,helvetica,sans-serif,serif",
+ "cvybg" },
+{ "arial,helvetica,sans-serif,serif",
+ "erfphrq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jra" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ub" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yrr" },
+{ "arial,helvetica,sans-serif,serif",
+ "tbireazrag" },
+{ "arial,helvetica,sans-serif,serif",
+ "qroevrsvat" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrzbpenpl" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nzrevpn" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fraq" },
+{ "arial,helvetica,sans-serif,serif",
+ "hf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jbeyq" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jne" },
+{ "arial,helvetica,sans-serif,serif",
+ "VV" },
+{ "arial,helvetica,sans-serif,serif",
+ "zrzbevrf" },
+{ "arial,helvetica,sans-serif,serif",
+ "H.F." },
+{ "arial,helvetica,sans-serif,serif",
+ "Onpx" },
+{ "arial,helvetica,sans-serif,serif",
+ "gb" },
+{ "arial,helvetica,sans-serif,serif",
+ "Gbc" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "\xc2""\xa9""" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "2000" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Pnoyr" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Argjbex." },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Nyy" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Evtugf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Erfreirq." },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Grezf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "haqre" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "juvpu" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "guvf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "freivpr" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "vf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "cebivqrq" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "gb" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "lbh." },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "Ernq" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "bhe" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "cevinpl" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "thvqryvarf" },
+{ "Verdana, HELVETICA, ARIAL, sans-serif,serif",
+ "." },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAA Argjbexf" },
+{ "verdana,ARIAL,sans-serif,serif",
+ "Pyvpx Urer" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAA.pbz" },
+{ "Lucida Grande",
+ "Svaq" },
+{ "verdana,courier new,courier,sans-serif,serif",
+ "PAA Fvgrf" },
+{ "arial,helvetica,verdana,serif",
+ " | " },
+{ "arial,helvetica,verdana,serif",
+ "Urnqyvar Arjf Oevrs" },
+{ "arial,helvetica,verdana,serif",
+ "Serr R-znvy" },
+{ "arial,helvetica,sans-serif,serif",
+ "Qrprzore 14, 2000" },
+{ "arial,helvetica,sans-serif,serif",
+ " -- Hcqngrq 03:32 n.z. RFG, 0832 TZG" },
+{ "helvetica,arial,verdana,serif",
+ "NEGF & FGLYR" },
+{ "helvetica,arial,verdana,serif",
+ "PAA.pbz Rhebcr" },
+{ "helvetica,arial,verdana,serif",
+ "punatr qrsnhyg rqvgvba" },
+{ "helvetica,arial,verdana,serif",
+ "zhygvzrqvn fubjpnfr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag-ryrpg Ohfu pnyyf sbe " },
+{ "arial,helvetica,sans-serif,serif",
+ "'fcvevg bs pbbcrengvba'" },
+{ "arial,helvetica,sans-serif,serif",
+ "Trbetr J. Ohfu nqqerffrq gur angvba sbe gur " },
+{ "arial,helvetica,sans-serif,serif",
+ "svefg gvzr gbavtug nf cerfvqrag-ryrpg, cyrqtvat gb " },
+{ "arial,helvetica,sans-serif,serif",
+ "jbex jvgu Qrzbpengvp pnaqvqngr Ny Tber gb " },
+{ "arial,helvetica,sans-serif,serif",
+ "\"urny bhe pbhagel\" naq pnyyvat sbe n \"fcvevg bs " },
+{ "arial,helvetica,sans-serif,serif",
+ "pbbcrengvba\" va Jnfuvatgba. Ohfu nyfb " },
+{ "arial,helvetica,sans-serif,serif",
+ "ernpurq bhg gb gubfr jub qvqa'g fhccbeg uvz, " },
+{ "arial,helvetica,sans-serif,serif",
+ "fnlvat \"gur cerfvqrag bs gur Havgrq Fgngrf vf " },
+{ "arial,helvetica,sans-serif,serif",
+ "gur cerfvqrag bs rirel fvatyr Nzrevpna, bs " },
+{ "arial,helvetica,sans-serif,serif",
+ "rirel enpr naq rirel onpxtebhaq.\"" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu'f erznexf pnzr nobhg na ubhe nsgre Tber " },
+{ "arial,helvetica,sans-serif,serif",
+ "raqrq uvf svir-jrrx cbyvgvpny naq yrtny onggyr sbe " },
+{ "arial,helvetica,sans-serif,serif",
+ "gur cerfvqrapl naq pnyyrq ba Nzrevpnaf gb " },
+{ "arial,helvetica,sans-serif,serif",
+ "havgr oruvaq Ohfu. \"V fnl gb Cerfvqrag-ryrpg " },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu gung jung erznvaf bs cnegvfna enapbe zhfg " },
+{ "arial,helvetica,sans-serif,serif",
+ "abj or chg nfvqr, naq znl Tbq oyrff uvf " },
+{ "arial,helvetica,sans-serif,serif",
+ "fgrjneqfuvc bs guvf pbhagel.\"" },
+{ "arial,helvetica,sans-serif,serif",
+ " " },
+{ "arial,helvetica,sans-serif,serif",
+ "Trbetr J. Ohfu fgngrzrag:" },
+{ "arial,helvetica,sans-serif,serif",
+ " " },
+{ "arial,helvetica,sans-serif,serif",
+ " | " },
+{ "arial,helvetica,sans-serif,serif",
+ " " },
+{ "arial,helvetica,sans-serif,serif",
+ "Ny Tber fgngrzrag:" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag-ryrpg Ohfu hetrf pbbcrengvba, pbafrafhf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ohfu genafvgvba pna fuvsg gb uvtu trne" },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvgu jvaare qrpvqrq, jung unccraf abj?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqrag Ohfu: Gur jbeyq ernpgf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbyy: Znwbevgl bs Nzrevpnaf npprcg Ohfu nf yrtvgvzngr " },
+{ "arial,helvetica,sans-serif,serif",
+ " Vagrenpgvir: " },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqragvny genafvgvba snpgf" },
+{ "arial,helvetica,sans-serif,serif",
+ " Tnyyrel: " },
+{ "arial,helvetica,sans-serif,serif",
+ "Cerfvqragvny Genafvgvba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ryrpgbeny ibgrf: Ohfu 271, Tber 267" },
+{ "arial,helvetica,sans-serif,serif",
+ "Pbzcyrgr ryrpgvba erfhygf" },
+{ "arial,helvetica,sans-serif,serif",
+ "Nanylfvf: Ivpgbel fraqf gerzbef guebhtu Rhebcr" },
+{ "arial,helvetica,sans-serif,serif",
+ "Ivpgbel erfgberf Ohfu qlanfgl gb Jnfuvatgba" },
+{ "arial,helvetica,sans-serif,serif",
+ "Fpubynef nethr nobhg ehyvat; nterr ba vzcnpg" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yrtny vffhrf yvatre nsgre fubjqbja va H.F. Fhcerzr " },
+{ "arial,helvetica,sans-serif,serif",
+ "Jvyy H.F. Fhcerzr Pbheg whfgvprf trg nybat va Ohfu i. " },
+{ "arial,helvetica,sans-serif,serif",
+ "Tber nsgrezngu?" },
+{ "arial,helvetica,sans-serif,serif",
+ "Yngrfg ryrpgvba ivqrb" },
+{ "arial,helvetica,sans-serif,serif",
+ " Zrffntr obneq: " },
+{ "arial,helvetica,sans-serif,serif",
+ "Cbfg lbhe bcvavba" },
+{ "arial,helvetica,verdana,serif",
+ "BA GUR FPRAR" },
+{ "arial,helvetica,verdana,serif",
+ "Wbanguna Xney: Obgu Ohfu naq Tber snpr " },
+{ "arial,helvetica,verdana,serif",
+ "arj punyyratrf abj gung ryrpgvba vf bire" },
+{ "arial,helvetica,verdana,serif",
+ "Ovyy Fpuarvqre ba ryrpgvba nsgrezngu" },
+{ "serif",
+ "14," },
+{ "serif",
+ "jjj.paa.pbz," },
+{ "serif",
+ "538" },
+{ "serif",
+ "1, 14, jjj.paa.pbz, 538" },
+{ "Arial,serif",
+ "Qrprzore" },
+{ "Arial,serif",
+ "14," },
+{ "Arial,serif",
+ "2000" },
+{ "Arial,serif",
+ "Npprff" },
+{ "Arial,serif",
+ "Ahzoref" },
+{ "serif",
+ " " },
+{ "serif",
+ "|" },
+{ "Arial,serif",
+ "Fubccvat" },
+{ "Arial,serif",
+ "Sbehzf" },
+{ "Arial,serif",
+ "Pung" },
+{ "Arial,serif",
+ "Vafgnag" },
+{ "Arial,serif",
+ "Zrffntr" },
+{ "Arial,serif",
+ "PbzchFreir" },
+{ "Arial,serif",
+ "5.0" },
+{ "Arial,serif",
+ "Serr!" },
+{ "Arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "verdana,serif",
+ "Nhgb" },
+{ "verdana,serif",
+ "Ohfvarff" },
+{ "verdana,serif",
+ "&" },
+{ "verdana,serif",
+ "Pnerref" },
+{ "verdana,serif",
+ "Pryroevgvrf" },
+{ "verdana,serif",
+ "Pbzchgvat" },
+{ "verdana,serif",
+ "Ragregnvazrag" },
+{ "verdana,serif",
+ "Urnygu" },
+{ "verdana,serif",
+ "Svgarff" },
+{ "verdana,serif",
+ "Ubyvqnl" },
+{ "verdana,serif",
+ "Prageny" },
+{ "verdana,serif",
+ "Vairfgvat" },
+{ "verdana,serif",
+ "Ybir" },
+{ "verdana,serif",
+ "Arjf/Ryrpgvba" },
+{ "verdana,serif",
+ "2000" },
+{ "verdana,serif",
+ "Fcbegf" },
+{ "verdana,serif",
+ "Grraf" },
+{ "verdana,serif",
+ "Geniry" },
+{ "verdana,serif",
+ "Jbzra" },
+{ "verdana,serif",
+ "Zber" },
+{ "verdana,serif",
+ "Punaaryf" },
+{ "arial,helvetica,serif",
+ "Pyvpx" },
+{ "arial,helvetica,serif",
+ "urer" },
+{ "arial,helvetica,serif",
+ "gb" },
+{ "arial,helvetica,serif",
+ "ragre" },
+{ "arial,helvetica,serif",
+ "n" },
+{ "arial,helvetica,serif",
+ "Jro" },
+{ "arial,helvetica,serif",
+ "fvgr" },
+{ "arial,helvetica,serif",
+ "nqqerff." },
+{ "Arial,serif",
+ "Ebhaq" },
+{ "Arial,serif",
+ "Bhg" },
+{ "Arial,serif",
+ "Lbhe" },
+{ "Arial,serif",
+ "Yvfg:" },
+{ "Arial,serif",
+ "Fubc@" },
+{ "Arial,serif",
+ "Onearf" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "Aboyr" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "Fnir" },
+{ "Arial,serif",
+ "$$" },
+{ "Arial,serif",
+ "ng" },
+{ "Arial,serif",
+ "tnc.pbz" },
+{ "serif",
+ "\xe2""\x80""\xa2""" },
+{ "Arial,serif",
+ "Tb" },
+{ "Arial,serif",
+ "Rys" },
+{ "Arial,serif",
+ "Objyvat" },
+{ "Arial,serif",
+ "Jvgu" },
+{ "Arial,serif",
+ "Fnagn." },
+{ "Arial,serif",
+ "Vg'f" },
+{ "Arial,serif",
+ "n" },
+{ "Arial,serif",
+ "Oynfg!" },
+{ "Arial,serif",
+ "Puevfgznf" },
+{ "Arial,serif",
+ "Fbatf" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "PQf" },
+{ "Arial,serif",
+ "Oebjfr" },
+{ "Arial,serif",
+ "Bhe" },
+{ "Arial,serif",
+ "Fryrpgvba" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "Punahxnu" },
+{ "Arial,serif",
+ "Tvsgf" },
+{ "Arial,serif",
+ "Ubyvqnl" },
+{ "Arial,serif",
+ "Tvsg" },
+{ "Arial,serif",
+ "Vqrnf" },
+{ "Arial,Helvetica,serif",
+ "Juvgr" },
+{ "Arial,Helvetica,serif",
+ "Cntrf" },
+{ "Arial,Helvetica,serif",
+ "Lryybj" },
+{ "Arial,Helvetica,serif",
+ "Zncf" },
+{ "Arial,Helvetica,serif",
+ "&" },
+{ "Arial,Helvetica,serif",
+ "Qverpgvbaf" },
+{ "Arial,,serif",
+ "Qnvyl" },
+{ "Arial,,serif",
+ "Ubebfpbcrf" },
+{ "Arial,Helvetica,serif",
+ "Nvesner" },
+{ "Arial,Helvetica,serif",
+ "Qrnyf" },
+{ "Arial,serif",
+ "Wbof@Zbafgre.pbz" },
+{ "Arial,Helvetica,serif",
+ "Fubc" },
+{ "Arial,Helvetica,serif",
+ "Nznmba.pbz" },
+{ "Arial,Helvetica,serif",
+ "Serr" },
+{ "Arial,Helvetica,serif",
+ "Terrgvatf" },
+{ "verdana,serif",
+ "Gvzr'f" },
+{ "verdana,serif",
+ "Ehaavat" },
+{ "verdana,serif",
+ "Bhg!" },
+{ "verdana,serif",
+ "Beqre" },
+{ "verdana,serif",
+ "Tvsgf" },
+{ "verdana,serif",
+ "Gb" },
+{ "verdana,serif",
+ "Neevir" },
+{ "verdana,serif",
+ "Rneyl" },
+{ "arial,helvetica,serif",
+ "Purpx" },
+{ "arial,helvetica,serif",
+ "Lbhe" },
+{ "arial,helvetica,serif",
+ "R-Znvy" },
+{ "Arial,serif",
+ "Pynffvp" },
+{ "Arial,serif",
+ "R-znvy" },
+{ "Arial,serif",
+ "Pragre" },
+{ "Arial,serif",
+ "Lbhe" },
+{ "Arial,serif",
+ "Fperra" },
+{ "Arial,serif",
+ "Anzr" },
+{ "Arial,serif",
+ "Cnffjbeq" },
+{ "Lucida Grande",
+ "TB" },
+{ "arial,serif",
+ "Zl" },
+{ "arial,serif",
+ "PbzchFreir" },
+{ "serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,serif",
+ "Crefbanyvmr" },
+{ "Arial,serif",
+ "Guvf" },
+{ "Arial,serif",
+ "Cntr" },
+{ "Arial,serif",
+ "Fubj" },
+{ "Arial,serif",
+ "Zl" },
+{ "Arial,serif",
+ "Fghss" },
+{ "arial,helvetica,serif",
+ "Fubccvat" },
+{ "arial,helvetica,serif",
+ "Fubegphgf" },
+{ "Arial,serif",
+ "Frnepu" },
+{ "Arial,serif",
+ "Fubccvat" },
+{ "Arial,serif",
+ "sbe:" },
+{ "Arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,serif",
+ "-" },
+{ "Arial,serif",
+ "CynlFgngvba" },
+{ "Arial,serif",
+ "2" },
+{ "Arial,serif",
+ "$15,000" },
+{ "Arial,serif",
+ "Tvirnjnl" },
+{ "Arial,serif",
+ "Gur" },
+{ "Arial,serif",
+ "Tevapu" },
+{ "Arial,serif",
+ "Obhgvdhr" },
+{ "Arial,serif",
+ "Tvsgf" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "Ertvfgevrf" },
+{ "arial,helvetica,serif",
+ "Wbxr" },
+{ "arial,helvetica,serif",
+ "bs" },
+{ "arial,helvetica,serif",
+ "gur" },
+{ "arial,helvetica,serif",
+ "Qnl" },
+{ "arial,helvetica,serif",
+ "Pebffjbeq" },
+{ "arial,helvetica,serif",
+ "Chmmyrf" },
+{ "arial,helvetica,serif",
+ "Fperra" },
+{ "arial,helvetica,serif",
+ "Fniref" },
+{ "arial,helvetica,serif",
+ "Pryroevgl" },
+{ "arial,helvetica,serif",
+ "Cubgbf" },
+{ "arial,helvetica,serif",
+ "Cubgb" },
+{ "arial,helvetica,serif",
+ "Crefbanyf" },
+{ "arial,helvetica,serif",
+ "Cynl" },
+{ "arial,helvetica,serif",
+ "Tnzrf" },
+{ "arial,helvetica,serif",
+ "Bayvar!" },
+{ "arial,helvetica,serif",
+ "Whfg" },
+{ "arial,helvetica,serif",
+ "sbe" },
+{ "arial,helvetica,serif",
+ "Sha" },
+{ "arial,helvetica,serif",
+ "Gbqnl'f" },
+{ "arial,helvetica,serif",
+ "Srngherq" },
+{ "arial,helvetica,serif",
+ "Fvgr" },
+{ "arial,helvetica,serif",
+ "Ner" },
+{ "arial,helvetica,serif",
+ "Lbh" },
+{ "arial,helvetica,serif",
+ "Tbgu" },
+{ "arial,helvetica,serif",
+ "be" },
+{ "arial,helvetica,serif",
+ "Abg?" },
+{ "arial,helvetica,serif",
+ "Ybbx" },
+{ "arial,helvetica,serif",
+ "ng" },
+{ "arial,helvetica,serif",
+ "gurve" },
+{ "arial,helvetica,serif",
+ "cvpgherf" },
+{ "arial,helvetica,serif",
+ "naq" },
+{ "arial,helvetica,serif",
+ "ibgr" },
+{ "arial,helvetica,serif",
+ "ba" },
+{ "arial,helvetica,serif",
+ "ubj" },
+{ "arial,helvetica,serif",
+ "Tbguvp" },
+{ "arial,helvetica,serif",
+ "crbcyr" },
+{ "arial,helvetica,serif",
+ "ybbx." },
+{ "arial,helvetica,serif",
+ "Be" },
+{ "arial,helvetica,serif",
+ "hcybnq" },
+{ "arial,helvetica,serif",
+ "cvpgher" },
+{ "arial,helvetica,serif",
+ "lbhe" },
+{ "arial,helvetica,serif",
+ "Tbgu" },
+{ "arial,helvetica,serif",
+ "frys." },
+{ "Arial,serif",
+ "Znva" },
+{ "Arial,serif",
+ "Zrah" },
+{ "Arial,serif",
+ "Phfgbzre" },
+{ "Arial,serif",
+ "Freivpr" },
+{ "Arial,serif",
+ "Perngr" },
+{ "Arial,serif",
+ "Ubzr" },
+{ "Arial,serif",
+ "Cntr" },
+{ "Arial,serif",
+ "Zrzore" },
+{ "Arial,serif",
+ "Erongr" },
+{ "Arial,serif",
+ "Cebtenz" },
+{ "Arial,serif",
+ "Ohl" },
+{ "Arial,serif",
+ "N" },
+{ "Arial,serif",
+ "Pbzchgre" },
+{ "Arial,serif",
+ "Onearf" },
+{ "Arial,serif",
+ "Aboyr" },
+{ "Arial,serif",
+ "Obbxf" },
+{ "arial,helvetica,serif",
+ "Zrzore" },
+{ "arial,helvetica,serif",
+ "Orarsvgf" },
+{ "Arial,serif",
+ "MVC" },
+{ "Arial,serif",
+ "Pbqr" },
+{ "Lucida Grande",
+ "Trg Jrngure" },
+{ "verdana,serif",
+ "5-Qnl" },
+{ "verdana,serif",
+ "Sberpnfg" },
+{ "verdana,serif",
+ "|" },
+{ "verdana,serif",
+ "Zncf" },
+{ "verdana,serif",
+ "Zber" },
+{ "verdana,serif",
+ "Jrngure" },
+{ "arial,helvetica,serif",
+ "Zl" },
+{ "arial,helvetica,serif",
+ "Jrngure" },
+{ "arial,helvetica,serif",
+ "Arjf" },
+{ "Arial,Helvetica,serif",
+ "Crefbanyvmr" },
+{ "Arial,Helvetica,serif",
+ "Zl" },
+{ "Arial,Helvetica,serif",
+ "Urnqyvarf" },
+{ "Arial,Helvetica,serif",
+ "Perngr" },
+{ "Arial,Helvetica,serif",
+ "Arjf" },
+{ "Arial,Helvetica,serif",
+ "Sbyqref" },
+{ "arial,helvitica,serif",
+ "Gbc" },
+{ "arial,helvitica,serif",
+ "Arjf" },
+{ "arial,helvitica,serif",
+ "Urnqyvarf" },
+{ "serif",
+ "|" },
+{ "arial,helvitica,serif",
+ "Cubgbf" },
+{ "arial,helvitica,serif",
+ "Tber" },
+{ "arial,helvitica,serif",
+ "Pbaprqrf," },
+{ "arial,helvitica,serif",
+ "Ohfu" },
+{ "arial,helvitica,serif",
+ "Pnyyf" },
+{ "arial,helvitica,serif",
+ "sbe" },
+{ "arial,helvitica,serif",
+ "Havgl" },
+{ "arial,helvitica,serif",
+ "Vpr" },
+{ "arial,helvitica,serif",
+ "Fgbez" },
+{ "arial,helvitica,serif",
+ "Fcernqf" },
+{ "arial,helvitica,serif",
+ "Npebff" },
+{ "arial,helvitica,serif",
+ "gur" },
+{ "arial,helvitica,serif",
+ "Fbhgu" },
+{ "arial,helvitica,serif",
+ "HF" },
+{ "arial,helvitica,serif",
+ "Rkcregf" },
+{ "arial,helvitica,serif",
+ "Guvax" },
+{ "arial,helvitica,serif",
+ "Pbyr" },
+{ "arial,helvitica,serif",
+ "Obzoref" },
+{ "arial,helvitica,serif",
+ "Syrq" },
+{ "arial,helvitica,serif",
+ "Gbxlb" },
+{ "arial,helvitica,serif",
+ "Fgbpx" },
+{ "arial,helvitica,serif",
+ "Cevprf" },
+{ "arial,helvitica,serif",
+ "Fyvc" },
+{ "arial,helvitica,serif",
+ "ZnfgrePneq" },
+{ "arial,helvitica,serif",
+ "Gb" },
+{ "arial,helvitica,serif",
+ "Ynhapu" },
+{ "arial,helvitica,serif",
+ "Fzneg" },
+{ "arial,helvitica,serif",
+ "Pneqf" },
+{ "arial,helvitica,serif",
+ "RRBP:" },
+{ "arial,helvitica,serif",
+ "Pbagenprcgvir" },
+{ "arial,helvitica,serif",
+ "Pbirentr" },
+{ "arial,helvitica,serif",
+ "Arrqrq" },
+{ "arial,helvitica,serif",
+ "Ehffvna" },
+{ "arial,helvitica,serif",
+ "Cerfvqrag" },
+{ "arial,helvitica,serif",
+ "Neevirf" },
+{ "arial,helvitica,serif",
+ "va" },
+{ "arial,helvitica,serif",
+ "Phon" },
+{ "arial,helvitica,serif",
+ "H.F." },
+{ "arial,helvitica,serif",
+ "Ahefr" },
+{ "arial,helvitica,serif",
+ "Beqrerq" },
+{ "arial,helvitica,serif",
+ "Bhg" },
+{ "arial,helvitica,serif",
+ "bs" },
+{ "arial,helvitica,serif",
+ "Avpnenthn" },
+{ "arial,helvitica,serif",
+ "Rk-Fcl" },
+{ "arial,helvitica,serif",
+ "Puvrs" },
+{ "arial,helvitica,serif",
+ "Fnvq" },
+{ "arial,helvitica,serif",
+ "Creh" },
+{ "arial,helvitica,serif",
+ "Obng" },
+{ "arial,helvitica,serif",
+ "Jbeyq" },
+{ "arial,helvitica,serif",
+ "Ohfvarff" },
+{ "Arial,serif",
+ "Ceb" },
+{ "Arial,serif",
+ "Sbbgonyy" },
+{ "Arial,serif",
+ "Pbyyrtr" },
+{ "Arial,serif",
+ "Onfxrgonyy" },
+{ "Arial,serif",
+ "ANFPNE" },
+{ "Arial, HELVETICA,serif",
+ "Ubpxrl" },
+{ "Arial,serif",
+ "Tbys" },
+{ "Arial, HELVETICA,serif",
+ "Zber" },
+{ "Arial, HELVETICA,serif",
+ "Fcbegf" },
+{ "Arial, HELVETICA,serif",
+ "Snagnfl" },
+{ "Arial, HELVETICA,serif",
+ "Tnzrf" },
+{ "arial,helvetica,serif",
+ "Fpberobneq" },
+{ "arial,helvetica,serif",
+ "Rirag" },
+{ "arial,helvetica,serif",
+ " \xe2""\x80""\xa2"" Punahxnu" },
+{ "arial,helvetica,serif",
+ " \xe2""\x80""\xa2"" Jvagre" },
+{ "arial,helvetica,serif",
+ "Fbyfgvpr" },
+{ "arial,helvetica,serif",
+ " \xe2""\x80""\xa2"" " },
+{ "arial,helvetica,serif",
+ "Puevfgznf" },
+{ "arial,helvetica,serif",
+ " \xe2""\x80""\xa2"" Obkvat" },
+{ "arial,helvetica,serif",
+ "Qnl" },
+{ "arial,helvetica,serif",
+ "(Pnanqn)" },
+{ "arial,helvetica,serif",
+ " \xe2""\x80""\xa2"" Xjnnamn" },
+{ "arial,helvetica,serif",
+ "Qngr" },
+{ "arial,helvetica,serif",
+ "Qrprzore" },
+{ "arial,helvetica,serif",
+ "21" },
+{ "arial,helvetica,serif",
+ "25" },
+{ "arial,helvetica,serif",
+ "26" },
+{ "arial,helvetica,serif",
+ "Qrp." },
+{ "arial,helvetica,serif",
+ "26-Wna." },
+{ "arial,helvetica,serif",
+ "1" },
+{ "arial,helvetica,serif",
+ "Fraq" },
+{ "arial,helvetica,serif",
+ "n" },
+{ "arial,helvetica,serif",
+ "Serr" },
+{ "arial,helvetica,serif",
+ "Terrgvat" },
+{ "arial,helvetica,serif",
+ "Pneq" },
+{ "arial,helvetica,serif",
+ "Erzvaqref" },
+{ "Helvetica,Arial,serif",
+ "PBHAGQBJA" },
+{ "Helvetica,Arial,serif",
+ "GB" },
+{ "Helvetica,Arial,serif",
+ "PUEVFGZNF" },
+{ "Helvetica,Arial,serif",
+ "2000" },
+{ "Arial,serif",
+ "Perngr" },
+{ "Arial,serif",
+ "Zl" },
+{ "Arial,serif",
+ "Fgbpx" },
+{ "Arial,serif",
+ "Yvfg" },
+{ "Arial,,serif",
+ "Qvfpynvzre" },
+{ "Arial,Helvetica,serif",
+ "QW" },
+{ "Arial,Helvetica,serif",
+ "30" },
+{ "Arial,Helvetica,serif",
+ "F&C" },
+{ "Arial,Helvetica,serif",
+ "500" },
+{ "Arial,Helvetica,serif",
+ "Anfqnd" },
+{ "Arial,Helvetica,serif",
+ "10794.439" },
+{ "Arial,Helvetica,serif",
+ "1359.990" },
+{ "Arial,Helvetica,serif",
+ "2822.770" },
+{ "Arial,Helvetica,serif",
+ "+26.170" },
+{ "Arial,Helvetica,serif",
+ "-11.190" },
+{ "Arial,Helvetica,serif",
+ "-109.000" },
+{ "Arial,Helvetica,serif",
+ "VAQVPRF" },
+{ "Arial,Helvetica,serif",
+ "Trarengrq:" },
+{ "Arial,Helvetica,serif",
+ "Qrp" },
+{ "Arial,Helvetica,serif",
+ "13," },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "6:58" },
+{ "Arial,Helvetica,serif",
+ "CZ" },
+{ "Arial,Helvetica,serif",
+ "(RG)" },
+{ "Arial,Helvetica,serif",
+ "Fgbpx" },
+{ "Arial,Helvetica,serif",
+ "Yvfg" },
+{ "Arial,Helvetica,serif",
+ " " },
+{ "Arial,Helvetica,serif",
+ "Cbegsbyvb" },
+{ "Lucida Grande",
+ "Trg Dhbgr" },
+{ "arial,helvetica,serif",
+ "Fgbpx" },
+{ "arial,helvetica,serif",
+ "Yvfg" },
+{ "arial,helvetica,serif",
+ "Ybpny" },
+{ "arial,helvetica,serif",
+ "Pragre" },
+{ "Arial, HELVETICA,serif",
+ "Frnepu" },
+{ "Arial, HELVETICA,serif",
+ "Wbo" },
+{ "Arial, HELVETICA,serif",
+ "Yvfgvatf" },
+{ "Arial, HELVETICA,serif",
+ "Zbivr" },
+{ "Arial, HELVETICA,serif",
+ "Fubjgvzrf" },
+{ "Arial, HELVETICA,serif",
+ "Arjf" },
+{ "Arial, HELVETICA,serif",
+ "Fgbevrf" },
+{ "Arial, HELVETICA,serif",
+ "Crefbanyf" },
+{ "Arial, HELVETICA,serif",
+ "Ubzr" },
+{ "Arial, HELVETICA,serif",
+ "sbe" },
+{ "Arial, HELVETICA,serif",
+ "Fnyr" },
+{ "Arial, HELVETICA,serif",
+ "Pbby" },
+{ "Arial, HELVETICA,serif",
+ "Qvavat" },
+{ "Arial, HELVETICA,serif",
+ "Fcbgf" },
+{ "arial,helvetica,serif",
+ "Obbxznexf" },
+{ "Arial,Helvetica,serif",
+ "Rqvg" },
+{ "arial,helvetica,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,helvetica,serif",
+ "Nqq" },
+{ "arial,helvetica,serif",
+ "Snibevgr" },
+{ "Arial,Helvetica,serif",
+ "Nobhg" },
+{ "Arial,Helvetica,serif",
+ "PbzchFreir" },
+{ "Arial,Helvetica,serif",
+ "-" },
+{ "Arial,Helvetica,serif",
+ "Wbof" },
+{ "Arial,Helvetica,serif",
+ "@" },
+{ "Arial,Helvetica,serif",
+ "Nqiregvfvat" },
+{ "Arial,Helvetica,serif",
+ "Cerff" },
+{ "Arial,Helvetica,serif",
+ "Eryrnfrf" },
+{ "Arial,Helvetica,serif",
+ "Phfgbzre" },
+{ "Arial,Helvetica,serif",
+ "Freivpr" },
+{ "Arial,Helvetica,serif",
+ "SND" },
+{ "Arial,Helvetica,serif",
+ "Vagreangvbany" },
+{ "Arial,Helvetica,serif",
+ "Jrofvgrf" },
+{ "Arial,Helvetica,serif",
+ "Qbjaybnq" },
+{ "Arial,Helvetica,serif",
+ "5.0" },
+{ "Arial,Helvetica,serif",
+ "Npprff" },
+{ "Arial,Helvetica,serif",
+ "Ahzoref" },
+{ "Arial,Helvetica,serif",
+ "Ohfvarff" },
+{ "Arial,Helvetica,serif",
+ "Nppbhagf" },
+{ "arial, helvetica,serif",
+ "\xc2""\xa9""" },
+{ "arial, helvetica,serif",
+ "Pbclevtug" },
+{ "arial, helvetica,serif",
+ "2000" },
+{ "arial, helvetica,serif",
+ "PbzchFreir" },
+{ "arial, helvetica,serif",
+ "Vagrenpgvir" },
+{ "arial, helvetica,serif",
+ "Freivprf," },
+{ "arial, helvetica,serif",
+ "Vap." },
+{ "arial, helvetica,serif",
+ "Yrtny" },
+{ "arial, helvetica,serif",
+ "Abgvprf" },
+{ "arial, helvetica,serif",
+ "-" },
+{ "arial, helvetica,serif",
+ "Cevinpl" },
+{ "arial, helvetica,serif",
+ "Cbyvpl" },
+{ "verdana,serif",
+ "Ohfvarff & Pnerref" },
+{ "verdana,serif",
+ "Urnygu & Svgarff" },
+{ "verdana,serif",
+ "Ubyvqnl Prageny" },
+{ "verdana,serif",
+ "Arjf/Ryrpgvba 2000" },
+{ "verdana,serif",
+ "Zber Punaaryf" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "arial,helvetica,serif",
+ "Pyvpx urer" },
+{ "arial,helvetica,serif",
+ " gb ragre n Jro fvgr nqqerff." },
+{ "serif",
+ "O&A.pbz" },
+{ "Arial,serif",
+ "Ebhaq Bhg Lbhe Yvfg: Fubc@" },
+{ "Arial,serif",
+ "Onearf & Aboyr naq Fnir $$ ng tnc.pbz " },
+{ "Arial,serif",
+ "Tb Rys Objyvat Jvgu Fnagn. Vg'f n Oynfg!" },
+{ "Arial,serif",
+ "Puevfgznf Fbatf & PQf" },
+{ "Arial,serif",
+ "Oebjfr Bhe Fryrpgvba bs Punahxnu Tvsgf" },
+{ "Arial,serif",
+ "Ubyvqnl Tvsg Vqrnf" },
+{ "Arial,Helvetica,serif",
+ "Zncf & Qverpgvbaf" },
+{ "Arial,Helvetica,serif",
+ "Juvgr Cntrf" },
+{ "Arial,,serif",
+ "Qnvyl Ubebfpbcrf" },
+{ "Arial,Helvetica,serif",
+ "Fubc Nznmba.pbz" },
+{ "Arial,Helvetica,serif",
+ "Lryybj Cntrf" },
+{ "Arial,Helvetica,serif",
+ "Nvesner Qrnyf" },
+{ "Arial,Helvetica,serif",
+ "Serr Terrgvatf" },
+{ "verdana,serif",
+ "Gvzr'f Ehaavat " },
+{ "verdana,serif",
+ "Bhg! Beqre Tvsgf" },
+{ "verdana,serif",
+ "Gb Neevir Rneyl" },
+{ "serif",
+ "Pyvpx" },
+{ "serif",
+ "Pyvpx " },
+{ "serif",
+ "Urer!" },
+{ "arial,helvetica,serif",
+ "Purpx Lbhe R-Znvy" },
+{ "serif",
+ "PbzchFreir" },
+{ "serif",
+ "2000" },
+{ "serif",
+ "PbzchFreir 2000 " },
+{ "Arial,serif",
+ "Lbhe Fperra Anzr" },
+{ "Arial,serif",
+ "Lbhe Cnffjbeq" },
+{ "Lucida Grande",
+ "TB" },
+{ "Arial,serif",
+ "Pynffvp R-znvy" },
+{ "Arial,serif",
+ "R-znvy Pragre" },
+{ "arial,serif",
+ "Zl PbzchFreir" },
+{ "serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,serif",
+ "Crefbanyvmr Guvf Cntr" },
+{ "Arial,serif",
+ "Fubj Zl Fghss" },
+{ "arial,helvetica,serif",
+ " " },
+{ "arial,helvetica,serif",
+ "Fubccvat Fubegphgf" },
+{ "Arial,serif",
+ "Frnepu Fubccvat sbe:" },
+{ "arial,helvetica,serif",
+ "Zl Jrngure" },
+{ "Arial,serif",
+ "MVC Pbqr" },
+{ "Lucida Grande",
+ "Trg Jrngure" },
+{ "verdana,serif",
+ "5-Qnl Sberpnfg" },
+{ "verdana,serif",
+ " | " },
+{ "verdana,serif",
+ "Zber Jrngure" },
+{ "arial,helvetica,serif",
+ "Zl Fgbpx Yvfg" },
+{ "Arial,serif",
+ "Perngr Zl Fgbpx Yvfg" },
+{ "Lucida Grande",
+ "Trg Dhbgr" },
+{ "serif",
+ "15," },
+{ "serif",
+ "jjj.pbzchfreir.pbz," },
+{ "serif",
+ "296" },
+{ "serif",
+ "1, 15, jjj.pbzchfreir.pbz, 296" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "Arial,serif",
+ "Qrprzore 14, 2000" },
+{ "arial,serif",
+ "Npprff Ahzoref" },
+{ "serif",
+ " " },
+{ "arial,serif",
+ "Fubccvat" },
+{ "arial,serif",
+ "Sbehzf" },
+{ "arial,serif",
+ "Pung" },
+{ "arial,serif",
+ "Vafgnag Zrffntr" },
+{ "Arial,serif",
+ "PbzchFreir 5.0 Serr!" },
+{ "arial,serif",
+ "Tb Rys Objyvat Jvgu Fnagn. Vg'f n Oynfg!" },
+{ "arial,serif",
+ "Puevfgznf Fbatf & PQf" },
+{ "arial,serif",
+ "Oebjfr Bhe Fryrpgvba bs Punahxnu Tvsgf" },
+{ "arial,serif",
+ "Lbhe Fperra Anzr" },
+{ "arial,serif",
+ "Lbhe Cnffjbeq" },
+{ "arial,serif",
+ "Pynffvp R-znvy" },
+{ "arial,serif",
+ "R-znvy Pragre" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,serif",
+ "Ubyvqnl Tvsgf & Ertvfgevrf" },
+{ "Arial,serif",
+ "Gur Tevapu Obhgvdhr" },
+{ "Arial,serif",
+ "$15,000 Ubyvqnl Tvirnjnl" },
+{ "Arial,serif",
+ "- " },
+{ "Arial,serif",
+ "CynlFgngvba 2" },
+{ "arial,helvetica,serif",
+ "Whfg sbe Sha" },
+{ "arial,helvetica,serif",
+ "Wbxr bs gur Qnl" },
+{ "arial,helvetica,serif",
+ "Pebffjbeq Chmmyrf" },
+{ "arial,helvetica,serif",
+ "Fperra Fniref" },
+{ "arial,helvetica,serif",
+ "Pryroevgl Cubgbf" },
+{ "arial,helvetica,serif",
+ "Cubgb Crefbanyf" },
+{ "Arial,Helvetica,serif",
+ "Crefbanyvmr Zl Urnqyvarf" },
+{ "Arial,Helvetica,serif",
+ "Perngr Zl Arjf Sbyqref" },
+{ "arial,helvitica,serif",
+ "Gbc Arjf Urnqyvarf" },
+{ "serif",
+ " | " },
+{ "arial,helvitica,serif",
+ "Tber Pbaprqrf, Ohfu Pnyyf sbe Havgl" },
+{ "arial,helvitica,serif",
+ "Vpr Fgbez Fcernqf Npebff gur Fbhgu" },
+{ "arial,helvitica,serif",
+ "HF Rkcregf Guvax Pbyr Obzoref Syrq" },
+{ "arial,helvitica,serif",
+ "Gbxlb Fgbpx Cevprf Fyvc" },
+{ "arial,helvitica,serif",
+ "ZnfgrePneq Gb Ynhapu Fzneg Pneqf" },
+{ "arial,helvitica,serif",
+ "RRBP: Pbagenprcgvir Pbirentr Arrqrq" },
+{ "Arial,Helvetica,serif",
+ "Fgbpx Yvfg" },
+{ "Arial,Helvetica,serif",
+ " " },
+{ "Arial,Helvetica,serif",
+ "Qrp 13, 2000 6:58 CZ (RG)" },
+{ "Arial,Helvetica,serif",
+ "QW 30" },
+{ "Arial,Helvetica,serif",
+ "F&C 500" },
+{ "arial,helvetica,serif",
+ "Ybpny Pragre" },
+{ "Arial, HELVETICA,serif",
+ "Frnepu Wbo Yvfgvatf" },
+{ "Arial, HELVETICA,serif",
+ "Zbivr Fubjgvzrf" },
+{ "verdana,arial,helvetica,serif",
+ "Jrqarfqnl," },
+{ "verdana,arial,helvetica,serif",
+ "Qrprzore" },
+{ "verdana,arial,helvetica,serif",
+ "13," },
+{ "verdana,arial,helvetica,serif",
+ "2000" },
+{ "sans-serif",
+ "Fhozvg" },
+{ "sans-serif",
+ "Dhrel" },
+{ "verdana,arial,helvetica,serif",
+ "Bbu" },
+{ "verdana,arial,helvetica,serif",
+ "yn" },
+{ "verdana,arial,helvetica,serif",
+ "yn!" },
+{ "verdana,arial,helvetica,serif",
+ "Serapu" },
+{ "verdana,arial,helvetica,serif",
+ "sbbq," },
+{ "verdana,arial,helvetica,serif",
+ "va" },
+{ "verdana,arial,helvetica,serif",
+ "Qvavat" },
+{ "verdana,arial,helvetica,serif",
+ "." },
+{ "verdana,arial,helvetica,serif",
+ "Fbba" },
+{ "verdana,arial,helvetica,serif",
+ "vg" },
+{ "verdana,arial,helvetica,serif",
+ "jvyy" },
+{ "verdana,arial,helvetica,serif",
+ "or" },
+{ "verdana,arial,helvetica,serif",
+ "Puevfgznf" },
+{ "verdana,arial,helvetica,serif",
+ "qnl." },
+{ "verdana,arial,helvetica,serif",
+ "Ragregnvazrag" },
+{ "verdana,arial,helvetica,serif",
+ "b" },
+{ "verdana,arial,helvetica,serif",
+ "Negf" },
+{ "verdana,arial,helvetica,serif",
+ "&" },
+{ "verdana,arial,helvetica,serif",
+ "Phygher" },
+{ "verdana,arial,helvetica,serif",
+ "Onef" },
+{ "verdana,arial,helvetica,serif",
+ "Pyhof" },
+{ "verdana,arial,helvetica,serif",
+ "Xvqf" },
+{ "verdana,arial,helvetica,serif",
+ "Snzvyl" },
+{ "verdana,arial,helvetica,serif",
+ "Zbivrf" },
+{ "verdana,arial,helvetica,serif",
+ "Yvir" },
+{ "verdana,arial,helvetica,serif",
+ "Zhfvp" },
+{ "verdana,arial,helvetica,serif",
+ "Fcbegf" },
+{ "verdana,arial,helvetica,serif",
+ "Erp" },
+{ "verdana,arial,helvetica,serif",
+ "Gurngre" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvgbe'f" },
+{ "verdana,arial,helvetica,serif",
+ "Thvqr" },
+{ "verdana,arial,helvetica,serif",
+ "Gur" },
+{ "verdana,arial,helvetica,serif",
+ "orfg" },
+{ "verdana,arial,helvetica,serif",
+ "snerf" },
+{ "verdana,arial,helvetica,serif",
+ "sebz" },
+{ "verdana,arial,helvetica,serif",
+ "rirel" },
+{ "verdana,arial,helvetica,serif",
+ "nveyvar." },
+{ "verdana,arial,helvetica,serif",
+ "Svaq" },
+{ "verdana,arial,helvetica,serif",
+ "syvtugf" },
+{ "verdana,arial,helvetica,serif",
+ "rnpu" },
+{ "verdana,arial,helvetica,serif",
+ "ybpny" },
+{ "verdana,arial,helvetica,serif",
+ "nvecbeg." },
+{ "verdana,arial,helvetica,serif",
+ "Fubccvat/Freivprf" },
+{ "verdana,arial,helvetica,serif",
+ "Nhpgvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "Nhgbf" },
+{ "verdana,arial,helvetica,serif",
+ "Pynffvsvrqf" },
+{ "verdana,arial,helvetica,serif",
+ "Urnygu" },
+{ "verdana,arial,helvetica,serif",
+ "Svgarff" },
+{ "verdana,arial,helvetica,serif",
+ "Wbof" },
+{ "verdana,arial,helvetica,serif",
+ "Pnerref" },
+{ "verdana,arial,helvetica,serif",
+ "Zbarl" },
+{ "verdana,arial,helvetica,serif",
+ "Erny" },
+{ "verdana,arial,helvetica,serif",
+ "Rfgngr" },
+{ "verdana,arial,helvetica,serif",
+ "Freivprf" },
+{ "verdana,arial,helvetica,serif",
+ "Fubccvat" },
+{ "verdana,arial,helvetica,serif",
+ "Geniry" },
+{ "verdana,arial,helvetica,serif",
+ "Obbxvat" },
+{ "verdana,arial,helvetica,serif",
+ "Zrrg" },
+{ "verdana,arial,helvetica,serif",
+ "gur" },
+{ "verdana,arial,helvetica,serif",
+ "Ybpnyf" },
+{ "verdana,arial,helvetica,serif",
+ "crefbanyf," },
+{ "verdana,arial,helvetica,serif",
+ "pung," },
+{ "verdana,arial,helvetica,serif",
+ "cvpf..." },
+{ "verdana,arial,helvetica,serif",
+ "Ybbxvat" },
+{ "verdana,arial,helvetica,serif",
+ "sbe" },
+{ "verdana,arial,helvetica,serif",
+ "ybir" },
+{ "verdana,arial,helvetica,serif",
+ "nyy" },
+{ "verdana,arial,helvetica,serif",
+ "gur" },
+{ "verdana,arial,helvetica,serif",
+ "evtug" },
+{ "verdana,arial,helvetica,serif",
+ "cynprf." },
+{ "verdana,arial,helvetica,serif",
+ "Trg" },
+{ "verdana,arial,helvetica,serif",
+ "uryc" },
+{ "verdana,arial,helvetica,serif",
+ "jvgu" },
+{ "verdana,arial,helvetica,serif",
+ "n" },
+{ "verdana,arial,helvetica,serif",
+ "serr," },
+{ "verdana,arial,helvetica,serif",
+ "yvir" },
+{ "verdana,arial,helvetica,serif",
+ "pbapvretr" },
+{ "verdana,arial,helvetica,serif",
+ "Orfg" },
+{ "verdana,arial,helvetica,serif",
+ "bs" },
+{ "verdana,arial,helvetica,serif",
+ "Pvgl" },
+{ "verdana,arial,helvetica,serif",
+ "gbc" },
+{ "verdana,arial,helvetica,serif",
+ "cvpxf," },
+{ "verdana,arial,helvetica,serif",
+ "Ybpny" },
+{ "verdana,arial,helvetica,serif",
+ "Rkcregf..." },
+{ "verdana,arial,helvetica,serif",
+ "Qvar" },
+{ "verdana,arial,helvetica,serif",
+ "bhg" },
+{ "verdana,arial,helvetica,serif",
+ "bsgra?" },
+{ "verdana,arial,helvetica,serif",
+ "Erivrj" },
+{ "verdana,arial,helvetica,serif",
+ "lbhe" },
+{ "verdana,arial,helvetica,serif",
+ "snir" },
+{ "verdana,arial,helvetica,serif",
+ "rngf!" },
+{ "verdana,arial,helvetica,serif",
+ "Uvtu" },
+{ "verdana,arial,helvetica,serif",
+ "gvzr" },
+{ "verdana,arial,helvetica,serif",
+ "lbh" },
+{ "verdana,arial,helvetica,serif",
+ "orpbzr" },
+{ "verdana,arial,helvetica,serif",
+ "Ybpny" },
+{ "verdana,arial,helvetica,serif",
+ "Rkcreg" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvgbe'f" },
+{ "verdana,arial,helvetica,serif",
+ "Thvqr" },
+{ "verdana,arial,helvetica,serif",
+ "ubgryf," },
+{ "verdana,arial,helvetica,serif",
+ "fvtugf," },
+{ "verdana,arial,helvetica,serif",
+ "zhfrhzf..." },
+{ "verdana,arial,helvetica,serif",
+ "Lbh'er" },
+{ "verdana,arial,helvetica,serif",
+ "ceb" },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl" },
+{ "verdana,arial,helvetica,serif",
+ "fubccvat" },
+{ "verdana,arial,helvetica,serif",
+ "ng" },
+{ "verdana,arial,helvetica,serif",
+ "FS" },
+{ "verdana,arial,helvetica,serif",
+ "Prager!" },
+{ "verdana,arial,helvetica,serif",
+ "Onu," },
+{ "verdana,arial,helvetica,serif",
+ "uhzoht" },
+{ "verdana,arial,helvetica,serif",
+ "gb" },
+{ "verdana,arial,helvetica,serif",
+ "Kznf" },
+{ "verdana,arial,helvetica,serif",
+ "yvtugf" },
+{ "verdana,arial,helvetica,serif",
+ "?" },
+{ "verdana,arial,helvetica,serif",
+ "..." },
+{ "verdana,arial,helvetica,serif",
+ "Zber" },
+{ "verdana,arial,helvetica,serif",
+ "genssvp" },
+{ "verdana,arial,helvetica,serif",
+ "yvtug" },
+{ "verdana,arial,helvetica,serif",
+ "fheirvyynapr" },
+{ "verdana,arial,helvetica,serif",
+ "pnzf" },
+{ "verdana,arial,helvetica,serif",
+ "pbzvat" },
+{ "verdana,arial,helvetica,serif",
+ "fbba:" },
+{ "verdana,arial,helvetica,serif",
+ "Gubhtugf" },
+{ "verdana,arial,helvetica,serif",
+ "Znlbe'f" },
+{ "verdana,arial,helvetica,serif",
+ "pnaqvqngrf" },
+{ "verdana,arial,helvetica,serif",
+ "birecbjrerq" },
+{ "verdana,arial,helvetica,serif",
+ "ehabssf" },
+{ "verdana,arial,helvetica,serif",
+ "Ernyvgl" },
+{ "verdana,arial,helvetica,serif",
+ "purpx" },
+{ "verdana,arial,helvetica,serif",
+ "bayvar" },
+{ "verdana,arial,helvetica,serif",
+ "ergnvyref" },
+{ "verdana,arial,helvetica,serif",
+ "guvf" },
+{ "verdana,arial,helvetica,serif",
+ "frnfba" },
+{ "verdana,arial,helvetica,serif",
+ "Pnycvar" },
+{ "verdana,arial,helvetica,serif",
+ "cyna" },
+{ "verdana,arial,helvetica,serif",
+ "qbhoyr" },
+{ "verdana,arial,helvetica,serif",
+ "F.W." },
+{ "verdana,arial,helvetica,serif",
+ "fvmr" },
+{ "verdana,arial,helvetica,serif",
+ "Pyvpx" },
+{ "verdana,arial,helvetica,serif",
+ "urer" },
+{ "verdana,arial,helvetica,serif",
+ "sbe" },
+{ "verdana,arial,helvetica,serif",
+ "zber" },
+{ "verdana,arial,helvetica,serif",
+ "arjf" },
+{ "verdana,arial,helvetica,serif",
+ "urnqyvarf" },
+{ "verdana,arial,helvetica,serif",
+ "Envqref" },
+{ "verdana,arial,helvetica,serif",
+ "flap" },
+{ "verdana,arial,helvetica,serif",
+ "hc" },
+{ "verdana,arial,helvetica,serif",
+ "sebag" },
+{ "verdana,arial,helvetica,serif",
+ "Nern'f" },
+{ "verdana,arial,helvetica,serif",
+ "2012" },
+{ "verdana,arial,helvetica,serif",
+ "Bylzcvp" },
+{ "verdana,arial,helvetica,serif",
+ "ovq" },
+{ "verdana,arial,helvetica,serif",
+ "ernpu" },
+{ "verdana,arial,helvetica,serif",
+ "sne" },
+{ "verdana,arial,helvetica,serif",
+ "naq" },
+{ "verdana,arial,helvetica,serif",
+ "jvqr" },
+{ "verdana,arial,helvetica,serif",
+ "Funexf'" },
+{ "verdana,arial,helvetica,serif",
+ "jva" },
+{ "verdana,arial,helvetica,serif",
+ "sbyybjf" },
+{ "verdana,arial,helvetica,serif",
+ "sbezhyn" },
+{ "verdana,arial,helvetica,serif",
+ "fcbegf" },
+{ "verdana,arial,helvetica,serif",
+ "Fcbegf" },
+{ "verdana,arial,helvetica,serif",
+ "Arjf" },
+{ "verdana,arial,helvetica,serif",
+ "sebz" },
+{ "verdana,arial,helvetica,serif",
+ "Fna" },
+{ "verdana,arial,helvetica,serif",
+ "Wbfr" },
+{ "verdana,arial,helvetica,serif",
+ "Zrephel" },
+{ "verdana,arial,helvetica,serif",
+ "Fbhaq" },
+{ "verdana,arial,helvetica,serif",
+ "Bss" },
+{ "verdana,arial,helvetica,serif",
+ "Gbqnl'f" },
+{ "verdana,arial,helvetica,serif",
+ "Arjf," },
+{ "verdana,arial,helvetica,serif",
+ "&" },
+{ "verdana,arial,helvetica,serif",
+ "Vffhrf" },
+{ "verdana,arial,helvetica,serif",
+ "Cerzvre" },
+{ "verdana,arial,helvetica,serif",
+ "Cnegaref" },
+{ "verdana,arial,helvetica,serif",
+ "Yvaxf" },
+{ "verdana,arial,helvetica,serif",
+ "Qrfvta" },
+{ "verdana,arial,helvetica,serif",
+ "evat" },
+{ "verdana,arial,helvetica,serif",
+ "bayvar!" },
+{ "verdana,arial,helvetica,serif",
+ "Funar" },
+{ "verdana,arial,helvetica,serif",
+ "Pb." },
+{ "verdana,arial,helvetica,serif",
+ "Cnegare" },
+{ "verdana,arial,helvetica,serif",
+ "Qvtvgny" },
+{ "verdana,arial,helvetica,serif",
+ "Pvgl:" },
+{ "verdana,arial,helvetica,serif",
+ "Wbva" },
+{ "verdana,arial,helvetica,serif",
+ "bhe" },
+{ "verdana,arial,helvetica,serif",
+ "Nssvyvngr" },
+{ "verdana,arial,helvetica,serif",
+ "Cebtenz." },
+{ "verdana,arial,helvetica,serif",
+ "Jvaavat" },
+{ "verdana,arial,helvetica,serif",
+ "Vqrnf" },
+{ "verdana,arial,helvetica,serif",
+ "Frr" },
+{ "verdana,arial,helvetica,serif",
+ "jung'f" },
+{ "verdana,arial,helvetica,serif",
+ "arj" },
+{ "verdana,arial,helvetica,serif",
+ "zbivrf." },
+{ "verdana,arial,helvetica,serif",
+ "Fubegphgf" },
+{ "verdana,arial,helvetica,serif",
+ "Gvzrfniref" },
+{ "verdana,arial,helvetica,serif",
+ "Grzcg" },
+{ "verdana,arial,helvetica,serif",
+ "gnfgr" },
+{ "verdana,arial,helvetica,serif",
+ "ohqf" },
+{ "verdana,arial,helvetica,serif",
+ "gbavtug!" },
+{ "verdana,arial,helvetica,serif",
+ "Vaperqvoyr" },
+{ "verdana,arial,helvetica,serif",
+ "qrnyf" },
+{ "verdana,arial,helvetica,serif",
+ "rOnl" },
+{ "verdana,arial,helvetica,serif",
+ "Fna" },
+{ "verdana,arial,helvetica,serif",
+ "Senapvfpb!" },
+{ "verdana,arial,helvetica,serif",
+ "Cer-frnfba" },
+{ "verdana,arial,helvetica,serif",
+ "fnyr!" },
+{ "verdana,arial,helvetica,serif",
+ "Obneqf," },
+{ "verdana,arial,helvetica,serif",
+ "fxvf" },
+{ "verdana,arial,helvetica,serif",
+ "-" },
+{ "verdana,arial,helvetica,serif",
+ "svaq" },
+{ "verdana,arial,helvetica,serif",
+ "ybpngvba" },
+{ "verdana,arial,helvetica,serif",
+ "arne" },
+{ "verdana,arial,helvetica,serif",
+ "lbh!" },
+{ "verdana,arial,helvetica,serif",
+ "Cresrpg" },
+{ "verdana,arial,helvetica,serif",
+ "Ncnegzrag" },
+{ "verdana,arial,helvetica,serif",
+ "Fcrpvnyf" },
+{ "verdana,arial,helvetica,serif",
+ "ba" },
+{ "verdana,arial,helvetica,serif",
+ "sybjref" },
+{ "verdana,arial,helvetica,serif",
+ "ubyvqnlf" },
+{ "verdana,arial,helvetica,serif",
+ "!" },
+{ "verdana,arial,helvetica,serif",
+ "Fcbafberq" },
+{ "verdana,arial,helvetica,serif",
+ "Zl" },
+{ "verdana,arial,helvetica,serif",
+ "Zbivr" },
+{ "verdana,arial,helvetica,serif",
+ "Gurngref" },
+{ "verdana,arial,helvetica,serif",
+ "Lryybj" },
+{ "verdana,arial,helvetica,serif",
+ "Cntrf" },
+{ "verdana,arial,helvetica,serif",
+ "Zl" },
+{ "verdana,arial,helvetica,serif",
+ "Fghss" },
+{ "verdana,arial,helvetica,serif",
+ "Jrngure" },
+{ "verdana,arial,helvetica,serif",
+ "FNA" },
+{ "verdana,arial,helvetica,serif",
+ "SENAPVFPB," },
+{ "verdana,arial,helvetica,serif",
+ "PN" },
+{ "verdana,arial,helvetica,serif",
+ "Zbfgyl" },
+{ "verdana,arial,helvetica,serif",
+ "Pybhql" },
+{ "verdana,arial,helvetica,serif",
+ "54\xc2""\xb0""S" },
+{ "verdana,arial,helvetica,serif",
+ "54" },
+{ "verdana,arial,helvetica,serif",
+ "Ybj" },
+{ "verdana,arial,helvetica,serif",
+ "47" },
+{ "verdana,arial,helvetica,serif",
+ "Zncf" },
+{ "verdana,arial,helvetica,serif",
+ "Qverpgvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "cbjrerq" },
+{ "verdana,arial,helvetica,serif",
+ "ol" },
+{ "verdana,arial,helvetica,serif",
+ "Zncdhrfg" },
+{ "verdana,arial,helvetica,serif",
+ "Ybggrel" },
+{ "verdana,arial,helvetica,serif",
+ "Ahzoref" },
+{ "verdana,arial,helvetica,serif",
+ "Erfreingvba" },
+{ "verdana,arial,helvetica,serif",
+ "Pragre" },
+{ "verdana,arial,helvetica,serif",
+ "Serr!" },
+{ "verdana,arial,helvetica,serif",
+ "Yvir" },
+{ "verdana,arial,helvetica,serif",
+ "Pbapvretr" },
+{ "verdana,arial,helvetica,serif",
+ "Cerfragrq" },
+{ "verdana,arial,helvetica,serif",
+ "ol..." },
+{ "verdana,arial,helvetica,serif",
+ "Lbhe" },
+{ "verdana,arial,helvetica,serif",
+ "Gbc" },
+{ "verdana,arial,helvetica,serif",
+ "Pyvpxf" },
+{ "verdana,arial,helvetica,serif",
+ "1." },
+{ "verdana,arial,helvetica,serif",
+ "2." },
+{ "verdana,arial,helvetica,serif",
+ "3." },
+{ "verdana,arial,helvetica,serif",
+ "4." },
+{ "verdana,arial,helvetica,serif",
+ "5." },
+{ "verdana,arial,helvetica,serif",
+ "6." },
+{ "verdana,arial,helvetica,serif",
+ "7." },
+{ "verdana,arial,helvetica,serif",
+ "8." },
+{ "verdana,arial,helvetica,serif",
+ "9." },
+{ "verdana,arial,helvetica,serif",
+ "10." },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnlf" },
+{ "verdana,arial,helvetica,serif",
+ "Svfurezna'f" },
+{ "verdana,arial,helvetica,serif",
+ "Junes" },
+{ "verdana,arial,helvetica,serif",
+ "Arj" },
+{ "verdana,arial,helvetica,serif",
+ "Lrne'f" },
+{ "verdana,arial,helvetica,serif",
+ "Rir" },
+{ "verdana,arial,helvetica,serif",
+ "Cnex" },
+{ "verdana,arial,helvetica,serif",
+ "Cnenqr" },
+{ "verdana,arial,helvetica,serif",
+ "Yvtugsrfg" },
+{ "verdana,arial,helvetica,serif",
+ "Pbheglneq" },
+{ "verdana,arial,helvetica,serif",
+ "Cnegl" },
+{ "verdana,arial,helvetica,serif",
+ "Pryroengvba" },
+{ "verdana,arial,helvetica,serif",
+ "Rkcerff" },
+{ "verdana,arial,helvetica,serif",
+ "Lrnef" },
+{ "verdana,arial,helvetica,serif",
+ "Arj" },
+{ "verdana,arial,helvetica,serif",
+ "Lrne'f" },
+{ "verdana,arial,helvetica,serif",
+ "Tnyn" },
+{ "verdana,arial,helvetica,serif",
+ "Pryroengvba" },
+{ "verdana,arial,helvetica,serif",
+ "Pehvfr" },
+{ "verdana,arial,helvetica,serif",
+ "\xe2""\x80""\x93""" },
+{ "verdana,arial,helvetica,serif",
+ "Pnyvsbeavn" },
+{ "verdana,arial,helvetica,serif",
+ "Ubeaoybjre," },
+{ "verdana,arial,helvetica,serif",
+ "Fna" },
+{ "verdana,arial,helvetica,serif",
+ "Senapvfpb" },
+{ "verdana,arial,helvetica,serif",
+ "Qnl" },
+{ "verdana,arial,helvetica,serif",
+ "2001" },
+{ "verdana,arial,helvetica,serif",
+ "riragf" },
+{ "verdana,arial,helvetica,serif",
+ "zbfg" },
+{ "verdana,arial,helvetica,serif",
+ "pyvpxf" },
+{ "verdana,arial,helvetica,serif",
+ "Pvgl" },
+{ "verdana,arial,helvetica,serif",
+ "gbqnl." },
+{ "verdana,arial,helvetica,serif",
+ "Znva" },
+{ "verdana,arial,helvetica,serif",
+ "|" },
+{ "verdana,arial,helvetica,serif",
+ "Ragregnvazrag" },
+{ "verdana,arial,helvetica,serif",
+ "Crbcyr" },
+{ "verdana,arial,helvetica,serif",
+ "Orfg" },
+{ "verdana,arial,helvetica,serif",
+ "bs" },
+{ "verdana,arial,helvetica,serif",
+ "Arjf" },
+{ "verdana,arial,helvetica,serif",
+ "Bgure" },
+{ "verdana,arial,helvetica,serif",
+ "Pvgvrf" },
+{ "verdana,arial,helvetica,serif",
+ "Fvgr" },
+{ "verdana,arial,helvetica,serif",
+ "Vaqrk" },
+{ "verdana,arial,helvetica,serif",
+ "Uryc" },
+{ "verdana,arial,helvetica,serif",
+ "Cebtenz" },
+{ "verdana,arial,helvetica,serif",
+ "Nobhg" },
+{ "verdana,arial,helvetica,serif",
+ "Hf" },
+{ "verdana,arial,helvetica,serif",
+ "Nernf:" },
+{ "verdana,arial,helvetica,serif",
+ "Jrqqvatf" },
+{ "verdana,arial,helvetica,serif",
+ "Pbzchgref" },
+{ "verdana,arial,helvetica,serif",
+ "Yrtny" },
+{ "verdana,arial,helvetica,serif",
+ "Erybpngvba" },
+{ "verdana,arial,helvetica,serif",
+ "Ubzr" },
+{ "verdana,arial,helvetica,serif",
+ "Vzcebirzrag" },
+{ "verdana,arial,helvetica,serif",
+ "Crefbany" },
+{ "verdana,arial,helvetica,serif",
+ "Svanapr" },
+{ "verdana,arial,helvetica,serif",
+ "Cevinpl" },
+{ "verdana,arial,helvetica,serif",
+ "Cbyvpl" },
+{ "verdana,arial,helvetica,serif",
+ "Abgvprf" },
+{ "verdana,arial,helvetica,serif",
+ "Pbclevtug" },
+{ "verdana,arial,helvetica,serif",
+ "1996-2000" },
+{ "verdana,arial,helvetica,serif",
+ "Qvtvgny" },
+{ "verdana,arial,helvetica,serif",
+ "Pvgl," },
+{ "verdana,arial,helvetica,serif",
+ "Nyy" },
+{ "verdana,arial,helvetica,serif",
+ "Evtugf" },
+{ "verdana,arial,helvetica,serif",
+ "Erfreirq." },
+{ "serif",
+ "16," },
+{ "serif",
+ "jjj.qvtvgnypvgl.pbz," },
+{ "serif",
+ "154" },
+{ "serif",
+ "1, 16, jjj.qvtvgnypvgl.pbz, 154" },
+{ "verdana,arial,helvetica,serif",
+ "Jrqarfqnl, Qrprzore 13, 2000" },
+{ "verdana,arial,helvetica,serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Bbu yn yn! Serapu " },
+{ "verdana,arial,helvetica,serif",
+ "sbbq, va " },
+{ "verdana,arial,helvetica,serif",
+ "Fbba vg jvyy or " },
+{ "verdana,arial,helvetica,serif",
+ " qnl." },
+{ "verdana,arial,helvetica,serif",
+ "b " },
+{ "verdana,arial,helvetica,serif",
+ "Negf & Phygher" },
+{ "verdana,arial,helvetica,serif",
+ "b " },
+{ "verdana,arial,helvetica,serif",
+ "Onef & Pyhof" },
+{ "verdana,arial,helvetica,serif",
+ "Xvqf & Snzvyl" },
+{ "verdana,arial,helvetica,serif",
+ "Yvir Zhfvp" },
+{ "verdana,arial,helvetica,serif",
+ "Fcbegf & Erp" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvgbe'f Thvqr" },
+{ "verdana,arial,helvetica,serif",
+ "Gur " },
+{ "verdana,arial,helvetica,serif",
+ "orfg snerf" },
+{ "verdana,arial,helvetica,serif",
+ " sebz " },
+{ "verdana,arial,helvetica,serif",
+ "rirel nveyvar." },
+{ "verdana,arial,helvetica,serif",
+ "Svaq syvtugf" },
+{ "verdana,arial,helvetica,serif",
+ "rnpu ybpny nvecbeg." },
+{ "verdana,arial,helvetica,serif",
+ "Urnygu & Svgarff" },
+{ "verdana,arial,helvetica,serif",
+ "Wbof & Pnerref" },
+{ "verdana,arial,helvetica,serif",
+ "Erny Rfgngr" },
+{ "verdana,arial,helvetica,serif",
+ "Geniry & Obbxvat" },
+{ "verdana,arial,helvetica,serif",
+ "Zrrg gur Ybpnyf" },
+{ "verdana,arial,helvetica,serif",
+ "crefbanyf, pung, cvpf..." },
+{ "verdana,arial,helvetica,serif",
+ "Ybbxvat sbe " },
+{ "verdana,arial,helvetica,serif",
+ "va nyy gur evtug " },
+{ "verdana,arial,helvetica,serif",
+ "Trg uryc jvgu n " },
+{ "verdana,arial,helvetica,serif",
+ "serr, yvir " },
+{ "verdana,arial,helvetica,serif",
+ "." },
+{ "verdana,arial,helvetica,serif",
+ "Orfg bs gur Pvgl" },
+{ "verdana,arial,helvetica,serif",
+ "gbc cvpxf, Ybpny Rkcregf..." },
+{ "verdana,arial,helvetica,serif",
+ "Qvar bhg bsgra?" },
+{ "verdana,arial,helvetica,serif",
+ " lbhe snir " },
+{ "verdana,arial,helvetica,serif",
+ "Uvtu gvzr lbh " },
+{ "verdana,arial,helvetica,serif",
+ "orpbzr n" },
+{ "verdana,arial,helvetica,serif",
+ "Ybpny Rkcreg" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvgbe'f Thvqr" },
+{ "verdana,arial,helvetica,serif",
+ "ubgryf, fvtugf, zhfrhzf..." },
+{ "verdana,arial,helvetica,serif",
+ "Lbh'er n ceb jvgu" },
+{ "verdana,arial,helvetica,serif",
+ "gur " },
+{ "verdana,arial,helvetica,serif",
+ "Ivfvgbe'f " },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl " },
+{ "verdana,arial,helvetica,serif",
+ "ng FS Prager!" },
+{ "verdana,arial,helvetica,serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Gbqnl'f Arjf, Fcbegf & Vffhrf" },
+{ "verdana,arial,helvetica,serif",
+ "Fbhaq Bss" },
+{ "verdana,arial,helvetica,serif",
+ "Onu, uhzoht gb " },
+{ "verdana,arial,helvetica,serif",
+ "Kznf yvtugf" },
+{ "verdana,arial,helvetica,serif",
+ "? ... Zber genssvp yvtug fheirvyynapr " },
+{ "verdana,arial,helvetica,serif",
+ "pnzf pbzvat fbba: " },
+{ "verdana,arial,helvetica,serif",
+ "Arjf urnqyvarf sebz Fna Wbfr Zrephel Arjf " },
+{ "verdana,arial,helvetica,serif",
+ "Znlbe'f pnaqvqngrf birecbjrerq va ehabssf" },
+{ "verdana,arial,helvetica,serif",
+ "Ernyvgl purpx sbe bayvar ergnvyref guvf frnfba" },
+{ "verdana,arial,helvetica,serif",
+ "Pnycvar cyna qbhoyr F.W. fvmr" },
+{ "verdana,arial,helvetica,serif",
+ "Pyvpx urer sbe zber arjf urnqyvarf" },
+{ "verdana,arial,helvetica,serif",
+ "Fcbegf urnqyvarf " },
+{ "verdana,arial,helvetica,serif",
+ "Envqref va flap hc sebag" },
+{ "verdana,arial,helvetica,serif",
+ "Nern'f 2012 Bylzcvp ovq jvyy ernpu sne naq jvqr" },
+{ "verdana,arial,helvetica,serif",
+ "Funexf' jva sbyybjf gur sbezhyn" },
+{ "verdana,arial,helvetica,serif",
+ "Pyvpx urer sbe zber fcbegf urnqyvarf" },
+{ "verdana,arial,helvetica,serif",
+ "Cerzvre Cnegaref" },
+{ "verdana,arial,helvetica,serif",
+ "Cerzvre Yvaxf" },
+{ "verdana,arial,helvetica,serif",
+ "Qrfvta lbhe evat" },
+{ "verdana,arial,helvetica,serif",
+ "bayvar! Funar Pb." },
+{ "verdana,arial,helvetica,serif",
+ "Zl Fghss" },
+{ "verdana,arial,helvetica,serif",
+ " " },
+{ "verdana,arial,helvetica,serif",
+ "Zl Zbivr Gurngref" },
+{ "verdana,arial,helvetica,serif",
+ "Zl Lryybj Cntrf" },
+{ "verdana,arial,helvetica,serif",
+ "FNA SENAPVFPB, PN" },
+{ "verdana,arial,helvetica,serif",
+ "Zbfgyl Pybhql 54\xc2""\xb0""S" },
+{ "verdana,arial,helvetica,serif",
+ "Uvtu 54 Ybj 47" },
+{ "verdana,arial,helvetica,serif",
+ "Zncf & Qverpgvbaf" },
+{ "verdana,arial,helvetica,serif",
+ "cbjrerq ol Zncdhrfg" },
+{ "verdana,arial,helvetica,serif",
+ "Gbqnl'f Ybggrel Ahzoref" },
+{ "verdana,arial,helvetica,serif",
+ "Erfreingvba Pragre" },
+{ "verdana,arial,helvetica,serif",
+ "Serr! Yvir Pbapvretr" },
+{ "verdana,arial,helvetica,serif",
+ "Cerfragrq ol..." },
+{ "verdana,arial,helvetica,serif",
+ "Lbhe Gbc Pyvpxf" },
+{ "verdana,arial,helvetica,serif",
+ "Zhfvp riragf jvgu gur zbfg" },
+{ "verdana,arial,helvetica,serif",
+ "pyvpxf ba Qvtvgny Pvgl gbqnl." },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnlf ng Svfurezna'f " },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrne'f Rir 2000" },
+{ "verdana,arial,helvetica,serif",
+ "Puevfgznf va gur Cnex naq " },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl Cnenqr" },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl Yvtugsrfg" },
+{ "verdana,arial,helvetica,serif",
+ "Zhfvp va gur Pbheglneq" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrne'f Rir Cnegl" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrne'f Rir Pryroengvba " },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrnef Rir Cnegl" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrne'f Tnyn" },
+{ "verdana,arial,helvetica,serif",
+ "Pryroengvba Pehvfr \xe2""\x80""\x93""" },
+{ "verdana,arial,helvetica,serif",
+ "Pnyvsbeavn Ubeaoybjre, Fna" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Lrnef Qnl 2001 " },
+{ "serif",
+ "Fzneg" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Nhgbzbgvir" },
+{ "serif",
+ "Ohfvarff" },
+{ "serif",
+ "Rkpunatr" },
+{ "serif",
+ "Terng" },
+{ "serif",
+ "Pbyyrpgvbaf" },
+{ "serif",
+ "Unys.pbz" },
+{ "serif",
+ "(na" },
+{ "serif",
+ "rOnl" },
+{ "serif",
+ "pbzcnal)" },
+{ "serif",
+ "Nagvdhrf" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Neg" },
+{ "serif",
+ "Obbxf" },
+{ "serif",
+ "|" },
+{ "serif",
+ "Zbivrf" },
+{ "serif",
+ "Zhfvp" },
+{ "serif",
+ "Pbvaf" },
+{ "serif",
+ "Fgnzcf" },
+{ "serif",
+ "Pbyyrpgvoyrf" },
+{ "serif",
+ "Pbzchgref" },
+{ "serif",
+ "Qbyyf," },
+{ "serif",
+ "Svtherf" },
+{ "serif",
+ "Wrjryel," },
+{ "serif",
+ "Trzfgbarf" },
+{ "serif",
+ "Cubgb" },
+{ "serif",
+ "Ryrpgebavpf" },
+{ "serif",
+ "Cbggrel" },
+{ "serif",
+ "Tynff" },
+{ "serif",
+ "Erny" },
+{ "serif",
+ "Rfgngr" },
+{ "serif",
+ "Fcbegf" },
+{ "serif",
+ "Gblf," },
+{ "serif",
+ "Orna" },
+{ "serif",
+ "Ont" },
+{ "serif",
+ "Cyhfu" },
+{ "serif",
+ "Rirelguvat" },
+{ "serif",
+ "Ryfr" },
+{ "serif",
+ ":" },
+{ "serif",
+ "Srnghevat" },
+{ "serif",
+ "Pybguvat" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Npprffbevrf" },
+{ "serif",
+ "," },
+{ "serif",
+ "Ubzr" },
+{ "serif",
+ "Tneqra" },
+{ "serif",
+ "Gvpxrgf" },
+{ "serif",
+ "Geniry" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "pngrtbevrf..." },
+{ "serif",
+ "Bire" },
+{ "serif",
+ "5" },
+{ "serif",
+ "zvyyvba" },
+{ "serif",
+ "vgrzf" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "fnyr!" },
+{ "serif",
+ "rOnl" },
+{ "serif",
+ "Nhfgenyvn" },
+{ "serif",
+ "Pnanqn" },
+{ "serif",
+ "Senapr" },
+{ "serif",
+ "Treznal" },
+{ "serif",
+ "Wncna" },
+{ "serif",
+ "Havgrq" },
+{ "serif",
+ "Xvatqbz" },
+{ "serif",
+ "Ybir" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "Qnapr" },
+{ "serif",
+ "Orne" },
+{ "serif",
+ "A'Flap" },
+{ "serif",
+ "Znevbarggrf" },
+{ "serif",
+ "Cbjrechss/Zbwb" },
+{ "serif",
+ "Wbwb" },
+{ "serif",
+ "Thaqnz" },
+{ "serif",
+ "Jvat" },
+{ "serif",
+ "Puneyvr'f" },
+{ "serif",
+ "Natryf" },
+{ "serif",
+ "Qbyyf" },
+{ "serif",
+ "Nccyvnaprf" },
+{ "serif",
+ "Fcbegvat" },
+{ "serif",
+ "Tbbqf" },
+{ "serif",
+ "Sheavgher" },
+{ "serif",
+ "zber..." },
+{ "Lucida Grande",
+ "Cvpx" },
+{ "Lucida Grande",
+ "n" },
+{ "Lucida Grande",
+ "ertvba" },
+{ "Lucida Grande",
+ "NM--Cubravk" },
+{ "Lucida Grande",
+ "PN--Ybf" },
+{ "Lucida Grande",
+ "Natryrf" },
+{ "Lucida Grande",
+ "PN--Bnxynaq" },
+{ "Lucida Grande",
+ "PN--Benatr" },
+{ "Lucida Grande",
+ "Pbhagl" },
+{ "Lucida Grande",
+ "PN--Fnpenzragb" },
+{ "Lucida Grande",
+ "PN--Fna" },
+{ "Lucida Grande",
+ "Qvrtb" },
+{ "Lucida Grande",
+ "Senapvfpb" },
+{ "Lucida Grande",
+ "Wbfr" },
+{ "Lucida Grande",
+ "PB--Qraire" },
+{ "Lucida Grande",
+ "PG--Unegsbeq" },
+{ "Lucida Grande",
+ "QP--Jnfuvatgba" },
+{ "Lucida Grande",
+ "SY--Wnpxfbaivyyr" },
+{ "Lucida Grande",
+ "SY--Zvnzv" },
+{ "Lucida Grande",
+ "SY--Beynaqb" },
+{ "Lucida Grande",
+ "SY--Gnzcn-Fg." },
+{ "Lucida Grande",
+ "Crgr" },
+{ "Lucida Grande",
+ "SY--Jrfg" },
+{ "Lucida Grande",
+ "Cnyz" },
+{ "Lucida Grande",
+ "Ornpu" },
+{ "Lucida Grande",
+ "TN--Ngynagn" },
+{ "Lucida Grande",
+ "UV--Ubabyhyh" },
+{ "Lucida Grande",
+ "VQ--Ovyyvatf-Obvfr" },
+{ "Lucida Grande",
+ "VY--Puvpntb" },
+{ "Lucida Grande",
+ "VA--Vaqvnancbyvf" },
+{ "Lucida Grande",
+ "XF--Xnafnf" },
+{ "Lucida Grande",
+ "Pvgl" },
+{ "Lucida Grande",
+ "XL--Ybhvfivyyr" },
+{ "Lucida Grande",
+ "YN--Arj" },
+{ "Lucida Grande",
+ "Beyrnaf" },
+{ "Lucida Grande",
+ "ZN--Obfgba" },
+{ "Lucida Grande",
+ "ZQ--Onygvzber" },
+{ "Lucida Grande",
+ "ZV--Qrgebvg" },
+{ "Lucida Grande",
+ "ZV--Tenaq" },
+{ "Lucida Grande",
+ "Encvqf" },
+{ "Lucida Grande",
+ "ZA--Zvaa-Fg." },
+{ "Lucida Grande",
+ "Cnhy" },
+{ "Lucida Grande",
+ "ZB--Xnafnf" },
+{ "Lucida Grande",
+ "ZB--Fg." },
+{ "Lucida Grande",
+ "Ybhvf" },
+{ "Lucida Grande",
+ "ZG--Ovyyvatf-Obvfr" },
+{ "Lucida Grande",
+ "AP--Puneybggr" },
+{ "Lucida Grande",
+ "AP--Terrafobeb" },
+{ "Lucida Grande",
+ "AP--Enyrvtu-Qheunz" },
+{ "Lucida Grande",
+ "AQ--Ovfznepx-Cvreer" },
+{ "Lucida Grande",
+ "AZ--Nyohdhredhr" },
+{ "Lucida Grande",
+ "AI--Ynf" },
+{ "Lucida Grande",
+ "Irtnf" },
+{ "Lucida Grande",
+ "AL--Nyonal" },
+{ "Lucida Grande",
+ "AL--Ohssnyb" },
+{ "Lucida Grande",
+ "AL--Arj" },
+{ "Lucida Grande",
+ "Lbex" },
+{ "Lucida Grande",
+ "AL--Ebpurfgre" },
+{ "Lucida Grande",
+ "BU--Pvapvaangv" },
+{ "Lucida Grande",
+ "BU--Pyrirynaq" },
+{ "Lucida Grande",
+ "BU--Pbyhzohf" },
+{ "Lucida Grande",
+ "BU--Qnlgba" },
+{ "Lucida Grande",
+ "BX--Bxynubzn" },
+{ "Lucida Grande",
+ "BE--Cbegynaq" },
+{ "Lucida Grande",
+ "CN--Cuvynqrycuvn" },
+{ "Lucida Grande",
+ "CN--Cvggfohetu" },
+{ "Lucida Grande",
+ "EV--Cebivqrapr" },
+{ "Lucida Grande",
+ "FQ--Ovfznepx-Cvreer" },
+{ "Lucida Grande",
+ "GA--Zrzcuvf" },
+{ "Lucida Grande",
+ "GA--Anfuivyyr" },
+{ "Lucida Grande",
+ "GK--Nhfgva" },
+{ "Lucida Grande",
+ "GK--Qnyynf-Sbeg" },
+{ "Lucida Grande",
+ "Jbegu" },
+{ "Lucida Grande",
+ "GK--Ubhfgba" },
+{ "Lucida Grande",
+ "GK--Fna" },
+{ "Lucida Grande",
+ "Nagbavb" },
+{ "Lucida Grande",
+ "HG--Fnyg" },
+{ "Lucida Grande",
+ "Ynxr" },
+{ "Lucida Grande",
+ "IN--Abesbyx-IN" },
+{ "Lucida Grande",
+ "IN--Evpuzbaq" },
+{ "Lucida Grande",
+ "JN--Frnggyr-Gnpbzn" },
+{ "Lucida Grande",
+ "JV--Zvyjnhxrr" },
+{ "Lucida Grande",
+ "Tb!" },
+{ "serif",
+ "Tvsgf" },
+{ "serif",
+ "-" },
+{ "serif",
+ "Zra" },
+{ "serif",
+ "Jbzra" },
+{ "serif",
+ "Puvyqera" },
+{ "Lucida Grande",
+ "Nyy" },
+{ "Lucida Grande",
+ "Gurzrf" },
+{ "Lucida Grande",
+ "Navzngvba" },
+{ "Lucida Grande",
+ "Nhgb" },
+{ "Lucida Grande",
+ "Enpvat" },
+{ "Lucida Grande",
+ "Oneovr" },
+{ "Lucida Grande",
+ "Onfronyy" },
+{ "Lucida Grande",
+ "Ornef" },
+{ "Lucida Grande",
+ "Oveguqnlf" },
+{ "Lucida Grande",
+ "Oebnqjnl" },
+{ "Lucida Grande",
+ "Puvangbja" },
+{ "Lucida Grande",
+ "Puvan-Cbggrel" },
+{ "Lucida Grande",
+ "Pbzvpf" },
+{ "Lucida Grande",
+ "Pbbxvat" },
+{ "Lucida Grande",
+ "PbhagelJrfgrea" },
+{ "Lucida Grande",
+ "Pelfgny-Tynff" },
+{ "Lucida Grande",
+ "Qvfnovyvgl" },
+{ "Lucida Grande",
+ "Erf." },
+{ "Lucida Grande",
+ "Ryrpgvba" },
+{ "Lucida Grande",
+ "2000" },
+{ "Lucida Grande",
+ "Ryivf" },
+{ "Lucida Grande",
+ "Snfuvba" },
+{ "Lucida Grande",
+ "Svar" },
+{ "Lucida Grande",
+ "Neg" },
+{ "Lucida Grande",
+ "Sbbgonyy" },
+{ "Lucida Grande",
+ "Sheavgher" },
+{ "Lucida Grande",
+ "Tnqtrgf" },
+{ "Lucida Grande",
+ "Tnzrf" },
+{ "Lucida Grande",
+ "Tneqravat" },
+{ "Lucida Grande",
+ "Tvsgf" },
+{ "Lucida Grande",
+ "Sbe" },
+{ "Lucida Grande",
+ "Puvy." },
+{ "Lucida Grande",
+ "Zra" },
+{ "Lucida Grande",
+ "Jb." },
+{ "Lucida Grande",
+ "Tbys" },
+{ "Lucida Grande",
+ "Terng" },
+{ "Lucida Grande",
+ "Bhgqbbef" },
+{ "Lucida Grande",
+ "Ubzr" },
+{ "Lucida Grande",
+ "Vzcebir." },
+{ "Lucida Grande",
+ "Vagrevbef" },
+{ "Lucida Grande",
+ "Gurngre" },
+{ "Lucida Grande",
+ "Vasnagf-Gbqqyref" },
+{ "Lucida Grande",
+ "VG" },
+{ "Lucida Grande",
+ "Fbyhgvbaf" },
+{ "Lucida Grande",
+ "Wnmm" },
+{ "Lucida Grande",
+ "Xberna" },
+{ "Lucida Grande",
+ "Zbqry" },
+{ "Lucida Grande",
+ "Genvaf" },
+{ "Lucida Grande",
+ "Zbivrf" },
+{ "Lucida Grande",
+ "Ba" },
+{ "Lucida Grande",
+ "Pnzchf" },
+{ "Lucida Grande",
+ "Crg" },
+{ "Lucida Grande",
+ "Fghss" },
+{ "Lucida Grande",
+ "Fpubby" },
+{ "Lucida Grande",
+ "Fhccyvrf" },
+{ "Lucida Grande",
+ "Fpv-Sv" },
+{ "Lucida Grande",
+ "Fravbef" },
+{ "Lucida Grande",
+ "Fcbegf" },
+{ "Lucida Grande",
+ "Riragf" },
+{ "Lucida Grande",
+ "Crefbaf" },
+{ "Lucida Grande",
+ "Geniry" },
+{ "Lucida Grande",
+ "GI" },
+{ "Lucida Grande",
+ "Pbcf" },
+{ "Lucida Grande",
+ "Inevrgl" },
+{ "Lucida Grande",
+ "Fvgpbzf" },
+{ "Lucida Grande",
+ "Fcbafbef" },
+{ "Lucida Grande",
+ "Jrqqvatf" },
+{ "Lucida Grande",
+ "Jrfgreaf" },
+{ "serif",
+ "Cbyvpr" },
+{ "serif",
+ "Fvmr" },
+{ "serif",
+ "Crccre" },
+{ "serif",
+ "Fcenl" },
+{ "serif",
+ "$44.95" },
+{ "serif",
+ "bayl" },
+{ "serif",
+ "$9.00" },
+{ "serif",
+ "rn" },
+{ "serif",
+ "Qb" },
+{ "serif",
+ "LBH!" },
+{ "serif",
+ "unir" },
+{ "serif",
+ "n" },
+{ "serif",
+ "~~SYLVAT" },
+{ "serif",
+ "CVT~~" },
+{ "serif",
+ "Zbabcbyl" },
+{ "serif",
+ "cvrpr?" },
+{ "serif",
+ "Yvir" },
+{ "serif",
+ "Obafnv" },
+{ "serif",
+ "Gerr" },
+{ "serif",
+ "Terng" },
+{ "serif",
+ "Kznf" },
+{ "serif",
+ "Tvsg" },
+{ "serif",
+ "2000" },
+{ "serif",
+ "Fnpntnjrn" },
+{ "serif",
+ "Tbyq" },
+{ "serif",
+ "Qbyynef" },
+{ "serif",
+ "C" },
+{ "serif",
+ "Q" },
+{ "serif",
+ "Va" },
+{ "serif",
+ "Ubyqre" },
+{ "serif",
+ "Gurl'yy" },
+{ "serif",
+ "Fjrne" },
+{ "serif",
+ "lbh" },
+{ "serif",
+ "unq" },
+{ "serif",
+ "Yvcbfhpgvba!" },
+{ "serif",
+ "23yof/2jxf" },
+{ "serif",
+ "V'ir" },
+{ "serif",
+ "Tbar" },
+{ "serif",
+ "Phpxbb" },
+{ "serif",
+ "Juvgu" },
+{ "serif",
+ "Pybpxf" },
+{ "serif",
+ "10" },
+{ "serif",
+ "@" },
+{ "serif",
+ "$1" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "srngherq" },
+{ "serif",
+ "vgrzf..." },
+{ "serif",
+ "Ebfvr" },
+{ "serif",
+ "Tvivat" },
+{ "serif",
+ "Zber" },
+{ "serif",
+ "Punevgl" },
+{ "serif",
+ "Cevpryrff" },
+{ "serif",
+ "Ivfn" },
+{ "serif",
+ "Tevapu" },
+{ "serif",
+ "tvir" },
+{ "serif",
+ "onpx" },
+{ "serif",
+ "ubyvqnl" },
+{ "serif",
+ "Ybfg" },
+{ "serif",
+ "Sbhaq" },
+{ "serif",
+ "102" },
+{ "serif",
+ "Qnyzngvnaf" },
+{ "serif",
+ "cebcf" },
+{ "serif",
+ "Ynfg" },
+{ "serif",
+ "hcqngrq:" },
+{ "serif",
+ "Qrp-14-00" },
+{ "serif",
+ "17:30:08" },
+{ "serif",
+ "CFG" },
+{ "serif",
+ "Naabhaprzragf" },
+{ "serif",
+ "Ertvfgre" },
+{ "serif",
+ "Fgber" },
+{ "serif",
+ "FnsrUneobe" },
+{ "serif",
+ "(Ehyrf" },
+{ "serif",
+ "Fnsrgl)" },
+{ "serif",
+ "Srrqonpx" },
+{ "serif",
+ "Sbehz" },
+{ "serif",
+ "Nobhg" },
+{ "serif",
+ "Wbof" },
+{ "serif",
+ "Nssvyvngrf" },
+{ "serif",
+ "Cebtenz" },
+{ "serif",
+ "Naljurer" },
+{ "serif",
+ "Pbclevtug" },
+{ "serif",
+ "\xc2""\xa9""" },
+{ "serif",
+ "1995-2000" },
+{ "serif",
+ "Vap." },
+{ "serif",
+ "Nyy" },
+{ "serif",
+ "Evtugf" },
+{ "serif",
+ "Erfreirq." },
+{ "serif",
+ "Qrfvtangrq" },
+{ "serif",
+ "genqrznexf" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "oenaqf" },
+{ "serif",
+ "ner" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "cebcregl" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "gurve" },
+{ "serif",
+ "erfcrpgvir" },
+{ "serif",
+ "bjaref." },
+{ "serif",
+ "Hfr" },
+{ "serif",
+ "guvf" },
+{ "serif",
+ "Jro" },
+{ "serif",
+ "fvgr" },
+{ "serif",
+ "pbafgvghgrf" },
+{ "serif",
+ "npprcgnapr" },
+{ "serif",
+ "Hfre" },
+{ "serif",
+ "Nterrzrag" },
+{ "serif",
+ "Cevinpl" },
+{ "serif",
+ "Cbyvpl" },
+{ "serif",
+ "." },
+{ "serif",
+ "Zl" },
+{ "serif",
+ "Fvgr" },
+{ "serif",
+ "Znc" },
+{ "serif",
+ "Oebjfr" },
+{ "serif",
+ "Fryy" },
+{ "serif",
+ "Freivprf" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Uryc" },
+{ "serif",
+ "Pbzzhavgl" },
+{ "serif",
+ "17," },
+{ "serif",
+ "jjj.ronl.pbz," },
+{ "serif",
+ "180" },
+{ "serif",
+ "1, 17, jjj.ronl.pbz, 180" },
+{ "serif",
+ "Fcbegvat Tbbqf" },
+{ "Lucida Grande",
+ "Cvpx n ertvba" },
+{ "Lucida Grande",
+ "Tb!" },
+{ "serif",
+ "Fzneg Frnepu" },
+{ "serif",
+ "Ohfvarff Rkpunatr" },
+{ "serif",
+ "Terng Pbyyrpgvbaf" },
+{ "serif",
+ " (na rOnl " },
+{ "serif",
+ "Nagvdhrf & Neg" },
+{ "serif",
+ " | " },
+{ "serif",
+ "Qbyyf, Svtherf" },
+{ "serif",
+ "Wrjryel, Trzfgbarf" },
+{ "serif",
+ "Cubgb & Ryrpgebavpf" },
+{ "serif",
+ "Cbggrel & Tynff" },
+{ "serif",
+ "Erny Rfgngr" },
+{ "serif",
+ "Gblf, Orna Ont Cyhfu" },
+{ "serif",
+ "Rirelguvat Ryfr" },
+{ "serif",
+ ": " },
+{ "serif",
+ "Pybguvat & Npprffbevrf" },
+{ "serif",
+ "," },
+{ "serif",
+ "Ubzr & Tneqra" },
+{ "serif",
+ ", " },
+{ "serif",
+ ", " },
+{ "serif",
+ "nyy pngrtbevrf..." },
+{ "serif",
+ "Bire 5 zvyyvba vgrzf sbe fnyr!" },
+{ "serif",
+ "Ybir gb Qnapr Orne" },
+{ "serif",
+ "A'Flap Znevbarggrf" },
+{ "serif",
+ "Cbjrechss/Zbwb Wbwb" },
+{ "serif",
+ "Thaqnz Jvat" },
+{ "serif",
+ "Puneyvr'f Natryf Qbyyf" },
+{ "serif",
+ "Tvsgf - Zra" },
+{ "serif",
+ "Tvsgf - Jbzra" },
+{ "serif",
+ "Tvsgf - Puvyqera" },
+{ "Lucida Grande",
+ "Nyy Gurzrf" },
+{ "serif",
+ "Cbyvpr Fvmr Crccre Fcenl $44.95 bayl $9.00 rn" },
+{ "serif",
+ "Qb LBH! unir n ~~SYLVAT CVT~~ Zbabcbyl cvrpr?" },
+{ "serif",
+ "Yvir Obafnv Gerr Tneqra - Terng Kznf Tvsg" },
+{ "serif",
+ "2000 Fnpntnjrn Tbyq Qbyynef C & Q Va Ubyqre" },
+{ "serif",
+ "Gurl'yy Fjrne lbh unq Yvcbfhpgvba! 23yof/2jxf" },
+{ "serif",
+ "V'ir Tbar Phpxbb Juvgu Phpxbb Pybpxf 10 @ $1" },
+{ "serif",
+ "nyy srngherq vgrzf..." },
+{ "serif",
+ "Tvivat Gerr" },
+{ "serif",
+ "Zber Punevgl" },
+{ "arial,serif",
+ "Perngr" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "Fgneg" },
+{ "arial,serif",
+ "Cntr!" },
+{ "arial,serif",
+ "Crefbanyvmr:" },
+{ "arial,serif",
+ "Cntr" },
+{ "arial,serif",
+ "Frggvatf" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "Pbagrag" },
+{ "arial,serif",
+ "Ynlbhg" },
+{ "arial,serif",
+ "Pbybe" },
+{ "arial,serif",
+ "Fraq" },
+{ "arial,serif",
+ "na" },
+{ "arial,serif",
+ "R-Xvff" },
+{ "verdana,serif",
+ "\xc2""\xbb""" },
+{ "arial,serif",
+ "Fvta" },
+{ "arial,serif",
+ "Hc" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "Va" },
+{ "arial,serif",
+ "Uryc" },
+{ "arial,serif",
+ " Gbqnl" },
+{ "arial,serif",
+ "Ba" },
+{ "arial,serif",
+ "Rkpvgr" },
+{ "arial,serif",
+ "12/14" },
+{ "arial,serif",
+ "4:35" },
+{ "arial,serif",
+ "RG" },
+{ "arial,serif",
+ " Arjf " },
+{ "arial,serif",
+ "Tber" },
+{ "arial,serif",
+ "Pbaprqrf" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "Ohfu" },
+{ "arial,serif",
+ " Cbyy " },
+{ "arial,serif",
+ "Cerfvqrag" },
+{ "arial,serif",
+ "Ohfu?" },
+{ "arial,serif",
+ " Gvc " },
+{ "arial,serif",
+ "Fraq" },
+{ "arial,serif",
+ "Vafgnag" },
+{ "arial,serif",
+ "Tvsgf" },
+{ "arial,serif",
+ " \xe2""\x80""\xa2"" " },
+{ "arial,serif",
+ "Ybhvf" },
+{ "arial,serif",
+ "Fvatf" },
+{ "arial,serif",
+ "Kznf" },
+{ "arial,serif",
+ "R-Pneq" },
+{ "arial,serif",
+ "Cubgb" },
+{ "arial,serif",
+ "Crefbanyf" },
+{ "arial,serif",
+ "Fyvqrfubj" },
+{ "arial,serif",
+ "Abegurea" },
+{ "arial,serif",
+ "Yvtugf" },
+{ "arial,serif",
+ "Cubgbf" },
+{ "arial,serif",
+ "33" },
+{ "arial,serif",
+ "Pbby" },
+{ "arial,serif",
+ "Tnqtrg" },
+{ "arial,serif",
+ "Tvsgf" },
+{ "arial,serif",
+ "Cerpvfvba" },
+{ "arial,serif",
+ "Frnepu" },
+{ "arial,serif",
+ "Frnepu: " },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "ZC3/Nhqvb" },
+{ "arial,serif",
+ "Iblrhe" },
+{ "arial,serif",
+ "Zber" },
+{ "arial,serif",
+ "Cebqhpgf:" },
+{ "arial,serif",
+ "Pnzrenf" },
+{ "arial,serif",
+ "Gblf" },
+{ "arial,serif",
+ "PQ" },
+{ "arial,serif",
+ "Cynlref" },
+{ "arial,serif",
+ "R-Pneqf:" },
+{ "arial,serif",
+ "Fabjsynxr" },
+{ "arial,serif",
+ "Tnzr" },
+{ "arial,serif",
+ " |" },
+{ "arial,serif",
+ "Kznf" },
+{ "arial,serif",
+ "Rys" },
+{ "arial,serif",
+ " Ubyvqnl" },
+{ "arial,serif",
+ "Rffragvnyf" },
+{ "arial,serif",
+ "QvtvPnzf" },
+{ "arial,serif",
+ "Fuvc" },
+{ "arial,serif",
+ "Serr" },
+{ "arial,serif",
+ "Zbfg-Jnagrq" },
+{ "arial,serif",
+ "33" },
+{ "arial,serif",
+ "Haqre" },
+{ "arial,serif",
+ "$20" },
+{ "arial,serif",
+ "Shaal" },
+{ "arial,serif",
+ "Pneqf" },
+{ "arial,serif",
+ "Unys" },
+{ "arial,serif",
+ "Cevpr" },
+{ "arial,serif",
+ "PQ/QIQ" },
+{ "arial,serif",
+ "Jvagre" },
+{ "arial,serif",
+ "Jbaqreynaq" },
+{ "arial,serif",
+ "Pngpu" },
+{ "arial,serif",
+ "N" },
+{ "arial,serif",
+ "Fabj" },
+{ "arial,serif",
+ "Synxr!" },
+{ "arial,serif",
+ "Zl" },
+{ "arial,serif",
+ "Fgbpxf" },
+{ "arial,serif",
+ "Puneyrf" },
+{ "arial,serif",
+ "Fpujno" },
+{ "arial,serif",
+ "Bcra" },
+{ "arial,serif",
+ "Nppbhag" },
+{ "arial,serif",
+ "Genqr" },
+{ "arial,serif",
+ "Abj" },
+{ "arial,serif",
+ "Shyy" },
+{ "arial,serif",
+ "Cbegsbyvb" },
+{ "arial,serif",
+ " | " },
+{ "arial,serif",
+ "Svaq" },
+{ "arial,serif",
+ "Flzoby" },
+{ "arial,serif",
+ "Trg" },
+{ "arial,serif",
+ "Dhbgrf" },
+{ "arial,serif",
+ ":" },
+{ "Lucida Grande",
+ "Tb" },
+{ "arial,serif",
+ "Gel" },
+{ "arial,serif",
+ "3" },
+{ "arial,serif",
+ "Rdhvgl" },
+{ "arial,serif",
+ "Ercbegf" },
+{ "arial,serif",
+ "Serr!" },
+{ "arial,serif",
+ "Anfqnd" },
+{ "arial,serif",
+ "Qbj" },
+{ "arial,serif",
+ "F&C" },
+{ "arial,serif",
+ "500" },
+{ "arial,serif",
+ "NGUZ" },
+{ "arial,serif",
+ "VAGH" },
+{ "arial,serif",
+ "Cevpr" },
+{ "arial,serif",
+ "2822.77" },
+{ "arial,serif",
+ "10794.44" },
+{ "arial,serif",
+ "1359.99" },
+{ "arial,serif",
+ "6.84" },
+{ "arial,serif",
+ "50.88" },
+{ "arial,serif",
+ "Punatr" },
+{ "arial,serif",
+ "-109.00" },
+{ "arial,serif",
+ "+26.17" },
+{ "arial,serif",
+ "-11.19" },
+{ "arial,serif",
+ "-0.19" },
+{ "arial,serif",
+ "-0.06" },
+{ "arial,serif",
+ "Bcra" },
+{ "arial,serif",
+ "na" },
+{ "arial,serif",
+ "nppbhag" },
+{ "arial,serif",
+ "jvgu" },
+{ "arial,serif",
+ "Fpujno" },
+{ "arial,serif",
+ "Zbfg" },
+{ "arial,serif",
+ "Npgvir" },
+{ "arial,serif",
+ "Fgbpx" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "*" },
+{ "arial,serif",
+ "=" },
+{ "arial,serif",
+ "Gbqnl" },
+{ "arial,serif",
+ "U/Y" },
+{ "arial,serif",
+ "=" },
+{ "arial,serif",
+ "52" },
+{ "arial,serif",
+ "jx" },
+{ "arial,serif",
+ "uvtu/ybj" },
+{ "arial,serif",
+ "Ynfg" },
+{ "arial,serif",
+ "hcqngr" },
+{ "arial,serif",
+ "Qrp" },
+{ "arial,serif",
+ "13" },
+{ "arial,serif",
+ "5:16CZ" },
+{ "arial,serif",
+ "RFG" },
+{ "arial,serif",
+ "Qngn" },
+{ "arial,serif",
+ "qrynlrq" },
+{ "arial,serif",
+ "ng" },
+{ "arial,serif",
+ "yrnfg" },
+{ "arial,serif",
+ "20" },
+{ "arial,serif",
+ "zvahgrf" },
+{ "arial,serif",
+ "Ubebfpbcr" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "qnvyl" },
+{ "arial,serif",
+ "sberpnfg!" },
+{ "arial,serif",
+ "Ragre" },
+{ "arial,serif",
+ "Lbhe" },
+{ "arial,serif",
+ "Oveguqnl" },
+{ "arial,serif",
+ "(ZZ" },
+{ "arial,serif",
+ "QQ" },
+{ "arial,serif",
+ "LLLL)" },
+{ "Lucida Grande",
+ "trg zl ubebfpbcr" },
+{ "arial,serif",
+ "Frr" },
+{ "arial,serif",
+ "nyy" },
+{ "arial,serif",
+ "fvtaf" },
+{ "arial,serif",
+ " \xe2""\x80""\xa2"" " },
+{ "arial,serif",
+ "Engr" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "ebznapr" },
+{ "arial,serif",
+ "Fraq R-Pneqf:" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Ubebfpbcrf" },
+{ "arial,serif",
+ "V-Puvat" },
+{ "arial,serif",
+ "Puvarfr" },
+{ "arial,serif",
+ " Rkcyber" },
+{ "arial,serif",
+ "Fubc" },
+{ "arial,serif",
+ "Ubyvqnl" },
+{ "arial,serif",
+ "," },
+{ "arial,serif",
+ "Nhpgvbaf" },
+{ "arial,serif",
+ "Pynffvsvrqf" },
+{ "arial,serif",
+ "Tvsg Onfxrgf" },
+{ "arial,serif",
+ "QIQ" },
+{ "arial,serif",
+ "..." },
+{ "arial,serif",
+ "Pbaarpg" },
+{ "arial,serif",
+ "Pung!" },
+{ "arial,serif",
+ "Zrffratre" },
+{ "arial,serif",
+ "CrbcyrSvaqre" },
+{ "arial,serif",
+ "Ibvpr Pung" },
+{ "arial,serif",
+ "Gbbyf" },
+{ "arial,serif",
+ "Nqqerff Obbx" },
+{ "arial,serif",
+ "Pnyraqne" },
+{ "arial,serif",
+ "Pbapreg Gvpxrgf" },
+{ "arial,serif",
+ "Fgbpx Dhbgrf" },
+{ "arial,serif",
+ "Lryybj Cntrf" },
+{ "arial,serif",
+ "Zber" },
+{ "arial,serif",
+ "Nhgbf" },
+{ "arial,serif",
+ "Pnef" },
+{ "arial,serif",
+ "Svanapvat" },
+{ "arial,serif",
+ "Gehpxf" },
+{ "arial,serif",
+ "Ohfvarff" },
+{ "arial,serif",
+ " Arj!" },
+{ "arial,serif",
+ "Pnerref" },
+{ "arial,serif",
+ "Vaqhfgevrf" },
+{ "arial,serif",
+ "Gbbyf" },
+{ "arial,serif",
+ "Pbzchgref" },
+{ "arial,serif",
+ "Qbjaybnqf" },
+{ "arial,serif",
+ "Fbsgjner" },
+{ "arial,serif",
+ "Ragregnvazrag" },
+{ "arial,serif",
+ "Zbivrf" },
+{ "arial,serif",
+ "Zhfvp" },
+{ "arial,serif",
+ "!" },
+{ "arial,serif",
+ "GI" },
+{ "arial,serif",
+ "Tnzrf" },
+{ "arial,serif",
+ "Pnfvabf" },
+{ "arial,serif",
+ "Bayvar" },
+{ "arial,serif",
+ "Urnygu" },
+{ "arial,serif",
+ "Pbaqvgvbaf" },
+{ "arial,serif",
+ "Qvrg" },
+{ "arial,serif",
+ "Fravbef" },
+{ "arial,serif",
+ "Ubzr/Erny" },
+{ "arial,serif",
+ "Rfgngr" },
+{ "arial,serif",
+ "Ohl" },
+{ "arial,serif",
+ "Svanapr" },
+{ "arial,serif",
+ "Qrfvta" },
+{ "arial,serif",
+ "Vairfgvat" },
+{ "arial,serif",
+ "Zhghny" },
+{ "arial,serif",
+ "Shaqf" },
+{ "arial,serif",
+ "Fgbpxf" },
+{ "arial,serif",
+ "401X" },
+{ "arial,serif",
+ "Yvsrfglyr" },
+{ "arial,serif",
+ "Rqhpngvba" },
+{ "arial,serif",
+ "Snzvyl" },
+{ "arial,serif",
+ "Eryngvbafuvcf" },
+{ "arial,serif",
+ "Oyvaq" },
+{ "arial,serif",
+ "Qngr" },
+{ "arial,serif",
+ "Crefbanyf" },
+{ "arial,serif",
+ "Grraf" },
+{ "arial,serif",
+ "Fcbegf" },
+{ "arial,serif",
+ "ASY" },
+{ "arial,serif",
+ "AON" },
+{ "arial,serif",
+ "APNN" },
+{ "arial,serif",
+ "Sbbgonyy" },
+{ "arial,serif",
+ "Geniry" },
+{ "arial,serif",
+ "Nvesnerf" },
+{ "arial,serif",
+ "Qrfgvangvbaf" },
+{ "arial,serif",
+ "Zncf" },
+{ "arial,serif",
+ "Serr" },
+{ "arial,serif",
+ "Jro" },
+{ "arial,serif",
+ "Npprff" },
+{ "arial,serif",
+ "Jvagre" },
+{ "arial,serif",
+ "Fxvaf" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "Zber" },
+{ "arial,serif",
+ "Arjf..." },
+{ "arial,serif",
+ "Fhaf" },
+{ "arial,serif",
+ "103," },
+{ "arial,serif",
+ "Fchef" },
+{ "arial,serif",
+ "93" },
+{ "arial,serif",
+ "Ubeargf" },
+{ "arial,serif",
+ "101," },
+{ "arial,serif",
+ "Xvatf" },
+{ "arial,serif",
+ "90" },
+{ "arial,serif",
+ "Fcbegf" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "(Qrp" },
+{ "arial,serif",
+ "14" },
+{ "arial,serif",
+ "4:25NZ)" },
+{ "arial,serif",
+ "Unpxre" },
+{ "arial,serif",
+ "Vasvygengrf" },
+{ "arial,serif",
+ "Perqvgpneqf.pbz" },
+{ "arial,serif",
+ "Qbg-pbzf'" },
+{ "arial,serif",
+ "Ubyvqnl" },
+{ "arial,serif",
+ "Cnegvrf" },
+{ "arial,serif",
+ "Yrff" },
+{ "arial,serif",
+ "Zreel" },
+{ "arial,serif",
+ "Grpuabybtl" },
+{ "arial,serif",
+ "12:41NZ)" },
+{ "arial,serif",
+ "Gbxlb" },
+{ "arial,serif",
+ "Fgbpx" },
+{ "arial,serif",
+ "Cevprf" },
+{ "arial,serif",
+ "Pybfr" },
+{ "arial,serif",
+ "Ybjre" },
+{ "arial,serif",
+ "Qbj" },
+{ "arial,serif",
+ "Raqf" },
+{ "arial,serif",
+ "Hc" },
+{ "arial,serif",
+ "26," },
+{ "arial,serif",
+ "Anfqnd" },
+{ "arial,serif",
+ "Qebcf" },
+{ "arial,serif",
+ "109" },
+{ "arial,serif",
+ "Ohfvarff" },
+{ "arial,serif",
+ "3:32NZ)" },
+{ "arial,serif",
+ "Pbaprqrf," },
+{ "arial,serif",
+ "Pnyyf" },
+{ "arial,serif",
+ "Sbe" },
+{ "arial,serif",
+ "Havgl" },
+{ "arial,serif",
+ "Chgva" },
+{ "arial,serif",
+ "Fgnegf" },
+{ "arial,serif",
+ "Uvfgbevp" },
+{ "arial,serif",
+ "Phona" },
+{ "arial,serif",
+ "Gevc" },
+{ "arial,serif",
+ "Gbc" },
+{ "arial,serif",
+ "Fgbevrf" },
+{ "arial,serif",
+ "4:01NZ)" },
+{ "arial,serif",
+ "Ubzr" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "Bqq" },
+{ "arial,serif",
+ "Cubgbf" },
+{ "arial,serif",
+ "Crefbanyvmr" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "trg" },
+{ "arial,serif",
+ "n" },
+{ "arial,serif",
+ "arjf" },
+{ "arial,serif",
+ "cubgb!" },
+{ "arial,serif",
+ "Cerfvqrag-Ryrpg" },
+{ "arial,serif",
+ "Ohfu" },
+{ "arial,serif",
+ "Frrxf" },
+{ "arial,serif",
+ "Havgl" },
+{ "arial,serif",
+ "Cerfvqrag-ryrpg" },
+{ "arial,serif",
+ "Ohfu," },
+{ "arial,serif",
+ "cercnevat" },
+{ "arial,serif",
+ "ragre" },
+{ "arial,serif",
+ "gur" },
+{ "arial,serif",
+ "Juvgr" },
+{ "arial,serif",
+ "Ubhfr" },
+{ "arial,serif",
+ "jvgubhg" },
+{ "arial,serif",
+ "ryrpgvba" },
+{ "arial,serif",
+ "znwbevgl," },
+{ "arial,serif",
+ "pnyyrq" },
+{ "arial,serif",
+ "hcba" },
+{ "arial,serif",
+ "angvba" },
+{ "arial,serif",
+ "Jrqarfqnl" },
+{ "arial,serif",
+ "avtug" },
+{ "arial,serif",
+ "\"chg" },
+{ "arial,serif",
+ "cbyvgvpf" },
+{ "arial,serif",
+ "oruvaq" },
+{ "arial,serif",
+ "hf" },
+{ "arial,serif",
+ "naq" },
+{ "arial,serif",
+ "jbex" },
+{ "arial,serif",
+ "gbtrgure\"" },
+{ "arial,serif",
+ "ba" },
+{ "arial,serif",
+ "Fbpvny" },
+{ "arial,serif",
+ "Frphevgl," },
+{ "arial,serif",
+ "Zrqvpner" },
+{ "arial,serif",
+ "gnk" },
+{ "arial,serif",
+ "eryvrs" },
+{ "arial,serif",
+ "nsgre" },
+{ "arial,serif",
+ "n" },
+{ "arial,serif",
+ "qvssvphyg" },
+{ "arial,serif",
+ "ryrpgvba." },
+{ "arial,serif",
+ "\"Bhe" },
+{ "arial,serif",
+ "zhfg" },
+{ "arial,serif",
+ "evfr" },
+{ "arial,serif",
+ "nobir" },
+{ "arial,serif",
+ "." },
+{ "arial,serif",
+ "zber" },
+{ "arial,serif",
+ " 101" },
+{ "arial,serif",
+ "Ubyvqnl" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "arial,serif",
+ "Ubg" },
+{ "arial,serif",
+ "Fcbgf " },
+{ "arial,serif",
+ "R-Pneqf" },
+{ "arial,serif",
+ "Jvagre" },
+{ "arial,serif",
+ "Jbaqreynaq" },
+{ "arial,serif",
+ "R-Pneq" },
+{ "arial,serif",
+ "Tvsg" },
+{ "arial,serif",
+ "Nggnpuzragf" },
+{ "arial,serif",
+ "Pbyyrpgvbaf " },
+{ "arial,serif",
+ "Fubc" },
+{ "arial,serif",
+ "Frperg" },
+{ "arial,serif",
+ "Fnagn" },
+{ "arial,serif",
+ "Onfxrgf" },
+{ "arial,serif",
+ "Tnqtrg" },
+{ "arial,serif",
+ "Theh" },
+{ "arial,serif",
+ "Qrcnegzragf " },
+{ "arial,serif",
+ "Pybgurf" },
+{ "arial,serif",
+ "Pbzchgref" },
+{ "arial,serif",
+ "Ryrpgebavpf" },
+{ "arial,serif",
+ "Ubzr" },
+{ "arial,serif",
+ "Gblf" },
+{ "arial,serif",
+ "Fcrpvny" },
+{ "arial,serif",
+ "Bssref" },
+{ "arial,serif",
+ "Pynffvsvrqf" },
+{ "arial,serif",
+ "R-Pneqf" },
+{ "arial,serif",
+ "Nhpgvbaf" },
+{ "arial,serif",
+ "Qba'g" },
+{ "arial,serif",
+ "Zvff" },
+{ "arial,serif",
+ "gung" },
+{ "arial,serif",
+ "Fnyr!" },
+{ "arial,serif",
+ "Jrngure" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "Pvgl" },
+{ "arial,serif",
+ "be" },
+{ "arial,serif",
+ "MVC" },
+{ "arial,serif",
+ "Pbqr:" },
+{ "Lucida Grande",
+ "Fhozvg" },
+{ "arial,serif",
+ "Nvecbeg" },
+{ "arial,serif",
+ "Qrynlf" },
+{ "arial,serif",
+ "Nyznanp" },
+{ "arial,serif",
+ " Zl" },
+{ "arial,serif",
+ "Obbxznexf" },
+{ "arial,serif",
+ " i " },
+{ "arial,serif",
+ "Yvaxf" },
+{ "arial,serif",
+ "Fperra" },
+{ "arial,serif",
+ "Fniref" },
+{ "arial,serif",
+ "Gbc" },
+{ "arial,serif",
+ "ZC3" },
+{ "arial,serif",
+ "Qbjaybnqf" },
+{ "arial,serif",
+ "Svaq" },
+{ "arial,serif",
+ "Qernz" },
+{ "arial,serif",
+ "Ubzr" },
+{ "arial,serif",
+ "Nyohzf" },
+{ "arial,serif",
+ "Navzngrq" },
+{ "arial,serif",
+ "R-Pneqf" },
+{ "arial,serif",
+ "Freivprf" },
+{ "arial,serif",
+ "Ohl" },
+{ "arial,serif",
+ "Obbxf" },
+{ "arial,serif",
+ "ng" },
+{ "arial,serif",
+ "Nznmba.pbz" },
+{ "arial,serif",
+ "Obbx" },
+{ "arial,serif",
+ "Syvtugf" },
+{ "arial,serif",
+ "Genirybpvgl" },
+{ "arial,serif",
+ "JroZQ" },
+{ "arial,serif",
+ "Urnygu" },
+{ "arial,serif",
+ "Pyvpx" },
+{ "arial,serif",
+ "rqvg" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "punatr" },
+{ "arial,serif",
+ "obbxznexf" },
+{ "arial,serif",
+ "Pung" },
+{ "arial,serif",
+ "Riragf" },
+{ "arial,serif",
+ "3769" },
+{ "arial,serif",
+ "crbcyr" },
+{ "arial,serif",
+ "punggvat" },
+{ "arial,serif",
+ "Fhmnaar" },
+{ "arial,serif",
+ "Fbzref" },
+{ "arial,serif",
+ "wbvaf" },
+{ "arial,serif",
+ "UFA" },
+{ "arial,serif",
+ "Qrp" },
+{ "arial,serif",
+ "15gu" },
+{ "arial,serif",
+ "@12abba" },
+{ "arial,serif",
+ "RG." },
+{ "arial,serif",
+ "8gu" },
+{ "arial,serif",
+ "Naavirefnel" },
+{ "arial,serif",
+ "Cnegl!" },
+{ "arial,serif",
+ "Ubyvqnl" },
+{ "arial,serif",
+ "Cerqvpgvbaf" },
+{ "arial,serif",
+ "Fhaqnl" },
+{ "arial,serif",
+ "guebhtu" },
+{ "arial,serif",
+ "Sevqnl" },
+{ "arial,serif",
+ "@9cz" },
+{ "arial,serif",
+ "RG" },
+{ "arial,serif",
+ "Zber" },
+{ "arial,serif",
+ "Pung" },
+{ "arial,serif",
+ "Riragf..." },
+{ "arial,serif",
+ "Zrffntr Obneqf" },
+{ "arial,serif",
+ "Ibvpr" },
+{ "arial,serif",
+ "Pung" },
+{ "arial,serif",
+ "Arj" },
+{ "arial,serif",
+ "Ubzrf" },
+{ "arial,serif",
+ "@Ubzrfgber.pbz" },
+{ "arial,serif",
+ "Argjbex:" },
+{ "arial,serif",
+ "Rkpvgr" },
+{ "arial,serif",
+ "Oyhr" },
+{ "arial,serif",
+ "Zbhagnva" },
+{ "arial,serif",
+ "Negf" },
+{ "arial,serif",
+ "Jrofubgf" },
+{ "arial,serif",
+ "Rkpvgr@Ubzr" },
+{ "arial,serif",
+ "Freivprf:" },
+{ "arial,serif",
+ "@Ubzr" },
+{ "arial,serif",
+ "Jro" },
+{ "arial,serif",
+ "Npprff" },
+{ "arial,serif",
+ "Bayvar" },
+{ "arial,serif",
+ "Fgbersebag" },
+{ "arial,serif",
+ "VFC" },
+{ "arial,serif",
+ "Jbex.pbz" },
+{ "arial,serif",
+ "Uryc" },
+{ "arial,serif",
+ " Arj!" },
+{ "arial,serif",
+ "Zbovyr" },
+{ "arial,serif",
+ "Nffvfgnag" },
+{ "arial,serif",
+ "Nqinaprq" },
+{ "arial,serif",
+ "Frnepu" },
+{ "arial,serif",
+ "Gbbyone" },
+{ "arial,serif",
+ "Vaivgr" },
+{ "arial,serif",
+ "Tybony" },
+{ "arial,serif",
+ "Rkpvgr:" },
+{ "arial,serif",
+ "Nhfgenyvn" },
+{ "arial,serif",
+ "Nhfgevn" },
+{ "arial,serif",
+ "Pnanqn" },
+{ "arial,serif",
+ "va" },
+{ "arial,serif",
+ "Puvarfr" },
+{ "arial,serif",
+ "Qraznex" },
+{ "arial,serif",
+ "Senapr" },
+{ "arial,serif",
+ "Treznal" },
+{ "arial,serif",
+ "Vgnyl" },
+{ "arial,serif",
+ "Wncna" },
+{ "arial,serif",
+ "Argureynaqf" },
+{ "arial,serif",
+ "Fcnva" },
+{ "arial,serif",
+ "Fjrqra" },
+{ "arial,serif",
+ "Fjvgmreynaq" },
+{ "arial,serif",
+ "H.X." },
+{ "arial,serif",
+ "Nqq" },
+{ "arial,serif",
+ "HEY" },
+{ "arial,serif",
+ "Nqiregvfr" },
+{ "arial,serif",
+ "Wbof@Rkpvgr" },
+{ "arial,serif",
+ "Cerff" },
+{ "arial,serif",
+ "Eryrnfrf" },
+{ "arial,serif",
+ "Vairfgbe" },
+{ "arial,serif",
+ "Eryngvbaf" },
+{ "arial,serif",
+ "Srrqonpx" },
+{ "arial,serif",
+ "cntr" },
+{ "arial,serif",
+ "Grezf" },
+{ "arial,serif",
+ "bs" },
+{ "arial,serif",
+ "Freivpr" },
+{ "arial,serif",
+ " / " },
+{ "arial,serif",
+ "Cevinpl" },
+{ "arial,serif",
+ "Fgngrzrag" },
+{ "arial,serif",
+ "Pbclevtug \xc2""\xa9"" 2000 Ng" },
+{ "arial,serif",
+ "Ubzr" },
+{ "arial,serif",
+ "Pbecbengvba," },
+{ "arial,serif",
+ "Nffbpvngrq" },
+{ "arial,serif",
+ "Cerff," },
+{ "arial,serif",
+ "Erhgref" },
+{ "arial,serif",
+ "Yvzvgrq," },
+{ "arial,serif",
+ "Havgrq" },
+{ "arial,serif",
+ "Cerff" },
+{ "arial,serif",
+ "Vagreangvbany," },
+{ "arial,serif",
+ "Gevohar" },
+{ "arial,serif",
+ "Zrqvn" },
+{ "arial,serif",
+ "Freivprf," },
+{ "arial,serif",
+ "Urnefg" },
+{ "arial,serif",
+ "Pbzzhavpngvbaf," },
+{ "arial,serif",
+ "Vap.," },
+{ "arial,serif",
+ "FcbegfGvpxre" },
+{ "arial,serif",
+ "Ragrecevfrf" },
+{ "arial,serif",
+ "YC," },
+{ "arial,serif",
+ "GvpxrgZnfgre" },
+{ "arial,serif",
+ "Pbec.," },
+{ "arial,serif",
+ "JrngureYnof" },
+{ "arial,serif",
+ "Dhvpxra.pbz," },
+{ "arial,serif",
+ "Dhbgrf" },
+{ "arial,serif",
+ "cebivqrq" },
+{ "arial,serif",
+ "ol" },
+{ "arial,serif",
+ "F&C/Pbzfgbpx." },
+{ "arial,serif",
+ "Nyy" },
+{ "arial,serif",
+ "Evtugf" },
+{ "arial,serif",
+ "Erfreirq." },
+{ "arial,serif",
+ "FcbegfGvpxre" },
+{ "arial,serif",
+ "Ragrecevfrf" },
+{ "arial,serif",
+ "YC," },
+{ "arial,serif",
+ "GvpxrgZnfgre" },
+{ "arial,serif",
+ "Pbec.," },
+{ "arial,serif",
+ "JrngureYnof" },
+{ "arial,serif",
+ "Vap.," },
+{ "arial,serif",
+ "Dhvpxra.pbz," },
+{ "arial,serif",
+ "Dhbgrf" },
+{ "arial,serif",
+ "cebivqrq" },
+{ "arial,serif",
+ "ol" },
+{ "arial,serif",
+ "F&C/Pbzfgbpx." },
+{ "arial,serif",
+ "Nyy" },
+{ "arial,serif",
+ "Evtugf" },
+{ "arial,serif",
+ "Erfreirq." },
+{ "arial,serif",
+ " Arjf " },
+{ "arial,serif",
+ "Tber Pbaprqrf gb Ohfu" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ " Cbyy " },
+{ "arial,serif",
+ "Cerfvqrag Ohfu?" },
+{ "arial,serif",
+ " Gvc " },
+{ "arial,serif",
+ "Fraq Vafgnag Tvsgf" },
+{ "arial,serif",
+ "Ybhvf Fvatf Kznf R-Pneq" },
+{ "arial,serif",
+ "Cubgb Crefbanyf Fyvqrfubj" },
+{ "arial,serif",
+ "Abegurea Yvtugf Cubgbf" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Pbby Tnqtrg Tvsgf" },
+{ "arial,serif",
+ " " },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "arial,serif",
+ "Frnepu: " },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Cebqhpgf: " },
+{ "arial,serif",
+ "PQ Cynlref" },
+{ "arial,serif",
+ "QvtvPnzf Fuvc Serr" },
+{ "arial,serif",
+ "Zbfg-Jnagrq Tvsgf" },
+{ "arial,serif",
+ "33 Tvsgf Haqre $20" },
+{ "arial,serif",
+ "Fraq Shaal Pneqf" },
+{ "arial,serif",
+ "Unys Cevpr PQ/QIQ" },
+{ "arial,serif",
+ "Jvagre Jbaqreynaq" },
+{ "arial,serif",
+ "Pngpu N Fabj Synxr!" },
+{ "arial,serif",
+ "R-Pneqf: " },
+{ "arial,serif",
+ "Fabjsynxr Tnzr" },
+{ "arial,serif",
+ " | " },
+{ "arial,serif",
+ "Kznf Rys" },
+{ "arial,serif",
+ "Zl Fgbpxf" },
+{ "arial,serif",
+ "Rqvg|Zvavzvmr|Pybfr" },
+{ "arial,serif",
+ "Puneyrf Fpujno" },
+{ "arial,serif",
+ "Bcra Nppbhag" },
+{ "arial,serif",
+ "Genqr Abj" },
+{ "arial,serif",
+ "Gel 3 Rdhvgl Ercbegf Serr!" },
+{ "arial,serif",
+ "Trg Dhbgrf" },
+{ "arial,serif",
+ ": " },
+{ "Lucida Grande",
+ "Tb" },
+{ "arial,serif",
+ "Shyy Cbegsbyvb" },
+{ "arial,serif",
+ " | " },
+{ "arial,serif",
+ "Svaq Flzoby" },
+{ "arial,serif",
+ "F&C 500" },
+{ "arial,serif",
+ "Bcra na nppbhag jvgu Fpujno" },
+{ "arial,serif",
+ "Zbfg Npgvir" },
+{ "arial,serif",
+ "Fgbpx Arjf" },
+{ "arial,serif",
+ "* = " },
+{ "arial,serif",
+ "Arjf Gbqnl" },
+{ "arial,serif",
+ " = 52 jx uvtu/ybj" },
+{ "arial,serif",
+ "Ynfg hcqngr Qrp 13 5:16CZ RFG" },
+{ "arial,serif",
+ "Qngn qrynlrq ng yrnfg 20 zvahgrf" },
+{ "arial,serif",
+ "Zl Ubebfpbcr" },
+{ "arial,serif",
+ "Trg lbhe qnvyl sberpnfg!" },
+{ "arial,serif",
+ "Ragre Lbhe Oveguqnl" },
+{ "arial,serif",
+ "(ZZ QQ LLLL)" },
+{ "arial,serif",
+ " Rkcyber Rkpvgr" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ ", " },
+{ "arial,serif",
+ "Tvsg Onfxrgf" },
+{ "arial,serif",
+ "... " },
+{ "arial,serif",
+ "Ibvpr Pung" },
+{ "arial,serif",
+ "Nqqerff Obbx" },
+{ "arial,serif",
+ "Pbapreg Gvpxrgf" },
+{ "arial,serif",
+ "Fgbpx Dhbgrf" },
+{ "arial,serif",
+ "Lryybj Cntrf" },
+{ "arial,serif",
+ " Arj!" },
+{ "arial,serif",
+ "Ubzr/Erny Rfgngr" },
+{ "arial,serif",
+ "Zhghny Shaqf" },
+{ "arial,serif",
+ "Oyvaq Qngr" },
+{ "arial,serif",
+ "APNN Sbbgonyy" },
+{ "arial,serif",
+ "Serr Jro Npprff" },
+{ "arial,serif",
+ "Trg Jvagre Fxvaf" },
+{ "arial,serif",
+ "Zl Arjf" },
+{ "arial,serif",
+ "Cerfvqrag-Ryrpg Ohfu Frrxf Havgl" },
+{ "arial,serif",
+ "Cerfvqrag-ryrpg Ohfu, cercnevat gb ragre gur Juvgr " },
+{ "arial,serif",
+ "Ubhfr jvgubhg na ryrpgvba znwbevgl, pnyyrq hcba gur " },
+{ "arial,serif",
+ "angvba Jrqarfqnl avtug gb \"chg cbyvgvpf oruvaq hf " },
+{ "arial,serif",
+ "Pyvpx" },
+{ "arial,serif",
+ "Urer!" },
+{ "arial,serif",
+ "Pyvpx Urer!" },
+{ "arial,serif",
+ " 101 Ubyvqnl Tvsgf" },
+{ "arial,serif",
+ "Fubc Rkpvgr" },
+{ "arial,serif",
+ "Ubg Fcbgf " },
+{ "arial,serif",
+ "Ubyvqnl R-Pneqf" },
+{ "arial,serif",
+ "R-Pneq Tvsg Nggnpuzragf" },
+{ "arial,serif",
+ "Pbyyrpgvbaf " },
+{ "arial,serif",
+ "Ubyvqnl Fubc" },
+{ "arial,serif",
+ "Tnqtrg Theh" },
+{ "arial,serif",
+ "Qrcnegzragf " },
+{ "arial,serif",
+ "Fcrpvny Bssref" },
+{ "arial,serif",
+ "Qba'g Zvff gung Fnyr!" },
+{ "arial,serif",
+ "Zl Jrngure" },
+{ "arial,serif",
+ "Ragre lbhe Pvgl be MVC Pbqr:" },
+{ "arial,serif",
+ "Nvecbeg Qrynlf" },
+{ "arial,serif",
+ " Zl Obbxznexf" },
+{ "arial,serif",
+ " i " },
+{ "arial,serif",
+ "Rkpvgr Yvaxf" },
+{ "serif",
+ "18," },
+{ "serif",
+ "jjj.rkpvgr.pbz," },
+{ "serif",
+ "213" },
+{ "serif",
+ "1, 18, jjj.rkpvgr.pbz, 213" },
+{ "arial,serif",
+ "Perngr lbhe Fgneg Cntr!" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Cntr Frggvatf" },
+{ "arial,serif",
+ "Fraq na R-Xvff" },
+{ "arial,serif",
+ "Fvta Hc" },
+{ "arial,serif",
+ "Fvta Va" },
+{ "arial,serif",
+ " Gbqnl Ba Rkpvgr" },
+{ "arial,serif",
+ "12/14 4:35 RG" },
+{ "arial,serif",
+ "Rkpvgr Cerpvfvba Frnepu" },
+{ "arial,serif",
+ " Ubyvqnl Rffragvnyf" },
+{ "Lucida Grande",
+ "trg zl ubebfpbcr" },
+{ "arial,serif",
+ "naq jbex gbtrgure\" ba Fbpvny Frphevgl, Zrqvpner naq " },
+{ "arial,serif",
+ "gnk eryvrs nsgre n qvssvphyg ryrpgvba. \"Bhe angvba zhfg " },
+{ "arial,serif",
+ "Jvagre " },
+{ "arial,serif",
+ "Frperg " },
+{ "arial,serif",
+ "Tvsg Nggnpuzragf" },
+{ "Lucida Grande",
+ "Fhozvg" },
+{ "arial,serif",
+ "Cubgb Fperra Fniref" },
+{ "arial,serif",
+ "Gbc ZC3 Qbjaybnqf" },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ "." },
+{ "Arial,serif",
+ "ubzr" },
+{ "Arial,serif",
+ "syvtugf" },
+{ "Arial,serif",
+ "ubgryf" },
+{ "Arial,serif",
+ "pnef" },
+{ "Arial,serif",
+ "cnpxntrf" },
+{ "Arial,serif",
+ "pehvfrf" },
+{ "Arial,serif",
+ "thvqrf" },
+{ "Arial,serif",
+ "zncf" },
+{ "Arial,serif",
+ "ohfvarff" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ " Frnepu: " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Gevcf" },
+{ "Arial,serif",
+ " | " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cebsvyr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Phfgbzre" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fhccbeg" },
+{ "Arial,serif",
+ "Tb" },
+{ "Arial,serif",
+ "zfa.pbz" },
+{ "Arial,serif",
+ "Oevgvfu" },
+{ "Arial,serif",
+ "Nvejnlf" },
+{ "Arial,serif",
+ "Fabj" },
+{ "Arial,serif",
+ "pbhagel" },
+{ "Arial,serif",
+ "ba" },
+{ "Arial,serif",
+ "Nzrevpna" },
+{ "Arial,serif",
+ "Nveyvarf" },
+{ "Arial,serif",
+ "Jrypbzr" },
+{ "Arial,serif",
+ "gb" },
+{ "Arial,serif",
+ "Rkcrqvn.pbz!" },
+{ "Arial,serif",
+ "Nyernql" },
+{ "Arial,serif",
+ "n" },
+{ "Arial,serif",
+ "zrzore?" },
+{ "Arial,serif",
+ "Fvta va" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Syvtugf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ubgryf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pnef" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cnpxntrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pehvfrf" },
+{ "Arial,serif",
+ "RKCERFF" },
+{ "Arial,serif",
+ "FRNEPU" },
+{ "arial,serif",
+ "Qrcnegvat" },
+{ "arial,serif",
+ "sebz:" },
+{ "arial,serif",
+ "Tbvat" },
+{ "arial,serif",
+ "gb:" },
+{ "arial,serif",
+ "Nqhygf:" },
+{ "arial,serif",
+ " 1 " },
+{ "arial,serif",
+ " 2" },
+{ "arial,serif",
+ " 3" },
+{ "arial,serif",
+ " 4" },
+{ "arial,serif",
+ " 5" },
+{ "arial,serif",
+ " 6" },
+{ "arial,serif",
+ "Jura" },
+{ "arial,serif",
+ "ner" },
+{ "arial,serif",
+ "lbh" },
+{ "arial,serif",
+ "yrnivat?" },
+{ "arial,serif",
+ "Wna" },
+{ "arial,serif",
+ "Sro" },
+{ "arial,serif",
+ "Zne" },
+{ "arial,serif",
+ "Nce" },
+{ "arial,serif",
+ "Znl" },
+{ "arial,serif",
+ "Wha" },
+{ "arial,serif",
+ "Why" },
+{ "arial,serif",
+ "Nht" },
+{ "arial,serif",
+ "Frc" },
+{ "arial,serif",
+ "Bpg" },
+{ "arial,serif",
+ "Abi" },
+{ "arial,serif",
+ "Qrp" },
+{ "arial,serif",
+ "1" },
+{ "arial,serif",
+ "2" },
+{ "arial,serif",
+ "3" },
+{ "arial,serif",
+ "4" },
+{ "arial,serif",
+ "5" },
+{ "arial,serif",
+ "6" },
+{ "arial,serif",
+ "7" },
+{ "arial,serif",
+ "8" },
+{ "arial,serif",
+ "9" },
+{ "arial,serif",
+ "10" },
+{ "arial,serif",
+ "11" },
+{ "arial,serif",
+ "12" },
+{ "arial,serif",
+ "13" },
+{ "arial,serif",
+ "14" },
+{ "arial,serif",
+ "15" },
+{ "arial,serif",
+ "16" },
+{ "arial,serif",
+ "17" },
+{ "arial,serif",
+ "18" },
+{ "arial,serif",
+ "19" },
+{ "arial,serif",
+ "20" },
+{ "arial,serif",
+ "21" },
+{ "arial,serif",
+ "22" },
+{ "arial,serif",
+ "23" },
+{ "arial,serif",
+ "24" },
+{ "arial,serif",
+ "25" },
+{ "arial,serif",
+ "26" },
+{ "arial,serif",
+ "27" },
+{ "arial,serif",
+ "28" },
+{ "arial,serif",
+ "29" },
+{ "arial,serif",
+ "30" },
+{ "arial,serif",
+ "31" },
+{ "arial,serif",
+ "NZ" },
+{ "arial,serif",
+ "Abba" },
+{ "arial,serif",
+ "CZ" },
+{ "arial,serif",
+ "ergheavat?" },
+{ "arial,serif",
+ "Gvc:" },
+{ "arial,serif",
+ "Jr" },
+{ "arial,serif",
+ "unir" },
+{ "arial,serif",
+ "znal" },
+{ "arial,serif",
+ "zber" },
+{ "arial,serif",
+ "syvtug," },
+{ "arial,serif",
+ "ubgry," },
+{ "arial,serif",
+ "naq" },
+{ "arial,serif",
+ "pne" },
+{ "arial,serif",
+ "bcgvbaf." },
+{ "Arial,serif",
+ "JVAGRE" },
+{ "Arial,serif",
+ "GENIRY" },
+{ "Arial,serif",
+ "QRNYF" },
+{ "Arial,serif",
+ "Fxv" },
+{ "Arial,serif",
+ "cnpxntrf:" },
+{ "Arial,serif",
+ "Nve," },
+{ "Arial,serif",
+ "ubgry," },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "pne" },
+{ "Arial,serif",
+ "sbe" },
+{ "Arial,serif",
+ "24" },
+{ "Arial,serif",
+ "gbc" },
+{ "Arial,serif",
+ "erfbegf" },
+{ "Arial,serif",
+ "Unjnvv" },
+{ "Arial,serif",
+ "sner" },
+{ "Arial,serif",
+ "fnyr:" },
+{ "Arial,serif",
+ "Fnir" },
+{ "Arial,serif",
+ "10%" },
+{ "Arial,serif",
+ "ba" },
+{ "Arial,serif",
+ "lbhe" },
+{ "Arial,serif",
+ "arkg" },
+{ "Arial,serif",
+ "syvtug" },
+{ "Arial,serif",
+ "Syl" },
+{ "Arial,serif",
+ "ubzr" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "ubyvqnlf:" },
+{ "Arial,serif",
+ "Snerf" },
+{ "Arial,serif",
+ "fgnegvat" },
+{ "Arial,serif",
+ "ng" },
+{ "Arial,serif",
+ "$141" },
+{ "Arial,serif",
+ "Rkpyhfvir:" },
+{ "Arial,serif",
+ "hc" },
+{ "Arial,serif",
+ "gb" },
+{ "Arial,serif",
+ "$200" },
+{ "Arial,serif",
+ "pehvfr" },
+{ "Arial,serif",
+ "Fgnl" },
+{ "Arial,serif",
+ "n" },
+{ "Arial,serif",
+ "3-fgne" },
+{ "Arial,serif",
+ "ubgry" },
+{ "Arial,serif",
+ "va" },
+{ "Arial,serif",
+ "Arj" },
+{ "Arial,serif",
+ "Lbex" },
+{ "Arial,serif",
+ "Pvgl" },
+{ "Arial,serif",
+ "sebz" },
+{ "Arial,serif",
+ "$125" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "GBQNL'F" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "QRNYF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rkcrqvn" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Geniryf:" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "serr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Npnchypb'f" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Enqvffba:" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "$79" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Invy" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "sner" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "fnyr:" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fnir" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "$25" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zber" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "fcrpvny" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "qrnyf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "GENIRY" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "CEBIVQREF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ybqtvat" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "&" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Erfbegf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Snzvyl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Inpngvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Grr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Gvzrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Trne" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nqiragher" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Inpngvba" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pehvfr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cebivqref" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "RKCRQVN" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FREIVPRF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nffbpvngrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cebtenz" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zbovyr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Freivprf" },
+{ "Arial,serif",
+ "Frnepu" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "PHFGBZRE" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FHCCBEG" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cevinpl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "naq" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "frphevgl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Perqvg" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pneq" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Thnenagrr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ntragf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ba" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "pnyy" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "24" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "uef." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Arj" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "gb" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "gur" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "fvgr?" },
+{ "Arial,serif",
+ "GENIRYRE" },
+{ "Arial,serif",
+ "GBBYF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Syvtug" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fgnghf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nvecbeg" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Vasbezngvba" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Urnygu" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fnsrgl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pheerapl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pbairegre" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zvyrntr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Genpxre" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qevivat" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qverpgvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Jrngure" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Geniry" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "R-Pneqf" },
+{ "Arial,serif",
+ "CEVPR" },
+{ "Arial,serif",
+ "ZNGPURE" },
+{ "Arial,serif",
+ "Fnir" },
+{ "Arial,serif",
+ "5%," },
+{ "Arial,serif",
+ "10%," },
+{ "Arial,serif",
+ "15%," },
+{ "Arial,serif",
+ "be" },
+{ "Arial,serif",
+ "zber" },
+{ "Arial,serif",
+ "ubgry" },
+{ "Arial,serif",
+ "ebbzf" },
+{ "Arial,serif",
+ "jvgu" },
+{ "Arial,serif",
+ "bhe" },
+{ "Arial,serif",
+ "Cevpr" },
+{ "Arial,serif",
+ "Zngpure" },
+{ "Arial,serif",
+ "freivprf." },
+{ "Arial,serif",
+ "Gryy" },
+{ "Arial,serif",
+ "hf" },
+{ "Arial,serif",
+ "lbhe" },
+{ "Arial,serif",
+ "cevpr," },
+{ "Arial,serif",
+ "qrfgvangvba," },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "geniry" },
+{ "Arial,serif",
+ "qngrf." },
+{ "Arial,serif",
+ "Jr'yy" },
+{ "Arial,serif",
+ "yrg" },
+{ "Arial,serif",
+ "lbh" },
+{ "Arial,serif",
+ "xabj" },
+{ "Arial,serif",
+ "vzzrqvngryl" },
+{ "Arial,serif",
+ "vs" },
+{ "Arial,serif",
+ "jr" },
+{ "Arial,serif",
+ "fhpprrqrq" },
+{ "Arial,serif",
+ "znxr" },
+{ "Arial,serif",
+ "abaershaqnoyr" },
+{ "Arial,serif",
+ "erfreingvbaf" },
+{ "Arial,serif",
+ "sbe" },
+{ "Arial,serif",
+ "lbh!" },
+{ "Arial,serif",
+ "ARJF" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "NQIVPR" },
+{ "Arial,serif",
+ "Ubyvqnl" },
+{ "Arial,serif",
+ "Geniry" },
+{ "Arial,serif",
+ "Pragre:" },
+{ "Arial,serif",
+ "Gvcf" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "qrnyf" },
+{ "Arial,serif",
+ "sbe" },
+{ "Arial,serif",
+ "ubyvqnl" },
+{ "Arial,serif",
+ "geniry" },
+{ "Arial,serif",
+ "Rkcrqvn" },
+{ "Arial,serif",
+ "Enqvb:" },
+{ "Arial,serif",
+ "Npgbe" },
+{ "Arial,serif",
+ "Xryfrl" },
+{ "Arial,serif",
+ "Tenzzre" },
+{ "Arial,serif",
+ "ba" },
+{ "Arial,serif",
+ "fnvyvat" },
+{ "Arial,serif",
+ "Arjf:" },
+{ "Arial,serif",
+ "Tb" },
+{ "Arial,serif",
+ "onpxfgntr" },
+{ "Arial,serif",
+ "ng" },
+{ "Arial,serif",
+ "Ybaqba'f" },
+{ "Arial,serif",
+ "byqrfg" },
+{ "Arial,serif",
+ "gurngre" },
+{ "Arial,serif",
+ "Pehvfr" },
+{ "Arial,serif",
+ "Nqivfbe:" },
+{ "Arial,serif",
+ "Jung'f" },
+{ "Arial,serif",
+ "arj" },
+{ "Arial,serif",
+ "baobneq" },
+{ "Arial,serif",
+ "2001" },
+{ "Arial,serif",
+ "Ohfvarff" },
+{ "Arial,serif",
+ "Geniry:" },
+{ "Arial,serif",
+ "Gnxr" },
+{ "Arial,serif",
+ "genvaf" },
+{ "Arial,serif",
+ "gb" },
+{ "Arial,serif",
+ "cynarf" },
+{ "Arial,serif",
+ "nebhaq" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "H.F." },
+{ "Arial,serif",
+ "QRFGVANGVBAF" },
+{ "Arial,serif",
+ "VAGRERFGF" },
+{ "Arial,serif",
+ "Fxv" },
+{ "Arial,serif",
+ "Zber" },
+{ "Arial,serif",
+ "guna" },
+{ "Arial,serif",
+ "80" },
+{ "Arial,serif",
+ "fxv" },
+{ "Arial,serif",
+ "qrfgvangvbaf" },
+{ "Arial,serif",
+ "\xc2""\xb7""" },
+{ "Arial,serif",
+ "Oevgvfu" },
+{ "Arial,serif",
+ "Pbyhzovn:" },
+{ "Arial,serif",
+ "Fxvvat" },
+{ "Arial,serif",
+ "vf" },
+{ "Arial,serif",
+ "oryvrivat" },
+{ "Arial,serif",
+ "Ivqrb" },
+{ "Arial,serif",
+ "gbhef:" },
+{ "Arial,serif",
+ "Fna" },
+{ "Arial,serif",
+ "Senapvfpb" },
+{ "Arial,serif",
+ "Gurer'f" },
+{ "Arial,serif",
+ "nyjnlf" },
+{ "Arial,serif",
+ "tbbq" },
+{ "Arial,serif",
+ "rkphfr" },
+{ "Arial,serif",
+ "trggvat" },
+{ "Arial,serif",
+ "njnl" },
+{ "Arial,serif",
+ "ol" },
+{ "Arial,serif",
+ "Onl." },
+{ "Arial,serif",
+ "Trg" },
+{ "Arial,serif",
+ "uvc" },
+{ "Arial,serif",
+ "jung" },
+{ "Arial,serif",
+ "frr" },
+{ "Arial,serif",
+ "jvgu" },
+{ "Arial,serif",
+ "bhe" },
+{ "Arial,serif",
+ "ivqrb" },
+{ "Arial,serif",
+ "gbhe." },
+{ "Arial,serif",
+ "Snzvyl" },
+{ "Arial,serif",
+ "Fcbafberq" },
+{ "Arial,serif",
+ "ol" },
+{ "Arial,serif",
+ "Qvfarl" },
+{ "Arial,serif",
+ "Inpngvbaf" },
+{ "Arial,serif",
+ "Fgbelobbx" },
+{ "Arial,serif",
+ "zngu" },
+{ "Arial,serif",
+ "Obfgba" },
+{ "Arial,serif",
+ "Zbhag" },
+{ "Arial,serif",
+ "Ireaba" },
+{ "Arial,serif",
+ "ubfgf" },
+{ "Arial,serif",
+ "qnl" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "erzrzoenapr" },
+{ "Arial,serif",
+ "Srngherq" },
+{ "Arial,serif",
+ "Qrfgvangvba:" },
+{ "Arial,serif",
+ "Beynaqb" },
+{ "Arial,serif",
+ "Qnmmyvat" },
+{ "Arial,serif",
+ "qrpbengvbaf" },
+{ "Arial,serif",
+ "Tevapuznf:" },
+{ "Arial,serif",
+ "Gurzr" },
+{ "Arial,serif",
+ "cnexf" },
+{ "Arial,serif",
+ "pryroengr" },
+{ "Arial,serif",
+ "frnfba." },
+{ "Arial,serif",
+ "Nqiragher" },
+{ "Arial,serif",
+ "Njnl.pbz" },
+{ "Arial,serif",
+ "10,000" },
+{ "Arial,serif",
+ "npgvir" },
+{ "Arial,serif",
+ "inpngvbaf" },
+{ "Arial,serif",
+ "jbeyqjvqr" },
+{ "Arial,serif",
+ "Rkcreg" },
+{ "Arial,serif",
+ "uryc" },
+{ "Arial,serif",
+ "pubbfvat" },
+{ "Arial,serif",
+ "cynaavat" },
+{ "Arial,serif",
+ "Urnygu" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "Fnsrgl" },
+{ "Arial,serif",
+ "ZrqvpvarCynarg" },
+{ "Arial,serif",
+ "Nygvghqr" },
+{ "Arial,serif",
+ "vyyarff" },
+{ "Arial,serif",
+ "Znynevn" },
+{ "Arial,serif",
+ "ceriragvba" },
+{ "Arial,serif",
+ "Geniry" },
+{ "Arial,serif",
+ "vafhenapr" },
+{ "Arial,serif",
+ "CEBIVQREF" },
+{ "Arial,serif",
+ "Geniryfpncr" },
+{ "Arial,serif",
+ "Qvfpbhag" },
+{ "Arial,serif",
+ "Ubgryf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ubgryf:" },
+{ "Arial,serif",
+ "Svaq" },
+{ "Arial,serif",
+ "snagnfgvp" },
+{ "Arial,serif",
+ "qrnyf" },
+{ "Arial,serif",
+ "gb" },
+{ "Arial,serif",
+ "240" },
+{ "Arial,serif",
+ "qrfgvangvbaf." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Geniryfpncr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qvfpbhag" },
+{ "Arial,serif",
+ "Obbx" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "zntvp" },
+{ "Arial,serif",
+ "gbqnl" },
+{ "Arial,serif",
+ "Qvfarl" },
+{ "Arial,serif",
+ "Geniry." },
+{ "Arial,serif",
+ "Inpngvbaf:" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ornpurf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qvfarl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Njnl.pbz" },
+{ "arial,serif",
+ "Havgrq" },
+{ "arial,serif",
+ "Xvatqbz" },
+{ "arial,serif",
+ "Treznal" },
+{ "arial,serif",
+ "Pnanqn" },
+{ "arial,serif",
+ "Bgure" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ABA-H.F." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FVGRF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FVGR" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "THVQR" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Erjneqf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Syvtugf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cevpr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zngpure" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Jvmneq" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Sner" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pnyraqne" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Neevinyf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qrcnegherf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ubgryf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ubgry" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fcrpvnygl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pnef" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pne" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cnpxntrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fcrpvny" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qrnyf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pehvfrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Yvarf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qrfgvangvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "&" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Vagrerfgf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Qrfgvangvba" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Thvqrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Tbys" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fxv" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Arjf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nqivpr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ol" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rq" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Crexvaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pbzzhavgl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Enqvb" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rkcrqvn" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Geniryf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zntnmvar" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zncf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Svaq" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "n" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Znc" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Trg" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nobhg" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rkcrqvn," },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Vap." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Wbof" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Vairfgbe" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Eryngvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nqiregvfr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Jvgu" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Hf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cerff" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pbagnpgvat" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rkcrqvn," },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Vap." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fjrrcfgnxrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "jvaaref" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ubzr" },
+{ "Arial,serif",
+ "|" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "syvtugf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ubgryf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "pnef" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "cnpxntrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "pehvfrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "qrfgvangvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "vagrerfgf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "zncf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ohfvarff" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "gbbyf" },
+{ "Arial,Helvetica,serif",
+ "\xc2""\xa9""2000" },
+{ "Arial,Helvetica,serif",
+ "Rkcrqvn," },
+{ "Arial,Helvetica,serif",
+ "Vap." },
+{ "Arial,Helvetica,serif",
+ "Nyy" },
+{ "Arial,Helvetica,serif",
+ "evtugf" },
+{ "Arial,Helvetica,serif",
+ "erfreirq." },
+{ "Arial,Helvetica,serif",
+ "Rkcrqvn.pbz" },
+{ "Arial,Helvetica,serif",
+ "grezf" },
+{ "Arial,Helvetica,serif",
+ "bs" },
+{ "Arial,Helvetica,serif",
+ "hfr." },
+{ "Arial,Helvetica,serif",
+ "Cevinpl" },
+{ "Arial,Helvetica,serif",
+ "cbyvpl." },
+{ "Arial,Helvetica,serif",
+ "Rkcrqvn," },
+{ "Arial,Helvetica,serif",
+ "Vap.," },
+{ "Arial,Helvetica,serif",
+ "abg" },
+{ "Arial,Helvetica,serif",
+ "erfcbafvoyr" },
+{ "Arial,Helvetica,serif",
+ "sbe" },
+{ "Arial,Helvetica,serif",
+ "pbagrag" },
+{ "Arial,Helvetica,serif",
+ "ba" },
+{ "Arial,Helvetica,serif",
+ "rkgreany" },
+{ "Arial,Helvetica,serif",
+ "Jro" },
+{ "Arial,Helvetica,serif",
+ "fvgrf." },
+{ "Arial,Helvetica,serif",
+ "Cubgb:" },
+{ "Arial,Helvetica,serif",
+ "Pbclevtug" },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "Gbqq" },
+{ "Arial,Helvetica,serif",
+ "Cbjryy" },
+{ "Arial,Helvetica,serif",
+ "&" },
+{ "Arial,Helvetica,serif",
+ "Invy" },
+{ "Arial,Helvetica,serif",
+ "Erfbegf," },
+{ "Arial,Helvetica,serif",
+ "Vap." },
+{ "arial,serif",
+ "Qrcnegvat sebz:" },
+{ "arial,serif",
+ "Tbvat gb:" },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ " " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ " Frnepu: " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zl Gevcf" },
+{ "Arial,serif",
+ " | " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zl Cebsvyr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Phfgbzre Fhccbeg" },
+{ "Arial,serif",
+ "Jrypbzr gb Rkcrqvn.pbz!" },
+{ "Arial,serif",
+ " Nyernql n zrzore? " },
+{ "Arial,serif",
+ "Fvta va" },
+{ "Arial,serif",
+ "R" },
+{ "Arial,serif",
+ "K" },
+{ "Arial,serif",
+ "C" },
+{ "Arial,serif",
+ "E" },
+{ "Arial,serif",
+ "F" },
+{ "Arial,serif",
+ "N" },
+{ "Arial,serif",
+ "P" },
+{ "Arial,serif",
+ "U" },
+{ "Arial,serif",
+ "RKCERFF FRNEPU" },
+{ "arial,serif",
+ " 1 " },
+{ "arial,serif",
+ "Jura ner lbh yrnivat?" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Jura ner lbh ergheavat?" },
+{ "arial,serif",
+ " Jr unir znal zber " },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ " naq " },
+{ "arial,serif",
+ " bcgvbaf." },
+{ "Arial,serif",
+ "J" },
+{ "Arial,serif",
+ "V" },
+{ "Arial,serif",
+ "A" },
+{ "Arial,serif",
+ "G" },
+{ "Arial,serif",
+ "I" },
+{ "Arial,serif",
+ "Y" },
+{ "Arial,serif",
+ "Q" },
+{ "Arial,serif",
+ "JVAGRE GENIRY QRNYF" },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ "Fxv cnpxntrf: Nve, ubgry, naq pne sbe 24 gbc erfbegf" },
+{ "Arial,serif",
+ "Unjnvv sner fnyr: Fnir 10% ba lbhe arkg syvtug" },
+{ "Arial,serif",
+ "Syl ubzr sbe gur ubyvqnlf: Snerf fgnegvat ng $141" },
+{ "Arial,serif",
+ "Rkpyhfvir: Fnir hc gb $200 ba lbhe arkg pehvfr" },
+{ "Arial,serif",
+ "Fgnl ng n 3-fgne ubgry va Arj Lbex Pvgl sebz $125" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "G" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "B" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Q" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "N" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "L" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "'" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "F" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "R" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Y" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "GBQNL'F QRNYF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Rkcrqvn Geniryf: " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Npnchypb'f Enqvffba: " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Invy sner fnyr: " },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Fnir $25" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zber fcrpvny qrnyf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "E" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "I" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "C" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "V" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "GENIRY CEBIVQREF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ybqtvat & Erfbegf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Snzvyl Inpngvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Grr Gvzrf & Trne" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nqiragher Inpngvbaf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Inpngvba Cnpxntrf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Pehvfr Cebivqref" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "K" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "P" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "RKCRQVN FREIVPRF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nffbpvngrf Cebtenz" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zbovyr Freivprf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "H" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Z" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "PHFGBZRE" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FHCCBEG" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Cevinpl naq frphevgl" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Perqvg Pneq Thnenagrr" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Ntragf ba pnyy 24 uef." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Arj gb gur fvgr?" },
+{ "Arial,serif",
+ "G" },
+{ "Arial,serif",
+ "E" },
+{ "Arial,serif",
+ "N" },
+{ "Arial,serif",
+ "I" },
+{ "Arial,serif",
+ "R" },
+{ "Arial,serif",
+ "Y" },
+{ "Arial,serif",
+ "B" },
+{ "Arial,serif",
+ "F" },
+{ "Arial,serif",
+ "GENIRYRE GBBYF" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Syvtug Fgnghf" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Nvecbeg Vasbezngvba" },
+{ "Arial,serif",
+ "Z" },
+{ "Arial,serif",
+ "CEVPR ZNGPURE" },
+{ "Arial,serif",
+ "Fnir 5%, 10%, 15%, be zber ba syvtugf" },
+{ "Arial,serif",
+ " be " },
+{ "Arial,serif",
+ "ubgry ebbzf" },
+{ "Arial,serif",
+ " jvgu bhe Cevpr Zngpure " },
+{ "Arial,serif",
+ "freivprf. Gryy hf lbhe cevpr, qrfgvangvba, naq geniry qngrf. Jr'yy yrg lbh xabj " },
+{ "Arial,serif",
+ "vzzrqvngryl vs jr fhpprrqrq naq znxr abaershaqnoyr erfreingvbaf sbe lbh!" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "ARJF & NQIVPR" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "A" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "-" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "." },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "ABA-H.F. FVGRF" },
+{ "arial,serif",
+ "Havgrq Xvatqbz" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "T" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "FVGR THVQR" },
+{ "Arial,Helvetica,Sans Serif,serif",
+ "Zl Erjneqf" },
+{ "serif",
+ "19," },
+{ "serif",
+ "jjj.rkcrqvn.pbz," },
+{ "serif",
+ "602" },
+{ "serif",
+ "1, 19, jjj.rkcrqvn.pbz, 602" },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "1,326,920,000" },
+{ "arial,sans-serif,serif",
+ "jro" },
+{ "arial,sans-serif,serif",
+ "cntrf" },
+{ "Lucida Grande",
+ "Tbbtyr Frnepu" },
+{ "Lucida Grande",
+ "V'z Srryvat Yhpxl" },
+{ "arial,sans-serif,serif",
+ "Nqinaprq Frnepu" },
+{ "arial,sans-serif,serif",
+ "Cersreraprf" },
+{ "arial,sans-serif,serif",
+ "Tbbtyr" },
+{ "arial,sans-serif,serif",
+ "Jro" },
+{ "arial,sans-serif,serif",
+ "Qverpgbel" },
+{ "arial,sans-serif,serif",
+ "gur" },
+{ "arial,sans-serif,serif",
+ "jro" },
+{ "arial,sans-serif,serif",
+ "betnavmrq" },
+{ "arial,sans-serif,serif",
+ "ol" },
+{ "arial,sans-serif,serif",
+ "gbcvp" },
+{ "arial,sans-serif,serif",
+ "Unir" },
+{ "arial,sans-serif,serif",
+ "n" },
+{ "arial,sans-serif,serif",
+ "perqvg" },
+{ "arial,sans-serif,serif",
+ "pneq" },
+{ "arial,sans-serif,serif",
+ "naq" },
+{ "arial,sans-serif,serif",
+ "5" },
+{ "arial,sans-serif,serif",
+ "zvahgrf?" },
+{ "arial,sans-serif,serif",
+ "Trg" },
+{ "arial,sans-serif,serif",
+ "lbhe" },
+{ "arial,sans-serif,serif",
+ "nq" },
+{ "arial,sans-serif,serif",
+ "ba" },
+{ "arial,sans-serif,serif",
+ "Tbbtyr" },
+{ "arial,sans-serif,serif",
+ "gbqnl" },
+{ "arial,sans-serif,serif",
+ "." },
+{ "arial,sans-serif,serif",
+ "Pbby" },
+{ "arial,sans-serif,serif",
+ "Wbof" },
+{ "arial,sans-serif,serif",
+ "-" },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr" },
+{ "arial,sans-serif,serif",
+ "jvgu" },
+{ "arial,sans-serif,serif",
+ "Hf" },
+{ "arial,sans-serif,serif",
+ "va" },
+{ "arial,sans-serif,serif",
+ "Ynathntr" },
+{ "arial,sans-serif,serif",
+ "Nyy Nobhg Tbbtyr" },
+{ "arial,sans-serif,serif",
+ "\xc2""\xa9""2000" },
+{ "arial,sans-serif,serif",
+ "Tbbtyr" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "serif",
+ "1," },
+{ "serif",
+ "20," },
+{ "serif",
+ "jjj.tbbtyr.pbz," },
+{ "serif",
+ "1049" },
+{ "serif",
+ "1, 20, jjj.tbbtyr.pbz, 1049" },
+{ "arial,sans-serif,serif",
+ "Frnepu 1,326,920,000 jro cntrf" },
+{ "Lucida Grande",
+ "Tbbtyr Frnepu" },
+{ "Lucida Grande",
+ "V'z Srryvat Yhpxl" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Nqinaprq Frnepu" },
+{ "arial,sans-serif,serif",
+ "Tbbtyr Jro Qverpgbel" },
+{ "arial,sans-serif,serif",
+ "gur jro betnavmrq ol gbcvp" },
+{ "arial,sans-serif,serif",
+ "Unir n perqvg pneq naq 5 zvahgrf? " },
+{ "arial,sans-serif,serif",
+ "Trg lbhe nq ba Tbbtyr gbqnl" },
+{ "arial,sans-serif,serif",
+ "Pbby Wbof" },
+{ "arial,sans-serif,serif",
+ " - " },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr jvgu Hf" },
+{ "arial,sans-serif,serif",
+ "Tbbtyr va lbhe Ynathntr" },
+{ "arial,sans-serif,serif",
+ "Nyy Nobhg Tbbtyr" },
+{ "arial,sans-serif,serif",
+ "\xc2""\xa9""2000 Tbbtyr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Freivprf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "R-Pbzzrepr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fbyhgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "hairvyf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vgf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Freivprf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cyngsbez." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Gur" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "arj" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "tenagf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ohfvarffrf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "zber" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "syrkvovyvgl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "va" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ubj" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "gurl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ohvyq," },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "qrcybl," },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "naq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "irevsl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "genafnpgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "sbe" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "n" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "oebnq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "fcrpgehz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "bs" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg-onfrq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "freivprf." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ernq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vs" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "lbh'er" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "jbaqrevat" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "\"Jub" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg?\"," },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "jurer" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vg'f" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "tbvat," },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "jul" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "lbh" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "fubhyq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "pner," },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vasbezngvba" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Jrrx" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "zntnmvar'f" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nhthfg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "2000" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "negvpyr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "fhzf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "hc" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "avpryl." },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "Fhofpevor" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "gb" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "vCynarg" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "Arjf" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "Freivprf" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Phfgbzre" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Cebsvyr:" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Svefg" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Havba" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Pbecbengvba" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ovyy" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cerfragzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cnlzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "gur" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "21fg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Praghel" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "vCynarg" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Urnqyvarf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Hairvyf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vaqhfgel'f" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Svefg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Shyy-Hc" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "O2O" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "OCNL\xc2""\xae""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fryrpgf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "R-Pbzzrepr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fbyhgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "sbe" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vgf" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ovyy" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cerfragzrag" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cnlzrag" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Flfgrz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Erdhvfvgr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Grpuabybtl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Grnz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "gb" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qryvire" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Frnzyrff" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Npprff" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pngnybt" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qngn" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg\xe2""\x84""\xa2""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "naq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Jvmarg\xe2""\x84""\xa2""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Grnz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "gb" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qryvire" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzcerurafvir" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbagrag" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fbyhgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Arg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znexrg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znxref" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cebpherzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Phfgbzref" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "PAA.pbz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nppbzzbqngrf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Erpbeq-Oernxvat" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Bayvar" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Genssvp" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ba" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ryrpgvba" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Avtug" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Hfvat" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg\xe2""\x84""\xa2""" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Grpuabybtl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fha" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Zvpebflfgrzf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Naabhapr" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fha'f" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Npdhvfvgvba" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "tencrIVAR" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Grpuabybtvrf" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Gur" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "abj" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "bssrevat" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "zbfg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "pbzcerurafvir" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "orfg-bs-oerrq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ninvynoyr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "gbqnl." },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Bhe" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Pbzzvgzrag" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "gb" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Fgnaqneqf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znal" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "cebqhpgf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ner" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "pbzcyvnag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "jvgu" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vaqhfgel" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "fgnaqneqf." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "YQNC " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Wnin\xe2""\x84""\xa2""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "VZNC" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "\xe2""\x80""\xa2""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cynarg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qverpgbel/Npprff" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ebhgre" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qverpgbel" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nccyvpngvba" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Zrffntvat" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "KZY" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "JNC" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "VPC" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "RPKcreg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "GenqvatKcreg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Jveryrff" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Argfpncr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cebkl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "RQV/RQVAG" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "K.509" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vPNY" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pregvsvpngr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znantrzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Flfgrz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pnyraqne" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagreangvbany" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "|" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fvgr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znc" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Srrqonpx" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cebqhpgf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fbyhgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fhccbeg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Freivprf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qbjaybnq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Qrirybcref" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nobhg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Hf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cerff" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ebbz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Rzcyblzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "\xc2""\xa9""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "2000" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fha-Argfpncr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nyyvnapr." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Nyy" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Evtugf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Erfreirq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cevinpl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cbyvpl" },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Freivprf " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg R-Pbzzrepr " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fbyhgvbaf hairvyf vgf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr Freivprf " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cyngsbez. Gur arj" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "cyngsbez tenagf " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ohfvarffrf zber " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "syrkvovyvgl va ubj gurl" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ohvyq, qrcybl, naq irevsl " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "genafnpgvbaf sbe n oebnq " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "fcrpgehz bs" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg-onfrq freivprf." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "ernq zber" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vs lbh'er jbaqrevat \"Jub" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vf vCynarg?\", jurer vg'f " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "tbvat, naq jul lbh fubhyq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "pner, " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vasbezngvba Jrrx" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "zntnmvar'f Nhthfg 2000 " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ " fhzf vg hc avpryl." },
+{ "Arial,Helvetica,SunSans,sans-serif,serif",
+ "Fhofpevor gb vCynarg Arjf Freivprf" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Phfgbzre Cebsvyr:" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Svefg Havba " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Ovyy Cerfragzrag naq Cnlzrag" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "sbe gur 21fg Praghel" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Urnqyvarf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Hairvyf Vaqhfgel'f Svefg Shyy-Hc " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "O2O " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzzrepr Cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "OCNL\xc2""\xae"" Fryrpgf vCynarg R-Pbzzrepr Fbyhgvbaf sbe" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vgf " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg Ovyy Cerfragzrag naq " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cnlzrag Flfgrz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg naq Erdhvfvgr Grpuabybtl Grnz gb Qryvire" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Frnzyrff Npprff gb Pngnybt Qngn" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg\xe2""\x84""\xa2"" naq Jvmarg\xe2""\x84""\xa2"" Grnz gb Qryvire" },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Pbzcerurafvir Pbagrag Fbyhgvbaf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ " gb Arg " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znexrg Znxref naq Cebpherzrag Phfgbzref" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "PAA.pbz Nppbzzbqngrf Erpbeq-Oernxvat Bayvar " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Genssvp ba Ryrpgvba Avtug " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Hfvat vCynarg\xe2""\x84""\xa2""" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg\xe2""\x84""\xa2"" naq Fha Zvpebflfgrzf Naabhapr " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Fha'f " },
+{ "Verdana,Arial,Helvetica,sunsans,sans-serif,serif",
+ "Npdhvfvgvba bs tencrIVAR Grpuabybtvrf" },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Gur vCynarg Cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg abj bssrevat gur zbfg pbzcerurafvir naq" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "orfg-bs-oerrq " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Vagrearg cyngsbez" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ " ninvynoyr gbqnl." },
+{ "Helvetica,sunsans,sans-serif,serif",
+ "Bhe Pbzzvgzrag gb Fgnaqneqf" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znal vCynarg cebqhpgf ner pbzcyvnag jvgu vaqhfgel fgnaqneqf." },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "YQNC " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Cynarg Qverpgbel/Npprff" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Qverpgbel Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Nccyvpngvba Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Zrffntvat Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg RPKcreg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg GenqvatKcreg" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Jveryrff Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Argfpncr Cebkl Freire" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Pregvsvpngr" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "Znantrzrag Flfgrz" },
+{ "Arial,Helvetica,sunsans,sans-serif,serif",
+ "vCynarg Pnyraqne Freire" },
+{ "serif",
+ "21," },
+{ "serif",
+ "jjj.vcynarg.pbz," },
+{ "serif",
+ "477" },
+{ "serif",
+ "1, 21, jjj.vcynarg.pbz, 477" },
+{ "Arial,Helvetica,serif",
+ "JJJ.ZNCDHRFG.PBZ" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Abegu" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Nzrevpna" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "&" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Rhebcrna" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Ohfvarff" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Fbyhgvbaf:" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Nqq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "zncf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "qverpgvbaf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "lbhe" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "pbzcnal'f" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Jro" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "fvgr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fgngr/Cebi:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "MVC/Cbfgny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbqr:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbhagel:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pnanqn" },
+{ "Verdana,Arial,Helvetica,serif",
+ "H.F." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pvgl:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "SEBZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nqqerff" },
+{ "Verdana,Arial,Helvetica,serif",
+ "be" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagrefrpgvba:" },
+{ "Lucida Grande",
+ "Trg Qverpgvbaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "GB" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yvir" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Genssvp" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ercbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Genssvp" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pnzf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nppvqragf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ol" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pvgl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Lryybj" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "&" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Juvgr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cntrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Svaq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ohfvarffrf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Crbcyr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Geniry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Thvqr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Engrq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Erfgnhenagf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ubgryf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Geniry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qrnyf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qevivat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qverpgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "HF" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "&" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pnanqn," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Rhebcr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pvgl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arjf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Orfg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pvgl," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zncf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgerrg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zncf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nvecbegf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jbeyq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ngynf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebnq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ngynf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Wbva" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zrzore" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ybtva" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZlZncDhrfg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arj" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZncDhrfg!" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gbcbtencuvp" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zncf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jveryrff" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Freivprf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZncDhrfg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebnq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ngynf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fvgr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vaqrk" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nqiregvfvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pbecbengr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cnegaref" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Uryc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pragre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZncFgber" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cevinpl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cbyvpl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " & " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Grezf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pbaqvgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "\xc2""\xa9""1999-2000" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZncDhrfg.pbz," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vap." },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nyy" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "evtugf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "erfreirq." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nqqerff be Vagrefrpgvba:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "MVC/Cbfgny Pbqr:" },
+{ "serif",
+ "22," },
+{ "serif",
+ "jjj.zncdhrfg.pbz," },
+{ "serif",
+ "121" },
+{ "serif",
+ "1, 22, jjj.zncdhrfg.pbz, 121" },
+{ "Arial,Helvetica,serif",
+ "J" },
+{ "Arial,Helvetica,serif",
+ "." },
+{ "Arial,Helvetica,serif",
+ "Z" },
+{ "Arial,Helvetica,serif",
+ "N" },
+{ "Arial,Helvetica,serif",
+ "C" },
+{ "Arial,Helvetica,serif",
+ "D" },
+{ "Arial,Helvetica,serif",
+ "H" },
+{ "Arial,Helvetica,serif",
+ "R" },
+{ "Arial,Helvetica,serif",
+ "F" },
+{ "Arial,Helvetica,serif",
+ "G" },
+{ "Arial,Helvetica,serif",
+ "P" },
+{ "Arial,Helvetica,serif",
+ "B" },
+{ "Arial,Helvetica,serif",
+ "JJJ.ZNCDHRFG.PBZ" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Abegu Nzrevpna & Rhebcrna Ohfvarff Fbyhgvbaf: " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Nqq zncf naq qverpgvbaf gb lbhe pbzcnal'f Jro fvgr." },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Lucida Grande",
+ "Trg Qverpgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgerrg Zncf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jbeyq Ngynf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ebnq Ngynf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qevivat Qverpgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "HF & Pnanqn," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yvir Genssvp Ercbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Genssvp Pnzf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nppvqragf ol Pvgl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Lryybj & Juvgr Cntrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Svaq Ohfvarffrf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Svaq Crbcyr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Geniry Thvqr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Engrq Erfgnhenagf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Engrq Ubgryf," },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Geniry " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pvgl Thvqr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Orfg bs gur Pvgl," },
+{ "Arial,Helvetica,sans-serif,serif",
+ " ZlZncDhrfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zrzore Ybtva" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arj ba ZncDhrfg!" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gbcbtencuvp Zncf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jveryrff Freivprf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ZncDhrfg Ebnq Ngynf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fvgr Vaqrk" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Uryc Pragre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cevinpl Cbyvpl" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " & " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Grezf naq Pbaqvgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "\xc2""\xa9""1999-2000 ZncDhrfg.pbz, Vap. Nyy evtugf erfreirq." },
+{ "Verdana, Arial,serif",
+ "Nyy" },
+{ "Verdana, Arial,serif",
+ "Cebqhpgf" },
+{ "Verdana, Arial,serif",
+ "|" },
+{ "Verdana, Arial,serif",
+ "Fhccbeg" },
+{ "Verdana, Arial,serif",
+ "Frnepu" },
+{ "Verdana, Arial,serif",
+ "zvpebfbsg.pbz" },
+{ "Verdana, Arial,serif",
+ "Ubzr" },
+{ "Verdana, Arial,serif",
+ "Riragf/Genvavat" },
+{ "Verdana, Arial,serif",
+ "Fhofpevor" },
+{ "Verdana, Arial,serif",
+ "Nobhg" },
+{ "Verdana, Arial,serif",
+ "Zvpebfbsg" },
+{ "Verdana, Arial,serif",
+ "HF/Jbeyqjvqr" },
+{ "Verdana, Arial,serif",
+ "Qbjaybnqf" },
+{ "Verdana, Arial,serif",
+ "Pbagnpg" },
+{ "Verdana, Arial,serif",
+ "Hf" },
+{ "Verdana, Arial,serif",
+ "ZFA.pbz" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Frnepu" },
+{ "Lucida Grande",
+ "TB" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cebqhpg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Snzvyl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fvgrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jvaqbjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bssvpr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Freiref" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrirybcre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gbbyf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Freivprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "rFreivprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hcqngr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ZFA" },
+{ "Verdana, Arial, Helvetica,serif",
+ "oPrageny" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Phfgbzre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ubzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "&" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Crefbany" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ohfvarff" },
+{ "Verdana, Arial, Helvetica,serif",
+ "VG" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cebsrffvbany" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cnegare/Erfryyre" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rqhpngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Erfbheprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fubc" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cerff" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Obbxf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arjfyrggref" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Wbof" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cevinpl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Fgngrzrag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "QBW" },
+{ "Verdana, Arial, Helvetica,serif",
+ "if." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Vaabingvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Trg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rkpunatr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "2000" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Rinyhngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "naq" },
+{ "Verdana, Arial, Helvetica,serif",
+ "grfg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "qevir" },
+{ "Verdana, Arial, Helvetica,serif",
+ "naljurer," },
+{ "Verdana, Arial, Helvetica,serif",
+ "nalgvzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pbzzhavpngvba." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg" },
+{ "Verdana, Arial, Helvetica,serif",
+ ".ARG:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "erfbheprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "sbe" },
+{ "Verdana, Arial, Helvetica,serif",
+ "qrirybcref," },
+{ "Verdana, Arial, Helvetica,serif",
+ "cebf," },
+{ "Verdana, Arial, Helvetica,serif",
+ "ohfvarff." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jvaqbjf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cebsrffvbany," },
+{ "Verdana, Arial, Helvetica,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica,serif",
+ "zbfg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "eryvnoyr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "rire." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Frr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ubj" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bssvpr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ng" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ubzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pna" },
+{ "Verdana, Arial, Helvetica,serif",
+ "cnl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "qvivqraqf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "va" },
+{ "Verdana, Arial, Helvetica,serif",
+ "fpubby" },
+{ "Verdana, Arial, Helvetica,serif",
+ "ng" },
+{ "Verdana, Arial, Helvetica,serif",
+ "jbex." },
+{ "verdana,arial,helvetica,serif",
+ "Gbqnl'f" },
+{ "verdana,arial,helvetica,serif",
+ "Arjf" },
+{ "verdana,arial,helvetica,serif",
+ "Jvaqbjf" },
+{ "verdana,arial,helvetica,serif",
+ "Zrqvn" },
+{ "verdana,arial,helvetica,serif",
+ "Nhqvb" },
+{ "verdana,arial,helvetica,serif",
+ "naq" },
+{ "verdana,arial,helvetica,serif",
+ "Ivqrb" },
+{ "verdana,arial,helvetica,serif",
+ "8:" },
+{ "verdana,arial,helvetica,serif",
+ "Frr" },
+{ "verdana,arial,helvetica,serif",
+ "urne" },
+{ "verdana,arial,helvetica,serif",
+ "gur" },
+{ "verdana,arial,helvetica,serif",
+ "qvssrerapr" },
+{ "verdana,arial,helvetica,serif",
+ "sbe" },
+{ "verdana,arial,helvetica,serif",
+ "lbhefrys." },
+{ "verdana,arial,helvetica,serif",
+ "Zvpebfbsg" },
+{ "verdana,arial,helvetica,serif",
+ "eryrnfrf" },
+{ "verdana,arial,helvetica,serif",
+ "OvmGnyx" },
+{ "verdana,arial,helvetica,serif",
+ "Freire" },
+{ "verdana,arial,helvetica,serif",
+ "2000" },
+{ "verdana,arial,helvetica,serif",
+ "Senzrjbex" },
+{ "verdana,arial,helvetica,serif",
+ "2.0." },
+{ "verdana,arial,helvetica,serif",
+ "Cvpx" },
+{ "verdana,arial,helvetica,serif",
+ "hc" },
+{ "verdana,arial,helvetica,serif",
+ "ebnqznc" },
+{ "verdana,arial,helvetica,serif",
+ "qrcyblvat" },
+{ "verdana,arial,helvetica,serif",
+ "Cebsrffvbany" },
+{ "verdana,arial,helvetica,serif",
+ "Bssvpr" },
+{ "verdana,arial,helvetica,serif",
+ "2000." },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl" },
+{ "verdana,arial,helvetica,serif",
+ "Fubccvat?" },
+{ "verdana,arial,helvetica,serif",
+ "Sebz" },
+{ "verdana,arial,helvetica,serif",
+ "fbsgjner" },
+{ "verdana,arial,helvetica,serif",
+ "gb" },
+{ "verdana,arial,helvetica,serif",
+ "onol" },
+{ "verdana,arial,helvetica,serif",
+ "pybgurf," },
+{ "verdana,arial,helvetica,serif",
+ "svaq" },
+{ "verdana,arial,helvetica,serif",
+ "vg" },
+{ "verdana,arial,helvetica,serif",
+ "nyy" },
+{ "verdana,arial,helvetica,serif",
+ "jura" },
+{ "verdana,arial,helvetica,serif",
+ "lbh" },
+{ "verdana,arial,helvetica,serif",
+ "fubc" },
+{ "verdana,arial,helvetica,serif",
+ "bayvar" },
+{ "verdana,arial,helvetica,serif",
+ "jvgu" },
+{ "verdana,arial,helvetica,serif",
+ "Zvpebfbsg." },
+{ "verdana,arial,helvetica,serif",
+ "Zber" },
+{ "verdana,arial,helvetica,serif",
+ "Arj" },
+{ "verdana,arial,helvetica,serif",
+ "Qbjaybnqf" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfhny" },
+{ "verdana,arial,helvetica,serif",
+ "Fghqvb.ARG:" },
+{ "verdana,arial,helvetica,serif",
+ "Trg" },
+{ "verdana,arial,helvetica,serif",
+ "Orgn" },
+{ "verdana,arial,helvetica,serif",
+ "1" },
+{ "verdana,arial,helvetica,serif",
+ "n" },
+{ "verdana,arial,helvetica,serif",
+ "svefg" },
+{ "verdana,arial,helvetica,serif",
+ "ybbx" },
+{ "verdana,arial,helvetica,serif",
+ "ng" },
+{ "verdana,arial,helvetica,serif",
+ "shgher" },
+{ "verdana,arial,helvetica,serif",
+ "bs" },
+{ "verdana,arial,helvetica,serif",
+ "qrirybczrag." },
+{ "verdana,arial,helvetica,serif",
+ "FC1" },
+{ "verdana,arial,helvetica,serif",
+ "unf" },
+{ "verdana,arial,helvetica,serif",
+ "yngrfg" },
+{ "verdana,arial,helvetica,serif",
+ "va" },
+{ "verdana,arial,helvetica,serif",
+ "pbzcngvovyvgl," },
+{ "verdana,arial,helvetica,serif",
+ "frghc," },
+{ "verdana,arial,helvetica,serif",
+ "eryvnovyvgl," },
+{ "verdana,arial,helvetica,serif",
+ "frphevgl" },
+{ "verdana,arial,helvetica,serif",
+ "hcqngrf." },
+{ "Verdana, Arial, Helvetica,serif",
+ " Ynfg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hcqngrq:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ghrfqnl," },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrprzore" },
+{ "Verdana, Arial, Helvetica,serif",
+ "12," },
+{ "Verdana, Arial, Helvetica,serif",
+ "2000" },
+{ "Verdana, Arial, Helvetica,serif",
+ "-" },
+{ "Verdana, Arial, Helvetica,serif",
+ "11:13" },
+{ "Verdana, Arial, Helvetica,serif",
+ "n.z." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cnpvsvp" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Gvzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ " \xc2""\xa9""2000" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbecbengvba." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Nyy" },
+{ "Verdana, Arial, Helvetica,serif",
+ "evtugf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "erfreirq." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grezf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "bs" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hfr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grkg-bayl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cntr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "|" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qvfnovyvgl/npprffvovyvgl" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbagnpg" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Hf" },
+{ "serif",
+ "23," },
+{ "serif",
+ "jjj.zvpebfbsg.pbz," },
+{ "serif",
+ "136" },
+{ "serif",
+ "1, 23, jjj.zvpebfbsg.pbz, 136" },
+{ "Verdana, Arial,serif",
+ " " },
+{ "Verdana, Arial,serif",
+ "Nyy Cebqhpgf" },
+{ "Verdana, Arial,serif",
+ " " },
+{ "Verdana, Arial,serif",
+ "zvpebfbsg.pbz Ubzr" },
+{ "Verdana, Arial,serif",
+ "Nobhg Zvpebfbsg" },
+{ "Verdana, Arial,serif",
+ "Pbagnpg Hf" },
+{ "Lucida Grande",
+ "TB" },
+{ "Verdana, Arial, Helvetica,serif",
+ " " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cebqhpg Snzvyl Fvgrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ " " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Qrirybcre Gbbyf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jro Freivprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Bssvpr rFreivprf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jvaqbjf Hcqngr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Phfgbzre Fvgrf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Ubzr & Crefbany" },
+{ "Verdana, Arial, Helvetica,serif",
+ "VG Cebsrffvbany" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg Cerff Obbxf" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg Wbof" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Cevinpl Fgngrzrag" },
+{ "Verdana, Arial, Helvetica,serif",
+ "QBW if. Vaabingvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Trg Rkpunatr " },
+{ "Verdana, Arial, Helvetica,serif",
+ "2000 Rinyhngvba" },
+{ "Verdana, Arial, Helvetica,serif",
+ "naq grfg qevir " },
+{ "Verdana, Arial, Helvetica,serif",
+ "naljurer, nalgvzr " },
+{ "Verdana, Arial, Helvetica,serif",
+ "pbzzhavpngvba." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Zvpebfbsg .ARG:" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Arjf naq erfbheprf " },
+{ "Verdana, Arial, Helvetica,serif",
+ "sbe qrirybcref, VG " },
+{ "Verdana, Arial, Helvetica,serif",
+ "cebf, naq ohfvarff." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Trg Jvaqbjf " },
+{ "Verdana, Arial, Helvetica,serif",
+ "2000 Cebsrffvbany," },
+{ "Verdana, Arial, Helvetica,serif",
+ "gur zbfg eryvnoyr " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Jvaqbjf rire." },
+{ "Verdana, Arial, Helvetica,serif",
+ "Frr ubj Bssvpr " },
+{ "Verdana, Arial, Helvetica,serif",
+ "2000 ng ubzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "pna cnl qvivqraqf va " },
+{ "Verdana, Arial, Helvetica,serif",
+ "fpubby naq ng jbex." },
+{ "verdana,arial,helvetica,serif",
+ "Gbqnl'f Arjf" },
+{ "verdana,arial,helvetica,serif",
+ "Jvaqbjf Zrqvn Nhqvb " },
+{ "verdana,arial,helvetica,serif",
+ "naq Ivqrb 8: " },
+{ "verdana,arial,helvetica,serif",
+ "Frr naq urne gur " },
+{ "verdana,arial,helvetica,serif",
+ "qvssrerapr sbe lbhefrys." },
+{ "verdana,arial,helvetica,serif",
+ "Zvpebfbsg eryrnfrf" },
+{ "verdana,arial,helvetica,serif",
+ "OvmGnyx Freire 2000 naq " },
+{ "verdana,arial,helvetica,serif",
+ "OvmGnyx Senzrjbex 2.0." },
+{ "verdana,arial,helvetica,serif",
+ "Cvpx hc gur ebnqznc" },
+{ "verdana,arial,helvetica,serif",
+ "sbe qrcyblvat Jvaqbjf " },
+{ "verdana,arial,helvetica,serif",
+ "2000 Cebsrffvbany naq " },
+{ "verdana,arial,helvetica,serif",
+ "Bssvpr 2000." },
+{ "verdana,arial,helvetica,serif",
+ "Ubyvqnl Fubccvat?" },
+{ "verdana,arial,helvetica,serif",
+ "Sebz fbsgjner gb onol " },
+{ "verdana,arial,helvetica,serif",
+ "pybgurf, svaq vg nyy jura " },
+{ "verdana,arial,helvetica,serif",
+ "lbh fubc bayvar jvgu " },
+{ "verdana,arial,helvetica,serif",
+ "Zvpebfbsg." },
+{ "verdana,arial,helvetica,serif",
+ "Zber Arjf" },
+{ "verdana,arial,helvetica,serif",
+ "Arj Qbjaybnqf" },
+{ "verdana,arial,helvetica,serif",
+ "Ivfhny Fghqvb.ARG:" },
+{ "verdana,arial,helvetica,serif",
+ "Trg Orgn 1 sbe n svefg ybbx " },
+{ "verdana,arial,helvetica,serif",
+ "ng gur shgher bs " },
+{ "verdana,arial,helvetica,serif",
+ "qrirybczrag." },
+{ "verdana,arial,helvetica,serif",
+ "Jvaqbjf 2000 FC1" },
+{ "verdana,arial,helvetica,serif",
+ "unf gur yngrfg va " },
+{ "verdana,arial,helvetica,serif",
+ "pbzcngvovyvgl, frghc, " },
+{ "verdana,arial,helvetica,serif",
+ "eryvnovyvgl, naq frphevgl " },
+{ "verdana,arial,helvetica,serif",
+ "hcqngrf." },
+{ "verdana,arial,helvetica,serif",
+ "Zber Qbjaybnqf" },
+{ "Verdana, Arial, Helvetica,serif",
+ " Ynfg Hcqngrq: Ghrfqnl, Qrprzore 12, 2000 - 11:13 n.z. Cnpvsvp Gvzr" },
+{ "Verdana, Arial, Helvetica,serif",
+ " \xc2""\xa9""2000 Zvpebfbsg Pbecbengvba. Nyy evtugf erfreirq. " },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grezf bs Hfr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Grkg-bayl Ubzr Cntr" },
+{ "Verdana, Arial, Helvetica,serif",
+ "Pbagnpg Hf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gurl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fnvq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung?!?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ":" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Nyvpvn" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fvyirefgbar" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jvaf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Guvf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lrne'f" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Sbbg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "va" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zbhgu" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Njneq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qrfreg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Vfynaq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cbyy:" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung'f" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "gur" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Bar" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Guvat" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lbh" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Whfg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pbhyqa'g" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qb" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jvgubhg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Vf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Iregvpny" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Yvzvg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "nf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Rkgerzr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Vg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ybbxf?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Purpx" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Bhg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zbivrtbref'" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Rneyl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ernpgvbaf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lbh'ir" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Tbg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fvk" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zbaguf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Yvir" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ubj" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jbhyq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qe." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Frhff" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Oernx" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Arjf?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Beqre" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Abj!" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Whenffvp" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cnex" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "naq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gur" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ybfg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jbeyq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "ba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "QIQ" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cebbs" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "bs" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Yvsr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ":" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Whfg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Znxrf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ehffryy" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pebjr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fb" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qnza" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Veerfvfgnoyr?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fgne" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "bs" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "gur" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jrrx" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jub" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qb" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jnag?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Uryra" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Uhag" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pebhpuvat" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gvtre," },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Uvqqra" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qentba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Orfg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Svyz" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lrne?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jngpu" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Genvyre" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Iregvpny" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Yvzvg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "16" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zvyyvba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qbyynef," },
+{ "Arial, Helvetica, sans-serif,serif",
+ "28" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gubhfnaq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Srrg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "naq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pyvzovat" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "1." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Haoernxnoyr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "2." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Frhff'" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Tevapu" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fgbyr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Puevfgznf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "3." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pebhpuvat" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gvtre," },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Uvqqra" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qentba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "4." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cebbs" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "bs" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Yvsr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "5." },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Tynqvngbe" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pnfg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Njnl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Guvegrra" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qnlf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gur" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Snzvyl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zna" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Unaavony" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Nyy" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cerggl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ubefrf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jbzra" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jnag" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qhqr," },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jurer'f" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pne?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Rzcrebef" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Arj" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Tebbir" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pubpbyng" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cbyybpx" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pbclevtug" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "1999-2000" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Zbivrsbar," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Vap." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Nzrevpn" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Bayvar," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " Nyy" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Evtugf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Erfreirq." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gel" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NBY" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "6.0!" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Trg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zbivrznvy" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "|" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pbagnpg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Hf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Nobhg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Zbivrsbar" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cevinpl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cbyvpl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Nssvyvngrf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Uryc" },
+{ "serif",
+ "24," },
+{ "serif",
+ "jjj.zbivrsbar.pbz," },
+{ "serif",
+ "177" },
+{ "serif",
+ "1, 24, jjj.zbivrsbar.pbz, 177" },
+{ "Arial, Helvetica, sans-serif,serif",
+ " " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gurl Fnvq Jung?!?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": Nyvpvn Fvyirefgbar Jvaf Guvf " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lrne'f Sbbg va Zbhgu Njneq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qrfreg Vfynaq Cbyy: " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung'f gur Bar Guvat Lbh Whfg " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pbhyqa'g Qb Jvgubhg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Vf " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Iregvpny Yvzvg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ " nf Rkgerzr nf Vg Ybbxf? " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Purpx " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Bhg Zbivrtbref' Rneyl Ernpgvbaf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Lbh'ir Tbg Fvk Zbaguf gb Yvir" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": Ubj Jbhyq Qe. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Frhff Oernx gur Arjf?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Beqre Abj! " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Whenffvp Cnex" },
+{ "Arial, Helvetica, sans-serif,serif",
+ " naq " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gur Ybfg Jbeyq" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "ba QIQ" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cebbs bs Yvsr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": Whfg Jung Znxrf " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Ehffryy Pebjr Fb Qnza " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fgne bs gur Jrrx" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": Jub Qb Jr " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jnag? Uryra Uhag" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pebhpuvat Gvtre, Uvqqra Qentba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Orfg Svyz bs gur Lrne? " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jngpu gur " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Iregvpny Yvzvg" },
+{ "Arial, Helvetica, sans-serif,serif",
+ ": 16 Zvyyvba Qbyynef, " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "28 Gubhfnaq Srrg naq Pyvzovat" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "1. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Haoernxnoyr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "2. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qe. Frhff' Ubj gur Tevapu " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Fgbyr Puevfgznf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "3. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pebhpuvat Gvtre, Uvqqra " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qentba" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "4. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Cebbs bs Yvsr" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "5. " },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Tynqvngbe" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Pnfg Njnl" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Guvegrra Qnlf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gur Snzvyl Zna" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Nyy gur Cerggl Ubefrf" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Jung Jbzra Jnag" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Qhqr, Jurer'f Zl Pne?" },
+{ "Arial, Helvetica, sans-serif,serif",
+ "Gur Rzcrebef Arj Tebbir" },
+{ "verdana,serif",
+ "Purpx" },
+{ "verdana,serif",
+ "bhg" },
+{ "verdana,serif",
+ "gur" },
+{ "verdana,serif",
+ "arj" },
+{ "verdana,serif",
+ "ZFA.pbz" },
+{ "verdana,serif",
+ "rFubc!" },
+{ "verdana,serif",
+ "Rnfvyl" },
+{ "verdana,serif",
+ "svaq" },
+{ "verdana,serif",
+ "jung" },
+{ "verdana,serif",
+ "lbh" },
+{ "verdana,serif",
+ "jnag" },
+{ "verdana,serif",
+ "gb" },
+{ "verdana,serif",
+ "ohl." },
+{ "arial,sans-serif,serif",
+ "ABIRZORE" },
+{ "arial,sans-serif,serif",
+ "12" },
+{ "arial,sans-serif,serif",
+ "pyvpx" },
+{ "arial,sans-serif,serif",
+ "urer" },
+{ "arial,sans-serif,serif",
+ "gb" },
+{ "arial,sans-serif,serif",
+ "frnepu" },
+{ "verdana,sans-serif,serif",
+ "Ubzr" },
+{ "verdana,sans-serif,serif",
+ "Ubgznvy" },
+{ "verdana,sans-serif,serif",
+ "Frnepu" },
+{ "verdana,sans-serif,serif",
+ "Fubccvat" },
+{ "verdana,sans-serif,serif",
+ "Zbarl" },
+{ "verdana,sans-serif,serif",
+ "Crbcyr" },
+{ "verdana,sans-serif,serif",
+ "&" },
+{ "verdana,sans-serif,serif",
+ "Pung" },
+{ "verdana,sans-serif,serif",
+ "ZFA" },
+{ "verdana,sans-serif,serif",
+ "Rkcybere:" },
+{ "verdana,sans-serif,serif",
+ "lbhe" },
+{ "verdana,sans-serif,serif",
+ "arj" },
+{ "verdana,sans-serif,serif",
+ "nyy-va-bar" },
+{ "verdana,sans-serif,serif",
+ "Jro" },
+{ "verdana,sans-serif,serif",
+ "Tbg" },
+{ "verdana,sans-serif,serif",
+ "GvIb?" },
+{ "verdana,sans-serif,serif",
+ "$100" },
+{ "verdana,sans-serif,serif",
+ "bss" },
+{ "verdana,sans-serif,serif",
+ "'gncr-yrff'" },
+{ "verdana,sans-serif,serif",
+ "IPE" },
+{ "arial,sans-serif,serif",
+ "oPrageny.pbz" },
+{ "arial,sans-serif,serif",
+ "Nhgbf" },
+{ "arial,sans-serif,serif",
+ "Ohfvarff" },
+{ "arial,sans-serif,serif",
+ "Pnerref" },
+{ "arial,sans-serif,serif",
+ "Pvgl Thvqrf" },
+{ "arial,sans-serif,serif",
+ "Pbzchgvat & Jro" },
+{ "arial,sans-serif,serif",
+ "Ryrpgvba" },
+{ "arial,sans-serif,serif",
+ "2000" },
+{ "arial,sans-serif,serif",
+ "Ragregnvazrag" },
+{ "arial,sans-serif,serif",
+ "Tnzrf" },
+{ "arial,sans-serif,serif",
+ "Urnygu" },
+{ "arial,sans-serif,serif",
+ "Ubhfr" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "Ubzr" },
+{ "arial,sans-serif,serif",
+ "Xvqf" },
+{ "arial,sans-serif,serif",
+ "Yrneavat" },
+{ "arial,sans-serif,serif",
+ "Erfrnepu" },
+{ "arial,sans-serif,serif",
+ "Ybir" },
+{ "arial,sans-serif,serif",
+ "Eryngvbafuvcf" },
+{ "arial,sans-serif,serif",
+ "Arjf" },
+{ "arial,sans-serif,serif",
+ "Crgf" },
+{ "arial,sans-serif,serif",
+ "Navznyf" },
+{ "arial,sans-serif,serif",
+ "Enqvb & Ivqrb" },
+{ "arial,sans-serif,serif",
+ "Fcbegf" },
+{ "arial,sans-serif,serif",
+ "Gunaxftvivat" },
+{ "arial,sans-serif,serif",
+ "Geniry" },
+{ "arial,sans-serif,serif",
+ "Jbzra" },
+{ "arial,sans-serif,serif",
+ "Nve Gvpxrgf" },
+{ "arial,sans-serif,serif",
+ "Onetnva" },
+{ "arial,sans-serif,serif",
+ "Pragre" },
+{ "arial,sans-serif,serif",
+ "Ohl Zhfvp" },
+{ "arial,sans-serif,serif",
+ "Ohl" },
+{ "arial,sans-serif,serif",
+ "Obbxf" },
+{ "arial,sans-serif,serif",
+ "Pnyraqne" },
+{ "arial,sans-serif,serif",
+ "Qbjaybnqf" },
+{ "arial,sans-serif,serif",
+ "Serr Tnzrf" },
+{ "arial,sans-serif,serif",
+ "Terrgvat" },
+{ "arial,sans-serif,serif",
+ "Pneqf" },
+{ "arial,sans-serif,serif",
+ "Ubzr Cntrf" },
+{ "arial,sans-serif,serif",
+ "Zncf" },
+{ "arial,sans-serif,serif",
+ "Fgbpx Dhbgrf" },
+{ "arial,sans-serif,serif",
+ "Zber" },
+{ "arial,sans-serif,serif",
+ "..." },
+{ "verdana,serif",
+ "Rnfl" },
+{ "verdana,serif",
+ "erpvcrf," },
+{ "verdana,serif",
+ "ubj-gbf" },
+{ "verdana,serif",
+ "Gbqnl" },
+{ "verdana,serif",
+ "ba" },
+{ "verdana,serif",
+ "ZFA" },
+{ "arial,sans-serif,serif",
+ "\xc2""\xb7"" " },
+{ "arial,sans-serif,serif",
+ "Yngrfg" },
+{ "arial,sans-serif,serif",
+ "ryrpgvba" },
+{ "arial,sans-serif,serif",
+ "hcqngrf" },
+{ "arial,sans-serif,serif",
+ "Qe." },
+{ "arial,sans-serif,serif",
+ "Ynhen" },
+{ "arial,sans-serif,serif",
+ "pnapryyrq?" },
+{ "arial,sans-serif,serif",
+ "Arj" },
+{ "arial,sans-serif,serif",
+ "'Evbg'" },
+{ "arial,sans-serif,serif",
+ "enpvat" },
+{ "arial,sans-serif,serif",
+ "tnzr" },
+{ "arial,sans-serif,serif",
+ "Sha," },
+{ "arial,sans-serif,serif",
+ "serr" },
+{ "arial,sans-serif,serif",
+ "ZFA" },
+{ "arial,sans-serif,serif",
+ "Rkcybere" },
+{ "verdana,serif",
+ "N" },
+{ "verdana,serif",
+ "snzvyl" },
+{ "verdana,serif",
+ "nssnve" },
+{ "arial,sans-serif,serif",
+ "Yrnea" },
+{ "arial,sans-serif,serif",
+ "ybir" },
+{ "arial,sans-serif,serif",
+ "lbhe" },
+{ "arial,sans-serif,serif",
+ "va-ynjf" },
+{ "arial,sans-serif,serif",
+ "Puvp" },
+{ "arial,sans-serif,serif",
+ "zngreavgl" },
+{ "arial,sans-serif,serif",
+ "jrne" },
+{ "arial,sans-serif,serif",
+ "fnyr" },
+{ "arial,sans-serif,serif",
+ "Fubj" },
+{ "arial,sans-serif,serif",
+ "bss" },
+{ "arial,sans-serif,serif",
+ "snzvyl" },
+{ "arial,sans-serif,serif",
+ "cubgbf" },
+{ "verdana,serif",
+ "Cnl" },
+{ "verdana,serif",
+ "zber" },
+{ "verdana,serif",
+ "sbe" },
+{ "verdana,serif",
+ "Zvpxrl?" },
+{ "verdana,serif",
+ "Qvfarlynaq" },
+{ "verdana,serif",
+ "cevpr" },
+{ "verdana,serif",
+ "uvxr" },
+{ "arial,sans-serif,serif",
+ "Rng," },
+{ "arial,sans-serif,serif",
+ "qevax" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "or" },
+{ "arial,sans-serif,serif",
+ "zreel:" },
+{ "arial,sans-serif,serif",
+ "ubyvqnl" },
+{ "arial,sans-serif,serif",
+ "thvqr" },
+{ "verdana,sans-serif,serif",
+ "R-znvy" },
+{ "verdana,sans-serif,serif",
+ "Cnffjbeq:" },
+{ "verdana,sans-serif,serif",
+ "Fvta" },
+{ "verdana,sans-serif,serif",
+ "hc" },
+{ "verdana,sans-serif,serif",
+ "sbe" },
+{ "verdana,sans-serif,serif",
+ "serr" },
+{ "verdana,sans-serif,serif",
+ "r-znvy" },
+{ "verdana,sans-serif,serif",
+ "Ubgznvy" },
+{ "verdana,sans-serif,serif",
+ "Zrzore" },
+{ "verdana,sans-serif,serif",
+ "Anzr:" },
+{ "verdana,sans-serif,serif",
+ "Bayvar" },
+{ "verdana,sans-serif,serif",
+ "Pbagnpgf" },
+{ "verdana,sans-serif,serif",
+ "Guvf" },
+{ "verdana,sans-serif,serif",
+ "frpgvba" },
+{ "verdana,sans-serif,serif",
+ "erdhverf" },
+{ "verdana,sans-serif,serif",
+ "gur" },
+{ "verdana,sans-serif,serif",
+ "yngrfg" },
+{ "verdana,sans-serif,serif",
+ "irefvbaf" },
+{ "verdana,sans-serif,serif",
+ "bs" },
+{ "verdana,sans-serif,serif",
+ "Vagrearg" },
+{ "verdana,sans-serif,serif",
+ "Rkcybere" },
+{ "verdana,sans-serif,serif",
+ "naq" },
+{ "verdana,sans-serif,serif",
+ "ZFA" },
+{ "verdana,sans-serif,serif",
+ "Zrffratre" },
+{ "verdana,sans-serif,serif",
+ "Freivpr" },
+{ "verdana,sans-serif,serif",
+ "." },
+{ "verdana,sans-serif,serif",
+ "Pbzzhavgvrf" },
+{ "verdana,sans-serif,serif",
+ "Puevfgvna" },
+{ "verdana,sans-serif,serif",
+ "Fvatyrf" },
+{ "verdana,sans-serif,serif",
+ "Pung" },
+{ "verdana,sans-serif,serif",
+ "Grra" },
+{ "verdana,sans-serif,serif",
+ "fubhg-bhg!" },
+{ "verdana,sans-serif,serif",
+ "PUNATR" },
+{ "verdana,sans-serif,serif",
+ "Pbagrag" },
+{ "arial,sans-serif,serif",
+ "/" },
+{ "verdana,sans-serif,serif",
+ "Ynlbhg" },
+{ "verdana,sans-serif,serif",
+ "Pbybef" },
+{ "verdana,sans-serif,serif",
+ "RAGRE" },
+{ "verdana,sans-serif,serif",
+ "M" },
+{ "verdana,sans-serif,serif",
+ "VC" },
+{ "verdana,sans-serif,serif",
+ "PBQR" },
+{ "verdana,sans-serif,serif",
+ "Ybpny" },
+{ "verdana,sans-serif,serif",
+ "Vasb" },
+{ "verdana,sans-serif,serif",
+ "&" },
+{ "verdana,sans-serif,serif",
+ "Jrngure:" },
+{ "verdana,sans-serif,serif",
+ "URYC" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "verdana,sans-serif,serif",
+ " Jbegu" },
+{ "verdana,sans-serif,serif",
+ "n" },
+{ "verdana,sans-serif,serif",
+ "Pyvpx" },
+{ "arial,sans-serif,serif",
+ "Serr" },
+{ "arial,sans-serif,serif",
+ "geniry" },
+{ "arial,sans-serif,serif",
+ "znt:" },
+{ "arial,sans-serif,serif",
+ "Frr" },
+{ "arial,sans-serif,serif",
+ "gur" },
+{ "arial,sans-serif,serif",
+ "jbeyq" },
+{ "arial,sans-serif,serif",
+ "sebz" },
+{ "arial,sans-serif,serif",
+ "pbhpu" },
+{ "arial,sans-serif,serif",
+ "Fgbpx" },
+{ "arial,sans-serif,serif",
+ "hc" },
+{ "arial,sans-serif,serif",
+ "ba" },
+{ "arial,sans-serif,serif",
+ "pbml" },
+{ "arial,sans-serif,serif",
+ "qbja" },
+{ "arial,sans-serif,serif",
+ "orqqvat" },
+{ "arial,sans-serif,serif",
+ "sbe" },
+{ "arial,sans-serif,serif",
+ "jvagre" },
+{ "arial,sans-serif,serif",
+ "Jnag" },
+{ "arial,sans-serif,serif",
+ "noebnq?" },
+{ "arial,sans-serif,serif",
+ "ynathntr" },
+{ "arial,sans-serif,serif",
+ "svefg" },
+{ "verdana,sans-serif,serif",
+ " Yvaxf" },
+{ "verdana,sans-serif,serif",
+ "&" },
+{ "verdana,sans-serif,serif",
+ "Erfbheprf" },
+{ "arial,,serif",
+ "Sebz" },
+{ "arial,,serif",
+ "ZFA" },
+{ "verdana,serif",
+ "Snfg" },
+{ "verdana,serif",
+ "Vagrearg" },
+{ "verdana,serif",
+ "Npprff" },
+{ "verdana,serif",
+ "Trg" },
+{ "verdana,serif",
+ "ZFA" },
+{ "verdana,serif",
+ "Pbzcnavba" },
+{ "verdana,serif",
+ "ba" },
+{ "verdana,serif",
+ "lbhe" },
+{ "verdana,serif",
+ "pryy" },
+{ "verdana,serif",
+ "cubar" },
+{ "verdana,serif",
+ "Zrffratre" },
+{ "verdana,serif",
+ "Freivpr" },
+{ "verdana,serif",
+ "Arj" },
+{ "verdana,serif",
+ "Rkcybere" },
+{ "arial,,serif",
+ "Zvpebfbsg" },
+{ "verdana,serif",
+ "zvpebfbsg.pbz" },
+{ "verdana,serif",
+ "oPrageny" },
+{ "verdana,serif",
+ "Jvaqbjf" },
+{ "verdana,serif",
+ "Hcqngr" },
+{ "verdana,serif",
+ "Bssvpr" },
+{ "arial,,serif",
+ "Jbeyqjvqr" },
+{ "verdana,serif",
+ "Pnanqn" },
+{ "verdana,serif",
+ "Zrkvpb" },
+{ "verdana,serif",
+ "Zber" },
+{ "verdana,serif",
+ "fvgrf..." },
+{ "arial,,serif",
+ "Bgure" },
+{ "arial,,serif",
+ "Erfbheprf" },
+{ "verdana,serif",
+ "Pbagnpg" },
+{ "verdana,serif",
+ "hf" },
+{ "verdana,serif",
+ "Frg" },
+{ "verdana,serif",
+ "nf" },
+{ "verdana,serif",
+ "ubzr" },
+{ "verdana,serif",
+ "cntr" },
+{ "verdana,sans-serif,serif",
+ " Arjf" },
+{ "arial,sans-serif,serif",
+ "ZFAOP" },
+{ "arial,sans-serif,serif",
+ "Syn." },
+{ "arial,sans-serif,serif",
+ "pbhagl" },
+{ "arial,sans-serif,serif",
+ "beqref" },
+{ "arial,sans-serif,serif",
+ "znahny" },
+{ "arial,sans-serif,serif",
+ "erpbhag" },
+{ "arial,sans-serif,serif",
+ "Rkcrpgngvbaf" },
+{ "arial,sans-serif,serif",
+ "ybj" },
+{ "arial,sans-serif,serif",
+ "Onenx" },
+{ "arial,sans-serif,serif",
+ "zrrgvat" },
+{ "arial,sans-serif,serif",
+ "Yrnu" },
+{ "arial,sans-serif,serif",
+ "Enova" },
+{ "arial,sans-serif,serif",
+ "qvrf" },
+{ "arial,sans-serif,serif",
+ "ng" },
+{ "arial,sans-serif,serif",
+ "ntr" },
+{ "arial,sans-serif,serif",
+ "72" },
+{ "arial,sans-serif,serif",
+ "Cbyvgvpf" },
+{ "arial,sans-serif,serif",
+ "--" },
+{ "arial,sans-serif,serif",
+ "sebz" },
+{ "arial,sans-serif,serif",
+ "Fyngr" },
+{ "arial,sans-serif,serif",
+ "Tber" },
+{ "arial,sans-serif,serif",
+ "pnzcnvta" },
+{ "arial,sans-serif,serif",
+ "qrznaqf" },
+{ "arial,sans-serif,serif",
+ "unaq" },
+{ "arial,sans-serif,serif",
+ "erpbhag," },
+{ "arial,sans-serif,serif",
+ "naq" },
+{ "arial,sans-serif,serif",
+ "bgure" },
+{ "arial,sans-serif,serif",
+ "fgbevrf" },
+{ "arial,sans-serif,serif",
+ "nebhaq" },
+{ "arial,sans-serif,serif",
+ "Jro." },
+{ "arial,sans-serif,serif",
+ "Jung" },
+{ "arial,sans-serif,serif",
+ "Ny" },
+{ "arial,sans-serif,serif",
+ "fubhyq" },
+{ "arial,sans-serif,serif",
+ "qb" },
+{ "verdana,sans-serif,serif",
+ " Crefbany" },
+{ "verdana,sans-serif,serif",
+ "Svanapr" },
+{ "arial,sans-serif,serif",
+ "ZbarlPrageny" },
+{ "arial,sans-serif,serif",
+ "Fgbpx" },
+{ "arial,sans-serif,serif",
+ "Dhbgrf" },
+{ "arial,sans-serif,serif",
+ "Svqryvgl Vairfgzragf" },
+{ "arial,sans-serif,serif",
+ "Qngrx Bayvar" },
+{ "arial,sans-serif,serif",
+ "Cngntba.pbz HFN" },
+{ "arial,sans-serif,serif",
+ "R*GENQR" },
+{ "arial,sans-serif,serif",
+ "Flzoby" },
+{ "arial,sans-serif,serif",
+ "$VAQH" },
+{ "arial,sans-serif,serif",
+ "$KNK.K" },
+{ "arial,sans-serif,serif",
+ "$PBZCK" },
+{ "arial,sans-serif,serif",
+ "$VAK" },
+{ "arial,sans-serif,serif",
+ "Anzr" },
+{ "arial,sans-serif,serif",
+ "Qbj" },
+{ "arial,sans-serif,serif",
+ "Wbarf" },
+{ "arial,sans-serif,serif",
+ "Vaqhfgevnyf" },
+{ "arial,sans-serif,serif",
+ "Vaqrk" },
+{ "arial,sans-serif,serif",
+ "Nzrk" },
+{ "arial,sans-serif,serif",
+ "Pbzcbfvgr" },
+{ "arial,sans-serif,serif",
+ "Anfqnd" },
+{ "arial,sans-serif,serif",
+ "Pbzovarq" },
+{ "arial,sans-serif,serif",
+ "F&C" },
+{ "arial,sans-serif,serif",
+ "500" },
+{ "arial,sans-serif,serif",
+ "VAQRK" },
+{ "arial,sans-serif,serif",
+ "Ynfg" },
+{ "arial,sans-serif,serif",
+ "10,602.950" },
+{ "arial,sans-serif,serif",
+ "899.350" },
+{ "arial,sans-serif,serif",
+ "3,028.990" },
+{ "arial,sans-serif,serif",
+ "1,365.980" },
+{ "arial,sans-serif,serif",
+ "Put" },
+{ "arial,sans-serif,serif",
+ "-231.300" },
+{ "arial,sans-serif,serif",
+ "-11.590" },
+{ "arial,sans-serif,serif",
+ "-171.360" },
+{ "arial,sans-serif,serif",
+ "-34.160" },
+{ "arial,sans-serif,serif",
+ "%Put" },
+{ "arial,sans-serif,serif",
+ "-2.13%" },
+{ "arial,sans-serif,serif",
+ "-1.27%" },
+{ "arial,sans-serif,serif",
+ "-5.35%" },
+{ "arial,sans-serif,serif",
+ "-2.44%" },
+{ "arial,sans-serif,serif",
+ "Nqq" },
+{ "arial,sans-serif,serif",
+ "lbhe" },
+{ "arial,sans-serif,serif",
+ "bja" },
+{ "arial,sans-serif,serif",
+ "flzobyf" },
+{ "Lucida Grande",
+ "trg dhbgr" },
+{ "arial,sans-serif,serif",
+ "Svaq" },
+{ "arial,sans-serif,serif",
+ "flzoby" },
+{ "Lucida Grande",
+ "trg puneg" },
+{ "arial,sans-serif,serif",
+ "Dhbgrf" },
+{ "arial,sans-serif,serif",
+ "fhccyvrq" },
+{ "arial,sans-serif,serif",
+ "ol" },
+{ "arial,sans-serif,serif",
+ "Fgnaqneq" },
+{ "arial,sans-serif,serif",
+ "&" },
+{ "arial,sans-serif,serif",
+ "Cbbe'f" },
+{ "arial,sans-serif,serif",
+ "Pbzfgbpx" },
+{ "arial,sans-serif,serif",
+ "naq" },
+{ "arial,sans-serif,serif",
+ "qrynlrq" },
+{ "arial,sans-serif,serif",
+ "ng" },
+{ "arial,sans-serif,serif",
+ "yrnfg" },
+{ "arial,sans-serif,serif",
+ "20" },
+{ "arial,sans-serif,serif",
+ "zvahgrf." },
+{ "arial,sans-serif,serif",
+ "Arjf" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Znexrg" },
+{ "arial,sans-serif,serif",
+ "Ercbeg" },
+{ "arial,sans-serif,serif",
+ "Gbc" },
+{ "arial,sans-serif,serif",
+ "10" },
+{ "arial,sans-serif,serif",
+ "Yvfgf" },
+{ "arial,sans-serif,serif",
+ "Genqvat" },
+{ "verdana,sans-serif,serif",
+ " Fubccvat" },
+{ "arial,sans-serif,serif",
+ "rFubc" },
+{ "arial,sans-serif,serif",
+ "Fnyrf" },
+{ "arial,sans-serif,serif",
+ "Srngherq" },
+{ "arial,sans-serif,serif",
+ "Bssref" },
+{ "verdana,arial, helvetica,serif",
+ "Fnyrf" },
+{ "verdana,arial, helvetica,serif",
+ "&" },
+{ "verdana,arial, helvetica,serif",
+ "Qrnyf" },
+{ "arial, helvetica,serif",
+ "Trg" },
+{ "arial, helvetica,serif",
+ "Serr" },
+{ "arial, helvetica,serif",
+ "Fuvccvat" },
+{ "arial, helvetica,serif",
+ "sbe" },
+{ "arial, helvetica,serif",
+ "2001" },
+{ "arial, helvetica,serif",
+ "$60" },
+{ "arial, helvetica,serif",
+ "va" },
+{ "arial, helvetica,serif",
+ "pbhcbaf" },
+{ "arial, helvetica,serif",
+ "sebz" },
+{ "arial, helvetica,serif",
+ "rOntf!" },
+{ "arial, helvetica,serif",
+ "Zber" },
+{ "arial, helvetica,serif",
+ "gur" },
+{ "arial, helvetica,serif",
+ "Onetnva" },
+{ "arial, helvetica,serif",
+ "Pragre" },
+{ "verdana,arial, helvetica,serif",
+ "Cbchyne" },
+{ "verdana,arial, helvetica,serif",
+ "Cvpxf" },
+{ "arial, helvetica,serif",
+ "Fcvrtry.pbz" },
+{ "arial, helvetica,serif",
+ "Zra'f" },
+{ "arial, helvetica,serif",
+ "Fnyr" },
+{ "arial, helvetica,serif",
+ "Uryyl" },
+{ "arial, helvetica,serif",
+ "Unafra" },
+{ "arial, helvetica,serif",
+ "fcrpvnyf" },
+{ "arial, helvetica,serif",
+ "Ubyvqnlf" },
+{ "arial, helvetica,serif",
+ "ng" },
+{ "arial, helvetica,serif",
+ "CRGfZNEG" },
+{ "verdana,arial, helvetica,serif",
+ "Ubj" },
+{ "verdana,arial, helvetica,serif",
+ "gb" },
+{ "verdana,arial, helvetica,serif",
+ "ohl..." },
+{ "arial, helvetica,serif",
+ "ZC3" },
+{ "arial, helvetica,serif",
+ "cynlref" },
+{ "arial, helvetica,serif",
+ "QIQ" },
+{ "arial, helvetica,serif",
+ "Qvtvgny" },
+{ "arial, helvetica,serif",
+ "pnzrenf" },
+{ "verdana,arial, helvetica,serif",
+ "Fvta" },
+{ "verdana,arial, helvetica,serif",
+ "hc" },
+{ "verdana,arial, helvetica,serif",
+ "sbe" },
+{ "verdana,arial, helvetica,serif",
+ "r-znvy" },
+{ "verdana,sans-serif,serif",
+ " Ybpny" },
+{ "verdana,sans-serif,serif",
+ "Ybbxhcf" },
+{ "arial,sans-serif,serif",
+ "Lryybj Cntrf" },
+{ "arial,sans-serif,serif",
+ "Juvgr Cntrf" },
+{ "arial,sans-serif,serif",
+ "Jrngure" },
+{ "arial,sans-serif,serif",
+ "Svaq" },
+{ "arial,sans-serif,serif",
+ "n" },
+{ "arial,sans-serif,serif",
+ "Wbo" },
+{ "arial,sans-serif,serif",
+ "Ncnegzragf" },
+{ "arial,sans-serif,serif",
+ "Ybpny" },
+{ "arial,sans-serif,serif",
+ "Enqvb" },
+{ "arial,sans-serif,serif",
+ "Rirag" },
+{ "arial,sans-serif,serif",
+ "Gvpxrgf" },
+{ "arial,sans-serif,serif",
+ "Fcbegf Fpberf" },
+{ "arial,sans-serif,serif",
+ "GI Yvfgvatf" },
+{ "arial,sans-serif,serif",
+ "Zber..." },
+{ "verdana,sans-serif,serif",
+ " Fcbgyvtug" },
+{ "verdana,serif",
+ "Ubg" },
+{ "verdana,serif",
+ "jverq:" },
+{ "verdana,serif",
+ "ovt" },
+{ "verdana,serif",
+ "qrnyf" },
+{ "verdana,serif",
+ "gbc" },
+{ "verdana,serif",
+ "grpu" },
+{ "verdana,serif",
+ "tnqtrg" },
+{ "verdana,serif",
+ "tvsgf" },
+{ "arial,sans-serif,serif",
+ "Phggvat-rqtr" },
+{ "arial,sans-serif,serif",
+ "Puevfgznf" },
+{ "verdana,sans-serif,serif",
+ "Fubegphgf" },
+{ "arial,sans-serif,serif",
+ "Rqvgbe'f" },
+{ "arial,sans-serif,serif",
+ "Cvpxf:" },
+{ "verdana,sans-serif,serif",
+ "Ubyvqnl" },
+{ "verdana,sans-serif,serif",
+ "gblf" },
+{ "verdana,sans-serif,serif",
+ "Cnegl" },
+{ "verdana,sans-serif,serif",
+ "cerc" },
+{ "verdana,sans-serif,serif",
+ "Fgbpxvat" },
+{ "verdana,sans-serif,serif",
+ "fghssref" },
+{ "verdana,sans-serif,serif",
+ "Zber..." },
+{ "arial,sans-serif,serif",
+ "Frnepu" },
+{ "arial,sans-serif,serif",
+ "rFu" },
+{ "arial,sans-serif,serif",
+ "b" },
+{ "arial,sans-serif,serif",
+ "c" },
+{ "arial,sans-serif,serif",
+ "sbe:" },
+{ "arial,sans-serif,serif",
+ "Gbc" },
+{ "arial,sans-serif,serif",
+ "Fubcf" },
+{ "verdana,sans-serif,serif",
+ "Uneel" },
+{ "verdana,sans-serif,serif",
+ "Cbggre" },
+{ "verdana,sans-serif,serif",
+ "Cbfgref" },
+{ "verdana,sans-serif,serif",
+ "Ubg-cevprq" },
+{ "verdana,sans-serif,serif",
+ "QIQf" },
+{ "verdana,sans-serif,serif",
+ "Srngher" },
+{ "verdana,sans-serif,serif",
+ "bs" },
+{ "verdana,sans-serif,serif",
+ "gur" },
+{ "verdana,sans-serif,serif",
+ "Qnl" },
+{ "verdana,sans-serif,serif",
+ "ZbarlPrageny" },
+{ "verdana,sans-serif,serif",
+ "Zbarl" },
+{ "verdana,sans-serif,serif",
+ "znexrg" },
+{ "verdana,sans-serif,serif",
+ "nqivpr" },
+{ "verdana,sans-serif,serif",
+ "gbbyf" },
+{ "verdana,sans-serif,serif",
+ "sebz" },
+{ "verdana,sans-serif,serif",
+ "Jro'f" },
+{ "verdana,sans-serif,serif",
+ "gbc" },
+{ "verdana,sans-serif,serif",
+ "crefbany" },
+{ "verdana,sans-serif,serif",
+ "svanapr" },
+{ "verdana,sans-serif,serif",
+ "fvgr" },
+{ "verdana,sans-serif,serif",
+ "Pyvpx" },
+{ "verdana,sans-serif,serif",
+ "gb" },
+{ "verdana,sans-serif,serif",
+ "nqq..." },
+{ "verdana,sans-serif,serif",
+ "Bar-Pyvpx" },
+{ "verdana,sans-serif,serif",
+ "Crefbanyvmngvba" },
+{ "verdana,sans-serif,serif",
+ "gbcvpf" },
+{ "verdana,sans-serif,serif",
+ "orybj" },
+{ "verdana,sans-serif,serif",
+ "nqq" },
+{ "verdana,sans-serif,serif",
+ "lbhe" },
+{ "verdana,sans-serif,serif",
+ "ubzr" },
+{ "verdana,sans-serif,serif",
+ "cntr." },
+{ "verdana,sans-serif,serif",
+ "NQQ" },
+{ "verdana,sans-serif,serif",
+ "Ubebfpbcrf:" },
+{ "verdana,sans-serif,serif",
+ "Jung'f" },
+{ "verdana,sans-serif,serif",
+ "lbhe" },
+{ "verdana,sans-serif,serif",
+ "fvta?" },
+{ "verdana,sans-serif,serif",
+ "Rkcrqvn.pbz:" },
+{ "verdana,sans-serif,serif",
+ "orfg" },
+{ "verdana,sans-serif,serif",
+ "geniry," },
+{ "verdana,sans-serif,serif",
+ "ubgry" },
+{ "verdana,sans-serif,serif",
+ "qrnyf" },
+{ "verdana,sans-serif,serif",
+ "Qvfpbirel.pbz:" },
+{ "verdana,sans-serif,serif",
+ "rirelqnl" },
+{ "verdana,sans-serif,serif",
+ "nqiragherf" },
+{ "verdana,sans-serif,serif",
+ "Ragre" },
+{ "verdana,sans-serif,serif",
+ "Mvc" },
+{ "verdana,sans-serif,serif",
+ "Pbqr" },
+{ "verdana,sans-serif,serif",
+ "Ivrj" },
+{ "verdana,sans-serif,serif",
+ "PvglFrnepu" },
+{ "verdana,sans-serif,serif",
+ "Pvgl" },
+{ "verdana,sans-serif,serif",
+ "Thvqrf" },
+{ "verdana,sans-serif,serif",
+ "ba" },
+{ "arial,sans-serif,serif",
+ "Grezf" },
+{ "arial,sans-serif,serif",
+ "bs" },
+{ "arial,sans-serif,serif",
+ "Hfr" },
+{ "serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Nqiregvfr" },
+{ "arial,sans-serif,serif",
+ "GEHFGr Nccebirq Cevinpl Fgngrzrag" },
+{ "arial,sans-serif,serif",
+ "TrgArgJvfr" },
+{ "arial,sans-serif,serif",
+ "\xc2""\xa9""2000" },
+{ "arial,sans-serif,serif",
+ "Zvpebfbsg" },
+{ "arial,sans-serif,serif",
+ "Pbecbengvba." },
+{ "arial,sans-serif,serif",
+ "Nyy" },
+{ "arial,sans-serif,serif",
+ "evtugf" },
+{ "arial,sans-serif,serif",
+ "erfreirq." },
+{ "verdana,sans-serif,serif",
+ "Erghea" },
+{ "verdana,sans-serif,serif",
+ "Gbc" },
+{ "serif",
+ "25," },
+{ "serif",
+ "jjj.zfa.pbz," },
+{ "serif",
+ "163" },
+{ "serif",
+ "1, 25, jjj.zfa.pbz, 163" },
+{ "verdana,serif",
+ "Purpx bhg gur arj ZFA.pbz " },
+{ "verdana,serif",
+ "Rnfvyl svaq jung lbh jnag gb ohl." },
+{ "arial,sans-serif,serif",
+ "ABIRZORE " },
+{ "arial,sans-serif,serif",
+ "pyvpx urer gb frnepu" },
+{ "verdana,sans-serif,serif",
+ "Crbcyr & Pung" },
+{ "verdana,sans-serif,serif",
+ "ZFA Rkcybere: lbhe arj" },
+{ "verdana,sans-serif,serif",
+ "nyy-va-bar Jro" },
+{ "verdana,sans-serif,serif",
+ "Tbg GvIb? $100 bss" },
+{ "verdana,sans-serif,serif",
+ "arj 'gncr-yrff' IPE" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "Pvgl Thvqrf" },
+{ "arial,sans-serif,serif",
+ "Pbzchgvat & Jro" },
+{ "arial,sans-serif,serif",
+ "Ryrpgvba 2000" },
+{ "arial,sans-serif,serif",
+ "Ubhfr & Ubzr" },
+{ "arial,sans-serif,serif",
+ "Yrneavat & Erfrnepu" },
+{ "arial,sans-serif,serif",
+ "Ybir & Eryngvbafuvcf" },
+{ "arial,sans-serif,serif",
+ "Crgf & Navznyf" },
+{ "arial,sans-serif,serif",
+ "Enqvb & Ivqrb" },
+{ "arial,sans-serif,serif",
+ "Nve Gvpxrgf" },
+{ "arial,sans-serif,serif",
+ "Onetnva Pragre" },
+{ "arial,sans-serif,serif",
+ "Ohl Zhfvp" },
+{ "arial,sans-serif,serif",
+ "Ohl Obbxf" },
+{ "arial,sans-serif,serif",
+ "Serr Tnzrf" },
+{ "arial,sans-serif,serif",
+ "Terrgvat Pneqf" },
+{ "arial,sans-serif,serif",
+ "Ubzr Cntrf" },
+{ "arial,sans-serif,serif",
+ "Fgbpx Dhbgrf" },
+{ "arial,sans-serif,serif",
+ "Zber ..." },
+{ "arial,sans-serif,serif",
+ "Rng, qevax & or zreel: ubyvqnl thvqr" },
+{ "verdana,serif",
+ "Rnfl erpvcrf, ubj-gbf" },
+{ "verdana,serif",
+ "Gbqnl ba ZFA" },
+{ "arial,sans-serif,serif",
+ "Yngrfg ryrpgvba hcqngrf" },
+{ "arial,sans-serif,serif",
+ "Qe. Ynhen pnapryyrq?" },
+{ "arial,sans-serif,serif",
+ "Arj 'Evbg' enpvat tnzr" },
+{ "arial,sans-serif,serif",
+ "Sha, serr ZFA Rkcybere" },
+{ "verdana,serif",
+ "N snzvyl nssnve" },
+{ "arial,sans-serif,serif",
+ "Yrnea gb ybir lbhe " },
+{ "arial,sans-serif,serif",
+ "Puvp zngreavgl jrne fnyr" },
+{ "arial,sans-serif,serif",
+ "Fubj bss snzvyl cubgbf" },
+{ "verdana,serif",
+ "Cnl zber " },
+{ "verdana,serif",
+ "sbe Zvpxrl? " },
+{ "verdana,serif",
+ "Qvfarlynaq " },
+{ "verdana,serif",
+ "cevpr uvxr" },
+{ "verdana,sans-serif,serif",
+ "Ubgznvy Zrzore Anzr:" },
+{ "verdana,sans-serif,serif",
+ "Fvta hc sbe serr r-znvy" },
+{ "verdana,sans-serif,serif",
+ "Bayvar Pbagnpgf" },
+{ "verdana,sans-serif,serif",
+ "Guvf frpgvba erdhverf gur " },
+{ "verdana,sans-serif,serif",
+ "yngrfg irefvbaf bs " },
+{ "verdana,sans-serif,serif",
+ "Vagrearg " },
+{ "verdana,sans-serif,serif",
+ " naq " },
+{ "verdana,sans-serif,serif",
+ "ZFA " },
+{ "verdana,sans-serif,serif",
+ "Zrffratre Freivpr" },
+{ "verdana,sans-serif,serif",
+ ". " },
+{ "verdana,sans-serif,serif",
+ "Puevfgvna Fvatyrf" },
+{ "verdana,sans-serif,serif",
+ "Grra fubhg-bhg!" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " / " },
+{ "verdana,sans-serif,serif",
+ "RAGRE " },
+{ "verdana,sans-serif,serif",
+ "VC PBQR" },
+{ "verdana,sans-serif,serif",
+ " sbe Ybpny Vasb & Jrngure:" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ " " },
+{ "verdana,sans-serif,serif",
+ " Jbegu n Pyvpx" },
+{ "arial,sans-serif,serif",
+ "Serr geniry znt: Frr " },
+{ "arial,sans-serif,serif",
+ "gur jbeyq sebz lbhe " },
+{ "arial,sans-serif,serif",
+ "Fgbpx hc ba pbml qbja " },
+{ "arial,sans-serif,serif",
+ "orqqvat sbe jvagre" },
+{ "arial,sans-serif,serif",
+ "Jnag gb geniry noebnq? " },
+{ "arial,sans-serif,serif",
+ "Yrnea gur ynathntr svefg" },
+{ "verdana,sans-serif,serif",
+ " Yvaxf & Erfbheprf" },
+{ "arial,,serif",
+ "Sebz ZFA" },
+{ "verdana,sans-serif,serif",
+ " Arjf" },
+{ "arial,sans-serif,serif",
+ " " },
+{ "arial,sans-serif,serif",
+ "ZFAOP Arjf" },
+{ "arial,sans-serif,serif",
+ "Syn. pbhagl beqref znahny erpbhag" },
+{ "arial,sans-serif,serif",
+ "Rkcrpgngvbaf ybj sbe Onenx zrrgvat" },
+{ "arial,sans-serif,serif",
+ "Yrnu Enova qvrf ng ntr 72" },
+{ "arial,sans-serif,serif",
+ "Cbyvgvpf -- sebz Fyngr" },
+{ "arial,sans-serif,serif",
+ "Tber pnzcnvta qrznaqf unaq erpbhag, " },
+{ "arial,sans-serif,serif",
+ "naq bgure ryrpgvba fgbevrf sebz nebhaq gur " },
+{ "arial,sans-serif,serif",
+ "Jung Ny Tber fubhyq qb" },
+{ "verdana,sans-serif,serif",
+ " Ybpny Ybbxhcf" },
+{ "arial,sans-serif,serif",
+ "Lryybj Cntrf" },
+{ "arial,sans-serif,serif",
+ "Juvgr Cntrf" },
+{ "arial,sans-serif,serif",
+ "Svaq n Wbo" },
+{ "arial,sans-serif,serif",
+ "Ybpny Enqvb" },
+{ "arial,sans-serif,serif",
+ "Rirag Gvpxrgf" },
+{ "arial,sans-serif,serif",
+ "Fcbegf Fpberf" },
+{ "arial,sans-serif,serif",
+ "GI Yvfgvatf" },
+{ "verdana,sans-serif,serif",
+ " Fcbgyvtug" },
+{ "arial,serif",
+ "AQ" },
+{ "arial,serif",
+ "JVAF" },
+{ "arial,serif",
+ "VA" },
+{ "arial,serif",
+ "BIREGVZR" },
+{ "arial,serif",
+ "Pbzcyrgr" },
+{ "arial,serif",
+ "pbirentr" },
+{ "arial,serif",
+ "bs" },
+{ "arial,serif",
+ "jva" },
+{ "arial,serif",
+ "bire" },
+{ "arial,serif",
+ "Nve" },
+{ "arial,serif",
+ "Sbepr" },
+{ "Lucida Grande",
+ "Fpberf" },
+{ "Lucida Grande",
+ "ZYO" },
+{ "Lucida Grande",
+ "AON" },
+{ "Lucida Grande",
+ "ASY" },
+{ "Lucida Grande",
+ "AUY" },
+{ "Lucida Grande",
+ "Pby" },
+{ "Lucida Grande",
+ "SO" },
+{ "Lucida Grande",
+ "OO" },
+{ "Lucida Grande",
+ "JOO" },
+{ "Lucida Grande",
+ "ZYF" },
+{ "Lucida Grande",
+ "JAON" },
+{ "arial,serif",
+ "Ab." },
+{ "arial,serif",
+ "8" },
+{ "arial,serif",
+ "Sybevqn" },
+{ "arial,serif",
+ "ubyqf" },
+{ "arial,serif",
+ "bss" },
+{ "arial,serif",
+ "Trbetvn" },
+{ "arial,serif",
+ "Cheqhr" },
+{ "arial,serif",
+ "enyyvrf" },
+{ "arial,serif",
+ "cnfg" },
+{ "arial,serif",
+ "12" },
+{ "arial,serif",
+ "Buvb" },
+{ "arial,serif",
+ "Fg." },
+{ "arial,serif",
+ "Gvtre" },
+{ "arial,serif",
+ "gjb" },
+{ "arial,serif",
+ "onpx" },
+{ "arial,serif",
+ "ragrevat" },
+{ "arial,serif",
+ "svany" },
+{ "arial,serif",
+ "ebhaq" },
+{ "arial,serif",
+ "Gjb" },
+{ "arial,serif",
+ "Qrivyf" },
+{ "arial,serif",
+ "fpber" },
+{ "arial,serif",
+ "4" },
+{ "arial,serif",
+ "tbnyf" },
+{ "arial,serif",
+ "va" },
+{ "arial,serif",
+ "9-0" },
+{ "arial,serif",
+ "ebhg" },
+{ "serif",
+ ".." },
+{ "serif",
+ "......" },
+{ "arial,serif",
+ "Guvf" },
+{ "arial,serif",
+ "cntr" },
+{ "arial,serif",
+ "ynfg" },
+{ "arial,serif",
+ "hcqngrq:" },
+{ "arial,serif",
+ "00:59" },
+{ "arial,serif",
+ "RG" },
+{ "arial,serif",
+ "Bpg" },
+{ "arial,serif",
+ "29," },
+{ "arial,serif",
+ "2000" },
+{ "sans-serif",
+ "Fhozvg" },
+{ "sans-serif",
+ "Dhrel" },
+{ "arial,serif",
+ "BGURE" },
+{ "arial,serif",
+ "GBC" },
+{ "arial,serif",
+ "FGBEVRF" },
+{ "arial,serif",
+ "Qhpxf" },
+{ "arial,serif",
+ "enyyl" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "jva" },
+{ "arial,serif",
+ "va" },
+{ "arial,serif",
+ "qbhoyr" },
+{ "arial,serif",
+ "BG" },
+{ "verdana,arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "7" },
+{ "arial,serif",
+ "Bertba" },
+{ "arial,serif",
+ "orngf" },
+{ "arial,serif",
+ "Nevmban" },
+{ "arial,serif",
+ "Fgngr" },
+{ "arial,serif",
+ "56-55" },
+{ "arial,serif",
+ "Abegujrfgrea" },
+{ "arial,serif",
+ "jvaf" },
+{ "arial,serif",
+ "ba" },
+{ "arial,serif",
+ "\xe2""\x80""\x98""Unvy" },
+{ "arial,serif",
+ "Znel\xe2""\x80""\x99""" },
+{ "arial,serif",
+ "Qbja" },
+{ "arial,serif",
+ "35-14," },
+{ "arial,serif",
+ "Jvyqpngf" },
+{ "arial,serif",
+ "ba" },
+{ "arial,serif",
+ "45-lneq" },
+{ "arial,serif",
+ "qrfcrengvba" },
+{ "arial,serif",
+ "cnff" },
+{ "arial,serif",
+ "Gvzorejbyirf\xe2""\x80""\x99""" },
+{ "arial,serif",
+ "Fzvgu" },
+{ "arial,serif",
+ "va" },
+{ "arial,serif",
+ "yvzob" },
+{ "arial,serif",
+ "hagvy" },
+{ "arial,serif",
+ "Guhefqnl" },
+{ "arial,serif",
+ "Neovgengbe" },
+{ "arial,serif",
+ "jvyy" },
+{ "arial,serif",
+ "urne" },
+{ "arial,serif",
+ "nethzragf" },
+{ "arial,serif",
+ "qrpvfvba" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "ibvq" },
+{ "arial,serif",
+ "pbagenpg" },
+{ "arial,serif",
+ "Qr" },
+{ "arial,serif",
+ "Sreena" },
+{ "arial,serif",
+ "gnxrf" },
+{ "arial,serif",
+ "svany" },
+{ "arial,serif",
+ "PNEG" },
+{ "arial,serif",
+ "cbyr" },
+{ "arial,serif",
+ "Frnfba" },
+{ "arial,serif",
+ "cbvagf" },
+{ "arial,serif",
+ "yrnqre" },
+{ "arial,serif",
+ "fgneg" },
+{ "arial,serif",
+ "svefg" },
+{ "arial,serif",
+ "Zneyobeb" },
+{ "arial,serif",
+ "500" },
+{ "arial,serif",
+ "Zvynabivpu" },
+{ "arial,serif",
+ "vf" },
+{ "arial,serif",
+ "svefg" },
+{ "arial,serif",
+ "KSY" },
+{ "arial,serif",
+ "cvpx" },
+{ "arial,serif",
+ "Sbezre" },
+{ "arial,serif",
+ "Gnzcn" },
+{ "arial,serif",
+ "Onl" },
+{ "arial,serif",
+ "DO" },
+{ "arial,serif",
+ "fryrpgrq" },
+{ "arial,serif",
+ "ol" },
+{ "arial,serif",
+ "YN" },
+{ "arial,serif",
+ "Kgerzr" },
+{ "arial,serif",
+ "Xbheavxbin" },
+{ "arial,serif",
+ "pybfr" },
+{ "arial,serif",
+ "gbhearl" },
+{ "arial,serif",
+ "Ohg" },
+{ "arial,serif",
+ "Ehffvna" },
+{ "arial,serif",
+ "zhfg" },
+{ "arial,serif",
+ "orng" },
+{ "arial,serif",
+ "1" },
+{ "arial,serif",
+ "Uvatvf" },
+{ "arial,serif",
+ "Xerzyva" },
+{ "arial,serif",
+ "Phc" },
+{ "arial,serif",
+ "Wrgf\xe2""\x80""\x99""" },
+{ "arial,serif",
+ "zntvpny" },
+{ "arial,serif",
+ "eha" },
+{ "arial,serif",
+ "jvyy" },
+{ "arial,serif",
+ "pbagvahr" },
+{ "arial,serif",
+ "Abinprx\xe2""\x80""\x99""f" },
+{ "arial,serif",
+ "Cvpxf:" },
+{ "arial,serif",
+ "A.L." },
+{ "arial,serif",
+ "cynlvat" },
+{ "arial,serif",
+ "vafcverq" },
+{ "arial,serif",
+ "haqre" },
+{ "arial,serif",
+ "Tebu" },
+{ "arial,serif",
+ "ORFG" },
+{ "arial,serif",
+ "BS" },
+{ "arial,serif",
+ "ZFAOP" },
+{ "arial,serif",
+ "FCBEGF" },
+{ "arial,serif",
+ "Jub" },
+{ "arial,serif",
+ "yrg" },
+{ "arial,serif",
+ "gur" },
+{ "arial,serif",
+ "qbtf" },
+{ "arial,serif",
+ "bhg?" },
+{ "arial,serif",
+ "pnerf?" },
+{ "arial,serif",
+ "Pncnpvgl" },
+{ "arial,serif",
+ "Pebjq:" },
+{ "arial,serif",
+ "Gur" },
+{ "arial,serif",
+ "vagrenpgvir" },
+{ "arial,serif",
+ "fcbegf" },
+{ "arial,serif",
+ "pbyhza" },
+{ "arial,serif",
+ "vf" },
+{ "arial,serif",
+ "urer" },
+{ "arial,serif",
+ "Gnxr" },
+{ "arial,serif",
+ "gur" },
+{ "arial,serif",
+ "zbhaq" },
+{ "arial,serif",
+ "\xe2""\x80""\x94""" },
+{ "arial,serif",
+ "cvgpu" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "Frevrf" },
+{ "arial,serif",
+ "uvggref" },
+{ "arial,serif",
+ "Vagrenpgvir:" },
+{ "arial,serif",
+ "pbzcner" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "cvgpu" },
+{ "arial,serif",
+ "fryrpgvba" },
+{ "arial,serif",
+ "jvgu" },
+{ "arial,serif",
+ "Pnaqvbggv" },
+{ "arial,serif",
+ "Bylzcvp" },
+{ "arial,serif",
+ "ersyrpgvbaf" },
+{ "arial,serif",
+ "Qnl-ol-qnl" },
+{ "arial,serif",
+ "ybbx" },
+{ "arial,serif",
+ "ng" },
+{ "arial,serif",
+ "Flqarl" },
+{ "arial,serif",
+ "Tnzrf" },
+{ "arial,serif",
+ "Urvfzna" },
+{ "arial,serif",
+ "Gebcul" },
+{ "arial,serif",
+ "jngpu" },
+{ "arial,serif",
+ "Vagrenpgvir:" },
+{ "arial,serif",
+ "Nanylfvf" },
+{ "arial,serif",
+ "bs" },
+{ "arial,serif",
+ "enpr" },
+{ "arial,serif",
+ "sbe" },
+{ "arial,serif",
+ "pbyyrtr" },
+{ "arial,serif",
+ "sbbgonyy\xe2""\x80""\x99""f" },
+{ "arial,serif",
+ "gbc" },
+{ "arial,serif",
+ "cevmr" },
+{ "arial,serif",
+ "jvyy" },
+{ "arial,serif",
+ "Jrrx" },
+{ "arial,serif",
+ "9" },
+{ "arial,serif",
+ "ASY" },
+{ "arial,serif",
+ "tnzrf?" },
+{ "arial,serif",
+ "Yvir" },
+{ "arial,serif",
+ "Ibgr:" },
+{ "arial,serif",
+ "Cvpx" },
+{ "arial,serif",
+ "jvaaref" },
+{ "arial,serif",
+ "nyy" },
+{ "arial,serif",
+ "zngpuhcf" },
+{ "arial,serif",
+ "Zhpu" },
+{ "arial,serif",
+ "nqb" },
+{ "arial,serif",
+ "nobhg" },
+{ "arial,serif",
+ "Naan" },
+{ "arial,serif",
+ "arrqf" },
+{ "arial,serif",
+ "vzcebir" },
+{ "arial,serif",
+ "cnegf" },
+{ "arial,serif",
+ "ure" },
+{ "arial,serif",
+ "tnzr" },
+{ "arial,serif",
+ "Pbire" },
+{ "arial,serif",
+ "|" },
+{ "arial,serif",
+ "Onfronyy" },
+{ "arial,serif",
+ "AON" },
+{ "arial,serif",
+ "ASY" },
+{ "arial,serif",
+ "Zbgbe" },
+{ "arial,serif",
+ "Fcbegf" },
+{ "arial,serif",
+ "AUY" },
+{ "arial,serif",
+ "Tbys" },
+{ "arial,serif",
+ "Bylzcvpf" },
+{ "arial,serif",
+ "Graavf" },
+{ "arial,serif",
+ "APNN" },
+{ "arial,serif",
+ "Ubbcf" },
+{ "arial,serif",
+ "Bgure" },
+{ "arial,serif",
+ "Snagnfl" },
+{ "arial,serif",
+ "Grnz" },
+{ "arial,serif",
+ "Cntrf" },
+{ "arial,serif",
+ "Pung/OOF" },
+{ "arial,serif",
+ "ZFAOP" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "ZFA" },
+{ "arial,serif",
+ "Svaq" },
+{ "arial,serif",
+ "Nobhg" },
+{ "arial,serif",
+ "Uryc" },
+{ "arial,serif",
+ "Vaqrk" },
+{ "arial,serif",
+ "Pbby" },
+{ "arial,serif",
+ "Gbbyf" },
+{ "arial,serif",
+ "Jevgr" },
+{ "arial,serif",
+ "Hf" },
+{ "arial,serif",
+ "Wbof" },
+{ "arial,serif",
+ "Nqiregvfvat" },
+{ "arial,serif",
+ "ba" },
+{ "arial,serif",
+ "L2X" },
+{ "arial,serif",
+ "Fgngrzrag" },
+{ "arial,serif",
+ "Grezf," },
+{ "arial,serif",
+ "Pbaqvgvbaf," },
+{ "arial,serif",
+ "naq" },
+{ "arial,serif",
+ "Cevinpl" },
+{ "verdana,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial, helvetica,serif",
+ "Rkcrpg" },
+{ "arial, helvetica,serif",
+ "Urng" },
+{ "arial, helvetica,serif",
+ "gb" },
+{ "arial, helvetica,serif",
+ "pbby" },
+{ "arial, helvetica,serif",
+ "bss" },
+{ "arial, helvetica,serif",
+ "jvgubhg" },
+{ "arial, helvetica,serif",
+ "\xe2""\x80""\x98""Mb" },
+{ "arial, helvetica,serif",
+ "N-Ebq" },
+{ "arial, helvetica,serif",
+ "n" },
+{ "arial, helvetica,serif",
+ "Lnaxrr?" },
+{ "arial, helvetica,serif",
+ "Qba\xe2""\x80""\x99""g" },
+{ "arial, helvetica,serif",
+ "qvfzvff" },
+{ "arial, helvetica,serif",
+ "vg" },
+{ "arial, helvetica,serif",
+ "Cvgvab" },
+{ "arial, helvetica,serif",
+ "fubhyq" },
+{ "arial, helvetica,serif",
+ "whfg" },
+{ "arial, helvetica,serif",
+ "fheeraqre" },
+{ "arial, helvetica,serif",
+ "jvgu" },
+{ "arial, helvetica,serif",
+ "Prygvpf" },
+{ "serif",
+ "Trne" },
+{ "serif",
+ "Hc" },
+{ "Arial, helvetica,serif",
+ "Vafvqr" },
+{ "Arial, helvetica,serif",
+ "AOP" },
+{ "Arial, helvetica,serif",
+ "Fcbegf" },
+{ "serif",
+ "26," },
+{ "serif",
+ "jjj.zfaop.pbz," },
+{ "serif",
+ "215" },
+{ "serif",
+ "1, 26, jjj.zfaop.pbz, 215" },
+{ "arial,serif",
+ "AQ JVAF VA BIREGVZR" },
+{ "arial,serif",
+ "Pbzcyrgr pbirentr bs " },
+{ "arial,serif",
+ "jva bire Nve Sbepr" },
+{ "arial,serif",
+ "Ab. 8 Sybevqn " },
+{ "arial,serif",
+ "ubyqf bss Trbetvn" },
+{ "arial,serif",
+ "Cheqhr enyyvrf cnfg " },
+{ "arial,serif",
+ "Ab. 12 Buvb Fg." },
+{ "arial,serif",
+ "Gvtre gjb onpx " },
+{ "arial,serif",
+ "ragrevat svany ebhaq" },
+{ "arial,serif",
+ "Gjb Qrivyf fpber" },
+{ "arial,serif",
+ "4 tbnyf va 9-0 ebhg" },
+{ "arial,serif",
+ "Guvf cntr ynfg hcqngrq: 00:59 RG Bpg 29, 2000" },
+{ "arial,serif",
+ "BGURE GBC FGBEVRF" },
+{ "arial,serif",
+ "Qhpxf enyyl gb jva va qbhoyr BG" },
+{ "arial,serif",
+ "Ab. 7 Bertba orngf Nevmban Fgngr 56-55" },
+{ "arial,serif",
+ "Abegujrfgrea jvaf ba \xe2""\x80""\x98""Unvy Znel\xe2""\x80""\x99""" },
+{ "arial,serif",
+ "Qbja 35-14, Jvyqpngf jva ba 45-lneq qrfcrengvba cnff" },
+{ "arial,serif",
+ "Gvzorejbyirf\xe2""\x80""\x99"" Fzvgu va yvzob hagvy Guhefqnl" },
+{ "arial,serif",
+ "Neovgengbe jvyy urne nethzragf ba qrpvfvba gb ibvq pbagenpg" },
+{ "arial,serif",
+ "Qr Sreena gnxrf svany PNEG cbyr" },
+{ "arial,serif",
+ "Frnfba cbvagf yrnqre jvyy fgneg svefg va Zneyobeb 500" },
+{ "arial,serif",
+ "Zvynabivpu vf svefg KSY cvpx" },
+{ "arial,serif",
+ "Sbezre Gnzcn Onl DO fryrpgrq ol YN Kgerzr" },
+{ "arial,serif",
+ "Xbheavxbin pybfr gb svefg gbhearl jva" },
+{ "arial,serif",
+ "Ohg Ehffvna zhfg orng Ab. 1 Uvatvf va Xerzyva Phc svany" },
+{ "Arial, helvetica,serif",
+ "Vafvqr AOP Fcbegf" },
+{ "Verdana,serif",
+ "\xe2""\x80""\xa2""" },
+{ "serif",
+ " " },
+{ "arial, helvetica,serif",
+ "Rkcrpg Urng gb " },
+{ "arial, helvetica,serif",
+ "pbby bss jvgubhg \xe2""\x80""\x98""Mb" },
+{ "arial, helvetica,serif",
+ "N-Ebq n Lnaxrr?" },
+{ "arial, helvetica,serif",
+ "Qba\xe2""\x80""\x99""g qvfzvff vg" },
+{ "times new roman, times,serif",
+ "JRQARFQNL," },
+{ "times new roman, times,serif",
+ "ABIRZORE" },
+{ "times new roman, times,serif",
+ "8," },
+{ "times new roman, times,serif",
+ "2000" },
+{ "times new roman, times,serif",
+ "2:59" },
+{ "times new roman, times,serif",
+ "NZ" },
+{ "times new roman, times,serif",
+ "RG" },
+{ "times new roman, times,serif",
+ "|" },
+{ "times new roman, times,serif",
+ "Crefbanyvmr" },
+{ "times new roman, times,serif",
+ "Lbhe" },
+{ "times new roman, times,serif",
+ "Jrngure" },
+{ "serif",
+ "Serr" },
+{ "serif",
+ "Qnvyl" },
+{ "serif",
+ "Pebffjbeq" },
+{ "serif",
+ "Chmmyr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yngrfg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arjf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nepuvirf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gvcf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Wbof" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Erny" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Rfgngr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fubccvat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ohfvarff" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Rqvgbevny/Bc-Rq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vagreangvbany" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Angvbany" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arj" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Lbex" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Ertvba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ALG" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Sebag" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cntr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Bovghnevrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cbyvgvpf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Dhvpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fpvrapr/Urnygu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fcbegf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Grpu/Vagrearg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jrngure" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pbeerpgvbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Negf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nhgbzbovyrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Obbxf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pnegbbaf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pebffjbeq" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Sbehzf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Tnzrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yrneavat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Argjbex" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yvivat" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zntnmvar" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Cubgbf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Geniry" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jrrx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "va" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Erivrj" },
+{ "serif",
+ "Fpvrapr" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "Urnygu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pynffvsvrqf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Uryc" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zrqvn" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Xvg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Zbovyr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgber" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fvgr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Vaqrk" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Grkg" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Irefvba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arjfcncre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " -" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fhofpevor" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ng" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "50%" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Phfgbzre" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Freivpr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Qvtvgny" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gur" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Gvzrf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Pb." },
+{ "times new roman, times,serif",
+ "FUBCCVAT" },
+{ "times new roman, times,serif",
+ "Jvar" },
+{ "serif",
+ "3" },
+{ "serif",
+ "jvarf" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "$21." },
+{ "serif",
+ "$25" },
+{ "serif",
+ "be" },
+{ "serif",
+ "zber" },
+{ "serif",
+ "trgf" },
+{ "serif",
+ "SERR" },
+{ "serif",
+ "qryvirel." },
+{ "times new roman, times,serif",
+ "Wrjryel" },
+{ "serif",
+ "Fnir" },
+{ "serif",
+ "hc" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "$700" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "GNT" },
+{ "serif",
+ "jngpurf" },
+{ "serif",
+ "ure!" },
+{ "times new roman, times,serif",
+ "Yhkhel" },
+{ "times new roman, times,serif",
+ "Tvsgf" },
+{ "serif",
+ "Obfpn" },
+{ "serif",
+ "yrngure" },
+{ "serif",
+ "npprffbevrf" },
+{ "serif",
+ "uvz" },
+{ "serif",
+ "-fuvccrq" },
+{ "serif",
+ "serr!" },
+{ "times new roman, times,serif",
+ "Geniry" },
+{ "serif",
+ "Rkpyhfvir" },
+{ "serif",
+ "Geniry" },
+{ "serif",
+ "Arjfyrggref" },
+{ "serif",
+ "Serr!" },
+{ "times new roman, times,serif",
+ "Obbxf" },
+{ "serif",
+ "90%" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "snibevgr" },
+{ "serif",
+ "zntnmvarf" },
+{ "serif",
+ "Obbx" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "Gevc.pbz" },
+{ "times new roman, times,serif",
+ "Svar" },
+{ "times new roman, times,serif",
+ "Negf" },
+{ "serif",
+ "Neg" },
+{ "serif",
+ "ersyrpg" },
+{ "serif",
+ "vaqvivqhnyvgl." },
+{ "serif",
+ "(Erhgref)" },
+{ "serif",
+ "Trbetr" },
+{ "serif",
+ "J." },
+{ "serif",
+ "Ohfu" },
+{ "serif",
+ "jvgu" },
+{ "serif",
+ "uvf" },
+{ "serif",
+ "sngure" },
+{ "serif",
+ "va" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "Tbireabe'f" },
+{ "serif",
+ "Znafvba" },
+{ "serif",
+ "Nhfgva," },
+{ "serif",
+ "Grk." },
+{ "serif",
+ "Ol" },
+{ "serif",
+ "EVPUNEQ" },
+{ "serif",
+ "Y." },
+{ "serif",
+ "OREXR" },
+{ "serif",
+ "ALG" },
+{ "serif",
+ "HCQNGR," },
+{ "serif",
+ "2:48" },
+{ "serif",
+ "N.Z." },
+{ "serif",
+ "RG" },
+{ "serif",
+ "Trbetr" },
+{ "serif",
+ "Jnyxre" },
+{ "serif",
+ "Ohfu" },
+{ "serif",
+ "jnf" },
+{ "serif",
+ "ryrpgrq" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "43eq" },
+{ "serif",
+ "cerfvqrag" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "Havgrq" },
+{ "serif",
+ "Fgngrf" },
+{ "serif",
+ "ol" },
+{ "serif",
+ "bar" },
+{ "serif",
+ "gvtugrfg" },
+{ "serif",
+ "znetvaf" },
+{ "serif",
+ "va" },
+{ "serif",
+ "uvfgbel," },
+{ "serif",
+ "pebjavat" },
+{ "serif",
+ "n" },
+{ "serif",
+ "fcrpgnphyne" },
+{ "serif",
+ "rkprcgvbanyyl" },
+{ "serif",
+ "oevfx" },
+{ "serif",
+ "cbyvgvpny" },
+{ "serif",
+ "evfr" },
+{ "serif",
+ "bayl" },
+{ "serif",
+ "rvtug" },
+{ "serif",
+ "lrnef" },
+{ "serif",
+ "nsgre" },
+{ "serif",
+ "uvf" },
+{ "serif",
+ "bja" },
+{ "serif",
+ "sngure" },
+{ "serif",
+ "ghearq" },
+{ "serif",
+ "bhg" },
+{ "serif",
+ "Juvgr" },
+{ "serif",
+ "Ubhfr. " },
+{ "serif",
+ "Tb" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "Negvpyr" },
+{ "serif",
+ "Uvtu" },
+{ "serif",
+ "Qenzn" },
+{ "serif",
+ "Hasbyqf" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "Napube" },
+{ "serif",
+ "Qrfxf" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Nanylfvf:" },
+{ "serif",
+ "Na" },
+{ "serif",
+ "Ryrpgbengr" },
+{ "serif",
+ "Qvivqrq," },
+{ "serif",
+ "Irel" },
+{ "serif",
+ "Raq" },
+{ "serif",
+ "Qrzbpengf" },
+{ "serif",
+ "Yrnqvat" },
+{ "serif",
+ "Enprf" },
+{ "serif",
+ "Qrzbpeng" },
+{ "serif",
+ "Eboo" },
+{ "serif",
+ "Vf" },
+{ "serif",
+ "Hafrngrq" },
+{ "serif",
+ "Ivetvavn" },
+{ "serif",
+ "Uvyynel" },
+{ "serif",
+ "Pyvagba" },
+{ "serif",
+ "Ryrpgrq" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "Frangr" },
+{ "serif",
+ "Sebz" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "Lbex" },
+{ "serif",
+ "NQNZ" },
+{ "serif",
+ "ANTBHEARL" },
+{ "serif",
+ "SEBZ" },
+{ "serif",
+ "JRQARFQNL'F" },
+{ "serif",
+ "GVZRF" },
+{ "serif",
+ "Uvyynel" },
+{ "serif",
+ "Ebqunz" },
+{ "serif",
+ "Pyvagba" },
+{ "serif",
+ "frangbe" },
+{ "serif",
+ "sebz" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "Lbex" },
+{ "serif",
+ "Ghrfqnl," },
+{ "serif",
+ "raqvat" },
+{ "serif",
+ "16-zbagu" },
+{ "serif",
+ "fcrpgnpyr" },
+{ "serif",
+ "pnzcnvta" },
+{ "serif",
+ "rafhevat" },
+{ "serif",
+ "gung" },
+{ "serif",
+ "jvyy" },
+{ "serif",
+ "erznva" },
+{ "serif",
+ "Jnfuvatgba" },
+{ "serif",
+ "ure" },
+{ "serif",
+ "uhfonaq" },
+{ "serif",
+ "yrnirf" },
+{ "serif",
+ "Ubhfr." },
+{ "serif",
+ "Pbfgyl" },
+{ "serif",
+ "Pnzcnvta" },
+{ "serif",
+ "Cnlf" },
+{ "serif",
+ "Bss" },
+{ "serif",
+ "sbe" },
+{ "serif",
+ "Pbemvar" },
+{ "serif",
+ "Ryrpgvba" },
+{ "serif",
+ "Srngherf" },
+{ "serif",
+ "Ubj" },
+{ "serif",
+ "Erfhygf" },
+{ "serif",
+ "Ner" },
+{ "serif",
+ "Pbyyrpgrq" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "Cebwrpgrq" },
+{ "serif",
+ "Yvir" },
+{ "serif",
+ "Ryrpgvba" },
+{ "serif",
+ "Ivqrb" },
+{ "serif",
+ "R-znvy" },
+{ "serif",
+ "Hcqngrf" },
+{ "serif",
+ "Qvfphff" },
+{ "serif",
+ "Jvgu" },
+{ "serif",
+ "Tnvy" },
+{ "serif",
+ "Pbyyvaf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tber" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ohfu" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Bguref" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Hanffvtarq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gb" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "jva" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znc" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "238" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "279" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "0" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "21" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "270" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cbchyne" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ibgr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "44,460,882" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(48%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "44,752,269" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "3,340,814" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(4%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rkcnaqrq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Erfhygf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryrpgbeny" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "CERFVQRAG" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(nf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "bs" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "2:59" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "n.z.)" },
+{ "times new roman, times,serif",
+ "ERFHYGF" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qrz." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ercho." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Bgure" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Arj" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "47" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "53" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cevbe" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "45" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "55" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Put." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "+2" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "-2" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "H.F." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "FRANGR" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "208" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "226" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "1" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "211" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "223" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "-3" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "+3" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "UBHFR" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "k-" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pyvagba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(Q)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ynmvb" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(E)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "3,410,511" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "2,669,374" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "114,921" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(55%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(43%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(2%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ercbegvat:" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "99%" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "-" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ARJ" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "LBEX" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbemvar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Senaxf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "1,447,689" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "1,362,711" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "80,964" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(50%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(47%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "WREFRL" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "31" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "17" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "2" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "30" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "18" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "-1" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "TBIREABE" },
+{ "times new roman, times,serif",
+ "ZBER" },
+{ "times new roman, times,serif",
+ "URNQYVARF" },
+{ "serif",
+ "OHFVARFF" },
+{ "serif",
+ "Pvgvtebhc" },
+{ "serif",
+ "Erinzcf" },
+{ "serif",
+ "Yraqvat" },
+{ "serif",
+ "Havg" },
+{ "serif",
+ "Nibvq" },
+{ "serif",
+ "Nohfvir" },
+{ "serif",
+ "Cenpgvprf" },
+{ "serif",
+ "10:31" },
+{ "serif",
+ "c.z." },
+{ "serif",
+ "Znexrg" },
+{ "serif",
+ "Cynpr:" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "Fpenzoyr" },
+{ "serif",
+ "Svyy" },
+{ "serif",
+ "Shaq" },
+{ "serif",
+ "11:53" },
+{ "serif",
+ "Qeht" },
+{ "serif",
+ "Znxref" },
+{ "serif",
+ "Unir" },
+{ "serif",
+ "Irefvbaf" },
+{ "serif",
+ "Pbyq" },
+{ "serif",
+ "Erzrqvrf" },
+{ "serif",
+ "Pna" },
+{ "serif",
+ "Urnyguvre" },
+{ "serif",
+ "Sbbqf" },
+{ "serif",
+ "Uryc" },
+{ "serif",
+ "Obggbz" },
+{ "serif",
+ "Yvar?" },
+{ "serif",
+ "ZBER" },
+{ "serif",
+ "OHFVARFF" },
+{ "serif",
+ "ARJF" },
+{ "serif",
+ "ANGVBANY" },
+{ "serif",
+ "Nggnpx" },
+{ "serif",
+ "Pyrna" },
+{ "serif",
+ "Nve" },
+{ "serif",
+ "Npg" },
+{ "serif",
+ "Snygref" },
+{ "serif",
+ "Uvtu" },
+{ "serif",
+ "Pbheg" },
+{ "serif",
+ "11:51" },
+{ "serif",
+ "N" },
+{ "serif",
+ "Fvyvpba" },
+{ "serif",
+ "Inyyrl" },
+{ "serif",
+ "Genvyre" },
+{ "serif",
+ "Cnex" },
+{ "serif",
+ "11:38" },
+{ "serif",
+ "Vyyvabvf" },
+{ "serif",
+ "Qeviref" },
+{ "serif",
+ "Snpr" },
+{ "serif",
+ "Ergrfgvat" },
+{ "serif",
+ "Nsgre" },
+{ "serif",
+ "H.F." },
+{ "serif",
+ "Vadhvel" },
+{ "serif",
+ "Svaqf" },
+{ "serif",
+ "Yvprafrf" },
+{ "serif",
+ "Unq" },
+{ "serif",
+ "Orra" },
+{ "serif",
+ "Fbyq" },
+{ "serif",
+ "Fgngr" },
+{ "serif",
+ "Qrcg." },
+{ "serif",
+ "Nfxf" },
+{ "serif",
+ "Grknf" },
+{ "serif",
+ "Erivrj" },
+{ "serif",
+ "Pncvgny" },
+{ "serif",
+ "Pnfr" },
+{ "serif",
+ "ANGVBANY" },
+{ "serif",
+ "CBYVGVPF" },
+{ "serif",
+ "Pbemvar" },
+{ "serif",
+ "Jvaf" },
+{ "serif",
+ "Pbfgyl" },
+{ "serif",
+ "Ovq" },
+{ "serif",
+ "Frangr" },
+{ "serif",
+ "Wrefrl" },
+{ "serif",
+ "2:38" },
+{ "serif",
+ "n.z." },
+{ "serif",
+ "Qrzbpeng" },
+{ "serif",
+ "Eboo" },
+{ "serif",
+ "Vf" },
+{ "serif",
+ "Hafrngrq" },
+{ "serif",
+ "Ivetvavn" },
+{ "serif",
+ "ALG" },
+{ "serif",
+ "HCQNGR" },
+{ "serif",
+ "2:33" },
+{ "serif",
+ "Jvgu" },
+{ "serif",
+ "11" },
+{ "serif",
+ "Tbireabefuvcf" },
+{ "serif",
+ "Orvat" },
+{ "serif",
+ "Pbagrfgrq," },
+{ "serif",
+ "Qrzbpengf" },
+{ "serif",
+ "Znantr" },
+{ "serif",
+ "Ergnva" },
+{ "serif",
+ "5" },
+{ "serif",
+ "Frngf" },
+{ "serif",
+ "2:27" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Nanylfvf:" },
+{ "serif",
+ "Na" },
+{ "serif",
+ "Ryrpgbengr" },
+{ "serif",
+ "Qvivqrq," },
+{ "serif",
+ "Irel" },
+{ "serif",
+ "Raq" },
+{ "serif",
+ "3:00" },
+{ "serif",
+ "CBYVGVPF" },
+{ "serif",
+ "FCBEGF" },
+{ "serif",
+ "Xavpxf" },
+{ "serif",
+ "Fgehttyr" },
+{ "serif",
+ "Ubyq" },
+{ "serif",
+ "Ba" },
+{ "serif",
+ "1:51" },
+{ "serif",
+ "Pmrpuf" },
+{ "serif",
+ "Orng" },
+{ "serif",
+ "Bvyref" },
+{ "serif",
+ "Bja" },
+{ "serif",
+ "Tnzr" },
+{ "serif",
+ "1:34" },
+{ "serif",
+ "Enatref" },
+{ "serif",
+ "Zveebe" },
+{ "serif",
+ "12:00" },
+{ "serif",
+ "Vfynaqref" },
+{ "serif",
+ "Gvrq" },
+{ "serif",
+ "Svefg" },
+{ "serif",
+ "Cynpr" },
+{ "serif",
+ "12:05" },
+{ "serif",
+ "FCBEGF" },
+{ "serif",
+ "NEGF" },
+{ "serif",
+ "Ovggre" },
+{ "serif",
+ "Vagenzheny" },
+{ "serif",
+ "Ehzoyvatf" },
+{ "serif",
+ "Puvpntb'f" },
+{ "serif",
+ "Dhvrg" },
+{ "serif",
+ "Zhfrhz" },
+{ "serif",
+ "11:07" },
+{ "serif",
+ "Ebyy" },
+{ "serif",
+ "Bire," },
+{ "serif",
+ "Zbfrf:" },
+{ "serif",
+ "Vg'f" },
+{ "serif",
+ "Pragehz" },
+{ "serif",
+ "Yvapbyarafvf" },
+{ "serif",
+ "9:59" },
+{ "serif",
+ "'Znpx" },
+{ "serif",
+ "Znory':" },
+{ "serif",
+ "Oebnqjnl'f" },
+{ "serif",
+ "Erirerq" },
+{ "serif",
+ "Sybc," },
+{ "serif",
+ "Erfheerpgrq" },
+{ "serif",
+ "Ybir" },
+{ "serif",
+ "'Mnhoresy\xc3""\xb6""gr':" },
+{ "serif",
+ "Oyraqvat" },
+{ "serif",
+ "Pbzvp" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "Frevbhf" },
+{ "serif",
+ "va" },
+{ "serif",
+ "n" },
+{ "serif",
+ "Zntvp" },
+{ "serif",
+ "Cbgvba" },
+{ "serif",
+ "NEGF" },
+{ "serif",
+ "VAGREANGVBANY" },
+{ "serif",
+ "Fvnzrfr" },
+{ "serif",
+ "Gjva" },
+{ "serif",
+ "'Fnqyl" },
+{ "serif",
+ "Qvrf'" },
+{ "serif",
+ "Fvfgre" },
+{ "serif",
+ "1:06" },
+{ "serif",
+ "Lhtbfynif" },
+{ "serif",
+ "Ovpxre" },
+{ "serif",
+ "Bire" },
+{ "serif",
+ "Nezl" },
+{ "serif",
+ "Frperg" },
+{ "serif",
+ "Cbyvpr" },
+{ "serif",
+ "11:24" },
+{ "serif",
+ "Puvrs" },
+{ "serif",
+ "Zrqvngbe" },
+{ "serif",
+ "Zvqrnfg" },
+{ "serif",
+ "Yrnir" },
+{ "serif",
+ "Ivbyrapr" },
+{ "serif",
+ "Fvzzref," },
+{ "serif",
+ "Gubhtu" },
+{ "serif",
+ "Obzovat" },
+{ "serif",
+ "Snvyf" },
+{ "serif",
+ "VAGREANGVBANY" },
+{ "serif",
+ "A.L." },
+{ "serif",
+ "ERTVBA" },
+{ "serif",
+ "Erchoyvpnaf" },
+{ "serif",
+ "Znvagnva" },
+{ "serif",
+ "Pbageby" },
+{ "serif",
+ "2:47" },
+{ "serif",
+ "Pncgherf" },
+{ "serif",
+ "Ynmvb'f" },
+{ "serif",
+ "Ybat" },
+{ "serif",
+ "Vfynaq" },
+{ "serif",
+ "Frng" },
+{ "serif",
+ "Ubhfr" },
+{ "serif",
+ "Enpr" },
+{ "serif",
+ "Qvfgevpg" },
+{ "serif",
+ "Gbb" },
+{ "serif",
+ "Pybfr" },
+{ "serif",
+ "Qrpyner" },
+{ "serif",
+ "Jvaare" },
+{ "serif",
+ "2:51" },
+{ "serif",
+ "A.L." },
+{ "serif",
+ "ERTVBA" },
+{ "serif",
+ "FPVRAPR" },
+{ "serif",
+ "Nakvrgl" },
+{ "serif",
+ "Frra" },
+{ "serif",
+ "Grrantref" },
+{ "serif",
+ "Jub" },
+{ "serif",
+ "Fzbxr" },
+{ "serif",
+ "R." },
+{ "serif",
+ "Pbyv" },
+{ "serif",
+ "Fhfcrpgrq" },
+{ "serif",
+ "Vyyarff" },
+{ "serif",
+ "21" },
+{ "serif",
+ "Puvyqera" },
+{ "serif",
+ "FPVRAPR" },
+{ "serif",
+ "GRPUABYBTL" },
+{ "serif",
+ "Crgf.pbz" },
+{ "serif",
+ "Cynaf" },
+{ "serif",
+ "1:41" },
+{ "serif",
+ "Rqhpngbef" },
+{ "serif",
+ "Ghea" },
+{ "serif",
+ "Vagrearg" },
+{ "serif",
+ "Nqinaprq" },
+{ "serif",
+ "Cynprzrag" },
+{ "serif",
+ "Pynffrf" },
+{ "serif",
+ "Pvfpb" },
+{ "serif",
+ "Flfgrzf" },
+{ "serif",
+ "Cebivqrf" },
+{ "serif",
+ "Whfg" },
+{ "serif",
+ "Nobhg" },
+{ "serif",
+ "Bayl" },
+{ "serif",
+ "Rkpvgrzrag" },
+{ "serif",
+ "10:37" },
+{ "serif",
+ "GRPUABYBTL" },
+{ "serif",
+ "YVIVAT" },
+{ "serif",
+ "Fbybtar:" },
+{ "serif",
+ "Orlbaq" },
+{ "serif",
+ "Cnevf," },
+{ "serif",
+ "Obhagvshy" },
+{ "serif",
+ "Jvyqrearff" },
+{ "serif",
+ "10:00" },
+{ "serif",
+ "Znegva" },
+{ "serif",
+ "Cnee:" },
+{ "serif",
+ "Sebz" },
+{ "serif",
+ "Ternfr" },
+{ "serif",
+ "Tybff" },
+{ "serif",
+ "10:01" },
+{ "serif",
+ "Fgne" },
+{ "serif",
+ "Nhghza:" },
+{ "serif",
+ "Cbgrag," },
+{ "serif",
+ "Zntvp" },
+{ "serif",
+ "Fntr" },
+{ "serif",
+ "Gvunal" },
+{ "serif",
+ "Qerffrf" },
+{ "serif",
+ "Qbjntre" },
+{ "serif",
+ "10:59" },
+{ "serif",
+ "YVIVAT" },
+{ "serif",
+ "ONPX" },
+{ "serif",
+ "GB" },
+{ "serif",
+ "GBC" },
+{ "serif",
+ "VAFVQR" },
+{ "times,serif",
+ "QVAVAT" },
+{ "times,serif",
+ "N" },
+{ "times,serif",
+ "Obhagvshy" },
+{ "times,serif",
+ "Jvyqrearff" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "Fbybtar" },
+{ "serif",
+ "ertvba" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "Senapr" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "jvyq" },
+{ "serif",
+ "sebagvre," },
+{ "serif",
+ "n" },
+{ "serif",
+ "cynpr" },
+{ "serif",
+ "jurer" },
+{ "serif",
+ "pvivyvmrq" },
+{ "serif",
+ "Serapuzra" },
+{ "serif",
+ "pna" },
+{ "serif",
+ "rawbl" },
+{ "serif",
+ "jrrxraq" },
+{ "serif",
+ "uhagvat." },
+{ "serif",
+ "Jvyy" },
+{ "serif",
+ "Lbh" },
+{ "serif",
+ "Ibgr" },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "Lrne?" },
+{ "serif",
+ "Qvfphff" },
+{ "serif",
+ "Cerfvqrag" },
+{ "serif",
+ "Ovyy" },
+{ "serif",
+ "Pyvagba" },
+{ "serif",
+ "fcrnxf" },
+{ "serif",
+ "bhg" },
+{ "serif",
+ "zrnavat" },
+{ "serif",
+ "ibgr." },
+{ "serif",
+ "Erfcbaq" },
+{ "serif",
+ "zrffntr" },
+{ "serif",
+ "," },
+{ "serif",
+ "Nohmm." },
+{ "serif",
+ "Guvf" },
+{ "serif",
+ "Dhrfgvba" },
+{ "serif",
+ "Bgure" },
+{ "serif",
+ "Ernqref" },
+{ "serif",
+ "Nfx" },
+{ "serif",
+ "Nafjre" },
+{ "serif",
+ "Znexrgf:" },
+{ "serif",
+ "Ng" },
+{ "serif",
+ " Qbj" },
+{ "serif",
+ " Anfqnd" },
+{ "serif",
+ " F&C" },
+{ "serif",
+ "500" },
+{ "serif",
+ " Ehffryy" },
+{ "serif",
+ "2000" },
+{ "serif",
+ " ALFR" },
+{ "serif",
+ "10952.18" },
+{ "serif",
+ "3415.79" },
+{ "serif",
+ "1431.87" },
+{ "serif",
+ "506.01" },
+{ "serif",
+ "664.52" },
+{ "serif",
+ "-25.03" },
+{ "serif",
+ "-0.42" },
+{ "serif",
+ "-0.32" },
+{ "serif",
+ "2.05" },
+{ "serif",
+ "-0.87" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Fgbpx" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Dhbgrf" },
+{ "times new roman, times,serif",
+ "Ybbx" },
+{ "times new roman, times,serif",
+ "Hc" },
+{ "times new roman, times,serif",
+ "Flzoby" },
+{ "serif",
+ "Jver" },
+{ "serif",
+ "Freivprf" },
+{ "serif",
+ "Fhqna" },
+{ "serif",
+ "Bccbfvgvba" },
+{ "serif",
+ "Fnlf" },
+{ "serif",
+ "Svtugvat" },
+{ "serif",
+ "Entrf" },
+{ "serif",
+ "Rnfg" },
+{ "serif",
+ "2:43" },
+{ "serif",
+ "Pynfurf" },
+{ "serif",
+ "Syner" },
+{ "serif",
+ "Orgjrra" },
+{ "serif",
+ "Cnyrfgvavnaf," },
+{ "serif",
+ "Vfenryv" },
+{ "serif",
+ "Nezl" },
+{ "serif",
+ "Angvbajvqr" },
+{ "serif",
+ "Cerfvqragvny" },
+{ "serif",
+ "Ergheaf" },
+{ "serif",
+ "2:41" },
+{ "serif",
+ "Zvyyvbaf" },
+{ "serif",
+ "Bayvar" },
+{ "serif",
+ "Tber," },
+{ "serif",
+ "Enpr" },
+{ "serif",
+ "2:40" },
+{ "serif",
+ "GI" },
+{ "serif",
+ "Argf" },
+{ "serif",
+ "Qrpyner" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "2:35" },
+{ "serif",
+ "Fhccbegref" },
+{ "serif",
+ "Purre" },
+{ "serif",
+ "Cebwrpgvbaf" },
+{ "serif",
+ "2:45" },
+{ "serif",
+ "SEBZ" },
+{ "serif",
+ "ERHGREF" },
+{ "serif",
+ "NC" },
+{ "times new roman, times,serif",
+ "ALP'f" },
+{ "times new roman, times,serif",
+ "Orfg" },
+{ "times new roman, times,serif",
+ "Fnxr" },
+{ "times new roman, times,serif",
+ "Onef" },
+{ "times new roman, times,serif",
+ "Gnyxf" },
+{ "times new roman, times,serif",
+ "&" },
+{ "times new roman, times,serif",
+ "Ernqvatf" },
+{ "times new roman, times,serif",
+ "Guvf" },
+{ "times new roman, times,serif",
+ "Jrrx" },
+{ "times new roman, times,serif",
+ "Ncnegzrag" },
+{ "times new roman, times,serif",
+ "Rail:" },
+{ "times new roman, times,serif",
+ "N" },
+{ "times new roman, times,serif",
+ "GevOrPn" },
+{ "times new roman, times,serif",
+ "Qernz" },
+{ "times new roman, times,serif",
+ "Ybsg" },
+{ "times new roman, times,serif",
+ "GXGF" },
+{ "times new roman, times,serif",
+ "Qvfpbhagf" },
+{ "times new roman, times,serif",
+ "Zbivrf:" },
+{ "times new roman, times,serif",
+ "Pevgvpf'" },
+{ "times new roman, times,serif",
+ "Cvpxf" },
+{ "times new roman, times,serif",
+ "Riragf" },
+{ "times new roman, times,serif",
+ "TB" },
+{ "times new roman, times,serif",
+ "GB" },
+{ "times new roman, times,serif",
+ "ARJLBEXGBQNL.PBZ" },
+{ "serif",
+ "Grpuabybtl" },
+{ "serif",
+ "Frpgvba" },
+{ "times,serif",
+ "Abi." },
+{ "times,serif",
+ "8," },
+{ "times,serif",
+ "1960" },
+{ "serif",
+ "Ba" },
+{ "serif",
+ "Abi." },
+{ "serif",
+ "8," },
+{ "serif",
+ "1960," },
+{ "serif",
+ "Znffnpuhfrggf" },
+{ "serif",
+ "Frangbe" },
+{ "serif",
+ "Wbua" },
+{ "serif",
+ "S." },
+{ "serif",
+ "Xraarql" },
+{ "serif",
+ "qrsrngrq" },
+{ "serif",
+ "Ivpr" },
+{ "serif",
+ "Evpuneq" },
+{ "serif",
+ "Z." },
+{ "serif",
+ "Avkba" },
+{ "serif",
+ "cerfvqrapl." },
+{ "serif",
+ "(" },
+{ "serif",
+ "Frr" },
+{ "serif",
+ "guvf" },
+{ "serif",
+ "sebag" },
+{ "serif",
+ "cntr." },
+{ "serif",
+ ")" },
+{ "serif",
+ "Va" },
+{ "serif",
+ "1900," },
+{ "serif",
+ "Znetnerg" },
+{ "serif",
+ "Zvgpuryy," },
+{ "serif",
+ "Nzrevpna" },
+{ "serif",
+ "nhgube" },
+{ "serif",
+ "\"Tbar" },
+{ "serif",
+ "Jvaq\"," },
+{ "serif",
+ "jnf" },
+{ "serif",
+ "obea." },
+{ "serif",
+ "(Ernq" },
+{ "serif",
+ "nobhg" },
+{ "serif",
+ "ure" },
+{ "serif",
+ "yvsr.)" },
+{ "serif",
+ "Frevbhf" },
+{ "serif",
+ "Cbgvba" },
+{ "serif",
+ "Gvzrf" },
+{ "serif",
+ "Bayvar" },
+{ "serif",
+ "Fgber" },
+{ "times new roman, times,serif",
+ "Pbclevtug" },
+{ "times new roman, times,serif",
+ "2000" },
+{ "times new roman, times,serif",
+ "Gur" },
+{ "times new roman, times,serif",
+ "Arj" },
+{ "times new roman, times,serif",
+ "Lbex" },
+{ "times new roman, times,serif",
+ "Gvzrf" },
+{ "times new roman, times,serif",
+ "Pbzcnal" },
+{ "times new roman, times,serif",
+ "Cevinpl" },
+{ "times new roman, times,serif",
+ "Vasbezngvba" },
+{ "serif",
+ "Jvyy Lbh " },
+{ "serif",
+ "Ibgr Guvf " },
+{ "serif",
+ "Cerfvqrag Ovyy " },
+{ "serif",
+ "Pyvagba fcrnxf bhg " },
+{ "serif",
+ "ba gur zrnavat bs " },
+{ "serif",
+ "gur ibgr. " },
+{ "serif",
+ "Erfcbaq " },
+{ "serif",
+ "gb uvf zrffntr" },
+{ "serif",
+ ", va " },
+{ "times new roman, times,serif",
+ "JRQARFQNL, ABIRZORE 8, 2000 " },
+{ "times new roman, times,serif",
+ "2:59 NZ RG | " },
+{ "times new roman, times,serif",
+ "Crefbanyvmr Lbhe Jrngure" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yngrfg Arjf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Nepuvirf " },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Erny Rfgngr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Arj Lbex Ertvba" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "ALG Sebag Cntr" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Dhvpx Arjf" },
+{ "Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Yrneavat Argjbex" },
+{ "Arial,Helvetica,sans-serif,serif",
+ "Jrrx va Erivrj" },
+{ "serif",
+ "Trbetr J. Ohfu jvgu uvf sngure va gur Tbireabe'f " },
+{ "serif",
+ "Znafvba va Nhfgva, Grk." },
+{ "serif",
+ "Ol EVPUNEQ Y. OREXR" },
+{ "serif",
+ "ALG HCQNGR, 2:48 N.Z. RG" },
+{ "serif",
+ "Trbetr Jnyxre Ohfu jnf ryrpgrq gur 43eq" },
+{ "serif",
+ "cerfvqrag bs gur Havgrq Fgngrf ol bar bs " },
+{ "serif",
+ "gur gvtugrfg znetvaf va uvfgbel, pebjavat n " },
+{ "serif",
+ "fcrpgnphyne naq rkprcgvbanyyl oevfx " },
+{ "serif",
+ "cbyvgvpny evfr bayl rvtug lrnef nsgre uvf bja " },
+{ "serif",
+ "sngure jnf ghearq bhg bs gur Juvgr" },
+{ "serif",
+ "Ryrpgvba Srngherf" },
+{ "serif",
+ "Ubj gur Erfhygf Ner Pbyyrpgrq " },
+{ "serif",
+ "naq Cebwrpgrq" },
+{ "serif",
+ "Yvir Ryrpgvba Ivqrb" },
+{ "serif",
+ "R-znvy Hcqngrf" },
+{ "serif",
+ "Qvfphff gur Erfhygf Jvgu Tnvy " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ " (nf bs 2:59 n.z.)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryrpgbeny Ibgr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cbchyne Ibgr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "44,460,882 " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "44,752,269 " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "0" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "3,340,814 " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gb jva" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rkcnaqrq Erfhygf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "H.F. FRANGR" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "H.F. UBHFR" },
+{ "times,serif",
+ "N Obhagvshy " },
+{ "serif",
+ "Gur Fbybtar " },
+{ "serif",
+ "ertvba bs Senapr " },
+{ "serif",
+ "vf gur jvyq " },
+{ "serif",
+ "sebagvre, n cynpr jurer pvivyvmrq " },
+{ "serif",
+ "Serapuzra pna rawbl n jrrxraq " },
+{ "serif",
+ "bs uhagvat." },
+{ "serif",
+ " Qbj" },
+{ "serif",
+ " Anfqnd" },
+{ "serif",
+ " F&C 500" },
+{ "serif",
+ " Ehffryy 2000" },
+{ "serif",
+ " ALFR" },
+{ "Lucida Grande",
+ "Tb" },
+{ "times new roman, times,serif",
+ "Ybbx Hc" },
+{ "serif",
+ "27," },
+{ "serif",
+ "jjj.algvzrf.pbz," },
+{ "serif",
+ "554" },
+{ "serif",
+ "1, 27, jjj.algvzrf.pbz, 554" },
+{ "serif",
+ "Cbyvgvpf" },
+{ "serif",
+ "Grkg" },
+{ "serif",
+ "Irefvba" },
+{ "serif",
+ "Cbyvgvpny" },
+{ "serif",
+ "Cbvagf" },
+{ "serif",
+ "Sebz" },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "Arj" },
+{ "serif",
+ "Lbex" },
+{ "serif",
+ "Gvzrf" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "Jro" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "NOPARJF.pbz" },
+{ "serif",
+ "Jngpu" },
+{ "serif",
+ "Jropnfg" },
+{ "serif",
+ "yvir" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "1:30" },
+{ "serif",
+ "nyy" },
+{ "serif",
+ "yngrfg" },
+{ "serif",
+ "cbyvgvpny" },
+{ "serif",
+ "arjf." },
+{ "serif",
+ "FVTA" },
+{ "serif",
+ "HC" },
+{ "serif",
+ "SBE" },
+{ "serif",
+ "R-ZNVY" },
+{ "serif",
+ "Pnzcnvta" },
+{ "serif",
+ "Pbhagqbja" },
+{ "serif",
+ "N" },
+{ "serif",
+ "qnvyl" },
+{ "serif",
+ "cerivrj" },
+{ "serif",
+ "cerfvqragvny" },
+{ "serif",
+ "enpr," },
+{ "serif",
+ "jrrxyl" },
+{ "serif",
+ "vafvtugf" },
+{ "serif",
+ "sebz" },
+{ "serif",
+ "ercbegref" },
+{ "serif",
+ "pnzcnvta" },
+{ "serif",
+ "genvy." },
+{ "serif",
+ "Nq" },
+{ "serif",
+ "nqf" },
+{ "serif",
+ "nanylfvf." },
+{ "serif",
+ "Cbyyf" },
+{ "serif",
+ "Lbex" },
+{ "serif",
+ "Gvzrf/POF" },
+{ "serif",
+ "Cbyy." },
+{ "serif",
+ "ERYNGRQ" },
+{ "serif",
+ "NEGVPYRF" },
+{ "serif",
+ "Cbyvgvpf" },
+{ "serif",
+ "CERFVQRAGVNY" },
+{ "serif",
+ "IVQRB" },
+{ "serif",
+ "FRNEPU" },
+{ "serif",
+ "Ivqrb" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Ragre" },
+{ "serif",
+ "n" },
+{ "serif",
+ "frnepu" },
+{ "serif",
+ "jbeq" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Gvcf" },
+{ "serif",
+ "PNAQVQNGR" },
+{ "serif",
+ "SVAQRE" },
+{ "serif",
+ "Ragre" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "mvc" },
+{ "serif",
+ "pbqr" },
+{ "serif",
+ "svaq" },
+{ "serif",
+ "ryrpgvbaf" },
+{ "serif",
+ "nern." },
+{ "serif",
+ "NAQ" },
+{ "serif",
+ "NHQVB" },
+{ "serif",
+ "Yvir" },
+{ "serif",
+ "Qnl" },
+{ "serif",
+ "Pbirentr" },
+{ "serif",
+ "ENPR" },
+{ "serif",
+ "Pnaqvqngr" },
+{ "serif",
+ "Cebsvyrf" },
+{ "serif",
+ "Trbetr" },
+{ "serif",
+ "J." },
+{ "serif",
+ "Ohfu" },
+{ "serif",
+ "(E)" },
+{ "serif",
+ "Eryngrq" },
+{ "serif",
+ "Negvpyrf" },
+{ "serif",
+ "(Cnfg" },
+{ "serif",
+ "Gjb" },
+{ "serif",
+ "Jrrxf)" },
+{ "serif",
+ "Ny" },
+{ "serif",
+ "Tber" },
+{ "serif",
+ "(Q)" },
+{ "serif",
+ "Cngevpx" },
+{ "serif",
+ "W." },
+{ "serif",
+ "Ohpunana" },
+{ "serif",
+ "(Ersbez)" },
+{ "serif",
+ "Enycu" },
+{ "serif",
+ "Anqre" },
+{ "serif",
+ "(Terra" },
+{ "serif",
+ "Cnegl)" },
+{ "serif",
+ "Zber" },
+{ "serif",
+ "Pnaqvqngrf" },
+{ "serif",
+ "Sbehz" },
+{ "serif",
+ "Jung" },
+{ "serif",
+ "vffhrf" },
+{ "serif",
+ "jvyy" },
+{ "serif",
+ "qrpvqr" },
+{ "serif",
+ "enpr?" },
+{ "serif",
+ "FRANGR" },
+{ "serif",
+ "ENPRF" },
+{ "serif",
+ "H.F." },
+{ "serif",
+ "Frangr" },
+{ "serif",
+ "--" },
+{ "serif",
+ "Wrefrl" },
+{ "serif",
+ "Pbaarpgvphg" },
+{ "serif",
+ "Jub" },
+{ "serif",
+ "unf" },
+{ "serif",
+ "rqtr" },
+{ "serif",
+ "Pyvagba/Ynmvb" },
+{ "serif",
+ "GUR" },
+{ "serif",
+ "GVZRF" },
+{ "serif",
+ "RQVGBEVNYF" },
+{ "serif",
+ "Ryrpgvba" },
+{ "serif",
+ "Raqbefrzragf" },
+{ "serif",
+ "VFFHRF" },
+{ "serif",
+ "VA" },
+{ "serif",
+ "QRCGU" },
+{ "serif",
+ "Yrneavat" },
+{ "serif",
+ "Argjbex:" },
+{ "serif",
+ "2000" },
+{ "serif",
+ "Yrffba" },
+{ "serif",
+ "cynaf," },
+{ "serif",
+ "pebffjbeqf," },
+{ "serif",
+ "Nfx" },
+{ "serif",
+ "Ercbegre" },
+{ "serif",
+ "dhvmmrf." },
+{ "serif",
+ "Gur" },
+{ "serif",
+ "Vagrearg" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Pnzcnvtaf" },
+{ "serif",
+ "Vagrearg'f" },
+{ "serif",
+ "vzcnpg" },
+{ "serif",
+ "cbyvgvpf." },
+{ "serif",
+ "Erfhygf" },
+{ "serif",
+ "Pnyvsbeavn" },
+{ "serif",
+ "Pnzcnvtaf" },
+{ "serif",
+ "PNYVSBEAVN" },
+{ "serif",
+ "Cpg." },
+{ "serif",
+ "ibgr" },
+{ "serif",
+ "pbhagrq" },
+{ "serif",
+ "3%" },
+{ "serif",
+ "185,870" },
+{ "serif",
+ "(52%)" },
+{ "serif",
+ "159,150" },
+{ "serif",
+ "(44%)" },
+{ "serif",
+ "Tber" },
+{ "serif",
+ "54" },
+{ "serif",
+ "0" },
+{ "serif",
+ "Abg" },
+{ "serif",
+ "nffvtarq" },
+{ "serif",
+ "Ny" },
+{ "serif",
+ "QRZBPENG" },
+{ "serif",
+ "ERCHOYVPNA" },
+{ "serif",
+ "RYRPGBENY" },
+{ "serif",
+ "IBGR" },
+{ "serif",
+ "CBCHYNE" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "CERFVQRAG" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "11:30" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "c.z.)" },
+{ "serif",
+ "Tbireabe" },
+{ "serif",
+ "Tbi." },
+{ "serif",
+ "Tenl" },
+{ "serif",
+ "Qnivf'f" },
+{ "serif",
+ "(Q)" },
+{ "serif",
+ "frng" },
+{ "serif",
+ "vf" },
+{ "serif",
+ "2002." },
+{ "serif",
+ "H.F." },
+{ "serif",
+ "Frangr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oevna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Errf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "AYC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvnar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Grzcyva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "NVC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvnaar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Srvafgrva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qrz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(v)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnvy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yvtugsbbg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yvo" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbfr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnznubeg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "EC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zrqrn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Orawnzva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tea" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gbz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnzcoryy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "TBC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(0%)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "0%" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "PNYVSBEAVN" },
+{ "serif",
+ "Frangbe" },
+{ "serif",
+ "Oneonen" },
+{ "serif",
+ "Obkre'f" },
+{ "serif",
+ "2004." },
+{ "serif",
+ "Ubhfr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Purely" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xervre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rzvy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebffv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvxr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gubzcfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cnzryn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryvmbaqb," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ehffry" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Punfr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvfgevpg" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Puneyrf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znegva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbua" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ZpQrezbgg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zbetna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jnyyl" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uretre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Obo" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xrag," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Punaavat" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbarf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qbhtynf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Bfr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ghzn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "3" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qbbyvggyr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znex" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Abeoret," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Eboreg" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Enl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jvyyvnz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Serl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "4" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xrefrl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Phyyrar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ynat," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xra" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nqnzf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cnlar," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zngfhv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "5" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nyna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oneerpn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Whfgva" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zbfpbfb," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ZpNhyvssr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ylaa" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jbbyfrl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Evpuneq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onegba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "6" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Puevfgbcure" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubsszna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Trbetr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvyyre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znegva" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fcebhy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "7" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nqnz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fcnexf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qnivq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fzvgufgrva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Revx" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onhzna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Anapl" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Crybfv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "8" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nearm" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jnfuvatgba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oneonen" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yrr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryyra" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wrssreqf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Serq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Sbyqinel," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "9" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pynhqr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uhgpuvfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gnhfpure," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Inyrevr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnaybvf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "10" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xherl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xnguela" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ehffbj," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cbzob," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fnagbf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "11" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yrff," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnemn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Evsxva" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Lbhat," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ynagbf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "12" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tehaqznaa," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Sbegarl" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Crgr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgnex," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubjneq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zben," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnzrf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tbrgm," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gvzbgul" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubruare," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "13" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Naan" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rfubb," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ovyy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Dhenvfuv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oynpx," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbfrcu" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qrua," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "14" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tbearl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jvzzref," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wvz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Phaarra," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubaqn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "15" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qraavf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Hzcuerff," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rqjneq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xyrva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Trar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gunla," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Mbr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ybstera," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "16" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pyvag" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ratyre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "R." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Penvt" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbssva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yneel" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Sragba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Evpx" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tneergg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fnz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Snee," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fpbgg" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Unegyrl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnel" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbaqvg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cntr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Evfxva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgrir" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jvyfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebfraoret," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rqzba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xnvfre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryvmnorgu" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gnlybe," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Enqnabivpu," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "19" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Neabyq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xevrtonhz," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pny" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qbbyrl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Evpu" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebqevthrm," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jnygre" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ehruyvt," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "20" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gubznf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znavba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znegvarm," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvpx" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cbegre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "W." },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pneybf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nthveer," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Shepvavgr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "22" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ybvf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnccf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgbxre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnel" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fnivgpu," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rygba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnyyrtyl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvpunry" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnfr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebtre" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Crroyrf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgrcura" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubfcbqne," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "23" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oenq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Furezna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wreel" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qblyr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Whna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Phqqrur," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "24" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oehpr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Npxre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ZpXrba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zrjf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fznyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fvq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tbyq," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "25" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Sneyrl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbffnpx," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Orezna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "26" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fpuvss," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebtna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvevnz" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Grq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oebja," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "27" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qervre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnavpr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Aryfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnl" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Unlgnf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ynjerapr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nyyvfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Enaqnyy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jrvffohpu," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "28" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pheevina," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Urael" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jnkzna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnpx" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Naqrefba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fpvyrccv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "29" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Urnear," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnfba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Urngu," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gbal" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tbff," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Knivre" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Orpreen," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uvyqn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fbyvf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xevfgn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yvroret-Jbat," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ZpThver," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tevssva," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jrore," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Whyvna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvkba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xngul" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jvyyvnzfba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Enfuvrq" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wvoev," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "32" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yhpvyyr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Eblony-Nyyneq," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Anguna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Penqqbpx," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jnlar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uneche," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "33" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tenpr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ancbyvgnab," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Whyvn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fvzba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnanyrf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "34" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pney" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "ZpTvyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tbeqba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zrtb," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znkvar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jngref," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qhafgna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "35" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qnavry" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wnar" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Unezna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xbabcxn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zngg" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Beangv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fgrira" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xhlxraqnyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "36" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ureo" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Crgref," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Whnavgn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvyyraqre-ZpQbanyq," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znetnerg" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tynmre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ireaba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ina," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "37" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Treevr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fpuvcfxr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Artyvn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xnera" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oynfqryy-Jvyxvafba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ubea," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "38" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Eblpr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tvyy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xnary," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xrvgu" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnaa," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Eba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wriavat," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "39" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Senax" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fpuzvg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yvaqoret," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yrjvf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "40" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xenzre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebqbysb" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Snivyn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "41" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ryv" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Cvebmmv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tjla" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onpn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onyyneq," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "42" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Errq," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pnyireg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ang" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Nqnz," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "43" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fzvgu," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zrhre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Znel" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Obab," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Bqra," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "44" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbafgnapr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Orggba," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qnan" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebuenonpure," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uhyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pevfryy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tybevn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ghpuzna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ratjnyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Yberggn" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fnapurm," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Obqqvr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "46" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pbk," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Abyna," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Vevf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tenunz," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qneeryy" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Vffn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Rqqvr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ebfr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Pboo," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Crgre" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xbhiryvf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Funeba" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zvyrf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "48" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ovyoenl," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qbevf" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Fhfna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qnivf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gnuve" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ounggv," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "49" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvivar," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Svyare," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Jvyybhtuol," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "YrrNaa" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Xraqnyy," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "50" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zhur," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Revp" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Obheqrggr," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oneenmn," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Enaql" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Phaavatunz," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "51" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Onexnpf," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qhapna" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Uhagre," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Orabvg," },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "52" },
+{ "Lucida Grande",
+ "FGNGR" },
+{ "Lucida Grande",
+ "ERFHYGF" },
+{ "Lucida Grande",
+ "Nyn." },
+{ "Lucida Grande",
+ "Nynfxn" },
+{ "Lucida Grande",
+ "Nevm." },
+{ "Lucida Grande",
+ "Nex." },
+{ "Lucida Grande",
+ "Pnyvs." },
+{ "Lucida Grande",
+ "Pbyb." },
+{ "Lucida Grande",
+ "Pbaa." },
+{ "Lucida Grande",
+ "Q.P." },
+{ "Lucida Grande",
+ "Qry." },
+{ "Lucida Grande",
+ "Syn." },
+{ "Lucida Grande",
+ "Tn." },
+{ "Lucida Grande",
+ "Unjnvv" },
+{ "Lucida Grande",
+ "Vqnub" },
+{ "Lucida Grande",
+ "Vyy." },
+{ "Lucida Grande",
+ "Vaq." },
+{ "Lucida Grande",
+ "Vbjn" },
+{ "Lucida Grande",
+ "Xna." },
+{ "Lucida Grande",
+ "Xl." },
+{ "Lucida Grande",
+ "Yn." },
+{ "Lucida Grande",
+ "Znvar" },
+{ "Lucida Grande",
+ "Zq." },
+{ "Lucida Grande",
+ "Znff." },
+{ "Lucida Grande",
+ "Zvpu." },
+{ "Lucida Grande",
+ "Zvaa." },
+{ "Lucida Grande",
+ "Zvff." },
+{ "Lucida Grande",
+ "Zb." },
+{ "Lucida Grande",
+ "Zbag." },
+{ "Lucida Grande",
+ "Aro." },
+{ "Lucida Grande",
+ "Ari." },
+{ "Lucida Grande",
+ "A.U." },
+{ "Lucida Grande",
+ "A.W." },
+{ "Lucida Grande",
+ "A.Z." },
+{ "Lucida Grande",
+ "A.L." },
+{ "Lucida Grande",
+ "A.P." },
+{ "Lucida Grande",
+ "A.Q." },
+{ "Lucida Grande",
+ "Buvb" },
+{ "Lucida Grande",
+ "Bxyn." },
+{ "Lucida Grande",
+ "Ber." },
+{ "Lucida Grande",
+ "Craa." },
+{ "Lucida Grande",
+ "E.V." },
+{ "Lucida Grande",
+ "F.P." },
+{ "Lucida Grande",
+ "F.Q." },
+{ "Lucida Grande",
+ "Graa." },
+{ "Lucida Grande",
+ "Grknf" },
+{ "Lucida Grande",
+ "Hgnu" },
+{ "Lucida Grande",
+ "Ig." },
+{ "Lucida Grande",
+ "In." },
+{ "Lucida Grande",
+ "Jnfu." },
+{ "Lucida Grande",
+ "J.I." },
+{ "Lucida Grande",
+ "Jvf." },
+{ "Lucida Grande",
+ "Jlb." },
+{ "times new roman, times,serif",
+ "ERTVBA" },
+{ "times new roman, times,serif",
+ "OL" },
+{ "serif",
+ "Abegurnfg" },
+{ "serif",
+ "Zvqjrfg" },
+{ "serif",
+ "Fbhgu" },
+{ "serif",
+ "Jrfg" },
+{ "serif",
+ "Fgngr" },
+{ "serif",
+ "Vasbezngvba" },
+{ "serif",
+ "RYRPGVBA" },
+{ "serif",
+ "PRAGRE" },
+{ "serif",
+ "Pnyvsbeavn" },
+{ "serif",
+ "Pbaterffvbany" },
+{ "serif",
+ "FVGRF" },
+{ "serif",
+ "Erchoyvpna" },
+{ "serif",
+ "Cnegl" },
+{ "serif",
+ "Qrzbpengvp" },
+{ "serif",
+ "Pbclevtug" },
+{ "serif",
+ "Gvzrf" },
+{ "serif",
+ "Pbzcnal" },
+{ "serif",
+ "Cbyvgvpf Grkg Irefvba" },
+{ "serif",
+ "Sebz Gur Arj Lbex " },
+{ "serif",
+ "Gvzrf ba gur Jro naq " },
+{ "serif",
+ "Jngpu gur Jropnfg yvir ng 1:30 " },
+{ "serif",
+ "c.z. sbe nyy gur yngrfg cbyvgvpny " },
+{ "serif",
+ "arjf." },
+{ "serif",
+ "FVTA HC SBE R-ZNVY" },
+{ "serif",
+ "Pnzcnvta Pbhagqbja" },
+{ "serif",
+ "N qnvyl cerivrj bs cerfvqragvny " },
+{ "serif",
+ "enpr, jvgu jrrxyl vafvtugf sebz " },
+{ "serif",
+ "ercbegref ba gur pnzcnvta genvy." },
+{ "serif",
+ "Gur Nq Pnzcnvta" },
+{ "serif",
+ "Gur yngrfg nqf naq nanylfvf." },
+{ "serif",
+ "Pnyvsbeavn Pnzcnvtaf" },
+{ "serif",
+ " " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "(nf bs 11:30 c.z.)" },
+{ "serif",
+ "CBCHYNE IBGR" },
+{ "serif",
+ "RYRPGBENY IBGR" },
+{ "serif",
+ "bs ibgr" },
+{ "serif",
+ "Trbetr J. Ohfu" },
+{ "serif",
+ "Ny Tber" },
+{ "serif",
+ "Tbi. Tenl Qnivf'f (Q) frng vf hc va 2002." },
+{ "serif",
+ "H.F. Frangr" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "H.F. FRANGR - PNYVSBEAVN " },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Ercbegvat: 0%" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Oevna Errf, AYC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvnar Grzcyva, NVC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Qvnaar Srvafgrva, Qrz (v)" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Tnvy Yvtugsbbg, Yvo" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Wbfr Pnznubeg, EC" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Zrqrn Orawnzva, Tea" },
+{ "verdana, arial, helvetica, sans-serif,serif",
+ "Gbz Pnzcoryy, TBC" },
+{ "Lucida Grande",
+ "FGNGR ERFHYGF" },
+{ "times new roman, times,serif",
+ "ERTVBA OL ERTVBA" },
+{ "serif",
+ "\xe2""\x80""\xa2"" " },
+{ "serif",
+ "28," },
+{ "serif",
+ "jjj.algvzrf.pbz_Gnoyr," },
+{ "serif",
+ "354" },
+{ "serif",
+ "1, 28, jjj.algvzrf.pbz_Gnoyr, 354" },
+{ "geneva,arial,serif",
+ "Pyvpx" },
+{ "geneva,arial,serif",
+ "Urer" },
+{ "geneva,arial,serif",
+ "!" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Trg" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Dhbgrf" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "&" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Arjf" },
+{ "ms sans serif,helvetica,serif",
+ "Ragre" },
+{ "ms sans serif,helvetica,serif",
+ "flzoby:" },
+{ "arial,serif",
+ "Tb" },
+{ "ms sans serif,helvetica,serif",
+ "Qba'g" },
+{ "ms sans serif,helvetica,serif",
+ "xabj" },
+{ "ms sans serif,helvetica,serif",
+ "gur" },
+{ "ms sans serif,helvetica,serif",
+ "flzoby?" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Gbqnl'f" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Gbc" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Fgbpxf" },
+{ "ms sans serif,helvetica,serif",
+ "fcbafberq" },
+{ "ms sans serif,helvetica,serif",
+ "ol" },
+{ "ms sans serif,helvetica,serif",
+ " Fgbpx" },
+{ "ms sans serif,helvetica,serif",
+ "YVAQ" },
+{ "ms sans serif,helvetica,serif",
+ "HGZQ" },
+{ "ms sans serif,helvetica,serif",
+ "ERI" },
+{ "ms sans serif,helvetica,serif",
+ "IEGL" },
+{ "ms sans serif,helvetica,serif",
+ "GMVK" },
+{ "ms sans serif,helvetica,serif",
+ "Cevpr" },
+{ "ms sans serif,helvetica,serif",
+ "17.81" },
+{ "ms sans serif,helvetica,serif",
+ "8.37" },
+{ "ms sans serif,helvetica,serif",
+ "6.15" },
+{ "ms sans serif,helvetica,serif",
+ "22.43" },
+{ "ms sans serif,helvetica,serif",
+ "18.68" },
+{ "ms sans serif,helvetica,serif",
+ "Punatr " },
+{ "Wingdings,serif",
+ "\xc3""\xa1""" },
+{ "ms sans serif,helvetica,serif",
+ "99% " },
+{ "ms sans serif,helvetica,serif",
+ "26% " },
+{ "ms sans serif,helvetica,serif",
+ "24% " },
+{ "ms sans serif,helvetica,serif",
+ "20% " },
+{ "ms sans serif,helvetica,serif",
+ "18% " },
+{ "ms sans serif,helvetica,serif",
+ "nf" },
+{ "ms sans serif,helvetica,serif",
+ "bs" },
+{ "ms sans serif,helvetica,serif",
+ "12/14/00" },
+{ "ms sans serif,helvetica,serif",
+ "07:12" },
+{ "ms sans serif,helvetica,serif",
+ "CZ" },
+{ "ms sans serif,helvetica,serif",
+ "RFG" },
+{ "Arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "Chg" },
+{ "arial,serif",
+ "va" },
+{ "arial,serif",
+ "LBHE" },
+{ "arial,serif",
+ "fgbpxf" },
+{ "arial,serif",
+ "urer!" },
+{ "arial,serif",
+ "Ertvfgrerq" },
+{ "arial,serif",
+ "Hfref:" },
+{ "arial,serif",
+ "Pyvpx" },
+{ "arial,serif",
+ "urer" },
+{ "arial,serif",
+ "gb" },
+{ "arial,serif",
+ "fvta" },
+{ "Arial,serif",
+ " " },
+{ "Times New Roman,helvetica,serif",
+ "Gur" },
+{ "Times New Roman,helvetica,serif",
+ "snfgrfg," },
+{ "Times New Roman,helvetica,serif",
+ "rnfvrfg" },
+{ "Times New Roman,helvetica,serif",
+ "jnl" },
+{ "Times New Roman,helvetica,serif",
+ "gb" },
+{ "Times New Roman,helvetica,serif",
+ "znantr" },
+{ "Times New Roman,helvetica,serif",
+ "lbhe" },
+{ "Times New Roman,helvetica,serif",
+ "svanaprf" },
+{ "Times New Roman,helvetica,serif",
+ "bayvar!" },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ "Genpx" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "nanylmr" },
+{ "Arial,serif",
+ "lbhe" },
+{ "Arial,serif",
+ "fgbpxf" },
+{ "Arial,serif",
+ "Ivrj" },
+{ "Arial,serif",
+ "cnl" },
+{ "Arial,serif",
+ "ovyyf" },
+{ "Arial,serif",
+ "bayvar" },
+{ "Arial,serif",
+ "Frr" },
+{ "Arial,serif",
+ "n" },
+{ "Arial,serif",
+ "shaq" },
+{ "Arial,serif",
+ "ceb'f" },
+{ "Arial,serif",
+ "gbc" },
+{ "Arial,serif",
+ "cvpx" },
+{ "Arial,serif",
+ "Trg" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "fpber" },
+{ "Arial,serif",
+ "ba" },
+{ "Arial,serif",
+ "Ernq" },
+{ "Arial,serif",
+ "oernxvat" },
+{ "Arial,serif",
+ "znexrg" },
+{ "Arial,serif",
+ "arjf" },
+{ "Arial,serif",
+ "Betnavmr" },
+{ "Arial,serif",
+ "svanaprf" },
+{ "Arial,serif",
+ "ZlSvanaprf" },
+{ "arial,serif",
+ "Lbhe" },
+{ "arial,serif",
+ "cntr:" },
+{ "arial,serif",
+ "Fgbpxf" },
+{ "arial,serif",
+ "," },
+{ "arial,serif",
+ "arjf" },
+{ "arial,serif",
+ "nyregf" },
+{ "arial,serif",
+ "naq" },
+{ "arial,serif",
+ "zber" },
+{ "arial,serif",
+ "..." },
+{ "Arial,serif",
+ "Vairfgvat" },
+{ "arial,serif",
+ "Cbegsbyvb" },
+{ "arial,serif",
+ "Dhbgrf" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "Fpberpneq" },
+{ "Arial,serif",
+ "Gnkrf" },
+{ "arial,serif",
+ "Bayvar" },
+{ "arial,serif",
+ "Svyvat" },
+{ "arial,serif",
+ "Gnk" },
+{ "arial,serif",
+ "Sbezf" },
+{ "arial,serif",
+ "Rfgvzngbe" },
+{ "Arial,serif",
+ "Fznyy" },
+{ "Arial,serif",
+ "Ohfvarff" },
+{ "arial,serif",
+ "Fgneg" },
+{ "arial,serif",
+ "n" },
+{ "arial,serif",
+ "Ohfvarff" },
+{ "arial,serif",
+ "Eha" },
+{ "arial,serif",
+ "lbhe" },
+{ "arial,serif",
+ "Gnkrf" },
+{ "Arial,serif",
+ "Vafhenapr" },
+{ "Arial,serif",
+ "Dhbgrf" },
+{ "arial,serif",
+ "Yvsr" },
+{ "arial,serif",
+ "Nhgb" },
+{ "arial,serif",
+ "Urnygu" },
+{ "Arial,serif",
+ "Ovyyf" },
+{ "Arial,serif",
+ "&" },
+{ "Arial,serif",
+ "Onaxvat" },
+{ "arial,serif",
+ "Ivrj" },
+{ "arial,serif",
+ "&" },
+{ "arial,serif",
+ "Cnl" },
+{ "arial,serif",
+ "Ovyyf" },
+{ "arial,serif",
+ "Genpx" },
+{ "arial,serif",
+ "Nppbhagf" },
+{ "arial,serif",
+ "Svaq" },
+{ "arial,serif",
+ "Engrf" },
+{ "Arial,serif",
+ "Ybnaf" },
+{ "arial,serif",
+ "Ohl" },
+{ "arial,serif",
+ "Ubzr" },
+{ "arial,serif",
+ "Ersvanapr" },
+{ "arial,serif",
+ "Pbafbyvqngr" },
+{ "arial,serif",
+ "Qrog" },
+{ "Arial,serif",
+ "Ergverzrag" },
+{ "arial,serif",
+ "Ergverzrag" },
+{ "arial,serif",
+ "Cynaare" },
+{ "arial,serif",
+ "401x" },
+{ "arial,serif",
+ "Thvqr" },
+{ "arial,serif",
+ "VENf" },
+{ "arial,serif",
+ "Jvyyf" },
+{ "Arial,serif",
+ "Fnivat" },
+{ "Arial,serif",
+ "Fcraqvat" },
+{ "arial,serif",
+ "Perqvg" },
+{ "arial,serif",
+ "Fzneg" },
+{ "arial,serif",
+ "Fnivat" },
+{ "arial,serif",
+ "Fubccvat" },
+{ "Arial,serif",
+ "Sbe" },
+{ "Arial,serif",
+ "Dhvpxra" },
+{ "Arial,serif",
+ "Fbsgjner" },
+{ "Arial,serif",
+ "Hfref" },
+{ "arial,serif",
+ "Grpu" },
+{ "arial,serif",
+ "Fhccbeg" },
+{ "arial,serif",
+ "Dhvpxra" },
+{ "arial,serif",
+ "2001" },
+{ "arial,serif",
+ "ZlSvanaprf" },
+{ "Arial,serif",
+ "Cyrnfr" },
+{ "Arial,serif",
+ "yrg" },
+{ "Arial,serif",
+ "hf" },
+{ "Arial,serif",
+ "xabj" },
+{ "Arial,serif",
+ "jung" },
+{ "Arial,serif",
+ "lbh" },
+{ "Arial,serif",
+ "guvax" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "bhe" },
+{ "Arial,serif",
+ "fvgr!" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Jurer" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "ner" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "gur" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "znexrgf," },
+{ "verdana,ms sans serif,helvetica,serif",
+ "arjf" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "naq" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "zl" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "cbegsbyvb?" },
+{ "ms sans serif,helvetica,serif",
+ "Anfqnd" },
+{ "ms sans serif,helvetica,serif",
+ "qngn" },
+{ "ms sans serif,helvetica,serif",
+ "qrynlrq" },
+{ "ms sans serif,helvetica,serif",
+ "ng" },
+{ "ms sans serif,helvetica,serif",
+ "yrnfg" },
+{ "ms sans serif,helvetica,serif",
+ "15" },
+{ "ms sans serif,helvetica,serif",
+ "zva." },
+{ "ms sans serif,helvetica,serif",
+ "Bgure" },
+{ "ms sans serif,helvetica,serif",
+ "20" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "fvgr" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "fghss" },
+{ "arial,serif",
+ "Fvta" },
+{ "arial,serif",
+ "Hc" },
+{ "arial,serif",
+ "\xc2""\xb7""" },
+{ "arial,serif",
+ "Fvgr" },
+{ "arial,serif",
+ "Srrqonpx" },
+{ "arial,serif",
+ "Uryc" },
+{ "arial,serif",
+ "Pragre" },
+{ "arial,serif",
+ "Serr" },
+{ "arial,serif",
+ "Arjfyrggref" },
+{ "arial,serif",
+ "Orpbzr" },
+{ "arial,serif",
+ "Na" },
+{ "arial,serif",
+ "Nssvyvngr" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "dhvpxra" },
+{ "arial,serif",
+ "Jro" },
+{ "arial,serif",
+ "Ragel" },
+{ "arial,serif",
+ "Fbsgjner" },
+{ "arial,serif",
+ "Gbc" },
+{ "arial,serif",
+ "Gra" },
+{ "arial,serif",
+ "SNDf" },
+{ "arial,serif",
+ "Cebqhpg" },
+{ "arial,serif",
+ "Gvcf" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "slv" },
+{ "arial,serif",
+ "Cevinpl" },
+{ "arial,serif",
+ "Evtugf" },
+{ "arial,serif",
+ "Vasbezngvba" },
+{ "arial,serif",
+ "sbe" },
+{ "arial,serif",
+ "Nqiregvfref" },
+{ "arial,serif",
+ "Pnerref@Vaghvg" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "frnepu" },
+{ "arial,serif",
+ " " },
+{ "arial,serif",
+ "Vaqrk" },
+{ "arial,serif",
+ "Znc" },
+{ "arial,serif",
+ "Uryc" },
+{ "arial,serif",
+ "Tybffnel" },
+{ "arial,serif",
+ "Vagrearg" },
+{ "arial,serif",
+ "Frnepu" },
+{ "ms sans serif,helvetica,serif",
+ " " },
+{ "ms sans serif,helvetica,serif",
+ "Fcbafberq" },
+{ "ms sans serif,helvetica,serif",
+ "Rkpvgr" },
+{ "serif",
+ "Lbhe" },
+{ "serif",
+ "Cevinpl" },
+{ "serif",
+ "Evtugf" },
+{ "ms sans serif,helvetica,serif",
+ "\xc2""\xa9""" },
+{ "ms sans serif,helvetica,serif",
+ "1997-2000" },
+{ "ms sans serif,helvetica,serif",
+ "Vaghvg" },
+{ "ms sans serif,helvetica,serif",
+ "Vap." },
+{ "ms sans serif,helvetica,serif",
+ "Yrtny" },
+{ "ms sans serif,helvetica,serif",
+ "Abgvprf" },
+{ "ms sans serif,helvetica,serif",
+ "Ol" },
+{ "ms sans serif,helvetica,serif",
+ "npprffvat" },
+{ "ms sans serif,helvetica,serif",
+ "naq" },
+{ "ms sans serif,helvetica,serif",
+ "hfvat" },
+{ "ms sans serif,helvetica,serif",
+ "guvf" },
+{ "ms sans serif,helvetica,serif",
+ "cntr" },
+{ "ms sans serif,helvetica,serif",
+ "lbh" },
+{ "ms sans serif,helvetica,serif",
+ "nterr" },
+{ "ms sans serif,helvetica,serif",
+ "gb" },
+{ "ms sans serif,helvetica,serif",
+ "Grezf" },
+{ "ms sans serif,helvetica,serif",
+ "Freivpr." },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Gvzr" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "gb" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "hctenqr" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "lbhe" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "oebjfre!" },
+{ "ms sans serif,helvetica,serif",
+ "Ragre flzoby: " },
+{ "serif",
+ "29," },
+{ "serif",
+ "jjj.dhvpxra.pbz," },
+{ "serif",
+ "331" },
+{ "serif",
+ "1, 29, jjj.dhvpxra.pbz, 331" },
+{ "geneva,arial,serif",
+ "Pyvpx Urer ! " },
+{ "Arial,serif",
+ " " },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Trg Dhbgrf & Arjf" },
+{ "ms sans serif,helvetica,serif",
+ " " },
+{ "arial,serif",
+ "Tb" },
+{ "ms sans serif,helvetica,serif",
+ "Qba'g xabj gur flzoby?" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Gbqnl'f Gbc Fgbpxf" },
+{ "ms sans serif,helvetica,serif",
+ " Fgbpx" },
+{ "ms sans serif,helvetica,serif",
+ "Punatr " },
+{ "ms sans serif,helvetica,serif",
+ "99% " },
+{ "ms sans serif,helvetica,serif",
+ "26% " },
+{ "ms sans serif,helvetica,serif",
+ "24% " },
+{ "ms sans serif,helvetica,serif",
+ "20% " },
+{ "ms sans serif,helvetica,serif",
+ "18% " },
+{ "ms sans serif,helvetica,serif",
+ "nf bs 12/14/00 07:12 CZ RFG" },
+{ "Arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "arial,serif",
+ "Chg va LBHE fgbpxf urer!" },
+{ "Arial,serif",
+ " \xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "Ertvfgrerq Hfref:" },
+{ "arial,serif",
+ "Pyvpx urer gb fvta va" },
+{ "Arial,serif",
+ " " },
+{ "Times New Roman,helvetica,serif",
+ "Gur snfgrfg, rnfvrfg jnl gb znantr lbhe svanaprf bayvar!" },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ " " },
+{ "Arial,serif",
+ "Genpx naq nanylmr lbhe fgbpxf" },
+{ "Arial,serif",
+ "Ivrj naq cnl ovyyf bayvar" },
+{ "Arial,serif",
+ "Frr n shaq ceb'f gbc cvpx" },
+{ "Arial,serif",
+ "Trg gur fpber ba lbhe fgbpxf" },
+{ "Arial,serif",
+ "Ernq oernxvat znexrg arjf" },
+{ "Arial,serif",
+ "Betnavmr lbhe svanaprf" },
+{ "arial,serif",
+ "Lbhe cntr: " },
+{ "arial,serif",
+ ", " },
+{ "arial,serif",
+ ", naq zber ..." },
+{ "arial,serif",
+ " " },
+{ "Arial,serif",
+ "Ovyyf & Onaxvat" },
+{ "arial,serif",
+ "Ivrj & Cnl Ovyyf" },
+{ "arial,serif",
+ "Genpx Nppbhagf" },
+{ "arial,serif",
+ "Svaq Engrf" },
+{ "arial,serif",
+ " ..." },
+{ "arial,serif",
+ "Ohl n Ubzr" },
+{ "arial,serif",
+ "Pbafbyvqngr Qrog" },
+{ "arial,serif",
+ "Bayvar Svyvat" },
+{ "arial,serif",
+ "Gnk Sbezf" },
+{ "arial,serif",
+ "Gnk Rfgvzngbe" },
+{ "arial,serif",
+ "Ergverzrag Cynaare" },
+{ "arial,serif",
+ "401x Thvqr" },
+{ "Arial,serif",
+ "Fznyy Ohfvarff" },
+{ "arial,serif",
+ "Fgneg n Ohfvarff" },
+{ "arial,serif",
+ "Eha lbhe Ohfvarff" },
+{ "Arial,serif",
+ "Fnivat & Fcraqvat" },
+{ "arial,serif",
+ "Qrog & Perqvg" },
+{ "arial,serif",
+ "Fzneg Fnivat" },
+{ "Arial,serif",
+ "Vafhenapr Dhbgrf" },
+{ "Arial,serif",
+ "Sbe Dhvpxra Fbsgjner Hfref" },
+{ "arial,serif",
+ "Grpu Fhccbeg" },
+{ "arial,serif",
+ "Dhvpxra 2001" },
+{ "Arial,serif",
+ "Cyrnfr yrg hf xabj jung lbh guvax bs bhe fvgr!" },
+{ "verdana,ms sans serif,helvetica,serif",
+ "Jurer ner gur znexrgf, arjf naq zl cbegsbyvb?" },
+{ "ms sans serif,helvetica,serif",
+ "Anfqnd qngn qrynlrq ng yrnfg 15 zva. Bgure qngn qrynlrq ng yrnfg 20 zva." },
+{ "arial, helvetica, geneva,serif",
+ "Ybt" },
+{ "arial, helvetica, geneva,serif",
+ "Va" },
+{ "arial, helvetica, geneva,serif",
+ "Cnegaref" },
+{ "arial, helvetica, geneva,serif",
+ "Zrrg" },
+{ "arial, helvetica, geneva,serif",
+ "gubhfnaqf" },
+{ "arial, helvetica, geneva,serif",
+ "bs" },
+{ "arial, helvetica, geneva,serif",
+ "bgure" },
+{ "arial, helvetica, geneva,serif",
+ "zhfvp" },
+{ "arial, helvetica, geneva,serif",
+ "snaf" },
+{ "arial, helvetica, geneva,serif",
+ "ng" },
+{ "arial, helvetica, geneva,serif",
+ "VPD" },
+{ "arial, helvetica, geneva,serif",
+ "Trg" },
+{ "arial, helvetica, geneva,serif",
+ "ybpny" },
+{ "arial, helvetica, geneva,serif",
+ "vasbezngvba" },
+{ "arial, helvetica, geneva,serif",
+ "naq" },
+{ "arial, helvetica, geneva,serif",
+ "pbapreg" },
+{ "arial, helvetica, geneva,serif",
+ "fpurqhyrf" },
+{ "arial, helvetica, geneva,serif",
+ "Svaq" },
+{ "arial, helvetica, geneva,serif",
+ "jung" },
+{ "arial, helvetica, geneva,serif",
+ "zbivrf" },
+{ "arial, helvetica, geneva,serif",
+ "ner" },
+{ "arial, helvetica, geneva,serif",
+ "cynlvat" },
+{ "arial, helvetica, geneva,serif",
+ "Zbivrsbar.pbz" },
+{ "arial, helvetica, geneva,serif",
+ "Gel" },
+{ "arial, helvetica, geneva,serif",
+ "Pbzchfreir" },
+{ "arial, helvetica, geneva,serif",
+ "2000" },
+{ "arial, helvetica, geneva,serif",
+ "trg" },
+{ "arial, helvetica, geneva,serif",
+ "500" },
+{ "arial, helvetica, geneva,serif",
+ "ubhef" },
+{ "arial, helvetica, geneva,serif",
+ "serr" },
+{ "arial,helvetica,geneva,serif",
+ "Zbqrea" },
+{ "arial,helvetica,geneva,serif",
+ "Ebpx" },
+{ "arial,helvetica,geneva,serif",
+ "Ivagntr" },
+{ "arial,helvetica,geneva,serif",
+ "Gbc" },
+{ "arial,helvetica,geneva,serif",
+ "Uvgf" },
+{ "arial,helvetica,geneva,serif",
+ "Pynffvpny" },
+{ "arial,helvetica,geneva,serif",
+ "Pbhagel" },
+{ "arial,helvetica,geneva,serif",
+ "&" },
+{ "arial,helvetica,geneva,serif",
+ "Sbyx" },
+{ "arial,helvetica,geneva,serif",
+ "Heona" },
+{ "arial,helvetica,geneva,serif",
+ "Qnapr" },
+{ "arial,helvetica,geneva,serif",
+ "Wnmm" },
+{ "arial,helvetica,geneva,serif",
+ "Oyhrf" },
+{ "arial,helvetica,geneva,serif",
+ "Jbeyq" },
+{ "arial,helvetica,geneva,serif",
+ "Arj" },
+{ "arial,helvetica,geneva,serif",
+ "Ntr" },
+{ "arial,helvetica,geneva,serif",
+ "Rpyrpgvp" },
+{ "arial,helvetica,geneva,serif",
+ "Yngva" },
+{ "arial,helvetica,geneva,serif",
+ "Pryroevgl" },
+{ "arial,helvetica,geneva,serif",
+ "Fcvaf" },
+{ "arial,helvetica,geneva,serif",
+ "Ubyvqnlf" },
+{ "arial,helvetica,geneva,serif",
+ "Gurzrf" },
+{ "arial, helvetica, geneva,serif",
+ "Qrp" },
+{ "arial, helvetica, geneva,serif",
+ "14," },
+{ "arial, helvetica, geneva,serif",
+ "2000" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq" },
+{ "arial, helvetica, geneva,serif",
+ "Fcvaare" },
+{ "arial, helvetica, geneva,serif",
+ "Cyhf" },
+{ "arial, helvetica, geneva,serif",
+ "3.1" },
+{ "arial, helvetica, geneva,serif",
+ "naq" },
+{ "arial, helvetica, geneva,serif",
+ "rawbl" },
+{ "arial, helvetica, geneva,serif",
+ "serr" },
+{ "arial, helvetica, geneva,serif",
+ "zhfvp" },
+{ "arial, helvetica, geneva,serif",
+ "abj!" },
+{ "arial, helvetica, geneva,serif",
+ "150+" },
+{ "arial, helvetica, geneva,serif",
+ "punaaryf" },
+{ "arial, helvetica, geneva,serif",
+ "cebtenzzrq" },
+{ "arial, helvetica, geneva,serif",
+ "ol" },
+{ "arial, helvetica, geneva,serif",
+ "rkcregf" },
+{ "arial, helvetica, geneva,serif",
+ "Ovbf" },
+{ "arial, helvetica, geneva,serif",
+ "PQ" },
+{ "arial, helvetica, geneva,serif",
+ "vasb" },
+{ "arial, helvetica, geneva,serif",
+ "sbe" },
+{ "arial, helvetica, geneva,serif",
+ "rirel" },
+{ "arial, helvetica, geneva,serif",
+ "negvfg" },
+{ "arial, helvetica, geneva,serif",
+ "lbh" },
+{ "arial, helvetica, geneva,serif",
+ "urne" },
+{ "arial, helvetica, geneva,serif",
+ "Uvtu-dhnyvgl" },
+{ "arial, helvetica, geneva,serif",
+ "nhqvb" },
+{ "arial, helvetica, geneva,serif",
+ "fgernzf" },
+{ "arial, helvetica, geneva,serif",
+ "Cresrpg" },
+{ "arial, helvetica, geneva,serif",
+ "yvfgravat" },
+{ "arial, helvetica, geneva,serif",
+ "jbex" },
+{ "arial, helvetica, geneva,serif",
+ "Gnxr" },
+{ "arial, helvetica, geneva,serif",
+ "n" },
+{ "arial, helvetica, geneva,serif",
+ "jnyxguebhtu" },
+{ "arial, helvetica, geneva,serif",
+ "Naabhapvat" },
+{ "arial, helvetica, geneva,serif",
+ "Tebhaq" },
+{ "arial, helvetica, geneva,serif",
+ "Mreb" },
+{ "arial, helvetica, geneva,serif",
+ "," },
+{ "arial, helvetica, geneva,serif",
+ "bhe" },
+{ "arial, helvetica, geneva,serif",
+ "jrrxyl" },
+{ "arial, helvetica, geneva,serif",
+ "arj" },
+{ "arial, helvetica, geneva,serif",
+ "fubj" },
+{ "arial, helvetica, geneva,serif",
+ "ubfgrq" },
+{ "arial, helvetica, geneva,serif",
+ "Puevf" },
+{ "arial, helvetica, geneva,serif",
+ "Qbhevqnf." },
+{ "arial, helvetica, geneva,serif",
+ "Qba'g" },
+{ "arial, helvetica, geneva,serif",
+ "zvff" },
+{ "arial, helvetica, geneva,serif",
+ "gjb" },
+{ "arial, helvetica, geneva,serif",
+ "oernxvat" },
+{ "arial, helvetica, geneva,serif",
+ "npebff" },
+{ "arial, helvetica, geneva,serif",
+ "nyy" },
+{ "arial, helvetica, geneva,serif",
+ "traerf," },
+{ "arial, helvetica, geneva,serif",
+ "vapyhqvat" },
+{ "arial, helvetica, geneva,serif",
+ "yvir" },
+{ "arial, helvetica, geneva,serif",
+ "cresbeznaprf" },
+{ "arial, helvetica, geneva,serif",
+ "vagreivrjf!" },
+{ "arial, helvetica, geneva,serif",
+ "Trg" },
+{ "arial, helvetica, geneva,serif",
+ "zber" },
+{ "arial, helvetica, geneva,serif",
+ "arj" },
+{ "arial, helvetica, geneva,serif",
+ "zhfvp" },
+{ "arial, helvetica, geneva,serif",
+ "sebz" },
+{ "arial, helvetica, geneva,serif",
+ "gbqnl'f" },
+{ "arial, helvetica, geneva,serif",
+ "gbc" },
+{ "arial, helvetica, geneva,serif",
+ "negvfgf" },
+{ "arial, helvetica, geneva,serif",
+ "jvgu" },
+{ "arial, helvetica, geneva,serif",
+ "bhe" },
+{ "arial, helvetica, geneva,serif",
+ "Zhfvp" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnqf" },
+{ "arial, helvetica, geneva,serif",
+ "frpgvba" },
+{ "arial, helvetica, geneva,serif",
+ "Evpx" },
+{ "arial, helvetica, geneva,serif",
+ "Aryfba" },
+{ "arial, helvetica, geneva,serif",
+ "FUrQNVFL" },
+{ "arial, helvetica, geneva,serif",
+ "Wnl" },
+{ "arial, helvetica, geneva,serif",
+ "Yril" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq" },
+{ "arial, helvetica, geneva,serif",
+ "3.1" },
+{ "arial, helvetica, geneva,serif",
+ "Arj!" },
+{ "arial, helvetica, geneva,serif",
+ "J/Fxvaf" },
+{ "arial, helvetica, geneva,serif",
+ "(2.6" },
+{ "arial, helvetica, geneva,serif",
+ "ZO)" },
+{ "arial, helvetica, geneva,serif",
+ "Ab" },
+{ "arial, helvetica, geneva,serif",
+ "Fxvaf" },
+{ "arial, helvetica, geneva,serif",
+ "(1.4" },
+{ "Lucida Grande",
+ "Havgrq" },
+{ "Lucida Grande",
+ "Fgngrf" },
+{ "Lucida Grande",
+ "Nhfgenyvn" },
+{ "Lucida Grande",
+ "Zvqqyr" },
+{ "Lucida Grande",
+ "Rnfg" },
+{ "Lucida Grande",
+ "Treznal" },
+{ "Lucida Grande",
+ "Fvatncber" },
+{ "Lucida Grande",
+ "Ubat" },
+{ "Lucida Grande",
+ "Xbat" },
+{ "Lucida Grande",
+ "Xvatqbz" },
+{ "Lucida Grande",
+ "Wncna" },
+{ "arial, helvetica, geneva,serif",
+ "Flfgrz" },
+{ "arial, helvetica, geneva,serif",
+ "Erdhverzragf:" },
+{ "arial, helvetica, geneva,serif",
+ "Cragvhz" },
+{ "arial, helvetica, geneva,serif",
+ "CP," },
+{ "arial, helvetica, geneva,serif",
+ "Jvaqbjf" },
+{ "arial, helvetica, geneva,serif",
+ "2000/98/95/AG" },
+{ "arial, helvetica, geneva,serif",
+ "4.0," },
+{ "arial, helvetica, geneva,serif",
+ "28.8" },
+{ "arial, helvetica, geneva,serif",
+ "xocf" },
+{ "arial, helvetica, geneva,serif",
+ "pbaarpgvba," },
+{ "arial, helvetica, geneva,serif",
+ "fbhaq" },
+{ "arial, helvetica, geneva,serif",
+ "pneq" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq" },
+{ "arial, helvetica, geneva,serif",
+ "Uryc" },
+{ "arial, helvetica, geneva,serif",
+ "CERIVRJ" },
+{ "arial, helvetica, geneva,serif",
+ "PYVPX" },
+{ "arial, helvetica, geneva,serif",
+ "SBE" },
+{ "arial, helvetica, geneva,serif",
+ "ZBER" },
+{ "arial, helvetica, geneva,serif",
+ "VASB" },
+{ "arial, helvetica, geneva,serif",
+ "Abg" },
+{ "arial, helvetica, geneva,serif",
+ "Pbaivaprq?" },
+{ "arial, helvetica, geneva,serif",
+ "Gel" },
+{ "arial, helvetica, geneva,serif",
+ "Fcvaare" },
+{ "arial, helvetica, geneva,serif",
+ "Yvgr" },
+{ "arial, helvetica, geneva,serif",
+ "," },
+{ "arial, helvetica, geneva,serif",
+ "Jro-onfrq" },
+{ "arial, helvetica, geneva,serif",
+ "cynlre!" },
+{ "arial, helvetica, geneva,serif",
+ "qbjaybnq" },
+{ "arial, helvetica, geneva,serif",
+ "be" },
+{ "arial, helvetica, geneva,serif",
+ "vafgnyyngvba" },
+{ "arial, helvetica, geneva,serif",
+ "Fgneg" },
+{ "arial, helvetica, geneva,serif",
+ "evtug" },
+{ "arial, helvetica, geneva,serif",
+ "abj" },
+{ "arial, helvetica, geneva,serif",
+ "Cevinpl" },
+{ "arial, helvetica, geneva,serif",
+ "cbyvpl" },
+{ "arial, helvetica, geneva,serif",
+ "." },
+{ "arial, helvetica, geneva,serif",
+ "Yrtny" },
+{ "arial, helvetica, geneva,serif",
+ "fghss" },
+{ "arial, helvetica, geneva,serif",
+ "\xc2""\xa9""" },
+{ "arial, helvetica, geneva,serif",
+ "2000" },
+{ "arial, helvetica, geneva,serif",
+ "Fcvaare" },
+{ "arial, helvetica, geneva,serif",
+ "Argjbexf," },
+{ "arial, helvetica, geneva,serif",
+ "Vap." },
+{ "arial, helvetica, geneva,serif",
+ "Fcvaare" },
+{ "arial, helvetica, geneva,serif",
+ "vf" },
+{ "arial, helvetica, geneva,serif",
+ "n" },
+{ "arial, helvetica, geneva,serif",
+ "freivpr" },
+{ "arial, helvetica, geneva,serif",
+ "znex" },
+{ "arial, helvetica, geneva,serif",
+ "bs" },
+{ "arial, helvetica, geneva,serif",
+ "Nzrevpn" },
+{ "arial, helvetica, geneva,serif",
+ "Bayvar," },
+{ "arial, helvetica, geneva,serif",
+ "Vap." },
+{ "arial, helvetica, geneva,serif",
+ "Nyy" },
+{ "arial, helvetica, geneva,serif",
+ "evtugf" },
+{ "arial, helvetica, geneva,serif",
+ "erfreirq." },
+{ "monospace",
+ "Ragre n anzr" },
+{ "serif",
+ "Rkcyber:" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq 3.1 " },
+{ "arial, helvetica, geneva,serif",
+ " J/Fxvaf (2.6 ZO)" },
+{ "arial, helvetica, geneva,serif",
+ " Ab Fxvaf (1.4 ZO)" },
+{ "Lucida Grande",
+ "Havgrq Fgngrf" },
+{ "arial, helvetica, geneva,serif",
+ "Flfgrz Erdhverzragf:" },
+{ "arial, helvetica, geneva,serif",
+ "Cragvhz CP, Jvaqbjf" },
+{ "arial, helvetica, geneva,serif",
+ "2000/98/95/AG 4.0," },
+{ "arial, helvetica, geneva,serif",
+ "28.8 xocf pbaarpgvba, " },
+{ "arial, helvetica, geneva,serif",
+ "fbhaq pneq" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq Uryc" },
+{ "arial, helvetica, geneva,serif",
+ "PYVPX SBE ZBER VASB" },
+{ "serif",
+ "Fcvaare" },
+{ "serif",
+ "Ubzr" },
+{ "serif",
+ "Fcvaare Ubzr" },
+{ "monospace",
+ "Ragre n anzr" },
+{ "sans-serif",
+ "Fhozvg" },
+{ "arial, helvetica, geneva,serif",
+ "Ybt Va" },
+{ "arial,helvetica,geneva,serif",
+ "Zbqrea Ebpx" },
+{ "serif",
+ "Jrypbzr" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "Fcvaare.pbz!" },
+{ "serif",
+ "Jrypbzr gb Fcvaare.pbz!" },
+{ "arial, helvetica, geneva,serif",
+ "Qrp 14, 2000" },
+{ "arial, helvetica, geneva,serif",
+ "Qbjaybnq Fcvaare Cyhf 3.1" },
+{ "arial, helvetica, geneva,serif",
+ "naq rawbl serr zhfvp abj!" },
+{ "arial, helvetica, geneva,serif",
+ "150+ zhfvp punaaryf cebtenzzrq ol rkcregf" },
+{ "arial, helvetica, geneva,serif",
+ "Ovbf naq PQ vasb sbe rirel negvfg lbh urne" },
+{ "arial, helvetica, geneva,serif",
+ "Uvtu-dhnyvgl nhqvb fgernzf" },
+{ "arial, helvetica, geneva,serif",
+ "Cresrpg sbe yvfgravat ng jbex" },
+{ "arial, helvetica, geneva,serif",
+ "Gnxr n jnyxguebhtu" },
+{ "arial, helvetica, geneva,serif",
+ "Naabhapvat " },
+{ "arial, helvetica, geneva,serif",
+ "Tebhaq Mreb" },
+{ "arial, helvetica, geneva,serif",
+ ", bhe jrrxyl arj zhfvp " },
+{ "arial, helvetica, geneva,serif",
+ "fubj ubfgrq ol Puevf Qbhevqnf. Qba'g zvff gjb " },
+{ "arial, helvetica, geneva,serif",
+ "ubhef bs oernxvat zhfvp npebff nyy traerf, " },
+{ "arial, helvetica, geneva,serif",
+ "vapyhqvat yvir cresbeznaprf naq vagreivrjf!" },
+{ "arial, helvetica, geneva,serif",
+ "Trg zber arj zhfvp sebz gbqnl'f gbc negvfgf " },
+{ "arial, helvetica, geneva,serif",
+ "jvgu bhe " },
+{ "arial, helvetica, geneva,serif",
+ "Zhfvp Qbjaybnqf frpgvba" },
+{ "arial, helvetica, geneva,serif",
+ "Evpx Aryfba" },
+{ "arial, helvetica, geneva,serif",
+ "Wnl Yril" },
+{ "arial, helvetica, geneva,serif",
+ " Ab Fxvaf (1.4 " },
+{ "serif",
+ "30," },
+{ "serif",
+ "jjj.fcvaare.pbz," },
+{ "serif",
+ "1022" },
+{ "serif",
+ "1, 30, jjj.fcvaare.pbz, 1022" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FRYRPG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "N" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "GBCVP" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "----------------------------" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FHAFGBER" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FHA" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "NHPGVBAF" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "UBJ" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "GB" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ ".PBZ" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ " >" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Rkrphgvir" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Xrlabgrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Crefcrpgvirf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ernqvat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pnfr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fghqvrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "CEBQHPGF" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "&" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FBYHGVBAF" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Uneqjner" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fbsgjner" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fbyhgvbaf," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Erfbheprf," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Thvqrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ " ZNEXRGF" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "VAQHFGEVRF" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FHCCBEG," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "GENVAVAT" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "PBAFHYGVAT" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Bayvar" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fhccbeg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pragre" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Freivprf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Genvavat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pbafhygvat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ohfvarff" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nqinagntr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Erfryyre" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Cebtenzf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "QRIRYBCRE" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "PBAARPGVBA" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fha" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Grpuabybtvrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Uneqjner," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gbbyf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Grpuavpny" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pb-Znexrgvat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Qbg-Pbz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ohvyqre" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "NOBHG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pbzcnal" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vasbezngvba" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zrqvn" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vairfgbe" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Eryngvbaf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fcbafbefuvcf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "WNIN.FHA.PBZ" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "VCYNARG.PBZ" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "TB" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Frnepu" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fha.pbz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fha" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "|" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ertvbany" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fvgrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fvgr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vaqrk" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ubj" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gb" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ohl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "JROPNFG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "EROEBNQPNFG:" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Freivpr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Znantrzrag" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ynhapu," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Qrprzore" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "5," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "2000," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "10nz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "CFG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Wbva" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Rq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Mnaqre," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Cng" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fhrygm," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Navy" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Tnqer," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "naq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Naql" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vatenz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gb" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "yrnea" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "ubj" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "freivpr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "znantrzrag" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "cebqhpgf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "freivprf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "uryc" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "znxr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "znantvat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gur" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Arg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Rssrpg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "n" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "ernyvgl." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "NHQVBPNFGF:" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fha" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gb" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Npdhver" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "UvtuTebhaq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Flfgrzf," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vap.," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "4," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "2000" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "11:15nz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "naq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "12:15cz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zvpebflfgrzf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "naabhaprq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gbqnl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gung" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "vg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "cynaf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "npdhver" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "UvtuTebhaq" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Flfgrzf," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Vap.," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "va" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fgbpx-sbe-fgbpx" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "genafnpgvba." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "@" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gryrpbz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nfvn" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "5-9," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "hf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "sbe" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "eroebnqpnfg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "bs" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fha'f" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Znva" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gurngre" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "cerfragngvba" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "sebz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fubj" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "sybbe" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "ng" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gryrpbz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nfvn" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "2000" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ubat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Xbat." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "BAYVAR" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "QVFPHFFVBA:" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FgneBssvpr[gz]" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "5.2" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pbzcnavba" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "12-18," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "nhgubef" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FgneBssvpr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "5.2" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pbzcnavba" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fcrpvny" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "qvfphffvba" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "sbehz" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "raq-hfref" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "bssvpr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fhvgr." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "QVTVGNY" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "WBHEARL" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "JROPNFG:" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Rkcybevat" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gur" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Sebagvref" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "bs" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fcnpr:" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gur" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Trzvav" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gryrfpbcr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Qvtvgny" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Wbhearl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "geniryf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Trzvav" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Gryrfpbcr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "ngbc" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Znhan" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Xrn." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Yrnea" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "guvf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "vagrearg" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "ernql" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "gryrfpbcr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "npdhverf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "qvfgevohgrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "vzntrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fpvragvfgf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "jbeyqjvqr." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zber\xc2""\xbb""" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Pbclevtug" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "1994-2000" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zvpebflfgrzf," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "901" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fna" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nagbavb" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ebnq," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Cnyb" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nygb," },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "PN" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "94303" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "HFN." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Nyy" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "evtugf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "erfreirq." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Grezf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Hfr" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Cevinpl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Cbyvpl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Srrqonpx" },
+{ "serif",
+ "31," },
+{ "serif",
+ "jjj.fha.pbz," },
+{ "serif",
+ "265" },
+{ "serif",
+ "1, 31, jjj.fha.pbz, 265" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "FRYRPG N GBCVP" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Zl Fha" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ " | " },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ertvbany Fvgrf" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fvgr Vaqrk" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Ubj Gb Ohl" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ " " },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "JROPNFG EROEBNQPNFG: Freivpr Znantrzrag Ynhapu, Qrprzore 5, 2000, 10nz CFG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Wbva Rq Mnaqre, Cng Fhrygm, Navy Tnqer, naq Naql Vatenz gb yrnea ubj freivpr znantrzrag " },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "cebqhpgf naq freivprf uryc znxr znantvat gur Arg Rssrpg n ernyvgl." },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "NHQVBPNFGF: Fha gb Npdhver UvtuTebhaq Flfgrzf, Vap., Qrprzore 4, 2000 11:15nz CFG " },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "naq 12:15cz CFG" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "Fha Zvpebflfgrzf naabhaprq gbqnl gung vg cynaf gb npdhver UvtuTebhaq Flfgrzf, Vap., va n" },
+{ "geneva,helvetica,arial,\"lucida sans\",sans-serif,serif",
+ "fgbpx-sbe-fgbpx genafnpgvba." },
+{ "Georgia,Times,serif",
+ "N" },
+{ "Georgia,Times,serif",
+ "Xvaqre," },
+{ "Georgia,Times,serif",
+ "Tragyre" },
+{ "Georgia,Times,serif",
+ "Fhcerzr" },
+{ "Georgia,Times,serif",
+ "Pbheg?" },
+{ "Arial,Helvetica,serif",
+ "ZNEPL" },
+{ "Arial,Helvetica,serif",
+ "AVTUFJNAQRE/NC" },
+{ "Arial,Helvetica,serif",
+ "Ohfu'f" },
+{ "Arial,Helvetica,serif",
+ "abzvarrf" },
+{ "Arial,Helvetica,serif",
+ "jvyy" },
+{ "Arial,Helvetica,serif",
+ "yvxryl" },
+{ "Arial,Helvetica,serif",
+ "or" },
+{ "Arial,Helvetica,serif",
+ "va" },
+{ "Arial,Helvetica,serif",
+ "gur" },
+{ "Arial,Helvetica,serif",
+ "pragevfg" },
+{ "Arial,Helvetica,serif",
+ "zbyq" },
+{ "Arial,Helvetica,serif",
+ "bs" },
+{ "Arial,Helvetica,serif",
+ "Nagubal" },
+{ "Arial,Helvetica,serif",
+ "Xraarql" },
+{ "Georgia,Times,serif",
+ "Gur" },
+{ "Georgia,Times,serif",
+ "choyvp'f" },
+{ "Georgia,Times,serif",
+ "cbfg-ryrpgvba" },
+{ "Georgia,Times,serif",
+ "creprcgvba" },
+{ "Georgia,Times,serif",
+ "bs" },
+{ "Georgia,Times,serif",
+ "gur" },
+{ "Georgia,Times,serif",
+ "Havgrq" },
+{ "Georgia,Times,serif",
+ "Fgngrf" },
+{ "Georgia,Times,serif",
+ "Fhcerzr" },
+{ "Georgia,Times,serif",
+ "Pbheg" },
+{ "Georgia,Times,serif",
+ "vf" },
+{ "Georgia,Times,serif",
+ "yvxryl" },
+{ "Georgia,Times,serif",
+ "gb" },
+{ "Georgia,Times,serif",
+ "zrna" },
+{ "Georgia,Times,serif",
+ "gung" },
+{ "Georgia,Times,serif",
+ "Ohfu'f" },
+{ "Georgia,Times,serif",
+ "whqvpvny" },
+{ "Georgia,Times,serif",
+ "nccbvagrrf" },
+{ "Georgia,Times,serif",
+ "jvyy" },
+{ "Georgia,Times,serif",
+ "or" },
+{ "Georgia,Times,serif",
+ "zbqrengr" },
+{ "Georgia,Times,serif",
+ "gur" },
+{ "Georgia,Times,serif",
+ "jubyr" },
+{ "Georgia,Times,serif",
+ "fgbel" },
+{ "Georgia,Times,serif",
+ ">>" },
+{ "Georgia,Times,serif",
+ "Eryngrq:" },
+{ "Georgia,Times,serif",
+ "Vafvqr" },
+{ "Georgia,Times,serif",
+ "Ohfu" },
+{ "Georgia,Times,serif",
+ "Qlanfgl" },
+{ "Georgia,Times,serif",
+ "Genafvgvba" },
+{ "Georgia,Times,serif",
+ "Thvqr" },
+{ "Georgia,Times,serif",
+ "Cubgb" },
+{ "Georgia,Times,serif",
+ "Rffnl:" },
+{ "Georgia,Times,serif",
+ "Gur" },
+{ "Georgia,Times,serif",
+ "Ryrpgvba'f" },
+{ "Georgia,Times,serif",
+ "Svany" },
+{ "Georgia,Times,serif",
+ "48" },
+{ "Georgia,Times,serif",
+ "Ubhef" },
+{ "Georgia,Times,serif",
+ "Fgnegf" },
+{ "Georgia,Times,serif",
+ "Cerfvqrapl" },
+{ "Georgia,Times,serif",
+ "Jvgu" },
+{ "Georgia,Times,serif",
+ "n" },
+{ "Georgia,Times,serif",
+ "Juvfcre" },
+{ "Georgia,Times,serif",
+ "Ivpgbel" },
+{ "Georgia,Times,serif",
+ "fcrrpu" },
+{ "Georgia,Times,serif",
+ "qjryyf" },
+{ "Georgia,Times,serif",
+ "yrff" },
+{ "Georgia,Times,serif",
+ "ba" },
+{ "Georgia,Times,serif",
+ "ivpgbe" },
+{ "Georgia,Times,serif",
+ "guna" },
+{ "Georgia,Times,serif",
+ "pbapvyvngvba" },
+{ "Georgia,Times,serif",
+ "Ubj" },
+{ "Georgia,Times,serif",
+ "Jbeyq" },
+{ "Georgia,Times,serif",
+ "Frrf" },
+{ "Georgia,Times,serif",
+ "Cerfvqrag" },
+{ "Georgia,Times,serif",
+ "N" },
+{ "Georgia,Times,serif",
+ "guhzoanvy" },
+{ "Georgia,Times,serif",
+ "fxrgpu" },
+{ "Georgia,Times,serif",
+ "bs" },
+{ "Georgia,Times,serif",
+ "ubcrf," },
+{ "Georgia,Times,serif",
+ "srnef" },
+{ "Georgia,Times,serif",
+ "naq" },
+{ "Georgia,Times,serif",
+ "punyyratrf" },
+{ "Georgia,Times,serif",
+ "Qb" },
+{ "Georgia,Times,serif",
+ "lbh" },
+{ "Georgia,Times,serif",
+ "guvax" },
+{ "Georgia,Times,serif",
+ "gur" },
+{ "Georgia,Times,serif",
+ "Fhcerzr" },
+{ "Georgia,Times,serif",
+ "Pbheg'f" },
+{ "Georgia,Times,serif",
+ "qrpvfvba" },
+{ "Georgia,Times,serif",
+ "jnf" },
+{ "Georgia,Times,serif",
+ "cbyvgvpny?" },
+{ "Georgia,Times,serif",
+ "Lrf" },
+{ "Georgia,Times,serif",
+ "Ab" },
+{ "Georgia,Times,serif",
+ "Abg" },
+{ "Georgia,Times,serif",
+ "fher" },
+{ "Georgia,Times,serif",
+ "Zber" },
+{ "Georgia,Times,serif",
+ "cbyyf" },
+{ "Georgia,Times,serif",
+ "Ubj" },
+{ "Georgia,Times,serif",
+ "Tbbq-Ybbxvat" },
+{ "Georgia,Times,serif",
+ "Ner" },
+{ "Georgia,Times,serif",
+ "Lbh?" },
+{ "Georgia,Times,serif",
+ "Gung'f" },
+{ "Georgia,Times,serif",
+ "vqrn" },
+{ "Georgia,Times,serif",
+ "oruvaq" },
+{ "Georgia,Times,serif",
+ "yngrfg" },
+{ "Georgia,Times,serif",
+ "jro" },
+{ "Georgia,Times,serif",
+ "penmr," },
+{ "Georgia,Times,serif",
+ "n" },
+{ "Georgia,Times,serif",
+ "fvgr" },
+{ "Georgia,Times,serif",
+ "pnyyrq" },
+{ "Georgia,Times,serif",
+ "Nz" },
+{ "Georgia,Times,serif",
+ "V" },
+{ "Georgia,Times,serif",
+ "Ubg" },
+{ "Georgia,Times,serif",
+ "Be" },
+{ "Georgia,Times,serif",
+ "Abg.pbz," },
+{ "Georgia,Times,serif",
+ "jurer" },
+{ "Georgia,Times,serif",
+ "engr" },
+{ "Georgia,Times,serif",
+ "crbcyr'f" },
+{ "Georgia,Times,serif",
+ "ybbxf," },
+{ "Georgia,Times,serif",
+ "be" },
+{ "Georgia,Times,serif",
+ "nfx" },
+{ "Georgia,Times,serif",
+ "crbcyr" },
+{ "Georgia,Times,serif",
+ "gb" },
+{ "Georgia,Times,serif",
+ "lbhef" },
+{ "Georgia,Times,serif",
+ "Nepuvir" },
+{ "Georgia,Times,serif",
+ "Irkvat" },
+{ "Georgia,Times,serif",
+ "Dhrfgvbaf" },
+{ "Georgia,Times,serif",
+ "sbe" },
+{ "Georgia,Times,serif",
+ "Ubyvqnlf" },
+{ "Georgia,Times,serif",
+ "Gur" },
+{ "Georgia,Times,serif",
+ "ryrpgvba" },
+{ "Georgia,Times,serif",
+ "univat" },
+{ "Georgia,Times,serif",
+ "cynlrq" },
+{ "Georgia,Times,serif",
+ "vgfrys" },
+{ "Georgia,Times,serif",
+ "bhg" },
+{ "Georgia,Times,serif",
+ "jvgu" },
+{ "Georgia,Times,serif",
+ "obgu" },
+{ "Georgia,Times,serif",
+ "onat" },
+{ "Georgia,Times,serif",
+ "naq" },
+{ "Georgia,Times,serif",
+ "juvzcre," },
+{ "Georgia,Times,serif",
+ "Ynapr" },
+{ "Georgia,Times,serif",
+ "Zbeebj" },
+{ "Georgia,Times,serif",
+ "cebcbfrf" },
+{ "Georgia,Times,serif",
+ "zrah" },
+{ "Georgia,Times,serif",
+ "bs" },
+{ "Georgia,Times,serif",
+ "rireterra" },
+{ "Georgia,Times,serif",
+ "veevgnagf" },
+{ "Georgia,Times,serif",
+ "1." },
+{ "Georgia,Times,serif",
+ "2." },
+{ "Georgia,Times,serif",
+ "3." },
+{ "Georgia,Times,serif",
+ "Wbua" },
+{ "Georgia,Times,serif",
+ "Yraaba'f" },
+{ "Georgia,Times,serif",
+ "Qrngu" },
+{ "Georgia,Times,serif",
+ "GVZR" },
+{ "Georgia,Times,serif",
+ "100:" },
+{ "Georgia,Times,serif",
+ "Orngyrf" },
+{ "Georgia,Times,serif",
+ "Svaqvat" },
+{ "Georgia,Times,serif",
+ "Tvsgf" },
+{ "Georgia,Times,serif",
+ "Qvtvgny" },
+{ "Georgia,Times,serif",
+ "pna" },
+{ "Georgia,Times,serif",
+ "uryc" },
+{ "Georgia,Times,serif",
+ "Crefba" },
+{ "Georgia,Times,serif",
+ "Lrne" },
+{ "Georgia,Times,serif",
+ "Jub" },
+{ "Georgia,Times,serif",
+ "jvyy" },
+{ "Georgia,Times,serif",
+ "vg" },
+{ "Georgia,Times,serif",
+ "or?" },
+{ "Verdana,Arial,serif",
+ "A" },
+{ "Verdana,Arial,serif",
+ "N" },
+{ "Verdana,Arial,serif",
+ "G" },
+{ "Verdana,Arial,serif",
+ "V" },
+{ "Verdana,Arial,serif",
+ "B" },
+{ "Georgia,Times,serif",
+ "Ny" },
+{ "Georgia,Times,serif",
+ "Tber'f" },
+{ "Georgia,Times,serif",
+ "Tenprshy" },
+{ "Georgia,Times,serif",
+ "Inyrqvpgbel" },
+{ "Georgia,Times,serif",
+ "Va" },
+{ "Georgia,Times,serif",
+ "n" },
+{ "Georgia,Times,serif",
+ "tenpvbhf" },
+{ "Georgia,Times,serif",
+ "pbaprffvba," },
+{ "Georgia,Times,serif",
+ "Tber" },
+{ "Georgia,Times,serif",
+ "svanyyl" },
+{ "Georgia,Times,serif",
+ "pbaarpgf" },
+{ "Georgia,Times,serif",
+ "Vg'f" },
+{ "Georgia,Times,serif",
+ "Gernfhel" },
+{ "Georgia,Times,serif",
+ "Frpergnel," },
+{ "Georgia,Times,serif",
+ "Fghcvq" },
+{ "Georgia,Times,serif",
+ "Ohfu" },
+{ "Georgia,Times,serif",
+ "unf" },
+{ "Georgia,Times,serif",
+ "gbhtu" },
+{ "Georgia,Times,serif",
+ "rpbabzvp" },
+{ "Georgia,Times,serif",
+ "npg" },
+{ "Georgia,Times,serif",
+ "sbyybj." },
+{ "Georgia,Times,serif",
+ "Jub'yy" },
+{ "Georgia,Times,serif",
+ "uvf" },
+{ "Georgia,Times,serif",
+ "Obo" },
+{ "Georgia,Times,serif",
+ "Ehova?" },
+{ "Georgia,Times,serif",
+ "Tber," },
+{ "Georgia,Times,serif",
+ "Frys-Znqr" },
+{ "Georgia,Times,serif",
+ "Fgngrfzna" },
+{ "Georgia,Times,serif",
+ "trgf" },
+{ "Georgia,Times,serif",
+ "ernql" },
+{ "Georgia,Times,serif",
+ "ernc" },
+{ "Georgia,Times,serif",
+ "orarsvgf" },
+{ "Georgia,Times,serif",
+ "ybjrerq" },
+{ "Georgia,Times,serif",
+ "rkcrpgngvbaf" },
+{ "Verdana,Arial,serif",
+ "J" },
+{ "Verdana,Arial,serif",
+ "E" },
+{ "Verdana,Arial,serif",
+ "Y" },
+{ "Verdana,Arial,serif",
+ "Q" },
+{ "Georgia,Times,serif",
+ "Pyvagba" },
+{ "Georgia,Times,serif",
+ "Pbyyrpgf" },
+{ "Georgia,Times,serif",
+ "Vevfu" },
+{ "Georgia,Times,serif",
+ "Cynhqvgf" },
+{ "Georgia,Times,serif",
+ "Verynaq" },
+{ "Georgia,Times,serif",
+ "fzvyrf" },
+{ "Georgia,Times,serif",
+ "ba" },
+{ "Georgia,Times,serif",
+ "Cerfvqrag" },
+{ "Georgia,Times,serif",
+ "qrfcvgr" },
+{ "Georgia,Times,serif",
+ "crnpr" },
+{ "Georgia,Times,serif",
+ "gebhoyrf" },
+{ "Georgia,Times,serif",
+ "Enznqna" },
+{ "Georgia,Times,serif",
+ "Qvnevrf" },
+{ "Georgia,Times,serif",
+ "Vfynz'f" },
+{ "Georgia,Times,serif",
+ "ubyl" },
+{ "Georgia,Times,serif",
+ "zbagu" },
+{ "Georgia,Times,serif",
+ "va" },
+{ "Georgia,Times,serif",
+ "bar" },
+{ "Georgia,Times,serif",
+ "Pnveb" },
+{ "Georgia,Times,serif",
+ "ubhfrubyq" },
+{ "Georgia,Times,serif",
+ "Qrfcvgr" },
+{ "Georgia,Times,serif",
+ "Gnyxf," },
+{ "Georgia,Times,serif",
+ "Srj" },
+{ "Georgia,Times,serif",
+ "Fvtaf" },
+{ "Georgia,Times,serif",
+ "Crnpr" },
+{ "Georgia,Times,serif",
+ "Zvqrnfg" },
+{ "Georgia,Times,serif",
+ "Ivbyrapr" },
+{ "Georgia,Times,serif",
+ "uneqravat" },
+{ "Georgia,Times,serif",
+ "cbfvgvbaf" },
+{ "Georgia,Times,serif",
+ "obgu" },
+{ "Georgia,Times,serif",
+ "fvqrf" },
+{ "Verdana,Arial,serif",
+ "F" },
+{ "Verdana,Arial,serif",
+ "Z" },
+{ "Verdana,Arial,serif",
+ "C" },
+{ "Verdana,Arial,serif",
+ "R" },
+{ "Georgia,Times,serif",
+ "Orfg" },
+{ "Georgia,Times,serif",
+ "&" },
+{ "Georgia,Times,serif",
+ "Jbefg" },
+{ "Georgia,Times,serif",
+ "2000" },
+{ "Georgia,Times,serif",
+ "Bhe" },
+{ "Georgia,Times,serif",
+ "cvpxf" },
+{ "Georgia,Times,serif",
+ "jung" },
+{ "Georgia,Times,serif",
+ "jr" },
+{ "Georgia,Times,serif",
+ "zbfg" },
+{ "Georgia,Times,serif",
+ "rawblrq" },
+{ "Georgia,Times,serif",
+ "Gurl'er" },
+{ "Georgia,Times,serif",
+ "Nyy" },
+{ "Georgia,Times,serif",
+ "Lbhe" },
+{ "Georgia,Times,serif",
+ "Sngure'f" },
+{ "Georgia,Times,serif",
+ "Byqfzbovyrf" },
+{ "Georgia,Times,serif",
+ "Abj" },
+{ "Georgia,Times,serif",
+ "N" },
+{ "Georgia,Times,serif",
+ "abfgnytvp" },
+{ "Georgia,Times,serif",
+ "erzrzorenapr" },
+{ "Georgia,Times,serif",
+ "jurryf" },
+{ "Georgia,Times,serif",
+ "cnfg" },
+{ "Georgia,Times,serif",
+ "Pbzvpf" },
+{ "Georgia,Times,serif",
+ "Guhzof-hc" },
+{ "Georgia,Times,serif",
+ "Puevf" },
+{ "Georgia,Times,serif",
+ "Jner" },
+{ "Georgia,Times,serif",
+ "naq" },
+{ "Georgia,Times,serif",
+ "Wbr" },
+{ "Georgia,Times,serif",
+ "Fnppb" },
+{ "Verdana,Arial,serif",
+ "H" },
+{ "Verdana,Arial,serif",
+ "P" },
+{ "Georgia,Times,serif",
+ "Nzrevpn'f" },
+{ "Georgia,Times,serif",
+ "Fpvrapr" },
+{ "Georgia,Times,serif",
+ "Zngu" },
+{ "Georgia,Times,serif",
+ "Tnc" },
+{ "Georgia,Times,serif",
+ "Rvtugu" },
+{ "Georgia,Times,serif",
+ "tenqref" },
+{ "Georgia,Times,serif",
+ "fpber" },
+{ "Georgia,Times,serif",
+ "orybj" },
+{ "Georgia,Times,serif",
+ "gurve" },
+{ "Georgia,Times,serif",
+ "crref" },
+{ "Georgia,Times,serif",
+ "bgure" },
+{ "Georgia,Times,serif",
+ "pbhagevrf" },
+{ "Georgia,Times,serif",
+ "Znyr" },
+{ "Georgia,Times,serif",
+ "Zvabevgl" },
+{ "Georgia,Times,serif",
+ "Nf" },
+{ "Georgia,Times,serif",
+ "zra" },
+{ "Georgia,Times,serif",
+ "fyvc" },
+{ "Georgia,Times,serif",
+ "44" },
+{ "Georgia,Times,serif",
+ "creprag" },
+{ "Georgia,Times,serif",
+ "haqretenqf," },
+{ "Georgia,Times,serif",
+ "fbzr" },
+{ "Georgia,Times,serif",
+ "pbyyrtrf" },
+{ "Georgia,Times,serif",
+ "npgviryl" },
+{ "Georgia,Times,serif",
+ "erpehvg" },
+{ "Georgia,Times,serif",
+ "gurz" },
+{ "Georgia,Times,serif",
+ "Arj" },
+{ "Georgia,Times,serif",
+ "Pnfr" },
+{ "Georgia,Times,serif",
+ "sbe" },
+{ "Georgia,Times,serif",
+ "Yngva" },
+{ "Georgia,Times,serif",
+ "Fbzr" },
+{ "Georgia,Times,serif",
+ "fpubbyf" },
+{ "Georgia,Times,serif",
+ "svaq" },
+{ "Georgia,Times,serif",
+ "xvqf" },
+{ "Georgia,Times,serif",
+ "yrnea" },
+{ "Georgia,Times,serif",
+ "zber" },
+{ "Georgia,Times,serif",
+ "nobhg" },
+{ "Georgia,Times,serif",
+ "Ratyvfu" },
+{ "Georgia,Times,serif",
+ "ol" },
+{ "Georgia,Times,serif",
+ "fghqlvat" },
+{ "Georgia,Times,serif",
+ "ynathntr" },
+{ "Georgia,Times,serif",
+ "napvrag" },
+{ "Georgia,Times,serif",
+ "Ebzr" },
+{ "Arial,Helvetica,serif",
+ "Zvyrfgbarf" },
+{ "Arial,Helvetica,serif",
+ "Gvzr'f" },
+{ "Arial,Helvetica,serif",
+ "ebhaqhc" },
+{ "Arial,Helvetica,serif",
+ "qrsvavat" },
+{ "Arial,Helvetica,serif",
+ "zbzragf" },
+{ "Arial,Helvetica,serif",
+ "yvirf" },
+{ "Arial,Helvetica,serif",
+ "jbeyq'f" },
+{ "Arial,Helvetica,serif",
+ "arjfznxref" },
+{ "Arial,Helvetica,serif",
+ "TB" },
+{ "Arial,Helvetica,serif",
+ "GB" },
+{ "Arial,Helvetica,serif",
+ "CNTR >>" },
+{ "Arial,Helvetica,serif",
+ "Pebffjbeq" },
+{ "Arial,Helvetica,serif",
+ "Bhe" },
+{ "Arial,Helvetica,serif",
+ "jrrxyl" },
+{ "Arial,Helvetica,serif",
+ "arjf" },
+{ "Arial,Helvetica,serif",
+ "chmmyr" },
+{ "Arial,Helvetica,serif",
+ "Bccbeghavgvrf" },
+{ "Arial,Helvetica,serif",
+ "Wbva" },
+{ "Arial,Helvetica,serif",
+ "GVZR'f" },
+{ "Arial,Helvetica,serif",
+ "Nssvyvngr" },
+{ "Arial,Helvetica,serif",
+ "Cebtenz" },
+{ "Arial,Helvetica,serif",
+ "Serr" },
+{ "Arial,Helvetica,serif",
+ "Cebqhpg" },
+{ "Arial,Helvetica,serif",
+ "Vasb" },
+{ "Arial,Helvetica,serif",
+ "Zrqvn" },
+{ "Arial,Helvetica,serif",
+ "Xvg" },
+{ "Arial,Helvetica,serif",
+ "GVZR" },
+{ "Arial,Helvetica,serif",
+ "obbx" },
+{ "Arial,Helvetica,serif",
+ "fryrpgvbaf" },
+{ "Arial,Helvetica,serif",
+ "GVZR" },
+{ "Arial,Helvetica,serif",
+ "Naahny:" },
+{ "Arial,Helvetica,serif",
+ "1999-2000" },
+{ "Arial,Helvetica,serif",
+ "100:" },
+{ "Arial,Helvetica,serif",
+ "Crefba" },
+{ "Arial,Helvetica,serif",
+ "Praghel" },
+{ "Arial,Helvetica,serif",
+ "Nyznanp" },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "75gu" },
+{ "Arial,Helvetica,serif",
+ "Naavirefnel" },
+{ "Arial,Helvetica,serif",
+ "Terng" },
+{ "Arial,Helvetica,serif",
+ "Vzntrf" },
+{ "Arial,Helvetica,serif",
+ "cnegaref" },
+{ "Arial,Helvetica,serif",
+ "Pbclevtug \xc2""\xa9"" 2000" },
+{ "Arial,Helvetica,serif",
+ "Gvzr" },
+{ "Arial,Helvetica,serif",
+ "Vap." },
+{ "Arial,Helvetica,serif",
+ "Nyy" },
+{ "Arial,Helvetica,serif",
+ "evtugf" },
+{ "Arial,Helvetica,serif",
+ "erfreirq." },
+{ "Arial,Helvetica,serif",
+ "Ercebqhpgvba" },
+{ "Arial,Helvetica,serif",
+ "jubyr" },
+{ "Arial,Helvetica,serif",
+ "be" },
+{ "Arial,Helvetica,serif",
+ "cneg" },
+{ "Arial,Helvetica,serif",
+ "jvgubhg" },
+{ "Arial,Helvetica,serif",
+ "crezvffvba" },
+{ "Arial,Helvetica,serif",
+ "vf" },
+{ "Arial,Helvetica,serif",
+ "cebuvovgrq." },
+{ "Arial,Helvetica,serif",
+ "SND" },
+{ "Arial,Helvetica,serif",
+ "|" },
+{ "Arial,Helvetica,serif",
+ "Fvgr" },
+{ "Arial,Helvetica,serif",
+ "Znc" },
+{ "Arial,Helvetica,serif",
+ "Frnepu" },
+{ "Arial,Helvetica,serif",
+ "Jevgr" },
+{ "Arial,Helvetica,serif",
+ "gb" },
+{ "Arial,Helvetica,serif",
+ "Hf" },
+{ "Arial,Helvetica,serif",
+ "Cevinpl" },
+{ "Arial,Helvetica,serif",
+ "Cbyvpl" },
+{ "Arial,Helvetica,serif",
+ "Grezf" },
+{ "Arial,Helvetica,serif",
+ "Hfr" },
+{ "Arial,Helvetica,serif",
+ "Cerff" },
+{ "Arial,Helvetica,serif",
+ "Eryrnfrf" },
+{ "Georgia,Times,serif",
+ "P" },
+{ "Georgia,Times,serif",
+ "b" },
+{ "Georgia,Times,serif",
+ "i" },
+{ "Georgia,Times,serif",
+ "r" },
+{ "Georgia,Times,serif",
+ "e" },
+{ "Arial,Helvetica,serif",
+ "N" },
+{ "Arial,Helvetica,serif",
+ "Pbafgvghgvbany" },
+{ "Arial,Helvetica,serif",
+ "Avtugzner" },
+{ "Arial,Helvetica,serif",
+ "Orsber" },
+{ "Arial,Helvetica,serif",
+ "Puevfgznf?" },
+{ "Georgia,Times,serif",
+ "J" },
+{ "Georgia,Times,serif",
+ "y" },
+{ "Georgia,Times,serif",
+ "q" },
+{ "Arial,Helvetica,serif",
+ "GVZR" },
+{ "Arial,Helvetica,serif",
+ "Vairfgvtngvba:" },
+{ "Arial,Helvetica,serif",
+ "Vfenry'f" },
+{ "Arial,Helvetica,serif",
+ "Jne" },
+{ "Arial,Helvetica,serif",
+ "Ntnvafg" },
+{ "Arial,Helvetica,serif",
+ "Cnyrfgvavna" },
+{ "Arial,Helvetica,serif",
+ "Puvyqera" },
+{ "Georgia,Times,serif",
+ "R" },
+{ "Georgia,Times,serif",
+ "a" },
+{ "Georgia,Times,serif",
+ "v" },
+{ "Georgia,Times,serif",
+ "z" },
+{ "Georgia,Times,serif",
+ "g" },
+{ "Arial,Helvetica,serif",
+ "Gur" },
+{ "Arial,Helvetica,serif",
+ "Cerfvqrag'f" },
+{ "Arial,Helvetica,serif",
+ "Ynfg-Zvahgr" },
+{ "Arial,Helvetica,serif",
+ "Chfu" },
+{ "Arial,Helvetica,serif",
+ "sbe" },
+{ "Arial,Helvetica,serif",
+ "n" },
+{ "Arial,Helvetica,serif",
+ "Terra" },
+{ "Arial,Helvetica,serif",
+ "Yrtnpl" },
+{ "Georgia,Times,serif",
+ "Z" },
+{ "Georgia,Times,serif",
+ "n" },
+{ "Arial,Helvetica,serif",
+ "Fgrcura" },
+{ "Arial,Helvetica,serif",
+ "Xvat" },
+{ "Arial,Helvetica,serif",
+ "Cbaqref" },
+{ "Arial,Helvetica,serif",
+ "uvf" },
+{ "Arial,Helvetica,serif",
+ "Nobegrq" },
+{ "Arial,Helvetica,serif",
+ "Nqiragher" },
+{ "Arial,Helvetica,serif",
+ "va" },
+{ "Arial,Helvetica,serif",
+ "Plorefcnpr" },
+{ "Georgia,Times,serif",
+ "N" },
+{ "Georgia,Times,serif",
+ "f" },
+{ "Arial,Helvetica,serif",
+ "Orfg" },
+{ "Arial,Helvetica,serif",
+ "naq" },
+{ "Arial,Helvetica,serif",
+ "Jbefg" },
+{ "Arial,Helvetica,serif",
+ "bs" },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "Fhofpevor" },
+{ "Arial,Helvetica,serif",
+ "sbe" },
+{ "Arial,Helvetica,serif",
+ "Xvqf" },
+{ "Arial,Helvetica,serif",
+ "Qvtvgny" },
+{ "Arial,Helvetica,serif",
+ "Vagreangvbany" },
+{ "Arial,Helvetica,serif",
+ "Rqvgvbaf" },
+{ "Arial,Helvetica,serif",
+ "Tvsg" },
+{ "Arial,Helvetica,serif",
+ "Fhofpevcgvbaf" },
+{ "Arial,Helvetica,serif",
+ "Phfgbzre" },
+{ "Arial,Helvetica,serif",
+ "Freivpr" },
+{ "Arial,Helvetica,serif",
+ "Erarj" },
+{ "Arial,Helvetica,serif",
+ "Punatr" },
+{ "Arial,Helvetica,serif",
+ "Nqqerff" },
+{ "Arial,Helvetica,serif",
+ "Cnl" },
+{ "Arial,Helvetica,serif",
+ "lbhe" },
+{ "Arial,Helvetica,serif",
+ "ovyy" },
+{ "Arial,Helvetica,serif",
+ "Naq" },
+{ "Arial,Helvetica,serif",
+ "zber..." },
+{ "Arial,Helvetica,serif",
+ "Arjf" },
+{ "Arial,Helvetica,serif",
+ "Erfhygf" },
+{ "Arial,Helvetica,serif",
+ "Cubgbf" },
+{ "Arial,Helvetica,serif",
+ "GI" },
+{ "Arial,Helvetica,serif",
+ "Pevgvp" },
+{ "Arial,Helvetica,serif",
+ "Pyvagba" },
+{ "Arial,Helvetica,serif",
+ "Nccenvfny" },
+{ "Arial,Helvetica,serif",
+ "Ryrpgbeny" },
+{ "Arial,Helvetica,serif",
+ "Pbyyrtr" },
+{ "Arial,Helvetica,serif",
+ "Cevzre" },
+{ "Arial,Helvetica,serif",
+ "Guvf" },
+{ "Arial,Helvetica,serif",
+ "Zbagu:" },
+{ "Arial,Helvetica,serif",
+ "Fcvevghnyvgl" },
+{ "Arial,Helvetica,serif",
+ "Cerivbhf:" },
+{ "Arial,Helvetica,serif",
+ "Qrfvta" },
+{ "Arial,Helvetica,serif",
+ "Fpvrapr" },
+{ "Arial,Helvetica,serif",
+ "Jro" },
+{ "Arial,Helvetica,serif",
+ "Svanapr" },
+{ "Arial,Helvetica,serif",
+ "Phyvanel" },
+{ "Arial,Helvetica,serif",
+ "Negf" },
+{ "Arial,Helvetica,serif",
+ "Pnveb" },
+{ "Arial,Helvetica,serif",
+ "pbeerfcbaqrag" },
+{ "Arial,Helvetica,serif",
+ "erpbeqf" },
+{ "Arial,Helvetica,serif",
+ "ure" },
+{ "Arial,Helvetica,serif",
+ "rkcrevraprf" },
+{ "Arial,Helvetica,serif",
+ "qhevat" },
+{ "Arial,Helvetica,serif",
+ "Vfynz'f" },
+{ "Arial,Helvetica,serif",
+ "ubyl" },
+{ "Arial,Helvetica,serif",
+ "zbagu" },
+{ "Arial,Helvetica,serif",
+ "Gur" },
+{ "Arial,Helvetica,serif",
+ "Ryrpgvba'f" },
+{ "Arial,Helvetica,serif",
+ "Svany" },
+{ "Arial,Helvetica,serif",
+ "48" },
+{ "Arial,Helvetica,serif",
+ "Ubhef" },
+{ "Arial,Helvetica,serif",
+ "ryrpgvba" },
+{ "Arial,Helvetica,serif",
+ "avtug" },
+{ "Arial,Helvetica,serif",
+ "gung" },
+{ "Arial,Helvetica,serif",
+ "jbhyqa'g" },
+{ "Arial,Helvetica,serif",
+ "raq" },
+{ "Arial,Helvetica,serif",
+ "svanyyl" },
+{ "Arial,Helvetica,serif",
+ "znqr" },
+{ "Arial,Helvetica,serif",
+ "vg" },
+{ "Arial,Helvetica,serif",
+ "qnja" },
+{ "Arial,Helvetica,serif",
+ "jvgu" },
+{ "Arial,Helvetica,serif",
+ "n" },
+{ "Arial,Helvetica,serif",
+ "Fhcerzr" },
+{ "Arial,Helvetica,serif",
+ "Pbheg" },
+{ "Arial,Helvetica,serif",
+ "ehyvat" },
+{ "Arial,Helvetica,serif",
+ "naq" },
+{ "Arial,Helvetica,serif",
+ "pnaqvqngrf'" },
+{ "Arial,Helvetica,serif",
+ "npprcgnapr" },
+{ "Arial,Helvetica,serif",
+ "bhgpbzr.12/00" },
+{ "Arial,Helvetica,serif",
+ "Lrne" },
+{ "Arial,Helvetica,serif",
+ "Uvfgbel" },
+{ "Arial,Helvetica,serif",
+ "Ba" },
+{ "Arial,Helvetica,serif",
+ "Qrp." },
+{ "Arial,Helvetica,serif",
+ "17," },
+{ "Arial,Helvetica,serif",
+ "nabhapr" },
+{ "Arial,Helvetica,serif",
+ "Lrne." },
+{ "Arial,Helvetica,serif",
+ "Gnxr" },
+{ "Arial,Helvetica,serif",
+ "ybbx" },
+{ "Arial,Helvetica,serif",
+ "onpx" },
+{ "Arial,Helvetica,serif",
+ "ng" },
+{ "Arial,Helvetica,serif",
+ "cnfg" },
+{ "Arial,Helvetica,serif",
+ "jvaaref," },
+{ "Arial,Helvetica,serif",
+ "sebz" },
+{ "Arial,Helvetica,serif",
+ "Yvaqoretu" },
+{ "Arial,Helvetica,serif",
+ "Pyvagba." },
+{ "Arial,Helvetica,serif",
+ "12/00" },
+{ "Arial,Helvetica,serif",
+ "Juvpu" },
+{ "Arial,Helvetica,serif",
+ "bayvar" },
+{ "Arial,Helvetica,serif",
+ "oebxre" },
+{ "Arial,Helvetica,serif",
+ "cnlf" },
+{ "Arial,Helvetica,serif",
+ "lbh" },
+{ "Arial,Helvetica,serif",
+ "$175.00" },
+{ "Arial,Helvetica,serif",
+ "gb" },
+{ "Arial,Helvetica,serif",
+ "bcra" },
+{ "Arial,Helvetica,serif",
+ "na" },
+{ "Arial,Helvetica,serif",
+ "nppbhag?" },
+{ "Arial,Helvetica,serif",
+ "Pyvpx" },
+{ "Arial,Helvetica,serif",
+ "urer." },
+{ "Arial,Helvetica,serif",
+ "Guhefqnl," },
+{ "Arial,Helvetica,serif",
+ "Qrprzore" },
+{ "Arial,Helvetica,serif",
+ "14," },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "Gbc" },
+{ "Arial,Helvetica,serif",
+ "Frnepurf" },
+{ "Arial,Helvetica,serif",
+ "Pbiref" },
+{ "Arial,Helvetica,serif",
+ "ZC3" },
+{ "Arial,Helvetica,serif",
+ "Zvqrnfg" },
+{ "Arial,Helvetica,serif",
+ "Ryrpgvba" },
+{ "Verdana,Arial,serif",
+ "NQINAPRQ" },
+{ "Verdana,Arial,serif",
+ "FRNEPU" },
+{ "Verdana,Arial,serif",
+ "GVZR:" },
+{ "Verdana,Arial,serif",
+ "FRNEPU GVZR:" },
+{ "Verdana,Arial,serif",
+ "NQINAPRQ FRNEPU" },
+{ "Arial,Helvetica,serif",
+ " Guhefqnl, Qrprzore 14, 2000" },
+{ "Arial,Helvetica,serif",
+ "Gbc Frnepurf " },
+{ "Arial,Helvetica,serif",
+ " " },
+{ "Arial,Helvetica,serif",
+ " " },
+{ "Georgia,Times,serif",
+ "N Xvaqre, Tragyre Fhcerzr Pbheg?" },
+{ "Arial,Helvetica,serif",
+ "ZNEPL AVTUFJNAQRE/NC" },
+{ "Arial,Helvetica,serif",
+ "Ohfu'f abzvarrf jvyy yvxryl or va " },
+{ "Arial,Helvetica,serif",
+ "gur pragevfg zbyq bs Nagubal " },
+{ "Georgia,Times,serif",
+ "Gur choyvp'f cbfg-ryrpgvba creprcgvba bs " },
+{ "Georgia,Times,serif",
+ "gur Havgrq Fgngrf Fhcerzr Pbheg vf yvxryl " },
+{ "Georgia,Times,serif",
+ "gb zrna gung Ohfu'f whqvpvny nccbvagrrf " },
+{ "Georgia,Times,serif",
+ "jvyy or zbqrengr" },
+{ "Georgia,Times,serif",
+ "gur jubyr fgbel >>" },
+{ "Georgia,Times,serif",
+ "Eryngrq: " },
+{ "Georgia,Times,serif",
+ "Vafvqr gur " },
+{ "Georgia,Times,serif",
+ "Ohfu Qlanfgl" },
+{ "Georgia,Times,serif",
+ "Genafvgvba " },
+{ "Georgia,Times,serif",
+ "Cubgb Rffnl: " },
+{ "Georgia,Times,serif",
+ "Gur " },
+{ "Georgia,Times,serif",
+ "Ryrpgvba'f Svany 48 " },
+{ "Georgia,Times,serif",
+ "Ohfu Fgnegf " },
+{ "Georgia,Times,serif",
+ "Cerfvqrapl Jvgu n " },
+{ "Georgia,Times,serif",
+ "Ivpgbel fcrrpu " },
+{ "Georgia,Times,serif",
+ "qjryyf yrff ba gur " },
+{ "Georgia,Times,serif",
+ "ivpgbe guna ba " },
+{ "Georgia,Times,serif",
+ "pbapvyvngvba " },
+{ "Georgia,Times,serif",
+ "Ubj gur Jbeyq " },
+{ "Georgia,Times,serif",
+ "Frrf Cerfvqrag " },
+{ "Georgia,Times,serif",
+ "Ohfu" },
+{ "Georgia,Times,serif",
+ "N guhzoanvy fxrgpu " },
+{ "Georgia,Times,serif",
+ "bs ubcrf, srnef naq " },
+{ "Georgia,Times,serif",
+ "P b i r e" },
+{ "Arial,Helvetica,serif",
+ "N Pbafgvghgvbany Avtugzner " },
+{ "Arial,Helvetica,serif",
+ "Orsber Puevfgznf?" },
+{ "Georgia,Times,serif",
+ "J b e y q" },
+{ "Arial,Helvetica,serif",
+ "N GVZR Vairfgvtngvba: Vfenry'f " },
+{ "Arial,Helvetica,serif",
+ "Jne Ntnvafg Cnyrfgvavna " },
+{ "Georgia,Times,serif",
+ "R a i v e b a z r a g" },
+{ "Arial,Helvetica,serif",
+ "Gur Cerfvqrag'f Ynfg-Zvahgr " },
+{ "Arial,Helvetica,serif",
+ "Chfu sbe n Terra Yrtnpl" },
+{ "Georgia,Times,serif",
+ "Z r q v n" },
+{ "Arial,Helvetica,serif",
+ "Fgrcura Xvat Cbaqref uvf " },
+{ "Arial,Helvetica,serif",
+ "Nobegrq Nqiragher va " },
+{ "Georgia,Times,serif",
+ "N e g f" },
+{ "Arial,Helvetica,serif",
+ "Gur Orfg naq Jbefg bs 2000" },
+{ "serif",
+ "32," },
+{ "serif",
+ "jjj.gvzr.pbz," },
+{ "serif",
+ "537" },
+{ "serif",
+ "1, 32, jjj.gvzr.pbz, 537" },
+{ "Verdana,Arial,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,Helvetica,serif",
+ "Cnyz" },
+{ "Arial,Helvetica,serif",
+ "Unaquryqf" },
+{ "Arial,Helvetica,serif",
+ "Zbfg" },
+{ "Arial,Helvetica,serif",
+ "Cbchyne" },
+{ "Arial,Helvetica,serif",
+ "Frnepu" },
+{ "Arial,Helvetica,serif",
+ "Negvpyrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xrljbeqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ubfgrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ol" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ner" },
+{ "Verdana,Arial,Helvetica,serif",
+ "UBG" },
+{ "Arial,Helvetica,serif",
+ "Jrypbzr" },
+{ "Arial,Helvetica,serif",
+ "gb" },
+{ "Arial,Helvetica,serif",
+ "Gbz'f" },
+{ "Arial,Helvetica,serif",
+ "Uneqjner" },
+{ "Arial,Helvetica,serif",
+ "Thvqr" },
+{ "Arial,Helvetica,serif",
+ "Hcqngrq:" },
+{ "Arial,Helvetica,serif",
+ "Qrprzore" },
+{ "Arial,Helvetica,serif",
+ "14," },
+{ "Arial,Helvetica,serif",
+ "2000" },
+{ "Arial,Helvetica,serif",
+ "Grpu" },
+{ "Arial,Helvetica,serif",
+ "Arjf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qrprzore" },
+{ "Verdana,Arial,Helvetica,serif",
+ "14," },
+{ "Verdana,Arial,Helvetica,serif",
+ "2000" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Abory" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jvaare" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fnlf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzchgvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ceboyrzf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "or" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fbyirq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fbba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "IVN" },
+{ "Verdana,Arial,Helvetica,serif",
+ "XG133N" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcfrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrgnvyf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Juvgr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "YRQf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ercynpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvtug" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohyof" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xvatfgba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "EVZZf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bire-cevprq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gbc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sverjnyyf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rnfvyl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cvreprq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "13," },
+{ "Verdana,Arial,Helvetica,serif",
+ "VOZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fcraq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "$1O" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yvahk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "va" },
+{ "Verdana,Arial,Helvetica,serif",
+ "01" },
+{ "Verdana,Arial,Helvetica,serif",
+ "GFZP" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fuvcf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "0.13-Zvpeba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohvyqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fhcrepbzchgre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Furyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Dhnaghz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzchgref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pybfre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "guna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gubhtug" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yvahk," },
+{ "Verdana,Arial,Helvetica,serif",
+ "C4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZF" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernqvrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ancfgre-yvxr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jvaqbjf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "12," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Enzohf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ynjlref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gnetrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Aivqvn" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "phgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cevprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FVF" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cynaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QQE" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcfrgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagry," },
+{ "Verdana,Arial,Helvetica,serif",
+ "1.3TUm" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nobhg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "$300" },
+{ "Verdana,Arial,Helvetica,serif",
+ "naabhaprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "0.13" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zvpeba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "oernxguebhtu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagry" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gbhgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "30az" },
+{ "Verdana,Arial,Helvetica,serif",
+ "genafvfgbe" },
+{ "Arial,serif",
+ "Tencuvpf" },
+{ "Arial,serif",
+ "Thvqr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "BcraTY" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gheobobbfgre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "-" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qvnzbaq'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Sver" },
+{ "Verdana,Arial,Helvetica,serif",
+ "TY2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "N" },
+{ "Verdana,Arial,Helvetica,serif",
+ "arj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jbexfgngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tencuvpf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pneq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rdhvccrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jvgu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "64" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZO" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QQE" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FTENZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "punyyratrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "AIVQVN'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Dhnqeb2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ceb." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "obgu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pneqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "svtug" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bhg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bhe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nguyba," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragvhz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "VVV" },
+{ "Verdana,Arial,Helvetica,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "grfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cyngsbezf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Urnq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gjb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Urnq:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zngebk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zvyyraavhz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "T450" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ryfn" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tynqvnp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Hygen:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "N" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cresrpg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tencuvpf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pneq?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gur" },
+{ "Verdana,Arial,Helvetica,serif",
+ "RIVY" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xleb?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zber" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Eryrnfrf" },
+{ "Arial,serif",
+ "PCH" },
+{ "Verdana,Arial,Helvetica,serif",
+ "11," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ybeq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xelb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Chgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Uvf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Unaqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "17" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbbyref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rkgerzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "birepybpxvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bsgra" },
+{ "Verdana,Arial,Helvetica,serif",
+ "snvyf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qhr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vafhssvpvrag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PCH" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbbyvat." },
+{ "Verdana,Arial,Helvetica,serif",
+ "unir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "grfgrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "17" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qvssrerag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbbyref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gryy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "lbh" },
+{ "Verdana,Arial,Helvetica,serif",
+ "juvpu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbqryf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ner" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fhvgnoyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "birepybpxvat." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragvhz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "if." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nguyba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Svany" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Erpbhag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnvagvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "n" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Arj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cvpgher" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gjrnxrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rapbqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vzcbegnag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rinyhngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Hcqngr" },
+{ "Arial,serif",
+ "Pbyhzaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Abirzore" },
+{ "Verdana,Arial,Helvetica,serif",
+ "28," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nabgure" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Erpbhag?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gur" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fvghngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rinyhngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erzvaqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znal" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gur" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fghss" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gung'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pheeragyl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tbvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "va" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fgngr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Sybevqn." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zhpu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jbhyq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvxr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "frr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "na" },
+{ "Verdana,Arial,Helvetica,serif",
+ "raq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "guvf," },
+{ "Verdana,Arial,Helvetica,serif",
+ "snpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "snpg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gung" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fbzrgvzrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "engure" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qvssvphyg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "svaq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gehgu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gnxrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "n" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ovg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zber" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gvzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "guna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcrpgrq." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Guvf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "negvpyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fhccbfrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcynva" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernfba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "oruvaq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "'" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragvhz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erpbhag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fvghngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "'." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gbz'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Uneqjner" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Thvqr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbzzhavgl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ovgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "&" },
+{ "Verdana,Arial,Helvetica,serif",
+ "olgrf:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Dhvrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Mbar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "olgrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "VQS," },
+{ "Verdana,Arial,Helvetica,serif",
+ "C4," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fbpxrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "7" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tnzvat," },
+{ "Verdana,Arial,Helvetica,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zber..." },
+{ "Arial,serif",
+ "Ohfvarff" },
+{ "Verdana,Arial,Helvetica,serif",
+ "18," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbzqrk!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jr'ir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tbg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vzcbegnag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Unzzre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "arjf," },
+{ "Verdana,Arial,Helvetica,serif",
+ "trg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cynl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zvpebfbsg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Kobk," },
+{ "Verdana,Arial,Helvetica,serif",
+ "gnxr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ybbx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ng" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NGv'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbbyvb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pber" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ybtvp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcfrg," },
+{ "Verdana,Arial,Helvetica,serif",
+ "erivfvg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zvpeba'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcfrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cynaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tnaqref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tnqtrgf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbzqrk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qnl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "1:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ebnqznc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pragnhef" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qentbaf," },
+{ "Verdana,Arial,Helvetica,serif",
+ "QQE" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FQENZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Enzohf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ng" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCS" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rzorqqrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "UGZY" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ornpbaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jbeel" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcregf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Onaq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebgrfgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ancfgre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ona" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,serif",
+ "snaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fhccerffvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrivpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nvqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "42-Ibyg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pne" },
+{ "Verdana,Arial,Helvetica,serif",
+ "flfgrzf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rqtne" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nyyna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cbr'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "chmmyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrpelcgrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tbyqra" },
+{ "Verdana,Arial,Helvetica,serif",
+ "r-zbarl?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "10," },
+{ "Verdana,Arial,Helvetica,serif",
+ "ANFN" },
+{ "Verdana,Arial,Helvetica,serif",
+ "frrxf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "oht-serr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zvpebsyhvqvp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "snoevpngrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fngryyvgr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cubgbf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fnyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrohgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "arj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vzntvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "9," },
+{ "Verdana,Arial,Helvetica,serif",
+ "1.13TUm" },
+{ "Verdana,Arial,Helvetica,serif",
+ "CVVV" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pelcgb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tebhc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znxr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "EVC" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbbg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "chgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QO" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znvasenzrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Enl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "thaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fgha" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ivpgvzf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "8," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fha" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fgnegf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Wnin" },
+{ "Verdana,Arial,Helvetica,serif",
+ "C2C" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebwrpg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Bayvar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "CP" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fnyrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrsl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sberpnfgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fvrzraf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "grfgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "xvq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "genpxvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrivprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "VOZ," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vasvarba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zntargvp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ENZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragntba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebcf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vevqvhz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fngryyvgrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "7," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qryy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcnaqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "freire" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PRB" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vafhygf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "SOV" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hfrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "xrlfgebxr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erpbeqre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "\"zbo" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pnfr\"" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qrnq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Phyg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "prafbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fgbccre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NEZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ubyqvatf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cevzre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "6," },
+{ "Verdana,Arial,Helvetica,serif",
+ "phg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fhaqnl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "HF" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QBW" },
+{ "Verdana,Arial,Helvetica,serif",
+ "purref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rhebcrna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "r-pevzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebcbfny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "CynlFgngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "1" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gbcf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbafbyr" },
+{ "Arial,serif",
+ "Pbafhzre" },
+{ "Arial,serif",
+ "Ryrpgebavpf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Snibevgr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tnqtrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Abznq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Whxrobk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sebz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Perngvir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jubrire" },
+{ "Verdana,Arial,Helvetica,serif",
+ "unfa'g" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zrffntr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ol" },
+{ "Verdana,Arial,Helvetica,serif",
+ "abj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jvyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "arire" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vg." },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3" },
+{ "Verdana,Arial,Helvetica,serif",
+ "xvpxf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fbzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znwbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohgg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "orpnhfr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernfbaf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gnxvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nqinagntr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rnfvre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rire" },
+{ "Verdana,Arial,Helvetica,serif",
+ "orsber," },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hagvy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erpragyl," },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbovyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3-qrivprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jrer" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvzvgrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bayl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ubhe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cynlgvzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "be" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fubpx-frafvgvir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3-PQEBZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrivprf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Perngvir'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Abznq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Whxrobk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hfvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "6" },
+{ "Verdana,Arial,Helvetica,serif",
+ "TO" },
+{ "Verdana,Arial,Helvetica,serif",
+ "abgrobbx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "uneq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qevir," },
+{ "Verdana,Arial,Helvetica,serif",
+ "bssref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gerzraqbhf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nzbhag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3-fgbentr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fcnpr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ubjrire," },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvggyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fxvyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rnfvyl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "or" },
+{ "Verdana,Arial,Helvetica,serif",
+ "'hctenqrq'" },
+{ "Verdana,Arial,Helvetica,serif",
+ "20" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rira" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zber." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fhzn'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Arba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cynlre:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Punyyratvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qvnzbaq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Evb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nznmvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cbffvovyvgvrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnyz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "VVVp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ik" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zbovyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zhfvpvnaf:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Guerr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZC3-Cynlref" },
+{ "Arial,serif",
+ "Znvaobneq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Bpgbore" },
+{ "Verdana,Arial,Helvetica,serif",
+ "30," },
+{ "Verdana,Arial,Helvetica,serif",
+ "QQE-FQENZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Unf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Svanyyl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Neevirq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gbqnl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ynhapuvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "760" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebivqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "svefg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bssvpvny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cyngsbez" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FQENZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fhccbeg." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jvyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "guvf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zrzbel" },
+{ "Verdana,Arial,Helvetica,serif",
+ "glcr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "noyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "uvtu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcrpgngvbaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "trarengrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ynfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "12" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbaguf?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "CP2100" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gjb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nguyba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cyngsbezf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ760" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "IVN'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hcpbzvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ncbyyb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ceb266." },
+{ "Verdana,Arial,Helvetica,serif",
+ "IVN'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fbhgu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Oevqtr:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "IG82P686O" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fhccbegvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "HygenNGN/100" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Hfhecre?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FvF630F" },
+{ "Verdana,Arial,Helvetica,serif",
+ "punyyratrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gur" },
+{ "Verdana,Arial,Helvetica,serif",
+ "v815r" },
+{ "Verdana,Arial,Helvetica,serif",
+ "arj," },
+{ "Verdana,Arial,Helvetica,serif",
+ "varkcrafvir," },
+{ "Verdana,Arial,Helvetica,serif",
+ "vagrtengrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "370" },
+{ "Verdana,Arial,Helvetica,serif",
+ "puvcfrg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jvgu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NTC4K" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbzvat:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Grfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "10" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Obneqf" },
+{ "Arial,serif",
+ "Fgbentr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "23," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ohea," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Onol," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ohea!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NBcra" },
+{ "Verdana,Arial,Helvetica,serif",
+ "12fcrrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jevgre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zber" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzcnavrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "eryrnfvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gurve" },
+{ "Verdana,Arial,Helvetica,serif",
+ "12fcrrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jevgref." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yrg'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "dhvpx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NBcra'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PEJ-1232" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ninvynoyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "avpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cevpr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "2.2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "TO" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cbegnoyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qvfpf:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pnfgyrjbbq'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "BEO" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qevir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "FPFV" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yvirf!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Dhnaghz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ngynf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "10X" },
+{ "Verdana,Arial,Helvetica,serif",
+ "VV" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Bs" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pncnpvgl:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QvnzbaqZnk" },
+{ "Verdana,Arial,Helvetica,serif",
+ "80" },
+{ "Arial,serif",
+ "UbjGb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Frcgrzore" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ghavat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rkgerzr:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Birepybpxvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nfhf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "N7I" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fznyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbqvsvpngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ibygntr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbagebyyre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nfhf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "N7I" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vzcebirf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pncnovyvgvrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qheba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebprffbef." },
+{ "Verdana,Arial,Helvetica,serif",
+ "npghny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gevpx:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fgnoyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "uvture" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ibygntr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zbqvslvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Na" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zbgureobneq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Sbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qheba-Birepybpxvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Byqvr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ghavat:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "C55G2C4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "X6-2+/500" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ohvyqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Flfgrz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "va" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cynfgvp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fhoznevar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ybbx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "na" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagrerfgvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pnfr" },
+{ "Arial,serif",
+ "Qvtvgny" },
+{ "Arial,serif",
+ "Ivqrb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT-4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbclvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QIQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ivqrb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PQ-EBZ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jryy-xabja" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QIQ/ZCRT-2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ivqrb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "frdhraprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ybg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qvfx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znxrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qvfgevohgvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PQ-EBZf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hafhvgnoyr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "gbby" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fubjf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gurer" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jnl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbaireg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qngn" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sebz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT-2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vagb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "oenaq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT-4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbezng." },
+{ "Verdana,Arial,Helvetica,serif",
+ "fnirf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ybg!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Lbh" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohea" },
+{ "Verdana,Arial,Helvetica,serif",
+ "110" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zvahgrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "shyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erfbyhgvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbbgntr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "bagb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PQ-E." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qvtvgny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zhygv-Gnyrag:" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fbal" },
+{ "Verdana,Arial,Helvetica,serif",
+ "QPE-CP100R" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Snfpvangvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "GI" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pneqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "CP" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Orggre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fbyhgvba?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zhygvgnyragrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Nyy-va-Bar" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tencuvp" },
+{ "Arial,serif",
+ "Qvfcynl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Whyl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "1999" },
+{ "Verdana,Arial,Helvetica,serif",
+ "GSG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cneg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "3" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagresnprf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Hfvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fgnaqneq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ITN" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbaarpgbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "GSG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qvfcynl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nyzbfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nofheq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fvghngvba." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ivrjvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Natyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Grpuabybtvrf" },
+{ "serif",
+ "Gbz'f" },
+{ "serif",
+ "Uneqjner" },
+{ "serif",
+ "Gbz'f " },
+{ "serif",
+ "Thvqr" },
+{ "serif",
+ "Uneqjner " },
+{ "serif",
+ "Svaq" },
+{ "serif",
+ "gur" },
+{ "serif",
+ "ybjrfg" },
+{ "serif",
+ "Svaq gur " },
+{ "serif",
+ "cevprf" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "ybjrfg cevprf " },
+{ "serif",
+ "PARG" },
+{ "serif",
+ "Fubccre!" },
+{ "serif",
+ "ng PARG " },
+{ "sans-serif",
+ "Tb" },
+{ "Arial,Helvetica,serif",
+ "Cnyz Unaquryqf" },
+{ "Arial,Helvetica,serif",
+ "Zbfg Cbchyne" },
+{ "serif",
+ "Yngrfg" },
+{ "serif",
+ "Grpu" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Yngrfg Grpu Arjf" },
+{ "serif",
+ "Cevprf" },
+{ "serif",
+ "Frnepu Cevprf" },
+{ "serif",
+ "Negvpyrf" },
+{ "serif",
+ "Frnepu Negvpyrf" },
+{ "serif",
+ "Xrljbeq" },
+{ "serif",
+ "Vaqrk" },
+{ "serif",
+ "Xrljbeq Vaqrk" },
+{ "serif",
+ "Nepuvir" },
+{ "serif",
+ "Pbzzhavgl" },
+{ "serif",
+ "Pbyhzaf" },
+{ "serif",
+ "Thvqrf" },
+{ "serif",
+ "Bireivrj" },
+{ "serif",
+ "Gbz'f Thvqrf " },
+{ "serif",
+ "Znvaobneq" },
+{ "serif",
+ "Znvaobneq Thvqr" },
+{ "serif",
+ "PCH" },
+{ "serif",
+ "PCH Thvqr" },
+{ "serif",
+ "Tencuvp" },
+{ "serif",
+ "Tencuvp Thvqr" },
+{ "serif",
+ "Qvfcynl" },
+{ "serif",
+ "Qvfcynl Thvqr" },
+{ "serif",
+ "Qvtvgny" },
+{ "serif",
+ "Ivqrb" },
+{ "serif",
+ "Qvtvgny Ivqrb " },
+{ "serif",
+ "Fgbentr" },
+{ "serif",
+ "Fgbentr Thvqr" },
+{ "serif",
+ "Ohfvarff" },
+{ "serif",
+ "Ohfvarff Thvqr" },
+{ "serif",
+ "Pbafhzre" },
+{ "serif",
+ "Ryrpgebavpf" },
+{ "serif",
+ "Pbafhzre " },
+{ "serif",
+ "UbjGb" },
+{ "serif",
+ "Yvaxf" },
+{ "Arial,Helvetica,serif",
+ "Grpu Arjf" },
+{ "Verdana,Arial,Helvetica,serif",
+ " " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yngrfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Yngrfg " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Grpuabybtl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Grpuabybtl " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qrprzore 14, 2000" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Abory jvaare fnlf " },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzchgvat ceboyrzf gb " },
+{ "Verdana,Arial,Helvetica,serif",
+ "or fbyirq fbba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "IVN XG133N puvcfrg " },
+{ "Verdana,Arial,Helvetica,serif",
+ "qrgnvyf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Juvgr YRQf gb ercynpr " },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvtug ohyof" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pyvpx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "urer" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pyvpx urer " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jvagrp" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sbe Jvagrp " },
+{ "Verdana,Arial,Helvetica,serif",
+ "onerobarf!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "NZQ " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Xvatfgba fnlf EVZZf " },
+{ "Verdana,Arial,Helvetica,serif",
+ "bire-cevprq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tencuvpf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Tencuvpf " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Thvqr" },
+{ "Arial,serif",
+ "Tencuvpf Thvqr" },
+{ "serif",
+ "PCH " },
+{ "Arial,serif",
+ "PCH Thvqr" },
+{ "serif",
+ " " },
+{ "Arial,Helvetica,serif",
+ "Jrypbzr gb Gbz'f Uneqjner Thvqr" },
+{ "Arial,Helvetica,serif",
+ "Hcqngrq: Qrprzore 14, 2000" },
+{ "Arial,Helvetica,serif",
+ "Tvtnolgr," },
+{ "Arial,Helvetica,serif",
+ "bar" },
+{ "Arial,Helvetica,serif",
+ "bs" },
+{ "Arial,Helvetica,serif",
+ "gbc" },
+{ "Arial,Helvetica,serif",
+ "3" },
+{ "Arial,Helvetica,serif",
+ "zbgureobneq" },
+{ "Arial,Helvetica,serif",
+ "pbzcnavrf" },
+{ "Arial,Helvetica,serif",
+ "va" },
+{ "Arial,Helvetica,serif",
+ "Gnvjna!" },
+{ "Arial,Helvetica,serif",
+ "Tvtnolgr, bar bs gbc 3 zbgureobneq pbzcnavrf va " },
+{ "Verdana,Arial,Helvetica,serif",
+ "ARJ!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qrprzore 13, 2000" },
+{ "Verdana,Arial,Helvetica,serif",
+ "BcraTY Gheobobbfgre - Qvnzbaq'f Sver TY2" },
+{ "Verdana,Arial,Helvetica,serif",
+ "N arj jbexfgngvba tencuvpf pneq rdhvccrq jvgu 64 ZO QQE FTENZ punyyratrf " },
+{ "Verdana,Arial,Helvetica,serif",
+ "AIVQVN'f Dhnqeb2 Ceb. Jr yrg obgu tencuvpf pneqf svtug vg bhg ba bhe Nguyba, " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragvhz VVV naq Cragvhz 4 grfg cyngsbezf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "ARJ!" },
+{ "Verdana,Arial,Helvetica,serif",
+ " " },
+{ "Verdana,Arial,Helvetica,serif",
+ "Urnq Gjb Urnq: Zngebk Zvyyraavhz T450" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ryfn Tynqvnp Hygen: N Cresrpg Tencuvpf Pneq?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gur RIVY Xleb?" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zber Eryrnfrf" },
+{ "serif",
+ "ARJ!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qrprzore 11, 2000" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ybeq Xelb Chgf Uvf Unaqf ba 17 Pbbyref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rkgerzr birepybpxvat bsgra snvyf qhr gb vafhssvpvrag PCH pbbyvat. Jr unir grfgrq " },
+{ "Verdana,Arial,Helvetica,serif",
+ "17 qvssrerag pbbyref naq gryy lbh juvpu zbqryf ner zbfg fhvgnoyr sbe birepybpxvat." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cragvhz 4 if. Nguyba - Svany Erpbhag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnvagvat n Arj Cvpgher bs Cragvhz 4 -" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gjrnxrq ZCRT4 Rapbqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "1" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Syng" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnary" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qvfcynlf" },
+{ "Arial,Helvetica,serif",
+ "Gbz'f" },
+{ "Arial,Helvetica,serif",
+ "Thvqrf" },
+{ "Arial,Helvetica,serif",
+ "Znvaobneq" },
+{ "Arial,Helvetica,serif",
+ "Tencuvpf" },
+{ "Arial,Helvetica,serif",
+ "Qvtvgny" },
+{ "Arial,Helvetica,serif",
+ "Ivqrb" },
+{ "Arial,Helvetica,serif",
+ "Ohfvarff" },
+{ "Arial,Helvetica,serif",
+ "UbjGb" },
+{ "Arial,Helvetica,serif",
+ "PCH" },
+{ "Arial,Helvetica,serif",
+ "Qvfcynl" },
+{ "Arial,Helvetica,serif",
+ "Fgbentr" },
+{ "Arial,Helvetica,serif",
+ "Pbafhzre" },
+{ "Arial,Helvetica,serif",
+ "Ryrpgebavpf" },
+{ "Arial,Helvetica,serif",
+ "Pbyhzaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbclevtug" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qbphzragf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fpevcgf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "orybatvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fvgr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gubznf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnofg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "1996" },
+{ "Verdana,Arial,Helvetica,serif",
+ "-" },
+{ "Verdana,Arial,Helvetica,serif",
+ "2000." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Zbfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vasbezngvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbagnvarq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbclevtugrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zngrevny." },
+{ "Verdana,Arial,Helvetica,serif",
+ "vyyrtny" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbcl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erqvfgevohgr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nal" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jvgubhg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rkcerffrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jevggra" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbafrag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nhgube." },
+{ "Verdana,Arial,Helvetica,serif",
+ "ABG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erfcbafvoyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "qnzntr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "znl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pnhfr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "lbhe" },
+{ "Verdana,Arial,Helvetica,serif",
+ "flfgrz." },
+{ "Arial,Helvetica,serif",
+ "Pbyhzaf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gubznf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnofg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Pbyhzaf" },
+{ "Arial,Helvetica,serif",
+ "Ubg!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jrypbzrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PARG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fubccre.pbz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gbz'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Uneqjner" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Thvqr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ragrevat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nyyvnapr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cnegarefuvc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "PARG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fubccre.pbz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "svyy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tnc" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernqre" },
+{ "Verdana,Arial,Helvetica,serif",
+ "freivprf." },
+{ "Verdana,Arial,Helvetica,serif",
+ "checbfr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nqqvgvba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cebivqr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "irel" },
+{ "Verdana,Arial,Helvetica,serif",
+ "orfg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cevpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzcnevfba" },
+{ "Verdana,Arial,Helvetica,serif",
+ "freivpr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernqref." },
+{ "Verdana,Arial,Helvetica,serif",
+ "PARG" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fubccre.pbz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vf," },
+{ "Verdana,Arial,Helvetica,serif",
+ "snpg," },
+{ "Verdana,Arial,Helvetica,serif",
+ "erpbtavmrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzcerurafvir" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erfcrpgrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "grpu" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fubccvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "flfgrz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Jro" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gbqnl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbaivaprq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "haovnfrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vaqrcraqrag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "tngurerq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "100" },
+{ "Verdana,Arial,Helvetica,serif",
+ "erfryyref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ernygvzr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "onfvf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rabezbhf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "orarsvg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "hfref" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jub" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jnag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zbarl." },
+{ "Verdana,Arial,Helvetica,serif",
+ "25," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Fvapr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "nobhg" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagry" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jnf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "sne" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ynml." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Juvyr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Vagry'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "HF-rzcyblrrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "rawblrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gunaxftvivat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ubyvqnl," },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rhebcrna" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbyyrnthrf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ohfl" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jbexvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gjrnx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "SynfX" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT" },
+{ "Verdana,Arial,Helvetica,serif",
+ "furqf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "yvtug" },
+{ "Verdana,Arial,Helvetica,serif",
+ "4'f" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ivqrb-rapbqvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "cresbeznapr." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Qba'g" },
+{ "Verdana,Arial,Helvetica,serif",
+ "zvff" },
+{ "Verdana,Arial,Helvetica,serif",
+ "gubfr" },
+{ "Verdana,Arial,Helvetica,serif",
+ "vafvtugf!" },
+{ "Arial,Helvetica,serif",
+ "Srrqonpx" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Ynlbhg!" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gryy" },
+{ "Verdana,Arial,Helvetica,serif",
+ "jung" },
+{ "Verdana,Arial,Helvetica,serif",
+ "guvax" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ynlbhg." },
+{ "Verdana,Arial,Helvetica,serif",
+ "nyfb" },
+{ "Verdana,Arial,Helvetica,serif",
+ "fraq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzzragf" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pevgvpvfz" },
+{ "Verdana,Arial,Helvetica,serif",
+ "pbzzrag" },
+{ "Verdana,Arial,Helvetica,serif",
+ "." },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cnvagvat" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Arj" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Cvpgher" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Gjrnxrq" },
+{ "Verdana,Arial,Helvetica,serif",
+ "ZCRT4" },
+{ "Verdana,Arial,Helvetica,serif",
+ "Rapbqvat" },
+{ "serif",
+ "24/7" },
+{ "serif",
+ "zrqvn" },
+{ "serif",
+ "33," },
+{ "serif",
+ "jjj.gbzfuneqjner.pbz," },
+{ "serif",
+ "454" },
+{ "serif",
+ "1, 33, jjj.gbzfuneqjner.pbz, 454" },
+{ "HELVETICA,ARIAL,serif",
+ "Jrypbzr" },
+{ "HELVETICA,ARIAL,serif",
+ "gb" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl.pbz!" },
+{ "HELVETICA,ARIAL,serif",
+ "Nyernql" },
+{ "HELVETICA,ARIAL,serif",
+ "n" },
+{ "HELVETICA,ARIAL,serif",
+ "zrzore?" },
+{ "HELVETICA,ARIAL,serif",
+ "Pyvpx" },
+{ "HELVETICA,ARIAL,serif",
+ "urer" },
+{ "HELVETICA,ARIAL,serif",
+ "gb" },
+{ "HELVETICA,ARIAL,serif",
+ "ybt" },
+{ "HELVETICA,ARIAL,serif",
+ "va." },
+{ "Lucida Grande",
+ "Fvgr" },
+{ "Lucida Grande",
+ "Fubegphgf" },
+{ "Lucida Grande",
+ "------------------" },
+{ "Lucida Grande",
+ "Zl" },
+{ "Lucida Grande",
+ "Gevcf" },
+{ "Lucida Grande",
+ "Phfgbzre" },
+{ "Lucida Grande",
+ "Freivpr" },
+{ "Lucida Grande",
+ "Geniry" },
+{ "Lucida Grande",
+ "Zrah" },
+{ "Lucida Grande",
+ "IVN" },
+{ "Lucida Grande",
+ "Envy" },
+{ "Lucida Grande",
+ "Pnanqn" },
+{ "Lucida Grande",
+ "Syvtug" },
+{ "Lucida Grande",
+ "Fgnghf" },
+{ "Lucida Grande",
+ "Zncf" },
+{ "Lucida Grande",
+ "Qevivat" },
+{ "Lucida Grande",
+ "Qverpgvbaf" },
+{ "Lucida Grande",
+ "Jrngure" },
+{ "Lucida Grande",
+ "Pheerapl" },
+{ "Lucida Grande",
+ "Pbairegre" },
+{ "Lucida Grande",
+ "Ohfvarff" },
+{ "Lucida Grande",
+ "Pragre" },
+{ "Lucida Grande",
+ "Zbovyr" },
+{ "Lucida Grande",
+ "Fbyhgvbaf" },
+{ "Lucida Grande",
+ "Fgber" },
+{ "Lucida Grande",
+ "Grnz" },
+{ "Lucida Grande",
+ "hc" },
+{ "Lucida Grande",
+ "gb" },
+{ "Lucida Grande",
+ "Geniryref" },
+{ "Lucida Grande",
+ "Argjbex" },
+{ "Lucida Grande",
+ "Ivqrb" },
+{ "Lucida Grande",
+ "Tnyyrel" },
+{ "Lucida Grande",
+ "Thvqr" },
+{ "Lucida Grande",
+ "Nobhg" },
+{ "Lucida Grande",
+ "Genirybpvgl" },
+{ "HELVETICA,ARIAL,serif",
+ "Zl" },
+{ "HELVETICA,ARIAL,serif",
+ "Gevcf" },
+{ "HELVETICA,ARIAL,serif",
+ " | " },
+{ "HELVETICA,ARIAL,serif",
+ "Nppbhag" },
+{ "sans-serif",
+ "Frnepu" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "qrynlf" },
+{ "HELVETICA,ARIAL,serif",
+ "npebff" },
+{ "HELVETICA,ARIAL,serif",
+ "gur" },
+{ "HELVETICA,ARIAL,serif",
+ "H.F...." },
+{ "HELVETICA,ARIAL,serif",
+ "!" },
+{ "HELVETICA,ARIAL,serif",
+ "Freivpr" },
+{ "HELVETICA,ARIAL,serif",
+ "Nyreg" },
+{ "HELVETICA,ARIAL,serif",
+ "Frphevgl" },
+{ "HELVETICA,ARIAL,serif",
+ "Thnenagrr" },
+{ "HELVETICA,ARIAL,serif",
+ "Cevinpl" },
+{ "HELVETICA,ARIAL,serif",
+ "Cbyvpl" },
+{ "HELVETICA,ARIAL,serif",
+ "Phfgbzre" },
+{ "HELVETICA,ARIAL,serif",
+ "Freivpr" },
+{ "HELVETICA,ARIAL,serif",
+ "Ragre" },
+{ "HELVETICA,ARIAL,serif",
+ "lbhe" },
+{ "HELVETICA,ARIAL,serif",
+ "qrfgvangvba" },
+{ "HELVETICA,ARIAL,serif",
+ "gb" },
+{ "HELVETICA,ARIAL,serif",
+ "svaq" },
+{ "HELVETICA,ARIAL,serif",
+ "qrnyf," },
+{ "HELVETICA,ARIAL,serif",
+ "snerf," },
+{ "HELVETICA,ARIAL,serif",
+ "thvqrf" },
+{ "HELVETICA,ARIAL,serif",
+ "&" },
+{ "HELVETICA,ARIAL,serif",
+ "zber:" },
+{ "HELVETICA,ARIAL,serif",
+ "Frnepu" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl.pbz" },
+{ "HELVETICA,ARIAL,serif",
+ "Yrnivat" },
+{ "HELVETICA,ARIAL,serif",
+ "sebz:" },
+{ "HELVETICA,ARIAL,serif",
+ "Tbvat" },
+{ "HELVETICA,ARIAL,serif",
+ "gb:" },
+{ "HELVETICA,ARIAL,serif",
+ "Nqhygf:" },
+{ "Lucida Grande",
+ "1" },
+{ "Lucida Grande",
+ "2" },
+{ "Lucida Grande",
+ "3" },
+{ "Lucida Grande",
+ "4" },
+{ "Lucida Grande",
+ "5" },
+{ "Lucida Grande",
+ "6" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrcneg:" },
+{ "Lucida Grande",
+ "Wna" },
+{ "Lucida Grande",
+ "Sro" },
+{ "Lucida Grande",
+ "Znepu" },
+{ "Lucida Grande",
+ "Ncevy" },
+{ "Lucida Grande",
+ "Znl" },
+{ "Lucida Grande",
+ "Whar" },
+{ "Lucida Grande",
+ "Whyl" },
+{ "Lucida Grande",
+ "Nht" },
+{ "Lucida Grande",
+ "Frcg" },
+{ "Lucida Grande",
+ "Bpg" },
+{ "Lucida Grande",
+ "Abi" },
+{ "Lucida Grande",
+ "Qrp" },
+{ "Lucida Grande",
+ "7" },
+{ "Lucida Grande",
+ "8" },
+{ "Lucida Grande",
+ "9" },
+{ "Lucida Grande",
+ "10" },
+{ "Lucida Grande",
+ "11" },
+{ "Lucida Grande",
+ "12" },
+{ "Lucida Grande",
+ "13" },
+{ "Lucida Grande",
+ "14" },
+{ "Lucida Grande",
+ "15" },
+{ "Lucida Grande",
+ "16" },
+{ "Lucida Grande",
+ "17" },
+{ "Lucida Grande",
+ "18" },
+{ "Lucida Grande",
+ "19" },
+{ "Lucida Grande",
+ "20" },
+{ "Lucida Grande",
+ "21" },
+{ "Lucida Grande",
+ "22" },
+{ "Lucida Grande",
+ "23" },
+{ "Lucida Grande",
+ "24" },
+{ "Lucida Grande",
+ "25" },
+{ "Lucida Grande",
+ "26" },
+{ "Lucida Grande",
+ "27" },
+{ "Lucida Grande",
+ "28" },
+{ "Lucida Grande",
+ "29" },
+{ "Lucida Grande",
+ "30" },
+{ "Lucida Grande",
+ "31" },
+{ "HELVETICA,ARIAL,serif",
+ "Erghea:" },
+{ "HELVETICA,ARIAL,serif",
+ "Fubj" },
+{ "HELVETICA,ARIAL,serif",
+ "zr" },
+{ "HELVETICA,ARIAL,serif",
+ "gur" },
+{ "HELVETICA,ARIAL,serif",
+ "orfg" },
+{ "HELVETICA,ARIAL,serif",
+ "ebhaqgevc" },
+{ "HELVETICA,ARIAL,serif",
+ "snerf" },
+{ "HELVETICA,ARIAL,serif",
+ "sbe:" },
+{ "HELVETICA,ARIAL,serif",
+ "nal" },
+{ "HELVETICA,ARIAL,serif",
+ "qngr " },
+{ "HELVETICA,ARIAL,serif",
+ "fcrpvsvp" },
+{ "HELVETICA,ARIAL,serif",
+ "qngrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl" },
+{ "HELVETICA,ARIAL,serif",
+ "orfg" },
+{ "HELVETICA,ARIAL,serif",
+ "sner" },
+{ "HELVETICA,ARIAL,serif",
+ "svaqre" },
+{ "HELVETICA,ARIAL,serif",
+ "Obfgba" },
+{ "HELVETICA,ARIAL,serif",
+ "Beynaqb" },
+{ "HELVETICA,ARIAL,serif",
+ "Cuvynqrycuvn" },
+{ "HELVETICA,ARIAL,serif",
+ "Ybf" },
+{ "HELVETICA,ARIAL,serif",
+ "Natryrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Ynf" },
+{ "HELVETICA,ARIAL,serif",
+ "Irtnf" },
+{ "HELVETICA,ARIAL,serif",
+ "Arj" },
+{ "HELVETICA,ARIAL,serif",
+ "Lbex" },
+{ "HELVETICA,ARIAL,serif",
+ "Fna" },
+{ "HELVETICA,ARIAL,serif",
+ "Senapvfpb" },
+{ "HELVETICA,ARIAL,serif",
+ "$201" },
+{ "HELVETICA,ARIAL,serif",
+ "$109" },
+{ "HELVETICA,ARIAL,serif",
+ "$78" },
+{ "HELVETICA,ARIAL,serif",
+ "$298" },
+{ "HELVETICA,ARIAL,serif",
+ "Obbx" },
+{ "HELVETICA,ARIAL,serif",
+ "vg!" },
+{ "HELVETICA,ARIAL,serif",
+ ">" },
+{ "HELVETICA,ARIAL,serif",
+ "Crefbanyvmr" },
+{ "HELVETICA,ARIAL,serif",
+ "Sner" },
+{ "HELVETICA,ARIAL,serif",
+ "Jngpure" },
+{ "HELVETICA,ARIAL,serif",
+ "crefbanyvmr" },
+{ "HELVETICA,ARIAL,serif",
+ "Sner" },
+{ "HELVETICA,ARIAL,serif",
+ "Jngpure" },
+{ "HELVETICA,ARIAL,serif",
+ "Oevgvfu" },
+{ "HELVETICA,ARIAL,serif",
+ "Nvejnlf" },
+{ "HELVETICA,ARIAL,serif",
+ "Jr" },
+{ "HELVETICA,ARIAL,serif",
+ "cersre" },
+{ "HELVETICA,ARIAL,serif",
+ "ZnfgrePneq!" },
+{ "HELVETICA,ARIAL,serif",
+ "Cnegaref" },
+{ "HELVETICA,ARIAL,serif",
+ "Ohfvarff" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "Pragre" },
+{ "HELVETICA,ARIAL,serif",
+ "ARJ!" },
+{ "HELVETICA,ARIAL,serif",
+ "Vafgnag" },
+{ "HELVETICA,ARIAL,serif",
+ "npprff" },
+{ "HELVETICA,ARIAL,serif",
+ "gb" },
+{ "HELVETICA,ARIAL,serif",
+ "gbc" },
+{ "HELVETICA,ARIAL,serif",
+ "ohfvarff" },
+{ "HELVETICA,ARIAL,serif",
+ "geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "gbbyf." },
+{ "HELVETICA,ARIAL,serif",
+ "Ercrng" },
+{ "HELVETICA,ARIAL,serif",
+ "gevcf," },
+{ "HELVETICA,ARIAL,serif",
+ "znantr" },
+{ "HELVETICA,ARIAL,serif",
+ "geniry," },
+{ "HELVETICA,ARIAL,serif",
+ "zber" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniryref" },
+{ "HELVETICA,ARIAL,serif",
+ "Argjbex" },
+{ "HELVETICA,ARIAL,serif",
+ "Zrffntr" },
+{ "HELVETICA,ARIAL,serif",
+ "Obneqf" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniryre" },
+{ "HELVETICA,ARIAL,serif",
+ "Erivrjf" },
+{ "HELVETICA,ARIAL,serif",
+ "Gvcf" },
+{ "HELVETICA,ARIAL,serif",
+ "&" },
+{ "HELVETICA,ARIAL,serif",
+ "Nqivpr" },
+{ "HELVETICA,ARIAL,serif",
+ "Fvgr" },
+{ "HELVETICA,ARIAL,serif",
+ "Uvtuyvtugf" },
+{ "HELVETICA,ARIAL,serif",
+ "SERR" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl" },
+{ "HELVETICA,ARIAL,serif",
+ "Zntnmvar" },
+{ "HELVETICA,ARIAL,serif",
+ "Grnz" },
+{ "HELVETICA,ARIAL,serif",
+ "hc" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "Cebsvyrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Frng" },
+{ "HELVETICA,ARIAL,serif",
+ "Zncf" },
+{ "HELVETICA,ARIAL,serif",
+ "Qernz" },
+{ "HELVETICA,ARIAL,serif",
+ "Cebgrpgvba" },
+{ "HELVETICA,ARIAL,serif",
+ "Ubgryf" },
+{ "HELVETICA,ARIAL,serif",
+ "ba" },
+{ "HELVETICA,ARIAL,serif",
+ "n" },
+{ "HELVETICA,ARIAL,serif",
+ "znc" },
+{ "HELVETICA,ARIAL,serif",
+ "Fnirq" },
+{ "HELVETICA,ARIAL,serif",
+ "Vgvarenevrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Orfg" },
+{ "HELVETICA,ARIAL,serif",
+ "Sner" },
+{ "HELVETICA,ARIAL,serif",
+ "Svaqre" },
+{ "HELVETICA,ARIAL,serif",
+ "Zbovyr" },
+{ "HELVETICA,ARIAL,serif",
+ "Fbyhgvbaf" },
+{ "HELVETICA,ARIAL,serif",
+ ">" },
+{ "HELVETICA,ARIAL,serif",
+ "Ivrj" },
+{ "HELVETICA,ARIAL,serif",
+ "nyy" },
+{ "HELVETICA,ARIAL,serif",
+ "fvgr" },
+{ "HELVETICA,ARIAL,serif",
+ "uvtuyvtugf..." },
+{ "HELVETICA,ARIAL,serif",
+ "Purpx" },
+{ "HELVETICA,ARIAL,serif",
+ "Tngrf" },
+{ "HELVETICA,ARIAL,serif",
+ "&" },
+{ "HELVETICA,ARIAL,serif",
+ "Gvzrf" },
+{ "HELVETICA,ARIAL,serif",
+ "sbe" },
+{ "HELVETICA,ARIAL,serif",
+ "Syvtugf" },
+{ "HELVETICA,ARIAL,serif",
+ "Nveyvar:" },
+{ "serif",
+ " " },
+{ "Lucida Grande",
+ "Fryrpg" },
+{ "Lucida Grande",
+ "nveyvar" },
+{ "Lucida Grande",
+ "NrebZrkvpb" },
+{ "Lucida Grande",
+ "Nve" },
+{ "Lucida Grande",
+ "Senapr" },
+{ "Lucida Grande",
+ "Nynfxn" },
+{ "Lucida Grande",
+ "Nzrevpn" },
+{ "Lucida Grande",
+ "Jrfg" },
+{ "Lucida Grande",
+ "Nzrevpna" },
+{ "Lucida Grande",
+ "Oevgvfu" },
+{ "Lucida Grande",
+ "Nvejnlf" },
+{ "Lucida Grande",
+ "Zvqynaq" },
+{ "Lucida Grande",
+ "Pnanqvna" },
+{ "Lucida Grande",
+ "Pbagvaragny" },
+{ "Lucida Grande",
+ "Qrygn" },
+{ "Lucida Grande",
+ "Sebagvre" },
+{ "Lucida Grande",
+ "Vorevn" },
+{ "Lucida Grande",
+ "Vprynaqnve" },
+{ "Lucida Grande",
+ "Yhsgunafn" },
+{ "Lucida Grande",
+ "Zvqjrfg" },
+{ "Lucida Grande",
+ "Rkcerff" },
+{ "Lucida Grande",
+ "Zrkvpnan" },
+{ "Lucida Grande",
+ "Qr" },
+{ "Lucida Grande",
+ "Nivnpvba" },
+{ "Lucida Grande",
+ "Abegujrfg" },
+{ "Lucida Grande",
+ "GJN" },
+{ "Lucida Grande",
+ "Havgrq" },
+{ "Lucida Grande",
+ "HF" },
+{ "Lucida Grande",
+ "Ivetva" },
+{ "Lucida Grande",
+ "Ngynagvp" },
+{ "HELVETICA,ARIAL,serif",
+ "Syvtug" },
+{ "HELVETICA,ARIAL,serif",
+ "Ahzore:" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrgnvyrq" },
+{ "HELVETICA,ARIAL,serif",
+ "frnepu" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrprzore" },
+{ "HELVETICA,ARIAL,serif",
+ "14" },
+{ "HELVETICA,ARIAL,serif",
+ "2000" },
+{ "HELVETICA,ARIAL,serif",
+ "...Yvatrevat" },
+{ "HELVETICA,ARIAL,serif",
+ "jrngure" },
+{ "HELVETICA,ARIAL,serif",
+ "rssrpgf" },
+{ "HELVETICA,ARIAL,serif",
+ "qrynl" },
+{ "HELVETICA,ARIAL,serif",
+ "geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "H.F." },
+{ "HELVETICA,ARIAL,serif",
+ "Qrnyf" },
+{ "HELVETICA,ARIAL,serif",
+ "Arjf" },
+{ "HELVETICA,ARIAL,serif",
+ "zber..." },
+{ "HELVETICA,ARIAL,serif",
+ "\xe2""\x80""\xa2""" },
+{ "HELVETICA,ARIAL,serif",
+ "Yrtraq" },
+{ "HELVETICA,ARIAL,serif",
+ "Nveyvarf" },
+{ "HELVETICA,ARIAL,serif",
+ "grzcbenevyl" },
+{ "HELVETICA,ARIAL,serif",
+ "fhfcraqf" },
+{ "HELVETICA,ARIAL,serif",
+ "bcrengvbaf." },
+{ "HELVETICA,ARIAL,serif",
+ "Vasb" },
+{ "HELVETICA,ARIAL,serif",
+ "sbe" },
+{ "HELVETICA,ARIAL,serif",
+ "bhe" },
+{ "HELVETICA,ARIAL,serif",
+ "phfgbzref." },
+{ "HELVETICA,ARIAL,serif",
+ "Angvbany" },
+{ "HELVETICA,ARIAL,serif",
+ "naq" },
+{ "HELVETICA,ARIAL,serif",
+ "Fnir" },
+{ "HELVETICA,ARIAL,serif",
+ "15%." },
+{ "HELVETICA,ARIAL,serif",
+ "Syl" },
+{ "HELVETICA,ARIAL,serif",
+ "Nfvn" },
+{ "HELVETICA,ARIAL,serif",
+ "FNIR" },
+{ "HELVETICA,ARIAL,serif",
+ "Puvan" },
+{ "HELVETICA,ARIAL,serif",
+ "Nveyvarf--" },
+{ "HELVETICA,ARIAL,serif",
+ "snerf" },
+{ "HELVETICA,ARIAL,serif",
+ "sebz" },
+{ "HELVETICA,ARIAL,serif",
+ "$562" },
+{ "HELVETICA,ARIAL,serif",
+ "Gur" },
+{ "HELVETICA,ARIAL,serif",
+ "Arjyl" },
+{ "HELVETICA,ARIAL,serif",
+ "erabingrq" },
+{ "HELVETICA,ARIAL,serif",
+ "Arj" },
+{ "HELVETICA,ARIAL,serif",
+ "Lbex" },
+{ "HELVETICA,ARIAL,serif",
+ "Enznqn" },
+{ "HELVETICA,ARIAL,serif",
+ "$135!" },
+{ "HELVETICA,ARIAL,serif",
+ "Pnevoorna" },
+{ "HELVETICA,ARIAL,serif",
+ "Pehvfrf" },
+{ "HELVETICA,ARIAL,serif",
+ "$249!" },
+{ "HELVETICA,ARIAL,serif",
+ "Jnez-jrngure" },
+{ "HELVETICA,ARIAL,serif",
+ "trgnjnlf" },
+{ "HELVETICA,ARIAL,serif",
+ "ner" },
+{ "HELVETICA,ARIAL,serif",
+ "ba" },
+{ "HELVETICA,ARIAL,serif",
+ "fnyr" },
+{ "HELVETICA,ARIAL,serif",
+ "abj" },
+{ "HELVETICA,ARIAL,serif",
+ "jvgu" },
+{ "HELVETICA,ARIAL,serif",
+ "Abejrtvna" },
+{ "HELVETICA,ARIAL,serif",
+ "Pehvfr" },
+{ "HELVETICA,ARIAL,serif",
+ "Yvar!" },
+{ "HELVETICA,ARIAL,serif",
+ "5gu" },
+{ "HELVETICA,ARIAL,serif",
+ "Avtug" },
+{ "HELVETICA,ARIAL,serif",
+ "va" },
+{ "HELVETICA,ARIAL,serif",
+ "Unjnvv!" },
+{ "HELVETICA,ARIAL,serif",
+ "Erynk" },
+{ "HELVETICA,ARIAL,serif",
+ "ornpu" },
+{ "HELVETICA,ARIAL,serif",
+ "va" },
+{ "HELVETICA,ARIAL,serif",
+ "Znhv" },
+{ "HELVETICA,ARIAL,serif",
+ "be" },
+{ "HELVETICA,ARIAL,serif",
+ "pyvzo" },
+{ "HELVETICA,ARIAL,serif",
+ "Qvnzbaq" },
+{ "HELVETICA,ARIAL,serif",
+ "Urnq" },
+{ "HELVETICA,ARIAL,serif",
+ "Bnuh" },
+{ "HELVETICA,ARIAL,serif",
+ "NN" },
+{ "HELVETICA,ARIAL,serif",
+ "Inpngvbaf!" },
+{ "HELVETICA,ARIAL,serif",
+ "Inpngvba" },
+{ "HELVETICA,ARIAL,serif",
+ "Svaqre" },
+{ "Lucida Grande",
+ "Qrfgvangvba" },
+{ "Lucida Grande",
+ "---------------------------" },
+{ "Lucida Grande",
+ "Ynf" },
+{ "Lucida Grande",
+ "Irtnf" },
+{ "Lucida Grande",
+ "Beynaqb" },
+{ "Lucida Grande",
+ "Pnapha" },
+{ "Lucida Grande",
+ "Unjnvv" },
+{ "Lucida Grande",
+ "Onunznf" },
+{ "Lucida Grande",
+ "------------" },
+{ "Lucida Grande",
+ "Nsevpn" },
+{ "Lucida Grande",
+ "Nfvn" },
+{ "Lucida Grande",
+ "Pnevoorna" },
+{ "Lucida Grande",
+ "Zrkvpb" },
+{ "Lucida Grande",
+ "Jrfgrea" },
+{ "Lucida Grande",
+ "H.F." },
+{ "Lucida Grande",
+ "Rnfgrea" },
+{ "Lucida Grande",
+ "Fbhgu" },
+{ "Lucida Grande",
+ "Cnpvsvp" },
+{ "Lucida Grande",
+ "Rhebcr" },
+{ "Lucida Grande",
+ "Yngva" },
+{ "Lucida Grande",
+ "Nagnepgvpn" },
+{ "Lucida Grande",
+ "Zbagu" },
+{ "Lucida Grande",
+ "Qrprzore-2000" },
+{ "Lucida Grande",
+ "Wnahnel-2001" },
+{ "Lucida Grande",
+ "Sroehnel-2001" },
+{ "Lucida Grande",
+ "Znepu-2001" },
+{ "Lucida Grande",
+ "Ncevy-2001" },
+{ "Lucida Grande",
+ "Znl-2001" },
+{ "Lucida Grande",
+ "Whar-2001" },
+{ "Lucida Grande",
+ "Whyl-2001" },
+{ "Lucida Grande",
+ "Nhthfg-2001" },
+{ "Lucida Grande",
+ "Frcgrzore-2001" },
+{ "Lucida Grande",
+ "Bpgbore-2001" },
+{ "Lucida Grande",
+ "Abirzore-2001" },
+{ "Lucida Grande",
+ "Cevpr" },
+{ "Lucida Grande",
+ "(cre" },
+{ "Lucida Grande",
+ "crefba)" },
+{ "Lucida Grande",
+ "$100" },
+{ "Lucida Grande",
+ "$250" },
+{ "Lucida Grande",
+ "$500" },
+{ "Lucida Grande",
+ "$1000" },
+{ "Lucida Grande",
+ "$1500" },
+{ "Lucida Grande",
+ "$2000" },
+{ "Lucida Grande",
+ "+" },
+{ "HELVETICA,ARIAL,serif",
+ "zber" },
+{ "HELVETICA,ARIAL,serif",
+ "qrgnvyf" },
+{ "HELVETICA,ARIAL,serif",
+ "urer..." },
+{ "HELVETICA,ARIAL,serif",
+ "Inpngvbaf" },
+{ "HELVETICA,ARIAL,serif",
+ "Pehvfrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Zbhagnva" },
+{ "HELVETICA,ARIAL,serif",
+ "Erfbeg" },
+{ "HELVETICA,ARIAL,serif",
+ "Thvqr" },
+{ "HELVETICA,ARIAL,serif",
+ "Abg" },
+{ "HELVETICA,ARIAL,serif",
+ "fher" },
+{ "HELVETICA,ARIAL,serif",
+ "jurer" },
+{ "HELVETICA,ARIAL,serif",
+ "tb" },
+{ "HELVETICA,ARIAL,serif",
+ "sbe" },
+{ "HELVETICA,ARIAL,serif",
+ "lbhe" },
+{ "HELVETICA,ARIAL,serif",
+ "fxvvat" },
+{ "HELVETICA,ARIAL,serif",
+ "fabjobneqvat" },
+{ "HELVETICA,ARIAL,serif",
+ "inpngvba?" },
+{ "HELVETICA,ARIAL,serif",
+ "Bhe" },
+{ "HELVETICA,ARIAL,serif",
+ "thvqr" },
+{ "HELVETICA,ARIAL,serif",
+ "unf" },
+{ "HELVETICA,ARIAL,serif",
+ "vasb" },
+{ "HELVETICA,ARIAL,serif",
+ "bire" },
+{ "HELVETICA,ARIAL,serif",
+ "500" },
+{ "HELVETICA,ARIAL,serif",
+ "Abegu" },
+{ "HELVETICA,ARIAL,serif",
+ "Nzrevpna" },
+{ "HELVETICA,ARIAL,serif",
+ "erfbegf." },
+{ "HELVETICA,ARIAL,serif",
+ "\xe2""\x80""\xa2""" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl'f" },
+{ "HELVETICA,ARIAL,serif",
+ "Orra" },
+{ "HELVETICA,ARIAL,serif",
+ "Gurer" },
+{ "HELVETICA,ARIAL,serif",
+ "Fxvvat" },
+{ "HELVETICA,ARIAL,serif",
+ "&" },
+{ "HELVETICA,ARIAL,serif",
+ "Fabjobneqvat" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrfgvangvba" },
+{ "HELVETICA,ARIAL,serif",
+ "Thvqrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Vagreangvbany" },
+{ "HELVETICA,ARIAL,serif",
+ "fvgrf:" },
+{ "HELVETICA,ARIAL,serif",
+ "HX" },
+{ "HELVETICA,ARIAL,serif",
+ " | " },
+{ "HELVETICA,ARIAL,serif",
+ "Pnanqn" },
+{ "HELVETICA,ARIAL,serif",
+ "Treznal" },
+{ "HELVETICA,,serif",
+ "Fvgr" },
+{ "HELVETICA,,serif",
+ "Thvqr" },
+{ "HELVETICA,,serif",
+ " | " },
+{ "HELVETICA,,serif",
+ "Phfgbzre" },
+{ "HELVETICA,,serif",
+ "Freivpr" },
+{ "HELVETICA,,serif",
+ "Nobhg" },
+{ "HELVETICA,,serif",
+ "Genirybpvgl.pbz" },
+{ "HELVETICA,,serif",
+ "Cevinpl" },
+{ "HELVETICA,,serif",
+ "Cbyvpl" },
+{ "HELVETICA,,serif",
+ "Frphevgl" },
+{ "HELVETICA,,serif",
+ "Thnenagrr" },
+{ "HELVETICA,,serif",
+ "Grezf" },
+{ "HELVETICA,,serif",
+ "bs" },
+{ "HELVETICA,,serif",
+ "Hfr" },
+{ "HELVETICA,,serif",
+ "Nqiregvfr" },
+{ "HELVETICA,,serif",
+ "Nssvyvngrf" },
+{ "HELVETICA,,serif",
+ "Genirybpvgl" },
+{ "HELVETICA,,serif",
+ "Zntnmvar" },
+{ "HELVETICA,,serif",
+ "Wbof" },
+{ "HELVETICA,,serif",
+ "Njneqf" },
+{ "HELVETICA,,serif",
+ "Pbagrfg" },
+{ "HELVETICA,,serif",
+ "Jvaaref" },
+{ "HELVETICA,,serif",
+ "Cerff" },
+{ "HELVETICA,,serif",
+ "Ebbz" },
+{ "HELVETICA,,serif",
+ "Vairfgbe" },
+{ "HELVETICA,,serif",
+ "Eryngvbaf" },
+{ "HELVETICA,,serif",
+ "Ubj" },
+{ "HELVETICA,,serif",
+ "gb" },
+{ "HELVETICA,,serif",
+ "erqrrz" },
+{ "HELVETICA,,serif",
+ "lbhe" },
+{ "HELVETICA,,serif",
+ "$99" },
+{ "HELVETICA,,serif",
+ "pbzcnavba" },
+{ "HELVETICA,,serif",
+ "sner" },
+{ "HELVETICA,,serif",
+ "ba" },
+{ "HELVETICA,,serif",
+ "Nzrevpna" },
+{ "HELVETICA,,serif",
+ "Nveyvarf." },
+{ "ARIAL,,serif",
+ "\xc2""\xa9""" },
+{ "ARIAL,,serif",
+ "2000" },
+{ "ARIAL,,serif",
+ "Genirybpvgl.pbz" },
+{ "ARIAL,,serif",
+ "Y.C." },
+{ "ARIAL,,serif",
+ "Nyy" },
+{ "ARIAL,,serif",
+ "evtugf" },
+{ "ARIAL,,serif",
+ "erfreirq." },
+{ "ARIAL,,serif",
+ "Genirybpvgl\xc2""\xae""," },
+{ "ARIAL,,serif",
+ "naq" },
+{ "ARIAL,,serif",
+ "gur" },
+{ "ARIAL,,serif",
+ "Genirybpvgl" },
+{ "ARIAL,,serif",
+ "fxlyvar" },
+{ "ARIAL,,serif",
+ "ybtb" },
+{ "ARIAL,,serif",
+ "ner" },
+{ "ARIAL,,serif",
+ "genqrznexf" },
+{ "ARIAL,,serif",
+ "naq/be" },
+{ "ARIAL,,serif",
+ "freivpr" },
+{ "ARIAL,,serif",
+ "znexf" },
+{ "ARIAL,,serif",
+ "bs" },
+{ "ARIAL,,serif",
+ "PFG#" },
+{ "ARIAL,,serif",
+ "2050374-40" },
+{ "ARIAL,,serif",
+ "Genirybpvgl" },
+{ "ARIAL,,serif",
+ "fxlyvar" },
+{ "ARIAL,,serif",
+ "ybtb" },
+{ "ARIAL,,serif",
+ "ner" },
+{ "ARIAL,,serif",
+ "genqrznexf" },
+{ "ARIAL,,serif",
+ "naq/be" },
+{ "ARIAL,,serif",
+ "freivpr" },
+{ "ARIAL,,serif",
+ "znexf" },
+{ "ARIAL,,serif",
+ "bs" },
+{ "ARIAL,,serif",
+ "Genirybpvgl.pbz" },
+{ "ARIAL,,serif",
+ "Y.C." },
+{ "serif",
+ "Nveyvar" },
+{ "serif",
+ "gvpxrgf," },
+{ "serif",
+ "ubgry" },
+{ "serif",
+ "nppbzzbqngvbaf" },
+{ "serif",
+ "Nveyvar gvpxrgf, ubgry " },
+{ "serif",
+ "eragny" },
+{ "serif",
+ "nppbzzbqngvbaf naq " },
+{ "serif",
+ "pne" },
+{ "serif",
+ "erfreingvbaf" },
+{ "serif",
+ "bayvar" },
+{ "serif",
+ "eragny pne erfreingvbaf " },
+{ "HELVETICA,ARIAL,serif",
+ "Jrypbzr gb Genirybpvgl.pbz!" },
+{ "HELVETICA,ARIAL,serif",
+ "Nyernql n zrzore? " },
+{ "HELVETICA,ARIAL,serif",
+ "Pyvpx urer gb ybt va." },
+{ "HELVETICA,ARIAL,serif",
+ "Zl Gevcf" },
+{ "HELVETICA,ARIAL,serif",
+ " | " },
+{ "HELVETICA,ARIAL,serif",
+ "Zl Nppbhag" },
+{ "Lucida Grande",
+ "Fvgr Fubegphgf" },
+{ "HELVETICA,ARIAL,serif",
+ " " },
+{ "HELVETICA,ARIAL,serif",
+ "Frnepu Genirybpvgl.pbz" },
+{ "HELVETICA,ARIAL,serif",
+ "Ragre lbhe qrfgvangvba gb svaq " },
+{ "HELVETICA,ARIAL,serif",
+ "qrnyf, snerf, thvqrf & zber:" },
+{ "HELVETICA,ARIAL,serif",
+ "Phfgbzre Freivpr" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry qrynlf npebff gur H.F.... " },
+{ "HELVETICA,ARIAL,serif",
+ "Freivpr Nyreg " },
+{ "HELVETICA,ARIAL,serif",
+ "Frphevgl Thnenagrr" },
+{ "HELVETICA,ARIAL,serif",
+ "Cevinpl Cbyvpl" },
+{ "HELVETICA,ARIAL,serif",
+ "Genirybpvgl orfg sner svaqre" },
+{ "HELVETICA,ARIAL,serif",
+ "Fubj zr gur orfg ebhaqgevc snerf sbe:" },
+{ "HELVETICA,ARIAL,serif",
+ "nal qngr " },
+{ "HELVETICA,ARIAL,serif",
+ "fcrpvsvp qngrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Yrnivat sebz:" },
+{ "HELVETICA,ARIAL,serif",
+ "Tbvat gb:" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrcneg: " },
+{ "Lucida Grande",
+ " " },
+{ "HELVETICA,ARIAL,serif",
+ " " },
+{ "HELVETICA,ARIAL,serif",
+ "Erghea: " },
+{ "sans-serif",
+ "TB" },
+{ "HELVETICA,ARIAL,serif",
+ "Sner Jngpure" },
+{ "HELVETICA,ARIAL,serif",
+ "> " },
+{ "HELVETICA,ARIAL,serif",
+ "Obbx vg!" },
+{ "HELVETICA,ARIAL,serif",
+ "Ybf Natryrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Ynf Irtnf" },
+{ "HELVETICA,ARIAL,serif",
+ "Arj Lbex" },
+{ "HELVETICA,ARIAL,serif",
+ "Fna Senapvfpb" },
+{ "HELVETICA,ARIAL,serif",
+ "Pyvpx urer gb crefbanyvmr Sner Jngpure" },
+{ "serif",
+ "Oevgvfu" },
+{ "serif",
+ "Oevgvfu " },
+{ "serif",
+ "Znfgre" },
+{ "serif",
+ "Znfgre " },
+{ "HELVETICA,ARIAL,serif",
+ "Jr cersre " },
+{ "HELVETICA,ARIAL,serif",
+ "Ohfvarff Geniry Pragre" },
+{ "HELVETICA,ARIAL,serif",
+ "Vafgnag npprff gb gbc " },
+{ "HELVETICA,ARIAL,serif",
+ "ohfvarff geniry gbbyf." },
+{ "HELVETICA,ARIAL,serif",
+ "Ercrng gevcf, znantr geniry," },
+{ "HELVETICA,ARIAL,serif",
+ "Geniryref Argjbex" },
+{ "HELVETICA,ARIAL,serif",
+ "Zrffntr Obneqf" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniryre Erivrjf" },
+{ "HELVETICA,ARIAL,serif",
+ "Gvcf & Nqivpr " },
+{ "HELVETICA,ARIAL,serif",
+ "Fvgr Uvtuyvtugf" },
+{ "HELVETICA,ARIAL,serif",
+ "SERR Genirybpvgl " },
+{ "HELVETICA,ARIAL,serif",
+ "Grnz hc gb Geniry" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry Cebsvyrf" },
+{ "HELVETICA,ARIAL,serif",
+ "Frng Zncf" },
+{ "HELVETICA,ARIAL,serif",
+ "Qernz Zncf" },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry Cebgrpgvba" },
+{ "HELVETICA,ARIAL,serif",
+ "Purpx Tngrf & Gvzrf sbe Syvtugf" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrgnvyrq frnepu" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrprzore 14 2000 ...Yvatrevat jrngure rssrpgf qrynl geniry npebff gur H.F. " },
+{ "Lucida Grande",
+ "Fryrpg nveyvar" },
+{ "HELVETICA,ARIAL,serif",
+ "Syvtug Ahzore:" },
+{ "HELVETICA,ARIAL,serif",
+ "Qrnyf & Arjf" },
+{ "serif",
+ "Urer" },
+{ "serif",
+ "Pyvpx Urer" },
+{ "HELVETICA,ARIAL,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "HELVETICA,ARIAL,serif",
+ "Yrtraq Nveyvarf grzcbenevyl fhfcraqf bcrengvbaf. Vasb sbe bhe phfgbzref." },
+{ "HELVETICA,ARIAL,serif",
+ "Geniry ba Angvbany Nveyvarf naq Fnir 15%." },
+{ "HELVETICA,ARIAL,serif",
+ "Syl gb Nfvn & FNIR ba Puvan Nveyvarf-- snerf sebz $562" },
+{ "HELVETICA,ARIAL,serif",
+ "Gur Arjyl erabingrq Arj Lbex Enznqn sebz $135!" },
+{ "HELVETICA,ARIAL,serif",
+ "Inpngvbaf & Pehvfrf" },
+{ "serif",
+ "Pehvfr" },
+{ "serif",
+ "Fnivatf" },
+{ "serif",
+ "Pehvfr " },
+{ "HELVETICA,ARIAL,serif",
+ "Pnevoorna Pehvfrf " },
+{ "HELVETICA,ARIAL,serif",
+ "sebz $249!" },
+{ "HELVETICA,ARIAL,serif",
+ "Jnez-jrngure trgnjnlf " },
+{ "HELVETICA,ARIAL,serif",
+ "ner ba fnyr abj jvgu " },
+{ "HELVETICA,ARIAL,serif",
+ "Abejrtvna Pehvfr Yvar!" },
+{ "HELVETICA,ARIAL,serif",
+ "Inpngvba Svaqre" },
+{ "Lucida Grande",
+ "Cevpr (cre crefba)" },
+{ "sans-serif",
+ "Svaq" },
+{ "sans-serif",
+ "Svaq " },
+{ "serif",
+ "Erynk" },
+{ "serif",
+ "Erynk " },
+{ "serif",
+ "Unjnvv!" },
+{ "serif",
+ "va " },
+{ "HELVETICA,ARIAL,serif",
+ "5gu Avtug SERR va " },
+{ "serif",
+ "34," },
+{ "serif",
+ "jjj.genirybpvgl.pbz," },
+{ "serif",
+ "430" },
+{ "serif",
+ "1, 34, jjj.genirybpvgl.pbz, 430" },
+{ "HELVETICA,ARIAL,serif",
+ "Ubgryf ba n znc" },
+{ "HELVETICA,ARIAL,serif",
+ "Ragre zber qrgnvyf urer..." },
+{ "HELVETICA,ARIAL,serif",
+ "Erynk ba gur ornpu va Znhv " },
+{ "Verdana,serif",
+ "Phfgbzvmr" },
+{ "Verdana,serif",
+ "IR!" },
+{ "sans-serif",
+ "Fhozvg" },
+{ "sans-serif",
+ "Dhrel" },
+{ "Verdana,serif",
+ " arjf" },
+{ "Verdana,serif",
+ " Yngrfg" },
+{ "Verdana,serif",
+ "Arjf" },
+{ "Verdana,serif",
+ " Arjf" },
+{ "Verdana,serif",
+ "Frnepu" },
+{ "Verdana,serif",
+ " Avtugyl" },
+{ "Verdana,serif",
+ "Arjfyrggre" },
+{ "Verdana,serif",
+ " qrcnegzragf" },
+{ "Verdana,serif",
+ " Tnzrf" },
+{ "Verdana,serif",
+ " " },
+{ "Verdana,serif",
+ "Ersrerapr" },
+{ "Verdana,serif",
+ "Qeviref" },
+{ "Verdana,serif",
+ "Vagreivrjf" },
+{ "Verdana,serif",
+ "Uneqjner" },
+{ "Verdana,serif",
+ " gnyx" },
+{ "Verdana,serif",
+ "onpx" },
+{ "Verdana,serif",
+ " Sbehzf" },
+{ "Verdana,serif",
+ " Trareny" },
+{ "Verdana,serif",
+ " Uneqjner" },
+{ "Verdana,serif",
+ " Fbsgjner" },
+{ "Verdana,serif",
+ " Fuval" },
+{ "Verdana,serif",
+ " Pung" },
+{ "Verdana,serif",
+ " fvgr" },
+{ "Verdana,serif",
+ "vasb" },
+{ "Verdana,serif",
+ "Nssvyvngrf" },
+{ "Verdana,serif",
+ "Yvaxf" },
+{ "Verdana,serif",
+ "Nobhg" },
+{ "Verdana,serif",
+ "Hf" },
+{ "Verdana,serif",
+ " nfx" },
+{ "Verdana,serif",
+ "qrif" },
+{ "Verdana,serif",
+ " Ynfg" },
+{ "Verdana,serif",
+ "Hcqngr:" },
+{ "Verdana,serif",
+ " Nfx" },
+{ "Verdana,serif",
+ "Ubbx" },
+{ "Verdana,serif",
+ " Qrprzore" },
+{ "Verdana,serif",
+ "7" },
+{ "Verdana,serif",
+ "Dhvpxvr" },
+{ "Verdana,serif",
+ "Arjf" },
+{ "Verdana,serif",
+ "Cntr" },
+{ "Verdana,serif",
+ "Ynhapu" },
+{ "Verdana,serif",
+ "yvaxf" },
+{ "Verdana,serif",
+ "va" },
+{ "Lucida Grande",
+ "Fnzr" },
+{ "Lucida Grande",
+ "Jvaqbj" },
+{ "Lucida Grande",
+ "Arj" },
+{ "Lucida Grande",
+ "jvaqbj" },
+{ "Lucida Grande",
+ "Arjf Frnepu" },
+{ "Verdana,serif",
+ "IR" },
+{ "Verdana,serif",
+ "-" },
+{ "Verdana,serif",
+ "Jr" },
+{ "Verdana,serif",
+ "zvtug" },
+{ "Verdana,serif",
+ "ynpx" },
+{ "Verdana,serif",
+ "gur" },
+{ "Verdana,serif",
+ "yratgu," },
+{ "Verdana,serif",
+ "ohg" },
+{ "Verdana,serif",
+ "jr" },
+{ "Verdana,serif",
+ "cnpx" },
+{ "Verdana,serif",
+ "rabhtu" },
+{ "Verdana,serif",
+ "tvegu" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "xvyy" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "euvab" },
+{ "Verdana,serif",
+ "-oj" },
+{ "Verdana,serif",
+ "JnePensg" },
+{ "Verdana,serif",
+ "VVV" },
+{ "Verdana,serif",
+ "cerivrj" },
+{ "Verdana,serif",
+ "Pnyy" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "Cbjre" },
+{ "Verdana,serif",
+ "VV" },
+{ "Verdana,serif",
+ "Cngpu" },
+{ "Verdana,serif",
+ "Pbzznaqre" },
+{ "Verdana,serif",
+ "Xrra" },
+{ "Verdana,serif",
+ "Uvfgbel" },
+{ "Verdana,serif",
+ "Ryvgr" },
+{ "Verdana,serif",
+ "Rqvgvba" },
+{ "Verdana,serif",
+ "Qrzb" },
+{ "Verdana,serif",
+ "Haqlvat" },
+{ "Verdana,serif",
+ "Vagreivrj" },
+{ "Verdana,serif",
+ "Fvyrag" },
+{ "Verdana,serif",
+ "Uhagre" },
+{ "Verdana,serif",
+ "Cerivrj" },
+{ "Verdana,serif",
+ "D3" },
+{ "Verdana,serif",
+ "1.27" },
+{ "Verdana,serif",
+ "Cbvag" },
+{ "Verdana,serif",
+ "Eryrnfr" },
+{ "Verdana,serif",
+ "QAS" },
+{ "Verdana,serif",
+ "Trgf" },
+{ "Verdana,serif",
+ "Qngr?" },
+{ "Verdana,serif",
+ "Vprjvaq" },
+{ "Verdana,serif",
+ "Qnyr" },
+{ "Verdana,serif",
+ "RC" },
+{ "Verdana,serif",
+ "ceri." },
+{ "Verdana,serif",
+ "JJVV" },
+{ "Verdana,serif",
+ "Bayvar" },
+{ "Verdana,serif",
+ "Zbivr" },
+{ "Verdana,serif",
+ "Zvfp." },
+{ "Verdana,serif",
+ "Fubgf" },
+{ "Verdana,serif",
+ "AJA'f" },
+{ "Verdana,serif",
+ "Hzore" },
+{ "Verdana,serif",
+ "Uhyx" },
+{ "Verdana,serif",
+ "Syl3Q" },
+{ "Verdana,serif",
+ "tnzr" },
+{ "Verdana,serif",
+ "ratvar" },
+{ "Verdana,serif",
+ "Pbyva" },
+{ "Verdana,serif",
+ "ZpEnr" },
+{ "Verdana,serif",
+ "Enyyl" },
+{ "Verdana,serif",
+ "2.0" },
+{ "Verdana,serif",
+ "qrzb" },
+{ "Verdana,serif",
+ "(12.14)" },
+{ "Verdana,serif",
+ "Xvatqbz" },
+{ "Verdana,serif",
+ "Haqre" },
+{ "Verdana,serif",
+ "Sver" },
+{ "Verdana,serif",
+ "(12.12)" },
+{ "Verdana,serif",
+ "Zrpu4" },
+{ "Verdana,serif",
+ "(12.08)" },
+{ "Verdana,serif",
+ "Arj" },
+{ "Verdana,serif",
+ "Gur" },
+{ "Verdana,serif",
+ "Snyyra" },
+{ "Verdana,serif",
+ "ABYS" },
+{ "Verdana,serif",
+ "cngpu" },
+{ "Verdana,serif",
+ "Sne" },
+{ "Verdana,serif",
+ "Tngr" },
+{ "Verdana,serif",
+ "(12.06)" },
+{ "Verdana,serif",
+ "Qrhf" },
+{ "Verdana,serif",
+ "Rk" },
+{ "Verdana,serif",
+ "ZC" },
+{ "Verdana,serif",
+ "Cngpu(12.05)" },
+{ "Verdana,serif",
+ "Zvq." },
+{ "Verdana,serif",
+ "Znqarff" },
+{ "Verdana,serif",
+ "2" },
+{ "Verdana,serif",
+ "(12.04)" },
+{ "Verdana,serif",
+ "Hcqngrq" },
+{ "Verdana,serif",
+ "Irabz" },
+{ "Verdana,serif",
+ "(12.03)" },
+{ "Verdana,serif",
+ "Nyvpr" },
+{ "Verdana,serif",
+ "(12.01)" },
+{ "Verdana,serif",
+ "Onyqhef" },
+{ "Verdana,serif",
+ "(11.29)" },
+{ "Verdana,serif",
+ "Onggyr" },
+{ "Verdana,serif",
+ "bs" },
+{ "Verdana,serif",
+ "Oevgvna" },
+{ "Verdana,serif",
+ "Grnz" },
+{ "Verdana,serif",
+ "Neran" },
+{ "Verdana,serif",
+ "(11.22)" },
+{ "Verdana,serif",
+ "Fnpevsvpr" },
+{ "Verdana,serif",
+ "Tvnagf" },
+{ "Verdana,serif",
+ "eri/pbqrf" },
+{ "Verdana,serif",
+ "(12.13)" },
+{ "Verdana,serif",
+ "Eboreg" },
+{ "Verdana,serif",
+ "Qhssl" },
+{ "Verdana,serif",
+ "D&N" },
+{ "Verdana,serif",
+ "Rfpncr" },
+{ "Verdana,serif",
+ "sebz" },
+{ "Verdana,serif",
+ "ZV" },
+{ "Verdana,serif",
+ "eri" },
+{ "Verdana,serif",
+ "(12.9)" },
+{ "Verdana,serif",
+ "Qrif" },
+{ "Verdana,serif",
+ "ba" },
+{ "Verdana,serif",
+ "QverpgK8" },
+{ "Verdana,serif",
+ "(12.8)" },
+{ "Verdana,serif",
+ "EN2" },
+{ "Verdana,serif",
+ "Erivrj" },
+{ "Verdana,serif",
+ "(12.6)" },
+{ "Verdana,serif",
+ "I5" },
+{ "Verdana,serif",
+ "6000" },
+{ "Verdana,serif",
+ "(12.5)" },
+{ "Verdana,serif",
+ "Snyybhg" },
+{ "Verdana,serif",
+ "Gnpgvpf" },
+{ "Verdana,serif",
+ "AUY" },
+{ "Verdana,serif",
+ "2001" },
+{ "Verdana,serif",
+ "Orvat" },
+{ "Verdana,serif",
+ "Oevna" },
+{ "Verdana,serif",
+ "(11.28)" },
+{ "Verdana,serif",
+ "Znq.2" },
+{ "Verdana,serif",
+ "eri." },
+{ "Verdana,serif",
+ "Oynpx" },
+{ "Verdana,serif",
+ "&" },
+{ "Verdana,serif",
+ "Juvgr" },
+{ "Verdana,serif",
+ "(11.21)" },
+{ "Verdana,serif",
+ "erivrj" },
+{ "Verdana,serif",
+ "(11.20)" },
+{ "Verdana,serif",
+ "Unyy" },
+{ "Verdana,serif",
+ "(11.10)" },
+{ "Verdana,serif",
+ "AJA" },
+{ "Verdana,serif",
+ "Gbc" },
+{ "Verdana,serif",
+ "10" },
+{ "Verdana,serif",
+ "(11.8)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/14/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/13/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/12/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/11/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/10/00" },
+{ "Verdana,serif",
+ "Qvfcynlvat" },
+{ "Verdana,serif",
+ "haphfgbzvmrq" },
+{ "Verdana,serif",
+ "arjf." },
+{ "Verdana,serif",
+ "Pyvpx" },
+{ "Verdana,serif",
+ "urer" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "phfgbzvmr." },
+{ "Verdana,serif",
+ "Guhefqnl," },
+{ "Verdana,serif",
+ "Qrprzore" },
+{ "Verdana,serif",
+ "14," },
+{ "Verdana,serif",
+ "2000" },
+{ "Verdana,serif",
+ "-" },
+{ "Verdana,serif",
+ "Unir" },
+{ "Verdana,serif",
+ "fbzr" },
+{ "Verdana,serif",
+ "arjf" },
+{ "Verdana,serif",
+ "be" },
+{ "Verdana,serif",
+ "qbrf" },
+{ "Verdana,serif",
+ "lbhe" },
+{ "Verdana,serif",
+ "jbzna" },
+{ "Verdana,serif",
+ "nfx" },
+{ "Verdana,serif",
+ "sbe" },
+{ "Verdana,serif",
+ "Fjrrg" },
+{ "Verdana,serif",
+ "Qvpx" },
+{ "Verdana,serif",
+ "Jvyyl" },
+{ "Verdana,serif",
+ "ol" },
+{ "Verdana,serif",
+ "anzr?" },
+{ "Verdana,serif",
+ "Gryy" },
+{ "Verdana,serif",
+ "hf!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "JnePensg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "VVV" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cerivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:14" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "CZ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(lbhe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gvzr)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "-" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Eboreg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"Ncnpur\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubjnegu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzrf:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ebyr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cynlvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgengrtl" },
+{ "Verdana,serif",
+ "Gur" },
+{ "Verdana,serif",
+ "jbzra" },
+{ "Verdana,serif",
+ "ng" },
+{ "Verdana,serif",
+ "Qnvyl" },
+{ "Verdana,serif",
+ "Enqne" },
+{ "Verdana,serif",
+ "cbfgrq" },
+{ "Verdana,serif",
+ "nabgure" },
+{ "Verdana,serif",
+ "JnePensg" },
+{ "Verdana,serif",
+ "VVV" },
+{ "Verdana,serif",
+ "cerivrj" },
+{ "Verdana,serif",
+ "," },
+{ "Verdana,serif",
+ "gnxvat" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "tynapr" },
+{ "Verdana,serif",
+ "Oyvmmneq'f" },
+{ "Verdana,serif",
+ "hcpbzvat" },
+{ "Verdana,serif",
+ "3-Q" },
+{ "Verdana,serif",
+ "erny" },
+{ "Verdana,serif",
+ "gvzr" },
+{ "Verdana,serif",
+ "fgengrtl" },
+{ "Verdana,serif",
+ "pnfu-pbj." },
+{ "Verdana,serif",
+ "Vapyhqrq," },
+{ "Verdana,serif",
+ "ner" },
+{ "Verdana,serif",
+ "srj" },
+{ "Verdana,serif",
+ "arj" },
+{ "Verdana,serif",
+ "fperraf." },
+{ "Verdana,serif",
+ "Urer'f" },
+{ "Verdana,serif",
+ "gnxr:" },
+{ "Verdana,serif",
+ "Jnepensg" },
+{ "Verdana,serif",
+ "vf" },
+{ "Verdana,serif",
+ "svefg" },
+{ "Verdana,serif",
+ "nggrzcg" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "perngr" },
+{ "Verdana,serif",
+ "3Q" },
+{ "Verdana,serif",
+ "ernygvzr" },
+{ "Verdana,serif",
+ "tnzr," },
+{ "Verdana,serif",
+ "juvpu" },
+{ "Verdana,serif",
+ "cynprf" },
+{ "Verdana,serif",
+ "rzcunfvf" },
+{ "Verdana,serif",
+ "ba" },
+{ "Verdana,serif",
+ "qrirybcvat" },
+{ "Verdana,serif",
+ "urebrf" },
+{ "Verdana,serif",
+ "naq" },
+{ "Verdana,serif",
+ "vaibyivat" },
+{ "Verdana,serif",
+ "gurz" },
+{ "Verdana,serif",
+ "va" },
+{ "Verdana,serif",
+ "pbzong," },
+{ "Verdana,serif",
+ "qbjacynlf" },
+{ "Verdana,serif",
+ "erfbhepr" },
+{ "Verdana,serif",
+ "znantrzrag" },
+{ "Verdana,serif",
+ "ehfu" },
+{ "Verdana,serif",
+ "gnpgvpf." },
+{ "Verdana,serif",
+ "Boivbhfyl" },
+{ "Verdana,serif",
+ "gur" },
+{ "Verdana,serif",
+ "zbir" },
+{ "Verdana,serif",
+ "cerfragf" },
+{ "Verdana,serif",
+ "rirel" },
+{ "Verdana,serif",
+ "qrirybcre" },
+{ "Verdana,serif",
+ "jvgu" },
+{ "Verdana,serif",
+ "fnzr" },
+{ "Verdana,serif",
+ "punyyratr:" },
+{ "Verdana,serif",
+ "jung" },
+{ "Verdana,serif",
+ "qb" },
+{ "Verdana,serif",
+ "pnzren." },
+{ "Verdana,serif",
+ "Erpragyl," },
+{ "Verdana,serif",
+ "tnzrf" },
+{ "Verdana,serif",
+ "yvxr" },
+{ "Verdana,serif",
+ "Tebhaq" },
+{ "Verdana,serif",
+ "Pbageby" },
+{ "Verdana,serif",
+ "unir" },
+{ "Verdana,serif",
+ "tvira" },
+{ "Verdana,serif",
+ "cynlref" },
+{ "Verdana,serif",
+ "na" },
+{ "Verdana,serif",
+ "rabezbhf" },
+{ "Verdana,serif",
+ "nzbhag" },
+{ "Verdana,serif",
+ "bs" },
+{ "Verdana,serif",
+ "serrqbz," },
+{ "Verdana,serif",
+ "nyybjvat" },
+{ "Verdana,serif",
+ "mbbz" },
+{ "Verdana,serif",
+ "sbe" },
+{ "Verdana,serif",
+ "dhvpx" },
+{ "Verdana,serif",
+ "ivrj" },
+{ "Verdana,serif",
+ "be" },
+{ "Verdana,serif",
+ "ubire" },
+{ "Verdana,serif",
+ "jryy" },
+{ "Verdana,serif",
+ "nobir" },
+{ "Verdana,serif",
+ "svryq." },
+{ "Verdana,serif",
+ "Fuval'f" },
+{ "Verdana,serif",
+ "pnzren" },
+{ "Verdana,serif",
+ "Fnpevsvpr" },
+{ "Verdana,serif",
+ "jnf" },
+{ "Verdana,serif",
+ "svkrq" },
+{ "Verdana,serif",
+ "znva" },
+{ "Verdana,serif",
+ "punenpgre," },
+{ "Verdana,serif",
+ "nyybjrq" },
+{ "Verdana,serif",
+ "ebgngvba" },
+{ "Verdana,serif",
+ "fznyy" },
+{ "Verdana,serif",
+ "mbbz." },
+{ "Verdana,serif",
+ "Ubjrire," },
+{ "Verdana,serif",
+ "Oyvmmneq" },
+{ "Verdana,serif",
+ "unf" },
+{ "Verdana,serif",
+ "qrpvqrq" },
+{ "Verdana,serif",
+ "svk" },
+{ "Verdana,serif",
+ "cynpr," },
+{ "Verdana,serif",
+ "cbffvovyvgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pnyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cbjre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "VV" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cngpu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:39" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Puevf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"Bhgynj\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "B'Oevra" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Erny-Gvzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(3)" },
+{ "Verdana,serif",
+ "SvyrCynarg" },
+{ "Verdana,serif",
+ "cngpu" },
+{ "Verdana,serif",
+ "(3.5" },
+{ "Verdana,serif",
+ "ZO)" },
+{ "Verdana,serif",
+ "Npgvivfvba'f" },
+{ "Verdana,serif",
+ "erpragyl" },
+{ "Verdana,serif",
+ "eryrnfrq" },
+{ "Verdana,serif",
+ "erny-gvzr" },
+{ "Verdana,serif",
+ "Pnyy" },
+{ "Verdana,serif",
+ "Cbjre" },
+{ "Verdana,serif",
+ "VV." },
+{ "Verdana,serif",
+ "cngpu," },
+{ "Verdana,serif",
+ "vagraqrq" },
+{ "Verdana,serif",
+ "hf" },
+{ "Verdana,serif",
+ "haqre" },
+{ "Verdana,serif",
+ "nyy" },
+{ "Verdana,serif",
+ "ynathntrf," },
+{ "Verdana,serif",
+ "svkrf" },
+{ "Verdana,serif",
+ "freireny" },
+{ "Verdana,serif",
+ "tnzr-eryngrq" },
+{ "Verdana,serif",
+ "vffhrf," },
+{ "Verdana,serif",
+ "znvayl" },
+{ "Verdana,serif",
+ "zhygvcynlre" },
+{ "Verdana,serif",
+ "qrcnegzrag." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbzznaqre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Xrra" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uvfgbel" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:29" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Npgvba" },
+{ "Verdana,serif",
+ "Ernyzf'" },
+{ "Verdana,serif",
+ "pbzcnal" },
+{ "Verdana,serif",
+ "cntr" },
+{ "Verdana,serif",
+ "orra" },
+{ "Verdana,serif",
+ "hcqngrq" },
+{ "Verdana,serif",
+ "negvpyr" },
+{ "Verdana,serif",
+ "qrnyf" },
+{ "Verdana,serif",
+ "uvfgbel" },
+{ "Verdana,serif",
+ "oruvaq" },
+{ "Verdana,serif",
+ "Pbzznaqre" },
+{ "Verdana,serif",
+ "Xrra," },
+{ "Verdana,serif",
+ "tnzr" },
+{ "Verdana,serif",
+ "Ncbtrr" },
+{ "Verdana,serif",
+ "(juvpu" },
+{ "Verdana,serif",
+ "Ernyzf" },
+{ "Verdana,serif",
+ "nf" },
+{ "Verdana,serif",
+ "jr" },
+{ "Verdana,serif",
+ "xabj" },
+{ "Verdana,serif",
+ "vg" },
+{ "Verdana,serif",
+ "abj)" },
+{ "Verdana,serif",
+ "fbzr" },
+{ "Verdana,serif",
+ "gra" },
+{ "Verdana,serif",
+ "lrnef" },
+{ "Verdana,serif",
+ "ntb." },
+{ "Verdana,serif",
+ "pyvc:" },
+{ "Verdana,serif",
+ "1991" },
+{ "Verdana,serif",
+ "penml" },
+{ "Verdana,serif",
+ "lrne" },
+{ "Verdana,serif",
+ "vq" },
+{ "Verdana,serif",
+ "Xrra. " },
+{ "Verdana,serif",
+ "Va" },
+{ "Verdana,serif",
+ "Wnahnel" },
+{ "Verdana,serif",
+ "1991," },
+{ "Verdana,serif",
+ "gurl" },
+{ "Verdana,serif",
+ "svavfurq" },
+{ "Verdana,serif",
+ "svany" },
+{ "Verdana,serif",
+ "hcqngr" },
+{ "Verdana,serif",
+ "Xrra" },
+{ "Verdana,serif",
+ "Ibegvpbaf," },
+{ "Verdana,serif",
+ "gura" },
+{ "Verdana,serif",
+ "jbexrq" },
+{ "Verdana,serif",
+ "nsberzragvbarq" },
+{ "Verdana,serif",
+ "Fbsgqvfx" },
+{ "Verdana,serif",
+ "gvgyrf. " },
+{ "Verdana,serif",
+ "thlf" },
+{ "Verdana,serif",
+ "zbirq" },
+{ "Verdana,serif",
+ "sebz" },
+{ "Verdana,serif",
+ "Fuerircbeg" },
+{ "Verdana,serif",
+ "Znqvfba," },
+{ "Verdana,serif",
+ "Jvfpbafva" },
+{ "Verdana,serif",
+ "Frcgrzore" },
+{ "Verdana,serif",
+ "tbg" },
+{ "Verdana,serif",
+ "fubpxrq" },
+{ "Verdana,serif",
+ "ubj" },
+{ "Verdana,serif",
+ "pbyq" },
+{ "Verdana,serif",
+ "(rira" },
+{ "Verdana,serif",
+ "gubhtu" },
+{ "Verdana,serif",
+ "Gbz" },
+{ "Verdana,serif",
+ "gurer)," },
+{ "Verdana,serif",
+ "lrg" },
+{ "Verdana,serif",
+ "fgvyy" },
+{ "Verdana,serif",
+ "znantrq" },
+{ "Verdana,serif",
+ "jbex" },
+{ "Verdana,serif",
+ "hc" },
+{ "Verdana,serif",
+ "gurer" },
+{ "Verdana,serif",
+ "fvk" },
+{ "Verdana,serif",
+ "zbaguf" },
+{ "Verdana,serif",
+ "(gurl" },
+{ "Verdana,serif",
+ "onpx" },
+{ "Verdana,serif",
+ "Qnyynf" },
+{ "Verdana,serif",
+ "Ncevy" },
+{ "Verdana,serif",
+ "1," },
+{ "Verdana,serif",
+ "92). " },
+{ "Verdana,serif",
+ "Naljnl," },
+{ "Verdana,serif",
+ "qhevat" },
+{ "Verdana,serif",
+ "guvf" },
+{ "Verdana,serif",
+ "Znqvfba" },
+{ "Verdana,serif",
+ "gung" },
+{ "Verdana,serif",
+ "ortna" },
+{ "Verdana,serif",
+ "zber" },
+{ "Verdana,serif",
+ "tnzrf..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ryvgr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rqvgvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qrzb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:26" },
+{ "Verdana,serif",
+ "Fvreen" },
+{ "Verdana,serif",
+ "Fghqvbf" },
+{ "Verdana,serif",
+ "cynlnoyr" },
+{ "Verdana,serif",
+ "qrzb" },
+{ "Verdana,serif",
+ "gurve" },
+{ "Verdana,serif",
+ "gnpgvpny" },
+{ "Verdana,serif",
+ "npgvba" },
+{ "Verdana,serif",
+ "FJNG" },
+{ "Verdana,serif",
+ "3:" },
+{ "Verdana,serif",
+ "Ryvgr" },
+{ "Verdana,serif",
+ "Rqvgvba." },
+{ "Verdana,serif",
+ "V" },
+{ "Verdana,serif",
+ "uvtuyl" },
+{ "Verdana,serif",
+ "erpbzzraq" },
+{ "Verdana,serif",
+ "qbjaybnqvat" },
+{ "Verdana,serif",
+ "vs" },
+{ "Verdana,serif",
+ "lbh" },
+{ "Verdana,serif",
+ "qba'g" },
+{ "Verdana,serif",
+ "nyernql" },
+{ "Verdana,serif",
+ "bja" },
+{ "Verdana,serif",
+ "tnzr." },
+{ "Verdana,serif",
+ "V'ir" },
+{ "Verdana,serif",
+ "oybja" },
+{ "Verdana,serif",
+ "znal" },
+{ "Verdana,serif",
+ "ubhef" },
+{ "Verdana,serif",
+ "dhvgr" },
+{ "Verdana,serif",
+ "ovg!" },
+{ "Verdana,serif",
+ "Hcqngr-" },
+{ "Verdana,serif",
+ "frag" },
+{ "Verdana,serif",
+ "jbeq" },
+{ "Verdana,serif",
+ "nyfb" },
+{ "Verdana,serif",
+ "pbby" },
+{ "Verdana,serif",
+ "Rqvgvba" },
+{ "Verdana,serif",
+ "zbivr" },
+{ "Verdana,serif",
+ "yriry." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Haqlvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vagreivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2:45" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ubeebe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(4)" },
+{ "Verdana,serif",
+ "TnzrPragre" },
+{ "Verdana,serif",
+ "chg" },
+{ "Verdana,serif",
+ "gbtrgure" },
+{ "Verdana,serif",
+ "vagreivrj" },
+{ "Verdana,serif",
+ "(vg'f" },
+{ "Verdana,serif",
+ "sbez" },
+{ "Verdana,serif",
+ "negvpyr," },
+{ "Verdana,serif",
+ "gubhtu)" },
+{ "Verdana,serif",
+ "uryq" },
+{ "Verdana,serif",
+ "Pyvir" },
+{ "Verdana,serif",
+ "Onexre," },
+{ "Verdana,serif",
+ "zna" },
+{ "Verdana,serif",
+ "Ryrpgebavp" },
+{ "Verdana,serif",
+ "Negf'" },
+{ "Verdana,serif",
+ "Haerny" },
+{ "Verdana,serif",
+ "ratvar-cbjrerq" },
+{ "Verdana,serif",
+ "npgvba/nqiragher" },
+{ "Verdana,serif",
+ "ubeebe" },
+{ "Verdana,serif",
+ "Haqlvat." },
+{ "Verdana,serif",
+ "Guvf" },
+{ "Verdana,serif",
+ "uvtu" },
+{ "Verdana,serif",
+ "zl" },
+{ "Verdana,serif",
+ "\"uvtuyl" },
+{ "Verdana,serif",
+ "nagvpvcngrq\"" },
+{ "Verdana,serif",
+ "yvfg." },
+{ "Verdana,serif",
+ "dhbgr:" },
+{ "Verdana,serif",
+ "\"Tnzrf" },
+{ "Verdana,serif",
+ "ol" },
+{ "Verdana,serif",
+ "ynetr" },
+{ "Verdana,serif",
+ "gung." },
+{ "Verdana,serif",
+ "Tnzrf" },
+{ "Verdana,serif",
+ "rira" },
+{ "Verdana,serif",
+ "nvz" },
+{ "Verdana,serif",
+ "punatr" },
+{ "Verdana,serif",
+ "lbh." },
+{ "Verdana,serif",
+ "Vg'f" },
+{ "Verdana,serif",
+ "abg" },
+{ "Verdana,serif",
+ "cneg" },
+{ "Verdana,serif",
+ "onfvp" },
+{ "Verdana,serif",
+ "guvaxvat." },
+{ "Verdana,serif",
+ "Gurl" },
+{ "Verdana,serif",
+ "znex" },
+{ "Verdana,serif",
+ "gvzr." },
+{ "Verdana,serif",
+ "orpnzr" },
+{ "Verdana,serif",
+ "nccnerag" },
+{ "Verdana,serif",
+ "zr." },
+{ "Verdana,serif",
+ "Zl" },
+{ "Verdana,serif",
+ "uhfonaq" },
+{ "Verdana,serif",
+ "qnhtugre" },
+{ "Verdana,serif",
+ "jub" },
+{ "Verdana,serif",
+ "11" },
+{ "Verdana,serif",
+ "fur" },
+{ "Verdana,serif",
+ "cynlf" },
+{ "Verdana,serif",
+ "Fhcre" },
+{ "Verdana,serif",
+ "Znevb" },
+{ "Verdana,serif",
+ "Oebf." },
+{ "Verdana,serif",
+ "s------" },
+{ "Verdana,serif",
+ "ghar" },
+{ "Verdana,serif",
+ "whfg" },
+{ "Verdana,serif",
+ "tbrf" },
+{ "Verdana,serif",
+ "ebhaq" },
+{ "Verdana,serif",
+ "ebhaq." },
+{ "Verdana,serif",
+ "Nyy" },
+{ "Verdana,serif",
+ "jrrxraq" },
+{ "Verdana,serif",
+ "fgnlf" },
+{ "Verdana,serif",
+ "hf;" },
+{ "Verdana,serif",
+ "ortvaf" },
+{ "Verdana,serif",
+ "Sevqnl" },
+{ "Verdana,serif",
+ "avtug" },
+{ "Verdana,serif",
+ "raqf" },
+{ "Verdana,serif",
+ "Fhaqnl" },
+{ "Verdana,serif",
+ "avtug." },
+{ "Verdana,serif",
+ "Gb" },
+{ "Verdana,serif",
+ "zr" },
+{ "Verdana,serif",
+ "vaqvpngvir," },
+{ "Verdana,serif",
+ "flzobyvmrf" },
+{ "Verdana,serif",
+ "ceboyrz." },
+{ "Verdana,serif",
+ "Ebhaq" },
+{ "Verdana,serif",
+ "abjurer." },
+{ "Verdana,serif",
+ "Vg" },
+{ "Verdana,serif",
+ "qbrfa'g" },
+{ "Verdana,serif",
+ "rire" },
+{ "Verdana,serif",
+ "fgbc." },
+{ "Verdana,serif",
+ "ahzoref" },
+{ "Verdana,serif",
+ "znl" },
+{ "Verdana,serif",
+ "punatr," },
+{ "Verdana,serif",
+ "zbhagnva" },
+{ "Verdana,serif",
+ "trg" },
+{ "Verdana,serif",
+ "fgrrcre," },
+{ "Verdana,serif",
+ "guvat" },
+{ "Verdana,serif",
+ "gung'f" },
+{ "Verdana,serif",
+ "punfvat" },
+{ "Verdana,serif",
+ "ovttre," },
+{ "Verdana,serif",
+ "ohg" },
+{ "Verdana,serif",
+ "vg'f" },
+{ "Verdana,serif",
+ "ghar." },
+{ "Verdana,serif",
+ "Naq" },
+{ "Verdana,serif",
+ "fgbel." },
+{ "Verdana,serif",
+ "Gung'f" },
+{ "Verdana,serif",
+ "oernguvat." },
+{ "Verdana,serif",
+ "oneryl" },
+{ "Verdana,serif",
+ "fgnlvat" },
+{ "Verdana,serif",
+ "nyvir." },
+{ "Verdana,serif",
+ "Fgbel" },
+{ "Verdana,serif",
+ "trgf" },
+{ "Verdana,serif",
+ "lbhe" },
+{ "Verdana,serif",
+ "thg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fvyrag" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uhagre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cerivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2:37" },
+{ "Verdana,serif",
+ "VagryTnzre.pbz" },
+{ "Verdana,serif",
+ "gnxrf" },
+{ "Verdana,serif",
+ "ybbx" },
+{ "Verdana,serif",
+ "Hygvzngvba'f" },
+{ "Verdana,serif",
+ "Fgengrtvp" },
+{ "Verdana,serif",
+ "Fvzhyngvbaf'" },
+{ "Verdana,serif",
+ "Fvyrag" },
+{ "Verdana,serif",
+ "Uhagre" },
+{ "Verdana,serif",
+ "yvggyr" },
+{ "Verdana,serif",
+ "unq" },
+{ "Verdana,serif",
+ "fnl" },
+{ "Verdana,serif",
+ "cerivrj:" },
+{ "Verdana,serif",
+ "Lbh\xe2""\x80""\x99""yy" },
+{ "Verdana,serif",
+ "or" },
+{ "Verdana,serif",
+ "noyr" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "cynl" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "shyy" },
+{ "Verdana,serif",
+ "pnerre" },
+{ "Verdana,serif",
+ "pbirevat" },
+{ "Verdana,serif",
+ "gur" },
+{ "Verdana,serif",
+ "ragver" },
+{ "Verdana,serif",
+ "fcna" },
+{ "Verdana,serif",
+ "bs" },
+{ "Verdana,serif",
+ "Jne" },
+{ "Verdana,serif",
+ "va" },
+{ "Verdana,serif",
+ "Ngynagvp," },
+{ "Verdana,serif",
+ "fvatyr" },
+{ "Verdana,serif",
+ "uvfgbevpny" },
+{ "Verdana,serif",
+ "fpranevb," },
+{ "Verdana,serif",
+ "be" },
+{ "Verdana,serif",
+ "enaqbz" },
+{ "Verdana,serif",
+ "fpranevb" },
+{ "Verdana,serif",
+ "trarengbe." },
+{ "Verdana,serif",
+ "unir" },
+{ "Verdana,serif",
+ "4" },
+{ "Verdana,serif",
+ "vzznphyngr" },
+{ "Verdana,serif",
+ "H-Obng" },
+{ "Verdana,serif",
+ "zbqryf" },
+{ "Verdana,serif",
+ "ng" },
+{ "Verdana,serif",
+ "lbhe" },
+{ "Verdana,serif",
+ "qvfcbfny" },
+{ "Verdana,serif",
+ "juvpu" },
+{ "Verdana,serif",
+ "lbh" },
+{ "Verdana,serif",
+ "pna" },
+{ "Verdana,serif",
+ "pbzcyrgryl" },
+{ "Verdana,serif",
+ "boyvgrengr" },
+{ "Verdana,serif",
+ "rarzl." },
+{ "Verdana,serif",
+ "npprff" },
+{ "Verdana,serif",
+ "nyy" },
+{ "Verdana,serif",
+ "znwbe" },
+{ "Verdana,serif",
+ "fgngvbaf" },
+{ "Verdana,serif",
+ "yvxr;" },
+{ "Verdana,serif",
+ "NN" },
+{ "Verdana,serif",
+ "thaf," },
+{ "Verdana,serif",
+ "ulqebcubarf," },
+{ "Verdana,serif",
+ "rira" },
+{ "Verdana,serif",
+ "gbecrqb" },
+{ "Verdana,serif",
+ "qrsyrpgvba" },
+{ "Verdana,serif",
+ "pnyphyngbe." },
+{ "Verdana,serif",
+ "Gurer" },
+{ "Verdana,serif",
+ "jvyy" },
+{ "Verdana,serif",
+ "svefg-crefba" },
+{ "Verdana,serif",
+ "zbqr" },
+{ "Verdana,serif",
+ "gung" },
+{ "Verdana,serif",
+ "ranoyrf" },
+{ "Verdana,serif",
+ "pbzznaqref" },
+{ "Verdana,serif",
+ "bhg" },
+{ "Verdana,serif",
+ "gurer" },
+{ "Verdana,serif",
+ "gnetrg" },
+{ "Verdana,serif",
+ "naq" },
+{ "Verdana,serif",
+ "nggnpx" },
+{ "Verdana,serif",
+ "obgu" },
+{ "Verdana,serif",
+ "frn" },
+{ "Verdana,serif",
+ "nveobear" },
+{ "Verdana,serif",
+ "rarzvrf" },
+{ "Verdana,serif",
+ "hfvat" },
+{ "Verdana,serif",
+ "nagv-nvepensg" },
+{ "Verdana,serif",
+ "znpuvar" },
+{ "Verdana,serif",
+ "thaf" },
+{ "Verdana,serif",
+ "qrpx" },
+{ "Verdana,serif",
+ "pnaaba. " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "D3" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1.27" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cbvag" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Eryrnfr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1:52" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(20)" },
+{ "Verdana,serif",
+ "yngrfg" },
+{ "Verdana,serif",
+ "cngpu/cbvag" },
+{ "Verdana,serif",
+ "eryrnfr/jungrire" },
+{ "Verdana,serif",
+ "(irefvba" },
+{ "Verdana,serif",
+ "1.27)" },
+{ "Verdana,serif",
+ "Fbsgjner'f" },
+{ "Verdana,serif",
+ "Dhnxr" },
+{ "Verdana,serif",
+ "Neran." },
+{ "Verdana,serif",
+ "eryrnfr" },
+{ "Verdana,serif",
+ "shyyl" },
+{ "Verdana,serif",
+ "pbzcngvoyr" },
+{ "Verdana,serif",
+ "VVV:" },
+{ "Verdana,serif",
+ "Grnz" },
+{ "Verdana,serif",
+ "Neran" },
+{ "Verdana,serif",
+ "vzcyrzragf" },
+{ "Verdana,serif",
+ "srrqonpx" },
+{ "Verdana,serif",
+ "erprvirq" },
+{ "Verdana,serif",
+ "ynfg" },
+{ "Verdana,serif",
+ "1.25)." },
+{ "Verdana,serif",
+ "Nf" },
+{ "Verdana,serif",
+ "cre" },
+{ "Verdana,serif",
+ "ahzrebhf" },
+{ "Verdana,serif",
+ "erdhrfgf," },
+{ "Verdana,serif",
+ "cbvag" },
+{ "Verdana,serif",
+ "eriregf" },
+{ "Verdana,serif",
+ "culfvpf" },
+{ "Verdana,serif",
+ "jnl" },
+{ "Verdana,serif",
+ "jrer" },
+{ "Verdana,serif",
+ "1.17" },
+{ "Verdana,serif",
+ "eryrnfr." },
+{ "Verdana,serif",
+ "Zber" },
+{ "Verdana,serif",
+ "vasbezngvba" },
+{ "Verdana,serif",
+ "(vapyhqvat" },
+{ "Verdana,serif",
+ "Znp" },
+{ "Verdana,serif",
+ "Yvahk" },
+{ "Verdana,serif",
+ "irefvbaf" },
+{ "Verdana,serif",
+ "cngpu)" },
+{ "Verdana,serif",
+ "pna" },
+{ "Verdana,serif",
+ "or" },
+{ "Verdana,serif",
+ "sbhaq" },
+{ "Verdana,serif",
+ "Tenrzr'f" },
+{ "Verdana,serif",
+ ".cyna" },
+{ "Verdana,serif",
+ "." },
+{ "Verdana,serif",
+ "Evtug" },
+{ "Verdana,serif",
+ "abj," },
+{ "Verdana,serif",
+ "bayl" },
+{ "Verdana,serif",
+ "cynpr" },
+{ "Verdana,serif",
+ "SGC" },
+{ "Verdana,serif",
+ "gung'yy" },
+{ "Verdana,serif",
+ "punatvat" },
+{ "Verdana,serif",
+ "fubegyl" },
+{ "Verdana,serif",
+ "(jr'yy" },
+{ "Verdana,serif",
+ "cbfg" },
+{ "Verdana,serif",
+ "zveebef)." },
+{ "Verdana,serif",
+ "Zveebef:" },
+{ "Verdana,serif",
+ "Tnzref" },
+{ "Verdana,serif",
+ "Oyhr'f" },
+{ "Verdana,serif",
+ "Arjf" },
+{ "Verdana,serif",
+ "BTY" },
+{ "Verdana,serif",
+ "D3Neran.arg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "QAS" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Trgf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qngr?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1:23" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(28)" },
+{ "Verdana,serif",
+ "Jngpu" },
+{ "Verdana,serif",
+ "bhg!" },
+{ "Verdana,serif",
+ "Cvtf" },
+{ "Verdana,serif",
+ "fhqqrayl" },
+{ "Verdana,serif",
+ "fcebhgrq" },
+{ "Verdana,serif",
+ "jvatf," },
+{ "Verdana,serif",
+ "uryy" },
+{ "Verdana,serif",
+ "sebmra" },
+{ "Verdana,serif",
+ "bire" },
+{ "Verdana,serif",
+ "Nevmban" },
+{ "Verdana,serif",
+ "Pneqvanyf" },
+{ "Verdana,serif",
+ "jba" },
+{ "Verdana,serif",
+ "Objy" },
+{ "Verdana,serif",
+ "orpnhfr" },
+{ "Verdana,serif",
+ "3QE'f" },
+{ "Verdana,serif",
+ "Trbetr" },
+{ "Verdana,serif",
+ "Oebhffneq" },
+{ "Verdana,serif",
+ "vffhrq" },
+{ "Verdana,serif",
+ "rfgvzngrq" },
+{ "Verdana,serif",
+ "qngr" },
+{ "Verdana,serif",
+ "(bgure" },
+{ "Verdana,serif",
+ "guna" },
+{ "Verdana,serif",
+ "\"Jura" },
+{ "Verdana,serif",
+ "qbar\")" },
+{ "Verdana,serif",
+ "Qhxr" },
+{ "Verdana,serif",
+ "Ahxrz" },
+{ "Verdana,serif",
+ "Sberire" },
+{ "Verdana,serif",
+ "(gunaxf" },
+{ "Verdana,serif",
+ "hjv" },
+{ "Verdana,serif",
+ "zNa)." },
+{ "Verdana,serif",
+ "Nppbeqvat" },
+{ "Verdana,serif",
+ "TO" },
+{ "Verdana,serif",
+ "\"Gur" },
+{ "Verdana,serif",
+ "jvyy" },
+{ "Verdana,serif",
+ "bhg" },
+{ "Verdana,serif",
+ "arkg" },
+{ "Verdana,serif",
+ "lrne." },
+{ "Verdana,serif",
+ "pybfr" },
+{ "Verdana,serif",
+ "abj.\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vprjvaq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnyr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "RC" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ceri." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1:21" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ovyyl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"Jvpxrq\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jvyfba" },
+{ "Verdana,serif",
+ "T-fcbg" },
+{ "Verdana,serif",
+ "Vprjvaq" },
+{ "Verdana,serif",
+ "Qnyr:" },
+{ "Verdana,serif",
+ "Urneg" },
+{ "Verdana,serif",
+ "Jvagre," },
+{ "Verdana,serif",
+ "nqq-sbe," },
+{ "Verdana,serif",
+ "bqqyl" },
+{ "Verdana,serif",
+ "rabhtu," },
+{ "Verdana,serif",
+ "Qnyr. " },
+{ "Verdana,serif",
+ "Vapyhqrq" },
+{ "Verdana,serif",
+ "nybat" },
+{ "Verdana,serif",
+ "qrgnvyrq" },
+{ "Verdana,serif",
+ "jevgr-hc" },
+{ "Verdana,serif",
+ "fubgf. " },
+{ "Verdana,serif",
+ "gnfgr:" },
+{ "Verdana,serif",
+ "Jvagre" },
+{ "Verdana,serif",
+ "rssrpgviryl" },
+{ "Verdana,serif",
+ "rkgraqf" },
+{ "Verdana,serif",
+ "bevtvany" },
+{ "Verdana,serif",
+ "nyy-arj" },
+{ "Verdana,serif",
+ "dhrfg" },
+{ "Verdana,serif",
+ "qrfvtarq" },
+{ "Verdana,serif",
+ "uvtu-yriry" },
+{ "Verdana,serif",
+ "punenpgref," },
+{ "Verdana,serif",
+ "punenpgref" },
+{ "Verdana,serif",
+ "pregnvayl" },
+{ "Verdana,serif",
+ "pbapyhfvba" },
+{ "Verdana,serif",
+ "Qnyr." },
+{ "Verdana,serif",
+ "Lrg," },
+{ "Verdana,serif",
+ "lbh'yy" },
+{ "Verdana,serif",
+ "noyr" },
+{ "Verdana,serif",
+ "fgneg" },
+{ "Verdana,serif",
+ "nyzbfg" },
+{ "Verdana,serif",
+ "nal" },
+{ "Verdana,serif",
+ "Qnyr," },
+{ "Verdana,serif",
+ "cebivqrq" },
+{ "Verdana,serif",
+ "srry" },
+{ "Verdana,serif",
+ "cbjreshy" },
+{ "Verdana,serif",
+ "rabhtu" },
+{ "Verdana,serif",
+ "zrnaf" },
+{ "Verdana,serif",
+ "cnfg" },
+{ "Verdana,serif",
+ "avagu" },
+{ "Verdana,serif",
+ "rkcrevrapr" },
+{ "Verdana,serif",
+ "yriry" },
+{ "Verdana,serif",
+ "snpr" },
+{ "Verdana,serif",
+ "punyyratrf;" },
+{ "Verdana,serif",
+ "vaqrrq" },
+{ "Verdana,serif",
+ "Q&Q" },
+{ "Verdana,serif",
+ "fgnaqneqf." },
+{ "Verdana,serif",
+ "Lbh'yy" },
+{ "Verdana,serif",
+ "bcgvba" },
+{ "Verdana,serif",
+ "fvzcyl" },
+{ "Verdana,serif",
+ "rkcnafvba" },
+{ "Verdana,serif",
+ "fpengpu" },
+{ "Verdana,serif",
+ "vzcbegvat" },
+{ "Verdana,serif",
+ "fgenvtug" },
+{ "Verdana,serif",
+ "vagb" },
+{ "Verdana,serif",
+ "fpranevb." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "JJVV" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bayvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zbivr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12:48" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NZ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Znffviryl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zhygvcynlre" },
+{ "Verdana,serif",
+ "Cynlarg.pbz" },
+{ "Verdana,serif",
+ "sbbgntr" },
+{ "Verdana,serif",
+ "(va" },
+{ "Verdana,serif",
+ "Ovax" },
+{ "Verdana,serif",
+ "sbezng)" },
+{ "Verdana,serif",
+ "gnxra" },
+{ "Verdana,serif",
+ "pybfrq" },
+{ "Verdana,serif",
+ "orgn" },
+{ "Verdana,serif",
+ "grfg" },
+{ "Verdana,serif",
+ "Pbearerq" },
+{ "Verdana,serif",
+ "Eng" },
+{ "Verdana,serif",
+ "znffviryl" },
+{ "Verdana,serif",
+ "bayvar" },
+{ "Verdana,serif",
+ "jnetnzr/erny-gvzr" },
+{ "Verdana,serif",
+ "fgengrtl," },
+{ "Verdana,serif",
+ "Jbeyq" },
+{ "Verdana,serif",
+ "Jne" },
+{ "Verdana,serif",
+ "VV" },
+{ "Verdana,serif",
+ "Bayvar." },
+{ "Verdana,serif",
+ "Gunaxf" },
+{ "Verdana,serif",
+ "Gvz" },
+{ "Verdana,serif",
+ "T." },
+{ "Verdana,serif",
+ "bar!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvfp." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fubgf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12:40" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Trareny" },
+{ "Verdana,serif",
+ "Nsgre" },
+{ "Verdana,serif",
+ "qnl" },
+{ "Verdana,serif",
+ "bss" },
+{ "Verdana,serif",
+ "(qba'g" },
+{ "Verdana,serif",
+ "gbb" },
+{ "Verdana,serif",
+ "gubfr)," },
+{ "Verdana,serif",
+ "svtherq" },
+{ "Verdana,serif",
+ "V'q" },
+{ "Verdana,serif",
+ "xvpx" },
+{ "Verdana,serif",
+ "guvatf" },
+{ "Verdana,serif",
+ "pbagnvavat" },
+{ "Verdana,serif",
+ "bhe" },
+{ "Verdana,serif",
+ "pbyyrpgvba" },
+{ "Verdana,serif",
+ "fperrafubgf" },
+{ "Verdana,serif",
+ "xrrc" },
+{ "Verdana,serif",
+ "hc-gb-qngr):" },
+{ "Verdana,serif",
+ "Arirejvagre" },
+{ "Verdana,serif",
+ "Avtugf" },
+{ "Verdana,serif",
+ "--" },
+{ "Verdana,serif",
+ "Cynarg" },
+{ "Verdana,serif",
+ "Arirejvagre" },
+{ "Verdana,serif",
+ "hccrq" },
+{ "Verdana,serif",
+ "ongpu" },
+{ "Verdana,serif",
+ "pbaprcg" },
+{ "Verdana,serif",
+ "neg/eraqrerq" },
+{ "Verdana,serif",
+ "vzntrf" },
+{ "Verdana,serif",
+ "Hzore" },
+{ "Verdana,serif",
+ "Uhyx." },
+{ "Verdana,serif",
+ "Fgne" },
+{ "Verdana,serif",
+ "Gerx:" },
+{ "Verdana,serif",
+ "Oevqtr" },
+{ "Verdana,serif",
+ "Pbzznaqre" },
+{ "Verdana,serif",
+ "bssvpvny" },
+{ "Verdana,serif",
+ "jrofvgr" },
+{ "Verdana,serif",
+ "frg" },
+{ "Verdana,serif",
+ "Jbeyq" },
+{ "Verdana,serif",
+ "Jne" },
+{ "Verdana,serif",
+ "VV:" },
+{ "Verdana,serif",
+ "Rhebcr" },
+{ "Verdana,serif",
+ "va" },
+{ "Verdana,serif",
+ "Thasver" },
+{ "Verdana,serif",
+ "zbfg" },
+{ "Verdana,serif",
+ "anzr" },
+{ "Verdana,serif",
+ "urneq" },
+{ "Verdana,serif",
+ "xvpxrq" },
+{ "Verdana,serif",
+ "TN-Fgengrtl" },
+{ "Verdana,serif",
+ "Fheivibe" },
+{ "Verdana,serif",
+ "bs" },
+{ "Verdana,serif",
+ "gur" },
+{ "Verdana,serif",
+ "Ntrf" },
+{ "Verdana,serif",
+ "ECTCynarg" },
+{ "Verdana,serif",
+ "ECT." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "AJA'f" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Hzore" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uhyx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:45" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Va-Ubhfr:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(6)" },
+{ "Verdana,serif",
+ "Vagrecynl" },
+{ "Verdana,serif",
+ "Avtugf" },
+{ "Verdana,serif",
+ "qrfxgbc" },
+{ "Verdana,serif",
+ "vzntr" },
+{ "Verdana,serif",
+ "yber" },
+{ "Verdana,serif",
+ "srnefbzr" },
+{ "Verdana,serif",
+ "ornfgvr:" },
+{ "Verdana,serif",
+ "uhyxf" },
+{ "Verdana,serif",
+ "znffvir" },
+{ "Verdana,serif",
+ "perngherf" },
+{ "Verdana,serif",
+ "qjryy" },
+{ "Verdana,serif",
+ "qrrc" },
+{ "Verdana,serif",
+ "orarngu" },
+{ "Verdana,serif",
+ "rnegu." },
+{ "Verdana,serif",
+ "Evccvat" },
+{ "Verdana,serif",
+ "guebhtu" },
+{ "Verdana,serif",
+ "ebpx" },
+{ "Verdana,serif",
+ "yvtug" },
+{ "Verdana,serif",
+ "haqreoehfu," },
+{ "Verdana,serif",
+ "enzcntr" },
+{ "Verdana,serif",
+ "pbagvahbhfyl" },
+{ "Verdana,serif",
+ "yrnivat" },
+{ "Verdana,serif",
+ "jnxr" },
+{ "Verdana,serif",
+ "qrfgehpgvba." },
+{ "Verdana,serif",
+ "Cbjreshyyl" },
+{ "Verdana,serif",
+ "ohvyg," },
+{ "Verdana,serif",
+ "Uhyxf" },
+{ "Verdana,serif",
+ "fbzrguvat" },
+{ "Verdana,serif",
+ "pebff" },
+{ "Verdana,serif",
+ "orgjrra" },
+{ "Verdana,serif",
+ "terng" },
+{ "Verdana,serif",
+ "ncr" },
+{ "Verdana,serif",
+ "orrgyr." },
+{ "Verdana,serif",
+ "Nezbe" },
+{ "Verdana,serif",
+ "cyngrf" },
+{ "Verdana,serif",
+ "pbire" },
+{ "Verdana,serif",
+ "iveghnyyl" },
+{ "Verdana,serif",
+ "vgf" },
+{ "Verdana,serif",
+ "puvgvabhf" },
+{ "Verdana,serif",
+ "obql," },
+{ "Verdana,serif",
+ "jubfr" },
+{ "Verdana,serif",
+ "fpnggrerq" },
+{ "Verdana,serif",
+ "srryref" },
+{ "Verdana,serif",
+ "erfrzoyr" },
+{ "Verdana,serif",
+ "fcnefr" },
+{ "Verdana,serif",
+ "unve." },
+{ "Verdana,serif",
+ "ybj," },
+{ "Verdana,serif",
+ "ebhaqrq" },
+{ "Verdana,serif",
+ "urnq" },
+{ "Verdana,serif",
+ "qbzvangrq" },
+{ "Verdana,serif",
+ "cnve" },
+{ "Verdana,serif",
+ "znaqvoyrf" },
+{ "Verdana,serif",
+ "ebjf" },
+{ "Verdana,serif",
+ "gevnathyne" },
+{ "Verdana,serif",
+ "grrgu." },
+{ "Verdana,serif",
+ "Na" },
+{ "Verdana,serif",
+ "uhyx" },
+{ "Verdana,serif",
+ "qryvire" },
+{ "Verdana,serif",
+ "oybjf" },
+{ "Verdana,serif",
+ "pehfu" },
+{ "Verdana,serif",
+ "rarzl." },
+{ "Verdana,serif",
+ "nqqvgvba," },
+{ "Verdana,serif",
+ "fgebat" },
+{ "Verdana,serif",
+ "ovgr" },
+{ "Verdana,serif",
+ "nezbe" },
+{ "Verdana,serif",
+ "rkbfxryrgbaf" },
+{ "Verdana,serif",
+ "rnfr." },
+{ "Verdana,serif",
+ "Qrfcvgr" },
+{ "Verdana,serif",
+ "ohyx," },
+{ "Verdana,serif",
+ "hzore" },
+{ "Verdana,serif",
+ "vagryyvtrag." },
+{ "Verdana,serif",
+ "Jura" },
+{ "Verdana,serif",
+ "oehgr" },
+{ "Verdana,serif",
+ "sbepr" },
+{ "Verdana,serif",
+ "jba'g" },
+{ "Verdana,serif",
+ "birepbzr" },
+{ "Verdana,serif",
+ "rarzl," },
+{ "Verdana,serif",
+ "pncnoyr" },
+{ "Verdana,serif",
+ "bhgguvaxvat" },
+{ "Verdana,serif",
+ "gubfr" },
+{ "Verdana,serif",
+ "nffhzr" },
+{ "Verdana,serif",
+ "fghcvq" },
+{ "Verdana,serif",
+ "ornfg." },
+{ "Verdana,serif",
+ "(Pyvpx" },
+{ "Verdana,serif",
+ "raynetr!)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Syl3Q" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tnzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ratvar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:19" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fbsgjner:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3Q" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ratvarf" },
+{ "Verdana,serif",
+ "abgvprq" },
+{ "Verdana,serif",
+ "BcraTY.bet" },
+{ "Verdana,serif",
+ "i2.0" },
+{ "Verdana,serif",
+ "nycun" },
+{ "Verdana,serif",
+ "Syl3Q" },
+{ "Verdana,serif",
+ "ratvar" },
+{ "Verdana,serif",
+ "eryrnfrq. " },
+{ "Verdana,serif",
+ "fubeg" },
+{ "Verdana,serif",
+ "oyheo" },
+{ "Verdana,serif",
+ "ratvar:" },
+{ "Verdana,serif",
+ "P++" },
+{ "Verdana,serif",
+ "zhygv-cynlre" },
+{ "Verdana,serif",
+ "hfvat" },
+{ "Verdana,serif",
+ "BcraTY" },
+{ "Verdana,serif",
+ "1.1," },
+{ "Verdana,serif",
+ "QverpgVachg," },
+{ "Verdana,serif",
+ "QverpgFbhaq," },
+{ "Verdana,serif",
+ "QverpgCynl" },
+{ "Verdana,serif",
+ "Jva9k/2x/AG." },
+{ "Verdana,serif",
+ "2.0" },
+{ "Verdana,serif",
+ "irefvba" },
+{ "Verdana,serif",
+ "nqqf" },
+{ "Verdana,serif",
+ "raunaprzragf" },
+{ "Verdana,serif",
+ "vapyhqvat" },
+{ "Verdana,serif",
+ "funqref" },
+{ "Verdana,serif",
+ "j/" },
+{ "Verdana,serif",
+ "funqre" },
+{ "Verdana,serif",
+ "rqvgbe," },
+{ "Verdana,serif",
+ "fhccbeg" },
+{ "Verdana,serif",
+ "snprf," },
+{ "Verdana,serif",
+ "pheirq" },
+{ "Verdana,serif",
+ "snprf" },
+{ "Verdana,serif",
+ "gev-fbhc" },
+{ "Verdana,serif",
+ "NpgvirK" },
+{ "Verdana,serif",
+ "sebag-raq" },
+{ "Verdana,serif",
+ "jro" },
+{ "Verdana,serif",
+ "tnzrf/ncc," },
+{ "Verdana,serif",
+ "pbzcngvovyvgl" },
+{ "Verdana,serif",
+ "Dhnxr3" },
+{ "Verdana,serif",
+ "rqvgbef" },
+{ "Verdana,serif",
+ "zrfu" },
+{ "Verdana,serif",
+ "svyr" },
+{ "Verdana,serif",
+ "sbezng" },
+{ "Verdana,serif",
+ "navzngrq" },
+{ "Verdana,serif",
+ "fgngvp" },
+{ "Verdana,serif",
+ "bowrpgf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbyva" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZpEnr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Enyyl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2.0" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "qrzb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:13" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Enpvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(19)" },
+{ "Verdana,serif",
+ "Pbqrznfgref" },
+{ "Verdana,serif",
+ "(lrf," },
+{ "Verdana,serif",
+ "glcr," },
+{ "Verdana,serif",
+ "qnzzvg!)" },
+{ "Verdana,serif",
+ "Pbyva" },
+{ "Verdana,serif",
+ "ZpEnr" },
+{ "Verdana,serif",
+ "Enyyl" },
+{ "Verdana,serif",
+ "2.0," },
+{ "Verdana,serif",
+ "tvivat" },
+{ "Verdana,serif",
+ "punapr" },
+{ "Verdana,serif",
+ "gel" },
+{ "Verdana,serif",
+ "enyyl" },
+{ "Verdana,serif",
+ "fvzhyngbe" },
+{ "Verdana,serif",
+ "uvg" },
+{ "Verdana,serif",
+ "fgberf" },
+{ "Verdana,serif",
+ "Rhebcr" },
+{ "Verdana,serif",
+ "zbagu. " },
+{ "Verdana,serif",
+ "Bayl" },
+{ "Verdana,serif",
+ "bar" },
+{ "Verdana,serif",
+ "pne" },
+{ "Verdana,serif",
+ "ninvynoyr" },
+{ "Verdana,serif",
+ "36.5" },
+{ "Verdana,serif",
+ "zrt" },
+{ "Verdana,serif",
+ "qrzb," },
+{ "Verdana,serif",
+ "guerr" },
+{ "Verdana,serif",
+ "genpxf," },
+{ "Verdana,serif",
+ "yrg'f" },
+{ "Verdana,serif",
+ "\"Nepnqr" },
+{ "Verdana,serif",
+ "Zbqr\". " },
+{ "Verdana,serif",
+ "Oyhr" },
+{ "Verdana,serif",
+ "'f! " },
+{ "Verdana,serif",
+ "Jubbcf," },
+{ "Verdana,serif",
+ "zvffrq" },
+{ "Verdana,serif",
+ "fvgr" },
+{ "Verdana,serif",
+ "i1.05" },
+{ "Verdana,serif",
+ "2.0." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ribyhgvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Oynpx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Juvgr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:16" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "/" },
+{ "Verdana,serif",
+ "Tnznfhgen" },
+{ "Verdana,serif",
+ "ivqrb" },
+{ "Verdana,serif",
+ "TQP" },
+{ "Verdana,serif",
+ "2000," },
+{ "Verdana,serif",
+ "Ribyhgvba" },
+{ "Verdana,serif",
+ "Oynpx" },
+{ "Verdana,serif",
+ "Juvgr" },
+{ "Verdana,serif",
+ "\"," },
+{ "Verdana,serif",
+ "Yvbaurnq'f" },
+{ "Verdana,serif",
+ "Ovt-Crgr" },
+{ "Verdana,serif",
+ "Zbylarhk." },
+{ "Verdana,serif",
+ "qrfpevcgvba" },
+{ "Verdana,serif",
+ "gbffrq" },
+{ "Verdana,serif",
+ "yvaxf" },
+{ "Verdana,serif",
+ "(" },
+{ "Verdana,serif",
+ "Erny" },
+{ "Verdana,serif",
+ "Dhvpxgvzr" },
+{ "Verdana,serif",
+ "synibef)" },
+{ "Verdana,serif",
+ "ivqrb:" },
+{ "Verdana,serif",
+ "vavgvny" },
+{ "Verdana,serif",
+ "qrfvta" },
+{ "Verdana,serif",
+ "fgntrf" },
+{ "Verdana,serif",
+ "qrirybczrag" },
+{ "Verdana,serif",
+ "grnz'f" },
+{ "Verdana,serif",
+ "nzovgvba" },
+{ "Verdana,serif",
+ "cebqhpr" },
+{ "Verdana,serif",
+ "vaabingvir" },
+{ "Verdana,serif",
+ "jnl." },
+{ "Verdana,serif",
+ "rkcyberf" },
+{ "Verdana,serif",
+ "cebprff" },
+{ "Verdana,serif",
+ "arrqrq" },
+{ "Verdana,serif",
+ "shysvyy" },
+{ "Verdana,serif",
+ "gurfr" },
+{ "Verdana,serif",
+ "nzovgvbaf" },
+{ "Verdana,serif",
+ "vyyhfgengrf" },
+{ "Verdana,serif",
+ "rknzcyrf" },
+{ "Verdana,serif",
+ "fgntr" },
+{ "Verdana,serif",
+ "qrirybczrag," },
+{ "Verdana,serif",
+ "qrzbafgengvbaf" },
+{ "Verdana,serif",
+ "vagrtengvba" },
+{ "Verdana,serif",
+ "nqinaprq" },
+{ "Verdana,serif",
+ "NV," },
+{ "Verdana,serif",
+ "grpuabybtl" },
+{ "Verdana,serif",
+ "vagresnpr" },
+{ "Verdana,serif",
+ "oernxguebhtuf" },
+{ "Verdana,serif",
+ "jvgubhg" },
+{ "Verdana,serif",
+ "pbzcebzvfvat" },
+{ "Verdana,serif",
+ "tnzrcynl." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Oevqtr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "D&N" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:06" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fcnpr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fubbgre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fvz." },
+{ "Verdana,serif",
+ "Tn-Fvz" },
+{ "Verdana,serif",
+ "ternfrq" },
+{ "Verdana,serif",
+ "Ynjerapr" },
+{ "Verdana,serif",
+ "Ubyynaq" },
+{ "Verdana,serif",
+ "Gbgnyyl" },
+{ "Verdana,serif",
+ "Tnzrf," },
+{ "Verdana,serif",
+ "pung" },
+{ "Verdana,serif",
+ "nobhg" },
+{ "Verdana,serif",
+ "Fgne" },
+{ "Verdana,serif",
+ "Gerx:" },
+{ "Verdana,serif",
+ "Oevqtr" },
+{ "Verdana,serif",
+ "qvssrerag" },
+{ "Verdana,serif",
+ "cerivbhf" },
+{ "Verdana,serif",
+ "(fhpu" },
+{ "Verdana,serif",
+ "Onggyrunjxf:" },
+{ "Verdana,serif",
+ "1942" },
+{ "Verdana,serif",
+ "K-Jvat:" },
+{ "Verdana,serif",
+ "Nyyvnapr)," },
+{ "Verdana,serif",
+ "ovg" },
+{ "Verdana,serif",
+ "jr'yy" },
+{ "Verdana,serif",
+ "frr" },
+{ "Verdana,serif",
+ "rqvgbe" },
+{ "Verdana,serif",
+ "(fuvccvat" },
+{ "Verdana,serif",
+ "tnzr)," },
+{ "Verdana,serif",
+ "tnzr'f" },
+{ "Verdana,serif",
+ "pbagebyf. " },
+{ "Verdana,serif",
+ "favc:" },
+{ "Verdana,serif",
+ "Lbh'ir" },
+{ "Verdana,serif",
+ "orra" },
+{ "Verdana,serif",
+ "cevznevyl" },
+{ "Verdana,serif",
+ "xabja" },
+{ "Verdana,serif",
+ "sbe" },
+{ "Verdana,serif",
+ "lbhe" },
+{ "Verdana,serif",
+ "terng" },
+{ "Verdana,serif",
+ "VV" },
+{ "Verdana,serif",
+ "naq" },
+{ "Verdana,serif",
+ "Jnef" },
+{ "Verdana,serif",
+ "onfrq" },
+{ "Verdana,serif",
+ "fvzhyngvbaf," },
+{ "Verdana,serif",
+ "fb" },
+{ "Verdana,serif",
+ "jung" },
+{ "Verdana,serif",
+ "cebzcgrq" },
+{ "Verdana,serif",
+ "lbh" },
+{ "Verdana,serif",
+ "znxr" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "tnzr" },
+{ "Verdana,serif",
+ "Gerx" },
+{ "Verdana,serif",
+ "havirefr?" },
+{ "Verdana,serif",
+ "Jr" },
+{ "Verdana,serif",
+ "jrera'g" },
+{ "Verdana,serif",
+ "ernql" },
+{ "Verdana,serif",
+ "yrnir" },
+{ "Verdana,serif",
+ "fcnpr" },
+{ "Verdana,serif",
+ "pbzong" },
+{ "Verdana,serif",
+ "neran," },
+{ "Verdana,serif",
+ "jnagrq" },
+{ "Verdana,serif",
+ "Npgvivfvba" },
+{ "Verdana,serif",
+ "nfxrq" },
+{ "Verdana,serif",
+ "guvax" },
+{ "Verdana,serif",
+ "bccbeghavgl" },
+{ "Verdana,serif",
+ "dhnyvgl" },
+{ "Verdana,serif",
+ "jbex." },
+{ "Verdana,serif",
+ "Qhevat" },
+{ "Verdana,serif",
+ "gnyxf" },
+{ "Verdana,serif",
+ "pnzr" },
+{ "Verdana,serif",
+ "znxr" },
+{ "Verdana,serif",
+ "vagrerfgvat" },
+{ "Verdana,serif",
+ "obgu" },
+{ "Verdana,serif",
+ "grnz" },
+{ "Verdana,serif",
+ "choyvp" },
+{ "Verdana,serif",
+ "fb" },
+{ "Verdana,serif",
+ "ortvaavat" },
+{ "Verdana,serif",
+ "Pbzznaqre." },
+{ "Verdana,serif",
+ "Gerx" },
+{ "Verdana,serif",
+ "evpu" },
+{ "Verdana,serif",
+ "35" },
+{ "Verdana,serif",
+ "jbegu" },
+{ "Verdana,serif",
+ "zngrevny," },
+{ "Verdana,serif",
+ "sbhe" },
+{ "Verdana,serif",
+ "GI" },
+{ "Verdana,serif",
+ "fubjf," },
+{ "Verdana,serif",
+ "avar" },
+{ "Verdana,serif",
+ "zbivrf," },
+{ "Verdana,serif",
+ "uhaqerqf" },
+{ "Verdana,serif",
+ "abiryf" },
+{ "Verdana,serif",
+ "ybg" },
+{ "Verdana,serif",
+ "zber." },
+{ "Verdana,serif",
+ "qenjvat" },
+{ "Verdana,serif",
+ "hcba" },
+{ "Verdana,serif",
+ "shaqnzragny" },
+{ "Verdana,serif",
+ "ryrzragf" },
+{ "Verdana,serif",
+ "yvprafr" },
+{ "Verdana,serif",
+ "sha" },
+{ "Verdana,serif",
+ "pbzcryyvat" },
+{ "Verdana,serif",
+ "qrsrafr," },
+{ "Verdana,serif",
+ "erfphr," },
+{ "Verdana,serif",
+ "qvcybznpl," },
+{ "Verdana,serif",
+ "rkcybengvba" },
+{ "Verdana,serif",
+ "fpvragvsvp" },
+{ "Verdana,serif",
+ "qvfpbirel." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Onyqhe'f" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tngr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "vagy." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cngpu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:50" },
+{ "Verdana,serif",
+ "OvbJner" },
+{ "Verdana,serif",
+ "vagreangvbany" },
+{ "Verdana,serif",
+ "Onyqhe'f" },
+{ "Verdana,serif",
+ "Tngr" },
+{ "Verdana,serif",
+ "VV:" },
+{ "Verdana,serif",
+ "Funqbjf" },
+{ "Verdana,serif",
+ "Nza. " },
+{ "Verdana,serif",
+ "Abguvat" },
+{ "Verdana,serif",
+ "/" },
+{ "Verdana,serif",
+ "Ratyvfu-ynathntr" },
+{ "Verdana,serif",
+ " (eryrnfrq" },
+{ "Verdana,serif",
+ "Abi." },
+{ "Verdana,serif",
+ "29)," },
+{ "Verdana,serif",
+ "vg. " },
+{ "Verdana,serif",
+ "Purpx" },
+{ "Verdana,serif",
+ "yvfg" },
+{ "Verdana,serif",
+ "b'" },
+{ "Verdana,serif",
+ "naq/be" },
+{ "Verdana,serif",
+ "qbjaybnq" },
+{ "Verdana,serif",
+ "vg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NZQ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "vagreivrjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:34" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uneqjner:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NZQ" },
+{ "Verdana,serif",
+ "Url," },
+{ "Verdana,serif",
+ "jbhyq" },
+{ "Verdana,serif",
+ "ghea" },
+{ "Verdana,serif",
+ "qbja" },
+{ "Verdana,serif",
+ "avpr" },
+{ "Verdana,serif",
+ "gjvaf" },
+{ "Verdana,serif",
+ "<Nvzrr" },
+{ "Verdana,serif",
+ "fznpxf" },
+{ "Verdana,serif",
+ "Ovyyl" },
+{ "Verdana,serif",
+ "hcfvqr" },
+{ "Verdana,serif",
+ "urnq>...? " },
+{ "Verdana,serif",
+ "Shyyba3Q" },
+{ "Verdana,serif",
+ "GrzcyrbsGrpu" },
+{ "Verdana,serif",
+ "vagreivrjf" },
+{ "Verdana,serif",
+ "NZQ. " },
+{ "Verdana,serif",
+ "Grzcyr'f" },
+{ "Verdana,serif",
+ "NZQ'f" },
+{ "Verdana,serif",
+ "Qerj" },
+{ "Verdana,serif",
+ "Cenvevr" },
+{ "Verdana,serif",
+ "(bar" },
+{ "Verdana,serif",
+ "CE" },
+{ "Verdana,serif",
+ "thlf)" },
+{ "Verdana,serif",
+ "ertneqvat" },
+{ "Verdana,serif",
+ "D3" },
+{ "Verdana,serif",
+ "rneavatf," },
+{ "Verdana,serif",
+ "sno" },
+{ "Verdana,serif",
+ "cynag," },
+{ "Verdana,serif",
+ "Gvzan" },
+{ "Verdana,serif",
+ "fglyr" },
+{ "Verdana,serif",
+ "puvc," },
+{ "Verdana,serif",
+ "angher" },
+{ "Verdana,serif",
+ "(cbfg" },
+{ "Verdana,serif",
+ "naabhaprzrag). " },
+{ "Verdana,serif",
+ "ShyyBa3Q.pbz'f" },
+{ "Verdana,serif",
+ "NZQ" },
+{ "Verdana,serif",
+ "f" },
+{ "Verdana,serif",
+ "2" },
+{ "Verdana,serif",
+ "puvcfrg/cebprffbe" },
+{ "Verdana,serif",
+ "vaqhfgel" },
+{ "Verdana,serif",
+ "srngher" },
+{ "Verdana,serif",
+ "gurl'ir" },
+{ "Verdana,serif",
+ "juvccrq" },
+{ "Verdana,serif",
+ "(cneg" },
+{ "Verdana,serif",
+ "1" },
+{ "Verdana,serif",
+ "IVN). " },
+{ "Arial,serif",
+ "Guvf" },
+{ "Arial,serif",
+ "vagreivrj" },
+{ "Arial,serif",
+ "(bapr" },
+{ "Arial,serif",
+ "ntnva," },
+{ "Arial,serif",
+ "jvgu" },
+{ "Arial,serif",
+ "Qerj)" },
+{ "Arial,serif",
+ "pbafvfgf" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "trareny" },
+{ "Arial,serif",
+ "vaqhfgel" },
+{ "Arial,serif",
+ "dhrfgvbaf" },
+{ "Arial,serif",
+ "NZQ" },
+{ "Arial,serif",
+ "fcrpvsvp" },
+{ "Arial,serif",
+ "ng" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "raq. " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Frirenapr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cnpxntr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:10" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Elna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"GurZlfgvp\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ybeq" },
+{ "Verdana,serif",
+ "yvzrlf" },
+{ "Verdana,serif",
+ "TnzrFcbg" },
+{ "Verdana,serif",
+ "HX" },
+{ "Verdana,serif",
+ "tehool" },
+{ "Verdana,serif",
+ "unaqf" },
+{ "Verdana,serif",
+ "gvgyr" },
+{ "Verdana,serif",
+ "Frirenapr" },
+{ "Verdana,serif",
+ ":" },
+{ "Verdana,serif",
+ "Oynqrf" },
+{ "Verdana,serif",
+ "Qnexarff" },
+{ "Verdana,serif",
+ "jevgvat" },
+{ "Verdana,serif",
+ "(Ybbxf" },
+{ "Verdana,serif",
+ "Ehar" },
+{ "Verdana,serif",
+ "zr)." },
+{ "Verdana,serif",
+ "nyjnlf," },
+{ "Verdana,serif",
+ "pbzrf" },
+{ "Verdana,serif",
+ "cntrf" },
+{ "Verdana,serif",
+ "yratgu" },
+{ "Verdana,serif",
+ "pbagnvaf" },
+{ "Verdana,serif",
+ "tnyyrel" },
+{ "Verdana,serif",
+ "ivrjvat" },
+{ "Verdana,serif",
+ "cyrnfher." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cntr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "erinzcrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:03" },
+{ "Verdana,serif",
+ "qrirybcref" },
+{ "Verdana,serif",
+ "puhzcf" },
+{ "Verdana,serif",
+ "Rir" },
+{ "Verdana,serif",
+ "ubzrcntr" },
+{ "Verdana,serif",
+ "fynccrq" },
+{ "Verdana,serif",
+ "rkpvgvat" },
+{ "Verdana,serif",
+ "pbagrag" },
+{ "Verdana,serif",
+ "zbeavat." },
+{ "Verdana,serif",
+ "Nzbat" },
+{ "Verdana,serif",
+ "nqqvgvbaf" },
+{ "Verdana,serif",
+ "irel" },
+{ "Verdana,serif",
+ "fjrrg" },
+{ "Verdana,serif",
+ "ybbxvat" },
+{ "Verdana,serif",
+ "ZZBT" },
+{ "Verdana,serif",
+ "(nf" },
+{ "Verdana,serif",
+ "vg)" },
+{ "Verdana,serif",
+ "svaq" },
+{ "Verdana,serif",
+ "Hcqngrq" },
+{ "Verdana,serif",
+ "Fuvcf" },
+{ "Verdana,serif",
+ "Qngnonfr" },
+{ "Verdana,serif",
+ "28" },
+{ "Verdana,serif",
+ "Arj" },
+{ "Verdana,serif",
+ "Fperrafubgf" },
+{ "Verdana,serif",
+ "Fpurzngvp" },
+{ "Verdana,serif",
+ "Oyhrcevagf" },
+{ "Verdana,serif",
+ "frira" },
+{ "Verdana,serif",
+ "fuvcf" },
+{ "Verdana,serif",
+ "Jnyycncref" },
+{ "Verdana,serif",
+ "ZC3" },
+{ "Verdana,serif",
+ "Nhqvb" },
+{ "Verdana,serif",
+ "Genpxf" },
+{ "Verdana,serif",
+ "Bar" },
+{ "Verdana,serif",
+ "Vyyhfgengrq" },
+{ "Verdana,serif",
+ "Fubeg-Fgbel" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tybony" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bcrengvbaf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "7:34" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(12)" },
+{ "Verdana,serif",
+ "ab" },
+{ "Verdana,serif",
+ "vqrn" },
+{ "Verdana,serif",
+ "sbyxf" },
+{ "Verdana,serif",
+ "lrfgreqnl" },
+{ "Verdana,serif",
+ "Wrss" },
+{ "Verdana,serif",
+ "Oneauneg," },
+{ "Verdana,serif",
+ "cebqhpre" },
+{ "Verdana,serif",
+ "arjyl" },
+{ "Verdana,serif",
+ "naabhaprq" },
+{ "Verdana,serif",
+ "Tybony" },
+{ "Verdana,serif",
+ "Bcrengvbaf" },
+{ "Verdana,serif",
+ "orvat" },
+{ "Verdana,serif",
+ "qrirybcrq" },
+{ "Verdana,serif",
+ "Onexvat" },
+{ "Verdana,serif",
+ "Qbt" },
+{ "Verdana,serif",
+ "Penir" },
+{ "Verdana,serif",
+ "Ragregnvazrag." },
+{ "Verdana,serif",
+ "Naljnlf" },
+{ "Verdana,serif",
+ "urer'f" },
+{ "Verdana,serif",
+ "pyvc" },
+{ "Verdana,serif",
+ "gnfgrf" },
+{ "Verdana,serif",
+ "yrff" },
+{ "Verdana,serif",
+ "svyyvat" },
+{ "Verdana,serif",
+ "Qnvyl" },
+{ "Verdana,serif",
+ "Enqne" },
+{ "Verdana,serif",
+ ":" },
+{ "Verdana,serif",
+ "Vf" },
+{ "Verdana,serif",
+ "pbeerpg" },
+{ "Verdana,serif",
+ "frrz" },
+{ "Verdana,serif",
+ "ernyvfgvp" },
+{ "Verdana,serif",
+ "vpbavp?" },
+{ "Verdana,serif",
+ "Wrss" },
+{ "Verdana,serif",
+ "Oneauneg" },
+{ "Verdana,serif",
+ "Nofbyhgryl," },
+{ "Verdana,serif",
+ "nofbyhgryl" },
+{ "Verdana,serif",
+ "ernyvfgvp." },
+{ "Verdana,serif",
+ "jrncbaf" },
+{ "Verdana,serif",
+ "raivebazragf" },
+{ "Verdana,serif",
+ "ynvq" },
+{ "Verdana,serif",
+ "jura" },
+{ "Verdana,serif",
+ "pbzcnerq" },
+{ "Verdana,serif",
+ "erny-yvsr" },
+{ "Verdana,serif",
+ "pbhagrecnegf." },
+{ "Verdana,serif",
+ "jnag" },
+{ "Verdana,serif",
+ "cynlre" },
+{ "Verdana,serif",
+ "zbqryf" },
+{ "Verdana,serif",
+ "ybbxvat." },
+{ "Verdana,serif",
+ "Vs" },
+{ "Verdana,serif",
+ "cynlvat" },
+{ "Verdana,serif",
+ "Creh," },
+{ "Verdana,serif",
+ "erory" },
+{ "Verdana,serif",
+ "Creh" },
+{ "Verdana,serif",
+ "ybbx." },
+{ "Verdana,serif",
+ "Fnl" },
+{ "Verdana,serif",
+ "H.F." },
+{ "Verdana,serif",
+ "tbvat" },
+{ "Verdana,serif",
+ "Pbyhzovn" },
+{ "Verdana,serif",
+ "gnxr" },
+{ "Verdana,serif",
+ "qeht" },
+{ "Verdana,serif",
+ "ybeq." },
+{ "Verdana,serif",
+ "Fcrpvny" },
+{ "Verdana,serif",
+ "Sbeprf." },
+{ "Verdana,serif",
+ "ernyvfgvp," },
+{ "Verdana,serif",
+ "fbhaq" },
+{ "Verdana,serif",
+ "qnzntr." },
+{ "Verdana,serif",
+ "Naq," },
+{ "Verdana,serif",
+ "tbny" },
+{ "Verdana,serif",
+ "vzzrefvir" },
+{ "Verdana,serif",
+ "svefg-crefba" },
+{ "Verdana,serif",
+ "fubbgre" },
+{ "Verdana,serif",
+ "CP." },
+{ "Verdana,serif",
+ "cynl" },
+{ "Verdana,serif",
+ "jryy." },
+{ "Verdana,serif",
+ "Jung" },
+{ "Verdana,serif",
+ "ernyyl" },
+{ "Verdana,serif",
+ "tbbq," },
+{ "Verdana,serif",
+ "jryy-onynaprq," },
+{ "Verdana,serif",
+ "ryvgr" },
+{ "Verdana,serif",
+ "svtugvat" },
+{ "Verdana,serif",
+ "orpbzr" },
+{ "Verdana,serif",
+ "bs." },
+{ "Verdana,serif",
+ "vagrafr," },
+{ "Verdana,serif",
+ "svyyrq" },
+{ "Verdana,serif",
+ "znexrg." },
+{ "Verdana,serif",
+ "fcrnxvat" },
+{ "Verdana,serif",
+ "Bcrengvbaf," },
+{ "Verdana,serif",
+ "\"bssvpvnyyl\"" },
+{ "Verdana,serif",
+ "CE:" },
+{ "Verdana,serif",
+ "Ba" },
+{ "Verdana,serif",
+ "onggyrsvryq," },
+{ "Verdana,serif",
+ "fhofgvghgr" },
+{ "Verdana,serif",
+ "pbzzhavpngvba" },
+{ "Verdana,serif",
+ "betnavmngvba," },
+{ "Verdana,serif",
+ "\"Tybony" },
+{ "Verdana,serif",
+ "Bcrengvbaf\"" },
+{ "Verdana,serif",
+ "hgvyvmrf" },
+{ "Verdana,serif",
+ "havdhr" },
+{ "Verdana,serif",
+ "srngherf" },
+{ "Verdana,serif",
+ "rapbhentr" },
+{ "Verdana,serif",
+ "ryrzragf." },
+{ "Verdana,serif",
+ "Vagryyvtrapr" },
+{ "Verdana,serif",
+ "Bssvpre" },
+{ "Verdana,serif",
+ "ebyr" },
+{ "Verdana,serif",
+ "nyybjf" },
+{ "Verdana,serif",
+ "yrnqre" },
+{ "Verdana,serif",
+ "rlrf" },
+{ "Verdana,serif",
+ "rnef" },
+{ "Verdana,serif",
+ "ragver" },
+{ "Verdana,serif",
+ "tebhc." },
+{ "Verdana,serif",
+ "pbbeqvangrf" },
+{ "Verdana,serif",
+ "gebbcf" },
+{ "Verdana,serif",
+ "pbzovangvba" },
+{ "Verdana,serif",
+ "pnzrenf" },
+{ "Verdana,serif",
+ "gbc-qbja" },
+{ "Verdana,serif",
+ "znc" },
+{ "Verdana,serif",
+ "erpbtavmr" },
+{ "Verdana,serif",
+ "ubg" },
+{ "Verdana,serif",
+ "fcbgf," },
+{ "Verdana,serif",
+ "thvqr" },
+{ "Verdana,serif",
+ "bowrpgvirf," },
+{ "Verdana,serif",
+ "qverpg" },
+{ "Verdana,serif",
+ "ervasbeprzragf" },
+{ "Verdana,serif",
+ "ibvpr" },
+{ "Verdana,serif",
+ "VC" },
+{ "Verdana,serif",
+ "jnlcbvag" },
+{ "Verdana,serif",
+ "frggvat." },
+{ "Verdana,serif",
+ "Fhpprffshyyl" },
+{ "Verdana,serif",
+ "qverpgvat" },
+{ "Verdana,serif",
+ "orfg" },
+{ "Verdana,serif",
+ "nffvtazrag" },
+{ "Verdana,serif",
+ "punyyratvat" },
+{ "Verdana,serif",
+ "rnpu" },
+{ "Verdana,serif",
+ "fbyqvre" },
+{ "Verdana,serif",
+ "fcrpvnyvmrq" },
+{ "Verdana,serif",
+ "fxvyyf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OT2" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ ":" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Onq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Obar" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6:26" },
+{ "Verdana,serif",
+ "Ybbxf" },
+{ "Verdana,serif",
+ "fgernx" },
+{ "Verdana,serif",
+ "fgbccrq" },
+{ "Verdana,serif",
+ "Wbua" },
+{ "Verdana,serif",
+ "\"Jneevbe\"" },
+{ "Verdana,serif",
+ "Xrrsre" },
+{ "Verdana,serif",
+ "TnzrFcl" },
+{ "Verdana,serif",
+ "jebgr" },
+{ "Verdana,serif",
+ "yrg" },
+{ "Verdana,serif",
+ "\"Onq" },
+{ "Verdana,serif",
+ "Thl\"" },
+{ "Verdana,serif",
+ "va-ubhfr" },
+{ "Verdana,serif",
+ "Onyqhef" },
+{ "Verdana,serif",
+ "abj" },
+{ "Verdana,serif",
+ "hc." },
+{ "Verdana,serif",
+ "Pbzvat" },
+{ "Verdana,serif",
+ "yratgu," },
+{ "Verdana,serif",
+ "vg'yy" },
+{ "Verdana,serif",
+ "uryc" },
+{ "Verdana,serif",
+ "ohvyq" },
+{ "Verdana,serif",
+ "anfgl" },
+{ "Verdana,serif",
+ "rivy" },
+{ "Verdana,serif",
+ "erchgngvba" },
+{ "Verdana,serif",
+ "lbh'ir" },
+{ "Verdana,serif",
+ "qlvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Znwrfgl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "vagreivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:49" },
+{ "Verdana,serif",
+ "Jbj" },
+{ "Verdana,serif",
+ "gnyx" },
+{ "Verdana,serif",
+ "ohggybnq" },
+{ "Verdana,serif",
+ "zbeavat," },
+{ "Verdana,serif",
+ "ybbxf" },
+{ "Verdana,serif",
+ "Znwrfgl" },
+{ "Verdana,serif",
+ "Qentba" },
+{ "Verdana,serif",
+ "fng" },
+{ "Verdana,serif",
+ "\"gur" },
+{ "Verdana,serif",
+ "zna\"" },
+{ "Verdana,serif",
+ "Yrqbhk" },
+{ "Verdana,serif",
+ "snagnfl" },
+{ "Verdana,serif",
+ "nqq-ba." },
+{ "Verdana,serif",
+ "Znwrfgl" },
+{ "Verdana,serif",
+ "Qentba" },
+{ "Verdana,serif",
+ "Qvq" },
+{ "Verdana,serif",
+ "rawblnoyr," },
+{ "Verdana,serif",
+ "punyyratvat," },
+{ "Verdana,serif",
+ "rgp.?" },
+{ "Verdana,serif",
+ "Ner" },
+{ "Verdana,serif",
+ "unccl" },
+{ "Verdana,serif",
+ "ghearq" },
+{ "Verdana,serif",
+ "bhg?" },
+{ "Verdana,serif",
+ "Trbetr" },
+{ "Verdana,serif",
+ "Yrqbhk" },
+{ "Verdana,serif",
+ "Bu" },
+{ "Verdana,serif",
+ "lrnu!" },
+{ "Verdana,serif",
+ "qbar" },
+{ "Verdana,serif",
+ "pbzchgre" },
+{ "Verdana,serif",
+ "orsber" },
+{ "Verdana,serif",
+ "nyjnlf" },
+{ "Verdana,serif",
+ "oynfg" },
+{ "Verdana,serif",
+ "npg" },
+{ "Verdana,serif",
+ "\"bire-gur-gbc\"." },
+{ "Verdana,serif",
+ "qbvat" },
+{ "Verdana,serif",
+ "ibvprf" },
+{ "Verdana,serif",
+ "pnegbba;" },
+{ "Verdana,serif",
+ "gjvfg" },
+{ "Verdana,serif",
+ "fgergpu" },
+{ "Verdana,serif",
+ "punenpgre" },
+{ "Verdana,serif",
+ "fperra." },
+{ "Verdana,serif",
+ "Ohg" },
+{ "Verdana,serif",
+ "fbzrgvzrf" },
+{ "Verdana,serif",
+ "\"npg\"" },
+{ "Verdana,serif",
+ "pbairl" },
+{ "Verdana,serif",
+ "rzbgvba" },
+{ "Verdana,serif",
+ "ibvpr." },
+{ "Verdana,serif",
+ "Crefbanyyl," },
+{ "Verdana,serif",
+ "cebwrpg" },
+{ "Verdana,serif",
+ "terng!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "jvgu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "na" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nzrevpna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:35" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(8)" },
+{ "Verdana,serif",
+ "Nzrepvna" },
+{ "Verdana,serif",
+ "ZpTrr" },
+{ "Verdana,serif",
+ "juvyr" },
+{ "Verdana,serif",
+ "birefrnf" },
+{ "Verdana,serif",
+ "cebzbf" },
+{ "Verdana,serif",
+ "uvf" },
+{ "Verdana,serif",
+ "gvgyr," },
+{ "Verdana,serif",
+ "Nyvpr." },
+{ "Verdana,serif",
+ "pbhcyr" },
+{ "Verdana,serif",
+ "rneyl" },
+{ "Verdana,serif",
+ "zbeava'" },
+{ "Verdana,serif",
+ "pyvccrgf" },
+{ "Verdana,serif",
+ "vagreivrj:" },
+{ "Verdana,serif",
+ "HX" },
+{ "Verdana,serif",
+ "Lbh'ir" },
+{ "Verdana,serif",
+ "nibvqrq" },
+{ "Verdana,serif",
+ "vagragvbany?" },
+{ "Verdana,serif",
+ "Nzrevpna" },
+{ "Verdana,serif",
+ "ZpTrr" },
+{ "Verdana,serif",
+ "nqqrq" },
+{ "Verdana,serif",
+ "nalguvat." },
+{ "Verdana,serif",
+ "Zhygvcynlre" },
+{ "Verdana,serif",
+ "zhygvcynlre-bayl" },
+{ "Verdana,serif",
+ "tnzrf," },
+{ "Verdana,serif",
+ "znxvat" },
+{ "Verdana,serif",
+ "bhef" },
+{ "Verdana,serif",
+ "fvatyr-cynlre" },
+{ "Verdana,serif",
+ "tnir" },
+{ "Verdana,serif",
+ "jbeel" },
+{ "Verdana,serif",
+ "jnfgvat" },
+{ "Verdana,serif",
+ "raretl" },
+{ "Verdana,serif",
+ "cvrpr" },
+{ "Verdana,serif",
+ "jnfa'g" },
+{ "Verdana,serif",
+ "rffragvny" },
+{ "Verdana,serif",
+ "gryyvat" },
+{ "Verdana,serif",
+ "raq" },
+{ "Verdana,serif",
+ "qnl," },
+{ "Verdana,serif",
+ "gelvat" },
+{ "Verdana,serif",
+ "nppbzcyvfu" },
+{ "Verdana,serif",
+ "gryy" },
+{ "Verdana,serif",
+ "tbbq" },
+{ "Verdana,serif",
+ "Fb," },
+{ "Verdana,serif",
+ "jung'f" },
+{ "Verdana,serif",
+ "snibhevgr" },
+{ "Verdana,serif",
+ "Nyvpr?" },
+{ "Verdana,serif",
+ "Purfuver" },
+{ "Verdana,serif",
+ "Png" },
+{ "Verdana,serif",
+ "qnza" },
+{ "Verdana,serif",
+ "pbby," },
+{ "Verdana,serif",
+ "Nyvpr" },
+{ "Verdana,serif",
+ "ure" },
+{ "Verdana,serif",
+ "ybg," },
+{ "Verdana,serif",
+ "fur'f" },
+{ "Verdana,serif",
+ "arng" },
+{ "Verdana,serif",
+ "tvey." },
+{ "Verdana,serif",
+ "crbcyr" },
+{ "Verdana,serif",
+ "snyyra" },
+{ "Verdana,serif",
+ "ybir" },
+{ "Verdana,serif",
+ "Nyvpr," },
+{ "Verdana,serif",
+ "jul" },
+{ "Verdana,serif",
+ "nccrnyvat." },
+{ "Verdana,serif",
+ "sne" },
+{ "Verdana,serif",
+ "vairagrq" },
+{ "Verdana,serif",
+ "tb," },
+{ "Verdana,serif",
+ "pnyyrq" },
+{ "Verdana,serif",
+ "Vafnar" },
+{ "Verdana,serif",
+ "Puvyqera," },
+{ "Verdana,serif",
+ "ercerfrag" },
+{ "Verdana,serif",
+ "unccra" },
+{ "Verdana,serif",
+ "ybfg" },
+{ "Verdana,serif",
+ "onggyr," },
+{ "Verdana,serif",
+ "cerggl" },
+{ "Verdana,serif",
+ "shaal." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nanepul" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pung" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:13" },
+{ "Verdana,serif",
+ "Nanepul" },
+{ "Verdana,serif",
+ "Bayvar" },
+{ "Verdana,serif",
+ "Fgengvpf" },
+{ "Verdana,serif",
+ "ubfgvat" },
+{ "Verdana,serif",
+ "zbqrengrq" },
+{ "Verdana,serif",
+ "frffvba" },
+{ "Verdana,serif",
+ "pheeragyl" },
+{ "Verdana,serif",
+ "ZZBECT" },
+{ "Verdana,serif",
+ "ebbz" },
+{ "Verdana,serif",
+ "#nbubp" },
+{ "Verdana,serif",
+ "Fhaqnl," },
+{ "Verdana,serif",
+ "Qrprzore" },
+{ "Verdana,serif",
+ "17gu," },
+{ "Verdana,serif",
+ "2cz" },
+{ "Verdana,serif",
+ "PFG" },
+{ "Verdana,serif",
+ "freiref" },
+{ "Verdana,serif",
+ "orybj" },
+{ "Verdana,serif",
+ "vep.fgengvpf.pbz" },
+{ "Verdana,serif",
+ "pbzch.fgengvpf.pbz" },
+{ "Verdana,serif",
+ "quc.fgengvpf.pbz" },
+{ "Verdana,serif",
+ "sejf.fgengvpf.pbz" },
+{ "Verdana,serif",
+ "vagjbexf.fgengvpf.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pebgrnz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(5)" },
+{ "Verdana,serif",
+ "Gubfr" },
+{ "Verdana,serif",
+ "jnpxl" },
+{ "Verdana,serif",
+ "onfgneqf" },
+{ "Verdana,serif",
+ "Fgbzcrq" },
+{ "Verdana,serif",
+ "Ebzna" },
+{ "Verdana,serif",
+ "Evonevp" },
+{ "Verdana,serif",
+ "Pebgrnz" },
+{ "Verdana,serif",
+ "ubzrlf" },
+{ "Verdana,serif",
+ "Frevbhf" },
+{ "Verdana,serif",
+ "Fnz" },
+{ "Verdana,serif",
+ "Nygubhtu" },
+{ "Verdana,serif",
+ "rknpgyl" },
+{ "Verdana,serif",
+ "eryngrq," },
+{ "Verdana,serif",
+ "qbrf" },
+{ "Verdana,serif",
+ "funer" },
+{ "Verdana,serif",
+ "bcvavbaf" },
+{ "Verdana,serif",
+ "tnzvat" },
+{ "Verdana,serif",
+ "vafvtug" },
+{ "Verdana,serif",
+ "shgher." },
+{ "Verdana,serif",
+ "rkgen" },
+{ "Verdana,serif",
+ "gnfgl" },
+{ "Verdana,serif",
+ "zbefry" },
+{ "Verdana,serif",
+ "ln" },
+{ "Verdana,serif",
+ "Fgbzcrq" },
+{ "Verdana,serif",
+ "Jung," },
+{ "Verdana,serif",
+ "bcvavba," },
+{ "Verdana,serif",
+ "fvatyr" },
+{ "Verdana,serif",
+ "rapbhentvat" },
+{ "Verdana,serif",
+ "geraq" },
+{ "Verdana,serif",
+ "nccrnerq" },
+{ "Verdana,serif",
+ "jul?" },
+{ "Verdana,serif",
+ "Ebzna" },
+{ "Verdana,serif",
+ "Evonevp" },
+{ "Verdana,serif",
+ "Pbzchgre" },
+{ "Verdana,serif",
+ "pbafbyr" },
+{ "Verdana,serif",
+ "fgnegvat" },
+{ "Verdana,serif",
+ "pbzr" },
+{ "Verdana,serif",
+ "pybfre" },
+{ "Verdana,serif",
+ "bgure." },
+{ "Verdana,serif",
+ "Zbfg" },
+{ "Verdana,serif",
+ "qrfver" },
+{ "Verdana,serif",
+ "qrirybc" },
+{ "Verdana,serif",
+ "pbafbyrf," },
+{ "Verdana,serif",
+ "cevznevyl" },
+{ "Verdana,serif",
+ "cyngsbez" },
+{ "Verdana,serif",
+ "cvenpl" },
+{ "Verdana,serif",
+ "zhpu" },
+{ "Verdana,serif",
+ "ybjre" },
+{ "Verdana,serif",
+ "Ohg," },
+{ "Verdana,serif",
+ "erfgevpgvbaf" },
+{ "Verdana,serif",
+ "perngbef," },
+{ "Verdana,serif",
+ "vaqrcraqrag" },
+{ "Verdana,serif",
+ "npghnyyl" },
+{ "Verdana,serif",
+ "Nyfb," },
+{ "Verdana,serif",
+ "gbbyf" },
+{ "Verdana,serif",
+ "njshy" },
+{ "Verdana,serif",
+ "hfr" },
+{ "Verdana,serif",
+ "qrirybczrag." },
+{ "Verdana,serif",
+ "Fbal" },
+{ "Verdana,serif",
+ "Zvpebfbsg" },
+{ "Verdana,serif",
+ "ragrevat" },
+{ "Verdana,serif",
+ "znexrg," },
+{ "Verdana,serif",
+ "gbgnyyl" },
+{ "Verdana,serif",
+ "fbat." },
+{ "Verdana,serif",
+ "Lbh" },
+{ "Verdana,serif",
+ "CP" },
+{ "Verdana,serif",
+ "CPf" },
+{ "Verdana,serif",
+ "pbafbyrf." },
+{ "Verdana,serif",
+ "Pbfg" },
+{ "Verdana,serif",
+ "cebqhpvat" },
+{ "Verdana,serif",
+ "cyngsbezf" },
+{ "Verdana,serif",
+ "Ng" },
+{ "Verdana,serif",
+ "yrnfg" },
+{ "Verdana,serif",
+ "gurbel," },
+{ "Verdana,serif",
+ "naljnl" },
+{ "Verdana,serif",
+ ":)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "K-Jvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OP" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnatonat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:49" },
+{ "Verdana,serif",
+ "gheaf" },
+{ "Verdana,serif",
+ "oblf" },
+{ "Verdana,serif",
+ "K-jvat:" },
+{ "Verdana,serif",
+ "Yrtnpl" },
+{ "Verdana,serif",
+ "hfrq" },
+{ "Verdana,serif",
+ "sbepr\"" },
+{ "Verdana,serif",
+ "(naq" },
+{ "Verdana,serif",
+ "yhovat)" },
+{ "Verdana,serif",
+ "fpberq" },
+{ "Verdana,serif",
+ "fvz" },
+{ "Verdana,serif",
+ "(Gurl" },
+{ "Verdana,serif",
+ "urycrq" },
+{ "Verdana,serif",
+ "K-Jvat" },
+{ "Verdana,serif",
+ "qnl)." },
+{ "Verdana,serif",
+ "ortva" },
+{ "Verdana,serif",
+ "nebhaq" },
+{ "Verdana,serif",
+ "Fngheqnl," },
+{ "Verdana,serif",
+ "16gu," },
+{ "Verdana,serif",
+ "10-11nz" },
+{ "Verdana,serif",
+ "CFG." },
+{ "Verdana,serif",
+ "vep" },
+{ "Verdana,serif",
+ "freire" },
+{ "Verdana,serif",
+ "vep.ubybarg.bet" },
+{ "Verdana,serif",
+ "punaary" },
+{ "Verdana,serif",
+ "#oevqtrpbzznaqre" },
+{ "Verdana,serif",
+ "fdhner." },
+{ "Verdana,serif",
+ "Uvg" },
+{ "Verdana,serif",
+ "OP." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gebcvpb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:32" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ghea-Onfrq" },
+{ "Verdana,serif",
+ "Znex" },
+{ "Verdana,serif",
+ "Nfpure" },
+{ "Verdana,serif",
+ "Dhnegre" },
+{ "Verdana,serif",
+ "Guerr" },
+{ "Verdana,serif",
+ "qebccrq" },
+{ "Verdana,serif",
+ "yvar" },
+{ "Verdana,serif",
+ "Cuvy" },
+{ "Verdana,serif",
+ "Fgrvazrlre" },
+{ "Verdana,serif",
+ "Cbc" },
+{ "Verdana,serif",
+ "Gbc" },
+{ "Verdana,serif",
+ "Fbsgjner" },
+{ "Verdana,serif",
+ "(Envyebnq" },
+{ "Verdana,serif",
+ "Glpbba" },
+{ "Verdana,serif",
+ "Nqq-Ba" },
+{ "Verdana,serif",
+ "Qrirybcref)" },
+{ "Verdana,serif",
+ "Whfg" },
+{ "Verdana,serif",
+ "vapnfr" },
+{ "Verdana,serif",
+ "nff" },
+{ "Verdana,serif",
+ "Xabj\"," },
+{ "Verdana,serif",
+ "tveyvrf" },
+{ "Verdana,serif",
+ "pnfgeb-vfu" },
+{ "Verdana,serif",
+ "fvzhyngbe," },
+{ "Verdana,serif",
+ "Gebcvpb." },
+{ "Verdana,serif",
+ "DG3" },
+{ "Verdana,serif",
+ "avtugpyho" },
+{ "Verdana,serif",
+ "jurer" },
+{ "Verdana,serif",
+ "Evpxl" },
+{ "Verdana,serif",
+ "Znegva" },
+{ "Verdana,serif",
+ "cresbezf?" },
+{ "Verdana,serif",
+ "Cuvy" },
+{ "Verdana,serif",
+ "Fgrvazrlre" },
+{ "Verdana,serif",
+ "Gebcvpb" },
+{ "Verdana,serif",
+ "Pnfgeb-vfu" },
+{ "Verdana,serif",
+ "svther" },
+{ "Verdana,serif",
+ "ehaavat" },
+{ "Verdana,serif",
+ "Pnevoorna" },
+{ "Verdana,serif",
+ "vfynaq," },
+{ "Verdana,serif",
+ "wnvyvat" },
+{ "Verdana,serif",
+ "bccbaragf," },
+{ "Verdana,serif",
+ "bccerffvat" },
+{ "Verdana,serif",
+ "crbcyr," },
+{ "Verdana,serif",
+ "Fjvff" },
+{ "Verdana,serif",
+ "onax" },
+{ "Verdana,serif",
+ "nppbhag," },
+{ "Verdana,serif",
+ "pnfr" },
+{ "Verdana,serif",
+ "or." },
+{ "Verdana,serif",
+ "lbh'er" },
+{ "Verdana,serif",
+ "pbashfvat" },
+{ "Verdana,serif",
+ "2," },
+{ "Verdana,serif",
+ "frdhry," },
+{ "Verdana,serif",
+ "Zrahqb-vfu" },
+{ "Verdana,serif",
+ "Fnyfn" },
+{ "Verdana,serif",
+ "onaq," },
+{ "Verdana,serif",
+ "funxr" },
+{ "Verdana,serif",
+ "oba-oba," },
+{ "Verdana,serif",
+ "vzcerff" },
+{ "Verdana,serif",
+ "13-lrne" },
+{ "Verdana,serif",
+ "byq" },
+{ "Verdana,serif",
+ "tebhcvrf," },
+{ "Verdana,serif",
+ "fpber" },
+{ "Verdana,serif",
+ "frpbaq" },
+{ "Verdana,serif",
+ "erivir" },
+{ "Verdana,serif",
+ "synttvat" },
+{ "Verdana,serif",
+ "sbegharf." },
+{ "Verdana,serif",
+ "tenoorq" },
+{ "Verdana,serif",
+ "Envyebnq" },
+{ "Verdana,serif",
+ "Zvpebcebfr," },
+{ "Verdana,serif",
+ "fbzrjung" },
+{ "Verdana,serif",
+ "Fvq" },
+{ "Verdana,serif",
+ "Zrvre'f" },
+{ "Verdana,serif",
+ "fhecevfr." },
+{ "Verdana,serif",
+ "Jvyy" },
+{ "Verdana,serif",
+ "qvabfnhef" },
+{ "Verdana,serif",
+ "Gebcvpb?" },
+{ "Verdana,serif",
+ "Whenffvp" },
+{ "Verdana,serif",
+ "Cnex" },
+{ "Verdana,serif",
+ "erzbgr" },
+{ "Verdana,serif",
+ "vfynaqf" },
+{ "Verdana,serif",
+ "Pnevoorna." },
+{ "Verdana,serif",
+ "yrnqref" },
+{ "Verdana,serif",
+ "qenj" },
+{ "Verdana,serif",
+ "Nzrevpna" },
+{ "Verdana,serif",
+ "gbhevfgf" },
+{ "Verdana,serif",
+ "gurl'er" },
+{ "Verdana,serif",
+ "ursgl" },
+{ "Verdana,serif",
+ "jnyyrgf." },
+{ "Verdana,serif",
+ "Obgu" },
+{ "Verdana,serif",
+ "pyvznk" },
+{ "Verdana,serif",
+ "rirelobql" },
+{ "Verdana,serif",
+ "rngra" },
+{ "Verdana,serif",
+ "ol..." },
+{ "Verdana,serif",
+ "jubbcf" },
+{ "Verdana,serif",
+ "fnvq" },
+{ "Verdana,serif",
+ "zhpu," },
+{ "Verdana,serif",
+ "ohl" },
+{ "Verdana,serif",
+ "lbhefrys." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnivq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Creel" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12:16" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(10)" },
+{ "Verdana,serif",
+ "zna," },
+{ "Verdana,serif",
+ "Qnivq" },
+{ "Verdana,serif",
+ "Creel" },
+{ "Verdana,serif",
+ ". " },
+{ "Verdana,serif",
+ "oevrs" },
+{ "Verdana,serif",
+ "D&N," },
+{ "Verdana,serif",
+ "jurgure" },
+{ "Verdana,serif",
+ "Fuval" },
+{ "Verdana,serif",
+ "fjvgpu" },
+{ "Verdana,serif",
+ "trnef" },
+{ "Verdana,serif",
+ "fvqr-fpebyyre" },
+{ "Verdana,serif",
+ "guveq-crefba" },
+{ "Verdana,serif",
+ "glcr" },
+{ "Verdana,serif",
+ "nqq-ba" },
+{ "Verdana,serif",
+ "Fnpevsvpr," },
+{ "Verdana,serif",
+ "jrag" },
+{ "Verdana,serif",
+ "ynprq" },
+{ "Verdana,serif",
+ "ohtf" },
+{ "Verdana,serif",
+ "(Zrffvnu," },
+{ "Verdana,serif",
+ "obk)" },
+{ "Verdana,serif",
+ "qevire" },
+{ "Verdana,serif",
+ "fcrpvsvp). " },
+{ "Verdana,serif",
+ "zbbq" },
+{ "Verdana,serif",
+ "ena" },
+{ "Verdana,serif",
+ "zbgvba" },
+{ "Verdana,serif",
+ "ybgvba):" },
+{ "Verdana,serif",
+ "IbbqbbRkgerzr" },
+{ "Verdana,serif",
+ "--" },
+{ "Verdana,serif",
+ "Nal" },
+{ "Verdana,serif",
+ "jbeq" },
+{ "Verdana,serif",
+ "na" },
+{ "Verdana,serif",
+ "nqq-ba" },
+{ "Verdana,serif",
+ "Fnpevsvpr?" },
+{ "Verdana,serif",
+ "QC" },
+{ "Verdana,serif",
+ "gnyxvat" },
+{ "Verdana,serif",
+ "evtug" },
+{ "Verdana,serif",
+ "abj..." },
+{ "Verdana,serif",
+ "Kznf" },
+{ "Verdana,serif",
+ "frevbhf" },
+{ "Verdana,serif",
+ "qvfphffvba" },
+{ "Verdana,serif",
+ "qverpgvba" },
+{ "Verdana,serif",
+ "Fnpevsvpr." },
+{ "Verdana,serif",
+ "RCVP" },
+{ "Verdana,serif",
+ "fgbel," },
+{ "Verdana,serif",
+ "rnfl" },
+{ "Verdana,serif",
+ "pbagvahr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZpTrr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:59" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nqiragher" },
+{ "Verdana,serif",
+ "Rheb" },
+{ "Verdana,serif",
+ "zrtn-fvgr" },
+{ "Verdana,serif",
+ "TnzrYbsg" },
+{ "Verdana,serif",
+ "vagreivrjrq" },
+{ "Verdana,serif",
+ "Ebthr" },
+{ "Verdana,serif",
+ "Ragregnvazrag'f" },
+{ "Verdana,serif",
+ "ratvarq" },
+{ "Verdana,serif",
+ "nqiragher" },
+{ "verdana,serif",
+ "TY:" },
+{ "verdana,serif",
+ "Ba" },
+{ "verdana,serif",
+ "gb" },
+{ "verdana,serif",
+ "gur" },
+{ "verdana,serif",
+ "tnzr" },
+{ "verdana,serif",
+ "vgfrys;" },
+{ "verdana,serif",
+ "jurer" },
+{ "verdana,serif",
+ "qb" },
+{ "verdana,serif",
+ "lbh" },
+{ "verdana,serif",
+ "trg" },
+{ "verdana,serif",
+ "vqrn" },
+{ "verdana,serif",
+ "znxr" },
+{ "verdana,serif",
+ "n" },
+{ "verdana,serif",
+ "yvxr" },
+{ "verdana,serif",
+ "guvf?" },
+{ "verdana,serif",
+ "Jung" },
+{ "verdana,serif",
+ "znxrf" },
+{ "verdana,serif",
+ "fvg" },
+{ "verdana,serif",
+ "hc" },
+{ "verdana,serif",
+ "fbzr" },
+{ "verdana,serif",
+ "qnl" },
+{ "verdana,serif",
+ "naq" },
+{ "verdana,serif",
+ "fnl," },
+{ "verdana,serif",
+ "\"V'z" },
+{ "verdana,serif",
+ "tbvat" },
+{ "verdana,serif",
+ "nobhg" },
+{ "verdana,serif",
+ "Nyvpr" },
+{ "verdana,serif",
+ "va" },
+{ "verdana,serif",
+ "Jbaqreynaq\"?" },
+{ "Verdana,serif",
+ "fhccbfr" },
+{ "Verdana,serif",
+ "sehfgengvba" },
+{ "Verdana,serif",
+ "gurer;" },
+{ "Verdana,serif",
+ "grezf" },
+{ "Verdana,serif",
+ "perngvir" },
+{ "Verdana,serif",
+ "fvqr" },
+{ "Verdana,serif",
+ "fgbelyvar" },
+{ "Verdana,serif",
+ "raivebazragf." },
+{ "Verdana,serif",
+ "ahzore" },
+{ "Verdana,serif",
+ "vqrnf," },
+{ "Verdana,serif",
+ "vafcverq" },
+{ "Verdana,serif",
+ "Nyvpr;" },
+{ "Verdana,serif",
+ "rcvcunal," },
+{ "Verdana,serif",
+ "thrff," },
+{ "Verdana,serif",
+ "fbat" },
+{ "Verdana,serif",
+ "ntr" },
+{ "Verdana,serif",
+ "jbaqref," },
+{ "Verdana,serif",
+ "jubyr" },
+{ "Verdana,serif",
+ "\"jbaqre\"" },
+{ "Verdana,serif",
+ "fghpx" },
+{ "Verdana,serif",
+ "gubhtug" },
+{ "Verdana,serif",
+ "jbaqreynaq," },
+{ "Verdana,serif",
+ "xarj" },
+{ "Verdana,serif",
+ "vzzrqvngryl" },
+{ "Verdana,serif",
+ "qb." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qrhf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rk" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ercbeg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:54" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(14)" },
+{ "Verdana,serif",
+ "VBA" },
+{ "Verdana,serif",
+ "Fgbez'f" },
+{ "Verdana,serif",
+ "Jneera" },
+{ "Verdana,serif",
+ "Fcrpgbe" },
+{ "Verdana,serif",
+ "Qrhf" },
+{ "Verdana,serif",
+ "Rk" },
+{ "Verdana,serif",
+ "cebterffvat," },
+{ "Verdana,serif",
+ "ovm:" },
+{ "Verdana,serif",
+ "Jr'er" },
+{ "Verdana,serif",
+ "fbba" },
+{ "Verdana,serif",
+ "grfgvat" },
+{ "Verdana,serif",
+ "Vg'yy" },
+{ "Verdana,serif",
+ "fznyyre" },
+{ "Verdana,serif",
+ "cngpu." },
+{ "Verdana,serif",
+ "ZHPU" },
+{ "Verdana,serif",
+ "fznyyre." },
+{ "Verdana,serif",
+ "ZC" },
+{ "Verdana,serif",
+ "cbffvoyr" },
+{ "Verdana,serif",
+ "jr'q" },
+{ "Verdana,serif",
+ "cngpu..." },
+{ "Verdana,serif",
+ "(OGJ" },
+{ "Verdana,serif",
+ "vapyhqrf" },
+{ "Verdana,serif",
+ "rirelguvat" },
+{ "Verdana,serif",
+ "cyna" },
+{ "Verdana,serif",
+ "FC" },
+{ "Verdana,serif",
+ "obgure" },
+{ "Verdana,serif",
+ "obgu," },
+{ "Verdana,serif",
+ "irefvbaf!)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "UFE" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "I4/5..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:25" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3qsk" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(48)" },
+{ "Verdana,serif",
+ "tveyf" },
+{ "Verdana,serif",
+ "NanaqGrpu" },
+{ "Verdana,serif",
+ "erivrjrq" },
+{ "Verdana,serif",
+ "3qsk" },
+{ "Verdana,serif",
+ "qeviref" },
+{ "Verdana,serif",
+ "uvqqra" },
+{ "Verdana,serif",
+ "fhesnpr" },
+{ "Verdana,serif",
+ "erzbiny" },
+{ "Verdana,serif",
+ "Ibbqbb4/5" },
+{ "Verdana,serif",
+ "obneqf," },
+{ "Verdana,serif",
+ "orapuznexf" },
+{ "Verdana,serif",
+ "n'cyragl" },
+{ "Verdana,serif",
+ "cebir" },
+{ "Verdana,serif",
+ "cbvag." },
+{ "Verdana,serif",
+ "ernq" },
+{ "Verdana,serif",
+ "erfhygf" },
+{ "Verdana,serif",
+ "urer" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bcgvzvmvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jbex-Fcnpr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:57" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "CP" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arjf" },
+{ "Verdana,serif",
+ "Bhe" },
+{ "Verdana,serif",
+ "cny" },
+{ "Verdana,serif",
+ "Wrerzl" },
+{ "Verdana,serif",
+ "\"Ovaxl-Qvaxhzf\"" },
+{ "Verdana,serif",
+ "Nyysbeq," },
+{ "Verdana,serif",
+ "sbezre" },
+{ "Verdana,serif",
+ "NTA3Q" },
+{ "Verdana,serif",
+ "fghq" },
+{ "Verdana,serif",
+ "TnzrefQrcbg" },
+{ "Verdana,serif",
+ "ur" },
+{ "Verdana,serif",
+ "pyrna" },
+{ "Verdana,serif",
+ "eng'f" },
+{ "Verdana,serif",
+ "arfg" },
+{ "Verdana,serif",
+ "pnyy" },
+{ "Verdana,serif",
+ "jbex-fcnpr;" },
+{ "Verdana,serif",
+ "pnoyrf, uneqjner" },
+{ "Verdana,serif",
+ "penc naq" },
+{ "Verdana,serif",
+ "nyy." },
+{ "Verdana,serif",
+ "Ovaxl," },
+{ "Verdana,serif",
+ "qvqa'g" },
+{ "Verdana,serif",
+ "qvntabfrq" },
+{ "Verdana,serif",
+ "grezvany" },
+{ "Verdana,serif",
+ "oenva" },
+{ "Verdana,serif",
+ "ghzbe" },
+{ "Verdana,serif",
+ "ybat" },
+{ "Verdana,serif",
+ "ntb," },
+{ "Verdana,serif",
+ "orngra" },
+{ "Verdana,serif",
+ "bqqf" },
+{ "Verdana,serif",
+ "nyvir" },
+{ "Verdana,serif",
+ "xvpxvat" },
+{ "Verdana,serif",
+ "pbagrag." },
+{ "Verdana,serif",
+ "Nalubj," },
+{ "Verdana,serif",
+ "V'z" },
+{ "Verdana,serif",
+ "cebhq" },
+{ "Verdana,serif",
+ "zna;" },
+{ "Verdana,serif",
+ "ureb." },
+{ "Verdana,serif",
+ "shyy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qentba'f" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ynve" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3Q" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:46" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(11)" },
+{ "Verdana,serif",
+ "Nuuuuu," },
+{ "Verdana,serif",
+ "Qentba'f" },
+{ "Verdana,serif",
+ "Ynve," },
+{ "Verdana,serif",
+ "zrzbevrf" },
+{ "Verdana,serif",
+ "(bs" },
+{ "Verdana,serif",
+ "pbhefr," },
+{ "Verdana,serif",
+ "hz," },
+{ "Verdana,serif",
+ "vg...V" },
+{ "Verdana,serif",
+ "vg...lrnu," },
+{ "Verdana,serif",
+ "gvpxrg)..." },
+{ "Verdana,serif",
+ "3QNC" },
+{ "Verdana,serif",
+ "cbccrq" },
+{ "Verdana,serif",
+ "Qentbafgbar'f" },
+{ "Verdana,serif",
+ "Fgrira" },
+{ "Verdana,serif",
+ "Cnefbaf" },
+{ "Verdana,serif",
+ "erznxr" },
+{ "Verdana,serif",
+ "(jryy," },
+{ "Verdana,serif",
+ "fbegf," },
+{ "Verdana,serif",
+ "ernq)" },
+{ "Verdana,serif",
+ "nepnqr" },
+{ "Verdana,serif",
+ "pynffvp," },
+{ "Verdana,serif",
+ "Ynve" },
+{ "Verdana,serif",
+ "avooyr" },
+{ "Verdana,serif",
+ "(jung" },
+{ "Verdana,serif",
+ "\"bs" },
+{ "Verdana,serif",
+ "fbegf\"," },
+{ "Verdana,serif",
+ "fbzr):" },
+{ "Verdana,serif",
+ "3QNpgvbaCynarg:" },
+{ "Verdana,serif",
+ "fnj" },
+{ "Verdana,serif",
+ "\"pnegbbal\"" },
+{ "Verdana,serif",
+ "ybbx," },
+{ "Verdana,serif",
+ "pryy" },
+{ "Verdana,serif",
+ "navzngvbaf." },
+{ "Verdana,serif",
+ "Qbrfa'g" },
+{ "Verdana,serif",
+ "fbeg" },
+{ "Verdana,serif",
+ "pbasyvpg" },
+{ "Verdana,serif",
+ "oevatvat" },
+{ "Verdana,serif",
+ "zbqrea" },
+{ "Verdana,serif",
+ "grpuabybtl?" },
+{ "Verdana,serif",
+ "Fgrira" },
+{ "Verdana,serif",
+ "Cnefbaf:" },
+{ "Verdana,serif",
+ "navzngvba" },
+{ "Verdana,serif",
+ "gehr" },
+{ "Verdana,serif",
+ "bevtvany," },
+{ "Verdana,serif",
+ "sryg" },
+{ "Verdana,serif",
+ "pnegbba" },
+{ "Verdana,serif",
+ "raivebazrag." },
+{ "Verdana,serif",
+ "jr'er" },
+{ "Verdana,serif",
+ "pncghevat" },
+{ "Verdana,serif",
+ "Qvex" },
+{ "Verdana,serif",
+ "Qnevat" },
+{ "Verdana,serif",
+ "pnfg" },
+{ "Verdana,serif",
+ "cbyltbany" },
+{ "Verdana,serif",
+ "pnegbbavfu" },
+{ "Verdana,serif",
+ "nccylvat" },
+{ "Verdana,serif",
+ "\"gbba" },
+{ "Verdana,serif",
+ "funqvat\". " },
+{ "Verdana,serif",
+ "Nabgure" },
+{ "Verdana,serif",
+ "zrgubq" },
+{ "Verdana,serif",
+ "jr'ir" },
+{ "Verdana,serif",
+ "pncgher" },
+{ "Verdana,serif",
+ "pbybe" },
+{ "Verdana,serif",
+ "uvtuyvtugf" },
+{ "Verdana,serif",
+ "yvtugvat" },
+{ "Verdana,serif",
+ "raivebazrag," },
+{ "Verdana,serif",
+ "qhcyvpngvat" },
+{ "Verdana,serif",
+ "Ynve." },
+{ "Verdana,serif",
+ "qvssreraprf" },
+{ "Verdana,serif",
+ "2Q" },
+{ "Verdana,serif",
+ "grpuavdhrf" },
+{ "Verdana,serif",
+ "vzcebir" },
+{ "Verdana,serif",
+ "tvir" },
+{ "Verdana,serif",
+ "vzzrqvngr" },
+{ "Verdana,serif",
+ "snzvyvnevgl" },
+{ "Verdana,serif",
+ "raivebazrag" },
+{ "Verdana,serif",
+ "erzrzore," },
+{ "Verdana,serif",
+ "bssrevat" },
+{ "Verdana,serif",
+ "snagnfgvp" },
+{ "Verdana,serif",
+ "tnzrcynl" },
+{ "Verdana,serif",
+ "punyyratrf" },
+{ "Verdana,serif",
+ "rawblzrag!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yhovatf..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:30" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvfpryynarbhf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Yhovatf" },
+{ "Verdana,serif",
+ "Zvfp." },
+{ "Verdana,serif",
+ "ahttrgf" },
+{ "Verdana,serif",
+ "penc" },
+{ "Verdana,serif",
+ "\"jbegul\"" },
+{ "Verdana,serif",
+ "cbfg..." },
+{ "Verdana,serif",
+ "Nguyba" },
+{ "Verdana,serif",
+ "if." },
+{ "Verdana,serif",
+ "Qheba" },
+{ "Verdana,serif",
+ "pngsvtug" },
+{ "Verdana,serif",
+ ". " },
+{ "Verdana,serif",
+ "Bx," },
+{ "Verdana,serif",
+ "Nguyba" },
+{ "Verdana,serif",
+ "ernpurf" },
+{ "Verdana,serif",
+ "1TUm" },
+{ "Verdana,serif",
+ "znex," },
+{ "Verdana,serif",
+ "Qhebaf" },
+{ "Verdana,serif",
+ "ohggpurnc," },
+{ "Verdana,serif",
+ "fubhyq" },
+{ "Verdana,serif",
+ "tb" },
+{ "Verdana,serif",
+ "(nf," },
+{ "Verdana,serif",
+ "pnfu" },
+{ "Verdana,serif",
+ "fnirq," },
+{ "Verdana,serif",
+ "pbhyq" },
+{ "Verdana,serif",
+ "zrzbel" },
+{ "Verdana,serif",
+ "fhpu" },
+{ "Verdana,serif",
+ "Qheba)...? " },
+{ "Verdana,serif",
+ "Grpu-Ercbeg" },
+{ "Verdana,serif",
+ "nafjre" },
+{ "Verdana,serif",
+ "\"Nguyba" },
+{ "Verdana,serif",
+ "if." },
+{ "Verdana,serif",
+ "Qheba" },
+{ "Verdana,serif",
+ "pngsvtug\"" },
+{ "Verdana,serif",
+ "Uryy," },
+{ "Verdana,serif",
+ "1.2TUm" },
+{ "Verdana,serif",
+ "Nguybaf" },
+{ "Verdana,serif",
+ "purnc" },
+{ "Verdana,serif",
+ "qnlf," },
+{ "Verdana,serif",
+ "uneq" },
+{ "Verdana,serif",
+ "_abg_" },
+{ "Verdana,serif",
+ "fhttrfg" },
+{ "Verdana,serif",
+ "qebccvat" },
+{ "Verdana,serif",
+ "ybnq" },
+{ "Verdana,serif",
+ "onq" },
+{ "Verdana,serif",
+ "oblf." },
+{ "Verdana,serif",
+ "Jvanzc" },
+{ "Verdana,serif",
+ "i2.71" },
+{ "Verdana,serif",
+ "Lrnu," },
+{ "Verdana,serif",
+ "lrfgreqnl," },
+{ "Verdana,serif",
+ "rznvyf" },
+{ "Verdana,serif",
+ "zbeavat" },
+{ "Verdana,serif",
+ "ohevrq" },
+{ "Verdana,serif",
+ "bgure" },
+{ "Verdana,serif",
+ "yhorf" },
+{ "Verdana,serif",
+ "nsgreabba. " },
+{ "Verdana,serif",
+ "Lrc," },
+{ "Verdana,serif",
+ "Ahyyfbsg" },
+{ "Verdana,serif",
+ "Jvanzc" },
+{ "Verdana,serif",
+ "i2.71" },
+{ "Verdana,serif",
+ "vf," },
+{ "Verdana,serif",
+ "cyrnfr" },
+{ "Verdana,serif",
+ "onpxlneq" },
+{ "Verdana,serif",
+ "guebj" },
+{ "Verdana,serif",
+ "lbhefrys" },
+{ "Verdana,serif",
+ "orngvat." },
+{ "Verdana,serif",
+ "Nycun" },
+{ "Verdana,serif",
+ "RI8" },
+{ "Verdana,serif",
+ "cebprffbe" },
+{ "Verdana,serif",
+ "negvpyr" },
+{ "Verdana,serif",
+ "ErnyJbeyqGrpu.pbz" },
+{ "Verdana,serif",
+ "Nycun" },
+{ "Verdana,serif",
+ "RI8" },
+{ "Verdana,serif",
+ "cebprffbe. " },
+{ "Verdana,serif",
+ "Pna" },
+{ "Verdana,serif",
+ "tebbir" },
+{ "Verdana,serif",
+ "onpx? " },
+{ "Verdana,serif",
+ "ernqvat" },
+{ "Verdana,serif",
+ "thl," },
+{ "Verdana,serif",
+ "zhfvp" },
+{ "Verdana,serif",
+ "cynlvat. " },
+{ "Verdana,serif",
+ "negvpyr:" },
+{ "Verdana,serif",
+ "\"Guvf" },
+{ "Verdana,serif",
+ "vafgnyyzrag" },
+{ "Verdana,serif",
+ "guerr-cneg" },
+{ "Verdana,serif",
+ "shghevfgvp" },
+{ "Verdana,serif",
+ "RI8," },
+{ "Verdana,serif",
+ "nzovgvbhf" },
+{ "Verdana,serif",
+ "zvpebcebprffbe" },
+{ "Verdana,serif",
+ "cebcbfrq." },
+{ "Verdana,serif",
+ "qrfpevorf" },
+{ "Verdana,serif",
+ "cngu" },
+{ "Verdana,serif",
+ "yrnqvat" },
+{ "Verdana,serif",
+ "bssref" },
+{ "Verdana,serif",
+ "trareny" },
+{ "Verdana,serif",
+ "bcrengvbany" },
+{ "Verdana,serif",
+ "punenpgrevfgvpf." },
+{ "Verdana,serif",
+ "Shgher" },
+{ "Verdana,serif",
+ "negvpyrf" },
+{ "Verdana,serif",
+ "rknzvar" },
+{ "Verdana,serif",
+ "grpuavpny" },
+{ "Verdana,serif",
+ "punyyratr" },
+{ "Verdana,serif",
+ "cebzvfr" },
+{ "Verdana,serif",
+ "Fvzhygnarbhf" },
+{ "Verdana,serif",
+ "Zhygvguernqvat," },
+{ "Verdana,serif",
+ "FZG," },
+{ "Verdana,serif",
+ "rkcybvg" },
+{ "Verdana,serif",
+ "vapernfr" },
+{ "Verdana,serif",
+ "cresbeznapr" },
+{ "Verdana,serif",
+ "shegure," },
+{ "Verdana,serif",
+ "vzcyvpngvbaf" },
+{ "Verdana,serif",
+ "pbzcrgvat" },
+{ "Verdana,serif",
+ "nccebnpurf" },
+{ "Verdana,serif",
+ "RCVP.\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZF" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "/" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Kobk" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbafbyrf:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvpebfbsg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "K-Obk" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(41)" },
+{ "Verdana,serif",
+ "fgnl" },
+{ "Verdana,serif",
+ "fghss," },
+{ "Verdana,serif",
+ "qnzzvg," },
+{ "Verdana,serif",
+ "CF2" },
+{ "Verdana,serif",
+ "KObk" },
+{ "Verdana,serif",
+ "znubtnal" },
+{ "Verdana,serif",
+ "(ybat" },
+{ "Verdana,serif",
+ "nagv-pbafbyr" },
+{ "Verdana,serif",
+ "crefba," },
+{ "Verdana,serif",
+ "guvf)..." },
+{ "Verdana,serif",
+ "Tnzrpragre" },
+{ "Verdana,serif",
+ "Zvpebfbsg'f" },
+{ "Verdana,serif",
+ "W." },
+{ "Verdana,serif",
+ "Nyyneq" },
+{ "Verdana,serif",
+ "RN'f" },
+{ "Verdana,serif",
+ "Kobk" },
+{ "Verdana,serif",
+ "arjf" },
+{ "Verdana,serif",
+ "(gung" },
+{ "Verdana,serif",
+ "whzcrq" },
+{ "Verdana,serif",
+ "onaqjntba," },
+{ "Verdana,serif",
+ "pbafbyr)." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fhzzbare" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fubgf" },
+{ "Verdana,serif",
+ "IbyvgvbaJngpu.pbz" },
+{ "Verdana,serif",
+ "7" },
+{ "Verdana,serif",
+ "fubgf" },
+{ "Verdana,serif",
+ "GUD" },
+{ "Verdana,serif",
+ "Ibyvgvba'f" },
+{ "Verdana,serif",
+ "sbegupbzvat" },
+{ "Verdana,serif",
+ "ECT," },
+{ "Verdana,serif",
+ "Fhzzbare" },
+{ "Verdana,serif",
+ "Qnzzvg" },
+{ "Verdana,serif",
+ "ivfhnyf" },
+{ "Verdana,serif",
+ "cbyvfurq" },
+{ "Verdana,serif",
+ "thlf)." },
+{ "Verdana,serif",
+ "Fbzr" },
+{ "Verdana,serif",
+ "frra" },
+{ "Verdana,serif",
+ "(V" },
+{ "Verdana,serif",
+ "pna'g" },
+{ "Verdana,serif",
+ "erpnyy" },
+{ "Verdana,serif",
+ "juvpu," },
+{ "Verdana,serif",
+ "cbvagrq" },
+{ "Verdana,serif",
+ "rznvy)" },
+{ "Verdana,serif",
+ "arj." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jvmneqel" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(9)" },
+{ "Verdana,serif",
+ "PTB" },
+{ "Verdana,serif",
+ "fznpxrq" },
+{ "Verdana,serif",
+ "Fvegrpu'f" },
+{ "Verdana,serif",
+ "Jvmneqel" },
+{ "Verdana,serif",
+ "8" },
+{ "Verdana,serif",
+ "vax" },
+{ "Verdana,serif",
+ "qrny" },
+{ "Verdana,serif",
+ "choyvfure" },
+{ "Verdana,serif",
+ "(nccneragyl" },
+{ "Verdana,serif",
+ "artbgvngvbaf" },
+{ "Verdana,serif",
+ "frireny). " },
+{ "Verdana,serif",
+ "favc" },
+{ "Verdana,serif",
+ "yratgul" },
+{ "Verdana,serif",
+ "Juvyr" },
+{ "Verdana,serif",
+ "fntn" },
+{ "Verdana,serif",
+ "hasbyqf" },
+{ "Verdana,serif",
+ "crefcrpgvir" },
+{ "verdana,serif",
+ "Jvmneqel'f" },
+{ "Verdana,serif",
+ "cnfg," },
+{ "Verdana,serif",
+ "qrfvtaref" },
+{ "Verdana,serif",
+ "cnvaf" },
+{ "Verdana,serif",
+ "njnl" },
+{ "Verdana,serif",
+ "\"fvk" },
+{ "Verdana,serif",
+ "gnax\"" },
+{ "Verdana,serif",
+ "pevgvpvfzf" },
+{ "Verdana,serif",
+ "cynthrq" },
+{ "Verdana,serif",
+ "Sbe" },
+{ "Verdana,serif",
+ "rknzcyr," },
+{ "Verdana,serif",
+ "irgrenaf" },
+{ "Verdana,serif",
+ "npphfgbzrq" },
+{ "Verdana,serif",
+ "fubbgre-fglyr" },
+{ "Verdana,serif",
+ "gnpgvpf" },
+{ "Verdana,serif",
+ "\"" },
+{ "verdana,serif",
+ "Rlr" },
+{ "verdana,serif",
+ "bs" },
+{ "verdana,serif",
+ "Orubyqre" },
+{ "Verdana,serif",
+ "gjb-fgrc\"" },
+{ "Verdana,serif",
+ "rarzvrf" },
+{ "Verdana,serif",
+ "fheebhaq" },
+{ "Verdana,serif",
+ "cnegl" },
+{ "Verdana,serif",
+ "urer." },
+{ "Verdana,serif",
+ "Pbaprea" },
+{ "Verdana,serif",
+ "bafperra" },
+{ "Verdana,serif",
+ "urnqrq" },
+{ "Verdana,serif",
+ "jbeyq" },
+{ "Verdana,serif",
+ "rdhvinyrag" },
+{ "Verdana,serif",
+ "Obbg" },
+{ "Verdana,serif",
+ "Uvyy" },
+{ "Verdana,serif",
+ "Fbcuvfgvpngrq" },
+{ "Verdana,serif",
+ "zbafgre" },
+{ "Verdana,serif",
+ "NV" },
+{ "Verdana,serif",
+ "sbrf" },
+{ "Verdana,serif",
+ "bsgra" },
+{ "Verdana,serif",
+ "pbairetr" },
+{ "Verdana,serif",
+ "jrnx" },
+{ "Verdana,serif",
+ "fvfgre" },
+{ "Verdana,serif",
+ "zntrf" },
+{ "Verdana,serif",
+ "yvar." },
+{ "Verdana,serif",
+ "Rira" },
+{ "Verdana,serif",
+ "ybjyl" },
+{ "Verdana,serif",
+ "tebhc" },
+{ "Verdana,serif",
+ "engf" },
+{ "Verdana,serif",
+ "fher" },
+{ "Verdana,serif",
+ "fcernq" },
+{ "Verdana,serif",
+ "nffnhyg" },
+{ "Verdana,serif",
+ "zhygvcyr" },
+{ "Verdana,serif",
+ "nqiragheref" },
+{ "Verdana,serif",
+ "fvzhygnarbhfyl," },
+{ "Verdana,serif",
+ "vagryyvtrag" },
+{ "Verdana,serif",
+ "nqirefnevrf" },
+{ "Verdana,serif",
+ "qernqrq" },
+{ "Verdana,serif",
+ "Qrngu" },
+{ "Verdana,serif",
+ "Ybeqf" },
+{ "Verdana,serif",
+ "xrl" },
+{ "Verdana,serif",
+ "ihyarenoyr," },
+{ "Verdana,serif",
+ "cbjreshy," },
+{ "Verdana,serif",
+ "zntvp-hfref" },
+{ "Verdana,serif",
+ "svefg." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "tnyyrel" },
+{ "Verdana,serif",
+ "\"pvarzngvp" },
+{ "Verdana,serif",
+ "tnyyrel\"," },
+{ "Verdana,serif",
+ "gurer'f" },
+{ "Verdana,serif",
+ "gurer. " },
+{ "Verdana,serif",
+ "ECTInhyg" },
+{ "Verdana,serif",
+ "Vfyr'f" },
+{ "Verdana,serif",
+ "ortvaf," },
+{ "Verdana,serif",
+ "10" },
+{ "Verdana,serif",
+ "cnpx'f" },
+{ "Verdana,serif",
+ "bcravat" },
+{ "Verdana,serif",
+ "zbivr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "oynu..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:15" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Oynu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(22)" },
+{ "Verdana,serif",
+ "Jubbcfvr," },
+{ "Verdana,serif",
+ "erzvaq" },
+{ "Verdana,serif",
+ "Gnaln" },
+{ "Verdana,serif",
+ "(Fjrrg" },
+{ "Verdana,serif",
+ "Qvpx'f" },
+{ "Verdana,serif",
+ "jbzna)" },
+{ "Verdana,serif",
+ "fyrcg" },
+{ "Verdana,serif",
+ "pvgl" },
+{ "Verdana,serif",
+ "Puvpntb" },
+{ "Verdana,serif",
+ "Fjrrg" },
+{ "Verdana,serif",
+ "Qvpx" },
+{ "Verdana,serif",
+ "cebonoyl" },
+{ "Verdana,serif",
+ "nebhaq. " },
+{ "Verdana,serif",
+ "jryy," },
+{ "Verdana,serif",
+ "ohggre" },
+{ "Verdana,serif",
+ "uvz" },
+{ "Verdana,serif",
+ "habcrarq" },
+{ "Verdana,serif",
+ "obkrq" },
+{ "Verdana,serif",
+ "pbcl" },
+{ "Verdana,serif",
+ "Fpnef" },
+{ "Verdana,serif",
+ "Iryvbhf" },
+{ "Verdana,serif",
+ "ungr" },
+{ "Verdana,serif",
+ "unovg" },
+{ "Verdana,serif",
+ "ntnva," },
+{ "Verdana,serif",
+ "zrna" },
+{ "Verdana,serif",
+ "Rirepenpx" },
+{ "Verdana,serif",
+ "gjryir" },
+{ "Verdana,serif",
+ "fgrc" },
+{ "Verdana,serif",
+ "cebtenz" },
+{ "Verdana,serif",
+ "cvpavp...uryy," },
+{ "Verdana,serif",
+ "trggvat" },
+{ "Verdana,serif",
+ "funxrf" },
+{ "Verdana,serif",
+ "guvaxvat" },
+{ "Verdana,serif",
+ "vg). " },
+{ "Verdana,serif",
+ "Ur" },
+{ "Verdana,serif",
+ "cvffrq" },
+{ "Verdana,serif",
+ "gnk" },
+{ "Verdana,serif",
+ "jevgr" },
+{ "Verdana,serif",
+ "nccneragyl" },
+{ "Verdana,serif",
+ "vgrz" },
+{ "Verdana,serif",
+ "erghea" },
+{ "Verdana,serif",
+ "lrne," },
+{ "Verdana,serif",
+ "qhzo" },
+{ "Verdana,serif",
+ "onfgneq). " },
+{ "Verdana,serif",
+ "Qvpx," },
+{ "Verdana,serif",
+ "qnlf" },
+{ "Verdana,serif",
+ "(gur" },
+{ "Verdana,serif",
+ "17gu)" },
+{ "Verdana,serif",
+ "urnqvat" },
+{ "Verdana,serif",
+ "Irtnf" },
+{ "Verdana,serif",
+ "(jvgu" },
+{ "Verdana,serif",
+ "Nvzrr)...cebonoyl" },
+{ "Verdana,serif",
+ "svavfu" },
+{ "Verdana,serif",
+ "Puevfgznf" },
+{ "Verdana,serif",
+ "fubccvat" },
+{ "Verdana,serif",
+ "tbggn" },
+{ "Verdana,serif",
+ "Pnrfne" },
+{ "Verdana,serif",
+ "sbehz" },
+{ "Verdana,serif",
+ "fubcf," },
+{ "Verdana,serif",
+ "fryy" },
+{ "Verdana,serif",
+ "fcrez" },
+{ "Verdana,serif",
+ "+" },
+{ "Verdana,serif",
+ "xvqarl," },
+{ "Verdana,serif",
+ "nssbeq" },
+{ "Verdana,serif",
+ "arneyl" },
+{ "Verdana,serif",
+ "nalguvat" },
+{ "Verdana,serif",
+ "fubcf" },
+{ "Verdana,serif",
+ "(lrnu," },
+{ "Verdana,serif",
+ "onetnva," },
+{ "Verdana,serif",
+ "cvpxvat" },
+{ "Verdana,serif",
+ "<qevccvat" },
+{ "Verdana,serif",
+ "fnepnfz>). " },
+{ "Verdana,serif",
+ "Qnzzvg," },
+{ "Verdana,serif",
+ "cvpx" },
+{ "Verdana,serif",
+ "Gbby" },
+{ "Verdana,serif",
+ "guvatl" },
+{ "Verdana,serif",
+ "lrg," },
+{ "Verdana,serif",
+ "ubcrshyyl" },
+{ "Verdana,serif",
+ "gbqnl. " },
+{ "Verdana,serif",
+ "unira'g" },
+{ "Verdana,serif",
+ "Fnyviny" },
+{ "Verdana,serif",
+ "fnsr" },
+{ "Verdana,serif",
+ "chepunfr. " },
+{ "Verdana,serif",
+ "Wrrm," },
+{ "Verdana,serif",
+ "oryvrir" },
+{ "Verdana,serif",
+ "jnf," },
+{ "Verdana,serif",
+ "uryy," },
+{ "Verdana,serif",
+ "1/2" },
+{ "Verdana,serif",
+ "ntb" },
+{ "Verdana,serif",
+ "purpxrq" },
+{ "Verdana,serif",
+ "pbapreg" },
+{ "Verdana,serif",
+ "Fnyg" },
+{ "Verdana,serif",
+ "Ynxr. " },
+{ "Verdana,serif",
+ "qrsvavgryl" },
+{ "Verdana,serif",
+ "Gbby-svk" },
+{ "Verdana,serif",
+ "(hz," },
+{ "Verdana,serif",
+ "cha" },
+{ "Verdana,serif",
+ "vagraqrq," },
+{ "Verdana,serif",
+ "fbzrguvat)." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jrqarfqnl," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qrprzore" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "13," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2000" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "-" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Unir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fbzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "be" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "qbrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "lbhe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "jbzna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "nfx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fjrrg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvpx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jvyyl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ol" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "anzr?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gryy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "hf!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tvnagf:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pvgvmra" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Xnohgb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "@" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "IR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:28" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(91)" },
+{ "Verdana,serif",
+ "ohfl" },
+{ "Verdana,serif",
+ "Zbba'f" },
+{ "Verdana,serif",
+ "Tvnagf:" },
+{ "Verdana,serif",
+ "Pvgvmra" },
+{ "Verdana,serif",
+ "Xnohgb" },
+{ "Verdana,serif",
+ "ragvergl" },
+{ "Verdana,serif",
+ "4:30" },
+{ "Verdana,serif",
+ "nz" },
+{ "Verdana,serif",
+ "zbeavat)" },
+{ "Verdana,serif",
+ "erivrj" },
+{ "Verdana,serif",
+ "purng" },
+{ "Verdana,serif",
+ "pbqrf" },
+{ "Verdana,serif",
+ "tnzr!" },
+{ "Verdana,serif",
+ "Abg" },
+{ "Verdana,serif",
+ "*pbhtu*" },
+{ "Verdana,serif",
+ "jbexrq," },
+{ "Verdana,serif",
+ "pbhefr." },
+{ "Verdana,serif",
+ "Tvnagf" },
+{ "Verdana,serif",
+ "znantr" },
+{ "Verdana,serif",
+ "ohfg" },
+{ "Verdana,serif",
+ "jvqr" },
+{ "Verdana,serif",
+ "bcra" },
+{ "Verdana,serif",
+ "pna-b-jubbc" },
+{ "Verdana,serif",
+ "nff?!" },
+{ "Verdana,serif",
+ "Nalubj" },
+{ "Verdana,serif",
+ "gnfgr" },
+{ "Verdana,serif",
+ "fhcre-frkl" },
+{ "Verdana,serif",
+ "ivfhnyf:" },
+{ "Verdana,serif",
+ "Gur" },
+{ "Verdana,serif",
+ "fghaavat" },
+{ "Verdana,serif",
+ "tencuvpf" },
+{ "Verdana,serif",
+ "srngherq" },
+{ "Verdana,serif",
+ "Tvnagf:" },
+{ "Verdana,serif",
+ "Pvgvmra" },
+{ "Verdana,serif",
+ "Xnohgb" },
+{ "Verdana,serif",
+ "fvzcyl" },
+{ "Verdana,serif",
+ "oyrj" },
+{ "Verdana,serif",
+ "zr" },
+{ "Verdana,serif",
+ "njnl." },
+{ "Verdana,serif",
+ "havdhr" },
+{ "Verdana,serif",
+ "neg" },
+{ "Verdana,serif",
+ "fglyr," },
+{ "Verdana,serif",
+ "uvtuyl" },
+{ "Verdana,serif",
+ "qrgnvyrq" },
+{ "Verdana,serif",
+ "punenpgref" },
+{ "Verdana,serif",
+ "rkcnafvir" },
+{ "Verdana,serif",
+ "raivebazragf" },
+{ "Verdana,serif",
+ "jrer" },
+{ "Verdana,serif",
+ "hayvxr" },
+{ "Verdana,serif",
+ "nalguvat" },
+{ "Verdana,serif",
+ "V\xe2""\x80""\x99""ir" },
+{ "Verdana,serif",
+ "rire" },
+{ "Verdana,serif",
+ "frra" },
+{ "Verdana,serif",
+ "orsber." },
+{ "Verdana,serif",
+ "Sebz" },
+{ "Verdana,serif",
+ "pbybeshy" },
+{ "Verdana,serif",
+ "fxl" },
+{ "Verdana,serif",
+ "cnfgry" },
+{ "Verdana,serif",
+ "jngref," },
+{ "Verdana,serif",
+ "qnex" },
+{ "Verdana,serif",
+ "gjvfgrq" },
+{ "Verdana,serif",
+ "Erncre" },
+{ "Verdana,serif",
+ "sbegerffrf" },
+{ "Verdana,serif",
+ "dhnvag" },
+{ "Verdana,serif",
+ "Fznegvr" },
+{ "Verdana,serif",
+ "ivyyntrf," },
+{ "Verdana,serif",
+ "Tvnagf" },
+{ "Verdana,serif",
+ "vf" },
+{ "Verdana,serif",
+ "qrsvavgryl" },
+{ "Verdana,serif",
+ "bar" },
+{ "Verdana,serif",
+ "orfg-ybbxvat" },
+{ "Verdana,serif",
+ "tnzrf" },
+{ "Verdana,serif",
+ "lrne." },
+{ "Verdana,serif",
+ "Guvf" },
+{ "Verdana,serif",
+ "bire" },
+{ "Verdana,serif",
+ "nohaqnapr" },
+{ "Verdana,serif",
+ "rlr-pnaql" },
+{ "Verdana,serif",
+ "qbrf" },
+{ "Verdana,serif",
+ "pbfg," },
+{ "Verdana,serif",
+ "ubjrire." },
+{ "Verdana,serif",
+ "AIVQVN" },
+{ "Verdana,serif",
+ "TrSbepr/TrSbepr" },
+{ "Verdana,serif",
+ "2" },
+{ "Verdana,serif",
+ "bjaref" },
+{ "Verdana,serif",
+ "haqhyngrq" },
+{ "Verdana,serif",
+ "jvgu" },
+{ "Verdana,serif",
+ "evpuyl" },
+{ "Verdana,serif",
+ "yriryf," },
+{ "Verdana,serif",
+ "fvzzrevat" },
+{ "Verdana,serif",
+ "ybiryl" },
+{ "Verdana,serif",
+ "grkgherf," },
+{ "Verdana,serif",
+ "ohzc" },
+{ "Verdana,serif",
+ "znccrq" },
+{ "Verdana,serif",
+ "ornhgvshy." },
+{ "Verdana,serif",
+ "Jvgu" },
+{ "Verdana,serif",
+ "nal" },
+{ "Verdana,serif",
+ "bgure" },
+{ "Verdana,serif",
+ "ivqrb" },
+{ "Verdana,serif",
+ "pneq," },
+{ "Verdana,serif",
+ "ubjrire," },
+{ "Verdana,serif",
+ "erfhygf" },
+{ "Verdana,serif",
+ "znl" },
+{ "Verdana,serif",
+ "inel." },
+{ "Verdana,serif",
+ "Ibbqbb" },
+{ "Verdana,serif",
+ "5," },
+{ "Verdana,serif",
+ "vgf" },
+{ "Verdana,serif",
+ "terng" },
+{ "Verdana,serif",
+ "shyy-fperra" },
+{ "Verdana,serif",
+ "nagv-nyvnfvat," },
+{ "Verdana,serif",
+ "tvirf" },
+{ "Verdana,serif",
+ "ohvyqvatf" },
+{ "Verdana,serif",
+ "avpr" },
+{ "Verdana,serif",
+ "pyrna" },
+{ "Verdana,serif",
+ "ybbx," },
+{ "Verdana,serif",
+ "ohg" },
+{ "Verdana,serif",
+ "senzr" },
+{ "Verdana,serif",
+ "engrf" },
+{ "Verdana,serif",
+ "qb" },
+{ "Verdana,serif",
+ "frrz" },
+{ "Verdana,serif",
+ "ovg" },
+{ "Verdana,serif",
+ "fyhttvfu," },
+{ "Verdana,serif",
+ "pbybef" },
+{ "Verdana,serif",
+ "nera\xe2""\x80""\x99""g" },
+{ "Verdana,serif",
+ "arneyl" },
+{ "Verdana,serif",
+ "nf" },
+{ "Verdana,serif",
+ "ivoenag" },
+{ "Verdana,serif",
+ "pbzcnerq" },
+{ "Verdana,serif",
+ "rssrpgf" },
+{ "Verdana,serif",
+ "ba" },
+{ "Verdana,serif",
+ "arjre" },
+{ "Verdana,serif",
+ "onfrq" },
+{ "Verdana,serif",
+ "obneq." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnatfgref" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:53" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Znairre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"Rvqbyba\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Urve" },
+{ "Verdana,serif",
+ "Rvqbf'" },
+{ "Verdana,serif",
+ "frdhry" },
+{ "Verdana,serif",
+ "Tnatfgref" },
+{ "Verdana,serif",
+ "gvgyrq," },
+{ "Verdana,serif",
+ "fhecevfvatyl," },
+{ "Verdana,serif",
+ "2." },
+{ "Verdana,serif",
+ "Orvat" },
+{ "Verdana,serif",
+ "Ubgubhfr" },
+{ "Verdana,serif",
+ "Perngvbaf," },
+{ "Verdana,serif",
+ "eha" },
+{ "Verdana,serif",
+ "ehssvna-ubbyvtnaf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvnoyb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tbbqvrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:51" },
+{ "Verdana,serif",
+ "Lrf," },
+{ "Verdana,serif",
+ "tbbqvrf" },
+{ "Verdana,serif",
+ "xvaq" },
+{ "Verdana,serif",
+ "lbh'q" },
+{ "Verdana,serif",
+ "chapu" },
+{ "Verdana,serif",
+ "zbz" },
+{ "Verdana,serif",
+ "gb." },
+{ "Verdana,serif",
+ "Qvnoyb" },
+{ "Verdana,serif",
+ "Rkcnafvba" },
+{ "Verdana,serif",
+ "sryybjf" },
+{ "Verdana,serif",
+ "va-punetr" },
+{ "Verdana,serif",
+ "VV'f" },
+{ "Verdana,serif",
+ "pvarzngvpf," },
+{ "Verdana,serif",
+ "fjrrgrfg" },
+{ "Verdana,serif",
+ "phgfprarf" },
+{ "Verdana,serif",
+ "rire." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "D3:" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Grnz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Neran" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:22" },
+{ "Verdana,serif",
+ "CP.VTA" },
+{ "Verdana,serif",
+ "jevggra" },
+{ "Verdana,serif",
+ "obl" },
+{ "Verdana,serif",
+ "Bhg" },
+{ "Verdana,serif",
+ "phevbfvgl," },
+{ "Verdana,serif",
+ "qvq" },
+{ "Verdana,serif",
+ "Neran?" },
+{ "Verdana,serif",
+ "Cbfg" },
+{ "Verdana,serif",
+ "pbzzragf" },
+{ "Verdana,serif",
+ "frpgvba." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:16" },
+{ "Verdana,serif",
+ "jbaqreshy" },
+{ "Verdana,serif",
+ "Onexre'f" },
+{ "Verdana,serif",
+ "Haqlvat" },
+{ "Verdana,serif",
+ "jnvg" },
+{ "Verdana,serif",
+ "hagvy" },
+{ "Verdana,serif",
+ "fvapr" },
+{ "Verdana,serif",
+ "rkpryyrag" },
+{ "Verdana,serif",
+ "ba," },
+{ "Verdana,serif",
+ "pebgpu" },
+{ "Verdana,serif",
+ "ovgvat" },
+{ "Verdana,serif",
+ "ercgvyrf" },
+{ "Verdana,serif",
+ "jebat?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZBB" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qvnel" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "3:07" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(2)" },
+{ "Verdana,serif",
+ "qrfvtare" },
+{ "Verdana,serif",
+ "qvnel" },
+{ "Verdana,serif",
+ "gvzr!" },
+{ "Verdana,serif",
+ "evtug," },
+{ "Verdana,serif",
+ "qebc" },
+{ "Verdana,serif",
+ "fzbpxf," },
+{ "Verdana,serif",
+ "teno" },
+{ "Verdana,serif",
+ "fbpxf," },
+{ "Verdana,serif",
+ "Znfgre" },
+{ "Verdana,serif",
+ "Bevba" },
+{ "Verdana,serif",
+ "Qrfvatre" },
+{ "Verdana,serif",
+ "Qvnel" },
+{ "Verdana,serif",
+ "sernxva'" },
+{ "Verdana,serif",
+ "znffvir." },
+{ "Verdana,serif",
+ "fybccl" },
+{ "Verdana,serif",
+ "ovgpu" },
+{ "Verdana,serif",
+ "qvnel:" },
+{ "Verdana,serif",
+ "Wbvavat" },
+{ "Verdana,serif",
+ "Zvffvba:" },
+{ "Verdana,serif",
+ "Vzcbffvoyr" },
+{ "Verdana,serif",
+ "Uhturf," },
+{ "Verdana,serif",
+ "fuvsgrq" },
+{ "Verdana,serif",
+ "\"svar" },
+{ "Verdana,serif",
+ "sbphf\"" },
+{ "Verdana,serif",
+ "cbfvgvba" },
+{ "Verdana,serif",
+ "fuvc" },
+{ "Verdana,serif",
+ "qrfvta," },
+{ "Verdana,serif",
+ "grpuabybtl," },
+{ "Verdana,serif",
+ "\"oebnq" },
+{ "Verdana,serif",
+ "cvrprf" },
+{ "Verdana,serif",
+ "vagrepbaarpg." },
+{ "Verdana,serif",
+ "Cbbe" },
+{ "Verdana,serif",
+ "ohttre!" },
+{ "Verdana,serif",
+ "Ur'f" },
+{ "Verdana,serif",
+ "fcrrq" },
+{ "Verdana,serif",
+ "pevgvpny" },
+{ "Verdana,serif",
+ "ryrzragf," },
+{ "Verdana,serif",
+ "rpbabzvp" },
+{ "Verdana,serif",
+ "zbqry" },
+{ "Verdana,serif",
+ "(zbarl," },
+{ "Verdana,serif",
+ "erfbheprf," },
+{ "Verdana,serif",
+ "cynarg-ohvyqvat)." },
+{ "Verdana,serif",
+ "njrfbzr" },
+{ "Verdana,serif",
+ "nfcrpg" },
+{ "Verdana,serif",
+ "Znfre" },
+{ "Verdana,serif",
+ "serfu" },
+{ "Verdana,serif",
+ "pncnpvgl," },
+{ "Verdana,serif",
+ "rssvpvrapl," },
+{ "Verdana,serif",
+ "engrf," },
+{ "Verdana,serif",
+ "harzcyblzrag," },
+{ "Verdana,serif",
+ "\"pbafhzre" },
+{ "Verdana,serif",
+ "rpbabzl,\"" },
+{ "Verdana,serif",
+ "genqr" },
+{ "Verdana,serif",
+ "(ng" },
+{ "Verdana,serif",
+ "ybpny," },
+{ "Verdana,serif",
+ "flfgrz," },
+{ "Verdana,serif",
+ "vagrefgryyne" },
+{ "Verdana,serif",
+ "yriry;" },
+{ "Verdana,serif",
+ "vagre-pvivyvmngvba)," },
+{ "Verdana,serif",
+ "tbireazrag" },
+{ "Verdana,serif",
+ "(ybpny," },
+{ "Verdana,serif",
+ "frpgbe," },
+{ "Verdana,serif",
+ "Vzcrevny)," },
+{ "Verdana,serif",
+ "nfcrpgf." },
+{ "Verdana,serif",
+ "Bs" },
+{ "Verdana,serif",
+ "xrrcvat" },
+{ "Verdana,serif",
+ "cnenqvtz," },
+{ "Verdana,serif",
+ "\"uvturfg" },
+{ "Verdana,serif",
+ "yriry\"" },
+{ "Verdana,serif",
+ "znpeb-znantrzrag," },
+{ "Verdana,serif",
+ "jubbfu" },
+{ "Verdana,serif",
+ "erq" },
+{ "Verdana,serif",
+ "gncr" },
+{ "Verdana,serif",
+ "zbhfr" },
+{ "Verdana,serif",
+ "pyvpxf" },
+{ "Verdana,serif",
+ "erfcbafr" },
+{ "Verdana,serif",
+ "\"fhzznel\"" },
+{ "Verdana,serif",
+ "cerfragrq" },
+{ "Verdana,serif",
+ "\"haqre" },
+{ "Verdana,serif",
+ "ubbq,\"" },
+{ "Verdana,serif",
+ "zvahgvnr" },
+{ "Verdana,serif",
+ "qrgnvy" },
+{ "Verdana,serif",
+ "znavchyngr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Rfpncr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sebz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ZV" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2:59" },
+{ "Verdana,serif",
+ "Habssvpvny" },
+{ "Verdana,serif",
+ "Rfpncr" },
+{ "Verdana,serif",
+ "Zbaxrl" },
+{ "Verdana,serif",
+ "Vfynaq" },
+{ "Verdana,serif",
+ "Frna" },
+{ "Verdana,serif",
+ "Pynex" },
+{ "Verdana,serif",
+ "Zvxr" },
+{ "Verdana,serif",
+ "Fgrzzyr" },
+{ "Verdana,serif",
+ "Vfynaq." },
+{ "Verdana,serif",
+ "Pna" },
+{ "Verdana,serif",
+ "lbh" },
+{ "Verdana,serif",
+ "tvir" },
+{ "Verdana,serif",
+ "hf" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "yvggyr" },
+{ "Verdana,serif",
+ "vafvtug" },
+{ "Verdana,serif",
+ "vagb" },
+{ "Verdana,serif",
+ "gur" },
+{ "Verdana,serif",
+ "chmmyr-znxvat" },
+{ "Verdana,serif",
+ "cebprff?" },
+{ "Verdana,serif",
+ "Ubj" },
+{ "Verdana,serif",
+ "qb" },
+{ "Verdana,serif",
+ "qrivfr" },
+{ "Verdana,serif",
+ "bsgra" },
+{ "Verdana,serif",
+ "vagevpngr" },
+{ "Verdana,serif",
+ "zrgubqf" },
+{ "Verdana,serif",
+ "hfrq" },
+{ "Verdana,serif",
+ "gb" },
+{ "Verdana,serif",
+ "trg" },
+{ "Verdana,serif",
+ "whfg" },
+{ "Verdana,serif",
+ "bar" },
+{ "Verdana,serif",
+ "bowrpg" },
+{ "Verdana,serif",
+ "znqr" },
+{ "Verdana,serif",
+ "-" },
+{ "Verdana,serif",
+ "fhpu" },
+{ "Verdana,serif",
+ "nf" },
+{ "Verdana,serif",
+ "\xe2""\x80""\x98""znxvat\xe2""\x80""\x99""" },
+{ "Verdana,serif",
+ "Hygvzngr" },
+{ "Verdana,serif",
+ "Vafhyg?" },
+{ "Verdana,serif",
+ "Svefg," },
+{ "Verdana,serif",
+ "qevax" },
+{ "Verdana,serif",
+ "orre." },
+{ "Verdana,serif",
+ "Xvqqvat." },
+{ "Verdana,serif",
+ "Nygubhtu" },
+{ "Verdana,serif",
+ "rirelbar" },
+{ "Verdana,serif",
+ "unf" },
+{ "Verdana,serif",
+ "qvssrerag" },
+{ "Verdana,serif",
+ "jnl" },
+{ "Verdana,serif",
+ "guvaxvat" },
+{ "Verdana,serif",
+ "gurfr" },
+{ "Verdana,serif",
+ "guvatf," },
+{ "Verdana,serif",
+ "onfvp" },
+{ "Verdana,serif",
+ "nccebnpu" },
+{ "Verdana,serif",
+ "frrzf" },
+{ "Verdana,serif",
+ "jbex." },
+{ "Verdana,serif",
+ "Zbfg" },
+{ "Verdana,serif",
+ "ovt" },
+{ "Verdana,serif",
+ "chmmyr" },
+{ "Verdana,serif",
+ "vqrnf" },
+{ "Verdana,serif",
+ "pbzr" },
+{ "Verdana,serif",
+ "sebz" },
+{ "Verdana,serif",
+ "fgbel." },
+{ "Verdana,serif",
+ "naablvat," },
+{ "Verdana,serif",
+ "abafrafvpny" },
+{ "Verdana,serif",
+ "chmmyrf" },
+{ "Verdana,serif",
+ "graq" },
+{ "Verdana,serif",
+ "srry" },
+{ "Verdana,serif",
+ "orpnhfr" },
+{ "Verdana,serif",
+ "tbny" },
+{ "Verdana,serif",
+ "qbrfa\xe2""\x80""\x99""g" },
+{ "Verdana,serif",
+ "zngpu" },
+{ "Verdana,serif",
+ "znva" },
+{ "Verdana,serif",
+ "punenpgre." },
+{ "Verdana,serif",
+ "gurer," },
+{ "Verdana,serif",
+ "jr" },
+{ "Verdana,serif",
+ "svaq" },
+{ "Verdana,serif",
+ "jnlf" },
+{ "Verdana,serif",
+ "oernxvat" },
+{ "Verdana,serif",
+ "hc" },
+{ "Verdana,serif",
+ "vagb" },
+{ "Verdana,serif",
+ "fznyyre" },
+{ "Verdana,serif",
+ "chmmyrf," },
+{ "Verdana,serif",
+ "vagrtengvat" },
+{ "Verdana,serif",
+ "gurz" },
+{ "Verdana,serif",
+ "rnpu" },
+{ "Verdana,serif",
+ "bgure," },
+{ "Verdana,serif",
+ "ybpngvbaf" },
+{ "Verdana,serif",
+ "jr\xe2""\x80""\x99""ir" },
+{ "Verdana,serif",
+ "qrfvtarq." },
+{ "Verdana,serif",
+ "Chmmyrf" },
+{ "Verdana,serif",
+ "vqragvsvrq" },
+{ "Verdana,serif",
+ "orvat" },
+{ "Verdana,serif",
+ "zber" },
+{ "Verdana,serif",
+ "gurzngvp" },
+{ "Verdana,serif",
+ "znxr" },
+{ "Verdana,serif",
+ "pbzcyvpngrq." },
+{ "Verdana,serif",
+ "Gura" },
+{ "Verdana,serif",
+ "(Jevggra" },
+{ "Verdana,serif",
+ "lbhef" },
+{ "Verdana,serif",
+ "gehyl)" },
+{ "Verdana,serif",
+ "onq-obl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "2:25" },
+{ "Verdana,serif",
+ "Tnvyr" },
+{ "Verdana,serif",
+ "r-znvyrq" },
+{ "Verdana,serif",
+ "yrggvat" },
+{ "Verdana,serif",
+ "JnepensgVVV.arg" },
+{ "Verdana,serif",
+ "Puevf" },
+{ "Verdana,serif",
+ "Zrgmra" },
+{ "Verdana,serif",
+ "Perngvir" },
+{ "Verdana,serif",
+ "Qverpgbe" },
+{ "Verdana,serif",
+ "nhgube" },
+{ "Verdana,serif",
+ "cerdhry" },
+{ "Verdana,serif",
+ "fgbel" },
+{ "Verdana,serif",
+ "Sevqnl." },
+{ "Verdana,serif",
+ "JnePensgVVV.Arg:" },
+{ "Verdana,serif",
+ "Gur" },
+{ "Verdana,serif",
+ "fgbel," },
+{ "Verdana,serif",
+ "Bs" },
+{ "Verdana,serif",
+ "Oybbq" },
+{ "Verdana,serif",
+ "Ubabe," },
+{ "Verdana,serif",
+ "jvyy" },
+{ "Verdana,serif",
+ "sbphf" },
+{ "Verdana,serif",
+ "ba" },
+{ "Verdana,serif",
+ "cer-uvfgbel" },
+{ "Verdana,serif",
+ "gvzr" },
+{ "Verdana,serif",
+ "crevbq" },
+{ "Verdana,serif",
+ "JnePensg" },
+{ "Verdana,serif",
+ "tnzrf," },
+{ "Verdana,serif",
+ "evtug?" },
+{ "Verdana,serif",
+ "Ubj" },
+{ "Verdana,serif",
+ "sne" },
+{ "Verdana,serif",
+ "cer-" },
+{ "Verdana,serif",
+ "vf" },
+{ "Verdana,serif",
+ "uvfgbel?" },
+{ "Verdana,serif",
+ "Zrgmra:" },
+{ "Verdana,serif",
+ "Npghnyyl," },
+{ "Verdana,serif",
+ "Oybbq" },
+{ "Verdana,serif",
+ "Ubabe" },
+{ "Verdana,serif",
+ "znlor" },
+{ "Verdana,serif",
+ "svir" },
+{ "Verdana,serif",
+ "VVV." },
+{ "Verdana,serif",
+ "'cerfrag" },
+{ "Verdana,serif",
+ "qnl'" },
+{ "Verdana,serif",
+ "syhat" },
+{ "Verdana,serif",
+ "cnfg." },
+{ "Verdana,serif",
+ "vagrag" },
+{ "Verdana,serif",
+ "oevat" },
+{ "Verdana,serif",
+ "Bepf" },
+{ "Verdana,serif",
+ "Uhznaf" },
+{ "Verdana,serif",
+ "fubj" },
+{ "Verdana,serif",
+ "sbphfrq" },
+{ "Verdana,serif",
+ "rknzcyr" },
+{ "Verdana,serif",
+ "fgrerbglcrf" },
+{ "Verdana,serif",
+ "nffbpvngrq" },
+{ "Verdana,serif",
+ "Jnepensg'f" },
+{ "Verdana,serif",
+ "gjb" },
+{ "Verdana,serif",
+ "enprf." },
+{ "Verdana,serif",
+ "frafr" },
+{ "Verdana,serif",
+ "zbzraghz" },
+{ "Verdana,serif",
+ "yrnq" },
+{ "Verdana,serif",
+ "snaf" },
+{ "Verdana,serif",
+ "sverq" },
+{ "Verdana,serif",
+ "ntnva." },
+{ "Verdana,serif",
+ "rkcnaq" },
+{ "Verdana,serif",
+ "enprf" },
+{ "Verdana,serif",
+ "qvzrafvbany" },
+{ "Verdana,serif",
+ "cerivbhfyl" },
+{ "Verdana,serif",
+ "yrq" },
+{ "Verdana,serif",
+ "oryvrir." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgnegbcvn" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1:51" },
+{ "Verdana,serif",
+ "Tnzrfcbg" },
+{ "Verdana,serif",
+ "Zhpxl" },
+{ "Verdana,serif",
+ "Sbbg" },
+{ "Verdana,serif",
+ "Fgnegbcvn" },
+{ "Verdana,serif",
+ "onfrq" },
+{ "Verdana,serif",
+ "erprag" },
+{ "Verdana,serif",
+ "tnzr. " },
+{ "Verdana,serif",
+ "erzrzore" },
+{ "Verdana,serif",
+ "Heona" },
+{ "Verdana,serif",
+ "Punbf" },
+{ "Verdana,serif",
+ "ybbxrq" },
+{ "Verdana,serif",
+ "ynpxrq" },
+{ "Verdana,serif",
+ "qrcnegzrag;" },
+{ "Verdana,serif",
+ "unaqf-ba" },
+{ "Verdana,serif",
+ "cerivrjf" },
+{ "Verdana,serif",
+ "frra). " },
+{ "Verdana,serif",
+ "cnegvphyne," },
+{ "Verdana,serif",
+ "zvffvbaf" },
+{ "Verdana,serif",
+ "(ubj" },
+{ "Verdana,serif",
+ "ybbxrq," },
+{ "Verdana,serif",
+ "cynlrq," },
+{ "Verdana,serif",
+ "birenyy" },
+{ "Verdana,serif",
+ "tnzr). " },
+{ "Verdana,serif",
+ "Fvapr" },
+{ "Verdana,serif",
+ "nffhzrf" },
+{ "Verdana,serif",
+ "bs/nobhg" },
+{ "Verdana,serif",
+ "tnzref" },
+{ "Verdana,serif",
+ "bapr" },
+{ "Verdana,serif",
+ "guevivat" },
+{ "Verdana,serif",
+ "tnynpgvp" },
+{ "Verdana,serif",
+ "argjbex" },
+{ "Verdana,serif",
+ "fgnefuvcf," },
+{ "Verdana,serif",
+ "fgngvbaf" },
+{ "Verdana,serif",
+ "cynargf" },
+{ "Verdana,serif",
+ "yrsg" },
+{ "Verdana,serif",
+ "yvsryrff" },
+{ "Verdana,serif",
+ "erqhaqnag" },
+{ "Verdana,serif",
+ "fpnef" },
+{ "Verdana,serif",
+ "ncbpnylcfr." },
+{ "Verdana,serif",
+ "erohvyqvat" },
+{ "Verdana,serif",
+ "freirq" },
+{ "Verdana,serif",
+ "pbzzrepr" },
+{ "Verdana,serif",
+ "rzcver" },
+{ "Verdana,serif",
+ "erhavgvat" },
+{ "Verdana,serif",
+ "fheivivat" },
+{ "Verdana,serif",
+ "nyvra" },
+{ "Verdana,serif",
+ "crbcyrf" },
+{ "Verdana,serif",
+ "onaare." },
+{ "Verdana,serif",
+ "fvzhyngvba" },
+{ "Verdana,serif",
+ "tvirf" },
+{ "Verdana,serif",
+ "erohvyq" },
+{ "Verdana,serif",
+ "fhpprffshyyl" },
+{ "Verdana,serif",
+ "znvagnva" },
+{ "Verdana,serif",
+ "frevrf" },
+{ "Verdana,serif",
+ "tvnag" },
+{ "Verdana,serif",
+ "Gbehf" },
+{ "Verdana,serif",
+ "(qbahgf)" },
+{ "Verdana,serif",
+ "npebff" },
+{ "Verdana,serif",
+ "tnynkl," },
+{ "Verdana,serif",
+ "cebivqvat" },
+{ "Verdana,serif",
+ "fhvgnoyr" },
+{ "Verdana,serif",
+ "ubzr" },
+{ "Verdana,serif",
+ "vaunovg" },
+{ "Verdana,serif",
+ "fcnpr," },
+{ "Verdana,serif",
+ "creuncf" },
+{ "Verdana,serif",
+ "gheavat" },
+{ "Verdana,serif",
+ "gvql" },
+{ "Verdana,serif",
+ "cebsvg" },
+{ "Verdana,serif",
+ "cebprff." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Nepnahz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "1:36" },
+{ "Verdana,serif",
+ "VagryTnzre" },
+{ "Verdana,serif",
+ "Gebvxn'f" },
+{ "Verdana,serif",
+ "qvnyrq" },
+{ "Verdana,serif",
+ "Nepnahz:" },
+{ "Verdana,serif",
+ "Fgrnzjbexf" },
+{ "Verdana,serif",
+ "Zntvpx" },
+{ "Verdana,serif",
+ "Bofphen" },
+{ "Verdana,serif",
+ "fcryyvat" },
+{ "Verdana,serif",
+ "ZS). " },
+{ "Verdana,serif",
+ "fanccrq" },
+{ "Verdana,serif",
+ "orgn. " },
+{ "Verdana,serif",
+ "favccrg:" },
+{ "Verdana,serif",
+ "Nepnahz" },
+{ "Verdana,serif",
+ "Q&Q-rfdhr" },
+{ "Verdana,serif",
+ "zntvp," },
+{ "Verdana,serif",
+ "qjneirf," },
+{ "Verdana,serif",
+ "ryirf," },
+{ "Verdana,serif",
+ "bepf" },
+{ "Verdana,serif",
+ "naq," },
+{ "Verdana,serif",
+ "uhznaf." },
+{ "Verdana,serif",
+ "znxrf" },
+{ "Verdana,serif",
+ "vaqhfgevny" },
+{ "Verdana,serif",
+ "eribyhgvba" },
+{ "Verdana,serif",
+ "nqirag" },
+{ "Verdana,serif",
+ "grpuabybtl." },
+{ "Verdana,serif",
+ "Fcryy" },
+{ "Verdana,serif",
+ "fyvatref" },
+{ "Verdana,serif",
+ "zrrg" },
+{ "Verdana,serif",
+ "thafyvatref" },
+{ "Verdana,serif",
+ "gurer." },
+{ "Verdana,serif",
+ "Zntvp" },
+{ "Verdana,serif",
+ "nera'g" },
+{ "Verdana,serif",
+ "cnguf;" },
+{ "Verdana,serif",
+ "qvnzrgevpnyyl" },
+{ "Verdana,serif",
+ "bccbfvat" },
+{ "Verdana,serif",
+ "cnguf" },
+{ "Verdana,serif",
+ "zntvp" },
+{ "Verdana,serif",
+ "gjvfgf" },
+{ "Verdana,serif",
+ "ynjf" },
+{ "Verdana,serif",
+ "qrcraqf" },
+{ "Verdana,serif",
+ "urnivyl" },
+{ "Verdana,serif",
+ "gurz." },
+{ "Verdana,serif",
+ "fhpu," },
+{ "Verdana,serif",
+ "graq" },
+{ "Verdana,serif",
+ "artngr" },
+{ "Verdana,serif",
+ "erfhygvat" },
+{ "Verdana,serif",
+ "erqhprq" },
+{ "Verdana,serif",
+ "rssrpgvirarff" },
+{ "Verdana,serif",
+ "fbzrbar" },
+{ "Verdana,serif",
+ "cngu." },
+{ "Verdana,serif",
+ "Sroehnel" },
+{ "Verdana,serif",
+ "2001" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Neg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zntvp" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fperraf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12:42" },
+{ "Verdana,serif",
+ "Orgurfqn" },
+{ "Verdana,serif",
+ "Puneloqvf'" },
+{ "Verdana,serif",
+ "npgvba/fgengrtl/ECT" },
+{ "Verdana,serif",
+ "&" },
+{ "Verdana,serif",
+ "Znlurz:" },
+{ "Verdana,serif",
+ "Neg" },
+{ "Verdana,serif",
+ "Zntvp." },
+{ "Verdana,serif",
+ "Srnghevat" },
+{ "Verdana,serif",
+ "3q-ratvar," },
+{ "Verdana,serif",
+ "fcryyf," },
+{ "Verdana,serif",
+ "punenpgre'f," },
+{ "Verdana,serif",
+ "zvqtrg" },
+{ "Verdana,serif",
+ "gbff" },
+{ "Verdana,serif",
+ "ybpny" },
+{ "Verdana,serif",
+ "Funexrl'f" },
+{ "Verdana,serif",
+ "one," },
+{ "Verdana,serif",
+ "qnex-ubefr" },
+{ "Verdana,serif",
+ "(V'ir" },
+{ "Verdana,serif",
+ "cynlrq" },
+{ "Verdana,serif",
+ "vg," },
+{ "Verdana,serif",
+ "xabj)." },
+{ "Verdana,serif",
+ "rneyvre," },
+{ "Verdana,serif",
+ "purpx" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jrfgjbbq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "pnyyf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "grfgre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ebyr-Cynlvat" },
+{ "Verdana,serif",
+ "whzcvat" },
+{ "Verdana,serif",
+ "erzvaqre" },
+{ "Verdana,serif",
+ "onaqjntba. " },
+{ "Verdana,serif",
+ "Jrfgjbbq" },
+{ "Verdana,serif",
+ "npprcgvat" },
+{ "Verdana,serif",
+ "nccyvpngvbaf" },
+{ "Verdana,serif",
+ "wbva" },
+{ "Verdana,serif",
+ "fpvrapr" },
+{ "Verdana,serif",
+ "svpgvba" },
+{ "Verdana,serif",
+ "gurzrq" },
+{ "Verdana,serif",
+ "unfa'g" },
+{ "Verdana,serif",
+ "bssvpvnyyl" },
+{ "Verdana,serif",
+ "naabhaprq). " },
+{ "Verdana,serif",
+ "(npghnyyl" },
+{ "Verdana,serif",
+ "evccrq" },
+{ "Verdana,serif",
+ "Ncnpur" },
+{ "Verdana,serif",
+ "4gu): " },
+{ "Verdana,serif",
+ "Jr'ir" },
+{ "Verdana,serif",
+ "ortha" },
+{ "Verdana,serif",
+ "npprcg" },
+{ "Verdana,serif",
+ "cnegvpvcngr" },
+{ "Verdana,serif",
+ "crefvfgrag" },
+{ "Verdana,serif",
+ "fgngr" },
+{ "Verdana,serif",
+ "vaivgr" },
+{ "Verdana,serif",
+ "nccyl!" },
+{ "Verdana,serif",
+ "vavgvnyyl" },
+{ "Verdana,serif",
+ "fryrpgrq" },
+{ "Verdana,serif",
+ "yvzvgrq" },
+{ "Verdana,serif",
+ "pbagvahrf" },
+{ "Verdana,serif",
+ "nqqvat" },
+{ "Verdana,serif",
+ "cebterffviryl" },
+{ "Verdana,serif",
+ "grfgref." },
+{ "Verdana,serif",
+ "Gurer" },
+{ "Verdana,serif",
+ "cyragl" },
+{ "Verdana,serif",
+ "vaibyirq" },
+{ "Verdana,serif",
+ "genafvgvba" },
+{ "Verdana,serif",
+ "ynetre" },
+{ "Verdana,serif",
+ "cunfrf" },
+{ "Verdana,serif",
+ "Orgn." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "uvtu-erf?" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:29" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(18)" },
+{ "Verdana,serif",
+ "ohqql" },
+{ "Verdana,serif",
+ "RN" },
+{ "Verdana,serif",
+ "'f" },
+{ "Verdana,serif",
+ "Enl" },
+{ "Verdana,serif",
+ "Zhmlxn" },
+{ "Verdana,serif",
+ "pbafvqrerq" },
+{ "Verdana,serif",
+ "perngvat" },
+{ "Verdana,serif",
+ "uvtu-erf" },
+{ "Verdana,serif",
+ "(800k600)" },
+{ "Verdana,serif",
+ "zbqr" },
+{ "Verdana,serif",
+ "Tngr," },
+{ "Verdana,serif",
+ "Gnyrf" },
+{ "Verdana,serif",
+ "Fjbeq" },
+{ "Verdana,serif",
+ "Pbnfg" },
+{ "Verdana,serif",
+ "Cynarfpncr" },
+{ "Verdana,serif",
+ "Gbezrag. " },
+{ "Verdana,serif",
+ "vg:" },
+{ "Verdana,serif",
+ "vzcbffvoyr," },
+{ "Verdana,serif",
+ "pbafvqrenoyr." },
+{ "Verdana,serif",
+ "fpurqhyrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbadhrfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "genvyre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:26" },
+{ "Verdana,serif",
+ "Qvtvgny" },
+{ "Verdana,serif",
+ "Naivy'f" },
+{ "Verdana,serif",
+ "fcvssl" },
+{ "Verdana,serif",
+ "19" },
+{ "Verdana,serif",
+ "NIV genvyre" },
+{ "Verdana,serif",
+ "fubjvat" },
+{ "Verdana,serif",
+ "pbzcyrgrq" },
+{ "Verdana,serif",
+ "Pbadhrfg:" },
+{ "Verdana,serif",
+ "Sebagvre" },
+{ "Verdana,serif",
+ "Jnef. " },
+{ "Verdana,serif",
+ "'rz" },
+{ "Verdana,serif",
+ "fjvsg" },
+{ "Verdana,serif",
+ "tbzre" },
+{ "Verdana,serif",
+ "(naabhaprq" },
+{ "Verdana,serif",
+ "choyvfu" },
+{ "Verdana,serif",
+ "vg)," },
+{ "Verdana,serif",
+ "xabjf" },
+{ "Verdana,serif",
+ "SC! " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gjrnxntr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:14" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(26)" },
+{ "Verdana,serif",
+ "Havirefr" },
+{ "Verdana,serif",
+ "NQrpnl" },
+{ "Verdana,serif",
+ "fcbggrq" },
+{ "Verdana,serif",
+ "3QErnyzf" },
+{ "Verdana,serif",
+ "pbqr-zbaxrl" },
+{ "Verdana,serif",
+ "Oenaqba" },
+{ "Verdana,serif",
+ "\"TerraZnevar\"" },
+{ "Verdana,serif",
+ "Ervauneg" },
+{ "Verdana,serif",
+ "bcgvzvmngvbaf" },
+{ "Verdana,serif",
+ "(QverpgK" },
+{ "Verdana,serif",
+ "gjrnxf) gung" },
+{ "Verdana,serif",
+ "3QE" },
+{ "Verdana,serif",
+ "fbzrqnl-gb-or-eryrnfrq" },
+{ "Verdana,serif",
+ "Sberire. " },
+{ "Verdana,serif",
+ "(V'yy" },
+{ "Verdana,serif",
+ "evc" },
+{ "Verdana,serif",
+ "fghss" },
+{ "Verdana,serif",
+ "HH" },
+{ "Verdana,serif",
+ "evccrq," },
+{ "Verdana,serif",
+ "onat-hc" },
+{ "Verdana,serif",
+ "wbo" },
+{ "Verdana,serif",
+ "favccvat" },
+{ "Verdana,serif",
+ "fghss):" },
+{ "Verdana,serif",
+ "vzcyvrf" },
+{ "Verdana,serif",
+ "urer," },
+{ "Verdana,serif",
+ "sbphf" },
+{ "Verdana,serif",
+ "QK8" },
+{ "Verdana,serif",
+ "zbqvsvpngvbaf" },
+{ "Verdana,serif",
+ "arprffnel" },
+{ "Verdana,serif",
+ "npuvrir" },
+{ "Verdana,serif",
+ "senzrengr." },
+{ "Verdana,serif",
+ "znqr" },
+{ "Verdana,serif",
+ "FVTAVSVPNAG" },
+{ "Verdana,serif",
+ "punatrf" },
+{ "Verdana,serif",
+ "ratvar'f" },
+{ "Verdana,serif",
+ "fcna" },
+{ "Verdana,serif",
+ "ohssre" },
+{ "Verdana,serif",
+ "bppyhfvba" },
+{ "Verdana,serif",
+ "cvcryvar" },
+{ "Verdana,serif",
+ "nqinagntr" },
+{ "Verdana,serif",
+ "QK" },
+{ "Verdana,serif",
+ "uneqjner" },
+{ "Verdana,serif",
+ "genafsbez." },
+{ "Verdana,serif",
+ "-----" },
+{ "Verdana,serif",
+ "PCH" },
+{ "Verdana,serif",
+ "obhaq," },
+{ "Verdana,serif",
+ "bcgvzvmngvba" },
+{ "Verdana,serif",
+ "eryrnfvat" },
+{ "Verdana,serif",
+ "grafvba." },
+{ "Verdana,serif",
+ "rknzcyr:" },
+{ "Verdana,serif",
+ "hfrf" },
+{ "Verdana,serif",
+ "ohssrevat" },
+{ "Verdana,serif",
+ "bppyhfvba." },
+{ "Verdana,serif",
+ "nccebnpu" },
+{ "Verdana,serif",
+ "orarsvgf," },
+{ "Verdana,serif",
+ "vagrafvir." },
+{ "Verdana,serif",
+ "fnirq" },
+{ "Verdana,serif",
+ "senzrengr" },
+{ "Verdana,serif",
+ "erqhpvat" },
+{ "Verdana,serif",
+ "pbzcyrkvgl" },
+{ "Verdana,serif",
+ "pnyphyngvbaf" },
+{ "Verdana,serif",
+ "erdhverq" },
+{ "Verdana,serif",
+ "pnyphyngvbaf." },
+{ "Verdana,serif",
+ "haeryngrq" },
+{ "Verdana,serif",
+ "eraqre" },
+{ "Verdana,serif",
+ "NCV" },
+{ "Verdana,serif",
+ "ynlre." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zber" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:36" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(29)" },
+{ "Verdana,serif",
+ "Jubn," },
+{ "Verdana,serif",
+ "yhor" },
+{ "Verdana,serif",
+ "qnl? " },
+{ "Verdana,serif",
+ "cerirag" },
+{ "Verdana,serif",
+ "enfu" },
+{ "Verdana,serif",
+ "unq..." },
+{ "Verdana,serif",
+ "Jvaqbjf" },
+{ "Verdana,serif",
+ "2000" },
+{ "Verdana,serif",
+ "FC2" },
+{ "Verdana,serif",
+ "Oht" },
+{ "Verdana,serif",
+ "Svk" },
+{ "Verdana,serif",
+ "Yvfg" },
+{ "Verdana,serif",
+ "NpgvirJva" },
+{ "Verdana,serif",
+ "sebz Zvpebfbsg'f" },
+{ "Verdana,serif",
+ "FC2" },
+{ "Verdana,serif",
+ "Jvaqbjf" },
+{ "Verdana,serif",
+ "2000. Ab" },
+{ "Verdana,serif",
+ "freivpr" },
+{ "Verdana,serif",
+ "cnpx," },
+{ "Verdana,serif",
+ "qrnq" },
+{ "Verdana,serif",
+ "png" },
+{ "Verdana,serif",
+ "ng!" },
+{ "Verdana,serif",
+ "infg" },
+{ "Verdana,serif",
+ "znwbevgl" },
+{ "Verdana,serif",
+ "uneqjner," },
+{ "Verdana,serif",
+ "qevire," },
+{ "Verdana,serif",
+ "argjbexvat" },
+{ "Verdana,serif",
+ "frphevgl" },
+{ "Verdana,serif",
+ "ceboyrzf. " },
+{ "Verdana,serif",
+ "freirf," },
+{ "Verdana,serif",
+ "cnpx" },
+{ "Verdana,serif",
+ "qhr" },
+{ "Verdana,serif",
+ "zvq" },
+{ "Verdana,serif",
+ "yngr" },
+{ "Verdana,serif",
+ "Wnahnel," },
+{ "Verdana,serif",
+ "nygubhtu" },
+{ "Verdana,serif",
+ "cevingr" },
+{ "Verdana,serif",
+ "(1)" },
+{ "Verdana,serif",
+ "fuvccrq" },
+{ "Verdana,serif",
+ "jrrx. " },
+{ "Verdana,serif",
+ "Arj" },
+{ "Verdana,serif",
+ "gjrnxrq" },
+{ "Verdana,serif",
+ "TrSbepr" },
+{ "Verdana,serif",
+ "qeviref" },
+{ "Verdana,serif",
+ "FA" },
+{ "Verdana,serif",
+ "Snpuzna" },
+{ "Verdana,serif",
+ "i3.1" },
+{ "Verdana,serif",
+ "(2.4" },
+{ "Verdana,serif",
+ "zrtf)" },
+{ "Verdana,serif",
+ "gjrnxrq" },
+{ "Verdana,serif",
+ "TrSbepr" },
+{ "Verdana,serif",
+ "qeviref." },
+{ "Verdana,serif",
+ "Cynlfgngvba" },
+{ "Verdana,serif",
+ "2" },
+{ "Verdana,serif",
+ "qvfnccbvagzragf." },
+{ "Verdana,serif",
+ "ZFAOP" },
+{ "Verdana,serif",
+ " (Gunaxf" },
+{ "Verdana,serif",
+ "Funpx" },
+{ "Verdana,serif",
+ ")," },
+{ "Verdana,serif",
+ "cvpxrq" },
+{ "Verdana,serif",
+ "bireyl" },
+{ "Verdana,serif",
+ "vzcerffrq. " },
+{ "Verdana,serif",
+ "!" },
+{ "Verdana,serif",
+ " N" },
+{ "Verdana,serif",
+ "favc:" },
+{ "Verdana,serif",
+ "\"NYY-VA-BAR" },
+{ "Verdana,serif",
+ "TVMZB" },
+{ "Verdana,serif",
+ "zbivr-dhnyvgl" },
+{ "Verdana,serif",
+ "tencuvpf." },
+{ "Verdana,serif",
+ "Wncnarfr" },
+{ "Verdana,serif",
+ "tnzref" },
+{ "Verdana,serif",
+ "znpuvar\xe2""\x80""\x99""f" },
+{ "Verdana,serif",
+ "svefg" },
+{ "Verdana,serif",
+ "snaf," },
+{ "Verdana,serif",
+ "fanccvat" },
+{ "Verdana,serif",
+ "zvyyvba" },
+{ "Verdana,serif",
+ "jrrx" },
+{ "Verdana,serif",
+ "vg" },
+{ "Verdana,serif",
+ "jrag" },
+{ "Verdana,serif",
+ "fnyr." },
+{ "Verdana,serif",
+ "Abg" },
+{ "Verdana,serif",
+ "nalzber." },
+{ "Verdana,serif",
+ "gvgyr" },
+{ "Verdana,serif",
+ "yngr-Abirzore" },
+{ "Verdana,serif",
+ "pbasrerapr" },
+{ "Verdana,serif",
+ "geraql" },
+{ "Verdana,serif",
+ "pbssrrubhfr" },
+{ "Verdana,serif",
+ "Gbxlb" },
+{ "Verdana,serif",
+ "fnvq" },
+{ "Verdana,serif",
+ "nyy:" },
+{ "Verdana,serif",
+ "'Jung" },
+{ "Verdana,serif",
+ "Unccrarq" },
+{ "Verdana,serif",
+ "CynlFgngvba" },
+{ "Verdana,serif",
+ "2?'\"" },
+{ "Verdana,serif",
+ "BcraTY" },
+{ "Verdana,serif",
+ "onaq" },
+{ "Verdana,serif",
+ "&" },
+{ "Verdana,serif",
+ "qnapr" },
+{ "Verdana,serif",
+ "cyht-va" },
+{ "Verdana,serif",
+ "i2.0" },
+{ "Verdana,serif",
+ "Fcbggrq" },
+{ "Verdana,serif",
+ "Xnenz'f" },
+{ "Verdana,serif",
+ "Tebhc" },
+{ "Verdana,serif",
+ "BcraTY-onfrq" },
+{ "Verdana,serif",
+ "cyht-va" },
+{ "Verdana,serif",
+ "JvaNzc" },
+{ "Verdana,serif",
+ "orng" },
+{ "Verdana,serif",
+ "qrgrpgbe" },
+{ "Verdana,serif",
+ "onaq" },
+{ "Verdana,serif",
+ "snibevgr" },
+{ "Verdana,serif",
+ "zhfvp." },
+{ "Verdana,serif",
+ "uhzna" },
+{ "Verdana,serif",
+ "zbqryf," },
+{ "Verdana,serif",
+ "yvtugf" },
+{ "Verdana,serif",
+ "funqvat," },
+{ "Verdana,serif",
+ "onpxtebhaq" },
+{ "Verdana,serif",
+ "qnapref\"" },
+{ "Verdana,serif",
+ "(qnzzvg" },
+{ "Verdana,serif",
+ "ynml). " },
+{ "Verdana,serif",
+ "qvt" },
+{ "Verdana,serif",
+ "cyht-vaf!" },
+{ "Verdana,serif",
+ "Ivbyrapr" },
+{ "Verdana,serif",
+ "Onyqhe'f" },
+{ "Verdana,serif",
+ "Tngr" },
+{ "Verdana,serif",
+ "2," },
+{ "Verdana,serif",
+ "jung'f" },
+{ "Verdana,serif",
+ "pbaarpgvba?" },
+{ "Verdana,serif",
+ "OvbJner'f" },
+{ "Verdana,serif",
+ "zragvbaf" },
+{ "Verdana,serif",
+ "vasyhraprq" },
+{ "Verdana,serif",
+ "ivbyrapr" },
+{ "Verdana,serif",
+ "(ivbyrapr)" },
+{ "Verdana,serif",
+ "ubeevoyr" },
+{ "Verdana,serif",
+ "qrfpevcgvba...V" },
+{ "Verdana,serif",
+ "rng" },
+{ "Verdana,serif",
+ "Jurngvrf" },
+{ "Verdana,serif",
+ "gbqnl." },
+{ "Verdana,serif",
+ "Nagubal" },
+{ "Verdana,serif",
+ "(2.71)" },
+{ "Verdana,serif",
+ "ZC3," },
+{ "Verdana,serif",
+ "PQ" },
+{ "Verdana,serif",
+ "nhqvb" },
+{ "Verdana,serif",
+ "sbezngf" },
+{ "Verdana,serif",
+ "cynlre," },
+{ "Verdana,serif",
+ "Jvanzc." },
+{ "Verdana,serif",
+ "Qvnoyb" },
+{ "Verdana,serif",
+ "Rkcnafvba" },
+{ "Verdana,serif",
+ "ACP" },
+{ "Verdana,serif",
+ "erirnyrq" },
+{ "Verdana,serif",
+ "CynargQvnoyb" },
+{ "Verdana,serif",
+ "erirnyrq" },
+{ "Verdana,serif",
+ "Naln" },
+{ "Verdana,serif",
+ "ACP" },
+{ "Verdana,serif",
+ "\"Ivqrb" },
+{ "Verdana,serif",
+ "Tnzrf" },
+{ "Verdana,serif",
+ "ner" },
+{ "Verdana,serif",
+ "'haurnygul'" },
+{ "Verdana,serif",
+ "tveyf\"" },
+{ "Verdana,serif",
+ "Zna," },
+{ "Verdana,serif",
+ "raynetr" },
+{ "Verdana,serif",
+ "Ynen" },
+{ "Verdana,serif",
+ "Pebsg'f" },
+{ "Verdana,serif",
+ "obbof" },
+{ "Verdana,serif",
+ "(cvpgher" },
+{ "Verdana,serif",
+ "gbc)" },
+{ "Verdana,serif",
+ "zr?" },
+{ "Verdana,serif",
+ "cbvagf" },
+{ "Verdana,serif",
+ "(nobhg" },
+{ "Verdana,serif",
+ "obbof," },
+{ "Verdana,serif",
+ "fgrerbglcvat" },
+{ "Verdana,serif",
+ "jnl)...jubbcf" },
+{ "Verdana,serif",
+ "Cuvy!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Oynqr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnexarff" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "9:05" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(17)" },
+{ "Verdana,serif",
+ "TnzrFrrx" },
+{ "Verdana,serif",
+ "ybivat" },
+{ "Verdana,serif",
+ "Whna" },
+{ "Verdana,serif",
+ "Qvnm-Ohfgnznagr," },
+{ "Verdana,serif",
+ "znantre" },
+{ "Verdana,serif",
+ "EroryNpg" },
+{ "Verdana,serif",
+ "funec" },
+{ "Verdana,serif",
+ "(ab" },
+{ "Verdana,serif",
+ "vagraqrq)" },
+{ "Verdana,serif",
+ "bire-gur-fubhyqre" },
+{ "Verdana,serif",
+ "Oynqr" },
+{ "Verdana,serif",
+ "Qnexarff." },
+{ "Verdana,serif",
+ "fvkgrra" },
+{ "Verdana,serif",
+ "fperra" },
+{ "Verdana,serif",
+ "npgvba." },
+{ "Verdana,serif",
+ "phg" },
+{ "Verdana,serif",
+ "Jung" },
+{ "Verdana,serif",
+ "znxrf" },
+{ "Verdana,serif",
+ "guvf" },
+{ "Verdana,serif",
+ "fgnaq" },
+{ "Verdana,serif",
+ "bhg" },
+{ "Verdana,serif",
+ "sebz" },
+{ "Verdana,serif",
+ "bgure" },
+{ "Verdana,serif",
+ "tnzrf" },
+{ "Verdana,serif",
+ "uvf" },
+{ "Verdana,serif",
+ "traer?" },
+{ "Verdana,serif",
+ "WQO:" },
+{ "Verdana,serif",
+ "Ernyvfz." },
+{ "Verdana,serif",
+ "srryvat" },
+{ "Verdana,serif",
+ "vafvqr" },
+{ "Verdana,serif",
+ "funqbjf" },
+{ "Verdana,serif",
+ "yvtugavat," },
+{ "Verdana,serif",
+ "vagrenpgvivgl" },
+{ "Verdana,serif",
+ "bowrpg" },
+{ "Verdana,serif",
+ "nebhaq," },
+{ "Verdana,serif",
+ "culfvpf," },
+{ "Verdana,serif",
+ "orunivbhe" },
+{ "Verdana,serif",
+ "rarzvrf," },
+{ "Verdana,serif",
+ "rgp..." },
+{ "Verdana,serif",
+ "bevragngrq" },
+{ "Verdana,serif",
+ "nguzbfcurevp" },
+{ "Verdana,serif",
+ "raivebzrag." },
+{ "Verdana,serif",
+ "gvzr," },
+{ "Verdana,serif",
+ "fjbeqf" },
+{ "Verdana,serif",
+ "cerff" },
+{ "Verdana,serif",
+ "uvggvat" },
+{ "Verdana,serif",
+ "rarzvrf." },
+{ "Verdana,serif",
+ "Pbzong" },
+{ "Verdana,serif",
+ "ivrj," },
+{ "Verdana,serif",
+ "punyyratvat." },
+{ "Verdana,serif",
+ "arrqf" },
+{ "Verdana,serif",
+ "znfgre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cbjrecynl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "hcqngr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:59" },
+{ "Verdana,serif",
+ "Qbrf" },
+{ "Verdana,serif",
+ "nalbar" },
+{ "Verdana,serif",
+ "\"Cbjrecynl\"" },
+{ "Verdana,serif",
+ "Inyir" },
+{ "Verdana,serif",
+ "fgnaqneqf" },
+{ "Verdana,serif",
+ "Vagrearg" },
+{ "Verdana,serif",
+ "cynl?" },
+{ "Verdana,serif",
+ "Jryy" },
+{ "Verdana,serif",
+ "Znggurj" },
+{ "Verdana,serif",
+ "Ybqtr," },
+{ "Verdana,serif",
+ "cebqhpg" },
+{ "Verdana,serif",
+ "znexrgvat" },
+{ "Verdana,serif",
+ "Inyir'f" },
+{ "Verdana,serif",
+ "cnegare" },
+{ "Verdana,serif",
+ "CbjreCynl," },
+{ "Verdana,serif",
+ "Pvfpb" },
+{ "Verdana,serif",
+ "Flfgrzf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Phfgbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "N.V." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "va" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fnpevsvpr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:39" },
+{ "Verdana,serif",
+ "Fbcuvfgel," },
+{ "Verdana,serif",
+ "(Znegva)" },
+{ "Verdana,serif",
+ "cebtenzzre" },
+{ "Verdana,serif",
+ "vasb" },
+{ "Verdana,serif",
+ "phfgbz" },
+{ "Verdana,serif",
+ "fpevcgf" },
+{ "Verdana,serif",
+ "jbeq:" },
+{ "Verdana,serif",
+ "yvfgf." },
+{ "Verdana,serif",
+ "npgvbaf" },
+{ "Verdana,serif",
+ "(uneqpbqrq)" },
+{ "Verdana,serif",
+ "rahzrengrq" },
+{ "Verdana,serif",
+ "rinyhngrq" },
+{ "Verdana,serif",
+ "pheerag" },
+{ "Verdana,serif",
+ "xabjyrqtr" },
+{ "Verdana,serif",
+ "onfr," },
+{ "Verdana,serif",
+ "pubbfrf" },
+{ "Verdana,serif",
+ "nccebcevngr" },
+{ "Verdana,serif",
+ "npgvba(f)" },
+{ "Verdana,serif",
+ "vffhrf" },
+{ "Verdana,serif",
+ "beqref" },
+{ "Verdana,serif",
+ "pneel" },
+{ "Verdana,serif",
+ "bhg." },
+{ "Verdana,serif",
+ "jvmneq" },
+{ "Verdana,serif",
+ "fgngf" },
+{ "Verdana,serif",
+ "pbqrq," },
+{ "Verdana,serif",
+ "ragevrf" },
+{ "Verdana,serif",
+ "qngnonfr" },
+{ "Verdana,serif",
+ "jvmneqf," },
+{ "Verdana,serif",
+ "bireevqqra." },
+{ "Verdana,serif",
+ "rafher" },
+{ "Verdana,serif",
+ "snve," },
+{ "Verdana,serif",
+ "zngpurq" },
+{ "Verdana,serif",
+ "rkcrevrapr." },
+{ "Verdana,serif",
+ "rqvg," },
+{ "Verdana,serif",
+ "ersenva" },
+{ "Verdana,serif",
+ "fb," },
+{ "Verdana,serif",
+ "ahzore," },
+{ "Verdana,serif",
+ "hanoyr" },
+{ "Verdana,serif",
+ "ntnvafg" },
+{ "Verdana,serif",
+ "hayrff" },
+{ "Verdana,serif",
+ "qngnonfr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vaql" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jne" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "8:01" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(7)" },
+{ "Verdana,serif",
+ "unccravat" },
+{ "Verdana,serif",
+ "TN-FVZ" },
+{ "Verdana,serif",
+ "qvfurq" },
+{ "Verdana,serif",
+ "Vaqrcraqrapr" },
+{ "Verdana,serif",
+ "Cnegvpyr" },
+{ "Verdana,serif",
+ "Flfgrzf" },
+{ "Verdana,serif",
+ "cebtenzzref," },
+{ "Verdana,serif",
+ "Inyr" },
+{ "Verdana,serif",
+ "Zvpunry" },
+{ "Verdana,serif",
+ "Cbjryy." },
+{ "Verdana,serif",
+ "jul:" },
+{ "Verdana,serif",
+ "Gryy" },
+{ "Verdana,serif",
+ "hf" },
+{ "Verdana,serif",
+ "zber" },
+{ "Verdana,serif",
+ "nobhg" },
+{ "Verdana,serif",
+ "ynfgrfg" },
+{ "Verdana,serif",
+ "ratvar" },
+{ "Verdana,serif",
+ "juvpu" },
+{ "Verdana,serif",
+ "V-Jne" },
+{ "Verdana,serif",
+ "onfrq." },
+{ "Verdana,serif",
+ "vgf" },
+{ "Verdana,serif",
+ "pncnovyvgvrf" },
+{ "Verdana,serif",
+ "ubj" },
+{ "Verdana,serif",
+ "unf" },
+{ "Verdana,serif",
+ "vg" },
+{ "Verdana,serif",
+ "nyybjrq" },
+{ "Verdana,serif",
+ "rkcnaq" },
+{ "Verdana,serif",
+ "orlbaq" },
+{ "Verdana,serif",
+ "fpbcr" },
+{ "Verdana,serif",
+ "bevtvany" },
+{ "Verdana,serif",
+ "gvgyr?" },
+{ "Verdana,serif",
+ "V-Jne" },
+{ "Verdana,serif",
+ "ragveryl" },
+{ "Verdana,serif",
+ "Syhk." },
+{ "Verdana,serif",
+ "arkg-trarengvba" },
+{ "Verdana,serif",
+ "uneqjner." },
+{ "Verdana,serif",
+ "srngherf," },
+{ "Verdana,serif",
+ "Qverpg3Q" },
+{ "Verdana,serif",
+ "grkgher" },
+{ "Verdana,serif",
+ "pbzcerffvba" },
+{ "Verdana,serif",
+ "nppryrengrq" },
+{ "Verdana,serif",
+ "G&Y." },
+{ "Verdana,serif",
+ "prager" },
+{ "Verdana,serif",
+ "Jvgu" },
+{ "Verdana,serif",
+ "Syhk," },
+{ "Verdana,serif",
+ "evpure" },
+{ "Verdana,serif",
+ "jbeyq," },
+{ "Verdana,serif",
+ "ernpu" },
+{ "Verdana,serif",
+ "gbhpu." },
+{ "Verdana,serif",
+ "fpevcgvat" },
+{ "Verdana,serif",
+ "ynathntr" },
+{ "Verdana,serif",
+ "orunivbhef" },
+{ "Verdana,serif",
+ "zvffvba" },
+{ "Verdana,serif",
+ "fpranevbf." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gbc" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "20" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fryyvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnzrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "7:56" },
+{ "Verdana,serif",
+ "TnzrJrrx" },
+{ "Verdana,serif",
+ "GEFG" },
+{ "Verdana,serif",
+ "fnyrf" },
+{ "Verdana,serif",
+ "svtherf" },
+{ "Verdana,serif",
+ "jrrx" },
+{ "Verdana,serif",
+ "11/26" },
+{ "Verdana,serif",
+ "gbqnl," },
+{ "Verdana,serif",
+ "Ebyyrepbnfgre" },
+{ "Verdana,serif",
+ "fanttvat" },
+{ "Verdana,serif",
+ "gbc" },
+{ "Verdana,serif",
+ "fcbg" },
+{ "Verdana,serif",
+ "Fvzf" },
+{ "Verdana,serif",
+ "pbzvat" },
+{ "Verdana,serif",
+ "cynpr." },
+{ "Verdana,serif",
+ "znanatrq" },
+{ "Verdana,serif",
+ "yvfg," },
+{ "Verdana,serif",
+ "Zrpujneevbe" },
+{ "Verdana,serif",
+ "4" },
+{ "Verdana,serif",
+ "fcbgf" },
+{ "Verdana,serif",
+ "ryrira." },
+{ "Verdana,serif",
+ "Bgure" },
+{ "Verdana,serif",
+ "gung," },
+{ "Verdana,serif",
+ "penccl" },
+{ "Verdana,serif",
+ "onetva-ova" },
+{ "Verdana,serif",
+ "fgbpxvat" },
+{ "Verdana,serif",
+ "fghssre" },
+{ "Verdana,serif",
+ "Sebttre" },
+{ "Verdana,serif",
+ "Jub" },
+{ "Verdana,serif",
+ "Jnagf" },
+{ "Verdana,serif",
+ "Zvyyvbaner." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FbS" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "7:15" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(1)" },
+{ "Verdana,serif",
+ "N" },
+{ "Verdana,serif",
+ "Fbyqvre" },
+{ "Verdana,serif",
+ "Sbeghar" },
+{ "Verdana,serif",
+ "ovgf" },
+{ "Verdana,serif",
+ "gb," },
+{ "Verdana,serif",
+ "fhpx" },
+{ "Verdana,serif",
+ "qbja," },
+{ "Verdana,serif",
+ "fbzrguvat. " },
+{ "Verdana,serif",
+ "Gurer'f" },
+{ "Verdana,serif",
+ "znccref" },
+{ "Verdana,serif",
+ "thvyq" },
+{ "Verdana,serif",
+ "FbSPragre.pbz" },
+{ "Verdana,serif",
+ "nppbeqvat" },
+{ "Verdana,serif",
+ "T-Fcbg" },
+{ "Verdana,serif",
+ "Cynlfgngvba" },
+{ "Verdana,serif",
+ "cbeg" },
+{ "Verdana,serif",
+ "FbS" },
+{ "Verdana,serif",
+ "tevyy" },
+{ "Verdana,serif",
+ "Cvcr" },
+{ "Verdana,serif",
+ "Qernz" },
+{ "Verdana,serif",
+ "Vagrenpgvir. " },
+{ "Verdana,serif",
+ "D4" },
+{ "Verdana,serif",
+ "2001. " },
+{ "Verdana,serif",
+ "Qernz," },
+{ "Verdana,serif",
+ "nyfb cbegvat" },
+{ "Verdana,serif",
+ "Iblntre" },
+{ "Verdana,serif",
+ "Sbepr" },
+{ "Verdana,serif",
+ "(whfg" },
+{ "Verdana,serif",
+ "SLZSV)." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OPZ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "greenva" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "grpu." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6:59" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(27)" },
+{ "Verdana,serif",
+ "3000NQ" },
+{ "Verdana,serif",
+ "Qrerx" },
+{ "Verdana,serif",
+ "Fzneg" },
+{ "Verdana,serif",
+ "greenva" },
+{ "Verdana,serif",
+ "Onggyrpehvfre" },
+{ "Verdana,serif",
+ "Zvyyraavhz" },
+{ "Verdana,serif",
+ "Tnynpgvp" },
+{ "Verdana,serif",
+ "Pbzznaq" },
+{ "Verdana,serif",
+ "fcnpr/cynargnel/ECT/svefg-crefba" },
+{ "Verdana,serif",
+ "OPZ" },
+{ "Verdana,serif",
+ "ratvarf). " },
+{ "Verdana,serif",
+ "ryfr" },
+{ "Verdana,serif",
+ "ertneqf" },
+{ "Verdana,serif",
+ "greenva," },
+{ "Verdana,serif",
+ "fyvtugyl" },
+{ "Verdana,serif",
+ "gevzzrq" },
+{ "Verdana,serif",
+ "graqf" },
+{ "Verdana,serif",
+ "ybatjvaqrq):" },
+{ "Verdana,serif",
+ "Gurfr" },
+{ "Verdana,serif",
+ "fcrnx" },
+{ "Verdana,serif",
+ "gurzfryirf." },
+{ "Verdana,serif",
+ "ynaqfpncrf," },
+{ "Verdana,serif",
+ "pybhq" },
+{ "Verdana,serif",
+ "ynlref" },
+{ "Verdana,serif",
+ "(nyy" },
+{ "Verdana,serif",
+ "qlanzvp" },
+{ "Verdana,serif",
+ "v.r" },
+{ "Verdana,serif",
+ "zbir," },
+{ "Verdana,serif",
+ "qbu!)" },
+{ "Verdana,serif",
+ "rgp." },
+{ "Verdana,serif",
+ "tebhaq-mreb" },
+{ "Verdana,serif",
+ "fubg." },
+{ "Verdana,serif",
+ "fhccbegf" },
+{ "Verdana,serif",
+ "glcrf" },
+{ "Verdana,serif",
+ "nygvghqr" },
+{ "Verdana,serif",
+ "enatrf" },
+{ "Verdana,serif",
+ "gvgyrf." },
+{ "Verdana,serif",
+ "vapyhqr" },
+{ "Verdana,serif",
+ "uvtu-nyvghqr" },
+{ "Verdana,serif",
+ "(svtugref," },
+{ "Verdana,serif",
+ "rgp)," },
+{ "Verdana,serif",
+ "ybj-nygvghqr" },
+{ "Verdana,serif",
+ "(ybj" },
+{ "Verdana,serif",
+ "sylvat" },
+{ "Verdana,serif",
+ "thafuvcf)," },
+{ "Verdana,serif",
+ "(qevivat," },
+{ "Verdana,serif",
+ "crefba" },
+{ "Verdana,serif",
+ "rgp)" },
+{ "Verdana,serif",
+ "frnzyrffyl." },
+{ "Verdana,serif",
+ "bguref" },
+{ "Verdana,serif",
+ "gbhgvat" },
+{ "Verdana,serif",
+ "jbeyqf" },
+{ "Verdana,serif",
+ "8XZ" },
+{ "Verdana,serif",
+ "enatrf," },
+{ "Verdana,serif",
+ "cnu!" },
+{ "Verdana,serif",
+ "yrtnpl" },
+{ "Verdana,serif",
+ "grpuaybtl" },
+{ "Verdana,serif",
+ "OP3X" },
+{ "Verdana,serif",
+ "i2.0k" },
+{ "Verdana,serif",
+ "fvmr" },
+{ "Verdana,serif",
+ "400" },
+{ "Verdana,serif",
+ "cynarg," },
+{ "Verdana,serif",
+ "fcnaf" },
+{ "Verdana,serif",
+ "40000xz." },
+{ "Verdana,serif",
+ "gungf" },
+{ "Verdana,serif",
+ "cynarg" },
+{ "Verdana,serif",
+ "Rnegu" },
+{ "Verdana,serif",
+ "yvir" },
+{ "Verdana,serif",
+ "va." },
+{ "Verdana,serif",
+ "Tb" },
+{ "Verdana,serif",
+ "zngu." },
+{ "Verdana,serif",
+ "Qhr" },
+{ "Verdana,serif",
+ "grpuabybtvrf," },
+{ "Verdana,serif",
+ "vzcyrzragrq" },
+{ "Verdana,serif",
+ "Orgn" },
+{ "Verdana,serif",
+ "fbzrgvzr" },
+{ "Verdana,serif",
+ "zbagu." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bssvpvny" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NBR" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "znc" },
+{ "Verdana,serif",
+ "\"Qe." },
+{ "Verdana,serif",
+ "Fchax\"" },
+{ "Verdana,serif",
+ "(url," },
+{ "Verdana,serif",
+ "hc...jryy," },
+{ "Verdana,serif",
+ "fbore" },
+{ "Verdana,serif",
+ "naljnlf)" },
+{ "Verdana,serif",
+ "fgnax" },
+{ "Verdana,serif",
+ "nffrf" },
+{ "Verdana,serif",
+ "Rafrzoyr" },
+{ "Verdana,serif",
+ "Ntr" },
+{ "Verdana,serif",
+ "Rzcverf" },
+{ "Verdana,serif",
+ "Pbadhrebef," },
+{ "Verdana,serif",
+ "\"Pnanyf\". Urer'f" },
+{ "Verdana,serif",
+ "qrfpevcgvba:" },
+{ "Verdana,serif",
+ "abgnoyr" },
+{ "Verdana,serif",
+ "znc," },
+{ "Verdana,serif",
+ "snpg," },
+{ "Verdana,serif",
+ "pnanyf," },
+{ "Verdana,serif",
+ "perngrf" },
+{ "Verdana,serif",
+ "fcvqrejro" },
+{ "Verdana,serif",
+ "rssrpg" },
+{ "Verdana,serif",
+ "pbaarpg" },
+{ "Verdana,serif",
+ "cynlre'f" },
+{ "Verdana,serif",
+ "gbja" },
+{ "Verdana,serif",
+ "trarengr" },
+{ "Verdana,serif",
+ "fcrpgnphyne-ybbxvat" },
+{ "Verdana,serif",
+ "zvavzncf." },
+{ "Verdana,serif",
+ "cynlref," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arirejvagre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6:53" },
+{ "Verdana,serif",
+ "jnkvat" },
+{ "Verdana,serif",
+ "cvtrba" },
+{ "Verdana,serif",
+ "pheeragyl," },
+{ "Verdana,serif",
+ "thrff" },
+{ "Verdana,serif",
+ "V'yy" },
+{ "Verdana,serif",
+ "onol." },
+{ "Verdana,serif",
+ "Npgvba" },
+{ "Verdana,serif",
+ "Gevc," },
+{ "Verdana,serif",
+ "chgf" },
+{ "Verdana,serif",
+ "zrnavat" },
+{ "Verdana,serif",
+ "cuenfr" },
+{ "Verdana,serif",
+ "\"ybbfr" },
+{ "Verdana,serif",
+ "tbbfr\"," },
+{ "Verdana,serif",
+ "Ovbjner'f" },
+{ "Verdana,serif",
+ "ECT" },
+{ "Verdana,serif",
+ "Hfvat" },
+{ "Verdana,serif",
+ "3eq" },
+{ "Verdana,serif",
+ "rqvgvba" },
+{ "Verdana,serif",
+ "ehyrf," },
+{ "Verdana,serif",
+ "fhccbfrq" },
+{ "Verdana,serif",
+ "crapvy" },
+{ "Verdana,serif",
+ "cncre" },
+{ "Verdana,serif",
+ "pbzchgre." },
+{ "Verdana,serif",
+ "fyrj" },
+{ "Verdana,serif",
+ "fjrrg-nff" },
+{ "Verdana,serif",
+ "fperraf" },
+{ "Verdana,serif",
+ "(Lrf," },
+{ "Verdana,serif",
+ "fjrrg)." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "D3N" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cbvag" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "eryrnfr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6:41" },
+{ "Verdana,serif",
+ "vq'f" },
+{ "Verdana,serif",
+ "Gbqq" },
+{ "Verdana,serif",
+ "Ubyyrafurnq" },
+{ "Verdana,serif",
+ "Dhnxr3jbeyq.pbz" },
+{ "Verdana,serif",
+ "sbehzf" },
+{ "Verdana,serif",
+ "i1.27" },
+{ "Verdana,serif",
+ "gbqnl" },
+{ "Verdana,serif",
+ "gbyq" },
+{ "Verdana,serif",
+ "cerivbhfyl)" },
+{ "Verdana,serif",
+ "\"fbba\". " },
+{ "Verdana,serif",
+ "Fcrnxvat" },
+{ "Verdana,serif",
+ "vq," },
+{ "Verdana,serif",
+ "Eboreg" },
+{ "Verdana,serif",
+ "Qhssl" },
+{ "Verdana,serif",
+ "rneyvre" },
+{ "Verdana,serif",
+ "ZS" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Svtugre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Npr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "6:05" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Syvtug" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbzong" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fvzf" },
+{ "Verdana,serif",
+ "Erqzbaq" },
+{ "Verdana,serif",
+ "(ZF) unir" },
+{ "Verdana,serif",
+ "znwbe" },
+{ "Verdana,serif",
+ "Svtugre" },
+{ "Verdana,serif",
+ "Npr" },
+{ "Verdana,serif",
+ "VV," },
+{ "Verdana,serif",
+ "Mbar.pbz'f" },
+{ "Verdana,serif",
+ "(\"zber" },
+{ "Verdana,serif",
+ "300" },
+{ "Verdana,serif",
+ "cvybgf" },
+{ "Verdana,serif",
+ "svtug" },
+{ "Verdana,serif",
+ "nve" },
+{ "Verdana,serif",
+ "neran\")" },
+{ "Verdana,serif",
+ "JJVV" },
+{ "Verdana,serif",
+ "13" },
+{ "Verdana,serif",
+ "ivagntr" },
+{ "Verdana,serif",
+ "svtugref" },
+{ "Verdana,serif",
+ "obzoref" },
+{ "Verdana,serif",
+ "ren," },
+{ "Verdana,serif",
+ "zncf" },
+{ "Verdana,serif",
+ "(Ratyvfu" },
+{ "Verdana,serif",
+ "Punaary" },
+{ "Verdana,serif",
+ "svpgvbany" },
+{ "Verdana,serif",
+ "\"10,000" },
+{ "Verdana,serif",
+ "sbbg" },
+{ "Verdana,serif",
+ "fcverf," },
+{ "Verdana,serif",
+ "anvy-ovgvat" },
+{ "Verdana,serif",
+ "pnalba," },
+{ "Verdana,serif",
+ "qrngu" },
+{ "Verdana,serif",
+ "objy" },
+{ "Verdana,serif",
+ "jnyyf" },
+{ "Verdana,serif",
+ "pnar" },
+{ "Verdana,serif",
+ "rfpncr. " },
+{ "Verdana,serif",
+ "Nybat" },
+{ "Verdana,serif",
+ "tbbqvrf," },
+{ "Verdana,serif",
+ "syvtug" },
+{ "Verdana,serif",
+ "nqwhfgzragf" },
+{ "Verdana,serif",
+ "npphenpl," },
+{ "Verdana,serif",
+ "vzcebirq" },
+{ "Verdana,serif",
+ "ebpxrg" },
+{ "Verdana,serif",
+ "genwrpgbevrf," },
+{ "Verdana,serif",
+ "urnil" },
+{ "Verdana,serif",
+ "znpuvar" },
+{ "Verdana,serif",
+ "tha" },
+{ "Verdana,serif",
+ "Trezna" },
+{ "Verdana,serif",
+ "Havgrq" },
+{ "Verdana,serif",
+ "Fgngrf" },
+{ "Verdana,serif",
+ "sbeprf," },
+{ "Verdana,serif",
+ "ertvfgrerq" },
+{ "Verdana,serif",
+ "fanxr" },
+{ "Verdana,serif",
+ "beqre" },
+{ "Verdana,serif",
+ "pbagvahr" },
+{ "Verdana,serif",
+ "cbbc" },
+{ "Verdana,serif",
+ "yrnea" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Wbua" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ebzreb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:54" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(68)" },
+{ "Verdana,serif",
+ "Pbagvahvat" },
+{ "Verdana,serif",
+ "Qbyyl" },
+{ "Verdana,serif",
+ "(zvyx)" },
+{ "Verdana,serif",
+ "lrne-raq" },
+{ "Verdana,serif",
+ "2000" },
+{ "Verdana,serif",
+ "D&Nf," },
+{ "Verdana,serif",
+ "Qnvxngnan" },
+{ "Verdana,serif",
+ "lbhat," },
+{ "Verdana,serif",
+ "zbarl)," },
+{ "Verdana,serif",
+ "Ebzreb," },
+{ "Verdana,serif",
+ "nobhg," },
+{ "Verdana,serif",
+ "guvaxf" },
+{ "Verdana,serif",
+ "(Qnvxngnan" },
+{ "Verdana,serif",
+ "znlor?!?!?!" },
+{ "Verdana,serif",
+ "qernz...<qevccvat" },
+{ "Verdana,serif",
+ "fnepnfz>. " },
+{ "Verdana,serif",
+ "vagreivrj," },
+{ "Verdana,serif",
+ "cevag" },
+{ "Verdana,serif",
+ "arg" },
+{ "Verdana,serif",
+ "fvgrf" },
+{ "Verdana,serif",
+ "(abg" },
+{ "Verdana,serif",
+ "fnlvat" },
+{ "Verdana,serif",
+ "<pbhtu>," },
+{ "Verdana,serif",
+ "fgvyy). " },
+{ "Verdana,serif",
+ "zbbq," },
+{ "Verdana,serif",
+ "(unq" },
+{ "Verdana,serif",
+ "vg):" },
+{ "Verdana,serif",
+ "Fgbzcrq:" },
+{ "Verdana,serif",
+ "jnf" },
+{ "Verdana,serif",
+ "lrne" },
+{ "Verdana,serif",
+ "yvxr" },
+{ "Verdana,serif",
+ "cnegvphyne" },
+{ "Verdana,serif",
+ "tnzvat" },
+{ "Verdana,serif",
+ "cebwrpgf?" },
+{ "Verdana,serif",
+ "Jrer" },
+{ "Verdana,serif",
+ "unccl" },
+{ "Verdana,serif",
+ "jvgu" },
+{ "Verdana,serif",
+ "erfcbafr" },
+{ "Verdana,serif",
+ "zbfg" },
+{ "Verdana,serif",
+ "erprag" },
+{ "Verdana,serif",
+ "tnzrf?" },
+{ "Verdana,serif",
+ "Vs" },
+{ "Verdana,serif",
+ "qvq" },
+{ "Verdana,serif",
+ "abg" },
+{ "Verdana,serif",
+ "eryrnfr" },
+{ "Verdana,serif",
+ "lrne," },
+{ "Verdana,serif",
+ "jrer" },
+{ "Verdana,serif",
+ "cebterff" },
+{ "Verdana,serif",
+ "znqr" },
+{ "Verdana,serif",
+ "qhevat" },
+{ "Verdana,serif",
+ "pheerag" },
+{ "Verdana,serif",
+ "tnzr'f" },
+{ "Verdana,serif",
+ "qrirybczrag?" },
+{ "Verdana,serif",
+ "Ebzreb:" },
+{ "Verdana,serif",
+ "Jryy," },
+{ "Verdana,serif",
+ "frperg" },
+{ "Verdana,serif",
+ "Qnvxngnan." },
+{ "Verdana,serif",
+ "Fher," },
+{ "Verdana,serif",
+ "ceboyrzf," },
+{ "Verdana,serif",
+ "jneenag" },
+{ "Verdana,serif",
+ "ibenpvgl" },
+{ "Verdana,serif",
+ "erivrjf." },
+{ "Verdana,serif",
+ "gubhtu," },
+{ "Verdana,serif",
+ "qrprag" },
+{ "Verdana,serif",
+ "Vba" },
+{ "Verdana,serif",
+ "Fgbez." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "thgf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Vaqerzn" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:52" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qnir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "\"Bpgnar\"" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zbeevfba" },
+{ "Verdana,serif",
+ "xvqf" },
+{ "Verdana,serif",
+ "Ubjfghssjbexf.pbz" },
+{ "Verdana,serif",
+ "(znlor" },
+{ "Verdana,serif",
+ "jbex..." },
+{ "Verdana,serif",
+ "gurl'q" },
+{ "Verdana,serif",
+ "snzbhf" },
+{ "Verdana,serif",
+ "=)" },
+{ "Verdana,serif",
+ "uboovrfg" },
+{ "Verdana,serif",
+ "cebhq," },
+{ "Verdana,serif",
+ "Vaqerzn" },
+{ "Verdana,serif",
+ "Tnzr" },
+{ "Verdana,serif",
+ "Pbafbyr." },
+{ "Verdana,serif",
+ "Ab" },
+{ "Verdana,serif",
+ "qbhog" },
+{ "Verdana,serif",
+ "erthyne" },
+{ "Verdana,serif",
+ "ernqre" },
+{ "Verdana,serif",
+ "zragvba" },
+{ "Verdana,serif",
+ "orsber," },
+{ "Verdana,serif",
+ "vasb." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "FgneXavtugf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:31" },
+{ "Verdana,serif",
+ "Fgengrtl" },
+{ "Verdana,serif",
+ "XavtugFbsg'f" },
+{ "Verdana,serif",
+ "ghea-onfrq" },
+{ "Verdana,serif",
+ "FgneXavtugf. " },
+{ "Verdana,serif",
+ "\"rkpyhfvir\"" },
+{ "Verdana,serif",
+ "genvyre" },
+{ "Verdana,serif",
+ "(4" },
+{ "Verdana,serif",
+ "zrtf) sbe" },
+{ "Verdana,serif",
+ "\"vagrefgryyne" },
+{ "Verdana,serif",
+ "pbadhrfg\". " },
+{ "Verdana,serif",
+ "gvqovg" },
+{ "Verdana,serif",
+ "FgneXavtugf" },
+{ "Verdana,serif",
+ "pbadhrfg." },
+{ "Verdana,serif",
+ "pbageby" },
+{ "Verdana,serif",
+ "flfgrzf" },
+{ "Verdana,serif",
+ "rkgenpg" },
+{ "Verdana,serif",
+ "erfbheprf" },
+{ "Verdana,serif",
+ "tebj" },
+{ "Verdana,serif",
+ "rzcver." },
+{ "Verdana,serif",
+ "fgnecbegf" },
+{ "Verdana,serif",
+ "nernf" },
+{ "Verdana,serif",
+ "arrq" },
+{ "Verdana,serif",
+ "gurz," },
+{ "Verdana,serif",
+ "cynargnel" },
+{ "Verdana,serif",
+ "qrsrafrf" },
+{ "Verdana,serif",
+ "qrsraq" },
+{ "Verdana,serif",
+ "vainqref." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "yhovatf..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:27" },
+{ "Verdana,serif",
+ "zvfp." },
+{ "Verdana,serif",
+ "zbeavat..." },
+{ "Verdana,serif",
+ "Jnembar" },
+{ "Verdana,serif",
+ "Bayvar" },
+{ "Verdana,serif",
+ "fubgf" },
+{ "Verdana,serif",
+ "NInhyg" },
+{ "Verdana,serif",
+ "Cnenqbk" },
+{ "Verdana,serif",
+ "Ragregnvazrag" },
+{ "Verdana,serif",
+ "Jnembar" },
+{ "Verdana,serif",
+ "Qnex" },
+{ "Verdana,serif",
+ "Ntr" },
+{ "Verdana,serif",
+ "Pnzrybg" },
+{ "Verdana,serif",
+ "Tvyqrajro" },
+{ "Verdana,serif",
+ "Zlguvp'f" },
+{ "Verdana,serif",
+ "ZZBECT," },
+{ "Verdana,serif",
+ "Qnex" },
+{ "Verdana,serif",
+ "Pnzrybg" },
+{ "Verdana,serif",
+ "(frg" },
+{ "Verdana,serif",
+ "Xvat" },
+{ "Verdana,serif",
+ "Neguhe" },
+{ "Verdana,serif",
+ "zvtug" },
+{ "Verdana,serif",
+ "fhttrfg)." },
+{ "Verdana,serif",
+ "Zbeebjvaq" },
+{ "Verdana,serif",
+ "Rqvgbe" },
+{ "Verdana,serif",
+ "Qrfgvangvba" },
+{ "Verdana,serif",
+ "Zbeebjvaq" },
+{ "Verdana,serif",
+ "frkl" },
+{ "Verdana,serif",
+ "Npgvba" },
+{ "Verdana,serif",
+ "Unys-Yvsr" },
+{ "Verdana,serif",
+ "gnxrf" },
+{ "Verdana,serif",
+ "qvir" },
+{ "Verdana,serif",
+ "CUY" },
+{ "Verdana,serif",
+ "UY" },
+{ "Verdana,serif",
+ "fubjpnfvat" },
+{ "Verdana,serif",
+ "\"qvir\"" },
+{ "Verdana,serif",
+ "orgn4," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgne" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jnef" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tnynkvrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:19" },
+{ "Verdana,serif",
+ "Irenag" },
+{ "Verdana,serif",
+ ")" },
+{ "Verdana,serif",
+ "fraqvat" },
+{ "Verdana,serif",
+ "Jnef" },
+{ "Verdana,serif",
+ "Tnynkvrf" },
+{ "Verdana,serif",
+ "Qrirybcre'f" },
+{ "Verdana,serif",
+ "Wbheany-glcr" },
+{ "Verdana,serif",
+ "(sbehzf" },
+{ "Verdana,serif",
+ "cbfg)" },
+{ "Verdana,serif",
+ "guvatnznwvt" },
+{ "Verdana,serif",
+ "wboovr" },
+{ "Verdana,serif",
+ "pbj" },
+{ "Verdana,serif",
+ "sbehzf. " },
+{ "Verdana,serif",
+ "eryrnfrf," },
+{ "Verdana,serif",
+ "vfa'g" },
+{ "Verdana,serif",
+ "ohg," },
+{ "Verdana,serif",
+ "zr," },
+{ "Verdana,serif",
+ "fcrrq. " },
+{ "Verdana,serif",
+ "QBRF" },
+{ "Verdana,serif",
+ "qvssref" },
+{ "Verdana,serif",
+ "Riredhrfg" },
+{ "Verdana,serif",
+ "\"Riredhrfg" },
+{ "Verdana,serif",
+ "Fcnpr\"," },
+{ "Verdana,serif",
+ "CiC" },
+{ "Verdana,serif",
+ "arjf," },
+{ "Verdana,serif",
+ "bu-fb" },
+{ "Verdana,serif",
+ "zber. " },
+{ "Verdana,serif",
+ "Nygubhtu," },
+{ "Verdana,serif",
+ "cntr," },
+{ "Verdana,serif",
+ "cerivbhfyl." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "XhS" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "-" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "TBQ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "vf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "abg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "cyrnfrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:06" },
+{ "Verdana,serif",
+ "Lrfgreqnl" },
+{ "Verdana,serif",
+ "ercbegrq" },
+{ "Verdana,serif",
+ "TnzrfQbznva" },
+{ "Verdana,serif",
+ "rkpyhfvir" },
+{ "Verdana,serif",
+ "Xvatqbz" },
+{ "Verdana,serif",
+ "Haqre" },
+{ "Verdana,serif",
+ "Sver" },
+{ "Verdana,serif",
+ "Qrzb" },
+{ "Verdana,serif",
+ "zhfg" },
+{ "Verdana,serif",
+ "ybirq" },
+{ "Verdana,serif",
+ ".." },
+{ "Verdana,serif",
+ "penpx" },
+{ "Verdana,serif",
+ "nqqvpgvir" },
+{ "Verdana,serif",
+ "cnpxf" },
+{ "Verdana,serif",
+ "zvaq" },
+{ "Verdana,serif",
+ "oybjvat" },
+{ "Verdana,serif",
+ "chapu)." },
+{ "Verdana,serif",
+ "Naljnlf," },
+{ "Verdana,serif",
+ "frrzf" },
+{ "Verdana,serif",
+ "npghny" },
+{ "Verdana,serif",
+ "Cunagntenz" },
+{ "Verdana,serif",
+ "fraq" },
+{ "Verdana,serif",
+ "qverpgyl" },
+{ "Verdana,serif",
+ "xvqqvrf" },
+{ "Verdana,serif",
+ "cebcre" },
+{ "Verdana,serif",
+ "nhgubevmvba" },
+{ "Verdana,serif",
+ "TBQ" },
+{ "Verdana,serif",
+ "(Jngpu" },
+{ "Verdana,serif",
+ "bhg!)" },
+{ "Verdana,serif",
+ "\"bssvpvny\"" },
+{ "Verdana,serif",
+ "yvax" },
+{ "Verdana,serif",
+ "\"ha-bssvpvny\"" },
+{ "Verdana,serif",
+ "qrzb." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "hcqngrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Uvgzna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5:05" },
+{ "Verdana,serif",
+ "Pbagenel" },
+{ "Verdana,serif",
+ "ercbegf," },
+{ "Verdana,serif",
+ "fgrnygul/guvaxre/fubbgre," },
+{ "Verdana,serif",
+ "Uvgzna:" },
+{ "Verdana,serif",
+ "Pbqranzr" },
+{ "Verdana,serif",
+ "47" },
+{ "Verdana,serif",
+ "\"jvguva" },
+{ "Verdana,serif",
+ "jrrx\". " },
+{ "Verdana,serif",
+ "fgrnzvat" },
+{ "Verdana,serif",
+ "ahttrg" },
+{ "Verdana,serif",
+ "Oelna" },
+{ "Verdana,serif",
+ "Qnivrf. " },
+{ "Verdana,serif",
+ "frrzrq" },
+{ "Verdana,serif",
+ "grpu" },
+{ "Verdana,serif",
+ "qrzb/orgn" },
+{ "Verdana,serif",
+ "ntrf" },
+{ "Verdana,serif",
+ "bx," },
+{ "Verdana,serif",
+ "ntb)," },
+{ "Verdana,serif",
+ "ercerfragngvir" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "NVGQ" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:52" },
+{ "Verdana,serif",
+ "TN-Fbhepr" },
+{ "Verdana,serif",
+ "lnaxrq" },
+{ "Verdana,serif",
+ "ohfvarff" },
+{ "Verdana,serif",
+ "Nagbvar" },
+{ "Verdana,serif",
+ "Ivyyrggr" },
+{ "Verdana,serif",
+ "Thvyynhzr" },
+{ "Verdana,serif",
+ "Tbhenhq" },
+{ "Verdana,serif",
+ "Qnexjbexf" },
+{ "Verdana,serif",
+ "Fghqvb" },
+{ "Verdana,serif",
+ "chyyrq" },
+{ "Verdana,serif",
+ "Nybar" },
+{ "Verdana,serif",
+ "(Gur" },
+{ "Verdana,serif",
+ "pynffvpf" },
+{ "Verdana,serif",
+ "fcnjarq" },
+{ "Verdana,serif",
+ "Erfvqrag" },
+{ "Verdana,serif",
+ "Rivy" },
+{ "Verdana,serif",
+ "Abpghear)." },
+{ "Verdana,serif",
+ "rneyl-zbeava'" },
+{ "Verdana,serif",
+ "TN-Fbhepr" },
+{ "Verdana,serif",
+ "traer" },
+{ "Verdana,serif",
+ "pngrtbevmr" },
+{ "Verdana,serif",
+ "tnzr?" },
+{ "Verdana,serif",
+ "fvzvyne" },
+{ "Verdana,serif",
+ "znexrg?" },
+{ "Verdana,serif",
+ "vasyhraprf" },
+{ "Verdana,serif",
+ "Qnexjbexf" },
+{ "Verdana,serif",
+ "Fghqvb" },
+{ "Verdana,serif",
+ "Qnex:" },
+{ "Verdana,serif",
+ "Avtugzner" },
+{ "Verdana,serif",
+ "nqiragher/fheiviny-ubeebe" },
+{ "Verdana,serif",
+ "srne" },
+{ "Verdana,serif",
+ "nathvfu." },
+{ "Verdana,serif",
+ "checbfr" },
+{ "Verdana,serif",
+ "gbgny)" },
+{ "Verdana,serif",
+ "rkcrevrapr," },
+{ "Verdana,serif",
+ "erzbivat" },
+{ "Verdana,serif",
+ "rzbgvbaf" },
+{ "Verdana,serif",
+ "yvzvgrq," },
+{ "Verdana,serif",
+ "vzzrefr" },
+{ "Verdana,serif",
+ "vzntvanel" },
+{ "Verdana,serif",
+ "oryvrinoyr" },
+{ "Verdana,serif",
+ "rkuvovg" },
+{ "Verdana,serif",
+ "pbhentr," },
+{ "Verdana,serif",
+ "granpvgl," },
+{ "Verdana,serif",
+ "crefrirenapr" },
+{ "Verdana,serif",
+ "vagryyvtrapr." },
+{ "Verdana,serif",
+ "Jr" },
+{ "Verdana,serif",
+ "jnag" },
+{ "Verdana,serif",
+ "tvir" },
+{ "Verdana,serif",
+ "cynlre" },
+{ "Verdana,serif",
+ "srryvat" },
+{ "Verdana,serif",
+ "ur\xe2""\x80""\x99""f" },
+{ "Verdana,serif",
+ "cnegvpvcngvat" },
+{ "Verdana,serif",
+ "xvaq" },
+{ "Verdana,serif",
+ "evgr" },
+{ "Verdana,serif",
+ "cnffntr:" },
+{ "Verdana,serif",
+ "ur" },
+{ "Verdana,serif",
+ "jba'g" },
+{ "Verdana,serif",
+ "dhvgr" },
+{ "Verdana,serif",
+ "fnzr" },
+{ "Verdana,serif",
+ "nsgre" },
+{ "Verdana,serif",
+ "cynlvat." },
+{ "Verdana,serif",
+ "abg" },
+{ "Verdana,serif",
+ "dhrfgvba" },
+{ "Verdana,serif",
+ "fnivat" },
+{ "Verdana,serif",
+ "jbeyq" },
+{ "Verdana,serif",
+ "bapr" },
+{ "Verdana,serif",
+ "ntnva." },
+{ "Verdana,serif",
+ "nffhzrf" },
+{ "Verdana,serif",
+ "ebyr" },
+{ "Verdana,serif",
+ "Rqjneq" },
+{ "Verdana,serif",
+ "Pneaol," },
+{ "Verdana,serif",
+ "ureb," },
+{ "Verdana,serif",
+ "jub" },
+{ "Verdana,serif",
+ "evpu" },
+{ "Verdana,serif",
+ "pbzcyrk" },
+{ "Verdana,serif",
+ "punenpgre" },
+{ "Verdana,serif",
+ "pbzongf" },
+{ "Verdana,serif",
+ "uvf" },
+{ "Verdana,serif",
+ "bja" },
+{ "Verdana,serif",
+ "qrzbaf" },
+{ "Verdana,serif",
+ "whfg" },
+{ "Verdana,serif",
+ "zhpu" },
+{ "Verdana,serif",
+ "perngherf" },
+{ "Verdana,serif",
+ "vaunovg" },
+{ "Verdana,serif",
+ "uryy" },
+{ "Verdana,serif",
+ "cyhatrq." },
+{ "Verdana,serif",
+ "vzcyvpngvbaf" },
+{ "Verdana,serif",
+ "Nybar" },
+{ "Verdana,serif",
+ "Qnex:" },
+{ "Verdana,serif",
+ "Arj" },
+{ "Verdana,serif",
+ "Avtugzner" },
+{ "Verdana,serif",
+ "uvz" },
+{ "Verdana,serif",
+ "qvfpbire" },
+{ "Verdana,serif",
+ "ernyvgvrf" },
+{ "Verdana,serif",
+ "pbhyq" },
+{ "Verdana,serif",
+ "bayl" },
+{ "Verdana,serif",
+ "fhfcrpg," },
+{ "Verdana,serif",
+ "pbafrdhraprf" },
+{ "Verdana,serif",
+ "grezf" },
+{ "Verdana,serif",
+ "tnzrcynl" },
+{ "Verdana,serif",
+ "nqiragher." },
+{ "Verdana,serif",
+ "nqncg" },
+{ "Verdana,serif",
+ "haqrefgnaq" },
+{ "Verdana,serif",
+ "guvf" },
+{ "Verdana,serif",
+ "havirefr" },
+{ "Verdana,serif",
+ "punyyratvat" },
+{ "Verdana,serif",
+ "beqre" },
+{ "Verdana,serif",
+ "svtug" },
+{ "Verdana,serif",
+ "nezf." },
+{ "Verdana,serif",
+ "Fbzr" },
+{ "Verdana,serif",
+ "crbcyr" },
+{ "Verdana,serif",
+ "haqretb" },
+{ "Verdana,serif",
+ "jung" },
+{ "Verdana,serif",
+ "pnyy" },
+{ "Verdana,serif",
+ "frafr" },
+{ "Verdana,serif",
+ "qrcevingvba" },
+{ "Verdana,serif",
+ "rkcrevraprf" },
+{ "Verdana,serif",
+ "vzzrefrf" },
+{ "Verdana,serif",
+ "fb" },
+{ "Verdana,serif",
+ "shyyl" },
+{ "Verdana,serif",
+ "tnzr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Eboreg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Qhssl" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(vq)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "11:00" },
+{ "Verdana,serif",
+ "fcvpr" },
+{ "Verdana,serif",
+ "abeznyyl" },
+{ "Verdana,serif",
+ "oynu" },
+{ "Verdana,serif",
+ "uhzc" },
+{ "Verdana,serif",
+ "Jrqarfqnl)," },
+{ "Verdana,serif",
+ "Qhssl," },
+{ "Verdana,serif",
+ "cbfg-tbyq" },
+{ "Verdana,serif",
+ "(zvyxvat" },
+{ "Verdana,serif",
+ "serr" },
+{ "Verdana,serif",
+ "nsgre" },
+{ "Verdana,serif",
+ "tbyq). " },
+{ "Verdana,serif",
+ "qbphzrag" },
+{ "Verdana,serif",
+ "punatrq" },
+{ "Verdana,serif",
+ "pbhefr" },
+{ "Verdana,serif",
+ "qrfvtavat" },
+{ "Verdana,serif",
+ "D3," },
+{ "Verdana,serif",
+ "GN" },
+{ "Verdana,serif",
+ "pbhyq," },
+{ "Verdana,serif",
+ "(bx," },
+{ "Verdana,serif",
+ "zber). " },
+{ "Verdana,serif",
+ "Ibbqbb" },
+{ "Verdana,serif",
+ "Rkgerzr:" },
+{ "Verdana,serif",
+ "gurer" },
+{ "Verdana,serif",
+ "nal" },
+{ "Verdana,serif",
+ "ratvarf" },
+{ "Verdana,serif",
+ "/" },
+{ "Verdana,serif",
+ "hgvyvgvrf" },
+{ "Verdana,serif",
+ "rgp." },
+{ "Verdana,serif",
+ "gung" },
+{ "Verdana,serif",
+ "nofbyhgryl" },
+{ "Verdana,serif",
+ "fhpxrq" },
+{ "Verdana,serif",
+ "nff" },
+{ "Verdana,serif",
+ "be" },
+{ "Verdana,serif",
+ "fybjrq" },
+{ "Verdana,serif",
+ "qbja" },
+{ "Verdana,serif",
+ "qrirybczrag" },
+{ "Verdana,serif",
+ "jnag" },
+{ "Verdana,serif",
+ "tb" },
+{ "Verdana,serif",
+ "cbfgny?" },
+{ "verdana,serif",
+ "Eboreg" },
+{ "verdana,serif",
+ "Qhssl:" },
+{ "Verdana,serif",
+ "DIZ'f" },
+{ "Verdana,serif",
+ "cbegnovyvgl" },
+{ "Verdana,serif",
+ "fbzrgvzrf." },
+{ "Verdana,serif",
+ "Qvq" },
+{ "Verdana,serif",
+ "jbex" },
+{ "Verdana,serif",
+ "qverpgyl" },
+{ "Verdana,serif",
+ "Pneznpx" },
+{ "Verdana,serif",
+ "D3" },
+{ "Verdana,serif",
+ "GN" },
+{ "Verdana,serif",
+ "whfg" },
+{ "Verdana,serif",
+ "purpxyvfg" },
+{ "Verdana,serif",
+ "guvatf" },
+{ "Verdana,serif",
+ "arrqrq" },
+{ "Verdana,serif",
+ "or" },
+{ "Verdana,serif",
+ "qbar?" },
+{ "Verdana,serif",
+ "zbfgyl" },
+{ "Verdana,serif",
+ "QBBZ" },
+{ "Verdana,serif",
+ "erfrnepu" },
+{ "Verdana,serif",
+ "fgvpxl" },
+{ "Verdana,serif",
+ "funerq" },
+{ "Verdana,serif",
+ "Wna," },
+{ "Verdana,serif",
+ "Cnhy," },
+{ "Verdana,serif",
+ "Tenrzr" },
+{ "Verdana,serif",
+ "zlfrys." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Bav" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "bireybnq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:56" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fbal" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cynlfgngvba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "V/VV" },
+{ "Verdana,serif",
+ "Ivqrb" },
+{ "Verdana,serif",
+ "ohapu" },
+{ "Verdana,serif",
+ "CF-2" },
+{ "Verdana,serif",
+ "Bav," },
+{ "Verdana,serif",
+ "Ohatvr'f" },
+{ "Verdana,serif",
+ "cnpxrq" },
+{ "Verdana,serif",
+ "znegvny" },
+{ "Verdana,serif",
+ "negf" },
+{ "Verdana,serif",
+ "fubgf," },
+{ "Verdana,serif",
+ "oyvc" },
+{ "Verdana,serif",
+ "Bav" },
+{ "Verdana,serif",
+ "funcvat" },
+{ "Verdana,serif",
+ "hc," },
+{ "Verdana,serif",
+ "yrff-guna-vzcerffvir" },
+{ "Verdana,serif",
+ "fubja" },
+{ "Verdana,serif",
+ "R3" },
+{ "Verdana,serif",
+ "RPGF" },
+{ "Verdana,serif",
+ "pnfgf" },
+{ "Verdana,serif",
+ "srznyr" },
+{ "Verdana,serif",
+ "navzr" },
+{ "Verdana,serif",
+ "Xbabxb," },
+{ "Verdana,serif",
+ "zretrf" },
+{ "Verdana,serif",
+ "unaq-gb-unaq" },
+{ "Verdana,serif",
+ "pbzong." },
+{ "Verdana,serif",
+ "Ercbegf" },
+{ "Verdana,serif",
+ "Gnxr" },
+{ "Verdana,serif",
+ "Gjb" },
+{ "Verdana,serif",
+ "uvqrbhf" },
+{ "Verdana,serif",
+ "senzr" },
+{ "Verdana,serif",
+ "engr" },
+{ "Verdana,serif",
+ "ceboyrzf" },
+{ "Verdana,serif",
+ "qbttrq" },
+{ "Verdana,serif",
+ "pbqr" },
+{ "Verdana,serif",
+ "pyrnarq" },
+{ "Verdana,serif",
+ "guvatf," },
+{ "Verdana,serif",
+ "tencuvpf" },
+{ "Verdana,serif",
+ "fzbbgurq" },
+{ "Verdana,serif",
+ "avpryl." },
+{ "Verdana,serif",
+ "Fvatyr-cynlref" },
+{ "Verdana,serif",
+ "rkcrpg" },
+{ "Verdana,serif",
+ "zrtn" },
+{ "Verdana,serif",
+ "pbecbengvbaf" },
+{ "Verdana,serif",
+ "gbbyrq-hc" },
+{ "Verdana,serif",
+ "fhcre-ivbyrag" },
+{ "Verdana,serif",
+ "cbyvpr" },
+{ "Verdana,serif",
+ "glcrf," },
+{ "Verdana,serif",
+ "fcyvg-fperra" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Snyybhg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Gnpgvpf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:46" },
+{ "Verdana,serif",
+ "qrirybcre'f" },
+{ "Verdana,serif",
+ "Inhyg" },
+{ "Verdana,serif",
+ "Argjbex" },
+{ "Verdana,serif",
+ "Snyybhg" },
+{ "Verdana,serif",
+ "Gnpgvpf:" },
+{ "Verdana,serif",
+ "Oebgureubbq" },
+{ "Verdana,serif",
+ "Fgrry" },
+{ "Verdana,serif",
+ "gbzbeebj" },
+{ "Verdana,serif",
+ "5cz" },
+{ "Verdana,serif",
+ "fnhpr:" },
+{ "Verdana,serif",
+ "Jung:" },
+{ "Verdana,serif",
+ "Pung" },
+{ "Verdana,serif",
+ "Jura:" },
+{ "Verdana,serif",
+ "14," },
+{ "Verdana,serif",
+ "5" },
+{ "Verdana,serif",
+ "c.z." },
+{ "Verdana,serif",
+ "CFG" },
+{ "Verdana,serif",
+ "Jurer:" },
+{ "Verdana,serif",
+ "uggc://ectinhyg.vta.pbz/pung/" },
+{ "Verdana,serif",
+ "#vtainhyg" },
+{ "Verdana,serif",
+ "zrzoref)" },
+{ "Verdana,serif",
+ "cersre" },
+{ "Verdana,serif",
+ "VEP" },
+{ "Verdana,serif",
+ "pyvrag" },
+{ "Verdana,serif",
+ "ubfg17.jroznfgre.pbz," },
+{ "Verdana,serif",
+ "nggraqnapr" },
+{ "Verdana,serif",
+ "Gnpgvpf" },
+{ "Verdana,serif",
+ "Grnz:" },
+{ "Verdana,serif",
+ "Gnlybe" },
+{ "Verdana,serif",
+ "Fravbe" },
+{ "Verdana,serif",
+ "Qrfvtare" },
+{ "Verdana,serif",
+ "Gbal" },
+{ "Verdana,serif",
+ "Bnxqra" },
+{ "Verdana,serif",
+ "Cebqhpre" },
+{ "Verdana,serif",
+ "Rq" },
+{ "Verdana,serif",
+ "Bezna" },
+{ "Verdana,serif",
+ "Yrnq" },
+{ "Verdana,serif",
+ "Xney" },
+{ "Verdana,serif",
+ "Oheqnpx" },
+{ "Verdana,serif",
+ "Cebtenzzre" },
+{ "Verdana,serif",
+ "Cneevfu" },
+{ "Verdana,serif",
+ "Ebqtref" },
+{ "Verdana,serif",
+ "Negvfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "N" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fuval" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "SCF" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Puevfgznf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:41" },
+{ "Verdana,serif",
+ "Nzonffnqbe'f" },
+{ "Verdana,serif",
+ "Qbznva" },
+{ "Verdana,serif",
+ "pryroengvba" },
+{ "Verdana,serif",
+ "fcvevg" },
+{ "Verdana,serif",
+ "svefg-crefba-fubbgre" },
+{ "Verdana,serif",
+ "zbq" },
+{ "Verdana,serif",
+ "gevrq" },
+{ "Verdana,serif",
+ "bhg," },
+{ "Verdana,serif",
+ "fbhaqf" },
+{ "Verdana,serif",
+ "pbby." },
+{ "Verdana,serif",
+ "ZTBA" },
+{ "Verdana,serif",
+ "cerivrjrq" },
+{ "Verdana,serif",
+ "," },
+{ "Verdana,serif",
+ "\"n" },
+{ "Verdana,serif",
+ "Qvnoyb" },
+{ "Verdana,serif",
+ "zrrgf" },
+{ "Verdana,serif",
+ "Jnepensg" },
+{ "Verdana,serif",
+ "glcr" },
+{ "Verdana,serif",
+ "nssnve," },
+{ "Verdana,serif",
+ "\xe2""\x80""\x9c""qhatrba" },
+{ "Verdana,serif",
+ "unpxvat\xe2""\x80""\x9d""" },
+{ "Verdana,serif",
+ "fglyr" },
+{ "Verdana,serif",
+ "pbzong" },
+{ "Verdana,serif",
+ "cyragl" },
+{ "Verdana,serif",
+ "ribyhgvba" },
+{ "Verdana,serif",
+ "ebyr-cynlvat" },
+{ "Verdana,serif",
+ "ryrzragf" },
+{ "Verdana,serif",
+ "guebja" },
+{ "Verdana,serif",
+ "va.\"" },
+{ "Verdana,serif",
+ "Vs" },
+{ "Verdana,serif",
+ "zvffrq" },
+{ "Verdana,serif",
+ "ynfg" },
+{ "Verdana,serif",
+ "avtug," },
+{ "Verdana,serif",
+ "oybxrf" },
+{ "Verdana,serif",
+ "TnzrfQbznva" },
+{ "Verdana,serif",
+ "eryrnfrq" },
+{ "Verdana,serif",
+ "\"rkpyhfvir\"" },
+{ "Verdana,serif",
+ "qrzb" },
+{ "Verdana,serif",
+ "Xvatqbz" },
+{ "Verdana,serif",
+ "Haqre" },
+{ "Verdana,serif",
+ "Sver," },
+{ "Verdana,serif",
+ "nppbeqvat" },
+{ "Verdana,serif",
+ "Tngurevat" },
+{ "Verdana,serif",
+ "Qrirybcref" },
+{ "Verdana,serif",
+ "jnf" },
+{ "Verdana,serif",
+ "snpg" },
+{ "Verdana,serif",
+ "\"yrnxrq\"." },
+{ "Verdana,serif",
+ "Tbfu," },
+{ "Verdana,serif",
+ "ab" },
+{ "Verdana,serif",
+ "jbaqre" },
+{ "Verdana,serif",
+ "rkpyhfvir." },
+{ "Verdana,serif",
+ "zntvp" },
+{ "Verdana,serif",
+ "8-onyy" },
+{ "Verdana,serif",
+ "fnlf" },
+{ "Verdana,serif",
+ "\"TQ" },
+{ "Verdana,serif",
+ "trggvat" },
+{ "Verdana,serif",
+ "ybir" },
+{ "Verdana,serif",
+ "TbQ" },
+{ "Verdana,serif",
+ "nalgvzr" },
+{ "Verdana,serif",
+ "fbba.\"" },
+{ "Verdana,serif",
+ "Ab" },
+{ "Verdana,serif",
+ "hfr" },
+{ "Verdana,serif",
+ "pelvat" },
+{ "Verdana,serif",
+ "fcvyyrq" },
+{ "Verdana,serif",
+ "zvyx" },
+{ "Verdana,serif",
+ "gubhtu," },
+{ "Verdana,serif",
+ "fant" },
+{ "Verdana,serif",
+ "160zo qrzb" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "OBF" },
+{ "Verdana,serif",
+ "SnyybhgK.pbz" },
+{ "Verdana,serif",
+ "D&N" },
+{ "Verdana,serif",
+ "fubegyl," },
+{ "Verdana,serif",
+ "zbzzn" },
+{ "Verdana,serif",
+ "gnhtug" },
+{ "Verdana,serif",
+ "zbzzn)," },
+{ "Verdana,serif",
+ "dhrfgvba" },
+{ "Verdana,serif",
+ "neg" },
+{ "Verdana,serif",
+ "cubgbf" },
+{ "Verdana,serif",
+ "12/6" },
+{ "Verdana,serif",
+ "(oyvzcf" },
+{ "Verdana,serif",
+ "phgfprar," },
+{ "Verdana,serif",
+ "gurl'yy" },
+{ "Verdana,serif",
+ "vapyhqrq" },
+{ "Verdana,serif",
+ "synzr," },
+{ "Verdana,serif",
+ "nsgrezngu" },
+{ "Verdana,serif",
+ "U-obzo...gur" },
+{ "Verdana,serif",
+ "nafjref" },
+{ "Verdana,serif",
+ "gurfr). " },
+{ "Verdana,serif",
+ "OBF" },
+{ "Verdana,serif",
+ "tnzr? " },
+{ "Verdana,serif",
+ "Fhpu" },
+{ "Verdana,serif",
+ "Eubzohf," },
+{ "Verdana,serif",
+ "Trareny," },
+{ "Verdana,serif",
+ "rgp.? " },
+{ "Verdana,serif",
+ "Svaq" },
+{ "Verdana,serif",
+ "SnyybhK" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "10:02" },
+{ "Verdana,serif",
+ "ynqvrf" },
+{ "Verdana,serif",
+ "Guerfu'f" },
+{ "Verdana,serif",
+ "Svevat" },
+{ "Verdana,serif",
+ "Fdhnq" },
+{ "Verdana,serif",
+ "CbcGbc'f" },
+{ "Verdana,serif",
+ "avsgl" },
+{ "Verdana,serif",
+ "vfynaq" },
+{ "Verdana,serif",
+ "cbjrerq" },
+{ "Verdana,serif",
+ "choyvfurq" },
+{ "Verdana,serif",
+ "Tngurevat" },
+{ "Verdana,serif",
+ "Qrirybcref." },
+{ "Verdana,serif",
+ "frk" },
+{ "Verdana,serif",
+ "ornpu:" },
+{ "Verdana,serif",
+ "ohvyg" },
+{ "Verdana,serif",
+ "ratvar," },
+{ "Verdana,serif",
+ "fpnyr" },
+{ "Verdana,serif",
+ "erfbyhgvba" },
+{ "Verdana,serif",
+ "rvgure" },
+{ "Verdana,serif",
+ "640" },
+{ "Verdana,serif",
+ "k" },
+{ "Verdana,serif",
+ "480" },
+{ "Verdana,serif",
+ "800" },
+{ "Verdana,serif",
+ "600" },
+{ "Verdana,serif",
+ "1600" },
+{ "Verdana,serif",
+ "1200." },
+{ "Verdana,serif",
+ "envyebnq" },
+{ "Verdana,serif",
+ "fcvxr" },
+{ "Verdana,serif",
+ "cbvagre" },
+{ "Verdana,serif",
+ "EEG2." },
+{ "Verdana,serif",
+ "nurnq" },
+{ "Verdana,serif",
+ "CbcGbc" },
+{ "Verdana,serif",
+ "fubbgvat" },
+{ "Verdana,serif",
+ "Znepu" },
+{ "Verdana,serif",
+ "eryrnfr," },
+{ "Verdana,serif",
+ "Znpvagbfu" },
+{ "Verdana,serif",
+ "fpurqhyrq" },
+{ "Verdana,serif",
+ "Qernzpnfg" },
+{ "Verdana,serif",
+ "erznvaf" },
+{ "Verdana,serif",
+ "hafpurqhyrq." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zvfp" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "SNDf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Thvqrf..." },
+{ "Verdana,serif",
+ "pbecbengr" },
+{ "Verdana,serif",
+ "gung)" },
+{ "Verdana,serif",
+ "Tnzrfcl" },
+{ "Verdana,serif",
+ "cbfgrq n" },
+{ "Verdana,serif",
+ "Tbbq" },
+{ "Verdana,serif",
+ "Thl" },
+{ "Verdana,serif",
+ "Thvqr" },
+{ "Verdana,serif",
+ "Qhnar" },
+{ "Verdana,serif",
+ "jvyq" },
+{ "Verdana,serif",
+ "puvzcf" },
+{ "Verdana,serif",
+ "chzcrq" },
+{ "Verdana,serif",
+ "uvtu-raq" },
+{ "Verdana,serif",
+ "thlref" },
+{ "Verdana,serif",
+ "trnerq" },
+{ "Verdana,serif",
+ "gbjneqf" },
+{ "Verdana,serif",
+ "tnzref." },
+{ "Verdana,serif",
+ "Arg" },
+{ "Verdana,serif",
+ "freivat" },
+{ "Verdana,serif",
+ "SND" },
+{ "Verdana,serif",
+ "rkcnafvba." },
+{ "Verdana,serif",
+ "zvfpryynarbhf" },
+{ "Verdana,serif",
+ "pbhyqa'g" },
+{ "Verdana,serif",
+ "zhfgre" },
+{ "Verdana,serif",
+ "\"erny\"" },
+{ "Verdana,serif",
+ "sbe..." },
+{ "Verdana,serif",
+ "Npbhfgvp" },
+{ "Verdana,serif",
+ "Rqtr" },
+{ "Verdana,serif",
+ "fbhaq" },
+{ "Verdana,serif",
+ "pneq" },
+{ "Verdana,serif",
+ "erivrj" },
+{ "Verdana,serif",
+ "3QFbhaqFhetr.pbz" },
+{ "Verdana,serif",
+ "vzcerffvbaf" },
+{ "Verdana,serif",
+ "Cuvyvcf" },
+{ "Verdana,serif",
+ "Frzvpbaqhpgbef\xe2""\x80""\x99""" },
+{ "Verdana,serif",
+ "Guhaqreoveq" },
+{ "Verdana,serif",
+ "Niratre" },
+{ "Verdana,serif",
+ "-" },
+{ "Verdana,serif",
+ "FNN7785" },
+{ "Verdana,serif",
+ "CPV" },
+{ "Verdana,serif",
+ "nhqvb" },
+{ "Verdana,serif",
+ "puvc" },
+{ "Verdana,serif",
+ "cbjref" },
+{ "Verdana,serif",
+ "Npbhfgvp" },
+{ "Verdana,serif",
+ "Rqtr" },
+{ "Verdana,serif",
+ "fbhaq" },
+{ "Verdana,serif",
+ "pneq. " },
+{ "Verdana,serif",
+ "Url," },
+{ "Verdana,serif",
+ "vg'f" },
+{ "Verdana,serif",
+ "frr" },
+{ "Verdana,serif",
+ "fbzr" },
+{ "Verdana,serif",
+ "pbzcrgvgvba" },
+{ "Verdana,serif",
+ "sbe" },
+{ "Verdana,serif",
+ "Perngvir" },
+{ "Verdana,serif",
+ "qnlf," },
+{ "Verdana,serif",
+ "svtherq" },
+{ "Verdana,serif",
+ "yrnfg" },
+{ "Verdana,serif",
+ "qrfreirq" },
+{ "Verdana,serif",
+ "yhor-wbo." },
+{ "Verdana,serif",
+ "\"Erpbeqvat" },
+{ "Verdana,serif",
+ "Vaqhfgel" },
+{ "Verdana,serif",
+ "Fpberf" },
+{ "Verdana,serif",
+ "Ivpgbel" },
+{ "Verdana,serif",
+ "Bire" },
+{ "Verdana,serif",
+ "Enqvb" },
+{ "Verdana,serif",
+ "Fgngvbaf\". " },
+{ "Verdana,serif",
+ "Lvxrf..." },
+{ "Verdana,serif",
+ "\"enqvb" },
+{ "Verdana,serif",
+ "oebnqpnfgref" },
+{ "Verdana,serif",
+ "rkrzcg" },
+{ "Verdana,serif",
+ "cnlvat" },
+{ "Verdana,serif",
+ "eblnygl" },
+{ "Verdana,serif",
+ "srrf" },
+{ "Verdana,serif",
+ "negvfgf" },
+{ "Verdana,serif",
+ "erpbeq" },
+{ "Verdana,serif",
+ "pbzcnavrf" },
+{ "Verdana,serif",
+ "fvzhypnfg" },
+{ "Verdana,serif",
+ "oebnqpnfg" },
+{ "Verdana,serif",
+ "Vagrearg\"" },
+{ "Verdana,serif",
+ "(gnyx" },
+{ "Verdana,serif",
+ "oyngnag" },
+{ "Verdana,serif",
+ "evc). " },
+{ "Verdana,serif",
+ "whvpr." },
+{ "Verdana,serif",
+ "Dhnxr" },
+{ "Verdana,serif",
+ "VVV:" },
+{ "Verdana,serif",
+ "Grnz" },
+{ "Verdana,serif",
+ "Neran" },
+{ "Verdana,serif",
+ "flfgrz" },
+{ "Verdana,serif",
+ "erdhverzragf" },
+{ "Verdana,serif",
+ "tbaan" },
+{ "Verdana,serif",
+ "bar:" },
+{ "Verdana,serif",
+ "zva." },
+{ "Verdana,serif",
+ "flfgrz" },
+{ "Verdana,serif",
+ "erdhverzragf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ShaEha" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Fgnpxre" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Oybpxf" },
+{ "Verdana,serif",
+ "serrjner" },
+{ "Verdana,serif",
+ "3Qsvyrf," },
+{ "Verdana,serif",
+ "jva" },
+{ "Verdana,serif",
+ "njneqf" },
+{ "Verdana,serif",
+ "cebivqr" },
+{ "Verdana,serif",
+ "zvahgrf" },
+{ "Verdana,serif",
+ "ragregnvazrag" },
+{ "Verdana,serif",
+ "pbzzrepvny" },
+{ "Verdana,serif",
+ "qnlf). " },
+{ "Verdana,serif",
+ "ShaEha" },
+{ "Verdana,serif",
+ "jrvtuf" },
+{ "Verdana,serif",
+ "zbqrfg" },
+{ "Verdana,serif",
+ "7.9" },
+{ "Verdana,serif",
+ "zrtf" },
+{ "Verdana,serif",
+ "ShaEha:" },
+{ "Verdana,serif",
+ "fznyy," },
+{ "Verdana,serif",
+ "(gvzr" },
+{ "Verdana,serif",
+ "nggnpx)" },
+{ "Verdana,serif",
+ "enpvat" },
+{ "Verdana,serif",
+ "hfre" },
+{ "Verdana,serif",
+ "qevir" },
+{ "Verdana,serif",
+ "snfg" },
+{ "Verdana,serif",
+ "enpr" },
+{ "Verdana,serif",
+ "genpx," },
+{ "Verdana,serif",
+ "nibvqvat" },
+{ "Verdana,serif",
+ "bofgnpyrf," },
+{ "Verdana,serif",
+ "pebffvat" },
+{ "Verdana,serif",
+ "purpxcbvagf" },
+{ "Verdana,serif",
+ "pbyyrpgvat" },
+{ "Verdana,serif",
+ "inevbhf" },
+{ "Verdana,serif",
+ "rkgenf." },
+{ "Verdana,serif",
+ "zrepunaqvmvat" },
+{ "Verdana,serif",
+ "tvirnjnl" },
+{ "Verdana,serif",
+ "erynhapu" },
+{ "Verdana,serif",
+ "Xnefgnqg'f" },
+{ "Verdana,serif",
+ "r-pbzzrepr" },
+{ "Verdana,serif",
+ "\"zl" },
+{ "Verdana,serif",
+ "jbeyq\"" },
+{ "Verdana,serif",
+ "VSN," },
+{ "Verdana,serif",
+ "Oreyva," },
+{ "Verdana,serif",
+ "vzcbegnag" },
+{ "Verdana,serif",
+ "rkuvovgvba" },
+{ "Verdana,serif",
+ "pbafhzre" },
+{ "Verdana,serif",
+ "ryrpgebavp" },
+{ "Verdana,serif",
+ "pbzzhavpngvbaf" },
+{ "Verdana,serif",
+ "3qsvyrf" },
+{ "Verdana,serif",
+ "Fgnpxre" },
+{ "Verdana,serif",
+ "Oybpxf" },
+{ "Verdana,serif",
+ "(uryy," },
+{ "Verdana,serif",
+ "jebat" },
+{ "Verdana,serif",
+ "Grgevf-glcr" },
+{ "Verdana,serif",
+ "ivetva" },
+{ "Verdana,serif",
+ "ubbxre," },
+{ "Verdana,serif",
+ "jba'g...ree," },
+{ "Verdana,serif",
+ "arirezvaq," },
+{ "Verdana,serif",
+ "gurer):" },
+{ "Verdana,serif",
+ "snyyvat" },
+{ "Verdana,serif",
+ "oybpxf" },
+{ "Verdana,serif",
+ "pynffvp" },
+{ "Verdana,serif",
+ "Grgevf." },
+{ "Verdana,serif",
+ "ehaf" },
+{ "Verdana,serif",
+ "ZF" },
+{ "Verdana,serif",
+ "Jvaqbjf," },
+{ "Verdana,serif",
+ "snapl" },
+{ "Verdana,serif",
+ "tencuvpf." },
+{ "Verdana,serif",
+ "fbsgjner" },
+{ "Verdana,serif",
+ "TAH" },
+{ "Verdana,serif",
+ "Trareny" },
+{ "Verdana,serif",
+ "Choyvp" },
+{ "Verdana,serif",
+ "Yvprafr." },
+{ "Verdana,serif",
+ "fbhepr;" },
+{ "Verdana,serif",
+ "fbhepr" },
+{ "Verdana,serif",
+ "pbqr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Tvnagf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "pbzvat" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "CF2..." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(15)" },
+{ "Verdana,serif",
+ "Zbba" },
+{ "Verdana,serif",
+ "Vagrecynl'f" },
+{ "Verdana,serif",
+ "trz," },
+{ "Verdana,serif",
+ "cbegrq" },
+{ "Verdana,serif",
+ "zvq-2001. " },
+{ "Verdana,serif",
+ "zvyx" },
+{ "Verdana,serif",
+ "znexrg" },
+{ "Verdana,serif",
+ "pbafbyr. " },
+{ "Verdana,serif",
+ "Hcqngr" },
+{ "Verdana,serif",
+ "Tvnagf," },
+{ "Verdana,serif",
+ "jbb!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Pbffnpxf" },
+{ "Verdana,serif",
+ "Svefg'f" },
+{ "Verdana,serif",
+ "Pbffnpxf:" },
+{ "Verdana,serif",
+ "Rhebcrna" },
+{ "Verdana,serif",
+ "tbq(f)" },
+{ "Verdana,serif",
+ "fcvg-fuvavat" },
+{ "Verdana,serif",
+ "ybhq," },
+{ "Verdana,serif",
+ "qngrq" },
+{ "Verdana,serif",
+ "Erab...nf" },
+{ "Verdana,serif",
+ "pvgl," },
+{ "Verdana,serif",
+ "thl" },
+{ "Verdana,serif",
+ "nggbearl" },
+{ "Verdana,serif",
+ "trareny," },
+{ "Verdana,serif",
+ "ubbxre). " },
+{ "Verdana,serif",
+ "frzv-ursgl" },
+{ "Verdana,serif",
+ "oyheo:" },
+{ "Verdana,serif",
+ "fhesnpr," },
+{ "Verdana,serif",
+ "fgehpgherf" },
+{ "Verdana,serif",
+ "havgf," },
+{ "verdana,serif",
+ "Pbffnpxf" },
+{ "Verdana,serif",
+ "snveyl" },
+{ "Verdana,serif",
+ "fgenvtugsbejneq" },
+{ "Verdana,serif",
+ "v.r.," },
+{ "Verdana,serif",
+ "tngurevat" },
+{ "Verdana,serif",
+ "(tbyq," },
+{ "Verdana,serif",
+ "veba," },
+{ "Verdana,serif",
+ "pbny," },
+{ "Verdana,serif",
+ "fgbar," },
+{ "Verdana,serif",
+ "jbbq," },
+{ "Verdana,serif",
+ "sbbq)," },
+{ "Verdana,serif",
+ "ohvyqvat" },
+{ "Verdana,serif",
+ "jrncbaf," },
+{ "Verdana,serif",
+ "nqinaprf," },
+{ "Verdana,serif",
+ "furre" },
+{ "Verdana,serif",
+ "bccbaragf" },
+{ "Verdana,serif",
+ "nyyvrf" },
+{ "Verdana,serif",
+ "vaibyirq," },
+{ "Verdana,serif",
+ "fgeratguf" },
+{ "Verdana,serif",
+ "grpu-gerr" },
+{ "Verdana,serif",
+ "nqinaprf" },
+{ "Verdana,serif",
+ "(hc" },
+{ "Verdana,serif",
+ "hctenqrf" },
+{ "Verdana,serif",
+ "cbffvoyr)" },
+{ "Verdana,serif",
+ "fgnaq" },
+{ "Verdana,serif",
+ "cnpx." },
+{ "Verdana,serif",
+ "ner" },
+{ "Verdana,serif",
+ "16" },
+{ "Verdana,serif",
+ "angvbaf" },
+{ "Verdana,serif",
+ "nf," },
+{ "Verdana,serif",
+ "jvgu," },
+{ "Verdana,serif",
+ "ntnvafg:" },
+{ "Verdana,serif",
+ "Nytrevn," },
+{ "Verdana,serif",
+ "Nhfgevn," },
+{ "Verdana,serif",
+ "Ratynaq," },
+{ "Verdana,serif",
+ "Senapr," },
+{ "Verdana,serif",
+ "Argureynaqf," },
+{ "Verdana,serif",
+ "Cvrzbagr," },
+{ "Verdana,serif",
+ "Cbynaq," },
+{ "Verdana,serif",
+ "Cbeghtny," },
+{ "Verdana,serif",
+ "Cehffvn," },
+{ "Verdana,serif",
+ "Ehffvn," },
+{ "Verdana,serif",
+ "Fnkbal," },
+{ "Verdana,serif",
+ "Fcnva," },
+{ "Verdana,serif",
+ "Fjrqra," },
+{ "Verdana,serif",
+ "Ghexrl," },
+{ "Verdana,serif",
+ "Hxenvar," },
+{ "Verdana,serif",
+ "Iravpr." },
+{ "Verdana,serif",
+ "Rnpu" },
+{ "Verdana,serif",
+ "pbhagel" },
+{ "Verdana,serif",
+ "bevtvany" },
+{ "Verdana,serif",
+ "fcrpvnyvmrq" },
+{ "Verdana,serif",
+ "rpbabzvp" },
+{ "Verdana,serif",
+ "grpuavpny" },
+{ "Verdana,serif",
+ "qrirybczrag," },
+{ "Verdana,serif",
+ "zvyvgnel" },
+{ "Verdana,serif",
+ "nqinagntrf" },
+{ "Verdana,serif",
+ "qenjonpxf\xe2""\x80""\x94""juvpu" },
+{ "Verdana,serif",
+ "fubhyq" },
+{ "Verdana,serif",
+ "nyybj" },
+{ "Verdana,serif",
+ "gnpgvpny," },
+{ "Verdana,serif",
+ "fgengrtvp," },
+{ "Verdana,serif",
+ "qvcybzngvp" },
+{ "Verdana,serif",
+ "cbffvovyvgvrf." },
+{ "Verdana,serif",
+ "Nzbat" },
+{ "Verdana,serif",
+ "havg" },
+{ "Verdana,serif",
+ "glcrf" },
+{ "Verdana,serif",
+ "ninvynoyr" },
+{ "Verdana,serif",
+ "vasnagel," },
+{ "Verdana,serif",
+ "pninyel," },
+{ "Verdana,serif",
+ "negvyyrel," },
+{ "Verdana,serif",
+ "aniny" },
+{ "Verdana,serif",
+ "sbeprf." },
+{ "Verdana,serif",
+ "Angvbaf" },
+{ "Verdana,serif",
+ "gurve" },
+{ "Verdana,serif",
+ "havgf" },
+{ "Verdana,serif",
+ "nybat" },
+{ "Verdana,serif",
+ "pbzzba" },
+{ "Verdana,serif",
+ "fvqrf." },
+{ "Verdana,serif",
+ "Hcqngr" },
+{ "Verdana,serif",
+ "Jnef," },
+{ "Verdana,serif",
+ "ybiva'" },
+{ "Verdana,serif",
+ "ahqvrf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "(40)" },
+{ "Verdana,serif",
+ "bireybbxrq" },
+{ "Verdana,serif",
+ "Elna" },
+{ "Verdana,serif",
+ "(\"GurZlfgvp\")" },
+{ "Verdana,serif",
+ "nern...punaprf" },
+{ "Verdana,serif",
+ "ur'f" },
+{ "Verdana,serif",
+ "anvyrq" },
+{ "Verdana,serif",
+ "jbzna" },
+{ "Verdana,serif",
+ "(orpnhfr," },
+{ "Verdana,serif",
+ "vzcerffvba," },
+{ "Verdana,serif",
+ "erdhverzrag" },
+{ "Verdana,serif",
+ "nern...cyhf," },
+{ "Verdana,serif",
+ "jevgr-bss" },
+{ "Verdana,serif",
+ "fbzrguvat). " },
+{ "Verdana,serif",
+ "ehzbe" },
+{ "Verdana,serif",
+ "jvaql" },
+{ "Verdana,serif",
+ "oerrml" },
+{ "Verdana,serif",
+ "Puvpntb," },
+{ "Verdana,serif",
+ "(wbxr," },
+{ "Verdana,serif",
+ "qnzzvg). " },
+{ "Verdana,serif",
+ "jvaql," },
+{ "Verdana,serif",
+ "\"lrnu," },
+{ "Verdana,serif",
+ "avpx" },
+{ "Verdana,serif",
+ "'gur" },
+{ "Verdana,serif",
+ "gbja'" },
+{ "Verdana,serif",
+ "gurer\"" },
+{ "Verdana,serif",
+ "jrngure" },
+{ "Verdana,serif",
+ "ERNY" },
+{ "Verdana,serif",
+ "ernfba" },
+{ "Verdana,serif",
+ "pvgvrf" },
+{ "Verdana,serif",
+ "anzr," },
+{ "Verdana,serif",
+ "uvfgbevpnyyl," },
+{ "Verdana,serif",
+ "qrs." },
+{ "Verdana,serif",
+ "jvgf" },
+{ "Verdana,serif",
+ "ubarfg," },
+{ "Verdana,serif",
+ "qvfgnapr" },
+{ "Verdana,serif",
+ "eryngvbafuvc" },
+{ "Verdana,serif",
+ "jbexf," },
+{ "Verdana,serif",
+ "ubbxre" },
+{ "Verdana,serif",
+ "abj. " },
+{ "Verdana,serif",
+ "yvirq" },
+{ "Verdana,serif",
+ "gbtrgure," },
+{ "Verdana,serif",
+ "(V'z" },
+{ "Verdana,serif",
+ "glcr). " },
+{ "Verdana,serif",
+ "Arkg" },
+{ "Verdana,serif",
+ "ivfvg" },
+{ "Verdana,serif",
+ "gbja)" },
+{ "Verdana,serif",
+ "uvqr" },
+{ "Verdana,serif",
+ "orq" },
+{ "Verdana,serif",
+ "gvzrf" },
+{ "Verdana,serif",
+ "zneevrq," },
+{ "Verdana,serif",
+ "url," },
+{ "Verdana,serif",
+ "eryngvbafuvcf" },
+{ "Verdana,serif",
+ "fgneg). " },
+{ "Verdana,serif",
+ "ubarfgl," },
+{ "Verdana,serif",
+ "tvey" },
+{ "Verdana,serif",
+ "ungrq" },
+{ "Verdana,serif",
+ "jura," },
+{ "Verdana,serif",
+ "boivbhf" },
+{ "Verdana,serif",
+ "ernfbaf," },
+{ "Verdana,serif",
+ "qvibeprq," },
+{ "Verdana,serif",
+ "uhtr" },
+{ "Verdana,serif",
+ "fubpxre). " },
+{ "Verdana,serif",
+ "Fur" },
+{ "Verdana,serif",
+ "qvrq," },
+{ "Verdana,serif",
+ "fcnexrq" },
+{ "Verdana,serif",
+ "wbxr" },
+{ "Verdana,serif",
+ "\"abj" },
+{ "Verdana,serif",
+ "qnq" },
+{ "Verdana,serif",
+ "fur'q" },
+{ "Verdana,serif",
+ "pbby\"...gung" },
+{ "Verdana,serif",
+ "onyybba. " },
+{ "Verdana,serif",
+ "uverq" },
+{ "Verdana,serif",
+ "uryc," },
+{ "Verdana,serif",
+ "rkphfr" },
+{ "Verdana,serif",
+ "fcnprq" },
+{ "Verdana,serif",
+ "Gbby'f" },
+{ "Verdana,serif",
+ "\"Fnyviny\"" },
+{ "Verdana,serif",
+ "obkfrg" },
+{ "Verdana,serif",
+ "(Gbby'f" },
+{ "Verdana,serif",
+ "sbhe+" },
+{ "Verdana,serif",
+ "lrnef...). " },
+{ "Verdana,serif",
+ "Gbby," },
+{ "Verdana,serif",
+ "pbafvqre" },
+{ "Verdana,serif",
+ "fpengpurq" },
+{ "Verdana,serif",
+ "onfgneq!" },
+{ "Verdana,serif",
+ "svanyyl," },
+{ "Verdana,serif",
+ "\"jryy" },
+{ "Verdana,serif",
+ "fznpx" },
+{ "Verdana,serif",
+ "fnyyl\"," },
+{ "Verdana,serif",
+ "Ohfu" },
+{ "Verdana,serif",
+ "svanyyl" },
+{ "Verdana,serif",
+ "(Tber" },
+{ "Verdana,serif",
+ "jvguqenj" },
+{ "Verdana,serif",
+ "gryrivfrq" },
+{ "Verdana,serif",
+ "nqqerff" },
+{ "Verdana,serif",
+ "angvba" },
+{ "Verdana,serif",
+ "gbavtug" },
+{ "Verdana,serif",
+ "9CZ" },
+{ "Verdana,serif",
+ "Rnfgrea)" },
+{ "Verdana,serif",
+ "s'a" },
+{ "Verdana,serif",
+ "fghoobea" },
+{ "Verdana,serif",
+ "onfgneq," },
+{ "Verdana,serif",
+ "oynzr" },
+{ "Verdana,serif",
+ "fhccbfr." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ynfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "5" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "qnlf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "qbayrzzba.pbz" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Zber" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "sha" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "guna" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Cbyltnzvfg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Wryy-B" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Jerfgyvat!" },
+{ "Arial,serif",
+ "Gur" },
+{ "Arial,serif",
+ "Ibbqbb" },
+{ "Arial,serif",
+ "Rkgerzr" },
+{ "Arial,serif",
+ "ybtb" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "IR" },
+{ "Arial,serif",
+ "\"thl\"" },
+{ "Arial,serif",
+ "(Uneel" },
+{ "Arial,serif",
+ "ZpOyvax)" },
+{ "Arial,serif",
+ "jrer" },
+{ "Arial,serif",
+ "qrfvtarq" },
+{ "Arial,serif",
+ "ol" },
+{ "Arial,serif",
+ "bar" },
+{ "Arial,serif",
+ "bayl" },
+{ "Arial,serif",
+ "Jnygre" },
+{ "Arial,serif",
+ "|2|" },
+{ "Arial,serif",
+ "Pbfgvanx" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "2" },
+{ "Arial,serif",
+ "Qrfvta.bet" },
+{ "Arial,serif",
+ "." },
+{ "Arial,serif",
+ "(Ur" },
+{ "Arial,serif",
+ "vf" },
+{ "Arial,serif",
+ "abg" },
+{ "Arial,serif",
+ "erfcbafvoyr" },
+{ "Arial,serif",
+ "sbe" },
+{ "Arial,serif",
+ "nal" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "pbagrag," },
+{ "Arial,serif",
+ "whfg" },
+{ "Arial,serif",
+ "ybtb;" },
+{ "Arial,serif",
+ "lrf," },
+{ "Arial,serif",
+ "fbzrobql" },
+{ "Arial,serif",
+ "unf" },
+{ "Arial,serif",
+ "nyernql" },
+{ "Arial,serif",
+ "znqr" },
+{ "Arial,serif",
+ "gung" },
+{ "Arial,serif",
+ "zvfgnxr)..." },
+{ "Arial,serif",
+ "fb" },
+{ "Arial,serif",
+ "Jnygre" },
+{ "Arial,serif",
+ "=" },
+{ "Arial,serif",
+ "Ybtb," },
+{ "Arial,serif",
+ "Ovyyl" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "tnat" },
+{ "Arial,serif",
+ "rirelguvat" },
+{ "Arial,serif",
+ "ryfr." },
+{ "Arial,serif",
+ "Jvgu" },
+{ "Arial,serif",
+ "bire" },
+{ "Arial,serif",
+ "130" },
+{ "Arial,serif",
+ "zvyyvba" },
+{ "Arial,serif",
+ "puhzc" },
+{ "Arial,serif",
+ "nffrf" },
+{ "Arial,serif",
+ "freirq" },
+{ "Arial,serif",
+ "fvapr" },
+{ "Arial,serif",
+ "Whar" },
+{ "Arial,serif",
+ "3eq," },
+{ "Arial,serif",
+ "1997." },
+{ "Times New Roman,serif",
+ "Nyy" },
+{ "Times New Roman,serif",
+ "genqrznexf" },
+{ "Times New Roman,serif",
+ "hfrq" },
+{ "Times New Roman,serif",
+ "ner" },
+{ "Times New Roman,serif",
+ "cebcregvrf" },
+{ "Times New Roman,serif",
+ "bs" },
+{ "Times New Roman,serif",
+ "gurve" },
+{ "Times New Roman,serif",
+ "erfcrpgvir" },
+{ "Times New Roman,serif",
+ "bjaref." },
+{ "Times New Roman,serif",
+ "evtugf" },
+{ "Times New Roman,serif",
+ "erfreirq. " },
+{ "Times New Roman,serif",
+ "cntrf" },
+{ "Times New Roman,serif",
+ "Pbclevtug" },
+{ "Times New Roman,serif",
+ "\xc2""\xa9""" },
+{ "Times New Roman,serif",
+ "1997," },
+{ "Times New Roman,serif",
+ "1998," },
+{ "Times New Roman,serif",
+ "1999," },
+{ "Times New Roman,serif",
+ "2000" },
+{ "Times New Roman,serif",
+ "ol" },
+{ "Times New Roman,serif",
+ "Ovyyl" },
+{ "Times New Roman,serif",
+ "Jvyfba" },
+{ "Times New Roman,serif",
+ "." },
+{ "Arial,serif",
+ "Pbclevtug" },
+{ "Arial,serif",
+ "bs" },
+{ "Arial,serif",
+ "nyy" },
+{ "Arial,serif",
+ "qbphzragf" },
+{ "Arial,serif",
+ "naq" },
+{ "Arial,serif",
+ "fpevcgf" },
+{ "Arial,serif",
+ "orybatvat" },
+{ "Arial,serif",
+ "gb" },
+{ "Arial,serif",
+ "guvf" },
+{ "Arial,serif",
+ "fvgr" },
+{ "Arial,serif",
+ "ol" },
+{ "Arial,serif",
+ "Ovyyl" },
+{ "Arial,serif",
+ "Jvyfba" },
+{ "Arial,serif",
+ "1997," },
+{ "Arial,serif",
+ "1998," },
+{ "Arial,serif",
+ "1999," },
+{ "Arial,serif",
+ "2000. " },
+{ "Arial,serif",
+ "Zbfg" },
+{ "Arial,serif",
+ "gur" },
+{ "Arial,serif",
+ "vasbezngvba" },
+{ "Arial,serif",
+ "pbagnvarq" },
+{ "Arial,serif",
+ "ba" },
+{ "Arial,serif",
+ "vf" },
+{ "Arial,serif",
+ "pbclevtugrq" },
+{ "Arial,serif",
+ "zngrevny. " },
+{ "Arial,serif",
+ "Vg" },
+{ "Arial,serif",
+ "vyyrtny" },
+{ "Arial,serif",
+ "pbcl" },
+{ "Arial,serif",
+ "be" },
+{ "Arial,serif",
+ "erqvfgevohgr" },
+{ "Arial,serif",
+ "va" },
+{ "Arial,serif",
+ "nal" },
+{ "Arial,serif",
+ "jnl" },
+{ "Arial,serif",
+ "jvgubhg" },
+{ "Arial,serif",
+ "rkcerffrq" },
+{ "Arial,serif",
+ "jevggra" },
+{ "Arial,serif",
+ "pbafrag" },
+{ "Arial,serif",
+ "nhgube. " },
+{ "Arial,serif",
+ "Gur" },
+{ "Arial,serif",
+ "Nhgube" },
+{ "Arial,serif",
+ "qbrf" },
+{ "Arial,serif",
+ "nyybj" },
+{ "Arial,serif",
+ "vgrzf" },
+{ "Arial,serif",
+ "sebz" },
+{ "Arial,serif",
+ "or" },
+{ "Arial,serif",
+ "hfrq" },
+{ "Arial,serif",
+ "nf" },
+{ "Arial,serif",
+ "n" },
+{ "Arial,serif",
+ "ersrerapr" },
+{ "Arial,serif",
+ "cvrpr" },
+{ "Arial,serif",
+ "sbe" },
+{ "Arial,serif",
+ "jung" },
+{ "Arial,serif",
+ "gurl" },
+{ "Arial,serif",
+ "ner" },
+{ "Arial,serif",
+ "jbexvat" },
+{ "Arial,serif",
+ "ba," },
+{ "Arial,serif",
+ "ohg" },
+{ "Arial,serif",
+ "zber" },
+{ "Arial,serif",
+ "lbhe" },
+{ "Arial,serif",
+ "bja" },
+{ "Arial,serif",
+ "hfr," },
+{ "Arial,serif",
+ "abg" },
+{ "Arial,serif",
+ "choyvp" },
+{ "Arial,serif",
+ "pbafhzcgvba" },
+{ "Arial,serif",
+ "cevag/ryrpgebavp" },
+{ "Arial,serif",
+ "zrqvn" },
+{ "Arial,serif",
+ "pbafhzcgvba. " },
+{ "Arial,serif",
+ "Guvf" },
+{ "Arial,serif",
+ "ABG" },
+{ "Arial,serif",
+ "erfcbafvoyr" },
+{ "Arial,serif",
+ "qnzntr" },
+{ "Arial,serif",
+ "gung" },
+{ "Arial,serif",
+ "znl" },
+{ "Arial,serif",
+ "pnhfr" },
+{ "Arial,serif",
+ "flfgrz." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Ybpny" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "gvzr" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "fpevcg" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "ol" },
+{ "Verdana,serif",
+ "Puevf" },
+{ "Verdana,serif",
+ "Aryy" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "," },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "hfrq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "jvgu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "crezvffvba." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Arjf" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "qngnonfr" },
+{ "Verdana,serif",
+ "Qnir" },
+{ "Verdana,serif",
+ "\"Bpgnar\"" },
+{ "Verdana,serif",
+ "Zbeevfba" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "." },
+{ "Verdana,serif",
+ " " },
+{ "Lucida Grande",
+ "Arjf Frnepu" },
+{ "Verdana,serif",
+ "Phfgbzvmr IR!" },
+{ "Verdana,serif",
+ " arjf" },
+{ "Verdana,serif",
+ " Yngrfg Arjf" },
+{ "Verdana,serif",
+ " Arjf Frnepu" },
+{ "Verdana,serif",
+ " Avtugyl Arjfyrggre" },
+{ "Verdana,serif",
+ " qrcnegzragf" },
+{ "Verdana,serif",
+ " Tnzrf" },
+{ "Verdana,serif",
+ " " },
+{ "Verdana,serif",
+ "Ersrerapr Qeviref" },
+{ "Verdana,serif",
+ " gnyx onpx" },
+{ "Verdana,serif",
+ " Sbehzf" },
+{ "Verdana,serif",
+ " Trareny" },
+{ "Verdana,serif",
+ " Uneqjner" },
+{ "Verdana,serif",
+ " Fbsgjner" },
+{ "Verdana,serif",
+ " Fuval" },
+{ "Verdana,serif",
+ " Pung" },
+{ "Verdana,serif",
+ " fvgr vasb" },
+{ "Verdana,serif",
+ "Nobhg Hf" },
+{ "Verdana,serif",
+ " nfx qrif" },
+{ "Verdana,serif",
+ " Ynfg Hcqngr:" },
+{ "Verdana,serif",
+ " Nfx Ubbx" },
+{ "Verdana,serif",
+ " Qrprzore 7" },
+{ "Verdana,serif",
+ "IR - Jr zvtug ynpx gur yratgu, ohg jr cnpx rabhtu tvegu gb xvyy n euvab -oj" },
+{ "Verdana,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,serif",
+ "Pnyy gb Cbjre VV Cngpu" },
+{ "Verdana,serif",
+ "Pbzznaqre Xrra Uvfgbel" },
+{ "Verdana,serif",
+ "Ryvgr Rqvgvba Qrzb" },
+{ "Verdana,serif",
+ "Haqlvat Vagreivrj" },
+{ "Verdana,serif",
+ "Fvyrag Uhagre VV Cerivrj" },
+{ "Verdana,serif",
+ "D3 1.27 Cbvag Eryrnfr" },
+{ "Verdana,serif",
+ "QAS Trgf Eryrnfr Qngr?" },
+{ "Verdana,serif",
+ "Vprjvaq Qnyr RC ceri." },
+{ "Verdana,serif",
+ "JJVV Bayvar Zbivr" },
+{ "Verdana,serif",
+ "Zvfp. Fubgf" },
+{ "Verdana,serif",
+ "AJA'f Hzore Uhyx" },
+{ "Verdana,serif",
+ "Syl3Q tnzr ratvar" },
+{ "Verdana,serif",
+ "Pbyva ZpEnr Enyyl 2.0 qrzb" },
+{ "Verdana,serif",
+ "D3 Cbvag Eryrnfr 1.27 (12.14)" },
+{ "Verdana,serif",
+ "Xvatqbz Haqre Sver (12.12)" },
+{ "Verdana,serif",
+ "Zrpu4 qrzb (12.08)" },
+{ "Verdana,serif",
+ "Arj Gur Snyyra qrzb (12.08)" },
+{ "Verdana,serif",
+ "ABYS cngpu (12.08)" },
+{ "Verdana,serif",
+ "Sne Tngr qrzb (12.06)" },
+{ "Verdana,serif",
+ "Qrhf Rk ZC Cngpu(12.05)" },
+{ "Verdana,serif",
+ "Zvq. Znqarff 2 qrzb (12.04)" },
+{ "Verdana,serif",
+ "Hcqngrq Irabz qrzb (12.03)" },
+{ "Verdana,serif",
+ "Nyvpr qrzb (12.01)" },
+{ "Verdana,serif",
+ "Onyqhef Tngr VV cngpu (11.29)" },
+{ "Verdana,serif",
+ "Onggyr bs Oevgvna qrzb (11.29)" },
+{ "Verdana,serif",
+ "Grnz Neran qrzb (11.22)" },
+{ "Verdana,serif",
+ "Fnpevsvpr qrzb (11.22)" },
+{ "Verdana,serif",
+ "Tvnagf eri/pbqrf (12.13)" },
+{ "Verdana,serif",
+ "Eboreg Qhssl D&N (12.13)" },
+{ "Verdana,serif",
+ "Rfpncr sebz ZV eri (12.9)" },
+{ "Verdana,serif",
+ "Qrif ba QverpgK8 (12.8)" },
+{ "Verdana,serif",
+ "EN2 Erivrj (12.6)" },
+{ "Verdana,serif",
+ "I5 6000 cerivrj (12.5)" },
+{ "Verdana,serif",
+ "Snyybhg Gnpgvpf (12.5)" },
+{ "Verdana,serif",
+ "AUY 2001 Erivrj (11.29)" },
+{ "Verdana,serif",
+ "Orvat Oevna Ubbx (11.28)" },
+{ "Verdana,serif",
+ "Zvq. Znq.2 eri. (11.28)" },
+{ "Verdana,serif",
+ "Oynpx & Juvgr (11.21)" },
+{ "Verdana,serif",
+ "Fnpevsvpr erivrj (11.20)" },
+{ "Verdana,serif",
+ "Unyy Vagreivrj (11.10)" },
+{ "Verdana,serif",
+ "AJA Gbc 10 (11.8)" },
+{ "Verdana,serif",
+ "Qvfcynlvat " },
+{ "Verdana,serif",
+ " arjf. Pyvpx " },
+{ "Verdana,serif",
+ " gb phfgbzvmr." },
+{ "Verdana,serif",
+ "Guhefqnl, Qrprzore 14, 2000" },
+{ "Verdana,serif",
+ " - " },
+{ "Verdana,serif",
+ "Unir fbzr arjf be qbrf lbhe jbzna nfx sbe Fjrrg Qvpx Jvyyl ol " },
+{ "Verdana,serif",
+ "anzr? " },
+{ "Verdana,serif",
+ "Gryy hf!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "4:14 CZ (lbhe gvzr) " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "- " },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Eboreg \"Ncnpur\" Ubjnegu" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " - Tnzrf: Ebyr Cynlvat Fgengrtl -" },
+{ "Verdana,serif",
+ "Gur jbzra ng Qnvyl Enqne cbfgrq nabgure " },
+{ "Verdana,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,serif",
+ ", gnxvat n tynapr ng Oyvmmneq'f " },
+{ "Verdana,serif",
+ "hcpbzvat 3-Q erny gvzr fgengrtl pnfu-pbj. Vapyhqrq, ner n srj arj fperraf. Urer'f n gnxr:" },
+{ "Verdana,serif",
+ "Jnepensg VVV vf Oyvmmneq'f svefg nggrzcg gb perngr n 3Q ernygvzr fgengrtl tnzr, juvpu " },
+{ "Verdana,serif",
+ "cynprf rzcunfvf ba qrirybcvat urebrf naq vaibyivat gurz va pbzong, naq " },
+{ "Verdana,serif",
+ "qbjacynlf erfbhepr znantrzrag naq ehfu gnpgvpf. Boivbhfyl gur zbir gb 3Q " },
+{ "Verdana,serif",
+ "cerfragf rirel qrirybcre jvgu gur fnzr punyyratr: jung gb qb jvgu gur pnzren. " },
+{ "Verdana,serif",
+ "Erpragyl, tnzrf yvxr Tebhaq Pbageby unir tvira cynlref na rabezbhf nzbhag bs " },
+{ "Verdana,serif",
+ "serrqbz, nyybjvat gurz gb mbbz va sbe n dhvpx ivrj be ubire jryy nobir gur svryq. " },
+{ "Verdana,serif",
+ "Fuval'f pnzren va Fnpevsvpr jnf svkrq ba gur znva punenpgre, naq nyybjrq sbe " },
+{ "Verdana,serif",
+ "ebgngvba naq n fznyy nzbhag bs mbbz. Ubjrire, Oyvmmneq unf qrpvqrq gb svk gur " },
+{ "Verdana,serif",
+ "Hcqngr" },
+{ "Verdana,serif",
+ ":" },
+{ "Verdana,serif",
+ "Fcrnxvat" },
+{ "Verdana,serif",
+ "bs" },
+{ "Verdana,serif",
+ "Pbffnpxf:" },
+{ "Verdana,serif",
+ "Rhebcrna" },
+{ "Verdana,serif",
+ "Jnef," },
+{ "Verdana,serif",
+ "TnzrFcbg" },
+{ "Verdana,serif",
+ "HX" },
+{ "Verdana,serif",
+ "unf" },
+{ "Verdana,serif",
+ "cbfgrq" },
+{ "Verdana,serif",
+ "n" },
+{ "Verdana,serif",
+ "yvggyr" },
+{ "Verdana,serif",
+ "unaqf-ba" },
+{ "Verdana,serif",
+ "cerivrj" },
+{ "Verdana,serif",
+ "ybiva'" },
+{ "Verdana,serif",
+ "guvf" },
+{ "Verdana,serif",
+ "tnzr" },
+{ "Verdana,serif",
+ "nf" },
+{ "Verdana,serif",
+ "jryy." },
+{ "Verdana,serif",
+ "Naq," },
+{ "Verdana,serif",
+ "pbhefr," },
+{ "Verdana,serif",
+ "gurl" },
+{ "Verdana,serif",
+ "unir" },
+{ "Verdana,serif",
+ "vapyhqrq" },
+{ "Verdana,serif",
+ "tnyyrel" },
+{ "Verdana,serif",
+ "ahqvrf" },
+{ "Verdana,serif",
+ "." },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ " " },
+{ "Verdana,serif",
+ "Cntr" },
+{ "Verdana,serif",
+ "Rkrphgvba" },
+{ "Verdana,serif",
+ "Gvzr:" },
+{ "Verdana,serif",
+ "266zf" },
+{ "Verdana,serif",
+ "Gvzrfgnzc:" },
+{ "serif",
+ "1," },
+{ "serif",
+ "35," },
+{ "serif",
+ "jjj.ibbqbbrkgerzr.pbz," },
+{ "serif",
+ "5045" },
+{ "serif",
+ "1, 35, jjj.ibbqbbrkgerzr.pbz, 5045" },
+{ "Lucida Grande",
+ "Qbar" },
+{ "Verdana,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,serif",
+ "Pnyy gb Cbjre VV Cngpu" },
+{ "Verdana,serif",
+ "Pbzznaqre Xrra Uvfgbel" },
+{ "Verdana,serif",
+ "Ryvgr Rqvgvba Qrzb" },
+{ "Verdana,serif",
+ "Haqlvat Vagreivrj" },
+{ "Verdana,serif",
+ "Fvyrag Uhagre VV Cerivrj" },
+{ "Verdana,serif",
+ "D3 1.27 Cbvag Eryrnfr" },
+{ "Verdana,serif",
+ "QAS Trgf Eryrnfr Qngr?" },
+{ "Verdana,serif",
+ "Vprjvaq Qnyr RC ceri." },
+{ "Verdana,serif",
+ "JJVV Bayvar Zbivr" },
+{ "Verdana,serif",
+ "Zvfp. Fubgf" },
+{ "Verdana,serif",
+ "AJA'f Hzore Uhyx" },
+{ "Verdana,serif",
+ "Syl3Q tnzr ratvar" },
+{ "Verdana,serif",
+ "Pbyva ZpEnr Enyyl 2.0 qrzb" },
+{ "Verdana,serif",
+ "D3 Cbvag Eryrnfr 1.27 (12.14)" },
+{ "Verdana,serif",
+ "Xvatqbz Haqre Sver (12.12)" },
+{ "Verdana,serif",
+ "Zrpu4 qrzb (12.08)" },
+{ "Verdana,serif",
+ "Arj Gur Snyyra qrzb (12.08)" },
+{ "Verdana,serif",
+ "ABYS cngpu (12.08)" },
+{ "Verdana,serif",
+ "Sne Tngr qrzb (12.06)" },
+{ "Verdana,serif",
+ "Qrhf Rk ZC Cngpu(12.05)" },
+{ "Verdana,serif",
+ "Zvq. Znqarff 2 qrzb (12.04)" },
+{ "Verdana,serif",
+ "Hcqngrq Irabz qrzb (12.03)" },
+{ "Verdana,serif",
+ "Nyvpr qrzb (12.01)" },
+{ "Verdana,serif",
+ "Onyqhef Tngr VV cngpu (11.29)" },
+{ "Verdana,serif",
+ "Onggyr bs Oevgvna qrzb (11.29)" },
+{ "Verdana,serif",
+ "Grnz Neran qrzb (11.22)" },
+{ "Verdana,serif",
+ "Fnpevsvpr qrzb (11.22)" },
+{ "Verdana,serif",
+ "Tvnagf eri/pbqrf (12.13)" },
+{ "Verdana,serif",
+ "Eboreg Qhssl D&N (12.13)" },
+{ "Verdana,serif",
+ "Rfpncr sebz ZV eri (12.9)" },
+{ "Verdana,serif",
+ "Qrif ba QverpgK8 (12.8)" },
+{ "Verdana,serif",
+ "EN2 Erivrj (12.6)" },
+{ "Verdana,serif",
+ "I5 6000 cerivrj (12.5)" },
+{ "Verdana,serif",
+ "Snyybhg Gnpgvpf (12.5)" },
+{ "Verdana,serif",
+ "AUY 2001 Erivrj (11.29)" },
+{ "Verdana,serif",
+ "Orvat Oevna Ubbx (11.28)" },
+{ "Verdana,serif",
+ "Zvq. Znq.2 eri. (11.28)" },
+{ "Verdana,serif",
+ "Oynpx & Juvgr (11.21)" },
+{ "Verdana,serif",
+ "Fnpevsvpr erivrj (11.20)" },
+{ "Verdana,serif",
+ "Unyy Vagreivrj (11.10)" },
+{ "Verdana,serif",
+ "AJA Gbc 10 (11.8)" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/14/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/13/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/12/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/11/00" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "12/10/00" },
+{ "Verdana,serif",
+ "haphfgbzvmrq" },
+{ "Verdana,serif",
+ " arjf. Pyvpx " },
+{ "Verdana,serif",
+ "urer" },
+{ "Verdana,serif",
+ "Guhefqnl, Qrprzore 14, 2000" },
+{ "Verdana,serif",
+ " - " },
+{ "Verdana,serif",
+ "Unir fbzr arjf be qbrf lbhe jbzna nfx sbe Fjrrg Qvpx Jvyyl ol " },
+{ "Verdana,serif",
+ "anzr? " },
+{ "Verdana,serif",
+ "Gryy hf!" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,Geneva,Arial,Helvetica,sans-serif,serif",
+ "Eboreg \"Ncnpur\" Ubjnegu" },
+{ "Verdana,serif",
+ "Gur jbzra ng Qnvyl Enqne cbfgrq nabgure " },
+{ "Verdana,serif",
+ "JnePensg VVV cerivrj" },
+{ "Verdana,serif",
+ ", gnxvat n tynapr ng Oyvmmneq'f " },
+{ "Verdana,serif",
+ "Jnepensg VVV vf Oyvmmneq'f svefg nggrzcg gb perngr n 3Q ernygvzr fgengrtl tnzr, juvpu " },
+{ "Verdana,serif",
+ "cynprf rzcunfvf ba qrirybcvat urebrf naq vaibyivat gurz va pbzong, naq " },
+{ "Verdana,serif",
+ "qbjacynlf erfbhepr znantrzrag naq ehfu gnpgvpf. Boivbhfyl gur zbir gb 3Q " },
+{ "Verdana,serif",
+ "cerfragf rirel qrirybcre jvgu gur fnzr punyyratr: jung gb qb jvgu gur pnzren. " },
+{ "Verdana,serif",
+ "Erpragyl, tnzrf yvxr Tebhaq Pbageby unir tvira cynlref na rabezbhf nzbhag bs " },
+{ "Verdana,serif",
+ "serrqbz, nyybjvat gurz gb mbbz va sbe n dhvpx ivrj be ubire jryy nobir gur svryq. " },
+{ "Verdana,serif",
+ "Fuval'f pnzren va Fnpevsvpr jnf svkrq ba gur znva punenpgre, naq nyybjrq sbe " },
+{ "Verdana,serif",
+ "ebgngvba naq n fznyy nzbhag bs mbbz. Ubjrire, Oyvmmneq unf qrpvqrq gb svk gur " },
+{ "sans-serif",
+ "cerivbhf" },
+{ "sans-serif",
+ "arkg" },
+{ "sans-serif",
+ "pbagragf" },
+{ "sans-serif",
+ "vaqrk" },
+{ "monospace",
+ "27" },
+{ "monospace",
+ "Frcgrzore," },
+{ "monospace",
+ "2000" },
+{ "sans-serif",
+ "1." },
+{ "sans-serif",
+ "Qbphzrag" },
+{ "sans-serif",
+ "Bowrpg" },
+{ "sans-serif",
+ "Zbqry" },
+{ "sans-serif",
+ "Pber" },
+{ "sans-serif",
+ "Rqvgbef" },
+{ "sans-serif",
+ "Neanhq" },
+{ "sans-serif",
+ "Yr" },
+{ "sans-serif",
+ "Ubef," },
+{ "sans-serif",
+ "VOZ" },
+{ "sans-serif",
+ "Zvxr" },
+{ "sans-serif",
+ "Punzcvba," },
+{ "sans-serif",
+ "NeobeGrkg" },
+{ "sans-serif",
+ "(sbe" },
+{ "sans-serif",
+ "QBZ" },
+{ "sans-serif",
+ "Yriry" },
+{ "sans-serif",
+ "1" },
+{ "sans-serif",
+ "sebz" },
+{ "sans-serif",
+ "Abirzore" },
+{ "sans-serif",
+ "20," },
+{ "sans-serif",
+ "1997)" },
+{ "sans-serif",
+ "Fgrir" },
+{ "sans-serif",
+ "Olear," },
+{ "sans-serif",
+ "WninFbsg" },
+{ "sans-serif",
+ "hagvy" },
+{ "sans-serif",
+ "19," },
+{ "sans-serif",
+ "Tniva" },
+{ "sans-serif",
+ "Avpby," },
+{ "sans-serif",
+ "Vafb" },
+{ "sans-serif",
+ "RCF" },
+{ "sans-serif",
+ "1)" },
+{ "sans-serif",
+ "Ynhera" },
+{ "sans-serif",
+ "Jbbq," },
+{ "sans-serif",
+ "FbsgDhnq," },
+{ "sans-serif",
+ "Vap." },
+{ "sans-serif",
+ "Gnoyr" },
+{ "sans-serif",
+ "bs" },
+{ "sans-serif",
+ "pbagragf" },
+{ "sans-serif",
+ "1.1." },
+{ "sans-serif",
+ "Bireivrj" },
+{ "sans-serif",
+ "bs" },
+{ "sans-serif",
+ "gur" },
+{ "sans-serif",
+ "Pber" },
+{ "sans-serif",
+ "Vagresnprf" },
+{ "sans-serif",
+ "1.1.1." },
+{ "sans-serif",
+ "Gur" },
+{ "sans-serif",
+ "QBZ" },
+{ "sans-serif",
+ "Fgehpgher" },
+{ "sans-serif",
+ "Zbqry" },
+{ "sans-serif",
+ "1.1.2." },
+{ "sans-serif",
+ "Zrzbel" },
+{ "sans-serif",
+ "Znantrzrag" },
+{ "sans-serif",
+ "1.1.3." },
+{ "sans-serif",
+ "Anzvat" },
+{ "sans-serif",
+ "Pbairagvbaf" },
+{ "sans-serif",
+ "1.1.4." },
+{ "sans-serif",
+ "Vaurevgnapr" },
+{ "sans-serif",
+ "if." },
+{ "sans-serif",
+ "Synggrarq" },
+{ "sans-serif",
+ "Ivrjf" },
+{ "sans-serif",
+ "bs" },
+{ "sans-serif",
+ "gur" },
+{ "sans-serif",
+ "NCV" },
+{ "sans-serif",
+ "1.1.5." },
+{ "sans-serif",
+ "QBZFgevat" },
+{ "sans-serif",
+ "glcr" },
+{ "sans-serif",
+ "1.1.6." },
+{ "sans-serif",
+ "QBZGvzrFgnzc" },
+{ "sans-serif",
+ "1.1.7." },
+{ "sans-serif",
+ "Fgevat" },
+{ "sans-serif",
+ "pbzcnevfbaf" },
+{ "sans-serif",
+ "va" },
+{ "sans-serif",
+ "1.1.8." },
+{ "sans-serif",
+ "KZY" },
+{ "sans-serif",
+ "Anzrfcnprf" },
+{ "sans-serif",
+ "1.2." },
+{ "sans-serif",
+ "Shaqnzragny" },
+{ "sans-serif",
+ "QBZRkprcgvba" },
+{ "sans-serif",
+ "," },
+{ "sans-serif",
+ "RkprcgvbaPbqr" },
+{ "sans-serif",
+ "QBZVzcyrzragngvba" },
+{ "sans-serif",
+ "QbphzragSentzrag" },
+{ "sans-serif",
+ "Qbphzrag" },
+{ "sans-serif",
+ "Abqr" },
+{ "sans-serif",
+ "AbqrYvfg" },
+{ "sans-serif",
+ "AnzrqAbqrZnc" },
+{ "sans-serif",
+ "PunenpgreQngn" },
+{ "sans-serif",
+ "Ngge" },
+{ "sans-serif",
+ "Ryrzrag" },
+{ "sans-serif",
+ "Grkg" },
+{ "sans-serif",
+ "Pbzzrag" },
+{ "sans-serif",
+ "1.3." },
+{ "sans-serif",
+ "Rkgraqrq" },
+{ "sans-serif",
+ "PQNGNFrpgvba" },
+{ "sans-serif",
+ "QbphzragGlcr" },
+{ "sans-serif",
+ "Abgngvba" },
+{ "sans-serif",
+ "Ragvgl" },
+{ "sans-serif",
+ "RagvglErsrerapr" },
+{ "sans-serif",
+ "CebprffvatVafgehpgvba" },
+{ "sans-serif",
+ "1.1." },
+{ "sans-serif",
+ "Bireivrj" },
+{ "sans-serif",
+ "gur" },
+{ "sans-serif",
+ "QBZ" },
+{ "sans-serif",
+ "Pber" },
+{ "sans-serif",
+ "Vagresnprf" },
+{ "sans-serif",
+ "Guvf" },
+{ "sans-serif",
+ "frpgvba" },
+{ "sans-serif",
+ "qrsvarf" },
+{ "sans-serif",
+ "n" },
+{ "sans-serif",
+ "frg" },
+{ "sans-serif",
+ "bowrpgf" },
+{ "sans-serif",
+ "naq" },
+{ "sans-serif",
+ "vagresnprf" },
+{ "sans-serif",
+ "sbe" },
+{ "sans-serif",
+ "npprffvat" },
+{ "sans-serif",
+ "znavchyngvat" },
+{ "sans-serif",
+ "qbphzrag" },
+{ "sans-serif",
+ "bowrpgf." },
+{ "sans-serif",
+ "Gur" },
+{ "sans-serif",
+ "shapgvbanyvgl" },
+{ "sans-serif",
+ "fcrpvsvrq" },
+{ "sans-serif",
+ "va" },
+{ "sans-serif",
+ "guvf" },
+{ "sans-serif",
+ "(gur" },
+{ "sans-serif",
+ "Pber" },
+{ "sans-serif",
+ "shapgvbanyvgl)" },
+{ "sans-serif",
+ "vf" },
+{ "sans-serif",
+ "fhssvpvrag" },
+{ "sans-serif",
+ "gb" },
+{ "sans-serif",
+ "nyybj" },
+{ "sans-serif",
+ "fbsgjner" },
+{ "sans-serif",
+ "qrirybcref" },
+{ "sans-serif",
+ "jro" },
+{ "sans-serif",
+ "fpevcg" },
+{ "sans-serif",
+ "nhgubef" },
+{ "sans-serif",
+ "npprff" },
+{ "sans-serif",
+ "znavchyngr" },
+{ "sans-serif",
+ "cnefrq" },
+{ "sans-serif",
+ "UGZY" },
+{ "sans-serif",
+ "KZY" },
+{ "sans-serif",
+ "pbagrag" },
+{ "sans-serif",
+ "vafvqr" },
+{ "sans-serif",
+ "pbasbezvat" },
+{ "sans-serif",
+ "cebqhpgf." },
+{ "sans-serif",
+ "NCV" },
+{ "sans-serif",
+ "nyfb" },
+{ "sans-serif",
+ "nyybjf" },
+{ "sans-serif",
+ "perngvba" },
+{ "sans-serif",
+ "cbchyngvba" },
+{ "monospace",
+ "Qbphzrag" },
+{ "sans-serif",
+ "bowrpg" },
+{ "sans-serif",
+ "hfvat" },
+{ "sans-serif",
+ "bayl" },
+{ "sans-serif",
+ "pnyyf;" },
+{ "sans-serif",
+ "ybnqvat" },
+{ "sans-serif",
+ "fnivat" },
+{ "sans-serif",
+ "vg" },
+{ "sans-serif",
+ "crefvfgragyl" },
+{ "sans-serif",
+ "yrsg" },
+{ "sans-serif",
+ "cebqhpg" },
+{ "sans-serif",
+ "gung" },
+{ "sans-serif",
+ "vzcyrzragf" },
+{ "sans-serif",
+ "NCV." },
+{ "sans-serif",
+ "1.1.1." },
+{ "sans-serif",
+ "Gur" },
+{ "sans-serif",
+ "QBZ" },
+{ "sans-serif",
+ "Fgehpgher" },
+{ "sans-serif",
+ "Zbqry" },
+{ "sans-serif",
+ "cerfragf" },
+{ "sans-serif",
+ "qbphzragf" },
+{ "sans-serif",
+ "nf" },
+{ "sans-serif",
+ "uvrenepul" },
+{ "monospace",
+ "Abqr" },
+{ "sans-serif",
+ "vzcyrzrag" },
+{ "sans-serif",
+ "bgure," },
+{ "sans-serif",
+ "zber" },
+{ "sans-serif",
+ "fcrpvnyvmrq" },
+{ "sans-serif",
+ "vagresnprf." },
+{ "sans-serif",
+ "Fbzr" },
+{ "sans-serif",
+ "glcrf" },
+{ "sans-serif",
+ "abqrf" },
+{ "sans-serif",
+ "znl" },
+{ "sans-serif",
+ "unir" },
+{ "sans-serif",
+ "puvyq" },
+{ "sans-serif",
+ "inevbhf" },
+{ "sans-serif",
+ "glcrf," },
+{ "sans-serif",
+ "bguref" },
+{ "sans-serif",
+ "ner" },
+{ "sans-serif",
+ "yrns" },
+{ "sans-serif",
+ "pnaabg" },
+{ "sans-serif",
+ "nalguvat" },
+{ "sans-serif",
+ "orybj" },
+{ "sans-serif",
+ "gurz" },
+{ "sans-serif",
+ "fgehpgher." },
+{ "sans-serif",
+ "Sbe" },
+{ "sans-serif",
+ "UGZY," },
+{ "sans-serif",
+ "abqr" },
+{ "sans-serif",
+ "juvpu" },
+{ "sans-serif",
+ "gurl" },
+{ "sans-serif",
+ "puvyqera," },
+{ "sans-serif",
+ "sbyybjf:" },
+{ "sans-serif",
+ "--" },
+{ "monospace",
+ "Ryrzrag" },
+{ "sans-serif",
+ "(znkvzhz" },
+{ "sans-serif",
+ "bar)," },
+{ "monospace",
+ "CebprffvatVafgehpgvba" },
+{ "sans-serif",
+ "," },
+{ "monospace",
+ "Pbzzrag" },
+{ "monospace",
+ "QbphzragGlcr" },
+{ "sans-serif",
+ "bar)" },
+{ "monospace",
+ "QbphzragSentzrag" },
+{ "monospace",
+ "Grkg" },
+{ "monospace",
+ "PQNGNFrpgvba" },
+{ "monospace",
+ "RagvglErsrerapr" },
+{ "sans-serif",
+ "ab" },
+{ "sans-serif",
+ "puvyqera" },
+{ "monospace",
+ "Ngge" },
+{ "monospace",
+ "Ragvgl" },
+{ "monospace",
+ "Abgngvba" },
+{ "sans-serif",
+ "fcrpvsvrf" },
+{ "monospace",
+ "AbqrYvfg" },
+{ "sans-serif",
+ "vagresnpr" },
+{ "sans-serif",
+ "unaqyr" },
+{ "sans-serif",
+ "beqrerq" },
+{ "sans-serif",
+ "yvfgf" },
+{ "monospace",
+ "Abqrf" },
+{ "sans-serif",
+ "fhpu" },
+{ "sans-serif",
+ "be" },
+{ "sans-serif",
+ "ryrzragf" },
+{ "sans-serif",
+ "erghearq" },
+{ "sans-serif",
+ "ol" },
+{ "monospace",
+ "trgRyrzragfOlGntAnzr" },
+{ "sans-serif",
+ "zrgubq" },
+{ "sans-serif",
+ "vagresnpr," },
+{ "monospace",
+ "AnzrqAbqrZnc" },
+{ "sans-serif",
+ "habeqrerq" },
+{ "sans-serif",
+ "frgf" },
+{ "sans-serif",
+ "ersreraprq" },
+{ "sans-serif",
+ "gurve" },
+{ "sans-serif",
+ "anzr" },
+{ "sans-serif",
+ "nggevohgr," },
+{ "sans-serif",
+ "nggevohgrf" },
+{ "sans-serif",
+ "na" },
+{ "sans-serif",
+ "." },
+{ "sans-serif",
+ "yvir" },
+{ "sans-serif",
+ ";" },
+{ "sans-serif",
+ "vf," },
+{ "sans-serif",
+ "punatrf" },
+{ "sans-serif",
+ "haqreylvat" },
+{ "sans-serif",
+ "fgehpgher" },
+{ "sans-serif",
+ "ersyrpgrq" },
+{ "sans-serif",
+ "nyy" },
+{ "sans-serif",
+ "eryrinag" },
+{ "sans-serif",
+ "rknzcyr," },
+{ "sans-serif",
+ "vs" },
+{ "sans-serif",
+ "hfre" },
+{ "sans-serif",
+ "trgf" },
+{ "sans-serif",
+ "pbagnvavat" },
+{ "sans-serif",
+ "gura" },
+{ "sans-serif",
+ "fhofrdhragyl" },
+{ "sans-serif",
+ "nqqf" },
+{ "sans-serif",
+ "ryrzrag" },
+{ "sans-serif",
+ "(be" },
+{ "sans-serif",
+ "erzbirf" },
+{ "sans-serif",
+ "zbqvsvrf" },
+{ "sans-serif",
+ "gurz)," },
+{ "sans-serif",
+ "gubfr" },
+{ "sans-serif",
+ "nhgbzngvpnyyl" },
+{ "sans-serif",
+ "jvgubhg" },
+{ "sans-serif",
+ "shegure" },
+{ "sans-serif",
+ "npgvba" },
+{ "sans-serif",
+ "ba" },
+{ "sans-serif",
+ "hfre'f" },
+{ "sans-serif",
+ "cneg." },
+{ "sans-serif",
+ "Yvxrjvfr," },
+{ "sans-serif",
+ "gerr" },
+{ "sans-serif",
+ "ersreraprf" },
+{ "sans-serif",
+ "Svanyyl," },
+{ "sans-serif",
+ "vaurevg" },
+{ "monospace",
+ "PunenpgreQngn" },
+{ "sans-serif",
+ "vagresnpr." },
+{ "sans-serif",
+ "1.1.2." },
+{ "sans-serif",
+ "Zrzbel" },
+{ "sans-serif",
+ "Znantrzrag" },
+{ "sans-serif",
+ "Zbfg" },
+{ "sans-serif",
+ "NCVf" },
+{ "sans-serif",
+ "qrsvarq" },
+{ "sans-serif",
+ "fcrpvsvpngvba" },
+{ "sans-serif",
+ "vagresnprf" },
+{ "sans-serif",
+ "engure" },
+{ "sans-serif",
+ "guna" },
+{ "sans-serif",
+ "pynffrf." },
+{ "sans-serif",
+ "Gung" },
+{ "sans-serif",
+ "zrnaf" },
+{ "sans-serif",
+ "vzcyrzragngvba" },
+{ "sans-serif",
+ "arrq" },
+{ "sans-serif",
+ "rkcbfr" },
+{ "sans-serif",
+ "zrgubqf" },
+{ "sans-serif",
+ "jvgu" },
+{ "sans-serif",
+ "anzrf" },
+{ "sans-serif",
+ "bcrengvba," },
+{ "sans-serif",
+ "abg" },
+{ "sans-serif",
+ "pynffrf" },
+{ "sans-serif",
+ "pbeerfcbaq" },
+{ "sans-serif",
+ "qverpgyl" },
+{ "sans-serif",
+ "or" },
+{ "sans-serif",
+ "vzcyrzragrq" },
+{ "sans-serif",
+ "guva" },
+{ "sans-serif",
+ "irarre" },
+{ "sans-serif",
+ "gbc" },
+{ "sans-serif",
+ "yrtnpl" },
+{ "sans-serif",
+ "nccyvpngvbaf" },
+{ "sans-serif",
+ "bja" },
+{ "sans-serif",
+ "qngn" },
+{ "sans-serif",
+ "fgehpgherf," },
+{ "sans-serif",
+ "arjre" },
+{ "sans-serif",
+ "qvssrerag" },
+{ "sans-serif",
+ "pynff" },
+{ "sans-serif",
+ "uvrenepuvrf." },
+{ "sans-serif",
+ "beqvanel" },
+{ "sans-serif",
+ "pbafgehpgbef" },
+{ "sans-serif",
+ "(va" },
+{ "sans-serif",
+ "Wnin" },
+{ "sans-serif",
+ "P++" },
+{ "sans-serif",
+ "frafr)" },
+{ "sans-serif",
+ "hfrq" },
+{ "sans-serif",
+ "perngr" },
+{ "sans-serif",
+ "bowrpgf," },
+{ "sans-serif",
+ "fvapr" },
+{ "sans-serif",
+ "pbafgehpgrq" },
+{ "sans-serif",
+ "yvggyr" },
+{ "sans-serif",
+ "eryngvbafuvc" },
+{ "sans-serif",
+ "pbairagvbany" },
+{ "sans-serif",
+ "fbyhgvba" },
+{ "sans-serif",
+ "bowrpg-bevragrq" },
+{ "sans-serif",
+ "qrfvta" },
+{ "sans-serif",
+ "qrsvar" },
+{ "sans-serif",
+ "snpgbel" },
+{ "sans-serif",
+ "vafgnaprf" },
+{ "sans-serif",
+ "Bowrpgf" },
+{ "sans-serif",
+ "vzcyrzragvat" },
+{ "sans-serif",
+ "fbzr" },
+{ "sans-serif",
+ "\"K\"" },
+{ "sans-serif",
+ "perngrq" },
+{ "sans-serif",
+ "\"perngrK()\"" },
+{ "sans-serif",
+ "vagresnpr;" },
+{ "sans-serif",
+ "orpnhfr" },
+{ "sans-serif",
+ "yvir" },
+{ "sans-serif",
+ "pbagrkg" },
+{ "sans-serif",
+ "fcrpvsvp" },
+{ "sans-serif",
+ "Qbphzrag." },
+{ "sans-serif",
+ "2" },
+{ "sans-serif",
+ "qbrf" },
+{ "sans-serif",
+ "abg" },
+{ "sans-serif",
+ "fgnaqneq" },
+{ "sans-serif",
+ "jnl" },
+{ "monospace",
+ "QBZVzcyrzragngvba" },
+{ "sans-serif",
+ "bowrpgf;" },
+{ "sans-serif",
+ "vzcyrzragngvbaf" },
+{ "sans-serif",
+ "zhfg" },
+{ "sans-serif",
+ "cebivqr" },
+{ "sans-serif",
+ "cebcevrgnel" },
+{ "sans-serif",
+ "obbgfgenccvat" },
+{ "sans-serif",
+ "gurfr" },
+{ "sans-serif",
+ "vagresnprf," },
+{ "sans-serif",
+ "bgure" },
+{ "sans-serif",
+ "pna" },
+{ "sans-serif",
+ "ohvyg" },
+{ "sans-serif",
+ "gurer." },
+{ "sans-serif",
+ "qrfvtarq" },
+{ "sans-serif",
+ "pbzcngvoyr" },
+{ "sans-serif",
+ "jvqr" },
+{ "sans-serif",
+ "enatr" },
+{ "sans-serif",
+ "ynathntrf," },
+{ "sans-serif",
+ "vapyhqvat" },
+{ "sans-serif",
+ "obgu" },
+{ "sans-serif",
+ "trareny-hfre" },
+{ "sans-serif",
+ "fpevcgvat" },
+{ "sans-serif",
+ "ynathntrf" },
+{ "sans-serif",
+ "punyyratvat" },
+{ "sans-serif",
+ "zbfgyl" },
+{ "sans-serif",
+ "cebsrffvbany" },
+{ "sans-serif",
+ "cebtenzzref." },
+{ "sans-serif",
+ "Guhf," },
+{ "sans-serif",
+ "bcrengr" },
+{ "sans-serif",
+ "npebff" },
+{ "sans-serif",
+ "inevrgl" },
+{ "sans-serif",
+ "zrzbel" },
+{ "sans-serif",
+ "znantrzrag" },
+{ "sans-serif",
+ "cuvybfbcuvrf," },
+{ "sans-serif",
+ "ynathntr" },
+{ "sans-serif",
+ "ovaqvatf" },
+{ "sans-serif",
+ "qb" },
+{ "sans-serif",
+ "ng" },
+{ "sans-serif",
+ "nyy," },
+{ "sans-serif",
+ "guebhtu" },
+{ "sans-serif",
+ "(abgnoyl" },
+{ "sans-serif",
+ "Wnin)" },
+{ "sans-serif",
+ "rkcyvpvg" },
+{ "sans-serif",
+ "ohg" },
+{ "sans-serif",
+ "nhgbzngvp" },
+{ "sans-serif",
+ "tneontr" },
+{ "sans-serif",
+ "pbyyrpgvba" },
+{ "sans-serif",
+ "zrpunavfz" },
+{ "sans-serif",
+ "erpynvz" },
+{ "sans-serif",
+ "hahfrq" },
+{ "sans-serif",
+ "zrzbel," },
+{ "sans-serif",
+ "(rfcrpvnyyl" },
+{ "sans-serif",
+ "P/P++)" },
+{ "sans-serif",
+ "trarenyyl" },
+{ "sans-serif",
+ "erdhver" },
+{ "sans-serif",
+ "cebtenzzre" },
+{ "sans-serif",
+ "rkcyvpvgyl" },
+{ "sans-serif",
+ "nyybpngr" },
+{ "sans-serif",
+ "genpx" },
+{ "sans-serif",
+ "jurer" },
+{ "sans-serif",
+ "hfrq," },
+{ "sans-serif",
+ "serr" },
+{ "sans-serif",
+ "er-hfr." },
+{ "sans-serif",
+ "Gb" },
+{ "sans-serif",
+ "rafher" },
+{ "sans-serif",
+ "pbafvfgrag" },
+{ "sans-serif",
+ "cyngsbezf," },
+{ "sans-serif",
+ "nqqerff" },
+{ "sans-serif",
+ "vffhrf" },
+{ "sans-serif",
+ "vafgrnq" },
+{ "sans-serif",
+ "yrnirf" },
+{ "sans-serif",
+ "vzcyrzragngvba." },
+{ "sans-serif",
+ "Arvgure" },
+{ "sans-serif",
+ "qrivfrq" },
+{ "sans-serif",
+ "Jbexvat" },
+{ "sans-serif",
+ "Tebhc" },
+{ "sans-serif",
+ "RPZNFpevcg" },
+{ "sans-serif",
+ "nal" },
+{ "sans-serif",
+ "zrgubqf," },
+{ "sans-serif",
+ "P" },
+{ "sans-serif",
+ "P++)" },
+{ "sans-serif",
+ "fhccbeg." },
+{ "sans-serif",
+ "Gurfr" },
+{ "sans-serif",
+ "rkgrafvbaf" },
+{ "sans-serif",
+ "jvyy" },
+{ "sans-serif",
+ "erfcbafvovyvgl" },
+{ "sans-serif",
+ "nqncgvat" },
+{ "sans-serif",
+ "ynathntr," },
+{ "sans-serif",
+ "Tebhc." },
+{ "sans-serif",
+ "1.1.3." },
+{ "sans-serif",
+ "Anzvat" },
+{ "sans-serif",
+ "Pbairagvbaf" },
+{ "sans-serif",
+ "Juvyr" },
+{ "sans-serif",
+ "jbhyq" },
+{ "sans-serif",
+ "avpr" },
+{ "sans-serif",
+ "nggevohgr" },
+{ "sans-serif",
+ "fubeg," },
+{ "sans-serif",
+ "vasbezngvir," },
+{ "sans-serif",
+ "vagreanyyl" },
+{ "sans-serif",
+ "pbafvfgrag," },
+{ "sans-serif",
+ "snzvyvne" },
+{ "sans-serif",
+ "hfref" },
+{ "sans-serif",
+ "fvzvyne" },
+{ "sans-serif",
+ "NCVf," },
+{ "sans-serif",
+ "fubhyq" },
+{ "sans-serif",
+ "pynfu" },
+{ "sans-serif",
+ "fhccbegrq" },
+{ "sans-serif",
+ "vzcyrzragngvbaf." },
+{ "sans-serif",
+ "Shegurezber," },
+{ "sans-serif",
+ "BZT" },
+{ "sans-serif",
+ "VQY" },
+{ "monospace",
+ "RPZNFpevcg" },
+{ "sans-serif",
+ "fvtavsvpnag" },
+{ "sans-serif",
+ "yvzvgngvbaf" },
+{ "sans-serif",
+ "novyvgl" },
+{ "sans-serif",
+ "qvfnzovthngr" },
+{ "sans-serif",
+ "anzrfcnprf" },
+{ "sans-serif",
+ "znxr" },
+{ "sans-serif",
+ "qvssvphyg" },
+{ "sans-serif",
+ "nibvq" },
+{ "sans-serif",
+ "anzvat" },
+{ "sans-serif",
+ "pbasyvpgf" },
+{ "sans-serif",
+ "anzrf." },
+{ "sans-serif",
+ "Fb," },
+{ "sans-serif",
+ "graq" },
+{ "sans-serif",
+ "ybat" },
+{ "sans-serif",
+ "dhvgr" },
+{ "sans-serif",
+ "qrfpevcgvir" },
+{ "sans-serif",
+ "beqre" },
+{ "sans-serif",
+ "havdhr" },
+{ "sans-serif",
+ "raivebazragf." },
+{ "sans-serif",
+ "unf" },
+{ "sans-serif",
+ "nggrzcgrq" },
+{ "sans-serif",
+ "vgf" },
+{ "sans-serif",
+ "hfr" },
+{ "sans-serif",
+ "grezf," },
+{ "sans-serif",
+ "rira" },
+{ "sans-serif",
+ "gubhtu" },
+{ "sans-serif",
+ "pbzzba" },
+{ "sans-serif",
+ "qvfgvapgvbaf" },
+{ "sans-serif",
+ "NCVf." },
+{ "sans-serif",
+ "jr" },
+{ "sans-serif",
+ "\"erzbir\"" },
+{ "sans-serif",
+ "jura" },
+{ "sans-serif",
+ "fgehpgheny" },
+{ "sans-serif",
+ "zbqry," },
+{ "sans-serif",
+ "\"qryrgr\"" },
+{ "sans-serif",
+ "evq" },
+{ "sans-serif",
+ "fbzrguvat" },
+{ "sans-serif",
+ "zbqry." },
+{ "sans-serif",
+ "guvat" },
+{ "sans-serif",
+ "qryrgrq" },
+{ "sans-serif",
+ "erghearq." },
+{ "sans-serif",
+ "erzbirq" },
+{ "sans-serif",
+ "erghearq," },
+{ "sans-serif",
+ "znxrf" },
+{ "sans-serif",
+ "frafr" },
+{ "sans-serif",
+ "erghea" },
+{ "sans-serif",
+ "vg." },
+{ "sans-serif",
+ "1.1.4." },
+{ "sans-serif",
+ "Vaurevgnapr" },
+{ "sans-serif",
+ "if." },
+{ "sans-serif",
+ "Synggrarq" },
+{ "sans-serif",
+ "Ivrjf" },
+{ "sans-serif",
+ "bs" },
+{ "sans-serif",
+ "gur" },
+{ "sans-serif",
+ "NCV" },
+{ "sans-serif",
+ "NCVf" },
+{ "sans-serif",
+ "cerfrag" },
+{ "sans-serif",
+ "gjb" },
+{ "sans-serif",
+ "fbzrjung" },
+{ "sans-serif",
+ "KZY/UGZY" },
+{ "sans-serif",
+ "qbphzrag:" },
+{ "sans-serif",
+ "bar" },
+{ "sans-serif",
+ "cerfragvat" },
+{ "sans-serif",
+ "\"bowrpg" },
+{ "sans-serif",
+ "bevragrq\"" },
+{ "sans-serif",
+ "nccebnpu" },
+{ "sans-serif",
+ "vaurevgnapr" },
+{ "sans-serif",
+ "\"fvzcyvsvrq\"" },
+{ "sans-serif",
+ "ivrj" },
+{ "sans-serif",
+ "znavchyngvba" },
+{ "sans-serif",
+ "qbar" },
+{ "sans-serif",
+ "ivn" },
+{ "sans-serif",
+ "erdhvevat" },
+{ "sans-serif",
+ "pnfgf" },
+{ "sans-serif",
+ "P-yvxr" },
+{ "sans-serif",
+ "ynathntrf)" },
+{ "sans-serif",
+ "dhrel" },
+{ "sans-serif",
+ "pnyyf" },
+{ "sans-serif",
+ "PBZ" },
+{ "sans-serif",
+ "bcrengvbaf" },
+{ "sans-serif",
+ "snveyl" },
+{ "sans-serif",
+ "rkcrafvir" },
+{ "sans-serif",
+ "PBZ," },
+{ "sans-serif",
+ "cresbeznapr-pevgvpny" },
+{ "sans-serif",
+ "raivebazragf," },
+{ "sans-serif",
+ "fb" },
+{ "sans-serif",
+ "whfg" },
+{ "sans-serif",
+ "Orpnhfr" },
+{ "sans-serif",
+ "znal" },
+{ "sans-serif",
+ "svaq" },
+{ "sans-serif",
+ "rnfvre" },
+{ "sans-serif",
+ "haqrefgnaq" },
+{ "sans-serif",
+ "\"rirelguvat" },
+{ "sans-serif",
+ "\"" },
+{ "sans-serif",
+ "QBZ," },
+{ "sans-serif",
+ "fhccbeg" },
+{ "sans-serif",
+ "shyy" },
+{ "sans-serif",
+ "uvture-yriry" },
+{ "sans-serif",
+ "jub" },
+{ "sans-serif",
+ "cersre" },
+{ "sans-serif",
+ "Va" },
+{ "sans-serif",
+ "cenpgvpr," },
+{ "sans-serif",
+ "gurer" },
+{ "sans-serif",
+ "pregnva" },
+{ "sans-serif",
+ "nzbhag" },
+{ "sans-serif",
+ "erqhaqnapl" },
+{ "sans-serif",
+ "pbafvqref" },
+{ "sans-serif",
+ "cevznel" },
+{ "sans-serif",
+ "NCV," },
+{ "sans-serif",
+ "\"rkgen\"" },
+{ "sans-serif",
+ "rzcybl," },
+{ "sans-serif",
+ "ryvzvangr" },
+{ "sans-serif",
+ "nanylfvf" },
+{ "sans-serif",
+ "qvpgngr." },
+{ "sans-serif",
+ "(Bs" },
+{ "sans-serif",
+ "pbhefr," },
+{ "sans-serif",
+ "B-B" },
+{ "sans-serif",
+ "lvryqf" },
+{ "sans-serif",
+ "vqragvpny" },
+{ "sans-serif",
+ "qba'g" },
+{ "sans-serif",
+ "fcrpvsl" },
+{ "sans-serif",
+ "pbzcyrgryl" },
+{ "sans-serif",
+ "erqhaqnag" },
+{ "sans-serif",
+ "bar.)" },
+{ "sans-serif",
+ "trarevp" },
+{ "monospace",
+ "abqrAnzr" },
+{ "sans-serif",
+ "fgvyy" },
+{ "monospace",
+ "gntAnzr" },
+{ "sans-serif",
+ "pbagnva" },
+{ "sans-serif",
+ "fnzr" },
+{ "sans-serif",
+ "inyhr," },
+{ "sans-serif",
+ "jbegujuvyr" },
+{ "sans-serif",
+ "obgu," },
+{ "sans-serif",
+ "tvira" },
+{ "sans-serif",
+ "pbafgvghrapvrf" },
+{ "sans-serif",
+ "fngvfsl." },
+{ "sans-serif",
+ "1.1.5." },
+{ "monospace",
+ "QBZFgevat" },
+{ "sans-serif",
+ "glcr" },
+{ "sans-serif",
+ "vagrebcrenovyvgl," },
+{ "sans-serif",
+ "sbyybjvat:" },
+{ "sans-serif",
+ "Glcr" },
+{ "sans-serif",
+ "Qrsvavgvba" },
+{ "sans-serif",
+ "QBZFgevat" },
+{ "sans-serif",
+ "N" },
+{ "monospace",
+ "QBZFgevat" },
+{ "sans-serif",
+ "frdhrapr" },
+{ "sans-serif",
+ "16-ovg" },
+{ "sans-serif",
+ "havgf" },
+{ "sans-serif",
+ "VQY" },
+{ "monospace",
+ "glcrqrs frdhrapr<hafvtarq fubeg> QBZFgevat;" },
+{ "sans-serif",
+ "Nccyvpngvbaf" },
+{ "sans-serif",
+ "rapbqr" },
+{ "sans-serif",
+ "HGS-16" },
+{ "sans-serif",
+ "(qrsvarq" },
+{ "sans-serif",
+ "[" },
+{ "sans-serif",
+ "Havpbqr" },
+{ "sans-serif",
+ "]" },
+{ "sans-serif",
+ "Nzraqzrag" },
+{ "sans-serif",
+ "VFB/VRP" },
+{ "sans-serif",
+ "10646" },
+{ "sans-serif",
+ "])." },
+{ "sans-serif",
+ "rapbqvat" },
+{ "sans-serif",
+ "jnf" },
+{ "sans-serif",
+ "pubfra" },
+{ "sans-serif",
+ "jvqrfcernq" },
+{ "sans-serif",
+ "vaqhfgel" },
+{ "sans-serif",
+ "cenpgvpr." },
+{ "sans-serif",
+ "Abgr" },
+{ "sans-serif",
+ "KZY," },
+{ "sans-serif",
+ "punenpgre" },
+{ "sans-serif",
+ "(naq" },
+{ "sans-serif",
+ "gurersber" },
+{ "sans-serif",
+ "abgngvba" },
+{ "sans-serif",
+ "ahzrevp" },
+{ "sans-serif",
+ "ersreraprf)" },
+{ "sans-serif",
+ "onfrq" },
+{ "sans-serif",
+ "HPF" },
+{ "sans-serif",
+ "[VFB-10646]." },
+{ "sans-serif",
+ "fvatyr" },
+{ "sans-serif",
+ "ersrerapr" },
+{ "sans-serif",
+ "fbhepr" },
+{ "sans-serif",
+ "pnfrf" },
+{ "sans-serif",
+ "16-ovg" },
+{ "sans-serif",
+ "havgf" },
+{ "sans-serif",
+ "(n" },
+{ "sans-serif",
+ "uvtu" },
+{ "sans-serif",
+ "fheebtngr" },
+{ "sans-serif",
+ "ybj" },
+{ "sans-serif",
+ "fheebtngr)." },
+{ "sans-serif",
+ "Abgr:" },
+{ "sans-serif",
+ "Rira" },
+{ "sans-serif",
+ "fgevat" },
+{ "sans-serif",
+ "glcr" },
+{ "sans-serif",
+ "rknzcyr" },
+{ "sans-serif",
+ "Wnin," },
+{ "sans-serif",
+ "obhaq" },
+{ "monospace",
+ "Fgevat" },
+{ "sans-serif",
+ "hfrf" },
+{ "sans-serif",
+ "rapbqvat." },
+{ "sans-serif",
+ "Nf" },
+{ "sans-serif",
+ "Nhthfg" },
+{ "sans-serif",
+ "2000," },
+{ "sans-serif",
+ "([" },
+{ "sans-serif",
+ "BZTVQY" },
+{ "sans-serif",
+ "])" },
+{ "sans-serif",
+ "vapyhqrq" },
+{ "monospace",
+ "jfgevat" },
+{ "sans-serif",
+ "glcr." },
+{ "sans-serif",
+ "Ubjrire," },
+{ "sans-serif",
+ "qrsvavgvba" },
+{ "sans-serif",
+ "qvq" },
+{ "sans-serif",
+ "zrrg" },
+{ "sans-serif",
+ "vagrebcrenovyvgl" },
+{ "sans-serif",
+ "pevgrevn" },
+{ "sans-serif",
+ "eryvrq" },
+{ "sans-serif",
+ "artbgvngvba" },
+{ "sans-serif",
+ "qrpvqr" },
+{ "sans-serif",
+ "jvqgu" },
+{ "sans-serif",
+ "punenpgre." },
+{ "sans-serif",
+ "1.1.6." },
+{ "monospace",
+ "QBZGvzrFgnzc" },
+{ "sans-serif",
+ "QBZGvzrFgnzc" },
+{ "monospace",
+ "QBZGvzrFgnzc" },
+{ "sans-serif",
+ "ercerfragf" },
+{ "sans-serif",
+ "ahzore" },
+{ "sans-serif",
+ "zvyyvfrpbaqf." },
+{ "monospace",
+ "glcrqrs hafvtarq ybat ybat QBZGvzrFgnzc;" },
+{ "sans-serif",
+ "glcrf." },
+{ "monospace",
+ "ybat" },
+{ "sans-serif",
+ "RPZNFpevcg," },
+{ "monospace",
+ "GvzrFgnzc" },
+{ "monospace",
+ "Qngr" },
+{ "monospace",
+ "vagrtre" },
+{ "sans-serif",
+ "gbb" },
+{ "sans-serif",
+ "fznyy." },
+{ "sans-serif",
+ "1.1.7." },
+{ "sans-serif",
+ "Fgevat" },
+{ "sans-serif",
+ "pbzcnevfbaf" },
+{ "sans-serif",
+ "va" },
+{ "sans-serif",
+ "vzcyl" },
+{ "sans-serif",
+ "zngpuvat." },
+{ "sans-serif",
+ "cebprffbef" },
+{ "sans-serif",
+ "nffhzr" },
+{ "sans-serif",
+ "hccrepnfr" },
+{ "sans-serif",
+ "(yrff" },
+{ "sans-serif",
+ "bsgra," },
+{ "sans-serif",
+ "ybjrepnfr)" },
+{ "sans-serif",
+ "abeznyvmngvba" },
+{ "sans-serif",
+ "guvatf" },
+{ "sans-serif",
+ "juvyr" },
+{ "sans-serif",
+ "pnfr" },
+{ "sans-serif",
+ "frafvgvir." },
+{ "sans-serif",
+ "checbfrf" },
+{ "sans-serif",
+ "zngpuvat" },
+{ "sans-serif",
+ "cresbezrq" },
+{ "sans-serif",
+ "cheryl" },
+{ "sans-serif",
+ "ovanel" },
+{ "sans-serif",
+ "pbzcnevfba" },
+{ "sans-serif",
+ "nqqvgvba," },
+{ "sans-serif",
+ "nffhzrf" },
+{ "sans-serif",
+ "abeznyvmngvbaf" },
+{ "sans-serif",
+ "gnxr" },
+{ "sans-serif",
+ "cynpr" },
+{ "sans-serif",
+ "cebprffbe," },
+{ "sans-serif",
+ "orsber" },
+{ "sans-serif",
+ "fgehpgherf" },
+{ "sans-serif",
+ "ohvyg." },
+{ "sans-serif",
+ "Orfvqrf" },
+{ "sans-serif",
+ "sbyqvat," },
+{ "sans-serif",
+ "nqqvgvbany" },
+{ "sans-serif",
+ "nccyvrq" },
+{ "sans-serif",
+ "grkg." },
+{ "sans-serif",
+ "J3P" },
+{ "sans-serif",
+ "V18A" },
+{ "sans-serif",
+ "cebprff" },
+{ "sans-serif",
+ "qrsvavat" },
+{ "sans-serif",
+ "rknpgyl" },
+{ "sans-serif",
+ "arprffnel," },
+{ "sans-serif",
+ "nccyvrq." },
+{ "sans-serif",
+ "rkcrpgf" },
+{ "sans-serif",
+ "rneyl" },
+{ "sans-serif",
+ "abeznyvmngvba," },
+{ "sans-serif",
+ "ernq" },
+{ "sans-serif",
+ "vagb" },
+{ "sans-serif",
+ "nffhzrq" },
+{ "sans-serif",
+ "nyernql" },
+{ "sans-serif",
+ "abeznyvmrq." },
+{ "sans-serif",
+ "nffher" },
+{ "sans-serif",
+ "grkg" },
+{ "sans-serif",
+ "erznvaf" },
+{ "sans-serif",
+ "abeznyvmrq" },
+{ "sans-serif",
+ "orvat" },
+{ "sans-serif",
+ "punatrq." },
+{ "sans-serif",
+ "qrgnvyf," },
+{ "sans-serif",
+ "cyrnfr" },
+{ "sans-serif",
+ "frr" },
+{ "sans-serif",
+ "Punezbq" },
+{ "sans-serif",
+ "]." },
+{ "sans-serif",
+ "1.1.8." },
+{ "sans-serif",
+ "KZY" },
+{ "sans-serif",
+ "Anzrfcnprf" },
+{ "sans-serif",
+ "fhccbegf" },
+{ "sans-serif",
+ "Anzrfcnprf" },
+{ "sans-serif",
+ "nhtzragvat" },
+{ "sans-serif",
+ "frireny" },
+{ "sans-serif",
+ "perngvat" },
+{ "sans-serif",
+ "nffbpvngrq" },
+{ "sans-serif",
+ "anzrfcnpr." },
+{ "sans-serif",
+ "sne" },
+{ "sans-serif",
+ "pbaprearq," },
+{ "sans-serif",
+ "fcrpvny" },
+{ "sans-serif",
+ "qrpynevat" },
+{ "sans-serif",
+ "anzrfcnprf" },
+{ "sans-serif",
+ "rkcbfrq" },
+{ "sans-serif",
+ "znavchyngrq" },
+{ "sans-serif",
+ "yvxr" },
+{ "sans-serif",
+ "nggevohgr." },
+{ "sans-serif",
+ "creznaragyl" },
+{ "sans-serif",
+ "anzrfcnpr" },
+{ "sans-serif",
+ "HEVf" },
+{ "sans-serif",
+ "trg" },
+{ "sans-serif",
+ "perngrq." },
+{ "sans-serif",
+ "Pbafrdhragyl," },
+{ "sans-serif",
+ "zbivat" },
+{ "sans-serif",
+ "jvguva" },
+{ "sans-serif",
+ "qbphzrag," },
+{ "sans-serif",
+ "erfhygf" },
+{ "sans-serif",
+ "punatr" },
+{ "sans-serif",
+ "cersvk" },
+{ "sans-serif",
+ "anzrfcnpr" },
+{ "sans-serif",
+ "HEV." },
+{ "sans-serif",
+ "Fvzvyneyl," },
+{ "sans-serif",
+ "cersvk" },
+{ "sans-serif",
+ "HEV," },
+{ "sans-serif",
+ "punatvat" },
+{ "sans-serif",
+ "abqr," },
+{ "sans-serif",
+ "erfhyg" },
+{ "sans-serif",
+ "erzbiny," },
+{ "sans-serif",
+ "zbqvsvpngvba" },
+{ "sans-serif",
+ "nccebcevngr" },
+{ "sans-serif",
+ "anzrfcnprf." },
+{ "sans-serif",
+ "Anzrfcnpr" },
+{ "sans-serif",
+ "inyvqngvba" },
+{ "sans-serif",
+ "rasbeprq;" },
+{ "sans-serif",
+ "nccyvpngvba" },
+{ "sans-serif",
+ "erfcbafvoyr." },
+{ "sans-serif",
+ "cnegvphyne," },
+{ "sans-serif",
+ "znccvat" },
+{ "sans-serif",
+ "orgjrra" },
+{ "sans-serif",
+ "cersvkrf" },
+{ "sans-serif",
+ "HEVf" },
+{ "sans-serif",
+ "rasbeprq," },
+{ "sans-serif",
+ "trareny," },
+{ "sans-serif",
+ "erfhygvat" },
+{ "sans-serif",
+ "frevnyvmrq" },
+{ "sans-serif",
+ "anviryl." },
+{ "sans-serif",
+ "qrpyner" },
+{ "sans-serif",
+ "rirel" },
+{ "sans-serif",
+ "frevnyvmvat" },
+{ "sans-serif",
+ "qbphzrag." },
+{ "sans-serif",
+ "qbrfa'g" },
+{ "sans-serif",
+ "cresbez" },
+{ "sans-serif",
+ "HEV" },
+{ "sans-serif",
+ "pnabavpnyvmngvba." },
+{ "sans-serif",
+ "inyvq" },
+{ "sans-serif",
+ "(r.t.," },
+{ "sans-serif",
+ "punenpgref" },
+{ "sans-serif",
+ "juvgrfcnprf" },
+{ "sans-serif",
+ "cebcreyl" },
+{ "sans-serif",
+ "rfpncrq)," },
+{ "sans-serif",
+ "yrkvpny" },
+{ "sans-serif",
+ "purpxvat" },
+{ "sans-serif",
+ "cresbezrq." },
+{ "sans-serif",
+ "Nofbyhgr" },
+{ "sans-serif",
+ "gerngrq" },
+{ "sans-serif",
+ "fgevatf" },
+{ "sans-serif",
+ "pbzcnerq" },
+{ "sans-serif",
+ "yvgrenyyl" },
+{ "sans-serif",
+ "Ubj" },
+{ "sans-serif",
+ "eryngvir" },
+{ "sans-serif",
+ "haqrsvarq." },
+{ "sans-serif",
+ "nofbyhgr" },
+{ "sans-serif",
+ "(v.r.," },
+{ "sans-serif",
+ "ortvaavat" },
+{ "sans-serif",
+ "fpurzr" },
+{ "sans-serif",
+ "pbyba)" },
+{ "sans-serif",
+ "hfrq." },
+{ "sans-serif",
+ "purpxvat," },
+{ "sans-serif",
+ "rzcgl" },
+{ "sans-serif",
+ "erny" },
+{ "sans-serif",
+ "zrgubqf." },
+{ "sans-serif",
+ "inyhr" },
+{ "monospace",
+ "ahyy" },
+{ "sans-serif",
+ "anzrfcnprHEV" },
+{ "sans-serif",
+ "cnenzrgre" },
+{ "sans-serif",
+ "jvfu" },
+{ "sans-serif",
+ "qrpynengvba" },
+{ "sans-serif",
+ "ol" },
+{ "sans-serif",
+ "qrsvavgvba" },
+{ "sans-serif",
+ "HEV:" },
+{ "sans-serif",
+ "uggc://jjj.j3.bet/2000/kzyaf/" },
+{ "sans-serif",
+ "\"." },
+{ "sans-serif",
+ "jubfr" },
+{ "sans-serif",
+ "dhnyvsvrq" },
+{ "sans-serif",
+ "anzr" },
+{ "sans-serif",
+ "\"kzyaf\"." },
+{ "sans-serif",
+ "Nygubhtu," },
+{ "sans-serif",
+ "gvzr" },
+{ "sans-serif",
+ "jevgvat," },
+{ "sans-serif",
+ "cneg" },
+{ "sans-serif",
+ "]," },
+{ "sans-serif",
+ "cynaarq" },
+{ "sans-serif",
+ "vapbecbengrq" },
+{ "sans-serif",
+ "shgher" },
+{ "sans-serif",
+ "erivfvba." },
+{ "sans-serif",
+ "anzrfcnprf," },
+{ "sans-serif",
+ "yvfg" },
+{ "sans-serif",
+ "nyjnlf" },
+{ "sans-serif",
+ "pbeerfcbaqvat" },
+{ "sans-serif",
+ "gehr" },
+{ "sans-serif",
+ "ragvgl" },
+{ "sans-serif",
+ "pbagnvaf" },
+{ "sans-serif",
+ "haobhaq" },
+{ "sans-serif",
+ "cersvkrf" },
+{ "sans-serif",
+ "pnfr," },
+{ "sans-serif",
+ "qrfpraqnagf" },
+{ "sans-serif",
+ "qrcraqvat" },
+{ "sans-serif",
+ "ner." },
+{ "sans-serif",
+ "Nyfb," },
+{ "sans-serif",
+ "orpnhfr," },
+{ "sans-serif",
+ "erznva" },
+{ "sans-serif",
+ "yrnq" },
+{ "sans-serif",
+ "frevnyvmrq." },
+{ "monospace",
+ "perngrRagvglErsrerapr" },
+{ "sans-serif",
+ "ragvgvrf," },
+{ "sans-serif",
+ "haobhaq." },
+{ "sans-serif",
+ "erfbyir" },
+{ "sans-serif",
+ "cersvkrf." },
+{ "sans-serif",
+ "ernfbaf," },
+{ "sans-serif",
+ "ragvgvrf" },
+{ "sans-serif",
+ "nibvqrq" },
+{ "sans-serif",
+ "rkgerzr" },
+{ "sans-serif",
+ "pner." },
+{ "sans-serif",
+ "vapyhqr" },
+{ "sans-serif",
+ "unaqyvat" },
+{ "sans-serif",
+ "gurfr." },
+{ "sans-serif",
+ "arj" },
+{ "monospace",
+ "perngrRyrzragAF" },
+{ "monospace",
+ "perngrNggevohgrAF" },
+{ "sans-serif",
+ "zrnag" },
+{ "sans-serif",
+ "njner" },
+{ "sans-serif",
+ "nccyvpngvbaf." },
+{ "sans-serif",
+ "Fvzcyr" },
+{ "monospace",
+ "perngrRyrzrag" },
+{ "monospace",
+ "perngrNggevohgr" },
+{ "sans-serif",
+ "Ryrzragf" },
+{ "sans-serif",
+ "cersvk," },
+{ "sans-serif",
+ "ybpny" },
+{ "sans-serif",
+ "anzr." },
+{ "sans-serif",
+ "vtabenag." },
+{ "sans-serif",
+ "Gurersber," },
+{ "sans-serif",
+ "fnsr" },
+{ "sans-serif",
+ "qrnyvat" },
+{ "sans-serif",
+ "barf" },
+{ "sans-serif",
+ "nibvqrq." },
+{ "sans-serif",
+ "fbyryl" },
+{ "sans-serif",
+ "vqragvsl" },
+{ "sans-serif",
+ "Ba" },
+{ "sans-serif",
+ "pbagenel," },
+{ "sans-serif",
+ "eryngrq" },
+{ "monospace",
+ "anzrfcnprHEV" },
+{ "monospace",
+ "ybpnyAnzr" },
+{ "sans-serif",
+ "shaqnzragny" },
+{ "sans-serif",
+ "qvssrerapr," },
+{ "sans-serif",
+ "zvkvat" },
+{ "sans-serif",
+ "hacerqvpgnoyr" },
+{ "sans-serif",
+ "erfhygf." },
+{ "monospace",
+ "frgNggevohgrAF" },
+{ "sans-serif",
+ "zber)" },
+{ "sans-serif",
+ "f." },
+{ "sans-serif",
+ "Pnyyvat" },
+{ "monospace",
+ "trgNggevohgr" },
+{ "sans-serif",
+ "pbhyq" },
+{ "sans-serif",
+ "nggevohgrf." },
+{ "sans-serif",
+ "qrcraqf" },
+{ "monospace",
+ "frgNggevohgrAbqr" },
+{ "monospace",
+ "abqrAnzrf" },
+{ "monospace",
+ "cersvk" },
+{ "monospace",
+ "trgNggevohgrAbqrAF" },
+{ "sans-serif",
+ "rvgure" },
+{ "sans-serif",
+ "qrcraqrag" },
+{ "sans-serif",
+ "znaare." },
+{ "sans-serif",
+ "thnenagrr" },
+{ "sans-serif",
+ "anzrq" },
+{ "sans-serif",
+ "vgrz" },
+{ "sans-serif",
+ "vgrz," },
+{ "sans-serif",
+ "abqr." },
+{ "sans-serif",
+ "vafgnapr," },
+{ "monospace",
+ "frgNggevohgr" },
+{ "sans-serif",
+ "nssrpg" },
+{ "monospace",
+ "trgNggevohgrAF" },
+{ "sans-serif",
+ "erfcrpgviryl," },
+{ "sans-serif",
+ "erghea." },
+{ "sans-serif",
+ "1.2." },
+{ "sans-serif",
+ "Shaqnzragny" },
+{ "sans-serif",
+ "pbafvqrerq" },
+{ "sans-serif",
+ "shaqnzragny" },
+{ "sans-serif",
+ "shyyl" },
+{ "sans-serif",
+ "hayrff" },
+{ "sans-serif",
+ "bgurejvfr" },
+{ "sans-serif",
+ "fcrpvsvrq." },
+{ "sans-serif",
+ "Rkprcgvba" },
+{ "sans-serif",
+ "QBZRkprcgvba" },
+{ "sans-serif",
+ "envfr" },
+{ "sans-serif",
+ "rkprcgvbaf" },
+{ "sans-serif",
+ "\"rkprcgvbany\"" },
+{ "sans-serif",
+ "pvephzfgnaprf," },
+{ "sans-serif",
+ "v.r.," },
+{ "sans-serif",
+ "bcrengvba" },
+{ "sans-serif",
+ "vzcbffvoyr" },
+{ "sans-serif",
+ "(rvgure" },
+{ "sans-serif",
+ "ybtvpny" },
+{ "sans-serif",
+ "ybfg," },
+{ "sans-serif",
+ "orpbzr" },
+{ "sans-serif",
+ "hafgnoyr)." },
+{ "sans-serif",
+ "reebe" },
+{ "sans-serif",
+ "inyhrf" },
+{ "sans-serif",
+ "cebprffvat" },
+{ "sans-serif",
+ "fvghngvbaf," },
+{ "sans-serif",
+ "bhg-bs-obhaq" },
+{ "sans-serif",
+ "reebef" },
+{ "sans-serif",
+ "Vzcyrzragngvbaf" },
+{ "sans-serif",
+ "haqre" },
+{ "sans-serif",
+ "pvephzfgnaprf." },
+{ "sans-serif",
+ "vzcyrzragngvba-qrcraqrag" },
+{ "sans-serif",
+ "rkprcgvba" },
+{ "sans-serif",
+ "nethzrag" },
+{ "sans-serif",
+ "cnffrq." },
+{ "sans-serif",
+ "flfgrzf" },
+{ "sans-serif",
+ "pbaprcg" },
+{ "sans-serif",
+ "rkprcgvbaf." },
+{ "sans-serif",
+ "flfgrzf," },
+{ "sans-serif",
+ "pbaqvgvbaf" },
+{ "sans-serif",
+ "vaqvpngrq" },
+{ "sans-serif",
+ "angvir" },
+{ "sans-serif",
+ "ercbegvat" },
+{ "sans-serif",
+ "zrpunavfzf." },
+{ "sans-serif",
+ "ovaqvatf," },
+{ "sans-serif",
+ "pbqrf" },
+{ "sans-serif",
+ "yvfgrq" },
+{ "sans-serif",
+ "qrfpevcgvbaf." },
+{ "monospace",
+ "rkprcgvba QBZRkprcgvba {" },
+{ "monospace",
+ " hafvtarq fubeg pbqr;" },
+{ "monospace",
+ "};" },
+{ "monospace",
+ "// RkprcgvbaPbqr" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAQRK_FVMR_REE = 1;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg QBZFGEVAT_FVMR_REE = 2;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg UVRENEPUL_ERDHRFG_REE = 3;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg JEBAT_QBPHZRAG_REE = 4;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAINYVQ_PUNENPGRE_REE = 5;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg AB_QNGN_NYYBJRQ_REE = 6;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg AB_ZBQVSVPNGVBA_NYYBJRQ_REE = 7;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg ABG_SBHAQ_REE = 8;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg ABG_FHCCBEGRQ_REE = 9;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAHFR_NGGEVOHGR_REE = 10;" },
+{ "monospace",
+ "// Vagebqhprq va QBZ Yriry 2:" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAINYVQ_FGNGR_REE = 11;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg FLAGNK_REE = 12;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAINYVQ_ZBQVSVPNGVBA_REE = 13;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg ANZRFCNPR_REE = 14;" },
+{ "monospace",
+ "pbafg hafvtarq fubeg VAINYVQ_NPPRFF_REE = 15;" },
+{ "sans-serif",
+ "tebhc" },
+{ "sans-serif",
+ "RkprcgvbaPbqr" },
+{ "sans-serif",
+ "Na" },
+{ "sans-serif",
+ "vagrtre" },
+{ "sans-serif",
+ "vaqvpngvat" },
+{ "sans-serif",
+ "trarengrq." },
+{ "sans-serif",
+ "Bgure" },
+{ "sans-serif",
+ "erfreirq" },
+{ "sans-serif",
+ "cbffvoyr" },
+{ "sans-serif",
+ "hfr." },
+{ "sans-serif",
+ "Qrsvarq" },
+{ "sans-serif",
+ "Pbafgnagf" },
+{ "monospace",
+ "QBZFGEVAT_FVMR_REE" },
+{ "sans-serif",
+ "Vs" },
+{ "sans-serif",
+ "svg" },
+{ "sans-serif",
+ "QBZFgevat" },
+{ "monospace",
+ "UVRENEPUL_ERDHRFG_REE" },
+{ "sans-serif",
+ "vafregrq" },
+{ "sans-serif",
+ "fbzrjurer" },
+{ "sans-serif",
+ "orybat" },
+{ "monospace",
+ "VAQRK_FVMR_REE" },
+{ "sans-serif",
+ "fvmr" },
+{ "sans-serif",
+ "artngvir," },
+{ "sans-serif",
+ "terngre" },
+{ "sans-serif",
+ "nyybjrq" },
+{ "monospace",
+ "VAHFR_NGGEVOHGR_REE" },
+{ "sans-serif",
+ "nggrzcg" },
+{ "sans-serif",
+ "znqr" },
+{ "sans-serif",
+ "nqq" },
+{ "sans-serif",
+ "ryfrjurer" },
+{ "monospace",
+ "VAINYVQ_NPPRFF_REE" },
+{ "sans-serif",
+ "," },
+{ "sans-serif",
+ "vagebqhprq" },
+{ "sans-serif",
+ "va" },
+{ "sans-serif",
+ "QBZ" },
+{ "sans-serif",
+ "Yriry" },
+{ "sans-serif",
+ "2" },
+{ "sans-serif",
+ "." },
+{ "sans-serif",
+ "bowrpg." },
+{ "monospace",
+ "VAINYVQ_PUNENPGRE_REE" },
+{ "sans-serif",
+ "vainyvq" },
+{ "sans-serif",
+ "vyyrtny" },
+{ "sans-serif",
+ "fcrpvsvrq," },
+{ "sans-serif",
+ "Frr" },
+{ "sans-serif",
+ "cebqhpgvba" },
+{ "sans-serif",
+ "2" },
+{ "sans-serif",
+ "yrtny" },
+{ "sans-serif",
+ "punenpgre," },
+{ "sans-serif",
+ "5" },
+{ "monospace",
+ "VAINYVQ_ZBQVSVPNGVBA_REE" },
+{ "sans-serif",
+ "zbqvsl" },
+{ "monospace",
+ "VAINYVQ_FGNGR_REE" },
+{ "sans-serif",
+ "abg," },
+{ "sans-serif",
+ "ybatre," },
+{ "sans-serif",
+ "hfnoyr." },
+{ "monospace",
+ "ANZRFCNPR_REE" },
+{ "sans-serif",
+ "vapbeerpg" },
+{ "sans-serif",
+ "ertneq" },
+{ "monospace",
+ "ABG_SBHAQ_REE" },
+{ "sans-serif",
+ "rkvfg" },
+{ "monospace",
+ "ABG_FHCCBEGRQ_REE" },
+{ "sans-serif",
+ "erdhrfgrq" },
+{ "monospace",
+ "AB_QNGN_NYYBJRQ_REE" },
+{ "monospace",
+ "AB_ZBQVSVPNGVBA_NYYBJRQ_REE" },
+{ "sans-serif",
+ "zbqvsvpngvbaf" },
+{ "monospace",
+ "FLAGNK_REE" },
+{ "monospace",
+ "JEBAT_QBPHZRAG_REE" },
+{ "sans-serif",
+ "(gung" },
+{ "sans-serif",
+ "vg)" },
+{ "sans-serif",
+ "Vagresnpr" },
+{ "sans-serif",
+ "QBZVzcyrzragngvba" },
+{ "sans-serif",
+ "cebivqrf" },
+{ "sans-serif",
+ "cresbezvat" },
+{ "sans-serif",
+ "vaqrcraqrag" },
+{ "sans-serif",
+ "cnegvphyne" },
+{ "sans-serif",
+ "vafgnapr" },
+{ "monospace",
+ "vagresnpr QBZVzcyrzragngvba {" },
+{ "monospace",
+ " obbyrna unfSrngher(va QBZFgevat srngher, " },
+{ "monospace",
+ " va QBZFgevat irefvba);" },
+{ "monospace",
+ " // Vagebqhprq va QBZ Yriry 2:" },
+{ "monospace",
+ " QbphzragGlcr perngrQbphzragGlcr(va QBZFgevat dhnyvsvrqAnzr, " },
+{ "monospace",
+ " va QBZFgevat choyvpVq, " },
+{ "monospace",
+ " va QBZFgevat flfgrzVq)" },
+{ "monospace",
+ " envfrf(QBZRkprcgvba);" },
+{ "monospace",
+ " Qbphzrag perngrQbphzrag(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat dhnyvsvrqAnzr, " },
+{ "monospace",
+ " va QbphzragGlcr qbpglcr)" },
+{ "sans-serif",
+ "Zrgubqf" },
+{ "monospace",
+ "perngrQbphzrag" },
+{ "sans-serif",
+ "Perngrf" },
+{ "sans-serif",
+ "ryrzrag." },
+{ "sans-serif",
+ "UGZY-bayl" },
+{ "sans-serif",
+ "zrgubq." },
+{ "sans-serif",
+ "Cnenzrgref" },
+{ "monospace",
+ "anzrfcnprHEV" },
+{ "sans-serif",
+ "bs" },
+{ "sans-serif",
+ "glcr" },
+{ "monospace",
+ "QBZFgevat" },
+{ "sans-serif",
+ "HEV" },
+{ "sans-serif",
+ "ryrzrag" },
+{ "sans-serif",
+ "perngr." },
+{ "monospace",
+ "dhnyvsvrqAnzr" },
+{ "monospace",
+ "qbpglcr" },
+{ "monospace",
+ "QbphzragGlcr" },
+{ "sans-serif",
+ "Jura" },
+{ "monospace",
+ "qbpglcr" },
+{ "monospace",
+ "Abqr.bjareQbphzrag" },
+{ "sans-serif",
+ "Erghea" },
+{ "sans-serif",
+ "Inyhr" },
+{ "sans-serif",
+ "Rkprcgvbaf" },
+{ "monospace",
+ "QBZRkprcgvba" },
+{ "sans-serif",
+ "VAINYVQ_PUNENPGRE_REE:" },
+{ "sans-serif",
+ "Envfrq" },
+{ "sans-serif",
+ "dhnyvsvrq" },
+{ "sans-serif",
+ "ANZRFCNPR_REE:" },
+{ "monospace",
+ "dhnyvsvrqAnzr" },
+{ "sans-serif",
+ "znysbezrq," },
+{ "sans-serif",
+ "\"kzy\"" },
+{ "sans-serif",
+ "uggc://jjj.j3.bet/KZY/1998/anzrfcnpr" },
+{ "sans-serif",
+ "JEBAT_QBPHZRAG_REE:" },
+{ "sans-serif",
+ "orra" },
+{ "monospace",
+ "perngrQbphzragGlcr" },
+{ "sans-serif",
+ "Ragvgl" },
+{ "sans-serif",
+ "qrpynengvbaf" },
+{ "sans-serif",
+ "abgngvbaf" },
+{ "sans-serif",
+ "ninvynoyr." },
+{ "sans-serif",
+ "rkcnafvbaf" },
+{ "sans-serif",
+ "qrsnhyg" },
+{ "sans-serif",
+ "nqqvgvbaf" },
+{ "sans-serif",
+ "bpphe." },
+{ "sans-serif",
+ "Vg" },
+{ "sans-serif",
+ "rkcrpgrq" },
+{ "sans-serif",
+ "irefvba" },
+{ "sans-serif",
+ "cbchyngvat" },
+{ "monospace",
+ "choyvpVq" },
+{ "sans-serif",
+ "rkgreany" },
+{ "sans-serif",
+ "fhofrg" },
+{ "sans-serif",
+ "choyvp" },
+{ "sans-serif",
+ "vqragvsvre." },
+{ "monospace",
+ "flfgrzVq" },
+{ "sans-serif",
+ "flfgrz" },
+{ "sans-serif",
+ "znysbezrq." },
+{ "monospace",
+ "unfSrngher" },
+{ "sans-serif",
+ "Grfg" },
+{ "sans-serif",
+ "srngher." },
+{ "monospace",
+ "srngher" },
+{ "sans-serif",
+ "srngher" },
+{ "sans-serif",
+ "grfg" },
+{ "sans-serif",
+ "(pnfr-vafrafvgvir)." },
+{ "sans-serif",
+ "srngherf" },
+{ "sans-serif",
+ "guebhtubhg" },
+{ "sans-serif",
+ "fcrpvsvpngvbaf" },
+{ "sans-serif",
+ "Pbzcyvnapr" },
+{ "sans-serif",
+ "frpgvba." },
+{ "sans-serif",
+ "pbasyvpgf," },
+{ "sans-serif",
+ "pbairagvba," },
+{ "sans-serif",
+ "ersreevat" },
+{ "sans-serif",
+ "bhgfvqr" },
+{ "sans-serif",
+ "erirefvat" },
+{ "sans-serif",
+ "Vagrearg" },
+{ "sans-serif",
+ "qbznva" },
+{ "sans-serif",
+ "crefba" },
+{ "sans-serif",
+ "betnavmngvba" },
+{ "sans-serif",
+ "orybatf" },
+{ "sans-serif",
+ "gb)" },
+{ "sans-serif",
+ "srngher," },
+{ "sans-serif",
+ "pbzcbarag" },
+{ "sans-serif",
+ "pbzcbarag," },
+{ "sans-serif",
+ "cersvk." },
+{ "sans-serif",
+ "FIT" },
+{ "sans-serif",
+ "\"bet.j3p.qbz.fit\"." },
+{ "monospace",
+ "irefvba" },
+{ "sans-serif",
+ "grfg." },
+{ "sans-serif",
+ "2," },
+{ "sans-serif",
+ "\"2.0\"" },
+{ "sans-serif",
+ "\"1.0\"." },
+{ "sans-serif",
+ "fhccbegvat" },
+{ "sans-serif",
+ "pnhfrf" },
+{ "monospace",
+ "gehr" },
+{ "monospace",
+ "obbyrna" },
+{ "sans-serif",
+ "irefvba," },
+{ "monospace",
+ "snyfr" },
+{ "sans-serif",
+ "bgurejvfr." },
+{ "sans-serif",
+ "Ab" },
+{ "sans-serif",
+ "QbphzragSentzrag" },
+{ "sans-serif",
+ "\"yvtugjrvtug\"" },
+{ "sans-serif",
+ "\"zvavzny\"" },
+{ "sans-serif",
+ "irel" },
+{ "sans-serif",
+ "jnag" },
+{ "sans-serif",
+ "noyr" },
+{ "sans-serif",
+ "rkgenpg" },
+{ "sans-serif",
+ "cbegvba" },
+{ "sans-serif",
+ "qbphzrag'f" },
+{ "sans-serif",
+ "sentzrag" },
+{ "sans-serif",
+ "Vzntvar" },
+{ "sans-serif",
+ "pbzznaq" },
+{ "sans-serif",
+ "phg" },
+{ "sans-serif",
+ "erneenatvat" },
+{ "sans-serif",
+ "sentzragf" },
+{ "sans-serif",
+ "nebhaq." },
+{ "sans-serif",
+ "qrfvenoyr" },
+{ "sans-serif",
+ "ubyq" },
+{ "sans-serif",
+ "angheny" },
+{ "sans-serif",
+ "Abqr" },
+{ "sans-serif",
+ "checbfr." },
+{ "sans-serif",
+ "shysvyy" },
+{ "sans-serif",
+ "ebyr," },
+{ "sans-serif",
+ "cbgragvnyyl" },
+{ "sans-serif",
+ "urniljrvtug" },
+{ "sans-serif",
+ "bowrpg," },
+{ "sans-serif",
+ "Jung" },
+{ "sans-serif",
+ "ernyyl" },
+{ "sans-serif",
+ "arrqrq" },
+{ "sans-serif",
+ "yvtugjrvtug" },
+{ "sans-serif",
+ "vafregvat" },
+{ "sans-serif",
+ "nabgure" },
+{ "sans-serif",
+ "nethzragf;" },
+{ "sans-serif",
+ "puvyq" },
+{ "sans-serif",
+ "zbirq" },
+{ "sans-serif",
+ "mreb" },
+{ "sans-serif",
+ "ercerfragvat" },
+{ "sans-serif",
+ "gbcf" },
+{ "sans-serif",
+ "fho-gerrf" },
+{ "sans-serif",
+ "jryy-sbezrq" },
+{ "sans-serif",
+ "qbphzragf" },
+{ "sans-serif",
+ "(nygubhtu" },
+{ "sans-serif",
+ "sbyybj" },
+{ "sans-serif",
+ "ehyrf" },
+{ "sans-serif",
+ "vzcbfrq" },
+{ "sans-serif",
+ "hcba" },
+{ "sans-serif",
+ "jryy-sbezrq" },
+{ "sans-serif",
+ "zhygvcyr" },
+{ "sans-serif",
+ "abqrf)." },
+{ "sans-serif",
+ "zvtug" },
+{ "sans-serif",
+ "Fhpu" },
+{ "sans-serif",
+ "zbqry" },
+{ "sans-serif",
+ "arvgure" },
+{ "sans-serif",
+ "abe" },
+{ "sans-serif",
+ "vaqrrq" },
+{ "sans-serif",
+ "puvyqera)" },
+{ "sans-serif",
+ "vgfrys" },
+{ "sans-serif",
+ "hfrshy" },
+{ "sans-serif",
+ "jvfurf" },
+{ "sans-serif",
+ "fvoyvatf" },
+{ "sans-serif",
+ "npgf" },
+{ "sans-serif",
+ "cnerag" },
+{ "monospace",
+ "vafregOrsber" },
+{ "monospace",
+ "nccraqPuvyq" },
+{ "monospace",
+ "vagresnpr QbphzragSentzrag : Abqr {" },
+{ "sans-serif",
+ "Qbphzrag" },
+{ "sans-serif",
+ "ragver" },
+{ "sans-serif",
+ "Pbaprcghnyyl," },
+{ "sans-serif",
+ "ebbg" },
+{ "sans-serif",
+ "gerr," },
+{ "sans-serif",
+ "qngn." },
+{ "sans-serif",
+ "Fvapr" },
+{ "sans-serif",
+ "ryrzragf," },
+{ "sans-serif",
+ "abqrf," },
+{ "sans-serif",
+ "pbzzragf," },
+{ "sans-serif",
+ "vafgehpgvbaf," },
+{ "sans-serif",
+ "rgp." },
+{ "sans-serif",
+ "snpgbel" },
+{ "monospace",
+ "bjareQbphzrag" },
+{ "sans-serif",
+ "nffbpvngrf" },
+{ "sans-serif",
+ "jrer" },
+{ "monospace",
+ "vagresnpr Qbphzrag : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr QbphzragGlcr qbpglcr;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZVzcyrzragngvba vzcyrzragngvba;" },
+{ "monospace",
+ " ernqbayl nggevohgr Ryrzrag qbphzragRyrzrag;" },
+{ "monospace",
+ " Ryrzrag perngrRyrzrag(va QBZFgevat gntAnzr)" },
+{ "monospace",
+ " QbphzragSentzrag perngrQbphzragSentzrag();" },
+{ "monospace",
+ " Grkg perngrGrkgAbqr(va QBZFgevat qngn);" },
+{ "monospace",
+ " Pbzzrag perngrPbzzrag(va QBZFgevat qngn);" },
+{ "monospace",
+ " PQNGNFrpgvba perngrPQNGNFrpgvba(va QBZFgevat qngn)" },
+{ "monospace",
+ " CebprffvatVafgehpgvba perngrCebprffvatVafgehpgvba(va QBZFgevat gnetrg, " },
+{ "monospace",
+ " va QBZFgevat qngn)" },
+{ "monospace",
+ " Ngge perngrNggevohgr(va QBZFgevat anzr)" },
+{ "monospace",
+ " RagvglErsrerapr perngrRagvglErsrerapr(va QBZFgevat anzr)" },
+{ "monospace",
+ " AbqrYvfg trgRyrzragfOlGntAnzr(va QBZFgevat gntanzr);" },
+{ "monospace",
+ " Abqr vzcbegAbqr(va Abqr vzcbegrqAbqr, " },
+{ "monospace",
+ " va obbyrna qrrc)" },
+{ "monospace",
+ " Ryrzrag perngrRyrzragAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat dhnyvsvrqAnzr)" },
+{ "monospace",
+ " Ngge perngrNggevohgrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat dhnyvsvrqAnzr)" },
+{ "monospace",
+ " AbqrYvfg trgRyrzragfOlGntAnzrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat ybpnyAnzr);" },
+{ "monospace",
+ " Ryrzrag trgRyrzragOlVq(va QBZFgevat ryrzragVq);" },
+{ "sans-serif",
+ "Nggevohgrf" },
+{ "sans-serif",
+ "ernqbayl" },
+{ "sans-serif",
+ "Qbphzrag" },
+{ "sans-serif",
+ "Glcr" },
+{ "sans-serif",
+ "Qrpynengvba" },
+{ "sans-serif",
+ "(frr" },
+{ "sans-serif",
+ ")" },
+{ "sans-serif",
+ "jryy" },
+{ "sans-serif",
+ "ergheaf" },
+{ "sans-serif",
+ "rqvgvat" },
+{ "sans-serif",
+ "Qrpynengvba." },
+{ "monospace",
+ "qbpGlcr" },
+{ "sans-serif",
+ "nygrerq" },
+{ "sans-serif",
+ "jnl," },
+{ "sans-serif",
+ "vaurevgrq" },
+{ "monospace",
+ "vafregAbqr" },
+{ "monospace",
+ "erzbirAbqr" },
+{ "monospace",
+ "qbphzragRyrzrag" },
+{ "monospace",
+ "Ryrzrag" },
+{ "sans-serif",
+ "pbairavrapr" },
+{ "sans-serif",
+ "qverpg" },
+{ "sans-serif",
+ "ebbg" },
+{ "sans-serif",
+ "qbphzragf," },
+{ "sans-serif",
+ "gntAnzr" },
+{ "sans-serif",
+ "\"UGZY\"." },
+{ "monospace",
+ "vzcyrzragngvba" },
+{ "monospace",
+ "QBZVzcyrzragngvba" },
+{ "sans-serif",
+ "unaqyrf" },
+{ "monospace",
+ "perngrNggevohgr" },
+{ "monospace",
+ "anzr" },
+{ "monospace",
+ "anzr" },
+{ "sans-serif",
+ "fgevat." },
+{ "monospace",
+ "perngrNggevohgrAF" },
+{ "sans-serif",
+ "vafgnagvngr." },
+{ "sans-serif",
+ "sbyybjvat" },
+{ "sans-serif",
+ "nggevohgrf:" },
+{ "sans-serif",
+ "Nggevohgr" },
+{ "monospace",
+ "Abqr.abqrAnzr" },
+{ "monospace",
+ "Abqr.anzrfcnprHEV" },
+{ "monospace",
+ "Abqr.cersvk" },
+{ "monospace",
+ "Abqr.ybpnyAnzr" },
+{ "monospace",
+ "Ngge.anzr" },
+{ "monospace",
+ "Abqr.abqrInyhr" },
+{ "sans-serif",
+ "Inyhr" },
+{ "sans-serif",
+ "dhnyvsvrqAnzr" },
+{ "sans-serif",
+ "rkgenpgrq" },
+{ "sans-serif",
+ "ybpny" },
+{ "sans-serif",
+ "\"," },
+{ "sans-serif",
+ "\"kzyaf\"" },
+{ "monospace",
+ "perngrPQNGNFrpgvba" },
+{ "monospace",
+ "qngn" },
+{ "sans-serif",
+ "pbagragf." },
+{ "sans-serif",
+ "ABG_FHCCBEGRQ_REE:" },
+{ "monospace",
+ "perngrPbzzrag" },
+{ "monospace",
+ "perngrQbphzragSentzrag" },
+{ "monospace",
+ "perngrRyrzrag" },
+{ "sans-serif",
+ "xabja" },
+{ "sans-serif",
+ "inyhrf," },
+{ "sans-serif",
+ "nggnpurq" },
+{ "monospace",
+ "gntAnzr" },
+{ "sans-serif",
+ "pnfr-frafvgvir." },
+{ "sans-serif",
+ "cebivqrq" },
+{ "sans-serif",
+ "znccrq" },
+{ "sans-serif",
+ "pnabavpny" },
+{ "sans-serif",
+ "sbez" },
+{ "monospace",
+ "perngrRyrzragAF" },
+{ "monospace",
+ "Ryrzrag.gntAnzr" },
+{ "monospace",
+ "perngrRagvglErsrerapr" },
+{ "sans-serif",
+ "xabja," },
+{ "sans-serif",
+ "qrfpraqnag" },
+{ "sans-serif",
+ "haobhaq;" },
+{ "sans-serif",
+ "(vgf" },
+{ "sans-serif",
+ ")." },
+{ "sans-serif",
+ "ersrerapr." },
+{ "monospace",
+ "perngrCebprffvatVafgehpgvba" },
+{ "sans-serif",
+ "fgevatf." },
+{ "monospace",
+ "gnetrg" },
+{ "sans-serif",
+ "gnetrg" },
+{ "sans-serif",
+ "vafgehpgvba." },
+{ "monospace",
+ "perngrGrkgAbqr" },
+{ "monospace",
+ "trgRyrzragOlVq" },
+{ "sans-serif",
+ "Ergheaf" },
+{ "monospace",
+ "VQ" },
+{ "monospace",
+ "ryrzragVq" },
+{ "sans-serif",
+ "rkvfgf," },
+{ "sans-serif",
+ "Orunivbe" },
+{ "sans-serif",
+ "vasbezngvba" },
+{ "sans-serif",
+ "fnlf" },
+{ "sans-serif",
+ "VQ." },
+{ "sans-serif",
+ "Nggevohgrf" },
+{ "sans-serif",
+ "\"VQ\"" },
+{ "sans-serif",
+ "VQ" },
+{ "sans-serif",
+ "qrsvarq." },
+{ "sans-serif",
+ "xabj" },
+{ "sans-serif",
+ "jurgure" },
+{ "monospace",
+ "ryrzragVq" },
+{ "monospace",
+ "vq" },
+{ "monospace",
+ "trgRyrzragfOlGntAnzr" },
+{ "monospace",
+ "Ryrzragf" },
+{ "sans-serif",
+ "gnt" },
+{ "sans-serif",
+ "rapbhagrerq" },
+{ "sans-serif",
+ "cerbeqre" },
+{ "sans-serif",
+ "genirefny" },
+{ "sans-serif",
+ "gerr." },
+{ "monospace",
+ "gntanzr" },
+{ "sans-serif",
+ "zngpu" },
+{ "sans-serif",
+ "ba." },
+{ "sans-serif",
+ "\"*\"" },
+{ "sans-serif",
+ "zngpurf" },
+{ "sans-serif",
+ "gntf." },
+{ "sans-serif",
+ "zngpurq" },
+{ "monospace",
+ "trgRyrzragfOlGntAnzrAF" },
+{ "sans-serif",
+ "ryrzragf" },
+{ "monospace",
+ "ybpnyAnzr" },
+{ "monospace",
+ "vzcbegAbqr" },
+{ "sans-serif",
+ "Vzcbegf" },
+{ "sans-serif",
+ "cnerag;" },
+{ "sans-serif",
+ "(" },
+{ "monospace",
+ "cneragAbqr" },
+{ "sans-serif",
+ "bevtvany" },
+{ "sans-serif",
+ "qbphzrag;" },
+{ "sans-serif",
+ "perngrf" },
+{ "sans-serif",
+ "pbcl" },
+{ "sans-serif",
+ "vzcbegvat" },
+{ "sans-serif",
+ "bjarq" },
+{ "sans-serif",
+ "abqr'f" },
+{ "monospace",
+ "abqrGlcr" },
+{ "sans-serif",
+ "cyhf" },
+{ "monospace",
+ "pybarAbqr" },
+{ "sans-serif",
+ "nygrerq." },
+{ "sans-serif",
+ "Nqqvgvbany" },
+{ "sans-serif",
+ "pbcvrq" },
+{ "sans-serif",
+ "nggrzcgvat" },
+{ "sans-serif",
+ "zveebe" },
+{ "sans-serif",
+ "orunivbe" },
+{ "sans-serif",
+ "nabgure," },
+{ "sans-serif",
+ "erpbtavmvat" },
+{ "sans-serif",
+ "QGQf" },
+{ "sans-serif",
+ "pnfr." },
+{ "sans-serif",
+ "qrfpevorf" },
+{ "sans-serif",
+ "fcrpvsvpf" },
+{ "sans-serif",
+ "rnpu" },
+{ "sans-serif",
+ "NGGEVOHGR_ABQR" },
+{ "monospace",
+ "bjareRyrzrag" },
+{ "monospace",
+ "fcrpvsvrq" },
+{ "sans-serif",
+ "synt" },
+{ "sans-serif",
+ "trarengrq" },
+{ "sans-serif",
+ "erphefviryl" },
+{ "sans-serif",
+ "vzcbegrq" },
+{ "sans-serif",
+ "ernffrzoyrq" },
+{ "sans-serif",
+ "fhogerr." },
+{ "monospace",
+ "qrrc" },
+{ "sans-serif",
+ "rssrpg" },
+{ "sans-serif",
+ "abqrf;" },
+{ "sans-serif",
+ "pneel" },
+{ "sans-serif",
+ "vzcbegrq." },
+{ "sans-serif",
+ "QBPHZRAG_SENTZRAG_ABQR" },
+{ "sans-serif",
+ "bcgvba" },
+{ "sans-serif",
+ "Bgurejvfr," },
+{ "sans-serif",
+ "fvzcyl" },
+{ "sans-serif",
+ "trarengrf" },
+{ "sans-serif",
+ "QBPHZRAG_ABQR" },
+{ "sans-serif",
+ "QBPHZRAG_GLCR_ABQR" },
+{ "sans-serif",
+ "RYRZRAG_ABQR" },
+{ "sans-serif",
+ "Fcrpvsvrq" },
+{ "sans-serif",
+ "vzcbegrq," },
+{ "sans-serif",
+ "Qrsnhyg" },
+{ "sans-serif",
+ "pbcvrq," },
+{ "sans-serif",
+ "anzr," },
+{ "sans-serif",
+ "nffvtarq." },
+{ "monospace",
+ "vzcbegAbqr" },
+{ "sans-serif",
+ "RAGVGL_ABQR" },
+{ "sans-serif",
+ "ubjrire" },
+{ "sans-serif",
+ "pheerag" },
+{ "sans-serif",
+ "eryrnfr" },
+{ "sans-serif",
+ "ernqbayl." },
+{ "sans-serif",
+ "Novyvgl" },
+{ "sans-serif",
+ "nqqvgvba" },
+{ "sans-serif",
+ "QBZ." },
+{ "sans-serif",
+ "vzcbeg," },
+{ "monospace",
+ "choyvpVq" },
+{ "monospace",
+ "flfgrzVq" },
+{ "monospace",
+ "abgngvbaAnzr" },
+{ "sans-serif",
+ "pbcvrq." },
+{ "sans-serif",
+ "vzcbeg" },
+{ "sans-serif",
+ "erdhrfgrq," },
+{ "sans-serif",
+ "RAGVGL_ERSRERAPR_ABQR" },
+{ "sans-serif",
+ "Bayl" },
+{ "sans-serif",
+ "qrfgvangvba" },
+{ "sans-serif",
+ "qvssreragyl." },
+{ "sans-serif",
+ "ABGNGVBA_ABQR" },
+{ "sans-serif",
+ "arire" },
+{ "sans-serif",
+ "puvyqera." },
+{ "sans-serif",
+ "CEBPRFFVAT_VAFGEHPGVBA_ABQR" },
+{ "sans-serif",
+ "pbcvrf" },
+{ "monospace",
+ "gnetrg" },
+{ "monospace",
+ "qngn" },
+{ "sans-serif",
+ "GRKG_ABQR," },
+{ "sans-serif",
+ "PQNGN_FRPGVBA_ABQR," },
+{ "sans-serif",
+ "PBZZRAG_ABQR" },
+{ "sans-serif",
+ "guerr" },
+{ "sans-serif",
+ "vaurevgvat" },
+{ "monospace",
+ "yratgu" },
+{ "monospace",
+ "vzcbegrqAbqr" },
+{ "monospace",
+ "Abqr" },
+{ "sans-serif",
+ "vzcbeg." },
+{ "monospace",
+ "qrrc" },
+{ "monospace",
+ "obbyrna" },
+{ "sans-serif",
+ "fhogerr" },
+{ "sans-serif",
+ "abqr;" },
+{ "sans-serif",
+ "vgfrys," },
+{ "sans-serif",
+ "rkcynvarq" },
+{ "sans-serif",
+ "nobir." },
+{ "sans-serif",
+ "abqrf." },
+{ "sans-serif",
+ "fhccbegrq." },
+{ "sans-serif",
+ "Abqr" },
+{ "sans-serif",
+ "qngnglcr" },
+{ "sans-serif",
+ "Bowrpg" },
+{ "sans-serif",
+ "Zbqry." },
+{ "sans-serif",
+ "nqqvat" },
+{ "sans-serif",
+ "envfrq." },
+{ "monospace",
+ "abqrInyhr" },
+{ "monospace",
+ "nggevohgrf" },
+{ "sans-serif",
+ "pnfgvat" },
+{ "sans-serif",
+ "qbja" },
+{ "sans-serif",
+ "qrevirq" },
+{ "sans-serif",
+ "boivbhf" },
+{ "sans-serif",
+ ")," },
+{ "sans-serif",
+ "pbairavrag" },
+{ "sans-serif",
+ "zrpunavfzf" },
+{ "sans-serif",
+ "vasbezngvba." },
+{ "monospace",
+ "vagresnpr Abqr {" },
+{ "monospace",
+ " // AbqrGlcr" },
+{ "monospace",
+ " pbafg hafvtarq fubeg RYRZRAG_ABQR = 1;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg NGGEVOHGR_ABQR = 2;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg GRKG_ABQR = 3;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg PQNGN_FRPGVBA_ABQR = 4;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg RAGVGL_ERSRERAPR_ABQR = 5;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg RAGVGL_ABQR = 6;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg CEBPRFFVAT_VAFGEHPGVBA_ABQR = 7;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg PBZZRAG_ABQR = 8;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg QBPHZRAG_ABQR = 9;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg QBPHZRAG_GLCR_ABQR = 10;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg QBPHZRAG_SENTZRAG_ABQR = 11;" },
+{ "monospace",
+ " pbafg hafvtarq fubeg ABGNGVBA_ABQR = 12;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat abqrAnzr;" },
+{ "monospace",
+ " nggevohgr QBZFgevat abqrInyhr;" },
+{ "monospace",
+ " // envfrf(QBZRkprcgvba) ba frggvat" },
+{ "monospace",
+ " // envfrf(QBZRkprcgvba) ba ergevriny" },
+{ "monospace",
+ " ernqbayl nggevohgr hafvtarq fubeg abqrGlcr;" },
+{ "monospace",
+ " ernqbayl nggevohgr Abqr cneragAbqr;" },
+{ "monospace",
+ " ernqbayl nggevohgr AbqrYvfg puvyqAbqrf;" },
+{ "monospace",
+ " ernqbayl nggevohgr Abqr svefgPuvyq;" },
+{ "monospace",
+ " ernqbayl nggevohgr Abqr ynfgPuvyq;" },
+{ "monospace",
+ " ernqbayl nggevohgr Abqr cerivbhfFvoyvat;" },
+{ "monospace",
+ " ernqbayl nggevohgr Abqr arkgFvoyvat;" },
+{ "monospace",
+ " ernqbayl nggevohgr AnzrqAbqrZnc nggevohgrf;" },
+{ "monospace",
+ " // Zbqvsvrq va QBZ Yriry 2:" },
+{ "monospace",
+ " ernqbayl nggevohgr Qbphzrag bjareQbphzrag;" },
+{ "monospace",
+ " Abqr vafregOrsber(va Abqr arjPuvyq, " },
+{ "monospace",
+ " va Abqr ersPuvyq)" },
+{ "monospace",
+ " Abqr ercynprPuvyq(va Abqr arjPuvyq, " },
+{ "monospace",
+ " va Abqr byqPuvyq)" },
+{ "monospace",
+ " Abqr erzbirPuvyq(va Abqr byqPuvyq)" },
+{ "monospace",
+ " Abqr nccraqPuvyq(va Abqr arjPuvyq)" },
+{ "monospace",
+ " obbyrna unfPuvyqAbqrf();" },
+{ "monospace",
+ " Abqr pybarAbqr(va obbyrna qrrc);" },
+{ "monospace",
+ " ibvq abeznyvmr();" },
+{ "monospace",
+ " obbyrna vfFhccbegrq(va QBZFgevat srngher, " },
+{ "monospace",
+ " va QBZFgevat irefvba);" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat anzrfcnprHEV;" },
+{ "monospace",
+ " nggevohgr QBZFgevat cersvk;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat ybpnyAnzr;" },
+{ "monospace",
+ " obbyrna unfNggevohgrf();" },
+{ "sans-serif",
+ "AbqrGlcr" },
+{ "sans-serif",
+ "vf." },
+{ "sans-serif",
+ "Ahzrevp" },
+{ "sans-serif",
+ "hc" },
+{ "sans-serif",
+ "200" },
+{ "monospace",
+ "NGGEVOHGR_ABQR" },
+{ "monospace",
+ "PQNGN_FRPGVBA_ABQR" },
+{ "monospace",
+ "PBZZRAG_ABQR" },
+{ "monospace",
+ "QBPHZRAG_SENTZRAG_ABQR" },
+{ "monospace",
+ "QBPHZRAG_ABQR" },
+{ "monospace",
+ "QBPHZRAG_GLCR_ABQR" },
+{ "monospace",
+ "RYRZRAG_ABQR" },
+{ "monospace",
+ "RAGVGL_ABQR" },
+{ "monospace",
+ "RAGVGL_ERSRERAPR_ABQR" },
+{ "monospace",
+ "ABGNGVBA_ABQR" },
+{ "monospace",
+ "CEBPRFFVAT_VAFGEHPGVBA_ABQR" },
+{ "monospace",
+ "GRKG_ABQR" },
+{ "sans-serif",
+ "inel" },
+{ "sans-serif",
+ "nppbeqvat" },
+{ "sans-serif",
+ "Ngge" },
+{ "sans-serif",
+ "abqrAnzr" },
+{ "sans-serif",
+ "abqrInyhr" },
+{ "sans-serif",
+ "nggevohgrf" },
+{ "sans-serif",
+ "PQNGNFrpgvba" },
+{ "sans-serif",
+ "Pbzzrag" },
+{ "sans-serif",
+ "QbphzragSentzrag" },
+{ "sans-serif",
+ "QbphzragGlcr" },
+{ "sans-serif",
+ "Ryrzrag" },
+{ "sans-serif",
+ "RagvglErsrerapr" },
+{ "sans-serif",
+ "Abgngvba" },
+{ "sans-serif",
+ "CebprffvatVafgehpgvba" },
+{ "sans-serif",
+ "Grkg" },
+{ "sans-serif",
+ "#pqngn-frpgvba" },
+{ "sans-serif",
+ "#pbzzrag" },
+{ "sans-serif",
+ "#qbphzrag" },
+{ "sans-serif",
+ "#qbphzrag-sentzrag" },
+{ "sans-serif",
+ "#grkg" },
+{ "sans-serif",
+ "PQNGN" },
+{ "sans-serif",
+ "Frpgvba" },
+{ "sans-serif",
+ "pbzzrag" },
+{ "sans-serif",
+ "ahyy" },
+{ "sans-serif",
+ "rkpyhqvat" },
+{ "sans-serif",
+ "AnzrqAbqrZnc" },
+{ "monospace",
+ "nggevohgrf" },
+{ "monospace",
+ "AnzrqAbqrZnc" },
+{ "sans-serif",
+ "(vs" },
+{ "monospace",
+ "puvyqAbqrf" },
+{ "monospace",
+ "AbqrYvfg" },
+{ "monospace",
+ "svefgPuvyq" },
+{ "sans-serif",
+ "svefg" },
+{ "monospace",
+ "ynfgPuvyq" },
+{ "sans-serif",
+ "ynfg" },
+{ "sans-serif",
+ "ernqbayl," },
+{ "monospace",
+ "RYRZRAG_ABQR" },
+{ "monospace",
+ "NGGEVOHGR_ABQR" },
+{ "sans-serif",
+ "zrgubq," },
+{ "sans-serif",
+ "hafcrpvsvrq." },
+{ "sans-serif",
+ "pbzchgrq" },
+{ "sans-serif",
+ "ybbxhc" },
+{ "sans-serif",
+ "rknzvangvba" },
+{ "sans-serif",
+ "fpbcr." },
+{ "sans-serif",
+ "zreryl" },
+{ "sans-serif",
+ "gvzr." },
+{ "sans-serif",
+ "Cre" },
+{ "sans-serif",
+ "Fcrpvsvpngvba" },
+{ "sans-serif",
+ "gb." },
+{ "sans-serif",
+ "anzrfcnpr," },
+{ "monospace",
+ "arkgFvoyvat" },
+{ "sans-serif",
+ "vzzrqvngryl" },
+{ "monospace",
+ "abqrAnzr" },
+{ "sans-serif",
+ "glcr;" },
+{ "sans-serif",
+ "gnoyr" },
+{ "monospace",
+ "abqrGlcr" },
+{ "monospace",
+ "hafvtarq" },
+{ "monospace",
+ "fubeg" },
+{ "sans-serif",
+ "pbqr" },
+{ "monospace",
+ "abqrInyhr" },
+{ "sans-serif",
+ "frggvat" },
+{ "sans-serif",
+ "rssrpg." },
+{ "sans-serif",
+ "ba" },
+{ "sans-serif",
+ "frggvat" },
+{ "sans-serif",
+ "AB_ZBQVSVPNGVBA_NYYBJRQ_REE:" },
+{ "sans-serif",
+ "ergevriny" },
+{ "sans-serif",
+ "QBZFGEVAT_FVMR_REE:" },
+{ "sans-serif",
+ "inevnoyr" },
+{ "sans-serif",
+ "cyngsbez." },
+{ "monospace",
+ "bjareQbphzrag" },
+{ "monospace",
+ "Qbphzrag" },
+{ "sans-serif",
+ "zbqvsvrq" },
+{ "sans-serif",
+ "lrg," },
+{ "monospace",
+ "cneragAbqr" },
+{ "sans-serif",
+ "cnerag" },
+{ "sans-serif",
+ "Nyy" },
+{ "sans-serif",
+ "rkprcg" },
+{ "sans-serif",
+ "cnerag." },
+{ "sans-serif",
+ "lrg" },
+{ "sans-serif",
+ "nqqrq" },
+{ "monospace",
+ "cersvk" },
+{ "sans-serif",
+ "crezvggrq," },
+{ "sans-serif",
+ "ubyqf" },
+{ "sans-serif",
+ "nccyvpnoyr." },
+{ "sans-serif",
+ "nccrne," },
+{ "sans-serif",
+ "punatr." },
+{ "monospace",
+ "cerivbhfFvoyvat" },
+{ "sans-serif",
+ "cerprqvat" },
+{ "monospace",
+ "nccraqPuvyq" },
+{ "sans-serif",
+ "Nqqf" },
+{ "monospace",
+ "arjPuvyq" },
+{ "sans-serif",
+ "raq" },
+{ "sans-serif",
+ "erzbirq." },
+{ "monospace",
+ "arjPuvyq" },
+{ "sans-serif",
+ "nqq." },
+{ "sans-serif",
+ "nqqrq." },
+{ "sans-serif",
+ "UVRENEPUL_ERDHRFG_REE:" },
+{ "sans-serif",
+ "nccraq" },
+{ "sans-serif",
+ "naprfgbef" },
+{ "monospace",
+ "pybarAbqr" },
+{ "sans-serif",
+ "qhcyvpngr" },
+{ "sans-serif",
+ "freirf" },
+{ "sans-serif",
+ "pbafgehpgbe" },
+{ "sans-serif",
+ ".)." },
+{ "sans-serif",
+ "Pybavat" },
+{ "sans-serif",
+ "cebprffbe" },
+{ "sans-serif",
+ "ercerfrag" },
+{ "sans-serif",
+ "qrsnhygrq" },
+{ "sans-serif",
+ "nggevohgrf," },
+{ "sans-serif",
+ "qrrc" },
+{ "sans-serif",
+ "pybar," },
+{ "sans-serif",
+ "pbagnvarq" },
+{ "monospace",
+ "Nggevohgr" },
+{ "sans-serif",
+ "qverpgyl," },
+{ "sans-serif",
+ "bccbfrq" },
+{ "sans-serif",
+ "pybarq" },
+{ "sans-serif",
+ "pybavat" },
+{ "sans-serif",
+ "vzzhgnoyr" },
+{ "sans-serif",
+ "zhgnoyr" },
+{ "sans-serif",
+ "pbcl," },
+{ "sans-serif",
+ "pybar" },
+{ "sans-serif",
+ "ernqbayl" },
+{ "sans-serif",
+ "pybarf" },
+{ "sans-serif",
+ "hafcrpvsvrq" },
+{ "sans-serif",
+ "Naq," },
+{ "sans-serif",
+ "qrcraqrag." },
+{ "monospace",
+ "unfNggevohgrf" },
+{ "sans-serif",
+ "ryrzrag)" },
+{ "monospace",
+ "unfPuvyqAbqrf" },
+{ "monospace",
+ "vafregOrsber" },
+{ "sans-serif",
+ "Vafregf" },
+{ "sans-serif",
+ "orsber" },
+{ "sans-serif",
+ "rkvfgvat" },
+{ "monospace",
+ "ersPuvyq" },
+{ "sans-serif",
+ "vafreg" },
+{ "sans-serif",
+ "vafregrq," },
+{ "sans-serif",
+ "beqre," },
+{ "sans-serif",
+ "vafreg." },
+{ "monospace",
+ "ersPuvyq" },
+{ "sans-serif",
+ "vafregrq." },
+{ "sans-serif",
+ "ernqbayl" },
+{ "sans-serif",
+ "ABG_SBHAQ_REE:" },
+{ "monospace",
+ "vfFhccbegrq" },
+{ "sans-serif",
+ "Grfgf" },
+{ "sans-serif",
+ "cnffrq" },
+{ "monospace",
+ "unfSrngher" },
+{ "sans-serif",
+ "1," },
+{ "sans-serif",
+ "\"2.0\"." },
+{ "sans-serif",
+ "pnhfr" },
+{ "monospace",
+ "abeznyvmr" },
+{ "sans-serif",
+ "Chgf" },
+{ "sans-serif",
+ "qrcgu" },
+{ "sans-serif",
+ "fho-gerr" },
+{ "sans-serif",
+ "haqrearngu" },
+{ "sans-serif",
+ "\"abezny\"" },
+{ "sans-serif",
+ "frpgvbaf," },
+{ "sans-serif",
+ "frcnengrf" },
+{ "sans-serif",
+ "nqwnprag" },
+{ "sans-serif",
+ "fnirq" },
+{ "sans-serif",
+ "er-ybnqrq," },
+{ "sans-serif",
+ "(fhpu" },
+{ "sans-serif",
+ "KCbvagre" },
+{ "sans-serif",
+ "ybbxhcf)" },
+{ "sans-serif",
+ "qrcraq" },
+{ "monospace",
+ "PQNGNFrpgvbaf" },
+{ "sans-serif",
+ "abeznyvmr" },
+{ "sans-serif",
+ "nybar" },
+{ "sans-serif",
+ "fhssvpvrag," },
+{ "sans-serif",
+ "KCbvagref" },
+{ "sans-serif",
+ "qvssreragvngr" },
+{ "monospace",
+ "erzbirPuvyq" },
+{ "sans-serif",
+ "Erzbirf" },
+{ "monospace",
+ "byqPuvyq" },
+{ "monospace",
+ "byqPuvyq" },
+{ "monospace",
+ "ercynprPuvyq" },
+{ "sans-serif",
+ "Ercynprf" },
+{ "sans-serif",
+ "ercynprq" },
+{ "sans-serif",
+ "beqre." },
+{ "sans-serif",
+ "chg" },
+{ "sans-serif",
+ "yvfg." },
+{ "sans-serif",
+ "ercynprq." },
+{ "sans-serif",
+ "AbqrYvfg" },
+{ "sans-serif",
+ "nofgenpgvba" },
+{ "sans-serif",
+ "pbafgenvavat" },
+{ "sans-serif",
+ "ubj" },
+{ "sans-serif",
+ "vzcyrzragrq." },
+{ "sans-serif",
+ "vgrzf" },
+{ "sans-serif",
+ "npprffvoyr" },
+{ "sans-serif",
+ "vagrteny" },
+{ "sans-serif",
+ "vaqrk," },
+{ "sans-serif",
+ "fgnegvat" },
+{ "sans-serif",
+ "0." },
+{ "monospace",
+ "vagresnpr AbqrYvfg {" },
+{ "monospace",
+ " Abqr vgrz(va hafvtarq ybat vaqrk);" },
+{ "monospace",
+ " ernqbayl nggevohgr hafvtarq ybat yratgu;" },
+{ "monospace",
+ "yratgu" },
+{ "monospace",
+ "ybat" },
+{ "sans-serif",
+ "vaqvprf" },
+{ "sans-serif",
+ "0" },
+{ "monospace",
+ "yratgu-1" },
+{ "sans-serif",
+ "vapyhfvir." },
+{ "monospace",
+ "vgrz" },
+{ "monospace",
+ "vaqrk" },
+{ "sans-serif",
+ "gu" },
+{ "sans-serif",
+ "pbyyrpgvba." },
+{ "sans-serif",
+ "rdhny" },
+{ "sans-serif",
+ "yvfg," },
+{ "monospace",
+ "vaqrk" },
+{ "sans-serif",
+ "Vaqrk" },
+{ "sans-serif",
+ "cbfvgvba" },
+{ "sans-serif",
+ "vaqrk." },
+{ "sans-serif",
+ "AnzrqAbqrZnc" },
+{ "sans-serif",
+ "pbyyrpgvbaf" },
+{ "sans-serif",
+ "npprffrq" },
+{ "monospace",
+ "AnzrqAbqrZncf" },
+{ "sans-serif",
+ "znvagnvarq" },
+{ "sans-serif",
+ "beqvany" },
+{ "sans-serif",
+ "rahzrengvba" },
+{ "sans-serif",
+ "Abqrf." },
+{ "monospace",
+ "vagresnpr AnzrqAbqrZnc {" },
+{ "monospace",
+ " Abqr trgAnzrqVgrz(va QBZFgevat anzr);" },
+{ "monospace",
+ " Abqr frgAnzrqVgrz(va Abqr net)" },
+{ "monospace",
+ " Abqr erzbirAnzrqVgrz(va QBZFgevat anzr)" },
+{ "monospace",
+ " Abqr trgAnzrqVgrzAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat ybpnyAnzr);" },
+{ "monospace",
+ " Abqr frgAnzrqVgrzAF(va Abqr net)" },
+{ "monospace",
+ " Abqr erzbirAnzrqVgrzAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat ybpnyAnzr)" },
+{ "sans-serif",
+ "znc." },
+{ "monospace",
+ "0" },
+{ "monospace",
+ "trgAnzrqVgrz" },
+{ "sans-serif",
+ "Ergevrirf" },
+{ "sans-serif",
+ "ergevrir." },
+{ "sans-serif",
+ "(bs" },
+{ "sans-serif",
+ "glcr)" },
+{ "monospace",
+ "trgAnzrqVgrzAF" },
+{ "sans-serif",
+ "znc," },
+{ "monospace",
+ "erzbirAnzrqVgrz" },
+{ "sans-serif",
+ "znc" },
+{ "sans-serif",
+ "ryrzrag," },
+{ "sans-serif",
+ "nccrnef" },
+{ "sans-serif",
+ "erzbir." },
+{ "sans-serif",
+ "rkvfgf." },
+{ "monospace",
+ "erzbirAnzrqVgrzAF" },
+{ "sans-serif",
+ "fb," },
+{ "monospace",
+ "frgAnzrqVgrz" },
+{ "sans-serif",
+ "bar." },
+{ "sans-serif",
+ "qrevir" },
+{ "sans-serif",
+ "fgberq" },
+{ "sans-serif",
+ "haqre," },
+{ "sans-serif",
+ "(gubfr" },
+{ "sans-serif",
+ "\"fcrpvny\"" },
+{ "sans-serif",
+ "inyhr)" },
+{ "sans-serif",
+ "pynfu." },
+{ "sans-serif",
+ "frra" },
+{ "sans-serif",
+ "cersrenoyr" },
+{ "sans-serif",
+ "nyybjvat" },
+{ "sans-serif",
+ "nyvnfrq." },
+{ "monospace",
+ "net" },
+{ "sans-serif",
+ "fgber" },
+{ "sans-serif",
+ "yngre" },
+{ "sans-serif",
+ "ercynprf" },
+{ "monospace",
+ "net" },
+{ "sans-serif",
+ "VAHFR_NGGEVOHGR_REE:" },
+{ "sans-serif",
+ "er-hfr" },
+{ "sans-serif",
+ "ryrzragf." },
+{ "monospace",
+ "frgAnzrqVgrzAF" },
+{ "sans-serif",
+ "PunenpgreQngn" },
+{ "sans-serif",
+ "rkgraqf" },
+{ "sans-serif",
+ "pynevgl" },
+{ "sans-serif",
+ "urer" },
+{ "sans-serif",
+ "Ab" },
+{ "monospace",
+ "bssfrgf" },
+{ "sans-serif",
+ "fgneg" },
+{ "sans-serif",
+ "ercerfragrq" },
+{ "sans-serif",
+ "HGS-16," },
+{ "sans-serif",
+ "v.r." },
+{ "sans-serif",
+ "havgf." },
+{ "sans-serif",
+ "sbyybjvat," },
+{ "sans-serif",
+ "grez" },
+{ "sans-serif",
+ "jurarire" },
+{ "sans-serif",
+ "arprffnel" },
+{ "sans-serif",
+ "vaqvpngr" },
+{ "sans-serif",
+ "vaqrkvat" },
+{ "sans-serif",
+ "PunenpgreQngn" },
+{ "monospace",
+ "vagresnpr PunenpgreQngn : Abqr {" },
+{ "monospace",
+ " nggevohgr QBZFgevat qngn;" },
+{ "monospace",
+ " QBZFgevat fhofgevatQngn(va hafvtarq ybat bssfrg, " },
+{ "monospace",
+ " va hafvtarq ybat pbhag)" },
+{ "monospace",
+ " ibvq nccraqQngn(va QBZFgevat net)" },
+{ "monospace",
+ " ibvq vafregQngn(va hafvtarq ybat bssfrg, " },
+{ "monospace",
+ " va QBZFgevat net)" },
+{ "monospace",
+ " ibvq qryrgrQngn(va hafvtarq ybat bssfrg, " },
+{ "monospace",
+ " va hafvtarq ybat pbhag)" },
+{ "monospace",
+ " ibvq ercynprQngn(va hafvtarq ybat bssfrg, " },
+{ "monospace",
+ " va hafvtarq ybat pbhag, " },
+{ "monospace",
+ " va QBZFgevat net)" },
+{ "sans-serif",
+ "neovgenel" },
+{ "sans-serif",
+ "yvzvgf" },
+{ "sans-serif",
+ "zrna" },
+{ "sans-serif",
+ "ragvergl" },
+{ "sans-serif",
+ "pnfrf," },
+{ "sans-serif",
+ "pnyy" },
+{ "monospace",
+ "fhofgevatQngn" },
+{ "sans-serif",
+ "ergevrir" },
+{ "sans-serif",
+ "nccebcevngryl" },
+{ "sans-serif",
+ "fvmrq" },
+{ "sans-serif",
+ "cvrprf." },
+{ "sans-serif",
+ "ninvynoyr" },
+{ "sans-serif",
+ "orybj." },
+{ "sans-serif",
+ "mreb," },
+{ "sans-serif",
+ "rzcgl." },
+{ "monospace",
+ "nccraqQngn" },
+{ "sans-serif",
+ "Nccraq" },
+{ "sans-serif",
+ "Hcba" },
+{ "sans-serif",
+ "fhpprff," },
+{ "sans-serif",
+ "pbapngrangvba" },
+{ "sans-serif",
+ "nccraq." },
+{ "monospace",
+ "qryrgrQngn" },
+{ "sans-serif",
+ "Erzbir" },
+{ "sans-serif",
+ "ersyrpg" },
+{ "monospace",
+ "bssfrg" },
+{ "sans-serif",
+ "bssfrg" },
+{ "sans-serif",
+ "erzbivat." },
+{ "monospace",
+ "pbhag" },
+{ "sans-serif",
+ "qryrgr." },
+{ "sans-serif",
+ "fhz" },
+{ "monospace",
+ "bssfrg" },
+{ "monospace",
+ "pbhag" },
+{ "sans-serif",
+ "rkprrqf" },
+{ "sans-serif",
+ "qryrgrq." },
+{ "sans-serif",
+ "VAQRK_FVMR_REE:" },
+{ "sans-serif",
+ "artngvir" },
+{ "sans-serif",
+ "artngvir." },
+{ "monospace",
+ "vafregQngn" },
+{ "sans-serif",
+ "Vafreg" },
+{ "sans-serif",
+ "havg" },
+{ "sans-serif",
+ "bssfrg." },
+{ "monospace",
+ "ercynprQngn" },
+{ "sans-serif",
+ "Ercynpr" },
+{ "sans-serif",
+ "ercynpvat." },
+{ "sans-serif",
+ "ercynpr." },
+{ "sans-serif",
+ "ercynprq;" },
+{ "monospace",
+ "erzbir" },
+{ "sans-serif",
+ "enatr," },
+{ "sans-serif",
+ "sbyybjrq" },
+{ "monospace",
+ "nccraq" },
+{ "sans-serif",
+ "vaibpngvba)." },
+{ "monospace",
+ "fhofgevatQngn" },
+{ "sans-serif",
+ "Rkgenpgf" },
+{ "sans-serif",
+ "Fgneg" },
+{ "sans-serif",
+ "fhofgevat" },
+{ "sans-serif",
+ "rkgenpg." },
+{ "sans-serif",
+ "fhofgevat." },
+{ "sans-serif",
+ "Ngge" },
+{ "sans-serif",
+ "Glcvpnyyl" },
+{ "sans-serif",
+ "nyybjnoyr" },
+{ "sans-serif",
+ "qrsvavgvba." },
+{ "sans-serif",
+ "npghnyyl" },
+{ "sans-serif",
+ "qrfpevor," },
+{ "sans-serif",
+ "pbafvqre" },
+{ "monospace",
+ "cerivbhfFvoyvat" },
+{ "monospace",
+ "arkgFvoyvat" },
+{ "sans-serif",
+ "gnxrf" },
+{ "sans-serif",
+ "cebcregvrf" },
+{ "sans-serif",
+ "univat" },
+{ "sans-serif",
+ "frcnengr" },
+{ "sans-serif",
+ "vqragvgl" },
+{ "sans-serif",
+ "jvgu;" },
+{ "sans-serif",
+ "rssvpvrag" },
+{ "sans-serif",
+ "vzzrqvngr" },
+{ "sans-serif",
+ "vzcyrzragbef" },
+{ "sans-serif",
+ "qvfgvapg." },
+{ "sans-serif",
+ "nggevohgr'f" },
+{ "sans-serif",
+ "rssrpgvir" },
+{ "sans-serif",
+ "qrgrezvarq" },
+{ "sans-serif",
+ "nffvtarq" },
+{ "sans-serif",
+ "inyhr;" },
+{ "sans-serif",
+ "bgurejvfr," },
+{ "sans-serif",
+ "vapyhqrf" },
+{ "sans-serif",
+ "inyhr(f)." },
+{ "sans-serif",
+ "ersreraprf," },
+{ "sans-serif",
+ "ercerfragngvba" },
+{ "sans-serif",
+ "rkcnaqrq." },
+{ "sans-serif",
+ "gerngf" },
+{ "sans-serif",
+ "fvzcyr" },
+{ "sans-serif",
+ "fgevatf," },
+{ "sans-serif",
+ "QGQ" },
+{ "sans-serif",
+ "fpurzn" },
+{ "sans-serif",
+ "qrpynerf" },
+{ "sans-serif",
+ "gbxravmrq" },
+{ "monospace",
+ "vagresnpr Ngge : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat anzr;" },
+{ "monospace",
+ " ernqbayl nggevohgr obbyrna fcrpvsvrq;" },
+{ "monospace",
+ " nggevohgr QBZFgevat inyhr;" },
+{ "monospace",
+ " ernqbayl nggevohgr Ryrzrag bjareRyrzrag;" },
+{ "monospace",
+ "bjareRyrzrag" },
+{ "monospace",
+ "fcrpvsvrq" },
+{ "sans-serif",
+ "punetr" },
+{ "sans-serif",
+ "hfre." },
+{ "sans-serif",
+ "(rira" },
+{ "sans-serif",
+ "raqf" },
+{ "sans-serif",
+ "syvccrq" },
+{ "sans-serif",
+ "er-fcrpvsl" },
+{ "sans-serif",
+ "QGQ," },
+{ "sans-serif",
+ "qryrgr" },
+{ "sans-serif",
+ "rkvfgf)." },
+{ "sans-serif",
+ "fhzznel:" },
+{ "sans-serif",
+ "inyhr." },
+{ "sans-serif",
+ "QGQ." },
+{ "sans-serif",
+ "#VZCYVRQ" },
+{ "sans-serif",
+ "nccrne" },
+{ "sans-serif",
+ "(v.r." },
+{ "sans-serif",
+ "erzbiny" },
+{ "sans-serif",
+ "bcrengvbaf)" },
+{ "monospace",
+ "inyhr" },
+{ "sans-serif",
+ "ergevriny," },
+{ "sans-serif",
+ "Punenpgre" },
+{ "sans-serif",
+ "trareny" },
+{ "sans-serif",
+ "inyhrf." },
+{ "sans-serif",
+ "frggvat," },
+{ "sans-serif",
+ "hacnefrq" },
+{ "sans-serif",
+ "V.r." },
+{ "sans-serif",
+ "erpbtavmr" },
+{ "sans-serif",
+ "znexhc" },
+{ "sans-serif",
+ "yvgreny" },
+{ "sans-serif",
+ "Ryrzrag" },
+{ "sans-serif",
+ "gurz;" },
+{ "sans-serif",
+ "vaurevgf" },
+{ "sans-serif",
+ "Gurer" },
+{ "sans-serif",
+ "ergevrirq" },
+{ "sans-serif",
+ "rknzvar" },
+{ "sans-serif",
+ "cbffvoyl" },
+{ "sans-serif",
+ "pbzcyrk" },
+{ "sans-serif",
+ "unaq," },
+{ "sans-serif",
+ "fnsryl" },
+{ "monospace",
+ "abeznyvmr" },
+{ "sans-serif",
+ "zbirq." },
+{ "monospace",
+ "vagresnpr Ryrzrag : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat gntAnzr;" },
+{ "monospace",
+ " QBZFgevat trgNggevohgr(va QBZFgevat anzr);" },
+{ "monospace",
+ " ibvq frgNggevohgr(va QBZFgevat anzr, " },
+{ "monospace",
+ " va QBZFgevat inyhr)" },
+{ "monospace",
+ " ibvq erzbirNggevohgr(va QBZFgevat anzr)" },
+{ "monospace",
+ " Ngge trgNggevohgrAbqr(va QBZFgevat anzr);" },
+{ "monospace",
+ " Ngge frgNggevohgrAbqr(va Ngge arjNgge)" },
+{ "monospace",
+ " Ngge erzbirNggevohgrAbqr(va Ngge byqNgge)" },
+{ "monospace",
+ " AbqrYvfg trgRyrzragfOlGntAnzr(va QBZFgevat anzr);" },
+{ "monospace",
+ " QBZFgevat trgNggevohgrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " ibvq frgNggevohgrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat inyhr)" },
+{ "monospace",
+ " ibvq erzbirNggevohgrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " Ngge trgNggevohgrAbqrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "monospace",
+ " va QBZFgevat ybpnyAnzr);" },
+{ "monospace",
+ " Ngge frgNggevohgrAbqrAF(va Ngge arjNgge)" },
+{ "monospace",
+ " obbyrna unfNggevohgr(va QBZFgevat anzr);" },
+{ "monospace",
+ " obbyrna unfNggevohgrAF(va QBZFgevat anzrfcnprHEV, " },
+{ "sans-serif",
+ "va:" },
+{ "monospace",
+ "<ryrzragRknzcyr vq=\"qrzb\"> " },
+{ "monospace",
+ " ... " },
+{ "monospace",
+ "</ryrzragRknzcyr> ," },
+{ "monospace",
+ "\"ryrzragRknzcyr\"" },
+{ "sans-serif",
+ "pnfr-cerfreivat" },
+{ "sans-serif",
+ "sbez," },
+{ "sans-serif",
+ "ertneqyrff" },
+{ "monospace",
+ "trgNggevohgr" },
+{ "sans-serif",
+ "fgevat," },
+{ "monospace",
+ "trgNggevohgrAF" },
+{ "monospace",
+ "trgNggevohgrAbqr" },
+{ "monospace",
+ "t" },
+{ "serif",
+ "36," },
+{ "serif",
+ "jjj.j3.bet_QBZY2Pber," },
+{ "serif",
+ "474" },
+{ "monospace",
+ "trgNggevohgrAbqrAF" },
+{ "sans-serif",
+ "qrfpraqnag" },
+{ "monospace",
+ "unfNggevohgr" },
+{ "sans-serif",
+ "ybbx" },
+{ "sans-serif",
+ "sbe." },
+{ "monospace",
+ "unfNggevohgrAF" },
+{ "monospace",
+ "erzbirNggevohgr" },
+{ "sans-serif",
+ "erzbir" },
+{ "monospace",
+ "erzbirNggevohgrAF" },
+{ "monospace",
+ "erzbirNggevohgrAF" },
+{ "sans-serif",
+ "ercynpvat" },
+{ "monospace",
+ "erzbirNggevohgrAbqr" },
+{ "monospace",
+ "byqNgge" },
+{ "monospace",
+ "Ngge" },
+{ "monospace",
+ "byqNgge" },
+{ "monospace",
+ "frgNggevohgr" },
+{ "sans-serif",
+ "punatrq" },
+{ "sans-serif",
+ "cnenzrgre." },
+{ "sans-serif",
+ "fgevat;" },
+{ "sans-serif",
+ "frg." },
+{ "sans-serif",
+ "Fb" },
+{ "sans-serif",
+ "flagnk" },
+{ "sans-serif",
+ "erpbtavmrq" },
+{ "sans-serif",
+ "ersrerapr)" },
+{ "sans-serif",
+ "grkg," },
+{ "sans-serif",
+ "arrqf" },
+{ "sans-serif",
+ "rfpncrq" },
+{ "sans-serif",
+ "jevggra" },
+{ "sans-serif",
+ "bhg." },
+{ "sans-serif",
+ "nffvta" },
+{ "sans-serif",
+ "ohvyq" },
+{ "sans-serif",
+ "fhogerr," },
+{ "sans-serif",
+ "nygre." },
+{ "sans-serif",
+ "Inyhr" },
+{ "sans-serif",
+ "sbez." },
+{ "monospace",
+ "frgNggevohgrAF" },
+{ "monospace",
+ "inyhr" },
+{ "monospace",
+ "frgNggevohgrAbqrAF" },
+{ "monospace",
+ "frgNggevohgrAbqr" },
+{ "monospace",
+ "arjNgge" },
+{ "monospace",
+ "arjNgge" },
+{ "monospace",
+ "frgNggevohgrAbqrAF" },
+{ "sans-serif",
+ "Grkg" },
+{ "sans-serif",
+ "grkghny" },
+{ "sans-serif",
+ "(grezrq" },
+{ "sans-serif",
+ "punenpgre" },
+{ "sans-serif",
+ "qngn" },
+{ "sans-serif",
+ "KZY)" },
+{ "sans-serif",
+ "ryrzrag'f" },
+{ "sans-serif",
+ "pbagrag," },
+{ "sans-serif",
+ "znexhc," },
+{ "sans-serif",
+ "vasbezngvba" },
+{ "sans-serif",
+ "vgrzf" },
+{ "sans-serif",
+ "(ryrzragf," },
+{ "sans-serif",
+ "rgp.)" },
+{ "sans-serif",
+ "oybpx" },
+{ "sans-serif",
+ "Hfref" },
+{ "sans-serif",
+ "vagreiravat" },
+{ "sans-serif",
+ "frcnengvbaf" },
+{ "sans-serif",
+ "trareny)" },
+{ "sans-serif",
+ "crefvfg" },
+{ "sans-serif",
+ "frffvbaf." },
+{ "monospace",
+ "abeznyvmr()" },
+{ "sans-serif",
+ "zretrf" },
+{ "monospace",
+ "vagresnpr Grkg : PunenpgreQngn {" },
+{ "monospace",
+ " Grkg fcyvgGrkg(va hafvtarq ybat bssfrg)" },
+{ "monospace",
+ "fcyvgGrkg" },
+{ "sans-serif",
+ "Oernxf" },
+{ "sans-serif",
+ "xrrcvat" },
+{ "sans-serif",
+ "Nsgre" },
+{ "sans-serif",
+ "fcyvg," },
+{ "sans-serif",
+ "cbvag." },
+{ "sans-serif",
+ "glcr," },
+{ "sans-serif",
+ "nsgre" },
+{ "sans-serif",
+ "cbvag," },
+{ "sans-serif",
+ "unq" },
+{ "sans-serif",
+ "fvoyvat" },
+{ "sans-serif",
+ "yratgu" },
+{ "sans-serif",
+ "Pbzzrag" },
+{ "sans-serif",
+ "pbzzrag," },
+{ "sans-serif",
+ "'" },
+{ "monospace",
+ "<!--" },
+{ "sans-serif",
+ "raqvat" },
+{ "monospace",
+ "-->" },
+{ "sans-serif",
+ "'." },
+{ "sans-serif",
+ "naq," },
+{ "sans-serif",
+ "nygubhtu" },
+{ "sans-serif",
+ "gbbyf" },
+{ "sans-serif",
+ "FTZY" },
+{ "monospace",
+ "vagresnpr Pbzzrag : PunenpgreQngn {" },
+{ "sans-serif",
+ "1.3." },
+{ "sans-serif",
+ "Rkgraqrq" },
+{ "sans-serif",
+ "fcrpvsvpngvba," },
+{ "sans-serif",
+ "qrnyf" },
+{ "sans-serif",
+ "UGZY." },
+{ "sans-serif",
+ "fhpu," },
+{ "sans-serif",
+ "qrgrezvar" },
+{ "sans-serif",
+ "abg." },
+{ "sans-serif",
+ "\"KZY\"" },
+{ "sans-serif",
+ "PQNGNFrpgvba" },
+{ "sans-serif",
+ "frpgvbaf" },
+{ "sans-serif",
+ "rfpncr" },
+{ "sans-serif",
+ "oybpxf" },
+{ "sans-serif",
+ "ertneqrq" },
+{ "sans-serif",
+ "znexhc." },
+{ "sans-serif",
+ "qryvzvgre" },
+{ "sans-serif",
+ "\"]]>\"" },
+{ "sans-serif",
+ "arfgrq." },
+{ "sans-serif",
+ "Gurve" },
+{ "sans-serif",
+ "checbfr" },
+{ "sans-serif",
+ "zngrevny" },
+{ "sans-serif",
+ "sentzragf," },
+{ "sans-serif",
+ "arrqvat" },
+{ "sans-serif",
+ "qryvzvgref." },
+{ "sans-serif",
+ "znl" },
+{ "sans-serif",
+ "gung," },
+{ "sans-serif",
+ "(\"punefrg\")" },
+{ "sans-serif",
+ "frevnyvmngvba," },
+{ "sans-serif",
+ "jevgr" },
+{ "sans-serif",
+ "bhg" },
+{ "sans-serif",
+ "Nqwnprag" },
+{ "sans-serif",
+ "zretrq" },
+{ "sans-serif",
+ "frevnyvmvat." },
+{ "sans-serif",
+ "gnxra" },
+{ "sans-serif",
+ "ercerfragrq." },
+{ "sans-serif",
+ "Snvyher" },
+{ "sans-serif",
+ "cebqhpr" },
+{ "sans-serif",
+ "KZY." },
+{ "sans-serif",
+ "Bar" },
+{ "sans-serif",
+ "cbgragvny" },
+{ "sans-serif",
+ "frevnyvmngvba" },
+{ "sans-serif",
+ "bhgchg" },
+{ "sans-serif",
+ "ersrerapr," },
+{ "sans-serif",
+ "bcra" },
+{ "sans-serif",
+ "Abgr," },
+{ "sans-serif",
+ "ubjrire," },
+{ "sans-serif",
+ "pbairefvba" },
+{ "sans-serif",
+ "yvoenevrf" },
+{ "sans-serif",
+ "jevgvat" },
+{ "sans-serif",
+ "zvffvat" },
+{ "sans-serif",
+ "rapbqvat," },
+{ "sans-serif",
+ "znxvat" },
+{ "sans-serif",
+ "gnfx" },
+{ "sans-serif",
+ "rafhevat" },
+{ "sans-serif",
+ "pbeehcgrq" },
+{ "sans-serif",
+ "qvssvphyg." },
+{ "monospace",
+ "vagresnpr PQNGNFrpgvba : Grkg {" },
+{ "sans-serif",
+ "QbphzragGlcr" },
+{ "sans-serif",
+ "Rnpu" },
+{ "sans-serif",
+ "ryfr" },
+{ "sans-serif",
+ "rssbegf" },
+{ "sans-serif",
+ "pyrneyl" },
+{ "sans-serif",
+ "haqrefgbbq" },
+{ "sans-serif",
+ "jevgvat." },
+{ "monospace",
+ "vagresnpr QbphzragGlcr : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr AnzrqAbqrZnc ragvgvrf;" },
+{ "monospace",
+ " ernqbayl nggevohgr AnzrqAbqrZnc abgngvbaf;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat choyvpVq;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat flfgrzVq;" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat vagreanyFhofrg;" },
+{ "monospace",
+ "ragvgvrf" },
+{ "sans-serif",
+ "vagreany," },
+{ "sans-serif",
+ "qrpynerq" },
+{ "sans-serif",
+ "Cnenzrgre" },
+{ "sans-serif",
+ "pbagnvarq." },
+{ "sans-serif",
+ "Qhcyvpngrf" },
+{ "sans-serif",
+ "qvfpneqrq." },
+{ "monospace",
+ "<!QBPGLCR rk FLFGRZ \"rk.qgq\" [" },
+{ "monospace",
+ " <!RAGVGL sbb \"sbb\">" },
+{ "monospace",
+ " <!RAGVGL one \"one\">" },
+{ "monospace",
+ " <!RAGVGL one \"one2\">" },
+{ "monospace",
+ " <!RAGVGL % onm \"onm\">" },
+{ "monospace",
+ "]>" },
+{ "monospace",
+ "<rk/>" },
+{ "monospace",
+ "sbb" },
+{ "monospace",
+ "one" },
+{ "sans-serif",
+ "frpbaq" },
+{ "monospace",
+ "onm" },
+{ "sans-serif",
+ "Rirel" },
+{ "monospace",
+ "ragvgvrf" },
+{ "sans-serif",
+ "jnl." },
+{ "monospace",
+ "vagreanyFhofrg" },
+{ "sans-serif",
+ "vagreany" },
+{ "sans-serif",
+ "npghny" },
+{ "sans-serif",
+ "zhpu" },
+{ "sans-serif",
+ "cnenzrgref," },
+{ "sans-serif",
+ "QGQ;" },
+{ "monospace",
+ "QBPGLCR" },
+{ "sans-serif",
+ "xrljbeq." },
+{ "monospace",
+ "abgngvbaf" },
+{ "sans-serif",
+ "abgngvbaf," },
+{ "monospace",
+ "abgngvbaf" },
+{ "sans-serif",
+ "vqragvsvre" },
+{ "sans-serif",
+ "fhofrg." },
+{ "sans-serif",
+ "Abgngvba" },
+{ "sans-serif",
+ "qrpynerf," },
+{ "sans-serif",
+ "sbezng" },
+{ "sans-serif",
+ "frpgvba" },
+{ "sans-serif",
+ "4.7" },
+{ "sans-serif",
+ "1.0" },
+{ "sans-serif",
+ "])," },
+{ "sans-serif",
+ "sbezny" },
+{ "sans-serif",
+ "vafgehpgvba" },
+{ "sans-serif",
+ "gnetrgf" },
+{ "sans-serif",
+ "2.6" },
+{ "sans-serif",
+ "abgngvba." },
+{ "monospace",
+ "vagresnpr Abgngvba : Abqr {" },
+{ "sans-serif",
+ "Ragvgl" },
+{ "sans-serif",
+ "ragvgl," },
+{ "sans-serif",
+ "hacnefrq," },
+{ "sans-serif",
+ "zbqryf" },
+{ "sans-serif",
+ "qrpynengvba." },
+{ "sans-serif",
+ "zbqryvat" },
+{ "sans-serif",
+ "fcrpvsvpngvba." },
+{ "sans-serif",
+ "ragvgl." },
+{ "sans-serif",
+ "pubbfr" },
+{ "sans-serif",
+ "rkcnaq" },
+{ "sans-serif",
+ "QBZ;" },
+{ "sans-serif",
+ "znaqngr" },
+{ "sans-serif",
+ "aba-inyvqngvat" },
+{ "sans-serif",
+ "ragvgvrf." },
+{ "sans-serif",
+ "rkcnaqrq" },
+{ "sans-serif",
+ "nccyvpngvbaf," },
+{ "sans-serif",
+ "ercynprzrag" },
+{ "sans-serif",
+ "ninvynoyr," },
+{ "sans-serif",
+ "jnagf" },
+{ "sans-serif",
+ "'f" },
+{ "sans-serif",
+ "pbagragf," },
+{ "sans-serif",
+ "qrfverq" },
+{ "sans-serif",
+ "vafgrnq." },
+{ "sans-serif",
+ "ersre" },
+{ "monospace",
+ "vagresnpr Ragvgl : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat abgngvbaAnzr;" },
+{ "monospace",
+ "abgngvbaAnzr" },
+{ "sans-serif",
+ "RagvglErsrerapr" },
+{ "sans-serif",
+ "cerqrsvarq" },
+{ "sans-serif",
+ "rdhvinyrag" },
+{ "sans-serif",
+ "Zberbire," },
+{ "sans-serif",
+ "ohvyqvat" },
+{ "sans-serif",
+ "cebivqvat" },
+{ "sans-serif",
+ "erfbyhgvba" },
+{ "monospace",
+ "vagresnpr RagvglErsrerapr : Abqr {" },
+{ "sans-serif",
+ "CebprffvatVafgehpgvba" },
+{ "sans-serif",
+ "\"cebprffvat" },
+{ "sans-serif",
+ "vafgehpgvba\"," },
+{ "sans-serif",
+ "xrrc" },
+{ "sans-serif",
+ "cebprffbe-fcrpvsvp" },
+{ "monospace",
+ "vagresnpr CebprffvatVafgehpgvba : Abqr {" },
+{ "monospace",
+ " ernqbayl nggevohgr QBZFgevat gnetrg;" },
+{ "sans-serif",
+ "aba" },
+{ "sans-serif",
+ "juvgr" },
+{ "sans-serif",
+ "fcnpr" },
+{ "monospace",
+ "?>" },
+{ "sans-serif",
+ "gbxra" },
+{ "sans-serif",
+ "ortvaf" },
+{ "serif",
+ "1, 36, jjj.j3.bet_QBZY2Pber, 474" },
+{ "sans-serif",
+ " " },
+{ "monospace",
+ "27 Frcgrzore, 2000" },
+{ "sans-serif",
+ "1. Qbphzrag Bowrpg Zbqry Pber" },
+{ "sans-serif",
+ "Neanhq Yr Ubef, VOZ" },
+{ "sans-serif",
+ "Zvxr Punzcvba, NeobeGrkg (sbe QBZ Yriry 1 sebz Abirzore 20, 1997)" },
+{ "sans-serif",
+ "Fgrir Olear, WninFbsg (sbe QBZ Yriry 1 hagvy Abirzore 19, 1997)" },
+{ "sans-serif",
+ "Tniva Avpby, Vafb RCF (sbe QBZ Yriry 1)" },
+{ "sans-serif",
+ "Ynhera Jbbq, FbsgDhnq, Vap. (sbe QBZ Yriry 1)" },
+{ "sans-serif",
+ "Gnoyr bs pbagragf" },
+{ "sans-serif",
+ "1.1. Bireivrj bs gur QBZ Pber Vagresnprf" },
+{ "sans-serif",
+ "1.1.1. Gur QBZ Fgehpgher Zbqry" },
+{ "sans-serif",
+ "1.1.2. Zrzbel Znantrzrag" },
+{ "sans-serif",
+ "1.1.3. Anzvat Pbairagvbaf" },
+{ "sans-serif",
+ "1.1.4. Vaurevgnapr if. Synggrarq Ivrjf bs gur NCV" },
+{ "sans-serif",
+ "1.1.5. Gur QBZFgevat glcr" },
+{ "sans-serif",
+ "1.1.6. Gur QBZGvzrFgnzc glcr" },
+{ "sans-serif",
+ "1.1.7. Fgevat pbzcnevfbaf va gur QBZ" },
+{ "sans-serif",
+ "1.1.8. KZY Anzrfcnprf" },
+{ "sans-serif",
+ "1.2. Shaqnzragny Vagresnprf" },
+{ "sans-serif",
+ ", " },
+{ "sans-serif",
+ "," },
+{ "sans-serif",
+ "1.3. Rkgraqrq Vagresnprf" },
+{ "sans-serif",
+ "1.1. Bireivrj bs gur QBZ Pber Vagresnprf" },
+{ "Verdana,Sans-serif,serif",
+ "Ylpbf" },
+{ "Verdana,Sans-serif,serif",
+ "Ubzr" },
+{ "Verdana,Sans-serif,serif",
+ "|" },
+{ "Verdana,Sans-serif,serif",
+ "Serr" },
+{ "Verdana,Sans-serif,serif",
+ "Jro" },
+{ "Verdana,Sans-serif,serif",
+ "Npprff" },
+{ "Verdana,Sans-serif,serif",
+ "Fvgr" },
+{ "Verdana,Sans-serif,serif",
+ "Znc" },
+{ "Verdana,Sans-serif,serif",
+ "Zl" },
+{ "Times, Times New Roman, serif,serif",
+ "G B C " },
+{ "Times, Times New Roman, serif,serif",
+ " F G B E V R F" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "hcqngrq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "4:20" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "c.z." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Qrp." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "14," },
+{ "Verdana,Geneva,sans-serif,serif",
+ "2000" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "CFG" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qryvirerq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "lbhe" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "vaobk" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "be" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "unaquryq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qrivpr." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "FRNEPU URNQYVARF" },
+{ "Lucida Grande",
+ "Jverq" },
+{ "Lucida Grande",
+ "Arjf" },
+{ "Lucida Grande",
+ "Zntnmvar" },
+{ "Lucida Grande",
+ "UbgObg" },
+{ "Lucida Grande",
+ "FRNEPU" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "PHEERAG" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "UBB-UN" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Unc" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "R-Ubyvqnlf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fcbafberq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "ol" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qryy" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pbzqrk" },
+{ "Arial,Geneva,sans-serif,serif",
+ "RQF" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tnqtrgf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "naq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tvmzbf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "ARP" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jveryrff" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jbeyq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "NG&G" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jveryrff" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Freivprf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ZC3" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ebpxf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jro" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Xrrc" },
+{ "Arial,Geneva,sans-serif,serif",
+ "hc" },
+{ "Arial,Geneva,sans-serif,serif",
+ "jvgu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "gur" },
+{ "Arial,Geneva,sans-serif,serif",
+ "ohmm" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "VF/VG" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Vasbfgehpgher" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Nypngry" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "VCB" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Bhgybbx" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Urjyrgg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cnpxneq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Znxvat" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tenqr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "\"Byq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fpubby\"" },
+{ "Arial,Geneva,sans-serif,serif",
+ "trgf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ubyyljbbq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Grpu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gur" },
+{ "Arial,Geneva,sans-serif,serif",
+ "fgnef" },
+{ "Arial,Geneva,sans-serif,serif",
+ "bs" },
+{ "Arial,Geneva,sans-serif,serif",
+ "zhygvzrqvn" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yvahk" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Rssrpg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Arjf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "sebz" },
+{ "Arial,Geneva,sans-serif,serif",
+ "sebag" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Rkrphgvir" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhzznel" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Urnq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "ubapubf," },
+{ "Arial,Geneva,sans-serif,serif",
+ "ovt" },
+{ "Arial,Geneva,sans-serif,serif",
+ "purrfrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cevinpl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Znggref" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gnxr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "vg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "crefbanyyl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jbzra" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "va" },
+{ "Arial,Geneva,sans-serif,serif",
+ "JvatfcnaOnax.pbz" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "H.F." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "i." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Zvpebfbsg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "gevny" },
+{ "Arial,Geneva,sans-serif,serif",
+ "00f" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Erny" },
+{ "Arial,Geneva,sans-serif,serif",
+ "ArgFynirf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "jnpxvarff" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Zrq-Grpu" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pragre" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Trargvpnyyl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "nygrerq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "R-Ovm" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ivtarggr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ylpbf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "50" },
+{ "Arial,Geneva,sans-serif,serif",
+ "zbfg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "cbchyne" },
+{ "Arial,Geneva,sans-serif,serif",
+ "frnepu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "grezf," },
+{ "Arial,Geneva,sans-serif,serif",
+ "nppbeqvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "gb" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ylpbf." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gur" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gbbyone." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Vg'f" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "serr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "jr'er" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "tvivat" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "vg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "njnl." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "CYNA N GEVC" },
+{ "Lucida Grande",
+ "Obbx" },
+{ "Lucida Grande",
+ "n" },
+{ "Lucida Grande",
+ "Syvtug" },
+{ "Lucida Grande",
+ "Qvfpbhag" },
+{ "Lucida Grande",
+ "Ubgry" },
+{ "Lucida Grande",
+ "Erag" },
+{ "Lucida Grande",
+ "Pne" },
+{ "Lucida Grande",
+ "Nve" },
+{ "Lucida Grande",
+ "Qrnyf" },
+{ "Lucida Grande",
+ "Genpx" },
+{ "Lucida Grande",
+ "Syvtugf" },
+{ "Lucida Grande",
+ "Fpurqhyrf" },
+{ "Lucida Grande",
+ "vagryyvGEVC" },
+{ "Lucida Grande",
+ "Nvecbeg" },
+{ "Lucida Grande",
+ "Zncf" },
+{ "Lucida Grande",
+ "Geniry" },
+{ "Lucida Grande",
+ "Pvgl" },
+{ "Lucida Grande",
+ "Thvqrf" },
+{ "Lucida Grande",
+ "GEVC" },
+{ "Lucida Grande",
+ "Ubzr" },
+{ "Lucida Grande",
+ "Tb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cbjrerq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ol" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "GEVC.pbz" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "JVERQ" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ZNTNMVAR" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Vffhr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "8.12" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhofpevor" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "gb" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fcrpvny" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "bssre!" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "UBGJVERQ" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Sebag" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qbbe" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jrozbaxrl" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Thvqrf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ETO" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tnyyrel" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Navzngvba" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Rkcerff" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Fhpx.pbz" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "UbgObg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qryvirerq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "unaquryq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "qrivprf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "NinagTb," },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "be" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "lbhe" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Va-Obk" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Arjf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "fgnss" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Pbagnpg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "hf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pryy" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Cubarf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Puvc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Va" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fnsre" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pnyyf," },
+{ "Arial,Geneva,sans-serif,serif",
+ "be" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fryyvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Srne?" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Va" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jung" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "or" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nabgure" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "choyvp" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "eryngvbaf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "punyyratr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gur" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pryyhyne" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cubar" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vaqhfgel," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "n" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbzcnal" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znexrgvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrivpr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fnlf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jvyy" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cerirag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pryy" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cubarf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "sebz" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unezvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "obql." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ol" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ryvfn" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ongvfgn." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "va" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Grpuabybtl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "-" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ohvyqvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "n" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Orggre" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Onyybg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Obk" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ZVG" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pnygrpu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "wbva" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "sbeprf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gb" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrirybc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ibgvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znpuvar" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gung" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vzcebir" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "hcba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pheerag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ceboyrzngvp" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "flfgrz" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bs" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "chapu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pneqf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bcgvpny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fpnaaref." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "havirefvgvrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jnag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "oevat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ibgr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zvfpbhagf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "\"arne" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "mreb.\"" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Cbyvgvpf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ubj" },
+{ "Arial,Geneva,sans-serif,serif",
+ "K" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Engrf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jvgu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "'J'" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ubj" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "arkg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nqzvavfgengvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jvgu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cbeabtencul" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Vagrearg?" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Erchoyvpnaf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unir" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cyrqtrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Whfgvpr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qrcnegzrag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cbhapr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Arg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cbeabtencuref," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Trbetr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "J." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ohfu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "envyrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ntnvafg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bssrafvir" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jrofvgrf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ur'f" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nyfb" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "raqbefrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "yvoenel" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fpubby" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "svygrevat." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qrpyna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ZpPhyyntu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ercbegf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Jnfuvatgba." },
+{ "Arial,Geneva,sans-serif,serif",
+ "ZF" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vffhrf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cebsvg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jneavat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fbsgjner" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tvnag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "erirahr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cebsvgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ybjre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "guna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rkcrpgrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "guvf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "dhnegre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qhr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fybjqbja" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "va" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fcraqvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ol" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "grpuabybtl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbzcnavrf." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Ohfvarff" },
+{ "Arial,Geneva,sans-serif,serif",
+ "SGP" },
+{ "Arial,Geneva,sans-serif,serif",
+ "BXf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "NBY" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gvzr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jneare" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zrtn-zrqvn" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "npdhvfvgvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "trgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "guhzof" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "hc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Srqreny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Genqr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pbzzvffvba," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cnivat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jnl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ynetrfg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "H.F." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "uvfgbel." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ohg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "VFC" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zrqvn" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbzcnavrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fgvyy" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zhfg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cnff" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zhfgre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "SPP." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Wvggref" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gnxr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qbja" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Znexrgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "onq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rneavatf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "arjf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unfa'g" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "obggbzrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bhg," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vairfgbef" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ubcrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "--" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Punfr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Znaunggna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "W.C." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Zbetna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pubehf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cebsvg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jneavatf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Bs" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbhefr," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fgbpxf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fyvqr." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Zrq-Grpu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ybat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Yvir" },
+{ "Arial,Geneva,sans-serif,serif",
+ "gur" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Sehvg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Syl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fpvragvfgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qvfpbire" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "trar" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zhgngvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "juvpu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qbhoyr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "yvsr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vafrpg." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Erfrnepuref" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fnl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "uhznaf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "funer" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "\"V'z" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "abg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrnq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "lrg\"" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "trar." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vg'f" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fabjvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "va" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Lbhe" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vaobk" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Zvqjrfgrea" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zrgrbebybtvfgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "puneg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jura" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jurer" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "lbh" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rkcrpg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "oyvmmneq." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ybt" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "trg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "crefbanyvmrq," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ubhe-ol-ubhe" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "arvtuobeubbq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jrngure" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "sberpnfg," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbzvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fbba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "lbhe" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ybpny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fgngvba'f" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jrofvgr." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Xngvr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qrna." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ivqrb" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Tnzrf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pebffunvef" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ivbyrag" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ivqrb" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tnzrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "srnghevat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fcrpvsvp" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tha" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znahsnpgheref'" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "oenaqf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ner" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "abguvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zber" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gbby" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gubfr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbzcnavrf," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbageby" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tebhc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "punetrf." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Ohfvarff:" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Va" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Oevrs" },
+{ "Arial,Geneva,sans-serif,serif",
+ "QG" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qvfpbhagf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Argjbex" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Npprff" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Trezna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gryrpbz" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ybbfraf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tevc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "argjbex." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Nyfb:" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pvfpb" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "frnyf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "22aq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "lrne...." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Krebk" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fryyf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Puvan" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bcrengvba...." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zber." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Zrq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Erpbeqf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Trg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Phr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Sebz" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ancfgre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Nobhg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "50,000" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrnguf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bpphe" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rnpu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "lrne" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zrqvpny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "reebef," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bsgra" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "yvaxrq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qbpgbef" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "univat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vasbezngvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ng" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gurve" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "svatregvcf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "N" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "perngvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ancfgre-yvxr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "crre-gb-crre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "unfgra" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "npprff" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "erpbeqf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Zvpuryyr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qryvb." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qvfnoyrq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Arrq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gbbyf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "sbe" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fpubbyf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Angvbany" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fpvrapr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Sbhaqngvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "eryrnfrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "na" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rkgrafvir" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "frg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cebqhpgvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "thvqryvarf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znxr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "yrneavat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "hfnoyr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vzcnverq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znahny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrkgrevgl," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ybj" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "be" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ab" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ivfvba," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "urnevat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "vzcnvezragf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Xnera" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fbybzba." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Phygher" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fbyivat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Puvan'f" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qbg-Pbz" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Chmmyr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pbzcnavrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "rlrvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jbeyq'f" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "pbafhzre" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "znexrg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "snpr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ynolevagu" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "tbireazragny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "phygheny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bofgnpyrf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "arj" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "obbx" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "hfrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "svpgvbany" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "punenpgref" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "uryc" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "sbervta" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ragercerarhef" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fhfgnvanoyr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Jro" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ohfvarffrf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Nzl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Jh" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ubat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Xbat." },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fpbhe:" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Tbvat," },
+{ "Arial,Geneva,sans-serif,serif",
+ "Tbar" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fpbhe," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Vap.'f" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nffrgf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jrer" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "nhpgvbarq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bss" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "uvturfg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ovqqre." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "svany" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gnyyl:" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "$9" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "zvyyvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "cnvq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bhg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fhecevfr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jvaare," },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "PragreFcna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pbzzhavpngvbaf." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Oenq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Xvat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Wrsserl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Greenpvnab." },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Ryfrjurer" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Gbqnl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ohfu'f" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Arg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ntraqn" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Vaqhfgel" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fgnaqneq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Plorepevzr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gerngl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fgvyy" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Qbrfa'g" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Phg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vg" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "MQAA" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Srqf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jnea" },
+{ "Arial,Geneva,sans-serif,serif",
+ "bs" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ubyvqnl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Unpxvatf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "HFN" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Gbqnl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ovyy" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Tngrf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fubg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Zbphzragnel" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ertvfgre" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Zve" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pbzcnal" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ynhapurf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Arj" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cyna" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "OOP" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Arjf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jrnenoyr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pbzchgref" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Jbexvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pynff" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Arj" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Lbex" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Gvzrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "(Ertvfgengvba" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Erdhverq)" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gur" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Svtug" },
+{ "Arial,Geneva,sans-serif,serif",
+ "gb" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cebgrpg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Nabalzbhf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cbfgvatf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ZFAOP" },
+{ "Arial,Geneva,sans-serif,serif",
+ "'Plorejneevbef'" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Uvg" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Cuvyvccvar" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vzcrnpuzrag" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gevny" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "PAA" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Vagrenpgvir" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Betnavmvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Bayvar" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Zbgure" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Wbarf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pbzr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gbtrgure," },
+{ "Arial,Geneva,sans-serif,serif",
+ "Evtug" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Abj," },
+{ "Arial,Geneva,sans-serif,serif",
+ "Bire" },
+{ "Arial,Geneva,sans-serif,serif",
+ "C2C" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Fnyba" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Grpuabybtl" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Erivrj" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gra" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Grpuabybtl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Erivrj" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Oenva-Znpuvar" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Vagresnprf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Hagnatyvat" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Pbqr" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Srrqonpx" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ " | " },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Uryc" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Nobhg" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Hf" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Wbof" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Nqiregvfr" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Rqvgbevny" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Cbyvpl" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Cevinpl" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Fgngrzrag" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Grezf" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "naq" },
+{ "Verdana, Arial, Geneva, sans-serif,serif",
+ "Pbaqvgvbaf" },
+{ "Verdana, Arial, Geneva,serif",
+ "Pbclevtug" },
+{ "Verdana, Arial, Geneva,serif",
+ "\xc2""\xa9""" },
+{ "Verdana, Arial, Geneva,serif",
+ "2000" },
+{ "Verdana, Arial, Geneva,serif",
+ "Jverq" },
+{ "Verdana, Arial, Geneva,serif",
+ "Qvtvgny" },
+{ "Verdana, Arial, Geneva,serif",
+ "Vap.," },
+{ "Verdana, Arial, Geneva,serif",
+ "n" },
+{ "Verdana, Arial, Geneva,serif",
+ "Ylpbf" },
+{ "Verdana, Arial, Geneva,serif",
+ "Argjbex" },
+{ "Verdana, Arial, Geneva,serif",
+ "fvgr." },
+{ "Verdana, Arial, Geneva,serif",
+ "Nyy" },
+{ "Verdana, Arial, Geneva,serif",
+ "evtugf" },
+{ "Verdana, Arial, Geneva,serif",
+ "erfreirq." },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Dhbgr" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Znexf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "\"Jura" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "vg" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "pbzrf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gb" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "jrngure," },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "vg'f" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "abg" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "bar" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "fvmr" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "svgf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "nyy." },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "V" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "pna'g" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gryy" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "lbh" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "ahzore" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "crbcyr" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gung" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "pnyy" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "zr" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "fnl," },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "'Cnhy," },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "qba'g" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "yvir" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "ng" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "nvecbeg.'\"" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "\xe2""\x80""\x94""" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Cnhy" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Qbhtynf," },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "puvrs" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "zrgrbebybtvfg" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "sbe" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "JPPB" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "gur" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "PRB" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "bs" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Qvtvgny" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Plpybar," },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "ba" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "arvtuobeubbq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "jrngure" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "sberpnfgvat" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "UVGF" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "&" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "ZVFP." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Enagf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "&" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Enirf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Ernqref" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "ba" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "L2X" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "..." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Ubgznvy." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qngrf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "naq" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "R-Iragf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Batbvat" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "tbvatf-ba." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Incbejner" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "2000" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Fhozvg" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "lbhe" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cvpxf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ZNEXRG" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "JNGPU" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Trg" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Dhbgr:" },
+{ "Lucida Grande",
+ "TB" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Flzoby" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "frnepu" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Znexrgf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "|" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Yvir" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Punegf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "VCB" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Rqtr" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Vaqrk" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Cbegsbyvbf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "ZRNAJUVYR..." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Cbcr" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Nffnvyf" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Jrfg" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Gur" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Jrfgrea" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "yvsrfglyr" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "srfgref" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "va" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "\"fcvevghny" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "naq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "zbeny" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "vzcbirefuzrag\"" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "gur" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Jrfg'f" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "arne-zbabcbyl" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "grpuabybtl" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "nyybjf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "vg" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "gb" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "qbzvangr" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "znff" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "pbzzhavpngvbaf," },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cbfvat" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "n" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "guerng" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "bgure" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "phygherf." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Fb" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "fnlf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Cbcr" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Wbua" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Cnhy" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "VV," },
+{ "Verdana,Geneva,sans-serif,serif",
+ "zrffntr" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "frag" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "jbeyq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "yrnqref" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "gjb" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "jrrxf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "nurnq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "bs" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Pngubyvp" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Puhepu'f" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Qnl" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Crnpr." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cbagvss" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "hetrq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "bguref" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "erwrpg" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "\"fynivfu" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "pbasbezvgl\"" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "phygher," },
+{ "Verdana,Geneva,sans-serif,serif",
+ "pnyyvat" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "\"curabzraba" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "infg" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cebcbegvbaf," },
+{ "Verdana,Geneva,sans-serif,serif",
+ "fhfgnvarq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "ol" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cbjreshy" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "zrqvn" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "pnzcnvtaf\"" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "qrfvtarq" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "cebcntngr" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "ivrjcbvagf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "gung" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "qnzntr" },
+{ "Verdana, Arial, Geneva,serif",
+ "erfreirq." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "FRNEPU URNQYVARF" },
+{ "Lucida Grande",
+ "Jverq Arjf" },
+{ "Lucida Grande",
+ "FRNEPU" },
+{ "serif",
+ "37," },
+{ "serif",
+ "jjj.jverq.pbz," },
+{ "serif",
+ "285" },
+{ "serif",
+ "1, 37, jjj.jverq.pbz, 285" },
+{ "Verdana,Sans-serif,serif",
+ "Ylpbf Ubzr" },
+{ "Verdana,Sans-serif,serif",
+ " | " },
+{ "Verdana,Sans-serif,serif",
+ "Serr Jro Npprff" },
+{ "Verdana,Sans-serif,serif",
+ "Fvgr Znc" },
+{ "Verdana,Sans-serif,serif",
+ "Zl Ylpbf" },
+{ "Verdana,Sans-serif,serif",
+ " " },
+{ "Times, Times New Roman, serif,serif",
+ "G B C F G B E V R F" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "hcqngrq 4:20 c.z. Qrp. 14, 2000 CFG" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Jverq Arjf qryvirerq gb lbhe" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " be " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " qrivpr." },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "PHEERAG UBB-UN" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Unc R-Ubyvqnlf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fcbafberq ol " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Tnqtrgf naq Tvmzbf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Gur Jveryrff Jbeyq" },
+{ "Arial,Geneva,sans-serif,serif",
+ "NG&G Jveryrff " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ZC3 Ebpxf gur Jro" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Xrrc hc jvgu gur ohmm " },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "VF/VG Vasbfgehpgher" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "VCB Bhgybbx" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Urjyrgg Cnpxneq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Znxvat gur Tenqr" },
+{ "Arial,Geneva,sans-serif,serif",
+ "\"Byq Fpubby\" trgf jverq" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Ubyyljbbq Grpu" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Gur fgnef bs zhygvzrqvn" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Pryy Cubarf Puvc Va" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Fnsre Pnyyf, be Fryyvat Srne?" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Va jung znl or nabgure choyvp eryngvbaf punyyratr sbe " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gur pryyhyne cubar vaqhfgel, n pbzcnal vf znexrgvat n " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "qrivpr vg fnlf jvyy cerirag pryy cubarf sebz unezvat gur " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "obql. Ol Ryvfn Ongvfgn." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "va Grpuabybtl" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "- - - - -" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ohvyqvat n Orggre Onyybg Obk" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ZVG naq Pnygrpu wbva sbeprf gb qrirybc n ibgvat znpuvar " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "gung jvyy vzcebir hcba gur pheerag ceboyrzngvp flfgrz " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "bs chapu pneqf naq bcgvpny fpnaaref. Gur havirefvgvrf " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jnag gb oevat ibgr zvfpbhagf gb \"arne mreb.\"" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "va Cbyvgvpf" },
+{ "Arial,Geneva,sans-serif,serif",
+ "Ubj K Engrf Jvgu 'J'" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Ubj jvyy gur arkg nqzvavfgengvba qrny jvgu cbeabtencul " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ba gur Vagrearg? Erchoyvpnaf unir cyrqtrq gung gur " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Whfgvpr Qrcnegzrag jvyy cbhapr ba Arg cbeabtencuref, " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "naq Trbetr J. Ohfu unf envyrq ntnvafg bssrafvir " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "jrofvgrf. Ur'f nyfb raqbefrq yvoenel naq fpubby svygrevat. " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qrpyna ZpPhyyntu ercbegf sebz Jnfuvatgba. " },
+{ "Arial,Geneva,sans-serif,serif",
+ "ZF Vffhrf Cebsvg Jneavat" },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Gur fbsgjner tvnag fnlf erirahr naq cebsvgf jvyy or " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "ybjre guna rkcrpgrq guvf dhnegre qhr gb n fybjqbja va " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "fcraqvat ol grpuabybtl pbzcnavrf. " },
+{ "Verdana,Geneva,sans-serif,serif",
+ "va Ohfvarff" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Dhbgr Znexf" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "\"Jura vg pbzrf gb " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "jrngure, vg'f abg bar " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "fvmr svgf nyy. V pna'g gryy " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "lbh gur ahzore bs " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "crbcyr gung pnyy zr naq " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "fnl, 'Cnhy, V qba'g yvir " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "ng gur nvecbeg.'\" " },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "\xe2""\x80""\x94"" Cnhy" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Qbhtynf, puvrs" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "zrgrbebybtvfg sbe JPPB" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "naq gur PRB bs Qvtvgny" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "Plpybar, ba arvtuobeubbq" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "jrngure sberpnfgvat" },
+{ "Verdana,Arial,Helvetica,sans-serif,serif",
+ "." },
+{ "Verdana,Geneva,sans-serif,serif",
+ "UVGF & ZVFP." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Enagf & Enirf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Ernqref ba L2X ... Ubgznvy." },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Qngrf naq R-Iragf" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Batbvat tbvatf-ba. " },
+{ "Verdana,Arial,Geneva,sans-serif,serif",
+ "Incbejner 2000" },
+{ "Verdana,Geneva,sans-serif,serif",
+ "Fhozvg lbhe cvpxf" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "ZNEXRG JNGPU" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ "Trg Dhbgr:" },
+{ "Verdana, Arial, Helvetica, sans-serif,serif",
+ " " },
+{ "Lucida Grande",
+ "TB" },
+{ "serif",
+ "arj!" },
+{ "serif",
+ "Cynl" },
+{ "serif",
+ "serr" },
+{ "serif",
+ "Snagnfl" },
+{ "serif",
+ "AON" },
+{ "serif",
+ "Ovt" },
+{ "serif",
+ "Gbl" },
+{ "serif",
+ "Oybjbhg" },
+{ "serif",
+ "ng" },
+{ "serif",
+ "Gblfehf.pbz" },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "serif",
+ "nqinaprq" },
+{ "serif",
+ "frnepu" },
+{ "serif",
+ "Fubc" },
+{ "serif",
+ "\xc2""\xb7""" },
+{ "serif",
+ "Nhpgvbaf" },
+{ "serif",
+ "Pynffvsvrqf" },
+{ "serif",
+ "Fubccvat" },
+{ "serif",
+ "Geniry" },
+{ "serif",
+ "Lryybj" },
+{ "serif",
+ "Ctf" },
+{ "serif",
+ "Zncf" },
+{ "serif",
+ "Zrqvn" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Fcbegf" },
+{ "serif",
+ "Fgbpx" },
+{ "serif",
+ "Dhbgrf" },
+{ "serif",
+ "GI" },
+{ "serif",
+ "Jrngure" },
+{ "serif",
+ "Pbaarpg" },
+{ "serif",
+ "Pung" },
+{ "serif",
+ "Pyhof" },
+{ "serif",
+ "Tnzrf" },
+{ "serif",
+ "TrbPvgvrf" },
+{ "serif",
+ "Terrgvatf" },
+{ "serif",
+ "Znvy" },
+{ "serif",
+ "Zrzoref" },
+{ "serif",
+ "Zrffratre" },
+{ "serif",
+ "Crefbanyf" },
+{ "serif",
+ "Crbcyr" },
+{ "serif",
+ "Frnepu" },
+{ "serif",
+ "Sbe" },
+{ "serif",
+ "Xvqf" },
+{ "serif",
+ "Crefbany" },
+{ "serif",
+ "Zl" },
+{ "serif",
+ "Lnubb!" },
+{ "serif",
+ "Nqqe" },
+{ "serif",
+ "Obbx" },
+{ "serif",
+ "Pnyraqne" },
+{ "serif",
+ "Oevrspnfr" },
+{ "serif",
+ "Cubgbf" },
+{ "serif",
+ "Nyregf" },
+{ "serif",
+ "Obbxznexf" },
+{ "serif",
+ "Pbzcnavba" },
+{ "serif",
+ "Ovyy" },
+{ "serif",
+ "Cnl" },
+{ "serif",
+ "zber..." },
+{ "arial,serif",
+ "Pngrtbevrf" },
+{ "serif",
+ "Nagvdhrf" },
+{ "serif",
+ "Nhgbf" },
+{ "serif",
+ "Pbvaf" },
+{ "serif",
+ "Pbzchgref" },
+{ "serif",
+ "Ryrpgebavpf" },
+{ "serif",
+ "Gvpxrgf" },
+{ "serif",
+ "Fcbegf Pneqf" },
+{ "serif",
+ "Fgnzcf" },
+{ "arial,serif",
+ "Vgrzf" },
+{ "serif",
+ "CynlFgngvba" },
+{ "serif",
+ "S1" },
+{ "serif",
+ "Enpr" },
+{ "serif",
+ "Pne" },
+{ "serif",
+ "Qrerx" },
+{ "serif",
+ "Wrgre" },
+{ "serif",
+ "Xngr" },
+{ "serif",
+ "Fcnqr" },
+{ "serif",
+ "Cnyz" },
+{ "serif",
+ "Cvybgf" },
+{ "serif",
+ "Uryyb" },
+{ "serif",
+ "Xvggl" },
+{ "serif",
+ "ZC3" },
+{ "serif",
+ "Cynlref" },
+{ "serif",
+ "Ybatnoretre" },
+{ "arial,serif",
+ "Lnubb!" },
+{ "arial,serif",
+ "Nhpgvbaf" },
+{ "serif",
+ "-" },
+{ "serif",
+ "Ovq," },
+{ "serif",
+ "ohl," },
+{ "serif",
+ "be" },
+{ "serif",
+ "fryy" },
+{ "serif",
+ "nalguvat!" },
+{ "arial,serif",
+ "Negf" },
+{ "arial,serif",
+ "&" },
+{ "arial,serif",
+ "Uhznavgvrf" },
+{ "serif",
+ "Yvgrengher" },
+{ "serif",
+ "," },
+{ "serif",
+ "Cubgbtencul" },
+{ "serif",
+ "..." },
+{ "arial,serif",
+ "Ohfvarff" },
+{ "arial,serif",
+ "Rpbabzl" },
+{ "serif",
+ "O2O" },
+{ "serif",
+ "Svanapr" },
+{ "serif",
+ "Fubccvat" },
+{ "serif",
+ "Wbof" },
+{ "arial,serif",
+ "Pbzchgref" },
+{ "arial,serif",
+ "Vagrearg" },
+{ "serif",
+ "Vagrearg" },
+{ "serif",
+ "JJJ" },
+{ "serif",
+ "Fbsgjner" },
+{ "arial,serif",
+ "Rqhpngvba" },
+{ "serif",
+ "Pbyyrtr" },
+{ "serif",
+ "naq" },
+{ "serif",
+ "Havirefvgl" },
+{ "serif",
+ "X-12" },
+{ "arial,serif",
+ "Ragregnvazrag" },
+{ "serif",
+ "Pbby" },
+{ "serif",
+ "Yvaxf" },
+{ "serif",
+ "Zbivrf" },
+{ "serif",
+ "Uhzbe" },
+{ "serif",
+ "Zhfvp" },
+{ "arial,serif",
+ "Tbireazrag" },
+{ "serif",
+ "Ryrpgvbaf" },
+{ "serif",
+ "Zvyvgnel" },
+{ "serif",
+ "Ynj" },
+{ "serif",
+ "Gnkrf" },
+{ "arial,serif",
+ "Urnygu" },
+{ "serif",
+ "Zrqvpvar" },
+{ "serif",
+ "Qvfrnfrf" },
+{ "serif",
+ "Qehtf" },
+{ "serif",
+ "Svgarff" },
+{ "arial,serif",
+ "Arjf" },
+{ "arial,serif",
+ "Zrqvn" },
+{ "serif",
+ "Shyy" },
+{ "serif",
+ "Pbirentr" },
+{ "serif",
+ "Arjfcncref" },
+{ "arial,serif",
+ "Erperngvba" },
+{ "arial,serif",
+ "Fcbegf" },
+{ "serif",
+ "Bhgqbbef" },
+{ "arial,serif",
+ "Ersrerapr" },
+{ "serif",
+ "Yvoenevrf" },
+{ "serif",
+ "Qvpgvbanevrf" },
+{ "serif",
+ "Dhbgngvbaf" },
+{ "arial,serif",
+ "Ertvbany" },
+{ "serif",
+ "Pbhagevrf" },
+{ "serif",
+ "Ertvbaf" },
+{ "serif",
+ "HF" },
+{ "serif",
+ "Fgngrf" },
+{ "arial,serif",
+ "Fpvrapr" },
+{ "serif",
+ "Navznyf" },
+{ "serif",
+ "Nfgebabzl" },
+{ "serif",
+ "Ratvarrevat" },
+{ "arial,serif",
+ "Fbpvny" },
+{ "serif",
+ "Nepunrbybtl" },
+{ "serif",
+ "Rpbabzvpf" },
+{ "serif",
+ "Ynathntrf" },
+{ "arial,serif",
+ "Fbpvrgl" },
+{ "arial,serif",
+ "Phygher" },
+{ "serif",
+ "Raivebazrag" },
+{ "serif",
+ "Eryvtvba" },
+{ "serif",
+ " \xc2""\xb7"" " },
+{ "serif",
+ "L!" },
+{ "serif",
+ "Oynve" },
+{ "serif",
+ "Jvgpu" },
+{ "serif",
+ "2" },
+{ "serif",
+ "Yhpxl" },
+{ "serif",
+ "Ahzoref" },
+{ "serif",
+ "Enqvb" },
+{ "serif",
+ "ghar" },
+{ "serif",
+ "va" },
+{ "serif",
+ "gb" },
+{ "serif",
+ "lbhe" },
+{ "serif",
+ "snibevgr" },
+{ "serif",
+ "fgngvba" },
+{ "serif",
+ "Vaivgrf" },
+{ "serif",
+ "cyna" },
+{ "serif",
+ "Unyybjrra" },
+{ "serif",
+ "cnegl" },
+{ "arial,serif",
+ "Vafvqr" },
+{ "arial,serif",
+ "Lnubb!" },
+{ "serif",
+ "7cz" },
+{ "serif",
+ "RG" },
+{ "serif",
+ ":" },
+{ "serif",
+ "HPYN" },
+{ "serif",
+ "if." },
+{ "serif",
+ "Nevmban" },
+{ "serif",
+ "9:45cz" },
+{ "serif",
+ "Zbol" },
+{ "serif",
+ "pbapreg" },
+{ "serif",
+ "zber..." },
+{ "arial,serif",
+ "Oebnqpnfg" },
+{ "arial,serif",
+ "Riragf" },
+{ "serif",
+ "tnzrf" },
+{ "serif",
+ "nhpgvbaf" },
+{ "serif",
+ "pyhof" },
+{ "serif",
+ "arjf" },
+{ "serif",
+ "Serr" },
+{ "serif",
+ "56X" },
+{ "serif",
+ "Npprff" },
+{ "serif",
+ "ubyvqnl" },
+{ "serif",
+ "geniry" },
+{ "serif",
+ "Fgber" },
+{ "serif",
+ "ohvyq" },
+{ "serif",
+ "na" },
+{ "serif",
+ "bayvar" },
+{ "serif",
+ "fgber" },
+{ "serif",
+ "10" },
+{ "serif",
+ "zvahgrf" },
+{ "arial,serif",
+ "Znexrgcynpr" },
+{ "serif",
+ "Ibgr" },
+{ "serif",
+ "pbhag" },
+{ "serif",
+ "ortvaf" },
+{ "serif",
+ "ynaqznex" },
+{ "serif",
+ "Xbfbib" },
+{ "serif",
+ "ryrpgvba" },
+{ "serif",
+ "Tber," },
+{ "serif",
+ "Ohfu" },
+{ "serif",
+ "genqr" },
+{ "serif",
+ "oneof," },
+{ "serif",
+ "uvg" },
+{ "serif",
+ "onggyrtebhaq" },
+{ "serif",
+ "fgngrf" },
+{ "serif",
+ "Fgnaqneq" },
+{ "serif",
+ "gvzr" },
+{ "serif",
+ "ergheaf" },
+{ "serif",
+ "Fhaqnl" },
+{ "serif",
+ "sbbgonyy" },
+{ "serif",
+ "AUY" },
+{ "arial,serif",
+ "Va" },
+{ "arial,serif",
+ "gur" },
+{ "arial,serif",
+ "Arjf" },
+{ "serif",
+ "Ybpny" },
+{ "serif",
+ "Lnubb!f" },
+{ "serif",
+ "Rhebcr" },
+{ "serif",
+ "Qraznex" },
+{ "serif",
+ "Senapr" },
+{ "serif",
+ "Treznal" },
+{ "serif",
+ "Vgnyl" },
+{ "serif",
+ "Abejnl" },
+{ "serif",
+ "Fcnva" },
+{ "serif",
+ "Fjrqra" },
+{ "serif",
+ "HX" },
+{ "serif",
+ "&" },
+{ "serif",
+ "Verynaq" },
+{ "serif",
+ "Nfvn" },
+{ "serif",
+ "Cnpvsvp" },
+{ "serif",
+ "Nfvn" },
+{ "serif",
+ "Nhfgenyvn" },
+{ "serif",
+ "AM" },
+{ "serif",
+ "Puvan" },
+{ "serif",
+ "Puvarfr" },
+{ "serif",
+ "UX" },
+{ "serif",
+ "Vaqvn" },
+{ "serif",
+ "Wncna" },
+{ "serif",
+ "Xbern" },
+{ "serif",
+ "Fvatncber" },
+{ "serif",
+ "Gnvjna" },
+{ "serif",
+ "Nzrevpnf" },
+{ "serif",
+ "Netragvan" },
+{ "serif",
+ "Oenmvy" },
+{ "serif",
+ "Pnanqn" },
+{ "serif",
+ "Zrkvpb" },
+{ "serif",
+ "Fcnavfu" },
+{ "serif",
+ "H.F." },
+{ "serif",
+ "Pvgvrf" },
+{ "serif",
+ "Ngynagn" },
+{ "serif",
+ "Obfgba" },
+{ "serif",
+ "Puvpntb" },
+{ "serif",
+ "Qnyynf/SJ" },
+{ "serif",
+ "YN" },
+{ "serif",
+ "ALP" },
+{ "serif",
+ "FS" },
+{ "serif",
+ "Onl" },
+{ "serif",
+ "Jnfu." },
+{ "serif",
+ "QP" },
+{ "serif",
+ "Zber" },
+{ "serif",
+ "Thvqrf" },
+{ "serif",
+ "Pnerref" },
+{ "serif",
+ "Urnygu" },
+{ "serif",
+ "Yvivat" },
+{ "serif",
+ "Crgf" },
+{ "serif",
+ "Erny" },
+{ "serif",
+ "Rfgngr" },
+{ "serif",
+ "Lnubbyvtnaf!" },
+{ "serif",
+ "Ragregnvazrag" },
+{ "serif",
+ "Nfgebybtl" },
+{ "serif",
+ "Riragf" },
+{ "serif",
+ "zber" },
+{ "serif",
+ "Svanapr" },
+{ "serif",
+ "Onaxvat" },
+{ "serif",
+ "Vafhenapr" },
+{ "serif",
+ "Ybnaf" },
+{ "serif",
+ "Yvir" },
+{ "serif",
+ "znexrg" },
+{ "serif",
+ "pbirentr" },
+{ "serif",
+ "Ybpny" },
+{ "serif",
+ "Ybqtvat" },
+{ "serif",
+ "Erfgnhenagf" },
+{ "serif",
+ "Cntrf" },
+{ "serif",
+ "Arjf" },
+{ "serif",
+ "Gbc" },
+{ "serif",
+ "Fgbevrf" },
+{ "serif",
+ "Ohfvarff" },
+{ "serif",
+ "Ragregnvazrag" },
+{ "serif",
+ "Ybggrel" },
+{ "serif",
+ "Cbyvgvpf" },
+{ "serif",
+ "Grpuabybtl" },
+{ "serif",
+ "Choyvfuvat" },
+{ "serif",
+ "Ubzr" },
+{ "serif",
+ "Zrffntr" },
+{ "serif",
+ "Obneqf" },
+{ "serif",
+ "Fznyy" },
+{ "serif",
+ "Ohfvarff" },
+{ "serif",
+ "Znexrgcynpr" },
+{ "serif",
+ "Fznyy" },
+{ "serif",
+ "Pragre" },
+{ "serif",
+ "Ohvyqvat" },
+{ "serif",
+ "Jrofvgr" },
+{ "serif",
+ "Ubfgvat" },
+{ "serif",
+ "Lnubb!" },
+{ "serif",
+ "Rireljurer" },
+{ "serif",
+ "Trg" },
+{ "serif",
+ "ba" },
+{ "serif",
+ "CQN," },
+{ "serif",
+ "Pryy" },
+{ "serif",
+ "Cubar," },
+{ "serif",
+ "Jro" },
+{ "serif",
+ "Cntre" },
+{ "serif",
+ "Ubj" },
+{ "serif",
+ "Fhttrfg" },
+{ "serif",
+ "n" },
+{ "serif",
+ "Fvgr" },
+{ "serif",
+ "Pbzcnal" },
+{ "serif",
+ "Vasb" },
+{ "serif",
+ "Pbclevtug" },
+{ "serif",
+ "Cbyvpl" },
+{ "serif",
+ "Grezf" },
+{ "serif",
+ "bs" },
+{ "serif",
+ "Freivpr" },
+{ "serif",
+ "Pbagevohgbef" },
+{ "serif",
+ "Nqiregvfvat" },
+{ "serif",
+ "\xc2""\xa9""" },
+{ "serif",
+ "2000" },
+{ "serif",
+ "Lnubb!" },
+{ "serif",
+ "Vap." },
+{ "serif",
+ "Nyy" },
+{ "serif",
+ "evtugf" },
+{ "serif",
+ "erfreirq." },
+{ "serif",
+ "Cevinpl" },
+{ "serif",
+ "Cbyvpl" },
+{ "serif",
+ "38," },
+{ "serif",
+ "jjj.lnubb.pbz," },
+{ "serif",
+ "177" },
+{ "serif",
+ "1, 38, jjj.lnubb.pbz, 177" },
+{ "serif",
+ " Cynl serr" },
+{ "serif",
+ "Snagnfl AON" },
+{ "serif",
+ "Ovt Gbl Oybjbhg" },
+{ "serif",
+ "ng Gblfehf.pbz" },
+{ "serif",
+ " " },
+{ "Lucida Grande",
+ "Frnepu" },
+{ "serif",
+ " " },
+{ "serif",
+ "nqinaprq frnepu" },
+{ "serif",
+ " \xc2""\xb7"" " },
+{ "serif",
+ "Lryybj Ctf" },
+{ "serif",
+ " " },
+{ "serif",
+ "Fgbpx Dhbgrf" },
+{ "serif",
+ "Crbcyr Frnepu" },
+{ "serif",
+ "Sbe Xvqf" },
+{ "serif",
+ "Zl Lnubb!" },
+{ "serif",
+ "Nqqe Obbx" },
+{ "serif",
+ "Ovyy Cnl" },
+{ "serif",
+ " " },
+{ "arial,serif",
+ "Lnubb! Nhpgvbaf" },
+{ "serif",
+ " - Ovq, ohl, be fryy nalguvat!" },
+{ "serif",
+ "\xc2""\xb7"" " },
+{ "serif",
+ " " },
+{ "serif",
+ "Fcbegf Pneqf" },
+{ "serif",
+ "S1 Enpr Pne" },
+{ "serif",
+ "Qrerx Wrgre" },
+{ "serif",
+ "Xngr Fcnqr" },
+{ "serif",
+ "Cnyz Cvybgf" },
+{ "serif",
+ "Uryyb Xvggl" },
+{ "serif",
+ "ZC3 Cynlref" },
+{ "arial,serif",
+ "Negf & Uhznavgvrf" },
+{ "serif",
+ ", " },
+{ "arial,serif",
+ "Ohfvarff & Rpbabzl" },
+{ "arial,serif",
+ "Pbzchgref & Vagrearg" },
+{ "serif",
+ "Pbyyrtr naq Havirefvgl" },
+{ "serif",
+ "Pbby Yvaxf" },
+{ "arial,serif",
+ "Arjf & Zrqvn" },
+{ "serif",
+ "Shyy Pbirentr" },
+{ "arial,serif",
+ "Erperngvba & Fcbegf" },
+{ "serif",
+ "HF Fgngrf" },
+{ "arial,serif",
+ "Fbpvny Fpvrapr" },
+{ "arial,serif",
+ "Fbpvrgl & Phygher" },
+{ "arial,serif",
+ "Va gur Arjf" },
+{ "serif",
+ "Ibgr pbhag ortvaf va ynaqznex " },
+{ "serif",
+ "Xbfbib ryrpgvba" },
+{ "serif",
+ "Tber, Ohfu genqr oneof, uvg " },
+{ "serif",
+ "onggyrtebhaq fgngrf" },
+{ "serif",
+ "Fgnaqneq gvzr ergheaf Fhaqnl" },
+{ "serif",
+ "Pbyyrtr sbbgonyy" },
+{ "serif",
+ " - " },
+{ "serif",
+ "CynlFgngvba 2 - " },
+{ "serif",
+ ", " },
+{ "serif",
+ "Serr " },
+{ "serif",
+ "56X Vagrearg Npprff" },
+{ "serif",
+ "L! Geniry" },
+{ "serif",
+ " - cyna lbhe ubyvqnl " },
+{ "serif",
+ "L! Fgber" },
+{ "serif",
+ " - ohvyq na bayvar fgber va " },
+{ "serif",
+ "10 zvahgrf" },
+{ "arial,serif",
+ "Oebnqpnfg Riragf" },
+{ "serif",
+ "7cz RG : HPYN if. " },
+{ "serif",
+ "9:45cz : " },
+{ "serif",
+ "Zbol va pbapreg" },
+{ "arial,serif",
+ "Vafvqr Lnubb!" },
+{ "serif",
+ "L! Zbivrf" },
+{ "serif",
+ "Oynve Jvgpu 2" },
+{ "serif",
+ "Yhpxl " },
+{ "serif",
+ "Lnubb! Enqvb" },
+{ "serif",
+ " - ghar va gb lbhe " },
+{ "serif",
+ "snibevgr fgngvba" },
+{ "serif",
+ "L! Vaivgrf" },
+{ "serif",
+ " - cyna lbhe Unyybjrra " },
+{ "Verdana,Geneva,Arial,serif",
+ "Pnzrenf" },
+{ "Arial,Geneva,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erivrjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uryc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Unaquryqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "TnzrFcbg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubyvqnl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrirybcre" },
+{ "serif",
+ "\xe2""\x80""\xa2""" },
+{ "Arial,Geneva,serif",
+ "Gbc" },
+{ "Arial,Geneva,serif",
+ "Qvtvgny" },
+{ "Arial,Geneva,serif",
+ "Pnzrenf" },
+{ "Arial,Geneva,serif",
+ "Qbjaybnq" },
+{ "Arial,Geneva,serif",
+ "Gur" },
+{ "Arial,Geneva,serif",
+ "Shgher" },
+{ "Arial,Geneva,serif",
+ "Serr" },
+{ "Arial,Geneva,serif",
+ "Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cebqhpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erivrjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohlvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Thvqrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Snfgre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Hcqngrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tb" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jveryrff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fzneg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubccvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZFA" },
+{ "Verdana,Geneva,Arial,serif",
+ "Rkcybere" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uv-Grpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbof" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qvtvgny" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pnzrenf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Frnepu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fznegre" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Barobk" },
+{ "Arial,Geneva,serif",
+ "Frnepu" },
+{ "Arial,Geneva,serif",
+ "Sbe:" },
+{ "serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ " Nyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg " },
+{ "Verdana,Geneva,Arial,serif",
+ " Gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jro " },
+{ "Arial,Geneva,serif",
+ " TB " },
+{ "Verdana,Geneva,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Verdana,Geneva,serif",
+ "Frnepu" },
+{ "Verdana,Geneva,serif",
+ "Gvcf" },
+{ "Verdana,Geneva,serif",
+ "Cbjre" },
+{ "Verdana,Geneva,serif",
+ "Zvpebfbsg" },
+{ "Verdana,Geneva,serif",
+ "jneaf" },
+{ "Verdana,Geneva,serif",
+ "bs" },
+{ "Verdana,Geneva,serif",
+ "D2" },
+{ "Verdana,Geneva,serif",
+ "cebsvg" },
+{ "Verdana,Geneva,serif",
+ "fubegsnyy" },
+{ "Verdana,Geneva,serif",
+ "Pbery" },
+{ "Verdana,Geneva,serif",
+ "gb" },
+{ "Verdana,Geneva,serif",
+ "fnl" },
+{ "Verdana,Geneva,serif",
+ "'nqvbf'" },
+{ "Verdana,Geneva,serif",
+ "Yvahk" },
+{ "Verdana,Geneva,serif",
+ "NBY-Gvzr" },
+{ "Verdana,Geneva,serif",
+ "Jneare" },
+{ "Verdana,Geneva,serif",
+ "pyrnef" },
+{ "Verdana,Geneva,serif",
+ "SGP" },
+{ "Verdana,Geneva,serif",
+ "uheqyr" },
+{ "Verdana,Geneva,serif",
+ "Nabgure" },
+{ "Verdana,Geneva,serif",
+ "tybjvat" },
+{ "Verdana,Geneva,serif",
+ "erivrj" },
+{ "Verdana,Geneva,serif",
+ "sbe" },
+{ "Verdana,Geneva,serif",
+ "Pneaviber" },
+{ "Verdana,Geneva,serif",
+ "Gur" },
+{ "Verdana,Geneva,serif",
+ "arj" },
+{ "Verdana,Geneva,serif",
+ "Fpbhe:" },
+{ "Verdana,Geneva,serif",
+ "Hfref" },
+{ "Verdana,Geneva,serif",
+ "jvyy" },
+{ "Verdana,Geneva,serif",
+ "cnl" },
+{ "Verdana,Geneva,serif",
+ "Vagry" },
+{ "Verdana,Geneva,serif",
+ "IP" },
+{ "Verdana,Geneva,serif",
+ "shaq" },
+{ "Verdana,Geneva,serif",
+ "gnetrgf" },
+{ "Verdana,Geneva,serif",
+ "NFCf," },
+{ "Verdana,Geneva,serif",
+ "jveryrff" },
+{ "Verdana,Geneva,serif",
+ "NapubeQrfx" },
+{ "Verdana,Geneva,serif",
+ ":" },
+{ "Verdana,Geneva,serif",
+ "Arire" },
+{ "Verdana,Geneva,serif",
+ "Trg" },
+{ "Verdana,Geneva,serif",
+ "Ybfg" },
+{ "Verdana,Geneva,serif",
+ "Ntnva" },
+{ "Verdana,Geneva,serif",
+ "Jvgu" },
+{ "Verdana,Geneva,serif",
+ "TCF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zber" },
+{ "Verdana,Geneva,Arial,serif",
+ "arjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "urnqyvarf" },
+{ "Verdana,Geneva,Arial,serif",
+ "..." },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrp" },
+{ "Verdana,Geneva,Arial,serif",
+ "14," },
+{ "Verdana,Geneva,Arial,serif",
+ "2000" },
+{ "Verdana,Geneva,Arial,serif",
+ "3:48" },
+{ "Verdana,Geneva,Arial,serif",
+ "CZ" },
+{ "Verdana,Geneva,Arial,serif",
+ "CG" },
+{ "Verdana,Geneva,Arial,serif",
+ "cebqhpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "erivrjf" },
+{ "Verdana,Geneva,serif",
+ "Qnmmyvat" },
+{ "Verdana,Geneva,serif",
+ "qvtvgny" },
+{ "Verdana,Geneva,serif",
+ "pnzrenf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nggragvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "nyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "fuhggreohtf!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zrrg" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "svir" },
+{ "Verdana,Geneva,Arial,serif",
+ "zbfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "cbchyne" },
+{ "Verdana,Geneva,Arial,serif",
+ "cvpxf" },
+{ "Verdana,Geneva,Arial,serif",
+ "naq" },
+{ "Verdana,Geneva,Arial,serif",
+ "svaq" },
+{ "Verdana,Geneva,Arial,serif",
+ "n" },
+{ "Verdana,Geneva,Arial,serif",
+ "snibevgr" },
+{ "Verdana,Geneva,Arial,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "bja." },
+{ "Verdana,Geneva,Arial,serif",
+ "1." },
+{ "Verdana,Geneva,Arial,serif",
+ "Bylzchf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Q-490" },
+{ "Verdana,Geneva,Arial,serif",
+ "Mbbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ernq" },
+{ "Verdana,Geneva,Arial,serif",
+ "erivrj" },
+{ "Verdana,Geneva,Arial,serif",
+ "|" },
+{ "Verdana,Geneva,Arial,serif",
+ "Purpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "cevprf" },
+{ "Verdana,Geneva,Arial,serif",
+ "2." },
+{ "Verdana,Geneva,Arial,serif",
+ "P-3030" },
+{ "Verdana,Geneva,Arial,serif",
+ "3." },
+{ "Verdana,Geneva,Arial,serif",
+ "Avxba" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbbycvk" },
+{ "Verdana,Geneva,Arial,serif",
+ "990" },
+{ "Verdana,Geneva,Arial,serif",
+ "4." },
+{ "Verdana,Geneva,Arial,serif",
+ "Xbqnx" },
+{ "Verdana,Geneva,Arial,serif",
+ "QP4800" },
+{ "Verdana,Geneva,Arial,serif",
+ "5." },
+{ "Verdana,Geneva,Arial,serif",
+ "Rcfba" },
+{ "Verdana,Geneva,Arial,serif",
+ "CubgbCP" },
+{ "Verdana,Geneva,Arial,serif",
+ "3000M" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zl" },
+{ "Verdana,Geneva,Arial,serif",
+ "cbegsbyvb" },
+{ "Arial,Geneva,serif",
+ " |" },
+{ "Arial,Geneva,serif",
+ "Trg Dhbgr" },
+{ "serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ "Ybbxhc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gvpxre" },
+{ "Verdana,Geneva,Arial,serif",
+ "qvfphffvbaf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zntargvp" },
+{ "Verdana,Geneva,Arial,serif",
+ "zrzbel?" },
+{ "Verdana,Geneva,Arial,serif",
+ "VOZ" },
+{ "Verdana,Geneva,Arial,serif",
+ "pynvzf" },
+{ "Verdana,Geneva,Arial,serif",
+ "gung" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZENZ," },
+{ "Verdana,Geneva,Arial,serif",
+ "jvyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "fcrrq" },
+{ "Verdana,Geneva,Arial,serif",
+ "hc" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbzchgvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "obbg" },
+{ "Verdana,Geneva,Arial,serif",
+ "gvzrf." },
+{ "Verdana,Geneva,Arial,serif",
+ "Vf" },
+{ "Verdana,Geneva,Arial,serif",
+ "guvf" },
+{ "Verdana,Geneva,Arial,serif",
+ "sbe" },
+{ "Verdana,Geneva,Arial,serif",
+ "erny?" },
+{ "Verdana,Geneva,Arial,serif",
+ "--" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zrzbel" },
+{ "Verdana,Geneva,Arial,serif",
+ "vf" },
+{ "Verdana,Geneva,Arial,serif",
+ "abg..." },
+{ "Verdana,Geneva,Arial,serif",
+ "Npghnyyl," },
+{ "Verdana,Geneva,Arial,serif",
+ "ENZ" },
+{ "Verdana,Geneva,Arial,serif",
+ "qbrf..." },
+{ "Verdana,Geneva,Arial,serif",
+ "Jung" },
+{ "Verdana,Geneva,Arial,serif",
+ "qb" },
+{ "Verdana,Geneva,Arial,serif",
+ "lbh" },
+{ "Verdana,Geneva,Arial,serif",
+ "guvax?" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nfvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nhfgenyvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Orytvhz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Puvan" },
+{ "Verdana,Geneva,Arial,serif",
+ "Senapr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Treznal" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vaqvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vgnyl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wncna" },
+{ "Verdana,Geneva,Arial,serif",
+ "Xbern" },
+{ "Verdana,Geneva,Arial,serif",
+ "Argureynaqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cbeghtny" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ehffvn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fbhgu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nsevpn" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fcnva" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fjvgmreynaq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gunvynaq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ghexrl" },
+{ "Verdana,Geneva,Arial,serif",
+ "H.X." },
+{ "Verdana,Geneva,Arial,serif",
+ "Nyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "wbof" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jul" },
+{ "Verdana,Geneva,Arial,serif",
+ "qb" },
+{ "Verdana,Geneva,Arial,serif",
+ "crbcyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "pyvpx?" },
+{ "Verdana,Geneva,Arial,serif",
+ "ybbxvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fureybpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubyzrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "fgngvfgvpf" },
+{ "Verdana,Geneva,Arial,serif",
+ "gb" },
+{ "Verdana,Geneva,Arial,serif",
+ "nanylmr" },
+{ "Verdana,Geneva,Arial,serif",
+ "bhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jro" },
+{ "Verdana,Geneva,Arial,serif",
+ "genssvp," },
+{ "Verdana,Geneva,Arial,serif",
+ "cnggreaf," },
+{ "Verdana,Geneva,Arial,serif",
+ "geraqf." },
+{ "Verdana,Geneva,Arial,serif",
+ "lbh?" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pyvpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "urer" },
+{ "Verdana,Geneva,Arial,serif",
+ "yrnea" },
+{ "Verdana,Geneva,Arial,serif",
+ "zber" },
+{ "Arial,Geneva,serif",
+ "Gvpxre(f)" },
+{ "Arial,Geneva,serif",
+ "Terng" },
+{ "Arial,Geneva,serif",
+ "Tnqtrgf" },
+{ "Arial,Geneva,serif",
+ "sbe" },
+{ "Arial,Geneva,serif",
+ "gur" },
+{ "Arial,Geneva,serif",
+ "Ubyvqnlf" },
+{ "arial,serif",
+ "\xe2""\x80""\xa2""" },
+{ "arial,serif",
+ "TNZRF" },
+{ "arial,serif",
+ "ABGROBBXF" },
+{ "arial,serif",
+ "TNQTRGF" },
+{ "arial,serif",
+ "CREVCURENYF" },
+{ "arial,serif",
+ "QVTVGNY" },
+{ "arial,serif",
+ "PNZRENF" },
+{ "arial,serif",
+ "QRFXGBCF" },
+{ "arial,serif",
+ "FBSGJNER" },
+{ "arial,serif",
+ "NHQVB" },
+{ "arial,serif",
+ "NAQ" },
+{ "arial,serif",
+ "IVFHNY" },
+{ "arial,serif",
+ "Abgrobbx" },
+{ "arial,serif",
+ "Flfgrzf" },
+{ "arial,serif",
+ "Qrfxgbc" },
+{ "arial,serif",
+ "Qvtvgny" },
+{ "arial,serif",
+ "Pnzren" },
+{ "arial,serif",
+ "Qrnyf" },
+{ "arial,serif",
+ "Zrzbel" },
+{ "arial,serif",
+ "Hctenqrf" },
+{ "Arial,Geneva,serif",
+ "YRG" },
+{ "Arial,Geneva,serif",
+ "HF" },
+{ "Arial,Geneva,serif",
+ "URYC" },
+{ "Arial,Geneva,serif",
+ "LBH" },
+{ "Arial,Geneva,serif",
+ "SVAQ" },
+{ "Arial,Geneva,serif",
+ "GUR" },
+{ "Arial,Geneva,serif",
+ "CRESRPG" },
+{ "Arial,Geneva,serif",
+ "TVSG" },
+{ "Arial,Geneva,serif",
+ "Naq" },
+{ "Arial,Geneva,serif",
+ "gur" },
+{ "Arial,Geneva,serif",
+ "jvaare" },
+{ "Arial,Geneva,serif",
+ "vf:" },
+{ "Arial,Geneva,serif",
+ "Fubccre'f" },
+{ "Arial,Geneva,serif",
+ "Pubvpr" },
+{ "Arial,Geneva,serif",
+ "Njneqf" },
+{ "Arial,Geneva,serif",
+ "2000" },
+{ "Arial,Geneva,serif",
+ "Ernqref" },
+{ "Arial,Geneva,serif",
+ "ibgrq" },
+{ "Arial,Geneva,serif",
+ "sbe" },
+{ "Arial,Geneva,serif",
+ "gurve" },
+{ "Arial,Geneva,serif",
+ "snibevgrf" },
+{ "Arial,Geneva,serif",
+ "va" },
+{ "Arial,Geneva,serif",
+ "pngrtbevrf" },
+{ "Arial,Geneva,serif",
+ "enatvat" },
+{ "Arial,Geneva,serif",
+ "sebz" },
+{ "Arial,Geneva,serif",
+ "Orfg" },
+{ "Arial,Geneva,serif",
+ "Qrfxgbc" },
+{ "Arial,Geneva,serif",
+ "CP" },
+{ "Arial,Geneva,serif",
+ "gb" },
+{ "Arial,Geneva,serif",
+ "Jro-Onfrq" },
+{ "Arial,Geneva,serif",
+ "Freivpr." },
+{ "Arial,Geneva,serif",
+ "Svaq" },
+{ "Arial,Geneva,serif",
+ "bhg" },
+{ "Arial,Geneva,serif",
+ "jung" },
+{ "Arial,Geneva,serif",
+ "gurl" },
+{ "Arial,Geneva,serif",
+ "pubfr." },
+{ "Arial,Geneva,serif",
+ "Frr" },
+{ "Arial,Geneva,serif",
+ "gur" },
+{ "Arial,Geneva,serif",
+ "erfhygf" },
+{ "Arial,Geneva,serif",
+ "ng" },
+{ "Arial,Geneva,serif",
+ "MQArg" },
+{ "Arial,Geneva,serif",
+ "Erivrjf" },
+{ "Arial,Geneva,serif",
+ "Erivrjf" },
+{ "Arial,Geneva,serif",
+ "CPf" },
+{ "Arial,Geneva,serif",
+ "," },
+{ "Arial,Geneva,serif",
+ "Abgrobbxf" },
+{ "Arial,Geneva,serif",
+ "Unaquryqf" },
+{ "Arial,Geneva,serif",
+ "Jveryrff" },
+{ "Arial,Geneva,serif",
+ "Pnzrenf" },
+{ "Arial,Geneva,serif",
+ "Grfgf" },
+{ "Arial,Geneva,serif",
+ "..." },
+{ "Arial,Geneva,serif",
+ "Ohfvarff" },
+{ "Arial,Geneva,serif",
+ "&" },
+{ "Arial,Geneva,serif",
+ "Grpu" },
+{ "Arial,Geneva,serif",
+ "Yvahk" },
+{ "Arial,Geneva,serif",
+ "VG" },
+{ "Arial,Geneva,serif",
+ "Erfbheprf" },
+{ "Arial,Geneva,serif",
+ "Grpu" },
+{ "Arial,Geneva,serif",
+ "Wbof" },
+{ "Arial,Geneva,serif",
+ "R-Pbzzrepr" },
+{ "Arial,Geneva,serif",
+ "Fznyy" },
+{ "Arial,Geneva,serif",
+ "Ovm" },
+{ "Arial,Geneva,serif",
+ "Arjf" },
+{ "Arial,Geneva,serif",
+ "Cntr" },
+{ "Arial,Geneva,serif",
+ "Bar" },
+{ "Arial,Geneva,serif",
+ "NapubeQrfx" },
+{ "Arial,Geneva,serif",
+ "Nyregf" },
+{ "Arial,Geneva,serif",
+ "Pbzchgvat" },
+{ "Arial,Geneva,serif",
+ "Ehzbef" },
+{ "Arial,Geneva,serif",
+ "TnzrFcbg" },
+{ "Arial,Geneva,serif",
+ "Qernzpnfg" },
+{ "Arial,Geneva,serif",
+ "CynlFgngvba" },
+{ "Arial,Geneva,serif",
+ "A64" },
+{ "Arial,Geneva,serif",
+ "Cerivrjf" },
+{ "Arial,Geneva,serif",
+ "Uvagf" },
+{ "Arial,Geneva,serif",
+ "Qbjaybnqf" },
+{ "Arial,Geneva,serif",
+ "Serr" },
+{ "Arial,Geneva,serif",
+ "Fbsgjner" },
+{ "Arial,Geneva,serif",
+ "Gbc" },
+{ "Arial,Geneva,serif",
+ "10" },
+{ "Arial,Geneva,serif",
+ "ZC3" },
+{ "Arial,Geneva,serif",
+ "Yvahk" },
+{ "Arial,Geneva,serif",
+ "Tnzrf" },
+{ "Arial,Geneva,serif",
+ "Fperrafniref" },
+{ "Arial,Geneva,serif",
+ "Ryrpgebavpf" },
+{ "Arial,Geneva,serif",
+ "Qvtvgny" },
+{ "Arial,Geneva,serif",
+ "Ivqrb" },
+{ "Arial,Geneva,serif",
+ "Pnzrenf" },
+{ "Arial,Geneva,serif",
+ "GIf" },
+{ "Arial,Geneva,serif",
+ "Nhqvb" },
+{ "Arial,Geneva,serif",
+ "Gurngre" },
+{ "Arial,Geneva,serif",
+ "Cubarf" },
+{ "Arial,Geneva,serif",
+ "Trne" },
+{ "Arial,Geneva,serif",
+ "Fubccvat" },
+{ "Arial,Geneva,serif",
+ "Uneqjner" },
+{ "Arial,Geneva,serif",
+ "Nhpgvbaf" },
+{ "Arial,Geneva,serif",
+ "Tebhc" },
+{ "Arial,Geneva,serif",
+ "Ohlvat" },
+{ "Arial,Geneva,serif",
+ "r-pragvirf" },
+{ "Arial,Geneva,serif",
+ "Uryc" },
+{ "Arial,Geneva,serif",
+ "Ubj-Gb" },
+{ "Arial,Geneva,serif",
+ "Ivehfrf" },
+{ "Arial,Geneva,serif",
+ "Svk" },
+{ "Arial,Geneva,serif",
+ "Vg" },
+{ "Arial,Geneva,serif",
+ "Rkcregf" },
+{ "Arial,Geneva,serif",
+ "Ubj-Gb" },
+{ "Arial,Geneva,serif",
+ "Hcqngrf" },
+{ "Arial,Geneva,serif",
+ "Bayvar" },
+{ "Arial,Geneva,serif",
+ "Pynffrf" },
+{ "Arial,Geneva,serif",
+ "Vairfgvat" },
+{ "Arial,Geneva,serif",
+ "Fgbpx" },
+{ "Arial,Geneva,serif",
+ "Dhbgrf" },
+{ "Arial,Geneva,serif",
+ "VCBf" },
+{ "Arial,Geneva,serif",
+ "Urnqyvar" },
+{ "Arial,Geneva,serif",
+ "Arjf" },
+{ "Arial,Geneva,serif",
+ "Cbegsbyvb" },
+{ "Arial,Geneva,serif",
+ "Yvsr" },
+{ "Arial,Geneva,serif",
+ "Snzvyl" },
+{ "Arial,Geneva,serif",
+ "Pbby" },
+{ "Arial,Geneva,serif",
+ "Arg" },
+{ "Arial,Geneva,serif",
+ "Yvsr" },
+{ "Arial,Geneva,serif",
+ "Zhfvp" },
+{ "Arial,Geneva,serif",
+ "Ynhtuf" },
+{ "Arial,Geneva,serif",
+ "ZC3" },
+{ "Arial,Geneva,serif",
+ "Qrirybcre" },
+{ "Arial,Geneva,serif",
+ "UGZY" },
+{ "Arial,Geneva,serif",
+ "Wnin" },
+{ "Arial,Geneva,serif",
+ "Serr" },
+{ "Arial,Geneva,serif",
+ "Fpevcgf" },
+{ "Arial,Geneva,serif",
+ "Jro" },
+{ "Arial,Geneva,serif",
+ "Tencuvpf" },
+{ "Arial,Geneva,serif",
+ "Hfnovyvgl" },
+{ "Arial,Geneva,serif",
+ "ZlMQArg" },
+{ "Arial,Geneva,serif",
+ "R-Znvy" },
+{ "Arial,Geneva,serif",
+ "rPvepyrf" },
+{ "Arial,Geneva,serif",
+ "Pnyraqne" },
+{ "Arial,Geneva,serif",
+ "Qvfphffvbaf" },
+{ "Arial,Geneva,serif",
+ "MQArg" },
+{ "Arial,Geneva,serif",
+ "Erjneqf" },
+{ "Arial,Geneva,serif",
+ "Gnxr" },
+{ "Arial,Geneva,serif",
+ "n" },
+{ "Arial,Geneva,serif",
+ "ovgr" },
+{ "Arial,Geneva,serif",
+ "bhg" },
+{ "Arial,Geneva,serif",
+ "bs" },
+{ "Arial,Geneva,serif",
+ "Nccyr'f" },
+{ "Arial,Geneva,serif",
+ "DhvpxGvzr" },
+{ "Arial,Geneva,serif",
+ "5" },
+{ "Arial,Geneva,serif",
+ "Trg" },
+{ "Arial,Geneva,serif",
+ "Synfu" },
+{ "Arial,Geneva,serif",
+ "4" },
+{ "Arial,Geneva,serif",
+ "fhccbeg," },
+{ "Arial,Geneva,serif",
+ "vzcebirq" },
+{ "Arial,Geneva,serif",
+ "nhqvb," },
+{ "Arial,Geneva,serif",
+ "snfgre" },
+{ "Arial,Geneva,serif",
+ "ivqrb" },
+{ "Arial,Geneva,serif",
+ "cynlonpx," },
+{ "Arial,Geneva,serif",
+ "naq" },
+{ "Arial,Geneva,serif",
+ "zber." },
+{ "Arial,Geneva,serif",
+ "Gnxr" },
+{ "Arial,Geneva,serif",
+ "n" },
+{ "Arial,Geneva,serif",
+ "farnx" },
+{ "Arial,Geneva,serif",
+ "crrx" },
+{ "Arial,Geneva,serif",
+ "gurfr" },
+{ "Arial,Geneva,serif",
+ "arj" },
+{ "Arial,Geneva,serif",
+ "srngherf" },
+{ "Arial,Geneva,serif",
+ "jvgu" },
+{ "Arial,Geneva,serif",
+ "DhvpxGvzr" },
+{ "Arial,Geneva,serif",
+ "5" },
+{ "Arial,Geneva,serif",
+ "Choyvp" },
+{ "Arial,Geneva,serif",
+ "Cerivrj" },
+{ "Arial,Geneva,serif",
+ "2." },
+{ "Arial,Geneva,serif",
+ "svyr" },
+{ "Arial,Geneva,serif",
+ "Trg" },
+{ "Arial,Geneva,serif",
+ "fgnegrq" },
+{ "Arial,Geneva,serif",
+ "jvgu" },
+{ "Arial,Geneva,serif",
+ "Bcren" },
+{ "Arial,Geneva,serif",
+ "5.0" },
+{ "Arial,Geneva,serif",
+ "Vagrearg" },
+{ "Arial,Geneva,serif",
+ "Rkcybere" },
+{ "Arial,Geneva,serif",
+ "Argfpncr" },
+{ "Arial,Geneva,serif",
+ "ner" },
+{ "Arial,Geneva,serif",
+ "abg" },
+{ "Arial,Geneva,serif",
+ "bayl" },
+{ "Arial,Geneva,serif",
+ "oebjfref" },
+{ "Arial,Geneva,serif",
+ "gurer." },
+{ "Arial,Geneva,serif",
+ "Yrnea" },
+{ "Arial,Geneva,serif",
+ "ubj" },
+{ "Arial,Geneva,serif",
+ "hfr" },
+{ "Arial,Geneva,serif",
+ "cbjreshy" },
+{ "Arial,Geneva,serif",
+ "bs" },
+{ "Arial,Geneva,serif",
+ "Bcren" },
+{ "Arial,Geneva,serif",
+ "5." },
+{ "Arial,Geneva,serif",
+ "qrgnvyf" },
+{ "Arial,Geneva,serif",
+ "Uryc" },
+{ "Arial,Geneva,serif",
+ "&" },
+{ "Arial,Geneva,serif",
+ "Fgernzvat" },
+{ "Arial,Geneva,serif",
+ "zrqvn'f" },
+{ "Arial,Geneva,serif",
+ "tbg" },
+{ "Arial,Geneva,serif",
+ "qvegl" },
+{ "Arial,Geneva,serif",
+ "frperg" },
+{ "Arial,Geneva,serif",
+ "Rira" },
+{ "Arial,Geneva,serif",
+ "nqinaprf" },
+{ "Arial,Geneva,serif",
+ "grpuabybtl," },
+{ "Arial,Geneva,serif",
+ "fgernzvat" },
+{ "Arial,Geneva,serif",
+ "jba'g" },
+{ "Arial,Geneva,serif",
+ "ghea" },
+{ "Arial,Geneva,serif",
+ "cebsvg" },
+{ "Arial,Geneva,serif",
+ "guebhtu" },
+{ "Arial,Geneva,serif",
+ "znvafgernz" },
+{ "Arial,Geneva,serif",
+ "punaaryf." },
+{ "Arial,Geneva,serif",
+ "jul" },
+{ "Arial,Geneva,serif",
+ "erny" },
+{ "Arial,Geneva,serif",
+ "zbarl" },
+{ "Arial,Geneva,serif",
+ "znxre" },
+{ "Arial,Geneva,serif",
+ "eulzrf" },
+{ "Arial,Geneva,serif",
+ "fpbea." },
+{ "Arial,Geneva,serif",
+ "Shyy" },
+{ "Arial,Geneva,serif",
+ "fgbel" },
+{ "Arial,Geneva,serif",
+ "NapubeQrfx" },
+{ "Arial,Geneva,serif",
+ "Sbe" },
+{ "Arial,Geneva,serif",
+ "CPf" },
+{ "Arial,Geneva,serif",
+ "vg'f" },
+{ "Arial,Geneva,serif",
+ "orfg" },
+{ "Arial,Geneva,serif",
+ "naq" },
+{ "Arial,Geneva,serif",
+ "jbefg" },
+{ "Arial,Geneva,serif",
+ "gvzrf" },
+{ "Arial,Geneva,serif",
+ "Znahsnpgheref" },
+{ "Arial,Geneva,serif",
+ "ergnvyref" },
+{ "Arial,Geneva,serif",
+ "jvyy" },
+{ "Arial,Geneva,serif",
+ "qb" },
+{ "Arial,Geneva,serif",
+ "nalguvat" },
+{ "Arial,Geneva,serif",
+ "trg" },
+{ "Arial,Geneva,serif",
+ "qbbef," },
+{ "Arial,Geneva,serif",
+ "znxvat" },
+{ "Arial,Geneva,serif",
+ "vg" },
+{ "Arial,Geneva,serif",
+ "ohlre'f" },
+{ "Arial,Geneva,serif",
+ "znexrg" },
+{ "Arial,Geneva,serif",
+ "--" },
+{ "Arial,Geneva,serif",
+ "ohg" },
+{ "Arial,Geneva,serif",
+ "pbzcnavrf" },
+{ "Arial,Geneva,serif",
+ "zvtug" },
+{ "Arial,Geneva,serif",
+ "or" },
+{ "Arial,Geneva,serif",
+ "fubbgvat" },
+{ "Arial,Geneva,serif",
+ "gurzfryirf" },
+{ "Arial,Geneva,serif",
+ "sbbg." },
+{ "Arial,Geneva,serif",
+ "CP" },
+{ "Arial,Geneva,serif",
+ ":" },
+{ "Arial,Geneva,serif",
+ "Iveghny" },
+{ "Arial,Geneva,serif",
+ "crbcyr" },
+{ "Arial,Geneva,serif",
+ "fcrnx" },
+{ "Arial,Geneva,serif",
+ "lbhe" },
+{ "Arial,Geneva,serif",
+ "rznvy" },
+{ "Arial,Geneva,serif",
+ "lbh" },
+{ "Arial,Geneva,serif",
+ "'Gvf" },
+{ "Arial,Geneva,serif",
+ "frnfba" },
+{ "Arial,Geneva,serif",
+ "fperra" },
+{ "Arial,Geneva,serif",
+ "fniref" },
+{ "Arial,Geneva,serif",
+ "Purpx" },
+{ "Arial,Geneva,serif",
+ "guvf" },
+{ "Arial,Geneva,serif",
+ "teno" },
+{ "Arial,Geneva,serif",
+ "ont" },
+{ "Arial,Geneva,serif",
+ "serrovrf" },
+{ "Arial,Geneva,serif",
+ "Znp" },
+{ "Arial,Geneva,serif",
+ "Znfgre" },
+{ "Arial,Geneva,serif",
+ "PFF" },
+{ "Arial,Geneva,serif",
+ "fglyrfurrgf" },
+{ "Arial,Geneva,serif",
+ "Cnyz" },
+{ "Arial,Geneva,serif",
+ "Unir" },
+{ "Arial,Geneva,serif",
+ "yrff" },
+{ "Arial,Geneva,serif",
+ "fgerff" },
+{ "Arial,Geneva,serif",
+ "zber" },
+{ "Arial,Geneva,serif",
+ "sha" },
+{ "Arial,Geneva,serif",
+ "PR" },
+{ "Arial,Geneva,serif",
+ "Cbegnoyr" },
+{ "Arial,Geneva,serif",
+ "yvoenel" },
+{ "Arial,Geneva,serif",
+ "Ernq" },
+{ "Arial,Geneva,serif",
+ "robbxf" },
+{ "Arial,Geneva,serif",
+ "naljurer" },
+{ "Arial,Geneva,serif",
+ "Qbjaybnqf" },
+{ "Arial,Geneva,serif",
+ "qnl:" },
+{ "Verdana,Geneva,Arial,serif",
+ "qbjaybnqf" },
+{ "Arial,Geneva,serif",
+ "Arj" },
+{ "Arial,Geneva,serif",
+ "sebz" },
+{ "Arial,Geneva,serif",
+ "Lnubb!" },
+{ "Arial,Geneva,serif",
+ "Vagrearg" },
+{ "Arial,Geneva,serif",
+ "Yvsr" },
+{ "Arial,Geneva,serif",
+ "Frr" },
+{ "Arial,Geneva,serif",
+ "bayvar" },
+{ "Arial,Geneva,serif",
+ "fubccvat" },
+{ "Arial,Geneva,serif",
+ "fvgrf" },
+{ "Arial,Geneva,serif",
+ "Fz@eg" },
+{ "Arial,Geneva,serif",
+ "Cnegare" },
+{ "Arial,Geneva,serif",
+ "Lbh" },
+{ "Arial,Geneva,serif",
+ "pna'g" },
+{ "Arial,Geneva,serif",
+ "yvir" },
+{ "Arial,Geneva,serif",
+ "jvgubhg" },
+{ "Arial,Geneva,serif",
+ "pregvsvpngvbaf" },
+{ "Arial,Geneva,serif",
+ "Orapuznex" },
+{ "Arial,Geneva,serif",
+ "Vafvqre" },
+{ "Arial,Geneva,serif",
+ "Pbzchgre" },
+{ "Arial,Geneva,serif",
+ "Tnzvat" },
+{ "Arial,Geneva,serif",
+ "Jbeyq" },
+{ "Arial,Geneva,serif",
+ "Ryrpgebavp" },
+{ "Arial,Geneva,serif",
+ "Zbaguyl" },
+{ "Arial,Geneva,serif",
+ "rJRRX" },
+{ "Arial,Geneva,serif",
+ "Rkcreg" },
+{ "Arial,Geneva,serif",
+ "Tnzre" },
+{ "Arial,Geneva,serif",
+ "SnzvylCP" },
+{ "Arial,Geneva,serif",
+ "Vagrenpgvir" },
+{ "Arial,Geneva,serif",
+ "Jrrx" },
+{ "Arial,Geneva,serif",
+ "Znpjbeyq" },
+{ "Arial,Geneva,serif",
+ "Bssvpvny" },
+{ "Arial,Geneva,serif",
+ "H.F." },
+{ "Arial,Geneva,serif",
+ "Zntnmvar" },
+{ "Arial,Geneva,serif",
+ "Ohfvarff" },
+{ "Arial,Geneva,serif",
+ "Fz@eg" },
+{ "Arial,Geneva,serif",
+ "Cnegare" },
+{ "Arial,Geneva,serif",
+ "Lnubb!" },
+{ "Arial,Geneva,serif",
+ "Mvss" },
+{ "Arial,Geneva,serif",
+ "Qnivf" },
+{ "Arial,Geneva,serif",
+ "Fzneg" },
+{ "Arial,Geneva,serif",
+ "Pyvpx" },
+{ "Arial,Geneva,serif",
+ "sbe" },
+{ "Arial,Geneva,serif",
+ "bayvar" },
+{ "Arial,Geneva,serif",
+ "zntnmvarf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZRZBEL" },
+{ "Verdana,Geneva,Arial,serif",
+ "OHL" },
+{ "Verdana,Geneva,Arial,serif",
+ "ABJ!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Obbx!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Unaqfcevat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Obbxf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fvzba" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubc" },
+{ "Verdana,Geneva,Arial,serif",
+ "Abj!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "Orfg!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Vg'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "Urer!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ureevat" },
+{ "Verdana,Geneva,Arial,serif",
+ "OhlvatGvcf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fnir" },
+{ "Verdana,Geneva,Arial,serif",
+ "10%" },
+{ "Verdana,Geneva,Arial,serif",
+ "abj" },
+{ "Verdana,Geneva,Arial,serif",
+ "ba" },
+{ "Verdana,Geneva,Arial,serif",
+ "cbjre-obbfgvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pehpvny" },
+{ "Verdana,Geneva,Arial,serif",
+ "ENZ!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrfxgbcf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ng" },
+{ "Verdana,Geneva,Arial,serif",
+ "gur" },
+{ "Verdana,Geneva,Arial,serif",
+ "PQJ" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pragre." },
+{ "Verdana,Geneva,Arial,serif",
+ "3" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbzchgre" },
+{ "Verdana,Geneva,Arial,serif",
+ "$1.99" },
+{ "Verdana,Geneva,Arial,serif",
+ "rnpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "+" },
+{ "Verdana,Geneva,Arial,serif",
+ "1" },
+{ "Verdana,Geneva,Arial,serif",
+ "SERR" },
+{ "Verdana,Geneva,Arial,serif",
+ "j/" },
+{ "Verdana,Geneva,Arial,serif",
+ "zrzorefuvc!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ivfbe" },
+{ "Verdana,Geneva,Arial,serif",
+ "unaquryqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ner" },
+{ "Verdana,Geneva,Arial,serif",
+ "cresrpg" },
+{ "Verdana,Geneva,Arial,serif",
+ "tvsgf" },
+{ "Verdana,Geneva,Arial,serif",
+ "crbcyr" },
+{ "Verdana,Geneva,Arial,serif",
+ "tb!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gnxr" },
+{ "Verdana,Geneva,Arial,serif",
+ "$10" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bss" },
+{ "Verdana,Geneva,Arial,serif",
+ "Onearf" },
+{ "Verdana,Geneva,Arial,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,serif",
+ "Aboyr.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gbqnl!" },
+{ "Verdana,Geneva,Arial,serif",
+ "ORFG" },
+{ "Verdana,Geneva,Arial,serif",
+ "QIQ'f," },
+{ "Verdana,Geneva,Arial,serif",
+ "bssvpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "fhccyvrf," },
+{ "Verdana,Geneva,Arial,serif",
+ "PQ'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "gblf!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubc" },
+{ "Verdana,Geneva,Arial,serif",
+ "ng" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qryy'f" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubzr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fbyhgvba" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pragre" },
+{ "Verdana,Geneva,Arial,serif",
+ "-" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qryy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fznyy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tngrjnl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Pbzchgvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "GBFUVON" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arj:" },
+{ "Verdana,Geneva,Arial,serif",
+ "Gbc" },
+{ "Verdana,Geneva,Arial,serif",
+ "100" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cebqhpgf" },
+{ "Verdana,Geneva,Arial,serif",
+ "bs" },
+{ "Verdana,Geneva,Arial,serif",
+ "2000" },
+{ "Verdana,Geneva,Arial,serif",
+ "sebz" },
+{ "Verdana,Geneva,Arial,serif",
+ "pbzchgrefubccre.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZFA" },
+{ "Verdana,Geneva,Arial,serif",
+ "Rkcybere" },
+{ "Verdana,Geneva,Arial,serif",
+ "ninvynoyr." },
+{ "Verdana,Geneva,Arial,serif",
+ "Qbjaybnq" },
+{ "Verdana,Geneva,Arial,serif",
+ "vg" },
+{ "Verdana,Geneva,Arial,serif",
+ "SERR!" },
+{ "Verdana,Geneva,Arial,serif",
+ "EVFX-SERR!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Sbe" },
+{ "Verdana,Geneva,Arial,serif",
+ "vafvtug" },
+{ "Verdana,Geneva,Arial,serif",
+ "vagb" },
+{ "Verdana,Geneva,Arial,serif",
+ "ohfvarff" },
+{ "Verdana,Geneva,Arial,serif",
+ "grpuabybtl." },
+{ "Verdana,Geneva,Arial,serif",
+ "Ubyvqnl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zhfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "-" },
+{ "Verdana,Geneva,Arial,serif",
+ "20" },
+{ "Verdana,Geneva,Arial,serif",
+ "Orfg" },
+{ "Verdana,Geneva,Arial,serif",
+ "&" },
+{ "Verdana,Geneva,Arial,serif",
+ "Jbefg" },
+{ "Verdana,Geneva,Arial,serif",
+ "r-Fubcf" },
+{ "Verdana,Geneva,Arial,serif",
+ " Zntnmvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "Bssref" },
+{ "Verdana,Geneva,Arial,serif",
+ " Srngherq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Yvaxf" },
+{ "Verdana,Geneva,Arial,serif",
+ " Vagry" },
+{ "Verdana,Geneva,Arial,serif",
+ "Znahsnpghere" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fubjpnfr " },
+{ "Verdana,Geneva,Arial,serif",
+ "Arrq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zber" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uryc?" },
+{ "Verdana,Geneva,Arial,serif",
+ " Fcbafberq" },
+{ "Verdana,Geneva,Arial,serif",
+ "Grpu" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbof" },
+{ "Verdana,Geneva,Arial,serif",
+ " | " },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg" },
+{ "Verdana,Geneva,Arial,serif",
+ "r-pragvirf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr" },
+{ "Verdana,Geneva,Arial,serif",
+ "R-znvy" },
+{ "Verdana,Geneva,Arial,serif",
+ "Arjfyrggref" },
+{ "Verdana,Geneva,Arial,serif",
+ "Hcqngrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZlMQArg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nyregf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Erjneqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Wbva" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zrzoref" },
+{ "Verdana,Geneva,Arial,serif",
+ "FvgrOhvyqre" },
+{ "Verdana,Geneva,Arial,serif",
+ "Srrqonpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cevinpl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Freivpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Grezf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nqiregvfr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nobhg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Hf" },
+{ "Verdana,Geneva,serif",
+ "Pbclevtug" },
+{ "Verdana,Geneva,serif",
+ "\xc2""\xa9""" },
+{ "Verdana,Geneva,serif",
+ "2000" },
+{ "Verdana,Geneva,serif",
+ "MQ" },
+{ "Verdana,Geneva,serif",
+ "Vap." },
+{ "Verdana,Geneva,serif",
+ "MQArg" },
+{ "Verdana,Geneva,serif",
+ "vf" },
+{ "Verdana,Geneva,serif",
+ "n" },
+{ "Verdana,Geneva,serif",
+ "ertvfgrerq" },
+{ "Verdana,Geneva,serif",
+ "freivpr" },
+{ "Verdana,Geneva,serif",
+ "znex" },
+{ "Verdana,Geneva,serif",
+ "bs" },
+{ "Verdana,Geneva,serif",
+ "Ybtb" },
+{ "Arial,Geneva,serif",
+ "Wbva" },
+{ "Arial,Geneva,serif",
+ "MQArg" },
+{ "Arial,Geneva,serif",
+ "Abj" },
+{ "Arial Black,Arial,Geneva,serif",
+ "SERR!" },
+{ "Verdana,Geneva,serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Verdana,Geneva,serif",
+ "Serr" },
+{ "Verdana,Geneva,serif",
+ "r-znvy" },
+{ "Verdana,Geneva,serif",
+ "&" },
+{ "Verdana,Geneva,serif",
+ "ibvprznvy" },
+{ "Verdana,Geneva,serif",
+ "qbjaybnqf" },
+{ "Verdana,Geneva,serif",
+ "arjfyrggref" },
+{ "Verdana,Geneva,serif",
+ "naq" },
+{ "Verdana,Geneva,serif",
+ "zber!" },
+{ "Arial,Geneva,serif",
+ "Fvta" },
+{ "Arial,Geneva,serif",
+ "hc" },
+{ "Arial,Geneva,serif",
+ "gbqnl" },
+{ "Arial,Geneva,serif",
+ "fgneg" },
+{ "Arial,Geneva,serif",
+ "rawblvat" },
+{ "Arial,Geneva,serif",
+ "nyy" },
+{ "Arial,Geneva,serif",
+ "orarsvgf" },
+{ "Arial,Geneva,serif",
+ "serr" },
+{ "Arial,Geneva,serif",
+ "zrzorefuvc" },
+{ "Arial,Geneva,serif",
+ "!" },
+{ "Arial,Geneva,serif",
+ "Dhvpx" },
+{ "Arial,Geneva,serif",
+ "Yvaxf" },
+{ "Verdana,Geneva,serif",
+ "Qrprzore'f" },
+{ "Verdana,Geneva,serif",
+ "Orfg" },
+{ "Verdana,Geneva,serif",
+ "Ohlf" },
+{ "Verdana,Geneva,serif",
+ "Trg" },
+{ "Verdana,Geneva,serif",
+ "FFY" },
+{ "Verdana,Geneva,serif",
+ "Rapelcgvba" },
+{ "Verdana,Geneva,serif",
+ "Pbzchgre" },
+{ "Verdana,Geneva,serif",
+ "Obbxf" },
+{ "Verdana,Geneva,serif",
+ "Qverpg" },
+{ "Arial,Geneva,serif",
+ "Frnepu Sbe:" },
+{ "serif",
+ "39," },
+{ "serif",
+ "jjj.mqarg.pbz," },
+{ "serif",
+ "399" },
+{ "serif",
+ "1, 39, jjj.mqarg.pbz, 399" },
+{ "Arial,Geneva,serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ "Qrp 14, 2000 3:48 CZ CG" },
+{ "Verdana,Geneva,serif",
+ "Zvpebfbsg jneaf bs D2 cebsvg " },
+{ "Verdana,Geneva,serif",
+ "Pbery gb fnl 'nqvbf' gb Yvahk" },
+{ "Verdana,Geneva,serif",
+ "NBY-Gvzr Jneare pyrnef SGP " },
+{ "Verdana,Geneva,serif",
+ "Nabgure tybjvat erivrj sbe " },
+{ "Verdana,Geneva,serif",
+ "Gur arj Fpbhe: Hfref jvyy " },
+{ "Verdana,Geneva,serif",
+ "Vagry IP shaq gnetrgf NFCf, " },
+{ "Verdana,Geneva,serif",
+ ": Arire Trg" },
+{ "Verdana,Geneva,serif",
+ "Ybfg Ntnva Jvgu TCF" },
+{ "Verdana,Geneva,Arial,serif",
+ "Zber arjf urnqyvarf" },
+{ "Verdana,Geneva,serif",
+ "Qnmmyvat qvtvgny " },
+{ "Arial,Geneva,serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ "Nggragvba nyy fuhggreohtf! Zrrg" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg'f svir zbfg cbchyne cvpxf " },
+{ "Arial,Geneva,serif",
+ "Terng Tnqtrgf sbe gur Ubyvqnlf" },
+{ "Arial,Geneva,serif",
+ " " },
+{ "arial,serif",
+ "QVTVGNY PNZRENF" },
+{ "arial,serif",
+ "NHQVB NAQ IVFHNY" },
+{ "arial,serif",
+ "Abgrobbx Flfgrzf" },
+{ "arial,serif",
+ "Qrfxgbc Flfgrzf" },
+{ "arial,serif",
+ "Qvtvgny Pnzren Qrnyf" },
+{ "arial,serif",
+ "Zrzbel Hctenqrf" },
+{ "Arial,Geneva,serif",
+ "YRG HF URYC LBH SVAQ GUR CRESRPG TVSG" },
+{ "Arial,Geneva,serif",
+ "Naq gur jvaare vf: Fubccre'f Pubvpr Njneqf 2000" },
+{ "Arial,Geneva,serif",
+ "Ernqref ibgrq sbe gurve snibevgrf va pngrtbevrf enatvat sebz Orfg " },
+{ "Arial,Geneva,serif",
+ "Qrfxgbc CP gb Orfg Jro-Onfrq Freivpr. Svaq bhg jung gurl pubfr." },
+{ "Arial,Geneva,serif",
+ "Frr gur erfhygf ng MQArg Erivrjf" },
+{ "Arial,Geneva,serif",
+ ", " },
+{ "Arial,Geneva,serif",
+ "Tebhc Ohlvat" },
+{ "Arial,Geneva,serif",
+ "Ohfvarff & Grpu" },
+{ "Arial,Geneva,serif",
+ "VG Erfbheprf" },
+{ "Arial,Geneva,serif",
+ ", " },
+{ "Arial,Geneva,serif",
+ "Grpu Wbof" },
+{ "Arial,Geneva,serif",
+ "Fznyy Ovm" },
+{ "Arial,Geneva,serif",
+ "Uryc & Ubj-Gb" },
+{ "Arial,Geneva,serif",
+ "Svk Vg" },
+{ "Arial,Geneva,serif",
+ "Serr Hcqngrf" },
+{ "Arial,Geneva,serif",
+ "Bayvar Pynffrf" },
+{ "Arial,Geneva,serif",
+ " | " },
+{ "serif",
+ "\xe2""\x80""\xa2"" " },
+{ "Arial,Geneva,serif",
+ "Gbc Qvtvgny Pnzrenf" },
+{ "Arial,Geneva,serif",
+ " " },
+{ "Arial,Geneva,serif",
+ "Qbjaybnq Gur Shgher" },
+{ "Arial,Geneva,serif",
+ "Serr Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ "Cebqhpg Erivrjf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Ohlvat Thvqrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Snfgre Qbjaybnqf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Serr Hcqngrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tb Jveryrff" },
+{ "Verdana,Geneva,Arial,serif",
+ "Fzneg Fubccvat" },
+{ "Verdana,Geneva,Arial,serif",
+ "ZFA Rkcybere" },
+{ "Verdana,Geneva,Arial,serif",
+ "Uv-Grpu Wbof" },
+{ "Verdana,Geneva,Arial,serif",
+ "Qvtvgny Pnzrenf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Frnepu Fznegre" },
+{ "Verdana,Geneva,Arial,serif",
+ "MQArg Barobk" },
+{ "serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ " " },
+{ "Verdana,Geneva,Arial,serif",
+ " Nyy MQArg " },
+{ "Arial,Geneva,serif",
+ " TB " },
+{ "Verdana,Geneva,serif",
+ "Frnepu Gvcf" },
+{ "Verdana,Geneva,serif",
+ "Cbjre Frnepu" },
+{ "Arial,Geneva,serif",
+ "Wbva MQArg Abj" },
+{ "Arial,Geneva,serif",
+ "Fvta hc gbqnl naq fgneg " },
+{ "Arial,Geneva,serif",
+ "rawblvat nyy gur orarsvgf " },
+{ "Arial,Geneva,serif",
+ "bs " },
+{ "Arial,Geneva,serif",
+ "serr zrzorefuvc" },
+{ "Verdana,Geneva,serif",
+ "Serr r-znvy & ibvprznvy" },
+{ "Verdana,Geneva,serif",
+ "Serr qbjaybnqf" },
+{ "Verdana,Geneva,serif",
+ "Serr arjfyrggref" },
+{ "Verdana,Geneva,serif",
+ "naq zber!" },
+{ "Arial,Geneva,serif",
+ "Dhvpx Yvaxf " },
+{ "Verdana,Geneva,serif",
+ "Qrprzore'f Orfg Ohlf" },
+{ "Verdana,Geneva,serif",
+ "Trg FFY Rapelcgvba" },
+{ "Verdana,Geneva,serif",
+ "Pbzchgre Obbxf Qverpg" },
+{ "Arial,Helvetica,Geneva,serif",
+ " VA:" },
+{ "Lucida Grande",
+ "TnzrFcbg" },
+{ "Lucida Grande",
+ "MQArg" },
+{ "Lucida Grande",
+ "Gur" },
+{ "Lucida Grande",
+ "Jro" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Npgvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nqiragher" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qevivat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Chmmyr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ebyr-Cynlvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fvzhyngvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fcbegf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fgengrtl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CynlFgngvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qernzpnfg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Avagraqb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "64" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Obl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbybe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Kobk" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrPhor" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nqinapr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Bhgcbfg.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Rtturnq.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ohl.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbagrfgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "R-Znvy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Sbehzf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "v-Qevir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Wbva" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrFcbg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Uryc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbagnpg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Hf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jbeyqjvqr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "3QSvyrf.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrThvqrf.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrFcl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nepnqr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrFclqre.pbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ " FRNEPU:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gurzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cnex" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ebyyre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbnfgre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "na" },
+{ "Arial,Helvetica,Geneva,serif",
+ "rkpryyrag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tnzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gung" },
+{ "Arial,Helvetica,Geneva,serif",
+ "yrgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbh" },
+{ "Arial,Helvetica,Geneva,serif",
+ "perngr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bja" },
+{ "Arial,Helvetica,Geneva,serif",
+ "iveghny" },
+{ "Arial,Helvetica,Geneva,serif",
+ "nzhfrzrag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cnex." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "oruvaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fprarf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bs" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "qrirybczrag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "naq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "genafvgvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "sebz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CF2." },
+{ "Arial,Helvetica,Geneva,serif",
+ "SHYY" },
+{ "Arial,Helvetica,Geneva,serif",
+ "FGBEL" },
+{ "Arial,Helvetica,Geneva,serif",
+ " TnzrFcbg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Urnqyvar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Arjf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "CP" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Tnzr" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Arjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Arirejvagre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Avtugf'" },
+{ "Arial,Helvetica,Geneva,serif",
+ "hzore" },
+{ "Arial,Helvetica,Geneva,serif",
+ "uhyx," },
+{ "Arial,Helvetica,Geneva,serif",
+ "Oevqtr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbzznaqre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fperraf," },
+{ "Arial,Helvetica,Geneva,serif",
+ "RIR" },
+{ "Arial,Helvetica,Geneva,serif",
+ "naq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ARJF" },
+{ "serif",
+ "." },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ivqrb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Svtugvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ivcref" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zrqvn," },
+{ "Arial,Helvetica,Geneva,serif",
+ "Avagraqb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fryy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "24" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zvyyvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TONf," },
+{ "Arial,Helvetica,Geneva,serif",
+ "CFB" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbzzrepvny," },
+{ "Arial,Helvetica,Geneva,serif",
+ " Fcrpvny" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ercbegf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ubyvqnl" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Tvsg" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Thvqr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Trg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cresrpg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tvsg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "jvgu" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ubyvqnl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "thvqr!" },
+{ "Arial,Helvetica,Geneva,serif",
+ "GNXR" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZR" },
+{ "Arial,Helvetica,Geneva,serif",
+ "GB" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TNZRF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "IVQRB" },
+{ "Arial,Helvetica,Geneva,serif",
+ " CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cerivrjf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Urneg" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "bs" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Jvagre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jr'ir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tbg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "arj" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vasbezngvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fperrafubgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "neg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "sebz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "hcpbzvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "nqq-ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Vprjvaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qnyr." },
+{ "Arial,Helvetica,Geneva,serif",
+ "SHYY" },
+{ "Arial,Helvetica,Geneva,serif",
+ "FGBEL" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Erivrjf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "WN2:" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Hasvavfurq" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ohfvarff" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Vg'f" },
+{ "Arial,Helvetica,Geneva,serif",
+ "n" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fgnaq-nybar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "sbe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fve-Grpu'f" },
+{ "Arial,Helvetica,Geneva,serif",
+ "haqreengrq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gnpgvpny" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ghea-onfrq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fgengrtl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tnzr," },
+{ "Arial,Helvetica,Geneva,serif",
+ "Wnttrq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nyyvnapr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2." },
+{ "Arial,Helvetica,Geneva,serif",
+ " Qbjaybnq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Serr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Thvqrf!" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Serr" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Thvqrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ab" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Bar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvirf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Sberire" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "," },
+{ "Arial,Helvetica,Geneva,serif",
+ "Furazhr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(QP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fnpevsvpr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fvta" },
+{ "Arial,Helvetica,Geneva,serif",
+ "hc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zber" },
+{ "Arial,Helvetica,Geneva,serif",
+ "thvqrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "!" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Ivqrb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzr" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Qenxna:" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Beqre" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Gur" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Synzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qenxna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "jnf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bevtvanyyl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fjbeq-fyvatvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "npgvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tnzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Abj" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vg'f" },
+{ "Arial,Helvetica,Geneva,serif",
+ "urnqrq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CynlFgngvba" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ebthr" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Fcrne" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cbchyne" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fdhnq-onfrq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fubbgre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "uvgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "QP." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Vf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tbbq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "pbairefvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "be" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fgevccrq-qbja" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cbeg?" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvir" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "TnzrFcbg" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Yvir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Lbh" },
+{ "Arial,Helvetica,Geneva,serif",
+ "pna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "nyjnlf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "frr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "svaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zbivrf," },
+{ "Arial,Helvetica,Geneva,serif",
+ "zhfvp," },
+{ "Arial,Helvetica,Geneva,serif",
+ "vagreivrjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ng" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrFcbg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvir." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Arjfyrggref" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Fvta" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Hc" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Sbe" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Arjfyrggref" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pna'g" },
+{ "Arial,Helvetica,Geneva,serif",
+ "xrrc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "hc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "rirel" },
+{ "Arial,Helvetica,Geneva,serif",
+ "qnl?" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Bhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "serr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "arjfyrggref" },
+{ "Arial,Helvetica,Geneva,serif",
+ "jvyy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbh" },
+{ "Arial,Helvetica,Geneva,serif",
+ "qngr!" },
+{ "Arial,Helvetica,Geneva,serif",
+ "GB" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ARJFYRGGRE" },
+{ "Arial,Helvetica,Geneva,serif",
+ "BE" },
+{ "Arial,Helvetica,Geneva,serif",
+ "IT" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Pbzchgre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jbeyq" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "PTJ'f" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Naavirefnel" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Cbyy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pnfg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ibgr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "va" },
+{ "Arial,Helvetica,Geneva,serif",
+ "200gu" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Naavirefnel" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cbyy." },
+{ "Arial,Helvetica,Geneva,serif",
+ "GUR" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CBYY" },
+{ "Arial,Helvetica,Geneva,serif",
+ "PBZCHGRE" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TNZVAT" },
+{ "Arial,Helvetica,Geneva,serif",
+ "JBEYQ" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Arkg-Trarengvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbafbyrf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Arkg-Trarengvba" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Pbafbyrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fpbbc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "pbafbyrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "yvxr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Kobk" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrPhor" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Obl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nqinapr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "." },
+{ "Arial,Helvetica,Geneva,serif",
+ " Erprag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "\xc2""\xb7""" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ublyr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Obneq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tvnagf:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pvgvmra" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Xnohgb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Punzcvbafuvc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fhesre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(QP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gurzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cnex" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ebyyre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbnfgre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CF2)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER" },
+{ "Arial,Helvetica,Geneva,serif",
+ "IT" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CP" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fgnegbcvn" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tybony" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Bcrengvbaf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "18" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jurryre:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nzrevpna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ceb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gehpxre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZQX2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nezntrqqba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qrzbf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qnir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zveen" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Serrfglyr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "OZK" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ANFPNE" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Enpvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "4" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvaxf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2001" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fgne" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gerx" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Iblntre:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ryvgr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Sbepr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "QRZBF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zbivrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nyvpr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "phfgbz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "genvyre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Funqbjonar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "rkpyhfvir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "punenpgre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zbivr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "APNN" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TnzrOernxre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zrtn" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "K5" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CF)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBIVRF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Uvagf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbzznaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "&" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbadhre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Erq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nyreg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Rfpncr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Sebz" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zbaxrl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Vfynaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Riretenpr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ubg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fubgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tbys" },
+{ "Arial,Helvetica,Geneva,serif",
+ "UVAGF" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Vafgnag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cbyy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Juvpu" },
+{ "Arial,Helvetica,Geneva,serif",
+ "RN" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cebcregl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "jbhyq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbh" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zbfg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "yvxr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "frr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Kobk" },
+{ "Arial,Helvetica,Geneva,serif",
+ "?" },
+{ "Arial,Helvetica,Geneva,serif",
+ "SVSN" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fbppre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Uneel" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cbggre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Wnzrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Obaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "007" },
+{ "Arial,Helvetica,Geneva,serif",
+ "AON" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "AUY" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ubpxrl" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Znqqra" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ASY" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fvzf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Abar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bs" },
+{ "Arial,Helvetica,Geneva,serif",
+ "nobir" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Erprag" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Eryrnfrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Tnzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Unys-Yvsr:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbhagre-Fgevxr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cnp-Zna:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Nqiragherf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Va" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gvzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pnyy" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cbjre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "VV" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gvtre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jbbqf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CTN" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gbhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2001" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ab" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Bar" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Yvirf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Sberire" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tenqvhf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "VVV" },
+{ "Arial,Helvetica,Geneva,serif",
+ "&" },
+{ "Arial,Helvetica,Geneva,serif",
+ "VI" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(CF2)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qnir" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Zveen" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Serrfglyr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "OZK" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(TOP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ernql" },
+{ "Arial,Helvetica,Geneva,serif",
+ "2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ehzoyr:" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ebhaq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fcvqre-Zna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "(A64)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Onawb-Gbbvr" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Qrfpevcgvba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "cbchyne" },
+{ "Arial,Helvetica,Geneva,serif",
+ "nqq-ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tbrf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ergnvy." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Geniry" },
+{ "Arial,Helvetica,Geneva,serif",
+ "onpx" },
+{ "Arial,Helvetica,Geneva,serif",
+ "va" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gvzr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "jvgu" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cnp-Zna." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Na" },
+{ "Arial,Helvetica,Geneva,serif",
+ "vzcebirq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "rzcver-ohvyqvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "tnzr." },
+{ "Arial,Helvetica,Geneva,serif",
+ "yngrfg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CTN" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gbhe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "frevrf." },
+{ "Arial,Helvetica,Geneva,serif",
+ "N" },
+{ "Arial,Helvetica,Geneva,serif",
+ "pbybeshy," },
+{ "Arial,Helvetica,Geneva,serif",
+ "60'f-fglyr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fubbgre." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gjb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "pynffvp" },
+{ "Arial,Helvetica,Geneva,serif",
+ "fubbgref" },
+{ "Arial,Helvetica,Geneva,serif",
+ "uvg" },
+{ "Arial,Helvetica,Geneva,serif",
+ "CF2." },
+{ "Arial,Helvetica,Geneva,serif",
+ "fghagf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ba" },
+{ "Arial,Helvetica,Geneva,serif",
+ "TOP." },
+{ "Arial,Helvetica,Geneva,serif",
+ "rira" },
+{ "Arial,Helvetica,Geneva,serif",
+ "zber" },
+{ "Arial,Helvetica,Geneva,serif",
+ "bire-gur-gbc" },
+{ "Arial,Helvetica,Geneva,serif",
+ "obkvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "frdhry." },
+{ "Arial,Helvetica,Geneva,serif",
+ "nznmvat" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fcvqre-Zna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "uvgf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "A64." },
+{ "Arial,Helvetica,Geneva,serif",
+ "Onawb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "naq" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Xnmbbvr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ner" },
+{ "Arial,Helvetica,Geneva,serif",
+ "onpx!" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Eryrnfr/Cevprf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Purpx" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Cevpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "THVQR" },
+{ "Verdana,Geneva,Arial,serif",
+ "Tnzrf.pbz" },
+{ "Verdana,Geneva,Arial,serif",
+ "WhzcFgneg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Frpher" },
+{ "Verdana,Geneva,Arial,serif",
+ "vagenargf" },
+{ "Verdana,Geneva,Arial,serif",
+ "rkgenargf!" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cynl" },
+{ "Verdana,Geneva,Arial,serif",
+ "tnzrf" },
+{ "Verdana,Geneva,Arial,serif",
+ "bayvar" },
+{ "Verdana,Geneva,Arial,serif",
+ "abj!" },
+{ "Verdana,Geneva,Arial,serif",
+ "ghea." },
+{ "Verdana,Geneva,Arial,serif",
+ "Urer" },
+{ "Verdana,Geneva,Arial,serif",
+ "fcrpvny" },
+{ "Verdana,Geneva,Arial,serif",
+ "ubyvqnl" },
+{ "Verdana,Geneva,Arial,serif",
+ "bssre" },
+{ "Verdana,Geneva,Arial,serif",
+ "WhzcFgneg." },
+{ "Arial,Helvetica,Geneva,serif",
+ " FRNEPU:" },
+{ "serif",
+ "NYY" },
+{ "serif",
+ "TNZRF" },
+{ "serif",
+ "NYY TNZRF" },
+{ "Arial,Helvetica,Geneva,serif",
+ " VA:" },
+{ "Arial,Helvetica,Geneva,serif",
+ " " },
+{ "Arial,Helvetica,Geneva,serif",
+ "CynlFgngvba 2" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Avagraqb 64" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzr Obl Pbybe" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tnzr Obl Nqinapr" },
+{ "Arial,Helvetica,Geneva,serif",
+ " TnzrFcbg Urnqyvar Arjf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "CP Tnzr Arjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Arirejvagre Avtugf' hzore uhyx, Oevqtr " },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbzznaqre fperraf, RIR fperraf, naq " },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER ARJF" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ivqrb Tnzr Arjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Svtugvat Ivcref 2 zrqvn, Avagraqb gb fryy 24 zvyyvba " },
+{ "Arial,Helvetica,Geneva,serif",
+ "TONf, CFB Pbzzrepvny, naq " },
+{ "Arial,Helvetica,Geneva,serif",
+ " Fcrpvny Ercbegf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Ubyvqnl Tvsg Thvqr" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Trg gur cresrpg tvsg jvgu bhe " },
+{ "Arial,Helvetica,Geneva,serif",
+ "ubyvqnl tvsg thvqr! " },
+{ "Arial,Helvetica,Geneva,serif",
+ "GNXR ZR GB CP TNZRF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "GNXR ZR GB IVQRB TNZRF" },
+{ "Arial,Helvetica,Geneva,serif",
+ " CP Cerivrjf" },
+{ "Tahoma,Arial,Helvetica,serif",
+ "Urneg bs Jvagre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Jr'ir tbg arj vasbezngvba naq " },
+{ "Arial,Helvetica,Geneva,serif",
+ "arj fperrafubgf naq neg sebz " },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur hcpbzvat nqq-ba gb Vprjvaq " },
+{ "Arial,Helvetica,Geneva,serif",
+ "Qnyr. " },
+{ "Arial,Helvetica,Geneva,serif",
+ "SHYY FGBEL" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Erprag Erivrjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ublyr Obneq Tnzrf (CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tvnagf: Pvgvmra Xnohgb" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Punzcvbafuvc Fhesre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gurzr Cnex Ebyyre" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Pbnfgre (CF2)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER CP ERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER IT ERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Erprag Cerivrjf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Fgnegbcvn (CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Tybony Bcrengvbaf (CP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "18 Jurryre: Nzrevpna" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Ceb Gehpxre (QP)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZQX2 Nezntrqqba (CF2)" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER CP CERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ "ZBER IT CERIVRJF" },
+{ "Arial,Helvetica,Geneva,serif",
+ " Erprag Qrzbf" },
+{ "Arial,Helvetica,Geneva,serif",
+ "Gurzr Cnex Ebyyre Pbnfgre vf " },
+{ "Arial,Helvetica,Geneva,serif",
+ "na rkpryyrag tnzr gung yrgf " },
+{ "Arial,Helvetica,Geneva,serif",
+ "lbh perngr lbhe bja iveghny " },
+{ "Arial,Helvetica,Geneva,serif",
+ "nzhfrzrag cnex. Tb oruvaq " },
+{ "Arial,Helvetica,Geneva,serif",
+ "gur fprarf bs vgf qrirybczrag " },
+{ "Arial,Helvetica,Geneva,serif",
+ "naq vgf genafvgvba sebz gur CP " },
+{ "Arial,Helvetica,Geneva,serif",
+ "gb gur CF2. " },
+{ "Arial,Helvetica,Geneva,serif",
+ "SHYY FGBEL" },
+{ "Verdana,Geneva,Arial,serif",
+ "Srrqonpx" },
+{ "Verdana,Geneva,Arial,serif",
+ "Lbhe" },
+{ "Verdana,Geneva,Arial,serif",
+ "Cevinpl" },
+{ "Verdana,Geneva,Arial,serif",
+ "Freivpr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Grezf" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nqiregvfr" },
+{ "Verdana,Geneva,Arial,serif",
+ "Nobhg" },
+{ "Verdana,Geneva,Arial,serif",
+ "Hf" },
+{ "serif",
+ "40," },
+{ "serif",
+ "jjj.mqarg.pbz_Tnzrfcbg.pbz," },
+{ "serif",
+ "471" },
+{ "serif",
+ "1, 40, jjj.mqarg.pbz_Tnzrfcbg.pbz, 471" },
+{ "serif",
+ "Cntr" },
+{ "serif",
+ "Zva" },
+{ "serif",
+ "Znk" },
+{ "serif",
+ "Zrna" },
+{ "serif",
+ "Fgq" },
+{ "serif",
+ "Gvzrf..." },
+{ "serif",
+ "ohtmvyyn.zbmvyyn.bet/" },
+{ "serif",
+ "rfca.tb.pbz/" },
+{ "serif",
+ "ubzr.argfpncr.pbz/" },
+{ "serif",
+ "ubgjverq.ylpbf.pbz/" },
+{ "serif",
+ "yke.zbmvyyn.bet/" },
+{ "serif",
+ "zl.argfpncr.pbz/" },
+{ "serif",
+ "arjf.parg.pbz/" },
+{ "serif",
+ "fynfuqbg.bet/" },
+{ "serif",
+ "inavyyn-cntr/" },
+{ "serif",
+ "jjj.nygnivfgn.pbz/" },
+{ "serif",
+ "jjj.nznmba.pbz/" },
+{ "serif",
+ "jjj.nby.pbz/" },
+{ "serif",
+ "jjj.nccyr.pbz/" },
+{ "serif",
+ "jjj.paa.pbz/" },
+{ "serif",
+ "jjj.pbzchfreir.pbz/" },
+{ "serif",
+ "jjj.qvtvgnypvgl.pbz/" },
+{ "serif",
+ "jjj.ronl.pbz/" },
+{ "serif",
+ "jjj.rkpvgr.pbz/" },
+{ "serif",
+ "jjj.rkcrqvn.pbz/" },
+{ "serif",
+ "jjj.tbbtyr.pbz/" },
+{ "serif",
+ "jjj.vcynarg.pbz/" },
+{ "serif",
+ "jjj.zncdhrfg.pbz/" },
+{ "serif",
+ "jjj.zvpebfbsg.pbz/" },
+{ "serif",
+ "jjj.zbivrsbar.pbz/" },
+{ "serif",
+ "jjj.zfa.pbz/" },
+{ "serif",
+ "jjj.zfaop.pbz/" },
+{ "serif",
+ "jjj.algvzrf.pbz/" },
+{ "serif",
+ "jjj.algvzrf.pbz_Gnoyr/" },
+{ "serif",
+ "jjj.dhvpxra.pbz/" },
+{ "serif",
+ "jjj.fcvaare.pbz/" },
+{ "serif",
+ "jjj.fha.pbz/" },
+{ "serif",
+ "jjj.gvzr.pbz/" },
+{ "serif",
+ "jjj.gbzfuneqjner.pbz/" },
+{ "serif",
+ "jjj.genirybpvgl.pbz/" },
+{ "serif",
+ "jjj.ibbqbbrkgerzr.pbz/" },
+{ "serif",
+ "jjj.j3.bet_QBZY2Pber/" },
+{ "serif",
+ "jjj.jverq.pbz/" },
+{ "serif",
+ "jjj.lnubb.pbz/" },
+{ "serif",
+ "jjj.mqarg.pbz/" },
+{ "serif",
+ "jjj.mqarg.pbz_Tnzrfcbg.pbz/" },
+{ "serif",
+ "558.00" },
+{ "serif",
+ "989.00" },
+{ "serif",
+ "796.00" },
+{ "serif",
+ "430.00" },
+{ "serif",
+ "554.00" },
+{ "serif",
+ "217.00" },
+{ "serif",
+ "327.00" },
+{ "serif",
+ "209.00" },
+{ "serif",
+ "58.00" },
+{ "serif",
+ "237.00" },
+{ "serif",
+ "231.00" },
+{ "serif",
+ "253.00" },
+{ "serif",
+ "477.00" },
+{ "serif",
+ "538.00" },
+{ "serif",
+ "296.00" },
+{ "serif",
+ "154.00" },
+{ "serif",
+ "180.00" },
+{ "serif",
+ "213.00" },
+{ "serif",
+ "602.00" },
+{ "serif",
+ "1049.00" },
+{ "serif",
+ "121.00" },
+{ "serif",
+ "136.00" },
+{ "serif",
+ "177.00" },
+{ "serif",
+ "163.00" },
+{ "serif",
+ "215.00" },
+{ "serif",
+ "354.00" },
+{ "serif",
+ "331.00" },
+{ "serif",
+ "1022.00" },
+{ "serif",
+ "265.00" },
+{ "serif",
+ "537.00" },
+{ "serif",
+ "454.00" },
+{ "serif",
+ "5045.00" },
+{ "serif",
+ "474.00" },
+{ "serif",
+ "285.00" },
+{ "serif",
+ "399.00" },
+{ "serif",
+ "471.00" },
+{ "serif",
+ "AnA" },
+{ "serif",
+ "558" },
+{ "serif",
+ "989" },
+{ "serif",
+ "796" },
+{ "serif",
+ "430" },
+{ "serif",
+ "554" },
+{ "serif",
+ "217" },
+{ "serif",
+ "327" },
+{ "serif",
+ "209" },
+{ "serif",
+ "58" },
+{ "serif",
+ "237" },
+{ "serif",
+ "231" },
+{ "serif",
+ "253" },
+{ "serif",
+ "477" },
+{ "serif",
+ "538" },
+{ "serif",
+ "296" },
+{ "serif",
+ "154" },
+{ "serif",
+ "180" },
+{ "serif",
+ "213" },
+{ "serif",
+ "602" },
+{ "serif",
+ "1049" },
+{ "serif",
+ "121" },
+{ "serif",
+ "136" },
+{ "serif",
+ "163" },
+{ "serif",
+ "215" },
+{ "serif",
+ "354" },
+{ "serif",
+ "331" },
+{ "serif",
+ "1022" },
+{ "serif",
+ "265" },
+{ "serif",
+ "537" },
+{ "serif",
+ "454" },
+{ "serif",
+ "5045" },
+{ "serif",
+ "474" },
+{ "serif",
+ "285" },
+{ "serif",
+ "[({cntr:\"ohtmvyyn.zbmvyyn.bet/\"," },
+{ "serif",
+ "fgngf:{zva:558," },
+{ "serif",
+ "znk:558," },
+{ "serif",
+ "vaqrkBsZnk:0," },
+{ "serif",
+ "zrna:AnA," },
+{ "serif",
+ "inev:AnA," },
+{ "serif",
+ "fgqq:AnA}," },
+{ "serif",
+ "inyf:[558]}),({cntr:\"rfca.tb.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:989," },
+{ "serif",
+ "znk:989," },
+{ "serif",
+ "inyf:[989]}),({cntr:\"ubzr.argfpncr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:796," },
+{ "serif",
+ "znk:796," },
+{ "serif",
+ "inyf:[796]}),({cntr:\"ubgjverq.ylpbf.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:430," },
+{ "serif",
+ "znk:430," },
+{ "serif",
+ "inyf:[430]}),({cntr:\"yke.zbmvyyn.bet/\"," },
+{ "serif",
+ "fgngf:{zva:554," },
+{ "serif",
+ "znk:554," },
+{ "serif",
+ "inyf:[554]}),({cntr:\"zl.argfpncr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:217," },
+{ "serif",
+ "znk:217," },
+{ "serif",
+ "inyf:[217]}),({cntr:\"arjf.parg.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:327," },
+{ "serif",
+ "znk:327," },
+{ "serif",
+ "inyf:[327]}),({cntr:\"fynfuqbg.bet/\"," },
+{ "serif",
+ "fgngf:{zva:209," },
+{ "serif",
+ "znk:209," },
+{ "serif",
+ "inyf:[209]}),({cntr:\"inavyyn-cntr/\"," },
+{ "serif",
+ "fgngf:{zva:58," },
+{ "serif",
+ "znk:58," },
+{ "serif",
+ "inyf:[58]}),({cntr:\"jjj.nygnivfgn.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:237," },
+{ "serif",
+ "znk:237," },
+{ "serif",
+ "inyf:[237]}),({cntr:\"jjj.nznmba.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:231," },
+{ "serif",
+ "znk:231," },
+{ "serif",
+ "inyf:[231]}),({cntr:\"jjj.nby.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:253," },
+{ "serif",
+ "znk:253," },
+{ "serif",
+ "inyf:[253]}),({cntr:\"jjj.nccyr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:477," },
+{ "serif",
+ "znk:477," },
+{ "serif",
+ "inyf:[477]}),({cntr:\"jjj.paa.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:538," },
+{ "serif",
+ "znk:538," },
+{ "serif",
+ "inyf:[538]}),({cntr:\"jjj.pbzchfreir.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:296," },
+{ "serif",
+ "znk:296," },
+{ "serif",
+ "inyf:[296]}),({cntr:\"jjj.qvtvgnypvgl.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:154," },
+{ "serif",
+ "znk:154," },
+{ "serif",
+ "inyf:[154]}),({cntr:\"jjj.ronl.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:180," },
+{ "serif",
+ "znk:180," },
+{ "serif",
+ "inyf:[180]}),({cntr:\"jjj.rkpvgr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:213," },
+{ "serif",
+ "znk:213," },
+{ "serif",
+ "inyf:[213]}),({cntr:\"jjj.rkcrqvn.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:602," },
+{ "serif",
+ "znk:602," },
+{ "serif",
+ "inyf:[602]}),({cntr:\"jjj.tbbtyr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:1049," },
+{ "serif",
+ "znk:1049," },
+{ "serif",
+ "inyf:[1049]}),({cntr:\"jjj.vcynarg.pbz/\"," },
+{ "serif",
+ "inyf:[477]}),({cntr:\"jjj.zncdhrfg.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:121," },
+{ "serif",
+ "znk:121," },
+{ "serif",
+ "inyf:[121]}),({cntr:\"jjj.zvpebfbsg.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:136," },
+{ "serif",
+ "znk:136," },
+{ "serif",
+ "inyf:[136]}),({cntr:\"jjj.zbivrsbar.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:177," },
+{ "serif",
+ "znk:177," },
+{ "serif",
+ "inyf:[177]}),({cntr:\"jjj.zfa.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:163," },
+{ "serif",
+ "znk:163," },
+{ "serif",
+ "inyf:[163]}),({cntr:\"jjj.zfaop.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:215," },
+{ "serif",
+ "znk:215," },
+{ "serif",
+ "inyf:[215]}),({cntr:\"jjj.algvzrf.pbz/\"," },
+{ "serif",
+ "inyf:[554]}),({cntr:\"jjj.algvzrf.pbz_Gnoyr/\"," },
+{ "serif",
+ "fgngf:{zva:354," },
+{ "serif",
+ "znk:354," },
+{ "serif",
+ "inyf:[354]}),({cntr:\"jjj.dhvpxra.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:331," },
+{ "serif",
+ "znk:331," },
+{ "serif",
+ "inyf:[331]}),({cntr:\"jjj.fcvaare.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:1022," },
+{ "serif",
+ "znk:1022," },
+{ "serif",
+ "inyf:[1022]}),({cntr:\"jjj.fha.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:265," },
+{ "serif",
+ "znk:265," },
+{ "serif",
+ "inyf:[265]}),({cntr:\"jjj.gvzr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:537," },
+{ "serif",
+ "znk:537," },
+{ "serif",
+ "inyf:[537]}),({cntr:\"jjj.gbzfuneqjner.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:454," },
+{ "serif",
+ "znk:454," },
+{ "serif",
+ "inyf:[454]}),({cntr:\"jjj.genirybpvgl.pbz/\"," },
+{ "serif",
+ "inyf:[430]}),({cntr:\"jjj.ibbqbbrkgerzr.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:5045," },
+{ "serif",
+ "znk:5045," },
+{ "serif",
+ "inyf:[5045]}),({cntr:\"jjj.j3.bet_QBZY2Pber/\"," },
+{ "serif",
+ "fgngf:{zva:474," },
+{ "serif",
+ "znk:474," },
+{ "serif",
+ "inyf:[474]}),({cntr:\"jjj.jverq.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:285," },
+{ "serif",
+ "znk:285," },
+{ "serif",
+ "inyf:[285]}),({cntr:\"jjj.lnubb.pbz/\"," },
+{ "serif",
+ "inyf:[177]}),({cntr:\"jjj.mqarg.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:399," },
+{ "serif",
+ "znk:399," },
+{ "serif",
+ "inyf:[399]}),({cntr:\"jjj.mqarg.pbz_Tnzrfcbg.pbz/\"," },
+{ "serif",
+ "fgngf:{zva:471," },
+{ "serif",
+ "znk:471," },
+{ "serif",
+ "inyf:[471]}),]" },
diff --git a/system/graphics/tests/mochitest/mochitest.ini b/system/graphics/tests/mochitest/mochitest.ini
new file mode 100644
index 000000000..6622245b5
--- /dev/null
+++ b/system/graphics/tests/mochitest/mochitest.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+
+[test_acceleration.html]
+subsuite = gpu
+fail-if = (os == "win" && os_version == "5.1" && e10s) # Bug 1253862
+[test_bug509244.html]
+[test_bug513439.html]
+[test_font_whitelist.html]
+[test_overdraw.html]
+# Disable test until bug 1064136 is fixed
+skip-if = true
diff --git a/system/graphics/tests/mochitest/test_acceleration.html b/system/graphics/tests/mochitest/test_acceleration.html
new file mode 100644
index 000000000..cb1fcd39b
--- /dev/null
+++ b/system/graphics/tests/mochitest/test_acceleration.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=627498
+-->
+<head>
+ <title>Test hardware acceleration</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=627498">Mozilla Bug 627498</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+// Make sure that acceleration is enabled/disabled the way we expect it to be
+// based on platform.
+
+var importObj = {};
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+var sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+var xr = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+
+var windows = SpecialPowers.Services.ww.getWindowEnumerator();
+var windowutils;
+var acceleratedWindows = 0;
+while (windows.hasMoreElements()) {
+ windowutils = windows.getNext().QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ try {
+ if (windowutils.layerManagerType != "Basic") {
+ acceleratedWindows++;
+ }
+ } catch (e) {
+ // The window may not have a layer manager, in which case we get an error.
+ // Don't count it as an accelerated window.
+ }
+}
+
+var osName = sysInfo.getProperty("name");
+switch(osName)
+{
+ case "Darwin": // Mac OS X.
+ // We only enable OpenGL layers on machines that don't support QuickDraw
+ // plugins. x86-64 architecture is a good proxy for this plugin support.
+ if (sysInfo.getProperty("arch") != "x86-64") {
+ is(acceleratedWindows, 0, "Acceleration not supported on x86 OS X");
+ } else {
+ // Workaround for SeaMonkey tinderboxes which don't support acceleration.
+ if (navigator.userAgent.match(/ SeaMonkey\//)) {
+ if (acceleratedWindows == 0) {
+ todo(false, "Acceleration not supported on x86-64 OS X" +
+ " (This is expected on SeaMonkey (tinderboxes).)");
+ break;
+ }
+ }
+
+ isnot(acceleratedWindows, 0, "Acceleration enabled on x86-64 OS X");
+ }
+ break;
+
+ case "Windows_NT": // Windows.
+ var version = parseFloat(sysInfo.getProperty("version"));
+ if (version == 5.0) {
+ is(acceleratedWindows, 0, "Acceleration not supported on Windows 2000");
+ } else {
+ // Workaround for SeaMonkey tinderboxes which don't support acceleration.
+ if (navigator.userAgent.match(/ SeaMonkey\//)) {
+ if (acceleratedWindows == 0) {
+ todo(false, "Acceleration not supported on Windows XP or newer" +
+ " (This is expected on SeaMonkey (tinderboxes).)");
+ break;
+ }
+ }
+
+ isnot(acceleratedWindows, 0, "Acceleration enabled on Windows XP or newer");
+ }
+
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ if (version < 6.2) {
+ ok(!gfxInfo.D2DEnabled, "Direct2D not supported on Windows 2008 or older");
+ ok(!gfxInfo.DWriteEnabled, "DirectWrite not supported on Windows 2008 or older");
+ } else {
+ ok(gfxInfo.D2DEnabled, "Direct2D enabled on Windows 8 or newer");
+ ok(gfxInfo.DWriteEnabled, "DirectWrite enabled on Windows 8 or newer");
+ }
+ break;
+
+ case "Linux":
+ todo(false, "Acceleration supported on Linux, but only on taskcluster instances (bug 1296086)");
+ break;
+
+ default:
+ if (xr.OS == "Android") {
+ isnot(acceleratedWindows, 0, "Acceleration enabled on Android");
+ } else {
+ is(acceleratedWindows, 0, "Acceleration not supported on '" + osName + "'");
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/tests/mochitest/test_bug509244.html b/system/graphics/tests/mochitest/test_bug509244.html
new file mode 100644
index 000000000..662d024d1
--- /dev/null
+++ b/system/graphics/tests/mochitest/test_bug509244.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=509244
+-->
+<head>
+ <title>Test for Bug 509244</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=509244">Mozilla Bug 509244</a>
+<p id="display">A</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 509244 **/
+
+function flush() { document.documentElement.offsetHeight; }
+
+var text = document.getElementById("display");
+
+// layout text, caching monospace font
+text.style.fontFamily = "monospace";
+flush();
+// relayout text so that monospace font is no longer used (but cached)
+text.style.fontFamily = "sans-serif";
+flush();
+
+// flush cache
+var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+ .getService(SpecialPowers.Ci.nsIObserverService);
+os.notifyObservers(null, "memory-pressure", "heap-minimize");
+
+// reuse font that was flushed from cache
+text.style.fontFamily = "monospace";
+flush();
+
+ok(true, "not crashed");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/tests/mochitest/test_bug513439.html b/system/graphics/tests/mochitest/test_bug513439.html
new file mode 100644
index 000000000..b3def2b6a
--- /dev/null
+++ b/system/graphics/tests/mochitest/test_bug513439.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=513439
+-->
+<head>
+ <title>Test for Bug 513439</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=513439">Mozilla Bug 513439</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 513439 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var domWindowUtils = SpecialPowers.DOMWindowUtils;
+SpecialPowers.pushPrefEnv({set: [["layout.css.devPixelsPerPx", "2"]]}, () => {
+ is(domWindowUtils.screenPixelsPerCSSPixel, 2, "devPixelsPerPx wasn't set correctly");
+
+ SpecialPowers.pushPrefEnv({set: [["layout.css.devPixelsPerPx", "1.5"]]}, () => {
+ is(domWindowUtils.screenPixelsPerCSSPixel, 1.5, "devPixelsPerPx wasn't set correctly");
+ SimpleTest.finish();
+ });
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/system/graphics/tests/mochitest/test_font_whitelist.html b/system/graphics/tests/mochitest/test_font_whitelist.html
new file mode 100644
index 000000000..52c88662c
--- /dev/null
+++ b/system/graphics/tests/mochitest/test_font_whitelist.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1121643
+-->
+<head>
+ <title>Test for Bug 1121643</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121643">Mozilla Bug 1121643</a>
+<span id="mono" style="font-family: monospace; font-size: 64px;">M</span>
+<span id="sans" style="font-family: sans-serif; font-size: 64px;">M</span>
+<span id="serif" style="font-family: serif; font-size: 64px;">M</span>
+<div id="content" style="display: none">
+
+</div>
+<script class="testbody" type="application/javascript;version=1.7">
+
+/** Test for Bug 1121643 **/
+
+const DOMUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+// Given an element id, returns the first font face name encountered.
+let fontUsed = id => {
+ let element = document.getElementById(id),
+ range = document.createRange();
+ range.selectNode(element);
+ return DOMUtils.getUsedFontFaces(range).item(0).CSSFamilyName;
+}
+
+// A map of the default mono, sans and serif fonts, obtained when
+// whitelisting is disabled.
+const fonts = { mono : fontUsed("mono"),
+ sans : fontUsed("sans"),
+ serif : fontUsed("serif") };
+
+// Set the font whitelist to contain none, some, or all of the
+// default mono, sans, and serif fonts. Check that the rendering
+// of our three test elements uses only fonts present in the
+// whitelist.
+let testFontWhitelist = function* (useMono, useSans, useSerif) {
+ let whitelist = [];
+ if (useMono) {
+ whitelist.push(fonts.mono);
+ }
+ if (useSans) {
+ whitelist.push(fonts.sans);
+ }
+ if (useSerif) {
+ whitelist.push(fonts.serif);
+ }
+ yield SpecialPowers.pushPrefEnv({"set": [["font.system.whitelist",
+ whitelist.join(", ")]]});
+ // If whitelist is empty, then whitelisting is considered disabled
+ // and all fonts are allowed.
+ info("font whitelist: " + JSON.stringify(whitelist));
+ let whitelistEmpty = whitelist.length === 0;
+ is(useMono || whitelistEmpty, fontUsed("mono") === fonts.mono,
+ "Correct mono whitelisting state; got " + fontUsed("mono") + ", requested " + fonts.mono);
+ is(useSans || whitelistEmpty, fontUsed("sans") === fonts.sans,
+ "Correct sans whitelisting state; got " + fontUsed("sans") + ", requested " + fonts.sans);
+ is(useSerif || whitelistEmpty, fontUsed("serif") === fonts.serif,
+ "Correct serif whitelisting state; got " + fontUsed("serif") + ", requested " + fonts.serif);
+}
+
+// Run tests to confirm that only whitelisting fonts are present in a
+// rendered page. Try turning mono, sans, and serif off and on in
+// every combination.
+add_task(function* () {
+ for (let useMono of [false, true]) {
+ for (let useSans of [false, true]) {
+ for (let useSerif of [false, true]) {
+ yield testFontWhitelist(useMono, useSans, useSerif);
+ }
+ }
+ }
+});
+
+</script>
+</body>
+</html>
diff --git a/system/graphics/tests/mochitest/test_overdraw.html b/system/graphics/tests/mochitest/test_overdraw.html
new file mode 100644
index 000000000..3ff9ec32e
--- /dev/null
+++ b/system/graphics/tests/mochitest/test_overdraw.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test overdraw</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript">
+var domWindowUtils = SpecialPowers.getDOMWindowUtils(window);
+
+var overdraw = domWindowUtils.requestCompositorProperty("overdraw");
+
+if (overdraw == -1) {
+ // Overdraw queries are not supported on non OMTC builds.
+ ok(overdraw == -1, "Platform doesn't use a compositor.");
+} else {
+ // Overdraw may be lower than 100% like on OS X where we don't
+ // composite the window corners.
+ ok(overdraw > 0.95 && overdraw < 200, "Overdraw: " + overdraw);
+}
+</script>
+</body>
diff --git a/system/graphics/tests/moz.build b/system/graphics/tests/moz.build
new file mode 100644
index 000000000..671425fcb
--- /dev/null
+++ b/system/graphics/tests/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
+MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
+BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']
diff --git a/system/graphics/tests/reftest/1086723-ref.html b/system/graphics/tests/reftest/1086723-ref.html
new file mode 100644
index 000000000..0242f9e96
--- /dev/null
+++ b/system/graphics/tests/reftest/1086723-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1086723</title>
+ <style type="text/css">
+ .test_div {
+ position: fixed;
+ overflow: hidden;
+ background: blue;
+ width: 50%;
+ height: 50%;
+ border-radius: 0px 50% 50% 0px;
+ }
+ .filler {
+ height: 5000px;
+ }
+ body,html {
+ overflow: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div class="test_div"></div>
+ <div class="filler"></div>
+</body>
+</html>
diff --git a/system/graphics/tests/reftest/1086723.html b/system/graphics/tests/reftest/1086723.html
new file mode 100644
index 000000000..4cfaafa4c
--- /dev/null
+++ b/system/graphics/tests/reftest/1086723.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html reftest-async-scroll reftest-async-scroll-x="0" reftest-async-scroll-y="2000">
+<head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1086723</title>
+ <style type="text/css">
+ .test_div {
+ position: fixed;
+ overflow: hidden;
+ background: blue;
+ width: 50%;
+ height: 50%;
+ border-radius: 0px 50% 50% 0px;
+ }
+ .filler {
+ height: 5000px;
+ }
+ body,html {
+ overflow: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div class="test_div"></div>
+ <div class="filler"></div>
+</body>
+</html>
diff --git a/system/graphics/tests/reftest/1131264-1.svg b/system/graphics/tests/reftest/1131264-1.svg
new file mode 100644
index 000000000..fdafd6b13
--- /dev/null
+++ b/system/graphics/tests/reftest/1131264-1.svg
@@ -0,0 +1,17 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100%" height="100%">
+
+ <title>Testcase for small radius circle with large center coordinates</title>
+ <!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1131264 -->
+
+ <rect width="100%" height="100%" fill="lime"/>
+
+ <circle r="5" cx="40" cy="40" fill="red" />
+ <circle r="1" cx="10004" cy="10004" fill="lime"
+ transform="scale(10 10) translate(-10000 -10000)"/>
+
+</svg>
diff --git a/system/graphics/tests/reftest/1143303-1.svg b/system/graphics/tests/reftest/1143303-1.svg
new file mode 100644
index 000000000..4654cff5c
--- /dev/null
+++ b/system/graphics/tests/reftest/1143303-1.svg
@@ -0,0 +1,26 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ width="100%" height="100%">
+ <title>Testcase for small circles</title>
+ <!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1143303 -->
+
+ <rect width="100%" height="100%" fill="lime"/>
+
+ <circle cx="200" cy="150" r="95" fill="red"/>
+ <g transform="translate(200, 150)" fill="lime">
+ <g transform="scale(1e8, 1e8)">
+ <circle cx="0" cy="0" r="1e-6"/>
+ </g>
+ </g>
+
+ <circle cx="342" cy="176.06" r="1" fill="red"/>
+ <g transform="translate(342,1098.55)" fill="lime">
+ <g transform="scale(418.2,-405.9)">
+ <circle cx="0" cy="2.2727" r=".006"/>
+ </g>
+ </g>
+
+</svg>
diff --git a/system/graphics/tests/reftest/1149923-ref.html b/system/graphics/tests/reftest/1149923-ref.html
new file mode 100644
index 000000000..46625a786
--- /dev/null
+++ b/system/graphics/tests/reftest/1149923-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1149923</title>
+ <style>
+ #outer {
+ width: 64px;
+ height: 64px;
+ background-color: #00f;
+ opacity: 1.0;
+ transform: rotate(90deg);
+ }
+ #inner {
+ width: 100%;
+ height: 100%;
+ background-color: #f00;
+ border-top-left-radius: 10%;
+ border-bottom-left-radius: 10%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id='outer'><div id='inner'></div></div>
+ </body>
+</html>
diff --git a/system/graphics/tests/reftest/1149923.html b/system/graphics/tests/reftest/1149923.html
new file mode 100644
index 000000000..ec5f777ad
--- /dev/null
+++ b/system/graphics/tests/reftest/1149923.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1149923</title>
+ <style>
+ #outer {
+ width: 64px;
+ height: 64px;
+ background-color: #00f;
+ opacity: 1.0;
+ transform: rotate(90deg);
+ }
+ #inner {
+ width: 100%;
+ height: 100%;
+ background-color: #f00;
+ will-change: transform;
+ border-top-left-radius: 10%;
+ border-bottom-left-radius: 10%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id='outer'><div id='inner'></div></div>
+ </body>
+</html>
diff --git a/system/graphics/tests/reftest/468496-1-ref.html b/system/graphics/tests/reftest/468496-1-ref.html
new file mode 100644
index 000000000..c045d7d76
--- /dev/null
+++ b/system/graphics/tests/reftest/468496-1-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+<title>stretched image artifacts (test)</title>
+<style>
+div {
+ height: 5px;
+ background-image: url("data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=");
+ background-repeat: repeat-x;
+}
+</style>
+</head><body>
+<div style="width: 540px">
+</div><div style="width: 541px">
+</div><div style="width: 542px">
+</div><div style="width: 543px">
+</div><div style="width: 544px">
+</div><div style="width: 545px">
+</div><div style="width: 546px">
+</div><div style="width: 547px">
+</div><div style="width: 548px">
+</div><div style="width: 549px">
+</div><div style="width: 550px">
+</div><div style="width: 551px">
+</div><div style="width: 552px">
+</div><div style="width: 553px">
+</div><div style="width: 554px">
+</div><div style="width: 555px">
+</div><div style="width: 556px">
+</div><div style="width: 557px">
+</div><div style="width: 558px">
+</div><div style="width: 559px">
+</div></body></html>
diff --git a/system/graphics/tests/reftest/468496-1.html b/system/graphics/tests/reftest/468496-1.html
new file mode 100644
index 000000000..a6bc6dde6
--- /dev/null
+++ b/system/graphics/tests/reftest/468496-1.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html><head>
+<title>stretched image artifacts (test)</title>
+<style>
+img { display: block; }
+div { height: 5px; }
+</style>
+</head><body>
+<div style="width: 540px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 541px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 542px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 543px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 544px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+
+</div><div style="width: 545px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 546px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 547px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 548px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 549px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 550px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 551px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 552px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 553px">
+
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 554px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 555px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 556px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 557px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 558px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 559px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div></body></html>
diff --git a/system/graphics/tests/reftest/611498-1.html b/system/graphics/tests/reftest/611498-1.html
new file mode 100644
index 000000000..28a9059ae
--- /dev/null
+++ b/system/graphics/tests/reftest/611498-1.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="reftest-wait">
+<script type="text/javascript">
+function done()
+{
+ document.documentElement.className = "";
+}
+function move()
+{
+ elem = document.getElementById("moving");
+ elem.addEventListener("transitionend", done, true);
+ elem.style.position = "relative";
+ elem.style.top = 0;
+}
+</script>
+<body style="background: url('bwinton.jpg'); background-attachment: fixed" onload="move()">
+<div id="moving" style="position: absolute; top: 50px; background-image: url('blacktrans.png'); width: 100px; height: 100px; -moz-transition: top 0.1s; padding: 2px;">blah blah blah</div>
+</body>
+</html>
diff --git a/system/graphics/tests/reftest/611498-ref.html b/system/graphics/tests/reftest/611498-ref.html
new file mode 100644
index 000000000..0763857c8
--- /dev/null
+++ b/system/graphics/tests/reftest/611498-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html>
+<body style="background: url('bwinton.jpg'); background-attachment: fixed">
+<div style="background: url('blacktrans.png'); width: 100px; height: 100px; padding: 2px;">blah blah blah</div>
+</body>
+</html>
diff --git a/system/graphics/tests/reftest/709477-1-ref.html b/system/graphics/tests/reftest/709477-1-ref.html
new file mode 100644
index 000000000..eb8897f8e
--- /dev/null
+++ b/system/graphics/tests/reftest/709477-1-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>glyph clipping (reference)</title>
+ <style>
+ #clip { position: absolute;
+ overflow: hidden;
+ font-size: 16px;
+ width: 500px;
+ height: 300px;}
+ /* Offsets keep the text far enough away from clip boundaries so that
+ cairo knows the text is within the clip. Non-unit alpha color makes
+ the bug show even without antialiasing. */
+ #text { position: absolute;
+ left: 100px;
+ top: 100px;
+ color: rgba(0,0,0,0.4)}
+ #cover { position: absolute;
+ top: 90px;
+ left: 120px;
+ height: 50px;
+ width: 60px;
+ background: transparent; }
+ #mod { position: absolute;
+ top: 400px;
+ left: 0px;
+ height: 2000px;
+ width: 600px;
+ background: transparent; }
+ </style>
+</head>
+<body>
+ <div id="clip">
+ <div id="text">
+ Some text that was</br>
+ initially partially covered.</br>
+ </div>
+ </div>
+ <div id="cover">
+ </div>
+ <div id="mod">
+ </div>
+</body>
+<script>
+ scrollTo(0,1);
+</script>
+</html>
diff --git a/system/graphics/tests/reftest/709477-1.html b/system/graphics/tests/reftest/709477-1.html
new file mode 100644
index 000000000..3e5f86c1b
--- /dev/null
+++ b/system/graphics/tests/reftest/709477-1.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>glyph clipping (test)</title>
+ <style>
+ #clip { position: absolute;
+ overflow: hidden;
+ font-size: 16px;
+ width: 500px;
+ height: 300px;}
+ /* Offsets keep the text far enough away from clip boundaries so that
+ cairo knows the text is within the clip. Non-unit alpha color makes
+ the bug show even without antialiasing. */
+ #text { position: absolute;
+ left: 100px;
+ top: 100px;
+ color: rgba(0,0,0,0.4)}
+ #cover { position: absolute;
+ top: 90px;
+ left: 120px;
+ height: 50px;
+ width: 60px;
+ background: green; }
+ #mod { position: absolute;
+ top: 400px;
+ left: 0px;
+ height: 2000px;
+ width: 600px;
+ background: green; }
+ </style>
+ <script>
+
+function doPaint()
+{
+ window.addEventListener("MozAfterPaint", doScroll, false);
+ var cover = document.getElementById("cover");
+ cover.style.background = "transparent";
+ var mod = document.getElementById("mod");
+ mod.style.background = "transparent";
+}
+
+function doScroll()
+{
+ window.removeEventListener("MozAfterPaint", doScroll, false);
+ window.addEventListener("MozAfterPaint", endTest, false);
+ scrollTo(0,1);
+}
+
+function endTest()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doPaint, false);
+ </script>
+</head>
+<body>
+ <div id="clip">
+ <div id="text">
+ Some text that was</br>
+ initially partially covered.</br>
+ </div>
+ </div>
+ <div id="cover">
+ </div>
+ <div id="mod">
+ </div>
+</body>
+</html>
diff --git a/system/graphics/tests/reftest/853889-1-ref.html b/system/graphics/tests/reftest/853889-1-ref.html
new file mode 100644
index 000000000..1a8513ded
--- /dev/null
+++ b/system/graphics/tests/reftest/853889-1-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head><title>Testcase for bug 853889</title></head>
+ <body>
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400px" height="400px">
+ <path d="M 0 0 L 0 50 L 400 50 L 400 0 Z"
+ fill="rgb(12,200,12)"></path>
+ </svg>
+ </body>
+</html>
diff --git a/system/graphics/tests/reftest/853889-1.html b/system/graphics/tests/reftest/853889-1.html
new file mode 100644
index 000000000..2b728c297
--- /dev/null
+++ b/system/graphics/tests/reftest/853889-1.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head><title>Testcase for bug 853889</title></head>
+ <body>
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400px" height="400px">
+
+ <path d="M 0 400 L 0 450 L 600 450 L 600 400 Z"
+ fill="rgb(200,12,12)"></path>
+ <path d="M 0 0 L 0 50 L 600 50 L 600 0 Z"
+ fill="rgb(200,12,12)"></path>
+
+ <path d="M 0 0 L 0 50 L 600 50 L 600 0 Z
+ M 0 400 L 0 450 L 600 450 L 600 400 Z"
+ fill="rgb(12,200,12)"></path>
+ </svg>
+ </body>
+</html>
diff --git a/system/graphics/tests/reftest/blacktrans.png b/system/graphics/tests/reftest/blacktrans.png
new file mode 100644
index 000000000..1b3ef9baa
--- /dev/null
+++ b/system/graphics/tests/reftest/blacktrans.png
Binary files differ
diff --git a/system/graphics/tests/reftest/bwinton.jpg b/system/graphics/tests/reftest/bwinton.jpg
new file mode 100644
index 000000000..708354511
--- /dev/null
+++ b/system/graphics/tests/reftest/bwinton.jpg
Binary files differ
diff --git a/system/graphics/tests/reftest/pass.svg b/system/graphics/tests/reftest/pass.svg
new file mode 100644
index 000000000..c09c6601e
--- /dev/null
+++ b/system/graphics/tests/reftest/pass.svg
@@ -0,0 +1,8 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+ <title>Testcase reference file for generic pass condition</title>
+ <rect width="100%" height="100%" fill="lime"/>
+</svg>
diff --git a/system/graphics/tests/reftest/reftest-stylo.list b/system/graphics/tests/reftest/reftest-stylo.list
new file mode 100644
index 000000000..378891e06
--- /dev/null
+++ b/system/graphics/tests/reftest/reftest-stylo.list
@@ -0,0 +1,12 @@
+# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
+# 468496-1 will also detect bugs in video drivers.
+== 468496-1.html 468496-1.html
+skip == 611498-1.html 611498-1.html
+skip == 709477-1.html 709477-1.html
+# bug 773482
+skip-if(!asyncPan) == 1086723.html 1086723.html
+== 853889-1.html 853889-1.html
+skip-if(Android) fuzzy-if(skiaContent,1,587) == 1143303-1.svg 1143303-1.svg
+== 1149923.html 1149923.html
+# use fuzzy due to few distorted pixels caused by border-radius
+== 1131264-1.svg 1131264-1.svg
diff --git a/system/graphics/tests/reftest/reftest.list b/system/graphics/tests/reftest/reftest.list
new file mode 100644
index 000000000..2c62d2033
--- /dev/null
+++ b/system/graphics/tests/reftest/reftest.list
@@ -0,0 +1,9 @@
+# 468496-1 will also detect bugs in video drivers.
+== 468496-1.html 468496-1-ref.html
+fuzzy(175,443) == 611498-1.html 611498-ref.html
+fuzzy-if(Android,8,1000) == 709477-1.html 709477-1-ref.html
+skip-if(!asyncPan) == 1086723.html 1086723-ref.html
+== 853889-1.html 853889-1-ref.html
+skip-if(Android) fuzzy-if(skiaContent,1,587) == 1143303-1.svg pass.svg
+fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius
+== 1131264-1.svg pass.svg
diff --git a/system/graphics/tests/unit/test_nsIScriptableRegion.js b/system/graphics/tests/unit/test_nsIScriptableRegion.js
new file mode 100644
index 000000000..51c4749f0
--- /dev/null
+++ b/system/graphics/tests/unit/test_nsIScriptableRegion.js
@@ -0,0 +1,10 @@
+function run_test()
+{
+ let rgn = Components.classes["@mozilla.org/gfx/region;1"].createInstance(Components.interfaces.nsIScriptableRegion);
+ do_check_true (rgn.getRects() === null)
+ rgn.unionRect(0,0,80,60);
+ do_check_true (rgn.getRects().toString() == "0,0,80,60")
+ rgn.unionRect(90,70,1,1);
+ do_check_true (rgn.getRects().toString() == "0,0,80,60,90,70,1,1")
+}
+
diff --git a/system/graphics/tests/unit/xpcshell.ini b/system/graphics/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..1354cd0e6
--- /dev/null
+++ b/system/graphics/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_nsIScriptableRegion.js]
diff --git a/system/graphics/thebes/CJKCompatSVS.cpp b/system/graphics/thebes/CJKCompatSVS.cpp
new file mode 100644
index 000000000..e68d98e92
--- /dev/null
+++ b/system/graphics/thebes/CJKCompatSVS.cpp
@@ -0,0 +1,1039 @@
+// Generated by gencjkcisvs.py. Do not edit.
+
+#include <stdint.h>
+
+#define U16(v) (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define U24(v) (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define U32(v) (((v) >> 24) & 0xFF), (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define GLYPH(v) U16(v >= 0x2F800 ? (v) - (0x2F800 - 0xFB00) : (v))
+
+// Fallback mappings for CJK Compatibility Ideographs Standardized Variants
+// taken from StandardizedVariants-6.3.0.txt.
+// Using OpenType format 14 cmap subtable structure to reuse the lookup code
+// for fonts. The glyphID field is used to store the corresponding codepoints
+// CJK Compatibility Ideographs. To fit codepoints into the 16-bit glyphID
+// field, CJK Compatibility Ideographs Supplement (U+2F800..U+2FA1F) will be
+// mapped to 0xFB00..0xFD1F.
+extern const uint8_t sCJKCompatSVSTable[] = {
+ U16(14), // format
+ U32(5065), // length
+ U32(3), // numVarSelectorRecords
+ U24(0xFE00), U32(0), U32(43), // varSelectorRecord[0]
+ U24(0xFE01), U32(0), U32(4557), // varSelectorRecord[1]
+ U24(0xFE02), U32(0), U32(5001), // varSelectorRecord[2]
+ // 0xFE00
+ U32(902), // numUVSMappings
+ U24(0x349E), GLYPH(0x2F80C),
+ U24(0x34B9), GLYPH(0x2F813),
+ U24(0x34BB), GLYPH(0x2F9CA),
+ U24(0x34DF), GLYPH(0x2F81F),
+ U24(0x3515), GLYPH(0x2F824),
+ U24(0x36EE), GLYPH(0x2F867),
+ U24(0x36FC), GLYPH(0x2F868),
+ U24(0x3781), GLYPH(0x2F876),
+ U24(0x382F), GLYPH(0x2F883),
+ U24(0x3862), GLYPH(0x2F888),
+ U24(0x387C), GLYPH(0x2F88A),
+ U24(0x38C7), GLYPH(0x2F896),
+ U24(0x38E3), GLYPH(0x2F89B),
+ U24(0x391C), GLYPH(0x2F8A2),
+ U24(0x393A), GLYPH(0x2F8A1),
+ U24(0x3A2E), GLYPH(0x2F8C2),
+ U24(0x3A6C), GLYPH(0x2F8C7),
+ U24(0x3AE4), GLYPH(0x2F8D1),
+ U24(0x3B08), GLYPH(0x2F8D0),
+ U24(0x3B19), GLYPH(0x2F8CE),
+ U24(0x3B49), GLYPH(0x2F8DE),
+ U24(0x3B9D), GLYPH(0xFAD2),
+ U24(0x3C18), GLYPH(0x2F8EE),
+ U24(0x3C4E), GLYPH(0x2F8F2),
+ U24(0x3D33), GLYPH(0x2F90A),
+ U24(0x3D96), GLYPH(0x2F916),
+ U24(0x3EAC), GLYPH(0x2F92A),
+ U24(0x3EB8), GLYPH(0x2F92C),
+ U24(0x3F1B), GLYPH(0x2F933),
+ U24(0x3FFC), GLYPH(0x2F93E),
+ U24(0x4008), GLYPH(0x2F93F),
+ U24(0x4018), GLYPH(0xFAD3),
+ U24(0x4039), GLYPH(0xFAD4),
+ U24(0x4046), GLYPH(0x2F94B),
+ U24(0x4096), GLYPH(0x2F94C),
+ U24(0x40E3), GLYPH(0x2F951),
+ U24(0x412F), GLYPH(0x2F958),
+ U24(0x4202), GLYPH(0x2F960),
+ U24(0x4227), GLYPH(0x2F964),
+ U24(0x42A0), GLYPH(0x2F967),
+ U24(0x4301), GLYPH(0x2F96D),
+ U24(0x4334), GLYPH(0x2F971),
+ U24(0x4359), GLYPH(0x2F974),
+ U24(0x43D5), GLYPH(0x2F981),
+ U24(0x43D9), GLYPH(0x2F8D7),
+ U24(0x440B), GLYPH(0x2F984),
+ U24(0x446B), GLYPH(0x2F98E),
+ U24(0x452B), GLYPH(0x2F9A7),
+ U24(0x455D), GLYPH(0x2F9AE),
+ U24(0x4561), GLYPH(0x2F9AF),
+ U24(0x456B), GLYPH(0x2F9B2),
+ U24(0x45D7), GLYPH(0x2F9BF),
+ U24(0x45F9), GLYPH(0x2F9C2),
+ U24(0x4635), GLYPH(0x2F9C8),
+ U24(0x46BE), GLYPH(0x2F9CD),
+ U24(0x46C7), GLYPH(0x2F9CE),
+ U24(0x4995), GLYPH(0x2F9EF),
+ U24(0x49E6), GLYPH(0x2F9F2),
+ U24(0x4A6E), GLYPH(0x2F9F8),
+ U24(0x4A76), GLYPH(0x2F9F9),
+ U24(0x4AB2), GLYPH(0x2F9FC),
+ U24(0x4B33), GLYPH(0x2FA03),
+ U24(0x4BCE), GLYPH(0x2FA08),
+ U24(0x4CCE), GLYPH(0x2FA0D),
+ U24(0x4CED), GLYPH(0x2FA0E),
+ U24(0x4CF8), GLYPH(0x2FA11),
+ U24(0x4D56), GLYPH(0x2FA16),
+ U24(0x4E0D), GLYPH(0xF967),
+ U24(0x4E26), GLYPH(0xFA70),
+ U24(0x4E32), GLYPH(0xF905),
+ U24(0x4E38), GLYPH(0x2F801),
+ U24(0x4E39), GLYPH(0xF95E),
+ U24(0x4E3D), GLYPH(0x2F800),
+ U24(0x4E41), GLYPH(0x2F802),
+ U24(0x4E82), GLYPH(0xF91B),
+ U24(0x4E86), GLYPH(0xF9BA),
+ U24(0x4EAE), GLYPH(0xF977),
+ U24(0x4EC0), GLYPH(0xF9FD),
+ U24(0x4ECC), GLYPH(0x2F819),
+ U24(0x4EE4), GLYPH(0xF9A8),
+ U24(0x4F60), GLYPH(0x2F804),
+ U24(0x4F80), GLYPH(0xFA73),
+ U24(0x4F86), GLYPH(0xF92D),
+ U24(0x4F8B), GLYPH(0xF9B5),
+ U24(0x4FAE), GLYPH(0xFA30),
+ U24(0x4FBB), GLYPH(0x2F806),
+ U24(0x4FBF), GLYPH(0xF965),
+ U24(0x5002), GLYPH(0x2F807),
+ U24(0x502B), GLYPH(0xF9D4),
+ U24(0x507A), GLYPH(0x2F808),
+ U24(0x5099), GLYPH(0x2F809),
+ U24(0x50CF), GLYPH(0x2F80B),
+ U24(0x50DA), GLYPH(0xF9BB),
+ U24(0x50E7), GLYPH(0xFA31),
+ U24(0x5140), GLYPH(0xFA0C),
+ U24(0x5145), GLYPH(0xFA74),
+ U24(0x514D), GLYPH(0xFA32),
+ U24(0x5154), GLYPH(0x2F80F),
+ U24(0x5164), GLYPH(0x2F810),
+ U24(0x5167), GLYPH(0x2F814),
+ U24(0x5168), GLYPH(0xFA72),
+ U24(0x5169), GLYPH(0xF978),
+ U24(0x516D), GLYPH(0xF9D1),
+ U24(0x5177), GLYPH(0x2F811),
+ U24(0x5180), GLYPH(0xFA75),
+ U24(0x518D), GLYPH(0x2F815),
+ U24(0x5192), GLYPH(0x2F8D2),
+ U24(0x5195), GLYPH(0x2F8D3),
+ U24(0x5197), GLYPH(0x2F817),
+ U24(0x51A4), GLYPH(0x2F818),
+ U24(0x51AC), GLYPH(0x2F81A),
+ U24(0x51B5), GLYPH(0xFA71),
+ U24(0x51B7), GLYPH(0xF92E),
+ U24(0x51C9), GLYPH(0xF979),
+ U24(0x51CC), GLYPH(0xF955),
+ U24(0x51DC), GLYPH(0xF954),
+ U24(0x51DE), GLYPH(0xFA15),
+ U24(0x51F5), GLYPH(0x2F81D),
+ U24(0x5203), GLYPH(0x2F81E),
+ U24(0x5207), GLYPH(0xFA00),
+ U24(0x5217), GLYPH(0xF99C),
+ U24(0x5229), GLYPH(0xF9DD),
+ U24(0x523A), GLYPH(0xF9FF),
+ U24(0x523B), GLYPH(0x2F820),
+ U24(0x5246), GLYPH(0x2F821),
+ U24(0x5272), GLYPH(0x2F822),
+ U24(0x5277), GLYPH(0x2F823),
+ U24(0x5289), GLYPH(0xF9C7),
+ U24(0x529B), GLYPH(0xF98A),
+ U24(0x52A3), GLYPH(0xF99D),
+ U24(0x52B3), GLYPH(0x2F992),
+ U24(0x52C7), GLYPH(0xFA76),
+ U24(0x52C9), GLYPH(0xFA33),
+ U24(0x52D2), GLYPH(0xF952),
+ U24(0x52DE), GLYPH(0xF92F),
+ U24(0x52E4), GLYPH(0xFA34),
+ U24(0x52F5), GLYPH(0xF97F),
+ U24(0x52FA), GLYPH(0xFA77),
+ U24(0x5305), GLYPH(0x2F829),
+ U24(0x5306), GLYPH(0x2F82A),
+ U24(0x5317), GLYPH(0xF963),
+ U24(0x533F), GLYPH(0xF9EB),
+ U24(0x5349), GLYPH(0x2F82C),
+ U24(0x5351), GLYPH(0xFA35),
+ U24(0x535A), GLYPH(0x2F82E),
+ U24(0x5373), GLYPH(0x2F82F),
+ U24(0x5375), GLYPH(0xF91C),
+ U24(0x537D), GLYPH(0x2F830),
+ U24(0x537F), GLYPH(0x2F831),
+ U24(0x53C3), GLYPH(0xF96B),
+ U24(0x53CA), GLYPH(0x2F836),
+ U24(0x53DF), GLYPH(0x2F837),
+ U24(0x53E5), GLYPH(0xF906),
+ U24(0x53EB), GLYPH(0x2F839),
+ U24(0x53F1), GLYPH(0x2F83A),
+ U24(0x5406), GLYPH(0x2F83B),
+ U24(0x540F), GLYPH(0xF9DE),
+ U24(0x541D), GLYPH(0xF9ED),
+ U24(0x5438), GLYPH(0x2F83D),
+ U24(0x5442), GLYPH(0xF980),
+ U24(0x5448), GLYPH(0x2F83E),
+ U24(0x5468), GLYPH(0x2F83F),
+ U24(0x549E), GLYPH(0x2F83C),
+ U24(0x54A2), GLYPH(0x2F840),
+ U24(0x54BD), GLYPH(0xF99E),
+ U24(0x54F6), GLYPH(0x2F841),
+ U24(0x5510), GLYPH(0x2F842),
+ U24(0x5553), GLYPH(0x2F843),
+ U24(0x5555), GLYPH(0xFA79),
+ U24(0x5563), GLYPH(0x2F844),
+ U24(0x5584), GLYPH(0x2F845),
+ U24(0x5587), GLYPH(0xF90B),
+ U24(0x5599), GLYPH(0xFA7A),
+ U24(0x559D), GLYPH(0xFA36),
+ U24(0x55AB), GLYPH(0x2F848),
+ U24(0x55B3), GLYPH(0x2F849),
+ U24(0x55C0), GLYPH(0xFA0D),
+ U24(0x55C2), GLYPH(0x2F84A),
+ U24(0x55E2), GLYPH(0xFA7B),
+ U24(0x5606), GLYPH(0xFA37),
+ U24(0x5651), GLYPH(0x2F84E),
+ U24(0x5668), GLYPH(0xFA38),
+ U24(0x5674), GLYPH(0x2F84F),
+ U24(0x56F9), GLYPH(0xF9A9),
+ U24(0x5716), GLYPH(0x2F84B),
+ U24(0x5717), GLYPH(0x2F84D),
+ U24(0x578B), GLYPH(0x2F855),
+ U24(0x57CE), GLYPH(0x2F852),
+ U24(0x57F4), GLYPH(0x2F853),
+ U24(0x580D), GLYPH(0x2F854),
+ U24(0x5831), GLYPH(0x2F857),
+ U24(0x5832), GLYPH(0x2F856),
+ U24(0x5840), GLYPH(0xFA39),
+ U24(0x585A), GLYPH(0xFA10),
+ U24(0x585E), GLYPH(0xF96C),
+ U24(0x58A8), GLYPH(0xFA3A),
+ U24(0x58AC), GLYPH(0x2F858),
+ U24(0x58B3), GLYPH(0xFA7D),
+ U24(0x58D8), GLYPH(0xF94A),
+ U24(0x58DF), GLYPH(0xF942),
+ U24(0x58EE), GLYPH(0x2F851),
+ U24(0x58F2), GLYPH(0x2F85A),
+ U24(0x58F7), GLYPH(0x2F85B),
+ U24(0x5906), GLYPH(0x2F85C),
+ U24(0x591A), GLYPH(0x2F85D),
+ U24(0x5922), GLYPH(0x2F85E),
+ U24(0x5944), GLYPH(0xFA7E),
+ U24(0x5948), GLYPH(0xF90C),
+ U24(0x5951), GLYPH(0xF909),
+ U24(0x5954), GLYPH(0xFA7F),
+ U24(0x5962), GLYPH(0x2F85F),
+ U24(0x5973), GLYPH(0xF981),
+ U24(0x59D8), GLYPH(0x2F865),
+ U24(0x59EC), GLYPH(0x2F862),
+ U24(0x5A1B), GLYPH(0x2F863),
+ U24(0x5A27), GLYPH(0x2F864),
+ U24(0x5A62), GLYPH(0xFA80),
+ U24(0x5A66), GLYPH(0x2F866),
+ U24(0x5AB5), GLYPH(0x2F986),
+ U24(0x5B08), GLYPH(0x2F869),
+ U24(0x5B28), GLYPH(0xFA81),
+ U24(0x5B3E), GLYPH(0x2F86A),
+ U24(0x5B85), GLYPH(0xFA04),
+ U24(0x5BC3), GLYPH(0x2F86D),
+ U24(0x5BD8), GLYPH(0x2F86E),
+ U24(0x5BE7), GLYPH(0xF95F),
+ U24(0x5BEE), GLYPH(0xF9BC),
+ U24(0x5BF3), GLYPH(0x2F870),
+ U24(0x5BFF), GLYPH(0x2F872),
+ U24(0x5C06), GLYPH(0x2F873),
+ U24(0x5C22), GLYPH(0x2F875),
+ U24(0x5C3F), GLYPH(0xF9BD),
+ U24(0x5C60), GLYPH(0x2F877),
+ U24(0x5C62), GLYPH(0xF94B),
+ U24(0x5C64), GLYPH(0xFA3B),
+ U24(0x5C65), GLYPH(0xF9DF),
+ U24(0x5C6E), GLYPH(0xFA3C),
+ U24(0x5C8D), GLYPH(0x2F87A),
+ U24(0x5CC0), GLYPH(0x2F879),
+ U24(0x5D19), GLYPH(0xF9D5),
+ U24(0x5D43), GLYPH(0x2F87C),
+ U24(0x5D50), GLYPH(0xF921),
+ U24(0x5D6B), GLYPH(0x2F87F),
+ U24(0x5D6E), GLYPH(0x2F87E),
+ U24(0x5D7C), GLYPH(0x2F880),
+ U24(0x5DB2), GLYPH(0x2F9F4),
+ U24(0x5DBA), GLYPH(0xF9AB),
+ U24(0x5DE1), GLYPH(0x2F881),
+ U24(0x5DE2), GLYPH(0x2F882),
+ U24(0x5DFD), GLYPH(0x2F884),
+ U24(0x5E28), GLYPH(0x2F885),
+ U24(0x5E3D), GLYPH(0x2F886),
+ U24(0x5E69), GLYPH(0x2F887),
+ U24(0x5E74), GLYPH(0xF98E),
+ U24(0x5EA6), GLYPH(0xFA01),
+ U24(0x5EB0), GLYPH(0x2F88B),
+ U24(0x5EB3), GLYPH(0x2F88C),
+ U24(0x5EB6), GLYPH(0x2F88D),
+ U24(0x5EC9), GLYPH(0xF9A2),
+ U24(0x5ECA), GLYPH(0xF928),
+ U24(0x5ED2), GLYPH(0xFA82),
+ U24(0x5ED3), GLYPH(0xFA0B),
+ U24(0x5ED9), GLYPH(0xFA83),
+ U24(0x5EEC), GLYPH(0xF982),
+ U24(0x5EFE), GLYPH(0x2F890),
+ U24(0x5F04), GLYPH(0xF943),
+ U24(0x5F22), GLYPH(0x2F894),
+ U24(0x5F53), GLYPH(0x2F874),
+ U24(0x5F62), GLYPH(0x2F899),
+ U24(0x5F69), GLYPH(0xFA84),
+ U24(0x5F6B), GLYPH(0x2F89A),
+ U24(0x5F8B), GLYPH(0xF9D8),
+ U24(0x5F9A), GLYPH(0x2F89C),
+ U24(0x5FA9), GLYPH(0xF966),
+ U24(0x5FAD), GLYPH(0xFA85),
+ U24(0x5FCD), GLYPH(0x2F89D),
+ U24(0x5FD7), GLYPH(0x2F89E),
+ U24(0x5FF5), GLYPH(0xF9A3),
+ U24(0x5FF9), GLYPH(0x2F89F),
+ U24(0x6012), GLYPH(0xF960),
+ U24(0x601C), GLYPH(0xF9AC),
+ U24(0x6075), GLYPH(0xFA6B),
+ U24(0x6081), GLYPH(0x2F8A0),
+ U24(0x6094), GLYPH(0xFA3D),
+ U24(0x60C7), GLYPH(0x2F8A5),
+ U24(0x60D8), GLYPH(0xFA86),
+ U24(0x60E1), GLYPH(0xF9B9),
+ U24(0x6108), GLYPH(0xFA88),
+ U24(0x6144), GLYPH(0xF9D9),
+ U24(0x6148), GLYPH(0x2F8A6),
+ U24(0x614C), GLYPH(0x2F8A7),
+ U24(0x614E), GLYPH(0xFA87),
+ U24(0x6160), GLYPH(0xFA8A),
+ U24(0x6168), GLYPH(0xFA3E),
+ U24(0x617A), GLYPH(0x2F8AA),
+ U24(0x618E), GLYPH(0xFA3F),
+ U24(0x6190), GLYPH(0xF98F),
+ U24(0x61A4), GLYPH(0x2F8AD),
+ U24(0x61AF), GLYPH(0x2F8AE),
+ U24(0x61B2), GLYPH(0x2F8AC),
+ U24(0x61DE), GLYPH(0x2F8AF),
+ U24(0x61F2), GLYPH(0xFA40),
+ U24(0x61F6), GLYPH(0xF90D),
+ U24(0x6200), GLYPH(0xF990),
+ U24(0x6210), GLYPH(0x2F8B2),
+ U24(0x621B), GLYPH(0x2F8B3),
+ U24(0x622E), GLYPH(0xF9D2),
+ U24(0x6234), GLYPH(0xFA8C),
+ U24(0x625D), GLYPH(0x2F8B4),
+ U24(0x62B1), GLYPH(0x2F8B5),
+ U24(0x62C9), GLYPH(0xF925),
+ U24(0x62CF), GLYPH(0xF95B),
+ U24(0x62D3), GLYPH(0xFA02),
+ U24(0x62D4), GLYPH(0x2F8B6),
+ U24(0x62FC), GLYPH(0x2F8BA),
+ U24(0x62FE), GLYPH(0xF973),
+ U24(0x633D), GLYPH(0x2F8B9),
+ U24(0x6350), GLYPH(0x2F8B7),
+ U24(0x6368), GLYPH(0x2F8BB),
+ U24(0x637B), GLYPH(0xF9A4),
+ U24(0x6383), GLYPH(0x2F8BC),
+ U24(0x63A0), GLYPH(0xF975),
+ U24(0x63A9), GLYPH(0x2F8C1),
+ U24(0x63C4), GLYPH(0xFA8D),
+ U24(0x63C5), GLYPH(0x2F8C0),
+ U24(0x63E4), GLYPH(0x2F8BD),
+ U24(0x641C), GLYPH(0xFA8E),
+ U24(0x6422), GLYPH(0x2F8BF),
+ U24(0x6452), GLYPH(0xFA8F),
+ U24(0x6469), GLYPH(0x2F8C3),
+ U24(0x6477), GLYPH(0x2F8C6),
+ U24(0x647E), GLYPH(0x2F8C4),
+ U24(0x649A), GLYPH(0xF991),
+ U24(0x649D), GLYPH(0x2F8C5),
+ U24(0x64C4), GLYPH(0xF930),
+ U24(0x654F), GLYPH(0xFA41),
+ U24(0x6556), GLYPH(0xFA90),
+ U24(0x656C), GLYPH(0x2F8C9),
+ U24(0x6578), GLYPH(0xF969),
+ U24(0x6599), GLYPH(0xF9BE),
+ U24(0x65C5), GLYPH(0xF983),
+ U24(0x65E2), GLYPH(0xFA42),
+ U24(0x65E3), GLYPH(0x2F8CB),
+ U24(0x6613), GLYPH(0xF9E0),
+ U24(0x6649), GLYPH(0x2F8CD),
+ U24(0x6674), GLYPH(0xFA12),
+ U24(0x6688), GLYPH(0xF9C5),
+ U24(0x6691), GLYPH(0xFA43),
+ U24(0x669C), GLYPH(0x2F8D5),
+ U24(0x66B4), GLYPH(0xFA06),
+ U24(0x66C6), GLYPH(0xF98B),
+ U24(0x66F4), GLYPH(0xF901),
+ U24(0x66F8), GLYPH(0x2F8CC),
+ U24(0x6700), GLYPH(0x2F8D4),
+ U24(0x6717), GLYPH(0xF929),
+ U24(0x671B), GLYPH(0xFA93),
+ U24(0x6721), GLYPH(0x2F8DA),
+ U24(0x674E), GLYPH(0xF9E1),
+ U24(0x6753), GLYPH(0x2F8DC),
+ U24(0x6756), GLYPH(0xFA94),
+ U24(0x675E), GLYPH(0x2F8DB),
+ U24(0x677B), GLYPH(0xF9C8),
+ U24(0x6785), GLYPH(0x2F8E0),
+ U24(0x6797), GLYPH(0xF9F4),
+ U24(0x67F3), GLYPH(0xF9C9),
+ U24(0x67FA), GLYPH(0x2F8DF),
+ U24(0x6817), GLYPH(0xF9DA),
+ U24(0x681F), GLYPH(0x2F8E5),
+ U24(0x6852), GLYPH(0x2F8E1),
+ U24(0x6881), GLYPH(0xF97A),
+ U24(0x6885), GLYPH(0xFA44),
+ U24(0x688E), GLYPH(0x2F8E4),
+ U24(0x68A8), GLYPH(0xF9E2),
+ U24(0x6914), GLYPH(0x2F8E6),
+ U24(0x6942), GLYPH(0x2F8E8),
+ U24(0x69A3), GLYPH(0x2F8E9),
+ U24(0x69EA), GLYPH(0x2F8EA),
+ U24(0x6A02), GLYPH(0xF914),
+ U24(0x6A13), GLYPH(0xF94C),
+ U24(0x6AA8), GLYPH(0x2F8EB),
+ U24(0x6AD3), GLYPH(0xF931),
+ U24(0x6ADB), GLYPH(0x2F8ED),
+ U24(0x6B04), GLYPH(0xF91D),
+ U24(0x6B21), GLYPH(0x2F8EF),
+ U24(0x6B54), GLYPH(0x2F8F1),
+ U24(0x6B72), GLYPH(0x2F8F3),
+ U24(0x6B77), GLYPH(0xF98C),
+ U24(0x6B79), GLYPH(0xFA95),
+ U24(0x6B9F), GLYPH(0x2F8F4),
+ U24(0x6BAE), GLYPH(0xF9A5),
+ U24(0x6BBA), GLYPH(0xF970),
+ U24(0x6BBB), GLYPH(0x2F8F6),
+ U24(0x6C4E), GLYPH(0x2F8FA),
+ U24(0x6C67), GLYPH(0x2F8FE),
+ U24(0x6C88), GLYPH(0xF972),
+ U24(0x6CBF), GLYPH(0x2F8FC),
+ U24(0x6CCC), GLYPH(0xF968),
+ U24(0x6CCD), GLYPH(0x2F8FD),
+ U24(0x6CE5), GLYPH(0xF9E3),
+ U24(0x6D16), GLYPH(0x2F8FF),
+ U24(0x6D1B), GLYPH(0xF915),
+ U24(0x6D1E), GLYPH(0xFA05),
+ U24(0x6D34), GLYPH(0x2F907),
+ U24(0x6D3E), GLYPH(0x2F900),
+ U24(0x6D41), GLYPH(0xF9CA),
+ U24(0x6D69), GLYPH(0x2F903),
+ U24(0x6D6A), GLYPH(0xF92A),
+ U24(0x6D77), GLYPH(0xFA45),
+ U24(0x6D78), GLYPH(0x2F904),
+ U24(0x6D85), GLYPH(0x2F905),
+ U24(0x6DCB), GLYPH(0xF9F5),
+ U24(0x6DDA), GLYPH(0xF94D),
+ U24(0x6DEA), GLYPH(0xF9D6),
+ U24(0x6DF9), GLYPH(0x2F90E),
+ U24(0x6E1A), GLYPH(0xFA46),
+ U24(0x6E2F), GLYPH(0x2F908),
+ U24(0x6E6E), GLYPH(0x2F909),
+ U24(0x6E9C), GLYPH(0xF9CB),
+ U24(0x6EBA), GLYPH(0xF9EC),
+ U24(0x6EC7), GLYPH(0x2F90C),
+ U24(0x6ECB), GLYPH(0xFA99),
+ U24(0x6ED1), GLYPH(0xF904),
+ U24(0x6EDB), GLYPH(0xFA98),
+ U24(0x6F0F), GLYPH(0xF94E),
+ U24(0x6F22), GLYPH(0xFA47),
+ U24(0x6F23), GLYPH(0xF992),
+ U24(0x6F6E), GLYPH(0x2F90F),
+ U24(0x6FC6), GLYPH(0x2F912),
+ U24(0x6FEB), GLYPH(0xF922),
+ U24(0x6FFE), GLYPH(0xF984),
+ U24(0x701B), GLYPH(0x2F915),
+ U24(0x701E), GLYPH(0xFA9B),
+ U24(0x7039), GLYPH(0x2F913),
+ U24(0x704A), GLYPH(0x2F917),
+ U24(0x7070), GLYPH(0x2F835),
+ U24(0x7077), GLYPH(0x2F919),
+ U24(0x707D), GLYPH(0x2F918),
+ U24(0x7099), GLYPH(0xF9FB),
+ U24(0x70AD), GLYPH(0x2F91A),
+ U24(0x70C8), GLYPH(0xF99F),
+ U24(0x70D9), GLYPH(0xF916),
+ U24(0x7145), GLYPH(0x2F91C),
+ U24(0x7149), GLYPH(0xF993),
+ U24(0x716E), GLYPH(0xFA48),
+ U24(0x719C), GLYPH(0x2F91E),
+ U24(0x71CE), GLYPH(0xF9C0),
+ U24(0x71D0), GLYPH(0xF9EE),
+ U24(0x7210), GLYPH(0xF932),
+ U24(0x721B), GLYPH(0xF91E),
+ U24(0x7228), GLYPH(0x2F920),
+ U24(0x722B), GLYPH(0xFA49),
+ U24(0x7235), GLYPH(0xFA9E),
+ U24(0x7250), GLYPH(0x2F922),
+ U24(0x7262), GLYPH(0xF946),
+ U24(0x7280), GLYPH(0x2F924),
+ U24(0x7295), GLYPH(0x2F925),
+ U24(0x72AF), GLYPH(0xFA9F),
+ U24(0x72C0), GLYPH(0xF9FA),
+ U24(0x72FC), GLYPH(0xF92B),
+ U24(0x732A), GLYPH(0xFA16),
+ U24(0x7375), GLYPH(0xF9A7),
+ U24(0x737A), GLYPH(0x2F928),
+ U24(0x7387), GLYPH(0xF961),
+ U24(0x738B), GLYPH(0x2F929),
+ U24(0x73A5), GLYPH(0x2F92B),
+ U24(0x73B2), GLYPH(0xF9AD),
+ U24(0x73DE), GLYPH(0xF917),
+ U24(0x7406), GLYPH(0xF9E4),
+ U24(0x7409), GLYPH(0xF9CC),
+ U24(0x7422), GLYPH(0xFA4A),
+ U24(0x7447), GLYPH(0x2F92E),
+ U24(0x745C), GLYPH(0x2F92F),
+ U24(0x7469), GLYPH(0xF9AE),
+ U24(0x7471), GLYPH(0xFAA1),
+ U24(0x7485), GLYPH(0x2F931),
+ U24(0x7489), GLYPH(0xF994),
+ U24(0x7498), GLYPH(0xF9EF),
+ U24(0x74CA), GLYPH(0x2F932),
+ U24(0x7506), GLYPH(0xFAA2),
+ U24(0x7524), GLYPH(0x2F934),
+ U24(0x753B), GLYPH(0xFAA3),
+ U24(0x753E), GLYPH(0x2F936),
+ U24(0x7559), GLYPH(0xF9CD),
+ U24(0x7565), GLYPH(0xF976),
+ U24(0x7570), GLYPH(0xF962),
+ U24(0x75E2), GLYPH(0xF9E5),
+ U24(0x7610), GLYPH(0x2F93A),
+ U24(0x761D), GLYPH(0xFAA4),
+ U24(0x761F), GLYPH(0xFAA5),
+ U24(0x7642), GLYPH(0xF9C1),
+ U24(0x7669), GLYPH(0xF90E),
+ U24(0x76CA), GLYPH(0xFA17),
+ U24(0x76DB), GLYPH(0xFAA7),
+ U24(0x76E7), GLYPH(0xF933),
+ U24(0x76F4), GLYPH(0xFAA8),
+ U24(0x7701), GLYPH(0xF96D),
+ U24(0x771E), GLYPH(0x2F945),
+ U24(0x771F), GLYPH(0x2F946),
+ U24(0x7740), GLYPH(0xFAAA),
+ U24(0x774A), GLYPH(0xFAA9),
+ U24(0x778B), GLYPH(0x2F94A),
+ U24(0x77A7), GLYPH(0xFA9D),
+ U24(0x784E), GLYPH(0x2F94E),
+ U24(0x786B), GLYPH(0xF9CE),
+ U24(0x788C), GLYPH(0xF93B),
+ U24(0x7891), GLYPH(0xFA4B),
+ U24(0x78CA), GLYPH(0xF947),
+ U24(0x78CC), GLYPH(0xFAAB),
+ U24(0x78FB), GLYPH(0xF964),
+ U24(0x792A), GLYPH(0xF985),
+ U24(0x793C), GLYPH(0xFA18),
+ U24(0x793E), GLYPH(0xFA4C),
+ U24(0x7948), GLYPH(0xFA4E),
+ U24(0x7949), GLYPH(0xFA4D),
+ U24(0x7950), GLYPH(0xFA4F),
+ U24(0x7956), GLYPH(0xFA50),
+ U24(0x795D), GLYPH(0xFA51),
+ U24(0x795E), GLYPH(0xFA19),
+ U24(0x7965), GLYPH(0xFA1A),
+ U24(0x797F), GLYPH(0xF93C),
+ U24(0x798D), GLYPH(0xFA52),
+ U24(0x798E), GLYPH(0xFA53),
+ U24(0x798F), GLYPH(0xFA1B),
+ U24(0x79AE), GLYPH(0xF9B6),
+ U24(0x79CA), GLYPH(0xF995),
+ U24(0x79EB), GLYPH(0x2F957),
+ U24(0x7A1C), GLYPH(0xF956),
+ U24(0x7A40), GLYPH(0xFA54),
+ U24(0x7A4A), GLYPH(0x2F95A),
+ U24(0x7A4F), GLYPH(0x2F95B),
+ U24(0x7A81), GLYPH(0xFA55),
+ U24(0x7AB1), GLYPH(0xFAAC),
+ U24(0x7ACB), GLYPH(0xF9F7),
+ U24(0x7AEE), GLYPH(0x2F95F),
+ U24(0x7B20), GLYPH(0xF9F8),
+ U24(0x7BC0), GLYPH(0xFA56),
+ U24(0x7BC6), GLYPH(0x2F962),
+ U24(0x7BC9), GLYPH(0x2F963),
+ U24(0x7C3E), GLYPH(0xF9A6),
+ U24(0x7C60), GLYPH(0xF944),
+ U24(0x7C7B), GLYPH(0xFAAE),
+ U24(0x7C92), GLYPH(0xF9F9),
+ U24(0x7CBE), GLYPH(0xFA1D),
+ U24(0x7CD2), GLYPH(0x2F966),
+ U24(0x7CD6), GLYPH(0xFA03),
+ U24(0x7CE3), GLYPH(0x2F969),
+ U24(0x7CE7), GLYPH(0xF97B),
+ U24(0x7CE8), GLYPH(0x2F968),
+ U24(0x7D00), GLYPH(0x2F96A),
+ U24(0x7D10), GLYPH(0xF9CF),
+ U24(0x7D22), GLYPH(0xF96A),
+ U24(0x7D2F), GLYPH(0xF94F),
+ U24(0x7D5B), GLYPH(0xFAAF),
+ U24(0x7D63), GLYPH(0x2F96C),
+ U24(0x7DA0), GLYPH(0xF93D),
+ U24(0x7DBE), GLYPH(0xF957),
+ U24(0x7DC7), GLYPH(0x2F96E),
+ U24(0x7DF4), GLYPH(0xF996),
+ U24(0x7E02), GLYPH(0x2F96F),
+ U24(0x7E09), GLYPH(0xFA58),
+ U24(0x7E37), GLYPH(0xF950),
+ U24(0x7E41), GLYPH(0xFA59),
+ U24(0x7E45), GLYPH(0x2F970),
+ U24(0x7F3E), GLYPH(0xFAB1),
+ U24(0x7F72), GLYPH(0xFA5A),
+ U24(0x7F79), GLYPH(0xF9E6),
+ U24(0x7F7A), GLYPH(0x2F976),
+ U24(0x7F85), GLYPH(0xF90F),
+ U24(0x7F95), GLYPH(0x2F978),
+ U24(0x7F9A), GLYPH(0xF9AF),
+ U24(0x7FBD), GLYPH(0xFA1E),
+ U24(0x7FFA), GLYPH(0x2F979),
+ U24(0x8001), GLYPH(0xF934),
+ U24(0x8005), GLYPH(0xFA5B),
+ U24(0x8046), GLYPH(0xF9B0),
+ U24(0x8060), GLYPH(0x2F97D),
+ U24(0x806F), GLYPH(0xF997),
+ U24(0x8070), GLYPH(0x2F97F),
+ U24(0x807E), GLYPH(0xF945),
+ U24(0x808B), GLYPH(0xF953),
+ U24(0x80AD), GLYPH(0x2F8D6),
+ U24(0x80B2), GLYPH(0x2F982),
+ U24(0x8103), GLYPH(0x2F983),
+ U24(0x813E), GLYPH(0x2F985),
+ U24(0x81D8), GLYPH(0xF926),
+ U24(0x81E8), GLYPH(0xF9F6),
+ U24(0x81ED), GLYPH(0xFA5C),
+ U24(0x8201), GLYPH(0x2F893),
+ U24(0x8204), GLYPH(0x2F98C),
+ U24(0x8218), GLYPH(0xFA6D),
+ U24(0x826F), GLYPH(0xF97C),
+ U24(0x8279), GLYPH(0xFA5D),
+ U24(0x828B), GLYPH(0x2F990),
+ U24(0x8291), GLYPH(0x2F98F),
+ U24(0x829D), GLYPH(0x2F991),
+ U24(0x82B1), GLYPH(0x2F993),
+ U24(0x82B3), GLYPH(0x2F994),
+ U24(0x82BD), GLYPH(0x2F995),
+ U24(0x82E5), GLYPH(0xF974),
+ U24(0x82E6), GLYPH(0x2F996),
+ U24(0x831D), GLYPH(0x2F999),
+ U24(0x8323), GLYPH(0x2F99C),
+ U24(0x8336), GLYPH(0xF9FE),
+ U24(0x8352), GLYPH(0xFAB3),
+ U24(0x8353), GLYPH(0x2F9A0),
+ U24(0x8363), GLYPH(0x2F99A),
+ U24(0x83AD), GLYPH(0x2F99B),
+ U24(0x83BD), GLYPH(0x2F99D),
+ U24(0x83C9), GLYPH(0xF93E),
+ U24(0x83CA), GLYPH(0x2F9A1),
+ U24(0x83CC), GLYPH(0x2F9A2),
+ U24(0x83DC), GLYPH(0x2F9A3),
+ U24(0x83E7), GLYPH(0x2F99E),
+ U24(0x83EF), GLYPH(0xFAB4),
+ U24(0x83F1), GLYPH(0xF958),
+ U24(0x843D), GLYPH(0xF918),
+ U24(0x8449), GLYPH(0xF96E),
+ U24(0x8457), GLYPH(0xFA5F),
+ U24(0x84EE), GLYPH(0xF999),
+ U24(0x84F1), GLYPH(0x2F9A8),
+ U24(0x84F3), GLYPH(0x2F9A9),
+ U24(0x84FC), GLYPH(0xF9C2),
+ U24(0x8516), GLYPH(0x2F9AA),
+ U24(0x8564), GLYPH(0x2F9AC),
+ U24(0x85CD), GLYPH(0xF923),
+ U24(0x85FA), GLYPH(0xF9F0),
+ U24(0x8606), GLYPH(0xF935),
+ U24(0x8612), GLYPH(0xFA20),
+ U24(0x862D), GLYPH(0xF91F),
+ U24(0x863F), GLYPH(0xF910),
+ U24(0x8650), GLYPH(0x2F9B3),
+ U24(0x865C), GLYPH(0xF936),
+ U24(0x8667), GLYPH(0x2F9B5),
+ U24(0x8669), GLYPH(0x2F9B6),
+ U24(0x8688), GLYPH(0x2F9B8),
+ U24(0x86A9), GLYPH(0x2F9B7),
+ U24(0x86E2), GLYPH(0x2F9BA),
+ U24(0x870E), GLYPH(0x2F9B9),
+ U24(0x8728), GLYPH(0x2F9BC),
+ U24(0x876B), GLYPH(0x2F9BD),
+ U24(0x8779), GLYPH(0xFAB5),
+ U24(0x8786), GLYPH(0x2F9BE),
+ U24(0x87BA), GLYPH(0xF911),
+ U24(0x87E1), GLYPH(0x2F9C0),
+ U24(0x8801), GLYPH(0x2F9C1),
+ U24(0x881F), GLYPH(0xF927),
+ U24(0x884C), GLYPH(0xFA08),
+ U24(0x8860), GLYPH(0x2F9C3),
+ U24(0x8863), GLYPH(0x2F9C4),
+ U24(0x88C2), GLYPH(0xF9A0),
+ U24(0x88CF), GLYPH(0xF9E7),
+ U24(0x88D7), GLYPH(0x2F9C6),
+ U24(0x88DE), GLYPH(0x2F9C7),
+ U24(0x88E1), GLYPH(0xF9E8),
+ U24(0x88F8), GLYPH(0xF912),
+ U24(0x88FA), GLYPH(0x2F9C9),
+ U24(0x8910), GLYPH(0xFA60),
+ U24(0x8941), GLYPH(0xFAB6),
+ U24(0x8964), GLYPH(0xF924),
+ U24(0x8986), GLYPH(0xFAB7),
+ U24(0x898B), GLYPH(0xFA0A),
+ U24(0x8996), GLYPH(0xFA61),
+ U24(0x8AA0), GLYPH(0x2F9CF),
+ U24(0x8AAA), GLYPH(0xF96F),
+ U24(0x8ABF), GLYPH(0xFAB9),
+ U24(0x8ACB), GLYPH(0xFABB),
+ U24(0x8AD2), GLYPH(0xF97D),
+ U24(0x8AD6), GLYPH(0xF941),
+ U24(0x8AED), GLYPH(0xFABE),
+ U24(0x8AF8), GLYPH(0xFA22),
+ U24(0x8AFE), GLYPH(0xF95D),
+ U24(0x8B01), GLYPH(0xFA62),
+ U24(0x8B39), GLYPH(0xFA63),
+ U24(0x8B58), GLYPH(0xF9FC),
+ U24(0x8B80), GLYPH(0xF95A),
+ U24(0x8B8A), GLYPH(0xFAC0),
+ U24(0x8C48), GLYPH(0xF900),
+ U24(0x8C55), GLYPH(0x2F9D2),
+ U24(0x8CAB), GLYPH(0x2F9D4),
+ U24(0x8CC1), GLYPH(0x2F9D5),
+ U24(0x8CC2), GLYPH(0xF948),
+ U24(0x8CC8), GLYPH(0xF903),
+ U24(0x8CD3), GLYPH(0xFA64),
+ U24(0x8D08), GLYPH(0xFA65),
+ U24(0x8D1B), GLYPH(0x2F9D6),
+ U24(0x8D77), GLYPH(0x2F9D7),
+ U24(0x8DBC), GLYPH(0x2F9DB),
+ U24(0x8DCB), GLYPH(0x2F9DA),
+ U24(0x8DEF), GLYPH(0xF937),
+ U24(0x8DF0), GLYPH(0x2F9DC),
+ U24(0x8ECA), GLYPH(0xF902),
+ U24(0x8ED4), GLYPH(0x2F9DE),
+ U24(0x8F26), GLYPH(0xF998),
+ U24(0x8F2A), GLYPH(0xF9D7),
+ U24(0x8F38), GLYPH(0xFAC2),
+ U24(0x8F3B), GLYPH(0xFA07),
+ U24(0x8F62), GLYPH(0xF98D),
+ U24(0x8F9E), GLYPH(0x2F98D),
+ U24(0x8FB0), GLYPH(0xF971),
+ U24(0x8FB6), GLYPH(0xFA66),
+ U24(0x9023), GLYPH(0xF99A),
+ U24(0x9038), GLYPH(0xFA25),
+ U24(0x9072), GLYPH(0xFAC3),
+ U24(0x907C), GLYPH(0xF9C3),
+ U24(0x908F), GLYPH(0xF913),
+ U24(0x9094), GLYPH(0x2F9E2),
+ U24(0x90CE), GLYPH(0xF92C),
+ U24(0x90DE), GLYPH(0xFA2E),
+ U24(0x90F1), GLYPH(0x2F9E3),
+ U24(0x90FD), GLYPH(0xFA26),
+ U24(0x9111), GLYPH(0x2F9E4),
+ U24(0x911B), GLYPH(0x2F9E6),
+ U24(0x916A), GLYPH(0xF919),
+ U24(0x9199), GLYPH(0xFAC4),
+ U24(0x91B4), GLYPH(0xF9B7),
+ U24(0x91CC), GLYPH(0xF9E9),
+ U24(0x91CF), GLYPH(0xF97E),
+ U24(0x91D1), GLYPH(0xF90A),
+ U24(0x9234), GLYPH(0xF9B1),
+ U24(0x9238), GLYPH(0x2F9E7),
+ U24(0x9276), GLYPH(0xFAC5),
+ U24(0x927C), GLYPH(0x2F9EA),
+ U24(0x92D7), GLYPH(0x2F9E8),
+ U24(0x92D8), GLYPH(0x2F9E9),
+ U24(0x9304), GLYPH(0xF93F),
+ U24(0x934A), GLYPH(0xF99B),
+ U24(0x93F9), GLYPH(0x2F9EB),
+ U24(0x9415), GLYPH(0x2F9EC),
+ U24(0x958B), GLYPH(0x2F9EE),
+ U24(0x95AD), GLYPH(0xF986),
+ U24(0x95B7), GLYPH(0x2F9F0),
+ U24(0x962E), GLYPH(0xF9C6),
+ U24(0x964B), GLYPH(0xF951),
+ U24(0x964D), GLYPH(0xFA09),
+ U24(0x9675), GLYPH(0xF959),
+ U24(0x9678), GLYPH(0xF9D3),
+ U24(0x967C), GLYPH(0xFAC6),
+ U24(0x9686), GLYPH(0xF9DC),
+ U24(0x96A3), GLYPH(0xF9F1),
+ U24(0x96B7), GLYPH(0xFA2F),
+ U24(0x96B8), GLYPH(0xF9B8),
+ U24(0x96C3), GLYPH(0x2F9F3),
+ U24(0x96E2), GLYPH(0xF9EA),
+ U24(0x96E3), GLYPH(0xFA68),
+ U24(0x96F6), GLYPH(0xF9B2),
+ U24(0x96F7), GLYPH(0xF949),
+ U24(0x9723), GLYPH(0x2F9F5),
+ U24(0x9732), GLYPH(0xF938),
+ U24(0x9748), GLYPH(0xF9B3),
+ U24(0x9756), GLYPH(0xFA1C),
+ U24(0x97DB), GLYPH(0xFAC9),
+ U24(0x97E0), GLYPH(0x2F9FA),
+ U24(0x97FF), GLYPH(0xFA69),
+ U24(0x980B), GLYPH(0xFACB),
+ U24(0x9818), GLYPH(0xF9B4),
+ U24(0x9829), GLYPH(0x2FA00),
+ U24(0x983B), GLYPH(0xFA6A),
+ U24(0x985E), GLYPH(0xF9D0),
+ U24(0x98E2), GLYPH(0x2FA02),
+ U24(0x98EF), GLYPH(0xFA2A),
+ U24(0x98FC), GLYPH(0xFA2B),
+ U24(0x9928), GLYPH(0xFA2C),
+ U24(0x9929), GLYPH(0x2FA04),
+ U24(0x99A7), GLYPH(0x2FA05),
+ U24(0x99C2), GLYPH(0x2FA06),
+ U24(0x99F1), GLYPH(0xF91A),
+ U24(0x99FE), GLYPH(0x2FA07),
+ U24(0x9A6A), GLYPH(0xF987),
+ U24(0x9B12), GLYPH(0xFACD),
+ U24(0x9B6F), GLYPH(0xF939),
+ U24(0x9C40), GLYPH(0x2FA0B),
+ U24(0x9C57), GLYPH(0xF9F2),
+ U24(0x9CFD), GLYPH(0x2FA0C),
+ U24(0x9D67), GLYPH(0x2FA0F),
+ U24(0x9DB4), GLYPH(0xFA2D),
+ U24(0x9DFA), GLYPH(0xF93A),
+ U24(0x9E1E), GLYPH(0xF920),
+ U24(0x9E7F), GLYPH(0xF940),
+ U24(0x9E97), GLYPH(0xF988),
+ U24(0x9E9F), GLYPH(0xF9F3),
+ U24(0x9EBB), GLYPH(0x2FA15),
+ U24(0x9ECE), GLYPH(0xF989),
+ U24(0x9EF9), GLYPH(0x2FA17),
+ U24(0x9EFE), GLYPH(0x2FA18),
+ U24(0x9F05), GLYPH(0x2FA19),
+ U24(0x9F0F), GLYPH(0x2FA1A),
+ U24(0x9F16), GLYPH(0x2FA1B),
+ U24(0x9F3B), GLYPH(0x2FA1C),
+ U24(0x9F43), GLYPH(0xFAD8),
+ U24(0x9F8D), GLYPH(0xF9C4),
+ U24(0x9F8E), GLYPH(0xFAD9),
+ U24(0x9F9C), GLYPH(0xF907),
+ U24(0x20122), GLYPH(0x2F803),
+ U24(0x2051C), GLYPH(0x2F812),
+ U24(0x20525), GLYPH(0x2F91B),
+ U24(0x2054B), GLYPH(0x2F816),
+ U24(0x2063A), GLYPH(0x2F80D),
+ U24(0x20804), GLYPH(0x2F9D9),
+ U24(0x208DE), GLYPH(0x2F9DD),
+ U24(0x20A2C), GLYPH(0x2F834),
+ U24(0x20B63), GLYPH(0x2F838),
+ U24(0x214E4), GLYPH(0x2F859),
+ U24(0x216A8), GLYPH(0x2F860),
+ U24(0x216EA), GLYPH(0x2F861),
+ U24(0x219C8), GLYPH(0x2F86C),
+ U24(0x21B18), GLYPH(0x2F871),
+ U24(0x21D0B), GLYPH(0x2F8F8),
+ U24(0x21DE4), GLYPH(0x2F87B),
+ U24(0x21DE6), GLYPH(0x2F87D),
+ U24(0x22183), GLYPH(0x2F889),
+ U24(0x2219F), GLYPH(0x2F939),
+ U24(0x22331), GLYPH(0x2F891),
+ U24(0x226D4), GLYPH(0x2F8A4),
+ U24(0x22844), GLYPH(0xFAD0),
+ U24(0x2284A), GLYPH(0xFACF),
+ U24(0x22B0C), GLYPH(0x2F8B8),
+ U24(0x22BF1), GLYPH(0x2F8BE),
+ U24(0x2300A), GLYPH(0x2F8CA),
+ U24(0x232B8), GLYPH(0x2F897),
+ U24(0x2335F), GLYPH(0x2F980),
+ U24(0x23393), GLYPH(0x2F989),
+ U24(0x2339C), GLYPH(0x2F98A),
+ U24(0x233C3), GLYPH(0x2F8DD),
+ U24(0x233D5), GLYPH(0xFAD1),
+ U24(0x2346D), GLYPH(0x2F8E3),
+ U24(0x236A3), GLYPH(0x2F8EC),
+ U24(0x238A7), GLYPH(0x2F8F0),
+ U24(0x23A8D), GLYPH(0x2F8F7),
+ U24(0x23AFA), GLYPH(0x2F8F9),
+ U24(0x23CBC), GLYPH(0x2F8FB),
+ U24(0x23D1E), GLYPH(0x2F906),
+ U24(0x23ED1), GLYPH(0x2F90D),
+ U24(0x23F5E), GLYPH(0x2F910),
+ U24(0x23F8E), GLYPH(0x2F911),
+ U24(0x24263), GLYPH(0x2F91D),
+ U24(0x242EE), GLYPH(0xFA6C),
+ U24(0x243AB), GLYPH(0x2F91F),
+ U24(0x24608), GLYPH(0x2F923),
+ U24(0x24735), GLYPH(0x2F926),
+ U24(0x24814), GLYPH(0x2F927),
+ U24(0x24C36), GLYPH(0x2F935),
+ U24(0x24C92), GLYPH(0x2F937),
+ U24(0x24FA1), GLYPH(0x2F93B),
+ U24(0x24FB8), GLYPH(0x2F93C),
+ U24(0x25044), GLYPH(0x2F93D),
+ U24(0x250F2), GLYPH(0x2F942),
+ U24(0x250F3), GLYPH(0x2F941),
+ U24(0x25119), GLYPH(0x2F943),
+ U24(0x25133), GLYPH(0x2F944),
+ U24(0x25249), GLYPH(0xFAD5),
+ U24(0x2541D), GLYPH(0x2F94D),
+ U24(0x25626), GLYPH(0x2F952),
+ U24(0x2569A), GLYPH(0x2F954),
+ U24(0x256C5), GLYPH(0x2F955),
+ U24(0x2597C), GLYPH(0x2F95C),
+ U24(0x25AA7), GLYPH(0x2F95D),
+ U24(0x25BAB), GLYPH(0x2F961),
+ U24(0x25C80), GLYPH(0x2F965),
+ U24(0x25CD0), GLYPH(0xFAD6),
+ U24(0x25F86), GLYPH(0x2F96B),
+ U24(0x261DA), GLYPH(0x2F898),
+ U24(0x26228), GLYPH(0x2F972),
+ U24(0x26247), GLYPH(0x2F973),
+ U24(0x262D9), GLYPH(0x2F975),
+ U24(0x2633E), GLYPH(0x2F977),
+ U24(0x264DA), GLYPH(0x2F97B),
+ U24(0x26523), GLYPH(0x2F97C),
+ U24(0x265A8), GLYPH(0x2F97E),
+ U24(0x267A7), GLYPH(0x2F987),
+ U24(0x267B5), GLYPH(0x2F988),
+ U24(0x26B3C), GLYPH(0x2F997),
+ U24(0x26C36), GLYPH(0x2F9A4),
+ U24(0x26CD5), GLYPH(0x2F9A6),
+ U24(0x26D6B), GLYPH(0x2F9A5),
+ U24(0x26F2C), GLYPH(0x2F9AD),
+ U24(0x26FB1), GLYPH(0x2F9B0),
+ U24(0x270D2), GLYPH(0x2F9B1),
+ U24(0x273CA), GLYPH(0x2F9AB),
+ U24(0x27667), GLYPH(0x2F9C5),
+ U24(0x278AE), GLYPH(0x2F9CB),
+ U24(0x27966), GLYPH(0x2F9CC),
+ U24(0x27CA8), GLYPH(0x2F9D3),
+ U24(0x27ED3), GLYPH(0xFAD7),
+ U24(0x27F2F), GLYPH(0x2F9D8),
+ U24(0x285D2), GLYPH(0x2F9E0),
+ U24(0x285ED), GLYPH(0x2F9E1),
+ U24(0x2872E), GLYPH(0x2F9E5),
+ U24(0x28BFA), GLYPH(0x2F9ED),
+ U24(0x28D77), GLYPH(0x2F9F1),
+ U24(0x29145), GLYPH(0x2F9F6),
+ U24(0x291DF), GLYPH(0x2F81C),
+ U24(0x2921A), GLYPH(0x2F9F7),
+ U24(0x2940A), GLYPH(0x2F9FB),
+ U24(0x29496), GLYPH(0x2F9FD),
+ U24(0x295B6), GLYPH(0x2FA01),
+ U24(0x29B30), GLYPH(0x2FA09),
+ U24(0x2A0CE), GLYPH(0x2FA10),
+ U24(0x2A105), GLYPH(0x2FA12),
+ U24(0x2A20E), GLYPH(0x2FA13),
+ U24(0x2A291), GLYPH(0x2FA14),
+ U24(0x2A392), GLYPH(0x2F88F),
+ U24(0x2A600), GLYPH(0x2FA1D),
+ // 0xFE01
+ U32(88), // numUVSMappings
+ U24(0x3B9D), GLYPH(0x2F8E7),
+ U24(0x3EB8), GLYPH(0x2F92D),
+ U24(0x4039), GLYPH(0x2F949),
+ U24(0x4FAE), GLYPH(0x2F805),
+ U24(0x50E7), GLYPH(0x2F80A),
+ U24(0x514D), GLYPH(0x2F80E),
+ U24(0x51B5), GLYPH(0x2F81B),
+ U24(0x5207), GLYPH(0x2F850),
+ U24(0x52C7), GLYPH(0x2F825),
+ U24(0x52C9), GLYPH(0x2F826),
+ U24(0x52E4), GLYPH(0x2F827),
+ U24(0x52FA), GLYPH(0x2F828),
+ U24(0x5317), GLYPH(0x2F82B),
+ U24(0x5351), GLYPH(0x2F82D),
+ U24(0x537F), GLYPH(0x2F832),
+ U24(0x5584), GLYPH(0x2F846),
+ U24(0x5599), GLYPH(0x2F847),
+ U24(0x559D), GLYPH(0xFA78),
+ U24(0x5606), GLYPH(0x2F84C),
+ U24(0x585A), GLYPH(0xFA7C),
+ U24(0x5B3E), GLYPH(0x2F86B),
+ U24(0x5BE7), GLYPH(0xF9AA),
+ U24(0x5C6E), GLYPH(0x2F878),
+ U24(0x5ECA), GLYPH(0x2F88E),
+ U24(0x5F22), GLYPH(0x2F895),
+ U24(0x6094), GLYPH(0x2F8A3),
+ U24(0x614C), GLYPH(0x2F8A9),
+ U24(0x614E), GLYPH(0x2F8A8),
+ U24(0x618E), GLYPH(0xFA89),
+ U24(0x61F2), GLYPH(0xFA8B),
+ U24(0x61F6), GLYPH(0x2F8B1),
+ U24(0x654F), GLYPH(0x2F8C8),
+ U24(0x6674), GLYPH(0xFA91),
+ U24(0x6691), GLYPH(0x2F8CF),
+ U24(0x6717), GLYPH(0xFA92),
+ U24(0x671B), GLYPH(0x2F8D9),
+ U24(0x6885), GLYPH(0x2F8E2),
+ U24(0x6A02), GLYPH(0xF95C),
+ U24(0x6BBA), GLYPH(0xFA96),
+ U24(0x6D41), GLYPH(0xFA97),
+ U24(0x6D77), GLYPH(0x2F901),
+ U24(0x6ECB), GLYPH(0x2F90B),
+ U24(0x6F22), GLYPH(0xFA9A),
+ U24(0x701E), GLYPH(0x2F914),
+ U24(0x716E), GLYPH(0xFA9C),
+ U24(0x7235), GLYPH(0x2F921),
+ U24(0x732A), GLYPH(0xFAA0),
+ U24(0x7387), GLYPH(0xF9DB),
+ U24(0x7471), GLYPH(0x2F930),
+ U24(0x7570), GLYPH(0x2F938),
+ U24(0x76CA), GLYPH(0xFAA6),
+ U24(0x76F4), GLYPH(0x2F940),
+ U24(0x771F), GLYPH(0x2F947),
+ U24(0x774A), GLYPH(0x2F948),
+ U24(0x788C), GLYPH(0x2F94F),
+ U24(0x78CC), GLYPH(0x2F950),
+ U24(0x7956), GLYPH(0x2F953),
+ U24(0x798F), GLYPH(0x2F956),
+ U24(0x7A40), GLYPH(0x2F959),
+ U24(0x7BC0), GLYPH(0xFAAD),
+ U24(0x7DF4), GLYPH(0xFA57),
+ U24(0x8005), GLYPH(0xFAB2),
+ U24(0x8201), GLYPH(0x2F98B),
+ U24(0x8279), GLYPH(0xFA5E),
+ U24(0x82E5), GLYPH(0x2F998),
+ U24(0x8457), GLYPH(0x2F99F),
+ U24(0x865C), GLYPH(0x2F9B4),
+ U24(0x8779), GLYPH(0x2F9BB),
+ U24(0x8996), GLYPH(0xFAB8),
+ U24(0x8AAA), GLYPH(0xF9A1),
+ U24(0x8AED), GLYPH(0x2F9D0),
+ U24(0x8AF8), GLYPH(0xFABA),
+ U24(0x8AFE), GLYPH(0xFABD),
+ U24(0x8B01), GLYPH(0xFABC),
+ U24(0x8B39), GLYPH(0xFABF),
+ U24(0x8B8A), GLYPH(0x2F9D1),
+ U24(0x8D08), GLYPH(0xFAC1),
+ U24(0x8F38), GLYPH(0x2F9DF),
+ U24(0x9038), GLYPH(0xFA67),
+ U24(0x96E3), GLYPH(0xFAC7),
+ U24(0x9756), GLYPH(0xFAC8),
+ U24(0x97FF), GLYPH(0xFACA),
+ U24(0x980B), GLYPH(0x2F9FE),
+ U24(0x983B), GLYPH(0xFACC),
+ U24(0x9B12), GLYPH(0x2FA0A),
+ U24(0x9F9C), GLYPH(0xF908),
+ U24(0x22331), GLYPH(0x2F892),
+ U24(0x25AA7), GLYPH(0x2F95E),
+ // 0xFE02
+ U32(12), // numUVSMappings
+ U24(0x537F), GLYPH(0x2F833),
+ U24(0x5BE7), GLYPH(0x2F86F),
+ U24(0x618E), GLYPH(0x2F8AB),
+ U24(0x61F2), GLYPH(0x2F8B0),
+ U24(0x6717), GLYPH(0x2F8D8),
+ U24(0x6A02), GLYPH(0xF9BF),
+ U24(0x6BBA), GLYPH(0x2F8F5),
+ U24(0x6D41), GLYPH(0x2F902),
+ U24(0x7DF4), GLYPH(0xFAB0),
+ U24(0x8005), GLYPH(0x2F97A),
+ U24(0x980B), GLYPH(0x2F9FF),
+ U24(0x9F9C), GLYPH(0xFACE),
+};
+
+#undef U16
+#undef U24
+#undef U32
+#undef GLYPH
+
+static_assert(sizeof sCJKCompatSVSTable == 5065, "Table generator has a bug.");
diff --git a/system/graphics/thebes/ContextStateTracker.cpp b/system/graphics/thebes/ContextStateTracker.cpp
new file mode 100644
index 000000000..a179abad9
--- /dev/null
+++ b/system/graphics/thebes/ContextStateTracker.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContextStateTracker.h"
+#include "GLContext.h"
+
+namespace mozilla {
+
+void
+ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
+{
+ if (!profiler_feature_active("gpu")) {
+ return;
+ }
+
+ if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
+ return;
+ }
+
+ if (mSectionStack.Length() > 0) {
+ // We need to end the query since we're starting a new section and restore it
+ // when this section is finished.
+ aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
+ Top().mCpuTimeEnd = TimeStamp::Now();
+ }
+
+ ContextState newSection(aSectionName);
+
+ GLuint queryObject;
+ aGL->fGenQueries(1, &queryObject);
+ newSection.mStartQueryHandle = queryObject;
+ newSection.mCpuTimeStart = TimeStamp::Now();
+
+ aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
+
+ mSectionStack.AppendElement(newSection);
+}
+
+void
+ContextStateTrackerOGL::PopOGLSection(GLContext* aGL, const char* aSectionName)
+{
+ // We might have ignored a section start if we started profiling
+ // in the middle section. If so we will ignore this unmatched end.
+ if (mSectionStack.Length() == 0) {
+ return;
+ }
+
+ int i = mSectionStack.Length() - 1;
+ MOZ_ASSERT(strcmp(mSectionStack[i].mSectionName, aSectionName) == 0);
+ aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
+ mSectionStack[i].mCpuTimeEnd = TimeStamp::Now();
+ mCompletedSections.AppendElement(mSectionStack[i]);
+ mSectionStack.RemoveElementAt(i);
+
+ if (i - 1 >= 0) {
+ const char* sectionToRestore = Top().mSectionName;
+
+ // We need to restore the outer section
+ // Well do this by completing this section and adding a new
+ // one with the same name
+ mCompletedSections.AppendElement(Top());
+ mSectionStack.RemoveElementAt(i - 1);
+
+ ContextState newSection(sectionToRestore);
+
+ GLuint queryObject;
+ aGL->fGenQueries(1, &queryObject);
+ newSection.mStartQueryHandle = queryObject;
+ newSection.mCpuTimeStart = TimeStamp::Now();
+
+ aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
+
+ mSectionStack.AppendElement(newSection);
+ }
+
+ Flush(aGL);
+}
+
+void
+ContextStateTrackerOGL::Flush(GLContext* aGL)
+{
+ TimeStamp now = TimeStamp::Now();
+
+ while (mCompletedSections.Length() != 0) {
+ // On mac we see QUERY_RESULT_AVAILABLE cause a GL flush if we query it
+ // too early. For profiling we rather have the last 200ms of data missing
+ // while causing let's measurement distortions.
+ if (mCompletedSections[0].mCpuTimeEnd + TimeDuration::FromMilliseconds(200) > now) {
+ break;
+ }
+
+ GLuint handle = mCompletedSections[0].mStartQueryHandle;
+
+ // We've waiting 200ms, content rendering at > 20 FPS will be ready. We
+ // shouldn't see any flushes now.
+ GLuint returned = 0;
+ aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
+
+ if (!returned) {
+ break;
+ }
+
+ GLuint gpuTime = 0;
+ aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT, &gpuTime);
+
+ aGL->fDeleteQueries(1, &handle);
+
+ mCompletedSections.RemoveElementAt(0);
+ }
+}
+
+void
+ContextStateTrackerOGL::DestroyOGL(GLContext* aGL)
+{
+ while (mCompletedSections.Length() != 0) {
+ GLuint handle = (GLuint)mCompletedSections[0].mStartQueryHandle;
+ aGL->fDeleteQueries(1, &handle);
+ mCompletedSections.RemoveElementAt(0);
+ }
+}
+
+} // namespace mozilla
+
diff --git a/system/graphics/thebes/ContextStateTracker.h b/system/graphics/thebes/ContextStateTracker.h
new file mode 100644
index 000000000..01cd3c5b2
--- /dev/null
+++ b/system/graphics/thebes/ContextStateTracker.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CONTEXTSTATETRACKER_H
+#define GFX_CONTEXTSTATETRACKER_H
+
+#include "GLTypes.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
+#include <string.h>
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+} // namespace gl
+
+/**
+ * This class tracks the state of the context for debugging and profiling.
+ * Each section pushes a new stack entry and must be matched by an end section.
+ * All nested section must be ended before ending a parent section.
+ */
+class ContextStateTracker {
+public:
+ ContextStateTracker() {}
+
+private:
+
+ bool IsProfiling() { return true; }
+
+protected:
+ typedef GLuint TimerQueryHandle;
+
+ class ContextState {
+ public:
+ explicit ContextState(const char* aSectionName)
+ : mSectionName(aSectionName)
+ {}
+
+ const char* mSectionName;
+ mozilla::TimeStamp mCpuTimeStart;
+ mozilla::TimeStamp mCpuTimeEnd;
+ TimerQueryHandle mStartQueryHandle;
+ };
+
+ ContextState& Top() {
+ MOZ_ASSERT(mSectionStack.Length());
+ return mSectionStack[mSectionStack.Length() - 1];
+ }
+
+ nsTArray<ContextState> mCompletedSections;
+ nsTArray<ContextState> mSectionStack;
+};
+
+/*
+class ID3D11DeviceContext;
+
+class ContextStateTrackerD3D11 final : public ContextStateTracker {
+public:
+ // TODO Implement me
+ void PushD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
+ void PopD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
+ void DestroyD3D11(ID3D11DeviceContext* aCtxt) {}
+
+private:
+ void Flush();
+};
+*/
+
+class ContextStateTrackerOGL final : public ContextStateTracker {
+ typedef mozilla::gl::GLContext GLContext;
+public:
+ void PushOGLSection(GLContext* aGL, const char* aSectionName);
+ void PopOGLSection(GLContext* aGL, const char* aSectionName);
+ void DestroyOGL(GLContext* aGL);
+private:
+ void Flush(GLContext* aGL);
+};
+
+} // namespace mozilla
+
+#endif
+
diff --git a/system/graphics/thebes/D3D11Checks.cpp b/system/graphics/thebes/D3D11Checks.cpp
new file mode 100644
index 000000000..b5be58ff5
--- /dev/null
+++ b/system/graphics/thebes/D3D11Checks.cpp
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "D3D11Checks.h"
+#include "gfxConfig.h"
+#include "GfxDriverInfo.h"
+#include "gfxPrefs.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "nsIGfxInfo.h"
+#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <d3d10_1.h>
+#include <d3d11.h>
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla::widget;
+using mozilla::layers::AutoTextureLock;
+
+/* static */ bool
+D3D11Checks::DoesRenderTargetViewNeedRecreating(ID3D11Device *aDevice)
+{
+ bool result = false;
+ // CreateTexture2D is known to crash on lower feature levels, see bugs
+ // 1170211 and 1089413.
+ if (aDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
+ return true;
+ }
+
+ RefPtr<ID3D11DeviceContext> deviceContext;
+ aDevice->GetImmediateContext(getter_AddRefs(deviceContext));
+ int backbufferWidth = 32; int backbufferHeight = 32;
+ RefPtr<ID3D11Texture2D> offscreenTexture;
+ RefPtr<IDXGIKeyedMutex> keyedMutex;
+
+ D3D11_TEXTURE2D_DESC offscreenTextureDesc = { 0 };
+ offscreenTextureDesc.Width = backbufferWidth;
+ offscreenTextureDesc.Height = backbufferHeight;
+ offscreenTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ offscreenTextureDesc.MipLevels = 0;
+ offscreenTextureDesc.ArraySize = 1;
+ offscreenTextureDesc.SampleDesc.Count = 1;
+ offscreenTextureDesc.SampleDesc.Quality = 0;
+ offscreenTextureDesc.Usage = D3D11_USAGE_DEFAULT;
+ offscreenTextureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+ offscreenTextureDesc.CPUAccessFlags = 0;
+ offscreenTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+
+ HRESULT hr = aDevice->CreateTexture2D(&offscreenTextureDesc, NULL, getter_AddRefs(offscreenTexture));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "DoesRecreatingCreateTexture2DFail";
+ return false;
+ }
+
+ hr = offscreenTexture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(keyedMutex));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "DoesRecreatingKeyedMutexFailed";
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
+ offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ offscreenRTVDesc.Texture2D.MipSlice = 0;
+
+ RefPtr<ID3D11RenderTargetView> offscreenRTView;
+ hr = aDevice->CreateRenderTargetView(offscreenTexture, &offscreenRTVDesc, getter_AddRefs(offscreenRTView));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "DoesRecreatingCreateRenderTargetViewFailed";
+ return false;
+ }
+
+ {
+ // Acquire and clear
+ HRESULT hr;
+ AutoTextureLock lock(keyedMutex, hr, INFINITE);
+ FLOAT color1[4] = { 1, 1, 0.5, 1 };
+ deviceContext->ClearRenderTargetView(offscreenRTView, color1);
+ }
+
+ {
+ HRESULT hr;
+ AutoTextureLock lock(keyedMutex, hr, INFINITE);
+ FLOAT color2[4] = { 1, 1, 0, 1 };
+
+ deviceContext->ClearRenderTargetView(offscreenRTView, color2);
+ D3D11_TEXTURE2D_DESC desc;
+
+ offscreenTexture->GetDesc(&desc);
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MiscFlags = 0;
+ desc.BindFlags = 0;
+ RefPtr<ID3D11Texture2D> cpuTexture;
+ hr = aDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(cpuTexture));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed";
+ return false;
+ }
+
+ deviceContext->CopyResource(cpuTexture, offscreenTexture);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr);
+ return false;
+ }
+ int resultColor = *(int*)mapped.pData;
+ deviceContext->Unmap(cpuTexture, 0);
+ cpuTexture = nullptr;
+
+ // XXX on some drivers resultColor will not have changed to
+ // match the clear
+ if (resultColor != 0xffffff00) {
+ gfxCriticalNote << "RenderTargetViewNeedsRecreating";
+ result = true;
+ }
+ }
+ return result;
+}
+
+/* static */ bool
+D3D11Checks::DoesDeviceWork()
+{
+ static bool checked = false;
+ static bool result = false;
+
+ if (checked)
+ return result;
+ checked = true;
+
+ if (gfxPrefs::Direct2DForceEnabled() ||
+ gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
+ {
+ result = true;
+ return true;
+ }
+
+ if (GetModuleHandleW(L"igd10umd32.dll")) {
+ const wchar_t* checkModules[] = {L"dlumd32.dll",
+ L"dlumd11.dll",
+ L"dlumd10.dll"};
+ for (int i=0; i<PR_ARRAY_SIZE(checkModules); i+=1) {
+ if (GetModuleHandleW(checkModules[i])) {
+ nsString displayLinkModuleVersionString;
+ gfxWindowsPlatform::GetDLLVersion(checkModules[i],
+ displayLinkModuleVersionString);
+ uint64_t displayLinkModuleVersion;
+ if (!ParseDriverVersion(displayLinkModuleVersionString,
+ &displayLinkModuleVersion)) {
+ gfxCriticalError() << "DisplayLink: could not parse version "
+ << checkModules[i];
+ return false;
+ }
+ if (displayLinkModuleVersion <= V(8,6,1,36484)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DisplayLink: too old version " << displayLinkModuleVersionString.get();
+ return false;
+ }
+ }
+ }
+ }
+ result = true;
+ return true;
+}
+
+static bool
+TryCreateTexture2D(ID3D11Device *device,
+ D3D11_TEXTURE2D_DESC* desc,
+ D3D11_SUBRESOURCE_DATA* data,
+ RefPtr<ID3D11Texture2D>& texture)
+{
+ // Older Intel driver version (see bug 1221348 for version #s) crash when
+ // creating a texture with shared keyed mutex and data.
+ MOZ_SEH_TRY {
+ return !FAILED(device->CreateTexture2D(desc, data, getter_AddRefs(texture)));
+ } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ // For now we want to aggregrate all the crash signature to a known crash.
+ gfxDevCrash(LogReason::TextureCreation) << "Crash creating texture. See bug 1221348.";
+ return false;
+ }
+}
+
+// See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails
+// with E_OUTOFMEMORY.
+static bool
+DoesTextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT format, UINT bindflags)
+{
+ // CreateTexture2D is known to crash on lower feature levels, see bugs
+ // 1170211 and 1089413.
+ if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
+ return false;
+ }
+
+ if (gfxPrefs::Direct2DForceEnabled() ||
+ gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
+ {
+ return true;
+ }
+
+ if (GetModuleHandleW(L"atidxx32.dll")) {
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ if (gfxInfo) {
+ nsString vendorID, vendorID2;
+ gfxInfo->GetAdapterVendorID(vendorID);
+ gfxInfo->GetAdapterVendorID2(vendorID2);
+ if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
+ if (!gfxPrefs::LayersAMDSwitchableGfxEnabled()) {
+ return false;
+ }
+ gfxCriticalError(CriticalLog::DefaultOptions(false)) << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU";
+ }
+ }
+ }
+
+ RefPtr<ID3D11Texture2D> texture;
+ D3D11_TEXTURE2D_DESC desc;
+ const int texture_size = 32;
+ desc.Width = texture_size;
+ desc.Height = texture_size;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = format;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ desc.BindFlags = bindflags;
+
+ uint32_t color[texture_size * texture_size];
+ for (size_t i = 0; i < sizeof(color)/sizeof(color[0]); i++) {
+ color[i] = 0xff00ffff;
+ }
+ // XXX If we pass the data directly at texture creation time we
+ // get a crash on Intel 8.5.10.[18xx-1994] drivers.
+ // We can work around this issue by doing UpdateSubresource.
+ if (!TryCreateTexture2D(device, &desc, nullptr, texture)) {
+ gfxCriticalNote << "DoesD3D11TextureSharingWork_TryCreateTextureFailure";
+ return false;
+ }
+
+ RefPtr<IDXGIKeyedMutex> sourceSharedMutex;
+ texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sourceSharedMutex));
+ if (FAILED(sourceSharedMutex->AcquireSync(0, 30*1000))) {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout";
+ // only wait for 30 seconds
+ return false;
+ }
+
+ RefPtr<ID3D11DeviceContext> deviceContext;
+ device->GetImmediateContext(getter_AddRefs(deviceContext));
+
+ int stride = texture_size * 4;
+ deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride, stride * texture_size);
+
+ if (FAILED(sourceSharedMutex->ReleaseSync(0))) {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout";
+ return false;
+ }
+
+ HANDLE shareHandle;
+ RefPtr<IDXGIResource> otherResource;
+ if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource),
+ getter_AddRefs(otherResource))))
+ {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure";
+ return false;
+ }
+
+ if (FAILED(otherResource->GetSharedHandle(&shareHandle))) {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
+ return false;
+ }
+
+ RefPtr<ID3D11Resource> sharedResource;
+ RefPtr<ID3D11Texture2D> sharedTexture;
+ if (FAILED(device->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource),
+ getter_AddRefs(sharedResource))))
+ {
+ gfxCriticalError(CriticalLog::DefaultOptions(false)) << "OpenSharedResource failed for format " << format;
+ return false;
+ }
+
+ if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D),
+ getter_AddRefs(sharedTexture))))
+ {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
+ return false;
+ }
+
+ // create a staging texture for readback
+ RefPtr<ID3D11Texture2D> cpuTexture;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MiscFlags = 0;
+ desc.BindFlags = 0;
+ if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture)))) {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure";
+ return false;
+ }
+
+ RefPtr<IDXGIKeyedMutex> sharedMutex;
+ sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sharedMutex));
+ {
+ HRESULT hr;
+ AutoTextureLock lock(sharedMutex, hr, 30*1000);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout";
+ // only wait for 30 seconds
+ return false;
+ }
+
+ // Copy to the cpu texture so that we can readback
+ deviceContext->CopyResource(cpuTexture, sharedTexture);
+
+ // We only need to hold on to the mutex during the copy.
+ sharedMutex->ReleaseSync(0);
+ }
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ uint32_t resultColor = 0;
+ if (SUCCEEDED(deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
+ // read the texture
+ resultColor = *(uint32_t*)mapped.pData;
+ deviceContext->Unmap(cpuTexture, 0);
+ } else {
+ gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed";
+ return false;
+ }
+
+ // check that the color we put in is the color we get out
+ if (resultColor != color[0]) {
+ // Shared surfaces seem to be broken on dual AMD & Intel HW when using the
+ // AMD GPU
+ gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch";
+ return false;
+ }
+
+ RefPtr<ID3D11ShaderResourceView> sharedView;
+
+ // This if(FAILED()) is the one that actually fails on systems affected by bug 1083071.
+ if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL, getter_AddRefs(sharedView)))) {
+ gfxCriticalNote << "CreateShaderResourceView failed for format" << format;
+ return false;
+ }
+
+ return true;
+}
+
+/* static */ bool
+D3D11Checks::DoesTextureSharingWork(ID3D11Device *device)
+{
+ return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+}
+
+/* static */ bool
+D3D11Checks::DoesAlphaTextureSharingWork(ID3D11Device *device)
+{
+ return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
+}
+
+/* static */ bool
+D3D11Checks::GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out)
+{
+ RefPtr<IDXGIDevice> dxgiDevice;
+ HRESULT hr = device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<IDXGIAdapter> dxgiAdapter;
+ if (FAILED(dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)))) {
+ return false;
+ }
+
+ return SUCCEEDED(dxgiAdapter->GetDesc(out));
+}
+
+/* static */ void
+D3D11Checks::WarnOnAdapterMismatch(ID3D11Device *device)
+{
+ DXGI_ADAPTER_DESC desc;
+ PodZero(&desc);
+ GetDxgiDesc(device, &desc);
+
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ nsString vendorID;
+ gfxInfo->GetAdapterVendorID(vendorID);
+ nsresult ec;
+ int32_t vendor = vendorID.ToInteger(&ec, 16);
+ if (vendor != desc.VendorId) {
+ gfxCriticalNote << "VendorIDMismatch V " << hexa(vendor) << " " << hexa(desc.VendorId);
+ }
+}
+
+/* static */ bool
+D3D11Checks::DoesRemotePresentWork(IDXGIAdapter* adapter)
+{
+ // Remote presentation was added in DXGI 1.2, for Windows 8 and the Platform Update to Windows 7.
+ RefPtr<IDXGIAdapter2> check;
+ HRESULT hr = adapter->QueryInterface(__uuidof(IDXGIAdapter2), getter_AddRefs(check));
+ return SUCCEEDED(hr) && check;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/D3D11Checks.h b/system/graphics/thebes/D3D11Checks.h
new file mode 100644
index 000000000..07a9d940f
--- /dev/null
+++ b/system/graphics/thebes/D3D11Checks.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_thebes_D3D11Checks_h
+#define mozilla_gfx_thebes_D3D11Checks_h
+
+struct ID3D11Device;
+struct IDXGIAdapter;
+struct DXGI_ADAPTER_DESC;
+
+namespace mozilla {
+namespace gfx {
+
+struct D3D11Checks
+{
+ static bool DoesRenderTargetViewNeedRecreating(ID3D11Device* aDevice);
+ static bool DoesDeviceWork();
+ static bool DoesTextureSharingWork(ID3D11Device *device);
+ static bool DoesAlphaTextureSharingWork(ID3D11Device *device);
+ static void WarnOnAdapterMismatch(ID3D11Device* device);
+ static bool GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out);
+ static bool DoesRemotePresentWork(IDXGIAdapter* adapter);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_thebes_D3D11Checks_h
diff --git a/system/graphics/thebes/DeviceManagerDx.cpp b/system/graphics/thebes/DeviceManagerDx.cpp
new file mode 100644
index 000000000..5f505f88b
--- /dev/null
+++ b/system/graphics/thebes/DeviceManagerDx.cpp
@@ -0,0 +1,867 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DeviceManagerDx.h"
+#include "D3D11Checks.h"
+#include "gfxConfig.h"
+#include "GfxDriverInfo.h"
+#include "gfxPrefs.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/D3DMessageUtils.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "nsIGfxInfo.h"
+#include <ddraw.h>
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla::widget;
+
+StaticAutoPtr<DeviceManagerDx> DeviceManagerDx::sInstance;
+
+// We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
+// since it doesn't include d3d11.h, so we use a static here. It should only
+// be used within InitializeD3D11.
+decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
+
+// We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h,
+// since it doesn't include ddraw.h, so we use a static here. It should only
+// be used within InitializeDirectDrawConfig.
+decltype(DirectDrawCreateEx)* sDirectDrawCreateExFn = nullptr;
+
+/* static */ void
+DeviceManagerDx::Init()
+{
+ sInstance = new DeviceManagerDx();
+}
+
+/* static */ void
+DeviceManagerDx::Shutdown()
+{
+ sInstance = nullptr;
+}
+
+DeviceManagerDx::DeviceManagerDx()
+ : mDeviceLock("gfxWindowsPlatform.mDeviceLock"),
+ mCompositorDeviceSupportsVideo(false)
+{
+ // Set up the D3D11 feature levels we can ask for.
+ if (IsWin8OrLater()) {
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
+ }
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
+}
+
+bool
+DeviceManagerDx::LoadD3D11()
+{
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+ MOZ_ASSERT(d3d11.IsEnabled());
+
+ if (sD3D11CreateDeviceFn) {
+ return true;
+ }
+
+ nsModuleHandle module(LoadLibrarySystem32(L"d3d11.dll"));
+ if (!module) {
+ d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_LIB"));
+ return false;
+ }
+
+ sD3D11CreateDeviceFn =
+ (decltype(D3D11CreateDevice)*)GetProcAddress(module, "D3D11CreateDevice");
+ if (!sD3D11CreateDeviceFn) {
+ // We should just be on Windows Vista or XP in this case.
+ d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_FUNCPTR"));
+ return false;
+ }
+
+ mD3D11Module.steal(module);
+ return true;
+}
+
+void
+DeviceManagerDx::ReleaseD3D11()
+{
+ MOZ_ASSERT(!mCompositorDevice);
+ MOZ_ASSERT(!mContentDevice);
+ MOZ_ASSERT(!mDecoderDevice);
+
+ mD3D11Module.reset();
+ sD3D11CreateDeviceFn = nullptr;
+}
+
+static inline bool
+ProcessOwnsCompositor()
+{
+ return XRE_GetProcessType() == GeckoProcessType_GPU ||
+ (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
+}
+
+bool
+DeviceManagerDx::CreateCompositorDevices()
+{
+ MOZ_ASSERT(ProcessOwnsCompositor());
+
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+ MOZ_ASSERT(d3d11.IsEnabled());
+
+ if (!LoadD3D11()) {
+ return false;
+ }
+
+ CreateCompositorDevice(d3d11);
+
+ if (!d3d11.IsEnabled()) {
+ MOZ_ASSERT(!mCompositorDevice);
+ ReleaseD3D11();
+ return false;
+ }
+
+ // We leak these everywhere and we need them our entire runtime anyway, let's
+ // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
+ // as well for D2D1 and device resets.
+ mD3D11Module.disown();
+
+ MOZ_ASSERT(mCompositorDevice);
+ return d3d11.IsEnabled();
+}
+
+void
+DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus)
+{
+ MOZ_ASSERT(!ProcessOwnsCompositor());
+
+ mDeviceStatus = Some(aDeviceStatus);
+}
+
+void
+DeviceManagerDx::ExportDeviceInfo(D3D11DeviceStatus* aOut)
+{
+ // Even though the parent process might not own the compositor, we still
+ // populate DeviceManagerDx with device statistics (for simplicity).
+ // That means it still gets queried for compositor information.
+ MOZ_ASSERT(XRE_IsParentProcess() || XRE_GetProcessType() == GeckoProcessType_GPU);
+
+ if (mDeviceStatus) {
+ *aOut = mDeviceStatus.value();
+ }
+}
+
+void
+DeviceManagerDx::CreateContentDevices()
+{
+ MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
+
+ if (!LoadD3D11()) {
+ return;
+ }
+
+ // We should have been assigned a DeviceStatus from the parent process,
+ // GPU process, or the same process if using in-process compositing.
+ MOZ_ASSERT(mDeviceStatus);
+
+ if (CreateContentDevice() == FeatureStatus::CrashedInHandler) {
+ DisableD3D11AfterCrash();
+ }
+}
+
+IDXGIAdapter1*
+DeviceManagerDx::GetDXGIAdapter()
+{
+ if (mAdapter) {
+ return mAdapter;
+ }
+
+ nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
+ decltype(CreateDXGIFactory1)* createDXGIFactory1 = (decltype(CreateDXGIFactory1)*)
+ GetProcAddress(dxgiModule, "CreateDXGIFactory1");
+
+ if (!createDXGIFactory1) {
+ return nullptr;
+ }
+
+ // Try to use a DXGI 1.1 adapter in order to share resources
+ // across processes.
+ RefPtr<IDXGIFactory1> factory1;
+ HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
+ getter_AddRefs(factory1));
+ if (FAILED(hr) || !factory1) {
+ // This seems to happen with some people running the iZ3D driver.
+ // They won't get acceleration.
+ return nullptr;
+ }
+
+ if (!mDeviceStatus) {
+ // If we haven't created a device yet, and have no existing device status,
+ // then this must be the compositor device. Pick the first adapter we can.
+ if (FAILED(factory1->EnumAdapters1(0, getter_AddRefs(mAdapter)))) {
+ return nullptr;
+ }
+ } else {
+ // In the UI and GPU process, we clear mDeviceStatus on device reset, so we
+ // should never reach here. Furthermore, the UI process does not create
+ // devices when using a GPU process.
+ //
+ // So, this should only ever get called on the content process.
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ // In the child process, we search for the adapter that matches the parent
+ // process. The first adapter can be mismatched on dual-GPU systems.
+ for (UINT index = 0; ; index++) {
+ RefPtr<IDXGIAdapter1> adapter;
+ if (FAILED(factory1->EnumAdapters1(index, getter_AddRefs(adapter)))) {
+ break;
+ }
+
+ const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
+
+ DXGI_ADAPTER_DESC desc;
+ if (SUCCEEDED(adapter->GetDesc(&desc)) &&
+ desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
+ desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
+ desc.VendorId == preferred.VendorId &&
+ desc.DeviceId == preferred.DeviceId)
+ {
+ mAdapter = adapter.forget();
+ break;
+ }
+ }
+ }
+
+ if (!mAdapter) {
+ return nullptr;
+ }
+
+ // We leak this module everywhere, we might as well do so here as well.
+ dxgiModule.disown();
+ return mAdapter;
+}
+
+bool
+DeviceManagerDx::CreateCompositorDeviceHelper(
+ FeatureState& aD3d11, IDXGIAdapter1* aAdapter, bool aAttemptVideoSupport, RefPtr<ID3D11Device>& aOutDevice)
+{
+ // Check if a failure was injected for testing.
+ if (gfxPrefs::DeviceFailForTesting()) {
+ aD3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_SIM"));
+ return false;
+ }
+
+ // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+ // to prevent bug 1092260. IE 11 also uses this flag.
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
+ if (aAttemptVideoSupport) {
+ flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+ if (!CreateDevice(aAdapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
+ if (!aAttemptVideoSupport) {
+ gfxCriticalError() << "Crash during D3D11 device creation";
+ aD3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE1"));
+ }
+ return false;
+ }
+
+ if (FAILED(hr) || !device) {
+ if (!aAttemptVideoSupport) {
+ aD3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE2"));
+ }
+ return false;
+ }
+ if (!D3D11Checks::DoesDeviceWork()) {
+ if (!aAttemptVideoSupport) {
+ aD3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_BROKEN"));
+ }
+ return false;
+ }
+
+ aOutDevice = device;
+ return true;
+}
+
+void
+DeviceManagerDx::CreateCompositorDevice(FeatureState& d3d11)
+{
+ if (gfxPrefs::LayersD3D11ForceWARP()) {
+ CreateWARPCompositorDevice();
+ return;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
+ if (!adapter) {
+ d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DXGI"));
+ return;
+ }
+
+ if (XRE_IsGPUProcess() && !D3D11Checks::DoesRemotePresentWork(adapter)) {
+ d3d11.SetFailed(
+ FeatureStatus::Unavailable,
+ "DXGI does not support out-of-process presentation",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_REMOTE_PRESENT"));
+ return;
+ }
+
+ RefPtr<ID3D11Device> device;
+ if (!CreateCompositorDeviceHelper(d3d11, adapter, true, device)) {
+ // Try again without video support and record that it failed.
+ mCompositorDeviceSupportsVideo = false;
+ if (!CreateCompositorDeviceHelper(d3d11, adapter, false, device)) {
+ return;
+ }
+ } else {
+ mCompositorDeviceSupportsVideo = true;
+ }
+
+ // Only test this when not using WARP since it can fail and cause
+ // GetDeviceRemovedReason to return weird values.
+ bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+
+ DXGI_ADAPTER_DESC desc;
+ PodZero(&desc);
+ adapter->GetDesc(&desc);
+
+ if (!textureSharingWorks) {
+ gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
+ FeatureStatus::Broken,
+ "Texture sharing doesn't work");
+ }
+ if (D3D11Checks::DoesRenderTargetViewNeedRecreating(device)) {
+ gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
+ FeatureStatus::Broken,
+ "RenderTargetViews need recreating");
+ }
+ if (XRE_IsParentProcess()) {
+ // It seems like this may only happen when we're using the NVIDIA gpu
+ D3D11Checks::WarnOnAdapterMismatch(device);
+ }
+
+ int featureLevel = device->GetFeatureLevel();
+ {
+ MutexAutoLock lock(mDeviceLock);
+ mCompositorDevice = device;
+ mDeviceStatus = Some(D3D11DeviceStatus(
+ false,
+ textureSharingWorks,
+ featureLevel,
+ DxgiAdapterDesc::From(desc)));
+ }
+ mCompositorDevice->SetExceptionMode(0);
+}
+
+bool
+DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
+ D3D_DRIVER_TYPE aDriverType,
+ UINT aFlags,
+ HRESULT& aResOut,
+ RefPtr<ID3D11Device>& aOutDevice)
+{
+ MOZ_SEH_TRY {
+ aResOut = sD3D11CreateDeviceFn(
+ aAdapter, aDriverType, nullptr,
+ aFlags,
+ mFeatureLevels.Elements(), mFeatureLevels.Length(),
+ D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
+ } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+}
+
+void
+DeviceManagerDx::CreateWARPCompositorDevice()
+{
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+ // to prevent bug 1092260. IE 11 also uses this flag.
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, flags, hr, device)) {
+ gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
+ d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE"));
+ }
+
+ if (FAILED(hr) || !device) {
+ // This should always succeed... in theory.
+ gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
+ d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
+ return;
+ }
+
+ // Only test for texture sharing on Windows 8 since it puts the device into
+ // an unusable state if used on Windows 7
+ bool textureSharingWorks = false;
+ if (IsWin8OrLater()) {
+ textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+ }
+
+ DxgiAdapterDesc nullAdapter;
+ PodZero(&nullAdapter);
+
+ int featureLevel = device->GetFeatureLevel();
+ {
+ MutexAutoLock lock(mDeviceLock);
+ mCompositorDevice = device;
+ mDeviceStatus = Some(D3D11DeviceStatus(
+ true,
+ textureSharingWorks,
+ featureLevel,
+ nullAdapter));
+ }
+ mCompositorDevice->SetExceptionMode(0);
+
+}
+
+FeatureStatus
+DeviceManagerDx::CreateContentDevice()
+{
+ RefPtr<IDXGIAdapter1> adapter;
+ if (!mDeviceStatus->isWARP()) {
+ adapter = GetDXGIAdapter();
+ if (!adapter) {
+ gfxCriticalNote << "Could not get a DXGI adapter";
+ return FeatureStatus::Unavailable;
+ }
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ D3D_DRIVER_TYPE type = mDeviceStatus->isWARP()
+ ? D3D_DRIVER_TYPE_WARP
+ : D3D_DRIVER_TYPE_UNKNOWN;
+ if (!CreateDevice(adapter, type, flags, hr, device)) {
+ gfxCriticalNote << "Recovered from crash while creating a D3D11 content device";
+ return FeatureStatus::CrashedInHandler;
+ }
+
+ if (FAILED(hr) || !device) {
+ gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
+ return FeatureStatus::Failed;
+ }
+
+ // InitializeD2D() will abort early if the compositor device did not support
+ // texture sharing. If we're in the content process, we can't rely on the
+ // parent device alone: some systems have dual GPUs that are capable of
+ // binding the parent and child processes to different GPUs. As a safety net,
+ // we re-check texture sharing against the newly created D3D11 content device.
+ // If it fails, we won't use Direct2D.
+ if (XRE_IsContentProcess()) {
+ if (!D3D11Checks::DoesTextureSharingWork(device)) {
+ return FeatureStatus::Failed;
+ }
+
+ DebugOnly<bool> ok = ContentAdapterIsParentAdapter(device);
+ MOZ_ASSERT(ok);
+ }
+
+ {
+ MutexAutoLock lock(mDeviceLock);
+ mContentDevice = device;
+ }
+ mContentDevice->SetExceptionMode(0);
+
+ RefPtr<ID3D10Multithread> multi;
+ hr = mContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+ if (SUCCEEDED(hr) && multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+ return FeatureStatus::Available;
+}
+
+RefPtr<ID3D11Device>
+DeviceManagerDx::CreateDecoderDevice()
+{
+ bool isAMD = false;
+ {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return nullptr;
+ }
+ isAMD = mDeviceStatus->adapter().VendorId == 0x1002;
+ }
+
+ bool reuseDevice = false;
+ if (gfxPrefs::Direct3D11ReuseDecoderDevice() < 0) {
+ // Use the default logic, which is to allow reuse of devices on AMD, but create
+ // separate devices everywhere else.
+ if (isAMD) {
+ reuseDevice = true;
+ }
+ } else if (gfxPrefs::Direct3D11ReuseDecoderDevice() > 0) {
+ reuseDevice = true;
+ }
+
+ if (reuseDevice) {
+ if (mCompositorDevice && mCompositorDeviceSupportsVideo && !mDecoderDevice) {
+ mDecoderDevice = mCompositorDevice;
+
+ RefPtr<ID3D10Multithread> multi;
+ mDecoderDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+ if (multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+ }
+
+ if (mDecoderDevice) {
+ RefPtr<ID3D11Device> dev = mDecoderDevice;
+ return dev.forget();
+ }
+ }
+
+ if (!sD3D11CreateDeviceFn) {
+ // We should just be on Windows Vista or XP in this case.
+ return nullptr;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
+ if (!adapter) {
+ return nullptr;
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ UINT flags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS |
+ D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+ if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
+ return nullptr;
+ }
+ if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> multi;
+ device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+
+ multi->SetMultithreadProtected(TRUE);
+
+ if (reuseDevice) {
+ mDecoderDevice = device;
+ }
+ return device;
+}
+
+void
+DeviceManagerDx::ResetDevices()
+{
+ MutexAutoLock lock(mDeviceLock);
+
+ mAdapter = nullptr;
+ mCompositorDevice = nullptr;
+ mContentDevice = nullptr;
+ mDeviceStatus = Nothing();
+ mDeviceResetReason = Nothing();
+ Factory::SetDirect3D11Device(nullptr);
+}
+
+bool
+DeviceManagerDx::MaybeResetAndReacquireDevices()
+{
+ DeviceResetReason resetReason;
+ if (!HasDeviceReset(&resetReason)) {
+ return false;
+ }
+
+ bool createCompositorDevice = !!mCompositorDevice;
+ bool createContentDevice = !!mContentDevice;
+
+ ResetDevices();
+
+ if (createCompositorDevice && !CreateCompositorDevices()) {
+ // Just stop, don't try anything more
+ return true;
+ }
+ if (createContentDevice) {
+ CreateContentDevices();
+ }
+
+ return true;
+}
+
+bool
+DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device)
+{
+ DXGI_ADAPTER_DESC desc;
+ if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
+ gfxCriticalNote << "Could not query device DXGI adapter info";
+ return false;
+ }
+
+ const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
+
+ if (desc.VendorId != preferred.VendorId ||
+ desc.DeviceId != preferred.DeviceId ||
+ desc.SubSysId != preferred.SubSysId ||
+ desc.AdapterLuid.HighPart != preferred.AdapterLuid.HighPart ||
+ desc.AdapterLuid.LowPart != preferred.AdapterLuid.LowPart)
+ {
+ gfxCriticalNote <<
+ "VendorIDMismatch P " <<
+ hexa(preferred.VendorId) << " " <<
+ hexa(desc.VendorId);
+ return false;
+ }
+
+ return true;
+}
+
+static DeviceResetReason HResultToResetReason(HRESULT hr)
+{
+ switch (hr) {
+ case DXGI_ERROR_DEVICE_HUNG:
+ return DeviceResetReason::HUNG;
+ case DXGI_ERROR_DEVICE_REMOVED:
+ return DeviceResetReason::REMOVED;
+ case DXGI_ERROR_DEVICE_RESET:
+ return DeviceResetReason::RESET;
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ return DeviceResetReason::DRIVER_ERROR;
+ case DXGI_ERROR_INVALID_CALL:
+ return DeviceResetReason::INVALID_CALL;
+ case E_OUTOFMEMORY:
+ return DeviceResetReason::OUT_OF_MEMORY;
+ default:
+ MOZ_ASSERT(false);
+ }
+ return DeviceResetReason::UNKNOWN;
+}
+
+bool
+DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason)
+{
+ MutexAutoLock lock(mDeviceLock);
+
+ if (mDeviceResetReason) {
+ if (aOutReason) {
+ *aOutReason = mDeviceResetReason.value();
+ }
+ return true;
+ }
+
+ DeviceResetReason reason;
+ if (GetAnyDeviceRemovedReason(&reason)) {
+ mDeviceResetReason = Some(reason);
+ if (aOutReason) {
+ *aOutReason = reason;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool
+DidDeviceReset(const RefPtr<ID3D11Device>& aDevice, DeviceResetReason* aOutReason)
+{
+ if (!aDevice) {
+ return false;
+ }
+ HRESULT hr = aDevice->GetDeviceRemovedReason();
+ if (hr == S_OK) {
+ return false;
+ }
+
+ *aOutReason = HResultToResetReason(hr);
+ return true;
+}
+
+bool
+DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason)
+{
+ // Caller must own the lock, since we access devices directly, and can be
+ // called from any thread.
+ mDeviceLock.AssertCurrentThreadOwns();
+
+ if (DidDeviceReset(mCompositorDevice, aOutReason) ||
+ DidDeviceReset(mContentDevice, aOutReason))
+ {
+ return true;
+ }
+
+ if (XRE_IsParentProcess() &&
+ NS_IsMainThread() &&
+ gfxPrefs::DeviceResetForTesting())
+ {
+ gfxPrefs::SetDeviceResetForTesting(0);
+ *aOutReason = DeviceResetReason::FORCED_RESET;
+ return true;
+ }
+
+ return false;
+}
+
+void
+DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason)
+{
+ {
+ MutexAutoLock lock(mDeviceLock);
+ mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
+ }
+}
+
+void
+DeviceManagerDx::NotifyD3D9DeviceReset()
+{
+ MutexAutoLock lock(mDeviceLock);
+ mDeviceResetReason = Some(DeviceResetReason::D3D9_RESET);
+}
+
+void
+DeviceManagerDx::DisableD3D11AfterCrash()
+{
+ gfxConfig::Disable(Feature::D3D11_COMPOSITING,
+ FeatureStatus::CrashedInHandler,
+ "Crashed while acquiring a Direct3D11 device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
+ ResetDevices();
+}
+
+RefPtr<ID3D11Device>
+DeviceManagerDx::GetCompositorDevice()
+{
+ MutexAutoLock lock(mDeviceLock);
+ return mCompositorDevice;
+}
+
+RefPtr<ID3D11Device>
+DeviceManagerDx::GetContentDevice()
+{
+ MutexAutoLock lock(mDeviceLock);
+ return mContentDevice;
+}
+
+unsigned
+DeviceManagerDx::GetCompositorFeatureLevel() const
+{
+ if (!mDeviceStatus) {
+ return 0;
+ }
+ return mDeviceStatus->featureLevel();
+}
+
+bool
+DeviceManagerDx::TextureSharingWorks()
+{
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->textureSharingWorks();
+}
+
+bool
+DeviceManagerDx::CanInitializeKeyedMutexTextures()
+{
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ // Disable this on all Intel devices because of crashes.
+ // See bug 1292923.
+ return mDeviceStatus->adapter().VendorId != 0x8086;
+}
+
+bool
+DeviceManagerDx::CheckRemotePresentSupport()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
+ if (!adapter) {
+ return false;
+ }
+ if (!D3D11Checks::DoesRemotePresentWork(adapter)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+DeviceManagerDx::IsWARP()
+{
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->isWARP();
+}
+
+void
+DeviceManagerDx::InitializeDirectDraw()
+{
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+
+ if (mDirectDraw) {
+ // Already initialized.
+ return;
+ }
+
+ FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
+ if (!ddraw.IsEnabled()) {
+ return;
+ }
+
+ // Check if DirectDraw is available on this system.
+ mDirectDrawDLL.own(LoadLibrarySystem32(L"ddraw.dll"));
+ if (!mDirectDrawDLL) {
+ ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
+ return;
+ }
+
+ sDirectDrawCreateExFn =
+ (decltype(DirectDrawCreateEx)*)GetProcAddress(mDirectDrawDLL, "DirectDrawCreateEx");
+ if (!sDirectDrawCreateExFn) {
+ ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
+ return;
+ }
+
+ HRESULT hr;
+ MOZ_SEH_TRY {
+ hr = sDirectDrawCreateExFn(nullptr, getter_AddRefs(mDirectDraw), IID_IDirectDraw7, nullptr);
+ } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
+ gfxCriticalNote << "DoesCreatingDirectDrawFailed";
+ return;
+ }
+ if (FAILED(hr)) {
+ ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
+ gfxCriticalNote << "DoesCreatingDirectDrawFailed " << hexa(hr);
+ return;
+ }
+}
+
+IDirectDraw7*
+DeviceManagerDx::GetDirectDraw()
+{
+ return mDirectDraw;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/DeviceManagerDx.h b/system/graphics/thebes/DeviceManagerDx.h
new file mode 100644
index 000000000..25c028b55
--- /dev/null
+++ b/system/graphics/thebes/DeviceManagerDx.h
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_thebes_DeviceManagerDx_h
+#define mozilla_gfx_thebes_DeviceManagerDx_h
+
+#include "gfxPlatform.h"
+#include "gfxTelemetry.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "nsTArray.h"
+#include "nsWindowsHelpers.h"
+
+#include <windows.h>
+#include <objbase.h>
+
+#include <dxgi.h>
+#include <d3d11.h>
+
+// This header is available in the June 2010 SDK and in the Win8 SDK
+#include <d3dcommon.h>
+// Win 8.0 SDK types we'll need when building using older sdks.
+#if !defined(D3D_FEATURE_LEVEL_11_1) // defined in the 8.0 SDK only
+#define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
+#define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
+#define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096
+#endif
+
+struct ID3D11Device;
+struct IDirectDraw7;
+
+namespace mozilla {
+namespace gfx {
+class FeatureState;
+
+class DeviceManagerDx final
+{
+public:
+ static void Init();
+ static void Shutdown();
+
+ DeviceManagerDx();
+
+ static DeviceManagerDx* Get() {
+ return sInstance;
+ }
+
+ RefPtr<ID3D11Device> GetCompositorDevice();
+ RefPtr<ID3D11Device> GetContentDevice();
+ RefPtr<ID3D11Device> CreateDecoderDevice();
+ IDirectDraw7* GetDirectDraw();
+
+ unsigned GetCompositorFeatureLevel() const;
+ bool TextureSharingWorks();
+ bool IsWARP();
+
+ // Returns true if we can create a texture with
+ // D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX and also
+ // upload texture data during the CreateTexture2D
+ // call. This crashes on some devices, so we might
+ // need to avoid it.
+ bool CanInitializeKeyedMutexTextures();
+
+ bool CreateCompositorDevices();
+ void CreateContentDevices();
+
+ void ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus);
+ void ExportDeviceInfo(D3D11DeviceStatus* aOut);
+
+ void ResetDevices();
+ void InitializeDirectDraw();
+
+ // Reset and reacquire the devices if a reset has happened.
+ // Returns whether a reset occurred not whether reacquiring
+ // was successful.
+ bool MaybeResetAndReacquireDevices();
+
+ // Test whether we can acquire a DXGI 1.2-compatible adapter. This should
+ // only be called on startup before devices are initialized.
+ bool CheckRemotePresentSupport();
+
+ // Device reset helpers.
+ bool HasDeviceReset(DeviceResetReason* aOutReason = nullptr);
+
+ // Note: these set the cached device reset reason, which will be picked up
+ // on the next frame.
+ void ForceDeviceReset(ForcedDeviceResetReason aReason);
+ void NotifyD3D9DeviceReset();
+
+private:
+ IDXGIAdapter1 *GetDXGIAdapter();
+
+ void DisableD3D11AfterCrash();
+
+ void CreateCompositorDevice(mozilla::gfx::FeatureState& d3d11);
+ bool CreateCompositorDeviceHelper(
+ mozilla::gfx::FeatureState& aD3d11,
+ IDXGIAdapter1* aAdapter,
+ bool aAttemptVideoSupport,
+ RefPtr<ID3D11Device>& aOutDevice);
+
+ void CreateWARPCompositorDevice();
+
+ mozilla::gfx::FeatureStatus CreateContentDevice();
+
+ bool CreateDevice(IDXGIAdapter* aAdapter,
+ D3D_DRIVER_TYPE aDriverType,
+ UINT aFlags,
+ HRESULT& aResOut,
+ RefPtr<ID3D11Device>& aOutDevice);
+
+ bool ContentAdapterIsParentAdapter(ID3D11Device* device);
+
+ bool LoadD3D11();
+ void ReleaseD3D11();
+
+ // Call GetDeviceRemovedReason on each device until one returns
+ // a failure.
+ bool GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason);
+
+private:
+ static StaticAutoPtr<DeviceManagerDx> sInstance;
+
+ // This is assigned during device creation. Afterwards, it is released if
+ // devices failed, and "forgotten" if devices succeeded (meaning, we leak
+ // the ref and unassign the module).
+ nsModuleHandle mD3D11Module;
+
+ mozilla::Mutex mDeviceLock;
+ nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
+ RefPtr<IDXGIAdapter1> mAdapter;
+ RefPtr<ID3D11Device> mCompositorDevice;
+ RefPtr<ID3D11Device> mContentDevice;
+ RefPtr<ID3D11Device> mDecoderDevice;
+ bool mCompositorDeviceSupportsVideo;
+
+ Maybe<D3D11DeviceStatus> mDeviceStatus;
+
+ nsModuleHandle mDirectDrawDLL;
+ RefPtr<IDirectDraw7> mDirectDraw;
+
+ Maybe<DeviceResetReason> mDeviceResetReason;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_thebes_DeviceManagerDx_h
diff --git a/system/graphics/thebes/DrawMode.h b/system/graphics/thebes/DrawMode.h
new file mode 100644
index 000000000..139dae4b0
--- /dev/null
+++ b/system/graphics/thebes/DrawMode.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DrawMode_h
+#define DrawMode_h
+
+#include "mozilla/TypedEnumBits.h"
+
+// Options for how the text should be drawn
+enum class DrawMode : int {
+ // GLYPH_FILL and GLYPH_STROKE draw into the current context
+ // and may be used together with bitwise OR.
+ GLYPH_FILL = 1 << 0,
+ // Note: using GLYPH_STROKE will destroy the current path.
+ GLYPH_STROKE = 1 << 1,
+ // Appends glyphs to the current path. Can NOT be used with
+ // GLYPH_FILL or GLYPH_STROKE.
+ GLYPH_PATH = 1 << 2,
+ // When GLYPH_FILL and GLYPH_STROKE are both set, draws the
+ // stroke underneath the fill.
+ GLYPH_STROKE_UNDERNEATH = 1 << 3
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DrawMode)
+#endif
diff --git a/system/graphics/thebes/PrintTarget.cpp b/system/graphics/thebes/PrintTarget.cpp
new file mode 100644
index 000000000..a07751300
--- /dev/null
+++ b/system/graphics/thebes/PrintTarget.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTarget.h"
+
+#include "cairo.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTarget::PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize)
+ : mCairoSurface(aCairoSurface)
+ , mSize(aSize)
+ , mIsFinished(false)
+#ifdef DEBUG
+ , mHasActivePage(false)
+#endif
+
+{
+#if 0
+ // aCairoSurface is null when our PrintTargetThebes subclass's ctor calls us.
+ // Once PrintTargetThebes is removed, enable this assertion.
+ MOZ_ASSERT(aCairoSurface && !cairo_surface_status(aCairoSurface),
+ "CreateOrNull factory methods should not call us without a "
+ "valid cairo_surface_t*");
+#endif
+
+ // CreateOrNull factory methods hand over ownership of aCairoSurface,
+ // so we don't call cairo_surface_reference(aSurface) here.
+
+ // This code was copied from gfxASurface::Init:
+#ifdef MOZ_TREE_CAIRO
+ if (mCairoSurface &&
+ cairo_surface_get_content(mCairoSurface) != CAIRO_CONTENT_COLOR) {
+ cairo_surface_set_subpixel_antialiasing(mCairoSurface,
+ CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+ }
+#endif
+}
+
+PrintTarget::~PrintTarget()
+{
+ // null surfaces are allowed here
+ cairo_surface_destroy(mCairoSurface);
+ mCairoSurface = nullptr;
+}
+
+already_AddRefed<DrawTarget>
+PrintTarget::MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder)
+{
+ MOZ_ASSERT(mCairoSurface,
+ "We shouldn't have been constructed without a cairo surface");
+
+ // This should not be called outside of BeginPage()/EndPage() calls since
+ // some backends can only provide a valid DrawTarget at that time.
+ MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget");
+
+ if (cairo_surface_status(mCairoSurface)) {
+ return nullptr;
+ }
+
+ // Note than aSize may not be the same as mSize (the size of mCairoSurface).
+ // See the comments in our header. If the sizes are different a clip will
+ // be applied to mCairoSurface.
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForCairoSurface(mCairoSurface, aSize);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ if (aRecorder) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+
+ return dt.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTarget::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
+{
+ if (!mRefDT) {
+ IntSize size(1, 1);
+
+ cairo_surface_t* surface =
+ cairo_surface_create_similar(mCairoSurface,
+ cairo_surface_get_content(mCairoSurface),
+ size.width, size.height);
+
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForCairoSurface(surface, size);
+
+ // The DT addrefs the surface, so we need drop our own reference to it:
+ cairo_surface_destroy(surface);
+
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ if (aRecorder) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+
+ mRefDT = dt.forget();
+ }
+ return do_AddRef(mRefDT);
+}
+
+already_AddRefed<DrawTarget>
+PrintTarget::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+ DrawTarget* aDrawTarget)
+{
+ MOZ_ASSERT(aRecorder);
+ MOZ_ASSERT(aDrawTarget);
+
+ RefPtr<DrawTarget> dt;
+
+ if (aRecorder) {
+ // It doesn't really matter what we pass as the DrawTarget here.
+ dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
+ }
+
+ if (!dt || !dt->IsValid()) {
+ gfxCriticalNote
+ << "Failed to create a recording DrawTarget for PrintTarget";
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+void
+PrintTarget::Finish()
+{
+ if (mIsFinished) {
+ return;
+ }
+ mIsFinished = true;
+
+ // null surfaces are allowed here
+ cairo_surface_finish(mCairoSurface);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTarget.h b/system/graphics/thebes/PrintTarget.h
new file mode 100644
index 000000000..111981c70
--- /dev/null
+++ b/system/graphics/thebes/PrintTarget.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGET_H
+#define MOZILLA_GFX_PRINTTARGET_H
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawEventRecorder;
+
+/**
+ * A class that is used to draw output that is to be sent to a printer or print
+ * preview.
+ *
+ * This class wraps a cairo_surface_t* and provides access to it via a
+ * DrawTarget. The various checkpointing methods manage the state of the
+ * platform specific cairo_surface_t*.
+ */
+class PrintTarget {
+public:
+
+ NS_INLINE_DECL_REFCOUNTING(PrintTarget);
+
+ /// Must be matched 1:1 by an EndPrinting/AbortPrinting call.
+ virtual nsresult BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName) {
+ return NS_OK;
+ }
+ virtual nsresult EndPrinting() {
+ return NS_OK;
+ }
+ virtual nsresult AbortPrinting() {
+#ifdef DEBUG
+ mHasActivePage = false;
+#endif
+ return NS_OK;
+ }
+ virtual nsresult BeginPage() {
+#ifdef DEBUG
+ MOZ_ASSERT(!mHasActivePage, "Missing EndPage() call");
+ mHasActivePage = true;
+#endif
+ return NS_OK;
+ }
+ virtual nsresult EndPage() {
+#ifdef DEBUG
+ mHasActivePage = false;
+#endif
+ return NS_OK;
+ }
+
+ /**
+ * Releases the resources used by this PrintTarget. Typically this should be
+ * called after calling EndPrinting(). Calling this more than once is
+ * allowed, but subsequent calls are a no-op.
+ *
+ * Note that any DrawTarget obtained from this PrintTarget will no longer be
+ * useful after this method has been called.
+ */
+ virtual void Finish();
+
+ /**
+ * Returns true if to print landscape our consumers must apply a 90 degrees
+ * rotation to our DrawTarget.
+ */
+ virtual bool RotateNeededForLandscape() const {
+ return false;
+ }
+
+ const IntSize& GetSize() const {
+ return mSize;
+ }
+
+ /**
+ * Makes a DrawTarget to draw the printer output to, or returns null on
+ * failure.
+ *
+ * If aRecorder is passed a recording DrawTarget will be created instead of
+ * the type of DrawTarget that would normally be returned for a particular
+ * subclass of this class. This argument is only intended to be used in
+ * the e10s content process if printing output can't otherwise be transfered
+ * over to the parent process using the normal DrawTarget type.
+ *
+ * NOTE: this should only be called between BeginPage()/EndPage() calls, and
+ * the returned DrawTarget should not be drawn to after EndPage() has been
+ * called.
+ *
+ * XXX For consistency with the old code this takes a size parameter even
+ * though we already have the size passed to our subclass's CreateOrNull
+ * factory methods. The size passed to the factory method comes from
+ * nsIDeviceContextSpec::MakePrintTarget overrides, whereas the size
+ * passed to us comes from nsDeviceContext::CreateRenderingContext. In at
+ * least one case (nsDeviceContextSpecAndroid::MakePrintTarget) these are
+ * different. At some point we should align the two sources and get rid of
+ * this method's size parameter.
+ *
+ * XXX For consistency with the old code this returns a new DrawTarget for
+ * each call. Perhaps we can create and cache a DrawTarget in our subclass's
+ * CreateOrNull factory methods and return that on each call? Currently that
+ * seems to cause Mochitest failures on Windows though, which coincidentally
+ * is the only platform where we get passed an aRecorder. Probably the
+ * issue is that we get called more than once with a different aRecorder, so
+ * storing one recording DrawTarget for our lifetime doesn't currently work.
+ *
+ * XXX Could we pass aRecorder to our subclass's CreateOrNull factory methods?
+ * We'd need to check that our consumers always pass the same aRecorder for
+ * our entire lifetime.
+ *
+ * XXX Once PrintTargetThebes is removed this can become non-virtual.
+ *
+ * XXX In the long run, this class and its sub-classes should be converted to
+ * use STL classes and mozilla::RefCounted<> so the can be moved to Moz2D.
+ *
+ * TODO: Consider adding a SetDPI method that calls
+ * cairo_surface_set_fallback_resolution.
+ */
+ virtual already_AddRefed<DrawTarget>
+ MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder = nullptr);
+
+ /**
+ * Returns a reference DrawTarget. Unlike MakeDrawTarget, this method is not
+ * restricted to being called between BeginPage()/EndPage() calls, and the
+ * returned DrawTarget it is still valid to use after EndPage() has been
+ * called.
+ */
+ virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder);
+
+protected:
+
+ // Only created via subclass's constructors
+ explicit PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize);
+
+ // Protected because we're refcounted
+ virtual ~PrintTarget();
+
+ already_AddRefed<DrawTarget>
+ CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+ DrawTarget* aDrawTarget);
+
+ cairo_surface_t* mCairoSurface;
+ RefPtr<DrawTarget> mRefDT; // reference DT
+ IntSize mSize;
+ bool mIsFinished;
+#ifdef DEBUG
+ bool mHasActivePage;
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGET_H */
diff --git a/system/graphics/thebes/PrintTargetPDF.cpp b/system/graphics/thebes/PrintTargetPDF.cpp
new file mode 100644
index 000000000..ce536c07f
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetPDF.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetPDF.h"
+
+#include "cairo.h"
+#include "cairo-pdf.h"
+
+namespace mozilla {
+namespace gfx {
+
+static cairo_status_t
+write_func(void *closure, const unsigned char *data, unsigned int length)
+{
+ nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
+ do {
+ uint32_t wrote = 0;
+ if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
+ break;
+ }
+ data += wrote; length -= wrote;
+ } while (length > 0);
+ NS_ASSERTION(length == 0, "not everything was written to the file");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+PrintTargetPDF::PrintTargetPDF(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ nsIOutputStream *aStream)
+ : PrintTarget(aCairoSurface, aSize)
+ , mStream(aStream)
+{
+}
+
+PrintTargetPDF::~PrintTargetPDF()
+{
+ // We get called first, then PrintTarget's dtor. That means that mStream
+ // is destroyed before PrintTarget's dtor calls cairo_surface_destroy. This
+ // can be a problem if Finish() hasn't been called on us, since
+ // cairo_surface_destroy will then call cairo_surface_finish and that will
+ // end up invoking write_func above with the by now dangling pointer mStream
+ // that mCairoSurface stored. To prevent that from happening we must call
+ // Flush here before mStream is deleted.
+ Finish();
+}
+
+/* static */ already_AddRefed<PrintTargetPDF>
+PrintTargetPDF::CreateOrNull(nsIOutputStream *aStream,
+ const IntSize& aSizeInPoints)
+{
+ cairo_surface_t* surface =
+ cairo_pdf_surface_create_for_stream(write_func, (void*)aStream,
+ aSizeInPoints.width,
+ aSizeInPoints.height);
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetPDF> target = new PrintTargetPDF(surface, aSizeInPoints,
+ aStream);
+ return target.forget();
+}
+
+nsresult
+PrintTargetPDF::EndPage()
+{
+ cairo_surface_show_page(mCairoSurface);
+ return NS_OK;
+}
+
+void
+PrintTargetPDF::Finish()
+{
+ if (mIsFinished) {
+ return; // We don't want to call Close() on mStream more than once
+ }
+ PrintTarget::Finish();
+ mStream->Close();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTargetPDF.h b/system/graphics/thebes/PrintTargetPDF.h
new file mode 100644
index 000000000..dd338c626
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetPDF.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETPDF_H
+#define MOZILLA_GFX_PRINTTARGETPDF_H
+
+#include "nsCOMPtr.h"
+#include "nsIOutputStream.h"
+#include "PrintTarget.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * PDF printing target.
+ */
+class PrintTargetPDF final : public PrintTarget
+{
+public:
+ static already_AddRefed<PrintTargetPDF>
+ CreateOrNull(nsIOutputStream *aStream,
+ const IntSize& aSizeInPoints);
+
+ virtual nsresult EndPage() override;
+ virtual void Finish() override;
+
+private:
+ PrintTargetPDF(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ nsIOutputStream *aStream);
+ virtual ~PrintTargetPDF();
+
+ nsCOMPtr<nsIOutputStream> mStream;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETPDF_H */
diff --git a/system/graphics/thebes/PrintTargetPS.cpp b/system/graphics/thebes/PrintTargetPS.cpp
new file mode 100644
index 000000000..5ea75a56c
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetPS.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetPS.h"
+
+#include "cairo.h"
+#include "cairo-ps.h"
+
+namespace mozilla {
+namespace gfx {
+
+static cairo_status_t
+write_func(void *closure, const unsigned char *data, unsigned int length)
+{
+ nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
+ do {
+ uint32_t wrote = 0;
+ if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
+ break;
+ }
+ data += wrote; length -= wrote;
+ } while (length > 0);
+ NS_ASSERTION(length == 0, "not everything was written to the file");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+PrintTargetPS::PrintTargetPS(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ nsIOutputStream *aStream,
+ PageOrientation aOrientation)
+ : PrintTarget(aCairoSurface, aSize)
+ , mStream(aStream)
+ , mOrientation(aOrientation)
+{
+}
+
+PrintTargetPS::~PrintTargetPS()
+{
+ // We get called first, then PrintTarget's dtor. That means that mStream
+ // is destroyed before PrintTarget's dtor calls cairo_surface_destroy. This
+ // can be a problem if Finish() hasn't been called on us, since
+ // cairo_surface_destroy will then call cairo_surface_finish and that will
+ // end up invoking write_func above with the by now dangling pointer mStream
+ // that mCairoSurface stored. To prevent that from happening we must call
+ // Flush here before mStream is deleted.
+ Finish();
+}
+
+/* static */ already_AddRefed<PrintTargetPS>
+PrintTargetPS::CreateOrNull(nsIOutputStream *aStream,
+ IntSize aSizeInPoints,
+ PageOrientation aOrientation)
+{
+ // The PS output does not specify the page size so to print landscape we need
+ // to rotate the drawing 90 degrees and print on portrait paper. If printing
+ // landscape, swap the width/height supplied to cairo to select a portrait
+ // print area. Our consumers are responsible for checking
+ // RotateForLandscape() and applying a rotation transform if true.
+ if (aOrientation == LANDSCAPE) {
+ Swap(aSizeInPoints.width, aSizeInPoints.height);
+ }
+
+ cairo_surface_t* surface =
+ cairo_ps_surface_create_for_stream(write_func, (void*)aStream,
+ aSizeInPoints.width,
+ aSizeInPoints.height);
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+ cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2);
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetPS> target = new PrintTargetPS(surface, aSizeInPoints,
+ aStream, aOrientation);
+ return target.forget();
+}
+
+nsresult
+PrintTargetPS::BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName)
+{
+ if (mOrientation == PORTRAIT) {
+ cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Portrait");
+ } else {
+ cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Landscape");
+ }
+ return NS_OK;
+}
+
+nsresult
+PrintTargetPS::EndPage()
+{
+ cairo_surface_show_page(mCairoSurface);
+ return NS_OK;
+}
+
+void
+PrintTargetPS::Finish()
+{
+ if (mIsFinished) {
+ return; // We don't want to call Close() on mStream more than once
+ }
+ PrintTarget::Finish();
+ mStream->Close();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTargetPS.h b/system/graphics/thebes/PrintTargetPS.h
new file mode 100644
index 000000000..68c1bd00b
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetPS.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTINGTARGETPS_H
+#define MOZILLA_GFX_PRINTINGTARGETPS_H
+
+#include "nsCOMPtr.h"
+#include "nsIOutputStream.h"
+#include "PrintTarget.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * PostScript printing target.
+ */
+class PrintTargetPS final : public PrintTarget {
+public:
+ enum PageOrientation {
+ PORTRAIT,
+ LANDSCAPE
+ };
+
+ static already_AddRefed<PrintTargetPS>
+ CreateOrNull(nsIOutputStream *aStream,
+ IntSize aSizeInPoints,
+ PageOrientation aOrientation);
+
+ virtual nsresult BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName) override;
+ virtual nsresult EndPage() override;
+ virtual void Finish() override;
+
+ virtual bool GetRotateForLandscape() {
+ return (mOrientation == LANDSCAPE);
+ }
+
+private:
+ PrintTargetPS(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ nsIOutputStream *aStream,
+ PageOrientation aOrientation);
+ virtual ~PrintTargetPS();
+
+ nsCOMPtr<nsIOutputStream> mStream;
+ PageOrientation mOrientation;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTINGTARGETPS_H */
diff --git a/system/graphics/thebes/PrintTargetRecording.cpp b/system/graphics/thebes/PrintTargetRecording.cpp
new file mode 100644
index 000000000..e0df17a86
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetRecording.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetRecording.h"
+
+#include "cairo.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTargetRecording::PrintTargetRecording(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize)
+ : PrintTarget(aCairoSurface, aSize)
+{
+}
+
+/* static */ already_AddRefed<PrintTargetRecording>
+PrintTargetRecording::CreateOrNull(const IntSize& aSize)
+{
+ if (!Factory::CheckSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ // Perhaps surprisingly, this surface is never actually drawn to. This class
+ // creates a DrawTargetRecording using CreateRecordingDrawTarget, and that
+ // needs another DrawTarget to be passed to it. You might expect the type of
+ // the DrawTarget that is passed to matter because it would seem logical to
+ // encoded its type in the recording, and on replaying the recording a
+ // DrawTarget of the same type would be created. However, the passed
+ // DrawTarget's type doesn't seem to be encoded any more accurately than just
+ // "BackendType::CAIRO". Even if it were, the code that replays the
+ // recording is PrintTranslator::TranslateRecording which (indirectly) calls
+ // MakePrintTarget on the type of nsIDeviceContextSpecProxy that is created
+ // for the platform that we're running on, and the type of DrawTarget that
+ // that returns is hardcoded.
+ //
+ // The only reason that we use cairo_recording_surface_create here is:
+ //
+ // * It's pretty much the only cairo_*_surface_create methods that's both
+ // available on all platforms and doesn't require allocating a
+ // potentially large surface.
+ //
+ // * Since we need a DrawTarget to pass to CreateRecordingDrawTarget we
+ // might as well leverage our base class's machinery to create a
+ // DrawTarget (it's as good a way as any other that will work), and to do
+ // that we need a cairo_surface_t.
+ //
+ // So the fact that this is a "recording" PrintTarget and the function that
+ // we call here is cairo_recording_surface_create is simply a coincidence. We
+ // could use any cairo_*_surface_create method and this class would still
+ // work.
+ //
+ cairo_surface_t* surface =
+ cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, nullptr);
+
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetRecording> target =
+ new PrintTargetRecording(surface, aSize);
+
+ return target.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetRecording::MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder)
+{
+ MOZ_ASSERT(aRecorder, "A DrawEventRecorder is required");
+
+ if (!aRecorder) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt = PrintTarget::MakeDrawTarget(aSize, nullptr);
+ if (dt) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+
+ return dt.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetRecording::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+ DrawTarget* aDrawTarget)
+{
+ MOZ_ASSERT(aRecorder);
+ MOZ_ASSERT(aDrawTarget);
+
+ RefPtr<DrawTarget> dt;
+
+ if (aRecorder) {
+ // It doesn't really matter what we pass as the DrawTarget here.
+ dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
+ }
+
+ if (!dt || !dt->IsValid()) {
+ gfxCriticalNote
+ << "Failed to create a recording DrawTarget for PrintTarget";
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTargetRecording.h b/system/graphics/thebes/PrintTargetRecording.h
new file mode 100644
index 000000000..c6512ae99
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetRecording.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETRECORDING_H
+#define MOZILLA_GFX_PRINTTARGETRECORDING_H
+
+#include "PrintTarget.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Recording printing target.
+ *
+ * This exists for use on e10s's content process in order to record print
+ * output, send it over to the parent process, and replay it on a DrawTarget
+ * there for printing.
+ */
+class PrintTargetRecording final : public PrintTarget
+{
+public:
+ static already_AddRefed<PrintTargetRecording>
+ CreateOrNull(const IntSize& aSize);
+
+ virtual already_AddRefed<DrawTarget>
+ MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder = nullptr) override;
+
+private:
+ PrintTargetRecording(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize);
+
+ already_AddRefed<DrawTarget>
+ CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
+ DrawTarget* aDrawTarget);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETRECORDING_H */
diff --git a/system/graphics/thebes/PrintTargetThebes.cpp b/system/graphics/thebes/PrintTargetThebes.cpp
new file mode 100644
index 000000000..1cacf098f
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetThebes.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetThebes.h"
+
+#include "gfxASurface.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+/* static */ already_AddRefed<PrintTargetThebes>
+PrintTargetThebes::CreateOrNull(gfxASurface* aSurface)
+{
+ MOZ_ASSERT(aSurface);
+
+ if (!aSurface || aSurface->CairoStatus()) {
+ return nullptr;
+ }
+
+ RefPtr<PrintTargetThebes> target = new PrintTargetThebes(aSurface);
+
+ return target.forget();
+}
+
+PrintTargetThebes::PrintTargetThebes(gfxASurface* aSurface)
+ : PrintTarget(nullptr, aSurface->GetSize())
+ , mGfxSurface(aSurface)
+{
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetThebes::MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder)
+{
+ // This should not be called outside of BeginPage()/EndPage() calls since
+ // some backends can only provide a valid DrawTarget at that time.
+ MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget");
+
+ RefPtr<gfx::DrawTarget> dt =
+ gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, aSize);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ if (aRecorder) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+
+ return dt.forget();
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetThebes::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
+{
+ if (!mRefDT) {
+ RefPtr<gfx::DrawTarget> dt =
+ gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, mSize);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ if (aRecorder) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+ mRefDT = dt->CreateSimilarDrawTarget(IntSize(1,1), dt->GetFormat());
+ }
+ return do_AddRef(mRefDT);
+}
+
+nsresult
+PrintTargetThebes::BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName)
+{
+ return mGfxSurface->BeginPrinting(aTitle, aPrintToFileName);
+}
+
+nsresult
+PrintTargetThebes::EndPrinting()
+{
+ return mGfxSurface->EndPrinting();
+}
+
+nsresult
+PrintTargetThebes::AbortPrinting()
+{
+#ifdef DEBUG
+ mHasActivePage = false;
+#endif
+ return mGfxSurface->AbortPrinting();
+}
+
+nsresult
+PrintTargetThebes::BeginPage()
+{
+#ifdef DEBUG
+ mHasActivePage = true;
+#endif
+ return mGfxSurface->BeginPage();
+}
+
+nsresult
+PrintTargetThebes::EndPage()
+{
+#ifdef DEBUG
+ mHasActivePage = false;
+#endif
+ return mGfxSurface->EndPage();
+}
+
+void
+PrintTargetThebes::Finish()
+{
+ return mGfxSurface->Finish();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTargetThebes.h b/system/graphics/thebes/PrintTargetThebes.h
new file mode 100644
index 000000000..aae3c9285
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetThebes.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETTHEBES_H
+#define MOZILLA_GFX_PRINTTARGETTHEBES_H
+
+#include "mozilla/gfx/PrintTarget.h"
+
+class gfxASurface;
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * XXX Remove this class.
+ *
+ * This class should go away once all the logic from the gfxASurface subclasses
+ * has been moved to new PrintTarget subclasses and we no longer need to
+ * wrap a gfxASurface.
+ *
+ * When removing this class, be sure to make PrintTarget::MakeDrawTarget
+ * non-virtual!
+ */
+class PrintTargetThebes final : public PrintTarget {
+public:
+
+ static already_AddRefed<PrintTargetThebes>
+ CreateOrNull(gfxASurface* aSurface);
+
+ virtual nsresult BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName) override;
+ virtual nsresult EndPrinting() override;
+ virtual nsresult AbortPrinting() override;
+ virtual nsresult BeginPage() override;
+ virtual nsresult EndPage() override;
+ virtual void Finish() override;
+
+ virtual already_AddRefed<DrawTarget>
+ MakeDrawTarget(const IntSize& aSize,
+ DrawEventRecorder* aRecorder = nullptr) override;
+
+ virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
+
+private:
+
+ // Only created via CreateOrNull
+ explicit PrintTargetThebes(gfxASurface* aSurface);
+
+ RefPtr<gfxASurface> mGfxSurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETTHEBES_H */
diff --git a/system/graphics/thebes/PrintTargetWindows.cpp b/system/graphics/thebes/PrintTargetWindows.cpp
new file mode 100644
index 000000000..5867a4772
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetWindows.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetWindows.h"
+
+#include "cairo-win32.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "nsCoord.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTargetWindows::PrintTargetWindows(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ HDC aDC)
+ : PrintTarget(aCairoSurface, aSize)
+ , mDC(aDC)
+{
+ // TODO: At least add basic memory reporting.
+ // 4 * mSize.width * mSize.height + sizeof(PrintTargetWindows) ?
+}
+
+/* static */ already_AddRefed<PrintTargetWindows>
+PrintTargetWindows::CreateOrNull(HDC aDC)
+{
+#ifdef NS_PRINTING
+ // Figure out the cairo surface size - Windows we need to use the printable
+ // area of the page. Note: we only scale the printing using the LOGPIXELSY,
+ // so we use that when calculating the surface width as well as the height.
+ int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
+ float width =
+ (::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
+ float height =
+ (::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
+ IntSize size = IntSize::Truncate(width, height);
+
+ if (!Factory::CheckSurfaceSize(size)) {
+ return nullptr;
+ }
+
+ cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
+
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetWindows> target =
+ new PrintTargetWindows(surface, size, aDC);
+
+ return target.forget();
+#else
+ return nullptr;
+#endif
+}
+
+nsresult
+PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName)
+{
+ const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1;
+
+ DOCINFOW docinfo;
+
+ nsString titleStr(aTitle);
+ if (titleStr.Length() > DOC_TITLE_LENGTH) {
+ titleStr.SetLength(DOC_TITLE_LENGTH - 3);
+ titleStr.AppendLiteral("...");
+ }
+
+ nsString docName(aPrintToFileName);
+ docinfo.cbSize = sizeof(docinfo);
+ docinfo.lpszDocName = titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document";
+ docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr;
+ docinfo.lpszDatatype = nullptr;
+ docinfo.fwType = 0;
+
+ ::StartDocW(mDC, &docinfo);
+
+ return NS_OK;
+}
+
+nsresult
+PrintTargetWindows::EndPrinting()
+{
+ int result = ::EndDoc(mDC);
+ return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+nsresult
+PrintTargetWindows::AbortPrinting()
+{
+ int result = ::AbortDoc(mDC);
+ return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+nsresult
+PrintTargetWindows::BeginPage()
+{
+ int result = ::StartPage(mDC);
+ return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+nsresult
+PrintTargetWindows::EndPage()
+{
+ cairo_surface_show_page(mCairoSurface);
+ int result = ::EndPage(mDC);
+ return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/PrintTargetWindows.h b/system/graphics/thebes/PrintTargetWindows.h
new file mode 100644
index 000000000..4f53b3949
--- /dev/null
+++ b/system/graphics/thebes/PrintTargetWindows.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETWINDOWS_H
+#define MOZILLA_GFX_PRINTTARGETWINDOWS_H
+
+#include "PrintTarget.h"
+
+/* include windows.h for the HDC definitions that we need. */
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Windows printing target.
+ */
+class PrintTargetWindows final : public PrintTarget
+{
+public:
+ static already_AddRefed<PrintTargetWindows>
+ CreateOrNull(HDC aDC);
+
+ virtual nsresult BeginPrinting(const nsAString& aTitle,
+ const nsAString& aPrintToFileName) override;
+ virtual nsresult EndPrinting() override;
+ virtual nsresult AbortPrinting() override;
+ virtual nsresult BeginPage() override;
+ virtual nsresult EndPage() override;
+
+private:
+ PrintTargetWindows(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize,
+ HDC aDC);
+ HDC mDC;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETWINDOWS_H */
diff --git a/system/graphics/thebes/RoundedRect.h b/system/graphics/thebes/RoundedRect.h
new file mode 100644
index 000000000..6da69275f
--- /dev/null
+++ b/system/graphics/thebes/RoundedRect.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxRect.h"
+#include "mozilla/gfx/PathHelpers.h"
+
+namespace mozilla {
+/* A rounded rectangle abstraction.
+ *
+ * This can represent a rectangle with a different pair of radii on each corner.
+ *
+ * Note: CoreGraphics and Direct2D only support rounded rectangle with the same
+ * radii on all corners. However, supporting CSS's border-radius requires the extra flexibility. */
+struct RoundedRect {
+ typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
+
+ RoundedRect(gfxRect &aRect, RectCornerRadii &aCorners) : rect(aRect), corners(aCorners) { }
+ void Deflate(gfxFloat aTopWidth, gfxFloat aBottomWidth, gfxFloat aLeftWidth, gfxFloat aRightWidth) {
+ // deflate the internal rect
+ rect.x += aLeftWidth;
+ rect.y += aTopWidth;
+ rect.width = std::max(0., rect.width - aLeftWidth - aRightWidth);
+ rect.height = std::max(0., rect.height - aTopWidth - aBottomWidth);
+
+ corners.radii[NS_CORNER_TOP_LEFT].width = std::max(0., corners.radii[NS_CORNER_TOP_LEFT].width - aLeftWidth);
+ corners.radii[NS_CORNER_TOP_LEFT].height = std::max(0., corners.radii[NS_CORNER_TOP_LEFT].height - aTopWidth);
+
+ corners.radii[NS_CORNER_TOP_RIGHT].width = std::max(0., corners.radii[NS_CORNER_TOP_RIGHT].width - aRightWidth);
+ corners.radii[NS_CORNER_TOP_RIGHT].height = std::max(0., corners.radii[NS_CORNER_TOP_RIGHT].height - aTopWidth);
+
+ corners.radii[NS_CORNER_BOTTOM_LEFT].width = std::max(0., corners.radii[NS_CORNER_BOTTOM_LEFT].width - aLeftWidth);
+ corners.radii[NS_CORNER_BOTTOM_LEFT].height = std::max(0., corners.radii[NS_CORNER_BOTTOM_LEFT].height - aBottomWidth);
+
+ corners.radii[NS_CORNER_BOTTOM_RIGHT].width = std::max(0., corners.radii[NS_CORNER_BOTTOM_RIGHT].width - aRightWidth);
+ corners.radii[NS_CORNER_BOTTOM_RIGHT].height = std::max(0., corners.radii[NS_CORNER_BOTTOM_RIGHT].height - aBottomWidth);
+ }
+ gfxRect rect;
+ RectCornerRadii corners;
+};
+
+} // namespace mozilla
diff --git a/system/graphics/thebes/SoftwareVsyncSource.cpp b/system/graphics/thebes/SoftwareVsyncSource.cpp
new file mode 100644
index 000000000..7e92b89ec
--- /dev/null
+++ b/system/graphics/thebes/SoftwareVsyncSource.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SoftwareVsyncSource.h"
+#include "base/task.h"
+#include "gfxPlatform.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+SoftwareVsyncSource::SoftwareVsyncSource()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mGlobalDisplay = new SoftwareDisplay();
+}
+
+SoftwareVsyncSource::~SoftwareVsyncSource()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mGlobalDisplay = nullptr;
+}
+
+SoftwareDisplay::SoftwareDisplay()
+ : mVsyncEnabled(false)
+{
+ // Mimic 60 fps
+ MOZ_ASSERT(NS_IsMainThread());
+ const double rate = 1000.0 / (double) gfxPlatform::GetSoftwareVsyncRate();
+ mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
+ mVsyncThread = new base::Thread("SoftwareVsyncThread");
+ MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "GFX: Could not start software vsync thread");
+}
+
+SoftwareDisplay::~SoftwareDisplay() {}
+
+void
+SoftwareDisplay::EnableVsync()
+{
+ MOZ_ASSERT(mVsyncThread->IsRunning());
+ if (NS_IsMainThread()) {
+ if (mVsyncEnabled) {
+ return;
+ }
+ mVsyncEnabled = true;
+
+ mVsyncThread->message_loop()->PostTask(
+ NewRunnableMethod(this, &SoftwareDisplay::EnableVsync));
+ return;
+ }
+
+ MOZ_ASSERT(IsInSoftwareVsyncThread());
+ NotifyVsync(mozilla::TimeStamp::Now());
+}
+
+void
+SoftwareDisplay::DisableVsync()
+{
+ MOZ_ASSERT(mVsyncThread->IsRunning());
+ if (NS_IsMainThread()) {
+ if (!mVsyncEnabled) {
+ return;
+ }
+ mVsyncEnabled = false;
+
+ mVsyncThread->message_loop()->PostTask(
+ NewRunnableMethod(this, &SoftwareDisplay::DisableVsync));
+ return;
+ }
+
+ MOZ_ASSERT(IsInSoftwareVsyncThread());
+ if (mCurrentVsyncTask) {
+ mCurrentVsyncTask->Cancel();
+ mCurrentVsyncTask = nullptr;
+ }
+}
+
+bool
+SoftwareDisplay::IsVsyncEnabled()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVsyncEnabled;
+}
+
+bool
+SoftwareDisplay::IsInSoftwareVsyncThread()
+{
+ return mVsyncThread->thread_id() == PlatformThread::CurrentId();
+}
+
+void
+SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)
+{
+ MOZ_ASSERT(IsInSoftwareVsyncThread());
+
+ mozilla::TimeStamp displayVsyncTime = aVsyncTimestamp;
+ mozilla::TimeStamp now = mozilla::TimeStamp::Now();
+ // Posted tasks can only have integer millisecond delays
+ // whereas TimeDurations can have floating point delays.
+ // Thus the vsync timestamp can be in the future, which large parts
+ // of the system can't handle, including animations. Force the timestamp to be now.
+ if (aVsyncTimestamp > now) {
+ displayVsyncTime = now;
+ }
+
+ Display::NotifyVsync(displayVsyncTime);
+
+ // Prevent skew by still scheduling based on the original
+ // vsync timestamp
+ ScheduleNextVsync(aVsyncTimestamp);
+}
+
+mozilla::TimeDuration
+SoftwareDisplay::GetVsyncRate()
+{
+ return mVsyncRate;
+}
+
+void
+SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp)
+{
+ MOZ_ASSERT(IsInSoftwareVsyncThread());
+ mozilla::TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
+ mozilla::TimeDuration delay = nextVsync - mozilla::TimeStamp::Now();
+ if (delay.ToMilliseconds() < 0) {
+ delay = mozilla::TimeDuration::FromMilliseconds(0);
+ nextVsync = mozilla::TimeStamp::Now();
+ }
+
+ mCurrentVsyncTask =
+ NewCancelableRunnableMethod<mozilla::TimeStamp>(this,
+ &SoftwareDisplay::NotifyVsync,
+ nextVsync);
+
+ RefPtr<Runnable> addrefedTask = mCurrentVsyncTask;
+ mVsyncThread->message_loop()->PostDelayedTask(
+ addrefedTask.forget(),
+ delay.ToMilliseconds());
+}
+
+void
+SoftwareDisplay::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ DisableVsync();
+ mVsyncThread->Stop();
+ delete mVsyncThread;
+}
diff --git a/system/graphics/thebes/SoftwareVsyncSource.h b/system/graphics/thebes/SoftwareVsyncSource.h
new file mode 100644
index 000000000..c86b2568a
--- /dev/null
+++ b/system/graphics/thebes/SoftwareVsyncSource.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef GFX_SOFTWARE_VSYNC_SOURCE_H
+#define GFX_SOFTWARE_VSYNC_SOURCE_H
+
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "base/thread.h"
+#include "nsISupportsImpl.h"
+#include "VsyncSource.h"
+
+class SoftwareDisplay final : public mozilla::gfx::VsyncSource::Display
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SoftwareDisplay)
+
+public:
+ SoftwareDisplay();
+ virtual void EnableVsync() override;
+ virtual void DisableVsync() override;
+ virtual bool IsVsyncEnabled() override;
+ bool IsInSoftwareVsyncThread();
+ virtual void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) override;
+ virtual mozilla::TimeDuration GetVsyncRate() override;
+ void ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp);
+ void Shutdown() override;
+
+protected:
+ ~SoftwareDisplay();
+
+private:
+ mozilla::TimeDuration mVsyncRate;
+ // Use a chromium thread because nsITimers* fire on the main thread
+ base::Thread* mVsyncThread;
+ RefPtr<mozilla::CancelableRunnable> mCurrentVsyncTask; // only access on vsync thread
+ bool mVsyncEnabled; // Only access on main thread
+}; // SoftwareDisplay
+
+// Fallback option to use a software timer to mimic vsync. Useful for gtests
+// To mimic a hardware vsync thread, we create a dedicated software timer
+// vsync thread.
+class SoftwareVsyncSource : public mozilla::gfx::VsyncSource
+{
+public:
+ SoftwareVsyncSource();
+ ~SoftwareVsyncSource();
+
+ virtual Display& GetGlobalDisplay() override
+ {
+ MOZ_ASSERT(mGlobalDisplay != nullptr);
+ return *mGlobalDisplay;
+ }
+
+private:
+ RefPtr<SoftwareDisplay> mGlobalDisplay;
+};
+
+#endif /* GFX_SOFTWARE_VSYNC_SOURCE_H */
diff --git a/system/graphics/thebes/VsyncSource.cpp b/system/graphics/thebes/VsyncSource.cpp
new file mode 100644
index 000000000..cb69db560
--- /dev/null
+++ b/system/graphics/thebes/VsyncSource.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VsyncSource.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "MainThreadUtils.h"
+
+namespace mozilla {
+namespace gfx {
+
+void
+VsyncSource::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ // Just use the global display until we have enough information to get the
+ // corresponding display for compositor.
+ GetGlobalDisplay().AddCompositorVsyncDispatcher(aCompositorVsyncDispatcher);
+}
+
+void
+VsyncSource::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ // See also AddCompositorVsyncDispatcher().
+ GetGlobalDisplay().RemoveCompositorVsyncDispatcher(aCompositorVsyncDispatcher);
+}
+
+RefPtr<RefreshTimerVsyncDispatcher>
+VsyncSource::GetRefreshTimerVsyncDispatcher()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ // See also AddCompositorVsyncDispatcher().
+ return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
+}
+
+VsyncSource::Display::Display()
+ : mDispatcherLock("display dispatcher lock")
+ , mRefreshTimerNeedsVsync(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
+}
+
+VsyncSource::Display::~Display()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mDispatcherLock);
+ mRefreshTimerVsyncDispatcher = nullptr;
+ mCompositorVsyncDispatchers.Clear();
+}
+
+void
+VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+ // Called on the vsync thread
+ MutexAutoLock lock(mDispatcherLock);
+
+ for (size_t i = 0; i < mCompositorVsyncDispatchers.Length(); i++) {
+ mCompositorVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp);
+ }
+
+ mRefreshTimerVsyncDispatcher->NotifyVsync(aVsyncTimestamp);
+}
+
+TimeDuration
+VsyncSource::Display::GetVsyncRate()
+{
+ // If hardware queries fail / are unsupported, we have to just guess.
+ return TimeDuration::FromMilliseconds(1000.0 / 60.0);
+}
+
+void
+VsyncSource::Display::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
+ mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
+ }
+ }
+ UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // Scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ if (mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
+ mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
+ }
+ }
+ UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mRefreshTimerNeedsVsync = aEnable;
+ UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::UpdateVsyncStatus()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
+ // NotifyVsync grabs a lock to dispatch vsync events
+ // When disabling vsync, we wait for the underlying thread to stop on some platforms
+ // We can deadlock if we wait for the underlying vsync thread to stop
+ // while the vsync thread is in NotifyVsync.
+ bool enableVsync = false;
+ { // scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync;
+ }
+
+ if (enableVsync) {
+ EnableVsync();
+ } else {
+ DisableVsync();
+ }
+
+ if (IsVsyncEnabled() != enableVsync) {
+ NS_WARNING("Vsync status did not change.");
+ }
+}
+
+RefPtr<RefreshTimerVsyncDispatcher>
+VsyncSource::Display::GetRefreshTimerVsyncDispatcher()
+{
+ return mRefreshTimerVsyncDispatcher;
+}
+
+void
+VsyncSource::Shutdown()
+{
+ GetGlobalDisplay().Shutdown();
+}
+
+} //namespace gfx
+} //namespace mozilla
diff --git a/system/graphics/thebes/VsyncSource.h b/system/graphics/thebes/VsyncSource.h
new file mode 100644
index 000000000..317f31134
--- /dev/null
+++ b/system/graphics/thebes/VsyncSource.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VSYNCSOURCE_H
+#define GFX_VSYNCSOURCE_H
+
+#include "nsTArray.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+class RefreshTimerVsyncDispatcher;
+class CompositorVsyncDispatcher;
+
+namespace gfx {
+
+// Controls how and when to enable/disable vsync. Lives as long as the
+// gfxPlatform does on the parent process
+class VsyncSource
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource)
+
+ typedef mozilla::RefreshTimerVsyncDispatcher RefreshTimerVsyncDispatcher;
+ typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
+
+public:
+ // Controls vsync unique to each display and unique on each platform
+ class Display {
+ public:
+ Display();
+ virtual ~Display();
+
+ // Notified when this display's vsync occurs, on the vsync thread
+ // The aVsyncTimestamp should normalize to the Vsync time that just occured
+ // However, different platforms give different vsync notification times.
+ // b2g - The vsync timestamp of the previous frame that was just displayed
+ // OSX - The vsync timestamp of the upcoming frame, in the future
+ // Windows: It's messy, see gfxWindowsPlatform.
+ // Android: TODO
+ // All platforms should normalize to the vsync that just occured.
+ // Large parts of Gecko assume TimeStamps should not be in the future such as animations
+ virtual void NotifyVsync(TimeStamp aVsyncTimestamp);
+
+ RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
+
+ void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+ void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+ void NotifyRefreshTimerVsyncStatus(bool aEnable);
+ virtual TimeDuration GetVsyncRate();
+
+ // These should all only be called on the main thread
+ virtual void EnableVsync() = 0;
+ virtual void DisableVsync() = 0;
+ virtual bool IsVsyncEnabled() = 0;
+ virtual void Shutdown() = 0;
+
+ private:
+ void UpdateVsyncStatus();
+
+ Mutex mDispatcherLock;
+ bool mRefreshTimerNeedsVsync;
+ nsTArray<RefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
+ RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
+ };
+
+ void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+ void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+
+ RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
+ virtual Display& GetGlobalDisplay() = 0; // Works across all displays
+ void Shutdown();
+
+protected:
+ virtual ~VsyncSource() {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VSYNCSOURCE_H */
diff --git a/system/graphics/thebes/cairo-xlib-utils.h b/system/graphics/thebes/cairo-xlib-utils.h
new file mode 100644
index 000000000..f64a84dce
--- /dev/null
+++ b/system/graphics/thebes/cairo-xlib-utils.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CAIROXLIBUTILS_H_
+#define CAIROXLIBUTILS_H_
+
+#include "cairo.h"
+#include <X11/Xlib.h>
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * This callback encapsulates Xlib-based rendering. We assume that the
+ * execution of the callback is equivalent to compositing some RGBA image of
+ * size (bounds_width, bounds_height) onto the drawable at offset (offset_x,
+ * offset_y), clipped to the union of the clip_rects if num_rects is greater
+ * than zero. This includes the assumption that the same RGBA image
+ * is composited if you call the callback multiple times with the same closure,
+ * display and visual during a single cairo_draw_with_xlib call.
+ *
+ * @return True on success, False on non-recoverable error
+ */
+typedef cairo_bool_t (* cairo_xlib_drawing_callback)
+ (void *closure,
+ Screen *screen,
+ Drawable drawable,
+ Visual *visual,
+ short offset_x, short offset_y,
+ XRectangle* clip_rects, unsigned int num_rects);
+
+/**
+ * This structure captures the result of the native drawing, in case the
+ * caller may wish to reapply the drawing efficiently later.
+ */
+typedef struct {
+ cairo_surface_t *surface;
+ cairo_bool_t uniform_alpha;
+ cairo_bool_t uniform_color;
+ double alpha; /* valid only if uniform_alpha is TRUE */
+ double r, g, b; /* valid only if uniform_color is TRUE */
+} cairo_xlib_drawing_result_t;
+
+/**
+ * This type specifies whether the native drawing callback draws all pixels
+ * in its bounds opaquely, independent of the contents of the target drawable,
+ * or whether it leaves pixels transparent/translucent or depends on the
+ * existing contents of the target drawable in some way.
+ */
+typedef enum _cairo_xlib_drawing_opacity {
+ CAIRO_XLIB_DRAWING_OPAQUE,
+ CAIRO_XLIB_DRAWING_TRANSPARENT
+} cairo_xlib_drawing_opacity_t;
+
+/**
+ * This type encodes the capabilities of the native drawing callback.
+ *
+ * If CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET is set, 'offset_x' and 'offset_y'
+ * can be nonzero in the call to the callback; otherwise they will be zero.
+ *
+ * If CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT is set, then 'num_rects' can be
+ * zero or one in the call to the callback. If
+ * CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST is set, then 'num_rects' can be
+ * anything in the call to the callback. Otherwise 'num_rects' will be zero.
+ * Do not set both of these values.
+ *
+ * If CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_SCREEN is set, then 'screen' can
+ * be any screen on any display, otherwise it will be the default screen of
+ * the display passed into cairo_draw_with_xlib.
+ *
+ * If CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL is set, then 'visual' can be
+ * any visual, otherwise it will be equal to
+ * DefaultVisualOfScreen (screen).
+ */
+typedef enum {
+ CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET = 0x01,
+ CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT = 0x02,
+ CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST = 0x04,
+ CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_SCREEN = 0x08,
+ CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL = 0x10
+} cairo_xlib_drawing_support_t;
+
+/**
+ * Draw Xlib output into any cairo context. All cairo transforms and effects
+ * are honored, including the current operator. This is equivalent to a
+ * cairo_set_source_surface and then cairo_paint.
+ * @param cr the context to draw into
+ * @param callback the code to perform Xlib rendering
+ * @param closure associated data
+ * @param dpy an X display to use in case the cairo context has no associated X display
+ * @param width the width of the putative image drawn by the callback
+ * @param height the height of the putative image drawn by the callback
+ * @param is_opaque set to CAIRO_XLIB_DRAWING_IS_OPAQUE to indicate
+ * that all alpha values of the putative image will be 1.0; the pixels drawn into
+ * the Drawable must not depend on the prior contents of the Drawable
+ * in any way
+ * @param capabilities the capabilities of the callback as described above.
+ * @param result if non-NULL, we *may* fill in the struct with information about
+ * the rendered image. 'surface' may be filled in with a surface representing
+ * the image, similar to the target of 'cr'. If 'uniform_alpha' is True then
+ * every pixel of the image has the same alpha value 'alpha'. If
+ * 'uniform_color' is True then every pixel of the image has the same RGB
+ * color (r, g, b). If the image has uniform color and alpha (or alpha is zero,
+ * in which case the color is always uniform) then we won't bother returning
+ * a surface for it.
+ */
+void cairo_draw_with_xlib (cairo_t *cr,
+ cairo_xlib_drawing_callback callback,
+ void *closure,
+ Display *dpy,
+ unsigned int width, unsigned int height,
+ cairo_xlib_drawing_opacity_t is_opaque,
+ cairo_xlib_drawing_support_t capabilities,
+ cairo_xlib_drawing_result_t *result);
+
+CAIRO_END_DECLS
+
+#endif /*CAIROXLIBUTILS_H_*/
diff --git a/system/graphics/thebes/d3dkmtQueryStatistics.h b/system/graphics/thebes/d3dkmtQueryStatistics.h
new file mode 100644
index 000000000..76f34d541
--- /dev/null
+++ b/system/graphics/thebes/d3dkmtQueryStatistics.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* This file is based on a header file that was briefly seen in the
+ * Windows 8 RC SDK. The work for this file itself was based on the one in ProcessHacker at
+ * http://processhacker.svn.sourceforge.net/viewvc/processhacker/2.x/trunk/plugins/ExtendedTools/d3dkmt.h?revision=4758&view=markup
+ * For more details see Mozilla Bug 689870.
+ * [Bug 917496 indicates that some of these structs may not match reality, and
+ * therefore should not be trusted. See the reference to bug 917496 in
+ * gfxWindowsPlatform.cpp.]
+ */
+
+typedef struct _D3DKMTQS_COUNTER
+{
+ ULONG Count;
+ ULONGLONG Bytes;
+} D3DKMTQS_COUNTER;
+
+typedef struct _D3DKMTQS_ADAPTER_INFO
+{
+ ULONG NbSegments;
+
+ ULONG Filler[4];
+ ULONGLONG Filler2[2]; // Assumed sizeof(LONGLONG) = sizeof(ULONGLONG)
+ struct {
+ ULONG Filler[14];
+ } Filler_RDMAB;
+ struct {
+ ULONG Filler[9];
+ } Filler_R;
+ struct {
+ ULONG Filler[4];
+ D3DKMTQS_COUNTER Filler2;
+ } Filler_P;
+ struct {
+ D3DKMTQS_COUNTER Filler[16];
+ ULONG Filler2[2];
+ } Filler_PF;
+ struct {
+ ULONGLONG Filler[8];
+ } Filler_PT;
+ struct {
+ ULONG Filler[2];
+ } Filler_SR;
+ struct {
+ ULONG Filler[7];
+ } Filler_L;
+ struct {
+ D3DKMTQS_COUNTER Filler[7];
+ } Filler_A;
+ struct {
+ D3DKMTQS_COUNTER Filler[4];
+ } Filler_T;
+ ULONG64 Reserved[8];
+} D3DKMTQS_ADAPTER_INFO;
+
+typedef struct _D3DKMTQS_SEGMENT_INFO_WIN7
+{
+ ULONG Filler[3];
+ struct {
+ ULONGLONG Filler;
+ ULONG Filler2[2];
+ } Filler_M;
+
+ ULONG Aperture;
+
+ ULONGLONG Filler3[5];
+ ULONG64 Filler4[8];
+} D3DKMTQS_SEGMENT_INFO_WIN7;
+
+typedef struct _D3DKMTQS_SEGMENT_INFO_WIN8
+{
+ ULONGLONG Filler[3];
+ struct {
+ ULONGLONG Filler;
+ ULONG Filler2[2];
+ } Filler_M;
+
+ ULONG Aperture;
+
+ ULONGLONG Filler3[5];
+ ULONG64 Filler4[8];
+} D3DKMTQS_SEGMENT_INFO_WIN8;
+
+typedef struct _D3DKMTQS_SYSTEM_MEMORY
+{
+ ULONGLONG BytesAllocated;
+ ULONG Filler[2];
+ ULONGLONG Filler2[7];
+} D3DKMTQS_SYSTEM_MEMORY;
+
+typedef struct _D3DKMTQS_PROCESS_INFO
+{
+ ULONG Filler[2];
+ struct {
+ ULONGLONG BytesAllocated;
+
+ ULONG Filler[2];
+ ULONGLONG Filler2[7];
+ } SystemMemory;
+ ULONG64 Reserved[8];
+} D3DKMTQS_PROCESS_INFO;
+
+typedef struct _D3DKMTQS_PROCESS_SEGMENT_INFO
+{
+ union {
+ struct {
+ ULONGLONG BytesCommitted;
+ } Win8;
+ struct {
+ ULONG BytesCommitted;
+ ULONG UnknownRandomness;
+ } Win7;
+ };
+
+ ULONGLONG Filler[2];
+ ULONG Filler2;
+ struct {
+ ULONG Filler;
+ D3DKMTQS_COUNTER Filler2[6];
+ ULONGLONG Filler3;
+ } Filler3;
+ struct {
+ ULONGLONG Filler;
+ } Filler4;
+ ULONG64 Reserved[8];
+} D3DKMTQS_PROCESS_SEGMENT_INFO;
+
+typedef enum _D3DKMTQS_TYPE
+{
+ D3DKMTQS_ADAPTER = 0,
+ D3DKMTQS_PROCESS = 1,
+ D3DKMTQS_SEGMENT = 3,
+ D3DKMTQS_PROCESS_SEGMENT = 4,
+} D3DKMTQS_TYPE;
+
+typedef union _D3DKMTQS_RESULT
+{
+ D3DKMTQS_ADAPTER_INFO AdapterInfo;
+ D3DKMTQS_SEGMENT_INFO_WIN7 SegmentInfoWin7;
+ D3DKMTQS_SEGMENT_INFO_WIN8 SegmentInfoWin8;
+ D3DKMTQS_PROCESS_INFO ProcessInfo;
+ D3DKMTQS_PROCESS_SEGMENT_INFO ProcessSegmentInfo;
+} D3DKMTQS_RESULT;
+
+typedef struct _D3DKMTQS_QUERY_SEGMENT
+{
+ ULONG SegmentId;
+} D3DKMTQS_QUERY_SEGMENT;
+
+typedef struct _D3DKMTQS
+{
+ D3DKMTQS_TYPE Type;
+ LUID AdapterLuid;
+ HANDLE hProcess;
+ D3DKMTQS_RESULT QueryResult;
+
+ union
+ {
+ D3DKMTQS_QUERY_SEGMENT QuerySegment;
+ D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
+ };
+} D3DKMTQS;
+
+extern "C" {
+typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(const D3DKMTQS *);
+}
diff --git a/system/graphics/thebes/genLanguageTagList.pl b/system/graphics/thebes/genLanguageTagList.pl
new file mode 100644
index 000000000..0fa6c65f8
--- /dev/null
+++ b/system/graphics/thebes/genLanguageTagList.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/env perl
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This tool is used to prepare a list of valid language subtags from the
+# IANA registry (http://www.iana.org/assignments/language-subtag-registry).
+
+# Run as
+#
+# perl genLanguageTagList.pl language-subtag-registry > gfxLanguageTagList.cpp
+#
+# where language-subtag-registry is a copy of the IANA registry file.
+
+use strict;
+
+my $timestamp = gmtime();
+print <<__END;
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Derived from the IANA language subtag registry by genLanguageTagList.pl.
+ *
+ * Created on $timestamp.
+ *
+ * * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
+ */
+
+__END
+
+my $isLanguage = 0;
+
+while (<>) {
+ # strip leading/trailing whitespace, if any
+ chomp;
+ s/^\s+//;
+ s/\s+$//;
+
+ # assume File-Date precedes the actual list;
+ # record the date, and begin assignment to an array of valid tags
+ if (m/^File-Date:\s*(.+)$/) {
+ print "// Based on IANA registry dated $1\n\n";
+ print "static const uint32_t sLanguageTagList[] = {";
+ next;
+ }
+
+ if (m/^%%/) {
+ $isLanguage = 0;
+ next;
+ }
+
+ # we only care about records of type 'language'
+ if (m/^Type:\s*(.+)$/) {
+ $isLanguage = ($1 eq 'language');
+ next;
+ }
+
+ # append the tag to our string, with ";" as a delimiter
+ if ($isLanguage && m/^Subtag:\s*([a-z]{2,3})\s*$/) {
+ my $tagstr = $1;
+ print "\n TRUETYPE_TAG(",
+ join(",", map { $_ eq " " ? " 0 " : "'" . $_ . "'" } split(//, substr($tagstr . " ", 0, 4))),
+ "), // ", $tagstr;
+ next;
+ }
+
+ if ($isLanguage && m/^Description:\s*(.+)$/) {
+ print " = $1";
+ $isLanguage = 0; # only print first Description field
+ next;
+ }
+}
+
+# at end of file, terminate our assignment to the array of tags
+print <<__END;
+
+ 0x0 // end of language code list
+};
+
+/*
+ * * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
+ */
+__END
diff --git a/system/graphics/thebes/genTables.py b/system/graphics/thebes/genTables.py
new file mode 100644
index 000000000..0e902c52a
--- /dev/null
+++ b/system/graphics/thebes/genTables.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+import sys
+
+def table_generator(f):
+ return ",\n".join([", ".join(["0x%2.2x" % h for h in [f(i) for i in range(r,r+16)]]) for r in range(0, 65536, 16)])
+
+def generate(output):
+ output.write("const uint8_t gfxUtils::sPremultiplyTable[256*256] = {\n");
+ output.write(table_generator(lambda i: ((i / 256) * (i % 256) + 254) / 255) + "\n")
+ output.write("};\n");
+ output.write("const uint8_t gfxUtils::sUnpremultiplyTable[256*256] = {\n");
+ output.write(table_generator(lambda i: (i % 256) * 255 / ((i / 256) if (i / 256) > 0 else 255) % 256) + "\n")
+ output.write("};\n");
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2:
+ print("Usage: genTables.py <header.h>", file=sys.stderr)
+ sys.exit(1)
+ with open(sys.argv[1], 'w') as f:
+ generate(f)
diff --git a/system/graphics/thebes/gencjkcisvs.py b/system/graphics/thebes/gencjkcisvs.py
new file mode 100644
index 000000000..401ebaa97
--- /dev/null
+++ b/system/graphics/thebes/gencjkcisvs.py
@@ -0,0 +1,77 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os.path
+import re
+import sys
+
+f = open(sys.argv[1] if len(sys.argv) > 1 else 'StandardizedVariants.txt')
+
+line = f.readline()
+m = re.compile('^# (StandardizedVariants(-\d+(\.\d+)*)?\.txt)').search(line)
+fileversion = m.group(1)
+vsdict = {}
+r = re.compile('^([0-9A-F]{4,6}) (FE0[0-9A-F]); CJK COMPATIBILITY IDEOGRAPH-([0-9A-F]{4,6});')
+while True:
+ line = f.readline()
+ if not line:
+ break
+ if not 'CJK COMPATIBILITY IDEOGRAPH-' in line:
+ continue
+
+ m = r.search(line)
+ unified = int(m.group(1), 16)
+ vs = int(m.group(2), 16)
+ compat = int(m.group(3), 16)
+
+ if not vs in vsdict:
+ vsdict[vs] = {}
+ vsdict[vs][unified] = compat
+
+f.close
+
+offsets = []
+length = 10 + 11 * len(vsdict)
+for (k, mappings) in sorted(vsdict.items()):
+ offsets.append(length)
+ length += 4 + 5 * len(mappings)
+
+f = open(sys.argv[2] if len(sys.argv) > 2 else 'CJKCompatSVS.cpp', 'wb')
+f.write("""// Generated by %s. Do not edit.
+
+#include <stdint.h>
+
+#define U16(v) (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define U24(v) (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define U32(v) (((v) >> 24) & 0xFF), (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
+#define GLYPH(v) U16(v >= 0x2F800 ? (v) - (0x2F800 - 0xFB00) : (v))
+
+// Fallback mappings for CJK Compatibility Ideographs Standardized Variants
+// taken from %s.
+// Using OpenType format 14 cmap subtable structure to reuse the lookup code
+// for fonts. The glyphID field is used to store the corresponding codepoints
+// CJK Compatibility Ideographs. To fit codepoints into the 16-bit glyphID
+// field, CJK Compatibility Ideographs Supplement (U+2F800..U+2FA1F) will be
+// mapped to 0xFB00..0xFD1F.
+extern const uint8_t sCJKCompatSVSTable[] = {
+""" % (os.path.basename(sys.argv[0]), fileversion))
+f.write(' U16(14), // format\n')
+f.write(' U32(%d), // length\n' % length)
+f.write(' U32(%d), // numVarSelectorRecords\n' % len(vsdict))
+for i, k in enumerate(sorted(vsdict.keys())):
+ f.write(' U24(0x%04X), U32(0), U32(%d), // varSelectorRecord[%d]\n' % (k, offsets[i], i))
+for (k, mappings) in sorted(vsdict.items()):
+ f.write(' // 0x%04X\n' % k)
+ f.write(' U32(%d), // numUVSMappings\n' % len(mappings))
+ for (unified, compat) in sorted(mappings.items()):
+ f.write(' U24(0x%04X), GLYPH(0x%04X),\n' % (unified, compat))
+f.write("""};
+
+#undef U16
+#undef U24
+#undef U32
+#undef GLYPH
+
+static_assert(sizeof sCJKCompatSVSTable == %d, "Table generator has a bug.");
+""" % length)
diff --git a/system/graphics/thebes/gfx2DGlue.h b/system/graphics/thebes/gfx2DGlue.h
new file mode 100644
index 000000000..da6dd622c
--- /dev/null
+++ b/system/graphics/thebes/gfx2DGlue.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_2D_GLUE_H
+#define GFX_2D_GLUE_H
+
+#include "gfxPlatform.h"
+#include "gfxRect.h"
+#include "gfxMatrix.h"
+#include "gfxContext.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+inline Rect ToRect(const gfxRect &aRect)
+{
+ return Rect(Float(aRect.x), Float(aRect.y),
+ Float(aRect.width), Float(aRect.height));
+}
+
+inline RectDouble ToRectDouble(const gfxRect &aRect)
+{
+ return RectDouble(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+inline Matrix ToMatrix(const gfxMatrix &aMatrix)
+{
+ return Matrix(Float(aMatrix._11), Float(aMatrix._12), Float(aMatrix._21),
+ Float(aMatrix._22), Float(aMatrix._31), Float(aMatrix._32));
+}
+
+inline gfxMatrix ThebesMatrix(const Matrix &aMatrix)
+{
+ return gfxMatrix(aMatrix._11, aMatrix._12, aMatrix._21,
+ aMatrix._22, aMatrix._31, aMatrix._32);
+}
+
+inline Point ToPoint(const gfxPoint &aPoint)
+{
+ return Point(Float(aPoint.x), Float(aPoint.y));
+}
+
+inline Size ToSize(const gfxSize &aSize)
+{
+ return Size(Float(aSize.width), Float(aSize.height));
+}
+
+inline gfxPoint ThebesPoint(const Point &aPoint)
+{
+ return gfxPoint(aPoint.x, aPoint.y);
+}
+
+inline gfxSize ThebesSize(const Size &aSize)
+{
+ return gfxSize(aSize.width, aSize.height);
+}
+
+inline gfxRect ThebesRect(const Rect &aRect)
+{
+ return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+inline gfxRect ThebesRect(const IntRect &aRect)
+{
+ return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+inline gfxRect ThebesRect(const RectDouble &aRect)
+{
+ return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+inline gfxImageFormat SurfaceFormatToImageFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ case SurfaceFormat::B8G8R8X8:
+ return SurfaceFormat::X8R8G8B8_UINT32;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case SurfaceFormat::A8:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+inline SurfaceFormat ImageFormatToSurfaceFormat(gfxImageFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return SurfaceFormat::B8G8R8A8;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return SurfaceFormat::B8G8R8X8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case SurfaceFormat::A8:
+ return SurfaceFormat::A8;
+ default:
+ case SurfaceFormat::UNKNOWN:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+inline gfxContentType ContentForFormat(const SurfaceFormat &aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::R5G6B5_UINT16:
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ return gfxContentType::COLOR;
+ case SurfaceFormat::A8:
+ return gfxContentType::ALPHA;
+ case SurfaceFormat::B8G8R8A8:
+ case SurfaceFormat::R8G8B8A8:
+ default:
+ return gfxContentType::COLOR_ALPHA;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/thebes/gfxASurface.cpp b/system/graphics/thebes/gfxASurface.cpp
new file mode 100644
index 000000000..0e94ef0be
--- /dev/null
+++ b/system/graphics/thebes/gfxASurface.cpp
@@ -0,0 +1,611 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMemoryReporter.h"
+#include "nsMemory.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Base64.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "gfx2DGlue.h"
+
+#include "gfxASurface.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "gfxRect.h"
+
+#include "cairo.h"
+#include <algorithm>
+
+#ifdef CAIRO_HAS_WIN32_SURFACE
+#include "gfxWindowsSurface.h"
+#endif
+
+#ifdef MOZ_X11
+#include "gfxXlibSurface.h"
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+
+#include "imgIEncoder.h"
+#include "nsComponentManagerUtils.h"
+#include "nsISupportsUtils.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsIClipboardHelper.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+static cairo_user_data_key_t gfxasurface_pointer_key;
+
+gfxASurface::gfxASurface()
+ : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
+ mSurfaceValid(false)
+{
+ MOZ_COUNT_CTOR(gfxASurface);
+}
+
+gfxASurface::~gfxASurface()
+{
+ RecordMemoryFreed();
+
+ MOZ_COUNT_DTOR(gfxASurface);
+}
+
+// Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
+// refcount mismatch issues.
+nsrefcnt
+gfxASurface::AddRef(void)
+{
+ if (mSurfaceValid) {
+ if (mFloatingRefs) {
+ // eat a floating ref
+ mFloatingRefs--;
+ } else {
+ cairo_surface_reference(mSurface);
+ }
+
+ return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
+ } else {
+ // the surface isn't valid, but we still need to refcount
+ // the gfxASurface
+ return ++mFloatingRefs;
+ }
+}
+
+nsrefcnt
+gfxASurface::Release(void)
+{
+ if (mSurfaceValid) {
+ NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
+
+ // Note that there is a destructor set on user data for mSurface,
+ // which will delete this gfxASurface wrapper when the surface's refcount goes
+ // out of scope.
+ nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
+ cairo_surface_destroy(mSurface);
+
+ // |this| may not be valid any more, don't use it!
+
+ return --refcnt;
+ } else {
+ if (--mFloatingRefs == 0) {
+ delete this;
+ return 0;
+ }
+
+ return mFloatingRefs;
+ }
+}
+
+void
+gfxASurface::SurfaceDestroyFunc(void *data) {
+ gfxASurface *surf = (gfxASurface*) data;
+ // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
+ delete surf;
+}
+
+gfxASurface*
+gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
+{
+ if (!csurf)
+ return nullptr;
+ return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
+}
+
+void
+gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
+{
+ if (!csurf)
+ return;
+ cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
+}
+
+already_AddRefed<gfxASurface>
+gfxASurface::Wrap (cairo_surface_t *csurf, const IntSize& aSize)
+{
+ RefPtr<gfxASurface> result;
+
+ /* Do we already have a wrapper for this surface? */
+ result = GetSurfaceWrapper(csurf);
+ if (result) {
+ // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
+ return result.forget();
+ }
+
+ /* No wrapper; figure out the surface type and create it */
+ cairo_surface_type_t stype = cairo_surface_get_type(csurf);
+
+ if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
+ result = new gfxImageSurface(csurf);
+ }
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
+ stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
+ result = new gfxWindowsSurface(csurf);
+ }
+#endif
+#ifdef MOZ_X11
+ else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
+ result = new gfxXlibSurface(csurf);
+ }
+#endif
+ else {
+ result = new gfxUnknownSurface(csurf, aSize);
+ }
+
+ // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
+
+ return result.forget();
+}
+
+void
+gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
+{
+ SetSurfaceWrapper(surface, this);
+ MOZ_ASSERT(surface, "surface should be a valid pointer");
+
+ mSurface = surface;
+ mSurfaceValid = !cairo_surface_status(surface);
+ if (!mSurfaceValid) {
+ gfxWarning() << "ASurface Init failed with Cairo status " << cairo_surface_status(surface) << " on " << hexa(surface);
+ }
+
+ if (existingSurface || !mSurfaceValid) {
+ mFloatingRefs = 0;
+ } else {
+ mFloatingRefs = 1;
+#ifdef MOZ_TREE_CAIRO
+ if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
+ cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+ }
+#endif
+ }
+}
+
+gfxSurfaceType
+gfxASurface::GetType() const
+{
+ if (!mSurfaceValid)
+ return (gfxSurfaceType)-1;
+
+ return (gfxSurfaceType)cairo_surface_get_type(mSurface);
+}
+
+gfxContentType
+gfxASurface::GetContentType() const
+{
+ if (!mSurfaceValid)
+ return (gfxContentType)-1;
+
+ return (gfxContentType)cairo_surface_get_content(mSurface);
+}
+
+void
+gfxASurface::SetDeviceOffset(const gfxPoint& offset)
+{
+ if (!mSurfaceValid)
+ return;
+ cairo_surface_set_device_offset(mSurface,
+ offset.x, offset.y);
+}
+
+gfxPoint
+gfxASurface::GetDeviceOffset() const
+{
+ if (!mSurfaceValid)
+ return gfxPoint(0.0, 0.0);
+ gfxPoint pt;
+ cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
+ return pt;
+}
+
+void
+gfxASurface::Flush() const
+{
+ if (!mSurfaceValid)
+ return;
+ cairo_surface_flush(mSurface);
+ gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
+}
+
+void
+gfxASurface::MarkDirty()
+{
+ if (!mSurfaceValid)
+ return;
+ cairo_surface_mark_dirty(mSurface);
+ gfxPlatform::ClearSourceSurfaceForSurface(this);
+}
+
+void
+gfxASurface::MarkDirty(const gfxRect& r)
+{
+ if (!mSurfaceValid)
+ return;
+ cairo_surface_mark_dirty_rectangle(mSurface,
+ (int) r.X(), (int) r.Y(),
+ (int) r.Width(), (int) r.Height());
+ gfxPlatform::ClearSourceSurfaceForSurface(this);
+}
+
+void
+gfxASurface::SetData(const cairo_user_data_key_t *key,
+ void *user_data,
+ thebes_destroy_func_t destroy)
+{
+ if (!mSurfaceValid)
+ return;
+ cairo_surface_set_user_data(mSurface, key, user_data, destroy);
+}
+
+void *
+gfxASurface::GetData(const cairo_user_data_key_t *key)
+{
+ if (!mSurfaceValid)
+ return nullptr;
+ return cairo_surface_get_user_data(mSurface, key);
+}
+
+void
+gfxASurface::Finish()
+{
+ // null surfaces are allowed here
+ cairo_surface_finish(mSurface);
+}
+
+already_AddRefed<gfxASurface>
+gfxASurface::CreateSimilarSurface(gfxContentType aContent,
+ const IntSize& aSize)
+{
+ if (!mSurface || !mSurfaceValid) {
+ return nullptr;
+ }
+
+ cairo_surface_t *surface =
+ cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
+ aSize.width, aSize.height);
+ if (cairo_surface_status(surface)) {
+ cairo_surface_destroy(surface);
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> result = Wrap(surface, aSize);
+ cairo_surface_destroy(surface);
+ return result.forget();
+}
+
+already_AddRefed<gfxImageSurface>
+gfxASurface::CopyToARGB32ImageSurface()
+{
+ if (!mSurface || !mSurfaceValid) {
+ return nullptr;
+ }
+
+ const IntSize size = GetSize();
+ RefPtr<gfxImageSurface> imgSurface =
+ new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
+
+ RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
+ RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
+
+ dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
+
+ return imgSurface.forget();
+}
+
+int
+gfxASurface::CairoStatus()
+{
+ if (!mSurfaceValid)
+ return -1;
+
+ return cairo_surface_status(mSurface);
+}
+
+/* static */
+int32_t
+gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
+{
+ cairo_format_t cformat = GfxFormatToCairoFormat(format);
+ return cairo_format_stride_for_width(cformat, (int)width);
+}
+
+nsresult
+gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
+{
+ return NS_OK;
+}
+
+nsresult
+gfxASurface::EndPrinting()
+{
+ return NS_OK;
+}
+
+nsresult
+gfxASurface::AbortPrinting()
+{
+ return NS_OK;
+}
+
+nsresult
+gfxASurface::BeginPage()
+{
+ return NS_OK;
+}
+
+nsresult
+gfxASurface::EndPage()
+{
+ return NS_OK;
+}
+
+gfxContentType
+gfxASurface::ContentFromFormat(gfxImageFormat format)
+{
+ switch (format) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return gfxContentType::COLOR_ALPHA;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ case SurfaceFormat::R5G6B5_UINT16:
+ return gfxContentType::COLOR;
+ case SurfaceFormat::A8:
+ return gfxContentType::ALPHA;
+
+ case SurfaceFormat::UNKNOWN:
+ default:
+ return gfxContentType::COLOR;
+ }
+}
+
+int32_t
+gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
+{
+ switch (format) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return 4;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return 2;
+ case SurfaceFormat::A8:
+ return 1;
+ default:
+ NS_WARNING("Unknown byte per pixel value for Image format");
+ }
+ return 0;
+}
+
+/** Memory reporting **/
+
+static const char *sDefaultSurfaceDescription =
+ "Memory used by gfx surface of the given type.";
+
+struct SurfaceMemoryReporterAttrs {
+ const char *path;
+ const char *description;
+};
+
+static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
+ {"gfx-surface-image", nullptr},
+ {"gfx-surface-pdf", nullptr},
+ {"gfx-surface-ps", nullptr},
+ {"gfx-surface-xlib",
+ "Memory used by xlib surfaces to store pixmaps. This memory lives in "
+ "the X server's process rather than in this application, so the bytes "
+ "accounted for here aren't counted in vsize, resident, explicit, or any of "
+ "the other measurements on this page."},
+ {"gfx-surface-xcb", nullptr},
+ {"gfx-surface-glitz???", nullptr}, // should never be used
+ {"gfx-surface-quartz", nullptr},
+ {"gfx-surface-win32", nullptr},
+ {"gfx-surface-beos", nullptr},
+ {"gfx-surface-directfb???", nullptr}, // should never be used
+ {"gfx-surface-svg", nullptr},
+ {"gfx-surface-os2", nullptr},
+ {"gfx-surface-win32printing", nullptr},
+ {"gfx-surface-quartzimage", nullptr},
+ {"gfx-surface-script", nullptr},
+ {"gfx-surface-qpainter", nullptr},
+ {"gfx-surface-recording", nullptr},
+ {"gfx-surface-vg", nullptr},
+ {"gfx-surface-gl", nullptr},
+ {"gfx-surface-drm", nullptr},
+ {"gfx-surface-tee", nullptr},
+ {"gfx-surface-xml", nullptr},
+ {"gfx-surface-skia", nullptr},
+ {"gfx-surface-subsurface", nullptr},
+};
+
+static_assert(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
+ size_t(gfxSurfaceType::Max), "sSurfaceMemoryReporterAttrs exceeds max capacity");
+static_assert(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
+ uint32_t(gfxSurfaceType::Skia), "CAIRO_SURFACE_TYPE_SKIA not equal to gfxSurfaceType::Skia");
+
+/* Surface size memory reporting */
+
+class SurfaceMemoryReporter final : public nsIMemoryReporter
+{
+ ~SurfaceMemoryReporter() {}
+
+ // We can touch this array on several different threads, and we don't
+ // want to introduce memory barriers when recording the memory used. To
+ // assure dynamic race checkers like TSan that this is OK, we use
+ // relaxed memory ordering here.
+ static Atomic<size_t, Relaxed> sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
+
+public:
+ static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes)
+ {
+ // A read-modify-write operation like += would require a memory barrier
+ // here, which would defeat the purpose of using relaxed memory
+ // ordering. So separate out the read and write operations.
+ sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes;
+ };
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
+ nsISupports *aData, bool aAnonymize) override
+ {
+ const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
+ for (size_t i = 0; i < len; i++) {
+ int64_t amount = sSurfaceMemoryUsed[i];
+
+ if (amount != 0) {
+ const char *path = sSurfaceMemoryReporterAttrs[i].path;
+ const char *desc = sSurfaceMemoryReporterAttrs[i].description;
+ if (!desc) {
+ desc = sDefaultSurfaceDescription;
+ }
+
+ aHandleReport->Callback(
+ EmptyCString(), nsCString(path), KIND_OTHER, UNITS_BYTES,
+ amount, nsCString(desc), aData);
+ }
+ }
+
+ return NS_OK;
+ }
+};
+
+Atomic<size_t, Relaxed> SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
+
+NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
+
+void
+gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
+ int32_t aBytes)
+{
+ if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
+ NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
+ return;
+ }
+
+ static bool registered = false;
+ if (!registered) {
+ RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
+ registered = true;
+ }
+
+ SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
+}
+
+void
+gfxASurface::RecordMemoryUsed(int32_t aBytes)
+{
+ RecordMemoryUsedForSurfaceType(GetType(), aBytes);
+ mBytesRecorded += aBytes;
+}
+
+void
+gfxASurface::RecordMemoryFreed()
+{
+ if (mBytesRecorded) {
+ RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
+ mBytesRecorded = 0;
+ }
+}
+
+size_t
+gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // We don't measure mSurface because cairo doesn't allow it.
+ return 0;
+}
+
+size_t
+gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+/* static */ uint8_t
+gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
+{
+ switch (aImageFormat) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return 4;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return 4;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return 2;
+ case SurfaceFormat::A8:
+ return 1;
+ case SurfaceFormat::UNKNOWN:
+ default:
+ NS_NOTREACHED("Not really sure what you want me to say here");
+ return 0;
+ }
+}
+
+void
+gfxASurface::SetOpaqueRect(const gfxRect& aRect)
+{
+ if (aRect.IsEmpty()) {
+ mOpaqueRect = nullptr;
+ } else if (!!mOpaqueRect) {
+ *mOpaqueRect = aRect;
+ } else {
+ mOpaqueRect = MakeUnique<gfxRect>(aRect);
+ }
+}
+
+/* static */const gfxRect&
+gfxASurface::GetEmptyOpaqueRect()
+{
+ static const gfxRect empty(0, 0, 0, 0);
+ return empty;
+}
+
+const IntSize
+gfxASurface::GetSize() const
+{
+ return IntSize(-1, -1);
+}
+
+SurfaceFormat
+gfxASurface::GetSurfaceFormat() const
+{
+ if (!mSurfaceValid) {
+ return SurfaceFormat::UNKNOWN;
+ }
+ return GfxFormatForCairoSurface(mSurface);
+}
+
+already_AddRefed<gfxImageSurface>
+gfxASurface::GetAsImageSurface()
+{
+ return nullptr;
+}
diff --git a/system/graphics/thebes/gfxASurface.h b/system/graphics/thebes/gfxASurface.h
new file mode 100644
index 000000000..ca7535e56
--- /dev/null
+++ b/system/graphics/thebes/gfxASurface.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ASURFACE_H
+#define GFX_ASURFACE_H
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+
+#include "gfxTypes.h"
+#include "nscore.h"
+#include "nsSize.h"
+#include "mozilla/gfx/Rect.h"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsStringFwd.h"
+#else
+#include "nsStringAPI.h"
+#endif
+
+class gfxImageSurface;
+struct gfxRect;
+struct gfxPoint;
+
+template <typename T>
+struct already_AddRefed;
+
+/**
+ * A surface is something you can draw on. Instantiate a subclass of this
+ * abstract class, and use gfxContext to draw on this surface.
+ */
+class gfxASurface {
+public:
+#ifdef MOZILLA_INTERNAL_API
+ nsrefcnt AddRef(void);
+ nsrefcnt Release(void);
+#else
+ virtual nsrefcnt AddRef(void);
+ virtual nsrefcnt Release(void);
+#endif
+
+public:
+
+ /** Wrap the given cairo surface and return a gfxASurface for it.
+ * This adds a reference to csurf (owned by the returned gfxASurface).
+ */
+ static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf, const mozilla::gfx::IntSize& aSize = mozilla::gfx::IntSize(-1, -1));
+
+ /*** this DOES NOT addref the surface */
+ cairo_surface_t *CairoSurface() {
+ return mSurface;
+ }
+
+ gfxSurfaceType GetType() const;
+
+ gfxContentType GetContentType() const;
+
+ void SetDeviceOffset(const gfxPoint& offset);
+ gfxPoint GetDeviceOffset() const;
+
+ void Flush() const;
+ void MarkDirty();
+ void MarkDirty(const gfxRect& r);
+
+ /* Printing backend functions */
+ virtual nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
+ virtual nsresult EndPrinting();
+ virtual nsresult AbortPrinting();
+ virtual nsresult BeginPage();
+ virtual nsresult EndPage();
+
+ void SetData(const cairo_user_data_key_t *key,
+ void *user_data,
+ thebes_destroy_func_t destroy);
+ void *GetData(const cairo_user_data_key_t *key);
+
+ virtual void Finish();
+
+ /**
+ * Create an offscreen surface that can be efficiently copied into
+ * this surface (at least if tiling is not involved).
+ * Returns null on error.
+ */
+ virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
+ const mozilla::gfx::IntSize& aSize);
+
+ /**
+ * Returns an image surface for this surface, or nullptr if not supported.
+ * This will not copy image data, just wraps an image surface around
+ * pixel data already available in memory.
+ */
+ virtual already_AddRefed<gfxImageSurface> GetAsImageSurface();
+
+ /**
+ * Creates a new ARGB32 image surface with the same contents as this surface.
+ * Returns null on error.
+ */
+ already_AddRefed<gfxImageSurface> CopyToARGB32ImageSurface();
+
+ int CairoStatus();
+
+ /* Provide a stride value that will respect all alignment requirements of
+ * the accelerated image-rendering code.
+ */
+ static int32_t FormatStrideForWidth(gfxImageFormat format, int32_t width);
+
+ static gfxContentType ContentFromFormat(gfxImageFormat format);
+
+ /**
+ * Record number of bytes for given surface type. Use positive bytes
+ * for allocations and negative bytes for deallocations.
+ */
+ static void RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
+ int32_t aBytes);
+
+ /**
+ * Same as above, but use current surface type as returned by GetType().
+ * The bytes will be accumulated until RecordMemoryFreed is called,
+ * in which case the value that was recorded for this surface will
+ * be freed.
+ */
+ void RecordMemoryUsed(int32_t aBytes);
+ void RecordMemoryFreed();
+
+ virtual int32_t KnownMemoryUsed() { return mBytesRecorded; }
+
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ // gfxASurface has many sub-classes. This method indicates if a sub-class
+ // is capable of measuring its own size accurately. If not, the caller
+ // must fall back to a computed size. (Note that gfxASurface can actually
+ // measure itself, but we must |return false| here because it serves as the
+ // (conservative) default for all the sub-classes. Therefore, this
+ // function should only be called on a |gfxASurface*| that actually points
+ // to a sub-class of gfxASurface.)
+ virtual bool SizeOfIsMeasured() const { return false; }
+
+ static int32_t BytePerPixelFromFormat(gfxImageFormat format);
+
+ virtual const mozilla::gfx::IntSize GetSize() const;
+
+ virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
+
+ void SetOpaqueRect(const gfxRect& aRect);
+
+ const gfxRect& GetOpaqueRect() {
+ if (!!mOpaqueRect)
+ return *mOpaqueRect;
+ return GetEmptyOpaqueRect();
+ }
+
+ static uint8_t BytesPerPixel(gfxImageFormat aImageFormat);
+
+protected:
+ gfxASurface();
+
+ static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
+ static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
+
+ // NB: Init() *must* be called from within subclass's
+ // constructors. It's unsafe to call it after the ctor finishes;
+ // leaks and use-after-frees are possible.
+ void Init(cairo_surface_t *surface, bool existingSurface = false);
+
+ // out-of-line helper to allow GetOpaqueRect() to be inlined
+ // without including gfxRect.h here
+ static const gfxRect& GetEmptyOpaqueRect();
+
+ virtual ~gfxASurface();
+
+ cairo_surface_t *mSurface;
+ mozilla::UniquePtr<gfxRect> mOpaqueRect;
+
+private:
+ static void SurfaceDestroyFunc(void *data);
+
+ int32_t mFloatingRefs;
+ int32_t mBytesRecorded;
+
+protected:
+ bool mSurfaceValid;
+};
+
+/**
+ * An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
+ */
+class gfxUnknownSurface : public gfxASurface {
+public:
+ gfxUnknownSurface(cairo_surface_t *surf, const mozilla::gfx::IntSize& aSize)
+ : mSize(aSize)
+ {
+ Init(surf, true);
+ }
+
+ virtual ~gfxUnknownSurface() { }
+ virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; }
+
+private:
+ mozilla::gfx::IntSize mSize;
+};
+
+#endif /* GFX_ASURFACE_H */
diff --git a/system/graphics/thebes/gfxAlphaRecovery.cpp b/system/graphics/thebes/gfxAlphaRecovery.cpp
new file mode 100644
index 000000000..810fbeffa
--- /dev/null
+++ b/system/graphics/thebes/gfxAlphaRecovery.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxAlphaRecovery.h"
+
+#include "gfxImageSurface.h"
+
+#define MOZILLA_SSE_INCLUDE_HEADER_FOR_SSE2
+#include "mozilla/SSE.h"
+
+/* static */ bool
+gfxAlphaRecovery::RecoverAlpha(gfxImageSurface* blackSurf,
+ const gfxImageSurface* whiteSurf)
+{
+ mozilla::gfx::IntSize size = blackSurf->GetSize();
+
+ if (size != whiteSurf->GetSize() ||
+ (blackSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
+ blackSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) ||
+ (whiteSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
+ whiteSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32))
+ return false;
+
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2() &&
+ RecoverAlphaSSE2(blackSurf, whiteSurf)) {
+ return true;
+ }
+#endif
+
+ blackSurf->Flush();
+ whiteSurf->Flush();
+
+ unsigned char* blackData = blackSurf->Data();
+ unsigned char* whiteData = whiteSurf->Data();
+
+ for (int32_t i = 0; i < size.height; ++i) {
+ uint32_t* blackPixel = reinterpret_cast<uint32_t*>(blackData);
+ const uint32_t* whitePixel = reinterpret_cast<uint32_t*>(whiteData);
+ for (int32_t j = 0; j < size.width; ++j) {
+ uint32_t recovered = RecoverPixel(blackPixel[j], whitePixel[j]);
+ blackPixel[j] = recovered;
+ }
+ blackData += blackSurf->Stride();
+ whiteData += whiteSurf->Stride();
+ }
+
+ blackSurf->MarkDirty();
+
+ return true;
+}
diff --git a/system/graphics/thebes/gfxAlphaRecovery.h b/system/graphics/thebes/gfxAlphaRecovery.h
new file mode 100644
index 000000000..9fed1681e
--- /dev/null
+++ b/system/graphics/thebes/gfxAlphaRecovery.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _GFXALPHARECOVERY_H_
+#define _GFXALPHARECOVERY_H_
+
+#include "mozilla/SSE.h"
+#include "gfxTypes.h"
+#include "mozilla/gfx/Rect.h"
+
+class gfxImageSurface;
+
+class gfxAlphaRecovery {
+public:
+ /**
+ * Some SIMD fast-paths only can be taken if the relative
+ * byte-alignment of images' pointers and strides meets certain
+ * criteria. Aligning image pointers and strides by
+ * |GoodAlignmentLog2()| below will ensure that fast-paths aren't
+ * skipped because of misalignment. Fast-paths may still be taken
+ * even if GoodAlignmentLog2() is not met, in some conditions.
+ */
+ static uint32_t GoodAlignmentLog2() { return 4; /* for SSE2 */ }
+
+ /* Given two surfaces of equal size with the same rendering, one onto a
+ * black background and the other onto white, recovers alpha values from
+ * the difference and sets the alpha values on the black surface.
+ * The surfaces must have format RGB24 or ARGB32.
+ * Returns true on success.
+ */
+ static bool RecoverAlpha (gfxImageSurface *blackSurface,
+ const gfxImageSurface *whiteSurface);
+
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ /* This does the same as the previous function, but uses SSE2
+ * optimizations. Usually this should not be called directly. Be sure to
+ * check mozilla::supports_sse2() before calling this function.
+ */
+ static bool RecoverAlphaSSE2 (gfxImageSurface *blackSurface,
+ const gfxImageSurface *whiteSurface);
+
+ /**
+ * A common use-case for alpha recovery is to paint into a
+ * temporary "white image", then paint onto a subrect of the
+ * surface, the "black image", into which alpha-recovered pixels
+ * are eventually to be written. This function returns a rect
+ * aligned so that recovering alpha for that rect will hit SIMD
+ * fast-paths, if possible. It's not always possible to align
+ * |aRect| so that fast-paths will be taken.
+ *
+ * The returned rect is always a superset of |aRect|.
+ */
+ static mozilla::gfx::IntRect AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
+ gfxImageSurface* aSurface);
+#else
+ static mozilla::gfx::IntRect AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
+ gfxImageSurface*)
+ { return aRect; }
+#endif
+
+ /** from cairo-xlib-utils.c, modified */
+ /**
+ * Given the RGB data for two image surfaces, one a source image composited
+ * with OVER onto a black background, and one a source image composited with
+ * OVER onto a white background, reconstruct the original image data into
+ * black_data.
+ *
+ * Consider a single color channel and a given pixel. Suppose the original
+ * premultiplied color value was C and the alpha value was A. Let the final
+ * on-black color be B and the final on-white color be W. All values range
+ * over 0-255.
+ *
+ * Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get
+ * A=255 - (W - C). Therefore it suffices to leave the black_data color
+ * data alone and set the alpha values using that simple formula. It shouldn't
+ * matter what color channel we pick for the alpha computation, but we'll
+ * pick green because if we went through a color channel downsample the green
+ * bits are likely to be the most accurate.
+ *
+ * This function needs to be in the header file since it's used by both
+ * gfxRecoverAlpha.cpp and gfxRecoverAlphaSSE2.cpp.
+ */
+
+ static inline uint32_t
+ RecoverPixel(uint32_t black, uint32_t white)
+ {
+ const uint32_t GREEN_MASK = 0x0000FF00;
+ const uint32_t ALPHA_MASK = 0xFF000000;
+
+ /* |diff| here is larger when the source image pixel is more transparent.
+ If both renderings are from the same source image composited with OVER,
+ then the color values on white will always be greater than those on
+ black, so |diff| would not overflow. However, overflow may happen, for
+ example, when a plugin plays a video and the image is rapidly changing.
+ If there is overflow, then behave as if we limit to the difference to
+ >= 0, which will make the rendering opaque. (Without this overflow
+ will make the rendering transparent.) */
+ uint32_t diff = (white & GREEN_MASK) - (black & GREEN_MASK);
+ /* |diff| is 0xFFFFxx00 on overflow and 0x0000xx00 otherwise, so use this
+ to limit the transparency. */
+ uint32_t limit = diff & ALPHA_MASK;
+ /* The alpha bits of the result */
+ uint32_t alpha = (ALPHA_MASK - (diff << 16)) | limit;
+
+ return alpha | (black & ~ALPHA_MASK);
+ }
+};
+
+#endif /* _GFXALPHARECOVERY_H_ */
diff --git a/system/graphics/thebes/gfxAlphaRecoverySSE2.cpp b/system/graphics/thebes/gfxAlphaRecoverySSE2.cpp
new file mode 100644
index 000000000..2c8987cbe
--- /dev/null
+++ b/system/graphics/thebes/gfxAlphaRecoverySSE2.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxAlphaRecovery.h"
+#include "gfxImageSurface.h"
+#include <emmintrin.h>
+
+// This file should only be compiled on x86 and x64 systems. Additionally,
+// you'll need to compile it with -msse2 if you're using GCC on x86.
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+__declspec(align(16)) static uint32_t greenMaski[] =
+ { 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
+__declspec(align(16)) static uint32_t alphaMaski[] =
+ { 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+static uint32_t greenMaski[] __attribute__ ((aligned (16))) =
+ { 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
+static uint32_t alphaMaski[] __attribute__ ((aligned (16))) =
+ { 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
+#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
+#pragma align 16 (greenMaski, alphaMaski)
+static uint32_t greenMaski[] = { 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
+static uint32_t alphaMaski[] = { 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
+#endif
+
+bool
+gfxAlphaRecovery::RecoverAlphaSSE2(gfxImageSurface* blackSurf,
+ const gfxImageSurface* whiteSurf)
+{
+ mozilla::gfx::IntSize size = blackSurf->GetSize();
+
+ if (size != whiteSurf->GetSize() ||
+ (blackSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
+ blackSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) ||
+ (whiteSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
+ whiteSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32))
+ return false;
+
+ blackSurf->Flush();
+ whiteSurf->Flush();
+
+ unsigned char* blackData = blackSurf->Data();
+ unsigned char* whiteData = whiteSurf->Data();
+
+ if ((NS_PTR_TO_UINT32(blackData) & 0xf) != (NS_PTR_TO_UINT32(whiteData) & 0xf) ||
+ (blackSurf->Stride() - whiteSurf->Stride()) & 0xf) {
+ // Cannot keep these in alignment.
+ return false;
+ }
+
+ __m128i greenMask = _mm_load_si128((__m128i*)greenMaski);
+ __m128i alphaMask = _mm_load_si128((__m128i*)alphaMaski);
+
+ for (int32_t i = 0; i < size.height; ++i) {
+ int32_t j = 0;
+ // Loop single pixels until at 4 byte alignment.
+ while (NS_PTR_TO_UINT32(blackData) & 0xf && j < size.width) {
+ *((uint32_t*)blackData) =
+ RecoverPixel(*reinterpret_cast<uint32_t*>(blackData),
+ *reinterpret_cast<uint32_t*>(whiteData));
+ blackData += 4;
+ whiteData += 4;
+ j++;
+ }
+ // This extra loop allows the compiler to do some more clever registry
+ // management and makes it about 5% faster than with only the 4 pixel
+ // at a time loop.
+ for (; j < size.width - 8; j += 8) {
+ __m128i black1 = _mm_load_si128((__m128i*)blackData);
+ __m128i white1 = _mm_load_si128((__m128i*)whiteData);
+ __m128i black2 = _mm_load_si128((__m128i*)(blackData + 16));
+ __m128i white2 = _mm_load_si128((__m128i*)(whiteData + 16));
+
+ // Execute the same instructions as described in RecoverPixel, only
+ // using an SSE2 packed saturated subtract.
+ white1 = _mm_subs_epu8(white1, black1);
+ white2 = _mm_subs_epu8(white2, black2);
+ white1 = _mm_subs_epu8(greenMask, white1);
+ white2 = _mm_subs_epu8(greenMask, white2);
+ // Producing the final black pixel in an XMM register and storing
+ // that is actually faster than doing a masked store since that
+ // does an unaligned storage. We have the black pixel in a register
+ // anyway.
+ black1 = _mm_andnot_si128(alphaMask, black1);
+ black2 = _mm_andnot_si128(alphaMask, black2);
+ white1 = _mm_slli_si128(white1, 2);
+ white2 = _mm_slli_si128(white2, 2);
+ white1 = _mm_and_si128(alphaMask, white1);
+ white2 = _mm_and_si128(alphaMask, white2);
+ black1 = _mm_or_si128(white1, black1);
+ black2 = _mm_or_si128(white2, black2);
+
+ _mm_store_si128((__m128i*)blackData, black1);
+ _mm_store_si128((__m128i*)(blackData + 16), black2);
+ blackData += 32;
+ whiteData += 32;
+ }
+ for (; j < size.width - 4; j += 4) {
+ __m128i black = _mm_load_si128((__m128i*)blackData);
+ __m128i white = _mm_load_si128((__m128i*)whiteData);
+
+ white = _mm_subs_epu8(white, black);
+ white = _mm_subs_epu8(greenMask, white);
+ black = _mm_andnot_si128(alphaMask, black);
+ white = _mm_slli_si128(white, 2);
+ white = _mm_and_si128(alphaMask, white);
+ black = _mm_or_si128(white, black);
+ _mm_store_si128((__m128i*)blackData, black);
+ blackData += 16;
+ whiteData += 16;
+ }
+ // Loop single pixels until we're done.
+ while (j < size.width) {
+ *((uint32_t*)blackData) =
+ RecoverPixel(*reinterpret_cast<uint32_t*>(blackData),
+ *reinterpret_cast<uint32_t*>(whiteData));
+ blackData += 4;
+ whiteData += 4;
+ j++;
+ }
+ blackData += blackSurf->Stride() - j * 4;
+ whiteData += whiteSurf->Stride() - j * 4;
+ }
+
+ blackSurf->MarkDirty();
+
+ return true;
+}
+
+static int32_t
+ByteAlignment(int32_t aAlignToLog2, int32_t aX, int32_t aY=0, int32_t aStride=1)
+{
+ return (aX + aStride * aY) & ((1 << aAlignToLog2) - 1);
+}
+
+/*static*/ mozilla::gfx::IntRect
+gfxAlphaRecovery::AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
+ gfxImageSurface* aSurface)
+{
+ NS_ASSERTION(mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 == aSurface->Format(),
+ "Thebes grew support for non-ARGB32 COLOR_ALPHA?");
+ static const int32_t kByteAlignLog2 = GoodAlignmentLog2();
+ static const int32_t bpp = 4;
+ static const int32_t pixPerAlign = (1 << kByteAlignLog2) / bpp;
+ //
+ // We're going to create a subimage of the surface with size
+ // <sw,sh> for alpha recovery, and want a SIMD fast-path. The
+ // rect <x,y, w,h> /needs/ to be redrawn, but it might not be
+ // properly aligned for SIMD. So we want to find a rect <x',y',
+ // w',h'> that's a superset of what needs to be redrawn but is
+ // properly aligned. Proper alignment is
+ //
+ // BPP * (x' + y' * sw) \cong 0 (mod ALIGN)
+ // BPP * w' \cong BPP * sw (mod ALIGN)
+ //
+ // (We assume the pixel at surface <0,0> is already ALIGN'd.)
+ // That rect (obviously) has to fit within the surface bounds, and
+ // we should also minimize the extra pixels redrawn only for
+ // alignment's sake. So we also want
+ //
+ // minimize <x',y', w',h'>
+ // 0 <= x' <= x
+ // 0 <= y' <= y
+ // w <= w' <= sw
+ // h <= h' <= sh
+ //
+ // This is a messy integer non-linear programming problem, except
+ // ... we can assume that ALIGN/BPP is a very small constant. So,
+ // brute force is viable. The algorithm below will find a
+ // solution if one exists, but isn't guaranteed to find the
+ // minimum solution. (For SSE2, ALIGN/BPP = 4, so it'll do at
+ // most 64 iterations below). In what's likely the common case,
+ // an already-aligned rectangle, it only needs 1 iteration.
+ //
+ // Is this alignment worth doing? Recovering alpha will take work
+ // proportional to w*h (assuming alpha recovery computation isn't
+ // memory bound). This analysis can lead to O(w+h) extra work
+ // (with small constants). In exchange, we expect to shave off a
+ // ALIGN/BPP constant by using SIMD-ized alpha recovery. So as
+ // w*h diverges from w+h, the win factor approaches ALIGN/BPP. We
+ // only really care about the w*h >> w+h case anyway; others
+ // should be fast enough even with the overhead. (Unless the cost
+ // of repainting the expanded rect is high, but in that case
+ // SIMD-ized alpha recovery won't make a difference so this code
+ // shouldn't be called.)
+ //
+ mozilla::gfx::IntSize surfaceSize = aSurface->GetSize();
+ const int32_t stride = bpp * surfaceSize.width;
+ if (stride != aSurface->Stride()) {
+ NS_WARNING("Unexpected stride, falling back on slow alpha recovery");
+ return aRect;
+ }
+
+ const int32_t x = aRect.x, y = aRect.y, w = aRect.width, h = aRect.height;
+ const int32_t r = x + w;
+ const int32_t sw = surfaceSize.width;
+ const int32_t strideAlign = ByteAlignment(kByteAlignLog2, stride);
+
+ // The outer two loops below keep the rightmost (|r| above) and
+ // bottommost pixels in |aRect| fixed wrt <x,y>, to ensure that we
+ // return only a superset of the original rect. These loops
+ // search for an aligned top-left pixel by trying to expand <x,y>
+ // left and up by <dx,dy> pixels, respectively.
+ //
+ // Then if a properly-aligned top-left pixel is found, the
+ // innermost loop tries to find an aligned stride by moving the
+ // rightmost pixel rightward by dr.
+ int32_t dx, dy, dr;
+ for (dy = 0; (dy < pixPerAlign) && (y - dy >= 0); ++dy) {
+ for (dx = 0; (dx < pixPerAlign) && (x - dx >= 0); ++dx) {
+ if (0 != ByteAlignment(kByteAlignLog2,
+ bpp * (x - dx), y - dy, stride)) {
+ continue;
+ }
+ for (dr = 0; (dr < pixPerAlign) && (r + dr <= sw); ++dr) {
+ if (strideAlign == ByteAlignment(kByteAlignLog2,
+ bpp * (w + dr + dx))) {
+ goto FOUND_SOLUTION;
+ }
+ }
+ }
+ }
+
+ // Didn't find a solution.
+ return aRect;
+
+FOUND_SOLUTION:
+ mozilla::gfx::IntRect solution = mozilla::gfx::IntRect(x - dx, y - dy, w + dr + dx, h + dy);
+ MOZ_ASSERT(mozilla::gfx::IntRect(0, 0, sw, surfaceSize.height).Contains(solution),
+ "'Solution' extends outside surface bounds!");
+ return solution;
+}
diff --git a/system/graphics/thebes/gfxBaseSharedMemorySurface.cpp b/system/graphics/thebes/gfxBaseSharedMemorySurface.cpp
new file mode 100644
index 000000000..7b84e9e6b
--- /dev/null
+++ b/system/graphics/thebes/gfxBaseSharedMemorySurface.cpp
@@ -0,0 +1,11 @@
+// vim:set ts=4 sts=4 sw=4 et cin:
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxBaseSharedMemorySurface.h"
+#include "cairo.h"
+
+const cairo_user_data_key_t SHM_KEY = {0};
+
diff --git a/system/graphics/thebes/gfxBaseSharedMemorySurface.h b/system/graphics/thebes/gfxBaseSharedMemorySurface.h
new file mode 100644
index 000000000..c9e2ab327
--- /dev/null
+++ b/system/graphics/thebes/gfxBaseSharedMemorySurface.h
@@ -0,0 +1,199 @@
+// vim:set ts=4 sts=4 sw=4 et cin:
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_SHARED_MEMORYSURFACE_H
+#define GFX_SHARED_MEMORYSURFACE_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/ipc/SharedMemory.h"
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+#include "pratom.h"
+
+typedef struct _cairo_user_data_key cairo_user_data_key_t;
+
+struct SharedImageInfo {
+ int32_t width;
+ int32_t height;
+ gfxImageFormat format;
+ int32_t readCount;
+};
+
+inline SharedImageInfo*
+GetShmInfoPtr(const mozilla::ipc::Shmem& aShmem)
+{
+ return reinterpret_cast<SharedImageInfo*>
+ (aShmem.get<char>() + aShmem.Size<char>() - sizeof(SharedImageInfo));
+}
+
+extern const cairo_user_data_key_t SHM_KEY;
+
+template <typename Base, typename Sub>
+class gfxBaseSharedMemorySurface : public Base {
+ typedef mozilla::ipc::SharedMemory SharedMemory;
+ typedef mozilla::ipc::Shmem Shmem;
+
+protected:
+ virtual ~gfxBaseSharedMemorySurface()
+ {
+ MOZ_COUNT_DTOR(gfxBaseSharedMemorySurface);
+ }
+
+public:
+ /**
+ * Return a new gfxSharedImageSurface around a shmem segment newly
+ * allocated by this function. |aAllocator| is the object used to
+ * allocate the new shmem segment. Null is returned if creating
+ * the surface failed.
+ *
+ * NB: the *caller* is responsible for freeing the Shmem allocated
+ * by this function.
+ */
+ template<class ShmemAllocator>
+ static already_AddRefed<Sub>
+ Create(ShmemAllocator* aAllocator,
+ const mozilla::gfx::IntSize& aSize,
+ gfxImageFormat aFormat,
+ SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
+ {
+ return Create<ShmemAllocator, false>(aAllocator, aSize, aFormat, aShmType);
+ }
+
+ /**
+ * Return a new gfxSharedImageSurface that wraps a shmem segment
+ * already created by the Create() above. Bad things will happen
+ * if an attempt is made to wrap any other shmem segment. Null is
+ * returned if creating the surface failed.
+ */
+ static already_AddRefed<Sub>
+ Open(const Shmem& aShmem)
+ {
+ SharedImageInfo* shmInfo = GetShmInfoPtr(aShmem);
+ mozilla::gfx::IntSize size(shmInfo->width, shmInfo->height);
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(size))
+ return nullptr;
+
+ gfxImageFormat format = shmInfo->format;
+ long stride = gfxImageSurface::ComputeStride(size, format);
+
+ RefPtr<Sub> s =
+ new Sub(size,
+ stride,
+ format,
+ aShmem);
+ // We didn't create this Shmem and so don't free it on errors
+ return (s->CairoStatus() != 0) ? nullptr : s.forget();
+ }
+
+ template<class ShmemAllocator>
+ static already_AddRefed<Sub>
+ CreateUnsafe(ShmemAllocator* aAllocator,
+ const mozilla::gfx::IntSize& aSize,
+ gfxImageFormat aFormat,
+ SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
+ {
+ return Create<ShmemAllocator, true>(aAllocator, aSize, aFormat, aShmType);
+ }
+
+ Shmem& GetShmem() { return mShmem; }
+
+ static bool IsSharedImage(gfxASurface *aSurface)
+ {
+ return (aSurface
+ && aSurface->GetType() == gfxSurfaceType::Image
+ && aSurface->GetData(&SHM_KEY));
+ }
+
+protected:
+ gfxBaseSharedMemorySurface(const mozilla::gfx::IntSize& aSize, long aStride,
+ gfxImageFormat aFormat,
+ const Shmem& aShmem)
+ : Base(aShmem.get<unsigned char>(), aSize, aStride, aFormat)
+ {
+ MOZ_COUNT_CTOR(gfxBaseSharedMemorySurface);
+
+ mShmem = aShmem;
+ this->SetData(&SHM_KEY, this, nullptr);
+ }
+
+private:
+ void WriteShmemInfo()
+ {
+ SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+ shmInfo->width = this->mSize.width;
+ shmInfo->height = this->mSize.height;
+ shmInfo->format = this->mFormat;
+ shmInfo->readCount = 0;
+ }
+
+ int32_t
+ ReadLock()
+ {
+ SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+ return PR_ATOMIC_INCREMENT(&shmInfo->readCount);
+ }
+
+ int32_t
+ ReadUnlock()
+ {
+ SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+ return PR_ATOMIC_DECREMENT(&shmInfo->readCount);
+ }
+
+ int32_t
+ GetReadCount()
+ {
+ SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+ return shmInfo->readCount;
+ }
+
+ static size_t GetAlignedSize(const mozilla::gfx::IntSize& aSize, long aStride)
+ {
+ #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
+ return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + aSize.height * aStride);
+ }
+
+ template<class ShmemAllocator, bool Unsafe>
+ static already_AddRefed<Sub>
+ Create(ShmemAllocator* aAllocator,
+ const mozilla::gfx::IntSize& aSize,
+ gfxImageFormat aFormat,
+ SharedMemory::SharedMemoryType aShmType)
+ {
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(aSize))
+ return nullptr;
+
+ Shmem shmem;
+ long stride = gfxImageSurface::ComputeStride(aSize, aFormat);
+ size_t size = GetAlignedSize(aSize, stride);
+ if (!Unsafe) {
+ if (!aAllocator->AllocShmem(size, aShmType, &shmem))
+ return nullptr;
+ } else {
+ if (!aAllocator->AllocUnsafeShmem(size, aShmType, &shmem))
+ return nullptr;
+ }
+
+ RefPtr<Sub> s =
+ new Sub(aSize, stride, aFormat, shmem);
+ if (s->CairoStatus() != 0) {
+ aAllocator->DeallocShmem(shmem);
+ return nullptr;
+ }
+ s->WriteShmemInfo();
+ return s.forget();
+ }
+
+ Shmem mShmem;
+
+ // Calling these is very bad, disallow it
+ gfxBaseSharedMemorySurface(const gfxBaseSharedMemorySurface&);
+ gfxBaseSharedMemorySurface& operator=(const gfxBaseSharedMemorySurface&);
+};
+
+#endif /* GFX_SHARED_MEMORYSURFACE_H */
diff --git a/system/graphics/thebes/gfxBlur.cpp b/system/graphics/thebes/gfxBlur.cpp
new file mode 100644
index 000000000..d5878e161
--- /dev/null
+++ b/system/graphics/thebes/gfxBlur.cpp
@@ -0,0 +1,1088 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxBlur.h"
+
+#include "gfx2DGlue.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Blur.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsExpirationTracker.h"
+#include "nsClassHashtable.h"
+#include "gfxUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxAlphaBoxBlur::gfxAlphaBoxBlur()
+{
+}
+
+gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
+{
+ mContext = nullptr;
+}
+
+gfxContext*
+gfxAlphaBoxBlur::Init(const gfxRect& aRect,
+ const IntSize& aSpreadRadius,
+ const IntSize& aBlurRadius,
+ const gfxRect* aDirtyRect,
+ const gfxRect* aSkipRect)
+{
+ mozilla::gfx::Rect rect(Float(aRect.x), Float(aRect.y),
+ Float(aRect.width), Float(aRect.height));
+ IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height);
+ IntSize blurRadius(aBlurRadius.width, aBlurRadius.height);
+ UniquePtr<Rect> dirtyRect;
+ if (aDirtyRect) {
+ dirtyRect = MakeUnique<Rect>(Float(aDirtyRect->x),
+ Float(aDirtyRect->y),
+ Float(aDirtyRect->width),
+ Float(aDirtyRect->height));
+ }
+ UniquePtr<Rect> skipRect;
+ if (aSkipRect) {
+ skipRect = MakeUnique<Rect>(Float(aSkipRect->x),
+ Float(aSkipRect->y),
+ Float(aSkipRect->width),
+ Float(aSkipRect->height));
+ }
+
+ mBlur = MakeUnique<AlphaBoxBlur>(rect, spreadRadius, blurRadius, dirtyRect.get(), skipRect.get());
+ size_t blurDataSize = mBlur->GetSurfaceAllocationSize();
+ if (blurDataSize == 0)
+ return nullptr;
+
+ IntSize size = mBlur->GetSize();
+
+ // Make an alpha-only surface to draw on. We will play with the data after
+ // everything is drawn to create a blur effect.
+ mData = MakeUniqueFallible<unsigned char[]>(blurDataSize);
+ if (!mData) {
+ return nullptr;
+ }
+ memset(mData.get(), 0, blurDataSize);
+
+ RefPtr<DrawTarget> dt =
+ gfxPlatform::CreateDrawTargetForData(mData.get(), size,
+ mBlur->GetStride(),
+ SurfaceFormat::A8);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ IntRect irect = mBlur->GetRect();
+ gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
+
+ mContext = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(mContext); // already checked for target above
+ mContext->SetMatrix(gfxMatrix::Translation(-topleft));
+
+ return mContext;
+}
+
+void
+DrawBlur(gfxContext* aDestinationCtx,
+ SourceSurface* aBlur,
+ const IntPoint& aTopLeft,
+ const Rect* aDirtyRect)
+{
+ DrawTarget *dest = aDestinationCtx->GetDrawTarget();
+
+ RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern();
+ Pattern* pat = thebesPat->GetPattern(dest, nullptr);
+
+ Matrix oldTransform = dest->GetTransform();
+ Matrix newTransform = oldTransform;
+ newTransform.PreTranslate(aTopLeft.x, aTopLeft.y);
+
+ // Avoid a semi-expensive clip operation if we can, otherwise
+ // clip to the dirty rect
+ if (aDirtyRect) {
+ dest->PushClipRect(*aDirtyRect);
+ }
+
+ dest->SetTransform(newTransform);
+ dest->MaskSurface(*pat, aBlur, Point(0, 0));
+ dest->SetTransform(oldTransform);
+
+ if (aDirtyRect) {
+ dest->PopClip();
+ }
+}
+
+already_AddRefed<SourceSurface>
+gfxAlphaBoxBlur::DoBlur(DrawTarget* aDT, IntPoint* aTopLeft)
+{
+ mBlur->Blur(mData.get());
+
+ *aTopLeft = mBlur->GetRect().TopLeft();
+
+ return aDT->CreateSourceSurfaceFromData(mData.get(),
+ mBlur->GetSize(),
+ mBlur->GetStride(),
+ SurfaceFormat::A8);
+}
+
+void
+gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx)
+{
+ if (!mContext)
+ return;
+
+ DrawTarget *dest = aDestinationCtx->GetDrawTarget();
+ if (!dest) {
+ NS_WARNING("Blurring not supported for Thebes contexts!");
+ return;
+ }
+
+ Rect* dirtyRect = mBlur->GetDirtyRect();
+
+ IntPoint topLeft;
+ RefPtr<SourceSurface> mask = DoBlur(dest, &topLeft);
+ if (!mask) {
+ NS_ERROR("Failed to create mask!");
+ return;
+ }
+
+ DrawBlur(aDestinationCtx, mask, topLeft, dirtyRect);
+}
+
+IntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd)
+{
+ mozilla::gfx::Point std(Float(aStd.x), Float(aStd.y));
+ IntSize size = AlphaBoxBlur::CalculateBlurRadius(std);
+ return IntSize(size.width, size.height);
+}
+
+struct BlurCacheKey : public PLDHashEntryHdr {
+ typedef const BlurCacheKey& KeyType;
+ typedef const BlurCacheKey* KeyTypePointer;
+ enum { ALLOW_MEMMOVE = true };
+
+ IntSize mMinSize;
+ IntSize mBlurRadius;
+ Color mShadowColor;
+ BackendType mBackend;
+ RectCornerRadii mCornerRadii;
+ bool mIsInset;
+
+ // Only used for inset blurs
+ bool mHasBorderRadius;
+ IntSize mInnerMinSize;
+
+ BlurCacheKey(IntSize aMinSize, IntSize aBlurRadius,
+ RectCornerRadii* aCornerRadii, const Color& aShadowColor,
+ BackendType aBackendType)
+ : BlurCacheKey(aMinSize, IntSize(0, 0),
+ aBlurRadius, aCornerRadii,
+ aShadowColor, false,
+ false, aBackendType)
+ {}
+
+ explicit BlurCacheKey(const BlurCacheKey* aOther)
+ : mMinSize(aOther->mMinSize)
+ , mBlurRadius(aOther->mBlurRadius)
+ , mShadowColor(aOther->mShadowColor)
+ , mBackend(aOther->mBackend)
+ , mCornerRadii(aOther->mCornerRadii)
+ , mIsInset(aOther->mIsInset)
+ , mHasBorderRadius(aOther->mHasBorderRadius)
+ , mInnerMinSize(aOther->mInnerMinSize)
+ { }
+
+ explicit BlurCacheKey(IntSize aOuterMinSize, IntSize aInnerMinSize,
+ IntSize aBlurRadius,
+ const RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor, bool aIsInset,
+ bool aHasBorderRadius, BackendType aBackendType)
+ : mMinSize(aOuterMinSize)
+ , mBlurRadius(aBlurRadius)
+ , mShadowColor(aShadowColor)
+ , mBackend(aBackendType)
+ , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
+ , mIsInset(aIsInset)
+ , mHasBorderRadius(aHasBorderRadius)
+ , mInnerMinSize(aInnerMinSize)
+ { }
+
+ static PLDHashNumber
+ HashKey(const KeyTypePointer aKey)
+ {
+ PLDHashNumber hash = 0;
+ hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height);
+ hash = AddToHash(hash, aKey->mBlurRadius.width, aKey->mBlurRadius.height);
+
+ hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.r,
+ sizeof(aKey->mShadowColor.r)));
+ hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.g,
+ sizeof(aKey->mShadowColor.g)));
+ hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.b,
+ sizeof(aKey->mShadowColor.b)));
+ hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.a,
+ sizeof(aKey->mShadowColor.a)));
+
+ for (int i = 0; i < 4; i++) {
+ hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
+ }
+
+ hash = AddToHash(hash, (uint32_t)aKey->mBackend);
+
+ if (aKey->mIsInset) {
+ hash = AddToHash(hash, aKey->mInnerMinSize.width, aKey->mInnerMinSize.height);
+ hash = AddToHash(hash, HashBytes(&aKey->mHasBorderRadius, sizeof(bool)));
+ }
+ return hash;
+ }
+
+ bool
+ KeyEquals(KeyTypePointer aKey) const
+ {
+ if (aKey->mMinSize == mMinSize &&
+ aKey->mBlurRadius == mBlurRadius &&
+ aKey->mCornerRadii == mCornerRadii &&
+ aKey->mShadowColor == mShadowColor &&
+ aKey->mBackend == mBackend) {
+
+ if (mIsInset) {
+ return (mHasBorderRadius == aKey->mHasBorderRadius) &&
+ (mInnerMinSize == aKey->mInnerMinSize);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ static KeyTypePointer
+ KeyToPointer(KeyType aKey)
+ {
+ return &aKey;
+ }
+};
+
+/**
+ * This class is what is cached. It need to be allocated in an object separated
+ * to the cache entry to be able to be tracked by the nsExpirationTracker.
+ * */
+struct BlurCacheData {
+ BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey)
+ : mBlur(aBlur)
+ , mExtendDest(aExtendDestBy)
+ , mKey(aKey)
+ {}
+
+ BlurCacheData(const BlurCacheData& aOther)
+ : mBlur(aOther.mBlur)
+ , mExtendDest(aOther.mExtendDest)
+ , mKey(aOther.mKey)
+ { }
+
+ nsExpirationState *GetExpirationState() {
+ return &mExpirationState;
+ }
+
+ nsExpirationState mExpirationState;
+ RefPtr<SourceSurface> mBlur;
+ IntMargin mExtendDest;
+ BlurCacheKey mKey;
+};
+
+/**
+ * This class implements a cache with no maximum size, that retains the
+ * SourceSurfaces used to draw the blurs.
+ *
+ * An entry stays in the cache as long as it is used often.
+ */
+class BlurCache final : public nsExpirationTracker<BlurCacheData,4>
+{
+ public:
+ BlurCache()
+ : nsExpirationTracker<BlurCacheData, 4>(GENERATION_MS, "BlurCache")
+ {
+ }
+
+ virtual void NotifyExpired(BlurCacheData* aObject)
+ {
+ RemoveObject(aObject);
+ mHashEntries.Remove(aObject->mKey);
+ }
+
+ BlurCacheData* Lookup(const IntSize aMinSize,
+ const IntSize& aBlurRadius,
+ RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor,
+ BackendType aBackendType)
+ {
+ BlurCacheData* blur =
+ mHashEntries.Get(BlurCacheKey(aMinSize, aBlurRadius,
+ aCornerRadii, aShadowColor,
+ aBackendType));
+ if (blur) {
+ MarkUsed(blur);
+ }
+
+ return blur;
+ }
+
+ BlurCacheData* LookupInsetBoxShadow(const IntSize aOuterMinSize,
+ const IntSize aInnerMinSize,
+ const IntSize& aBlurRadius,
+ const RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor,
+ const bool& aHasBorderRadius,
+ BackendType aBackendType)
+ {
+ bool insetBoxShadow = true;
+ BlurCacheKey key(aOuterMinSize, aInnerMinSize,
+ aBlurRadius, aCornerRadii,
+ aShadowColor, insetBoxShadow,
+ aHasBorderRadius, aBackendType);
+ BlurCacheData* blur = mHashEntries.Get(key);
+ if (blur) {
+ MarkUsed(blur);
+ }
+
+ return blur;
+ }
+
+ // Returns true if we successfully register the blur in the cache, false
+ // otherwise.
+ bool RegisterEntry(BlurCacheData* aValue)
+ {
+ nsresult rv = AddObject(aValue);
+ if (NS_FAILED(rv)) {
+ // We are OOM, and we cannot track this object. We don't want stall
+ // entries in the hash table (since the expiration tracker is responsible
+ // for removing the cache entries), so we avoid putting that entry in the
+ // table, which is a good things considering we are short on memory
+ // anyway, we probably don't want to retain things.
+ return false;
+ }
+ mHashEntries.Put(aValue->mKey, aValue);
+ return true;
+ }
+
+ protected:
+ static const uint32_t GENERATION_MS = 1000;
+ /**
+ * FIXME use nsTHashtable to avoid duplicating the BlurCacheKey.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
+ */
+ nsClassHashtable<BlurCacheKey, BlurCacheData> mHashEntries;
+};
+
+static BlurCache* gBlurCache = nullptr;
+
+static IntSize
+ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii,
+ IntSize aBlurRadius,
+ IntMargin& aSlice,
+ const IntSize& aRectSize)
+{
+ float cornerWidth = 0;
+ float cornerHeight = 0;
+ if (aCornerRadii) {
+ RectCornerRadii corners = *aCornerRadii;
+ for (size_t i = 0; i < 4; i++) {
+ cornerWidth = std::max(cornerWidth, corners[i].width);
+ cornerHeight = std::max(cornerHeight, corners[i].height);
+ }
+ }
+
+ aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height,
+ ceil(cornerWidth) + aBlurRadius.width,
+ ceil(cornerHeight) + aBlurRadius.height,
+ ceil(cornerWidth) + aBlurRadius.width);
+
+ IntSize minSize(aSlice.LeftRight() + 1,
+ aSlice.TopBottom() + 1);
+
+ // If aRectSize is smaller than minSize, the border-image approach won't
+ // work; there's no way to squeeze parts of the min box-shadow source
+ // image such that the result looks correct. So we need to adjust minSize
+ // in such a way that we can later draw it without stretching in the affected
+ // dimension. We also need to adjust "slice" to ensure that we're not trying
+ // to slice away more than we have.
+ if (aRectSize.width < minSize.width) {
+ minSize.width = aRectSize.width;
+ aSlice.left = 0;
+ aSlice.right = 0;
+ }
+ if (aRectSize.height < minSize.height) {
+ minSize.height = aRectSize.height;
+ aSlice.top = 0;
+ aSlice.bottom = 0;
+ }
+
+ MOZ_ASSERT(aSlice.LeftRight() <= minSize.width);
+ MOZ_ASSERT(aSlice.TopBottom() <= minSize.height);
+ return minSize;
+}
+
+void
+CacheBlur(DrawTarget& aDT,
+ const IntSize& aMinSize,
+ const IntSize& aBlurRadius,
+ RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor,
+ IntMargin aExtendDest,
+ SourceSurface* aBoxShadow)
+{
+ BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType());
+ BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
+ if (!gBlurCache->RegisterEntry(data)) {
+ delete data;
+ }
+}
+
+// Blurs a small surface and creates the mask.
+static already_AddRefed<SourceSurface>
+CreateBlurMask(const IntSize& aMinSize,
+ RectCornerRadii* aCornerRadii,
+ IntSize aBlurRadius,
+ IntMargin& aExtendDestBy,
+ IntMargin& aSliceBorder,
+ DrawTarget& aDestDrawTarget)
+{
+ gfxAlphaBoxBlur blur;
+ IntRect minRect(IntPoint(), aMinSize);
+
+ gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), IntSize(),
+ aBlurRadius, nullptr, nullptr);
+
+ if (!blurCtx) {
+ return nullptr;
+ }
+
+ DrawTarget* blurDT = blurCtx->GetDrawTarget();
+ ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
+
+ if (aCornerRadii) {
+ RefPtr<Path> roundedRect =
+ MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii);
+ blurDT->Fill(roundedRect, black);
+ } else {
+ blurDT->FillRect(Rect(minRect), black);
+ }
+
+ IntPoint topLeft;
+ RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
+ if (!result) {
+ return nullptr;
+ }
+
+ IntRect expandedMinRect(topLeft, result->GetSize());
+ aExtendDestBy = expandedMinRect - minRect;
+ aSliceBorder += aExtendDestBy;
+
+ MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
+ MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
+
+ return result.forget();
+}
+
+static already_AddRefed<SourceSurface>
+CreateBoxShadow(DrawTarget& aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
+{
+ IntSize blurredSize = aBlurMask->GetSize();
+ RefPtr<DrawTarget> boxShadowDT =
+ Factory::CreateDrawTarget(aDestDT.GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8);
+
+ if (!boxShadowDT) {
+ return nullptr;
+ }
+
+ ColorPattern shadowColor(ToDeviceColor(aShadowColor));
+ boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
+ return boxShadowDT->Snapshot();
+}
+
+static already_AddRefed<SourceSurface>
+GetBlur(gfxContext* aDestinationCtx,
+ const IntSize& aRectSize,
+ const IntSize& aBlurRadius,
+ RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor,
+ IntMargin& aExtendDestBy,
+ IntMargin& aSlice)
+{
+ if (!gBlurCache) {
+ gBlurCache = new BlurCache();
+ }
+
+ IntSize minSize =
+ ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);
+
+ // We can get seams using the min size rect when drawing to the destination rect
+ // if we have a non-pixel aligned destination transformation. In those cases,
+ // fallback to just rendering the destination rect.
+ Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
+ bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation();
+ if (useDestRect) {
+ minSize = aRectSize;
+ }
+
+ DrawTarget& destDT = *aDestinationCtx->GetDrawTarget();
+
+ BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
+ aCornerRadii, aShadowColor,
+ destDT.GetBackendType());
+ if (cached && !useDestRect) {
+ // See CreateBlurMask() for these values
+ aExtendDestBy = cached->mExtendDest;
+ aSlice = aSlice + aExtendDestBy;
+ RefPtr<SourceSurface> blur = cached->mBlur;
+ return blur.forget();
+ }
+
+ RefPtr<SourceSurface> blurMask =
+ CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice,
+ destDT);
+
+ if (!blurMask) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDT, blurMask, aShadowColor);
+ if (!boxShadow) {
+ return nullptr;
+ }
+
+ if (useDestRect) {
+ // Since we're just going to paint the actual rect to the destination
+ aSlice.SizeTo(0, 0, 0, 0);
+ } else {
+ CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
+ aExtendDestBy, boxShadow);
+ }
+ return boxShadow.forget();
+}
+
+void
+gfxAlphaBoxBlur::ShutdownBlurCache()
+{
+ delete gBlurCache;
+ gBlurCache = nullptr;
+}
+
+static Rect
+RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft)
+{
+ return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop);
+}
+
+static void
+RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface,
+ const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
+{
+ if (aSkipRect.Contains(aDest)) {
+ return;
+ }
+
+ if ((!aDT.GetTransform().IsRectilinear() &&
+ aDT.GetBackendType() != BackendType::CAIRO) ||
+ (aDT.GetBackendType() == BackendType::DIRECT2D1_1)) {
+ // Use stretching if possible, since it leads to less seams when the
+ // destination is transformed. However, don't do this if we're using cairo,
+ // because if cairo is using pixman it won't render anything for large
+ // stretch factors because pixman's internal fixed point precision is not
+ // high enough to handle those scale factors.
+ // Calling FillRect on a D2D backend with a repeating pattern is much slower
+ // than DrawSurface, so special case the D2D backend here.
+ aDT.DrawSurface(aSurface, aDest, aSrc);
+ return;
+ }
+
+ SurfacePattern pattern(aSurface, ExtendMode::REPEAT,
+ Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()),
+ SamplingFilter::GOOD, RoundedToInt(aSrc));
+ aDT.FillRect(aDest, pattern);
+}
+
+static void
+DrawCorner(DrawTarget& aDT, SourceSurface* aSurface,
+ const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
+{
+ if (aSkipRect.Contains(aDest)) {
+ return;
+ }
+
+ aDT.DrawSurface(aSurface, aDest, aSrc);
+}
+
+static void
+DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur,
+ Rect aDstOuter, Rect aDstInner, Rect aSrcOuter, Rect aSrcInner,
+ Rect aSkipRect)
+{
+ // Corners: top left, top right, bottom left, bottom right
+ DrawCorner(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.X(),
+ aDstInner.Y(), aDstOuter.X()),
+ RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.X(),
+ aSrcInner.Y(), aSrcOuter.X()),
+ aSkipRect);
+
+ DrawCorner(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstOuter.Y(), aDstOuter.XMost(),
+ aDstInner.Y(), aDstInner.XMost()),
+ RectWithEdgesTRBL(aSrcOuter.Y(), aSrcOuter.XMost(),
+ aSrcInner.Y(), aSrcInner.XMost()),
+ aSkipRect);
+
+ DrawCorner(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.X(),
+ aDstOuter.YMost(), aDstOuter.X()),
+ RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.X(),
+ aSrcOuter.YMost(), aSrcOuter.X()),
+ aSkipRect);
+
+ DrawCorner(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstInner.YMost(), aDstOuter.XMost(),
+ aDstOuter.YMost(), aDstInner.XMost()),
+ RectWithEdgesTRBL(aSrcInner.YMost(), aSrcOuter.XMost(),
+ aSrcOuter.YMost(), aSrcInner.XMost()),
+ aSkipRect);
+
+ // Edges: top, left, right, bottom
+ RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.XMost(),
+ aDstInner.Y(), aDstInner.X()),
+ RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(),
+ aSrcInner.Y(), aSrcInner.X()),
+ aSkipRect);
+ RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstInner.Y(), aDstInner.X(),
+ aDstInner.YMost(), aDstOuter.X()),
+ RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(),
+ aSrcInner.YMost(), aSrcOuter.X()),
+ aSkipRect);
+
+ RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstInner.Y(), aDstOuter.XMost(),
+ aDstInner.YMost(), aDstInner.XMost()),
+ RectWithEdgesTRBL(aSrcInner.Y(), aSrcOuter.XMost(),
+ aSrcInner.YMost(), aSrcInner.XMost()),
+ aSkipRect);
+ RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+ RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.XMost(),
+ aDstOuter.YMost(), aDstInner.X()),
+ RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.XMost(),
+ aSrcOuter.YMost(), aSrcInner.X()),
+ aSkipRect);
+}
+
+/***
+ * We draw a blurred a rectangle by only blurring a smaller rectangle and
+ * splitting the rectangle into 9 parts.
+ * First, a small minimum source rect is calculated and used to create a blur
+ * mask since the actual blurring itself is expensive. Next, we use the mask
+ * with the given shadow color to create a minimally-sized box shadow of the
+ * right color. Finally, we cut out the 9 parts from the box-shadow source and
+ * paint each part in the right place, stretching the non-corner parts to fill
+ * the space between the corners.
+ */
+
+/* static */ void
+gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
+ const gfxRect& aRect,
+ RectCornerRadii* aCornerRadii,
+ const gfxPoint& aBlurStdDev,
+ const Color& aShadowColor,
+ const gfxRect& aDirtyRect,
+ const gfxRect& aSkipRect)
+{
+ IntSize blurRadius = CalculateBlurRadius(aBlurStdDev);
+
+ IntRect rect = RoundedToInt(ToRect(aRect));
+ IntMargin extendDestBy;
+ IntMargin slice;
+
+ RefPtr<SourceSurface> boxShadow = GetBlur(aDestinationCtx,
+ rect.Size(), blurRadius,
+ aCornerRadii, aShadowColor,
+ extendDestBy, slice);
+ if (!boxShadow) {
+ return;
+ }
+
+ DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget();
+ destDrawTarget.PushClipRect(ToRect(aDirtyRect));
+
+ // Copy the right parts from boxShadow into destDrawTarget. The middle parts
+ // will be stretched, border-image style.
+
+ Rect srcOuter(Point(), Size(boxShadow->GetSize()));
+ Rect srcInner = srcOuter;
+ srcInner.Deflate(Margin(slice));
+
+ rect.Inflate(extendDestBy);
+ Rect dstOuter(rect);
+ Rect dstInner(rect);
+ dstInner.Deflate(Margin(slice));
+
+ Rect skipRect = ToRect(aSkipRect);
+
+ if (srcInner.IsEqualInterior(srcOuter)) {
+ MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter));
+ // The target rect is smaller than the minimal size so just draw the surface
+ destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner);
+ } else {
+ DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner,
+ srcOuter, srcInner, skipRect);
+
+ // Middle part
+ RepeatOrStretchSurface(destDrawTarget, boxShadow,
+ RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(),
+ dstInner.YMost(), dstInner.X()),
+ RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(),
+ srcInner.YMost(), srcInner.X()),
+ skipRect);
+ }
+
+ // A note about anti-aliasing and seems between adjacent parts:
+ // We don't explicitly disable anti-aliasing in the DrawSurface calls above,
+ // so if there's a transform on destDrawTarget that is not pixel-aligned,
+ // there will be seams between adjacent parts of the box-shadow. It's hard to
+ // avoid those without the use of an intermediate surface.
+ // You might think that we could avoid those by just turning of AA, but there
+ // is a problem with that: Box-shadow rendering needs to clip out the
+ // element's border box, and we'd like that clip to have anti-aliasing -
+ // especially if the element has rounded corners! So we can't do that unless
+ // we have a way to say "Please anti-alias the clip, but don't antialias the
+ // destination rect of the DrawSurface call".
+ // On OS X there is an additional problem with turning off AA: CoreGraphics
+ // will not just fill the pixels that have their pixel center inside the
+ // filled shape. Instead, it will fill all the pixels which are partially
+ // covered by the shape. So for pixels on the edge between two adjacent parts,
+ // all those pixels will be painted to by both parts, which looks very bad.
+
+ destDrawTarget.PopClip();
+}
+
+static already_AddRefed<Path>
+GetBoxShadowInsetPath(DrawTarget* aDrawTarget,
+ const Rect aOuterRect, const Rect aInnerRect,
+ const bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii)
+{
+ /***
+ * We create an inset path by having two rects.
+ *
+ * -----------------------
+ * | ________________ |
+ * | | | |
+ * | | | |
+ * | ------------------ |
+ * |_____________________|
+ *
+ * The outer rect and the inside rect. The path
+ * creates a frame around the content where we draw the inset shadow.
+ */
+ RefPtr<PathBuilder> builder =
+ aDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD);
+ AppendRectToPath(builder, aOuterRect, true);
+
+ if (aHasBorderRadius) {
+ AppendRoundedRectToPath(builder, aInnerRect, aInnerClipRadii, false);
+ } else {
+ AppendRectToPath(builder, aInnerRect, false);
+ }
+ return builder->Finish();
+}
+
+static void
+FillDestinationPath(gfxContext* aDestinationCtx,
+ const Rect aDestinationRect,
+ const Rect aShadowClipRect,
+ const Color& aShadowColor,
+ const bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii)
+{
+ // When there is no blur radius, fill the path onto the destination
+ // surface.
+ aDestinationCtx->SetColor(aShadowColor);
+ DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
+ RefPtr<Path> shadowPath = GetBoxShadowInsetPath(destDrawTarget, aDestinationRect,
+ aShadowClipRect, aHasBorderRadius,
+ aInnerClipRadii);
+
+ aDestinationCtx->SetPath(shadowPath);
+ aDestinationCtx->Fill();
+}
+
+static void
+CacheInsetBlur(const IntSize aMinOuterSize,
+ const IntSize aMinInnerSize,
+ const IntSize& aBlurRadius,
+ const RectCornerRadii* aCornerRadii,
+ const Color& aShadowColor,
+ const bool& aHasBorderRadius,
+ BackendType aBackendType,
+ SourceSurface* aBoxShadow)
+{
+ bool isInsetBlur = true;
+ BlurCacheKey key(aMinOuterSize, aMinInnerSize,
+ aBlurRadius, aCornerRadii,
+ aShadowColor, isInsetBlur,
+ aHasBorderRadius, aBackendType);
+ IntMargin extendDestBy(0, 0, 0, 0);
+ BlurCacheData* data = new BlurCacheData(aBoxShadow, extendDestBy, key);
+ if (!gBlurCache->RegisterEntry(data)) {
+ delete data;
+ }
+}
+
+already_AddRefed<mozilla::gfx::SourceSurface>
+gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect,
+ const mozilla::gfx::Rect aWhitespaceRect,
+ const bool aIsDestRect,
+ const mozilla::gfx::Color& aShadowColor,
+ const mozilla::gfx::IntSize& aBlurRadius,
+ const bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii,
+ DrawTarget* aDestDrawTarget)
+{
+ if (!gBlurCache) {
+ gBlurCache = new BlurCache();
+ }
+
+ IntSize outerSize((int)aOuterRect.width, (int)aOuterRect.height);
+ IntSize whitespaceSize((int)aWhitespaceRect.width, (int)aWhitespaceRect.height);
+ BlurCacheData* cached =
+ gBlurCache->LookupInsetBoxShadow(outerSize, whitespaceSize,
+ aBlurRadius, &aInnerClipRadii,
+ aShadowColor, aHasBorderRadius,
+ aDestDrawTarget->GetBackendType());
+
+ if (cached && !aIsDestRect) {
+ // So we don't forget the actual cached blur
+ RefPtr<SourceSurface> cachedBlur = cached->mBlur;
+ return cachedBlur.forget();
+ }
+
+ // If we can do a min rect, the whitespace rect will be expanded in Init to
+ // aOuterRect.
+ Rect blurRect = aIsDestRect ? aOuterRect : aWhitespaceRect;
+ IntSize zeroSpread(0, 0);
+ gfxContext* minGfxContext = Init(ThebesRect(blurRect),
+ zeroSpread, aBlurRadius,
+ nullptr, nullptr);
+ if (!minGfxContext) {
+ return nullptr;
+ }
+
+ // This is really annoying. When we create the AlphaBoxBlur, the gfxContext
+ // has a translation applied to it that is the topLeft point. This is actually
+ // the rect we gave it plus the blur radius. The rects we give this for the outer
+ // and whitespace rects are based at (0, 0). We could either translate those rects
+ // when we don't have a destination rect or ignore the translation when using
+ // the dest rect. The dest rects layout gives us expect this translation.
+ if (!aIsDestRect) {
+ minGfxContext->SetMatrix(gfxMatrix());
+ }
+
+ DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
+
+ // Fill in the path between the inside white space / outer rects
+ // NOT the inner frame
+ RefPtr<Path> maskPath =
+ GetBoxShadowInsetPath(minDrawTarget, aOuterRect,
+ aWhitespaceRect, aHasBorderRadius,
+ aInnerClipRadii);
+
+ Color black(0.f, 0.f, 0.f, 1.f);
+ minGfxContext->SetColor(black);
+ minGfxContext->SetPath(maskPath);
+ minGfxContext->Fill();
+
+ // Create the A8 mask
+ IntPoint topLeft;
+ RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
+ if (!minMask) {
+ return nullptr;
+ }
+
+ // Fill in with the color we actually wanted
+ RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(*aDestDrawTarget, minMask, aShadowColor);
+ if (!minInsetBlur) {
+ return nullptr;
+ }
+
+ if (!aIsDestRect) {
+ CacheInsetBlur(outerSize, whitespaceSize,
+ aBlurRadius, &aInnerClipRadii,
+ aShadowColor, aHasBorderRadius,
+ aDestDrawTarget->GetBackendType(),
+ minInsetBlur);
+ }
+
+ return minInsetBlur.forget();
+}
+
+/***
+ * We create our minimal rect with 2 rects.
+ * The first is the inside whitespace rect, that is "cut out"
+ * from the box. This is (1). This must be the size
+ * of the blur radius + corner radius so we can have a big enough
+ * inside cut.
+ *
+ * The second (2) is one blur radius surrounding the inner
+ * frame of (1). This is the amount of blur space required
+ * to get a proper blend.
+ *
+ * B = one blur size
+ * W = one blur + corner radii - known as inner margin
+ * ___________________________________
+ * | |
+ * | | | |
+ * | (2) | (1) | (2) |
+ * | B | W | B |
+ * | | | |
+ * | | | |
+ * | | |
+ * |________________________________|
+ */
+static void GetBlurMargins(const bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii,
+ const IntSize aBlurRadius,
+ Margin& aOutBlurMargin,
+ Margin& aOutInnerMargin)
+{
+ float cornerWidth = 0;
+ float cornerHeight = 0;
+ if (aHasBorderRadius) {
+ for (size_t i = 0; i < 4; i++) {
+ cornerWidth = std::max(cornerWidth, aInnerClipRadii[i].width);
+ cornerHeight = std::max(cornerHeight, aInnerClipRadii[i].height);
+ }
+ }
+
+ // Only the inside whitespace size cares about the border radius size.
+ // Outer sizes only care about blur.
+ int width = cornerWidth + aBlurRadius.width;
+ int height = cornerHeight + aBlurRadius.height;
+
+ aOutInnerMargin.SizeTo(height, width, height, width);
+ aOutBlurMargin.SizeTo(aBlurRadius.height, aBlurRadius.width,
+ aBlurRadius.height, aBlurRadius.width);
+}
+
+static bool
+GetInsetBoxShadowRects(const Margin aBlurMargin,
+ const Margin aInnerMargin,
+ const Rect aShadowClipRect,
+ const Rect aDestinationRect,
+ Rect& aOutWhitespaceRect,
+ Rect& aOutOuterRect)
+{
+ // We always copy (2 * blur radius) + corner radius worth of data to the destination rect
+ // This covers the blend of the path + the actual blur
+ // Need +1 so that we copy the edges correctly as we'll copy
+ // over the min box shadow corners then the +1 for the edges between
+ // Note, the (x,y) coordinates are from the blur margin
+ // since the frame outside the whitespace rect is 1 blur radius extra space.
+ Rect insideWhiteSpace(aBlurMargin.left,
+ aBlurMargin.top,
+ aInnerMargin.LeftRight() + 1,
+ aInnerMargin.TopBottom() + 1);
+
+ // If the inner white space rect is larger than the shadow clip rect
+ // our approach does not work as we'll just copy one corner
+ // and cover the destination. In those cases, fallback to the destination rect
+ bool useDestRect = (aShadowClipRect.width <= aInnerMargin.LeftRight()) ||
+ (aShadowClipRect.height <= aInnerMargin.TopBottom());
+
+ if (useDestRect) {
+ aOutWhitespaceRect = aShadowClipRect;
+ aOutOuterRect = aDestinationRect;
+ } else {
+ aOutWhitespaceRect = insideWhiteSpace;
+ aOutOuterRect = aOutWhitespaceRect;
+ aOutOuterRect.Inflate(aBlurMargin);
+ }
+
+ return useDestRect;
+}
+
+void
+gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx,
+ const Rect aDestinationRect,
+ const Rect aShadowClipRect,
+ const IntSize aBlurRadius,
+ const IntSize aSpreadRadius,
+ const Color& aShadowColor,
+ bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii,
+ const Rect aSkipRect,
+ const Point aShadowOffset)
+{
+ if ((aBlurRadius.width == 0 && aBlurRadius.height == 0)
+ || aShadowClipRect.IsEmpty()) {
+ FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect,
+ aShadowColor, aHasBorderRadius, aInnerClipRadii);
+ return;
+ }
+
+ DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
+
+ Margin innerMargin;
+ Margin blurMargin;
+ GetBlurMargins(aHasBorderRadius, aInnerClipRadii, aBlurRadius,
+ blurMargin, innerMargin);
+
+ Rect whitespaceRect;
+ Rect outerRect;
+ bool useDestRect = GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect,
+ aDestinationRect, whitespaceRect, outerRect);
+
+ RefPtr<SourceSurface> minBlur = GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor,
+ aBlurRadius, aHasBorderRadius, aInnerClipRadii,
+ destDrawTarget);
+ if (!minBlur) {
+ return;
+ }
+
+ if (useDestRect) {
+ IntSize blurSize = minBlur->GetSize();
+ Rect srcBlur(0, 0, blurSize.width, blurSize.height);
+ Rect destBlur = aDestinationRect;
+
+ // The blur itself expands the rect by the blur margin, so we
+ // have to mimic that here.
+ destBlur.Inflate(blurMargin);
+ MOZ_ASSERT(srcBlur.Size() == destBlur.Size());
+ destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur);
+ } else {
+ Rect srcOuter(outerRect);
+ Rect srcInner(srcOuter);
+ srcInner.Deflate(blurMargin); // The outer color fill
+ srcInner.Deflate(innerMargin); // The inner whitespace
+
+ // The shadow clip rect already takes into account the spread radius
+ Rect outerFillRect(aShadowClipRect);
+ outerFillRect.Inflate(blurMargin);
+ FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor, false, RectCornerRadii());
+
+ // Inflate once for the frame around the whitespace
+ Rect destRect(aShadowClipRect);
+ destRect.Inflate(blurMargin);
+
+ // Deflate for the blurred in white space
+ Rect destInnerRect(aShadowClipRect);
+ destInnerRect.Deflate(innerMargin);
+
+ DrawBoxShadows(*destDrawTarget, minBlur,
+ destRect, destInnerRect,
+ srcOuter, srcInner,
+ aSkipRect);
+ }
+}
diff --git a/system/graphics/thebes/gfxBlur.h b/system/graphics/thebes/gfxBlur.h
new file mode 100644
index 000000000..9cc5f3716
--- /dev/null
+++ b/system/graphics/thebes/gfxBlur.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BLUR_H
+#define GFX_BLUR_H
+
+#include "gfxTypes.h"
+#include "nsSize.h"
+#include "gfxPoint.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+class gfxContext;
+struct gfxRect;
+
+namespace mozilla {
+ namespace gfx {
+ class AlphaBoxBlur;
+ struct Color;
+ struct RectCornerRadii;
+ class SourceSurface;
+ class DrawTarget;
+ } // namespace gfx
+} // namespace mozilla
+
+/**
+ * Implementation of a triple box blur approximation of a Gaussian blur.
+ *
+ * A Gaussian blur is good for blurring because, when done independently
+ * in the horizontal and vertical directions, it matches the result that
+ * would be obtained using a different (rotated) set of axes. A triple
+ * box blur is a very close approximation of a Gaussian.
+ *
+ * Creates an 8-bit alpha channel context for callers to draw in,
+ * spreads the contents of that context, blurs the contents, and applies
+ * it as an alpha mask on a different existing context.
+ *
+ * A spread N makes each output pixel the maximum value of all source
+ * pixels within a square of side length 2N+1 centered on the output pixel.
+ *
+ * A temporary surface is created in the Init function. The caller then draws
+ * any desired content onto the context acquired through GetContext, and lastly
+ * calls Paint to apply the blurred content as an alpha mask.
+ */
+class gfxAlphaBoxBlur
+{
+ typedef mozilla::gfx::Color Color;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
+
+public:
+ gfxAlphaBoxBlur();
+
+ ~gfxAlphaBoxBlur();
+
+ /**
+ * Constructs a box blur and initializes the temporary surface.
+ * @param aRect The coordinates of the surface to create in device units.
+ *
+ * @param aBlurRadius The blur radius in pixels. This is the radius of
+ * the entire (triple) kernel function. Each individual box blur has
+ * radius approximately 1/3 this value, or diameter approximately 2/3
+ * this value. This parameter should nearly always be computed using
+ * CalculateBlurRadius, below.
+ *
+ * @param aDirtyRect A pointer to a dirty rect, measured in device units,
+ * if available. This will be used for optimizing the blur operation. It
+ * is safe to pass nullptr here.
+ *
+ * @param aSkipRect A pointer to a rect, measured in device units, that
+ * represents an area where blurring is unnecessary and shouldn't be done
+ * for speed reasons. It is safe to pass nullptr here.
+ */
+ gfxContext* Init(const gfxRect& aRect,
+ const mozilla::gfx::IntSize& aSpreadRadius,
+ const mozilla::gfx::IntSize& aBlurRadius,
+ const gfxRect* aDirtyRect,
+ const gfxRect* aSkipRect);
+
+ /**
+ * Returns the context that should be drawn to supply the alpha mask to be
+ * blurred. If the returned surface is null, then there was an error in
+ * its creation.
+ */
+ gfxContext* GetContext()
+ {
+ return mContext;
+ }
+
+ already_AddRefed<mozilla::gfx::SourceSurface>
+ DoBlur(DrawTarget* aDT, mozilla::gfx::IntPoint* aTopLeft);
+
+ /**
+ * Does the actual blurring/spreading and mask applying. Users of this
+ * object must have drawn whatever they want to be blurred onto the internal
+ * gfxContext returned by GetContext before calling this.
+ *
+ * @param aDestinationCtx The graphics context on which to apply the
+ * blurred mask.
+ */
+ void Paint(gfxContext* aDestinationCtx);
+
+ /**
+ * Calculates a blur radius that, when used with box blur, approximates
+ * a Gaussian blur with the given standard deviation. The result of
+ * this function should be used as the aBlurRadius parameter to Init,
+ * above.
+ */
+ static mozilla::gfx::IntSize CalculateBlurRadius(const gfxPoint& aStandardDeviation);
+
+ /**
+ * Blurs a coloured rectangle onto aDestinationCtx. This is equivalent
+ * to calling Init(), drawing a rectangle onto the returned surface
+ * and then calling Paint, but may let us optimize better in the
+ * backend.
+ *
+ * @param aDestinationCtx The destination to blur to.
+ * @param aRect The rectangle to blur in device pixels.
+ * @param aCornerRadii Corner radii for aRect, if it is a rounded
+ * rectangle.
+ * @param aBlurRadius The standard deviation of the blur.
+ * @param aShadowColor The color to draw the blurred shadow.
+ * @param aDirtyRect An area in device pixels that is dirty and needs
+ * to be redrawn.
+ * @param aSkipRect An area in device pixels to avoid blurring over,
+ * to prevent unnecessary work.
+ */
+ static void BlurRectangle(gfxContext *aDestinationCtx,
+ const gfxRect& aRect,
+ RectCornerRadii* aCornerRadii,
+ const gfxPoint& aBlurStdDev,
+ const Color& aShadowColor,
+ const gfxRect& aDirtyRect,
+ const gfxRect& aSkipRect);
+
+ static void ShutdownBlurCache();
+
+ /***
+ * Blurs an inset box shadow according to a given path.
+ * This is equivalent to calling Init(), drawing the inset path,
+ * and calling paint. Do not call Init() if using this method.
+ *
+ * @param aDestinationCtx The destination to blur to.
+ * @param aDestinationRect The destination rect in device pixels
+ * @param aShadowClipRect The destiniation inner rect of the
+ * inset path in device pixels.
+ * @param aBlurRadius The standard deviation of the blur.
+ * @param aSpreadRadius The spread radius in device pixels.
+ * @param aShadowColor The color of the blur.
+ * @param aHasBorderRadius If this element also has a border radius
+ * @param aInnerClipRadii Corner radii for the inside rect if it is a rounded rect.
+ * @param aSKipRect An area in device pixels we don't have to paint in.
+ */
+ void BlurInsetBox(gfxContext* aDestinationCtx,
+ const mozilla::gfx::Rect aDestinationRect,
+ const mozilla::gfx::Rect aShadowClipRect,
+ const mozilla::gfx::IntSize aBlurRadius,
+ const mozilla::gfx::IntSize aSpreadRadius,
+ const mozilla::gfx::Color& aShadowColor,
+ const bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii,
+ const mozilla::gfx::Rect aSkipRect,
+ const mozilla::gfx::Point aShadowOffset);
+
+protected:
+ already_AddRefed<mozilla::gfx::SourceSurface>
+ GetInsetBlur(const mozilla::gfx::Rect aOuterRect,
+ const mozilla::gfx::Rect aWhitespaceRect,
+ const bool aIsDestRect,
+ const mozilla::gfx::Color& aShadowColor,
+ const mozilla::gfx::IntSize& aBlurRadius,
+ const bool aHasBorderRadius,
+ const RectCornerRadii& aInnerClipRadii,
+ DrawTarget* aDestDrawTarget);
+
+ /**
+ * The context of the temporary alpha surface.
+ */
+ RefPtr<gfxContext> mContext;
+
+ /**
+ * The temporary alpha surface.
+ */
+ mozilla::UniquePtr<unsigned char[]> mData;
+
+ /**
+ * The object that actually does the blurring for us.
+ */
+ mozilla::UniquePtr<mozilla::gfx::AlphaBoxBlur> mBlur;
+};
+
+#endif /* GFX_BLUR_H */
diff --git a/system/graphics/thebes/gfxColor.h b/system/graphics/thebes/gfxColor.h
new file mode 100644
index 000000000..4ad7d9f06
--- /dev/null
+++ b/system/graphics/thebes/gfxColor.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_COLOR_H
+#define GFX_COLOR_H
+
+#include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
+#include "mozilla/EndianUtils.h" // for mozilla::NativeEndian::swapToBigEndian
+
+/**
+ * GFX_BLOCK_RGB_TO_FRGB(from,to)
+ * sizeof(*from) == sizeof(char)
+ * sizeof(*to) == sizeof(uint32_t)
+ *
+ * Copy 4 pixels at a time, reading blocks of 12 bytes (RGB x4)
+ * and writing blocks of 16 bytes (FRGB x4)
+ */
+#define GFX_BLOCK_RGB_TO_FRGB(from,to) \
+ PR_BEGIN_MACRO \
+ uint32_t m0 = ((uint32_t*)from)[0], \
+ m1 = ((uint32_t*)from)[1], \
+ m2 = ((uint32_t*)from)[2], \
+ rgbr = mozilla::NativeEndian::swapToBigEndian(m0), \
+ gbrg = mozilla::NativeEndian::swapToBigEndian(m1), \
+ brgb = mozilla::NativeEndian::swapToBigEndian(m2), \
+ p0, p1, p2, p3; \
+ p0 = 0xFF000000 | ((rgbr) >> 8); \
+ p1 = 0xFF000000 | ((rgbr) << 16) | ((gbrg) >> 16); \
+ p2 = 0xFF000000 | ((gbrg) << 8) | ((brgb) >> 24); \
+ p3 = 0xFF000000 | (brgb); \
+ to[0] = p0; to[1] = p1; to[2] = p2; to[3] = p3; \
+ PR_END_MACRO
+
+/**
+ * Fast approximate division by 255. It has the property that
+ * for all 0 <= n <= 255*255, GFX_DIVIDE_BY_255(n) == n/255.
+ * But it only uses two adds and two shifts instead of an
+ * integer division (which is expensive on many processors).
+ *
+ * equivalent to ((v)/255)
+ */
+#define GFX_DIVIDE_BY_255(v) \
+ (((((unsigned)(v)) << 8) + ((unsigned)(v)) + 255) >> 16)
+
+/**
+ * Fast premultiply
+ *
+ * equivalent to (((c)*(a))/255)
+ */
+uint8_t MOZ_ALWAYS_INLINE gfxPreMultiply(uint8_t c, uint8_t a) {
+ return GFX_DIVIDE_BY_255((c)*(a));
+}
+
+/**
+ * Pack the 4 8-bit channels (A,R,G,B)
+ * into a 32-bit packed NON-premultiplied pixel.
+ */
+uint32_t MOZ_ALWAYS_INLINE
+gfxPackedPixelNoPreMultiply(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
+ return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
+}
+
+/**
+ * Pack the 4 8-bit channels (A,R,G,B)
+ * into a 32-bit packed premultiplied pixel.
+ */
+uint32_t MOZ_ALWAYS_INLINE
+gfxPackedPixel(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
+ if (a == 0x00)
+ return 0x00000000;
+ else if (a == 0xFF) {
+ return gfxPackedPixelNoPreMultiply(a, r, g, b);
+ } else {
+ return ((a) << 24) |
+ (gfxPreMultiply(r,a) << 16) |
+ (gfxPreMultiply(g,a) << 8) |
+ (gfxPreMultiply(b,a));
+ }
+}
+
+#endif /* _GFX_COLOR_H */
diff --git a/system/graphics/thebes/gfxContext.cpp b/system/graphics/thebes/gfxContext.cpp
new file mode 100644
index 000000000..3ab92f174
--- /dev/null
+++ b/system/graphics/thebes/gfxContext.cpp
@@ -0,0 +1,1256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h>
+
+#include "mozilla/Alignment.h"
+
+#include "cairo.h"
+
+#include "gfxContext.h"
+
+#include "gfxMatrix.h"
+#include "gfxUtils.h"
+#include "gfxASurface.h"
+#include "gfxPattern.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "GeckoProfiler.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/DrawTargetTiled.h"
+#include <algorithm>
+
+#if XP_WIN
+#include "gfxWindowsPlatform.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+UserDataKey gfxContext::sDontUseAsSourceKey;
+
+
+PatternFromState::operator mozilla::gfx::Pattern&()
+{
+ gfxContext::AzureState &state = mContext->CurrentState();
+
+ if (state.pattern) {
+ return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
+ }
+
+ if (state.sourceSurface) {
+ Matrix transform = state.surfTransform;
+
+ if (state.patternTransformChanged) {
+ Matrix mat = mContext->GetDTTransform();
+ if (!mat.Invert()) {
+ mPattern = new (mColorPattern.addr())
+ ColorPattern(Color()); // transparent black to paint nothing
+ return *mPattern;
+ }
+ transform = transform * state.patternTransform * mat;
+ }
+
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
+ return *mPattern;
+ }
+
+ mPattern = new (mColorPattern.addr())
+ ColorPattern(state.color);
+ return *mPattern;
+}
+
+
+gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
+ : mPathIsRect(false)
+ , mTransformChanged(false)
+ , mDT(aTarget)
+{
+ if (!aTarget) {
+ gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
+ }
+
+ MOZ_COUNT_CTOR(gfxContext);
+
+ mStateStack.SetLength(1);
+ CurrentState().drawTarget = mDT;
+ CurrentState().deviceOffset = aDeviceOffset;
+ mDT->SetTransform(GetDTTransform());
+}
+
+/* static */ already_AddRefed<gfxContext>
+gfxContext::CreateOrNull(DrawTarget* aTarget,
+ const mozilla::gfx::Point& aDeviceOffset)
+{
+ if (!aTarget || !aTarget->IsValid()) {
+ gfxCriticalNote << "Invalid target in gfxContext::CreateOrNull " << hexa(aTarget);
+ return nullptr;
+ }
+
+ RefPtr<gfxContext> result = new gfxContext(aTarget, aDeviceOffset);
+ return result.forget();
+}
+
+/* static */ already_AddRefed<gfxContext>
+gfxContext::CreatePreservingTransformOrNull(DrawTarget* aTarget)
+{
+ if (!aTarget || !aTarget->IsValid()) {
+ gfxCriticalNote << "Invalid target in gfxContext::CreatePreservingTransformOrNull " << hexa(aTarget);
+ return nullptr;
+ }
+
+ Matrix transform = aTarget->GetTransform();
+ RefPtr<gfxContext> result = new gfxContext(aTarget);
+ result->SetMatrix(ThebesMatrix(transform));
+ return result.forget();
+}
+
+gfxContext::~gfxContext()
+{
+ for (int i = mStateStack.Length() - 1; i >= 0; i--) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ mStateStack[i].drawTarget->PopClip();
+ }
+ }
+ MOZ_COUNT_DTOR(gfxContext);
+}
+
+void
+gfxContext::Save()
+{
+ CurrentState().transform = mTransform;
+ mStateStack.AppendElement(AzureState(CurrentState()));
+ CurrentState().pushedClips.Clear();
+}
+
+void
+gfxContext::Restore()
+{
+ for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
+ mDT->PopClip();
+ }
+
+ mStateStack.RemoveElementAt(mStateStack.Length() - 1);
+
+ mDT = CurrentState().drawTarget;
+
+ ChangeTransform(CurrentState().transform, false);
+}
+
+// drawing
+void
+gfxContext::NewPath()
+{
+ mPath = nullptr;
+ mPathBuilder = nullptr;
+ mPathIsRect = false;
+ mTransformChanged = false;
+}
+
+void
+gfxContext::ClosePath()
+{
+ EnsurePathBuilder();
+ mPathBuilder->Close();
+}
+
+already_AddRefed<Path> gfxContext::GetPath()
+{
+ EnsurePath();
+ RefPtr<Path> path(mPath);
+ return path.forget();
+}
+
+void gfxContext::SetPath(Path* path)
+{
+ MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
+ path->GetBackendType() == BackendType::RECORDING ||
+ (mDT->GetBackendType() == BackendType::DIRECT2D1_1 && path->GetBackendType() == BackendType::DIRECT2D));
+ mPath = path;
+ mPathBuilder = nullptr;
+ mPathIsRect = false;
+ mTransformChanged = false;
+}
+
+gfxPoint
+gfxContext::CurrentPoint()
+{
+ EnsurePathBuilder();
+ return ThebesPoint(mPathBuilder->CurrentPoint());
+}
+
+void
+gfxContext::Fill()
+{
+ Fill(PatternFromState(this));
+}
+
+void
+gfxContext::Fill(const Pattern& aPattern)
+{
+ PROFILER_LABEL("gfxContext", "Fill",
+ js::ProfileEntry::Category::GRAPHICS);
+ FillAzure(aPattern, 1.0f);
+}
+
+void
+gfxContext::MoveTo(const gfxPoint& pt)
+{
+ EnsurePathBuilder();
+ mPathBuilder->MoveTo(ToPoint(pt));
+}
+
+void
+gfxContext::LineTo(const gfxPoint& pt)
+{
+ EnsurePathBuilder();
+ mPathBuilder->LineTo(ToPoint(pt));
+}
+
+void
+gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
+{
+ EnsurePathBuilder();
+ mPathBuilder->MoveTo(ToPoint(start));
+ mPathBuilder->LineTo(ToPoint(end));
+}
+
+// XXX snapToPixels is only valid when snapping for filled
+// rectangles and for even-width stroked rectangles.
+// For odd-width stroked rectangles, we need to offset x/y by
+// 0.5...
+void
+gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
+{
+ Rect rec = ToRect(rect);
+
+ if (snapToPixels) {
+ gfxRect newRect(rect);
+ if (UserToDevicePixelSnapped(newRect, true)) {
+ gfxMatrix mat = ThebesMatrix(mTransform);
+ if (mat.Invert()) {
+ // We need the user space rect.
+ rec = ToRect(mat.TransformBounds(newRect));
+ } else {
+ rec = Rect();
+ }
+ }
+ }
+
+ if (!mPathBuilder && !mPathIsRect) {
+ mPathIsRect = true;
+ mRect = rec;
+ return;
+ }
+
+ EnsurePathBuilder();
+
+ mPathBuilder->MoveTo(rec.TopLeft());
+ mPathBuilder->LineTo(rec.TopRight());
+ mPathBuilder->LineTo(rec.BottomRight());
+ mPathBuilder->LineTo(rec.BottomLeft());
+ mPathBuilder->Close();
+}
+
+// transform stuff
+void
+gfxContext::Multiply(const gfxMatrix& matrix)
+{
+ ChangeTransform(ToMatrix(matrix) * mTransform);
+}
+
+void
+gfxContext::SetMatrix(const gfxMatrix& matrix)
+{
+ ChangeTransform(ToMatrix(matrix));
+}
+
+gfxMatrix
+gfxContext::CurrentMatrix() const
+{
+ return ThebesMatrix(mTransform);
+}
+
+gfxPoint
+gfxContext::DeviceToUser(const gfxPoint& point) const
+{
+ return ThebesPoint(mTransform.Inverse().TransformPoint(ToPoint(point)));
+}
+
+Size
+gfxContext::DeviceToUser(const Size& size) const
+{
+ return mTransform.Inverse().TransformSize(size);
+}
+
+gfxRect
+gfxContext::DeviceToUser(const gfxRect& rect) const
+{
+ return ThebesRect(mTransform.Inverse().TransformBounds(ToRect(rect)));
+}
+
+gfxPoint
+gfxContext::UserToDevice(const gfxPoint& point) const
+{
+ return ThebesPoint(mTransform.TransformPoint(ToPoint(point)));
+}
+
+Size
+gfxContext::UserToDevice(const Size& size) const
+{
+ const Matrix &matrix = mTransform;
+
+ Size newSize;
+ newSize.width = size.width * matrix._11 + size.height * matrix._12;
+ newSize.height = size.width * matrix._21 + size.height * matrix._22;
+ return newSize;
+}
+
+gfxRect
+gfxContext::UserToDevice(const gfxRect& rect) const
+{
+ const Matrix &matrix = mTransform;
+ return ThebesRect(matrix.TransformBounds(ToRect(rect)));
+}
+
+bool
+gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
+{
+ if (mDT->GetUserData(&sDisablePixelSnapping))
+ return false;
+
+ // if we're not at 1.0 scale, don't snap, unless we're
+ // ignoring the scale. If we're not -just- a scale,
+ // never snap.
+ const gfxFloat epsilon = 0.0000001;
+#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
+ Matrix mat = mTransform;
+ if (!ignoreScale &&
+ (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
+ !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
+ return false;
+#undef WITHIN_E
+
+ gfxPoint p1 = UserToDevice(rect.TopLeft());
+ gfxPoint p2 = UserToDevice(rect.TopRight());
+ gfxPoint p3 = UserToDevice(rect.BottomRight());
+
+ // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
+ // two opposite corners define the entire rectangle. So check if
+ // the axis-aligned rectangle with opposite corners p1 and p3
+ // define an axis-aligned rectangle whose other corners are p2 and p4.
+ // We actually only need to check one of p2 and p4, since an affine
+ // transform maps parallelograms to parallelograms.
+ if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
+ p1.Round();
+ p3.Round();
+
+ rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
+ rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
+ std::max(p1.y, p3.y) - rect.Y()));
+ return true;
+ }
+
+ return false;
+}
+
+bool
+gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
+{
+ if (mDT->GetUserData(&sDisablePixelSnapping))
+ return false;
+
+ // if we're not at 1.0 scale, don't snap, unless we're
+ // ignoring the scale. If we're not -just- a scale,
+ // never snap.
+ const gfxFloat epsilon = 0.0000001;
+#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
+ Matrix mat = mTransform;
+ if (!ignoreScale &&
+ (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
+ !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
+ return false;
+#undef WITHIN_E
+
+ pt = UserToDevice(pt);
+ pt.Round();
+ return true;
+}
+
+void
+gfxContext::SetAntialiasMode(AntialiasMode mode)
+{
+ CurrentState().aaMode = mode;
+}
+
+AntialiasMode
+gfxContext::CurrentAntialiasMode() const
+{
+ return CurrentState().aaMode;
+}
+
+void
+gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
+{
+ AzureState &state = CurrentState();
+
+ state.dashPattern.SetLength(ndash);
+ for (int i = 0; i < ndash; i++) {
+ state.dashPattern[i] = Float(dashes[i]);
+ }
+ state.strokeOptions.mDashLength = ndash;
+ state.strokeOptions.mDashOffset = Float(offset);
+ state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
+ : nullptr;
+}
+
+bool
+gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
+{
+ const AzureState &state = CurrentState();
+ int count = state.strokeOptions.mDashLength;
+
+ if (count <= 0 || !dashes.SetLength(count, fallible)) {
+ return false;
+ }
+
+ for (int i = 0; i < count; i++) {
+ dashes[i] = state.dashPattern[i];
+ }
+
+ *offset = state.strokeOptions.mDashOffset;
+
+ return true;
+}
+
+gfxFloat
+gfxContext::CurrentDashOffset() const
+{
+ return CurrentState().strokeOptions.mDashOffset;
+}
+
+void
+gfxContext::SetLineWidth(gfxFloat width)
+{
+ CurrentState().strokeOptions.mLineWidth = Float(width);
+}
+
+gfxFloat
+gfxContext::CurrentLineWidth() const
+{
+ return CurrentState().strokeOptions.mLineWidth;
+}
+
+void
+gfxContext::SetOp(CompositionOp aOp)
+{
+ CurrentState().op = aOp;
+}
+
+CompositionOp
+gfxContext::CurrentOp() const
+{
+ return CurrentState().op;
+}
+
+void
+gfxContext::SetLineCap(CapStyle cap)
+{
+ CurrentState().strokeOptions.mLineCap = cap;
+}
+
+CapStyle
+gfxContext::CurrentLineCap() const
+{
+ return CurrentState().strokeOptions.mLineCap;
+}
+
+void
+gfxContext::SetLineJoin(JoinStyle join)
+{
+ CurrentState().strokeOptions.mLineJoin = join;
+}
+
+JoinStyle
+gfxContext::CurrentLineJoin() const
+{
+ return CurrentState().strokeOptions.mLineJoin;
+}
+
+void
+gfxContext::SetMiterLimit(gfxFloat limit)
+{
+ CurrentState().strokeOptions.mMiterLimit = Float(limit);
+}
+
+gfxFloat
+gfxContext::CurrentMiterLimit() const
+{
+ return CurrentState().strokeOptions.mMiterLimit;
+}
+
+// clipping
+void
+gfxContext::Clip(const Rect& rect)
+{
+ AzureState::PushedClip clip = { nullptr, rect, mTransform };
+ CurrentState().pushedClips.AppendElement(clip);
+ mDT->PushClipRect(rect);
+ NewPath();
+}
+
+void
+gfxContext::Clip(const gfxRect& rect)
+{
+ Clip(ToRect(rect));
+}
+
+void
+gfxContext::Clip(Path* aPath)
+{
+ mDT->PushClip(aPath);
+ AzureState::PushedClip clip = { aPath, Rect(), mTransform };
+ CurrentState().pushedClips.AppendElement(clip);
+}
+
+void
+gfxContext::Clip()
+{
+ if (mPathIsRect) {
+ MOZ_ASSERT(!mTransformChanged);
+
+ AzureState::PushedClip clip = { nullptr, mRect, mTransform };
+ CurrentState().pushedClips.AppendElement(clip);
+ mDT->PushClipRect(mRect);
+ } else {
+ EnsurePath();
+ mDT->PushClip(mPath);
+ AzureState::PushedClip clip = { mPath, Rect(), mTransform };
+ CurrentState().pushedClips.AppendElement(clip);
+ }
+}
+
+void
+gfxContext::PopClip()
+{
+ MOZ_ASSERT(CurrentState().pushedClips.Length() > 0);
+
+ CurrentState().pushedClips.RemoveElementAt(CurrentState().pushedClips.Length() - 1);
+ mDT->PopClip();
+}
+
+gfxRect
+gfxContext::GetClipExtents()
+{
+ Rect rect = GetAzureDeviceSpaceClipBounds();
+
+ if (rect.width == 0 || rect.height == 0) {
+ return gfxRect(0, 0, 0, 0);
+ }
+
+ Matrix mat = mTransform;
+ mat.Invert();
+ rect = mat.TransformBounds(rect);
+
+ return ThebesRect(rect);
+}
+
+bool
+gfxContext::HasComplexClip() const
+{
+ for (int i = mStateStack.Length() - 1; i >= 0; i--) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
+ if (clip.path || !clip.transform.IsRectilinear()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+gfxContext::ExportClip(ClipExporter& aExporter)
+{
+ for (unsigned int i = 0; i < mStateStack.Length(); i++) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
+ gfx::Matrix transform = clip.transform;
+ transform.PostTranslate(-GetDeviceOffset());
+
+ aExporter.BeginClip(transform);
+ if (clip.path) {
+ clip.path->StreamToSink(&aExporter);
+ } else {
+ aExporter.MoveTo(clip.rect.TopLeft());
+ aExporter.LineTo(clip.rect.TopRight());
+ aExporter.LineTo(clip.rect.BottomRight());
+ aExporter.LineTo(clip.rect.BottomLeft());
+ aExporter.Close();
+ }
+ aExporter.EndClip();
+ }
+ }
+
+ return true;
+}
+
+bool
+gfxContext::ClipContainsRect(const gfxRect& aRect)
+{
+ // Since we always return false when the clip list contains a
+ // non-rectangular clip or a non-rectilinear transform, our 'total' clip
+ // is always a rectangle if we hit the end of this function.
+ Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
+
+ for (unsigned int i = 0; i < mStateStack.Length(); i++) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
+ if (clip.path || !clip.transform.IsRectilinear()) {
+ // Cairo behavior is we return false if the clip contains a non-
+ // rectangle.
+ return false;
+ } else {
+ Rect clipRect = mTransform.TransformBounds(clip.rect);
+
+ clipBounds.IntersectRect(clipBounds, clipRect);
+ }
+ }
+ }
+
+ return clipBounds.Contains(ToRect(aRect));
+}
+
+// rendering sources
+
+void
+gfxContext::SetColor(const Color& aColor)
+{
+ CurrentState().pattern = nullptr;
+ CurrentState().sourceSurfCairo = nullptr;
+ CurrentState().sourceSurface = nullptr;
+ CurrentState().color = ToDeviceColor(aColor);
+}
+
+void
+gfxContext::SetDeviceColor(const Color& aColor)
+{
+ CurrentState().pattern = nullptr;
+ CurrentState().sourceSurfCairo = nullptr;
+ CurrentState().sourceSurface = nullptr;
+ CurrentState().color = aColor;
+}
+
+bool
+gfxContext::GetDeviceColor(Color& aColorOut)
+{
+ if (CurrentState().sourceSurface) {
+ return false;
+ }
+ if (CurrentState().pattern) {
+ return CurrentState().pattern->GetSolidColor(aColorOut);
+ }
+
+ aColorOut = CurrentState().color;
+ return true;
+}
+
+void
+gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
+{
+ CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
+ CurrentState().pattern = nullptr;
+ CurrentState().patternTransformChanged = false;
+ // Keep the underlying cairo surface around while we keep the
+ // sourceSurface.
+ CurrentState().sourceSurfCairo = surface;
+ CurrentState().sourceSurface =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
+ CurrentState().color = Color(0, 0, 0, 0);
+}
+
+void
+gfxContext::SetPattern(gfxPattern *pattern)
+{
+ CurrentState().sourceSurfCairo = nullptr;
+ CurrentState().sourceSurface = nullptr;
+ CurrentState().patternTransformChanged = false;
+ CurrentState().pattern = pattern;
+}
+
+already_AddRefed<gfxPattern>
+gfxContext::GetPattern()
+{
+ RefPtr<gfxPattern> pat;
+
+ AzureState &state = CurrentState();
+ if (state.pattern) {
+ pat = state.pattern;
+ } else if (state.sourceSurface) {
+ NS_ASSERTION(false, "Ugh, this isn't good.");
+ } else {
+ pat = new gfxPattern(state.color);
+ }
+ return pat.forget();
+}
+
+void
+gfxContext::SetFontSmoothingBackgroundColor(const Color& aColor)
+{
+ CurrentState().fontSmoothingBackgroundColor = aColor;
+}
+
+Color
+gfxContext::GetFontSmoothingBackgroundColor()
+{
+ return CurrentState().fontSmoothingBackgroundColor;
+}
+
+// masking
+void
+gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform)
+{
+ Matrix old = mTransform;
+ Matrix mat = aTransform * mTransform;
+
+ ChangeTransform(mat);
+ mDT->MaskSurface(PatternFromState(this), aSurface, Point(),
+ DrawOptions(aAlpha, CurrentState().op, CurrentState().aaMode));
+ ChangeTransform(old);
+}
+
+void
+gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
+{
+ // We clip here to bind to the mask surface bounds, see above.
+ mDT->MaskSurface(PatternFromState(this),
+ surface,
+ offset,
+ DrawOptions(alpha, CurrentState().op, CurrentState().aaMode));
+}
+
+void
+gfxContext::Paint(gfxFloat alpha)
+{
+ PROFILER_LABEL("gfxContext", "Paint",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ AzureState &state = CurrentState();
+
+ if (state.sourceSurface && !state.sourceSurfCairo &&
+ !state.patternTransformChanged)
+ {
+ // This is the case where a PopGroupToSource has been done and this
+ // paint is executed without changing the transform or the source.
+ Matrix oldMat = mDT->GetTransform();
+
+ IntSize surfSize = state.sourceSurface->GetSize();
+
+ mDT->SetTransform(Matrix::Translation(-state.deviceOffset.x,
+ -state.deviceOffset.y));
+
+ mDT->DrawSurface(state.sourceSurface,
+ Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
+ Rect(Point(), Size(surfSize.width, surfSize.height)),
+ DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
+ mDT->SetTransform(oldMat);
+ return;
+ }
+
+ Matrix mat = mDT->GetTransform();
+ mat.Invert();
+ Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
+
+ mDT->FillRect(paintRect, PatternFromState(this),
+ DrawOptions(Float(alpha), GetOp()));
+}
+
+void
+gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
+{
+ if (gfxPrefs::UseNativePushLayer()) {
+ Save();
+ mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
+ } else {
+ DrawTarget* oldDT = mDT;
+
+ PushNewDT(content);
+
+ if (oldDT != mDT) {
+ PushClipsToDT(mDT);
+ }
+ mDT->SetTransform(GetDTTransform());
+
+ CurrentState().mBlendOpacity = aOpacity;
+ CurrentState().mBlendMask = aMask;
+#ifdef DEBUG
+ CurrentState().mWasPushedForBlendBack = true;
+#endif
+ CurrentState().mBlendMaskTransform = aMaskTransform;
+ }
+}
+
+static gfxRect
+GetRoundOutDeviceClipExtents(gfxContext* aCtx)
+{
+ gfxContextMatrixAutoSaveRestore save(aCtx);
+ aCtx->SetMatrix(gfxMatrix());
+ gfxRect r = aCtx->GetClipExtents();
+ r.RoundOut();
+ return r;
+}
+
+void
+gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
+{
+ IntRect clipExtents;
+ if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
+ gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
+ clipExtents = IntRect::Truncate(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ }
+ bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+ mDT->GetOpaqueRect().Contains(clipExtents)) &&
+ !mDT->GetUserData(&sDontUseAsSourceKey);
+
+ if (gfxPrefs::UseNativePushLayer()) {
+ Save();
+
+ if (pushOpaqueWithCopiedBG) {
+ mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
+ } else {
+ mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
+ }
+ } else {
+ RefPtr<SourceSurface> source;
+ // This snapshot can be nullptr if the DrawTarget is a cairo target that is currently
+ // in an error state.
+ if (pushOpaqueWithCopiedBG && (source = mDT->Snapshot())) {
+ DrawTarget *oldDT = mDT;
+ Point oldDeviceOffset = CurrentState().deviceOffset;
+
+ PushNewDT(gfxContentType::COLOR);
+
+ if (oldDT == mDT) {
+ // Creating new DT failed.
+ return;
+ }
+
+ CurrentState().mBlendOpacity = aOpacity;
+ CurrentState().mBlendMask = aMask;
+#ifdef DEBUG
+ CurrentState().mWasPushedForBlendBack = true;
+#endif
+ CurrentState().mBlendMaskTransform = aMaskTransform;
+
+ Point offset = CurrentState().deviceOffset - oldDeviceOffset;
+ Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
+ Rect sourceRect = surfRect + offset;
+
+ mDT->SetTransform(Matrix());
+
+ // XXX: It's really sad that we have to do this (for performance).
+ // Once DrawTarget gets a PushLayer API we can implement this within
+ // DrawTargetTiled.
+ if (source->GetType() == SurfaceType::TILED) {
+ SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
+ for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
+ Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
+ sourceTiled->mOrigins[i].y,
+ sourceTiled->mSnapshots[i]->GetSize().width,
+ sourceTiled->mSnapshots[i]->GetSize().height));
+
+ if (tileSourceRect.IsEmpty()) {
+ continue;
+ }
+ Rect tileDestRect = tileSourceRect - offset;
+ tileSourceRect -= sourceTiled->mOrigins[i];
+
+ mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
+ }
+ } else {
+ mDT->DrawSurface(source, surfRect, sourceRect);
+ }
+ mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
+
+ PushClipsToDT(mDT);
+ mDT->SetTransform(GetDTTransform());
+ return;
+ }
+ DrawTarget* oldDT = mDT;
+
+ PushNewDT(content);
+
+ if (oldDT != mDT) {
+ PushClipsToDT(mDT);
+ }
+
+ mDT->SetTransform(GetDTTransform());
+ CurrentState().mBlendOpacity = aOpacity;
+ CurrentState().mBlendMask = aMask;
+#ifdef DEBUG
+ CurrentState().mWasPushedForBlendBack = true;
+#endif
+ CurrentState().mBlendMaskTransform = aMaskTransform;
+ }
+}
+
+void
+gfxContext::PopGroupAndBlend()
+{
+ if (gfxPrefs::UseNativePushLayer()) {
+ mDT->PopLayer();
+ Restore();
+ } else {
+ MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
+ Float opacity = CurrentState().mBlendOpacity;
+ RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
+ Matrix maskTransform = CurrentState().mBlendMaskTransform;
+
+ RefPtr<SourceSurface> src = mDT->Snapshot();
+ Point deviceOffset = CurrentState().deviceOffset;
+ Restore();
+ CurrentState().sourceSurfCairo = nullptr;
+ CurrentState().sourceSurface = src;
+ CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
+ CurrentState().pattern = nullptr;
+ CurrentState().patternTransformChanged = false;
+
+ Matrix mat = mTransform;
+ mat.Invert();
+ mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
+
+ CurrentState().surfTransform = mat;
+
+ CompositionOp oldOp = GetOp();
+ SetOp(CompositionOp::OP_OVER);
+
+ if (mask) {
+ if (!maskTransform.HasNonTranslation()) {
+ Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
+ } else {
+ Mask(mask, opacity, maskTransform);
+ }
+ } else {
+ Paint(opacity);
+ }
+
+ SetOp(oldOp);
+ }
+}
+
+#ifdef MOZ_DUMP_PAINTING
+void
+gfxContext::WriteAsPNG(const char* aFile)
+{
+ gfxUtils::WriteAsPNG(mDT, aFile);
+}
+
+void
+gfxContext::DumpAsDataURI()
+{
+ gfxUtils::DumpAsDataURI(mDT);
+}
+
+void
+gfxContext::CopyAsDataURI()
+{
+ gfxUtils::CopyAsDataURI(mDT);
+}
+#endif
+
+void
+gfxContext::EnsurePath()
+{
+ if (mPathBuilder) {
+ mPath = mPathBuilder->Finish();
+ mPathBuilder = nullptr;
+ }
+
+ if (mPath) {
+ if (mTransformChanged) {
+ Matrix mat = mTransform;
+ mat.Invert();
+ mat = mPathTransform * mat;
+ mPathBuilder = mPath->TransformedCopyToBuilder(mat);
+ mPath = mPathBuilder->Finish();
+ mPathBuilder = nullptr;
+
+ mTransformChanged = false;
+ }
+ return;
+ }
+
+ EnsurePathBuilder();
+ mPath = mPathBuilder->Finish();
+ mPathBuilder = nullptr;
+}
+
+void
+gfxContext::EnsurePathBuilder()
+{
+ if (mPathBuilder && !mTransformChanged) {
+ return;
+ }
+
+ if (mPath) {
+ if (!mTransformChanged) {
+ mPathBuilder = mPath->CopyToBuilder();
+ mPath = nullptr;
+ } else {
+ Matrix invTransform = mTransform;
+ invTransform.Invert();
+ Matrix toNewUS = mPathTransform * invTransform;
+ mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS);
+ }
+ return;
+ }
+
+ DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
+
+ if (!mPathBuilder) {
+ mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
+
+ if (mPathIsRect) {
+ mPathBuilder->MoveTo(mRect.TopLeft());
+ mPathBuilder->LineTo(mRect.TopRight());
+ mPathBuilder->LineTo(mRect.BottomRight());
+ mPathBuilder->LineTo(mRect.BottomLeft());
+ mPathBuilder->Close();
+ }
+ }
+
+ if (mTransformChanged) {
+ // This could be an else if since this should never happen when
+ // mPathBuilder is nullptr and mPath is nullptr. But this way we can
+ // assert if all the state is as expected.
+ MOZ_ASSERT(oldPath);
+ MOZ_ASSERT(!mPathIsRect);
+
+ Matrix invTransform = mTransform;
+ invTransform.Invert();
+ Matrix toNewUS = mPathTransform * invTransform;
+
+ RefPtr<Path> path = mPathBuilder->Finish();
+ if (!path) {
+ gfxCriticalError() << "gfxContext::EnsurePathBuilder failed in PathBuilder::Finish";
+ }
+ mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
+ }
+
+ mPathIsRect = false;
+}
+
+void
+gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity)
+{
+ AzureState &state = CurrentState();
+
+ CompositionOp op = GetOp();
+
+ if (mPathIsRect) {
+ MOZ_ASSERT(!mTransformChanged);
+
+ if (op == CompositionOp::OP_SOURCE) {
+ // Emulate cairo operator source which is bound by mask!
+ mDT->ClearRect(mRect);
+ mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity));
+ } else {
+ mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity, op, state.aaMode));
+ }
+ } else {
+ EnsurePath();
+ mDT->Fill(mPath, aPattern, DrawOptions(aOpacity, op, state.aaMode));
+ }
+}
+
+void
+gfxContext::PushClipsToDT(DrawTarget *aDT)
+{
+ // Don't need to save the old transform, we'll be setting a new one soon!
+
+ // Push all clips from the bottom of the stack to the clip before ours.
+ for (unsigned int i = 0; i < mStateStack.Length() - 1; i++) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
+ if (mStateStack[i].pushedClips[c].path) {
+ aDT->PushClip(mStateStack[i].pushedClips[c].path);
+ } else {
+ aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
+ }
+ }
+ }
+}
+
+CompositionOp
+gfxContext::GetOp()
+{
+ if (CurrentState().op != CompositionOp::OP_SOURCE) {
+ return CurrentState().op;
+ }
+
+ AzureState &state = CurrentState();
+ if (state.pattern) {
+ if (state.pattern->IsOpaque()) {
+ return CompositionOp::OP_OVER;
+ } else {
+ return CompositionOp::OP_SOURCE;
+ }
+ } else if (state.sourceSurface) {
+ if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
+ return CompositionOp::OP_OVER;
+ } else {
+ return CompositionOp::OP_SOURCE;
+ }
+ } else {
+ if (state.color.a > 0.999) {
+ return CompositionOp::OP_OVER;
+ } else {
+ return CompositionOp::OP_SOURCE;
+ }
+ }
+}
+
+/* SVG font code can change the transform after having set the pattern on the
+ * context. When the pattern is set it is in user space, if the transform is
+ * changed after doing so the pattern needs to be converted back into userspace.
+ * We just store the old pattern transform here so that we only do the work
+ * needed here if the pattern is actually used.
+ * We need to avoid doing this when this ChangeTransform comes from a restore,
+ * since the current pattern and the current transform are both part of the
+ * state we know the new CurrentState()'s values are valid. But if we assume
+ * a change they might become invalid since patternTransformChanged is part of
+ * the state and might be false for the restored AzureState.
+ */
+void
+gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
+{
+ AzureState &state = CurrentState();
+
+ if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
+ && !state.patternTransformChanged) {
+ state.patternTransform = GetDTTransform();
+ state.patternTransformChanged = true;
+ }
+
+ if (mPathIsRect) {
+ Matrix invMatrix = aNewMatrix;
+
+ invMatrix.Invert();
+
+ Matrix toNewUS = mTransform * invMatrix;
+
+ if (toNewUS.IsRectilinear()) {
+ mRect = toNewUS.TransformBounds(mRect);
+ mRect.NudgeToIntegers();
+ } else {
+ mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
+
+ mPathBuilder->MoveTo(toNewUS.TransformPoint(mRect.TopLeft()));
+ mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.TopRight()));
+ mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomRight()));
+ mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomLeft()));
+ mPathBuilder->Close();
+
+ mPathIsRect = false;
+ }
+
+ // No need to consider the transform changed now!
+ mTransformChanged = false;
+ } else if ((mPath || mPathBuilder) && !mTransformChanged) {
+ mTransformChanged = true;
+ mPathTransform = mTransform;
+ }
+
+ mTransform = aNewMatrix;
+
+ mDT->SetTransform(GetDTTransform());
+}
+
+Rect
+gfxContext::GetAzureDeviceSpaceClipBounds()
+{
+ Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
+ Float(mDT->GetSize().width), Float(mDT->GetSize().height));
+ for (unsigned int i = 0; i < mStateStack.Length(); i++) {
+ for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+ AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
+ if (clip.path) {
+ Rect bounds = clip.path->GetBounds(clip.transform);
+ rect.IntersectRect(rect, bounds);
+ } else {
+ rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
+ }
+ }
+ }
+
+ return rect;
+}
+
+Point
+gfxContext::GetDeviceOffset() const
+{
+ return CurrentState().deviceOffset;
+}
+
+Matrix
+gfxContext::GetDeviceTransform() const
+{
+ return Matrix::Translation(-CurrentState().deviceOffset.x,
+ -CurrentState().deviceOffset.y);
+}
+
+Matrix
+gfxContext::GetDTTransform() const
+{
+ Matrix mat = mTransform;
+ mat._31 -= CurrentState().deviceOffset.x;
+ mat._32 -= CurrentState().deviceOffset.y;
+ return mat;
+}
+
+void
+gfxContext::PushNewDT(gfxContentType content)
+{
+ Rect clipBounds = GetAzureDeviceSpaceClipBounds();
+ clipBounds.RoundOut();
+
+ clipBounds.width = std::max(1.0f, clipBounds.width);
+ clipBounds.height = std::max(1.0f, clipBounds.height);
+
+ SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content);
+
+ RefPtr<DrawTarget> newDT =
+ mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
+ format);
+
+ if (!newDT) {
+ NS_WARNING("Failed to create DrawTarget of sufficient size.");
+ newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
+
+ if (!newDT) {
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()
+#ifdef XP_WIN
+ && !(mDT->GetBackendType() == BackendType::DIRECT2D1_1 &&
+ !DeviceManagerDx::Get()->GetContentDevice())
+#endif
+ ) {
+ // If even this fails.. we're most likely just out of memory!
+ NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
+ }
+ newDT = CurrentState().drawTarget;
+ }
+ }
+
+ Save();
+
+ CurrentState().drawTarget = newDT;
+ CurrentState().deviceOffset = clipBounds.TopLeft();
+
+ mDT = newDT;
+}
+
diff --git a/system/graphics/thebes/gfxContext.h b/system/graphics/thebes/gfxContext.h
new file mode 100644
index 000000000..49f6a7dca
--- /dev/null
+++ b/system/graphics/thebes/gfxContext.h
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CONTEXT_H
+#define GFX_CONTEXT_H
+
+#include "gfxTypes.h"
+
+#include "gfxASurface.h"
+#include "gfxPoint.h"
+#include "gfxRect.h"
+#include "gfxMatrix.h"
+#include "gfxPattern.h"
+#include "nsTArray.h"
+
+#include "mozilla/gfx/2D.h"
+
+typedef struct _cairo cairo_t;
+class GlyphBufferAzure;
+
+namespace mozilla {
+namespace gfx {
+struct RectCornerRadii;
+} // namespace gfx
+} // namespace mozilla
+
+class ClipExporter;
+
+/**
+ * This is the main class for doing actual drawing. It is initialized using
+ * a surface and can be drawn on. It manages various state information like
+ * a current transformation matrix (CTM), a current path, current color,
+ * etc.
+ *
+ * All drawing happens by creating a path and then stroking or filling it.
+ * The functions like Rectangle and Arc do not do any drawing themselves.
+ * When a path is drawn (stroked or filled), it is filled/stroked with a
+ * pattern set by SetPattern, SetColor or SetSource.
+ *
+ * Note that the gfxContext takes coordinates in device pixels,
+ * as opposed to app units.
+ */
+class gfxContext final {
+ typedef mozilla::gfx::CapStyle CapStyle;
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+ typedef mozilla::gfx::JoinStyle JoinStyle;
+ typedef mozilla::gfx::FillRule FillRule;
+ typedef mozilla::gfx::Path Path;
+ typedef mozilla::gfx::Pattern Pattern;
+ typedef mozilla::gfx::Rect Rect;
+ typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
+ typedef mozilla::gfx::Size Size;
+
+ NS_INLINE_DECL_REFCOUNTING(gfxContext)
+
+public:
+ /**
+ * Initialize this context from a DrawTarget.
+ * Strips any transform from aTarget.
+ * aTarget will be flushed in the gfxContext's destructor.
+ * If aTarget is null or invalid, nullptr is returned. The caller
+ * is responsible for handling this scenario as appropriate.
+ */
+ static already_AddRefed<gfxContext>
+ CreateOrNull(mozilla::gfx::DrawTarget* aTarget,
+ const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
+
+ /**
+ * Create a new gfxContext wrapping aTarget and preserving aTarget's
+ * transform. Note that the transform is moved from aTarget to the resulting
+ * gfxContext, aTarget will no longer have its transform.
+ * If aTarget is null or invalid, nullptr is returned. The caller
+ * is responsible for handling this scenario as appropriate.
+ */
+ static already_AddRefed<gfxContext>
+ CreatePreservingTransformOrNull(mozilla::gfx::DrawTarget* aTarget);
+
+ mozilla::gfx::DrawTarget *GetDrawTarget() { return mDT; }
+
+ /**
+ ** State
+ **/
+ // XXX document exactly what bits are saved
+ void Save();
+ void Restore();
+
+ /**
+ ** Paths & Drawing
+ **/
+
+ /**
+ * Fill the current path according to the current settings.
+ *
+ * Does not consume the current path.
+ */
+ void Fill();
+ void Fill(const Pattern& aPattern);
+
+ /**
+ * Forgets the current path.
+ */
+ void NewPath();
+
+ /**
+ * Closes the path, i.e. connects the last drawn point to the first one.
+ *
+ * Filling a path will implicitly close it.
+ */
+ void ClosePath();
+
+ /**
+ * Returns the current path.
+ */
+ already_AddRefed<Path> GetPath();
+
+ /**
+ * Sets the given path as the current path.
+ */
+ void SetPath(Path* path);
+
+ /**
+ * Moves the pen to a new point without drawing a line.
+ */
+ void MoveTo(const gfxPoint& pt);
+
+ /**
+ * Returns the current point in the current path.
+ */
+ gfxPoint CurrentPoint();
+
+ /**
+ * Draws a line from the current point to pt.
+ *
+ * @see MoveTo
+ */
+ void LineTo(const gfxPoint& pt);
+
+ // path helpers
+ /**
+ * Draws a line from start to end.
+ */
+ void Line(const gfxPoint& start, const gfxPoint& end); // XXX snapToPixels option?
+
+ /**
+ * Draws the rectangle given by rect.
+ * @param snapToPixels ?
+ */
+ void Rectangle(const gfxRect& rect, bool snapToPixels = false);
+ void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); }
+
+ /**
+ ** Transformation Matrix manipulation
+ **/
+
+ /**
+ * Post-multiplies 'other' onto the current CTM, i.e. this
+ * matrix's transformation will take place before the previously set
+ * transformations.
+ */
+ void Multiply(const gfxMatrix& other);
+
+ /**
+ * Replaces the current transformation matrix with matrix.
+ */
+ void SetMatrix(const gfxMatrix& matrix);
+
+ /**
+ * Returns the current transformation matrix.
+ */
+ gfxMatrix CurrentMatrix() const;
+
+ /**
+ * Converts a point from device to user coordinates using the inverse
+ * transformation matrix.
+ */
+ gfxPoint DeviceToUser(const gfxPoint& point) const;
+
+ /**
+ * Converts a size from device to user coordinates. This does not apply
+ * translation components of the matrix.
+ */
+ Size DeviceToUser(const Size& size) const;
+
+ /**
+ * Converts a rectangle from device to user coordinates; this has the
+ * same effect as using DeviceToUser on both the rectangle's point and
+ * size.
+ */
+ gfxRect DeviceToUser(const gfxRect& rect) const;
+
+ /**
+ * Converts a point from user to device coordinates using the transformation
+ * matrix.
+ */
+ gfxPoint UserToDevice(const gfxPoint& point) const;
+
+ /**
+ * Converts a size from user to device coordinates. This does not apply
+ * translation components of the matrix.
+ */
+ Size UserToDevice(const Size& size) const;
+
+ /**
+ * Converts a rectangle from user to device coordinates. The
+ * resulting rectangle is the minimum device-space rectangle that
+ * encloses the user-space rectangle given.
+ */
+ gfxRect UserToDevice(const gfxRect& rect) const;
+
+ /**
+ * Takes the given rect and tries to align it to device pixels. If
+ * this succeeds, the method will return true, and the rect will
+ * be in device coordinates (already transformed by the CTM). If it
+ * fails, the method will return false, and the rect will not be
+ * changed.
+ *
+ * If ignoreScale is true, then snapping will take place even if
+ * the CTM has a scale applied. Snapping never takes place if
+ * there is a rotation in the CTM.
+ */
+ bool UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale = false) const;
+
+ /**
+ * Takes the given point and tries to align it to device pixels. If
+ * this succeeds, the method will return true, and the point will
+ * be in device coordinates (already transformed by the CTM). If it
+ * fails, the method will return false, and the point will not be
+ * changed.
+ *
+ * If ignoreScale is true, then snapping will take place even if
+ * the CTM has a scale applied. Snapping never takes place if
+ * there is a rotation in the CTM.
+ */
+ bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const;
+
+ /**
+ ** Painting sources
+ **/
+
+ /**
+ * Set a solid color to use for drawing. This color is in the device color space
+ * and is not transformed.
+ */
+ void SetDeviceColor(const mozilla::gfx::Color& aColor);
+
+ /**
+ * Gets the current color. It's returned in the device color space.
+ * returns false if there is something other than a color
+ * set as the current source (pattern, surface, etc)
+ */
+ bool GetDeviceColor(mozilla::gfx::Color& aColorOut);
+
+ /**
+ * Set a solid color in the sRGB color space to use for drawing.
+ * If CMS is not enabled, the color is treated as a device-space color
+ * and this call is identical to SetDeviceColor().
+ */
+ void SetColor(const mozilla::gfx::Color& aColor);
+
+ /**
+ * Uses a surface for drawing. This is a shorthand for creating a
+ * pattern and setting it.
+ *
+ * @param offset from the source surface, to use only part of it.
+ * May need to make it negative.
+ */
+ void SetSource(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0));
+
+ /**
+ * Uses a pattern for drawing.
+ */
+ void SetPattern(gfxPattern *pattern);
+
+ /**
+ * Set the color that text drawn on top of transparent pixels should be
+ * anti-aliased into.
+ */
+ void SetFontSmoothingBackgroundColor(const mozilla::gfx::Color& aColor);
+ mozilla::gfx::Color GetFontSmoothingBackgroundColor();
+
+ /**
+ * Get the source pattern (solid color, normal pattern, surface, etc)
+ */
+ already_AddRefed<gfxPattern> GetPattern();
+
+ /**
+ ** Painting
+ **/
+ /**
+ * Paints the current source surface/pattern everywhere in the current
+ * clip region.
+ */
+ void Paint(gfxFloat alpha = 1.0);
+
+ /**
+ ** Painting with a Mask
+ **/
+ /**
+ * Like Paint, except that it only draws the source where pattern is
+ * non-transparent.
+ */
+ void Mask(mozilla::gfx::SourceSurface *aSurface, mozilla::gfx::Float aAlpha, const mozilla::gfx::Matrix& aTransform);
+ void Mask(mozilla::gfx::SourceSurface *aSurface, const mozilla::gfx::Matrix& aTransform) { Mask(aSurface, 1.0f, aTransform); }
+ void Mask(mozilla::gfx::SourceSurface *surface, float alpha = 1.0f, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
+
+ /**
+ ** Line Properties
+ **/
+
+ void SetDash(gfxFloat *dashes, int ndash, gfxFloat offset);
+ // Return true if dashing is set, false if it's not enabled or the
+ // context is in an error state. |offset| can be nullptr to mean
+ // "don't care".
+ bool CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const;
+ // Returns 0.0 if dashing isn't enabled.
+ gfxFloat CurrentDashOffset() const;
+
+ /**
+ * Sets the line width that's used for line drawing.
+ */
+ void SetLineWidth(gfxFloat width);
+
+ /**
+ * Returns the currently set line width.
+ *
+ * @see SetLineWidth
+ */
+ gfxFloat CurrentLineWidth() const;
+
+ /**
+ * Sets the line caps, i.e. how line endings are drawn.
+ */
+ void SetLineCap(CapStyle cap);
+ CapStyle CurrentLineCap() const;
+
+ /**
+ * Sets the line join, i.e. how the connection between two lines is
+ * drawn.
+ */
+ void SetLineJoin(JoinStyle join);
+ JoinStyle CurrentLineJoin() const;
+
+ void SetMiterLimit(gfxFloat limit);
+ gfxFloat CurrentMiterLimit() const;
+
+ /**
+ * Sets the operator used for all further drawing. The operator affects
+ * how drawing something will modify the destination. For example, the
+ * OVER operator will do alpha blending of source and destination, while
+ * SOURCE will replace the destination with the source.
+ */
+ void SetOp(CompositionOp op);
+ CompositionOp CurrentOp() const;
+
+ void SetAntialiasMode(mozilla::gfx::AntialiasMode mode);
+ mozilla::gfx::AntialiasMode CurrentAntialiasMode() const;
+
+ /**
+ ** Clipping
+ **/
+
+ /**
+ * Clips all further drawing to the current path.
+ * This does not consume the current path.
+ */
+ void Clip();
+
+ /**
+ * Helper functions that will create a rect path and call Clip().
+ * Any current path will be destroyed by these functions!
+ */
+ void Clip(const Rect& rect);
+ void Clip(const gfxRect& rect); // will clip to a rect
+ void Clip(Path* aPath);
+
+ void PopClip();
+
+ /**
+ * This will return the current bounds of the clip region in user
+ * space.
+ */
+ gfxRect GetClipExtents();
+
+ /**
+ * Whether the current clip is not a simple rectangle.
+ */
+ bool HasComplexClip() const;
+
+ /**
+ * Returns true if the given rectangle is fully contained in the current clip.
+ * This is conservative; it may return false even when the given rectangle is
+ * fully contained by the current clip.
+ */
+ bool ClipContainsRect(const gfxRect& aRect);
+
+ /**
+ * Exports the current clip using the provided exporter.
+ */
+ bool ExportClip(ClipExporter& aExporter);
+
+ /**
+ * Groups
+ */
+ void PushGroupForBlendBack(gfxContentType content, mozilla::gfx::Float aOpacity = 1.0f,
+ mozilla::gfx::SourceSurface* aMask = nullptr,
+ const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
+
+ /**
+ * Like PushGroupForBlendBack, but if the current surface is gfxContentType::COLOR and
+ * content is gfxContentType::COLOR_ALPHA, makes the pushed surface gfxContentType::COLOR
+ * instead and copies the contents of the current surface to the pushed
+ * surface. This is good for pushing opacity groups, since blending the
+ * group back to the current surface with some alpha applied will give
+ * the correct results and using an opaque pushed surface gives better
+ * quality and performance.
+ */
+ void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR,
+ mozilla::gfx::Float aOpacity = 1.0f,
+ mozilla::gfx::SourceSurface* aMask = nullptr,
+ const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
+ void PopGroupAndBlend();
+
+ mozilla::gfx::Point GetDeviceOffset() const;
+
+#ifdef MOZ_DUMP_PAINTING
+ /**
+ * Debug functions to encode the current surface as a PNG and export it.
+ */
+
+ /**
+ * Writes a binary PNG file.
+ */
+ void WriteAsPNG(const char* aFile);
+
+ /**
+ * Write as a PNG encoded Data URL to stdout.
+ */
+ void DumpAsDataURI();
+
+ /**
+ * Copy a PNG encoded Data URL to the clipboard.
+ */
+ void CopyAsDataURI();
+#endif
+
+ static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
+
+private:
+
+ /**
+ * Initialize this context from a DrawTarget.
+ * Strips any transform from aTarget.
+ * aTarget will be flushed in the gfxContext's destructor. Use the static
+ * ContextForDrawTargetNoTransform() when you want this behavior, as that
+ * version deals with null DrawTarget better.
+ */
+ explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
+ const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
+ ~gfxContext();
+
+ friend class PatternFromState;
+ friend class GlyphBufferAzure;
+
+ typedef mozilla::gfx::Matrix Matrix;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Color Color;
+ typedef mozilla::gfx::StrokeOptions StrokeOptions;
+ typedef mozilla::gfx::Float Float;
+ typedef mozilla::gfx::PathBuilder PathBuilder;
+ typedef mozilla::gfx::SourceSurface SourceSurface;
+
+ struct AzureState {
+ AzureState()
+ : op(mozilla::gfx::CompositionOp::OP_OVER)
+ , color(0, 0, 0, 1.0f)
+ , aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL)
+ , patternTransformChanged(false)
+ , mBlendOpacity(0.0f)
+ {}
+
+ mozilla::gfx::CompositionOp op;
+ Color color;
+ RefPtr<gfxPattern> pattern;
+ RefPtr<gfxASurface> sourceSurfCairo;
+ RefPtr<SourceSurface> sourceSurface;
+ mozilla::gfx::Point sourceSurfaceDeviceOffset;
+ Matrix surfTransform;
+ Matrix transform;
+ struct PushedClip {
+ RefPtr<Path> path;
+ Rect rect;
+ Matrix transform;
+ };
+ nsTArray<PushedClip> pushedClips;
+ nsTArray<Float> dashPattern;
+ StrokeOptions strokeOptions;
+ RefPtr<DrawTarget> drawTarget;
+ mozilla::gfx::AntialiasMode aaMode;
+ bool patternTransformChanged;
+ Matrix patternTransform;
+ Color fontSmoothingBackgroundColor;
+ // This is used solely for using minimal intermediate surface size.
+ mozilla::gfx::Point deviceOffset;
+ // Support groups
+ mozilla::gfx::Float mBlendOpacity;
+ RefPtr<SourceSurface> mBlendMask;
+ Matrix mBlendMaskTransform;
+#ifdef DEBUG
+ bool mWasPushedForBlendBack;
+#endif
+ };
+
+ // This ensures mPath contains a valid path (in user space!)
+ void EnsurePath();
+ // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
+ void EnsurePathBuilder();
+ void FillAzure(const Pattern& aPattern, mozilla::gfx::Float aOpacity);
+ void PushClipsToDT(mozilla::gfx::DrawTarget *aDT);
+ CompositionOp GetOp();
+ void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix, bool aUpdatePatternTransform = true);
+ Rect GetAzureDeviceSpaceClipBounds();
+ Matrix GetDeviceTransform() const;
+ Matrix GetDTTransform() const;
+ void PushNewDT(gfxContentType content);
+
+ bool mPathIsRect;
+ bool mTransformChanged;
+ Matrix mPathTransform;
+ Rect mRect;
+ RefPtr<PathBuilder> mPathBuilder;
+ RefPtr<Path> mPath;
+ Matrix mTransform;
+ nsTArray<AzureState> mStateStack;
+
+ AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
+ const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
+
+ RefPtr<DrawTarget> mDT;
+};
+
+/**
+ * Sentry helper class for functions with multiple return points that need to
+ * call Save() on a gfxContext and have Restore() called automatically on the
+ * gfxContext before they return.
+ */
+class gfxContextAutoSaveRestore
+{
+public:
+ gfxContextAutoSaveRestore() : mContext(nullptr) {}
+
+ explicit gfxContextAutoSaveRestore(gfxContext *aContext) : mContext(aContext) {
+ mContext->Save();
+ }
+
+ ~gfxContextAutoSaveRestore() {
+ Restore();
+ }
+
+ void SetContext(gfxContext *aContext) {
+ NS_ASSERTION(!mContext, "Not going to call Restore() on some context!!!");
+ mContext = aContext;
+ mContext->Save();
+ }
+
+ void EnsureSaved(gfxContext *aContext) {
+ MOZ_ASSERT(!mContext || mContext == aContext, "wrong context");
+ if (!mContext) {
+ mContext = aContext;
+ mContext->Save();
+ }
+ }
+
+ void Restore() {
+ if (mContext) {
+ mContext->Restore();
+ mContext = nullptr;
+ }
+ }
+
+private:
+ gfxContext *mContext;
+};
+
+/**
+ * Sentry helper class for functions with multiple return points that need to
+ * back up the current matrix of a context and have it automatically restored
+ * before they return.
+ */
+class gfxContextMatrixAutoSaveRestore
+{
+public:
+ gfxContextMatrixAutoSaveRestore() :
+ mContext(nullptr)
+ {
+ }
+
+ explicit gfxContextMatrixAutoSaveRestore(gfxContext *aContext) :
+ mContext(aContext), mMatrix(aContext->CurrentMatrix())
+ {
+ }
+
+ ~gfxContextMatrixAutoSaveRestore()
+ {
+ if (mContext) {
+ mContext->SetMatrix(mMatrix);
+ }
+ }
+
+ void SetContext(gfxContext *aContext)
+ {
+ NS_ASSERTION(!mContext,
+ "Not going to restore the matrix on some context!");
+ mContext = aContext;
+ mMatrix = aContext->CurrentMatrix();
+ }
+
+ void Restore()
+ {
+ if (mContext) {
+ mContext->SetMatrix(mMatrix);
+ mContext = nullptr;
+ }
+ }
+
+ const gfxMatrix& Matrix()
+ {
+ MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
+ return mMatrix;
+ }
+
+ bool HasMatrix() const { return !!mContext; }
+
+private:
+ gfxContext *mContext;
+ gfxMatrix mMatrix;
+};
+
+
+class DrawTargetAutoDisableSubpixelAntialiasing {
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget *aDT, bool aDisable)
+ {
+ if (aDisable) {
+ mDT = aDT;
+ mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
+ mDT->SetPermitSubpixelAA(false);
+ }
+ }
+ ~DrawTargetAutoDisableSubpixelAntialiasing()
+ {
+ if (mDT) {
+ mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
+ }
+ }
+
+private:
+ RefPtr<DrawTarget> mDT;
+ bool mSubpixelAntialiasingEnabled;
+};
+
+/* This class lives on the stack and allows gfxContext users to easily, and
+ * performantly get a gfx::Pattern to use for drawing in their current context.
+ */
+class PatternFromState
+{
+public:
+ explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
+ ~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
+
+ operator mozilla::gfx::Pattern&();
+
+private:
+ union {
+ mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
+ mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
+ };
+
+ gfxContext *mContext;
+ mozilla::gfx::Pattern *mPattern;
+};
+
+/* This interface should be implemented to handle exporting the clip from a context.
+ */
+class ClipExporter : public mozilla::gfx::PathSink {
+public:
+ virtual void BeginClip(const mozilla::gfx::Matrix& aMatrix) = 0;
+ virtual void EndClip() = 0;
+};
+
+#endif /* GFX_CONTEXT_H */
diff --git a/system/graphics/thebes/gfxDWriteCommon.cpp b/system/graphics/thebes/gfxDWriteCommon.cpp
new file mode 100644
index 000000000..3047818bb
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteCommon.cpp
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxDWriteCommon.h"
+
+#include <unordered_map>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/gfx/Logging.h"
+
+static mozilla::Atomic<uint64_t> sNextFontFileKey;
+static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
+
+IDWriteFontFileLoader* gfxDWriteFontFileLoader::mInstance = nullptr;
+
+class gfxDWriteFontFileStream final : public IDWriteFontFileStream
+{
+public:
+ /**
+ * Used by the FontFileLoader to create a new font stream,
+ * this font stream is created from data in memory. The memory
+ * passed may be released after object creation, it will be
+ * copied internally.
+ *
+ * @param aData Font data
+ */
+ gfxDWriteFontFileStream(FallibleTArray<uint8_t> *aData,
+ uint64_t aFontFileKey);
+ ~gfxDWriteFontFileStream();
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileStream)) {
+ *ppObject = static_cast<IDWriteFontFileStream*>(this);
+ return S_OK;
+ }
+ else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ }
+ else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
+ ++mRefCnt;
+ return mRefCnt;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ NS_PRECONDITION(0 != mRefCnt, "dup release");
+ --mRefCnt;
+ if (mRefCnt == 0) {
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ // IDWriteFontFileStream methods
+ virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ OUT void** fragmentContext);
+
+ virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+
+ virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
+
+ virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
+
+private:
+ FallibleTArray<uint8_t> mData;
+ nsAutoRefCnt mRefCnt;
+ uint64_t mFontFileKey;
+};
+
+gfxDWriteFontFileStream::gfxDWriteFontFileStream(FallibleTArray<uint8_t> *aData,
+ uint64_t aFontFileKey)
+ : mFontFileKey(aFontFileKey)
+{
+ mData.SwapElements(*aData);
+}
+
+gfxDWriteFontFileStream::~gfxDWriteFontFileStream()
+{
+ sFontFileStreams.erase(mFontFileKey);
+}
+
+HRESULT STDMETHODCALLTYPE
+gfxDWriteFontFileStream::GetFileSize(UINT64 *fileSize)
+{
+ *fileSize = mData.Length();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+gfxDWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+gfxDWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ void **fragmentContext)
+{
+ // We are required to do bounds checking.
+ if (fileOffset + fragmentSize > (UINT64)mData.Length()) {
+ return E_FAIL;
+ }
+ // We should be alive for the duration of this.
+ *fragmentStart = &mData[fileOffset];
+ *fragmentContext = nullptr;
+ return S_OK;
+}
+
+void STDMETHODCALLTYPE
+gfxDWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
+{
+}
+
+HRESULT STDMETHODCALLTYPE
+gfxDWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ IDWriteFontFileStream **fontFileStream)
+{
+ if (!fontFileReferenceKey || !fontFileStream) {
+ return E_POINTER;
+ }
+
+ uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
+ auto found = sFontFileStreams.find(fontFileKey);
+ if (found == sFontFileStreams.end()) {
+ *fontFileStream = nullptr;
+ return E_FAIL;
+ }
+
+ found->second->AddRef();
+ *fontFileStream = found->second;
+ return S_OK;
+}
+
+/* static */
+HRESULT
+gfxDWriteFontFileLoader::CreateCustomFontFile(FallibleTArray<uint8_t>& aFontData,
+ IDWriteFontFile** aFontFile,
+ IDWriteFontFileStream** aFontFileStream)
+{
+ MOZ_ASSERT(aFontFile);
+ MOZ_ASSERT(aFontFileStream);
+
+ IDWriteFactory *factory = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
+ if (!factory) {
+ gfxCriticalError() << "Failed to get DWrite Factory in CreateCustomFontFile.";
+ return E_FAIL;
+ }
+
+ uint64_t fontFileKey = sNextFontFileKey++;
+ RefPtr<IDWriteFontFileStream> ffsRef = new gfxDWriteFontFileStream(&aFontData, fontFileKey);
+ sFontFileStreams[fontFileKey] = ffsRef;
+
+ RefPtr<IDWriteFontFile> fontFile;
+ HRESULT hr = factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey), Instance(), getter_AddRefs(fontFile));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to load font file from data!");
+ return hr;
+ }
+
+ fontFile.forget(aFontFile);
+ ffsRef.forget(aFontFileStream);
+
+ return S_OK;
+}
diff --git a/system/graphics/thebes/gfxDWriteCommon.h b/system/graphics/thebes/gfxDWriteCommon.h
new file mode 100644
index 000000000..a355b59c6
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteCommon.h
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DWRITECOMMON_H
+#define GFX_DWRITECOMMON_H
+
+// Mozilla includes
+#include "nscore.h"
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+#include "cairo-features.h"
+#include "gfxFontConstants.h"
+#include "nsTArray.h"
+#include "gfxWindowsPlatform.h"
+#include "nsIUUIDGenerator.h"
+
+#include <windows.h>
+#include <dwrite.h>
+
+static inline DWRITE_FONT_STRETCH
+DWriteFontStretchFromStretch(int16_t aStretch)
+{
+ switch (aStretch) {
+ case NS_FONT_STRETCH_ULTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
+ case NS_FONT_STRETCH_EXTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
+ case NS_FONT_STRETCH_CONDENSED:
+ return DWRITE_FONT_STRETCH_CONDENSED;
+ case NS_FONT_STRETCH_SEMI_CONDENSED:
+ return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
+ case NS_FONT_STRETCH_NORMAL:
+ return DWRITE_FONT_STRETCH_NORMAL;
+ case NS_FONT_STRETCH_SEMI_EXPANDED:
+ return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
+ case NS_FONT_STRETCH_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXPANDED;
+ case NS_FONT_STRETCH_EXTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
+ case NS_FONT_STRETCH_ULTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
+ default:
+ return DWRITE_FONT_STRETCH_UNDEFINED;
+ }
+}
+
+static inline int16_t
+FontStretchFromDWriteStretch(DWRITE_FONT_STRETCH aStretch)
+{
+ switch (aStretch) {
+ case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
+ return NS_FONT_STRETCH_ULTRA_CONDENSED;
+ case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
+ return NS_FONT_STRETCH_EXTRA_CONDENSED;
+ case DWRITE_FONT_STRETCH_CONDENSED:
+ return NS_FONT_STRETCH_CONDENSED;
+ case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
+ return NS_FONT_STRETCH_SEMI_CONDENSED;
+ case DWRITE_FONT_STRETCH_NORMAL:
+ return NS_FONT_STRETCH_NORMAL;
+ case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
+ return NS_FONT_STRETCH_SEMI_EXPANDED;
+ case DWRITE_FONT_STRETCH_EXPANDED:
+ return NS_FONT_STRETCH_EXPANDED;
+ case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
+ return NS_FONT_STRETCH_EXTRA_EXPANDED;
+ case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
+ return NS_FONT_STRETCH_ULTRA_EXPANDED;
+ default:
+ return NS_FONT_STRETCH_NORMAL;
+ }
+}
+
+struct ffReferenceKey
+{
+ FallibleTArray<uint8_t> *mArray;
+ nsID mGUID;
+};
+
+class gfxDWriteFontFileLoader : public IDWriteFontFileLoader
+{
+public:
+ gfxDWriteFontFileLoader()
+ {
+ }
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileLoader)) {
+ *ppObject = static_cast<IDWriteFontFileLoader*>(this);
+ return S_OK;
+ } else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ return 1;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ return 1;
+ }
+
+ // IDWriteFontFileLoader methods
+ /**
+ * Important! Note the key here -has- to be a pointer to a uint64_t.
+ */
+ virtual HRESULT STDMETHODCALLTYPE
+ CreateStreamFromKey(void const* fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ OUT IDWriteFontFileStream** fontFileStream);
+
+ /**
+ * Gets the singleton loader instance. Note that when using this font
+ * loader, the key must be a pointer to a unint64_t.
+ */
+ static IDWriteFontFileLoader* Instance()
+ {
+ if (!mInstance) {
+ mInstance = new gfxDWriteFontFileLoader();
+ gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+ RegisterFontFileLoader(mInstance);
+ }
+ return mInstance;
+ }
+
+ /**
+ * Creates a IDWriteFontFile and IDWriteFontFileStream from aFontData.
+ * aFontData will be empty on return as it swaps out the data.
+ *
+ * @param aFontData the font data for the custom font file
+ * @param aFontFile out param for the created font file
+ * @param aFontFileStream out param for the corresponding stream
+ * @return HRESULT of internal calls
+ */
+ static HRESULT CreateCustomFontFile(FallibleTArray<uint8_t>& aFontData,
+ IDWriteFontFile** aFontFile,
+ IDWriteFontFileStream** aFontFileStream);
+
+private:
+ static IDWriteFontFileLoader* mInstance;
+};
+
+#endif /* GFX_DWRITECOMMON_H */
diff --git a/system/graphics/thebes/gfxDWriteFontList.cpp b/system/graphics/thebes/gfxDWriteFontList.cpp
new file mode 100644
index 000000000..c08ca8265
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteFontList.cpp
@@ -0,0 +1,1813 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "gfxDWriteFontList.h"
+#include "gfxDWriteFonts.h"
+#include "nsUnicharUtils.h"
+#include "nsILocaleService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISimpleEnumerator.h"
+#include "GeckoProfiler.h"
+
+#include "gfxGDIFontList.h"
+
+#include "nsIWindowsRegKey.h"
+
+#include "harfbuzz/hb.h"
+
+using namespace mozilla;
+
+#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug)
+
+#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug)
+
+#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_cmapdata), \
+ LogLevel::Debug)
+
+static __inline void
+BuildKeyNameFromFontName(nsAString &aName)
+{
+ if (aName.Length() >= LF_FACESIZE)
+ aName.Truncate(LF_FACESIZE - 1);
+ ToLowerCase(aName);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// gfxDWriteFontFamily
+
+gfxDWriteFontFamily::~gfxDWriteFontFamily()
+{
+}
+
+static HRESULT
+GetDirectWriteFontName(IDWriteFont *aFont, nsAString& aFontName)
+{
+ HRESULT hr;
+
+ RefPtr<IDWriteLocalizedStrings> names;
+ hr = aFont->GetFaceNames(getter_AddRefs(names));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ BOOL exists;
+ AutoTArray<wchar_t,32> faceName;
+ UINT32 englishIdx = 0;
+ hr = names->FindLocaleName(L"en-us", &englishIdx, &exists);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (!exists) {
+ // No english found, use whatever is first in the list.
+ englishIdx = 0;
+ }
+ UINT32 length;
+ hr = names->GetStringLength(englishIdx, &length);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ faceName.SetLength(length + 1);
+ hr = names->GetString(englishIdx, faceName.Elements(), length + 1);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ aFontName.Assign(faceName.Elements());
+ return S_OK;
+}
+
+#define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
+#define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
+
+// for use in reading postscript or fullname
+static HRESULT
+GetDirectWriteFaceName(IDWriteFont *aFont,
+ DWRITE_INFORMATIONAL_STRING_ID aWhichName,
+ nsAString& aFontName)
+{
+ HRESULT hr;
+
+ BOOL exists;
+ RefPtr<IDWriteLocalizedStrings> infostrings;
+ hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings), &exists);
+ if (FAILED(hr) || !exists) {
+ return E_FAIL;
+ }
+
+ AutoTArray<wchar_t,32> faceName;
+ UINT32 englishIdx = 0;
+ hr = infostrings->FindLocaleName(L"en-us", &englishIdx, &exists);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (!exists) {
+ // No english found, use whatever is first in the list.
+ englishIdx = 0;
+ }
+ UINT32 length;
+ hr = infostrings->GetStringLength(englishIdx, &length);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ faceName.SetLength(length + 1);
+ hr = infostrings->GetString(englishIdx, faceName.Elements(), length + 1);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ aFontName.Assign(faceName.Elements());
+ return S_OK;
+}
+
+void
+gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
+{
+ HRESULT hr;
+ if (mHasStyles) {
+ return;
+ }
+ mHasStyles = true;
+
+ gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
+
+ bool skipFaceNames = mFaceNamesInitialized ||
+ !fp->NeedFullnamePostscriptNames();
+ bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
+ fp->NeedFullnamePostscriptNames() &&
+ aFontInfoData;
+
+ for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
+ RefPtr<IDWriteFont> font;
+ hr = mDWFamily->GetFont(i, getter_AddRefs(font));
+ if (FAILED(hr)) {
+ // This should never happen.
+ NS_WARNING("Failed to get existing font from family.");
+ continue;
+ }
+
+ if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
+ // We don't want these in the font list; we'll apply simulations
+ // on the fly when appropriate.
+ continue;
+ }
+
+ // name
+ nsString fullID(mName);
+ nsAutoString faceName;
+ hr = GetDirectWriteFontName(font, faceName);
+ if (FAILED(hr)) {
+ continue;
+ }
+ fullID.Append(' ');
+ fullID.Append(faceName);
+
+ gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font);
+ fe->SetForceGDIClassic(mForceGDIClassic);
+ AddFontEntry(fe);
+
+ // postscript/fullname if needed
+ nsAutoString psname, fullname;
+ if (fontInfoShouldHaveFaceNames) {
+ aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
+ if (!fullname.IsEmpty()) {
+ fp->AddFullname(fe, fullname);
+ }
+ if (!psname.IsEmpty()) {
+ fp->AddPostscriptName(fe, psname);
+ }
+ } else if (!skipFaceNames) {
+ hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
+ if (FAILED(hr)) {
+ skipFaceNames = true;
+ } else if (psname.Length() > 0) {
+ fp->AddPostscriptName(fe, psname);
+ }
+
+ hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname);
+ if (FAILED(hr)) {
+ skipFaceNames = true;
+ } else if (fullname.Length() > 0) {
+ fp->AddFullname(fe, fullname);
+ }
+ }
+
+ if (LOG_FONTLIST_ENABLED()) {
+ LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d psname: %s fullname: %s",
+ NS_ConvertUTF16toUTF8(fe->Name()).get(),
+ NS_ConvertUTF16toUTF8(Name()).get(),
+ (fe->IsItalic()) ?
+ "italic" : (fe->IsOblique() ? "oblique" : "normal"),
+ fe->Weight(), fe->Stretch(),
+ NS_ConvertUTF16toUTF8(psname).get(),
+ NS_ConvertUTF16toUTF8(fullname).get()));
+ }
+ }
+
+ // assume that if no error, all postscript/fullnames were initialized
+ if (!skipFaceNames) {
+ mFaceNamesInitialized = true;
+ }
+
+ if (!mAvailableFonts.Length()) {
+ NS_WARNING("Family with no font faces in it.");
+ }
+
+ if (mIsBadUnderlineFamily) {
+ SetBadUnderlineFonts();
+ }
+}
+
+void
+gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
+ bool aNeedFullnamePostscriptNames,
+ FontInfoData *aFontInfoData)
+{
+ // if all needed names have already been read, skip
+ if (mOtherFamilyNamesInitialized &&
+ (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
+ return;
+ }
+
+ // If we've been passed a FontInfoData, we skip the DWrite implementation
+ // here and fall back to the generic code which will use that info.
+ if (!aFontInfoData) {
+ // DirectWrite version of this will try to read
+ // postscript/fullnames via DirectWrite API
+ FindStyleVariations();
+ }
+
+ // fallback to looking up via name table
+ if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
+ gfxFontFamily::ReadFaceNames(aPlatformFontList,
+ aNeedFullnamePostscriptNames,
+ aFontInfoData);
+ }
+}
+
+void
+gfxDWriteFontFamily::LocalizedName(nsAString &aLocalizedName)
+{
+ aLocalizedName.AssignLiteral("Unknown Font");
+ HRESULT hr;
+ nsresult rv;
+ nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
+ &rv);
+ nsCOMPtr<nsILocale> locale;
+ rv = ls->GetApplicationLocale(getter_AddRefs(locale));
+ nsString localeName;
+ if (NS_SUCCEEDED(rv)) {
+ rv = locale->GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE),
+ localeName);
+ }
+ if (NS_FAILED(rv)) {
+ localeName.AssignLiteral("en-us");
+ }
+
+ RefPtr<IDWriteLocalizedStrings> names;
+
+ hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
+ if (FAILED(hr)) {
+ return;
+ }
+ UINT32 idx = 0;
+ BOOL exists;
+ hr = names->FindLocaleName(localeName.get(),
+ &idx,
+ &exists);
+ if (FAILED(hr)) {
+ return;
+ }
+ if (!exists) {
+ // Use english is localized is not found.
+ hr = names->FindLocaleName(L"en-us", &idx, &exists);
+ if (FAILED(hr)) {
+ return;
+ }
+ if (!exists) {
+ // Use 0 index if english is not found.
+ idx = 0;
+ }
+ }
+ AutoTArray<WCHAR, 32> famName;
+ UINT32 length;
+
+ hr = names->GetStringLength(idx, &length);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ if (!famName.SetLength(length + 1, fallible)) {
+ // Eeep - running out of memory. Unlikely to end well.
+ return;
+ }
+
+ hr = names->GetString(idx, famName.Elements(), length + 1);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ aLocalizedName = nsDependentString(famName.Elements());
+}
+
+void
+gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ // TODO:
+ // This doesn't currently account for |mDWFamily|
+}
+
+void
+gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// gfxDWriteFontEntry
+
+gfxDWriteFontEntry::~gfxDWriteFontEntry()
+{
+}
+
+bool
+gfxDWriteFontEntry::IsSymbolFont()
+{
+ if (mFont) {
+ return mFont->IsSymbolFont();
+ } else {
+ return false;
+ }
+}
+
+static bool
+UsingArabicOrHebrewScriptSystemLocale()
+{
+ LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID());
+ switch (langid) {
+ case LANG_ARABIC:
+ case LANG_DARI:
+ case LANG_PASHTO:
+ case LANG_PERSIAN:
+ case LANG_SINDHI:
+ case LANG_UIGHUR:
+ case LANG_URDU:
+ case LANG_HEBREW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+nsresult
+gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t> &aBuffer)
+{
+ gfxDWriteFontList *pFontList = gfxDWriteFontList::PlatformFontList();
+ const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag);
+
+ // Don't use GDI table loading for symbol fonts or for
+ // italic fonts in Arabic-script system locales because of
+ // potential cmap discrepancies, see bug 629386.
+ // Ditto for Hebrew, bug 837498.
+ if (mFont && pFontList->UseGDIFontTableAccess() &&
+ !(mStyle && UsingArabicOrHebrewScriptSystemLocale()) &&
+ !mFont->IsSymbolFont())
+ {
+ LOGFONTW logfont = { 0 };
+ if (InitLogFont(mFont, &logfont)) {
+ AutoDC dc;
+ AutoSelectFont font(dc.GetDC(), &logfont);
+ if (font.IsValid()) {
+ uint32_t tableSize =
+ ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0);
+ if (tableSize != GDI_ERROR) {
+ if (aBuffer.SetLength(tableSize, fallible)) {
+ ::GetFontData(dc.GetDC(), tagBE, 0,
+ aBuffer.Elements(), aBuffer.Length());
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+
+ RefPtr<IDWriteFontFace> fontFace;
+ nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint8_t *tableData;
+ uint32_t len;
+ void *tableContext = nullptr;
+ BOOL exists;
+ HRESULT hr =
+ fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len,
+ &tableContext, &exists);
+ if (FAILED(hr) || !exists) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aBuffer.SetLength(len, fallible)) {
+ memcpy(aBuffer.Elements(), tableData, len);
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (tableContext) {
+ fontFace->ReleaseFontTable(&tableContext);
+ }
+
+ return rv;
+}
+
+// Access to font tables packaged in hb_blob_t form
+
+// object attached to the Harfbuzz blob, used to release
+// the table when the blob is destroyed
+class FontTableRec {
+public:
+ FontTableRec(IDWriteFontFace *aFontFace, void *aContext)
+ : mFontFace(aFontFace), mContext(aContext)
+ {
+ MOZ_COUNT_CTOR(FontTableRec);
+ }
+
+ ~FontTableRec() {
+ MOZ_COUNT_DTOR(FontTableRec);
+ mFontFace->ReleaseFontTable(mContext);
+ }
+
+private:
+ RefPtr<IDWriteFontFace> mFontFace;
+ void *mContext;
+};
+
+static void
+DestroyBlobFunc(void* aUserData)
+{
+ FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
+ delete ftr;
+}
+
+hb_blob_t *
+gfxDWriteFontEntry::GetFontTable(uint32_t aTag)
+{
+ // try to avoid potentially expensive DWrite call if we haven't actually
+ // created the font face yet, by using the gfxFontEntry method that will
+ // use CopyFontTable and then cache the data
+ if (!mFontFace) {
+ return gfxFontEntry::GetFontTable(aTag);
+ }
+
+ const void *data;
+ UINT32 size;
+ void *context;
+ BOOL exists;
+ HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag),
+ &data, &size, &context, &exists);
+ if (SUCCEEDED(hr) && exists) {
+ FontTableRec *ftr = new FontTableRec(mFontFace, context);
+ return hb_blob_create(static_cast<const char*>(data), size,
+ HB_MEMORY_MODE_READONLY,
+ ftr, DestroyBlobFunc);
+ }
+
+ return nullptr;
+}
+
+nsresult
+gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxCharacterMap> charmap;
+ nsresult rv;
+ bool symbolFont;
+
+ if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+ mUVSOffset,
+ symbolFont))) {
+ rv = NS_OK;
+ } else {
+ uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+ charmap = new gfxCharacterMap();
+ AutoTable cmapTable(this, kCMAP);
+
+ if (cmapTable) {
+ bool unicodeFont = false, symbolFont = false; // currently ignored
+ uint32_t cmapLen;
+ const uint8_t* cmapData =
+ reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
+ &cmapLen));
+ rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
+ *charmap, mUVSOffset,
+ unicodeFont, symbolFont);
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+
+ mHasCmapTable = NS_SUCCEEDED(rv);
+ if (mHasCmapTable) {
+ // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used
+ // by sites to represent a "Play" icon, but the glyph in Segoe UI Light
+ // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.)
+ // Fallback to Segoe UI Symbol is preferred.
+ if (FamilyName().EqualsLiteral("Segoe UI")) {
+ charmap->clear(0x25b6);
+ charmap->clear(0x25c0);
+ }
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ mCharacterMap = pfl->FindCharMap(charmap);
+ } else {
+ // if error occurred, initialize to null cmap
+ mCharacterMap = new gfxCharacterMap();
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
+ NS_ConvertUTF16toUTF8(mName).get(),
+ charmap->SizeOfIncludingThis(moz_malloc_size_of),
+ charmap->mHash, mCharacterMap == charmap ? " new" : ""));
+ if (LOG_CMAPDATA_ENABLED()) {
+ char prefix[256];
+ SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+ NS_ConvertUTF16toUTF8(mName).get());
+ charmap->Dump(prefix, eGfxLog_cmapdata);
+ }
+
+ return rv;
+}
+
+gfxFont *
+gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
+ bool aNeedsBold)
+{
+ return new gfxDWriteFont(this, aFontStyle, aNeedsBold);
+}
+
+nsresult
+gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
+ DWRITE_FONT_SIMULATIONS aSimulations)
+{
+ // initialize mFontFace if this hasn't been done before
+ if (!mFontFace) {
+ HRESULT hr;
+ if (mFont) {
+ hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
+ } else if (mFontFile) {
+ IDWriteFontFile *fontFile = mFontFile.get();
+ hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+ CreateFontFace(mFaceType,
+ 1,
+ &fontFile,
+ 0,
+ DWRITE_FONT_SIMULATIONS_NONE,
+ getter_AddRefs(mFontFace));
+ } else {
+ NS_NOTREACHED("invalid font entry");
+ return NS_ERROR_FAILURE;
+ }
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // check whether we need to add a DWrite simulated style
+ if ((aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
+ !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) {
+ // if so, we need to return not mFontFace itself but a version that
+ // has the Bold simulation - unfortunately, DWrite doesn't provide
+ // a simple API for this
+ UINT32 numberOfFiles = 0;
+ if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoTArray<IDWriteFontFile*,1> files;
+ files.AppendElements(numberOfFiles);
+ if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
+ return NS_ERROR_FAILURE;
+ }
+ HRESULT hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+ CreateFontFace(mFontFace->GetType(),
+ numberOfFiles,
+ files.Elements(),
+ mFontFace->GetIndex(),
+ aSimulations,
+ aFontFace);
+ for (UINT32 i = 0; i < numberOfFiles; ++i) {
+ files[i]->Release();
+ }
+ return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
+ }
+
+ // no simulation: we can just add a reference to mFontFace and return that
+ *aFontFace = mFontFace;
+ (*aFontFace)->AddRef();
+ return NS_OK;
+}
+
+bool
+gfxDWriteFontEntry::InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont)
+{
+ HRESULT hr;
+
+ BOOL isInSystemCollection;
+ IDWriteGdiInterop *gdi =
+ gfxDWriteFontList::PlatformFontList()->GetGDIInterop();
+ hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
+ // If the font is not in the system collection, GDI will be unable to
+ // select it and load its tables, so we return false here to indicate
+ // failure, and let CopyFontTable fall back to DWrite native methods.
+ return (SUCCEEDED(hr) && isInSystemCollection);
+}
+
+bool
+gfxDWriteFontEntry::IsCJKFont()
+{
+ if (mIsCJK != UNINITIALIZED_VALUE) {
+ return mIsCJK;
+ }
+
+ mIsCJK = false;
+
+ const uint32_t kOS2Tag = TRUETYPE_TAG('O','S','/','2');
+ AutoTArray<uint8_t, 128> buffer;
+ if (CopyFontTable(kOS2Tag, buffer) != NS_OK) {
+ return mIsCJK;
+ }
+
+ // ulCodePageRange bit definitions for the CJK codepages,
+ // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
+ const uint32_t CJK_CODEPAGE_BITS =
+ (1 << 17) | // codepage 932 - JIS/Japan
+ (1 << 18) | // codepage 936 - Chinese (simplified)
+ (1 << 19) | // codepage 949 - Korean Wansung
+ (1 << 20) | // codepage 950 - Chinese (traditional)
+ (1 << 21); // codepage 1361 - Korean Johab
+
+ if (buffer.Length() >= offsetof(OS2Table, sxHeight)) {
+ const OS2Table* os2 =
+ reinterpret_cast<const OS2Table*>(buffer.Elements());
+ if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
+ mIsCJK = true;
+ }
+ }
+
+ return mIsCJK;
+}
+
+void
+gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ // TODO:
+ // This doesn't currently account for the |mFont| and |mFontFile| members
+}
+
+void
+gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// gfxDWriteFontList
+
+gfxDWriteFontList::gfxDWriteFontList()
+ : mForceGDIClassicMaxFontSize(0.0)
+{
+}
+
+// bug 602792 - CJK systems default to large CJK fonts which cause excessive
+// I/O strain during cold startup due to dwrite caching bugs. Default to
+// Arial to avoid this.
+
+gfxFontFamily *
+gfxDWriteFontList::GetDefaultFontForPlatform(const gfxFontStyle *aStyle)
+{
+ nsAutoString resolvedName;
+
+ // try Arial first
+ gfxFontFamily *ff;
+ if ((ff = FindFamily(NS_LITERAL_STRING("Arial")))) {
+ return ff;
+ }
+
+ // otherwise, use local default
+ NONCLIENTMETRICSW ncm;
+ ncm.cbSize = sizeof(ncm);
+ BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
+ sizeof(ncm), &ncm, 0);
+
+ if (status) {
+ ff = FindFamily(nsDependentString(ncm.lfMessageFont.lfFaceName));
+ if (ff) {
+ return ff;
+ }
+ }
+
+ return nullptr;
+}
+
+gfxFontEntry *
+gfxDWriteFontList::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ gfxFontEntry *lookup;
+
+ lookup = LookupInFaceNameLists(aFontName);
+ if (!lookup) {
+ return nullptr;
+ }
+
+ gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
+ gfxDWriteFontEntry *fe =
+ new gfxDWriteFontEntry(lookup->Name(),
+ dwriteLookup->mFont,
+ aWeight,
+ aStretch,
+ aStyle);
+ fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
+ return fe;
+}
+
+gfxFontEntry *
+gfxDWriteFontList::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ nsresult rv;
+ nsAutoString uniqueName;
+ rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
+ if (NS_FAILED(rv)) {
+ free((void*)aFontData);
+ return nullptr;
+ }
+
+ FallibleTArray<uint8_t> newFontData;
+
+ rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
+ free((void*)aFontData);
+
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ RefPtr<IDWriteFontFileStream> fontFileStream;
+ RefPtr<IDWriteFontFile> fontFile;
+ HRESULT hr =
+ gfxDWriteFontFileLoader::CreateCustomFontFile(newFontData,
+ getter_AddRefs(fontFile),
+ getter_AddRefs(fontFileStream));
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to create custom font file reference.");
+ return nullptr;
+ }
+
+ BOOL isSupported;
+ DWRITE_FONT_FILE_TYPE fileType;
+ UINT32 numFaces;
+
+ gfxDWriteFontEntry *entry =
+ new gfxDWriteFontEntry(uniqueName,
+ fontFile,
+ fontFileStream,
+ aWeight,
+ aStretch,
+ aStyle);
+
+ fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
+ if (!isSupported || numFaces > 1) {
+ // We don't know how to deal with 0 faces either.
+ delete entry;
+ return nullptr;
+ }
+
+ return entry;
+}
+
+enum DWriteInitError {
+ errGDIInterop = 1,
+ errSystemFontCollection = 2,
+ errNoFonts = 3
+};
+
+nsresult
+gfxDWriteFontList::InitFontListForPlatform()
+{
+ LARGE_INTEGER frequency; // ticks per second
+ LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
+ double elapsedTime, upTime;
+ char nowTime[256], nowDate[256];
+
+ if (LOG_FONTINIT_ENABLED()) {
+ GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT,
+ nullptr, nullptr, nowTime, 256);
+ GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
+ upTime = (double) GetTickCount();
+ }
+ QueryPerformanceFrequency(&frequency);
+ QueryPerformanceCounter(&t1); // start
+
+ HRESULT hr;
+ mGDIFontTableAccess =
+ Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading",
+ false);
+
+ mFontSubstitutes.Clear();
+ mNonExistingFonts.Clear();
+
+ hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+ GetGdiInterop(getter_AddRefs(mGDIInterop));
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ QueryPerformanceCounter(&t2); // base-class/interop initialization
+
+ RefPtr<IDWriteFactory> factory =
+ gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
+
+ hr = factory->GetSystemFontCollection(getter_AddRefs(mSystemFonts));
+ NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
+
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ QueryPerformanceCounter(&t3); // system font collection
+
+ GetFontsFromCollection(mSystemFonts);
+
+ // if no fonts found, something is out of whack, bail and use GDI backend
+ NS_ASSERTION(mFontFamilies.Count() != 0,
+ "no fonts found in the system fontlist -- holy crap batman!");
+ if (mFontFamilies.Count() == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ QueryPerformanceCounter(&t4); // iterate over system fonts
+
+ mBundledFonts = CreateBundledFontsCollection(factory);
+ if (mBundledFonts) {
+ GetFontsFromCollection(mBundledFonts);
+ }
+
+ mOtherFamilyNamesInitialized = true;
+ GetFontSubstitutes();
+
+ // bug 642093 - DirectWrite does not support old bitmap (.fon)
+ // font files, but a few of these such as "Courier" and "MS Sans Serif"
+ // are frequently specified in shoddy CSS, without appropriate fallbacks.
+ // By mapping these to TrueType equivalents, we provide better consistency
+ // with both pre-DW systems and with IE9, which appears to do the same.
+ GetDirectWriteSubstitutes();
+
+ // bug 551313 - DirectWrite creates a Gill Sans family out of
+ // poorly named members of the Gill Sans MT family containing
+ // only Ultra Bold weights. This causes big problems for pages
+ // using Gill Sans which is usually only available on OSX
+
+ nsAutoString nameGillSans(L"Gill Sans");
+ nsAutoString nameGillSansMT(L"Gill Sans MT");
+ BuildKeyNameFromFontName(nameGillSans);
+ BuildKeyNameFromFontName(nameGillSansMT);
+
+ gfxFontFamily *gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
+ gfxFontFamily *gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
+
+ if (gillSansFamily && gillSansMTFamily) {
+ gillSansFamily->FindStyleVariations();
+ nsTArray<RefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList();
+ uint32_t i;
+
+ bool allUltraBold = true;
+ for (i = 0; i < faces.Length(); i++) {
+ // does the face have 'Ultra Bold' in the name?
+ if (faces[i]->Name().Find(NS_LITERAL_STRING("Ultra Bold")) == -1) {
+ allUltraBold = false;
+ break;
+ }
+ }
+
+ // if all the Gill Sans faces are Ultra Bold ==> move faces
+ // for Gill Sans into Gill Sans MT family
+ if (allUltraBold) {
+
+ // add faces to Gill Sans MT
+ for (i = 0; i < faces.Length(); i++) {
+ // change the entry's family name to match its adoptive family
+ faces[i]->mFamilyName = gillSansMTFamily->Name();
+ gillSansMTFamily->AddFontEntry(faces[i]);
+
+ if (LOG_FONTLIST_ENABLED()) {
+ gfxFontEntry *fe = faces[i];
+ LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d",
+ NS_ConvertUTF16toUTF8(fe->Name()).get(),
+ NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(),
+ (fe->IsItalic()) ?
+ "italic" : (fe->IsOblique() ? "oblique" : "normal"),
+ fe->Weight(), fe->Stretch()));
+ }
+ }
+
+ // remove Gills Sans
+ mFontFamilies.Remove(nameGillSans);
+ }
+ }
+
+ nsAdoptingCString classicFamilies =
+ Preferences::GetCString("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families");
+ if (classicFamilies) {
+ nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
+ while (tokenizer.hasMoreTokens()) {
+ NS_ConvertUTF8toUTF16 name(tokenizer.nextToken());
+ BuildKeyNameFromFontName(name);
+ gfxFontFamily *family = mFontFamilies.GetWeak(name);
+ if (family) {
+ static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
+ }
+ }
+ }
+ mForceGDIClassicMaxFontSize =
+ Preferences::GetInt("gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
+ mForceGDIClassicMaxFontSize);
+
+ GetPrefsAndStartLoader();
+
+ QueryPerformanceCounter(&t5); // misc initialization
+
+ if (LOG_FONTINIT_ENABLED()) {
+ // determine dwrite version
+ nsAutoString dwriteVers;
+ gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
+ LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime));
+ LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime/1000));
+ LOG_FONTINIT(("(fontinit) dwrite version: %s\n",
+ NS_ConvertUTF16toUTF8(dwriteVers).get()));
+ }
+
+ elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
+ LOG_FONTINIT((
+ "(fontinit) Total time in InitFontList: %9.3f ms (families: %d, %s)\n",
+ elapsedTime, mSystemFonts->GetFontFamilyCount(),
+ (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
+
+ elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
+ LOG_FONTINIT(("(fontinit) --- base/interop obj initialization init: %9.3f ms\n", elapsedTime));
+
+ elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
+ LOG_FONTINIT(("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime));
+
+ elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
+ LOG_FONTINIT(("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime));
+
+ elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
+ LOG_FONTINIT(("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime));
+
+ return NS_OK;
+}
+
+void
+gfxDWriteFontList::GetFontsFromCollection(IDWriteFontCollection* aCollection)
+{
+ for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) {
+ RefPtr<IDWriteFontFamily> family;
+ aCollection->GetFontFamily(i, getter_AddRefs(family));
+
+ RefPtr<IDWriteLocalizedStrings> names;
+ HRESULT hr = family->GetFamilyNames(getter_AddRefs(names));
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ UINT32 englishIdx = 0;
+
+ BOOL exists;
+ hr = names->FindLocaleName(L"en-us", &englishIdx, &exists);
+ if (FAILED(hr)) {
+ continue;
+ }
+ if (!exists) {
+ // Use 0 index if english is not found.
+ englishIdx = 0;
+ }
+
+ AutoTArray<WCHAR, 32> enName;
+ UINT32 length;
+
+ hr = names->GetStringLength(englishIdx, &length);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ if (!enName.SetLength(length + 1, fallible)) {
+ // Eeep - running out of memory. Unlikely to end well.
+ continue;
+ }
+
+ hr = names->GetString(englishIdx, enName.Elements(), length + 1);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ nsAutoString name(enName.Elements());
+ BuildKeyNameFromFontName(name);
+
+ RefPtr<gfxFontFamily> fam;
+
+ if (mFontFamilies.GetWeak(name)) {
+ continue;
+ }
+
+ nsDependentString familyName(enName.Elements());
+
+ fam = new gfxDWriteFontFamily(familyName, family);
+ if (!fam) {
+ continue;
+ }
+
+ if (mBadUnderlineFamilyNames.Contains(name)) {
+ fam->SetBadUnderlineFamily();
+ }
+ mFontFamilies.Put(name, fam);
+
+ // now add other family name localizations, if present
+ uint32_t nameCount = names->GetCount();
+ uint32_t nameIndex;
+
+ for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
+ UINT32 nameLen;
+ AutoTArray<WCHAR, 32> localizedName;
+
+ // only add other names
+ if (nameIndex == englishIdx) {
+ continue;
+ }
+
+ hr = names->GetStringLength(nameIndex, &nameLen);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ if (!localizedName.SetLength(nameLen + 1, fallible)) {
+ continue;
+ }
+
+ hr = names->GetString(nameIndex, localizedName.Elements(),
+ nameLen + 1);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ nsDependentString locName(localizedName.Elements());
+
+ if (!familyName.Equals(locName)) {
+ AddOtherFamilyName(fam, locName);
+ }
+
+ }
+
+ // at this point, all family names have been read in
+ fam->SetOtherFamilyNamesInitialized();
+ }
+}
+
+static void
+RemoveCharsetFromFontSubstitute(nsAString &aName)
+{
+ int32_t comma = aName.FindChar(char16_t(','));
+ if (comma >= 0)
+ aName.Truncate(comma);
+}
+
+#define MAX_VALUE_NAME 512
+#define MAX_VALUE_DATA 512
+
+nsresult
+gfxDWriteFontList::GetFontSubstitutes()
+{
+ HKEY hKey;
+ DWORD i, rv, lenAlias, lenActual, valueType;
+ WCHAR aliasName[MAX_VALUE_NAME];
+ WCHAR actualName[MAX_VALUE_DATA];
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
+ 0, KEY_READ, &hKey) != ERROR_SUCCESS)
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
+ aliasName[0] = 0;
+ lenAlias = ArrayLength(aliasName);
+ actualName[0] = 0;
+ lenActual = sizeof(actualName);
+ rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
+ (LPBYTE)actualName, &lenActual);
+
+ if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
+ continue;
+ }
+
+ if (aliasName[0] == WCHAR('@')) {
+ continue;
+ }
+
+ nsAutoString substituteName((char16_t*) aliasName);
+ nsAutoString actualFontName((char16_t*) actualName);
+ RemoveCharsetFromFontSubstitute(substituteName);
+ BuildKeyNameFromFontName(substituteName);
+ RemoveCharsetFromFontSubstitute(actualFontName);
+ BuildKeyNameFromFontName(actualFontName);
+ gfxFontFamily *ff;
+ if (!actualFontName.IsEmpty() &&
+ (ff = mFontFamilies.GetWeak(actualFontName))) {
+ mFontSubstitutes.Put(substituteName, ff);
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ }
+ return NS_OK;
+}
+
+struct FontSubstitution {
+ const WCHAR* aliasName;
+ const WCHAR* actualName;
+};
+
+static const FontSubstitution sDirectWriteSubs[] = {
+ { L"MS Sans Serif", L"Microsoft Sans Serif" },
+ { L"MS Serif", L"Times New Roman" },
+ { L"Courier", L"Courier New" },
+ { L"Small Fonts", L"Arial" },
+ { L"Roman", L"Times New Roman" },
+ { L"Script", L"Mistral" }
+};
+
+void
+gfxDWriteFontList::GetDirectWriteSubstitutes()
+{
+ for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
+ const FontSubstitution& sub(sDirectWriteSubs[i]);
+ nsAutoString substituteName((char16_t*)sub.aliasName);
+ BuildKeyNameFromFontName(substituteName);
+ if (nullptr != mFontFamilies.GetWeak(substituteName)) {
+ // don't do the substitution if user actually has a usable font
+ // with this name installed
+ continue;
+ }
+ nsAutoString actualFontName((char16_t*)sub.actualName);
+ BuildKeyNameFromFontName(actualFontName);
+ gfxFontFamily *ff;
+ if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
+ mFontSubstitutes.Put(substituteName, ff);
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ }
+}
+
+bool
+gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName)
+{
+ gfxFontFamily *family = FindFamily(aFontName);
+ if (family) {
+ family->LocalizedName(aFamilyName);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+gfxDWriteFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ nsAutoString keyName(aFamily);
+ BuildKeyNameFromFontName(keyName);
+
+ gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
+ if (ff) {
+ aOutput->AppendElement(ff);
+ return true;
+ }
+
+ if (mNonExistingFonts.Contains(keyName)) {
+ return false;
+ }
+
+ return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+ aDevToCssSize);
+}
+
+void
+gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+
+ aSizes->mFontListSize +=
+ SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
+ aSizes->mFontListSize +=
+ mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+}
+
+void
+gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+static HRESULT GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName)
+{
+ HRESULT hr;
+ RefPtr<IDWriteFontFamily> family;
+
+ // clean out previous value
+ aFamilyName.Truncate();
+
+ hr = aFont->GetFontFamily(getter_AddRefs(family));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ RefPtr<IDWriteLocalizedStrings> familyNames;
+
+ hr = family->GetFamilyNames(getter_AddRefs(familyNames));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ UINT32 index = 0;
+ BOOL exists = false;
+
+ hr = familyNames->FindLocaleName(L"en-us", &index, &exists);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // If the specified locale doesn't exist, select the first on the list.
+ if (!exists) {
+ index = 0;
+ }
+
+ AutoTArray<WCHAR, 32> name;
+ UINT32 length;
+
+ hr = familyNames->GetStringLength(index, &length);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (!name.SetLength(length + 1, fallible)) {
+ return E_FAIL;
+ }
+ hr = familyNames->GetString(index, name.Elements(), length + 1);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ aFamilyName.Assign(name.Elements());
+ return S_OK;
+}
+
+// bug 705594 - the method below doesn't actually do any "drawing", it's only
+// used to invoke the DirectWrite layout engine to determine the fallback font
+// for a given character.
+
+IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun(
+ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuringMode,
+ DWRITE_GLYPH_RUN const* glyphRun,
+ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+ IUnknown* clientDrawingEffect
+ )
+{
+ if (!mSystemFonts) {
+ return E_FAIL;
+ }
+
+ HRESULT hr = S_OK;
+
+ RefPtr<IDWriteFont> font;
+ hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
+ getter_AddRefs(font));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // copy the family name
+ hr = GetFamilyName(font, mFamilyName);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Arial is used as the default fallback font
+ // so if it matches ==> no font found
+ if (mFamilyName.EqualsLiteral("Arial")) {
+ mFamilyName.Truncate();
+ return E_FAIL;
+ }
+ return hr;
+}
+
+gfxFontEntry*
+gfxDWriteFontList::PlatformGlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily)
+{
+ HRESULT hr;
+
+ RefPtr<IDWriteFactory> dwFactory =
+ gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
+ if (!dwFactory) {
+ return nullptr;
+ }
+
+ // initialize fallback renderer
+ if (!mFallbackRenderer) {
+ mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
+ }
+
+ // initialize text format
+ if (!mFallbackFormat) {
+ hr = dwFactory->CreateTextFormat(L"Arial", nullptr,
+ DWRITE_FONT_WEIGHT_REGULAR,
+ DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL,
+ 72.0f, L"en-us",
+ getter_AddRefs(mFallbackFormat));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ }
+
+ // set up string with fallback character
+ wchar_t str[16];
+ uint32_t strLen;
+
+ if (IS_IN_BMP(aCh)) {
+ str[0] = static_cast<wchar_t> (aCh);
+ str[1] = 0;
+ strLen = 1;
+ } else {
+ str[0] = static_cast<wchar_t> (H_SURROGATE(aCh));
+ str[1] = static_cast<wchar_t> (L_SURROGATE(aCh));
+ str[2] = 0;
+ strLen = 2;
+ }
+
+ // set up layout
+ RefPtr<IDWriteTextLayout> fallbackLayout;
+
+ hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat,
+ 200.0f, 200.0f,
+ getter_AddRefs(fallbackLayout));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ // call the draw method to invoke the DirectWrite layout functions
+ // which determine the fallback font
+ hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ gfxFontFamily *family = FindFamily(mFallbackRenderer->FallbackFamilyName());
+ if (family) {
+ gfxFontEntry *fontEntry;
+ bool needsBold; // ignored in the system fallback case
+ fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
+ if (fontEntry && fontEntry->HasCharacter(aCh)) {
+ *aMatchedFamily = family;
+ return fontEntry;
+ }
+ }
+
+ return nullptr;
+}
+
+// used to load system-wide font info on off-main thread
+class DirectWriteFontInfo : public FontInfoData {
+public:
+ DirectWriteFontInfo(bool aLoadOtherNames,
+ bool aLoadFaceNames,
+ bool aLoadCmaps,
+ IDWriteFontCollection* aSystemFonts
+ , IDWriteFontCollection* aBundledFonts
+ ) :
+ FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
+ , mSystemFonts(aSystemFonts)
+ , mBundledFonts(aBundledFonts)
+ {}
+
+ virtual ~DirectWriteFontInfo() {}
+
+ // loads font data for all members of a given family
+ virtual void LoadFontFamilyData(const nsAString& aFamilyName);
+
+private:
+ RefPtr<IDWriteFontCollection> mSystemFonts;
+ RefPtr<IDWriteFontCollection> mBundledFonts;
+};
+
+void
+DirectWriteFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
+{
+ // lookup the family
+ AutoTArray<wchar_t, 32> famName;
+
+ uint32_t len = aFamilyName.Length();
+ if(!famName.SetLength(len + 1, fallible)) {
+ return;
+ }
+ memcpy(famName.Elements(), aFamilyName.BeginReading(), len * sizeof(char16_t));
+ famName[len] = 0;
+
+ HRESULT hr;
+ BOOL exists = false;
+
+ uint32_t index;
+ RefPtr<IDWriteFontFamily> family;
+ hr = mSystemFonts->FindFamilyName(famName.Elements(), &index, &exists);
+ if (SUCCEEDED(hr) && exists) {
+ mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
+ if (!family) {
+ return;
+ }
+ }
+
+ if (!family && mBundledFonts) {
+ hr = mBundledFonts->FindFamilyName(famName.Elements(), &index, &exists);
+ if (SUCCEEDED(hr) && exists) {
+ mBundledFonts->GetFontFamily(index, getter_AddRefs(family));
+ }
+ }
+
+ if (!family) {
+ return;
+ }
+
+ // later versions of DirectWrite support querying the fullname/psname
+ bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
+
+ for (uint32_t i = 0; i < family->GetFontCount(); i++) {
+ // get the font
+ RefPtr<IDWriteFont> dwFont;
+ hr = family->GetFont(i, getter_AddRefs(dwFont));
+ if (FAILED(hr)) {
+ // This should never happen.
+ NS_WARNING("Failed to get existing font from family.");
+ continue;
+ }
+
+ if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
+ // We don't want these in the font list; we'll apply simulations
+ // on the fly when appropriate.
+ continue;
+ }
+
+ mLoadStats.fonts++;
+
+ // get the name of the face
+ nsString fullID(aFamilyName);
+ nsAutoString fontName;
+ hr = GetDirectWriteFontName(dwFont, fontName);
+ if (FAILED(hr)) {
+ continue;
+ }
+ fullID.Append(' ');
+ fullID.Append(fontName);
+
+ FontFaceData fontData;
+ bool haveData = true;
+ RefPtr<IDWriteFontFace> dwFontFace;
+
+ if (mLoadFaceNames) {
+ // try to load using DirectWrite first
+ if (loadFaceNamesUsingDirectWrite) {
+ hr = GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
+ if (FAILED(hr)) {
+ loadFaceNamesUsingDirectWrite = false;
+ }
+ hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
+ if (FAILED(hr)) {
+ loadFaceNamesUsingDirectWrite = false;
+ }
+ }
+
+ // if DirectWrite read fails, load directly from name table
+ if (!loadFaceNamesUsingDirectWrite) {
+ hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
+ if (SUCCEEDED(hr)) {
+ uint32_t kNAME =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
+ const char *nameData;
+ BOOL exists;
+ void* ctx;
+ uint32_t nameSize;
+
+ hr = dwFontFace->TryGetFontTable(
+ kNAME,
+ (const void**)&nameData, &nameSize, &ctx, &exists);
+
+ if (SUCCEEDED(hr) && nameData && nameSize > 0) {
+ gfxFontUtils::ReadCanonicalName(nameData, nameSize,
+ gfxFontUtils::NAME_ID_FULL,
+ fontData.mFullName);
+ gfxFontUtils::ReadCanonicalName(nameData, nameSize,
+ gfxFontUtils::NAME_ID_POSTSCRIPT,
+ fontData.mPostscriptName);
+ dwFontFace->ReleaseFontTable(ctx);
+ }
+ }
+ }
+
+ haveData = !fontData.mPostscriptName.IsEmpty() ||
+ !fontData.mFullName.IsEmpty();
+ if (haveData) {
+ mLoadStats.facenames++;
+ }
+ }
+
+ // cmaps
+ if (mLoadCmaps) {
+ if (!dwFontFace) {
+ hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
+ if (!SUCCEEDED(hr)) {
+ continue;
+ }
+ }
+
+ uint32_t kCMAP =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
+ const uint8_t *cmapData;
+ BOOL exists;
+ void* ctx;
+ uint32_t cmapSize;
+
+ hr = dwFontFace->TryGetFontTable(kCMAP,
+ (const void**)&cmapData, &cmapSize, &ctx, &exists);
+
+ if (SUCCEEDED(hr)) {
+ bool cmapLoaded = false;
+ bool unicodeFont = false, symbolFont = false;
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ uint32_t offset;
+
+ if (cmapData &&
+ cmapSize > 0 &&
+ NS_SUCCEEDED(
+ gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
+ offset, unicodeFont, symbolFont))) {
+ fontData.mCharacterMap = charmap;
+ fontData.mUVSOffset = offset;
+ fontData.mSymbolFont = symbolFont;
+ cmapLoaded = true;
+ mLoadStats.cmaps++;
+ }
+ dwFontFace->ReleaseFontTable(ctx);
+ haveData = haveData || cmapLoaded;
+ }
+ }
+
+ // if have data, load
+ if (haveData) {
+ mFontFaceData.Put(fullID, fontData);
+ }
+ }
+}
+
+already_AddRefed<FontInfoData>
+gfxDWriteFontList::CreateFontInfoData()
+{
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ RefPtr<DirectWriteFontInfo> fi =
+ new DirectWriteFontInfo(false, NeedFullnamePostscriptNames(), loadCmaps,
+ mSystemFonts
+ , mBundledFonts
+ );
+
+ return fi.forget();
+}
+
+
+
+#define IMPL_QI_FOR_DWRITE(_interface) \
+ public: \
+ IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject) \
+ { \
+ if (__uuidof(_interface) == riid) { \
+ *ppvObject = this; \
+ } else if (__uuidof(IUnknown) == riid) { \
+ *ppvObject = this; \
+ } else { \
+ *ppvObject = nullptr; \
+ return E_NOINTERFACE; \
+ } \
+ this->AddRef(); \
+ return S_OK; \
+ }
+
+class BundledFontFileEnumerator
+ : public IDWriteFontFileEnumerator
+{
+ IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator)
+
+ NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator)
+
+public:
+ BundledFontFileEnumerator(IDWriteFactory *aFactory,
+ nsIFile *aFontDir);
+
+ IFACEMETHODIMP MoveNext(BOOL * hasCurrentFile);
+
+ IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile ** fontFile);
+
+private:
+ BundledFontFileEnumerator() = delete;
+ BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
+ BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) = delete;
+ virtual ~BundledFontFileEnumerator() {}
+
+ RefPtr<IDWriteFactory> mFactory;
+
+ nsCOMPtr<nsIFile> mFontDir;
+ nsCOMPtr<nsISimpleEnumerator> mEntries;
+ nsCOMPtr<nsISupports> mCurrent;
+};
+
+BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory *aFactory,
+ nsIFile *aFontDir)
+ : mFactory(aFactory)
+ , mFontDir(aFontDir)
+{
+ mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
+}
+
+IFACEMETHODIMP
+BundledFontFileEnumerator::MoveNext(BOOL * aHasCurrentFile)
+{
+ bool hasMore = false;
+ if (mEntries) {
+ if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) {
+ if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) {
+ hasMore = true;
+ }
+ }
+ }
+ *aHasCurrentFile = hasMore;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile ** aFontFile)
+{
+ nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent);
+ if (!file) {
+ return E_FAIL;
+ }
+ nsString path;
+ if (NS_FAILED(file->GetPath(path))) {
+ return E_FAIL;
+ }
+ return mFactory->CreateFontFileReference((const WCHAR*)path.get(),
+ nullptr, aFontFile);
+}
+
+class BundledFontLoader
+ : public IDWriteFontCollectionLoader
+{
+ IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader)
+
+ NS_INLINE_DECL_REFCOUNTING(BundledFontLoader)
+
+public:
+ BundledFontLoader()
+ {
+ }
+
+ IFACEMETHODIMP CreateEnumeratorFromKey(
+ IDWriteFactory *aFactory,
+ const void *aCollectionKey,
+ UINT32 aCollectionKeySize,
+ IDWriteFontFileEnumerator **aFontFileEnumerator);
+
+private:
+ BundledFontLoader(const BundledFontLoader&) = delete;
+ BundledFontLoader& operator=(const BundledFontLoader&) = delete;
+ virtual ~BundledFontLoader() { }
+};
+
+IFACEMETHODIMP
+BundledFontLoader::CreateEnumeratorFromKey(
+ IDWriteFactory *aFactory,
+ const void *aCollectionKey,
+ UINT32 aCollectionKeySize,
+ IDWriteFontFileEnumerator **aFontFileEnumerator)
+{
+ nsIFile *fontDir = *(nsIFile**)aCollectionKey;
+ *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir);
+ NS_ADDREF(*aFontFileEnumerator);
+ return S_OK;
+}
+
+already_AddRefed<IDWriteFontCollection>
+gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory)
+{
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ return nullptr;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return nullptr;
+ }
+
+ RefPtr<BundledFontLoader> loader = new BundledFontLoader();
+ if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) {
+ return nullptr;
+ }
+
+ const void *key = localDir.get();
+ RefPtr<IDWriteFontCollection> collection;
+ HRESULT hr =
+ aFactory->CreateCustomFontCollection(loader, &key, sizeof(key),
+ getter_AddRefs(collection));
+
+ aFactory->UnregisterFontCollectionLoader(loader);
+
+ if (FAILED(hr)) {
+ return nullptr;
+ } else {
+ return collection.forget();
+ }
+}
+
diff --git a/system/graphics/thebes/gfxDWriteFontList.h b/system/graphics/thebes/gfxDWriteFontList.h
new file mode 100644
index 000000000..c2ffeb0c9
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteFontList.h
@@ -0,0 +1,441 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DWRITEFONTLIST_H
+#define GFX_DWRITEFONTLIST_H
+
+#include "mozilla/MemoryReporting.h"
+#include "gfxDWriteCommon.h"
+
+#include "gfxFont.h"
+#include "gfxUserFontSet.h"
+#include "cairo-win32.h"
+
+#include "gfxPlatformFontList.h"
+#include "gfxPlatform.h"
+#include <algorithm>
+
+
+/**
+ * gfxDWriteFontFamily is a class that describes one of the fonts on the
+ * users system. It holds each gfxDWriteFontEntry (maps more directly to
+ * a font face) which holds font type, charset info and character map info.
+ */
+class gfxDWriteFontEntry;
+
+/**
+ * \brief Class representing directwrite font family.
+ */
+class gfxDWriteFontFamily : public gfxFontFamily
+{
+public:
+ /**
+ * Constructs a new DWriteFont Family.
+ *
+ * \param aName Name identifying the family
+ * \param aFamily IDWriteFontFamily object representing the directwrite
+ * family object.
+ */
+ gfxDWriteFontFamily(const nsAString& aName,
+ IDWriteFontFamily *aFamily)
+ : gfxFontFamily(aName), mDWFamily(aFamily), mForceGDIClassic(false) {}
+ virtual ~gfxDWriteFontFamily();
+
+ void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) final;
+
+ void LocalizedName(nsAString& aLocalizedName) final;
+
+ void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
+ bool aNeedFullnamePostscriptNames,
+ FontInfoData *aFontInfoData = nullptr) final;
+
+ void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
+
+ void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const final;
+ void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const final;
+
+protected:
+ /** This font family's directwrite fontfamily object */
+ RefPtr<IDWriteFontFamily> mDWFamily;
+ bool mForceGDIClassic;
+};
+
+/**
+ * \brief Class representing DirectWrite FontEntry (a unique font style/family)
+ */
+class gfxDWriteFontEntry : public gfxFontEntry
+{
+public:
+ /**
+ * Constructs a font entry.
+ *
+ * \param aFaceName The name of the corresponding font face.
+ * \param aFont DirectWrite font object
+ */
+ gfxDWriteFontEntry(const nsAString& aFaceName,
+ IDWriteFont *aFont)
+ : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
+ mForceGDIClassic(false)
+ {
+ DWRITE_FONT_STYLE dwriteStyle = aFont->GetStyle();
+ mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
+ NS_FONT_STYLE_ITALIC :
+ (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
+ NS_FONT_STYLE_OBLIQUE : NS_FONT_STYLE_NORMAL));
+ mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
+ uint16_t weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
+
+ weight = std::max<uint16_t>(100, weight);
+ weight = std::min<uint16_t>(900, weight);
+ mWeight = weight;
+
+ mIsCJK = UNINITIALIZED_VALUE;
+ }
+
+ /**
+ * Constructs a font entry using a font. But with custom font values.
+ * This is used for creating correct font entries for @font-face with local
+ * font source.
+ *
+ * \param aFaceName The name of the corresponding font face.
+ * \param aFont DirectWrite font object
+ * \param aWeight Weight of the font
+ * \param aStretch Stretch of the font
+ * \param aStyle italic or oblique of font
+ */
+ gfxDWriteFontEntry(const nsAString& aFaceName,
+ IDWriteFont *aFont,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+ : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
+ mForceGDIClassic(false)
+ {
+ mWeight = aWeight;
+ mStretch = aStretch;
+ mStyle = aStyle;
+ mIsLocalUserFont = true;
+ mIsCJK = UNINITIALIZED_VALUE;
+ }
+
+ /**
+ * Constructs a font entry using a font file.
+ *
+ * \param aFaceName The name of the corresponding font face.
+ * \param aFontFile DirectWrite fontfile object
+ * \param aFontFileStream DirectWrite fontfile stream object
+ * \param aWeight Weight of the font
+ * \param aStretch Stretch of the font
+ * \param aStyle italic or oblique of font
+ */
+ gfxDWriteFontEntry(const nsAString& aFaceName,
+ IDWriteFontFile *aFontFile,
+ IDWriteFontFileStream *aFontFileStream,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+ : gfxFontEntry(aFaceName), mFont(nullptr), mFontFile(aFontFile),
+ mFontFileStream(aFontFileStream), mForceGDIClassic(false)
+ {
+ mWeight = aWeight;
+ mStretch = aStretch;
+ mStyle = aStyle;
+ mIsDataUserFont = true;
+ mIsCJK = UNINITIALIZED_VALUE;
+ }
+
+ virtual ~gfxDWriteFontEntry();
+
+ virtual bool IsSymbolFont();
+
+ virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
+
+ nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
+
+ bool IsCJKFont();
+
+ void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
+ bool GetForceGDIClassic() { return mForceGDIClassic; }
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+protected:
+ friend class gfxDWriteFont;
+ friend class gfxDWriteFontList;
+
+ virtual nsresult CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) override;
+
+ virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
+ bool aNeedsBold);
+
+ nsresult CreateFontFace(
+ IDWriteFontFace **aFontFace,
+ DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE);
+
+ static bool InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont);
+
+ /**
+ * A fontentry only needs to have either of these. If it has both only
+ * the IDWriteFont will be used.
+ */
+ RefPtr<IDWriteFont> mFont;
+ RefPtr<IDWriteFontFile> mFontFile;
+
+ // For custom fonts, we hold a reference to the IDWriteFontFileStream for
+ // for the IDWriteFontFile, so that the data is available.
+ RefPtr<IDWriteFontFileStream> mFontFileStream;
+
+ // font face corresponding to the mFont/mFontFile *without* any DWrite
+ // style simulations applied
+ RefPtr<IDWriteFontFace> mFontFace;
+
+ DWRITE_FONT_FACE_TYPE mFaceType;
+
+ int8_t mIsCJK;
+ bool mForceGDIClassic;
+};
+
+// custom text renderer used to determine the fallback font for a given char
+class DWriteFontFallbackRenderer final : public IDWriteTextRenderer
+{
+public:
+ DWriteFontFallbackRenderer(IDWriteFactory *aFactory)
+ : mRefCount(0)
+ {
+ HRESULT hr = S_OK;
+
+ hr = aFactory->GetSystemFontCollection(getter_AddRefs(mSystemFonts));
+ NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
+ }
+
+ ~DWriteFontFallbackRenderer()
+ {}
+
+ // IDWriteTextRenderer methods
+ IFACEMETHOD(DrawGlyphRun)(
+ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuringMode,
+ DWRITE_GLYPH_RUN const* glyphRun,
+ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+ IUnknown* clientDrawingEffect
+ );
+
+ IFACEMETHOD(DrawUnderline)(
+ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_UNDERLINE const* underline,
+ IUnknown* clientDrawingEffect
+ )
+ {
+ return E_NOTIMPL;
+ }
+
+
+ IFACEMETHOD(DrawStrikethrough)(
+ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_STRIKETHROUGH const* strikethrough,
+ IUnknown* clientDrawingEffect
+ )
+ {
+ return E_NOTIMPL;
+ }
+
+
+ IFACEMETHOD(DrawInlineObject)(
+ void* clientDrawingContext,
+ FLOAT originX,
+ FLOAT originY,
+ IDWriteInlineObject* inlineObject,
+ BOOL isSideways,
+ BOOL isRightToLeft,
+ IUnknown* clientDrawingEffect
+ )
+ {
+ return E_NOTIMPL;
+ }
+
+ // IDWritePixelSnapping methods
+
+ IFACEMETHOD(IsPixelSnappingDisabled)(
+ void* clientDrawingContext,
+ BOOL* isDisabled
+ )
+ {
+ *isDisabled = FALSE;
+ return S_OK;
+ }
+
+ IFACEMETHOD(GetCurrentTransform)(
+ void* clientDrawingContext,
+ DWRITE_MATRIX* transform
+ )
+ {
+ const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
+ *transform = ident;
+ return S_OK;
+ }
+
+ IFACEMETHOD(GetPixelsPerDip)(
+ void* clientDrawingContext,
+ FLOAT* pixelsPerDip
+ )
+ {
+ *pixelsPerDip = 1.0f;
+ return S_OK;
+ }
+
+ // IUnknown methods
+
+ IFACEMETHOD_(unsigned long, AddRef) ()
+ {
+ return InterlockedIncrement(&mRefCount);
+ }
+
+ IFACEMETHOD_(unsigned long, Release) ()
+ {
+ unsigned long newCount = InterlockedDecrement(&mRefCount);
+ if (newCount == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return newCount;
+ }
+
+ IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject)
+ {
+ if (__uuidof(IDWriteTextRenderer) == riid) {
+ *ppvObject = this;
+ } else if (__uuidof(IDWritePixelSnapping) == riid) {
+ *ppvObject = this;
+ } else if (__uuidof(IUnknown) == riid) {
+ *ppvObject = this;
+ } else {
+ *ppvObject = nullptr;
+ return E_FAIL;
+ }
+
+ this->AddRef();
+ return S_OK;
+ }
+
+ const nsString& FallbackFamilyName() { return mFamilyName; }
+
+protected:
+ long mRefCount;
+ RefPtr<IDWriteFontCollection> mSystemFonts;
+ nsString mFamilyName;
+};
+
+
+
+class gfxDWriteFontList : public gfxPlatformFontList {
+public:
+ gfxDWriteFontList();
+
+ static gfxDWriteFontList* PlatformFontList() {
+ return static_cast<gfxDWriteFontList*>(sPlatformFontList);
+ }
+
+ // initialize font lists
+ virtual nsresult InitFontListForPlatform() override;
+
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+ bool GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName);
+
+ IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
+ bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
+
+ bool FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0) override;
+
+ gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+protected:
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
+ // attempt to use platform-specific fallback for the given character,
+ // return null if no usable result found
+ gfxFontEntry*
+ PlatformGlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily) override;
+
+private:
+ friend class gfxDWriteFontFamily;
+
+ nsresult GetFontSubstitutes();
+
+ void GetDirectWriteSubstitutes();
+
+ virtual bool UsesSystemFallback() { return true; }
+
+ void GetFontsFromCollection(IDWriteFontCollection* aCollection);
+
+ already_AddRefed<IDWriteFontCollection>
+ CreateBundledFontsCollection(IDWriteFactory* aFactory);
+
+ /**
+ * Fonts listed in the registry as substitutes but for which no actual
+ * font family is found.
+ */
+ nsTArray<nsString> mNonExistingFonts;
+
+ /**
+ * Table of font substitutes, we grab this from the registry to get
+ * alternative font names.
+ */
+ FontFamilyTable mFontSubstitutes;
+
+ virtual already_AddRefed<FontInfoData> CreateFontInfoData();
+
+ gfxFloat mForceGDIClassicMaxFontSize;
+
+ // whether to use GDI font table access routines
+ bool mGDIFontTableAccess;
+ RefPtr<IDWriteGdiInterop> mGDIInterop;
+
+ RefPtr<DWriteFontFallbackRenderer> mFallbackRenderer;
+ RefPtr<IDWriteTextFormat> mFallbackFormat;
+
+ RefPtr<IDWriteFontCollection> mSystemFonts;
+ RefPtr<IDWriteFontCollection> mBundledFonts;
+};
+
+
+#endif /* GFX_DWRITEFONTLIST_H */
diff --git a/system/graphics/thebes/gfxDWriteFonts.cpp b/system/graphics/thebes/gfxDWriteFonts.cpp
new file mode 100644
index 000000000..96a935245
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteFonts.cpp
@@ -0,0 +1,709 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxDWriteFonts.h"
+
+#include "mozilla/MemoryReporting.h"
+
+#include <algorithm>
+#include "gfxDWriteFontList.h"
+#include "gfxContext.h"
+#include "gfxTextRun.h"
+#include <dwrite.h>
+
+#include "harfbuzz/hb.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+// This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common,
+// but we can't declare it in the gfxFont.h or gfxFontUtils.h headers
+// because those are exported, and the cairo headers aren't.
+static inline cairo_antialias_t
+GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
+{
+ switch (anAntialiasOption) {
+ default:
+ case gfxFont::kAntialiasDefault:
+ return CAIRO_ANTIALIAS_DEFAULT;
+ case gfxFont::kAntialiasNone:
+ return CAIRO_ANTIALIAS_NONE;
+ case gfxFont::kAntialiasGrayscale:
+ return CAIRO_ANTIALIAS_GRAY;
+ case gfxFont::kAntialiasSubpixel:
+ return CAIRO_ANTIALIAS_SUBPIXEL;
+ }
+}
+
+// Code to determine whether Windows is set to use ClearType font smoothing;
+// based on private functions in cairo-win32-font.c
+
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+#define SPI_GETFONTSMOOTHINGTYPE 0x200a
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+#define FE_FONTSMOOTHINGCLEARTYPE 2
+#endif
+
+static bool
+UsingClearType()
+{
+ BOOL fontSmoothing;
+ if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
+ !fontSmoothing)
+ {
+ return false;
+ }
+
+ UINT type;
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
+ type == FE_FONTSMOOTHINGCLEARTYPE)
+ {
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// gfxDWriteFont
+gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold,
+ AntialiasOption anAAOption)
+ : gfxFont(aFontEntry, aFontStyle, anAAOption)
+ , mCairoFontFace(nullptr)
+ , mMetrics(nullptr)
+ , mSpaceGlyph(0)
+ , mNeedsOblique(false)
+ , mNeedsBold(aNeedsBold)
+ , mUseSubpixelPositions(false)
+ , mAllowManualShowGlyphs(true)
+{
+ gfxDWriteFontEntry *fe =
+ static_cast<gfxDWriteFontEntry*>(aFontEntry);
+ nsresult rv;
+ DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
+ if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
+ fe->IsUpright() &&
+ GetStyle()->allowSyntheticStyle) {
+ // For this we always use the font_matrix for uniformity. Not the
+ // DWrite simulation.
+ mNeedsOblique = true;
+ }
+ if (aNeedsBold) {
+ sims |= DWRITE_FONT_SIMULATIONS_BOLD;
+ }
+
+ rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
+
+ if (NS_FAILED(rv)) {
+ mIsValid = false;
+ return;
+ }
+
+ ComputeMetrics(anAAOption);
+}
+
+gfxDWriteFont::~gfxDWriteFont()
+{
+ if (mCairoFontFace) {
+ cairo_font_face_destroy(mCairoFontFace);
+ }
+ if (mScaledFont) {
+ cairo_scaled_font_destroy(mScaledFont);
+ }
+ delete mMetrics;
+}
+
+gfxFont*
+gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
+{
+ return new gfxDWriteFont(static_cast<gfxDWriteFontEntry*>(mFontEntry.get()),
+ &mStyle, mNeedsBold, anAAOption);
+}
+
+const gfxFont::Metrics&
+gfxDWriteFont::GetHorizontalMetrics()
+{
+ return *mMetrics;
+}
+
+bool
+gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics)
+{
+ gfxFontStyle style(mStyle);
+ style.weight = 700;
+ bool needsBold;
+
+ gfxFontEntry* fe =
+ gfxPlatformFontList::PlatformFontList()->
+ FindFontForFamily(NS_LITERAL_STRING("Arial"), &style, needsBold);
+ if (!fe || fe == mFontEntry) {
+ return false;
+ }
+
+ RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, needsBold);
+ gfxDWriteFont *dwFont = static_cast<gfxDWriteFont*>(font.get());
+ dwFont->mFontFace->GetMetrics(aFontMetrics);
+
+ return true;
+}
+
+void
+gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
+{
+ DWRITE_FONT_METRICS fontMetrics;
+ if (!(mFontEntry->Weight() == 900 &&
+ !mFontEntry->IsUserFont() &&
+ mFontEntry->Name().EqualsLiteral("Arial Black") &&
+ GetFakeMetricsForArialBlack(&fontMetrics)))
+ {
+ mFontFace->GetMetrics(&fontMetrics);
+ }
+
+ if (mStyle.sizeAdjust >= 0.0) {
+ gfxFloat aspect = (gfxFloat)fontMetrics.xHeight /
+ fontMetrics.designUnitsPerEm;
+ mAdjustedSize = mStyle.GetAdjustedSize(aspect);
+ } else {
+ mAdjustedSize = mStyle.size;
+ }
+
+ // Note that GetMeasuringMode depends on mAdjustedSize
+ if ((anAAOption == gfxFont::kAntialiasDefault &&
+ UsingClearType() &&
+ GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
+ anAAOption == gfxFont::kAntialiasSubpixel)
+ {
+ mUseSubpixelPositions = true;
+ // note that this may be reset to FALSE if we determine that a bitmap
+ // strike is going to be used
+ }
+
+ gfxDWriteFontEntry *fe =
+ static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
+ if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
+ mAdjustedSize = NS_lround(mAdjustedSize);
+ mUseSubpixelPositions = false;
+ // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
+ // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
+ // which fails to render bitmap glyphs (see bug 626299).
+ // This option will be passed to the cairo_dwrite_scaled_font_t
+ // after creation.
+ mAllowManualShowGlyphs = false;
+ }
+
+ mMetrics = new gfxFont::Metrics;
+ ::memset(mMetrics, 0, sizeof(*mMetrics));
+
+ mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
+
+ mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
+ mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
+
+ mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor);
+ mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor);
+ mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
+
+ mMetrics->emHeight = mAdjustedSize;
+ mMetrics->emAscent = mMetrics->emHeight *
+ mMetrics->maxAscent / mMetrics->maxHeight;
+ mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
+
+ mMetrics->maxAdvance = mAdjustedSize;
+
+ // try to get the true maxAdvance value from 'hhea'
+ gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
+ TRUETYPE_TAG('h','h','e','a'));
+ if (hheaTable) {
+ uint32_t len;
+ const MetricsHeader* hhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(hheaTable, &len));
+ if (len >= sizeof(MetricsHeader)) {
+ mMetrics->maxAdvance =
+ uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
+ }
+ }
+
+ mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
+ mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
+
+ UINT32 ucs = L' ';
+ UINT16 glyph;
+ HRESULT hr = mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph);
+ if (FAILED(hr)) {
+ mMetrics->spaceWidth = 0;
+ } else {
+ mSpaceGlyph = glyph;
+ mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
+ }
+
+ // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
+ // if the table is not available or if using hinted/pixel-snapped widths
+ if (mUseSubpixelPositions) {
+ mMetrics->aveCharWidth = 0;
+ gfxFontEntry::AutoTable os2Table(GetFontEntry(),
+ TRUETYPE_TAG('O','S','/','2'));
+ if (os2Table) {
+ uint32_t len;
+ const OS2Table* os2 =
+ reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
+ if (len >= 4) {
+ // Not checking against sizeof(mozilla::OS2Table) here because older
+ // versions of the table have different sizes; we only need the first
+ // two 16-bit fields here.
+ mMetrics->aveCharWidth =
+ int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
+ }
+ }
+ }
+
+ if (mMetrics->aveCharWidth < 1) {
+ ucs = L'x';
+ if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
+ mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
+ }
+ if (mMetrics->aveCharWidth < 1) {
+ // Let's just assume the X is square.
+ mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
+ }
+ }
+
+ ucs = L'0';
+ if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
+ mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
+ }
+ if (mMetrics->zeroOrAveCharWidth < 1) {
+ mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
+ }
+
+ mMetrics->underlineOffset =
+ fontMetrics.underlinePosition * mFUnitsConvFactor;
+ mMetrics->underlineSize =
+ fontMetrics.underlineThickness * mFUnitsConvFactor;
+ mMetrics->strikeoutOffset =
+ fontMetrics.strikethroughPosition * mFUnitsConvFactor;
+ mMetrics->strikeoutSize =
+ fontMetrics.strikethroughThickness * mFUnitsConvFactor;
+
+ SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
+
+#if 0
+ printf("Font: %p (%s) size: %f\n", this,
+ NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
+ printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
+ printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
+ printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
+ printf(" spaceWidth: %f aveCharWidth: %f zeroOrAve: %f\n",
+ mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroOrAveCharWidth);
+ printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
+ printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
+ mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
+#endif
+}
+
+using namespace mozilla; // for AutoSwap_* types
+
+struct EBLCHeader {
+ AutoSwap_PRUint32 version;
+ AutoSwap_PRUint32 numSizes;
+};
+
+struct SbitLineMetrics {
+ int8_t ascender;
+ int8_t descender;
+ uint8_t widthMax;
+ int8_t caretSlopeNumerator;
+ int8_t caretSlopeDenominator;
+ int8_t caretOffset;
+ int8_t minOriginSB;
+ int8_t minAdvanceSB;
+ int8_t maxBeforeBL;
+ int8_t minAfterBL;
+ int8_t pad1;
+ int8_t pad2;
+};
+
+struct BitmapSizeTable {
+ AutoSwap_PRUint32 indexSubTableArrayOffset;
+ AutoSwap_PRUint32 indexTablesSize;
+ AutoSwap_PRUint32 numberOfIndexSubTables;
+ AutoSwap_PRUint32 colorRef;
+ SbitLineMetrics hori;
+ SbitLineMetrics vert;
+ AutoSwap_PRUint16 startGlyphIndex;
+ AutoSwap_PRUint16 endGlyphIndex;
+ uint8_t ppemX;
+ uint8_t ppemY;
+ uint8_t bitDepth;
+ uint8_t flags;
+};
+
+typedef EBLCHeader EBSCHeader;
+
+struct BitmapScaleTable {
+ SbitLineMetrics hori;
+ SbitLineMetrics vert;
+ uint8_t ppemX;
+ uint8_t ppemY;
+ uint8_t substitutePpemX;
+ uint8_t substitutePpemY;
+};
+
+bool
+gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize)
+{
+ uint8_t *tableData;
+ uint32_t len;
+ void *tableContext;
+ BOOL exists;
+ HRESULT hr =
+ mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'),
+ (const void**)&tableData, &len,
+ &tableContext, &exists);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ bool hasStrike = false;
+ // not really a loop, but this lets us use 'break' to skip out of the block
+ // as soon as we know the answer, and skips it altogether if the table is
+ // not present
+ while (exists) {
+ if (len < sizeof(EBLCHeader)) {
+ break;
+ }
+ const EBLCHeader *hdr = reinterpret_cast<const EBLCHeader*>(tableData);
+ if (hdr->version != 0x00020000) {
+ break;
+ }
+ uint32_t numSizes = hdr->numSizes;
+ if (numSizes > 0xffff) { // sanity-check, prevent overflow below
+ break;
+ }
+ if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
+ break;
+ }
+ const BitmapSizeTable *sizeTable =
+ reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
+ for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
+ if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
+ // we ignore a strike that contains fewer than 4 glyphs,
+ // as that probably indicates a font such as Courier New
+ // that provides bitmaps ONLY for the "shading" characters
+ // U+2591..2593
+ hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
+ uint16_t(sizeTable->startGlyphIndex) + 3);
+ break;
+ }
+ }
+ // if we reach here, we didn't find a strike; unconditionally break
+ // out of the while-loop block
+ break;
+ }
+ mFontFace->ReleaseFontTable(tableContext);
+
+ if (hasStrike) {
+ return true;
+ }
+
+ // if we didn't find a real strike, check if the font calls for scaling
+ // another bitmap to this size
+ hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
+ (const void**)&tableData, &len,
+ &tableContext, &exists);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ while (exists) {
+ if (len < sizeof(EBSCHeader)) {
+ break;
+ }
+ const EBSCHeader *hdr = reinterpret_cast<const EBSCHeader*>(tableData);
+ if (hdr->version != 0x00020000) {
+ break;
+ }
+ uint32_t numSizes = hdr->numSizes;
+ if (numSizes > 0xffff) {
+ break;
+ }
+ if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
+ break;
+ }
+ const BitmapScaleTable *scaleTable =
+ reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
+ for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
+ if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
+ hasStrike = true;
+ break;
+ }
+ }
+ break;
+ }
+ mFontFace->ReleaseFontTable(tableContext);
+
+ return hasStrike;
+}
+
+uint32_t
+gfxDWriteFont::GetSpaceGlyph()
+{
+ return mSpaceGlyph;
+}
+
+bool
+gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
+{
+ cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
+ if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
+ // Don't cairo_set_scaled_font as that would propagate the error to
+ // the cairo_t, precluding any further drawing.
+ return false;
+ }
+ cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), scaledFont);
+ return true;
+}
+
+bool
+gfxDWriteFont::IsValid() const
+{
+ return mFontFace != nullptr;
+}
+
+IDWriteFontFace*
+gfxDWriteFont::GetFontFace()
+{
+ return mFontFace.get();
+}
+
+cairo_font_face_t *
+gfxDWriteFont::CairoFontFace()
+{
+ if (!mCairoFontFace) {
+#ifdef CAIRO_HAS_DWRITE_FONT
+ mCairoFontFace =
+ cairo_dwrite_font_face_create_for_dwrite_fontface(
+ ((gfxDWriteFontEntry*)mFontEntry.get())->mFont, mFontFace);
+#endif
+ }
+ return mCairoFontFace;
+}
+
+
+cairo_scaled_font_t *
+gfxDWriteFont::GetCairoScaledFont()
+{
+ if (!mScaledFont) {
+ cairo_matrix_t sizeMatrix;
+ cairo_matrix_t identityMatrix;
+
+ cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
+ cairo_matrix_init_identity(&identityMatrix);
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+ if (mNeedsOblique) {
+ double skewfactor = OBLIQUE_SKEW_FACTOR;
+
+ cairo_matrix_t style;
+ cairo_matrix_init(&style,
+ 1, //xx
+ 0, //yx
+ -1 * skewfactor, //xy
+ 1, //yy
+ 0, //x0
+ 0); //y0
+ cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
+ }
+
+ if (mAntialiasOption != kAntialiasDefault) {
+ cairo_font_options_set_antialias(fontOptions,
+ GetCairoAntialiasOption(mAntialiasOption));
+ }
+
+ mScaledFont = cairo_scaled_font_create(CairoFontFace(),
+ &sizeMatrix,
+ &identityMatrix,
+ fontOptions);
+ cairo_font_options_destroy(fontOptions);
+
+ cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont,
+ mAllowManualShowGlyphs);
+
+ cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont,
+ GetForceGDIClassic());
+ }
+
+ NS_ASSERTION(mAdjustedSize == 0.0 ||
+ cairo_scaled_font_status(mScaledFont)
+ == CAIRO_STATUS_SUCCESS,
+ "Failed to make scaled font");
+
+ return mScaledFont;
+}
+
+gfxFont::RunMetrics
+gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ Spacing* aSpacing,
+ uint16_t aOrientation)
+{
+ gfxFont::RunMetrics metrics =
+ gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
+ aRefDrawTarget, aSpacing, aOrientation);
+
+ // if aBoundingBoxType is LOOSE_INK_EXTENTS
+ // and the underlying cairo font may be antialiased,
+ // we can't trust Windows to have considered all the pixels
+ // so we need to add "padding" to the bounds.
+ // (see bugs 475968, 439831, compare also bug 445087)
+ if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
+ mAntialiasOption != kAntialiasNone &&
+ GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
+ metrics.mBoundingBox.width > 0) {
+ metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
+ metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
+ }
+
+ return metrics;
+}
+
+bool
+gfxDWriteFont::ProvidesGlyphWidths() const
+{
+ return !mUseSubpixelPositions ||
+ (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
+}
+
+int32_t
+gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
+{
+ if (!mGlyphWidths) {
+ mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
+ }
+
+ int32_t width = -1;
+ if (mGlyphWidths->Get(aGID, &width)) {
+ return width;
+ }
+
+ width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0);
+ mGlyphWidths->Put(aGID, width);
+ return width;
+}
+
+already_AddRefed<GlyphRenderingOptions>
+gfxDWriteFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
+{
+ if (UsingClearType()) {
+ return Factory::CreateDWriteGlyphRenderingOptions(
+ gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ?
+ gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL));
+ } else {
+ return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()->
+ GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE));
+ }
+}
+
+bool
+gfxDWriteFont::GetForceGDIClassic()
+{
+ return static_cast<gfxDWriteFontEntry*>(mFontEntry.get())->GetForceGDIClassic() &&
+ cairo_dwrite_get_cleartype_rendering_mode() < 0 &&
+ GetAdjustedSize() <=
+ gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize();
+}
+
+DWRITE_MEASURING_MODE
+gfxDWriteFont::GetMeasuringMode()
+{
+ return GetForceGDIClassic()
+ ? DWRITE_MEASURING_MODE_GDI_CLASSIC
+ : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode();
+}
+
+gfxFloat
+gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph)
+{
+ DWRITE_GLYPH_METRICS metrics;
+ HRESULT hr;
+ if (mUseSubpixelPositions) {
+ hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
+ if (SUCCEEDED(hr)) {
+ return metrics.advanceWidth * mFUnitsConvFactor;
+ }
+ } else {
+ hr = mFontFace->GetGdiCompatibleGlyphMetrics(
+ FLOAT(mAdjustedSize), 1.0f, nullptr,
+ GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL,
+ &aGlyph, 1, &metrics, FALSE);
+ if (SUCCEEDED(hr)) {
+ return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
+ }
+ }
+ return 0;
+}
+
+void
+gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ aSizes->mFontInstances += aMallocSizeOf(mMetrics);
+ if (mGlyphWidths) {
+ aSizes->mFontInstances +=
+ mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+}
+
+void
+gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+already_AddRefed<ScaledFont>
+gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
+{
+ bool wantCairo = aTarget->GetBackendType() == BackendType::CAIRO;
+ if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) {
+ RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
+ return scaledFont.forget();
+ }
+
+ NativeFont nativeFont;
+ nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
+ nativeFont.mFont = GetFontFace();
+
+ if (wantCairo) {
+ mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
+ GetAdjustedSize(),
+ GetCairoScaledFont());
+ } else if (aTarget->GetBackendType() == BackendType::SKIA) {
+ gfxDWriteFontEntry *fe =
+ static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
+ bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
+
+ const gfxFontStyle* fontStyle = GetStyle();
+ mAzureScaledFont =
+ Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
+ GetAdjustedSize(),
+ useEmbeddedBitmap,
+ GetForceGDIClassic());
+ } else {
+ mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
+ GetAdjustedSize());
+ }
+
+ mAzureScaledFontIsCairo = wantCairo;
+
+ RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
+ return scaledFont.forget();
+}
diff --git a/system/graphics/thebes/gfxDWriteFonts.h b/system/graphics/thebes/gfxDWriteFonts.h
new file mode 100644
index 000000000..72ac7a2b5
--- /dev/null
+++ b/system/graphics/thebes/gfxDWriteFonts.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WINDOWSDWRITEFONTS_H
+#define GFX_WINDOWSDWRITEFONTS_H
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+#include <dwrite.h>
+
+#include "gfxFont.h"
+#include "gfxUserFontSet.h"
+#include "cairo-win32.h"
+
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+
+/**
+ * \brief Class representing a font face for a font entry.
+ */
+class gfxDWriteFont : public gfxFont
+{
+public:
+ gfxDWriteFont(gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold = false,
+ AntialiasOption = kAntialiasDefault);
+ ~gfxDWriteFont();
+
+ virtual gfxFont*
+ CopyWithAntialiasOption(AntialiasOption anAAOption) override;
+
+ virtual uint32_t GetSpaceGlyph() override;
+
+ virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
+
+ virtual bool AllowSubpixelAA() override
+ { return mAllowManualShowGlyphs; }
+
+ bool IsValid() const;
+
+ IDWriteFontFace *GetFontFace();
+
+ /* override Measure to add padding for antialiasing */
+ virtual RunMetrics Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget *aDrawTargetForTightBoundingBox,
+ Spacing *aSpacing,
+ uint16_t aOrientation) override;
+
+ virtual bool ProvidesGlyphWidths() const override;
+
+ virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
+ uint16_t aGID) override;
+
+ virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+ GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) override;
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+
+ virtual FontType GetType() const override { return FONT_TYPE_DWRITE; }
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFont(mozilla::gfx::DrawTarget *aTarget) override;
+
+ virtual cairo_scaled_font_t *GetCairoScaledFont() override;
+
+protected:
+ virtual const Metrics& GetHorizontalMetrics() override;
+
+ bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);
+
+ void ComputeMetrics(AntialiasOption anAAOption);
+
+ bool HasBitmapStrikeForSize(uint32_t aSize);
+
+ cairo_font_face_t *CairoFontFace();
+
+ gfxFloat MeasureGlyphWidth(uint16_t aGlyph);
+
+ DWRITE_MEASURING_MODE GetMeasuringMode();
+ bool GetForceGDIClassic();
+
+ RefPtr<IDWriteFontFace> mFontFace;
+ cairo_font_face_t *mCairoFontFace;
+
+ Metrics *mMetrics;
+
+ // cache of glyph widths in 16.16 fixed-point pixels
+ mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,int32_t>> mGlyphWidths;
+
+ uint32_t mSpaceGlyph;
+
+ bool mNeedsOblique;
+ bool mNeedsBold;
+ bool mUseSubpixelPositions;
+ bool mAllowManualShowGlyphs;
+ bool mAzureScaledFontIsCairo;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxDrawable.cpp b/system/graphics/thebes/gfxDrawable.cpp
new file mode 100644
index 000000000..7d25cc975
--- /dev/null
+++ b/system/graphics/thebes/gfxDrawable.cpp
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxDrawable.h"
+#include "gfxASurface.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#ifdef MOZ_X11
+#include "cairo.h"
+#include "gfxXlibSurface.h"
+#endif
+#include "mozilla/gfx/Logging.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
+ const IntSize aSize,
+ const gfxMatrix aTransform)
+ : gfxDrawable(aSize)
+ , mSourceSurface(aSurface)
+ , mTransform(aTransform)
+{
+ if (!mSourceSurface) {
+ gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
+ }
+}
+
+bool
+gfxSurfaceDrawable::DrawWithSamplingRect(DrawTarget* aDrawTarget,
+ CompositionOp aOp,
+ AntialiasMode aAntialiasMode,
+ const gfxRect& aFillRect,
+ const gfxRect& aSamplingRect,
+ ExtendMode aExtendMode,
+ const SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity)
+{
+ if (!mSourceSurface) {
+ return true;
+ }
+
+ // When drawing with CLAMP we can expand the sampling rect to the nearest pixel
+ // without changing the result.
+ IntRect intRect = IntRect::RoundOut(aSamplingRect.x, aSamplingRect.y,
+ aSamplingRect.width, aSamplingRect.height);
+
+ IntSize size = mSourceSurface->GetSize();
+ if (!IntRect(IntPoint(), size).Contains(intRect)) {
+ return false;
+ }
+
+ DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
+ ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
+ return true;
+}
+
+bool
+gfxSurfaceDrawable::Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ ExtendMode aExtendMode,
+ const SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity,
+ const gfxMatrix& aTransform)
+
+{
+ if (!mSourceSurface) {
+ return true;
+ }
+
+ DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
+ aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
+ aExtendMode, aSamplingFilter, aOpacity, aTransform);
+ return true;
+}
+
+void
+gfxSurfaceDrawable::DrawInternal(DrawTarget* aDrawTarget,
+ CompositionOp aOp,
+ AntialiasMode aAntialiasMode,
+ const gfxRect& aFillRect,
+ const IntRect& aSamplingRect,
+ ExtendMode aExtendMode,
+ const SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity,
+ const gfxMatrix& aTransform)
+{
+ Matrix patternTransform = ToMatrix(aTransform * mTransform);
+ patternTransform.Invert();
+
+ SurfacePattern pattern(mSourceSurface, aExtendMode,
+ patternTransform, aSamplingFilter, aSamplingRect);
+
+ Rect fillRect = ToRect(aFillRect);
+
+ if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
+ // Emulate cairo operator source which is bound by mask!
+ aDrawTarget->ClearRect(fillRect);
+ aDrawTarget->FillRect(fillRect, pattern);
+ } else {
+ aDrawTarget->FillRect(fillRect, pattern,
+ DrawOptions(aOpacity, aOp, aAntialiasMode));
+ }
+}
+
+gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
+ const IntSize aSize)
+ : gfxDrawable(aSize)
+ , mCallback(aCallback)
+{
+}
+
+already_AddRefed<gfxSurfaceDrawable>
+gfxCallbackDrawable::MakeSurfaceDrawable(const SamplingFilter aSamplingFilter)
+{
+ SurfaceFormat format =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
+ RefPtr<DrawTarget> dt =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize,
+ format);
+ if (!dt || !dt->IsValid())
+ return nullptr;
+
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(ctx); // already checked for target above
+ Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP,
+ aSamplingFilter);
+
+ RefPtr<SourceSurface> surface = dt->Snapshot();
+ if (surface) {
+ RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
+ return drawable.forget();
+ }
+ return nullptr;
+}
+
+static bool
+IsRepeatingExtendMode(ExtendMode aExtendMode)
+{
+ switch (aExtendMode) {
+ case ExtendMode::REPEAT:
+ case ExtendMode::REPEAT_X:
+ case ExtendMode::REPEAT_Y:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+gfxCallbackDrawable::Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ ExtendMode aExtendMode,
+ const SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity,
+ const gfxMatrix& aTransform)
+{
+ if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 || aContext->CurrentOp() != CompositionOp::OP_OVER) &&
+ !mSurfaceDrawable) {
+ mSurfaceDrawable = MakeSurfaceDrawable(aSamplingFilter);
+ }
+
+ if (mSurfaceDrawable)
+ return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode,
+ aSamplingFilter,
+ aOpacity, aTransform);
+
+ if (mCallback)
+ return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform);
+
+ return false;
+}
+
+gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
+ const IntSize aSize)
+ : gfxDrawable(aSize)
+ , mPattern(aPattern)
+{
+}
+
+gfxPatternDrawable::~gfxPatternDrawable()
+{
+}
+
+class DrawingCallbackFromDrawable : public gfxDrawingCallback {
+public:
+ explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
+ : mDrawable(aDrawable) {
+ NS_ASSERTION(aDrawable, "aDrawable is null!");
+ }
+
+ virtual ~DrawingCallbackFromDrawable() {}
+
+ virtual bool operator()(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ const SamplingFilter aSamplingFilter,
+ const gfxMatrix& aTransform = gfxMatrix())
+ {
+ return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP,
+ aSamplingFilter, 1.0,
+ aTransform);
+ }
+private:
+ RefPtr<gfxDrawable> mDrawable;
+};
+
+already_AddRefed<gfxCallbackDrawable>
+gfxPatternDrawable::MakeCallbackDrawable()
+{
+ RefPtr<gfxDrawingCallback> callback =
+ new DrawingCallbackFromDrawable(this);
+ RefPtr<gfxCallbackDrawable> callbackDrawable =
+ new gfxCallbackDrawable(callback, mSize);
+ return callbackDrawable.forget();
+}
+
+bool
+gfxPatternDrawable::Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ ExtendMode aExtendMode,
+ const SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity,
+ const gfxMatrix& aTransform)
+{
+ DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
+
+ if (!mPattern)
+ return false;
+
+ if (IsRepeatingExtendMode(aExtendMode)) {
+ // We can't use mPattern directly: We want our repeated tiles to have
+ // the size mSize, which might not be the case in mPattern.
+ // So we need to draw mPattern into a surface of size mSize, create
+ // a pattern from the surface and draw that pattern.
+ // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
+ // those things, so we use them here. Drawing mPattern into the surface
+ // will happen through this Draw() method with aRepeat = false.
+ RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
+ return callbackDrawable->Draw(aContext, aFillRect, aExtendMode,
+ aSamplingFilter,
+ aOpacity, aTransform);
+ }
+
+ gfxMatrix oldMatrix = mPattern->GetMatrix();
+ mPattern->SetMatrix(aTransform * oldMatrix);
+ DrawOptions drawOptions(aOpacity);
+ aDrawTarget.FillRect(ToRect(aFillRect),
+ *mPattern->GetPattern(&aDrawTarget), drawOptions);
+ mPattern->SetMatrix(oldMatrix);
+ return true;
+}
diff --git a/system/graphics/thebes/gfxDrawable.h b/system/graphics/thebes/gfxDrawable.h
new file mode 100644
index 000000000..487bd9c54
--- /dev/null
+++ b/system/graphics/thebes/gfxDrawable.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DRAWABLE_H
+#define GFX_DRAWABLE_H
+
+#include "gfxRect.h"
+#include "gfxMatrix.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Types.h"
+
+class gfxContext;
+class gfxPattern;
+
+/**
+ * gfxDrawable
+ * An Interface representing something that has an intrinsic size and can draw
+ * itself repeatedly.
+ */
+class gfxDrawable {
+ NS_INLINE_DECL_REFCOUNTING(gfxDrawable)
+public:
+ typedef mozilla::gfx::AntialiasMode AntialiasMode;
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ explicit gfxDrawable(const mozilla::gfx::IntSize aSize)
+ : mSize(aSize) {}
+
+ /**
+ * Draw into aContext filling aFillRect, possibly repeating, using aSamplingFilter.
+ * aTransform is a userspace to "image"space matrix. For example, if Draw
+ * draws using a gfxPattern, this is the matrix that should be set on the
+ * pattern prior to rendering it.
+ * @return whether drawing was successful
+ */
+ virtual bool Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0,
+ const gfxMatrix& aTransform = gfxMatrix()) = 0;
+
+ virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
+ CompositionOp aOp,
+ AntialiasMode aAntialiasMode,
+ const gfxRect& aFillRect,
+ const gfxRect& aSamplingRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0)
+ {
+ return false;
+ }
+
+ virtual mozilla::gfx::IntSize Size() { return mSize; }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxDrawable() {}
+
+ const mozilla::gfx::IntSize mSize;
+};
+
+/**
+ * gfxSurfaceDrawable
+ * A convenience implementation of gfxDrawable for surfaces.
+ */
+class gfxSurfaceDrawable : public gfxDrawable {
+public:
+ gfxSurfaceDrawable(mozilla::gfx::SourceSurface* aSurface, const mozilla::gfx::IntSize aSize,
+ const gfxMatrix aTransform = gfxMatrix());
+ virtual ~gfxSurfaceDrawable() {}
+
+ virtual bool Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0,
+ const gfxMatrix& aTransform = gfxMatrix());
+
+ virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
+ CompositionOp aOp,
+ AntialiasMode aAntialiasMode,
+ const gfxRect& aFillRect,
+ const gfxRect& aSamplingRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0);
+
+protected:
+ void DrawInternal(DrawTarget* aDrawTarget,
+ CompositionOp aOp,
+ AntialiasMode aAntialiasMode,
+ const gfxRect& aFillRect,
+ const mozilla::gfx::IntRect& aSamplingRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity,
+ const gfxMatrix& aTransform = gfxMatrix());
+
+ RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
+ const gfxMatrix mTransform;
+};
+
+/**
+ * gfxDrawingCallback
+ * A simple drawing functor.
+ */
+class gfxDrawingCallback {
+ NS_INLINE_DECL_REFCOUNTING(gfxDrawingCallback)
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxDrawingCallback() {}
+
+public:
+ /**
+ * Draw into aContext filling aFillRect using aSamplingFilter.
+ * aTransform is a userspace to "image"space matrix. For example, if Draw
+ * draws using a gfxPattern, this is the matrix that should be set on the
+ * pattern prior to rendering it.
+ * @return whether drawing was successful
+ */
+ virtual bool operator()(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ const gfxMatrix& aTransform = gfxMatrix()) = 0;
+
+};
+
+/**
+ * gfxCallbackDrawable
+ * A convenience implementation of gfxDrawable for callbacks.
+ */
+class gfxCallbackDrawable : public gfxDrawable {
+public:
+ gfxCallbackDrawable(gfxDrawingCallback* aCallback, const mozilla::gfx::IntSize aSize);
+ virtual ~gfxCallbackDrawable() {}
+
+ virtual bool Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0,
+ const gfxMatrix& aTransform = gfxMatrix());
+
+protected:
+ already_AddRefed<gfxSurfaceDrawable>
+ MakeSurfaceDrawable(mozilla::gfx::SamplingFilter aSamplingFilter =
+ mozilla::gfx::SamplingFilter::LINEAR);
+
+ RefPtr<gfxDrawingCallback> mCallback;
+ RefPtr<gfxSurfaceDrawable> mSurfaceDrawable;
+};
+
+/**
+ * gfxPatternDrawable
+ * A convenience implementation of gfxDrawable for patterns.
+ */
+class gfxPatternDrawable : public gfxDrawable {
+public:
+ gfxPatternDrawable(gfxPattern* aPattern,
+ const mozilla::gfx::IntSize aSize);
+ virtual ~gfxPatternDrawable();
+
+ virtual bool Draw(gfxContext* aContext,
+ const gfxRect& aFillRect,
+ mozilla::gfx::ExtendMode aExtendMode,
+ const mozilla::gfx::SamplingFilter aSamplingFilter,
+ gfxFloat aOpacity = 1.0,
+ const gfxMatrix& aTransform = gfxMatrix());
+
+
+protected:
+ already_AddRefed<gfxCallbackDrawable> MakeCallbackDrawable();
+
+ RefPtr<gfxPattern> mPattern;
+};
+
+#endif /* GFX_DRAWABLE_H */
diff --git a/system/graphics/thebes/gfxEnv.h b/system/graphics/thebes/gfxEnv.h
new file mode 100644
index 000000000..ff4a8063a
--- /dev/null
+++ b/system/graphics/thebes/gfxEnv.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ENV_H
+#define GFX_ENV_H
+
+#include "prenv.h"
+
+// To register the check for an environment variable existence (and not empty),
+// add a line in this file using the DECL_GFX_ENV macro.
+//
+// For example this line in the .h:
+// DECL_GFX_ENV("MOZ_DISABLE_CONTEXT_SHARING_GLX",DisableContextSharingGLX);
+// means that you can call
+// bool var = gfxEnv::DisableContextSharingGLX();
+// and that the value will be checked only once, first time we call it, then cached.
+
+#define DECL_GFX_ENV(Env, Name) \
+ static bool Name() { \
+ static bool isSet = IsEnvSet(Env);\
+ return isSet; \
+ }
+
+class gfxEnv final
+{
+public:
+ // This is where DECL_GFX_ENV for each of the environment variables should go.
+ // We will keep these in an alphabetical order by the environment variable,
+ // to make it easier to see if a method accessing an entry already exists.
+ // Just insert yours in the list.
+
+ // Debugging inside of ContainerLayerComposite
+ DECL_GFX_ENV("DUMP_DEBUG", DumpDebug);
+
+ // OpenGL shader debugging in OGLShaderProgram, in DEBUG only
+ DECL_GFX_ENV("MOZ_DEBUG_SHADERS", DebugShaders);
+
+ // Disabling context sharing in GLContextProviderGLX
+ DECL_GFX_ENV("MOZ_DISABLE_CONTEXT_SHARING_GLX", DisableContextSharingGlx);
+
+ // Disabling the crash guard in DriverCrashGuard
+ DECL_GFX_ENV("MOZ_DISABLE_CRASH_GUARD", DisableCrashGuard);
+ DECL_GFX_ENV("MOZ_FORCE_CRASH_GUARD_NIGHTLY", ForceCrashGuardNightly);
+
+ // We force present to work around some Windows bugs - disable that if this
+ // environment variable is set.
+ DECL_GFX_ENV("MOZ_DISABLE_FORCE_PRESENT", DisableForcePresent);
+
+ // Together with paint dumping, only when MOZ_DUMP_PAINTING is defined.
+ // Dumping compositor textures is broken pretty badly. For example,
+ // on Linux it crashes TextureHost::GetAsSurface() returns null.
+ // Expect to have to fix things like this if you turn it on.
+ // Meanwhile, content-side texture dumping
+ // (conditioned on DebugDumpPainting()) is a good replacement.
+ DECL_GFX_ENV("MOZ_DUMP_COMPOSITOR_TEXTURES", DumpCompositorTextures);
+
+ // Dumping the layer list in LayerSorter
+ DECL_GFX_ENV("MOZ_DUMP_LAYER_SORT_LIST", DumpLayerSortList);
+
+ // Paint dumping, only when MOZ_DUMP_PAINTING is defined.
+ DECL_GFX_ENV("MOZ_DUMP_PAINT", DumpPaint);
+ DECL_GFX_ENV("MOZ_DUMP_PAINT_INTERMEDIATE", DumpPaintIntermediate);
+ DECL_GFX_ENV("MOZ_DUMP_PAINT_ITEMS", DumpPaintItems);
+ DECL_GFX_ENV("MOZ_DUMP_PAINT_TO_FILE", DumpPaintToFile);
+
+ // Force double buffering in ContentClient
+ DECL_GFX_ENV("MOZ_FORCE_DOUBLE_BUFFERING", ForceDoubleBuffering);
+
+ // Force gfxDevCrash to use MOZ_CRASH in Beta and Release
+ DECL_GFX_ENV("MOZ_GFX_CRASH_MOZ_CRASH", GfxDevCrashMozCrash);
+
+ DECL_GFX_ENV("MOZ_GFX_VR_NO_DISTORTION", VRNoDistortion);
+
+ // Debugging in GLContext
+ DECL_GFX_ENV("MOZ_GL_DEBUG", GlDebug);
+ DECL_GFX_ENV("MOZ_GL_DEBUG_VERBOSE", GlDebugVerbose);
+ DECL_GFX_ENV("MOZ_GL_DEBUG_ABORT_ON_ERROR", GlDebugAbortOnError);
+
+ // Count GL extensions
+ DECL_GFX_ENV("MOZ_GL_DUMP_EXTS", GlDumpExtensions);
+
+ // Very noisy GLContext and GLContextProviderELG
+ DECL_GFX_ENV("MOZ_GL_SPEW", GlSpew);
+
+ // Do extra work before and after each GLX call in GLContextProviderGLX
+ DECL_GFX_ENV("MOZ_GLX_DEBUG", GlxDebug);
+
+ // Use X compositing
+ DECL_GFX_ENV("MOZ_LAYERS_ENABLE_XLIB_SURFACES", LayersEnableXlibSurfaces);
+
+ // GL compositing on Windows
+ DECL_GFX_ENV("MOZ_LAYERS_PREFER_EGL", LayersPreferEGL);
+
+ // Offscreen GL context for main layer manager
+ DECL_GFX_ENV("MOZ_LAYERS_PREFER_OFFSCREEN", LayersPreferOffscreen);
+
+ // Stop the VR rendering
+ DECL_GFX_ENV("NO_VR_RENDERING", NoVRRendering);
+
+ // WARNING:
+ // Please make sure that you've added your new envvar to the list above in
+ // alphabetical order. Please do not just append it to the end of the list.
+
+private:
+ // Helper function, can be re-used in the other macros
+ static bool IsEnvSet(const char* aName) {
+ const char* val = PR_GetEnv(aName);
+ return (val != 0 && *val != '\0');
+ }
+
+ gfxEnv() {};
+ ~gfxEnv() {};
+ gfxEnv(const gfxEnv&) = delete;
+ gfxEnv& operator=(const gfxEnv&) = delete;
+};
+
+#undef DECL_GFX_ENV
+
+#endif /* GFX_ENV_H */
diff --git a/system/graphics/thebes/gfxFT2FontBase.cpp b/system/graphics/thebes/gfxFT2FontBase.cpp
new file mode 100644
index 000000000..241f28f63
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2FontBase.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFT2FontBase.h"
+#include "gfxFT2Utils.h"
+#include "harfbuzz/hb.h"
+#include "mozilla/Likely.h"
+#include "gfxFontConstants.h"
+#include "gfxFontUtils.h"
+
+using namespace mozilla::gfx;
+
+gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle)
+ : gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
+ mSpaceGlyph(0),
+ mHasMetrics(false)
+{
+ cairo_scaled_font_reference(mScaledFont);
+ gfxFT2LockedFace face(this);
+ mFUnitsConvFactor = face.XScale();
+}
+
+gfxFT2FontBase::~gfxFT2FontBase()
+{
+ cairo_scaled_font_destroy(mScaledFont);
+}
+
+uint32_t
+gfxFT2FontBase::GetGlyph(uint32_t aCharCode)
+{
+ // FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
+ // through all the postscript glyph names in the font. Therefore use a
+ // lightweight cache, which is stored on the cairo_font_face_t.
+
+ cairo_font_face_t *face =
+ cairo_scaled_font_get_font_face(CairoScaledFont());
+
+ if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
+ return 0;
+
+ // This cache algorithm and size is based on what is done in
+ // cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
+ // think the concept is that adjacent characters probably come mostly from
+ // one Unicode block. This assumption is probably not so valid with
+ // scripts with large character sets as used for East Asian languages.
+
+ struct CmapCacheSlot {
+ uint32_t mCharCode;
+ uint32_t mGlyphIndex;
+ };
+ const uint32_t kNumSlots = 256;
+ static cairo_user_data_key_t sCmapCacheKey;
+
+ CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
+ (cairo_font_face_get_user_data(face, &sCmapCacheKey));
+
+ if (!slots) {
+ // cairo's caches can keep some cairo_font_faces alive past our last
+ // destroy, so the destroy function (free) for the cache must be
+ // callable from cairo without any assumptions about what other
+ // modules have not been shutdown.
+ slots = static_cast<CmapCacheSlot*>
+ (calloc(kNumSlots, sizeof(CmapCacheSlot)));
+ if (!slots)
+ return 0;
+
+ cairo_status_t status =
+ cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
+ if (status != CAIRO_STATUS_SUCCESS) { // OOM
+ free(slots);
+ return 0;
+ }
+
+ // Invalidate slot 0 by setting its char code to something that would
+ // never end up in slot 0. All other slots are already invalid
+ // because they have mCharCode = 0 and a glyph for char code 0 will
+ // always be in the slot 0.
+ slots[0].mCharCode = 1;
+ }
+
+ CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
+ if (slot->mCharCode != aCharCode) {
+ slot->mCharCode = aCharCode;
+ slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode);
+ }
+
+ return slot->mGlyphIndex;
+}
+
+void
+gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents)
+{
+ NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL");
+
+ cairo_glyph_t glyphs[1];
+ glyphs[0].index = aGlyph;
+ glyphs[0].x = 0.0;
+ glyphs[0].y = 0.0;
+ // cairo does some caching for us here but perhaps a small gain could be
+ // made by caching more. It is usually only the advance that is needed,
+ // so caching only the advance could allow many requests to be cached with
+ // little memory use. Ideally this cache would be merged with
+ // gfxGlyphExtents.
+ cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
+}
+
+const gfxFont::Metrics&
+gfxFT2FontBase::GetHorizontalMetrics()
+{
+ if (mHasMetrics)
+ return mMetrics;
+
+ if (MOZ_UNLIKELY(GetStyle()->size <= 0.0) ||
+ MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) {
+ new(&mMetrics) gfxFont::Metrics(); // zero initialize
+ mSpaceGlyph = GetGlyph(' ');
+ } else {
+ gfxFT2LockedFace face(this);
+ face.GetMetrics(&mMetrics, &mSpaceGlyph);
+ }
+
+ SanitizeMetrics(&mMetrics, false);
+
+#if 0
+ // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
+ // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
+
+ fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
+ fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
+ fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
+ fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
+ fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
+ fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
+#endif
+
+ mHasMetrics = true;
+ return mMetrics;
+}
+
+// Get the glyphID of a space
+uint32_t
+gfxFT2FontBase::GetSpaceGlyph()
+{
+ GetHorizontalMetrics();
+ return mSpaceGlyph;
+}
+
+uint32_t
+gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector)
+{
+ if (variation_selector) {
+ uint32_t id =
+ gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector);
+ if (id) {
+ return id;
+ }
+ unicode = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
+ if (unicode) {
+ return GetGlyph(unicode);
+ }
+ return 0;
+ }
+
+ return GetGlyph(unicode);
+}
+
+int32_t
+gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
+{
+ cairo_text_extents_t extents;
+ GetGlyphExtents(aGID, &extents);
+ // convert to 16.16 fixed point
+ return NS_lround(0x10000 * extents.x_advance);
+}
+
+bool
+gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
+{
+ // The scaled font ctm is not relevant right here because
+ // cairo_set_scaled_font does not record the scaled font itself, but
+ // merely the font_face, font_matrix, font_options. The scaled_font used
+ // for the target can be different from the scaled_font passed to
+ // cairo_set_scaled_font. (Unfortunately we have measured only for an
+ // identity ctm.)
+ cairo_scaled_font_t *cairoFont = CairoScaledFont();
+
+ if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
+ // Don't cairo_set_scaled_font as that would propagate the error to
+ // the cairo_t, precluding any further drawing.
+ return false;
+ }
+ // Thoughts on which font_options to set on the context:
+ //
+ // cairoFont has been created for screen rendering.
+ //
+ // When the context is being used for screen rendering, we should set
+ // font_options such that the same scaled_font gets used (when the ctm is
+ // the same). The use of explicit font_options recorded in
+ // CreateScaledFont ensures that this will happen.
+ //
+ // XXXkt: For pdf and ps surfaces, I don't know whether it's better to
+ // remove surface-specific options, or try to draw with the same
+ // scaled_font that was used to measure. As the same font_face is being
+ // used, its font_options will often override some values anyway (unless
+ // perhaps we remove those from the FcPattern at face creation).
+ //
+ // I can't see any significant difference in printing, irrespective of
+ // what is set here. It's too late to change things here as measuring has
+ // already taken place. We should really be measuring with a different
+ // font for pdf and ps surfaces (bug 403513).
+ cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
+ return true;
+}
diff --git a/system/graphics/thebes/gfxFT2FontBase.h b/system/graphics/thebes/gfxFT2FontBase.h
new file mode 100644
index 000000000..498c74ff9
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2FontBase.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FT2FONTBASE_H
+#define GFX_FT2FONTBASE_H
+
+#include "cairo.h"
+#include "gfxContext.h"
+#include "gfxFont.h"
+#include "mozilla/gfx/2D.h"
+
+class gfxFT2FontBase : public gfxFont {
+public:
+ gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle);
+ virtual ~gfxFT2FontBase();
+
+ uint32_t GetGlyph(uint32_t aCharCode);
+ void GetGlyphExtents(uint32_t aGlyph,
+ cairo_text_extents_t* aExtents);
+ virtual uint32_t GetSpaceGlyph() override;
+ virtual bool ProvidesGetGlyph() const override { return true; }
+ virtual uint32_t GetGlyph(uint32_t unicode,
+ uint32_t variation_selector) override;
+ virtual bool ProvidesGlyphWidths() const override { return true; }
+ virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
+ uint16_t aGID) override;
+
+ cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
+ virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
+
+ virtual FontType GetType() const override { return FONT_TYPE_FT2; }
+
+protected:
+ virtual const Metrics& GetHorizontalMetrics() override;
+
+ uint32_t mSpaceGlyph;
+ bool mHasMetrics;
+ Metrics mMetrics;
+};
+
+#endif /* GFX_FT2FONTBASE_H */
diff --git a/system/graphics/thebes/gfxFT2FontList.cpp b/system/graphics/thebes/gfxFT2FontList.cpp
new file mode 100644
index 000000000..b032be13d
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2FontList.cpp
@@ -0,0 +1,1598 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Base64.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "gfxAndroidPlatform.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsIInputStream.h"
+#define gfxToolkitPlatform gfxAndroidPlatform
+
+#include "nsXULAppAPI.h"
+#include <dirent.h>
+#include <android/log.h>
+#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
+
+#include "ft2build.h"
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_TABLES_H
+#include "cairo-ft.h"
+
+#include "gfxFT2FontList.h"
+#include "gfxFT2Fonts.h"
+#include "gfxUserFontSet.h"
+#include "gfxFontUtils.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsIObserverService.h"
+#include "nsTArray.h"
+#include "nsUnicharUtils.h"
+#include "nsCRT.h"
+
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIMemory.h"
+#include "gfxFontConstants.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/scache/StartupCache.h"
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+using namespace mozilla;
+
+static LazyLogModule sFontInfoLog("fontInfoLog");
+
+#undef LOG
+#define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
+
+static cairo_user_data_key_t sFTUserFontDataKey;
+
+static __inline void
+BuildKeyNameFromFontName(nsAString &aName)
+{
+ ToLowerCase(aName);
+}
+
+// Helper to access the FT_Face for a given FT2FontEntry,
+// creating a temporary face if the entry does not have one yet.
+// This allows us to read font names, tables, etc if necessary
+// without permanently instantiating a freetype face and consuming
+// memory long-term.
+// This may fail (resulting in a null FT_Face), e.g. if it fails to
+// allocate memory to uncompress a font from omnijar.
+class AutoFTFace {
+public:
+ AutoFTFace(FT2FontEntry* aFontEntry)
+ : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
+ {
+ if (aFontEntry->mFTFace) {
+ mFace = aFontEntry->mFTFace;
+ return;
+ }
+
+ NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
+ "can't use AutoFTFace for fonts without a filename");
+ FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary();
+
+ // A relative path (no initial "/") means this is a resource in
+ // omnijar, not an installed font on the device.
+ // The NS_ASSERTIONs here should never fail, as the resource must have
+ // been read successfully during font-list initialization or we'd never
+ // have created the font entry. The only legitimate runtime failure
+ // here would be memory allocation, in which case mFace remains null.
+ if (aFontEntry->mFilename[0] != '/') {
+ RefPtr<nsZipArchive> reader =
+ Omnijar::GetReader(Omnijar::Type::GRE);
+ nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
+ NS_ASSERTION(item, "failed to find zip entry");
+
+ uint32_t bufSize = item->RealSize();
+ mFontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
+ if (mFontDataBuf) {
+ nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
+ cursor.Copy(&bufSize);
+ NS_ASSERTION(bufSize == item->RealSize(),
+ "error reading bundled font");
+
+ if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
+ aFontEntry->mFTFontIndex,
+ &mFace)) {
+ NS_WARNING("failed to create freetype face");
+ }
+ }
+ } else {
+ if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
+ aFontEntry->mFTFontIndex, &mFace)) {
+ NS_WARNING("failed to create freetype face");
+ }
+ }
+ if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
+ NS_WARNING("failed to select Unicode charmap");
+ }
+ mOwnsFace = true;
+ }
+
+ ~AutoFTFace() {
+ if (mFace && mOwnsFace) {
+ FT_Done_Face(mFace);
+ if (mFontDataBuf) {
+ free(mFontDataBuf);
+ }
+ }
+ }
+
+ operator FT_Face() { return mFace; }
+
+ // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
+ // we do -not- free the mFontDataBuf (if used); that also becomes the
+ // responsibility of the new owner of the face.
+ FT_Face forget() {
+ NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
+ mOwnsFace = false;
+ return mFace;
+ }
+
+ const uint8_t* FontData() const { return mFontDataBuf; }
+
+private:
+ FT_Face mFace;
+ uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
+ // or null for fonts instantiated from a file.
+ // If non-null, this must survive as long as the
+ // FT_Face.
+ bool mOwnsFace;
+};
+
+/*
+ * FT2FontEntry
+ * gfxFontEntry subclass corresponding to a specific face that can be
+ * rendered by freetype. This is associated with a face index in a
+ * file (normally a .ttf/.otf file holding a single face, but in principle
+ * there could be .ttc files with multiple faces).
+ * The FT2FontEntry can create the necessary FT_Face on demand, and can
+ * then create a Cairo font_face and scaled_font for drawing.
+ */
+
+cairo_scaled_font_t *
+FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
+{
+ cairo_font_face_t *cairoFace = CairoFontFace();
+ if (!cairoFace) {
+ return nullptr;
+ }
+
+ cairo_scaled_font_t *scaledFont = nullptr;
+
+ cairo_matrix_t sizeMatrix;
+ cairo_matrix_t identityMatrix;
+
+ // XXX deal with adjusted size
+ cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
+ cairo_matrix_init_identity(&identityMatrix);
+
+ // synthetic oblique by skewing via the font matrix
+ bool needsOblique = IsUpright() &&
+ aStyle->style != NS_FONT_STYLE_NORMAL &&
+ aStyle->allowSyntheticStyle;
+
+ if (needsOblique) {
+ cairo_matrix_t style;
+ cairo_matrix_init(&style,
+ 1, //xx
+ 0, //yx
+ -1 * OBLIQUE_SKEW_FACTOR, //xy
+ 1, //yy
+ 0, //x0
+ 0); //y0
+ cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
+ }
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
+ cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
+ }
+
+ scaledFont = cairo_scaled_font_create(cairoFace,
+ &sizeMatrix,
+ &identityMatrix, fontOptions);
+ cairo_font_options_destroy(fontOptions);
+
+ NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
+ "Failed to make scaled font");
+
+ return scaledFont;
+}
+
+FT2FontEntry::~FT2FontEntry()
+{
+ // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
+ mFTFace = nullptr;
+
+#ifndef ANDROID
+ if (mFontFace) {
+ cairo_font_face_destroy(mFontFace);
+ mFontFace = nullptr;
+ }
+#endif
+}
+
+gfxFont*
+FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
+{
+ cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
+ if (!scaledFont) {
+ return nullptr;
+ }
+ gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
+ cairo_scaled_font_destroy(scaledFont);
+ return font;
+}
+
+/* static */
+FT2FontEntry*
+FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ // Ownership of aFontData is passed in here; the fontEntry must
+ // retain it as long as the FT_Face needs it, and ensure it is
+ // eventually deleted.
+ FT_Face face;
+ FT_Error error =
+ FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
+ aFontData, aLength, 0, &face);
+ if (error != FT_Err_Ok) {
+ free((void*)aFontData);
+ return nullptr;
+ }
+ if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
+ FT_Done_Face(face);
+ free((void*)aFontData);
+ return nullptr;
+ }
+ // Create our FT2FontEntry, which inherits the name of the userfont entry
+ // as it's not guaranteed that the face has valid names (bug 737315)
+ FT2FontEntry* fe =
+ FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
+ aFontData);
+ if (fe) {
+ fe->mStyle = aStyle;
+ fe->mWeight = aWeight;
+ fe->mStretch = aStretch;
+ fe->mIsDataUserFont = true;
+ }
+ return fe;
+}
+
+class FTUserFontData {
+public:
+ FTUserFontData(FT_Face aFace, const uint8_t* aData)
+ : mFace(aFace), mFontData(aData)
+ {
+ }
+
+ ~FTUserFontData()
+ {
+ FT_Done_Face(mFace);
+ if (mFontData) {
+ free((void*)mFontData);
+ }
+ }
+
+ const uint8_t *FontData() const { return mFontData; }
+
+private:
+ FT_Face mFace;
+ const uint8_t *mFontData;
+};
+
+static void
+FTFontDestroyFunc(void *data)
+{
+ FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
+ delete userFontData;
+}
+
+/* static */
+FT2FontEntry*
+FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
+{
+ FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
+ fe->mFilename = aFLE.filepath();
+ fe->mFTFontIndex = aFLE.index();
+ fe->mWeight = aFLE.weight();
+ fe->mStretch = aFLE.stretch();
+ fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+ return fe;
+}
+
+// Helpers to extract font entry properties from an FT_Face
+static bool
+FTFaceIsItalic(FT_Face aFace)
+{
+ return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
+}
+
+static uint16_t
+FTFaceGetWeight(FT_Face aFace)
+{
+ TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
+ uint16_t os2weight = 0;
+ if (os2 && os2->version != 0xffff) {
+ // Technically, only 100 to 900 are valid, but some fonts
+ // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
+ // it set to 6 instead of 600. We try to be nice and handle that
+ // as well.
+ if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
+ os2weight = os2->usWeightClass;
+ } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
+ os2weight = os2->usWeightClass * 100;
+ }
+ }
+
+ uint16_t result;
+ if (os2weight != 0) {
+ result = os2weight;
+ } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
+ result = 700;
+ } else {
+ result = 400;
+ }
+
+ NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
+
+ return result;
+}
+
+// Used to create the font entry for installed faces on the device,
+// when iterating over the fonts directories.
+// We use the FT_Face to retrieve the details needed for the font entry,
+// but unless we have been passed font data (i.e. for a user font),
+// we do *not* save a reference to it, nor create a cairo face,
+// as we don't want to keep a freetype face for every installed font
+// permanently in memory.
+/* static */
+FT2FontEntry*
+FT2FontEntry::CreateFontEntry(FT_Face aFace,
+ const char* aFilename, uint8_t aIndex,
+ const nsAString& aName,
+ const uint8_t* aFontData)
+{
+ FT2FontEntry *fe = new FT2FontEntry(aName);
+ fe->mStyle = (FTFaceIsItalic(aFace) ?
+ NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+ fe->mWeight = FTFaceGetWeight(aFace);
+ fe->mFilename = aFilename;
+ fe->mFTFontIndex = aIndex;
+
+ if (aFontData) {
+ fe->mFTFace = aFace;
+ int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
+ FT_LOAD_DEFAULT :
+ (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
+ fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
+ FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
+ cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
+ userFontData, FTFontDestroyFunc);
+ }
+
+ return fe;
+}
+
+// construct font entry name for an installed font from names in the FT_Face,
+// and then create our FT2FontEntry
+static FT2FontEntry*
+CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
+{
+ if (!aFace->family_name) {
+ return nullptr;
+ }
+ nsAutoString fontName;
+ AppendUTF8toUTF16(aFace->family_name, fontName);
+ if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
+ fontName.Append(' ');
+ AppendUTF8toUTF16(aFace->style_name, fontName);
+ }
+ return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
+}
+
+FT2FontEntry*
+gfxFT2Font::GetFontEntry()
+{
+ return static_cast<FT2FontEntry*> (mFontEntry.get());
+}
+
+cairo_font_face_t *
+FT2FontEntry::CairoFontFace()
+{
+ if (!mFontFace) {
+ AutoFTFace face(this);
+ if (!face) {
+ return nullptr;
+ }
+ mFTFace = face.forget();
+ int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
+ FT_LOAD_DEFAULT :
+ (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
+ mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
+ FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
+ cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
+ userFontData, FTFontDestroyFunc);
+ }
+ return mFontFace;
+}
+
+// Copied/modified from similar code in gfxMacPlatformFontList.mm:
+// Complex scripts will not render correctly unless Graphite or OT
+// layout tables are present.
+// For OpenType, we also check that the GSUB table supports the relevant
+// script tag, to avoid using things like Arial Unicode MS for Lao (it has
+// the characters, but lacks OpenType support).
+
+// TODO: consider whether we should move this to gfxFontEntry and do similar
+// cmap-masking on all platforms to avoid using fonts that won't shape
+// properly.
+
+nsresult
+FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+
+ AutoTArray<uint8_t, 16384> buffer;
+ nsresult rv = CopyFontTable(TTAG_cmap, buffer);
+
+ if (NS_SUCCEEDED(rv)) {
+ bool unicodeFont;
+ bool symbolFont;
+ rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
+ *charmap, mUVSOffset,
+ unicodeFont, symbolFont);
+ }
+
+ if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
+ // We assume a Graphite font knows what it's doing,
+ // and provides whatever shaping is needed for the
+ // characters it supports, so only check/clear the
+ // complex-script ranges for non-Graphite fonts
+
+ // for layout support, check for the presence of opentype layout tables
+ bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
+
+ for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
+ sr->rangeStart; sr++) {
+ // check to see if the cmap includes complex script codepoints
+ if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
+ // We check for GSUB here, as GPOS alone would not be ok.
+ if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
+ continue;
+ }
+ charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
+ }
+ }
+ }
+
+ mHasCmapTable = NS_SUCCEEDED(rv);
+ if (mHasCmapTable) {
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ mCharacterMap = pfl->FindCharMap(charmap);
+ } else {
+ // if error occurred, initialize to null cmap
+ mCharacterMap = new gfxCharacterMap();
+ }
+ return rv;
+}
+
+nsresult
+FT2FontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
+{
+ AutoFTFace face(this);
+ if (!face) {
+ return NS_ERROR_FAILURE;
+ }
+
+ FT_Error status;
+ FT_ULong len = 0;
+ status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
+ if (status != FT_Err_Ok || len == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aBuffer.SetLength(len, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint8_t *buf = aBuffer.Elements();
+ status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
+ NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+hb_blob_t*
+FT2FontEntry::GetFontTable(uint32_t aTableTag)
+{
+ if (mFontFace) {
+ // if there's a cairo font face, we may be able to return a blob
+ // that just wraps a range of the attached user font data
+ FTUserFontData *userFontData = static_cast<FTUserFontData*>(
+ cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
+ if (userFontData && userFontData->FontData()) {
+ return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
+ aTableTag);
+ }
+ }
+
+ // otherwise, use the default method (which in turn will call our
+ // implementation of CopyFontTable)
+ return gfxFontEntry::GetFontTable(aTableTag);
+}
+
+void
+FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ aSizes->mFontListSize +=
+ mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+void
+FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+/*
+ * FT2FontFamily
+ * A standard gfxFontFamily; just adds a method used to support sending
+ * the font list from chrome to content via IPC.
+ */
+
+void
+FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
+ Visibility aVisibility)
+{
+ for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
+ const FT2FontEntry *fe =
+ static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
+ if (!fe) {
+ continue;
+ }
+
+ aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
+ fe->mFilename,
+ fe->Weight(), fe->Stretch(),
+ fe->mStyle,
+ fe->mFTFontIndex,
+ aVisibility == kHidden));
+ }
+}
+
+/*
+ * Startup cache support for the font list:
+ * We store the list of families and faces, with their style attributes and the
+ * corresponding font files, in the startup cache.
+ * This allows us to recreate the gfxFT2FontList collection of families and
+ * faces without instantiating Freetype faces for each font file (in order to
+ * find their attributes), leading to significantly quicker startup.
+ */
+
+#define CACHE_KEY "font.cached-list"
+
+class FontNameCache {
+public:
+ // Creates the object but does NOT load the cached data from the startup
+ // cache; call Init() after creation to do that.
+ FontNameCache()
+ : mMap(&mOps, sizeof(FNCMapEntry), 0)
+ , mWriteNeeded(false)
+ {
+ // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
+ // it to |mMap|'s constructor. A more normal approach here would be to
+ // have a static |sOps| member. Unfortunately, this mysteriously but
+ // consistently makes Fennec start-up slower, so we take this
+ // unorthodox approach instead. It's safe because PLDHashTable's
+ // constructor doesn't dereference the pointer; it just makes a copy of
+ // it.
+ mOps = (PLDHashTableOps) {
+ StringHash,
+ HashMatchEntry,
+ MoveEntry,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+ };
+
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "FontNameCache should only be used in chrome process");
+ mCache = mozilla::scache::StartupCache::GetSingleton();
+ }
+
+ ~FontNameCache()
+ {
+ if (!mWriteNeeded || !mCache) {
+ return;
+ }
+
+ nsAutoCString buf;
+ for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<FNCMapEntry*>(iter.Get());
+ if (!entry->mFileExists) {
+ // skip writing entries for files that are no longer present
+ continue;
+ }
+ buf.Append(entry->mFilename);
+ buf.Append(';');
+ buf.Append(entry->mFaces);
+ buf.Append(';');
+ buf.AppendInt(entry->mTimestamp);
+ buf.Append(';');
+ buf.AppendInt(entry->mFilesize);
+ buf.Append(';');
+ }
+ mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
+ }
+
+ // This may be called more than once (if we re-load the font list).
+ void Init()
+ {
+ if (!mCache) {
+ return;
+ }
+
+ uint32_t size;
+ UniquePtr<char[]> buf;
+ if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
+ return;
+ }
+
+ LOG(("got: %s from the cache", nsDependentCString(buf.get(), size).get()));
+
+ mMap.Clear();
+ mWriteNeeded = false;
+
+ const char* beginning = buf.get();
+ const char* end = strchr(beginning, ';');
+ while (end) {
+ nsCString filename(beginning, end - beginning);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ';'))) {
+ break;
+ }
+ nsCString faceList(beginning, end - beginning);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ';'))) {
+ break;
+ }
+ uint32_t timestamp = strtoul(beginning, nullptr, 10);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ';'))) {
+ break;
+ }
+ uint32_t filesize = strtoul(beginning, nullptr, 10);
+
+ auto mapEntry =
+ static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible));
+ if (mapEntry) {
+ mapEntry->mFilename.Assign(filename);
+ mapEntry->mTimestamp = timestamp;
+ mapEntry->mFilesize = filesize;
+ mapEntry->mFaces.Assign(faceList);
+ // entries from the startupcache are marked "non-existing"
+ // until we have confirmed that the file still exists
+ mapEntry->mFileExists = false;
+ }
+
+ beginning = end + 1;
+ end = strchr(beginning, ';');
+ }
+ }
+
+ void
+ GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
+ uint32_t *aTimestamp, uint32_t *aFilesize)
+ {
+ auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
+ if (entry) {
+ *aTimestamp = entry->mTimestamp;
+ *aFilesize = entry->mFilesize;
+ aFaceList.Assign(entry->mFaces);
+ // this entry does correspond to an existing file
+ // (although it might not be up-to-date, in which case
+ // it will get overwritten via CacheFileInfo)
+ entry->mFileExists = true;
+ }
+ }
+
+ void
+ CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
+ uint32_t aTimestamp, uint32_t aFilesize)
+ {
+ auto entry =
+ static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
+ if (entry) {
+ entry->mFilename.Assign(aFileName);
+ entry->mTimestamp = aTimestamp;
+ entry->mFilesize = aFilesize;
+ entry->mFaces.Assign(aFaceList);
+ entry->mFileExists = true;
+ }
+ mWriteNeeded = true;
+ }
+
+private:
+ mozilla::scache::StartupCache* mCache;
+ PLDHashTable mMap;
+ bool mWriteNeeded;
+
+ PLDHashTableOps mOps;
+
+ typedef struct : public PLDHashEntryHdr {
+ public:
+ nsCString mFilename;
+ uint32_t mTimestamp;
+ uint32_t mFilesize;
+ nsCString mFaces;
+ bool mFileExists;
+ } FNCMapEntry;
+
+ static PLDHashNumber StringHash(const void *key)
+ {
+ return HashString(reinterpret_cast<const char*>(key));
+ }
+
+ static bool HashMatchEntry(const PLDHashEntryHdr *aHdr, const void *key)
+ {
+ const FNCMapEntry* entry =
+ static_cast<const FNCMapEntry*>(aHdr);
+ return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
+ }
+
+ static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
+ PLDHashEntryHdr *aTo)
+ {
+ FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
+ const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
+ to->mFilename.Assign(from->mFilename);
+ to->mTimestamp = from->mTimestamp;
+ to->mFilesize = from->mFilesize;
+ to->mFaces.Assign(from->mFaces);
+ to->mFileExists = from->mFileExists;
+ }
+};
+
+/***************************************************************
+ *
+ * gfxFT2FontList
+ *
+ */
+
+// For Mobile, we use gfxFT2Fonts, and we build the font list by directly
+// scanning the system's Fonts directory for OpenType and TrueType files.
+
+#define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
+
+class WillShutdownObserver : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit WillShutdownObserver(gfxFT2FontList* aFontList)
+ : mFontList(aFontList)
+ { }
+
+ void Remove()
+ {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+ }
+ mFontList = nullptr;
+ }
+
+protected:
+ virtual ~WillShutdownObserver()
+ { }
+
+ gfxFT2FontList *mFontList;
+};
+
+NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+WillShutdownObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
+ mFontList->WillShutdown();
+ } else {
+ NS_NOTREACHED("unexpected notification topic");
+ }
+ return NS_OK;
+}
+
+gfxFT2FontList::gfxFT2FontList()
+ : mJarModifiedTime(0)
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ mObserver = new WillShutdownObserver(this);
+ obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+ }
+}
+
+gfxFT2FontList::~gfxFT2FontList()
+{
+ if (mObserver) {
+ mObserver->Remove();
+ }
+}
+
+void
+gfxFT2FontList::AppendFacesFromCachedFaceList(
+ const nsCString& aFileName,
+ const nsCString& aFaceList,
+ StandardFile aStdFile,
+ FT2FontFamily::Visibility aVisibility)
+{
+ const char *beginning = aFaceList.get();
+ const char *end = strchr(beginning, ',');
+ while (end) {
+ NS_ConvertUTF8toUTF16 familyName(beginning, end - beginning);
+ ToLowerCase(familyName);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ','))) {
+ break;
+ }
+ NS_ConvertUTF8toUTF16 faceName(beginning, end - beginning);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ','))) {
+ break;
+ }
+ uint32_t index = strtoul(beginning, nullptr, 10);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ','))) {
+ break;
+ }
+ bool italic = (*beginning != '0');
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ','))) {
+ break;
+ }
+ uint32_t weight = strtoul(beginning, nullptr, 10);
+ beginning = end + 1;
+ if (!(end = strchr(beginning, ','))) {
+ break;
+ }
+ int32_t stretch = strtol(beginning, nullptr, 10);
+
+ FontListEntry fle(familyName, faceName, aFileName,
+ weight, stretch, italic, index,
+ aVisibility == FT2FontFamily::kHidden);
+ AppendFaceFromFontListEntry(fle, aStdFile);
+
+ beginning = end + 1;
+ end = strchr(beginning, ',');
+ }
+}
+
+static void
+AppendToFaceList(nsCString& aFaceList,
+ nsAString& aFamilyName, FT2FontEntry* aFontEntry)
+{
+ aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
+ aFaceList.Append(',');
+ aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
+ aFaceList.Append(',');
+ aFaceList.AppendInt(aFontEntry->mFTFontIndex);
+ aFaceList.Append(',');
+ aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
+ aFaceList.Append(',');
+ aFaceList.AppendInt(aFontEntry->Weight());
+ aFaceList.Append(',');
+ aFaceList.AppendInt(aFontEntry->Stretch());
+ aFaceList.Append(',');
+}
+
+void
+FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
+{
+ // note if the family is in the "bad underline" blacklist
+ if (aFamily->IsBadUnderlineFamily()) {
+ mIsBadUnderlineFont = true;
+ }
+
+ // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
+ // because of unwanted on-by-default "ae" ligature.
+ // (See also AppendFaceFromFontListEntry.)
+ if (aFamily->Name().EqualsLiteral("roboto")) {
+ mIgnoreGSUB = true;
+ }
+
+ // bug 706888 - set the IgnoreGSUB flag on the broken version of
+ // Droid Sans Arabic from certain phones, as identified by the
+ // font checksum in the 'head' table
+ else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
+ AutoFTFace face(this);
+ if (face) {
+ const TT_Header *head = static_cast<const TT_Header*>
+ (FT_Get_Sfnt_Table(face, ft_sfnt_head));
+ if (head && head->CheckSum_Adjust == 0xe445242) {
+ mIgnoreGSUB = true;
+ }
+ }
+ }
+}
+
+void
+gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
+ FontNameCache *aCache,
+ StandardFile aStdFile,
+ FT2FontFamily::Visibility aVisibility)
+{
+ nsCString cachedFaceList;
+ uint32_t filesize = 0, timestamp = 0;
+ if (aCache) {
+ aCache->GetInfoForFile(aFileName, cachedFaceList, &timestamp, &filesize);
+ }
+
+ struct stat s;
+ int statRetval = stat(aFileName.get(), &s);
+ if (!cachedFaceList.IsEmpty() && 0 == statRetval &&
+ s.st_mtime == timestamp && s.st_size == filesize)
+ {
+ LOG(("using cached font info for %s", aFileName.get()));
+ AppendFacesFromCachedFaceList(aFileName, cachedFaceList, aStdFile,
+ aVisibility);
+ return;
+ }
+
+ FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
+ FT_Face dummy;
+ if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
+ LOG(("reading font info via FreeType for %s", aFileName.get()));
+ nsCString newFaceList;
+ timestamp = s.st_mtime;
+ filesize = s.st_size;
+ for (FT_Long i = 0; i < dummy->num_faces; i++) {
+ FT_Face face;
+ if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
+ continue;
+ }
+ AddFaceToList(aFileName, i, aStdFile, aVisibility, face, newFaceList);
+ FT_Done_Face(face);
+ }
+ FT_Done_Face(dummy);
+ if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
+ aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
+ }
+ }
+}
+
+void
+gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
+{
+ bool jarChanged = false;
+
+ mozilla::scache::StartupCache* cache =
+ mozilla::scache::StartupCache::GetSingleton();
+ UniquePtr<char[]> cachedModifiedTimeBuf;
+ uint32_t longSize;
+ if (cache &&
+ NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
+ &cachedModifiedTimeBuf,
+ &longSize)) &&
+ longSize == sizeof(int64_t))
+ {
+ nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
+ jarFile->GetLastModifiedTime(&mJarModifiedTime);
+ if (mJarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
+ jarChanged = true;
+ }
+ }
+
+ static const char* sJarSearchPaths[] = {
+ "res/fonts/*.ttf$",
+ };
+ RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
+ for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
+ nsZipFind* find;
+ if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
+ const char* path;
+ uint16_t len;
+ while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
+ nsCString entryName(path, len);
+ AppendFacesFromOmnijarEntry(reader, entryName, aCache,
+ jarChanged);
+ }
+ delete find;
+ }
+ }
+}
+
+// Given the freetype face corresponding to an entryName and face index,
+// add the face to the available font list and to the faceList string
+void
+gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
+ StandardFile aStdFile,
+ FT2FontFamily::Visibility aVisibility,
+ FT_Face aFace,
+ nsCString& aFaceList)
+{
+ if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
+ // ignore faces that don't support a Unicode charmap
+ return;
+ }
+
+ // build the font entry name and create an FT2FontEntry,
+ // but do -not- keep a reference to the FT_Face
+ RefPtr<FT2FontEntry> fe =
+ CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
+
+ auto& fontFamilies =
+ (aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
+ mFontFamilies;
+
+ if (fe) {
+ NS_ConvertUTF8toUTF16 name(aFace->family_name);
+ BuildKeyNameFromFontName(name);
+ RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
+ if (!family) {
+ family = new FT2FontFamily(name);
+ fontFamilies.Put(name, family);
+ if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
+ family->SetSkipSpaceFeatureCheck(true);
+ }
+ if (mBadUnderlineFamilyNames.Contains(name)) {
+ family->SetBadUnderlineFamily();
+ }
+ }
+ fe->mStandardFace = (aStdFile == kStandard);
+ family->AddFontEntry(fe);
+
+ fe->CheckForBrokenFont(family);
+
+ AppendToFaceList(aFaceList, name, fe);
+ if (LOG_ENABLED()) {
+ LOG(("(fontinit) added (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d",
+ NS_ConvertUTF16toUTF8(fe->Name()).get(),
+ NS_ConvertUTF16toUTF8(family->Name()).get(),
+ fe->IsItalic() ? "italic" : "normal",
+ fe->Weight(), fe->Stretch()));
+ }
+ }
+}
+
+void
+gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
+ const nsCString& aEntryName,
+ FontNameCache *aCache,
+ bool aJarChanged)
+{
+ nsCString faceList;
+ if (aCache && !aJarChanged) {
+ uint32_t filesize, timestamp;
+ aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
+ if (faceList.Length() > 0) {
+ AppendFacesFromCachedFaceList(aEntryName, faceList);
+ return;
+ }
+ }
+
+ nsZipItem *item = aArchive->GetItem(aEntryName.get());
+ NS_ASSERTION(item, "failed to find zip entry");
+
+ uint32_t bufSize = item->RealSize();
+ // We use fallible allocation here; if there's not enough RAM, we'll simply
+ // ignore the bundled fonts and fall back to the device's installed fonts.
+ auto buf = MakeUniqueFallible<uint8_t[]>(bufSize);
+ if (!buf) {
+ return;
+ }
+
+ nsZipCursor cursor(item, aArchive, buf.get(), bufSize);
+ uint8_t* data = cursor.Copy(&bufSize);
+ NS_ASSERTION(data && bufSize == item->RealSize(),
+ "error reading bundled font");
+ if (!data) {
+ return;
+ }
+
+ FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
+
+ FT_Face dummy;
+ if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, 0, &dummy)) {
+ return;
+ }
+
+ for (FT_Long i = 0; i < dummy->num_faces; i++) {
+ FT_Face face;
+ if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, i, &face)) {
+ continue;
+ }
+ AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
+ face, faceList);
+ FT_Done_Face(face);
+ }
+
+ FT_Done_Face(dummy);
+
+ if (aCache && !faceList.IsEmpty()) {
+ aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
+ }
+}
+
+// Called on each family after all fonts are added to the list;
+// this will sort faces to give priority to "standard" font files
+// if aUserArg is non-null (i.e. we're using it as a boolean flag)
+static void
+FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
+ RefPtr<gfxFontFamily>& aFamily,
+ bool aSortFaces)
+{
+ gfxFontFamily *family = aFamily.get();
+
+ family->SetHasStyles(true);
+
+ if (aSortFaces) {
+ family->SortAvailableFonts();
+ }
+ family->CheckForSimpleFamily();
+}
+
+void
+gfxFT2FontList::FindFonts()
+{
+ gfxFontCache *fc = gfxFontCache::GetCache();
+ if (fc)
+ fc->AgeAllGenerations();
+ ClearLangGroupPrefFonts();
+ mCodepointsWithNoFonts.reset();
+
+ mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
+ mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
+
+ if (!XRE_IsParentProcess()) {
+ // Content process: ask the Chrome process to give us the list
+ InfallibleTArray<FontListEntry> fonts;
+ mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
+ for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
+ // We don't need to identify "standard" font files here,
+ // as the faces are already sorted.
+ AppendFaceFromFontListEntry(fonts[i], kUnknown);
+ }
+ // Passing null for userdata tells Finalize that it does not need
+ // to sort faces (because they were already sorted by chrome,
+ // so we just maintain the existing order)
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ FinalizeFamilyMemberList(key, family, /* aSortFaces */ false);
+ }
+ for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ FinalizeFamilyMemberList(key, family, /* aSortFaces */ false );
+ }
+
+ LOG(("got font list from chrome process: %d faces in %d families "
+ "and %d in hidden families",
+ fonts.Length(), mFontFamilies.Count(),
+ mHiddenFontFamilies.Count()));
+ return;
+ }
+
+ // Chrome process: get the cached list (if any)
+ if (!mFontNameCache) {
+ mFontNameCache = MakeUnique<FontNameCache>();
+ }
+ mFontNameCache->Init();
+
+ // ANDROID_ROOT is the root of the android system, typically /system;
+ // font files are in /$ANDROID_ROOT/fonts/
+ nsCString root;
+ char *androidRoot = PR_GetEnv("ANDROID_ROOT");
+ if (androidRoot) {
+ root = androidRoot;
+ } else {
+ root = NS_LITERAL_CSTRING("/system");
+ }
+ root.AppendLiteral("/fonts");
+
+ FindFontsInDir(root, mFontNameCache.get(), FT2FontFamily::kVisible);
+
+ if (mFontFamilies.Count() == 0) {
+ // if we can't find/read the font directory, we are doomed!
+ NS_RUNTIMEABORT("Could not read the system fonts directory");
+ }
+
+ // Look for fonts stored in omnijar, unless we're on a low-memory
+ // device where we don't want to spend the RAM to decompress them.
+ // (Prefs may disable this, or force-enable it even with low memory.)
+ bool lowmem;
+ nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
+ if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
+ Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
+ Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
+ FindFontsInOmnijar(mFontNameCache.get());
+ }
+
+ // Look for downloaded fonts in a profile-agnostic "fonts" directory.
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+ if (dirSvc) {
+ nsCOMPtr<nsIFile> appDir;
+ nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile), getter_AddRefs(appDir));
+ if (NS_SUCCEEDED(rv)) {
+ appDir->AppendNative(NS_LITERAL_CSTRING("fonts"));
+ nsCString localPath;
+ if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
+ FindFontsInDir(localPath, mFontNameCache.get(),
+ FT2FontFamily::kVisible);
+ }
+ }
+ }
+
+ // look for locally-added fonts in a "fonts" subdir of the profile
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
+ getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv) &&
+ NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ nsCString localPath;
+ rv = localDir->GetNativePath(localPath);
+ if (NS_SUCCEEDED(rv)) {
+ FindFontsInDir(localPath, mFontNameCache.get(),
+ FT2FontFamily::kVisible);
+ }
+ }
+
+ // Finalize the families by sorting faces into standard order
+ // and marking "simple" families.
+ // Passing non-null userData here says that we want faces to be sorted.
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
+ }
+ for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
+ }
+}
+
+void
+gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
+ FontNameCache *aFNC,
+ FT2FontFamily::Visibility aVisibility)
+{
+ static const char* sStandardFonts[] = {
+ "DroidSans.ttf",
+ "DroidSans-Bold.ttf",
+ "DroidSerif-Regular.ttf",
+ "DroidSerif-Bold.ttf",
+ "DroidSerif-Italic.ttf",
+ "DroidSerif-BoldItalic.ttf",
+ "DroidSansMono.ttf",
+ "DroidSansArabic.ttf",
+ "DroidSansHebrew.ttf",
+ "DroidSansThai.ttf",
+ "MTLmr3m.ttf",
+ "MTLc3m.ttf",
+ "NanumGothic.ttf",
+ "DroidSansJapanese.ttf",
+ "DroidSansFallback.ttf"
+ };
+
+ DIR *d = opendir(aDir.get());
+ if (!d) {
+ return;
+ }
+
+ struct dirent *ent = nullptr;
+ while ((ent = readdir(d)) != nullptr) {
+ const char *ext = strrchr(ent->d_name, '.');
+ if (!ext) {
+ continue;
+ }
+ if (strcasecmp(ext, ".ttf") == 0 ||
+ strcasecmp(ext, ".otf") == 0 ||
+ strcasecmp(ext, ".woff") == 0 ||
+ strcasecmp(ext, ".ttc") == 0) {
+ bool isStdFont = false;
+ for (unsigned int i = 0;
+ i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
+ isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
+ }
+
+ nsCString s(aDir);
+ s.Append('/');
+ s.Append(ent->d_name);
+
+ // Add the face(s) from this file to our font list;
+ // note that if we have cached info for this file in fnc,
+ // and the file is unchanged, we won't actually need to read it.
+ // If the file is new/changed, this will update the FontNameCache.
+ AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown,
+ aVisibility);
+ }
+ }
+
+ closedir(d);
+}
+
+void
+gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
+ StandardFile aStdFile)
+{
+ FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
+ if (fe) {
+ auto& fontFamilies =
+ aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
+ fe->mStandardFace = (aStdFile == kStandard);
+ nsAutoString name(aFLE.familyName());
+ RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
+ if (!family) {
+ family = new FT2FontFamily(name);
+ fontFamilies.Put(name, family);
+ if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
+ family->SetSkipSpaceFeatureCheck(true);
+ }
+ if (mBadUnderlineFamilyNames.Contains(name)) {
+ family->SetBadUnderlineFamily();
+ }
+ }
+ family->AddFontEntry(fe);
+
+ fe->CheckForBrokenFont(family);
+ }
+}
+
+void
+gfxFT2FontList::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ auto family = static_cast<FT2FontFamily*>(iter.Data().get());
+ family->AddFacesToFontList(retValue, FT2FontFamily::kVisible);
+ }
+ for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ auto family = static_cast<FT2FontFamily*>(iter.Data().get());
+ family->AddFacesToFontList(retValue, FT2FontFamily::kHidden);
+ }
+}
+
+static void
+LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
+{
+ AutoTArray<nsString, 5> skiplist;
+ gfxFontUtils::GetPrefsFontList(
+ "font.whitelist.skip_default_features_space_check",
+ skiplist);
+ uint32_t numFonts = skiplist.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ ToLowerCase(skiplist[i]);
+ aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
+ }
+}
+
+void
+PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
+ RefPtr<gfxFontFamily>& aFamily)
+{
+ gfxFontFamily *family = aFamily.get();
+
+ auto& faces = family->GetFontList();
+ size_t count = faces.Length();
+ for (size_t i = 0; i < count; ++i) {
+ FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
+ if (fe->mFTFontIndex != 0) {
+ NS_NOTREACHED("don't try to preload a multi-face font");
+ continue;
+ }
+
+ // XXX Should we move the i/o here off the main thread?
+
+ // Map the font data in fe->mFilename, so we can calculate its CRC32.
+ int fd = open(fe->mFilename.get(), O_RDONLY);
+ if (fd < 0) {
+ continue;
+ }
+ struct stat buf;
+ if (fstat(fd, &buf) != 0 || buf.st_size < 12) {
+ close(fd);
+ continue;
+ }
+ char* data = static_cast<char*>(
+ mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
+ close(fd);
+ if (data == MAP_FAILED) {
+ continue;
+ }
+
+ // Calculate CRC32
+ uint32_t crc = crc32(0, nullptr, 0);
+ crc = crc32(crc, (Bytef*)data, buf.st_size);
+ munmap(data, buf.st_size);
+
+#if 0
+ ALOG("\n**** Preloading family [%s] face [%s] CRC32 [0x%08x]",
+ NS_ConvertUTF16toUTF8(family->Name()).get(),
+ fe->mFilename.get(),
+ crc);
+#endif
+
+ fe->mUserFontData = MakeUnique<gfxUserFontData>();
+ fe->mUserFontData->mRealName = fe->Name();
+ fe->mUserFontData->mCRC32 = crc;
+ fe->mUserFontData->mLength = buf.st_size;
+
+ // Stash it persistently in the user-font cache.
+ gfxUserFontSet::UserFontCache::CacheFont(
+ fe, gfxUserFontSet::UserFontCache::kPersistent);
+ }
+}
+
+nsresult
+gfxFT2FontList::InitFontListForPlatform()
+{
+ // reset hidden font list
+ mHiddenFontFamilies.Clear();
+
+ LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
+
+ FindFonts();
+
+ for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ PreloadAsUserFontFaces(key, family);
+ }
+ return NS_OK;
+}
+
+// called for each family name, based on the assumption that the
+// first part of the full name is the family name
+
+gfxFontEntry*
+gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ // walk over list of names
+ FT2FontEntry* fontEntry = nullptr;
+ nsString fullName(aFontName);
+
+ // Note that we only check mFontFamilies here, not mHiddenFontFamilies;
+ // hence @font-face { src:local(...) } will not find hidden fonts.
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ // Check family name, based on the assumption that the
+ // first part of the full name is the family name
+ RefPtr<gfxFontFamily>& fontFamily = iter.Data();
+
+ // does the family name match up to the length of the family name?
+ const nsString& family = fontFamily->Name();
+ nsString fullNameFamily;
+
+ fullName.Left(fullNameFamily, family.Length());
+
+ // if so, iterate over faces in this family to see if there is a match
+ if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
+ nsTArray<RefPtr<gfxFontEntry> >& fontList = fontFamily->GetFontList();
+ int index, len = fontList.Length();
+ for (index = 0; index < len; index++) {
+ gfxFontEntry* fe = fontList[index];
+ if (!fe) {
+ continue;
+ }
+ if (fe->Name().Equals(fullName,
+ nsCaseInsensitiveStringComparator())) {
+ fontEntry = static_cast<FT2FontEntry*>(fe);
+ goto searchDone;
+ }
+ }
+ }
+ }
+
+searchDone:
+ if (!fontEntry) {
+ return nullptr;
+ }
+
+ // Clone the font entry so that we can then set its style descriptors
+ // from the userfont entry rather than the actual font.
+
+ // Ensure existence of mFTFace in the original entry
+ fontEntry->CairoFontFace();
+ if (!fontEntry->mFTFace) {
+ return nullptr;
+ }
+
+ FT2FontEntry* fe =
+ FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
+ fontEntry->mFilename.get(),
+ fontEntry->mFTFontIndex,
+ fontEntry->Name(), nullptr);
+ if (fe) {
+ fe->mStyle = aStyle;
+ fe->mWeight = aWeight;
+ fe->mStretch = aStretch;
+ fe->mIsLocalUserFont = true;
+ }
+
+ return fe;
+}
+
+gfxFontFamily*
+gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
+{
+ gfxFontFamily *ff = nullptr;
+ /* TODO: what about Qt or other platforms that may use this? */
+ return ff;
+}
+
+gfxFontEntry*
+gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ // The FT2 font needs the font data to persist, so we do NOT free it here
+ // but instead pass ownership to the font entry.
+ // Deallocation will happen later, when the font face is destroyed.
+ return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
+ aStyle, aFontData, aLength);
+}
+
+void
+gfxFT2FontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFamilyArray.AppendElement(family);
+ }
+ for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFamilyArray.AppendElement(family);
+ }
+}
+
+void
+gfxFT2FontList::WillShutdown()
+{
+ mozilla::scache::StartupCache* cache =
+ mozilla::scache::StartupCache::GetSingleton();
+ if (cache && mJarModifiedTime > 0) {
+ cache->PutBuffer(JAR_LAST_MODIFED_TIME,
+ (char*)&mJarModifiedTime, sizeof(mJarModifiedTime));
+ }
+ mFontNameCache = nullptr;
+}
diff --git a/system/graphics/thebes/gfxFT2FontList.h b/system/graphics/thebes/gfxFT2FontList.h
new file mode 100644
index 000000000..9fb566c15
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2FontList.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FT2FONTLIST_H
+#define GFX_FT2FONTLIST_H
+
+#include "mozilla/MemoryReporting.h"
+#include "gfxPlatformFontList.h"
+
+namespace mozilla {
+ namespace dom {
+ class FontListEntry;
+ };
+};
+using mozilla::dom::FontListEntry;
+
+class FontNameCache;
+typedef struct FT_FaceRec_* FT_Face;
+class nsZipArchive;
+class WillShutdownObserver;
+
+class FT2FontEntry : public gfxFontEntry
+{
+public:
+ FT2FontEntry(const nsAString& aFaceName) :
+ gfxFontEntry(aFaceName),
+ mFTFace(nullptr),
+ mFontFace(nullptr),
+ mFTFontIndex(0)
+ {
+ }
+
+ ~FT2FontEntry();
+
+ const nsString& GetName() const {
+ return Name();
+ }
+
+ // create a font entry for a downloaded font
+ static FT2FontEntry*
+ CreateFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+ // create a font entry representing an installed font, identified by
+ // a FontListEntry; the freetype and cairo faces will not be instantiated
+ // until actually needed
+ static FT2FontEntry*
+ CreateFontEntry(const FontListEntry& aFLE);
+
+ // Create a font entry for a given freetype face; if it is an installed font,
+ // also record the filename and index.
+ // aFontData (if non-nullptr) is NS_Malloc'ed data that aFace depends on,
+ // to be freed after the face is destroyed
+ static FT2FontEntry*
+ CreateFontEntry(FT_Face aFace,
+ const char *aFilename, uint8_t aIndex,
+ const nsAString& aName,
+ const uint8_t* aFontData = nullptr);
+
+ virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
+ bool aNeedsBold) override;
+
+ // Create (if necessary) and return the cairo_font_face for this font.
+ // This may fail and return null, so caller must be prepared to handle this.
+ cairo_font_face_t *CairoFontFace();
+
+ // Create a cairo_scaled_font for this face, with the given style.
+ // This may fail and return null, so caller must be prepared to handle this.
+ cairo_scaled_font_t *CreateScaledFont(const gfxFontStyle *aStyle);
+
+ nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
+
+ virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
+
+ virtual nsresult CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) override;
+
+ // Check for various kinds of brokenness, and set flags on the entry
+ // accordingly so that we avoid using bad font tables
+ void CheckForBrokenFont(gfxFontFamily *aFamily);
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const override;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const override;
+
+ FT_Face mFTFace;
+ cairo_font_face_t *mFontFace;
+
+ nsCString mFilename;
+ uint8_t mFTFontIndex;
+};
+
+class FT2FontFamily : public gfxFontFamily
+{
+public:
+ // Flags to indicate whether a font should be "visible" in the global
+ // font list (available for use in font-family), or "hidden" (available
+ // only to support a matching data: URI used in @font-face).
+ typedef enum {
+ kVisible,
+ kHidden
+ } Visibility;
+
+ FT2FontFamily(const nsAString& aName) :
+ gfxFontFamily(aName) { }
+
+ // Append this family's faces to the IPC fontlist
+ void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
+ Visibility aVisibility);
+};
+
+class gfxFT2FontList : public gfxPlatformFontList
+{
+public:
+ gfxFT2FontList();
+ virtual ~gfxFT2FontList();
+
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+ void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
+
+ static gfxFT2FontList* PlatformFontList() {
+ return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
+ }
+
+ virtual void GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray);
+
+ void WillShutdown();
+
+protected:
+ typedef enum {
+ kUnknown,
+ kStandard
+ } StandardFile;
+
+ // initialize font lists
+ virtual nsresult InitFontListForPlatform() override;
+
+ void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
+ StandardFile aStdFile);
+
+ void AppendFacesFromFontFile(const nsCString& aFileName,
+ FontNameCache *aCache,
+ StandardFile aStdFile,
+ FT2FontFamily::Visibility aVisibility);
+
+ void AppendFacesFromOmnijarEntry(nsZipArchive *aReader,
+ const nsCString& aEntryName,
+ FontNameCache *aCache,
+ bool aJarChanged);
+
+ // the defaults here are suitable for reading bundled fonts from omnijar
+ void AppendFacesFromCachedFaceList(const nsCString& aFileName,
+ const nsCString& aFaceList,
+ StandardFile aStdFile = kStandard,
+ FT2FontFamily::Visibility aVisibility =
+ FT2FontFamily::kVisible);
+
+ void AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
+ StandardFile aStdFile,
+ FT2FontFamily::Visibility aVisibility,
+ FT_Face aFace, nsCString& aFaceList);
+
+ void FindFonts();
+
+ void FindFontsInOmnijar(FontNameCache *aCache);
+
+ void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
+ FT2FontFamily::Visibility aVisibility);
+
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
+ nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
+
+private:
+ FontFamilyTable mHiddenFontFamilies;
+
+ mozilla::UniquePtr<FontNameCache> mFontNameCache;
+ int64_t mJarModifiedTime;
+ RefPtr<WillShutdownObserver> mObserver;
+};
+
+#endif /* GFX_FT2FONTLIST_H */
diff --git a/system/graphics/thebes/gfxFT2Fonts.cpp b/system/graphics/thebes/gfxFT2Fonts.cpp
new file mode 100644
index 000000000..190254191
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2Fonts.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(MOZ_WIDGET_GTK)
+#include "gfxPlatformGtk.h"
+#define gfxToolkitPlatform gfxPlatformGtk
+#elif defined(XP_WIN)
+#include "gfxWindowsPlatform.h"
+#define gfxToolkitPlatform gfxWindowsPlatform
+#elif defined(ANDROID)
+#include "gfxAndroidPlatform.h"
+#define gfxToolkitPlatform gfxAndroidPlatform
+#endif
+
+#include "gfxTypes.h"
+#include "gfxFT2Fonts.h"
+#include "gfxFT2FontBase.h"
+#include "gfxFT2Utils.h"
+#include "gfxFT2FontList.h"
+#include "gfxTextRun.h"
+#include <locale.h>
+#include "nsGkAtoms.h"
+#include "nsTArray.h"
+#include "nsUnicodeRange.h"
+#include "nsCRT.h"
+#include "nsXULAppAPI.h"
+
+#include "mozilla/Logging.h"
+#include "prinit.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/gfx/2D.h"
+
+/**
+ * gfxFT2Font
+ */
+
+bool
+gfxFT2Font::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ if (!gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
+ aVertical, aShapedText)) {
+ // harfbuzz must have failed(?!), just render raw glyphs
+ AddRange(aText, aOffset, aLength, aShapedText);
+ PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
+ aVertical, aShapedText);
+ }
+
+ return true;
+}
+
+void
+gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
+ uint32_t aLength, gfxShapedText *aShapedText)
+{
+ const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+ // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
+ gfxFT2LockedFace faceLock(this);
+ FT_Face face = faceLock.get();
+
+ gfxShapedText::CompressedGlyph *charGlyphs =
+ aShapedText->GetCharacterGlyphs();
+
+ const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
+
+ FT_UInt spaceGlyph = GetSpaceGlyph();
+
+ for (uint32_t i = 0; i < aLength; i++, aOffset++) {
+ char16_t ch = aText[i];
+
+ if (ch == 0) {
+ // treat this null byte as a missing glyph, don't create a glyph for it
+ aShapedText->SetMissingGlyph(aOffset, 0, this);
+ continue;
+ }
+
+ NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected");
+
+ if (cgdNext) {
+ cgd = cgdNext;
+ cgdNext = nullptr;
+ } else {
+ cgd = GetGlyphDataForChar(ch);
+ }
+
+ FT_UInt gid = cgd->glyphIndex;
+ int32_t advance = 0;
+
+ if (gid == 0) {
+ advance = -1; // trigger the missing glyphs case below
+ } else {
+ // find next character and its glyph -- in case they exist
+ // and exist in the current font face -- to compute kerning
+ char16_t chNext = 0;
+ FT_UInt gidNext = 0;
+ FT_Pos lsbDeltaNext = 0;
+
+ if (FT_HAS_KERNING(face) && i + 1 < aLength) {
+ chNext = aText[i + 1];
+ if (chNext != 0) {
+ cgdNext = GetGlyphDataForChar(chNext);
+ gidNext = cgdNext->glyphIndex;
+ if (gidNext && gidNext != spaceGlyph)
+ lsbDeltaNext = cgdNext->lsbDelta;
+ }
+ }
+
+ advance = cgd->xAdvance;
+
+ // now add kerning to the current glyph's advance
+ if (chNext && gidNext) {
+ FT_Vector kerning; kerning.x = 0;
+ FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning);
+ advance += kerning.x;
+ if (cgd->rsbDelta - lsbDeltaNext >= 32) {
+ advance -= 64;
+ } else if (cgd->rsbDelta - lsbDeltaNext < -32) {
+ advance += 64;
+ }
+ }
+
+ // convert 26.6 fixed point to app units
+ // round rather than truncate to nearest pixel
+ // because these advances are often scaled
+ advance = ((advance * appUnitsPerDevUnit + 32) >> 6);
+ }
+
+ if (advance >= 0 &&
+ gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+ gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
+ charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
+ } else if (gid == 0) {
+ // gid = 0 only happens when the glyph is missing from the font
+ aShapedText->SetMissingGlyph(aOffset, ch, this);
+ } else {
+ gfxTextRun::DetailedGlyph details;
+ details.mGlyphID = gid;
+ NS_ASSERTION(details.mGlyphID == gid,
+ "Seriously weird glyph ID detected!");
+ details.mAdvance = advance;
+ details.mXOffset = 0;
+ details.mYOffset = 0;
+ gfxShapedText::CompressedGlyph g;
+ g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
+ aShapedText->SetGlyphs(aOffset, g, &details);
+ }
+ }
+}
+
+gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
+ FT2FontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold)
+ : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
+ , mCharGlyphCache(32)
+{
+ NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
+ mApplySyntheticBold = aNeedsBold;
+}
+
+gfxFT2Font::~gfxFT2Font()
+{
+}
+
+void
+gfxFT2Font::FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd)
+{
+ gfxFT2LockedFace faceLock(this);
+ FT_Face face = faceLock.get();
+
+ if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE) {
+ FT_Select_Charmap(face, FT_ENCODING_UNICODE);
+ }
+ FT_UInt gid = FT_Get_Char_Index(face, ch);
+
+ if (gid == 0) {
+ // this font doesn't support this char!
+ NS_ASSERTION(gid != 0, "We don't have a glyph, but font indicated that it supported this char in tables?");
+ gd->glyphIndex = 0;
+ return;
+ }
+
+ FT_Int32 flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
+ FT_LOAD_DEFAULT :
+ (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
+ FT_Error err = FT_Load_Glyph(face, gid, flags);
+
+ if (err) {
+ // hmm, this is weird, we failed to load a glyph that we had?
+ NS_WARNING("Failed to load glyph that we got from Get_Char_index");
+
+ gd->glyphIndex = 0;
+ return;
+ }
+
+ gd->glyphIndex = gid;
+ gd->lsbDelta = face->glyph->lsb_delta;
+ gd->rsbDelta = face->glyph->rsb_delta;
+ gd->xAdvance = face->glyph->advance.x;
+}
+
+void
+gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ aSizes->mFontInstances +=
+ mCharGlyphCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+void
+gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
diff --git a/system/graphics/thebes/gfxFT2Fonts.h b/system/graphics/thebes/gfxFT2Fonts.h
new file mode 100644
index 000000000..8e4691c06
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2Fonts.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FT2FONTS_H
+#define GFX_FT2FONTS_H
+
+#include "mozilla/MemoryReporting.h"
+#include "cairo.h"
+#include "gfxTypes.h"
+#include "gfxFont.h"
+#include "gfxFT2FontBase.h"
+#include "gfxContext.h"
+#include "gfxFontUtils.h"
+#include "gfxUserFontSet.h"
+
+class FT2FontEntry;
+
+class gfxFT2Font : public gfxFT2FontBase {
+public: // new functions
+ gfxFT2Font(cairo_scaled_font_t *aCairoFont,
+ FT2FontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold);
+ virtual ~gfxFT2Font ();
+
+ FT2FontEntry *GetFontEntry();
+
+ struct CachedGlyphData {
+ CachedGlyphData()
+ : glyphIndex(0xffffffffU) { }
+
+ CachedGlyphData(uint32_t gid)
+ : glyphIndex(gid) { }
+
+ uint32_t glyphIndex;
+ int32_t lsbDelta;
+ int32_t rsbDelta;
+ int32_t xAdvance;
+ };
+
+ const CachedGlyphData* GetGlyphDataForChar(uint32_t ch) {
+ CharGlyphMapEntryType *entry = mCharGlyphCache.PutEntry(ch);
+
+ if (!entry)
+ return nullptr;
+
+ if (entry->mData.glyphIndex == 0xffffffffU) {
+ // this is a new entry, fill it
+ FillGlyphDataForChar(ch, &entry->mData);
+ }
+
+ return &entry->mData;
+ }
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+
+protected:
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText) override;
+
+ void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);
+
+ void AddRange(const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ gfxShapedText *aShapedText);
+
+ typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
+ typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;
+ CharGlyphMap mCharGlyphCache;
+};
+
+#endif /* GFX_FT2FONTS_H */
+
diff --git a/system/graphics/thebes/gfxFT2Utils.cpp b/system/graphics/thebes/gfxFT2Utils.cpp
new file mode 100644
index 000000000..77e1c9632
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2Utils.cpp
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFT2FontBase.h"
+#include "gfxFT2Utils.h"
+#include "mozilla/Likely.h"
+#include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_TABLES_H
+#include <algorithm>
+
+#ifndef FT_FACE_FLAG_COLOR
+#define FT_FACE_FLAG_COLOR ( 1L << 14 )
+#endif
+
+#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
+#include <fontconfig/fcfreetype.h>
+#endif
+
+#include "prlink.h"
+
+// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
+static inline FT_Long
+ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
+{
+ FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
+ return ROUND_26_6_TO_INT(fixed26dot6);
+}
+
+// Snap a line to pixels while keeping the center and size of the line as
+// close to the original position as possible.
+//
+// Pango does similar snapping for underline and strikethrough when fonts are
+// hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
+// top and size of lines. Optimizing the distance between the line and
+// baseline is probably good for the gap between text and underline, but
+// optimizing the center of the line is better for positioning strikethough.
+static void
+SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
+{
+ gfxFloat snappedSize = std::max(floor(aSize + 0.5), 1.0);
+ // Correct offset for change in size
+ gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
+ // Snap offset
+ aOffset = floor(offset + 0.5);
+ aSize = snappedSize;
+}
+
+void
+gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
+ uint32_t* aSpaceGlyph)
+{
+ NS_PRECONDITION(aMetrics != nullptr, "aMetrics must not be NULL");
+ NS_PRECONDITION(aSpaceGlyph != nullptr, "aSpaceGlyph must not be NULL");
+
+ if (MOZ_UNLIKELY(!mFace)) {
+ // No face. This unfortunate situation might happen if the font
+ // file is (re)moved at the wrong time.
+ const gfxFloat emHeight = mGfxFont->GetAdjustedSize();
+ aMetrics->emHeight = emHeight;
+ aMetrics->maxAscent = aMetrics->emAscent = 0.8 * emHeight;
+ aMetrics->maxDescent = aMetrics->emDescent = 0.2 * emHeight;
+ aMetrics->maxHeight = emHeight;
+ aMetrics->internalLeading = 0.0;
+ aMetrics->externalLeading = 0.2 * emHeight;
+ const gfxFloat spaceWidth = 0.5 * emHeight;
+ aMetrics->spaceWidth = spaceWidth;
+ aMetrics->maxAdvance = spaceWidth;
+ aMetrics->aveCharWidth = spaceWidth;
+ aMetrics->zeroOrAveCharWidth = spaceWidth;
+ const gfxFloat xHeight = 0.5 * emHeight;
+ aMetrics->xHeight = xHeight;
+ aMetrics->capHeight = aMetrics->maxAscent;
+ const gfxFloat underlineSize = emHeight / 14.0;
+ aMetrics->underlineSize = underlineSize;
+ aMetrics->underlineOffset = -underlineSize;
+ aMetrics->strikeoutOffset = 0.25 * emHeight;
+ aMetrics->strikeoutSize = underlineSize;
+
+ *aSpaceGlyph = 0;
+ return;
+ }
+
+ const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
+
+ aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
+ aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
+ aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
+ gfxFloat lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
+
+ gfxFloat emHeight;
+ // Scale for vertical design metric conversion: pixels per design unit.
+ // If this remains at 0.0, we can't use metrics from OS/2 etc.
+ gfxFloat yScale = 0.0;
+ if (FT_IS_SCALABLE(mFace)) {
+ // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
+ // have subpixel accuracy.
+ //
+ // 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, so that the result may be
+ // interpreted as pixels in 26.6 fixed point format.
+ yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
+ emHeight = mFace->units_per_EM * yScale;
+ } else { // Not scalable.
+ emHeight = ftMetrics.y_ppem;
+ // FT_Face doc says units_per_EM and a bunch of following fields
+ // are "only relevant to scalable outlines". If it's an sfnt,
+ // we can get units_per_EM from the 'head' table instead; otherwise,
+ // we don't have a unitsPerEm value so we can't compute/use yScale.
+ const TT_Header* head =
+ static_cast<TT_Header*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_head));
+ if (head) {
+ // Even if the font is not explicitly scalable, if the face has
+ // color bitmaps, it should be treated as scalable and scaled to
+ // the desired size. Metrics based on y_ppem need to be rescaled
+ // for the adjusted size. This makes metrics agree with the
+ // scales we pass to Cairo for Fontconfig fonts.
+ if (mFace->face_flags & FT_FACE_FLAG_COLOR) {
+ emHeight = mGfxFont->GetAdjustedSize();
+ gfxFloat adjustScale = emHeight / ftMetrics.y_ppem;
+ aMetrics->maxAscent *= adjustScale;
+ aMetrics->maxDescent *= adjustScale;
+ aMetrics->maxAdvance *= adjustScale;
+ lineHeight *= adjustScale;
+ }
+ gfxFloat emUnit = head->Units_Per_EM;
+ yScale = emHeight / emUnit;
+ }
+ }
+
+ TT_OS2 *os2 =
+ static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
+
+ if (os2 && os2->sTypoAscender && yScale > 0.0) {
+ aMetrics->emAscent = os2->sTypoAscender * yScale;
+ aMetrics->emDescent = -os2->sTypoDescender * yScale;
+ FT_Short typoHeight =
+ os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
+ lineHeight = typoHeight * yScale;
+
+ // If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
+ // set maxAscent/Descent from the sTypo* fields instead of hhea.
+ const uint16_t kUseTypoMetricsMask = 1 << 7;
+ if (os2->fsSelection & kUseTypoMetricsMask) {
+ aMetrics->maxAscent = NS_round(aMetrics->emAscent);
+ aMetrics->maxDescent = NS_round(aMetrics->emDescent);
+ } else {
+ // maxAscent/maxDescent get used for frame heights, and some fonts
+ // don't have the HHEA table ascent/descent set (bug 279032).
+ // We use NS_round here to parallel the pixel-rounded values that
+ // freetype gives us for ftMetrics.ascender/descender.
+ aMetrics->maxAscent =
+ std::max(aMetrics->maxAscent, NS_round(aMetrics->emAscent));
+ aMetrics->maxDescent =
+ std::max(aMetrics->maxDescent, NS_round(aMetrics->emDescent));
+ }
+ } else {
+ aMetrics->emAscent = aMetrics->maxAscent;
+ aMetrics->emDescent = aMetrics->maxDescent;
+ }
+
+ cairo_text_extents_t extents;
+ *aSpaceGlyph = GetCharExtents(' ', &extents);
+ if (*aSpaceGlyph) {
+ aMetrics->spaceWidth = extents.x_advance;
+ } else {
+ aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
+ }
+
+ aMetrics->zeroOrAveCharWidth = 0.0;
+ if (GetCharExtents('0', &extents)) {
+ aMetrics->zeroOrAveCharWidth = extents.x_advance;
+ }
+
+ // Prefering a measured x over sxHeight because sxHeight doesn't consider
+ // hinting, but maybe the x extents are not quite right in some fancy
+ // script fonts. CSS 2.1 suggests possibly using the height of an "o",
+ // which would have a more consistent glyph across fonts.
+ if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
+ aMetrics->xHeight = -extents.y_bearing;
+ aMetrics->aveCharWidth = extents.x_advance;
+ } else {
+ if (os2 && os2->sxHeight && yScale > 0.0) {
+ aMetrics->xHeight = os2->sxHeight * yScale;
+ } else {
+ // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
+ // impossible or impractical to determine the x-height, a value of
+ // 0.5em should be used."
+ aMetrics->xHeight = 0.5 * emHeight;
+ }
+ aMetrics->aveCharWidth = 0.0; // updated below
+ }
+
+ if (GetCharExtents('H', &extents) && extents.y_bearing < 0.0) {
+ aMetrics->capHeight = -extents.y_bearing;
+ } else {
+ if (os2 && os2->sCapHeight && yScale > 0.0) {
+ aMetrics->capHeight = os2->sCapHeight * yScale;
+ } else {
+ aMetrics->capHeight = aMetrics->maxAscent;
+ }
+ }
+
+ // aveCharWidth is used for the width of text input elements so be
+ // liberal rather than conservative in the estimate.
+ if (os2 && os2->xAvgCharWidth) {
+ // Round to pixels as this is compared with maxAdvance to guess
+ // whether this is a fixed width font.
+ gfxFloat avgCharWidth =
+ ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
+ aMetrics->aveCharWidth =
+ std::max(aMetrics->aveCharWidth, avgCharWidth);
+ }
+ aMetrics->aveCharWidth =
+ std::max(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
+ if (aMetrics->aveCharWidth == 0.0) {
+ aMetrics->aveCharWidth = aMetrics->spaceWidth;
+ }
+ if (aMetrics->zeroOrAveCharWidth == 0.0) {
+ aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
+ }
+ // Apparently hinting can mean that max_advance is not always accurate.
+ aMetrics->maxAdvance =
+ std::max(aMetrics->maxAdvance, aMetrics->aveCharWidth);
+
+ // gfxFont::Metrics::underlineOffset is the position of the top of the
+ // underline.
+ //
+ // FT_FaceRec documentation describes underline_position as "the
+ // center of the underlining stem". This was the original definition
+ // of the PostScript metric, but in the PostScript table of OpenType
+ // fonts the metric is "the top of the underline"
+ // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
+ // (up to version 2.3.7) doesn't make any adjustment.
+ //
+ // Therefore get the underline position directly from the table
+ // ourselves when this table exists. Use FreeType's metrics for
+ // other (including older PostScript) fonts.
+ if (mFace->underline_position && mFace->underline_thickness && yScale > 0.0) {
+ aMetrics->underlineSize = mFace->underline_thickness * yScale;
+ TT_Postscript *post = static_cast<TT_Postscript*>
+ (FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
+ if (post && post->underlinePosition) {
+ aMetrics->underlineOffset = post->underlinePosition * yScale;
+ } else {
+ aMetrics->underlineOffset = mFace->underline_position * yScale
+ + 0.5 * aMetrics->underlineSize;
+ }
+ } else { // No underline info.
+ // Imitate Pango.
+ aMetrics->underlineSize = emHeight / 14.0;
+ aMetrics->underlineOffset = -aMetrics->underlineSize;
+ }
+
+ if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition && yScale > 0.0) {
+ aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
+ aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
+ } else { // No strikeout info.
+ aMetrics->strikeoutSize = aMetrics->underlineSize;
+ // Use OpenType spec's suggested position for Roman font.
+ aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
+ + 0.5 * aMetrics->strikeoutSize;
+ }
+ SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
+
+ aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
+
+ // Make the line height an integer number of pixels so that lines will be
+ // equally spaced (rather than just being snapped to pixels, some up and
+ // some down). Layout calculates line height from the emHeight +
+ // internalLeading + externalLeading, but first each of these is rounded
+ // to layout units. To ensure that the result is an integer number of
+ // pixels, round each of the components to pixels.
+ aMetrics->emHeight = floor(emHeight + 0.5);
+
+ // maxHeight will normally be an integer, but round anyway in case
+ // FreeType is configured differently.
+ aMetrics->internalLeading =
+ floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
+
+ // Text input boxes currently don't work well with lineHeight
+ // significantly less than maxHeight (with Verdana, for example).
+ lineHeight = floor(std::max(lineHeight, aMetrics->maxHeight) + 0.5);
+ aMetrics->externalLeading =
+ lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
+
+ // Ensure emAscent + emDescent == emHeight
+ gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
+ aMetrics->emAscent = sum > 0.0 ?
+ aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
+ aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
+}
+
+uint32_t
+gfxFT2LockedFace::GetGlyph(uint32_t aCharCode)
+{
+ if (MOZ_UNLIKELY(!mFace))
+ return 0;
+
+#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
+ // FcFreeTypeCharIndex will search starting from the most recently
+ // selected charmap. This can cause non-determistic behavior when more
+ // than one charmap supports a character but with different glyphs, as
+ // with older versions of MS Gothic, for example. Always prefer a Unicode
+ // charmap, if there is one. (FcFreeTypeCharIndex usually does the
+ // appropriate Unicode conversion, but some fonts have non-Roman glyphs
+ // for FT_ENCODING_APPLE_ROMAN characters.)
+ if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
+ FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
+ }
+
+ return FcFreeTypeCharIndex(mFace, aCharCode);
+#else
+ return FT_Get_Char_Index(mFace, aCharCode);
+#endif
+}
+
+typedef FT_UInt (*GetCharVariantFunction)(FT_Face face,
+ FT_ULong charcode,
+ FT_ULong variantSelector);
+
+uint32_t
+gfxFT2LockedFace::GetUVSGlyph(uint32_t aCharCode, uint32_t aVariantSelector)
+{
+ NS_PRECONDITION(aVariantSelector, "aVariantSelector should not be NULL");
+
+ if (MOZ_UNLIKELY(!mFace))
+ return 0;
+
+ // This function is available from FreeType 2.3.6 (June 2008).
+ static CharVariantFunction sGetCharVariantPtr = FindCharVariantFunction();
+ if (!sGetCharVariantPtr)
+ return 0;
+
+#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
+ // FcFreeTypeCharIndex may have changed the selected charmap.
+ // FT_Face_GetCharVariantIndex needs a unicode charmap.
+ if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
+ FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
+ }
+#endif
+
+ return (*sGetCharVariantPtr)(mFace, aCharCode, aVariantSelector);
+}
+
+uint32_t
+gfxFT2LockedFace::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
+{
+ NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL");
+
+ if (!mFace)
+ return 0;
+
+ FT_UInt gid = mGfxFont->GetGlyph(aChar);
+ if (gid) {
+ mGfxFont->GetGlyphExtents(gid, aExtents);
+ }
+
+ return gid;
+}
+
+gfxFT2LockedFace::CharVariantFunction
+gfxFT2LockedFace::FindCharVariantFunction()
+{
+ // This function is available from FreeType 2.3.6 (June 2008).
+ PRLibrary *lib = nullptr;
+ CharVariantFunction function =
+ reinterpret_cast<CharVariantFunction>
+ (PR_FindFunctionSymbolAndLibrary("FT_Face_GetCharVariantIndex", &lib));
+ if (!lib) {
+ return nullptr;
+ }
+
+ FT_Int major;
+ FT_Int minor;
+ FT_Int patch;
+ FT_Library_Version(mFace->glyph->library, &major, &minor, &patch);
+
+ // Versions 2.4.0 to 2.4.3 crash if configured with
+ // FT_CONFIG_OPTION_OLD_INTERNALS. Presence of the symbol FT_Alloc
+ // indicates FT_CONFIG_OPTION_OLD_INTERNALS.
+ if (major == 2 && minor == 4 && patch < 4 &&
+ PR_FindFunctionSymbol(lib, "FT_Alloc")) {
+ function = nullptr;
+ }
+
+ // Decrement the reference count incremented in
+ // PR_FindFunctionSymbolAndLibrary.
+ PR_UnloadLibrary(lib);
+
+ return function;
+}
diff --git a/system/graphics/thebes/gfxFT2Utils.h b/system/graphics/thebes/gfxFT2Utils.h
new file mode 100644
index 000000000..35958bbf7
--- /dev/null
+++ b/system/graphics/thebes/gfxFT2Utils.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FT2UTILS_H
+#define GFX_FT2UTILS_H
+
+#include "cairo-ft.h"
+#include "gfxFT2FontBase.h"
+#include "mozilla/Likely.h"
+
+// Rounding and truncation functions for a FreeType fixed point number
+// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
+// part and low 6 bits for the fractional part.
+#define FLOAT_FROM_26_6(x) ((x) / 64.0)
+#define FLOAT_FROM_16_16(x) ((x) / 65536.0)
+#define ROUND_26_6_TO_INT(x) ((x) >= 0 ? ((32 + (x)) >> 6) \
+ : -((32 - (x)) >> 6))
+
+typedef struct FT_FaceRec_* FT_Face;
+
+class gfxFT2LockedFace {
+public:
+ explicit gfxFT2LockedFace(gfxFT2FontBase *aFont) :
+ mGfxFont(aFont),
+ mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
+ { }
+ ~gfxFT2LockedFace()
+ {
+ if (mFace) {
+ cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
+ }
+ }
+
+ FT_Face get() { return mFace; };
+
+ /**
+ * Get the glyph id for a Unicode character representable by a single
+ * glyph, or return zero if there is no such glyph. This does no caching,
+ * so you probably want gfxFcFont::GetGlyph.
+ */
+ uint32_t GetGlyph(uint32_t aCharCode);
+ /**
+ * Returns 0 if there is no variation selector cmap subtable.
+ */
+ uint32_t GetUVSGlyph(uint32_t aCharCode, uint32_t aVariantSelector);
+
+ void GetMetrics(gfxFont::Metrics* aMetrics, uint32_t* aSpaceGlyph);
+
+ // A scale factor for use in converting horizontal metrics from font units
+ // to pixels.
+ gfxFloat XScale()
+ {
+ if (MOZ_UNLIKELY(!mFace))
+ return 0.0;
+
+ const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
+
+ if (FT_IS_SCALABLE(mFace)) {
+ // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
+ // have subpixel accuracy.
+ //
+ // FT_Size_Metrics::x_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, so that the result
+ // may be interpreted as pixels in 26.6 fixed point format.
+ return FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.x_scale));
+ }
+
+ // Not scalable.
+ // FT_Size_Metrics doc says x_scale is "only relevant for scalable
+ // font formats".
+ return gfxFloat(ftMetrics.x_ppem) / gfxFloat(mFace->units_per_EM);
+ }
+
+protected:
+ /**
+ * Get extents for a simple character representable by a single glyph.
+ * The return value is the glyph id of that glyph or zero if no such glyph
+ * exists. aExtents is only set when this returns a non-zero glyph id.
+ */
+ uint32_t GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
+
+ typedef FT_UInt (*CharVariantFunction)(FT_Face face,
+ FT_ULong charcode,
+ FT_ULong variantSelector);
+ CharVariantFunction FindCharVariantFunction();
+
+ RefPtr<gfxFT2FontBase> mGfxFont;
+ FT_Face mFace;
+};
+
+#endif /* GFX_FT2UTILS_H */
diff --git a/system/graphics/thebes/gfxFailure.h b/system/graphics/thebes/gfxFailure.h
new file mode 100644
index 000000000..b35ffba39
--- /dev/null
+++ b/system/graphics/thebes/gfxFailure.h
@@ -0,0 +1,25 @@
+/* vim: se cin sw=2 ts=2 et : */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfxFailure_h_
+#define gfxFailure_h_
+
+#include "nsString.h"
+#include "nsIGfxInfo.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+ namespace gfx {
+ inline
+ void LogFailure(const nsCString &failure) {
+ nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+ gfxInfo->LogFailure(failure);
+ }
+ } // namespace gfx
+} // namespace mozilla
+
+#endif // gfxFailure_h_
diff --git a/system/graphics/thebes/gfxFcPlatformFontList.cpp b/system/graphics/thebes/gfxFcPlatformFontList.cpp
new file mode 100644
index 000000000..0fb4bcb3a
--- /dev/null
+++ b/system/graphics/thebes/gfxFcPlatformFontList.cpp
@@ -0,0 +1,1877 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+
+#include "gfxFcPlatformFontList.h"
+#include "gfxFont.h"
+#include "gfxFontConstants.h"
+#include "gfxFontFamilyList.h"
+#include "gfxFT2Utils.h"
+#include "gfxPlatform.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/TimeStamp.h"
+#include "nsGkAtoms.h"
+#include "nsILanguageAtomService.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeRange.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsCharSeparatedTokenizer.h"
+
+#include "mozilla/gfx/HelpersCairo.h"
+
+#include <fontconfig/fcfreetype.h>
+
+#ifdef MOZ_WIDGET_GTK
+#include <gdk/gdk.h>
+#include "gfxPlatformGtk.h"
+#endif
+
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::unicode;
+
+#ifndef FC_POSTSCRIPT_NAME
+#define FC_POSTSCRIPT_NAME "postscriptname" /* String */
+#endif
+
+#define PRINTING_FC_PROPERTY "gfx.printing"
+
+#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug)
+#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_cmapdata), \
+ LogLevel::Debug)
+
+static const FcChar8*
+ToFcChar8Ptr(const char* aStr)
+{
+ return reinterpret_cast<const FcChar8*>(aStr);
+}
+
+static const char*
+ToCharPtr(const FcChar8 *aStr)
+{
+ return reinterpret_cast<const char*>(aStr);
+}
+
+FT_Library gfxFcPlatformFontList::sCairoFTLibrary = nullptr;
+
+static cairo_user_data_key_t sFcFontlistUserFontDataKey;
+
+// canonical name ==> first en name or first name if no en name
+// This is the required logic for fullname lookups as per CSS3 Fonts spec.
+static uint32_t
+FindCanonicalNameIndex(FcPattern* aFont, const char* aLangField)
+{
+ uint32_t n = 0, en = 0;
+ FcChar8* lang;
+ while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
+ // look for 'en' or variants, en-US, en-JP etc.
+ uint32_t len = strlen(ToCharPtr(lang));
+ bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
+ if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
+ en = n;
+ break;
+ }
+ n++;
+ }
+ return en;
+}
+
+static void
+GetFaceNames(FcPattern* aFont, const nsAString& aFamilyName,
+ nsAString& aPostscriptName, nsAString& aFullname)
+{
+ // get the Postscript name
+ FcChar8* psname;
+ if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) {
+ AppendUTF8toUTF16(ToCharPtr(psname), aPostscriptName);
+ }
+
+ // get the canonical fullname (i.e. en name or first name)
+ uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
+ FcChar8* fullname;
+ if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
+ AppendUTF8toUTF16(ToCharPtr(fullname), aFullname);
+ }
+
+ // if have fullname, done
+ if (!aFullname.IsEmpty()) {
+ return;
+ }
+
+ // otherwise, set the fullname to family + style name [en] and use that
+ aFullname.Append(aFamilyName);
+
+ // figure out the en style name
+ en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
+ nsAutoString style;
+ FcChar8* stylename = nullptr;
+ FcPatternGetString(aFont, FC_STYLE, en, &stylename);
+ if (stylename) {
+ AppendUTF8toUTF16(ToCharPtr(stylename), style);
+ }
+
+ if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
+ aFullname.Append(' ');
+ aFullname.Append(style);
+ }
+}
+
+static uint16_t
+MapFcWeight(int aFcWeight)
+{
+ if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
+ return 100;
+ } else if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
+ return 200;
+ } else if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
+ return 300;
+ } else if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
+ // This includes FC_WEIGHT_BOOK
+ return 400;
+ } else if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
+ return 500;
+ } else if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
+ return 600;
+ } else if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
+ return 700;
+ } else if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
+ return 800;
+ } else if (aFcWeight <= FC_WEIGHT_BLACK) {
+ return 900;
+ }
+
+ // including FC_WEIGHT_EXTRABLACK
+ return 901;
+}
+
+static int16_t
+MapFcWidth(int aFcWidth)
+{
+ if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
+ return NS_FONT_STRETCH_ULTRA_CONDENSED;
+ }
+ if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
+ return NS_FONT_STRETCH_EXTRA_CONDENSED;
+ }
+ if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
+ return NS_FONT_STRETCH_CONDENSED;
+ }
+ if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
+ return NS_FONT_STRETCH_SEMI_CONDENSED;
+ }
+ if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
+ return NS_FONT_STRETCH_NORMAL;
+ }
+ if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
+ return NS_FONT_STRETCH_SEMI_EXPANDED;
+ }
+ if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
+ return NS_FONT_STRETCH_EXPANDED;
+ }
+ if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
+ return NS_FONT_STRETCH_EXTRA_EXPANDED;
+ }
+ return NS_FONT_STRETCH_ULTRA_EXPANDED;
+}
+
+gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
+ FcPattern* aFontPattern,
+ bool aIgnoreFcCharmap)
+ : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
+ mFTFace(nullptr), mFTFaceInitialized(false),
+ mIgnoreFcCharmap(aIgnoreFcCharmap),
+ mAspect(0.0), mFontData(nullptr)
+{
+ // italic
+ int slant;
+ if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
+ slant = FC_SLANT_ROMAN;
+ }
+ if (slant == FC_SLANT_OBLIQUE) {
+ mStyle = NS_FONT_STYLE_OBLIQUE;
+ } else if (slant > 0) {
+ mStyle = NS_FONT_STYLE_ITALIC;
+ }
+
+ // weight
+ int weight;
+ if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
+ weight = FC_WEIGHT_REGULAR;
+ }
+ mWeight = MapFcWeight(weight);
+
+ // width
+ int width;
+ if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
+ width = FC_WIDTH_NORMAL;
+ }
+ mStretch = MapFcWidth(width);
+}
+
+gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t *aData,
+ FT_Face aFace)
+ : gfxFontEntry(aFaceName),
+ mFTFace(aFace), mFTFaceInitialized(true),
+ mIgnoreFcCharmap(true),
+ mAspect(0.0), mFontData(aData)
+{
+ mWeight = aWeight;
+ mStyle = aStyle;
+ mStretch = aStretch;
+ mIsDataUserFont = true;
+
+ // Use fontconfig to fill out the pattern from the FTFace.
+ // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
+ // least). The dummy file passed here is removed below.
+ //
+ // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
+ // is passed as the "blanks" argument, which provides that unexpectedly
+ // blank glyphs are elided. Here, however, we pass nullptr for
+ // "blanks", effectively assuming that, if the font has a blank glyph,
+ // then the author intends any associated character to be rendered
+ // blank.
+ mFontPattern = FcFreeTypeQueryFace(mFTFace, ToFcChar8Ptr(""), 0, nullptr);
+ // given that we have a FT_Face, not really sure this is possible...
+ if (!mFontPattern) {
+ mFontPattern = FcPatternCreate();
+ }
+ FcPatternDel(mFontPattern, FC_FILE);
+ FcPatternDel(mFontPattern, FC_INDEX);
+
+ // Make a new pattern and store the face in it so that cairo uses
+ // that when creating a cairo font face.
+ FcPatternAddFTFace(mFontPattern, FC_FT_FACE, mFTFace);
+
+ mUserFontData = new FTUserFontData(mFTFace, mFontData);
+}
+
+gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
+ FcPattern* aFontPattern,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+ : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
+ mFTFace(nullptr), mFTFaceInitialized(false),
+ mAspect(0.0), mFontData(nullptr)
+{
+ mWeight = aWeight;
+ mStyle = aStyle;
+ mStretch = aStretch;
+ mIsLocalUserFont = true;
+
+ // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
+ // via src:local()...
+ // If the local font happens to come from the application fontset,
+ // we want to set it to true so that color/svg fonts will work even
+ // if the default glyphs are blank; but if the local font is a non-
+ // sfnt face (e.g. legacy type 1) then we need to set it to false
+ // because our cmap-reading code will fail and we depend on FT+Fc to
+ // determine the coverage.
+ // We set the flag here, but may flip it the first time TestCharacterMap
+ // is called, at which point we'll look to see whether a 'cmap' is
+ // actually present in the font.
+ mIgnoreFcCharmap = true;
+}
+
+gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
+{
+}
+
+static bool
+PatternHasLang(const FcPattern *aPattern, const FcChar8 *aLang)
+{
+ FcLangSet *langset;
+
+ if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
+ return false;
+ }
+
+ if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
+ return true;
+ }
+ return false;
+}
+
+bool
+gfxFontconfigFontEntry::SupportsLangGroup(nsIAtom *aLangGroup) const
+{
+ if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
+ return true;
+ }
+
+ nsAutoCString fcLang;
+ gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
+ pfl->GetSampleLangForGroup(aLangGroup, fcLang);
+ if (fcLang.IsEmpty()) {
+ return true;
+ }
+
+ // is lang included in the underlying pattern?
+ return PatternHasLang(mFontPattern, ToFcChar8Ptr(fcLang.get()));
+}
+
+nsresult
+gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxCharacterMap> charmap;
+ nsresult rv;
+ bool symbolFont = false; // currently ignored
+
+ if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+ mUVSOffset,
+ symbolFont))) {
+ rv = NS_OK;
+ } else {
+ uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+ charmap = new gfxCharacterMap();
+ AutoTable cmapTable(this, kCMAP);
+
+ if (cmapTable) {
+ bool unicodeFont = false; // currently ignored
+ uint32_t cmapLen;
+ const uint8_t* cmapData =
+ reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
+ &cmapLen));
+ rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
+ *charmap, mUVSOffset,
+ unicodeFont, symbolFont);
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+
+ mHasCmapTable = NS_SUCCEEDED(rv);
+ if (mHasCmapTable) {
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ mCharacterMap = pfl->FindCharMap(charmap);
+ } else {
+ // if error occurred, initialize to null cmap
+ mCharacterMap = new gfxCharacterMap();
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
+ NS_ConvertUTF16toUTF8(mName).get(),
+ charmap->SizeOfIncludingThis(moz_malloc_size_of),
+ charmap->mHash, mCharacterMap == charmap ? " new" : ""));
+ if (LOG_CMAPDATA_ENABLED()) {
+ char prefix[256];
+ SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+ NS_ConvertUTF16toUTF8(mName).get());
+ charmap->Dump(prefix, eGfxLog_cmapdata);
+ }
+
+ return rv;
+}
+
+static bool
+HasChar(FcPattern *aFont, FcChar32 aCh)
+{
+ FcCharSet *charset = nullptr;
+ FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
+ return charset && FcCharSetHasChar(charset, aCh);
+}
+
+bool
+gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh)
+{
+ // For user fonts, or for fonts bundled with the app (which might include
+ // color/svg glyphs where the default glyphs may be blank, and thus confuse
+ // fontconfig/freetype's char map checking), we instead check the cmap
+ // directly for character coverage.
+ if (mIgnoreFcCharmap) {
+ // If it does not actually have a cmap, switch our strategy to use
+ // fontconfig's charmap after all (except for data fonts, which must
+ // always have a cmap to have passed OTS validation).
+ if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c','m','a','p'))) {
+ mIgnoreFcCharmap = false;
+ // ...and continue with HasChar() below.
+ } else {
+ return gfxFontEntry::TestCharacterMap(aCh);
+ }
+ }
+ // otherwise (for system fonts), use the charmap in the pattern
+ return HasChar(mFontPattern, aCh);
+}
+
+hb_blob_t*
+gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
+{
+ // for data fonts, read directly from the font data
+ if (mFontData) {
+ return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
+ }
+
+ return gfxFontEntry::GetFontTable(aTableTag);
+}
+
+void
+gfxFontconfigFontEntry::MaybeReleaseFTFace()
+{
+ // don't release if either HB or Gr face still exists
+ if (mHBFace || mGrFace) {
+ return;
+ }
+ // only close out FT_Face for system fonts, not for data fonts
+ if (!mIsDataUserFont) {
+ if (mFTFace) {
+ FT_Done_Face(mFTFace);
+ mFTFace = nullptr;
+ }
+ mFTFaceInitialized = false;
+ }
+}
+
+void
+gfxFontconfigFontEntry::ForgetHBFace()
+{
+ gfxFontEntry::ForgetHBFace();
+ MaybeReleaseFTFace();
+}
+
+void
+gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace)
+{
+ gfxFontEntry::ReleaseGrFace(aFace);
+ MaybeReleaseFTFace();
+}
+
+double
+gfxFontconfigFontEntry::GetAspect()
+{
+ if (mAspect == 0.0) {
+ // default to aspect = 0.5
+ mAspect = 0.5;
+
+ // create a font to calculate x-height / em-height
+ gfxFontStyle s;
+ s.size = 100.0; // pick large size to avoid possible hinting artifacts
+ RefPtr<gfxFont> font = FindOrMakeFont(&s, false);
+ if (font) {
+ const gfxFont::Metrics& metrics =
+ font->GetMetrics(gfxFont::eHorizontal);
+
+ // The factor of 0.1 ensures that xHeight is sane so fonts don't
+ // become huge. Strictly ">" ensures that xHeight and emHeight are
+ // not both zero.
+ if (metrics.xHeight > 0.1 * metrics.emHeight) {
+ mAspect = metrics.xHeight / metrics.emHeight;
+ }
+ }
+ }
+ return mAspect;
+}
+
+static void
+PrepareFontOptions(FcPattern* aPattern,
+ cairo_font_options_t* aFontOptions)
+{
+ NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions");
+
+ // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
+
+ FcBool printing;
+ if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
+ FcResultMatch) {
+ printing = FcFalse;
+ }
+
+ // Font options are set explicitly here to improve cairo's caching
+ // behavior and to record the relevant parts of the pattern for
+ // SetupCairoFont (so that the pattern can be released).
+ //
+ // Most font_options have already been set as defaults on the FcPattern
+ // with cairo_ft_font_options_substitute(), then user and system
+ // fontconfig configurations were applied. The resulting font_options
+ // have been recorded on the face during
+ // cairo_ft_font_face_create_for_pattern().
+ //
+ // None of the settings here cause this scaled_font to behave any
+ // differently from how it would behave if it were created from the same
+ // face with default font_options.
+ //
+ // We set options explicitly so that the same scaled_font will be found in
+ // the cairo_scaled_font_map when cairo loads glyphs from a context with
+ // the same font_face, font_matrix, ctm, and surface font_options.
+ //
+ // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
+ // font_options on the cairo_ft_font_face, and doesn't consider default
+ // option values to not match any explicit values.
+ //
+ // Even after cairo_set_scaled_font is used to set font_options for the
+ // cairo context, when cairo looks for a scaled_font for the context, it
+ // will look for a font with some option values from the target surface if
+ // any values are left default on the context font_options. If this
+ // scaled_font is created with default font_options, cairo will not find
+ // it.
+ //
+ // The one option not recorded in the pattern is hint_metrics, which will
+ // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
+ // We should be considering the font_options of the surface on which this
+ // font will be used, but currently we don't have different gfxFonts for
+ // different surface font_options, so we'll create a font suitable for the
+ // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
+ if (printing) {
+ cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF);
+ } else {
+ cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON);
+ }
+
+ // The remaining options have been recorded on the pattern and the face.
+ // _cairo_ft_options_merge has some logic to decide which options from the
+ // scaled_font or from the cairo_ft_font_face take priority in the way the
+ // font behaves.
+ //
+ // In the majority of cases, _cairo_ft_options_merge uses the options from
+ // the cairo_ft_font_face, so sometimes it is not so important which
+ // values are set here so long as they are not defaults, but we'll set
+ // them to the exact values that we expect from the font, to be consistent
+ // and to protect against changes in cairo.
+ //
+ // In some cases, _cairo_ft_options_merge uses some options from the
+ // scaled_font's font_options rather than options on the
+ // cairo_ft_font_face (from fontconfig).
+ // https://bugs.freedesktop.org/show_bug.cgi?id=11838
+ //
+ // Surface font options were set on the pattern in
+ // cairo_ft_font_options_substitute. If fontconfig has changed the
+ // hint_style then that is what the user (or distribution) wants, so we
+ // use the setting from the FcPattern.
+ //
+ // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
+ FcBool hinting = FcFalse;
+ if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
+ hinting = FcTrue;
+ }
+
+ cairo_hint_style_t hint_style;
+ if (printing || !hinting) {
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ } else {
+ int fc_hintstyle;
+ if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
+ 0, &fc_hintstyle) != FcResultMatch) {
+ fc_hintstyle = FC_HINT_FULL;
+ }
+ switch (fc_hintstyle) {
+ case FC_HINT_NONE:
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ break;
+ case FC_HINT_SLIGHT:
+ hint_style = CAIRO_HINT_STYLE_SLIGHT;
+ break;
+ case FC_HINT_MEDIUM:
+ default: // This fallback mirrors _get_pattern_ft_options in cairo.
+ hint_style = CAIRO_HINT_STYLE_MEDIUM;
+ break;
+ case FC_HINT_FULL:
+ hint_style = CAIRO_HINT_STYLE_FULL;
+ break;
+ }
+ }
+ cairo_font_options_set_hint_style(aFontOptions, hint_style);
+
+ int rgba;
+ if (FcPatternGetInteger(aPattern,
+ FC_RGBA, 0, &rgba) != FcResultMatch) {
+ rgba = FC_RGBA_UNKNOWN;
+ }
+ cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ switch (rgba) {
+ case FC_RGBA_UNKNOWN:
+ case FC_RGBA_NONE:
+ default:
+ // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
+ // is disabled through cairo_antialias_t.
+ rgba = FC_RGBA_NONE;
+ // subpixel_order won't be used by the font as we won't use
+ // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
+ // caching reasons described above. Fall through:
+ MOZ_FALLTHROUGH;
+ case FC_RGBA_RGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+ break;
+ case FC_RGBA_BGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+ break;
+ case FC_RGBA_VRGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+ break;
+ case FC_RGBA_VBGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+ break;
+ }
+ cairo_font_options_set_subpixel_order(aFontOptions, subpixel_order);
+
+ FcBool fc_antialias;
+ if (FcPatternGetBool(aPattern,
+ FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
+ fc_antialias = FcTrue;
+ }
+ cairo_antialias_t antialias;
+ if (!fc_antialias) {
+ antialias = CAIRO_ANTIALIAS_NONE;
+ } else if (rgba == FC_RGBA_NONE) {
+ antialias = CAIRO_ANTIALIAS_GRAY;
+ } else {
+ antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+ }
+ cairo_font_options_set_antialias(aFontOptions, antialias);
+}
+
+cairo_scaled_font_t*
+gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
+ gfxFloat aAdjustedSize,
+ const gfxFontStyle *aStyle,
+ bool aNeedsBold)
+{
+ if (aNeedsBold) {
+ FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
+ }
+
+ // synthetic oblique by skewing via the font matrix
+ bool needsOblique = IsUpright() &&
+ aStyle->style != NS_FONT_STYLE_NORMAL &&
+ aStyle->allowSyntheticStyle;
+
+ if (needsOblique) {
+ // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
+ FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
+ FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
+ }
+
+ cairo_font_face_t *face =
+ cairo_ft_font_face_create_for_pattern(aRenderPattern);
+
+ if (mFontData) {
+ // for data fonts, add the face/data pointer to the cairo font face
+ // so that it gets deleted whenever cairo decides
+ NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
+ NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
+ cairo_font_face_set_user_data(face,
+ &sFcFontlistUserFontDataKey,
+ new FTUserFontDataRef(mUserFontData),
+ FTUserFontDataRef::Destroy);
+ }
+
+ cairo_scaled_font_t *scaledFont = nullptr;
+
+ cairo_matrix_t sizeMatrix;
+ cairo_matrix_t identityMatrix;
+
+ cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
+ cairo_matrix_init_identity(&identityMatrix);
+
+ if (needsOblique) {
+ const double kSkewFactor = OBLIQUE_SKEW_FACTOR;
+
+ cairo_matrix_t style;
+ cairo_matrix_init(&style,
+ 1, //xx
+ 0, //yx
+ -1 * kSkewFactor, //xy
+ 1, //yy
+ 0, //x0
+ 0); //y0
+ cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
+ }
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+ PrepareFontOptions(aRenderPattern, fontOptions);
+
+ scaledFont = cairo_scaled_font_create(face, &sizeMatrix,
+ &identityMatrix, fontOptions);
+ cairo_font_options_destroy(fontOptions);
+
+ NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
+ "Failed to make scaled font");
+
+ cairo_font_face_destroy(face);
+
+ return scaledFont;
+}
+
+#ifdef MOZ_WIDGET_GTK
+// defintion included below
+static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
+#endif
+
+#ifdef MOZ_X11
+static bool
+GetXftInt(Display* aDisplay, const char* aName, int* aResult)
+{
+ if (!aDisplay) {
+ return false;
+ }
+ char* value = XGetDefault(aDisplay, "Xft", aName);
+ if (!value) {
+ return false;
+ }
+ if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
+ return true;
+ }
+ char* end;
+ *aResult = strtol(value, &end, 0);
+ if (end != value) {
+ return true;
+ }
+ return false;
+}
+#endif
+
+static void
+PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
+{
+ FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
+
+ // This gets cairo_font_options_t for the Screen. We should have
+ // different font options for printing (no hinting) but we are not told
+ // what we are measuring for.
+ //
+ // If cairo adds support for lcd_filter, gdk will not provide the default
+ // setting for that option. We could get the default setting by creating
+ // an xlib surface once, recording its font_options, and then merging the
+ // gdk options.
+ //
+ // Using an xlib surface would also be an option to get Screen font
+ // options for non-GTK X11 toolkits, but less efficient than using GDK to
+ // pick up dynamic changes.
+ if(aIsPrinterFont) {
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+ cairo_ft_font_options_substitute(options, aPattern);
+ cairo_font_options_destroy(options);
+ FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
+ } else {
+#ifdef MOZ_WIDGET_GTK
+ ApplyGdkScreenFontOptions(aPattern);
+#endif
+
+#ifdef MOZ_X11
+ FcValue value;
+ int lcdfilter;
+ if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value)
+ == FcResultNoMatch &&
+ GetXftInt(DefaultXDisplay(), "lcdfilter", &lcdfilter)) {
+ FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter);
+ }
+#endif
+ }
+
+ FcDefaultSubstitute(aPattern);
+}
+
+static inline gfxFloat
+SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
+{
+ return aStyle.sizeAdjust >= 0.0 ?
+ aStyle.GetAdjustedSize(aEntry->GetAspect()) :
+ aStyle.size;
+}
+
+static double
+ChooseFontSize(gfxFontconfigFontEntry* aEntry,
+ const gfxFontStyle& aStyle)
+{
+ double requestedSize = SizeForStyle(aEntry, aStyle);
+ double bestDist = -1.0;
+ double bestSize = requestedSize;
+ double size;
+ int v = 0;
+ while (FcPatternGetDouble(aEntry->GetPattern(),
+ FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
+ ++v;
+ double dist = fabs(size - requestedSize);
+ if (bestDist < 0.0 || dist < bestDist) {
+ bestDist = dist;
+ bestSize = size;
+ }
+ }
+ // If the font has bitmaps but wants to be scaled, then let it scale.
+ if (bestSize >= 0.0) {
+ FcBool scalable;
+ if (FcPatternGetBool(aEntry->GetPattern(),
+ FC_SCALABLE, 0, &scalable) == FcResultMatch &&
+ scalable) {
+ return requestedSize;
+ }
+ }
+ return bestSize;
+}
+
+gfxFont*
+gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
+ bool aNeedsBold)
+{
+ nsAutoRef<FcPattern> pattern(FcPatternCreate());
+ if (!pattern) {
+ NS_WARNING("Failed to create Fontconfig pattern for font instance");
+ return nullptr;
+ }
+
+ double size = ChooseFontSize(this, *aFontStyle);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
+
+ PreparePattern(pattern, aFontStyle->printerFont);
+ nsAutoRef<FcPattern> renderPattern
+ (FcFontRenderPrepare(nullptr, pattern, mFontPattern));
+ if (!renderPattern) {
+ NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
+ return nullptr;
+ }
+
+ cairo_scaled_font_t* scaledFont =
+ CreateScaledFont(renderPattern, size, aFontStyle, aNeedsBold);
+ gfxFont* newFont =
+ new gfxFontconfigFont(scaledFont, renderPattern, size,
+ this, aFontStyle, aNeedsBold);
+ cairo_scaled_font_destroy(scaledFont);
+
+ return newFont;
+}
+
+nsresult
+gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer)
+{
+ NS_ASSERTION(!mIsDataUserFont,
+ "data fonts should be reading tables directly from memory");
+
+ if (!mFTFaceInitialized) {
+ mFTFaceInitialized = true;
+ FcChar8 *filename;
+ if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
+ return NS_ERROR_FAILURE;
+ }
+ int index;
+ if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
+ index = 0; // default to 0 if not found in pattern
+ }
+ if (FT_New_Face(gfxFcPlatformFontList::GetFTLibrary(),
+ (const char*)filename, index, &mFTFace) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (!mFTFace) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ FT_ULong length = 0;
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ if (!aBuffer.SetLength(length, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
+ aBuffer.Clear();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
+{
+ if (mHasStyles) {
+ return;
+ }
+
+ // add font entries for each of the faces
+ uint32_t numFonts = mFontPatterns.Length();
+ NS_ASSERTION(numFonts, "font family containing no faces!!");
+ uint32_t numRegularFaces = 0;
+ for (uint32_t i = 0; i < numFonts; i++) {
+ FcPattern* face = mFontPatterns[i];
+
+ // figure out the psname/fullname and choose which to use as the facename
+ nsAutoString psname, fullname;
+ GetFaceNames(face, mName, psname, fullname);
+ const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
+
+ gfxFontconfigFontEntry *fontEntry =
+ new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
+ AddFontEntry(fontEntry);
+
+ if (fontEntry->IsUpright() &&
+ fontEntry->Weight() == NS_FONT_WEIGHT_NORMAL &&
+ fontEntry->Stretch() == NS_FONT_STRETCH_NORMAL) {
+ numRegularFaces++;
+ }
+
+ if (LOG_FONTLIST_ENABLED()) {
+ LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d"
+ " psname: %s fullname: %s",
+ NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(Name()).get(),
+ (fontEntry->IsItalic()) ?
+ "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
+ fontEntry->Weight(), fontEntry->Stretch(),
+ NS_ConvertUTF16toUTF8(psname).get(),
+ NS_ConvertUTF16toUTF8(fullname).get()));
+ }
+ }
+
+ // somewhat arbitrary, but define a family with two or more regular
+ // faces as a family for which intra-family fallback should be used
+ if (numRegularFaces > 1) {
+ mCheckForFallbackFaces = true;
+ }
+ mFaceNamesInitialized = true;
+ mFontPatterns.Clear();
+ SetHasStyles(true);
+}
+
+void
+gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
+{
+ NS_ASSERTION(!mHasStyles,
+ "font patterns must not be added to already enumerated families");
+
+ FcBool outline;
+ if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch ||
+ !outline) {
+ mHasNonScalableFaces = true;
+
+ FcBool scalable;
+ if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch &&
+ scalable) {
+ mForceScalable = true;
+ }
+ }
+
+ nsCountedRef<FcPattern> pattern(aFontPattern);
+ mFontPatterns.AppendElement(pattern);
+}
+
+static const double kRejectDistance = 10000.0;
+
+// Calculate a distance score representing the size disparity between the
+// requested style's size and the font entry's size.
+static double
+SizeDistance(gfxFontconfigFontEntry* aEntry,
+ const gfxFontStyle& aStyle,
+ bool aForceScalable)
+{
+ double requestedSize = SizeForStyle(aEntry, aStyle);
+ double bestDist = -1.0;
+ double size;
+ int v = 0;
+ while (FcPatternGetDouble(aEntry->GetPattern(),
+ FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
+ ++v;
+ double dist = fabs(size - requestedSize);
+ if (bestDist < 0.0 || dist < bestDist) {
+ bestDist = dist;
+ }
+ }
+ if (bestDist < 0.0) {
+ // No size means scalable
+ return -1.0;
+ } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
+ // fontconfig prefers a matching family or lang to pixelsize of bitmap
+ // fonts. CSS suggests a tolerance of 20% on pixelsize.
+ return bestDist;
+ } else {
+ // Reject any non-scalable fonts that are not within tolerance.
+ return kRejectDistance;
+ }
+}
+
+void
+gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
+ nsTArray<gfxFontEntry*>& aFontEntryList,
+ bool& aNeedsSyntheticBold)
+{
+ gfxFontFamily::FindAllFontsForStyle(aFontStyle,
+ aFontEntryList,
+ aNeedsSyntheticBold);
+
+ if (!mHasNonScalableFaces) {
+ return;
+ }
+
+ // Iterate over the the available fonts while compacting any groups
+ // of unscalable fonts with matching styles into a single entry
+ // corresponding to the closest available size. If the closest
+ // available size is rejected for being outside tolernace, then the
+ // entire group will be skipped.
+ size_t skipped = 0;
+ gfxFontconfigFontEntry* bestEntry = nullptr;
+ double bestDist = -1.0;
+ for (size_t i = 0; i < aFontEntryList.Length(); i++) {
+ gfxFontconfigFontEntry* entry =
+ static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
+ double dist = SizeDistance(entry, aFontStyle, mForceScalable);
+ // If the entry is scalable or has a style that does not match
+ // the group of unscalable fonts, then start a new group.
+ if (dist < 0.0 ||
+ !bestEntry ||
+ bestEntry->Stretch() != entry->Stretch() ||
+ bestEntry->Weight() != entry->Weight() ||
+ bestEntry->mStyle != entry->mStyle) {
+ // If the best entry in this group is still outside the tolerance,
+ // then skip the entire group.
+ if (bestDist >= kRejectDistance) {
+ skipped++;
+ }
+ // Remove any compacted entries from the previous group.
+ if (skipped) {
+ i -= skipped;
+ aFontEntryList.RemoveElementsAt(i, skipped);
+ skipped = 0;
+ }
+ // Mark the start of the new group.
+ bestEntry = entry;
+ bestDist = dist;
+ } else {
+ // If this entry more closely matches the requested size than the
+ // current best in the group, then take this entry instead.
+ if (dist < bestDist) {
+ aFontEntryList[i-1-skipped] = entry;
+ bestEntry = entry;
+ bestDist = dist;
+ }
+ skipped++;
+ }
+ }
+ // If the best entry in this group is still outside the tolerance,
+ // then skip the entire group.
+ if (bestDist >= kRejectDistance) {
+ skipped++;
+ }
+ // Remove any compacted entries from the current group.
+ if (skipped) {
+ aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
+ }
+}
+
+gfxFontconfigFont::gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
+ FcPattern *aPattern,
+ gfxFloat aAdjustedSize,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold) :
+ gfxFontconfigFontBase(aScaledFont, aPattern, aFontEntry, aFontStyle)
+{
+ mAdjustedSize = aAdjustedSize;
+}
+
+gfxFontconfigFont::~gfxFontconfigFont()
+{
+}
+
+gfxFcPlatformFontList::gfxFcPlatformFontList()
+ : mLocalNames(64)
+ , mGenericMappings(32)
+ , mFcSubstituteCache(64)
+ , mLastConfig(nullptr)
+ , mAlwaysUseFontconfigGenerics(true)
+{
+ // if the rescan interval is set, start the timer
+ int rescanInterval = FcConfigGetRescanInterval(nullptr);
+ if (rescanInterval) {
+ mLastConfig = FcConfigGetCurrent();
+ mCheckFontUpdatesTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (mCheckFontUpdatesTimer) {
+ mCheckFontUpdatesTimer->
+ InitWithFuncCallback(CheckFontUpdates, this,
+ (rescanInterval + 1) * 1000,
+ nsITimer::TYPE_REPEATING_SLACK);
+ } else {
+ NS_WARNING("Failure to create font updates timer");
+ }
+ }
+
+ mBundledFontsInitialized = false;
+}
+
+gfxFcPlatformFontList::~gfxFcPlatformFontList()
+{
+ if (mCheckFontUpdatesTimer) {
+ mCheckFontUpdatesTimer->Cancel();
+ mCheckFontUpdatesTimer = nullptr;
+ }
+}
+
+void
+gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts)
+{
+ // This iterates over the fonts in a font set and adds in gfxFontFamily
+ // objects for each family. The patterns for individual fonts are not
+ // copied here. When a family is actually used, the fonts in the family
+ // are enumerated and the patterns copied. Note that we're explicitly
+ // excluding non-scalable fonts such as X11 bitmap fonts, which
+ // Chrome Skia/Webkit code does also.
+
+ if (!aFontSet) {
+ NS_WARNING("AddFontSetFamilies called with a null font set.");
+ return;
+ }
+
+ FcChar8* lastFamilyName = (FcChar8*)"";
+ RefPtr<gfxFontconfigFontFamily> fontFamily;
+ nsAutoString familyName;
+ for (int f = 0; f < aFontSet->nfont; f++) {
+ FcPattern* font = aFontSet->fonts[f];
+
+ // get canonical name
+ uint32_t cIndex = FindCanonicalNameIndex(font, FC_FAMILYLANG);
+ FcChar8* canonical = nullptr;
+ FcPatternGetString(font, FC_FAMILY, cIndex, &canonical);
+ if (!canonical) {
+ continue;
+ }
+
+ // same as the last one? no need to add a new family, skip
+ if (FcStrCmp(canonical, lastFamilyName) != 0) {
+ lastFamilyName = canonical;
+
+ // add new family if one doesn't already exist
+ familyName.Truncate();
+ AppendUTF8toUTF16(ToCharPtr(canonical), familyName);
+ nsAutoString keyName(familyName);
+ ToLowerCase(keyName);
+
+ fontFamily = static_cast<gfxFontconfigFontFamily*>
+ (mFontFamilies.GetWeak(keyName));
+ if (!fontFamily) {
+ fontFamily = new gfxFontconfigFontFamily(familyName);
+ mFontFamilies.Put(keyName, fontFamily);
+ }
+ // Record if the family contains fonts from the app font set
+ // (in which case we won't rely on fontconfig's charmap, due to
+ // bug 1276594).
+ if (aAppFonts) {
+ fontFamily->SetFamilyContainsAppFonts(true);
+ }
+
+ // Add pointers to other localized family names. Most fonts
+ // only have a single name, so the first call to GetString
+ // will usually not match
+ FcChar8* otherName;
+ int n = (cIndex == 0 ? 1 : 0);
+ while (FcPatternGetString(font, FC_FAMILY, n, &otherName) == FcResultMatch) {
+ NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
+ AddOtherFamilyName(fontFamily, otherFamilyName);
+ n++;
+ if (n == int(cIndex)) {
+ n++; // skip over canonical name
+ }
+ }
+ }
+
+ NS_ASSERTION(fontFamily, "font must belong to a font family");
+ fontFamily->AddFontPattern(font);
+
+ // map the psname, fullname ==> font family for local font lookups
+ nsAutoString psname, fullname;
+ GetFaceNames(font, familyName, psname, fullname);
+ if (!psname.IsEmpty()) {
+ ToLowerCase(psname);
+ mLocalNames.Put(psname, font);
+ }
+ if (!fullname.IsEmpty()) {
+ ToLowerCase(fullname);
+ mLocalNames.Put(fullname, font);
+ }
+ }
+}
+
+nsresult
+gfxFcPlatformFontList::InitFontListForPlatform()
+{
+ mLastConfig = FcConfigGetCurrent();
+
+ mLocalNames.Clear();
+ mFcSubstituteCache.Clear();
+
+ // iterate over available fonts
+ FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
+ AddFontSetFamilies(systemFonts, /* aAppFonts = */ false);
+ mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
+
+ ActivateBundledFonts();
+ FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
+ AddFontSetFamilies(appFonts, /* aAppFonts = */ true);
+
+ mOtherFamilyNamesInitialized = true;
+
+ return NS_OK;
+}
+
+// For displaying the fontlist in UI, use explicit call to FcFontList. Using
+// FcFontList results in the list containing the localized names as dictated
+// by system defaults.
+static void
+GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsIAtom *aLangGroup)
+{
+ aListOfFonts.Clear();
+
+ nsAutoRef<FcPattern> pat(FcPatternCreate());
+ if (!pat) {
+ return;
+ }
+
+ nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
+ if (!os) {
+ return;
+ }
+
+ // add the lang to the pattern
+ nsAutoCString fcLang;
+ gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
+ pfl->GetSampleLangForGroup(aLangGroup, fcLang);
+ if (!fcLang.IsEmpty()) {
+ FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
+ }
+
+ nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os));
+ if (!fs) {
+ return;
+ }
+
+ for (int i = 0; i < fs->nfont; i++) {
+ char *family;
+
+ if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &family) != FcResultMatch)
+ {
+ continue;
+ }
+
+ // Remove duplicates...
+ nsAutoString strFamily;
+ AppendUTF8toUTF16(family, strFamily);
+ if (aListOfFonts.Contains(strFamily)) {
+ continue;
+ }
+
+ aListOfFonts.AppendElement(strFamily);
+ }
+
+ aListOfFonts.Sort();
+}
+
+void
+gfxFcPlatformFontList::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ // Get the list of font family names using fontconfig
+ GetSystemFontList(aListOfFonts, aLangGroup);
+
+ // Under Linux, the generics "serif", "sans-serif" and "monospace"
+ // are included in the pref fontlist. These map to whatever fontconfig
+ // decides they should be for a given language, rather than one of the
+ // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
+ bool serif = false, sansSerif = false, monospace = false;
+ if (aGenericFamily.IsEmpty())
+ serif = sansSerif = monospace = true;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
+ serif = true;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
+ sansSerif = true;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
+ monospace = true;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
+ aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
+ serif = sansSerif = true;
+ else
+ NS_NOTREACHED("unexpected CSS generic font family");
+
+ // The first in the list becomes the default in
+ // FontBuilder.readFontSelection() if the preference-selected font is not
+ // available, so put system configured defaults first.
+ if (monospace)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
+ if (sansSerif)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
+ if (serif)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
+}
+
+gfxFontFamily*
+gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
+{
+ // Get the default font by using a fake name to retrieve the first
+ // scalable font that fontconfig suggests for the given language.
+ PrefFontList* prefFonts =
+ FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
+ NS_ASSERTION(prefFonts, "null list of generic fonts");
+ if (prefFonts && !prefFonts->IsEmpty()) {
+ return (*prefFonts)[0];
+ }
+ return nullptr;
+}
+
+gfxFontEntry*
+gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ nsAutoString keyName(aFontName);
+ ToLowerCase(keyName);
+
+ // if name is not in the global list, done
+ FcPattern* fontPattern = mLocalNames.Get(keyName);
+ if (!fontPattern) {
+ return nullptr;
+ }
+
+ return new gfxFontconfigFontEntry(aFontName, fontPattern,
+ aWeight, aStretch, aStyle);
+}
+
+gfxFontEntry*
+gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ FT_Face face;
+ FT_Error error =
+ FT_New_Memory_Face(gfxFcPlatformFontList::GetFTLibrary(),
+ aFontData, aLength, 0, &face);
+ if (error != FT_Err_Ok) {
+ NS_Free((void*)aFontData);
+ return nullptr;
+ }
+ if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
+ FT_Done_Face(face);
+ NS_Free((void*)aFontData);
+ return nullptr;
+ }
+
+ return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
+ aStyle, aFontData, face);
+}
+
+bool
+gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ nsAutoString familyName(aFamily);
+ ToLowerCase(familyName);
+ nsIAtom* language = (aStyle ? aStyle->language.get() : nullptr);
+
+ // deprecated generic names are explicitly converted to standard generics
+ bool isDeprecatedGeneric = false;
+ if (familyName.EqualsLiteral("sans") ||
+ familyName.EqualsLiteral("sans serif")) {
+ familyName.AssignLiteral("sans-serif");
+ isDeprecatedGeneric = true;
+ } else if (familyName.EqualsLiteral("mono")) {
+ familyName.AssignLiteral("monospace");
+ isDeprecatedGeneric = true;
+ }
+
+ // fontconfig generics? use fontconfig to determine the family for lang
+ if (isDeprecatedGeneric ||
+ mozilla::FontFamilyName::Convert(familyName).IsGeneric()) {
+ PrefFontList* prefFonts = FindGenericFamilies(familyName, language);
+ if (prefFonts && !prefFonts->IsEmpty()) {
+ aOutput->AppendElements(*prefFonts);
+ return true;
+ }
+ return false;
+ }
+
+ // fontconfig allows conditional substitutions in such a way that it's
+ // difficult to distinguish an explicit substitution from other suggested
+ // choices. To sniff out explicit substitutions, compare the substitutions
+ // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
+ // substitutions
+ //
+ // Example:
+ //
+ // serif ==> DejaVu Serif, ...
+ // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
+ //
+ // In this case fontconfig is including Tex Gyre Heros and
+ // Nimbus Sans L as alternatives for Helvetica.
+
+ // Because the FcConfigSubstitute call is quite expensive, we cache the
+ // actual font families found via this process. So check the cache first:
+ NS_ConvertUTF16toUTF8 familyToFind(familyName);
+ AutoTArray<gfxFontFamily*,10> cachedFamilies;
+ if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
+ if (cachedFamilies.IsEmpty()) {
+ return false;
+ }
+ aOutput->AppendElements(cachedFamilies);
+ return true;
+ }
+
+ // It wasn't in the cache, so we need to ask fontconfig...
+ const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
+ FcChar8* sentinelFirstFamily = nullptr;
+ nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
+ FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
+ FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
+ FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
+
+ // substitutions for font, -moz-sentinel pattern
+ nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
+ FcPatternAddString(fontWithSentinel, FC_FAMILY,
+ ToFcChar8Ptr(familyToFind.get()));
+ FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
+ FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
+
+ // Add all font family matches until reaching the sentinel.
+ FcChar8* substName = nullptr;
+ for (int i = 0;
+ FcPatternGetString(fontWithSentinel, FC_FAMILY,
+ i, &substName) == FcResultMatch;
+ i++)
+ {
+ NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
+ if (sentinelFirstFamily &&
+ FcStrCmp(substName, sentinelFirstFamily) == 0) {
+ break;
+ }
+ gfxPlatformFontList::FindAndAddFamilies(subst, &cachedFamilies);
+ }
+
+ // Cache the resulting list, so we don't have to do this again.
+ mFcSubstituteCache.Put(familyToFind, cachedFamilies);
+
+ if (cachedFamilies.IsEmpty()) {
+ return false;
+ }
+ aOutput->AppendElements(cachedFamilies);
+ return true;
+}
+
+bool
+gfxFcPlatformFontList::GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName)
+{
+ aFamilyName.Truncate();
+
+ // The fontconfig list of fonts includes generic family names in the
+ // font list. For these, just use the generic name.
+ if (aFontName.EqualsLiteral("serif") ||
+ aFontName.EqualsLiteral("sans-serif") ||
+ aFontName.EqualsLiteral("monospace")) {
+ aFamilyName.Assign(aFontName);
+ return true;
+ }
+
+ nsAutoRef<FcPattern> pat(FcPatternCreate());
+ if (!pat) {
+ return true;
+ }
+
+ nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
+ if (!os) {
+ return true;
+ }
+
+ // add the family name to the pattern
+ NS_ConvertUTF16toUTF8 familyName(aFontName);
+ FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(familyName.get()));
+
+ nsAutoRef<FcFontSet> givenFS(FcFontList(nullptr, pat, os));
+ if (!givenFS) {
+ return true;
+ }
+
+ // See if there is a font face with first family equal to the given family
+ // (needs to be in sync with names coming from GetFontList())
+ nsTArray<nsCString> candidates;
+ for (int i = 0; i < givenFS->nfont; i++) {
+ char* firstFamily;
+
+ if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &firstFamily) != FcResultMatch)
+ {
+ continue;
+ }
+
+ nsDependentCString first(firstFamily);
+ if (!candidates.Contains(first)) {
+ candidates.AppendElement(first);
+
+ if (familyName.Equals(first)) {
+ aFamilyName.Assign(aFontName);
+ return true;
+ }
+ }
+ }
+
+ // Because fontconfig conflates different family name types, need to
+ // double check that the candidate name is not simply a different
+ // name type. For example, if a font with nameID=16 "Minion Pro" and
+ // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
+ // family="Minion Pro" will return a set of patterns some of which
+ // will have a first family of "Minion Pro Caption". Ignore these
+ // patterns and use the first candidate that maps to a font set with
+ // the same number of faces and an identical set of patterns.
+ for (uint32_t j = 0; j < candidates.Length(); ++j) {
+ FcPatternDel(pat, FC_FAMILY);
+ FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
+
+ nsAutoRef<FcFontSet> candidateFS(FcFontList(nullptr, pat, os));
+ if (!candidateFS) {
+ return true;
+ }
+
+ if (candidateFS->nfont != givenFS->nfont) {
+ continue;
+ }
+
+ bool equal = true;
+ for (int i = 0; i < givenFS->nfont; ++i) {
+ if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
+ equal = false;
+ break;
+ }
+ }
+ if (equal) {
+ AppendUTF8toUTF16(candidates[j], aFamilyName);
+ return true;
+ }
+ }
+
+ // didn't find localized name, leave family name blank
+ return true;
+}
+
+static const char kFontNamePrefix[] = "font.name.";
+
+void
+gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
+ nsIAtom* aLanguage,
+ nsTArray<gfxFontFamily*>& aFamilyList)
+{
+ bool usePrefFontList = false;
+
+ // treat -moz-fixed as monospace
+ if (aGenericType == eFamily_moz_fixed) {
+ aGenericType = eFamily_monospace;
+ }
+
+ const char* generic = GetGenericName(aGenericType);
+ NS_ASSERTION(generic, "weird generic font type");
+ if (!generic) {
+ return;
+ }
+
+ // By default, most font prefs on Linux map to "use fontconfig"
+ // keywords. So only need to explicitly lookup font pref if
+ // non-default settings exist
+ NS_ConvertASCIItoUTF16 genericToLookup(generic);
+ if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
+ aLanguage == nsGkAtoms::x_math) {
+ nsIAtom* langGroup = GetLangGroup(aLanguage);
+ nsAutoCString langGroupStr;
+ if (langGroup) {
+ langGroup->ToUTF8String(langGroupStr);
+ }
+ nsAutoCString prefFontName(kFontNamePrefix);
+ prefFontName.Append(generic);
+ prefFontName.Append('.');
+ prefFontName.Append(langGroupStr);
+ nsAdoptingString fontlistValue = Preferences::GetString(prefFontName.get());
+ if (fontlistValue) {
+ if (!fontlistValue.EqualsLiteral("serif") &&
+ !fontlistValue.EqualsLiteral("sans-serif") &&
+ !fontlistValue.EqualsLiteral("monospace")) {
+ usePrefFontList = true;
+ } else {
+ // serif, sans-serif or monospace was specified
+ genericToLookup.Assign(fontlistValue);
+ }
+ }
+ }
+
+ // when pref fonts exist, use standard pref font lookup
+ if (usePrefFontList) {
+ return gfxPlatformFontList::AddGenericFonts(aGenericType,
+ aLanguage,
+ aFamilyList);
+ }
+
+ PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
+ NS_ASSERTION(prefFonts, "null generic font list");
+ aFamilyList.AppendElements(*prefFonts);
+}
+
+void
+gfxFcPlatformFontList::ClearLangGroupPrefFonts()
+{
+ ClearGenericMappings();
+ gfxPlatformFontList::ClearLangGroupPrefFonts();
+ mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
+}
+
+/* static */ FT_Library
+gfxFcPlatformFontList::GetFTLibrary()
+{
+ if (!sCairoFTLibrary) {
+ // Use cairo's FT_Library so that cairo takes care of shutdown of the
+ // FT_Library after it has destroyed its font_faces, and FT_Done_Face
+ // has been called on each FT_Face, at least until this bug is fixed:
+ // https://bugs.freedesktop.org/show_bug.cgi?id=18857
+ //
+ // Cairo keeps it's own FT_Library object for creating FT_Face
+ // instances, so use that. There's no simple API for accessing this
+ // so use the hacky method below of making a font and extracting
+ // the library pointer from that.
+
+ bool needsBold;
+ gfxFontStyle style;
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+ gfxFontFamily* family = pfl->GetDefaultFont(&style);
+ NS_ASSERTION(family, "couldn't find a default font family");
+ gfxFontEntry* fe = family->FindFontForStyle(style, needsBold);
+ if (!fe) {
+ return nullptr;
+ }
+ RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
+ if (!font) {
+ return nullptr;
+ }
+
+ gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
+ gfxFT2LockedFace face(ft2Font);
+ if (!face.get()) {
+ return nullptr;
+ }
+
+ sCairoFTLibrary = face.get()->glyph->library;
+ }
+
+ return sCairoFTLibrary;
+}
+
+gfxPlatformFontList::PrefFontList*
+gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
+ nsIAtom* aLanguage)
+{
+ // set up name
+ NS_ConvertUTF16toUTF8 generic(aGeneric);
+
+ nsAutoCString fcLang;
+ GetSampleLangForGroup(aLanguage, fcLang);
+ ToLowerCase(fcLang);
+
+ nsAutoCString genericLang(generic);
+ if (fcLang.Length() > 0) {
+ genericLang.Append('-');
+ }
+ genericLang.Append(fcLang);
+
+ // try to get the family from the cache
+ PrefFontList* prefFonts = mGenericMappings.Get(genericLang);
+ if (prefFonts) {
+ return prefFonts;
+ }
+
+ // if not found, ask fontconfig to pick the appropriate font
+ nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
+ FcPatternAddString(genericPattern, FC_FAMILY,
+ ToFcChar8Ptr(generic.get()));
+
+ // -- prefer scalable fonts
+ FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
+
+ // -- add the lang to the pattern
+ if (!fcLang.IsEmpty()) {
+ FcPatternAddString(genericPattern, FC_LANG,
+ ToFcChar8Ptr(fcLang.get()));
+ }
+
+ // -- perform substitutions
+ FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
+ FcDefaultSubstitute(genericPattern);
+
+ // -- sort to get the closest matches
+ FcResult result;
+ nsAutoRef<FcFontSet> faces(FcFontSort(nullptr, genericPattern, FcFalse,
+ nullptr, &result));
+
+ if (!faces) {
+ return nullptr;
+ }
+
+ // -- select the fonts to be used for the generic
+ prefFonts = new PrefFontList; // can be empty but in practice won't happen
+ uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
+ bool foundFontWithLang = false;
+ for (int i = 0; i < faces->nfont; i++) {
+ FcPattern* font = faces->fonts[i];
+ FcChar8* mappedGeneric = nullptr;
+
+ FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
+ if (mappedGeneric) {
+ NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
+ AutoTArray<gfxFontFamily*,1> genericFamilies;
+ if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
+ &genericFamilies)) {
+ MOZ_ASSERT(genericFamilies.Length() == 1,
+ "expected a single family");
+ if (!prefFonts->Contains(genericFamilies[0])) {
+ prefFonts->AppendElement(genericFamilies[0]);
+ bool foundLang =
+ !fcLang.IsEmpty() &&
+ PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
+ foundFontWithLang = foundFontWithLang || foundLang;
+ // check to see if the list is full
+ if (prefFonts->Length() >= limit) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // if no font in the list matches the lang, trim all but the first one
+ if (!prefFonts->IsEmpty() && !foundFontWithLang) {
+ prefFonts->TruncateLength(1);
+ }
+
+ mGenericMappings.Put(genericLang, prefFonts);
+ return prefFonts;
+}
+
+bool
+gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics()
+{
+ bool prefFontsUseOnlyGenerics = true;
+ uint32_t count;
+ char** names;
+ nsresult rv = Preferences::GetRootBranch()->
+ GetChildList(kFontNamePrefix, &count, &names);
+ if (NS_SUCCEEDED(rv) && count) {
+ for (size_t i = 0; i < count; i++) {
+ // Check whether all font.name prefs map to generic keywords
+ // and that the pref name and keyword match.
+ // Ex: font.name.serif.ar ==> "serif" (ok)
+ // Ex: font.name.serif.ar ==> "monospace" (return false)
+ // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
+
+ nsDependentCString prefName(names[i] +
+ ArrayLength(kFontNamePrefix) - 1);
+ nsCCharSeparatedTokenizer tokenizer(prefName, '.');
+ const nsDependentCSubstring& generic = tokenizer.nextToken();
+ const nsDependentCSubstring& langGroup = tokenizer.nextToken();
+ nsAdoptingCString fontPrefValue = Preferences::GetCString(names[i]);
+
+ if (!langGroup.EqualsLiteral("x-math") &&
+ !generic.Equals(fontPrefValue)) {
+ prefFontsUseOnlyGenerics = false;
+ break;
+ }
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
+ }
+ return prefFontsUseOnlyGenerics;
+}
+
+/* static */ void
+gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
+{
+ // check for font updates
+ FcInitBringUptoDate();
+
+ // update fontlist if current config changed
+ gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis);
+ FcConfig* current = FcConfigGetCurrent();
+ if (current != pfl->GetLastConfig()) {
+ pfl->UpdateFontList();
+ pfl->ForceGlobalReflow();
+ }
+}
+
+void
+gfxFcPlatformFontList::ActivateBundledFonts()
+{
+ if (!mBundledFontsInitialized) {
+ mBundledFontsInitialized = true;
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ return;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return;
+ }
+ if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
+ return;
+ }
+ }
+ if (!mBundledFontsPath.IsEmpty()) {
+ FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
+ }
+}
+
+#ifdef MOZ_WIDGET_GTK
+/***************************************************************************
+ *
+ * This function must be last in the file because it uses the system cairo
+ * library. Above this point the cairo library used is the tree cairo if
+ * MOZ_TREE_CAIRO.
+ */
+
+#if MOZ_TREE_CAIRO
+// Tree cairo symbols have different names. Disable their activation through
+// preprocessor macros.
+#undef cairo_ft_font_options_substitute
+
+// The system cairo functions are not declared because the include paths cause
+// the gdk headers to pick up the tree cairo.h.
+extern "C" {
+NS_VISIBILITY_DEFAULT void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+ FcPattern *pattern);
+}
+#endif
+
+static void
+ApplyGdkScreenFontOptions(FcPattern *aPattern)
+{
+ const cairo_font_options_t *options =
+ gdk_screen_get_font_options(gdk_screen_get_default());
+
+ cairo_ft_font_options_substitute(options, aPattern);
+}
+
+#endif // MOZ_WIDGET_GTK
diff --git a/system/graphics/thebes/gfxFcPlatformFontList.h b/system/graphics/thebes/gfxFcPlatformFontList.h
new file mode 100644
index 000000000..01929cc0e
--- /dev/null
+++ b/system/graphics/thebes/gfxFcPlatformFontList.h
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFXFCPLATFORMFONTLIST_H_
+#define GFXFCPLATFORMFONTLIST_H_
+
+#include "gfxFont.h"
+#include "gfxFontEntry.h"
+#include "gfxFT2FontBase.h"
+#include "gfxPlatformFontList.h"
+#include "mozilla/mozalloc.h"
+#include "nsClassHashtable.h"
+
+#include <fontconfig/fontconfig.h>
+#include "ft2build.h"
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include <cairo.h>
+#include <cairo-ft.h>
+
+#include "gfxFontconfigUtils.h" // xxx - only for nsAutoRefTraits<FcPattern>, etc.
+
+template <>
+class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
+{
+public:
+ static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
+};
+
+template <>
+class nsAutoRefTraits<FcConfig> : public nsPointerRefTraits<FcConfig>
+{
+public:
+ static void Release(FcConfig *ptr) { FcConfigDestroy(ptr); }
+ static void AddRef(FcConfig *ptr) { FcConfigReference(ptr); }
+};
+
+// Helper classes used for clearning out user font data when cairo font
+// face is destroyed. Since multiple faces may use the same data, be
+// careful to assure that the data is only cleared out when all uses
+// expire. The font entry object contains a refptr to FTUserFontData and
+// each cairo font created from that font entry contains a
+// FTUserFontDataRef with a refptr to that same FTUserFontData object.
+
+class FTUserFontData {
+public:
+ NS_INLINE_DECL_REFCOUNTING(FTUserFontData)
+
+ explicit FTUserFontData(FT_Face aFace, const uint8_t* aData)
+ : mFace(aFace), mFontData(aData)
+ {
+ }
+
+ const uint8_t *FontData() const { return mFontData; }
+
+private:
+ ~FTUserFontData()
+ {
+ FT_Done_Face(mFace);
+ if (mFontData) {
+ NS_Free((void*)mFontData);
+ }
+ }
+
+ FT_Face mFace;
+ const uint8_t *mFontData;
+};
+
+class FTUserFontDataRef {
+public:
+ explicit FTUserFontDataRef(FTUserFontData *aUserFontData)
+ : mUserFontData(aUserFontData)
+ {
+ }
+
+ static void Destroy(void* aData) {
+ FTUserFontDataRef* aUserFontDataRef =
+ static_cast<FTUserFontDataRef*>(aData);
+ delete aUserFontDataRef;
+ }
+
+private:
+ RefPtr<FTUserFontData> mUserFontData;
+};
+
+// The names for the font entry and font classes should really
+// the common 'Fc' abbreviation but the gfxPangoFontGroup code already
+// defines versions of these, so use the verbose name for now.
+
+class gfxFontconfigFontEntry : public gfxFontEntry {
+public:
+ // used for system fonts with explicit patterns
+ explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
+ FcPattern* aFontPattern,
+ bool aIgnoreFcCharmap);
+
+ // used for data fonts where the fontentry takes ownership
+ // of the font data and the FT_Face
+ explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t *aData,
+ FT_Face aFace);
+
+ // used for @font-face local system fonts with explicit patterns
+ explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
+ FcPattern* aFontPattern,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ FcPattern* GetPattern() { return mFontPattern; }
+
+ bool SupportsLangGroup(nsIAtom *aLangGroup) const override;
+
+ nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
+ bool TestCharacterMap(uint32_t aCh) override;
+
+ hb_blob_t* GetFontTable(uint32_t aTableTag) override;
+
+ void ForgetHBFace() override;
+ void ReleaseGrFace(gr_face* aFace) override;
+
+ double GetAspect();
+
+protected:
+ virtual ~gfxFontconfigFontEntry();
+
+ gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
+ bool aNeedsBold) override;
+
+ // helper method for creating cairo font from pattern
+ cairo_scaled_font_t*
+ CreateScaledFont(FcPattern* aRenderPattern,
+ gfxFloat aAdjustedSize,
+ const gfxFontStyle *aStyle,
+ bool aNeedsBold);
+
+ // override to pull data from FTFace
+ virtual nsresult
+ CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) override;
+
+ // if HB or GR faces are gone, close down the FT_Face
+ void MaybeReleaseFTFace();
+
+ // pattern for a single face of a family
+ nsCountedRef<FcPattern> mFontPattern;
+
+ // user font data, when needed
+ RefPtr<FTUserFontData> mUserFontData;
+
+ // FTFace - initialized when needed
+ FT_Face mFTFace;
+ bool mFTFaceInitialized;
+
+ // Whether TestCharacterMap should check the actual cmap rather than asking
+ // fontconfig about character coverage.
+ // We do this for app-bundled (rather than system) fonts, as they may
+ // include color glyphs that fontconfig would overlook, and for fonts
+ // loaded via @font-face.
+ bool mIgnoreFcCharmap;
+
+ double mAspect;
+
+ // data font
+ const uint8_t* mFontData;
+};
+
+class gfxFontconfigFontFamily : public gfxFontFamily {
+public:
+ explicit gfxFontconfigFontFamily(const nsAString& aName) :
+ gfxFontFamily(aName),
+ mContainsAppFonts(false),
+ mHasNonScalableFaces(false),
+ mForceScalable(false)
+ { }
+
+ void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) override;
+
+ // Families are constructed initially with just references to patterns.
+ // When necessary, these are enumerated within FindStyleVariations.
+ void AddFontPattern(FcPattern* aFontPattern);
+
+ void SetFamilyContainsAppFonts(bool aContainsAppFonts)
+ {
+ mContainsAppFonts = aContainsAppFonts;
+ }
+
+ void
+ FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
+ nsTArray<gfxFontEntry*>& aFontEntryList,
+ bool& aNeedsSyntheticBold) override;
+
+protected:
+ virtual ~gfxFontconfigFontFamily() { }
+
+ nsTArray<nsCountedRef<FcPattern> > mFontPatterns;
+
+ bool mContainsAppFonts;
+ bool mHasNonScalableFaces;
+ bool mForceScalable;
+};
+
+class gfxFontconfigFont : public gfxFontconfigFontBase {
+public:
+ gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
+ FcPattern *aPattern,
+ gfxFloat aAdjustedSize,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold);
+
+protected:
+ virtual ~gfxFontconfigFont();
+};
+
+class nsILanguageAtomService;
+
+class gfxFcPlatformFontList : public gfxPlatformFontList {
+public:
+ gfxFcPlatformFontList();
+
+ static gfxFcPlatformFontList* PlatformFontList() {
+ return static_cast<gfxFcPlatformFontList*>(sPlatformFontList);
+ }
+
+ // initialize font lists
+ virtual nsresult InitFontListForPlatform() override;
+
+ void GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts) override;
+
+
+ gfxFontEntry*
+ LookupLocalFont(const nsAString& aFontName, uint16_t aWeight,
+ int16_t aStretch, uint8_t aStyle) override;
+
+ gfxFontEntry*
+ MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength) override;
+
+ bool FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0) override;
+
+ bool GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName) override;
+
+ FcConfig* GetLastConfig() const { return mLastConfig; }
+
+ // override to use fontconfig lookup for generics
+ void AddGenericFonts(mozilla::FontFamilyType aGenericType,
+ nsIAtom* aLanguage,
+ nsTArray<gfxFontFamily*>& aFamilyList) override;
+
+ void ClearLangGroupPrefFonts() override;
+
+ // clear out cached generic-lang ==> family-list mappings
+ void ClearGenericMappings() {
+ mGenericMappings.Clear();
+ }
+
+ static FT_Library GetFTLibrary();
+
+protected:
+ virtual ~gfxFcPlatformFontList();
+
+ // Add all the font families found in a font set.
+ // aAppFonts indicates whether this is the system or application fontset.
+ void AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts);
+
+ // figure out which families fontconfig maps a generic to
+ // (aGeneric assumed already lowercase)
+ PrefFontList* FindGenericFamilies(const nsAString& aGeneric,
+ nsIAtom* aLanguage);
+
+ // are all pref font settings set to use fontconfig generics?
+ bool PrefFontListsUseOnlyGenerics();
+
+ static void CheckFontUpdates(nsITimer *aTimer, void *aThis);
+
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
+ void ActivateBundledFonts();
+ nsCString mBundledFontsPath;
+ bool mBundledFontsInitialized;
+
+ // to avoid enumerating all fonts, maintain a mapping of local font
+ // names to family
+ nsBaseHashtable<nsStringHashKey,
+ nsCountedRef<FcPattern>,
+ FcPattern*> mLocalNames;
+
+ // caching generic/lang ==> font family list
+ nsClassHashtable<nsCStringHashKey,
+ PrefFontList> mGenericMappings;
+
+ // Caching family lookups as found by FindAndAddFamilies after resolving
+ // substitutions. The gfxFontFamily objects cached here are owned by the
+ // gfxFcPlatformFontList via its mFamilies table; note that if the main
+ // font list is rebuilt (e.g. due to a fontconfig configuration change),
+ // these pointers will be invalidated. InitFontList() flushes the cache
+ // in this case.
+ nsDataHashtable<nsCStringHashKey,
+ nsTArray<gfxFontFamily*>> mFcSubstituteCache;
+
+ nsCOMPtr<nsITimer> mCheckFontUpdatesTimer;
+ nsCountedRef<FcConfig> mLastConfig;
+
+ // By default, font prefs under Linux are set to simply lookup
+ // via fontconfig the appropriate font for serif/sans-serif/monospace.
+ // Rather than check each time a font pref is used, check them all at startup
+ // and set a boolean to flag the case that non-default user font prefs exist
+ // Note: langGroup == x-math is handled separately
+ bool mAlwaysUseFontconfigGenerics;
+
+ static FT_Library sCairoFTLibrary;
+};
+
+#endif /* GFXPLATFORMFONTLIST_H_ */
diff --git a/system/graphics/thebes/gfxFont-Impl.h b/system/graphics/thebes/gfxFont-Impl.h
new file mode 100644
index 000000000..2661d1f40
--- /dev/null
+++ b/system/graphics/thebes/gfxFont-Impl.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_IMPL_H
+#define GFX_FONT_IMPL_H
+
+#include "mozilla/DebugOnly.h"
+using mozilla::DebugOnly;
+
+#ifdef __GNUC__
+#define GFX_MAYBE_UNUSED __attribute__((unused))
+#else
+#define GFX_MAYBE_UNUSED
+#endif
+
+template<typename T>
+gfxShapedWord*
+gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aLength,
+ uint32_t aHash,
+ Script aRunScript,
+ bool aVertical,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags,
+ gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
+{
+ // if the cache is getting too big, flush it and start over
+ uint32_t wordCacheMaxEntries =
+ gfxPlatform::GetPlatform()->WordCacheMaxEntries();
+ if (mWordCache->Count() > wordCacheMaxEntries) {
+ NS_WARNING("flushing shaped-word cache");
+ ClearCachedWords();
+ }
+
+ // if there's a cached entry for this word, just return it
+ CacheHashKey key(aText, aLength, aHash,
+ aRunScript,
+ aAppUnitsPerDevUnit,
+ aFlags);
+
+ CacheHashEntry *entry = mWordCache->PutEntry(key);
+ if (!entry) {
+ NS_WARNING("failed to create word cache entry - expect missing text");
+ return nullptr;
+ }
+ gfxShapedWord* sw = entry->mShapedWord.get();
+
+ if (sw) {
+ sw->ResetAge();
+#ifndef RELEASE_OR_BETA
+ if (aTextPerf) {
+ aTextPerf->current.wordCacheHit++;
+ }
+#endif
+ return sw;
+ }
+
+#ifndef RELEASE_OR_BETA
+ if (aTextPerf) {
+ aTextPerf->current.wordCacheMiss++;
+ }
+#endif
+
+ sw = gfxShapedWord::Create(aText, aLength, aRunScript, aAppUnitsPerDevUnit,
+ aFlags);
+ entry->mShapedWord.reset(sw);
+ if (!sw) {
+ NS_WARNING("failed to create gfxShapedWord - expect missing text");
+ return nullptr;
+ }
+
+ DebugOnly<bool> ok =
+ ShapeText(aDrawTarget, aText, 0, aLength, aRunScript, aVertical, sw);
+
+ NS_WARNING_ASSERTION(ok, "failed to shape word - expect garbled text");
+
+ return sw;
+}
+
+#endif // GFX_FONT_IMPL_H \ No newline at end of file
diff --git a/system/graphics/thebes/gfxFont.cpp b/system/graphics/thebes/gfxFont.cpp
new file mode 100644
index 000000000..dba322521
--- /dev/null
+++ b/system/graphics/thebes/gfxFont.cpp
@@ -0,0 +1,3958 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFont.h"
+#include "gfxFont-Impl.h"
+
+#include "mozilla/BinarySearch.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/SVGContextPaint.h"
+
+#include "mozilla/Logging.h"
+
+#include "nsITimer.h"
+
+#include "gfxGlyphExtents.h"
+#include "gfxPlatform.h"
+#include "gfxTextRun.h"
+#include "nsGkAtoms.h"
+
+#include "gfxTypes.h"
+#include "gfxContext.h"
+#include "gfxFontMissingGlyphs.h"
+#include "gfxGraphiteShaper.h"
+#include "gfxHarfBuzzShaper.h"
+#include "gfxUserFontSet.h"
+#include "nsIUGenCategory.h"
+#include "nsSpecialCasingData.h"
+#include "nsTextRunTransformations.h"
+#include "nsUnicodeProperties.h"
+#include "nsStyleConsts.h"
+#include "mozilla/AppUnits.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "gfxMathTable.h"
+#include "gfxSVGGlyphs.h"
+#include "gfx2DGlue.h"
+
+#include "GreekCasing.h"
+
+#include "cairo.h"
+
+#ifdef XP_WIN
+#include "cairo-win32.h"
+#include "gfxWindowsPlatform.h"
+#endif
+
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-ot.h"
+#include "graphite2/Font.h"
+
+#include <algorithm>
+#include <limits>
+#include <cmath>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+using mozilla::services::GetObserverService;
+
+gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
+
+#ifdef DEBUG_roc
+#define DEBUG_TEXT_RUN_STORAGE_METRICS
+#endif
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+uint32_t gTextRunStorageHighWaterMark = 0;
+uint32_t gTextRunStorage = 0;
+uint32_t gFontCount = 0;
+uint32_t gGlyphExtentsCount = 0;
+uint32_t gGlyphExtentsWidthsTotalSize = 0;
+uint32_t gGlyphExtentsSetupEagerSimple = 0;
+uint32_t gGlyphExtentsSetupEagerTight = 0;
+uint32_t gGlyphExtentsSetupLazyTight = 0;
+uint32_t gGlyphExtentsSetupFallBackToTight = 0;
+#endif
+
+#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug)
+
+
+/*
+ * gfxFontCache - global cache of gfxFont instances.
+ * Expires unused fonts after a short interval;
+ * notifies fonts to age their cached shaped-word records;
+ * observes memory-pressure notification and tells fonts to clear their
+ * shaped-word caches to free up memory.
+ */
+
+MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
+
+NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
+
+NS_IMETHODIMP
+gfxFontCache::MemoryReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
+{
+ FontCacheSizes sizes;
+
+ gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
+ &sizes);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/font-cache", KIND_HEAP, UNITS_BYTES,
+ sizes.mFontInstances,
+ "Memory used for active font instances.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/font-shaped-words", KIND_HEAP, UNITS_BYTES,
+ sizes.mShapedWords,
+ "Memory used to cache shaped glyph data.");
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
+
+NS_IMETHODIMP
+gfxFontCache::Observer::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData)
+{
+ if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
+ gfxFontCache *fontCache = gfxFontCache::GetCache();
+ if (fontCache) {
+ fontCache->FlushShapedWordCaches();
+ }
+ } else {
+ NS_NOTREACHED("unexpected notification topic");
+ }
+ return NS_OK;
+}
+
+nsresult
+gfxFontCache::Init()
+{
+ NS_ASSERTION(!gGlobalCache, "Where did this come from?");
+ gGlobalCache = new gfxFontCache();
+ if (!gGlobalCache) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ RegisterStrongMemoryReporter(new MemoryReporter());
+ return NS_OK;
+}
+
+void
+gfxFontCache::Shutdown()
+{
+ delete gGlobalCache;
+ gGlobalCache = nullptr;
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
+ printf("Total number of fonts=%d\n", gFontCount);
+ printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
+ int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
+ printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
+ printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
+ printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
+ printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
+ printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
+#endif
+}
+
+gfxFontCache::gfxFontCache()
+ : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000,
+ "gfxFontCache")
+{
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) {
+ obs->AddObserver(new Observer, "memory-pressure", false);
+ }
+
+#ifndef RELEASE_OR_BETA
+ // Currently disabled for release builds, due to unexplained crashes
+ // during expiration; see bug 717175 & 894798.
+ mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (mWordCacheExpirationTimer) {
+ mWordCacheExpirationTimer->
+ InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
+ SHAPED_WORD_TIMEOUT_SECONDS * 1000,
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+#endif
+}
+
+gfxFontCache::~gfxFontCache()
+{
+ // Ensure the user font cache releases its references to font entries,
+ // so they aren't kept alive after the font instances and font-list
+ // have been shut down.
+ gfxUserFontSet::UserFontCache::Shutdown();
+
+ if (mWordCacheExpirationTimer) {
+ mWordCacheExpirationTimer->Cancel();
+ mWordCacheExpirationTimer = nullptr;
+ }
+
+ // Expire everything that has a zero refcount, so we don't leak them.
+ AgeAllGenerations();
+ // All fonts should be gone.
+ NS_WARNING_ASSERTION(mFonts.Count() == 0,
+ "Fonts still alive while shutting down gfxFontCache");
+ // Note that we have to delete everything through the expiration
+ // tracker, since there might be fonts not in the hashtable but in
+ // the tracker.
+}
+
+bool
+gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
+{
+ const gfxCharacterMap* fontUnicodeRangeMap = mFont->GetUnicodeRangeMap();
+ return aKey->mFontEntry == mFont->GetFontEntry() &&
+ aKey->mStyle->Equals(*mFont->GetStyle()) &&
+ ((!aKey->mUnicodeRangeMap && !fontUnicodeRangeMap) ||
+ (aKey->mUnicodeRangeMap && fontUnicodeRangeMap &&
+ aKey->mUnicodeRangeMap->Equals(fontUnicodeRangeMap)));
+}
+
+already_AddRefed<gfxFont>
+gfxFontCache::Lookup(const gfxFontEntry* aFontEntry,
+ const gfxFontStyle* aStyle,
+ const gfxCharacterMap* aUnicodeRangeMap)
+{
+ Key key(aFontEntry, aStyle, aUnicodeRangeMap);
+ HashEntry *entry = mFonts.GetEntry(key);
+
+ if (!entry)
+ return nullptr;
+
+ RefPtr<gfxFont> font = entry->mFont;
+ return font.forget();
+}
+
+void
+gfxFontCache::AddNew(gfxFont *aFont)
+{
+ Key key(aFont->GetFontEntry(), aFont->GetStyle(),
+ aFont->GetUnicodeRangeMap());
+ HashEntry *entry = mFonts.PutEntry(key);
+ if (!entry)
+ return;
+ gfxFont *oldFont = entry->mFont;
+ entry->mFont = aFont;
+ // Assert that we can find the entry we just put in (this fails if the key
+ // has a NaN float value in it, e.g. 'sizeAdjust').
+ MOZ_ASSERT(entry == mFonts.GetEntry(key));
+ // If someone's asked us to replace an existing font entry, then that's a
+ // bit weird, but let it happen, and expire the old font if it's not used.
+ if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
+ // if oldFont == aFont, recount should be > 0,
+ // so we shouldn't be here.
+ NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
+ NotifyExpired(oldFont);
+ }
+}
+
+void
+gfxFontCache::NotifyReleased(gfxFont *aFont)
+{
+ nsresult rv = AddObject(aFont);
+ if (NS_FAILED(rv)) {
+ // We couldn't track it for some reason. Kill it now.
+ DestroyFont(aFont);
+ }
+ // Note that we might have fonts that aren't in the hashtable, perhaps because
+ // of OOM adding to the hashtable or because someone did an AddNew where
+ // we already had a font. These fonts are added to the expiration tracker
+ // anyway, even though Lookup can't resurrect them. Eventually they will
+ // expire and be deleted.
+}
+
+void
+gfxFontCache::NotifyExpired(gfxFont *aFont)
+{
+ aFont->ClearCachedWords();
+ RemoveObject(aFont);
+ DestroyFont(aFont);
+}
+
+void
+gfxFontCache::DestroyFont(gfxFont *aFont)
+{
+ Key key(aFont->GetFontEntry(), aFont->GetStyle(),
+ aFont->GetUnicodeRangeMap());
+ HashEntry *entry = mFonts.GetEntry(key);
+ if (entry && entry->mFont == aFont) {
+ mFonts.RemoveEntry(entry);
+ }
+ NS_ASSERTION(aFont->GetRefCount() == 0,
+ "Destroying with non-zero ref count!");
+ delete aFont;
+}
+
+/*static*/
+void
+gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
+{
+ gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
+ for (auto it = cache->mFonts.Iter(); !it.Done(); it.Next()) {
+ it.Get()->mFont->AgeCachedWords();
+ }
+}
+
+void
+gfxFontCache::FlushShapedWordCaches()
+{
+ for (auto it = mFonts.Iter(); !it.Done(); it.Next()) {
+ it.Get()->mFont->ClearCachedWords();
+ }
+}
+
+void
+gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ // TODO: add the overhead of the expiration tracker (generation arrays)
+
+ aSizes->mFontInstances += mFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mFonts.ConstIter(); !iter.Done(); iter.Next()) {
+ iter.Get()->mFont->AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ }
+}
+
+void
+gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+#define MAX_SSXX_VALUE 99
+#define MAX_CVXX_VALUE 99
+
+static void
+LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
+ const nsAString& aFamily,
+ const nsTArray<gfxAlternateValue>& altValue,
+ nsTArray<gfxFontFeature>& aFontFeatures)
+{
+ uint32_t numAlternates = altValue.Length();
+ for (uint32_t i = 0; i < numAlternates; i++) {
+ const gfxAlternateValue& av = altValue.ElementAt(i);
+ AutoTArray<uint32_t,4> values;
+
+ // map <family, name, feature> ==> <values>
+ bool found =
+ featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
+ av.value, values);
+ uint32_t numValues = values.Length();
+
+ // nothing defined, skip
+ if (!found || numValues == 0) {
+ continue;
+ }
+
+ gfxFontFeature feature;
+ if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
+ NS_ASSERTION(numValues <= 2,
+ "too many values allowed for character-variant");
+ // character-variant(12 3) ==> 'cv12' = 3
+ uint32_t nn = values.ElementAt(0);
+ // ignore values greater than 99
+ if (nn == 0 || nn > MAX_CVXX_VALUE) {
+ continue;
+ }
+ feature.mValue = 1;
+ if (numValues > 1) {
+ feature.mValue = values.ElementAt(1);
+ }
+ feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
+ aFontFeatures.AppendElement(feature);
+
+ } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
+ // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
+ feature.mValue = 1;
+ for (uint32_t v = 0; v < numValues; v++) {
+ uint32_t nn = values.ElementAt(v);
+ if (nn == 0 || nn > MAX_SSXX_VALUE) {
+ continue;
+ }
+ feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
+ aFontFeatures.AppendElement(feature);
+ }
+
+ } else {
+ NS_ASSERTION(numValues == 1,
+ "too many values for font-specific font-variant-alternates");
+ feature.mValue = values.ElementAt(0);
+
+ switch (av.alternate) {
+ case NS_FONT_VARIANT_ALTERNATES_STYLISTIC: // salt
+ feature.mTag = HB_TAG('s','a','l','t');
+ break;
+ case NS_FONT_VARIANT_ALTERNATES_SWASH: // swsh, cswh
+ feature.mTag = HB_TAG('s','w','s','h');
+ aFontFeatures.AppendElement(feature);
+ feature.mTag = HB_TAG('c','s','w','h');
+ break;
+ case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
+ feature.mTag = HB_TAG('o','r','n','m');
+ break;
+ case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
+ feature.mTag = HB_TAG('n','a','l','t');
+ break;
+ default:
+ feature.mTag = 0;
+ break;
+ }
+
+ NS_ASSERTION(feature.mTag, "unsupported alternate type");
+ if (!feature.mTag) {
+ continue;
+ }
+ aFontFeatures.AppendElement(feature);
+ }
+ }
+}
+
+/* static */ void
+gfxFontShaper::MergeFontFeatures(
+ const gfxFontStyle *aStyle,
+ const nsTArray<gfxFontFeature>& aFontFeatures,
+ bool aDisableLigatures,
+ const nsAString& aFamilyName,
+ bool aAddSmallCaps,
+ void (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
+ void* aHandleFeatureData)
+{
+ uint32_t numAlts = aStyle->alternateValues.Length();
+ const nsTArray<gfxFontFeature>& styleRuleFeatures =
+ aStyle->featureSettings;
+
+ // Bail immediately if nothing to do, which is the common case.
+ if (styleRuleFeatures.IsEmpty() &&
+ aFontFeatures.IsEmpty() &&
+ !aDisableLigatures &&
+ aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
+ aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
+ numAlts == 0) {
+ return;
+ }
+
+ nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
+
+ // Ligature features are enabled by default in the generic shaper,
+ // so we explicitly turn them off if necessary (for letter-spacing)
+ if (aDisableLigatures) {
+ mergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
+ mergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
+ }
+
+ // add feature values from font
+ uint32_t i, count;
+
+ count = aFontFeatures.Length();
+ for (i = 0; i < count; i++) {
+ const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
+ mergedFeatures.Put(feature.mTag, feature.mValue);
+ }
+
+ // font-variant-caps - handled here due to the need for fallback handling
+ // petite caps cases can fallback to appropriate smallcaps
+ uint32_t variantCaps = aStyle->variantCaps;
+ switch (variantCaps) {
+ case NS_FONT_VARIANT_CAPS_NORMAL:
+ break;
+
+ case NS_FONT_VARIANT_CAPS_ALLSMALL:
+ mergedFeatures.Put(HB_TAG('c','2','s','c'), 1);
+ // fall through to the small-caps case
+ MOZ_FALLTHROUGH;
+
+ case NS_FONT_VARIANT_CAPS_SMALLCAPS:
+ mergedFeatures.Put(HB_TAG('s','m','c','p'), 1);
+ break;
+
+ case NS_FONT_VARIANT_CAPS_ALLPETITE:
+ mergedFeatures.Put(aAddSmallCaps ? HB_TAG('c','2','s','c') :
+ HB_TAG('c','2','p','c'), 1);
+ // fall through to the petite-caps case
+ MOZ_FALLTHROUGH;
+
+ case NS_FONT_VARIANT_CAPS_PETITECAPS:
+ mergedFeatures.Put(aAddSmallCaps ? HB_TAG('s','m','c','p') :
+ HB_TAG('p','c','a','p'), 1);
+ break;
+
+ case NS_FONT_VARIANT_CAPS_TITLING:
+ mergedFeatures.Put(HB_TAG('t','i','t','l'), 1);
+ break;
+
+ case NS_FONT_VARIANT_CAPS_UNICASE:
+ mergedFeatures.Put(HB_TAG('u','n','i','c'), 1);
+ break;
+
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected variantCaps");
+ break;
+ }
+
+ // font-variant-position - handled here due to the need for fallback
+ switch (aStyle->variantSubSuper) {
+ case NS_FONT_VARIANT_POSITION_NORMAL:
+ break;
+ case NS_FONT_VARIANT_POSITION_SUPER:
+ mergedFeatures.Put(HB_TAG('s','u','p','s'), 1);
+ break;
+ case NS_FONT_VARIANT_POSITION_SUB:
+ mergedFeatures.Put(HB_TAG('s','u','b','s'), 1);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper");
+ break;
+ }
+
+ // add font-specific feature values from style rules
+ if (aStyle->featureValueLookup && numAlts > 0) {
+ AutoTArray<gfxFontFeature,4> featureList;
+
+ // insert list of alternate feature settings
+ LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
+ aStyle->alternateValues, featureList);
+
+ count = featureList.Length();
+ for (i = 0; i < count; i++) {
+ const gfxFontFeature& feature = featureList.ElementAt(i);
+ mergedFeatures.Put(feature.mTag, feature.mValue);
+ }
+ }
+
+ // add feature values from style rules
+ count = styleRuleFeatures.Length();
+ for (i = 0; i < count; i++) {
+ const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
+ mergedFeatures.Put(feature.mTag, feature.mValue);
+ }
+
+ if (mergedFeatures.Count() != 0) {
+ for (auto iter = mergedFeatures.Iter(); !iter.Done(); iter.Next()) {
+ aHandleFeature(iter.Key(), iter.Data(), aHandleFeatureData);
+ }
+ }
+}
+
+// Work out whether cairo will snap inter-glyph spacing to pixels.
+//
+// Layout does not align text to pixel boundaries, so, with font drawing
+// backends that snap glyph positions to pixels, it is important that
+// inter-glyph spacing within words is always an integer number of pixels.
+// This ensures that the drawing backend snaps all of the word's glyphs in the
+// same direction and so inter-glyph spacing remains the same.
+//
+/* static */ void
+gfxFontShaper::GetRoundOffsetsToPixels(DrawTarget* aDrawTarget,
+ bool* aRoundX, bool* aRoundY)
+{
+ *aRoundX = false;
+ // Could do something fancy here for ScaleFactors of
+ // AxisAlignedTransforms, but we leave things simple.
+ // Not much point rounding if a matrix will mess things up anyway.
+ // Also return false for non-cairo contexts.
+ if (aDrawTarget->GetTransform().HasNonTranslation()) {
+ *aRoundY = false;
+ return;
+ }
+
+ // All raster backends snap glyphs to pixels vertically.
+ // Print backends set CAIRO_HINT_METRICS_OFF.
+ *aRoundY = true;
+
+ cairo_t* cr = gfxFont::RefCairo(aDrawTarget);
+ cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
+
+ // bug 1198921 - this sometimes fails under Windows for whatver reason
+ NS_ASSERTION(scaled_font, "null cairo scaled font should never be returned "
+ "by cairo_get_scaled_font");
+ if (!scaled_font) {
+ *aRoundX = true; // default to the same as the fallback path below
+ return;
+ }
+
+ // Sometimes hint metrics gets set for us, most notably for printing.
+ cairo_hint_metrics_t hint_metrics =
+ cairo_scaled_font_get_hint_metrics(scaled_font);
+
+ switch (hint_metrics) {
+ case CAIRO_HINT_METRICS_OFF:
+ *aRoundY = false;
+ return;
+ case CAIRO_HINT_METRICS_DEFAULT:
+ // Here we mimic what cairo surface/font backends do. Printing
+ // surfaces have already been handled by hint_metrics. The
+ // fallback show_glyphs implementation composites pixel-aligned
+ // glyph surfaces, so we just pick surface/font combinations that
+ // override this.
+ switch (cairo_scaled_font_get_type(scaled_font)) {
+#if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
+ case CAIRO_FONT_TYPE_DWRITE:
+ // show_glyphs is implemented on the font and so is used for
+ // all surface types; however, it may pixel-snap depending on
+ // the dwrite rendering mode
+ if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
+ gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
+ DWRITE_MEASURING_MODE_NATURAL) {
+ return;
+ }
+ MOZ_FALLTHROUGH;
+#endif
+ case CAIRO_FONT_TYPE_QUARTZ:
+ // Quartz surfaces implement show_glyphs for Quartz fonts
+ if (cairo_surface_get_type(cairo_get_target(cr)) ==
+ CAIRO_SURFACE_TYPE_QUARTZ) {
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case CAIRO_HINT_METRICS_ON:
+ break;
+ }
+ *aRoundX = true;
+}
+
+void
+gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
+ const char16_t *aString,
+ uint32_t aLength)
+{
+ CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+
+ gfxTextRun::CompressedGlyph extendCluster;
+ extendCluster.SetComplex(false, true, 0);
+
+ ClusterIterator iter(aString, aLength);
+
+ // the ClusterIterator won't be able to tell us if the string
+ // _begins_ with a cluster-extender, so we handle that here
+ if (aLength) {
+ uint32_t ch = *aString;
+ if (aLength > 1 && NS_IS_HIGH_SURROGATE(ch) &&
+ NS_IS_LOW_SURROGATE(aString[1])) {
+ ch = SURROGATE_TO_UCS4(ch, aString[1]);
+ }
+ if (IsClusterExtender(ch)) {
+ *glyphs = extendCluster;
+ }
+ }
+
+ while (!iter.AtEnd()) {
+ if (*iter == char16_t(' ')) {
+ glyphs->SetIsSpace();
+ }
+ // advance iter to the next cluster-start (or end of text)
+ iter.Next();
+ // step past the first char of the cluster
+ aString++;
+ glyphs++;
+ // mark all the rest as cluster-continuations
+ while (aString < iter) {
+ *glyphs = extendCluster;
+ glyphs++;
+ aString++;
+ }
+ }
+}
+
+void
+gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
+ const uint8_t *aString,
+ uint32_t aLength)
+{
+ CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+ const uint8_t *limit = aString + aLength;
+
+ while (aString < limit) {
+ if (*aString == uint8_t(' ')) {
+ glyphs->SetIsSpace();
+ }
+ aString++;
+ glyphs++;
+ }
+}
+
+gfxShapedText::DetailedGlyph *
+gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
+{
+ NS_ASSERTION(aIndex < GetLength(), "Index out of range");
+
+ if (!mDetailedGlyphs) {
+ mDetailedGlyphs = MakeUnique<DetailedGlyphStore>();
+ }
+
+ return mDetailedGlyphs->Allocate(aIndex, aCount);
+}
+
+void
+gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
+ const DetailedGlyph *aGlyphs)
+{
+ NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
+ NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
+ "First character can't be a ligature continuation!");
+
+ uint32_t glyphCount = aGlyph.GetGlyphCount();
+ if (glyphCount > 0) {
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
+ memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
+ }
+ GetCharacterGlyphs()[aIndex] = aGlyph;
+}
+
+#define ZWNJ 0x200C
+#define ZWJ 0x200D
+static inline bool
+IsDefaultIgnorable(uint32_t aChar)
+{
+ return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
+ aChar == ZWNJ || aChar == ZWJ;
+}
+
+void
+gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
+{
+ uint8_t category = GetGeneralCategory(aChar);
+ if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
+ category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+ {
+ GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
+ }
+
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+
+ details->mGlyphID = aChar;
+ if (IsDefaultIgnorable(aChar)) {
+ // Setting advance width to zero will prevent drawing the hexbox
+ details->mAdvance = 0;
+ } else {
+ gfxFloat width =
+ std::max(aFont->GetMetrics(gfxFont::eHorizontal).aveCharWidth,
+ gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
+ mAppUnitsPerDevUnit)));
+ details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
+ }
+ details->mXOffset = 0;
+ details->mYOffset = 0;
+ GetCharacterGlyphs()[aIndex].SetMissing(1);
+}
+
+bool
+gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
+{
+ if (IsDefaultIgnorable(aCh)) {
+ // There are a few default-ignorables of Letter category (currently,
+ // just the Hangul filler characters) that we'd better not discard
+ // if they're followed by additional characters in the same cluster.
+ // Some fonts use them to carry the width of a whole cluster of
+ // combining jamos; see bug 1238243.
+ if (GetGenCategory(aCh) == nsIUGenCategory::kLetter &&
+ aIndex + 1 < GetLength() &&
+ !GetCharacterGlyphs()[aIndex + 1].IsClusterStart()) {
+ return false;
+ }
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+ details->mGlyphID = aCh;
+ details->mAdvance = 0;
+ details->mXOffset = 0;
+ details->mYOffset = 0;
+ GetCharacterGlyphs()[aIndex].SetMissing(1);
+ return true;
+ }
+ return false;
+}
+
+void
+gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+ uint32_t aOffset,
+ uint32_t aLength)
+{
+ uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
+ CompressedGlyph *charGlyphs = GetCharacterGlyphs();
+ for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
+ CompressedGlyph *glyphData = charGlyphs + i;
+ if (glyphData->IsSimpleGlyph()) {
+ // simple glyphs ==> just add the advance
+ int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
+ if (CompressedGlyph::IsSimpleAdvance(advance)) {
+ glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
+ } else {
+ // rare case, tested by making this the default
+ uint32_t glyphIndex = glyphData->GetSimpleGlyph();
+ glyphData->SetComplex(true, true, 1);
+ DetailedGlyph detail = {glyphIndex, advance, 0, 0};
+ SetGlyphs(i, *glyphData, &detail);
+ }
+ } else {
+ // complex glyphs ==> add offset at cluster/ligature boundaries
+ uint32_t detailedLength = glyphData->GetGlyphCount();
+ if (detailedLength) {
+ DetailedGlyph *details = GetDetailedGlyphs(i);
+ if (!details) {
+ continue;
+ }
+ if (IsRightToLeft()) {
+ details[0].mAdvance += synAppUnitOffset;
+ } else {
+ details[detailedLength - 1].mAdvance += synAppUnitOffset;
+ }
+ }
+ }
+ }
+}
+
+void
+gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
+{
+ mAscent = std::max(mAscent, aOther.mAscent);
+ mDescent = std::max(mDescent, aOther.mDescent);
+ if (aOtherIsOnLeft) {
+ mBoundingBox =
+ (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
+ } else {
+ mBoundingBox =
+ mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
+ }
+ mAdvanceWidth += aOther.mAdvanceWidth;
+}
+
+gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+ AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
+ mScaledFont(aScaledFont),
+ mFontEntry(aFontEntry), mIsValid(true),
+ mApplySyntheticBold(false),
+ mMathInitialized(false),
+ mStyle(*aFontStyle),
+ mAdjustedSize(0.0),
+ mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
+ mAntialiasOption(anAAOption)
+{
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ ++gFontCount;
+#endif
+ mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
+}
+
+gfxFont::~gfxFont()
+{
+ mFontEntry->NotifyFontDestroyed(this);
+
+ if (mGlyphChangeObservers) {
+ for (auto it = mGlyphChangeObservers->Iter(); !it.Done(); it.Next()) {
+ it.Get()->GetKey()->ForgetFont();
+ }
+ }
+}
+
+gfxFloat
+gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
+{
+ if (!SetupCairoFont(aDrawTarget)) {
+ return 0;
+ }
+ if (ProvidesGlyphWidths()) {
+ return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0;
+ }
+ if (mFUnitsConvFactor < 0.0f) {
+ GetMetrics(eHorizontal);
+ }
+ NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
+ "missing font unit conversion factor");
+ if (!mHarfBuzzShaper) {
+ mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
+ }
+ gfxHarfBuzzShaper* shaper =
+ static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
+ if (!shaper->Initialize()) {
+ return 0;
+ }
+ return shaper->GetGlyphHAdvance(aGID) / 65536.0;
+}
+
+static void
+CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
+ uint32_t aFeatureIndex, hb_set_t *aLookups)
+{
+ uint32_t lookups[32];
+ uint32_t i, len, offset;
+
+ offset = 0;
+ do {
+ len = ArrayLength(lookups);
+ hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
+ offset, &len, lookups);
+ for (i = 0; i < len; i++) {
+ hb_set_add(aLookups, lookups[i]);
+ }
+ offset += len;
+ } while (len == ArrayLength(lookups));
+}
+
+static void
+CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
+ const nsTHashtable<nsUint32HashKey>&
+ aSpecificFeatures,
+ hb_set_t *aOtherLookups,
+ hb_set_t *aSpecificFeatureLookups,
+ uint32_t aScriptIndex, uint32_t aLangIndex)
+{
+ uint32_t reqFeatureIndex;
+ if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
+ aScriptIndex,
+ aLangIndex,
+ &reqFeatureIndex)) {
+ CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
+ aOtherLookups);
+ }
+
+ uint32_t featureIndexes[32];
+ uint32_t i, len, offset;
+
+ offset = 0;
+ do {
+ len = ArrayLength(featureIndexes);
+ hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
+ aScriptIndex, aLangIndex,
+ offset, &len, featureIndexes);
+
+ for (i = 0; i < len; i++) {
+ uint32_t featureIndex = featureIndexes[i];
+
+ // get the feature tag
+ hb_tag_t featureTag;
+ uint32_t tagLen = 1;
+ hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
+ aScriptIndex, aLangIndex,
+ offset + i, &tagLen,
+ &featureTag);
+
+ // collect lookups
+ hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
+ aSpecificFeatureLookups : aOtherLookups;
+ CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
+ }
+ offset += len;
+ } while (len == ArrayLength(featureIndexes));
+}
+
+static bool
+HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
+ hb_tag_t aScriptTag, uint32_t aScriptIndex,
+ uint16_t aGlyph,
+ const nsTHashtable<nsUint32HashKey>&
+ aDefaultFeatures,
+ bool& aHasDefaultFeatureWithGlyph)
+{
+ uint32_t numLangs, lang;
+ hb_set_t *defaultFeatureLookups = hb_set_create();
+ hb_set_t *nonDefaultFeatureLookups = hb_set_create();
+
+ // default lang
+ CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
+ nonDefaultFeatureLookups, defaultFeatureLookups,
+ aScriptIndex,
+ HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
+
+ // iterate over langs
+ numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
+ aScriptIndex, 0,
+ nullptr, nullptr);
+ for (lang = 0; lang < numLangs; lang++) {
+ CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
+ nonDefaultFeatureLookups,
+ defaultFeatureLookups,
+ aScriptIndex, lang);
+ }
+
+ // look for the glyph among default feature lookups
+ aHasDefaultFeatureWithGlyph = false;
+ hb_set_t *glyphs = hb_set_create();
+ hb_codepoint_t index = -1;
+ while (hb_set_next(defaultFeatureLookups, &index)) {
+ hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
+ glyphs, glyphs, glyphs,
+ nullptr);
+ if (hb_set_has(glyphs, aGlyph)) {
+ aHasDefaultFeatureWithGlyph = true;
+ break;
+ }
+ }
+
+ // look for the glyph among non-default feature lookups
+ // if no default feature lookups contained spaces
+ bool hasNonDefaultFeatureWithGlyph = false;
+ if (!aHasDefaultFeatureWithGlyph) {
+ hb_set_clear(glyphs);
+ index = -1;
+ while (hb_set_next(nonDefaultFeatureLookups, &index)) {
+ hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
+ glyphs, glyphs, glyphs,
+ nullptr);
+ if (hb_set_has(glyphs, aGlyph)) {
+ hasNonDefaultFeatureWithGlyph = true;
+ break;
+ }
+ }
+ }
+
+ hb_set_destroy(glyphs);
+ hb_set_destroy(defaultFeatureLookups);
+ hb_set_destroy(nonDefaultFeatureLookups);
+
+ return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
+}
+
+static void
+HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
+ hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
+ uint16_t aGlyph)
+{
+ // iterate over the scripts in the font
+ uint32_t numScripts, numLangs, script, lang;
+ hb_set_t *otherLookups = hb_set_create();
+ hb_set_t *specificFeatureLookups = hb_set_create();
+ nsTHashtable<nsUint32HashKey> specificFeature;
+
+ specificFeature.PutEntry(aSpecificFeature);
+
+ numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
+ nullptr, nullptr);
+
+ for (script = 0; script < numScripts; script++) {
+ // default lang
+ CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
+ otherLookups, specificFeatureLookups,
+ script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
+
+ // iterate over langs
+ numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
+ script, 0,
+ nullptr, nullptr);
+ for (lang = 0; lang < numLangs; lang++) {
+ CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
+ otherLookups, specificFeatureLookups,
+ script, lang);
+ }
+ }
+
+ // look for the glyph among non-specific feature lookups
+ hb_set_t *glyphs = hb_set_create();
+ hb_codepoint_t index = -1;
+ while (hb_set_next(otherLookups, &index)) {
+ hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
+ glyphs, glyphs, glyphs,
+ nullptr);
+ if (hb_set_has(glyphs, aGlyph)) {
+ aHasGlyph = true;
+ break;
+ }
+ }
+
+ // look for the glyph among specific feature lookups
+ hb_set_clear(glyphs);
+ index = -1;
+ while (hb_set_next(specificFeatureLookups, &index)) {
+ hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
+ glyphs, glyphs, glyphs,
+ nullptr);
+ if (hb_set_has(glyphs, aGlyph)) {
+ aHasGlyphSpecific = true;
+ break;
+ }
+ }
+
+ hb_set_destroy(glyphs);
+ hb_set_destroy(specificFeatureLookups);
+ hb_set_destroy(otherLookups);
+}
+
+nsDataHashtable<nsUint32HashKey,Script> *gfxFont::sScriptTagToCode = nullptr;
+nsTHashtable<nsUint32HashKey> *gfxFont::sDefaultFeatures = nullptr;
+
+static inline bool
+HasSubstitution(uint32_t *aBitVector, Script aScript) {
+ return (aBitVector[static_cast<uint32_t>(aScript) >> 5]
+ & (1 << (static_cast<uint32_t>(aScript) & 0x1f))) != 0;
+}
+
+// union of all default substitution features across scripts
+static const hb_tag_t defaultFeatures[] = {
+ HB_TAG('a','b','v','f'),
+ HB_TAG('a','b','v','s'),
+ HB_TAG('a','k','h','n'),
+ HB_TAG('b','l','w','f'),
+ HB_TAG('b','l','w','s'),
+ HB_TAG('c','a','l','t'),
+ HB_TAG('c','c','m','p'),
+ HB_TAG('c','f','a','r'),
+ HB_TAG('c','j','c','t'),
+ HB_TAG('c','l','i','g'),
+ HB_TAG('f','i','n','2'),
+ HB_TAG('f','i','n','3'),
+ HB_TAG('f','i','n','a'),
+ HB_TAG('h','a','l','f'),
+ HB_TAG('h','a','l','n'),
+ HB_TAG('i','n','i','t'),
+ HB_TAG('i','s','o','l'),
+ HB_TAG('l','i','g','a'),
+ HB_TAG('l','j','m','o'),
+ HB_TAG('l','o','c','l'),
+ HB_TAG('l','t','r','a'),
+ HB_TAG('l','t','r','m'),
+ HB_TAG('m','e','d','2'),
+ HB_TAG('m','e','d','i'),
+ HB_TAG('m','s','e','t'),
+ HB_TAG('n','u','k','t'),
+ HB_TAG('p','r','e','f'),
+ HB_TAG('p','r','e','s'),
+ HB_TAG('p','s','t','f'),
+ HB_TAG('p','s','t','s'),
+ HB_TAG('r','c','l','t'),
+ HB_TAG('r','l','i','g'),
+ HB_TAG('r','k','r','f'),
+ HB_TAG('r','p','h','f'),
+ HB_TAG('r','t','l','a'),
+ HB_TAG('r','t','l','m'),
+ HB_TAG('t','j','m','o'),
+ HB_TAG('v','a','t','u'),
+ HB_TAG('v','e','r','t'),
+ HB_TAG('v','j','m','o')
+};
+
+void
+gfxFont::CheckForFeaturesInvolvingSpace()
+{
+ mFontEntry->mHasSpaceFeaturesInitialized = true;
+
+ bool log = LOG_FONTINIT_ENABLED();
+ TimeStamp start;
+ if (MOZ_UNLIKELY(log)) {
+ start = TimeStamp::Now();
+ }
+
+ bool result = false;
+
+ uint32_t spaceGlyph = GetSpaceGlyph();
+ if (!spaceGlyph) {
+ return;
+ }
+
+ hb_face_t *face = GetFontEntry()->GetHBFace();
+
+ // GSUB lookups - examine per script
+ if (hb_ot_layout_has_substitution(face)) {
+
+ // set up the script ==> code hashtable if needed
+ if (!sScriptTagToCode) {
+ sScriptTagToCode =
+ new nsDataHashtable<nsUint32HashKey,
+ Script>(size_t(Script::NUM_SCRIPT_CODES));
+ sScriptTagToCode->Put(HB_TAG('D','F','L','T'), Script::COMMON);
+ for (Script s = Script::ARABIC; s < Script::NUM_SCRIPT_CODES;
+ s = Script(static_cast<int>(s) + 1)) {
+ hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
+ hb_tag_t s1, s2;
+ hb_ot_tags_from_script(scriptTag, &s1, &s2);
+ sScriptTagToCode->Put(s1, s);
+ if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
+ sScriptTagToCode->Put(s2, s);
+ }
+ }
+
+ uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
+ sDefaultFeatures =
+ new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
+ for (uint32_t i = 0; i < numDefaultFeatures; i++) {
+ sDefaultFeatures->PutEntry(defaultFeatures[i]);
+ }
+ }
+
+ // iterate over the scripts in the font
+ hb_tag_t scriptTags[8];
+
+ uint32_t len, offset = 0;
+ do {
+ len = ArrayLength(scriptTags);
+ hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
+ &len, scriptTags);
+ for (uint32_t i = 0; i < len; i++) {
+ bool isDefaultFeature = false;
+ Script s;
+ if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
+ scriptTags[i], offset + i,
+ spaceGlyph,
+ *sDefaultFeatures,
+ isDefaultFeature) ||
+ !sScriptTagToCode->Get(scriptTags[i], &s))
+ {
+ continue;
+ }
+ result = true;
+ uint32_t index = static_cast<uint32_t>(s) >> 5;
+ uint32_t bit = static_cast<uint32_t>(s) & 0x1f;
+ if (isDefaultFeature) {
+ mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
+ } else {
+ mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
+ }
+ }
+ offset += len;
+ } while (len == ArrayLength(scriptTags));
+ }
+
+ // spaces in default features of default script?
+ // ==> can't use word cache, skip GPOS analysis
+ bool canUseWordCache = true;
+ if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
+ Script::COMMON)) {
+ canUseWordCache = false;
+ }
+
+ // GPOS lookups - distinguish kerning from non-kerning features
+ mFontEntry->mHasSpaceFeaturesKerning = false;
+ mFontEntry->mHasSpaceFeaturesNonKerning = false;
+
+ if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
+ bool hasKerning = false, hasNonKerning = false;
+ HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
+ HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
+ if (hasKerning || hasNonKerning) {
+ result = true;
+ }
+ mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
+ mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
+ }
+
+ hb_face_destroy(face);
+ mFontEntry->mHasSpaceFeatures = result;
+
+ if (MOZ_UNLIKELY(log)) {
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ LOG_FONTINIT((
+ "(fontinit-spacelookups) font: %s - "
+ "subst default: %8.8x %8.8x %8.8x %8.8x "
+ "subst non-default: %8.8x %8.8x %8.8x %8.8x "
+ "kerning: %s non-kerning: %s time: %6.3f\n",
+ NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
+ mFontEntry->mDefaultSubSpaceFeatures[3],
+ mFontEntry->mDefaultSubSpaceFeatures[2],
+ mFontEntry->mDefaultSubSpaceFeatures[1],
+ mFontEntry->mDefaultSubSpaceFeatures[0],
+ mFontEntry->mNonDefaultSubSpaceFeatures[3],
+ mFontEntry->mNonDefaultSubSpaceFeatures[2],
+ mFontEntry->mNonDefaultSubSpaceFeatures[1],
+ mFontEntry->mNonDefaultSubSpaceFeatures[0],
+ (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
+ (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
+ elapsed.ToMilliseconds()
+ ));
+ }
+}
+
+bool
+gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript)
+{
+ NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
+ "need to initialize space lookup flags");
+ NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
+ if (aRunScript == Script::INVALID ||
+ aRunScript >= Script::NUM_SCRIPT_CODES) {
+ return false;
+ }
+
+ // default features have space lookups ==> true
+ if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
+ Script::COMMON) ||
+ HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
+ aRunScript))
+ {
+ return true;
+ }
+
+ // non-default features have space lookups and some type of
+ // font feature, in font or style is specified ==> true
+ if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
+ Script::COMMON) ||
+ HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
+ aRunScript)) &&
+ (!mStyle.featureSettings.IsEmpty() ||
+ !mFontEntry->mFeatureSettings.IsEmpty()))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+gfxFont::SpaceMayParticipateInShaping(Script aRunScript)
+{
+ // avoid checking fonts known not to include default space-dependent features
+ if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
+ if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
+ mFontEntry->mFeatureSettings.IsEmpty()) {
+ return false;
+ }
+ }
+
+ if (FontCanSupportGraphite()) {
+ if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+ return mFontEntry->HasGraphiteSpaceContextuals();
+ }
+ }
+
+ // We record the presence of space-dependent features in the font entry
+ // so that subsequent instantiations for the same font face won't
+ // require us to re-check the tables; however, the actual check is done
+ // by gfxFont because not all font entry subclasses know how to create
+ // a harfbuzz face for introspection.
+ if (!mFontEntry->mHasSpaceFeaturesInitialized) {
+ CheckForFeaturesInvolvingSpace();
+ }
+
+ if (!mFontEntry->mHasSpaceFeatures) {
+ return false;
+ }
+
+ // if font has substitution rules or non-kerning positioning rules
+ // that involve spaces, bypass
+ if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
+ mFontEntry->mHasSpaceFeaturesNonKerning) {
+ return true;
+ }
+
+ // if kerning explicitly enabled/disabled via font-feature-settings or
+ // font-kerning and kerning rules use spaces, only bypass when enabled
+ if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
+ return mKerningEnabled;
+ }
+
+ return false;
+}
+
+bool
+gfxFont::SupportsFeature(Script aScript, uint32_t aFeatureTag)
+{
+ if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+ return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
+ }
+ return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
+}
+
+bool
+gfxFont::SupportsVariantCaps(Script aScript,
+ uint32_t aVariantCaps,
+ bool& aFallbackToSmallCaps,
+ bool& aSyntheticLowerToSmallCaps,
+ bool& aSyntheticUpperToSmallCaps)
+{
+ bool ok = true; // cases without fallback are fine
+ aFallbackToSmallCaps = false;
+ aSyntheticLowerToSmallCaps = false;
+ aSyntheticUpperToSmallCaps = false;
+ switch (aVariantCaps) {
+ case NS_FONT_VARIANT_CAPS_SMALLCAPS:
+ ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
+ if (!ok) {
+ aSyntheticLowerToSmallCaps = true;
+ }
+ break;
+ case NS_FONT_VARIANT_CAPS_ALLSMALL:
+ ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
+ SupportsFeature(aScript, HB_TAG('c','2','s','c'));
+ if (!ok) {
+ aSyntheticLowerToSmallCaps = true;
+ aSyntheticUpperToSmallCaps = true;
+ }
+ break;
+ case NS_FONT_VARIANT_CAPS_PETITECAPS:
+ ok = SupportsFeature(aScript, HB_TAG('p','c','a','p'));
+ if (!ok) {
+ ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
+ aFallbackToSmallCaps = ok;
+ }
+ if (!ok) {
+ aSyntheticLowerToSmallCaps = true;
+ }
+ break;
+ case NS_FONT_VARIANT_CAPS_ALLPETITE:
+ ok = SupportsFeature(aScript, HB_TAG('p','c','a','p')) &&
+ SupportsFeature(aScript, HB_TAG('c','2','p','c'));
+ if (!ok) {
+ ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
+ SupportsFeature(aScript, HB_TAG('c','2','s','c'));
+ aFallbackToSmallCaps = ok;
+ }
+ if (!ok) {
+ aSyntheticLowerToSmallCaps = true;
+ aSyntheticUpperToSmallCaps = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ NS_ASSERTION(!(ok && (aSyntheticLowerToSmallCaps ||
+ aSyntheticUpperToSmallCaps)),
+ "shouldn't use synthetic features if we found real ones");
+
+ NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
+ "if we found a usable fallback, that counts as ok");
+
+ return ok;
+}
+
+bool
+gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
+ const uint8_t *aString,
+ uint32_t aLength, Script aRunScript)
+{
+ NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
+ aLength);
+ return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(),
+ aLength, aRunScript);
+}
+
+bool
+gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
+ const char16_t *aString,
+ uint32_t aLength, Script aRunScript)
+{
+ NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
+ aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
+ "unknown value of font-variant-position");
+
+ uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ?
+ HB_TAG('s','u','p','s') : HB_TAG('s','u','b','s');
+
+ if (!SupportsFeature(aRunScript, feature)) {
+ return false;
+ }
+
+ // xxx - for graphite, don't really know how to sniff lookups so bail
+ if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+ return true;
+ }
+
+ if (!mHarfBuzzShaper) {
+ mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
+ }
+ gfxHarfBuzzShaper* shaper =
+ static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
+ if (!shaper->Initialize()) {
+ return false;
+ }
+
+ // get the hbset containing input glyphs for the feature
+ const hb_set_t *inputGlyphs = mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
+
+ // create an hbset containing default glyphs for the script run
+ hb_set_t *defaultGlyphsInRun = hb_set_create();
+
+ // for each character, get the glyph id
+ for (uint32_t i = 0; i < aLength; i++) {
+ uint32_t ch = aString[i];
+
+ if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
+ NS_IS_LOW_SURROGATE(aString[i + 1])) {
+ i++;
+ ch = SURROGATE_TO_UCS4(ch, aString[i]);
+ }
+
+ if (ch == 0xa0) {
+ ch = ' ';
+ }
+
+ hb_codepoint_t gid = shaper->GetNominalGlyph(ch);
+ hb_set_add(defaultGlyphsInRun, gid);
+ }
+
+ // intersect with input glyphs, if size is not the same ==> fallback
+ uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
+ hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
+ uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
+ hb_set_destroy(defaultGlyphsInRun);
+
+ return origSize == intersectionSize;
+}
+
+bool
+gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
+{
+ aFeatureOn = false;
+
+ if (mStyle.featureSettings.IsEmpty() &&
+ GetFontEntry()->mFeatureSettings.IsEmpty()) {
+ return false;
+ }
+
+ // add feature values from font
+ bool featureSet = false;
+ uint32_t i, count;
+
+ nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
+ count = fontFeatures.Length();
+ for (i = 0; i < count; i++) {
+ const gfxFontFeature& feature = fontFeatures.ElementAt(i);
+ if (feature.mTag == aFeature) {
+ featureSet = true;
+ aFeatureOn = (feature.mValue != 0);
+ }
+ }
+
+ // add feature values from style rules
+ nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
+ count = styleFeatures.Length();
+ for (i = 0; i < count; i++) {
+ const gfxFontFeature& feature = styleFeatures.ElementAt(i);
+ if (feature.mTag == aFeature) {
+ featureSet = true;
+ aFeatureOn = (feature.mValue != 0);
+ }
+ }
+
+ return featureSet;
+}
+
+/**
+ * A helper function in case we need to do any rounding or other
+ * processing here.
+ */
+#define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
+ (double(aAppUnits)*double(aDevUnitsPerAppUnit))
+
+static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
+ switch (aAAOption) {
+ case gfxFont::kAntialiasSubpixel:
+ return AntialiasMode::SUBPIXEL;
+ case gfxFont::kAntialiasGrayscale:
+ return AntialiasMode::GRAY;
+ case gfxFont::kAntialiasNone:
+ return AntialiasMode::NONE;
+ default:
+ return AntialiasMode::DEFAULT;
+ }
+}
+
+class GlyphBufferAzure
+{
+public:
+ GlyphBufferAzure(const TextRunDrawParams& aRunParams,
+ const FontDrawParams& aFontParams)
+ : mRunParams(aRunParams)
+ , mFontParams(aFontParams)
+ , mNumGlyphs(0)
+ {
+ }
+
+ ~GlyphBufferAzure()
+ {
+ Flush(true); // flush any remaining buffered glyphs
+ }
+
+ void OutputGlyph(uint32_t aGlyphID, const gfxPoint& aPt)
+ {
+ Glyph *glyph = AppendGlyph();
+ glyph->mIndex = aGlyphID;
+ glyph->mPosition.x = aPt.x;
+ glyph->mPosition.y = aPt.y;
+ glyph->mPosition = mFontParams.matInv.TransformPoint(glyph->mPosition);
+ Flush(false); // this will flush only if the buffer is full
+ }
+
+ const TextRunDrawParams& mRunParams;
+ const FontDrawParams& mFontParams;
+
+private:
+#define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
+
+ Glyph *AppendGlyph()
+ {
+ return &mGlyphBuffer[mNumGlyphs++];
+ }
+
+ static DrawMode
+ GetStrokeMode(DrawMode aMode)
+ {
+ return aMode & (DrawMode::GLYPH_STROKE |
+ DrawMode::GLYPH_STROKE_UNDERNEATH);
+ }
+
+ // Render the buffered glyphs to the draw target and clear the buffer.
+ // This actually flushes the glyphs only if the buffer is full, or if the
+ // aFinish parameter is true; otherwise it simply returns.
+ void Flush(bool aFinish)
+ {
+ // Ensure there's enough room for a glyph to be added to the buffer
+ if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
+ return;
+ }
+
+ if (mRunParams.isRTL) {
+ Glyph *begin = &mGlyphBuffer[0];
+ Glyph *end = &mGlyphBuffer[mNumGlyphs];
+ std::reverse(begin, end);
+ }
+
+ gfx::GlyphBuffer buf;
+ buf.mGlyphs = mGlyphBuffer;
+ buf.mNumGlyphs = mNumGlyphs;
+
+ gfxContext::AzureState state = mRunParams.context->CurrentState();
+ if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
+ if (state.pattern || mFontParams.contextPaint) {
+ Pattern *pat;
+
+ RefPtr<gfxPattern> fillPattern;
+ if (!mFontParams.contextPaint ||
+ !(fillPattern = mFontParams.contextPaint->GetFillPattern(
+ mRunParams.context->GetDrawTarget(),
+ mRunParams.context->CurrentMatrix()))) {
+ if (state.pattern) {
+ pat = state.pattern->GetPattern(mRunParams.dt,
+ state.patternTransformChanged ?
+ &state.patternTransform : nullptr);
+ } else {
+ pat = nullptr;
+ }
+ } else {
+ pat = fillPattern->GetPattern(mRunParams.dt);
+ }
+
+ if (pat) {
+ Matrix saved;
+ Matrix *mat = nullptr;
+ if (mFontParams.passedInvMatrix) {
+ // The brush matrix needs to be multiplied with the
+ // inverted matrix as well, to move the brush into the
+ // space of the glyphs.
+
+ // This relies on the returned Pattern not to be reused
+ // by others, but regenerated on GetPattern calls. This
+ // is true!
+ if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
+ mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
+ } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
+ mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
+ } else if (pat->GetType() == PatternType::SURFACE) {
+ mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
+ }
+
+ if (mat) {
+ saved = *mat;
+ *mat = (*mat) * (*mFontParams.passedInvMatrix);
+ }
+ }
+
+ mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+ *pat, mFontParams.drawOptions,
+ mFontParams.renderingOptions);
+
+ if (mat) {
+ *mat = saved;
+ }
+ }
+ } else if (state.sourceSurface) {
+ mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+ SurfacePattern(state.sourceSurface,
+ ExtendMode::CLAMP,
+ state.surfTransform),
+ mFontParams.drawOptions,
+ mFontParams.renderingOptions);
+ } else {
+ mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+ ColorPattern(state.color),
+ mFontParams.drawOptions,
+ mFontParams.renderingOptions);
+ }
+ }
+ if (GetStrokeMode(mRunParams.drawMode) == DrawMode::GLYPH_STROKE &&
+ mRunParams.strokeOpts) {
+ Pattern *pat;
+ if (mRunParams.textStrokePattern) {
+ pat = mRunParams.textStrokePattern->GetPattern(
+ mRunParams.dt, state.patternTransformChanged
+ ? &state.patternTransform
+ : nullptr);
+
+ if (pat) {
+ Matrix saved;
+ Matrix *mat = nullptr;
+ if (mFontParams.passedInvMatrix) {
+ // The brush matrix needs to be multiplied with the
+ // inverted matrix as well, to move the brush into the
+ // space of the glyphs.
+
+ // This relies on the returned Pattern not to be reused
+ // by others, but regenerated on GetPattern calls. This
+ // is true!
+ if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
+ mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
+ } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
+ mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
+ } else if (pat->GetType() == PatternType::SURFACE) {
+ mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
+ }
+
+ if (mat) {
+ saved = *mat;
+ *mat = (*mat) * (*mFontParams.passedInvMatrix);
+ }
+ }
+ FlushStroke(buf, *pat);
+
+ if (mat) {
+ *mat = saved;
+ }
+ }
+ } else {
+ FlushStroke(buf,
+ ColorPattern(
+ Color::FromABGR(mRunParams.textStrokeColor)));
+ }
+ }
+ if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
+ mRunParams.context->EnsurePathBuilder();
+ Matrix mat = mRunParams.dt->GetTransform();
+ mFontParams.scaledFont->CopyGlyphsToBuilder(
+ buf, mRunParams.context->mPathBuilder, &mat);
+ }
+
+ mNumGlyphs = 0;
+ }
+
+ void FlushStroke(gfx::GlyphBuffer& aBuf, const Pattern& aPattern)
+ {
+ RefPtr<Path> path =
+ mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt);
+ mRunParams.dt->Stroke(path, aPattern, *mRunParams.strokeOpts,
+ (mRunParams.drawOpts) ? *mRunParams.drawOpts
+ : DrawOptions());
+ }
+
+ Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
+ unsigned int mNumGlyphs;
+
+#undef GLYPH_BUFFER_SIZE
+};
+
+// Bug 674909. When synthetic bolding text by drawing twice, need to
+// render using a pixel offset in device pixels, otherwise text
+// doesn't appear bolded, it appears as if a bad text shadow exists
+// when a non-identity transform exists. Use an offset factor so that
+// the second draw occurs at a constant offset in device pixels.
+
+double
+gfxFont::CalcXScale(DrawTarget* aDrawTarget)
+{
+ // determine magnitude of a 1px x offset in device space
+ Size t = aDrawTarget->GetTransform().TransformSize(Size(1.0, 0.0));
+ if (t.width == 1.0 && t.height == 0.0) {
+ // short-circuit the most common case to avoid sqrt() and division
+ return 1.0;
+ }
+
+ double m = sqrt(t.width * t.width + t.height * t.height);
+
+ NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
+ if (m == 0.0) {
+ return 0.0; // effectively disables offset
+ }
+
+ // scale factor so that offsets are 1px in device pixels
+ return 1.0 / m;
+}
+
+// Draw an individual glyph at a specific location.
+// *aPt is the glyph position in appUnits; it is converted to device
+// coordinates (devPt) here.
+void
+gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
+ GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
+{
+ const TextRunDrawParams& runParams(aBuffer.mRunParams);
+ const FontDrawParams& fontParams(aBuffer.mFontParams);
+
+ double glyphX, glyphY;
+ if (fontParams.isVerticalFont) {
+ glyphX = aPt->x;
+ if (runParams.isRTL) {
+ aPt->y -= aAdvance;
+ glyphY = aPt->y;
+ } else {
+ glyphY = aPt->y;
+ aPt->y += aAdvance;
+ }
+ } else {
+ glyphY = aPt->y;
+ if (runParams.isRTL) {
+ aPt->x -= aAdvance;
+ glyphX = aPt->x;
+ } else {
+ glyphX = aPt->x;
+ aPt->x += aAdvance;
+ }
+ }
+ gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
+ ToDeviceUnits(glyphY, runParams.devPerApp));
+
+ if (fontParams.haveSVGGlyphs) {
+ if (!runParams.paintSVGGlyphs) {
+ return;
+ }
+ NS_WARNING_ASSERTION(
+ runParams.drawMode != DrawMode::GLYPH_PATH,
+ "Rendering SVG glyph despite request for glyph path");
+ if (RenderSVGGlyph(runParams.context, devPt,
+ aGlyphID, fontParams.contextPaint,
+ runParams.callbacks, *aEmittedGlyphs)) {
+ return;
+ }
+ }
+
+ if (fontParams.haveColorGlyphs &&
+ RenderColorGlyph(runParams.dt, runParams.context,
+ fontParams.scaledFont, fontParams.renderingOptions,
+ fontParams.drawOptions,
+ fontParams.matInv.TransformPoint(gfx::Point(devPt.x, devPt.y)),
+ aGlyphID)) {
+ return;
+ }
+
+ aBuffer.OutputGlyph(aGlyphID, devPt);
+
+ // Synthetic bolding (if required) by multi-striking.
+ for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
+ if (fontParams.isVerticalFont) {
+ devPt.y += fontParams.synBoldOnePixelOffset;
+ } else {
+ devPt.x += fontParams.synBoldOnePixelOffset;
+ }
+ aBuffer.OutputGlyph(aGlyphID, devPt);
+ }
+
+ *aEmittedGlyphs = true;
+}
+
+// Draw a run of CharacterGlyph records from the given offset in aShapedText.
+// Returns true if glyph paths were actually emitted.
+bool
+gfxFont::DrawGlyphs(const gfxShapedText *aShapedText,
+ uint32_t aOffset, // offset in the textrun
+ uint32_t aCount, // length of run to draw
+ gfxPoint *aPt,
+ const TextRunDrawParams& aRunParams,
+ const FontDrawParams& aFontParams)
+{
+ bool emittedGlyphs = false;
+ GlyphBufferAzure buffer(aRunParams, aFontParams);
+
+ gfxFloat& inlineCoord = aFontParams.isVerticalFont ? aPt->y : aPt->x;
+
+ if (aRunParams.spacing) {
+ inlineCoord += aRunParams.isRTL ? -aRunParams.spacing[0].mBefore
+ : aRunParams.spacing[0].mBefore;
+ }
+
+ const gfxShapedText::CompressedGlyph *glyphData =
+ &aShapedText->GetCharacterGlyphs()[aOffset];
+
+ for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
+ if (glyphData->IsSimpleGlyph()) {
+ DrawOneGlyph(glyphData->GetSimpleGlyph(),
+ glyphData->GetSimpleAdvance(),
+ aPt, buffer, &emittedGlyphs);
+ } else {
+ uint32_t glyphCount = glyphData->GetGlyphCount();
+ if (glyphCount > 0) {
+ const gfxShapedText::DetailedGlyph *details =
+ aShapedText->GetDetailedGlyphs(aOffset + i);
+ NS_ASSERTION(details, "detailedGlyph should not be missing!");
+ for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
+ double advance = details->mAdvance;
+
+ if (glyphData->IsMissing()) {
+ // Default-ignorable chars will have zero advance width;
+ // we don't have to draw the hexbox for them.
+ if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
+ advance > 0) {
+ double glyphX = aPt->x;
+ double glyphY = aPt->y;
+ if (aRunParams.isRTL) {
+ if (aFontParams.isVerticalFont) {
+ glyphY -= advance;
+ } else {
+ glyphX -= advance;
+ }
+ }
+ Point pt(Float(ToDeviceUnits(glyphX, aRunParams.devPerApp)),
+ Float(ToDeviceUnits(glyphY, aRunParams.devPerApp)));
+ Float advanceDevUnits =
+ Float(ToDeviceUnits(advance, aRunParams.devPerApp));
+ Float height = GetMetrics(eHorizontal).maxAscent;
+ Rect glyphRect = aFontParams.isVerticalFont ?
+ Rect(pt.x - height / 2, pt.y,
+ height, advanceDevUnits) :
+ Rect(pt.x, pt.y - height,
+ advanceDevUnits, height);
+
+ // If there's a fake-italic skew in effect as part
+ // of the drawTarget's transform, we need to remove
+ // this before drawing the hexbox. (Bug 983985)
+ Matrix oldMat;
+ if (aFontParams.passedInvMatrix) {
+ oldMat = aRunParams.dt->GetTransform();
+ aRunParams.dt->SetTransform(
+ *aFontParams.passedInvMatrix * oldMat);
+ }
+
+ gfxFontMissingGlyphs::DrawMissingGlyph(
+ details->mGlyphID, glyphRect, *aRunParams.dt,
+ PatternFromState(aRunParams.context),
+ aShapedText->GetAppUnitsPerDevUnit());
+
+ // Restore the matrix, if we modified it before
+ // drawing the hexbox.
+ if (aFontParams.passedInvMatrix) {
+ aRunParams.dt->SetTransform(oldMat);
+ }
+ }
+ } else {
+ gfxPoint glyphXY(*aPt);
+ if (aFontParams.isVerticalFont) {
+ glyphXY.x += details->mYOffset;
+ glyphXY.y += details->mXOffset;
+ } else {
+ glyphXY.x += details->mXOffset;
+ glyphXY.y += details->mYOffset;
+ }
+ DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
+ buffer, &emittedGlyphs);
+ }
+
+ inlineCoord += aRunParams.isRTL ? -advance : advance;
+ }
+ }
+ }
+
+ if (aRunParams.spacing) {
+ double space = aRunParams.spacing[i].mAfter;
+ if (i + 1 < aCount) {
+ space += aRunParams.spacing[i + 1].mBefore;
+ }
+ inlineCoord += aRunParams.isRTL ? -space : space;
+ }
+ }
+
+ return emittedGlyphs;
+}
+
+// This method is mostly parallel to DrawGlyphs.
+void
+gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfxPoint* aPt,
+ uint32_t aOffset, uint32_t aCount,
+ const EmphasisMarkDrawParams& aParams)
+{
+ gfxFloat& inlineCoord = aParams.isVertical ? aPt->y : aPt->x;
+ gfxTextRun::Range markRange(aParams.mark);
+ gfxTextRun::DrawParams params(aParams.context);
+
+ gfxFloat clusterStart = -std::numeric_limits<gfxFloat>::infinity();
+ bool shouldDrawEmphasisMark = false;
+ for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
+ if (aParams.spacing) {
+ inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
+ }
+ if (aShapedText->IsClusterStart(idx) ||
+ clusterStart == -std::numeric_limits<gfxFloat>::infinity()) {
+ clusterStart = inlineCoord;
+ }
+ if (aShapedText->CharMayHaveEmphasisMark(idx)) {
+ shouldDrawEmphasisMark = true;
+ }
+ inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
+ if (shouldDrawEmphasisMark &&
+ (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
+ gfxFloat clusterAdvance = inlineCoord - clusterStart;
+ // Move the coord backward to get the needed start point.
+ gfxFloat delta = (clusterAdvance + aParams.advance) / 2;
+ inlineCoord -= delta;
+ aParams.mark->Draw(markRange, *aPt, params);
+ inlineCoord += delta;
+ shouldDrawEmphasisMark = false;
+ }
+ if (aParams.spacing) {
+ inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
+ }
+ }
+}
+
+void
+gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
+ gfxPoint *aPt, const TextRunDrawParams& aRunParams,
+ uint16_t aOrientation)
+{
+ NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
+ !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
+ "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
+
+ if (aStart >= aEnd) {
+ return;
+ }
+
+ FontDrawParams fontParams;
+
+ if (aRunParams.drawOpts) {
+ fontParams.drawOptions = *aRunParams.drawOpts;
+ }
+
+ fontParams.scaledFont = GetScaledFont(aRunParams.dt);
+ if (!fontParams.scaledFont) {
+ return;
+ }
+
+ fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
+ fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
+ fontParams.contextPaint = aRunParams.runContextPaint;
+ fontParams.isVerticalFont =
+ aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+
+ bool sideways = false;
+ gfxPoint origPt = *aPt;
+ if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
+ sideways = true;
+ aRunParams.context->Save();
+ gfxPoint p(aPt->x * aRunParams.devPerApp,
+ aPt->y * aRunParams.devPerApp);
+ const Metrics& metrics = GetMetrics(eHorizontal);
+ // Get a matrix we can use to draw the (horizontally-shaped) textrun
+ // with 90-degree CW rotation.
+ const gfxFloat
+ rotation = (aOrientation ==
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
+ ? -M_PI / 2.0 : M_PI / 2.0;
+ gfxMatrix mat =
+ aRunParams.context->CurrentMatrix().
+ Translate(p). // translate origin for rotation
+ Rotate(rotation). // turn 90deg CCW (sideways-left) or CW (*-right)
+ Translate(-p); // undo the translation
+
+ // If we're drawing rotated horizontal text for an element styled
+ // text-orientation:mixed, the dominant baseline will be vertical-
+ // centered. So in this case, we need to adjust the position so that
+ // the rotated horizontal text (which uses an alphabetic baseline) will
+ // look OK when juxtaposed with upright glyphs (rendered on a centered
+ // vertical baseline). The adjustment here is somewhat ad hoc; we
+ // should eventually look for baseline tables[1] in the fonts and use
+ // those if available.
+ // [1] See http://www.microsoft.com/typography/otspec/base.htm
+ if (aTextRun->UseCenterBaseline()) {
+ gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
+ mat.Translate(baseAdj);
+ }
+
+ aRunParams.context->SetMatrix(mat);
+ }
+
+ UniquePtr<SVGContextPaint> contextPaint;
+ if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
+ // If no pattern is specified for fill, use the current pattern
+ NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
+ "no pattern supplied for stroking text");
+ RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
+ contextPaint.reset(
+ new SimpleTextContextPaint(fillPattern, nullptr,
+ aRunParams.context->CurrentMatrix()));
+ fontParams.contextPaint = contextPaint.get();
+ }
+
+ // Synthetic-bold strikes are each offset one device pixel in run direction.
+ // (these values are only needed if IsSyntheticBold() is true)
+ if (IsSyntheticBold()) {
+ double xscale = CalcXScale(aRunParams.context->GetDrawTarget());
+ fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
+ if (xscale != 0.0) {
+ // use as many strikes as needed for the the increased advance
+ fontParams.extraStrikes =
+ std::max(1, NS_lroundf(GetSyntheticBoldOffset() / xscale));
+ }
+ } else {
+ fontParams.synBoldOnePixelOffset = 0;
+ fontParams.extraStrikes = 0;
+ }
+
+ bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
+ if (!AllowSubpixelAA()) {
+ aRunParams.dt->SetPermitSubpixelAA(false);
+ }
+
+ Matrix mat;
+ Matrix oldMat = aRunParams.dt->GetTransform();
+
+ // This is nullptr when we have inverse-transformed glyphs and we need
+ // to transform the Brush inside flush.
+ fontParams.passedInvMatrix = nullptr;
+
+ fontParams.renderingOptions = GetGlyphRenderingOptions(&aRunParams);
+ fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
+
+ // The cairo DrawTarget backend uses the cairo_scaled_font directly
+ // and so has the font skew matrix applied already.
+ if (mScaledFont &&
+ aRunParams.dt->GetBackendType() != BackendType::CAIRO) {
+ cairo_matrix_t matrix;
+ cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
+ if (matrix.xy != 0) {
+ // If this matrix applies a skew, which can happen when drawing
+ // oblique fonts, we will set the DrawTarget matrix to apply the
+ // skew. We'll need to move the glyphs by the inverse of the skew to
+ // get the glyphs positioned correctly in the new device space
+ // though, since the font matrix should only be applied to drawing
+ // the glyphs, and not to their position.
+ mat = Matrix(matrix.xx, matrix.yx,
+ matrix.xy, matrix.yy,
+ matrix.x0, matrix.y0);
+
+ mat._11 = mat._22 = 1.0;
+ mat._21 /= GetAdjustedSize();
+
+ aRunParams.dt->SetTransform(mat * oldMat);
+
+ fontParams.matInv = mat;
+ fontParams.matInv.Invert();
+
+ fontParams.passedInvMatrix = &fontParams.matInv;
+ }
+ }
+
+ gfxFloat& baseline = fontParams.isVerticalFont ? aPt->x : aPt->y;
+ gfxFloat origBaseline = baseline;
+ if (mStyle.baselineOffset != 0.0) {
+ baseline +=
+ mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
+ }
+
+ bool emittedGlyphs =
+ DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt,
+ aRunParams, fontParams);
+
+ baseline = origBaseline;
+
+ if (aRunParams.callbacks && emittedGlyphs) {
+ aRunParams.callbacks->NotifyGlyphPathEmitted();
+ }
+
+ aRunParams.dt->SetTransform(oldMat);
+ aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
+
+ if (sideways) {
+ aRunParams.context->Restore();
+ // adjust updated aPt to account for the transform we were using
+ gfxFloat advance = aPt->x - origPt.x;
+ if (aOrientation ==
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) {
+ *aPt = gfxPoint(origPt.x, origPt.y - advance);
+ } else {
+ *aPt = gfxPoint(origPt.x, origPt.y + advance);
+ }
+ }
+}
+
+bool
+gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
+ uint32_t aGlyphId, SVGContextPaint* aContextPaint) const
+{
+ if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
+ return false;
+ }
+
+ const gfxFloat devUnitsPerSVGUnit =
+ GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
+ gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
+
+ aContext->Save();
+ aContext->SetMatrix(
+ aContext->CurrentMatrix().Translate(aPoint.x, aPoint.y).
+ Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
+
+ aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
+
+ bool rv = GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId,
+ aContextPaint);
+ aContext->Restore();
+ aContext->NewPath();
+ return rv;
+}
+
+bool
+gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
+ uint32_t aGlyphId, SVGContextPaint* aContextPaint,
+ gfxTextRunDrawCallbacks *aCallbacks,
+ bool& aEmittedGlyphs) const
+{
+ if (aCallbacks && aEmittedGlyphs) {
+ aCallbacks->NotifyGlyphPathEmitted();
+ aEmittedGlyphs = false;
+ }
+ return RenderSVGGlyph(aContext, aPoint, aGlyphId, aContextPaint);
+}
+
+bool
+gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget,
+ gfxContext* aContext,
+ mozilla::gfx::ScaledFont* scaledFont,
+ GlyphRenderingOptions* aRenderingOptions,
+ mozilla::gfx::DrawOptions aDrawOptions,
+ const mozilla::gfx::Point& aPoint,
+ uint32_t aGlyphId) const
+{
+ AutoTArray<uint16_t, 8> layerGlyphs;
+ AutoTArray<mozilla::gfx::Color, 8> layerColors;
+
+ mozilla::gfx::Color defaultColor;
+ if (!aContext->GetDeviceColor(defaultColor)) {
+ defaultColor = mozilla::gfx::Color(0, 0, 0);
+ }
+ if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, defaultColor,
+ layerGlyphs, layerColors)) {
+ return false;
+ }
+
+ for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
+ layerIndex++) {
+ Glyph glyph;
+ glyph.mIndex = layerGlyphs[layerIndex];
+ glyph.mPosition = aPoint;
+
+ mozilla::gfx::GlyphBuffer buffer;
+ buffer.mGlyphs = &glyph;
+ buffer.mNumGlyphs = 1;
+
+ aDrawTarget->FillGlyphs(scaledFont, buffer,
+ ColorPattern(layerColors[layerIndex]),
+ aDrawOptions, aRenderingOptions);
+ }
+ return true;
+}
+
+static void
+UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
+{
+ *aDestMin = std::min(*aDestMin, aX);
+ *aDestMax = std::max(*aDestMax, aX);
+}
+
+// We get precise glyph extents if the textrun creator requested them, or
+// if the font is a user font --- in which case the author may be relying
+// on overflowing glyphs.
+static bool
+NeedsGlyphExtents(gfxFont *aFont, const gfxTextRun *aTextRun)
+{
+ return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
+ aFont->GetFontEntry()->IsUserFont();
+}
+
+bool
+gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
+ const gfxTextRun* aTextRun)
+{
+ if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized &&
+ GetAdjustedSize() >= 1.0) {
+ gfxGlyphExtents *extents =
+ GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
+ gfxRect glyphExtents;
+ mFontEntry->mSpaceGlyphIsInvisible =
+ extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
+ GetSpaceGlyph(), &glyphExtents) &&
+ glyphExtents.IsEmpty();
+ mFontEntry->mSpaceGlyphIsInvisibleInitialized = true;
+ }
+ return mFontEntry->mSpaceGlyphIsInvisible;
+}
+
+gfxFont::RunMetrics
+gfxFont::Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ Spacing *aSpacing,
+ uint16_t aOrientation)
+{
+ // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
+ // and the underlying cairo font may be antialiased,
+ // we need to create a copy in order to avoid getting cached extents.
+ // This is only used by MathML layout at present.
+ if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
+ mAntialiasOption != kAntialiasNone) {
+ if (!mNonAAFont) {
+ mNonAAFont.reset(CopyWithAntialiasOption(kAntialiasNone));
+ }
+ // if font subclass doesn't implement CopyWithAntialiasOption(),
+ // it will return null and we'll proceed to use the existing font
+ if (mNonAAFont) {
+ return mNonAAFont->Measure(aTextRun, aStart, aEnd,
+ TIGHT_HINTED_OUTLINE_EXTENTS,
+ aRefDrawTarget, aSpacing, aOrientation);
+ }
+ }
+
+ const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
+ // Current position in appunits
+ gfxFont::Orientation orientation =
+ aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT
+ ? eVertical : eHorizontal;
+ const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
+
+ gfxFloat baselineOffset = 0;
+ if (aTextRun->UseCenterBaseline() && orientation == eHorizontal) {
+ // For a horizontal font being used in vertical writing mode with
+ // text-orientation:mixed, the overall metrics we're accumulating
+ // will be aimed at a center baseline. But this font's metrics were
+ // based on the alphabetic baseline. So we compute a baseline offset
+ // that will be applied to ascent/descent values and glyph rects
+ // to effectively shift them relative to the baseline.
+ // XXX Eventually we should probably use the BASE table, if present.
+ // But it usually isn't, so we need an ad hoc adjustment for now.
+ baselineOffset = appUnitsPerDevUnit *
+ (fontMetrics.emAscent - fontMetrics.emDescent) / 2;
+ }
+
+ RunMetrics metrics;
+ metrics.mAscent = fontMetrics.maxAscent * appUnitsPerDevUnit;
+ metrics.mDescent = fontMetrics.maxDescent * appUnitsPerDevUnit;
+
+ if (aStart == aEnd) {
+ // exit now before we look at aSpacing[0], which is undefined
+ metrics.mAscent -= baselineOffset;
+ metrics.mDescent += baselineOffset;
+ metrics.mBoundingBox = gfxRect(0, -metrics.mAscent,
+ 0, metrics.mAscent + metrics.mDescent);
+ return metrics;
+ }
+
+ gfxFloat advanceMin = 0, advanceMax = 0;
+ const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
+ bool isRTL = aTextRun->IsRightToLeft();
+ double direction = aTextRun->GetDirection();
+ bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
+ gfxGlyphExtents *extents =
+ ((aBoundingBoxType == LOOSE_INK_EXTENTS &&
+ !needsGlyphExtents &&
+ !aTextRun->HasDetailedGlyphs()) ||
+ (MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) ||
+ (MOZ_UNLIKELY(GetStyle()->size == 0))) ? nullptr
+ : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
+ double x = 0;
+ if (aSpacing) {
+ x += direction*aSpacing[0].mBefore;
+ }
+ uint32_t spaceGlyph = GetSpaceGlyph();
+ bool allGlyphsInvisible = true;
+ uint32_t i;
+ for (i = aStart; i < aEnd; ++i) {
+ const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
+ if (glyphData->IsSimpleGlyph()) {
+ double advance = glyphData->GetSimpleAdvance();
+ uint32_t glyphIndex = glyphData->GetSimpleGlyph();
+ if (glyphIndex != spaceGlyph ||
+ !IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun)) {
+ allGlyphsInvisible = false;
+ }
+ // Only get the real glyph horizontal extent if we were asked
+ // for the tight bounding box or we're in quality mode
+ if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
+ extents){
+ uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
+ if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
+ aBoundingBoxType == LOOSE_INK_EXTENTS) {
+ UnionRange(x, &advanceMin, &advanceMax);
+ UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
+ } else {
+ gfxRect glyphRect;
+ if (!extents->GetTightGlyphExtentsAppUnits(this,
+ aRefDrawTarget, glyphIndex, &glyphRect)) {
+ glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
+ advance, metrics.mBoundingBox.Height());
+ }
+ if (orientation == eVertical) {
+ Swap(glyphRect.x, glyphRect.y);
+ Swap(glyphRect.width, glyphRect.height);
+ }
+ if (isRTL) {
+ glyphRect -= gfxPoint(advance, 0);
+ }
+ glyphRect += gfxPoint(x, 0);
+ metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
+ }
+ }
+ x += direction*advance;
+ } else {
+ allGlyphsInvisible = false;
+ uint32_t glyphCount = glyphData->GetGlyphCount();
+ if (glyphCount > 0) {
+ const gfxTextRun::DetailedGlyph *details =
+ aTextRun->GetDetailedGlyphs(i);
+ NS_ASSERTION(details != nullptr,
+ "detailedGlyph record should not be missing!");
+ uint32_t j;
+ for (j = 0; j < glyphCount; ++j, ++details) {
+ uint32_t glyphIndex = details->mGlyphID;
+ gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
+ double advance = details->mAdvance;
+ gfxRect glyphRect;
+ if (glyphData->IsMissing() || !extents ||
+ !extents->GetTightGlyphExtentsAppUnits(this,
+ aRefDrawTarget, glyphIndex, &glyphRect)) {
+ // We might have failed to get glyph extents due to
+ // OOM or something
+ glyphRect = gfxRect(0, -metrics.mAscent,
+ advance, metrics.mAscent + metrics.mDescent);
+ }
+ if (orientation == eVertical) {
+ Swap(glyphRect.x, glyphRect.y);
+ Swap(glyphRect.width, glyphRect.height);
+ }
+ if (isRTL) {
+ glyphRect -= gfxPoint(advance, 0);
+ }
+ glyphRect += glyphPt;
+ metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
+ x += direction*advance;
+ }
+ }
+ }
+ // Every other glyph type is ignored
+ if (aSpacing) {
+ double space = aSpacing[i - aStart].mAfter;
+ if (i + 1 < aEnd) {
+ space += aSpacing[i + 1 - aStart].mBefore;
+ }
+ x += direction*space;
+ }
+ }
+
+ if (allGlyphsInvisible) {
+ metrics.mBoundingBox.SetEmpty();
+ } else {
+ if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
+ UnionRange(x, &advanceMin, &advanceMax);
+ gfxRect fontBox(advanceMin, -metrics.mAscent,
+ advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
+ metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
+ }
+ if (isRTL) {
+ metrics.mBoundingBox -= gfxPoint(x, 0);
+ }
+ }
+
+ // If the font may be rendered with a fake-italic effect, we need to allow
+ // for the top-right of the glyphs being skewed to the right, and the
+ // bottom-left being skewed further left.
+ if (mStyle.style != NS_FONT_STYLE_NORMAL &&
+ mFontEntry->IsUpright() &&
+ mStyle.allowSyntheticStyle) {
+ gfxFloat extendLeftEdge =
+ ceil(OBLIQUE_SKEW_FACTOR * metrics.mBoundingBox.YMost());
+ gfxFloat extendRightEdge =
+ ceil(OBLIQUE_SKEW_FACTOR * -metrics.mBoundingBox.y);
+ metrics.mBoundingBox.width += extendLeftEdge + extendRightEdge;
+ metrics.mBoundingBox.x -= extendLeftEdge;
+ }
+
+ if (baselineOffset != 0) {
+ metrics.mAscent -= baselineOffset;
+ metrics.mDescent += baselineOffset;
+ metrics.mBoundingBox.y += baselineOffset;
+ }
+
+ metrics.mAdvanceWidth = x*direction;
+ return metrics;
+}
+
+void
+gfxFont::AgeCachedWords()
+{
+ if (mWordCache) {
+ for (auto it = mWordCache->Iter(); !it.Done(); it.Next()) {
+ CacheHashEntry *entry = it.Get();
+ if (!entry->mShapedWord) {
+ NS_ASSERTION(entry->mShapedWord,
+ "cache entry has no gfxShapedWord!");
+ it.Remove();
+ } else if (entry->mShapedWord->IncrementAge() ==
+ kShapedWordCacheMaxAge) {
+ it.Remove();
+ }
+ }
+ }
+}
+
+void
+gfxFont::NotifyGlyphsChanged()
+{
+ uint32_t i, count = mGlyphExtentsArray.Length();
+ for (i = 0; i < count; ++i) {
+ // Flush cached extents array
+ mGlyphExtentsArray[i]->NotifyGlyphsChanged();
+ }
+
+ if (mGlyphChangeObservers) {
+ for (auto it = mGlyphChangeObservers->Iter(); !it.Done(); it.Next()) {
+ it.Get()->GetKey()->NotifyGlyphsChanged();
+ }
+ }
+}
+
+// If aChar is a "word boundary" for shaped-word caching purposes, return it;
+// else return 0.
+static char16_t
+IsBoundarySpace(char16_t aChar, char16_t aNextChar)
+{
+ if ((aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar)) {
+ return aChar;
+ }
+ return 0;
+}
+
+#ifdef __GNUC__
+#define GFX_MAYBE_UNUSED __attribute__((unused))
+#else
+#define GFX_MAYBE_UNUSED
+#endif
+
+/* GetShapedWord is in gfxFont-Impl.h */
+
+bool
+gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
+{
+ const gfxShapedWord* sw = mShapedWord.get();
+ if (!sw) {
+ return false;
+ }
+ if (sw->GetLength() != aKey->mLength ||
+ sw->GetFlags() != aKey->mFlags ||
+ sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
+ sw->GetScript() != aKey->mScript) {
+ return false;
+ }
+ if (sw->TextIs8Bit()) {
+ if (aKey->mTextIs8Bit) {
+ return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
+ aKey->mLength * sizeof(uint8_t)));
+ }
+ // The key has 16-bit text, even though all the characters are < 256,
+ // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
+ // comparing with will have 8-bit text.
+ const uint8_t *s1 = sw->Text8Bit();
+ const char16_t *s2 = aKey->mText.mDouble;
+ const char16_t *s2end = s2 + aKey->mLength;
+ while (s2 < s2end) {
+ if (*s1++ != *s2++) {
+ return false;
+ }
+ }
+ return true;
+ }
+ NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
+ !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
+ return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
+ aKey->mLength * sizeof(char16_t)));
+}
+
+bool
+gfxFont::ShapeText(DrawTarget *aDrawTarget,
+ const uint8_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ nsDependentCSubstring ascii((const char*)aText, aLength);
+ nsAutoString utf16;
+ AppendASCIItoUTF16(ascii, utf16);
+ if (utf16.Length() != aLength) {
+ return false;
+ }
+ return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength,
+ aScript, aVertical, aShapedText);
+}
+
+bool
+gfxFont::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ bool ok = false;
+
+ // XXX Currently, we do all vertical shaping through harfbuzz.
+ // Vertical graphite support may be wanted as a future enhancement.
+ if (FontCanSupportGraphite() && !aVertical) {
+ if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+ if (!mGraphiteShaper) {
+ mGraphiteShaper = MakeUnique<gfxGraphiteShaper>(this);
+ }
+ ok = mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
+ aScript, aVertical, aShapedText);
+ }
+ }
+
+ if (!ok) {
+ if (!mHarfBuzzShaper) {
+ mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
+ }
+ ok = mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
+ aScript, aVertical, aShapedText);
+ }
+
+ NS_WARNING_ASSERTION(ok, "shaper failed, expect scrambled or missing text");
+
+ PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
+ aVertical, aShapedText);
+
+ return ok;
+}
+
+void
+gfxFont::PostShapingFixup(DrawTarget* aDrawTarget,
+ const char16_t* aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ bool aVertical,
+ gfxShapedText* aShapedText)
+{
+ if (IsSyntheticBold()) {
+ const Metrics& metrics =
+ GetMetrics(aVertical ? eVertical : eHorizontal);
+ if (metrics.maxAdvance > metrics.aveCharWidth) {
+ float synBoldOffset =
+ GetSyntheticBoldOffset() * CalcXScale(aDrawTarget);
+ aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
+ aOffset, aLength);
+ }
+ }
+}
+
+#define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
+ // over-stressing platform shapers
+#define BACKTRACK_LIMIT 16 // backtrack this far looking for a good place
+ // to split into fragments for separate shaping
+
+template<typename T>
+bool
+gfxFont::ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxTextRun *aTextRun)
+{
+ aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
+
+ bool ok = true;
+
+ while (ok && aLength > 0) {
+ uint32_t fragLen = aLength;
+
+ // limit the length of text we pass to shapers in a single call
+ if (fragLen > MAX_SHAPING_LENGTH) {
+ fragLen = MAX_SHAPING_LENGTH;
+
+ // in the 8-bit case, there are no multi-char clusters,
+ // so we don't need to do this check
+ if (sizeof(T) == sizeof(char16_t)) {
+ uint32_t i;
+ for (i = 0; i < BACKTRACK_LIMIT; ++i) {
+ if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
+ fragLen -= i;
+ break;
+ }
+ }
+ if (i == BACKTRACK_LIMIT) {
+ // if we didn't find any cluster start while backtracking,
+ // just check that we're not in the middle of a surrogate
+ // pair; back up by one code unit if we are.
+ if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
+ NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
+ --fragLen;
+ }
+ }
+ }
+ }
+
+ ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript, aVertical,
+ aTextRun);
+
+ aText += fragLen;
+ aOffset += fragLen;
+ aLength -= fragLen;
+ }
+
+ return ok;
+}
+
+// Check if aCh is an unhandled control character that should be displayed
+// as a hexbox rather than rendered by some random font on the system.
+// We exclude \r as stray &#13;s are rather common (bug 941940).
+// Note that \n and \t don't come through here, as they have specific
+// meanings that have already been handled.
+static bool
+IsInvalidControlChar(uint32_t aCh)
+{
+ return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
+}
+
+template<typename T>
+bool
+gfxFont::ShapeTextWithoutWordCache(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxTextRun *aTextRun)
+{
+ uint32_t fragStart = 0;
+ bool ok = true;
+
+ for (uint32_t i = 0; i <= aLength && ok; ++i) {
+ T ch = (i < aLength) ? aText[i] : '\n';
+ bool invalid = gfxFontGroup::IsInvalidChar(ch);
+ uint32_t length = i - fragStart;
+
+ // break into separate fragments when we hit an invalid char
+ if (!invalid) {
+ continue;
+ }
+
+ if (length > 0) {
+ ok = ShapeFragmentWithoutWordCache(aDrawTarget, aText + fragStart,
+ aOffset + fragStart, length,
+ aScript, aVertical, aTextRun);
+ }
+
+ if (i == aLength) {
+ break;
+ }
+
+ // fragment was terminated by an invalid char: skip it,
+ // unless it's a control char that we want to show as a hexbox,
+ // but record where TAB or NEWLINE occur
+ if (ch == '\t') {
+ aTextRun->SetIsTab(aOffset + i);
+ } else if (ch == '\n') {
+ aTextRun->SetIsNewline(aOffset + i);
+ } else if (IsInvalidControlChar(ch) &&
+ !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
+ if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
+ ShapeFragmentWithoutWordCache(aDrawTarget, aText + i,
+ aOffset + i, 1,
+ aScript, aVertical, aTextRun);
+ } else {
+ aTextRun->SetMissingGlyph(aOffset + i, ch, this);
+ }
+ }
+ fragStart = i + 1;
+ }
+
+ NS_WARNING_ASSERTION(ok, "failed to shape text - expect garbled text");
+ return ok;
+}
+
+#ifndef RELEASE_OR_BETA
+#define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
+#else
+#define TEXT_PERF_INCR(tp, m)
+#endif
+
+inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
+inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
+
+inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
+{
+ return memchr(aString, 0x20, aLen) != nullptr;
+}
+
+inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
+{
+ for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
+ if (*ch == 0x20) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template<typename T>
+bool
+gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString, // text for this font run
+ uint32_t aRunStart, // position in the textrun
+ uint32_t aRunLength,
+ Script aRunScript,
+ bool aVertical)
+{
+ if (aRunLength == 0) {
+ return true;
+ }
+
+ gfxTextPerfMetrics *tp = nullptr;
+
+#ifndef RELEASE_OR_BETA
+ tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
+ if (tp) {
+ if (mStyle.systemFont) {
+ tp->current.numChromeTextRuns++;
+ } else {
+ tp->current.numContentTextRuns++;
+ }
+ tp->current.numChars += aRunLength;
+ if (aRunLength > tp->current.maxTextRunLen) {
+ tp->current.maxTextRunLen = aRunLength;
+ }
+ }
+#endif
+
+ uint32_t wordCacheCharLimit =
+ gfxPlatform::GetPlatform()->WordCacheCharLimit();
+
+ // If spaces can participate in shaping (e.g. within lookups for automatic
+ // fractions), need to shape without using the word cache which segments
+ // textruns on space boundaries. Word cache can be used if the textrun
+ // is short enough to fit in the word cache and it lacks spaces.
+ if (SpaceMayParticipateInShaping(aRunScript)) {
+ if (aRunLength > wordCacheCharLimit ||
+ HasSpaces(aString, aRunLength)) {
+ TEXT_PERF_INCR(tp, wordCacheSpaceRules);
+ return ShapeTextWithoutWordCache(aDrawTarget, aString,
+ aRunStart, aRunLength,
+ aRunScript, aVertical,
+ aTextRun);
+ }
+ }
+
+ InitWordCache();
+
+ // the only flags we care about for ShapedWord construction/caching
+ uint32_t flags = aTextRun->GetFlags();
+ flags &= (gfxTextRunFactory::TEXT_IS_RTL |
+ gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
+ gfxTextRunFactory::TEXT_USE_MATH_SCRIPT |
+ gfxTextRunFactory::TEXT_ORIENT_MASK);
+ if (sizeof(T) == sizeof(uint8_t)) {
+ flags |= gfxTextRunFactory::TEXT_IS_8BIT;
+ }
+
+ uint32_t wordStart = 0;
+ uint32_t hash = 0;
+ bool wordIs8Bit = true;
+ int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
+
+ T nextCh = aString[0];
+ for (uint32_t i = 0; i <= aRunLength; ++i) {
+ T ch = nextCh;
+ nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
+ T boundary = IsBoundarySpace(ch, nextCh);
+ bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
+ uint32_t length = i - wordStart;
+
+ // break into separate ShapedWords when we hit an invalid char,
+ // or a boundary space (always handled individually),
+ // or the first non-space after a space
+ if (!boundary && !invalid) {
+ if (!IsChar8Bit(ch)) {
+ wordIs8Bit = false;
+ }
+ // include this character in the hash, and move on to next
+ hash = gfxShapedWord::HashMix(hash, ch);
+ continue;
+ }
+
+ // We've decided to break here (i.e. we're at the end of a "word");
+ // shape the word and add it to the textrun.
+ // For words longer than the limit, we don't use the
+ // font's word cache but just shape directly into the textrun.
+ if (length > wordCacheCharLimit) {
+ TEXT_PERF_INCR(tp, wordCacheLong);
+ bool ok = ShapeFragmentWithoutWordCache(aDrawTarget,
+ aString + wordStart,
+ aRunStart + wordStart,
+ length,
+ aRunScript,
+ aVertical,
+ aTextRun);
+ if (!ok) {
+ return false;
+ }
+ } else if (length > 0) {
+ uint32_t wordFlags = flags;
+ // in the 8-bit version of this method, TEXT_IS_8BIT was
+ // already set as part of |flags|, so no need for a per-word
+ // adjustment here
+ if (sizeof(T) == sizeof(char16_t)) {
+ if (wordIs8Bit) {
+ wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
+ }
+ }
+ gfxShapedWord* sw = GetShapedWord(aDrawTarget,
+ aString + wordStart, length,
+ hash, aRunScript, aVertical,
+ appUnitsPerDevUnit,
+ wordFlags, tp);
+ if (sw) {
+ aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
+ } else {
+ return false; // failed, presumably out of memory?
+ }
+ }
+
+ if (boundary) {
+ // word was terminated by a space: add that to the textrun
+ uint16_t orientation = flags & gfxTextRunFactory::TEXT_ORIENT_MASK;
+ if (orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+ orientation = aVertical ?
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT :
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+ }
+ if (boundary != ' ' ||
+ !aTextRun->SetSpaceGlyphIfSimple(this, aRunStart + i, ch,
+ orientation)) {
+ // Currently, the only "boundary" characters we recognize are
+ // space and no-break space, which are both 8-bit, so we force
+ // that flag (below). If we ever change IsBoundarySpace, we
+ // may need to revise this.
+ // Avoid tautological-constant-out-of-range-compare in 8-bit:
+ DebugOnly<char16_t> boundary16 = boundary;
+ NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
+ gfxShapedWord *sw =
+ GetShapedWord(aDrawTarget, &boundary, 1,
+ gfxShapedWord::HashMix(0, boundary),
+ aRunScript, aVertical, appUnitsPerDevUnit,
+ flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
+ if (sw) {
+ aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
+ } else {
+ return false;
+ }
+ }
+ hash = 0;
+ wordStart = i + 1;
+ wordIs8Bit = true;
+ continue;
+ }
+
+ if (i == aRunLength) {
+ break;
+ }
+
+ NS_ASSERTION(invalid,
+ "how did we get here except via an invalid char?");
+
+ // word was terminated by an invalid char: skip it,
+ // unless it's a control char that we want to show as a hexbox,
+ // but record where TAB or NEWLINE occur
+ if (ch == '\t') {
+ aTextRun->SetIsTab(aRunStart + i);
+ } else if (ch == '\n') {
+ aTextRun->SetIsNewline(aRunStart + i);
+ } else if (IsInvalidControlChar(ch) &&
+ !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
+ if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
+ ShapeFragmentWithoutWordCache(aDrawTarget, aString + i,
+ aRunStart + i, 1,
+ aRunScript, aVertical, aTextRun);
+ } else {
+ aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
+ }
+ }
+
+ hash = 0;
+ wordStart = i + 1;
+ wordIs8Bit = true;
+ }
+
+ return true;
+}
+
+// Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
+template bool
+gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const uint8_t *aString,
+ uint32_t aRunStart,
+ uint32_t aRunLength,
+ Script aRunScript,
+ bool aVertical);
+template bool
+gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const char16_t *aString,
+ uint32_t aRunStart,
+ uint32_t aRunLength,
+ Script aRunScript,
+ bool aVertical);
+
+template<>
+bool
+gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ uint8_t aMatchType,
+ uint16_t aOrientation,
+ Script aScript,
+ bool aSyntheticLower,
+ bool aSyntheticUpper)
+{
+ bool ok = true;
+
+ RefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
+ if (!smallCapsFont) {
+ NS_WARNING("failed to get reduced-size font for smallcaps!");
+ smallCapsFont = this;
+ }
+
+ enum RunCaseAction {
+ kNoChange,
+ kUppercaseReduce,
+ kUppercase
+ };
+
+ RunCaseAction runAction = kNoChange;
+ uint32_t runStart = 0;
+ bool vertical =
+ aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+
+ for (uint32_t i = 0; i <= aLength; ++i) {
+ uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
+ // a trailing surrogate as well as the
+ // current code unit.
+ RunCaseAction chAction = kNoChange;
+ // Unless we're at the end, figure out what treatment the current
+ // character will need.
+ if (i < aLength) {
+ uint32_t ch = aText[i];
+ if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
+ NS_IS_LOW_SURROGATE(aText[i + 1])) {
+ ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
+ extraCodeUnits = 1;
+ }
+ // Characters that aren't the start of a cluster are ignored here.
+ // They get added to whatever lowercase/non-lowercase run we're in.
+ if (IsClusterExtender(ch)) {
+ chAction = runAction;
+ } else {
+ if (ch != ToUpperCase(ch) || SpecialUpper(ch)) {
+ // ch is lower case
+ chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
+ } else if (ch != ToLowerCase(ch)) {
+ // ch is upper case
+ chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
+ if (mStyle.explicitLanguage &&
+ mStyle.language == nsGkAtoms::el) {
+ // In Greek, check for characters that will be modified by
+ // the GreekUpperCase mapping - this catches accented
+ // capitals where the accent is to be removed (bug 307039).
+ // These are handled by using the full-size font with the
+ // uppercasing transform.
+ mozilla::GreekCasing::State state;
+ uint32_t ch2 = mozilla::GreekCasing::UpperCase(ch, state);
+ if (ch != ch2 && !aSyntheticUpper) {
+ chAction = kUppercase;
+ }
+ }
+ }
+ }
+ }
+
+ // At the end of the text or when the current character needs different
+ // casing treatment from the current run, finish the run-in-progress
+ // and prepare to accumulate a new run.
+ // Note that we do not look at any source data for offset [i] here,
+ // as that would be invalid in the case where i==length.
+ if ((i == aLength || runAction != chAction) && runStart < i) {
+ uint32_t runLength = i - runStart;
+ gfxFont* f = this;
+ switch (runAction) {
+ case kNoChange:
+ // just use the current font and the existing string
+ aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
+ aOrientation);
+ if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
+ aText + runStart,
+ aOffset + runStart, runLength,
+ aScript, vertical)) {
+ ok = false;
+ }
+ break;
+
+ case kUppercaseReduce:
+ // use reduced-size font, then fall through to uppercase the text
+ f = smallCapsFont;
+ MOZ_FALLTHROUGH;
+
+ case kUppercase:
+ // apply uppercase transform to the string
+ nsDependentSubstring origString(aText + runStart, runLength);
+ nsAutoString convertedString;
+ AutoTArray<bool,50> charsToMergeArray;
+ AutoTArray<bool,50> deletedCharsArray;
+
+ bool mergeNeeded = nsCaseTransformTextRunFactory::
+ TransformString(origString,
+ convertedString,
+ true,
+ mStyle.explicitLanguage
+ ? mStyle.language.get() : nullptr,
+ charsToMergeArray,
+ deletedCharsArray);
+
+ if (mergeNeeded) {
+ // This is the hard case: the transformation caused chars
+ // to be inserted or deleted, so we can't shape directly
+ // into the destination textrun but have to handle the
+ // mismatch of character positions.
+ gfxTextRunFactory::Parameters params = {
+ aDrawTarget, nullptr, nullptr, nullptr, 0,
+ aTextRun->GetAppUnitsPerDevUnit()
+ };
+ RefPtr<gfxTextRun> tempRun(
+ gfxTextRun::Create(&params, convertedString.Length(),
+ aTextRun->GetFontGroup(), 0));
+ tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
+ if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
+ convertedString.BeginReading(),
+ 0, convertedString.Length(),
+ aScript, vertical)) {
+ ok = false;
+ } else {
+ RefPtr<gfxTextRun> mergedRun(
+ gfxTextRun::Create(&params, runLength,
+ aTextRun->GetFontGroup(), 0));
+ MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
+ charsToMergeArray.Elements(),
+ deletedCharsArray.Elements());
+ gfxTextRun::Range runRange(0, runLength);
+ aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
+ aOffset + runStart);
+ }
+ } else {
+ aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
+ true, aOrientation);
+ if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
+ convertedString.BeginReading(),
+ aOffset + runStart, runLength,
+ aScript, vertical)) {
+ ok = false;
+ }
+ }
+ break;
+ }
+
+ runStart = i;
+ }
+
+ i += extraCodeUnits;
+ if (i < aLength) {
+ runAction = chAction;
+ }
+ }
+
+ return ok;
+}
+
+template<>
+bool
+gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const uint8_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ uint8_t aMatchType,
+ uint16_t aOrientation,
+ Script aScript,
+ bool aSyntheticLower,
+ bool aSyntheticUpper)
+{
+ NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
+ aLength);
+ return InitFakeSmallCapsRun(aDrawTarget, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
+ aOffset, aLength, aMatchType, aOrientation,
+ aScript, aSyntheticLower, aSyntheticUpper);
+}
+
+already_AddRefed<gfxFont>
+gfxFont::GetSmallCapsFont()
+{
+ gfxFontStyle style(*GetStyle());
+ style.size *= SMALL_CAPS_SCALE_FACTOR;
+ style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+ gfxFontEntry* fe = GetFontEntry();
+ bool needsBold = style.weight >= 600 && !fe->IsBold();
+ return fe->FindOrMakeFont(&style, needsBold, mUnicodeRangeMap);
+}
+
+already_AddRefed<gfxFont>
+gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
+{
+ gfxFontStyle style(*GetStyle());
+ style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
+ gfxFontEntry* fe = GetFontEntry();
+ bool needsBold = style.weight >= 600 && !fe->IsBold();
+ return fe->FindOrMakeFont(&style, needsBold, mUnicodeRangeMap);
+}
+
+static void
+DestroyRefCairo(void* aData)
+{
+ cairo_t* refCairo = static_cast<cairo_t*>(aData);
+ MOZ_ASSERT(refCairo);
+ cairo_destroy(refCairo);
+}
+
+/* static */ cairo_t *
+gfxFont::RefCairo(DrawTarget* aDT)
+{
+ // DrawTargets that don't use a Cairo backend can be given a 1x1 "reference"
+ // |cairo_t*|, stored in the DrawTarget's user data, for doing font-related
+ // operations.
+ static UserDataKey sRefCairo;
+
+ cairo_t* refCairo = nullptr;
+ if (aDT->GetBackendType() == BackendType::CAIRO) {
+ refCairo = static_cast<cairo_t*>
+ (aDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+ if (refCairo) {
+ return refCairo;
+ }
+ }
+
+ refCairo = static_cast<cairo_t*>(aDT->GetUserData(&sRefCairo));
+ if (!refCairo) {
+ refCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
+ aDT->AddUserData(&sRefCairo, refCairo, DestroyRefCairo);
+ }
+
+ return refCairo;
+}
+
+gfxGlyphExtents *
+gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
+ uint32_t i, count = mGlyphExtentsArray.Length();
+ for (i = 0; i < count; ++i) {
+ if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
+ return mGlyphExtentsArray[i].get();
+ }
+ gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
+ if (glyphExtents) {
+ mGlyphExtentsArray.AppendElement(glyphExtents);
+ // Initialize the extents of a space glyph, assuming that spaces don't
+ // render anything!
+ glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
+ }
+ return glyphExtents;
+}
+
+void
+gfxFont::SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
+ bool aNeedTight, gfxGlyphExtents *aExtents)
+{
+ gfxRect svgBounds;
+ if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
+ mFontEntry->GetSVGGlyphExtents(aDrawTarget, aGlyphID, &svgBounds)) {
+ gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
+ aExtents->SetTightGlyphExtents(aGlyphID,
+ gfxRect(svgBounds.x * d2a,
+ svgBounds.y * d2a,
+ svgBounds.width * d2a,
+ svgBounds.height * d2a));
+ return;
+ }
+
+ cairo_glyph_t glyph;
+ glyph.index = aGlyphID;
+ glyph.x = 0;
+ glyph.y = 0;
+ cairo_text_extents_t extents;
+ cairo_glyph_extents(gfxFont::RefCairo(aDrawTarget), &glyph, 1, &extents);
+
+ const Metrics& fontMetrics = GetMetrics(eHorizontal);
+ int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
+ if (!aNeedTight && extents.x_bearing >= 0 &&
+ extents.y_bearing >= -fontMetrics.maxAscent &&
+ extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
+ uint32_t appUnitsWidth =
+ uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
+ if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
+ aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
+ return;
+ }
+ }
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ if (!aNeedTight) {
+ ++gGlyphExtentsSetupFallBackToTight;
+ }
+#endif
+
+ gfxFloat d2a = appUnitsPerDevUnit;
+ gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
+ extents.width*d2a, extents.height*d2a);
+ aExtents->SetTightGlyphExtents(aGlyphID, bounds);
+}
+
+// Try to initialize font metrics by reading sfnt tables directly;
+// set mIsValid=TRUE and return TRUE on success.
+// Return FALSE if the gfxFontEntry subclass does not
+// implement GetFontTable(), or for non-sfnt fonts where tables are
+// not available.
+// If this returns TRUE without setting the mIsValid flag, then we -did-
+// apparently find an sfnt, but it was too broken to be used.
+bool
+gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
+{
+ mIsValid = false; // font is NOT valid in case of early return
+
+ const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
+ const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
+ const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
+
+ uint32_t len;
+
+ if (mFUnitsConvFactor < 0.0) {
+ // If the conversion factor from FUnits is not yet set,
+ // get the unitsPerEm from the 'head' table via the font entry
+ uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
+ if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
+ return false;
+ }
+ mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
+ }
+
+ // 'hhea' table is required to get vertical extents
+ gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
+ if (!hheaTable) {
+ return false; // no 'hhea' table -> not an sfnt
+ }
+ const MetricsHeader* hhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(hheaTable, &len));
+ if (len < sizeof(MetricsHeader)) {
+ return false;
+ }
+
+#define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
+#define SET_SIGNED(field,src) aMetrics.field = int16_t(src) * mFUnitsConvFactor
+
+ SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
+ SET_SIGNED(maxAscent, hhea->ascender);
+ SET_SIGNED(maxDescent, -int16_t(hhea->descender));
+ SET_SIGNED(externalLeading, hhea->lineGap);
+
+ // 'post' table is required for underline metrics
+ gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
+ if (!postTable) {
+ return true; // no 'post' table -> sfnt is not valid
+ }
+ const PostTable *post =
+ reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
+ if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
+ return true; // bad post table -> sfnt is not valid
+ }
+
+ SET_SIGNED(underlineOffset, post->underlinePosition);
+ SET_UNSIGNED(underlineSize, post->underlineThickness);
+
+ // 'OS/2' table is optional, if not found we'll estimate xHeight
+ // and aveCharWidth by measuring glyphs
+ gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
+ if (os2Table) {
+ const OS2Table *os2 =
+ reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
+ // although sxHeight and sCapHeight are signed fields, we consider
+ // negative values to be erroneous and just ignore them
+ if (uint16_t(os2->version) >= 2) {
+ // version 2 and later includes the x-height and cap-height fields
+ if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
+ int16_t(os2->sxHeight) > 0) {
+ SET_SIGNED(xHeight, os2->sxHeight);
+ }
+ if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
+ int16_t(os2->sCapHeight) > 0) {
+ SET_SIGNED(capHeight, os2->sCapHeight);
+ }
+ }
+ // this should always be present in any valid OS/2 of any version
+ if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
+ SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
+ SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
+ SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
+
+ // for fonts with USE_TYPO_METRICS set in the fsSelection field,
+ // let the OS/2 sTypo* metrics override those from the hhea table
+ // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
+ const uint16_t kUseTypoMetricsMask = 1 << 7;
+ if (uint16_t(os2->fsSelection) & kUseTypoMetricsMask) {
+ SET_SIGNED(maxAscent, os2->sTypoAscender);
+ SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
+ SET_SIGNED(externalLeading, os2->sTypoLineGap);
+ }
+ }
+ }
+
+#undef SET_SIGNED
+#undef SET_UNSIGNED
+
+ mIsValid = true;
+
+ return true;
+}
+
+static double
+RoundToNearestMultiple(double aValue, double aFraction)
+{
+ return floor(aValue/aFraction + 0.5) * aFraction;
+}
+
+void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
+{
+ aMetrics.maxAscent =
+ ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
+ aMetrics.maxDescent =
+ ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
+
+ if (aMetrics.xHeight <= 0) {
+ // only happens if we couldn't find either font metrics
+ // or a char to measure;
+ // pick an arbitrary value that's better than zero
+ aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
+ }
+
+ // If we have a font that doesn't provide a capHeight value, use maxAscent
+ // as a reasonable fallback.
+ if (aMetrics.capHeight <= 0) {
+ aMetrics.capHeight = aMetrics.maxAscent;
+ }
+
+ aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
+
+ if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
+ aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
+ } else {
+ aMetrics.internalLeading = 0.0;
+ }
+
+ aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
+ / aMetrics.maxHeight;
+ aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
+
+ if (GetFontEntry()->IsFixedPitch()) {
+ // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
+ // advance than the average character width... this forces
+ // those fonts to be recognized like fixed pitch fonts by layout.
+ aMetrics.maxAdvance = aMetrics.aveCharWidth;
+ }
+
+ if (!aMetrics.strikeoutOffset) {
+ aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
+ }
+ if (!aMetrics.strikeoutSize) {
+ aMetrics.strikeoutSize = aMetrics.underlineSize;
+ }
+}
+
+void
+gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
+{
+ // Even if this font size is zero, this font is created with non-zero size.
+ // However, for layout and others, we should return the metrics of zero size font.
+ if (mStyle.size == 0.0 || mStyle.sizeAdjust == 0.0) {
+ memset(aMetrics, 0, sizeof(gfxFont::Metrics));
+ return;
+ }
+
+ aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
+ aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
+
+ aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
+
+ if (aMetrics->maxAscent < 1.0) {
+ // We cannot draw strikeout line and overline in the ascent...
+ aMetrics->underlineSize = 0;
+ aMetrics->underlineOffset = 0;
+ aMetrics->strikeoutSize = 0;
+ aMetrics->strikeoutOffset = 0;
+ return;
+ }
+
+ /**
+ * Some CJK fonts have bad underline offset. Therefore, if this is such font,
+ * we need to lower the underline offset to bottom of *em* descent.
+ * However, if this is system font, we should not do this for the rendering compatibility with
+ * another application's UI on the platform.
+ * XXX Should not use this hack if the font size is too small?
+ * Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
+ */
+ if (!mStyle.systemFont && aIsBadUnderlineFont) {
+ // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
+ // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
+ aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
+
+ // Next, we put the underline to bottom of below of the descent space.
+ if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
+ aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
+ } else {
+ aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
+ aMetrics->underlineSize - aMetrics->emDescent);
+ }
+ }
+ // If underline positioned is too far from the text, descent position is preferred so that underline
+ // will stay within the boundary.
+ else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
+ if (aMetrics->underlineSize > aMetrics->maxDescent)
+ aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
+ // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
+ aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
+ }
+
+ // If strikeout line is overflowed from the ascent, the line should be resized and moved for
+ // that being in the ascent space.
+ // Note that the strikeoutOffset is *middle* of the strikeout line position.
+ gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
+ if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
+ if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
+ aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
+ halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
+ }
+ gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
+ aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
+ }
+
+ // If overline is larger than the ascent, the line should be resized.
+ if (aMetrics->underlineSize > aMetrics->maxAscent) {
+ aMetrics->underlineSize = aMetrics->maxAscent;
+ }
+}
+
+// Create a Metrics record to be used for vertical layout. This should never
+// fail, as we've already decided this is a valid font. We do not have the
+// option of marking it invalid (as can happen if we're unable to read
+// horizontal metrics), because that could break a font that we're already
+// using for horizontal text.
+// So we will synthesize *something* usable here even if there aren't any of the
+// usual font tables (which can happen in the case of a legacy bitmap or Type1
+// font for which the platform-specific backend used platform APIs instead of
+// sfnt tables to create the horizontal metrics).
+const gfxFont::Metrics*
+gfxFont::CreateVerticalMetrics()
+{
+ const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
+ const uint32_t kVheaTableTag = TRUETYPE_TAG('v','h','e','a');
+ const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
+ const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
+ uint32_t len;
+
+ Metrics* metrics = new Metrics;
+ ::memset(metrics, 0, sizeof(Metrics));
+
+ // Some basic defaults, in case the font lacks any real metrics tables.
+ // TODO: consider what rounding (if any) we should apply to these.
+ metrics->emHeight = GetAdjustedSize();
+ metrics->emAscent = metrics->emHeight / 2;
+ metrics->emDescent = metrics->emHeight - metrics->emAscent;
+
+ metrics->maxAscent = metrics->emAscent;
+ metrics->maxDescent = metrics->emDescent;
+
+ const float UNINITIALIZED_LEADING = -10000.0f;
+ metrics->externalLeading = UNINITIALIZED_LEADING;
+
+ if (mFUnitsConvFactor < 0.0) {
+ uint16_t upem = GetFontEntry()->UnitsPerEm();
+ if (upem != gfxFontEntry::kInvalidUPEM) {
+ mFUnitsConvFactor = GetAdjustedSize() / upem;
+ }
+ }
+
+#define SET_UNSIGNED(field,src) metrics->field = uint16_t(src) * mFUnitsConvFactor
+#define SET_SIGNED(field,src) metrics->field = int16_t(src) * mFUnitsConvFactor
+
+ gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
+ if (os2Table && mFUnitsConvFactor >= 0.0) {
+ const OS2Table *os2 =
+ reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
+ // These fields should always be present in any valid OS/2 table
+ if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
+ SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
+ // Use ascent+descent from the horizontal metrics as the default
+ // advance (aveCharWidth) in vertical mode
+ gfxFloat ascentDescent = gfxFloat(mFUnitsConvFactor) *
+ (int16_t(os2->sTypoAscender) - int16_t(os2->sTypoDescender));
+ metrics->aveCharWidth =
+ std::max(metrics->emHeight, ascentDescent);
+ // Use xAvgCharWidth from horizontal metrics as minimum font extent
+ // for vertical layout, applying half of it to ascent and half to
+ // descent (to work with a default centered baseline).
+ gfxFloat halfCharWidth =
+ int16_t(os2->xAvgCharWidth) * gfxFloat(mFUnitsConvFactor) / 2;
+ metrics->maxAscent = std::max(metrics->maxAscent, halfCharWidth);
+ metrics->maxDescent = std::max(metrics->maxDescent, halfCharWidth);
+ }
+ }
+
+ // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
+ // and use the line height from its ascent/descent.
+ if (!metrics->aveCharWidth) {
+ gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
+ if (hheaTable && mFUnitsConvFactor >= 0.0) {
+ const MetricsHeader* hhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(hheaTable, &len));
+ if (len >= sizeof(MetricsHeader)) {
+ SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
+ int16_t(hhea->descender));
+ metrics->maxAscent = metrics->aveCharWidth / 2;
+ metrics->maxDescent =
+ metrics->aveCharWidth - metrics->maxAscent;
+ }
+ }
+ }
+
+ // Read real vertical metrics if available.
+ gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
+ if (vheaTable && mFUnitsConvFactor >= 0.0) {
+ const MetricsHeader* vhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(vheaTable, &len));
+ if (len >= sizeof(MetricsHeader)) {
+ SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
+ // Redistribute space between ascent/descent because we want a
+ // centered vertical baseline by default.
+ gfxFloat halfExtent = 0.5 * gfxFloat(mFUnitsConvFactor) *
+ (int16_t(vhea->ascender) + std::abs(int16_t(vhea->descender)));
+ // Some bogus fonts have ascent and descent set to zero in 'vhea'.
+ // In that case we just ignore them and keep our synthetic values
+ // from above.
+ if (halfExtent > 0) {
+ metrics->maxAscent = halfExtent;
+ metrics->maxDescent = halfExtent;
+ SET_SIGNED(externalLeading, vhea->lineGap);
+ }
+ }
+ }
+
+ // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
+ // font of some kind (Type1, bitmap, vector, ...), so fall back to using
+ // whatever the platform backend figured out for horizontal layout.
+ // And if we haven't set externalLeading yet, then copy that from the
+ // horizontal metrics as well, to help consistency of CSS line-height.
+ if (!metrics->aveCharWidth ||
+ metrics->externalLeading == UNINITIALIZED_LEADING) {
+ const Metrics& horizMetrics = GetHorizontalMetrics();
+ if (!metrics->aveCharWidth) {
+ metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent;
+ }
+ if (metrics->externalLeading == UNINITIALIZED_LEADING) {
+ metrics->externalLeading = horizMetrics.externalLeading;
+ }
+ }
+
+ // Get underline thickness from the 'post' table if available.
+ gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
+ if (postTable) {
+ const PostTable *post =
+ reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable,
+ &len));
+ if (len >= offsetof(PostTable, underlineThickness) +
+ sizeof(uint16_t)) {
+ SET_UNSIGNED(underlineSize, post->underlineThickness);
+ // Also use for strikeout if we didn't find that in OS/2 above.
+ if (!metrics->strikeoutSize) {
+ metrics->strikeoutSize = metrics->underlineSize;
+ }
+ }
+ }
+
+#undef SET_UNSIGNED
+#undef SET_SIGNED
+
+ // If we didn't read this from a vhea table, it will still be zero.
+ // In any case, let's make sure it is not less than the value we've
+ // come up with for aveCharWidth.
+ metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth);
+
+ // Thickness of underline and strikeout may have been read from tables,
+ // but in case they were not present, ensure a minimum of 1 pixel.
+ // We synthesize our own positions, as font metrics don't provide these
+ // for vertical layout.
+ metrics->underlineSize = std::max(1.0, metrics->underlineSize);
+ metrics->underlineOffset = - metrics->maxDescent - metrics->underlineSize;
+
+ metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize);
+ metrics->strikeoutOffset = - 0.5 * metrics->strikeoutSize;
+
+ // Somewhat arbitrary values for now, subject to future refinement...
+ metrics->spaceWidth = metrics->aveCharWidth;
+ metrics->zeroOrAveCharWidth = metrics->aveCharWidth;
+ metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
+ metrics->xHeight = metrics->emHeight / 2;
+ metrics->capHeight = metrics->maxAscent;
+
+ return metrics;
+}
+
+gfxFloat
+gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
+{
+ // return an appropriate width for various Unicode space characters
+ // that we "fake" if they're not actually present in the font;
+ // returns negative value if the char is not a known space.
+ switch (aCh) {
+ case 0x2000: // en quad
+ case 0x2002: return GetAdjustedSize() / 2; // en space
+ case 0x2001: // em quad
+ case 0x2003: return GetAdjustedSize(); // em space
+ case 0x2004: return GetAdjustedSize() / 3; // three-per-em space
+ case 0x2005: return GetAdjustedSize() / 4; // four-per-em space
+ case 0x2006: return GetAdjustedSize() / 6; // six-per-em space
+ case 0x2007: return GetMetrics(eHorizontal).zeroOrAveCharWidth; // figure space
+ case 0x2008: return GetMetrics(eHorizontal).spaceWidth; // punctuation space
+ case 0x2009: return GetAdjustedSize() / 5; // thin space
+ case 0x200a: return GetAdjustedSize() / 10; // hair space
+ case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space
+ default: return -1.0;
+ }
+}
+
+void
+gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
+ aSizes->mFontInstances +=
+ mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (mWordCache) {
+ aSizes->mShapedWords += mWordCache->SizeOfIncludingThis(aMallocSizeOf);
+ }
+}
+
+void
+gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+void
+gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
+{
+ if (!mGlyphChangeObservers) {
+ mGlyphChangeObservers.reset(
+ new nsTHashtable<nsPtrHashKey<GlyphChangeObserver>>);
+ }
+ mGlyphChangeObservers->PutEntry(aObserver);
+}
+
+void
+gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
+{
+ NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
+ NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
+ mGlyphChangeObservers->RemoveEntry(aObserver);
+}
+
+
+#define DEFAULT_PIXEL_FONT_SIZE 16.0f
+
+/*static*/ uint32_t
+gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
+{
+ if (!aLangTag.Length() || aLangTag.Length() > 4) {
+ return NO_FONT_LANGUAGE_OVERRIDE;
+ }
+ uint32_t index, result = 0;
+ for (index = 0; index < aLangTag.Length(); ++index) {
+ char16_t ch = aLangTag[index];
+ if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
+ return NO_FONT_LANGUAGE_OVERRIDE;
+ }
+ result = (result << 8) + ch;
+ }
+ while (index++ < 4) {
+ result = (result << 8) + 0x20;
+ }
+ return result;
+}
+
+gfxFontStyle::gfxFontStyle() :
+ language(nsGkAtoms::x_western),
+ size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(-1.0f), baselineOffset(0.0f),
+ languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
+ weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
+ systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
+ style(NS_FONT_STYLE_NORMAL),
+ allowSyntheticWeight(true), allowSyntheticStyle(true),
+ noFallbackVariantFeatures(true),
+ explicitLanguage(false),
+ variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
+ variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
+{
+}
+
+gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
+ gfxFloat aSize,
+ nsIAtom *aLanguage, bool aExplicitLanguage,
+ float aSizeAdjust, bool aSystemFont,
+ bool aPrinterFont,
+ bool aAllowWeightSynthesis,
+ bool aAllowStyleSynthesis,
+ const nsString& aLanguageOverride):
+ language(aLanguage),
+ size(aSize), sizeAdjust(aSizeAdjust), baselineOffset(0.0f),
+ languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
+ weight(aWeight), stretch(aStretch),
+ systemFont(aSystemFont), printerFont(aPrinterFont),
+ useGrayscaleAntialiasing(false),
+ style(aStyle),
+ allowSyntheticWeight(aAllowWeightSynthesis),
+ allowSyntheticStyle(aAllowStyleSynthesis),
+ noFallbackVariantFeatures(true),
+ explicitLanguage(aExplicitLanguage),
+ variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
+ variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
+{
+ MOZ_ASSERT(!mozilla::IsNaN(size));
+ MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
+
+ if (weight > 900)
+ weight = 900;
+ if (weight < 100)
+ weight = 100;
+
+ if (size >= FONT_MAX_SIZE) {
+ size = FONT_MAX_SIZE;
+ sizeAdjust = -1.0f;
+ } else if (size < 0.0) {
+ NS_WARNING("negative font size");
+ size = 0.0;
+ }
+
+ if (!language) {
+ NS_WARNING("null language");
+ language = nsGkAtoms::x_western;
+ }
+}
+
+gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
+ language(aStyle.language),
+ featureValueLookup(aStyle.featureValueLookup),
+ size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
+ baselineOffset(aStyle.baselineOffset),
+ languageOverride(aStyle.languageOverride),
+ weight(aStyle.weight), stretch(aStyle.stretch),
+ systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
+ useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
+ style(aStyle.style),
+ allowSyntheticWeight(aStyle.allowSyntheticWeight),
+ allowSyntheticStyle(aStyle.allowSyntheticStyle),
+ noFallbackVariantFeatures(aStyle.noFallbackVariantFeatures),
+ explicitLanguage(aStyle.explicitLanguage),
+ variantCaps(aStyle.variantCaps),
+ variantSubSuper(aStyle.variantSubSuper)
+{
+ featureSettings.AppendElements(aStyle.featureSettings);
+ alternateValues.AppendElements(aStyle.alternateValues);
+}
+
+int8_t
+gfxFontStyle::ComputeWeight() const
+{
+ int8_t baseWeight = (weight + 50) / 100;
+
+ if (baseWeight < 0)
+ baseWeight = 0;
+ if (baseWeight > 9)
+ baseWeight = 9;
+
+ return baseWeight;
+}
+
+void
+gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)
+{
+ NS_PRECONDITION(variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
+ baselineOffset == 0,
+ "can't adjust this style for sub/superscript");
+
+ // calculate the baseline offset (before changing the size)
+ if (variantSubSuper == NS_FONT_VARIANT_POSITION_SUPER) {
+ baselineOffset = size * -NS_FONT_SUPERSCRIPT_OFFSET_RATIO;
+ } else {
+ baselineOffset = size * NS_FONT_SUBSCRIPT_OFFSET_RATIO;
+ }
+
+ // calculate reduced size, roughly mimicing behavior of font-size: smaller
+ float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
+ if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
+ size *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
+ } else if (cssSize >= NS_FONT_SUB_SUPER_LARGE_SIZE) {
+ size *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
+ } else {
+ gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
+ (NS_FONT_SUB_SUPER_LARGE_SIZE -
+ NS_FONT_SUB_SUPER_SMALL_SIZE);
+ size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
+ t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
+ }
+
+ // clear the variant field
+ variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
+}
+
+bool
+gfxFont::TryGetMathTable()
+{
+ if (!mMathInitialized) {
+ mMathInitialized = true;
+
+ hb_face_t *face = GetFontEntry()->GetHBFace();
+ if (face) {
+ if (hb_ot_math_has_data(face)) {
+ mMathTable = MakeUnique<gfxMathTable>(face, GetAdjustedSize());
+ }
+ hb_face_destroy(face);
+ }
+ }
+
+ return !!mMathTable;
+}
diff --git a/system/graphics/thebes/gfxFont.h b/system/graphics/thebes/gfxFont.h
new file mode 100644
index 000000000..ab1677b9c
--- /dev/null
+++ b/system/graphics/thebes/gfxFont.h
@@ -0,0 +1,2223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_H
+#define GFX_FONT_H
+
+#include "gfxTypes.h"
+#include "gfxFontEntry.h"
+#include "nsString.h"
+#include "gfxPoint.h"
+#include "gfxPattern.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "gfxRect.h"
+#include "nsExpirationTracker.h"
+#include "gfxPlatform.h"
+#include "nsIAtom.h"
+#include "mozilla/HashFunctions.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Attributes.h"
+#include <algorithm>
+#include "DrawMode.h"
+#include "nsDataHashtable.h"
+#include "harfbuzz/hb.h"
+#include "mozilla/gfx/2D.h"
+#include "nsColor.h"
+
+typedef struct _cairo cairo_t;
+typedef struct _cairo_scaled_font cairo_scaled_font_t;
+//typedef struct gr_face gr_face;
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+class gfxContext;
+class gfxTextRun;
+class gfxFont;
+class gfxGlyphExtents;
+class gfxShapedText;
+class gfxShapedWord;
+class gfxSkipChars;
+class gfxMathTable;
+
+// If you change this value, also change MAX_FONT_SIZE in cairo-ft-font.c to match
+#define FONT_MAX_SIZE 2000.0
+
+#define NO_FONT_LANGUAGE_OVERRIDE 0
+
+#define SMALL_CAPS_SCALE_FACTOR 0.8
+
+// The skew factor used for synthetic-italic [oblique] fonts;
+// we use a platform-dependent value to harmonize with the platform's own APIs.
+#ifdef XP_WIN
+#define OBLIQUE_SKEW_FACTOR 0.3
+#elif defined(MOZ_WIDGET_GTK)
+#define OBLIQUE_SKEW_FACTOR 0.2
+#else
+#define OBLIQUE_SKEW_FACTOR 0.25
+#endif
+
+struct gfxTextRunDrawCallbacks;
+
+namespace mozilla {
+class SVGContextPaint;
+namespace gfx {
+class GlyphRenderingOptions;
+} // namespace gfx
+} // namespace mozilla
+
+struct gfxFontStyle {
+ gfxFontStyle();
+ gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
+ gfxFloat aSize, nsIAtom *aLanguage, bool aExplicitLanguage,
+ float aSizeAdjust, bool aSystemFont,
+ bool aPrinterFont,
+ bool aWeightSynthesis, bool aStyleSynthesis,
+ const nsString& aLanguageOverride);
+ gfxFontStyle(const gfxFontStyle& aStyle);
+
+ // the language (may be an internal langGroup code rather than an actual
+ // language code) specified in the document or element's lang property,
+ // or inferred from the charset
+ RefPtr<nsIAtom> language;
+
+ // Features are composed of (1) features from style rules (2) features
+ // from feature setttings rules and (3) family-specific features. (1) and
+ // (3) are guaranteed to be mutually exclusive
+
+ // custom opentype feature settings
+ nsTArray<gfxFontFeature> featureSettings;
+
+ // Some font-variant property values require font-specific settings
+ // defined via @font-feature-values rules. These are resolved after
+ // font matching occurs.
+
+ // -- list of value tags for specific alternate features
+ nsTArray<gfxAlternateValue> alternateValues;
+
+ // -- object used to look these up once the font is matched
+ RefPtr<gfxFontFeatureValueSet> featureValueLookup;
+
+ // The logical size of the font, in pixels
+ gfxFloat size;
+
+ // The aspect-value (ie., the ratio actualsize:actualxheight) that any
+ // actual physical font created from this font structure must have when
+ // rendering or measuring a string. A value of -1.0 means no adjustment
+ // needs to be done; otherwise the value must be nonnegative.
+ float sizeAdjust;
+
+ // baseline offset, used when simulating sub/superscript glyphs
+ float baselineOffset;
+
+ // Language system tag, to override document language;
+ // an OpenType "language system" tag represented as a 32-bit integer
+ // (see http://www.microsoft.com/typography/otspec/languagetags.htm).
+ // Normally 0, so font rendering will use the document or element language
+ // (see above) to control any language-specific rendering, but the author
+ // can override this for cases where the options implemented in the font
+ // do not directly match the actual language. (E.g. lang may be Macedonian,
+ // but the font in use does not explicitly support this; the author can
+ // use font-language-override to request the Serbian option in the font
+ // in order to get correct glyph shapes.)
+ uint32_t languageOverride;
+
+ // The weight of the font: 100, 200, ... 900.
+ uint16_t weight;
+
+ // The stretch of the font (the sum of various NS_FONT_STRETCH_*
+ // constants; see gfxFontConstants.h).
+ int8_t stretch;
+
+ // Say that this font is a system font and therefore does not
+ // require certain fixup that we do for fonts from untrusted
+ // sources.
+ bool systemFont : 1;
+
+ // Say that this font is used for print or print preview.
+ bool printerFont : 1;
+
+ // Used to imitate -webkit-font-smoothing: antialiased
+ bool useGrayscaleAntialiasing : 1;
+
+ // The style of font (normal, italic, oblique)
+ uint8_t style : 2;
+
+ // Whether synthetic styles are allowed
+ bool allowSyntheticWeight : 1;
+ bool allowSyntheticStyle : 1;
+
+ // some variant features require fallback which complicates the shaping
+ // code, so set up a bool to indicate when shaping with fallback is needed
+ bool noFallbackVariantFeatures : 1;
+
+ // whether the |language| field comes from explicit lang tagging in the
+ // document, or was inferred from charset/system locale
+ bool explicitLanguage : 1;
+
+ // caps variant (small-caps, petite-caps, etc.)
+ uint8_t variantCaps;
+
+ // sub/superscript variant
+ uint8_t variantSubSuper;
+
+ // Return the final adjusted font size for the given aspect ratio.
+ // Not meant to be called when sizeAdjust = -1.0.
+ gfxFloat GetAdjustedSize(gfxFloat aspect) const {
+ NS_ASSERTION(sizeAdjust >= 0.0, "Not meant to be called when sizeAdjust = -1.0");
+ gfxFloat adjustedSize = std::max(NS_round(size*(sizeAdjust/aspect)), 1.0);
+ return std::min(adjustedSize, FONT_MAX_SIZE);
+ }
+
+ PLDHashNumber Hash() const {
+ return ((style + (systemFont << 7) +
+ (weight << 8)) + uint32_t(size*1000) + uint32_t(sizeAdjust*1000)) ^
+ nsISupportsHashKey::HashKey(language);
+ }
+
+ int8_t ComputeWeight() const;
+
+ // Adjust this style to simulate sub/superscript (as requested in the
+ // variantSubSuper field) using size and baselineOffset instead.
+ void AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel);
+
+ bool Equals(const gfxFontStyle& other) const {
+ return
+ (*reinterpret_cast<const uint64_t*>(&size) ==
+ *reinterpret_cast<const uint64_t*>(&other.size)) &&
+ (style == other.style) &&
+ (variantCaps == other.variantCaps) &&
+ (variantSubSuper == other.variantSubSuper) &&
+ (allowSyntheticWeight == other.allowSyntheticWeight) &&
+ (allowSyntheticStyle == other.allowSyntheticStyle) &&
+ (systemFont == other.systemFont) &&
+ (printerFont == other.printerFont) &&
+ (useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) &&
+ (explicitLanguage == other.explicitLanguage) &&
+ (weight == other.weight) &&
+ (stretch == other.stretch) &&
+ (language == other.language) &&
+ (baselineOffset == other.baselineOffset) &&
+ (*reinterpret_cast<const uint32_t*>(&sizeAdjust) ==
+ *reinterpret_cast<const uint32_t*>(&other.sizeAdjust)) &&
+ (featureSettings == other.featureSettings) &&
+ (languageOverride == other.languageOverride) &&
+ (alternateValues == other.alternateValues) &&
+ (featureValueLookup == other.featureValueLookup);
+ }
+
+ static void ParseFontFeatureSettings(const nsString& aFeatureString,
+ nsTArray<gfxFontFeature>& aFeatures);
+
+ static uint32_t ParseFontLanguageOverride(const nsString& aLangTag);
+};
+
+struct gfxTextRange {
+ enum {
+ // flags for recording the kind of font-matching that was used
+ kFontGroup = 0x0001,
+ kPrefsFallback = 0x0002,
+ kSystemFallback = 0x0004
+ };
+ gfxTextRange(uint32_t aStart, uint32_t aEnd,
+ gfxFont* aFont, uint8_t aMatchType,
+ uint16_t aOrientation)
+ : start(aStart),
+ end(aEnd),
+ font(aFont),
+ matchType(aMatchType),
+ orientation(aOrientation)
+ { }
+ uint32_t Length() const { return end - start; }
+ uint32_t start, end;
+ RefPtr<gfxFont> font;
+ uint8_t matchType;
+ uint16_t orientation;
+};
+
+
+/**
+ * Font cache design:
+ *
+ * The mFonts hashtable contains most fonts, indexed by (gfxFontEntry*, style).
+ * It does not add a reference to the fonts it contains.
+ * When a font's refcount decreases to zero, instead of deleting it we
+ * add it to our expiration tracker.
+ * The expiration tracker tracks fonts with zero refcount. After a certain
+ * period of time, such fonts expire and are deleted.
+ *
+ * We're using 3 generations with a ten-second generation interval, so
+ * zero-refcount fonts will be deleted 20-30 seconds after their refcount
+ * goes to zero, if timer events fire in a timely manner.
+ *
+ * The font cache also handles timed expiration of cached ShapedWords
+ * for "persistent" fonts: it has a repeating timer, and notifies
+ * each cached font to "age" its shaped words. The words will be released
+ * by the fonts if they get aged three times without being re-used in the
+ * meantime.
+ *
+ * Note that the ShapedWord timeout is much larger than the font timeout,
+ * so that in the case of a short-lived font, we'll discard the gfxFont
+ * completely, with all its words, and avoid the cost of aging the words
+ * individually. That only happens with longer-lived fonts.
+ */
+struct FontCacheSizes {
+ FontCacheSizes()
+ : mFontInstances(0), mShapedWords(0)
+ { }
+
+ size_t mFontInstances; // memory used by instances of gfxFont subclasses
+ size_t mShapedWords; // memory used by the per-font shapedWord caches
+};
+
+class gfxFontCache final : public nsExpirationTracker<gfxFont,3> {
+public:
+ enum {
+ FONT_TIMEOUT_SECONDS = 10,
+ SHAPED_WORD_TIMEOUT_SECONDS = 60
+ };
+
+ gfxFontCache();
+ ~gfxFontCache();
+
+ /*
+ * Get the global gfxFontCache. You must call Init() before
+ * calling this method --- the result will not be null.
+ */
+ static gfxFontCache* GetCache() {
+ return gGlobalCache;
+ }
+
+ static nsresult Init();
+ // It's OK to call this even if Init() has not been called.
+ static void Shutdown();
+
+ // Look up a font in the cache. Returns an addrefed pointer, or null
+ // if there's nothing matching in the cache
+ already_AddRefed<gfxFont>
+ Lookup(const gfxFontEntry* aFontEntry,
+ const gfxFontStyle* aStyle,
+ const gfxCharacterMap* aUnicodeRangeMap);
+
+ // We created a new font (presumably because Lookup returned null);
+ // put it in the cache. The font's refcount should be nonzero. It is
+ // allowable to add a new font even if there is one already in the
+ // cache with the same key; we'll forget about the old one.
+ void AddNew(gfxFont *aFont);
+
+ // The font's refcount has gone to zero; give ownership of it to
+ // the cache. We delete it if it's not acquired again after a certain
+ // amount of time.
+ void NotifyReleased(gfxFont *aFont);
+
+ // This gets called when the timeout has expired on a zero-refcount
+ // font; we just delete it.
+ virtual void NotifyExpired(gfxFont *aFont) override;
+
+ // Cleans out the hashtable and removes expired fonts waiting for cleanup.
+ // Other gfxFont objects may be still in use but they will be pushed
+ // into the expiration queues and removed.
+ void Flush() {
+ mFonts.Clear();
+ AgeAllGenerations();
+ }
+
+ void FlushShapedWordCaches();
+
+ void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+ void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+
+protected:
+ class MemoryReporter final : public nsIMemoryReporter
+ {
+ ~MemoryReporter() {}
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+ };
+
+ // Observer for notifications that the font cache cares about
+ class Observer final
+ : public nsIObserver
+ {
+ ~Observer() {}
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ };
+
+ void DestroyFont(gfxFont *aFont);
+
+ static gfxFontCache *gGlobalCache;
+
+ struct Key {
+ const gfxFontEntry* mFontEntry;
+ const gfxFontStyle* mStyle;
+ const gfxCharacterMap* mUnicodeRangeMap;
+ Key(const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle,
+ const gfxCharacterMap* aUnicodeRangeMap)
+ : mFontEntry(aFontEntry), mStyle(aStyle),
+ mUnicodeRangeMap(aUnicodeRangeMap)
+ {}
+ };
+
+ class HashEntry : public PLDHashEntryHdr {
+ public:
+ typedef const Key& KeyType;
+ typedef const Key* KeyTypePointer;
+
+ // When constructing a new entry in the hashtable, we'll leave this
+ // blank. The caller of Put() will fill this in.
+ explicit HashEntry(KeyTypePointer aStr) : mFont(nullptr) { }
+ HashEntry(const HashEntry& toCopy) : mFont(toCopy.mFont) { }
+ ~HashEntry() { }
+
+ bool KeyEquals(const KeyTypePointer aKey) const;
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey) {
+ return mozilla::HashGeneric(aKey->mStyle->Hash(), aKey->mFontEntry,
+ aKey->mUnicodeRangeMap);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+ gfxFont* mFont;
+ };
+
+ nsTHashtable<HashEntry> mFonts;
+
+ static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
+ nsCOMPtr<nsITimer> mWordCacheExpirationTimer;
+};
+
+class gfxTextPerfMetrics {
+public:
+
+ struct TextCounts {
+ uint32_t numContentTextRuns;
+ uint32_t numChromeTextRuns;
+ uint32_t numChars;
+ uint32_t maxTextRunLen;
+ uint32_t wordCacheSpaceRules;
+ uint32_t wordCacheLong;
+ uint32_t wordCacheHit;
+ uint32_t wordCacheMiss;
+ uint32_t fallbackPrefs;
+ uint32_t fallbackSystem;
+ uint32_t textrunConst;
+ uint32_t textrunDestr;
+ uint32_t genericLookups;
+ };
+
+ uint32_t reflowCount;
+
+ // counts per reflow operation
+ TextCounts current;
+
+ // totals for the lifetime of a document
+ TextCounts cumulative;
+
+ gfxTextPerfMetrics() {
+ memset(this, 0, sizeof(gfxTextPerfMetrics));
+ }
+
+ // add current totals to cumulative ones
+ void Accumulate() {
+ if (current.numChars == 0) {
+ return;
+ }
+ cumulative.numContentTextRuns += current.numContentTextRuns;
+ cumulative.numChromeTextRuns += current.numChromeTextRuns;
+ cumulative.numChars += current.numChars;
+ if (current.maxTextRunLen > cumulative.maxTextRunLen) {
+ cumulative.maxTextRunLen = current.maxTextRunLen;
+ }
+ cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules;
+ cumulative.wordCacheLong += current.wordCacheLong;
+ cumulative.wordCacheHit += current.wordCacheHit;
+ cumulative.wordCacheMiss += current.wordCacheMiss;
+ cumulative.fallbackPrefs += current.fallbackPrefs;
+ cumulative.fallbackSystem += current.fallbackSystem;
+ cumulative.textrunConst += current.textrunConst;
+ cumulative.textrunDestr += current.textrunDestr;
+ cumulative.genericLookups += current.genericLookups;
+ memset(&current, 0, sizeof(current));
+ }
+};
+
+class gfxTextRunFactory {
+ NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
+
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ // Flags in the mask 0xFFFF0000 are reserved for textrun clients
+ // Flags in the mask 0x0000F000 are reserved for per-platform fonts
+ // Flags in the mask 0x00000FFF are set by the textrun creator.
+ enum {
+ CACHE_TEXT_FLAGS = 0xF0000000,
+ USER_TEXT_FLAGS = 0x0FFF0000,
+ TEXTRUN_TEXT_FLAGS = 0x0000FFFF,
+ SETTABLE_FLAGS = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS,
+
+ /**
+ * When set, the text string pointer used to create the text run
+ * is guaranteed to be available during the lifetime of the text run.
+ */
+ TEXT_IS_PERSISTENT = 0x0001,
+ /**
+ * When set, the text is known to be all-ASCII (< 128).
+ */
+ TEXT_IS_ASCII = 0x0002,
+ /**
+ * When set, the text is RTL.
+ */
+ TEXT_IS_RTL = 0x0004,
+ /**
+ * When set, spacing is enabled and the textrun needs to call GetSpacing
+ * on the spacing provider.
+ */
+ TEXT_ENABLE_SPACING = 0x0008,
+ /**
+ * When set, GetHyphenationBreaks may return true for some character
+ * positions, otherwise it will always return false for all characters.
+ */
+ TEXT_ENABLE_HYPHEN_BREAKS = 0x0010,
+ /**
+ * When set, the text has no characters above 255 and it is stored
+ * in the textrun in 8-bit format.
+ */
+ TEXT_IS_8BIT = 0x0020,
+ /**
+ * When set, the RunMetrics::mBoundingBox field will be initialized
+ * properly based on glyph extents, in particular, glyph extents that
+ * overflow the standard font-box (the box defined by the ascent, descent
+ * and advance width of the glyph). When not set, it may just be the
+ * standard font-box even if glyphs overflow.
+ */
+ TEXT_NEED_BOUNDING_BOX = 0x0040,
+ /**
+ * When set, optional ligatures are disabled. Ligatures that are
+ * required for legible text should still be enabled.
+ */
+ TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
+ /**
+ * When set, the textrun should favour speed of construction over
+ * quality. This may involve disabling ligatures and/or kerning or
+ * other effects.
+ */
+ TEXT_OPTIMIZE_SPEED = 0x0100,
+ /**
+ * For internal use by the memory reporter when accounting for
+ * storage used by textruns.
+ * Because the reporter may visit each textrun multiple times while
+ * walking the frame trees and textrun cache, it needs to mark
+ * textruns that have been seen so as to avoid multiple-accounting.
+ */
+ TEXT_RUN_SIZE_ACCOUNTED = 0x0200,
+ /**
+ * When set, the textrun should discard control characters instead of
+ * turning them into hexboxes.
+ */
+ TEXT_HIDE_CONTROL_CHARACTERS = 0x0400,
+
+ /**
+ * Field for orientation of the textrun and glyphs within it.
+ * Possible values of the TEXT_ORIENT_MASK field:
+ * TEXT_ORIENT_HORIZONTAL
+ * TEXT_ORIENT_VERTICAL_UPRIGHT
+ * TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
+ * TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
+ * TEXT_ORIENT_VERTICAL_MIXED
+ * For all VERTICAL settings, the x and y coordinates of glyph
+ * positions are exchanged, so that simple advances are vertical.
+ *
+ * The MIXED value indicates vertical textRuns for which the CSS
+ * text-orientation property is 'mixed', but is never used for
+ * individual glyphRuns; it will be resolved to either UPRIGHT
+ * or SIDEWAYS_RIGHT according to the UTR50 properties of the
+ * characters, and separate glyphRuns created for the resulting
+ * glyph orientations.
+ */
+ TEXT_ORIENT_MASK = 0xF000,
+ TEXT_ORIENT_HORIZONTAL = 0x0000,
+ TEXT_ORIENT_VERTICAL_UPRIGHT = 0x1000,
+ TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT = 0x2000,
+ TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT = 0x4000,
+ TEXT_ORIENT_VERTICAL_MIXED = 0x8000,
+
+ /**
+ * nsTextFrameThebes sets these, but they're defined here rather than
+ * in nsTextFrameUtils.h because ShapedWord creation/caching also needs
+ * to check the _INCOMING flag
+ */
+ TEXT_TRAILING_ARABICCHAR = 0x20000000,
+ /**
+ * When set, the previous character for this textrun was an Arabic
+ * character. This is used for the context detection necessary for
+ * bidi.numeral implementation.
+ */
+ TEXT_INCOMING_ARABICCHAR = 0x40000000,
+
+ // Set if the textrun should use the OpenType 'math' script.
+ TEXT_USE_MATH_SCRIPT = 0x80000000,
+ };
+
+ /**
+ * This record contains all the parameters needed to initialize a textrun.
+ */
+ struct Parameters {
+ // Shape text params suggesting where the textrun will be rendered
+ DrawTarget *mDrawTarget;
+ // Pointer to arbitrary user data (which should outlive the textrun)
+ void *mUserData;
+ // A description of which characters have been stripped from the original
+ // DOM string to produce the characters in the textrun. May be null
+ // if that information is not relevant.
+ gfxSkipChars *mSkipChars;
+ // A list of where linebreaks are currently placed in the textrun. May
+ // be null if mInitialBreakCount is zero.
+ uint32_t *mInitialBreaks;
+ uint32_t mInitialBreakCount;
+ // The ratio to use to convert device pixels to application layout units
+ int32_t mAppUnitsPerDevUnit;
+ };
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxTextRunFactory() {}
+};
+
+/**
+ * gfxFontShaper
+ *
+ * This class implements text shaping (character to glyph mapping and
+ * glyph layout). There is a gfxFontShaper subclass for each text layout
+ * technology (uniscribe, core text, harfbuzz,....) we support.
+ *
+ * The shaper is responsible for setting up glyph data in gfxTextRuns.
+ *
+ * A generic, platform-independent shaper relies only on the standard
+ * gfxFont interface and can work with any concrete subclass of gfxFont.
+ *
+ * Platform-specific implementations designed to interface to platform
+ * shaping APIs such as Uniscribe or CoreText may rely on features of a
+ * specific font subclass to access native font references
+ * (such as CTFont, HFONT, DWriteFont, etc).
+ */
+
+class gfxFontShaper {
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::unicode::Script Script;
+
+ explicit gfxFontShaper(gfxFont *aFont)
+ : mFont(aFont)
+ {
+ NS_ASSERTION(aFont, "shaper requires a valid font!");
+ }
+
+ virtual ~gfxFontShaper() { }
+
+ // Shape a piece of text and store the resulting glyph data into
+ // aShapedText. Parameters aOffset/aLength indicate the range of
+ // aShapedText to be updated; aLength is also the length of aText.
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText) = 0;
+
+ gfxFont *GetFont() const { return mFont; }
+
+ static void
+ MergeFontFeatures(const gfxFontStyle *aStyle,
+ const nsTArray<gfxFontFeature>& aFontFeatures,
+ bool aDisableLigatures,
+ const nsAString& aFamilyName,
+ bool aAddSmallCaps,
+ void (*aHandleFeature)(const uint32_t&,
+ uint32_t&, void*),
+ void* aHandleFeatureData);
+
+protected:
+ // Work out whether cairo will snap inter-glyph spacing to pixels.
+ static void GetRoundOffsetsToPixels(DrawTarget* aDrawTarget,
+ bool* aRoundX, bool* aRoundY);
+
+ // the font this shaper is working with. The font owns a UniquePtr reference
+ // to this object, and will destroy it before it dies. Thus, mFont will always
+ // be valid.
+ gfxFont* MOZ_NON_OWNING_REF mFont;
+};
+
+
+/*
+ * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun.
+ * These are objects that store a list of zero or more glyphs for each character.
+ * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets.
+ * The idea is that a string is rendered by a loop that draws each glyph
+ * at its designated offset from the current point, then advances the current
+ * point by the glyph's advance in the direction of the textrun (LTR or RTL).
+ * Each glyph advance is always rounded to the nearest appunit; this ensures
+ * consistent results when dividing the text in a textrun into multiple text
+ * frames (frame boundaries are always aligned to appunits). We optimize
+ * for the case where a character has a single glyph and zero xoffset and yoffset,
+ * and the glyph ID and advance are in a reasonable range so we can pack all
+ * necessary data into 32 bits.
+ *
+ * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont)
+ * or directly into a gfxTextRun (for cases where we want to shape textruns in
+ * their entirety rather than using cached words, because there may be layout
+ * features that depend on the inter-word spaces).
+ */
+class gfxShapedText
+{
+public:
+ typedef mozilla::unicode::Script Script;
+
+ gfxShapedText(uint32_t aLength, uint32_t aFlags,
+ int32_t aAppUnitsPerDevUnit)
+ : mLength(aLength)
+ , mFlags(aFlags)
+ , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
+ { }
+
+ virtual ~gfxShapedText() { }
+
+ /**
+ * This class records the information associated with a character in the
+ * input string. It's optimized for the case where there is one glyph
+ * representing that character alone.
+ *
+ * A character can have zero or more associated glyphs. Each glyph
+ * has an advance width and an x and y offset.
+ * A character may be the start of a cluster.
+ * A character may be the start of a ligature group.
+ * A character can be "missing", indicating that the system is unable
+ * to render the character.
+ *
+ * All characters in a ligature group conceptually share all the glyphs
+ * associated with the characters in a group.
+ */
+ class CompressedGlyph {
+ public:
+ CompressedGlyph() { mValue = 0; }
+
+ enum {
+ // Indicates that a cluster and ligature group starts at this
+ // character; this character has a single glyph with a reasonable
+ // advance and zero offsets. A "reasonable" advance
+ // is one that fits in the available bits (currently 12) (specified
+ // in appunits).
+ FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
+
+ // Indicates whether a linebreak is allowed before this character;
+ // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value
+ // indicating the kind of linebreak (if any) allowed here.
+ FLAGS_CAN_BREAK_BEFORE = 0x60000000U,
+
+ FLAGS_CAN_BREAK_SHIFT = 29,
+ FLAG_BREAK_TYPE_NONE = 0,
+ FLAG_BREAK_TYPE_NORMAL = 1,
+ FLAG_BREAK_TYPE_HYPHEN = 2,
+
+ FLAG_CHAR_IS_SPACE = 0x10000000U,
+
+ // The advance is stored in appunits
+ ADVANCE_MASK = 0x0FFF0000U,
+ ADVANCE_SHIFT = 16,
+
+ GLYPH_MASK = 0x0000FFFFU,
+
+ // Non-simple glyphs may or may not have glyph data in the
+ // corresponding mDetailedGlyphs entry. They have the following
+ // flag bits:
+
+ // When NOT set, indicates that this character corresponds to a
+ // missing glyph and should be skipped (or possibly, render the character
+ // Unicode value in some special way). If there are glyphs,
+ // the mGlyphID is actually the UTF16 character code. The bit is
+ // inverted so we can memset the array to zero to indicate all missing.
+ FLAG_NOT_MISSING = 0x01,
+ FLAG_NOT_CLUSTER_START = 0x02,
+ FLAG_NOT_LIGATURE_GROUP_START = 0x04,
+
+ FLAG_CHAR_IS_TAB = 0x08,
+ FLAG_CHAR_IS_NEWLINE = 0x10,
+ // Per CSS Text Decoration Module Level 3, emphasis marks are not
+ // drawn for any character in Unicode categories Z*, Cc, Cf, and Cn
+ // which is not combined with any combining characters. This flag is
+ // set for all those characters except 0x20 whitespace.
+ FLAG_CHAR_NO_EMPHASIS_MARK = 0x20,
+ CHAR_TYPE_FLAGS_MASK = 0x38,
+
+ GLYPH_COUNT_MASK = 0x00FFFF00U,
+ GLYPH_COUNT_SHIFT = 8
+ };
+
+ // "Simple glyphs" have a simple glyph ID, simple advance and their
+ // x and y offsets are zero. Also the glyph extents do not overflow
+ // the font-box defined by the font ascent, descent and glyph advance width.
+ // These case is optimized to avoid storing DetailedGlyphs.
+
+ // Returns true if the glyph ID aGlyph fits into the compressed representation
+ static bool IsSimpleGlyphID(uint32_t aGlyph) {
+ return (aGlyph & GLYPH_MASK) == aGlyph;
+ }
+ // Returns true if the advance aAdvance fits into the compressed representation.
+ // aAdvance is in appunits.
+ static bool IsSimpleAdvance(uint32_t aAdvance) {
+ return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
+ }
+
+ bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
+ uint32_t GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
+ uint32_t GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
+
+ bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; }
+ bool IsClusterStart() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START);
+ }
+ bool IsLigatureGroupStart() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
+ }
+ bool IsLigatureContinuation() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 &&
+ (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) ==
+ (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING);
+ }
+
+ // Return true if the original character was a normal (breakable,
+ // trimmable) space (U+0020). Not true for other characters that
+ // may happen to map to the space glyph (U+00A0).
+ bool CharIsSpace() const {
+ return (mValue & FLAG_CHAR_IS_SPACE) != 0;
+ }
+
+ bool CharIsTab() const {
+ return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0;
+ }
+ bool CharIsNewline() const {
+ return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0;
+ }
+ bool CharMayHaveEmphasisMark() const {
+ return !CharIsSpace() &&
+ (IsSimpleGlyph() || !(mValue & FLAG_CHAR_NO_EMPHASIS_MARK));
+ }
+
+ uint32_t CharTypeFlags() const {
+ return IsSimpleGlyph() ? 0 : (mValue & CHAR_TYPE_FLAGS_MASK);
+ }
+
+ void SetClusterStart(bool aIsClusterStart) {
+ NS_ASSERTION(!IsSimpleGlyph(),
+ "can't call SetClusterStart on simple glyphs");
+ if (aIsClusterStart) {
+ mValue &= ~FLAG_NOT_CLUSTER_START;
+ } else {
+ mValue |= FLAG_NOT_CLUSTER_START;
+ }
+ }
+
+ uint8_t CanBreakBefore() const {
+ return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT;
+ }
+ // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
+ uint32_t SetCanBreakBefore(uint8_t aCanBreakBefore) {
+ NS_ASSERTION(aCanBreakBefore <= 2,
+ "Bogus break-before value!");
+ uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
+ uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
+ mValue ^= toggle;
+ return toggle;
+ }
+
+ CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
+ NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
+ NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
+ NS_ASSERTION(!CharTypeFlags(), "Char type flags lost");
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
+ FLAG_IS_SIMPLE_GLYPH |
+ (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
+ return *this;
+ }
+ CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
+ uint32_t aGlyphCount) {
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
+ FLAG_NOT_MISSING |
+ CharTypeFlags() |
+ (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
+ (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
+ (aGlyphCount << GLYPH_COUNT_SHIFT);
+ return *this;
+ }
+ /**
+ * Missing glyphs are treated as ligature group starts; don't mess with
+ * the cluster-start flag (see bugs 618870 and 619286).
+ */
+ CompressedGlyph& SetMissing(uint32_t aGlyphCount) {
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START |
+ FLAG_CHAR_IS_SPACE)) |
+ CharTypeFlags() |
+ (aGlyphCount << GLYPH_COUNT_SHIFT);
+ return *this;
+ }
+ uint32_t GetGlyphCount() const {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
+ }
+
+ void SetIsSpace() {
+ mValue |= FLAG_CHAR_IS_SPACE;
+ }
+ void SetIsTab() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_IS_TAB;
+ }
+ void SetIsNewline() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_IS_NEWLINE;
+ }
+ void SetNoEmphasisMark() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_NO_EMPHASIS_MARK;
+ }
+
+ private:
+ uint32_t mValue;
+ };
+
+ // Accessor for the array of CompressedGlyph records, which will be in
+ // a different place in gfxShapedWord vs gfxTextRun
+ virtual const CompressedGlyph *GetCharacterGlyphs() const = 0;
+ virtual CompressedGlyph *GetCharacterGlyphs() = 0;
+
+ /**
+ * When the glyphs for a character don't fit into a CompressedGlyph record
+ * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
+ */
+ struct DetailedGlyph {
+ /** The glyphID, or the Unicode character
+ * if this is a missing glyph */
+ uint32_t mGlyphID;
+ /** The advance, x-offset and y-offset of the glyph, in appunits
+ * mAdvance is in the text direction (RTL or LTR)
+ * mXOffset is always from left to right
+ * mYOffset is always from top to bottom */
+ int32_t mAdvance;
+ float mXOffset, mYOffset;
+ };
+
+ void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
+ const DetailedGlyph *aGlyphs);
+
+ void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
+
+ void SetIsSpace(uint32_t aIndex) {
+ GetCharacterGlyphs()[aIndex].SetIsSpace();
+ }
+
+ bool HasDetailedGlyphs() const {
+ return mDetailedGlyphs != nullptr;
+ }
+
+ bool IsLigatureGroupStart(uint32_t aPos) {
+ NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+ return GetCharacterGlyphs()[aPos].IsLigatureGroupStart();
+ }
+
+ // NOTE that this must not be called for a character offset that does
+ // not have any DetailedGlyph records; callers must have verified that
+ // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero.
+ DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) const {
+ NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() &&
+ !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() &&
+ GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0,
+ "invalid use of GetDetailedGlyphs; check the caller!");
+ return mDetailedGlyphs->Get(aCharIndex);
+ }
+
+ void AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+ uint32_t aOffset, uint32_t aLength);
+
+ // Mark clusters in the CompressedGlyph records, starting at aOffset,
+ // based on the Unicode properties of the text in aString.
+ // This is also responsible to set the IsSpace flag for space characters.
+ void SetupClusterBoundaries(uint32_t aOffset,
+ const char16_t *aString,
+ uint32_t aLength);
+ // In 8-bit text, there won't actually be any clusters, but we still need
+ // the space-marking functionality.
+ void SetupClusterBoundaries(uint32_t aOffset,
+ const uint8_t *aString,
+ uint32_t aLength);
+
+ uint32_t GetFlags() const {
+ return mFlags;
+ }
+
+ bool IsVertical() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK) !=
+ gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL;
+ }
+
+ bool UseCenterBaseline() const {
+ uint32_t orient = GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK;
+ return orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED ||
+ orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+ }
+
+ bool IsRightToLeft() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
+ }
+
+ bool IsSidewaysLeft() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK) ==
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
+ }
+
+ // Return true if the logical inline direction is reversed compared to
+ // normal physical coordinates (i.e. if it is leftwards or upwards)
+ bool IsInlineReversed() const {
+ return IsSidewaysLeft() != IsRightToLeft();
+ }
+
+ gfxFloat GetDirection() const {
+ return IsInlineReversed() ? -1.0f : 1.0f;
+ }
+
+ bool DisableLigatures() const {
+ return (GetFlags() &
+ gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
+ }
+
+ bool TextIs8Bit() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
+ }
+
+ int32_t GetAppUnitsPerDevUnit() const {
+ return mAppUnitsPerDevUnit;
+ }
+
+ uint32_t GetLength() const {
+ return mLength;
+ }
+
+ bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
+
+protected:
+ // Allocate aCount DetailedGlyphs for the given index
+ DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
+ uint32_t aCount);
+
+ // Ensure the glyph on the given index is complex glyph so that we can use
+ // it to record specific characters that layout may need to detect.
+ void EnsureComplexGlyph(uint32_t aIndex, CompressedGlyph& aGlyph)
+ {
+ MOZ_ASSERT(GetCharacterGlyphs() + aIndex == &aGlyph);
+ if (aGlyph.IsSimpleGlyph()) {
+ DetailedGlyph details = {
+ aGlyph.GetSimpleGlyph(),
+ (int32_t) aGlyph.GetSimpleAdvance(),
+ 0, 0
+ };
+ SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1),
+ &details);
+ }
+ }
+
+ // For characters whose glyph data does not fit the "simple" glyph criteria
+ // in CompressedGlyph, we use a sorted array to store the association
+ // between the source character offset and an index into an array
+ // DetailedGlyphs. The CompressedGlyph record includes a count of
+ // the number of DetailedGlyph records that belong to the character,
+ // starting at the given index.
+ class DetailedGlyphStore {
+ public:
+ DetailedGlyphStore()
+ : mLastUsed(0)
+ { }
+
+ // This is optimized for the most common calling patterns:
+ // we rarely need random access to the records, access is most commonly
+ // sequential through the textRun, so we record the last-used index
+ // and check whether the caller wants the same record again, or the
+ // next; if not, it's most likely we're starting over from the start
+ // of the run, so we check the first entry before resorting to binary
+ // search as a last resort.
+ // NOTE that this must not be called for a character offset that does
+ // not have any DetailedGlyph records; callers must have verified that
+ // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero
+ // before calling this, otherwise the assertions here will fire (in a
+ // debug build), and we'll probably crash.
+ DetailedGlyph* Get(uint32_t aOffset) {
+ NS_ASSERTION(mOffsetToIndex.Length() > 0,
+ "no detailed glyph records!");
+ DetailedGlyph* details = mDetails.Elements();
+ // check common cases (fwd iteration, initial entry, etc) first
+ if (mLastUsed < mOffsetToIndex.Length() - 1 &&
+ aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) {
+ ++mLastUsed;
+ } else if (aOffset == mOffsetToIndex[0].mOffset) {
+ mLastUsed = 0;
+ } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) {
+ // do nothing
+ } else if (mLastUsed > 0 &&
+ aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) {
+ --mLastUsed;
+ } else {
+ mLastUsed =
+ mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset());
+ }
+ NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex,
+ "detailed glyph record missing!");
+ return details + mOffsetToIndex[mLastUsed].mIndex;
+ }
+
+ DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) {
+ uint32_t detailIndex = mDetails.Length();
+ DetailedGlyph *details = mDetails.AppendElements(aCount);
+ // We normally set up glyph records sequentially, so the common case
+ // here is to append new records to the mOffsetToIndex array;
+ // test for that before falling back to the InsertElementSorted
+ // method.
+ if (mOffsetToIndex.Length() == 0 ||
+ aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
+ mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex));
+ } else {
+ mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
+ CompareRecordOffsets());
+ }
+ return details;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
+ return aMallocSizeOf(this) +
+ mDetails.ShallowSizeOfExcludingThis(aMallocSizeOf) +
+ mOffsetToIndex.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ private:
+ struct DGRec {
+ DGRec(const uint32_t& aOffset, const uint32_t& aIndex)
+ : mOffset(aOffset), mIndex(aIndex) { }
+ uint32_t mOffset; // source character offset in the textrun
+ uint32_t mIndex; // index where this char's DetailedGlyphs begin
+ };
+
+ struct CompareToOffset {
+ bool Equals(const DGRec& a, const uint32_t& b) const {
+ return a.mOffset == b;
+ }
+ bool LessThan(const DGRec& a, const uint32_t& b) const {
+ return a.mOffset < b;
+ }
+ };
+
+ struct CompareRecordOffsets {
+ bool Equals(const DGRec& a, const DGRec& b) const {
+ return a.mOffset == b.mOffset;
+ }
+ bool LessThan(const DGRec& a, const DGRec& b) const {
+ return a.mOffset < b.mOffset;
+ }
+ };
+
+ // Concatenated array of all the DetailedGlyph records needed for the
+ // textRun; individual character offsets are associated with indexes
+ // into this array via the mOffsetToIndex table.
+ nsTArray<DetailedGlyph> mDetails;
+
+ // For each character offset that needs DetailedGlyphs, we record the
+ // index in mDetails where the list of glyphs begins. This array is
+ // sorted by mOffset.
+ nsTArray<DGRec> mOffsetToIndex;
+
+ // Records the most recently used index into mOffsetToIndex, so that
+ // we can support sequential access more quickly than just doing
+ // a binary search each time.
+ nsTArray<DGRec>::index_type mLastUsed;
+ };
+
+ mozilla::UniquePtr<DetailedGlyphStore> mDetailedGlyphs;
+
+ // Number of char16_t characters and CompressedGlyph glyph records
+ uint32_t mLength;
+
+ // Shaping flags (direction, ligature-suppression)
+ uint32_t mFlags;
+
+ int32_t mAppUnitsPerDevUnit;
+};
+
+/*
+ * gfxShapedWord: an individual (space-delimited) run of text shaped with a
+ * particular font, without regard to external context.
+ *
+ * The glyph data is copied into gfxTextRuns as needed from the cache of
+ * ShapedWords associated with each gfxFont instance.
+ */
+class gfxShapedWord final : public gfxShapedText
+{
+public:
+ typedef mozilla::unicode::Script Script;
+
+ // Create a ShapedWord that can hold glyphs for aLength characters,
+ // with mCharacterGlyphs sized appropriately.
+ //
+ // Returns null on allocation failure (does NOT use infallible alloc)
+ // so caller must check for success.
+ //
+ // This does NOT perform shaping, so the returned word contains no
+ // glyph data; the caller must call gfxFont::ShapeText() with appropriate
+ // parameters to set up the glyphs.
+ static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
+ Script aRunScript,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags) {
+ NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
+ "excessive length for gfxShapedWord!");
+
+ // Compute size needed including the mCharacterGlyphs array
+ // and a copy of the original text
+ uint32_t size =
+ offsetof(gfxShapedWord, mCharGlyphsStorage) +
+ aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
+ void *storage = malloc(size);
+ if (!storage) {
+ return nullptr;
+ }
+
+ // Construct in the pre-allocated storage, using placement new
+ return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+ aAppUnitsPerDevUnit, aFlags);
+ }
+
+ static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength,
+ Script aRunScript,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags) {
+ NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
+ "excessive length for gfxShapedWord!");
+
+ // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
+ // then we convert the text to an 8-bit version and call the 8-bit
+ // Create function instead.
+ if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+ nsAutoCString narrowText;
+ LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
+ narrowText);
+ return Create((const uint8_t*)(narrowText.BeginReading()),
+ aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
+ }
+
+ uint32_t size =
+ offsetof(gfxShapedWord, mCharGlyphsStorage) +
+ aLength * (sizeof(CompressedGlyph) + sizeof(char16_t));
+ void *storage = malloc(size);
+ if (!storage) {
+ return nullptr;
+ }
+
+ return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+ aAppUnitsPerDevUnit, aFlags);
+ }
+
+ // Override operator delete to properly free the object that was
+ // allocated via malloc.
+ void operator delete(void* p) {
+ free(p);
+ }
+
+ virtual const CompressedGlyph *GetCharacterGlyphs() const override {
+ return &mCharGlyphsStorage[0];
+ }
+ virtual CompressedGlyph *GetCharacterGlyphs() override {
+ return &mCharGlyphsStorage[0];
+ }
+
+ const uint8_t* Text8Bit() const {
+ NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
+ return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength());
+ }
+
+ const char16_t* TextUnicode() const {
+ NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
+ return reinterpret_cast<const char16_t*>(mCharGlyphsStorage + GetLength());
+ }
+
+ char16_t GetCharAt(uint32_t aOffset) const {
+ NS_ASSERTION(aOffset < GetLength(), "aOffset out of range");
+ return TextIs8Bit() ?
+ char16_t(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
+ }
+
+ Script GetScript() const {
+ return mScript;
+ }
+
+ void ResetAge() {
+ mAgeCounter = 0;
+ }
+ uint32_t IncrementAge() {
+ return ++mAgeCounter;
+ }
+
+ // Helper used when hashing a word for the shaped-word caches
+ static uint32_t HashMix(uint32_t aHash, char16_t aCh)
+ {
+ return (aHash >> 28) ^ (aHash << 4) ^ aCh;
+ }
+
+private:
+ // so that gfxTextRun can share our DetailedGlyphStore class
+ friend class gfxTextRun;
+
+ // Construct storage for a ShapedWord, ready to receive glyph data
+ gfxShapedWord(const uint8_t *aText, uint32_t aLength,
+ Script aRunScript,
+ int32_t aAppUnitsPerDevUnit, uint32_t aFlags)
+ : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
+ aAppUnitsPerDevUnit)
+ , mScript(aRunScript)
+ , mAgeCounter(0)
+ {
+ memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+ uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
+ memcpy(text, aText, aLength * sizeof(uint8_t));
+ }
+
+ gfxShapedWord(const char16_t *aText, uint32_t aLength,
+ Script aRunScript,
+ int32_t aAppUnitsPerDevUnit, uint32_t aFlags)
+ : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
+ , mScript(aRunScript)
+ , mAgeCounter(0)
+ {
+ memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+ char16_t *text = reinterpret_cast<char16_t*>(&mCharGlyphsStorage[aLength]);
+ memcpy(text, aText, aLength * sizeof(char16_t));
+ SetupClusterBoundaries(0, aText, aLength);
+ }
+
+ Script mScript;
+
+ uint32_t mAgeCounter;
+
+ // The mCharGlyphsStorage array is actually a variable-size member;
+ // when the ShapedWord is created, its size will be increased as necessary
+ // to allow the proper number of glyphs to be stored.
+ // The original text, in either 8-bit or 16-bit form, will be stored
+ // immediately following the CompressedGlyphs.
+ CompressedGlyph mCharGlyphsStorage[1];
+};
+
+class GlyphBufferAzure;
+struct TextRunDrawParams;
+struct FontDrawParams;
+struct EmphasisMarkDrawParams;
+
+class gfxFont {
+
+ friend class gfxHarfBuzzShaper;
+ friend class gfxGraphiteShaper;
+
+protected:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::unicode::Script Script;
+ typedef mozilla::SVGContextPaint SVGContextPaint;
+
+public:
+ nsrefcnt AddRef(void) {
+ NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
+ if (mExpirationState.IsTracked()) {
+ gfxFontCache::GetCache()->RemoveObject(this);
+ }
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this));
+ return mRefCnt;
+ }
+ nsrefcnt Release(void) {
+ NS_PRECONDITION(0 != mRefCnt, "dup release");
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "gfxFont");
+ if (mRefCnt == 0) {
+ NotifyReleased();
+ // |this| may have been deleted.
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ int32_t GetRefCount() { return mRefCnt; }
+
+ // options to specify the kind of AA to be used when creating a font
+ typedef enum {
+ kAntialiasDefault,
+ kAntialiasNone,
+ kAntialiasGrayscale,
+ kAntialiasSubpixel
+ } AntialiasOption;
+
+protected:
+ nsAutoRefCnt mRefCnt;
+ cairo_scaled_font_t *mScaledFont;
+
+ void NotifyReleased() {
+ gfxFontCache *cache = gfxFontCache::GetCache();
+ if (cache) {
+ // Don't delete just yet; return the object to the cache for
+ // possibly recycling within some time limit
+ cache->NotifyReleased(this);
+ } else {
+ // The cache may have already been shut down.
+ delete this;
+ }
+ }
+
+ gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+ AntialiasOption anAAOption = kAntialiasDefault,
+ cairo_scaled_font_t *aScaledFont = nullptr);
+
+public:
+ virtual ~gfxFont();
+
+ bool Valid() const {
+ return mIsValid;
+ }
+
+ // options for the kind of bounding box to return from measurement
+ typedef enum {
+ LOOSE_INK_EXTENTS,
+ // A box that encloses all the painted pixels, and may
+ // include sidebearings and/or additional ascent/descent
+ // within the glyph cell even if the ink is smaller.
+ TIGHT_INK_EXTENTS,
+ // A box that tightly encloses all the painted pixels
+ // (although actually on Windows, at least, it may be
+ // slightly larger than strictly necessary because
+ // we can't get precise extents with ClearType).
+ TIGHT_HINTED_OUTLINE_EXTENTS
+ // A box that tightly encloses the glyph outline,
+ // ignoring possible antialiasing pixels that extend
+ // beyond this.
+ // NOTE: The default implementation of gfxFont::Measure(),
+ // which works with the glyph extents cache, does not
+ // differentiate between this and TIGHT_INK_EXTENTS.
+ // Whether the distinction is important depends on the
+ // antialiasing behavior of the platform; currently the
+ // distinction is only implemented in the gfxWindowsFont
+ // subclass, because of ClearType's tendency to paint
+ // outside the hinted outline.
+ // Also NOTE: it is relatively expensive to request this,
+ // as it does not use cached glyph extents in the font.
+ } BoundingBoxType;
+
+ const nsString& GetName() const { return mFontEntry->Name(); }
+ const gfxFontStyle *GetStyle() const { return &mStyle; }
+
+ virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
+
+ virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) {
+ // platforms where this actually matters should override
+ return nullptr;
+ }
+
+ gfxFloat GetAdjustedSize() const {
+ return mAdjustedSize > 0.0
+ ? mAdjustedSize
+ : (mStyle.sizeAdjust == 0.0 ? 0.0 : mStyle.size);
+ }
+
+ float FUnitsToDevUnitsFactor() const {
+ // check this was set up during font initialization
+ NS_ASSERTION(mFUnitsConvFactor >= 0.0f, "mFUnitsConvFactor not valid");
+ return mFUnitsConvFactor;
+ }
+
+ // check whether this is an sfnt we can potentially use with harfbuzz
+ bool FontCanSupportHarfBuzz() {
+ return mFontEntry->HasCmapTable();
+ }
+
+ // check whether this is an sfnt we can potentially use with Graphite
+ bool FontCanSupportGraphite() {
+ return mFontEntry->HasGraphiteTables();
+ }
+
+ // Whether this is a font that may be doing full-color rendering,
+ // and therefore needs us to use a mask for text-shadow even when
+ // we're not actually blurring.
+ bool AlwaysNeedsMaskForShadow() {
+ return mFontEntry->TryGetColorGlyphs() ||
+ mFontEntry->TryGetSVGData(this) ||
+ mFontEntry->HasFontTable(TRUETYPE_TAG('C','B','D','T')) ||
+ mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
+ }
+
+ // whether a feature is supported by the font (limited to a small set
+ // of features for which some form of fallback needs to be implemented)
+ bool SupportsFeature(Script aScript, uint32_t aFeatureTag);
+
+ // whether the font supports "real" small caps, petite caps etc.
+ // aFallbackToSmallCaps true when petite caps should fallback to small caps
+ bool SupportsVariantCaps(Script aScript, uint32_t aVariantCaps,
+ bool& aFallbackToSmallCaps,
+ bool& aSyntheticLowerToSmallCaps,
+ bool& aSyntheticUpperToSmallCaps);
+
+ // whether the font supports subscript/superscript feature
+ // for fallback, need to verify that all characters in the run
+ // have variant substitutions
+ bool SupportsSubSuperscript(uint32_t aSubSuperscript,
+ const uint8_t *aString,
+ uint32_t aLength,
+ Script aRunScript);
+
+ bool SupportsSubSuperscript(uint32_t aSubSuperscript,
+ const char16_t *aString,
+ uint32_t aLength,
+ Script aRunScript);
+
+ // Subclasses may choose to look up glyph ids for characters.
+ // If they do not override this, gfxHarfBuzzShaper will fetch the cmap
+ // table and use that.
+ virtual bool ProvidesGetGlyph() const {
+ return false;
+ }
+ // Map unicode character to glyph ID.
+ // Only used if ProvidesGetGlyph() returns true.
+ virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) {
+ return 0;
+ }
+ // Return the horizontal advance of a glyph.
+ gfxFloat GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID);
+
+ // Return Azure GlyphRenderingOptions for drawing this font.
+ virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+ GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr)
+ { return nullptr; }
+
+ gfxFloat SynthesizeSpaceWidth(uint32_t aCh);
+
+ // Font metrics
+ struct Metrics {
+ gfxFloat capHeight;
+ gfxFloat xHeight;
+ gfxFloat strikeoutSize;
+ gfxFloat strikeoutOffset;
+ gfxFloat underlineSize;
+ gfxFloat underlineOffset;
+
+ gfxFloat internalLeading;
+ gfxFloat externalLeading;
+
+ gfxFloat emHeight;
+ gfxFloat emAscent;
+ gfxFloat emDescent;
+ gfxFloat maxHeight;
+ gfxFloat maxAscent;
+ gfxFloat maxDescent;
+ gfxFloat maxAdvance;
+
+ gfxFloat aveCharWidth;
+ gfxFloat spaceWidth;
+ gfxFloat zeroOrAveCharWidth; // width of '0', or if there is
+ // no '0' glyph in this font,
+ // equal to .aveCharWidth
+ };
+
+ enum Orientation {
+ eHorizontal,
+ eVertical
+ };
+
+ const Metrics& GetMetrics(Orientation aOrientation)
+ {
+ if (aOrientation == eHorizontal) {
+ return GetHorizontalMetrics();
+ }
+ if (!mVerticalMetrics) {
+ mVerticalMetrics.reset(CreateVerticalMetrics());
+ }
+ return *mVerticalMetrics;
+ }
+
+ /**
+ * We let layout specify spacing on either side of any
+ * character. We need to specify both before and after
+ * spacing so that substring measurement can do the right things.
+ * These values are in appunits. They're always an integral number of
+ * appunits, but we specify them in floats in case very large spacing
+ * values are required.
+ */
+ struct Spacing {
+ gfxFloat mBefore;
+ gfxFloat mAfter;
+ };
+ /**
+ * Metrics for a particular string
+ */
+ struct RunMetrics {
+ RunMetrics() {
+ mAdvanceWidth = mAscent = mDescent = 0.0;
+ }
+
+ void CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft);
+
+ // can be negative (partly due to negative spacing).
+ // Advance widths should be additive: the advance width of the
+ // (offset1, length1) plus the advance width of (offset1 + length1,
+ // length2) should be the advance width of (offset1, length1 + length2)
+ gfxFloat mAdvanceWidth;
+
+ // For zero-width substrings, these must be zero!
+ gfxFloat mAscent; // always non-negative
+ gfxFloat mDescent; // always non-negative
+
+ // Bounding box that is guaranteed to include everything drawn.
+ // If a tight boundingBox was requested when these metrics were
+ // generated, this will tightly wrap the glyphs, otherwise it is
+ // "loose" and may be larger than the true bounding box.
+ // Coordinates are relative to the baseline left origin, so typically
+ // mBoundingBox.y == -mAscent
+ gfxRect mBoundingBox;
+ };
+
+ /**
+ * Draw a series of glyphs to aContext. The direction of aTextRun must
+ * be honoured.
+ * @param aStart the first character to draw
+ * @param aEnd draw characters up to here
+ * @param aPt the baseline origin; the left end of the baseline
+ * for LTR textruns, the right end for RTL textruns.
+ * On return, this will be updated to the other end of the baseline.
+ * In application units, really!
+ * @param aRunParams record with drawing parameters, see TextRunDrawParams.
+ * Particular fields of interest include
+ * .spacing spacing to insert before and after characters (for RTL
+ * glyphs, before-spacing is inserted to the right of characters). There
+ * are aEnd - aStart elements in this array, unless it's null to indicate
+ * that there is no spacing.
+ * .drawMode specifies whether the fill or stroke of the glyph should be
+ * drawn, or if it should be drawn into the current path
+ * .contextPaint information about how to construct the fill and
+ * stroke pattern. Can be nullptr if we are not stroking the text, which
+ * indicates that the current source from context should be used for fill
+ * .context the Thebes graphics context to which we're drawing
+ * .dt Moz2D DrawTarget to which we're drawing
+ *
+ * Callers guarantee:
+ * -- aStart and aEnd are aligned to cluster and ligature boundaries
+ * -- all glyphs use this font
+ */
+ void Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
+ gfxPoint *aPt, const TextRunDrawParams& aRunParams,
+ uint16_t aOrientation);
+
+ /**
+ * Draw the emphasis marks for the given text run. Its prerequisite
+ * and output are similiar to the method Draw().
+ * @param aPt the baseline origin of the emphasis marks.
+ * @param aParams some drawing parameters, see EmphasisMarkDrawParams.
+ */
+ void DrawEmphasisMarks(const gfxTextRun* aShapedText, gfxPoint* aPt,
+ uint32_t aOffset, uint32_t aCount,
+ const EmphasisMarkDrawParams& aParams);
+
+ /**
+ * Measure a run of characters. See gfxTextRun::Metrics.
+ * @param aTight if false, then return the union of the glyph extents
+ * with the font-box for the characters (the rectangle with x=0,width=
+ * the advance width for the character run,y=-(font ascent), and height=
+ * font ascent + font descent). Otherwise, we must return as tight as possible
+ * an approximation to the area actually painted by glyphs.
+ * @param aDrawTargetForTightBoundingBox when aTight is true, this must
+ * be non-null.
+ * @param aSpacing spacing to insert before and after glyphs. The bounding box
+ * need not include the spacing itself, but the spacing affects the glyph
+ * positions. null if there is no spacing.
+ *
+ * Callers guarantee:
+ * -- aStart and aEnd are aligned to cluster and ligature boundaries
+ * -- all glyphs use this font
+ *
+ * The default implementation just uses font metrics and aTextRun's
+ * advances, and assumes no characters fall outside the font box. In
+ * general this is insufficient, because that assumption is not always true.
+ */
+ virtual RunMetrics Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ Spacing *aSpacing, uint16_t aOrientation);
+ /**
+ * Line breaks have been changed at the beginning and/or end of a substring
+ * of the text. Reshaping may be required; glyph updating is permitted.
+ * @return true if anything was changed, false otherwise
+ */
+ bool NotifyLineBreaksChanged(gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aLength)
+ { return false; }
+
+ // Expiration tracking
+ nsExpirationState *GetExpirationState() { return &mExpirationState; }
+
+ // Get the glyphID of a space
+ virtual uint32_t GetSpaceGlyph() = 0;
+
+ gfxGlyphExtents *GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit);
+
+ // You need to call SetupCairoFont on aDrawTarget just before calling this.
+ virtual void SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
+ bool aNeedTight, gfxGlyphExtents *aExtents);
+
+ // This is called by the default Draw() implementation above.
+ virtual bool SetupCairoFont(DrawTarget* aDrawTarget) = 0;
+
+ virtual bool AllowSubpixelAA() { return true; }
+
+ bool IsSyntheticBold() { return mApplySyntheticBold; }
+
+ // Amount by which synthetic bold "fattens" the glyphs:
+ // For size S up to a threshold size T, we use (0.25 + 3S / 4T),
+ // so that the result ranges from 0.25 to 1.0; thereafter,
+ // simply use (S / T).
+ gfxFloat GetSyntheticBoldOffset() {
+ gfxFloat size = GetAdjustedSize();
+ const gfxFloat threshold = 48.0;
+ return size < threshold ? (0.25 + 0.75 * size / threshold) :
+ (size / threshold);
+ }
+
+ gfxFontEntry *GetFontEntry() const { return mFontEntry.get(); }
+ bool HasCharacter(uint32_t ch) {
+ if (!mIsValid ||
+ (mUnicodeRangeMap && !mUnicodeRangeMap->test(ch))) {
+ return false;
+ }
+ return mFontEntry->HasCharacter(ch);
+ }
+
+ const gfxCharacterMap* GetUnicodeRangeMap() const {
+ return mUnicodeRangeMap.get();
+ }
+
+ void SetUnicodeRangeMap(gfxCharacterMap* aUnicodeRangeMap) {
+ mUnicodeRangeMap = aUnicodeRangeMap;
+ }
+
+ uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) {
+ if (!mIsValid) {
+ return 0;
+ }
+ return mFontEntry->GetUVSGlyph(aCh, aVS);
+ }
+
+ template<typename T>
+ bool InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ uint8_t aMatchType,
+ uint16_t aOrientation,
+ Script aScript,
+ bool aSyntheticLower,
+ bool aSyntheticUpper);
+
+ // call the (virtual) InitTextRun method to do glyph generation/shaping,
+ // limiting the length of text passed by processing the run in multiple
+ // segments if necessary
+ template<typename T>
+ bool SplitAndInitTextRun(DrawTarget *aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString,
+ uint32_t aRunStart,
+ uint32_t aRunLength,
+ Script aRunScript,
+ bool aVertical);
+
+ // Get a ShapedWord representing the given text (either 8- or 16-bit)
+ // for use in setting up a gfxTextRun.
+ template<typename T>
+ gfxShapedWord* GetShapedWord(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aLength,
+ uint32_t aHash,
+ Script aRunScript,
+ bool aVertical,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags,
+ gfxTextPerfMetrics *aTextPerf);
+
+ // Ensure the ShapedWord cache is initialized. This MUST be called before
+ // any attempt to use GetShapedWord().
+ void InitWordCache() {
+ if (!mWordCache) {
+ mWordCache = mozilla::MakeUnique<nsTHashtable<CacheHashEntry>>();
+ }
+ }
+
+ // Called by the gfxFontCache timer to increment the age of all the words,
+ // so that they'll expire after a sufficient period of non-use
+ void AgeCachedWords();
+
+ // Discard all cached word records; called on memory-pressure notification.
+ void ClearCachedWords() {
+ if (mWordCache) {
+ mWordCache->Clear();
+ }
+ }
+
+ // Glyph rendering/geometry has changed, so invalidate data as necessary.
+ void NotifyGlyphsChanged();
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+
+ typedef enum {
+ FONT_TYPE_DWRITE,
+ FONT_TYPE_GDI,
+ FONT_TYPE_FT2,
+ FONT_TYPE_MAC,
+ FONT_TYPE_OS2,
+ FONT_TYPE_CAIRO,
+ FONT_TYPE_FONTCONFIG
+ } FontType;
+
+ virtual FontType GetType() const = 0;
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget)
+ { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
+
+ bool KerningDisabled() {
+ return mKerningSet && !mKerningEnabled;
+ }
+
+ /**
+ * Subclass this object to be notified of glyph changes. Delete the object
+ * when no longer needed.
+ */
+ class GlyphChangeObserver {
+ public:
+ virtual ~GlyphChangeObserver()
+ {
+ if (mFont) {
+ mFont->RemoveGlyphChangeObserver(this);
+ }
+ }
+ // This gets called when the gfxFont dies.
+ void ForgetFont() { mFont = nullptr; }
+ virtual void NotifyGlyphsChanged() = 0;
+ protected:
+ explicit GlyphChangeObserver(gfxFont *aFont) : mFont(aFont)
+ {
+ mFont->AddGlyphChangeObserver(this);
+ }
+ // This pointer is nulled by ForgetFont in the gfxFont's
+ // destructor. Before the gfxFont dies.
+ gfxFont* MOZ_NON_OWNING_REF mFont;
+ };
+ friend class GlyphChangeObserver;
+
+ bool GlyphsMayChange()
+ {
+ // Currently only fonts with SVG glyphs can have animated glyphs
+ return mFontEntry->TryGetSVGData(this);
+ }
+
+ static void DestroySingletons() {
+ delete sScriptTagToCode;
+ delete sDefaultFeatures;
+ }
+
+ // Call TryGetMathTable() to try and load the Open Type MATH table.
+ // If (and ONLY if) TryGetMathTable() has returned true, the MathTable()
+ // method may be called to access the gfxMathTable data.
+ bool TryGetMathTable();
+ gfxMathTable* MathTable() {
+ MOZ_RELEASE_ASSERT(mMathTable, "A successful call to TryGetMathTable() must be performed before calling this function");
+ return mMathTable.get();
+ }
+
+ // return a cloned font resized and offset to simulate sub/superscript glyphs
+ virtual already_AddRefed<gfxFont>
+ GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
+
+ /**
+ * Return the reference cairo_t object from aDT.
+ */
+ static cairo_t* RefCairo(mozilla::gfx::DrawTarget* aDT);
+
+protected:
+ virtual const Metrics& GetHorizontalMetrics() = 0;
+
+ const Metrics* CreateVerticalMetrics();
+
+ // Output a single glyph at *aPt, which is updated by the glyph's advance.
+ // Normal glyphs are simply accumulated in aBuffer until it is full and
+ // gets flushed, but SVG or color-font glyphs will instead be rendered
+ // directly to the destination (found from the buffer's parameters).
+ void DrawOneGlyph(uint32_t aGlyphID,
+ double aAdvance,
+ gfxPoint *aPt,
+ GlyphBufferAzure& aBuffer,
+ bool *aEmittedGlyphs) const;
+
+ // Output a run of glyphs at *aPt, which is updated to follow the last glyph
+ // in the run. This method also takes account of any letter-spacing provided
+ // in aRunParams.
+ bool DrawGlyphs(const gfxShapedText *aShapedText,
+ uint32_t aOffset, // offset in the textrun
+ uint32_t aCount, // length of run to draw
+ gfxPoint *aPt,
+ const TextRunDrawParams& aRunParams,
+ const FontDrawParams& aFontParams);
+
+ // set the font size and offset used for
+ // synthetic subscript/superscript glyphs
+ void CalculateSubSuperSizeAndOffset(int32_t aAppUnitsPerDevPixel,
+ gfxFloat& aSubSuperSizeRatio,
+ float& aBaselineOffset);
+
+ // Return a font that is a "clone" of this one, but reduced to 80% size
+ // (and with variantCaps set to normal).
+ // Default implementation relies on gfxFontEntry::CreateFontInstance;
+ // backends that don't implement that will need to override this and use
+ // an alternative technique. (gfxFontconfigFonts, I'm looking at you...)
+ virtual already_AddRefed<gfxFont> GetSmallCapsFont();
+
+ // subclasses may provide (possibly hinted) glyph widths (in font units);
+ // if they do not override this, harfbuzz will use unhinted widths
+ // derived from the font tables
+ virtual bool ProvidesGlyphWidths() const {
+ return false;
+ }
+
+ // The return value is interpreted as a horizontal advance in 16.16 fixed
+ // point format.
+ virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID) {
+ return -1;
+ }
+
+ bool IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
+ const gfxTextRun* aTextRun);
+
+ void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
+ void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
+
+ // whether font contains substitution lookups containing spaces
+ bool HasSubstitutionRulesWithSpaceLookups(Script aRunScript);
+
+ // do spaces participate in shaping rules? if so, can't used word cache
+ bool SpaceMayParticipateInShaping(Script aRunScript);
+
+ // For 8-bit text, expand to 16-bit and then call the following method.
+ bool ShapeText(DrawTarget *aContext,
+ const uint8_t *aText,
+ uint32_t aOffset, // dest offset in gfxShapedText
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText); // where to store the result
+
+ // Call the appropriate shaper to generate glyphs for aText and store
+ // them into aShapedText.
+ virtual bool ShapeText(DrawTarget *aContext,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText);
+
+ // Helper to adjust for synthetic bold and set character-type flags
+ // in the shaped text; implementations of ShapeText should call this
+ // after glyph shaping has been completed.
+ void PostShapingFixup(DrawTarget* aContext,
+ const char16_t* aText,
+ uint32_t aOffset, // position within aShapedText
+ uint32_t aLength,
+ bool aVertical,
+ gfxShapedText* aShapedText);
+
+ // Shape text directly into a range within a textrun, without using the
+ // font's word cache. Intended for use when the font has layout features
+ // that involve space, and therefore require shaping complete runs rather
+ // than isolated words, or for long strings that are inefficient to cache.
+ // This will split the text on "invalid" characters (tab/newline) that are
+ // not handled via normal shaping, but does not otherwise divide up the
+ // text.
+ template<typename T>
+ bool ShapeTextWithoutWordCache(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxTextRun *aTextRun);
+
+ // Shape a fragment of text (a run that is known to contain only
+ // "valid" characters, no newlines/tabs/other control chars).
+ // All non-wordcache shaping goes through here; this is the function
+ // that will ensure we don't pass excessively long runs to the various
+ // platform shapers.
+ template<typename T>
+ bool ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget,
+ const T *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxTextRun *aTextRun);
+
+ void CheckForFeaturesInvolvingSpace();
+
+ // whether a given feature is included in feature settings from both the
+ // font and the style. aFeatureOn set if resolved feature value is non-zero
+ bool HasFeatureSet(uint32_t aFeature, bool& aFeatureOn);
+
+ // used when analyzing whether a font has space contextual lookups
+ static nsDataHashtable<nsUint32HashKey,Script> *sScriptTagToCode;
+ static nsTHashtable<nsUint32HashKey> *sDefaultFeatures;
+
+ RefPtr<gfxFontEntry> mFontEntry;
+
+ struct CacheHashKey {
+ union {
+ const uint8_t *mSingle;
+ const char16_t *mDouble;
+ } mText;
+ uint32_t mLength;
+ uint32_t mFlags;
+ Script mScript;
+ int32_t mAppUnitsPerDevUnit;
+ PLDHashNumber mHashKey;
+ bool mTextIs8Bit;
+
+ CacheHashKey(const uint8_t *aText, uint32_t aLength,
+ uint32_t aStringHash,
+ Script aScriptCode, int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags)
+ : mLength(aLength),
+ mFlags(aFlags),
+ mScript(aScriptCode),
+ mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
+ mHashKey(aStringHash + static_cast<int32_t>(aScriptCode) +
+ aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000),
+ mTextIs8Bit(true)
+ {
+ NS_ASSERTION(aFlags & gfxTextRunFactory::TEXT_IS_8BIT,
+ "8-bit flag should have been set");
+ mText.mSingle = aText;
+ }
+
+ CacheHashKey(const char16_t *aText, uint32_t aLength,
+ uint32_t aStringHash,
+ Script aScriptCode, int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags)
+ : mLength(aLength),
+ mFlags(aFlags),
+ mScript(aScriptCode),
+ mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
+ mHashKey(aStringHash + static_cast<int32_t>(aScriptCode) +
+ aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000),
+ mTextIs8Bit(false)
+ {
+ // We can NOT assert that TEXT_IS_8BIT is false in aFlags here,
+ // because this might be an 8bit-only word from a 16-bit textrun,
+ // in which case the text we're passed is still in 16-bit form,
+ // and we'll have to use an 8-to-16bit comparison in KeyEquals.
+ mText.mDouble = aText;
+ }
+ };
+
+ class CacheHashEntry : public PLDHashEntryHdr {
+ public:
+ typedef const CacheHashKey &KeyType;
+ typedef const CacheHashKey *KeyTypePointer;
+
+ // When constructing a new entry in the hashtable, the caller of Put()
+ // will fill us in.
+ explicit CacheHashEntry(KeyTypePointer aKey) { }
+ CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); }
+ ~CacheHashEntry() { }
+
+ bool KeyEquals(const KeyTypePointer aKey) const;
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+ static PLDHashNumber HashKey(const KeyTypePointer aKey) {
+ return aKey->mHashKey;
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(mShapedWord.get());
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ mozilla::UniquePtr<gfxShapedWord> mShapedWord;
+ };
+
+ mozilla::UniquePtr<nsTHashtable<CacheHashEntry> > mWordCache;
+
+ static const uint32_t kShapedWordCacheMaxAge = 3;
+
+ bool mIsValid;
+
+ // use synthetic bolding for environments where this is not supported
+ // by the platform
+ bool mApplySyntheticBold;
+
+ bool mKerningSet; // kerning explicitly set?
+ bool mKerningEnabled; // if set, on or off?
+
+ bool mMathInitialized; // TryGetMathTable() called?
+
+ nsExpirationState mExpirationState;
+ gfxFontStyle mStyle;
+ nsTArray<mozilla::UniquePtr<gfxGlyphExtents>> mGlyphExtentsArray;
+ mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<GlyphChangeObserver>>>
+ mGlyphChangeObservers;
+
+ gfxFloat mAdjustedSize;
+
+ // Conversion factor from font units to dev units; note that this may be
+ // zero (in the degenerate case where mAdjustedSize has become zero).
+ // This is OK because we only multiply by this factor, never divide.
+ float mFUnitsConvFactor;
+
+ // the AA setting requested for this font - may affect glyph bounds
+ AntialiasOption mAntialiasOption;
+
+ // a copy of the font without antialiasing, if needed for separate
+ // measurement by mathml code
+ mozilla::UniquePtr<gfxFont> mNonAAFont;
+
+ // we create either or both of these shapers when needed, depending
+ // whether the font has graphite tables, and whether graphite shaping
+ // is actually enabled
+ mozilla::UniquePtr<gfxFontShaper> mHarfBuzzShaper;
+ mozilla::UniquePtr<gfxFontShaper> mGraphiteShaper;
+
+ // if a userfont with unicode-range specified, contains map of *possible*
+ // ranges supported by font
+ RefPtr<gfxCharacterMap> mUnicodeRangeMap;
+
+ RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
+
+ // For vertical metrics, created on demand.
+ mozilla::UniquePtr<const Metrics> mVerticalMetrics;
+
+ // Table used for MathML layout.
+ mozilla::UniquePtr<gfxMathTable> mMathTable;
+
+ // Helper for subclasses that want to initialize standard metrics from the
+ // tables of sfnt (TrueType/OpenType) fonts.
+ // This will use mFUnitsConvFactor if it is already set, else compute it
+ // from mAdjustedSize and the unitsPerEm in the font's 'head' table.
+ // Returns TRUE and sets mIsValid=TRUE if successful;
+ // Returns TRUE but leaves mIsValid=FALSE if the font seems to be broken.
+ // Returns FALSE if the font does not appear to be an sfnt at all,
+ // and should be handled (if possible) using other APIs.
+ bool InitMetricsFromSfntTables(Metrics& aMetrics);
+
+ // Helper to calculate various derived metrics from the results of
+ // InitMetricsFromSfntTables or equivalent platform code
+ void CalculateDerivedMetrics(Metrics& aMetrics);
+
+ // some fonts have bad metrics, this method sanitize them.
+ // if this font has bad underline offset, aIsBadUnderlineFont should be true.
+ void SanitizeMetrics(Metrics *aMetrics, bool aIsBadUnderlineFont);
+
+ bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
+ uint32_t aGlyphId, SVGContextPaint* aContextPaint) const;
+ bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
+ uint32_t aGlyphId, SVGContextPaint* aContextPaint,
+ gfxTextRunDrawCallbacks *aCallbacks,
+ bool& aEmittedGlyphs) const;
+
+ bool RenderColorGlyph(DrawTarget* aDrawTarget,
+ gfxContext* aContext,
+ mozilla::gfx::ScaledFont* scaledFont,
+ mozilla::gfx::GlyphRenderingOptions* renderingOptions,
+ mozilla::gfx::DrawOptions drawOptions,
+ const mozilla::gfx::Point& aPoint,
+ uint32_t aGlyphId) const;
+
+ // Bug 674909. When synthetic bolding text by drawing twice, need to
+ // render using a pixel offset in device pixels, otherwise text
+ // doesn't appear bolded, it appears as if a bad text shadow exists
+ // when a non-identity transform exists. Use an offset factor so that
+ // the second draw occurs at a constant offset in device pixels.
+ // This helper calculates the scale factor we need to apply to the
+ // synthetic-bold offset.
+ static double CalcXScale(DrawTarget* aDrawTarget);
+};
+
+// proportion of ascent used for x-height, if unable to read value from font
+#define DEFAULT_XHEIGHT_FACTOR 0.56f
+
+// Parameters passed to gfxFont methods for drawing glyphs from a textrun.
+// The TextRunDrawParams are set up once per textrun; the FontDrawParams
+// are dependent on the specific font, so they are set per GlyphRun.
+
+struct TextRunDrawParams {
+ RefPtr<mozilla::gfx::DrawTarget> dt;
+ gfxContext *context;
+ gfxFont::Spacing *spacing;
+ gfxTextRunDrawCallbacks *callbacks;
+ mozilla::SVGContextPaint *runContextPaint;
+ mozilla::gfx::Color fontSmoothingBGColor;
+ gfxFloat direction;
+ double devPerApp;
+ nscolor textStrokeColor;
+ gfxPattern *textStrokePattern;
+ const mozilla::gfx::StrokeOptions *strokeOpts;
+ const mozilla::gfx::DrawOptions *drawOpts;
+ DrawMode drawMode;
+ bool isVerticalRun;
+ bool isRTL;
+ bool paintSVGGlyphs;
+};
+
+struct FontDrawParams {
+ RefPtr<mozilla::gfx::ScaledFont> scaledFont;
+ RefPtr<mozilla::gfx::GlyphRenderingOptions> renderingOptions;
+ mozilla::SVGContextPaint *contextPaint;
+ mozilla::gfx::Matrix *passedInvMatrix;
+ mozilla::gfx::Matrix matInv;
+ double synBoldOnePixelOffset;
+ int32_t extraStrikes;
+ mozilla::gfx::DrawOptions drawOptions;
+ bool isVerticalFont;
+ bool haveSVGGlyphs;
+ bool haveColorGlyphs;
+};
+
+struct EmphasisMarkDrawParams {
+ gfxContext* context;
+ gfxFont::Spacing* spacing;
+ gfxTextRun* mark;
+ gfxFloat advance;
+ gfxFloat direction;
+ bool isVertical;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxFontConstants.h b/system/graphics/thebes/gfxFontConstants.h
new file mode 100644
index 000000000..3eae49ee1
--- /dev/null
+++ b/system/graphics/thebes/gfxFontConstants.h
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* font constants shared by both thebes and layout */
+
+#ifndef GFX_FONT_CONSTANTS_H
+#define GFX_FONT_CONSTANTS_H
+
+/*
+ * This file is separate from gfxFont.h so that layout can include it
+ * without bringing in gfxFont.h and everything it includes.
+ */
+
+#define NS_FONT_STYLE_NORMAL 0
+#define NS_FONT_STYLE_ITALIC 1
+#define NS_FONT_STYLE_OBLIQUE 2
+
+#define NS_FONT_WEIGHT_NORMAL 400
+#define NS_FONT_WEIGHT_BOLD 700
+#define NS_FONT_WEIGHT_THIN 100
+
+#define NS_FONT_STRETCH_ULTRA_CONDENSED (-4)
+#define NS_FONT_STRETCH_EXTRA_CONDENSED (-3)
+#define NS_FONT_STRETCH_CONDENSED (-2)
+#define NS_FONT_STRETCH_SEMI_CONDENSED (-1)
+#define NS_FONT_STRETCH_NORMAL 0
+#define NS_FONT_STRETCH_SEMI_EXPANDED 1
+#define NS_FONT_STRETCH_EXPANDED 2
+#define NS_FONT_STRETCH_EXTRA_EXPANDED 3
+#define NS_FONT_STRETCH_ULTRA_EXPANDED 4
+
+#define NS_FONT_SMOOTHING_AUTO 0
+#define NS_FONT_SMOOTHING_GRAYSCALE 1
+
+#define NS_FONT_KERNING_AUTO 0
+#define NS_FONT_KERNING_NONE 1
+#define NS_FONT_KERNING_NORMAL 2
+
+#define NS_FONT_SYNTHESIS_WEIGHT 0x1
+#define NS_FONT_SYNTHESIS_STYLE 0x2
+
+#define NS_FONT_DISPLAY_AUTO 0
+#define NS_FONT_DISPLAY_BLOCK 1
+#define NS_FONT_DISPLAY_SWAP 2
+#define NS_FONT_DISPLAY_FALLBACK 3
+#define NS_FONT_DISPLAY_OPTIONAL 4
+
+enum {
+ eFeatureAlternates_historical,
+ eFeatureAlternates_stylistic,
+ eFeatureAlternates_styleset,
+ eFeatureAlternates_character_variant,
+ eFeatureAlternates_swash,
+ eFeatureAlternates_ornaments,
+ eFeatureAlternates_annotation,
+
+ eFeatureAlternates_numFeatures
+};
+
+// alternates - simple enumerated values
+#define NS_FONT_VARIANT_ALTERNATES_HISTORICAL (1 << eFeatureAlternates_historical)
+
+// alternates - values that use functional syntax
+#define NS_FONT_VARIANT_ALTERNATES_STYLISTIC (1 << eFeatureAlternates_stylistic)
+#define NS_FONT_VARIANT_ALTERNATES_STYLESET (1 << eFeatureAlternates_styleset)
+#define NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT (1 << eFeatureAlternates_character_variant)
+#define NS_FONT_VARIANT_ALTERNATES_SWASH (1 << eFeatureAlternates_swash)
+#define NS_FONT_VARIANT_ALTERNATES_ORNAMENTS (1 << eFeatureAlternates_ornaments)
+#define NS_FONT_VARIANT_ALTERNATES_ANNOTATION (1 << eFeatureAlternates_annotation)
+
+#define NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK \
+ NS_FONT_VARIANT_ALTERNATES_HISTORICAL
+
+#define NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK ( \
+ NS_FONT_VARIANT_ALTERNATES_STYLISTIC | \
+ NS_FONT_VARIANT_ALTERNATES_STYLESET | \
+ NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT | \
+ NS_FONT_VARIANT_ALTERNATES_SWASH | \
+ NS_FONT_VARIANT_ALTERNATES_ORNAMENTS | \
+ NS_FONT_VARIANT_ALTERNATES_ANNOTATION )
+
+#define NS_FONT_VARIANT_CAPS_NORMAL 0
+#define NS_FONT_VARIANT_CAPS_SMALLCAPS 1
+#define NS_FONT_VARIANT_CAPS_ALLSMALL 2
+#define NS_FONT_VARIANT_CAPS_PETITECAPS 3
+#define NS_FONT_VARIANT_CAPS_ALLPETITE 4
+#define NS_FONT_VARIANT_CAPS_TITLING 5
+#define NS_FONT_VARIANT_CAPS_UNICASE 6
+
+enum {
+ eFeatureEastAsian_jis78,
+ eFeatureEastAsian_jis83,
+ eFeatureEastAsian_jis90,
+ eFeatureEastAsian_jis04,
+ eFeatureEastAsian_simplified,
+ eFeatureEastAsian_traditional,
+ eFeatureEastAsian_full_width,
+ eFeatureEastAsian_prop_width,
+ eFeatureEastAsian_ruby,
+
+ eFeatureEastAsian_numFeatures
+};
+
+#define NS_FONT_VARIANT_EAST_ASIAN_JIS78 (1 << eFeatureEastAsian_jis78)
+#define NS_FONT_VARIANT_EAST_ASIAN_JIS83 (1 << eFeatureEastAsian_jis83)
+#define NS_FONT_VARIANT_EAST_ASIAN_JIS90 (1 << eFeatureEastAsian_jis90)
+#define NS_FONT_VARIANT_EAST_ASIAN_JIS04 (1 << eFeatureEastAsian_jis04)
+#define NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED (1 << eFeatureEastAsian_simplified)
+#define NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL (1 << eFeatureEastAsian_traditional)
+#define NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH (1 << eFeatureEastAsian_full_width)
+#define NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH (1 << eFeatureEastAsian_prop_width)
+#define NS_FONT_VARIANT_EAST_ASIAN_RUBY (1 << eFeatureEastAsian_ruby)
+
+#define NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK ( \
+ NS_FONT_VARIANT_EAST_ASIAN_JIS78 | \
+ NS_FONT_VARIANT_EAST_ASIAN_JIS83 | \
+ NS_FONT_VARIANT_EAST_ASIAN_JIS90 | \
+ NS_FONT_VARIANT_EAST_ASIAN_JIS04 | \
+ NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED | \
+ NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL )
+
+#define NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK ( \
+ NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH | \
+ NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH )
+
+enum {
+ eFeatureLigatures_none,
+ eFeatureLigatures_common,
+ eFeatureLigatures_no_common,
+ eFeatureLigatures_discretionary,
+ eFeatureLigatures_no_discretionary,
+ eFeatureLigatures_historical,
+ eFeatureLigatures_no_historical,
+ eFeatureLigatures_contextual,
+ eFeatureLigatures_no_contextual,
+
+ eFeatureLigatures_numFeatures
+};
+
+#define NS_FONT_VARIANT_LIGATURES_NONE (1 << eFeatureLigatures_none)
+#define NS_FONT_VARIANT_LIGATURES_COMMON (1 << eFeatureLigatures_common)
+#define NS_FONT_VARIANT_LIGATURES_NO_COMMON (1 << eFeatureLigatures_no_common)
+#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY (1 << eFeatureLigatures_discretionary)
+#define NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY (1 << eFeatureLigatures_no_discretionary)
+#define NS_FONT_VARIANT_LIGATURES_HISTORICAL (1 << eFeatureLigatures_historical)
+#define NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL (1 << eFeatureLigatures_no_historical)
+#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL (1 << eFeatureLigatures_contextual)
+#define NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL (1 << eFeatureLigatures_no_contextual)
+
+#define NS_FONT_VARIANT_LIGATURES_COMMON_MASK ( \
+ NS_FONT_VARIANT_LIGATURES_COMMON | \
+ NS_FONT_VARIANT_LIGATURES_NO_COMMON )
+
+#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK ( \
+ NS_FONT_VARIANT_LIGATURES_DISCRETIONARY | \
+ NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY )
+
+#define NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK ( \
+ NS_FONT_VARIANT_LIGATURES_HISTORICAL | \
+ NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL )
+
+#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK \
+ NS_FONT_VARIANT_LIGATURES_CONTEXTUAL | \
+ NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL
+
+enum {
+ eFeatureNumeric_lining,
+ eFeatureNumeric_oldstyle,
+ eFeatureNumeric_proportional,
+ eFeatureNumeric_tabular,
+ eFeatureNumeric_diagonal_fractions,
+ eFeatureNumeric_stacked_fractions,
+ eFeatureNumeric_slashedzero,
+ eFeatureNumeric_ordinal,
+
+ eFeatureNumeric_numFeatures
+};
+
+#define NS_FONT_VARIANT_NUMERIC_LINING (1 << eFeatureNumeric_lining)
+#define NS_FONT_VARIANT_NUMERIC_OLDSTYLE (1 << eFeatureNumeric_oldstyle)
+#define NS_FONT_VARIANT_NUMERIC_PROPORTIONAL (1 << eFeatureNumeric_proportional)
+#define NS_FONT_VARIANT_NUMERIC_TABULAR (1 << eFeatureNumeric_tabular)
+#define NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS (1 << eFeatureNumeric_diagonal_fractions)
+#define NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS (1 << eFeatureNumeric_stacked_fractions)
+#define NS_FONT_VARIANT_NUMERIC_SLASHZERO (1 << eFeatureNumeric_slashedzero)
+#define NS_FONT_VARIANT_NUMERIC_ORDINAL (1 << eFeatureNumeric_ordinal)
+
+#define NS_FONT_VARIANT_NUMERIC_FIGURE_MASK \
+ NS_FONT_VARIANT_NUMERIC_LINING | \
+ NS_FONT_VARIANT_NUMERIC_OLDSTYLE
+
+#define NS_FONT_VARIANT_NUMERIC_SPACING_MASK \
+ NS_FONT_VARIANT_NUMERIC_PROPORTIONAL | \
+ NS_FONT_VARIANT_NUMERIC_TABULAR
+
+#define NS_FONT_VARIANT_NUMERIC_FRACTION_MASK \
+ NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS | \
+ NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS
+
+#define NS_FONT_VARIANT_POSITION_NORMAL 0
+#define NS_FONT_VARIANT_POSITION_SUPER 1
+#define NS_FONT_VARIANT_POSITION_SUB 2
+
+#define NS_FONT_VARIANT_WIDTH_NORMAL 0
+#define NS_FONT_VARIANT_WIDTH_FULL 1
+#define NS_FONT_VARIANT_WIDTH_HALF 2
+#define NS_FONT_VARIANT_WIDTH_THIRD 3
+#define NS_FONT_VARIANT_WIDTH_QUARTER 4
+
+// based on fixed offset values used within WebKit
+#define NS_FONT_SUBSCRIPT_OFFSET_RATIO (0.20)
+#define NS_FONT_SUPERSCRIPT_OFFSET_RATIO (0.34)
+
+// this roughly corresponds to font-size: smaller behavior
+// at smaller sizes <20px the ratio is closer to 0.8 while at
+// larger sizes >45px the ratio is closer to 0.667 and in between
+// a blend of values is used
+#define NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL (0.82)
+#define NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE (0.667)
+#define NS_FONT_SUB_SUPER_SMALL_SIZE (20.0)
+#define NS_FONT_SUB_SUPER_LARGE_SIZE (45.0)
+
+// pref lang ids for font prefs
+enum eFontPrefLang {
+ #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_
+ #include "gfxFontPrefLangList.h"
+ #undef FONT_PREF_LANG
+
+ , eFontPrefLang_CJKSet // special code for CJK set
+ , eFontPrefLang_Emoji // special code for emoji presentation
+ , eFontPrefLang_First = eFontPrefLang_Western
+ , eFontPrefLang_Last = eFontPrefLang_Others
+ , eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1)
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxFontEntry.cpp b/system/graphics/thebes/gfxFontEntry.cpp
new file mode 100644
index 000000000..90c0bc858
--- /dev/null
+++ b/system/graphics/thebes/gfxFontEntry.cpp
@@ -0,0 +1,1831 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/MathAlgorithms.h"
+
+#include "mozilla/Logging.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsILanguageAtomService.h"
+
+#include "gfxFontEntry.h"
+#include "gfxTextRun.h"
+#include "gfxPlatform.h"
+#include "nsGkAtoms.h"
+
+#include "gfxTypes.h"
+#include "gfxContext.h"
+#include "gfxFontConstants.h"
+#include "gfxGraphiteShaper.h"
+#include "gfxHarfBuzzShaper.h"
+#include "gfxUserFontSet.h"
+#include "gfxPlatformFontList.h"
+#include "nsUnicodeProperties.h"
+#include "nsMathUtils.h"
+#include "nsBidiUtils.h"
+#include "nsUnicodeRange.h"
+#include "nsStyleConsts.h"
+#include "mozilla/AppUnits.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "gfxSVGGlyphs.h"
+#include "gfx2DGlue.h"
+
+#include "cairo.h"
+
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-ot.h"
+#include "graphite2/Font.h"
+
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+using mozilla::services::GetObserverService;
+
+void
+gfxCharacterMap::NotifyReleased()
+{
+ gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
+ if (mShared) {
+ fontlist->RemoveCmap(this);
+ }
+ delete this;
+}
+
+gfxFontEntry::gfxFontEntry() :
+ mStyle(NS_FONT_STYLE_NORMAL), mFixedPitch(false),
+ mIsValid(true),
+ mIsBadUnderlineFont(false),
+ mIsUserFontContainer(false),
+ mIsDataUserFont(false),
+ mIsLocalUserFont(false),
+ mStandardFace(false),
+ mSymbolFont(false),
+ mIgnoreGDEF(false),
+ mIgnoreGSUB(false),
+ mSVGInitialized(false),
+ mHasSpaceFeaturesInitialized(false),
+ mHasSpaceFeatures(false),
+ mHasSpaceFeaturesKerning(false),
+ mHasSpaceFeaturesNonKerning(false),
+ mSkipDefaultFeatureSpaceCheck(false),
+ mGraphiteSpaceContextualsInitialized(false),
+ mHasGraphiteSpaceContextuals(false),
+ mSpaceGlyphIsInvisible(false),
+ mSpaceGlyphIsInvisibleInitialized(false),
+ mCheckedForGraphiteTables(false),
+ mHasCmapTable(false),
+ mGrFaceInitialized(false),
+ mCheckedForColorGlyph(false),
+ mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+ mUVSOffset(0), mUVSData(nullptr),
+ mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
+ mCOLR(nullptr),
+ mCPAL(nullptr),
+ mUnitsPerEm(0),
+ mHBFace(nullptr),
+ mGrFace(nullptr),
+ mGrFaceRefCnt(0),
+ mComputedSizeOfUserFont(0)
+{
+ memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
+ memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
+}
+
+gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
+ mName(aName), mStyle(NS_FONT_STYLE_NORMAL), mFixedPitch(false),
+ mIsValid(true),
+ mIsBadUnderlineFont(false),
+ mIsUserFontContainer(false),
+ mIsDataUserFont(false),
+ mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
+ mSymbolFont(false),
+ mIgnoreGDEF(false),
+ mIgnoreGSUB(false),
+ mSVGInitialized(false),
+ mHasSpaceFeaturesInitialized(false),
+ mHasSpaceFeatures(false),
+ mHasSpaceFeaturesKerning(false),
+ mHasSpaceFeaturesNonKerning(false),
+ mSkipDefaultFeatureSpaceCheck(false),
+ mGraphiteSpaceContextualsInitialized(false),
+ mHasGraphiteSpaceContextuals(false),
+ mSpaceGlyphIsInvisible(false),
+ mSpaceGlyphIsInvisibleInitialized(false),
+ mCheckedForGraphiteTables(false),
+ mHasCmapTable(false),
+ mGrFaceInitialized(false),
+ mCheckedForColorGlyph(false),
+ mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+ mUVSOffset(0), mUVSData(nullptr),
+ mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
+ mCOLR(nullptr),
+ mCPAL(nullptr),
+ mUnitsPerEm(0),
+ mHBFace(nullptr),
+ mGrFace(nullptr),
+ mGrFaceRefCnt(0),
+ mComputedSizeOfUserFont(0)
+{
+ memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
+ memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
+}
+
+gfxFontEntry::~gfxFontEntry()
+{
+ if (mCOLR) {
+ hb_blob_destroy(mCOLR);
+ }
+
+ if (mCPAL) {
+ hb_blob_destroy(mCPAL);
+ }
+
+ // For downloaded fonts, we need to tell the user font cache that this
+ // entry is being deleted.
+ if (mIsDataUserFont) {
+ gfxUserFontSet::UserFontCache::ForgetFont(this);
+ }
+
+ if (mFeatureInputs) {
+ for (auto iter = mFeatureInputs->Iter(); !iter.Done(); iter.Next()) {
+ hb_set_t*& set = iter.Data();
+ hb_set_destroy(set);
+ }
+ }
+
+ // By the time the entry is destroyed, all font instances that were
+ // using it should already have been deleted, and so the HB and/or Gr
+ // face objects should have been released.
+ MOZ_ASSERT(!mHBFace);
+ MOZ_ASSERT(!mGrFaceInitialized);
+}
+
+bool gfxFontEntry::IsSymbolFont()
+{
+ return mSymbolFont;
+}
+
+bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
+{
+ if (!mCharacterMap) {
+ ReadCMAP();
+ NS_ASSERTION(mCharacterMap, "failed to initialize character map");
+ }
+ return mCharacterMap->test(aCh);
+}
+
+nsresult gfxFontEntry::InitializeUVSMap()
+{
+ // mUVSOffset will not be initialized
+ // until cmap is initialized.
+ if (!mCharacterMap) {
+ ReadCMAP();
+ NS_ASSERTION(mCharacterMap, "failed to initialize character map");
+ }
+
+ if (!mUVSOffset) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mUVSData) {
+ const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
+ AutoTable cmapTable(this, kCmapTag);
+ if (!cmapTable) {
+ mUVSOffset = 0; // don't bother to read the table again
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePtr<uint8_t[]> uvsData;
+ unsigned int cmapLen;
+ const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
+ nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
+ (const uint8_t*)cmapData + mUVSOffset,
+ cmapLen - mUVSOffset, uvsData);
+
+ if (NS_FAILED(rv)) {
+ mUVSOffset = 0; // don't bother to read the table again
+ return rv;
+ }
+
+ mUVSData = Move(uvsData);
+ }
+
+ return NS_OK;
+}
+
+uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
+{
+ InitializeUVSMap();
+
+ if (mUVSData) {
+ return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData.get(), aCh, aVS);
+ }
+
+ return 0;
+}
+
+bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
+{
+ hb_face_t *face = GetHBFace();
+ if (!face) {
+ return false;
+ }
+
+ unsigned int index;
+ hb_tag_t chosenScript;
+ bool found =
+ hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
+ aScriptTags, &index, &chosenScript);
+ hb_face_destroy(face);
+
+ return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
+}
+
+nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
+ mCharacterMap = new gfxCharacterMap();
+ return NS_OK;
+}
+
+nsString
+gfxFontEntry::RealFaceName()
+{
+ AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
+ if (nameTable) {
+ nsAutoString name;
+ nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
+ if (NS_SUCCEEDED(rv)) {
+ return name;
+ }
+ }
+ return Name();
+}
+
+already_AddRefed<gfxFont>
+gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle,
+ bool aNeedsBold,
+ gfxCharacterMap* aUnicodeRangeMap)
+{
+ // the font entry name is the psname, not the family name
+ RefPtr<gfxFont> font =
+ gfxFontCache::GetCache()->Lookup(this, aStyle, aUnicodeRangeMap);
+
+ if (!font) {
+ gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
+ if (!newFont)
+ return nullptr;
+ if (!newFont->Valid()) {
+ delete newFont;
+ return nullptr;
+ }
+ font = newFont;
+ font->SetUnicodeRangeMap(aUnicodeRangeMap);
+ gfxFontCache::GetCache()->AddNew(font);
+ }
+ return font.forget();
+}
+
+uint16_t
+gfxFontEntry::UnitsPerEm()
+{
+ if (!mUnitsPerEm) {
+ AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
+ if (headTable) {
+ uint32_t len;
+ const HeadTable* head =
+ reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
+ &len));
+ if (len >= sizeof(HeadTable)) {
+ mUnitsPerEm = head->unitsPerEm;
+ }
+ }
+
+ // if we didn't find a usable 'head' table, or if the value was
+ // outside the valid range, record it as invalid
+ if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
+ mUnitsPerEm = kInvalidUPEM;
+ }
+ }
+ return mUnitsPerEm;
+}
+
+bool
+gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
+{
+ NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
+ return mSVGGlyphs->HasSVGGlyph(aGlyphId);
+}
+
+bool
+gfxFontEntry::GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
+ gfxRect *aResult)
+{
+ MOZ_ASSERT(mSVGInitialized,
+ "SVG data has not yet been loaded. TryGetSVGData() first.");
+ MOZ_ASSERT(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
+ "font has invalid unitsPerEm");
+
+ cairo_matrix_t fontMatrix;
+ cairo_get_font_matrix(gfxFont::RefCairo(aDrawTarget), &fontMatrix);
+
+ gfxMatrix svgToAppSpace(fontMatrix.xx, fontMatrix.yx,
+ fontMatrix.xy, fontMatrix.yy,
+ fontMatrix.x0, fontMatrix.y0);
+ svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
+
+ return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
+}
+
+bool
+gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
+ SVGContextPaint* aContextPaint)
+{
+ NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
+ return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, aContextPaint);
+}
+
+bool
+gfxFontEntry::TryGetSVGData(gfxFont* aFont)
+{
+ if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
+ return false;
+ }
+
+ if (!mSVGInitialized) {
+ mSVGInitialized = true;
+
+ // If UnitsPerEm is not known/valid, we can't use SVG glyphs
+ if (UnitsPerEm() == kInvalidUPEM) {
+ return false;
+ }
+
+ // We don't use AutoTable here because we'll pass ownership of this
+ // blob to the gfxSVGGlyphs, once we've confirmed the table exists
+ hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
+ if (!svgTable) {
+ return false;
+ }
+
+ // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
+ // with it.
+ mSVGGlyphs = MakeUnique<gfxSVGGlyphs>(svgTable, this);
+ }
+
+ if (mSVGGlyphs && !mFontsUsingSVGGlyphs.Contains(aFont)) {
+ mFontsUsingSVGGlyphs.AppendElement(aFont);
+ }
+
+ return !!mSVGGlyphs;
+}
+
+void
+gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
+{
+ mFontsUsingSVGGlyphs.RemoveElement(aFont);
+}
+
+void
+gfxFontEntry::NotifyGlyphsChanged()
+{
+ for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
+ gfxFont* font = mFontsUsingSVGGlyphs[i];
+ font->NotifyGlyphsChanged();
+ }
+}
+
+bool
+gfxFontEntry::TryGetColorGlyphs()
+{
+ if (mCheckedForColorGlyph) {
+ return (mCOLR && mCPAL);
+ }
+
+ mCheckedForColorGlyph = true;
+
+ mCOLR = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
+ if (!mCOLR) {
+ return false;
+ }
+
+ mCPAL = GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L'));
+ if (!mCPAL) {
+ hb_blob_destroy(mCOLR);
+ mCOLR = nullptr;
+ return false;
+ }
+
+ // validation COLR and CPAL table
+ if (gfxFontUtils::ValidateColorGlyphs(mCOLR, mCPAL)) {
+ return true;
+ }
+
+ hb_blob_destroy(mCOLR);
+ hb_blob_destroy(mCPAL);
+ mCOLR = nullptr;
+ mCPAL = nullptr;
+ return false;
+}
+
+/**
+ * FontTableBlobData
+ *
+ * See FontTableHashEntry for the general strategy.
+ */
+
+class gfxFontEntry::FontTableBlobData {
+public:
+ explicit FontTableBlobData(nsTArray<uint8_t>&& aBuffer)
+ : mTableData(Move(aBuffer))
+ , mHashtable(nullptr)
+ , mHashKey(0)
+ {
+ MOZ_COUNT_CTOR(FontTableBlobData);
+ }
+
+ ~FontTableBlobData() {
+ MOZ_COUNT_DTOR(FontTableBlobData);
+ if (mHashtable && mHashKey) {
+ mHashtable->RemoveEntry(mHashKey);
+ }
+ }
+
+ // Useful for creating blobs
+ const char *GetTable() const
+ {
+ return reinterpret_cast<const char*>(mTableData.Elements());
+ }
+ uint32_t GetTableLength() const { return mTableData.Length(); }
+
+ // Tell this FontTableBlobData to remove the HashEntry when this is
+ // destroyed.
+ void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
+ uint32_t aHashKey)
+ {
+ mHashtable = aHashtable;
+ mHashKey = aHashKey;
+ }
+
+ // Disconnect from the HashEntry (because the blob has already been
+ // removed from the hashtable).
+ void ForgetHashEntry()
+ {
+ mHashtable = nullptr;
+ mHashKey = 0;
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+ return mTableData.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ // The font table data block
+ nsTArray<uint8_t> mTableData;
+
+ // The blob destroy function needs to know the owning hashtable
+ // and the hashtable key, so that it can remove the entry.
+ nsTHashtable<FontTableHashEntry> *mHashtable;
+ uint32_t mHashKey;
+
+ // not implemented
+ FontTableBlobData(const FontTableBlobData&);
+};
+
+hb_blob_t *
+gfxFontEntry::FontTableHashEntry::
+ShareTableAndGetBlob(nsTArray<uint8_t>&& aTable,
+ nsTHashtable<FontTableHashEntry> *aHashtable)
+{
+ Clear();
+ // adopts elements of aTable
+ mSharedBlobData = new FontTableBlobData(Move(aTable));
+
+ mBlob = hb_blob_create(mSharedBlobData->GetTable(),
+ mSharedBlobData->GetTableLength(),
+ HB_MEMORY_MODE_READONLY,
+ mSharedBlobData, DeleteFontTableBlobData);
+ if (mBlob == hb_blob_get_empty() ) {
+ // The FontTableBlobData was destroyed during hb_blob_create().
+ // The (empty) blob is still be held in the hashtable with a strong
+ // reference.
+ return hb_blob_reference(mBlob);
+ }
+
+ // Tell the FontTableBlobData to remove this hash entry when destroyed.
+ // The hashtable does not keep a strong reference.
+ mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
+ return mBlob;
+}
+
+void
+gfxFontEntry::FontTableHashEntry::Clear()
+{
+ // If the FontTableBlobData is managing the hash entry, then the blob is
+ // not owned by this HashEntry; otherwise there is strong reference to the
+ // blob that must be removed.
+ if (mSharedBlobData) {
+ mSharedBlobData->ForgetHashEntry();
+ mSharedBlobData = nullptr;
+ } else if (mBlob) {
+ hb_blob_destroy(mBlob);
+ }
+ mBlob = nullptr;
+}
+
+// a hb_destroy_func for hb_blob_create
+
+/* static */ void
+gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
+{
+ delete static_cast<FontTableBlobData*>(aBlobData);
+}
+
+hb_blob_t *
+gfxFontEntry::FontTableHashEntry::GetBlob() const
+{
+ return hb_blob_reference(mBlob);
+}
+
+bool
+gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
+{
+ if (!mFontTableCache) {
+ // we do this here rather than on fontEntry construction
+ // because not all shapers will access the table cache at all
+ mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8);
+ }
+
+ FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
+ if (!entry) {
+ return false;
+ }
+
+ *aBlob = entry->GetBlob();
+ return true;
+}
+
+hb_blob_t *
+gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
+ nsTArray<uint8_t>* aBuffer)
+{
+ if (MOZ_UNLIKELY(!mFontTableCache)) {
+ // we do this here rather than on fontEntry construction
+ // because not all shapers will access the table cache at all
+ mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8);
+ }
+
+ FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
+ if (MOZ_UNLIKELY(!entry)) { // OOM
+ return nullptr;
+ }
+
+ if (!aBuffer) {
+ // ensure the entry is null
+ entry->Clear();
+ return nullptr;
+ }
+
+ return entry->ShareTableAndGetBlob(Move(*aBuffer), mFontTableCache.get());
+}
+
+already_AddRefed<gfxCharacterMap>
+gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
+ uint32_t& aUVSOffset,
+ bool& aSymbolFont)
+{
+ if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
+ return nullptr;
+ }
+
+ return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
+}
+
+hb_blob_t *
+gfxFontEntry::GetFontTable(uint32_t aTag)
+{
+ hb_blob_t *blob;
+ if (GetExistingFontTable(aTag, &blob)) {
+ return blob;
+ }
+
+ nsTArray<uint8_t> buffer;
+ bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
+
+ return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
+}
+
+// callback for HarfBuzz to get a font table (in hb_blob_t form)
+// from the font entry (passed as aUserData)
+/*static*/ hb_blob_t *
+gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
+{
+ gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
+
+ // bug 589682 - ignore the GDEF table in buggy fonts (applies to
+ // Italic and BoldItalic faces of Times New Roman)
+ if (aTag == TRUETYPE_TAG('G','D','E','F') &&
+ fontEntry->IgnoreGDEF()) {
+ return nullptr;
+ }
+
+ // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
+ // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
+ if (aTag == TRUETYPE_TAG('G','S','U','B') &&
+ fontEntry->IgnoreGSUB()) {
+ return nullptr;
+ }
+
+ return fontEntry->GetFontTable(aTag);
+}
+
+/*static*/ void
+gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
+{
+ gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
+ fe->ForgetHBFace();
+}
+
+void
+gfxFontEntry::ForgetHBFace()
+{
+ mHBFace = nullptr;
+}
+
+hb_face_t*
+gfxFontEntry::GetHBFace()
+{
+ if (!mHBFace) {
+ mHBFace = hb_face_create_for_tables(HBGetTable, this,
+ HBFaceDeletedCallback);
+ return mHBFace;
+ }
+ return hb_face_reference(mHBFace);
+}
+
+/*static*/ const void*
+gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
+ size_t *aLen)
+{
+ gfxFontEntry *fontEntry =
+ static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
+ hb_blob_t *blob = fontEntry->GetFontTable(aName);
+ if (blob) {
+ unsigned int blobLength;
+ const void *tableData = hb_blob_get_data(blob, &blobLength);
+ fontEntry->mGrTableMap->Put(tableData, blob);
+ *aLen = blobLength;
+ return tableData;
+ }
+ *aLen = 0;
+ return nullptr;
+}
+
+/*static*/ void
+gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
+ const void *aTableBuffer)
+{
+ gfxFontEntry *fontEntry =
+ static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
+ void *data;
+ if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
+ fontEntry->mGrTableMap->Remove(aTableBuffer);
+ hb_blob_destroy(static_cast<hb_blob_t*>(data));
+ }
+}
+
+gr_face*
+gfxFontEntry::GetGrFace()
+{
+ if (!mGrFaceInitialized) {
+ gr_face_ops faceOps = {
+ sizeof(gr_face_ops),
+ GrGetTable,
+ GrReleaseTable
+ };
+ mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
+ mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
+ mGrFaceInitialized = true;
+ }
+ ++mGrFaceRefCnt;
+ return mGrFace;
+}
+
+void
+gfxFontEntry::ReleaseGrFace(gr_face *aFace)
+{
+ MOZ_ASSERT(aFace == mGrFace); // sanity-check
+ MOZ_ASSERT(mGrFaceRefCnt > 0);
+ if (--mGrFaceRefCnt == 0) {
+ gr_face_destroy(mGrFace);
+ mGrFace = nullptr;
+ mGrFaceInitialized = false;
+ delete mGrTableMap;
+ mGrTableMap = nullptr;
+ }
+}
+
+void
+gfxFontEntry::DisconnectSVG()
+{
+ if (mSVGInitialized && mSVGGlyphs) {
+ mSVGGlyphs = nullptr;
+ mSVGInitialized = false;
+ }
+}
+
+bool
+gfxFontEntry::HasFontTable(uint32_t aTableTag)
+{
+ AutoTable table(this, aTableTag);
+ return table && hb_blob_get_length(table) > 0;
+}
+
+void
+gfxFontEntry::CheckForGraphiteTables()
+{
+ mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
+}
+
+bool
+gfxFontEntry::HasGraphiteSpaceContextuals()
+{
+ if (!mGraphiteSpaceContextualsInitialized) {
+ gr_face* face = GetGrFace();
+ if (face) {
+ const gr_faceinfo* faceInfo = gr_face_info(face, 0);
+ mHasGraphiteSpaceContextuals =
+ faceInfo->space_contextuals != gr_faceinfo::gr_space_none;
+ }
+ ReleaseGrFace(face); // always balance GetGrFace, even if face is null
+ mGraphiteSpaceContextualsInitialized = true;
+ }
+ return mHasGraphiteSpaceContextuals;
+}
+
+#define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
+
+static_assert(int(Script::NUM_SCRIPT_CODES) <= FEATURE_SCRIPT_MASK, "Too many script codes");
+
+// high-order three bytes of tag with script in low-order byte
+#define SCRIPT_FEATURE(s,tag) (((~FEATURE_SCRIPT_MASK) & (tag)) | \
+ ((FEATURE_SCRIPT_MASK) & static_cast<uint32_t>(s)))
+
+bool
+gfxFontEntry::SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag)
+{
+ if (!mSupportedFeatures) {
+ mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey,bool>>();
+ }
+
+ // note: high-order three bytes *must* be unique for each feature
+ // listed below (see SCRIPT_FEATURE macro def'n)
+ NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
+ aFeatureTag == HB_TAG('c','2','s','c') ||
+ aFeatureTag == HB_TAG('p','c','a','p') ||
+ aFeatureTag == HB_TAG('c','2','p','c') ||
+ aFeatureTag == HB_TAG('s','u','p','s') ||
+ aFeatureTag == HB_TAG('s','u','b','s') ||
+ aFeatureTag == HB_TAG('v','e','r','t'),
+ "use of unknown feature tag");
+
+ // note: graphite feature support uses the last script index
+ NS_ASSERTION(int(aScript) < FEATURE_SCRIPT_MASK - 1,
+ "need to bump the size of the feature shift");
+
+ uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
+ bool result;
+ if (mSupportedFeatures->Get(scriptFeature, &result)) {
+ return result;
+ }
+
+ result = false;
+
+ hb_face_t *face = GetHBFace();
+
+ if (hb_ot_layout_has_substitution(face)) {
+ hb_script_t hbScript =
+ gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
+
+ // Get the OpenType tag(s) that match this script code
+ hb_tag_t scriptTags[4] = {
+ HB_TAG_NONE,
+ HB_TAG_NONE,
+ HB_TAG_NONE,
+ HB_TAG_NONE
+ };
+ hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
+
+ // Replace the first remaining NONE with DEFAULT
+ hb_tag_t* scriptTag = &scriptTags[0];
+ while (*scriptTag != HB_TAG_NONE) {
+ ++scriptTag;
+ }
+ *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
+
+ // Now check for 'smcp' under the first of those scripts that is present
+ const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
+ scriptTag = &scriptTags[0];
+ while (*scriptTag != HB_TAG_NONE) {
+ unsigned int scriptIndex;
+ if (hb_ot_layout_table_find_script(face, kGSUB, *scriptTag,
+ &scriptIndex)) {
+ if (hb_ot_layout_language_find_feature(face, kGSUB,
+ scriptIndex,
+ HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+ aFeatureTag, nullptr)) {
+ result = true;
+ }
+ break;
+ }
+ ++scriptTag;
+ }
+ }
+
+ hb_face_destroy(face);
+
+ mSupportedFeatures->Put(scriptFeature, result);
+
+ return result;
+}
+
+const hb_set_t*
+gfxFontEntry::InputsForOpenTypeFeature(Script aScript, uint32_t aFeatureTag)
+{
+ if (!mFeatureInputs) {
+ mFeatureInputs = MakeUnique<nsDataHashtable<nsUint32HashKey,hb_set_t*>>();
+ }
+
+ NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
+ aFeatureTag == HB_TAG('s','u','b','s'),
+ "use of unknown feature tag");
+
+ uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
+ hb_set_t *inputGlyphs;
+ if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
+ return inputGlyphs;
+ }
+
+ inputGlyphs = hb_set_create();
+
+ hb_face_t *face = GetHBFace();
+
+ if (hb_ot_layout_has_substitution(face)) {
+ hb_script_t hbScript =
+ gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
+
+ // Get the OpenType tag(s) that match this script code
+ hb_tag_t scriptTags[4] = {
+ HB_TAG_NONE,
+ HB_TAG_NONE,
+ HB_TAG_NONE,
+ HB_TAG_NONE
+ };
+ hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
+
+ // Replace the first remaining NONE with DEFAULT
+ hb_tag_t* scriptTag = &scriptTags[0];
+ while (*scriptTag != HB_TAG_NONE) {
+ ++scriptTag;
+ }
+ *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
+
+ const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
+ hb_tag_t features[2] = { aFeatureTag, HB_TAG_NONE };
+ hb_set_t *featurelookups = hb_set_create();
+ hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr,
+ features, featurelookups);
+ hb_codepoint_t index = -1;
+ while (hb_set_next(featurelookups, &index)) {
+ hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index,
+ nullptr, inputGlyphs,
+ nullptr, nullptr);
+ }
+ hb_set_destroy(featurelookups);
+ }
+
+ hb_face_destroy(face);
+
+ mFeatureInputs->Put(scriptFeature, inputGlyphs);
+ return inputGlyphs;
+}
+
+bool
+gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag)
+{
+ if (!mSupportedFeatures) {
+ mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey,bool>>();
+ }
+
+ // note: high-order three bytes *must* be unique for each feature
+ // listed below (see SCRIPT_FEATURE macro def'n)
+ NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
+ aFeatureTag == HB_TAG('c','2','s','c') ||
+ aFeatureTag == HB_TAG('p','c','a','p') ||
+ aFeatureTag == HB_TAG('c','2','p','c') ||
+ aFeatureTag == HB_TAG('s','u','p','s') ||
+ aFeatureTag == HB_TAG('s','u','b','s'),
+ "use of unknown feature tag");
+
+ // graphite feature check uses the last script slot
+ uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
+ bool result;
+ if (mSupportedFeatures->Get(scriptFeature, &result)) {
+ return result;
+ }
+
+ gr_face* face = GetGrFace();
+ result = face ? gr_face_find_fref(face, aFeatureTag) != nullptr : false;
+ ReleaseGrFace(face);
+
+ mSupportedFeatures->Put(scriptFeature, result);
+
+ return result;
+}
+
+bool
+gfxFontEntry::GetColorLayersInfo(uint32_t aGlyphId,
+ const mozilla::gfx::Color& aDefaultColor,
+ nsTArray<uint16_t>& aLayerGlyphs,
+ nsTArray<mozilla::gfx::Color>& aLayerColors)
+{
+ return gfxFontUtils::GetColorGlyphLayers(mCOLR,
+ mCPAL,
+ aGlyphId,
+ aDefaultColor,
+ aLayerGlyphs,
+ aLayerColors);
+}
+
+size_t
+gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = 0;
+ if (mBlob) {
+ n += aMallocSizeOf(mBlob);
+ }
+ if (mSharedBlobData) {
+ n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ return n;
+}
+
+void
+gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+ // cmaps are shared so only non-shared cmaps are included here
+ if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
+ aSizes->mCharMapsSize +=
+ mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (mFontTableCache) {
+ aSizes->mFontTableCacheSize +=
+ mFontTableCache->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ // If the font has UVS data, we count that as part of the character map.
+ if (mUVSData) {
+ aSizes->mCharMapsSize += aMallocSizeOf(mUVSData.get());
+ }
+
+ // The following, if present, are essentially cached forms of font table
+ // data, so we'll accumulate them together with the basic table cache.
+ if (mUserFontData) {
+ aSizes->mFontTableCacheSize +=
+ mUserFontData->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (mSVGGlyphs) {
+ aSizes->mFontTableCacheSize +=
+ mSVGGlyphs->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (mSupportedFeatures) {
+ aSizes->mFontTableCacheSize +=
+ mSupportedFeatures->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (mFeatureInputs) {
+ aSizes->mFontTableCacheSize +=
+ mFeatureInputs->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ for (auto iter = mFeatureInputs->ConstIter(); !iter.Done();
+ iter.Next()) {
+ // There's no API to get the real size of an hb_set, so we'll use
+ // an approximation based on knowledge of the implementation.
+ aSizes->mFontTableCacheSize += 8192; // vector of 64K bits
+ }
+ }
+ // We don't include the size of mCOLR/mCPAL here, because (depending on the
+ // font backend implementation) they will either wrap blocks of data owned
+ // by the system (and potentially shared), or tables that are in our font
+ // table cache and therefore already counted.
+}
+
+void
+gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+// This is used to report the size of an individual downloaded font in the
+// user font cache. (Fonts that are part of the platform font list accumulate
+// their sizes to the font list's reporter using the AddSizeOf... methods
+// above.)
+size_t
+gfxFontEntry::ComputedSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ FontListSizes s = { 0 };
+ AddSizeOfExcludingThis(aMallocSizeOf, &s);
+
+ // When reporting memory used for the main platform font list,
+ // where we're typically summing the totals for a few hundred font faces,
+ // we report the fields of FontListSizes separately.
+ // But for downloaded user fonts, the actual resource data (added below)
+ // will dominate, and the minor overhead of these pieces isn't worth
+ // splitting out for an individual font.
+ size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize;
+
+ if (mIsDataUserFont) {
+ MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?");
+ result += mComputedSizeOfUserFont;
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// class gfxFontFamily
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// we consider faces with mStandardFace == true to be "less than" those with false,
+// because during style matching, earlier entries are tried first
+class FontEntryStandardFaceComparator {
+ public:
+ bool Equals(const RefPtr<gfxFontEntry>& a, const RefPtr<gfxFontEntry>& b) const {
+ return a->mStandardFace == b->mStandardFace;
+ }
+ bool LessThan(const RefPtr<gfxFontEntry>& a, const RefPtr<gfxFontEntry>& b) const {
+ return (a->mStandardFace == true && b->mStandardFace == false);
+ }
+};
+
+void
+gfxFontFamily::SortAvailableFonts()
+{
+ mAvailableFonts.Sort(FontEntryStandardFaceComparator());
+}
+
+bool
+gfxFontFamily::HasOtherFamilyNames()
+{
+ // need to read in other family names to determine this
+ if (!mOtherFamilyNamesInitialized) {
+ ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
+ }
+ return mHasOtherFamilyNames;
+}
+
+gfxFontEntry*
+gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
+ bool& aNeedsSyntheticBold)
+{
+ AutoTArray<gfxFontEntry*,4> matched;
+ FindAllFontsForStyle(aFontStyle, matched, aNeedsSyntheticBold);
+ if (!matched.IsEmpty()) {
+ return matched[0];
+ }
+ return nullptr;
+}
+
+#define STYLE_SHIFT 2 // number of bits to contain style distance
+
+// style distance ==> [0,2]
+static inline uint32_t
+StyleDistance(uint32_t aFontStyle, uint32_t aTargetStyle)
+{
+ if (aFontStyle == aTargetStyle) {
+ return 0; // styles match exactly ==> 0
+ }
+ if (aFontStyle == NS_FONT_STYLE_NORMAL ||
+ aTargetStyle == NS_FONT_STYLE_NORMAL) {
+ return 2; // one is normal (but not the other) ==> 2
+ }
+ return 1; // neither is normal; must be italic vs oblique ==> 1
+}
+
+#define REVERSE_STRETCH_DISTANCE 5
+
+// stretch distance ==> [0,13]
+static inline uint32_t
+StretchDistance(int16_t aFontStretch, int16_t aTargetStretch)
+{
+ int32_t distance = 0;
+ if (aTargetStretch != aFontStretch) {
+ // stretch values are in the range -4 .. +4
+ // if aTargetStretch is positive, we prefer more-positive values;
+ // if zero or negative, prefer more-negative
+ if (aTargetStretch > 0) {
+ distance = (aFontStretch - aTargetStretch);
+ } else {
+ distance = (aTargetStretch - aFontStretch);
+ }
+ // if the computed "distance" here is negative, it means that
+ // aFontEntry lies in the "non-preferred" direction from aTargetStretch,
+ // so we treat that as larger than any preferred-direction distance
+ // (max possible is 4) by adding an extra 5 to the absolute value
+ if (distance < 0) {
+ distance = -distance + REVERSE_STRETCH_DISTANCE;
+ }
+ }
+ return uint32_t(distance);
+}
+
+// CSS currently limits font weights to multiples of 100 but the weight
+// matching code below does not assume this.
+//
+// Calculate weight distance with values in the range (0..1000). In general,
+// heavier weights match towards even heavier weights while lighter weights
+// match towards even lighter weights. Target weight values in the range
+// [400..500] are special, since they will first match up to 500, then down
+// towards 0, then up again towards 999.
+//
+// Example: with target 600 and font weight 800, distance will be 200. With
+// target 300 and font weight 600, distance will be 900, since heavier
+// weights are farther away than lighter weights. If the target is 5 and the
+// font weight 995, the distance would be 1590 for the same reason.
+
+#define REVERSE_WEIGHT_DISTANCE 600
+#define WEIGHT_SHIFT 11 // number of bits to contain weight distance
+
+// weight distance ==> [0,1598]
+static inline uint32_t
+WeightDistance(uint32_t aFontWeight, uint32_t aTargetWeight)
+{
+ // Compute a measure of the "distance" between the requested
+ // weight and the given fontEntry
+
+ int32_t distance = 0, addedDistance = 0;
+ if (aTargetWeight != aFontWeight) {
+ if (aTargetWeight > 500) {
+ distance = aFontWeight - aTargetWeight;
+ } else if (aTargetWeight < 400) {
+ distance = aTargetWeight - aFontWeight;
+ } else {
+ // special case - target is between 400 and 500
+
+ // font weights between 400 and 500 are close
+ if (aFontWeight >= 400 && aFontWeight <= 500) {
+ if (aFontWeight < aTargetWeight) {
+ distance = 500 - aFontWeight;
+ } else {
+ distance = aFontWeight - aTargetWeight;
+ }
+ } else {
+ // font weights outside use rule for target weights < 400 with
+ // added distance to separate from font weights in
+ // the [400..500] range
+ distance = aTargetWeight - aFontWeight;
+ addedDistance = 100;
+ }
+ }
+ if (distance < 0) {
+ distance = -distance + REVERSE_WEIGHT_DISTANCE;
+ }
+ distance += addedDistance;
+ }
+ return uint32_t(distance);
+}
+
+#define MAX_DISTANCE 0xffffffff
+
+static inline uint32_t
+WeightStyleStretchDistance(gfxFontEntry* aFontEntry,
+ const gfxFontStyle& aTargetStyle)
+{
+ // weight/style/stretch priority: stretch >> style >> weight
+ uint32_t stretchDist =
+ StretchDistance(aFontEntry->mStretch, aTargetStyle.stretch);
+ uint32_t styleDist = StyleDistance(aFontEntry->mStyle, aTargetStyle.style);
+ uint32_t weightDist =
+ WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
+
+ NS_ASSERTION(weightDist < (1 << WEIGHT_SHIFT), "weight value out of bounds");
+ NS_ASSERTION(styleDist < (1 << STYLE_SHIFT), "slope value out of bounds");
+
+ return (stretchDist << (STYLE_SHIFT + WEIGHT_SHIFT)) |
+ (styleDist << WEIGHT_SHIFT) |
+ weightDist;
+}
+
+void
+gfxFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
+ nsTArray<gfxFontEntry*>& aFontEntryList,
+ bool& aNeedsSyntheticBold)
+{
+ if (!mHasStyles) {
+ FindStyleVariations(); // collect faces for the family, if not already done
+ }
+
+ NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
+ NS_ASSERTION(aFontEntryList.IsEmpty(), "non-empty fontlist passed in");
+
+ aNeedsSyntheticBold = false;
+
+ int8_t baseWeight = aFontStyle.ComputeWeight();
+ bool wantBold = baseWeight >= 6;
+ gfxFontEntry *fe = nullptr;
+
+ // If the family has only one face, we simply return it; no further
+ // checking needed
+ uint32_t count = mAvailableFonts.Length();
+ if (count == 1) {
+ fe = mAvailableFonts[0];
+ aNeedsSyntheticBold =
+ wantBold && !fe->IsBold() && aFontStyle.allowSyntheticWeight;
+ aFontEntryList.AppendElement(fe);
+ return;
+ }
+
+ // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
+ // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
+ // stored in the above order; note that some of the entries may be nullptr.
+ // We can then pick the required entry based on whether the request is for
+ // bold or non-bold, italic or non-italic, without running the more complex
+ // matching algorithm used for larger families with many weights and/or widths.
+
+ if (mIsSimpleFamily) {
+ // Family has no more than the "standard" 4 faces, at fixed indexes;
+ // calculate which one we want.
+ // Note that we cannot simply return it as not all 4 faces are necessarily present.
+ bool wantItalic = (aFontStyle.style != NS_FONT_STYLE_NORMAL);
+ uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
+ (wantBold ? kBoldMask : 0);
+
+ // if the desired style is available, return it directly
+ fe = mAvailableFonts[faceIndex];
+ if (fe) {
+ // no need to set aNeedsSyntheticBold here as we matched the boldness request
+ aFontEntryList.AppendElement(fe);
+ return;
+ }
+
+ // order to check fallback faces in a simple family, depending on requested style
+ static const uint8_t simpleFallbacks[4][3] = {
+ { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular
+ { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
+ { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic
+ { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic
+ };
+ const uint8_t *order = simpleFallbacks[faceIndex];
+
+ for (uint8_t trial = 0; trial < 3; ++trial) {
+ // check remaining faces in order of preference to find the first that actually exists
+ fe = mAvailableFonts[order[trial]];
+ if (fe) {
+ aNeedsSyntheticBold =
+ wantBold && !fe->IsBold() &&
+ aFontStyle.allowSyntheticWeight;
+ aFontEntryList.AppendElement(fe);
+ return;
+ }
+ }
+
+ // this can't happen unless we have totally broken the font-list manager!
+ NS_NOTREACHED("no face found in simple font family!");
+ }
+
+ // Pick the font(s) that are closest to the desired weight, style, and
+ // stretch. Iterate over all fonts, measuring the weight/style distance.
+ // Because of unicode-range values, there may be more than one font for a
+ // given but the 99% use case is only a single font entry per
+ // weight/style/stretch distance value. To optimize this, only add entries
+ // to the matched font array when another entry already has the same
+ // weight/style/stretch distance and add the last matched font entry. For
+ // normal platform fonts with a single font entry for each
+ // weight/style/stretch combination, only the last matched font entry will
+ // be added.
+
+ uint32_t minDistance = MAX_DISTANCE;
+ gfxFontEntry* matched = nullptr;
+ // iterate in forward order so that faces like 'Bold' are matched before
+ // matching style distance faces such as 'Bold Outline' (see bug 1185812)
+ for (uint32_t i = 0; i < count; i++) {
+ fe = mAvailableFonts[i];
+ // weight/style/stretch priority: stretch >> style >> weight
+ uint32_t distance = WeightStyleStretchDistance(fe, aFontStyle);
+ if (distance < minDistance) {
+ matched = fe;
+ if (!aFontEntryList.IsEmpty()) {
+ aFontEntryList.Clear();
+ }
+ minDistance = distance;
+ } else if (distance == minDistance) {
+ if (matched) {
+ aFontEntryList.AppendElement(matched);
+ }
+ matched = fe;
+ }
+ }
+
+ NS_ASSERTION(matched, "didn't match a font within a family");
+
+ if (matched) {
+ aFontEntryList.AppendElement(matched);
+ if (!matched->IsBold() && aFontStyle.weight >= 600 &&
+ aFontStyle.allowSyntheticWeight) {
+ aNeedsSyntheticBold = true;
+ }
+ }
+}
+
+void
+gfxFontFamily::CheckForSimpleFamily()
+{
+ // already checked this family
+ if (mIsSimpleFamily) {
+ return;
+ }
+
+ uint32_t count = mAvailableFonts.Length();
+ if (count > 4 || count == 0) {
+ return; // can't be "simple" if there are >4 faces;
+ // if none then the family is unusable anyway
+ }
+
+ if (count == 1) {
+ mIsSimpleFamily = true;
+ return;
+ }
+
+ int16_t firstStretch = mAvailableFonts[0]->Stretch();
+
+ gfxFontEntry *faces[4] = { 0 };
+ for (uint8_t i = 0; i < count; ++i) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (fe->Stretch() != firstStretch || fe->IsOblique()) {
+ // simple families don't have varying font-stretch or oblique
+ return;
+ }
+ uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
+ (fe->Weight() >= 600 ? kBoldMask : 0);
+ if (faces[faceIndex]) {
+ return; // two faces resolve to the same slot; family isn't "simple"
+ }
+ faces[faceIndex] = fe;
+ }
+
+ // we have successfully slotted the available faces into the standard
+ // 4-face framework
+ mAvailableFonts.SetLength(4);
+ for (uint8_t i = 0; i < 4; ++i) {
+ if (mAvailableFonts[i].get() != faces[i]) {
+ mAvailableFonts[i].swap(faces[i]);
+ }
+ }
+
+ mIsSimpleFamily = true;
+}
+
+#ifdef DEBUG
+bool
+gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) {
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ for (i = 0; i < numFonts; i++) {
+ if (mAvailableFonts[i] == aFontEntry) {
+ return true;
+ }
+ // userfonts contain the actual real font entry
+ if (mAvailableFonts[i] && mAvailableFonts[i]->mIsUserFontContainer) {
+ gfxUserFontEntry* ufe =
+ static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get());
+ if (ufe->GetPlatformFontEntry() == aFontEntry) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
+void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
+{
+ // just return the primary name; subclasses should override
+ aLocalizedName = mName;
+}
+
+// metric for how close a given font matches a style
+static int32_t
+CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
+{
+ int32_t rank = 0;
+ if (aStyle) {
+ // italics
+ bool wantUpright = (aStyle->style == NS_FONT_STYLE_NORMAL);
+ if (aFontEntry->IsUpright() == wantUpright) {
+ rank += 10;
+ }
+
+ // measure of closeness of weight to the desired value
+ rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
+ } else {
+ // if no font to match, prefer non-bold, non-italic fonts
+ if (aFontEntry->IsUpright()) {
+ rank += 3;
+ }
+ if (!aFontEntry->IsBold()) {
+ rank += 2;
+ }
+ }
+
+ return rank;
+}
+
+#define RANK_MATCHED_CMAP 20
+
+void
+gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
+{
+ if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
+ // none of the faces in the family support the required char,
+ // so bail out immediately
+ return;
+ }
+
+ bool needsBold;
+ gfxFontEntry *fe =
+ FindFontForStyle(aMatchData->mStyle ? *aMatchData->mStyle
+ : gfxFontStyle(),
+ needsBold);
+
+ if (fe && !fe->SkipDuringSystemFallback()) {
+ int32_t rank = 0;
+
+ if (fe->HasCharacter(aMatchData->mCh)) {
+ rank += RANK_MATCHED_CMAP;
+ aMatchData->mCount++;
+
+ LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
+ uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
+ Script script = GetScriptCode(aMatchData->mCh);
+ MOZ_LOG(log, LogLevel::Debug,\
+ ("(textrun-systemfallback-fonts) char: u+%6.6x "
+ "unicode-range: %d script: %d match: [%s]\n",
+ aMatchData->mCh,
+ unicodeRange, int(script),
+ NS_ConvertUTF16toUTF8(fe->Name()).get()));
+ }
+ }
+
+ aMatchData->mCmapsTested++;
+ if (rank == 0) {
+ return;
+ }
+
+ // omitting from original windows code -- family name, lang group, pitch
+ // not available in current FontEntry implementation
+ rank += CalcStyleMatch(fe, aMatchData->mStyle);
+
+ // xxx - add whether AAT font with morphing info for specific lang groups
+
+ if (rank > aMatchData->mMatchRank
+ || (rank == aMatchData->mMatchRank &&
+ Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
+ {
+ aMatchData->mBestMatch = fe;
+ aMatchData->mMatchedFamily = this;
+ aMatchData->mMatchRank = rank;
+ }
+ }
+}
+
+void
+gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
+{
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ for (i = 0; i < numFonts; i++) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (fe && fe->HasCharacter(aMatchData->mCh)) {
+ int32_t rank = RANK_MATCHED_CMAP;
+ rank += CalcStyleMatch(fe, aMatchData->mStyle);
+ if (rank > aMatchData->mMatchRank
+ || (rank == aMatchData->mMatchRank &&
+ Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
+ {
+ aMatchData->mBestMatch = fe;
+ aMatchData->mMatchedFamily = this;
+ aMatchData->mMatchRank = rank;
+ }
+ }
+ }
+}
+
+/*static*/ void
+gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
+ const char *aNameData,
+ uint32_t aDataLength,
+ nsTArray<nsString>& aOtherFamilyNames,
+ bool useFullName)
+{
+ const gfxFontUtils::NameHeader *nameHeader =
+ reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
+
+ uint32_t nameCount = nameHeader->count;
+ if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
+ NS_WARNING("invalid font (name records)");
+ return;
+ }
+
+ const gfxFontUtils::NameRecord *nameRecord =
+ reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
+ uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
+
+ for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
+ uint32_t nameLen = nameRecord->length;
+ uint32_t nameOff = nameRecord->offset; // offset from base of string storage
+
+ if (stringsBase + nameOff + nameLen > aDataLength) {
+ NS_WARNING("invalid font (name table strings)");
+ return;
+ }
+
+ uint16_t nameID = nameRecord->nameID;
+ if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
+ (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
+ nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
+ nsAutoString otherFamilyName;
+ bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
+ nameLen,
+ uint32_t(nameRecord->platformID),
+ uint32_t(nameRecord->encodingID),
+ uint32_t(nameRecord->languageID),
+ otherFamilyName);
+ // add if not same as canonical family name
+ if (ok && otherFamilyName != aFamilyName) {
+ aOtherFamilyNames.AppendElement(otherFamilyName);
+ }
+ }
+ }
+}
+
+// returns true if other names were found, false otherwise
+bool
+gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
+ hb_blob_t *aNameTable,
+ bool useFullName)
+{
+ uint32_t dataLength;
+ const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
+ AutoTArray<nsString,4> otherFamilyNames;
+
+ ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
+ otherFamilyNames, useFullName);
+
+ uint32_t n = otherFamilyNames.Length();
+ for (uint32_t i = 0; i < n; i++) {
+ aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
+ }
+
+ return n != 0;
+}
+
+void
+gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
+{
+ if (mOtherFamilyNamesInitialized)
+ return;
+ mOtherFamilyNamesInitialized = true;
+
+ FindStyleVariations();
+
+ // read in other family names for the first face in the list
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
+
+ for (i = 0; i < numFonts; ++i) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (!fe) {
+ continue;
+ }
+ gfxFontEntry::AutoTable nameTable(fe, kNAME);
+ if (!nameTable) {
+ continue;
+ }
+ mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
+ nameTable);
+ break;
+ }
+
+ // read in other names for the first face in the list with the assumption
+ // that if extra names don't exist in that face then they don't exist in
+ // other faces for the same font
+ if (!mHasOtherFamilyNames)
+ return;
+
+ // read in names for all faces, needed to catch cases where fonts have
+ // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
+ for ( ; i < numFonts; i++) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (!fe) {
+ continue;
+ }
+ gfxFontEntry::AutoTable nameTable(fe, kNAME);
+ if (!nameTable) {
+ continue;
+ }
+ ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
+ }
+}
+
+void
+gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
+ bool aNeedFullnamePostscriptNames,
+ FontInfoData *aFontInfoData)
+{
+ // if all needed names have already been read, skip
+ if (mOtherFamilyNamesInitialized &&
+ (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
+ return;
+
+ bool asyncFontLoaderDisabled = false;
+
+ if (!mOtherFamilyNamesInitialized &&
+ aFontInfoData &&
+ aFontInfoData->mLoadOtherNames &&
+ !asyncFontLoaderDisabled)
+ {
+ AutoTArray<nsString,4> otherFamilyNames;
+ bool foundOtherNames =
+ aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
+ if (foundOtherNames) {
+ uint32_t i, n = otherFamilyNames.Length();
+ for (i = 0; i < n; i++) {
+ aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
+ }
+ }
+ mOtherFamilyNamesInitialized = true;
+ }
+
+ // if all needed data has been initialized, return
+ if (mOtherFamilyNamesInitialized &&
+ (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
+ return;
+ }
+
+ FindStyleVariations(aFontInfoData);
+
+ // check again, as style enumeration code may have loaded names
+ if (mOtherFamilyNamesInitialized &&
+ (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
+ return;
+ }
+
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
+
+ bool firstTime = true, readAllFaces = false;
+ for (i = 0; i < numFonts; ++i) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (!fe) {
+ continue;
+ }
+
+ nsAutoString fullname, psname;
+ bool foundFaceNames = false;
+ if (!mFaceNamesInitialized &&
+ aNeedFullnamePostscriptNames &&
+ aFontInfoData &&
+ aFontInfoData->mLoadFaceNames) {
+ aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
+ if (!fullname.IsEmpty()) {
+ aPlatformFontList->AddFullname(fe, fullname);
+ }
+ if (!psname.IsEmpty()) {
+ aPlatformFontList->AddPostscriptName(fe, psname);
+ }
+ foundFaceNames = true;
+
+ // found everything needed? skip to next font
+ if (mOtherFamilyNamesInitialized) {
+ continue;
+ }
+ }
+
+ // load directly from the name table
+ gfxFontEntry::AutoTable nameTable(fe, kNAME);
+ if (!nameTable) {
+ continue;
+ }
+
+ if (aNeedFullnamePostscriptNames && !foundFaceNames) {
+ if (gfxFontUtils::ReadCanonicalName(
+ nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
+ {
+ aPlatformFontList->AddFullname(fe, fullname);
+ }
+
+ if (gfxFontUtils::ReadCanonicalName(
+ nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
+ {
+ aPlatformFontList->AddPostscriptName(fe, psname);
+ }
+ }
+
+ if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
+ bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
+ nameTable);
+
+ // if the first face has a different name, scan all faces, otherwise
+ // assume the family doesn't have other names
+ if (firstTime && foundOtherName) {
+ mHasOtherFamilyNames = true;
+ readAllFaces = true;
+ }
+ firstTime = false;
+ }
+
+ // if not reading in any more names, skip other faces
+ if (!readAllFaces && !aNeedFullnamePostscriptNames) {
+ break;
+ }
+ }
+
+ mFaceNamesInitialized = true;
+ mOtherFamilyNamesInitialized = true;
+}
+
+
+gfxFontEntry*
+gfxFontFamily::FindFont(const nsAString& aPostscriptName)
+{
+ // find the font using a simple linear search
+ uint32_t numFonts = mAvailableFonts.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ gfxFontEntry *fe = mAvailableFonts[i].get();
+ if (fe && fe->Name() == aPostscriptName)
+ return fe;
+ }
+ return nullptr;
+}
+
+void
+gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
+{
+ FindStyleVariations(aFontInfoData);
+
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ for (i = 0; i < numFonts; i++) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ // don't try to load cmaps for downloadable fonts not yet loaded
+ if (!fe || fe->mIsUserFontContainer) {
+ continue;
+ }
+ fe->ReadCMAP(aFontInfoData);
+ mFamilyCharacterMap.Union(*(fe->mCharacterMap));
+ }
+ mFamilyCharacterMap.Compact();
+ mFamilyCharacterMapInitialized = true;
+}
+
+void
+gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize +=
+ mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ aSizes->mCharMapsSize +=
+ mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mAvailableFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
+ gfxFontEntry *fe = mAvailableFonts[i];
+ if (fe) {
+ fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
+ }
+ }
+}
+
+void
+gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
diff --git a/system/graphics/thebes/gfxFontEntry.h b/system/graphics/thebes/gfxFontEntry.h
new file mode 100644
index 000000000..7f0e7215b
--- /dev/null
+++ b/system/graphics/thebes/gfxFontEntry.h
@@ -0,0 +1,777 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONTENTRY_H
+#define GFX_FONTENTRY_H
+
+#include "gfxTypes.h"
+#include "nsString.h"
+#include "gfxFontConstants.h"
+#include "gfxFontFeatures.h"
+#include "gfxFontUtils.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsUnicodeScriptCodes.h"
+#include "nsDataHashtable.h"
+#include "harfbuzz/hb.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/UniquePtr.h"
+
+typedef struct gr_face gr_face;
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+struct gfxFontStyle;
+class gfxContext;
+class gfxFont;
+class gfxFontFamily;
+class gfxUserFontData;
+class gfxSVGGlyphs;
+class FontInfoData;
+struct FontListSizes;
+class nsIAtom;
+
+namespace mozilla {
+class SVGContextPaint;
+};
+
+class gfxCharacterMap : public gfxSparseBitSet {
+public:
+ nsrefcnt AddRef() {
+ NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this));
+ return mRefCnt;
+ }
+
+ nsrefcnt Release() {
+ NS_PRECONDITION(0 != mRefCnt, "dup release");
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap");
+ if (mRefCnt == 0) {
+ NotifyReleased();
+ // |this| has been deleted.
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ gfxCharacterMap() :
+ mHash(0), mBuildOnTheFly(false), mShared(false)
+ { }
+
+ explicit gfxCharacterMap(const gfxSparseBitSet& aOther) :
+ gfxSparseBitSet(aOther),
+ mHash(0), mBuildOnTheFly(false), mShared(false)
+ { }
+
+ void CalcHash() { mHash = GetChecksum(); }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ // hash of the cmap bitvector
+ uint32_t mHash;
+
+ // if cmap is built on the fly it's never shared
+ bool mBuildOnTheFly;
+
+ // cmap is shared globally
+ bool mShared;
+
+protected:
+ void NotifyReleased();
+
+ nsAutoRefCnt mRefCnt;
+
+private:
+ gfxCharacterMap(const gfxCharacterMap&);
+ gfxCharacterMap& operator=(const gfxCharacterMap&);
+};
+
+class gfxFontEntry {
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::unicode::Script Script;
+
+ NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
+
+ explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
+
+ // unique name for the face, *not* the family; not necessarily the
+ // "real" or user-friendly name, may be an internal identifier
+ const nsString& Name() const { return mName; }
+
+ // family name
+ const nsString& FamilyName() const { return mFamilyName; }
+
+ // The following two methods may be relatively expensive, as they
+ // will (usually, except on Linux) load and parse the 'name' table;
+ // they are intended only for the font-inspection API, not for
+ // perf-critical layout/drawing work.
+
+ // The "real" name of the face, if available from the font resource;
+ // returns Name() if nothing better is available.
+ virtual nsString RealFaceName();
+
+ uint16_t Weight() const { return mWeight; }
+ int16_t Stretch() const { return mStretch; }
+
+ bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
+ bool IsLocalUserFont() const { return mIsLocalUserFont; }
+ bool IsFixedPitch() const { return mFixedPitch; }
+ bool IsItalic() const { return mStyle == NS_FONT_STYLE_ITALIC; }
+ bool IsOblique() const { return mStyle == NS_FONT_STYLE_OBLIQUE; }
+ bool IsUpright() const { return mStyle == NS_FONT_STYLE_NORMAL; }
+ bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above
+ bool IgnoreGDEF() const { return mIgnoreGDEF; }
+ bool IgnoreGSUB() const { return mIgnoreGSUB; }
+
+ // whether a feature is supported by the font (limited to a small set
+ // of features for which some form of fallback needs to be implemented)
+ bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
+ bool SupportsGraphiteFeature(uint32_t aFeatureTag);
+
+ // returns a set containing all input glyph ids for a given feature
+ const hb_set_t*
+ InputsForOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
+
+ virtual bool IsSymbolFont();
+
+ virtual bool HasFontTable(uint32_t aTableTag);
+
+ inline bool HasGraphiteTables() {
+ if (!mCheckedForGraphiteTables) {
+ CheckForGraphiteTables();
+ mCheckedForGraphiteTables = true;
+ }
+ return mHasGraphiteTables;
+ }
+
+ inline bool HasCmapTable() {
+ if (!mCharacterMap) {
+ ReadCMAP();
+ NS_ASSERTION(mCharacterMap, "failed to initialize character map");
+ }
+ return mHasCmapTable;
+ }
+
+ inline bool HasCharacter(uint32_t ch) {
+ if (mCharacterMap && mCharacterMap->test(ch)) {
+ return true;
+ }
+ return TestCharacterMap(ch);
+ }
+
+ virtual bool SkipDuringSystemFallback() { return false; }
+ nsresult InitializeUVSMap();
+ uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
+
+ // All concrete gfxFontEntry subclasses (except gfxUserFontEntry) need
+ // to override this, otherwise the font will never be used as it will
+ // be considered to support no characters.
+ // ReadCMAP() must *always* set the mCharacterMap pointer to a valid
+ // gfxCharacterMap, even if empty, as other code assumes this pointer
+ // can be safely dereferenced.
+ virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
+
+ bool TryGetSVGData(gfxFont* aFont);
+ bool HasSVGGlyph(uint32_t aGlyphId);
+ bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
+ gfxRect *aResult);
+ bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
+ mozilla::SVGContextPaint* aContextPaint);
+ // Call this when glyph geometry or rendering has changed
+ // (e.g. animated SVG glyphs)
+ void NotifyGlyphsChanged();
+
+ bool TryGetColorGlyphs();
+ bool GetColorLayersInfo(uint32_t aGlyphId,
+ const mozilla::gfx::Color& aDefaultColor,
+ nsTArray<uint16_t>& layerGlyphs,
+ nsTArray<mozilla::gfx::Color>& layerColors);
+
+ virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
+ return true;
+ }
+ virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
+ return true;
+ }
+
+ // Access to raw font table data (needed for Harfbuzz):
+ // returns a pointer to data owned by the fontEntry or the OS,
+ // which will remain valid until the blob is destroyed.
+ // The data MUST be treated as read-only; we may be getting a
+ // reference to a shared system font cache.
+ //
+ // The default implementation uses CopyFontTable to get the data
+ // into a byte array, and maintains a cache of loaded tables.
+ //
+ // Subclasses should override this if they can provide more efficient
+ // access than copying table data into our own buffers.
+ //
+ // Get blob that encapsulates a specific font table, or nullptr if
+ // the table doesn't exist in the font.
+ //
+ // Caller is responsible to call hb_blob_destroy() on the returned blob
+ // (if non-nullptr) when no longer required. For transient access to a
+ // table, use of AutoTable (below) is generally preferred.
+ virtual hb_blob_t *GetFontTable(uint32_t aTag);
+
+ // Stack-based utility to return a specified table, automatically releasing
+ // the blob when the AutoTable goes out of scope.
+ class AutoTable {
+ public:
+ AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag)
+ {
+ mBlob = aFontEntry->GetFontTable(aTag);
+ }
+ ~AutoTable() {
+ if (mBlob) {
+ hb_blob_destroy(mBlob);
+ }
+ }
+ operator hb_blob_t*() const { return mBlob; }
+ private:
+ hb_blob_t* mBlob;
+ // not implemented:
+ AutoTable(const AutoTable&) = delete;
+ AutoTable& operator=(const AutoTable&) = delete;
+ };
+
+ already_AddRefed<gfxFont>
+ FindOrMakeFont(const gfxFontStyle *aStyle,
+ bool aNeedsBold,
+ gfxCharacterMap* aUnicodeRangeMap = nullptr);
+
+ // Get an existing font table cache entry in aBlob if it has been
+ // registered, or return false if not. Callers must call
+ // hb_blob_destroy on aBlob if true is returned.
+ //
+ // Note that some gfxFont implementations may not call this at all,
+ // if it is more efficient to get the table from the OS at that level.
+ bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob);
+
+ // Elements of aTable are transferred (not copied) to and returned in a
+ // new hb_blob_t which is registered on the gfxFontEntry, but the initial
+ // reference is owned by the caller. Removing the last reference
+ // unregisters the table from the font entry.
+ //
+ // Pass nullptr for aBuffer to indicate that the table is not present and
+ // nullptr will be returned. Also returns nullptr on OOM.
+ hb_blob_t *ShareFontTableAndGetBlob(uint32_t aTag,
+ nsTArray<uint8_t>* aTable);
+
+ // Get the font's unitsPerEm from the 'head' table, in the case of an
+ // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts,
+ // if present on the platform.
+ uint16_t UnitsPerEm();
+ enum {
+ kMinUPEM = 16, // Limits on valid unitsPerEm range, from the
+ kMaxUPEM = 16384, // OpenType spec
+ kInvalidUPEM = uint16_t(-1)
+ };
+
+ // Shaper face accessors:
+ // NOTE that harfbuzz and graphite handle ownership/lifetime of the face
+ // object in completely different ways.
+
+ // Get HarfBuzz face corresponding to this font file.
+ // Caller must release with hb_face_destroy() when finished with it,
+ // and the font entry will be notified via ForgetHBFace.
+ hb_face_t* GetHBFace();
+ virtual void ForgetHBFace();
+
+ // Get Graphite face corresponding to this font file.
+ // Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
+ gr_face* GetGrFace();
+ virtual void ReleaseGrFace(gr_face* aFace);
+
+ // Does the font have graphite contextuals that involve the space glyph
+ // (and therefore we should bypass the word cache)?
+ bool HasGraphiteSpaceContextuals();
+
+ // Release any SVG-glyphs document this font may have loaded.
+ void DisconnectSVG();
+
+ // Called to notify that aFont is being destroyed. Needed when we're tracking
+ // the fonts belonging to this font entry.
+ void NotifyFontDestroyed(gfxFont* aFont);
+
+ // For memory reporting of the platform font list.
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+ // Used for reporting on individual font entries in the user font cache,
+ // which are not present in the platform font list.
+ size_t
+ ComputedSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ // Used when checking for complex script support, to mask off cmap ranges
+ struct ScriptRange {
+ uint32_t rangeStart;
+ uint32_t rangeEnd;
+ hb_tag_t tags[3]; // one or two OpenType script tags to check,
+ // plus a NULL terminator
+ };
+
+ bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags);
+
+ nsString mName;
+ nsString mFamilyName;
+
+ uint8_t mStyle : 2; // italic/oblique
+ bool mFixedPitch : 1;
+ bool mIsValid : 1;
+ bool mIsBadUnderlineFont : 1;
+ bool mIsUserFontContainer : 1; // userfont entry
+ bool mIsDataUserFont : 1; // platform font entry (data)
+ bool mIsLocalUserFont : 1; // platform font entry (local)
+ bool mStandardFace : 1;
+ bool mSymbolFont : 1;
+ bool mIgnoreGDEF : 1;
+ bool mIgnoreGSUB : 1;
+ bool mSVGInitialized : 1;
+ bool mHasSpaceFeaturesInitialized : 1;
+ bool mHasSpaceFeatures : 1;
+ bool mHasSpaceFeaturesKerning : 1;
+ bool mHasSpaceFeaturesNonKerning : 1;
+ bool mSkipDefaultFeatureSpaceCheck : 1;
+ bool mGraphiteSpaceContextualsInitialized : 1;
+ bool mHasGraphiteSpaceContextuals : 1;
+ bool mSpaceGlyphIsInvisible : 1;
+ bool mSpaceGlyphIsInvisibleInitialized : 1;
+ bool mHasGraphiteTables : 1;
+ bool mCheckedForGraphiteTables : 1;
+ bool mHasCmapTable : 1;
+ bool mGrFaceInitialized : 1;
+ bool mCheckedForColorGlyph : 1;
+
+ // bitvector of substitution space features per script, one each
+ // for default and non-default features
+ uint32_t mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
+ uint32_t mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
+
+ uint16_t mWeight;
+ int16_t mStretch;
+
+ RefPtr<gfxCharacterMap> mCharacterMap;
+ uint32_t mUVSOffset;
+ mozilla::UniquePtr<uint8_t[]> mUVSData;
+ mozilla::UniquePtr<gfxUserFontData> mUserFontData;
+ mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
+ // list of gfxFonts that are using SVG glyphs
+ nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
+ nsTArray<gfxFontFeature> mFeatureSettings;
+ mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
+ mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
+ uint32_t mLanguageOverride;
+
+ // Color Layer font support
+ hb_blob_t* mCOLR;
+ hb_blob_t* mCPAL;
+
+protected:
+ friend class gfxPlatformFontList;
+ friend class gfxMacPlatformFontList;
+ friend class gfxUserFcFontEntry;
+ friend class gfxFontFamily;
+ friend class gfxSingleFaceMacFontFamily;
+ friend class gfxUserFontEntry;
+
+ gfxFontEntry();
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxFontEntry();
+
+ virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
+ NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
+ return nullptr;
+ }
+
+ virtual void CheckForGraphiteTables();
+
+ // Copy a font table into aBuffer.
+ // The caller will be responsible for ownership of the data.
+ virtual nsresult CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) {
+ NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?");
+ return NS_ERROR_FAILURE;
+ }
+
+ // lookup the cmap in cached font data
+ virtual already_AddRefed<gfxCharacterMap>
+ GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
+ uint32_t& aUVSOffset,
+ bool& aSymbolFont);
+
+ // helper for HasCharacter(), which is what client code should call
+ virtual bool TestCharacterMap(uint32_t aCh);
+
+ // Font's unitsPerEm from the 'head' table, if available (will be set to
+ // kInvalidUPEM for non-sfnt font formats)
+ uint16_t mUnitsPerEm;
+
+ // Shaper-specific face objects, shared by all instantiations of the same
+ // physical font, regardless of size.
+ // Usually, only one of these will actually be created for any given font
+ // entry, depending on the font tables that are present.
+
+ // hb_face_t is refcounted internally, so each shaper that's using it will
+ // bump the ref count when it acquires the face, and "destroy" (release) it
+ // in its destructor. The font entry has only this non-owning reference to
+ // the face; when the face is deleted, it will tell the font entry to forget
+ // it, so that a new face will be created next time it is needed.
+ hb_face_t* mHBFace;
+
+ static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData);
+
+ // Callback that the hb_face will use to tell us when it is being deleted.
+ static void HBFaceDeletedCallback(void *aUserData);
+
+ // gr_face is -not- refcounted, so it will be owned directly by the font
+ // entry, and we'll keep a count of how many references we've handed out;
+ // each shaper is responsible to call ReleaseGrFace on its entry when
+ // finished with it, so that we know when it can be deleted.
+ gr_face* mGrFace;
+
+ // hashtable to map raw table data ptr back to its owning blob, for use by
+ // graphite table-release callback
+ nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap;
+
+ // number of current users of this entry's mGrFace
+ nsrefcnt mGrFaceRefCnt;
+
+ static const void* GrGetTable(const void *aAppFaceHandle,
+ unsigned int aName,
+ size_t *aLen);
+ static void GrReleaseTable(const void *aAppFaceHandle,
+ const void *aTableBuffer);
+
+ // For memory reporting: size of user-font data belonging to this entry.
+ // We record this in the font entry because the actual data block may be
+ // handed over to platform APIs, so that it would become difficult (and
+ // platform-specific) to measure it directly at report-gathering time.
+ uint32_t mComputedSizeOfUserFont;
+
+private:
+ /**
+ * Font table hashtable, to support GetFontTable for harfbuzz.
+ *
+ * The harfbuzz shaper (and potentially other clients) needs access to raw
+ * font table data. This needs to be cached so that it can be used
+ * repeatedly (each time we construct a text run; in some cases, for
+ * each character/glyph within the run) without re-fetching large tables
+ * every time.
+ *
+ * Because we may instantiate many gfxFonts for the same physical font
+ * file (at different sizes), we should ensure that they can share a
+ * single cached copy of the font tables. To do this, we implement table
+ * access and sharing on the fontEntry rather than the font itself.
+ *
+ * The default implementation uses GetFontTable() to read font table
+ * data into byte arrays, and wraps them in blobs which are registered in
+ * a hashtable. The hashtable can then return pre-existing blobs to
+ * harfbuzz.
+ *
+ * Harfbuzz will "destroy" the blobs when it is finished with them. When
+ * the last blob reference is removed, the FontTableBlobData user data
+ * will remove the blob from the hashtable if still registered.
+ */
+
+ class FontTableBlobData;
+
+ /**
+ * FontTableHashEntry manages the entries of hb_blob_t's containing font
+ * table data.
+ *
+ * This is used to share font tables across fonts with the same
+ * font entry (but different sizes) for use by HarfBuzz. The hashtable
+ * does not own a strong reference to the blob, but keeps a weak pointer,
+ * managed by FontTableBlobData. Similarly FontTableBlobData keeps only a
+ * weak pointer to the hashtable, managed by FontTableHashEntry.
+ */
+
+ class FontTableHashEntry : public nsUint32HashKey
+ {
+ public:
+ // Declarations for nsTHashtable
+
+ typedef nsUint32HashKey KeyClass;
+ typedef KeyClass::KeyType KeyType;
+ typedef KeyClass::KeyTypePointer KeyTypePointer;
+
+ explicit FontTableHashEntry(KeyTypePointer aTag)
+ : KeyClass(aTag)
+ , mSharedBlobData(nullptr)
+ , mBlob(nullptr)
+ { }
+
+ // NOTE: This assumes the new entry belongs to the same hashtable as
+ // the old, because the mHashtable pointer in mSharedBlobData (if
+ // present) will not be updated.
+ FontTableHashEntry(FontTableHashEntry&& toMove)
+ : KeyClass(mozilla::Move(toMove))
+ , mSharedBlobData(mozilla::Move(toMove.mSharedBlobData))
+ , mBlob(mozilla::Move(toMove.mBlob))
+ {
+ toMove.mSharedBlobData = nullptr;
+ toMove.mBlob = nullptr;
+ }
+
+ ~FontTableHashEntry() { Clear(); }
+
+ // FontTable/Blob API
+
+ // Transfer (not copy) elements of aTable to a new hb_blob_t and
+ // return ownership to the caller. A weak reference to the blob is
+ // recorded in the hashtable entry so that others may use the same
+ // table.
+ hb_blob_t *
+ ShareTableAndGetBlob(nsTArray<uint8_t>&& aTable,
+ nsTHashtable<FontTableHashEntry> *aHashtable);
+
+ // Return a strong reference to the blob.
+ // Callers must hb_blob_destroy the returned blob.
+ hb_blob_t *GetBlob() const;
+
+ void Clear();
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ private:
+ static void DeleteFontTableBlobData(void *aBlobData);
+ // not implemented
+ FontTableHashEntry& operator=(FontTableHashEntry& toCopy);
+
+ FontTableBlobData *mSharedBlobData;
+ hb_blob_t *mBlob;
+ };
+
+ mozilla::UniquePtr<nsTHashtable<FontTableHashEntry> > mFontTableCache;
+
+ gfxFontEntry(const gfxFontEntry&);
+ gfxFontEntry& operator=(const gfxFontEntry&);
+};
+
+
+// used when iterating over all fonts looking for a match for a given character
+struct GlobalFontMatch {
+ GlobalFontMatch(const uint32_t aCharacter,
+ mozilla::unicode::Script aRunScript,
+ const gfxFontStyle *aStyle) :
+ mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle),
+ mMatchRank(0), mCount(0), mCmapsTested(0)
+ {
+
+ }
+
+ const uint32_t mCh; // codepoint to be matched
+ mozilla::unicode::Script mRunScript; // Unicode script for the codepoint
+ const gfxFontStyle* mStyle; // style to match
+ int32_t mMatchRank; // metric indicating closest match
+ RefPtr<gfxFontEntry> mBestMatch; // current best match
+ RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
+ uint32_t mCount; // number of fonts matched
+ uint32_t mCmapsTested; // number of cmaps tested
+};
+
+class gfxFontFamily {
+public:
+ NS_INLINE_DECL_REFCOUNTING(gfxFontFamily)
+
+ explicit gfxFontFamily(const nsAString& aName) :
+ mName(aName),
+ mOtherFamilyNamesInitialized(false),
+ mHasOtherFamilyNames(false),
+ mFaceNamesInitialized(false),
+ mHasStyles(false),
+ mIsSimpleFamily(false),
+ mIsBadUnderlineFamily(false),
+ mFamilyCharacterMapInitialized(false),
+ mSkipDefaultFeatureSpaceCheck(false),
+ mCheckForFallbackFaces(false)
+ { }
+
+ const nsString& Name() { return mName; }
+
+ virtual void LocalizedName(nsAString& aLocalizedName);
+ virtual bool HasOtherFamilyNames();
+
+ nsTArray<RefPtr<gfxFontEntry> >& GetFontList() { return mAvailableFonts; }
+
+ void AddFontEntry(RefPtr<gfxFontEntry> aFontEntry) {
+ // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces
+ // of Times New Roman, because of buggy table in those fonts
+ if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() &&
+ Name().EqualsLiteral("Times New Roman"))
+ {
+ aFontEntry->mIgnoreGDEF = true;
+ }
+ if (aFontEntry->mFamilyName.IsEmpty()) {
+ aFontEntry->mFamilyName = Name();
+ } else {
+ MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name()));
+ }
+ aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck;
+ mAvailableFonts.AppendElement(aFontEntry);
+ }
+
+ // note that the styles for this family have been added
+ bool HasStyles() { return mHasStyles; }
+ void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
+
+ // choose a specific face to match a style using CSS font matching
+ // rules (weight matching occurs here). may return a face that doesn't
+ // precisely match (e.g. normal face when no italic face exists).
+ // aNeedsSyntheticBold is set to true when synthetic bolding is
+ // needed, false otherwise
+ gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle,
+ bool& aNeedsSyntheticBold);
+
+ virtual void
+ FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
+ nsTArray<gfxFontEntry*>& aFontEntryList,
+ bool& aNeedsSyntheticBold);
+
+ // checks for a matching font within the family
+ // used as part of the font fallback process
+ void FindFontForChar(GlobalFontMatch *aMatchData);
+
+ // checks all fonts for a matching font within the family
+ void SearchAllFontsForChar(GlobalFontMatch *aMatchData);
+
+ // read in other family names, if any, and use functor to add each into cache
+ virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
+
+ // helper method for reading localized family names from the name table
+ // of a single face
+ static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
+ const char *aNameData,
+ uint32_t aDataLength,
+ nsTArray<nsString>& aOtherFamilyNames,
+ bool useFullName);
+
+ // set when other family names have been read in
+ void SetOtherFamilyNamesInitialized() {
+ mOtherFamilyNamesInitialized = true;
+ }
+
+ // read in other localized family names, fullnames and Postscript names
+ // for all faces and append to lookup tables
+ virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
+ bool aNeedFullnamePostscriptNames,
+ FontInfoData *aFontInfoData = nullptr);
+
+ // find faces belonging to this family (platform implementations override this;
+ // should be made pure virtual once all subclasses have been updated)
+ virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { }
+
+ // search for a specific face using the Postscript name
+ gfxFontEntry* FindFont(const nsAString& aPostscriptName);
+
+ // read in cmaps for all the faces
+ void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr);
+
+ bool TestCharacterMap(uint32_t aCh) {
+ if (!mFamilyCharacterMapInitialized) {
+ ReadAllCMAPs();
+ }
+ return mFamilyCharacterMap.test(aCh);
+ }
+
+ void ResetCharacterMap() {
+ mFamilyCharacterMap.reset();
+ mFamilyCharacterMapInitialized = false;
+ }
+
+ // mark this family as being in the "bad" underline offset blacklist
+ void SetBadUnderlineFamily() {
+ mIsBadUnderlineFamily = true;
+ if (mHasStyles) {
+ SetBadUnderlineFonts();
+ }
+ }
+
+ bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
+ bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
+
+ // sort available fonts to put preferred (standard) faces towards the end
+ void SortAvailableFonts();
+
+ // check whether the family fits into the simple 4-face model,
+ // so we can use simplified style-matching;
+ // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
+ void CheckForSimpleFamily();
+
+ // For memory reporter
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+#ifdef DEBUG
+ // Only used for debugging checks - does a linear search
+ bool ContainsFace(gfxFontEntry* aFontEntry);
+#endif
+
+ void SetSkipSpaceFeatureCheck(bool aSkipCheck) {
+ mSkipDefaultFeatureSpaceCheck = aSkipCheck;
+ }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxFontFamily()
+ {
+ }
+
+ bool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
+ hb_blob_t *aNameTable,
+ bool useFullName = false);
+
+ // set whether this font family is in "bad" underline offset blacklist.
+ void SetBadUnderlineFonts() {
+ uint32_t i, numFonts = mAvailableFonts.Length();
+ for (i = 0; i < numFonts; i++) {
+ if (mAvailableFonts[i]) {
+ mAvailableFonts[i]->mIsBadUnderlineFont = true;
+ }
+ }
+ }
+
+ nsString mName;
+ nsTArray<RefPtr<gfxFontEntry> > mAvailableFonts;
+ gfxSparseBitSet mFamilyCharacterMap;
+ bool mOtherFamilyNamesInitialized : 1;
+ bool mHasOtherFamilyNames : 1;
+ bool mFaceNamesInitialized : 1;
+ bool mHasStyles : 1;
+ bool mIsSimpleFamily : 1;
+ bool mIsBadUnderlineFamily : 1;
+ bool mFamilyCharacterMapInitialized : 1;
+ bool mSkipDefaultFeatureSpaceCheck : 1;
+ bool mCheckForFallbackFaces : 1; // check other faces for character
+
+ enum {
+ // for "simple" families, the faces are stored in mAvailableFonts
+ // with fixed positions:
+ kRegularFaceIndex = 0,
+ kBoldFaceIndex = 1,
+ kItalicFaceIndex = 2,
+ kBoldItalicFaceIndex = 3,
+ // mask values for selecting face with bold and/or italic attributes
+ kBoldMask = 0x01,
+ kItalicMask = 0x02
+ };
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxFontFamilyList.h b/system/graphics/thebes/gfxFontFamilyList.h
new file mode 100644
index 000000000..9d1a3cf49
--- /dev/null
+++ b/system/graphics/thebes/gfxFontFamilyList.h
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_FAMILY_LIST_H
+#define GFX_FONT_FAMILY_LIST_H
+
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
+
+namespace mozilla {
+
+/**
+ * type of font family name, either a name (e.g. Helvetica) or a
+ * generic (e.g. serif, sans-serif), with the ability to distinguish
+ * between unquoted and quoted names for serializaiton
+ */
+
+enum FontFamilyType : uint32_t {
+ eFamily_none = 0, // used when finding generics
+
+ // explicitly named font family (e.g. Helvetica)
+ eFamily_named,
+ eFamily_named_quoted,
+
+ // generics
+ eFamily_serif, // pref font code relies on this ordering!!!
+ eFamily_sans_serif,
+ eFamily_monospace,
+ eFamily_cursive,
+ eFamily_fantasy,
+
+ // special
+ eFamily_moz_variable,
+ eFamily_moz_fixed,
+ eFamily_moz_emoji,
+
+ eFamily_generic_first = eFamily_serif,
+ eFamily_generic_last = eFamily_fantasy,
+ eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1)
+};
+
+enum QuotedName { eQuotedName, eUnquotedName };
+
+/**
+ * font family name, a string for the name if not a generic and
+ * a font type indicated named family or which generic family
+ */
+
+struct FontFamilyName final {
+ FontFamilyName()
+ : mType(eFamily_named)
+ {}
+
+ // named font family - e.g. Helvetica
+ explicit FontFamilyName(const nsAString& aFamilyName,
+ QuotedName aQuoted = eUnquotedName) {
+ mType = (aQuoted == eQuotedName) ? eFamily_named_quoted : eFamily_named;
+ mName = aFamilyName;
+ }
+
+ // generic font family - e.g. sans-serif
+ explicit FontFamilyName(FontFamilyType aType) {
+ NS_ASSERTION(aType != eFamily_named &&
+ aType != eFamily_named_quoted &&
+ aType != eFamily_none,
+ "expected a generic font type");
+ mName.Truncate();
+ mType = aType;
+ }
+
+ FontFamilyName(const FontFamilyName& aCopy) {
+ mType = aCopy.mType;
+ mName = aCopy.mName;
+ }
+
+ bool IsNamed() const {
+ return mType == eFamily_named || mType == eFamily_named_quoted;
+ }
+
+ bool IsGeneric() const {
+ return !IsNamed();
+ }
+
+ void AppendToString(nsAString& aFamilyList, bool aQuotes = true) const {
+ switch (mType) {
+ case eFamily_named:
+ aFamilyList.Append(mName);
+ break;
+ case eFamily_named_quoted:
+ if (aQuotes) {
+ aFamilyList.Append('"');
+ }
+ aFamilyList.Append(mName);
+ if (aQuotes) {
+ aFamilyList.Append('"');
+ }
+ break;
+ case eFamily_serif:
+ aFamilyList.AppendLiteral("serif");
+ break;
+ case eFamily_sans_serif:
+ aFamilyList.AppendLiteral("sans-serif");
+ break;
+ case eFamily_monospace:
+ aFamilyList.AppendLiteral("monospace");
+ break;
+ case eFamily_cursive:
+ aFamilyList.AppendLiteral("cursive");
+ break;
+ case eFamily_fantasy:
+ aFamilyList.AppendLiteral("fantasy");
+ break;
+ case eFamily_moz_fixed:
+ aFamilyList.AppendLiteral("-moz-fixed");
+ break;
+ default:
+ break;
+ }
+ }
+
+ // helper method that converts generic names to the right enum value
+ static FontFamilyName
+ Convert(const nsAString& aFamilyOrGenericName) {
+ // should only be passed a single font - not entirely correct, a family
+ // *could* have a comma in it but in practice never does so
+ // for debug purposes this is fine
+ NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1,
+ "Convert method should only be passed a single family name");
+
+ FontFamilyType genericType = eFamily_none;
+ if (aFamilyOrGenericName.LowerCaseEqualsLiteral("serif")) {
+ genericType = eFamily_serif;
+ } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("sans-serif")) {
+ genericType = eFamily_sans_serif;
+ } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("monospace")) {
+ genericType = eFamily_monospace;
+ } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("cursive")) {
+ genericType = eFamily_cursive;
+ } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("fantasy")) {
+ genericType = eFamily_fantasy;
+ } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("-moz-fixed")) {
+ genericType = eFamily_moz_fixed;
+ } else {
+ return FontFamilyName(aFamilyOrGenericName, eUnquotedName);
+ }
+
+ return FontFamilyName(genericType);
+ }
+
+ // memory reporting
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ FontFamilyType mType;
+ nsString mName; // empty if mType != eFamily_named
+};
+
+inline bool
+operator==(const FontFamilyName& a, const FontFamilyName& b) {
+ return a.mType == b.mType && a.mName == b.mName;
+}
+
+/**
+ * font family list, array of font families and a default font type.
+ * font family names are either named strings or generics. the default
+ * font type is used to preserve the variable font fallback behavior
+ */
+
+class FontFamilyList {
+public:
+ FontFamilyList()
+ : mDefaultFontType(eFamily_none)
+ {
+ }
+
+ explicit FontFamilyList(FontFamilyType aGenericType)
+ : mDefaultFontType(eFamily_none)
+ {
+ Append(FontFamilyName(aGenericType));
+ }
+
+ FontFamilyList(const nsAString& aFamilyName,
+ QuotedName aQuoted)
+ : mDefaultFontType(eFamily_none)
+ {
+ Append(FontFamilyName(aFamilyName, aQuoted));
+ }
+
+ FontFamilyList(const FontFamilyList& aOther)
+ : mFontlist(aOther.mFontlist)
+ , mDefaultFontType(aOther.mDefaultFontType)
+ {
+ }
+
+ void Append(const FontFamilyName& aFamilyName) {
+ mFontlist.AppendElement(aFamilyName);
+ }
+
+ void Append(const nsTArray<nsString>& aFamilyNameList) {
+ uint32_t len = aFamilyNameList.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ mFontlist.AppendElement(FontFamilyName(aFamilyNameList[i],
+ eUnquotedName));
+ }
+ }
+
+ void Clear() {
+ mFontlist.Clear();
+ }
+
+ uint32_t Length() const {
+ return mFontlist.Length();
+ }
+
+ bool IsEmpty() const {
+ return mFontlist.IsEmpty();
+ }
+
+ const nsTArray<FontFamilyName>& GetFontlist() const {
+ return mFontlist;
+ }
+
+ bool Equals(const FontFamilyList& aFontlist) const {
+ return mFontlist == aFontlist.mFontlist &&
+ mDefaultFontType == aFontlist.mDefaultFontType;
+ }
+
+ FontFamilyType FirstGeneric() const {
+ uint32_t len = mFontlist.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ const FontFamilyName& name = mFontlist[i];
+ if (name.IsGeneric()) {
+ return name.mType;
+ }
+ }
+ return eFamily_none;
+ }
+
+ bool HasGeneric() const {
+ return FirstGeneric() != eFamily_none;
+ }
+
+ bool HasDefaultGeneric() const {
+ uint32_t len = mFontlist.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ const FontFamilyName& name = mFontlist[i];
+ if (name.mType == mDefaultFontType) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Find the first generic (but ignoring cursive and fantasy, as they are
+ // rarely configured in any useful way) in the list.
+ // If found, move it to the start and return true; else return false.
+ bool PrioritizeFirstGeneric() {
+ uint32_t len = mFontlist.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ const FontFamilyName name = mFontlist[i];
+ if (name.IsGeneric()) {
+ if (name.mType == eFamily_cursive ||
+ name.mType == eFamily_fantasy) {
+ continue;
+ }
+ if (i > 0) {
+ mFontlist.RemoveElementAt(i);
+ mFontlist.InsertElementAt(0, name);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void PrependGeneric(FontFamilyType aType) {
+ mFontlist.InsertElementAt(0, FontFamilyName(aType));
+ }
+
+ void ToString(nsAString& aFamilyList,
+ bool aQuotes = true,
+ bool aIncludeDefault = false) const {
+ aFamilyList.Truncate();
+ uint32_t len = mFontlist.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ if (i != 0) {
+ aFamilyList.Append(',');
+ }
+ const FontFamilyName& name = mFontlist[i];
+ name.AppendToString(aFamilyList, aQuotes);
+ }
+ if (aIncludeDefault && mDefaultFontType != eFamily_none) {
+ if (!aFamilyList.IsEmpty()) {
+ aFamilyList.Append(',');
+ }
+ if (mDefaultFontType == eFamily_serif) {
+ aFamilyList.AppendLiteral("serif");
+ } else {
+ aFamilyList.AppendLiteral("sans-serif");
+ }
+ }
+ }
+
+ // searches for a specific non-generic name, lowercase comparison
+ bool Contains(const nsAString& aFamilyName) const {
+ uint32_t len = mFontlist.Length();
+ nsAutoString fam(aFamilyName);
+ ToLowerCase(fam);
+ for (uint32_t i = 0; i < len; i++) {
+ const FontFamilyName& name = mFontlist[i];
+ if (name.mType != eFamily_named &&
+ name.mType != eFamily_named_quoted) {
+ continue;
+ }
+ nsAutoString listname(name.mName);
+ ToLowerCase(listname);
+ if (listname.Equals(fam)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ FontFamilyType GetDefaultFontType() const { return mDefaultFontType; }
+ void SetDefaultFontType(FontFamilyType aType) {
+ NS_ASSERTION(aType == eFamily_none || aType == eFamily_serif ||
+ aType == eFamily_sans_serif,
+ "default font type must be either serif or sans-serif");
+ mDefaultFontType = aType;
+ }
+
+ // memory reporting
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ size_t n = 0;
+ n += mFontlist.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (size_t i = 0; i < mFontlist.Length(); i++) {
+ n += mFontlist[i].SizeOfExcludingThis(aMallocSizeOf);
+ }
+ return n;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ nsTArray<FontFamilyName> mFontlist;
+ FontFamilyType mDefaultFontType; // none, serif or sans-serif
+};
+
+inline bool
+operator==(const FontFamilyList& a, const FontFamilyList& b) {
+ return a.Equals(b);
+}
+
+} // namespace mozilla
+
+#endif /* GFX_FONT_FAMILY_LIST_H */
diff --git a/system/graphics/thebes/gfxFontFeatures.cpp b/system/graphics/thebes/gfxFontFeatures.cpp
new file mode 100644
index 000000000..dcc5e369a
--- /dev/null
+++ b/system/graphics/thebes/gfxFontFeatures.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFontFeatures.h"
+#include "nsUnicharUtils.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla;
+
+gfxFontFeatureValueSet::gfxFontFeatureValueSet()
+ : mFontFeatureValues(8)
+{
+}
+
+bool
+gfxFontFeatureValueSet::GetFontFeatureValuesFor(const nsAString& aFamily,
+ uint32_t aVariantProperty,
+ const nsAString& aName,
+ nsTArray<uint32_t>& aValues)
+{
+ nsAutoString family(aFamily), name(aName);
+ ToLowerCase(family);
+ ToLowerCase(name);
+ FeatureValueHashKey key(family, aVariantProperty, name);
+
+ aValues.Clear();
+ FeatureValueHashEntry *entry = mFontFeatureValues.GetEntry(key);
+ if (entry) {
+ NS_ASSERTION(entry->mValues.Length() > 0,
+ "null array of font feature values");
+ aValues.AppendElements(entry->mValues);
+ return true;
+ }
+
+ return false;
+}
+
+
+void
+gfxFontFeatureValueSet::AddFontFeatureValues(const nsAString& aFamily,
+ const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aValues)
+{
+ nsAutoString family(aFamily);
+ ToLowerCase(family);
+
+ uint32_t i, numFeatureValues = aValues.Length();
+ for (i = 0; i < numFeatureValues; i++) {
+ const FeatureValues& fv = aValues.ElementAt(i);
+ uint32_t alternate = fv.alternate;
+ uint32_t j, numValues = fv.valuelist.Length();
+ for (j = 0; j < numValues; j++) {
+ const ValueList& v = fv.valuelist.ElementAt(j);
+ nsAutoString name(v.name);
+ ToLowerCase(name);
+ FeatureValueHashKey key(family, alternate, name);
+ FeatureValueHashEntry *entry = mFontFeatureValues.PutEntry(key);
+ entry->mKey = key;
+ entry->mValues = v.featureSelectors;
+ }
+ }
+}
+
+bool
+gfxFontFeatureValueSet::FeatureValueHashEntry::KeyEquals(
+ const KeyTypePointer aKey) const
+{
+ return aKey->mPropVal == mKey.mPropVal &&
+ aKey->mFamily.Equals(mKey.mFamily) &&
+ aKey->mName.Equals(mKey.mName);
+}
+
+PLDHashNumber
+gfxFontFeatureValueSet::FeatureValueHashEntry::HashKey(
+ const KeyTypePointer aKey)
+{
+ return HashString(aKey->mFamily) + HashString(aKey->mName) +
+ aKey->mPropVal * uint32_t(0xdeadbeef);
+}
+
diff --git a/system/graphics/thebes/gfxFontFeatures.h b/system/graphics/thebes/gfxFontFeatures.h
new file mode 100644
index 000000000..121a001b2
--- /dev/null
+++ b/system/graphics/thebes/gfxFontFeatures.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_FEATURES_H
+#define GFX_FONT_FEATURES_H
+
+#include "nsTHashtable.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+// An OpenType feature tag and value pair
+struct gfxFontFeature {
+ uint32_t mTag; // see http://www.microsoft.com/typography/otspec/featuretags.htm
+ uint32_t mValue; // 0 = off, 1 = on, larger values may be used as parameters
+ // to features that select among multiple alternatives
+};
+
+inline bool
+operator<(const gfxFontFeature& a, const gfxFontFeature& b)
+{
+ return (a.mTag < b.mTag) || ((a.mTag == b.mTag) && (a.mValue < b.mValue));
+}
+
+inline bool
+operator==(const gfxFontFeature& a, const gfxFontFeature& b)
+{
+ return (a.mTag == b.mTag) && (a.mValue == b.mValue);
+}
+
+struct gfxAlternateValue {
+ uint32_t alternate; // constants in gfxFontConstants.h
+ nsString value; // string value to be looked up
+};
+
+inline bool
+operator<(const gfxAlternateValue& a, const gfxAlternateValue& b)
+{
+ return (a.alternate < b.alternate) ||
+ ((a.alternate == b.alternate) && (a.value < b.value));
+}
+
+inline bool
+operator==(const gfxAlternateValue& a, const gfxAlternateValue& b)
+{
+ return (a.alternate == b.alternate) && (a.value == b.value);
+}
+
+class gfxFontFeatureValueSet final {
+public:
+ NS_INLINE_DECL_REFCOUNTING(gfxFontFeatureValueSet)
+
+ gfxFontFeatureValueSet();
+
+ struct ValueList {
+ ValueList(const nsAString& aName, const nsTArray<uint32_t>& aSelectors)
+ : name(aName), featureSelectors(aSelectors)
+ {}
+ nsString name;
+ nsTArray<uint32_t> featureSelectors;
+ };
+
+ struct FeatureValues {
+ uint32_t alternate;
+ nsTArray<ValueList> valuelist;
+ };
+
+ // returns true if found, false otherwise
+ bool
+ GetFontFeatureValuesFor(const nsAString& aFamily,
+ uint32_t aVariantProperty,
+ const nsAString& aName,
+ nsTArray<uint32_t>& aValues);
+ void
+ AddFontFeatureValues(const nsAString& aFamily,
+ const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aValues);
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~gfxFontFeatureValueSet() {}
+
+ struct FeatureValueHashKey {
+ nsString mFamily;
+ uint32_t mPropVal;
+ nsString mName;
+
+ FeatureValueHashKey()
+ : mPropVal(0)
+ { }
+ FeatureValueHashKey(const nsAString& aFamily,
+ uint32_t aPropVal,
+ const nsAString& aName)
+ : mFamily(aFamily), mPropVal(aPropVal), mName(aName)
+ { }
+ FeatureValueHashKey(const FeatureValueHashKey& aKey)
+ : mFamily(aKey.mFamily), mPropVal(aKey.mPropVal), mName(aKey.mName)
+ { }
+ };
+
+ class FeatureValueHashEntry : public PLDHashEntryHdr {
+ public:
+ typedef const FeatureValueHashKey &KeyType;
+ typedef const FeatureValueHashKey *KeyTypePointer;
+
+ explicit FeatureValueHashEntry(KeyTypePointer aKey) { }
+ FeatureValueHashEntry(const FeatureValueHashEntry& toCopy)
+ {
+ NS_ERROR("Should not be called");
+ }
+ ~FeatureValueHashEntry() { }
+
+ bool KeyEquals(const KeyTypePointer aKey) const;
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey);
+ enum { ALLOW_MEMMOVE = true };
+
+ FeatureValueHashKey mKey;
+ nsTArray<uint32_t> mValues;
+ };
+
+ nsTHashtable<FeatureValueHashEntry> mFontFeatureValues;
+ };
+
+#endif
diff --git a/system/graphics/thebes/gfxFontInfoLoader.cpp b/system/graphics/thebes/gfxFontInfoLoader.cpp
new file mode 100644
index 000000000..c9abef2ea
--- /dev/null
+++ b/system/graphics/thebes/gfxFontInfoLoader.cpp
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFontInfoLoader.h"
+#include "nsCRT.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h" // for nsRunnable
+#include "gfxPlatformFontList.h"
+#include "mozilla/gfx/Logging.h"
+#ifdef XP_WIN
+#include <d3d11.h>
+#endif
+
+using namespace mozilla;
+using services::GetObserverService;
+
+#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug)
+
+void
+FontInfoData::Load()
+{
+ TimeStamp start = TimeStamp::Now();
+
+ uint32_t i, n = mFontFamiliesToLoad.Length();
+ mLoadStats.families = n;
+ for (i = 0; i < n && !mCanceled; i++) {
+ // font file memory mapping sometimes causes exceptions - bug 1100949
+ MOZ_SEH_TRY {
+ LoadFontFamilyData(mFontFamiliesToLoad[i]);
+ } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalError() <<
+ "Exception occurred reading font data for " <<
+ NS_ConvertUTF16toUTF8(mFontFamiliesToLoad[i]).get();
+ }
+ }
+
+ mLoadTime = TimeStamp::Now() - start;
+}
+
+class FontInfoLoadCompleteEvent : public Runnable {
+ virtual ~FontInfoLoadCompleteEvent() {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ explicit FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) :
+ mFontInfo(aFontInfo)
+ {}
+
+ NS_IMETHOD Run() override;
+
+ RefPtr<FontInfoData> mFontInfo;
+};
+
+class AsyncFontInfoLoader : public Runnable {
+ virtual ~AsyncFontInfoLoader() {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ explicit AsyncFontInfoLoader(FontInfoData *aFontInfo) :
+ mFontInfo(aFontInfo)
+ {
+ mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
+ }
+
+ NS_IMETHOD Run() override;
+
+ RefPtr<FontInfoData> mFontInfo;
+ RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
+};
+
+class ShutdownThreadEvent : public Runnable {
+ virtual ~ShutdownThreadEvent() {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ explicit ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
+ NS_IMETHOD Run() override {
+ mThread->Shutdown();
+ return NS_OK;
+ }
+ nsCOMPtr<nsIThread> mThread;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(ShutdownThreadEvent, Runnable);
+
+// runs on main thread after async font info loading is done
+nsresult
+FontInfoLoadCompleteEvent::Run()
+{
+ gfxFontInfoLoader *loader =
+ static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
+
+ loader->FinalizeLoader(mFontInfo);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(FontInfoLoadCompleteEvent, Runnable);
+
+// runs on separate thread
+nsresult
+AsyncFontInfoLoader::Run()
+{
+ // load platform-specific font info
+ mFontInfo->Load();
+
+ // post a completion event that transfer the data to the fontlist
+ NS_DispatchToMainThread(mCompleteEvent);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(AsyncFontInfoLoader, Runnable);
+
+NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData)
+{
+ if (!nsCRT::strcmp(aTopic, "quit-application")) {
+ mLoader->CancelLoader();
+ } else {
+ NS_NOTREACHED("unexpected notification topic");
+ }
+ return NS_OK;
+}
+
+void
+gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
+{
+ mInterval = aInterval;
+
+ NS_ASSERTION(!mFontInfo,
+ "fontinfo should be null when starting font loader");
+
+ // sanity check
+ if (mState != stateInitial &&
+ mState != stateTimerOff &&
+ mState != stateTimerOnDelay) {
+ CancelLoader();
+ }
+
+ // set up timer
+ if (!mTimer) {
+ mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mTimer) {
+ NS_WARNING("Failure to create font info loader timer");
+ return;
+ }
+ }
+
+ AddShutdownObserver();
+
+ // delay? ==> start async thread after a delay
+ if (aDelay) {
+ mState = stateTimerOnDelay;
+ mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay,
+ nsITimer::TYPE_ONE_SHOT);
+ return;
+ }
+
+ mFontInfo = CreateFontInfoData();
+
+ // initialize
+ InitLoader();
+
+ // start async load
+ nsresult rv = NS_NewNamedThread("Font Loader",
+ getter_AddRefs(mFontLoaderThread),
+ nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ mState = stateAsyncLoad;
+
+ nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
+
+ mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL);
+
+ if (LOG_FONTINIT_ENABLED()) {
+ LOG_FONTINIT(("(fontinit) fontloader started (fontinfo: %p)\n",
+ mFontInfo.get()));
+ }
+}
+
+void
+gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo)
+{
+ // Avoid loading data if loader has already been canceled.
+ // This should mean that CancelLoader() ran and the Load
+ // thread has already Shutdown(), and likely before processing
+ // the Shutdown event it handled the load event and sent back
+ // our Completion event, thus we end up here.
+ if (mState != stateAsyncLoad || mFontInfo != aFontInfo) {
+ return;
+ }
+
+ mLoadTime = mFontInfo->mLoadTime;
+
+ // try to load all font data immediately
+ if (LoadFontInfo()) {
+ CancelLoader();
+ return;
+ }
+
+ // not all work completed ==> run load on interval
+ mState = stateTimerOnInterval;
+ mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval,
+ nsITimer::TYPE_REPEATING_SLACK);
+}
+
+void
+gfxFontInfoLoader::CancelLoader()
+{
+ if (mState == stateInitial) {
+ return;
+ }
+ mState = stateTimerOff;
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ if (mFontInfo) // null during any initial delay
+ mFontInfo->mCanceled = true;
+ if (mFontLoaderThread) {
+ NS_DispatchToMainThread(new ShutdownThreadEvent(mFontLoaderThread));
+ mFontLoaderThread = nullptr;
+ }
+ RemoveShutdownObserver();
+ CleanupLoader();
+}
+
+void
+gfxFontInfoLoader::LoadFontInfoTimerFire()
+{
+ if (mState == stateTimerOnDelay) {
+ mState = stateTimerOnInterval;
+ mTimer->SetDelay(mInterval);
+ }
+
+ bool done = LoadFontInfo();
+ if (done) {
+ CancelLoader();
+ }
+}
+
+gfxFontInfoLoader::~gfxFontInfoLoader()
+{
+ RemoveShutdownObserver();
+ MOZ_COUNT_DTOR(gfxFontInfoLoader);
+}
+
+void
+gfxFontInfoLoader::AddShutdownObserver()
+{
+ if (mObserver) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) {
+ mObserver = new ShutdownObserver(this);
+ obs->AddObserver(mObserver, "quit-application", false);
+ }
+}
+
+void
+gfxFontInfoLoader::RemoveShutdownObserver()
+{
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(mObserver, "quit-application");
+ mObserver = nullptr;
+ }
+ }
+}
diff --git a/system/graphics/thebes/gfxFontInfoLoader.h b/system/graphics/thebes/gfxFontInfoLoader.h
new file mode 100644
index 000000000..40b655aa5
--- /dev/null
+++ b/system/graphics/thebes/gfxFontInfoLoader.h
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_INFO_LOADER_H
+#define GFX_FONT_INFO_LOADER_H
+
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsIThread.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "gfxFont.h"
+#include "nsIRunnable.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/TimeStamp.h"
+#include "nsISupportsImpl.h"
+
+// data retrieved for a given face
+
+struct FontFaceData {
+ FontFaceData() : mUVSOffset(0), mSymbolFont(false) {}
+
+ FontFaceData(const FontFaceData& aFontFaceData) {
+ mFullName = aFontFaceData.mFullName;
+ mPostscriptName = aFontFaceData.mPostscriptName;
+ mCharacterMap = aFontFaceData.mCharacterMap;
+ mUVSOffset = aFontFaceData.mUVSOffset;
+ mSymbolFont = aFontFaceData.mSymbolFont;
+ }
+
+ nsString mFullName;
+ nsString mPostscriptName;
+ RefPtr<gfxCharacterMap> mCharacterMap;
+ uint32_t mUVSOffset;
+ bool mSymbolFont;
+};
+
+// base class used to contain cached system-wide font info.
+// methods in this class are called on off-main threads so
+// all methods use only static methods or other thread-safe
+// font data access API's. specifically, no use is made of
+// gfxPlatformFontList, gfxFontFamily, gfxFamily or any
+// harfbuzz API methods within FontInfoData subclasses.
+
+class FontInfoData {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontInfoData)
+
+ FontInfoData(bool aLoadOtherNames,
+ bool aLoadFaceNames,
+ bool aLoadCmaps) :
+ mCanceled(false),
+ mLoadOtherNames(aLoadOtherNames),
+ mLoadFaceNames(aLoadFaceNames),
+ mLoadCmaps(aLoadCmaps)
+ {
+ MOZ_COUNT_CTOR(FontInfoData);
+ }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~FontInfoData() {
+ MOZ_COUNT_DTOR(FontInfoData);
+ }
+
+public:
+ virtual void Load();
+
+ // loads font data for all fonts of a given family
+ // (called on async thread)
+ virtual void LoadFontFamilyData(const nsAString& aFamilyName) = 0;
+
+ // -- methods overriden by platform-specific versions --
+
+ // fetches cmap data for a particular font from cached font data
+ virtual already_AddRefed<gfxCharacterMap>
+ GetCMAP(const nsAString& aFontName,
+ uint32_t& aUVSOffset,
+ bool& aSymbolFont)
+ {
+ FontFaceData faceData;
+ if (!mFontFaceData.Get(aFontName, &faceData) ||
+ !faceData.mCharacterMap) {
+ return nullptr;
+ }
+
+ aUVSOffset = faceData.mUVSOffset;
+ aSymbolFont = faceData.mSymbolFont;
+ RefPtr<gfxCharacterMap> cmap = faceData.mCharacterMap;
+ return cmap.forget();
+ }
+
+ // fetches fullname/postscript names from cached font data
+ virtual void GetFaceNames(const nsAString& aFontName,
+ nsAString& aFullName,
+ nsAString& aPostscriptName)
+ {
+ FontFaceData faceData;
+ if (!mFontFaceData.Get(aFontName, &faceData)) {
+ return;
+ }
+
+ aFullName = faceData.mFullName;
+ aPostscriptName = faceData.mPostscriptName;
+ }
+
+ // fetches localized family name data from cached font data
+ virtual bool GetOtherFamilyNames(const nsAString& aFamilyName,
+ nsTArray<nsString>& aOtherFamilyNames)
+ {
+ return mOtherFamilyNames.Get(aFamilyName, &aOtherFamilyNames);
+ }
+
+ nsTArray<nsString> mFontFamiliesToLoad;
+
+ // currently non-issue but beware,
+ // this is also set during cleanup after finishing
+ mozilla::Atomic<bool> mCanceled;
+
+ // time spent on the loader thread
+ mozilla::TimeDuration mLoadTime;
+
+ struct FontCounts {
+ uint32_t families;
+ uint32_t fonts;
+ uint32_t cmaps;
+ uint32_t facenames;
+ uint32_t othernames;
+ };
+
+ FontCounts mLoadStats;
+
+ bool mLoadOtherNames;
+ bool mLoadFaceNames;
+ bool mLoadCmaps;
+
+ // face name ==> per-face data
+ nsDataHashtable<nsStringHashKey, FontFaceData> mFontFaceData;
+
+ // canonical family name ==> array of localized family names
+ nsDataHashtable<nsStringHashKey, nsTArray<nsString> > mOtherFamilyNames;
+};
+
+// gfxFontInfoLoader - helper class for loading font info on async thread
+// For large, "all fonts on system" data, data needed on a given platform
+// (e.g. localized names, face names, cmaps) are loaded async.
+
+// helper class for loading in font info on a separate async thread
+// once async thread completes, completion process is run on regular
+// intervals to prevent tying up the main thread
+
+class gfxFontInfoLoader {
+public:
+
+ // state transitions:
+ // initial ---StartLoader with delay---> timer on delay
+ // initial ---StartLoader without delay---> timer on interval
+ // timer on delay ---LoaderTimerFire---> timer on interval
+ // timer on delay ---CancelLoader---> timer off
+ // timer on interval ---CancelLoader---> timer off
+ // timer off ---StartLoader with delay---> timer on delay
+ // timer off ---StartLoader without delay---> timer on interval
+ typedef enum {
+ stateInitial,
+ stateTimerOnDelay,
+ stateAsyncLoad,
+ stateTimerOnInterval,
+ stateTimerOff
+ } TimerState;
+
+ gfxFontInfoLoader() :
+ mInterval(0), mState(stateInitial)
+ {
+ MOZ_COUNT_CTOR(gfxFontInfoLoader);
+ }
+
+ virtual ~gfxFontInfoLoader();
+
+ // start timer with an initial delay, then call Run method at regular intervals
+ void StartLoader(uint32_t aDelay, uint32_t aInterval);
+
+ // Finalize - async load complete, transfer data (on intervals if necessary)
+ virtual void FinalizeLoader(FontInfoData *aFontInfo);
+
+ // cancel the timer and cleanup
+ void CancelLoader();
+
+ uint32_t GetInterval() { return mInterval; }
+
+protected:
+ class ShutdownObserver : public nsIObserver
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit ShutdownObserver(gfxFontInfoLoader *aLoader)
+ : mLoader(aLoader)
+ { }
+
+ protected:
+ virtual ~ShutdownObserver()
+ { }
+
+ gfxFontInfoLoader *mLoader;
+ };
+
+ // CreateFontInfo - create platform-specific object used
+ // to load system-wide font info
+ virtual already_AddRefed<FontInfoData> CreateFontInfoData() {
+ return nullptr;
+ }
+
+ // Init - initialization before async loader thread runs
+ virtual void InitLoader() = 0;
+
+ // LoadFontInfo - transfer font info data within a time limit, return
+ // true when done
+ virtual bool LoadFontInfo() = 0;
+
+ // Cleanup - finish and cleanup after done, including possible reflows
+ virtual void CleanupLoader() {
+ mFontInfo = nullptr;
+ }
+
+ // Timer interval callbacks
+ static void LoadFontInfoCallback(nsITimer *aTimer, void *aThis) {
+ gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
+ loader->LoadFontInfoTimerFire();
+ }
+
+ static void DelayedStartCallback(nsITimer *aTimer, void *aThis) {
+ gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
+ loader->StartLoader(0, loader->GetInterval());
+ }
+
+ void LoadFontInfoTimerFire();
+
+ void AddShutdownObserver();
+ void RemoveShutdownObserver();
+
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsIObserver> mObserver;
+ nsCOMPtr<nsIThread> mFontLoaderThread;
+ uint32_t mInterval;
+ TimerState mState;
+
+ // after async font loader completes, data is stored here
+ RefPtr<FontInfoData> mFontInfo;
+
+ // time spent on the loader thread
+ mozilla::TimeDuration mLoadTime;
+};
+
+#endif /* GFX_FONT_INFO_LOADER_H */
diff --git a/system/graphics/thebes/gfxFontMissingGlyphs.cpp b/system/graphics/thebes/gfxFontMissingGlyphs.cpp
new file mode 100644
index 000000000..391166b58
--- /dev/null
+++ b/system/graphics/thebes/gfxFontMissingGlyphs.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFontMissingGlyphs.h"
+
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/RefPtr.h"
+#include "nsDeviceContext.h"
+#include "nsLayoutUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
+ ((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
+ (b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \
+ (b40 << 12) | (b41 << 13) | (b42 << 14))
+
+static const uint16_t glyphMicroFont[16] = {
+ CHAR_BITS(0, 1, 0,
+ 1, 0, 1,
+ 1, 0, 1,
+ 1, 0, 1,
+ 0, 1, 0),
+ CHAR_BITS(0, 1, 0,
+ 0, 1, 0,
+ 0, 1, 0,
+ 0, 1, 0,
+ 0, 1, 0),
+ CHAR_BITS(1, 1, 1,
+ 0, 0, 1,
+ 1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1),
+ CHAR_BITS(1, 1, 1,
+ 0, 0, 1,
+ 1, 1, 1,
+ 0, 0, 1,
+ 1, 1, 1),
+ CHAR_BITS(1, 0, 1,
+ 1, 0, 1,
+ 1, 1, 1,
+ 0, 0, 1,
+ 0, 0, 1),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1,
+ 0, 0, 1,
+ 1, 1, 1),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1,
+ 1, 0, 1,
+ 1, 1, 1),
+ CHAR_BITS(1, 1, 1,
+ 0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1),
+ CHAR_BITS(0, 1, 0,
+ 1, 0, 1,
+ 0, 1, 0,
+ 1, 0, 1,
+ 0, 1, 0),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 1,
+ 1, 1, 1,
+ 0, 0, 1,
+ 0, 0, 1),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 1,
+ 1, 1, 1,
+ 1, 0, 1,
+ 1, 0, 1),
+ CHAR_BITS(1, 1, 0,
+ 1, 0, 1,
+ 1, 1, 0,
+ 1, 0, 1,
+ 1, 1, 0),
+ CHAR_BITS(0, 1, 1,
+ 1, 0, 0,
+ 1, 0, 0,
+ 1, 0, 0,
+ 0, 1, 1),
+ CHAR_BITS(1, 1, 0,
+ 1, 0, 1,
+ 1, 0, 1,
+ 1, 0, 1,
+ 1, 1, 0),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1),
+ CHAR_BITS(1, 1, 1,
+ 1, 0, 0,
+ 1, 1, 1,
+ 1, 0, 0,
+ 1, 0, 0)
+};
+
+/* Parameters that control the rendering of hexboxes. They look like this:
+
+ BMP codepoints non-BMP codepoints
+ (U+0000 - U+FFFF) (U+10000 - U+10FFFF)
+
+ +---------+ +-------------+
+ | | | |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | | | |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | HHH HHH | | HHH HHH HHH |
+ | | | |
+ +---------+ +-------------+
+*/
+
+/** Width of a minifont glyph (see above) */
+static const int MINIFONT_WIDTH = 3;
+/** Height of a minifont glyph (see above) */
+static const int MINIFONT_HEIGHT = 5;
+/**
+ * Gap between minifont glyphs (both horizontal and vertical) and also
+ * the minimum desired gap between the box border and the glyphs
+ */
+static const int HEX_CHAR_GAP = 1;
+/**
+ * The amount of space between the vertical edge of the glyphbox and the
+ * box border. We make this nonzero so that when multiple missing glyphs
+ * occur consecutively there's a gap between their rendered boxes.
+ */
+static const int BOX_HORIZONTAL_INSET = 1;
+/** The width of the border */
+static const int BOX_BORDER_WIDTH = 1;
+/**
+ * The scaling factor for the border opacity; this is multiplied by the current
+ * opacity being used to draw the text.
+ */
+static const Float BOX_BORDER_OPACITY = 0.5;
+/**
+ * Draw a single hex character using the current color. A nice way to do this
+ * would be to fill in an A8 image surface and then use it as a mask
+ * to paint the current color. Tragically this doesn't currently work with the
+ * Quartz cairo backend which doesn't generally support masking with surfaces.
+ * So for now we just paint a bunch of rectangles...
+ */
+static void
+DrawHexChar(uint32_t aDigit, const Point& aPt, DrawTarget& aDrawTarget,
+ const Pattern &aPattern)
+{
+ // To avoid the potential for seams showing between rects when we're under
+ // a transform we concat all the rects into a PathBuilder and fill the
+ // resulting Path (rather than using DrawTarget::FillRect).
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ uint32_t glyphBits = glyphMicroFont[aDigit];
+ for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
+ for (int x = 0; x < MINIFONT_WIDTH; ++x) {
+ if (glyphBits & 1) {
+ Rect r(aPt.x + x, aPt.y + y, 1, 1);
+ MaybeSnapToDevicePixels(r, aDrawTarget, true);
+ builder->MoveTo(r.TopLeft());
+ builder->LineTo(r.TopRight());
+ builder->LineTo(r.BottomRight());
+ builder->LineTo(r.BottomLeft());
+ builder->Close();
+ }
+ glyphBits >>= 1;
+ }
+ }
+ RefPtr<Path> path = builder->Finish();
+ aDrawTarget.Fill(path, aPattern);
+}
+
+void
+gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
+ const Rect& aRect,
+ DrawTarget& aDrawTarget,
+ const Pattern& aPattern,
+ uint32_t aAppUnitsPerDevPixel)
+{
+ // If we're currently drawing with some kind of pattern, we just draw the
+ // missing-glyph data in black.
+ ColorPattern color = aPattern.GetType() == PatternType::COLOR ?
+ static_cast<const ColorPattern&>(aPattern) :
+ ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
+
+ // Stroke a rectangle so that the stroke's left edge is inset one pixel
+ // from the left edge of the glyph box and the stroke's right edge
+ // is inset one pixel from the right edge of the glyph box.
+ Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
+ Float borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
+ Float borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
+ Rect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
+ borderRight - borderLeft,
+ aRect.Height() - 2.0 * halfBorderWidth);
+ if (!borderStrokeRect.IsEmpty()) {
+ ColorPattern adjustedColor = color;
+ color.mColor.a *= BOX_BORDER_OPACITY;
+ StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
+ aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
+ }
+
+ Point center = aRect.Center();
+ Float halfGap = HEX_CHAR_GAP / 2.f;
+ Float top = -(MINIFONT_HEIGHT + halfGap);
+ // We always want integer scaling, otherwise the "bitmap" glyphs will look
+ // even uglier than usual when zoomed
+ int32_t devPixelsPerCSSPx =
+ std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
+ aAppUnitsPerDevPixel);
+ AutoRestoreTransform autoRestoreTransform(&aDrawTarget);
+ aDrawTarget.SetTransform(
+ aDrawTarget.GetTransform().PreTranslate(center).
+ PreScale(devPixelsPerCSSPx,
+ devPixelsPerCSSPx));
+ if (aChar < 0x10000) {
+ if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
+ aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
+ // Draw 4 digits for BMP
+ Float left = -(MINIFONT_WIDTH + halfGap);
+ DrawHexChar((aChar >> 12) & 0xF,
+ Point(left, top), aDrawTarget, color);
+ DrawHexChar((aChar >> 8) & 0xF,
+ Point(halfGap, top), aDrawTarget, color);
+ DrawHexChar((aChar >> 4) & 0xF,
+ Point(left, halfGap), aDrawTarget, color);
+ DrawHexChar(aChar & 0xF,
+ Point(halfGap, halfGap), aDrawTarget, color);
+ }
+ } else {
+ if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
+ aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
+ // Draw 6 digits for non-BMP
+ Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
+ Float second = -(MINIFONT_WIDTH / 2.0);
+ Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
+ DrawHexChar((aChar >> 20) & 0xF,
+ Point(first, top), aDrawTarget, color);
+ DrawHexChar((aChar >> 16) & 0xF,
+ Point(second, top), aDrawTarget, color);
+ DrawHexChar((aChar >> 12) & 0xF,
+ Point(third, top), aDrawTarget, color);
+ DrawHexChar((aChar >> 8) & 0xF,
+ Point(first, halfGap), aDrawTarget, color);
+ DrawHexChar((aChar >> 4) & 0xF,
+ Point(second, halfGap), aDrawTarget, color);
+ DrawHexChar(aChar & 0xF,
+ Point(third, halfGap), aDrawTarget, color);
+ }
+ }
+}
+
+Float
+gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
+ uint32_t aAppUnitsPerDevPixel)
+{
+/**
+ * The minimum desired width for a missing-glyph glyph box. I've laid it out
+ * like this so you can see what goes where.
+ */
+ Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
+ MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
+ ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
+ HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
+ // Note that this will give us floating-point division, so the width will
+ // -not- be snapped to integer multiples of its basic pixel value
+ width *= Float(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
+ return width;
+}
diff --git a/system/graphics/thebes/gfxFontMissingGlyphs.h b/system/graphics/thebes/gfxFontMissingGlyphs.h
new file mode 100644
index 000000000..1814a89a7
--- /dev/null
+++ b/system/graphics/thebes/gfxFontMissingGlyphs.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONTMISSINGGLYPHS_H
+#define GFX_FONTMISSINGGLYPHS_H
+
+#include "mozilla/Attributes.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+class Pattern;
+} // namespace gfx
+} // namespace mozilla
+
+/**
+ * This class should not be instantiated. It's just a container
+ * for some helper functions.
+ */
+class gfxFontMissingGlyphs final
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Float Float;
+ typedef mozilla::gfx::Pattern Pattern;
+ typedef mozilla::gfx::Rect Rect;
+
+ gfxFontMissingGlyphs() = delete; // prevent instantiation
+
+public:
+ /**
+ * Draw hexboxes for a missing glyph.
+ * @param aChar the UTF16 codepoint for the character
+ * @param aRect the glyph-box for the glyph that is missing
+ * @param aDrawTarget the DrawTarget to draw to
+ * @param aPattern the pattern currently being used to paint
+ * @param aAppUnitsPerDevPixel the appUnits to devPixel ratio we're using,
+ * (so we can scale glyphs to a sensible size)
+ */
+ static void DrawMissingGlyph(uint32_t aChar,
+ const Rect& aRect,
+ DrawTarget& aDrawTarget,
+ const Pattern& aPattern,
+ uint32_t aAppUnitsPerDevPixel);
+ /**
+ * @return the desired minimum width for a glyph-box that will allow
+ * the hexboxes to be drawn reasonably.
+ */
+ static Float GetDesiredMinWidth(uint32_t aChar,
+ uint32_t aAppUnitsPerDevUnit);
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxFontPrefLangList.h b/system/graphics/thebes/gfxFontPrefLangList.h
new file mode 100644
index 000000000..804b2b17e
--- /dev/null
+++ b/system/graphics/thebes/gfxFontPrefLangList.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// this needs to match the list of pref font.default.xx entries listed in all.js!
+
+FONT_PREF_LANG(Western, "x-western", x_western),
+FONT_PREF_LANG(Japanese, "ja", Japanese),
+FONT_PREF_LANG(ChineseTW, "zh-TW", Taiwanese),
+FONT_PREF_LANG(ChineseCN, "zh-CN", Chinese),
+FONT_PREF_LANG(ChineseHK, "zh-HK", HongKongChinese),
+FONT_PREF_LANG(Korean, "ko", ko),
+FONT_PREF_LANG(Cyrillic, "x-cyrillic", x_cyrillic),
+FONT_PREF_LANG(Greek, "el", el),
+FONT_PREF_LANG(Thai, "th", th),
+FONT_PREF_LANG(Hebrew, "he", he),
+FONT_PREF_LANG(Arabic, "ar", ar),
+FONT_PREF_LANG(Devanagari, "x-devanagari", x_devanagari),
+FONT_PREF_LANG(Tamil, "x-tamil", x_tamil),
+FONT_PREF_LANG(Armenian, "x-armn", x_armn),
+FONT_PREF_LANG(Bengali, "x-beng", x_beng),
+FONT_PREF_LANG(Canadian, "x-cans", x_cans),
+FONT_PREF_LANG(Ethiopic, "x-ethi", x_ethi),
+FONT_PREF_LANG(Georgian, "x-geor", x_geor),
+FONT_PREF_LANG(Gujarati, "x-gujr", x_gujr),
+FONT_PREF_LANG(Gurmukhi, "x-guru", x_guru),
+FONT_PREF_LANG(Khmer, "x-khmr", x_khmr),
+FONT_PREF_LANG(Malayalam, "x-mlym", x_mlym),
+FONT_PREF_LANG(Mathematics, "x-math", x_math),
+FONT_PREF_LANG(Oriya, "x-orya", x_orya),
+FONT_PREF_LANG(Telugu, "x-telu", x_telu),
+FONT_PREF_LANG(Kannada, "x-knda", x_knda),
+FONT_PREF_LANG(Sinhala, "x-sinh", x_sinh),
+FONT_PREF_LANG(Tibetan, "x-tibt", x_tibt),
+FONT_PREF_LANG(Others, "x-unicode", Unicode)
diff --git a/system/graphics/thebes/gfxFontTest.cpp b/system/graphics/thebes/gfxFontTest.cpp
new file mode 100644
index 000000000..b42189ca9
--- /dev/null
+++ b/system/graphics/thebes/gfxFontTest.cpp
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "gfxFontTest.h"
+
+gfxFontTestStore* gfxFontTestStore::sCurrentStore = nullptr;
diff --git a/system/graphics/thebes/gfxFontTest.h b/system/graphics/thebes/gfxFontTest.h
new file mode 100644
index 000000000..fa7cbbcd2
--- /dev/null
+++ b/system/graphics/thebes/gfxFontTest.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_TEST_H
+#define GFX_FONT_TEST_H
+
+#include "nsString.h"
+#include "nsTArray.h"
+
+#include "cairo.h"
+
+struct gfxFontTestItem {
+ gfxFontTestItem(const nsCString& fontName,
+ cairo_glyph_t *cglyphs, int nglyphs)
+ : platformFont(fontName)
+ {
+ glyphs = new cairo_glyph_t[nglyphs];
+ memcpy (glyphs, cglyphs, sizeof(cairo_glyph_t) * nglyphs);
+ num_glyphs = nglyphs;
+ }
+
+ gfxFontTestItem(const gfxFontTestItem& other) {
+ platformFont = other.platformFont;
+ num_glyphs = other.num_glyphs;
+ glyphs = new cairo_glyph_t[num_glyphs];
+ memcpy (glyphs, other.glyphs, sizeof(cairo_glyph_t) * num_glyphs);
+ }
+
+ ~gfxFontTestItem() {
+ delete [] glyphs;
+ }
+
+ nsCString platformFont;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+};
+
+
+class gfxFontTestStore {
+public:
+ gfxFontTestStore() { }
+
+ void AddItem (const nsCString& fontString,
+ cairo_glyph_t *cglyphs, int nglyphs)
+ {
+ items.AppendElement(gfxFontTestItem(fontString, cglyphs, nglyphs));
+ }
+
+ void AddItem (const nsString& fontString,
+ cairo_glyph_t *cglyphs, int nglyphs)
+ {
+ items.AppendElement(gfxFontTestItem(NS_ConvertUTF16toUTF8(fontString), cglyphs, nglyphs));
+ }
+
+ nsTArray<gfxFontTestItem> items;
+
+public:
+ static gfxFontTestStore *CurrentStore() {
+ return sCurrentStore;
+ }
+
+ static gfxFontTestStore *NewStore() {
+ if (sCurrentStore)
+ delete sCurrentStore;
+
+ sCurrentStore = new gfxFontTestStore;
+ return sCurrentStore;
+ }
+
+ static void DeleteStore() {
+ if (sCurrentStore)
+ delete sCurrentStore;
+
+ sCurrentStore = nullptr;
+ }
+
+protected:
+ static gfxFontTestStore *sCurrentStore;
+};
+
+
+#endif /* GFX_FONT_TEST_H */
diff --git a/system/graphics/thebes/gfxFontUtils.cpp b/system/graphics/thebes/gfxFontUtils.cpp
new file mode 100644
index 000000000..0d4b309f6
--- /dev/null
+++ b/system/graphics/thebes/gfxFontUtils.cpp
@@ -0,0 +1,1789 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BinarySearch.h"
+
+#include "gfxFontUtils.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/Sprintf.h"
+
+#include "nsCOMPtr.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIUnicodeDecoder.h"
+
+#include "harfbuzz/hb.h"
+
+#include "plbase64.h"
+#include "mozilla/Logging.h"
+
+#define LOG(log, args) MOZ_LOG(gfxPlatform::GetLog(log), \
+ LogLevel::Debug, args)
+
+#define UNICODE_BMP_LIMIT 0x10000
+
+using namespace mozilla;
+
+#pragma pack(1)
+
+typedef struct {
+ AutoSwap_PRUint16 format;
+ AutoSwap_PRUint16 reserved;
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint32 language;
+ AutoSwap_PRUint32 startCharCode;
+ AutoSwap_PRUint32 numChars;
+} Format10CmapHeader;
+
+typedef struct {
+ AutoSwap_PRUint16 format;
+ AutoSwap_PRUint16 reserved;
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint32 language;
+ AutoSwap_PRUint32 numGroups;
+} Format12CmapHeader;
+
+typedef struct {
+ AutoSwap_PRUint32 startCharCode;
+ AutoSwap_PRUint32 endCharCode;
+ AutoSwap_PRUint32 startGlyphId;
+} Format12Group;
+
+#pragma pack()
+
+void
+gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
+{
+ NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
+ uint32_t b, numBlocks = mBlocks.Length();
+
+ for (b = 0; b < numBlocks; b++) {
+ Block *block = mBlocks[b].get();
+ if (!block) {
+ continue;
+ }
+ const int BUFSIZE = 256;
+ char outStr[BUFSIZE];
+ int index = 0;
+ index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
+ for (int i = 0; i < 32; i += 4) {
+ for (int j = i; j < i + 4; j++) {
+ uint8_t bits = block->mBits[j];
+ uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
+ uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
+ uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
+
+ index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped);
+ }
+ if (i + 4 != 32) index += snprintf(&outStr[index], BUFSIZE - index, " ");
+ }
+ index += snprintf(&outStr[index], BUFSIZE - index, "]");
+ LOG(aWhichLog, ("%s", outStr));
+ }
+}
+
+nsresult
+gfxFontUtils::ReadCMAPTableFormat10(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap)
+{
+ // Ensure table is large enough that we can safely read the header
+ NS_ENSURE_TRUE(aLength >= sizeof(Format10CmapHeader),
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // Sanity-check header fields
+ const Format10CmapHeader *cmap10 =
+ reinterpret_cast<const Format10CmapHeader*>(aBuf);
+ NS_ENSURE_TRUE(uint16_t(cmap10->format) == 10,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ NS_ENSURE_TRUE(uint16_t(cmap10->reserved) == 0,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint32_t tablelen = cmap10->length;
+ NS_ENSURE_TRUE(tablelen >= sizeof(Format10CmapHeader) &&
+ tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
+
+ NS_ENSURE_TRUE(cmap10->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint32_t numChars = cmap10->numChars;
+ NS_ENSURE_TRUE(tablelen == sizeof(Format10CmapHeader) +
+ numChars * sizeof(uint16_t), NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint32_t charCode = cmap10->startCharCode;
+ NS_ENSURE_TRUE(charCode <= CMAP_MAX_CODEPOINT &&
+ charCode + numChars <= CMAP_MAX_CODEPOINT,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // glyphs[] array immediately follows the subtable header
+ const AutoSwap_PRUint16 *glyphs =
+ reinterpret_cast<const AutoSwap_PRUint16 *>(cmap10 + 1);
+
+ for (uint32_t i = 0; i < numChars; ++i) {
+ if (uint16_t(*glyphs) != 0) {
+ aCharacterMap.set(charCode);
+ }
+ ++charCode;
+ ++glyphs;
+ }
+
+ aCharacterMap.Compact();
+
+ return NS_OK;
+}
+
+nsresult
+gfxFontUtils::ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap)
+{
+ // Ensure table is large enough that we can safely read the header
+ NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // Sanity-check header fields
+ const Format12CmapHeader *cmap12 =
+ reinterpret_cast<const Format12CmapHeader*>(aBuf);
+ NS_ENSURE_TRUE(uint16_t(cmap12->format) == 12,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint32_t tablelen = cmap12->length;
+ NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) &&
+ tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
+
+ NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // Check that the table is large enough for the group array
+ const uint32_t numGroups = cmap12->numGroups;
+ NS_ENSURE_TRUE((tablelen - sizeof(Format12CmapHeader)) /
+ sizeof(Format12Group) >= numGroups,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // The array of groups immediately follows the subtable header.
+ const Format12Group *group =
+ reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
+
+ // Check that groups are in correct order and do not overlap,
+ // and record character coverage in aCharacterMap.
+ uint32_t prevEndCharCode = 0;
+ for (uint32_t i = 0; i < numGroups; i++, group++) {
+ uint32_t startCharCode = group->startCharCode;
+ const uint32_t endCharCode = group->endCharCode;
+ NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
+ startCharCode <= endCharCode &&
+ endCharCode <= CMAP_MAX_CODEPOINT,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ // don't include a character that maps to glyph ID 0 (.notdef)
+ if (group->startGlyphId == 0) {
+ startCharCode++;
+ }
+ if (startCharCode <= endCharCode) {
+ aCharacterMap.SetRange(startCharCode, endCharCode);
+ }
+ prevEndCharCode = endCharCode;
+ }
+
+ aCharacterMap.Compact();
+
+ return NS_OK;
+}
+
+nsresult
+gfxFontUtils::ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap)
+{
+ enum {
+ OffsetFormat = 0,
+ OffsetLength = 2,
+ OffsetLanguage = 4,
+ OffsetSegCountX2 = 6
+ };
+
+ NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ uint16_t tablelen = ReadShortAt(aBuf, OffsetLength);
+ NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
+ NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // This field should normally (except for Mac platform subtables) be zero according to
+ // the OT spec, but some buggy fonts have lang = 1 (which would be English for MacOS).
+ // E.g. Arial Narrow Bold, v. 1.1 (Tiger), Arial Unicode MS (see bug 530614).
+ // So accept either zero or one here; the error should be harmless.
+ NS_ENSURE_TRUE((ReadShortAt(aBuf, OffsetLanguage) & 0xfffe) == 0,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint16_t segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
+ NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4),
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ const uint16_t segCount = segCountX2 / 2;
+
+ const uint16_t *endCounts = reinterpret_cast<const uint16_t*>(aBuf + 14);
+ const uint16_t *startCounts = endCounts + 1 /* skip one uint16_t for reservedPad */ + segCount;
+ const uint16_t *idDeltas = startCounts + segCount;
+ const uint16_t *idRangeOffsets = idDeltas + segCount;
+ uint16_t prevEndCount = 0;
+ for (uint16_t i = 0; i < segCount; i++) {
+ const uint16_t endCount = ReadShortAt16(endCounts, i);
+ const uint16_t startCount = ReadShortAt16(startCounts, i);
+ const uint16_t idRangeOffset = ReadShortAt16(idRangeOffsets, i);
+
+ // sanity-check range
+ // This permits ranges to overlap by 1 character, which is strictly
+ // incorrect but occurs in Baskerville on OS X 10.7 (see bug 689087),
+ // and appears to be harmless in practice
+ NS_ENSURE_TRUE(startCount >= prevEndCount && startCount <= endCount,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ prevEndCount = endCount;
+
+ if (idRangeOffset == 0) {
+ // figure out if there's a code in the range that would map to
+ // glyph ID 0 (.notdef); if so, we need to skip setting that
+ // character code in the map
+ const uint16_t skipCode = 65536 - ReadShortAt16(idDeltas, i);
+ if (startCount < skipCode) {
+ aCharacterMap.SetRange(startCount,
+ std::min<uint16_t>(skipCode - 1,
+ endCount));
+ }
+ if (skipCode < endCount) {
+ aCharacterMap.SetRange(std::max<uint16_t>(startCount,
+ skipCode + 1),
+ endCount);
+ }
+ } else {
+ // const uint16_t idDelta = ReadShortAt16(idDeltas, i); // Unused: self-documenting.
+ for (uint32_t c = startCount; c <= endCount; ++c) {
+ if (c == 0xFFFF)
+ break;
+
+ const uint16_t *gdata = (idRangeOffset/2
+ + (c - startCount)
+ + &idRangeOffsets[i]);
+
+ NS_ENSURE_TRUE((uint8_t*)gdata > aBuf &&
+ (uint8_t*)gdata < aBuf + aLength,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ // make sure we have a glyph
+ if (*gdata != 0) {
+ // The glyph index at this point is:
+ uint16_t glyph = ReadShortAt16(idDeltas, i) + *gdata;
+ if (glyph) {
+ aCharacterMap.set(c);
+ }
+ }
+ }
+ }
+ }
+
+ aCharacterMap.Compact();
+
+ return NS_OK;
+}
+
+nsresult
+gfxFontUtils::ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength,
+ UniquePtr<uint8_t[]>& aTable)
+{
+ enum {
+ OffsetFormat = 0,
+ OffsetTableLength = 2,
+ OffsetNumVarSelectorRecords = 6,
+ OffsetVarSelectorRecords = 10,
+
+ SizeOfVarSelectorRecord = 11,
+ VSRecOffsetVarSelector = 0,
+ VSRecOffsetDefUVSOffset = 3,
+ VSRecOffsetNonDefUVSOffset = 7,
+
+ SizeOfDefUVSTable = 4,
+ DefUVSOffsetStartUnicodeValue = 0,
+ DefUVSOffsetAdditionalCount = 3,
+
+ SizeOfNonDefUVSTable = 5,
+ NonDefUVSOffsetUnicodeValue = 0,
+ NonDefUVSOffsetGlyphID = 3
+ };
+ NS_ENSURE_TRUE(aLength >= OffsetVarSelectorRecords,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 14,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ uint32_t tablelen = ReadLongAt(aBuf, OffsetTableLength);
+ NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
+ NS_ENSURE_TRUE(tablelen >= OffsetVarSelectorRecords,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ const uint32_t numVarSelectorRecords = ReadLongAt(aBuf, OffsetNumVarSelectorRecords);
+ NS_ENSURE_TRUE((tablelen - OffsetVarSelectorRecords) /
+ SizeOfVarSelectorRecord >= numVarSelectorRecords,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ const uint8_t *records = aBuf + OffsetVarSelectorRecords;
+ for (uint32_t i = 0; i < numVarSelectorRecords;
+ i++, records += SizeOfVarSelectorRecord) {
+ const uint32_t varSelector = ReadUint24At(records, VSRecOffsetVarSelector);
+ const uint32_t defUVSOffset = ReadLongAt(records, VSRecOffsetDefUVSOffset);
+ const uint32_t nonDefUVSOffset = ReadLongAt(records, VSRecOffsetNonDefUVSOffset);
+ NS_ENSURE_TRUE(varSelector <= CMAP_MAX_CODEPOINT &&
+ defUVSOffset <= tablelen - 4 &&
+ nonDefUVSOffset <= tablelen - 4,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+
+ if (defUVSOffset) {
+ const uint32_t numUnicodeValueRanges = ReadLongAt(aBuf, defUVSOffset);
+ NS_ENSURE_TRUE((tablelen - defUVSOffset) /
+ SizeOfDefUVSTable >= numUnicodeValueRanges,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ const uint8_t *tables = aBuf + defUVSOffset + 4;
+ uint32_t prevEndUnicode = 0;
+ for (uint32_t j = 0; j < numUnicodeValueRanges; j++, tables += SizeOfDefUVSTable) {
+ const uint32_t startUnicode = ReadUint24At(tables, DefUVSOffsetStartUnicodeValue);
+ const uint32_t endUnicode = startUnicode + tables[DefUVSOffsetAdditionalCount];
+ NS_ENSURE_TRUE((prevEndUnicode < startUnicode || j == 0) &&
+ endUnicode <= CMAP_MAX_CODEPOINT,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ prevEndUnicode = endUnicode;
+ }
+ }
+
+ if (nonDefUVSOffset) {
+ const uint32_t numUVSMappings = ReadLongAt(aBuf, nonDefUVSOffset);
+ NS_ENSURE_TRUE((tablelen - nonDefUVSOffset) /
+ SizeOfNonDefUVSTable >= numUVSMappings,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ const uint8_t *tables = aBuf + nonDefUVSOffset + 4;
+ uint32_t prevUnicode = 0;
+ for (uint32_t j = 0; j < numUVSMappings; j++, tables += SizeOfNonDefUVSTable) {
+ const uint32_t unicodeValue = ReadUint24At(tables, NonDefUVSOffsetUnicodeValue);
+ NS_ENSURE_TRUE((prevUnicode < unicodeValue || j == 0) &&
+ unicodeValue <= CMAP_MAX_CODEPOINT,
+ NS_ERROR_GFX_CMAP_MALFORMED);
+ prevUnicode = unicodeValue;
+ }
+ }
+ }
+
+ aTable = MakeUnique<uint8_t[]>(tablelen);
+ memcpy(aTable.get(), aBuf, tablelen);
+
+ return NS_OK;
+}
+
+// For fonts with two format-4 tables, we allow the first one (Unicode platform)
+// to be replaced by the Microsoft-platform subtable.
+
+#define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft) || \
+ ((p) == PLATFORM_ID_UNICODE))
+
+#define acceptableUCS4Encoding(p, e, k) \
+ ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform)
+
+#define acceptablePlatform(p) ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT)
+#define isSymbol(p,e) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol)
+#define isUVSEncoding(p, e) ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform)
+
+uint32_t
+gfxFontUtils::FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength,
+ uint32_t *aTableOffset,
+ uint32_t *aUVSTableOffset,
+ bool *aSymbolEncoding)
+{
+ enum {
+ OffsetVersion = 0,
+ OffsetNumTables = 2,
+ SizeOfHeader = 4,
+
+ TableOffsetPlatformID = 0,
+ TableOffsetEncodingID = 2,
+ TableOffsetOffset = 4,
+ SizeOfTable = 8,
+
+ SubtableOffsetFormat = 0
+ };
+ enum {
+ EncodingIDSymbol = 0,
+ EncodingIDMicrosoft = 1,
+ EncodingIDDefaultForUnicodePlatform = 0,
+ EncodingIDUCS4ForUnicodePlatform = 3,
+ EncodingIDUVSForUnicodePlatform = 5,
+ EncodingIDUCS4ForMicrosoftPlatform = 10
+ };
+
+ if (aUVSTableOffset) {
+ *aUVSTableOffset = 0;
+ }
+
+ if (!aBuf || aBufLength < SizeOfHeader) {
+ // cmap table is missing, or too small to contain header fields!
+ return 0;
+ }
+
+ // uint16_t version = ReadShortAt(aBuf, OffsetVersion); // Unused: self-documenting.
+ uint16_t numTables = ReadShortAt(aBuf, OffsetNumTables);
+ if (aBufLength < uint32_t(SizeOfHeader + numTables * SizeOfTable)) {
+ return 0;
+ }
+
+ // save the format we want here
+ uint32_t keepFormat = 0;
+
+ const uint8_t *table = aBuf + SizeOfHeader;
+ for (uint16_t i = 0; i < numTables; ++i, table += SizeOfTable) {
+ const uint16_t platformID = ReadShortAt(table, TableOffsetPlatformID);
+ if (!acceptablePlatform(platformID))
+ continue;
+
+ const uint16_t encodingID = ReadShortAt(table, TableOffsetEncodingID);
+ const uint32_t offset = ReadLongAt(table, TableOffsetOffset);
+ if (aBufLength - 2 < offset) {
+ // this subtable is not valid - beyond end of buffer
+ return 0;
+ }
+
+ const uint8_t *subtable = aBuf + offset;
+ const uint16_t format = ReadShortAt(subtable, SubtableOffsetFormat);
+
+ if (isSymbol(platformID, encodingID)) {
+ keepFormat = format;
+ *aTableOffset = offset;
+ *aSymbolEncoding = true;
+ break;
+ } else if (format == 4 && acceptableFormat4(platformID, encodingID, keepFormat)) {
+ keepFormat = format;
+ *aTableOffset = offset;
+ *aSymbolEncoding = false;
+ } else if ((format == 10 || format == 12) &&
+ acceptableUCS4Encoding(platformID, encodingID, keepFormat)) {
+ keepFormat = format;
+ *aTableOffset = offset;
+ *aSymbolEncoding = false;
+ if (platformID > PLATFORM_ID_UNICODE || !aUVSTableOffset || *aUVSTableOffset) {
+ break; // we don't want to try anything else when this format is available.
+ }
+ } else if (format == 14 && isUVSEncoding(platformID, encodingID) && aUVSTableOffset) {
+ *aUVSTableOffset = offset;
+ if (keepFormat == 10 || keepFormat == 12) {
+ break;
+ }
+ }
+ }
+
+ return keepFormat;
+}
+
+nsresult
+gfxFontUtils::ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength,
+ gfxSparseBitSet& aCharacterMap,
+ uint32_t& aUVSOffset,
+ bool& aUnicodeFont, bool& aSymbolFont)
+{
+ uint32_t offset;
+ bool symbol;
+ uint32_t format = FindPreferredSubtable(aBuf, aBufLength,
+ &offset, &aUVSOffset, &symbol);
+
+ switch (format) {
+ case 4:
+ if (symbol) {
+ aUnicodeFont = false;
+ aSymbolFont = true;
+ } else {
+ aUnicodeFont = true;
+ aSymbolFont = false;
+ }
+ return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset,
+ aCharacterMap);
+
+ case 10:
+ aUnicodeFont = true;
+ aSymbolFont = false;
+ return ReadCMAPTableFormat10(aBuf + offset, aBufLength - offset,
+ aCharacterMap);
+
+ case 12:
+ aUnicodeFont = true;
+ aSymbolFont = false;
+ return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset,
+ aCharacterMap);
+
+ default:
+ break;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+#pragma pack(1)
+
+typedef struct {
+ AutoSwap_PRUint16 format;
+ AutoSwap_PRUint16 length;
+ AutoSwap_PRUint16 language;
+ AutoSwap_PRUint16 segCountX2;
+ AutoSwap_PRUint16 searchRange;
+ AutoSwap_PRUint16 entrySelector;
+ AutoSwap_PRUint16 rangeShift;
+
+ AutoSwap_PRUint16 arrays[1];
+} Format4Cmap;
+
+typedef struct {
+ AutoSwap_PRUint16 format;
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint32 numVarSelectorRecords;
+
+ typedef struct {
+ AutoSwap_PRUint24 varSelector;
+ AutoSwap_PRUint32 defaultUVSOffset;
+ AutoSwap_PRUint32 nonDefaultUVSOffset;
+ } VarSelectorRecord;
+
+ VarSelectorRecord varSelectorRecords[1];
+} Format14Cmap;
+
+typedef struct {
+ AutoSwap_PRUint32 numUVSMappings;
+
+ typedef struct {
+ AutoSwap_PRUint24 unicodeValue;
+ AutoSwap_PRUint16 glyphID;
+ } UVSMapping;
+
+ UVSMapping uvsMappings[1];
+} NonDefUVSTable;
+
+#pragma pack()
+
+uint32_t
+gfxFontUtils::MapCharToGlyphFormat4(const uint8_t* aBuf, uint32_t aLength,
+ char16_t aCh)
+{
+ const Format4Cmap *cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf);
+
+ uint16_t segCount = (uint16_t)(cmap4->segCountX2) / 2;
+
+ const AutoSwap_PRUint16* endCodes = &cmap4->arrays[0];
+ const AutoSwap_PRUint16* startCodes = &cmap4->arrays[segCount + 1];
+ const AutoSwap_PRUint16* idDelta = &startCodes[segCount];
+ const AutoSwap_PRUint16* idRangeOffset = &idDelta[segCount];
+
+ // Sanity-check that the fixed-size arrays don't exceed the buffer.
+ const uint8_t* const limit = aBuf + aLength;
+ if ((const uint8_t*)(&idRangeOffset[segCount]) > limit) {
+ return 0; // broken font, just bail out safely
+ }
+
+ // For most efficient binary search, we want to work on a range of segment
+ // indexes that is a power of 2 so that we can always halve it by shifting.
+ // So we find the largest power of 2 that is <= segCount.
+ // We will offset this range by segOffset so as to reach the end
+ // of the table, provided that doesn't put us beyond the target
+ // value from the outset.
+ uint32_t powerOf2 = mozilla::FindHighestBit(segCount);
+ uint32_t segOffset = segCount - powerOf2;
+ uint32_t idx = 0;
+
+ if (uint16_t(startCodes[segOffset]) <= aCh) {
+ idx = segOffset;
+ }
+
+ // Repeatedly halve the size of the range until we find the target group
+ while (powerOf2 > 1) {
+ powerOf2 >>= 1;
+ if (uint16_t(startCodes[idx + powerOf2]) <= aCh) {
+ idx += powerOf2;
+ }
+ }
+
+ if (aCh >= uint16_t(startCodes[idx]) && aCh <= uint16_t(endCodes[idx])) {
+ uint16_t result;
+ if (uint16_t(idRangeOffset[idx]) == 0) {
+ result = aCh;
+ } else {
+ uint16_t offset = aCh - uint16_t(startCodes[idx]);
+ const AutoSwap_PRUint16* glyphIndexTable =
+ (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[idx] +
+ uint16_t(idRangeOffset[idx]));
+ if ((const uint8_t*)(glyphIndexTable + offset + 1) > limit) {
+ return 0; // broken font, just bail out safely
+ }
+ result = glyphIndexTable[offset];
+ }
+
+ // Note that this is unsigned 16-bit arithmetic, and may wrap around
+ // (which is required behavior per spec)
+ result += uint16_t(idDelta[idx]);
+ return result;
+ }
+
+ return 0;
+}
+
+uint32_t
+gfxFontUtils::MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh)
+{
+ const Format10CmapHeader *cmap10 =
+ reinterpret_cast<const Format10CmapHeader*>(aBuf);
+
+ uint32_t startChar = cmap10->startCharCode;
+ uint32_t numChars = cmap10->numChars;
+
+ if (aCh < startChar || aCh >= startChar + numChars) {
+ return 0;
+ }
+
+ const AutoSwap_PRUint16 *glyphs =
+ reinterpret_cast<const AutoSwap_PRUint16 *>(cmap10 + 1);
+
+ uint16_t glyph = glyphs[aCh - startChar];
+ return glyph;
+}
+
+uint32_t
+gfxFontUtils::MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh)
+{
+ const Format12CmapHeader *cmap12 =
+ reinterpret_cast<const Format12CmapHeader*>(aBuf);
+
+ // We know that numGroups is within range for the subtable size
+ // because it was checked by ReadCMAPTableFormat12.
+ uint32_t numGroups = cmap12->numGroups;
+
+ // The array of groups immediately follows the subtable header.
+ const Format12Group *groups =
+ reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
+
+ // For most efficient binary search, we want to work on a range that
+ // is a power of 2 so that we can always halve it by shifting.
+ // So we find the largest power of 2 that is <= numGroups.
+ // We will offset this range by rangeOffset so as to reach the end
+ // of the table, provided that doesn't put us beyond the target
+ // value from the outset.
+ uint32_t powerOf2 = mozilla::FindHighestBit(numGroups);
+ uint32_t rangeOffset = numGroups - powerOf2;
+ uint32_t range = 0;
+ uint32_t startCharCode;
+
+ if (groups[rangeOffset].startCharCode <= aCh) {
+ range = rangeOffset;
+ }
+
+ // Repeatedly halve the size of the range until we find the target group
+ while (powerOf2 > 1) {
+ powerOf2 >>= 1;
+ if (groups[range + powerOf2].startCharCode <= aCh) {
+ range += powerOf2;
+ }
+ }
+
+ // Check if the character is actually present in the range and return
+ // the corresponding glyph ID
+ startCharCode = groups[range].startCharCode;
+ if (startCharCode <= aCh && groups[range].endCharCode >= aCh) {
+ return groups[range].startGlyphId + aCh - startCharCode;
+ }
+
+ // Else it's not present, so return the .notdef glyph
+ return 0;
+}
+
+namespace {
+
+struct Format14CmapWrapper
+{
+ const Format14Cmap& mCmap14;
+ explicit Format14CmapWrapper(const Format14Cmap& cmap14) : mCmap14(cmap14) {}
+ uint32_t operator[](size_t index) const {
+ return mCmap14.varSelectorRecords[index].varSelector;
+ }
+};
+
+struct NonDefUVSTableWrapper
+{
+ const NonDefUVSTable& mTable;
+ explicit NonDefUVSTableWrapper(const NonDefUVSTable& table) : mTable(table) {}
+ uint32_t operator[](size_t index) const {
+ return mTable.uvsMappings[index].unicodeValue;
+ }
+};
+
+} // namespace
+
+uint16_t
+gfxFontUtils::MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS)
+{
+ using mozilla::BinarySearch;
+ const Format14Cmap *cmap14 = reinterpret_cast<const Format14Cmap*>(aBuf);
+
+ size_t index;
+ if (!BinarySearch(Format14CmapWrapper(*cmap14),
+ 0, cmap14->numVarSelectorRecords, aVS, &index)) {
+ return 0;
+ }
+
+ const uint32_t nonDefUVSOffset = cmap14->varSelectorRecords[index].nonDefaultUVSOffset;
+ if (!nonDefUVSOffset) {
+ return 0;
+ }
+
+ const NonDefUVSTable *table = reinterpret_cast<const NonDefUVSTable*>
+ (aBuf + nonDefUVSOffset);
+
+ if (BinarySearch(NonDefUVSTableWrapper(*table), 0, table->numUVSMappings,
+ aCh, &index)) {
+ return table->uvsMappings[index].glyphID;
+ }
+
+ return 0;
+}
+
+uint32_t
+gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength,
+ uint32_t aUnicode, uint32_t aVarSelector)
+{
+ uint32_t offset, uvsOffset;
+ bool symbol;
+ uint32_t format = FindPreferredSubtable(aCmapBuf, aBufLength, &offset,
+ &uvsOffset, &symbol);
+
+ uint32_t gid;
+ switch (format) {
+ case 4:
+ gid = aUnicode < UNICODE_BMP_LIMIT ?
+ MapCharToGlyphFormat4(aCmapBuf + offset, aBufLength - offset,
+ char16_t(aUnicode)) : 0;
+ break;
+ case 10:
+ gid = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode);
+ break;
+ case 12:
+ gid = MapCharToGlyphFormat12(aCmapBuf + offset, aUnicode);
+ break;
+ default:
+ NS_WARNING("unsupported cmap format, glyphs will be missing");
+ gid = 0;
+ }
+
+ if (aVarSelector && uvsOffset && gid) {
+ uint32_t varGID =
+ gfxFontUtils::MapUVSToGlyphFormat14(aCmapBuf + uvsOffset,
+ aUnicode, aVarSelector);
+ if (!varGID) {
+ aUnicode = gfxFontUtils::GetUVSFallback(aUnicode, aVarSelector);
+ if (aUnicode) {
+ switch (format) {
+ case 4:
+ if (aUnicode < UNICODE_BMP_LIMIT) {
+ varGID = MapCharToGlyphFormat4(aCmapBuf + offset,
+ aBufLength - offset,
+ char16_t(aUnicode));
+ }
+ break;
+ case 10:
+ varGID = MapCharToGlyphFormat10(aCmapBuf + offset,
+ aUnicode);
+ break;
+ case 12:
+ varGID = MapCharToGlyphFormat12(aCmapBuf + offset,
+ aUnicode);
+ break;
+ }
+ }
+ }
+ if (varGID) {
+ gid = varGID;
+ }
+
+ // else the variation sequence was not supported, use default mapping
+ // of the character code alone
+ }
+
+ return gid;
+}
+
+void gfxFontUtils::ParseFontList(const nsAString& aFamilyList,
+ nsTArray<nsString>& aFontList)
+{
+ const char16_t kComma = char16_t(',');
+
+ // append each font name to the list
+ nsAutoString fontname;
+ const char16_t *p, *p_end;
+ aFamilyList.BeginReading(p);
+ aFamilyList.EndReading(p_end);
+
+ while (p < p_end) {
+ const char16_t *nameStart = p;
+ while (++p != p_end && *p != kComma)
+ /* nothing */ ;
+
+ // pull out a single name and clean out leading/trailing whitespace
+ fontname = Substring(nameStart, p);
+ fontname.CompressWhitespace(true, true);
+
+ // append it to the list
+ aFontList.AppendElement(fontname);
+ ++p;
+ }
+}
+
+void gfxFontUtils::AppendPrefsFontList(const char *aPrefName,
+ nsTArray<nsString>& aFontList)
+{
+ // get the list of single-face font families
+ nsAdoptingString fontlistValue = Preferences::GetString(aPrefName);
+ if (!fontlistValue) {
+ return;
+ }
+
+ ParseFontList(fontlistValue, aFontList);
+}
+
+void gfxFontUtils::GetPrefsFontList(const char *aPrefName,
+ nsTArray<nsString>& aFontList)
+{
+ aFontList.Clear();
+ AppendPrefsFontList(aPrefName, aFontList);
+}
+
+// produce a unique font name that is (1) a valid Postscript name and (2) less
+// than 31 characters in length. Using AddFontMemResourceEx on Windows fails
+// for names longer than 30 characters in length.
+
+#define MAX_B64_LEN 32
+
+nsresult gfxFontUtils::MakeUniqueUserFontName(nsAString& aName)
+{
+ nsCOMPtr<nsIUUIDGenerator> uuidgen =
+ do_GetService("@mozilla.org/uuid-generator;1");
+ NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
+
+ nsID guid;
+
+ NS_ASSERTION(sizeof(guid) * 2 <= MAX_B64_LEN, "size of nsID has changed!");
+
+ nsresult rv = uuidgen->GenerateUUIDInPlace(&guid);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char guidB64[MAX_B64_LEN] = {0};
+
+ if (!PL_Base64Encode(reinterpret_cast<char*>(&guid), sizeof(guid), guidB64))
+ return NS_ERROR_FAILURE;
+
+ // all b64 characters except for '/' are allowed in Postscript names, so convert / ==> -
+ char *p;
+ for (p = guidB64; *p; p++) {
+ if (*p == '/')
+ *p = '-';
+ }
+
+ aName.AssignLiteral(u"uf");
+ aName.AppendASCII(guidB64);
+ return NS_OK;
+}
+
+
+// TrueType/OpenType table handling code
+
+// need byte aligned structs
+#pragma pack(1)
+
+// name table stores set of name record structures, followed by
+// large block containing all the strings. name record offset and length
+// indicates the offset and length within that block.
+// http://www.microsoft.com/typography/otspec/name.htm
+struct NameRecordData {
+ uint32_t offset;
+ uint32_t length;
+};
+
+#pragma pack()
+
+static bool
+IsValidSFNTVersion(uint32_t version)
+{
+ // normally 0x00010000, CFF-style OT fonts == 'OTTO' and Apple TT fonts = 'true'
+ // 'typ1' is also possible for old Type 1 fonts in a SFNT container but not supported
+ return version == 0x10000 ||
+ version == TRUETYPE_TAG('O','T','T','O') ||
+ version == TRUETYPE_TAG('t','r','u','e');
+}
+
+// Copy and swap UTF-16 values, assume no surrogate pairs, can be in place.
+// aInBuf and aOutBuf are NOT necessarily 16-bit-aligned, so we should avoid
+// accessing them directly as uint16_t* values.
+// aLen is count of UTF-16 values, so the byte buffers are twice that.
+static void
+CopySwapUTF16(const char* aInBuf, char* aOutBuf, uint32_t aLen)
+{
+ const char* end = aInBuf + aLen * 2;
+ while (aInBuf < end) {
+ uint8_t b0 = *aInBuf++;
+ *aOutBuf++ = *aInBuf++;
+ *aOutBuf++ = b0;
+ }
+}
+
+gfxUserFontType
+gfxFontUtils::DetermineFontDataType(const uint8_t *aFontData, uint32_t aFontDataLength)
+{
+ // test for OpenType font data
+ // problem: EOT-Lite with 0x10000 length will look like TrueType!
+ if (aFontDataLength >= sizeof(SFNTHeader)) {
+ const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+ uint32_t sfntVersion = sfntHeader->sfntVersion;
+ if (IsValidSFNTVersion(sfntVersion)) {
+ return GFX_USERFONT_OPENTYPE;
+ }
+ }
+
+ // test for WOFF
+ if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
+ const AutoSwap_PRUint32 *version =
+ reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
+ if (uint32_t(*version) == TRUETYPE_TAG('w','O','F','F')) {
+ return GFX_USERFONT_WOFF;
+ }
+ if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) &&
+ uint32_t(*version) == TRUETYPE_TAG('w','O','F','2')) {
+ return GFX_USERFONT_WOFF2;
+ }
+ }
+
+ // tests for other formats here
+
+ return GFX_USERFONT_UNKNOWN;
+}
+
+static int
+DirEntryCmp(const void* aKey, const void* aItem)
+{
+ int32_t tag = *static_cast<const int32_t*>(aKey);
+ const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
+ return tag - int32_t(entry->tag);
+}
+
+/* static */
+TableDirEntry*
+gfxFontUtils::FindTableDirEntry(const void* aFontData, uint32_t aTableTag)
+{
+ const SFNTHeader* header =
+ reinterpret_cast<const SFNTHeader*>(aFontData);
+ const TableDirEntry* dir =
+ reinterpret_cast<const TableDirEntry*>(header + 1);
+ return static_cast<TableDirEntry*>
+ (bsearch(&aTableTag, dir, uint16_t(header->numTables),
+ sizeof(TableDirEntry), DirEntryCmp));
+}
+
+/* static */
+hb_blob_t*
+gfxFontUtils::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
+{
+ const TableDirEntry* dir = FindTableDirEntry(aFontData, aTableTag);
+ if (dir) {
+ return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
+ dir->offset, dir->length,
+ HB_MEMORY_MODE_READONLY, nullptr, nullptr);
+
+ }
+ return nullptr;
+}
+
+nsresult
+gfxFontUtils::RenameFont(const nsAString& aName, const uint8_t *aFontData,
+ uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont)
+{
+ NS_ASSERTION(aNewFont, "null font data array");
+
+ uint64_t dataLength(aFontDataLength);
+
+ // new name table
+ static const uint32_t neededNameIDs[] = {NAME_ID_FAMILY,
+ NAME_ID_STYLE,
+ NAME_ID_UNIQUE,
+ NAME_ID_FULL,
+ NAME_ID_POSTSCRIPT};
+
+ // calculate new name table size
+ uint16_t nameCount = ArrayLength(neededNameIDs);
+
+ // leave room for null-terminator
+ uint32_t nameStrLength = (aName.Length() + 1) * sizeof(char16_t);
+ if (nameStrLength > 65535) {
+ // The name length _in bytes_ must fit in an unsigned short field;
+ // therefore, a name longer than this cannot be used.
+ return NS_ERROR_FAILURE;
+ }
+
+ // round name table size up to 4-byte multiple
+ uint32_t nameTableSize = (sizeof(NameHeader) +
+ sizeof(NameRecord) * nameCount +
+ nameStrLength +
+ 3) & ~3;
+
+ if (dataLength + nameTableSize > UINT32_MAX)
+ return NS_ERROR_FAILURE;
+
+ // bug 505386 - need to handle unpadded font length
+ uint32_t paddedFontDataSize = (aFontDataLength + 3) & ~3;
+ uint32_t adjFontDataSize = paddedFontDataSize + nameTableSize;
+
+ // create new buffer: old font data plus new name table
+ if (!aNewFont->AppendElements(adjFontDataSize, fallible))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // copy the old font data
+ uint8_t *newFontData = reinterpret_cast<uint8_t*>(aNewFont->Elements());
+
+ // null the last four bytes in case the font length is not a multiple of 4
+ memset(newFontData + aFontDataLength, 0, paddedFontDataSize - aFontDataLength);
+
+ // copy font data
+ memcpy(newFontData, aFontData, aFontDataLength);
+
+ // null out the last 4 bytes for checksum calculations
+ memset(newFontData + adjFontDataSize - 4, 0, 4);
+
+ NameHeader *nameHeader = reinterpret_cast<NameHeader*>(newFontData +
+ paddedFontDataSize);
+
+ // -- name header
+ nameHeader->format = 0;
+ nameHeader->count = nameCount;
+ nameHeader->stringOffset = sizeof(NameHeader) + nameCount * sizeof(NameRecord);
+
+ // -- name records
+ uint32_t i;
+ NameRecord *nameRecord = reinterpret_cast<NameRecord*>(nameHeader + 1);
+
+ for (i = 0; i < nameCount; i++, nameRecord++) {
+ nameRecord->platformID = PLATFORM_ID_MICROSOFT;
+ nameRecord->encodingID = ENCODING_ID_MICROSOFT_UNICODEBMP;
+ nameRecord->languageID = LANG_ID_MICROSOFT_EN_US;
+ nameRecord->nameID = neededNameIDs[i];
+ nameRecord->offset = 0;
+ nameRecord->length = nameStrLength;
+ }
+
+ // -- string data, located after the name records, stored in big-endian form
+ char16_t *strData = reinterpret_cast<char16_t*>(nameRecord);
+
+ mozilla::NativeEndian::copyAndSwapToBigEndian(strData,
+ aName.BeginReading(),
+ aName.Length());
+ strData[aName.Length()] = 0; // add null termination
+
+ // adjust name table header to point to the new name table
+ SFNTHeader *sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData);
+
+ // table directory entries begin immediately following SFNT header
+ TableDirEntry *dirEntry =
+ FindTableDirEntry(newFontData, TRUETYPE_TAG('n','a','m','e'));
+ // function only called if font validates, so this should always be true
+ MOZ_ASSERT(dirEntry, "attempt to rename font with no name table");
+
+ uint32_t numTables = sfntHeader->numTables;
+
+ // note: dirEntry now points to 'name' table record
+
+ // recalculate name table checksum
+ uint32_t checkSum = 0;
+ AutoSwap_PRUint32 *nameData = reinterpret_cast<AutoSwap_PRUint32*> (nameHeader);
+ AutoSwap_PRUint32 *nameDataEnd = nameData + (nameTableSize >> 2);
+
+ while (nameData < nameDataEnd)
+ checkSum = checkSum + *nameData++;
+
+ // adjust name table entry to point to new name table
+ dirEntry->offset = paddedFontDataSize;
+ dirEntry->length = nameTableSize;
+ dirEntry->checkSum = checkSum;
+
+ // fix up checksums
+ uint32_t checksum = 0;
+
+ // checksum for font = (checksum of header) + (checksum of tables)
+ uint32_t headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
+ const AutoSwap_PRUint32 *headerData =
+ reinterpret_cast<const AutoSwap_PRUint32*>(newFontData);
+
+ // header length is in bytes, checksum calculated in longwords
+ for (i = 0; i < (headerLen >> 2); i++, headerData++) {
+ checksum += *headerData;
+ }
+
+ uint32_t headOffset = 0;
+ dirEntry = reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
+
+ for (i = 0; i < numTables; i++, dirEntry++) {
+ if (dirEntry->tag == TRUETYPE_TAG('h','e','a','d')) {
+ headOffset = dirEntry->offset;
+ }
+ checksum += dirEntry->checkSum;
+ }
+
+ NS_ASSERTION(headOffset != 0, "no head table for font");
+
+ HeadTable *headData = reinterpret_cast<HeadTable*>(newFontData + headOffset);
+
+ headData->checkSumAdjustment = HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum;
+
+ return NS_OK;
+}
+
+// This is only called after the basic validity of the downloaded sfnt
+// data has been checked, so it should never fail to find the name table
+// (though it might fail to read it, if memory isn't available);
+// other checks here are just for extra paranoia.
+nsresult
+gfxFontUtils::GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength,
+ nsAString& aFullName)
+{
+ aFullName.AssignLiteral("(MISSING NAME)"); // should always get replaced
+
+ const TableDirEntry *dirEntry =
+ FindTableDirEntry(aFontData, TRUETYPE_TAG('n','a','m','e'));
+
+ // should never fail, as we're only called after font validation succeeded
+ NS_ENSURE_TRUE(dirEntry, NS_ERROR_NOT_AVAILABLE);
+
+ uint32_t len = dirEntry->length;
+ NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset,
+ NS_ERROR_UNEXPECTED);
+
+ hb_blob_t *nameBlob =
+ hb_blob_create((const char*)aFontData + dirEntry->offset, len,
+ HB_MEMORY_MODE_READONLY, nullptr, nullptr);
+ nsresult rv = GetFullNameFromTable(nameBlob, aFullName);
+ hb_blob_destroy(nameBlob);
+
+ return rv;
+}
+
+nsresult
+gfxFontUtils::GetFullNameFromTable(hb_blob_t *aNameTable,
+ nsAString& aFullName)
+{
+ nsAutoString name;
+ nsresult rv =
+ gfxFontUtils::ReadCanonicalName(aNameTable,
+ gfxFontUtils::NAME_ID_FULL,
+ name);
+ if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
+ aFullName = name;
+ return NS_OK;
+ }
+ rv = gfxFontUtils::ReadCanonicalName(aNameTable,
+ gfxFontUtils::NAME_ID_FAMILY,
+ name);
+ if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
+ nsAutoString styleName;
+ rv = gfxFontUtils::ReadCanonicalName(aNameTable,
+ gfxFontUtils::NAME_ID_STYLE,
+ styleName);
+ if (NS_SUCCEEDED(rv) && !styleName.IsEmpty()) {
+ name.Append(' ');
+ name.Append(styleName);
+ aFullName = name;
+ }
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+gfxFontUtils::GetFamilyNameFromTable(hb_blob_t *aNameTable,
+ nsAString& aFullName)
+{
+ nsAutoString name;
+ nsresult rv =
+ gfxFontUtils::ReadCanonicalName(aNameTable,
+ gfxFontUtils::NAME_ID_FAMILY,
+ name);
+ if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
+ aFullName = name;
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+enum {
+ CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US,
+ PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MICROSOFT
+};
+
+nsresult
+gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
+ uint32_t aNameID, int32_t aPlatformID,
+ nsTArray<nsString>& aNames)
+{
+ return ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
+ aPlatformID, aNames);
+}
+
+nsresult
+gfxFontUtils::ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
+ nsString& aName)
+{
+ uint32_t nameTableLen;
+ const char *nameTable = hb_blob_get_data(aNameTable, &nameTableLen);
+ return ReadCanonicalName(nameTable, nameTableLen, aNameID, aName);
+}
+
+nsresult
+gfxFontUtils::ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
+ uint32_t aNameID, nsString& aName)
+{
+ nsresult rv;
+
+ nsTArray<nsString> names;
+
+ // first, look for the English name (this will succeed 99% of the time)
+ rv = ReadNames(aNameData, aDataLen, aNameID, CANONICAL_LANG_ID,
+ PLATFORM_ID, names);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // otherwise, grab names for all languages
+ if (names.Length() == 0) {
+ rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
+ PLATFORM_ID, names);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // return the first name (99.9% of the time names will
+ // contain a single English name)
+ if (names.Length()) {
+ aName.Assign(names[0]);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+// Charsets to use for decoding Mac platform font names.
+// This table is sorted by {encoding, language}, with the wildcard "ANY" being
+// greater than any defined values for each field; we use a binary search on both
+// fields, and fall back to matching only encoding if necessary
+
+// Some "redundant" entries for specific combinations are included such as
+// encoding=roman, lang=english, in order that common entries will be found
+// on the first search.
+
+#define ANY 0xffff
+const gfxFontUtils::MacFontNameCharsetMapping gfxFontUtils::gMacFontNameCharsets[] =
+{
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ENGLISH, "macintosh" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ICELANDIC, "x-mac-icelandic" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_TURKISH, "x-mac-turkish" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_POLISH, "x-mac-ce" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ROMANIAN, "x-mac-romanian" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_CZECH, "x-mac-ce" },
+ { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_SLOVAK, "x-mac-ce" },
+ { ENCODING_ID_MAC_ROMAN, ANY, "macintosh" },
+ { ENCODING_ID_MAC_JAPANESE, LANG_ID_MAC_JAPANESE, "Shift_JIS" },
+ { ENCODING_ID_MAC_JAPANESE, ANY, "Shift_JIS" },
+ { ENCODING_ID_MAC_TRAD_CHINESE, LANG_ID_MAC_TRAD_CHINESE, "Big5" },
+ { ENCODING_ID_MAC_TRAD_CHINESE, ANY, "Big5" },
+ { ENCODING_ID_MAC_KOREAN, LANG_ID_MAC_KOREAN, "EUC-KR" },
+ { ENCODING_ID_MAC_KOREAN, ANY, "EUC-KR" },
+ { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_ARABIC, "x-mac-arabic" },
+ { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_URDU, "x-mac-farsi" },
+ { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_FARSI, "x-mac-farsi" },
+ { ENCODING_ID_MAC_ARABIC, ANY, "x-mac-arabic" },
+ { ENCODING_ID_MAC_HEBREW, LANG_ID_MAC_HEBREW, "x-mac-hebrew" },
+ { ENCODING_ID_MAC_HEBREW, ANY, "x-mac-hebrew" },
+ { ENCODING_ID_MAC_GREEK, ANY, "x-mac-greek" },
+ { ENCODING_ID_MAC_CYRILLIC, ANY, "x-mac-cyrillic" },
+ { ENCODING_ID_MAC_DEVANAGARI, ANY, "x-mac-devanagari"},
+ { ENCODING_ID_MAC_GURMUKHI, ANY, "x-mac-gurmukhi" },
+ { ENCODING_ID_MAC_GUJARATI, ANY, "x-mac-gujarati" },
+ { ENCODING_ID_MAC_SIMP_CHINESE, LANG_ID_MAC_SIMP_CHINESE, "gb18030" },
+ { ENCODING_ID_MAC_SIMP_CHINESE, ANY, "gb18030" }
+};
+
+const char* gfxFontUtils::gISOFontNameCharsets[] =
+{
+ /* 0 */ "windows-1252", /* US-ASCII */
+ /* 1 */ nullptr , /* spec says "ISO 10646" but does not specify encoding form! */
+ /* 2 */ "windows-1252" /* ISO-8859-1 */
+};
+
+const char* gfxFontUtils::gMSFontNameCharsets[] =
+{
+ /* [0] ENCODING_ID_MICROSOFT_SYMBOL */ "" ,
+ /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */ "" ,
+ /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */ "Shift_JIS" ,
+ /* [3] ENCODING_ID_MICROSOFT_PRC */ nullptr ,
+ /* [4] ENCODING_ID_MICROSOFT_BIG5 */ "Big5" ,
+ /* [5] ENCODING_ID_MICROSOFT_WANSUNG */ nullptr ,
+ /* [6] ENCODING_ID_MICROSOFT_JOHAB */ nullptr ,
+ /* [7] reserved */ nullptr ,
+ /* [8] reserved */ nullptr ,
+ /* [9] reserved */ nullptr ,
+ /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ ""
+};
+
+struct MacCharsetMappingComparator
+{
+ typedef gfxFontUtils::MacFontNameCharsetMapping MacFontNameCharsetMapping;
+ const MacFontNameCharsetMapping& mSearchValue;
+ explicit MacCharsetMappingComparator(const MacFontNameCharsetMapping& aSearchValue)
+ : mSearchValue(aSearchValue) {}
+ int operator()(const MacFontNameCharsetMapping& aEntry) const {
+ if (mSearchValue < aEntry) {
+ return -1;
+ }
+ if (aEntry < mSearchValue) {
+ return 1;
+ }
+ return 0;
+ }
+};
+
+// Return the name of the charset we should use to decode a font name
+// given the name table attributes.
+// Special return values:
+// "" charset is UTF16BE, no need for a converter
+// nullptr unknown charset, do not attempt conversion
+const char*
+gfxFontUtils::GetCharsetForFontName(uint16_t aPlatform, uint16_t aScript, uint16_t aLanguage)
+{
+ switch (aPlatform)
+ {
+ case PLATFORM_ID_UNICODE:
+ return "";
+
+ case PLATFORM_ID_MAC:
+ {
+ MacFontNameCharsetMapping searchValue = { aScript, aLanguage, nullptr };
+ for (uint32_t i = 0; i < 2; ++i) {
+ size_t idx;
+ if (BinarySearchIf(gMacFontNameCharsets, 0, ArrayLength(gMacFontNameCharsets),
+ MacCharsetMappingComparator(searchValue), &idx)) {
+ return gMacFontNameCharsets[idx].mCharsetName;
+ }
+
+ // no match, so try again finding one in any language
+ searchValue.mLanguage = ANY;
+ }
+ }
+ break;
+
+ case PLATFORM_ID_ISO:
+ if (aScript < ArrayLength(gISOFontNameCharsets)) {
+ return gISOFontNameCharsets[aScript];
+ }
+ break;
+
+ case PLATFORM_ID_MICROSOFT:
+ if (aScript < ArrayLength(gMSFontNameCharsets)) {
+ return gMSFontNameCharsets[aScript];
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+// convert a raw name from the name table to an nsString, if possible;
+// return value indicates whether conversion succeeded
+bool
+gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen,
+ uint32_t aPlatformCode, uint32_t aScriptCode,
+ uint32_t aLangCode, nsAString& aName)
+{
+ if (aByteLen <= 0) {
+ NS_WARNING("empty font name");
+ aName.SetLength(0);
+ return true;
+ }
+
+ const char *csName = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode);
+
+ if (!csName) {
+ // nullptr -> unknown charset
+#ifdef DEBUG
+ char warnBuf[128];
+ if (aByteLen > 64)
+ aByteLen = 64;
+ SprintfLiteral(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>",
+ aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
+ NS_WARNING(warnBuf);
+#endif
+ return false;
+ }
+
+ if (csName[0] == 0) {
+ // empty charset name: data is utf16be, no need to instantiate a converter
+ uint32_t strLen = aByteLen / 2;
+ aName.SetLength(strLen);
+#ifdef IS_LITTLE_ENDIAN
+ CopySwapUTF16(aNameData, reinterpret_cast<char*>(aName.BeginWriting()),
+ strLen);
+#else
+ memcpy(aName.BeginWriting(), aNameData, strLen * 2);
+#endif
+ return true;
+ }
+
+ nsCOMPtr<nsIUnicodeDecoder> decoder =
+ mozilla::dom::EncodingUtils::DecoderForEncoding(csName);
+ if (!decoder) {
+ NS_WARNING("failed to get the decoder for a font name string");
+ return false;
+ }
+
+ int32_t destLength;
+ nsresult rv = decoder->GetMaxLength(aNameData, aByteLen, &destLength);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("decoder->GetMaxLength failed, invalid font name?");
+ return false;
+ }
+
+ // make space for the converted string
+ aName.SetLength(destLength);
+ rv = decoder->Convert(aNameData, &aByteLen,
+ aName.BeginWriting(), &destLength);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("decoder->Convert failed, invalid font name?");
+ return false;
+ }
+ aName.Truncate(destLength); // set the actual length
+
+ return true;
+}
+
+nsresult
+gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
+ uint32_t aNameID,
+ int32_t aLangID, int32_t aPlatformID,
+ nsTArray<nsString>& aNames)
+{
+ NS_ASSERTION(aDataLen != 0, "null name table");
+
+ if (!aDataLen) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // -- name table data
+ const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
+
+ uint32_t nameCount = nameHeader->count;
+
+ // -- sanity check the number of name records
+ if (uint64_t(nameCount) * sizeof(NameRecord) > aDataLen) {
+ NS_WARNING("invalid font (name table data)");
+ return NS_ERROR_FAILURE;
+ }
+
+ // -- iterate through name records
+ const NameRecord *nameRecord
+ = reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
+ uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset);
+
+ uint32_t i;
+ for (i = 0; i < nameCount; i++, nameRecord++) {
+ uint32_t platformID;
+
+ // skip over unwanted nameID's
+ if (uint32_t(nameRecord->nameID) != aNameID) {
+ continue;
+ }
+
+ // skip over unwanted platform data
+ platformID = nameRecord->platformID;
+ if (aPlatformID != PLATFORM_ALL &&
+ platformID != uint32_t(aPlatformID)) {
+ continue;
+ }
+
+ // skip over unwanted languages
+ if (aLangID != LANG_ALL &&
+ uint32_t(nameRecord->languageID) != uint32_t(aLangID)) {
+ continue;
+ }
+
+ // add name to names array
+
+ // -- calculate string location
+ uint32_t namelen = nameRecord->length;
+ uint32_t nameoff = nameRecord->offset; // offset from base of string storage
+
+ if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen)
+ > aDataLen) {
+ NS_WARNING("invalid font (name table strings)");
+ return NS_ERROR_FAILURE;
+ }
+
+ // -- decode if necessary and make nsString
+ nsAutoString name;
+
+ DecodeFontName(aNameData + nameStringsBase + nameoff, namelen,
+ platformID, uint32_t(nameRecord->encodingID),
+ uint32_t(nameRecord->languageID), name);
+
+ uint32_t k, numNames;
+ bool foundName = false;
+
+ numNames = aNames.Length();
+ for (k = 0; k < numNames; k++) {
+ if (name.Equals(aNames[k])) {
+ foundName = true;
+ break;
+ }
+ }
+
+ if (!foundName)
+ aNames.AppendElement(name);
+
+ }
+
+ return NS_OK;
+}
+
+#pragma pack(1)
+
+struct COLRBaseGlyphRecord {
+ AutoSwap_PRUint16 glyphId;
+ AutoSwap_PRUint16 firstLayerIndex;
+ AutoSwap_PRUint16 numLayers;
+};
+
+struct COLRLayerRecord {
+ AutoSwap_PRUint16 glyphId;
+ AutoSwap_PRUint16 paletteEntryIndex;
+};
+
+struct CPALColorRecord {
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+};
+
+#pragma pack()
+
+bool
+gfxFontUtils::ValidateColorGlyphs(hb_blob_t* aCOLR, hb_blob_t* aCPAL)
+{
+ unsigned int colrLength;
+ const COLRHeader* colr =
+ reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &colrLength));
+ unsigned int cpalLength;
+ const CPALHeaderVersion0* cpal =
+ reinterpret_cast<const CPALHeaderVersion0*>(hb_blob_get_data(aCPAL, &cpalLength));
+
+ if (!colr || !cpal || !colrLength || !cpalLength) {
+ return false;
+ }
+
+ if (uint16_t(colr->version) != 0 || uint16_t(cpal->version) != 0) {
+ // We only support version 0 headers.
+ return false;
+ }
+
+ const uint32_t offsetBaseGlyphRecord = colr->offsetBaseGlyphRecord;
+ const uint16_t numBaseGlyphRecord = colr->numBaseGlyphRecord;
+ const uint32_t offsetLayerRecord = colr->offsetLayerRecord;
+ const uint16_t numLayerRecords = colr->numLayerRecords;
+
+ const uint32_t offsetFirstColorRecord = cpal->offsetFirstColorRecord;
+ const uint16_t numColorRecords = cpal->numColorRecords;
+ const uint32_t numPaletteEntries = cpal->numPaletteEntries;
+
+ if (offsetBaseGlyphRecord >= colrLength) {
+ return false;
+ }
+
+ if (offsetLayerRecord >= colrLength) {
+ return false;
+ }
+
+ if (offsetFirstColorRecord >= cpalLength) {
+ return false;
+ }
+
+ if (!numPaletteEntries) {
+ return false;
+ }
+
+ if (sizeof(COLRBaseGlyphRecord) * numBaseGlyphRecord >
+ colrLength - offsetBaseGlyphRecord) {
+ // COLR base glyph record will be overflow
+ return false;
+ }
+
+ if (sizeof(COLRLayerRecord) * numLayerRecords >
+ colrLength - offsetLayerRecord) {
+ // COLR layer record will be overflow
+ return false;
+ }
+
+ if (sizeof(CPALColorRecord) * numColorRecords >
+ cpalLength - offsetFirstColorRecord) {
+ // CPAL color record will be overflow
+ return false;
+ }
+
+ if (numPaletteEntries * uint16_t(cpal->numPalettes) != numColorRecords ) {
+ // palette of CPAL color record will be overflow.
+ return false;
+ }
+
+ uint16_t lastGlyphId = 0;
+ const COLRBaseGlyphRecord* baseGlyph =
+ reinterpret_cast<const COLRBaseGlyphRecord*>(
+ reinterpret_cast<const uint8_t*>(colr) + offsetBaseGlyphRecord);
+
+ for (uint16_t i = 0; i < numBaseGlyphRecord; i++, baseGlyph++) {
+ const uint32_t firstLayerIndex = baseGlyph->firstLayerIndex;
+ const uint16_t numLayers = baseGlyph->numLayers;
+ const uint16_t glyphId = baseGlyph->glyphId;
+
+ if (lastGlyphId && lastGlyphId >= glyphId) {
+ // glyphId must be sorted
+ return false;
+ }
+ lastGlyphId = glyphId;
+
+ if (!numLayers) {
+ // no layer
+ return false;
+ }
+ if (firstLayerIndex + numLayers > numLayerRecords) {
+ // layer length of target glyph is overflow
+ return false;
+ }
+ }
+
+ const COLRLayerRecord* layer =
+ reinterpret_cast<const COLRLayerRecord*>(
+ reinterpret_cast<const uint8_t*>(colr) + offsetLayerRecord);
+
+ for (uint16_t i = 0; i < numLayerRecords; i++, layer++) {
+ if (uint16_t(layer->paletteEntryIndex) >= numPaletteEntries &&
+ uint16_t(layer->paletteEntryIndex) != 0xFFFF) {
+ // CPAL palette entry record is overflow
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int
+CompareBaseGlyph(const void* key, const void* data)
+{
+ uint32_t glyphId = (uint32_t)(uintptr_t)key;
+ const COLRBaseGlyphRecord* baseGlyph =
+ reinterpret_cast<const COLRBaseGlyphRecord*>(data);
+ uint32_t baseGlyphId = uint16_t(baseGlyph->glyphId);
+
+ if (baseGlyphId == glyphId) {
+ return 0;
+ }
+
+ return baseGlyphId > glyphId ? -1 : 1;
+}
+
+static
+COLRBaseGlyphRecord*
+LookForBaseGlyphRecord(const COLRHeader* aCOLR, uint32_t aGlyphId)
+{
+ const uint8_t* baseGlyphRecords =
+ reinterpret_cast<const uint8_t*>(aCOLR) +
+ uint32_t(aCOLR->offsetBaseGlyphRecord);
+ // BaseGlyphRecord is sorted by glyphId
+ return reinterpret_cast<COLRBaseGlyphRecord*>(
+ bsearch((void*)(uintptr_t)aGlyphId,
+ baseGlyphRecords,
+ uint16_t(aCOLR->numBaseGlyphRecord),
+ sizeof(COLRBaseGlyphRecord),
+ CompareBaseGlyph));
+}
+
+bool
+gfxFontUtils::GetColorGlyphLayers(hb_blob_t* aCOLR,
+ hb_blob_t* aCPAL,
+ uint32_t aGlyphId,
+ const mozilla::gfx::Color& aDefaultColor,
+ nsTArray<uint16_t>& aGlyphs,
+ nsTArray<mozilla::gfx::Color>& aColors)
+{
+ unsigned int blobLength;
+ const COLRHeader* colr =
+ reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR,
+ &blobLength));
+ MOZ_ASSERT(colr, "Cannot get COLR raw data");
+ MOZ_ASSERT(blobLength, "Found COLR data, but length is 0");
+
+ COLRBaseGlyphRecord* baseGlyph = LookForBaseGlyphRecord(colr, aGlyphId);
+ if (!baseGlyph) {
+ return false;
+ }
+
+ const CPALHeaderVersion0* cpal =
+ reinterpret_cast<const CPALHeaderVersion0*>(
+ hb_blob_get_data(aCPAL, &blobLength));
+ MOZ_ASSERT(cpal, "Cannot get CPAL raw data");
+ MOZ_ASSERT(blobLength, "Found CPAL data, but length is 0");
+
+ const COLRLayerRecord* layer =
+ reinterpret_cast<const COLRLayerRecord*>(
+ reinterpret_cast<const uint8_t*>(colr) +
+ uint32_t(colr->offsetLayerRecord) +
+ sizeof(COLRLayerRecord) * uint16_t(baseGlyph->firstLayerIndex));
+ const uint16_t numLayers = baseGlyph->numLayers;
+ const uint32_t offsetFirstColorRecord = cpal->offsetFirstColorRecord;
+
+ for (uint16_t layerIndex = 0; layerIndex < numLayers; layerIndex++) {
+ aGlyphs.AppendElement(uint16_t(layer->glyphId));
+ if (uint16_t(layer->paletteEntryIndex) == 0xFFFF) {
+ aColors.AppendElement(aDefaultColor);
+ } else {
+ const CPALColorRecord* color =
+ reinterpret_cast<const CPALColorRecord*>(
+ reinterpret_cast<const uint8_t*>(cpal) +
+ offsetFirstColorRecord +
+ sizeof(CPALColorRecord) * uint16_t(layer->paletteEntryIndex));
+ aColors.AppendElement(mozilla::gfx::Color(color->red / 255.0,
+ color->green / 255.0,
+ color->blue / 255.0,
+ color->alpha / 255.0));
+ }
+ layer++;
+ }
+ return true;
+}
+
+#ifdef XP_WIN
+
+/* static */
+bool
+gfxFontUtils::IsCffFont(const uint8_t* aFontData)
+{
+ // this is only called after aFontData has passed basic validation,
+ // so we know there is enough data present to allow us to read the version!
+ const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+ return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O'));
+}
+
+#endif
+
+#undef acceptablePlatform
+#undef isSymbol
+#undef isUVSEncoding
+#undef LOG
+#undef LOG_ENABLED
diff --git a/system/graphics/thebes/gfxFontUtils.h b/system/graphics/thebes/gfxFontUtils.h
new file mode 100644
index 000000000..1e87e5436
--- /dev/null
+++ b/system/graphics/thebes/gfxFontUtils.h
@@ -0,0 +1,1027 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_UTILS_H
+#define GFX_FONT_UTILS_H
+
+#include "gfxPlatform.h"
+#include "nsComponentManagerUtils.h"
+#include "nsTArray.h"
+#include "mozilla/Likely.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+
+#include "zlib.h"
+#include <algorithm>
+
+/* Bug 341128 - w32api defines min/max which causes problems with <bitset> */
+#ifdef __MINGW32__
+#undef min
+#undef max
+#endif
+
+typedef struct hb_blob_t hb_blob_t;
+
+class gfxSparseBitSet {
+private:
+ enum { BLOCK_SIZE = 32 }; // ==> 256 codepoints per block
+ enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 };
+ enum { BLOCK_INDEX_SHIFT = 8 };
+
+ struct Block {
+ Block(const Block& aBlock) { memcpy(mBits, aBlock.mBits, sizeof(mBits)); }
+ explicit Block(unsigned char memsetValue = 0) { memset(mBits, memsetValue, BLOCK_SIZE); }
+ uint8_t mBits[BLOCK_SIZE];
+ };
+
+public:
+ gfxSparseBitSet() { }
+ gfxSparseBitSet(const gfxSparseBitSet& aBitset) {
+ uint32_t len = aBitset.mBlocks.Length();
+ mBlocks.AppendElements(len);
+ for (uint32_t i = 0; i < len; ++i) {
+ Block *block = aBitset.mBlocks[i].get();
+ if (block) {
+ mBlocks[i] = mozilla::MakeUnique<Block>(*block);
+ }
+ }
+ }
+
+ bool Equals(const gfxSparseBitSet *aOther) const {
+ if (mBlocks.Length() != aOther->mBlocks.Length()) {
+ return false;
+ }
+ size_t n = mBlocks.Length();
+ for (size_t i = 0; i < n; ++i) {
+ const Block *b1 = mBlocks[i].get();
+ const Block *b2 = aOther->mBlocks[i].get();
+ if (!b1 != !b2) {
+ return false;
+ }
+ if (!b1) {
+ continue;
+ }
+ if (memcmp(&b1->mBits, &b2->mBits, BLOCK_SIZE) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool test(uint32_t aIndex) const {
+ NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
+ uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
+ if (blockIndex >= mBlocks.Length()) {
+ return false;
+ }
+ const Block *block = mBlocks[blockIndex].get();
+ if (!block) {
+ return false;
+ }
+ return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
+ }
+
+ // dump out contents of bitmap
+ void Dump(const char* aPrefix, eGfxLog aWhichLog) const;
+
+ bool TestRange(uint32_t aStart, uint32_t aEnd) {
+ uint32_t startBlock, endBlock, blockLen;
+
+ // start point is beyond the end of the block array? return false immediately
+ startBlock = aStart >> BLOCK_INDEX_SHIFT;
+ blockLen = mBlocks.Length();
+ if (startBlock >= blockLen) return false;
+
+ // check for blocks in range, if none, return false
+ uint32_t blockIndex;
+ bool hasBlocksInRange = false;
+
+ endBlock = aEnd >> BLOCK_INDEX_SHIFT;
+ for (blockIndex = startBlock; blockIndex <= endBlock; blockIndex++) {
+ if (blockIndex < blockLen && mBlocks[blockIndex]) {
+ hasBlocksInRange = true;
+ }
+ }
+ if (!hasBlocksInRange) {
+ return false;
+ }
+
+ Block *block;
+ uint32_t i, start, end;
+
+ // first block, check bits
+ if ((block = mBlocks[startBlock].get())) {
+ start = aStart;
+ end = std::min(aEnd, ((startBlock+1) << BLOCK_INDEX_SHIFT) - 1);
+ for (i = start; i <= end; i++) {
+ if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
+ return true;
+ }
+ }
+ }
+ if (endBlock == startBlock) {
+ return false;
+ }
+
+ // [2..n-1] blocks check bytes
+ for (blockIndex = startBlock + 1; blockIndex < endBlock; blockIndex++) {
+ uint32_t index;
+
+ if (blockIndex >= blockLen ||
+ !(block = mBlocks[blockIndex].get())) {
+ continue;
+ }
+ for (index = 0; index < BLOCK_SIZE; index++) {
+ if (block->mBits[index]) {
+ return true;
+ }
+ }
+ }
+
+ // last block, check bits
+ if (endBlock < blockLen && (block = mBlocks[endBlock].get())) {
+ start = endBlock << BLOCK_INDEX_SHIFT;
+ end = aEnd;
+ for (i = start; i <= end; i++) {
+ if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void set(uint32_t aIndex) {
+ uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
+ if (blockIndex >= mBlocks.Length()) {
+ mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
+ }
+ Block *block = mBlocks[blockIndex].get();
+ if (!block) {
+ block = new Block;
+ mBlocks[blockIndex].reset(block);
+ }
+ block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
+ }
+
+ void set(uint32_t aIndex, bool aValue) {
+ if (aValue)
+ set(aIndex);
+ else
+ clear(aIndex);
+ }
+
+ void SetRange(uint32_t aStart, uint32_t aEnd) {
+ const uint32_t startIndex = aStart/BLOCK_SIZE_BITS;
+ const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS;
+
+ if (endIndex >= mBlocks.Length()) {
+ uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
+ mBlocks.AppendElements(numNewBlocks);
+ }
+
+ for (uint32_t i = startIndex; i <= endIndex; ++i) {
+ const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
+ const uint32_t blockLastBit = blockFirstBit + BLOCK_SIZE_BITS - 1;
+
+ Block *block = mBlocks[i].get();
+ if (!block) {
+ bool fullBlock =
+ (aStart <= blockFirstBit && aEnd >= blockLastBit);
+
+ block = new Block(fullBlock ? 0xFF : 0);
+ mBlocks[i].reset(block);
+
+ if (fullBlock) {
+ continue;
+ }
+ }
+
+ const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
+ const uint32_t end = std::min<uint32_t>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
+
+ for (uint32_t bit = start; bit <= end; ++bit) {
+ block->mBits[bit>>3] |= 1 << (bit & 0x7);
+ }
+ }
+ }
+
+ void clear(uint32_t aIndex) {
+ uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
+ if (blockIndex >= mBlocks.Length()) {
+ mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
+ }
+ Block *block = mBlocks[blockIndex].get();
+ if (!block) {
+ return;
+ }
+ block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7));
+ }
+
+ void ClearRange(uint32_t aStart, uint32_t aEnd) {
+ const uint32_t startIndex = aStart/BLOCK_SIZE_BITS;
+ const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS;
+
+ if (endIndex >= mBlocks.Length()) {
+ uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
+ mBlocks.AppendElements(numNewBlocks);
+ }
+
+ for (uint32_t i = startIndex; i <= endIndex; ++i) {
+ const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
+
+ Block *block = mBlocks[i].get();
+ if (!block) {
+ // any nonexistent block is implicitly all clear,
+ // so there's no need to even create it
+ continue;
+ }
+
+ const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
+ const uint32_t end = std::min<uint32_t>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
+
+ for (uint32_t bit = start; bit <= end; ++bit) {
+ block->mBits[bit>>3] &= ~(1 << (bit & 0x7));
+ }
+ }
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ size_t total = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (uint32_t i = 0; i < mBlocks.Length(); i++) {
+ if (mBlocks[i]) {
+ total += aMallocSizeOf(mBlocks[i].get());
+ }
+ }
+ return total;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ // clear out all blocks in the array
+ void reset() {
+ uint32_t i;
+ for (i = 0; i < mBlocks.Length(); i++) {
+ mBlocks[i] = nullptr;
+ }
+ }
+
+ // set this bitset to the union of its current contents and another
+ void Union(const gfxSparseBitSet& aBitset) {
+ // ensure mBlocks is large enough
+ uint32_t blockCount = aBitset.mBlocks.Length();
+ if (blockCount > mBlocks.Length()) {
+ uint32_t needed = blockCount - mBlocks.Length();
+ mBlocks.AppendElements(needed);
+ }
+ // for each block that may be present in aBitset...
+ for (uint32_t i = 0; i < blockCount; ++i) {
+ // if it is missing (implicitly empty), just skip
+ if (!aBitset.mBlocks[i]) {
+ continue;
+ }
+ // if the block is missing in this set, just copy the other
+ if (!mBlocks[i]) {
+ mBlocks[i] = mozilla::MakeUnique<Block>(*aBitset.mBlocks[i]);
+ continue;
+ }
+ // else set existing block to the union of both
+ uint32_t *dst = reinterpret_cast<uint32_t*>(mBlocks[i]->mBits);
+ const uint32_t *src =
+ reinterpret_cast<const uint32_t*>(aBitset.mBlocks[i]->mBits);
+ for (uint32_t j = 0; j < BLOCK_SIZE / 4; ++j) {
+ dst[j] |= src[j];
+ }
+ }
+ }
+
+ void Compact() {
+ mBlocks.Compact();
+ }
+
+ uint32_t GetChecksum() const {
+ uint32_t check = adler32(0, Z_NULL, 0);
+ for (uint32_t i = 0; i < mBlocks.Length(); i++) {
+ if (mBlocks[i]) {
+ const Block *block = mBlocks[i].get();
+ check = adler32(check, (uint8_t*) (&i), 4);
+ check = adler32(check, (uint8_t*) block, sizeof(Block));
+ }
+ }
+ return check;
+ }
+
+private:
+ nsTArray<mozilla::UniquePtr<Block>> mBlocks;
+};
+
+#define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+
+namespace mozilla {
+
+// Byte-swapping types and name table structure definitions moved from
+// gfxFontUtils.cpp to .h file so that gfxFont.cpp can also refer to them
+#pragma pack(1)
+
+struct AutoSwap_PRUint16 {
+#ifdef __SUNPRO_CC
+ AutoSwap_PRUint16& operator = (const uint16_t aValue)
+ {
+ this->value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT AutoSwap_PRUint16(uint16_t aValue)
+ {
+ value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+ operator uint16_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+ operator uint32_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+ operator uint64_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ uint16_t value;
+};
+
+struct AutoSwap_PRInt16 {
+#ifdef __SUNPRO_CC
+ AutoSwap_PRInt16& operator = (const int16_t aValue)
+ {
+ this->value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT AutoSwap_PRInt16(int16_t aValue)
+ {
+ value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+ operator int16_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+ operator uint32_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ int16_t value;
+};
+
+struct AutoSwap_PRUint32 {
+#ifdef __SUNPRO_CC
+ AutoSwap_PRUint32& operator = (const uint32_t aValue)
+ {
+ this->value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT AutoSwap_PRUint32(uint32_t aValue)
+ {
+ value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+ operator uint32_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ uint32_t value;
+};
+
+struct AutoSwap_PRInt32 {
+#ifdef __SUNPRO_CC
+ AutoSwap_PRInt32& operator = (const int32_t aValue)
+ {
+ this->value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT AutoSwap_PRInt32(int32_t aValue)
+ {
+ value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+ operator int32_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ int32_t value;
+};
+
+struct AutoSwap_PRUint64 {
+#ifdef __SUNPRO_CC
+ AutoSwap_PRUint64& operator = (const uint64_t aValue)
+ {
+ this->value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT AutoSwap_PRUint64(uint64_t aValue)
+ {
+ value = mozilla::NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+ operator uint64_t() const
+ {
+ return mozilla::NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ uint64_t value;
+};
+
+struct AutoSwap_PRUint24 {
+ operator uint32_t() const { return value[0] << 16 | value[1] << 8 | value[2]; }
+private:
+ AutoSwap_PRUint24() { }
+ uint8_t value[3];
+};
+
+struct SFNTHeader {
+ AutoSwap_PRUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0.
+ AutoSwap_PRUint16 numTables; // Number of tables.
+ AutoSwap_PRUint16 searchRange; // (Maximum power of 2 <= numTables) x 16.
+ AutoSwap_PRUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
+ AutoSwap_PRUint16 rangeShift; // NumTables x 16-searchRange.
+};
+
+struct TableDirEntry {
+ AutoSwap_PRUint32 tag; // 4 -byte identifier.
+ AutoSwap_PRUint32 checkSum; // CheckSum for this table.
+ AutoSwap_PRUint32 offset; // Offset from beginning of TrueType font file.
+ AutoSwap_PRUint32 length; // Length of this table.
+};
+
+struct HeadTable {
+ enum {
+ HEAD_VERSION = 0x00010000,
+ HEAD_MAGIC_NUMBER = 0x5F0F3CF5,
+ HEAD_CHECKSUM_CALC_CONST = 0xB1B0AFBA
+ };
+
+ AutoSwap_PRUint32 tableVersionNumber; // Fixed, 0x00010000 for version 1.0.
+ AutoSwap_PRUint32 fontRevision; // Set by font manufacturer.
+ AutoSwap_PRUint32 checkSumAdjustment; // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum.
+ AutoSwap_PRUint32 magicNumber; // Set to 0x5F0F3CF5.
+ AutoSwap_PRUint16 flags;
+ AutoSwap_PRUint16 unitsPerEm; // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
+ AutoSwap_PRUint64 created; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
+ AutoSwap_PRUint64 modified; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
+ AutoSwap_PRInt16 xMin; // For all glyph bounding boxes.
+ AutoSwap_PRInt16 yMin; // For all glyph bounding boxes.
+ AutoSwap_PRInt16 xMax; // For all glyph bounding boxes.
+ AutoSwap_PRInt16 yMax; // For all glyph bounding boxes.
+ AutoSwap_PRUint16 macStyle; // Bit 0: Bold (if set to 1);
+ AutoSwap_PRUint16 lowestRecPPEM; // Smallest readable size in pixels.
+ AutoSwap_PRInt16 fontDirectionHint;
+ AutoSwap_PRInt16 indexToLocFormat;
+ AutoSwap_PRInt16 glyphDataFormat;
+};
+
+struct OS2Table {
+ AutoSwap_PRUint16 version; // 0004 = OpenType 1.5
+ AutoSwap_PRInt16 xAvgCharWidth;
+ AutoSwap_PRUint16 usWeightClass;
+ AutoSwap_PRUint16 usWidthClass;
+ AutoSwap_PRUint16 fsType;
+ AutoSwap_PRInt16 ySubscriptXSize;
+ AutoSwap_PRInt16 ySubscriptYSize;
+ AutoSwap_PRInt16 ySubscriptXOffset;
+ AutoSwap_PRInt16 ySubscriptYOffset;
+ AutoSwap_PRInt16 ySuperscriptXSize;
+ AutoSwap_PRInt16 ySuperscriptYSize;
+ AutoSwap_PRInt16 ySuperscriptXOffset;
+ AutoSwap_PRInt16 ySuperscriptYOffset;
+ AutoSwap_PRInt16 yStrikeoutSize;
+ AutoSwap_PRInt16 yStrikeoutPosition;
+ AutoSwap_PRInt16 sFamilyClass;
+ uint8_t panose[10];
+ AutoSwap_PRUint32 unicodeRange1;
+ AutoSwap_PRUint32 unicodeRange2;
+ AutoSwap_PRUint32 unicodeRange3;
+ AutoSwap_PRUint32 unicodeRange4;
+ uint8_t achVendID[4];
+ AutoSwap_PRUint16 fsSelection;
+ AutoSwap_PRUint16 usFirstCharIndex;
+ AutoSwap_PRUint16 usLastCharIndex;
+ AutoSwap_PRInt16 sTypoAscender;
+ AutoSwap_PRInt16 sTypoDescender;
+ AutoSwap_PRInt16 sTypoLineGap;
+ AutoSwap_PRUint16 usWinAscent;
+ AutoSwap_PRUint16 usWinDescent;
+ AutoSwap_PRUint32 codePageRange1;
+ AutoSwap_PRUint32 codePageRange2;
+ AutoSwap_PRInt16 sxHeight;
+ AutoSwap_PRInt16 sCapHeight;
+ AutoSwap_PRUint16 usDefaultChar;
+ AutoSwap_PRUint16 usBreakChar;
+ AutoSwap_PRUint16 usMaxContext;
+};
+
+struct PostTable {
+ AutoSwap_PRUint32 version;
+ AutoSwap_PRInt32 italicAngle;
+ AutoSwap_PRInt16 underlinePosition;
+ AutoSwap_PRUint16 underlineThickness;
+ AutoSwap_PRUint32 isFixedPitch;
+ AutoSwap_PRUint32 minMemType42;
+ AutoSwap_PRUint32 maxMemType42;
+ AutoSwap_PRUint32 minMemType1;
+ AutoSwap_PRUint32 maxMemType1;
+};
+
+// This structure is used for both 'hhea' and 'vhea' tables.
+// The field names here are those of the horizontal version; the
+// vertical table just exchanges vertical and horizontal coordinates.
+struct MetricsHeader {
+ AutoSwap_PRUint32 version;
+ AutoSwap_PRInt16 ascender;
+ AutoSwap_PRInt16 descender;
+ AutoSwap_PRInt16 lineGap;
+ AutoSwap_PRUint16 advanceWidthMax;
+ AutoSwap_PRInt16 minLeftSideBearing;
+ AutoSwap_PRInt16 minRightSideBearing;
+ AutoSwap_PRInt16 xMaxExtent;
+ AutoSwap_PRInt16 caretSlopeRise;
+ AutoSwap_PRInt16 caretSlopeRun;
+ AutoSwap_PRInt16 caretOffset;
+ AutoSwap_PRInt16 reserved1;
+ AutoSwap_PRInt16 reserved2;
+ AutoSwap_PRInt16 reserved3;
+ AutoSwap_PRInt16 reserved4;
+ AutoSwap_PRInt16 metricDataFormat;
+ AutoSwap_PRUint16 numOfLongMetrics;
+};
+
+struct MaxpTableHeader {
+ AutoSwap_PRUint32 version; // CFF: 0x00005000; TrueType: 0x00010000
+ AutoSwap_PRUint16 numGlyphs;
+// truetype version has additional fields that we don't currently use
+};
+
+// old 'kern' table, supported on Windows
+// see http://www.microsoft.com/typography/otspec/kern.htm
+struct KernTableVersion0 {
+ AutoSwap_PRUint16 version; // 0x0000
+ AutoSwap_PRUint16 nTables;
+};
+
+struct KernTableSubtableHeaderVersion0 {
+ AutoSwap_PRUint16 version;
+ AutoSwap_PRUint16 length;
+ AutoSwap_PRUint16 coverage;
+};
+
+// newer Mac-only 'kern' table, ignored by Windows
+// see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6kern.html
+struct KernTableVersion1 {
+ AutoSwap_PRUint32 version; // 0x00010000
+ AutoSwap_PRUint32 nTables;
+};
+
+struct KernTableSubtableHeaderVersion1 {
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint16 coverage;
+ AutoSwap_PRUint16 tupleIndex;
+};
+
+struct COLRHeader {
+ AutoSwap_PRUint16 version;
+ AutoSwap_PRUint16 numBaseGlyphRecord;
+ AutoSwap_PRUint32 offsetBaseGlyphRecord;
+ AutoSwap_PRUint32 offsetLayerRecord;
+ AutoSwap_PRUint16 numLayerRecords;
+};
+
+struct CPALHeaderVersion0 {
+ AutoSwap_PRUint16 version;
+ AutoSwap_PRUint16 numPaletteEntries;
+ AutoSwap_PRUint16 numPalettes;
+ AutoSwap_PRUint16 numColorRecords;
+ AutoSwap_PRUint32 offsetFirstColorRecord;
+};
+
+#pragma pack()
+
+// Return just the highest bit of the given value, i.e., the highest
+// power of 2 that is <= value, or zero if the input value is zero.
+inline uint32_t
+FindHighestBit(uint32_t value)
+{
+ // propagate highest bit into all lower bits of the value
+ value |= (value >> 1);
+ value |= (value >> 2);
+ value |= (value >> 4);
+ value |= (value >> 8);
+ value |= (value >> 16);
+ // isolate the leftmost bit
+ return (value & ~(value >> 1));
+}
+
+} // namespace mozilla
+
+// used for overlaying name changes without touching original font data
+struct FontDataOverlay {
+ // overlaySrc != 0 ==> use overlay
+ uint32_t overlaySrc; // src offset from start of font data
+ uint32_t overlaySrcLen; // src length
+ uint32_t overlayDest; // dest offset from start of font data
+};
+
+enum gfxUserFontType {
+ GFX_USERFONT_UNKNOWN = 0,
+ GFX_USERFONT_OPENTYPE = 1,
+ GFX_USERFONT_SVG = 2,
+ GFX_USERFONT_WOFF = 3,
+ GFX_USERFONT_WOFF2 = 4
+};
+#define GFX_PREF_WOFF2_ENABLED "gfx.downloadable_fonts.woff2.enabled"
+
+extern const uint8_t sCJKCompatSVSTable[];
+
+class gfxFontUtils {
+
+public:
+ // these are public because gfxFont.cpp also looks into the name table
+ enum {
+ NAME_ID_FAMILY = 1,
+ NAME_ID_STYLE = 2,
+ NAME_ID_UNIQUE = 3,
+ NAME_ID_FULL = 4,
+ NAME_ID_VERSION = 5,
+ NAME_ID_POSTSCRIPT = 6,
+ NAME_ID_PREFERRED_FAMILY = 16,
+ NAME_ID_PREFERRED_STYLE = 17,
+
+ PLATFORM_ALL = -1,
+ PLATFORM_ID_UNICODE = 0, // Mac OS uses this typically
+ PLATFORM_ID_MAC = 1,
+ PLATFORM_ID_ISO = 2,
+ PLATFORM_ID_MICROSOFT = 3,
+
+ ENCODING_ID_MAC_ROMAN = 0, // traditional Mac OS script manager encodings
+ ENCODING_ID_MAC_JAPANESE = 1, // (there are others defined, but some were never
+ ENCODING_ID_MAC_TRAD_CHINESE = 2, // implemented by Apple, and I have never seen them
+ ENCODING_ID_MAC_KOREAN = 3, // used in font names)
+ ENCODING_ID_MAC_ARABIC = 4,
+ ENCODING_ID_MAC_HEBREW = 5,
+ ENCODING_ID_MAC_GREEK = 6,
+ ENCODING_ID_MAC_CYRILLIC = 7,
+ ENCODING_ID_MAC_DEVANAGARI = 9,
+ ENCODING_ID_MAC_GURMUKHI = 10,
+ ENCODING_ID_MAC_GUJARATI = 11,
+ ENCODING_ID_MAC_SIMP_CHINESE = 25,
+
+ ENCODING_ID_MICROSOFT_SYMBOL = 0, // Microsoft platform encoding IDs
+ ENCODING_ID_MICROSOFT_UNICODEBMP = 1,
+ ENCODING_ID_MICROSOFT_SHIFTJIS = 2,
+ ENCODING_ID_MICROSOFT_PRC = 3,
+ ENCODING_ID_MICROSOFT_BIG5 = 4,
+ ENCODING_ID_MICROSOFT_WANSUNG = 5,
+ ENCODING_ID_MICROSOFT_JOHAB = 6,
+ ENCODING_ID_MICROSOFT_UNICODEFULL = 10,
+
+ LANG_ALL = -1,
+ LANG_ID_MAC_ENGLISH = 0, // many others are defined, but most don't affect
+ LANG_ID_MAC_HEBREW = 10, // the charset; should check all the central/eastern
+ LANG_ID_MAC_JAPANESE = 11, // european codes, though
+ LANG_ID_MAC_ARABIC = 12,
+ LANG_ID_MAC_ICELANDIC = 15,
+ LANG_ID_MAC_TURKISH = 17,
+ LANG_ID_MAC_TRAD_CHINESE = 19,
+ LANG_ID_MAC_URDU = 20,
+ LANG_ID_MAC_KOREAN = 23,
+ LANG_ID_MAC_POLISH = 25,
+ LANG_ID_MAC_FARSI = 31,
+ LANG_ID_MAC_SIMP_CHINESE = 33,
+ LANG_ID_MAC_ROMANIAN = 37,
+ LANG_ID_MAC_CZECH = 38,
+ LANG_ID_MAC_SLOVAK = 39,
+
+ LANG_ID_MICROSOFT_EN_US = 0x0409, // with Microsoft platformID, EN US lang code
+
+ CMAP_MAX_CODEPOINT = 0x10ffff // maximum possible Unicode codepoint
+ // contained in a cmap
+ };
+
+ // name table has a header, followed by name records, followed by string data
+ struct NameHeader {
+ mozilla::AutoSwap_PRUint16 format; // Format selector (=0).
+ mozilla::AutoSwap_PRUint16 count; // Number of name records.
+ mozilla::AutoSwap_PRUint16 stringOffset; // Offset to start of string storage
+ // (from start of table)
+ };
+
+ struct NameRecord {
+ mozilla::AutoSwap_PRUint16 platformID; // Platform ID
+ mozilla::AutoSwap_PRUint16 encodingID; // Platform-specific encoding ID
+ mozilla::AutoSwap_PRUint16 languageID; // Language ID
+ mozilla::AutoSwap_PRUint16 nameID; // Name ID.
+ mozilla::AutoSwap_PRUint16 length; // String length (in bytes).
+ mozilla::AutoSwap_PRUint16 offset; // String offset from start of storage
+ // (in bytes).
+ };
+
+ // for reading big-endian font data on either big or little-endian platforms
+
+ static inline uint16_t
+ ReadShortAt(const uint8_t *aBuf, uint32_t aIndex)
+ {
+ return (aBuf[aIndex] << 8) | aBuf[aIndex + 1];
+ }
+
+ static inline uint16_t
+ ReadShortAt16(const uint16_t *aBuf, uint32_t aIndex)
+ {
+ const uint8_t *buf = reinterpret_cast<const uint8_t*>(aBuf);
+ uint32_t index = aIndex << 1;
+ return (buf[index] << 8) | buf[index+1];
+ }
+
+ static inline uint32_t
+ ReadUint24At(const uint8_t *aBuf, uint32_t aIndex)
+ {
+ return ((aBuf[aIndex] << 16) | (aBuf[aIndex + 1] << 8) |
+ (aBuf[aIndex + 2]));
+ }
+
+ static inline uint32_t
+ ReadLongAt(const uint8_t *aBuf, uint32_t aIndex)
+ {
+ return ((aBuf[aIndex] << 24) | (aBuf[aIndex + 1] << 16) |
+ (aBuf[aIndex + 2] << 8) | (aBuf[aIndex + 3]));
+ }
+
+ static nsresult
+ ReadCMAPTableFormat10(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap);
+
+ static nsresult
+ ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap);
+
+ static nsresult
+ ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength,
+ gfxSparseBitSet& aCharacterMap);
+
+ static nsresult
+ ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength,
+ mozilla::UniquePtr<uint8_t[]>& aTable);
+
+ static uint32_t
+ FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength,
+ uint32_t *aTableOffset, uint32_t *aUVSTableOffset,
+ bool *aSymbolEncoding);
+
+ static nsresult
+ ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength,
+ gfxSparseBitSet& aCharacterMap,
+ uint32_t& aUVSOffset,
+ bool& aUnicodeFont, bool& aSymbolFont);
+
+ static uint32_t
+ MapCharToGlyphFormat4(const uint8_t *aBuf, uint32_t aLength, char16_t aCh);
+
+ static uint32_t
+ MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh);
+
+ static uint32_t
+ MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh);
+
+ static uint16_t
+ MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS);
+
+ // sCJKCompatSVSTable is a 'cmap' format 14 subtable that maps
+ // <char + var-selector> pairs to the corresponding Unicode
+ // compatibility ideograph codepoints.
+ static MOZ_ALWAYS_INLINE uint32_t
+ GetUVSFallback(uint32_t aCh, uint32_t aVS) {
+ aCh = MapUVSToGlyphFormat14(sCJKCompatSVSTable, aCh, aVS);
+ return aCh >= 0xFB00 ? aCh + (0x2F800 - 0xFB00) : aCh;
+ }
+
+ static uint32_t
+ MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength,
+ uint32_t aUnicode, uint32_t aVarSelector = 0);
+
+#ifdef XP_WIN
+ // determine whether a font (which has already been sanitized, so is known
+ // to be a valid sfnt) is CFF format rather than TrueType
+ static bool
+ IsCffFont(const uint8_t* aFontData);
+#endif
+
+ // determine the format of font data
+ static gfxUserFontType
+ DetermineFontDataType(const uint8_t *aFontData, uint32_t aFontDataLength);
+
+ // Read the fullname from the sfnt data (used to save the original name
+ // prior to renaming the font for installation).
+ // This is called with sfnt data that has already been validated,
+ // so it should always succeed in finding the name table.
+ static nsresult
+ GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength,
+ nsAString& aFullName);
+
+ // helper to get fullname from name table, constructing from family+style
+ // if no explicit fullname is present
+ static nsresult
+ GetFullNameFromTable(hb_blob_t *aNameTable,
+ nsAString& aFullName);
+
+ // helper to get family name from name table
+ static nsresult
+ GetFamilyNameFromTable(hb_blob_t *aNameTable,
+ nsAString& aFamilyName);
+
+ // Find the table directory entry for a given table tag, in a (validated)
+ // buffer of 'sfnt' data. Returns null if the tag is not present.
+ static mozilla::TableDirEntry*
+ FindTableDirEntry(const void* aFontData, uint32_t aTableTag);
+
+ // Return a blob that wraps a table found within a buffer of font data.
+ // The blob does NOT own its data; caller guarantees that the buffer
+ // will remain valid at least as long as the blob.
+ // Returns null if the specified table is not found.
+ // This method assumes aFontData is valid 'sfnt' data; before using this,
+ // caller is responsible to do any sanitization/validation necessary.
+ static hb_blob_t*
+ GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
+
+ // create a new name table and build a new font with that name table
+ // appended on the end, returns true on success
+ static nsresult
+ RenameFont(const nsAString& aName, const uint8_t *aFontData,
+ uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont);
+
+ // read all names matching aNameID, returning in aNames array
+ static nsresult
+ ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
+ int32_t aPlatformID, nsTArray<nsString>& aNames);
+
+ // reads English or first name matching aNameID, returning in aName
+ // platform based on OS
+ static nsresult
+ ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
+ nsString& aName);
+
+ static nsresult
+ ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
+ uint32_t aNameID, nsString& aName);
+
+ // convert a name from the raw name table data into an nsString,
+ // provided we know how; return true if successful, or false
+ // if we can't handle the encoding
+ static bool
+ DecodeFontName(const char *aBuf, int32_t aLength,
+ uint32_t aPlatformCode, uint32_t aScriptCode,
+ uint32_t aLangCode, nsAString& dest);
+
+ static inline bool IsJoinCauser(uint32_t ch) {
+ return (ch == 0x200D);
+ }
+
+ static inline bool IsJoinControl(uint32_t ch) {
+ return (ch == 0x200C || ch == 0x200D);
+ }
+
+ enum {
+ kUnicodeVS1 = 0xFE00,
+ kUnicodeVS16 = 0xFE0F,
+ kUnicodeVS17 = 0xE0100,
+ kUnicodeVS256 = 0xE01EF
+ };
+
+ static inline bool IsVarSelector(uint32_t ch) {
+ return (ch >= kUnicodeVS1 && ch <= kUnicodeVS16) ||
+ (ch >= kUnicodeVS17 && ch <= kUnicodeVS256);
+ }
+
+ enum {
+ kUnicodeRegionalIndicatorA = 0x1F1E6,
+ kUnicodeRegionalIndicatorZ = 0x1F1FF
+ };
+
+ static inline bool IsRegionalIndicator(uint32_t aCh) {
+ return aCh >= kUnicodeRegionalIndicatorA &&
+ aCh <= kUnicodeRegionalIndicatorZ;
+ }
+
+ static inline bool IsInvalid(uint32_t ch) {
+ return (ch == 0xFFFD);
+ }
+
+ // Font code may want to know if there is the potential for bidi behavior
+ // to be triggered by any of the characters in a text run; this can be
+ // used to test that possibility.
+ enum {
+ kUnicodeBidiScriptsStart = 0x0590,
+ kUnicodeBidiScriptsEnd = 0x08FF,
+ kUnicodeBidiPresentationStart = 0xFB1D,
+ kUnicodeBidiPresentationEnd = 0xFEFC,
+ kUnicodeFirstHighSurrogateBlock = 0xD800,
+ kUnicodeRLM = 0x200F,
+ kUnicodeRLE = 0x202B,
+ kUnicodeRLO = 0x202E
+ };
+
+ static inline bool PotentialRTLChar(char16_t aCh) {
+ if (aCh >= kUnicodeBidiScriptsStart && aCh <= kUnicodeBidiScriptsEnd)
+ // bidi scripts Hebrew, Arabic, Syriac, Thaana, N'Ko are all encoded together
+ return true;
+
+ if (aCh == kUnicodeRLM || aCh == kUnicodeRLE || aCh == kUnicodeRLO)
+ // directional controls that trigger bidi layout
+ return true;
+
+ if (aCh >= kUnicodeBidiPresentationStart &&
+ aCh <= kUnicodeBidiPresentationEnd)
+ // presentation forms of Arabic and Hebrew letters
+ return true;
+
+ if ((aCh & 0xFF00) == kUnicodeFirstHighSurrogateBlock)
+ // surrogate that could be part of a bidi supplementary char
+ // (Cypriot, Aramaic, Phoenecian, etc)
+ return true;
+
+ // otherwise we know this char cannot trigger bidi reordering
+ return false;
+ }
+
+ // parse a simple list of font family names into
+ // an array of strings
+ static void ParseFontList(const nsAString& aFamilyList,
+ nsTArray<nsString>& aFontList);
+
+ // for a given font list pref name, append list of font names
+ static void AppendPrefsFontList(const char *aPrefName,
+ nsTArray<nsString>& aFontList);
+
+ // for a given font list pref name, initialize a list of font names
+ static void GetPrefsFontList(const char *aPrefName,
+ nsTArray<nsString>& aFontList);
+
+ // generate a unique font name
+ static nsresult MakeUniqueUserFontName(nsAString& aName);
+
+ // for color layer from glyph using COLR and CPAL tables
+ static bool ValidateColorGlyphs(hb_blob_t* aCOLR, hb_blob_t* aCPAL);
+ static bool GetColorGlyphLayers(hb_blob_t* aCOLR,
+ hb_blob_t* aCPAL,
+ uint32_t aGlyphId,
+ const mozilla::gfx::Color& aDefaultColor,
+ nsTArray<uint16_t> &aGlyphs,
+ nsTArray<mozilla::gfx::Color> &aColors);
+
+protected:
+ friend struct MacCharsetMappingComparator;
+
+ static nsresult
+ ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
+ int32_t aLangID, int32_t aPlatformID, nsTArray<nsString>& aNames);
+
+ // convert opentype name-table platform/encoding/language values to a charset name
+ // we can use to convert the name data to unicode, or "" if data is UTF16BE
+ static const char*
+ GetCharsetForFontName(uint16_t aPlatform, uint16_t aScript, uint16_t aLanguage);
+
+ struct MacFontNameCharsetMapping {
+ uint16_t mEncoding;
+ uint16_t mLanguage;
+ const char *mCharsetName;
+
+ bool operator<(const MacFontNameCharsetMapping& rhs) const {
+ return (mEncoding < rhs.mEncoding) ||
+ ((mEncoding == rhs.mEncoding) && (mLanguage < rhs.mLanguage));
+ }
+ };
+ static const MacFontNameCharsetMapping gMacFontNameCharsets[];
+ static const char* gISOFontNameCharsets[];
+ static const char* gMSFontNameCharsets[];
+};
+
+#endif /* GFX_FONT_UTILS_H */
diff --git a/system/graphics/thebes/gfxFontconfigFonts.cpp b/system/graphics/thebes/gfxFontconfigFonts.cpp
new file mode 100644
index 000000000..9caecc4c0
--- /dev/null
+++ b/system/graphics/thebes/gfxFontconfigFonts.cpp
@@ -0,0 +1,2255 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlink.h"
+#include "gfxTypes.h"
+
+#include "nsTArray.h"
+
+#include "gfxContext.h"
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+#include "gfxFontconfigFonts.h"
+#include "gfxFT2FontBase.h"
+#include "gfxFT2Utils.h"
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-glib.h"
+#include "harfbuzz/hb-ot.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeScriptCodes.h"
+#include "gfxFontconfigUtils.h"
+#include "gfxUserFontSet.h"
+#include "gfxFontConstants.h"
+#include "nsGkAtoms.h"
+#include "nsILanguageAtomService.h"
+#include "nsServiceManagerUtils.h"
+
+#include <cairo.h>
+#include <cairo-ft.h>
+#include "mozilla/gfx/HelpersCairo.h"
+
+#include <fontconfig/fcfreetype.h>
+#include <pango/pango.h>
+
+#include FT_TRUETYPE_TABLES_H
+
+#ifdef MOZ_WIDGET_GTK
+#include <gdk/gdk.h>
+#endif
+
+#include <math.h>
+
+using namespace mozilla;
+using namespace mozilla::unicode;
+
+#define PRINTING_FC_PROPERTY "gfx.printing"
+
+static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
+
+static cairo_scaled_font_t *
+CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
+
+static FT_Library gFTLibrary;
+
+// FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97
+// and so fontconfig-2.3.0 (2005).
+#ifndef FC_FAMILYLANG
+#define FC_FAMILYLANG "familylang"
+#endif
+#ifndef FC_FULLNAME
+#define FC_FULLNAME "fullname"
+#endif
+
+static PRFuncPtr
+FindFunctionSymbol(const char *name)
+{
+ PRLibrary *lib = nullptr;
+ PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(name, &lib);
+ if (lib) {
+ PR_UnloadLibrary(lib);
+ }
+
+ return result;
+}
+
+static bool HasChar(FcPattern *aFont, FcChar32 wc)
+{
+ FcCharSet *charset = nullptr;
+ FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
+
+ return charset && FcCharSetHasChar(charset, wc);
+}
+
+/**
+ * gfxFcFontEntry:
+ *
+ * An abstract base class of for gfxFontEntry implementations used by
+ * gfxFcFont and gfxUserFontSet.
+ */
+
+class gfxFcFontEntry : public gfxFontEntry {
+public:
+ // For all FontEntrys attached to gfxFcFonts, there will be only one
+ // pattern in this array. This is always a font pattern, not a fully
+ // resolved pattern. gfxFcFont only uses this to construct a PangoFont.
+ //
+ // FontEntrys for src:local() fonts in gfxUserFontSet may return more than
+ // one pattern. (See comment in gfxUserFcFontEntry.)
+ const nsTArray< nsCountedRef<FcPattern> >& GetPatterns()
+ {
+ return mPatterns;
+ }
+
+ static gfxFcFontEntry *LookupFontEntry(cairo_font_face_t *aFace)
+ {
+ return static_cast<gfxFcFontEntry*>
+ (cairo_font_face_get_user_data(aFace, &sFontEntryKey));
+ }
+
+ // override the gfxFontEntry impl to read the name from fontconfig
+ // instead of trying to get the 'name' table, as we don't implement
+ // GetFontTable() here
+ virtual nsString RealFaceName();
+
+ // This is needed to make gfxFontEntry::HasCharacter(aCh) work.
+ virtual bool TestCharacterMap(uint32_t aCh)
+ {
+ for (uint32_t i = 0; i < mPatterns.Length(); ++i) {
+ if (HasChar(mPatterns[i], aCh)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+protected:
+ explicit gfxFcFontEntry(const nsAString& aName)
+ : gfxFontEntry(aName)
+ {
+ }
+
+ // One pattern is the common case and some subclasses rely on successful
+ // addition of the first element to the array.
+ AutoTArray<nsCountedRef<FcPattern>,1> mPatterns;
+
+ static cairo_user_data_key_t sFontEntryKey;
+};
+
+cairo_user_data_key_t gfxFcFontEntry::sFontEntryKey;
+
+nsString
+gfxFcFontEntry::RealFaceName()
+{
+ FcChar8 *name;
+ if (!mPatterns.IsEmpty()) {
+ if (FcPatternGetString(mPatterns[0],
+ FC_FULLNAME, 0, &name) == FcResultMatch) {
+ return NS_ConvertUTF8toUTF16((const char*)name);
+ }
+ if (FcPatternGetString(mPatterns[0],
+ FC_FAMILY, 0, &name) == FcResultMatch) {
+ NS_ConvertUTF8toUTF16 result((const char*)name);
+ if (FcPatternGetString(mPatterns[0],
+ FC_STYLE, 0, &name) == FcResultMatch) {
+ result.Append(' ');
+ AppendUTF8toUTF16((const char*)name, result);
+ }
+ return result;
+ }
+ }
+ // fall back to gfxFontEntry implementation (only works for sfnt fonts)
+ return gfxFontEntry::RealFaceName();
+}
+
+/**
+ * gfxSystemFcFontEntry:
+ *
+ * An implementation of gfxFcFontEntry used by gfxFcFonts for system fonts,
+ * including those from regular family-name based font selection as well as
+ * those from src:local().
+ *
+ * All gfxFcFonts using the same cairo_font_face_t share the same FontEntry.
+ */
+
+class gfxSystemFcFontEntry : public gfxFcFontEntry {
+public:
+ // For memory efficiency, aFontPattern should be a font pattern,
+ // not a fully resolved pattern.
+ gfxSystemFcFontEntry(cairo_font_face_t *aFontFace,
+ FcPattern *aFontPattern,
+ const nsAString& aName)
+ : gfxFcFontEntry(aName), mFontFace(aFontFace),
+ mFTFace(nullptr), mFTFaceInitialized(false)
+ {
+ cairo_font_face_reference(mFontFace);
+ cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, this, nullptr);
+
+ // mPatterns is an AutoTArray with 1 space always available, so the
+ // AppendElement always succeeds.
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[0] = aFontPattern;
+
+ FcChar8 *name;
+ if (FcPatternGetString(aFontPattern,
+ FC_FAMILY, 0, &name) == FcResultMatch) {
+ mFamilyName = NS_ConvertUTF8toUTF16((const char*)name);
+ }
+ }
+
+ ~gfxSystemFcFontEntry()
+ {
+ cairo_font_face_set_user_data(mFontFace,
+ &sFontEntryKey,
+ nullptr,
+ nullptr);
+ cairo_font_face_destroy(mFontFace);
+ }
+
+ virtual void ForgetHBFace() override;
+ virtual void ReleaseGrFace(gr_face* aFace) override;
+
+protected:
+ virtual nsresult
+ CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer) override;
+
+ void MaybeReleaseFTFace();
+
+private:
+ cairo_font_face_t *mFontFace;
+ FT_Face mFTFace;
+ bool mFTFaceInitialized;
+};
+
+nsresult
+gfxSystemFcFontEntry::CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer)
+{
+ if (!mFTFaceInitialized) {
+ mFTFaceInitialized = true;
+ FcChar8 *filename;
+ if (FcPatternGetString(mPatterns[0], FC_FILE, 0, &filename) != FcResultMatch) {
+ return NS_ERROR_FAILURE;
+ }
+ int index;
+ if (FcPatternGetInteger(mPatterns[0], FC_INDEX, 0, &index) != FcResultMatch) {
+ index = 0; // default to 0 if not found in pattern
+ }
+ if (FT_New_Face(gfxPangoFontGroup::GetFTLibrary(),
+ (const char*)filename, index, &mFTFace) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (!mFTFace) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ FT_ULong length = 0;
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ if (!aBuffer.SetLength(length, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
+ aBuffer.Clear();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+gfxSystemFcFontEntry::MaybeReleaseFTFace()
+{
+ // don't release if either HB or Gr face still exists
+ if (mHBFace || mGrFace) {
+ return;
+ }
+ if (mFTFace) {
+ FT_Done_Face(mFTFace);
+ mFTFace = nullptr;
+ }
+ mFTFaceInitialized = false;
+}
+
+void
+gfxSystemFcFontEntry::ForgetHBFace()
+{
+ gfxFontEntry::ForgetHBFace();
+ MaybeReleaseFTFace();
+}
+
+void
+gfxSystemFcFontEntry::ReleaseGrFace(gr_face* aFace)
+{
+ gfxFontEntry::ReleaseGrFace(aFace);
+ MaybeReleaseFTFace();
+}
+
+// A namespace for @font-face family names in FcPatterns so that fontconfig
+// aliases do not pick up families from @font-face rules and so that
+// fontconfig rules can distinguish between web fonts and platform fonts.
+// http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html
+#define FONT_FACE_FAMILY_PREFIX "@font-face:"
+
+/**
+ * gfxUserFcFontEntry:
+ *
+ * An abstract class for objects in a gfxUserFontSet that can provide
+ * FcPattern* handles to fonts.
+ *
+ * Separate implementations of this class support local fonts from src:local()
+ * and web fonts from src:url().
+ */
+
+// There is a one-to-one correspondence between gfxUserFcFontEntry objects and
+// @font-face rules, but sometimes a one-to-many correspondence between font
+// entries and font patterns.
+//
+// http://www.w3.org/TR/2002/WD-css3-webfonts-20020802#font-descriptions
+// provided a font-size descriptor to specify the sizes supported by the face,
+// but the "Editor's Draft 27 June 2008"
+// http://dev.w3.org/csswg/css3-fonts/#font-resources does not provide such a
+// descriptor, and Mozilla does not recognize such a descriptor.
+//
+// Font face names used in src:local() also do not usually specify a size.
+//
+// PCF format fonts have each size in a different file, and each of these
+// files is referenced by its own pattern, but really these are each
+// different sizes of one face with one name.
+//
+// Multiple patterns in an entry also effectively deals with a set of
+// PostScript Type 1 font files that all have the same face name but are in
+// several files because of the limit on the number of glyphs in a Type 1 font
+// file. (e.g. Computer Modern.)
+
+class gfxUserFcFontEntry : public gfxFcFontEntry {
+protected:
+ explicit gfxUserFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+ : gfxFcFontEntry(aFontName)
+ {
+ mStyle = aStyle;
+ mWeight = aWeight;
+ mStretch = aStretch;
+ }
+
+ // Helper function to change a pattern so that it matches the CSS style
+ // descriptors and so gets properly sorted in font selection. This also
+ // avoids synthetic style effects being added by the renderer when the
+ // style of the font itself does not match the descriptor provided by the
+ // author.
+ void AdjustPatternToCSS(FcPattern *aPattern);
+};
+
+void
+gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern)
+{
+ int fontWeight = -1;
+ FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &fontWeight);
+ int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight / 100);
+ if (cssWeight != fontWeight) {
+ FcPatternDel(aPattern, FC_WEIGHT);
+ FcPatternAddInteger(aPattern, FC_WEIGHT, cssWeight);
+ }
+
+ int fontSlant;
+ FcResult res = FcPatternGetInteger(aPattern, FC_SLANT, 0, &fontSlant);
+ // gfxFontEntry doesn't understand the difference between oblique
+ // and italic.
+ if (res != FcResultMatch ||
+ IsItalic() != (fontSlant != FC_SLANT_ROMAN)) {
+ FcPatternDel(aPattern, FC_SLANT);
+ FcPatternAddInteger(aPattern, FC_SLANT,
+ IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN);
+ }
+
+ int fontWidth = -1;
+ FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth);
+ int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch);
+ if (cssWidth != fontWidth) {
+ FcPatternDel(aPattern, FC_WIDTH);
+ FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth);
+ }
+
+ // Ensure that there is a fullname property (if there is a family
+ // property) so that fontconfig rules can identify the real name of the
+ // font, because the family property will be replaced.
+ FcChar8 *unused;
+ if (FcPatternGetString(aPattern,
+ FC_FULLNAME, 0, &unused) == FcResultNoMatch) {
+ nsAutoCString fullname;
+ if (gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(aPattern,
+ &fullname)) {
+ FcPatternAddString(aPattern, FC_FULLNAME,
+ gfxFontconfigUtils::ToFcChar8(fullname));
+ }
+ }
+
+ nsAutoCString family;
+ family.Append(FONT_FACE_FAMILY_PREFIX);
+ AppendUTF16toUTF8(Name(), family);
+
+ FcPatternDel(aPattern, FC_FAMILY);
+ FcPatternDel(aPattern, FC_FAMILYLANG);
+ FcPatternAddString(aPattern, FC_FAMILY,
+ gfxFontconfigUtils::ToFcChar8(family));
+}
+
+/**
+ * gfxLocalFcFontEntry:
+ *
+ * An implementation of gfxUserFcFontEntry for local fonts from src:local().
+ *
+ * This class is used only in gfxUserFontSet and for providing FcPattern*
+ * handles to system fonts for font selection. gfxFcFonts created from these
+ * patterns will use gfxSystemFcFontEntrys, which may be shared with
+ * gfxFcFonts from regular family-name based font selection.
+ */
+
+class gfxLocalFcFontEntry : public gfxUserFcFontEntry {
+public:
+ gfxLocalFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const nsTArray< nsCountedRef<FcPattern> >& aPatterns)
+ : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle)
+ {
+ if (!mPatterns.SetCapacity(aPatterns.Length(), fallible))
+ return; // OOM
+
+ for (uint32_t i = 0; i < aPatterns.Length(); ++i) {
+ FcPattern *pattern = FcPatternDuplicate(aPatterns.ElementAt(i));
+ if (!pattern)
+ return; // OOM
+
+ AdjustPatternToCSS(pattern);
+
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[i].own(pattern);
+ }
+ mIsLocalUserFont = true;
+ }
+};
+
+/**
+ * gfxDownloadedFcFontEntry:
+ *
+ * An implementation of gfxFcFontEntry for web fonts from src:url().
+ *
+ * When a cairo_font_face_t is created for these fonts, the cairo_font_face_t
+ * keeps a reference to the FontEntry to keep the font data alive.
+ */
+
+class gfxDownloadedFcFontEntry : public gfxUserFcFontEntry {
+public:
+ // This takes ownership of the face and its underlying data
+ gfxDownloadedFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t *aData, FT_Face aFace)
+ : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle),
+ mFontData(aData), mFace(aFace)
+ {
+ NS_PRECONDITION(aFace != nullptr, "aFace is NULL!");
+ mIsDataUserFont = true;
+ InitPattern();
+ }
+
+ virtual ~gfxDownloadedFcFontEntry();
+
+ // Returns true on success
+ bool SetCairoFace(cairo_font_face_t *aFace);
+
+ virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
+
+protected:
+ void InitPattern();
+
+ // mFontData holds the data used to instantiate the FT_Face;
+ // this has to persist until we are finished with the face,
+ // then be released with free().
+ const uint8_t* mFontData;
+
+ FT_Face mFace;
+};
+
+// A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
+static const char *kFontEntryFcProp = "-moz-font-entry";
+
+static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
+ gfxDownloadedFcFontEntry *aFontEntry)
+{
+ FcValue value;
+ value.type = FcTypeFTFace; // void* field of union
+ value.u.f = aFontEntry;
+
+ return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse);
+}
+
+static FcBool DelDownloadedFontEntry(FcPattern *aPattern)
+{
+ return FcPatternDel(aPattern, kFontEntryFcProp);
+}
+
+static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern)
+{
+ FcValue value;
+ if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch)
+ return nullptr;
+
+ if (value.type != FcTypeFTFace) {
+ NS_NOTREACHED("Wrong type for -moz-font-entry font property");
+ return nullptr;
+ }
+
+ return static_cast<gfxDownloadedFcFontEntry*>(value.u.f);
+}
+
+gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry()
+{
+ if (mPatterns.Length() != 0) {
+ // Remove back reference to this font entry and the face in case
+ // anyone holds a reference to the pattern.
+ NS_ASSERTION(mPatterns.Length() == 1,
+ "More than one pattern in gfxDownloadedFcFontEntry!");
+ DelDownloadedFontEntry(mPatterns[0]);
+ FcPatternDel(mPatterns[0], FC_FT_FACE);
+ }
+ FT_Done_Face(mFace);
+ free((void*)mFontData);
+}
+
+typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
+ const FcChar8 *file, int id,
+ FcBlanks *blanks);
+
+void
+gfxDownloadedFcFontEntry::InitPattern()
+{
+ static QueryFaceFunction sQueryFacePtr =
+ reinterpret_cast<QueryFaceFunction>
+ (FindFunctionSymbol("FcFreeTypeQueryFace"));
+ FcPattern *pattern;
+
+ // FcFreeTypeQueryFace is the same function used to construct patterns for
+ // system fonts and so is the preferred function to use for this purpose.
+ // This will set up the langset property, which helps with sorting, and
+ // the foundry, fullname, and fontversion properties, which properly
+ // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is
+ // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has
+ // fontconfig-2.4.1.)
+ if (sQueryFacePtr) {
+ // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
+ // least). The dummy file passed here is removed below.
+ //
+ // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
+ // is passed as the "blanks" argument, which provides that unexpectedly
+ // blank glyphs are elided. Here, however, we pass nullptr for
+ // "blanks", effectively assuming that, if the font has a blank glyph,
+ // then the author intends any associated character to be rendered
+ // blank.
+ pattern =
+ (*sQueryFacePtr)(mFace,
+ gfxFontconfigUtils::ToFcChar8(""),
+ 0,
+ nullptr);
+ if (!pattern)
+ // Either OOM, or fontconfig chose to skip this font because it
+ // has "no encoded characters", which I think means "BDF and PCF
+ // fonts which are not in Unicode (or the effectively equivalent
+ // ISO Latin-1) encoding".
+ return;
+
+ // These properties don't make sense for this face without a file.
+ FcPatternDel(pattern, FC_FILE);
+ FcPatternDel(pattern, FC_INDEX);
+
+ } else {
+ // Do the minimum necessary to construct a pattern for sorting.
+
+ // FC_CHARSET is vital to determine which characters are supported.
+ nsAutoRef<FcCharSet> charset(FcFreeTypeCharSet(mFace, nullptr));
+ // If there are no characters then assume we don't know how to read
+ // this font.
+ if (!charset || FcCharSetCount(charset) == 0)
+ return;
+
+ pattern = FcPatternCreate();
+ FcPatternAddCharSet(pattern, FC_CHARSET, charset);
+
+ // FC_PIXEL_SIZE can be important for font selection of fixed-size
+ // fonts.
+ if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) {
+ for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) {
+#if HAVE_FT_BITMAP_SIZE_Y_PPEM
+ double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem);
+#else
+ double size = mFace->available_sizes[i].height;
+#endif
+ FcPatternAddDouble (pattern, FC_PIXEL_SIZE, size);
+ }
+
+ // Not sure whether this is important;
+ // imitating FcFreeTypeQueryFace:
+ FcPatternAddBool (pattern, FC_ANTIALIAS, FcFalse);
+ }
+
+ // Setting up the FC_LANGSET property is very difficult with the APIs
+ // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET
+ // property seems better than having a property with an empty LangSet.
+ // With no FC_LANGSET property, fontconfig sort functions will
+ // consider this face to have the same priority as (otherwise equal)
+ // faces that have support for the primary requested language, but
+ // will not consider any language to have been satisfied (and so will
+ // continue to look for a face with language support in fallback
+ // fonts).
+ }
+
+ AdjustPatternToCSS(pattern);
+
+ FcPatternAddFTFace(pattern, FC_FT_FACE, mFace);
+ AddDownloadedFontEntry(pattern, this);
+
+ // There is never more than one pattern
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[0].own(pattern);
+}
+
+static void ReleaseDownloadedFontEntry(void *data)
+{
+ gfxDownloadedFcFontEntry *downloadedFontEntry =
+ static_cast<gfxDownloadedFcFontEntry*>(data);
+ NS_RELEASE(downloadedFontEntry);
+}
+
+bool gfxDownloadedFcFontEntry::SetCairoFace(cairo_font_face_t *aFace)
+{
+ if (CAIRO_STATUS_SUCCESS !=
+ cairo_font_face_set_user_data(aFace, &sFontEntryKey, this,
+ ReleaseDownloadedFontEntry))
+ return false;
+
+ // Hold a reference to this font entry to keep the font face data.
+ NS_ADDREF(this);
+ return true;
+}
+
+hb_blob_t *
+gfxDownloadedFcFontEntry::GetFontTable(uint32_t aTableTag)
+{
+ // The entry already owns the (sanitized) sfnt data in mFontData,
+ // so we can just return a blob that "wraps" the appropriate chunk of it.
+ // The blob should not attempt to free its data, as the entire sfnt data
+ // will be freed when the font entry is deleted.
+ return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
+}
+
+/*
+ * gfxFcFont
+ *
+ * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
+ * cairo_scaled_font created from an FcPattern.
+ */
+
+class gfxFcFont : public gfxFontconfigFontBase {
+public:
+ virtual ~gfxFcFont();
+ static already_AddRefed<gfxFcFont>
+ GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+ const gfxFontStyle *aFontStyle);
+
+ // return a cloned font resized and offset to simulate sub/superscript glyphs
+ virtual already_AddRefed<gfxFont>
+ GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) override;
+
+protected:
+ virtual already_AddRefed<gfxFont> MakeScaledFont(gfxFontStyle *aFontStyle,
+ gfxFloat aFontScale);
+ virtual already_AddRefed<gfxFont> GetSmallCapsFont() override;
+
+private:
+ gfxFcFont(cairo_scaled_font_t *aCairoFont,
+ FcPattern *aPattern,
+ gfxFcFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle);
+
+ // key for locating a gfxFcFont corresponding to a cairo_scaled_font
+ static cairo_user_data_key_t sGfxFontKey;
+};
+
+/**
+ * gfxFcFontSet:
+ *
+ * Translation from a desired FcPattern to a sorted set of font references
+ * (fontconfig cache data) and (when needed) fonts.
+ */
+
+class gfxFcFontSet final {
+public:
+ NS_INLINE_DECL_REFCOUNTING(gfxFcFontSet)
+
+ explicit gfxFcFontSet(FcPattern *aPattern,
+ gfxUserFontSet *aUserFontSet)
+ : mSortPattern(aPattern), mUserFontSet(aUserFontSet),
+ mFcFontsTrimmed(0),
+ mHaveFallbackFonts(false)
+ {
+ bool waitForUserFont;
+ mFcFontSet = SortPreferredFonts(waitForUserFont);
+ mWaitingForUserFont = waitForUserFont;
+ }
+
+ // A reference is held by the FontSet.
+ // The caller may add a ref to keep the font alive longer than the FontSet.
+ gfxFcFont *GetFontAt(uint32_t i, const gfxFontStyle *aFontStyle)
+ {
+ if (i >= mFonts.Length() || !mFonts[i].mFont) {
+ // GetFontPatternAt sets up mFonts
+ FcPattern *fontPattern = GetFontPatternAt(i);
+ if (!fontPattern)
+ return nullptr;
+
+ mFonts[i].mFont =
+ gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern,
+ aFontStyle);
+ }
+ return mFonts[i].mFont;
+ }
+
+ FcPattern *GetFontPatternAt(uint32_t i);
+
+ bool WaitingForUserFont() const {
+ return mWaitingForUserFont;
+ }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~gfxFcFontSet()
+ {
+ }
+
+ nsReturnRef<FcFontSet> SortPreferredFonts(bool& aWaitForUserFont);
+ nsReturnRef<FcFontSet> SortFallbackFonts();
+
+ struct FontEntry {
+ explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
+ nsCountedRef<FcPattern> mPattern;
+ RefPtr<gfxFcFont> mFont;
+ };
+
+ struct LangSupportEntry {
+ LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
+ mLang(aLang), mBestSupport(aSupport) {}
+ FcChar8 *mLang;
+ FcLangResult mBestSupport;
+ };
+
+public:
+ // public for nsTArray
+ class LangComparator {
+ public:
+ bool Equals(const LangSupportEntry& a, const FcChar8 *b) const
+ {
+ return FcStrCmpIgnoreCase(a.mLang, b) == 0;
+ }
+ };
+
+private:
+ // The requested pattern
+ nsCountedRef<FcPattern> mSortPattern;
+ // Fonts from @font-face rules
+ RefPtr<gfxUserFontSet> mUserFontSet;
+ // A (trimmed) list of font patterns and fonts that is built up as
+ // required.
+ nsTArray<FontEntry> mFonts;
+ // Holds a list of font patterns that will be trimmed. This is first set
+ // to a list of preferred fonts. Then, if/when all the preferred fonts
+ // have been trimmed and added to mFonts, this is set to a list of
+ // fallback fonts.
+ nsAutoRef<FcFontSet> mFcFontSet;
+ // The set of characters supported by the fonts in mFonts.
+ nsAutoRef<FcCharSet> mCharSet;
+ // The index of the next font in mFcFontSet that has not yet been
+ // considered for mFonts.
+ int mFcFontsTrimmed;
+ // True iff fallback fonts are either stored in mFcFontSet or have been
+ // trimmed and added to mFonts (so that mFcFontSet is nullptr).
+ bool mHaveFallbackFonts;
+ // True iff there was a user font set with pending downloads,
+ // so the set may be updated when downloads complete
+ bool mWaitingForUserFont;
+};
+
+// Find the FcPattern for an @font-face font suitable for CSS family |aFamily|
+// and style |aStyle| properties.
+static const nsTArray< nsCountedRef<FcPattern> >*
+FindFontPatterns(gfxUserFontSet *mUserFontSet,
+ const nsACString &aFamily, uint8_t aStyle,
+ uint16_t aWeight, int16_t aStretch,
+ bool& aWaitForUserFont)
+{
+ // Convert to UTF16
+ NS_ConvertUTF8toUTF16 utf16Family(aFamily);
+
+ // needsBold is not used here. Instead synthetic bold is enabled through
+ // FcFontRenderPrepare when the weight in the requested pattern is
+ // compared against the weight in the font pattern.
+ bool needsBold;
+
+ gfxFontStyle style;
+ style.style = aStyle;
+ style.weight = aWeight;
+ style.stretch = aStretch;
+
+ gfxUserFcFontEntry *fontEntry = nullptr;
+ gfxFontFamily *family = mUserFontSet->LookupFamily(utf16Family);
+ if (family) {
+ gfxUserFontEntry* userFontEntry =
+ mUserFontSet->FindUserFontEntryAndLoad(family, style, needsBold,
+ aWaitForUserFont);
+ if (userFontEntry) {
+ fontEntry = static_cast<gfxUserFcFontEntry*>
+ (userFontEntry->GetPlatformFontEntry());
+ }
+
+ // Accept synthetic oblique for italic and oblique.
+ // xxx - this isn't really ideal behavior, for docs that only use a
+ // single italic face it will also pull down the normal face
+ // and probably never use it
+ if (!fontEntry && aStyle != NS_FONT_STYLE_NORMAL) {
+ style.style = NS_FONT_STYLE_NORMAL;
+ userFontEntry =
+ mUserFontSet->FindUserFontEntryAndLoad(family, style,
+ needsBold,
+ aWaitForUserFont);
+ if (userFontEntry) {
+ fontEntry = static_cast<gfxUserFcFontEntry*>
+ (userFontEntry->GetPlatformFontEntry());
+ }
+ }
+ }
+
+ if (!fontEntry) {
+ return nullptr;
+ }
+
+ return &fontEntry->GetPatterns();
+}
+
+typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
+ int id);
+
+// FcPatternRemove is available in fontconfig-2.3.0 (2005)
+static FcBool
+moz_FcPatternRemove(FcPattern *p, const char *object, int id)
+{
+ static FcPatternRemoveFunction sFcPatternRemovePtr =
+ reinterpret_cast<FcPatternRemoveFunction>
+ (FindFunctionSymbol("FcPatternRemove"));
+
+ if (!sFcPatternRemovePtr)
+ return FcFalse;
+
+ return (*sFcPatternRemovePtr)(p, object, id);
+}
+
+// fontconfig prefers a matching family or lang to pixelsize of bitmap
+// fonts. CSS suggests a tolerance of 20% on pixelsize.
+static bool
+SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
+{
+ double size;
+ int v = 0;
+ while (FcPatternGetDouble(aFont,
+ FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
+ ++v;
+ if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
+ return true;
+ }
+
+ // No size means scalable
+ return v == 0;
+}
+
+// Sorting only the preferred fonts first usually saves having to sort through
+// every font on the system.
+nsReturnRef<FcFontSet>
+gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
+{
+ aWaitForUserFont = false;
+
+ gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
+ if (!utils)
+ return nsReturnRef<FcFontSet>();
+
+ // The list of families in mSortPattern has values with both weak and
+ // strong bindings. Values with strong bindings should be preferred.
+ // Values with weak bindings are default fonts that should be considered
+ // only when the font provides the best support for a requested language
+ // or after other fonts have satisfied all the requested languages.
+ //
+ // There are no direct fontconfig APIs to get the binding type. The
+ // binding only takes effect in the sort and match functions.
+
+ // |requiredLangs| is a list of requested languages that have not yet been
+ // satisfied. gfxFontconfigUtils only sets one FC_LANG property value,
+ // but FcConfigSubstitute may add more values (e.g. prepending "en" to
+ // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
+ // text.)
+ AutoTArray<LangSupportEntry,10> requiredLangs;
+ for (int v = 0; ; ++v) {
+ FcChar8 *lang;
+ FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
+ if (result != FcResultMatch) {
+ // No need to check FcPatternGetLangSet() because
+ // gfxFontconfigUtils sets only a string value for FC_LANG and
+ // FcConfigSubstitute cannot add LangSets.
+ NS_ASSERTION(result != FcResultTypeMismatch,
+ "Expected a string for FC_LANG");
+ break;
+ }
+
+ if (!requiredLangs.Contains(lang, LangComparator())) {
+ FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
+ if (bestLangSupport != FcLangDifferentLang) {
+ requiredLangs.
+ AppendElement(LangSupportEntry(lang, bestLangSupport));
+ }
+ }
+ }
+
+ nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
+ if (!fontSet)
+ return fontSet.out();
+
+ // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
+ // doesn't happen, Roman will be used.
+ int requestedSlant = FC_SLANT_ROMAN;
+ FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
+ double requestedSize = -1.0;
+ FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
+
+ nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies(32);
+ FcChar8 *family;
+ for (int v = 0;
+ FcPatternGetString(mSortPattern,
+ FC_FAMILY, v, &family) == FcResultMatch; ++v) {
+ const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nullptr;
+
+ // Is this an @font-face family?
+ bool isUserFont = false;
+ if (mUserFontSet) {
+ // Have some @font-face definitions
+
+ nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family));
+ NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX);
+
+ if (StringBeginsWith(cFamily, userPrefix)) {
+ isUserFont = true;
+
+ // Trim off the prefix
+ nsDependentCSubstring cssFamily(cFamily, userPrefix.Length());
+
+ uint8_t thebesStyle =
+ gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant);
+ uint16_t thebesWeight =
+ gfxFontconfigUtils::GetThebesWeight(mSortPattern);
+ int16_t thebesStretch =
+ gfxFontconfigUtils::GetThebesStretch(mSortPattern);
+
+ bool waitForUserFont;
+ familyFonts = FindFontPatterns(mUserFontSet, cssFamily,
+ thebesStyle,
+ thebesWeight, thebesStretch,
+ waitForUserFont);
+ if (waitForUserFont) {
+ aWaitForUserFont = true;
+ }
+ }
+ }
+
+ if (!isUserFont) {
+ familyFonts = &utils->GetFontsForFamily(family);
+ }
+
+ if (!familyFonts || familyFonts->Length() == 0) {
+ // There are no fonts matching this family, so there is no point
+ // in searching for this family in the FontSort.
+ //
+ // Perhaps the original pattern should be retained for
+ // FcFontRenderPrepare. However, the only a useful config
+ // substitution test against missing families that i can imagine
+ // would only be interested in the preferred family
+ // (qual="first"), so always keep the first family and use the
+ // same pattern for Sort and RenderPrepare.
+ if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
+ --v;
+ }
+ continue;
+ }
+
+ // Aliases seem to often end up occurring more than once, but
+ // duplicate families can't be removed from the sort pattern without
+ // knowing whether duplicates have the same binding.
+ gfxFontconfigUtils::DepFcStrEntry *familyEntry =
+ existingFamilies.PutEntry(family);
+ if (familyEntry) {
+ if (familyEntry->mKey) // old entry
+ continue;
+
+ familyEntry->mKey = family; // initialize new entry
+ }
+
+ for (uint32_t f = 0; f < familyFonts->Length(); ++f) {
+ FcPattern *font = familyFonts->ElementAt(f);
+
+ // Fix up the family name of user-font patterns, as the same
+ // font entry may be used (via the UserFontCache) for multiple
+ // CSS family names
+ if (isUserFont) {
+ font = FcPatternDuplicate(font);
+ FcPatternDel(font, FC_FAMILY);
+ FcPatternAddString(font, FC_FAMILY, family);
+ }
+
+ // User fonts are already filtered by slant (but not size) in
+ // mUserFontSet->FindUserFontEntry().
+ if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+ continue;
+
+ for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
+ const LangSupportEntry& langEntry = requiredLangs[r];
+ FcLangResult support =
+ gfxFontconfigUtils::GetLangSupport(font, langEntry.mLang);
+ if (support <= langEntry.mBestSupport) { // lower is better
+ requiredLangs.RemoveElementAt(r);
+ --r;
+ }
+ }
+
+ // FcFontSetDestroy will remove a reference but FcFontSetAdd
+ // does _not_ take a reference!
+ if (FcFontSetAdd(fontSet, font)) {
+ // We don't add a reference here for user fonts, because we're
+ // using a local clone of the pattern (see above) in order to
+ // override the family name
+ if (!isUserFont) {
+ FcPatternReference(font);
+ }
+ }
+ }
+ }
+
+ FcPattern *truncateMarker = nullptr;
+ for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
+ const nsTArray< nsCountedRef<FcPattern> >& langFonts =
+ utils->GetFontsForLang(requiredLangs[r].mLang);
+
+ bool haveLangFont = false;
+ for (uint32_t f = 0; f < langFonts.Length(); ++f) {
+ FcPattern *font = langFonts[f];
+ if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+ continue;
+
+ haveLangFont = true;
+ if (FcFontSetAdd(fontSet, font)) {
+ FcPatternReference(font);
+ }
+ }
+
+ if (!haveLangFont && langFonts.Length() > 0) {
+ // There is a font that supports this language but it didn't pass
+ // the slant and size criteria. Weak default font families should
+ // not be considered until the language has been satisfied.
+ //
+ // Insert a font that supports the language so that it will mark
+ // the position of fonts from weak families in the sorted set and
+ // they can be removed. The language and weak families will be
+ // considered in the fallback fonts, which use fontconfig's
+ // algorithm.
+ //
+ // Of the fonts that don't meet slant and size criteria, strong
+ // default font families should be considered before (other) fonts
+ // for this language, so this marker font will be removed (as well
+ // as the fonts from weak families), and strong families will be
+ // reconsidered in the fallback fonts.
+ FcPattern *font = langFonts[0];
+ if (FcFontSetAdd(fontSet, font)) {
+ FcPatternReference(font);
+ truncateMarker = font;
+ }
+ break;
+ }
+ }
+
+ FcFontSet *sets[1] = { fontSet };
+ FcResult result;
+ fontSet.own(FcFontSetSort(nullptr, sets, 1, mSortPattern,
+ FcFalse, nullptr, &result));
+
+ if (truncateMarker != nullptr && fontSet) {
+ nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
+
+ for (int f = 0; f < fontSet->nfont; ++f) {
+ FcPattern *font = fontSet->fonts[f];
+ if (font == truncateMarker)
+ break;
+
+ if (FcFontSetAdd(truncatedSet, font)) {
+ FcPatternReference(font);
+ }
+ }
+
+ fontSet.steal(truncatedSet);
+ }
+
+ return fontSet.out();
+}
+
+nsReturnRef<FcFontSet>
+gfxFcFontSet::SortFallbackFonts()
+{
+ // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
+ // but would take much longer due to comparing all the character sets.
+ //
+ // The references to fonts in this FcFontSet are almost free
+ // as they are pointers into mmaped cache files.
+ //
+ // GetFontPatternAt() will trim lazily if and as needed, which will also
+ // remove duplicates of preferred fonts.
+ FcResult result;
+ return nsReturnRef<FcFontSet>(FcFontSort(nullptr, mSortPattern,
+ FcFalse, nullptr, &result));
+}
+
+// GetFontAt relies on this setting up all patterns up to |i|.
+FcPattern *
+gfxFcFontSet::GetFontPatternAt(uint32_t i)
+{
+ while (i >= mFonts.Length()) {
+ while (!mFcFontSet) {
+ if (mHaveFallbackFonts)
+ return nullptr;
+
+ mFcFontSet = SortFallbackFonts();
+ mHaveFallbackFonts = true;
+ mFcFontsTrimmed = 0;
+ // Loop to test that mFcFontSet is non-nullptr.
+ }
+
+ while (mFcFontsTrimmed < mFcFontSet->nfont) {
+ FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
+ ++mFcFontsTrimmed;
+
+ if (mFonts.Length() != 0) {
+ // See if the next font provides support for any extra
+ // characters. Most often the next font is not going to
+ // support more characters so check for a SubSet first before
+ // allocating a new CharSet with Union.
+ FcCharSet *supportedChars = mCharSet;
+ if (!supportedChars) {
+ FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
+ FC_CHARSET, 0, &supportedChars);
+ }
+
+ if (supportedChars) {
+ FcCharSet *newChars = nullptr;
+ FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
+ if (newChars) {
+ if (FcCharSetIsSubset(newChars, supportedChars))
+ continue;
+
+ mCharSet.own(FcCharSetUnion(supportedChars, newChars));
+ } else if (!mCharSet) {
+ mCharSet.own(FcCharSetCopy(supportedChars));
+ }
+ }
+ }
+
+ mFonts.AppendElement(font);
+ if (mFonts.Length() >= i)
+ break;
+ }
+
+ if (mFcFontsTrimmed == mFcFontSet->nfont) {
+ // finished with this font set
+ mFcFontSet.reset();
+ }
+ }
+
+ return mFonts[i].mPattern;
+}
+
+#ifdef MOZ_WIDGET_GTK
+static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
+#endif
+
+// Apply user settings and defaults to pattern in preparation for matching.
+static void
+PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
+ double aSizeAdjustFactor, bool aIsPrinterFont)
+{
+ FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
+
+ // This gets cairo_font_options_t for the Screen. We should have
+ // different font options for printing (no hinting) but we are not told
+ // what we are measuring for.
+ //
+ // If cairo adds support for lcd_filter, gdk will not provide the default
+ // setting for that option. We could get the default setting by creating
+ // an xlib surface once, recording its font_options, and then merging the
+ // gdk options.
+ //
+ // Using an xlib surface would also be an option to get Screen font
+ // options for non-GTK X11 toolkits, but less efficient than using GDK to
+ // pick up dynamic changes.
+ if(aIsPrinterFont) {
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+ cairo_ft_font_options_substitute(options, aPattern);
+ cairo_font_options_destroy(options);
+ FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
+ } else {
+#ifdef MOZ_WIDGET_GTK
+ ApplyGdkScreenFontOptions(aPattern);
+#endif
+ }
+
+ // Protect against any fontconfig settings that may have incorrectly
+ // modified the pixelsize, and consider aSizeAdjustFactor.
+ double size = aFallbackSize;
+ if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
+ || aSizeAdjustFactor != 1.0) {
+ FcPatternDel(aPattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
+ }
+
+ FcDefaultSubstitute(aPattern);
+}
+
+/**
+ ** gfxPangoFontGroup
+ **/
+
+gfxPangoFontGroup::gfxPangoFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize)
+ : gfxFontGroup(aFontFamilyList, aStyle, nullptr, aUserFontSet, aDevToCssSize),
+ mPangoLanguage(GuessPangoLanguage(aStyle->language))
+{
+ // This language is passed to the font for shaping.
+ // Shaping doesn't know about lang groups so make it a real language.
+ if (mPangoLanguage) {
+ mStyle.language = NS_Atomize(pango_language_to_string(mPangoLanguage));
+ }
+
+ // dummy entry, will be replaced when actually needed
+ mFonts.AppendElement(FamilyFace());
+ mSkipUpdateUserFonts = true;
+}
+
+gfxPangoFontGroup::~gfxPangoFontGroup()
+{
+}
+
+gfxFontGroup *
+gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
+{
+ return new gfxPangoFontGroup(mFamilyList, aStyle, mUserFontSet, mDevToCssSize);
+}
+
+void
+gfxPangoFontGroup::FindGenericFontsPFG(FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ void *aClosure)
+{
+ AutoTArray<nsString, 5> resolvedGenerics;
+ ResolveGenericFontNamesPFG(aGenericType, aLanguage, resolvedGenerics);
+ uint32_t g = 0, numGenerics = resolvedGenerics.Length();
+ for (g = 0; g < numGenerics; g++) {
+ FindPlatformFontPFG(resolvedGenerics[g], false, aClosure);
+ }
+}
+
+/* static */ void
+gfxPangoFontGroup::ResolveGenericFontNamesPFG(FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ nsTArray<nsString>& aGenericFamilies)
+{
+ static const char kGeneric_serif[] = "serif";
+ static const char kGeneric_sans_serif[] = "sans-serif";
+ static const char kGeneric_monospace[] = "monospace";
+ static const char kGeneric_cursive[] = "cursive";
+ static const char kGeneric_fantasy[] = "fantasy";
+
+ // treat -moz-fixed as monospace
+ if (aGenericType == eFamily_moz_fixed) {
+ aGenericType = eFamily_monospace;
+ }
+
+ // type should be standard generic type at this point
+ NS_ASSERTION(aGenericType >= eFamily_serif &&
+ aGenericType <= eFamily_fantasy,
+ "standard generic font family type required");
+
+ // create the lang string
+ nsIAtom *langGroupAtom = nullptr;
+ nsAutoCString langGroupString;
+ if (aLanguage) {
+ if (!gLangService) {
+ CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
+ }
+ if (gLangService) {
+ nsresult rv;
+ langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
+ }
+ }
+ if (!langGroupAtom) {
+ langGroupAtom = nsGkAtoms::Unicode;
+ }
+ langGroupAtom->ToUTF8String(langGroupString);
+
+ // map generic type to string
+ const char *generic = nullptr;
+ switch (aGenericType) {
+ case eFamily_serif:
+ generic = kGeneric_serif;
+ break;
+ case eFamily_sans_serif:
+ generic = kGeneric_sans_serif;
+ break;
+ case eFamily_monospace:
+ generic = kGeneric_monospace;
+ break;
+ case eFamily_cursive:
+ generic = kGeneric_cursive;
+ break;
+ case eFamily_fantasy:
+ generic = kGeneric_fantasy;
+ break;
+ default:
+ break;
+ }
+
+ if (!generic) {
+ return;
+ }
+
+ aGenericFamilies.Clear();
+
+ // load family for "font.name.generic.lang"
+ nsAutoCString prefFontName("font.name.");
+ prefFontName.Append(generic);
+ prefFontName.Append('.');
+ prefFontName.Append(langGroupString);
+ gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
+ aGenericFamilies);
+
+ // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
+ if (!aGenericFamilies.IsEmpty()) {
+ nsAutoCString prefFontListName("font.name-list.");
+ prefFontListName.Append(generic);
+ prefFontListName.Append('.');
+ prefFontListName.Append(langGroupString);
+ gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
+ aGenericFamilies);
+ }
+
+#if 0 // dump out generic mappings
+ printf("%s ===> ", prefFontName.get());
+ for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
+ if (k > 0) printf(", ");
+ printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
+ }
+ printf("\n");
+#endif
+}
+
+void gfxPangoFontGroup::EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure)
+{
+ // initialize fonts in the font family list
+ const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
+
+ // lookup fonts in the fontlist
+ uint32_t i, numFonts = fontlist.Length();
+ for (i = 0; i < numFonts; i++) {
+ const FontFamilyName& name = fontlist[i];
+ if (name.IsNamed()) {
+ FindPlatformFontPFG(name.mName, true, aClosure);
+ } else {
+ FindGenericFontsPFG(name.mType, aLanguage, aClosure);
+ }
+ }
+
+ // if necessary, append default generic onto the end
+ if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+ !mFamilyList.HasDefaultGeneric()) {
+ FindGenericFontsPFG(mFamilyList.GetDefaultFontType(),
+ aLanguage, aClosure);
+ }
+}
+
+void
+gfxPangoFontGroup::FindPlatformFontPFG(const nsAString& fontName,
+ bool aUseFontSet,
+ void *aClosure)
+{
+ nsTArray<nsString> *list = static_cast<nsTArray<nsString>*>(aClosure);
+
+ if (!list->Contains(fontName)) {
+ // names present in the user fontset are not matched against system fonts
+ if (aUseFontSet && mUserFontSet && mUserFontSet->HasFamily(fontName)) {
+ nsAutoString userFontName =
+ NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
+ list->AppendElement(userFontName);
+ } else {
+ list->AppendElement(fontName);
+ }
+ }
+}
+
+gfxFcFont *
+gfxPangoFontGroup::GetBaseFont()
+{
+ if (mFonts[0].Font() == nullptr) {
+ gfxFont* font = GetBaseFontSet()->GetFontAt(0, GetStyle());
+ mFonts[0] = FamilyFace(nullptr, font);
+ }
+
+ return static_cast<gfxFcFont*>(mFonts[0].Font());
+}
+
+gfxFont*
+gfxPangoFontGroup::GetFirstValidFont(uint32_t aCh)
+{
+ return GetFontAt(0);
+}
+
+gfxFont *
+gfxPangoFontGroup::GetFontAt(int32_t i, uint32_t aCh)
+{
+ // If it turns out to be hard for all clients that cache font
+ // groups to call UpdateUserFonts at appropriate times, we could
+ // instead consider just calling UpdateUserFonts from someplace
+ // more central (such as here).
+ NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
+ "Whoever was caching this font group should have "
+ "called UpdateUserFonts on it");
+
+ NS_PRECONDITION(i == 0, "Only have one font");
+
+ return GetBaseFont();
+}
+
+void
+gfxPangoFontGroup::UpdateUserFonts()
+{
+ uint64_t newGeneration = GetGeneration();
+ if (newGeneration == mCurrGeneration)
+ return;
+
+ mFonts[0] = FamilyFace();
+ mFontSets.Clear();
+ ClearCachedData();
+ mCurrGeneration = newGeneration;
+}
+
+already_AddRefed<gfxFcFontSet>
+gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
+ nsAutoRef<FcPattern> *aMatchPattern)
+{
+ const char *lang = pango_language_to_string(aLang);
+
+ RefPtr<nsIAtom> langGroup;
+ if (aLang != mPangoLanguage) {
+ // Set up langGroup for Mozilla's font prefs.
+ langGroup = NS_Atomize(lang);
+ }
+
+ AutoTArray<nsString, 20> fcFamilyList;
+ EnumerateFontListPFG(langGroup ? langGroup.get() : mStyle.language.get(),
+ &fcFamilyList);
+
+ // To consider: A fontset cache here could be helpful.
+
+ // Get a pattern suitable for matching.
+ nsAutoRef<FcPattern> pattern
+ (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
+
+ PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
+
+ RefPtr<gfxFcFontSet> fontset =
+ new gfxFcFontSet(pattern, mUserFontSet);
+
+ mSkipDrawing = fontset->WaitingForUserFont();
+
+ if (aMatchPattern)
+ aMatchPattern->steal(pattern);
+
+ return fontset.forget();
+}
+
+gfxPangoFontGroup::
+FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
+ gfxFcFontSet *aFontSet)
+ : mLang(aLang), mFontSet(aFontSet)
+{
+}
+
+gfxFcFontSet *
+gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
+{
+ GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
+
+ if (!aLang)
+ return mFontSets[0].mFontSet;
+
+ for (uint32_t i = 0; i < mFontSets.Length(); ++i) {
+ if (mFontSets[i].mLang == aLang)
+ return mFontSets[i].mFontSet;
+ }
+
+ RefPtr<gfxFcFontSet> fontSet =
+ MakeFontSet(aLang, mSizeAdjustFactor);
+ mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
+
+ return fontSet;
+}
+
+already_AddRefed<gfxFont>
+gfxPangoFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
+ uint32_t aNextCh, Script aRunScript,
+ gfxFont *aPrevMatchedFont,
+ uint8_t *aMatchType)
+{
+ if (aPrevMatchedFont) {
+ // Don't switch fonts for control characters, regardless of
+ // whether they are present in the current font, as they won't
+ // actually be rendered (see bug 716229)
+ uint8_t category = GetGeneralCategory(aCh);
+ if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+
+ // if this character is a join-control or the previous is a join-causer,
+ // use the same font as the previous range if we can
+ if (gfxFontUtils::IsJoinControl(aCh) ||
+ gfxFontUtils::IsJoinCauser(aPrevCh)) {
+ if (aPrevMatchedFont->HasCharacter(aCh)) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+ }
+ }
+
+ // if this character is a variation selector,
+ // use the previous font regardless of whether it supports VS or not.
+ // otherwise the text run will be divided.
+ if (gfxFontUtils::IsVarSelector(aCh)) {
+ if (aPrevMatchedFont) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+ // VS alone. it's meaningless to search different fonts
+ return nullptr;
+ }
+
+ // The real fonts that fontconfig provides for generic/fallback families
+ // depend on the language used, so a different FontSet is used for each
+ // language (except for the variation below).
+ //
+ // With most fontconfig configurations any real family names prior to a
+ // fontconfig generic with corresponding fonts installed will still lead
+ // to the same leading fonts in each FontSet.
+ //
+ // There is an inefficiency here therefore because the same base FontSet
+ // could often be used if these real families support the character.
+ // However, with fontconfig aliases, it is difficult to distinguish
+ // where exactly alias fonts end and generic/fallback fonts begin.
+ //
+ // The variation from pure language-based matching used here is that the
+ // same primary/base font is always used irrespective of the language.
+ // This provides that SCRIPT_COMMON characters are consistently rendered
+ // with the same font (bug 339513 and bug 416725). This is particularly
+ // important with the word cache as script can't be reliably determined
+ // from surrounding words. It also often avoids the unnecessary extra
+ // FontSet efficiency mentioned above.
+ //
+ // However, in two situations, the base font is not checked before the
+ // language-specific FontSet.
+ //
+ // 1. When we don't have a language to make a good choice for
+ // the base font.
+ //
+ // 2. For system fonts, use the default Pango behavior to give
+ // consistency with other apps. This is relevant when un-localized
+ // builds are run in non-Latin locales. This special-case probably
+ // wouldn't be necessary but for bug 91190.
+
+ gfxFcFontSet *fontSet = GetBaseFontSet();
+ uint32_t nextFont = 0;
+ FcPattern *basePattern = nullptr;
+ if (!mStyle.systemFont && mPangoLanguage) {
+ basePattern = fontSet->GetFontPatternAt(0);
+ if (HasChar(basePattern, aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return RefPtr<gfxFont>(GetBaseFont()).forget();
+ }
+
+ nextFont = 1;
+ }
+
+ // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values
+ // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag.
+ // Note that PangoScript is defined to be compatible with GUnicodeScript:
+ // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript
+ const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript);
+ const PangoScript script =
+ (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag));
+
+ // Might be nice to call pango_language_includes_script only once for the
+ // run rather than for each character.
+ PangoLanguage *scriptLang;
+ if ((!basePattern ||
+ !pango_language_includes_script(mPangoLanguage, script)) &&
+ (scriptLang = pango_script_get_sample_language(script))) {
+ fontSet = GetFontSet(scriptLang);
+ nextFont = 0;
+ }
+
+ for (uint32_t i = nextFont;
+ FcPattern *pattern = fontSet->GetFontPatternAt(i);
+ ++i) {
+ if (pattern == basePattern) {
+ continue; // already checked basePattern
+ }
+
+ if (HasChar(pattern, aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return RefPtr<gfxFont>(fontSet->GetFontAt(i, GetStyle())).forget();
+ }
+ }
+
+ return nullptr;
+}
+
+/**
+ ** gfxFcFont
+ **/
+
+cairo_user_data_key_t gfxFcFont::sGfxFontKey;
+
+gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
+ FcPattern *aPattern,
+ gfxFcFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle)
+ : gfxFontconfigFontBase(aCairoFont, aPattern, aFontEntry, aFontStyle)
+{
+ cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, nullptr);
+}
+
+gfxFcFont::~gfxFcFont()
+{
+ cairo_scaled_font_set_user_data(mScaledFont,
+ &sGfxFontKey,
+ nullptr,
+ nullptr);
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
+{
+ gfxFontStyle style(*GetStyle());
+ style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
+ return MakeScaledFont(&style, style.size / GetStyle()->size);
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::MakeScaledFont(gfxFontStyle *aFontStyle, gfxFloat aScaleFactor)
+{
+ gfxFcFontEntry* fe = static_cast<gfxFcFontEntry*>(GetFontEntry());
+ RefPtr<gfxFont> font =
+ gfxFontCache::GetCache()->Lookup(fe, aFontStyle, nullptr);
+ if (font) {
+ return font.forget();
+ }
+
+ cairo_matrix_t fontMatrix;
+ cairo_scaled_font_get_font_matrix(mScaledFont, &fontMatrix);
+ cairo_matrix_scale(&fontMatrix, aScaleFactor, aScaleFactor);
+
+ cairo_matrix_t ctm;
+ cairo_scaled_font_get_ctm(mScaledFont, &ctm);
+
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_scaled_font_get_font_options(mScaledFont, options);
+
+ cairo_scaled_font_t *newFont =
+ cairo_scaled_font_create(cairo_scaled_font_get_font_face(mScaledFont),
+ &fontMatrix, &ctm, options);
+ cairo_font_options_destroy(options);
+
+ font = new gfxFcFont(newFont, GetPattern(), fe, aFontStyle);
+ gfxFontCache::GetCache()->AddNew(font);
+ cairo_scaled_font_destroy(newFont);
+
+ return font.forget();
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::GetSmallCapsFont()
+{
+ gfxFontStyle style(*GetStyle());
+ style.size *= SMALL_CAPS_SCALE_FACTOR;
+ style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+ return MakeScaledFont(&style, SMALL_CAPS_SCALE_FACTOR);
+}
+
+/* static */ void
+gfxPangoFontGroup::Shutdown()
+{
+ // Resetting gFTLibrary in case this is wanted again after a
+ // cairo_debug_reset_static_data.
+ gFTLibrary = nullptr;
+}
+
+/* static */ gfxFontEntry *
+gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
+ if (!utils)
+ return nullptr;
+
+ // The font face name from @font-face { src: local() } is not well
+ // defined.
+ //
+ // On MS Windows, this name gets compared with
+ // ENUMLOGFONTEXW::elfFullName, which for OpenType fonts seems to be the
+ // full font name from the name table. For CFF OpenType fonts this is the
+ // same as the PostScript name, but for TrueType fonts it is usually
+ // different.
+ //
+ // On Mac, the font face name is compared with the PostScript name, even
+ // for TrueType fonts.
+ //
+ // Fontconfig only records the full font names, so the behavior here
+ // follows that on MS Windows. However, to provide the possibility
+ // of aliases to compensate for variations, the font face name is passed
+ // through FcConfigSubstitute.
+
+ nsAutoRef<FcPattern> pattern(FcPatternCreate());
+ if (!pattern)
+ return nullptr;
+
+ NS_ConvertUTF16toUTF8 fullname(aFontName);
+ FcPatternAddString(pattern, FC_FULLNAME,
+ gfxFontconfigUtils::ToFcChar8(fullname));
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+
+ FcChar8 *name;
+ for (int v = 0;
+ FcPatternGetString(pattern, FC_FULLNAME, v, &name) == FcResultMatch;
+ ++v) {
+ const nsTArray< nsCountedRef<FcPattern> >& fonts =
+ utils->GetFontsForFullname(name);
+
+ if (fonts.Length() != 0)
+ return new gfxLocalFcFontEntry(aFontName,
+ aWeight,
+ aStretch,
+ aStyle,
+ fonts);
+ }
+
+ return nullptr;
+}
+
+/* static */ FT_Library
+gfxPangoFontGroup::GetFTLibrary()
+{
+ if (!gFTLibrary) {
+ // Use cairo's FT_Library so that cairo takes care of shutdown of the
+ // FT_Library after it has destroyed its font_faces, and FT_Done_Face
+ // has been called on each FT_Face, at least until this bug is fixed:
+ // https://bugs.freedesktop.org/show_bug.cgi?id=18857
+ //
+ // Cairo's FT_Library can be obtained from any cairo_scaled_font. The
+ // font properties requested here are chosen to get an FT_Face that is
+ // likely to be also used elsewhere.
+ gfxFontStyle style;
+ RefPtr<gfxPangoFontGroup> fontGroup =
+ new gfxPangoFontGroup(FontFamilyList(eFamily_sans_serif),
+ &style, nullptr, 1.0);
+
+ gfxFcFont *font = fontGroup->GetBaseFont();
+ if (!font)
+ return nullptr;
+
+ gfxFT2LockedFace face(font);
+ if (!face.get())
+ return nullptr;
+
+ gFTLibrary = face.get()->glyph->library;
+ }
+
+ return gFTLibrary;
+}
+
+/* static */ gfxFontEntry *
+gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ // Ownership of aFontData is passed in here, and transferred to the
+ // new fontEntry, which will release it when no longer needed.
+
+ // Using face_index = 0 for the first face in the font, as we have no
+ // other information. FT_New_Memory_Face checks for a nullptr FT_Library.
+ FT_Face face;
+ FT_Error error =
+ FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
+ if (error != 0) {
+ free((void*)aFontData);
+ return nullptr;
+ }
+
+ return new gfxDownloadedFcFontEntry(aFontName, aWeight,
+ aStretch, aStyle,
+ aFontData, face);
+}
+
+
+static double
+GetPixelSize(FcPattern *aPattern)
+{
+ double size;
+ if (FcPatternGetDouble(aPattern,
+ FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ return size;
+
+ NS_NOTREACHED("No size on pattern");
+ return 0.0;
+}
+
+/**
+ * The following gfxFcFonts are accessed from the cairo_scaled_font or created
+ * from the FcPattern, not from the gfxFontCache hash table. The gfxFontCache
+ * hash table is keyed by desired family and style, whereas here we only know
+ * actual family and style. There may be more than one of these fonts with
+ * the same family and style, but different PangoFont and actual font face.
+ *
+ * The point of this is to record the exact font face for gfxTextRun glyph
+ * indices. The style of this font does not necessarily represent the exact
+ * gfxFontStyle used to build the text run. Notably, the language is not
+ * recorded.
+ */
+
+/* static */
+already_AddRefed<gfxFcFont>
+gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+ const gfxFontStyle *aFontStyle)
+{
+ nsAutoRef<FcPattern> renderPattern
+ (FcFontRenderPrepare(nullptr, aRequestedPattern, aFontPattern));
+
+ // If synthetic bold/italic is not allowed by the style, adjust the
+ // resulting pattern to match the actual properties of the font.
+ if (!aFontStyle->allowSyntheticWeight) {
+ int weight;
+ if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0,
+ &weight) == FcResultMatch) {
+ FcPatternDel(renderPattern, FC_WEIGHT);
+ FcPatternAddInteger(renderPattern, FC_WEIGHT, weight);
+ }
+ }
+ if (!aFontStyle->allowSyntheticStyle) {
+ int slant;
+ if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0,
+ &slant) == FcResultMatch) {
+ FcPatternDel(renderPattern, FC_SLANT);
+ FcPatternAddInteger(renderPattern, FC_SLANT, slant);
+ }
+ }
+
+ cairo_font_face_t *face =
+ cairo_ft_font_face_create_for_pattern(renderPattern);
+
+ // Reuse an existing font entry if available.
+ RefPtr<gfxFcFontEntry> fe = gfxFcFontEntry::LookupFontEntry(face);
+ if (!fe) {
+ gfxDownloadedFcFontEntry *downloadedFontEntry =
+ GetDownloadedFontEntry(aFontPattern);
+ if (downloadedFontEntry) {
+ // Web font
+ fe = downloadedFontEntry;
+ if (cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
+ // cairo_font_face_t is using the web font data.
+ // Hold a reference to the font entry to keep the font face
+ // data.
+ if (!downloadedFontEntry->SetCairoFace(face)) {
+ // OOM. Let cairo pick a fallback font
+ cairo_font_face_destroy(face);
+ face = cairo_ft_font_face_create_for_pattern(aRequestedPattern);
+ fe = gfxFcFontEntry::LookupFontEntry(face);
+ }
+ }
+ }
+ if (!fe) {
+ // Get a unique name for the font face from the file and id.
+ nsAutoString name;
+ FcChar8 *fc_file;
+ if (FcPatternGetString(renderPattern,
+ FC_FILE, 0, &fc_file) == FcResultMatch) {
+ int index;
+ if (FcPatternGetInteger(renderPattern,
+ FC_INDEX, 0, &index) != FcResultMatch) {
+ // cairo defaults to 0.
+ index = 0;
+ }
+
+ AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
+ if (index != 0) {
+ name.Append('/');
+ name.AppendInt(index);
+ }
+ }
+
+ fe = new gfxSystemFcFontEntry(face, aFontPattern, name);
+ }
+ }
+
+ gfxFontStyle style(*aFontStyle);
+ style.size = GetPixelSize(renderPattern);
+ style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
+ style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
+
+ RefPtr<gfxFont> font =
+ gfxFontCache::GetCache()->Lookup(fe, &style, nullptr);
+ if (!font) {
+ // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
+ // not necessarily enough to provide a key that will describe a unique
+ // font. cairoFont contains information from renderPattern, which is a
+ // fully resolved pattern from FcFontRenderPrepare.
+ // FcFontRenderPrepare takes the requested pattern and the face
+ // pattern as input and can modify elements of the resulting pattern
+ // that affect rendering but are not included in the gfxFontStyle.
+ cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
+ font = new gfxFcFont(cairoFont, renderPattern, fe, &style);
+ gfxFontCache::GetCache()->AddNew(font);
+ cairo_scaled_font_destroy(cairoFont);
+ }
+
+ cairo_font_face_destroy(face);
+
+ RefPtr<gfxFcFont> retval(static_cast<gfxFcFont*>(font.get()));
+ return retval.forget();
+}
+
+gfxFcFontSet *
+gfxPangoFontGroup::GetBaseFontSet()
+{
+ if (mFontSets.Length() > 0)
+ return mFontSets[0].mFontSet;
+
+ mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
+ nsAutoRef<FcPattern> pattern;
+ RefPtr<gfxFcFontSet> fontSet =
+ MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
+
+ double size = GetPixelSize(pattern);
+ if (size != 0.0 && mStyle.sizeAdjust > 0.0) {
+ gfxFcFont *font = fontSet->GetFontAt(0, GetStyle());
+ if (font) {
+ const gfxFont::Metrics& metrics =
+ font->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
+
+ // The factor of 0.1 ensures that xHeight is sane so fonts don't
+ // become huge. Strictly ">" ensures that xHeight and emHeight are
+ // not both zero.
+ if (metrics.xHeight > 0.1 * metrics.emHeight) {
+ mSizeAdjustFactor =
+ mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
+
+ size *= mSizeAdjustFactor;
+ FcPatternDel(pattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
+
+ fontSet = new gfxFcFontSet(pattern, mUserFontSet);
+ }
+ }
+ }
+
+ PangoLanguage *pangoLang = mPangoLanguage;
+ FcChar8 *fcLang;
+ if (!pangoLang &&
+ FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
+ pangoLang =
+ pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
+ }
+
+ mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
+
+ return fontSet;
+}
+
+/**
+ ** gfxTextRun
+ *
+ * A serious problem:
+ *
+ * -- We draw with a font that's hinted for the CTM, but we measure with a font
+ * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
+ *
+ **/
+
+// This will fetch an existing scaled_font if one exists.
+static cairo_scaled_font_t *
+CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace)
+{
+ double size = GetPixelSize(aPattern);
+
+ cairo_matrix_t fontMatrix;
+ FcMatrix *fcMatrix;
+ if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
+ cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
+ else
+ cairo_matrix_init_identity(&fontMatrix);
+ cairo_matrix_scale(&fontMatrix, size, size);
+
+ FcBool printing;
+ if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != FcResultMatch) {
+ printing = FcFalse;
+ }
+
+ // The cairo_scaled_font is created with a unit ctm so that metrics and
+ // positions are in user space, but this means that hinting effects will
+ // not be estimated accurately for non-unit transformations.
+ cairo_matrix_t identityMatrix;
+ cairo_matrix_init_identity(&identityMatrix);
+
+ // Font options are set explicitly here to improve cairo's caching
+ // behavior and to record the relevant parts of the pattern for
+ // SetupCairoFont (so that the pattern can be released).
+ //
+ // Most font_options have already been set as defaults on the FcPattern
+ // with cairo_ft_font_options_substitute(), then user and system
+ // fontconfig configurations were applied. The resulting font_options
+ // have been recorded on the face during
+ // cairo_ft_font_face_create_for_pattern().
+ //
+ // None of the settings here cause this scaled_font to behave any
+ // differently from how it would behave if it were created from the same
+ // face with default font_options.
+ //
+ // We set options explicitly so that the same scaled_font will be found in
+ // the cairo_scaled_font_map when cairo loads glyphs from a context with
+ // the same font_face, font_matrix, ctm, and surface font_options.
+ //
+ // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
+ // font_options on the cairo_ft_font_face, and doesn't consider default
+ // option values to not match any explicit values.
+ //
+ // Even after cairo_set_scaled_font is used to set font_options for the
+ // cairo context, when cairo looks for a scaled_font for the context, it
+ // will look for a font with some option values from the target surface if
+ // any values are left default on the context font_options. If this
+ // scaled_font is created with default font_options, cairo will not find
+ // it.
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ // The one option not recorded in the pattern is hint_metrics, which will
+ // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
+ // We should be considering the font_options of the surface on which this
+ // font will be used, but currently we don't have different gfxFonts for
+ // different surface font_options, so we'll create a font suitable for the
+ // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
+ if (printing) {
+ cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
+ } else {
+ cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON);
+ }
+
+ // The remaining options have been recorded on the pattern and the face.
+ // _cairo_ft_options_merge has some logic to decide which options from the
+ // scaled_font or from the cairo_ft_font_face take priority in the way the
+ // font behaves.
+ //
+ // In the majority of cases, _cairo_ft_options_merge uses the options from
+ // the cairo_ft_font_face, so sometimes it is not so important which
+ // values are set here so long as they are not defaults, but we'll set
+ // them to the exact values that we expect from the font, to be consistent
+ // and to protect against changes in cairo.
+ //
+ // In some cases, _cairo_ft_options_merge uses some options from the
+ // scaled_font's font_options rather than options on the
+ // cairo_ft_font_face (from fontconfig).
+ // https://bugs.freedesktop.org/show_bug.cgi?id=11838
+ //
+ // Surface font options were set on the pattern in
+ // cairo_ft_font_options_substitute. If fontconfig has changed the
+ // hint_style then that is what the user (or distribution) wants, so we
+ // use the setting from the FcPattern.
+ //
+ // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
+ FcBool hinting = FcFalse;
+ if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
+ hinting = FcTrue;
+ }
+
+ cairo_hint_style_t hint_style;
+ if (printing || !hinting) {
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ } else {
+#ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
+ int fc_hintstyle;
+ if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
+ 0, &fc_hintstyle ) != FcResultMatch) {
+ fc_hintstyle = FC_HINT_FULL;
+ }
+ switch (fc_hintstyle) {
+ case FC_HINT_NONE:
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ break;
+ case FC_HINT_SLIGHT:
+ hint_style = CAIRO_HINT_STYLE_SLIGHT;
+ break;
+ case FC_HINT_MEDIUM:
+ default: // This fallback mirrors _get_pattern_ft_options in cairo.
+ hint_style = CAIRO_HINT_STYLE_MEDIUM;
+ break;
+ case FC_HINT_FULL:
+ hint_style = CAIRO_HINT_STYLE_FULL;
+ break;
+ }
+#else // no FC_HINT_STYLE
+ hint_style = CAIRO_HINT_STYLE_FULL;
+#endif
+ }
+ cairo_font_options_set_hint_style(fontOptions, hint_style);
+
+ int rgba;
+ if (FcPatternGetInteger(aPattern,
+ FC_RGBA, 0, &rgba) != FcResultMatch) {
+ rgba = FC_RGBA_UNKNOWN;
+ }
+ cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ switch (rgba) {
+ case FC_RGBA_UNKNOWN:
+ case FC_RGBA_NONE:
+ default:
+ // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
+ // is disabled through cairo_antialias_t.
+ rgba = FC_RGBA_NONE;
+ // subpixel_order won't be used by the font as we won't use
+ // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
+ // caching reasons described above. Fall through:
+ MOZ_FALLTHROUGH;
+ case FC_RGBA_RGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+ break;
+ case FC_RGBA_BGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+ break;
+ case FC_RGBA_VRGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+ break;
+ case FC_RGBA_VBGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+ break;
+ }
+ cairo_font_options_set_subpixel_order(fontOptions, subpixel_order);
+
+ FcBool fc_antialias;
+ if (FcPatternGetBool(aPattern,
+ FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
+ fc_antialias = FcTrue;
+ }
+ cairo_antialias_t antialias;
+ if (!fc_antialias) {
+ antialias = CAIRO_ANTIALIAS_NONE;
+ } else if (rgba == FC_RGBA_NONE) {
+ antialias = CAIRO_ANTIALIAS_GRAY;
+ } else {
+ antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+ }
+ cairo_font_options_set_antialias(fontOptions, antialias);
+
+ cairo_scaled_font_t *scaledFont =
+ cairo_scaled_font_create(aFace, &fontMatrix, &identityMatrix,
+ fontOptions);
+
+ cairo_font_options_destroy(fontOptions);
+
+ NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
+ "Failed to create scaled font");
+ return scaledFont;
+}
+
+/* static */
+PangoLanguage *
+GuessPangoLanguage(nsIAtom *aLanguage)
+{
+ if (!aLanguage)
+ return nullptr;
+
+ // Pango and fontconfig won't understand mozilla's internal langGroups, so
+ // find a real language.
+ nsAutoCString lang;
+ gfxFontconfigUtils::GetSampleLangForGroup(aLanguage, &lang);
+ if (lang.IsEmpty())
+ return nullptr;
+
+ return pango_language_from_string(lang.get());
+}
+
+#ifdef MOZ_WIDGET_GTK
+/***************************************************************************
+ *
+ * This function must be last in the file because it uses the system cairo
+ * library. Above this point the cairo library used is the tree cairo if
+ * MOZ_TREE_CAIRO.
+ */
+
+#if MOZ_TREE_CAIRO
+// Tree cairo symbols have different names. Disable their activation through
+// preprocessor macros.
+#undef cairo_ft_font_options_substitute
+
+// The system cairo functions are not declared because the include paths cause
+// the gdk headers to pick up the tree cairo.h.
+extern "C" {
+NS_VISIBILITY_DEFAULT void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+ FcPattern *pattern);
+}
+#endif
+
+static void
+ApplyGdkScreenFontOptions(FcPattern *aPattern)
+{
+ const cairo_font_options_t *options =
+ gdk_screen_get_font_options(gdk_screen_get_default());
+
+ cairo_ft_font_options_substitute(options, aPattern);
+}
+
+#endif // MOZ_WIDGET_GTK
+
diff --git a/system/graphics/thebes/gfxFontconfigFonts.h b/system/graphics/thebes/gfxFontconfigFonts.h
new file mode 100644
index 000000000..cea9d0dbf
--- /dev/null
+++ b/system/graphics/thebes/gfxFontconfigFonts.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONTCONFIG_FONTS_H
+#define GFX_FONTCONFIG_FONTS_H
+
+#include "cairo.h"
+#include "gfxTypes.h"
+#include "gfxTextRun.h"
+
+#include "nsAutoRef.h"
+#include "nsTArray.h"
+
+#include <pango/pango.h>
+
+class gfxFcFontSet;
+class gfxFcFont;
+typedef struct _FcPattern FcPattern;
+typedef struct FT_FaceRec_* FT_Face;
+typedef struct FT_LibraryRec_ *FT_Library;
+
+class gfxPangoFontGroup : public gfxFontGroup {
+public:
+ gfxPangoFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize);
+ virtual ~gfxPangoFontGroup();
+
+ virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
+
+ virtual gfxFont* GetFirstValidFont(uint32_t aCh = 0x20);
+
+ virtual void UpdateUserFonts();
+
+ virtual already_AddRefed<gfxFont>
+ FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
+ Script aRunScript, gfxFont *aPrevMatchedFont,
+ uint8_t *aMatchType);
+
+ static void Shutdown();
+
+ // Used for @font-face { src: local(); }
+ static gfxFontEntry *NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+ // Used for @font-face { src: url(); }
+ static gfxFontEntry *NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+private:
+
+ virtual gfxFont *GetFontAt(int32_t i, uint32_t aCh = 0x20);
+
+ // @param aLang [in] language to use for pref fonts and system default font
+ // selection, or nullptr for the language guessed from the
+ // gfxFontStyle.
+ // The FontGroup holds a reference to this set.
+ gfxFcFontSet *GetFontSet(PangoLanguage *aLang = nullptr);
+
+ class FontSetByLangEntry {
+ public:
+ FontSetByLangEntry(PangoLanguage *aLang, gfxFcFontSet *aFontSet);
+ PangoLanguage *mLang;
+ RefPtr<gfxFcFontSet> mFontSet;
+ };
+ // There is only one of entry in this array unless characters from scripts
+ // of other languages are measured.
+ AutoTArray<FontSetByLangEntry,1> mFontSets;
+
+ gfxFloat mSizeAdjustFactor;
+ PangoLanguage *mPangoLanguage;
+
+ // @param aLang [in] language to use for pref fonts and system font
+ // resolution, or nullptr to guess a language from the gfxFontStyle.
+ // @param aMatchPattern [out] if non-nullptr, will return the pattern used.
+ already_AddRefed<gfxFcFontSet>
+ MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
+ nsAutoRef<FcPattern> *aMatchPattern = nullptr);
+
+ gfxFcFontSet *GetBaseFontSet();
+ gfxFcFont *GetBaseFont();
+
+ gfxFloat GetSizeAdjustFactor()
+ {
+ if (mFontSets.Length() == 0)
+ GetBaseFontSet();
+ return mSizeAdjustFactor;
+ }
+
+ // old helper methods from gfxFontGroup, moved here so that those methods
+ // can be revamped without affecting the legacy code here
+
+ // iterate over the fontlist, lookup names and expand generics
+ void EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure);
+
+ // expand a generic to a list of specific names based on prefs
+ void FindGenericFontsPFG(mozilla::FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ void *aClosure);
+
+ // lookup and add a font with a given name (i.e. *not* a generic!)
+ void FindPlatformFontPFG(const nsAString& aName,
+ bool aUseFontSet,
+ void *aClosure);
+
+ static void
+ ResolveGenericFontNamesPFG(mozilla::FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ nsTArray<nsString>& aGenericFamilies);
+
+
+ friend class gfxSystemFcFontEntry;
+ static FT_Library GetFTLibrary();
+};
+
+#endif /* GFX_FONTCONFIG_FONTS_H */
diff --git a/system/graphics/thebes/gfxFontconfigUtils.cpp b/system/graphics/thebes/gfxFontconfigUtils.cpp
new file mode 100644
index 000000000..4afb27e37
--- /dev/null
+++ b/system/graphics/thebes/gfxFontconfigUtils.cpp
@@ -0,0 +1,1088 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "gfxFontconfigUtils.h"
+#include "gfxFont.h"
+#include "nsGkAtoms.h"
+
+#include <locale.h>
+#include <fontconfig/fontconfig.h>
+
+#include "nsServiceManagerUtils.h"
+#include "nsILanguageAtomService.h"
+#include "nsTArray.h"
+#include "mozilla/Preferences.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#include "nsIAtom.h"
+#include "nsCRT.h"
+#include "gfxFontConstants.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace mozilla;
+
+/* static */ gfxFontconfigUtils* gfxFontconfigUtils::sUtils = nullptr;
+static nsILanguageAtomService* gLangService = nullptr;
+
+/* static */ void
+gfxFontconfigUtils::Shutdown() {
+ if (sUtils) {
+ delete sUtils;
+ sUtils = nullptr;
+ }
+ NS_IF_RELEASE(gLangService);
+}
+
+/* static */ uint8_t
+gfxFontconfigUtils::FcSlantToThebesStyle(int aFcSlant)
+{
+ switch (aFcSlant) {
+ case FC_SLANT_ITALIC:
+ return NS_FONT_STYLE_ITALIC;
+ case FC_SLANT_OBLIQUE:
+ return NS_FONT_STYLE_OBLIQUE;
+ default:
+ return NS_FONT_STYLE_NORMAL;
+ }
+}
+
+/* static */ uint8_t
+gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
+{
+ int slant;
+ if (FcPatternGetInteger(aPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
+ return NS_FONT_STYLE_NORMAL;
+ }
+
+ return FcSlantToThebesStyle(slant);
+}
+
+/* static */ int
+gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
+{
+ if (aFontStyle.style == NS_FONT_STYLE_ITALIC)
+ return FC_SLANT_ITALIC;
+ if (aFontStyle.style == NS_FONT_STYLE_OBLIQUE)
+ return FC_SLANT_OBLIQUE;
+
+ return FC_SLANT_ROMAN;
+}
+
+// OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
+#ifndef FC_WEIGHT_THIN
+#define FC_WEIGHT_THIN 0 // 2.1.93
+#define FC_WEIGHT_EXTRALIGHT 40 // 2.1.93
+#define FC_WEIGHT_REGULAR 80 // 2.1.93
+#define FC_WEIGHT_EXTRABOLD 205 // 2.1.93
+#endif
+// book was introduced in fontconfig-2.2.90 (and so fontconfig-2.3.0 in 2005)
+#ifndef FC_WEIGHT_BOOK
+#define FC_WEIGHT_BOOK 75
+#endif
+// extra black was introduced in fontconfig-2.4.91 (2007)
+#ifndef FC_WEIGHT_EXTRABLACK
+#define FC_WEIGHT_EXTRABLACK 215
+#endif
+
+/* static */ uint16_t
+gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
+{
+ int weight;
+ if (FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
+ return NS_FONT_WEIGHT_NORMAL;
+
+ if (weight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2)
+ return 100;
+ if (weight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2)
+ return 200;
+ if (weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2)
+ return 300;
+ if (weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
+ // This includes FC_WEIGHT_BOOK
+ return 400;
+ if (weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
+ return 500;
+ if (weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
+ return 600;
+ if (weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2)
+ return 700;
+ if (weight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2)
+ return 800;
+ if (weight <= FC_WEIGHT_BLACK)
+ return 900;
+
+ // including FC_WEIGHT_EXTRABLACK
+ return 901;
+}
+
+/* static */ int
+gfxFontconfigUtils::FcWeightForBaseWeight(int8_t aBaseWeight)
+{
+ NS_PRECONDITION(aBaseWeight >= 0 && aBaseWeight <= 10,
+ "base weight out of range");
+
+ switch (aBaseWeight) {
+ case 2:
+ return FC_WEIGHT_EXTRALIGHT;
+ case 3:
+ return FC_WEIGHT_LIGHT;
+ case 4:
+ return FC_WEIGHT_REGULAR;
+ case 5:
+ return FC_WEIGHT_MEDIUM;
+ case 6:
+ return FC_WEIGHT_DEMIBOLD;
+ case 7:
+ return FC_WEIGHT_BOLD;
+ case 8:
+ return FC_WEIGHT_EXTRABOLD;
+ case 9:
+ return FC_WEIGHT_BLACK;
+ }
+
+ // extremes
+ return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
+}
+
+/* static */ int16_t
+gfxFontconfigUtils::GetThebesStretch(FcPattern *aPattern)
+{
+ int width;
+ if (FcPatternGetInteger(aPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
+ return NS_FONT_STRETCH_NORMAL;
+ }
+
+ if (width <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
+ return NS_FONT_STRETCH_ULTRA_CONDENSED;
+ }
+ if (width <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
+ return NS_FONT_STRETCH_EXTRA_CONDENSED;
+ }
+ if (width <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
+ return NS_FONT_STRETCH_CONDENSED;
+ }
+ if (width <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
+ return NS_FONT_STRETCH_SEMI_CONDENSED;
+ }
+ if (width <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
+ return NS_FONT_STRETCH_NORMAL;
+ }
+ if (width <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
+ return NS_FONT_STRETCH_SEMI_EXPANDED;
+ }
+ if (width <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
+ return NS_FONT_STRETCH_EXPANDED;
+ }
+ if (width <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
+ return NS_FONT_STRETCH_EXTRA_EXPANDED;
+ }
+ return NS_FONT_STRETCH_ULTRA_EXPANDED;
+}
+
+/* static */ int
+gfxFontconfigUtils::FcWidthForThebesStretch(int16_t aStretch)
+{
+ switch (aStretch) {
+ default: // this will catch "normal" (0) as well as out-of-range values
+ return FC_WIDTH_NORMAL;
+ case NS_FONT_STRETCH_ULTRA_CONDENSED:
+ return FC_WIDTH_ULTRACONDENSED;
+ case NS_FONT_STRETCH_EXTRA_CONDENSED:
+ return FC_WIDTH_EXTRACONDENSED;
+ case NS_FONT_STRETCH_CONDENSED:
+ return FC_WIDTH_CONDENSED;
+ case NS_FONT_STRETCH_SEMI_CONDENSED:
+ return FC_WIDTH_SEMICONDENSED;
+ case NS_FONT_STRETCH_SEMI_EXPANDED:
+ return FC_WIDTH_SEMIEXPANDED;
+ case NS_FONT_STRETCH_EXPANDED:
+ return FC_WIDTH_EXPANDED;
+ case NS_FONT_STRETCH_EXTRA_EXPANDED:
+ return FC_WIDTH_EXTRAEXPANDED;
+ case NS_FONT_STRETCH_ULTRA_EXPANDED:
+ return FC_WIDTH_ULTRAEXPANDED;
+ }
+}
+
+// This makes a guess at an FC_WEIGHT corresponding to a base weight and
+// offset (without any knowledge of which weights are available).
+
+/* static */ int
+GuessFcWeight(const gfxFontStyle& aFontStyle)
+{
+ /*
+ * weights come in two parts crammed into one
+ * integer -- the "base" weight is weight / 100,
+ * the rest of the value is the "offset" from that
+ * weight -- the number of steps to move to adjust
+ * the weight in the list of supported font weights,
+ * this value can be negative or positive.
+ */
+ int8_t weight = aFontStyle.ComputeWeight();
+
+ // ComputeWeight trimmed the range of weights for us
+ NS_ASSERTION(weight >= 0 && weight <= 10,
+ "base weight out of range");
+
+ return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
+}
+
+static void
+AddString(FcPattern *aPattern, const char *object, const char *aString)
+{
+ FcPatternAddString(aPattern, object,
+ gfxFontconfigUtils::ToFcChar8(aString));
+}
+
+static void
+AddWeakString(FcPattern *aPattern, const char *object, const char *aString)
+{
+ FcValue value;
+ value.type = FcTypeString;
+ value.u.s = gfxFontconfigUtils::ToFcChar8(aString);
+
+ FcPatternAddWeak(aPattern, object, value, FcTrue);
+}
+
+static void
+AddLangGroup(FcPattern *aPattern, nsIAtom *aLangGroup)
+{
+ // Translate from mozilla's internal mapping into fontconfig's
+ nsAutoCString lang;
+ gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
+
+ if (!lang.IsEmpty()) {
+ AddString(aPattern, FC_LANG, lang.get());
+ }
+}
+
+nsReturnRef<FcPattern>
+gfxFontconfigUtils::NewPattern(const nsTArray<nsString>& aFamilies,
+ const gfxFontStyle& aFontStyle,
+ const char *aLang)
+{
+ static const char* sFontconfigGenerics[] =
+ { "sans-serif", "serif", "monospace", "fantasy", "cursive" };
+
+ nsAutoRef<FcPattern> pattern(FcPatternCreate());
+ if (!pattern)
+ return nsReturnRef<FcPattern>();
+
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
+ FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
+ FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
+ FcPatternAddInteger(pattern, FC_WIDTH, FcWidthForThebesStretch(aFontStyle.stretch));
+
+ if (aLang) {
+ AddString(pattern, FC_LANG, aLang);
+ }
+
+ bool useWeakBinding = false;
+ for (uint32_t i = 0; i < aFamilies.Length(); ++i) {
+ NS_ConvertUTF16toUTF8 family(aFamilies[i]);
+ if (!useWeakBinding) {
+ AddString(pattern, FC_FAMILY, family.get());
+
+ // fontconfig generic families are typically implemented with weak
+ // aliases (so that the preferred font depends on language).
+ // However, this would give them lower priority than subsequent
+ // non-generic families in the list. To ensure that subsequent
+ // families do not have a higher priority, they are given weak
+ // bindings.
+ for (uint32_t g = 0;
+ g < ArrayLength(sFontconfigGenerics);
+ ++g) {
+ if (0 == FcStrCmpIgnoreCase(ToFcChar8(sFontconfigGenerics[g]),
+ ToFcChar8(family.get()))) {
+ useWeakBinding = true;
+ break;
+ }
+ }
+ } else {
+ AddWeakString(pattern, FC_FAMILY, family.get());
+ }
+ }
+
+ return pattern.out();
+}
+
+gfxFontconfigUtils::gfxFontconfigUtils()
+ : mFontsByFamily(32)
+ , mFontsByFullname(32)
+ , mLangSupportTable(32)
+ , mLastConfig(nullptr)
+ , mBundledFontsInitialized(false)
+{
+ UpdateFontListInternal();
+}
+
+nsresult
+gfxFontconfigUtils::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ aListOfFonts.Clear();
+
+ nsTArray<nsCString> fonts;
+ nsresult rv = GetFontListInternal(fonts, aLangGroup);
+ if (NS_FAILED(rv))
+ return rv;
+
+ for (uint32_t i = 0; i < fonts.Length(); ++i) {
+ aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(fonts[i]));
+ }
+
+ aListOfFonts.Sort();
+
+ int32_t serif = 0, sansSerif = 0, monospace = 0;
+
+ // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
+ // "monospace", slightly different from CSS's 5.
+ if (aGenericFamily.IsEmpty())
+ serif = sansSerif = monospace = 1;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
+ serif = 1;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
+ sansSerif = 1;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
+ monospace = 1;
+ else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
+ aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
+ serif = sansSerif = 1;
+ else
+ NS_NOTREACHED("unexpected CSS generic font family");
+
+ // The first in the list becomes the default in
+ // FontBuilder.readFontSelection() if the preference-selected font is not
+ // available, so put system configured defaults first.
+ if (monospace)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
+ if (sansSerif)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
+ if (serif)
+ aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
+
+ return NS_OK;
+}
+
+struct MozLangGroupData {
+ nsIAtom* const& mozLangGroup;
+ const char *defaultLang;
+};
+
+const MozLangGroupData MozLangGroups[] = {
+ { nsGkAtoms::x_western, "en" },
+ { nsGkAtoms::x_cyrillic, "ru" },
+ { nsGkAtoms::x_devanagari, "hi" },
+ { nsGkAtoms::x_tamil, "ta" },
+ { nsGkAtoms::x_armn, "hy" },
+ { nsGkAtoms::x_beng, "bn" },
+ { nsGkAtoms::x_cans, "iu" },
+ { nsGkAtoms::x_ethi, "am" },
+ { nsGkAtoms::x_geor, "ka" },
+ { nsGkAtoms::x_gujr, "gu" },
+ { nsGkAtoms::x_guru, "pa" },
+ { nsGkAtoms::x_khmr, "km" },
+ { nsGkAtoms::x_knda, "kn" },
+ { nsGkAtoms::x_mlym, "ml" },
+ { nsGkAtoms::x_orya, "or" },
+ { nsGkAtoms::x_sinh, "si" },
+ { nsGkAtoms::x_telu, "te" },
+ { nsGkAtoms::x_tibt, "bo" },
+ { nsGkAtoms::Unicode, 0 },
+};
+
+static bool
+TryLangForGroup(const nsACString& aOSLang, nsIAtom *aLangGroup,
+ nsACString *aFcLang)
+{
+ // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
+ // aOSLang is in the form "language[_territory][.codeset][@modifier]".
+ // fontconfig takes languages in the form "language-territory".
+ // nsILanguageAtomService takes languages in the form language-subtag,
+ // where subtag may be a territory. fontconfig and nsILanguageAtomService
+ // handle case-conversion for us.
+ const char *pos, *end;
+ aOSLang.BeginReading(pos);
+ aOSLang.EndReading(end);
+ aFcLang->Truncate();
+ while (pos < end) {
+ switch (*pos) {
+ case '.':
+ case '@':
+ end = pos;
+ break;
+ case '_':
+ aFcLang->Append('-');
+ break;
+ default:
+ aFcLang->Append(*pos);
+ }
+ ++pos;
+ }
+
+ nsIAtom *atom =
+ gLangService->LookupLanguage(*aFcLang);
+
+ return atom == aLangGroup;
+}
+
+/* static */ void
+gfxFontconfigUtils::GetSampleLangForGroup(nsIAtom *aLangGroup,
+ nsACString *aFcLang)
+{
+ NS_PRECONDITION(aFcLang != nullptr, "aFcLang must not be NULL");
+
+ const MozLangGroupData *langGroup = nullptr;
+
+ for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
+ if (aLangGroup == MozLangGroups[i].mozLangGroup) {
+ langGroup = &MozLangGroups[i];
+ break;
+ }
+ }
+
+ if (!langGroup) {
+ // Not a special mozilla language group.
+ // Use aLangGroup as a language code.
+ aLangGroup->ToUTF8String(*aFcLang);
+ return;
+ }
+
+ // Check the environment for the users preferred language that corresponds
+ // to this langGroup.
+ if (!gLangService) {
+ CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
+ }
+
+ if (gLangService) {
+ const char *languages = getenv("LANGUAGE");
+ if (languages) {
+ const char separator = ':';
+
+ for (const char *pos = languages; true; ++pos) {
+ if (*pos == '\0' || *pos == separator) {
+ if (languages < pos &&
+ TryLangForGroup(Substring(languages, pos),
+ aLangGroup, aFcLang))
+ return;
+
+ if (*pos == '\0')
+ break;
+
+ languages = pos + 1;
+ }
+ }
+ }
+ const char *ctype = setlocale(LC_CTYPE, nullptr);
+ if (ctype &&
+ TryLangForGroup(nsDependentCString(ctype), aLangGroup, aFcLang))
+ return;
+ }
+
+ if (langGroup->defaultLang) {
+ aFcLang->Assign(langGroup->defaultLang);
+ } else {
+ aFcLang->Truncate();
+ }
+}
+
+nsresult
+gfxFontconfigUtils::GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
+ nsIAtom *aLangGroup)
+{
+ FcPattern *pat = nullptr;
+ FcObjectSet *os = nullptr;
+ FcFontSet *fs = nullptr;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ aListOfFonts.Clear();
+
+ pat = FcPatternCreate();
+ if (!pat)
+ goto end;
+
+ os = FcObjectSetBuild(FC_FAMILY, nullptr);
+ if (!os)
+ goto end;
+
+ // take the pattern and add the lang group to it
+ if (aLangGroup) {
+ AddLangGroup(pat, aLangGroup);
+ }
+
+ fs = FcFontList(nullptr, pat, os);
+ if (!fs)
+ goto end;
+
+ for (int i = 0; i < fs->nfont; i++) {
+ char *family;
+
+ if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &family) != FcResultMatch)
+ {
+ continue;
+ }
+
+ // Remove duplicates...
+ nsAutoCString strFamily(family);
+ if (aListOfFonts.Contains(strFamily))
+ continue;
+
+ aListOfFonts.AppendElement(strFamily);
+ }
+
+ rv = NS_OK;
+
+ end:
+ if (NS_FAILED(rv))
+ aListOfFonts.Clear();
+
+ if (pat)
+ FcPatternDestroy(pat);
+ if (os)
+ FcObjectSetDestroy(os);
+ if (fs)
+ FcFontSetDestroy(fs);
+
+ return rv;
+}
+
+nsresult
+gfxFontconfigUtils::UpdateFontList()
+{
+ return UpdateFontListInternal(true);
+}
+
+nsresult
+gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
+{
+ if (!aForce) {
+ // This checks periodically according to fontconfig's configured
+ // <rescan> interval.
+ FcInitBringUptoDate();
+ } else if (!FcConfigUptoDate(nullptr)) { // check now with aForce
+ mLastConfig = nullptr;
+ FcInitReinitialize();
+ }
+
+ // FcInitReinitialize() (used by FcInitBringUptoDate) creates a new config
+ // before destroying the old config, so the only way that we'd miss an
+ // update is if fontconfig did more than one update and the memory for the
+ // most recent config happened to be at the same location as the original
+ // config.
+ FcConfig *currentConfig = FcConfigGetCurrent();
+ if (currentConfig == mLastConfig)
+ return NS_OK;
+
+ ActivateBundledFonts();
+
+ // These FcFontSets are owned by fontconfig
+ FcFontSet *fontSets[] = {
+ FcConfigGetFonts(currentConfig, FcSetSystem)
+ , FcConfigGetFonts(currentConfig, FcSetApplication)
+ };
+
+ mFontsByFamily.Clear();
+ mFontsByFullname.Clear();
+ mLangSupportTable.Clear();
+
+ // Record the existing font families
+ for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
+ FcFontSet *fontSet = fontSets[fs];
+ if (!fontSet) { // the application set might not exist
+ continue;
+ }
+ for (int f = 0; f < fontSet->nfont; ++f) {
+ FcPattern *font = fontSet->fonts[f];
+
+ FcChar8 *family;
+ for (int v = 0;
+ FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
+ ++v) {
+ FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
+ if (entry) {
+ bool added = entry->AddFont(font);
+
+ if (!entry->mKey) {
+ // The reference to the font pattern keeps the pointer
+ // to string for the key valid. If adding the font
+ // failed then the entry must be removed.
+ if (added) {
+ entry->mKey = family;
+ } else {
+ mFontsByFamily.RemoveEntry(entry);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mLastConfig = currentConfig;
+ return NS_OK;
+}
+
+nsresult
+gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+ aFamilyName.Truncate();
+
+ // The fontconfig has generic family names in the font list.
+ if (aFontName.EqualsLiteral("serif") ||
+ aFontName.EqualsLiteral("sans-serif") ||
+ aFontName.EqualsLiteral("monospace")) {
+ aFamilyName.Assign(aFontName);
+ return NS_OK;
+ }
+
+ nsresult rv = UpdateFontListInternal();
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ConvertUTF16toUTF8 fontname(aFontName);
+
+ // return empty string if no such family exists
+ if (!IsExistingFamily(fontname))
+ return NS_OK;
+
+ FcPattern *pat = nullptr;
+ FcObjectSet *os = nullptr;
+ FcFontSet *givenFS = nullptr;
+ nsTArray<nsCString> candidates;
+ FcFontSet *candidateFS = nullptr;
+ rv = NS_ERROR_FAILURE;
+
+ pat = FcPatternCreate();
+ if (!pat)
+ goto end;
+
+ FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
+
+ os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, nullptr);
+ if (!os)
+ goto end;
+
+ givenFS = FcFontList(nullptr, pat, os);
+ if (!givenFS)
+ goto end;
+
+ // The first value associated with a FC_FAMILY property is the family
+ // returned by GetFontList(), so use this value if appropriate.
+
+ // See if there is a font face with first family equal to the given family.
+ for (int i = 0; i < givenFS->nfont; ++i) {
+ char *firstFamily;
+ if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
+ (FcChar8 **) &firstFamily) != FcResultMatch)
+ continue;
+
+ nsDependentCString first(firstFamily);
+ if (!candidates.Contains(first)) {
+ candidates.AppendElement(first);
+
+ if (fontname.Equals(first)) {
+ aFamilyName.Assign(aFontName);
+ rv = NS_OK;
+ goto end;
+ }
+ }
+ }
+
+ // See if any of the first family names represent the same set of font
+ // faces as the given family.
+ for (uint32_t j = 0; j < candidates.Length(); ++j) {
+ FcPatternDel(pat, FC_FAMILY);
+ FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
+
+ candidateFS = FcFontList(nullptr, pat, os);
+ if (!candidateFS)
+ goto end;
+
+ if (candidateFS->nfont != givenFS->nfont)
+ continue;
+
+ bool equal = true;
+ for (int i = 0; i < givenFS->nfont; ++i) {
+ if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
+ equal = false;
+ break;
+ }
+ }
+ if (equal) {
+ AppendUTF8toUTF16(candidates[j], aFamilyName);
+ rv = NS_OK;
+ goto end;
+ }
+ }
+
+ // No match found; return empty string.
+ rv = NS_OK;
+
+ end:
+ if (pat)
+ FcPatternDestroy(pat);
+ if (os)
+ FcObjectSetDestroy(os);
+ if (givenFS)
+ FcFontSetDestroy(givenFS);
+ if (candidateFS)
+ FcFontSetDestroy(candidateFS);
+
+ return rv;
+}
+
+bool
+gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
+{
+ return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName)) != nullptr;
+}
+
+const nsTArray< nsCountedRef<FcPattern> >&
+gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
+{
+ FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
+
+ if (!entry)
+ return mEmptyPatternArray;
+
+ return entry->GetFonts();
+}
+
+// Fontconfig only provides a fullname property for fonts in formats with SFNT
+// wrappers. For other font formats (including PCF and PS Type 1), a fullname
+// must be generated from the family and style properties. Only the first
+// family and style is checked, but that should be OK, as I don't expect
+// non-SFNT fonts to have multiple families or styles.
+bool
+gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(FcPattern *aFont,
+ nsACString *aFullname)
+{
+ FcChar8 *family;
+ if (FcPatternGetString(aFont, FC_FAMILY, 0, &family) != FcResultMatch)
+ return false;
+
+ aFullname->Truncate();
+ aFullname->Append(ToCString(family));
+
+ FcChar8 *style;
+ if (FcPatternGetString(aFont, FC_STYLE, 0, &style) == FcResultMatch &&
+ strcmp(ToCString(style), "Regular") != 0) {
+ aFullname->Append(' ');
+ aFullname->Append(ToCString(style));
+ }
+
+ return true;
+}
+
+bool
+gfxFontconfigUtils::FontsByFullnameEntry::KeyEquals(KeyTypePointer aKey) const
+{
+ const FcChar8 *key = mKey;
+ // If mKey is nullptr, key comes from the style and family of the first
+ // font.
+ nsAutoCString fullname;
+ if (!key) {
+ NS_ASSERTION(mFonts.Length(), "No font in FontsByFullnameEntry!");
+ GetFullnameFromFamilyAndStyle(mFonts[0], &fullname);
+
+ key = ToFcChar8(fullname);
+ }
+
+ return FcStrCmpIgnoreCase(aKey, key) == 0;
+}
+
+void
+gfxFontconfigUtils::AddFullnameEntries()
+{
+ // These FcFontSets are owned by fontconfig
+ FcFontSet *fontSets[] = {
+ FcConfigGetFonts(nullptr, FcSetSystem)
+ , FcConfigGetFonts(nullptr, FcSetApplication)
+ };
+
+ for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
+ FcFontSet *fontSet = fontSets[fs];
+ if (!fontSet) {
+ continue;
+ }
+ // Record the existing font families
+ for (int f = 0; f < fontSet->nfont; ++f) {
+ FcPattern *font = fontSet->fonts[f];
+
+ int v = 0;
+ FcChar8 *fullname;
+ while (FcPatternGetString(font,
+ FC_FULLNAME, v, &fullname) == FcResultMatch) {
+ FontsByFullnameEntry *entry =
+ mFontsByFullname.PutEntry(fullname);
+ if (entry) {
+ // entry always has space for one font, so the first
+ // AddFont will always succeed, and so the entry will
+ // always have a font from which to obtain the key.
+ bool added = entry->AddFont(font);
+ // The key may be nullptr either if this is the first
+ // font, or if the first font does not have a fullname
+ // property, and so the key is obtained from the font.
+ // Set the key in both cases. The check that AddFont
+ // succeeded is required for the second case.
+ if (!entry->mKey && added) {
+ entry->mKey = fullname;
+ }
+ }
+
+ ++v;
+ }
+
+ // Fontconfig does not provide a fullname property for all fonts.
+ if (v == 0) {
+ nsAutoCString name;
+ if (!GetFullnameFromFamilyAndStyle(font, &name))
+ continue;
+
+ FontsByFullnameEntry *entry =
+ mFontsByFullname.PutEntry(ToFcChar8(name));
+ if (entry) {
+ entry->AddFont(font);
+ // Either entry->mKey has been set for a previous font or it
+ // remains nullptr to indicate that the key is obtained from
+ // the first font.
+ }
+ }
+ }
+ }
+}
+
+const nsTArray< nsCountedRef<FcPattern> >&
+gfxFontconfigUtils::GetFontsForFullname(const FcChar8 *aFullname)
+{
+ if (mFontsByFullname.Count() == 0) {
+ AddFullnameEntries();
+ }
+
+ FontsByFullnameEntry *entry = mFontsByFullname.GetEntry(aFullname);
+
+ if (!entry)
+ return mEmptyPatternArray;
+
+ return entry->GetFonts();
+}
+
+static FcLangResult
+CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
+ FcLangResult result = FcLangDifferentLang;
+ for (uint32_t i = 0; ; ++i) {
+ FcChar8 a = FcToLower(aLangA[i]);
+ FcChar8 b = FcToLower(aLangB[i]);
+
+ if (a != b) {
+ if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
+ return FcLangDifferentCountry;
+
+ return result;
+ }
+ if (a == '\0')
+ return FcLangEqual;
+
+ if (a == '-') {
+ result = FcLangDifferentCountry;
+ }
+ }
+}
+
+/* static */
+FcLangResult
+gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
+{
+ // When fontconfig builds a pattern for a system font, it will set a
+ // single LangSet property value for the font. That value may be removed
+ // and additional string values may be added through FcConfigSubsitute
+ // with FcMatchScan. Values that are neither LangSet nor string are
+ // considered errors in fontconfig sort and match functions.
+ //
+ // If no string nor LangSet value is found, then either the font is a
+ // system font and the LangSet has been removed through FcConfigSubsitute,
+ // or the font is a web font and its language support is unknown.
+ // Returning FcLangDifferentLang for these fonts ensures that this font
+ // will not be assumed to satisfy the language, and so language will be
+ // prioritized in sorting fallback fonts.
+ FcValue value;
+ FcLangResult best = FcLangDifferentLang;
+ for (int v = 0;
+ FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch;
+ ++v) {
+
+ FcLangResult support;
+ switch (value.type) {
+ case FcTypeLangSet:
+ support = FcLangSetHasLang(value.u.l, aLang);
+ break;
+ case FcTypeString:
+ support = CompareLangString(value.u.s, aLang);
+ break;
+ default:
+ // error. continue to see if there is a useful value.
+ continue;
+ }
+
+ if (support < best) { // lower is better
+ if (support == FcLangEqual)
+ return support;
+ best = support;
+ }
+ }
+
+ return best;
+}
+
+gfxFontconfigUtils::LangSupportEntry *
+gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts)
+{
+ // Currently any unrecognized languages from documents will be converted
+ // to x-unicode by nsILanguageAtomService, so there is a limit on the
+ // langugages that will be added here. Reconsider when/if document
+ // languages are passed to this routine.
+
+ LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
+ if (!entry)
+ return nullptr;
+
+ FcLangResult best = FcLangDifferentLang;
+
+ if (!entry->IsKeyInitialized()) {
+ entry->InitKey(aLang);
+ } else {
+ // mSupport is already initialized.
+ if (!aWithFonts)
+ return entry;
+
+ best = entry->mSupport;
+ // If there is support for this language, an empty font list indicates
+ // that the list hasn't been initialized yet.
+ if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
+ return entry;
+ }
+
+ // These FcFontSets are owned by fontconfig
+ FcFontSet *fontSets[] = {
+ FcConfigGetFonts(nullptr, FcSetSystem)
+ , FcConfigGetFonts(nullptr, FcSetApplication)
+ };
+
+ AutoTArray<FcPattern*,100> fonts;
+
+ for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
+ FcFontSet *fontSet = fontSets[fs];
+ if (!fontSet) {
+ continue;
+ }
+ for (int f = 0; f < fontSet->nfont; ++f) {
+ FcPattern *font = fontSet->fonts[f];
+
+ FcLangResult support = GetLangSupport(font, aLang);
+
+ if (support < best) { // lower is better
+ best = support;
+ if (aWithFonts) {
+ fonts.Clear();
+ } else if (best == FcLangEqual) {
+ break;
+ }
+ }
+
+ // The font list in the LangSupportEntry is expected to be used
+ // only when no default fonts support the language. There would
+ // be a large number of fonts in entries for languages using Latin
+ // script but these do not need to be created because default
+ // fonts already support these languages.
+ if (aWithFonts && support != FcLangDifferentLang &&
+ support == best) {
+ fonts.AppendElement(font);
+ }
+ }
+ }
+
+ entry->mSupport = best;
+ if (aWithFonts) {
+ if (fonts.Length() != 0) {
+ entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
+ } else if (best != FcLangDifferentLang) {
+ // Previously there was a font that supported this language at the
+ // level of entry->mSupport, but it has now disappeared. At least
+ // entry->mSupport needs to be recalculated, but this is an
+ // indication that the set of installed fonts has changed, so
+ // update all caches.
+ mLastConfig = nullptr; // invalidates caches
+ UpdateFontListInternal(true);
+ return GetLangSupportEntry(aLang, aWithFonts);
+ }
+ }
+
+ return entry;
+}
+
+FcLangResult
+gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
+{
+ UpdateFontListInternal();
+
+ LangSupportEntry *entry = GetLangSupportEntry(aLang, false);
+ if (!entry)
+ return FcLangEqual;
+
+ return entry->mSupport;
+}
+
+const nsTArray< nsCountedRef<FcPattern> >&
+gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
+{
+ LangSupportEntry *entry = GetLangSupportEntry(aLang, true);
+ if (!entry)
+ return mEmptyPatternArray;
+
+ return entry->mFonts;
+}
+
+
+void
+gfxFontconfigUtils::ActivateBundledFonts()
+{
+ if (!mBundledFontsInitialized) {
+ mBundledFontsInitialized = true;
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ return;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return;
+ }
+ if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
+ return;
+ }
+ }
+ if (!mBundledFontsPath.IsEmpty()) {
+ FcConfigAppFontAddDir(nullptr, (const FcChar8*)mBundledFontsPath.get());
+ }
+}
+
+
+gfxFontconfigFontBase::gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
+ FcPattern *aPattern,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle)
+ : gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
+ , mPattern(aPattern)
+{
+}
+
diff --git a/system/graphics/thebes/gfxFontconfigUtils.h b/system/graphics/thebes/gfxFontconfigUtils.h
new file mode 100644
index 000000000..db0fe518e
--- /dev/null
+++ b/system/graphics/thebes/gfxFontconfigUtils.h
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONTCONFIG_UTILS_H
+#define GFX_FONTCONFIG_UTILS_H
+
+#include "gfxPlatform.h"
+
+#include "mozilla/MathAlgorithms.h"
+#include "nsAutoRef.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "nsISupportsImpl.h"
+#include "gfxFT2FontBase.h"
+
+#include <fontconfig/fontconfig.h>
+
+
+template <>
+class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
+{
+public:
+ static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
+ static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
+};
+
+template <>
+class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
+{
+public:
+ static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
+};
+
+template <>
+class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
+{
+public:
+ static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
+};
+
+class gfxIgnoreCaseCStringComparator
+{
+ public:
+ bool Equals(const nsACString& a, const nsACString& b) const
+ {
+ return nsCString(a).Equals(b, nsCaseInsensitiveCStringComparator());
+ }
+
+ bool LessThan(const nsACString& a, const nsACString& b) const
+ {
+ return a < b;
+ }
+};
+
+class gfxFontconfigUtils {
+public:
+ gfxFontconfigUtils();
+
+ static gfxFontconfigUtils* GetFontconfigUtils() {
+ if (!sUtils)
+ sUtils = new gfxFontconfigUtils();
+ return sUtils;
+ }
+
+ static void Shutdown();
+
+ nsresult GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts);
+
+ nsresult UpdateFontList();
+
+ nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
+ const nsTArray< nsCountedRef<FcPattern> >&
+ GetFontsForFamily(const FcChar8 *aFamilyName);
+
+ const nsTArray< nsCountedRef<FcPattern> >&
+ GetFontsForFullname(const FcChar8 *aFullname);
+
+ // Returns the best support that any font offers for |aLang|.
+ FcLangResult GetBestLangSupport(const FcChar8 *aLang);
+ // Returns the fonts offering this best level of support.
+ const nsTArray< nsCountedRef<FcPattern> >&
+ GetFontsForLang(const FcChar8 *aLang);
+
+ // Retuns the language support for a fontconfig font pattern
+ static FcLangResult GetLangSupport(FcPattern *aFont, const FcChar8 *aLang);
+
+ // Conversions between FcChar8*, which is unsigned char*,
+ // and (signed) char*, that check the type of the argument.
+ static const FcChar8 *ToFcChar8(const char *aCharPtr)
+ {
+ return reinterpret_cast<const FcChar8*>(aCharPtr);
+ }
+ static const FcChar8 *ToFcChar8(const nsCString& aCString)
+ {
+ return ToFcChar8(aCString.get());
+ }
+ static const char *ToCString(const FcChar8 *aChar8Ptr)
+ {
+ return reinterpret_cast<const char*>(aChar8Ptr);
+ }
+
+ static uint8_t FcSlantToThebesStyle(int aFcSlant);
+ static uint8_t GetThebesStyle(FcPattern *aPattern); // slant
+ static uint16_t GetThebesWeight(FcPattern *aPattern);
+ static int16_t GetThebesStretch(FcPattern *aPattern);
+
+ static int GetFcSlant(const gfxFontStyle& aFontStyle);
+ // Returns a precise FC_WEIGHT from |aBaseWeight|,
+ // which is a CSS absolute weight / 100.
+ static int FcWeightForBaseWeight(int8_t aBaseWeight);
+
+ static int FcWidthForThebesStretch(int16_t aStretch);
+
+ static bool GetFullnameFromFamilyAndStyle(FcPattern *aFont,
+ nsACString *aFullname);
+
+ // This doesn't consider which faces exist, and so initializes the pattern
+ // using a guessed weight, and doesn't consider sizeAdjust.
+ static nsReturnRef<FcPattern>
+ NewPattern(const nsTArray<nsString>& aFamilies,
+ const gfxFontStyle& aFontStyle, const char *aLang);
+
+ /**
+ * @param aLangGroup [in] a Mozilla langGroup.
+ * @param aFcLang [out] returns a language suitable for fontconfig
+ * matching |aLangGroup| or an empty string if no match is found.
+ */
+ static void GetSampleLangForGroup(nsIAtom *aLangGroup,
+ nsACString *aFcLang);
+
+protected:
+ // Base class for hash table entries with case-insensitive FcChar8
+ // string keys.
+ class FcStrEntryBase : public PLDHashEntryHdr {
+ public:
+ typedef const FcChar8 *KeyType;
+ typedef const FcChar8 *KeyTypePointer;
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ // Case-insensitive hash.
+ //
+ // fontconfig always ignores case of ASCII characters in family
+ // names and languages, but treatment of whitespace in families is
+ // not consistent. FcFontSort and FcFontMatch ignore whitespace
+ // except for whitespace in the first character, while FcFontList
+ // and config subsitution tests require whitespace to match
+ // exactly. CSS 2.1 implies that whitespace is important in the
+ // font-family property. FcStrCmpIgnoreCase considers whitespace
+ // important.
+ static PLDHashNumber HashKey(const FcChar8 *aKey) {
+ uint32_t hash = 0;
+ for (const FcChar8 *c = aKey; *c != '\0'; ++c) {
+ hash = mozilla::RotateLeft(hash, 3) ^ FcToLower(*c);
+ }
+ return hash;
+ }
+ enum { ALLOW_MEMMOVE = true };
+ };
+
+public:
+ // Hash entry with a dependent const FcChar8* pointer to an external
+ // string for a key (and no data). The user must ensure that the string
+ // associated with the pointer is not destroyed. This entry type is
+ // useful for family name keys as the family name string is held in the
+ // font pattern.
+ class DepFcStrEntry : public FcStrEntryBase {
+ public:
+ // When constructing a new entry in the hashtable, the key is left
+ // nullptr. The caller of PutEntry() must fill in mKey when nullptr.
+ // This provides a mechanism for the caller of PutEntry() to determine
+ // whether the entry has been initialized.
+ explicit DepFcStrEntry(KeyTypePointer aName)
+ : mKey(nullptr) { }
+
+ DepFcStrEntry(const DepFcStrEntry& toCopy)
+ : mKey(toCopy.mKey) { }
+
+ bool KeyEquals(KeyTypePointer aKey) const {
+ return FcStrCmpIgnoreCase(aKey, mKey) == 0;
+ }
+
+ const FcChar8 *mKey;
+ };
+
+ // Hash entry that uses a copy of an FcChar8 string to store the key.
+ // This entry type is useful for language keys, as languages are usually
+ // not stored as strings in font patterns.
+ class CopiedFcStrEntry : public FcStrEntryBase {
+ public:
+ // When constructing a new entry in the hashtable, the key is void.
+ // The caller of PutEntry() must call InitKey() when IsKeyInitialized()
+ // returns false. This provides a mechanism for the caller of
+ // PutEntry() to determine whether the entry has been initialized.
+ explicit CopiedFcStrEntry(KeyTypePointer aName) {
+ mKey.SetIsVoid(true);
+ }
+
+ CopiedFcStrEntry(const CopiedFcStrEntry& toCopy)
+ : mKey(toCopy.mKey) { }
+
+ bool KeyEquals(KeyTypePointer aKey) const {
+ return FcStrCmpIgnoreCase(aKey, ToFcChar8(mKey)) == 0;
+ }
+
+ bool IsKeyInitialized() { return !mKey.IsVoid(); }
+ void InitKey(const FcChar8* aKey) { mKey.Assign(ToCString(aKey)); }
+
+ private:
+ nsCString mKey;
+ };
+
+protected:
+ class FontsByFcStrEntry : public DepFcStrEntry {
+ public:
+ explicit FontsByFcStrEntry(KeyTypePointer aName)
+ : DepFcStrEntry(aName) { }
+
+ FontsByFcStrEntry(const FontsByFcStrEntry& toCopy)
+ : DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
+
+ bool AddFont(FcPattern *aFont) {
+ return mFonts.AppendElement(aFont) != nullptr;
+ }
+ const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
+ return mFonts;
+ }
+ private:
+ nsTArray< nsCountedRef<FcPattern> > mFonts;
+ };
+
+ // FontsByFullnameEntry is similar to FontsByFcStrEntry (used for
+ // mFontsByFamily) except for two differences:
+ //
+ // * The font does not always contain a single string for the fullname, so
+ // the key is sometimes a combination of family and style.
+ //
+ // * There is usually only one font.
+ class FontsByFullnameEntry : public DepFcStrEntry {
+ public:
+ // When constructing a new entry in the hashtable, the key is left
+ // nullptr. The caller of PutEntry() is must fill in mKey when adding
+ // the first font if the key is not derived from the family and style.
+ // If the key is derived from family and style, a font must be added.
+ explicit FontsByFullnameEntry(KeyTypePointer aName)
+ : DepFcStrEntry(aName) { }
+
+ FontsByFullnameEntry(const FontsByFullnameEntry& toCopy)
+ : DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
+
+ bool KeyEquals(KeyTypePointer aKey) const;
+
+ bool AddFont(FcPattern *aFont) {
+ return mFonts.AppendElement(aFont) != nullptr;
+ }
+ const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
+ return mFonts;
+ }
+
+ // Don't memmove the AutoTArray.
+ enum { ALLOW_MEMMOVE = false };
+ private:
+ // There is usually only one font, but sometimes more.
+ AutoTArray<nsCountedRef<FcPattern>,1> mFonts;
+ };
+
+ class LangSupportEntry : public CopiedFcStrEntry {
+ public:
+ explicit LangSupportEntry(KeyTypePointer aName)
+ : CopiedFcStrEntry(aName) { }
+
+ LangSupportEntry(const LangSupportEntry& toCopy)
+ : CopiedFcStrEntry(toCopy), mSupport(toCopy.mSupport) { }
+
+ FcLangResult mSupport;
+ nsTArray< nsCountedRef<FcPattern> > mFonts;
+ };
+
+ static gfxFontconfigUtils* sUtils;
+
+ bool IsExistingFamily(const nsCString& aFamilyName);
+
+ nsresult GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
+ nsIAtom *aLangGroup);
+ nsresult UpdateFontListInternal(bool aForce = false);
+
+ void AddFullnameEntries();
+
+ LangSupportEntry *GetLangSupportEntry(const FcChar8 *aLang,
+ bool aWithFonts);
+
+ // mFontsByFamily and mFontsByFullname contain entries only for families
+ // and fullnames for which there are fonts.
+ nsTHashtable<FontsByFcStrEntry> mFontsByFamily;
+ nsTHashtable<FontsByFullnameEntry> mFontsByFullname;
+ // mLangSupportTable contains an entry for each language that has been
+ // looked up through GetLangSupportEntry, even when the language is not
+ // supported.
+ nsTHashtable<LangSupportEntry> mLangSupportTable;
+ const nsTArray< nsCountedRef<FcPattern> > mEmptyPatternArray;
+
+ FcConfig *mLastConfig;
+
+ void ActivateBundledFonts();
+
+ nsCString mBundledFontsPath;
+ bool mBundledFontsInitialized;
+};
+
+class gfxFontconfigFontBase : public gfxFT2FontBase {
+public:
+ gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
+ FcPattern *aPattern,
+ gfxFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle);
+
+ virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }
+ virtual FcPattern *GetPattern() const { return mPattern; }
+
+private:
+ nsCountedRef<FcPattern> mPattern;
+};
+
+#endif /* GFX_FONTCONFIG_UTILS_H */
diff --git a/system/graphics/thebes/gfxGDIFont.cpp b/system/graphics/thebes/gfxGDIFont.cpp
new file mode 100644
index 000000000..d6e98105c
--- /dev/null
+++ b/system/graphics/thebes/gfxGDIFont.cpp
@@ -0,0 +1,563 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxGDIFont.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/WindowsVersion.h"
+
+#include <algorithm>
+#include "gfxWindowsPlatform.h"
+#include "gfxContext.h"
+#include "mozilla/Preferences.h"
+#include "nsUnicodeProperties.h"
+#include "gfxFontConstants.h"
+#include "gfxTextRun.h"
+
+#include "cairo-win32.h"
+
+#define ROUND(x) floor((x) + 0.5)
+
+using namespace mozilla;
+using namespace mozilla::unicode;
+
+static inline cairo_antialias_t
+GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
+{
+ switch (anAntialiasOption) {
+ default:
+ case gfxFont::kAntialiasDefault:
+ return CAIRO_ANTIALIAS_DEFAULT;
+ case gfxFont::kAntialiasNone:
+ return CAIRO_ANTIALIAS_NONE;
+ case gfxFont::kAntialiasGrayscale:
+ return CAIRO_ANTIALIAS_GRAY;
+ case gfxFont::kAntialiasSubpixel:
+ return CAIRO_ANTIALIAS_SUBPIXEL;
+ }
+}
+
+gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold,
+ AntialiasOption anAAOption)
+ : gfxFont(aFontEntry, aFontStyle, anAAOption),
+ mFont(nullptr),
+ mFontFace(nullptr),
+ mMetrics(nullptr),
+ mSpaceGlyph(0),
+ mNeedsBold(aNeedsBold),
+ mScriptCache(nullptr)
+{
+ Initialize();
+}
+
+gfxGDIFont::~gfxGDIFont()
+{
+ if (mScaledFont) {
+ cairo_scaled_font_destroy(mScaledFont);
+ }
+ if (mFontFace) {
+ cairo_font_face_destroy(mFontFace);
+ }
+ if (mFont) {
+ ::DeleteObject(mFont);
+ }
+ if (mScriptCache) {
+ ScriptFreeCache(&mScriptCache);
+ }
+ delete mMetrics;
+}
+
+gfxFont*
+gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
+{
+ return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
+ &mStyle, mNeedsBold, anAAOption);
+}
+
+bool
+gfxGDIFont::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ if (!mIsValid) {
+ NS_WARNING("invalid font! expect incorrect text rendering");
+ return false;
+ }
+
+ // Ensure the cairo font is set up, so there's no risk it'll fall back to
+ // creating a "toy" font internally (see bug 544617).
+ // We must check that this succeeded, otherwise we risk cairo creating the
+ // wrong kind of font internally as a fallback (bug 744480).
+ if (!SetupCairoFont(aDrawTarget)) {
+ return false;
+ }
+
+ return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
+ aVertical, aShapedText);
+}
+
+const gfxFont::Metrics&
+gfxGDIFont::GetHorizontalMetrics()
+{
+ return *mMetrics;
+}
+
+uint32_t
+gfxGDIFont::GetSpaceGlyph()
+{
+ return mSpaceGlyph;
+}
+
+bool
+gfxGDIFont::SetupCairoFont(DrawTarget* aDrawTarget)
+{
+ if (!mScaledFont ||
+ cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
+ // Don't cairo_set_scaled_font as that would propagate the error to
+ // the cairo_t, precluding any further drawing.
+ return false;
+ }
+ cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
+ return true;
+}
+
+gfxFont::RunMetrics
+gfxGDIFont::Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget *aRefDrawTarget,
+ Spacing *aSpacing,
+ uint16_t aOrientation)
+{
+ gfxFont::RunMetrics metrics =
+ gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
+ aRefDrawTarget, aSpacing, aOrientation);
+
+ // if aBoundingBoxType is LOOSE_INK_EXTENTS
+ // and the underlying cairo font may be antialiased,
+ // we can't trust Windows to have considered all the pixels
+ // so we need to add "padding" to the bounds.
+ // (see bugs 475968, 439831, compare also bug 445087)
+ if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
+ mAntialiasOption != kAntialiasNone &&
+ metrics.mBoundingBox.width > 0) {
+ metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
+ metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
+ }
+
+ return metrics;
+}
+
+void
+gfxGDIFont::Initialize()
+{
+ NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");
+
+ LOGFONTW logFont;
+
+ // Figure out if we want to do synthetic oblique styling.
+ GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
+ bool wantFakeItalic = mStyle.style != NS_FONT_STYLE_NORMAL &&
+ fe->IsUpright() && mStyle.allowSyntheticStyle;
+
+ // If the font's family has an actual italic face (but font matching
+ // didn't choose it), we have to use a cairo transform instead of asking
+ // GDI to italicize, because that would use a different face and result
+ // in a possible glyph ID mismatch between shaping and rendering.
+ //
+ // We use the mFamilyHasItalicFace flag in the entry in case of user fonts,
+ // where the *CSS* family may not know about italic faces that are present
+ // in the *GDI* family, and which GDI would use if we asked it to perform
+ // the "italicization".
+ bool useCairoFakeItalic = wantFakeItalic && fe->mFamilyHasItalicFace;
+
+ if (mAdjustedSize == 0.0) {
+ mAdjustedSize = mStyle.size;
+ if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
+ // to implement font-size-adjust, we first create the "unadjusted" font
+ FillLogFont(logFont, mAdjustedSize,
+ wantFakeItalic && !useCairoFakeItalic);
+ mFont = ::CreateFontIndirectW(&logFont);
+
+ // initialize its metrics so we can calculate size adjustment
+ Initialize();
+
+ // Unless the font was so small that GDI metrics rounded to zero,
+ // calculate the properly adjusted size, and then proceed
+ // to recreate mFont and recalculate metrics
+ if (mMetrics->xHeight > 0.0 && mMetrics->emHeight > 0.0) {
+ gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
+ mAdjustedSize = mStyle.GetAdjustedSize(aspect);
+ }
+
+ // delete the temporary font and metrics
+ ::DeleteObject(mFont);
+ mFont = nullptr;
+ delete mMetrics;
+ mMetrics = nullptr;
+ } else if (mStyle.sizeAdjust == 0.0) {
+ mAdjustedSize = 0.0;
+ }
+ }
+
+ // (bug 724231) for local user fonts, we don't use GDI's synthetic bold,
+ // as it could lead to a different, incompatible face being used
+ // but instead do our own multi-striking
+ if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) {
+ mApplySyntheticBold = true;
+ }
+
+ // this may end up being zero
+ mAdjustedSize = ROUND(mAdjustedSize);
+ FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
+ mFont = ::CreateFontIndirectW(&logFont);
+
+ mMetrics = new gfxFont::Metrics;
+ ::memset(mMetrics, 0, sizeof(*mMetrics));
+
+ AutoDC dc;
+ SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
+ AutoSelectFont selectFont(dc.GetDC(), mFont);
+
+ // Get font metrics if size > 0
+ if (mAdjustedSize > 0.0) {
+
+ OUTLINETEXTMETRIC oMetrics;
+ TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
+
+ if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
+ mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
+ mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
+ mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
+ mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
+
+ const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
+ GLYPHMETRICS gm;
+ DWORD len = GetGlyphOutlineW(dc.GetDC(), char16_t('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
+ if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
+ // 56% of ascent, best guess for true type
+ mMetrics->xHeight =
+ ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
+ } else {
+ mMetrics->xHeight = gm.gmptGlyphOrigin.y;
+ }
+ len = GetGlyphOutlineW(dc.GetDC(), char16_t('H'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
+ if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
+ mMetrics->capHeight = metrics.tmAscent - metrics.tmInternalLeading;
+ } else {
+ mMetrics->capHeight = gm.gmptGlyphOrigin.y;
+ }
+ mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
+ gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
+ mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
+ mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
+ if (oMetrics.otmEMSquare > 0) {
+ mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
+ }
+ } else {
+ // Make a best-effort guess at extended metrics
+ // this is based on general typographic guidelines
+
+ // GetTextMetrics can fail if the font file has been removed
+ // or corrupted recently.
+ BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
+ if (!result) {
+ NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
+ mIsValid = false;
+ memset(mMetrics, 0, sizeof(*mMetrics));
+ return;
+ }
+
+ mMetrics->xHeight =
+ ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
+ mMetrics->strikeoutSize = 1;
+ mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
+ mMetrics->underlineSize = 1;
+ mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
+ mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
+ mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
+ mMetrics->emDescent = metrics.tmDescent;
+ mMetrics->capHeight = mMetrics->emAscent;
+ }
+
+ mMetrics->internalLeading = metrics.tmInternalLeading;
+ mMetrics->externalLeading = metrics.tmExternalLeading;
+ mMetrics->maxHeight = metrics.tmHeight;
+ mMetrics->maxAscent = metrics.tmAscent;
+ mMetrics->maxDescent = metrics.tmDescent;
+ mMetrics->maxAdvance = metrics.tmMaxCharWidth;
+ mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth);
+ // The font is monospace when TMPF_FIXED_PITCH is *not* set!
+ // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
+ if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
+ mMetrics->maxAdvance = mMetrics->aveCharWidth;
+ }
+
+ // For fonts with USE_TYPO_METRICS set in the fsSelection field,
+ // let the OS/2 sTypo* metrics override the previous values.
+ // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
+ // Using the equivalent values from oMetrics provides inconsistent
+ // results with CFF fonts, so we instead rely on OS2Table.
+ gfxFontEntry::AutoTable os2Table(mFontEntry,
+ TRUETYPE_TAG('O','S','/','2'));
+ if (os2Table) {
+ uint32_t len;
+ const OS2Table *os2 =
+ reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table,
+ &len));
+ if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
+ const uint16_t kUseTypoMetricsMask = 1 << 7;
+ if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask)) {
+ double ascent = int16_t(os2->sTypoAscender);
+ double descent = int16_t(os2->sTypoDescender);
+ double lineGap = int16_t(os2->sTypoLineGap);
+ mMetrics->maxAscent = ROUND(ascent * mFUnitsConvFactor);
+ mMetrics->maxDescent = -ROUND(descent * mFUnitsConvFactor);
+ mMetrics->maxHeight =
+ mMetrics->maxAscent + mMetrics->maxDescent;
+ mMetrics->internalLeading =
+ mMetrics->maxHeight - mMetrics->emHeight;
+ gfxFloat lineHeight =
+ ROUND((ascent - descent + lineGap) * mFUnitsConvFactor);
+ lineHeight = std::max(lineHeight, mMetrics->maxHeight);
+ mMetrics->externalLeading =
+ lineHeight - mMetrics->maxHeight;
+ }
+ }
+ // although sxHeight and sCapHeight are signed fields, we consider
+ // negative values to be erroneous and just ignore them
+ if (uint16_t(os2->version) >= 2) {
+ // version 2 and later includes the x-height and cap-height fields
+ if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
+ int16_t(os2->sxHeight) > 0) {
+ mMetrics->xHeight = ROUND(int16_t(os2->sxHeight) * mFUnitsConvFactor);
+ }
+ if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
+ int16_t(os2->sCapHeight) > 0) {
+ mMetrics->capHeight = ROUND(int16_t(os2->sCapHeight) * mFUnitsConvFactor);
+ }
+ }
+ }
+
+ WORD glyph;
+ SIZE size;
+ DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
+ GGI_MARK_NONEXISTING_GLYPHS);
+ if (ret != GDI_ERROR && glyph != 0xFFFF) {
+ mSpaceGlyph = glyph;
+ // Cache the width of a single space.
+ GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
+ mMetrics->spaceWidth = ROUND(size.cx);
+ } else {
+ mMetrics->spaceWidth = mMetrics->aveCharWidth;
+ }
+
+ // Cache the width of digit zero, if available.
+ ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph,
+ GGI_MARK_NONEXISTING_GLYPHS);
+ if (ret != GDI_ERROR && glyph != 0xFFFF) {
+ GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size);
+ mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
+ } else {
+ mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
+ }
+
+ SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
+ } else {
+ mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero
+ }
+
+ if (IsSyntheticBold()) {
+ mMetrics->aveCharWidth += GetSyntheticBoldOffset();
+ mMetrics->maxAdvance += GetSyntheticBoldOffset();
+ }
+
+ mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
+ mFont);
+
+ cairo_matrix_t sizeMatrix, ctm;
+ cairo_matrix_init_identity(&ctm);
+ cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
+
+ if (useCairoFakeItalic) {
+ // Skew the matrix to do fake italic if it wasn't already applied
+ // via the LOGFONT
+ double skewfactor = OBLIQUE_SKEW_FACTOR;
+ cairo_matrix_t style;
+ cairo_matrix_init(&style,
+ 1, //xx
+ 0, //yx
+ -1 * skewfactor, //xy
+ 1, //yy
+ 0, //x0
+ 0); //y0
+ cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
+ }
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+ if (mAntialiasOption != kAntialiasDefault) {
+ cairo_font_options_set_antialias(fontOptions,
+ GetCairoAntialiasOption(mAntialiasOption));
+ }
+ mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
+ &ctm, fontOptions);
+ cairo_font_options_destroy(fontOptions);
+
+ if (!mScaledFont ||
+ cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
+#ifdef DEBUG
+ char warnBuf[1024];
+ SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
+ NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
+ mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
+ NS_WARNING(warnBuf);
+#endif
+ mIsValid = false;
+ } else {
+ mIsValid = true;
+ }
+
+#if 0
+ printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
+ NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
+ printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
+ printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
+ printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
+ printf(" spaceWidth: %f aveCharWidth: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth);
+ printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
+ printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
+ mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
+#endif
+}
+
+void
+gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
+ bool aUseGDIFakeItalic)
+{
+ GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
+
+ uint16_t weight;
+ if (fe->IsUserFont()) {
+ if (fe->IsLocalUserFont()) {
+ // for local user fonts, don't change the original weight
+ // in the entry's logfont, because that could alter the
+ // choice of actual face used (bug 724231)
+ weight = 0;
+ } else {
+ // avoid GDI synthetic bold which occurs when weight
+ // specified is >= font data weight + 200
+ weight = mNeedsBold ? 700 : 200;
+ }
+ } else {
+ weight = mNeedsBold ? 700 : fe->Weight();
+ }
+
+ fe->FillLogFont(&aLogFont, weight, aSize);
+
+ // If GDI synthetic italic is wanted, force the lfItalic field to true
+ if (aUseGDIFakeItalic) {
+ aLogFont.lfItalic = 1;
+ }
+}
+
+uint32_t
+gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
+{
+ // Callback used only for fonts that lack a 'cmap' table.
+
+ // We don't support variation selector sequences or non-BMP characters
+ // in the legacy bitmap, vector or postscript fonts that might use
+ // this code path.
+ if (aUnicode > 0xffff || aVarSelector) {
+ return 0;
+ }
+
+ if (!mGlyphIDs) {
+ mGlyphIDs = MakeUnique<nsDataHashtable<nsUint32HashKey,uint32_t>>(64);
+ }
+
+ uint32_t gid;
+ if (mGlyphIDs->Get(aUnicode, &gid)) {
+ return gid;
+ }
+
+ wchar_t ch = aUnicode;
+ WORD glyph;
+ DWORD ret = ScriptGetCMap(nullptr, &mScriptCache, &ch, 1, 0, &glyph);
+ if (ret != S_OK) {
+ AutoDC dc;
+ AutoSelectFont fs(dc.GetDC(), GetHFONT());
+ if (ret == E_PENDING) {
+ // Try ScriptGetCMap again now that we've set up the font.
+ ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
+ }
+ if (ret != S_OK) {
+ // If ScriptGetCMap still failed, fall back to GetGlyphIndicesW
+ // (see bug 1105807).
+ ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
+ GGI_MARK_NONEXISTING_GLYPHS);
+ if (ret == GDI_ERROR || glyph == 0xFFFF) {
+ glyph = 0;
+ }
+ }
+ }
+
+ mGlyphIDs->Put(aUnicode, glyph);
+ return glyph;
+}
+
+int32_t
+gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
+{
+ if (!mGlyphWidths) {
+ mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
+ }
+
+ int32_t width;
+ if (mGlyphWidths->Get(aGID, &width)) {
+ return width;
+ }
+
+ DCFromDrawTarget dc(aDrawTarget);
+ AutoSelectFont fs(dc, GetHFONT());
+
+ int devWidth;
+ if (GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
+ // clamp value to range [0..0x7fff], and convert to 16.16 fixed-point
+ devWidth = std::min(std::max(0, devWidth), 0x7fff);
+ width = devWidth << 16;
+ mGlyphWidths->Put(aGID, width);
+ return width;
+ }
+
+ return -1;
+}
+
+void
+gfxGDIFont::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ aSizes->mFontInstances += aMallocSizeOf(mMetrics);
+ if (mGlyphWidths) {
+ aSizes->mFontInstances +=
+ mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+}
+
+void
+gfxGDIFont::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
diff --git a/system/graphics/thebes/gfxGDIFont.h b/system/graphics/thebes/gfxGDIFont.h
new file mode 100644
index 000000000..9d8ac9552
--- /dev/null
+++ b/system/graphics/thebes/gfxGDIFont.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GDIFONT_H
+#define GFX_GDIFONT_H
+
+#include "mozilla/MemoryReporting.h"
+#include "gfxFont.h"
+#include "gfxGDIFontList.h"
+
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+
+#include "cairo.h"
+#include "usp10.h"
+
+class gfxGDIFont : public gfxFont
+{
+public:
+ gfxGDIFont(GDIFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle,
+ bool aNeedsBold,
+ AntialiasOption anAAOption = kAntialiasDefault);
+
+ virtual ~gfxGDIFont();
+
+ HFONT GetHFONT() { return mFont; }
+
+ cairo_font_face_t *CairoFontFace() { return mFontFace; }
+ cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; }
+
+ /* overrides for the pure virtual methods in gfxFont */
+ virtual uint32_t GetSpaceGlyph() override;
+
+ virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
+
+ /* override Measure to add padding for antialiasing */
+ virtual RunMetrics Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget *aDrawTargetForTightBoundingBox,
+ Spacing *aSpacing,
+ uint16_t aOrientation) override;
+
+ /* required for MathML to suppress effects of ClearType "padding" */
+ virtual gfxFont*
+ CopyWithAntialiasOption(AntialiasOption anAAOption) override;
+
+ // If the font has a cmap table, we handle it purely with harfbuzz;
+ // but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
+ virtual bool ProvidesGetGlyph() const override {
+ return !mFontEntry->HasCmapTable();
+ }
+
+ virtual uint32_t GetGlyph(uint32_t aUnicode,
+ uint32_t aVarSelector) override;
+
+ virtual bool ProvidesGlyphWidths() const override { return true; }
+
+ // get hinted glyph width in pixels as 16.16 fixed-point value
+ virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
+ uint16_t aGID) override;
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const;
+
+ virtual FontType GetType() const override { return FONT_TYPE_GDI; }
+
+protected:
+ virtual const Metrics& GetHorizontalMetrics() override;
+
+ /* override to ensure the cairo font is set up properly */
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText) override;
+
+ void Initialize(); // creates metrics and Cairo fonts
+
+ // Fill the given LOGFONT record according to our style, but don't adjust
+ // the lfItalic field if we're going to use a cairo transform for fake
+ // italics.
+ void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
+
+ HFONT mFont;
+ cairo_font_face_t *mFontFace;
+
+ Metrics *mMetrics;
+ uint32_t mSpaceGlyph;
+
+ bool mNeedsBold;
+
+ // cache of glyph IDs (used for non-sfnt fonts only)
+ mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,uint32_t> > mGlyphIDs;
+ SCRIPT_CACHE mScriptCache;
+
+ // cache of glyph widths in 16.16 fixed-point pixels
+ mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,int32_t> > mGlyphWidths;
+};
+
+#endif /* GFX_GDIFONT_H */
diff --git a/system/graphics/thebes/gfxGDIFontList.cpp b/system/graphics/thebes/gfxGDIFontList.cpp
new file mode 100644
index 000000000..ae2a43368
--- /dev/null
+++ b/system/graphics/thebes/gfxGDIFontList.cpp
@@ -0,0 +1,1168 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+#include <algorithm>
+
+#include "mozilla/Logging.h"
+#include "mozilla/Sprintf.h"
+
+#include "gfxGDIFontList.h"
+#include "gfxWindowsPlatform.h"
+#include "gfxUserFontSet.h"
+#include "gfxFontUtils.h"
+#include "gfxGDIFont.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+#include "nsUnicharUtils.h"
+
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIWindowsRegKey.h"
+#include "gfxFontConstants.h"
+#include "GeckoProfiler.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/WindowsVersion.h"
+
+#include <usp10.h>
+
+using namespace mozilla;
+
+#define ROUND(x) floor((x) + 0.5)
+
+#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug)
+
+#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_cmapdata), \
+ LogLevel::Debug)
+
+static __inline void
+BuildKeyNameFromFontName(nsAString &aName)
+{
+ if (aName.Length() >= LF_FACESIZE)
+ aName.Truncate(LF_FACESIZE - 1);
+ ToLowerCase(aName);
+}
+
+// Implementation of gfxPlatformFontList for Win32 GDI,
+// using GDI font enumeration APIs to get the list of fonts
+
+class WinUserFontData : public gfxUserFontData {
+public:
+ WinUserFontData(HANDLE aFontRef)
+ : mFontRef(aFontRef)
+ { }
+
+ virtual ~WinUserFontData()
+ {
+ DebugOnly<BOOL> success;
+ success = RemoveFontMemResourceEx(mFontRef);
+#if DEBUG
+ if (!success) {
+ char buf[256];
+ SprintfLiteral(buf, "error deleting font handle (%p) - RemoveFontMemResourceEx failed", mFontRef);
+ NS_ASSERTION(success, buf);
+ }
+#endif
+ }
+
+ HANDLE mFontRef;
+};
+
+BYTE
+FontTypeToOutPrecision(uint8_t fontType)
+{
+ BYTE ret;
+ switch (fontType) {
+ case GFX_FONT_TYPE_TT_OPENTYPE:
+ case GFX_FONT_TYPE_TRUETYPE:
+ ret = OUT_TT_ONLY_PRECIS;
+ break;
+ case GFX_FONT_TYPE_PS_OPENTYPE:
+ ret = OUT_PS_ONLY_PRECIS;
+ break;
+ case GFX_FONT_TYPE_TYPE1:
+ ret = OUT_OUTLINE_PRECIS;
+ break;
+ case GFX_FONT_TYPE_RASTER:
+ ret = OUT_RASTER_PRECIS;
+ break;
+ case GFX_FONT_TYPE_DEVICE:
+ ret = OUT_DEVICE_PRECIS;
+ break;
+ default:
+ ret = OUT_DEFAULT_PRECIS;
+ }
+ return ret;
+}
+
+/***************************************************************
+ *
+ * GDIFontEntry
+ *
+ */
+
+GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
+ gfxWindowsFontType aFontType,
+ uint8_t aStyle, uint16_t aWeight,
+ int16_t aStretch,
+ gfxUserFontData *aUserFontData,
+ bool aFamilyHasItalicFace)
+ : gfxFontEntry(aFaceName),
+ mWindowsFamily(0), mWindowsPitch(0),
+ mFontType(aFontType),
+ mForceGDI(false),
+ mFamilyHasItalicFace(aFamilyHasItalicFace),
+ mCharset(), mUnicodeRanges()
+{
+ mUserFontData.reset(aUserFontData);
+ mStyle = aStyle;
+ mWeight = aWeight;
+ mStretch = aStretch;
+ if (IsType1())
+ mForceGDI = true;
+ mIsDataUserFont = aUserFontData != nullptr;
+
+ InitLogFont(aFaceName, aFontType);
+}
+
+nsresult
+GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ // skip non-SFNT fonts completely
+ if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE &&
+ mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
+ mFontType != GFX_FONT_TYPE_TRUETYPE)
+ {
+ mCharacterMap = new gfxCharacterMap();
+ mCharacterMap->mBuildOnTheFly = true;
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<gfxCharacterMap> charmap;
+ nsresult rv;
+ bool unicodeFont = false, symbolFont = false;
+
+ if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+ mUVSOffset,
+ symbolFont))) {
+ mSymbolFont = symbolFont;
+ rv = NS_OK;
+ } else {
+ uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+ charmap = new gfxCharacterMap();
+ AutoTArray<uint8_t, 16384> cmap;
+ rv = CopyFontTable(kCMAP, cmap);
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
+ *charmap, mUVSOffset,
+ unicodeFont, symbolFont);
+ }
+ mSymbolFont = symbolFont;
+ }
+
+ mHasCmapTable = NS_SUCCEEDED(rv);
+ if (mHasCmapTable) {
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ mCharacterMap = pfl->FindCharMap(charmap);
+ } else {
+ // if error occurred, initialize to null cmap
+ mCharacterMap = new gfxCharacterMap();
+ // For fonts where we failed to read the character map,
+ // we can take a slow path to look up glyphs character by character
+ mCharacterMap->mBuildOnTheFly = true;
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
+ NS_ConvertUTF16toUTF8(mName).get(),
+ charmap->SizeOfIncludingThis(moz_malloc_size_of),
+ charmap->mHash, mCharacterMap == charmap ? " new" : ""));
+ if (LOG_CMAPDATA_ENABLED()) {
+ char prefix[256];
+ SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+ NS_ConvertUTF16toUTF8(mName).get());
+ charmap->Dump(prefix, eGfxLog_cmapdata);
+ }
+
+ return rv;
+}
+
+bool
+GDIFontEntry::IsSymbolFont()
+{
+ // initialize cmap first
+ HasCmapTable();
+ return mSymbolFont;
+}
+
+gfxFont *
+GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold)
+{
+ return new gfxGDIFont(this, aFontStyle, aNeedsBold);
+}
+
+nsresult
+GDIFontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
+{
+ if (!IsTrueType()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoDC dc;
+ AutoSelectFont font(dc.GetDC(), &mLogFont);
+ if (font.IsValid()) {
+ uint32_t tableSize =
+ ::GetFontData(dc.GetDC(),
+ NativeEndian::swapToBigEndian(aTableTag),
+ 0, nullptr, 0);
+ if (tableSize != GDI_ERROR) {
+ if (aBuffer.SetLength(tableSize, fallible)) {
+ ::GetFontData(dc.GetDC(),
+ NativeEndian::swapToBigEndian(aTableTag), 0,
+ aBuffer.Elements(), tableSize);
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+void
+GDIFontEntry::FillLogFont(LOGFONTW *aLogFont,
+ uint16_t aWeight, gfxFloat aSize)
+{
+ memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
+
+ aLogFont->lfHeight = (LONG)-ROUND(aSize);
+
+ if (aLogFont->lfHeight == 0) {
+ aLogFont->lfHeight = -1;
+ }
+
+ // If a non-zero weight is passed in, use this to override the original
+ // weight in the entry's logfont. This is used to control synthetic bolding
+ // for installed families with no bold face, and for downloaded fonts
+ // (but NOT for local user fonts, because it could cause a different,
+ // glyph-incompatible face to be used)
+ if (aWeight) {
+ aLogFont->lfWeight = aWeight;
+ }
+
+ // for non-local() user fonts, we never want to apply italics here;
+ // if the face is described as italic, we should use it as-is,
+ // and if it's not, but then the element is styled italic, we'll use
+ // a cairo transform to create fake italic (oblique)
+ if (mIsDataUserFont) {
+ aLogFont->lfItalic = 0;
+ }
+}
+
+#define MISSING_GLYPH 0x1F // glyph index returned for missing characters
+ // on WinXP with .fon fonts, but not Type1 (.pfb)
+
+bool
+GDIFontEntry::TestCharacterMap(uint32_t aCh)
+{
+ if (!mCharacterMap) {
+ ReadCMAP();
+ NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
+ }
+
+ if (mCharacterMap->mBuildOnTheFly) {
+ if (aCh > 0xFFFF)
+ return false;
+
+ // previous code was using the group style
+ gfxFontStyle fakeStyle;
+ if (!IsUpright()) {
+ fakeStyle.style = NS_FONT_STYLE_ITALIC;
+ }
+ fakeStyle.weight = mWeight * 100;
+
+ RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false);
+ if (!tempFont || !tempFont->Valid())
+ return false;
+ gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get());
+
+ HDC dc = GetDC((HWND)nullptr);
+ SetGraphicsMode(dc, GM_ADVANCED);
+ HFONT hfont = font->GetHFONT();
+ HFONT oldFont = (HFONT)SelectObject(dc, hfont);
+
+ wchar_t str[1] = { (wchar_t)aCh };
+ WORD glyph[1];
+
+ bool hasGlyph = false;
+
+ // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a
+ // missing glyph or 0x1F in other cases to indicate the "invalid"
+ // glyph. Map both cases to "not found"
+ if (IsType1() || mForceGDI) {
+ // Type1 fonts and uniscribe APIs don't get along.
+ // ScriptGetCMap will return E_HANDLE
+ DWORD ret = GetGlyphIndicesW(dc, str, 1,
+ glyph, GGI_MARK_NONEXISTING_GLYPHS);
+ if (ret != GDI_ERROR
+ && glyph[0] != 0xFFFF
+ && (IsType1() || glyph[0] != MISSING_GLYPH))
+ {
+ hasGlyph = true;
+ }
+ } else {
+ // ScriptGetCMap works better than GetGlyphIndicesW
+ // for things like bitmap/vector fonts
+ SCRIPT_CACHE sc = nullptr;
+ HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph);
+ if (rv == S_OK)
+ hasGlyph = true;
+ }
+
+ SelectObject(dc, oldFont);
+ ReleaseDC(nullptr, dc);
+
+ if (hasGlyph) {
+ mCharacterMap->set(aCh);
+ return true;
+ }
+ } else {
+ // font had a cmap so simply check that
+ return mCharacterMap->test(aCh);
+ }
+
+ return false;
+}
+
+void
+GDIFontEntry::InitLogFont(const nsAString& aName,
+ gfxWindowsFontType aFontType)
+{
+#define CLIP_TURNOFF_FONTASSOCIATION 0x40
+
+ mLogFont.lfHeight = -1;
+
+ // Fill in logFont structure
+ mLogFont.lfWidth = 0;
+ mLogFont.lfEscapement = 0;
+ mLogFont.lfOrientation = 0;
+ mLogFont.lfUnderline = FALSE;
+ mLogFont.lfStrikeOut = FALSE;
+ mLogFont.lfCharSet = DEFAULT_CHARSET;
+ mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType);
+ mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
+ mLogFont.lfQuality = DEFAULT_QUALITY;
+ mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ // always force lfItalic if we want it. Font selection code will
+ // do its best to give us an italic font entry, but if no face exists
+ // it may give us a regular one based on weight. Windows should
+ // do fake italic for us in that case.
+ mLogFont.lfItalic = !IsUpright();
+ mLogFont.lfWeight = mWeight;
+
+ int len = std::min<int>(aName.Length(), LF_FACESIZE - 1);
+ memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t));
+ mLogFont.lfFaceName[len] = '\0';
+}
+
+GDIFontEntry*
+GDIFontEntry::CreateFontEntry(const nsAString& aName,
+ gfxWindowsFontType aFontType,
+ uint8_t aStyle,
+ uint16_t aWeight, int16_t aStretch,
+ gfxUserFontData* aUserFontData,
+ bool aFamilyHasItalicFace)
+{
+ // jtdfix - need to set charset, unicode ranges, pitch/family
+
+ GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aStyle,
+ aWeight, aStretch, aUserFontData,
+ aFamilyHasItalicFace);
+
+ return fe;
+}
+
+void
+GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+/***************************************************************
+ *
+ * GDIFontFamily
+ *
+ */
+
+int CALLBACK
+GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
+ const NEWTEXTMETRICEXW *nmetrics,
+ DWORD fontType, LPARAM data)
+{
+ const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
+ LOGFONTW logFont = lpelfe->elfLogFont;
+ GDIFontFamily *ff = reinterpret_cast<GDIFontFamily*>(data);
+
+ // Some fonts claim to support things > 900, but we don't so clamp the sizes
+ logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900));
+
+ gfxWindowsFontType feType = GDIFontEntry::DetermineFontType(metrics, fontType);
+
+ GDIFontEntry *fe = nullptr;
+ for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
+ fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
+ if (feType > fe->mFontType) {
+ // if the new type is better than the old one, remove the old entries
+ ff->mAvailableFonts.RemoveElementAt(i);
+ --i;
+ } else if (feType < fe->mFontType) {
+ // otherwise if the new type is worse, skip it
+ return 1;
+ }
+ }
+
+ for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
+ fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
+ // check if we already know about this face
+ if (fe->mWeight == logFont.lfWeight &&
+ fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
+ // update the charset bit here since this could be different
+ fe->mCharset.set(metrics.tmCharSet);
+ return 1;
+ }
+ }
+
+ // We can't set the hasItalicFace flag correctly here,
+ // because we might not have seen the family's italic face(s) yet.
+ // So we'll set that flag for all members after loading all the faces.
+ uint8_t italicStyle = (logFont.lfItalic == 0xFF ?
+ NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+ fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName),
+ feType, italicStyle,
+ (uint16_t) (logFont.lfWeight), 0,
+ nullptr, false);
+ if (!fe)
+ return 1;
+
+ ff->AddFontEntry(fe);
+
+ // mark the charset bit
+ fe->mCharset.set(metrics.tmCharSet);
+
+ fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0;
+ fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F;
+
+ if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
+ nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
+ nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 &&
+ nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) {
+
+ // set the unicode ranges
+ uint32_t x = 0;
+ for (uint32_t i = 0; i < 4; ++i) {
+ DWORD range = nmetrics->ntmFontSig.fsUsb[i];
+ for (uint32_t k = 0; k < 32; ++k) {
+ fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0);
+ }
+ }
+ }
+
+ if (LOG_FONTLIST_ENABLED()) {
+ LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d",
+ NS_ConvertUTF16toUTF8(fe->Name()).get(),
+ NS_ConvertUTF16toUTF8(ff->Name()).get(),
+ (logFont.lfItalic == 0xff) ? "italic" : "normal",
+ logFont.lfWeight, fe->Stretch()));
+ }
+ return 1;
+}
+
+void
+GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
+{
+ if (mHasStyles)
+ return;
+ mHasStyles = true;
+
+ HDC hdc = GetDC(nullptr);
+ SetGraphicsMode(hdc, GM_ADVANCED);
+
+ LOGFONTW logFont;
+ memset(&logFont, 0, sizeof(LOGFONTW));
+ logFont.lfCharSet = DEFAULT_CHARSET;
+ logFont.lfPitchAndFamily = 0;
+ uint32_t l = std::min<uint32_t>(mName.Length(), LF_FACESIZE - 1);
+ memcpy(logFont.lfFaceName, mName.get(), l * sizeof(char16_t));
+
+ EnumFontFamiliesExW(hdc, &logFont,
+ (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc,
+ (LPARAM)this, 0);
+ if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) {
+ LOG_FONTLIST(("(fontlist) no styles available in family \"%s\"",
+ NS_ConvertUTF16toUTF8(mName).get()));
+ }
+
+ ReleaseDC(nullptr, hdc);
+
+ if (mIsBadUnderlineFamily) {
+ SetBadUnderlineFonts();
+ }
+
+ // check for existence of italic face(s); if present, set the
+ // FamilyHasItalic flag on all faces so that we'll know *not*
+ // to use GDI's fake-italic effect with them
+ size_t count = mAvailableFonts.Length();
+ for (size_t i = 0; i < count; ++i) {
+ if (mAvailableFonts[i]->IsItalic()) {
+ for (uint32_t j = 0; j < count; ++j) {
+ static_cast<GDIFontEntry*>(mAvailableFonts[j].get())->
+ mFamilyHasItalicFace = true;
+ }
+ break;
+ }
+ }
+}
+
+/***************************************************************
+ *
+ * gfxGDIFontList
+ *
+ */
+
+gfxGDIFontList::gfxGDIFontList()
+ : mFontSubstitutes(32)
+{
+#ifdef MOZ_BUNDLED_FONTS
+ ActivateBundledFonts();
+#endif
+}
+
+static void
+RemoveCharsetFromFontSubstitute(nsAString &aName)
+{
+ int32_t comma = aName.FindChar(char16_t(','));
+ if (comma >= 0)
+ aName.Truncate(comma);
+}
+
+#define MAX_VALUE_NAME 512
+#define MAX_VALUE_DATA 512
+
+nsresult
+gfxGDIFontList::GetFontSubstitutes()
+{
+ HKEY hKey;
+ DWORD i, rv, lenAlias, lenActual, valueType;
+ WCHAR aliasName[MAX_VALUE_NAME];
+ WCHAR actualName[MAX_VALUE_DATA];
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
+ 0, KEY_READ, &hKey) != ERROR_SUCCESS)
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
+ aliasName[0] = 0;
+ lenAlias = ArrayLength(aliasName);
+ actualName[0] = 0;
+ lenActual = sizeof(actualName);
+ rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
+ (LPBYTE)actualName, &lenActual);
+
+ if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
+ continue;
+ }
+
+ if (aliasName[0] == WCHAR('@')) {
+ continue;
+ }
+
+ nsAutoString substituteName((char16_t*) aliasName);
+ nsAutoString actualFontName((char16_t*) actualName);
+ RemoveCharsetFromFontSubstitute(substituteName);
+ BuildKeyNameFromFontName(substituteName);
+ RemoveCharsetFromFontSubstitute(actualFontName);
+ BuildKeyNameFromFontName(actualFontName);
+ gfxFontFamily *ff;
+ if (!actualFontName.IsEmpty() &&
+ (ff = mFontFamilies.GetWeak(actualFontName))) {
+ mFontSubstitutes.Put(substituteName, ff);
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ }
+
+ // "Courier" on a default Windows install is an ugly bitmap font.
+ // If there is no substitution for Courier in the registry
+ // substitute "Courier" with "Courier New".
+ nsAutoString substituteName;
+ substituteName.AssignLiteral("Courier");
+ BuildKeyNameFromFontName(substituteName);
+ if (!mFontSubstitutes.GetWeak(substituteName)) {
+ gfxFontFamily *ff;
+ nsAutoString actualFontName;
+ actualFontName.AssignLiteral("Courier New");
+ BuildKeyNameFromFontName(actualFontName);
+ ff = mFontFamilies.GetWeak(actualFontName);
+ if (ff) {
+ mFontSubstitutes.Put(substituteName, ff);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+gfxGDIFontList::InitFontListForPlatform()
+{
+ mFontSubstitutes.Clear();
+ mNonExistingFonts.Clear();
+
+ // iterate over available families
+ LOGFONTW logfont;
+ memset(&logfont, 0, sizeof(logfont));
+ logfont.lfCharSet = DEFAULT_CHARSET;
+
+ AutoDC hdc;
+ int result = EnumFontFamiliesExW(hdc.GetDC(), &logfont,
+ (FONTENUMPROCW)&EnumFontFamExProc,
+ 0, 0);
+
+ GetFontSubstitutes();
+
+ GetPrefsAndStartLoader();
+
+ return NS_OK;
+}
+
+int CALLBACK
+gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
+ NEWTEXTMETRICEXW *lpntme,
+ DWORD fontType,
+ LPARAM lParam)
+{
+ const LOGFONTW& lf = lpelfe->elfLogFont;
+
+ if (lf.lfFaceName[0] == '@') {
+ return 1;
+ }
+
+ nsAutoString name(lf.lfFaceName);
+ BuildKeyNameFromFontName(name);
+
+ gfxGDIFontList *fontList = PlatformFontList();
+
+ if (!fontList->mFontFamilies.GetWeak(name)) {
+ nsDependentString faceName(lf.lfFaceName);
+ RefPtr<gfxFontFamily> family = new GDIFontFamily(faceName);
+ fontList->mFontFamilies.Put(name, family);
+
+ // if locale is such that CJK font names are the default coming from
+ // GDI, then if a family name is non-ASCII immediately read in other
+ // family names. This assures that MS Gothic, MS Mincho are all found
+ // before lookups begin.
+ if (!IsASCII(faceName)) {
+ family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());
+ }
+
+ if (fontList->mBadUnderlineFamilyNames.Contains(name))
+ family->SetBadUnderlineFamily();
+ }
+
+ return 1;
+}
+
+gfxFontEntry*
+gfxGDIFontList::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ gfxFontEntry *lookup;
+
+ lookup = LookupInFaceNameLists(aFontName);
+ if (!lookup) {
+ return nullptr;
+ }
+
+ bool isCFF = false; // jtdfix -- need to determine this
+
+ // use the face name from the lookup font entry, which will be the localized
+ // face name which GDI mapping tables use (e.g. with the system locale set to
+ // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
+ // 'Arial Vet' which can be used as a key in GDI font lookups).
+ GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(),
+ gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
+ lookup->mStyle, lookup->mWeight, aStretch, nullptr,
+ static_cast<GDIFontEntry*>(lookup)->mFamilyHasItalicFace);
+
+ if (!fe)
+ return nullptr;
+
+ fe->mIsLocalUserFont = true;
+
+ // make the new font entry match the userfont entry style characteristics
+ fe->mWeight = (aWeight == 0 ? 400 : aWeight);
+ fe->mStyle = aStyle;
+
+ return fe;
+}
+
+// If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
+// we modify the subtable header to mark it as Unicode instead, because
+// otherwise GDI will refuse to load the font.
+// NOTE that this function does not bounds-check every access to the font data.
+// This is OK because we only use it on data that has already been validated
+// by OTS, and therefore we will not hit out-of-bounds accesses here.
+static bool
+FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength)
+{
+ struct CmapHeader {
+ AutoSwap_PRUint16 version;
+ AutoSwap_PRUint16 numTables;
+ };
+ struct CmapEncodingRecord {
+ AutoSwap_PRUint16 platformID;
+ AutoSwap_PRUint16 encodingID;
+ AutoSwap_PRUint32 offset;
+ };
+ const uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+ const TableDirEntry* dir =
+ gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
+ if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
+ CmapHeader *cmap =
+ reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
+ CmapEncodingRecord *encRec =
+ reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
+ int32_t symbolSubtable = -1;
+ for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
+ if (uint16_t(encRec[i].platformID) !=
+ gfxFontUtils::PLATFORM_ID_MICROSOFT) {
+ continue; // only interested in MS platform
+ }
+ if (uint16_t(encRec[i].encodingID) ==
+ gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
+ // We've got a Microsoft/Unicode table, so don't interfere.
+ symbolSubtable = -1;
+ break;
+ }
+ if (uint16_t(encRec[i].encodingID) ==
+ gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
+ // Found a symbol subtable; remember it for possible fixup,
+ // but if we subsequently find a Microsoft/Unicode subtable,
+ // we'll cancel this.
+ symbolSubtable = i;
+ }
+ }
+ if (symbolSubtable != -1) {
+ // We found a windows/symbol cmap table, and no windows/unicode one;
+ // change the encoding ID so that AddFontMemResourceEx will accept it
+ encRec[symbolSubtable].encodingID =
+ gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
+ return true;
+ }
+ }
+ return false;
+}
+
+gfxFontEntry*
+gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ // MakePlatformFont is responsible for deleting the font data with free
+ // so we set up a stack object to ensure it is freed even if we take an
+ // early exit
+ struct FontDataDeleter {
+ FontDataDeleter(const uint8_t* aFontData)
+ : mFontData(aFontData) { }
+ ~FontDataDeleter() { free((void*)mFontData); }
+ const uint8_t *mFontData;
+ };
+ FontDataDeleter autoDelete(aFontData);
+
+ bool isCFF = gfxFontUtils::IsCffFont(aFontData);
+
+ nsresult rv;
+ HANDLE fontRef = nullptr;
+
+ nsAutoString uniqueName;
+ rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ FallibleTArray<uint8_t> newFontData;
+
+ rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
+
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ DWORD numFonts = 0;
+
+ uint8_t *fontData = reinterpret_cast<uint8_t*> (newFontData.Elements());
+ uint32_t fontLength = newFontData.Length();
+ NS_ASSERTION(fontData, "null font data after renaming");
+
+ // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
+ // "A font that is added by AddFontMemResourceEx is always private
+ // to the process that made the call and is not enumerable."
+ fontRef = AddFontMemResourceEx(fontData, fontLength,
+ 0 /* reserved */, &numFonts);
+ if (!fontRef) {
+ if (FixupSymbolEncodedFont(fontData, fontLength)) {
+ fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
+ }
+ }
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ // only load fonts with a single face contained in the data
+ // AddFontMemResourceEx generates an additional face name for
+ // vertical text if the font supports vertical writing but since
+ // the font is referenced via the name this can be ignored
+ if (fontRef && numFonts > 2) {
+ RemoveFontMemResourceEx(fontRef);
+ return nullptr;
+ }
+
+ // make a new font entry using the unique name
+ WinUserFontData *winUserFontData = new WinUserFontData(fontRef);
+ uint16_t w = (aWeight == 0 ? 400 : aWeight);
+
+ GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName,
+ gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
+ aStyle, w, aStretch, winUserFontData, false);
+
+ if (fe) {
+ fe->mIsDataUserFont = true;
+ }
+
+ return fe;
+}
+
+bool
+gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ nsAutoString keyName(aFamily);
+ BuildKeyNameFromFontName(keyName);
+
+ gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
+ if (ff) {
+ aOutput->AppendElement(ff);
+ return true;
+ }
+
+ if (mNonExistingFonts.Contains(keyName)) {
+ return false;
+ }
+
+ return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+ aDevToCssSize);
+}
+
+gfxFontFamily*
+gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
+{
+ gfxFontFamily *ff = nullptr;
+
+ // this really shouldn't fail to find a font....
+ NONCLIENTMETRICSW ncm;
+ ncm.cbSize = sizeof(ncm);
+ BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
+ sizeof(ncm), &ncm, 0);
+ if (status) {
+ ff = FindFamily(nsDependentString(ncm.lfMessageFont.lfFaceName));
+ if (ff) {
+ return ff;
+ }
+ }
+
+ // ...but just in case, try another (long-deprecated) approach as well
+ HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+ LOGFONTW logFont;
+ if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
+ ff = FindFamily(nsDependentString(logFont.lfFaceName));
+ }
+
+ return ff;
+}
+
+void
+gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ aSizes->mFontListSize +=
+ SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
+ aSizes->mFontListSize +=
+ mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
+ aSizes->mFontListSize +=
+ mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+}
+
+void
+gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+// used to load system-wide font info on off-main thread
+class GDIFontInfo : public FontInfoData {
+public:
+ GDIFontInfo(bool aLoadOtherNames,
+ bool aLoadFaceNames,
+ bool aLoadCmaps) :
+ FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
+ {}
+
+ virtual ~GDIFontInfo() {}
+
+ virtual void Load() {
+ mHdc = GetDC(nullptr);
+ SetGraphicsMode(mHdc, GM_ADVANCED);
+ FontInfoData::Load();
+ ReleaseDC(nullptr, mHdc);
+ }
+
+ // loads font data for all members of a given family
+ virtual void LoadFontFamilyData(const nsAString& aFamilyName);
+
+ // callback for GDI EnumFontFamiliesExW call
+ static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW *lpelfe,
+ const NEWTEXTMETRICEXW *nmetrics,
+ DWORD fontType, LPARAM data);
+
+ HDC mHdc;
+};
+
+struct EnumerateFontsForFamilyData {
+ EnumerateFontsForFamilyData(const nsAString& aFamilyName,
+ GDIFontInfo& aFontInfo)
+ : mFamilyName(aFamilyName), mFontInfo(aFontInfo)
+ {}
+
+ nsString mFamilyName;
+ nsTArray<nsString> mOtherFamilyNames;
+ GDIFontInfo& mFontInfo;
+ nsString mPreviousFontName;
+};
+
+int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
+ const ENUMLOGFONTEXW *lpelfe,
+ const NEWTEXTMETRICEXW *nmetrics,
+ DWORD fontType, LPARAM data)
+{
+ EnumerateFontsForFamilyData *famData =
+ reinterpret_cast<EnumerateFontsForFamilyData*>(data);
+ HDC hdc = famData->mFontInfo.mHdc;
+ LOGFONTW logFont = lpelfe->elfLogFont;
+ const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
+
+ AutoSelectFont font(hdc, &logFont);
+ if (!font.IsValid()) {
+ return 1;
+ }
+
+ FontFaceData fontData;
+ nsDependentString fontName(lpelfe->elfFullName);
+
+ // callback called for each style-charset so return if style already seen
+ if (fontName.Equals(famData->mPreviousFontName)) {
+ return 1;
+ }
+ famData->mPreviousFontName = fontName;
+ famData->mFontInfo.mLoadStats.fonts++;
+
+ // read name table info
+ bool nameDataLoaded = false;
+ if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
+ uint32_t kNAME =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
+ uint32_t nameSize;
+ AutoTArray<uint8_t, 1024> nameData;
+
+ nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
+ if (nameSize != GDI_ERROR &&
+ nameSize > 0 &&
+ nameData.SetLength(nameSize, fallible)) {
+ ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);
+
+ // face names
+ if (famData->mFontInfo.mLoadFaceNames) {
+ gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
+ gfxFontUtils::NAME_ID_FULL,
+ fontData.mFullName);
+ gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
+ gfxFontUtils::NAME_ID_POSTSCRIPT,
+ fontData.mPostscriptName);
+ nameDataLoaded = true;
+ famData->mFontInfo.mLoadStats.facenames++;
+ }
+
+ // other family names
+ if (famData->mFontInfo.mLoadOtherNames) {
+ gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName,
+ (const char*)(nameData.Elements()),
+ nameSize,
+ famData->mOtherFamilyNames,
+ false);
+ }
+ }
+ }
+
+ // read cmap
+ bool cmapLoaded = false;
+ gfxWindowsFontType feType =
+ GDIFontEntry::DetermineFontType(metrics, fontType);
+ if (famData->mFontInfo.mLoadCmaps &&
+ (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
+ feType == GFX_FONT_TYPE_TT_OPENTYPE ||
+ feType == GFX_FONT_TYPE_TRUETYPE))
+ {
+ uint32_t kCMAP =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
+ uint32_t cmapSize;
+ AutoTArray<uint8_t, 1024> cmapData;
+
+ cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
+ if (cmapSize != GDI_ERROR &&
+ cmapSize > 0 &&
+ cmapData.SetLength(cmapSize, fallible)) {
+ ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
+ bool cmapLoaded = false;
+ bool unicodeFont = false, symbolFont = false;
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ uint32_t offset;
+
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(),
+ cmapSize, *charmap,
+ offset, unicodeFont,
+ symbolFont))) {
+ fontData.mCharacterMap = charmap;
+ fontData.mUVSOffset = offset;
+ fontData.mSymbolFont = symbolFont;
+ cmapLoaded = true;
+ famData->mFontInfo.mLoadStats.cmaps++;
+ }
+ }
+ }
+
+ if (cmapLoaded || nameDataLoaded) {
+ famData->mFontInfo.mFontFaceData.Put(fontName, fontData);
+ }
+
+ return famData->mFontInfo.mCanceled ? 0 : 1;
+}
+
+void
+GDIFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
+{
+ // iterate over the family
+ LOGFONTW logFont;
+ memset(&logFont, 0, sizeof(LOGFONTW));
+ logFont.lfCharSet = DEFAULT_CHARSET;
+ logFont.lfPitchAndFamily = 0;
+ uint32_t l = std::min<uint32_t>(aFamilyName.Length(), LF_FACESIZE - 1);
+ memcpy(logFont.lfFaceName, aFamilyName.BeginReading(), l * sizeof(char16_t));
+
+ EnumerateFontsForFamilyData data(aFamilyName, *this);
+
+ EnumFontFamiliesExW(mHdc, &logFont,
+ (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
+ (LPARAM)(&data), 0);
+
+ // if found other names, insert them
+ if (data.mOtherFamilyNames.Length() != 0) {
+ mOtherFamilyNames.Put(aFamilyName, data.mOtherFamilyNames);
+ mLoadStats.othernames += data.mOtherFamilyNames.Length();
+ }
+}
+
+already_AddRefed<FontInfoData>
+gfxGDIFontList::CreateFontInfoData()
+{
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ RefPtr<GDIFontInfo> fi =
+ new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
+
+ return fi.forget();
+}
+
+#ifdef MOZ_BUNDLED_FONTS
+
+void
+gfxGDIFontList::ActivateBundledFonts()
+{
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ return;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ bool hasMore;
+ while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> entry;
+ if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) {
+ break;
+ }
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+ if (!file) {
+ continue;
+ }
+ nsAutoString path;
+ if (NS_FAILED(file->GetPath(path))) {
+ continue;
+ }
+ AddFontResourceExW(path.get(), FR_PRIVATE, nullptr);
+ }
+}
+
+#endif
diff --git a/system/graphics/thebes/gfxGDIFontList.h b/system/graphics/thebes/gfxGDIFontList.h
new file mode 100644
index 000000000..90ae413ed
--- /dev/null
+++ b/system/graphics/thebes/gfxGDIFontList.h
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GDIFONTLIST_H
+#define GFX_GDIFONTLIST_H
+
+#include "mozilla/MemoryReporting.h"
+#include "gfxWindowsPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "nsGkAtoms.h"
+
+#include <windows.h>
+
+class AutoDC // get the global device context, and auto-release it on destruction
+{
+public:
+ AutoDC() {
+ mDC = ::GetDC(nullptr);
+ }
+
+ ~AutoDC() {
+ ::ReleaseDC(nullptr, mDC);
+ }
+
+ HDC GetDC() {
+ return mDC;
+ }
+
+private:
+ HDC mDC;
+};
+
+class AutoSelectFont // select a font into the given DC, and auto-restore
+{
+public:
+ AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
+ : mOwnsFont(false)
+ {
+ mFont = ::CreateFontIndirectW(aLogFont);
+ if (mFont) {
+ mOwnsFont = true;
+ mDC = aDC;
+ mOldFont = (HFONT)::SelectObject(aDC, mFont);
+ } else {
+ mOldFont = nullptr;
+ }
+ }
+
+ AutoSelectFont(HDC aDC, HFONT aFont)
+ : mOwnsFont(false)
+ {
+ mDC = aDC;
+ mFont = aFont;
+ mOldFont = (HFONT)::SelectObject(aDC, aFont);
+ }
+
+ ~AutoSelectFont() {
+ if (mOldFont) {
+ ::SelectObject(mDC, mOldFont);
+ if (mOwnsFont) {
+ ::DeleteObject(mFont);
+ }
+ }
+ }
+
+ bool IsValid() const {
+ return mFont != nullptr;
+ }
+
+ HFONT GetFont() const {
+ return mFont;
+ }
+
+private:
+ HDC mDC;
+ HFONT mFont;
+ HFONT mOldFont;
+ bool mOwnsFont;
+};
+
+/**
+ * List of different types of fonts we support on Windows.
+ * These can generally be lumped in to 3 categories where we have to
+ * do special things: Really old fonts bitmap and vector fonts (device
+ * and raster), Type 1 fonts, and TrueType/OpenType fonts.
+ *
+ * This list is sorted in order from least prefered to most prefered.
+ * We prefer Type1 fonts over OpenType fonts to avoid falling back to
+ * things like Arial (opentype) when you ask for Helvetica (type1)
+ **/
+enum gfxWindowsFontType {
+ GFX_FONT_TYPE_UNKNOWN = 0,
+ GFX_FONT_TYPE_DEVICE,
+ GFX_FONT_TYPE_RASTER,
+ GFX_FONT_TYPE_TRUETYPE,
+ GFX_FONT_TYPE_PS_OPENTYPE,
+ GFX_FONT_TYPE_TT_OPENTYPE,
+ GFX_FONT_TYPE_TYPE1
+};
+
+// A single member of a font family (i.e. a single face, such as Times Italic)
+// represented as a LOGFONT that will resolve to the correct face.
+// This replaces FontEntry from gfxWindowsFonts.h/cpp.
+class GDIFontEntry : public gfxFontEntry
+{
+public:
+ LPLOGFONTW GetLogFont() { return &mLogFont; }
+
+ nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
+
+ virtual bool IsSymbolFont();
+
+ void FillLogFont(LOGFONTW *aLogFont, uint16_t aWeight, gfxFloat aSize);
+
+ static gfxWindowsFontType DetermineFontType(const NEWTEXTMETRICW& metrics,
+ DWORD fontType)
+ {
+ gfxWindowsFontType feType;
+ if (metrics.ntmFlags & NTM_TYPE1)
+ feType = GFX_FONT_TYPE_TYPE1;
+ else if (metrics.ntmFlags & NTM_PS_OPENTYPE)
+ feType = GFX_FONT_TYPE_PS_OPENTYPE;
+ else if (metrics.ntmFlags & NTM_TT_OPENTYPE)
+ feType = GFX_FONT_TYPE_TT_OPENTYPE;
+ else if (fontType == TRUETYPE_FONTTYPE)
+ feType = GFX_FONT_TYPE_TRUETYPE;
+ else if (fontType == RASTER_FONTTYPE)
+ feType = GFX_FONT_TYPE_RASTER;
+ else if (fontType == DEVICE_FONTTYPE)
+ feType = GFX_FONT_TYPE_DEVICE;
+ else
+ feType = GFX_FONT_TYPE_UNKNOWN;
+
+ return feType;
+ }
+
+ bool IsType1() const {
+ return (mFontType == GFX_FONT_TYPE_TYPE1);
+ }
+
+ bool IsTrueType() const {
+ return (mFontType == GFX_FONT_TYPE_TRUETYPE ||
+ mFontType == GFX_FONT_TYPE_PS_OPENTYPE ||
+ mFontType == GFX_FONT_TYPE_TT_OPENTYPE);
+ }
+
+ virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
+ if (aGeneric.IsEmpty()) {
+ return true;
+ }
+
+ // Japanese 'Mincho' fonts do not belong to FF_MODERN even if
+ // they are fixed pitch because they have variable stroke width.
+ if (mWindowsFamily == FF_ROMAN && mWindowsPitch & FIXED_PITCH) {
+ return aGeneric.EqualsLiteral("monospace");
+ }
+
+ // Japanese 'Gothic' fonts do not belong to FF_SWISS even if
+ // they are variable pitch because they have constant stroke width.
+ if (mWindowsFamily == FF_MODERN && mWindowsPitch & VARIABLE_PITCH) {
+ return aGeneric.EqualsLiteral("sans-serif");
+ }
+
+ // All other fonts will be grouped correctly using family...
+ switch (mWindowsFamily) {
+ case FF_DONTCARE:
+ return false;
+ case FF_ROMAN:
+ return aGeneric.EqualsLiteral("serif");
+ case FF_SWISS:
+ return aGeneric.EqualsLiteral("sans-serif");
+ case FF_MODERN:
+ return aGeneric.EqualsLiteral("monospace");
+ case FF_SCRIPT:
+ return aGeneric.EqualsLiteral("cursive");
+ case FF_DECORATIVE:
+ return aGeneric.EqualsLiteral("fantasy");
+ }
+
+ return false;
+ }
+
+ virtual bool SupportsLangGroup(nsIAtom* aLangGroup) const override {
+ if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
+ return true;
+ }
+
+ int16_t bit = -1;
+
+ /* map our langgroup names in to Windows charset bits */
+ if (aLangGroup == nsGkAtoms::x_western) {
+ bit = ANSI_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::Japanese) {
+ bit = SHIFTJIS_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::ko) {
+ bit = HANGEUL_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::zh_cn) {
+ bit = GB2312_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::zh_tw) {
+ bit = CHINESEBIG5_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::el_) {
+ bit = GREEK_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::he) {
+ bit = HEBREW_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::ar) {
+ bit = ARABIC_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::x_cyrillic) {
+ bit = RUSSIAN_CHARSET;
+ } else if (aLangGroup == nsGkAtoms::th) {
+ bit = THAI_CHARSET;
+ }
+
+ if (bit != -1) {
+ return mCharset.test(bit);
+ }
+
+ return false;
+ }
+
+ virtual bool SupportsRange(uint8_t range) {
+ return mUnicodeRanges.test(range);
+ }
+
+ virtual bool SkipDuringSystemFallback() {
+ return !HasCmapTable(); // explicitly skip non-SFNT fonts
+ }
+
+ virtual bool TestCharacterMap(uint32_t aCh);
+
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+ // create a font entry for a font with a given name
+ static GDIFontEntry* CreateFontEntry(const nsAString& aName,
+ gfxWindowsFontType aFontType,
+ uint8_t aStyle,
+ uint16_t aWeight, int16_t aStretch,
+ gfxUserFontData* aUserFontData,
+ bool aFamilyHasItalicFace);
+
+ // create a font entry for a font referenced by its fullname
+ static GDIFontEntry* LoadLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ uint8_t mWindowsFamily;
+ uint8_t mWindowsPitch;
+
+ gfxWindowsFontType mFontType;
+ bool mForceGDI : 1;
+
+ // For src:local user-fonts, we keep track of whether the platform family
+ // contains an italic face, because in this case we can't safely ask GDI
+ // to create synthetic italics (oblique) via the LOGFONT.
+ // (For other types of font, this is just set to false.)
+ bool mFamilyHasItalicFace : 1;
+
+ gfxSparseBitSet mCharset;
+ gfxSparseBitSet mUnicodeRanges;
+
+protected:
+ friend class gfxWindowsFont;
+
+ GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
+ uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
+ gfxUserFontData *aUserFontData, bool aFamilyHasItalicFace);
+
+ void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
+
+ virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
+
+ virtual nsresult CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) override;
+
+ LOGFONTW mLogFont;
+};
+
+// a single font family, referencing one or more faces
+class GDIFontFamily : public gfxFontFamily
+{
+public:
+ GDIFontFamily(nsAString &aName) :
+ gfxFontFamily(aName) {}
+
+ virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
+
+private:
+ static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
+ const NEWTEXTMETRICEXW *nmetrics,
+ DWORD fontType, LPARAM data);
+};
+
+class gfxGDIFontList : public gfxPlatformFontList {
+public:
+ static gfxGDIFontList* PlatformFontList() {
+ return static_cast<gfxGDIFontList*>(sPlatformFontList);
+ }
+
+ // initialize font lists
+ virtual nsresult InitFontListForPlatform() override;
+
+ bool FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0) override;
+
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+protected:
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
+private:
+ friend class gfxWindowsPlatform;
+
+ gfxGDIFontList();
+
+ nsresult GetFontSubstitutes();
+
+ static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
+ NEWTEXTMETRICEXW *lpntme,
+ DWORD fontType,
+ LPARAM lParam);
+
+ virtual already_AddRefed<FontInfoData> CreateFontInfoData();
+
+ void ActivateBundledFonts();
+
+ FontFamilyTable mFontSubstitutes;
+ nsTArray<nsString> mNonExistingFonts;
+};
+
+#endif /* GFX_GDIFONTLIST_H */
diff --git a/system/graphics/thebes/gfxGdkNativeRenderer.cpp b/system/graphics/thebes/gfxGdkNativeRenderer.cpp
new file mode 100644
index 000000000..f8964ad8f
--- /dev/null
+++ b/system/graphics/thebes/gfxGdkNativeRenderer.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxGdkNativeRenderer.h"
+#include "gfxContext.h"
+#include "gfxPlatformGtk.h"
+
+#ifdef MOZ_X11
+#include <gdk/gdkx.h>
+#include "cairo-xlib.h"
+#include "gfxXlibSurface.h"
+
+#if (MOZ_WIDGET_GTK == 2)
+nsresult
+gfxGdkNativeRenderer::DrawWithXlib(cairo_surface_t* surface,
+ nsIntPoint offset,
+ mozilla::gfx::IntRect* clipRects, uint32_t numClipRects)
+{
+ GdkDrawable *drawable = gfxPlatformGtk::GetGdkDrawable(surface);
+ if (!drawable) {
+ int depth = cairo_xlib_surface_get_depth(surface);
+ GdkScreen* screen = gdk_colormap_get_screen(mColormap);
+ drawable =
+ gdk_pixmap_foreign_new_for_screen(screen, cairo_xlib_surface_get_drawable(surface),
+ cairo_xlib_surface_get_width(surface),
+ cairo_xlib_surface_get_height(surface),
+ depth);
+ if (!drawable)
+ return NS_ERROR_FAILURE;
+
+ gdk_drawable_set_colormap(drawable, mColormap);
+ gfxPlatformGtk::SetGdkDrawable(surface, drawable);
+ g_object_unref(drawable); // The drawable now belongs to |surface|.
+ }
+
+ GdkRectangle clipRect;
+ if (numClipRects) {
+ NS_ASSERTION(numClipRects == 1, "Too many clip rects");
+ clipRect.x = clipRects[0].x;
+ clipRect.y = clipRects[0].y;
+ clipRect.width = clipRects[0].width;
+ clipRect.height = clipRects[0].height;
+ }
+
+ return DrawWithGDK(drawable, offset.x, offset.y,
+ numClipRects ? &clipRect : nullptr, numClipRects);
+}
+
+void
+gfxGdkNativeRenderer::Draw(gfxContext* ctx, mozilla::gfx::IntSize size,
+ uint32_t flags, GdkColormap* colormap)
+{
+ mColormap = colormap;
+
+ Visual* visual =
+ gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(colormap));
+ Screen* screen =
+ gdk_x11_screen_get_xscreen(gdk_colormap_get_screen(colormap));
+
+ gfxXlibNativeRenderer::Draw(ctx, size, flags, screen, visual);
+}
+
+#else
+// TODO GTK3
+#endif
+
+#endif
diff --git a/system/graphics/thebes/gfxGdkNativeRenderer.h b/system/graphics/thebes/gfxGdkNativeRenderer.h
new file mode 100644
index 000000000..d95b1fef0
--- /dev/null
+++ b/system/graphics/thebes/gfxGdkNativeRenderer.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFXGDKNATIVERENDER_H_
+#define GFXGDKNATIVERENDER_H_
+
+#include <gdk/gdk.h>
+#include "nsSize.h"
+#ifdef MOZ_X11
+#include "gfxXlibNativeRenderer.h"
+#endif
+
+class gfxContext;
+
+/**
+ * This class lets us take code that draws into an GDK drawable and lets us
+ * use it to draw into any Thebes context. The user should subclass this class,
+ * override DrawWithGDK, and then call Draw(). The drawing will be subjected
+ * to all Thebes transformations, clipping etc.
+ */
+class gfxGdkNativeRenderer
+#ifdef MOZ_X11
+ : private gfxXlibNativeRenderer
+#endif
+{
+public:
+ /**
+ * Perform the native drawing.
+ * @param offsetX draw at this offset into the given drawable
+ * @param offsetY draw at this offset into the given drawable
+ * @param clipRects an array of rects; clip to the union
+ * @param numClipRects the number of rects in the array, or zero if
+ * no clipping is required
+ */
+
+#if (MOZ_WIDGET_GTK == 2)
+ virtual nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX,
+ gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects) = 0;
+#endif
+
+ enum {
+ // If set, then Draw() is opaque, i.e., every pixel in the intersection
+ // of the clipRect and (offset.x,offset.y,bounds.width,bounds.height)
+ // will be set and there is no dependence on what the existing pixels
+ // in the drawable are set to.
+ DRAW_IS_OPAQUE =
+#ifdef MOZ_X11
+ gfxXlibNativeRenderer::DRAW_IS_OPAQUE
+#else
+ 0x1
+#endif
+ // If set, then numClipRects can be zero or one.
+ // If not set, then numClipRects will be zero.
+ , DRAW_SUPPORTS_CLIP_RECT =
+#ifdef MOZ_X11
+ gfxXlibNativeRenderer::DRAW_SUPPORTS_CLIP_RECT
+#else
+ 0x2
+#endif
+ };
+
+ /**
+ * @param flags see above
+ * @param bounds Draw()'s drawing is guaranteed to be restricted to
+ * the rectangle (offset.x,offset.y,bounds.width,bounds.height)
+ * @param dpy a display to use for the drawing if ctx doesn't have one
+ */
+#if (MOZ_WIDGET_GTK == 2)
+ void Draw(gfxContext* ctx, mozilla::gfx::IntSize size,
+ uint32_t flags, GdkColormap* colormap);
+#endif
+
+private:
+#ifdef MOZ_X11
+ // for gfxXlibNativeRenderer:
+ virtual nsresult DrawWithXlib(cairo_surface_t* surface,
+ nsIntPoint offset,
+ mozilla::gfx::IntRect* clipRects, uint32_t numClipRects) override;
+
+#if (MOZ_WIDGET_GTK == 2)
+ GdkColormap *mColormap;
+#endif
+#endif
+};
+
+#endif /*GFXGDKNATIVERENDER_H_*/
diff --git a/system/graphics/thebes/gfxGlyphExtents.cpp b/system/graphics/thebes/gfxGlyphExtents.cpp
new file mode 100644
index 000000000..cb8f5838b
--- /dev/null
+++ b/system/graphics/thebes/gfxGlyphExtents.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxGlyphExtents.h"
+#include "gfxTextRun.h"
+
+using namespace mozilla;
+
+#ifdef DEBUG_roc
+#define DEBUG_TEXT_RUN_STORAGE_METRICS
+#endif
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+extern uint32_t gTextRunStorageHighWaterMark;
+extern uint32_t gTextRunStorage;
+extern uint32_t gFontCount;
+extern uint32_t gGlyphExtentsCount;
+extern uint32_t gGlyphExtentsWidthsTotalSize;
+extern uint32_t gGlyphExtentsSetupEagerSimple;
+extern uint32_t gGlyphExtentsSetupEagerTight;
+extern uint32_t gGlyphExtentsSetupLazyTight;
+extern uint32_t gGlyphExtentsSetupFallBackToTight;
+#endif
+
+gfxGlyphExtents::~gfxGlyphExtents()
+{
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ gGlyphExtentsWidthsTotalSize +=
+ mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
+ gGlyphExtentsCount++;
+#endif
+ MOZ_COUNT_DTOR(gfxGlyphExtents);
+}
+
+bool
+gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont* aFont,
+ DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents)
+{
+ HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
+ if (!entry) {
+ // Some functions higher up in the call chain deliberately pass in a
+ // nullptr DrawTarget, e.g. GetBaselinePosition() passes nullptr to
+ // gfxTextRun::MeasureText() and that nullptr reaches here.
+ if (!aDrawTarget) {
+ NS_WARNING("Could not get glyph extents (no aDrawTarget)");
+ return false;
+ }
+
+ if (aFont->SetupCairoFont(aDrawTarget)) {
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ ++gGlyphExtentsSetupLazyTight;
+#endif
+ aFont->SetupGlyphExtents(aDrawTarget, aGlyphID, true, this);
+ entry = mTightGlyphExtents.GetEntry(aGlyphID);
+ }
+ if (!entry) {
+ NS_WARNING("Could not get glyph extents");
+ return false;
+ }
+ }
+
+ *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
+ return true;
+}
+
+gfxGlyphExtents::GlyphWidths::~GlyphWidths()
+{
+ uint32_t i, count = mBlocks.Length();
+ for (i = 0; i < count; ++i) {
+ uintptr_t bits = mBlocks[i];
+ if (bits && !(bits & 0x1)) {
+ delete[] reinterpret_cast<uint16_t *>(bits);
+ }
+ }
+}
+
+uint32_t
+gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ uint32_t i;
+ uint32_t size = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (i = 0; i < mBlocks.Length(); ++i) {
+ uintptr_t bits = mBlocks[i];
+ if (bits && !(bits & 0x1)) {
+ size += aMallocSizeOf(reinterpret_cast<void*>(bits));
+ }
+ }
+ return size;
+}
+
+void
+gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
+{
+ uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
+ uint32_t len = mBlocks.Length();
+ if (block >= len) {
+ uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
+ if (!elems)
+ return;
+ memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
+ }
+
+ uintptr_t bits = mBlocks[block];
+ uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
+ if (!bits) {
+ mBlocks[block] = MakeSingle(glyphOffset, aWidth);
+ return;
+ }
+
+ uint16_t *newBlock;
+ if (bits & 0x1) {
+ // Expand the block to a real block. We could avoid this by checking
+ // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
+ newBlock = new uint16_t[BLOCK_SIZE];
+ if (!newBlock)
+ return;
+ uint32_t i;
+ for (i = 0; i < BLOCK_SIZE; ++i) {
+ newBlock[i] = INVALID_WIDTH;
+ }
+ newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
+ mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
+ } else {
+ newBlock = reinterpret_cast<uint16_t *>(bits);
+ }
+ newBlock[glyphOffset] = aWidth;
+}
+
+void
+gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
+{
+ HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
+ if (!entry)
+ return;
+ entry->x = aExtentsAppUnits.X();
+ entry->y = aExtentsAppUnits.Y();
+ entry->width = aExtentsAppUnits.Width();
+ entry->height = aExtentsAppUnits.Height();
+}
+
+size_t
+gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
+ mTightGlyphExtents.ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+size_t
+gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
diff --git a/system/graphics/thebes/gfxGlyphExtents.h b/system/graphics/thebes/gfxGlyphExtents.h
new file mode 100644
index 000000000..7da6d9192
--- /dev/null
+++ b/system/graphics/thebes/gfxGlyphExtents.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GLYPHEXTENTS_H
+#define GFX_GLYPHEXTENTS_H
+
+#include "gfxFont.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
+
+class gfxContext;
+struct gfxRect;
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+} // namespace mozilla
+
+/**
+ * This stores glyph bounds information for a particular gfxFont, at
+ * a particular appunits-per-dev-pixel ratio (because the compressed glyph
+ * width array is stored in appunits).
+ *
+ * We store a hashtable from glyph IDs to float bounding rects. For the
+ * common case where the glyph has no horizontal left bearing, and no
+ * y overflow above the font ascent or below the font descent, and tight
+ * bounding boxes are not required, we avoid storing the glyph ID in the hashtable
+ * and instead consult an array of 16-bit glyph XMost values (in appunits).
+ * This array always has an entry for the font's space glyph --- the width is
+ * assumed to be zero.
+ */
+class gfxGlyphExtents {
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+public:
+ explicit gfxGlyphExtents(int32_t aAppUnitsPerDevUnit) :
+ mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) {
+ MOZ_COUNT_CTOR(gfxGlyphExtents);
+ }
+ ~gfxGlyphExtents();
+
+ enum { INVALID_WIDTH = 0xFFFF };
+
+ void NotifyGlyphsChanged() {
+ mTightGlyphExtents.Clear();
+ }
+
+ // returns INVALID_WIDTH => not a contained glyph
+ // Otherwise the glyph has no before-bearing or vertical bearings,
+ // and the result is its width measured from the baseline origin, in
+ // appunits.
+ uint16_t GetContainedGlyphWidthAppUnits(uint32_t aGlyphID) const {
+ return mContainedGlyphWidths.Get(aGlyphID);
+ }
+
+ bool IsGlyphKnown(uint32_t aGlyphID) const {
+ return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH ||
+ mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
+ }
+
+ bool IsGlyphKnownWithTightExtents(uint32_t aGlyphID) const {
+ return mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
+ }
+
+ // Get glyph extents; a rectangle relative to the left baseline origin
+ // Returns true on success. Can fail on OOM or when aContext is null
+ // and extents were not (successfully) prefetched.
+ bool GetTightGlyphExtentsAppUnits(gfxFont* aFont,
+ DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents);
+
+ void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) {
+ mContainedGlyphWidths.Set(aGlyphID, aWidth);
+ }
+ void SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits);
+
+ int32_t GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+private:
+ class HashEntry : public nsUint32HashKey {
+ public:
+ // When constructing a new entry in the hashtable, we'll leave this
+ // blank. The caller of Put() will fill this in.
+ explicit HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {}
+ HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) {
+ x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height;
+ }
+
+ float x, y, width, height;
+ };
+
+ enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks
+
+ class GlyphWidths {
+ public:
+ void Set(uint32_t aIndex, uint16_t aValue);
+ uint16_t Get(uint32_t aIndex) const {
+ uint32_t block = aIndex >> BLOCK_SIZE_BITS;
+ if (block >= mBlocks.Length())
+ return INVALID_WIDTH;
+ uintptr_t bits = mBlocks[block];
+ if (!bits)
+ return INVALID_WIDTH;
+ uint32_t indexInBlock = aIndex & (BLOCK_SIZE - 1);
+ if (bits & 0x1) {
+ if (GetGlyphOffset(bits) != indexInBlock)
+ return INVALID_WIDTH;
+ return GetWidth(bits);
+ }
+ uint16_t *widths = reinterpret_cast<uint16_t *>(bits);
+ return widths[indexInBlock];
+ }
+
+ uint32_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ ~GlyphWidths();
+
+ private:
+ static uint32_t GetGlyphOffset(uintptr_t aBits) {
+ NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
+ return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1);
+ }
+ static uint32_t GetWidth(uintptr_t aBits) {
+ NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
+ return aBits >> (1 + BLOCK_SIZE_BITS);
+ }
+ static uintptr_t MakeSingle(uint32_t aGlyphOffset, uint16_t aWidth) {
+ return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1;
+ }
+
+ nsTArray<uintptr_t> mBlocks;
+ };
+
+ GlyphWidths mContainedGlyphWidths;
+ nsTHashtable<HashEntry> mTightGlyphExtents;
+ int32_t mAppUnitsPerDevUnit;
+
+private:
+ // not implemented:
+ gfxGlyphExtents(const gfxGlyphExtents& aOther) = delete;
+ gfxGlyphExtents& operator=(const gfxGlyphExtents& aOther) = delete;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxGradientCache.cpp b/system/graphics/thebes/gfxGradientCache.cpp
new file mode 100644
index 000000000..bfa82685c
--- /dev/null
+++ b/system/graphics/thebes/gfxGradientCache.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/2D.h"
+#include "nsTArray.h"
+#include "PLDHashTable.h"
+#include "nsExpirationTracker.h"
+#include "nsClassHashtable.h"
+#include "gfxGradientCache.h"
+#include <time.h>
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla;
+
+struct GradientCacheKey : public PLDHashEntryHdr {
+ typedef const GradientCacheKey& KeyType;
+ typedef const GradientCacheKey* KeyTypePointer;
+ enum { ALLOW_MEMMOVE = true };
+ const nsTArray<GradientStop> mStops;
+ ExtendMode mExtend;
+ BackendType mBackendType;
+
+ GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
+ : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
+ { }
+
+ explicit GradientCacheKey(const GradientCacheKey* aOther)
+ : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
+ { }
+
+ union FloatUint32
+ {
+ float f;
+ uint32_t u;
+ };
+
+ static PLDHashNumber
+ HashKey(const KeyTypePointer aKey)
+ {
+ PLDHashNumber hash = 0;
+ FloatUint32 convert;
+ hash = AddToHash(hash, int(aKey->mBackendType));
+ hash = AddToHash(hash, int(aKey->mExtend));
+ for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
+ hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
+ // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
+ convert.f = aKey->mStops[i].offset;
+ hash = AddToHash(hash, convert.f ? convert.u : 0);
+ }
+ return hash;
+ }
+
+ bool KeyEquals(KeyTypePointer aKey) const
+ {
+ bool sameStops = true;
+ if (aKey->mStops.Length() != mStops.Length()) {
+ sameStops = false;
+ } else {
+ for (uint32_t i = 0; i < mStops.Length(); i++) {
+ if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
+ mStops[i].offset != aKey->mStops[i].offset) {
+ sameStops = false;
+ break;
+ }
+ }
+ }
+
+ return sameStops &&
+ (aKey->mBackendType == mBackendType) &&
+ (aKey->mExtend == mExtend);
+ }
+ static KeyTypePointer KeyToPointer(KeyType aKey)
+ {
+ return &aKey;
+ }
+};
+
+/**
+ * This class is what is cached. It need to be allocated in an object separated
+ * to the cache entry to be able to be tracked by the nsExpirationTracker.
+ * */
+struct GradientCacheData {
+ GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey)
+ : mStops(aStops),
+ mKey(aKey)
+ {}
+
+ GradientCacheData(const GradientCacheData& aOther)
+ : mStops(aOther.mStops),
+ mKey(aOther.mKey)
+ { }
+
+ nsExpirationState *GetExpirationState() {
+ return &mExpirationState;
+ }
+
+ nsExpirationState mExpirationState;
+ const RefPtr<GradientStops> mStops;
+ GradientCacheKey mKey;
+};
+
+/**
+ * This class implements a cache with no maximum size, that retains the
+ * gfxPatterns used to draw the gradients.
+ *
+ * The key is the nsStyleGradient that defines the gradient, and the size of the
+ * gradient.
+ *
+ * The value is the gfxPattern, and whether or not we perform an optimization
+ * based on the actual gradient property.
+ *
+ * An entry stays in the cache as long as it is used often. As long as a cache
+ * entry is in the cache, all the references it has are guaranteed to be valid:
+ * the nsStyleRect for the key, the gfxPattern for the value.
+ */
+class GradientCache final : public nsExpirationTracker<GradientCacheData,4>
+{
+ public:
+ GradientCache()
+ : nsExpirationTracker<GradientCacheData,4>(MAX_GENERATION_MS,
+ "GradientCache")
+ {
+ srand(time(nullptr));
+ mTimerPeriod = rand() % MAX_GENERATION_MS + 1;
+ }
+
+ virtual void NotifyExpired(GradientCacheData* aObject)
+ {
+ // This will free the gfxPattern.
+ RemoveObject(aObject);
+ mHashEntries.Remove(aObject->mKey);
+ }
+
+ GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
+ {
+ GradientCacheData* gradient =
+ mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
+
+ if (gradient) {
+ MarkUsed(gradient);
+ }
+
+ return gradient;
+ }
+
+ // Returns true if we successfully register the gradient in the cache, false
+ // otherwise.
+ bool RegisterEntry(GradientCacheData* aValue)
+ {
+ nsresult rv = AddObject(aValue);
+ if (NS_FAILED(rv)) {
+ // We are OOM, and we cannot track this object. We don't want stall
+ // entries in the hash table (since the expiration tracker is responsible
+ // for removing the cache entries), so we avoid putting that entry in the
+ // table, which is a good things considering we are short on memory
+ // anyway, we probably don't want to retain things.
+ return false;
+ }
+ mHashEntries.Put(aValue->mKey, aValue);
+ return true;
+ }
+
+ protected:
+ uint32_t mTimerPeriod;
+ static const uint32_t MAX_GENERATION_MS = 10000;
+ /**
+ * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
+ */
+ nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
+};
+
+static GradientCache* gGradientCache = nullptr;
+
+GradientStops *
+gfxGradientCache::GetGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
+{
+ if (!gGradientCache) {
+ gGradientCache = new GradientCache();
+ }
+ GradientCacheData* cached =
+ gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType());
+ if (cached && cached->mStops) {
+ if (!cached->mStops->IsValid()) {
+ gGradientCache->NotifyExpired(cached);
+ } else {
+ return cached->mStops;
+ }
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<GradientStops>
+gfxGradientCache::GetOrCreateGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
+{
+ if (aDT->IsRecording()) {
+ return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
+ }
+
+ RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
+ if (!gs) {
+ gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
+ if (!gs) {
+ return nullptr;
+ }
+ GradientCacheData *cached =
+ new GradientCacheData(gs, GradientCacheKey(aStops, aExtend,
+ aDT->GetBackendType()));
+ if (!gGradientCache->RegisterEntry(cached)) {
+ delete cached;
+ }
+ }
+ return gs.forget();
+}
+
+void
+gfxGradientCache::PurgeAllCaches()
+{
+ if (gGradientCache) {
+ gGradientCache->AgeAllGenerations();
+ }
+}
+
+void
+gfxGradientCache::Shutdown()
+{
+ delete gGradientCache;
+ gGradientCache = nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/gfxGradientCache.h b/system/graphics/thebes/gfxGradientCache.h
new file mode 100644
index 000000000..70ae258eb
--- /dev/null
+++ b/system/graphics/thebes/gfxGradientCache.h
@@ -0,0 +1,36 @@
+
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GRADIENT_CACHE_H
+#define GFX_GRADIENT_CACHE_H
+
+#include "nsTArray.h"
+#include "gfxPattern.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class gfxGradientCache {
+public:
+ static gfx::GradientStops*
+ GetGradientStops(const gfx::DrawTarget *aDT,
+ nsTArray<gfx::GradientStop>& aStops,
+ gfx::ExtendMode aExtend);
+
+ static already_AddRefed<gfx::GradientStops>
+ GetOrCreateGradientStops(const gfx::DrawTarget *aDT,
+ nsTArray<gfx::GradientStop>& aStops,
+ gfx::ExtendMode aExtend);
+
+ static void PurgeAllCaches();
+ static void Shutdown();
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/thebes/gfxGraphiteShaper.cpp b/system/graphics/thebes/gfxGraphiteShaper.cpp
new file mode 100644
index 000000000..aeebf30f2
--- /dev/null
+++ b/system/graphics/thebes/gfxGraphiteShaper.cpp
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxGraphiteShaper.h"
+#include "nsString.h"
+#include "gfxContext.h"
+#include "gfxFontConstants.h"
+#include "gfxTextRun.h"
+
+#include "graphite2/Font.h"
+#include "graphite2/Segment.h"
+
+#include "harfbuzz/hb.h"
+
+#define FloatToFixed(f) (65536 * (f))
+#define FixedToFloat(f) ((f) * (1.0 / 65536.0))
+// Right shifts of negative (signed) integers are undefined, as are overflows
+// when converting unsigned to negative signed integers.
+// (If speed were an issue we could make some 2's complement assumptions.)
+#define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \
+ : -((32767 - (f)) >> 16))
+
+using namespace mozilla; // for AutoSwap_* types
+
+/*
+ * Creation and destruction; on deletion, release any font tables we're holding
+ */
+
+gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
+ : gfxFontShaper(aFont),
+ mGrFace(mFont->GetFontEntry()->GetGrFace()),
+ mGrFont(nullptr), mFallbackToSmallCaps(false)
+{
+ mCallbackData.mFont = aFont;
+}
+
+gfxGraphiteShaper::~gfxGraphiteShaper()
+{
+ if (mGrFont) {
+ gr_font_destroy(mGrFont);
+ }
+ mFont->GetFontEntry()->ReleaseGrFace(mGrFace);
+}
+
+/*static*/ float
+gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid)
+{
+ const CallbackData *cb =
+ static_cast<const CallbackData*>(appFontHandle);
+ return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mDrawTarget, glyphid));
+}
+
+static inline uint32_t
+MakeGraphiteLangTag(uint32_t aTag)
+{
+ uint32_t grLangTag = aTag;
+ // replace trailing space-padding with NULs for graphite
+ uint32_t mask = 0x000000FF;
+ while ((grLangTag & mask) == ' ') {
+ grLangTag &= ~mask;
+ mask <<= 8;
+ }
+ return grLangTag;
+}
+
+struct GrFontFeatures {
+ gr_face *mFace;
+ gr_feature_val *mFeatures;
+};
+
+static void
+AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
+{
+ GrFontFeatures *f = static_cast<GrFontFeatures*>(aUserArg);
+
+ const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag);
+ if (fref) {
+ gr_fref_set_feature_value(fref, aValue, f->mFeatures);
+ }
+}
+
+bool
+gfxGraphiteShaper::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ // some font back-ends require this in order to get proper hinted metrics
+ if (!mFont->SetupCairoFont(aDrawTarget)) {
+ return false;
+ }
+
+ mCallbackData.mDrawTarget = aDrawTarget;
+
+ const gfxFontStyle *style = mFont->GetStyle();
+
+ if (!mGrFont) {
+ if (!mGrFace) {
+ return false;
+ }
+
+ if (mFont->ProvidesGlyphWidths()) {
+ gr_font_ops ops = {
+ sizeof(gr_font_ops),
+ &GrGetAdvance,
+ nullptr // vertical text not yet implemented
+ };
+ mGrFont = gr_make_font_with_ops(mFont->GetAdjustedSize(),
+ &mCallbackData, &ops, mGrFace);
+ } else {
+ mGrFont = gr_make_font(mFont->GetAdjustedSize(), mGrFace);
+ }
+
+ if (!mGrFont) {
+ return false;
+ }
+
+ // determine whether petite-caps falls back to small-caps
+ if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
+ switch (style->variantCaps) {
+ case NS_FONT_VARIANT_CAPS_ALLPETITE:
+ case NS_FONT_VARIANT_CAPS_PETITECAPS:
+ bool synLower, synUpper;
+ mFont->SupportsVariantCaps(aScript, style->variantCaps,
+ mFallbackToSmallCaps, synLower,
+ synUpper);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ gfxFontEntry *entry = mFont->GetFontEntry();
+ uint32_t grLang = 0;
+ if (style->languageOverride) {
+ grLang = MakeGraphiteLangTag(style->languageOverride);
+ } else if (entry->mLanguageOverride) {
+ grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
+ } else if (style->explicitLanguage) {
+ nsAutoCString langString;
+ style->language->ToUTF8String(langString);
+ grLang = GetGraphiteTagForLang(langString);
+ }
+ gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
+
+ // insert any merged features into Graphite feature list
+ GrFontFeatures f = {mGrFace, grFeatures};
+ MergeFontFeatures(style,
+ mFont->GetFontEntry()->mFeatureSettings,
+ aShapedText->DisableLigatures(),
+ mFont->GetFontEntry()->FamilyName(),
+ mFallbackToSmallCaps,
+ AddFeature,
+ &f);
+
+ // Graphite shaping doesn't map U+00a0 (nbsp) to space if it is missing
+ // from the font, so check for that possibility. (Most fonts double-map
+ // the space glyph to both 0x20 and 0xA0, so this won't often be needed;
+ // so we don't copy the text until we know it's required.)
+ nsAutoString transformed;
+ const char16_t NO_BREAK_SPACE = 0x00a0;
+ if (!entry->HasCharacter(NO_BREAK_SPACE)) {
+ nsDependentSubstring src(aText, aLength);
+ if (src.FindChar(NO_BREAK_SPACE) != kNotFound) {
+ transformed = src;
+ transformed.ReplaceChar(NO_BREAK_SPACE, ' ');
+ aText = transformed.BeginReading();
+ }
+ }
+
+ size_t numChars = gr_count_unicode_characters(gr_utf16,
+ aText, aText + aLength,
+ nullptr);
+ gr_bidirtl grBidi = gr_bidirtl(aShapedText->IsRightToLeft()
+ ? (gr_rtl | gr_nobidi) : gr_nobidi);
+ gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
+ gr_utf16, aText, numChars, grBidi);
+
+ gr_featureval_destroy(grFeatures);
+
+ if (!seg) {
+ return false;
+ }
+
+ nsresult rv = SetGlyphsFromSegment(aDrawTarget, aShapedText, aOffset, aLength,
+ aText, seg);
+
+ gr_seg_destroy(seg);
+
+ return NS_SUCCEEDED(rv);
+}
+
+#define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
+ // for short (typical) runs up to this length
+
+struct Cluster {
+ uint32_t baseChar; // in UTF16 code units, not Unicode character indices
+ uint32_t baseGlyph;
+ uint32_t nChars; // UTF16 code units
+ uint32_t nGlyphs;
+ Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
+};
+
+nsresult
+gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget *aDrawTarget,
+ gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ const char16_t *aText,
+ gr_segment *aSegment)
+{
+ int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
+ bool rtl = aShapedText->IsRightToLeft();
+
+ uint32_t glyphCount = gr_seg_n_slots(aSegment);
+
+ // identify clusters; graphite may have reordered/expanded/ligated glyphs.
+ AutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
+ AutoTArray<uint16_t,SMALL_GLYPH_RUN> gids;
+ AutoTArray<float,SMALL_GLYPH_RUN> xLocs;
+ AutoTArray<float,SMALL_GLYPH_RUN> yLocs;
+
+ if (!clusters.SetLength(aLength, fallible) ||
+ !gids.SetLength(glyphCount, fallible) ||
+ !xLocs.SetLength(glyphCount, fallible) ||
+ !yLocs.SetLength(glyphCount, fallible))
+ {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // walk through the glyph slots and check which original character
+ // each is associated with
+ uint32_t gIndex = 0; // glyph slot index
+ uint32_t cIndex = 0; // current cluster index
+ for (const gr_slot *slot = gr_seg_first_slot(aSegment);
+ slot != nullptr;
+ slot = gr_slot_next_in_segment(slot), gIndex++)
+ {
+ uint32_t before =
+ gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot)));
+ uint32_t after =
+ gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot)));
+ gids[gIndex] = gr_slot_gid(slot);
+ xLocs[gIndex] = gr_slot_origin_X(slot);
+ yLocs[gIndex] = gr_slot_origin_Y(slot);
+
+ // if this glyph has a "before" character index that precedes the
+ // current cluster's char index, we need to merge preceding
+ // clusters until it gets included
+ while (before < clusters[cIndex].baseChar && cIndex > 0) {
+ clusters[cIndex-1].nChars += clusters[cIndex].nChars;
+ clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
+ --cIndex;
+ }
+
+ // if there's a gap between the current cluster's base character and
+ // this glyph's, extend the cluster to include the intervening chars
+ if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
+ before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
+ {
+ NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
+ Cluster& c = clusters[cIndex + 1];
+ c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
+ c.nChars = before - c.baseChar;
+ c.baseGlyph = gIndex;
+ c.nGlyphs = 0;
+ ++cIndex;
+ }
+
+ // increment cluster's glyph count to include current slot
+ NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
+ ++clusters[cIndex].nGlyphs;
+
+ // bump |after| index if it falls in the middle of a surrogate pair
+ if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 &&
+ NS_IS_LOW_SURROGATE(aText[after + 1])) {
+ after++;
+ }
+ // extend cluster if necessary to reach the glyph's "after" index
+ if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
+ clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
+ }
+ }
+
+ bool roundX, roundY;
+ GetRoundOffsetsToPixels(aDrawTarget, &roundX, &roundY);
+
+ gfxShapedText::CompressedGlyph *charGlyphs =
+ aShapedText->GetCharacterGlyphs() + aOffset;
+
+ // now put glyphs into the textrun, one cluster at a time
+ for (uint32_t i = 0; i <= cIndex; ++i) {
+ const Cluster& c = clusters[i];
+
+ float adv; // total advance of the cluster
+ if (rtl) {
+ if (i == 0) {
+ adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
+ } else {
+ adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
+ }
+ } else {
+ if (i == cIndex) {
+ adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
+ } else {
+ adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
+ }
+ }
+
+ // Check for default-ignorable char that didn't get filtered, combined,
+ // etc by the shaping process, and skip it.
+ uint32_t offs = c.baseChar;
+ NS_ASSERTION(offs < aLength, "unexpected offset");
+ if (c.nGlyphs == 1 && c.nChars == 1 &&
+ aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
+ continue;
+ }
+
+ uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits :
+ NSToIntRound(adv * dev2appUnits);
+ if (c.nGlyphs == 1 &&
+ gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
+ gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
+ charGlyphs[offs].IsClusterStart() &&
+ yLocs[c.baseGlyph] == 0)
+ {
+ charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
+ } else {
+ // not a one-to-one mapping with simple metrics: use DetailedGlyph
+ AutoTArray<gfxShapedText::DetailedGlyph,8> details;
+ float clusterLoc;
+ for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
+ gfxShapedText::DetailedGlyph* d = details.AppendElement();
+ d->mGlyphID = gids[j];
+ d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
+ -yLocs[j] * dev2appUnits;
+ if (j == c.baseGlyph) {
+ d->mXOffset = 0;
+ d->mAdvance = appAdvance;
+ clusterLoc = xLocs[j];
+ } else {
+ float dx = rtl ? (xLocs[j] - clusterLoc) :
+ (xLocs[j] - clusterLoc - adv);
+ d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits :
+ dx * dev2appUnits;
+ d->mAdvance = 0;
+ }
+ }
+ gfxShapedText::CompressedGlyph g;
+ g.SetComplex(charGlyphs[offs].IsClusterStart(),
+ true, details.Length());
+ aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
+ }
+
+ for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
+ NS_ASSERTION(j < aLength, "unexpected offset");
+ gfxShapedText::CompressedGlyph &g = charGlyphs[j];
+ NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+ g.SetComplex(g.IsClusterStart(), false, 0);
+ }
+ }
+
+ return NS_OK;
+}
+
+#undef SMALL_GLYPH_RUN
+
+// for language tag validation - include list of tags from the IANA registry
+#include "gfxLanguageTagList.cpp"
+
+nsTHashtable<nsUint32HashKey> *gfxGraphiteShaper::sLanguageTags;
+
+/*static*/ uint32_t
+gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang)
+{
+ int len = aLang.Length();
+ if (len < 2) {
+ return 0;
+ }
+
+ // convert primary language subtag to a left-packed, NUL-padded integer
+ // for the Graphite API
+ uint32_t grLang = 0;
+ for (int i = 0; i < 4; ++i) {
+ grLang <<= 8;
+ if (i < len) {
+ uint8_t ch = aLang[i];
+ if (ch == '-') {
+ // found end of primary language subtag, truncate here
+ len = i;
+ continue;
+ }
+ if (ch < 'a' || ch > 'z') {
+ // invalid character in tag, so ignore it completely
+ return 0;
+ }
+ grLang += ch;
+ }
+ }
+
+ // valid tags must have length = 2 or 3
+ if (len < 2 || len > 3) {
+ return 0;
+ }
+
+ if (!sLanguageTags) {
+ // store the registered IANA tags in a hash for convenient validation
+ sLanguageTags = new nsTHashtable<nsUint32HashKey>(ArrayLength(sLanguageTagList));
+ for (const uint32_t *tag = sLanguageTagList; *tag != 0; ++tag) {
+ sLanguageTags->PutEntry(*tag);
+ }
+ }
+
+ // only accept tags known in the IANA registry
+ if (sLanguageTags->GetEntry(grLang)) {
+ return grLang;
+ }
+
+ return 0;
+}
+
+/*static*/ void
+gfxGraphiteShaper::Shutdown()
+{
+#ifdef NS_FREE_PERMANENT_DATA
+ if (sLanguageTags) {
+ sLanguageTags->Clear();
+ delete sLanguageTags;
+ sLanguageTags = nullptr;
+ }
+#endif
+}
diff --git a/system/graphics/thebes/gfxGraphiteShaper.h b/system/graphics/thebes/gfxGraphiteShaper.h
new file mode 100644
index 000000000..e409973cc
--- /dev/null
+++ b/system/graphics/thebes/gfxGraphiteShaper.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GRAPHITESHAPER_H
+#define GFX_GRAPHITESHAPER_H
+
+#include "gfxFont.h"
+
+#include "mozilla/gfx/2D.h"
+
+struct gr_face;
+struct gr_font;
+struct gr_segment;
+
+class gfxGraphiteShaper : public gfxFontShaper {
+public:
+ explicit gfxGraphiteShaper(gfxFont *aFont);
+ virtual ~gfxGraphiteShaper();
+
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText);
+
+ static void Shutdown();
+
+protected:
+ nsresult SetGlyphsFromSegment(DrawTarget *aDrawTarget,
+ gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ const char16_t *aText,
+ gr_segment *aSegment);
+
+ static float GrGetAdvance(const void* appFontHandle, uint16_t glyphid);
+
+ gr_face *mGrFace; // owned by the font entry; shaper must call
+ // gfxFontEntry::ReleaseGrFace when finished with it
+ gr_font *mGrFont; // owned by the shaper itself
+
+ struct CallbackData {
+ gfxFont* mFont;
+ mozilla::gfx::DrawTarget* mDrawTarget;
+ };
+
+ CallbackData mCallbackData;
+ bool mFallbackToSmallCaps; // special fallback for the petite-caps case
+
+ // Convert HTML 'lang' (BCP47) to Graphite language code
+ static uint32_t GetGraphiteTagForLang(const nsCString& aLang);
+ static nsTHashtable<nsUint32HashKey> *sLanguageTags;
+};
+
+#endif /* GFX_GRAPHITESHAPER_H */
diff --git a/system/graphics/thebes/gfxHarfBuzzShaper.cpp b/system/graphics/thebes/gfxHarfBuzzShaper.cpp
new file mode 100644
index 000000000..4a563a95f
--- /dev/null
+++ b/system/graphics/thebes/gfxHarfBuzzShaper.cpp
@@ -0,0 +1,1786 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "gfxContext.h"
+#include "gfxFontConstants.h"
+#include "gfxHarfBuzzShaper.h"
+#include "gfxFontUtils.h"
+#include "gfxTextRun.h"
+#include "mozilla/Sprintf.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeScriptCodes.h"
+
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-ot.h"
+
+#include "unicode/unorm.h"
+#include "unicode/utext.h"
+
+static const UNormalizer2 * sNormalizer = nullptr;
+
+#include <algorithm>
+
+#define FloatToFixed(f) (65536 * (f))
+#define FixedToFloat(f) ((f) * (1.0 / 65536.0))
+// Right shifts of negative (signed) integers are undefined, as are overflows
+// when converting unsigned to negative signed integers.
+// (If speed were an issue we could make some 2's complement assumptions.)
+#define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \
+ : -((32767 - (f)) >> 16))
+
+using namespace mozilla; // for AutoSwap_* types
+using namespace mozilla::unicode; // for Unicode property lookup
+
+/*
+ * Creation and destruction; on deletion, release any font tables we're holding
+ */
+
+gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
+ : gfxFontShaper(aFont),
+ mHBFace(aFont->GetFontEntry()->GetHBFace()),
+ mHBFont(nullptr),
+ mKernTable(nullptr),
+ mHmtxTable(nullptr),
+ mVmtxTable(nullptr),
+ mVORGTable(nullptr),
+ mLocaTable(nullptr),
+ mGlyfTable(nullptr),
+ mCmapTable(nullptr),
+ mCmapFormat(-1),
+ mSubtableOffset(0),
+ mUVSTableOffset(0),
+ mNumLongHMetrics(0),
+ mNumLongVMetrics(0),
+ mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
+ mUseFontGlyphWidths(false),
+ mInitialized(false),
+ mVerticalInitialized(false),
+ mLoadedLocaGlyf(false),
+ mLocaLongOffsets(false)
+{
+}
+
+gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
+{
+ if (mCmapTable) {
+ hb_blob_destroy(mCmapTable);
+ }
+ if (mHmtxTable) {
+ hb_blob_destroy(mHmtxTable);
+ }
+ if (mKernTable) {
+ hb_blob_destroy(mKernTable);
+ }
+ if (mVmtxTable) {
+ hb_blob_destroy(mVmtxTable);
+ }
+ if (mVORGTable) {
+ hb_blob_destroy(mVORGTable);
+ }
+ if (mLocaTable) {
+ hb_blob_destroy(mLocaTable);
+ }
+ if (mGlyfTable) {
+ hb_blob_destroy(mGlyfTable);
+ }
+ if (mHBFont) {
+ hb_font_destroy(mHBFont);
+ }
+ if (mHBFace) {
+ hb_face_destroy(mHBFace);
+ }
+}
+
+#define UNICODE_BMP_LIMIT 0x10000
+
+hb_codepoint_t
+gfxHarfBuzzShaper::GetNominalGlyph(hb_codepoint_t unicode) const
+{
+ hb_codepoint_t gid = 0;
+
+ if (mUseFontGetGlyph) {
+ gid = mFont->GetGlyph(unicode, 0);
+ } else {
+ // we only instantiate a harfbuzz shaper if there's a cmap available
+ NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
+ "we cannot be using this font!");
+
+ NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
+ "cmap data not correctly set up, expect disaster");
+
+ uint32_t length;
+ const uint8_t* data =
+ (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
+
+ switch (mCmapFormat) {
+ case 4:
+ gid = unicode < UNICODE_BMP_LIMIT ?
+ gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
+ length - mSubtableOffset,
+ unicode) : 0;
+ break;
+ case 10:
+ gid = gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
+ unicode);
+ break;
+ case 12:
+ gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
+ unicode);
+ break;
+ default:
+ NS_WARNING("unsupported cmap format, glyphs will be missing");
+ break;
+ }
+ }
+
+ if (!gid) {
+ // if there's no glyph for &nbsp;, just use the space glyph instead
+ if (unicode == 0xA0) {
+ gid = mFont->GetSpaceGlyph();
+ }
+ }
+
+ return gid;
+}
+
+hb_codepoint_t
+gfxHarfBuzzShaper::GetVariationGlyph(hb_codepoint_t unicode,
+ hb_codepoint_t variation_selector) const
+{
+ if (mUseFontGetGlyph) {
+ return mFont->GetGlyph(unicode, variation_selector);
+ }
+
+ NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
+ "we cannot be using this font!");
+ NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
+ "cmap data not correctly set up, expect disaster");
+
+ uint32_t length;
+ const uint8_t* data =
+ (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
+
+ if (mUVSTableOffset) {
+ hb_codepoint_t gid =
+ gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
+ unicode, variation_selector);
+ if (gid) {
+ return gid;
+ }
+ }
+
+ uint32_t compat =
+ gfxFontUtils::GetUVSFallback(unicode, variation_selector);
+ if (compat) {
+ switch (mCmapFormat) {
+ case 4:
+ if (compat < UNICODE_BMP_LIMIT) {
+ return gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
+ length - mSubtableOffset,
+ compat);
+ }
+ break;
+ case 10:
+ return gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
+ compat);
+ break;
+ case 12:
+ return gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
+ compat);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+VertFormsGlyphCompare(const void* aKey, const void* aElem)
+{
+ return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem)));
+}
+
+// Return a vertical presentation-form codepoint corresponding to the
+// given Unicode value, or 0 if no such form is available.
+static hb_codepoint_t
+GetVerticalPresentationForm(hb_codepoint_t unicode)
+{
+ static const uint16_t sVerticalForms[][2] = {
+ { 0x2013, 0xfe32 }, // EN DASH
+ { 0x2014, 0xfe31 }, // EM DASH
+ { 0x2025, 0xfe30 }, // TWO DOT LEADER
+ { 0x2026, 0xfe19 }, // HORIZONTAL ELLIPSIS
+ { 0x3001, 0xfe11 }, // IDEOGRAPHIC COMMA
+ { 0x3002, 0xfe12 }, // IDEOGRAPHIC FULL STOP
+ { 0x3008, 0xfe3f }, // LEFT ANGLE BRACKET
+ { 0x3009, 0xfe40 }, // RIGHT ANGLE BRACKET
+ { 0x300a, 0xfe3d }, // LEFT DOUBLE ANGLE BRACKET
+ { 0x300b, 0xfe3e }, // RIGHT DOUBLE ANGLE BRACKET
+ { 0x300c, 0xfe41 }, // LEFT CORNER BRACKET
+ { 0x300d, 0xfe42 }, // RIGHT CORNER BRACKET
+ { 0x300e, 0xfe43 }, // LEFT WHITE CORNER BRACKET
+ { 0x300f, 0xfe44 }, // RIGHT WHITE CORNER BRACKET
+ { 0x3010, 0xfe3b }, // LEFT BLACK LENTICULAR BRACKET
+ { 0x3011, 0xfe3c }, // RIGHT BLACK LENTICULAR BRACKET
+ { 0x3014, 0xfe39 }, // LEFT TORTOISE SHELL BRACKET
+ { 0x3015, 0xfe3a }, // RIGHT TORTOISE SHELL BRACKET
+ { 0x3016, 0xfe17 }, // LEFT WHITE LENTICULAR BRACKET
+ { 0x3017, 0xfe18 }, // RIGHT WHITE LENTICULAR BRACKET
+ { 0xfe4f, 0xfe34 }, // WAVY LOW LINE
+ { 0xff01, 0xfe15 }, // FULLWIDTH EXCLAMATION MARK
+ { 0xff08, 0xfe35 }, // FULLWIDTH LEFT PARENTHESIS
+ { 0xff09, 0xfe36 }, // FULLWIDTH RIGHT PARENTHESIS
+ { 0xff0c, 0xfe10 }, // FULLWIDTH COMMA
+ { 0xff1a, 0xfe13 }, // FULLWIDTH COLON
+ { 0xff1b, 0xfe14 }, // FULLWIDTH SEMICOLON
+ { 0xff1f, 0xfe16 }, // FULLWIDTH QUESTION MARK
+ { 0xff3b, 0xfe47 }, // FULLWIDTH LEFT SQUARE BRACKET
+ { 0xff3d, 0xfe48 }, // FULLWIDTH RIGHT SQUARE BRACKET
+ { 0xff3f, 0xfe33 }, // FULLWIDTH LOW LINE
+ { 0xff5b, 0xfe37 }, // FULLWIDTH LEFT CURLY BRACKET
+ { 0xff5d, 0xfe38 } // FULLWIDTH RIGHT CURLY BRACKET
+ };
+ const uint16_t* charPair =
+ static_cast<const uint16_t*>(bsearch(&unicode,
+ sVerticalForms,
+ ArrayLength(sVerticalForms),
+ sizeof(sVerticalForms[0]),
+ VertFormsGlyphCompare));
+ return charPair ? charPair[1] : 0;
+}
+
+static hb_bool_t
+HBGetNominalGlyph(hb_font_t *font, void *font_data,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+
+ if (fcd->mShaper->UseVerticalPresentationForms()) {
+ hb_codepoint_t verticalForm = GetVerticalPresentationForm(unicode);
+ if (verticalForm) {
+ *glyph = fcd->mShaper->GetNominalGlyph(verticalForm);
+ if (*glyph != 0) {
+ return true;
+ }
+ }
+ // fall back to the non-vertical form if we didn't find an alternate
+ }
+
+ *glyph = fcd->mShaper->GetNominalGlyph(unicode);
+ return *glyph != 0;
+}
+
+static hb_bool_t
+HBGetVariationGlyph(hb_font_t *font, void *font_data,
+ hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+ hb_codepoint_t *glyph,
+ void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+
+ if (fcd->mShaper->UseVerticalPresentationForms()) {
+ hb_codepoint_t verticalForm = GetVerticalPresentationForm(unicode);
+ if (verticalForm) {
+ *glyph = fcd->mShaper->GetVariationGlyph(verticalForm,
+ variation_selector);
+ if (*glyph != 0) {
+ return true;
+ }
+ }
+ // fall back to the non-vertical form if we didn't find an alternate
+ }
+
+ *glyph = fcd->mShaper->GetVariationGlyph(unicode, variation_selector);
+ return *glyph != 0;
+}
+
+// Glyph metrics structures, shared (with appropriate reinterpretation of
+// field names) by horizontal and vertical metrics tables.
+struct LongMetric {
+ AutoSwap_PRUint16 advanceWidth; // or advanceHeight, when vertical
+ AutoSwap_PRInt16 lsb; // or tsb, when vertical
+};
+
+struct GlyphMetrics {
+ LongMetric metrics[1]; // actually numberOfLongMetrics
+// the variable-length metrics[] array is immediately followed by:
+// AutoSwap_PRUint16 leftSideBearing[];
+};
+
+hb_position_t
+gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const
+{
+ // font did not implement GetGlyphWidth, so get an unhinted value
+ // directly from the font tables
+
+ NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
+ "font is lacking metrics, we shouldn't be here");
+
+ if (glyph >= uint32_t(mNumLongHMetrics)) {
+ glyph = mNumLongHMetrics - 1;
+ }
+
+ // glyph must be valid now, because we checked during initialization
+ // that mNumLongHMetrics is > 0, and that the metrics table is large enough
+ // to contain mNumLongHMetrics records
+ const GlyphMetrics* metrics =
+ reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mHmtxTable,
+ nullptr));
+ return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
+ uint16_t(metrics->metrics[glyph].advanceWidth));
+}
+
+hb_position_t
+gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) const
+{
+ if (!mVmtxTable) {
+ // Must be a "vertical" font that doesn't actually have vertical metrics;
+ // use a fixed advance.
+ return FloatToFixed(mFont->GetMetrics(gfxFont::eVertical).aveCharWidth);
+ }
+
+ NS_ASSERTION(mNumLongVMetrics > 0,
+ "font is lacking metrics, we shouldn't be here");
+
+ if (glyph >= uint32_t(mNumLongVMetrics)) {
+ glyph = mNumLongVMetrics - 1;
+ }
+
+ // glyph must be valid now, because we checked during initialization
+ // that mNumLongVMetrics is > 0, and that the metrics table is large enough
+ // to contain mNumLongVMetrics records
+ const GlyphMetrics* metrics =
+ reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mVmtxTable,
+ nullptr));
+ return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
+ uint16_t(metrics->metrics[glyph].advanceWidth));
+}
+
+/* static */
+hb_position_t
+gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph, void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+ gfxFont *gfxfont = fcd->mShaper->GetFont();
+ if (gfxfont->ProvidesGlyphWidths()) {
+ return gfxfont->GetGlyphWidth(*fcd->mDrawTarget, glyph);
+ }
+ return fcd->mShaper->GetGlyphHAdvance(glyph);
+}
+
+/* static */
+hb_position_t
+gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph, void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+ // Currently, we don't offer gfxFont subclasses a method to override this
+ // and provide hinted platform-specific vertical advances (analogous to the
+ // GetGlyphWidth method for horizontal advances). If that proves necessary,
+ // we'll add a new gfxFont method and call it from here.
+ return fcd->mShaper->GetGlyphVAdvance(glyph);
+}
+
+struct VORG {
+ AutoSwap_PRUint16 majorVersion;
+ AutoSwap_PRUint16 minorVersion;
+ AutoSwap_PRInt16 defaultVertOriginY;
+ AutoSwap_PRUint16 numVertOriginYMetrics;
+};
+
+struct VORGrec {
+ AutoSwap_PRUint16 glyphIndex;
+ AutoSwap_PRInt16 vertOriginY;
+};
+
+/* static */
+hb_bool_t
+gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_position_t *x, hb_position_t *y,
+ void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+ fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
+ return true;
+}
+
+void
+gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph,
+ hb_position_t *aX, hb_position_t *aY) const
+{
+ *aX = -0.5 * GetGlyphHAdvance(aGlyph);
+
+ if (mVORGTable) {
+ // We checked in Initialize() that the VORG table is safely readable,
+ // so no length/bounds-check needed here.
+ const VORG* vorg =
+ reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
+
+ const VORGrec *lo = reinterpret_cast<const VORGrec*>(vorg + 1);
+ const VORGrec *hi = lo + uint16_t(vorg->numVertOriginYMetrics);
+ const VORGrec *limit = hi;
+ while (lo < hi) {
+ const VORGrec *mid = lo + (hi - lo) / 2;
+ if (uint16_t(mid->glyphIndex) < aGlyph) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+
+ if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
+ *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
+ int16_t(lo->vertOriginY));
+ } else {
+ *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
+ int16_t(vorg->defaultVertOriginY));
+ }
+ return;
+ }
+
+ if (mVmtxTable) {
+ bool emptyGlyf;
+ const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+ if (glyf) {
+ if (emptyGlyf) {
+ *aY = 0;
+ return;
+ }
+
+ const GlyphMetrics* metrics =
+ reinterpret_cast<const GlyphMetrics*>
+ (hb_blob_get_data(mVmtxTable, nullptr));
+ int16_t lsb;
+ if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) {
+ // Glyph is covered by the first (advance & sidebearing) array
+ lsb = int16_t(metrics->metrics[aGlyph].lsb);
+ } else {
+ // Glyph is covered by the second (sidebearing-only) array
+ const AutoSwap_PRInt16* sidebearings =
+ reinterpret_cast<const AutoSwap_PRInt16*>
+ (&metrics->metrics[mNumLongVMetrics]);
+ lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]);
+ }
+ *aY = -FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
+ (lsb + int16_t(glyf->yMax)));
+ return;
+ } else {
+ // XXX TODO: not a truetype font; need to get glyph extents
+ // via some other API?
+ // For now, fall through to default code below.
+ }
+ }
+
+ // XXX should we consider using OS/2 sTypo* metrics if available?
+
+ gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
+ TRUETYPE_TAG('h','h','e','a'));
+ if (hheaTable) {
+ uint32_t len;
+ const MetricsHeader* hhea =
+ reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable,
+ &len));
+ if (len >= sizeof(MetricsHeader)) {
+ // divide up the default advance we're using (1em) in proportion
+ // to ascender:descender from the hhea table
+ int16_t a = int16_t(hhea->ascender);
+ int16_t d = int16_t(hhea->descender);
+ *aY = -FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d));
+ return;
+ }
+ }
+
+ NS_NOTREACHED("we shouldn't be here!");
+ *aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
+}
+
+static hb_bool_t
+HBGetGlyphExtents(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+ return fcd->mShaper->GetGlyphExtents(glyph, extents);
+}
+
+// Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
+// Returns null if not found, otherwise pointer to the beginning of the
+// glyph's data. Sets aEmptyGlyf true if there is no actual data;
+// otherwise, it's guaranteed that we can read at least the bounding box.
+const gfxHarfBuzzShaper::Glyf*
+gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const
+{
+ if (!mLoadedLocaGlyf) {
+ mLoadedLocaGlyf = true; // only try this once; if it fails, this
+ // isn't a truetype font
+ gfxFontEntry *entry = mFont->GetFontEntry();
+ uint32_t len;
+ gfxFontEntry::AutoTable headTable(entry,
+ TRUETYPE_TAG('h','e','a','d'));
+ if (!headTable) {
+ return nullptr;
+ }
+ const HeadTable* head =
+ reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
+ &len));
+ if (len < sizeof(HeadTable)) {
+ return nullptr;
+ }
+ mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
+ mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
+ mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
+ }
+
+ if (!mLocaTable || !mGlyfTable) {
+ // it's not a truetype font
+ return nullptr;
+ }
+
+ uint32_t offset; // offset of glyph record in the 'glyf' table
+ uint32_t len;
+ const char* data = hb_blob_get_data(mLocaTable, &len);
+ if (mLocaLongOffsets) {
+ if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
+ return nullptr;
+ }
+ const AutoSwap_PRUint32* offsets =
+ reinterpret_cast<const AutoSwap_PRUint32*>(data);
+ offset = offsets[aGlyph];
+ *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+ } else {
+ if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
+ return nullptr;
+ }
+ const AutoSwap_PRUint16* offsets =
+ reinterpret_cast<const AutoSwap_PRUint16*>(data);
+ offset = uint16_t(offsets[aGlyph]);
+ *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+ offset *= 2;
+ }
+
+ data = hb_blob_get_data(mGlyfTable, &len);
+ if (offset + sizeof(Glyf) > len) {
+ return nullptr;
+ }
+
+ return reinterpret_cast<const Glyf*>(data + offset);
+}
+
+hb_bool_t
+gfxHarfBuzzShaper::GetGlyphExtents(hb_codepoint_t aGlyph,
+ hb_glyph_extents_t *aExtents) const
+{
+ bool emptyGlyf;
+ const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+ if (!glyf) {
+ // TODO: for non-truetype fonts, get extents some other way?
+ return false;
+ }
+
+ if (emptyGlyf) {
+ aExtents->x_bearing = 0;
+ aExtents->y_bearing = 0;
+ aExtents->width = 0;
+ aExtents->height = 0;
+ return true;
+ }
+
+ double f = mFont->FUnitsToDevUnitsFactor();
+ aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
+ aExtents->width =
+ FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
+
+ // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
+ // positive-upwards; hence the apparently-reversed subtractions here.
+ aExtents->y_bearing =
+ FloatToFixed(int16_t(glyf->yMax) * f -
+ mFont->GetHorizontalMetrics().emAscent);
+ aExtents->height =
+ FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
+
+ return true;
+}
+
+static hb_bool_t
+HBGetContourPoint(hb_font_t *font, void *font_data,
+ unsigned int point_index, hb_codepoint_t glyph,
+ hb_position_t *x, hb_position_t *y,
+ void *user_data)
+{
+ /* not yet implemented - no support for used of hinted contour points
+ to fine-tune anchor positions in GPOS AnchorFormat2 */
+ return false;
+}
+
+struct KernHeaderFmt0 {
+ AutoSwap_PRUint16 nPairs;
+ AutoSwap_PRUint16 searchRange;
+ AutoSwap_PRUint16 entrySelector;
+ AutoSwap_PRUint16 rangeShift;
+};
+
+struct KernPair {
+ AutoSwap_PRUint16 left;
+ AutoSwap_PRUint16 right;
+ AutoSwap_PRInt16 value;
+};
+
+// Find a kern pair in a Format 0 subtable.
+// The aSubtable parameter points to the subtable itself, NOT its header,
+// as the header structure differs between Windows and Mac (v0 and v1.0)
+// versions of the 'kern' table.
+// aSubtableLen is the length of the subtable EXCLUDING its header.
+// If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
+// added to aValue, so that multiple subtables can accumulate a total
+// kerning value for a given pair.
+static void
+GetKernValueFmt0(const void* aSubtable,
+ uint32_t aSubtableLen,
+ uint16_t aFirstGlyph,
+ uint16_t aSecondGlyph,
+ int32_t& aValue,
+ bool aIsOverride = false,
+ bool aIsMinimum = false)
+{
+ const KernHeaderFmt0* hdr =
+ reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
+
+ const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
+ const KernPair *hi = lo + uint16_t(hdr->nPairs);
+ const KernPair *limit = hi;
+
+ if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
+ reinterpret_cast<const char*>(hi)) {
+ // subtable is not large enough to contain the claimed number
+ // of kern pairs, so just ignore it
+ return;
+ }
+
+#define KERN_PAIR_KEY(l,r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
+
+ uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
+ while (lo < hi) {
+ const KernPair *mid = lo + (hi - lo) / 2;
+ if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+
+ if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
+ if (aIsOverride) {
+ aValue = int16_t(lo->value);
+ } else if (aIsMinimum) {
+ aValue = std::max(aValue, int32_t(lo->value));
+ } else {
+ aValue += int16_t(lo->value);
+ }
+ }
+}
+
+// Get kerning value from Apple (version 1.0) kern table,
+// subtable format 2 (simple N x M array of kerning values)
+
+// See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
+// for details of version 1.0 format 2 subtable.
+
+struct KernHeaderVersion1Fmt2 {
+ KernTableSubtableHeaderVersion1 header;
+ AutoSwap_PRUint16 rowWidth;
+ AutoSwap_PRUint16 leftOffsetTable;
+ AutoSwap_PRUint16 rightOffsetTable;
+ AutoSwap_PRUint16 array;
+};
+
+struct KernClassTableHdr {
+ AutoSwap_PRUint16 firstGlyph;
+ AutoSwap_PRUint16 nGlyphs;
+ AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries
+};
+
+static int16_t
+GetKernValueVersion1Fmt2(const void* aSubtable,
+ uint32_t aSubtableLen,
+ uint16_t aFirstGlyph,
+ uint16_t aSecondGlyph)
+{
+ if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
+ return 0;
+ }
+
+ const char* base = reinterpret_cast<const char*>(aSubtable);
+ const char* subtableEnd = base + aSubtableLen;
+
+ const KernHeaderVersion1Fmt2* h =
+ reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
+ uint32_t offset = h->array;
+
+ const KernClassTableHdr* leftClassTable =
+ reinterpret_cast<const KernClassTableHdr*>(base +
+ uint16_t(h->leftOffsetTable));
+ if (reinterpret_cast<const char*>(leftClassTable) +
+ sizeof(KernClassTableHdr) > subtableEnd) {
+ return 0;
+ }
+ if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
+ aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
+ if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
+ if (reinterpret_cast<const char*>(leftClassTable) +
+ sizeof(KernClassTableHdr) +
+ aFirstGlyph * sizeof(uint16_t) >= subtableEnd) {
+ return 0;
+ }
+ offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
+ }
+ }
+
+ const KernClassTableHdr* rightClassTable =
+ reinterpret_cast<const KernClassTableHdr*>(base +
+ uint16_t(h->rightOffsetTable));
+ if (reinterpret_cast<const char*>(rightClassTable) +
+ sizeof(KernClassTableHdr) > subtableEnd) {
+ return 0;
+ }
+ if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
+ aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
+ if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
+ if (reinterpret_cast<const char*>(rightClassTable) +
+ sizeof(KernClassTableHdr) +
+ aSecondGlyph * sizeof(uint16_t) >= subtableEnd) {
+ return 0;
+ }
+ offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
+ }
+ }
+
+ const AutoSwap_PRInt16* pval =
+ reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
+ if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
+ return 0;
+ }
+ return *pval;
+}
+
+// Get kerning value from Apple (version 1.0) kern table,
+// subtable format 3 (simple N x M array of kerning values)
+
+// See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
+// for details of version 1.0 format 3 subtable.
+
+struct KernHeaderVersion1Fmt3 {
+ KernTableSubtableHeaderVersion1 header;
+ AutoSwap_PRUint16 glyphCount;
+ uint8_t kernValueCount;
+ uint8_t leftClassCount;
+ uint8_t rightClassCount;
+ uint8_t flags;
+};
+
+static int16_t
+GetKernValueVersion1Fmt3(const void* aSubtable,
+ uint32_t aSubtableLen,
+ uint16_t aFirstGlyph,
+ uint16_t aSecondGlyph)
+{
+ // check that we can safely read the header fields
+ if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
+ return 0;
+ }
+
+ const KernHeaderVersion1Fmt3* hdr =
+ reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
+ if (hdr->flags != 0) {
+ return 0;
+ }
+
+ uint16_t glyphCount = hdr->glyphCount;
+
+ // check that table is large enough for the arrays
+ if (sizeof(KernHeaderVersion1Fmt3) +
+ hdr->kernValueCount * sizeof(int16_t) +
+ glyphCount + glyphCount +
+ hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) {
+ return 0;
+ }
+
+ if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
+ // glyphs are out of range for the class tables
+ return 0;
+ }
+
+ // get pointers to the four arrays within the subtable
+ const AutoSwap_PRInt16* kernValue =
+ reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
+ const uint8_t* leftClass =
+ reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
+ const uint8_t* rightClass = leftClass + glyphCount;
+ const uint8_t* kernIndex = rightClass + glyphCount;
+
+ uint8_t lc = leftClass[aFirstGlyph];
+ uint8_t rc = rightClass[aSecondGlyph];
+ if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
+ return 0;
+ }
+
+ uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
+ rightClass[aSecondGlyph]];
+ if (ki >= hdr->kernValueCount) {
+ return 0;
+ }
+
+ return kernValue[ki];
+}
+
+#define KERN0_COVERAGE_HORIZONTAL 0x0001
+#define KERN0_COVERAGE_MINIMUM 0x0002
+#define KERN0_COVERAGE_CROSS_STREAM 0x0004
+#define KERN0_COVERAGE_OVERRIDE 0x0008
+#define KERN0_COVERAGE_RESERVED 0x00F0
+
+#define KERN1_COVERAGE_VERTICAL 0x8000
+#define KERN1_COVERAGE_CROSS_STREAM 0x4000
+#define KERN1_COVERAGE_VARIATION 0x2000
+#define KERN1_COVERAGE_RESERVED 0x1F00
+
+hb_position_t
+gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
+ uint16_t aSecondGlyph) const
+{
+ // We want to ignore any kern pairs involving <space>, because we are
+ // handling words in isolation, the only space characters seen here are
+ // the ones artificially added by the textRun code.
+ uint32_t spaceGlyph = mFont->GetSpaceGlyph();
+ if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
+ return 0;
+ }
+
+ if (!mKernTable) {
+ mKernTable = mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
+ if (!mKernTable) {
+ mKernTable = hb_blob_get_empty();
+ }
+ }
+
+ uint32_t len;
+ const char* base = hb_blob_get_data(mKernTable, &len);
+ if (len < sizeof(KernTableVersion0)) {
+ return 0;
+ }
+ int32_t value = 0;
+
+ // First try to interpret as "version 0" kern table
+ // (see http://www.microsoft.com/typography/otspec/kern.htm)
+ const KernTableVersion0* kern0 =
+ reinterpret_cast<const KernTableVersion0*>(base);
+ if (uint16_t(kern0->version) == 0) {
+ uint16_t nTables = kern0->nTables;
+ uint32_t offs = sizeof(KernTableVersion0);
+ for (uint16_t i = 0; i < nTables; ++i) {
+ if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
+ break;
+ }
+ const KernTableSubtableHeaderVersion0* st0 =
+ reinterpret_cast<const KernTableSubtableHeaderVersion0*>
+ (base + offs);
+ uint16_t subtableLen = uint16_t(st0->length);
+ if (offs + subtableLen > len) {
+ break;
+ }
+ offs += subtableLen;
+ uint16_t coverage = st0->coverage;
+ if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
+ // we only care about horizontal kerning (for now)
+ continue;
+ }
+ if (coverage &
+ (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
+ // we don't support cross-stream kerning, and
+ // reserved bits should be zero;
+ // ignore the subtable if not
+ continue;
+ }
+ uint8_t format = (coverage >> 8);
+ switch (format) {
+ case 0:
+ GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0),
+ aFirstGlyph, aSecondGlyph, value,
+ (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
+ (coverage & KERN0_COVERAGE_MINIMUM) != 0);
+ break;
+ default:
+ // TODO: implement support for other formats,
+ // if they're ever used in practice
+#if DEBUG
+ {
+ char buf[1024];
+ SprintfLiteral(buf, "unknown kern subtable in %s: "
+ "ver 0 format %d\n",
+ NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
+ format);
+ NS_WARNING(buf);
+ }
+#endif
+ break;
+ }
+ }
+ } else {
+ // It wasn't a "version 0" table; check if it is Apple version 1.0
+ // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
+ const KernTableVersion1* kern1 =
+ reinterpret_cast<const KernTableVersion1*>(base);
+ if (uint32_t(kern1->version) == 0x00010000) {
+ uint32_t nTables = kern1->nTables;
+ uint32_t offs = sizeof(KernTableVersion1);
+ for (uint32_t i = 0; i < nTables; ++i) {
+ if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
+ break;
+ }
+ const KernTableSubtableHeaderVersion1* st1 =
+ reinterpret_cast<const KernTableSubtableHeaderVersion1*>
+ (base + offs);
+ uint32_t subtableLen = uint32_t(st1->length);
+ offs += subtableLen;
+ uint16_t coverage = st1->coverage;
+ if (coverage &
+ (KERN1_COVERAGE_VERTICAL |
+ KERN1_COVERAGE_CROSS_STREAM |
+ KERN1_COVERAGE_VARIATION |
+ KERN1_COVERAGE_RESERVED)) {
+ // we only care about horizontal kerning (for now),
+ // we don't support cross-stream kerning,
+ // we don't support variations,
+ // reserved bits should be zero;
+ // ignore the subtable if not
+ continue;
+ }
+ uint8_t format = (coverage & 0xff);
+ switch (format) {
+ case 0:
+ GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1),
+ aFirstGlyph, aSecondGlyph, value);
+ break;
+ case 2:
+ value = GetKernValueVersion1Fmt2(st1, subtableLen,
+ aFirstGlyph, aSecondGlyph);
+ break;
+ case 3:
+ value = GetKernValueVersion1Fmt3(st1, subtableLen,
+ aFirstGlyph, aSecondGlyph);
+ break;
+ default:
+ // TODO: implement support for other formats.
+ // Note that format 1 cannot be supported here,
+ // as it requires the full glyph array to run the FSM,
+ // not just the current glyph pair.
+#if DEBUG
+ {
+ char buf[1024];
+ SprintfLiteral(buf, "unknown kern subtable in %s: "
+ "ver 0 format %d\n",
+ NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
+ format);
+ NS_WARNING(buf);
+ }
+#endif
+ break;
+ }
+ }
+ }
+ }
+
+ if (value != 0) {
+ return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
+ }
+ return 0;
+}
+
+static hb_position_t
+HBGetHKerning(hb_font_t *font, void *font_data,
+ hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+ void *user_data)
+{
+ const gfxHarfBuzzShaper::FontCallbackData *fcd =
+ static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+ return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
+}
+
+/*
+ * HarfBuzz unicode property callbacks
+ */
+
+static hb_codepoint_t
+HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
+ void *user_data)
+{
+ return GetMirroredChar(aCh);
+}
+
+static hb_unicode_general_category_t
+HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
+ void *user_data)
+{
+ return hb_unicode_general_category_t(GetGeneralCategory(aCh));
+}
+
+static hb_script_t
+HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
+{
+ return hb_script_t(GetScriptTagForCode(GetScriptCode(aCh)));
+}
+
+static hb_unicode_combining_class_t
+HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
+ void *user_data)
+{
+ return hb_unicode_combining_class_t(GetCombiningClass(aCh));
+}
+
+// Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+// note that some letters do not have a dagesh presForm encoded
+static const char16_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+ 0xFB30, // ALEF
+ 0xFB31, // BET
+ 0xFB32, // GIMEL
+ 0xFB33, // DALET
+ 0xFB34, // HE
+ 0xFB35, // VAV
+ 0xFB36, // ZAYIN
+ 0, // HET
+ 0xFB38, // TET
+ 0xFB39, // YOD
+ 0xFB3A, // FINAL KAF
+ 0xFB3B, // KAF
+ 0xFB3C, // LAMED
+ 0, // FINAL MEM
+ 0xFB3E, // MEM
+ 0, // FINAL NUN
+ 0xFB40, // NUN
+ 0xFB41, // SAMEKH
+ 0, // AYIN
+ 0xFB43, // FINAL PE
+ 0xFB44, // PE
+ 0, // FINAL TSADI
+ 0xFB46, // TSADI
+ 0xFB47, // QOF
+ 0xFB48, // RESH
+ 0xFB49, // SHIN
+ 0xFB4A // TAV
+};
+
+static hb_bool_t
+HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t a,
+ hb_codepoint_t b,
+ hb_codepoint_t *ab,
+ void *user_data)
+{
+ if (sNormalizer) {
+ UChar32 ch = unorm2_composePair(sNormalizer, a, b);
+ if (ch >= 0) {
+ *ab = ch;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static hb_bool_t
+HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t ab,
+ hb_codepoint_t *a,
+ hb_codepoint_t *b,
+ void *user_data)
+{
+ if (!sNormalizer) {
+ return false;
+ }
+
+ // Canonical decompositions are never more than two characters,
+ // or a maximum of 4 utf-16 code units.
+ const unsigned MAX_DECOMP_LENGTH = 4;
+
+ UErrorCode error = U_ZERO_ERROR;
+ UChar decomp[MAX_DECOMP_LENGTH];
+ int32_t len = unorm2_getRawDecomposition(sNormalizer, ab, decomp,
+ MAX_DECOMP_LENGTH, &error);
+ if (U_FAILURE(error) || len < 0) {
+ return false;
+ }
+
+ UText text = UTEXT_INITIALIZER;
+ utext_openUChars(&text, decomp, len, &error);
+ NS_ASSERTION(U_SUCCESS(error), "UText failure?");
+
+ UChar32 ch = UTEXT_NEXT32(&text);
+ if (ch != U_SENTINEL) {
+ *a = ch;
+ }
+ ch = UTEXT_NEXT32(&text);
+ if (ch != U_SENTINEL) {
+ *b = ch;
+ }
+ utext_close(&text);
+
+ return *b != 0 || *a != ab;
+}
+
+static void
+AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
+{
+ nsTArray<hb_feature_t>* features = static_cast<nsTArray<hb_feature_t>*> (aUserArg);
+
+ hb_feature_t feat = { 0, 0, 0, UINT_MAX };
+ feat.tag = aTag;
+ feat.value = aValue;
+ features->AppendElement(feat);
+}
+
+/*
+ * gfxFontShaper override to initialize the text run using HarfBuzz
+ */
+
+static hb_font_funcs_t * sHBFontFuncs = nullptr;
+static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
+static const hb_script_t sMathScript =
+ hb_ot_tag_to_script(HB_TAG('m','a','t','h'));
+
+bool
+gfxHarfBuzzShaper::Initialize()
+{
+ if (mInitialized) {
+ return mHBFont != nullptr;
+ }
+ mInitialized = true;
+ mCallbackData.mShaper = this;
+
+ mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
+
+ if (!sHBFontFuncs) {
+ // static function callback pointers, initialized by the first
+ // harfbuzz shaper used
+ sHBFontFuncs = hb_font_funcs_create();
+ hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs,
+ HBGetNominalGlyph,
+ nullptr, nullptr);
+ hb_font_funcs_set_variation_glyph_func(sHBFontFuncs,
+ HBGetVariationGlyph,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
+ HBGetGlyphHAdvance,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs,
+ HBGetGlyphVAdvance,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
+ HBGetGlyphVOrigin,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func(sHBFontFuncs,
+ HBGetGlyphExtents,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
+ HBGetContourPoint,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
+ HBGetHKerning,
+ nullptr, nullptr);
+
+ sHBUnicodeFuncs =
+ hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
+ hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
+ HBGetMirroring,
+ nullptr, nullptr);
+ hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
+ nullptr, nullptr);
+ hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
+ HBGetGeneralCategory,
+ nullptr, nullptr);
+ hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
+ HBGetCombiningClass,
+ nullptr, nullptr);
+ hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
+ HBUnicodeCompose,
+ nullptr, nullptr);
+ hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
+ HBUnicodeDecompose,
+ nullptr, nullptr);
+
+ UErrorCode error = U_ZERO_ERROR;
+ sNormalizer = unorm2_getNFCInstance(&error);
+ MOZ_ASSERT(U_SUCCESS(error), "failed to get ICU normalizer");
+ }
+
+ gfxFontEntry *entry = mFont->GetFontEntry();
+ if (!mUseFontGetGlyph) {
+ // get the cmap table and find offset to our subtable
+ mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
+ if (!mCmapTable) {
+ NS_WARNING("failed to load cmap, glyphs will be missing");
+ return false;
+ }
+ uint32_t len;
+ const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
+ bool symbol;
+ mCmapFormat = gfxFontUtils::
+ FindPreferredSubtable(data, len,
+ &mSubtableOffset, &mUVSTableOffset,
+ &symbol);
+ if (mCmapFormat <= 0) {
+ return false;
+ }
+ }
+
+ if (!mUseFontGlyphWidths) {
+ // If font doesn't implement GetGlyphWidth, we will be reading
+ // the metrics table directly, so make sure we can load it.
+ if (!LoadHmtxTable()) {
+ return false;
+ }
+ }
+
+ mHBFont = hb_font_create(mHBFace);
+ hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
+ hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
+ uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
+ hb_font_set_scale(mHBFont, scale, scale);
+
+ return true;
+}
+
+bool
+gfxHarfBuzzShaper::LoadHmtxTable()
+{
+ // Read mNumLongHMetrics from metrics-head table without caching its
+ // blob, and preload/cache the metrics table.
+ gfxFontEntry *entry = mFont->GetFontEntry();
+ gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
+ if (hheaTable) {
+ uint32_t len;
+ const MetricsHeader* hhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(hheaTable, &len));
+ if (len >= sizeof(MetricsHeader)) {
+ mNumLongHMetrics = hhea->numOfLongMetrics;
+ if (mNumLongHMetrics > 0 &&
+ int16_t(hhea->metricDataFormat) == 0) {
+ // no point reading metrics if number of entries is zero!
+ // in that case, we won't be able to use this font
+ // (this method will return FALSE below if mHmtxTable
+ // is null)
+ mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
+ if (mHmtxTable && hb_blob_get_length(mHmtxTable) <
+ mNumLongHMetrics * sizeof(LongMetric)) {
+ // metrics table is not large enough for the claimed
+ // number of entries: invalid, do not use.
+ hb_blob_destroy(mHmtxTable);
+ mHmtxTable = nullptr;
+ }
+ }
+ }
+ }
+ if (!mHmtxTable) {
+ return false;
+ }
+ return true;
+}
+
+bool
+gfxHarfBuzzShaper::InitializeVertical()
+{
+ // We only try this once. If we don't have a mHmtxTable after that,
+ // this font can't handle vertical shaping, so return false.
+ if (mVerticalInitialized) {
+ return mHmtxTable != nullptr;
+ }
+ mVerticalInitialized = true;
+
+ if (!mHmtxTable) {
+ if (!LoadHmtxTable()) {
+ return false;
+ }
+ }
+
+ // Load vertical metrics if present in the font; if not, we'll synthesize
+ // vertical glyph advances based on (horizontal) ascent/descent metrics.
+ gfxFontEntry *entry = mFont->GetFontEntry();
+ gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v','h','e','a'));
+ if (vheaTable) {
+ uint32_t len;
+ const MetricsHeader* vhea =
+ reinterpret_cast<const MetricsHeader*>
+ (hb_blob_get_data(vheaTable, &len));
+ if (len >= sizeof(MetricsHeader)) {
+ mNumLongVMetrics = vhea->numOfLongMetrics;
+ gfxFontEntry::AutoTable
+ maxpTable(entry, TRUETYPE_TAG('m','a','x','p'));
+ int numGlyphs = -1; // invalid if we fail to read 'maxp'
+ if (maxpTable &&
+ hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) {
+ const MaxpTableHeader* maxp =
+ reinterpret_cast<const MaxpTableHeader*>
+ (hb_blob_get_data(maxpTable, nullptr));
+ numGlyphs = uint16_t(maxp->numGlyphs);
+ }
+ if (mNumLongVMetrics > 0 && mNumLongVMetrics <= numGlyphs &&
+ int16_t(vhea->metricDataFormat) == 0) {
+ mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v','m','t','x'));
+ if (mVmtxTable && hb_blob_get_length(mVmtxTable) <
+ mNumLongVMetrics * sizeof(LongMetric) +
+ (numGlyphs - mNumLongVMetrics) * sizeof(int16_t)) {
+ // metrics table is not large enough for the claimed
+ // number of entries: invalid, do not use.
+ hb_blob_destroy(mVmtxTable);
+ mVmtxTable = nullptr;
+ }
+ }
+ }
+ }
+
+ // For CFF fonts only, load a VORG table if present.
+ if (entry->HasFontTable(TRUETYPE_TAG('C','F','F',' '))) {
+ mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V','O','R','G'));
+ if (mVORGTable) {
+ uint32_t len;
+ const VORG* vorg =
+ reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable,
+ &len));
+ if (len < sizeof(VORG) ||
+ uint16_t(vorg->majorVersion) != 1 ||
+ uint16_t(vorg->minorVersion) != 0 ||
+ len < sizeof(VORG) + uint16_t(vorg->numVertOriginYMetrics) *
+ sizeof(VORGrec)) {
+ // VORG table is an unknown version, or not large enough
+ // to be valid -- discard it.
+ NS_WARNING("discarding invalid VORG table");
+ hb_blob_destroy(mVORGTable);
+ mVORGTable = nullptr;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ // some font back-ends require this in order to get proper hinted metrics
+ if (!mFont->SetupCairoFont(aDrawTarget)) {
+ return false;
+ }
+
+ mCallbackData.mDrawTarget = aDrawTarget;
+ mUseVerticalPresentationForms = false;
+
+ if (!Initialize()) {
+ return false;
+ }
+
+ if (aVertical) {
+ if (!InitializeVertical()) {
+ return false;
+ }
+ if (!mFont->GetFontEntry()->
+ SupportsOpenTypeFeature(aScript, HB_TAG('v','e','r','t'))) {
+ mUseVerticalPresentationForms = true;
+ }
+ }
+
+ const gfxFontStyle *style = mFont->GetStyle();
+
+ // determine whether petite-caps falls back to small-caps
+ bool addSmallCaps = false;
+ if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
+ switch (style->variantCaps) {
+ case NS_FONT_VARIANT_CAPS_ALLPETITE:
+ case NS_FONT_VARIANT_CAPS_PETITECAPS:
+ bool synLower, synUpper;
+ mFont->SupportsVariantCaps(aScript, style->variantCaps,
+ addSmallCaps, synLower, synUpper);
+ break;
+ default:
+ break;
+ }
+ }
+
+ gfxFontEntry *entry = mFont->GetFontEntry();
+
+ // insert any merged features into hb_feature array
+ AutoTArray<hb_feature_t,20> features;
+ MergeFontFeatures(style,
+ entry->mFeatureSettings,
+ aShapedText->DisableLigatures(),
+ entry->FamilyName(),
+ addSmallCaps,
+ AddOpenTypeFeature,
+ &features);
+
+ bool isRightToLeft = aShapedText->IsRightToLeft();
+ hb_buffer_t *buffer = hb_buffer_create();
+ hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
+
+ hb_buffer_set_direction(buffer,
+ aVertical ? HB_DIRECTION_TTB :
+ (isRightToLeft ? HB_DIRECTION_RTL :
+ HB_DIRECTION_LTR));
+ hb_script_t scriptTag;
+ if (aShapedText->GetFlags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
+ scriptTag = sMathScript;
+ } else {
+ scriptTag = GetHBScriptUsedForShaping(aScript);
+ }
+ hb_buffer_set_script(buffer, scriptTag);
+
+ hb_language_t language;
+ if (style->languageOverride) {
+ language = hb_ot_tag_to_language(style->languageOverride);
+ } else if (entry->mLanguageOverride) {
+ language = hb_ot_tag_to_language(entry->mLanguageOverride);
+ } else if (style->explicitLanguage) {
+ nsCString langString;
+ style->language->ToUTF8String(langString);
+ language =
+ hb_language_from_string(langString.get(), langString.Length());
+ } else {
+ language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
+ }
+ hb_buffer_set_language(buffer, language);
+
+ uint32_t length = aLength;
+ hb_buffer_add_utf16(buffer,
+ reinterpret_cast<const uint16_t*>(aText),
+ length, 0, length);
+
+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
+
+ hb_shape(mHBFont, buffer, features.Elements(), features.Length());
+
+ if (isRightToLeft) {
+ hb_buffer_reverse(buffer);
+ }
+
+ nsresult rv = SetGlyphsFromRun(aDrawTarget, aShapedText, aOffset, aLength,
+ aText, buffer, aVertical);
+
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "failed to store glyphs into gfxShapedWord");
+ hb_buffer_destroy(buffer);
+
+ return NS_SUCCEEDED(rv);
+}
+
+#define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
+ // will fit without requiring separate allocation
+ // for charToGlyphArray
+
+nsresult
+gfxHarfBuzzShaper::SetGlyphsFromRun(DrawTarget *aDrawTarget,
+ gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ const char16_t *aText,
+ hb_buffer_t *aBuffer,
+ bool aVertical)
+{
+ uint32_t numGlyphs;
+ const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
+ if (numGlyphs == 0) {
+ return NS_OK;
+ }
+
+ AutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
+
+ uint32_t wordLength = aLength;
+ static const int32_t NO_GLYPH = -1;
+ AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
+ if (!charToGlyphArray.SetLength(wordLength, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t *charToGlyph = charToGlyphArray.Elements();
+ for (uint32_t offset = 0; offset < wordLength; ++offset) {
+ charToGlyph[offset] = NO_GLYPH;
+ }
+
+ for (uint32_t i = 0; i < numGlyphs; ++i) {
+ uint32_t loc = ginfo[i].cluster;
+ if (loc < wordLength) {
+ charToGlyph[loc] = i;
+ }
+ }
+
+ int32_t glyphStart = 0; // looking for a clump that starts at this glyph
+ int32_t charStart = 0; // and this char index within the range of the run
+
+ bool roundI, roundB;
+ if (aVertical) {
+ GetRoundOffsetsToPixels(aDrawTarget, &roundB, &roundI);
+ } else {
+ GetRoundOffsetsToPixels(aDrawTarget, &roundI, &roundB);
+ }
+
+ int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+ gfxShapedText::CompressedGlyph *charGlyphs =
+ aShapedText->GetCharacterGlyphs() + aOffset;
+
+ // factor to convert 16.16 fixed-point pixels to app units
+ // (only used if not rounding)
+ double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
+
+ // Residual from rounding of previous advance, for use in rounding the
+ // subsequent offset or advance appropriately. 16.16 fixed-point
+ //
+ // When rounding, the goal is to make the distance between glyphs and
+ // their base glyph equal to the integral number of pixels closest to that
+ // suggested by that shaper.
+ // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
+ //
+ // The value of the residual is the part of the desired distance that has
+ // not been included in integer offsets.
+ hb_position_t residual = 0;
+
+ // keep track of y-position to set glyph offsets if needed
+ nscoord bPos = 0;
+
+ const hb_glyph_position_t *posInfo =
+ hb_buffer_get_glyph_positions(aBuffer, nullptr);
+
+ while (glyphStart < int32_t(numGlyphs)) {
+
+ int32_t charEnd = ginfo[glyphStart].cluster;
+ int32_t glyphEnd = glyphStart;
+ int32_t charLimit = wordLength;
+ while (charEnd < charLimit) {
+ // This is normally executed once for each iteration of the outer loop,
+ // but in unusual cases where the character/glyph association is complex,
+ // the initial character range might correspond to a non-contiguous
+ // glyph range with "holes" in it. If so, we will repeat this loop to
+ // extend the character range until we have a contiguous glyph sequence.
+ charEnd += 1;
+ while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
+ charEnd += 1;
+ }
+
+ // find the maximum glyph index covered by the clump so far
+ for (int32_t i = charStart; i < charEnd; ++i) {
+ if (charToGlyph[i] != NO_GLYPH) {
+ glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
+ // update extent of glyph range
+ }
+ }
+
+ if (glyphEnd == glyphStart + 1) {
+ // for the common case of a single-glyph clump,
+ // we can skip the following checks
+ break;
+ }
+
+ if (glyphEnd == glyphStart) {
+ // no glyphs, try to extend the clump
+ continue;
+ }
+
+ // check whether all glyphs in the range are associated with the characters
+ // in our clump; if not, we have a discontinuous range, and should extend it
+ // unless we've reached the end of the text
+ bool allGlyphsAreWithinCluster = true;
+ for (int32_t i = glyphStart; i < glyphEnd; ++i) {
+ int32_t glyphCharIndex = ginfo[i].cluster;
+ if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
+ allGlyphsAreWithinCluster = false;
+ break;
+ }
+ }
+ if (allGlyphsAreWithinCluster) {
+ break;
+ }
+ }
+
+ NS_ASSERTION(glyphStart < glyphEnd,
+ "character/glyph clump contains no glyphs!");
+ NS_ASSERTION(charStart != charEnd,
+ "character/glyph clump contains no characters!");
+
+ // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
+ // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
+ // and endCharIndex to the limit (position beyond the last char),
+ // adjusting for the offset of the stringRange relative to the textRun.
+ int32_t baseCharIndex, endCharIndex;
+ while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
+ charEnd++;
+ baseCharIndex = charStart;
+ endCharIndex = charEnd;
+
+ // Then we check if the clump falls outside our actual string range;
+ // if so, just go to the next.
+ if (baseCharIndex >= int32_t(wordLength)) {
+ glyphStart = glyphEnd;
+ charStart = charEnd;
+ continue;
+ }
+ // Ensure we won't try to go beyond the valid length of the textRun's text
+ endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
+
+ // Now we're ready to set the glyph info in the textRun
+ int32_t glyphsInClump = glyphEnd - glyphStart;
+
+ // Check for default-ignorable char that didn't get filtered, combined,
+ // etc by the shaping process, and remove from the run.
+ // (This may be done within harfbuzz eventually.)
+ if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
+ aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
+ aText[baseCharIndex])) {
+ glyphStart = glyphEnd;
+ charStart = charEnd;
+ continue;
+ }
+
+ // HarfBuzz gives us physical x- and y-coordinates, but we will store
+ // them as logical inline- and block-direction values in the textrun.
+
+ hb_position_t i_offset, i_advance; // inline-direction offset/advance
+ hb_position_t b_offset, b_advance; // block-direction offset/advance
+ if (aVertical) {
+ i_offset = posInfo[glyphStart].y_offset;
+ i_advance = posInfo[glyphStart].y_advance;
+ b_offset = posInfo[glyphStart].x_offset;
+ b_advance = posInfo[glyphStart].x_advance;
+ } else {
+ i_offset = posInfo[glyphStart].x_offset;
+ i_advance = posInfo[glyphStart].x_advance;
+ b_offset = posInfo[glyphStart].y_offset;
+ b_advance = posInfo[glyphStart].y_advance;
+ }
+
+ nscoord iOffset, advance;
+ if (roundI) {
+ iOffset =
+ appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
+ // Desired distance from the base glyph to the next reference point.
+ hb_position_t width = i_advance - i_offset;
+ int intWidth = FixedToIntRound(width);
+ residual = width - FloatToFixed(intWidth);
+ advance = appUnitsPerDevUnit * intWidth + iOffset;
+ } else {
+ iOffset = floor(hb2appUnits * i_offset + 0.5);
+ advance = floor(hb2appUnits * i_advance + 0.5);
+ }
+ // Check if it's a simple one-to-one mapping
+ if (glyphsInClump == 1 &&
+ gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
+ gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
+ charGlyphs[baseCharIndex].IsClusterStart() &&
+ iOffset == 0 && b_offset == 0 &&
+ b_advance == 0 && bPos == 0)
+ {
+ charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
+ ginfo[glyphStart].codepoint);
+ } else {
+ // Collect all glyphs in a list to be assigned to the first char;
+ // there must be at least one in the clump, and we already measured
+ // its advance, hence the placement of the loop-exit test and the
+ // measurement of the next glyph.
+ // For vertical orientation, we add a "base offset" to compensate
+ // for the positioning within the cluster being based on horizontal
+ // glyph origin/offset.
+ hb_position_t baseIOffset, baseBOffset;
+ if (aVertical) {
+ baseIOffset = 2 * (i_offset - i_advance);
+ baseBOffset = GetGlyphHAdvance(ginfo[glyphStart].codepoint);
+ }
+ while (1) {
+ gfxTextRun::DetailedGlyph* details =
+ detailedGlyphs.AppendElement();
+ details->mGlyphID = ginfo[glyphStart].codepoint;
+
+ details->mXOffset = iOffset;
+ details->mAdvance = advance;
+
+ details->mYOffset = bPos -
+ (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
+ : floor(hb2appUnits * b_offset + 0.5));
+
+ if (b_advance != 0) {
+ bPos -=
+ roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
+ : floor(hb2appUnits * b_advance + 0.5);
+ }
+ if (++glyphStart >= glyphEnd) {
+ break;
+ }
+
+ if (aVertical) {
+ i_offset = baseIOffset - posInfo[glyphStart].y_offset;
+ i_advance = posInfo[glyphStart].y_advance;
+ b_offset = baseBOffset - posInfo[glyphStart].x_offset;
+ b_advance = posInfo[glyphStart].x_advance;
+ } else {
+ i_offset = posInfo[glyphStart].x_offset;
+ i_advance = posInfo[glyphStart].x_advance;
+ b_offset = posInfo[glyphStart].y_offset;
+ b_advance = posInfo[glyphStart].y_advance;
+ }
+
+ if (roundI) {
+ iOffset = appUnitsPerDevUnit *
+ FixedToIntRound(i_offset + residual);
+ // Desired distance to the next reference point. The
+ // residual is considered here, and includes the residual
+ // from the base glyph offset and subsequent advances, so
+ // that the distance from the base glyph is optimized
+ // rather than the distance from combining marks.
+ i_advance += residual;
+ int intAdvance = FixedToIntRound(i_advance);
+ residual = i_advance - FloatToFixed(intAdvance);
+ advance = appUnitsPerDevUnit * intAdvance;
+ } else {
+ iOffset = floor(hb2appUnits * i_offset + 0.5);
+ advance = floor(hb2appUnits * i_advance + 0.5);
+ }
+ }
+
+ gfxShapedText::CompressedGlyph g;
+ g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
+ true, detailedGlyphs.Length());
+ aShapedText->SetGlyphs(aOffset + baseCharIndex,
+ g, detailedGlyphs.Elements());
+
+ detailedGlyphs.Clear();
+ }
+
+ // the rest of the chars in the group are ligature continuations,
+ // no associated glyphs
+ while (++baseCharIndex != endCharIndex &&
+ baseCharIndex < int32_t(wordLength)) {
+ gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
+ NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+ g.SetComplex(g.IsClusterStart(), false, 0);
+ }
+
+ glyphStart = glyphEnd;
+ charStart = charEnd;
+ }
+
+ return NS_OK;
+}
diff --git a/system/graphics/thebes/gfxHarfBuzzShaper.h b/system/graphics/thebes/gfxHarfBuzzShaper.h
new file mode 100644
index 000000000..b4b61159f
--- /dev/null
+++ b/system/graphics/thebes/gfxHarfBuzzShaper.h
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_HARFBUZZSHAPER_H
+#define GFX_HARFBUZZSHAPER_H
+
+#include "gfxFont.h"
+
+#include "harfbuzz/hb.h"
+#include "nsUnicodeProperties.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace mozilla;
+
+class gfxHarfBuzzShaper : public gfxFontShaper {
+public:
+ explicit gfxHarfBuzzShaper(gfxFont *aFont);
+ virtual ~gfxHarfBuzzShaper();
+
+ /*
+ * For HarfBuzz font callback functions, font_data is a ptr to a
+ * FontCallbackData struct
+ */
+ struct FontCallbackData {
+ gfxHarfBuzzShaper* mShaper;
+ mozilla::gfx::DrawTarget* mDrawTarget;
+ };
+
+ bool Initialize();
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText);
+
+ // get a given font table in harfbuzz blob form
+ hb_blob_t * GetFontTable(hb_tag_t aTag) const;
+
+ // map unicode character to glyph ID
+ hb_codepoint_t GetNominalGlyph(hb_codepoint_t unicode) const;
+ hb_codepoint_t GetVariationGlyph(hb_codepoint_t unicode,
+ hb_codepoint_t variation_selector) const;
+
+ // get harfbuzz glyph advance, in font design units
+ hb_position_t GetGlyphHAdvance(hb_codepoint_t glyph) const;
+
+ hb_position_t GetGlyphVAdvance(hb_codepoint_t glyph) const;
+
+ void GetGlyphVOrigin(hb_codepoint_t aGlyph,
+ hb_position_t *aX, hb_position_t *aY) const;
+
+ // get harfbuzz horizontal advance in 16.16 fixed point format.
+ static hb_position_t
+ HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph, void *user_data);
+
+ // get harfbuzz vertical advance in 16.16 fixed point format.
+ static hb_position_t
+ HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph, void *user_data);
+
+ static hb_bool_t
+ HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_position_t *x, hb_position_t *y,
+ void *user_data);
+
+ hb_position_t GetHKerning(uint16_t aFirstGlyph,
+ uint16_t aSecondGlyph) const;
+
+ hb_bool_t GetGlyphExtents(hb_codepoint_t aGlyph,
+ hb_glyph_extents_t *aExtents) const;
+
+ bool UseVerticalPresentationForms() const
+ {
+ return mUseVerticalPresentationForms;
+ }
+
+ static hb_script_t
+ GetHBScriptUsedForShaping(Script aScript) {
+ // Decide what harfbuzz script code will be used for shaping
+ hb_script_t hbScript;
+ if (aScript <= Script::INHERITED) {
+ // For unresolved "common" or "inherited" runs,
+ // default to Latin for now.
+ hbScript = HB_SCRIPT_LATIN;
+ } else {
+ hbScript =
+ hb_script_t(mozilla::unicode::GetScriptTagForCode(aScript));
+ }
+ return hbScript;
+ }
+
+protected:
+ nsresult SetGlyphsFromRun(DrawTarget *aDrawTarget,
+ gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ const char16_t *aText,
+ hb_buffer_t *aBuffer,
+ bool aVertical);
+
+ // retrieve glyph positions, applying advance adjustments and attachments
+ // returns results in appUnits
+ nscoord GetGlyphPositions(gfxContext *aContext,
+ hb_buffer_t *aBuffer,
+ nsTArray<nsPoint>& aPositions,
+ uint32_t aAppUnitsPerDevUnit);
+
+ bool InitializeVertical();
+ bool LoadHmtxTable();
+
+ struct Glyf { // we only need the bounding-box at the beginning
+ // of the glyph record, not the actual outline data
+ AutoSwap_PRInt16 numberOfContours;
+ AutoSwap_PRInt16 xMin;
+ AutoSwap_PRInt16 yMin;
+ AutoSwap_PRInt16 xMax;
+ AutoSwap_PRInt16 yMax;
+ };
+
+ const Glyf *FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const;
+
+ // harfbuzz face object: we acquire a reference from the font entry
+ // on shaper creation, and release it in our destructor
+ hb_face_t *mHBFace;
+
+ // size-specific font object, owned by the gfxHarfBuzzShaper
+ hb_font_t *mHBFont;
+
+ FontCallbackData mCallbackData;
+
+ // Following table references etc are declared "mutable" because the
+ // harfbuzz callback functions take a const ptr to the shaper, but
+ // wish to cache tables here to avoid repeatedly looking them up
+ // in the font.
+
+ // Old-style TrueType kern table, if we're not doing GPOS kerning
+ mutable hb_blob_t *mKernTable;
+
+ // Cached copy of the hmtx table.
+ mutable hb_blob_t *mHmtxTable;
+
+ // For vertical fonts, cached vmtx and VORG table, if present.
+ mutable hb_blob_t *mVmtxTable;
+ mutable hb_blob_t *mVORGTable;
+ // And for vertical TrueType (not CFF) fonts that have vmtx,
+ // we also use loca and glyf to get glyph bounding boxes.
+ mutable hb_blob_t *mLocaTable;
+ mutable hb_blob_t *mGlyfTable;
+
+ // Cached pointer to cmap subtable to be used for char-to-glyph mapping.
+ // This comes from GetFontTablePtr; if it is non-null, our destructor
+ // must call ReleaseFontTablePtr to avoid permanently caching the table.
+ mutable hb_blob_t *mCmapTable;
+ mutable int32_t mCmapFormat;
+ mutable uint32_t mSubtableOffset;
+ mutable uint32_t mUVSTableOffset;
+
+ // Cached copy of numLongMetrics field from the hhea table,
+ // for use when looking up glyph metrics; initialized to 0 by the
+ // constructor so we can tell it hasn't been set yet.
+ // This is a signed value so that we can use -1 to indicate
+ // an error (if the hhea table was not available).
+ mutable int32_t mNumLongHMetrics;
+ // Similarly for vhea if it's a vertical font.
+ mutable int32_t mNumLongVMetrics;
+
+ // Whether the font implements GetGlyph, or we should read tables
+ // directly
+ bool mUseFontGetGlyph;
+ // Whether the font implements GetGlyphWidth, or we should read tables
+ // directly to get ideal widths
+ bool mUseFontGlyphWidths;
+
+ bool mInitialized;
+ bool mVerticalInitialized;
+
+ // Whether to use vertical presentation forms for CJK characters
+ // when available (only set if the 'vert' feature is not available).
+ bool mUseVerticalPresentationForms;
+
+ // these are set from the FindGlyf callback on first use of the glyf data
+ mutable bool mLoadedLocaGlyf;
+ mutable bool mLocaLongOffsets;
+};
+
+#endif /* GFX_HARFBUZZSHAPER_H */
diff --git a/system/graphics/thebes/gfxImageSurface.cpp b/system/graphics/thebes/gfxImageSurface.cpp
new file mode 100644
index 000000000..fe236892b
--- /dev/null
+++ b/system/graphics/thebes/gfxImageSurface.cpp
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "mozilla/MemoryReporting.h"
+#if defined(HAVE_POSIX_MEMALIGN)
+#include "gfxAlphaRecovery.h"
+#endif
+#include "gfxImageSurface.h"
+
+#include "cairo.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "gfx2DGlue.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxImageSurface::gfxImageSurface()
+ : mSize(0, 0),
+ mOwnsData(false),
+ mFormat(SurfaceFormat::UNKNOWN),
+ mStride(0)
+{
+}
+
+void
+gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
+{
+ if (!csurf || cairo_surface_status(csurf)) {
+ MakeInvalid();
+ return;
+ }
+
+ mSize.width = cairo_image_surface_get_width(csurf);
+ mSize.height = cairo_image_surface_get_height(csurf);
+ mData = cairo_image_surface_get_data(csurf);
+ mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
+ mOwnsData = false;
+ mStride = cairo_image_surface_get_stride(csurf);
+
+ Init(csurf, true);
+}
+
+gfxImageSurface::gfxImageSurface(unsigned char *aData, const IntSize& aSize,
+ long aStride, gfxImageFormat aFormat)
+{
+ InitWithData(aData, aSize, aStride, aFormat);
+}
+
+void
+gfxImageSurface::MakeInvalid()
+{
+ mSize = IntSize(-1, -1);
+ mData = nullptr;
+ mStride = 0;
+}
+
+void
+gfxImageSurface::InitWithData(unsigned char *aData, const IntSize& aSize,
+ long aStride, gfxImageFormat aFormat)
+{
+ mSize = aSize;
+ mOwnsData = false;
+ mData = aData;
+ mFormat = aFormat;
+ mStride = aStride;
+
+ if (!Factory::CheckSurfaceSize(aSize))
+ MakeInvalid();
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
+ cairo_surface_t *surface =
+ cairo_image_surface_create_for_data((unsigned char*)mData,
+ cformat,
+ mSize.width,
+ mSize.height,
+ mStride);
+
+ // cairo_image_surface_create_for_data can return a 'null' surface
+ // in out of memory conditions. The gfxASurface::Init call checks
+ // the surface it receives to see if there is an error with the
+ // surface and handles it appropriately. That is why there is
+ // no check here.
+ Init(surface);
+}
+
+static void*
+TryAllocAlignedBytes(size_t aSize)
+{
+ // Use fallible allocators here
+#if defined(HAVE_POSIX_MEMALIGN)
+ void* ptr;
+ // Try to align for fast alpha recovery. This should only help
+ // cairo too, can't hurt.
+ return moz_posix_memalign(&ptr,
+ 1 << gfxAlphaRecovery::GoodAlignmentLog2(),
+ aSize) ?
+ nullptr : ptr;
+#else
+ // Oh well, hope that luck is with us in the allocator
+ return malloc(aSize);
+#endif
+}
+
+gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, bool aClear)
+ : mSize(size), mData(nullptr), mFormat(format)
+{
+ AllocateAndInit(0, 0, aClear);
+}
+
+void
+gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
+ bool aClear)
+{
+ // The callers should set mSize and mFormat.
+ MOZ_ASSERT(!mData);
+ mData = nullptr;
+ mOwnsData = false;
+
+ mStride = aStride > 0 ? aStride : ComputeStride();
+ if (aMinimalAllocation < mSize.height * mStride)
+ aMinimalAllocation = mSize.height * mStride;
+
+ if (!Factory::CheckSurfaceSize(mSize))
+ MakeInvalid();
+
+ // if we have a zero-sized surface, just leave mData nullptr
+ if (mSize.height * mStride > 0) {
+
+ // This can fail to allocate memory aligned as we requested,
+ // or it can fail to allocate any memory at all.
+ mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
+ if (!mData)
+ return;
+ if (aClear)
+ memset(mData, 0, aMinimalAllocation);
+ }
+
+ mOwnsData = true;
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
+ cairo_surface_t *surface =
+ cairo_image_surface_create_for_data((unsigned char*)mData,
+ cformat,
+ mSize.width,
+ mSize.height,
+ mStride);
+
+ Init(surface);
+
+ if (mSurfaceValid) {
+ RecordMemoryUsed(mSize.height * ComputeStride() +
+ sizeof(gfxImageSurface));
+ }
+}
+
+gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format,
+ long aStride, int32_t aExtraBytes, bool aClear)
+ : mSize(size), mData(nullptr), mFormat(format)
+{
+ AllocateAndInit(aStride, aExtraBytes, aClear);
+}
+
+gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
+{
+ mSize.width = cairo_image_surface_get_width(csurf);
+ mSize.height = cairo_image_surface_get_height(csurf);
+ mData = cairo_image_surface_get_data(csurf);
+ mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
+ mOwnsData = false;
+ mStride = cairo_image_surface_get_stride(csurf);
+
+ Init(csurf, true);
+}
+
+gfxImageSurface::~gfxImageSurface()
+{
+ if (mOwnsData)
+ free(mData);
+}
+
+/*static*/ long
+gfxImageSurface::ComputeStride(const IntSize& aSize, gfxImageFormat aFormat)
+{
+ long stride;
+
+ if (aFormat == SurfaceFormat::A8R8G8B8_UINT32)
+ stride = aSize.width * 4;
+ else if (aFormat == SurfaceFormat::X8R8G8B8_UINT32)
+ stride = aSize.width * 4;
+ else if (aFormat == SurfaceFormat::R5G6B5_UINT16)
+ stride = aSize.width * 2;
+ else if (aFormat == SurfaceFormat::A8)
+ stride = aSize.width;
+ else {
+ NS_WARNING("Unknown format specified to gfxImageSurface!");
+ stride = aSize.width * 4;
+ }
+
+ stride = ((stride + 3) / 4) * 4;
+
+ return stride;
+}
+
+size_t
+gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf);
+ if (mOwnsData) {
+ n += aMallocSizeOf(mData);
+ }
+ return n;
+}
+
+size_t
+gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+bool
+gfxImageSurface::SizeOfIsMeasured() const
+{
+ return true;
+}
+
+// helper function for the CopyFrom methods
+static void
+CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
+{
+ if (aDestStride == aSrcStride) {
+ memcpy (aDest, aSrc, aSrcStride * aSize.height);
+ } else {
+ int lineSize = std::min(aDestStride, aSrcStride);
+ for (int i = 0; i < aSize.height; i++) {
+ unsigned char* src = aSrc + aSrcStride * i;
+ unsigned char* dst = aDest + aDestStride * i;
+
+ memcpy (dst, src, lineSize);
+ }
+ }
+}
+
+// helper function for the CopyFrom methods
+static bool
+FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2)
+{
+ if (a1 != a2 &&
+ !(a1 == SurfaceFormat::A8R8G8B8_UINT32 &&
+ a2 == SurfaceFormat::X8R8G8B8_UINT32) &&
+ !(a1 == SurfaceFormat::X8R8G8B8_UINT32 &&
+ a2 == SurfaceFormat::A8R8G8B8_UINT32)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+gfxImageSurface::CopyFrom (SourceSurface *aSurface)
+{
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ if (!data) {
+ return false;
+ }
+
+ IntSize size(data->GetSize().width, data->GetSize().height);
+ if (size != mSize) {
+ return false;
+ }
+
+ if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
+ mFormat)) {
+ return false;
+ }
+
+ CopyForStride(mData, data->GetData(), size, mStride, data->Stride());
+
+ return true;
+}
+
+
+bool
+gfxImageSurface::CopyFrom(gfxImageSurface *other)
+{
+ if (other->mSize != mSize) {
+ return false;
+ }
+
+ if (!FormatsAreCompatible(other->mFormat, mFormat)) {
+ return false;
+ }
+
+ CopyForStride(mData, other->mData, mSize, mStride, other->mStride);
+
+ return true;
+}
+
+bool
+gfxImageSurface::CopyTo(SourceSurface *aSurface) {
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ if (!data) {
+ return false;
+ }
+
+ IntSize size(data->GetSize().width, data->GetSize().height);
+ if (size != mSize) {
+ return false;
+ }
+
+ if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
+ mFormat)) {
+ return false;
+ }
+
+ CopyForStride(data->GetData(), mData, size, data->Stride(), mStride);
+
+ return true;
+}
+
+already_AddRefed<DataSourceSurface>
+gfxImageSurface::CopyToB8G8R8A8DataSourceSurface()
+{
+ RefPtr<DataSourceSurface> dataSurface =
+ Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height),
+ SurfaceFormat::B8G8R8A8);
+ if (dataSurface) {
+ CopyTo(dataSurface);
+ }
+ return dataSurface.forget();
+}
+
+already_AddRefed<gfxSubimageSurface>
+gfxImageSurface::GetSubimage(const gfxRect& aRect)
+{
+ gfxRect r(aRect);
+ r.Round();
+ MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r));
+
+ gfxImageFormat format = Format();
+
+ unsigned char* subData = Data() +
+ (Stride() * (int)r.Y()) +
+ (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format());
+
+ if (format == SurfaceFormat::A8R8G8B8_UINT32 &&
+ GetOpaqueRect().Contains(aRect)) {
+ format = SurfaceFormat::X8R8G8B8_UINT32;
+ }
+
+ RefPtr<gfxSubimageSurface> image =
+ new gfxSubimageSurface(this, subData,
+ IntSize((int)r.Width(), (int)r.Height()),
+ format);
+
+ return image.forget();
+}
+
+gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent,
+ unsigned char* aData,
+ const IntSize& aSize,
+ gfxImageFormat aFormat)
+ : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat)
+ , mParent(aParent)
+{
+}
+
+already_AddRefed<gfxImageSurface>
+gfxImageSurface::GetAsImageSurface()
+{
+ RefPtr<gfxImageSurface> surface = this;
+ return surface.forget();
+}
diff --git a/system/graphics/thebes/gfxImageSurface.h b/system/graphics/thebes/gfxImageSurface.h
new file mode 100644
index 000000000..dea580f5e
--- /dev/null
+++ b/system/graphics/thebes/gfxImageSurface.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGESURFACE_H
+#define GFX_IMAGESURFACE_H
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/RefPtr.h"
+#include "gfxASurface.h"
+#include "nsSize.h"
+
+// ARGB -- raw buffer.. wont be changed.. good for storing data.
+
+class gfxSubimageSurface;
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class SourceSurface;
+} // namespace gfx
+} // namespace mozilla
+
+/**
+ * A raw image buffer. The format can be set in the constructor. Its main
+ * purpose is for storing read-only images and using it as a source surface,
+ * but it can also be drawn to.
+ */
+class gfxImageSurface : public gfxASurface {
+public:
+ /**
+ * Construct an image surface around an existing buffer of image data.
+ * @param aData A buffer containing the image data
+ * @param aSize The size of the buffer
+ * @param aStride The stride of the buffer
+ * @param format Format of the data
+ *
+ * @see gfxImageFormat
+ */
+ gfxImageSurface(unsigned char *aData, const mozilla::gfx::IntSize& aSize,
+ long aStride, gfxImageFormat aFormat);
+
+ /**
+ * Construct an image surface.
+ * @param aSize The size of the buffer
+ * @param format Format of the data
+ *
+ * @see gfxImageFormat
+ */
+ gfxImageSurface(const mozilla::gfx::IntSize& size, gfxImageFormat format, bool aClear = true);
+
+ /**
+ * Construct an image surface, with a specified stride and allowing the
+ * allocation of more memory than required for the storage of the surface
+ * itself. When aStride and aMinimalAllocation are <=0, this constructor
+ * is the equivalent of the preceeding one.
+ *
+ * @param format Format of the data
+ * @param aSize The size of the buffer
+ * @param aStride The stride of the buffer - if <=0, use ComputeStride()
+ * @param aMinimalAllocation Allocate at least this many bytes. If smaller
+ * than width * stride, or width*stride <=0, this value is ignored.
+ * @param aClear
+ *
+ * @see gfxImageFormat
+ */
+ gfxImageSurface(const mozilla::gfx::IntSize& aSize, gfxImageFormat aFormat,
+ long aStride, int32_t aMinimalAllocation, bool aClear);
+
+ explicit gfxImageSurface(cairo_surface_t *csurf);
+
+ virtual ~gfxImageSurface();
+
+ // ImageSurface methods
+ gfxImageFormat Format() const { return mFormat; }
+
+ virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; }
+ int32_t Width() const {
+ if (mSize.width < 0) {
+ return 0;
+ }
+ return mSize.width;
+ }
+ int32_t Height() const {
+ if (mSize.height < 0) {
+ return 0;
+ }
+ return mSize.height;
+ }
+
+ /**
+ * Distance in bytes between the start of a line and the start of the
+ * next line.
+ */
+ int32_t Stride() const { return mStride; }
+ /**
+ * Returns a pointer for the image data. Users of this function can
+ * write to it, but must not attempt to free the buffer.
+ */
+ unsigned char* Data() const { return mData; } // delete this data under us and die.
+ /**
+ * Returns the total size of the image data.
+ */
+ int32_t GetDataSize() const {
+ if (mStride < 0 || mSize.height < 0) {
+ return 0;
+ }
+ return mStride*mSize.height;
+ }
+
+ /* Fast copy from another image surface; returns TRUE if successful, FALSE otherwise */
+ bool CopyFrom (gfxImageSurface *other);
+
+ /**
+ * Fast copy from a source surface; returns TRUE if successful, FALSE otherwise
+ * Assumes that the format of this surface is compatable with aSurface
+ */
+ bool CopyFrom (mozilla::gfx::SourceSurface *aSurface);
+
+ /**
+ * Fast copy to a source surface; returns TRUE if successful, FALSE otherwise
+ * Assumes that the format of this surface is compatible with aSurface
+ */
+ bool CopyTo (mozilla::gfx::SourceSurface *aSurface);
+
+ /**
+ * Copy to a Moz2D DataSourceSurface.
+ * Marked as virtual so that browsercomps can access this method.
+ */
+ virtual already_AddRefed<mozilla::gfx::DataSourceSurface> CopyToB8G8R8A8DataSourceSurface();
+
+ /* return new Subimage with pointing to original image starting from aRect.pos
+ * and size of aRect.size. New subimage keeping current image reference
+ */
+ already_AddRefed<gfxSubimageSurface> GetSubimage(const gfxRect& aRect);
+
+ virtual already_AddRefed<gfxImageSurface> GetAsImageSurface() override;
+
+ /** See gfxASurface.h. */
+ static long ComputeStride(const mozilla::gfx::IntSize&, gfxImageFormat);
+
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ override;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ override;
+ virtual bool SizeOfIsMeasured() const override;
+
+protected:
+ gfxImageSurface();
+ void InitWithData(unsigned char *aData, const mozilla::gfx::IntSize& aSize,
+ long aStride, gfxImageFormat aFormat);
+ /**
+ * See the parameters to the matching constructor. This should only
+ * be called once, in the constructor, which has already set mSize
+ * and mFormat.
+ */
+ void AllocateAndInit(long aStride, int32_t aMinimalAllocation, bool aClear);
+ void InitFromSurface(cairo_surface_t *csurf);
+
+ long ComputeStride() const {
+ if (mSize.height < 0 || mSize.width < 0) {
+ return 0;
+ }
+ return ComputeStride(mSize, mFormat);
+ }
+
+ void MakeInvalid();
+
+ mozilla::gfx::IntSize mSize;
+ bool mOwnsData;
+ unsigned char *mData;
+ gfxImageFormat mFormat;
+ long mStride;
+};
+
+class gfxSubimageSurface : public gfxImageSurface {
+protected:
+ friend class gfxImageSurface;
+ gfxSubimageSurface(gfxImageSurface* aParent,
+ unsigned char* aData,
+ const mozilla::gfx::IntSize& aSize,
+ gfxImageFormat aFormat);
+private:
+ RefPtr<gfxImageSurface> mParent;
+};
+
+#endif /* GFX_IMAGESURFACE_H */
diff --git a/system/graphics/thebes/gfxLanguageTagList.cpp b/system/graphics/thebes/gfxLanguageTagList.cpp
new file mode 100644
index 000000000..c6b71591e
--- /dev/null
+++ b/system/graphics/thebes/gfxLanguageTagList.cpp
@@ -0,0 +1,7876 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Derived from the IANA language subtag registry by genLanguageTagList.pl.
+ *
+ * Created on Mon Nov 7 14:52:44 2011.
+ *
+ * * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
+ */
+
+// Based on IANA registry dated 2011-08-25
+
+static const uint32_t sLanguageTagList[] = {
+ TRUETYPE_TAG('a','a', 0 , 0 ), // aa = Afar
+ TRUETYPE_TAG('a','b', 0 , 0 ), // ab = Abkhazian
+ TRUETYPE_TAG('a','e', 0 , 0 ), // ae = Avestan
+ TRUETYPE_TAG('a','f', 0 , 0 ), // af = Afrikaans
+ TRUETYPE_TAG('a','k', 0 , 0 ), // ak = Akan
+ TRUETYPE_TAG('a','m', 0 , 0 ), // am = Amharic
+ TRUETYPE_TAG('a','n', 0 , 0 ), // an = Aragonese
+ TRUETYPE_TAG('a','r', 0 , 0 ), // ar = Arabic
+ TRUETYPE_TAG('a','s', 0 , 0 ), // as = Assamese
+ TRUETYPE_TAG('a','v', 0 , 0 ), // av = Avaric
+ TRUETYPE_TAG('a','y', 0 , 0 ), // ay = Aymara
+ TRUETYPE_TAG('a','z', 0 , 0 ), // az = Azerbaijani
+ TRUETYPE_TAG('b','a', 0 , 0 ), // ba = Bashkir
+ TRUETYPE_TAG('b','e', 0 , 0 ), // be = Belarusian
+ TRUETYPE_TAG('b','g', 0 , 0 ), // bg = Bulgarian
+ TRUETYPE_TAG('b','h', 0 , 0 ), // bh = Bihari languages
+ TRUETYPE_TAG('b','i', 0 , 0 ), // bi = Bislama
+ TRUETYPE_TAG('b','m', 0 , 0 ), // bm = Bambara
+ TRUETYPE_TAG('b','n', 0 , 0 ), // bn = Bengali
+ TRUETYPE_TAG('b','o', 0 , 0 ), // bo = Tibetan
+ TRUETYPE_TAG('b','r', 0 , 0 ), // br = Breton
+ TRUETYPE_TAG('b','s', 0 , 0 ), // bs = Bosnian
+ TRUETYPE_TAG('c','a', 0 , 0 ), // ca = Catalan
+ TRUETYPE_TAG('c','e', 0 , 0 ), // ce = Chechen
+ TRUETYPE_TAG('c','h', 0 , 0 ), // ch = Chamorro
+ TRUETYPE_TAG('c','o', 0 , 0 ), // co = Corsican
+ TRUETYPE_TAG('c','r', 0 , 0 ), // cr = Cree
+ TRUETYPE_TAG('c','s', 0 , 0 ), // cs = Czech
+ TRUETYPE_TAG('c','u', 0 , 0 ), // cu = Church Slavic
+ TRUETYPE_TAG('c','v', 0 , 0 ), // cv = Chuvash
+ TRUETYPE_TAG('c','y', 0 , 0 ), // cy = Welsh
+ TRUETYPE_TAG('d','a', 0 , 0 ), // da = Danish
+ TRUETYPE_TAG('d','e', 0 , 0 ), // de = German
+ TRUETYPE_TAG('d','v', 0 , 0 ), // dv = Dhivehi
+ TRUETYPE_TAG('d','z', 0 , 0 ), // dz = Dzongkha
+ TRUETYPE_TAG('e','e', 0 , 0 ), // ee = Ewe
+ TRUETYPE_TAG('e','l', 0 , 0 ), // el = Modern Greek (1453-)
+ TRUETYPE_TAG('e','n', 0 , 0 ), // en = English
+ TRUETYPE_TAG('e','o', 0 , 0 ), // eo = Esperanto
+ TRUETYPE_TAG('e','s', 0 , 0 ), // es = Spanish
+ TRUETYPE_TAG('e','t', 0 , 0 ), // et = Estonian
+ TRUETYPE_TAG('e','u', 0 , 0 ), // eu = Basque
+ TRUETYPE_TAG('f','a', 0 , 0 ), // fa = Persian
+ TRUETYPE_TAG('f','f', 0 , 0 ), // ff = Fulah
+ TRUETYPE_TAG('f','i', 0 , 0 ), // fi = Finnish
+ TRUETYPE_TAG('f','j', 0 , 0 ), // fj = Fijian
+ TRUETYPE_TAG('f','o', 0 , 0 ), // fo = Faroese
+ TRUETYPE_TAG('f','r', 0 , 0 ), // fr = French
+ TRUETYPE_TAG('f','y', 0 , 0 ), // fy = Western Frisian
+ TRUETYPE_TAG('g','a', 0 , 0 ), // ga = Irish
+ TRUETYPE_TAG('g','d', 0 , 0 ), // gd = Scottish Gaelic
+ TRUETYPE_TAG('g','l', 0 , 0 ), // gl = Galician
+ TRUETYPE_TAG('g','n', 0 , 0 ), // gn = Guarani
+ TRUETYPE_TAG('g','u', 0 , 0 ), // gu = Gujarati
+ TRUETYPE_TAG('g','v', 0 , 0 ), // gv = Manx
+ TRUETYPE_TAG('h','a', 0 , 0 ), // ha = Hausa
+ TRUETYPE_TAG('h','e', 0 , 0 ), // he = Hebrew
+ TRUETYPE_TAG('h','i', 0 , 0 ), // hi = Hindi
+ TRUETYPE_TAG('h','o', 0 , 0 ), // ho = Hiri Motu
+ TRUETYPE_TAG('h','r', 0 , 0 ), // hr = Croatian
+ TRUETYPE_TAG('h','t', 0 , 0 ), // ht = Haitian
+ TRUETYPE_TAG('h','u', 0 , 0 ), // hu = Hungarian
+ TRUETYPE_TAG('h','y', 0 , 0 ), // hy = Armenian
+ TRUETYPE_TAG('h','z', 0 , 0 ), // hz = Herero
+ TRUETYPE_TAG('i','a', 0 , 0 ), // ia = Interlingua (International Auxiliary Language
+ TRUETYPE_TAG('i','d', 0 , 0 ), // id = Indonesian
+ TRUETYPE_TAG('i','e', 0 , 0 ), // ie = Interlingue
+ TRUETYPE_TAG('i','g', 0 , 0 ), // ig = Igbo
+ TRUETYPE_TAG('i','i', 0 , 0 ), // ii = Sichuan Yi
+ TRUETYPE_TAG('i','k', 0 , 0 ), // ik = Inupiaq
+ TRUETYPE_TAG('i','n', 0 , 0 ), // in = Indonesian
+ TRUETYPE_TAG('i','o', 0 , 0 ), // io = Ido
+ TRUETYPE_TAG('i','s', 0 , 0 ), // is = Icelandic
+ TRUETYPE_TAG('i','t', 0 , 0 ), // it = Italian
+ TRUETYPE_TAG('i','u', 0 , 0 ), // iu = Inuktitut
+ TRUETYPE_TAG('i','w', 0 , 0 ), // iw = Hebrew
+ TRUETYPE_TAG('j','a', 0 , 0 ), // ja = Japanese
+ TRUETYPE_TAG('j','i', 0 , 0 ), // ji = Yiddish
+ TRUETYPE_TAG('j','v', 0 , 0 ), // jv = Javanese
+ TRUETYPE_TAG('j','w', 0 , 0 ), // jw = Javanese
+ TRUETYPE_TAG('k','a', 0 , 0 ), // ka = Georgian
+ TRUETYPE_TAG('k','g', 0 , 0 ), // kg = Kongo
+ TRUETYPE_TAG('k','i', 0 , 0 ), // ki = Kikuyu
+ TRUETYPE_TAG('k','j', 0 , 0 ), // kj = Kuanyama
+ TRUETYPE_TAG('k','k', 0 , 0 ), // kk = Kazakh
+ TRUETYPE_TAG('k','l', 0 , 0 ), // kl = Kalaallisut
+ TRUETYPE_TAG('k','m', 0 , 0 ), // km = Central Khmer
+ TRUETYPE_TAG('k','n', 0 , 0 ), // kn = Kannada
+ TRUETYPE_TAG('k','o', 0 , 0 ), // ko = Korean
+ TRUETYPE_TAG('k','r', 0 , 0 ), // kr = Kanuri
+ TRUETYPE_TAG('k','s', 0 , 0 ), // ks = Kashmiri
+ TRUETYPE_TAG('k','u', 0 , 0 ), // ku = Kurdish
+ TRUETYPE_TAG('k','v', 0 , 0 ), // kv = Komi
+ TRUETYPE_TAG('k','w', 0 , 0 ), // kw = Cornish
+ TRUETYPE_TAG('k','y', 0 , 0 ), // ky = Kirghiz
+ TRUETYPE_TAG('l','a', 0 , 0 ), // la = Latin
+ TRUETYPE_TAG('l','b', 0 , 0 ), // lb = Luxembourgish
+ TRUETYPE_TAG('l','g', 0 , 0 ), // lg = Ganda
+ TRUETYPE_TAG('l','i', 0 , 0 ), // li = Limburgan
+ TRUETYPE_TAG('l','n', 0 , 0 ), // ln = Lingala
+ TRUETYPE_TAG('l','o', 0 , 0 ), // lo = Lao
+ TRUETYPE_TAG('l','t', 0 , 0 ), // lt = Lithuanian
+ TRUETYPE_TAG('l','u', 0 , 0 ), // lu = Luba-Katanga
+ TRUETYPE_TAG('l','v', 0 , 0 ), // lv = Latvian
+ TRUETYPE_TAG('m','g', 0 , 0 ), // mg = Malagasy
+ TRUETYPE_TAG('m','h', 0 , 0 ), // mh = Marshallese
+ TRUETYPE_TAG('m','i', 0 , 0 ), // mi = Maori
+ TRUETYPE_TAG('m','k', 0 , 0 ), // mk = Macedonian
+ TRUETYPE_TAG('m','l', 0 , 0 ), // ml = Malayalam
+ TRUETYPE_TAG('m','n', 0 , 0 ), // mn = Mongolian
+ TRUETYPE_TAG('m','o', 0 , 0 ), // mo = Moldavian
+ TRUETYPE_TAG('m','r', 0 , 0 ), // mr = Marathi
+ TRUETYPE_TAG('m','s', 0 , 0 ), // ms = Malay (macrolanguage)
+ TRUETYPE_TAG('m','t', 0 , 0 ), // mt = Maltese
+ TRUETYPE_TAG('m','y', 0 , 0 ), // my = Burmese
+ TRUETYPE_TAG('n','a', 0 , 0 ), // na = Nauru
+ TRUETYPE_TAG('n','b', 0 , 0 ), // nb = Norwegian Bokmål
+ TRUETYPE_TAG('n','d', 0 , 0 ), // nd = North Ndebele
+ TRUETYPE_TAG('n','e', 0 , 0 ), // ne = Nepali
+ TRUETYPE_TAG('n','g', 0 , 0 ), // ng = Ndonga
+ TRUETYPE_TAG('n','l', 0 , 0 ), // nl = Dutch
+ TRUETYPE_TAG('n','n', 0 , 0 ), // nn = Norwegian Nynorsk
+ TRUETYPE_TAG('n','o', 0 , 0 ), // no = Norwegian
+ TRUETYPE_TAG('n','r', 0 , 0 ), // nr = South Ndebele
+ TRUETYPE_TAG('n','v', 0 , 0 ), // nv = Navajo
+ TRUETYPE_TAG('n','y', 0 , 0 ), // ny = Nyanja
+ TRUETYPE_TAG('o','c', 0 , 0 ), // oc = Occitan (post 1500)
+ TRUETYPE_TAG('o','j', 0 , 0 ), // oj = Ojibwa
+ TRUETYPE_TAG('o','m', 0 , 0 ), // om = Oromo
+ TRUETYPE_TAG('o','r', 0 , 0 ), // or = Oriya
+ TRUETYPE_TAG('o','s', 0 , 0 ), // os = Ossetian
+ TRUETYPE_TAG('p','a', 0 , 0 ), // pa = Panjabi
+ TRUETYPE_TAG('p','i', 0 , 0 ), // pi = Pali
+ TRUETYPE_TAG('p','l', 0 , 0 ), // pl = Polish
+ TRUETYPE_TAG('p','s', 0 , 0 ), // ps = Pushto
+ TRUETYPE_TAG('p','t', 0 , 0 ), // pt = Portuguese
+ TRUETYPE_TAG('q','u', 0 , 0 ), // qu = Quechua
+ TRUETYPE_TAG('r','m', 0 , 0 ), // rm = Romansh
+ TRUETYPE_TAG('r','n', 0 , 0 ), // rn = Rundi
+ TRUETYPE_TAG('r','o', 0 , 0 ), // ro = Romanian
+ TRUETYPE_TAG('r','u', 0 , 0 ), // ru = Russian
+ TRUETYPE_TAG('r','w', 0 , 0 ), // rw = Kinyarwanda
+ TRUETYPE_TAG('s','a', 0 , 0 ), // sa = Sanskrit
+ TRUETYPE_TAG('s','c', 0 , 0 ), // sc = Sardinian
+ TRUETYPE_TAG('s','d', 0 , 0 ), // sd = Sindhi
+ TRUETYPE_TAG('s','e', 0 , 0 ), // se = Northern Sami
+ TRUETYPE_TAG('s','g', 0 , 0 ), // sg = Sango
+ TRUETYPE_TAG('s','h', 0 , 0 ), // sh = Serbo-Croatian
+ TRUETYPE_TAG('s','i', 0 , 0 ), // si = Sinhala
+ TRUETYPE_TAG('s','k', 0 , 0 ), // sk = Slovak
+ TRUETYPE_TAG('s','l', 0 , 0 ), // sl = Slovenian
+ TRUETYPE_TAG('s','m', 0 , 0 ), // sm = Samoan
+ TRUETYPE_TAG('s','n', 0 , 0 ), // sn = Shona
+ TRUETYPE_TAG('s','o', 0 , 0 ), // so = Somali
+ TRUETYPE_TAG('s','q', 0 , 0 ), // sq = Albanian
+ TRUETYPE_TAG('s','r', 0 , 0 ), // sr = Serbian
+ TRUETYPE_TAG('s','s', 0 , 0 ), // ss = Swati
+ TRUETYPE_TAG('s','t', 0 , 0 ), // st = Southern Sotho
+ TRUETYPE_TAG('s','u', 0 , 0 ), // su = Sundanese
+ TRUETYPE_TAG('s','v', 0 , 0 ), // sv = Swedish
+ TRUETYPE_TAG('s','w', 0 , 0 ), // sw = Swahili (macrolanguage)
+ TRUETYPE_TAG('t','a', 0 , 0 ), // ta = Tamil
+ TRUETYPE_TAG('t','e', 0 , 0 ), // te = Telugu
+ TRUETYPE_TAG('t','g', 0 , 0 ), // tg = Tajik
+ TRUETYPE_TAG('t','h', 0 , 0 ), // th = Thai
+ TRUETYPE_TAG('t','i', 0 , 0 ), // ti = Tigrinya
+ TRUETYPE_TAG('t','k', 0 , 0 ), // tk = Turkmen
+ TRUETYPE_TAG('t','l', 0 , 0 ), // tl = Tagalog
+ TRUETYPE_TAG('t','n', 0 , 0 ), // tn = Tswana
+ TRUETYPE_TAG('t','o', 0 , 0 ), // to = Tonga (Tonga Islands)
+ TRUETYPE_TAG('t','r', 0 , 0 ), // tr = Turkish
+ TRUETYPE_TAG('t','s', 0 , 0 ), // ts = Tsonga
+ TRUETYPE_TAG('t','t', 0 , 0 ), // tt = Tatar
+ TRUETYPE_TAG('t','w', 0 , 0 ), // tw = Twi
+ TRUETYPE_TAG('t','y', 0 , 0 ), // ty = Tahitian
+ TRUETYPE_TAG('u','g', 0 , 0 ), // ug = Uighur
+ TRUETYPE_TAG('u','k', 0 , 0 ), // uk = Ukrainian
+ TRUETYPE_TAG('u','r', 0 , 0 ), // ur = Urdu
+ TRUETYPE_TAG('u','z', 0 , 0 ), // uz = Uzbek
+ TRUETYPE_TAG('v','e', 0 , 0 ), // ve = Venda
+ TRUETYPE_TAG('v','i', 0 , 0 ), // vi = Vietnamese
+ TRUETYPE_TAG('v','o', 0 , 0 ), // vo = Volapük
+ TRUETYPE_TAG('w','a', 0 , 0 ), // wa = Walloon
+ TRUETYPE_TAG('w','o', 0 , 0 ), // wo = Wolof
+ TRUETYPE_TAG('x','h', 0 , 0 ), // xh = Xhosa
+ TRUETYPE_TAG('y','i', 0 , 0 ), // yi = Yiddish
+ TRUETYPE_TAG('y','o', 0 , 0 ), // yo = Yoruba
+ TRUETYPE_TAG('z','a', 0 , 0 ), // za = Zhuang
+ TRUETYPE_TAG('z','h', 0 , 0 ), // zh = Chinese
+ TRUETYPE_TAG('z','u', 0 , 0 ), // zu = Zulu
+ TRUETYPE_TAG('a','a','a', 0 ), // aaa = Ghotuo
+ TRUETYPE_TAG('a','a','b', 0 ), // aab = Alumu-Tesu
+ TRUETYPE_TAG('a','a','c', 0 ), // aac = Ari
+ TRUETYPE_TAG('a','a','d', 0 ), // aad = Amal
+ TRUETYPE_TAG('a','a','e', 0 ), // aae = Arbëreshë Albanian
+ TRUETYPE_TAG('a','a','f', 0 ), // aaf = Aranadan
+ TRUETYPE_TAG('a','a','g', 0 ), // aag = Ambrak
+ TRUETYPE_TAG('a','a','h', 0 ), // aah = Abu' Arapesh
+ TRUETYPE_TAG('a','a','i', 0 ), // aai = Arifama-Miniafia
+ TRUETYPE_TAG('a','a','k', 0 ), // aak = Ankave
+ TRUETYPE_TAG('a','a','l', 0 ), // aal = Afade
+ TRUETYPE_TAG('a','a','m', 0 ), // aam = Aramanik
+ TRUETYPE_TAG('a','a','n', 0 ), // aan = Anambé
+ TRUETYPE_TAG('a','a','o', 0 ), // aao = Algerian Saharan Arabic
+ TRUETYPE_TAG('a','a','p', 0 ), // aap = Pará Arára
+ TRUETYPE_TAG('a','a','q', 0 ), // aaq = Eastern Abnaki
+ TRUETYPE_TAG('a','a','s', 0 ), // aas = Aasáx
+ TRUETYPE_TAG('a','a','t', 0 ), // aat = Arvanitika Albanian
+ TRUETYPE_TAG('a','a','u', 0 ), // aau = Abau
+ TRUETYPE_TAG('a','a','v', 0 ), // aav = Austro-Asiatic languages
+ TRUETYPE_TAG('a','a','w', 0 ), // aaw = Solong
+ TRUETYPE_TAG('a','a','x', 0 ), // aax = Mandobo Atas
+ TRUETYPE_TAG('a','a','z', 0 ), // aaz = Amarasi
+ TRUETYPE_TAG('a','b','a', 0 ), // aba = Abé
+ TRUETYPE_TAG('a','b','b', 0 ), // abb = Bankon
+ TRUETYPE_TAG('a','b','c', 0 ), // abc = Ambala Ayta
+ TRUETYPE_TAG('a','b','d', 0 ), // abd = Manide
+ TRUETYPE_TAG('a','b','e', 0 ), // abe = Western Abnaki
+ TRUETYPE_TAG('a','b','f', 0 ), // abf = Abai Sungai
+ TRUETYPE_TAG('a','b','g', 0 ), // abg = Abaga
+ TRUETYPE_TAG('a','b','h', 0 ), // abh = Tajiki Arabic
+ TRUETYPE_TAG('a','b','i', 0 ), // abi = Abidji
+ TRUETYPE_TAG('a','b','j', 0 ), // abj = Aka-Bea
+ TRUETYPE_TAG('a','b','l', 0 ), // abl = Lampung Nyo
+ TRUETYPE_TAG('a','b','m', 0 ), // abm = Abanyom
+ TRUETYPE_TAG('a','b','n', 0 ), // abn = Abua
+ TRUETYPE_TAG('a','b','o', 0 ), // abo = Abon
+ TRUETYPE_TAG('a','b','p', 0 ), // abp = Abellen Ayta
+ TRUETYPE_TAG('a','b','q', 0 ), // abq = Abaza
+ TRUETYPE_TAG('a','b','r', 0 ), // abr = Abron
+ TRUETYPE_TAG('a','b','s', 0 ), // abs = Ambonese Malay
+ TRUETYPE_TAG('a','b','t', 0 ), // abt = Ambulas
+ TRUETYPE_TAG('a','b','u', 0 ), // abu = Abure
+ TRUETYPE_TAG('a','b','v', 0 ), // abv = Baharna Arabic
+ TRUETYPE_TAG('a','b','w', 0 ), // abw = Pal
+ TRUETYPE_TAG('a','b','x', 0 ), // abx = Inabaknon
+ TRUETYPE_TAG('a','b','y', 0 ), // aby = Aneme Wake
+ TRUETYPE_TAG('a','b','z', 0 ), // abz = Abui
+ TRUETYPE_TAG('a','c','a', 0 ), // aca = Achagua
+ TRUETYPE_TAG('a','c','b', 0 ), // acb = Ãncá
+ TRUETYPE_TAG('a','c','d', 0 ), // acd = Gikyode
+ TRUETYPE_TAG('a','c','e', 0 ), // ace = Achinese
+ TRUETYPE_TAG('a','c','f', 0 ), // acf = Saint Lucian Creole French
+ TRUETYPE_TAG('a','c','h', 0 ), // ach = Acoli
+ TRUETYPE_TAG('a','c','i', 0 ), // aci = Aka-Cari
+ TRUETYPE_TAG('a','c','k', 0 ), // ack = Aka-Kora
+ TRUETYPE_TAG('a','c','l', 0 ), // acl = Akar-Bale
+ TRUETYPE_TAG('a','c','m', 0 ), // acm = Mesopotamian Arabic
+ TRUETYPE_TAG('a','c','n', 0 ), // acn = Achang
+ TRUETYPE_TAG('a','c','p', 0 ), // acp = Eastern Acipa
+ TRUETYPE_TAG('a','c','q', 0 ), // acq = Ta'izzi-Adeni Arabic
+ TRUETYPE_TAG('a','c','r', 0 ), // acr = Achi
+ TRUETYPE_TAG('a','c','s', 0 ), // acs = Acroá
+ TRUETYPE_TAG('a','c','t', 0 ), // act = Achterhoeks
+ TRUETYPE_TAG('a','c','u', 0 ), // acu = Achuar-Shiwiar
+ TRUETYPE_TAG('a','c','v', 0 ), // acv = Achumawi
+ TRUETYPE_TAG('a','c','w', 0 ), // acw = Hijazi Arabic
+ TRUETYPE_TAG('a','c','x', 0 ), // acx = Omani Arabic
+ TRUETYPE_TAG('a','c','y', 0 ), // acy = Cypriot Arabic
+ TRUETYPE_TAG('a','c','z', 0 ), // acz = Acheron
+ TRUETYPE_TAG('a','d','a', 0 ), // ada = Adangme
+ TRUETYPE_TAG('a','d','b', 0 ), // adb = Adabe
+ TRUETYPE_TAG('a','d','d', 0 ), // add = Dzodinka
+ TRUETYPE_TAG('a','d','e', 0 ), // ade = Adele
+ TRUETYPE_TAG('a','d','f', 0 ), // adf = Dhofari Arabic
+ TRUETYPE_TAG('a','d','g', 0 ), // adg = Andegerebinha
+ TRUETYPE_TAG('a','d','h', 0 ), // adh = Adhola
+ TRUETYPE_TAG('a','d','i', 0 ), // adi = Adi
+ TRUETYPE_TAG('a','d','j', 0 ), // adj = Adioukrou
+ TRUETYPE_TAG('a','d','l', 0 ), // adl = Galo
+ TRUETYPE_TAG('a','d','n', 0 ), // adn = Adang
+ TRUETYPE_TAG('a','d','o', 0 ), // ado = Abu
+ TRUETYPE_TAG('a','d','p', 0 ), // adp = Adap
+ TRUETYPE_TAG('a','d','q', 0 ), // adq = Adangbe
+ TRUETYPE_TAG('a','d','r', 0 ), // adr = Adonara
+ TRUETYPE_TAG('a','d','s', 0 ), // ads = Adamorobe Sign Language
+ TRUETYPE_TAG('a','d','t', 0 ), // adt = Adnyamathanha
+ TRUETYPE_TAG('a','d','u', 0 ), // adu = Aduge
+ TRUETYPE_TAG('a','d','w', 0 ), // adw = Amundava
+ TRUETYPE_TAG('a','d','x', 0 ), // adx = Amdo Tibetan
+ TRUETYPE_TAG('a','d','y', 0 ), // ady = Adyghe
+ TRUETYPE_TAG('a','d','z', 0 ), // adz = Adzera
+ TRUETYPE_TAG('a','e','a', 0 ), // aea = Areba
+ TRUETYPE_TAG('a','e','b', 0 ), // aeb = Tunisian Arabic
+ TRUETYPE_TAG('a','e','c', 0 ), // aec = Saidi Arabic
+ TRUETYPE_TAG('a','e','d', 0 ), // aed = Argentine Sign Language
+ TRUETYPE_TAG('a','e','e', 0 ), // aee = Northeast Pashayi
+ TRUETYPE_TAG('a','e','k', 0 ), // aek = Haeke
+ TRUETYPE_TAG('a','e','l', 0 ), // ael = Ambele
+ TRUETYPE_TAG('a','e','m', 0 ), // aem = Arem
+ TRUETYPE_TAG('a','e','n', 0 ), // aen = Armenian Sign Language
+ TRUETYPE_TAG('a','e','q', 0 ), // aeq = Aer
+ TRUETYPE_TAG('a','e','r', 0 ), // aer = Eastern Arrernte
+ TRUETYPE_TAG('a','e','s', 0 ), // aes = Alsea
+ TRUETYPE_TAG('a','e','u', 0 ), // aeu = Akeu
+ TRUETYPE_TAG('a','e','w', 0 ), // aew = Ambakich
+ TRUETYPE_TAG('a','e','y', 0 ), // aey = Amele
+ TRUETYPE_TAG('a','e','z', 0 ), // aez = Aeka
+ TRUETYPE_TAG('a','f','a', 0 ), // afa = Afro-Asiatic languages
+ TRUETYPE_TAG('a','f','b', 0 ), // afb = Gulf Arabic
+ TRUETYPE_TAG('a','f','d', 0 ), // afd = Andai
+ TRUETYPE_TAG('a','f','e', 0 ), // afe = Putukwam
+ TRUETYPE_TAG('a','f','g', 0 ), // afg = Afghan Sign Language
+ TRUETYPE_TAG('a','f','h', 0 ), // afh = Afrihili
+ TRUETYPE_TAG('a','f','i', 0 ), // afi = Akrukay
+ TRUETYPE_TAG('a','f','k', 0 ), // afk = Nanubae
+ TRUETYPE_TAG('a','f','n', 0 ), // afn = Defaka
+ TRUETYPE_TAG('a','f','o', 0 ), // afo = Eloyi
+ TRUETYPE_TAG('a','f','p', 0 ), // afp = Tapei
+ TRUETYPE_TAG('a','f','s', 0 ), // afs = Afro-Seminole Creole
+ TRUETYPE_TAG('a','f','t', 0 ), // aft = Afitti
+ TRUETYPE_TAG('a','f','u', 0 ), // afu = Awutu
+ TRUETYPE_TAG('a','f','z', 0 ), // afz = Obokuitai
+ TRUETYPE_TAG('a','g','a', 0 ), // aga = Aguano
+ TRUETYPE_TAG('a','g','b', 0 ), // agb = Legbo
+ TRUETYPE_TAG('a','g','c', 0 ), // agc = Agatu
+ TRUETYPE_TAG('a','g','d', 0 ), // agd = Agarabi
+ TRUETYPE_TAG('a','g','e', 0 ), // age = Angal
+ TRUETYPE_TAG('a','g','f', 0 ), // agf = Arguni
+ TRUETYPE_TAG('a','g','g', 0 ), // agg = Angor
+ TRUETYPE_TAG('a','g','h', 0 ), // agh = Ngelima
+ TRUETYPE_TAG('a','g','i', 0 ), // agi = Agariya
+ TRUETYPE_TAG('a','g','j', 0 ), // agj = Argobba
+ TRUETYPE_TAG('a','g','k', 0 ), // agk = Isarog Agta
+ TRUETYPE_TAG('a','g','l', 0 ), // agl = Fembe
+ TRUETYPE_TAG('a','g','m', 0 ), // agm = Angaataha
+ TRUETYPE_TAG('a','g','n', 0 ), // agn = Agutaynen
+ TRUETYPE_TAG('a','g','o', 0 ), // ago = Tainae
+ TRUETYPE_TAG('a','g','p', 0 ), // agp = Paranan
+ TRUETYPE_TAG('a','g','q', 0 ), // agq = Aghem
+ TRUETYPE_TAG('a','g','r', 0 ), // agr = Aguaruna
+ TRUETYPE_TAG('a','g','s', 0 ), // ags = Esimbi
+ TRUETYPE_TAG('a','g','t', 0 ), // agt = Central Cagayan Agta
+ TRUETYPE_TAG('a','g','u', 0 ), // agu = Aguacateco
+ TRUETYPE_TAG('a','g','v', 0 ), // agv = Remontado Dumagat
+ TRUETYPE_TAG('a','g','w', 0 ), // agw = Kahua
+ TRUETYPE_TAG('a','g','x', 0 ), // agx = Aghul
+ TRUETYPE_TAG('a','g','y', 0 ), // agy = Southern Alta
+ TRUETYPE_TAG('a','g','z', 0 ), // agz = Mt. Iriga Agta
+ TRUETYPE_TAG('a','h','a', 0 ), // aha = Ahanta
+ TRUETYPE_TAG('a','h','b', 0 ), // ahb = Axamb
+ TRUETYPE_TAG('a','h','g', 0 ), // ahg = Qimant
+ TRUETYPE_TAG('a','h','h', 0 ), // ahh = Aghu
+ TRUETYPE_TAG('a','h','i', 0 ), // ahi = Tiagbamrin Aizi
+ TRUETYPE_TAG('a','h','k', 0 ), // ahk = Akha
+ TRUETYPE_TAG('a','h','l', 0 ), // ahl = Igo
+ TRUETYPE_TAG('a','h','m', 0 ), // ahm = Mobumrin Aizi
+ TRUETYPE_TAG('a','h','n', 0 ), // ahn = Àhàn
+ TRUETYPE_TAG('a','h','o', 0 ), // aho = Ahom
+ TRUETYPE_TAG('a','h','p', 0 ), // ahp = Aproumu Aizi
+ TRUETYPE_TAG('a','h','r', 0 ), // ahr = Ahirani
+ TRUETYPE_TAG('a','h','s', 0 ), // ahs = Ashe
+ TRUETYPE_TAG('a','h','t', 0 ), // aht = Ahtena
+ TRUETYPE_TAG('a','i','a', 0 ), // aia = Arosi
+ TRUETYPE_TAG('a','i','b', 0 ), // aib = Ainu (China)
+ TRUETYPE_TAG('a','i','c', 0 ), // aic = Ainbai
+ TRUETYPE_TAG('a','i','d', 0 ), // aid = Alngith
+ TRUETYPE_TAG('a','i','e', 0 ), // aie = Amara
+ TRUETYPE_TAG('a','i','f', 0 ), // aif = Agi
+ TRUETYPE_TAG('a','i','g', 0 ), // aig = Antigua and Barbuda Creole English
+ TRUETYPE_TAG('a','i','h', 0 ), // aih = Ai-Cham
+ TRUETYPE_TAG('a','i','i', 0 ), // aii = Assyrian Neo-Aramaic
+ TRUETYPE_TAG('a','i','j', 0 ), // aij = Lishanid Noshan
+ TRUETYPE_TAG('a','i','k', 0 ), // aik = Ake
+ TRUETYPE_TAG('a','i','l', 0 ), // ail = Aimele
+ TRUETYPE_TAG('a','i','m', 0 ), // aim = Aimol
+ TRUETYPE_TAG('a','i','n', 0 ), // ain = Ainu (Japan)
+ TRUETYPE_TAG('a','i','o', 0 ), // aio = Aiton
+ TRUETYPE_TAG('a','i','p', 0 ), // aip = Burumakok
+ TRUETYPE_TAG('a','i','q', 0 ), // aiq = Aimaq
+ TRUETYPE_TAG('a','i','r', 0 ), // air = Airoran
+ TRUETYPE_TAG('a','i','s', 0 ), // ais = Nataoran Amis
+ TRUETYPE_TAG('a','i','t', 0 ), // ait = Arikem
+ TRUETYPE_TAG('a','i','w', 0 ), // aiw = Aari
+ TRUETYPE_TAG('a','i','x', 0 ), // aix = Aighon
+ TRUETYPE_TAG('a','i','y', 0 ), // aiy = Ali
+ TRUETYPE_TAG('a','j','a', 0 ), // aja = Aja (Sudan)
+ TRUETYPE_TAG('a','j','g', 0 ), // ajg = Aja (Benin)
+ TRUETYPE_TAG('a','j','i', 0 ), // aji = Ajië
+ TRUETYPE_TAG('a','j','p', 0 ), // ajp = South Levantine Arabic
+ TRUETYPE_TAG('a','j','t', 0 ), // ajt = Judeo-Tunisian Arabic
+ TRUETYPE_TAG('a','j','u', 0 ), // aju = Judeo-Moroccan Arabic
+ TRUETYPE_TAG('a','j','w', 0 ), // ajw = Ajawa
+ TRUETYPE_TAG('a','j','z', 0 ), // ajz = Amri Karbi
+ TRUETYPE_TAG('a','k','b', 0 ), // akb = Batak Angkola
+ TRUETYPE_TAG('a','k','c', 0 ), // akc = Mpur
+ TRUETYPE_TAG('a','k','d', 0 ), // akd = Ukpet-Ehom
+ TRUETYPE_TAG('a','k','e', 0 ), // ake = Akawaio
+ TRUETYPE_TAG('a','k','f', 0 ), // akf = Akpa
+ TRUETYPE_TAG('a','k','g', 0 ), // akg = Anakalangu
+ TRUETYPE_TAG('a','k','h', 0 ), // akh = Angal Heneng
+ TRUETYPE_TAG('a','k','i', 0 ), // aki = Aiome
+ TRUETYPE_TAG('a','k','j', 0 ), // akj = Aka-Jeru
+ TRUETYPE_TAG('a','k','k', 0 ), // akk = Akkadian
+ TRUETYPE_TAG('a','k','l', 0 ), // akl = Aklanon
+ TRUETYPE_TAG('a','k','m', 0 ), // akm = Aka-Bo
+ TRUETYPE_TAG('a','k','o', 0 ), // ako = Akurio
+ TRUETYPE_TAG('a','k','p', 0 ), // akp = Siwu
+ TRUETYPE_TAG('a','k','q', 0 ), // akq = Ak
+ TRUETYPE_TAG('a','k','r', 0 ), // akr = Araki
+ TRUETYPE_TAG('a','k','s', 0 ), // aks = Akaselem
+ TRUETYPE_TAG('a','k','t', 0 ), // akt = Akolet
+ TRUETYPE_TAG('a','k','u', 0 ), // aku = Akum
+ TRUETYPE_TAG('a','k','v', 0 ), // akv = Akhvakh
+ TRUETYPE_TAG('a','k','w', 0 ), // akw = Akwa
+ TRUETYPE_TAG('a','k','x', 0 ), // akx = Aka-Kede
+ TRUETYPE_TAG('a','k','y', 0 ), // aky = Aka-Kol
+ TRUETYPE_TAG('a','k','z', 0 ), // akz = Alabama
+ TRUETYPE_TAG('a','l','a', 0 ), // ala = Alago
+ TRUETYPE_TAG('a','l','c', 0 ), // alc = Qawasqar
+ TRUETYPE_TAG('a','l','d', 0 ), // ald = Alladian
+ TRUETYPE_TAG('a','l','e', 0 ), // ale = Aleut
+ TRUETYPE_TAG('a','l','f', 0 ), // alf = Alege
+ TRUETYPE_TAG('a','l','g', 0 ), // alg = Algonquian languages
+ TRUETYPE_TAG('a','l','h', 0 ), // alh = Alawa
+ TRUETYPE_TAG('a','l','i', 0 ), // ali = Amaimon
+ TRUETYPE_TAG('a','l','j', 0 ), // alj = Alangan
+ TRUETYPE_TAG('a','l','k', 0 ), // alk = Alak
+ TRUETYPE_TAG('a','l','l', 0 ), // all = Allar
+ TRUETYPE_TAG('a','l','m', 0 ), // alm = Amblong
+ TRUETYPE_TAG('a','l','n', 0 ), // aln = Gheg Albanian
+ TRUETYPE_TAG('a','l','o', 0 ), // alo = Larike-Wakasihu
+ TRUETYPE_TAG('a','l','p', 0 ), // alp = Alune
+ TRUETYPE_TAG('a','l','q', 0 ), // alq = Algonquin
+ TRUETYPE_TAG('a','l','r', 0 ), // alr = Alutor
+ TRUETYPE_TAG('a','l','s', 0 ), // als = Tosk Albanian
+ TRUETYPE_TAG('a','l','t', 0 ), // alt = Southern Altai
+ TRUETYPE_TAG('a','l','u', 0 ), // alu = 'Are'are
+ TRUETYPE_TAG('a','l','v', 0 ), // alv = Atlantic-Congo languages
+ TRUETYPE_TAG('a','l','w', 0 ), // alw = Alaba-K’abeena
+ TRUETYPE_TAG('a','l','x', 0 ), // alx = Amol
+ TRUETYPE_TAG('a','l','y', 0 ), // aly = Alyawarr
+ TRUETYPE_TAG('a','l','z', 0 ), // alz = Alur
+ TRUETYPE_TAG('a','m','a', 0 ), // ama = Amanayé
+ TRUETYPE_TAG('a','m','b', 0 ), // amb = Ambo
+ TRUETYPE_TAG('a','m','c', 0 ), // amc = Amahuaca
+ TRUETYPE_TAG('a','m','e', 0 ), // ame = Yanesha'
+ TRUETYPE_TAG('a','m','f', 0 ), // amf = Hamer-Banna
+ TRUETYPE_TAG('a','m','g', 0 ), // amg = Amarag
+ TRUETYPE_TAG('a','m','i', 0 ), // ami = Amis
+ TRUETYPE_TAG('a','m','j', 0 ), // amj = Amdang
+ TRUETYPE_TAG('a','m','k', 0 ), // amk = Ambai
+ TRUETYPE_TAG('a','m','l', 0 ), // aml = War-Jaintia
+ TRUETYPE_TAG('a','m','m', 0 ), // amm = Ama (Papua New Guinea)
+ TRUETYPE_TAG('a','m','n', 0 ), // amn = Amanab
+ TRUETYPE_TAG('a','m','o', 0 ), // amo = Amo
+ TRUETYPE_TAG('a','m','p', 0 ), // amp = Alamblak
+ TRUETYPE_TAG('a','m','q', 0 ), // amq = Amahai
+ TRUETYPE_TAG('a','m','r', 0 ), // amr = Amarakaeri
+ TRUETYPE_TAG('a','m','s', 0 ), // ams = Southern Amami-Oshima
+ TRUETYPE_TAG('a','m','t', 0 ), // amt = Amto
+ TRUETYPE_TAG('a','m','u', 0 ), // amu = Guerrero Amuzgo
+ TRUETYPE_TAG('a','m','v', 0 ), // amv = Ambelau
+ TRUETYPE_TAG('a','m','w', 0 ), // amw = Western Neo-Aramaic
+ TRUETYPE_TAG('a','m','x', 0 ), // amx = Anmatyerre
+ TRUETYPE_TAG('a','m','y', 0 ), // amy = Ami
+ TRUETYPE_TAG('a','m','z', 0 ), // amz = Atampaya
+ TRUETYPE_TAG('a','n','a', 0 ), // ana = Andaqui
+ TRUETYPE_TAG('a','n','b', 0 ), // anb = Andoa
+ TRUETYPE_TAG('a','n','c', 0 ), // anc = Ngas
+ TRUETYPE_TAG('a','n','d', 0 ), // and = Ansus
+ TRUETYPE_TAG('a','n','e', 0 ), // ane = Xârâcùù
+ TRUETYPE_TAG('a','n','f', 0 ), // anf = Animere
+ TRUETYPE_TAG('a','n','g', 0 ), // ang = Old English (ca. 450-1100)
+ TRUETYPE_TAG('a','n','h', 0 ), // anh = Nend
+ TRUETYPE_TAG('a','n','i', 0 ), // ani = Andi
+ TRUETYPE_TAG('a','n','j', 0 ), // anj = Anor
+ TRUETYPE_TAG('a','n','k', 0 ), // ank = Goemai
+ TRUETYPE_TAG('a','n','l', 0 ), // anl = Anu
+ TRUETYPE_TAG('a','n','m', 0 ), // anm = Anal
+ TRUETYPE_TAG('a','n','n', 0 ), // ann = Obolo
+ TRUETYPE_TAG('a','n','o', 0 ), // ano = Andoque
+ TRUETYPE_TAG('a','n','p', 0 ), // anp = Angika
+ TRUETYPE_TAG('a','n','q', 0 ), // anq = Jarawa (India)
+ TRUETYPE_TAG('a','n','r', 0 ), // anr = Andh
+ TRUETYPE_TAG('a','n','s', 0 ), // ans = Anserma
+ TRUETYPE_TAG('a','n','t', 0 ), // ant = Antakarinya
+ TRUETYPE_TAG('a','n','u', 0 ), // anu = Anuak
+ TRUETYPE_TAG('a','n','v', 0 ), // anv = Denya
+ TRUETYPE_TAG('a','n','w', 0 ), // anw = Anaang
+ TRUETYPE_TAG('a','n','x', 0 ), // anx = Andra-Hus
+ TRUETYPE_TAG('a','n','y', 0 ), // any = Anyin
+ TRUETYPE_TAG('a','n','z', 0 ), // anz = Anem
+ TRUETYPE_TAG('a','o','a', 0 ), // aoa = Angolar
+ TRUETYPE_TAG('a','o','b', 0 ), // aob = Abom
+ TRUETYPE_TAG('a','o','c', 0 ), // aoc = Pemon
+ TRUETYPE_TAG('a','o','d', 0 ), // aod = Andarum
+ TRUETYPE_TAG('a','o','e', 0 ), // aoe = Angal Enen
+ TRUETYPE_TAG('a','o','f', 0 ), // aof = Bragat
+ TRUETYPE_TAG('a','o','g', 0 ), // aog = Angoram
+ TRUETYPE_TAG('a','o','h', 0 ), // aoh = Arma
+ TRUETYPE_TAG('a','o','i', 0 ), // aoi = Anindilyakwa
+ TRUETYPE_TAG('a','o','j', 0 ), // aoj = Mufian
+ TRUETYPE_TAG('a','o','k', 0 ), // aok = Arhö
+ TRUETYPE_TAG('a','o','l', 0 ), // aol = Alor
+ TRUETYPE_TAG('a','o','m', 0 ), // aom = Ömie
+ TRUETYPE_TAG('a','o','n', 0 ), // aon = Bumbita Arapesh
+ TRUETYPE_TAG('a','o','r', 0 ), // aor = Aore
+ TRUETYPE_TAG('a','o','s', 0 ), // aos = Taikat
+ TRUETYPE_TAG('a','o','t', 0 ), // aot = A'tong
+ TRUETYPE_TAG('a','o','x', 0 ), // aox = Atorada
+ TRUETYPE_TAG('a','o','z', 0 ), // aoz = Uab Meto
+ TRUETYPE_TAG('a','p','a', 0 ), // apa = Apache languages
+ TRUETYPE_TAG('a','p','b', 0 ), // apb = Sa'a
+ TRUETYPE_TAG('a','p','c', 0 ), // apc = North Levantine Arabic
+ TRUETYPE_TAG('a','p','d', 0 ), // apd = Sudanese Arabic
+ TRUETYPE_TAG('a','p','e', 0 ), // ape = Bukiyip
+ TRUETYPE_TAG('a','p','f', 0 ), // apf = Pahanan Agta
+ TRUETYPE_TAG('a','p','g', 0 ), // apg = Ampanang
+ TRUETYPE_TAG('a','p','h', 0 ), // aph = Athpariya
+ TRUETYPE_TAG('a','p','i', 0 ), // api = Apiaká
+ TRUETYPE_TAG('a','p','j', 0 ), // apj = Jicarilla Apache
+ TRUETYPE_TAG('a','p','k', 0 ), // apk = Kiowa Apache
+ TRUETYPE_TAG('a','p','l', 0 ), // apl = Lipan Apache
+ TRUETYPE_TAG('a','p','m', 0 ), // apm = Mescalero-Chiricahua Apache
+ TRUETYPE_TAG('a','p','n', 0 ), // apn = Apinayé
+ TRUETYPE_TAG('a','p','o', 0 ), // apo = Ambul
+ TRUETYPE_TAG('a','p','p', 0 ), // app = Apma
+ TRUETYPE_TAG('a','p','q', 0 ), // apq = A-Pucikwar
+ TRUETYPE_TAG('a','p','r', 0 ), // apr = Arop-Lokep
+ TRUETYPE_TAG('a','p','s', 0 ), // aps = Arop-Sissano
+ TRUETYPE_TAG('a','p','t', 0 ), // apt = Apatani
+ TRUETYPE_TAG('a','p','u', 0 ), // apu = Apurinã
+ TRUETYPE_TAG('a','p','v', 0 ), // apv = Alapmunte
+ TRUETYPE_TAG('a','p','w', 0 ), // apw = Western Apache
+ TRUETYPE_TAG('a','p','x', 0 ), // apx = Aputai
+ TRUETYPE_TAG('a','p','y', 0 ), // apy = Apalaí
+ TRUETYPE_TAG('a','p','z', 0 ), // apz = Safeyoka
+ TRUETYPE_TAG('a','q','a', 0 ), // aqa = Alacalufan languages
+ TRUETYPE_TAG('a','q','c', 0 ), // aqc = Archi
+ TRUETYPE_TAG('a','q','d', 0 ), // aqd = Ampari Dogon
+ TRUETYPE_TAG('a','q','g', 0 ), // aqg = Arigidi
+ TRUETYPE_TAG('a','q','l', 0 ), // aql = Algic languages
+ TRUETYPE_TAG('a','q','m', 0 ), // aqm = Atohwaim
+ TRUETYPE_TAG('a','q','n', 0 ), // aqn = Northern Alta
+ TRUETYPE_TAG('a','q','p', 0 ), // aqp = Atakapa
+ TRUETYPE_TAG('a','q','r', 0 ), // aqr = Arhâ
+ TRUETYPE_TAG('a','q','z', 0 ), // aqz = Akuntsu
+ TRUETYPE_TAG('a','r','b', 0 ), // arb = Standard Arabic
+ TRUETYPE_TAG('a','r','c', 0 ), // arc = Official Aramaic (700-300 BCE)
+ TRUETYPE_TAG('a','r','d', 0 ), // ard = Arabana
+ TRUETYPE_TAG('a','r','e', 0 ), // are = Western Arrarnta
+ TRUETYPE_TAG('a','r','h', 0 ), // arh = Arhuaco
+ TRUETYPE_TAG('a','r','i', 0 ), // ari = Arikara
+ TRUETYPE_TAG('a','r','j', 0 ), // arj = Arapaso
+ TRUETYPE_TAG('a','r','k', 0 ), // ark = Arikapú
+ TRUETYPE_TAG('a','r','l', 0 ), // arl = Arabela
+ TRUETYPE_TAG('a','r','n', 0 ), // arn = Mapudungun
+ TRUETYPE_TAG('a','r','o', 0 ), // aro = Araona
+ TRUETYPE_TAG('a','r','p', 0 ), // arp = Arapaho
+ TRUETYPE_TAG('a','r','q', 0 ), // arq = Algerian Arabic
+ TRUETYPE_TAG('a','r','r', 0 ), // arr = Karo (Brazil)
+ TRUETYPE_TAG('a','r','s', 0 ), // ars = Najdi Arabic
+ TRUETYPE_TAG('a','r','t', 0 ), // art = Artificial languages
+ TRUETYPE_TAG('a','r','u', 0 ), // aru = Aruá (Amazonas State)
+ TRUETYPE_TAG('a','r','v', 0 ), // arv = Arbore
+ TRUETYPE_TAG('a','r','w', 0 ), // arw = Arawak
+ TRUETYPE_TAG('a','r','x', 0 ), // arx = Aruá (Rodonia State)
+ TRUETYPE_TAG('a','r','y', 0 ), // ary = Moroccan Arabic
+ TRUETYPE_TAG('a','r','z', 0 ), // arz = Egyptian Arabic
+ TRUETYPE_TAG('a','s','a', 0 ), // asa = Asu (Tanzania)
+ TRUETYPE_TAG('a','s','b', 0 ), // asb = Assiniboine
+ TRUETYPE_TAG('a','s','c', 0 ), // asc = Casuarina Coast Asmat
+ TRUETYPE_TAG('a','s','d', 0 ), // asd = Asas
+ TRUETYPE_TAG('a','s','e', 0 ), // ase = American Sign Language
+ TRUETYPE_TAG('a','s','f', 0 ), // asf = Australian Sign Language
+ TRUETYPE_TAG('a','s','g', 0 ), // asg = Cishingini
+ TRUETYPE_TAG('a','s','h', 0 ), // ash = Abishira
+ TRUETYPE_TAG('a','s','i', 0 ), // asi = Buruwai
+ TRUETYPE_TAG('a','s','j', 0 ), // asj = Nsari
+ TRUETYPE_TAG('a','s','k', 0 ), // ask = Ashkun
+ TRUETYPE_TAG('a','s','l', 0 ), // asl = Asilulu
+ TRUETYPE_TAG('a','s','n', 0 ), // asn = Xingú Asuriní
+ TRUETYPE_TAG('a','s','o', 0 ), // aso = Dano
+ TRUETYPE_TAG('a','s','p', 0 ), // asp = Algerian Sign Language
+ TRUETYPE_TAG('a','s','q', 0 ), // asq = Austrian Sign Language
+ TRUETYPE_TAG('a','s','r', 0 ), // asr = Asuri
+ TRUETYPE_TAG('a','s','s', 0 ), // ass = Ipulo
+ TRUETYPE_TAG('a','s','t', 0 ), // ast = Asturian
+ TRUETYPE_TAG('a','s','u', 0 ), // asu = Tocantins Asurini
+ TRUETYPE_TAG('a','s','v', 0 ), // asv = Asoa
+ TRUETYPE_TAG('a','s','w', 0 ), // asw = Australian Aborigines Sign Language
+ TRUETYPE_TAG('a','s','x', 0 ), // asx = Muratayak
+ TRUETYPE_TAG('a','s','y', 0 ), // asy = Yaosakor Asmat
+ TRUETYPE_TAG('a','s','z', 0 ), // asz = As
+ TRUETYPE_TAG('a','t','a', 0 ), // ata = Pele-Ata
+ TRUETYPE_TAG('a','t','b', 0 ), // atb = Zaiwa
+ TRUETYPE_TAG('a','t','c', 0 ), // atc = Atsahuaca
+ TRUETYPE_TAG('a','t','d', 0 ), // atd = Ata Manobo
+ TRUETYPE_TAG('a','t','e', 0 ), // ate = Atemble
+ TRUETYPE_TAG('a','t','g', 0 ), // atg = Ivbie North-Okpela-Arhe
+ TRUETYPE_TAG('a','t','h', 0 ), // ath = Athapascan languages
+ TRUETYPE_TAG('a','t','i', 0 ), // ati = Attié
+ TRUETYPE_TAG('a','t','j', 0 ), // atj = Atikamekw
+ TRUETYPE_TAG('a','t','k', 0 ), // atk = Ati
+ TRUETYPE_TAG('a','t','l', 0 ), // atl = Mt. Iraya Agta
+ TRUETYPE_TAG('a','t','m', 0 ), // atm = Ata
+ TRUETYPE_TAG('a','t','n', 0 ), // atn = Ashtiani
+ TRUETYPE_TAG('a','t','o', 0 ), // ato = Atong
+ TRUETYPE_TAG('a','t','p', 0 ), // atp = Pudtol Atta
+ TRUETYPE_TAG('a','t','q', 0 ), // atq = Aralle-Tabulahan
+ TRUETYPE_TAG('a','t','r', 0 ), // atr = Waimiri-Atroari
+ TRUETYPE_TAG('a','t','s', 0 ), // ats = Gros Ventre
+ TRUETYPE_TAG('a','t','t', 0 ), // att = Pamplona Atta
+ TRUETYPE_TAG('a','t','u', 0 ), // atu = Reel
+ TRUETYPE_TAG('a','t','v', 0 ), // atv = Northern Altai
+ TRUETYPE_TAG('a','t','w', 0 ), // atw = Atsugewi
+ TRUETYPE_TAG('a','t','x', 0 ), // atx = Arutani
+ TRUETYPE_TAG('a','t','y', 0 ), // aty = Aneityum
+ TRUETYPE_TAG('a','t','z', 0 ), // atz = Arta
+ TRUETYPE_TAG('a','u','a', 0 ), // aua = Asumboa
+ TRUETYPE_TAG('a','u','b', 0 ), // aub = Alugu
+ TRUETYPE_TAG('a','u','c', 0 ), // auc = Waorani
+ TRUETYPE_TAG('a','u','d', 0 ), // aud = Anuta
+ TRUETYPE_TAG('a','u','e', 0 ), // aue = =/Kx'au//'ein
+ TRUETYPE_TAG('a','u','f', 0 ), // auf = Arauan languages
+ TRUETYPE_TAG('a','u','g', 0 ), // aug = Aguna
+ TRUETYPE_TAG('a','u','h', 0 ), // auh = Aushi
+ TRUETYPE_TAG('a','u','i', 0 ), // aui = Anuki
+ TRUETYPE_TAG('a','u','j', 0 ), // auj = Awjilah
+ TRUETYPE_TAG('a','u','k', 0 ), // auk = Heyo
+ TRUETYPE_TAG('a','u','l', 0 ), // aul = Aulua
+ TRUETYPE_TAG('a','u','m', 0 ), // aum = Asu (Nigeria)
+ TRUETYPE_TAG('a','u','n', 0 ), // aun = Molmo One
+ TRUETYPE_TAG('a','u','o', 0 ), // auo = Auyokawa
+ TRUETYPE_TAG('a','u','p', 0 ), // aup = Makayam
+ TRUETYPE_TAG('a','u','q', 0 ), // auq = Anus
+ TRUETYPE_TAG('a','u','r', 0 ), // aur = Aruek
+ TRUETYPE_TAG('a','u','s', 0 ), // aus = Australian languages
+ TRUETYPE_TAG('a','u','t', 0 ), // aut = Austral
+ TRUETYPE_TAG('a','u','u', 0 ), // auu = Auye
+ TRUETYPE_TAG('a','u','w', 0 ), // auw = Awyi
+ TRUETYPE_TAG('a','u','x', 0 ), // aux = Aurá
+ TRUETYPE_TAG('a','u','y', 0 ), // auy = Awiyaana
+ TRUETYPE_TAG('a','u','z', 0 ), // auz = Uzbeki Arabic
+ TRUETYPE_TAG('a','v','b', 0 ), // avb = Avau
+ TRUETYPE_TAG('a','v','d', 0 ), // avd = Alviri-Vidari
+ TRUETYPE_TAG('a','v','i', 0 ), // avi = Avikam
+ TRUETYPE_TAG('a','v','k', 0 ), // avk = Kotava
+ TRUETYPE_TAG('a','v','l', 0 ), // avl = Eastern Egyptian Bedawi Arabic
+ TRUETYPE_TAG('a','v','n', 0 ), // avn = Avatime
+ TRUETYPE_TAG('a','v','o', 0 ), // avo = Agavotaguerra
+ TRUETYPE_TAG('a','v','s', 0 ), // avs = Aushiri
+ TRUETYPE_TAG('a','v','t', 0 ), // avt = Au
+ TRUETYPE_TAG('a','v','u', 0 ), // avu = Avokaya
+ TRUETYPE_TAG('a','v','v', 0 ), // avv = Avá-Canoeiro
+ TRUETYPE_TAG('a','w','a', 0 ), // awa = Awadhi
+ TRUETYPE_TAG('a','w','b', 0 ), // awb = Awa (Papua New Guinea)
+ TRUETYPE_TAG('a','w','c', 0 ), // awc = Cicipu
+ TRUETYPE_TAG('a','w','d', 0 ), // awd = Arawakan languages
+ TRUETYPE_TAG('a','w','e', 0 ), // awe = Awetí
+ TRUETYPE_TAG('a','w','h', 0 ), // awh = Awbono
+ TRUETYPE_TAG('a','w','i', 0 ), // awi = Aekyom
+ TRUETYPE_TAG('a','w','k', 0 ), // awk = Awabakal
+ TRUETYPE_TAG('a','w','m', 0 ), // awm = Arawum
+ TRUETYPE_TAG('a','w','n', 0 ), // awn = Awngi
+ TRUETYPE_TAG('a','w','o', 0 ), // awo = Awak
+ TRUETYPE_TAG('a','w','r', 0 ), // awr = Awera
+ TRUETYPE_TAG('a','w','s', 0 ), // aws = South Awyu
+ TRUETYPE_TAG('a','w','t', 0 ), // awt = Araweté
+ TRUETYPE_TAG('a','w','u', 0 ), // awu = Central Awyu
+ TRUETYPE_TAG('a','w','v', 0 ), // awv = Jair Awyu
+ TRUETYPE_TAG('a','w','w', 0 ), // aww = Awun
+ TRUETYPE_TAG('a','w','x', 0 ), // awx = Awara
+ TRUETYPE_TAG('a','w','y', 0 ), // awy = Edera Awyu
+ TRUETYPE_TAG('a','x','b', 0 ), // axb = Abipon
+ TRUETYPE_TAG('a','x','g', 0 ), // axg = Mato Grosso Arára
+ TRUETYPE_TAG('a','x','k', 0 ), // axk = Yaka (Central African Republic)
+ TRUETYPE_TAG('a','x','m', 0 ), // axm = Middle Armenian
+ TRUETYPE_TAG('a','x','x', 0 ), // axx = Xaragure
+ TRUETYPE_TAG('a','y','a', 0 ), // aya = Awar
+ TRUETYPE_TAG('a','y','b', 0 ), // ayb = Ayizo Gbe
+ TRUETYPE_TAG('a','y','c', 0 ), // ayc = Southern Aymara
+ TRUETYPE_TAG('a','y','d', 0 ), // ayd = Ayabadhu
+ TRUETYPE_TAG('a','y','e', 0 ), // aye = Ayere
+ TRUETYPE_TAG('a','y','g', 0 ), // ayg = Ginyanga
+ TRUETYPE_TAG('a','y','h', 0 ), // ayh = Hadrami Arabic
+ TRUETYPE_TAG('a','y','i', 0 ), // ayi = Leyigha
+ TRUETYPE_TAG('a','y','k', 0 ), // ayk = Akuku
+ TRUETYPE_TAG('a','y','l', 0 ), // ayl = Libyan Arabic
+ TRUETYPE_TAG('a','y','n', 0 ), // ayn = Sanaani Arabic
+ TRUETYPE_TAG('a','y','o', 0 ), // ayo = Ayoreo
+ TRUETYPE_TAG('a','y','p', 0 ), // ayp = North Mesopotamian Arabic
+ TRUETYPE_TAG('a','y','q', 0 ), // ayq = Ayi (Papua New Guinea)
+ TRUETYPE_TAG('a','y','r', 0 ), // ayr = Central Aymara
+ TRUETYPE_TAG('a','y','s', 0 ), // ays = Sorsogon Ayta
+ TRUETYPE_TAG('a','y','t', 0 ), // ayt = Magbukun Ayta
+ TRUETYPE_TAG('a','y','u', 0 ), // ayu = Ayu
+ TRUETYPE_TAG('a','y','x', 0 ), // ayx = Ayi (China)
+ TRUETYPE_TAG('a','y','y', 0 ), // ayy = Tayabas Ayta
+ TRUETYPE_TAG('a','y','z', 0 ), // ayz = Mai Brat
+ TRUETYPE_TAG('a','z','a', 0 ), // aza = Azha
+ TRUETYPE_TAG('a','z','b', 0 ), // azb = South Azerbaijani
+ TRUETYPE_TAG('a','z','c', 0 ), // azc = Uto-Aztecan languages
+ TRUETYPE_TAG('a','z','g', 0 ), // azg = San Pedro Amuzgos Amuzgo
+ TRUETYPE_TAG('a','z','j', 0 ), // azj = North Azerbaijani
+ TRUETYPE_TAG('a','z','m', 0 ), // azm = Ipalapa Amuzgo
+ TRUETYPE_TAG('a','z','o', 0 ), // azo = Awing
+ TRUETYPE_TAG('a','z','t', 0 ), // azt = Faire Atta
+ TRUETYPE_TAG('a','z','z', 0 ), // azz = Highland Puebla Nahuatl
+ TRUETYPE_TAG('b','a','a', 0 ), // baa = Babatana
+ TRUETYPE_TAG('b','a','b', 0 ), // bab = Bainouk-Gunyuño
+ TRUETYPE_TAG('b','a','c', 0 ), // bac = Badui
+ TRUETYPE_TAG('b','a','d', 0 ), // bad = Banda languages
+ TRUETYPE_TAG('b','a','e', 0 ), // bae = Baré
+ TRUETYPE_TAG('b','a','f', 0 ), // baf = Nubaca
+ TRUETYPE_TAG('b','a','g', 0 ), // bag = Tuki
+ TRUETYPE_TAG('b','a','h', 0 ), // bah = Bahamas Creole English
+ TRUETYPE_TAG('b','a','i', 0 ), // bai = Bamileke languages
+ TRUETYPE_TAG('b','a','j', 0 ), // baj = Barakai
+ TRUETYPE_TAG('b','a','l', 0 ), // bal = Baluchi
+ TRUETYPE_TAG('b','a','n', 0 ), // ban = Balinese
+ TRUETYPE_TAG('b','a','o', 0 ), // bao = Waimaha
+ TRUETYPE_TAG('b','a','p', 0 ), // bap = Bantawa
+ TRUETYPE_TAG('b','a','r', 0 ), // bar = Bavarian
+ TRUETYPE_TAG('b','a','s', 0 ), // bas = Basa (Cameroon)
+ TRUETYPE_TAG('b','a','t', 0 ), // bat = Baltic languages
+ TRUETYPE_TAG('b','a','u', 0 ), // bau = Bada (Nigeria)
+ TRUETYPE_TAG('b','a','v', 0 ), // bav = Vengo
+ TRUETYPE_TAG('b','a','w', 0 ), // baw = Bambili-Bambui
+ TRUETYPE_TAG('b','a','x', 0 ), // bax = Bamun
+ TRUETYPE_TAG('b','a','y', 0 ), // bay = Batuley
+ TRUETYPE_TAG('b','a','z', 0 ), // baz = Tunen
+ TRUETYPE_TAG('b','b','a', 0 ), // bba = Baatonum
+ TRUETYPE_TAG('b','b','b', 0 ), // bbb = Barai
+ TRUETYPE_TAG('b','b','c', 0 ), // bbc = Batak Toba
+ TRUETYPE_TAG('b','b','d', 0 ), // bbd = Bau
+ TRUETYPE_TAG('b','b','e', 0 ), // bbe = Bangba
+ TRUETYPE_TAG('b','b','f', 0 ), // bbf = Baibai
+ TRUETYPE_TAG('b','b','g', 0 ), // bbg = Barama
+ TRUETYPE_TAG('b','b','h', 0 ), // bbh = Bugan
+ TRUETYPE_TAG('b','b','i', 0 ), // bbi = Barombi
+ TRUETYPE_TAG('b','b','j', 0 ), // bbj = Ghomálá'
+ TRUETYPE_TAG('b','b','k', 0 ), // bbk = Babanki
+ TRUETYPE_TAG('b','b','l', 0 ), // bbl = Bats
+ TRUETYPE_TAG('b','b','m', 0 ), // bbm = Babango
+ TRUETYPE_TAG('b','b','n', 0 ), // bbn = Uneapa
+ TRUETYPE_TAG('b','b','o', 0 ), // bbo = Northern Bobo Madaré
+ TRUETYPE_TAG('b','b','p', 0 ), // bbp = West Central Banda
+ TRUETYPE_TAG('b','b','q', 0 ), // bbq = Bamali
+ TRUETYPE_TAG('b','b','r', 0 ), // bbr = Girawa
+ TRUETYPE_TAG('b','b','s', 0 ), // bbs = Bakpinka
+ TRUETYPE_TAG('b','b','t', 0 ), // bbt = Mburku
+ TRUETYPE_TAG('b','b','u', 0 ), // bbu = Kulung (Nigeria)
+ TRUETYPE_TAG('b','b','v', 0 ), // bbv = Karnai
+ TRUETYPE_TAG('b','b','w', 0 ), // bbw = Baba
+ TRUETYPE_TAG('b','b','x', 0 ), // bbx = Bubia
+ TRUETYPE_TAG('b','b','y', 0 ), // bby = Befang
+ TRUETYPE_TAG('b','b','z', 0 ), // bbz = Babalia Creole Arabic
+ TRUETYPE_TAG('b','c','a', 0 ), // bca = Central Bai
+ TRUETYPE_TAG('b','c','b', 0 ), // bcb = Bainouk-Samik
+ TRUETYPE_TAG('b','c','c', 0 ), // bcc = Southern Balochi
+ TRUETYPE_TAG('b','c','d', 0 ), // bcd = North Babar
+ TRUETYPE_TAG('b','c','e', 0 ), // bce = Bamenyam
+ TRUETYPE_TAG('b','c','f', 0 ), // bcf = Bamu
+ TRUETYPE_TAG('b','c','g', 0 ), // bcg = Baga Binari
+ TRUETYPE_TAG('b','c','h', 0 ), // bch = Bariai
+ TRUETYPE_TAG('b','c','i', 0 ), // bci = Baoulé
+ TRUETYPE_TAG('b','c','j', 0 ), // bcj = Bardi
+ TRUETYPE_TAG('b','c','k', 0 ), // bck = Bunaba
+ TRUETYPE_TAG('b','c','l', 0 ), // bcl = Central Bicolano
+ TRUETYPE_TAG('b','c','m', 0 ), // bcm = Bannoni
+ TRUETYPE_TAG('b','c','n', 0 ), // bcn = Bali (Nigeria)
+ TRUETYPE_TAG('b','c','o', 0 ), // bco = Kaluli
+ TRUETYPE_TAG('b','c','p', 0 ), // bcp = Bali (Democratic Republic of Congo)
+ TRUETYPE_TAG('b','c','q', 0 ), // bcq = Bench
+ TRUETYPE_TAG('b','c','r', 0 ), // bcr = Babine
+ TRUETYPE_TAG('b','c','s', 0 ), // bcs = Kohumono
+ TRUETYPE_TAG('b','c','t', 0 ), // bct = Bendi
+ TRUETYPE_TAG('b','c','u', 0 ), // bcu = Awad Bing
+ TRUETYPE_TAG('b','c','v', 0 ), // bcv = Shoo-Minda-Nye
+ TRUETYPE_TAG('b','c','w', 0 ), // bcw = Bana
+ TRUETYPE_TAG('b','c','y', 0 ), // bcy = Bacama
+ TRUETYPE_TAG('b','c','z', 0 ), // bcz = Bainouk-Gunyaamolo
+ TRUETYPE_TAG('b','d','a', 0 ), // bda = Bayot
+ TRUETYPE_TAG('b','d','b', 0 ), // bdb = Basap
+ TRUETYPE_TAG('b','d','c', 0 ), // bdc = Emberá-Baudó
+ TRUETYPE_TAG('b','d','d', 0 ), // bdd = Bunama
+ TRUETYPE_TAG('b','d','e', 0 ), // bde = Bade
+ TRUETYPE_TAG('b','d','f', 0 ), // bdf = Biage
+ TRUETYPE_TAG('b','d','g', 0 ), // bdg = Bonggi
+ TRUETYPE_TAG('b','d','h', 0 ), // bdh = Baka (Sudan)
+ TRUETYPE_TAG('b','d','i', 0 ), // bdi = Burun
+ TRUETYPE_TAG('b','d','j', 0 ), // bdj = Bai
+ TRUETYPE_TAG('b','d','k', 0 ), // bdk = Budukh
+ TRUETYPE_TAG('b','d','l', 0 ), // bdl = Indonesian Bajau
+ TRUETYPE_TAG('b','d','m', 0 ), // bdm = Buduma
+ TRUETYPE_TAG('b','d','n', 0 ), // bdn = Baldemu
+ TRUETYPE_TAG('b','d','o', 0 ), // bdo = Morom
+ TRUETYPE_TAG('b','d','p', 0 ), // bdp = Bende
+ TRUETYPE_TAG('b','d','q', 0 ), // bdq = Bahnar
+ TRUETYPE_TAG('b','d','r', 0 ), // bdr = West Coast Bajau
+ TRUETYPE_TAG('b','d','s', 0 ), // bds = Burunge
+ TRUETYPE_TAG('b','d','t', 0 ), // bdt = Bokoto
+ TRUETYPE_TAG('b','d','u', 0 ), // bdu = Oroko
+ TRUETYPE_TAG('b','d','v', 0 ), // bdv = Bodo Parja
+ TRUETYPE_TAG('b','d','w', 0 ), // bdw = Baham
+ TRUETYPE_TAG('b','d','x', 0 ), // bdx = Budong-Budong
+ TRUETYPE_TAG('b','d','y', 0 ), // bdy = Bandjalang
+ TRUETYPE_TAG('b','d','z', 0 ), // bdz = Badeshi
+ TRUETYPE_TAG('b','e','a', 0 ), // bea = Beaver
+ TRUETYPE_TAG('b','e','b', 0 ), // beb = Bebele
+ TRUETYPE_TAG('b','e','c', 0 ), // bec = Iceve-Maci
+ TRUETYPE_TAG('b','e','d', 0 ), // bed = Bedoanas
+ TRUETYPE_TAG('b','e','e', 0 ), // bee = Byangsi
+ TRUETYPE_TAG('b','e','f', 0 ), // bef = Benabena
+ TRUETYPE_TAG('b','e','g', 0 ), // beg = Belait
+ TRUETYPE_TAG('b','e','h', 0 ), // beh = Biali
+ TRUETYPE_TAG('b','e','i', 0 ), // bei = Bekati'
+ TRUETYPE_TAG('b','e','j', 0 ), // bej = Beja
+ TRUETYPE_TAG('b','e','k', 0 ), // bek = Bebeli
+ TRUETYPE_TAG('b','e','m', 0 ), // bem = Bemba (Zambia)
+ TRUETYPE_TAG('b','e','o', 0 ), // beo = Beami
+ TRUETYPE_TAG('b','e','p', 0 ), // bep = Besoa
+ TRUETYPE_TAG('b','e','q', 0 ), // beq = Beembe
+ TRUETYPE_TAG('b','e','r', 0 ), // ber = Berber languages
+ TRUETYPE_TAG('b','e','s', 0 ), // bes = Besme
+ TRUETYPE_TAG('b','e','t', 0 ), // bet = Guiberoua Béte
+ TRUETYPE_TAG('b','e','u', 0 ), // beu = Blagar
+ TRUETYPE_TAG('b','e','v', 0 ), // bev = Daloa Bété
+ TRUETYPE_TAG('b','e','w', 0 ), // bew = Betawi
+ TRUETYPE_TAG('b','e','x', 0 ), // bex = Jur Modo
+ TRUETYPE_TAG('b','e','y', 0 ), // bey = Beli (Papua New Guinea)
+ TRUETYPE_TAG('b','e','z', 0 ), // bez = Bena (Tanzania)
+ TRUETYPE_TAG('b','f','a', 0 ), // bfa = Bari
+ TRUETYPE_TAG('b','f','b', 0 ), // bfb = Pauri Bareli
+ TRUETYPE_TAG('b','f','c', 0 ), // bfc = Northern Bai
+ TRUETYPE_TAG('b','f','d', 0 ), // bfd = Bafut
+ TRUETYPE_TAG('b','f','e', 0 ), // bfe = Betaf
+ TRUETYPE_TAG('b','f','f', 0 ), // bff = Bofi
+ TRUETYPE_TAG('b','f','g', 0 ), // bfg = Busang Kayan
+ TRUETYPE_TAG('b','f','h', 0 ), // bfh = Blafe
+ TRUETYPE_TAG('b','f','i', 0 ), // bfi = British Sign Language
+ TRUETYPE_TAG('b','f','j', 0 ), // bfj = Bafanji
+ TRUETYPE_TAG('b','f','k', 0 ), // bfk = Ban Khor Sign Language
+ TRUETYPE_TAG('b','f','l', 0 ), // bfl = Banda-Ndélé
+ TRUETYPE_TAG('b','f','m', 0 ), // bfm = Mmen
+ TRUETYPE_TAG('b','f','n', 0 ), // bfn = Bunak
+ TRUETYPE_TAG('b','f','o', 0 ), // bfo = Malba Birifor
+ TRUETYPE_TAG('b','f','p', 0 ), // bfp = Beba
+ TRUETYPE_TAG('b','f','q', 0 ), // bfq = Badaga
+ TRUETYPE_TAG('b','f','r', 0 ), // bfr = Bazigar
+ TRUETYPE_TAG('b','f','s', 0 ), // bfs = Southern Bai
+ TRUETYPE_TAG('b','f','t', 0 ), // bft = Balti
+ TRUETYPE_TAG('b','f','u', 0 ), // bfu = Gahri
+ TRUETYPE_TAG('b','f','w', 0 ), // bfw = Bondo
+ TRUETYPE_TAG('b','f','x', 0 ), // bfx = Bantayanon
+ TRUETYPE_TAG('b','f','y', 0 ), // bfy = Bagheli
+ TRUETYPE_TAG('b','f','z', 0 ), // bfz = Mahasu Pahari
+ TRUETYPE_TAG('b','g','a', 0 ), // bga = Gwamhi-Wuri
+ TRUETYPE_TAG('b','g','b', 0 ), // bgb = Bobongko
+ TRUETYPE_TAG('b','g','c', 0 ), // bgc = Haryanvi
+ TRUETYPE_TAG('b','g','d', 0 ), // bgd = Rathwi Bareli
+ TRUETYPE_TAG('b','g','e', 0 ), // bge = Bauria
+ TRUETYPE_TAG('b','g','f', 0 ), // bgf = Bangandu
+ TRUETYPE_TAG('b','g','g', 0 ), // bgg = Bugun
+ TRUETYPE_TAG('b','g','i', 0 ), // bgi = Giangan
+ TRUETYPE_TAG('b','g','j', 0 ), // bgj = Bangolan
+ TRUETYPE_TAG('b','g','k', 0 ), // bgk = Bit
+ TRUETYPE_TAG('b','g','l', 0 ), // bgl = Bo (Laos)
+ TRUETYPE_TAG('b','g','m', 0 ), // bgm = Baga Mboteni
+ TRUETYPE_TAG('b','g','n', 0 ), // bgn = Western Balochi
+ TRUETYPE_TAG('b','g','o', 0 ), // bgo = Baga Koga
+ TRUETYPE_TAG('b','g','p', 0 ), // bgp = Eastern Balochi
+ TRUETYPE_TAG('b','g','q', 0 ), // bgq = Bagri
+ TRUETYPE_TAG('b','g','r', 0 ), // bgr = Bawm Chin
+ TRUETYPE_TAG('b','g','s', 0 ), // bgs = Tagabawa
+ TRUETYPE_TAG('b','g','t', 0 ), // bgt = Bughotu
+ TRUETYPE_TAG('b','g','u', 0 ), // bgu = Mbongno
+ TRUETYPE_TAG('b','g','v', 0 ), // bgv = Warkay-Bipim
+ TRUETYPE_TAG('b','g','w', 0 ), // bgw = Bhatri
+ TRUETYPE_TAG('b','g','x', 0 ), // bgx = Balkan Gagauz Turkish
+ TRUETYPE_TAG('b','g','y', 0 ), // bgy = Benggoi
+ TRUETYPE_TAG('b','g','z', 0 ), // bgz = Banggai
+ TRUETYPE_TAG('b','h','a', 0 ), // bha = Bharia
+ TRUETYPE_TAG('b','h','b', 0 ), // bhb = Bhili
+ TRUETYPE_TAG('b','h','c', 0 ), // bhc = Biga
+ TRUETYPE_TAG('b','h','d', 0 ), // bhd = Bhadrawahi
+ TRUETYPE_TAG('b','h','e', 0 ), // bhe = Bhaya
+ TRUETYPE_TAG('b','h','f', 0 ), // bhf = Odiai
+ TRUETYPE_TAG('b','h','g', 0 ), // bhg = Binandere
+ TRUETYPE_TAG('b','h','h', 0 ), // bhh = Bukharic
+ TRUETYPE_TAG('b','h','i', 0 ), // bhi = Bhilali
+ TRUETYPE_TAG('b','h','j', 0 ), // bhj = Bahing
+ TRUETYPE_TAG('b','h','k', 0 ), // bhk = Albay Bicolano
+ TRUETYPE_TAG('b','h','l', 0 ), // bhl = Bimin
+ TRUETYPE_TAG('b','h','m', 0 ), // bhm = Bathari
+ TRUETYPE_TAG('b','h','n', 0 ), // bhn = Bohtan Neo-Aramaic
+ TRUETYPE_TAG('b','h','o', 0 ), // bho = Bhojpuri
+ TRUETYPE_TAG('b','h','p', 0 ), // bhp = Bima
+ TRUETYPE_TAG('b','h','q', 0 ), // bhq = Tukang Besi South
+ TRUETYPE_TAG('b','h','r', 0 ), // bhr = Bara Malagasy
+ TRUETYPE_TAG('b','h','s', 0 ), // bhs = Buwal
+ TRUETYPE_TAG('b','h','t', 0 ), // bht = Bhattiyali
+ TRUETYPE_TAG('b','h','u', 0 ), // bhu = Bhunjia
+ TRUETYPE_TAG('b','h','v', 0 ), // bhv = Bahau
+ TRUETYPE_TAG('b','h','w', 0 ), // bhw = Biak
+ TRUETYPE_TAG('b','h','x', 0 ), // bhx = Bhalay
+ TRUETYPE_TAG('b','h','y', 0 ), // bhy = Bhele
+ TRUETYPE_TAG('b','h','z', 0 ), // bhz = Bada (Indonesia)
+ TRUETYPE_TAG('b','i','a', 0 ), // bia = Badimaya
+ TRUETYPE_TAG('b','i','b', 0 ), // bib = Bissa
+ TRUETYPE_TAG('b','i','c', 0 ), // bic = Bikaru
+ TRUETYPE_TAG('b','i','d', 0 ), // bid = Bidiyo
+ TRUETYPE_TAG('b','i','e', 0 ), // bie = Bepour
+ TRUETYPE_TAG('b','i','f', 0 ), // bif = Biafada
+ TRUETYPE_TAG('b','i','g', 0 ), // big = Biangai
+ TRUETYPE_TAG('b','i','j', 0 ), // bij = Vaghat-Ya-Bijim-Legeri
+ TRUETYPE_TAG('b','i','k', 0 ), // bik = Bikol
+ TRUETYPE_TAG('b','i','l', 0 ), // bil = Bile
+ TRUETYPE_TAG('b','i','m', 0 ), // bim = Bimoba
+ TRUETYPE_TAG('b','i','n', 0 ), // bin = Bini
+ TRUETYPE_TAG('b','i','o', 0 ), // bio = Nai
+ TRUETYPE_TAG('b','i','p', 0 ), // bip = Bila
+ TRUETYPE_TAG('b','i','q', 0 ), // biq = Bipi
+ TRUETYPE_TAG('b','i','r', 0 ), // bir = Bisorio
+ TRUETYPE_TAG('b','i','t', 0 ), // bit = Berinomo
+ TRUETYPE_TAG('b','i','u', 0 ), // biu = Biete
+ TRUETYPE_TAG('b','i','v', 0 ), // biv = Southern Birifor
+ TRUETYPE_TAG('b','i','w', 0 ), // biw = Kol (Cameroon)
+ TRUETYPE_TAG('b','i','x', 0 ), // bix = Bijori
+ TRUETYPE_TAG('b','i','y', 0 ), // biy = Birhor
+ TRUETYPE_TAG('b','i','z', 0 ), // biz = Baloi
+ TRUETYPE_TAG('b','j','a', 0 ), // bja = Budza
+ TRUETYPE_TAG('b','j','b', 0 ), // bjb = Banggarla
+ TRUETYPE_TAG('b','j','c', 0 ), // bjc = Bariji
+ TRUETYPE_TAG('b','j','d', 0 ), // bjd = Bandjigali
+ TRUETYPE_TAG('b','j','e', 0 ), // bje = Biao-Jiao Mien
+ TRUETYPE_TAG('b','j','f', 0 ), // bjf = Barzani Jewish Neo-Aramaic
+ TRUETYPE_TAG('b','j','g', 0 ), // bjg = Bidyogo
+ TRUETYPE_TAG('b','j','h', 0 ), // bjh = Bahinemo
+ TRUETYPE_TAG('b','j','i', 0 ), // bji = Burji
+ TRUETYPE_TAG('b','j','j', 0 ), // bjj = Kanauji
+ TRUETYPE_TAG('b','j','k', 0 ), // bjk = Barok
+ TRUETYPE_TAG('b','j','l', 0 ), // bjl = Bulu (Papua New Guinea)
+ TRUETYPE_TAG('b','j','m', 0 ), // bjm = Bajelani
+ TRUETYPE_TAG('b','j','n', 0 ), // bjn = Banjar
+ TRUETYPE_TAG('b','j','o', 0 ), // bjo = Mid-Southern Banda
+ TRUETYPE_TAG('b','j','q', 0 ), // bjq = Southern Betsimisaraka Malagasy
+ TRUETYPE_TAG('b','j','r', 0 ), // bjr = Binumarien
+ TRUETYPE_TAG('b','j','s', 0 ), // bjs = Bajan
+ TRUETYPE_TAG('b','j','t', 0 ), // bjt = Balanta-Ganja
+ TRUETYPE_TAG('b','j','u', 0 ), // bju = Busuu
+ TRUETYPE_TAG('b','j','v', 0 ), // bjv = Bedjond
+ TRUETYPE_TAG('b','j','w', 0 ), // bjw = Bakwé
+ TRUETYPE_TAG('b','j','x', 0 ), // bjx = Banao Itneg
+ TRUETYPE_TAG('b','j','y', 0 ), // bjy = Bayali
+ TRUETYPE_TAG('b','j','z', 0 ), // bjz = Baruga
+ TRUETYPE_TAG('b','k','a', 0 ), // bka = Kyak
+ TRUETYPE_TAG('b','k','b', 0 ), // bkb = Finallig
+ TRUETYPE_TAG('b','k','c', 0 ), // bkc = Baka (Cameroon)
+ TRUETYPE_TAG('b','k','d', 0 ), // bkd = Binukid
+ TRUETYPE_TAG('b','k','f', 0 ), // bkf = Beeke
+ TRUETYPE_TAG('b','k','g', 0 ), // bkg = Buraka
+ TRUETYPE_TAG('b','k','h', 0 ), // bkh = Bakoko
+ TRUETYPE_TAG('b','k','i', 0 ), // bki = Baki
+ TRUETYPE_TAG('b','k','j', 0 ), // bkj = Pande
+ TRUETYPE_TAG('b','k','k', 0 ), // bkk = Brokskat
+ TRUETYPE_TAG('b','k','l', 0 ), // bkl = Berik
+ TRUETYPE_TAG('b','k','m', 0 ), // bkm = Kom (Cameroon)
+ TRUETYPE_TAG('b','k','n', 0 ), // bkn = Bukitan
+ TRUETYPE_TAG('b','k','o', 0 ), // bko = Kwa'
+ TRUETYPE_TAG('b','k','p', 0 ), // bkp = Boko (Democratic Republic of Congo)
+ TRUETYPE_TAG('b','k','q', 0 ), // bkq = Bakairí
+ TRUETYPE_TAG('b','k','r', 0 ), // bkr = Bakumpai
+ TRUETYPE_TAG('b','k','s', 0 ), // bks = Northern Sorsoganon
+ TRUETYPE_TAG('b','k','t', 0 ), // bkt = Boloki
+ TRUETYPE_TAG('b','k','u', 0 ), // bku = Buhid
+ TRUETYPE_TAG('b','k','v', 0 ), // bkv = Bekwarra
+ TRUETYPE_TAG('b','k','w', 0 ), // bkw = Bekwel
+ TRUETYPE_TAG('b','k','x', 0 ), // bkx = Baikeno
+ TRUETYPE_TAG('b','k','y', 0 ), // bky = Bokyi
+ TRUETYPE_TAG('b','k','z', 0 ), // bkz = Bungku
+ TRUETYPE_TAG('b','l','a', 0 ), // bla = Siksika
+ TRUETYPE_TAG('b','l','b', 0 ), // blb = Bilua
+ TRUETYPE_TAG('b','l','c', 0 ), // blc = Bella Coola
+ TRUETYPE_TAG('b','l','d', 0 ), // bld = Bolango
+ TRUETYPE_TAG('b','l','e', 0 ), // ble = Balanta-Kentohe
+ TRUETYPE_TAG('b','l','f', 0 ), // blf = Buol
+ TRUETYPE_TAG('b','l','g', 0 ), // blg = Balau
+ TRUETYPE_TAG('b','l','h', 0 ), // blh = Kuwaa
+ TRUETYPE_TAG('b','l','i', 0 ), // bli = Bolia
+ TRUETYPE_TAG('b','l','j', 0 ), // blj = Bolongan
+ TRUETYPE_TAG('b','l','k', 0 ), // blk = Pa'o Karen
+ TRUETYPE_TAG('b','l','l', 0 ), // bll = Biloxi
+ TRUETYPE_TAG('b','l','m', 0 ), // blm = Beli (Sudan)
+ TRUETYPE_TAG('b','l','n', 0 ), // bln = Southern Catanduanes Bicolano
+ TRUETYPE_TAG('b','l','o', 0 ), // blo = Anii
+ TRUETYPE_TAG('b','l','p', 0 ), // blp = Blablanga
+ TRUETYPE_TAG('b','l','q', 0 ), // blq = Baluan-Pam
+ TRUETYPE_TAG('b','l','r', 0 ), // blr = Blang
+ TRUETYPE_TAG('b','l','s', 0 ), // bls = Balaesang
+ TRUETYPE_TAG('b','l','t', 0 ), // blt = Tai Dam
+ TRUETYPE_TAG('b','l','v', 0 ), // blv = Bolo
+ TRUETYPE_TAG('b','l','w', 0 ), // blw = Balangao
+ TRUETYPE_TAG('b','l','x', 0 ), // blx = Mag-Indi Ayta
+ TRUETYPE_TAG('b','l','y', 0 ), // bly = Notre
+ TRUETYPE_TAG('b','l','z', 0 ), // blz = Balantak
+ TRUETYPE_TAG('b','m','a', 0 ), // bma = Lame
+ TRUETYPE_TAG('b','m','b', 0 ), // bmb = Bembe
+ TRUETYPE_TAG('b','m','c', 0 ), // bmc = Biem
+ TRUETYPE_TAG('b','m','d', 0 ), // bmd = Baga Manduri
+ TRUETYPE_TAG('b','m','e', 0 ), // bme = Limassa
+ TRUETYPE_TAG('b','m','f', 0 ), // bmf = Bom
+ TRUETYPE_TAG('b','m','g', 0 ), // bmg = Bamwe
+ TRUETYPE_TAG('b','m','h', 0 ), // bmh = Kein
+ TRUETYPE_TAG('b','m','i', 0 ), // bmi = Bagirmi
+ TRUETYPE_TAG('b','m','j', 0 ), // bmj = Bote-Majhi
+ TRUETYPE_TAG('b','m','k', 0 ), // bmk = Ghayavi
+ TRUETYPE_TAG('b','m','l', 0 ), // bml = Bomboli
+ TRUETYPE_TAG('b','m','m', 0 ), // bmm = Northern Betsimisaraka Malagasy
+ TRUETYPE_TAG('b','m','n', 0 ), // bmn = Bina (Papua New Guinea)
+ TRUETYPE_TAG('b','m','o', 0 ), // bmo = Bambalang
+ TRUETYPE_TAG('b','m','p', 0 ), // bmp = Bulgebi
+ TRUETYPE_TAG('b','m','q', 0 ), // bmq = Bomu
+ TRUETYPE_TAG('b','m','r', 0 ), // bmr = Muinane
+ TRUETYPE_TAG('b','m','s', 0 ), // bms = Bilma Kanuri
+ TRUETYPE_TAG('b','m','t', 0 ), // bmt = Biao Mon
+ TRUETYPE_TAG('b','m','u', 0 ), // bmu = Somba-Siawari
+ TRUETYPE_TAG('b','m','v', 0 ), // bmv = Bum
+ TRUETYPE_TAG('b','m','w', 0 ), // bmw = Bomwali
+ TRUETYPE_TAG('b','m','x', 0 ), // bmx = Baimak
+ TRUETYPE_TAG('b','m','y', 0 ), // bmy = Bemba (Democratic Republic of Congo)
+ TRUETYPE_TAG('b','m','z', 0 ), // bmz = Baramu
+ TRUETYPE_TAG('b','n','a', 0 ), // bna = Bonerate
+ TRUETYPE_TAG('b','n','b', 0 ), // bnb = Bookan
+ TRUETYPE_TAG('b','n','c', 0 ), // bnc = Bontok
+ TRUETYPE_TAG('b','n','d', 0 ), // bnd = Banda (Indonesia)
+ TRUETYPE_TAG('b','n','e', 0 ), // bne = Bintauna
+ TRUETYPE_TAG('b','n','f', 0 ), // bnf = Masiwang
+ TRUETYPE_TAG('b','n','g', 0 ), // bng = Benga
+ TRUETYPE_TAG('b','n','i', 0 ), // bni = Bangi
+ TRUETYPE_TAG('b','n','j', 0 ), // bnj = Eastern Tawbuid
+ TRUETYPE_TAG('b','n','k', 0 ), // bnk = Bierebo
+ TRUETYPE_TAG('b','n','l', 0 ), // bnl = Boon
+ TRUETYPE_TAG('b','n','m', 0 ), // bnm = Batanga
+ TRUETYPE_TAG('b','n','n', 0 ), // bnn = Bunun
+ TRUETYPE_TAG('b','n','o', 0 ), // bno = Bantoanon
+ TRUETYPE_TAG('b','n','p', 0 ), // bnp = Bola
+ TRUETYPE_TAG('b','n','q', 0 ), // bnq = Bantik
+ TRUETYPE_TAG('b','n','r', 0 ), // bnr = Butmas-Tur
+ TRUETYPE_TAG('b','n','s', 0 ), // bns = Bundeli
+ TRUETYPE_TAG('b','n','t', 0 ), // bnt = Bantu languages
+ TRUETYPE_TAG('b','n','u', 0 ), // bnu = Bentong
+ TRUETYPE_TAG('b','n','v', 0 ), // bnv = Bonerif
+ TRUETYPE_TAG('b','n','w', 0 ), // bnw = Bisis
+ TRUETYPE_TAG('b','n','x', 0 ), // bnx = Bangubangu
+ TRUETYPE_TAG('b','n','y', 0 ), // bny = Bintulu
+ TRUETYPE_TAG('b','n','z', 0 ), // bnz = Beezen
+ TRUETYPE_TAG('b','o','a', 0 ), // boa = Bora
+ TRUETYPE_TAG('b','o','b', 0 ), // bob = Aweer
+ TRUETYPE_TAG('b','o','e', 0 ), // boe = Mundabli
+ TRUETYPE_TAG('b','o','f', 0 ), // bof = Bolon
+ TRUETYPE_TAG('b','o','g', 0 ), // bog = Bamako Sign Language
+ TRUETYPE_TAG('b','o','h', 0 ), // boh = Boma
+ TRUETYPE_TAG('b','o','i', 0 ), // boi = Barbareño
+ TRUETYPE_TAG('b','o','j', 0 ), // boj = Anjam
+ TRUETYPE_TAG('b','o','k', 0 ), // bok = Bonjo
+ TRUETYPE_TAG('b','o','l', 0 ), // bol = Bole
+ TRUETYPE_TAG('b','o','m', 0 ), // bom = Berom
+ TRUETYPE_TAG('b','o','n', 0 ), // bon = Bine
+ TRUETYPE_TAG('b','o','o', 0 ), // boo = Tiemacèwè Bozo
+ TRUETYPE_TAG('b','o','p', 0 ), // bop = Bonkiman
+ TRUETYPE_TAG('b','o','q', 0 ), // boq = Bogaya
+ TRUETYPE_TAG('b','o','r', 0 ), // bor = Borôro
+ TRUETYPE_TAG('b','o','t', 0 ), // bot = Bongo
+ TRUETYPE_TAG('b','o','u', 0 ), // bou = Bondei
+ TRUETYPE_TAG('b','o','v', 0 ), // bov = Tuwuli
+ TRUETYPE_TAG('b','o','w', 0 ), // bow = Rema
+ TRUETYPE_TAG('b','o','x', 0 ), // box = Buamu
+ TRUETYPE_TAG('b','o','y', 0 ), // boy = Bodo (Central African Republic)
+ TRUETYPE_TAG('b','o','z', 0 ), // boz = Tiéyaxo Bozo
+ TRUETYPE_TAG('b','p','a', 0 ), // bpa = Dakaka
+ TRUETYPE_TAG('b','p','b', 0 ), // bpb = Barbacoas
+ TRUETYPE_TAG('b','p','d', 0 ), // bpd = Banda-Banda
+ TRUETYPE_TAG('b','p','g', 0 ), // bpg = Bonggo
+ TRUETYPE_TAG('b','p','h', 0 ), // bph = Botlikh
+ TRUETYPE_TAG('b','p','i', 0 ), // bpi = Bagupi
+ TRUETYPE_TAG('b','p','j', 0 ), // bpj = Binji
+ TRUETYPE_TAG('b','p','k', 0 ), // bpk = Orowe
+ TRUETYPE_TAG('b','p','l', 0 ), // bpl = Broome Pearling Lugger Pidgin
+ TRUETYPE_TAG('b','p','m', 0 ), // bpm = Biyom
+ TRUETYPE_TAG('b','p','n', 0 ), // bpn = Dzao Min
+ TRUETYPE_TAG('b','p','o', 0 ), // bpo = Anasi
+ TRUETYPE_TAG('b','p','p', 0 ), // bpp = Kaure
+ TRUETYPE_TAG('b','p','q', 0 ), // bpq = Banda Malay
+ TRUETYPE_TAG('b','p','r', 0 ), // bpr = Koronadal Blaan
+ TRUETYPE_TAG('b','p','s', 0 ), // bps = Sarangani Blaan
+ TRUETYPE_TAG('b','p','t', 0 ), // bpt = Barrow Point
+ TRUETYPE_TAG('b','p','u', 0 ), // bpu = Bongu
+ TRUETYPE_TAG('b','p','v', 0 ), // bpv = Bian Marind
+ TRUETYPE_TAG('b','p','w', 0 ), // bpw = Bo (Papua New Guinea)
+ TRUETYPE_TAG('b','p','x', 0 ), // bpx = Palya Bareli
+ TRUETYPE_TAG('b','p','y', 0 ), // bpy = Bishnupriya
+ TRUETYPE_TAG('b','p','z', 0 ), // bpz = Bilba
+ TRUETYPE_TAG('b','q','a', 0 ), // bqa = Tchumbuli
+ TRUETYPE_TAG('b','q','b', 0 ), // bqb = Bagusa
+ TRUETYPE_TAG('b','q','c', 0 ), // bqc = Boko (Benin)
+ TRUETYPE_TAG('b','q','d', 0 ), // bqd = Bung
+ TRUETYPE_TAG('b','q','f', 0 ), // bqf = Baga Kaloum
+ TRUETYPE_TAG('b','q','g', 0 ), // bqg = Bago-Kusuntu
+ TRUETYPE_TAG('b','q','h', 0 ), // bqh = Baima
+ TRUETYPE_TAG('b','q','i', 0 ), // bqi = Bakhtiari
+ TRUETYPE_TAG('b','q','j', 0 ), // bqj = Bandial
+ TRUETYPE_TAG('b','q','k', 0 ), // bqk = Banda-Mbrès
+ TRUETYPE_TAG('b','q','l', 0 ), // bql = Bilakura
+ TRUETYPE_TAG('b','q','m', 0 ), // bqm = Wumboko
+ TRUETYPE_TAG('b','q','n', 0 ), // bqn = Bulgarian Sign Language
+ TRUETYPE_TAG('b','q','o', 0 ), // bqo = Balo
+ TRUETYPE_TAG('b','q','p', 0 ), // bqp = Busa
+ TRUETYPE_TAG('b','q','q', 0 ), // bqq = Biritai
+ TRUETYPE_TAG('b','q','r', 0 ), // bqr = Burusu
+ TRUETYPE_TAG('b','q','s', 0 ), // bqs = Bosngun
+ TRUETYPE_TAG('b','q','t', 0 ), // bqt = Bamukumbit
+ TRUETYPE_TAG('b','q','u', 0 ), // bqu = Boguru
+ TRUETYPE_TAG('b','q','v', 0 ), // bqv = Begbere-Ejar
+ TRUETYPE_TAG('b','q','w', 0 ), // bqw = Buru (Nigeria)
+ TRUETYPE_TAG('b','q','x', 0 ), // bqx = Baangi
+ TRUETYPE_TAG('b','q','y', 0 ), // bqy = Bengkala Sign Language
+ TRUETYPE_TAG('b','q','z', 0 ), // bqz = Bakaka
+ TRUETYPE_TAG('b','r','a', 0 ), // bra = Braj
+ TRUETYPE_TAG('b','r','b', 0 ), // brb = Lave
+ TRUETYPE_TAG('b','r','c', 0 ), // brc = Berbice Creole Dutch
+ TRUETYPE_TAG('b','r','d', 0 ), // brd = Baraamu
+ TRUETYPE_TAG('b','r','f', 0 ), // brf = Bera
+ TRUETYPE_TAG('b','r','g', 0 ), // brg = Baure
+ TRUETYPE_TAG('b','r','h', 0 ), // brh = Brahui
+ TRUETYPE_TAG('b','r','i', 0 ), // bri = Mokpwe
+ TRUETYPE_TAG('b','r','j', 0 ), // brj = Bieria
+ TRUETYPE_TAG('b','r','k', 0 ), // brk = Birked
+ TRUETYPE_TAG('b','r','l', 0 ), // brl = Birwa
+ TRUETYPE_TAG('b','r','m', 0 ), // brm = Barambu
+ TRUETYPE_TAG('b','r','n', 0 ), // brn = Boruca
+ TRUETYPE_TAG('b','r','o', 0 ), // bro = Brokkat
+ TRUETYPE_TAG('b','r','p', 0 ), // brp = Barapasi
+ TRUETYPE_TAG('b','r','q', 0 ), // brq = Breri
+ TRUETYPE_TAG('b','r','r', 0 ), // brr = Birao
+ TRUETYPE_TAG('b','r','s', 0 ), // brs = Baras
+ TRUETYPE_TAG('b','r','t', 0 ), // brt = Bitare
+ TRUETYPE_TAG('b','r','u', 0 ), // bru = Eastern Bru
+ TRUETYPE_TAG('b','r','v', 0 ), // brv = Western Bru
+ TRUETYPE_TAG('b','r','w', 0 ), // brw = Bellari
+ TRUETYPE_TAG('b','r','x', 0 ), // brx = Bodo (India)
+ TRUETYPE_TAG('b','r','y', 0 ), // bry = Burui
+ TRUETYPE_TAG('b','r','z', 0 ), // brz = Bilbil
+ TRUETYPE_TAG('b','s','a', 0 ), // bsa = Abinomn
+ TRUETYPE_TAG('b','s','b', 0 ), // bsb = Brunei Bisaya
+ TRUETYPE_TAG('b','s','c', 0 ), // bsc = Bassari
+ TRUETYPE_TAG('b','s','e', 0 ), // bse = Wushi
+ TRUETYPE_TAG('b','s','f', 0 ), // bsf = Bauchi
+ TRUETYPE_TAG('b','s','g', 0 ), // bsg = Bashkardi
+ TRUETYPE_TAG('b','s','h', 0 ), // bsh = Kati
+ TRUETYPE_TAG('b','s','i', 0 ), // bsi = Bassossi
+ TRUETYPE_TAG('b','s','j', 0 ), // bsj = Bangwinji
+ TRUETYPE_TAG('b','s','k', 0 ), // bsk = Burushaski
+ TRUETYPE_TAG('b','s','l', 0 ), // bsl = Basa-Gumna
+ TRUETYPE_TAG('b','s','m', 0 ), // bsm = Busami
+ TRUETYPE_TAG('b','s','n', 0 ), // bsn = Barasana-Eduria
+ TRUETYPE_TAG('b','s','o', 0 ), // bso = Buso
+ TRUETYPE_TAG('b','s','p', 0 ), // bsp = Baga Sitemu
+ TRUETYPE_TAG('b','s','q', 0 ), // bsq = Bassa
+ TRUETYPE_TAG('b','s','r', 0 ), // bsr = Bassa-Kontagora
+ TRUETYPE_TAG('b','s','s', 0 ), // bss = Akoose
+ TRUETYPE_TAG('b','s','t', 0 ), // bst = Basketo
+ TRUETYPE_TAG('b','s','u', 0 ), // bsu = Bahonsuai
+ TRUETYPE_TAG('b','s','v', 0 ), // bsv = Baga Sobané
+ TRUETYPE_TAG('b','s','w', 0 ), // bsw = Baiso
+ TRUETYPE_TAG('b','s','x', 0 ), // bsx = Yangkam
+ TRUETYPE_TAG('b','s','y', 0 ), // bsy = Sabah Bisaya
+ TRUETYPE_TAG('b','t','a', 0 ), // bta = Bata
+ TRUETYPE_TAG('b','t','b', 0 ), // btb = Beti (Cameroon)
+ TRUETYPE_TAG('b','t','c', 0 ), // btc = Bati (Cameroon)
+ TRUETYPE_TAG('b','t','d', 0 ), // btd = Batak Dairi
+ TRUETYPE_TAG('b','t','e', 0 ), // bte = Gamo-Ningi
+ TRUETYPE_TAG('b','t','f', 0 ), // btf = Birgit
+ TRUETYPE_TAG('b','t','g', 0 ), // btg = Gagnoa Bété
+ TRUETYPE_TAG('b','t','h', 0 ), // bth = Biatah Bidayuh
+ TRUETYPE_TAG('b','t','i', 0 ), // bti = Burate
+ TRUETYPE_TAG('b','t','j', 0 ), // btj = Bacanese Malay
+ TRUETYPE_TAG('b','t','k', 0 ), // btk = Batak languages
+ TRUETYPE_TAG('b','t','l', 0 ), // btl = Bhatola
+ TRUETYPE_TAG('b','t','m', 0 ), // btm = Batak Mandailing
+ TRUETYPE_TAG('b','t','n', 0 ), // btn = Ratagnon
+ TRUETYPE_TAG('b','t','o', 0 ), // bto = Rinconada Bikol
+ TRUETYPE_TAG('b','t','p', 0 ), // btp = Budibud
+ TRUETYPE_TAG('b','t','q', 0 ), // btq = Batek
+ TRUETYPE_TAG('b','t','r', 0 ), // btr = Baetora
+ TRUETYPE_TAG('b','t','s', 0 ), // bts = Batak Simalungun
+ TRUETYPE_TAG('b','t','t', 0 ), // btt = Bete-Bendi
+ TRUETYPE_TAG('b','t','u', 0 ), // btu = Batu
+ TRUETYPE_TAG('b','t','v', 0 ), // btv = Bateri
+ TRUETYPE_TAG('b','t','w', 0 ), // btw = Butuanon
+ TRUETYPE_TAG('b','t','x', 0 ), // btx = Batak Karo
+ TRUETYPE_TAG('b','t','y', 0 ), // bty = Bobot
+ TRUETYPE_TAG('b','t','z', 0 ), // btz = Batak Alas-Kluet
+ TRUETYPE_TAG('b','u','a', 0 ), // bua = Buriat
+ TRUETYPE_TAG('b','u','b', 0 ), // bub = Bua
+ TRUETYPE_TAG('b','u','c', 0 ), // buc = Bushi
+ TRUETYPE_TAG('b','u','d', 0 ), // bud = Ntcham
+ TRUETYPE_TAG('b','u','e', 0 ), // bue = Beothuk
+ TRUETYPE_TAG('b','u','f', 0 ), // buf = Bushoong
+ TRUETYPE_TAG('b','u','g', 0 ), // bug = Buginese
+ TRUETYPE_TAG('b','u','h', 0 ), // buh = Younuo Bunu
+ TRUETYPE_TAG('b','u','i', 0 ), // bui = Bongili
+ TRUETYPE_TAG('b','u','j', 0 ), // buj = Basa-Gurmana
+ TRUETYPE_TAG('b','u','k', 0 ), // buk = Bugawac
+ TRUETYPE_TAG('b','u','m', 0 ), // bum = Bulu (Cameroon)
+ TRUETYPE_TAG('b','u','n', 0 ), // bun = Sherbro
+ TRUETYPE_TAG('b','u','o', 0 ), // buo = Terei
+ TRUETYPE_TAG('b','u','p', 0 ), // bup = Busoa
+ TRUETYPE_TAG('b','u','q', 0 ), // buq = Brem
+ TRUETYPE_TAG('b','u','s', 0 ), // bus = Bokobaru
+ TRUETYPE_TAG('b','u','t', 0 ), // but = Bungain
+ TRUETYPE_TAG('b','u','u', 0 ), // buu = Budu
+ TRUETYPE_TAG('b','u','v', 0 ), // buv = Bun
+ TRUETYPE_TAG('b','u','w', 0 ), // buw = Bubi
+ TRUETYPE_TAG('b','u','x', 0 ), // bux = Boghom
+ TRUETYPE_TAG('b','u','y', 0 ), // buy = Bullom So
+ TRUETYPE_TAG('b','u','z', 0 ), // buz = Bukwen
+ TRUETYPE_TAG('b','v','a', 0 ), // bva = Barein
+ TRUETYPE_TAG('b','v','b', 0 ), // bvb = Bube
+ TRUETYPE_TAG('b','v','c', 0 ), // bvc = Baelelea
+ TRUETYPE_TAG('b','v','d', 0 ), // bvd = Baeggu
+ TRUETYPE_TAG('b','v','e', 0 ), // bve = Berau Malay
+ TRUETYPE_TAG('b','v','f', 0 ), // bvf = Boor
+ TRUETYPE_TAG('b','v','g', 0 ), // bvg = Bonkeng
+ TRUETYPE_TAG('b','v','h', 0 ), // bvh = Bure
+ TRUETYPE_TAG('b','v','i', 0 ), // bvi = Belanda Viri
+ TRUETYPE_TAG('b','v','j', 0 ), // bvj = Baan
+ TRUETYPE_TAG('b','v','k', 0 ), // bvk = Bukat
+ TRUETYPE_TAG('b','v','l', 0 ), // bvl = Bolivian Sign Language
+ TRUETYPE_TAG('b','v','m', 0 ), // bvm = Bamunka
+ TRUETYPE_TAG('b','v','n', 0 ), // bvn = Buna
+ TRUETYPE_TAG('b','v','o', 0 ), // bvo = Bolgo
+ TRUETYPE_TAG('b','v','q', 0 ), // bvq = Birri
+ TRUETYPE_TAG('b','v','r', 0 ), // bvr = Burarra
+ TRUETYPE_TAG('b','v','t', 0 ), // bvt = Bati (Indonesia)
+ TRUETYPE_TAG('b','v','u', 0 ), // bvu = Bukit Malay
+ TRUETYPE_TAG('b','v','v', 0 ), // bvv = Baniva
+ TRUETYPE_TAG('b','v','w', 0 ), // bvw = Boga
+ TRUETYPE_TAG('b','v','x', 0 ), // bvx = Dibole
+ TRUETYPE_TAG('b','v','y', 0 ), // bvy = Baybayanon
+ TRUETYPE_TAG('b','v','z', 0 ), // bvz = Bauzi
+ TRUETYPE_TAG('b','w','a', 0 ), // bwa = Bwatoo
+ TRUETYPE_TAG('b','w','b', 0 ), // bwb = Namosi-Naitasiri-Serua
+ TRUETYPE_TAG('b','w','c', 0 ), // bwc = Bwile
+ TRUETYPE_TAG('b','w','d', 0 ), // bwd = Bwaidoka
+ TRUETYPE_TAG('b','w','e', 0 ), // bwe = Bwe Karen
+ TRUETYPE_TAG('b','w','f', 0 ), // bwf = Boselewa
+ TRUETYPE_TAG('b','w','g', 0 ), // bwg = Barwe
+ TRUETYPE_TAG('b','w','h', 0 ), // bwh = Bishuo
+ TRUETYPE_TAG('b','w','i', 0 ), // bwi = Baniwa
+ TRUETYPE_TAG('b','w','j', 0 ), // bwj = Láá Láá Bwamu
+ TRUETYPE_TAG('b','w','k', 0 ), // bwk = Bauwaki
+ TRUETYPE_TAG('b','w','l', 0 ), // bwl = Bwela
+ TRUETYPE_TAG('b','w','m', 0 ), // bwm = Biwat
+ TRUETYPE_TAG('b','w','n', 0 ), // bwn = Wunai Bunu
+ TRUETYPE_TAG('b','w','o', 0 ), // bwo = Boro (Ethiopia)
+ TRUETYPE_TAG('b','w','p', 0 ), // bwp = Mandobo Bawah
+ TRUETYPE_TAG('b','w','q', 0 ), // bwq = Southern Bobo Madaré
+ TRUETYPE_TAG('b','w','r', 0 ), // bwr = Bura-Pabir
+ TRUETYPE_TAG('b','w','s', 0 ), // bws = Bomboma
+ TRUETYPE_TAG('b','w','t', 0 ), // bwt = Bafaw-Balong
+ TRUETYPE_TAG('b','w','u', 0 ), // bwu = Buli (Ghana)
+ TRUETYPE_TAG('b','w','w', 0 ), // bww = Bwa
+ TRUETYPE_TAG('b','w','x', 0 ), // bwx = Bu-Nao Bunu
+ TRUETYPE_TAG('b','w','y', 0 ), // bwy = Cwi Bwamu
+ TRUETYPE_TAG('b','w','z', 0 ), // bwz = Bwisi
+ TRUETYPE_TAG('b','x','a', 0 ), // bxa = Bauro
+ TRUETYPE_TAG('b','x','b', 0 ), // bxb = Belanda Bor
+ TRUETYPE_TAG('b','x','c', 0 ), // bxc = Molengue
+ TRUETYPE_TAG('b','x','d', 0 ), // bxd = Pela
+ TRUETYPE_TAG('b','x','e', 0 ), // bxe = Birale
+ TRUETYPE_TAG('b','x','f', 0 ), // bxf = Bilur
+ TRUETYPE_TAG('b','x','g', 0 ), // bxg = Bangala
+ TRUETYPE_TAG('b','x','h', 0 ), // bxh = Buhutu
+ TRUETYPE_TAG('b','x','i', 0 ), // bxi = Pirlatapa
+ TRUETYPE_TAG('b','x','j', 0 ), // bxj = Bayungu
+ TRUETYPE_TAG('b','x','k', 0 ), // bxk = Bukusu
+ TRUETYPE_TAG('b','x','l', 0 ), // bxl = Jalkunan
+ TRUETYPE_TAG('b','x','m', 0 ), // bxm = Mongolia Buriat
+ TRUETYPE_TAG('b','x','n', 0 ), // bxn = Burduna
+ TRUETYPE_TAG('b','x','o', 0 ), // bxo = Barikanchi
+ TRUETYPE_TAG('b','x','p', 0 ), // bxp = Bebil
+ TRUETYPE_TAG('b','x','q', 0 ), // bxq = Beele
+ TRUETYPE_TAG('b','x','r', 0 ), // bxr = Russia Buriat
+ TRUETYPE_TAG('b','x','s', 0 ), // bxs = Busam
+ TRUETYPE_TAG('b','x','u', 0 ), // bxu = China Buriat
+ TRUETYPE_TAG('b','x','v', 0 ), // bxv = Berakou
+ TRUETYPE_TAG('b','x','w', 0 ), // bxw = Bankagooma
+ TRUETYPE_TAG('b','x','x', 0 ), // bxx = Borna (Democratic Republic of Congo)
+ TRUETYPE_TAG('b','x','z', 0 ), // bxz = Binahari
+ TRUETYPE_TAG('b','y','a', 0 ), // bya = Batak
+ TRUETYPE_TAG('b','y','b', 0 ), // byb = Bikya
+ TRUETYPE_TAG('b','y','c', 0 ), // byc = Ubaghara
+ TRUETYPE_TAG('b','y','d', 0 ), // byd = Benyadu'
+ TRUETYPE_TAG('b','y','e', 0 ), // bye = Pouye
+ TRUETYPE_TAG('b','y','f', 0 ), // byf = Bete
+ TRUETYPE_TAG('b','y','g', 0 ), // byg = Baygo
+ TRUETYPE_TAG('b','y','h', 0 ), // byh = Bhujel
+ TRUETYPE_TAG('b','y','i', 0 ), // byi = Buyu
+ TRUETYPE_TAG('b','y','j', 0 ), // byj = Bina (Nigeria)
+ TRUETYPE_TAG('b','y','k', 0 ), // byk = Biao
+ TRUETYPE_TAG('b','y','l', 0 ), // byl = Bayono
+ TRUETYPE_TAG('b','y','m', 0 ), // bym = Bidyara
+ TRUETYPE_TAG('b','y','n', 0 ), // byn = Bilin
+ TRUETYPE_TAG('b','y','o', 0 ), // byo = Biyo
+ TRUETYPE_TAG('b','y','p', 0 ), // byp = Bumaji
+ TRUETYPE_TAG('b','y','q', 0 ), // byq = Basay
+ TRUETYPE_TAG('b','y','r', 0 ), // byr = Baruya
+ TRUETYPE_TAG('b','y','s', 0 ), // bys = Burak
+ TRUETYPE_TAG('b','y','t', 0 ), // byt = Berti
+ TRUETYPE_TAG('b','y','v', 0 ), // byv = Medumba
+ TRUETYPE_TAG('b','y','w', 0 ), // byw = Belhariya
+ TRUETYPE_TAG('b','y','x', 0 ), // byx = Qaqet
+ TRUETYPE_TAG('b','y','y', 0 ), // byy = Buya
+ TRUETYPE_TAG('b','y','z', 0 ), // byz = Banaro
+ TRUETYPE_TAG('b','z','a', 0 ), // bza = Bandi
+ TRUETYPE_TAG('b','z','b', 0 ), // bzb = Andio
+ TRUETYPE_TAG('b','z','c', 0 ), // bzc = Southern Betsimisaraka Malagasy
+ TRUETYPE_TAG('b','z','d', 0 ), // bzd = Bribri
+ TRUETYPE_TAG('b','z','e', 0 ), // bze = Jenaama Bozo
+ TRUETYPE_TAG('b','z','f', 0 ), // bzf = Boikin
+ TRUETYPE_TAG('b','z','g', 0 ), // bzg = Babuza
+ TRUETYPE_TAG('b','z','h', 0 ), // bzh = Mapos Buang
+ TRUETYPE_TAG('b','z','i', 0 ), // bzi = Bisu
+ TRUETYPE_TAG('b','z','j', 0 ), // bzj = Belize Kriol English
+ TRUETYPE_TAG('b','z','k', 0 ), // bzk = Nicaragua Creole English
+ TRUETYPE_TAG('b','z','l', 0 ), // bzl = Boano (Sulawesi)
+ TRUETYPE_TAG('b','z','m', 0 ), // bzm = Bolondo
+ TRUETYPE_TAG('b','z','n', 0 ), // bzn = Boano (Maluku)
+ TRUETYPE_TAG('b','z','o', 0 ), // bzo = Bozaba
+ TRUETYPE_TAG('b','z','p', 0 ), // bzp = Kemberano
+ TRUETYPE_TAG('b','z','q', 0 ), // bzq = Buli (Indonesia)
+ TRUETYPE_TAG('b','z','r', 0 ), // bzr = Biri
+ TRUETYPE_TAG('b','z','s', 0 ), // bzs = Brazilian Sign Language
+ TRUETYPE_TAG('b','z','t', 0 ), // bzt = Brithenig
+ TRUETYPE_TAG('b','z','u', 0 ), // bzu = Burmeso
+ TRUETYPE_TAG('b','z','v', 0 ), // bzv = Bebe
+ TRUETYPE_TAG('b','z','w', 0 ), // bzw = Basa (Nigeria)
+ TRUETYPE_TAG('b','z','x', 0 ), // bzx = Kɛlɛngaxo Bozo
+ TRUETYPE_TAG('b','z','y', 0 ), // bzy = Obanliku
+ TRUETYPE_TAG('b','z','z', 0 ), // bzz = Evant
+ TRUETYPE_TAG('c','a','a', 0 ), // caa = Chortí
+ TRUETYPE_TAG('c','a','b', 0 ), // cab = Garifuna
+ TRUETYPE_TAG('c','a','c', 0 ), // cac = Chuj
+ TRUETYPE_TAG('c','a','d', 0 ), // cad = Caddo
+ TRUETYPE_TAG('c','a','e', 0 ), // cae = Lehar
+ TRUETYPE_TAG('c','a','f', 0 ), // caf = Southern Carrier
+ TRUETYPE_TAG('c','a','g', 0 ), // cag = Nivaclé
+ TRUETYPE_TAG('c','a','h', 0 ), // cah = Cahuarano
+ TRUETYPE_TAG('c','a','i', 0 ), // cai = Central American Indian languages
+ TRUETYPE_TAG('c','a','j', 0 ), // caj = Chané
+ TRUETYPE_TAG('c','a','k', 0 ), // cak = Kaqchikel
+ TRUETYPE_TAG('c','a','l', 0 ), // cal = Carolinian
+ TRUETYPE_TAG('c','a','m', 0 ), // cam = Cemuhî
+ TRUETYPE_TAG('c','a','n', 0 ), // can = Chambri
+ TRUETYPE_TAG('c','a','o', 0 ), // cao = Chácobo
+ TRUETYPE_TAG('c','a','p', 0 ), // cap = Chipaya
+ TRUETYPE_TAG('c','a','q', 0 ), // caq = Car Nicobarese
+ TRUETYPE_TAG('c','a','r', 0 ), // car = Galibi Carib
+ TRUETYPE_TAG('c','a','s', 0 ), // cas = Tsimané
+ TRUETYPE_TAG('c','a','u', 0 ), // cau = Caucasian languages
+ TRUETYPE_TAG('c','a','v', 0 ), // cav = Cavineña
+ TRUETYPE_TAG('c','a','w', 0 ), // caw = Callawalla
+ TRUETYPE_TAG('c','a','x', 0 ), // cax = Chiquitano
+ TRUETYPE_TAG('c','a','y', 0 ), // cay = Cayuga
+ TRUETYPE_TAG('c','a','z', 0 ), // caz = Canichana
+ TRUETYPE_TAG('c','b','a', 0 ), // cba = Chibchan languages
+ TRUETYPE_TAG('c','b','b', 0 ), // cbb = Cabiyarí
+ TRUETYPE_TAG('c','b','c', 0 ), // cbc = Carapana
+ TRUETYPE_TAG('c','b','d', 0 ), // cbd = Carijona
+ TRUETYPE_TAG('c','b','e', 0 ), // cbe = Chipiajes
+ TRUETYPE_TAG('c','b','g', 0 ), // cbg = Chimila
+ TRUETYPE_TAG('c','b','h', 0 ), // cbh = Cagua
+ TRUETYPE_TAG('c','b','i', 0 ), // cbi = Chachi
+ TRUETYPE_TAG('c','b','j', 0 ), // cbj = Ede Cabe
+ TRUETYPE_TAG('c','b','k', 0 ), // cbk = Chavacano
+ TRUETYPE_TAG('c','b','l', 0 ), // cbl = Bualkhaw Chin
+ TRUETYPE_TAG('c','b','n', 0 ), // cbn = Nyahkur
+ TRUETYPE_TAG('c','b','o', 0 ), // cbo = Izora
+ TRUETYPE_TAG('c','b','r', 0 ), // cbr = Cashibo-Cacataibo
+ TRUETYPE_TAG('c','b','s', 0 ), // cbs = Cashinahua
+ TRUETYPE_TAG('c','b','t', 0 ), // cbt = Chayahuita
+ TRUETYPE_TAG('c','b','u', 0 ), // cbu = Candoshi-Shapra
+ TRUETYPE_TAG('c','b','v', 0 ), // cbv = Cacua
+ TRUETYPE_TAG('c','b','w', 0 ), // cbw = Kinabalian
+ TRUETYPE_TAG('c','b','y', 0 ), // cby = Carabayo
+ TRUETYPE_TAG('c','c','a', 0 ), // cca = Cauca
+ TRUETYPE_TAG('c','c','c', 0 ), // ccc = Chamicuro
+ TRUETYPE_TAG('c','c','d', 0 ), // ccd = Cafundo Creole
+ TRUETYPE_TAG('c','c','e', 0 ), // cce = Chopi
+ TRUETYPE_TAG('c','c','g', 0 ), // ccg = Samba Daka
+ TRUETYPE_TAG('c','c','h', 0 ), // cch = Atsam
+ TRUETYPE_TAG('c','c','j', 0 ), // ccj = Kasanga
+ TRUETYPE_TAG('c','c','l', 0 ), // ccl = Cutchi-Swahili
+ TRUETYPE_TAG('c','c','m', 0 ), // ccm = Malaccan Creole Malay
+ TRUETYPE_TAG('c','c','n', 0 ), // ccn = North Caucasian languages
+ TRUETYPE_TAG('c','c','o', 0 ), // cco = Comaltepec Chinantec
+ TRUETYPE_TAG('c','c','p', 0 ), // ccp = Chakma
+ TRUETYPE_TAG('c','c','q', 0 ), // ccq = Chaungtha
+ TRUETYPE_TAG('c','c','r', 0 ), // ccr = Cacaopera
+ TRUETYPE_TAG('c','c','s', 0 ), // ccs = South Caucasian languages
+ TRUETYPE_TAG('c','d','a', 0 ), // cda = Choni
+ TRUETYPE_TAG('c','d','c', 0 ), // cdc = Chadic languages
+ TRUETYPE_TAG('c','d','d', 0 ), // cdd = Caddoan languages
+ TRUETYPE_TAG('c','d','e', 0 ), // cde = Chenchu
+ TRUETYPE_TAG('c','d','f', 0 ), // cdf = Chiru
+ TRUETYPE_TAG('c','d','g', 0 ), // cdg = Chamari
+ TRUETYPE_TAG('c','d','h', 0 ), // cdh = Chambeali
+ TRUETYPE_TAG('c','d','i', 0 ), // cdi = Chodri
+ TRUETYPE_TAG('c','d','j', 0 ), // cdj = Churahi
+ TRUETYPE_TAG('c','d','m', 0 ), // cdm = Chepang
+ TRUETYPE_TAG('c','d','n', 0 ), // cdn = Chaudangsi
+ TRUETYPE_TAG('c','d','o', 0 ), // cdo = Min Dong Chinese
+ TRUETYPE_TAG('c','d','r', 0 ), // cdr = Cinda-Regi-Tiyal
+ TRUETYPE_TAG('c','d','s', 0 ), // cds = Chadian Sign Language
+ TRUETYPE_TAG('c','d','y', 0 ), // cdy = Chadong
+ TRUETYPE_TAG('c','d','z', 0 ), // cdz = Koda
+ TRUETYPE_TAG('c','e','a', 0 ), // cea = Lower Chehalis
+ TRUETYPE_TAG('c','e','b', 0 ), // ceb = Cebuano
+ TRUETYPE_TAG('c','e','g', 0 ), // ceg = Chamacoco
+ TRUETYPE_TAG('c','e','l', 0 ), // cel = Celtic languages
+ TRUETYPE_TAG('c','e','n', 0 ), // cen = Cen
+ TRUETYPE_TAG('c','e','t', 0 ), // cet = Centúúm
+ TRUETYPE_TAG('c','f','a', 0 ), // cfa = Dijim-Bwilim
+ TRUETYPE_TAG('c','f','d', 0 ), // cfd = Cara
+ TRUETYPE_TAG('c','f','g', 0 ), // cfg = Como Karim
+ TRUETYPE_TAG('c','f','m', 0 ), // cfm = Falam Chin
+ TRUETYPE_TAG('c','g','a', 0 ), // cga = Changriwa
+ TRUETYPE_TAG('c','g','c', 0 ), // cgc = Kagayanen
+ TRUETYPE_TAG('c','g','g', 0 ), // cgg = Chiga
+ TRUETYPE_TAG('c','g','k', 0 ), // cgk = Chocangacakha
+ TRUETYPE_TAG('c','h','b', 0 ), // chb = Chibcha
+ TRUETYPE_TAG('c','h','c', 0 ), // chc = Catawba
+ TRUETYPE_TAG('c','h','d', 0 ), // chd = Highland Oaxaca Chontal
+ TRUETYPE_TAG('c','h','f', 0 ), // chf = Tabasco Chontal
+ TRUETYPE_TAG('c','h','g', 0 ), // chg = Chagatai
+ TRUETYPE_TAG('c','h','h', 0 ), // chh = Chinook
+ TRUETYPE_TAG('c','h','j', 0 ), // chj = Ojitlán Chinantec
+ TRUETYPE_TAG('c','h','k', 0 ), // chk = Chuukese
+ TRUETYPE_TAG('c','h','l', 0 ), // chl = Cahuilla
+ TRUETYPE_TAG('c','h','m', 0 ), // chm = Mari (Russia)
+ TRUETYPE_TAG('c','h','n', 0 ), // chn = Chinook jargon
+ TRUETYPE_TAG('c','h','o', 0 ), // cho = Choctaw
+ TRUETYPE_TAG('c','h','p', 0 ), // chp = Chipewyan
+ TRUETYPE_TAG('c','h','q', 0 ), // chq = Quiotepec Chinantec
+ TRUETYPE_TAG('c','h','r', 0 ), // chr = Cherokee
+ TRUETYPE_TAG('c','h','t', 0 ), // cht = Cholón
+ TRUETYPE_TAG('c','h','w', 0 ), // chw = Chuwabu
+ TRUETYPE_TAG('c','h','x', 0 ), // chx = Chantyal
+ TRUETYPE_TAG('c','h','y', 0 ), // chy = Cheyenne
+ TRUETYPE_TAG('c','h','z', 0 ), // chz = Ozumacín Chinantec
+ TRUETYPE_TAG('c','i','a', 0 ), // cia = Cia-Cia
+ TRUETYPE_TAG('c','i','b', 0 ), // cib = Ci Gbe
+ TRUETYPE_TAG('c','i','c', 0 ), // cic = Chickasaw
+ TRUETYPE_TAG('c','i','d', 0 ), // cid = Chimariko
+ TRUETYPE_TAG('c','i','e', 0 ), // cie = Cineni
+ TRUETYPE_TAG('c','i','h', 0 ), // cih = Chinali
+ TRUETYPE_TAG('c','i','k', 0 ), // cik = Chitkuli Kinnauri
+ TRUETYPE_TAG('c','i','m', 0 ), // cim = Cimbrian
+ TRUETYPE_TAG('c','i','n', 0 ), // cin = Cinta Larga
+ TRUETYPE_TAG('c','i','p', 0 ), // cip = Chiapanec
+ TRUETYPE_TAG('c','i','r', 0 ), // cir = Tiri
+ TRUETYPE_TAG('c','i','w', 0 ), // ciw = Chippewa
+ TRUETYPE_TAG('c','i','y', 0 ), // ciy = Chaima
+ TRUETYPE_TAG('c','j','a', 0 ), // cja = Western Cham
+ TRUETYPE_TAG('c','j','e', 0 ), // cje = Chru
+ TRUETYPE_TAG('c','j','h', 0 ), // cjh = Upper Chehalis
+ TRUETYPE_TAG('c','j','i', 0 ), // cji = Chamalal
+ TRUETYPE_TAG('c','j','k', 0 ), // cjk = Chokwe
+ TRUETYPE_TAG('c','j','m', 0 ), // cjm = Eastern Cham
+ TRUETYPE_TAG('c','j','n', 0 ), // cjn = Chenapian
+ TRUETYPE_TAG('c','j','o', 0 ), // cjo = Ashéninka Pajonal
+ TRUETYPE_TAG('c','j','p', 0 ), // cjp = Cabécar
+ TRUETYPE_TAG('c','j','r', 0 ), // cjr = Chorotega
+ TRUETYPE_TAG('c','j','s', 0 ), // cjs = Shor
+ TRUETYPE_TAG('c','j','v', 0 ), // cjv = Chuave
+ TRUETYPE_TAG('c','j','y', 0 ), // cjy = Jinyu Chinese
+ TRUETYPE_TAG('c','k','a', 0 ), // cka = Khumi Awa Chin
+ TRUETYPE_TAG('c','k','b', 0 ), // ckb = Central Kurdish
+ TRUETYPE_TAG('c','k','h', 0 ), // ckh = Chak
+ TRUETYPE_TAG('c','k','l', 0 ), // ckl = Cibak
+ TRUETYPE_TAG('c','k','o', 0 ), // cko = Anufo
+ TRUETYPE_TAG('c','k','q', 0 ), // ckq = Kajakse
+ TRUETYPE_TAG('c','k','r', 0 ), // ckr = Kairak
+ TRUETYPE_TAG('c','k','s', 0 ), // cks = Tayo
+ TRUETYPE_TAG('c','k','t', 0 ), // ckt = Chukot
+ TRUETYPE_TAG('c','k','u', 0 ), // cku = Koasati
+ TRUETYPE_TAG('c','k','v', 0 ), // ckv = Kavalan
+ TRUETYPE_TAG('c','k','x', 0 ), // ckx = Caka
+ TRUETYPE_TAG('c','k','y', 0 ), // cky = Cakfem-Mushere
+ TRUETYPE_TAG('c','k','z', 0 ), // ckz = Cakchiquel-Quiché Mixed Language
+ TRUETYPE_TAG('c','l','a', 0 ), // cla = Ron
+ TRUETYPE_TAG('c','l','c', 0 ), // clc = Chilcotin
+ TRUETYPE_TAG('c','l','d', 0 ), // cld = Chaldean Neo-Aramaic
+ TRUETYPE_TAG('c','l','e', 0 ), // cle = Lealao Chinantec
+ TRUETYPE_TAG('c','l','h', 0 ), // clh = Chilisso
+ TRUETYPE_TAG('c','l','i', 0 ), // cli = Chakali
+ TRUETYPE_TAG('c','l','k', 0 ), // clk = Idu-Mishmi
+ TRUETYPE_TAG('c','l','l', 0 ), // cll = Chala
+ TRUETYPE_TAG('c','l','m', 0 ), // clm = Clallam
+ TRUETYPE_TAG('c','l','o', 0 ), // clo = Lowland Oaxaca Chontal
+ TRUETYPE_TAG('c','l','u', 0 ), // clu = Caluyanun
+ TRUETYPE_TAG('c','l','w', 0 ), // clw = Chulym
+ TRUETYPE_TAG('c','l','y', 0 ), // cly = Eastern Highland Chatino
+ TRUETYPE_TAG('c','m','a', 0 ), // cma = Maa
+ TRUETYPE_TAG('c','m','c', 0 ), // cmc = Chamic languages
+ TRUETYPE_TAG('c','m','e', 0 ), // cme = Cerma
+ TRUETYPE_TAG('c','m','g', 0 ), // cmg = Classical Mongolian
+ TRUETYPE_TAG('c','m','i', 0 ), // cmi = Emberá-Chamí
+ TRUETYPE_TAG('c','m','k', 0 ), // cmk = Chimakum
+ TRUETYPE_TAG('c','m','l', 0 ), // cml = Campalagian
+ TRUETYPE_TAG('c','m','m', 0 ), // cmm = Michigamea
+ TRUETYPE_TAG('c','m','n', 0 ), // cmn = Mandarin Chinese
+ TRUETYPE_TAG('c','m','o', 0 ), // cmo = Central Mnong
+ TRUETYPE_TAG('c','m','r', 0 ), // cmr = Mro Chin
+ TRUETYPE_TAG('c','m','s', 0 ), // cms = Messapic
+ TRUETYPE_TAG('c','m','t', 0 ), // cmt = Camtho
+ TRUETYPE_TAG('c','n','a', 0 ), // cna = Changthang
+ TRUETYPE_TAG('c','n','b', 0 ), // cnb = Chinbon Chin
+ TRUETYPE_TAG('c','n','c', 0 ), // cnc = Côông
+ TRUETYPE_TAG('c','n','g', 0 ), // cng = Northern Qiang
+ TRUETYPE_TAG('c','n','h', 0 ), // cnh = Haka Chin
+ TRUETYPE_TAG('c','n','i', 0 ), // cni = Asháninka
+ TRUETYPE_TAG('c','n','k', 0 ), // cnk = Khumi Chin
+ TRUETYPE_TAG('c','n','l', 0 ), // cnl = Lalana Chinantec
+ TRUETYPE_TAG('c','n','o', 0 ), // cno = Con
+ TRUETYPE_TAG('c','n','s', 0 ), // cns = Central Asmat
+ TRUETYPE_TAG('c','n','t', 0 ), // cnt = Tepetotutla Chinantec
+ TRUETYPE_TAG('c','n','u', 0 ), // cnu = Chenoua
+ TRUETYPE_TAG('c','n','w', 0 ), // cnw = Ngawn Chin
+ TRUETYPE_TAG('c','n','x', 0 ), // cnx = Middle Cornish
+ TRUETYPE_TAG('c','o','a', 0 ), // coa = Cocos Islands Malay
+ TRUETYPE_TAG('c','o','b', 0 ), // cob = Chicomuceltec
+ TRUETYPE_TAG('c','o','c', 0 ), // coc = Cocopa
+ TRUETYPE_TAG('c','o','d', 0 ), // cod = Cocama-Cocamilla
+ TRUETYPE_TAG('c','o','e', 0 ), // coe = Koreguaje
+ TRUETYPE_TAG('c','o','f', 0 ), // cof = Colorado
+ TRUETYPE_TAG('c','o','g', 0 ), // cog = Chong
+ TRUETYPE_TAG('c','o','h', 0 ), // coh = Chonyi-Dzihana-Kauma
+ TRUETYPE_TAG('c','o','j', 0 ), // coj = Cochimi
+ TRUETYPE_TAG('c','o','k', 0 ), // cok = Santa Teresa Cora
+ TRUETYPE_TAG('c','o','l', 0 ), // col = Columbia-Wenatchi
+ TRUETYPE_TAG('c','o','m', 0 ), // com = Comanche
+ TRUETYPE_TAG('c','o','n', 0 ), // con = Cofán
+ TRUETYPE_TAG('c','o','o', 0 ), // coo = Comox
+ TRUETYPE_TAG('c','o','p', 0 ), // cop = Coptic
+ TRUETYPE_TAG('c','o','q', 0 ), // coq = Coquille
+ TRUETYPE_TAG('c','o','t', 0 ), // cot = Caquinte
+ TRUETYPE_TAG('c','o','u', 0 ), // cou = Wamey
+ TRUETYPE_TAG('c','o','v', 0 ), // cov = Cao Miao
+ TRUETYPE_TAG('c','o','w', 0 ), // cow = Cowlitz
+ TRUETYPE_TAG('c','o','x', 0 ), // cox = Nanti
+ TRUETYPE_TAG('c','o','y', 0 ), // coy = Coyaima
+ TRUETYPE_TAG('c','o','z', 0 ), // coz = Chochotec
+ TRUETYPE_TAG('c','p','a', 0 ), // cpa = Palantla Chinantec
+ TRUETYPE_TAG('c','p','b', 0 ), // cpb = Ucayali-Yurúa Ashéninka
+ TRUETYPE_TAG('c','p','c', 0 ), // cpc = Ajyíninka Apurucayali
+ TRUETYPE_TAG('c','p','e', 0 ), // cpe = English-based creoles and pidgins
+ TRUETYPE_TAG('c','p','f', 0 ), // cpf = French-based creoles and pidgins
+ TRUETYPE_TAG('c','p','g', 0 ), // cpg = Cappadocian Greek
+ TRUETYPE_TAG('c','p','i', 0 ), // cpi = Chinese Pidgin English
+ TRUETYPE_TAG('c','p','n', 0 ), // cpn = Cherepon
+ TRUETYPE_TAG('c','p','p', 0 ), // cpp = Portuguese-based creoles and pidgins
+ TRUETYPE_TAG('c','p','s', 0 ), // cps = Capiznon
+ TRUETYPE_TAG('c','p','u', 0 ), // cpu = Pichis Ashéninka
+ TRUETYPE_TAG('c','p','x', 0 ), // cpx = Pu-Xian Chinese
+ TRUETYPE_TAG('c','p','y', 0 ), // cpy = South Ucayali Ashéninka
+ TRUETYPE_TAG('c','q','d', 0 ), // cqd = Chuanqiandian Cluster Miao
+ TRUETYPE_TAG('c','q','u', 0 ), // cqu = Chilean Quechua
+ TRUETYPE_TAG('c','r','a', 0 ), // cra = Chara
+ TRUETYPE_TAG('c','r','b', 0 ), // crb = Island Carib
+ TRUETYPE_TAG('c','r','c', 0 ), // crc = Lonwolwol
+ TRUETYPE_TAG('c','r','d', 0 ), // crd = Coeur d'Alene
+ TRUETYPE_TAG('c','r','f', 0 ), // crf = Caramanta
+ TRUETYPE_TAG('c','r','g', 0 ), // crg = Michif
+ TRUETYPE_TAG('c','r','h', 0 ), // crh = Crimean Tatar
+ TRUETYPE_TAG('c','r','i', 0 ), // cri = Sãotomense
+ TRUETYPE_TAG('c','r','j', 0 ), // crj = Southern East Cree
+ TRUETYPE_TAG('c','r','k', 0 ), // crk = Plains Cree
+ TRUETYPE_TAG('c','r','l', 0 ), // crl = Northern East Cree
+ TRUETYPE_TAG('c','r','m', 0 ), // crm = Moose Cree
+ TRUETYPE_TAG('c','r','n', 0 ), // crn = El Nayar Cora
+ TRUETYPE_TAG('c','r','o', 0 ), // cro = Crow
+ TRUETYPE_TAG('c','r','p', 0 ), // crp = Creoles and pidgins
+ TRUETYPE_TAG('c','r','q', 0 ), // crq = Iyo'wujwa Chorote
+ TRUETYPE_TAG('c','r','r', 0 ), // crr = Carolina Algonquian
+ TRUETYPE_TAG('c','r','s', 0 ), // crs = Seselwa Creole French
+ TRUETYPE_TAG('c','r','t', 0 ), // crt = Iyojwa'ja Chorote
+ TRUETYPE_TAG('c','r','v', 0 ), // crv = Chaura
+ TRUETYPE_TAG('c','r','w', 0 ), // crw = Chrau
+ TRUETYPE_TAG('c','r','x', 0 ), // crx = Carrier
+ TRUETYPE_TAG('c','r','y', 0 ), // cry = Cori
+ TRUETYPE_TAG('c','r','z', 0 ), // crz = Cruzeño
+ TRUETYPE_TAG('c','s','a', 0 ), // csa = Chiltepec Chinantec
+ TRUETYPE_TAG('c','s','b', 0 ), // csb = Kashubian
+ TRUETYPE_TAG('c','s','c', 0 ), // csc = Catalan Sign Language
+ TRUETYPE_TAG('c','s','d', 0 ), // csd = Chiangmai Sign Language
+ TRUETYPE_TAG('c','s','e', 0 ), // cse = Czech Sign Language
+ TRUETYPE_TAG('c','s','f', 0 ), // csf = Cuba Sign Language
+ TRUETYPE_TAG('c','s','g', 0 ), // csg = Chilean Sign Language
+ TRUETYPE_TAG('c','s','h', 0 ), // csh = Asho Chin
+ TRUETYPE_TAG('c','s','i', 0 ), // csi = Coast Miwok
+ TRUETYPE_TAG('c','s','k', 0 ), // csk = Jola-Kasa
+ TRUETYPE_TAG('c','s','l', 0 ), // csl = Chinese Sign Language
+ TRUETYPE_TAG('c','s','m', 0 ), // csm = Central Sierra Miwok
+ TRUETYPE_TAG('c','s','n', 0 ), // csn = Colombian Sign Language
+ TRUETYPE_TAG('c','s','o', 0 ), // cso = Sochiapam Chinantec
+ TRUETYPE_TAG('c','s','q', 0 ), // csq = Croatia Sign Language
+ TRUETYPE_TAG('c','s','r', 0 ), // csr = Costa Rican Sign Language
+ TRUETYPE_TAG('c','s','s', 0 ), // css = Southern Ohlone
+ TRUETYPE_TAG('c','s','t', 0 ), // cst = Northern Ohlone
+ TRUETYPE_TAG('c','s','u', 0 ), // csu = Central Sudanic languages
+ TRUETYPE_TAG('c','s','w', 0 ), // csw = Swampy Cree
+ TRUETYPE_TAG('c','s','y', 0 ), // csy = Siyin Chin
+ TRUETYPE_TAG('c','s','z', 0 ), // csz = Coos
+ TRUETYPE_TAG('c','t','a', 0 ), // cta = Tataltepec Chatino
+ TRUETYPE_TAG('c','t','c', 0 ), // ctc = Chetco
+ TRUETYPE_TAG('c','t','d', 0 ), // ctd = Tedim Chin
+ TRUETYPE_TAG('c','t','e', 0 ), // cte = Tepinapa Chinantec
+ TRUETYPE_TAG('c','t','g', 0 ), // ctg = Chittagonian
+ TRUETYPE_TAG('c','t','l', 0 ), // ctl = Tlacoatzintepec Chinantec
+ TRUETYPE_TAG('c','t','m', 0 ), // ctm = Chitimacha
+ TRUETYPE_TAG('c','t','n', 0 ), // ctn = Chhintange
+ TRUETYPE_TAG('c','t','o', 0 ), // cto = Emberá-Catío
+ TRUETYPE_TAG('c','t','p', 0 ), // ctp = Western Highland Chatino
+ TRUETYPE_TAG('c','t','s', 0 ), // cts = Northern Catanduanes Bicolano
+ TRUETYPE_TAG('c','t','t', 0 ), // ctt = Wayanad Chetti
+ TRUETYPE_TAG('c','t','u', 0 ), // ctu = Chol
+ TRUETYPE_TAG('c','t','z', 0 ), // ctz = Zacatepec Chatino
+ TRUETYPE_TAG('c','u','a', 0 ), // cua = Cua
+ TRUETYPE_TAG('c','u','b', 0 ), // cub = Cubeo
+ TRUETYPE_TAG('c','u','c', 0 ), // cuc = Usila Chinantec
+ TRUETYPE_TAG('c','u','g', 0 ), // cug = Cung
+ TRUETYPE_TAG('c','u','h', 0 ), // cuh = Chuka
+ TRUETYPE_TAG('c','u','i', 0 ), // cui = Cuiba
+ TRUETYPE_TAG('c','u','j', 0 ), // cuj = Mashco Piro
+ TRUETYPE_TAG('c','u','k', 0 ), // cuk = San Blas Kuna
+ TRUETYPE_TAG('c','u','l', 0 ), // cul = Culina
+ TRUETYPE_TAG('c','u','m', 0 ), // cum = Cumeral
+ TRUETYPE_TAG('c','u','o', 0 ), // cuo = Cumanagoto
+ TRUETYPE_TAG('c','u','p', 0 ), // cup = Cupeño
+ TRUETYPE_TAG('c','u','q', 0 ), // cuq = Cun
+ TRUETYPE_TAG('c','u','r', 0 ), // cur = Chhulung
+ TRUETYPE_TAG('c','u','s', 0 ), // cus = Cushitic languages
+ TRUETYPE_TAG('c','u','t', 0 ), // cut = Teutila Cuicatec
+ TRUETYPE_TAG('c','u','u', 0 ), // cuu = Tai Ya
+ TRUETYPE_TAG('c','u','v', 0 ), // cuv = Cuvok
+ TRUETYPE_TAG('c','u','w', 0 ), // cuw = Chukwa
+ TRUETYPE_TAG('c','u','x', 0 ), // cux = Tepeuxila Cuicatec
+ TRUETYPE_TAG('c','v','g', 0 ), // cvg = Chug
+ TRUETYPE_TAG('c','v','n', 0 ), // cvn = Valle Nacional Chinantec
+ TRUETYPE_TAG('c','w','a', 0 ), // cwa = Kabwa
+ TRUETYPE_TAG('c','w','b', 0 ), // cwb = Maindo
+ TRUETYPE_TAG('c','w','d', 0 ), // cwd = Woods Cree
+ TRUETYPE_TAG('c','w','e', 0 ), // cwe = Kwere
+ TRUETYPE_TAG('c','w','g', 0 ), // cwg = Chewong
+ TRUETYPE_TAG('c','w','t', 0 ), // cwt = Kuwaataay
+ TRUETYPE_TAG('c','y','a', 0 ), // cya = Nopala Chatino
+ TRUETYPE_TAG('c','y','b', 0 ), // cyb = Cayubaba
+ TRUETYPE_TAG('c','y','o', 0 ), // cyo = Cuyonon
+ TRUETYPE_TAG('c','z','h', 0 ), // czh = Huizhou Chinese
+ TRUETYPE_TAG('c','z','k', 0 ), // czk = Knaanic
+ TRUETYPE_TAG('c','z','n', 0 ), // czn = Zenzontepec Chatino
+ TRUETYPE_TAG('c','z','o', 0 ), // czo = Min Zhong Chinese
+ TRUETYPE_TAG('c','z','t', 0 ), // czt = Zotung Chin
+ TRUETYPE_TAG('d','a','a', 0 ), // daa = Dangaléat
+ TRUETYPE_TAG('d','a','c', 0 ), // dac = Dambi
+ TRUETYPE_TAG('d','a','d', 0 ), // dad = Marik
+ TRUETYPE_TAG('d','a','e', 0 ), // dae = Duupa
+ TRUETYPE_TAG('d','a','f', 0 ), // daf = Dan
+ TRUETYPE_TAG('d','a','g', 0 ), // dag = Dagbani
+ TRUETYPE_TAG('d','a','h', 0 ), // dah = Gwahatike
+ TRUETYPE_TAG('d','a','i', 0 ), // dai = Day
+ TRUETYPE_TAG('d','a','j', 0 ), // daj = Dar Fur Daju
+ TRUETYPE_TAG('d','a','k', 0 ), // dak = Dakota
+ TRUETYPE_TAG('d','a','l', 0 ), // dal = Dahalo
+ TRUETYPE_TAG('d','a','m', 0 ), // dam = Damakawa
+ TRUETYPE_TAG('d','a','o', 0 ), // dao = Daai Chin
+ TRUETYPE_TAG('d','a','p', 0 ), // dap = Nisi (India)
+ TRUETYPE_TAG('d','a','q', 0 ), // daq = Dandami Maria
+ TRUETYPE_TAG('d','a','r', 0 ), // dar = Dargwa
+ TRUETYPE_TAG('d','a','s', 0 ), // das = Daho-Doo
+ TRUETYPE_TAG('d','a','u', 0 ), // dau = Dar Sila Daju
+ TRUETYPE_TAG('d','a','v', 0 ), // dav = Taita
+ TRUETYPE_TAG('d','a','w', 0 ), // daw = Davawenyo
+ TRUETYPE_TAG('d','a','x', 0 ), // dax = Dayi
+ TRUETYPE_TAG('d','a','y', 0 ), // day = Land Dayak languages
+ TRUETYPE_TAG('d','a','z', 0 ), // daz = Dao
+ TRUETYPE_TAG('d','b','a', 0 ), // dba = Bangi Me
+ TRUETYPE_TAG('d','b','b', 0 ), // dbb = Deno
+ TRUETYPE_TAG('d','b','d', 0 ), // dbd = Dadiya
+ TRUETYPE_TAG('d','b','e', 0 ), // dbe = Dabe
+ TRUETYPE_TAG('d','b','f', 0 ), // dbf = Edopi
+ TRUETYPE_TAG('d','b','g', 0 ), // dbg = Dogul Dom Dogon
+ TRUETYPE_TAG('d','b','i', 0 ), // dbi = Doka
+ TRUETYPE_TAG('d','b','j', 0 ), // dbj = Ida'an
+ TRUETYPE_TAG('d','b','l', 0 ), // dbl = Dyirbal
+ TRUETYPE_TAG('d','b','m', 0 ), // dbm = Duguri
+ TRUETYPE_TAG('d','b','n', 0 ), // dbn = Duriankere
+ TRUETYPE_TAG('d','b','o', 0 ), // dbo = Dulbu
+ TRUETYPE_TAG('d','b','p', 0 ), // dbp = Duwai
+ TRUETYPE_TAG('d','b','q', 0 ), // dbq = Daba
+ TRUETYPE_TAG('d','b','r', 0 ), // dbr = Dabarre
+ TRUETYPE_TAG('d','b','u', 0 ), // dbu = Bondum Dom Dogon
+ TRUETYPE_TAG('d','b','v', 0 ), // dbv = Dungu
+ TRUETYPE_TAG('d','b','y', 0 ), // dby = Dibiyaso
+ TRUETYPE_TAG('d','c','c', 0 ), // dcc = Deccan
+ TRUETYPE_TAG('d','c','r', 0 ), // dcr = Negerhollands
+ TRUETYPE_TAG('d','d','d', 0 ), // ddd = Dongotono
+ TRUETYPE_TAG('d','d','e', 0 ), // dde = Doondo
+ TRUETYPE_TAG('d','d','g', 0 ), // ddg = Fataluku
+ TRUETYPE_TAG('d','d','i', 0 ), // ddi = West Goodenough
+ TRUETYPE_TAG('d','d','j', 0 ), // ddj = Jaru
+ TRUETYPE_TAG('d','d','n', 0 ), // ddn = Dendi (Benin)
+ TRUETYPE_TAG('d','d','o', 0 ), // ddo = Dido
+ TRUETYPE_TAG('d','d','s', 0 ), // dds = Donno So Dogon
+ TRUETYPE_TAG('d','d','w', 0 ), // ddw = Dawera-Daweloor
+ TRUETYPE_TAG('d','e','c', 0 ), // dec = Dagik
+ TRUETYPE_TAG('d','e','d', 0 ), // ded = Dedua
+ TRUETYPE_TAG('d','e','e', 0 ), // dee = Dewoin
+ TRUETYPE_TAG('d','e','f', 0 ), // def = Dezfuli
+ TRUETYPE_TAG('d','e','g', 0 ), // deg = Degema
+ TRUETYPE_TAG('d','e','h', 0 ), // deh = Dehwari
+ TRUETYPE_TAG('d','e','i', 0 ), // dei = Demisa
+ TRUETYPE_TAG('d','e','k', 0 ), // dek = Dek
+ TRUETYPE_TAG('d','e','l', 0 ), // del = Delaware
+ TRUETYPE_TAG('d','e','m', 0 ), // dem = Dem
+ TRUETYPE_TAG('d','e','n', 0 ), // den = Slave (Athapascan)
+ TRUETYPE_TAG('d','e','p', 0 ), // dep = Pidgin Delaware
+ TRUETYPE_TAG('d','e','q', 0 ), // deq = Dendi (Central African Republic)
+ TRUETYPE_TAG('d','e','r', 0 ), // der = Deori
+ TRUETYPE_TAG('d','e','s', 0 ), // des = Desano
+ TRUETYPE_TAG('d','e','v', 0 ), // dev = Domung
+ TRUETYPE_TAG('d','e','z', 0 ), // dez = Dengese
+ TRUETYPE_TAG('d','g','a', 0 ), // dga = Southern Dagaare
+ TRUETYPE_TAG('d','g','b', 0 ), // dgb = Bunoge Dogon
+ TRUETYPE_TAG('d','g','c', 0 ), // dgc = Casiguran Dumagat Agta
+ TRUETYPE_TAG('d','g','d', 0 ), // dgd = Dagaari Dioula
+ TRUETYPE_TAG('d','g','e', 0 ), // dge = Degenan
+ TRUETYPE_TAG('d','g','g', 0 ), // dgg = Doga
+ TRUETYPE_TAG('d','g','h', 0 ), // dgh = Dghwede
+ TRUETYPE_TAG('d','g','i', 0 ), // dgi = Northern Dagara
+ TRUETYPE_TAG('d','g','k', 0 ), // dgk = Dagba
+ TRUETYPE_TAG('d','g','n', 0 ), // dgn = Dagoman
+ TRUETYPE_TAG('d','g','o', 0 ), // dgo = Dogri (individual language)
+ TRUETYPE_TAG('d','g','r', 0 ), // dgr = Dogrib
+ TRUETYPE_TAG('d','g','s', 0 ), // dgs = Dogoso
+ TRUETYPE_TAG('d','g','u', 0 ), // dgu = Degaru
+ TRUETYPE_TAG('d','g','x', 0 ), // dgx = Doghoro
+ TRUETYPE_TAG('d','g','z', 0 ), // dgz = Daga
+ TRUETYPE_TAG('d','h','a', 0 ), // dha = Dhanwar (India)
+ TRUETYPE_TAG('d','h','d', 0 ), // dhd = Dhundari
+ TRUETYPE_TAG('d','h','g', 0 ), // dhg = Dhangu
+ TRUETYPE_TAG('d','h','i', 0 ), // dhi = Dhimal
+ TRUETYPE_TAG('d','h','l', 0 ), // dhl = Dhalandji
+ TRUETYPE_TAG('d','h','m', 0 ), // dhm = Zemba
+ TRUETYPE_TAG('d','h','n', 0 ), // dhn = Dhanki
+ TRUETYPE_TAG('d','h','o', 0 ), // dho = Dhodia
+ TRUETYPE_TAG('d','h','r', 0 ), // dhr = Dhargari
+ TRUETYPE_TAG('d','h','s', 0 ), // dhs = Dhaiso
+ TRUETYPE_TAG('d','h','u', 0 ), // dhu = Dhurga
+ TRUETYPE_TAG('d','h','v', 0 ), // dhv = Dehu
+ TRUETYPE_TAG('d','h','w', 0 ), // dhw = Dhanwar (Nepal)
+ TRUETYPE_TAG('d','i','a', 0 ), // dia = Dia
+ TRUETYPE_TAG('d','i','b', 0 ), // dib = South Central Dinka
+ TRUETYPE_TAG('d','i','c', 0 ), // dic = Lakota Dida
+ TRUETYPE_TAG('d','i','d', 0 ), // did = Didinga
+ TRUETYPE_TAG('d','i','f', 0 ), // dif = Dieri
+ TRUETYPE_TAG('d','i','g', 0 ), // dig = Digo
+ TRUETYPE_TAG('d','i','h', 0 ), // dih = Kumiai
+ TRUETYPE_TAG('d','i','i', 0 ), // dii = Dimbong
+ TRUETYPE_TAG('d','i','j', 0 ), // dij = Dai
+ TRUETYPE_TAG('d','i','k', 0 ), // dik = Southwestern Dinka
+ TRUETYPE_TAG('d','i','l', 0 ), // dil = Dilling
+ TRUETYPE_TAG('d','i','m', 0 ), // dim = Dime
+ TRUETYPE_TAG('d','i','n', 0 ), // din = Dinka
+ TRUETYPE_TAG('d','i','o', 0 ), // dio = Dibo
+ TRUETYPE_TAG('d','i','p', 0 ), // dip = Northeastern Dinka
+ TRUETYPE_TAG('d','i','q', 0 ), // diq = Dimli (individual language)
+ TRUETYPE_TAG('d','i','r', 0 ), // dir = Dirim
+ TRUETYPE_TAG('d','i','s', 0 ), // dis = Dimasa
+ TRUETYPE_TAG('d','i','t', 0 ), // dit = Dirari
+ TRUETYPE_TAG('d','i','u', 0 ), // diu = Diriku
+ TRUETYPE_TAG('d','i','w', 0 ), // diw = Northwestern Dinka
+ TRUETYPE_TAG('d','i','x', 0 ), // dix = Dixon Reef
+ TRUETYPE_TAG('d','i','y', 0 ), // diy = Diuwe
+ TRUETYPE_TAG('d','i','z', 0 ), // diz = Ding
+ TRUETYPE_TAG('d','j','b', 0 ), // djb = Djinba
+ TRUETYPE_TAG('d','j','c', 0 ), // djc = Dar Daju Daju
+ TRUETYPE_TAG('d','j','d', 0 ), // djd = Djamindjung
+ TRUETYPE_TAG('d','j','e', 0 ), // dje = Zarma
+ TRUETYPE_TAG('d','j','f', 0 ), // djf = Djangun
+ TRUETYPE_TAG('d','j','i', 0 ), // dji = Djinang
+ TRUETYPE_TAG('d','j','j', 0 ), // djj = Djeebbana
+ TRUETYPE_TAG('d','j','k', 0 ), // djk = Eastern Maroon Creole
+ TRUETYPE_TAG('d','j','l', 0 ), // djl = Djiwarli
+ TRUETYPE_TAG('d','j','m', 0 ), // djm = Jamsay Dogon
+ TRUETYPE_TAG('d','j','n', 0 ), // djn = Djauan
+ TRUETYPE_TAG('d','j','o', 0 ), // djo = Jangkang
+ TRUETYPE_TAG('d','j','r', 0 ), // djr = Djambarrpuyngu
+ TRUETYPE_TAG('d','j','u', 0 ), // dju = Kapriman
+ TRUETYPE_TAG('d','j','w', 0 ), // djw = Djawi
+ TRUETYPE_TAG('d','k','a', 0 ), // dka = Dakpakha
+ TRUETYPE_TAG('d','k','k', 0 ), // dkk = Dakka
+ TRUETYPE_TAG('d','k','l', 0 ), // dkl = Kolum So Dogon
+ TRUETYPE_TAG('d','k','r', 0 ), // dkr = Kuijau
+ TRUETYPE_TAG('d','k','s', 0 ), // dks = Southeastern Dinka
+ TRUETYPE_TAG('d','k','x', 0 ), // dkx = Mazagway
+ TRUETYPE_TAG('d','l','g', 0 ), // dlg = Dolgan
+ TRUETYPE_TAG('d','l','m', 0 ), // dlm = Dalmatian
+ TRUETYPE_TAG('d','l','n', 0 ), // dln = Darlong
+ TRUETYPE_TAG('d','m','a', 0 ), // dma = Duma
+ TRUETYPE_TAG('d','m','b', 0 ), // dmb = Mombo Dogon
+ TRUETYPE_TAG('d','m','c', 0 ), // dmc = Dimir
+ TRUETYPE_TAG('d','m','e', 0 ), // dme = Dugwor
+ TRUETYPE_TAG('d','m','g', 0 ), // dmg = Upper Kinabatangan
+ TRUETYPE_TAG('d','m','k', 0 ), // dmk = Domaaki
+ TRUETYPE_TAG('d','m','l', 0 ), // dml = Dameli
+ TRUETYPE_TAG('d','m','m', 0 ), // dmm = Dama
+ TRUETYPE_TAG('d','m','n', 0 ), // dmn = Mande languages
+ TRUETYPE_TAG('d','m','o', 0 ), // dmo = Kemezung
+ TRUETYPE_TAG('d','m','r', 0 ), // dmr = East Damar
+ TRUETYPE_TAG('d','m','s', 0 ), // dms = Dampelas
+ TRUETYPE_TAG('d','m','u', 0 ), // dmu = Dubu
+ TRUETYPE_TAG('d','m','v', 0 ), // dmv = Dumpas
+ TRUETYPE_TAG('d','m','x', 0 ), // dmx = Dema
+ TRUETYPE_TAG('d','m','y', 0 ), // dmy = Demta
+ TRUETYPE_TAG('d','n','a', 0 ), // dna = Upper Grand Valley Dani
+ TRUETYPE_TAG('d','n','d', 0 ), // dnd = Daonda
+ TRUETYPE_TAG('d','n','e', 0 ), // dne = Ndendeule
+ TRUETYPE_TAG('d','n','g', 0 ), // dng = Dungan
+ TRUETYPE_TAG('d','n','i', 0 ), // dni = Lower Grand Valley Dani
+ TRUETYPE_TAG('d','n','k', 0 ), // dnk = Dengka
+ TRUETYPE_TAG('d','n','n', 0 ), // dnn = Dzùùngoo
+ TRUETYPE_TAG('d','n','r', 0 ), // dnr = Danaru
+ TRUETYPE_TAG('d','n','t', 0 ), // dnt = Mid Grand Valley Dani
+ TRUETYPE_TAG('d','n','u', 0 ), // dnu = Danau
+ TRUETYPE_TAG('d','n','w', 0 ), // dnw = Western Dani
+ TRUETYPE_TAG('d','n','y', 0 ), // dny = Dení
+ TRUETYPE_TAG('d','o','a', 0 ), // doa = Dom
+ TRUETYPE_TAG('d','o','b', 0 ), // dob = Dobu
+ TRUETYPE_TAG('d','o','c', 0 ), // doc = Northern Dong
+ TRUETYPE_TAG('d','o','e', 0 ), // doe = Doe
+ TRUETYPE_TAG('d','o','f', 0 ), // dof = Domu
+ TRUETYPE_TAG('d','o','h', 0 ), // doh = Dong
+ TRUETYPE_TAG('d','o','i', 0 ), // doi = Dogri (macrolanguage)
+ TRUETYPE_TAG('d','o','k', 0 ), // dok = Dondo
+ TRUETYPE_TAG('d','o','l', 0 ), // dol = Doso
+ TRUETYPE_TAG('d','o','n', 0 ), // don = Toura (Papua New Guinea)
+ TRUETYPE_TAG('d','o','o', 0 ), // doo = Dongo
+ TRUETYPE_TAG('d','o','p', 0 ), // dop = Lukpa
+ TRUETYPE_TAG('d','o','q', 0 ), // doq = Dominican Sign Language
+ TRUETYPE_TAG('d','o','r', 0 ), // dor = Dori'o
+ TRUETYPE_TAG('d','o','s', 0 ), // dos = Dogosé
+ TRUETYPE_TAG('d','o','t', 0 ), // dot = Dass
+ TRUETYPE_TAG('d','o','v', 0 ), // dov = Dombe
+ TRUETYPE_TAG('d','o','w', 0 ), // dow = Doyayo
+ TRUETYPE_TAG('d','o','x', 0 ), // dox = Bussa
+ TRUETYPE_TAG('d','o','y', 0 ), // doy = Dompo
+ TRUETYPE_TAG('d','o','z', 0 ), // doz = Dorze
+ TRUETYPE_TAG('d','p','p', 0 ), // dpp = Papar
+ TRUETYPE_TAG('d','r','a', 0 ), // dra = Dravidian languages
+ TRUETYPE_TAG('d','r','b', 0 ), // drb = Dair
+ TRUETYPE_TAG('d','r','c', 0 ), // drc = Minderico
+ TRUETYPE_TAG('d','r','d', 0 ), // drd = Darmiya
+ TRUETYPE_TAG('d','r','e', 0 ), // dre = Dolpo
+ TRUETYPE_TAG('d','r','g', 0 ), // drg = Rungus
+ TRUETYPE_TAG('d','r','h', 0 ), // drh = Darkhat
+ TRUETYPE_TAG('d','r','i', 0 ), // dri = C'lela
+ TRUETYPE_TAG('d','r','l', 0 ), // drl = Darling
+ TRUETYPE_TAG('d','r','n', 0 ), // drn = West Damar
+ TRUETYPE_TAG('d','r','o', 0 ), // dro = Daro-Matu Melanau
+ TRUETYPE_TAG('d','r','q', 0 ), // drq = Dura
+ TRUETYPE_TAG('d','r','r', 0 ), // drr = Dororo
+ TRUETYPE_TAG('d','r','s', 0 ), // drs = Gedeo
+ TRUETYPE_TAG('d','r','t', 0 ), // drt = Drents
+ TRUETYPE_TAG('d','r','u', 0 ), // dru = Rukai
+ TRUETYPE_TAG('d','r','w', 0 ), // drw = Darwazi
+ TRUETYPE_TAG('d','r','y', 0 ), // dry = Darai
+ TRUETYPE_TAG('d','s','b', 0 ), // dsb = Lower Sorbian
+ TRUETYPE_TAG('d','s','e', 0 ), // dse = Dutch Sign Language
+ TRUETYPE_TAG('d','s','h', 0 ), // dsh = Daasanach
+ TRUETYPE_TAG('d','s','i', 0 ), // dsi = Disa
+ TRUETYPE_TAG('d','s','l', 0 ), // dsl = Danish Sign Language
+ TRUETYPE_TAG('d','s','n', 0 ), // dsn = Dusner
+ TRUETYPE_TAG('d','s','o', 0 ), // dso = Desiya
+ TRUETYPE_TAG('d','s','q', 0 ), // dsq = Tadaksahak
+ TRUETYPE_TAG('d','t','a', 0 ), // dta = Daur
+ TRUETYPE_TAG('d','t','b', 0 ), // dtb = Labuk-Kinabatangan Kadazan
+ TRUETYPE_TAG('d','t','d', 0 ), // dtd = Ditidaht
+ TRUETYPE_TAG('d','t','i', 0 ), // dti = Ana Tinga Dogon
+ TRUETYPE_TAG('d','t','k', 0 ), // dtk = Tene Kan Dogon
+ TRUETYPE_TAG('d','t','m', 0 ), // dtm = Tomo Kan Dogon
+ TRUETYPE_TAG('d','t','p', 0 ), // dtp = Central Dusun
+ TRUETYPE_TAG('d','t','r', 0 ), // dtr = Lotud
+ TRUETYPE_TAG('d','t','s', 0 ), // dts = Toro So Dogon
+ TRUETYPE_TAG('d','t','t', 0 ), // dtt = Toro Tegu Dogon
+ TRUETYPE_TAG('d','t','u', 0 ), // dtu = Tebul Ure Dogon
+ TRUETYPE_TAG('d','u','a', 0 ), // dua = Duala
+ TRUETYPE_TAG('d','u','b', 0 ), // dub = Dubli
+ TRUETYPE_TAG('d','u','c', 0 ), // duc = Duna
+ TRUETYPE_TAG('d','u','d', 0 ), // dud = Hun-Saare
+ TRUETYPE_TAG('d','u','e', 0 ), // due = Umiray Dumaget Agta
+ TRUETYPE_TAG('d','u','f', 0 ), // duf = Dumbea
+ TRUETYPE_TAG('d','u','g', 0 ), // dug = Duruma
+ TRUETYPE_TAG('d','u','h', 0 ), // duh = Dungra Bhil
+ TRUETYPE_TAG('d','u','i', 0 ), // dui = Dumun
+ TRUETYPE_TAG('d','u','j', 0 ), // duj = Dhuwal
+ TRUETYPE_TAG('d','u','k', 0 ), // duk = Uyajitaya
+ TRUETYPE_TAG('d','u','l', 0 ), // dul = Alabat Island Agta
+ TRUETYPE_TAG('d','u','m', 0 ), // dum = Middle Dutch (ca. 1050-1350)
+ TRUETYPE_TAG('d','u','n', 0 ), // dun = Dusun Deyah
+ TRUETYPE_TAG('d','u','o', 0 ), // duo = Dupaninan Agta
+ TRUETYPE_TAG('d','u','p', 0 ), // dup = Duano
+ TRUETYPE_TAG('d','u','q', 0 ), // duq = Dusun Malang
+ TRUETYPE_TAG('d','u','r', 0 ), // dur = Dii
+ TRUETYPE_TAG('d','u','s', 0 ), // dus = Dumi
+ TRUETYPE_TAG('d','u','u', 0 ), // duu = Drung
+ TRUETYPE_TAG('d','u','v', 0 ), // duv = Duvle
+ TRUETYPE_TAG('d','u','w', 0 ), // duw = Dusun Witu
+ TRUETYPE_TAG('d','u','x', 0 ), // dux = Duungooma
+ TRUETYPE_TAG('d','u','y', 0 ), // duy = Dicamay Agta
+ TRUETYPE_TAG('d','u','z', 0 ), // duz = Duli
+ TRUETYPE_TAG('d','v','a', 0 ), // dva = Duau
+ TRUETYPE_TAG('d','w','a', 0 ), // dwa = Diri
+ TRUETYPE_TAG('d','w','l', 0 ), // dwl = Walo Kumbe Dogon
+ TRUETYPE_TAG('d','w','r', 0 ), // dwr = Dawro
+ TRUETYPE_TAG('d','w','s', 0 ), // dws = Dutton World Speedwords
+ TRUETYPE_TAG('d','w','w', 0 ), // dww = Dawawa
+ TRUETYPE_TAG('d','y','a', 0 ), // dya = Dyan
+ TRUETYPE_TAG('d','y','b', 0 ), // dyb = Dyaberdyaber
+ TRUETYPE_TAG('d','y','d', 0 ), // dyd = Dyugun
+ TRUETYPE_TAG('d','y','g', 0 ), // dyg = Villa Viciosa Agta
+ TRUETYPE_TAG('d','y','i', 0 ), // dyi = Djimini Senoufo
+ TRUETYPE_TAG('d','y','m', 0 ), // dym = Yanda Dom Dogon
+ TRUETYPE_TAG('d','y','n', 0 ), // dyn = Dyangadi
+ TRUETYPE_TAG('d','y','o', 0 ), // dyo = Jola-Fonyi
+ TRUETYPE_TAG('d','y','u', 0 ), // dyu = Dyula
+ TRUETYPE_TAG('d','y','y', 0 ), // dyy = Dyaabugay
+ TRUETYPE_TAG('d','z','a', 0 ), // dza = Tunzu
+ TRUETYPE_TAG('d','z','d', 0 ), // dzd = Daza
+ TRUETYPE_TAG('d','z','g', 0 ), // dzg = Dazaga
+ TRUETYPE_TAG('d','z','l', 0 ), // dzl = Dzalakha
+ TRUETYPE_TAG('d','z','n', 0 ), // dzn = Dzando
+ TRUETYPE_TAG('e','b','g', 0 ), // ebg = Ebughu
+ TRUETYPE_TAG('e','b','k', 0 ), // ebk = Eastern Bontok
+ TRUETYPE_TAG('e','b','o', 0 ), // ebo = Teke-Ebo
+ TRUETYPE_TAG('e','b','r', 0 ), // ebr = Ebrié
+ TRUETYPE_TAG('e','b','u', 0 ), // ebu = Embu
+ TRUETYPE_TAG('e','c','r', 0 ), // ecr = Eteocretan
+ TRUETYPE_TAG('e','c','s', 0 ), // ecs = Ecuadorian Sign Language
+ TRUETYPE_TAG('e','c','y', 0 ), // ecy = Eteocypriot
+ TRUETYPE_TAG('e','e','e', 0 ), // eee = E
+ TRUETYPE_TAG('e','f','a', 0 ), // efa = Efai
+ TRUETYPE_TAG('e','f','e', 0 ), // efe = Efe
+ TRUETYPE_TAG('e','f','i', 0 ), // efi = Efik
+ TRUETYPE_TAG('e','g','a', 0 ), // ega = Ega
+ TRUETYPE_TAG('e','g','l', 0 ), // egl = Emilian
+ TRUETYPE_TAG('e','g','o', 0 ), // ego = Eggon
+ TRUETYPE_TAG('e','g','x', 0 ), // egx = Egyptian languages
+ TRUETYPE_TAG('e','g','y', 0 ), // egy = Egyptian (Ancient)
+ TRUETYPE_TAG('e','h','u', 0 ), // ehu = Ehueun
+ TRUETYPE_TAG('e','i','p', 0 ), // eip = Eipomek
+ TRUETYPE_TAG('e','i','t', 0 ), // eit = Eitiep
+ TRUETYPE_TAG('e','i','v', 0 ), // eiv = Askopan
+ TRUETYPE_TAG('e','j','a', 0 ), // eja = Ejamat
+ TRUETYPE_TAG('e','k','a', 0 ), // eka = Ekajuk
+ TRUETYPE_TAG('e','k','e', 0 ), // eke = Ekit
+ TRUETYPE_TAG('e','k','g', 0 ), // ekg = Ekari
+ TRUETYPE_TAG('e','k','i', 0 ), // eki = Eki
+ TRUETYPE_TAG('e','k','k', 0 ), // ekk = Standard Estonian
+ TRUETYPE_TAG('e','k','l', 0 ), // ekl = Kol
+ TRUETYPE_TAG('e','k','m', 0 ), // ekm = Elip
+ TRUETYPE_TAG('e','k','o', 0 ), // eko = Koti
+ TRUETYPE_TAG('e','k','p', 0 ), // ekp = Ekpeye
+ TRUETYPE_TAG('e','k','r', 0 ), // ekr = Yace
+ TRUETYPE_TAG('e','k','y', 0 ), // eky = Eastern Kayah
+ TRUETYPE_TAG('e','l','e', 0 ), // ele = Elepi
+ TRUETYPE_TAG('e','l','h', 0 ), // elh = El Hugeirat
+ TRUETYPE_TAG('e','l','i', 0 ), // eli = Nding
+ TRUETYPE_TAG('e','l','k', 0 ), // elk = Elkei
+ TRUETYPE_TAG('e','l','m', 0 ), // elm = Eleme
+ TRUETYPE_TAG('e','l','o', 0 ), // elo = El Molo
+ TRUETYPE_TAG('e','l','p', 0 ), // elp = Elpaputih
+ TRUETYPE_TAG('e','l','u', 0 ), // elu = Elu
+ TRUETYPE_TAG('e','l','x', 0 ), // elx = Elamite
+ TRUETYPE_TAG('e','m','a', 0 ), // ema = Emai-Iuleha-Ora
+ TRUETYPE_TAG('e','m','b', 0 ), // emb = Embaloh
+ TRUETYPE_TAG('e','m','e', 0 ), // eme = Emerillon
+ TRUETYPE_TAG('e','m','g', 0 ), // emg = Eastern Meohang
+ TRUETYPE_TAG('e','m','i', 0 ), // emi = Mussau-Emira
+ TRUETYPE_TAG('e','m','k', 0 ), // emk = Eastern Maninkakan
+ TRUETYPE_TAG('e','m','m', 0 ), // emm = Mamulique
+ TRUETYPE_TAG('e','m','n', 0 ), // emn = Eman
+ TRUETYPE_TAG('e','m','o', 0 ), // emo = Emok
+ TRUETYPE_TAG('e','m','p', 0 ), // emp = Northern Emberá
+ TRUETYPE_TAG('e','m','s', 0 ), // ems = Pacific Gulf Yupik
+ TRUETYPE_TAG('e','m','u', 0 ), // emu = Eastern Muria
+ TRUETYPE_TAG('e','m','w', 0 ), // emw = Emplawas
+ TRUETYPE_TAG('e','m','x', 0 ), // emx = Erromintxela
+ TRUETYPE_TAG('e','m','y', 0 ), // emy = Epigraphic Mayan
+ TRUETYPE_TAG('e','n','a', 0 ), // ena = Apali
+ TRUETYPE_TAG('e','n','b', 0 ), // enb = Markweeta
+ TRUETYPE_TAG('e','n','c', 0 ), // enc = En
+ TRUETYPE_TAG('e','n','d', 0 ), // end = Ende
+ TRUETYPE_TAG('e','n','f', 0 ), // enf = Forest Enets
+ TRUETYPE_TAG('e','n','h', 0 ), // enh = Tundra Enets
+ TRUETYPE_TAG('e','n','m', 0 ), // enm = Middle English (1100-1500)
+ TRUETYPE_TAG('e','n','n', 0 ), // enn = Engenni
+ TRUETYPE_TAG('e','n','o', 0 ), // eno = Enggano
+ TRUETYPE_TAG('e','n','q', 0 ), // enq = Enga
+ TRUETYPE_TAG('e','n','r', 0 ), // enr = Emumu
+ TRUETYPE_TAG('e','n','u', 0 ), // enu = Enu
+ TRUETYPE_TAG('e','n','v', 0 ), // env = Enwan (Edu State)
+ TRUETYPE_TAG('e','n','w', 0 ), // enw = Enwan (Akwa Ibom State)
+ TRUETYPE_TAG('e','o','t', 0 ), // eot = Beti (Côte d'Ivoire)
+ TRUETYPE_TAG('e','p','i', 0 ), // epi = Epie
+ TRUETYPE_TAG('e','r','a', 0 ), // era = Eravallan
+ TRUETYPE_TAG('e','r','g', 0 ), // erg = Sie
+ TRUETYPE_TAG('e','r','h', 0 ), // erh = Eruwa
+ TRUETYPE_TAG('e','r','i', 0 ), // eri = Ogea
+ TRUETYPE_TAG('e','r','k', 0 ), // erk = South Efate
+ TRUETYPE_TAG('e','r','o', 0 ), // ero = Horpa
+ TRUETYPE_TAG('e','r','r', 0 ), // err = Erre
+ TRUETYPE_TAG('e','r','s', 0 ), // ers = Ersu
+ TRUETYPE_TAG('e','r','t', 0 ), // ert = Eritai
+ TRUETYPE_TAG('e','r','w', 0 ), // erw = Erokwanas
+ TRUETYPE_TAG('e','s','e', 0 ), // ese = Ese Ejja
+ TRUETYPE_TAG('e','s','h', 0 ), // esh = Eshtehardi
+ TRUETYPE_TAG('e','s','i', 0 ), // esi = North Alaskan Inupiatun
+ TRUETYPE_TAG('e','s','k', 0 ), // esk = Northwest Alaska Inupiatun
+ TRUETYPE_TAG('e','s','l', 0 ), // esl = Egypt Sign Language
+ TRUETYPE_TAG('e','s','m', 0 ), // esm = Esuma
+ TRUETYPE_TAG('e','s','n', 0 ), // esn = Salvadoran Sign Language
+ TRUETYPE_TAG('e','s','o', 0 ), // eso = Estonian Sign Language
+ TRUETYPE_TAG('e','s','q', 0 ), // esq = Esselen
+ TRUETYPE_TAG('e','s','s', 0 ), // ess = Central Siberian Yupik
+ TRUETYPE_TAG('e','s','u', 0 ), // esu = Central Yupik
+ TRUETYPE_TAG('e','s','x', 0 ), // esx = Eskimo-Aleut languages
+ TRUETYPE_TAG('e','t','b', 0 ), // etb = Etebi
+ TRUETYPE_TAG('e','t','c', 0 ), // etc = Etchemin
+ TRUETYPE_TAG('e','t','h', 0 ), // eth = Ethiopian Sign Language
+ TRUETYPE_TAG('e','t','n', 0 ), // etn = Eton (Vanuatu)
+ TRUETYPE_TAG('e','t','o', 0 ), // eto = Eton (Cameroon)
+ TRUETYPE_TAG('e','t','r', 0 ), // etr = Edolo
+ TRUETYPE_TAG('e','t','s', 0 ), // ets = Yekhee
+ TRUETYPE_TAG('e','t','t', 0 ), // ett = Etruscan
+ TRUETYPE_TAG('e','t','u', 0 ), // etu = Ejagham
+ TRUETYPE_TAG('e','t','x', 0 ), // etx = Eten
+ TRUETYPE_TAG('e','t','z', 0 ), // etz = Semimi
+ TRUETYPE_TAG('e','u','q', 0 ), // euq = Basque (family)
+ TRUETYPE_TAG('e','v','e', 0 ), // eve = Even
+ TRUETYPE_TAG('e','v','h', 0 ), // evh = Uvbie
+ TRUETYPE_TAG('e','v','n', 0 ), // evn = Evenki
+ TRUETYPE_TAG('e','w','o', 0 ), // ewo = Ewondo
+ TRUETYPE_TAG('e','x','t', 0 ), // ext = Extremaduran
+ TRUETYPE_TAG('e','y','a', 0 ), // eya = Eyak
+ TRUETYPE_TAG('e','y','o', 0 ), // eyo = Keiyo
+ TRUETYPE_TAG('e','z','e', 0 ), // eze = Uzekwe
+ TRUETYPE_TAG('f','a','a', 0 ), // faa = Fasu
+ TRUETYPE_TAG('f','a','b', 0 ), // fab = Fa D'ambu
+ TRUETYPE_TAG('f','a','d', 0 ), // fad = Wagi
+ TRUETYPE_TAG('f','a','f', 0 ), // faf = Fagani
+ TRUETYPE_TAG('f','a','g', 0 ), // fag = Finongan
+ TRUETYPE_TAG('f','a','h', 0 ), // fah = Baissa Fali
+ TRUETYPE_TAG('f','a','i', 0 ), // fai = Faiwol
+ TRUETYPE_TAG('f','a','j', 0 ), // faj = Faita
+ TRUETYPE_TAG('f','a','k', 0 ), // fak = Fang (Cameroon)
+ TRUETYPE_TAG('f','a','l', 0 ), // fal = South Fali
+ TRUETYPE_TAG('f','a','m', 0 ), // fam = Fam
+ TRUETYPE_TAG('f','a','n', 0 ), // fan = Fang (Equatorial Guinea)
+ TRUETYPE_TAG('f','a','p', 0 ), // fap = Palor
+ TRUETYPE_TAG('f','a','r', 0 ), // far = Fataleka
+ TRUETYPE_TAG('f','a','t', 0 ), // fat = Fanti
+ TRUETYPE_TAG('f','a','u', 0 ), // fau = Fayu
+ TRUETYPE_TAG('f','a','x', 0 ), // fax = Fala
+ TRUETYPE_TAG('f','a','y', 0 ), // fay = Southwestern Fars
+ TRUETYPE_TAG('f','a','z', 0 ), // faz = Northwestern Fars
+ TRUETYPE_TAG('f','b','l', 0 ), // fbl = West Albay Bikol
+ TRUETYPE_TAG('f','c','s', 0 ), // fcs = Quebec Sign Language
+ TRUETYPE_TAG('f','e','r', 0 ), // fer = Feroge
+ TRUETYPE_TAG('f','f','i', 0 ), // ffi = Foia Foia
+ TRUETYPE_TAG('f','f','m', 0 ), // ffm = Maasina Fulfulde
+ TRUETYPE_TAG('f','g','r', 0 ), // fgr = Fongoro
+ TRUETYPE_TAG('f','i','a', 0 ), // fia = Nobiin
+ TRUETYPE_TAG('f','i','e', 0 ), // fie = Fyer
+ TRUETYPE_TAG('f','i','l', 0 ), // fil = Filipino
+ TRUETYPE_TAG('f','i','p', 0 ), // fip = Fipa
+ TRUETYPE_TAG('f','i','r', 0 ), // fir = Firan
+ TRUETYPE_TAG('f','i','t', 0 ), // fit = Tornedalen Finnish
+ TRUETYPE_TAG('f','i','u', 0 ), // fiu = Finno-Ugrian languages
+ TRUETYPE_TAG('f','i','w', 0 ), // fiw = Fiwaga
+ TRUETYPE_TAG('f','k','v', 0 ), // fkv = Kven Finnish
+ TRUETYPE_TAG('f','l','a', 0 ), // fla = Kalispel-Pend d'Oreille
+ TRUETYPE_TAG('f','l','h', 0 ), // flh = Foau
+ TRUETYPE_TAG('f','l','i', 0 ), // fli = Fali
+ TRUETYPE_TAG('f','l','l', 0 ), // fll = North Fali
+ TRUETYPE_TAG('f','l','n', 0 ), // fln = Flinders Island
+ TRUETYPE_TAG('f','l','r', 0 ), // flr = Fuliiru
+ TRUETYPE_TAG('f','l','y', 0 ), // fly = Tsotsitaal
+ TRUETYPE_TAG('f','m','p', 0 ), // fmp = Fe'fe'
+ TRUETYPE_TAG('f','m','u', 0 ), // fmu = Far Western Muria
+ TRUETYPE_TAG('f','n','g', 0 ), // fng = Fanagalo
+ TRUETYPE_TAG('f','n','i', 0 ), // fni = Fania
+ TRUETYPE_TAG('f','o','d', 0 ), // fod = Foodo
+ TRUETYPE_TAG('f','o','i', 0 ), // foi = Foi
+ TRUETYPE_TAG('f','o','m', 0 ), // fom = Foma
+ TRUETYPE_TAG('f','o','n', 0 ), // fon = Fon
+ TRUETYPE_TAG('f','o','r', 0 ), // for = Fore
+ TRUETYPE_TAG('f','o','s', 0 ), // fos = Siraya
+ TRUETYPE_TAG('f','o','x', 0 ), // fox = Formosan languages
+ TRUETYPE_TAG('f','p','e', 0 ), // fpe = Fernando Po Creole English
+ TRUETYPE_TAG('f','q','s', 0 ), // fqs = Fas
+ TRUETYPE_TAG('f','r','c', 0 ), // frc = Cajun French
+ TRUETYPE_TAG('f','r','d', 0 ), // frd = Fordata
+ TRUETYPE_TAG('f','r','k', 0 ), // frk = Frankish
+ TRUETYPE_TAG('f','r','m', 0 ), // frm = Middle French (ca. 1400-1600)
+ TRUETYPE_TAG('f','r','o', 0 ), // fro = Old French (842-ca. 1400)
+ TRUETYPE_TAG('f','r','p', 0 ), // frp = Arpitan
+ TRUETYPE_TAG('f','r','q', 0 ), // frq = Forak
+ TRUETYPE_TAG('f','r','r', 0 ), // frr = Northern Frisian
+ TRUETYPE_TAG('f','r','s', 0 ), // frs = Eastern Frisian
+ TRUETYPE_TAG('f','r','t', 0 ), // frt = Fortsenal
+ TRUETYPE_TAG('f','s','e', 0 ), // fse = Finnish Sign Language
+ TRUETYPE_TAG('f','s','l', 0 ), // fsl = French Sign Language
+ TRUETYPE_TAG('f','s','s', 0 ), // fss = Finland-Swedish Sign Language
+ TRUETYPE_TAG('f','u','b', 0 ), // fub = Adamawa Fulfulde
+ TRUETYPE_TAG('f','u','c', 0 ), // fuc = Pulaar
+ TRUETYPE_TAG('f','u','d', 0 ), // fud = East Futuna
+ TRUETYPE_TAG('f','u','e', 0 ), // fue = Borgu Fulfulde
+ TRUETYPE_TAG('f','u','f', 0 ), // fuf = Pular
+ TRUETYPE_TAG('f','u','h', 0 ), // fuh = Western Niger Fulfulde
+ TRUETYPE_TAG('f','u','i', 0 ), // fui = Bagirmi Fulfulde
+ TRUETYPE_TAG('f','u','j', 0 ), // fuj = Ko
+ TRUETYPE_TAG('f','u','m', 0 ), // fum = Fum
+ TRUETYPE_TAG('f','u','n', 0 ), // fun = Fulniô
+ TRUETYPE_TAG('f','u','q', 0 ), // fuq = Central-Eastern Niger Fulfulde
+ TRUETYPE_TAG('f','u','r', 0 ), // fur = Friulian
+ TRUETYPE_TAG('f','u','t', 0 ), // fut = Futuna-Aniwa
+ TRUETYPE_TAG('f','u','u', 0 ), // fuu = Furu
+ TRUETYPE_TAG('f','u','v', 0 ), // fuv = Nigerian Fulfulde
+ TRUETYPE_TAG('f','u','y', 0 ), // fuy = Fuyug
+ TRUETYPE_TAG('f','v','r', 0 ), // fvr = Fur
+ TRUETYPE_TAG('f','w','a', 0 ), // fwa = Fwâi
+ TRUETYPE_TAG('f','w','e', 0 ), // fwe = Fwe
+ TRUETYPE_TAG('g','a','a', 0 ), // gaa = Ga
+ TRUETYPE_TAG('g','a','b', 0 ), // gab = Gabri
+ TRUETYPE_TAG('g','a','c', 0 ), // gac = Mixed Great Andamanese
+ TRUETYPE_TAG('g','a','d', 0 ), // gad = Gaddang
+ TRUETYPE_TAG('g','a','e', 0 ), // gae = Guarequena
+ TRUETYPE_TAG('g','a','f', 0 ), // gaf = Gende
+ TRUETYPE_TAG('g','a','g', 0 ), // gag = Gagauz
+ TRUETYPE_TAG('g','a','h', 0 ), // gah = Alekano
+ TRUETYPE_TAG('g','a','i', 0 ), // gai = Borei
+ TRUETYPE_TAG('g','a','j', 0 ), // gaj = Gadsup
+ TRUETYPE_TAG('g','a','k', 0 ), // gak = Gamkonora
+ TRUETYPE_TAG('g','a','l', 0 ), // gal = Galoli
+ TRUETYPE_TAG('g','a','m', 0 ), // gam = Kandawo
+ TRUETYPE_TAG('g','a','n', 0 ), // gan = Gan Chinese
+ TRUETYPE_TAG('g','a','o', 0 ), // gao = Gants
+ TRUETYPE_TAG('g','a','p', 0 ), // gap = Gal
+ TRUETYPE_TAG('g','a','q', 0 ), // gaq = Gata'
+ TRUETYPE_TAG('g','a','r', 0 ), // gar = Galeya
+ TRUETYPE_TAG('g','a','s', 0 ), // gas = Adiwasi Garasia
+ TRUETYPE_TAG('g','a','t', 0 ), // gat = Kenati
+ TRUETYPE_TAG('g','a','u', 0 ), // gau = Mudhili Gadaba
+ TRUETYPE_TAG('g','a','v', 0 ), // gav = Gabutamon
+ TRUETYPE_TAG('g','a','w', 0 ), // gaw = Nobonob
+ TRUETYPE_TAG('g','a','x', 0 ), // gax = Borana-Arsi-Guji Oromo
+ TRUETYPE_TAG('g','a','y', 0 ), // gay = Gayo
+ TRUETYPE_TAG('g','a','z', 0 ), // gaz = West Central Oromo
+ TRUETYPE_TAG('g','b','a', 0 ), // gba = Gbaya (Central African Republic)
+ TRUETYPE_TAG('g','b','b', 0 ), // gbb = Kaytetye
+ TRUETYPE_TAG('g','b','c', 0 ), // gbc = Garawa
+ TRUETYPE_TAG('g','b','d', 0 ), // gbd = Karadjeri
+ TRUETYPE_TAG('g','b','e', 0 ), // gbe = Niksek
+ TRUETYPE_TAG('g','b','f', 0 ), // gbf = Gaikundi
+ TRUETYPE_TAG('g','b','g', 0 ), // gbg = Gbanziri
+ TRUETYPE_TAG('g','b','h', 0 ), // gbh = Defi Gbe
+ TRUETYPE_TAG('g','b','i', 0 ), // gbi = Galela
+ TRUETYPE_TAG('g','b','j', 0 ), // gbj = Bodo Gadaba
+ TRUETYPE_TAG('g','b','k', 0 ), // gbk = Gaddi
+ TRUETYPE_TAG('g','b','l', 0 ), // gbl = Gamit
+ TRUETYPE_TAG('g','b','m', 0 ), // gbm = Garhwali
+ TRUETYPE_TAG('g','b','n', 0 ), // gbn = Mo'da
+ TRUETYPE_TAG('g','b','o', 0 ), // gbo = Northern Grebo
+ TRUETYPE_TAG('g','b','p', 0 ), // gbp = Gbaya-Bossangoa
+ TRUETYPE_TAG('g','b','q', 0 ), // gbq = Gbaya-Bozoum
+ TRUETYPE_TAG('g','b','r', 0 ), // gbr = Gbagyi
+ TRUETYPE_TAG('g','b','s', 0 ), // gbs = Gbesi Gbe
+ TRUETYPE_TAG('g','b','u', 0 ), // gbu = Gagadu
+ TRUETYPE_TAG('g','b','v', 0 ), // gbv = Gbanu
+ TRUETYPE_TAG('g','b','x', 0 ), // gbx = Eastern Xwla Gbe
+ TRUETYPE_TAG('g','b','y', 0 ), // gby = Gbari
+ TRUETYPE_TAG('g','b','z', 0 ), // gbz = Zoroastrian Dari
+ TRUETYPE_TAG('g','c','c', 0 ), // gcc = Mali
+ TRUETYPE_TAG('g','c','d', 0 ), // gcd = Ganggalida
+ TRUETYPE_TAG('g','c','e', 0 ), // gce = Galice
+ TRUETYPE_TAG('g','c','f', 0 ), // gcf = Guadeloupean Creole French
+ TRUETYPE_TAG('g','c','l', 0 ), // gcl = Grenadian Creole English
+ TRUETYPE_TAG('g','c','n', 0 ), // gcn = Gaina
+ TRUETYPE_TAG('g','c','r', 0 ), // gcr = Guianese Creole French
+ TRUETYPE_TAG('g','c','t', 0 ), // gct = Colonia Tovar German
+ TRUETYPE_TAG('g','d','a', 0 ), // gda = Gade Lohar
+ TRUETYPE_TAG('g','d','b', 0 ), // gdb = Pottangi Ollar Gadaba
+ TRUETYPE_TAG('g','d','c', 0 ), // gdc = Gugu Badhun
+ TRUETYPE_TAG('g','d','d', 0 ), // gdd = Gedaged
+ TRUETYPE_TAG('g','d','e', 0 ), // gde = Gude
+ TRUETYPE_TAG('g','d','f', 0 ), // gdf = Guduf-Gava
+ TRUETYPE_TAG('g','d','g', 0 ), // gdg = Ga'dang
+ TRUETYPE_TAG('g','d','h', 0 ), // gdh = Gadjerawang
+ TRUETYPE_TAG('g','d','i', 0 ), // gdi = Gundi
+ TRUETYPE_TAG('g','d','j', 0 ), // gdj = Gurdjar
+ TRUETYPE_TAG('g','d','k', 0 ), // gdk = Gadang
+ TRUETYPE_TAG('g','d','l', 0 ), // gdl = Dirasha
+ TRUETYPE_TAG('g','d','m', 0 ), // gdm = Laal
+ TRUETYPE_TAG('g','d','n', 0 ), // gdn = Umanakaina
+ TRUETYPE_TAG('g','d','o', 0 ), // gdo = Ghodoberi
+ TRUETYPE_TAG('g','d','q', 0 ), // gdq = Mehri
+ TRUETYPE_TAG('g','d','r', 0 ), // gdr = Wipi
+ TRUETYPE_TAG('g','d','u', 0 ), // gdu = Gudu
+ TRUETYPE_TAG('g','d','x', 0 ), // gdx = Godwari
+ TRUETYPE_TAG('g','e','a', 0 ), // gea = Geruma
+ TRUETYPE_TAG('g','e','b', 0 ), // geb = Kire
+ TRUETYPE_TAG('g','e','c', 0 ), // gec = Gboloo Grebo
+ TRUETYPE_TAG('g','e','d', 0 ), // ged = Gade
+ TRUETYPE_TAG('g','e','g', 0 ), // geg = Gengle
+ TRUETYPE_TAG('g','e','h', 0 ), // geh = Hutterite German
+ TRUETYPE_TAG('g','e','i', 0 ), // gei = Gebe
+ TRUETYPE_TAG('g','e','j', 0 ), // gej = Gen
+ TRUETYPE_TAG('g','e','k', 0 ), // gek = Yiwom
+ TRUETYPE_TAG('g','e','l', 0 ), // gel = ut-Ma'in
+ TRUETYPE_TAG('g','e','m', 0 ), // gem = Germanic languages
+ TRUETYPE_TAG('g','e','q', 0 ), // geq = Geme
+ TRUETYPE_TAG('g','e','s', 0 ), // ges = Geser-Gorom
+ TRUETYPE_TAG('g','e','w', 0 ), // gew = Gera
+ TRUETYPE_TAG('g','e','x', 0 ), // gex = Garre
+ TRUETYPE_TAG('g','e','y', 0 ), // gey = Enya
+ TRUETYPE_TAG('g','e','z', 0 ), // gez = Geez
+ TRUETYPE_TAG('g','f','k', 0 ), // gfk = Patpatar
+ TRUETYPE_TAG('g','f','t', 0 ), // gft = Gafat
+ TRUETYPE_TAG('g','g','a', 0 ), // gga = Gao
+ TRUETYPE_TAG('g','g','b', 0 ), // ggb = Gbii
+ TRUETYPE_TAG('g','g','d', 0 ), // ggd = Gugadj
+ TRUETYPE_TAG('g','g','e', 0 ), // gge = Guragone
+ TRUETYPE_TAG('g','g','g', 0 ), // ggg = Gurgula
+ TRUETYPE_TAG('g','g','k', 0 ), // ggk = Kungarakany
+ TRUETYPE_TAG('g','g','l', 0 ), // ggl = Ganglau
+ TRUETYPE_TAG('g','g','n', 0 ), // ggn = Eastern Gurung
+ TRUETYPE_TAG('g','g','o', 0 ), // ggo = Southern Gondi
+ TRUETYPE_TAG('g','g','r', 0 ), // ggr = Aghu Tharnggalu
+ TRUETYPE_TAG('g','g','t', 0 ), // ggt = Gitua
+ TRUETYPE_TAG('g','g','u', 0 ), // ggu = Gagu
+ TRUETYPE_TAG('g','g','w', 0 ), // ggw = Gogodala
+ TRUETYPE_TAG('g','h','a', 0 ), // gha = Ghadamès
+ TRUETYPE_TAG('g','h','c', 0 ), // ghc = Hiberno-Scottish Gaelic
+ TRUETYPE_TAG('g','h','e', 0 ), // ghe = Southern Ghale
+ TRUETYPE_TAG('g','h','h', 0 ), // ghh = Northern Ghale
+ TRUETYPE_TAG('g','h','k', 0 ), // ghk = Geko Karen
+ TRUETYPE_TAG('g','h','l', 0 ), // ghl = Ghulfan
+ TRUETYPE_TAG('g','h','n', 0 ), // ghn = Ghanongga
+ TRUETYPE_TAG('g','h','o', 0 ), // gho = Ghomara
+ TRUETYPE_TAG('g','h','r', 0 ), // ghr = Ghera
+ TRUETYPE_TAG('g','h','s', 0 ), // ghs = Guhu-Samane
+ TRUETYPE_TAG('g','h','t', 0 ), // ght = Kutang Ghale
+ TRUETYPE_TAG('g','i','a', 0 ), // gia = Kitja
+ TRUETYPE_TAG('g','i','b', 0 ), // gib = Gibanawa
+ TRUETYPE_TAG('g','i','c', 0 ), // gic = Gail
+ TRUETYPE_TAG('g','i','d', 0 ), // gid = Gidar
+ TRUETYPE_TAG('g','i','g', 0 ), // gig = Goaria
+ TRUETYPE_TAG('g','i','l', 0 ), // gil = Gilbertese
+ TRUETYPE_TAG('g','i','m', 0 ), // gim = Gimi (Eastern Highlands)
+ TRUETYPE_TAG('g','i','n', 0 ), // gin = Hinukh
+ TRUETYPE_TAG('g','i','o', 0 ), // gio = Gelao
+ TRUETYPE_TAG('g','i','p', 0 ), // gip = Gimi (West New Britain)
+ TRUETYPE_TAG('g','i','q', 0 ), // giq = Green Gelao
+ TRUETYPE_TAG('g','i','r', 0 ), // gir = Red Gelao
+ TRUETYPE_TAG('g','i','s', 0 ), // gis = North Giziga
+ TRUETYPE_TAG('g','i','t', 0 ), // git = Gitxsan
+ TRUETYPE_TAG('g','i','w', 0 ), // giw = White Gelao
+ TRUETYPE_TAG('g','i','x', 0 ), // gix = Gilima
+ TRUETYPE_TAG('g','i','y', 0 ), // giy = Giyug
+ TRUETYPE_TAG('g','i','z', 0 ), // giz = South Giziga
+ TRUETYPE_TAG('g','j','i', 0 ), // gji = Geji
+ TRUETYPE_TAG('g','j','k', 0 ), // gjk = Kachi Koli
+ TRUETYPE_TAG('g','j','n', 0 ), // gjn = Gonja
+ TRUETYPE_TAG('g','j','u', 0 ), // gju = Gujari
+ TRUETYPE_TAG('g','k','a', 0 ), // gka = Guya
+ TRUETYPE_TAG('g','k','e', 0 ), // gke = Ndai
+ TRUETYPE_TAG('g','k','n', 0 ), // gkn = Gokana
+ TRUETYPE_TAG('g','k','p', 0 ), // gkp = Guinea Kpelle
+ TRUETYPE_TAG('g','l','c', 0 ), // glc = Bon Gula
+ TRUETYPE_TAG('g','l','d', 0 ), // gld = Nanai
+ TRUETYPE_TAG('g','l','h', 0 ), // glh = Northwest Pashayi
+ TRUETYPE_TAG('g','l','i', 0 ), // gli = Guliguli
+ TRUETYPE_TAG('g','l','j', 0 ), // glj = Gula Iro
+ TRUETYPE_TAG('g','l','k', 0 ), // glk = Gilaki
+ TRUETYPE_TAG('g','l','o', 0 ), // glo = Galambu
+ TRUETYPE_TAG('g','l','r', 0 ), // glr = Glaro-Twabo
+ TRUETYPE_TAG('g','l','u', 0 ), // glu = Gula (Chad)
+ TRUETYPE_TAG('g','l','w', 0 ), // glw = Glavda
+ TRUETYPE_TAG('g','l','y', 0 ), // gly = Gule
+ TRUETYPE_TAG('g','m','a', 0 ), // gma = Gambera
+ TRUETYPE_TAG('g','m','b', 0 ), // gmb = Gula'alaa
+ TRUETYPE_TAG('g','m','d', 0 ), // gmd = Mághdì
+ TRUETYPE_TAG('g','m','e', 0 ), // gme = East Germanic languages
+ TRUETYPE_TAG('g','m','h', 0 ), // gmh = Middle High German (ca. 1050-1500)
+ TRUETYPE_TAG('g','m','l', 0 ), // gml = Middle Low German
+ TRUETYPE_TAG('g','m','m', 0 ), // gmm = Gbaya-Mbodomo
+ TRUETYPE_TAG('g','m','n', 0 ), // gmn = Gimnime
+ TRUETYPE_TAG('g','m','q', 0 ), // gmq = North Germanic languages
+ TRUETYPE_TAG('g','m','u', 0 ), // gmu = Gumalu
+ TRUETYPE_TAG('g','m','v', 0 ), // gmv = Gamo
+ TRUETYPE_TAG('g','m','w', 0 ), // gmw = West Germanic languages
+ TRUETYPE_TAG('g','m','x', 0 ), // gmx = Magoma
+ TRUETYPE_TAG('g','m','y', 0 ), // gmy = Mycenaean Greek
+ TRUETYPE_TAG('g','n','a', 0 ), // gna = Kaansa
+ TRUETYPE_TAG('g','n','b', 0 ), // gnb = Gangte
+ TRUETYPE_TAG('g','n','c', 0 ), // gnc = Guanche
+ TRUETYPE_TAG('g','n','d', 0 ), // gnd = Zulgo-Gemzek
+ TRUETYPE_TAG('g','n','e', 0 ), // gne = Ganang
+ TRUETYPE_TAG('g','n','g', 0 ), // gng = Ngangam
+ TRUETYPE_TAG('g','n','h', 0 ), // gnh = Lere
+ TRUETYPE_TAG('g','n','i', 0 ), // gni = Gooniyandi
+ TRUETYPE_TAG('g','n','k', 0 ), // gnk = //Gana
+ TRUETYPE_TAG('g','n','l', 0 ), // gnl = Gangulu
+ TRUETYPE_TAG('g','n','m', 0 ), // gnm = Ginuman
+ TRUETYPE_TAG('g','n','n', 0 ), // gnn = Gumatj
+ TRUETYPE_TAG('g','n','o', 0 ), // gno = Northern Gondi
+ TRUETYPE_TAG('g','n','q', 0 ), // gnq = Gana
+ TRUETYPE_TAG('g','n','r', 0 ), // gnr = Gureng Gureng
+ TRUETYPE_TAG('g','n','t', 0 ), // gnt = Guntai
+ TRUETYPE_TAG('g','n','u', 0 ), // gnu = Gnau
+ TRUETYPE_TAG('g','n','w', 0 ), // gnw = Western Bolivian Guaraní
+ TRUETYPE_TAG('g','n','z', 0 ), // gnz = Ganzi
+ TRUETYPE_TAG('g','o','a', 0 ), // goa = Guro
+ TRUETYPE_TAG('g','o','b', 0 ), // gob = Playero
+ TRUETYPE_TAG('g','o','c', 0 ), // goc = Gorakor
+ TRUETYPE_TAG('g','o','d', 0 ), // god = Godié
+ TRUETYPE_TAG('g','o','e', 0 ), // goe = Gongduk
+ TRUETYPE_TAG('g','o','f', 0 ), // gof = Gofa
+ TRUETYPE_TAG('g','o','g', 0 ), // gog = Gogo
+ TRUETYPE_TAG('g','o','h', 0 ), // goh = Old High German (ca. 750-1050)
+ TRUETYPE_TAG('g','o','i', 0 ), // goi = Gobasi
+ TRUETYPE_TAG('g','o','j', 0 ), // goj = Gowlan
+ TRUETYPE_TAG('g','o','k', 0 ), // gok = Gowli
+ TRUETYPE_TAG('g','o','l', 0 ), // gol = Gola
+ TRUETYPE_TAG('g','o','m', 0 ), // gom = Goan Konkani
+ TRUETYPE_TAG('g','o','n', 0 ), // gon = Gondi
+ TRUETYPE_TAG('g','o','o', 0 ), // goo = Gone Dau
+ TRUETYPE_TAG('g','o','p', 0 ), // gop = Yeretuar
+ TRUETYPE_TAG('g','o','q', 0 ), // goq = Gorap
+ TRUETYPE_TAG('g','o','r', 0 ), // gor = Gorontalo
+ TRUETYPE_TAG('g','o','s', 0 ), // gos = Gronings
+ TRUETYPE_TAG('g','o','t', 0 ), // got = Gothic
+ TRUETYPE_TAG('g','o','u', 0 ), // gou = Gavar
+ TRUETYPE_TAG('g','o','w', 0 ), // gow = Gorowa
+ TRUETYPE_TAG('g','o','x', 0 ), // gox = Gobu
+ TRUETYPE_TAG('g','o','y', 0 ), // goy = Goundo
+ TRUETYPE_TAG('g','o','z', 0 ), // goz = Gozarkhani
+ TRUETYPE_TAG('g','p','a', 0 ), // gpa = Gupa-Abawa
+ TRUETYPE_TAG('g','p','n', 0 ), // gpn = Taiap
+ TRUETYPE_TAG('g','q','a', 0 ), // gqa = Ga'anda
+ TRUETYPE_TAG('g','q','i', 0 ), // gqi = Guiqiong
+ TRUETYPE_TAG('g','q','n', 0 ), // gqn = Guana (Brazil)
+ TRUETYPE_TAG('g','q','r', 0 ), // gqr = Gor
+ TRUETYPE_TAG('g','r','a', 0 ), // gra = Rajput Garasia
+ TRUETYPE_TAG('g','r','b', 0 ), // grb = Grebo
+ TRUETYPE_TAG('g','r','c', 0 ), // grc = Ancient Greek (to 1453)
+ TRUETYPE_TAG('g','r','d', 0 ), // grd = Guruntum-Mbaaru
+ TRUETYPE_TAG('g','r','g', 0 ), // grg = Madi
+ TRUETYPE_TAG('g','r','h', 0 ), // grh = Gbiri-Niragu
+ TRUETYPE_TAG('g','r','i', 0 ), // gri = Ghari
+ TRUETYPE_TAG('g','r','j', 0 ), // grj = Southern Grebo
+ TRUETYPE_TAG('g','r','k', 0 ), // grk = Greek languages
+ TRUETYPE_TAG('g','r','m', 0 ), // grm = Kota Marudu Talantang
+ TRUETYPE_TAG('g','r','o', 0 ), // gro = Groma
+ TRUETYPE_TAG('g','r','q', 0 ), // grq = Gorovu
+ TRUETYPE_TAG('g','r','r', 0 ), // grr = Taznatit
+ TRUETYPE_TAG('g','r','s', 0 ), // grs = Gresi
+ TRUETYPE_TAG('g','r','t', 0 ), // grt = Garo
+ TRUETYPE_TAG('g','r','u', 0 ), // gru = Kistane
+ TRUETYPE_TAG('g','r','v', 0 ), // grv = Central Grebo
+ TRUETYPE_TAG('g','r','w', 0 ), // grw = Gweda
+ TRUETYPE_TAG('g','r','x', 0 ), // grx = Guriaso
+ TRUETYPE_TAG('g','r','y', 0 ), // gry = Barclayville Grebo
+ TRUETYPE_TAG('g','r','z', 0 ), // grz = Guramalum
+ TRUETYPE_TAG('g','s','e', 0 ), // gse = Ghanaian Sign Language
+ TRUETYPE_TAG('g','s','g', 0 ), // gsg = German Sign Language
+ TRUETYPE_TAG('g','s','l', 0 ), // gsl = Gusilay
+ TRUETYPE_TAG('g','s','m', 0 ), // gsm = Guatemalan Sign Language
+ TRUETYPE_TAG('g','s','n', 0 ), // gsn = Gusan
+ TRUETYPE_TAG('g','s','o', 0 ), // gso = Southwest Gbaya
+ TRUETYPE_TAG('g','s','p', 0 ), // gsp = Wasembo
+ TRUETYPE_TAG('g','s','s', 0 ), // gss = Greek Sign Language
+ TRUETYPE_TAG('g','s','w', 0 ), // gsw = Swiss German
+ TRUETYPE_TAG('g','t','a', 0 ), // gta = Guató
+ TRUETYPE_TAG('g','t','i', 0 ), // gti = Gbati-ri
+ TRUETYPE_TAG('g','u','a', 0 ), // gua = Shiki
+ TRUETYPE_TAG('g','u','b', 0 ), // gub = Guajajára
+ TRUETYPE_TAG('g','u','c', 0 ), // guc = Wayuu
+ TRUETYPE_TAG('g','u','d', 0 ), // gud = Yocoboué Dida
+ TRUETYPE_TAG('g','u','e', 0 ), // gue = Gurinji
+ TRUETYPE_TAG('g','u','f', 0 ), // guf = Gupapuyngu
+ TRUETYPE_TAG('g','u','g', 0 ), // gug = Paraguayan Guaraní
+ TRUETYPE_TAG('g','u','h', 0 ), // guh = Guahibo
+ TRUETYPE_TAG('g','u','i', 0 ), // gui = Eastern Bolivian Guaraní
+ TRUETYPE_TAG('g','u','k', 0 ), // guk = Gumuz
+ TRUETYPE_TAG('g','u','l', 0 ), // gul = Sea Island Creole English
+ TRUETYPE_TAG('g','u','m', 0 ), // gum = Guambiano
+ TRUETYPE_TAG('g','u','n', 0 ), // gun = Mbyá Guaraní
+ TRUETYPE_TAG('g','u','o', 0 ), // guo = Guayabero
+ TRUETYPE_TAG('g','u','p', 0 ), // gup = Gunwinggu
+ TRUETYPE_TAG('g','u','q', 0 ), // guq = Aché
+ TRUETYPE_TAG('g','u','r', 0 ), // gur = Farefare
+ TRUETYPE_TAG('g','u','s', 0 ), // gus = Guinean Sign Language
+ TRUETYPE_TAG('g','u','t', 0 ), // gut = Maléku Jaíka
+ TRUETYPE_TAG('g','u','u', 0 ), // guu = Yanomamö
+ TRUETYPE_TAG('g','u','v', 0 ), // guv = Gey
+ TRUETYPE_TAG('g','u','w', 0 ), // guw = Gun
+ TRUETYPE_TAG('g','u','x', 0 ), // gux = Gourmanchéma
+ TRUETYPE_TAG('g','u','z', 0 ), // guz = Gusii
+ TRUETYPE_TAG('g','v','a', 0 ), // gva = Guana (Paraguay)
+ TRUETYPE_TAG('g','v','c', 0 ), // gvc = Guanano
+ TRUETYPE_TAG('g','v','e', 0 ), // gve = Duwet
+ TRUETYPE_TAG('g','v','f', 0 ), // gvf = Golin
+ TRUETYPE_TAG('g','v','j', 0 ), // gvj = Guajá
+ TRUETYPE_TAG('g','v','l', 0 ), // gvl = Gulay
+ TRUETYPE_TAG('g','v','m', 0 ), // gvm = Gurmana
+ TRUETYPE_TAG('g','v','n', 0 ), // gvn = Kuku-Yalanji
+ TRUETYPE_TAG('g','v','o', 0 ), // gvo = Gavião Do Jiparaná
+ TRUETYPE_TAG('g','v','p', 0 ), // gvp = Pará Gavião
+ TRUETYPE_TAG('g','v','r', 0 ), // gvr = Western Gurung
+ TRUETYPE_TAG('g','v','s', 0 ), // gvs = Gumawana
+ TRUETYPE_TAG('g','v','y', 0 ), // gvy = Guyani
+ TRUETYPE_TAG('g','w','a', 0 ), // gwa = Mbato
+ TRUETYPE_TAG('g','w','b', 0 ), // gwb = Gwa
+ TRUETYPE_TAG('g','w','c', 0 ), // gwc = Kalami
+ TRUETYPE_TAG('g','w','d', 0 ), // gwd = Gawwada
+ TRUETYPE_TAG('g','w','e', 0 ), // gwe = Gweno
+ TRUETYPE_TAG('g','w','f', 0 ), // gwf = Gowro
+ TRUETYPE_TAG('g','w','g', 0 ), // gwg = Moo
+ TRUETYPE_TAG('g','w','i', 0 ), // gwi = Gwichʼin
+ TRUETYPE_TAG('g','w','j', 0 ), // gwj = /Gwi
+ TRUETYPE_TAG('g','w','n', 0 ), // gwn = Gwandara
+ TRUETYPE_TAG('g','w','r', 0 ), // gwr = Gwere
+ TRUETYPE_TAG('g','w','t', 0 ), // gwt = Gawar-Bati
+ TRUETYPE_TAG('g','w','u', 0 ), // gwu = Guwamu
+ TRUETYPE_TAG('g','w','w', 0 ), // gww = Kwini
+ TRUETYPE_TAG('g','w','x', 0 ), // gwx = Gua
+ TRUETYPE_TAG('g','x','x', 0 ), // gxx = Wè Southern
+ TRUETYPE_TAG('g','y','a', 0 ), // gya = Northwest Gbaya
+ TRUETYPE_TAG('g','y','b', 0 ), // gyb = Garus
+ TRUETYPE_TAG('g','y','d', 0 ), // gyd = Kayardild
+ TRUETYPE_TAG('g','y','e', 0 ), // gye = Gyem
+ TRUETYPE_TAG('g','y','f', 0 ), // gyf = Gungabula
+ TRUETYPE_TAG('g','y','g', 0 ), // gyg = Gbayi
+ TRUETYPE_TAG('g','y','i', 0 ), // gyi = Gyele
+ TRUETYPE_TAG('g','y','l', 0 ), // gyl = Gayil
+ TRUETYPE_TAG('g','y','m', 0 ), // gym = Ngäbere
+ TRUETYPE_TAG('g','y','n', 0 ), // gyn = Guyanese Creole English
+ TRUETYPE_TAG('g','y','r', 0 ), // gyr = Guarayu
+ TRUETYPE_TAG('g','y','y', 0 ), // gyy = Gunya
+ TRUETYPE_TAG('g','z','a', 0 ), // gza = Ganza
+ TRUETYPE_TAG('g','z','i', 0 ), // gzi = Gazi
+ TRUETYPE_TAG('g','z','n', 0 ), // gzn = Gane
+ TRUETYPE_TAG('h','a','a', 0 ), // haa = Han
+ TRUETYPE_TAG('h','a','b', 0 ), // hab = Hanoi Sign Language
+ TRUETYPE_TAG('h','a','c', 0 ), // hac = Gurani
+ TRUETYPE_TAG('h','a','d', 0 ), // had = Hatam
+ TRUETYPE_TAG('h','a','e', 0 ), // hae = Eastern Oromo
+ TRUETYPE_TAG('h','a','f', 0 ), // haf = Haiphong Sign Language
+ TRUETYPE_TAG('h','a','g', 0 ), // hag = Hanga
+ TRUETYPE_TAG('h','a','h', 0 ), // hah = Hahon
+ TRUETYPE_TAG('h','a','i', 0 ), // hai = Haida
+ TRUETYPE_TAG('h','a','j', 0 ), // haj = Hajong
+ TRUETYPE_TAG('h','a','k', 0 ), // hak = Hakka Chinese
+ TRUETYPE_TAG('h','a','l', 0 ), // hal = Halang
+ TRUETYPE_TAG('h','a','m', 0 ), // ham = Hewa
+ TRUETYPE_TAG('h','a','n', 0 ), // han = Hangaza
+ TRUETYPE_TAG('h','a','o', 0 ), // hao = Hakö
+ TRUETYPE_TAG('h','a','p', 0 ), // hap = Hupla
+ TRUETYPE_TAG('h','a','q', 0 ), // haq = Ha
+ TRUETYPE_TAG('h','a','r', 0 ), // har = Harari
+ TRUETYPE_TAG('h','a','s', 0 ), // has = Haisla
+ TRUETYPE_TAG('h','a','v', 0 ), // hav = Havu
+ TRUETYPE_TAG('h','a','w', 0 ), // haw = Hawaiian
+ TRUETYPE_TAG('h','a','x', 0 ), // hax = Southern Haida
+ TRUETYPE_TAG('h','a','y', 0 ), // hay = Haya
+ TRUETYPE_TAG('h','a','z', 0 ), // haz = Hazaragi
+ TRUETYPE_TAG('h','b','a', 0 ), // hba = Hamba
+ TRUETYPE_TAG('h','b','b', 0 ), // hbb = Huba
+ TRUETYPE_TAG('h','b','n', 0 ), // hbn = Heiban
+ TRUETYPE_TAG('h','b','o', 0 ), // hbo = Ancient Hebrew
+ TRUETYPE_TAG('h','b','u', 0 ), // hbu = Habu
+ TRUETYPE_TAG('h','c','a', 0 ), // hca = Andaman Creole Hindi
+ TRUETYPE_TAG('h','c','h', 0 ), // hch = Huichol
+ TRUETYPE_TAG('h','d','n', 0 ), // hdn = Northern Haida
+ TRUETYPE_TAG('h','d','s', 0 ), // hds = Honduras Sign Language
+ TRUETYPE_TAG('h','d','y', 0 ), // hdy = Hadiyya
+ TRUETYPE_TAG('h','e','a', 0 ), // hea = Northern Qiandong Miao
+ TRUETYPE_TAG('h','e','d', 0 ), // hed = Herdé
+ TRUETYPE_TAG('h','e','g', 0 ), // heg = Helong
+ TRUETYPE_TAG('h','e','h', 0 ), // heh = Hehe
+ TRUETYPE_TAG('h','e','i', 0 ), // hei = Heiltsuk
+ TRUETYPE_TAG('h','e','m', 0 ), // hem = Hemba
+ TRUETYPE_TAG('h','g','m', 0 ), // hgm = Hai//om
+ TRUETYPE_TAG('h','g','w', 0 ), // hgw = Haigwai
+ TRUETYPE_TAG('h','h','i', 0 ), // hhi = Hoia Hoia
+ TRUETYPE_TAG('h','h','r', 0 ), // hhr = Kerak
+ TRUETYPE_TAG('h','h','y', 0 ), // hhy = Hoyahoya
+ TRUETYPE_TAG('h','i','a', 0 ), // hia = Lamang
+ TRUETYPE_TAG('h','i','b', 0 ), // hib = Hibito
+ TRUETYPE_TAG('h','i','d', 0 ), // hid = Hidatsa
+ TRUETYPE_TAG('h','i','f', 0 ), // hif = Fiji Hindi
+ TRUETYPE_TAG('h','i','g', 0 ), // hig = Kamwe
+ TRUETYPE_TAG('h','i','h', 0 ), // hih = Pamosu
+ TRUETYPE_TAG('h','i','i', 0 ), // hii = Hinduri
+ TRUETYPE_TAG('h','i','j', 0 ), // hij = Hijuk
+ TRUETYPE_TAG('h','i','k', 0 ), // hik = Seit-Kaitetu
+ TRUETYPE_TAG('h','i','l', 0 ), // hil = Hiligaynon
+ TRUETYPE_TAG('h','i','m', 0 ), // him = Himachali languages
+ TRUETYPE_TAG('h','i','o', 0 ), // hio = Tsoa
+ TRUETYPE_TAG('h','i','r', 0 ), // hir = Himarimã
+ TRUETYPE_TAG('h','i','t', 0 ), // hit = Hittite
+ TRUETYPE_TAG('h','i','w', 0 ), // hiw = Hiw
+ TRUETYPE_TAG('h','i','x', 0 ), // hix = Hixkaryána
+ TRUETYPE_TAG('h','j','i', 0 ), // hji = Haji
+ TRUETYPE_TAG('h','k','a', 0 ), // hka = Kahe
+ TRUETYPE_TAG('h','k','e', 0 ), // hke = Hunde
+ TRUETYPE_TAG('h','k','k', 0 ), // hkk = Hunjara-Kaina Ke
+ TRUETYPE_TAG('h','k','s', 0 ), // hks = Hong Kong Sign Language
+ TRUETYPE_TAG('h','l','a', 0 ), // hla = Halia
+ TRUETYPE_TAG('h','l','b', 0 ), // hlb = Halbi
+ TRUETYPE_TAG('h','l','d', 0 ), // hld = Halang Doan
+ TRUETYPE_TAG('h','l','e', 0 ), // hle = Hlersu
+ TRUETYPE_TAG('h','l','t', 0 ), // hlt = Nga La
+ TRUETYPE_TAG('h','l','u', 0 ), // hlu = Hieroglyphic Luwian
+ TRUETYPE_TAG('h','m','a', 0 ), // hma = Southern Mashan Hmong
+ TRUETYPE_TAG('h','m','b', 0 ), // hmb = Humburi Senni Songhay
+ TRUETYPE_TAG('h','m','c', 0 ), // hmc = Central Huishui Hmong
+ TRUETYPE_TAG('h','m','d', 0 ), // hmd = Large Flowery Miao
+ TRUETYPE_TAG('h','m','e', 0 ), // hme = Eastern Huishui Hmong
+ TRUETYPE_TAG('h','m','f', 0 ), // hmf = Hmong Don
+ TRUETYPE_TAG('h','m','g', 0 ), // hmg = Southwestern Guiyang Hmong
+ TRUETYPE_TAG('h','m','h', 0 ), // hmh = Southwestern Huishui Hmong
+ TRUETYPE_TAG('h','m','i', 0 ), // hmi = Northern Huishui Hmong
+ TRUETYPE_TAG('h','m','j', 0 ), // hmj = Ge
+ TRUETYPE_TAG('h','m','k', 0 ), // hmk = Maek
+ TRUETYPE_TAG('h','m','l', 0 ), // hml = Luopohe Hmong
+ TRUETYPE_TAG('h','m','m', 0 ), // hmm = Central Mashan Hmong
+ TRUETYPE_TAG('h','m','n', 0 ), // hmn = Hmong
+ TRUETYPE_TAG('h','m','p', 0 ), // hmp = Northern Mashan Hmong
+ TRUETYPE_TAG('h','m','q', 0 ), // hmq = Eastern Qiandong Miao
+ TRUETYPE_TAG('h','m','r', 0 ), // hmr = Hmar
+ TRUETYPE_TAG('h','m','s', 0 ), // hms = Southern Qiandong Miao
+ TRUETYPE_TAG('h','m','t', 0 ), // hmt = Hamtai
+ TRUETYPE_TAG('h','m','u', 0 ), // hmu = Hamap
+ TRUETYPE_TAG('h','m','v', 0 ), // hmv = Hmong Dô
+ TRUETYPE_TAG('h','m','w', 0 ), // hmw = Western Mashan Hmong
+ TRUETYPE_TAG('h','m','x', 0 ), // hmx = Hmong-Mien languages
+ TRUETYPE_TAG('h','m','y', 0 ), // hmy = Southern Guiyang Hmong
+ TRUETYPE_TAG('h','m','z', 0 ), // hmz = Hmong Shua
+ TRUETYPE_TAG('h','n','a', 0 ), // hna = Mina (Cameroon)
+ TRUETYPE_TAG('h','n','d', 0 ), // hnd = Southern Hindko
+ TRUETYPE_TAG('h','n','e', 0 ), // hne = Chhattisgarhi
+ TRUETYPE_TAG('h','n','h', 0 ), // hnh = //Ani
+ TRUETYPE_TAG('h','n','i', 0 ), // hni = Hani
+ TRUETYPE_TAG('h','n','j', 0 ), // hnj = Hmong Njua
+ TRUETYPE_TAG('h','n','n', 0 ), // hnn = Hanunoo
+ TRUETYPE_TAG('h','n','o', 0 ), // hno = Northern Hindko
+ TRUETYPE_TAG('h','n','s', 0 ), // hns = Caribbean Hindustani
+ TRUETYPE_TAG('h','n','u', 0 ), // hnu = Hung
+ TRUETYPE_TAG('h','o','a', 0 ), // hoa = Hoava
+ TRUETYPE_TAG('h','o','b', 0 ), // hob = Mari (Madang Province)
+ TRUETYPE_TAG('h','o','c', 0 ), // hoc = Ho
+ TRUETYPE_TAG('h','o','d', 0 ), // hod = Holma
+ TRUETYPE_TAG('h','o','e', 0 ), // hoe = Horom
+ TRUETYPE_TAG('h','o','h', 0 ), // hoh = Hobyót
+ TRUETYPE_TAG('h','o','i', 0 ), // hoi = Holikachuk
+ TRUETYPE_TAG('h','o','j', 0 ), // hoj = Hadothi
+ TRUETYPE_TAG('h','o','k', 0 ), // hok = Hokan languages
+ TRUETYPE_TAG('h','o','l', 0 ), // hol = Holu
+ TRUETYPE_TAG('h','o','m', 0 ), // hom = Homa
+ TRUETYPE_TAG('h','o','o', 0 ), // hoo = Holoholo
+ TRUETYPE_TAG('h','o','p', 0 ), // hop = Hopi
+ TRUETYPE_TAG('h','o','r', 0 ), // hor = Horo
+ TRUETYPE_TAG('h','o','s', 0 ), // hos = Ho Chi Minh City Sign Language
+ TRUETYPE_TAG('h','o','t', 0 ), // hot = Hote
+ TRUETYPE_TAG('h','o','v', 0 ), // hov = Hovongan
+ TRUETYPE_TAG('h','o','w', 0 ), // how = Honi
+ TRUETYPE_TAG('h','o','y', 0 ), // hoy = Holiya
+ TRUETYPE_TAG('h','o','z', 0 ), // hoz = Hozo
+ TRUETYPE_TAG('h','p','o', 0 ), // hpo = Hpon
+ TRUETYPE_TAG('h','p','s', 0 ), // hps = Hawai'i Pidgin Sign Language
+ TRUETYPE_TAG('h','r','a', 0 ), // hra = Hrangkhol
+ TRUETYPE_TAG('h','r','e', 0 ), // hre = Hre
+ TRUETYPE_TAG('h','r','k', 0 ), // hrk = Haruku
+ TRUETYPE_TAG('h','r','m', 0 ), // hrm = Horned Miao
+ TRUETYPE_TAG('h','r','o', 0 ), // hro = Haroi
+ TRUETYPE_TAG('h','r','r', 0 ), // hrr = Horuru
+ TRUETYPE_TAG('h','r','t', 0 ), // hrt = Hértevin
+ TRUETYPE_TAG('h','r','u', 0 ), // hru = Hruso
+ TRUETYPE_TAG('h','r','x', 0 ), // hrx = Hunsrik
+ TRUETYPE_TAG('h','r','z', 0 ), // hrz = Harzani
+ TRUETYPE_TAG('h','s','b', 0 ), // hsb = Upper Sorbian
+ TRUETYPE_TAG('h','s','h', 0 ), // hsh = Hungarian Sign Language
+ TRUETYPE_TAG('h','s','l', 0 ), // hsl = Hausa Sign Language
+ TRUETYPE_TAG('h','s','n', 0 ), // hsn = Xiang Chinese
+ TRUETYPE_TAG('h','s','s', 0 ), // hss = Harsusi
+ TRUETYPE_TAG('h','t','i', 0 ), // hti = Hoti
+ TRUETYPE_TAG('h','t','o', 0 ), // hto = Minica Huitoto
+ TRUETYPE_TAG('h','t','s', 0 ), // hts = Hadza
+ TRUETYPE_TAG('h','t','u', 0 ), // htu = Hitu
+ TRUETYPE_TAG('h','t','x', 0 ), // htx = Middle Hittite
+ TRUETYPE_TAG('h','u','b', 0 ), // hub = Huambisa
+ TRUETYPE_TAG('h','u','c', 0 ), // huc = =/Hua
+ TRUETYPE_TAG('h','u','d', 0 ), // hud = Huaulu
+ TRUETYPE_TAG('h','u','e', 0 ), // hue = San Francisco Del Mar Huave
+ TRUETYPE_TAG('h','u','f', 0 ), // huf = Humene
+ TRUETYPE_TAG('h','u','g', 0 ), // hug = Huachipaeri
+ TRUETYPE_TAG('h','u','h', 0 ), // huh = Huilliche
+ TRUETYPE_TAG('h','u','i', 0 ), // hui = Huli
+ TRUETYPE_TAG('h','u','j', 0 ), // huj = Northern Guiyang Hmong
+ TRUETYPE_TAG('h','u','k', 0 ), // huk = Hulung
+ TRUETYPE_TAG('h','u','l', 0 ), // hul = Hula
+ TRUETYPE_TAG('h','u','m', 0 ), // hum = Hungana
+ TRUETYPE_TAG('h','u','o', 0 ), // huo = Hu
+ TRUETYPE_TAG('h','u','p', 0 ), // hup = Hupa
+ TRUETYPE_TAG('h','u','q', 0 ), // huq = Tsat
+ TRUETYPE_TAG('h','u','r', 0 ), // hur = Halkomelem
+ TRUETYPE_TAG('h','u','s', 0 ), // hus = Huastec
+ TRUETYPE_TAG('h','u','t', 0 ), // hut = Humla
+ TRUETYPE_TAG('h','u','u', 0 ), // huu = Murui Huitoto
+ TRUETYPE_TAG('h','u','v', 0 ), // huv = San Mateo Del Mar Huave
+ TRUETYPE_TAG('h','u','w', 0 ), // huw = Hukumina
+ TRUETYPE_TAG('h','u','x', 0 ), // hux = Nüpode Huitoto
+ TRUETYPE_TAG('h','u','y', 0 ), // huy = Hulaulá
+ TRUETYPE_TAG('h','u','z', 0 ), // huz = Hunzib
+ TRUETYPE_TAG('h','v','c', 0 ), // hvc = Haitian Vodoun Culture Language
+ TRUETYPE_TAG('h','v','e', 0 ), // hve = San Dionisio Del Mar Huave
+ TRUETYPE_TAG('h','v','k', 0 ), // hvk = Haveke
+ TRUETYPE_TAG('h','v','n', 0 ), // hvn = Sabu
+ TRUETYPE_TAG('h','v','v', 0 ), // hvv = Santa María Del Mar Huave
+ TRUETYPE_TAG('h','w','a', 0 ), // hwa = Wané
+ TRUETYPE_TAG('h','w','c', 0 ), // hwc = Hawai'i Creole English
+ TRUETYPE_TAG('h','w','o', 0 ), // hwo = Hwana
+ TRUETYPE_TAG('h','y','a', 0 ), // hya = Hya
+ TRUETYPE_TAG('h','y','x', 0 ), // hyx = Armenian (family)
+ TRUETYPE_TAG('i','a','i', 0 ), // iai = Iaai
+ TRUETYPE_TAG('i','a','n', 0 ), // ian = Iatmul
+ TRUETYPE_TAG('i','a','p', 0 ), // iap = Iapama
+ TRUETYPE_TAG('i','a','r', 0 ), // iar = Purari
+ TRUETYPE_TAG('i','b','a', 0 ), // iba = Iban
+ TRUETYPE_TAG('i','b','b', 0 ), // ibb = Ibibio
+ TRUETYPE_TAG('i','b','d', 0 ), // ibd = Iwaidja
+ TRUETYPE_TAG('i','b','e', 0 ), // ibe = Akpes
+ TRUETYPE_TAG('i','b','g', 0 ), // ibg = Ibanag
+ TRUETYPE_TAG('i','b','i', 0 ), // ibi = Ibilo
+ TRUETYPE_TAG('i','b','l', 0 ), // ibl = Ibaloi
+ TRUETYPE_TAG('i','b','m', 0 ), // ibm = Agoi
+ TRUETYPE_TAG('i','b','n', 0 ), // ibn = Ibino
+ TRUETYPE_TAG('i','b','r', 0 ), // ibr = Ibuoro
+ TRUETYPE_TAG('i','b','u', 0 ), // ibu = Ibu
+ TRUETYPE_TAG('i','b','y', 0 ), // iby = Ibani
+ TRUETYPE_TAG('i','c','a', 0 ), // ica = Ede Ica
+ TRUETYPE_TAG('i','c','h', 0 ), // ich = Etkywan
+ TRUETYPE_TAG('i','c','l', 0 ), // icl = Icelandic Sign Language
+ TRUETYPE_TAG('i','c','r', 0 ), // icr = Islander Creole English
+ TRUETYPE_TAG('i','d','a', 0 ), // ida = Idakho-Isukha-Tiriki
+ TRUETYPE_TAG('i','d','b', 0 ), // idb = Indo-Portuguese
+ TRUETYPE_TAG('i','d','c', 0 ), // idc = Idon
+ TRUETYPE_TAG('i','d','d', 0 ), // idd = Ede Idaca
+ TRUETYPE_TAG('i','d','e', 0 ), // ide = Idere
+ TRUETYPE_TAG('i','d','i', 0 ), // idi = Idi
+ TRUETYPE_TAG('i','d','r', 0 ), // idr = Indri
+ TRUETYPE_TAG('i','d','s', 0 ), // ids = Idesa
+ TRUETYPE_TAG('i','d','t', 0 ), // idt = Idaté
+ TRUETYPE_TAG('i','d','u', 0 ), // idu = Idoma
+ TRUETYPE_TAG('i','f','a', 0 ), // ifa = Amganad Ifugao
+ TRUETYPE_TAG('i','f','b', 0 ), // ifb = Batad Ifugao
+ TRUETYPE_TAG('i','f','e', 0 ), // ife = Ifè
+ TRUETYPE_TAG('i','f','f', 0 ), // iff = Ifo
+ TRUETYPE_TAG('i','f','k', 0 ), // ifk = Tuwali Ifugao
+ TRUETYPE_TAG('i','f','m', 0 ), // ifm = Teke-Fuumu
+ TRUETYPE_TAG('i','f','u', 0 ), // ifu = Mayoyao Ifugao
+ TRUETYPE_TAG('i','f','y', 0 ), // ify = Keley-I Kallahan
+ TRUETYPE_TAG('i','g','b', 0 ), // igb = Ebira
+ TRUETYPE_TAG('i','g','e', 0 ), // ige = Igede
+ TRUETYPE_TAG('i','g','g', 0 ), // igg = Igana
+ TRUETYPE_TAG('i','g','l', 0 ), // igl = Igala
+ TRUETYPE_TAG('i','g','m', 0 ), // igm = Kanggape
+ TRUETYPE_TAG('i','g','n', 0 ), // ign = Ignaciano
+ TRUETYPE_TAG('i','g','o', 0 ), // igo = Isebe
+ TRUETYPE_TAG('i','g','s', 0 ), // igs = Interglossa
+ TRUETYPE_TAG('i','g','w', 0 ), // igw = Igwe
+ TRUETYPE_TAG('i','h','b', 0 ), // ihb = Iha Based Pidgin
+ TRUETYPE_TAG('i','h','i', 0 ), // ihi = Ihievbe
+ TRUETYPE_TAG('i','h','p', 0 ), // ihp = Iha
+ TRUETYPE_TAG('i','i','r', 0 ), // iir = Indo-Iranian languages
+ TRUETYPE_TAG('i','j','c', 0 ), // ijc = Izon
+ TRUETYPE_TAG('i','j','e', 0 ), // ije = Biseni
+ TRUETYPE_TAG('i','j','j', 0 ), // ijj = Ede Ije
+ TRUETYPE_TAG('i','j','n', 0 ), // ijn = Kalabari
+ TRUETYPE_TAG('i','j','o', 0 ), // ijo = Ijo languages
+ TRUETYPE_TAG('i','j','s', 0 ), // ijs = Southeast Ijo
+ TRUETYPE_TAG('i','k','e', 0 ), // ike = Eastern Canadian Inuktitut
+ TRUETYPE_TAG('i','k','i', 0 ), // iki = Iko
+ TRUETYPE_TAG('i','k','k', 0 ), // ikk = Ika
+ TRUETYPE_TAG('i','k','l', 0 ), // ikl = Ikulu
+ TRUETYPE_TAG('i','k','o', 0 ), // iko = Olulumo-Ikom
+ TRUETYPE_TAG('i','k','p', 0 ), // ikp = Ikpeshi
+ TRUETYPE_TAG('i','k','t', 0 ), // ikt = Western Canadian Inuktitut
+ TRUETYPE_TAG('i','k','v', 0 ), // ikv = Iku-Gora-Ankwa
+ TRUETYPE_TAG('i','k','w', 0 ), // ikw = Ikwere
+ TRUETYPE_TAG('i','k','x', 0 ), // ikx = Ik
+ TRUETYPE_TAG('i','k','z', 0 ), // ikz = Ikizu
+ TRUETYPE_TAG('i','l','a', 0 ), // ila = Ile Ape
+ TRUETYPE_TAG('i','l','b', 0 ), // ilb = Ila
+ TRUETYPE_TAG('i','l','g', 0 ), // ilg = Garig-Ilgar
+ TRUETYPE_TAG('i','l','i', 0 ), // ili = Ili Turki
+ TRUETYPE_TAG('i','l','k', 0 ), // ilk = Ilongot
+ TRUETYPE_TAG('i','l','l', 0 ), // ill = Iranun
+ TRUETYPE_TAG('i','l','o', 0 ), // ilo = Iloko
+ TRUETYPE_TAG('i','l','s', 0 ), // ils = International Sign
+ TRUETYPE_TAG('i','l','u', 0 ), // ilu = Ili'uun
+ TRUETYPE_TAG('i','l','v', 0 ), // ilv = Ilue
+ TRUETYPE_TAG('i','l','w', 0 ), // ilw = Talur
+ TRUETYPE_TAG('i','m','a', 0 ), // ima = Mala Malasar
+ TRUETYPE_TAG('i','m','e', 0 ), // ime = Imeraguen
+ TRUETYPE_TAG('i','m','i', 0 ), // imi = Anamgura
+ TRUETYPE_TAG('i','m','l', 0 ), // iml = Miluk
+ TRUETYPE_TAG('i','m','n', 0 ), // imn = Imonda
+ TRUETYPE_TAG('i','m','o', 0 ), // imo = Imbongu
+ TRUETYPE_TAG('i','m','r', 0 ), // imr = Imroing
+ TRUETYPE_TAG('i','m','s', 0 ), // ims = Marsian
+ TRUETYPE_TAG('i','m','y', 0 ), // imy = Milyan
+ TRUETYPE_TAG('i','n','b', 0 ), // inb = Inga
+ TRUETYPE_TAG('i','n','c', 0 ), // inc = Indic languages
+ TRUETYPE_TAG('i','n','e', 0 ), // ine = Indo-European languages
+ TRUETYPE_TAG('i','n','g', 0 ), // ing = Degexit'an
+ TRUETYPE_TAG('i','n','h', 0 ), // inh = Ingush
+ TRUETYPE_TAG('i','n','j', 0 ), // inj = Jungle Inga
+ TRUETYPE_TAG('i','n','l', 0 ), // inl = Indonesian Sign Language
+ TRUETYPE_TAG('i','n','m', 0 ), // inm = Minaean
+ TRUETYPE_TAG('i','n','n', 0 ), // inn = Isinai
+ TRUETYPE_TAG('i','n','o', 0 ), // ino = Inoke-Yate
+ TRUETYPE_TAG('i','n','p', 0 ), // inp = Iñapari
+ TRUETYPE_TAG('i','n','s', 0 ), // ins = Indian Sign Language
+ TRUETYPE_TAG('i','n','t', 0 ), // int = Intha
+ TRUETYPE_TAG('i','n','z', 0 ), // inz = Ineseño
+ TRUETYPE_TAG('i','o','r', 0 ), // ior = Inor
+ TRUETYPE_TAG('i','o','u', 0 ), // iou = Tuma-Irumu
+ TRUETYPE_TAG('i','o','w', 0 ), // iow = Iowa-Oto
+ TRUETYPE_TAG('i','p','i', 0 ), // ipi = Ipili
+ TRUETYPE_TAG('i','p','o', 0 ), // ipo = Ipiko
+ TRUETYPE_TAG('i','q','u', 0 ), // iqu = Iquito
+ TRUETYPE_TAG('i','r','a', 0 ), // ira = Iranian languages
+ TRUETYPE_TAG('i','r','e', 0 ), // ire = Iresim
+ TRUETYPE_TAG('i','r','h', 0 ), // irh = Irarutu
+ TRUETYPE_TAG('i','r','i', 0 ), // iri = Irigwe
+ TRUETYPE_TAG('i','r','k', 0 ), // irk = Iraqw
+ TRUETYPE_TAG('i','r','n', 0 ), // irn = Irántxe
+ TRUETYPE_TAG('i','r','o', 0 ), // iro = Iroquoian languages
+ TRUETYPE_TAG('i','r','r', 0 ), // irr = Ir
+ TRUETYPE_TAG('i','r','u', 0 ), // iru = Irula
+ TRUETYPE_TAG('i','r','x', 0 ), // irx = Kamberau
+ TRUETYPE_TAG('i','r','y', 0 ), // iry = Iraya
+ TRUETYPE_TAG('i','s','a', 0 ), // isa = Isabi
+ TRUETYPE_TAG('i','s','c', 0 ), // isc = Isconahua
+ TRUETYPE_TAG('i','s','d', 0 ), // isd = Isnag
+ TRUETYPE_TAG('i','s','e', 0 ), // ise = Italian Sign Language
+ TRUETYPE_TAG('i','s','g', 0 ), // isg = Irish Sign Language
+ TRUETYPE_TAG('i','s','h', 0 ), // ish = Esan
+ TRUETYPE_TAG('i','s','i', 0 ), // isi = Nkem-Nkum
+ TRUETYPE_TAG('i','s','k', 0 ), // isk = Ishkashimi
+ TRUETYPE_TAG('i','s','m', 0 ), // ism = Masimasi
+ TRUETYPE_TAG('i','s','n', 0 ), // isn = Isanzu
+ TRUETYPE_TAG('i','s','o', 0 ), // iso = Isoko
+ TRUETYPE_TAG('i','s','r', 0 ), // isr = Israeli Sign Language
+ TRUETYPE_TAG('i','s','t', 0 ), // ist = Istriot
+ TRUETYPE_TAG('i','s','u', 0 ), // isu = Isu (Menchum Division)
+ TRUETYPE_TAG('i','t','b', 0 ), // itb = Binongan Itneg
+ TRUETYPE_TAG('i','t','c', 0 ), // itc = Italic languages
+ TRUETYPE_TAG('i','t','e', 0 ), // ite = Itene
+ TRUETYPE_TAG('i','t','i', 0 ), // iti = Inlaod Itneg
+ TRUETYPE_TAG('i','t','k', 0 ), // itk = Judeo-Italian
+ TRUETYPE_TAG('i','t','l', 0 ), // itl = Itelmen
+ TRUETYPE_TAG('i','t','m', 0 ), // itm = Itu Mbon Uzo
+ TRUETYPE_TAG('i','t','o', 0 ), // ito = Itonama
+ TRUETYPE_TAG('i','t','r', 0 ), // itr = Iteri
+ TRUETYPE_TAG('i','t','s', 0 ), // its = Isekiri
+ TRUETYPE_TAG('i','t','t', 0 ), // itt = Maeng Itneg
+ TRUETYPE_TAG('i','t','v', 0 ), // itv = Itawit
+ TRUETYPE_TAG('i','t','w', 0 ), // itw = Ito
+ TRUETYPE_TAG('i','t','x', 0 ), // itx = Itik
+ TRUETYPE_TAG('i','t','y', 0 ), // ity = Moyadan Itneg
+ TRUETYPE_TAG('i','t','z', 0 ), // itz = Itzá
+ TRUETYPE_TAG('i','u','m', 0 ), // ium = Iu Mien
+ TRUETYPE_TAG('i','v','b', 0 ), // ivb = Ibatan
+ TRUETYPE_TAG('i','v','v', 0 ), // ivv = Ivatan
+ TRUETYPE_TAG('i','w','k', 0 ), // iwk = I-Wak
+ TRUETYPE_TAG('i','w','m', 0 ), // iwm = Iwam
+ TRUETYPE_TAG('i','w','o', 0 ), // iwo = Iwur
+ TRUETYPE_TAG('i','w','s', 0 ), // iws = Sepik Iwam
+ TRUETYPE_TAG('i','x','c', 0 ), // ixc = Ixcatec
+ TRUETYPE_TAG('i','x','l', 0 ), // ixl = Ixil
+ TRUETYPE_TAG('i','y','a', 0 ), // iya = Iyayu
+ TRUETYPE_TAG('i','y','o', 0 ), // iyo = Mesaka
+ TRUETYPE_TAG('i','y','x', 0 ), // iyx = Yaka (Congo)
+ TRUETYPE_TAG('i','z','h', 0 ), // izh = Ingrian
+ TRUETYPE_TAG('i','z','i', 0 ), // izi = Izi-Ezaa-Ikwo-Mgbo
+ TRUETYPE_TAG('i','z','r', 0 ), // izr = Izere
+ TRUETYPE_TAG('j','a','a', 0 ), // jaa = Jamamadí
+ TRUETYPE_TAG('j','a','b', 0 ), // jab = Hyam
+ TRUETYPE_TAG('j','a','c', 0 ), // jac = Popti'
+ TRUETYPE_TAG('j','a','d', 0 ), // jad = Jahanka
+ TRUETYPE_TAG('j','a','e', 0 ), // jae = Yabem
+ TRUETYPE_TAG('j','a','f', 0 ), // jaf = Jara
+ TRUETYPE_TAG('j','a','h', 0 ), // jah = Jah Hut
+ TRUETYPE_TAG('j','a','j', 0 ), // jaj = Zazao
+ TRUETYPE_TAG('j','a','k', 0 ), // jak = Jakun
+ TRUETYPE_TAG('j','a','l', 0 ), // jal = Yalahatan
+ TRUETYPE_TAG('j','a','m', 0 ), // jam = Jamaican Creole English
+ TRUETYPE_TAG('j','a','o', 0 ), // jao = Yanyuwa
+ TRUETYPE_TAG('j','a','q', 0 ), // jaq = Yaqay
+ TRUETYPE_TAG('j','a','r', 0 ), // jar = Jarawa (Nigeria)
+ TRUETYPE_TAG('j','a','s', 0 ), // jas = New Caledonian Javanese
+ TRUETYPE_TAG('j','a','t', 0 ), // jat = Jakati
+ TRUETYPE_TAG('j','a','u', 0 ), // jau = Yaur
+ TRUETYPE_TAG('j','a','x', 0 ), // jax = Jambi Malay
+ TRUETYPE_TAG('j','a','y', 0 ), // jay = Yan-nhangu
+ TRUETYPE_TAG('j','a','z', 0 ), // jaz = Jawe
+ TRUETYPE_TAG('j','b','e', 0 ), // jbe = Judeo-Berber
+ TRUETYPE_TAG('j','b','j', 0 ), // jbj = Arandai
+ TRUETYPE_TAG('j','b','n', 0 ), // jbn = Nafusi
+ TRUETYPE_TAG('j','b','o', 0 ), // jbo = Lojban
+ TRUETYPE_TAG('j','b','r', 0 ), // jbr = Jofotek-Bromnya
+ TRUETYPE_TAG('j','b','t', 0 ), // jbt = Jabutí
+ TRUETYPE_TAG('j','b','u', 0 ), // jbu = Jukun Takum
+ TRUETYPE_TAG('j','c','s', 0 ), // jcs = Jamaican Country Sign Language
+ TRUETYPE_TAG('j','c','t', 0 ), // jct = Krymchak
+ TRUETYPE_TAG('j','d','a', 0 ), // jda = Jad
+ TRUETYPE_TAG('j','d','g', 0 ), // jdg = Jadgali
+ TRUETYPE_TAG('j','d','t', 0 ), // jdt = Judeo-Tat
+ TRUETYPE_TAG('j','e','b', 0 ), // jeb = Jebero
+ TRUETYPE_TAG('j','e','e', 0 ), // jee = Jerung
+ TRUETYPE_TAG('j','e','g', 0 ), // jeg = Jeng
+ TRUETYPE_TAG('j','e','h', 0 ), // jeh = Jeh
+ TRUETYPE_TAG('j','e','i', 0 ), // jei = Yei
+ TRUETYPE_TAG('j','e','k', 0 ), // jek = Jeri Kuo
+ TRUETYPE_TAG('j','e','l', 0 ), // jel = Yelmek
+ TRUETYPE_TAG('j','e','n', 0 ), // jen = Dza
+ TRUETYPE_TAG('j','e','r', 0 ), // jer = Jere
+ TRUETYPE_TAG('j','e','t', 0 ), // jet = Manem
+ TRUETYPE_TAG('j','e','u', 0 ), // jeu = Jonkor Bourmataguil
+ TRUETYPE_TAG('j','g','b', 0 ), // jgb = Ngbee
+ TRUETYPE_TAG('j','g','e', 0 ), // jge = Judeo-Georgian
+ TRUETYPE_TAG('j','g','o', 0 ), // jgo = Ngomba
+ TRUETYPE_TAG('j','h','i', 0 ), // jhi = Jehai
+ TRUETYPE_TAG('j','h','s', 0 ), // jhs = Jhankot Sign Language
+ TRUETYPE_TAG('j','i','a', 0 ), // jia = Jina
+ TRUETYPE_TAG('j','i','b', 0 ), // jib = Jibu
+ TRUETYPE_TAG('j','i','c', 0 ), // jic = Tol
+ TRUETYPE_TAG('j','i','d', 0 ), // jid = Bu
+ TRUETYPE_TAG('j','i','e', 0 ), // jie = Jilbe
+ TRUETYPE_TAG('j','i','g', 0 ), // jig = Djingili
+ TRUETYPE_TAG('j','i','h', 0 ), // jih = Shangzhai
+ TRUETYPE_TAG('j','i','i', 0 ), // jii = Jiiddu
+ TRUETYPE_TAG('j','i','l', 0 ), // jil = Jilim
+ TRUETYPE_TAG('j','i','m', 0 ), // jim = Jimi (Cameroon)
+ TRUETYPE_TAG('j','i','o', 0 ), // jio = Jiamao
+ TRUETYPE_TAG('j','i','q', 0 ), // jiq = Guanyinqiao
+ TRUETYPE_TAG('j','i','t', 0 ), // jit = Jita
+ TRUETYPE_TAG('j','i','u', 0 ), // jiu = Youle Jinuo
+ TRUETYPE_TAG('j','i','v', 0 ), // jiv = Shuar
+ TRUETYPE_TAG('j','i','y', 0 ), // jiy = Buyuan Jinuo
+ TRUETYPE_TAG('j','k','o', 0 ), // jko = Kubo
+ TRUETYPE_TAG('j','k','u', 0 ), // jku = Labir
+ TRUETYPE_TAG('j','l','e', 0 ), // jle = Ngile
+ TRUETYPE_TAG('j','l','s', 0 ), // jls = Jamaican Sign Language
+ TRUETYPE_TAG('j','m','a', 0 ), // jma = Dima
+ TRUETYPE_TAG('j','m','b', 0 ), // jmb = Zumbun
+ TRUETYPE_TAG('j','m','c', 0 ), // jmc = Machame
+ TRUETYPE_TAG('j','m','d', 0 ), // jmd = Yamdena
+ TRUETYPE_TAG('j','m','i', 0 ), // jmi = Jimi (Nigeria)
+ TRUETYPE_TAG('j','m','l', 0 ), // jml = Jumli
+ TRUETYPE_TAG('j','m','n', 0 ), // jmn = Makuri Naga
+ TRUETYPE_TAG('j','m','r', 0 ), // jmr = Kamara
+ TRUETYPE_TAG('j','m','s', 0 ), // jms = Mashi (Nigeria)
+ TRUETYPE_TAG('j','m','x', 0 ), // jmx = Western Juxtlahuaca Mixtec
+ TRUETYPE_TAG('j','n','a', 0 ), // jna = Jangshung
+ TRUETYPE_TAG('j','n','d', 0 ), // jnd = Jandavra
+ TRUETYPE_TAG('j','n','g', 0 ), // jng = Yangman
+ TRUETYPE_TAG('j','n','i', 0 ), // jni = Janji
+ TRUETYPE_TAG('j','n','j', 0 ), // jnj = Yemsa
+ TRUETYPE_TAG('j','n','l', 0 ), // jnl = Rawat
+ TRUETYPE_TAG('j','n','s', 0 ), // jns = Jaunsari
+ TRUETYPE_TAG('j','o','b', 0 ), // job = Joba
+ TRUETYPE_TAG('j','o','d', 0 ), // jod = Wojenaka
+ TRUETYPE_TAG('j','o','r', 0 ), // jor = Jorá
+ TRUETYPE_TAG('j','o','s', 0 ), // jos = Jordanian Sign Language
+ TRUETYPE_TAG('j','o','w', 0 ), // jow = Jowulu
+ TRUETYPE_TAG('j','p','a', 0 ), // jpa = Jewish Palestinian Aramaic
+ TRUETYPE_TAG('j','p','r', 0 ), // jpr = Judeo-Persian
+ TRUETYPE_TAG('j','p','x', 0 ), // jpx = Japanese (family)
+ TRUETYPE_TAG('j','q','r', 0 ), // jqr = Jaqaru
+ TRUETYPE_TAG('j','r','a', 0 ), // jra = Jarai
+ TRUETYPE_TAG('j','r','b', 0 ), // jrb = Judeo-Arabic
+ TRUETYPE_TAG('j','r','r', 0 ), // jrr = Jiru
+ TRUETYPE_TAG('j','r','t', 0 ), // jrt = Jorto
+ TRUETYPE_TAG('j','r','u', 0 ), // jru = Japrería
+ TRUETYPE_TAG('j','s','l', 0 ), // jsl = Japanese Sign Language
+ TRUETYPE_TAG('j','u','a', 0 ), // jua = Júma
+ TRUETYPE_TAG('j','u','b', 0 ), // jub = Wannu
+ TRUETYPE_TAG('j','u','c', 0 ), // juc = Jurchen
+ TRUETYPE_TAG('j','u','d', 0 ), // jud = Worodougou
+ TRUETYPE_TAG('j','u','h', 0 ), // juh = Hõne
+ TRUETYPE_TAG('j','u','k', 0 ), // juk = Wapan
+ TRUETYPE_TAG('j','u','l', 0 ), // jul = Jirel
+ TRUETYPE_TAG('j','u','m', 0 ), // jum = Jumjum
+ TRUETYPE_TAG('j','u','n', 0 ), // jun = Juang
+ TRUETYPE_TAG('j','u','o', 0 ), // juo = Jiba
+ TRUETYPE_TAG('j','u','p', 0 ), // jup = Hupdë
+ TRUETYPE_TAG('j','u','r', 0 ), // jur = Jurúna
+ TRUETYPE_TAG('j','u','s', 0 ), // jus = Jumla Sign Language
+ TRUETYPE_TAG('j','u','t', 0 ), // jut = Jutish
+ TRUETYPE_TAG('j','u','u', 0 ), // juu = Ju
+ TRUETYPE_TAG('j','u','w', 0 ), // juw = Wãpha
+ TRUETYPE_TAG('j','u','y', 0 ), // juy = Juray
+ TRUETYPE_TAG('j','v','d', 0 ), // jvd = Javindo
+ TRUETYPE_TAG('j','v','n', 0 ), // jvn = Caribbean Javanese
+ TRUETYPE_TAG('j','w','i', 0 ), // jwi = Jwira-Pepesa
+ TRUETYPE_TAG('j','y','a', 0 ), // jya = Jiarong
+ TRUETYPE_TAG('j','y','e', 0 ), // jye = Judeo-Yemeni Arabic
+ TRUETYPE_TAG('j','y','y', 0 ), // jyy = Jaya
+ TRUETYPE_TAG('k','a','a', 0 ), // kaa = Kara-Kalpak
+ TRUETYPE_TAG('k','a','b', 0 ), // kab = Kabyle
+ TRUETYPE_TAG('k','a','c', 0 ), // kac = Kachin
+ TRUETYPE_TAG('k','a','d', 0 ), // kad = Kadara
+ TRUETYPE_TAG('k','a','e', 0 ), // kae = Ketangalan
+ TRUETYPE_TAG('k','a','f', 0 ), // kaf = Katso
+ TRUETYPE_TAG('k','a','g', 0 ), // kag = Kajaman
+ TRUETYPE_TAG('k','a','h', 0 ), // kah = Kara (Central African Republic)
+ TRUETYPE_TAG('k','a','i', 0 ), // kai = Karekare
+ TRUETYPE_TAG('k','a','j', 0 ), // kaj = Jju
+ TRUETYPE_TAG('k','a','k', 0 ), // kak = Kayapa Kallahan
+ TRUETYPE_TAG('k','a','m', 0 ), // kam = Kamba (Kenya)
+ TRUETYPE_TAG('k','a','o', 0 ), // kao = Xaasongaxango
+ TRUETYPE_TAG('k','a','p', 0 ), // kap = Bezhta
+ TRUETYPE_TAG('k','a','q', 0 ), // kaq = Capanahua
+ TRUETYPE_TAG('k','a','r', 0 ), // kar = Karen languages
+ TRUETYPE_TAG('k','a','v', 0 ), // kav = Katukína
+ TRUETYPE_TAG('k','a','w', 0 ), // kaw = Kawi
+ TRUETYPE_TAG('k','a','x', 0 ), // kax = Kao
+ TRUETYPE_TAG('k','a','y', 0 ), // kay = Kamayurá
+ TRUETYPE_TAG('k','b','a', 0 ), // kba = Kalarko
+ TRUETYPE_TAG('k','b','b', 0 ), // kbb = Kaxuiâna
+ TRUETYPE_TAG('k','b','c', 0 ), // kbc = Kadiwéu
+ TRUETYPE_TAG('k','b','d', 0 ), // kbd = Kabardian
+ TRUETYPE_TAG('k','b','e', 0 ), // kbe = Kanju
+ TRUETYPE_TAG('k','b','f', 0 ), // kbf = Kakauhua
+ TRUETYPE_TAG('k','b','g', 0 ), // kbg = Khamba
+ TRUETYPE_TAG('k','b','h', 0 ), // kbh = Camsá
+ TRUETYPE_TAG('k','b','i', 0 ), // kbi = Kaptiau
+ TRUETYPE_TAG('k','b','j', 0 ), // kbj = Kari
+ TRUETYPE_TAG('k','b','k', 0 ), // kbk = Grass Koiari
+ TRUETYPE_TAG('k','b','l', 0 ), // kbl = Kanembu
+ TRUETYPE_TAG('k','b','m', 0 ), // kbm = Iwal
+ TRUETYPE_TAG('k','b','n', 0 ), // kbn = Kare (Central African Republic)
+ TRUETYPE_TAG('k','b','o', 0 ), // kbo = Keliko
+ TRUETYPE_TAG('k','b','p', 0 ), // kbp = Kabiyè
+ TRUETYPE_TAG('k','b','q', 0 ), // kbq = Kamano
+ TRUETYPE_TAG('k','b','r', 0 ), // kbr = Kafa
+ TRUETYPE_TAG('k','b','s', 0 ), // kbs = Kande
+ TRUETYPE_TAG('k','b','t', 0 ), // kbt = Abadi
+ TRUETYPE_TAG('k','b','u', 0 ), // kbu = Kabutra
+ TRUETYPE_TAG('k','b','v', 0 ), // kbv = Dera (Indonesia)
+ TRUETYPE_TAG('k','b','w', 0 ), // kbw = Kaiep
+ TRUETYPE_TAG('k','b','x', 0 ), // kbx = Ap Ma
+ TRUETYPE_TAG('k','b','y', 0 ), // kby = Manga Kanuri
+ TRUETYPE_TAG('k','b','z', 0 ), // kbz = Duhwa
+ TRUETYPE_TAG('k','c','a', 0 ), // kca = Khanty
+ TRUETYPE_TAG('k','c','b', 0 ), // kcb = Kawacha
+ TRUETYPE_TAG('k','c','c', 0 ), // kcc = Lubila
+ TRUETYPE_TAG('k','c','d', 0 ), // kcd = Ngkâlmpw Kanum
+ TRUETYPE_TAG('k','c','e', 0 ), // kce = Kaivi
+ TRUETYPE_TAG('k','c','f', 0 ), // kcf = Ukaan
+ TRUETYPE_TAG('k','c','g', 0 ), // kcg = Tyap
+ TRUETYPE_TAG('k','c','h', 0 ), // kch = Vono
+ TRUETYPE_TAG('k','c','i', 0 ), // kci = Kamantan
+ TRUETYPE_TAG('k','c','j', 0 ), // kcj = Kobiana
+ TRUETYPE_TAG('k','c','k', 0 ), // kck = Kalanga
+ TRUETYPE_TAG('k','c','l', 0 ), // kcl = Kela (Papua New Guinea)
+ TRUETYPE_TAG('k','c','m', 0 ), // kcm = Gula (Central African Republic)
+ TRUETYPE_TAG('k','c','n', 0 ), // kcn = Nubi
+ TRUETYPE_TAG('k','c','o', 0 ), // kco = Kinalakna
+ TRUETYPE_TAG('k','c','p', 0 ), // kcp = Kanga
+ TRUETYPE_TAG('k','c','q', 0 ), // kcq = Kamo
+ TRUETYPE_TAG('k','c','r', 0 ), // kcr = Katla
+ TRUETYPE_TAG('k','c','s', 0 ), // kcs = Koenoem
+ TRUETYPE_TAG('k','c','t', 0 ), // kct = Kaian
+ TRUETYPE_TAG('k','c','u', 0 ), // kcu = Kami (Tanzania)
+ TRUETYPE_TAG('k','c','v', 0 ), // kcv = Kete
+ TRUETYPE_TAG('k','c','w', 0 ), // kcw = Kabwari
+ TRUETYPE_TAG('k','c','x', 0 ), // kcx = Kachama-Ganjule
+ TRUETYPE_TAG('k','c','y', 0 ), // kcy = Korandje
+ TRUETYPE_TAG('k','c','z', 0 ), // kcz = Konongo
+ TRUETYPE_TAG('k','d','a', 0 ), // kda = Worimi
+ TRUETYPE_TAG('k','d','c', 0 ), // kdc = Kutu
+ TRUETYPE_TAG('k','d','d', 0 ), // kdd = Yankunytjatjara
+ TRUETYPE_TAG('k','d','e', 0 ), // kde = Makonde
+ TRUETYPE_TAG('k','d','f', 0 ), // kdf = Mamusi
+ TRUETYPE_TAG('k','d','g', 0 ), // kdg = Seba
+ TRUETYPE_TAG('k','d','h', 0 ), // kdh = Tem
+ TRUETYPE_TAG('k','d','i', 0 ), // kdi = Kumam
+ TRUETYPE_TAG('k','d','j', 0 ), // kdj = Karamojong
+ TRUETYPE_TAG('k','d','k', 0 ), // kdk = Numee
+ TRUETYPE_TAG('k','d','l', 0 ), // kdl = Tsikimba
+ TRUETYPE_TAG('k','d','m', 0 ), // kdm = Kagoma
+ TRUETYPE_TAG('k','d','n', 0 ), // kdn = Kunda
+ TRUETYPE_TAG('k','d','o', 0 ), // kdo = Kordofanian languages
+ TRUETYPE_TAG('k','d','p', 0 ), // kdp = Kaningdon-Nindem
+ TRUETYPE_TAG('k','d','q', 0 ), // kdq = Koch
+ TRUETYPE_TAG('k','d','r', 0 ), // kdr = Karaim
+ TRUETYPE_TAG('k','d','t', 0 ), // kdt = Kuy
+ TRUETYPE_TAG('k','d','u', 0 ), // kdu = Kadaru
+ TRUETYPE_TAG('k','d','v', 0 ), // kdv = Kado
+ TRUETYPE_TAG('k','d','w', 0 ), // kdw = Koneraw
+ TRUETYPE_TAG('k','d','x', 0 ), // kdx = Kam
+ TRUETYPE_TAG('k','d','y', 0 ), // kdy = Keder
+ TRUETYPE_TAG('k','d','z', 0 ), // kdz = Kwaja
+ TRUETYPE_TAG('k','e','a', 0 ), // kea = Kabuverdianu
+ TRUETYPE_TAG('k','e','b', 0 ), // keb = Kélé
+ TRUETYPE_TAG('k','e','c', 0 ), // kec = Keiga
+ TRUETYPE_TAG('k','e','d', 0 ), // ked = Kerewe
+ TRUETYPE_TAG('k','e','e', 0 ), // kee = Eastern Keres
+ TRUETYPE_TAG('k','e','f', 0 ), // kef = Kpessi
+ TRUETYPE_TAG('k','e','g', 0 ), // keg = Tese
+ TRUETYPE_TAG('k','e','h', 0 ), // keh = Keak
+ TRUETYPE_TAG('k','e','i', 0 ), // kei = Kei
+ TRUETYPE_TAG('k','e','j', 0 ), // kej = Kadar
+ TRUETYPE_TAG('k','e','k', 0 ), // kek = Kekchí
+ TRUETYPE_TAG('k','e','l', 0 ), // kel = Kela (Democratic Republic of Congo)
+ TRUETYPE_TAG('k','e','m', 0 ), // kem = Kemak
+ TRUETYPE_TAG('k','e','n', 0 ), // ken = Kenyang
+ TRUETYPE_TAG('k','e','o', 0 ), // keo = Kakwa
+ TRUETYPE_TAG('k','e','p', 0 ), // kep = Kaikadi
+ TRUETYPE_TAG('k','e','q', 0 ), // keq = Kamar
+ TRUETYPE_TAG('k','e','r', 0 ), // ker = Kera
+ TRUETYPE_TAG('k','e','s', 0 ), // kes = Kugbo
+ TRUETYPE_TAG('k','e','t', 0 ), // ket = Ket
+ TRUETYPE_TAG('k','e','u', 0 ), // keu = Akebu
+ TRUETYPE_TAG('k','e','v', 0 ), // kev = Kanikkaran
+ TRUETYPE_TAG('k','e','w', 0 ), // kew = West Kewa
+ TRUETYPE_TAG('k','e','x', 0 ), // kex = Kukna
+ TRUETYPE_TAG('k','e','y', 0 ), // key = Kupia
+ TRUETYPE_TAG('k','e','z', 0 ), // kez = Kukele
+ TRUETYPE_TAG('k','f','a', 0 ), // kfa = Kodava
+ TRUETYPE_TAG('k','f','b', 0 ), // kfb = Northwestern Kolami
+ TRUETYPE_TAG('k','f','c', 0 ), // kfc = Konda-Dora
+ TRUETYPE_TAG('k','f','d', 0 ), // kfd = Korra Koraga
+ TRUETYPE_TAG('k','f','e', 0 ), // kfe = Kota (India)
+ TRUETYPE_TAG('k','f','f', 0 ), // kff = Koya
+ TRUETYPE_TAG('k','f','g', 0 ), // kfg = Kudiya
+ TRUETYPE_TAG('k','f','h', 0 ), // kfh = Kurichiya
+ TRUETYPE_TAG('k','f','i', 0 ), // kfi = Kannada Kurumba
+ TRUETYPE_TAG('k','f','j', 0 ), // kfj = Kemiehua
+ TRUETYPE_TAG('k','f','k', 0 ), // kfk = Kinnauri
+ TRUETYPE_TAG('k','f','l', 0 ), // kfl = Kung
+ TRUETYPE_TAG('k','f','m', 0 ), // kfm = Khunsari
+ TRUETYPE_TAG('k','f','n', 0 ), // kfn = Kuk
+ TRUETYPE_TAG('k','f','o', 0 ), // kfo = Koro (Côte d'Ivoire)
+ TRUETYPE_TAG('k','f','p', 0 ), // kfp = Korwa
+ TRUETYPE_TAG('k','f','q', 0 ), // kfq = Korku
+ TRUETYPE_TAG('k','f','r', 0 ), // kfr = Kachchi
+ TRUETYPE_TAG('k','f','s', 0 ), // kfs = Bilaspuri
+ TRUETYPE_TAG('k','f','t', 0 ), // kft = Kanjari
+ TRUETYPE_TAG('k','f','u', 0 ), // kfu = Katkari
+ TRUETYPE_TAG('k','f','v', 0 ), // kfv = Kurmukar
+ TRUETYPE_TAG('k','f','w', 0 ), // kfw = Kharam Naga
+ TRUETYPE_TAG('k','f','x', 0 ), // kfx = Kullu Pahari
+ TRUETYPE_TAG('k','f','y', 0 ), // kfy = Kumaoni
+ TRUETYPE_TAG('k','f','z', 0 ), // kfz = Koromfé
+ TRUETYPE_TAG('k','g','a', 0 ), // kga = Koyaga
+ TRUETYPE_TAG('k','g','b', 0 ), // kgb = Kawe
+ TRUETYPE_TAG('k','g','c', 0 ), // kgc = Kasseng
+ TRUETYPE_TAG('k','g','d', 0 ), // kgd = Kataang
+ TRUETYPE_TAG('k','g','e', 0 ), // kge = Komering
+ TRUETYPE_TAG('k','g','f', 0 ), // kgf = Kube
+ TRUETYPE_TAG('k','g','g', 0 ), // kgg = Kusunda
+ TRUETYPE_TAG('k','g','h', 0 ), // kgh = Upper Tanudan Kalinga
+ TRUETYPE_TAG('k','g','i', 0 ), // kgi = Selangor Sign Language
+ TRUETYPE_TAG('k','g','j', 0 ), // kgj = Gamale Kham
+ TRUETYPE_TAG('k','g','k', 0 ), // kgk = Kaiwá
+ TRUETYPE_TAG('k','g','l', 0 ), // kgl = Kunggari
+ TRUETYPE_TAG('k','g','m', 0 ), // kgm = Karipúna
+ TRUETYPE_TAG('k','g','n', 0 ), // kgn = Karingani
+ TRUETYPE_TAG('k','g','o', 0 ), // kgo = Krongo
+ TRUETYPE_TAG('k','g','p', 0 ), // kgp = Kaingang
+ TRUETYPE_TAG('k','g','q', 0 ), // kgq = Kamoro
+ TRUETYPE_TAG('k','g','r', 0 ), // kgr = Abun
+ TRUETYPE_TAG('k','g','s', 0 ), // kgs = Kumbainggar
+ TRUETYPE_TAG('k','g','t', 0 ), // kgt = Somyev
+ TRUETYPE_TAG('k','g','u', 0 ), // kgu = Kobol
+ TRUETYPE_TAG('k','g','v', 0 ), // kgv = Karas
+ TRUETYPE_TAG('k','g','w', 0 ), // kgw = Karon Dori
+ TRUETYPE_TAG('k','g','x', 0 ), // kgx = Kamaru
+ TRUETYPE_TAG('k','g','y', 0 ), // kgy = Kyerung
+ TRUETYPE_TAG('k','h','a', 0 ), // kha = Khasi
+ TRUETYPE_TAG('k','h','b', 0 ), // khb = Lü
+ TRUETYPE_TAG('k','h','c', 0 ), // khc = Tukang Besi North
+ TRUETYPE_TAG('k','h','d', 0 ), // khd = Bädi Kanum
+ TRUETYPE_TAG('k','h','e', 0 ), // khe = Korowai
+ TRUETYPE_TAG('k','h','f', 0 ), // khf = Khuen
+ TRUETYPE_TAG('k','h','g', 0 ), // khg = Khams Tibetan
+ TRUETYPE_TAG('k','h','h', 0 ), // khh = Kehu
+ TRUETYPE_TAG('k','h','i', 0 ), // khi = Khoisan languages
+ TRUETYPE_TAG('k','h','j', 0 ), // khj = Kuturmi
+ TRUETYPE_TAG('k','h','k', 0 ), // khk = Halh Mongolian
+ TRUETYPE_TAG('k','h','l', 0 ), // khl = Lusi
+ TRUETYPE_TAG('k','h','n', 0 ), // khn = Khandesi
+ TRUETYPE_TAG('k','h','o', 0 ), // kho = Khotanese
+ TRUETYPE_TAG('k','h','p', 0 ), // khp = Kapori
+ TRUETYPE_TAG('k','h','q', 0 ), // khq = Koyra Chiini Songhay
+ TRUETYPE_TAG('k','h','r', 0 ), // khr = Kharia
+ TRUETYPE_TAG('k','h','s', 0 ), // khs = Kasua
+ TRUETYPE_TAG('k','h','t', 0 ), // kht = Khamti
+ TRUETYPE_TAG('k','h','u', 0 ), // khu = Nkhumbi
+ TRUETYPE_TAG('k','h','v', 0 ), // khv = Khvarshi
+ TRUETYPE_TAG('k','h','w', 0 ), // khw = Khowar
+ TRUETYPE_TAG('k','h','x', 0 ), // khx = Kanu
+ TRUETYPE_TAG('k','h','y', 0 ), // khy = Kele (Democratic Republic of Congo)
+ TRUETYPE_TAG('k','h','z', 0 ), // khz = Keapara
+ TRUETYPE_TAG('k','i','a', 0 ), // kia = Kim
+ TRUETYPE_TAG('k','i','b', 0 ), // kib = Koalib
+ TRUETYPE_TAG('k','i','c', 0 ), // kic = Kickapoo
+ TRUETYPE_TAG('k','i','d', 0 ), // kid = Koshin
+ TRUETYPE_TAG('k','i','e', 0 ), // kie = Kibet
+ TRUETYPE_TAG('k','i','f', 0 ), // kif = Eastern Parbate Kham
+ TRUETYPE_TAG('k','i','g', 0 ), // kig = Kimaama
+ TRUETYPE_TAG('k','i','h', 0 ), // kih = Kilmeri
+ TRUETYPE_TAG('k','i','i', 0 ), // kii = Kitsai
+ TRUETYPE_TAG('k','i','j', 0 ), // kij = Kilivila
+ TRUETYPE_TAG('k','i','l', 0 ), // kil = Kariya
+ TRUETYPE_TAG('k','i','m', 0 ), // kim = Karagas
+ TRUETYPE_TAG('k','i','o', 0 ), // kio = Kiowa
+ TRUETYPE_TAG('k','i','p', 0 ), // kip = Sheshi Kham
+ TRUETYPE_TAG('k','i','q', 0 ), // kiq = Kosadle
+ TRUETYPE_TAG('k','i','s', 0 ), // kis = Kis
+ TRUETYPE_TAG('k','i','t', 0 ), // kit = Agob
+ TRUETYPE_TAG('k','i','u', 0 ), // kiu = Kirmanjki (individual language)
+ TRUETYPE_TAG('k','i','v', 0 ), // kiv = Kimbu
+ TRUETYPE_TAG('k','i','w', 0 ), // kiw = Northeast Kiwai
+ TRUETYPE_TAG('k','i','x', 0 ), // kix = Khiamniungan Naga
+ TRUETYPE_TAG('k','i','y', 0 ), // kiy = Kirikiri
+ TRUETYPE_TAG('k','i','z', 0 ), // kiz = Kisi
+ TRUETYPE_TAG('k','j','a', 0 ), // kja = Mlap
+ TRUETYPE_TAG('k','j','b', 0 ), // kjb = Q'anjob'al
+ TRUETYPE_TAG('k','j','c', 0 ), // kjc = Coastal Konjo
+ TRUETYPE_TAG('k','j','d', 0 ), // kjd = Southern Kiwai
+ TRUETYPE_TAG('k','j','e', 0 ), // kje = Kisar
+ TRUETYPE_TAG('k','j','f', 0 ), // kjf = Khalaj
+ TRUETYPE_TAG('k','j','g', 0 ), // kjg = Khmu
+ TRUETYPE_TAG('k','j','h', 0 ), // kjh = Khakas
+ TRUETYPE_TAG('k','j','i', 0 ), // kji = Zabana
+ TRUETYPE_TAG('k','j','j', 0 ), // kjj = Khinalugh
+ TRUETYPE_TAG('k','j','k', 0 ), // kjk = Highland Konjo
+ TRUETYPE_TAG('k','j','l', 0 ), // kjl = Western Parbate Kham
+ TRUETYPE_TAG('k','j','m', 0 ), // kjm = Kháng
+ TRUETYPE_TAG('k','j','n', 0 ), // kjn = Kunjen
+ TRUETYPE_TAG('k','j','o', 0 ), // kjo = Harijan Kinnauri
+ TRUETYPE_TAG('k','j','p', 0 ), // kjp = Pwo Eastern Karen
+ TRUETYPE_TAG('k','j','q', 0 ), // kjq = Western Keres
+ TRUETYPE_TAG('k','j','r', 0 ), // kjr = Kurudu
+ TRUETYPE_TAG('k','j','s', 0 ), // kjs = East Kewa
+ TRUETYPE_TAG('k','j','t', 0 ), // kjt = Phrae Pwo Karen
+ TRUETYPE_TAG('k','j','u', 0 ), // kju = Kashaya
+ TRUETYPE_TAG('k','j','x', 0 ), // kjx = Ramopa
+ TRUETYPE_TAG('k','j','y', 0 ), // kjy = Erave
+ TRUETYPE_TAG('k','j','z', 0 ), // kjz = Bumthangkha
+ TRUETYPE_TAG('k','k','a', 0 ), // kka = Kakanda
+ TRUETYPE_TAG('k','k','b', 0 ), // kkb = Kwerisa
+ TRUETYPE_TAG('k','k','c', 0 ), // kkc = Odoodee
+ TRUETYPE_TAG('k','k','d', 0 ), // kkd = Kinuku
+ TRUETYPE_TAG('k','k','e', 0 ), // kke = Kakabe
+ TRUETYPE_TAG('k','k','f', 0 ), // kkf = Kalaktang Monpa
+ TRUETYPE_TAG('k','k','g', 0 ), // kkg = Mabaka Valley Kalinga
+ TRUETYPE_TAG('k','k','h', 0 ), // kkh = Khün
+ TRUETYPE_TAG('k','k','i', 0 ), // kki = Kagulu
+ TRUETYPE_TAG('k','k','j', 0 ), // kkj = Kako
+ TRUETYPE_TAG('k','k','k', 0 ), // kkk = Kokota
+ TRUETYPE_TAG('k','k','l', 0 ), // kkl = Kosarek Yale
+ TRUETYPE_TAG('k','k','m', 0 ), // kkm = Kiong
+ TRUETYPE_TAG('k','k','n', 0 ), // kkn = Kon Keu
+ TRUETYPE_TAG('k','k','o', 0 ), // kko = Karko
+ TRUETYPE_TAG('k','k','p', 0 ), // kkp = Gugubera
+ TRUETYPE_TAG('k','k','q', 0 ), // kkq = Kaiku
+ TRUETYPE_TAG('k','k','r', 0 ), // kkr = Kir-Balar
+ TRUETYPE_TAG('k','k','s', 0 ), // kks = Giiwo
+ TRUETYPE_TAG('k','k','t', 0 ), // kkt = Koi
+ TRUETYPE_TAG('k','k','u', 0 ), // kku = Tumi
+ TRUETYPE_TAG('k','k','v', 0 ), // kkv = Kangean
+ TRUETYPE_TAG('k','k','w', 0 ), // kkw = Teke-Kukuya
+ TRUETYPE_TAG('k','k','x', 0 ), // kkx = Kohin
+ TRUETYPE_TAG('k','k','y', 0 ), // kky = Guguyimidjir
+ TRUETYPE_TAG('k','k','z', 0 ), // kkz = Kaska
+ TRUETYPE_TAG('k','l','a', 0 ), // kla = Klamath-Modoc
+ TRUETYPE_TAG('k','l','b', 0 ), // klb = Kiliwa
+ TRUETYPE_TAG('k','l','c', 0 ), // klc = Kolbila
+ TRUETYPE_TAG('k','l','d', 0 ), // kld = Gamilaraay
+ TRUETYPE_TAG('k','l','e', 0 ), // kle = Kulung (Nepal)
+ TRUETYPE_TAG('k','l','f', 0 ), // klf = Kendeje
+ TRUETYPE_TAG('k','l','g', 0 ), // klg = Tagakaulo
+ TRUETYPE_TAG('k','l','h', 0 ), // klh = Weliki
+ TRUETYPE_TAG('k','l','i', 0 ), // kli = Kalumpang
+ TRUETYPE_TAG('k','l','j', 0 ), // klj = Turkic Khalaj
+ TRUETYPE_TAG('k','l','k', 0 ), // klk = Kono (Nigeria)
+ TRUETYPE_TAG('k','l','l', 0 ), // kll = Kagan Kalagan
+ TRUETYPE_TAG('k','l','m', 0 ), // klm = Migum
+ TRUETYPE_TAG('k','l','n', 0 ), // kln = Kalenjin
+ TRUETYPE_TAG('k','l','o', 0 ), // klo = Kapya
+ TRUETYPE_TAG('k','l','p', 0 ), // klp = Kamasa
+ TRUETYPE_TAG('k','l','q', 0 ), // klq = Rumu
+ TRUETYPE_TAG('k','l','r', 0 ), // klr = Khaling
+ TRUETYPE_TAG('k','l','s', 0 ), // kls = Kalasha
+ TRUETYPE_TAG('k','l','t', 0 ), // klt = Nukna
+ TRUETYPE_TAG('k','l','u', 0 ), // klu = Klao
+ TRUETYPE_TAG('k','l','v', 0 ), // klv = Maskelynes
+ TRUETYPE_TAG('k','l','w', 0 ), // klw = Lindu
+ TRUETYPE_TAG('k','l','x', 0 ), // klx = Koluwawa
+ TRUETYPE_TAG('k','l','y', 0 ), // kly = Kalao
+ TRUETYPE_TAG('k','l','z', 0 ), // klz = Kabola
+ TRUETYPE_TAG('k','m','a', 0 ), // kma = Konni
+ TRUETYPE_TAG('k','m','b', 0 ), // kmb = Kimbundu
+ TRUETYPE_TAG('k','m','c', 0 ), // kmc = Southern Dong
+ TRUETYPE_TAG('k','m','d', 0 ), // kmd = Majukayang Kalinga
+ TRUETYPE_TAG('k','m','e', 0 ), // kme = Bakole
+ TRUETYPE_TAG('k','m','f', 0 ), // kmf = Kare (Papua New Guinea)
+ TRUETYPE_TAG('k','m','g', 0 ), // kmg = Kâte
+ TRUETYPE_TAG('k','m','h', 0 ), // kmh = Kalam
+ TRUETYPE_TAG('k','m','i', 0 ), // kmi = Kami (Nigeria)
+ TRUETYPE_TAG('k','m','j', 0 ), // kmj = Kumarbhag Paharia
+ TRUETYPE_TAG('k','m','k', 0 ), // kmk = Limos Kalinga
+ TRUETYPE_TAG('k','m','l', 0 ), // kml = Lower Tanudan Kalinga
+ TRUETYPE_TAG('k','m','m', 0 ), // kmm = Kom (India)
+ TRUETYPE_TAG('k','m','n', 0 ), // kmn = Awtuw
+ TRUETYPE_TAG('k','m','o', 0 ), // kmo = Kwoma
+ TRUETYPE_TAG('k','m','p', 0 ), // kmp = Gimme
+ TRUETYPE_TAG('k','m','q', 0 ), // kmq = Kwama
+ TRUETYPE_TAG('k','m','r', 0 ), // kmr = Northern Kurdish
+ TRUETYPE_TAG('k','m','s', 0 ), // kms = Kamasau
+ TRUETYPE_TAG('k','m','t', 0 ), // kmt = Kemtuik
+ TRUETYPE_TAG('k','m','u', 0 ), // kmu = Kanite
+ TRUETYPE_TAG('k','m','v', 0 ), // kmv = Karipúna Creole French
+ TRUETYPE_TAG('k','m','w', 0 ), // kmw = Komo (Democratic Republic of Congo)
+ TRUETYPE_TAG('k','m','x', 0 ), // kmx = Waboda
+ TRUETYPE_TAG('k','m','y', 0 ), // kmy = Koma
+ TRUETYPE_TAG('k','m','z', 0 ), // kmz = Khorasani Turkish
+ TRUETYPE_TAG('k','n','a', 0 ), // kna = Dera (Nigeria)
+ TRUETYPE_TAG('k','n','b', 0 ), // knb = Lubuagan Kalinga
+ TRUETYPE_TAG('k','n','c', 0 ), // knc = Central Kanuri
+ TRUETYPE_TAG('k','n','d', 0 ), // knd = Konda
+ TRUETYPE_TAG('k','n','e', 0 ), // kne = Kankanaey
+ TRUETYPE_TAG('k','n','f', 0 ), // knf = Mankanya
+ TRUETYPE_TAG('k','n','g', 0 ), // kng = Koongo
+ TRUETYPE_TAG('k','n','i', 0 ), // kni = Kanufi
+ TRUETYPE_TAG('k','n','j', 0 ), // knj = Western Kanjobal
+ TRUETYPE_TAG('k','n','k', 0 ), // knk = Kuranko
+ TRUETYPE_TAG('k','n','l', 0 ), // knl = Keninjal
+ TRUETYPE_TAG('k','n','m', 0 ), // knm = Kanamarí
+ TRUETYPE_TAG('k','n','n', 0 ), // knn = Konkani (individual language)
+ TRUETYPE_TAG('k','n','o', 0 ), // kno = Kono (Sierra Leone)
+ TRUETYPE_TAG('k','n','p', 0 ), // knp = Kwanja
+ TRUETYPE_TAG('k','n','q', 0 ), // knq = Kintaq
+ TRUETYPE_TAG('k','n','r', 0 ), // knr = Kaningra
+ TRUETYPE_TAG('k','n','s', 0 ), // kns = Kensiu
+ TRUETYPE_TAG('k','n','t', 0 ), // knt = Panoan Katukína
+ TRUETYPE_TAG('k','n','u', 0 ), // knu = Kono (Guinea)
+ TRUETYPE_TAG('k','n','v', 0 ), // knv = Tabo
+ TRUETYPE_TAG('k','n','w', 0 ), // knw = Kung-Ekoka
+ TRUETYPE_TAG('k','n','x', 0 ), // knx = Kendayan
+ TRUETYPE_TAG('k','n','y', 0 ), // kny = Kanyok
+ TRUETYPE_TAG('k','n','z', 0 ), // knz = Kalamsé
+ TRUETYPE_TAG('k','o','a', 0 ), // koa = Konomala
+ TRUETYPE_TAG('k','o','c', 0 ), // koc = Kpati
+ TRUETYPE_TAG('k','o','d', 0 ), // kod = Kodi
+ TRUETYPE_TAG('k','o','e', 0 ), // koe = Kacipo-Balesi
+ TRUETYPE_TAG('k','o','f', 0 ), // kof = Kubi
+ TRUETYPE_TAG('k','o','g', 0 ), // kog = Cogui
+ TRUETYPE_TAG('k','o','h', 0 ), // koh = Koyo
+ TRUETYPE_TAG('k','o','i', 0 ), // koi = Komi-Permyak
+ TRUETYPE_TAG('k','o','j', 0 ), // koj = Sara Dunjo
+ TRUETYPE_TAG('k','o','k', 0 ), // kok = Konkani (macrolanguage)
+ TRUETYPE_TAG('k','o','l', 0 ), // kol = Kol (Papua New Guinea)
+ TRUETYPE_TAG('k','o','o', 0 ), // koo = Konzo
+ TRUETYPE_TAG('k','o','p', 0 ), // kop = Waube
+ TRUETYPE_TAG('k','o','q', 0 ), // koq = Kota (Gabon)
+ TRUETYPE_TAG('k','o','s', 0 ), // kos = Kosraean
+ TRUETYPE_TAG('k','o','t', 0 ), // kot = Lagwan
+ TRUETYPE_TAG('k','o','u', 0 ), // kou = Koke
+ TRUETYPE_TAG('k','o','v', 0 ), // kov = Kudu-Camo
+ TRUETYPE_TAG('k','o','w', 0 ), // kow = Kugama
+ TRUETYPE_TAG('k','o','x', 0 ), // kox = Coxima
+ TRUETYPE_TAG('k','o','y', 0 ), // koy = Koyukon
+ TRUETYPE_TAG('k','o','z', 0 ), // koz = Korak
+ TRUETYPE_TAG('k','p','a', 0 ), // kpa = Kutto
+ TRUETYPE_TAG('k','p','b', 0 ), // kpb = Mullu Kurumba
+ TRUETYPE_TAG('k','p','c', 0 ), // kpc = Curripaco
+ TRUETYPE_TAG('k','p','d', 0 ), // kpd = Koba
+ TRUETYPE_TAG('k','p','e', 0 ), // kpe = Kpelle
+ TRUETYPE_TAG('k','p','f', 0 ), // kpf = Komba
+ TRUETYPE_TAG('k','p','g', 0 ), // kpg = Kapingamarangi
+ TRUETYPE_TAG('k','p','h', 0 ), // kph = Kplang
+ TRUETYPE_TAG('k','p','i', 0 ), // kpi = Kofei
+ TRUETYPE_TAG('k','p','j', 0 ), // kpj = Karajá
+ TRUETYPE_TAG('k','p','k', 0 ), // kpk = Kpan
+ TRUETYPE_TAG('k','p','l', 0 ), // kpl = Kpala
+ TRUETYPE_TAG('k','p','m', 0 ), // kpm = Koho
+ TRUETYPE_TAG('k','p','n', 0 ), // kpn = Kepkiriwát
+ TRUETYPE_TAG('k','p','o', 0 ), // kpo = Ikposo
+ TRUETYPE_TAG('k','p','p', 0 ), // kpp = Paku Karen
+ TRUETYPE_TAG('k','p','q', 0 ), // kpq = Korupun-Sela
+ TRUETYPE_TAG('k','p','r', 0 ), // kpr = Korafe-Yegha
+ TRUETYPE_TAG('k','p','s', 0 ), // kps = Tehit
+ TRUETYPE_TAG('k','p','t', 0 ), // kpt = Karata
+ TRUETYPE_TAG('k','p','u', 0 ), // kpu = Kafoa
+ TRUETYPE_TAG('k','p','v', 0 ), // kpv = Komi-Zyrian
+ TRUETYPE_TAG('k','p','w', 0 ), // kpw = Kobon
+ TRUETYPE_TAG('k','p','x', 0 ), // kpx = Mountain Koiali
+ TRUETYPE_TAG('k','p','y', 0 ), // kpy = Koryak
+ TRUETYPE_TAG('k','p','z', 0 ), // kpz = Kupsabiny
+ TRUETYPE_TAG('k','q','a', 0 ), // kqa = Mum
+ TRUETYPE_TAG('k','q','b', 0 ), // kqb = Kovai
+ TRUETYPE_TAG('k','q','c', 0 ), // kqc = Doromu-Koki
+ TRUETYPE_TAG('k','q','d', 0 ), // kqd = Koy Sanjaq Surat
+ TRUETYPE_TAG('k','q','e', 0 ), // kqe = Kalagan
+ TRUETYPE_TAG('k','q','f', 0 ), // kqf = Kakabai
+ TRUETYPE_TAG('k','q','g', 0 ), // kqg = Khe
+ TRUETYPE_TAG('k','q','h', 0 ), // kqh = Kisankasa
+ TRUETYPE_TAG('k','q','i', 0 ), // kqi = Koitabu
+ TRUETYPE_TAG('k','q','j', 0 ), // kqj = Koromira
+ TRUETYPE_TAG('k','q','k', 0 ), // kqk = Kotafon Gbe
+ TRUETYPE_TAG('k','q','l', 0 ), // kql = Kyenele
+ TRUETYPE_TAG('k','q','m', 0 ), // kqm = Khisa
+ TRUETYPE_TAG('k','q','n', 0 ), // kqn = Kaonde
+ TRUETYPE_TAG('k','q','o', 0 ), // kqo = Eastern Krahn
+ TRUETYPE_TAG('k','q','p', 0 ), // kqp = Kimré
+ TRUETYPE_TAG('k','q','q', 0 ), // kqq = Krenak
+ TRUETYPE_TAG('k','q','r', 0 ), // kqr = Kimaragang
+ TRUETYPE_TAG('k','q','s', 0 ), // kqs = Northern Kissi
+ TRUETYPE_TAG('k','q','t', 0 ), // kqt = Klias River Kadazan
+ TRUETYPE_TAG('k','q','u', 0 ), // kqu = Seroa
+ TRUETYPE_TAG('k','q','v', 0 ), // kqv = Okolod
+ TRUETYPE_TAG('k','q','w', 0 ), // kqw = Kandas
+ TRUETYPE_TAG('k','q','x', 0 ), // kqx = Mser
+ TRUETYPE_TAG('k','q','y', 0 ), // kqy = Koorete
+ TRUETYPE_TAG('k','q','z', 0 ), // kqz = Korana
+ TRUETYPE_TAG('k','r','a', 0 ), // kra = Kumhali
+ TRUETYPE_TAG('k','r','b', 0 ), // krb = Karkin
+ TRUETYPE_TAG('k','r','c', 0 ), // krc = Karachay-Balkar
+ TRUETYPE_TAG('k','r','d', 0 ), // krd = Kairui-Midiki
+ TRUETYPE_TAG('k','r','e', 0 ), // kre = Panará
+ TRUETYPE_TAG('k','r','f', 0 ), // krf = Koro (Vanuatu)
+ TRUETYPE_TAG('k','r','h', 0 ), // krh = Kurama
+ TRUETYPE_TAG('k','r','i', 0 ), // kri = Krio
+ TRUETYPE_TAG('k','r','j', 0 ), // krj = Kinaray-A
+ TRUETYPE_TAG('k','r','k', 0 ), // krk = Kerek
+ TRUETYPE_TAG('k','r','l', 0 ), // krl = Karelian
+ TRUETYPE_TAG('k','r','m', 0 ), // krm = Krim
+ TRUETYPE_TAG('k','r','n', 0 ), // krn = Sapo
+ TRUETYPE_TAG('k','r','o', 0 ), // kro = Kru languages
+ TRUETYPE_TAG('k','r','p', 0 ), // krp = Korop
+ TRUETYPE_TAG('k','r','r', 0 ), // krr = Kru'ng 2
+ TRUETYPE_TAG('k','r','s', 0 ), // krs = Gbaya (Sudan)
+ TRUETYPE_TAG('k','r','t', 0 ), // krt = Tumari Kanuri
+ TRUETYPE_TAG('k','r','u', 0 ), // kru = Kurukh
+ TRUETYPE_TAG('k','r','v', 0 ), // krv = Kavet
+ TRUETYPE_TAG('k','r','w', 0 ), // krw = Western Krahn
+ TRUETYPE_TAG('k','r','x', 0 ), // krx = Karon
+ TRUETYPE_TAG('k','r','y', 0 ), // kry = Kryts
+ TRUETYPE_TAG('k','r','z', 0 ), // krz = Sota Kanum
+ TRUETYPE_TAG('k','s','a', 0 ), // ksa = Shuwa-Zamani
+ TRUETYPE_TAG('k','s','b', 0 ), // ksb = Shambala
+ TRUETYPE_TAG('k','s','c', 0 ), // ksc = Southern Kalinga
+ TRUETYPE_TAG('k','s','d', 0 ), // ksd = Kuanua
+ TRUETYPE_TAG('k','s','e', 0 ), // kse = Kuni
+ TRUETYPE_TAG('k','s','f', 0 ), // ksf = Bafia
+ TRUETYPE_TAG('k','s','g', 0 ), // ksg = Kusaghe
+ TRUETYPE_TAG('k','s','h', 0 ), // ksh = Kölsch
+ TRUETYPE_TAG('k','s','i', 0 ), // ksi = Krisa
+ TRUETYPE_TAG('k','s','j', 0 ), // ksj = Uare
+ TRUETYPE_TAG('k','s','k', 0 ), // ksk = Kansa
+ TRUETYPE_TAG('k','s','l', 0 ), // ksl = Kumalu
+ TRUETYPE_TAG('k','s','m', 0 ), // ksm = Kumba
+ TRUETYPE_TAG('k','s','n', 0 ), // ksn = Kasiguranin
+ TRUETYPE_TAG('k','s','o', 0 ), // kso = Kofa
+ TRUETYPE_TAG('k','s','p', 0 ), // ksp = Kaba
+ TRUETYPE_TAG('k','s','q', 0 ), // ksq = Kwaami
+ TRUETYPE_TAG('k','s','r', 0 ), // ksr = Borong
+ TRUETYPE_TAG('k','s','s', 0 ), // kss = Southern Kisi
+ TRUETYPE_TAG('k','s','t', 0 ), // kst = Winyé
+ TRUETYPE_TAG('k','s','u', 0 ), // ksu = Khamyang
+ TRUETYPE_TAG('k','s','v', 0 ), // ksv = Kusu
+ TRUETYPE_TAG('k','s','w', 0 ), // ksw = S'gaw Karen
+ TRUETYPE_TAG('k','s','x', 0 ), // ksx = Kedang
+ TRUETYPE_TAG('k','s','y', 0 ), // ksy = Kharia Thar
+ TRUETYPE_TAG('k','s','z', 0 ), // ksz = Kodaku
+ TRUETYPE_TAG('k','t','a', 0 ), // kta = Katua
+ TRUETYPE_TAG('k','t','b', 0 ), // ktb = Kambaata
+ TRUETYPE_TAG('k','t','c', 0 ), // ktc = Kholok
+ TRUETYPE_TAG('k','t','d', 0 ), // ktd = Kokata
+ TRUETYPE_TAG('k','t','e', 0 ), // kte = Nubri
+ TRUETYPE_TAG('k','t','f', 0 ), // ktf = Kwami
+ TRUETYPE_TAG('k','t','g', 0 ), // ktg = Kalkutung
+ TRUETYPE_TAG('k','t','h', 0 ), // kth = Karanga
+ TRUETYPE_TAG('k','t','i', 0 ), // kti = North Muyu
+ TRUETYPE_TAG('k','t','j', 0 ), // ktj = Plapo Krumen
+ TRUETYPE_TAG('k','t','k', 0 ), // ktk = Kaniet
+ TRUETYPE_TAG('k','t','l', 0 ), // ktl = Koroshi
+ TRUETYPE_TAG('k','t','m', 0 ), // ktm = Kurti
+ TRUETYPE_TAG('k','t','n', 0 ), // ktn = Karitiâna
+ TRUETYPE_TAG('k','t','o', 0 ), // kto = Kuot
+ TRUETYPE_TAG('k','t','p', 0 ), // ktp = Kaduo
+ TRUETYPE_TAG('k','t','q', 0 ), // ktq = Katabaga
+ TRUETYPE_TAG('k','t','r', 0 ), // ktr = Kota Marudu Tinagas
+ TRUETYPE_TAG('k','t','s', 0 ), // kts = South Muyu
+ TRUETYPE_TAG('k','t','t', 0 ), // ktt = Ketum
+ TRUETYPE_TAG('k','t','u', 0 ), // ktu = Kituba (Democratic Republic of Congo)
+ TRUETYPE_TAG('k','t','v', 0 ), // ktv = Eastern Katu
+ TRUETYPE_TAG('k','t','w', 0 ), // ktw = Kato
+ TRUETYPE_TAG('k','t','x', 0 ), // ktx = Kaxararí
+ TRUETYPE_TAG('k','t','y', 0 ), // kty = Kango (Bas-Uélé District)
+ TRUETYPE_TAG('k','t','z', 0 ), // ktz = Ju/'hoan
+ TRUETYPE_TAG('k','u','b', 0 ), // kub = Kutep
+ TRUETYPE_TAG('k','u','c', 0 ), // kuc = Kwinsu
+ TRUETYPE_TAG('k','u','d', 0 ), // kud = 'Auhelawa
+ TRUETYPE_TAG('k','u','e', 0 ), // kue = Kuman
+ TRUETYPE_TAG('k','u','f', 0 ), // kuf = Western Katu
+ TRUETYPE_TAG('k','u','g', 0 ), // kug = Kupa
+ TRUETYPE_TAG('k','u','h', 0 ), // kuh = Kushi
+ TRUETYPE_TAG('k','u','i', 0 ), // kui = Kuikúro-Kalapálo
+ TRUETYPE_TAG('k','u','j', 0 ), // kuj = Kuria
+ TRUETYPE_TAG('k','u','k', 0 ), // kuk = Kepo'
+ TRUETYPE_TAG('k','u','l', 0 ), // kul = Kulere
+ TRUETYPE_TAG('k','u','m', 0 ), // kum = Kumyk
+ TRUETYPE_TAG('k','u','n', 0 ), // kun = Kunama
+ TRUETYPE_TAG('k','u','o', 0 ), // kuo = Kumukio
+ TRUETYPE_TAG('k','u','p', 0 ), // kup = Kunimaipa
+ TRUETYPE_TAG('k','u','q', 0 ), // kuq = Karipuna
+ TRUETYPE_TAG('k','u','s', 0 ), // kus = Kusaal
+ TRUETYPE_TAG('k','u','t', 0 ), // kut = Kutenai
+ TRUETYPE_TAG('k','u','u', 0 ), // kuu = Upper Kuskokwim
+ TRUETYPE_TAG('k','u','v', 0 ), // kuv = Kur
+ TRUETYPE_TAG('k','u','w', 0 ), // kuw = Kpagua
+ TRUETYPE_TAG('k','u','x', 0 ), // kux = Kukatja
+ TRUETYPE_TAG('k','u','y', 0 ), // kuy = Kuuku-Ya'u
+ TRUETYPE_TAG('k','u','z', 0 ), // kuz = Kunza
+ TRUETYPE_TAG('k','v','a', 0 ), // kva = Bagvalal
+ TRUETYPE_TAG('k','v','b', 0 ), // kvb = Kubu
+ TRUETYPE_TAG('k','v','c', 0 ), // kvc = Kove
+ TRUETYPE_TAG('k','v','d', 0 ), // kvd = Kui (Indonesia)
+ TRUETYPE_TAG('k','v','e', 0 ), // kve = Kalabakan
+ TRUETYPE_TAG('k','v','f', 0 ), // kvf = Kabalai
+ TRUETYPE_TAG('k','v','g', 0 ), // kvg = Kuni-Boazi
+ TRUETYPE_TAG('k','v','h', 0 ), // kvh = Komodo
+ TRUETYPE_TAG('k','v','i', 0 ), // kvi = Kwang
+ TRUETYPE_TAG('k','v','j', 0 ), // kvj = Psikye
+ TRUETYPE_TAG('k','v','k', 0 ), // kvk = Korean Sign Language
+ TRUETYPE_TAG('k','v','l', 0 ), // kvl = Brek Karen
+ TRUETYPE_TAG('k','v','m', 0 ), // kvm = Kendem
+ TRUETYPE_TAG('k','v','n', 0 ), // kvn = Border Kuna
+ TRUETYPE_TAG('k','v','o', 0 ), // kvo = Dobel
+ TRUETYPE_TAG('k','v','p', 0 ), // kvp = Kompane
+ TRUETYPE_TAG('k','v','q', 0 ), // kvq = Geba Karen
+ TRUETYPE_TAG('k','v','r', 0 ), // kvr = Kerinci
+ TRUETYPE_TAG('k','v','s', 0 ), // kvs = Kunggara
+ TRUETYPE_TAG('k','v','t', 0 ), // kvt = Lahta Karen
+ TRUETYPE_TAG('k','v','u', 0 ), // kvu = Yinbaw Karen
+ TRUETYPE_TAG('k','v','v', 0 ), // kvv = Kola
+ TRUETYPE_TAG('k','v','w', 0 ), // kvw = Wersing
+ TRUETYPE_TAG('k','v','x', 0 ), // kvx = Parkari Koli
+ TRUETYPE_TAG('k','v','y', 0 ), // kvy = Yintale Karen
+ TRUETYPE_TAG('k','v','z', 0 ), // kvz = Tsakwambo
+ TRUETYPE_TAG('k','w','a', 0 ), // kwa = Dâw
+ TRUETYPE_TAG('k','w','b', 0 ), // kwb = Kwa
+ TRUETYPE_TAG('k','w','c', 0 ), // kwc = Likwala
+ TRUETYPE_TAG('k','w','d', 0 ), // kwd = Kwaio
+ TRUETYPE_TAG('k','w','e', 0 ), // kwe = Kwerba
+ TRUETYPE_TAG('k','w','f', 0 ), // kwf = Kwara'ae
+ TRUETYPE_TAG('k','w','g', 0 ), // kwg = Sara Kaba Deme
+ TRUETYPE_TAG('k','w','h', 0 ), // kwh = Kowiai
+ TRUETYPE_TAG('k','w','i', 0 ), // kwi = Awa-Cuaiquer
+ TRUETYPE_TAG('k','w','j', 0 ), // kwj = Kwanga
+ TRUETYPE_TAG('k','w','k', 0 ), // kwk = Kwakiutl
+ TRUETYPE_TAG('k','w','l', 0 ), // kwl = Kofyar
+ TRUETYPE_TAG('k','w','m', 0 ), // kwm = Kwambi
+ TRUETYPE_TAG('k','w','n', 0 ), // kwn = Kwangali
+ TRUETYPE_TAG('k','w','o', 0 ), // kwo = Kwomtari
+ TRUETYPE_TAG('k','w','p', 0 ), // kwp = Kodia
+ TRUETYPE_TAG('k','w','q', 0 ), // kwq = Kwak
+ TRUETYPE_TAG('k','w','r', 0 ), // kwr = Kwer
+ TRUETYPE_TAG('k','w','s', 0 ), // kws = Kwese
+ TRUETYPE_TAG('k','w','t', 0 ), // kwt = Kwesten
+ TRUETYPE_TAG('k','w','u', 0 ), // kwu = Kwakum
+ TRUETYPE_TAG('k','w','v', 0 ), // kwv = Sara Kaba Náà
+ TRUETYPE_TAG('k','w','w', 0 ), // kww = Kwinti
+ TRUETYPE_TAG('k','w','x', 0 ), // kwx = Khirwar
+ TRUETYPE_TAG('k','w','y', 0 ), // kwy = San Salvador Kongo
+ TRUETYPE_TAG('k','w','z', 0 ), // kwz = Kwadi
+ TRUETYPE_TAG('k','x','a', 0 ), // kxa = Kairiru
+ TRUETYPE_TAG('k','x','b', 0 ), // kxb = Krobu
+ TRUETYPE_TAG('k','x','c', 0 ), // kxc = Konso
+ TRUETYPE_TAG('k','x','d', 0 ), // kxd = Brunei
+ TRUETYPE_TAG('k','x','e', 0 ), // kxe = Kakihum
+ TRUETYPE_TAG('k','x','f', 0 ), // kxf = Manumanaw Karen
+ TRUETYPE_TAG('k','x','h', 0 ), // kxh = Karo (Ethiopia)
+ TRUETYPE_TAG('k','x','i', 0 ), // kxi = Keningau Murut
+ TRUETYPE_TAG('k','x','j', 0 ), // kxj = Kulfa
+ TRUETYPE_TAG('k','x','k', 0 ), // kxk = Zayein Karen
+ TRUETYPE_TAG('k','x','l', 0 ), // kxl = Nepali Kurux
+ TRUETYPE_TAG('k','x','m', 0 ), // kxm = Northern Khmer
+ TRUETYPE_TAG('k','x','n', 0 ), // kxn = Kanowit-Tanjong Melanau
+ TRUETYPE_TAG('k','x','o', 0 ), // kxo = Kanoé
+ TRUETYPE_TAG('k','x','p', 0 ), // kxp = Wadiyara Koli
+ TRUETYPE_TAG('k','x','q', 0 ), // kxq = Smärky Kanum
+ TRUETYPE_TAG('k','x','r', 0 ), // kxr = Koro (Papua New Guinea)
+ TRUETYPE_TAG('k','x','s', 0 ), // kxs = Kangjia
+ TRUETYPE_TAG('k','x','t', 0 ), // kxt = Koiwat
+ TRUETYPE_TAG('k','x','u', 0 ), // kxu = Kui (India)
+ TRUETYPE_TAG('k','x','v', 0 ), // kxv = Kuvi
+ TRUETYPE_TAG('k','x','w', 0 ), // kxw = Konai
+ TRUETYPE_TAG('k','x','x', 0 ), // kxx = Likuba
+ TRUETYPE_TAG('k','x','y', 0 ), // kxy = Kayong
+ TRUETYPE_TAG('k','x','z', 0 ), // kxz = Kerewo
+ TRUETYPE_TAG('k','y','a', 0 ), // kya = Kwaya
+ TRUETYPE_TAG('k','y','b', 0 ), // kyb = Butbut Kalinga
+ TRUETYPE_TAG('k','y','c', 0 ), // kyc = Kyaka
+ TRUETYPE_TAG('k','y','d', 0 ), // kyd = Karey
+ TRUETYPE_TAG('k','y','e', 0 ), // kye = Krache
+ TRUETYPE_TAG('k','y','f', 0 ), // kyf = Kouya
+ TRUETYPE_TAG('k','y','g', 0 ), // kyg = Keyagana
+ TRUETYPE_TAG('k','y','h', 0 ), // kyh = Karok
+ TRUETYPE_TAG('k','y','i', 0 ), // kyi = Kiput
+ TRUETYPE_TAG('k','y','j', 0 ), // kyj = Karao
+ TRUETYPE_TAG('k','y','k', 0 ), // kyk = Kamayo
+ TRUETYPE_TAG('k','y','l', 0 ), // kyl = Kalapuya
+ TRUETYPE_TAG('k','y','m', 0 ), // kym = Kpatili
+ TRUETYPE_TAG('k','y','n', 0 ), // kyn = Northern Binukidnon
+ TRUETYPE_TAG('k','y','o', 0 ), // kyo = Kelon
+ TRUETYPE_TAG('k','y','p', 0 ), // kyp = Kang
+ TRUETYPE_TAG('k','y','q', 0 ), // kyq = Kenga
+ TRUETYPE_TAG('k','y','r', 0 ), // kyr = Kuruáya
+ TRUETYPE_TAG('k','y','s', 0 ), // kys = Baram Kayan
+ TRUETYPE_TAG('k','y','t', 0 ), // kyt = Kayagar
+ TRUETYPE_TAG('k','y','u', 0 ), // kyu = Western Kayah
+ TRUETYPE_TAG('k','y','v', 0 ), // kyv = Kayort
+ TRUETYPE_TAG('k','y','w', 0 ), // kyw = Kudmali
+ TRUETYPE_TAG('k','y','x', 0 ), // kyx = Rapoisi
+ TRUETYPE_TAG('k','y','y', 0 ), // kyy = Kambaira
+ TRUETYPE_TAG('k','y','z', 0 ), // kyz = Kayabí
+ TRUETYPE_TAG('k','z','a', 0 ), // kza = Western Karaboro
+ TRUETYPE_TAG('k','z','b', 0 ), // kzb = Kaibobo
+ TRUETYPE_TAG('k','z','c', 0 ), // kzc = Bondoukou Kulango
+ TRUETYPE_TAG('k','z','d', 0 ), // kzd = Kadai
+ TRUETYPE_TAG('k','z','e', 0 ), // kze = Kosena
+ TRUETYPE_TAG('k','z','f', 0 ), // kzf = Da'a Kaili
+ TRUETYPE_TAG('k','z','g', 0 ), // kzg = Kikai
+ TRUETYPE_TAG('k','z','h', 0 ), // kzh = Kenuzi-Dongola
+ TRUETYPE_TAG('k','z','i', 0 ), // kzi = Kelabit
+ TRUETYPE_TAG('k','z','j', 0 ), // kzj = Coastal Kadazan
+ TRUETYPE_TAG('k','z','k', 0 ), // kzk = Kazukuru
+ TRUETYPE_TAG('k','z','l', 0 ), // kzl = Kayeli
+ TRUETYPE_TAG('k','z','m', 0 ), // kzm = Kais
+ TRUETYPE_TAG('k','z','n', 0 ), // kzn = Kokola
+ TRUETYPE_TAG('k','z','o', 0 ), // kzo = Kaningi
+ TRUETYPE_TAG('k','z','p', 0 ), // kzp = Kaidipang
+ TRUETYPE_TAG('k','z','q', 0 ), // kzq = Kaike
+ TRUETYPE_TAG('k','z','r', 0 ), // kzr = Karang
+ TRUETYPE_TAG('k','z','s', 0 ), // kzs = Sugut Dusun
+ TRUETYPE_TAG('k','z','t', 0 ), // kzt = Tambunan Dusun
+ TRUETYPE_TAG('k','z','u', 0 ), // kzu = Kayupulau
+ TRUETYPE_TAG('k','z','v', 0 ), // kzv = Komyandaret
+ TRUETYPE_TAG('k','z','w', 0 ), // kzw = Karirí-Xocó
+ TRUETYPE_TAG('k','z','x', 0 ), // kzx = Kamarian
+ TRUETYPE_TAG('k','z','y', 0 ), // kzy = Kango (Tshopo District)
+ TRUETYPE_TAG('k','z','z', 0 ), // kzz = Kalabra
+ TRUETYPE_TAG('l','a','a', 0 ), // laa = Southern Subanen
+ TRUETYPE_TAG('l','a','b', 0 ), // lab = Linear A
+ TRUETYPE_TAG('l','a','c', 0 ), // lac = Lacandon
+ TRUETYPE_TAG('l','a','d', 0 ), // lad = Ladino
+ TRUETYPE_TAG('l','a','e', 0 ), // lae = Pattani
+ TRUETYPE_TAG('l','a','f', 0 ), // laf = Lafofa
+ TRUETYPE_TAG('l','a','g', 0 ), // lag = Langi
+ TRUETYPE_TAG('l','a','h', 0 ), // lah = Lahnda
+ TRUETYPE_TAG('l','a','i', 0 ), // lai = Lambya
+ TRUETYPE_TAG('l','a','j', 0 ), // laj = Lango (Uganda)
+ TRUETYPE_TAG('l','a','k', 0 ), // lak = Laka (Nigeria)
+ TRUETYPE_TAG('l','a','l', 0 ), // lal = Lalia
+ TRUETYPE_TAG('l','a','m', 0 ), // lam = Lamba
+ TRUETYPE_TAG('l','a','n', 0 ), // lan = Laru
+ TRUETYPE_TAG('l','a','p', 0 ), // lap = Laka (Chad)
+ TRUETYPE_TAG('l','a','q', 0 ), // laq = Qabiao
+ TRUETYPE_TAG('l','a','r', 0 ), // lar = Larteh
+ TRUETYPE_TAG('l','a','s', 0 ), // las = Lama (Togo)
+ TRUETYPE_TAG('l','a','u', 0 ), // lau = Laba
+ TRUETYPE_TAG('l','a','w', 0 ), // law = Lauje
+ TRUETYPE_TAG('l','a','x', 0 ), // lax = Tiwa
+ TRUETYPE_TAG('l','a','y', 0 ), // lay = Lama (Myanmar)
+ TRUETYPE_TAG('l','a','z', 0 ), // laz = Aribwatsa
+ TRUETYPE_TAG('l','b','a', 0 ), // lba = Lui
+ TRUETYPE_TAG('l','b','b', 0 ), // lbb = Label
+ TRUETYPE_TAG('l','b','c', 0 ), // lbc = Lakkia
+ TRUETYPE_TAG('l','b','e', 0 ), // lbe = Lak
+ TRUETYPE_TAG('l','b','f', 0 ), // lbf = Tinani
+ TRUETYPE_TAG('l','b','g', 0 ), // lbg = Laopang
+ TRUETYPE_TAG('l','b','i', 0 ), // lbi = La'bi
+ TRUETYPE_TAG('l','b','j', 0 ), // lbj = Ladakhi
+ TRUETYPE_TAG('l','b','k', 0 ), // lbk = Central Bontok
+ TRUETYPE_TAG('l','b','l', 0 ), // lbl = Libon Bikol
+ TRUETYPE_TAG('l','b','m', 0 ), // lbm = Lodhi
+ TRUETYPE_TAG('l','b','n', 0 ), // lbn = Lamet
+ TRUETYPE_TAG('l','b','o', 0 ), // lbo = Laven
+ TRUETYPE_TAG('l','b','q', 0 ), // lbq = Wampar
+ TRUETYPE_TAG('l','b','r', 0 ), // lbr = Northern Lorung
+ TRUETYPE_TAG('l','b','s', 0 ), // lbs = Libyan Sign Language
+ TRUETYPE_TAG('l','b','t', 0 ), // lbt = Lachi
+ TRUETYPE_TAG('l','b','u', 0 ), // lbu = Labu
+ TRUETYPE_TAG('l','b','v', 0 ), // lbv = Lavatbura-Lamusong
+ TRUETYPE_TAG('l','b','w', 0 ), // lbw = Tolaki
+ TRUETYPE_TAG('l','b','x', 0 ), // lbx = Lawangan
+ TRUETYPE_TAG('l','b','y', 0 ), // lby = Lamu-Lamu
+ TRUETYPE_TAG('l','b','z', 0 ), // lbz = Lardil
+ TRUETYPE_TAG('l','c','c', 0 ), // lcc = Legenyem
+ TRUETYPE_TAG('l','c','d', 0 ), // lcd = Lola
+ TRUETYPE_TAG('l','c','e', 0 ), // lce = Loncong
+ TRUETYPE_TAG('l','c','f', 0 ), // lcf = Lubu
+ TRUETYPE_TAG('l','c','h', 0 ), // lch = Luchazi
+ TRUETYPE_TAG('l','c','l', 0 ), // lcl = Lisela
+ TRUETYPE_TAG('l','c','m', 0 ), // lcm = Tungag
+ TRUETYPE_TAG('l','c','p', 0 ), // lcp = Western Lawa
+ TRUETYPE_TAG('l','c','q', 0 ), // lcq = Luhu
+ TRUETYPE_TAG('l','c','s', 0 ), // lcs = Lisabata-Nuniali
+ TRUETYPE_TAG('l','d','b', 0 ), // ldb = Idun
+ TRUETYPE_TAG('l','d','d', 0 ), // ldd = Luri
+ TRUETYPE_TAG('l','d','g', 0 ), // ldg = Lenyima
+ TRUETYPE_TAG('l','d','h', 0 ), // ldh = Lamja-Dengsa-Tola
+ TRUETYPE_TAG('l','d','i', 0 ), // ldi = Laari
+ TRUETYPE_TAG('l','d','j', 0 ), // ldj = Lemoro
+ TRUETYPE_TAG('l','d','k', 0 ), // ldk = Leelau
+ TRUETYPE_TAG('l','d','l', 0 ), // ldl = Kaan
+ TRUETYPE_TAG('l','d','m', 0 ), // ldm = Landoma
+ TRUETYPE_TAG('l','d','n', 0 ), // ldn = Láadan
+ TRUETYPE_TAG('l','d','o', 0 ), // ldo = Loo
+ TRUETYPE_TAG('l','d','p', 0 ), // ldp = Tso
+ TRUETYPE_TAG('l','d','q', 0 ), // ldq = Lufu
+ TRUETYPE_TAG('l','e','a', 0 ), // lea = Lega-Shabunda
+ TRUETYPE_TAG('l','e','b', 0 ), // leb = Lala-Bisa
+ TRUETYPE_TAG('l','e','c', 0 ), // lec = Leco
+ TRUETYPE_TAG('l','e','d', 0 ), // led = Lendu
+ TRUETYPE_TAG('l','e','e', 0 ), // lee = Lyélé
+ TRUETYPE_TAG('l','e','f', 0 ), // lef = Lelemi
+ TRUETYPE_TAG('l','e','g', 0 ), // leg = Lengua
+ TRUETYPE_TAG('l','e','h', 0 ), // leh = Lenje
+ TRUETYPE_TAG('l','e','i', 0 ), // lei = Lemio
+ TRUETYPE_TAG('l','e','j', 0 ), // lej = Lengola
+ TRUETYPE_TAG('l','e','k', 0 ), // lek = Leipon
+ TRUETYPE_TAG('l','e','l', 0 ), // lel = Lele (Democratic Republic of Congo)
+ TRUETYPE_TAG('l','e','m', 0 ), // lem = Nomaande
+ TRUETYPE_TAG('l','e','n', 0 ), // len = Lenca
+ TRUETYPE_TAG('l','e','o', 0 ), // leo = Leti (Cameroon)
+ TRUETYPE_TAG('l','e','p', 0 ), // lep = Lepcha
+ TRUETYPE_TAG('l','e','q', 0 ), // leq = Lembena
+ TRUETYPE_TAG('l','e','r', 0 ), // ler = Lenkau
+ TRUETYPE_TAG('l','e','s', 0 ), // les = Lese
+ TRUETYPE_TAG('l','e','t', 0 ), // let = Lesing-Gelimi
+ TRUETYPE_TAG('l','e','u', 0 ), // leu = Kara (Papua New Guinea)
+ TRUETYPE_TAG('l','e','v', 0 ), // lev = Lamma
+ TRUETYPE_TAG('l','e','w', 0 ), // lew = Ledo Kaili
+ TRUETYPE_TAG('l','e','x', 0 ), // lex = Luang
+ TRUETYPE_TAG('l','e','y', 0 ), // ley = Lemolang
+ TRUETYPE_TAG('l','e','z', 0 ), // lez = Lezghian
+ TRUETYPE_TAG('l','f','a', 0 ), // lfa = Lefa
+ TRUETYPE_TAG('l','f','n', 0 ), // lfn = Lingua Franca Nova
+ TRUETYPE_TAG('l','g','a', 0 ), // lga = Lungga
+ TRUETYPE_TAG('l','g','b', 0 ), // lgb = Laghu
+ TRUETYPE_TAG('l','g','g', 0 ), // lgg = Lugbara
+ TRUETYPE_TAG('l','g','h', 0 ), // lgh = Laghuu
+ TRUETYPE_TAG('l','g','i', 0 ), // lgi = Lengilu
+ TRUETYPE_TAG('l','g','k', 0 ), // lgk = Lingarak
+ TRUETYPE_TAG('l','g','l', 0 ), // lgl = Wala
+ TRUETYPE_TAG('l','g','m', 0 ), // lgm = Lega-Mwenga
+ TRUETYPE_TAG('l','g','n', 0 ), // lgn = Opuuo
+ TRUETYPE_TAG('l','g','q', 0 ), // lgq = Logba
+ TRUETYPE_TAG('l','g','r', 0 ), // lgr = Lengo
+ TRUETYPE_TAG('l','g','t', 0 ), // lgt = Pahi
+ TRUETYPE_TAG('l','g','u', 0 ), // lgu = Longgu
+ TRUETYPE_TAG('l','g','z', 0 ), // lgz = Ligenza
+ TRUETYPE_TAG('l','h','a', 0 ), // lha = Laha (Viet Nam)
+ TRUETYPE_TAG('l','h','h', 0 ), // lhh = Laha (Indonesia)
+ TRUETYPE_TAG('l','h','i', 0 ), // lhi = Lahu Shi
+ TRUETYPE_TAG('l','h','l', 0 ), // lhl = Lahul Lohar
+ TRUETYPE_TAG('l','h','m', 0 ), // lhm = Lhomi
+ TRUETYPE_TAG('l','h','n', 0 ), // lhn = Lahanan
+ TRUETYPE_TAG('l','h','p', 0 ), // lhp = Lhokpu
+ TRUETYPE_TAG('l','h','s', 0 ), // lhs = Mlahsö
+ TRUETYPE_TAG('l','h','t', 0 ), // lht = Lo-Toga
+ TRUETYPE_TAG('l','h','u', 0 ), // lhu = Lahu
+ TRUETYPE_TAG('l','i','a', 0 ), // lia = West-Central Limba
+ TRUETYPE_TAG('l','i','b', 0 ), // lib = Likum
+ TRUETYPE_TAG('l','i','c', 0 ), // lic = Hlai
+ TRUETYPE_TAG('l','i','d', 0 ), // lid = Nyindrou
+ TRUETYPE_TAG('l','i','e', 0 ), // lie = Likila
+ TRUETYPE_TAG('l','i','f', 0 ), // lif = Limbu
+ TRUETYPE_TAG('l','i','g', 0 ), // lig = Ligbi
+ TRUETYPE_TAG('l','i','h', 0 ), // lih = Lihir
+ TRUETYPE_TAG('l','i','i', 0 ), // lii = Lingkhim
+ TRUETYPE_TAG('l','i','j', 0 ), // lij = Ligurian
+ TRUETYPE_TAG('l','i','k', 0 ), // lik = Lika
+ TRUETYPE_TAG('l','i','l', 0 ), // lil = Lillooet
+ TRUETYPE_TAG('l','i','o', 0 ), // lio = Liki
+ TRUETYPE_TAG('l','i','p', 0 ), // lip = Sekpele
+ TRUETYPE_TAG('l','i','q', 0 ), // liq = Libido
+ TRUETYPE_TAG('l','i','r', 0 ), // lir = Liberian English
+ TRUETYPE_TAG('l','i','s', 0 ), // lis = Lisu
+ TRUETYPE_TAG('l','i','u', 0 ), // liu = Logorik
+ TRUETYPE_TAG('l','i','v', 0 ), // liv = Liv
+ TRUETYPE_TAG('l','i','w', 0 ), // liw = Col
+ TRUETYPE_TAG('l','i','x', 0 ), // lix = Liabuku
+ TRUETYPE_TAG('l','i','y', 0 ), // liy = Banda-Bambari
+ TRUETYPE_TAG('l','i','z', 0 ), // liz = Libinza
+ TRUETYPE_TAG('l','j','e', 0 ), // lje = Rampi
+ TRUETYPE_TAG('l','j','i', 0 ), // lji = Laiyolo
+ TRUETYPE_TAG('l','j','l', 0 ), // ljl = Li'o
+ TRUETYPE_TAG('l','j','p', 0 ), // ljp = Lampung Api
+ TRUETYPE_TAG('l','k','a', 0 ), // lka = Lakalei
+ TRUETYPE_TAG('l','k','b', 0 ), // lkb = Kabras
+ TRUETYPE_TAG('l','k','c', 0 ), // lkc = Kucong
+ TRUETYPE_TAG('l','k','d', 0 ), // lkd = Lakondê
+ TRUETYPE_TAG('l','k','e', 0 ), // lke = Kenyi
+ TRUETYPE_TAG('l','k','h', 0 ), // lkh = Lakha
+ TRUETYPE_TAG('l','k','i', 0 ), // lki = Laki
+ TRUETYPE_TAG('l','k','j', 0 ), // lkj = Remun
+ TRUETYPE_TAG('l','k','l', 0 ), // lkl = Laeko-Libuat
+ TRUETYPE_TAG('l','k','n', 0 ), // lkn = Lakon
+ TRUETYPE_TAG('l','k','o', 0 ), // lko = Khayo
+ TRUETYPE_TAG('l','k','r', 0 ), // lkr = Päri
+ TRUETYPE_TAG('l','k','s', 0 ), // lks = Kisa
+ TRUETYPE_TAG('l','k','t', 0 ), // lkt = Lakota
+ TRUETYPE_TAG('l','k','y', 0 ), // lky = Lokoya
+ TRUETYPE_TAG('l','l','a', 0 ), // lla = Lala-Roba
+ TRUETYPE_TAG('l','l','b', 0 ), // llb = Lolo
+ TRUETYPE_TAG('l','l','c', 0 ), // llc = Lele (Guinea)
+ TRUETYPE_TAG('l','l','d', 0 ), // lld = Ladin
+ TRUETYPE_TAG('l','l','e', 0 ), // lle = Lele (Papua New Guinea)
+ TRUETYPE_TAG('l','l','f', 0 ), // llf = Hermit
+ TRUETYPE_TAG('l','l','g', 0 ), // llg = Lole
+ TRUETYPE_TAG('l','l','h', 0 ), // llh = Lamu
+ TRUETYPE_TAG('l','l','i', 0 ), // lli = Teke-Laali
+ TRUETYPE_TAG('l','l','k', 0 ), // llk = Lelak
+ TRUETYPE_TAG('l','l','l', 0 ), // lll = Lilau
+ TRUETYPE_TAG('l','l','m', 0 ), // llm = Lasalimu
+ TRUETYPE_TAG('l','l','n', 0 ), // lln = Lele (Chad)
+ TRUETYPE_TAG('l','l','o', 0 ), // llo = Khlor
+ TRUETYPE_TAG('l','l','p', 0 ), // llp = North Efate
+ TRUETYPE_TAG('l','l','q', 0 ), // llq = Lolak
+ TRUETYPE_TAG('l','l','s', 0 ), // lls = Lithuanian Sign Language
+ TRUETYPE_TAG('l','l','u', 0 ), // llu = Lau
+ TRUETYPE_TAG('l','l','x', 0 ), // llx = Lauan
+ TRUETYPE_TAG('l','m','a', 0 ), // lma = East Limba
+ TRUETYPE_TAG('l','m','b', 0 ), // lmb = Merei
+ TRUETYPE_TAG('l','m','c', 0 ), // lmc = Limilngan
+ TRUETYPE_TAG('l','m','d', 0 ), // lmd = Lumun
+ TRUETYPE_TAG('l','m','e', 0 ), // lme = Pévé
+ TRUETYPE_TAG('l','m','f', 0 ), // lmf = South Lembata
+ TRUETYPE_TAG('l','m','g', 0 ), // lmg = Lamogai
+ TRUETYPE_TAG('l','m','h', 0 ), // lmh = Lambichhong
+ TRUETYPE_TAG('l','m','i', 0 ), // lmi = Lombi
+ TRUETYPE_TAG('l','m','j', 0 ), // lmj = West Lembata
+ TRUETYPE_TAG('l','m','k', 0 ), // lmk = Lamkang
+ TRUETYPE_TAG('l','m','l', 0 ), // lml = Hano
+ TRUETYPE_TAG('l','m','m', 0 ), // lmm = Lamam
+ TRUETYPE_TAG('l','m','n', 0 ), // lmn = Lambadi
+ TRUETYPE_TAG('l','m','o', 0 ), // lmo = Lombard
+ TRUETYPE_TAG('l','m','p', 0 ), // lmp = Limbum
+ TRUETYPE_TAG('l','m','q', 0 ), // lmq = Lamatuka
+ TRUETYPE_TAG('l','m','r', 0 ), // lmr = Lamalera
+ TRUETYPE_TAG('l','m','u', 0 ), // lmu = Lamenu
+ TRUETYPE_TAG('l','m','v', 0 ), // lmv = Lomaiviti
+ TRUETYPE_TAG('l','m','w', 0 ), // lmw = Lake Miwok
+ TRUETYPE_TAG('l','m','x', 0 ), // lmx = Laimbue
+ TRUETYPE_TAG('l','m','y', 0 ), // lmy = Lamboya
+ TRUETYPE_TAG('l','m','z', 0 ), // lmz = Lumbee
+ TRUETYPE_TAG('l','n','a', 0 ), // lna = Langbashe
+ TRUETYPE_TAG('l','n','b', 0 ), // lnb = Mbalanhu
+ TRUETYPE_TAG('l','n','d', 0 ), // lnd = Lundayeh
+ TRUETYPE_TAG('l','n','g', 0 ), // lng = Langobardic
+ TRUETYPE_TAG('l','n','h', 0 ), // lnh = Lanoh
+ TRUETYPE_TAG('l','n','i', 0 ), // lni = Daantanai'
+ TRUETYPE_TAG('l','n','j', 0 ), // lnj = Leningitij
+ TRUETYPE_TAG('l','n','l', 0 ), // lnl = South Central Banda
+ TRUETYPE_TAG('l','n','m', 0 ), // lnm = Langam
+ TRUETYPE_TAG('l','n','n', 0 ), // lnn = Lorediakarkar
+ TRUETYPE_TAG('l','n','o', 0 ), // lno = Lango (Sudan)
+ TRUETYPE_TAG('l','n','s', 0 ), // lns = Lamnso'
+ TRUETYPE_TAG('l','n','u', 0 ), // lnu = Longuda
+ TRUETYPE_TAG('l','n','z', 0 ), // lnz = Lonzo
+ TRUETYPE_TAG('l','o','a', 0 ), // loa = Loloda
+ TRUETYPE_TAG('l','o','b', 0 ), // lob = Lobi
+ TRUETYPE_TAG('l','o','c', 0 ), // loc = Inonhan
+ TRUETYPE_TAG('l','o','e', 0 ), // loe = Saluan
+ TRUETYPE_TAG('l','o','f', 0 ), // lof = Logol
+ TRUETYPE_TAG('l','o','g', 0 ), // log = Logo
+ TRUETYPE_TAG('l','o','h', 0 ), // loh = Narim
+ TRUETYPE_TAG('l','o','i', 0 ), // loi = Loma (Côte d'Ivoire)
+ TRUETYPE_TAG('l','o','j', 0 ), // loj = Lou
+ TRUETYPE_TAG('l','o','k', 0 ), // lok = Loko
+ TRUETYPE_TAG('l','o','l', 0 ), // lol = Mongo
+ TRUETYPE_TAG('l','o','m', 0 ), // lom = Loma (Liberia)
+ TRUETYPE_TAG('l','o','n', 0 ), // lon = Malawi Lomwe
+ TRUETYPE_TAG('l','o','o', 0 ), // loo = Lombo
+ TRUETYPE_TAG('l','o','p', 0 ), // lop = Lopa
+ TRUETYPE_TAG('l','o','q', 0 ), // loq = Lobala
+ TRUETYPE_TAG('l','o','r', 0 ), // lor = Téén
+ TRUETYPE_TAG('l','o','s', 0 ), // los = Loniu
+ TRUETYPE_TAG('l','o','t', 0 ), // lot = Otuho
+ TRUETYPE_TAG('l','o','u', 0 ), // lou = Louisiana Creole French
+ TRUETYPE_TAG('l','o','v', 0 ), // lov = Lopi
+ TRUETYPE_TAG('l','o','w', 0 ), // low = Tampias Lobu
+ TRUETYPE_TAG('l','o','x', 0 ), // lox = Loun
+ TRUETYPE_TAG('l','o','y', 0 ), // loy = Lowa
+ TRUETYPE_TAG('l','o','z', 0 ), // loz = Lozi
+ TRUETYPE_TAG('l','p','a', 0 ), // lpa = Lelepa
+ TRUETYPE_TAG('l','p','e', 0 ), // lpe = Lepki
+ TRUETYPE_TAG('l','p','n', 0 ), // lpn = Long Phuri Naga
+ TRUETYPE_TAG('l','p','o', 0 ), // lpo = Lipo
+ TRUETYPE_TAG('l','p','x', 0 ), // lpx = Lopit
+ TRUETYPE_TAG('l','r','a', 0 ), // lra = Rara Bakati'
+ TRUETYPE_TAG('l','r','c', 0 ), // lrc = Northern Luri
+ TRUETYPE_TAG('l','r','e', 0 ), // lre = Laurentian
+ TRUETYPE_TAG('l','r','g', 0 ), // lrg = Laragia
+ TRUETYPE_TAG('l','r','i', 0 ), // lri = Marachi
+ TRUETYPE_TAG('l','r','k', 0 ), // lrk = Loarki
+ TRUETYPE_TAG('l','r','l', 0 ), // lrl = Lari
+ TRUETYPE_TAG('l','r','m', 0 ), // lrm = Marama
+ TRUETYPE_TAG('l','r','n', 0 ), // lrn = Lorang
+ TRUETYPE_TAG('l','r','o', 0 ), // lro = Laro
+ TRUETYPE_TAG('l','r','r', 0 ), // lrr = Southern Lorung
+ TRUETYPE_TAG('l','r','t', 0 ), // lrt = Larantuka Malay
+ TRUETYPE_TAG('l','r','v', 0 ), // lrv = Larevat
+ TRUETYPE_TAG('l','r','z', 0 ), // lrz = Lemerig
+ TRUETYPE_TAG('l','s','a', 0 ), // lsa = Lasgerdi
+ TRUETYPE_TAG('l','s','d', 0 ), // lsd = Lishana Deni
+ TRUETYPE_TAG('l','s','e', 0 ), // lse = Lusengo
+ TRUETYPE_TAG('l','s','g', 0 ), // lsg = Lyons Sign Language
+ TRUETYPE_TAG('l','s','h', 0 ), // lsh = Lish
+ TRUETYPE_TAG('l','s','i', 0 ), // lsi = Lashi
+ TRUETYPE_TAG('l','s','l', 0 ), // lsl = Latvian Sign Language
+ TRUETYPE_TAG('l','s','m', 0 ), // lsm = Saamia
+ TRUETYPE_TAG('l','s','o', 0 ), // lso = Laos Sign Language
+ TRUETYPE_TAG('l','s','p', 0 ), // lsp = Panamanian Sign Language
+ TRUETYPE_TAG('l','s','r', 0 ), // lsr = Aruop
+ TRUETYPE_TAG('l','s','s', 0 ), // lss = Lasi
+ TRUETYPE_TAG('l','s','t', 0 ), // lst = Trinidad and Tobago Sign Language
+ TRUETYPE_TAG('l','s','y', 0 ), // lsy = Mauritian Sign Language
+ TRUETYPE_TAG('l','t','c', 0 ), // ltc = Late Middle Chinese
+ TRUETYPE_TAG('l','t','g', 0 ), // ltg = Latgalian
+ TRUETYPE_TAG('l','t','i', 0 ), // lti = Leti (Indonesia)
+ TRUETYPE_TAG('l','t','n', 0 ), // ltn = Latundê
+ TRUETYPE_TAG('l','t','o', 0 ), // lto = Tsotso
+ TRUETYPE_TAG('l','t','s', 0 ), // lts = Tachoni
+ TRUETYPE_TAG('l','t','u', 0 ), // ltu = Latu
+ TRUETYPE_TAG('l','u','a', 0 ), // lua = Luba-Lulua
+ TRUETYPE_TAG('l','u','c', 0 ), // luc = Aringa
+ TRUETYPE_TAG('l','u','d', 0 ), // lud = Ludian
+ TRUETYPE_TAG('l','u','e', 0 ), // lue = Luvale
+ TRUETYPE_TAG('l','u','f', 0 ), // luf = Laua
+ TRUETYPE_TAG('l','u','i', 0 ), // lui = Luiseno
+ TRUETYPE_TAG('l','u','j', 0 ), // luj = Luna
+ TRUETYPE_TAG('l','u','k', 0 ), // luk = Lunanakha
+ TRUETYPE_TAG('l','u','l', 0 ), // lul = Olu'bo
+ TRUETYPE_TAG('l','u','m', 0 ), // lum = Luimbi
+ TRUETYPE_TAG('l','u','n', 0 ), // lun = Lunda
+ TRUETYPE_TAG('l','u','o', 0 ), // luo = Luo (Kenya and Tanzania)
+ TRUETYPE_TAG('l','u','p', 0 ), // lup = Lumbu
+ TRUETYPE_TAG('l','u','q', 0 ), // luq = Lucumi
+ TRUETYPE_TAG('l','u','r', 0 ), // lur = Laura
+ TRUETYPE_TAG('l','u','s', 0 ), // lus = Lushai
+ TRUETYPE_TAG('l','u','t', 0 ), // lut = Lushootseed
+ TRUETYPE_TAG('l','u','u', 0 ), // luu = Lumba-Yakkha
+ TRUETYPE_TAG('l','u','v', 0 ), // luv = Luwati
+ TRUETYPE_TAG('l','u','w', 0 ), // luw = Luo (Cameroon)
+ TRUETYPE_TAG('l','u','y', 0 ), // luy = Luyia
+ TRUETYPE_TAG('l','u','z', 0 ), // luz = Southern Luri
+ TRUETYPE_TAG('l','v','a', 0 ), // lva = Maku'a
+ TRUETYPE_TAG('l','v','k', 0 ), // lvk = Lavukaleve
+ TRUETYPE_TAG('l','v','s', 0 ), // lvs = Standard Latvian
+ TRUETYPE_TAG('l','v','u', 0 ), // lvu = Levuka
+ TRUETYPE_TAG('l','w','a', 0 ), // lwa = Lwalu
+ TRUETYPE_TAG('l','w','e', 0 ), // lwe = Lewo Eleng
+ TRUETYPE_TAG('l','w','g', 0 ), // lwg = Wanga
+ TRUETYPE_TAG('l','w','h', 0 ), // lwh = White Lachi
+ TRUETYPE_TAG('l','w','l', 0 ), // lwl = Eastern Lawa
+ TRUETYPE_TAG('l','w','m', 0 ), // lwm = Laomian
+ TRUETYPE_TAG('l','w','o', 0 ), // lwo = Luwo
+ TRUETYPE_TAG('l','w','t', 0 ), // lwt = Lewotobi
+ TRUETYPE_TAG('l','w','w', 0 ), // lww = Lewo
+ TRUETYPE_TAG('l','y','a', 0 ), // lya = Layakha
+ TRUETYPE_TAG('l','y','g', 0 ), // lyg = Lyngngam
+ TRUETYPE_TAG('l','y','n', 0 ), // lyn = Luyana
+ TRUETYPE_TAG('l','z','h', 0 ), // lzh = Literary Chinese
+ TRUETYPE_TAG('l','z','l', 0 ), // lzl = Litzlitz
+ TRUETYPE_TAG('l','z','n', 0 ), // lzn = Leinong Naga
+ TRUETYPE_TAG('l','z','z', 0 ), // lzz = Laz
+ TRUETYPE_TAG('m','a','a', 0 ), // maa = San Jerónimo Tecóatl Mazatec
+ TRUETYPE_TAG('m','a','b', 0 ), // mab = Yutanduchi Mixtec
+ TRUETYPE_TAG('m','a','d', 0 ), // mad = Madurese
+ TRUETYPE_TAG('m','a','e', 0 ), // mae = Bo-Rukul
+ TRUETYPE_TAG('m','a','f', 0 ), // maf = Mafa
+ TRUETYPE_TAG('m','a','g', 0 ), // mag = Magahi
+ TRUETYPE_TAG('m','a','i', 0 ), // mai = Maithili
+ TRUETYPE_TAG('m','a','j', 0 ), // maj = Jalapa De Díaz Mazatec
+ TRUETYPE_TAG('m','a','k', 0 ), // mak = Makasar
+ TRUETYPE_TAG('m','a','m', 0 ), // mam = Mam
+ TRUETYPE_TAG('m','a','n', 0 ), // man = Mandingo
+ TRUETYPE_TAG('m','a','p', 0 ), // map = Austronesian languages
+ TRUETYPE_TAG('m','a','q', 0 ), // maq = Chiquihuitlán Mazatec
+ TRUETYPE_TAG('m','a','s', 0 ), // mas = Masai
+ TRUETYPE_TAG('m','a','t', 0 ), // mat = San Francisco Matlatzinca
+ TRUETYPE_TAG('m','a','u', 0 ), // mau = Huautla Mazatec
+ TRUETYPE_TAG('m','a','v', 0 ), // mav = Sateré-Mawé
+ TRUETYPE_TAG('m','a','w', 0 ), // maw = Mampruli
+ TRUETYPE_TAG('m','a','x', 0 ), // max = North Moluccan Malay
+ TRUETYPE_TAG('m','a','z', 0 ), // maz = Central Mazahua
+ TRUETYPE_TAG('m','b','a', 0 ), // mba = Higaonon
+ TRUETYPE_TAG('m','b','b', 0 ), // mbb = Western Bukidnon Manobo
+ TRUETYPE_TAG('m','b','c', 0 ), // mbc = Macushi
+ TRUETYPE_TAG('m','b','d', 0 ), // mbd = Dibabawon Manobo
+ TRUETYPE_TAG('m','b','e', 0 ), // mbe = Molale
+ TRUETYPE_TAG('m','b','f', 0 ), // mbf = Baba Malay
+ TRUETYPE_TAG('m','b','h', 0 ), // mbh = Mangseng
+ TRUETYPE_TAG('m','b','i', 0 ), // mbi = Ilianen Manobo
+ TRUETYPE_TAG('m','b','j', 0 ), // mbj = Nadëb
+ TRUETYPE_TAG('m','b','k', 0 ), // mbk = Malol
+ TRUETYPE_TAG('m','b','l', 0 ), // mbl = Maxakalí
+ TRUETYPE_TAG('m','b','m', 0 ), // mbm = Ombamba
+ TRUETYPE_TAG('m','b','n', 0 ), // mbn = Macaguán
+ TRUETYPE_TAG('m','b','o', 0 ), // mbo = Mbo (Cameroon)
+ TRUETYPE_TAG('m','b','p', 0 ), // mbp = Malayo
+ TRUETYPE_TAG('m','b','q', 0 ), // mbq = Maisin
+ TRUETYPE_TAG('m','b','r', 0 ), // mbr = Nukak Makú
+ TRUETYPE_TAG('m','b','s', 0 ), // mbs = Sarangani Manobo
+ TRUETYPE_TAG('m','b','t', 0 ), // mbt = Matigsalug Manobo
+ TRUETYPE_TAG('m','b','u', 0 ), // mbu = Mbula-Bwazza
+ TRUETYPE_TAG('m','b','v', 0 ), // mbv = Mbulungish
+ TRUETYPE_TAG('m','b','w', 0 ), // mbw = Maring
+ TRUETYPE_TAG('m','b','x', 0 ), // mbx = Mari (East Sepik Province)
+ TRUETYPE_TAG('m','b','y', 0 ), // mby = Memoni
+ TRUETYPE_TAG('m','b','z', 0 ), // mbz = Amoltepec Mixtec
+ TRUETYPE_TAG('m','c','a', 0 ), // mca = Maca
+ TRUETYPE_TAG('m','c','b', 0 ), // mcb = Machiguenga
+ TRUETYPE_TAG('m','c','c', 0 ), // mcc = Bitur
+ TRUETYPE_TAG('m','c','d', 0 ), // mcd = Sharanahua
+ TRUETYPE_TAG('m','c','e', 0 ), // mce = Itundujia Mixtec
+ TRUETYPE_TAG('m','c','f', 0 ), // mcf = Matsés
+ TRUETYPE_TAG('m','c','g', 0 ), // mcg = Mapoyo
+ TRUETYPE_TAG('m','c','h', 0 ), // mch = Maquiritari
+ TRUETYPE_TAG('m','c','i', 0 ), // mci = Mese
+ TRUETYPE_TAG('m','c','j', 0 ), // mcj = Mvanip
+ TRUETYPE_TAG('m','c','k', 0 ), // mck = Mbunda
+ TRUETYPE_TAG('m','c','l', 0 ), // mcl = Macaguaje
+ TRUETYPE_TAG('m','c','m', 0 ), // mcm = Malaccan Creole Portuguese
+ TRUETYPE_TAG('m','c','n', 0 ), // mcn = Masana
+ TRUETYPE_TAG('m','c','o', 0 ), // mco = Coatlán Mixe
+ TRUETYPE_TAG('m','c','p', 0 ), // mcp = Makaa
+ TRUETYPE_TAG('m','c','q', 0 ), // mcq = Ese
+ TRUETYPE_TAG('m','c','r', 0 ), // mcr = Menya
+ TRUETYPE_TAG('m','c','s', 0 ), // mcs = Mambai
+ TRUETYPE_TAG('m','c','t', 0 ), // mct = Mengisa
+ TRUETYPE_TAG('m','c','u', 0 ), // mcu = Cameroon Mambila
+ TRUETYPE_TAG('m','c','v', 0 ), // mcv = Minanibai
+ TRUETYPE_TAG('m','c','w', 0 ), // mcw = Mawa (Chad)
+ TRUETYPE_TAG('m','c','x', 0 ), // mcx = Mpiemo
+ TRUETYPE_TAG('m','c','y', 0 ), // mcy = South Watut
+ TRUETYPE_TAG('m','c','z', 0 ), // mcz = Mawan
+ TRUETYPE_TAG('m','d','a', 0 ), // mda = Mada (Nigeria)
+ TRUETYPE_TAG('m','d','b', 0 ), // mdb = Morigi
+ TRUETYPE_TAG('m','d','c', 0 ), // mdc = Male (Papua New Guinea)
+ TRUETYPE_TAG('m','d','d', 0 ), // mdd = Mbum
+ TRUETYPE_TAG('m','d','e', 0 ), // mde = Maba (Chad)
+ TRUETYPE_TAG('m','d','f', 0 ), // mdf = Moksha
+ TRUETYPE_TAG('m','d','g', 0 ), // mdg = Massalat
+ TRUETYPE_TAG('m','d','h', 0 ), // mdh = Maguindanaon
+ TRUETYPE_TAG('m','d','i', 0 ), // mdi = Mamvu
+ TRUETYPE_TAG('m','d','j', 0 ), // mdj = Mangbetu
+ TRUETYPE_TAG('m','d','k', 0 ), // mdk = Mangbutu
+ TRUETYPE_TAG('m','d','l', 0 ), // mdl = Maltese Sign Language
+ TRUETYPE_TAG('m','d','m', 0 ), // mdm = Mayogo
+ TRUETYPE_TAG('m','d','n', 0 ), // mdn = Mbati
+ TRUETYPE_TAG('m','d','p', 0 ), // mdp = Mbala
+ TRUETYPE_TAG('m','d','q', 0 ), // mdq = Mbole
+ TRUETYPE_TAG('m','d','r', 0 ), // mdr = Mandar
+ TRUETYPE_TAG('m','d','s', 0 ), // mds = Maria (Papua New Guinea)
+ TRUETYPE_TAG('m','d','t', 0 ), // mdt = Mbere
+ TRUETYPE_TAG('m','d','u', 0 ), // mdu = Mboko
+ TRUETYPE_TAG('m','d','v', 0 ), // mdv = Santa Lucía Monteverde Mixtec
+ TRUETYPE_TAG('m','d','w', 0 ), // mdw = Mbosi
+ TRUETYPE_TAG('m','d','x', 0 ), // mdx = Dizin
+ TRUETYPE_TAG('m','d','y', 0 ), // mdy = Male (Ethiopia)
+ TRUETYPE_TAG('m','d','z', 0 ), // mdz = Suruí Do Pará
+ TRUETYPE_TAG('m','e','a', 0 ), // mea = Menka
+ TRUETYPE_TAG('m','e','b', 0 ), // meb = Ikobi-Mena
+ TRUETYPE_TAG('m','e','c', 0 ), // mec = Mara
+ TRUETYPE_TAG('m','e','d', 0 ), // med = Melpa
+ TRUETYPE_TAG('m','e','e', 0 ), // mee = Mengen
+ TRUETYPE_TAG('m','e','f', 0 ), // mef = Megam
+ TRUETYPE_TAG('m','e','g', 0 ), // meg = Mea
+ TRUETYPE_TAG('m','e','h', 0 ), // meh = Southwestern Tlaxiaco Mixtec
+ TRUETYPE_TAG('m','e','i', 0 ), // mei = Midob
+ TRUETYPE_TAG('m','e','j', 0 ), // mej = Meyah
+ TRUETYPE_TAG('m','e','k', 0 ), // mek = Mekeo
+ TRUETYPE_TAG('m','e','l', 0 ), // mel = Central Melanau
+ TRUETYPE_TAG('m','e','m', 0 ), // mem = Mangala
+ TRUETYPE_TAG('m','e','n', 0 ), // men = Mende (Sierra Leone)
+ TRUETYPE_TAG('m','e','o', 0 ), // meo = Kedah Malay
+ TRUETYPE_TAG('m','e','p', 0 ), // mep = Miriwung
+ TRUETYPE_TAG('m','e','q', 0 ), // meq = Merey
+ TRUETYPE_TAG('m','e','r', 0 ), // mer = Meru
+ TRUETYPE_TAG('m','e','s', 0 ), // mes = Masmaje
+ TRUETYPE_TAG('m','e','t', 0 ), // met = Mato
+ TRUETYPE_TAG('m','e','u', 0 ), // meu = Motu
+ TRUETYPE_TAG('m','e','v', 0 ), // mev = Mann
+ TRUETYPE_TAG('m','e','w', 0 ), // mew = Maaka
+ TRUETYPE_TAG('m','e','y', 0 ), // mey = Hassaniyya
+ TRUETYPE_TAG('m','e','z', 0 ), // mez = Menominee
+ TRUETYPE_TAG('m','f','a', 0 ), // mfa = Pattani Malay
+ TRUETYPE_TAG('m','f','b', 0 ), // mfb = Bangka
+ TRUETYPE_TAG('m','f','c', 0 ), // mfc = Mba
+ TRUETYPE_TAG('m','f','d', 0 ), // mfd = Mendankwe-Nkwen
+ TRUETYPE_TAG('m','f','e', 0 ), // mfe = Morisyen
+ TRUETYPE_TAG('m','f','f', 0 ), // mff = Naki
+ TRUETYPE_TAG('m','f','g', 0 ), // mfg = Mixifore
+ TRUETYPE_TAG('m','f','h', 0 ), // mfh = Matal
+ TRUETYPE_TAG('m','f','i', 0 ), // mfi = Wandala
+ TRUETYPE_TAG('m','f','j', 0 ), // mfj = Mefele
+ TRUETYPE_TAG('m','f','k', 0 ), // mfk = North Mofu
+ TRUETYPE_TAG('m','f','l', 0 ), // mfl = Putai
+ TRUETYPE_TAG('m','f','m', 0 ), // mfm = Marghi South
+ TRUETYPE_TAG('m','f','n', 0 ), // mfn = Cross River Mbembe
+ TRUETYPE_TAG('m','f','o', 0 ), // mfo = Mbe
+ TRUETYPE_TAG('m','f','p', 0 ), // mfp = Makassar Malay
+ TRUETYPE_TAG('m','f','q', 0 ), // mfq = Moba
+ TRUETYPE_TAG('m','f','r', 0 ), // mfr = Marithiel
+ TRUETYPE_TAG('m','f','s', 0 ), // mfs = Mexican Sign Language
+ TRUETYPE_TAG('m','f','t', 0 ), // mft = Mokerang
+ TRUETYPE_TAG('m','f','u', 0 ), // mfu = Mbwela
+ TRUETYPE_TAG('m','f','v', 0 ), // mfv = Mandjak
+ TRUETYPE_TAG('m','f','w', 0 ), // mfw = Mulaha
+ TRUETYPE_TAG('m','f','x', 0 ), // mfx = Melo
+ TRUETYPE_TAG('m','f','y', 0 ), // mfy = Mayo
+ TRUETYPE_TAG('m','f','z', 0 ), // mfz = Mabaan
+ TRUETYPE_TAG('m','g','a', 0 ), // mga = Middle Irish (900-1200)
+ TRUETYPE_TAG('m','g','b', 0 ), // mgb = Mararit
+ TRUETYPE_TAG('m','g','c', 0 ), // mgc = Morokodo
+ TRUETYPE_TAG('m','g','d', 0 ), // mgd = Moru
+ TRUETYPE_TAG('m','g','e', 0 ), // mge = Mango
+ TRUETYPE_TAG('m','g','f', 0 ), // mgf = Maklew
+ TRUETYPE_TAG('m','g','g', 0 ), // mgg = Mpongmpong
+ TRUETYPE_TAG('m','g','h', 0 ), // mgh = Makhuwa-Meetto
+ TRUETYPE_TAG('m','g','i', 0 ), // mgi = Lijili
+ TRUETYPE_TAG('m','g','j', 0 ), // mgj = Abureni
+ TRUETYPE_TAG('m','g','k', 0 ), // mgk = Mawes
+ TRUETYPE_TAG('m','g','l', 0 ), // mgl = Maleu-Kilenge
+ TRUETYPE_TAG('m','g','m', 0 ), // mgm = Mambae
+ TRUETYPE_TAG('m','g','n', 0 ), // mgn = Mbangi
+ TRUETYPE_TAG('m','g','o', 0 ), // mgo = Meta'
+ TRUETYPE_TAG('m','g','p', 0 ), // mgp = Eastern Magar
+ TRUETYPE_TAG('m','g','q', 0 ), // mgq = Malila
+ TRUETYPE_TAG('m','g','r', 0 ), // mgr = Mambwe-Lungu
+ TRUETYPE_TAG('m','g','s', 0 ), // mgs = Manda (Tanzania)
+ TRUETYPE_TAG('m','g','t', 0 ), // mgt = Mongol
+ TRUETYPE_TAG('m','g','u', 0 ), // mgu = Mailu
+ TRUETYPE_TAG('m','g','v', 0 ), // mgv = Matengo
+ TRUETYPE_TAG('m','g','w', 0 ), // mgw = Matumbi
+ TRUETYPE_TAG('m','g','x', 0 ), // mgx = Omati
+ TRUETYPE_TAG('m','g','y', 0 ), // mgy = Mbunga
+ TRUETYPE_TAG('m','g','z', 0 ), // mgz = Mbugwe
+ TRUETYPE_TAG('m','h','a', 0 ), // mha = Manda (India)
+ TRUETYPE_TAG('m','h','b', 0 ), // mhb = Mahongwe
+ TRUETYPE_TAG('m','h','c', 0 ), // mhc = Mocho
+ TRUETYPE_TAG('m','h','d', 0 ), // mhd = Mbugu
+ TRUETYPE_TAG('m','h','e', 0 ), // mhe = Besisi
+ TRUETYPE_TAG('m','h','f', 0 ), // mhf = Mamaa
+ TRUETYPE_TAG('m','h','g', 0 ), // mhg = Margu
+ TRUETYPE_TAG('m','h','h', 0 ), // mhh = Maskoy Pidgin
+ TRUETYPE_TAG('m','h','i', 0 ), // mhi = Ma'di
+ TRUETYPE_TAG('m','h','j', 0 ), // mhj = Mogholi
+ TRUETYPE_TAG('m','h','k', 0 ), // mhk = Mungaka
+ TRUETYPE_TAG('m','h','l', 0 ), // mhl = Mauwake
+ TRUETYPE_TAG('m','h','m', 0 ), // mhm = Makhuwa-Moniga
+ TRUETYPE_TAG('m','h','n', 0 ), // mhn = Mócheno
+ TRUETYPE_TAG('m','h','o', 0 ), // mho = Mashi (Zambia)
+ TRUETYPE_TAG('m','h','p', 0 ), // mhp = Balinese Malay
+ TRUETYPE_TAG('m','h','q', 0 ), // mhq = Mandan
+ TRUETYPE_TAG('m','h','r', 0 ), // mhr = Eastern Mari
+ TRUETYPE_TAG('m','h','s', 0 ), // mhs = Buru (Indonesia)
+ TRUETYPE_TAG('m','h','t', 0 ), // mht = Mandahuaca
+ TRUETYPE_TAG('m','h','u', 0 ), // mhu = Digaro-Mishmi
+ TRUETYPE_TAG('m','h','w', 0 ), // mhw = Mbukushu
+ TRUETYPE_TAG('m','h','x', 0 ), // mhx = Maru
+ TRUETYPE_TAG('m','h','y', 0 ), // mhy = Ma'anyan
+ TRUETYPE_TAG('m','h','z', 0 ), // mhz = Mor (Mor Islands)
+ TRUETYPE_TAG('m','i','a', 0 ), // mia = Miami
+ TRUETYPE_TAG('m','i','b', 0 ), // mib = Atatláhuca Mixtec
+ TRUETYPE_TAG('m','i','c', 0 ), // mic = Mi'kmaq
+ TRUETYPE_TAG('m','i','d', 0 ), // mid = Mandaic
+ TRUETYPE_TAG('m','i','e', 0 ), // mie = Ocotepec Mixtec
+ TRUETYPE_TAG('m','i','f', 0 ), // mif = Mofu-Gudur
+ TRUETYPE_TAG('m','i','g', 0 ), // mig = San Miguel El Grande Mixtec
+ TRUETYPE_TAG('m','i','h', 0 ), // mih = Chayuco Mixtec
+ TRUETYPE_TAG('m','i','i', 0 ), // mii = Chigmecatitlán Mixtec
+ TRUETYPE_TAG('m','i','j', 0 ), // mij = Abar
+ TRUETYPE_TAG('m','i','k', 0 ), // mik = Mikasuki
+ TRUETYPE_TAG('m','i','l', 0 ), // mil = Peñoles Mixtec
+ TRUETYPE_TAG('m','i','m', 0 ), // mim = Alacatlatzala Mixtec
+ TRUETYPE_TAG('m','i','n', 0 ), // min = Minangkabau
+ TRUETYPE_TAG('m','i','o', 0 ), // mio = Pinotepa Nacional Mixtec
+ TRUETYPE_TAG('m','i','p', 0 ), // mip = Apasco-Apoala Mixtec
+ TRUETYPE_TAG('m','i','q', 0 ), // miq = Mískito
+ TRUETYPE_TAG('m','i','r', 0 ), // mir = Isthmus Mixe
+ TRUETYPE_TAG('m','i','s', 0 ), // mis = Uncoded languages
+ TRUETYPE_TAG('m','i','t', 0 ), // mit = Southern Puebla Mixtec
+ TRUETYPE_TAG('m','i','u', 0 ), // miu = Cacaloxtepec Mixtec
+ TRUETYPE_TAG('m','i','w', 0 ), // miw = Akoye
+ TRUETYPE_TAG('m','i','x', 0 ), // mix = Mixtepec Mixtec
+ TRUETYPE_TAG('m','i','y', 0 ), // miy = Ayutla Mixtec
+ TRUETYPE_TAG('m','i','z', 0 ), // miz = Coatzospan Mixtec
+ TRUETYPE_TAG('m','j','a', 0 ), // mja = Mahei
+ TRUETYPE_TAG('m','j','c', 0 ), // mjc = San Juan Colorado Mixtec
+ TRUETYPE_TAG('m','j','d', 0 ), // mjd = Northwest Maidu
+ TRUETYPE_TAG('m','j','e', 0 ), // mje = Muskum
+ TRUETYPE_TAG('m','j','g', 0 ), // mjg = Tu
+ TRUETYPE_TAG('m','j','h', 0 ), // mjh = Mwera (Nyasa)
+ TRUETYPE_TAG('m','j','i', 0 ), // mji = Kim Mun
+ TRUETYPE_TAG('m','j','j', 0 ), // mjj = Mawak
+ TRUETYPE_TAG('m','j','k', 0 ), // mjk = Matukar
+ TRUETYPE_TAG('m','j','l', 0 ), // mjl = Mandeali
+ TRUETYPE_TAG('m','j','m', 0 ), // mjm = Medebur
+ TRUETYPE_TAG('m','j','n', 0 ), // mjn = Ma (Papua New Guinea)
+ TRUETYPE_TAG('m','j','o', 0 ), // mjo = Malankuravan
+ TRUETYPE_TAG('m','j','p', 0 ), // mjp = Malapandaram
+ TRUETYPE_TAG('m','j','q', 0 ), // mjq = Malaryan
+ TRUETYPE_TAG('m','j','r', 0 ), // mjr = Malavedan
+ TRUETYPE_TAG('m','j','s', 0 ), // mjs = Miship
+ TRUETYPE_TAG('m','j','t', 0 ), // mjt = Sauria Paharia
+ TRUETYPE_TAG('m','j','u', 0 ), // mju = Manna-Dora
+ TRUETYPE_TAG('m','j','v', 0 ), // mjv = Mannan
+ TRUETYPE_TAG('m','j','w', 0 ), // mjw = Karbi
+ TRUETYPE_TAG('m','j','x', 0 ), // mjx = Mahali
+ TRUETYPE_TAG('m','j','y', 0 ), // mjy = Mahican
+ TRUETYPE_TAG('m','j','z', 0 ), // mjz = Majhi
+ TRUETYPE_TAG('m','k','a', 0 ), // mka = Mbre
+ TRUETYPE_TAG('m','k','b', 0 ), // mkb = Mal Paharia
+ TRUETYPE_TAG('m','k','c', 0 ), // mkc = Siliput
+ TRUETYPE_TAG('m','k','e', 0 ), // mke = Mawchi
+ TRUETYPE_TAG('m','k','f', 0 ), // mkf = Miya
+ TRUETYPE_TAG('m','k','g', 0 ), // mkg = Mak (China)
+ TRUETYPE_TAG('m','k','h', 0 ), // mkh = Mon-Khmer languages
+ TRUETYPE_TAG('m','k','i', 0 ), // mki = Dhatki
+ TRUETYPE_TAG('m','k','j', 0 ), // mkj = Mokilese
+ TRUETYPE_TAG('m','k','k', 0 ), // mkk = Byep
+ TRUETYPE_TAG('m','k','l', 0 ), // mkl = Mokole
+ TRUETYPE_TAG('m','k','m', 0 ), // mkm = Moklen
+ TRUETYPE_TAG('m','k','n', 0 ), // mkn = Kupang Malay
+ TRUETYPE_TAG('m','k','o', 0 ), // mko = Mingang Doso
+ TRUETYPE_TAG('m','k','p', 0 ), // mkp = Moikodi
+ TRUETYPE_TAG('m','k','q', 0 ), // mkq = Bay Miwok
+ TRUETYPE_TAG('m','k','r', 0 ), // mkr = Malas
+ TRUETYPE_TAG('m','k','s', 0 ), // mks = Silacayoapan Mixtec
+ TRUETYPE_TAG('m','k','t', 0 ), // mkt = Vamale
+ TRUETYPE_TAG('m','k','u', 0 ), // mku = Konyanka Maninka
+ TRUETYPE_TAG('m','k','v', 0 ), // mkv = Mafea
+ TRUETYPE_TAG('m','k','w', 0 ), // mkw = Kituba (Congo)
+ TRUETYPE_TAG('m','k','x', 0 ), // mkx = Kinamiging Manobo
+ TRUETYPE_TAG('m','k','y', 0 ), // mky = East Makian
+ TRUETYPE_TAG('m','k','z', 0 ), // mkz = Makasae
+ TRUETYPE_TAG('m','l','a', 0 ), // mla = Malo
+ TRUETYPE_TAG('m','l','b', 0 ), // mlb = Mbule
+ TRUETYPE_TAG('m','l','c', 0 ), // mlc = Cao Lan
+ TRUETYPE_TAG('m','l','d', 0 ), // mld = Malakhel
+ TRUETYPE_TAG('m','l','e', 0 ), // mle = Manambu
+ TRUETYPE_TAG('m','l','f', 0 ), // mlf = Mal
+ TRUETYPE_TAG('m','l','h', 0 ), // mlh = Mape
+ TRUETYPE_TAG('m','l','i', 0 ), // mli = Malimpung
+ TRUETYPE_TAG('m','l','j', 0 ), // mlj = Miltu
+ TRUETYPE_TAG('m','l','k', 0 ), // mlk = Ilwana
+ TRUETYPE_TAG('m','l','l', 0 ), // mll = Malua Bay
+ TRUETYPE_TAG('m','l','m', 0 ), // mlm = Mulam
+ TRUETYPE_TAG('m','l','n', 0 ), // mln = Malango
+ TRUETYPE_TAG('m','l','o', 0 ), // mlo = Mlomp
+ TRUETYPE_TAG('m','l','p', 0 ), // mlp = Bargam
+ TRUETYPE_TAG('m','l','q', 0 ), // mlq = Western Maninkakan
+ TRUETYPE_TAG('m','l','r', 0 ), // mlr = Vame
+ TRUETYPE_TAG('m','l','s', 0 ), // mls = Masalit
+ TRUETYPE_TAG('m','l','u', 0 ), // mlu = To'abaita
+ TRUETYPE_TAG('m','l','v', 0 ), // mlv = Motlav
+ TRUETYPE_TAG('m','l','w', 0 ), // mlw = Moloko
+ TRUETYPE_TAG('m','l','x', 0 ), // mlx = Malfaxal
+ TRUETYPE_TAG('m','l','z', 0 ), // mlz = Malaynon
+ TRUETYPE_TAG('m','m','a', 0 ), // mma = Mama
+ TRUETYPE_TAG('m','m','b', 0 ), // mmb = Momina
+ TRUETYPE_TAG('m','m','c', 0 ), // mmc = Michoacán Mazahua
+ TRUETYPE_TAG('m','m','d', 0 ), // mmd = Maonan
+ TRUETYPE_TAG('m','m','e', 0 ), // mme = Mae
+ TRUETYPE_TAG('m','m','f', 0 ), // mmf = Mundat
+ TRUETYPE_TAG('m','m','g', 0 ), // mmg = North Ambrym
+ TRUETYPE_TAG('m','m','h', 0 ), // mmh = Mehináku
+ TRUETYPE_TAG('m','m','i', 0 ), // mmi = Musar
+ TRUETYPE_TAG('m','m','j', 0 ), // mmj = Majhwar
+ TRUETYPE_TAG('m','m','k', 0 ), // mmk = Mukha-Dora
+ TRUETYPE_TAG('m','m','l', 0 ), // mml = Man Met
+ TRUETYPE_TAG('m','m','m', 0 ), // mmm = Maii
+ TRUETYPE_TAG('m','m','n', 0 ), // mmn = Mamanwa
+ TRUETYPE_TAG('m','m','o', 0 ), // mmo = Mangga Buang
+ TRUETYPE_TAG('m','m','p', 0 ), // mmp = Siawi
+ TRUETYPE_TAG('m','m','q', 0 ), // mmq = Musak
+ TRUETYPE_TAG('m','m','r', 0 ), // mmr = Western Xiangxi Miao
+ TRUETYPE_TAG('m','m','t', 0 ), // mmt = Malalamai
+ TRUETYPE_TAG('m','m','u', 0 ), // mmu = Mmaala
+ TRUETYPE_TAG('m','m','v', 0 ), // mmv = Miriti
+ TRUETYPE_TAG('m','m','w', 0 ), // mmw = Emae
+ TRUETYPE_TAG('m','m','x', 0 ), // mmx = Madak
+ TRUETYPE_TAG('m','m','y', 0 ), // mmy = Migaama
+ TRUETYPE_TAG('m','m','z', 0 ), // mmz = Mabaale
+ TRUETYPE_TAG('m','n','a', 0 ), // mna = Mbula
+ TRUETYPE_TAG('m','n','b', 0 ), // mnb = Muna
+ TRUETYPE_TAG('m','n','c', 0 ), // mnc = Manchu
+ TRUETYPE_TAG('m','n','d', 0 ), // mnd = Mondé
+ TRUETYPE_TAG('m','n','e', 0 ), // mne = Naba
+ TRUETYPE_TAG('m','n','f', 0 ), // mnf = Mundani
+ TRUETYPE_TAG('m','n','g', 0 ), // mng = Eastern Mnong
+ TRUETYPE_TAG('m','n','h', 0 ), // mnh = Mono (Democratic Republic of Congo)
+ TRUETYPE_TAG('m','n','i', 0 ), // mni = Manipuri
+ TRUETYPE_TAG('m','n','j', 0 ), // mnj = Munji
+ TRUETYPE_TAG('m','n','k', 0 ), // mnk = Mandinka
+ TRUETYPE_TAG('m','n','l', 0 ), // mnl = Tiale
+ TRUETYPE_TAG('m','n','m', 0 ), // mnm = Mapena
+ TRUETYPE_TAG('m','n','n', 0 ), // mnn = Southern Mnong
+ TRUETYPE_TAG('m','n','o', 0 ), // mno = Manobo languages
+ TRUETYPE_TAG('m','n','p', 0 ), // mnp = Min Bei Chinese
+ TRUETYPE_TAG('m','n','q', 0 ), // mnq = Minriq
+ TRUETYPE_TAG('m','n','r', 0 ), // mnr = Mono (USA)
+ TRUETYPE_TAG('m','n','s', 0 ), // mns = Mansi
+ TRUETYPE_TAG('m','n','t', 0 ), // mnt = Maykulan
+ TRUETYPE_TAG('m','n','u', 0 ), // mnu = Mer
+ TRUETYPE_TAG('m','n','v', 0 ), // mnv = Rennell-Bellona
+ TRUETYPE_TAG('m','n','w', 0 ), // mnw = Mon
+ TRUETYPE_TAG('m','n','x', 0 ), // mnx = Manikion
+ TRUETYPE_TAG('m','n','y', 0 ), // mny = Manyawa
+ TRUETYPE_TAG('m','n','z', 0 ), // mnz = Moni
+ TRUETYPE_TAG('m','o','a', 0 ), // moa = Mwan
+ TRUETYPE_TAG('m','o','c', 0 ), // moc = Mocoví
+ TRUETYPE_TAG('m','o','d', 0 ), // mod = Mobilian
+ TRUETYPE_TAG('m','o','e', 0 ), // moe = Montagnais
+ TRUETYPE_TAG('m','o','f', 0 ), // mof = Mohegan-Montauk-Narragansett
+ TRUETYPE_TAG('m','o','g', 0 ), // mog = Mongondow
+ TRUETYPE_TAG('m','o','h', 0 ), // moh = Mohawk
+ TRUETYPE_TAG('m','o','i', 0 ), // moi = Mboi
+ TRUETYPE_TAG('m','o','j', 0 ), // moj = Monzombo
+ TRUETYPE_TAG('m','o','k', 0 ), // mok = Morori
+ TRUETYPE_TAG('m','o','m', 0 ), // mom = Mangue
+ TRUETYPE_TAG('m','o','o', 0 ), // moo = Monom
+ TRUETYPE_TAG('m','o','p', 0 ), // mop = Mopán Maya
+ TRUETYPE_TAG('m','o','q', 0 ), // moq = Mor (Bomberai Peninsula)
+ TRUETYPE_TAG('m','o','r', 0 ), // mor = Moro
+ TRUETYPE_TAG('m','o','s', 0 ), // mos = Mossi
+ TRUETYPE_TAG('m','o','t', 0 ), // mot = Barí
+ TRUETYPE_TAG('m','o','u', 0 ), // mou = Mogum
+ TRUETYPE_TAG('m','o','v', 0 ), // mov = Mohave
+ TRUETYPE_TAG('m','o','w', 0 ), // mow = Moi (Congo)
+ TRUETYPE_TAG('m','o','x', 0 ), // mox = Molima
+ TRUETYPE_TAG('m','o','y', 0 ), // moy = Shekkacho
+ TRUETYPE_TAG('m','o','z', 0 ), // moz = Mukulu
+ TRUETYPE_TAG('m','p','a', 0 ), // mpa = Mpoto
+ TRUETYPE_TAG('m','p','b', 0 ), // mpb = Mullukmulluk
+ TRUETYPE_TAG('m','p','c', 0 ), // mpc = Mangarayi
+ TRUETYPE_TAG('m','p','d', 0 ), // mpd = Machinere
+ TRUETYPE_TAG('m','p','e', 0 ), // mpe = Majang
+ TRUETYPE_TAG('m','p','g', 0 ), // mpg = Marba
+ TRUETYPE_TAG('m','p','h', 0 ), // mph = Maung
+ TRUETYPE_TAG('m','p','i', 0 ), // mpi = Mpade
+ TRUETYPE_TAG('m','p','j', 0 ), // mpj = Martu Wangka
+ TRUETYPE_TAG('m','p','k', 0 ), // mpk = Mbara (Chad)
+ TRUETYPE_TAG('m','p','l', 0 ), // mpl = Middle Watut
+ TRUETYPE_TAG('m','p','m', 0 ), // mpm = Yosondúa Mixtec
+ TRUETYPE_TAG('m','p','n', 0 ), // mpn = Mindiri
+ TRUETYPE_TAG('m','p','o', 0 ), // mpo = Miu
+ TRUETYPE_TAG('m','p','p', 0 ), // mpp = Migabac
+ TRUETYPE_TAG('m','p','q', 0 ), // mpq = Matís
+ TRUETYPE_TAG('m','p','r', 0 ), // mpr = Vangunu
+ TRUETYPE_TAG('m','p','s', 0 ), // mps = Dadibi
+ TRUETYPE_TAG('m','p','t', 0 ), // mpt = Mian
+ TRUETYPE_TAG('m','p','u', 0 ), // mpu = Makuráp
+ TRUETYPE_TAG('m','p','v', 0 ), // mpv = Mungkip
+ TRUETYPE_TAG('m','p','w', 0 ), // mpw = Mapidian
+ TRUETYPE_TAG('m','p','x', 0 ), // mpx = Misima-Paneati
+ TRUETYPE_TAG('m','p','y', 0 ), // mpy = Mapia
+ TRUETYPE_TAG('m','p','z', 0 ), // mpz = Mpi
+ TRUETYPE_TAG('m','q','a', 0 ), // mqa = Maba (Indonesia)
+ TRUETYPE_TAG('m','q','b', 0 ), // mqb = Mbuko
+ TRUETYPE_TAG('m','q','c', 0 ), // mqc = Mangole
+ TRUETYPE_TAG('m','q','e', 0 ), // mqe = Matepi
+ TRUETYPE_TAG('m','q','f', 0 ), // mqf = Momuna
+ TRUETYPE_TAG('m','q','g', 0 ), // mqg = Kota Bangun Kutai Malay
+ TRUETYPE_TAG('m','q','h', 0 ), // mqh = Tlazoyaltepec Mixtec
+ TRUETYPE_TAG('m','q','i', 0 ), // mqi = Mariri
+ TRUETYPE_TAG('m','q','j', 0 ), // mqj = Mamasa
+ TRUETYPE_TAG('m','q','k', 0 ), // mqk = Rajah Kabunsuwan Manobo
+ TRUETYPE_TAG('m','q','l', 0 ), // mql = Mbelime
+ TRUETYPE_TAG('m','q','m', 0 ), // mqm = South Marquesan
+ TRUETYPE_TAG('m','q','n', 0 ), // mqn = Moronene
+ TRUETYPE_TAG('m','q','o', 0 ), // mqo = Modole
+ TRUETYPE_TAG('m','q','p', 0 ), // mqp = Manipa
+ TRUETYPE_TAG('m','q','q', 0 ), // mqq = Minokok
+ TRUETYPE_TAG('m','q','r', 0 ), // mqr = Mander
+ TRUETYPE_TAG('m','q','s', 0 ), // mqs = West Makian
+ TRUETYPE_TAG('m','q','t', 0 ), // mqt = Mok
+ TRUETYPE_TAG('m','q','u', 0 ), // mqu = Mandari
+ TRUETYPE_TAG('m','q','v', 0 ), // mqv = Mosimo
+ TRUETYPE_TAG('m','q','w', 0 ), // mqw = Murupi
+ TRUETYPE_TAG('m','q','x', 0 ), // mqx = Mamuju
+ TRUETYPE_TAG('m','q','y', 0 ), // mqy = Manggarai
+ TRUETYPE_TAG('m','q','z', 0 ), // mqz = Malasanga
+ TRUETYPE_TAG('m','r','a', 0 ), // mra = Mlabri
+ TRUETYPE_TAG('m','r','b', 0 ), // mrb = Marino
+ TRUETYPE_TAG('m','r','c', 0 ), // mrc = Maricopa
+ TRUETYPE_TAG('m','r','d', 0 ), // mrd = Western Magar
+ TRUETYPE_TAG('m','r','e', 0 ), // mre = Martha's Vineyard Sign Language
+ TRUETYPE_TAG('m','r','f', 0 ), // mrf = Elseng
+ TRUETYPE_TAG('m','r','g', 0 ), // mrg = Mising
+ TRUETYPE_TAG('m','r','h', 0 ), // mrh = Mara Chin
+ TRUETYPE_TAG('m','r','j', 0 ), // mrj = Western Mari
+ TRUETYPE_TAG('m','r','k', 0 ), // mrk = Hmwaveke
+ TRUETYPE_TAG('m','r','l', 0 ), // mrl = Mortlockese
+ TRUETYPE_TAG('m','r','m', 0 ), // mrm = Merlav
+ TRUETYPE_TAG('m','r','n', 0 ), // mrn = Cheke Holo
+ TRUETYPE_TAG('m','r','o', 0 ), // mro = Mru
+ TRUETYPE_TAG('m','r','p', 0 ), // mrp = Morouas
+ TRUETYPE_TAG('m','r','q', 0 ), // mrq = North Marquesan
+ TRUETYPE_TAG('m','r','r', 0 ), // mrr = Maria (India)
+ TRUETYPE_TAG('m','r','s', 0 ), // mrs = Maragus
+ TRUETYPE_TAG('m','r','t', 0 ), // mrt = Marghi Central
+ TRUETYPE_TAG('m','r','u', 0 ), // mru = Mono (Cameroon)
+ TRUETYPE_TAG('m','r','v', 0 ), // mrv = Mangareva
+ TRUETYPE_TAG('m','r','w', 0 ), // mrw = Maranao
+ TRUETYPE_TAG('m','r','x', 0 ), // mrx = Maremgi
+ TRUETYPE_TAG('m','r','y', 0 ), // mry = Mandaya
+ TRUETYPE_TAG('m','r','z', 0 ), // mrz = Marind
+ TRUETYPE_TAG('m','s','b', 0 ), // msb = Masbatenyo
+ TRUETYPE_TAG('m','s','c', 0 ), // msc = Sankaran Maninka
+ TRUETYPE_TAG('m','s','d', 0 ), // msd = Yucatec Maya Sign Language
+ TRUETYPE_TAG('m','s','e', 0 ), // mse = Musey
+ TRUETYPE_TAG('m','s','f', 0 ), // msf = Mekwei
+ TRUETYPE_TAG('m','s','g', 0 ), // msg = Moraid
+ TRUETYPE_TAG('m','s','h', 0 ), // msh = Masikoro Malagasy
+ TRUETYPE_TAG('m','s','i', 0 ), // msi = Sabah Malay
+ TRUETYPE_TAG('m','s','j', 0 ), // msj = Ma (Democratic Republic of Congo)
+ TRUETYPE_TAG('m','s','k', 0 ), // msk = Mansaka
+ TRUETYPE_TAG('m','s','l', 0 ), // msl = Molof
+ TRUETYPE_TAG('m','s','m', 0 ), // msm = Agusan Manobo
+ TRUETYPE_TAG('m','s','n', 0 ), // msn = Vurës
+ TRUETYPE_TAG('m','s','o', 0 ), // mso = Mombum
+ TRUETYPE_TAG('m','s','p', 0 ), // msp = Maritsauá
+ TRUETYPE_TAG('m','s','q', 0 ), // msq = Caac
+ TRUETYPE_TAG('m','s','r', 0 ), // msr = Mongolian Sign Language
+ TRUETYPE_TAG('m','s','s', 0 ), // mss = West Masela
+ TRUETYPE_TAG('m','s','t', 0 ), // mst = Cataelano Mandaya
+ TRUETYPE_TAG('m','s','u', 0 ), // msu = Musom
+ TRUETYPE_TAG('m','s','v', 0 ), // msv = Maslam
+ TRUETYPE_TAG('m','s','w', 0 ), // msw = Mansoanka
+ TRUETYPE_TAG('m','s','x', 0 ), // msx = Moresada
+ TRUETYPE_TAG('m','s','y', 0 ), // msy = Aruamu
+ TRUETYPE_TAG('m','s','z', 0 ), // msz = Momare
+ TRUETYPE_TAG('m','t','a', 0 ), // mta = Cotabato Manobo
+ TRUETYPE_TAG('m','t','b', 0 ), // mtb = Anyin Morofo
+ TRUETYPE_TAG('m','t','c', 0 ), // mtc = Munit
+ TRUETYPE_TAG('m','t','d', 0 ), // mtd = Mualang
+ TRUETYPE_TAG('m','t','e', 0 ), // mte = Mono (Solomon Islands)
+ TRUETYPE_TAG('m','t','f', 0 ), // mtf = Murik (Papua New Guinea)
+ TRUETYPE_TAG('m','t','g', 0 ), // mtg = Una
+ TRUETYPE_TAG('m','t','h', 0 ), // mth = Munggui
+ TRUETYPE_TAG('m','t','i', 0 ), // mti = Maiwa (Papua New Guinea)
+ TRUETYPE_TAG('m','t','j', 0 ), // mtj = Moskona
+ TRUETYPE_TAG('m','t','k', 0 ), // mtk = Mbe'
+ TRUETYPE_TAG('m','t','l', 0 ), // mtl = Montol
+ TRUETYPE_TAG('m','t','m', 0 ), // mtm = Mator
+ TRUETYPE_TAG('m','t','n', 0 ), // mtn = Matagalpa
+ TRUETYPE_TAG('m','t','o', 0 ), // mto = Totontepec Mixe
+ TRUETYPE_TAG('m','t','p', 0 ), // mtp = Wichí Lhamtés Nocten
+ TRUETYPE_TAG('m','t','q', 0 ), // mtq = Muong
+ TRUETYPE_TAG('m','t','r', 0 ), // mtr = Mewari
+ TRUETYPE_TAG('m','t','s', 0 ), // mts = Yora
+ TRUETYPE_TAG('m','t','t', 0 ), // mtt = Mota
+ TRUETYPE_TAG('m','t','u', 0 ), // mtu = Tututepec Mixtec
+ TRUETYPE_TAG('m','t','v', 0 ), // mtv = Asaro'o
+ TRUETYPE_TAG('m','t','w', 0 ), // mtw = Southern Binukidnon
+ TRUETYPE_TAG('m','t','x', 0 ), // mtx = Tidaá Mixtec
+ TRUETYPE_TAG('m','t','y', 0 ), // mty = Nabi
+ TRUETYPE_TAG('m','u','a', 0 ), // mua = Mundang
+ TRUETYPE_TAG('m','u','b', 0 ), // mub = Mubi
+ TRUETYPE_TAG('m','u','c', 0 ), // muc = Mbu'
+ TRUETYPE_TAG('m','u','d', 0 ), // mud = Mednyj Aleut
+ TRUETYPE_TAG('m','u','e', 0 ), // mue = Media Lengua
+ TRUETYPE_TAG('m','u','g', 0 ), // mug = Musgu
+ TRUETYPE_TAG('m','u','h', 0 ), // muh = Mündü
+ TRUETYPE_TAG('m','u','i', 0 ), // mui = Musi
+ TRUETYPE_TAG('m','u','j', 0 ), // muj = Mabire
+ TRUETYPE_TAG('m','u','k', 0 ), // muk = Mugom
+ TRUETYPE_TAG('m','u','l', 0 ), // mul = Multiple languages
+ TRUETYPE_TAG('m','u','m', 0 ), // mum = Maiwala
+ TRUETYPE_TAG('m','u','n', 0 ), // mun = Munda languages
+ TRUETYPE_TAG('m','u','o', 0 ), // muo = Nyong
+ TRUETYPE_TAG('m','u','p', 0 ), // mup = Malvi
+ TRUETYPE_TAG('m','u','q', 0 ), // muq = Eastern Xiangxi Miao
+ TRUETYPE_TAG('m','u','r', 0 ), // mur = Murle
+ TRUETYPE_TAG('m','u','s', 0 ), // mus = Creek
+ TRUETYPE_TAG('m','u','t', 0 ), // mut = Western Muria
+ TRUETYPE_TAG('m','u','u', 0 ), // muu = Yaaku
+ TRUETYPE_TAG('m','u','v', 0 ), // muv = Muthuvan
+ TRUETYPE_TAG('m','u','x', 0 ), // mux = Bo-Ung
+ TRUETYPE_TAG('m','u','y', 0 ), // muy = Muyang
+ TRUETYPE_TAG('m','u','z', 0 ), // muz = Mursi
+ TRUETYPE_TAG('m','v','a', 0 ), // mva = Manam
+ TRUETYPE_TAG('m','v','b', 0 ), // mvb = Mattole
+ TRUETYPE_TAG('m','v','d', 0 ), // mvd = Mamboru
+ TRUETYPE_TAG('m','v','e', 0 ), // mve = Marwari (Pakistan)
+ TRUETYPE_TAG('m','v','f', 0 ), // mvf = Peripheral Mongolian
+ TRUETYPE_TAG('m','v','g', 0 ), // mvg = Yucuañe Mixtec
+ TRUETYPE_TAG('m','v','h', 0 ), // mvh = Mire
+ TRUETYPE_TAG('m','v','i', 0 ), // mvi = Miyako
+ TRUETYPE_TAG('m','v','k', 0 ), // mvk = Mekmek
+ TRUETYPE_TAG('m','v','l', 0 ), // mvl = Mbara (Australia)
+ TRUETYPE_TAG('m','v','m', 0 ), // mvm = Muya
+ TRUETYPE_TAG('m','v','n', 0 ), // mvn = Minaveha
+ TRUETYPE_TAG('m','v','o', 0 ), // mvo = Marovo
+ TRUETYPE_TAG('m','v','p', 0 ), // mvp = Duri
+ TRUETYPE_TAG('m','v','q', 0 ), // mvq = Moere
+ TRUETYPE_TAG('m','v','r', 0 ), // mvr = Marau
+ TRUETYPE_TAG('m','v','s', 0 ), // mvs = Massep
+ TRUETYPE_TAG('m','v','t', 0 ), // mvt = Mpotovoro
+ TRUETYPE_TAG('m','v','u', 0 ), // mvu = Marfa
+ TRUETYPE_TAG('m','v','v', 0 ), // mvv = Tagal Murut
+ TRUETYPE_TAG('m','v','w', 0 ), // mvw = Machinga
+ TRUETYPE_TAG('m','v','x', 0 ), // mvx = Meoswar
+ TRUETYPE_TAG('m','v','y', 0 ), // mvy = Indus Kohistani
+ TRUETYPE_TAG('m','v','z', 0 ), // mvz = Mesqan
+ TRUETYPE_TAG('m','w','a', 0 ), // mwa = Mwatebu
+ TRUETYPE_TAG('m','w','b', 0 ), // mwb = Juwal
+ TRUETYPE_TAG('m','w','c', 0 ), // mwc = Are
+ TRUETYPE_TAG('m','w','d', 0 ), // mwd = Mudbura
+ TRUETYPE_TAG('m','w','e', 0 ), // mwe = Mwera (Chimwera)
+ TRUETYPE_TAG('m','w','f', 0 ), // mwf = Murrinh-Patha
+ TRUETYPE_TAG('m','w','g', 0 ), // mwg = Aiklep
+ TRUETYPE_TAG('m','w','h', 0 ), // mwh = Mouk-Aria
+ TRUETYPE_TAG('m','w','i', 0 ), // mwi = Labo
+ TRUETYPE_TAG('m','w','j', 0 ), // mwj = Maligo
+ TRUETYPE_TAG('m','w','k', 0 ), // mwk = Kita Maninkakan
+ TRUETYPE_TAG('m','w','l', 0 ), // mwl = Mirandese
+ TRUETYPE_TAG('m','w','m', 0 ), // mwm = Sar
+ TRUETYPE_TAG('m','w','n', 0 ), // mwn = Nyamwanga
+ TRUETYPE_TAG('m','w','o', 0 ), // mwo = Central Maewo
+ TRUETYPE_TAG('m','w','p', 0 ), // mwp = Kala Lagaw Ya
+ TRUETYPE_TAG('m','w','q', 0 ), // mwq = Mün Chin
+ TRUETYPE_TAG('m','w','r', 0 ), // mwr = Marwari
+ TRUETYPE_TAG('m','w','s', 0 ), // mws = Mwimbi-Muthambi
+ TRUETYPE_TAG('m','w','t', 0 ), // mwt = Moken
+ TRUETYPE_TAG('m','w','u', 0 ), // mwu = Mittu
+ TRUETYPE_TAG('m','w','v', 0 ), // mwv = Mentawai
+ TRUETYPE_TAG('m','w','w', 0 ), // mww = Hmong Daw
+ TRUETYPE_TAG('m','w','x', 0 ), // mwx = Mediak
+ TRUETYPE_TAG('m','w','y', 0 ), // mwy = Mosiro
+ TRUETYPE_TAG('m','w','z', 0 ), // mwz = Moingi
+ TRUETYPE_TAG('m','x','a', 0 ), // mxa = Northwest Oaxaca Mixtec
+ TRUETYPE_TAG('m','x','b', 0 ), // mxb = Tezoatlán Mixtec
+ TRUETYPE_TAG('m','x','c', 0 ), // mxc = Manyika
+ TRUETYPE_TAG('m','x','d', 0 ), // mxd = Modang
+ TRUETYPE_TAG('m','x','e', 0 ), // mxe = Mele-Fila
+ TRUETYPE_TAG('m','x','f', 0 ), // mxf = Malgbe
+ TRUETYPE_TAG('m','x','g', 0 ), // mxg = Mbangala
+ TRUETYPE_TAG('m','x','h', 0 ), // mxh = Mvuba
+ TRUETYPE_TAG('m','x','i', 0 ), // mxi = Mozarabic
+ TRUETYPE_TAG('m','x','j', 0 ), // mxj = Miju-Mishmi
+ TRUETYPE_TAG('m','x','k', 0 ), // mxk = Monumbo
+ TRUETYPE_TAG('m','x','l', 0 ), // mxl = Maxi Gbe
+ TRUETYPE_TAG('m','x','m', 0 ), // mxm = Meramera
+ TRUETYPE_TAG('m','x','n', 0 ), // mxn = Moi (Indonesia)
+ TRUETYPE_TAG('m','x','o', 0 ), // mxo = Mbowe
+ TRUETYPE_TAG('m','x','p', 0 ), // mxp = Tlahuitoltepec Mixe
+ TRUETYPE_TAG('m','x','q', 0 ), // mxq = Juquila Mixe
+ TRUETYPE_TAG('m','x','r', 0 ), // mxr = Murik (Malaysia)
+ TRUETYPE_TAG('m','x','s', 0 ), // mxs = Huitepec Mixtec
+ TRUETYPE_TAG('m','x','t', 0 ), // mxt = Jamiltepec Mixtec
+ TRUETYPE_TAG('m','x','u', 0 ), // mxu = Mada (Cameroon)
+ TRUETYPE_TAG('m','x','v', 0 ), // mxv = Metlatónoc Mixtec
+ TRUETYPE_TAG('m','x','w', 0 ), // mxw = Namo
+ TRUETYPE_TAG('m','x','x', 0 ), // mxx = Mahou
+ TRUETYPE_TAG('m','x','y', 0 ), // mxy = Southeastern Nochixtlán Mixtec
+ TRUETYPE_TAG('m','x','z', 0 ), // mxz = Central Masela
+ TRUETYPE_TAG('m','y','b', 0 ), // myb = Mbay
+ TRUETYPE_TAG('m','y','c', 0 ), // myc = Mayeka
+ TRUETYPE_TAG('m','y','d', 0 ), // myd = Maramba
+ TRUETYPE_TAG('m','y','e', 0 ), // mye = Myene
+ TRUETYPE_TAG('m','y','f', 0 ), // myf = Bambassi
+ TRUETYPE_TAG('m','y','g', 0 ), // myg = Manta
+ TRUETYPE_TAG('m','y','h', 0 ), // myh = Makah
+ TRUETYPE_TAG('m','y','i', 0 ), // myi = Mina (India)
+ TRUETYPE_TAG('m','y','j', 0 ), // myj = Mangayat
+ TRUETYPE_TAG('m','y','k', 0 ), // myk = Mamara Senoufo
+ TRUETYPE_TAG('m','y','l', 0 ), // myl = Moma
+ TRUETYPE_TAG('m','y','m', 0 ), // mym = Me'en
+ TRUETYPE_TAG('m','y','n', 0 ), // myn = Mayan languages
+ TRUETYPE_TAG('m','y','o', 0 ), // myo = Anfillo
+ TRUETYPE_TAG('m','y','p', 0 ), // myp = Pirahã
+ TRUETYPE_TAG('m','y','q', 0 ), // myq = Forest Maninka
+ TRUETYPE_TAG('m','y','r', 0 ), // myr = Muniche
+ TRUETYPE_TAG('m','y','s', 0 ), // mys = Mesmes
+ TRUETYPE_TAG('m','y','t', 0 ), // myt = Sangab Mandaya
+ TRUETYPE_TAG('m','y','u', 0 ), // myu = Mundurukú
+ TRUETYPE_TAG('m','y','v', 0 ), // myv = Erzya
+ TRUETYPE_TAG('m','y','w', 0 ), // myw = Muyuw
+ TRUETYPE_TAG('m','y','x', 0 ), // myx = Masaaba
+ TRUETYPE_TAG('m','y','y', 0 ), // myy = Macuna
+ TRUETYPE_TAG('m','y','z', 0 ), // myz = Classical Mandaic
+ TRUETYPE_TAG('m','z','a', 0 ), // mza = Santa María Zacatepec Mixtec
+ TRUETYPE_TAG('m','z','b', 0 ), // mzb = Tumzabt
+ TRUETYPE_TAG('m','z','c', 0 ), // mzc = Madagascar Sign Language
+ TRUETYPE_TAG('m','z','d', 0 ), // mzd = Malimba
+ TRUETYPE_TAG('m','z','e', 0 ), // mze = Morawa
+ TRUETYPE_TAG('m','z','g', 0 ), // mzg = Monastic Sign Language
+ TRUETYPE_TAG('m','z','h', 0 ), // mzh = Wichí Lhamtés Güisnay
+ TRUETYPE_TAG('m','z','i', 0 ), // mzi = Ixcatlán Mazatec
+ TRUETYPE_TAG('m','z','j', 0 ), // mzj = Manya
+ TRUETYPE_TAG('m','z','k', 0 ), // mzk = Nigeria Mambila
+ TRUETYPE_TAG('m','z','l', 0 ), // mzl = Mazatlán Mixe
+ TRUETYPE_TAG('m','z','m', 0 ), // mzm = Mumuye
+ TRUETYPE_TAG('m','z','n', 0 ), // mzn = Mazanderani
+ TRUETYPE_TAG('m','z','o', 0 ), // mzo = Matipuhy
+ TRUETYPE_TAG('m','z','p', 0 ), // mzp = Movima
+ TRUETYPE_TAG('m','z','q', 0 ), // mzq = Mori Atas
+ TRUETYPE_TAG('m','z','r', 0 ), // mzr = Marúbo
+ TRUETYPE_TAG('m','z','s', 0 ), // mzs = Macanese
+ TRUETYPE_TAG('m','z','t', 0 ), // mzt = Mintil
+ TRUETYPE_TAG('m','z','u', 0 ), // mzu = Inapang
+ TRUETYPE_TAG('m','z','v', 0 ), // mzv = Manza
+ TRUETYPE_TAG('m','z','w', 0 ), // mzw = Deg
+ TRUETYPE_TAG('m','z','x', 0 ), // mzx = Mawayana
+ TRUETYPE_TAG('m','z','y', 0 ), // mzy = Mozambican Sign Language
+ TRUETYPE_TAG('m','z','z', 0 ), // mzz = Maiadomu
+ TRUETYPE_TAG('n','a','a', 0 ), // naa = Namla
+ TRUETYPE_TAG('n','a','b', 0 ), // nab = Southern Nambikuára
+ TRUETYPE_TAG('n','a','c', 0 ), // nac = Narak
+ TRUETYPE_TAG('n','a','d', 0 ), // nad = Nijadali
+ TRUETYPE_TAG('n','a','e', 0 ), // nae = Naka'ela
+ TRUETYPE_TAG('n','a','f', 0 ), // naf = Nabak
+ TRUETYPE_TAG('n','a','g', 0 ), // nag = Naga Pidgin
+ TRUETYPE_TAG('n','a','h', 0 ), // nah = Nahuatl languages
+ TRUETYPE_TAG('n','a','i', 0 ), // nai = North American Indian languages
+ TRUETYPE_TAG('n','a','j', 0 ), // naj = Nalu
+ TRUETYPE_TAG('n','a','k', 0 ), // nak = Nakanai
+ TRUETYPE_TAG('n','a','l', 0 ), // nal = Nalik
+ TRUETYPE_TAG('n','a','m', 0 ), // nam = Nangikurrunggurr
+ TRUETYPE_TAG('n','a','n', 0 ), // nan = Min Nan Chinese
+ TRUETYPE_TAG('n','a','o', 0 ), // nao = Naaba
+ TRUETYPE_TAG('n','a','p', 0 ), // nap = Neapolitan
+ TRUETYPE_TAG('n','a','q', 0 ), // naq = Nama (Namibia)
+ TRUETYPE_TAG('n','a','r', 0 ), // nar = Iguta
+ TRUETYPE_TAG('n','a','s', 0 ), // nas = Naasioi
+ TRUETYPE_TAG('n','a','t', 0 ), // nat = Hungworo
+ TRUETYPE_TAG('n','a','w', 0 ), // naw = Nawuri
+ TRUETYPE_TAG('n','a','x', 0 ), // nax = Nakwi
+ TRUETYPE_TAG('n','a','y', 0 ), // nay = Narrinyeri
+ TRUETYPE_TAG('n','a','z', 0 ), // naz = Coatepec Nahuatl
+ TRUETYPE_TAG('n','b','a', 0 ), // nba = Nyemba
+ TRUETYPE_TAG('n','b','b', 0 ), // nbb = Ndoe
+ TRUETYPE_TAG('n','b','c', 0 ), // nbc = Chang Naga
+ TRUETYPE_TAG('n','b','d', 0 ), // nbd = Ngbinda
+ TRUETYPE_TAG('n','b','e', 0 ), // nbe = Konyak Naga
+ TRUETYPE_TAG('n','b','f', 0 ), // nbf = Naxi
+ TRUETYPE_TAG('n','b','g', 0 ), // nbg = Nagarchal
+ TRUETYPE_TAG('n','b','h', 0 ), // nbh = Ngamo
+ TRUETYPE_TAG('n','b','i', 0 ), // nbi = Mao Naga
+ TRUETYPE_TAG('n','b','j', 0 ), // nbj = Ngarinman
+ TRUETYPE_TAG('n','b','k', 0 ), // nbk = Nake
+ TRUETYPE_TAG('n','b','m', 0 ), // nbm = Ngbaka Ma'bo
+ TRUETYPE_TAG('n','b','n', 0 ), // nbn = Kuri
+ TRUETYPE_TAG('n','b','o', 0 ), // nbo = Nkukoli
+ TRUETYPE_TAG('n','b','p', 0 ), // nbp = Nnam
+ TRUETYPE_TAG('n','b','q', 0 ), // nbq = Nggem
+ TRUETYPE_TAG('n','b','r', 0 ), // nbr = Numana-Nunku-Gbantu-Numbu
+ TRUETYPE_TAG('n','b','s', 0 ), // nbs = Namibian Sign Language
+ TRUETYPE_TAG('n','b','t', 0 ), // nbt = Na
+ TRUETYPE_TAG('n','b','u', 0 ), // nbu = Rongmei Naga
+ TRUETYPE_TAG('n','b','v', 0 ), // nbv = Ngamambo
+ TRUETYPE_TAG('n','b','w', 0 ), // nbw = Southern Ngbandi
+ TRUETYPE_TAG('n','b','x', 0 ), // nbx = Ngura
+ TRUETYPE_TAG('n','b','y', 0 ), // nby = Ningera
+ TRUETYPE_TAG('n','c','a', 0 ), // nca = Iyo
+ TRUETYPE_TAG('n','c','b', 0 ), // ncb = Central Nicobarese
+ TRUETYPE_TAG('n','c','c', 0 ), // ncc = Ponam
+ TRUETYPE_TAG('n','c','d', 0 ), // ncd = Nachering
+ TRUETYPE_TAG('n','c','e', 0 ), // nce = Yale
+ TRUETYPE_TAG('n','c','f', 0 ), // ncf = Notsi
+ TRUETYPE_TAG('n','c','g', 0 ), // ncg = Nisga'a
+ TRUETYPE_TAG('n','c','h', 0 ), // nch = Central Huasteca Nahuatl
+ TRUETYPE_TAG('n','c','i', 0 ), // nci = Classical Nahuatl
+ TRUETYPE_TAG('n','c','j', 0 ), // ncj = Northern Puebla Nahuatl
+ TRUETYPE_TAG('n','c','k', 0 ), // nck = Nakara
+ TRUETYPE_TAG('n','c','l', 0 ), // ncl = Michoacán Nahuatl
+ TRUETYPE_TAG('n','c','m', 0 ), // ncm = Nambo
+ TRUETYPE_TAG('n','c','n', 0 ), // ncn = Nauna
+ TRUETYPE_TAG('n','c','o', 0 ), // nco = Sibe
+ TRUETYPE_TAG('n','c','p', 0 ), // ncp = Ndaktup
+ TRUETYPE_TAG('n','c','r', 0 ), // ncr = Ncane
+ TRUETYPE_TAG('n','c','s', 0 ), // ncs = Nicaraguan Sign Language
+ TRUETYPE_TAG('n','c','t', 0 ), // nct = Chothe Naga
+ TRUETYPE_TAG('n','c','u', 0 ), // ncu = Chumburung
+ TRUETYPE_TAG('n','c','x', 0 ), // ncx = Central Puebla Nahuatl
+ TRUETYPE_TAG('n','c','z', 0 ), // ncz = Natchez
+ TRUETYPE_TAG('n','d','a', 0 ), // nda = Ndasa
+ TRUETYPE_TAG('n','d','b', 0 ), // ndb = Kenswei Nsei
+ TRUETYPE_TAG('n','d','c', 0 ), // ndc = Ndau
+ TRUETYPE_TAG('n','d','d', 0 ), // ndd = Nde-Nsele-Nta
+ TRUETYPE_TAG('n','d','f', 0 ), // ndf = Nadruvian
+ TRUETYPE_TAG('n','d','g', 0 ), // ndg = Ndengereko
+ TRUETYPE_TAG('n','d','h', 0 ), // ndh = Ndali
+ TRUETYPE_TAG('n','d','i', 0 ), // ndi = Samba Leko
+ TRUETYPE_TAG('n','d','j', 0 ), // ndj = Ndamba
+ TRUETYPE_TAG('n','d','k', 0 ), // ndk = Ndaka
+ TRUETYPE_TAG('n','d','l', 0 ), // ndl = Ndolo
+ TRUETYPE_TAG('n','d','m', 0 ), // ndm = Ndam
+ TRUETYPE_TAG('n','d','n', 0 ), // ndn = Ngundi
+ TRUETYPE_TAG('n','d','p', 0 ), // ndp = Ndo
+ TRUETYPE_TAG('n','d','q', 0 ), // ndq = Ndombe
+ TRUETYPE_TAG('n','d','r', 0 ), // ndr = Ndoola
+ TRUETYPE_TAG('n','d','s', 0 ), // nds = Low German
+ TRUETYPE_TAG('n','d','t', 0 ), // ndt = Ndunga
+ TRUETYPE_TAG('n','d','u', 0 ), // ndu = Dugun
+ TRUETYPE_TAG('n','d','v', 0 ), // ndv = Ndut
+ TRUETYPE_TAG('n','d','w', 0 ), // ndw = Ndobo
+ TRUETYPE_TAG('n','d','x', 0 ), // ndx = Nduga
+ TRUETYPE_TAG('n','d','y', 0 ), // ndy = Lutos
+ TRUETYPE_TAG('n','d','z', 0 ), // ndz = Ndogo
+ TRUETYPE_TAG('n','e','a', 0 ), // nea = Eastern Ngad'a
+ TRUETYPE_TAG('n','e','b', 0 ), // neb = Toura (Côte d'Ivoire)
+ TRUETYPE_TAG('n','e','c', 0 ), // nec = Nedebang
+ TRUETYPE_TAG('n','e','d', 0 ), // ned = Nde-Gbite
+ TRUETYPE_TAG('n','e','e', 0 ), // nee = Nêlêmwa-Nixumwak
+ TRUETYPE_TAG('n','e','f', 0 ), // nef = Nefamese
+ TRUETYPE_TAG('n','e','g', 0 ), // neg = Negidal
+ TRUETYPE_TAG('n','e','h', 0 ), // neh = Nyenkha
+ TRUETYPE_TAG('n','e','i', 0 ), // nei = Neo-Hittite
+ TRUETYPE_TAG('n','e','j', 0 ), // nej = Neko
+ TRUETYPE_TAG('n','e','k', 0 ), // nek = Neku
+ TRUETYPE_TAG('n','e','m', 0 ), // nem = Nemi
+ TRUETYPE_TAG('n','e','n', 0 ), // nen = Nengone
+ TRUETYPE_TAG('n','e','o', 0 ), // neo = Ná-Meo
+ TRUETYPE_TAG('n','e','q', 0 ), // neq = North Central Mixe
+ TRUETYPE_TAG('n','e','r', 0 ), // ner = Yahadian
+ TRUETYPE_TAG('n','e','s', 0 ), // nes = Bhoti Kinnauri
+ TRUETYPE_TAG('n','e','t', 0 ), // net = Nete
+ TRUETYPE_TAG('n','e','v', 0 ), // nev = Nyaheun
+ TRUETYPE_TAG('n','e','w', 0 ), // new = Newari
+ TRUETYPE_TAG('n','e','x', 0 ), // nex = Neme
+ TRUETYPE_TAG('n','e','y', 0 ), // ney = Neyo
+ TRUETYPE_TAG('n','e','z', 0 ), // nez = Nez Perce
+ TRUETYPE_TAG('n','f','a', 0 ), // nfa = Dhao
+ TRUETYPE_TAG('n','f','d', 0 ), // nfd = Ahwai
+ TRUETYPE_TAG('n','f','l', 0 ), // nfl = Ayiwo
+ TRUETYPE_TAG('n','f','r', 0 ), // nfr = Nafaanra
+ TRUETYPE_TAG('n','f','u', 0 ), // nfu = Mfumte
+ TRUETYPE_TAG('n','g','a', 0 ), // nga = Ngbaka
+ TRUETYPE_TAG('n','g','b', 0 ), // ngb = Northern Ngbandi
+ TRUETYPE_TAG('n','g','c', 0 ), // ngc = Ngombe (Democratic Republic of Congo)
+ TRUETYPE_TAG('n','g','d', 0 ), // ngd = Ngando (Central African Republic)
+ TRUETYPE_TAG('n','g','e', 0 ), // nge = Ngemba
+ TRUETYPE_TAG('n','g','f', 0 ), // ngf = Trans-New Guinea languages
+ TRUETYPE_TAG('n','g','g', 0 ), // ngg = Ngbaka Manza
+ TRUETYPE_TAG('n','g','h', 0 ), // ngh = N/u
+ TRUETYPE_TAG('n','g','i', 0 ), // ngi = Ngizim
+ TRUETYPE_TAG('n','g','j', 0 ), // ngj = Ngie
+ TRUETYPE_TAG('n','g','k', 0 ), // ngk = Ngalkbun
+ TRUETYPE_TAG('n','g','l', 0 ), // ngl = Lomwe
+ TRUETYPE_TAG('n','g','m', 0 ), // ngm = Ngatik Men's Creole
+ TRUETYPE_TAG('n','g','n', 0 ), // ngn = Ngwo
+ TRUETYPE_TAG('n','g','o', 0 ), // ngo = Ngoni
+ TRUETYPE_TAG('n','g','p', 0 ), // ngp = Ngulu
+ TRUETYPE_TAG('n','g','q', 0 ), // ngq = Ngurimi
+ TRUETYPE_TAG('n','g','r', 0 ), // ngr = Nanggu
+ TRUETYPE_TAG('n','g','s', 0 ), // ngs = Gvoko
+ TRUETYPE_TAG('n','g','t', 0 ), // ngt = Ngeq
+ TRUETYPE_TAG('n','g','u', 0 ), // ngu = Guerrero Nahuatl
+ TRUETYPE_TAG('n','g','v', 0 ), // ngv = Nagumi
+ TRUETYPE_TAG('n','g','w', 0 ), // ngw = Ngwaba
+ TRUETYPE_TAG('n','g','x', 0 ), // ngx = Nggwahyi
+ TRUETYPE_TAG('n','g','y', 0 ), // ngy = Tibea
+ TRUETYPE_TAG('n','g','z', 0 ), // ngz = Ngungwel
+ TRUETYPE_TAG('n','h','a', 0 ), // nha = Nhanda
+ TRUETYPE_TAG('n','h','b', 0 ), // nhb = Beng
+ TRUETYPE_TAG('n','h','c', 0 ), // nhc = Tabasco Nahuatl
+ TRUETYPE_TAG('n','h','d', 0 ), // nhd = Chiripá
+ TRUETYPE_TAG('n','h','e', 0 ), // nhe = Eastern Huasteca Nahuatl
+ TRUETYPE_TAG('n','h','f', 0 ), // nhf = Nhuwala
+ TRUETYPE_TAG('n','h','g', 0 ), // nhg = Tetelcingo Nahuatl
+ TRUETYPE_TAG('n','h','h', 0 ), // nhh = Nahari
+ TRUETYPE_TAG('n','h','i', 0 ), // nhi = Zacatlán-Ahuacatlán-Tepetzintla Nahuatl
+ TRUETYPE_TAG('n','h','k', 0 ), // nhk = Isthmus-Cosoleacaque Nahuatl
+ TRUETYPE_TAG('n','h','m', 0 ), // nhm = Morelos Nahuatl
+ TRUETYPE_TAG('n','h','n', 0 ), // nhn = Central Nahuatl
+ TRUETYPE_TAG('n','h','o', 0 ), // nho = Takuu
+ TRUETYPE_TAG('n','h','p', 0 ), // nhp = Isthmus-Pajapan Nahuatl
+ TRUETYPE_TAG('n','h','q', 0 ), // nhq = Huaxcaleca Nahuatl
+ TRUETYPE_TAG('n','h','r', 0 ), // nhr = Naro
+ TRUETYPE_TAG('n','h','t', 0 ), // nht = Ometepec Nahuatl
+ TRUETYPE_TAG('n','h','u', 0 ), // nhu = Noone
+ TRUETYPE_TAG('n','h','v', 0 ), // nhv = Temascaltepec Nahuatl
+ TRUETYPE_TAG('n','h','w', 0 ), // nhw = Western Huasteca Nahuatl
+ TRUETYPE_TAG('n','h','x', 0 ), // nhx = Isthmus-Mecayapan Nahuatl
+ TRUETYPE_TAG('n','h','y', 0 ), // nhy = Northern Oaxaca Nahuatl
+ TRUETYPE_TAG('n','h','z', 0 ), // nhz = Santa María La Alta Nahuatl
+ TRUETYPE_TAG('n','i','a', 0 ), // nia = Nias
+ TRUETYPE_TAG('n','i','b', 0 ), // nib = Nakame
+ TRUETYPE_TAG('n','i','c', 0 ), // nic = Niger-Kordofanian languages
+ TRUETYPE_TAG('n','i','d', 0 ), // nid = Ngandi
+ TRUETYPE_TAG('n','i','e', 0 ), // nie = Niellim
+ TRUETYPE_TAG('n','i','f', 0 ), // nif = Nek
+ TRUETYPE_TAG('n','i','g', 0 ), // nig = Ngalakan
+ TRUETYPE_TAG('n','i','h', 0 ), // nih = Nyiha (Tanzania)
+ TRUETYPE_TAG('n','i','i', 0 ), // nii = Nii
+ TRUETYPE_TAG('n','i','j', 0 ), // nij = Ngaju
+ TRUETYPE_TAG('n','i','k', 0 ), // nik = Southern Nicobarese
+ TRUETYPE_TAG('n','i','l', 0 ), // nil = Nila
+ TRUETYPE_TAG('n','i','m', 0 ), // nim = Nilamba
+ TRUETYPE_TAG('n','i','n', 0 ), // nin = Ninzo
+ TRUETYPE_TAG('n','i','o', 0 ), // nio = Nganasan
+ TRUETYPE_TAG('n','i','q', 0 ), // niq = Nandi
+ TRUETYPE_TAG('n','i','r', 0 ), // nir = Nimboran
+ TRUETYPE_TAG('n','i','s', 0 ), // nis = Nimi
+ TRUETYPE_TAG('n','i','t', 0 ), // nit = Southeastern Kolami
+ TRUETYPE_TAG('n','i','u', 0 ), // niu = Niuean
+ TRUETYPE_TAG('n','i','v', 0 ), // niv = Gilyak
+ TRUETYPE_TAG('n','i','w', 0 ), // niw = Nimo
+ TRUETYPE_TAG('n','i','x', 0 ), // nix = Hema
+ TRUETYPE_TAG('n','i','y', 0 ), // niy = Ngiti
+ TRUETYPE_TAG('n','i','z', 0 ), // niz = Ningil
+ TRUETYPE_TAG('n','j','a', 0 ), // nja = Nzanyi
+ TRUETYPE_TAG('n','j','b', 0 ), // njb = Nocte Naga
+ TRUETYPE_TAG('n','j','d', 0 ), // njd = Ndonde Hamba
+ TRUETYPE_TAG('n','j','h', 0 ), // njh = Lotha Naga
+ TRUETYPE_TAG('n','j','i', 0 ), // nji = Gudanji
+ TRUETYPE_TAG('n','j','j', 0 ), // njj = Njen
+ TRUETYPE_TAG('n','j','l', 0 ), // njl = Njalgulgule
+ TRUETYPE_TAG('n','j','m', 0 ), // njm = Angami Naga
+ TRUETYPE_TAG('n','j','n', 0 ), // njn = Liangmai Naga
+ TRUETYPE_TAG('n','j','o', 0 ), // njo = Ao Naga
+ TRUETYPE_TAG('n','j','r', 0 ), // njr = Njerep
+ TRUETYPE_TAG('n','j','s', 0 ), // njs = Nisa
+ TRUETYPE_TAG('n','j','t', 0 ), // njt = Ndyuka-Trio Pidgin
+ TRUETYPE_TAG('n','j','u', 0 ), // nju = Ngadjunmaya
+ TRUETYPE_TAG('n','j','x', 0 ), // njx = Kunyi
+ TRUETYPE_TAG('n','j','y', 0 ), // njy = Njyem
+ TRUETYPE_TAG('n','k','a', 0 ), // nka = Nkoya
+ TRUETYPE_TAG('n','k','b', 0 ), // nkb = Khoibu Naga
+ TRUETYPE_TAG('n','k','c', 0 ), // nkc = Nkongho
+ TRUETYPE_TAG('n','k','d', 0 ), // nkd = Koireng
+ TRUETYPE_TAG('n','k','e', 0 ), // nke = Duke
+ TRUETYPE_TAG('n','k','f', 0 ), // nkf = Inpui Naga
+ TRUETYPE_TAG('n','k','g', 0 ), // nkg = Nekgini
+ TRUETYPE_TAG('n','k','h', 0 ), // nkh = Khezha Naga
+ TRUETYPE_TAG('n','k','i', 0 ), // nki = Thangal Naga
+ TRUETYPE_TAG('n','k','j', 0 ), // nkj = Nakai
+ TRUETYPE_TAG('n','k','k', 0 ), // nkk = Nokuku
+ TRUETYPE_TAG('n','k','m', 0 ), // nkm = Namat
+ TRUETYPE_TAG('n','k','n', 0 ), // nkn = Nkangala
+ TRUETYPE_TAG('n','k','o', 0 ), // nko = Nkonya
+ TRUETYPE_TAG('n','k','p', 0 ), // nkp = Niuatoputapu
+ TRUETYPE_TAG('n','k','q', 0 ), // nkq = Nkami
+ TRUETYPE_TAG('n','k','r', 0 ), // nkr = Nukuoro
+ TRUETYPE_TAG('n','k','s', 0 ), // nks = North Asmat
+ TRUETYPE_TAG('n','k','t', 0 ), // nkt = Nyika (Tanzania)
+ TRUETYPE_TAG('n','k','u', 0 ), // nku = Bouna Kulango
+ TRUETYPE_TAG('n','k','v', 0 ), // nkv = Nyika (Malawi and Zambia)
+ TRUETYPE_TAG('n','k','w', 0 ), // nkw = Nkutu
+ TRUETYPE_TAG('n','k','x', 0 ), // nkx = Nkoroo
+ TRUETYPE_TAG('n','k','z', 0 ), // nkz = Nkari
+ TRUETYPE_TAG('n','l','a', 0 ), // nla = Ngombale
+ TRUETYPE_TAG('n','l','c', 0 ), // nlc = Nalca
+ TRUETYPE_TAG('n','l','e', 0 ), // nle = East Nyala
+ TRUETYPE_TAG('n','l','g', 0 ), // nlg = Gela
+ TRUETYPE_TAG('n','l','i', 0 ), // nli = Grangali
+ TRUETYPE_TAG('n','l','j', 0 ), // nlj = Nyali
+ TRUETYPE_TAG('n','l','k', 0 ), // nlk = Ninia Yali
+ TRUETYPE_TAG('n','l','l', 0 ), // nll = Nihali
+ TRUETYPE_TAG('n','l','n', 0 ), // nln = Durango Nahuatl
+ TRUETYPE_TAG('n','l','o', 0 ), // nlo = Ngul
+ TRUETYPE_TAG('n','l','r', 0 ), // nlr = Ngarla
+ TRUETYPE_TAG('n','l','u', 0 ), // nlu = Nchumbulu
+ TRUETYPE_TAG('n','l','v', 0 ), // nlv = Orizaba Nahuatl
+ TRUETYPE_TAG('n','l','x', 0 ), // nlx = Nahali
+ TRUETYPE_TAG('n','l','y', 0 ), // nly = Nyamal
+ TRUETYPE_TAG('n','l','z', 0 ), // nlz = Nalögo
+ TRUETYPE_TAG('n','m','a', 0 ), // nma = Maram Naga
+ TRUETYPE_TAG('n','m','b', 0 ), // nmb = Big Nambas
+ TRUETYPE_TAG('n','m','c', 0 ), // nmc = Ngam
+ TRUETYPE_TAG('n','m','d', 0 ), // nmd = Ndumu
+ TRUETYPE_TAG('n','m','e', 0 ), // nme = Mzieme Naga
+ TRUETYPE_TAG('n','m','f', 0 ), // nmf = Tangkhul Naga
+ TRUETYPE_TAG('n','m','g', 0 ), // nmg = Kwasio
+ TRUETYPE_TAG('n','m','h', 0 ), // nmh = Monsang Naga
+ TRUETYPE_TAG('n','m','i', 0 ), // nmi = Nyam
+ TRUETYPE_TAG('n','m','j', 0 ), // nmj = Ngombe (Central African Republic)
+ TRUETYPE_TAG('n','m','k', 0 ), // nmk = Namakura
+ TRUETYPE_TAG('n','m','l', 0 ), // nml = Ndemli
+ TRUETYPE_TAG('n','m','m', 0 ), // nmm = Manangba
+ TRUETYPE_TAG('n','m','n', 0 ), // nmn = !Xóõ
+ TRUETYPE_TAG('n','m','o', 0 ), // nmo = Moyon Naga
+ TRUETYPE_TAG('n','m','p', 0 ), // nmp = Nimanbur
+ TRUETYPE_TAG('n','m','q', 0 ), // nmq = Nambya
+ TRUETYPE_TAG('n','m','r', 0 ), // nmr = Nimbari
+ TRUETYPE_TAG('n','m','s', 0 ), // nms = Letemboi
+ TRUETYPE_TAG('n','m','t', 0 ), // nmt = Namonuito
+ TRUETYPE_TAG('n','m','u', 0 ), // nmu = Northeast Maidu
+ TRUETYPE_TAG('n','m','v', 0 ), // nmv = Ngamini
+ TRUETYPE_TAG('n','m','w', 0 ), // nmw = Nimoa
+ TRUETYPE_TAG('n','m','x', 0 ), // nmx = Nama (Papua New Guinea)
+ TRUETYPE_TAG('n','m','y', 0 ), // nmy = Namuyi
+ TRUETYPE_TAG('n','m','z', 0 ), // nmz = Nawdm
+ TRUETYPE_TAG('n','n','a', 0 ), // nna = Nyangumarta
+ TRUETYPE_TAG('n','n','b', 0 ), // nnb = Nande
+ TRUETYPE_TAG('n','n','c', 0 ), // nnc = Nancere
+ TRUETYPE_TAG('n','n','d', 0 ), // nnd = West Ambae
+ TRUETYPE_TAG('n','n','e', 0 ), // nne = Ngandyera
+ TRUETYPE_TAG('n','n','f', 0 ), // nnf = Ngaing
+ TRUETYPE_TAG('n','n','g', 0 ), // nng = Maring Naga
+ TRUETYPE_TAG('n','n','h', 0 ), // nnh = Ngiemboon
+ TRUETYPE_TAG('n','n','i', 0 ), // nni = North Nuaulu
+ TRUETYPE_TAG('n','n','j', 0 ), // nnj = Nyangatom
+ TRUETYPE_TAG('n','n','k', 0 ), // nnk = Nankina
+ TRUETYPE_TAG('n','n','l', 0 ), // nnl = Northern Rengma Naga
+ TRUETYPE_TAG('n','n','m', 0 ), // nnm = Namia
+ TRUETYPE_TAG('n','n','n', 0 ), // nnn = Ngete
+ TRUETYPE_TAG('n','n','p', 0 ), // nnp = Wancho Naga
+ TRUETYPE_TAG('n','n','q', 0 ), // nnq = Ngindo
+ TRUETYPE_TAG('n','n','r', 0 ), // nnr = Narungga
+ TRUETYPE_TAG('n','n','s', 0 ), // nns = Ningye
+ TRUETYPE_TAG('n','n','t', 0 ), // nnt = Nanticoke
+ TRUETYPE_TAG('n','n','u', 0 ), // nnu = Dwang
+ TRUETYPE_TAG('n','n','v', 0 ), // nnv = Nugunu (Australia)
+ TRUETYPE_TAG('n','n','w', 0 ), // nnw = Southern Nuni
+ TRUETYPE_TAG('n','n','x', 0 ), // nnx = Ngong
+ TRUETYPE_TAG('n','n','y', 0 ), // nny = Nyangga
+ TRUETYPE_TAG('n','n','z', 0 ), // nnz = Nda'nda'
+ TRUETYPE_TAG('n','o','a', 0 ), // noa = Woun Meu
+ TRUETYPE_TAG('n','o','c', 0 ), // noc = Nuk
+ TRUETYPE_TAG('n','o','d', 0 ), // nod = Northern Thai
+ TRUETYPE_TAG('n','o','e', 0 ), // noe = Nimadi
+ TRUETYPE_TAG('n','o','f', 0 ), // nof = Nomane
+ TRUETYPE_TAG('n','o','g', 0 ), // nog = Nogai
+ TRUETYPE_TAG('n','o','h', 0 ), // noh = Nomu
+ TRUETYPE_TAG('n','o','i', 0 ), // noi = Noiri
+ TRUETYPE_TAG('n','o','j', 0 ), // noj = Nonuya
+ TRUETYPE_TAG('n','o','k', 0 ), // nok = Nooksack
+ TRUETYPE_TAG('n','o','m', 0 ), // nom = Nocamán
+ TRUETYPE_TAG('n','o','n', 0 ), // non = Old Norse
+ TRUETYPE_TAG('n','o','o', 0 ), // noo = Nootka
+ TRUETYPE_TAG('n','o','p', 0 ), // nop = Numanggang
+ TRUETYPE_TAG('n','o','q', 0 ), // noq = Ngongo
+ TRUETYPE_TAG('n','o','s', 0 ), // nos = Eastern Nisu
+ TRUETYPE_TAG('n','o','t', 0 ), // not = Nomatsiguenga
+ TRUETYPE_TAG('n','o','u', 0 ), // nou = Ewage-Notu
+ TRUETYPE_TAG('n','o','v', 0 ), // nov = Novial
+ TRUETYPE_TAG('n','o','w', 0 ), // now = Nyambo
+ TRUETYPE_TAG('n','o','y', 0 ), // noy = Noy
+ TRUETYPE_TAG('n','o','z', 0 ), // noz = Nayi
+ TRUETYPE_TAG('n','p','a', 0 ), // npa = Nar Phu
+ TRUETYPE_TAG('n','p','b', 0 ), // npb = Nupbikha
+ TRUETYPE_TAG('n','p','h', 0 ), // nph = Phom Naga
+ TRUETYPE_TAG('n','p','l', 0 ), // npl = Southeastern Puebla Nahuatl
+ TRUETYPE_TAG('n','p','n', 0 ), // npn = Mondropolon
+ TRUETYPE_TAG('n','p','o', 0 ), // npo = Pochuri Naga
+ TRUETYPE_TAG('n','p','s', 0 ), // nps = Nipsan
+ TRUETYPE_TAG('n','p','u', 0 ), // npu = Puimei Naga
+ TRUETYPE_TAG('n','p','y', 0 ), // npy = Napu
+ TRUETYPE_TAG('n','q','g', 0 ), // nqg = Southern Nago
+ TRUETYPE_TAG('n','q','k', 0 ), // nqk = Kura Ede Nago
+ TRUETYPE_TAG('n','q','m', 0 ), // nqm = Ndom
+ TRUETYPE_TAG('n','q','n', 0 ), // nqn = Nen
+ TRUETYPE_TAG('n','q','o', 0 ), // nqo = N'Ko
+ TRUETYPE_TAG('n','r','a', 0 ), // nra = Ngom
+ TRUETYPE_TAG('n','r','b', 0 ), // nrb = Nara
+ TRUETYPE_TAG('n','r','c', 0 ), // nrc = Noric
+ TRUETYPE_TAG('n','r','e', 0 ), // nre = Southern Rengma Naga
+ TRUETYPE_TAG('n','r','g', 0 ), // nrg = Narango
+ TRUETYPE_TAG('n','r','i', 0 ), // nri = Chokri Naga
+ TRUETYPE_TAG('n','r','l', 0 ), // nrl = Ngarluma
+ TRUETYPE_TAG('n','r','m', 0 ), // nrm = Narom
+ TRUETYPE_TAG('n','r','n', 0 ), // nrn = Norn
+ TRUETYPE_TAG('n','r','p', 0 ), // nrp = North Picene
+ TRUETYPE_TAG('n','r','r', 0 ), // nrr = Norra
+ TRUETYPE_TAG('n','r','t', 0 ), // nrt = Northern Kalapuya
+ TRUETYPE_TAG('n','r','u', 0 ), // nru = Narua
+ TRUETYPE_TAG('n','r','x', 0 ), // nrx = Ngurmbur
+ TRUETYPE_TAG('n','r','z', 0 ), // nrz = Lala
+ TRUETYPE_TAG('n','s','a', 0 ), // nsa = Sangtam Naga
+ TRUETYPE_TAG('n','s','c', 0 ), // nsc = Nshi
+ TRUETYPE_TAG('n','s','d', 0 ), // nsd = Southern Nisu
+ TRUETYPE_TAG('n','s','e', 0 ), // nse = Nsenga
+ TRUETYPE_TAG('n','s','g', 0 ), // nsg = Ngasa
+ TRUETYPE_TAG('n','s','h', 0 ), // nsh = Ngoshie
+ TRUETYPE_TAG('n','s','i', 0 ), // nsi = Nigerian Sign Language
+ TRUETYPE_TAG('n','s','k', 0 ), // nsk = Naskapi
+ TRUETYPE_TAG('n','s','l', 0 ), // nsl = Norwegian Sign Language
+ TRUETYPE_TAG('n','s','m', 0 ), // nsm = Sumi Naga
+ TRUETYPE_TAG('n','s','n', 0 ), // nsn = Nehan
+ TRUETYPE_TAG('n','s','o', 0 ), // nso = Pedi
+ TRUETYPE_TAG('n','s','p', 0 ), // nsp = Nepalese Sign Language
+ TRUETYPE_TAG('n','s','q', 0 ), // nsq = Northern Sierra Miwok
+ TRUETYPE_TAG('n','s','r', 0 ), // nsr = Maritime Sign Language
+ TRUETYPE_TAG('n','s','s', 0 ), // nss = Nali
+ TRUETYPE_TAG('n','s','t', 0 ), // nst = Tase Naga
+ TRUETYPE_TAG('n','s','u', 0 ), // nsu = Sierra Negra Nahuatl
+ TRUETYPE_TAG('n','s','v', 0 ), // nsv = Southwestern Nisu
+ TRUETYPE_TAG('n','s','w', 0 ), // nsw = Navut
+ TRUETYPE_TAG('n','s','x', 0 ), // nsx = Nsongo
+ TRUETYPE_TAG('n','s','y', 0 ), // nsy = Nasal
+ TRUETYPE_TAG('n','s','z', 0 ), // nsz = Nisenan
+ TRUETYPE_TAG('n','t','e', 0 ), // nte = Nathembo
+ TRUETYPE_TAG('n','t','i', 0 ), // nti = Natioro
+ TRUETYPE_TAG('n','t','j', 0 ), // ntj = Ngaanyatjarra
+ TRUETYPE_TAG('n','t','k', 0 ), // ntk = Ikoma-Nata-Isenye
+ TRUETYPE_TAG('n','t','m', 0 ), // ntm = Nateni
+ TRUETYPE_TAG('n','t','o', 0 ), // nto = Ntomba
+ TRUETYPE_TAG('n','t','p', 0 ), // ntp = Northern Tepehuan
+ TRUETYPE_TAG('n','t','r', 0 ), // ntr = Delo
+ TRUETYPE_TAG('n','t','s', 0 ), // nts = Natagaimas
+ TRUETYPE_TAG('n','t','u', 0 ), // ntu = Natügu
+ TRUETYPE_TAG('n','t','w', 0 ), // ntw = Nottoway
+ TRUETYPE_TAG('n','t','y', 0 ), // nty = Mantsi
+ TRUETYPE_TAG('n','t','z', 0 ), // ntz = Natanzi
+ TRUETYPE_TAG('n','u','a', 0 ), // nua = Yuaga
+ TRUETYPE_TAG('n','u','b', 0 ), // nub = Nubian languages
+ TRUETYPE_TAG('n','u','c', 0 ), // nuc = Nukuini
+ TRUETYPE_TAG('n','u','d', 0 ), // nud = Ngala
+ TRUETYPE_TAG('n','u','e', 0 ), // nue = Ngundu
+ TRUETYPE_TAG('n','u','f', 0 ), // nuf = Nusu
+ TRUETYPE_TAG('n','u','g', 0 ), // nug = Nungali
+ TRUETYPE_TAG('n','u','h', 0 ), // nuh = Ndunda
+ TRUETYPE_TAG('n','u','i', 0 ), // nui = Ngumbi
+ TRUETYPE_TAG('n','u','j', 0 ), // nuj = Nyole
+ TRUETYPE_TAG('n','u','k', 0 ), // nuk = Nuu-chah-nulth
+ TRUETYPE_TAG('n','u','l', 0 ), // nul = Nusa Laut
+ TRUETYPE_TAG('n','u','m', 0 ), // num = Niuafo'ou
+ TRUETYPE_TAG('n','u','n', 0 ), // nun = Anong
+ TRUETYPE_TAG('n','u','o', 0 ), // nuo = Nguôn
+ TRUETYPE_TAG('n','u','p', 0 ), // nup = Nupe-Nupe-Tako
+ TRUETYPE_TAG('n','u','q', 0 ), // nuq = Nukumanu
+ TRUETYPE_TAG('n','u','r', 0 ), // nur = Nukuria
+ TRUETYPE_TAG('n','u','s', 0 ), // nus = Nuer
+ TRUETYPE_TAG('n','u','t', 0 ), // nut = Nung (Viet Nam)
+ TRUETYPE_TAG('n','u','u', 0 ), // nuu = Ngbundu
+ TRUETYPE_TAG('n','u','v', 0 ), // nuv = Northern Nuni
+ TRUETYPE_TAG('n','u','w', 0 ), // nuw = Nguluwan
+ TRUETYPE_TAG('n','u','x', 0 ), // nux = Mehek
+ TRUETYPE_TAG('n','u','y', 0 ), // nuy = Nunggubuyu
+ TRUETYPE_TAG('n','u','z', 0 ), // nuz = Tlamacazapa Nahuatl
+ TRUETYPE_TAG('n','v','h', 0 ), // nvh = Nasarian
+ TRUETYPE_TAG('n','v','m', 0 ), // nvm = Namiae
+ TRUETYPE_TAG('n','w','a', 0 ), // nwa = Nawathinehena
+ TRUETYPE_TAG('n','w','b', 0 ), // nwb = Nyabwa
+ TRUETYPE_TAG('n','w','c', 0 ), // nwc = Classical Newari
+ TRUETYPE_TAG('n','w','e', 0 ), // nwe = Ngwe
+ TRUETYPE_TAG('n','w','i', 0 ), // nwi = Southwest Tanna
+ TRUETYPE_TAG('n','w','m', 0 ), // nwm = Nyamusa-Molo
+ TRUETYPE_TAG('n','w','r', 0 ), // nwr = Nawaru
+ TRUETYPE_TAG('n','w','x', 0 ), // nwx = Middle Newar
+ TRUETYPE_TAG('n','w','y', 0 ), // nwy = Nottoway-Meherrin
+ TRUETYPE_TAG('n','x','a', 0 ), // nxa = Nauete
+ TRUETYPE_TAG('n','x','d', 0 ), // nxd = Ngando (Democratic Republic of Congo)
+ TRUETYPE_TAG('n','x','e', 0 ), // nxe = Nage
+ TRUETYPE_TAG('n','x','g', 0 ), // nxg = Ngad'a
+ TRUETYPE_TAG('n','x','i', 0 ), // nxi = Nindi
+ TRUETYPE_TAG('n','x','l', 0 ), // nxl = South Nuaulu
+ TRUETYPE_TAG('n','x','m', 0 ), // nxm = Numidian
+ TRUETYPE_TAG('n','x','n', 0 ), // nxn = Ngawun
+ TRUETYPE_TAG('n','x','q', 0 ), // nxq = Naxi
+ TRUETYPE_TAG('n','x','r', 0 ), // nxr = Ninggerum
+ TRUETYPE_TAG('n','x','u', 0 ), // nxu = Narau
+ TRUETYPE_TAG('n','x','x', 0 ), // nxx = Nafri
+ TRUETYPE_TAG('n','y','b', 0 ), // nyb = Nyangbo
+ TRUETYPE_TAG('n','y','c', 0 ), // nyc = Nyanga-li
+ TRUETYPE_TAG('n','y','d', 0 ), // nyd = Nyore
+ TRUETYPE_TAG('n','y','e', 0 ), // nye = Nyengo
+ TRUETYPE_TAG('n','y','f', 0 ), // nyf = Giryama
+ TRUETYPE_TAG('n','y','g', 0 ), // nyg = Nyindu
+ TRUETYPE_TAG('n','y','h', 0 ), // nyh = Nyigina
+ TRUETYPE_TAG('n','y','i', 0 ), // nyi = Ama (Sudan)
+ TRUETYPE_TAG('n','y','j', 0 ), // nyj = Nyanga
+ TRUETYPE_TAG('n','y','k', 0 ), // nyk = Nyaneka
+ TRUETYPE_TAG('n','y','l', 0 ), // nyl = Nyeu
+ TRUETYPE_TAG('n','y','m', 0 ), // nym = Nyamwezi
+ TRUETYPE_TAG('n','y','n', 0 ), // nyn = Nyankole
+ TRUETYPE_TAG('n','y','o', 0 ), // nyo = Nyoro
+ TRUETYPE_TAG('n','y','p', 0 ), // nyp = Nyang'i
+ TRUETYPE_TAG('n','y','q', 0 ), // nyq = Nayini
+ TRUETYPE_TAG('n','y','r', 0 ), // nyr = Nyiha (Malawi)
+ TRUETYPE_TAG('n','y','s', 0 ), // nys = Nyunga
+ TRUETYPE_TAG('n','y','t', 0 ), // nyt = Nyawaygi
+ TRUETYPE_TAG('n','y','u', 0 ), // nyu = Nyungwe
+ TRUETYPE_TAG('n','y','v', 0 ), // nyv = Nyulnyul
+ TRUETYPE_TAG('n','y','w', 0 ), // nyw = Nyaw
+ TRUETYPE_TAG('n','y','x', 0 ), // nyx = Nganyaywana
+ TRUETYPE_TAG('n','y','y', 0 ), // nyy = Nyakyusa-Ngonde
+ TRUETYPE_TAG('n','z','a', 0 ), // nza = Tigon Mbembe
+ TRUETYPE_TAG('n','z','b', 0 ), // nzb = Njebi
+ TRUETYPE_TAG('n','z','i', 0 ), // nzi = Nzima
+ TRUETYPE_TAG('n','z','k', 0 ), // nzk = Nzakara
+ TRUETYPE_TAG('n','z','m', 0 ), // nzm = Zeme Naga
+ TRUETYPE_TAG('n','z','s', 0 ), // nzs = New Zealand Sign Language
+ TRUETYPE_TAG('n','z','u', 0 ), // nzu = Teke-Nzikou
+ TRUETYPE_TAG('n','z','y', 0 ), // nzy = Nzakambay
+ TRUETYPE_TAG('n','z','z', 0 ), // nzz = Nanga Dama Dogon
+ TRUETYPE_TAG('o','a','a', 0 ), // oaa = Orok
+ TRUETYPE_TAG('o','a','c', 0 ), // oac = Oroch
+ TRUETYPE_TAG('o','a','r', 0 ), // oar = Old Aramaic (up to 700 BCE)
+ TRUETYPE_TAG('o','a','v', 0 ), // oav = Old Avar
+ TRUETYPE_TAG('o','b','i', 0 ), // obi = Obispeño
+ TRUETYPE_TAG('o','b','k', 0 ), // obk = Southern Bontok
+ TRUETYPE_TAG('o','b','l', 0 ), // obl = Oblo
+ TRUETYPE_TAG('o','b','m', 0 ), // obm = Moabite
+ TRUETYPE_TAG('o','b','o', 0 ), // obo = Obo Manobo
+ TRUETYPE_TAG('o','b','r', 0 ), // obr = Old Burmese
+ TRUETYPE_TAG('o','b','t', 0 ), // obt = Old Breton
+ TRUETYPE_TAG('o','b','u', 0 ), // obu = Obulom
+ TRUETYPE_TAG('o','c','a', 0 ), // oca = Ocaina
+ TRUETYPE_TAG('o','c','h', 0 ), // och = Old Chinese
+ TRUETYPE_TAG('o','c','o', 0 ), // oco = Old Cornish
+ TRUETYPE_TAG('o','c','u', 0 ), // ocu = Atzingo Matlatzinca
+ TRUETYPE_TAG('o','d','a', 0 ), // oda = Odut
+ TRUETYPE_TAG('o','d','k', 0 ), // odk = Od
+ TRUETYPE_TAG('o','d','t', 0 ), // odt = Old Dutch
+ TRUETYPE_TAG('o','d','u', 0 ), // odu = Odual
+ TRUETYPE_TAG('o','f','o', 0 ), // ofo = Ofo
+ TRUETYPE_TAG('o','f','s', 0 ), // ofs = Old Frisian
+ TRUETYPE_TAG('o','f','u', 0 ), // ofu = Efutop
+ TRUETYPE_TAG('o','g','b', 0 ), // ogb = Ogbia
+ TRUETYPE_TAG('o','g','c', 0 ), // ogc = Ogbah
+ TRUETYPE_TAG('o','g','e', 0 ), // oge = Old Georgian
+ TRUETYPE_TAG('o','g','g', 0 ), // ogg = Ogbogolo
+ TRUETYPE_TAG('o','g','o', 0 ), // ogo = Khana
+ TRUETYPE_TAG('o','g','u', 0 ), // ogu = Ogbronuagum
+ TRUETYPE_TAG('o','h','t', 0 ), // oht = Old Hittite
+ TRUETYPE_TAG('o','h','u', 0 ), // ohu = Old Hungarian
+ TRUETYPE_TAG('o','i','a', 0 ), // oia = Oirata
+ TRUETYPE_TAG('o','i','n', 0 ), // oin = Inebu One
+ TRUETYPE_TAG('o','j','b', 0 ), // ojb = Northwestern Ojibwa
+ TRUETYPE_TAG('o','j','c', 0 ), // ojc = Central Ojibwa
+ TRUETYPE_TAG('o','j','g', 0 ), // ojg = Eastern Ojibwa
+ TRUETYPE_TAG('o','j','p', 0 ), // ojp = Old Japanese
+ TRUETYPE_TAG('o','j','s', 0 ), // ojs = Severn Ojibwa
+ TRUETYPE_TAG('o','j','v', 0 ), // ojv = Ontong Java
+ TRUETYPE_TAG('o','j','w', 0 ), // ojw = Western Ojibwa
+ TRUETYPE_TAG('o','k','a', 0 ), // oka = Okanagan
+ TRUETYPE_TAG('o','k','b', 0 ), // okb = Okobo
+ TRUETYPE_TAG('o','k','d', 0 ), // okd = Okodia
+ TRUETYPE_TAG('o','k','e', 0 ), // oke = Okpe (Southwestern Edo)
+ TRUETYPE_TAG('o','k','h', 0 ), // okh = Koresh-e Rostam
+ TRUETYPE_TAG('o','k','i', 0 ), // oki = Okiek
+ TRUETYPE_TAG('o','k','j', 0 ), // okj = Oko-Juwoi
+ TRUETYPE_TAG('o','k','k', 0 ), // okk = Kwamtim One
+ TRUETYPE_TAG('o','k','l', 0 ), // okl = Old Kentish Sign Language
+ TRUETYPE_TAG('o','k','m', 0 ), // okm = Middle Korean (10th-16th cent.)
+ TRUETYPE_TAG('o','k','n', 0 ), // okn = Oki-No-Erabu
+ TRUETYPE_TAG('o','k','o', 0 ), // oko = Old Korean (3rd-9th cent.)
+ TRUETYPE_TAG('o','k','r', 0 ), // okr = Kirike
+ TRUETYPE_TAG('o','k','s', 0 ), // oks = Oko-Eni-Osayen
+ TRUETYPE_TAG('o','k','u', 0 ), // oku = Oku
+ TRUETYPE_TAG('o','k','v', 0 ), // okv = Orokaiva
+ TRUETYPE_TAG('o','k','x', 0 ), // okx = Okpe (Northwestern Edo)
+ TRUETYPE_TAG('o','l','a', 0 ), // ola = Walungge
+ TRUETYPE_TAG('o','l','d', 0 ), // old = Mochi
+ TRUETYPE_TAG('o','l','e', 0 ), // ole = Olekha
+ TRUETYPE_TAG('o','l','m', 0 ), // olm = Oloma
+ TRUETYPE_TAG('o','l','o', 0 ), // olo = Livvi
+ TRUETYPE_TAG('o','l','r', 0 ), // olr = Olrat
+ TRUETYPE_TAG('o','m','a', 0 ), // oma = Omaha-Ponca
+ TRUETYPE_TAG('o','m','b', 0 ), // omb = East Ambae
+ TRUETYPE_TAG('o','m','c', 0 ), // omc = Mochica
+ TRUETYPE_TAG('o','m','e', 0 ), // ome = Omejes
+ TRUETYPE_TAG('o','m','g', 0 ), // omg = Omagua
+ TRUETYPE_TAG('o','m','i', 0 ), // omi = Omi
+ TRUETYPE_TAG('o','m','k', 0 ), // omk = Omok
+ TRUETYPE_TAG('o','m','l', 0 ), // oml = Ombo
+ TRUETYPE_TAG('o','m','n', 0 ), // omn = Minoan
+ TRUETYPE_TAG('o','m','o', 0 ), // omo = Utarmbung
+ TRUETYPE_TAG('o','m','p', 0 ), // omp = Old Manipuri
+ TRUETYPE_TAG('o','m','q', 0 ), // omq = Oto-Manguean languages
+ TRUETYPE_TAG('o','m','r', 0 ), // omr = Old Marathi
+ TRUETYPE_TAG('o','m','t', 0 ), // omt = Omotik
+ TRUETYPE_TAG('o','m','u', 0 ), // omu = Omurano
+ TRUETYPE_TAG('o','m','v', 0 ), // omv = Omotic languages
+ TRUETYPE_TAG('o','m','w', 0 ), // omw = South Tairora
+ TRUETYPE_TAG('o','m','x', 0 ), // omx = Old Mon
+ TRUETYPE_TAG('o','n','a', 0 ), // ona = Ona
+ TRUETYPE_TAG('o','n','b', 0 ), // onb = Lingao
+ TRUETYPE_TAG('o','n','e', 0 ), // one = Oneida
+ TRUETYPE_TAG('o','n','g', 0 ), // ong = Olo
+ TRUETYPE_TAG('o','n','i', 0 ), // oni = Onin
+ TRUETYPE_TAG('o','n','j', 0 ), // onj = Onjob
+ TRUETYPE_TAG('o','n','k', 0 ), // onk = Kabore One
+ TRUETYPE_TAG('o','n','n', 0 ), // onn = Onobasulu
+ TRUETYPE_TAG('o','n','o', 0 ), // ono = Onondaga
+ TRUETYPE_TAG('o','n','p', 0 ), // onp = Sartang
+ TRUETYPE_TAG('o','n','r', 0 ), // onr = Northern One
+ TRUETYPE_TAG('o','n','s', 0 ), // ons = Ono
+ TRUETYPE_TAG('o','n','t', 0 ), // ont = Ontenu
+ TRUETYPE_TAG('o','n','u', 0 ), // onu = Unua
+ TRUETYPE_TAG('o','n','w', 0 ), // onw = Old Nubian
+ TRUETYPE_TAG('o','n','x', 0 ), // onx = Onin Based Pidgin
+ TRUETYPE_TAG('o','o','d', 0 ), // ood = Tohono O'odham
+ TRUETYPE_TAG('o','o','g', 0 ), // oog = Ong
+ TRUETYPE_TAG('o','o','n', 0 ), // oon = Önge
+ TRUETYPE_TAG('o','o','r', 0 ), // oor = Oorlams
+ TRUETYPE_TAG('o','o','s', 0 ), // oos = Old Ossetic
+ TRUETYPE_TAG('o','p','a', 0 ), // opa = Okpamheri
+ TRUETYPE_TAG('o','p','k', 0 ), // opk = Kopkaka
+ TRUETYPE_TAG('o','p','m', 0 ), // opm = Oksapmin
+ TRUETYPE_TAG('o','p','o', 0 ), // opo = Opao
+ TRUETYPE_TAG('o','p','t', 0 ), // opt = Opata
+ TRUETYPE_TAG('o','p','y', 0 ), // opy = Ofayé
+ TRUETYPE_TAG('o','r','a', 0 ), // ora = Oroha
+ TRUETYPE_TAG('o','r','c', 0 ), // orc = Orma
+ TRUETYPE_TAG('o','r','e', 0 ), // ore = Orejón
+ TRUETYPE_TAG('o','r','g', 0 ), // org = Oring
+ TRUETYPE_TAG('o','r','h', 0 ), // orh = Oroqen
+ TRUETYPE_TAG('o','r','n', 0 ), // orn = Orang Kanaq
+ TRUETYPE_TAG('o','r','o', 0 ), // oro = Orokolo
+ TRUETYPE_TAG('o','r','r', 0 ), // orr = Oruma
+ TRUETYPE_TAG('o','r','s', 0 ), // ors = Orang Seletar
+ TRUETYPE_TAG('o','r','t', 0 ), // ort = Adivasi Oriya
+ TRUETYPE_TAG('o','r','u', 0 ), // oru = Ormuri
+ TRUETYPE_TAG('o','r','v', 0 ), // orv = Old Russian
+ TRUETYPE_TAG('o','r','w', 0 ), // orw = Oro Win
+ TRUETYPE_TAG('o','r','x', 0 ), // orx = Oro
+ TRUETYPE_TAG('o','r','z', 0 ), // orz = Ormu
+ TRUETYPE_TAG('o','s','a', 0 ), // osa = Osage
+ TRUETYPE_TAG('o','s','c', 0 ), // osc = Oscan
+ TRUETYPE_TAG('o','s','i', 0 ), // osi = Osing
+ TRUETYPE_TAG('o','s','o', 0 ), // oso = Ososo
+ TRUETYPE_TAG('o','s','p', 0 ), // osp = Old Spanish
+ TRUETYPE_TAG('o','s','t', 0 ), // ost = Osatu
+ TRUETYPE_TAG('o','s','u', 0 ), // osu = Southern One
+ TRUETYPE_TAG('o','s','x', 0 ), // osx = Old Saxon
+ TRUETYPE_TAG('o','t','a', 0 ), // ota = Ottoman Turkish (1500-1928)
+ TRUETYPE_TAG('o','t','b', 0 ), // otb = Old Tibetan
+ TRUETYPE_TAG('o','t','d', 0 ), // otd = Ot Danum
+ TRUETYPE_TAG('o','t','e', 0 ), // ote = Mezquital Otomi
+ TRUETYPE_TAG('o','t','i', 0 ), // oti = Oti
+ TRUETYPE_TAG('o','t','k', 0 ), // otk = Old Turkish
+ TRUETYPE_TAG('o','t','l', 0 ), // otl = Tilapa Otomi
+ TRUETYPE_TAG('o','t','m', 0 ), // otm = Eastern Highland Otomi
+ TRUETYPE_TAG('o','t','n', 0 ), // otn = Tenango Otomi
+ TRUETYPE_TAG('o','t','o', 0 ), // oto = Otomian languages
+ TRUETYPE_TAG('o','t','q', 0 ), // otq = Querétaro Otomi
+ TRUETYPE_TAG('o','t','r', 0 ), // otr = Otoro
+ TRUETYPE_TAG('o','t','s', 0 ), // ots = Estado de México Otomi
+ TRUETYPE_TAG('o','t','t', 0 ), // ott = Temoaya Otomi
+ TRUETYPE_TAG('o','t','u', 0 ), // otu = Otuke
+ TRUETYPE_TAG('o','t','w', 0 ), // otw = Ottawa
+ TRUETYPE_TAG('o','t','x', 0 ), // otx = Texcatepec Otomi
+ TRUETYPE_TAG('o','t','y', 0 ), // oty = Old Tamil
+ TRUETYPE_TAG('o','t','z', 0 ), // otz = Ixtenco Otomi
+ TRUETYPE_TAG('o','u','a', 0 ), // oua = Tagargrent
+ TRUETYPE_TAG('o','u','b', 0 ), // oub = Glio-Oubi
+ TRUETYPE_TAG('o','u','e', 0 ), // oue = Oune
+ TRUETYPE_TAG('o','u','i', 0 ), // oui = Old Uighur
+ TRUETYPE_TAG('o','u','m', 0 ), // oum = Ouma
+ TRUETYPE_TAG('o','u','n', 0 ), // oun = !O!ung
+ TRUETYPE_TAG('o','w','i', 0 ), // owi = Owiniga
+ TRUETYPE_TAG('o','w','l', 0 ), // owl = Old Welsh
+ TRUETYPE_TAG('o','y','b', 0 ), // oyb = Oy
+ TRUETYPE_TAG('o','y','d', 0 ), // oyd = Oyda
+ TRUETYPE_TAG('o','y','m', 0 ), // oym = Wayampi
+ TRUETYPE_TAG('o','y','y', 0 ), // oyy = Oya'oya
+ TRUETYPE_TAG('o','z','m', 0 ), // ozm = Koonzime
+ TRUETYPE_TAG('p','a','a', 0 ), // paa = Papuan languages
+ TRUETYPE_TAG('p','a','b', 0 ), // pab = Parecís
+ TRUETYPE_TAG('p','a','c', 0 ), // pac = Pacoh
+ TRUETYPE_TAG('p','a','d', 0 ), // pad = Paumarí
+ TRUETYPE_TAG('p','a','e', 0 ), // pae = Pagibete
+ TRUETYPE_TAG('p','a','f', 0 ), // paf = Paranawát
+ TRUETYPE_TAG('p','a','g', 0 ), // pag = Pangasinan
+ TRUETYPE_TAG('p','a','h', 0 ), // pah = Tenharim
+ TRUETYPE_TAG('p','a','i', 0 ), // pai = Pe
+ TRUETYPE_TAG('p','a','k', 0 ), // pak = Parakanã
+ TRUETYPE_TAG('p','a','l', 0 ), // pal = Pahlavi
+ TRUETYPE_TAG('p','a','m', 0 ), // pam = Pampanga
+ TRUETYPE_TAG('p','a','o', 0 ), // pao = Northern Paiute
+ TRUETYPE_TAG('p','a','p', 0 ), // pap = Papiamento
+ TRUETYPE_TAG('p','a','q', 0 ), // paq = Parya
+ TRUETYPE_TAG('p','a','r', 0 ), // par = Panamint
+ TRUETYPE_TAG('p','a','s', 0 ), // pas = Papasena
+ TRUETYPE_TAG('p','a','t', 0 ), // pat = Papitalai
+ TRUETYPE_TAG('p','a','u', 0 ), // pau = Palauan
+ TRUETYPE_TAG('p','a','v', 0 ), // pav = Pakaásnovos
+ TRUETYPE_TAG('p','a','w', 0 ), // paw = Pawnee
+ TRUETYPE_TAG('p','a','x', 0 ), // pax = Pankararé
+ TRUETYPE_TAG('p','a','y', 0 ), // pay = Pech
+ TRUETYPE_TAG('p','a','z', 0 ), // paz = Pankararú
+ TRUETYPE_TAG('p','b','b', 0 ), // pbb = Páez
+ TRUETYPE_TAG('p','b','c', 0 ), // pbc = Patamona
+ TRUETYPE_TAG('p','b','e', 0 ), // pbe = Mezontla Popoloca
+ TRUETYPE_TAG('p','b','f', 0 ), // pbf = Coyotepec Popoloca
+ TRUETYPE_TAG('p','b','g', 0 ), // pbg = Paraujano
+ TRUETYPE_TAG('p','b','h', 0 ), // pbh = E'ñapa Woromaipu
+ TRUETYPE_TAG('p','b','i', 0 ), // pbi = Parkwa
+ TRUETYPE_TAG('p','b','l', 0 ), // pbl = Mak (Nigeria)
+ TRUETYPE_TAG('p','b','n', 0 ), // pbn = Kpasam
+ TRUETYPE_TAG('p','b','o', 0 ), // pbo = Papel
+ TRUETYPE_TAG('p','b','p', 0 ), // pbp = Badyara
+ TRUETYPE_TAG('p','b','r', 0 ), // pbr = Pangwa
+ TRUETYPE_TAG('p','b','s', 0 ), // pbs = Central Pame
+ TRUETYPE_TAG('p','b','t', 0 ), // pbt = Southern Pashto
+ TRUETYPE_TAG('p','b','u', 0 ), // pbu = Northern Pashto
+ TRUETYPE_TAG('p','b','v', 0 ), // pbv = Pnar
+ TRUETYPE_TAG('p','b','y', 0 ), // pby = Pyu
+ TRUETYPE_TAG('p','b','z', 0 ), // pbz = Palu
+ TRUETYPE_TAG('p','c','a', 0 ), // pca = Santa Inés Ahuatempan Popoloca
+ TRUETYPE_TAG('p','c','b', 0 ), // pcb = Pear
+ TRUETYPE_TAG('p','c','c', 0 ), // pcc = Bouyei
+ TRUETYPE_TAG('p','c','d', 0 ), // pcd = Picard
+ TRUETYPE_TAG('p','c','e', 0 ), // pce = Ruching Palaung
+ TRUETYPE_TAG('p','c','f', 0 ), // pcf = Paliyan
+ TRUETYPE_TAG('p','c','g', 0 ), // pcg = Paniya
+ TRUETYPE_TAG('p','c','h', 0 ), // pch = Pardhan
+ TRUETYPE_TAG('p','c','i', 0 ), // pci = Duruwa
+ TRUETYPE_TAG('p','c','j', 0 ), // pcj = Parenga
+ TRUETYPE_TAG('p','c','k', 0 ), // pck = Paite Chin
+ TRUETYPE_TAG('p','c','l', 0 ), // pcl = Pardhi
+ TRUETYPE_TAG('p','c','m', 0 ), // pcm = Nigerian Pidgin
+ TRUETYPE_TAG('p','c','n', 0 ), // pcn = Piti
+ TRUETYPE_TAG('p','c','p', 0 ), // pcp = Pacahuara
+ TRUETYPE_TAG('p','c','r', 0 ), // pcr = Panang
+ TRUETYPE_TAG('p','c','w', 0 ), // pcw = Pyapun
+ TRUETYPE_TAG('p','d','a', 0 ), // pda = Anam
+ TRUETYPE_TAG('p','d','c', 0 ), // pdc = Pennsylvania German
+ TRUETYPE_TAG('p','d','i', 0 ), // pdi = Pa Di
+ TRUETYPE_TAG('p','d','n', 0 ), // pdn = Podena
+ TRUETYPE_TAG('p','d','o', 0 ), // pdo = Padoe
+ TRUETYPE_TAG('p','d','t', 0 ), // pdt = Plautdietsch
+ TRUETYPE_TAG('p','d','u', 0 ), // pdu = Kayan
+ TRUETYPE_TAG('p','e','a', 0 ), // pea = Peranakan Indonesian
+ TRUETYPE_TAG('p','e','b', 0 ), // peb = Eastern Pomo
+ TRUETYPE_TAG('p','e','d', 0 ), // ped = Mala (Papua New Guinea)
+ TRUETYPE_TAG('p','e','e', 0 ), // pee = Taje
+ TRUETYPE_TAG('p','e','f', 0 ), // pef = Northeastern Pomo
+ TRUETYPE_TAG('p','e','g', 0 ), // peg = Pengo
+ TRUETYPE_TAG('p','e','h', 0 ), // peh = Bonan
+ TRUETYPE_TAG('p','e','i', 0 ), // pei = Chichimeca-Jonaz
+ TRUETYPE_TAG('p','e','j', 0 ), // pej = Northern Pomo
+ TRUETYPE_TAG('p','e','k', 0 ), // pek = Penchal
+ TRUETYPE_TAG('p','e','l', 0 ), // pel = Pekal
+ TRUETYPE_TAG('p','e','m', 0 ), // pem = Phende
+ TRUETYPE_TAG('p','e','o', 0 ), // peo = Old Persian (ca. 600-400 B.C.)
+ TRUETYPE_TAG('p','e','p', 0 ), // pep = Kunja
+ TRUETYPE_TAG('p','e','q', 0 ), // peq = Southern Pomo
+ TRUETYPE_TAG('p','e','s', 0 ), // pes = Iranian Persian
+ TRUETYPE_TAG('p','e','v', 0 ), // pev = Pémono
+ TRUETYPE_TAG('p','e','x', 0 ), // pex = Petats
+ TRUETYPE_TAG('p','e','y', 0 ), // pey = Petjo
+ TRUETYPE_TAG('p','e','z', 0 ), // pez = Eastern Penan
+ TRUETYPE_TAG('p','f','a', 0 ), // pfa = Pááfang
+ TRUETYPE_TAG('p','f','e', 0 ), // pfe = Peere
+ TRUETYPE_TAG('p','f','l', 0 ), // pfl = Pfaelzisch
+ TRUETYPE_TAG('p','g','a', 0 ), // pga = Sudanese Creole Arabic
+ TRUETYPE_TAG('p','g','g', 0 ), // pgg = Pangwali
+ TRUETYPE_TAG('p','g','i', 0 ), // pgi = Pagi
+ TRUETYPE_TAG('p','g','k', 0 ), // pgk = Rerep
+ TRUETYPE_TAG('p','g','l', 0 ), // pgl = Primitive Irish
+ TRUETYPE_TAG('p','g','n', 0 ), // pgn = Paelignian
+ TRUETYPE_TAG('p','g','s', 0 ), // pgs = Pangseng
+ TRUETYPE_TAG('p','g','u', 0 ), // pgu = Pagu
+ TRUETYPE_TAG('p','g','y', 0 ), // pgy = Pongyong
+ TRUETYPE_TAG('p','h','a', 0 ), // pha = Pa-Hng
+ TRUETYPE_TAG('p','h','d', 0 ), // phd = Phudagi
+ TRUETYPE_TAG('p','h','g', 0 ), // phg = Phuong
+ TRUETYPE_TAG('p','h','h', 0 ), // phh = Phukha
+ TRUETYPE_TAG('p','h','i', 0 ), // phi = Philippine languages
+ TRUETYPE_TAG('p','h','k', 0 ), // phk = Phake
+ TRUETYPE_TAG('p','h','l', 0 ), // phl = Phalura
+ TRUETYPE_TAG('p','h','m', 0 ), // phm = Phimbi
+ TRUETYPE_TAG('p','h','n', 0 ), // phn = Phoenician
+ TRUETYPE_TAG('p','h','o', 0 ), // pho = Phunoi
+ TRUETYPE_TAG('p','h','q', 0 ), // phq = Phana'
+ TRUETYPE_TAG('p','h','r', 0 ), // phr = Pahari-Potwari
+ TRUETYPE_TAG('p','h','t', 0 ), // pht = Phu Thai
+ TRUETYPE_TAG('p','h','u', 0 ), // phu = Phuan
+ TRUETYPE_TAG('p','h','v', 0 ), // phv = Pahlavani
+ TRUETYPE_TAG('p','h','w', 0 ), // phw = Phangduwali
+ TRUETYPE_TAG('p','i','a', 0 ), // pia = Pima Bajo
+ TRUETYPE_TAG('p','i','b', 0 ), // pib = Yine
+ TRUETYPE_TAG('p','i','c', 0 ), // pic = Pinji
+ TRUETYPE_TAG('p','i','d', 0 ), // pid = Piaroa
+ TRUETYPE_TAG('p','i','e', 0 ), // pie = Piro
+ TRUETYPE_TAG('p','i','f', 0 ), // pif = Pingelapese
+ TRUETYPE_TAG('p','i','g', 0 ), // pig = Pisabo
+ TRUETYPE_TAG('p','i','h', 0 ), // pih = Pitcairn-Norfolk
+ TRUETYPE_TAG('p','i','i', 0 ), // pii = Pini
+ TRUETYPE_TAG('p','i','j', 0 ), // pij = Pijao
+ TRUETYPE_TAG('p','i','l', 0 ), // pil = Yom
+ TRUETYPE_TAG('p','i','m', 0 ), // pim = Powhatan
+ TRUETYPE_TAG('p','i','n', 0 ), // pin = Piame
+ TRUETYPE_TAG('p','i','o', 0 ), // pio = Piapoco
+ TRUETYPE_TAG('p','i','p', 0 ), // pip = Pero
+ TRUETYPE_TAG('p','i','r', 0 ), // pir = Piratapuyo
+ TRUETYPE_TAG('p','i','s', 0 ), // pis = Pijin
+ TRUETYPE_TAG('p','i','t', 0 ), // pit = Pitta Pitta
+ TRUETYPE_TAG('p','i','u', 0 ), // piu = Pintupi-Luritja
+ TRUETYPE_TAG('p','i','v', 0 ), // piv = Pileni
+ TRUETYPE_TAG('p','i','w', 0 ), // piw = Pimbwe
+ TRUETYPE_TAG('p','i','x', 0 ), // pix = Piu
+ TRUETYPE_TAG('p','i','y', 0 ), // piy = Piya-Kwonci
+ TRUETYPE_TAG('p','i','z', 0 ), // piz = Pije
+ TRUETYPE_TAG('p','j','t', 0 ), // pjt = Pitjantjatjara
+ TRUETYPE_TAG('p','k','a', 0 ), // pka = ArdhamÄgadhÄ« PrÄkrit
+ TRUETYPE_TAG('p','k','b', 0 ), // pkb = Pokomo
+ TRUETYPE_TAG('p','k','c', 0 ), // pkc = Paekche
+ TRUETYPE_TAG('p','k','g', 0 ), // pkg = Pak-Tong
+ TRUETYPE_TAG('p','k','h', 0 ), // pkh = Pankhu
+ TRUETYPE_TAG('p','k','n', 0 ), // pkn = Pakanha
+ TRUETYPE_TAG('p','k','o', 0 ), // pko = Pökoot
+ TRUETYPE_TAG('p','k','p', 0 ), // pkp = Pukapuka
+ TRUETYPE_TAG('p','k','r', 0 ), // pkr = Attapady Kurumba
+ TRUETYPE_TAG('p','k','s', 0 ), // pks = Pakistan Sign Language
+ TRUETYPE_TAG('p','k','t', 0 ), // pkt = Maleng
+ TRUETYPE_TAG('p','k','u', 0 ), // pku = Paku
+ TRUETYPE_TAG('p','l','a', 0 ), // pla = Miani
+ TRUETYPE_TAG('p','l','b', 0 ), // plb = Polonombauk
+ TRUETYPE_TAG('p','l','c', 0 ), // plc = Central Palawano
+ TRUETYPE_TAG('p','l','d', 0 ), // pld = Polari
+ TRUETYPE_TAG('p','l','e', 0 ), // ple = Palu'e
+ TRUETYPE_TAG('p','l','f', 0 ), // plf = Central Malayo-Polynesian languages
+ TRUETYPE_TAG('p','l','g', 0 ), // plg = Pilagá
+ TRUETYPE_TAG('p','l','h', 0 ), // plh = Paulohi
+ TRUETYPE_TAG('p','l','j', 0 ), // plj = Polci
+ TRUETYPE_TAG('p','l','k', 0 ), // plk = Kohistani Shina
+ TRUETYPE_TAG('p','l','l', 0 ), // pll = Shwe Palaung
+ TRUETYPE_TAG('p','l','n', 0 ), // pln = Palenquero
+ TRUETYPE_TAG('p','l','o', 0 ), // plo = Oluta Popoluca
+ TRUETYPE_TAG('p','l','p', 0 ), // plp = Palpa
+ TRUETYPE_TAG('p','l','q', 0 ), // plq = Palaic
+ TRUETYPE_TAG('p','l','r', 0 ), // plr = Palaka Senoufo
+ TRUETYPE_TAG('p','l','s', 0 ), // pls = San Marcos Tlalcoyalco Popoloca
+ TRUETYPE_TAG('p','l','t', 0 ), // plt = Plateau Malagasy
+ TRUETYPE_TAG('p','l','u', 0 ), // plu = Palikúr
+ TRUETYPE_TAG('p','l','v', 0 ), // plv = Southwest Palawano
+ TRUETYPE_TAG('p','l','w', 0 ), // plw = Brooke's Point Palawano
+ TRUETYPE_TAG('p','l','y', 0 ), // ply = Bolyu
+ TRUETYPE_TAG('p','l','z', 0 ), // plz = Paluan
+ TRUETYPE_TAG('p','m','a', 0 ), // pma = Paama
+ TRUETYPE_TAG('p','m','b', 0 ), // pmb = Pambia
+ TRUETYPE_TAG('p','m','c', 0 ), // pmc = Palumata
+ TRUETYPE_TAG('p','m','e', 0 ), // pme = Pwaamei
+ TRUETYPE_TAG('p','m','f', 0 ), // pmf = Pamona
+ TRUETYPE_TAG('p','m','h', 0 ), // pmh = MÄhÄrÄṣṭri PrÄkrit
+ TRUETYPE_TAG('p','m','i', 0 ), // pmi = Northern Pumi
+ TRUETYPE_TAG('p','m','j', 0 ), // pmj = Southern Pumi
+ TRUETYPE_TAG('p','m','k', 0 ), // pmk = Pamlico
+ TRUETYPE_TAG('p','m','l', 0 ), // pml = Lingua Franca
+ TRUETYPE_TAG('p','m','m', 0 ), // pmm = Pomo
+ TRUETYPE_TAG('p','m','n', 0 ), // pmn = Pam
+ TRUETYPE_TAG('p','m','o', 0 ), // pmo = Pom
+ TRUETYPE_TAG('p','m','q', 0 ), // pmq = Northern Pame
+ TRUETYPE_TAG('p','m','r', 0 ), // pmr = Paynamar
+ TRUETYPE_TAG('p','m','s', 0 ), // pms = Piemontese
+ TRUETYPE_TAG('p','m','t', 0 ), // pmt = Tuamotuan
+ TRUETYPE_TAG('p','m','u', 0 ), // pmu = Mirpur Panjabi
+ TRUETYPE_TAG('p','m','w', 0 ), // pmw = Plains Miwok
+ TRUETYPE_TAG('p','m','x', 0 ), // pmx = Poumei Naga
+ TRUETYPE_TAG('p','m','y', 0 ), // pmy = Papuan Malay
+ TRUETYPE_TAG('p','m','z', 0 ), // pmz = Southern Pame
+ TRUETYPE_TAG('p','n','a', 0 ), // pna = Punan Bah-Biau
+ TRUETYPE_TAG('p','n','b', 0 ), // pnb = Western Panjabi
+ TRUETYPE_TAG('p','n','c', 0 ), // pnc = Pannei
+ TRUETYPE_TAG('p','n','e', 0 ), // pne = Western Penan
+ TRUETYPE_TAG('p','n','g', 0 ), // png = Pongu
+ TRUETYPE_TAG('p','n','h', 0 ), // pnh = Penrhyn
+ TRUETYPE_TAG('p','n','i', 0 ), // pni = Aoheng
+ TRUETYPE_TAG('p','n','m', 0 ), // pnm = Punan Batu 1
+ TRUETYPE_TAG('p','n','n', 0 ), // pnn = Pinai-Hagahai
+ TRUETYPE_TAG('p','n','o', 0 ), // pno = Panobo
+ TRUETYPE_TAG('p','n','p', 0 ), // pnp = Pancana
+ TRUETYPE_TAG('p','n','q', 0 ), // pnq = Pana (Burkina Faso)
+ TRUETYPE_TAG('p','n','r', 0 ), // pnr = Panim
+ TRUETYPE_TAG('p','n','s', 0 ), // pns = Ponosakan
+ TRUETYPE_TAG('p','n','t', 0 ), // pnt = Pontic
+ TRUETYPE_TAG('p','n','u', 0 ), // pnu = Jiongnai Bunu
+ TRUETYPE_TAG('p','n','v', 0 ), // pnv = Pinigura
+ TRUETYPE_TAG('p','n','w', 0 ), // pnw = Panytyima
+ TRUETYPE_TAG('p','n','x', 0 ), // pnx = Phong-Kniang
+ TRUETYPE_TAG('p','n','y', 0 ), // pny = Pinyin
+ TRUETYPE_TAG('p','n','z', 0 ), // pnz = Pana (Central African Republic)
+ TRUETYPE_TAG('p','o','c', 0 ), // poc = Poqomam
+ TRUETYPE_TAG('p','o','d', 0 ), // pod = Ponares
+ TRUETYPE_TAG('p','o','e', 0 ), // poe = San Juan Atzingo Popoloca
+ TRUETYPE_TAG('p','o','f', 0 ), // pof = Poke
+ TRUETYPE_TAG('p','o','g', 0 ), // pog = Potiguára
+ TRUETYPE_TAG('p','o','h', 0 ), // poh = Poqomchi'
+ TRUETYPE_TAG('p','o','i', 0 ), // poi = Highland Popoluca
+ TRUETYPE_TAG('p','o','k', 0 ), // pok = Pokangá
+ TRUETYPE_TAG('p','o','m', 0 ), // pom = Southeastern Pomo
+ TRUETYPE_TAG('p','o','n', 0 ), // pon = Pohnpeian
+ TRUETYPE_TAG('p','o','o', 0 ), // poo = Central Pomo
+ TRUETYPE_TAG('p','o','p', 0 ), // pop = Pwapwa
+ TRUETYPE_TAG('p','o','q', 0 ), // poq = Texistepec Popoluca
+ TRUETYPE_TAG('p','o','s', 0 ), // pos = Sayula Popoluca
+ TRUETYPE_TAG('p','o','t', 0 ), // pot = Potawatomi
+ TRUETYPE_TAG('p','o','v', 0 ), // pov = Upper Guinea Crioulo
+ TRUETYPE_TAG('p','o','w', 0 ), // pow = San Felipe Otlaltepec Popoloca
+ TRUETYPE_TAG('p','o','x', 0 ), // pox = Polabian
+ TRUETYPE_TAG('p','o','y', 0 ), // poy = Pogolo
+ TRUETYPE_TAG('p','o','z', 0 ), // poz = Malayo-Polynesian languages
+ TRUETYPE_TAG('p','p','a', 0 ), // ppa = Pao
+ TRUETYPE_TAG('p','p','e', 0 ), // ppe = Papi
+ TRUETYPE_TAG('p','p','i', 0 ), // ppi = Paipai
+ TRUETYPE_TAG('p','p','k', 0 ), // ppk = Uma
+ TRUETYPE_TAG('p','p','l', 0 ), // ppl = Pipil
+ TRUETYPE_TAG('p','p','m', 0 ), // ppm = Papuma
+ TRUETYPE_TAG('p','p','n', 0 ), // ppn = Papapana
+ TRUETYPE_TAG('p','p','o', 0 ), // ppo = Folopa
+ TRUETYPE_TAG('p','p','p', 0 ), // ppp = Pelende
+ TRUETYPE_TAG('p','p','q', 0 ), // ppq = Pei
+ TRUETYPE_TAG('p','p','r', 0 ), // ppr = Piru
+ TRUETYPE_TAG('p','p','s', 0 ), // pps = San Luís Temalacayuca Popoloca
+ TRUETYPE_TAG('p','p','t', 0 ), // ppt = Pare
+ TRUETYPE_TAG('p','p','u', 0 ), // ppu = Papora
+ TRUETYPE_TAG('p','q','a', 0 ), // pqa = Pa'a
+ TRUETYPE_TAG('p','q','e', 0 ), // pqe = Eastern Malayo-Polynesian languages
+ TRUETYPE_TAG('p','q','m', 0 ), // pqm = Malecite-Passamaquoddy
+ TRUETYPE_TAG('p','q','w', 0 ), // pqw = Western Malayo-Polynesian languages
+ TRUETYPE_TAG('p','r','a', 0 ), // pra = Prakrit languages
+ TRUETYPE_TAG('p','r','b', 0 ), // prb = Lua'
+ TRUETYPE_TAG('p','r','c', 0 ), // prc = Parachi
+ TRUETYPE_TAG('p','r','d', 0 ), // prd = Parsi-Dari
+ TRUETYPE_TAG('p','r','e', 0 ), // pre = Principense
+ TRUETYPE_TAG('p','r','f', 0 ), // prf = Paranan
+ TRUETYPE_TAG('p','r','g', 0 ), // prg = Prussian
+ TRUETYPE_TAG('p','r','h', 0 ), // prh = Porohanon
+ TRUETYPE_TAG('p','r','i', 0 ), // pri = Paicî
+ TRUETYPE_TAG('p','r','k', 0 ), // prk = Parauk
+ TRUETYPE_TAG('p','r','l', 0 ), // prl = Peruvian Sign Language
+ TRUETYPE_TAG('p','r','m', 0 ), // prm = Kibiri
+ TRUETYPE_TAG('p','r','n', 0 ), // prn = Prasuni
+ TRUETYPE_TAG('p','r','o', 0 ), // pro = Old Provençal (to 1500)
+ TRUETYPE_TAG('p','r','p', 0 ), // prp = Parsi
+ TRUETYPE_TAG('p','r','q', 0 ), // prq = Ashéninka Perené
+ TRUETYPE_TAG('p','r','r', 0 ), // prr = Puri
+ TRUETYPE_TAG('p','r','s', 0 ), // prs = Dari
+ TRUETYPE_TAG('p','r','t', 0 ), // prt = Phai
+ TRUETYPE_TAG('p','r','u', 0 ), // pru = Puragi
+ TRUETYPE_TAG('p','r','w', 0 ), // prw = Parawen
+ TRUETYPE_TAG('p','r','x', 0 ), // prx = Purik
+ TRUETYPE_TAG('p','r','y', 0 ), // pry = Pray 3
+ TRUETYPE_TAG('p','r','z', 0 ), // prz = Providencia Sign Language
+ TRUETYPE_TAG('p','s','a', 0 ), // psa = Asue Awyu
+ TRUETYPE_TAG('p','s','c', 0 ), // psc = Persian Sign Language
+ TRUETYPE_TAG('p','s','d', 0 ), // psd = Plains Indian Sign Language
+ TRUETYPE_TAG('p','s','e', 0 ), // pse = Central Malay
+ TRUETYPE_TAG('p','s','g', 0 ), // psg = Penang Sign Language
+ TRUETYPE_TAG('p','s','h', 0 ), // psh = Southwest Pashayi
+ TRUETYPE_TAG('p','s','i', 0 ), // psi = Southeast Pashayi
+ TRUETYPE_TAG('p','s','l', 0 ), // psl = Puerto Rican Sign Language
+ TRUETYPE_TAG('p','s','m', 0 ), // psm = Pauserna
+ TRUETYPE_TAG('p','s','n', 0 ), // psn = Panasuan
+ TRUETYPE_TAG('p','s','o', 0 ), // pso = Polish Sign Language
+ TRUETYPE_TAG('p','s','p', 0 ), // psp = Philippine Sign Language
+ TRUETYPE_TAG('p','s','q', 0 ), // psq = Pasi
+ TRUETYPE_TAG('p','s','r', 0 ), // psr = Portuguese Sign Language
+ TRUETYPE_TAG('p','s','s', 0 ), // pss = Kaulong
+ TRUETYPE_TAG('p','s','t', 0 ), // pst = Central Pashto
+ TRUETYPE_TAG('p','s','u', 0 ), // psu = Sauraseni PrÄkrit
+ TRUETYPE_TAG('p','s','w', 0 ), // psw = Port Sandwich
+ TRUETYPE_TAG('p','s','y', 0 ), // psy = Piscataway
+ TRUETYPE_TAG('p','t','a', 0 ), // pta = Pai Tavytera
+ TRUETYPE_TAG('p','t','h', 0 ), // pth = Pataxó Hã-Ha-Hãe
+ TRUETYPE_TAG('p','t','i', 0 ), // pti = Pintiini
+ TRUETYPE_TAG('p','t','n', 0 ), // ptn = Patani
+ TRUETYPE_TAG('p','t','o', 0 ), // pto = Zo'é
+ TRUETYPE_TAG('p','t','p', 0 ), // ptp = Patep
+ TRUETYPE_TAG('p','t','r', 0 ), // ptr = Piamatsina
+ TRUETYPE_TAG('p','t','t', 0 ), // ptt = Enrekang
+ TRUETYPE_TAG('p','t','u', 0 ), // ptu = Bambam
+ TRUETYPE_TAG('p','t','v', 0 ), // ptv = Port Vato
+ TRUETYPE_TAG('p','t','w', 0 ), // ptw = Pentlatch
+ TRUETYPE_TAG('p','t','y', 0 ), // pty = Pathiya
+ TRUETYPE_TAG('p','u','a', 0 ), // pua = Western Highland Purepecha
+ TRUETYPE_TAG('p','u','b', 0 ), // pub = Purum
+ TRUETYPE_TAG('p','u','c', 0 ), // puc = Punan Merap
+ TRUETYPE_TAG('p','u','d', 0 ), // pud = Punan Aput
+ TRUETYPE_TAG('p','u','e', 0 ), // pue = Puelche
+ TRUETYPE_TAG('p','u','f', 0 ), // puf = Punan Merah
+ TRUETYPE_TAG('p','u','g', 0 ), // pug = Phuie
+ TRUETYPE_TAG('p','u','i', 0 ), // pui = Puinave
+ TRUETYPE_TAG('p','u','j', 0 ), // puj = Punan Tubu
+ TRUETYPE_TAG('p','u','k', 0 ), // puk = Pu Ko
+ TRUETYPE_TAG('p','u','m', 0 ), // pum = Puma
+ TRUETYPE_TAG('p','u','o', 0 ), // puo = Puoc
+ TRUETYPE_TAG('p','u','p', 0 ), // pup = Pulabu
+ TRUETYPE_TAG('p','u','q', 0 ), // puq = Puquina
+ TRUETYPE_TAG('p','u','r', 0 ), // pur = Puruborá
+ TRUETYPE_TAG('p','u','t', 0 ), // put = Putoh
+ TRUETYPE_TAG('p','u','u', 0 ), // puu = Punu
+ TRUETYPE_TAG('p','u','w', 0 ), // puw = Puluwatese
+ TRUETYPE_TAG('p','u','x', 0 ), // pux = Puare
+ TRUETYPE_TAG('p','u','y', 0 ), // puy = Purisimeño
+ TRUETYPE_TAG('p','u','z', 0 ), // puz = Purum Naga
+ TRUETYPE_TAG('p','w','a', 0 ), // pwa = Pawaia
+ TRUETYPE_TAG('p','w','b', 0 ), // pwb = Panawa
+ TRUETYPE_TAG('p','w','g', 0 ), // pwg = Gapapaiwa
+ TRUETYPE_TAG('p','w','m', 0 ), // pwm = Molbog
+ TRUETYPE_TAG('p','w','n', 0 ), // pwn = Paiwan
+ TRUETYPE_TAG('p','w','o', 0 ), // pwo = Pwo Western Karen
+ TRUETYPE_TAG('p','w','r', 0 ), // pwr = Powari
+ TRUETYPE_TAG('p','w','w', 0 ), // pww = Pwo Northern Karen
+ TRUETYPE_TAG('p','x','m', 0 ), // pxm = Quetzaltepec Mixe
+ TRUETYPE_TAG('p','y','e', 0 ), // pye = Pye Krumen
+ TRUETYPE_TAG('p','y','m', 0 ), // pym = Fyam
+ TRUETYPE_TAG('p','y','n', 0 ), // pyn = Poyanáwa
+ TRUETYPE_TAG('p','y','s', 0 ), // pys = Paraguayan Sign Language
+ TRUETYPE_TAG('p','y','u', 0 ), // pyu = Puyuma
+ TRUETYPE_TAG('p','y','x', 0 ), // pyx = Pyu (Myanmar)
+ TRUETYPE_TAG('p','y','y', 0 ), // pyy = Pyen
+ TRUETYPE_TAG('p','z','n', 0 ), // pzn = Para Naga = Private use
+ TRUETYPE_TAG('q','u','a', 0 ), // qua = Quapaw
+ TRUETYPE_TAG('q','u','b', 0 ), // qub = Huallaga Huánuco Quechua
+ TRUETYPE_TAG('q','u','c', 0 ), // quc = K'iche'
+ TRUETYPE_TAG('q','u','d', 0 ), // qud = Calderón Highland Quichua
+ TRUETYPE_TAG('q','u','f', 0 ), // quf = Lambayeque Quechua
+ TRUETYPE_TAG('q','u','g', 0 ), // qug = Chimborazo Highland Quichua
+ TRUETYPE_TAG('q','u','h', 0 ), // quh = South Bolivian Quechua
+ TRUETYPE_TAG('q','u','i', 0 ), // qui = Quileute
+ TRUETYPE_TAG('q','u','k', 0 ), // quk = Chachapoyas Quechua
+ TRUETYPE_TAG('q','u','l', 0 ), // qul = North Bolivian Quechua
+ TRUETYPE_TAG('q','u','m', 0 ), // qum = Sipacapense
+ TRUETYPE_TAG('q','u','n', 0 ), // qun = Quinault
+ TRUETYPE_TAG('q','u','p', 0 ), // qup = Southern Pastaza Quechua
+ TRUETYPE_TAG('q','u','q', 0 ), // quq = Quinqui
+ TRUETYPE_TAG('q','u','r', 0 ), // qur = Yanahuanca Pasco Quechua
+ TRUETYPE_TAG('q','u','s', 0 ), // qus = Santiago del Estero Quichua
+ TRUETYPE_TAG('q','u','v', 0 ), // quv = Sacapulteco
+ TRUETYPE_TAG('q','u','w', 0 ), // quw = Tena Lowland Quichua
+ TRUETYPE_TAG('q','u','x', 0 ), // qux = Yauyos Quechua
+ TRUETYPE_TAG('q','u','y', 0 ), // quy = Ayacucho Quechua
+ TRUETYPE_TAG('q','u','z', 0 ), // quz = Cusco Quechua
+ TRUETYPE_TAG('q','v','a', 0 ), // qva = Ambo-Pasco Quechua
+ TRUETYPE_TAG('q','v','c', 0 ), // qvc = Cajamarca Quechua
+ TRUETYPE_TAG('q','v','e', 0 ), // qve = Eastern Apurímac Quechua
+ TRUETYPE_TAG('q','v','h', 0 ), // qvh = Huamalíes-Dos de Mayo Huánuco Quechua
+ TRUETYPE_TAG('q','v','i', 0 ), // qvi = Imbabura Highland Quichua
+ TRUETYPE_TAG('q','v','j', 0 ), // qvj = Loja Highland Quichua
+ TRUETYPE_TAG('q','v','l', 0 ), // qvl = Cajatambo North Lima Quechua
+ TRUETYPE_TAG('q','v','m', 0 ), // qvm = Margos-Yarowilca-Lauricocha Quechua
+ TRUETYPE_TAG('q','v','n', 0 ), // qvn = North Junín Quechua
+ TRUETYPE_TAG('q','v','o', 0 ), // qvo = Napo Lowland Quechua
+ TRUETYPE_TAG('q','v','p', 0 ), // qvp = Pacaraos Quechua
+ TRUETYPE_TAG('q','v','s', 0 ), // qvs = San Martín Quechua
+ TRUETYPE_TAG('q','v','w', 0 ), // qvw = Huaylla Wanca Quechua
+ TRUETYPE_TAG('q','v','y', 0 ), // qvy = Queyu
+ TRUETYPE_TAG('q','v','z', 0 ), // qvz = Northern Pastaza Quichua
+ TRUETYPE_TAG('q','w','a', 0 ), // qwa = Corongo Ancash Quechua
+ TRUETYPE_TAG('q','w','c', 0 ), // qwc = Classical Quechua
+ TRUETYPE_TAG('q','w','e', 0 ), // qwe = Quechuan (family)
+ TRUETYPE_TAG('q','w','h', 0 ), // qwh = Huaylas Ancash Quechua
+ TRUETYPE_TAG('q','w','m', 0 ), // qwm = Kuman (Russia)
+ TRUETYPE_TAG('q','w','s', 0 ), // qws = Sihuas Ancash Quechua
+ TRUETYPE_TAG('q','w','t', 0 ), // qwt = Kwalhioqua-Tlatskanai
+ TRUETYPE_TAG('q','x','a', 0 ), // qxa = Chiquián Ancash Quechua
+ TRUETYPE_TAG('q','x','c', 0 ), // qxc = Chincha Quechua
+ TRUETYPE_TAG('q','x','h', 0 ), // qxh = Panao Huánuco Quechua
+ TRUETYPE_TAG('q','x','l', 0 ), // qxl = Salasaca Highland Quichua
+ TRUETYPE_TAG('q','x','n', 0 ), // qxn = Northern Conchucos Ancash Quechua
+ TRUETYPE_TAG('q','x','o', 0 ), // qxo = Southern Conchucos Ancash Quechua
+ TRUETYPE_TAG('q','x','p', 0 ), // qxp = Puno Quechua
+ TRUETYPE_TAG('q','x','q', 0 ), // qxq = Qashqa'i
+ TRUETYPE_TAG('q','x','r', 0 ), // qxr = Cañar Highland Quichua
+ TRUETYPE_TAG('q','x','s', 0 ), // qxs = Southern Qiang
+ TRUETYPE_TAG('q','x','t', 0 ), // qxt = Santa Ana de Tusi Pasco Quechua
+ TRUETYPE_TAG('q','x','u', 0 ), // qxu = Arequipa-La Unión Quechua
+ TRUETYPE_TAG('q','x','w', 0 ), // qxw = Jauja Wanca Quechua
+ TRUETYPE_TAG('q','y','a', 0 ), // qya = Quenya
+ TRUETYPE_TAG('q','y','p', 0 ), // qyp = Quiripi
+ TRUETYPE_TAG('r','a','a', 0 ), // raa = Dungmali
+ TRUETYPE_TAG('r','a','b', 0 ), // rab = Camling
+ TRUETYPE_TAG('r','a','c', 0 ), // rac = Rasawa
+ TRUETYPE_TAG('r','a','d', 0 ), // rad = Rade
+ TRUETYPE_TAG('r','a','f', 0 ), // raf = Western Meohang
+ TRUETYPE_TAG('r','a','g', 0 ), // rag = Logooli
+ TRUETYPE_TAG('r','a','h', 0 ), // rah = Rabha
+ TRUETYPE_TAG('r','a','i', 0 ), // rai = Ramoaaina
+ TRUETYPE_TAG('r','a','j', 0 ), // raj = Rajasthani
+ TRUETYPE_TAG('r','a','k', 0 ), // rak = Tulu-Bohuai
+ TRUETYPE_TAG('r','a','l', 0 ), // ral = Ralte
+ TRUETYPE_TAG('r','a','m', 0 ), // ram = Canela
+ TRUETYPE_TAG('r','a','n', 0 ), // ran = Riantana
+ TRUETYPE_TAG('r','a','o', 0 ), // rao = Rao
+ TRUETYPE_TAG('r','a','p', 0 ), // rap = Rapanui
+ TRUETYPE_TAG('r','a','q', 0 ), // raq = Saam
+ TRUETYPE_TAG('r','a','r', 0 ), // rar = Rarotongan
+ TRUETYPE_TAG('r','a','s', 0 ), // ras = Tegali
+ TRUETYPE_TAG('r','a','t', 0 ), // rat = Razajerdi
+ TRUETYPE_TAG('r','a','u', 0 ), // rau = Raute
+ TRUETYPE_TAG('r','a','v', 0 ), // rav = Sampang
+ TRUETYPE_TAG('r','a','w', 0 ), // raw = Rawang
+ TRUETYPE_TAG('r','a','x', 0 ), // rax = Rang
+ TRUETYPE_TAG('r','a','y', 0 ), // ray = Rapa
+ TRUETYPE_TAG('r','a','z', 0 ), // raz = Rahambuu
+ TRUETYPE_TAG('r','b','b', 0 ), // rbb = Rumai Palaung
+ TRUETYPE_TAG('r','b','k', 0 ), // rbk = Northern Bontok
+ TRUETYPE_TAG('r','b','l', 0 ), // rbl = Miraya Bikol
+ TRUETYPE_TAG('r','c','f', 0 ), // rcf = Réunion Creole French
+ TRUETYPE_TAG('r','d','b', 0 ), // rdb = Rudbari
+ TRUETYPE_TAG('r','e','a', 0 ), // rea = Rerau
+ TRUETYPE_TAG('r','e','b', 0 ), // reb = Rembong
+ TRUETYPE_TAG('r','e','e', 0 ), // ree = Rejang Kayan
+ TRUETYPE_TAG('r','e','g', 0 ), // reg = Kara (Tanzania)
+ TRUETYPE_TAG('r','e','i', 0 ), // rei = Reli
+ TRUETYPE_TAG('r','e','j', 0 ), // rej = Rejang
+ TRUETYPE_TAG('r','e','l', 0 ), // rel = Rendille
+ TRUETYPE_TAG('r','e','m', 0 ), // rem = Remo
+ TRUETYPE_TAG('r','e','n', 0 ), // ren = Rengao
+ TRUETYPE_TAG('r','e','r', 0 ), // rer = Rer Bare
+ TRUETYPE_TAG('r','e','s', 0 ), // res = Reshe
+ TRUETYPE_TAG('r','e','t', 0 ), // ret = Retta
+ TRUETYPE_TAG('r','e','y', 0 ), // rey = Reyesano
+ TRUETYPE_TAG('r','g','a', 0 ), // rga = Roria
+ TRUETYPE_TAG('r','g','e', 0 ), // rge = Romano-Greek
+ TRUETYPE_TAG('r','g','k', 0 ), // rgk = Rangkas
+ TRUETYPE_TAG('r','g','n', 0 ), // rgn = Romagnol
+ TRUETYPE_TAG('r','g','r', 0 ), // rgr = Resígaro
+ TRUETYPE_TAG('r','g','s', 0 ), // rgs = Southern Roglai
+ TRUETYPE_TAG('r','g','u', 0 ), // rgu = Ringgou
+ TRUETYPE_TAG('r','h','g', 0 ), // rhg = Rohingya
+ TRUETYPE_TAG('r','h','p', 0 ), // rhp = Yahang
+ TRUETYPE_TAG('r','i','a', 0 ), // ria = Riang (India)
+ TRUETYPE_TAG('r','i','e', 0 ), // rie = Rien
+ TRUETYPE_TAG('r','i','f', 0 ), // rif = Tarifit
+ TRUETYPE_TAG('r','i','l', 0 ), // ril = Riang (Myanmar)
+ TRUETYPE_TAG('r','i','m', 0 ), // rim = Nyaturu
+ TRUETYPE_TAG('r','i','n', 0 ), // rin = Nungu
+ TRUETYPE_TAG('r','i','r', 0 ), // rir = Ribun
+ TRUETYPE_TAG('r','i','t', 0 ), // rit = Ritarungo
+ TRUETYPE_TAG('r','i','u', 0 ), // riu = Riung
+ TRUETYPE_TAG('r','j','g', 0 ), // rjg = Rajong
+ TRUETYPE_TAG('r','j','i', 0 ), // rji = Raji
+ TRUETYPE_TAG('r','j','s', 0 ), // rjs = Rajbanshi
+ TRUETYPE_TAG('r','k','a', 0 ), // rka = Kraol
+ TRUETYPE_TAG('r','k','b', 0 ), // rkb = Rikbaktsa
+ TRUETYPE_TAG('r','k','h', 0 ), // rkh = Rakahanga-Manihiki
+ TRUETYPE_TAG('r','k','i', 0 ), // rki = Rakhine
+ TRUETYPE_TAG('r','k','m', 0 ), // rkm = Marka
+ TRUETYPE_TAG('r','k','t', 0 ), // rkt = Rangpuri
+ TRUETYPE_TAG('r','m','a', 0 ), // rma = Rama
+ TRUETYPE_TAG('r','m','b', 0 ), // rmb = Rembarunga
+ TRUETYPE_TAG('r','m','c', 0 ), // rmc = Carpathian Romani
+ TRUETYPE_TAG('r','m','d', 0 ), // rmd = Traveller Danish
+ TRUETYPE_TAG('r','m','e', 0 ), // rme = Angloromani
+ TRUETYPE_TAG('r','m','f', 0 ), // rmf = Kalo Finnish Romani
+ TRUETYPE_TAG('r','m','g', 0 ), // rmg = Traveller Norwegian
+ TRUETYPE_TAG('r','m','h', 0 ), // rmh = Murkim
+ TRUETYPE_TAG('r','m','i', 0 ), // rmi = Lomavren
+ TRUETYPE_TAG('r','m','k', 0 ), // rmk = Romkun
+ TRUETYPE_TAG('r','m','l', 0 ), // rml = Baltic Romani
+ TRUETYPE_TAG('r','m','m', 0 ), // rmm = Roma
+ TRUETYPE_TAG('r','m','n', 0 ), // rmn = Balkan Romani
+ TRUETYPE_TAG('r','m','o', 0 ), // rmo = Sinte Romani
+ TRUETYPE_TAG('r','m','p', 0 ), // rmp = Rempi
+ TRUETYPE_TAG('r','m','q', 0 ), // rmq = Caló
+ TRUETYPE_TAG('r','m','r', 0 ), // rmr = Caló
+ TRUETYPE_TAG('r','m','s', 0 ), // rms = Romanian Sign Language
+ TRUETYPE_TAG('r','m','t', 0 ), // rmt = Domari
+ TRUETYPE_TAG('r','m','u', 0 ), // rmu = Tavringer Romani
+ TRUETYPE_TAG('r','m','v', 0 ), // rmv = Romanova
+ TRUETYPE_TAG('r','m','w', 0 ), // rmw = Welsh Romani
+ TRUETYPE_TAG('r','m','x', 0 ), // rmx = Romam
+ TRUETYPE_TAG('r','m','y', 0 ), // rmy = Vlax Romani
+ TRUETYPE_TAG('r','m','z', 0 ), // rmz = Marma
+ TRUETYPE_TAG('r','n','a', 0 ), // rna = Runa
+ TRUETYPE_TAG('r','n','d', 0 ), // rnd = Ruund
+ TRUETYPE_TAG('r','n','g', 0 ), // rng = Ronga
+ TRUETYPE_TAG('r','n','l', 0 ), // rnl = Ranglong
+ TRUETYPE_TAG('r','n','n', 0 ), // rnn = Roon
+ TRUETYPE_TAG('r','n','p', 0 ), // rnp = Rongpo
+ TRUETYPE_TAG('r','n','w', 0 ), // rnw = Rungwa
+ TRUETYPE_TAG('r','o','a', 0 ), // roa = Romance languages
+ TRUETYPE_TAG('r','o','b', 0 ), // rob = Tae'
+ TRUETYPE_TAG('r','o','c', 0 ), // roc = Cacgia Roglai
+ TRUETYPE_TAG('r','o','d', 0 ), // rod = Rogo
+ TRUETYPE_TAG('r','o','e', 0 ), // roe = Ronji
+ TRUETYPE_TAG('r','o','f', 0 ), // rof = Rombo
+ TRUETYPE_TAG('r','o','g', 0 ), // rog = Northern Roglai
+ TRUETYPE_TAG('r','o','l', 0 ), // rol = Romblomanon
+ TRUETYPE_TAG('r','o','m', 0 ), // rom = Romany
+ TRUETYPE_TAG('r','o','o', 0 ), // roo = Rotokas
+ TRUETYPE_TAG('r','o','p', 0 ), // rop = Kriol
+ TRUETYPE_TAG('r','o','r', 0 ), // ror = Rongga
+ TRUETYPE_TAG('r','o','u', 0 ), // rou = Runga
+ TRUETYPE_TAG('r','o','w', 0 ), // row = Dela-Oenale
+ TRUETYPE_TAG('r','p','n', 0 ), // rpn = Repanbitip
+ TRUETYPE_TAG('r','p','t', 0 ), // rpt = Rapting
+ TRUETYPE_TAG('r','r','i', 0 ), // rri = Ririo
+ TRUETYPE_TAG('r','r','o', 0 ), // rro = Waima
+ TRUETYPE_TAG('r','s','b', 0 ), // rsb = Romano-Serbian
+ TRUETYPE_TAG('r','s','i', 0 ), // rsi = Rennellese Sign Language
+ TRUETYPE_TAG('r','s','l', 0 ), // rsl = Russian Sign Language
+ TRUETYPE_TAG('r','t','h', 0 ), // rth = Ratahan
+ TRUETYPE_TAG('r','t','m', 0 ), // rtm = Rotuman
+ TRUETYPE_TAG('r','t','w', 0 ), // rtw = Rathawi
+ TRUETYPE_TAG('r','u','b', 0 ), // rub = Gungu
+ TRUETYPE_TAG('r','u','c', 0 ), // ruc = Ruuli
+ TRUETYPE_TAG('r','u','e', 0 ), // rue = Rusyn
+ TRUETYPE_TAG('r','u','f', 0 ), // ruf = Luguru
+ TRUETYPE_TAG('r','u','g', 0 ), // rug = Roviana
+ TRUETYPE_TAG('r','u','h', 0 ), // ruh = Ruga
+ TRUETYPE_TAG('r','u','i', 0 ), // rui = Rufiji
+ TRUETYPE_TAG('r','u','k', 0 ), // ruk = Che
+ TRUETYPE_TAG('r','u','o', 0 ), // ruo = Istro Romanian
+ TRUETYPE_TAG('r','u','p', 0 ), // rup = Macedo-Romanian
+ TRUETYPE_TAG('r','u','q', 0 ), // ruq = Megleno Romanian
+ TRUETYPE_TAG('r','u','t', 0 ), // rut = Rutul
+ TRUETYPE_TAG('r','u','u', 0 ), // ruu = Lanas Lobu
+ TRUETYPE_TAG('r','u','y', 0 ), // ruy = Mala (Nigeria)
+ TRUETYPE_TAG('r','u','z', 0 ), // ruz = Ruma
+ TRUETYPE_TAG('r','w','a', 0 ), // rwa = Rawo
+ TRUETYPE_TAG('r','w','k', 0 ), // rwk = Rwa
+ TRUETYPE_TAG('r','w','m', 0 ), // rwm = Amba (Uganda)
+ TRUETYPE_TAG('r','w','o', 0 ), // rwo = Rawa
+ TRUETYPE_TAG('r','w','r', 0 ), // rwr = Marwari (India)
+ TRUETYPE_TAG('r','y','n', 0 ), // ryn = Northern Amami-Oshima
+ TRUETYPE_TAG('r','y','s', 0 ), // rys = Yaeyama
+ TRUETYPE_TAG('r','y','u', 0 ), // ryu = Central Okinawan
+ TRUETYPE_TAG('s','a','a', 0 ), // saa = Saba
+ TRUETYPE_TAG('s','a','b', 0 ), // sab = Buglere
+ TRUETYPE_TAG('s','a','c', 0 ), // sac = Meskwaki
+ TRUETYPE_TAG('s','a','d', 0 ), // sad = Sandawe
+ TRUETYPE_TAG('s','a','e', 0 ), // sae = Sabanê
+ TRUETYPE_TAG('s','a','f', 0 ), // saf = Safaliba
+ TRUETYPE_TAG('s','a','h', 0 ), // sah = Yakut
+ TRUETYPE_TAG('s','a','i', 0 ), // sai = South American Indian languages
+ TRUETYPE_TAG('s','a','j', 0 ), // saj = Sahu
+ TRUETYPE_TAG('s','a','k', 0 ), // sak = Sake
+ TRUETYPE_TAG('s','a','l', 0 ), // sal = Salishan languages
+ TRUETYPE_TAG('s','a','m', 0 ), // sam = Samaritan Aramaic
+ TRUETYPE_TAG('s','a','o', 0 ), // sao = Sause
+ TRUETYPE_TAG('s','a','p', 0 ), // sap = Sanapaná
+ TRUETYPE_TAG('s','a','q', 0 ), // saq = Samburu
+ TRUETYPE_TAG('s','a','r', 0 ), // sar = Saraveca
+ TRUETYPE_TAG('s','a','s', 0 ), // sas = Sasak
+ TRUETYPE_TAG('s','a','t', 0 ), // sat = Santali
+ TRUETYPE_TAG('s','a','u', 0 ), // sau = Saleman
+ TRUETYPE_TAG('s','a','v', 0 ), // sav = Saafi-Saafi
+ TRUETYPE_TAG('s','a','w', 0 ), // saw = Sawi
+ TRUETYPE_TAG('s','a','x', 0 ), // sax = Sa
+ TRUETYPE_TAG('s','a','y', 0 ), // say = Saya
+ TRUETYPE_TAG('s','a','z', 0 ), // saz = Saurashtra
+ TRUETYPE_TAG('s','b','a', 0 ), // sba = Ngambay
+ TRUETYPE_TAG('s','b','b', 0 ), // sbb = Simbo
+ TRUETYPE_TAG('s','b','c', 0 ), // sbc = Kele (Papua New Guinea)
+ TRUETYPE_TAG('s','b','d', 0 ), // sbd = Southern Samo
+ TRUETYPE_TAG('s','b','e', 0 ), // sbe = Saliba
+ TRUETYPE_TAG('s','b','f', 0 ), // sbf = Shabo
+ TRUETYPE_TAG('s','b','g', 0 ), // sbg = Seget
+ TRUETYPE_TAG('s','b','h', 0 ), // sbh = Sori-Harengan
+ TRUETYPE_TAG('s','b','i', 0 ), // sbi = Seti
+ TRUETYPE_TAG('s','b','j', 0 ), // sbj = Surbakhal
+ TRUETYPE_TAG('s','b','k', 0 ), // sbk = Safwa
+ TRUETYPE_TAG('s','b','l', 0 ), // sbl = Botolan Sambal
+ TRUETYPE_TAG('s','b','m', 0 ), // sbm = Sagala
+ TRUETYPE_TAG('s','b','n', 0 ), // sbn = Sindhi Bhil
+ TRUETYPE_TAG('s','b','o', 0 ), // sbo = Sabüm
+ TRUETYPE_TAG('s','b','p', 0 ), // sbp = Sangu (Tanzania)
+ TRUETYPE_TAG('s','b','q', 0 ), // sbq = Sileibi
+ TRUETYPE_TAG('s','b','r', 0 ), // sbr = Sembakung Murut
+ TRUETYPE_TAG('s','b','s', 0 ), // sbs = Subiya
+ TRUETYPE_TAG('s','b','t', 0 ), // sbt = Kimki
+ TRUETYPE_TAG('s','b','u', 0 ), // sbu = Stod Bhoti
+ TRUETYPE_TAG('s','b','v', 0 ), // sbv = Sabine
+ TRUETYPE_TAG('s','b','w', 0 ), // sbw = Simba
+ TRUETYPE_TAG('s','b','x', 0 ), // sbx = Seberuang
+ TRUETYPE_TAG('s','b','y', 0 ), // sby = Soli
+ TRUETYPE_TAG('s','b','z', 0 ), // sbz = Sara Kaba
+ TRUETYPE_TAG('s','c','a', 0 ), // sca = Sansu
+ TRUETYPE_TAG('s','c','b', 0 ), // scb = Chut
+ TRUETYPE_TAG('s','c','e', 0 ), // sce = Dongxiang
+ TRUETYPE_TAG('s','c','f', 0 ), // scf = San Miguel Creole French
+ TRUETYPE_TAG('s','c','g', 0 ), // scg = Sanggau
+ TRUETYPE_TAG('s','c','h', 0 ), // sch = Sakachep
+ TRUETYPE_TAG('s','c','i', 0 ), // sci = Sri Lankan Creole Malay
+ TRUETYPE_TAG('s','c','k', 0 ), // sck = Sadri
+ TRUETYPE_TAG('s','c','l', 0 ), // scl = Shina
+ TRUETYPE_TAG('s','c','n', 0 ), // scn = Sicilian
+ TRUETYPE_TAG('s','c','o', 0 ), // sco = Scots
+ TRUETYPE_TAG('s','c','p', 0 ), // scp = Helambu Sherpa
+ TRUETYPE_TAG('s','c','q', 0 ), // scq = Sa'och
+ TRUETYPE_TAG('s','c','s', 0 ), // scs = North Slavey
+ TRUETYPE_TAG('s','c','u', 0 ), // scu = Shumcho
+ TRUETYPE_TAG('s','c','v', 0 ), // scv = Sheni
+ TRUETYPE_TAG('s','c','w', 0 ), // scw = Sha
+ TRUETYPE_TAG('s','c','x', 0 ), // scx = Sicel
+ TRUETYPE_TAG('s','d','a', 0 ), // sda = Toraja-Sa'dan
+ TRUETYPE_TAG('s','d','b', 0 ), // sdb = Shabak
+ TRUETYPE_TAG('s','d','c', 0 ), // sdc = Sassarese Sardinian
+ TRUETYPE_TAG('s','d','e', 0 ), // sde = Surubu
+ TRUETYPE_TAG('s','d','f', 0 ), // sdf = Sarli
+ TRUETYPE_TAG('s','d','g', 0 ), // sdg = Savi
+ TRUETYPE_TAG('s','d','h', 0 ), // sdh = Southern Kurdish
+ TRUETYPE_TAG('s','d','j', 0 ), // sdj = Suundi
+ TRUETYPE_TAG('s','d','k', 0 ), // sdk = Sos Kundi
+ TRUETYPE_TAG('s','d','l', 0 ), // sdl = Saudi Arabian Sign Language
+ TRUETYPE_TAG('s','d','m', 0 ), // sdm = Semandang
+ TRUETYPE_TAG('s','d','n', 0 ), // sdn = Gallurese Sardinian
+ TRUETYPE_TAG('s','d','o', 0 ), // sdo = Bukar-Sadung Bidayuh
+ TRUETYPE_TAG('s','d','p', 0 ), // sdp = Sherdukpen
+ TRUETYPE_TAG('s','d','r', 0 ), // sdr = Oraon Sadri
+ TRUETYPE_TAG('s','d','s', 0 ), // sds = Sened
+ TRUETYPE_TAG('s','d','t', 0 ), // sdt = Shuadit
+ TRUETYPE_TAG('s','d','u', 0 ), // sdu = Sarudu
+ TRUETYPE_TAG('s','d','v', 0 ), // sdv = Eastern Sudanic languages
+ TRUETYPE_TAG('s','d','x', 0 ), // sdx = Sibu Melanau
+ TRUETYPE_TAG('s','d','z', 0 ), // sdz = Sallands
+ TRUETYPE_TAG('s','e','a', 0 ), // sea = Semai
+ TRUETYPE_TAG('s','e','b', 0 ), // seb = Shempire Senoufo
+ TRUETYPE_TAG('s','e','c', 0 ), // sec = Sechelt
+ TRUETYPE_TAG('s','e','d', 0 ), // sed = Sedang
+ TRUETYPE_TAG('s','e','e', 0 ), // see = Seneca
+ TRUETYPE_TAG('s','e','f', 0 ), // sef = Cebaara Senoufo
+ TRUETYPE_TAG('s','e','g', 0 ), // seg = Segeju
+ TRUETYPE_TAG('s','e','h', 0 ), // seh = Sena
+ TRUETYPE_TAG('s','e','i', 0 ), // sei = Seri
+ TRUETYPE_TAG('s','e','j', 0 ), // sej = Sene
+ TRUETYPE_TAG('s','e','k', 0 ), // sek = Sekani
+ TRUETYPE_TAG('s','e','l', 0 ), // sel = Selkup
+ TRUETYPE_TAG('s','e','m', 0 ), // sem = Semitic languages
+ TRUETYPE_TAG('s','e','n', 0 ), // sen = Nanerigé Sénoufo
+ TRUETYPE_TAG('s','e','o', 0 ), // seo = Suarmin
+ TRUETYPE_TAG('s','e','p', 0 ), // sep = Sìcìté Sénoufo
+ TRUETYPE_TAG('s','e','q', 0 ), // seq = Senara Sénoufo
+ TRUETYPE_TAG('s','e','r', 0 ), // ser = Serrano
+ TRUETYPE_TAG('s','e','s', 0 ), // ses = Koyraboro Senni Songhai
+ TRUETYPE_TAG('s','e','t', 0 ), // set = Sentani
+ TRUETYPE_TAG('s','e','u', 0 ), // seu = Serui-Laut
+ TRUETYPE_TAG('s','e','v', 0 ), // sev = Nyarafolo Senoufo
+ TRUETYPE_TAG('s','e','w', 0 ), // sew = Sewa Bay
+ TRUETYPE_TAG('s','e','y', 0 ), // sey = Secoya
+ TRUETYPE_TAG('s','e','z', 0 ), // sez = Senthang Chin
+ TRUETYPE_TAG('s','f','b', 0 ), // sfb = Langue des signes de Belgique Francophone
+ TRUETYPE_TAG('s','f','m', 0 ), // sfm = Small Flowery Miao
+ TRUETYPE_TAG('s','f','s', 0 ), // sfs = South African Sign Language
+ TRUETYPE_TAG('s','f','w', 0 ), // sfw = Sehwi
+ TRUETYPE_TAG('s','g','a', 0 ), // sga = Old Irish (to 900)
+ TRUETYPE_TAG('s','g','b', 0 ), // sgb = Mag-antsi Ayta
+ TRUETYPE_TAG('s','g','c', 0 ), // sgc = Kipsigis
+ TRUETYPE_TAG('s','g','d', 0 ), // sgd = Surigaonon
+ TRUETYPE_TAG('s','g','e', 0 ), // sge = Segai
+ TRUETYPE_TAG('s','g','g', 0 ), // sgg = Swiss-German Sign Language
+ TRUETYPE_TAG('s','g','h', 0 ), // sgh = Shughni
+ TRUETYPE_TAG('s','g','i', 0 ), // sgi = Suga
+ TRUETYPE_TAG('s','g','k', 0 ), // sgk = Sangkong
+ TRUETYPE_TAG('s','g','l', 0 ), // sgl = Sanglechi-Ishkashimi
+ TRUETYPE_TAG('s','g','m', 0 ), // sgm = Singa
+ TRUETYPE_TAG('s','g','n', 0 ), // sgn = Sign languages
+ TRUETYPE_TAG('s','g','o', 0 ), // sgo = Songa
+ TRUETYPE_TAG('s','g','p', 0 ), // sgp = Singpho
+ TRUETYPE_TAG('s','g','r', 0 ), // sgr = Sangisari
+ TRUETYPE_TAG('s','g','s', 0 ), // sgs = Samogitian
+ TRUETYPE_TAG('s','g','t', 0 ), // sgt = Brokpake
+ TRUETYPE_TAG('s','g','u', 0 ), // sgu = Salas
+ TRUETYPE_TAG('s','g','w', 0 ), // sgw = Sebat Bet Gurage
+ TRUETYPE_TAG('s','g','x', 0 ), // sgx = Sierra Leone Sign Language
+ TRUETYPE_TAG('s','g','y', 0 ), // sgy = Sanglechi
+ TRUETYPE_TAG('s','g','z', 0 ), // sgz = Sursurunga
+ TRUETYPE_TAG('s','h','a', 0 ), // sha = Shall-Zwall
+ TRUETYPE_TAG('s','h','b', 0 ), // shb = Ninam
+ TRUETYPE_TAG('s','h','c', 0 ), // shc = Sonde
+ TRUETYPE_TAG('s','h','d', 0 ), // shd = Kundal Shahi
+ TRUETYPE_TAG('s','h','e', 0 ), // she = Sheko
+ TRUETYPE_TAG('s','h','g', 0 ), // shg = Shua
+ TRUETYPE_TAG('s','h','h', 0 ), // shh = Shoshoni
+ TRUETYPE_TAG('s','h','i', 0 ), // shi = Tachelhit
+ TRUETYPE_TAG('s','h','j', 0 ), // shj = Shatt
+ TRUETYPE_TAG('s','h','k', 0 ), // shk = Shilluk
+ TRUETYPE_TAG('s','h','l', 0 ), // shl = Shendu
+ TRUETYPE_TAG('s','h','m', 0 ), // shm = Shahrudi
+ TRUETYPE_TAG('s','h','n', 0 ), // shn = Shan
+ TRUETYPE_TAG('s','h','o', 0 ), // sho = Shanga
+ TRUETYPE_TAG('s','h','p', 0 ), // shp = Shipibo-Conibo
+ TRUETYPE_TAG('s','h','q', 0 ), // shq = Sala
+ TRUETYPE_TAG('s','h','r', 0 ), // shr = Shi
+ TRUETYPE_TAG('s','h','s', 0 ), // shs = Shuswap
+ TRUETYPE_TAG('s','h','t', 0 ), // sht = Shasta
+ TRUETYPE_TAG('s','h','u', 0 ), // shu = Chadian Arabic
+ TRUETYPE_TAG('s','h','v', 0 ), // shv = Shehri
+ TRUETYPE_TAG('s','h','w', 0 ), // shw = Shwai
+ TRUETYPE_TAG('s','h','x', 0 ), // shx = She
+ TRUETYPE_TAG('s','h','y', 0 ), // shy = Tachawit
+ TRUETYPE_TAG('s','h','z', 0 ), // shz = Syenara Senoufo
+ TRUETYPE_TAG('s','i','a', 0 ), // sia = Akkala Sami
+ TRUETYPE_TAG('s','i','b', 0 ), // sib = Sebop
+ TRUETYPE_TAG('s','i','d', 0 ), // sid = Sidamo
+ TRUETYPE_TAG('s','i','e', 0 ), // sie = Simaa
+ TRUETYPE_TAG('s','i','f', 0 ), // sif = Siamou
+ TRUETYPE_TAG('s','i','g', 0 ), // sig = Paasaal
+ TRUETYPE_TAG('s','i','h', 0 ), // sih = Zire
+ TRUETYPE_TAG('s','i','i', 0 ), // sii = Shom Peng
+ TRUETYPE_TAG('s','i','j', 0 ), // sij = Numbami
+ TRUETYPE_TAG('s','i','k', 0 ), // sik = Sikiana
+ TRUETYPE_TAG('s','i','l', 0 ), // sil = Tumulung Sisaala
+ TRUETYPE_TAG('s','i','m', 0 ), // sim = Mende (Papua New Guinea)
+ TRUETYPE_TAG('s','i','o', 0 ), // sio = Siouan languages
+ TRUETYPE_TAG('s','i','p', 0 ), // sip = Sikkimese
+ TRUETYPE_TAG('s','i','q', 0 ), // siq = Sonia
+ TRUETYPE_TAG('s','i','r', 0 ), // sir = Siri
+ TRUETYPE_TAG('s','i','s', 0 ), // sis = Siuslaw
+ TRUETYPE_TAG('s','i','t', 0 ), // sit = Sino-Tibetan languages
+ TRUETYPE_TAG('s','i','u', 0 ), // siu = Sinagen
+ TRUETYPE_TAG('s','i','v', 0 ), // siv = Sumariup
+ TRUETYPE_TAG('s','i','w', 0 ), // siw = Siwai
+ TRUETYPE_TAG('s','i','x', 0 ), // six = Sumau
+ TRUETYPE_TAG('s','i','y', 0 ), // siy = Sivandi
+ TRUETYPE_TAG('s','i','z', 0 ), // siz = Siwi
+ TRUETYPE_TAG('s','j','a', 0 ), // sja = Epena
+ TRUETYPE_TAG('s','j','b', 0 ), // sjb = Sajau Basap
+ TRUETYPE_TAG('s','j','d', 0 ), // sjd = Kildin Sami
+ TRUETYPE_TAG('s','j','e', 0 ), // sje = Pite Sami
+ TRUETYPE_TAG('s','j','g', 0 ), // sjg = Assangori
+ TRUETYPE_TAG('s','j','k', 0 ), // sjk = Kemi Sami
+ TRUETYPE_TAG('s','j','l', 0 ), // sjl = Sajalong
+ TRUETYPE_TAG('s','j','m', 0 ), // sjm = Mapun
+ TRUETYPE_TAG('s','j','n', 0 ), // sjn = Sindarin
+ TRUETYPE_TAG('s','j','o', 0 ), // sjo = Xibe
+ TRUETYPE_TAG('s','j','p', 0 ), // sjp = Surjapuri
+ TRUETYPE_TAG('s','j','r', 0 ), // sjr = Siar-Lak
+ TRUETYPE_TAG('s','j','s', 0 ), // sjs = Senhaja De Srair
+ TRUETYPE_TAG('s','j','t', 0 ), // sjt = Ter Sami
+ TRUETYPE_TAG('s','j','u', 0 ), // sju = Ume Sami
+ TRUETYPE_TAG('s','j','w', 0 ), // sjw = Shawnee
+ TRUETYPE_TAG('s','k','a', 0 ), // ska = Skagit
+ TRUETYPE_TAG('s','k','b', 0 ), // skb = Saek
+ TRUETYPE_TAG('s','k','c', 0 ), // skc = Sauk
+ TRUETYPE_TAG('s','k','d', 0 ), // skd = Southern Sierra Miwok
+ TRUETYPE_TAG('s','k','e', 0 ), // ske = Seke (Vanuatu)
+ TRUETYPE_TAG('s','k','f', 0 ), // skf = Sakirabiá
+ TRUETYPE_TAG('s','k','g', 0 ), // skg = Sakalava Malagasy
+ TRUETYPE_TAG('s','k','h', 0 ), // skh = Sikule
+ TRUETYPE_TAG('s','k','i', 0 ), // ski = Sika
+ TRUETYPE_TAG('s','k','j', 0 ), // skj = Seke (Nepal)
+ TRUETYPE_TAG('s','k','k', 0 ), // skk = Sok
+ TRUETYPE_TAG('s','k','m', 0 ), // skm = Sakam
+ TRUETYPE_TAG('s','k','n', 0 ), // skn = Kolibugan Subanon
+ TRUETYPE_TAG('s','k','o', 0 ), // sko = Seko Tengah
+ TRUETYPE_TAG('s','k','p', 0 ), // skp = Sekapan
+ TRUETYPE_TAG('s','k','q', 0 ), // skq = Sininkere
+ TRUETYPE_TAG('s','k','r', 0 ), // skr = Seraiki
+ TRUETYPE_TAG('s','k','s', 0 ), // sks = Maia
+ TRUETYPE_TAG('s','k','t', 0 ), // skt = Sakata
+ TRUETYPE_TAG('s','k','u', 0 ), // sku = Sakao
+ TRUETYPE_TAG('s','k','v', 0 ), // skv = Skou
+ TRUETYPE_TAG('s','k','w', 0 ), // skw = Skepi Creole Dutch
+ TRUETYPE_TAG('s','k','x', 0 ), // skx = Seko Padang
+ TRUETYPE_TAG('s','k','y', 0 ), // sky = Sikaiana
+ TRUETYPE_TAG('s','k','z', 0 ), // skz = Sekar
+ TRUETYPE_TAG('s','l','a', 0 ), // sla = Slavic languages
+ TRUETYPE_TAG('s','l','c', 0 ), // slc = Sáliba
+ TRUETYPE_TAG('s','l','d', 0 ), // sld = Sissala
+ TRUETYPE_TAG('s','l','e', 0 ), // sle = Sholaga
+ TRUETYPE_TAG('s','l','f', 0 ), // slf = Swiss-Italian Sign Language
+ TRUETYPE_TAG('s','l','g', 0 ), // slg = Selungai Murut
+ TRUETYPE_TAG('s','l','h', 0 ), // slh = Southern Puget Sound Salish
+ TRUETYPE_TAG('s','l','i', 0 ), // sli = Lower Silesian
+ TRUETYPE_TAG('s','l','j', 0 ), // slj = Salumá
+ TRUETYPE_TAG('s','l','l', 0 ), // sll = Salt-Yui
+ TRUETYPE_TAG('s','l','m', 0 ), // slm = Pangutaran Sama
+ TRUETYPE_TAG('s','l','n', 0 ), // sln = Salinan
+ TRUETYPE_TAG('s','l','p', 0 ), // slp = Lamaholot
+ TRUETYPE_TAG('s','l','q', 0 ), // slq = Salchuq
+ TRUETYPE_TAG('s','l','r', 0 ), // slr = Salar
+ TRUETYPE_TAG('s','l','s', 0 ), // sls = Singapore Sign Language
+ TRUETYPE_TAG('s','l','t', 0 ), // slt = Sila
+ TRUETYPE_TAG('s','l','u', 0 ), // slu = Selaru
+ TRUETYPE_TAG('s','l','w', 0 ), // slw = Sialum
+ TRUETYPE_TAG('s','l','x', 0 ), // slx = Salampasu
+ TRUETYPE_TAG('s','l','y', 0 ), // sly = Selayar
+ TRUETYPE_TAG('s','l','z', 0 ), // slz = Ma'ya
+ TRUETYPE_TAG('s','m','a', 0 ), // sma = Southern Sami
+ TRUETYPE_TAG('s','m','b', 0 ), // smb = Simbari
+ TRUETYPE_TAG('s','m','c', 0 ), // smc = Som
+ TRUETYPE_TAG('s','m','d', 0 ), // smd = Sama
+ TRUETYPE_TAG('s','m','f', 0 ), // smf = Auwe
+ TRUETYPE_TAG('s','m','g', 0 ), // smg = Simbali
+ TRUETYPE_TAG('s','m','h', 0 ), // smh = Samei
+ TRUETYPE_TAG('s','m','i', 0 ), // smi = Sami languages
+ TRUETYPE_TAG('s','m','j', 0 ), // smj = Lule Sami
+ TRUETYPE_TAG('s','m','k', 0 ), // smk = Bolinao
+ TRUETYPE_TAG('s','m','l', 0 ), // sml = Central Sama
+ TRUETYPE_TAG('s','m','m', 0 ), // smm = Musasa
+ TRUETYPE_TAG('s','m','n', 0 ), // smn = Inari Sami
+ TRUETYPE_TAG('s','m','p', 0 ), // smp = Samaritan
+ TRUETYPE_TAG('s','m','q', 0 ), // smq = Samo
+ TRUETYPE_TAG('s','m','r', 0 ), // smr = Simeulue
+ TRUETYPE_TAG('s','m','s', 0 ), // sms = Skolt Sami
+ TRUETYPE_TAG('s','m','t', 0 ), // smt = Simte
+ TRUETYPE_TAG('s','m','u', 0 ), // smu = Somray
+ TRUETYPE_TAG('s','m','v', 0 ), // smv = Samvedi
+ TRUETYPE_TAG('s','m','w', 0 ), // smw = Sumbawa
+ TRUETYPE_TAG('s','m','x', 0 ), // smx = Samba
+ TRUETYPE_TAG('s','m','y', 0 ), // smy = Semnani
+ TRUETYPE_TAG('s','m','z', 0 ), // smz = Simeku
+ TRUETYPE_TAG('s','n','b', 0 ), // snb = Sebuyau
+ TRUETYPE_TAG('s','n','c', 0 ), // snc = Sinaugoro
+ TRUETYPE_TAG('s','n','e', 0 ), // sne = Bau Bidayuh
+ TRUETYPE_TAG('s','n','f', 0 ), // snf = Noon
+ TRUETYPE_TAG('s','n','g', 0 ), // sng = Sanga (Democratic Republic of Congo)
+ TRUETYPE_TAG('s','n','h', 0 ), // snh = Shinabo
+ TRUETYPE_TAG('s','n','i', 0 ), // sni = Sensi
+ TRUETYPE_TAG('s','n','j', 0 ), // snj = Riverain Sango
+ TRUETYPE_TAG('s','n','k', 0 ), // snk = Soninke
+ TRUETYPE_TAG('s','n','l', 0 ), // snl = Sangil
+ TRUETYPE_TAG('s','n','m', 0 ), // snm = Southern Ma'di
+ TRUETYPE_TAG('s','n','n', 0 ), // snn = Siona
+ TRUETYPE_TAG('s','n','o', 0 ), // sno = Snohomish
+ TRUETYPE_TAG('s','n','p', 0 ), // snp = Siane
+ TRUETYPE_TAG('s','n','q', 0 ), // snq = Sangu (Gabon)
+ TRUETYPE_TAG('s','n','r', 0 ), // snr = Sihan
+ TRUETYPE_TAG('s','n','s', 0 ), // sns = South West Bay
+ TRUETYPE_TAG('s','n','u', 0 ), // snu = Senggi
+ TRUETYPE_TAG('s','n','v', 0 ), // snv = Sa'ban
+ TRUETYPE_TAG('s','n','w', 0 ), // snw = Selee
+ TRUETYPE_TAG('s','n','x', 0 ), // snx = Sam
+ TRUETYPE_TAG('s','n','y', 0 ), // sny = Saniyo-Hiyewe
+ TRUETYPE_TAG('s','n','z', 0 ), // snz = Sinsauru
+ TRUETYPE_TAG('s','o','a', 0 ), // soa = Thai Song
+ TRUETYPE_TAG('s','o','b', 0 ), // sob = Sobei
+ TRUETYPE_TAG('s','o','c', 0 ), // soc = So (Democratic Republic of Congo)
+ TRUETYPE_TAG('s','o','d', 0 ), // sod = Songoora
+ TRUETYPE_TAG('s','o','e', 0 ), // soe = Songomeno
+ TRUETYPE_TAG('s','o','g', 0 ), // sog = Sogdian
+ TRUETYPE_TAG('s','o','h', 0 ), // soh = Aka
+ TRUETYPE_TAG('s','o','i', 0 ), // soi = Sonha
+ TRUETYPE_TAG('s','o','j', 0 ), // soj = Soi
+ TRUETYPE_TAG('s','o','k', 0 ), // sok = Sokoro
+ TRUETYPE_TAG('s','o','l', 0 ), // sol = Solos
+ TRUETYPE_TAG('s','o','n', 0 ), // son = Songhai languages
+ TRUETYPE_TAG('s','o','o', 0 ), // soo = Songo
+ TRUETYPE_TAG('s','o','p', 0 ), // sop = Songe
+ TRUETYPE_TAG('s','o','q', 0 ), // soq = Kanasi
+ TRUETYPE_TAG('s','o','r', 0 ), // sor = Somrai
+ TRUETYPE_TAG('s','o','s', 0 ), // sos = Seeku
+ TRUETYPE_TAG('s','o','u', 0 ), // sou = Southern Thai
+ TRUETYPE_TAG('s','o','v', 0 ), // sov = Sonsorol
+ TRUETYPE_TAG('s','o','w', 0 ), // sow = Sowanda
+ TRUETYPE_TAG('s','o','x', 0 ), // sox = So (Cameroon)
+ TRUETYPE_TAG('s','o','y', 0 ), // soy = Miyobe
+ TRUETYPE_TAG('s','o','z', 0 ), // soz = Temi
+ TRUETYPE_TAG('s','p','b', 0 ), // spb = Sepa (Indonesia)
+ TRUETYPE_TAG('s','p','c', 0 ), // spc = Sapé
+ TRUETYPE_TAG('s','p','d', 0 ), // spd = Saep
+ TRUETYPE_TAG('s','p','e', 0 ), // spe = Sepa (Papua New Guinea)
+ TRUETYPE_TAG('s','p','g', 0 ), // spg = Sian
+ TRUETYPE_TAG('s','p','i', 0 ), // spi = Saponi
+ TRUETYPE_TAG('s','p','k', 0 ), // spk = Sengo
+ TRUETYPE_TAG('s','p','l', 0 ), // spl = Selepet
+ TRUETYPE_TAG('s','p','m', 0 ), // spm = Sepen
+ TRUETYPE_TAG('s','p','o', 0 ), // spo = Spokane
+ TRUETYPE_TAG('s','p','p', 0 ), // spp = Supyire Senoufo
+ TRUETYPE_TAG('s','p','q', 0 ), // spq = Loreto-Ucayali Spanish
+ TRUETYPE_TAG('s','p','r', 0 ), // spr = Saparua
+ TRUETYPE_TAG('s','p','s', 0 ), // sps = Saposa
+ TRUETYPE_TAG('s','p','t', 0 ), // spt = Spiti Bhoti
+ TRUETYPE_TAG('s','p','u', 0 ), // spu = Sapuan
+ TRUETYPE_TAG('s','p','x', 0 ), // spx = South Picene
+ TRUETYPE_TAG('s','p','y', 0 ), // spy = Sabaot
+ TRUETYPE_TAG('s','q','a', 0 ), // sqa = Shama-Sambuga
+ TRUETYPE_TAG('s','q','h', 0 ), // sqh = Shau
+ TRUETYPE_TAG('s','q','j', 0 ), // sqj = Albanian languages
+ TRUETYPE_TAG('s','q','m', 0 ), // sqm = Suma
+ TRUETYPE_TAG('s','q','n', 0 ), // sqn = Susquehannock
+ TRUETYPE_TAG('s','q','o', 0 ), // sqo = Sorkhei
+ TRUETYPE_TAG('s','q','q', 0 ), // sqq = Sou
+ TRUETYPE_TAG('s','q','r', 0 ), // sqr = Siculo Arabic
+ TRUETYPE_TAG('s','q','s', 0 ), // sqs = Sri Lankan Sign Language
+ TRUETYPE_TAG('s','q','t', 0 ), // sqt = Soqotri
+ TRUETYPE_TAG('s','q','u', 0 ), // squ = Squamish
+ TRUETYPE_TAG('s','r','a', 0 ), // sra = Saruga
+ TRUETYPE_TAG('s','r','b', 0 ), // srb = Sora
+ TRUETYPE_TAG('s','r','c', 0 ), // src = Logudorese Sardinian
+ TRUETYPE_TAG('s','r','e', 0 ), // sre = Sara
+ TRUETYPE_TAG('s','r','f', 0 ), // srf = Nafi
+ TRUETYPE_TAG('s','r','g', 0 ), // srg = Sulod
+ TRUETYPE_TAG('s','r','h', 0 ), // srh = Sarikoli
+ TRUETYPE_TAG('s','r','i', 0 ), // sri = Siriano
+ TRUETYPE_TAG('s','r','k', 0 ), // srk = Serudung Murut
+ TRUETYPE_TAG('s','r','l', 0 ), // srl = Isirawa
+ TRUETYPE_TAG('s','r','m', 0 ), // srm = Saramaccan
+ TRUETYPE_TAG('s','r','n', 0 ), // srn = Sranan Tongo
+ TRUETYPE_TAG('s','r','o', 0 ), // sro = Campidanese Sardinian
+ TRUETYPE_TAG('s','r','q', 0 ), // srq = Sirionó
+ TRUETYPE_TAG('s','r','r', 0 ), // srr = Serer
+ TRUETYPE_TAG('s','r','s', 0 ), // srs = Sarsi
+ TRUETYPE_TAG('s','r','t', 0 ), // srt = Sauri
+ TRUETYPE_TAG('s','r','u', 0 ), // sru = Suruí
+ TRUETYPE_TAG('s','r','v', 0 ), // srv = Southern Sorsoganon
+ TRUETYPE_TAG('s','r','w', 0 ), // srw = Serua
+ TRUETYPE_TAG('s','r','x', 0 ), // srx = Sirmauri
+ TRUETYPE_TAG('s','r','y', 0 ), // sry = Sera
+ TRUETYPE_TAG('s','r','z', 0 ), // srz = Shahmirzadi
+ TRUETYPE_TAG('s','s','a', 0 ), // ssa = Nilo-Saharan languages
+ TRUETYPE_TAG('s','s','b', 0 ), // ssb = Southern Sama
+ TRUETYPE_TAG('s','s','c', 0 ), // ssc = Suba-Simbiti
+ TRUETYPE_TAG('s','s','d', 0 ), // ssd = Siroi
+ TRUETYPE_TAG('s','s','e', 0 ), // sse = Balangingi
+ TRUETYPE_TAG('s','s','f', 0 ), // ssf = Thao
+ TRUETYPE_TAG('s','s','g', 0 ), // ssg = Seimat
+ TRUETYPE_TAG('s','s','h', 0 ), // ssh = Shihhi Arabic
+ TRUETYPE_TAG('s','s','i', 0 ), // ssi = Sansi
+ TRUETYPE_TAG('s','s','j', 0 ), // ssj = Sausi
+ TRUETYPE_TAG('s','s','k', 0 ), // ssk = Sunam
+ TRUETYPE_TAG('s','s','l', 0 ), // ssl = Western Sisaala
+ TRUETYPE_TAG('s','s','m', 0 ), // ssm = Semnam
+ TRUETYPE_TAG('s','s','n', 0 ), // ssn = Waata
+ TRUETYPE_TAG('s','s','o', 0 ), // sso = Sissano
+ TRUETYPE_TAG('s','s','p', 0 ), // ssp = Spanish Sign Language
+ TRUETYPE_TAG('s','s','q', 0 ), // ssq = So'a
+ TRUETYPE_TAG('s','s','r', 0 ), // ssr = Swiss-French Sign Language
+ TRUETYPE_TAG('s','s','s', 0 ), // sss = Sô
+ TRUETYPE_TAG('s','s','t', 0 ), // sst = Sinasina
+ TRUETYPE_TAG('s','s','u', 0 ), // ssu = Susuami
+ TRUETYPE_TAG('s','s','v', 0 ), // ssv = Shark Bay
+ TRUETYPE_TAG('s','s','x', 0 ), // ssx = Samberigi
+ TRUETYPE_TAG('s','s','y', 0 ), // ssy = Saho
+ TRUETYPE_TAG('s','s','z', 0 ), // ssz = Sengseng
+ TRUETYPE_TAG('s','t','a', 0 ), // sta = Settla
+ TRUETYPE_TAG('s','t','b', 0 ), // stb = Northern Subanen
+ TRUETYPE_TAG('s','t','d', 0 ), // std = Sentinel
+ TRUETYPE_TAG('s','t','e', 0 ), // ste = Liana-Seti
+ TRUETYPE_TAG('s','t','f', 0 ), // stf = Seta
+ TRUETYPE_TAG('s','t','g', 0 ), // stg = Trieng
+ TRUETYPE_TAG('s','t','h', 0 ), // sth = Shelta
+ TRUETYPE_TAG('s','t','i', 0 ), // sti = Bulo Stieng
+ TRUETYPE_TAG('s','t','j', 0 ), // stj = Matya Samo
+ TRUETYPE_TAG('s','t','k', 0 ), // stk = Arammba
+ TRUETYPE_TAG('s','t','l', 0 ), // stl = Stellingwerfs
+ TRUETYPE_TAG('s','t','m', 0 ), // stm = Setaman
+ TRUETYPE_TAG('s','t','n', 0 ), // stn = Owa
+ TRUETYPE_TAG('s','t','o', 0 ), // sto = Stoney
+ TRUETYPE_TAG('s','t','p', 0 ), // stp = Southeastern Tepehuan
+ TRUETYPE_TAG('s','t','q', 0 ), // stq = Saterfriesisch
+ TRUETYPE_TAG('s','t','r', 0 ), // str = Straits Salish
+ TRUETYPE_TAG('s','t','s', 0 ), // sts = Shumashti
+ TRUETYPE_TAG('s','t','t', 0 ), // stt = Budeh Stieng
+ TRUETYPE_TAG('s','t','u', 0 ), // stu = Samtao
+ TRUETYPE_TAG('s','t','v', 0 ), // stv = Silt'e
+ TRUETYPE_TAG('s','t','w', 0 ), // stw = Satawalese
+ TRUETYPE_TAG('s','u','a', 0 ), // sua = Sulka
+ TRUETYPE_TAG('s','u','b', 0 ), // sub = Suku
+ TRUETYPE_TAG('s','u','c', 0 ), // suc = Western Subanon
+ TRUETYPE_TAG('s','u','e', 0 ), // sue = Suena
+ TRUETYPE_TAG('s','u','g', 0 ), // sug = Suganga
+ TRUETYPE_TAG('s','u','i', 0 ), // sui = Suki
+ TRUETYPE_TAG('s','u','j', 0 ), // suj = Shubi
+ TRUETYPE_TAG('s','u','k', 0 ), // suk = Sukuma
+ TRUETYPE_TAG('s','u','l', 0 ), // sul = Surigaonon
+ TRUETYPE_TAG('s','u','m', 0 ), // sum = Sumo-Mayangna
+ TRUETYPE_TAG('s','u','q', 0 ), // suq = Suri
+ TRUETYPE_TAG('s','u','r', 0 ), // sur = Mwaghavul
+ TRUETYPE_TAG('s','u','s', 0 ), // sus = Susu
+ TRUETYPE_TAG('s','u','t', 0 ), // sut = Subtiaba
+ TRUETYPE_TAG('s','u','v', 0 ), // suv = Sulung
+ TRUETYPE_TAG('s','u','w', 0 ), // suw = Sumbwa
+ TRUETYPE_TAG('s','u','x', 0 ), // sux = Sumerian
+ TRUETYPE_TAG('s','u','y', 0 ), // suy = Suyá
+ TRUETYPE_TAG('s','u','z', 0 ), // suz = Sunwar
+ TRUETYPE_TAG('s','v','a', 0 ), // sva = Svan
+ TRUETYPE_TAG('s','v','b', 0 ), // svb = Ulau-Suain
+ TRUETYPE_TAG('s','v','c', 0 ), // svc = Vincentian Creole English
+ TRUETYPE_TAG('s','v','e', 0 ), // sve = Serili
+ TRUETYPE_TAG('s','v','k', 0 ), // svk = Slovakian Sign Language
+ TRUETYPE_TAG('s','v','r', 0 ), // svr = Savara
+ TRUETYPE_TAG('s','v','s', 0 ), // svs = Savosavo
+ TRUETYPE_TAG('s','v','x', 0 ), // svx = Skalvian
+ TRUETYPE_TAG('s','w','b', 0 ), // swb = Maore Comorian
+ TRUETYPE_TAG('s','w','c', 0 ), // swc = Congo Swahili
+ TRUETYPE_TAG('s','w','f', 0 ), // swf = Sere
+ TRUETYPE_TAG('s','w','g', 0 ), // swg = Swabian
+ TRUETYPE_TAG('s','w','h', 0 ), // swh = Swahili (individual language)
+ TRUETYPE_TAG('s','w','i', 0 ), // swi = Sui
+ TRUETYPE_TAG('s','w','j', 0 ), // swj = Sira
+ TRUETYPE_TAG('s','w','k', 0 ), // swk = Malawi Sena
+ TRUETYPE_TAG('s','w','l', 0 ), // swl = Swedish Sign Language
+ TRUETYPE_TAG('s','w','m', 0 ), // swm = Samosa
+ TRUETYPE_TAG('s','w','n', 0 ), // swn = Sawknah
+ TRUETYPE_TAG('s','w','o', 0 ), // swo = Shanenawa
+ TRUETYPE_TAG('s','w','p', 0 ), // swp = Suau
+ TRUETYPE_TAG('s','w','q', 0 ), // swq = Sharwa
+ TRUETYPE_TAG('s','w','r', 0 ), // swr = Saweru
+ TRUETYPE_TAG('s','w','s', 0 ), // sws = Seluwasan
+ TRUETYPE_TAG('s','w','t', 0 ), // swt = Sawila
+ TRUETYPE_TAG('s','w','u', 0 ), // swu = Suwawa
+ TRUETYPE_TAG('s','w','v', 0 ), // swv = Shekhawati
+ TRUETYPE_TAG('s','w','w', 0 ), // sww = Sowa
+ TRUETYPE_TAG('s','w','x', 0 ), // swx = Suruahá
+ TRUETYPE_TAG('s','w','y', 0 ), // swy = Sarua
+ TRUETYPE_TAG('s','x','b', 0 ), // sxb = Suba
+ TRUETYPE_TAG('s','x','c', 0 ), // sxc = Sicanian
+ TRUETYPE_TAG('s','x','e', 0 ), // sxe = Sighu
+ TRUETYPE_TAG('s','x','g', 0 ), // sxg = Shixing
+ TRUETYPE_TAG('s','x','k', 0 ), // sxk = Southern Kalapuya
+ TRUETYPE_TAG('s','x','l', 0 ), // sxl = Selian
+ TRUETYPE_TAG('s','x','m', 0 ), // sxm = Samre
+ TRUETYPE_TAG('s','x','n', 0 ), // sxn = Sangir
+ TRUETYPE_TAG('s','x','o', 0 ), // sxo = Sorothaptic
+ TRUETYPE_TAG('s','x','r', 0 ), // sxr = Saaroa
+ TRUETYPE_TAG('s','x','s', 0 ), // sxs = Sasaru
+ TRUETYPE_TAG('s','x','u', 0 ), // sxu = Upper Saxon
+ TRUETYPE_TAG('s','x','w', 0 ), // sxw = Saxwe Gbe
+ TRUETYPE_TAG('s','y','a', 0 ), // sya = Siang
+ TRUETYPE_TAG('s','y','b', 0 ), // syb = Central Subanen
+ TRUETYPE_TAG('s','y','c', 0 ), // syc = Classical Syriac
+ TRUETYPE_TAG('s','y','d', 0 ), // syd = Samoyedic languages
+ TRUETYPE_TAG('s','y','i', 0 ), // syi = Seki
+ TRUETYPE_TAG('s','y','k', 0 ), // syk = Sukur
+ TRUETYPE_TAG('s','y','l', 0 ), // syl = Sylheti
+ TRUETYPE_TAG('s','y','m', 0 ), // sym = Maya Samo
+ TRUETYPE_TAG('s','y','n', 0 ), // syn = Senaya
+ TRUETYPE_TAG('s','y','o', 0 ), // syo = Suoy
+ TRUETYPE_TAG('s','y','r', 0 ), // syr = Syriac
+ TRUETYPE_TAG('s','y','s', 0 ), // sys = Sinyar
+ TRUETYPE_TAG('s','y','w', 0 ), // syw = Kagate
+ TRUETYPE_TAG('s','y','y', 0 ), // syy = Al-Sayyid Bedouin Sign Language
+ TRUETYPE_TAG('s','z','a', 0 ), // sza = Semelai
+ TRUETYPE_TAG('s','z','b', 0 ), // szb = Ngalum
+ TRUETYPE_TAG('s','z','c', 0 ), // szc = Semaq Beri
+ TRUETYPE_TAG('s','z','d', 0 ), // szd = Seru
+ TRUETYPE_TAG('s','z','e', 0 ), // sze = Seze
+ TRUETYPE_TAG('s','z','g', 0 ), // szg = Sengele
+ TRUETYPE_TAG('s','z','l', 0 ), // szl = Silesian
+ TRUETYPE_TAG('s','z','n', 0 ), // szn = Sula
+ TRUETYPE_TAG('s','z','p', 0 ), // szp = Suabo
+ TRUETYPE_TAG('s','z','v', 0 ), // szv = Isu (Fako Division)
+ TRUETYPE_TAG('s','z','w', 0 ), // szw = Sawai
+ TRUETYPE_TAG('t','a','a', 0 ), // taa = Lower Tanana
+ TRUETYPE_TAG('t','a','b', 0 ), // tab = Tabassaran
+ TRUETYPE_TAG('t','a','c', 0 ), // tac = Lowland Tarahumara
+ TRUETYPE_TAG('t','a','d', 0 ), // tad = Tause
+ TRUETYPE_TAG('t','a','e', 0 ), // tae = Tariana
+ TRUETYPE_TAG('t','a','f', 0 ), // taf = Tapirapé
+ TRUETYPE_TAG('t','a','g', 0 ), // tag = Tagoi
+ TRUETYPE_TAG('t','a','i', 0 ), // tai = Tai languages
+ TRUETYPE_TAG('t','a','j', 0 ), // taj = Eastern Tamang
+ TRUETYPE_TAG('t','a','k', 0 ), // tak = Tala
+ TRUETYPE_TAG('t','a','l', 0 ), // tal = Tal
+ TRUETYPE_TAG('t','a','n', 0 ), // tan = Tangale
+ TRUETYPE_TAG('t','a','o', 0 ), // tao = Yami
+ TRUETYPE_TAG('t','a','p', 0 ), // tap = Taabwa
+ TRUETYPE_TAG('t','a','q', 0 ), // taq = Tamasheq
+ TRUETYPE_TAG('t','a','r', 0 ), // tar = Central Tarahumara
+ TRUETYPE_TAG('t','a','s', 0 ), // tas = Tay Boi
+ TRUETYPE_TAG('t','a','u', 0 ), // tau = Upper Tanana
+ TRUETYPE_TAG('t','a','v', 0 ), // tav = Tatuyo
+ TRUETYPE_TAG('t','a','w', 0 ), // taw = Tai
+ TRUETYPE_TAG('t','a','x', 0 ), // tax = Tamki
+ TRUETYPE_TAG('t','a','y', 0 ), // tay = Atayal
+ TRUETYPE_TAG('t','a','z', 0 ), // taz = Tocho
+ TRUETYPE_TAG('t','b','a', 0 ), // tba = Aikanã
+ TRUETYPE_TAG('t','b','b', 0 ), // tbb = Tapeba
+ TRUETYPE_TAG('t','b','c', 0 ), // tbc = Takia
+ TRUETYPE_TAG('t','b','d', 0 ), // tbd = Kaki Ae
+ TRUETYPE_TAG('t','b','e', 0 ), // tbe = Tanimbili
+ TRUETYPE_TAG('t','b','f', 0 ), // tbf = Mandara
+ TRUETYPE_TAG('t','b','g', 0 ), // tbg = North Tairora
+ TRUETYPE_TAG('t','b','h', 0 ), // tbh = Thurawal
+ TRUETYPE_TAG('t','b','i', 0 ), // tbi = Gaam
+ TRUETYPE_TAG('t','b','j', 0 ), // tbj = Tiang
+ TRUETYPE_TAG('t','b','k', 0 ), // tbk = Calamian Tagbanwa
+ TRUETYPE_TAG('t','b','l', 0 ), // tbl = Tboli
+ TRUETYPE_TAG('t','b','m', 0 ), // tbm = Tagbu
+ TRUETYPE_TAG('t','b','n', 0 ), // tbn = Barro Negro Tunebo
+ TRUETYPE_TAG('t','b','o', 0 ), // tbo = Tawala
+ TRUETYPE_TAG('t','b','p', 0 ), // tbp = Taworta
+ TRUETYPE_TAG('t','b','q', 0 ), // tbq = Tibeto-Burman languages
+ TRUETYPE_TAG('t','b','r', 0 ), // tbr = Tumtum
+ TRUETYPE_TAG('t','b','s', 0 ), // tbs = Tanguat
+ TRUETYPE_TAG('t','b','t', 0 ), // tbt = Tembo (Kitembo)
+ TRUETYPE_TAG('t','b','u', 0 ), // tbu = Tubar
+ TRUETYPE_TAG('t','b','v', 0 ), // tbv = Tobo
+ TRUETYPE_TAG('t','b','w', 0 ), // tbw = Tagbanwa
+ TRUETYPE_TAG('t','b','x', 0 ), // tbx = Kapin
+ TRUETYPE_TAG('t','b','y', 0 ), // tby = Tabaru
+ TRUETYPE_TAG('t','b','z', 0 ), // tbz = Ditammari
+ TRUETYPE_TAG('t','c','a', 0 ), // tca = Ticuna
+ TRUETYPE_TAG('t','c','b', 0 ), // tcb = Tanacross
+ TRUETYPE_TAG('t','c','c', 0 ), // tcc = Datooga
+ TRUETYPE_TAG('t','c','d', 0 ), // tcd = Tafi
+ TRUETYPE_TAG('t','c','e', 0 ), // tce = Southern Tutchone
+ TRUETYPE_TAG('t','c','f', 0 ), // tcf = Malinaltepec Me'phaa
+ TRUETYPE_TAG('t','c','g', 0 ), // tcg = Tamagario
+ TRUETYPE_TAG('t','c','h', 0 ), // tch = Turks And Caicos Creole English
+ TRUETYPE_TAG('t','c','i', 0 ), // tci = Wára
+ TRUETYPE_TAG('t','c','k', 0 ), // tck = Tchitchege
+ TRUETYPE_TAG('t','c','l', 0 ), // tcl = Taman (Myanmar)
+ TRUETYPE_TAG('t','c','m', 0 ), // tcm = Tanahmerah
+ TRUETYPE_TAG('t','c','n', 0 ), // tcn = Tichurong
+ TRUETYPE_TAG('t','c','o', 0 ), // tco = Taungyo
+ TRUETYPE_TAG('t','c','p', 0 ), // tcp = Tawr Chin
+ TRUETYPE_TAG('t','c','q', 0 ), // tcq = Kaiy
+ TRUETYPE_TAG('t','c','s', 0 ), // tcs = Torres Strait Creole
+ TRUETYPE_TAG('t','c','t', 0 ), // tct = T'en
+ TRUETYPE_TAG('t','c','u', 0 ), // tcu = Southeastern Tarahumara
+ TRUETYPE_TAG('t','c','w', 0 ), // tcw = Tecpatlán Totonac
+ TRUETYPE_TAG('t','c','x', 0 ), // tcx = Toda
+ TRUETYPE_TAG('t','c','y', 0 ), // tcy = Tulu
+ TRUETYPE_TAG('t','c','z', 0 ), // tcz = Thado Chin
+ TRUETYPE_TAG('t','d','a', 0 ), // tda = Tagdal
+ TRUETYPE_TAG('t','d','b', 0 ), // tdb = Panchpargania
+ TRUETYPE_TAG('t','d','c', 0 ), // tdc = Emberá-Tadó
+ TRUETYPE_TAG('t','d','d', 0 ), // tdd = Tai Nüa
+ TRUETYPE_TAG('t','d','e', 0 ), // tde = Tiranige Diga Dogon
+ TRUETYPE_TAG('t','d','f', 0 ), // tdf = Talieng
+ TRUETYPE_TAG('t','d','g', 0 ), // tdg = Western Tamang
+ TRUETYPE_TAG('t','d','h', 0 ), // tdh = Thulung
+ TRUETYPE_TAG('t','d','i', 0 ), // tdi = Tomadino
+ TRUETYPE_TAG('t','d','j', 0 ), // tdj = Tajio
+ TRUETYPE_TAG('t','d','k', 0 ), // tdk = Tambas
+ TRUETYPE_TAG('t','d','l', 0 ), // tdl = Sur
+ TRUETYPE_TAG('t','d','n', 0 ), // tdn = Tondano
+ TRUETYPE_TAG('t','d','o', 0 ), // tdo = Teme
+ TRUETYPE_TAG('t','d','q', 0 ), // tdq = Tita
+ TRUETYPE_TAG('t','d','r', 0 ), // tdr = Todrah
+ TRUETYPE_TAG('t','d','s', 0 ), // tds = Doutai
+ TRUETYPE_TAG('t','d','t', 0 ), // tdt = Tetun Dili
+ TRUETYPE_TAG('t','d','u', 0 ), // tdu = Tempasuk Dusun
+ TRUETYPE_TAG('t','d','v', 0 ), // tdv = Toro
+ TRUETYPE_TAG('t','d','x', 0 ), // tdx = Tandroy-Mahafaly Malagasy
+ TRUETYPE_TAG('t','d','y', 0 ), // tdy = Tadyawan
+ TRUETYPE_TAG('t','e','a', 0 ), // tea = Temiar
+ TRUETYPE_TAG('t','e','b', 0 ), // teb = Tetete
+ TRUETYPE_TAG('t','e','c', 0 ), // tec = Terik
+ TRUETYPE_TAG('t','e','d', 0 ), // ted = Tepo Krumen
+ TRUETYPE_TAG('t','e','e', 0 ), // tee = Huehuetla Tepehua
+ TRUETYPE_TAG('t','e','f', 0 ), // tef = Teressa
+ TRUETYPE_TAG('t','e','g', 0 ), // teg = Teke-Tege
+ TRUETYPE_TAG('t','e','h', 0 ), // teh = Tehuelche
+ TRUETYPE_TAG('t','e','i', 0 ), // tei = Torricelli
+ TRUETYPE_TAG('t','e','k', 0 ), // tek = Ibali Teke
+ TRUETYPE_TAG('t','e','m', 0 ), // tem = Timne
+ TRUETYPE_TAG('t','e','n', 0 ), // ten = Tama (Colombia)
+ TRUETYPE_TAG('t','e','o', 0 ), // teo = Teso
+ TRUETYPE_TAG('t','e','p', 0 ), // tep = Tepecano
+ TRUETYPE_TAG('t','e','q', 0 ), // teq = Temein
+ TRUETYPE_TAG('t','e','r', 0 ), // ter = Tereno
+ TRUETYPE_TAG('t','e','s', 0 ), // tes = Tengger
+ TRUETYPE_TAG('t','e','t', 0 ), // tet = Tetum
+ TRUETYPE_TAG('t','e','u', 0 ), // teu = Soo
+ TRUETYPE_TAG('t','e','v', 0 ), // tev = Teor
+ TRUETYPE_TAG('t','e','w', 0 ), // tew = Tewa (USA)
+ TRUETYPE_TAG('t','e','x', 0 ), // tex = Tennet
+ TRUETYPE_TAG('t','e','y', 0 ), // tey = Tulishi
+ TRUETYPE_TAG('t','f','i', 0 ), // tfi = Tofin Gbe
+ TRUETYPE_TAG('t','f','n', 0 ), // tfn = Tanaina
+ TRUETYPE_TAG('t','f','o', 0 ), // tfo = Tefaro
+ TRUETYPE_TAG('t','f','r', 0 ), // tfr = Teribe
+ TRUETYPE_TAG('t','f','t', 0 ), // tft = Ternate
+ TRUETYPE_TAG('t','g','a', 0 ), // tga = Sagalla
+ TRUETYPE_TAG('t','g','b', 0 ), // tgb = Tobilung
+ TRUETYPE_TAG('t','g','c', 0 ), // tgc = Tigak
+ TRUETYPE_TAG('t','g','d', 0 ), // tgd = Ciwogai
+ TRUETYPE_TAG('t','g','e', 0 ), // tge = Eastern Gorkha Tamang
+ TRUETYPE_TAG('t','g','f', 0 ), // tgf = Chalikha
+ TRUETYPE_TAG('t','g','g', 0 ), // tgg = Tangga
+ TRUETYPE_TAG('t','g','h', 0 ), // tgh = Tobagonian Creole English
+ TRUETYPE_TAG('t','g','i', 0 ), // tgi = Lawunuia
+ TRUETYPE_TAG('t','g','n', 0 ), // tgn = Tandaganon
+ TRUETYPE_TAG('t','g','o', 0 ), // tgo = Sudest
+ TRUETYPE_TAG('t','g','p', 0 ), // tgp = Tangoa
+ TRUETYPE_TAG('t','g','q', 0 ), // tgq = Tring
+ TRUETYPE_TAG('t','g','r', 0 ), // tgr = Tareng
+ TRUETYPE_TAG('t','g','s', 0 ), // tgs = Nume
+ TRUETYPE_TAG('t','g','t', 0 ), // tgt = Central Tagbanwa
+ TRUETYPE_TAG('t','g','u', 0 ), // tgu = Tanggu
+ TRUETYPE_TAG('t','g','v', 0 ), // tgv = Tingui-Boto
+ TRUETYPE_TAG('t','g','w', 0 ), // tgw = Tagwana Senoufo
+ TRUETYPE_TAG('t','g','x', 0 ), // tgx = Tagish
+ TRUETYPE_TAG('t','g','y', 0 ), // tgy = Togoyo
+ TRUETYPE_TAG('t','h','c', 0 ), // thc = Tai Hang Tong
+ TRUETYPE_TAG('t','h','d', 0 ), // thd = Thayore
+ TRUETYPE_TAG('t','h','e', 0 ), // the = Chitwania Tharu
+ TRUETYPE_TAG('t','h','f', 0 ), // thf = Thangmi
+ TRUETYPE_TAG('t','h','h', 0 ), // thh = Northern Tarahumara
+ TRUETYPE_TAG('t','h','i', 0 ), // thi = Tai Long
+ TRUETYPE_TAG('t','h','k', 0 ), // thk = Tharaka
+ TRUETYPE_TAG('t','h','l', 0 ), // thl = Dangaura Tharu
+ TRUETYPE_TAG('t','h','m', 0 ), // thm = Aheu
+ TRUETYPE_TAG('t','h','n', 0 ), // thn = Thachanadan
+ TRUETYPE_TAG('t','h','p', 0 ), // thp = Thompson
+ TRUETYPE_TAG('t','h','q', 0 ), // thq = Kochila Tharu
+ TRUETYPE_TAG('t','h','r', 0 ), // thr = Rana Tharu
+ TRUETYPE_TAG('t','h','s', 0 ), // ths = Thakali
+ TRUETYPE_TAG('t','h','t', 0 ), // tht = Tahltan
+ TRUETYPE_TAG('t','h','u', 0 ), // thu = Thuri
+ TRUETYPE_TAG('t','h','v', 0 ), // thv = Tahaggart Tamahaq
+ TRUETYPE_TAG('t','h','w', 0 ), // thw = Thudam
+ TRUETYPE_TAG('t','h','x', 0 ), // thx = The
+ TRUETYPE_TAG('t','h','y', 0 ), // thy = Tha
+ TRUETYPE_TAG('t','h','z', 0 ), // thz = Tayart Tamajeq
+ TRUETYPE_TAG('t','i','a', 0 ), // tia = Tidikelt Tamazight
+ TRUETYPE_TAG('t','i','c', 0 ), // tic = Tira
+ TRUETYPE_TAG('t','i','d', 0 ), // tid = Tidong
+ TRUETYPE_TAG('t','i','e', 0 ), // tie = Tingal
+ TRUETYPE_TAG('t','i','f', 0 ), // tif = Tifal
+ TRUETYPE_TAG('t','i','g', 0 ), // tig = Tigre
+ TRUETYPE_TAG('t','i','h', 0 ), // tih = Timugon Murut
+ TRUETYPE_TAG('t','i','i', 0 ), // tii = Tiene
+ TRUETYPE_TAG('t','i','j', 0 ), // tij = Tilung
+ TRUETYPE_TAG('t','i','k', 0 ), // tik = Tikar
+ TRUETYPE_TAG('t','i','l', 0 ), // til = Tillamook
+ TRUETYPE_TAG('t','i','m', 0 ), // tim = Timbe
+ TRUETYPE_TAG('t','i','n', 0 ), // tin = Tindi
+ TRUETYPE_TAG('t','i','o', 0 ), // tio = Teop
+ TRUETYPE_TAG('t','i','p', 0 ), // tip = Trimuris
+ TRUETYPE_TAG('t','i','q', 0 ), // tiq = Tiéfo
+ TRUETYPE_TAG('t','i','s', 0 ), // tis = Masadiit Itneg
+ TRUETYPE_TAG('t','i','t', 0 ), // tit = Tinigua
+ TRUETYPE_TAG('t','i','u', 0 ), // tiu = Adasen
+ TRUETYPE_TAG('t','i','v', 0 ), // tiv = Tiv
+ TRUETYPE_TAG('t','i','w', 0 ), // tiw = Tiwi
+ TRUETYPE_TAG('t','i','x', 0 ), // tix = Southern Tiwa
+ TRUETYPE_TAG('t','i','y', 0 ), // tiy = Tiruray
+ TRUETYPE_TAG('t','i','z', 0 ), // tiz = Tai Hongjin
+ TRUETYPE_TAG('t','j','a', 0 ), // tja = Tajuasohn
+ TRUETYPE_TAG('t','j','g', 0 ), // tjg = Tunjung
+ TRUETYPE_TAG('t','j','i', 0 ), // tji = Northern Tujia
+ TRUETYPE_TAG('t','j','m', 0 ), // tjm = Timucua
+ TRUETYPE_TAG('t','j','n', 0 ), // tjn = Tonjon
+ TRUETYPE_TAG('t','j','o', 0 ), // tjo = Temacine Tamazight
+ TRUETYPE_TAG('t','j','s', 0 ), // tjs = Southern Tujia
+ TRUETYPE_TAG('t','j','u', 0 ), // tju = Tjurruru
+ TRUETYPE_TAG('t','k','a', 0 ), // tka = Truká
+ TRUETYPE_TAG('t','k','b', 0 ), // tkb = Buksa
+ TRUETYPE_TAG('t','k','d', 0 ), // tkd = Tukudede
+ TRUETYPE_TAG('t','k','e', 0 ), // tke = Takwane
+ TRUETYPE_TAG('t','k','f', 0 ), // tkf = Tukumanféd
+ TRUETYPE_TAG('t','k','g', 0 ), // tkg = Tesaka Malagasy
+ TRUETYPE_TAG('t','k','k', 0 ), // tkk = Takpa
+ TRUETYPE_TAG('t','k','l', 0 ), // tkl = Tokelau
+ TRUETYPE_TAG('t','k','m', 0 ), // tkm = Takelma
+ TRUETYPE_TAG('t','k','n', 0 ), // tkn = Toku-No-Shima
+ TRUETYPE_TAG('t','k','p', 0 ), // tkp = Tikopia
+ TRUETYPE_TAG('t','k','q', 0 ), // tkq = Tee
+ TRUETYPE_TAG('t','k','r', 0 ), // tkr = Tsakhur
+ TRUETYPE_TAG('t','k','s', 0 ), // tks = Takestani
+ TRUETYPE_TAG('t','k','t', 0 ), // tkt = Kathoriya Tharu
+ TRUETYPE_TAG('t','k','u', 0 ), // tku = Upper Necaxa Totonac
+ TRUETYPE_TAG('t','k','w', 0 ), // tkw = Teanu
+ TRUETYPE_TAG('t','k','x', 0 ), // tkx = Tangko
+ TRUETYPE_TAG('t','k','z', 0 ), // tkz = Takua
+ TRUETYPE_TAG('t','l','a', 0 ), // tla = Southwestern Tepehuan
+ TRUETYPE_TAG('t','l','b', 0 ), // tlb = Tobelo
+ TRUETYPE_TAG('t','l','c', 0 ), // tlc = Yecuatla Totonac
+ TRUETYPE_TAG('t','l','d', 0 ), // tld = Talaud
+ TRUETYPE_TAG('t','l','f', 0 ), // tlf = Telefol
+ TRUETYPE_TAG('t','l','g', 0 ), // tlg = Tofanma
+ TRUETYPE_TAG('t','l','h', 0 ), // tlh = Klingon
+ TRUETYPE_TAG('t','l','i', 0 ), // tli = Tlingit
+ TRUETYPE_TAG('t','l','j', 0 ), // tlj = Talinga-Bwisi
+ TRUETYPE_TAG('t','l','k', 0 ), // tlk = Taloki
+ TRUETYPE_TAG('t','l','l', 0 ), // tll = Tetela
+ TRUETYPE_TAG('t','l','m', 0 ), // tlm = Tolomako
+ TRUETYPE_TAG('t','l','n', 0 ), // tln = Talondo'
+ TRUETYPE_TAG('t','l','o', 0 ), // tlo = Talodi
+ TRUETYPE_TAG('t','l','p', 0 ), // tlp = Filomena Mata-Coahuitlán Totonac
+ TRUETYPE_TAG('t','l','q', 0 ), // tlq = Tai Loi
+ TRUETYPE_TAG('t','l','r', 0 ), // tlr = Talise
+ TRUETYPE_TAG('t','l','s', 0 ), // tls = Tambotalo
+ TRUETYPE_TAG('t','l','t', 0 ), // tlt = Teluti
+ TRUETYPE_TAG('t','l','u', 0 ), // tlu = Tulehu
+ TRUETYPE_TAG('t','l','v', 0 ), // tlv = Taliabu
+ TRUETYPE_TAG('t','l','w', 0 ), // tlw = South Wemale
+ TRUETYPE_TAG('t','l','x', 0 ), // tlx = Khehek
+ TRUETYPE_TAG('t','l','y', 0 ), // tly = Talysh
+ TRUETYPE_TAG('t','m','a', 0 ), // tma = Tama (Chad)
+ TRUETYPE_TAG('t','m','b', 0 ), // tmb = Katbol
+ TRUETYPE_TAG('t','m','c', 0 ), // tmc = Tumak
+ TRUETYPE_TAG('t','m','d', 0 ), // tmd = Haruai
+ TRUETYPE_TAG('t','m','e', 0 ), // tme = Tremembé
+ TRUETYPE_TAG('t','m','f', 0 ), // tmf = Toba-Maskoy
+ TRUETYPE_TAG('t','m','g', 0 ), // tmg = Ternateño
+ TRUETYPE_TAG('t','m','h', 0 ), // tmh = Tamashek
+ TRUETYPE_TAG('t','m','i', 0 ), // tmi = Tutuba
+ TRUETYPE_TAG('t','m','j', 0 ), // tmj = Samarokena
+ TRUETYPE_TAG('t','m','k', 0 ), // tmk = Northwestern Tamang
+ TRUETYPE_TAG('t','m','l', 0 ), // tml = Tamnim Citak
+ TRUETYPE_TAG('t','m','m', 0 ), // tmm = Tai Thanh
+ TRUETYPE_TAG('t','m','n', 0 ), // tmn = Taman (Indonesia)
+ TRUETYPE_TAG('t','m','o', 0 ), // tmo = Temoq
+ TRUETYPE_TAG('t','m','p', 0 ), // tmp = Tai Mène
+ TRUETYPE_TAG('t','m','q', 0 ), // tmq = Tumleo
+ TRUETYPE_TAG('t','m','r', 0 ), // tmr = Jewish Babylonian Aramaic (ca. 200-1200 CE)
+ TRUETYPE_TAG('t','m','s', 0 ), // tms = Tima
+ TRUETYPE_TAG('t','m','t', 0 ), // tmt = Tasmate
+ TRUETYPE_TAG('t','m','u', 0 ), // tmu = Iau
+ TRUETYPE_TAG('t','m','v', 0 ), // tmv = Tembo (Motembo)
+ TRUETYPE_TAG('t','m','w', 0 ), // tmw = Temuan
+ TRUETYPE_TAG('t','m','y', 0 ), // tmy = Tami
+ TRUETYPE_TAG('t','m','z', 0 ), // tmz = Tamanaku
+ TRUETYPE_TAG('t','n','a', 0 ), // tna = Tacana
+ TRUETYPE_TAG('t','n','b', 0 ), // tnb = Western Tunebo
+ TRUETYPE_TAG('t','n','c', 0 ), // tnc = Tanimuca-Retuarã
+ TRUETYPE_TAG('t','n','d', 0 ), // tnd = Angosturas Tunebo
+ TRUETYPE_TAG('t','n','e', 0 ), // tne = Tinoc Kallahan
+ TRUETYPE_TAG('t','n','f', 0 ), // tnf = Tangshewi
+ TRUETYPE_TAG('t','n','g', 0 ), // tng = Tobanga
+ TRUETYPE_TAG('t','n','h', 0 ), // tnh = Maiani
+ TRUETYPE_TAG('t','n','i', 0 ), // tni = Tandia
+ TRUETYPE_TAG('t','n','k', 0 ), // tnk = Kwamera
+ TRUETYPE_TAG('t','n','l', 0 ), // tnl = Lenakel
+ TRUETYPE_TAG('t','n','m', 0 ), // tnm = Tabla
+ TRUETYPE_TAG('t','n','n', 0 ), // tnn = North Tanna
+ TRUETYPE_TAG('t','n','o', 0 ), // tno = Toromono
+ TRUETYPE_TAG('t','n','p', 0 ), // tnp = Whitesands
+ TRUETYPE_TAG('t','n','q', 0 ), // tnq = Taino
+ TRUETYPE_TAG('t','n','r', 0 ), // tnr = Bedik
+ TRUETYPE_TAG('t','n','s', 0 ), // tns = Tenis
+ TRUETYPE_TAG('t','n','t', 0 ), // tnt = Tontemboan
+ TRUETYPE_TAG('t','n','u', 0 ), // tnu = Tay Khang
+ TRUETYPE_TAG('t','n','v', 0 ), // tnv = Tangchangya
+ TRUETYPE_TAG('t','n','w', 0 ), // tnw = Tonsawang
+ TRUETYPE_TAG('t','n','x', 0 ), // tnx = Tanema
+ TRUETYPE_TAG('t','n','y', 0 ), // tny = Tongwe
+ TRUETYPE_TAG('t','n','z', 0 ), // tnz = Tonga (Thailand)
+ TRUETYPE_TAG('t','o','b', 0 ), // tob = Toba
+ TRUETYPE_TAG('t','o','c', 0 ), // toc = Coyutla Totonac
+ TRUETYPE_TAG('t','o','d', 0 ), // tod = Toma
+ TRUETYPE_TAG('t','o','e', 0 ), // toe = Tomedes
+ TRUETYPE_TAG('t','o','f', 0 ), // tof = Gizrra
+ TRUETYPE_TAG('t','o','g', 0 ), // tog = Tonga (Nyasa)
+ TRUETYPE_TAG('t','o','h', 0 ), // toh = Gitonga
+ TRUETYPE_TAG('t','o','i', 0 ), // toi = Tonga (Zambia)
+ TRUETYPE_TAG('t','o','j', 0 ), // toj = Tojolabal
+ TRUETYPE_TAG('t','o','l', 0 ), // tol = Tolowa
+ TRUETYPE_TAG('t','o','m', 0 ), // tom = Tombulu
+ TRUETYPE_TAG('t','o','o', 0 ), // too = Xicotepec De Juárez Totonac
+ TRUETYPE_TAG('t','o','p', 0 ), // top = Papantla Totonac
+ TRUETYPE_TAG('t','o','q', 0 ), // toq = Toposa
+ TRUETYPE_TAG('t','o','r', 0 ), // tor = Togbo-Vara Banda
+ TRUETYPE_TAG('t','o','s', 0 ), // tos = Highland Totonac
+ TRUETYPE_TAG('t','o','u', 0 ), // tou = Tho
+ TRUETYPE_TAG('t','o','v', 0 ), // tov = Upper Taromi
+ TRUETYPE_TAG('t','o','w', 0 ), // tow = Jemez
+ TRUETYPE_TAG('t','o','x', 0 ), // tox = Tobian
+ TRUETYPE_TAG('t','o','y', 0 ), // toy = Topoiyo
+ TRUETYPE_TAG('t','o','z', 0 ), // toz = To
+ TRUETYPE_TAG('t','p','a', 0 ), // tpa = Taupota
+ TRUETYPE_TAG('t','p','c', 0 ), // tpc = Azoyú Me'phaa
+ TRUETYPE_TAG('t','p','e', 0 ), // tpe = Tippera
+ TRUETYPE_TAG('t','p','f', 0 ), // tpf = Tarpia
+ TRUETYPE_TAG('t','p','g', 0 ), // tpg = Kula
+ TRUETYPE_TAG('t','p','i', 0 ), // tpi = Tok Pisin
+ TRUETYPE_TAG('t','p','j', 0 ), // tpj = Tapieté
+ TRUETYPE_TAG('t','p','k', 0 ), // tpk = Tupinikin
+ TRUETYPE_TAG('t','p','l', 0 ), // tpl = Tlacoapa Me'phaa
+ TRUETYPE_TAG('t','p','m', 0 ), // tpm = Tampulma
+ TRUETYPE_TAG('t','p','n', 0 ), // tpn = Tupinambá
+ TRUETYPE_TAG('t','p','o', 0 ), // tpo = Tai Pao
+ TRUETYPE_TAG('t','p','p', 0 ), // tpp = Pisaflores Tepehua
+ TRUETYPE_TAG('t','p','q', 0 ), // tpq = Tukpa
+ TRUETYPE_TAG('t','p','r', 0 ), // tpr = Tuparí
+ TRUETYPE_TAG('t','p','t', 0 ), // tpt = Tlachichilco Tepehua
+ TRUETYPE_TAG('t','p','u', 0 ), // tpu = Tampuan
+ TRUETYPE_TAG('t','p','v', 0 ), // tpv = Tanapag
+ TRUETYPE_TAG('t','p','w', 0 ), // tpw = Tupí
+ TRUETYPE_TAG('t','p','x', 0 ), // tpx = Acatepec Me'phaa
+ TRUETYPE_TAG('t','p','y', 0 ), // tpy = Trumai
+ TRUETYPE_TAG('t','p','z', 0 ), // tpz = Tinputz
+ TRUETYPE_TAG('t','q','b', 0 ), // tqb = Tembé
+ TRUETYPE_TAG('t','q','l', 0 ), // tql = Lehali
+ TRUETYPE_TAG('t','q','m', 0 ), // tqm = Turumsa
+ TRUETYPE_TAG('t','q','n', 0 ), // tqn = Tenino
+ TRUETYPE_TAG('t','q','o', 0 ), // tqo = Toaripi
+ TRUETYPE_TAG('t','q','p', 0 ), // tqp = Tomoip
+ TRUETYPE_TAG('t','q','q', 0 ), // tqq = Tunni
+ TRUETYPE_TAG('t','q','r', 0 ), // tqr = Torona
+ TRUETYPE_TAG('t','q','t', 0 ), // tqt = Western Totonac
+ TRUETYPE_TAG('t','q','u', 0 ), // tqu = Touo
+ TRUETYPE_TAG('t','q','w', 0 ), // tqw = Tonkawa
+ TRUETYPE_TAG('t','r','a', 0 ), // tra = Tirahi
+ TRUETYPE_TAG('t','r','b', 0 ), // trb = Terebu
+ TRUETYPE_TAG('t','r','c', 0 ), // trc = Copala Triqui
+ TRUETYPE_TAG('t','r','d', 0 ), // trd = Turi
+ TRUETYPE_TAG('t','r','e', 0 ), // tre = East Tarangan
+ TRUETYPE_TAG('t','r','f', 0 ), // trf = Trinidadian Creole English
+ TRUETYPE_TAG('t','r','g', 0 ), // trg = Lishán Didán
+ TRUETYPE_TAG('t','r','h', 0 ), // trh = Turaka
+ TRUETYPE_TAG('t','r','i', 0 ), // tri = Trió
+ TRUETYPE_TAG('t','r','j', 0 ), // trj = Toram
+ TRUETYPE_TAG('t','r','k', 0 ), // trk = Turkic languages
+ TRUETYPE_TAG('t','r','l', 0 ), // trl = Traveller Scottish
+ TRUETYPE_TAG('t','r','m', 0 ), // trm = Tregami
+ TRUETYPE_TAG('t','r','n', 0 ), // trn = Trinitario
+ TRUETYPE_TAG('t','r','o', 0 ), // tro = Tarao Naga
+ TRUETYPE_TAG('t','r','p', 0 ), // trp = Kok Borok
+ TRUETYPE_TAG('t','r','q', 0 ), // trq = San Martín Itunyoso Triqui
+ TRUETYPE_TAG('t','r','r', 0 ), // trr = Taushiro
+ TRUETYPE_TAG('t','r','s', 0 ), // trs = Chicahuaxtla Triqui
+ TRUETYPE_TAG('t','r','t', 0 ), // trt = Tunggare
+ TRUETYPE_TAG('t','r','u', 0 ), // tru = Turoyo
+ TRUETYPE_TAG('t','r','v', 0 ), // trv = Taroko
+ TRUETYPE_TAG('t','r','w', 0 ), // trw = Torwali
+ TRUETYPE_TAG('t','r','x', 0 ), // trx = Tringgus-Sembaan Bidayuh
+ TRUETYPE_TAG('t','r','y', 0 ), // try = Turung
+ TRUETYPE_TAG('t','r','z', 0 ), // trz = Torá
+ TRUETYPE_TAG('t','s','a', 0 ), // tsa = Tsaangi
+ TRUETYPE_TAG('t','s','b', 0 ), // tsb = Tsamai
+ TRUETYPE_TAG('t','s','c', 0 ), // tsc = Tswa
+ TRUETYPE_TAG('t','s','d', 0 ), // tsd = Tsakonian
+ TRUETYPE_TAG('t','s','e', 0 ), // tse = Tunisian Sign Language
+ TRUETYPE_TAG('t','s','f', 0 ), // tsf = Southwestern Tamang
+ TRUETYPE_TAG('t','s','g', 0 ), // tsg = Tausug
+ TRUETYPE_TAG('t','s','h', 0 ), // tsh = Tsuvan
+ TRUETYPE_TAG('t','s','i', 0 ), // tsi = Tsimshian
+ TRUETYPE_TAG('t','s','j', 0 ), // tsj = Tshangla
+ TRUETYPE_TAG('t','s','k', 0 ), // tsk = Tseku
+ TRUETYPE_TAG('t','s','l', 0 ), // tsl = Ts'ün-Lao
+ TRUETYPE_TAG('t','s','m', 0 ), // tsm = Turkish Sign Language
+ TRUETYPE_TAG('t','s','p', 0 ), // tsp = Northern Toussian
+ TRUETYPE_TAG('t','s','q', 0 ), // tsq = Thai Sign Language
+ TRUETYPE_TAG('t','s','r', 0 ), // tsr = Akei
+ TRUETYPE_TAG('t','s','s', 0 ), // tss = Taiwan Sign Language
+ TRUETYPE_TAG('t','s','t', 0 ), // tst = Tondi Songway Kiini
+ TRUETYPE_TAG('t','s','u', 0 ), // tsu = Tsou
+ TRUETYPE_TAG('t','s','v', 0 ), // tsv = Tsogo
+ TRUETYPE_TAG('t','s','w', 0 ), // tsw = Tsishingini
+ TRUETYPE_TAG('t','s','x', 0 ), // tsx = Mubami
+ TRUETYPE_TAG('t','s','y', 0 ), // tsy = Tebul Sign Language
+ TRUETYPE_TAG('t','s','z', 0 ), // tsz = Purepecha
+ TRUETYPE_TAG('t','t','a', 0 ), // tta = Tutelo
+ TRUETYPE_TAG('t','t','b', 0 ), // ttb = Gaa
+ TRUETYPE_TAG('t','t','c', 0 ), // ttc = Tektiteko
+ TRUETYPE_TAG('t','t','d', 0 ), // ttd = Tauade
+ TRUETYPE_TAG('t','t','e', 0 ), // tte = Bwanabwana
+ TRUETYPE_TAG('t','t','f', 0 ), // ttf = Tuotomb
+ TRUETYPE_TAG('t','t','g', 0 ), // ttg = Tutong
+ TRUETYPE_TAG('t','t','h', 0 ), // tth = Upper Ta'oih
+ TRUETYPE_TAG('t','t','i', 0 ), // tti = Tobati
+ TRUETYPE_TAG('t','t','j', 0 ), // ttj = Tooro
+ TRUETYPE_TAG('t','t','k', 0 ), // ttk = Totoro
+ TRUETYPE_TAG('t','t','l', 0 ), // ttl = Totela
+ TRUETYPE_TAG('t','t','m', 0 ), // ttm = Northern Tutchone
+ TRUETYPE_TAG('t','t','n', 0 ), // ttn = Towei
+ TRUETYPE_TAG('t','t','o', 0 ), // tto = Lower Ta'oih
+ TRUETYPE_TAG('t','t','p', 0 ), // ttp = Tombelala
+ TRUETYPE_TAG('t','t','q', 0 ), // ttq = Tawallammat Tamajaq
+ TRUETYPE_TAG('t','t','r', 0 ), // ttr = Tera
+ TRUETYPE_TAG('t','t','s', 0 ), // tts = Northeastern Thai
+ TRUETYPE_TAG('t','t','t', 0 ), // ttt = Muslim Tat
+ TRUETYPE_TAG('t','t','u', 0 ), // ttu = Torau
+ TRUETYPE_TAG('t','t','v', 0 ), // ttv = Titan
+ TRUETYPE_TAG('t','t','w', 0 ), // ttw = Long Wat
+ TRUETYPE_TAG('t','t','y', 0 ), // tty = Sikaritai
+ TRUETYPE_TAG('t','t','z', 0 ), // ttz = Tsum
+ TRUETYPE_TAG('t','u','a', 0 ), // tua = Wiarumus
+ TRUETYPE_TAG('t','u','b', 0 ), // tub = Tübatulabal
+ TRUETYPE_TAG('t','u','c', 0 ), // tuc = Mutu
+ TRUETYPE_TAG('t','u','d', 0 ), // tud = Tuxá
+ TRUETYPE_TAG('t','u','e', 0 ), // tue = Tuyuca
+ TRUETYPE_TAG('t','u','f', 0 ), // tuf = Central Tunebo
+ TRUETYPE_TAG('t','u','g', 0 ), // tug = Tunia
+ TRUETYPE_TAG('t','u','h', 0 ), // tuh = Taulil
+ TRUETYPE_TAG('t','u','i', 0 ), // tui = Tupuri
+ TRUETYPE_TAG('t','u','j', 0 ), // tuj = Tugutil
+ TRUETYPE_TAG('t','u','l', 0 ), // tul = Tula
+ TRUETYPE_TAG('t','u','m', 0 ), // tum = Tumbuka
+ TRUETYPE_TAG('t','u','n', 0 ), // tun = Tunica
+ TRUETYPE_TAG('t','u','o', 0 ), // tuo = Tucano
+ TRUETYPE_TAG('t','u','p', 0 ), // tup = Tupi languages
+ TRUETYPE_TAG('t','u','q', 0 ), // tuq = Tedaga
+ TRUETYPE_TAG('t','u','s', 0 ), // tus = Tuscarora
+ TRUETYPE_TAG('t','u','t', 0 ), // tut = Altaic languages
+ TRUETYPE_TAG('t','u','u', 0 ), // tuu = Tututni
+ TRUETYPE_TAG('t','u','v', 0 ), // tuv = Turkana
+ TRUETYPE_TAG('t','u','w', 0 ), // tuw = Tungus languages
+ TRUETYPE_TAG('t','u','x', 0 ), // tux = Tuxináwa
+ TRUETYPE_TAG('t','u','y', 0 ), // tuy = Tugen
+ TRUETYPE_TAG('t','u','z', 0 ), // tuz = Turka
+ TRUETYPE_TAG('t','v','a', 0 ), // tva = Vaghua
+ TRUETYPE_TAG('t','v','d', 0 ), // tvd = Tsuvadi
+ TRUETYPE_TAG('t','v','e', 0 ), // tve = Te'un
+ TRUETYPE_TAG('t','v','k', 0 ), // tvk = Southeast Ambrym
+ TRUETYPE_TAG('t','v','l', 0 ), // tvl = Tuvalu
+ TRUETYPE_TAG('t','v','m', 0 ), // tvm = Tela-Masbuar
+ TRUETYPE_TAG('t','v','n', 0 ), // tvn = Tavoyan
+ TRUETYPE_TAG('t','v','o', 0 ), // tvo = Tidore
+ TRUETYPE_TAG('t','v','s', 0 ), // tvs = Taveta
+ TRUETYPE_TAG('t','v','t', 0 ), // tvt = Tutsa Naga
+ TRUETYPE_TAG('t','v','w', 0 ), // tvw = Sedoa
+ TRUETYPE_TAG('t','v','y', 0 ), // tvy = Timor Pidgin
+ TRUETYPE_TAG('t','w','a', 0 ), // twa = Twana
+ TRUETYPE_TAG('t','w','b', 0 ), // twb = Western Tawbuid
+ TRUETYPE_TAG('t','w','c', 0 ), // twc = Teshenawa
+ TRUETYPE_TAG('t','w','d', 0 ), // twd = Twents
+ TRUETYPE_TAG('t','w','e', 0 ), // twe = Tewa (Indonesia)
+ TRUETYPE_TAG('t','w','f', 0 ), // twf = Northern Tiwa
+ TRUETYPE_TAG('t','w','g', 0 ), // twg = Tereweng
+ TRUETYPE_TAG('t','w','h', 0 ), // twh = Tai Dón
+ TRUETYPE_TAG('t','w','l', 0 ), // twl = Tawara
+ TRUETYPE_TAG('t','w','m', 0 ), // twm = Tawang Monpa
+ TRUETYPE_TAG('t','w','n', 0 ), // twn = Twendi
+ TRUETYPE_TAG('t','w','o', 0 ), // two = Tswapong
+ TRUETYPE_TAG('t','w','p', 0 ), // twp = Ere
+ TRUETYPE_TAG('t','w','q', 0 ), // twq = Tasawaq
+ TRUETYPE_TAG('t','w','r', 0 ), // twr = Southwestern Tarahumara
+ TRUETYPE_TAG('t','w','t', 0 ), // twt = Turiwára
+ TRUETYPE_TAG('t','w','u', 0 ), // twu = Termanu
+ TRUETYPE_TAG('t','w','w', 0 ), // tww = Tuwari
+ TRUETYPE_TAG('t','w','x', 0 ), // twx = Tewe
+ TRUETYPE_TAG('t','w','y', 0 ), // twy = Tawoyan
+ TRUETYPE_TAG('t','x','a', 0 ), // txa = Tombonuo
+ TRUETYPE_TAG('t','x','b', 0 ), // txb = Tokharian B
+ TRUETYPE_TAG('t','x','c', 0 ), // txc = Tsetsaut
+ TRUETYPE_TAG('t','x','e', 0 ), // txe = Totoli
+ TRUETYPE_TAG('t','x','g', 0 ), // txg = Tangut
+ TRUETYPE_TAG('t','x','h', 0 ), // txh = Thracian
+ TRUETYPE_TAG('t','x','i', 0 ), // txi = Ikpeng
+ TRUETYPE_TAG('t','x','m', 0 ), // txm = Tomini
+ TRUETYPE_TAG('t','x','n', 0 ), // txn = West Tarangan
+ TRUETYPE_TAG('t','x','o', 0 ), // txo = Toto
+ TRUETYPE_TAG('t','x','q', 0 ), // txq = Tii
+ TRUETYPE_TAG('t','x','r', 0 ), // txr = Tartessian
+ TRUETYPE_TAG('t','x','s', 0 ), // txs = Tonsea
+ TRUETYPE_TAG('t','x','t', 0 ), // txt = Citak
+ TRUETYPE_TAG('t','x','u', 0 ), // txu = Kayapó
+ TRUETYPE_TAG('t','x','x', 0 ), // txx = Tatana
+ TRUETYPE_TAG('t','x','y', 0 ), // txy = Tanosy Malagasy
+ TRUETYPE_TAG('t','y','a', 0 ), // tya = Tauya
+ TRUETYPE_TAG('t','y','e', 0 ), // tye = Kyenga
+ TRUETYPE_TAG('t','y','h', 0 ), // tyh = O'du
+ TRUETYPE_TAG('t','y','i', 0 ), // tyi = Teke-Tsaayi
+ TRUETYPE_TAG('t','y','j', 0 ), // tyj = Tai Do
+ TRUETYPE_TAG('t','y','l', 0 ), // tyl = Thu Lao
+ TRUETYPE_TAG('t','y','n', 0 ), // tyn = Kombai
+ TRUETYPE_TAG('t','y','p', 0 ), // typ = Thaypan
+ TRUETYPE_TAG('t','y','r', 0 ), // tyr = Tai Daeng
+ TRUETYPE_TAG('t','y','s', 0 ), // tys = Tày Sa Pa
+ TRUETYPE_TAG('t','y','t', 0 ), // tyt = Tày Tac
+ TRUETYPE_TAG('t','y','u', 0 ), // tyu = Kua
+ TRUETYPE_TAG('t','y','v', 0 ), // tyv = Tuvinian
+ TRUETYPE_TAG('t','y','x', 0 ), // tyx = Teke-Tyee
+ TRUETYPE_TAG('t','y','z', 0 ), // tyz = Tày
+ TRUETYPE_TAG('t','z','a', 0 ), // tza = Tanzanian Sign Language
+ TRUETYPE_TAG('t','z','h', 0 ), // tzh = Tzeltal
+ TRUETYPE_TAG('t','z','j', 0 ), // tzj = Tz'utujil
+ TRUETYPE_TAG('t','z','m', 0 ), // tzm = Central Atlas Tamazight
+ TRUETYPE_TAG('t','z','n', 0 ), // tzn = Tugun
+ TRUETYPE_TAG('t','z','o', 0 ), // tzo = Tzotzil
+ TRUETYPE_TAG('t','z','x', 0 ), // tzx = Tabriak
+ TRUETYPE_TAG('u','a','m', 0 ), // uam = Uamué
+ TRUETYPE_TAG('u','a','n', 0 ), // uan = Kuan
+ TRUETYPE_TAG('u','a','r', 0 ), // uar = Tairuma
+ TRUETYPE_TAG('u','b','a', 0 ), // uba = Ubang
+ TRUETYPE_TAG('u','b','i', 0 ), // ubi = Ubi
+ TRUETYPE_TAG('u','b','l', 0 ), // ubl = Buhi'non Bikol
+ TRUETYPE_TAG('u','b','r', 0 ), // ubr = Ubir
+ TRUETYPE_TAG('u','b','u', 0 ), // ubu = Umbu-Ungu
+ TRUETYPE_TAG('u','b','y', 0 ), // uby = Ubykh
+ TRUETYPE_TAG('u','d','a', 0 ), // uda = Uda
+ TRUETYPE_TAG('u','d','e', 0 ), // ude = Udihe
+ TRUETYPE_TAG('u','d','g', 0 ), // udg = Muduga
+ TRUETYPE_TAG('u','d','i', 0 ), // udi = Udi
+ TRUETYPE_TAG('u','d','j', 0 ), // udj = Ujir
+ TRUETYPE_TAG('u','d','l', 0 ), // udl = Wuzlam
+ TRUETYPE_TAG('u','d','m', 0 ), // udm = Udmurt
+ TRUETYPE_TAG('u','d','u', 0 ), // udu = Uduk
+ TRUETYPE_TAG('u','e','s', 0 ), // ues = Kioko
+ TRUETYPE_TAG('u','f','i', 0 ), // ufi = Ufim
+ TRUETYPE_TAG('u','g','a', 0 ), // uga = Ugaritic
+ TRUETYPE_TAG('u','g','b', 0 ), // ugb = Kuku-Ugbanh
+ TRUETYPE_TAG('u','g','e', 0 ), // uge = Ughele
+ TRUETYPE_TAG('u','g','n', 0 ), // ugn = Ugandan Sign Language
+ TRUETYPE_TAG('u','g','o', 0 ), // ugo = Ugong
+ TRUETYPE_TAG('u','g','y', 0 ), // ugy = Uruguayan Sign Language
+ TRUETYPE_TAG('u','h','a', 0 ), // uha = Uhami
+ TRUETYPE_TAG('u','h','n', 0 ), // uhn = Damal
+ TRUETYPE_TAG('u','i','s', 0 ), // uis = Uisai
+ TRUETYPE_TAG('u','i','v', 0 ), // uiv = Iyive
+ TRUETYPE_TAG('u','j','i', 0 ), // uji = Tanjijili
+ TRUETYPE_TAG('u','k','a', 0 ), // uka = Kaburi
+ TRUETYPE_TAG('u','k','g', 0 ), // ukg = Ukuriguma
+ TRUETYPE_TAG('u','k','h', 0 ), // ukh = Ukhwejo
+ TRUETYPE_TAG('u','k','l', 0 ), // ukl = Ukrainian Sign Language
+ TRUETYPE_TAG('u','k','p', 0 ), // ukp = Ukpe-Bayobiri
+ TRUETYPE_TAG('u','k','q', 0 ), // ukq = Ukwa
+ TRUETYPE_TAG('u','k','s', 0 ), // uks = Urubú-Kaapor Sign Language
+ TRUETYPE_TAG('u','k','u', 0 ), // uku = Ukue
+ TRUETYPE_TAG('u','k','w', 0 ), // ukw = Ukwuani-Aboh-Ndoni
+ TRUETYPE_TAG('u','l','a', 0 ), // ula = Fungwa
+ TRUETYPE_TAG('u','l','b', 0 ), // ulb = Ulukwumi
+ TRUETYPE_TAG('u','l','c', 0 ), // ulc = Ulch
+ TRUETYPE_TAG('u','l','f', 0 ), // ulf = Usku
+ TRUETYPE_TAG('u','l','i', 0 ), // uli = Ulithian
+ TRUETYPE_TAG('u','l','k', 0 ), // ulk = Meriam
+ TRUETYPE_TAG('u','l','l', 0 ), // ull = Ullatan
+ TRUETYPE_TAG('u','l','m', 0 ), // ulm = Ulumanda'
+ TRUETYPE_TAG('u','l','n', 0 ), // uln = Unserdeutsch
+ TRUETYPE_TAG('u','l','u', 0 ), // ulu = Uma' Lung
+ TRUETYPE_TAG('u','l','w', 0 ), // ulw = Ulwa
+ TRUETYPE_TAG('u','m','a', 0 ), // uma = Umatilla
+ TRUETYPE_TAG('u','m','b', 0 ), // umb = Umbundu
+ TRUETYPE_TAG('u','m','c', 0 ), // umc = Marrucinian
+ TRUETYPE_TAG('u','m','d', 0 ), // umd = Umbindhamu
+ TRUETYPE_TAG('u','m','g', 0 ), // umg = Umbuygamu
+ TRUETYPE_TAG('u','m','i', 0 ), // umi = Ukit
+ TRUETYPE_TAG('u','m','m', 0 ), // umm = Umon
+ TRUETYPE_TAG('u','m','n', 0 ), // umn = Makyan Naga
+ TRUETYPE_TAG('u','m','o', 0 ), // umo = Umotína
+ TRUETYPE_TAG('u','m','p', 0 ), // ump = Umpila
+ TRUETYPE_TAG('u','m','r', 0 ), // umr = Umbugarla
+ TRUETYPE_TAG('u','m','s', 0 ), // ums = Pendau
+ TRUETYPE_TAG('u','m','u', 0 ), // umu = Munsee
+ TRUETYPE_TAG('u','n','a', 0 ), // una = North Watut
+ TRUETYPE_TAG('u','n','d', 0 ), // und = Undetermined
+ TRUETYPE_TAG('u','n','e', 0 ), // une = Uneme
+ TRUETYPE_TAG('u','n','g', 0 ), // ung = Ngarinyin
+ TRUETYPE_TAG('u','n','k', 0 ), // unk = Enawené-Nawé
+ TRUETYPE_TAG('u','n','m', 0 ), // unm = Unami
+ TRUETYPE_TAG('u','n','p', 0 ), // unp = Worora
+ TRUETYPE_TAG('u','n','r', 0 ), // unr = Mundari
+ TRUETYPE_TAG('u','n','x', 0 ), // unx = Munda
+ TRUETYPE_TAG('u','n','z', 0 ), // unz = Unde Kaili
+ TRUETYPE_TAG('u','o','k', 0 ), // uok = Uokha
+ TRUETYPE_TAG('u','p','i', 0 ), // upi = Umeda
+ TRUETYPE_TAG('u','p','v', 0 ), // upv = Uripiv-Wala-Rano-Atchin
+ TRUETYPE_TAG('u','r','a', 0 ), // ura = Urarina
+ TRUETYPE_TAG('u','r','b', 0 ), // urb = Urubú-Kaapor
+ TRUETYPE_TAG('u','r','c', 0 ), // urc = Urningangg
+ TRUETYPE_TAG('u','r','e', 0 ), // ure = Uru
+ TRUETYPE_TAG('u','r','f', 0 ), // urf = Uradhi
+ TRUETYPE_TAG('u','r','g', 0 ), // urg = Urigina
+ TRUETYPE_TAG('u','r','h', 0 ), // urh = Urhobo
+ TRUETYPE_TAG('u','r','i', 0 ), // uri = Urim
+ TRUETYPE_TAG('u','r','j', 0 ), // urj = Uralic languages
+ TRUETYPE_TAG('u','r','k', 0 ), // urk = Urak Lawoi'
+ TRUETYPE_TAG('u','r','l', 0 ), // url = Urali
+ TRUETYPE_TAG('u','r','m', 0 ), // urm = Urapmin
+ TRUETYPE_TAG('u','r','n', 0 ), // urn = Uruangnirin
+ TRUETYPE_TAG('u','r','o', 0 ), // uro = Ura (Papua New Guinea)
+ TRUETYPE_TAG('u','r','p', 0 ), // urp = Uru-Pa-In
+ TRUETYPE_TAG('u','r','r', 0 ), // urr = Lehalurup
+ TRUETYPE_TAG('u','r','t', 0 ), // urt = Urat
+ TRUETYPE_TAG('u','r','u', 0 ), // uru = Urumi
+ TRUETYPE_TAG('u','r','v', 0 ), // urv = Uruava
+ TRUETYPE_TAG('u','r','w', 0 ), // urw = Sop
+ TRUETYPE_TAG('u','r','x', 0 ), // urx = Urimo
+ TRUETYPE_TAG('u','r','y', 0 ), // ury = Orya
+ TRUETYPE_TAG('u','r','z', 0 ), // urz = Uru-Eu-Wau-Wau
+ TRUETYPE_TAG('u','s','a', 0 ), // usa = Usarufa
+ TRUETYPE_TAG('u','s','h', 0 ), // ush = Ushojo
+ TRUETYPE_TAG('u','s','i', 0 ), // usi = Usui
+ TRUETYPE_TAG('u','s','k', 0 ), // usk = Usaghade
+ TRUETYPE_TAG('u','s','p', 0 ), // usp = Uspanteco
+ TRUETYPE_TAG('u','s','u', 0 ), // usu = Uya
+ TRUETYPE_TAG('u','t','a', 0 ), // uta = Otank
+ TRUETYPE_TAG('u','t','e', 0 ), // ute = Ute-Southern Paiute
+ TRUETYPE_TAG('u','t','p', 0 ), // utp = Amba (Solomon Islands)
+ TRUETYPE_TAG('u','t','r', 0 ), // utr = Etulo
+ TRUETYPE_TAG('u','t','u', 0 ), // utu = Utu
+ TRUETYPE_TAG('u','u','m', 0 ), // uum = Urum
+ TRUETYPE_TAG('u','u','n', 0 ), // uun = Kulon-Pazeh
+ TRUETYPE_TAG('u','u','r', 0 ), // uur = Ura (Vanuatu)
+ TRUETYPE_TAG('u','u','u', 0 ), // uuu = U
+ TRUETYPE_TAG('u','v','e', 0 ), // uve = West Uvean
+ TRUETYPE_TAG('u','v','h', 0 ), // uvh = Uri
+ TRUETYPE_TAG('u','v','l', 0 ), // uvl = Lote
+ TRUETYPE_TAG('u','w','a', 0 ), // uwa = Kuku-Uwanh
+ TRUETYPE_TAG('u','y','a', 0 ), // uya = Doko-Uyanga
+ TRUETYPE_TAG('u','z','n', 0 ), // uzn = Northern Uzbek
+ TRUETYPE_TAG('u','z','s', 0 ), // uzs = Southern Uzbek
+ TRUETYPE_TAG('v','a','a', 0 ), // vaa = Vaagri Booli
+ TRUETYPE_TAG('v','a','e', 0 ), // vae = Vale
+ TRUETYPE_TAG('v','a','f', 0 ), // vaf = Vafsi
+ TRUETYPE_TAG('v','a','g', 0 ), // vag = Vagla
+ TRUETYPE_TAG('v','a','h', 0 ), // vah = Varhadi-Nagpuri
+ TRUETYPE_TAG('v','a','i', 0 ), // vai = Vai
+ TRUETYPE_TAG('v','a','j', 0 ), // vaj = Vasekela Bushman
+ TRUETYPE_TAG('v','a','l', 0 ), // val = Vehes
+ TRUETYPE_TAG('v','a','m', 0 ), // vam = Vanimo
+ TRUETYPE_TAG('v','a','n', 0 ), // van = Valman
+ TRUETYPE_TAG('v','a','o', 0 ), // vao = Vao
+ TRUETYPE_TAG('v','a','p', 0 ), // vap = Vaiphei
+ TRUETYPE_TAG('v','a','r', 0 ), // var = Huarijio
+ TRUETYPE_TAG('v','a','s', 0 ), // vas = Vasavi
+ TRUETYPE_TAG('v','a','u', 0 ), // vau = Vanuma
+ TRUETYPE_TAG('v','a','v', 0 ), // vav = Varli
+ TRUETYPE_TAG('v','a','y', 0 ), // vay = Wayu
+ TRUETYPE_TAG('v','b','b', 0 ), // vbb = Southeast Babar
+ TRUETYPE_TAG('v','b','k', 0 ), // vbk = Southwestern Bontok
+ TRUETYPE_TAG('v','e','c', 0 ), // vec = Venetian
+ TRUETYPE_TAG('v','e','d', 0 ), // ved = Veddah
+ TRUETYPE_TAG('v','e','l', 0 ), // vel = Veluws
+ TRUETYPE_TAG('v','e','m', 0 ), // vem = Vemgo-Mabas
+ TRUETYPE_TAG('v','e','o', 0 ), // veo = Ventureño
+ TRUETYPE_TAG('v','e','p', 0 ), // vep = Veps
+ TRUETYPE_TAG('v','e','r', 0 ), // ver = Mom Jango
+ TRUETYPE_TAG('v','g','r', 0 ), // vgr = Vaghri
+ TRUETYPE_TAG('v','g','t', 0 ), // vgt = Vlaamse Gebarentaal
+ TRUETYPE_TAG('v','i','c', 0 ), // vic = Virgin Islands Creole English
+ TRUETYPE_TAG('v','i','d', 0 ), // vid = Vidunda
+ TRUETYPE_TAG('v','i','f', 0 ), // vif = Vili
+ TRUETYPE_TAG('v','i','g', 0 ), // vig = Viemo
+ TRUETYPE_TAG('v','i','l', 0 ), // vil = Vilela
+ TRUETYPE_TAG('v','i','n', 0 ), // vin = Vinza
+ TRUETYPE_TAG('v','i','s', 0 ), // vis = Vishavan
+ TRUETYPE_TAG('v','i','t', 0 ), // vit = Viti
+ TRUETYPE_TAG('v','i','v', 0 ), // viv = Iduna
+ TRUETYPE_TAG('v','k','a', 0 ), // vka = Kariyarra
+ TRUETYPE_TAG('v','k','i', 0 ), // vki = Ija-Zuba
+ TRUETYPE_TAG('v','k','j', 0 ), // vkj = Kujarge
+ TRUETYPE_TAG('v','k','k', 0 ), // vkk = Kaur
+ TRUETYPE_TAG('v','k','l', 0 ), // vkl = Kulisusu
+ TRUETYPE_TAG('v','k','m', 0 ), // vkm = Kamakan
+ TRUETYPE_TAG('v','k','o', 0 ), // vko = Kodeoha
+ TRUETYPE_TAG('v','k','p', 0 ), // vkp = Korlai Creole Portuguese
+ TRUETYPE_TAG('v','k','t', 0 ), // vkt = Tenggarong Kutai Malay
+ TRUETYPE_TAG('v','k','u', 0 ), // vku = Kurrama
+ TRUETYPE_TAG('v','l','p', 0 ), // vlp = Valpei
+ TRUETYPE_TAG('v','l','s', 0 ), // vls = Vlaams
+ TRUETYPE_TAG('v','m','a', 0 ), // vma = Martuyhunira
+ TRUETYPE_TAG('v','m','b', 0 ), // vmb = Mbabaram
+ TRUETYPE_TAG('v','m','c', 0 ), // vmc = Juxtlahuaca Mixtec
+ TRUETYPE_TAG('v','m','d', 0 ), // vmd = Mudu Koraga
+ TRUETYPE_TAG('v','m','e', 0 ), // vme = East Masela
+ TRUETYPE_TAG('v','m','f', 0 ), // vmf = Mainfränkisch
+ TRUETYPE_TAG('v','m','g', 0 ), // vmg = Minigir
+ TRUETYPE_TAG('v','m','h', 0 ), // vmh = Maraghei
+ TRUETYPE_TAG('v','m','i', 0 ), // vmi = Miwa
+ TRUETYPE_TAG('v','m','j', 0 ), // vmj = Ixtayutla Mixtec
+ TRUETYPE_TAG('v','m','k', 0 ), // vmk = Makhuwa-Shirima
+ TRUETYPE_TAG('v','m','l', 0 ), // vml = Malgana
+ TRUETYPE_TAG('v','m','m', 0 ), // vmm = Mitlatongo Mixtec
+ TRUETYPE_TAG('v','m','p', 0 ), // vmp = Soyaltepec Mazatec
+ TRUETYPE_TAG('v','m','q', 0 ), // vmq = Soyaltepec Mixtec
+ TRUETYPE_TAG('v','m','r', 0 ), // vmr = Marenje
+ TRUETYPE_TAG('v','m','s', 0 ), // vms = Moksela
+ TRUETYPE_TAG('v','m','u', 0 ), // vmu = Muluridyi
+ TRUETYPE_TAG('v','m','v', 0 ), // vmv = Valley Maidu
+ TRUETYPE_TAG('v','m','w', 0 ), // vmw = Makhuwa
+ TRUETYPE_TAG('v','m','x', 0 ), // vmx = Tamazola Mixtec
+ TRUETYPE_TAG('v','m','y', 0 ), // vmy = Ayautla Mazatec
+ TRUETYPE_TAG('v','m','z', 0 ), // vmz = Mazatlán Mazatec
+ TRUETYPE_TAG('v','n','k', 0 ), // vnk = Vano
+ TRUETYPE_TAG('v','n','m', 0 ), // vnm = Vinmavis
+ TRUETYPE_TAG('v','n','p', 0 ), // vnp = Vunapu
+ TRUETYPE_TAG('v','o','r', 0 ), // vor = Voro
+ TRUETYPE_TAG('v','o','t', 0 ), // vot = Votic
+ TRUETYPE_TAG('v','r','a', 0 ), // vra = Vera'a
+ TRUETYPE_TAG('v','r','o', 0 ), // vro = Võro
+ TRUETYPE_TAG('v','r','s', 0 ), // vrs = Varisi
+ TRUETYPE_TAG('v','r','t', 0 ), // vrt = Burmbar
+ TRUETYPE_TAG('v','s','i', 0 ), // vsi = Moldova Sign Language
+ TRUETYPE_TAG('v','s','l', 0 ), // vsl = Venezuelan Sign Language
+ TRUETYPE_TAG('v','s','v', 0 ), // vsv = Valencian Sign Language
+ TRUETYPE_TAG('v','t','o', 0 ), // vto = Vitou
+ TRUETYPE_TAG('v','u','m', 0 ), // vum = Vumbu
+ TRUETYPE_TAG('v','u','n', 0 ), // vun = Vunjo
+ TRUETYPE_TAG('v','u','t', 0 ), // vut = Vute
+ TRUETYPE_TAG('v','w','a', 0 ), // vwa = Awa (China)
+ TRUETYPE_TAG('w','a','a', 0 ), // waa = Walla Walla
+ TRUETYPE_TAG('w','a','b', 0 ), // wab = Wab
+ TRUETYPE_TAG('w','a','c', 0 ), // wac = Wasco-Wishram
+ TRUETYPE_TAG('w','a','d', 0 ), // wad = Wandamen
+ TRUETYPE_TAG('w','a','e', 0 ), // wae = Walser
+ TRUETYPE_TAG('w','a','f', 0 ), // waf = Wakoná
+ TRUETYPE_TAG('w','a','g', 0 ), // wag = Wa'ema
+ TRUETYPE_TAG('w','a','h', 0 ), // wah = Watubela
+ TRUETYPE_TAG('w','a','i', 0 ), // wai = Wares
+ TRUETYPE_TAG('w','a','j', 0 ), // waj = Waffa
+ TRUETYPE_TAG('w','a','k', 0 ), // wak = Wakashan languages
+ TRUETYPE_TAG('w','a','l', 0 ), // wal = Wolaytta
+ TRUETYPE_TAG('w','a','m', 0 ), // wam = Wampanoag
+ TRUETYPE_TAG('w','a','n', 0 ), // wan = Wan
+ TRUETYPE_TAG('w','a','o', 0 ), // wao = Wappo
+ TRUETYPE_TAG('w','a','p', 0 ), // wap = Wapishana
+ TRUETYPE_TAG('w','a','q', 0 ), // waq = Wageman
+ TRUETYPE_TAG('w','a','r', 0 ), // war = Waray (Philippines)
+ TRUETYPE_TAG('w','a','s', 0 ), // was = Washo
+ TRUETYPE_TAG('w','a','t', 0 ), // wat = Kaninuwa
+ TRUETYPE_TAG('w','a','u', 0 ), // wau = Waurá
+ TRUETYPE_TAG('w','a','v', 0 ), // wav = Waka
+ TRUETYPE_TAG('w','a','w', 0 ), // waw = Waiwai
+ TRUETYPE_TAG('w','a','x', 0 ), // wax = Watam
+ TRUETYPE_TAG('w','a','y', 0 ), // way = Wayana
+ TRUETYPE_TAG('w','a','z', 0 ), // waz = Wampur
+ TRUETYPE_TAG('w','b','a', 0 ), // wba = Warao
+ TRUETYPE_TAG('w','b','b', 0 ), // wbb = Wabo
+ TRUETYPE_TAG('w','b','e', 0 ), // wbe = Waritai
+ TRUETYPE_TAG('w','b','f', 0 ), // wbf = Wara
+ TRUETYPE_TAG('w','b','h', 0 ), // wbh = Wanda
+ TRUETYPE_TAG('w','b','i', 0 ), // wbi = Vwanji
+ TRUETYPE_TAG('w','b','j', 0 ), // wbj = Alagwa
+ TRUETYPE_TAG('w','b','k', 0 ), // wbk = Waigali
+ TRUETYPE_TAG('w','b','l', 0 ), // wbl = Wakhi
+ TRUETYPE_TAG('w','b','m', 0 ), // wbm = Wa
+ TRUETYPE_TAG('w','b','p', 0 ), // wbp = Warlpiri
+ TRUETYPE_TAG('w','b','q', 0 ), // wbq = Waddar
+ TRUETYPE_TAG('w','b','r', 0 ), // wbr = Wagdi
+ TRUETYPE_TAG('w','b','t', 0 ), // wbt = Wanman
+ TRUETYPE_TAG('w','b','v', 0 ), // wbv = Wajarri
+ TRUETYPE_TAG('w','b','w', 0 ), // wbw = Woi
+ TRUETYPE_TAG('w','c','a', 0 ), // wca = Yanomámi
+ TRUETYPE_TAG('w','c','i', 0 ), // wci = Waci Gbe
+ TRUETYPE_TAG('w','d','d', 0 ), // wdd = Wandji
+ TRUETYPE_TAG('w','d','g', 0 ), // wdg = Wadaginam
+ TRUETYPE_TAG('w','d','j', 0 ), // wdj = Wadjiginy
+ TRUETYPE_TAG('w','d','u', 0 ), // wdu = Wadjigu
+ TRUETYPE_TAG('w','e','a', 0 ), // wea = Wewaw
+ TRUETYPE_TAG('w','e','c', 0 ), // wec = Wè Western
+ TRUETYPE_TAG('w','e','d', 0 ), // wed = Wedau
+ TRUETYPE_TAG('w','e','h', 0 ), // weh = Weh
+ TRUETYPE_TAG('w','e','i', 0 ), // wei = Kiunum
+ TRUETYPE_TAG('w','e','m', 0 ), // wem = Weme Gbe
+ TRUETYPE_TAG('w','e','n', 0 ), // wen = Sorbian languages
+ TRUETYPE_TAG('w','e','o', 0 ), // weo = North Wemale
+ TRUETYPE_TAG('w','e','p', 0 ), // wep = Westphalien
+ TRUETYPE_TAG('w','e','r', 0 ), // wer = Weri
+ TRUETYPE_TAG('w','e','s', 0 ), // wes = Cameroon Pidgin
+ TRUETYPE_TAG('w','e','t', 0 ), // wet = Perai
+ TRUETYPE_TAG('w','e','u', 0 ), // weu = Welaung
+ TRUETYPE_TAG('w','e','w', 0 ), // wew = Wejewa
+ TRUETYPE_TAG('w','f','g', 0 ), // wfg = Yafi
+ TRUETYPE_TAG('w','g','a', 0 ), // wga = Wagaya
+ TRUETYPE_TAG('w','g','b', 0 ), // wgb = Wagawaga
+ TRUETYPE_TAG('w','g','g', 0 ), // wgg = Wangganguru
+ TRUETYPE_TAG('w','g','i', 0 ), // wgi = Wahgi
+ TRUETYPE_TAG('w','g','o', 0 ), // wgo = Waigeo
+ TRUETYPE_TAG('w','g','w', 0 ), // wgw = Wagawaga
+ TRUETYPE_TAG('w','g','y', 0 ), // wgy = Warrgamay
+ TRUETYPE_TAG('w','h','a', 0 ), // wha = Manusela
+ TRUETYPE_TAG('w','h','g', 0 ), // whg = North Wahgi
+ TRUETYPE_TAG('w','h','k', 0 ), // whk = Wahau Kenyah
+ TRUETYPE_TAG('w','h','u', 0 ), // whu = Wahau Kayan
+ TRUETYPE_TAG('w','i','b', 0 ), // wib = Southern Toussian
+ TRUETYPE_TAG('w','i','c', 0 ), // wic = Wichita
+ TRUETYPE_TAG('w','i','e', 0 ), // wie = Wik-Epa
+ TRUETYPE_TAG('w','i','f', 0 ), // wif = Wik-Keyangan
+ TRUETYPE_TAG('w','i','g', 0 ), // wig = Wik-Ngathana
+ TRUETYPE_TAG('w','i','h', 0 ), // wih = Wik-Me'anha
+ TRUETYPE_TAG('w','i','i', 0 ), // wii = Minidien
+ TRUETYPE_TAG('w','i','j', 0 ), // wij = Wik-Iiyanh
+ TRUETYPE_TAG('w','i','k', 0 ), // wik = Wikalkan
+ TRUETYPE_TAG('w','i','l', 0 ), // wil = Wilawila
+ TRUETYPE_TAG('w','i','m', 0 ), // wim = Wik-Mungkan
+ TRUETYPE_TAG('w','i','n', 0 ), // win = Ho-Chunk
+ TRUETYPE_TAG('w','i','r', 0 ), // wir = Wiraféd
+ TRUETYPE_TAG('w','i','t', 0 ), // wit = Wintu
+ TRUETYPE_TAG('w','i','u', 0 ), // wiu = Wiru
+ TRUETYPE_TAG('w','i','v', 0 ), // wiv = Muduapa
+ TRUETYPE_TAG('w','i','w', 0 ), // wiw = Wirangu
+ TRUETYPE_TAG('w','i','y', 0 ), // wiy = Wiyot
+ TRUETYPE_TAG('w','j','a', 0 ), // wja = Waja
+ TRUETYPE_TAG('w','j','i', 0 ), // wji = Warji
+ TRUETYPE_TAG('w','k','a', 0 ), // wka = Kw'adza
+ TRUETYPE_TAG('w','k','b', 0 ), // wkb = Kumbaran
+ TRUETYPE_TAG('w','k','d', 0 ), // wkd = Wakde
+ TRUETYPE_TAG('w','k','l', 0 ), // wkl = Kalanadi
+ TRUETYPE_TAG('w','k','u', 0 ), // wku = Kunduvadi
+ TRUETYPE_TAG('w','k','w', 0 ), // wkw = Wakawaka
+ TRUETYPE_TAG('w','l','a', 0 ), // wla = Walio
+ TRUETYPE_TAG('w','l','c', 0 ), // wlc = Mwali Comorian
+ TRUETYPE_TAG('w','l','e', 0 ), // wle = Wolane
+ TRUETYPE_TAG('w','l','g', 0 ), // wlg = Kunbarlang
+ TRUETYPE_TAG('w','l','i', 0 ), // wli = Waioli
+ TRUETYPE_TAG('w','l','k', 0 ), // wlk = Wailaki
+ TRUETYPE_TAG('w','l','l', 0 ), // wll = Wali (Sudan)
+ TRUETYPE_TAG('w','l','m', 0 ), // wlm = Middle Welsh
+ TRUETYPE_TAG('w','l','o', 0 ), // wlo = Wolio
+ TRUETYPE_TAG('w','l','r', 0 ), // wlr = Wailapa
+ TRUETYPE_TAG('w','l','s', 0 ), // wls = Wallisian
+ TRUETYPE_TAG('w','l','u', 0 ), // wlu = Wuliwuli
+ TRUETYPE_TAG('w','l','v', 0 ), // wlv = Wichí Lhamtés Vejoz
+ TRUETYPE_TAG('w','l','w', 0 ), // wlw = Walak
+ TRUETYPE_TAG('w','l','x', 0 ), // wlx = Wali (Ghana)
+ TRUETYPE_TAG('w','l','y', 0 ), // wly = Waling
+ TRUETYPE_TAG('w','m','a', 0 ), // wma = Mawa (Nigeria)
+ TRUETYPE_TAG('w','m','b', 0 ), // wmb = Wambaya
+ TRUETYPE_TAG('w','m','c', 0 ), // wmc = Wamas
+ TRUETYPE_TAG('w','m','d', 0 ), // wmd = Mamaindé
+ TRUETYPE_TAG('w','m','e', 0 ), // wme = Wambule
+ TRUETYPE_TAG('w','m','h', 0 ), // wmh = Waima'a
+ TRUETYPE_TAG('w','m','i', 0 ), // wmi = Wamin
+ TRUETYPE_TAG('w','m','m', 0 ), // wmm = Maiwa (Indonesia)
+ TRUETYPE_TAG('w','m','n', 0 ), // wmn = Waamwang
+ TRUETYPE_TAG('w','m','o', 0 ), // wmo = Wom (Papua New Guinea)
+ TRUETYPE_TAG('w','m','s', 0 ), // wms = Wambon
+ TRUETYPE_TAG('w','m','t', 0 ), // wmt = Walmajarri
+ TRUETYPE_TAG('w','m','w', 0 ), // wmw = Mwani
+ TRUETYPE_TAG('w','m','x', 0 ), // wmx = Womo
+ TRUETYPE_TAG('w','n','b', 0 ), // wnb = Wanambre
+ TRUETYPE_TAG('w','n','c', 0 ), // wnc = Wantoat
+ TRUETYPE_TAG('w','n','d', 0 ), // wnd = Wandarang
+ TRUETYPE_TAG('w','n','e', 0 ), // wne = Waneci
+ TRUETYPE_TAG('w','n','g', 0 ), // wng = Wanggom
+ TRUETYPE_TAG('w','n','i', 0 ), // wni = Ndzwani Comorian
+ TRUETYPE_TAG('w','n','k', 0 ), // wnk = Wanukaka
+ TRUETYPE_TAG('w','n','m', 0 ), // wnm = Wanggamala
+ TRUETYPE_TAG('w','n','o', 0 ), // wno = Wano
+ TRUETYPE_TAG('w','n','p', 0 ), // wnp = Wanap
+ TRUETYPE_TAG('w','n','u', 0 ), // wnu = Usan
+ TRUETYPE_TAG('w','o','a', 0 ), // woa = Tyaraity
+ TRUETYPE_TAG('w','o','b', 0 ), // wob = Wè Northern
+ TRUETYPE_TAG('w','o','c', 0 ), // woc = Wogeo
+ TRUETYPE_TAG('w','o','d', 0 ), // wod = Wolani
+ TRUETYPE_TAG('w','o','e', 0 ), // woe = Woleaian
+ TRUETYPE_TAG('w','o','f', 0 ), // wof = Gambian Wolof
+ TRUETYPE_TAG('w','o','g', 0 ), // wog = Wogamusin
+ TRUETYPE_TAG('w','o','i', 0 ), // woi = Kamang
+ TRUETYPE_TAG('w','o','k', 0 ), // wok = Longto
+ TRUETYPE_TAG('w','o','m', 0 ), // wom = Wom (Nigeria)
+ TRUETYPE_TAG('w','o','n', 0 ), // won = Wongo
+ TRUETYPE_TAG('w','o','o', 0 ), // woo = Manombai
+ TRUETYPE_TAG('w','o','r', 0 ), // wor = Woria
+ TRUETYPE_TAG('w','o','s', 0 ), // wos = Hanga Hundi
+ TRUETYPE_TAG('w','o','w', 0 ), // wow = Wawonii
+ TRUETYPE_TAG('w','o','y', 0 ), // woy = Weyto
+ TRUETYPE_TAG('w','p','c', 0 ), // wpc = Maco
+ TRUETYPE_TAG('w','r','a', 0 ), // wra = Warapu
+ TRUETYPE_TAG('w','r','b', 0 ), // wrb = Warluwara
+ TRUETYPE_TAG('w','r','d', 0 ), // wrd = Warduji
+ TRUETYPE_TAG('w','r','g', 0 ), // wrg = Warungu
+ TRUETYPE_TAG('w','r','h', 0 ), // wrh = Wiradhuri
+ TRUETYPE_TAG('w','r','i', 0 ), // wri = Wariyangga
+ TRUETYPE_TAG('w','r','l', 0 ), // wrl = Warlmanpa
+ TRUETYPE_TAG('w','r','m', 0 ), // wrm = Warumungu
+ TRUETYPE_TAG('w','r','n', 0 ), // wrn = Warnang
+ TRUETYPE_TAG('w','r','p', 0 ), // wrp = Waropen
+ TRUETYPE_TAG('w','r','r', 0 ), // wrr = Wardaman
+ TRUETYPE_TAG('w','r','s', 0 ), // wrs = Waris
+ TRUETYPE_TAG('w','r','u', 0 ), // wru = Waru
+ TRUETYPE_TAG('w','r','v', 0 ), // wrv = Waruna
+ TRUETYPE_TAG('w','r','w', 0 ), // wrw = Gugu Warra
+ TRUETYPE_TAG('w','r','x', 0 ), // wrx = Wae Rana
+ TRUETYPE_TAG('w','r','y', 0 ), // wry = Merwari
+ TRUETYPE_TAG('w','r','z', 0 ), // wrz = Waray (Australia)
+ TRUETYPE_TAG('w','s','a', 0 ), // wsa = Warembori
+ TRUETYPE_TAG('w','s','i', 0 ), // wsi = Wusi
+ TRUETYPE_TAG('w','s','k', 0 ), // wsk = Waskia
+ TRUETYPE_TAG('w','s','r', 0 ), // wsr = Owenia
+ TRUETYPE_TAG('w','s','s', 0 ), // wss = Wasa
+ TRUETYPE_TAG('w','s','u', 0 ), // wsu = Wasu
+ TRUETYPE_TAG('w','s','v', 0 ), // wsv = Wotapuri-Katarqalai
+ TRUETYPE_TAG('w','t','f', 0 ), // wtf = Watiwa
+ TRUETYPE_TAG('w','t','i', 0 ), // wti = Berta
+ TRUETYPE_TAG('w','t','k', 0 ), // wtk = Watakataui
+ TRUETYPE_TAG('w','t','m', 0 ), // wtm = Mewati
+ TRUETYPE_TAG('w','t','w', 0 ), // wtw = Wotu
+ TRUETYPE_TAG('w','u','a', 0 ), // wua = Wikngenchera
+ TRUETYPE_TAG('w','u','b', 0 ), // wub = Wunambal
+ TRUETYPE_TAG('w','u','d', 0 ), // wud = Wudu
+ TRUETYPE_TAG('w','u','h', 0 ), // wuh = Wutunhua
+ TRUETYPE_TAG('w','u','l', 0 ), // wul = Silimo
+ TRUETYPE_TAG('w','u','m', 0 ), // wum = Wumbvu
+ TRUETYPE_TAG('w','u','n', 0 ), // wun = Bungu
+ TRUETYPE_TAG('w','u','r', 0 ), // wur = Wurrugu
+ TRUETYPE_TAG('w','u','t', 0 ), // wut = Wutung
+ TRUETYPE_TAG('w','u','u', 0 ), // wuu = Wu Chinese
+ TRUETYPE_TAG('w','u','v', 0 ), // wuv = Wuvulu-Aua
+ TRUETYPE_TAG('w','u','x', 0 ), // wux = Wulna
+ TRUETYPE_TAG('w','u','y', 0 ), // wuy = Wauyai
+ TRUETYPE_TAG('w','w','a', 0 ), // wwa = Waama
+ TRUETYPE_TAG('w','w','o', 0 ), // wwo = Wetamut
+ TRUETYPE_TAG('w','w','r', 0 ), // wwr = Warrwa
+ TRUETYPE_TAG('w','w','w', 0 ), // www = Wawa
+ TRUETYPE_TAG('w','x','a', 0 ), // wxa = Waxianghua
+ TRUETYPE_TAG('w','y','a', 0 ), // wya = Wyandot
+ TRUETYPE_TAG('w','y','b', 0 ), // wyb = Wangaaybuwan-Ngiyambaa
+ TRUETYPE_TAG('w','y','m', 0 ), // wym = Wymysorys
+ TRUETYPE_TAG('w','y','r', 0 ), // wyr = Wayoró
+ TRUETYPE_TAG('w','y','y', 0 ), // wyy = Western Fijian
+ TRUETYPE_TAG('x','a','a', 0 ), // xaa = Andalusian Arabic
+ TRUETYPE_TAG('x','a','b', 0 ), // xab = Sambe
+ TRUETYPE_TAG('x','a','c', 0 ), // xac = Kachari
+ TRUETYPE_TAG('x','a','d', 0 ), // xad = Adai
+ TRUETYPE_TAG('x','a','e', 0 ), // xae = Aequian
+ TRUETYPE_TAG('x','a','g', 0 ), // xag = Aghwan
+ TRUETYPE_TAG('x','a','i', 0 ), // xai = Kaimbé
+ TRUETYPE_TAG('x','a','l', 0 ), // xal = Kalmyk
+ TRUETYPE_TAG('x','a','m', 0 ), // xam = /Xam
+ TRUETYPE_TAG('x','a','n', 0 ), // xan = Xamtanga
+ TRUETYPE_TAG('x','a','o', 0 ), // xao = Khao
+ TRUETYPE_TAG('x','a','p', 0 ), // xap = Apalachee
+ TRUETYPE_TAG('x','a','q', 0 ), // xaq = Aquitanian
+ TRUETYPE_TAG('x','a','r', 0 ), // xar = Karami
+ TRUETYPE_TAG('x','a','s', 0 ), // xas = Kamas
+ TRUETYPE_TAG('x','a','t', 0 ), // xat = Katawixi
+ TRUETYPE_TAG('x','a','u', 0 ), // xau = Kauwera
+ TRUETYPE_TAG('x','a','v', 0 ), // xav = Xavánte
+ TRUETYPE_TAG('x','a','w', 0 ), // xaw = Kawaiisu
+ TRUETYPE_TAG('x','a','y', 0 ), // xay = Kayan Mahakam
+ TRUETYPE_TAG('x','b','a', 0 ), // xba = Kamba (Brazil)
+ TRUETYPE_TAG('x','b','b', 0 ), // xbb = Lower Burdekin
+ TRUETYPE_TAG('x','b','c', 0 ), // xbc = Bactrian
+ TRUETYPE_TAG('x','b','i', 0 ), // xbi = Kombio
+ TRUETYPE_TAG('x','b','m', 0 ), // xbm = Middle Breton
+ TRUETYPE_TAG('x','b','n', 0 ), // xbn = Kenaboi
+ TRUETYPE_TAG('x','b','o', 0 ), // xbo = Bolgarian
+ TRUETYPE_TAG('x','b','r', 0 ), // xbr = Kambera
+ TRUETYPE_TAG('x','b','w', 0 ), // xbw = Kambiwá
+ TRUETYPE_TAG('x','b','x', 0 ), // xbx = Kabixí
+ TRUETYPE_TAG('x','c','b', 0 ), // xcb = Cumbric
+ TRUETYPE_TAG('x','c','c', 0 ), // xcc = Camunic
+ TRUETYPE_TAG('x','c','e', 0 ), // xce = Celtiberian
+ TRUETYPE_TAG('x','c','g', 0 ), // xcg = Cisalpine Gaulish
+ TRUETYPE_TAG('x','c','h', 0 ), // xch = Chemakum
+ TRUETYPE_TAG('x','c','l', 0 ), // xcl = Classical Armenian
+ TRUETYPE_TAG('x','c','m', 0 ), // xcm = Comecrudo
+ TRUETYPE_TAG('x','c','n', 0 ), // xcn = Cotoname
+ TRUETYPE_TAG('x','c','o', 0 ), // xco = Chorasmian
+ TRUETYPE_TAG('x','c','r', 0 ), // xcr = Carian
+ TRUETYPE_TAG('x','c','t', 0 ), // xct = Classical Tibetan
+ TRUETYPE_TAG('x','c','u', 0 ), // xcu = Curonian
+ TRUETYPE_TAG('x','c','v', 0 ), // xcv = Chuvantsy
+ TRUETYPE_TAG('x','c','w', 0 ), // xcw = Coahuilteco
+ TRUETYPE_TAG('x','c','y', 0 ), // xcy = Cayuse
+ TRUETYPE_TAG('x','d','c', 0 ), // xdc = Dacian
+ TRUETYPE_TAG('x','d','m', 0 ), // xdm = Edomite
+ TRUETYPE_TAG('x','d','y', 0 ), // xdy = Malayic Dayak
+ TRUETYPE_TAG('x','e','b', 0 ), // xeb = Eblan
+ TRUETYPE_TAG('x','e','d', 0 ), // xed = Hdi
+ TRUETYPE_TAG('x','e','g', 0 ), // xeg = //Xegwi
+ TRUETYPE_TAG('x','e','l', 0 ), // xel = Kelo
+ TRUETYPE_TAG('x','e','m', 0 ), // xem = Kembayan
+ TRUETYPE_TAG('x','e','p', 0 ), // xep = Epi-Olmec
+ TRUETYPE_TAG('x','e','r', 0 ), // xer = Xerénte
+ TRUETYPE_TAG('x','e','s', 0 ), // xes = Kesawai
+ TRUETYPE_TAG('x','e','t', 0 ), // xet = Xetá
+ TRUETYPE_TAG('x','e','u', 0 ), // xeu = Keoru-Ahia
+ TRUETYPE_TAG('x','f','a', 0 ), // xfa = Faliscan
+ TRUETYPE_TAG('x','g','a', 0 ), // xga = Galatian
+ TRUETYPE_TAG('x','g','f', 0 ), // xgf = Gabrielino-Fernandeño
+ TRUETYPE_TAG('x','g','l', 0 ), // xgl = Galindan
+ TRUETYPE_TAG('x','g','n', 0 ), // xgn = Mongolian languages
+ TRUETYPE_TAG('x','g','r', 0 ), // xgr = Garza
+ TRUETYPE_TAG('x','h','a', 0 ), // xha = Harami
+ TRUETYPE_TAG('x','h','c', 0 ), // xhc = Hunnic
+ TRUETYPE_TAG('x','h','d', 0 ), // xhd = Hadrami
+ TRUETYPE_TAG('x','h','e', 0 ), // xhe = Khetrani
+ TRUETYPE_TAG('x','h','r', 0 ), // xhr = Hernican
+ TRUETYPE_TAG('x','h','t', 0 ), // xht = Hattic
+ TRUETYPE_TAG('x','h','u', 0 ), // xhu = Hurrian
+ TRUETYPE_TAG('x','h','v', 0 ), // xhv = Khua
+ TRUETYPE_TAG('x','i','a', 0 ), // xia = Xiandao
+ TRUETYPE_TAG('x','i','b', 0 ), // xib = Iberian
+ TRUETYPE_TAG('x','i','i', 0 ), // xii = Xiri
+ TRUETYPE_TAG('x','i','l', 0 ), // xil = Illyrian
+ TRUETYPE_TAG('x','i','n', 0 ), // xin = Xinca
+ TRUETYPE_TAG('x','i','p', 0 ), // xip = Xipináwa
+ TRUETYPE_TAG('x','i','r', 0 ), // xir = Xiriâna
+ TRUETYPE_TAG('x','i','v', 0 ), // xiv = Indus Valley Language
+ TRUETYPE_TAG('x','i','y', 0 ), // xiy = Xipaya
+ TRUETYPE_TAG('x','k','a', 0 ), // xka = Kalkoti
+ TRUETYPE_TAG('x','k','b', 0 ), // xkb = Northern Nago
+ TRUETYPE_TAG('x','k','c', 0 ), // xkc = Kho'ini
+ TRUETYPE_TAG('x','k','d', 0 ), // xkd = Mendalam Kayan
+ TRUETYPE_TAG('x','k','e', 0 ), // xke = Kereho
+ TRUETYPE_TAG('x','k','f', 0 ), // xkf = Khengkha
+ TRUETYPE_TAG('x','k','g', 0 ), // xkg = Kagoro
+ TRUETYPE_TAG('x','k','h', 0 ), // xkh = Karahawyana
+ TRUETYPE_TAG('x','k','i', 0 ), // xki = Kenyan Sign Language
+ TRUETYPE_TAG('x','k','j', 0 ), // xkj = Kajali
+ TRUETYPE_TAG('x','k','k', 0 ), // xkk = Kaco'
+ TRUETYPE_TAG('x','k','l', 0 ), // xkl = Mainstream Kenyah
+ TRUETYPE_TAG('x','k','n', 0 ), // xkn = Kayan River Kayan
+ TRUETYPE_TAG('x','k','o', 0 ), // xko = Kiorr
+ TRUETYPE_TAG('x','k','p', 0 ), // xkp = Kabatei
+ TRUETYPE_TAG('x','k','q', 0 ), // xkq = Koroni
+ TRUETYPE_TAG('x','k','r', 0 ), // xkr = Xakriabá
+ TRUETYPE_TAG('x','k','s', 0 ), // xks = Kumbewaha
+ TRUETYPE_TAG('x','k','t', 0 ), // xkt = Kantosi
+ TRUETYPE_TAG('x','k','u', 0 ), // xku = Kaamba
+ TRUETYPE_TAG('x','k','v', 0 ), // xkv = Kgalagadi
+ TRUETYPE_TAG('x','k','w', 0 ), // xkw = Kembra
+ TRUETYPE_TAG('x','k','x', 0 ), // xkx = Karore
+ TRUETYPE_TAG('x','k','y', 0 ), // xky = Uma' Lasan
+ TRUETYPE_TAG('x','k','z', 0 ), // xkz = Kurtokha
+ TRUETYPE_TAG('x','l','a', 0 ), // xla = Kamula
+ TRUETYPE_TAG('x','l','b', 0 ), // xlb = Loup B
+ TRUETYPE_TAG('x','l','c', 0 ), // xlc = Lycian
+ TRUETYPE_TAG('x','l','d', 0 ), // xld = Lydian
+ TRUETYPE_TAG('x','l','e', 0 ), // xle = Lemnian
+ TRUETYPE_TAG('x','l','g', 0 ), // xlg = Ligurian (Ancient)
+ TRUETYPE_TAG('x','l','i', 0 ), // xli = Liburnian
+ TRUETYPE_TAG('x','l','n', 0 ), // xln = Alanic
+ TRUETYPE_TAG('x','l','o', 0 ), // xlo = Loup A
+ TRUETYPE_TAG('x','l','p', 0 ), // xlp = Lepontic
+ TRUETYPE_TAG('x','l','s', 0 ), // xls = Lusitanian
+ TRUETYPE_TAG('x','l','u', 0 ), // xlu = Cuneiform Luwian
+ TRUETYPE_TAG('x','l','y', 0 ), // xly = Elymian
+ TRUETYPE_TAG('x','m','a', 0 ), // xma = Mushungulu
+ TRUETYPE_TAG('x','m','b', 0 ), // xmb = Mbonga
+ TRUETYPE_TAG('x','m','c', 0 ), // xmc = Makhuwa-Marrevone
+ TRUETYPE_TAG('x','m','d', 0 ), // xmd = Mbudum
+ TRUETYPE_TAG('x','m','e', 0 ), // xme = Median
+ TRUETYPE_TAG('x','m','f', 0 ), // xmf = Mingrelian
+ TRUETYPE_TAG('x','m','g', 0 ), // xmg = Mengaka
+ TRUETYPE_TAG('x','m','h', 0 ), // xmh = Kuku-Muminh
+ TRUETYPE_TAG('x','m','j', 0 ), // xmj = Majera
+ TRUETYPE_TAG('x','m','k', 0 ), // xmk = Ancient Macedonian
+ TRUETYPE_TAG('x','m','l', 0 ), // xml = Malaysian Sign Language
+ TRUETYPE_TAG('x','m','m', 0 ), // xmm = Manado Malay
+ TRUETYPE_TAG('x','m','n', 0 ), // xmn = Manichaean Middle Persian
+ TRUETYPE_TAG('x','m','o', 0 ), // xmo = Morerebi
+ TRUETYPE_TAG('x','m','p', 0 ), // xmp = Kuku-Mu'inh
+ TRUETYPE_TAG('x','m','q', 0 ), // xmq = Kuku-Mangk
+ TRUETYPE_TAG('x','m','r', 0 ), // xmr = Meroitic
+ TRUETYPE_TAG('x','m','s', 0 ), // xms = Moroccan Sign Language
+ TRUETYPE_TAG('x','m','t', 0 ), // xmt = Matbat
+ TRUETYPE_TAG('x','m','u', 0 ), // xmu = Kamu
+ TRUETYPE_TAG('x','m','v', 0 ), // xmv = Antankarana Malagasy
+ TRUETYPE_TAG('x','m','w', 0 ), // xmw = Tsimihety Malagasy
+ TRUETYPE_TAG('x','m','x', 0 ), // xmx = Maden
+ TRUETYPE_TAG('x','m','y', 0 ), // xmy = Mayaguduna
+ TRUETYPE_TAG('x','m','z', 0 ), // xmz = Mori Bawah
+ TRUETYPE_TAG('x','n','a', 0 ), // xna = Ancient North Arabian
+ TRUETYPE_TAG('x','n','b', 0 ), // xnb = Kanakanabu
+ TRUETYPE_TAG('x','n','d', 0 ), // xnd = Na-Dene languages
+ TRUETYPE_TAG('x','n','g', 0 ), // xng = Middle Mongolian
+ TRUETYPE_TAG('x','n','h', 0 ), // xnh = Kuanhua
+ TRUETYPE_TAG('x','n','n', 0 ), // xnn = Northern Kankanay
+ TRUETYPE_TAG('x','n','o', 0 ), // xno = Anglo-Norman
+ TRUETYPE_TAG('x','n','r', 0 ), // xnr = Kangri
+ TRUETYPE_TAG('x','n','s', 0 ), // xns = Kanashi
+ TRUETYPE_TAG('x','n','t', 0 ), // xnt = Narragansett
+ TRUETYPE_TAG('x','o','c', 0 ), // xoc = O'chi'chi'
+ TRUETYPE_TAG('x','o','d', 0 ), // xod = Kokoda
+ TRUETYPE_TAG('x','o','g', 0 ), // xog = Soga
+ TRUETYPE_TAG('x','o','i', 0 ), // xoi = Kominimung
+ TRUETYPE_TAG('x','o','k', 0 ), // xok = Xokleng
+ TRUETYPE_TAG('x','o','m', 0 ), // xom = Komo (Sudan)
+ TRUETYPE_TAG('x','o','n', 0 ), // xon = Konkomba
+ TRUETYPE_TAG('x','o','o', 0 ), // xoo = Xukurú
+ TRUETYPE_TAG('x','o','p', 0 ), // xop = Kopar
+ TRUETYPE_TAG('x','o','r', 0 ), // xor = Korubo
+ TRUETYPE_TAG('x','o','w', 0 ), // xow = Kowaki
+ TRUETYPE_TAG('x','p','c', 0 ), // xpc = Pecheneg
+ TRUETYPE_TAG('x','p','e', 0 ), // xpe = Liberia Kpelle
+ TRUETYPE_TAG('x','p','g', 0 ), // xpg = Phrygian
+ TRUETYPE_TAG('x','p','i', 0 ), // xpi = Pictish
+ TRUETYPE_TAG('x','p','k', 0 ), // xpk = Kulina Pano
+ TRUETYPE_TAG('x','p','m', 0 ), // xpm = Pumpokol
+ TRUETYPE_TAG('x','p','n', 0 ), // xpn = Kapinawá
+ TRUETYPE_TAG('x','p','o', 0 ), // xpo = Pochutec
+ TRUETYPE_TAG('x','p','p', 0 ), // xpp = Puyo-Paekche
+ TRUETYPE_TAG('x','p','q', 0 ), // xpq = Mohegan-Pequot
+ TRUETYPE_TAG('x','p','r', 0 ), // xpr = Parthian
+ TRUETYPE_TAG('x','p','s', 0 ), // xps = Pisidian
+ TRUETYPE_TAG('x','p','u', 0 ), // xpu = Punic
+ TRUETYPE_TAG('x','p','y', 0 ), // xpy = Puyo
+ TRUETYPE_TAG('x','q','a', 0 ), // xqa = Karakhanid
+ TRUETYPE_TAG('x','q','t', 0 ), // xqt = Qatabanian
+ TRUETYPE_TAG('x','r','a', 0 ), // xra = Krahô
+ TRUETYPE_TAG('x','r','b', 0 ), // xrb = Eastern Karaboro
+ TRUETYPE_TAG('x','r','e', 0 ), // xre = Kreye
+ TRUETYPE_TAG('x','r','i', 0 ), // xri = Krikati-Timbira
+ TRUETYPE_TAG('x','r','m', 0 ), // xrm = Armazic
+ TRUETYPE_TAG('x','r','n', 0 ), // xrn = Arin
+ TRUETYPE_TAG('x','r','r', 0 ), // xrr = Raetic
+ TRUETYPE_TAG('x','r','t', 0 ), // xrt = Aranama-Tamique
+ TRUETYPE_TAG('x','r','u', 0 ), // xru = Marriammu
+ TRUETYPE_TAG('x','r','w', 0 ), // xrw = Karawa
+ TRUETYPE_TAG('x','s','a', 0 ), // xsa = Sabaean
+ TRUETYPE_TAG('x','s','b', 0 ), // xsb = Tinà Sambal
+ TRUETYPE_TAG('x','s','c', 0 ), // xsc = Scythian
+ TRUETYPE_TAG('x','s','d', 0 ), // xsd = Sidetic
+ TRUETYPE_TAG('x','s','e', 0 ), // xse = Sempan
+ TRUETYPE_TAG('x','s','h', 0 ), // xsh = Shamang
+ TRUETYPE_TAG('x','s','i', 0 ), // xsi = Sio
+ TRUETYPE_TAG('x','s','j', 0 ), // xsj = Subi
+ TRUETYPE_TAG('x','s','l', 0 ), // xsl = South Slavey
+ TRUETYPE_TAG('x','s','m', 0 ), // xsm = Kasem
+ TRUETYPE_TAG('x','s','n', 0 ), // xsn = Sanga (Nigeria)
+ TRUETYPE_TAG('x','s','o', 0 ), // xso = Solano
+ TRUETYPE_TAG('x','s','p', 0 ), // xsp = Silopi
+ TRUETYPE_TAG('x','s','q', 0 ), // xsq = Makhuwa-Saka
+ TRUETYPE_TAG('x','s','r', 0 ), // xsr = Sherpa
+ TRUETYPE_TAG('x','s','s', 0 ), // xss = Assan
+ TRUETYPE_TAG('x','s','u', 0 ), // xsu = Sanumá
+ TRUETYPE_TAG('x','s','v', 0 ), // xsv = Sudovian
+ TRUETYPE_TAG('x','s','y', 0 ), // xsy = Saisiyat
+ TRUETYPE_TAG('x','t','a', 0 ), // xta = Alcozauca Mixtec
+ TRUETYPE_TAG('x','t','b', 0 ), // xtb = Chazumba Mixtec
+ TRUETYPE_TAG('x','t','c', 0 ), // xtc = Katcha-Kadugli-Miri
+ TRUETYPE_TAG('x','t','d', 0 ), // xtd = Diuxi-Tilantongo Mixtec
+ TRUETYPE_TAG('x','t','e', 0 ), // xte = Ketengban
+ TRUETYPE_TAG('x','t','g', 0 ), // xtg = Transalpine Gaulish
+ TRUETYPE_TAG('x','t','i', 0 ), // xti = Sinicahua Mixtec
+ TRUETYPE_TAG('x','t','j', 0 ), // xtj = San Juan Teita Mixtec
+ TRUETYPE_TAG('x','t','l', 0 ), // xtl = Tijaltepec Mixtec
+ TRUETYPE_TAG('x','t','m', 0 ), // xtm = Magdalena Peñasco Mixtec
+ TRUETYPE_TAG('x','t','n', 0 ), // xtn = Northern Tlaxiaco Mixtec
+ TRUETYPE_TAG('x','t','o', 0 ), // xto = Tokharian A
+ TRUETYPE_TAG('x','t','p', 0 ), // xtp = San Miguel Piedras Mixtec
+ TRUETYPE_TAG('x','t','q', 0 ), // xtq = Tumshuqese
+ TRUETYPE_TAG('x','t','r', 0 ), // xtr = Early Tripuri
+ TRUETYPE_TAG('x','t','s', 0 ), // xts = Sindihui Mixtec
+ TRUETYPE_TAG('x','t','t', 0 ), // xtt = Tacahua Mixtec
+ TRUETYPE_TAG('x','t','u', 0 ), // xtu = Cuyamecalco Mixtec
+ TRUETYPE_TAG('x','t','w', 0 ), // xtw = Tawandê
+ TRUETYPE_TAG('x','t','y', 0 ), // xty = Yoloxochitl Mixtec
+ TRUETYPE_TAG('x','t','z', 0 ), // xtz = Tasmanian
+ TRUETYPE_TAG('x','u','a', 0 ), // xua = Alu Kurumba
+ TRUETYPE_TAG('x','u','b', 0 ), // xub = Betta Kurumba
+ TRUETYPE_TAG('x','u','g', 0 ), // xug = Kunigami
+ TRUETYPE_TAG('x','u','j', 0 ), // xuj = Jennu Kurumba
+ TRUETYPE_TAG('x','u','m', 0 ), // xum = Umbrian
+ TRUETYPE_TAG('x','u','o', 0 ), // xuo = Kuo
+ TRUETYPE_TAG('x','u','p', 0 ), // xup = Upper Umpqua
+ TRUETYPE_TAG('x','u','r', 0 ), // xur = Urartian
+ TRUETYPE_TAG('x','u','t', 0 ), // xut = Kuthant
+ TRUETYPE_TAG('x','u','u', 0 ), // xuu = Kxoe
+ TRUETYPE_TAG('x','v','e', 0 ), // xve = Venetic
+ TRUETYPE_TAG('x','v','i', 0 ), // xvi = Kamviri
+ TRUETYPE_TAG('x','v','n', 0 ), // xvn = Vandalic
+ TRUETYPE_TAG('x','v','o', 0 ), // xvo = Volscian
+ TRUETYPE_TAG('x','v','s', 0 ), // xvs = Vestinian
+ TRUETYPE_TAG('x','w','a', 0 ), // xwa = Kwaza
+ TRUETYPE_TAG('x','w','c', 0 ), // xwc = Woccon
+ TRUETYPE_TAG('x','w','e', 0 ), // xwe = Xwela Gbe
+ TRUETYPE_TAG('x','w','g', 0 ), // xwg = Kwegu
+ TRUETYPE_TAG('x','w','l', 0 ), // xwl = Western Xwla Gbe
+ TRUETYPE_TAG('x','w','o', 0 ), // xwo = Written Oirat
+ TRUETYPE_TAG('x','w','r', 0 ), // xwr = Kwerba Mamberamo
+ TRUETYPE_TAG('x','x','b', 0 ), // xxb = Boro (Ghana)
+ TRUETYPE_TAG('x','x','k', 0 ), // xxk = Ke'o
+ TRUETYPE_TAG('x','x','r', 0 ), // xxr = Koropó
+ TRUETYPE_TAG('x','x','t', 0 ), // xxt = Tambora
+ TRUETYPE_TAG('x','y','l', 0 ), // xyl = Yalakalore
+ TRUETYPE_TAG('x','z','h', 0 ), // xzh = Zhang-Zhung
+ TRUETYPE_TAG('x','z','m', 0 ), // xzm = Zemgalian
+ TRUETYPE_TAG('x','z','p', 0 ), // xzp = Ancient Zapotec
+ TRUETYPE_TAG('y','a','a', 0 ), // yaa = Yaminahua
+ TRUETYPE_TAG('y','a','b', 0 ), // yab = Yuhup
+ TRUETYPE_TAG('y','a','c', 0 ), // yac = Pass Valley Yali
+ TRUETYPE_TAG('y','a','d', 0 ), // yad = Yagua
+ TRUETYPE_TAG('y','a','e', 0 ), // yae = Pumé
+ TRUETYPE_TAG('y','a','f', 0 ), // yaf = Yaka (Democratic Republic of Congo)
+ TRUETYPE_TAG('y','a','g', 0 ), // yag = Yámana
+ TRUETYPE_TAG('y','a','h', 0 ), // yah = Yazgulyam
+ TRUETYPE_TAG('y','a','i', 0 ), // yai = Yagnobi
+ TRUETYPE_TAG('y','a','j', 0 ), // yaj = Banda-Yangere
+ TRUETYPE_TAG('y','a','k', 0 ), // yak = Yakama
+ TRUETYPE_TAG('y','a','l', 0 ), // yal = Yalunka
+ TRUETYPE_TAG('y','a','m', 0 ), // yam = Yamba
+ TRUETYPE_TAG('y','a','n', 0 ), // yan = Mayangna
+ TRUETYPE_TAG('y','a','o', 0 ), // yao = Yao
+ TRUETYPE_TAG('y','a','p', 0 ), // yap = Yapese
+ TRUETYPE_TAG('y','a','q', 0 ), // yaq = Yaqui
+ TRUETYPE_TAG('y','a','r', 0 ), // yar = Yabarana
+ TRUETYPE_TAG('y','a','s', 0 ), // yas = Nugunu (Cameroon)
+ TRUETYPE_TAG('y','a','t', 0 ), // yat = Yambeta
+ TRUETYPE_TAG('y','a','u', 0 ), // yau = Yuwana
+ TRUETYPE_TAG('y','a','v', 0 ), // yav = Yangben
+ TRUETYPE_TAG('y','a','w', 0 ), // yaw = Yawalapití
+ TRUETYPE_TAG('y','a','x', 0 ), // yax = Yauma
+ TRUETYPE_TAG('y','a','y', 0 ), // yay = Agwagwune
+ TRUETYPE_TAG('y','a','z', 0 ), // yaz = Lokaa
+ TRUETYPE_TAG('y','b','a', 0 ), // yba = Yala
+ TRUETYPE_TAG('y','b','b', 0 ), // ybb = Yemba
+ TRUETYPE_TAG('y','b','d', 0 ), // ybd = Yangbye
+ TRUETYPE_TAG('y','b','e', 0 ), // ybe = West Yugur
+ TRUETYPE_TAG('y','b','h', 0 ), // ybh = Yakha
+ TRUETYPE_TAG('y','b','i', 0 ), // ybi = Yamphu
+ TRUETYPE_TAG('y','b','j', 0 ), // ybj = Hasha
+ TRUETYPE_TAG('y','b','k', 0 ), // ybk = Bokha
+ TRUETYPE_TAG('y','b','l', 0 ), // ybl = Yukuben
+ TRUETYPE_TAG('y','b','m', 0 ), // ybm = Yaben
+ TRUETYPE_TAG('y','b','n', 0 ), // ybn = Yabaâna
+ TRUETYPE_TAG('y','b','o', 0 ), // ybo = Yabong
+ TRUETYPE_TAG('y','b','x', 0 ), // ybx = Yawiyo
+ TRUETYPE_TAG('y','b','y', 0 ), // yby = Yaweyuha
+ TRUETYPE_TAG('y','c','h', 0 ), // ych = Chesu
+ TRUETYPE_TAG('y','c','l', 0 ), // ycl = Lolopo
+ TRUETYPE_TAG('y','c','n', 0 ), // ycn = Yucuna
+ TRUETYPE_TAG('y','c','p', 0 ), // ycp = Chepya
+ TRUETYPE_TAG('y','d','d', 0 ), // ydd = Eastern Yiddish
+ TRUETYPE_TAG('y','d','e', 0 ), // yde = Yangum Dey
+ TRUETYPE_TAG('y','d','g', 0 ), // ydg = Yidgha
+ TRUETYPE_TAG('y','d','k', 0 ), // ydk = Yoidik
+ TRUETYPE_TAG('y','d','s', 0 ), // yds = Yiddish Sign Language
+ TRUETYPE_TAG('y','e','a', 0 ), // yea = Ravula
+ TRUETYPE_TAG('y','e','c', 0 ), // yec = Yeniche
+ TRUETYPE_TAG('y','e','e', 0 ), // yee = Yimas
+ TRUETYPE_TAG('y','e','i', 0 ), // yei = Yeni
+ TRUETYPE_TAG('y','e','j', 0 ), // yej = Yevanic
+ TRUETYPE_TAG('y','e','l', 0 ), // yel = Yela
+ TRUETYPE_TAG('y','e','n', 0 ), // yen = Yendang
+ TRUETYPE_TAG('y','e','r', 0 ), // yer = Tarok
+ TRUETYPE_TAG('y','e','s', 0 ), // yes = Yeskwa
+ TRUETYPE_TAG('y','e','t', 0 ), // yet = Yetfa
+ TRUETYPE_TAG('y','e','u', 0 ), // yeu = Yerukula
+ TRUETYPE_TAG('y','e','v', 0 ), // yev = Yapunda
+ TRUETYPE_TAG('y','e','y', 0 ), // yey = Yeyi
+ TRUETYPE_TAG('y','g','l', 0 ), // ygl = Yangum Gel
+ TRUETYPE_TAG('y','g','m', 0 ), // ygm = Yagomi
+ TRUETYPE_TAG('y','g','p', 0 ), // ygp = Gepo
+ TRUETYPE_TAG('y','g','r', 0 ), // ygr = Yagaria
+ TRUETYPE_TAG('y','g','w', 0 ), // ygw = Yagwoia
+ TRUETYPE_TAG('y','h','a', 0 ), // yha = Baha Buyang
+ TRUETYPE_TAG('y','h','d', 0 ), // yhd = Judeo-Iraqi Arabic
+ TRUETYPE_TAG('y','h','l', 0 ), // yhl = Hlepho Phowa
+ TRUETYPE_TAG('y','i','a', 0 ), // yia = Yinggarda
+ TRUETYPE_TAG('y','i','f', 0 ), // yif = Ache
+ TRUETYPE_TAG('y','i','g', 0 ), // yig = Wusa Nasu
+ TRUETYPE_TAG('y','i','h', 0 ), // yih = Western Yiddish
+ TRUETYPE_TAG('y','i','i', 0 ), // yii = Yidiny
+ TRUETYPE_TAG('y','i','j', 0 ), // yij = Yindjibarndi
+ TRUETYPE_TAG('y','i','k', 0 ), // yik = Dongshanba Lalo
+ TRUETYPE_TAG('y','i','l', 0 ), // yil = Yindjilandji
+ TRUETYPE_TAG('y','i','m', 0 ), // yim = Yimchungru Naga
+ TRUETYPE_TAG('y','i','n', 0 ), // yin = Yinchia
+ TRUETYPE_TAG('y','i','p', 0 ), // yip = Pholo
+ TRUETYPE_TAG('y','i','q', 0 ), // yiq = Miqie
+ TRUETYPE_TAG('y','i','r', 0 ), // yir = North Awyu
+ TRUETYPE_TAG('y','i','s', 0 ), // yis = Yis
+ TRUETYPE_TAG('y','i','t', 0 ), // yit = Eastern Lalu
+ TRUETYPE_TAG('y','i','u', 0 ), // yiu = Awu
+ TRUETYPE_TAG('y','i','v', 0 ), // yiv = Northern Nisu
+ TRUETYPE_TAG('y','i','x', 0 ), // yix = Axi Yi
+ TRUETYPE_TAG('y','i','y', 0 ), // yiy = Yir Yoront
+ TRUETYPE_TAG('y','i','z', 0 ), // yiz = Azhe
+ TRUETYPE_TAG('y','k','a', 0 ), // yka = Yakan
+ TRUETYPE_TAG('y','k','g', 0 ), // ykg = Northern Yukaghir
+ TRUETYPE_TAG('y','k','i', 0 ), // yki = Yoke
+ TRUETYPE_TAG('y','k','k', 0 ), // ykk = Yakaikeke
+ TRUETYPE_TAG('y','k','l', 0 ), // ykl = Khlula
+ TRUETYPE_TAG('y','k','m', 0 ), // ykm = Kap
+ TRUETYPE_TAG('y','k','o', 0 ), // yko = Yasa
+ TRUETYPE_TAG('y','k','r', 0 ), // ykr = Yekora
+ TRUETYPE_TAG('y','k','t', 0 ), // ykt = Kathu
+ TRUETYPE_TAG('y','k','y', 0 ), // yky = Yakoma
+ TRUETYPE_TAG('y','l','a', 0 ), // yla = Yaul
+ TRUETYPE_TAG('y','l','b', 0 ), // ylb = Yaleba
+ TRUETYPE_TAG('y','l','e', 0 ), // yle = Yele
+ TRUETYPE_TAG('y','l','g', 0 ), // ylg = Yelogu
+ TRUETYPE_TAG('y','l','i', 0 ), // yli = Angguruk Yali
+ TRUETYPE_TAG('y','l','l', 0 ), // yll = Yil
+ TRUETYPE_TAG('y','l','m', 0 ), // ylm = Limi
+ TRUETYPE_TAG('y','l','n', 0 ), // yln = Langnian Buyang
+ TRUETYPE_TAG('y','l','o', 0 ), // ylo = Naluo Yi
+ TRUETYPE_TAG('y','l','r', 0 ), // ylr = Yalarnnga
+ TRUETYPE_TAG('y','l','u', 0 ), // ylu = Aribwaung
+ TRUETYPE_TAG('y','l','y', 0 ), // yly = Nyâlayu
+ TRUETYPE_TAG('y','m','a', 0 ), // yma = Yamphe
+ TRUETYPE_TAG('y','m','b', 0 ), // ymb = Yambes
+ TRUETYPE_TAG('y','m','c', 0 ), // ymc = Southern Muji
+ TRUETYPE_TAG('y','m','d', 0 ), // ymd = Muda
+ TRUETYPE_TAG('y','m','e', 0 ), // yme = Yameo
+ TRUETYPE_TAG('y','m','g', 0 ), // ymg = Yamongeri
+ TRUETYPE_TAG('y','m','h', 0 ), // ymh = Mili
+ TRUETYPE_TAG('y','m','i', 0 ), // ymi = Moji
+ TRUETYPE_TAG('y','m','k', 0 ), // ymk = Makwe
+ TRUETYPE_TAG('y','m','l', 0 ), // yml = Iamalele
+ TRUETYPE_TAG('y','m','m', 0 ), // ymm = Maay
+ TRUETYPE_TAG('y','m','n', 0 ), // ymn = Yamna
+ TRUETYPE_TAG('y','m','o', 0 ), // ymo = Yangum Mon
+ TRUETYPE_TAG('y','m','p', 0 ), // ymp = Yamap
+ TRUETYPE_TAG('y','m','q', 0 ), // ymq = Qila Muji
+ TRUETYPE_TAG('y','m','r', 0 ), // ymr = Malasar
+ TRUETYPE_TAG('y','m','s', 0 ), // yms = Mysian
+ TRUETYPE_TAG('y','m','t', 0 ), // ymt = Mator-Taygi-Karagas
+ TRUETYPE_TAG('y','m','x', 0 ), // ymx = Northern Muji
+ TRUETYPE_TAG('y','m','z', 0 ), // ymz = Muzi
+ TRUETYPE_TAG('y','n','a', 0 ), // yna = Aluo
+ TRUETYPE_TAG('y','n','d', 0 ), // ynd = Yandruwandha
+ TRUETYPE_TAG('y','n','e', 0 ), // yne = Lang'e
+ TRUETYPE_TAG('y','n','g', 0 ), // yng = Yango
+ TRUETYPE_TAG('y','n','h', 0 ), // ynh = Yangho
+ TRUETYPE_TAG('y','n','k', 0 ), // ynk = Naukan Yupik
+ TRUETYPE_TAG('y','n','l', 0 ), // ynl = Yangulam
+ TRUETYPE_TAG('y','n','n', 0 ), // ynn = Yana
+ TRUETYPE_TAG('y','n','o', 0 ), // yno = Yong
+ TRUETYPE_TAG('y','n','s', 0 ), // yns = Yansi
+ TRUETYPE_TAG('y','n','u', 0 ), // ynu = Yahuna
+ TRUETYPE_TAG('y','o','b', 0 ), // yob = Yoba
+ TRUETYPE_TAG('y','o','g', 0 ), // yog = Yogad
+ TRUETYPE_TAG('y','o','i', 0 ), // yoi = Yonaguni
+ TRUETYPE_TAG('y','o','k', 0 ), // yok = Yokuts
+ TRUETYPE_TAG('y','o','l', 0 ), // yol = Yola
+ TRUETYPE_TAG('y','o','m', 0 ), // yom = Yombe
+ TRUETYPE_TAG('y','o','n', 0 ), // yon = Yongkom
+ TRUETYPE_TAG('y','o','s', 0 ), // yos = Yos
+ TRUETYPE_TAG('y','o','x', 0 ), // yox = Yoron
+ TRUETYPE_TAG('y','o','y', 0 ), // yoy = Yoy
+ TRUETYPE_TAG('y','p','a', 0 ), // ypa = Phala
+ TRUETYPE_TAG('y','p','b', 0 ), // ypb = Labo Phowa
+ TRUETYPE_TAG('y','p','g', 0 ), // ypg = Phola
+ TRUETYPE_TAG('y','p','h', 0 ), // yph = Phupha
+ TRUETYPE_TAG('y','p','k', 0 ), // ypk = Yupik languages
+ TRUETYPE_TAG('y','p','m', 0 ), // ypm = Phuma
+ TRUETYPE_TAG('y','p','n', 0 ), // ypn = Ani Phowa
+ TRUETYPE_TAG('y','p','o', 0 ), // ypo = Alo Phola
+ TRUETYPE_TAG('y','p','p', 0 ), // ypp = Phupa
+ TRUETYPE_TAG('y','p','z', 0 ), // ypz = Phuza
+ TRUETYPE_TAG('y','r','a', 0 ), // yra = Yerakai
+ TRUETYPE_TAG('y','r','b', 0 ), // yrb = Yareba
+ TRUETYPE_TAG('y','r','e', 0 ), // yre = Yaouré
+ TRUETYPE_TAG('y','r','i', 0 ), // yri = Yarí
+ TRUETYPE_TAG('y','r','k', 0 ), // yrk = Nenets
+ TRUETYPE_TAG('y','r','l', 0 ), // yrl = Nhengatu
+ TRUETYPE_TAG('y','r','n', 0 ), // yrn = Yerong
+ TRUETYPE_TAG('y','r','s', 0 ), // yrs = Yarsun
+ TRUETYPE_TAG('y','r','w', 0 ), // yrw = Yarawata
+ TRUETYPE_TAG('y','s','c', 0 ), // ysc = Yassic
+ TRUETYPE_TAG('y','s','d', 0 ), // ysd = Samatao
+ TRUETYPE_TAG('y','s','l', 0 ), // ysl = Yugoslavian Sign Language
+ TRUETYPE_TAG('y','s','n', 0 ), // ysn = Sani
+ TRUETYPE_TAG('y','s','o', 0 ), // yso = Nisi (China)
+ TRUETYPE_TAG('y','s','p', 0 ), // ysp = Southern Lolopo
+ TRUETYPE_TAG('y','s','r', 0 ), // ysr = Sirenik Yupik
+ TRUETYPE_TAG('y','s','s', 0 ), // yss = Yessan-Mayo
+ TRUETYPE_TAG('y','s','y', 0 ), // ysy = Sanie
+ TRUETYPE_TAG('y','t','a', 0 ), // yta = Talu
+ TRUETYPE_TAG('y','t','l', 0 ), // ytl = Tanglang
+ TRUETYPE_TAG('y','t','p', 0 ), // ytp = Thopho
+ TRUETYPE_TAG('y','t','w', 0 ), // ytw = Yout Wam
+ TRUETYPE_TAG('y','u','a', 0 ), // yua = Yucateco
+ TRUETYPE_TAG('y','u','b', 0 ), // yub = Yugambal
+ TRUETYPE_TAG('y','u','c', 0 ), // yuc = Yuchi
+ TRUETYPE_TAG('y','u','d', 0 ), // yud = Judeo-Tripolitanian Arabic
+ TRUETYPE_TAG('y','u','e', 0 ), // yue = Yue Chinese
+ TRUETYPE_TAG('y','u','f', 0 ), // yuf = Havasupai-Walapai-Yavapai
+ TRUETYPE_TAG('y','u','g', 0 ), // yug = Yug
+ TRUETYPE_TAG('y','u','i', 0 ), // yui = Yurutí
+ TRUETYPE_TAG('y','u','j', 0 ), // yuj = Karkar-Yuri
+ TRUETYPE_TAG('y','u','k', 0 ), // yuk = Yuki
+ TRUETYPE_TAG('y','u','l', 0 ), // yul = Yulu
+ TRUETYPE_TAG('y','u','m', 0 ), // yum = Quechan
+ TRUETYPE_TAG('y','u','n', 0 ), // yun = Bena (Nigeria)
+ TRUETYPE_TAG('y','u','p', 0 ), // yup = Yukpa
+ TRUETYPE_TAG('y','u','q', 0 ), // yuq = Yuqui
+ TRUETYPE_TAG('y','u','r', 0 ), // yur = Yurok
+ TRUETYPE_TAG('y','u','t', 0 ), // yut = Yopno
+ TRUETYPE_TAG('y','u','u', 0 ), // yuu = Yugh
+ TRUETYPE_TAG('y','u','w', 0 ), // yuw = Yau (Morobe Province)
+ TRUETYPE_TAG('y','u','x', 0 ), // yux = Southern Yukaghir
+ TRUETYPE_TAG('y','u','y', 0 ), // yuy = East Yugur
+ TRUETYPE_TAG('y','u','z', 0 ), // yuz = Yuracare
+ TRUETYPE_TAG('y','v','a', 0 ), // yva = Yawa
+ TRUETYPE_TAG('y','v','t', 0 ), // yvt = Yavitero
+ TRUETYPE_TAG('y','w','a', 0 ), // ywa = Kalou
+ TRUETYPE_TAG('y','w','l', 0 ), // ywl = Western Lalu
+ TRUETYPE_TAG('y','w','n', 0 ), // ywn = Yawanawa
+ TRUETYPE_TAG('y','w','q', 0 ), // ywq = Wuding-Luquan Yi
+ TRUETYPE_TAG('y','w','r', 0 ), // ywr = Yawuru
+ TRUETYPE_TAG('y','w','t', 0 ), // ywt = Xishanba Lalo
+ TRUETYPE_TAG('y','w','u', 0 ), // ywu = Wumeng Nasu
+ TRUETYPE_TAG('y','w','w', 0 ), // yww = Yawarawarga
+ TRUETYPE_TAG('y','y','u', 0 ), // yyu = Yau (Sandaun Province)
+ TRUETYPE_TAG('y','y','z', 0 ), // yyz = Ayizi
+ TRUETYPE_TAG('y','z','g', 0 ), // yzg = E'ma Buyang
+ TRUETYPE_TAG('y','z','k', 0 ), // yzk = Zokhuo
+ TRUETYPE_TAG('z','a','a', 0 ), // zaa = Sierra de Juárez Zapotec
+ TRUETYPE_TAG('z','a','b', 0 ), // zab = San Juan Guelavía Zapotec
+ TRUETYPE_TAG('z','a','c', 0 ), // zac = Ocotlán Zapotec
+ TRUETYPE_TAG('z','a','d', 0 ), // zad = Cajonos Zapotec
+ TRUETYPE_TAG('z','a','e', 0 ), // zae = Yareni Zapotec
+ TRUETYPE_TAG('z','a','f', 0 ), // zaf = Ayoquesco Zapotec
+ TRUETYPE_TAG('z','a','g', 0 ), // zag = Zaghawa
+ TRUETYPE_TAG('z','a','h', 0 ), // zah = Zangwal
+ TRUETYPE_TAG('z','a','i', 0 ), // zai = Isthmus Zapotec
+ TRUETYPE_TAG('z','a','j', 0 ), // zaj = Zaramo
+ TRUETYPE_TAG('z','a','k', 0 ), // zak = Zanaki
+ TRUETYPE_TAG('z','a','l', 0 ), // zal = Zauzou
+ TRUETYPE_TAG('z','a','m', 0 ), // zam = Miahuatlán Zapotec
+ TRUETYPE_TAG('z','a','o', 0 ), // zao = Ozolotepec Zapotec
+ TRUETYPE_TAG('z','a','p', 0 ), // zap = Zapotec
+ TRUETYPE_TAG('z','a','q', 0 ), // zaq = Aloápam Zapotec
+ TRUETYPE_TAG('z','a','r', 0 ), // zar = Rincón Zapotec
+ TRUETYPE_TAG('z','a','s', 0 ), // zas = Santo Domingo Albarradas Zapotec
+ TRUETYPE_TAG('z','a','t', 0 ), // zat = Tabaa Zapotec
+ TRUETYPE_TAG('z','a','u', 0 ), // zau = Zangskari
+ TRUETYPE_TAG('z','a','v', 0 ), // zav = Yatzachi Zapotec
+ TRUETYPE_TAG('z','a','w', 0 ), // zaw = Mitla Zapotec
+ TRUETYPE_TAG('z','a','x', 0 ), // zax = Xadani Zapotec
+ TRUETYPE_TAG('z','a','y', 0 ), // zay = Zayse-Zergulla
+ TRUETYPE_TAG('z','a','z', 0 ), // zaz = Zari
+ TRUETYPE_TAG('z','b','c', 0 ), // zbc = Central Berawan
+ TRUETYPE_TAG('z','b','e', 0 ), // zbe = East Berawan
+ TRUETYPE_TAG('z','b','l', 0 ), // zbl = Blissymbols
+ TRUETYPE_TAG('z','b','t', 0 ), // zbt = Batui
+ TRUETYPE_TAG('z','b','w', 0 ), // zbw = West Berawan
+ TRUETYPE_TAG('z','c','a', 0 ), // zca = Coatecas Altas Zapotec
+ TRUETYPE_TAG('z','c','h', 0 ), // zch = Central Hongshuihe Zhuang
+ TRUETYPE_TAG('z','d','j', 0 ), // zdj = Ngazidja Comorian
+ TRUETYPE_TAG('z','e','a', 0 ), // zea = Zeeuws
+ TRUETYPE_TAG('z','e','g', 0 ), // zeg = Zenag
+ TRUETYPE_TAG('z','e','h', 0 ), // zeh = Eastern Hongshuihe Zhuang
+ TRUETYPE_TAG('z','e','n', 0 ), // zen = Zenaga
+ TRUETYPE_TAG('z','g','a', 0 ), // zga = Kinga
+ TRUETYPE_TAG('z','g','b', 0 ), // zgb = Guibei Zhuang
+ TRUETYPE_TAG('z','g','m', 0 ), // zgm = Minz Zhuang
+ TRUETYPE_TAG('z','g','n', 0 ), // zgn = Guibian Zhuang
+ TRUETYPE_TAG('z','g','r', 0 ), // zgr = Magori
+ TRUETYPE_TAG('z','h','b', 0 ), // zhb = Zhaba
+ TRUETYPE_TAG('z','h','d', 0 ), // zhd = Dai Zhuang
+ TRUETYPE_TAG('z','h','i', 0 ), // zhi = Zhire
+ TRUETYPE_TAG('z','h','n', 0 ), // zhn = Nong Zhuang
+ TRUETYPE_TAG('z','h','w', 0 ), // zhw = Zhoa
+ TRUETYPE_TAG('z','h','x', 0 ), // zhx = Chinese (family)
+ TRUETYPE_TAG('z','i','a', 0 ), // zia = Zia
+ TRUETYPE_TAG('z','i','b', 0 ), // zib = Zimbabwe Sign Language
+ TRUETYPE_TAG('z','i','k', 0 ), // zik = Zimakani
+ TRUETYPE_TAG('z','i','l', 0 ), // zil = Zialo
+ TRUETYPE_TAG('z','i','m', 0 ), // zim = Mesme
+ TRUETYPE_TAG('z','i','n', 0 ), // zin = Zinza
+ TRUETYPE_TAG('z','i','r', 0 ), // zir = Ziriya
+ TRUETYPE_TAG('z','i','w', 0 ), // ziw = Zigula
+ TRUETYPE_TAG('z','i','z', 0 ), // ziz = Zizilivakan
+ TRUETYPE_TAG('z','k','a', 0 ), // zka = Kaimbulawa
+ TRUETYPE_TAG('z','k','b', 0 ), // zkb = Koibal
+ TRUETYPE_TAG('z','k','g', 0 ), // zkg = Koguryo
+ TRUETYPE_TAG('z','k','h', 0 ), // zkh = Khorezmian
+ TRUETYPE_TAG('z','k','k', 0 ), // zkk = Karankawa
+ TRUETYPE_TAG('z','k','o', 0 ), // zko = Kott
+ TRUETYPE_TAG('z','k','p', 0 ), // zkp = São Paulo Kaingáng
+ TRUETYPE_TAG('z','k','r', 0 ), // zkr = Zakhring
+ TRUETYPE_TAG('z','k','t', 0 ), // zkt = Kitan
+ TRUETYPE_TAG('z','k','u', 0 ), // zku = Kaurna
+ TRUETYPE_TAG('z','k','v', 0 ), // zkv = Krevinian
+ TRUETYPE_TAG('z','k','z', 0 ), // zkz = Khazar
+ TRUETYPE_TAG('z','l','e', 0 ), // zle = East Slavic languages
+ TRUETYPE_TAG('z','l','j', 0 ), // zlj = Liujiang Zhuang
+ TRUETYPE_TAG('z','l','m', 0 ), // zlm = Malay (individual language)
+ TRUETYPE_TAG('z','l','n', 0 ), // zln = Lianshan Zhuang
+ TRUETYPE_TAG('z','l','q', 0 ), // zlq = Liuqian Zhuang
+ TRUETYPE_TAG('z','l','s', 0 ), // zls = South Slavic languages
+ TRUETYPE_TAG('z','l','w', 0 ), // zlw = West Slavic languages
+ TRUETYPE_TAG('z','m','a', 0 ), // zma = Manda (Australia)
+ TRUETYPE_TAG('z','m','b', 0 ), // zmb = Zimba
+ TRUETYPE_TAG('z','m','c', 0 ), // zmc = Margany
+ TRUETYPE_TAG('z','m','d', 0 ), // zmd = Maridan
+ TRUETYPE_TAG('z','m','e', 0 ), // zme = Mangerr
+ TRUETYPE_TAG('z','m','f', 0 ), // zmf = Mfinu
+ TRUETYPE_TAG('z','m','g', 0 ), // zmg = Marti Ke
+ TRUETYPE_TAG('z','m','h', 0 ), // zmh = Makolkol
+ TRUETYPE_TAG('z','m','i', 0 ), // zmi = Negeri Sembilan Malay
+ TRUETYPE_TAG('z','m','j', 0 ), // zmj = Maridjabin
+ TRUETYPE_TAG('z','m','k', 0 ), // zmk = Mandandanyi
+ TRUETYPE_TAG('z','m','l', 0 ), // zml = Madngele
+ TRUETYPE_TAG('z','m','m', 0 ), // zmm = Marimanindji
+ TRUETYPE_TAG('z','m','n', 0 ), // zmn = Mbangwe
+ TRUETYPE_TAG('z','m','o', 0 ), // zmo = Molo
+ TRUETYPE_TAG('z','m','p', 0 ), // zmp = Mpuono
+ TRUETYPE_TAG('z','m','q', 0 ), // zmq = Mituku
+ TRUETYPE_TAG('z','m','r', 0 ), // zmr = Maranunggu
+ TRUETYPE_TAG('z','m','s', 0 ), // zms = Mbesa
+ TRUETYPE_TAG('z','m','t', 0 ), // zmt = Maringarr
+ TRUETYPE_TAG('z','m','u', 0 ), // zmu = Muruwari
+ TRUETYPE_TAG('z','m','v', 0 ), // zmv = Mbariman-Gudhinma
+ TRUETYPE_TAG('z','m','w', 0 ), // zmw = Mbo (Democratic Republic of Congo)
+ TRUETYPE_TAG('z','m','x', 0 ), // zmx = Bomitaba
+ TRUETYPE_TAG('z','m','y', 0 ), // zmy = Mariyedi
+ TRUETYPE_TAG('z','m','z', 0 ), // zmz = Mbandja
+ TRUETYPE_TAG('z','n','a', 0 ), // zna = Zan Gula
+ TRUETYPE_TAG('z','n','d', 0 ), // znd = Zande languages
+ TRUETYPE_TAG('z','n','e', 0 ), // zne = Zande (individual language)
+ TRUETYPE_TAG('z','n','g', 0 ), // zng = Mang
+ TRUETYPE_TAG('z','n','k', 0 ), // znk = Manangkari
+ TRUETYPE_TAG('z','n','s', 0 ), // zns = Mangas
+ TRUETYPE_TAG('z','o','c', 0 ), // zoc = Copainalá Zoque
+ TRUETYPE_TAG('z','o','h', 0 ), // zoh = Chimalapa Zoque
+ TRUETYPE_TAG('z','o','m', 0 ), // zom = Zou
+ TRUETYPE_TAG('z','o','o', 0 ), // zoo = Asunción Mixtepec Zapotec
+ TRUETYPE_TAG('z','o','q', 0 ), // zoq = Tabasco Zoque
+ TRUETYPE_TAG('z','o','r', 0 ), // zor = Rayón Zoque
+ TRUETYPE_TAG('z','o','s', 0 ), // zos = Francisco León Zoque
+ TRUETYPE_TAG('z','p','a', 0 ), // zpa = Lachiguiri Zapotec
+ TRUETYPE_TAG('z','p','b', 0 ), // zpb = Yautepec Zapotec
+ TRUETYPE_TAG('z','p','c', 0 ), // zpc = Choapan Zapotec
+ TRUETYPE_TAG('z','p','d', 0 ), // zpd = Southeastern Ixtlán Zapotec
+ TRUETYPE_TAG('z','p','e', 0 ), // zpe = Petapa Zapotec
+ TRUETYPE_TAG('z','p','f', 0 ), // zpf = San Pedro Quiatoni Zapotec
+ TRUETYPE_TAG('z','p','g', 0 ), // zpg = Guevea De Humboldt Zapotec
+ TRUETYPE_TAG('z','p','h', 0 ), // zph = Totomachapan Zapotec
+ TRUETYPE_TAG('z','p','i', 0 ), // zpi = Santa María Quiegolani Zapotec
+ TRUETYPE_TAG('z','p','j', 0 ), // zpj = Quiavicuzas Zapotec
+ TRUETYPE_TAG('z','p','k', 0 ), // zpk = Tlacolulita Zapotec
+ TRUETYPE_TAG('z','p','l', 0 ), // zpl = Lachixío Zapotec
+ TRUETYPE_TAG('z','p','m', 0 ), // zpm = Mixtepec Zapotec
+ TRUETYPE_TAG('z','p','n', 0 ), // zpn = Santa Inés Yatzechi Zapotec
+ TRUETYPE_TAG('z','p','o', 0 ), // zpo = Amatlán Zapotec
+ TRUETYPE_TAG('z','p','p', 0 ), // zpp = El Alto Zapotec
+ TRUETYPE_TAG('z','p','q', 0 ), // zpq = Zoogocho Zapotec
+ TRUETYPE_TAG('z','p','r', 0 ), // zpr = Santiago Xanica Zapotec
+ TRUETYPE_TAG('z','p','s', 0 ), // zps = Coatlán Zapotec
+ TRUETYPE_TAG('z','p','t', 0 ), // zpt = San Vicente Coatlán Zapotec
+ TRUETYPE_TAG('z','p','u', 0 ), // zpu = Yalálag Zapotec
+ TRUETYPE_TAG('z','p','v', 0 ), // zpv = Chichicapan Zapotec
+ TRUETYPE_TAG('z','p','w', 0 ), // zpw = Zaniza Zapotec
+ TRUETYPE_TAG('z','p','x', 0 ), // zpx = San Baltazar Loxicha Zapotec
+ TRUETYPE_TAG('z','p','y', 0 ), // zpy = Mazaltepec Zapotec
+ TRUETYPE_TAG('z','p','z', 0 ), // zpz = Texmelucan Zapotec
+ TRUETYPE_TAG('z','q','e', 0 ), // zqe = Qiubei Zhuang
+ TRUETYPE_TAG('z','r','a', 0 ), // zra = Kara (Korea)
+ TRUETYPE_TAG('z','r','g', 0 ), // zrg = Mirgan
+ TRUETYPE_TAG('z','r','n', 0 ), // zrn = Zerenkel
+ TRUETYPE_TAG('z','r','o', 0 ), // zro = Záparo
+ TRUETYPE_TAG('z','r','p', 0 ), // zrp = Zarphatic
+ TRUETYPE_TAG('z','r','s', 0 ), // zrs = Mairasi
+ TRUETYPE_TAG('z','s','a', 0 ), // zsa = Sarasira
+ TRUETYPE_TAG('z','s','k', 0 ), // zsk = Kaskean
+ TRUETYPE_TAG('z','s','l', 0 ), // zsl = Zambian Sign Language
+ TRUETYPE_TAG('z','s','m', 0 ), // zsm = Standard Malay
+ TRUETYPE_TAG('z','s','r', 0 ), // zsr = Southern Rincon Zapotec
+ TRUETYPE_TAG('z','s','u', 0 ), // zsu = Sukurum
+ TRUETYPE_TAG('z','t','e', 0 ), // zte = Elotepec Zapotec
+ TRUETYPE_TAG('z','t','g', 0 ), // ztg = Xanaguía Zapotec
+ TRUETYPE_TAG('z','t','l', 0 ), // ztl = Lapaguía-Guivini Zapotec
+ TRUETYPE_TAG('z','t','m', 0 ), // ztm = San Agustín Mixtepec Zapotec
+ TRUETYPE_TAG('z','t','n', 0 ), // ztn = Santa Catarina Albarradas Zapotec
+ TRUETYPE_TAG('z','t','p', 0 ), // ztp = Loxicha Zapotec
+ TRUETYPE_TAG('z','t','q', 0 ), // ztq = Quioquitani-Quierí Zapotec
+ TRUETYPE_TAG('z','t','s', 0 ), // zts = Tilquiapan Zapotec
+ TRUETYPE_TAG('z','t','t', 0 ), // ztt = Tejalapan Zapotec
+ TRUETYPE_TAG('z','t','u', 0 ), // ztu = Güilá Zapotec
+ TRUETYPE_TAG('z','t','x', 0 ), // ztx = Zaachila Zapotec
+ TRUETYPE_TAG('z','t','y', 0 ), // zty = Yatee Zapotec
+ TRUETYPE_TAG('z','u','a', 0 ), // zua = Zeem
+ TRUETYPE_TAG('z','u','h', 0 ), // zuh = Tokano
+ TRUETYPE_TAG('z','u','m', 0 ), // zum = Kumzari
+ TRUETYPE_TAG('z','u','n', 0 ), // zun = Zuni
+ TRUETYPE_TAG('z','u','y', 0 ), // zuy = Zumaya
+ TRUETYPE_TAG('z','w','a', 0 ), // zwa = Zay
+ TRUETYPE_TAG('z','x','x', 0 ), // zxx = No linguistic content
+ TRUETYPE_TAG('z','y','b', 0 ), // zyb = Yongbei Zhuang
+ TRUETYPE_TAG('z','y','g', 0 ), // zyg = Yang Zhuang
+ TRUETYPE_TAG('z','y','j', 0 ), // zyj = Youjiang Zhuang
+ TRUETYPE_TAG('z','y','n', 0 ), // zyn = Yongnan Zhuang
+ TRUETYPE_TAG('z','y','p', 0 ), // zyp = Zyphe
+ TRUETYPE_TAG('z','z','a', 0 ), // zza = Zaza
+ TRUETYPE_TAG('z','z','j', 0 ), // zzj = Zuojiang Zhuang
+ 0x0 // end of language code list
+};
+
+/*
+ * * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
+ */
diff --git a/system/graphics/thebes/gfxLineSegment.h b/system/graphics/thebes/gfxLineSegment.h
new file mode 100644
index 000000000..a217fb309
--- /dev/null
+++ b/system/graphics/thebes/gfxLineSegment.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LINESEGMENT_H
+#define GFX_LINESEGMENT_H
+
+#include "gfxTypes.h"
+#include "gfxPoint.h"
+
+struct gfxLineSegment {
+ gfxLineSegment(const gfxPoint &aStart, const gfxPoint &aEnd)
+ : mStart(aStart)
+ , mEnd(aEnd)
+ {}
+
+ bool PointsOnSameSide(const gfxPoint& aOne, const gfxPoint& aTwo)
+ {
+ // Solve the equation y - mStart.y - ((mEnd.y - mStart.y)/(mEnd.x - mStart.x))(x - mStart.x) for both points
+
+ gfxFloat deltaY = (mEnd.y - mStart.y);
+ gfxFloat deltaX = (mEnd.x - mStart.x);
+
+ gfxFloat one = deltaX * (aOne.y - mStart.y) - deltaY * (aOne.x - mStart.x);
+ gfxFloat two = deltaX * (aTwo.y - mStart.y) - deltaY * (aTwo.x - mStart.x);
+
+ // If both results have the same sign, then we're on the correct side of the line.
+ // 0 (on the line) is always considered in.
+
+ if ((one >= 0 && two >= 0) || (one <= 0 && two <= 0))
+ return true;
+ return false;
+ }
+
+ /**
+ * Determines if two line segments intersect, and returns the intersection
+ * point in aIntersection if they do.
+ *
+ * Coincident lines are considered not intersecting as they don't have an
+ * intersection point.
+ */
+ bool Intersects(const gfxLineSegment& aOther, gfxPoint& aIntersection)
+ {
+ gfxFloat denominator = (aOther.mEnd.y - aOther.mStart.y) * (mEnd.x - mStart.x ) -
+ (aOther.mEnd.x - aOther.mStart.x ) * (mEnd.y - mStart.y);
+
+ // Parallel or coincident. We treat coincident as not intersecting since
+ // these lines are guaranteed to have corners that intersect instead.
+ if (!denominator) {
+ return false;
+ }
+
+ gfxFloat anumerator = (aOther.mEnd.x - aOther.mStart.x) * (mStart.y - aOther.mStart.y) -
+ (aOther.mEnd.y - aOther.mStart.y) * (mStart.x - aOther.mStart.x);
+
+ gfxFloat bnumerator = (mEnd.x - mStart.x) * (mStart.y - aOther.mStart.y) -
+ (mEnd.y - mStart.y) * (mStart.x - aOther.mStart.x);
+
+ gfxFloat ua = anumerator / denominator;
+ gfxFloat ub = bnumerator / denominator;
+
+ if (ua <= 0.0 || ua >= 1.0 ||
+ ub <= 0.0 || ub >= 1.0) {
+ //Intersection is outside of the segment
+ return false;
+ }
+
+ aIntersection = mStart + (mEnd - mStart) * ua;
+ return true;
+ }
+
+ gfxPoint mStart;
+ gfxPoint mEnd;
+};
+
+#endif /* GFX_LINESEGMENT_H */
diff --git a/system/graphics/thebes/gfxMathTable.cpp b/system/graphics/thebes/gfxMathTable.cpp
new file mode 100644
index 000000000..d9f4462ff
--- /dev/null
+++ b/system/graphics/thebes/gfxMathTable.cpp
@@ -0,0 +1,211 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxMathTable.h"
+
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-ot.h"
+
+#define FixedToFloat(f) ((f) * (1.0 / 65536.0))
+#define FloatToFixed(f) (65536 * (f))
+
+using namespace mozilla;
+
+gfxMathTable::gfxMathTable(hb_face_t *aFace, gfxFloat aSize)
+{
+ mHBFont = hb_font_create(aFace);
+ if (mHBFont) {
+ hb_font_set_ppem(mHBFont, aSize, aSize);
+ uint32_t scale = FloatToFixed(aSize);
+ hb_font_set_scale(mHBFont, scale, scale);
+ }
+
+ mMathVariantCache.glyphID = 0;
+ ClearCache();
+}
+
+gfxMathTable::~gfxMathTable()
+{
+ if (mHBFont) {
+ hb_font_destroy(mHBFont);
+ }
+}
+
+gfxFloat
+gfxMathTable::Constant(MathConstant aConstant) const
+{
+ int32_t value = hb_ot_math_get_constant(mHBFont, static_cast<hb_ot_math_constant_t>(aConstant));
+ if (aConstant == ScriptPercentScaleDown ||
+ aConstant == ScriptScriptPercentScaleDown ||
+ aConstant == RadicalDegreeBottomRaisePercent) {
+ return value / 100.0;
+ }
+ return FixedToFloat(value);
+}
+
+gfxFloat
+gfxMathTable::ItalicsCorrection(uint32_t aGlyphID) const
+{
+ return FixedToFloat(hb_ot_math_get_glyph_italics_correction(mHBFont, aGlyphID));
+}
+
+uint32_t
+gfxMathTable::VariantsSize(uint32_t aGlyphID, bool aVertical,
+ uint16_t aSize) const
+{
+ UpdateMathVariantCache(aGlyphID, aVertical);
+ if (aSize < kMaxCachedSizeCount) {
+ return mMathVariantCache.sizes[aSize];
+ }
+
+ // If the size index exceeds the cache size, we just read the value with
+ // hb_ot_math_get_glyph_variants.
+ hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
+ hb_ot_math_glyph_variant_t variant;
+ unsigned int count = 1;
+ hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, aSize, &count,
+ &variant);
+ return count > 0 ? variant.glyph : 0;
+}
+
+bool
+gfxMathTable::VariantsParts(uint32_t aGlyphID, bool aVertical,
+ uint32_t aGlyphs[4]) const
+{
+ UpdateMathVariantCache(aGlyphID, aVertical);
+ memcpy(aGlyphs, mMathVariantCache.parts, sizeof(mMathVariantCache.parts));
+ return mMathVariantCache.arePartsValid;
+}
+
+void
+gfxMathTable::ClearCache() const
+{
+ memset(mMathVariantCache.sizes, 0, sizeof(mMathVariantCache.sizes));
+ memset(mMathVariantCache.parts, 0, sizeof(mMathVariantCache.parts));
+ mMathVariantCache.arePartsValid = false;
+}
+
+void
+gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID, bool aVertical) const
+{
+ if (aGlyphID == mMathVariantCache.glyphID &&
+ aVertical == mMathVariantCache.vertical)
+ return;
+
+ mMathVariantCache.glyphID = aGlyphID;
+ mMathVariantCache.vertical = aVertical;
+ ClearCache();
+
+ // Cache the first size variants.
+ hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
+ hb_ot_math_glyph_variant_t variant[kMaxCachedSizeCount];
+ unsigned int count = kMaxCachedSizeCount;
+ hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, 0, &count,
+ variant);
+ for (unsigned int i = 0; i < count; i++) {
+ mMathVariantCache.sizes[i] = variant[i].glyph;
+ }
+
+ // Try and cache the parts of the glyph assembly.
+ // XXXfredw The structure of the Open Type Math table is a bit more general
+ // than the one currently used by the nsMathMLChar code, so we try to fallback
+ // in reasonable way. We use the approach of the copyComponents function in
+ // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
+ //
+ // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
+ // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
+ // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
+ // stored from bottom to top in the Open Type MATH table while they are
+ // stored from top to bottom in nsMathMLChar.
+
+ hb_ot_math_glyph_part_t parts[5];
+ count = MOZ_ARRAY_LENGTH(parts);
+ unsigned int offset = 0;
+ if (hb_ot_math_get_glyph_assembly(mHBFont, aGlyphID, direction, offset, &count, parts, NULL) > MOZ_ARRAY_LENGTH(parts))
+ return; // Not supported: Too many pieces.
+ if (count <= 0)
+ return; // Not supported: No pieces.
+
+ // Count the number of non extender pieces
+ uint16_t nonExtenderCount = 0;
+ for (uint16_t i = 0; i < count; i++) {
+ if (!(parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER)) {
+ nonExtenderCount++;
+ }
+ }
+ if (nonExtenderCount > 3) {
+ // Not supported: too many pieces
+ return;
+ }
+
+ // Now browse the list of pieces
+
+ // 0 = look for a left/bottom glyph
+ // 1 = look for an extender between left/bottom and mid
+ // 2 = look for a middle glyph
+ // 3 = look for an extender between middle and right/top
+ // 4 = look for a right/top glyph
+ // 5 = no more piece expected
+ uint8_t state = 0;
+
+ // First extender char found.
+ uint32_t extenderChar = 0;
+
+ for (uint16_t i = 0; i < count; i++) {
+
+ bool isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER;
+ uint32_t glyph = parts[i].glyph;
+
+ if ((state == 1 || state == 2) && nonExtenderCount < 3) {
+ // do not try to find a middle glyph
+ state += 2;
+ }
+
+ if (isExtender) {
+ if (!extenderChar) {
+ extenderChar = glyph;
+ mMathVariantCache.parts[3] = extenderChar;
+ } else if (extenderChar != glyph) {
+ // Not supported: different extenders
+ return;
+ }
+
+ if (state == 0) { // or state == 1
+ // ignore left/bottom piece and multiple successive extenders
+ state = 1;
+ } else if (state == 2) { // or state == 3
+ // ignore middle piece and multiple successive extenders
+ state = 3;
+ } else if (state >= 4) {
+ // Not supported: unexpected extender
+ return;
+ }
+
+ continue;
+ }
+
+ if (state == 0) {
+ // copy left/bottom part
+ mMathVariantCache.parts[aVertical ? 2 : 0] = glyph;
+ state = 1;
+ continue;
+ }
+
+ if (state == 1 || state == 2) {
+ // copy middle part
+ mMathVariantCache.parts[1] = glyph;
+ state = 3;
+ continue;
+ }
+
+ if (state == 3 || state == 4) {
+ // copy right/top part
+ mMathVariantCache.parts[aVertical ? 0 : 2] = glyph;
+ state = 5;
+ }
+
+ }
+
+ mMathVariantCache.arePartsValid = true;
+}
diff --git a/system/graphics/thebes/gfxMathTable.h b/system/graphics/thebes/gfxMathTable.h
new file mode 100644
index 000000000..bb22ff8df
--- /dev/null
+++ b/system/graphics/thebes/gfxMathTable.h
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MATH_TABLE_H
+#define GFX_MATH_TABLE_H
+
+#include "gfxFont.h"
+
+/**
+ * Used by |gfxFont| to represent the MATH table of an OpenType font.
+ * Each |gfxFont| owns at most one |gfxMathTable| instance.
+ */
+class gfxMathTable
+{
+public:
+ /**
+ * @param aFace The HarfBuzz face containing the math table.
+ * @param aSize The font size to pass to HarfBuzz.
+ */
+ gfxMathTable(hb_face_t *aFace, gfxFloat aSize);
+
+ /**
+ * Releases our reference to the MATH table and cleans up everything else.
+ */
+ ~gfxMathTable();
+
+ enum MathConstant {
+ // The order of the constants must match the order of the fields
+ // defined in the MATH table.
+ 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
+ };
+
+ /**
+ * Returns the value of the specified constant from the MATH table.
+ */
+ gfxFloat Constant(MathConstant aConstant) const;
+
+ /**
+ * Returns the value of the specified constant in app units.
+ */
+ nscoord Constant(MathConstant aConstant,
+ uint32_t aAppUnitsPerDevPixel) const
+ {
+ return NSToCoordRound(Constant(aConstant) * aAppUnitsPerDevPixel);
+ }
+
+ /**
+ * If the MATH table contains an italic correction for that glyph, this
+ * function returns the corresponding value. Otherwise it returns 0.
+ */
+ gfxFloat
+ ItalicsCorrection(uint32_t aGlyphID) const;
+
+ /**
+ * @param aGlyphID glyph index of the character we want to stretch
+ * @param aVertical direction of the stretching (vertical/horizontal)
+ * @param aSize the desired size variant
+ *
+ * Returns the glyph index of the desired size variant or 0 if there is not
+ * any such size variant.
+ */
+ uint32_t VariantsSize(uint32_t aGlyphID, bool aVertical,
+ uint16_t aSize) const;
+
+ /**
+ * @param aGlyphID glyph index of the character we want to stretch
+ * @param aVertical direction of the stretching (vertical/horizontal)
+ * @param aGlyphs pre-allocated buffer of 4 elements where the glyph
+ * indexes (or 0 for absent parts) will be stored. The parts are stored in
+ * the order expected by the nsMathMLChar: Top (or Left), Middle, Bottom
+ * (or Right), Glue.
+ *
+ * Tries to fill-in aGlyphs with the relevant glyph indexes and returns
+ * whether the operation was successful. The function returns false if
+ * there is not any assembly for the character we want to stretch or if
+ * the format is not supported by the nsMathMLChar code.
+ *
+ */
+ bool VariantsParts(uint32_t aGlyphID, bool aVertical,
+ uint32_t aGlyphs[4]) const;
+
+private:
+ // size-specific font object, owned by the gfxMathTable
+ hb_font_t *mHBFont;
+
+ static const unsigned int kMaxCachedSizeCount = 10;
+ struct MathVariantCacheEntry {
+ uint32_t glyphID;
+ bool vertical;
+ uint32_t sizes[kMaxCachedSizeCount];
+ uint32_t parts[4];
+ bool arePartsValid;
+ };
+ mutable MathVariantCacheEntry mMathVariantCache;
+ void ClearCache() const;
+ void UpdateMathVariantCache(uint32_t aGlyphID, bool aVertical) const;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxMatrix.cpp b/system/graphics/thebes/gfxMatrix.cpp
new file mode 100644
index 000000000..25a4d5a6f
--- /dev/null
+++ b/system/graphics/thebes/gfxMatrix.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxMatrix.h"
+#include "cairo.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+
+using namespace mozilla::gfx;
+
+#define CAIRO_MATRIX(x) reinterpret_cast<cairo_matrix_t*>((x))
+#define CONST_CAIRO_MATRIX(x) reinterpret_cast<const cairo_matrix_t*>((x))
+
+const gfxMatrix&
+gfxMatrix::Reset()
+{
+ cairo_matrix_init_identity(CAIRO_MATRIX(this));
+ return *this;
+}
+
+bool
+gfxMatrix::Invert()
+{
+ return cairo_matrix_invert(CAIRO_MATRIX(this)) == CAIRO_STATUS_SUCCESS;
+}
+
+gfxMatrix&
+gfxMatrix::Scale(gfxFloat x, gfxFloat y)
+{
+ cairo_matrix_scale(CAIRO_MATRIX(this), x, y);
+ return *this;
+}
+
+gfxMatrix&
+gfxMatrix::Translate(const gfxPoint& pt)
+{
+ cairo_matrix_translate(CAIRO_MATRIX(this), pt.x, pt.y);
+ return *this;
+}
+
+gfxMatrix&
+gfxMatrix::Rotate(gfxFloat radians)
+{
+ cairo_matrix_rotate(CAIRO_MATRIX(this), radians);
+ return *this;
+}
+
+const gfxMatrix&
+gfxMatrix::operator *= (const gfxMatrix& m)
+{
+ cairo_matrix_multiply(CAIRO_MATRIX(this), CAIRO_MATRIX(this), CONST_CAIRO_MATRIX(&m));
+ return *this;
+}
+
+gfxMatrix&
+gfxMatrix::PreMultiply(const gfxMatrix& m)
+{
+ cairo_matrix_multiply(CAIRO_MATRIX(this), CONST_CAIRO_MATRIX(&m), CAIRO_MATRIX(this));
+ return *this;
+}
+
+/* static */ gfxMatrix
+gfxMatrix::Rotation(gfxFloat aAngle)
+{
+ gfxMatrix newMatrix;
+
+ gfxFloat s = sin(aAngle);
+ gfxFloat c = cos(aAngle);
+
+ newMatrix._11 = c;
+ newMatrix._12 = s;
+ newMatrix._21 = -s;
+ newMatrix._22 = c;
+
+ return newMatrix;
+}
+
+gfxPoint
+gfxMatrix::Transform(const gfxPoint& point) const
+{
+ gfxPoint ret = point;
+ cairo_matrix_transform_point(CONST_CAIRO_MATRIX(this), &ret.x, &ret.y);
+ return ret;
+}
+
+gfxSize
+gfxMatrix::Transform(const gfxSize& size) const
+{
+ gfxSize ret = size;
+ cairo_matrix_transform_distance(CONST_CAIRO_MATRIX(this), &ret.width, &ret.height);
+ return ret;
+}
+
+gfxRect
+gfxMatrix::Transform(const gfxRect& rect) const
+{
+ return gfxRect(Transform(rect.TopLeft()), Transform(rect.Size()));
+}
+
+gfxRect
+gfxMatrix::TransformBounds(const gfxRect& rect) const
+{
+ /* Code taken from cairo-matrix.c, _cairo_matrix_transform_bounding_box isn't public */
+ int i;
+ double quad_x[4], quad_y[4];
+ double min_x, max_x;
+ double min_y, max_y;
+
+ quad_x[0] = rect.X();
+ quad_y[0] = rect.Y();
+ cairo_matrix_transform_point (CONST_CAIRO_MATRIX(this), &quad_x[0], &quad_y[0]);
+
+ quad_x[1] = rect.XMost();
+ quad_y[1] = rect.Y();
+ cairo_matrix_transform_point (CONST_CAIRO_MATRIX(this), &quad_x[1], &quad_y[1]);
+
+ quad_x[2] = rect.X();
+ quad_y[2] = rect.YMost();
+ cairo_matrix_transform_point (CONST_CAIRO_MATRIX(this), &quad_x[2], &quad_y[2]);
+
+ quad_x[3] = rect.XMost();
+ quad_y[3] = rect.YMost();
+ cairo_matrix_transform_point (CONST_CAIRO_MATRIX(this), &quad_x[3], &quad_y[3]);
+
+ min_x = max_x = quad_x[0];
+ min_y = max_y = quad_y[0];
+
+ for (i = 1; i < 4; i++) {
+ if (quad_x[i] < min_x)
+ min_x = quad_x[i];
+ if (quad_x[i] > max_x)
+ max_x = quad_x[i];
+
+ if (quad_y[i] < min_y)
+ min_y = quad_y[i];
+ if (quad_y[i] > max_y)
+ max_y = quad_y[i];
+ }
+
+ return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+
+static void NudgeToInteger(double *aVal)
+{
+ float f = float(*aVal);
+ mozilla::gfx::NudgeToInteger(&f);
+ *aVal = f;
+}
+
+gfxMatrix&
+gfxMatrix::NudgeToIntegers(void)
+{
+ NudgeToInteger(&_11);
+ NudgeToInteger(&_21);
+ NudgeToInteger(&_12);
+ NudgeToInteger(&_22);
+ NudgeToInteger(&_31);
+ NudgeToInteger(&_32);
+ return *this;
+}
+
+mozilla::gfx::Matrix4x4
+gfxMatrix::operator *(const mozilla::gfx::Matrix4x4& aMatrix) const
+{
+ Matrix4x4 resultMatrix;
+
+ resultMatrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
+ resultMatrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
+ resultMatrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
+ resultMatrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
+
+ resultMatrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
+ resultMatrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
+ resultMatrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
+ resultMatrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
+
+ resultMatrix._31 = aMatrix._31;
+ resultMatrix._32 = aMatrix._32;
+ resultMatrix._33 = aMatrix._33;
+ resultMatrix._34 = aMatrix._34;
+
+ resultMatrix._41 = _31 * aMatrix._11 + _32 * aMatrix._21 + aMatrix._41;
+ resultMatrix._42 = _31 * aMatrix._12 + _32 * aMatrix._22 + aMatrix._42;
+ resultMatrix._43 = _31 * aMatrix._13 + _32 * aMatrix._23 + aMatrix._43;
+ resultMatrix._44 = _31 * aMatrix._14 + _32 * aMatrix._24 + aMatrix._44;
+
+ return resultMatrix;
+}
diff --git a/system/graphics/thebes/gfxMatrix.h b/system/graphics/thebes/gfxMatrix.h
new file mode 100644
index 000000000..4f92262cc
--- /dev/null
+++ b/system/graphics/thebes/gfxMatrix.h
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MATRIX_H
+#define GFX_MATRIX_H
+
+#include "gfxPoint.h"
+#include "gfxTypes.h"
+#include "gfxRect.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/gfx/MatrixFwd.h"
+
+// XX - I don't think this class should use gfxFloat at all,
+// but should use 'double' and be called gfxDoubleMatrix;
+// we can then typedef that to gfxMatrix where we typedef
+// double to be gfxFloat.
+
+/**
+ * A matrix that represents an affine transformation. Projective
+ * transformations are not supported. This matrix looks like:
+ *
+ * / a b 0 \
+ * | c d 0 |
+ * \ tx ty 1 /
+ *
+ * So, transforming a point (x, y) results in:
+ *
+ * / a b 0 \ / a * x + c * y + tx \ T
+ * (x y 1) * | c d 0 | = | b * x + d * y + ty |
+ * \ tx ty 1 / \ 1 /
+ *
+ */
+class gfxMatrix {
+public:
+ double _11; double _12;
+ double _21; double _22;
+ double _31; double _32;
+
+ /**
+ * Initializes this matrix as the identity matrix.
+ */
+ gfxMatrix() { Reset(); }
+
+ /**
+ * Initializes the matrix from individual components. See the class
+ * description for the layout of the matrix.
+ */
+ gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) :
+ _11(a), _12(b),
+ _21(c), _22(d),
+ _31(tx), _32(ty) { }
+
+ MOZ_ALWAYS_INLINE gfxMatrix Copy() const {
+ return gfxMatrix(*this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const gfxMatrix& m) {
+ if (m.IsIdentity()) {
+ return stream << "[identity]";
+ }
+
+ return stream << "["
+ << m._11 << " " << m._12
+ << m._21 << " " << m._22
+ << m._31 << " " << m._32
+ << "]";
+ }
+
+ /**
+ * Post-multiplies m onto the matrix.
+ */
+ const gfxMatrix& operator *= (const gfxMatrix& m);
+
+ /**
+ * Multiplies *this with m and returns the result.
+ */
+ gfxMatrix operator * (const gfxMatrix& m) const {
+ return gfxMatrix(*this) *= m;
+ }
+
+ /**
+ * Multiplies *this with aMatrix and returns the result.
+ */
+ mozilla::gfx::Matrix4x4 operator * (const mozilla::gfx::Matrix4x4& aMatrix) const;
+
+ /* Returns true if the other matrix is fuzzy-equal to this matrix.
+ * Note that this isn't a cheap comparison!
+ */
+ bool operator==(const gfxMatrix& other) const
+ {
+ return FuzzyEqual(_11, other._11) && FuzzyEqual(_12, other._12) &&
+ FuzzyEqual(_21, other._21) && FuzzyEqual(_22, other._22) &&
+ FuzzyEqual(_31, other._31) && FuzzyEqual(_32, other._32);
+ }
+
+ bool operator!=(const gfxMatrix& other) const
+ {
+ return !(*this == other);
+ }
+
+ // matrix operations
+ /**
+ * Resets this matrix to the identity matrix.
+ */
+ const gfxMatrix& Reset();
+
+ bool IsIdentity() const {
+ return _11 == 1.0 && _12 == 0.0 &&
+ _21 == 0.0 && _22 == 1.0 &&
+ _31 == 0.0 && _32 == 0.0;
+ }
+
+ /* Returns true if the matrix is a rectilinear transformation (i.e.
+ * grid-aligned rectangles are transformed to grid-aligned rectangles)
+ */
+ bool IsRectilinear() const {
+ if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
+ return true;
+ } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Inverts this matrix, if possible. Otherwise, the matrix is left
+ * unchanged.
+ *
+ * XXX should this do something with the return value of
+ * cairo_matrix_invert?
+ */
+ bool Invert();
+
+ /**
+ * Check if matrix is singular (no inverse exists).
+ */
+ bool IsSingular() const {
+ // if the determinant (ad - bc) is zero it's singular
+ return (_11 * _22) == (_12 * _21);
+ }
+
+ /**
+ * Scales this matrix. The scale is pre-multiplied onto this matrix,
+ * i.e. the scaling takes place before the other transformations.
+ */
+ gfxMatrix& Scale(gfxFloat x, gfxFloat y);
+
+ /**
+ * Translates this matrix. The translation is pre-multiplied onto this matrix,
+ * i.e. the translation takes place before the other transformations.
+ */
+ gfxMatrix& Translate(const gfxPoint& pt);
+
+ gfxMatrix& Translate(gfxFloat x, gfxFloat y) {
+ return Translate(gfxPoint(x, y));
+ }
+
+ /**
+ * Rotates this matrix. The rotation is pre-multiplied onto this matrix,
+ * i.e. the translation takes place after the other transformations.
+ *
+ * @param radians Angle in radians.
+ */
+ gfxMatrix& Rotate(gfxFloat radians);
+
+ /**
+ * Multiplies the current matrix with m.
+ * This is a pre-multiplication, i.e. the transformations of m are
+ * applied _before_ the existing transformations.
+ */
+ gfxMatrix& PreMultiply(const gfxMatrix& m);
+
+ static gfxMatrix Translation(gfxFloat aX, gfxFloat aY)
+ {
+ return gfxMatrix(1.0, 0.0, 0.0, 1.0, aX, aY);
+ }
+
+ static gfxMatrix Translation(gfxPoint aPoint)
+ {
+ return Translation(aPoint.x, aPoint.y);
+ }
+
+ static gfxMatrix Rotation(gfxFloat aAngle);
+
+ static gfxMatrix Scaling(gfxFloat aX, gfxFloat aY)
+ {
+ return gfxMatrix(aX, 0.0, 0.0, aY, 0.0, 0.0);
+ }
+
+ /**
+ * Transforms a point according to this matrix.
+ */
+ gfxPoint Transform(const gfxPoint& point) const;
+
+
+ /**
+ * Transform a distance according to this matrix. This does not apply
+ * any translation components.
+ */
+ gfxSize Transform(const gfxSize& size) const;
+
+ /**
+ * Transforms both the point and distance according to this matrix.
+ */
+ gfxRect Transform(const gfxRect& rect) const;
+
+ gfxRect TransformBounds(const gfxRect& rect) const;
+
+ /**
+ * Returns the translation component of this matrix.
+ */
+ gfxPoint GetTranslation() const {
+ return gfxPoint(_31, _32);
+ }
+
+ /**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ bool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !FuzzyEqual(_31, floor(_31 + 0.5)) ||
+ !FuzzyEqual(_32, floor(_32 + 0.5));
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a straight translation
+ */
+ bool HasNonTranslation() const {
+ return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
+ !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix only has an integer translation.
+ */
+ bool HasOnlyIntegerTranslation() const {
+ return !HasNonIntegerTranslation();
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or a -1 y scale (y axis flip)
+ */
+ bool HasNonTranslationOrFlip() const {
+ return !FuzzyEqual(_11, 1.0) ||
+ (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) ||
+ !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or scale; this is, if there is
+ * no rotation.
+ */
+ bool HasNonAxisAlignedTransform() const {
+ return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /**
+ * Computes the determinant of this matrix.
+ */
+ double Determinant() const {
+ return _11*_22 - _12*_21;
+ }
+
+ /* Computes the scale factors of this matrix; that is,
+ * the amounts each basis vector is scaled by.
+ * The xMajor parameter indicates if the larger scale is
+ * to be assumed to be in the X direction or not.
+ */
+ gfxSize ScaleFactors(bool xMajor) const {
+ double det = Determinant();
+
+ if (det == 0.0)
+ return gfxSize(0.0, 0.0);
+
+ gfxSize sz = xMajor ? gfxSize(1.0, 0.0) : gfxSize(0.0, 1.0);
+ sz = Transform(sz);
+
+ double major = sqrt(sz.width * sz.width + sz.height * sz.height);
+ double minor = 0.0;
+
+ // ignore mirroring
+ if (det < 0.0)
+ det = - det;
+
+ if (major)
+ minor = det / major;
+
+ if (xMajor)
+ return gfxSize(major, minor);
+
+ return gfxSize(minor, major);
+ }
+
+ /**
+ * Snap matrix components that are close to integers
+ * to integers. In particular, components that are integral when
+ * converted to single precision are set to those integers.
+ */
+ gfxMatrix& NudgeToIntegers(void);
+
+ /**
+ * Returns true if matrix is multiple of 90 degrees rotation with flipping,
+ * scaling and translation.
+ */
+ bool PreservesAxisAlignedRectangles() const {
+ return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0))
+ || (FuzzyEqual(_21, 0.0) && FuzzyEqual(_12, 0.0)));
+ }
+
+private:
+ static bool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
+ return fabs(aV2 - aV1) < 1e-6;
+ }
+};
+
+#endif /* GFX_MATRIX_H */
diff --git a/system/graphics/thebes/gfxPattern.cpp b/system/graphics/thebes/gfxPattern.cpp
new file mode 100644
index 000000000..3092c8008
--- /dev/null
+++ b/system/graphics/thebes/gfxPattern.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPattern.h"
+
+#include "gfxUtils.h"
+#include "gfxTypes.h"
+#include "gfxASurface.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxGradientCache.h"
+#include "mozilla/gfx/2D.h"
+
+#include "cairo.h"
+
+#include <vector>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxPattern::gfxPattern(const Color& aColor)
+ : mExtend(ExtendMode::CLAMP)
+{
+ mGfxPattern.InitColorPattern(ToDeviceColor(aColor));
+}
+
+// linear
+gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1)
+ : mExtend(ExtendMode::CLAMP)
+{
+ mGfxPattern.InitLinearGradientPattern(Point(x0, y0), Point(x1, y1), nullptr);
+}
+
+// radial
+gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0,
+ gfxFloat cx1, gfxFloat cy1, gfxFloat radius1)
+ : mExtend(ExtendMode::CLAMP)
+{
+ mGfxPattern.InitRadialGradientPattern(Point(cx0, cy0), Point(cx1, cy1),
+ radius0, radius1, nullptr);
+}
+
+// Azure
+gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aPatternToUserSpace)
+ : mPatternToUserSpace(aPatternToUserSpace)
+ , mExtend(ExtendMode::CLAMP)
+{
+ mGfxPattern.InitSurfacePattern(aSurface, mExtend, Matrix(), // matrix is overridden in GetPattern()
+ mozilla::gfx::SamplingFilter::GOOD);
+}
+
+void
+gfxPattern::AddColorStop(gfxFloat offset, const Color& c)
+{
+ if (mGfxPattern.GetPattern()->GetType() != PatternType::LINEAR_GRADIENT &&
+ mGfxPattern.GetPattern()->GetType() != PatternType::RADIAL_GRADIENT) {
+ return;
+ }
+
+ mStops = nullptr;
+
+ GradientStop stop;
+ stop.offset = offset;
+ stop.color = ToDeviceColor(c);
+ mStopsList.AppendElement(stop);
+}
+
+void
+gfxPattern::SetColorStops(GradientStops* aStops)
+{
+ mStops = aStops;
+}
+
+void
+gfxPattern::CacheColorStops(const DrawTarget *aDT)
+{
+ mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, mStopsList, mExtend);
+}
+
+void
+gfxPattern::SetMatrix(const gfxMatrix& aPatternToUserSpace)
+{
+ mPatternToUserSpace = ToMatrix(aPatternToUserSpace);
+ // Cairo-pattern matrices specify the conversion from DrawTarget to pattern
+ // space. Azure pattern matrices specify the conversion from pattern to
+ // DrawTarget space.
+ mPatternToUserSpace.Invert();
+}
+
+gfxMatrix
+gfxPattern::GetMatrix() const
+{
+ // invert at the higher precision of gfxMatrix
+ // cause we need to convert at some point anyways
+ gfxMatrix mat = ThebesMatrix(mPatternToUserSpace);
+ mat.Invert();
+ return mat;
+}
+
+gfxMatrix
+gfxPattern::GetInverseMatrix() const
+{
+ return ThebesMatrix(mPatternToUserSpace);
+}
+
+Pattern*
+gfxPattern::GetPattern(const DrawTarget *aTarget,
+ Matrix *aOriginalUserToDevice)
+{
+ Matrix patternToUser = mPatternToUserSpace;
+
+ if (aOriginalUserToDevice &&
+ *aOriginalUserToDevice != aTarget->GetTransform()) {
+ // mPatternToUserSpace maps from pattern space to the original user space,
+ // but aTarget now has a transform to a different user space. In order for
+ // the Pattern* that we return to be usable in aTarget's new user space we
+ // need the Pattern's mMatrix to be the transform from pattern space to
+ // aTarget's -new- user space. That transform is equivalent to the
+ // transform from pattern space to original user space (patternToUser),
+ // multiplied by the transform from original user space to device space,
+ // multiplied by the transform from device space to current user space.
+
+ Matrix deviceToCurrentUser = aTarget->GetTransform();
+ deviceToCurrentUser.Invert();
+
+ patternToUser = patternToUser * *aOriginalUserToDevice * deviceToCurrentUser;
+ }
+ patternToUser.NudgeToIntegers();
+
+ if (!mStops &&
+ !mStopsList.IsEmpty()) {
+ mStops = aTarget->CreateGradientStops(mStopsList.Elements(),
+ mStopsList.Length(), mExtend);
+ }
+
+ switch (mGfxPattern.GetPattern()->GetType()) {
+ case PatternType::SURFACE: {
+ SurfacePattern* surfacePattern = static_cast<SurfacePattern*>(mGfxPattern.GetPattern());
+ surfacePattern->mMatrix = patternToUser;
+ surfacePattern->mExtendMode = mExtend;
+ break;
+ }
+ case PatternType::LINEAR_GRADIENT: {
+ LinearGradientPattern* linearGradientPattern = static_cast<LinearGradientPattern*>(mGfxPattern.GetPattern());
+ linearGradientPattern->mMatrix = patternToUser;
+ linearGradientPattern->mStops = mStops;
+ break;
+ }
+ case PatternType::RADIAL_GRADIENT: {
+ RadialGradientPattern* radialGradientPattern = static_cast<RadialGradientPattern*>(mGfxPattern.GetPattern());
+ radialGradientPattern->mMatrix = patternToUser;
+ radialGradientPattern->mStops = mStops;
+ break;
+ }
+ default:
+ /* Reassure the compiler we are handling all the enum values. */
+ break;
+ }
+
+ return mGfxPattern.GetPattern();
+}
+
+void
+gfxPattern::SetExtend(ExtendMode aExtend)
+{
+ mExtend = aExtend;
+ mStops = nullptr;
+}
+
+bool
+gfxPattern::IsOpaque()
+{
+ if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
+ return false;
+ }
+
+ if (static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
+ return true;
+ }
+ return false;
+}
+
+void
+gfxPattern::SetSamplingFilter(gfx::SamplingFilter filter)
+{
+ if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
+ return;
+ }
+
+ static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter = filter;
+}
+
+SamplingFilter
+gfxPattern::SamplingFilter() const
+{
+ if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
+ return gfx::SamplingFilter::GOOD;
+ }
+ return static_cast<const SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter;
+}
+
+bool
+gfxPattern::GetSolidColor(Color& aColorOut)
+{
+ if (mGfxPattern.GetPattern()->GetType() == PatternType::COLOR) {
+ aColorOut = static_cast<ColorPattern*>(mGfxPattern.GetPattern())->mColor;
+ return true;
+ }
+
+ return false;
+}
+
+int
+gfxPattern::CairoStatus()
+{
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/system/graphics/thebes/gfxPattern.h b/system/graphics/thebes/gfxPattern.h
new file mode 100644
index 000000000..a16d1ad39
--- /dev/null
+++ b/system/graphics/thebes/gfxPattern.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PATTERN_H
+#define GFX_PATTERN_H
+
+#include "gfxTypes.h"
+
+#include "gfxMatrix.h"
+#include "mozilla/Alignment.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PatternHelpers.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+typedef struct _cairo_pattern cairo_pattern_t;
+
+
+class gfxPattern final{
+ NS_INLINE_DECL_REFCOUNTING(gfxPattern)
+
+public:
+ explicit gfxPattern(const mozilla::gfx::Color& aColor);
+ // linear
+ gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1); // linear
+ gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0,
+ gfxFloat cx1, gfxFloat cy1, gfxFloat radius1); // radial
+ gfxPattern(mozilla::gfx::SourceSurface *aSurface,
+ const mozilla::gfx::Matrix &aPatternToUserSpace);
+
+ void AddColorStop(gfxFloat offset, const mozilla::gfx::Color& c);
+ void SetColorStops(mozilla::gfx::GradientStops* aStops);
+
+ // This should only be called on a cairo pattern that we want to use with
+ // Azure. We will read back the color stops from cairo and try to look
+ // them up in the cache.
+ void CacheColorStops(const mozilla::gfx::DrawTarget *aDT);
+
+ void SetMatrix(const gfxMatrix& matrix);
+ gfxMatrix GetMatrix() const;
+ gfxMatrix GetInverseMatrix() const;
+
+ /* Get an Azure Pattern for the current Cairo pattern. aPattern transform
+ * specifies the transform that was set on the DrawTarget when the pattern
+ * was set. When this is nullptr it is assumed the transform is identical
+ * to the current transform.
+ */
+ mozilla::gfx::Pattern *GetPattern(const mozilla::gfx::DrawTarget *aTarget,
+ mozilla::gfx::Matrix *aOriginalUserToDevice = nullptr);
+ bool IsOpaque();
+
+ // clamp, repeat, reflect
+ void SetExtend(mozilla::gfx::ExtendMode aExtend);
+
+ int CairoStatus();
+
+ void SetSamplingFilter(mozilla::gfx::SamplingFilter aSamplingFilter);
+ mozilla::gfx::SamplingFilter SamplingFilter() const;
+
+ /* returns TRUE if it succeeded */
+ bool GetSolidColor(mozilla::gfx::Color& aColorOut);
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~gfxPattern() {}
+
+ mozilla::gfx::GeneralPattern mGfxPattern;
+ RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
+ mozilla::gfx::Matrix mPatternToUserSpace;
+ RefPtr<mozilla::gfx::GradientStops> mStops;
+ nsTArray<mozilla::gfx::GradientStop> mStopsList;
+ mozilla::gfx::ExtendMode mExtend;
+};
+
+#endif /* GFX_PATTERN_H */
diff --git a/system/graphics/thebes/gfxPlatform.cpp b/system/graphics/thebes/gfxPlatform.cpp
new file mode 100644
index 000000000..28282a90d
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatform.cpp
@@ -0,0 +1,2477 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/Services.h"
+
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "gfxEnv.h"
+#include "gfxTextRun.h"
+#include "gfxConfig.h"
+#include "MediaPrefs.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#include "nsXULAppAPI.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+
+#if defined(XP_WIN)
+#include "gfxWindowsPlatform.h"
+#elif defined(MOZ_WIDGET_GTK)
+#include "gfxPlatformGtk.h"
+#elif defined(ANDROID)
+#include "gfxAndroidPlatform.h"
+#endif
+
+#ifdef XP_WIN
+#include "mozilla/WindowsVersion.h"
+#endif
+
+#include "nsGkAtoms.h"
+#include "gfxPlatformFontList.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "nsUnicodeProperties.h"
+#include "harfbuzz/hb.h"
+#include "gfxGraphiteShaper.h"
+#include "gfx2DGlue.h"
+#include "gfxGradientCache.h"
+#include "gfxUtils.h" // for NextPowerOfTwo
+
+#include "nsUnicodeRange.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+#include "nsILocaleService.h"
+#include "nsIObserverService.h"
+#include "nsIScreenManager.h"
+#include "FrameMetrics.h"
+#include "MainThreadUtils.h"
+
+#include "nsWeakReference.h"
+
+#include "cairo.h"
+#include "qcms.h"
+
+#include "imgITools.h"
+
+#include "plstr.h"
+#include "nsCRT.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "mozilla/gfx/Logging.h"
+
+#if defined(MOZ_WIDGET_GTK)
+#include "gfxPlatformGtk.h" // xxx - for UseFcFontList
+#endif
+
+#ifdef USE_SKIA
+# ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wshadow"
+# endif
+# include "skia/include/core/SkGraphics.h"
+# ifdef USE_SKIA_GPU
+# include "skia/include/gpu/GrContext.h"
+# include "skia/include/gpu/gl/GrGLInterface.h"
+# include "SkiaGLGlue.h"
+# endif
+# ifdef MOZ_ENABLE_FREETYPE
+# include "skia/include/ports/SkTypeface_cairo.h"
+# endif
+# ifdef __GNUC__
+# pragma GCC diagnostic pop // -Wshadow
+# endif
+static const uint32_t kDefaultGlyphCacheSize = -1;
+
+#endif
+
+#if !defined(USE_SKIA) || !defined(USE_SKIA_GPU)
+class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {
+};
+#endif
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+#include "nsAlgorithm.h"
+#include "nsIGfxInfo.h"
+#include "nsIXULRuntime.h"
+#include "VsyncSource.h"
+#include "SoftwareVsyncSource.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "prsystem.h"
+
+namespace mozilla {
+namespace layers {
+void ShutdownTileCache();
+} // namespace layers
+} // namespace mozilla
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+gfxPlatform *gPlatform = nullptr;
+static bool gEverInitialized = false;
+
+static Mutex* gGfxPlatformPrefsLock = nullptr;
+
+// These two may point to the same profile
+static qcms_profile *gCMSOutputProfile = nullptr;
+static qcms_profile *gCMSsRGBProfile = nullptr;
+
+static bool gCMSRGBTransformFailed = false;
+static qcms_transform *gCMSRGBTransform = nullptr;
+static qcms_transform *gCMSInverseRGBTransform = nullptr;
+static qcms_transform *gCMSRGBATransform = nullptr;
+
+static bool gCMSInitialized = false;
+static eCMSMode gCMSMode = eCMSMode_Off;
+
+static void ShutdownCMS();
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/SourceSurfaceCairo.h"
+using namespace mozilla::gfx;
+
+/* Class to listen for pref changes so that chrome code can dynamically
+ force sRGB as an output profile. See Bug #452125. */
+class SRGBOverrideObserver final : public nsIObserver,
+ public nsSupportsWeakReference
+{
+ ~SRGBOverrideObserver() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+/// This override of the LogForwarder, initially used for the critical graphics
+/// errors, is sending the log to the crash annotations as well, but only
+/// if the capacity set with the method below is >= 2. We always retain the
+/// very first critical message, and the latest capacity-1 messages are
+/// rotated through. Note that we don't expect the total number of times
+/// this gets called to be large - it is meant for critical errors only.
+
+class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
+{
+public:
+ explicit CrashStatsLogForwarder(const char* aKey);
+ virtual void Log(const std::string& aString) override;
+ virtual void CrashAction(LogReason aReason) override;
+ virtual bool UpdateStringsVector(const std::string& aString) override;
+
+ virtual LoggingRecord LoggingRecordCopy() override;
+
+ void SetCircularBufferSize(uint32_t aCapacity);
+
+private:
+ // Helper for the Log()
+ void UpdateCrashReport();
+
+private:
+ LoggingRecord mBuffer;
+ nsCString mCrashCriticalKey;
+ uint32_t mMaxCapacity;
+ int32_t mIndex;
+ Mutex mMutex;
+};
+
+CrashStatsLogForwarder::CrashStatsLogForwarder(const char* aKey)
+ : mBuffer()
+ , mCrashCriticalKey(aKey)
+ , mMaxCapacity(0)
+ , mIndex(-1)
+ , mMutex("CrashStatsLogForwarder")
+{
+}
+
+void CrashStatsLogForwarder::SetCircularBufferSize(uint32_t aCapacity)
+{
+ MutexAutoLock lock(mMutex);
+
+ mMaxCapacity = aCapacity;
+ mBuffer.reserve(static_cast<size_t>(aCapacity));
+}
+
+LoggingRecord
+CrashStatsLogForwarder::LoggingRecordCopy()
+{
+ MutexAutoLock lock(mMutex);
+ return mBuffer;
+}
+
+bool
+CrashStatsLogForwarder::UpdateStringsVector(const std::string& aString)
+{
+ // We want at least the first one and the last one. Otherwise, no point.
+ if (mMaxCapacity < 2) {
+ return false;
+ }
+
+ mIndex += 1;
+ MOZ_ASSERT(mIndex >= 0);
+
+ // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ...
+ int32_t index = mIndex ? (mIndex-1) % (mMaxCapacity-1) + 1 : 0;
+ MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity);
+ MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size());
+
+ bool ignored;
+ double tStamp = (TimeStamp::NowLoRes()-TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+
+ // Checking for index >= mBuffer.size(), rather than index == mBuffer.size()
+ // just out of paranoia, but we know index <= mBuffer.size().
+ LoggingRecordEntry newEntry(mIndex,aString,tStamp);
+ if (index >= static_cast<int32_t>(mBuffer.size())) {
+ mBuffer.push_back(newEntry);
+ } else {
+ mBuffer[index] = newEntry;
+ }
+ return true;
+}
+
+void CrashStatsLogForwarder::UpdateCrashReport()
+{
+ std::stringstream message;
+ std::string logAnnotation;
+
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ logAnnotation = "|[";
+ break;
+ case GeckoProcessType_Content:
+ logAnnotation = "|[C";
+ break;
+ case GeckoProcessType_GPU:
+ logAnnotation = "|[G";
+ break;
+ default:
+ logAnnotation = "|[X";
+ break;
+ }
+
+ for (LoggingRecord::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) {
+ message << logAnnotation << Get<0>(*it) << "]" << Get<1>(*it) << " (t=" << Get<2>(*it) << ") ";
+ }
+
+ nsresult annotated = NS_ERROR_NOT_IMPLEMENTED;
+ if (annotated != NS_OK) {
+ printf("Crash Annotation %s: %s",
+ mCrashCriticalKey.get(), message.str().c_str());
+ }
+}
+
+class LogForwarderEvent : public Runnable
+{
+ virtual ~LogForwarderEvent() {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ explicit LogForwarderEvent(const nsCString& aMessage) : mMessage(aMessage) {}
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread() && (XRE_IsContentProcess() || XRE_IsGPUProcess()));
+
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ Unused << cc->SendGraphicsError(mMessage);
+ } else if (XRE_IsGPUProcess()) {
+ GPUParent* gp = GPUParent::GetSingleton();
+ Unused << gp->SendGraphicsError(mMessage);
+ }
+
+ return NS_OK;
+ }
+
+protected:
+ nsCString mMessage;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(LogForwarderEvent, Runnable);
+
+void CrashStatsLogForwarder::Log(const std::string& aString)
+{
+ MutexAutoLock lock(mMutex);
+
+ if (UpdateStringsVector(aString)) {
+ UpdateCrashReport();
+ }
+
+ // Add it to the parent strings
+ if (!XRE_IsParentProcess()) {
+ nsCString stringToSend(aString.c_str());
+ if (NS_IsMainThread()) {
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ Unused << cc->SendGraphicsError(stringToSend);
+ } else if (XRE_IsGPUProcess()) {
+ GPUParent* gp = GPUParent::GetSingleton();
+ Unused << gp->SendGraphicsError(stringToSend);
+ }
+ } else {
+ nsCOMPtr<nsIRunnable> r1 = new LogForwarderEvent(stringToSend);
+ NS_DispatchToMainThread(r1);
+ }
+ }
+}
+
+void
+CrashStatsLogForwarder::CrashAction(LogReason aReason)
+{
+ MOZ_CRASH("GFX_CRASH");
+}
+
+NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference)
+
+#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
+
+#define GFX_PREF_FALLBACK_USE_CMAPS "gfx.font_rendering.fallback.always_use_cmaps"
+
+#define GFX_PREF_OPENTYPE_SVG "gfx.font_rendering.opentype_svg.enabled"
+
+#define GFX_PREF_WORD_CACHE_CHARLIMIT "gfx.font_rendering.wordcache.charlimit"
+#define GFX_PREF_WORD_CACHE_MAXENTRIES "gfx.font_rendering.wordcache.maxentries"
+
+#define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled"
+
+#define BIDI_NUMERAL_PREF "bidi.numeral"
+
+#define GFX_PREF_CMS_FORCE_SRGB "gfx.color_management.force_srgb"
+
+NS_IMETHODIMP
+SRGBOverrideObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t* someData)
+{
+ NS_ASSERTION(NS_strcmp(someData,
+ (u"" GFX_PREF_CMS_FORCE_SRGB)) == 0,
+ "Restarting CMS on wrong pref!");
+ ShutdownCMS();
+ // Update current cms profile.
+ gfxPlatform::CreateCMSOutputProfile();
+ return NS_OK;
+}
+
+static const char* kObservedPrefs[] = {
+ "gfx.downloadable_fonts.",
+ "gfx.font_rendering.",
+ BIDI_NUMERAL_PREF,
+ nullptr
+};
+
+class FontPrefsObserver final : public nsIObserver
+{
+ ~FontPrefsObserver() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(FontPrefsObserver, nsIObserver)
+
+NS_IMETHODIMP
+FontPrefsObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData)
+{
+ if (!someData) {
+ NS_ERROR("font pref observer code broken");
+ return NS_ERROR_UNEXPECTED;
+ }
+ NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone");
+ gfxPlatform::GetPlatform()->FontsPrefsChanged(NS_ConvertUTF16toUTF8(someData).get());
+
+ return NS_OK;
+}
+
+class MemoryPressureObserver final : public nsIObserver
+{
+ ~MemoryPressureObserver() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(MemoryPressureObserver, nsIObserver)
+
+NS_IMETHODIMP
+MemoryPressureObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData)
+{
+ NS_ASSERTION(strcmp(aTopic, "memory-pressure") == 0, "unexpected event topic");
+ Factory::PurgeAllCaches();
+ gfxGradientCache::PurgeAllCaches();
+
+ gfxPlatform::PurgeSkiaFontCache();
+ gfxPlatform::GetPlatform()->PurgeSkiaGPUCache();
+ return NS_OK;
+}
+
+gfxPlatform::gfxPlatform()
+ : mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo)
+ , mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo)
+ , mTilesInfoCollector(this, &gfxPlatform::GetTilesSupportInfo)
+ , mCompositorBackend(layers::LayersBackend::LAYERS_NONE)
+ , mScreenDepth(0)
+ , mDeviceCounter(0)
+{
+ mAllowDownloadableFonts = UNINITIALIZED_VALUE;
+ mFallbackUsesCmaps = UNINITIALIZED_VALUE;
+
+ mWordCacheCharLimit = UNINITIALIZED_VALUE;
+ mWordCacheMaxEntries = UNINITIALIZED_VALUE;
+ mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
+ mOpenTypeSVGEnabled = UNINITIALIZED_VALUE;
+ mBidiNumeralOption = UNINITIALIZED_VALUE;
+
+ mSkiaGlue = nullptr;
+
+ uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+ uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+#ifdef USE_SKIA
+ canvasMask |= BackendTypeBit(BackendType::SKIA);
+ contentMask |= BackendTypeBit(BackendType::SKIA);
+#endif
+ InitBackendPrefs(canvasMask, BackendType::CAIRO,
+ contentMask, BackendType::CAIRO);
+
+ mTotalSystemMemory = PR_GetPhysicalMemorySize();
+}
+
+gfxPlatform*
+gfxPlatform::GetPlatform()
+{
+ if (!gPlatform) {
+ Init();
+ }
+ return gPlatform;
+}
+
+bool
+gfxPlatform::Initialized()
+{
+ return !!gPlatform;
+}
+
+void RecordingPrefChanged(const char *aPrefName, void *aClosure)
+{
+ if (Preferences::GetBool("gfx.2d.recording", false)) {
+ nsAutoCString fileName;
+ nsAdoptingString prefFileName = Preferences::GetString("gfx.2d.recordingfile");
+
+ if (prefFileName) {
+ fileName.Append(NS_ConvertUTF16toUTF8(prefFileName));
+ } else {
+ nsCOMPtr<nsIFile> tmpFile;
+ if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
+ return;
+ }
+ fileName.AppendPrintf("moz2drec_%i_%i.aer", XRE_GetProcessType(), getpid());
+
+ nsresult rv = tmpFile->AppendNative(fileName);
+ if (NS_FAILED(rv))
+ return;
+
+ rv = tmpFile->GetNativePath(fileName);
+ if (NS_FAILED(rv))
+ return;
+ }
+
+ gPlatform->mRecorder = Factory::CreateEventRecorderForFile(fileName.BeginReading());
+ printf_stderr("Recording to %s\n", fileName.get());
+ Factory::SetGlobalEventRecorder(gPlatform->mRecorder);
+ } else {
+ Factory::SetGlobalEventRecorder(nullptr);
+ }
+}
+
+#if defined(USE_SKIA)
+static uint32_t GetSkiaGlyphCacheSize()
+{
+ // 10mb as the default cache size on desktop due to talos perf tweaking.
+ // Chromium uses 20mb and skia default uses 2mb.
+ // We don't need to change the font cache count since we usually
+ // cache thrash due to asian character sets in talos.
+ // Only increase memory on the content proces
+ uint32_t cacheSize = 10 * 1024 * 1024;
+ if (mozilla::BrowserTabsRemoteAutostart()) {
+ return XRE_IsContentProcess() ? cacheSize : kDefaultGlyphCacheSize;
+ }
+
+ return cacheSize;
+}
+#endif
+
+void
+gfxPlatform::Init()
+{
+ MOZ_RELEASE_ASSERT(!XRE_IsGPUProcess(), "GFX: Not allowed in GPU process.");
+ MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");
+
+ if (gEverInitialized) {
+ NS_RUNTIMEABORT("Already started???");
+ }
+ gEverInitialized = true;
+
+ // Initialize the preferences by creating the singleton.
+ gfxPrefs::GetSingleton();
+ MediaPrefs::GetSingleton();
+ gfxVars::Initialize();
+
+ gfxConfig::Init();
+
+ if (XRE_IsParentProcess()) {
+ GPUProcessManager::Initialize();
+
+ if (Preferences::GetBool("media.wmf.skip-blacklist")) {
+ gfxVars::SetPDMWMFDisableD3D11Dlls(nsCString());
+ gfxVars::SetPDMWMFDisableD3D9Dlls(nsCString());
+ } else {
+ gfxVars::SetPDMWMFDisableD3D11Dlls(Preferences::GetCString("media.wmf.disable-d3d11-for-dlls"));
+ gfxVars::SetPDMWMFDisableD3D9Dlls(Preferences::GetCString("media.wmf.disable-d3d9-for-dlls"));
+ }
+ }
+
+ InitMoz2DLogging();
+
+ gGfxPlatformPrefsLock = new Mutex("gfxPlatform::gGfxPlatformPrefsLock");
+
+ /* Initialize the GfxInfo service.
+ * Note: we can't call functions on GfxInfo that depend
+ * on gPlatform until after it has been initialized
+ * below. GfxInfo initialization annotates our
+ * crash reports so we want to do it before
+ * we try to load any drivers and do device detection
+ * incase that code crashes. See bug #591561. */
+ nsCOMPtr<nsIGfxInfo> gfxInfo;
+ /* this currently will only succeed on Windows */
+ gfxInfo = services::GetGfxInfo();
+
+#if defined(XP_WIN)
+ gPlatform = new gfxWindowsPlatform;
+#elif defined(MOZ_WIDGET_GTK)
+ gPlatform = new gfxPlatformGtk;
+#elif defined(ANDROID)
+ gPlatform = new gfxAndroidPlatform;
+#else
+ #error "No gfxPlatform implementation available"
+#endif
+ gPlatform->InitAcceleration();
+
+ if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+ GPUProcessManager* gpu = GPUProcessManager::Get();
+ gpu->LaunchGPUProcess();
+ }
+
+ if (XRE_IsParentProcess()) {
+ if (gfxPlatform::ForceSoftwareVsync()) {
+ gPlatform->mVsyncSource = (gPlatform)->gfxPlatform::CreateHardwareVsyncSource();
+ } else {
+ gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
+ }
+ }
+
+#ifdef USE_SKIA
+ SkGraphics::Init();
+# ifdef MOZ_ENABLE_FREETYPE
+ SkInitCairoFT(gPlatform->FontHintingEnabled());
+# endif
+#endif
+
+#ifdef MOZ_GL_DEBUG
+ GLContext::StaticInit();
+#endif
+
+ InitLayersIPC();
+
+ gPlatform->PopulateScreenInfo();
+ gPlatform->ComputeTileSize();
+
+ nsresult rv;
+
+ bool usePlatformFontList = true;
+#if defined(MOZ_WIDGET_GTK)
+ usePlatformFontList = gfxPlatformGtk::UseFcFontList();
+#endif
+
+ if (usePlatformFontList) {
+ rv = gfxPlatformFontList::Init();
+ if (NS_FAILED(rv)) {
+ NS_RUNTIMEABORT("Could not initialize gfxPlatformFontList");
+ }
+ }
+
+ gPlatform->mScreenReferenceSurface =
+ gPlatform->CreateOffscreenSurface(IntSize(1, 1),
+ SurfaceFormat::A8R8G8B8_UINT32);
+ if (!gPlatform->mScreenReferenceSurface) {
+ NS_RUNTIMEABORT("Could not initialize mScreenReferenceSurface");
+ }
+
+ gPlatform->mScreenReferenceDrawTarget =
+ gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
+ SurfaceFormat::B8G8R8A8);
+ if (!gPlatform->mScreenReferenceDrawTarget ||
+ !gPlatform->mScreenReferenceDrawTarget->IsValid()) {
+ NS_RUNTIMEABORT("Could not initialize mScreenReferenceDrawTarget");
+ }
+
+ rv = gfxFontCache::Init();
+ if (NS_FAILED(rv)) {
+ NS_RUNTIMEABORT("Could not initialize gfxFontCache");
+ }
+
+ /* Create and register our CMS Override observer. */
+ gPlatform->mSRGBOverrideObserver = new SRGBOverrideObserver();
+ Preferences::AddWeakObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
+
+ gPlatform->mFontPrefsObserver = new FontPrefsObserver();
+ Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
+
+ GLContext::PlatformStartup();
+
+ Preferences::RegisterCallbackAndCall(RecordingPrefChanged, "gfx.2d.recording", nullptr);
+
+ CreateCMSOutputProfile();
+
+ // Listen to memory pressure event so we can purge DrawTarget caches
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ gPlatform->mMemoryPressureObserver = new MemoryPressureObserver();
+ obs->AddObserver(gPlatform->mMemoryPressureObserver, "memory-pressure", false);
+ }
+
+ // Request the imgITools service, implicitly initializing ImageLib.
+ nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1");
+ if (!imgTools) {
+ NS_RUNTIMEABORT("Could not initialize ImageLib");
+ }
+
+ RegisterStrongMemoryReporter(new GfxMemoryImageReporter());
+
+#ifdef USE_SKIA
+ uint32_t skiaCacheSize = GetSkiaGlyphCacheSize();
+ if (skiaCacheSize != kDefaultGlyphCacheSize) {
+ SkGraphics::SetFontCacheLimit(skiaCacheSize);
+ }
+#endif
+
+ InitNullMetadata();
+ InitOpenGLConfig();
+
+ if (obs) {
+ obs->NotifyObservers(nullptr, "gfx-features-ready", nullptr);
+ }
+}
+
+/* static */ void
+gfxPlatform::InitMoz2DLogging()
+{
+ auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
+ fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
+
+ mozilla::gfx::Config cfg;
+ cfg.mLogForwarder = fwd;
+ cfg.mMaxTextureSize = gfxPrefs::MaxTextureSize();
+ cfg.mMaxAllocSize = gfxPrefs::MaxAllocSize();
+
+ gfx::Factory::Init(cfg);
+}
+
+static bool sLayersIPCIsUp = false;
+
+/* static */ void
+gfxPlatform::InitNullMetadata()
+{
+ ScrollMetadata::sNullMetadata = new ScrollMetadata();
+ ClearOnShutdown(&ScrollMetadata::sNullMetadata);
+}
+
+void
+gfxPlatform::Shutdown()
+{
+ // In some cases, gPlatform may not be created but Shutdown() called,
+ // e.g., during xpcshell tests.
+ if (!gPlatform) {
+ return;
+ }
+
+ MOZ_ASSERT(!sLayersIPCIsUp);
+
+ // These may be called before the corresponding subsystems have actually
+ // started up. That's OK, they can handle it.
+ gfxFontCache::Shutdown();
+ gfxFontGroup::Shutdown();
+ gfxGradientCache::Shutdown();
+ gfxAlphaBoxBlur::ShutdownBlurCache();
+ gfxGraphiteShaper::Shutdown();
+ gfxPlatformFontList::Shutdown();
+ ShutdownTileCache();
+
+ // Free the various non-null transforms and loaded profiles
+ ShutdownCMS();
+
+ /* Unregister our CMS Override callback. */
+ NS_ASSERTION(gPlatform->mSRGBOverrideObserver, "mSRGBOverrideObserver has alreay gone");
+ Preferences::RemoveObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
+ gPlatform->mSRGBOverrideObserver = nullptr;
+
+ NS_ASSERTION(gPlatform->mFontPrefsObserver, "mFontPrefsObserver has alreay gone");
+ Preferences::RemoveObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
+ gPlatform->mFontPrefsObserver = nullptr;
+
+ NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
+ }
+
+ gPlatform->mMemoryPressureObserver = nullptr;
+ gPlatform->mSkiaGlue = nullptr;
+
+ if (XRE_IsParentProcess()) {
+ gPlatform->mVsyncSource->Shutdown();
+ }
+
+ gPlatform->mVsyncSource = nullptr;
+
+ // Shut down the default GL context provider.
+ GLContextProvider::Shutdown();
+
+#if defined(XP_WIN)
+ // The above shutdown calls operate on the available context providers on
+ // most platforms. Windows is a "special snowflake", though, and has three
+ // context providers available, so we have to shut all of them down.
+ // We should only support the default GL provider on Windows; then, this
+ // could go away. Unfortunately, we currently support WGL (the default) for
+ // WebGL on Optimus.
+ GLContextProviderEGL::Shutdown();
+#endif
+
+ if (XRE_IsParentProcess()) {
+ GPUProcessManager::Shutdown();
+ }
+
+ gfx::Factory::ShutDown();
+
+ delete gGfxPlatformPrefsLock;
+
+ gfxVars::Shutdown();
+ gfxPrefs::DestroySingleton();
+ gfxFont::DestroySingletons();
+
+ gfxConfig::Shutdown();
+
+ gPlatform->WillShutdown();
+
+ delete gPlatform;
+ gPlatform = nullptr;
+}
+
+/* static */ void
+gfxPlatform::InitLayersIPC()
+{
+ if (sLayersIPCIsUp) {
+ return;
+ }
+ sLayersIPCIsUp = true;
+
+ if (XRE_IsParentProcess())
+ {
+ layers::CompositorThreadHolder::Start();
+ }
+}
+
+/* static */ void
+gfxPlatform::ShutdownLayersIPC()
+{
+ if (!sLayersIPCIsUp) {
+ return;
+ }
+ sLayersIPCIsUp = false;
+
+ if (XRE_IsContentProcess()) {
+ // cf bug 1215265.
+ if (gfxPrefs::ChildProcessShutdown()) {
+ layers::CompositorBridgeChild::ShutDown();
+ layers::ImageBridgeChild::ShutDown();
+ }
+ } else if (XRE_IsParentProcess()) {
+ layers::CompositorBridgeChild::ShutDown();
+ layers::ImageBridgeChild::ShutDown();
+
+ // This has to happen after shutting down the child protocols.
+ layers::CompositorThreadHolder::Shutdown();
+ } else {
+ // TODO: There are other kind of processes and we should make sure gfx
+ // stuff is either not created there or shut down properly.
+ }
+}
+
+void
+gfxPlatform::WillShutdown()
+{
+ // Destoy these first in case they depend on backend-specific resources.
+ // Otherwise, the backend's destructor would be called before the
+ // base gfxPlatform destructor.
+ mScreenReferenceSurface = nullptr;
+ mScreenReferenceDrawTarget = nullptr;
+}
+
+gfxPlatform::~gfxPlatform()
+{
+ // The cairo folks think we should only clean up in debug builds,
+ // but we're generally in the habit of trying to shut down as
+ // cleanly as possible even in production code, so call this
+ // cairo_debug_* function unconditionally.
+ //
+ // because cairo can assert and thus crash on shutdown, don't do this in release builds
+#ifdef NS_FREE_PERMANENT_DATA
+#ifdef USE_SKIA
+ // must do Skia cleanup before Cairo cleanup, because Skia may be referencing
+ // Cairo objects e.g. through SkCairoFTTypeface
+ SkGraphics::PurgeFontCache();
+#endif
+
+#if MOZ_TREE_CAIRO
+ cairo_debug_reset_static_data();
+#endif
+#endif
+}
+
+/* static */ already_AddRefed<DrawTarget>
+gfxPlatform::CreateDrawTargetForSurface(gfxASurface *aSurface, const IntSize& aSize)
+{
+ SurfaceFormat format = aSurface->GetSurfaceFormat();
+ RefPtr<DrawTarget> drawTarget = Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(), aSize, &format);
+ if (!drawTarget) {
+ gfxWarning() << "gfxPlatform::CreateDrawTargetForSurface failed in CreateDrawTargetForCairoSurface";
+ return nullptr;
+ }
+ return drawTarget.forget();
+}
+
+cairo_user_data_key_t kSourceSurface;
+
+/**
+ * Record the backend that was used to construct the SourceSurface.
+ * When getting the cached SourceSurface for a gfxASurface/DrawTarget pair,
+ * we check to make sure the DrawTarget's backend matches the backend
+ * for the cached SourceSurface, and only use it if they match. This
+ * can avoid expensive and unnecessary readbacks.
+ */
+struct SourceSurfaceUserData
+{
+ RefPtr<SourceSurface> mSrcSurface;
+ BackendType mBackendType;
+};
+
+void SourceBufferDestroy(void *srcSurfUD)
+{
+ delete static_cast<SourceSurfaceUserData*>(srcSurfUD);
+}
+
+UserDataKey kThebesSurface;
+
+struct DependentSourceSurfaceUserData
+{
+ RefPtr<gfxASurface> mSurface;
+};
+
+void SourceSurfaceDestroyed(void *aData)
+{
+ delete static_cast<DependentSourceSurfaceUserData*>(aData);
+}
+
+void
+gfxPlatform::ClearSourceSurfaceForSurface(gfxASurface *aSurface)
+{
+ aSurface->SetData(&kSourceSurface, nullptr, nullptr);
+}
+
+/* static */ already_AddRefed<SourceSurface>
+gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget,
+ gfxASurface *aSurface,
+ bool aIsPlugin)
+{
+ if (!aSurface->CairoSurface() || aSurface->CairoStatus()) {
+ return nullptr;
+ }
+
+ if (!aTarget) {
+ aTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+ }
+
+ void *userData = aSurface->GetData(&kSourceSurface);
+
+ if (userData) {
+ SourceSurfaceUserData *surf = static_cast<SourceSurfaceUserData*>(userData);
+
+ if (surf->mSrcSurface->IsValid() && surf->mBackendType == aTarget->GetBackendType()) {
+ RefPtr<SourceSurface> srcSurface(surf->mSrcSurface);
+ return srcSurface.forget();
+ }
+ // We can just continue here as when setting new user data the destroy
+ // function will be called for the old user data.
+ }
+
+ SurfaceFormat format = aSurface->GetSurfaceFormat();
+
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
+ // If we're going to be used with a CAIRO DrawTarget, then just create a
+ // SourceSurfaceCairo since we don't know the underlying type of the CAIRO
+ // DrawTarget and can't pick a better surface type. Doing this also avoids
+ // readback of aSurface's surface into memory if, for example, aSurface
+ // wraps an xlib cairo surface (which can be important to avoid a major
+ // slowdown).
+ //
+ // We return here regardless of whether CreateSourceSurfaceFromNativeSurface
+ // succeeds or not since we don't expect to be able to do any better below
+ // if it fails.
+ //
+ // Note that the returned SourceSurfaceCairo holds a strong reference to
+ // the cairo_surface_t* that it wraps, which essencially means it holds a
+ // strong reference to aSurface since aSurface shares its
+ // cairo_surface_t*'s reference count variable. As a result we can't cache
+ // srcBuffer on aSurface (see below) since aSurface would then hold a
+ // strong reference back to srcBuffer, creating a reference loop and a
+ // memory leak. Not caching is fine since wrapping is cheap enough (no
+ // copying) so we can just wrap again next time we're called.
+ return Factory::CreateSourceSurfaceForCairoSurface(aSurface->CairoSurface(),
+ aSurface->GetSize(), format);
+ }
+
+ RefPtr<SourceSurface> srcBuffer;
+
+ // Currently no other DrawTarget types implement CreateSourceSurfaceFromNativeSurface
+
+ if (!srcBuffer) {
+ // If aSurface wraps data, we can create a SourceSurfaceRawData that wraps
+ // the same data, then optimize it for aTarget:
+ RefPtr<DataSourceSurface> surf = GetWrappedDataSourceSurface(aSurface);
+ if (surf) {
+ srcBuffer = aIsPlugin ? aTarget->OptimizeSourceSurfaceForUnknownAlpha(surf)
+ : aTarget->OptimizeSourceSurface(surf);
+
+ if (srcBuffer == surf) {
+ // GetWrappedDataSourceSurface returns a SourceSurface that holds a
+ // strong reference to aSurface since it wraps aSurface's data and
+ // needs it to stay alive. As a result we can't cache srcBuffer on
+ // aSurface (below) since aSurface would then hold a strong reference
+ // back to srcBuffer, creating a reference loop and a memory leak. Not
+ // caching is fine since wrapping is cheap enough (no copying) so we
+ // can just wrap again next time we're called.
+ //
+ // Note that the check below doesn't catch this since srcBuffer will be a
+ // SourceSurfaceRawData object (even if aSurface is not a gfxImageSurface
+ // object), which is why we need this separate check.
+ return srcBuffer.forget();
+ }
+ }
+ }
+
+ if (!srcBuffer) {
+ MOZ_ASSERT(aTarget->GetBackendType() != BackendType::CAIRO,
+ "We already tried CreateSourceSurfaceFromNativeSurface with a "
+ "DrawTargetCairo above");
+ // We've run out of performant options. We now try creating a SourceSurface
+ // using a temporary DrawTargetCairo and then optimizing it to aTarget's
+ // actual type. The CreateSourceSurfaceFromNativeSurface() call will
+ // likely create a DataSourceSurface (possibly involving copying and/or
+ // readback), and the OptimizeSourceSurface may well copy again and upload
+ // to the GPU. So, while this code path is rarely hit, hitting it may be
+ // very slow.
+ srcBuffer = Factory::CreateSourceSurfaceForCairoSurface(aSurface->CairoSurface(),
+ aSurface->GetSize(), format);
+ if (srcBuffer) {
+ srcBuffer = aTarget->OptimizeSourceSurface(srcBuffer);
+ }
+ }
+
+ if (!srcBuffer) {
+ return nullptr;
+ }
+
+ if ((srcBuffer->GetType() == SurfaceType::CAIRO &&
+ static_cast<SourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
+ aSurface->CairoSurface()) ||
+ (srcBuffer->GetType() == SurfaceType::CAIRO_IMAGE &&
+ static_cast<DataSourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
+ aSurface->CairoSurface())) {
+ // See the "Note that the returned SourceSurfaceCairo..." comment above.
+ return srcBuffer.forget();
+ }
+
+ // Add user data to aSurface so we can cache lookups in the future.
+ SourceSurfaceUserData *srcSurfUD = new SourceSurfaceUserData;
+ srcSurfUD->mBackendType = aTarget->GetBackendType();
+ srcSurfUD->mSrcSurface = srcBuffer;
+ aSurface->SetData(&kSourceSurface, srcSurfUD, SourceBufferDestroy);
+
+ return srcBuffer.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+gfxPlatform::GetWrappedDataSourceSurface(gfxASurface* aSurface)
+{
+ RefPtr<gfxImageSurface> image = aSurface->GetAsImageSurface();
+ if (!image) {
+ return nullptr;
+ }
+ RefPtr<DataSourceSurface> result =
+ Factory::CreateWrappingDataSourceSurface(image->Data(),
+ image->Stride(),
+ image->GetSize(),
+ ImageFormatToSurfaceFormat(image->Format()));
+
+ if (!result) {
+ return nullptr;
+ }
+
+ // If we wrapped the underlying data of aSurface, then we need to add user data
+ // to make sure aSurface stays alive until we are done with the data.
+ DependentSourceSurfaceUserData *srcSurfUD = new DependentSourceSurfaceUserData;
+ srcSurfUD->mSurface = aSurface;
+ result->AddUserData(&kThebesSurface, srcSurfUD, SourceSurfaceDestroyed);
+
+ return result.forget();
+}
+
+already_AddRefed<ScaledFont>
+gfxPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
+{
+ NativeFont nativeFont;
+ nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
+ nativeFont.mFont = aFont->GetCairoScaledFont();
+ return Factory::CreateScaledFontForNativeFont(nativeFont,
+ aFont->GetAdjustedSize());
+}
+
+void
+gfxPlatform::ComputeTileSize()
+{
+ // The tile size should be picked in the parent processes
+ // and sent to the child processes over IPDL GetTileSize.
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ int32_t w = gfxPrefs::LayersTileWidth();
+ int32_t h = gfxPrefs::LayersTileHeight();
+
+ if (gfxPrefs::LayersTilesAdjust()) {
+ gfx::IntSize screenSize = GetScreenSize();
+ if (screenSize.width > 0) {
+ // Choose a size so that there are between 2 and 4 tiles per screen width.
+ // FIXME: we should probably make sure this is within the max texture size,
+ // but I think everything should at least support 1024
+ w = h = clamped(int32_t(RoundUpPow2(screenSize.width)) / 4, 256, 1024);
+ }
+ }
+
+ // Don't allow changing the tile size after we've set it.
+ // Right now the code assumes that the tile size doesn't change.
+ MOZ_ASSERT(gfxVars::TileSize().width == -1 &&
+ gfxVars::TileSize().height == -1);
+
+ gfxVars::SetTileSize(IntSize(w, h));
+}
+
+void
+gfxPlatform::PopulateScreenInfo()
+{
+ nsCOMPtr<nsIScreenManager> manager = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ MOZ_ASSERT(manager, "failed to get nsIScreenManager");
+
+ nsCOMPtr<nsIScreen> screen;
+ manager->GetPrimaryScreen(getter_AddRefs(screen));
+ if (!screen) {
+ // This can happen in xpcshell, for instance
+ return;
+ }
+
+ screen->GetColorDepth(&mScreenDepth);
+
+ int left, top;
+ screen->GetRect(&left, &top, &mScreenSize.width, &mScreenSize.height);
+}
+
+bool
+gfxPlatform::SupportsAzureContentForDrawTarget(DrawTarget* aTarget)
+{
+ if (!aTarget || !aTarget->IsValid()) {
+ return false;
+ }
+
+#ifdef USE_SKIA_GPU
+ // Skia content rendering doesn't support GPU acceleration, so we can't
+ // use the same backend if the current backend is accelerated.
+ if ((aTarget->GetType() == DrawTargetType::HARDWARE_RASTER)
+ && (aTarget->GetBackendType() == BackendType::SKIA))
+ {
+ return false;
+ }
+#endif
+
+ return SupportsAzureContentForType(aTarget->GetBackendType());
+}
+
+bool gfxPlatform::AllowOpenGLCanvas()
+{
+ // For now, only allow Skia+OpenGL, unless it's blocked.
+ // Allow acceleration on Skia if the preference is set, unless it's blocked
+ // as long as we have the accelerated layers
+
+ // The compositor backend is only set correctly in the parent process,
+ // so we let content process always assume correct compositor backend.
+ // The callers have to do the right thing.
+ bool correctBackend = !XRE_IsParentProcess() ||
+ ((mCompositorBackend == LayersBackend::LAYERS_OPENGL) &&
+ (GetContentBackendFor(mCompositorBackend) == BackendType::SKIA));
+
+ if (gfxPrefs::CanvasAzureAccelerated() && correctBackend) {
+ nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+ int32_t status;
+ nsCString discardFailureId;
+ return !gfxInfo ||
+ (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
+ discardFailureId,
+ &status)) &&
+ status == nsIGfxInfo::FEATURE_STATUS_OK);
+ }
+ return false;
+}
+
+void
+gfxPlatform::InitializeSkiaCacheLimits()
+{
+ if (AllowOpenGLCanvas()) {
+#ifdef USE_SKIA_GPU
+ bool usingDynamicCache = gfxPrefs::CanvasSkiaGLDynamicCache();
+ int cacheItemLimit = gfxPrefs::CanvasSkiaGLCacheItems();
+ uint64_t cacheSizeLimit = std::max(gfxPrefs::CanvasSkiaGLCacheSize(), (int32_t)0);
+
+ // Prefs are in megabytes, but we want the sizes in bytes
+ cacheSizeLimit *= 1024*1024;
+
+ if (usingDynamicCache) {
+ if (mTotalSystemMemory < 512*1024*1024) {
+ // We need a very minimal cache on anything smaller than 512mb.
+ // Note the large jump as we cross 512mb (from 2mb to 32mb).
+ cacheSizeLimit = 2*1024*1024;
+ } else if (mTotalSystemMemory > 0) {
+ cacheSizeLimit = mTotalSystemMemory / 16;
+ }
+ }
+
+ // Ensure cache size doesn't overflow on 32-bit platforms.
+ cacheSizeLimit = std::min(cacheSizeLimit, (uint64_t)SIZE_MAX);
+
+ #ifdef DEBUG
+ printf_stderr("Determined SkiaGL cache limits: Size %" PRIu64 ", Items: %i\n", cacheSizeLimit, cacheItemLimit);
+ #endif
+
+ mSkiaGlue->GetGrContext()->setResourceCacheLimits(cacheItemLimit, (size_t)cacheSizeLimit);
+#endif
+ }
+}
+
+SkiaGLGlue*
+gfxPlatform::GetSkiaGLGlue()
+{
+#ifdef USE_SKIA_GPU
+ // Check the accelerated Canvas is enabled for the first time,
+ // because the callers should check it before using.
+ if (!mSkiaGlue && !AllowOpenGLCanvas()) {
+ return nullptr;
+ }
+
+ if (!mSkiaGlue) {
+ /* Dummy context. We always draw into a FBO.
+ *
+ * FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it
+ * stands, this only works on the main thread.
+ */
+ RefPtr<GLContext> glContext;
+ nsCString discardFailureId;
+ glContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE |
+ CreateContextFlags::ALLOW_OFFLINE_RENDERER,
+ &discardFailureId);
+ if (!glContext) {
+ printf_stderr("Failed to create GLContext for SkiaGL!\n");
+ return nullptr;
+ }
+ mSkiaGlue = new SkiaGLGlue(glContext);
+ MOZ_ASSERT(mSkiaGlue->GetGrContext(), "No GrContext");
+ InitializeSkiaCacheLimits();
+ }
+#endif
+
+ return mSkiaGlue;
+}
+
+void
+gfxPlatform::PurgeSkiaFontCache()
+{
+#ifdef USE_SKIA
+ if (gfxPlatform::GetPlatform()->GetDefaultContentBackend() == BackendType::SKIA) {
+ SkGraphics::PurgeFontCache();
+ }
+#endif
+}
+
+void
+gfxPlatform::PurgeSkiaGPUCache()
+{
+#ifdef USE_SKIA_GPU
+ if (!mSkiaGlue)
+ return;
+
+ mSkiaGlue->GetGrContext()->freeGpuResources();
+ // GrContext::flush() doesn't call glFlush. Call it here.
+ mSkiaGlue->GetGLContext()->MakeCurrent();
+ mSkiaGlue->GetGLContext()->fFlush();
+#endif
+}
+
+bool
+gfxPlatform::HasEnoughTotalSystemMemoryForSkiaGL()
+{
+ return true;
+}
+
+already_AddRefed<DrawTarget>
+gfxPlatform::CreateDrawTargetForBackend(BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat)
+{
+ // There is a bunch of knowledge in the gfxPlatform heirarchy about how to
+ // create the best offscreen surface for the current system and situation. We
+ // can easily take advantage of this for the Cairo backend, so that's what we
+ // do.
+ // mozilla::gfx::Factory can get away without having all this knowledge for
+ // now, but this might need to change in the future (using
+ // CreateOffscreenSurface() and CreateDrawTargetForSurface() for all
+ // backends).
+ if (aBackend == BackendType::CAIRO) {
+ RefPtr<gfxASurface> surf = CreateOffscreenSurface(aSize, SurfaceFormatToImageFormat(aFormat));
+ if (!surf || surf->CairoStatus()) {
+ return nullptr;
+ }
+
+ return CreateDrawTargetForSurface(surf, aSize);
+ } else {
+ return Factory::CreateDrawTarget(aBackend, aSize, aFormat);
+ }
+}
+
+already_AddRefed<DrawTarget>
+gfxPlatform::CreateOffscreenCanvasDrawTarget(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ NS_ASSERTION(mPreferredCanvasBackend != BackendType::NONE, "No backend.");
+ RefPtr<DrawTarget> target = CreateDrawTargetForBackend(mPreferredCanvasBackend, aSize, aFormat);
+ if (target ||
+ mFallbackCanvasBackend == BackendType::NONE) {
+ return target.forget();
+ }
+
+#ifdef XP_WIN
+ // On Windows, the fallback backend (Cairo) should use its image backend.
+ return Factory::CreateDrawTarget(mFallbackCanvasBackend, aSize, aFormat);
+#else
+ return CreateDrawTargetForBackend(mFallbackCanvasBackend, aSize, aFormat);
+#endif
+}
+
+already_AddRefed<DrawTarget>
+gfxPlatform::CreateOffscreenContentDrawTarget(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ NS_ASSERTION(mPreferredCanvasBackend != BackendType::NONE, "No backend.");
+ return CreateDrawTargetForBackend(mContentBackend, aSize, aFormat);
+}
+
+already_AddRefed<DrawTarget>
+gfxPlatform::CreateSimilarSoftwareDrawTarget(DrawTarget* aDT,
+ const IntSize& aSize,
+ SurfaceFormat aFormat)
+{
+ RefPtr<DrawTarget> dt;
+
+ if (Factory::DoesBackendSupportDataDrawtarget(aDT->GetBackendType())) {
+ dt = aDT->CreateSimilarDrawTarget(aSize, aFormat);
+ } else {
+ dt = Factory::CreateDrawTarget(BackendType::SKIA, aSize, aFormat);
+ }
+
+ return dt.forget();
+}
+
+/* static */ already_AddRefed<DrawTarget>
+gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat)
+{
+ BackendType backendType = gfxVars::ContentBackend();
+ NS_ASSERTION(backendType != BackendType::NONE, "No backend.");
+
+ if (!Factory::DoesBackendSupportDataDrawtarget(backendType)) {
+ backendType = BackendType::CAIRO;
+ }
+
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(backendType,
+ aData, aSize,
+ aStride, aFormat);
+
+ return dt.forget();
+}
+
+/* static */ BackendType
+gfxPlatform::BackendTypeForName(const nsCString& aName)
+{
+ if (aName.EqualsLiteral("cairo"))
+ return BackendType::CAIRO;
+ if (aName.EqualsLiteral("skia"))
+ return BackendType::SKIA;
+ if (aName.EqualsLiteral("direct2d"))
+ return BackendType::DIRECT2D;
+ if (aName.EqualsLiteral("direct2d1.1"))
+ return BackendType::DIRECT2D1_1;
+ return BackendType::NONE;
+}
+
+nsresult
+gfxPlatform::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
+ aGenericFamily,
+ aListOfFonts);
+ return NS_OK;
+}
+
+nsresult
+gfxPlatform::UpdateFontList()
+{
+ gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+ return NS_OK;
+}
+
+nsresult
+gfxPlatform::GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName)
+{
+ gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName,
+ aFamilyName);
+ return NS_OK;
+}
+
+bool
+gfxPlatform::DownloadableFontsEnabled()
+{
+ if (mAllowDownloadableFonts == UNINITIALIZED_VALUE) {
+ mAllowDownloadableFonts =
+ Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_ENABLED, false);
+ }
+
+ return mAllowDownloadableFonts;
+}
+
+bool
+gfxPlatform::UseCmapsDuringSystemFallback()
+{
+ if (mFallbackUsesCmaps == UNINITIALIZED_VALUE) {
+ mFallbackUsesCmaps =
+ Preferences::GetBool(GFX_PREF_FALLBACK_USE_CMAPS, false);
+ }
+
+ return mFallbackUsesCmaps;
+}
+
+bool
+gfxPlatform::OpenTypeSVGEnabled()
+{
+ if (mOpenTypeSVGEnabled == UNINITIALIZED_VALUE) {
+ mOpenTypeSVGEnabled =
+ Preferences::GetBool(GFX_PREF_OPENTYPE_SVG, false);
+ }
+
+ return mOpenTypeSVGEnabled > 0;
+}
+
+uint32_t
+gfxPlatform::WordCacheCharLimit()
+{
+ if (mWordCacheCharLimit == UNINITIALIZED_VALUE) {
+ mWordCacheCharLimit =
+ Preferences::GetInt(GFX_PREF_WORD_CACHE_CHARLIMIT, 32);
+ if (mWordCacheCharLimit < 0) {
+ mWordCacheCharLimit = 32;
+ }
+ }
+
+ return uint32_t(mWordCacheCharLimit);
+}
+
+uint32_t
+gfxPlatform::WordCacheMaxEntries()
+{
+ if (mWordCacheMaxEntries == UNINITIALIZED_VALUE) {
+ mWordCacheMaxEntries =
+ Preferences::GetInt(GFX_PREF_WORD_CACHE_MAXENTRIES, 10000);
+ if (mWordCacheMaxEntries < 0) {
+ mWordCacheMaxEntries = 10000;
+ }
+ }
+
+ return uint32_t(mWordCacheMaxEntries);
+}
+
+bool
+gfxPlatform::UseGraphiteShaping()
+{
+ if (mGraphiteShapingEnabled == UNINITIALIZED_VALUE) {
+ mGraphiteShapingEnabled =
+ Preferences::GetBool(GFX_PREF_GRAPHITE_SHAPING, false);
+ }
+
+ return mGraphiteShapingEnabled;
+}
+
+gfxFontEntry*
+gfxPlatform::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
+ aWeight,
+ aStretch,
+ aStyle);
+}
+
+gfxFontEntry*
+gfxPlatform::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
+ aWeight,
+ aStretch,
+ aStyle,
+ aFontData,
+ aLength);
+}
+
+mozilla::layers::DiagnosticTypes
+gfxPlatform::GetLayerDiagnosticTypes()
+{
+ mozilla::layers::DiagnosticTypes type = DiagnosticTypes::NO_DIAGNOSTIC;
+ if (gfxPrefs::DrawLayerBorders()) {
+ type |= mozilla::layers::DiagnosticTypes::LAYER_BORDERS;
+ }
+ if (gfxPrefs::DrawTileBorders()) {
+ type |= mozilla::layers::DiagnosticTypes::TILE_BORDERS;
+ }
+ if (gfxPrefs::DrawBigImageBorders()) {
+ type |= mozilla::layers::DiagnosticTypes::BIGIMAGE_BORDERS;
+ }
+ if (gfxPrefs::FlashLayerBorders()) {
+ type |= mozilla::layers::DiagnosticTypes::FLASH_BORDERS;
+ }
+ return type;
+}
+
+void
+gfxPlatform::InitBackendPrefs(uint32_t aCanvasBitmask, BackendType aCanvasDefault,
+ uint32_t aContentBitmask, BackendType aContentDefault)
+{
+ mPreferredCanvasBackend = GetCanvasBackendPref(aCanvasBitmask);
+ if (mPreferredCanvasBackend == BackendType::NONE) {
+ mPreferredCanvasBackend = aCanvasDefault;
+ }
+
+ if (mPreferredCanvasBackend == BackendType::DIRECT2D1_1) {
+ // Falling back to D2D 1.0 won't help us here. When D2D 1.1 DT creation
+ // fails it means the surface was too big or there's something wrong with
+ // the device. D2D 1.0 will encounter a similar situation.
+ mFallbackCanvasBackend =
+ GetCanvasBackendPref(aCanvasBitmask &
+ ~(BackendTypeBit(mPreferredCanvasBackend) | BackendTypeBit(BackendType::DIRECT2D)));
+ } else {
+ mFallbackCanvasBackend =
+ GetCanvasBackendPref(aCanvasBitmask & ~BackendTypeBit(mPreferredCanvasBackend));
+ }
+
+ mContentBackendBitmask = aContentBitmask;
+ mContentBackend = GetContentBackendPref(mContentBackendBitmask);
+ if (mContentBackend == BackendType::NONE) {
+ mContentBackend = aContentDefault;
+ // mContentBackendBitmask is our canonical reference for supported
+ // backends so we need to add the default if we are using it and
+ // overriding the prefs.
+ mContentBackendBitmask |= BackendTypeBit(aContentDefault);
+ }
+
+ if (XRE_IsParentProcess()) {
+ gfxVars::SetContentBackend(mContentBackend);
+ }
+}
+
+/* static */ BackendType
+gfxPlatform::GetCanvasBackendPref(uint32_t aBackendBitmask)
+{
+ return GetBackendPref("gfx.canvas.azure.backends", aBackendBitmask);
+}
+
+/* static */ BackendType
+gfxPlatform::GetContentBackendPref(uint32_t &aBackendBitmask)
+{
+ return GetBackendPref("gfx.content.azure.backends", aBackendBitmask);
+}
+
+/* static */ BackendType
+gfxPlatform::GetBackendPref(const char* aBackendPrefName, uint32_t &aBackendBitmask)
+{
+ nsTArray<nsCString> backendList;
+ nsCString prefString;
+ if (NS_SUCCEEDED(Preferences::GetCString(aBackendPrefName, &prefString))) {
+ ParseString(prefString, ',', backendList);
+ }
+
+ uint32_t allowedBackends = 0;
+ BackendType result = BackendType::NONE;
+ for (uint32_t i = 0; i < backendList.Length(); ++i) {
+ BackendType type = BackendTypeForName(backendList[i]);
+ if (BackendTypeBit(type) & aBackendBitmask) {
+ allowedBackends |= BackendTypeBit(type);
+ if (result == BackendType::NONE) {
+ result = type;
+ }
+ }
+ }
+
+ aBackendBitmask = allowedBackends;
+ return result;
+}
+
+bool
+gfxPlatform::InSafeMode()
+{
+ static bool sSafeModeInitialized = false;
+ static bool sInSafeMode = false;
+
+ if (!sSafeModeInitialized) {
+ sSafeModeInitialized = true;
+ nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
+ if (xr) {
+ xr->GetInSafeMode(&sInSafeMode);
+ }
+ }
+ return sInSafeMode;
+}
+
+bool
+gfxPlatform::OffMainThreadCompositingEnabled()
+{
+ return UsesOffMainThreadCompositing();
+}
+
+eCMSMode
+gfxPlatform::GetCMSMode()
+{
+ if (!gCMSInitialized) {
+ int32_t mode = gfxPrefs::CMSMode();
+ if (mode >= 0 && mode < eCMSMode_AllCount) {
+ gCMSMode = static_cast<eCMSMode>(mode);
+ }
+
+ bool enableV4 = gfxPrefs::CMSEnableV4();
+ if (enableV4) {
+ qcms_enable_iccv4();
+ }
+ gCMSInitialized = true;
+ }
+ return gCMSMode;
+}
+
+int
+gfxPlatform::GetRenderingIntent()
+{
+ // gfxPrefs.h is using 0 as the default for the rendering
+ // intent preference, based on that being the value for
+ // QCMS_INTENT_DEFAULT. Assert here to catch if that ever
+ // changes and we can then figure out what to do about it.
+ MOZ_ASSERT(QCMS_INTENT_DEFAULT == 0);
+
+ /* Try to query the pref system for a rendering intent. */
+ int32_t pIntent = gfxPrefs::CMSRenderingIntent();
+ if ((pIntent < QCMS_INTENT_MIN) || (pIntent > QCMS_INTENT_MAX)) {
+ /* If the pref is out of range, use embedded profile. */
+ pIntent = -1;
+ }
+ return pIntent;
+}
+
+void
+gfxPlatform::TransformPixel(const Color& in, Color& out, qcms_transform *transform)
+{
+
+ if (transform) {
+ /* we want the bytes in RGB order */
+#ifdef IS_LITTLE_ENDIAN
+ /* ABGR puts the bytes in |RGBA| order on little endian */
+ uint32_t packed = in.ToABGR();
+ qcms_transform_data(transform,
+ (uint8_t *)&packed, (uint8_t *)&packed,
+ 1);
+ out = Color::FromABGR(packed);
+#else
+ /* ARGB puts the bytes in |ARGB| order on big endian */
+ uint32_t packed = in.UnusualToARGB();
+ /* add one to move past the alpha byte */
+ qcms_transform_data(transform,
+ (uint8_t *)&packed + 1, (uint8_t *)&packed + 1,
+ 1);
+ out = Color::UnusualFromARGB(packed);
+#endif
+ }
+
+ else if (&out != &in)
+ out = in;
+}
+
+void
+gfxPlatform::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
+{
+ mem = nullptr;
+ size = 0;
+}
+
+void
+gfxPlatform::GetCMSOutputProfileData(void *&mem, size_t &size)
+{
+ nsAdoptingCString fname = Preferences::GetCString("gfx.color_management.display_profile");
+ if (!fname.IsEmpty()) {
+ qcms_data_from_path(fname, &mem, &size);
+ }
+ else {
+ gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfile(mem, size);
+ }
+}
+
+void
+gfxPlatform::CreateCMSOutputProfile()
+{
+ if (!gCMSOutputProfile) {
+ /* Determine if we're using the internal override to force sRGB as
+ an output profile for reftests. See Bug 452125.
+
+ Note that we don't normally (outside of tests) set a
+ default value of this preference, which means nsIPrefBranch::GetBoolPref
+ will typically throw (and leave its out-param untouched).
+ */
+ if (Preferences::GetBool(GFX_PREF_CMS_FORCE_SRGB, false)) {
+ gCMSOutputProfile = GetCMSsRGBProfile();
+ }
+
+ if (!gCMSOutputProfile) {
+ void* mem = nullptr;
+ size_t size = 0;
+
+ GetCMSOutputProfileData(mem, size);
+ if ((mem != nullptr) && (size > 0)) {
+ gCMSOutputProfile = qcms_profile_from_memory(mem, size);
+ free(mem);
+ }
+ }
+
+ /* Determine if the profile looks bogus. If so, close the profile
+ * and use sRGB instead. See bug 460629, */
+ if (gCMSOutputProfile && qcms_profile_is_bogus(gCMSOutputProfile)) {
+ NS_ASSERTION(gCMSOutputProfile != GetCMSsRGBProfile(),
+ "Builtin sRGB profile tagged as bogus!!!");
+ qcms_profile_release(gCMSOutputProfile);
+ gCMSOutputProfile = nullptr;
+ }
+
+ if (!gCMSOutputProfile) {
+ gCMSOutputProfile = GetCMSsRGBProfile();
+ }
+ /* Precache the LUT16 Interpolations for the output profile. See
+ bug 444661 for details. */
+ qcms_profile_precache_output_transform(gCMSOutputProfile);
+ }
+}
+
+qcms_profile *
+gfxPlatform::GetCMSOutputProfile()
+{
+ return gCMSOutputProfile;
+}
+
+qcms_profile *
+gfxPlatform::GetCMSsRGBProfile()
+{
+ if (!gCMSsRGBProfile) {
+
+ /* Create the profile using qcms. */
+ gCMSsRGBProfile = qcms_profile_sRGB();
+ }
+ return gCMSsRGBProfile;
+}
+
+qcms_transform *
+gfxPlatform::GetCMSRGBTransform()
+{
+ if (!gCMSRGBTransform && !gCMSRGBTransformFailed) {
+ qcms_profile *inProfile, *outProfile;
+ outProfile = GetCMSOutputProfile();
+ inProfile = GetCMSsRGBProfile();
+
+ if (!inProfile || !outProfile)
+ return nullptr;
+
+ gCMSRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8,
+ outProfile, QCMS_DATA_RGB_8,
+ QCMS_INTENT_PERCEPTUAL);
+ if (!gCMSRGBTransform) {
+ gCMSRGBTransformFailed = true;
+ }
+ }
+
+ return gCMSRGBTransform;
+}
+
+qcms_transform *
+gfxPlatform::GetCMSInverseRGBTransform()
+{
+ if (!gCMSInverseRGBTransform) {
+ qcms_profile *inProfile, *outProfile;
+ inProfile = GetCMSOutputProfile();
+ outProfile = GetCMSsRGBProfile();
+
+ if (!inProfile || !outProfile)
+ return nullptr;
+
+ gCMSInverseRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8,
+ outProfile, QCMS_DATA_RGB_8,
+ QCMS_INTENT_PERCEPTUAL);
+ }
+
+ return gCMSInverseRGBTransform;
+}
+
+qcms_transform *
+gfxPlatform::GetCMSRGBATransform()
+{
+ if (!gCMSRGBATransform) {
+ qcms_profile *inProfile, *outProfile;
+ outProfile = GetCMSOutputProfile();
+ inProfile = GetCMSsRGBProfile();
+
+ if (!inProfile || !outProfile)
+ return nullptr;
+
+ gCMSRGBATransform = qcms_transform_create(inProfile, QCMS_DATA_RGBA_8,
+ outProfile, QCMS_DATA_RGBA_8,
+ QCMS_INTENT_PERCEPTUAL);
+ }
+
+ return gCMSRGBATransform;
+}
+
+/* Shuts down various transforms and profiles for CMS. */
+static void ShutdownCMS()
+{
+
+ if (gCMSRGBTransform) {
+ qcms_transform_release(gCMSRGBTransform);
+ gCMSRGBTransform = nullptr;
+ }
+ if (gCMSInverseRGBTransform) {
+ qcms_transform_release(gCMSInverseRGBTransform);
+ gCMSInverseRGBTransform = nullptr;
+ }
+ if (gCMSRGBATransform) {
+ qcms_transform_release(gCMSRGBATransform);
+ gCMSRGBATransform = nullptr;
+ }
+ if (gCMSOutputProfile) {
+ qcms_profile_release(gCMSOutputProfile);
+
+ // handle the aliased case
+ if (gCMSsRGBProfile == gCMSOutputProfile)
+ gCMSsRGBProfile = nullptr;
+ gCMSOutputProfile = nullptr;
+ }
+ if (gCMSsRGBProfile) {
+ qcms_profile_release(gCMSsRGBProfile);
+ gCMSsRGBProfile = nullptr;
+ }
+
+ // Reset the state variables
+ gCMSMode = eCMSMode_Off;
+ gCMSInitialized = false;
+}
+
+// default SetupClusterBoundaries, based on Unicode properties;
+// platform subclasses may override if they wish
+void
+gfxPlatform::SetupClusterBoundaries(gfxTextRun *aTextRun, const char16_t *aString)
+{
+ if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
+ // 8-bit text doesn't have clusters.
+ // XXX is this true in all languages???
+ // behdad: don't think so. Czech for example IIRC has a
+ // 'ch' grapheme.
+ // jfkthame: but that's not expected to behave as a grapheme cluster
+ // for selection/editing/etc.
+ return;
+ }
+
+ aTextRun->SetupClusterBoundaries(0, aString, aTextRun->GetLength());
+}
+
+int32_t
+gfxPlatform::GetBidiNumeralOption()
+{
+ if (mBidiNumeralOption == UNINITIALIZED_VALUE) {
+ mBidiNumeralOption = Preferences::GetInt(BIDI_NUMERAL_PREF, 0);
+ }
+ return mBidiNumeralOption;
+}
+
+/* static */ void
+gfxPlatform::FlushFontAndWordCaches()
+{
+ gfxFontCache *fontCache = gfxFontCache::GetCache();
+ if (fontCache) {
+ fontCache->AgeAllGenerations();
+ fontCache->FlushShapedWordCaches();
+ }
+
+ gfxPlatform::PurgeSkiaFontCache();
+}
+
+void
+gfxPlatform::FontsPrefsChanged(const char *aPref)
+{
+ NS_ASSERTION(aPref != nullptr, "null preference");
+ if (!strcmp(GFX_DOWNLOADABLE_FONTS_ENABLED, aPref)) {
+ mAllowDownloadableFonts = UNINITIALIZED_VALUE;
+ } else if (!strcmp(GFX_PREF_FALLBACK_USE_CMAPS, aPref)) {
+ mFallbackUsesCmaps = UNINITIALIZED_VALUE;
+ } else if (!strcmp(GFX_PREF_WORD_CACHE_CHARLIMIT, aPref)) {
+ mWordCacheCharLimit = UNINITIALIZED_VALUE;
+ FlushFontAndWordCaches();
+ } else if (!strcmp(GFX_PREF_WORD_CACHE_MAXENTRIES, aPref)) {
+ mWordCacheMaxEntries = UNINITIALIZED_VALUE;
+ FlushFontAndWordCaches();
+ } else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) {
+ mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
+ FlushFontAndWordCaches();
+ } else if (!strcmp(BIDI_NUMERAL_PREF, aPref)) {
+ mBidiNumeralOption = UNINITIALIZED_VALUE;
+ } else if (!strcmp(GFX_PREF_OPENTYPE_SVG, aPref)) {
+ mOpenTypeSVGEnabled = UNINITIALIZED_VALUE;
+ gfxFontCache::GetCache()->AgeAllGenerations();
+ }
+}
+
+
+mozilla::LogModule*
+gfxPlatform::GetLog(eGfxLog aWhichLog)
+{
+ // logs shared across gfx
+ static LazyLogModule sFontlistLog("fontlist");
+ static LazyLogModule sFontInitLog("fontinit");
+ static LazyLogModule sTextrunLog("textrun");
+ static LazyLogModule sTextrunuiLog("textrunui");
+ static LazyLogModule sCmapDataLog("cmapdata");
+ static LazyLogModule sTextPerfLog("textperf");
+
+ switch (aWhichLog) {
+ case eGfxLog_fontlist:
+ return sFontlistLog;
+ case eGfxLog_fontinit:
+ return sFontInitLog;
+ case eGfxLog_textrun:
+ return sTextrunLog;
+ case eGfxLog_textrunui:
+ return sTextrunuiLog;
+ case eGfxLog_cmapdata:
+ return sCmapDataLog;
+ case eGfxLog_textperf:
+ return sTextPerfLog;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unexpected log type");
+ return nullptr;
+}
+
+mozilla::gfx::SurfaceFormat
+gfxPlatform::Optimal2DFormatForContent(gfxContentType aContent)
+{
+ switch (aContent) {
+ case gfxContentType::COLOR:
+ switch (GetOffscreenFormat()) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return mozilla::gfx::SurfaceFormat::B8G8R8A8;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return mozilla::gfx::SurfaceFormat::B8G8R8X8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return mozilla::gfx::SurfaceFormat::R5G6B5_UINT16;
+ default:
+ NS_NOTREACHED("unknown gfxImageFormat for gfxContentType::COLOR");
+ return mozilla::gfx::SurfaceFormat::B8G8R8A8;
+ }
+ case gfxContentType::ALPHA:
+ return mozilla::gfx::SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return mozilla::gfx::SurfaceFormat::B8G8R8A8;
+ default:
+ NS_NOTREACHED("unknown gfxContentType");
+ return mozilla::gfx::SurfaceFormat::B8G8R8A8;
+ }
+}
+
+gfxImageFormat
+gfxPlatform::OptimalFormatForContent(gfxContentType aContent)
+{
+ switch (aContent) {
+ case gfxContentType::COLOR:
+ return GetOffscreenFormat();
+ case gfxContentType::ALPHA:
+ return SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ default:
+ NS_NOTREACHED("unknown gfxContentType");
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ }
+}
+
+/**
+ * There are a number of layers acceleration (or layers in general) preferences
+ * that should be consistent for the lifetime of the application (bug 840967).
+ * As such, we will evaluate them all as soon as one of them is evaluated
+ * and remember the values. Changing these preferences during the run will
+ * not have any effect until we restart.
+ */
+static mozilla::Atomic<bool> sLayersSupportsHardwareVideoDecoding(false);
+static bool sLayersHardwareVideoDecodingFailed = false;
+static bool sBufferRotationCheckPref = true;
+
+static mozilla::Atomic<bool> sLayersAccelerationPrefsInitialized(false);
+
+void VideoDecodingFailedChangedCallback(const char* aPref, void*)
+{
+ sLayersHardwareVideoDecodingFailed = Preferences::GetBool(aPref, false);
+ gfxPlatform::GetPlatform()->UpdateCanUseHardwareVideoDecoding();
+}
+
+void
+gfxPlatform::UpdateCanUseHardwareVideoDecoding()
+{
+ if (XRE_IsParentProcess()) {
+ gfxVars::SetCanUseHardwareVideoDecoding(CanUseHardwareVideoDecoding());
+ }
+}
+
+void
+gfxPlatform::InitAcceleration()
+{
+ if (sLayersAccelerationPrefsInitialized) {
+ return;
+ }
+
+ InitCompositorAccelerationPrefs();
+
+ // If this is called for the first time on a non-main thread, we're screwed.
+ // At the moment there's no explicit guarantee that the main thread calls
+ // this before the compositor thread, but let's at least make the assumption
+ // explicit.
+ MOZ_ASSERT(NS_IsMainThread(), "can only initialize prefs on the main thread");
+
+ gfxPrefs::GetSingleton();
+
+ if (XRE_IsParentProcess()) {
+ gfxVars::SetBrowserTabsRemoteAutostart(BrowserTabsRemoteAutostart());
+ gfxVars::SetOffscreenFormat(GetOffscreenFormat());
+ gfxVars::SetRequiresAcceleratedGLContextForCompositorOGL(
+ RequiresAcceleratedGLContextForCompositorOGL());
+ }
+
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ nsCString discardFailureId;
+ int32_t status;
+
+ if (Preferences::GetBool("media.hardware-video-decoding.enabled", false) &&
+#ifdef XP_WIN
+ Preferences::GetBool("media.windows-media-foundation.use-dxva", true) &&
+#endif
+ NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ discardFailureId, &status))) {
+ if (status == nsIGfxInfo::FEATURE_STATUS_OK || gfxPrefs::HardwareVideoDecodingForceEnabled()) {
+ sLayersSupportsHardwareVideoDecoding = true;
+ }
+ }
+
+ sLayersAccelerationPrefsInitialized = true;
+
+ if (XRE_IsParentProcess()) {
+ Preferences::RegisterCallbackAndCall(VideoDecodingFailedChangedCallback,
+ "media.hardware-video-decoding.failed",
+ nullptr,
+ Preferences::ExactMatch);
+ InitGPUProcessPrefs();
+ }
+}
+
+void
+gfxPlatform::InitGPUProcessPrefs()
+{
+ // We want to hide this from about:support, so only set a default if the
+ // pref is known to be true.
+ if (!gfxPrefs::GPUProcessDevEnabled() && !gfxPrefs::GPUProcessDevForceEnabled()) {
+ return;
+ }
+
+ FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
+
+ gpuProc.SetDefaultFromPref(
+ gfxPrefs::GetGPUProcessDevEnabledPrefName(),
+ true,
+ gfxPrefs::GetGPUProcessDevEnabledPrefDefault());
+
+ if (gfxPrefs::GPUProcessDevForceEnabled()) {
+ gpuProc.UserForceEnable("User force-enabled via pref");
+ }
+
+ // We require E10S - otherwise, there is very little benefit to the GPU
+ // process, since the UI process must still use acceleration for
+ // performance.
+ if (!BrowserTabsRemoteAutostart()) {
+ gpuProc.ForceDisable(
+ FeatureStatus::Unavailable,
+ "Multi-process mode is not enabled",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_E10S"));
+ return;
+ }
+ if (InSafeMode()) {
+ gpuProc.ForceDisable(
+ FeatureStatus::Blocked,
+ "Safe-mode is enabled",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_SAFE_MODE"));
+ return;
+ }
+ if (gfxPrefs::LayerScopeEnabled()) {
+ gpuProc.ForceDisable(
+ FeatureStatus::Blocked,
+ "LayerScope does not work in the GPU process",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_LAYERSCOPE"));
+ return;
+ }
+}
+
+void
+gfxPlatform::InitCompositorAccelerationPrefs()
+{
+ const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
+
+ FeatureState& feature = gfxConfig::GetFeature(Feature::HW_COMPOSITING);
+
+ // Base value - does the platform allow acceleration?
+ if (feature.SetDefault(AccelerateLayersByDefault(),
+ FeatureStatus::Blocked,
+ "Acceleration blocked by platform"))
+ {
+ if (!gfxPrefs::LayersAccelerationEnabledDoNotUseDirectly()) {
+ feature.UserDisable("Disabled by pref",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_PREF"));
+ } else if (acceleratedEnv && *acceleratedEnv == '0') {
+ feature.UserDisable("Disabled by envvar",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_ENV"));
+ }
+ } else {
+ if (acceleratedEnv && *acceleratedEnv == '1') {
+ feature.UserEnable("Enabled by envvar");
+ }
+ }
+
+ // This has specific meaning elsewhere, so we always record it.
+ if (gfxPrefs::LayersAccelerationEnabledDoNotUseDirectly() &&
+ gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly()) {
+ feature.UserForceEnable("Force-enabled by prefs");
+ }
+
+ // Safe mode trumps everything.
+ if (InSafeMode()) {
+ feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by safe-mode",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
+ }
+}
+
+bool
+gfxPlatform::CanUseHardwareVideoDecoding()
+{
+ // this function is called from the compositor thread, so it is not
+ // safe to init the prefs etc. from here.
+ MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
+ return sLayersSupportsHardwareVideoDecoding && !sLayersHardwareVideoDecodingFailed;
+}
+
+bool
+gfxPlatform::AccelerateLayersByDefault()
+{
+ // Note: add any new platform defines here that should get HWA by default.
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_UIKIT)
+ return true;
+#elif defined(MOZ_GL_PROVIDER)
+ // GL provider manually declared
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+gfxPlatform::BufferRotationEnabled()
+{
+ MutexAutoLock autoLock(*gGfxPlatformPrefsLock);
+
+ return sBufferRotationCheckPref && gfxPrefs::BufferRotationEnabled();
+}
+
+void
+gfxPlatform::DisableBufferRotation()
+{
+ MutexAutoLock autoLock(*gGfxPlatformPrefsLock);
+
+ sBufferRotationCheckPref = false;
+}
+
+already_AddRefed<ScaledFont>
+gfxPlatform::GetScaledFontForFontWithCairoSkia(DrawTarget* aTarget, gfxFont* aFont)
+{
+ NativeFont nativeFont;
+ if (aTarget->GetBackendType() == BackendType::CAIRO || aTarget->GetBackendType() == BackendType::SKIA) {
+ nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
+ nativeFont.mFont = aFont->GetCairoScaledFont();
+ return Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
+ }
+
+ return nullptr;
+}
+
+/* static */ bool
+gfxPlatform::UsesOffMainThreadCompositing()
+{
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ return true;
+ }
+
+ static bool firstTime = true;
+ static bool result = false;
+
+ if (firstTime) {
+ MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
+ result =
+ gfxVars::BrowserTabsRemoteAutostart() ||
+ !gfxPrefs::LayersOffMainThreadCompositionForceDisabled();
+#if defined(MOZ_WIDGET_GTK)
+ // Linux users who chose OpenGL are being grandfathered in to OMTC
+ result |= gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly();
+
+#endif
+ firstTime = false;
+ }
+
+ return result;
+}
+
+/***
+ * The preference "layout.frame_rate" has 3 meanings depending on the value:
+ *
+ * -1 = Auto (default), use hardware vsync or software vsync @ 60 hz if hw vsync fails.
+ * 0 = ASAP mode - used during talos testing.
+ * X = Software vsync at a rate of X times per second.
+ */
+already_AddRefed<mozilla::gfx::VsyncSource>
+gfxPlatform::CreateHardwareVsyncSource()
+{
+ RefPtr<mozilla::gfx::VsyncSource> softwareVsync = new SoftwareVsyncSource();
+ return softwareVsync.forget();
+}
+
+/* static */ bool
+gfxPlatform::IsInLayoutAsapMode()
+{
+ // There are 2 modes of ASAP mode.
+ // 1 is that the refresh driver and compositor are in lock step
+ // the second is that the compositor goes ASAP and the refresh driver
+ // goes at whatever the configurated rate is. This only checks the version
+ // talos uses, which is the refresh driver and compositor are in lockstep.
+ return gfxPrefs::LayoutFrameRate() == 0;
+}
+
+/* static */ bool
+gfxPlatform::ForceSoftwareVsync()
+{
+ return gfxPrefs::LayoutFrameRate() > 0;
+}
+
+/* static */ int
+gfxPlatform::GetSoftwareVsyncRate()
+{
+ int preferenceRate = gfxPrefs::LayoutFrameRate();
+ if (preferenceRate <= 0) {
+ return gfxPlatform::GetDefaultFrameRate();
+ }
+ return preferenceRate;
+}
+
+/* static */ int
+gfxPlatform::GetDefaultFrameRate()
+{
+ return 60;
+}
+
+void
+gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj)
+{
+ if (!gfxPlatform::AsyncPanZoomEnabled()) {
+ return;
+ }
+
+ if (SupportsApzWheelInput()) {
+ aObj.DefineProperty("ApzWheelInput", 1);
+ }
+
+ if (SupportsApzTouchInput()) {
+ aObj.DefineProperty("ApzTouchInput", 1);
+ }
+
+ if (SupportsApzDragInput()) {
+ aObj.DefineProperty("ApzDragInput", 1);
+ }
+}
+
+void
+gfxPlatform::GetTilesSupportInfo(mozilla::widget::InfoObject& aObj)
+{
+ if (!gfxPrefs::LayersTilesEnabled()) {
+ return;
+ }
+
+ IntSize tileSize = gfxVars::TileSize();
+ aObj.DefineProperty("TileHeight", tileSize.height);
+ aObj.DefineProperty("TileWidth", tileSize.width);
+}
+
+/*static*/ bool
+gfxPlatform::AsyncPanZoomEnabled()
+{
+#if !defined(MOZ_WIDGET_UIKIT)
+ // For XUL applications (everything but Firefox on Android) we only want
+ // to use APZ when E10S is enabled or when the user explicitly enable it.
+ if (BrowserTabsRemoteAutostart() || gfxPrefs::APZDesktopEnabled()) {
+ return gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly();
+ } else {
+ return false;
+ }
+#else
+ return gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly();
+#endif
+}
+
+/*static*/ bool
+gfxPlatform::PerfWarnings()
+{
+ return gfxPrefs::PerfWarnings();
+}
+
+void
+gfxPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
+{
+ if (gfxConfig::IsEnabled(Feature::OPENGL_COMPOSITING)) {
+ aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
+ }
+ else {
+ static int tell_me_once = 0;
+ if (!tell_me_once) {
+ NS_WARNING("OpenGL-accelerated layers are not supported on this system");
+ tell_me_once = 1;
+ }
+ }
+}
+
+void
+gfxPlatform::GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends)
+{
+ if (useAcceleration) {
+ GetAcceleratedCompositorBackends(aBackends);
+ }
+ aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
+}
+
+void
+gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend)
+{
+ if (mCompositorBackend == aBackend) {
+ return;
+ }
+
+ if (mCompositorBackend != LayersBackend::LAYERS_NONE) {
+ gfxCriticalNote << "Compositors might be mixed ("
+ << int(mCompositorBackend) << "," << int(aBackend) << ")";
+ }
+
+ // Set the backend before we notify so it's available immediately.
+ mCompositorBackend = aBackend;
+}
+
+void
+gfxPlatform::FetchAndImportContentDeviceData()
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
+
+ mozilla::gfx::ContentDeviceData data;
+ cc->SendGetGraphicsDeviceInitData(&data);
+
+ ImportContentDeviceData(data);
+}
+
+void
+gfxPlatform::ImportContentDeviceData(const mozilla::gfx::ContentDeviceData& aData)
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ const DevicePrefs& prefs = aData.prefs();
+ gfxConfig::Inherit(Feature::HW_COMPOSITING, prefs.hwCompositing());
+ gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, prefs.oglCompositing());
+}
+
+void
+gfxPlatform::BuildContentDeviceData(mozilla::gfx::ContentDeviceData* aOut)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // Make sure our settings are synchronized from the GPU process.
+ GPUProcessManager::Get()->EnsureGPUReady();
+
+ aOut->prefs().hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING);
+ aOut->prefs().oglCompositing() = gfxConfig::GetValue(Feature::OPENGL_COMPOSITING);
+}
+
+void
+gfxPlatform::ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ gfxConfig::ImportChange(Feature::OPENGL_COMPOSITING, aData.oglCompositing());
+}
+
+bool
+gfxPlatform::SupportsApzDragInput() const
+{
+ return gfxPrefs::APZDragEnabled();
+}
+
+void
+gfxPlatform::BumpDeviceCounter()
+{
+ mDeviceCounter++;
+}
+
+void
+gfxPlatform::InitOpenGLConfig()
+{
+ #ifdef XP_WIN
+ // Don't enable by default on Windows, since it could show up in about:support even
+ // though it'll never get used. Only attempt if user enables the pref
+ if (!Preferences::GetBool("layers.prefer-opengl")){
+ return;
+ }
+ #endif
+
+ FeatureState& openGLFeature = gfxConfig::GetFeature(Feature::OPENGL_COMPOSITING);
+
+ // Check to see hw comp supported
+ if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+ openGLFeature.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_OPENGL_NEED_HWCOMP"));
+ return;
+ }
+
+ #ifdef XP_WIN
+ openGLFeature.SetDefaultFromPref(
+ gfxPrefs::GetLayersPreferOpenGLPrefName(),
+ true,
+ gfxPrefs::GetLayersPreferOpenGLPrefDefault());
+ #else
+ openGLFeature.EnableByDefault();
+ #endif
+
+ // When layers acceleration is force-enabled, enable it even for blacklisted
+ // devices.
+ if (gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly()) {
+ openGLFeature.UserForceEnable("Force-enabled by pref");
+ return;
+ }
+
+ nsCString message;
+ nsCString failureId;
+ if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &message, failureId)) {
+ openGLFeature.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
+ }
+}
+
+bool
+gfxPlatform::IsGfxInfoStatusOkay(int32_t aFeature, nsCString* aOutMessage, nsCString& aFailureId)
+{
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ if (!gfxInfo) {
+ return true;
+ }
+
+ int32_t status;
+ if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aFailureId, &status)) &&
+ status != nsIGfxInfo::FEATURE_STATUS_OK)
+ {
+ aOutMessage->AssignLiteral("#BLOCKLIST_");
+ aOutMessage->AppendASCII(aFailureId.get());
+ return false;
+ }
+
+ return true;
+}
diff --git a/system/graphics/thebes/gfxPlatform.h b/system/graphics/thebes/gfxPlatform.h
new file mode 100644
index 000000000..a4d5417a5
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatform.h
@@ -0,0 +1,844 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PLATFORM_H
+#define GFX_PLATFORM_H
+
+#include "mozilla/Logging.h"
+#include "mozilla/gfx/Types.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsUnicodeScriptCodes.h"
+
+#include "gfxTypes.h"
+#include "gfxFontFamilyList.h"
+#include "gfxBlur.h"
+#include "gfxSkipChars.h"
+#include "nsRect.h"
+
+#include "qcms.h"
+
+#include "mozilla/RefPtr.h"
+#include "GfxInfoCollector.h"
+
+#include "mozilla/layers/CompositorTypes.h"
+
+class gfxASurface;
+class gfxFont;
+class gfxFontGroup;
+struct gfxFontStyle;
+class gfxUserFontSet;
+class gfxFontEntry;
+class gfxPlatformFontList;
+class gfxTextRun;
+class nsIURI;
+class nsIAtom;
+class nsIObserver;
+class SRGBOverrideObserver;
+class gfxTextPerfMetrics;
+
+namespace mozilla {
+namespace gl {
+class SkiaGLGlue;
+} // namespace gl
+namespace gfx {
+class DrawTarget;
+class SourceSurface;
+class DataSourceSurface;
+class ScaledFont;
+class DrawEventRecorder;
+class VsyncSource;
+class ContentDeviceData;
+class GPUDeviceData;
+class FeatureState;
+
+inline uint32_t
+BackendTypeBit(BackendType b)
+{
+ return 1 << uint8_t(b);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#define MOZ_PERFORMANCE_WARNING(module, ...) \
+ do { \
+ if (gfxPlatform::PerfWarnings()) { \
+ printf_stderr("[" module "] " __VA_ARGS__); \
+ } \
+ } while (0)
+
+enum eCMSMode {
+ eCMSMode_Off = 0, // No color management
+ eCMSMode_All = 1, // Color manage everything
+ eCMSMode_TaggedOnly = 2, // Color manage tagged Images Only
+ eCMSMode_AllCount = 3
+};
+
+enum eGfxLog {
+ // all font enumerations, localized names, fullname/psnames, cmap loads
+ eGfxLog_fontlist = 0,
+ // timing info on font initialization
+ eGfxLog_fontinit = 1,
+ // dump text runs, font matching, system fallback for content
+ eGfxLog_textrun = 2,
+ // dump text runs, font matching, system fallback for chrome
+ eGfxLog_textrunui = 3,
+ // dump cmap coverage data as they are loaded
+ eGfxLog_cmapdata = 4,
+ // text perf data
+ eGfxLog_textperf = 5
+};
+
+// when searching through pref langs, max number of pref langs
+const uint32_t kMaxLenPrefLangList = 32;
+
+#define UNINITIALIZED_VALUE (-1)
+
+inline const char*
+GetBackendName(mozilla::gfx::BackendType aBackend)
+{
+ switch (aBackend) {
+ case mozilla::gfx::BackendType::DIRECT2D:
+ return "direct2d";
+ case mozilla::gfx::BackendType::CAIRO:
+ return "cairo";
+ case mozilla::gfx::BackendType::SKIA:
+ return "skia";
+ case mozilla::gfx::BackendType::RECORDING:
+ return "recording";
+ case mozilla::gfx::BackendType::DIRECT2D1_1:
+ return "direct2d 1.1";
+ case mozilla::gfx::BackendType::NONE:
+ return "none";
+ case mozilla::gfx::BackendType::BACKEND_LAST:
+ return "invalid";
+ }
+ MOZ_CRASH("Incomplete switch");
+}
+
+enum class DeviceResetReason
+{
+ OK = 0,
+ HUNG,
+ REMOVED,
+ RESET,
+ DRIVER_ERROR,
+ INVALID_CALL,
+ OUT_OF_MEMORY,
+ FORCED_RESET,
+ UNKNOWN,
+ D3D9_RESET
+};
+
+enum class ForcedDeviceResetReason
+{
+ OPENSHAREDHANDLE = 0,
+ COMPOSITOR_UPDATED,
+};
+
+class gfxPlatform {
+ friend class SRGBOverrideObserver;
+
+public:
+ typedef mozilla::gfx::Color Color;
+ typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SourceSurface SourceSurface;
+ typedef mozilla::unicode::Script Script;
+
+ /**
+ * Return a pointer to the current active platform.
+ * This is a singleton; it contains mostly convenience
+ * functions to obtain platform-specific objects.
+ */
+ static gfxPlatform *GetPlatform();
+
+ /**
+ * Returns whether or not graphics has been initialized yet.
+ */
+ static bool Initialized();
+
+ /**
+ * Shut down Thebes.
+ * Init() arranges for this to be called at an appropriate time.
+ */
+ static void Shutdown();
+
+ static void InitLayersIPC();
+ static void ShutdownLayersIPC();
+
+ /**
+ * Initialize ScrollMetadata statics. Does not depend on gfxPlatform.
+ */
+ static void InitNullMetadata();
+
+ static void InitMoz2DLogging();
+
+ /**
+ * Create an offscreen surface of the given dimensions
+ * and image format.
+ */
+ virtual already_AddRefed<gfxASurface>
+ CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat) = 0;
+
+ /**
+ * Beware that this method may return DrawTargets which are not fully supported
+ * on the current platform and might fail silently in subtle ways. This is a massive
+ * potential footgun. You should only use these methods for canvas drawing really.
+ * Use extreme caution if you use them for content where you are not 100% sure we
+ * support the DrawTarget we get back.
+ * See SupportsAzureContentForDrawTarget.
+ */
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetForSurface(gfxASurface *aSurface, const mozilla::gfx::IntSize& aSize);
+
+ /*
+ * Creates a SourceSurface for a gfxASurface. This function does no caching,
+ * so the caller should cache the gfxASurface if it will be used frequently.
+ * The returned surface keeps a reference to aTarget, so it is OK to keep the
+ * surface, even if aTarget changes.
+ * aTarget should not keep a reference to the returned surface because that
+ * will cause a cycle.
+ *
+ * This function is static so that it can be accessed from
+ * PluginInstanceChild (where we can't call gfxPlatform::GetPlatform()
+ * because the prefs service can only be accessed from the main process).
+ *
+ * aIsPlugin is used to tell the backend that they can optimize this surface
+ * specifically because it's used for a plugin. This is mostly for Skia.
+ */
+ static already_AddRefed<SourceSurface>
+ GetSourceSurfaceForSurface(mozilla::gfx::DrawTarget *aTarget,
+ gfxASurface *aSurface,
+ bool aIsPlugin = false);
+
+ static void ClearSourceSurfaceForSurface(gfxASurface *aSurface);
+
+ static already_AddRefed<DataSourceSurface>
+ GetWrappedDataSourceSurface(gfxASurface *aSurface);
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont);
+
+ already_AddRefed<DrawTarget>
+ CreateOffscreenContentDrawTarget(const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat);
+
+ already_AddRefed<DrawTarget>
+ CreateOffscreenCanvasDrawTarget(const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat);
+
+ already_AddRefed<DrawTarget>
+ CreateSimilarSoftwareDrawTarget(DrawTarget* aDT, const IntSize &aSize, mozilla::gfx::SurfaceFormat aFormat);
+
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetForData(unsigned char* aData, const mozilla::gfx::IntSize& aSize,
+ int32_t aStride, mozilla::gfx::SurfaceFormat aFormat);
+
+ /**
+ * Returns true if rendering to data surfaces produces the same results as
+ * rendering to offscreen surfaces on this platform, making it safe to
+ * render content to data surfaces. This is generally false on platforms
+ * which use different backends for each type of DrawTarget.
+ */
+ virtual bool CanRenderContentToDataSurface() const {
+ return false;
+ }
+
+ /**
+ * Returns true if we should use Azure to render content with aTarget. For
+ * example, it is possible that we are using Direct2D for rendering and thus
+ * using Azure. But we want to render to a CairoDrawTarget, in which case
+ * SupportsAzureContent will return true but SupportsAzureContentForDrawTarget
+ * will return false.
+ */
+ bool SupportsAzureContentForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
+
+ bool SupportsAzureContentForType(mozilla::gfx::BackendType aType) {
+ return BackendTypeBit(aType) & mContentBackendBitmask;
+ }
+
+ /// This function also lets us know if the current preferences/platform
+ /// combination allows for both accelerated and not accelerated canvas
+ /// implementations. If it does, and other relevant preferences are
+ /// asking for it, we will examine the commands in the first few seconds
+ /// of the canvas usage, and potentially change to accelerated or
+ /// non-accelerated canvas.
+ bool AllowOpenGLCanvas();
+ virtual void InitializeSkiaCacheLimits();
+
+ static bool AsyncPanZoomEnabled();
+
+ virtual void GetAzureBackendInfo(mozilla::widget::InfoObject &aObj) {
+ aObj.DefineProperty("AzureCanvasBackend", GetBackendName(mPreferredCanvasBackend));
+ aObj.DefineProperty("AzureCanvasAccelerated", AllowOpenGLCanvas());
+ aObj.DefineProperty("AzureFallbackCanvasBackend", GetBackendName(mFallbackCanvasBackend));
+ aObj.DefineProperty("AzureContentBackend", GetBackendName(mContentBackend));
+ }
+ void GetApzSupportInfo(mozilla::widget::InfoObject& aObj);
+ void GetTilesSupportInfo(mozilla::widget::InfoObject& aObj);
+
+ // Get the default content backend that will be used with the default
+ // compositor. If the compositor is known when calling this function,
+ // GetContentBackendFor() should be called instead.
+ mozilla::gfx::BackendType GetDefaultContentBackend() {
+ return mContentBackend;
+ }
+
+ // Return the best content backend available that is compatible with the
+ // given layers backend.
+ virtual mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) {
+ return mContentBackend;
+ }
+
+ mozilla::gfx::BackendType GetPreferredCanvasBackend() {
+ return mPreferredCanvasBackend;
+ }
+ mozilla::gfx::BackendType GetFallbackCanvasBackend() {
+ return mFallbackCanvasBackend;
+ }
+ /*
+ * Font bits
+ */
+
+ virtual void SetupClusterBoundaries(gfxTextRun *aTextRun, const char16_t *aString);
+
+ /**
+ * Fill aListOfFonts with the results of querying the list of font names
+ * that correspond to the given language group or generic font family
+ * (or both, or neither).
+ */
+ virtual nsresult GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts);
+
+ /**
+ * Rebuilds the any cached system font lists
+ */
+ virtual nsresult UpdateFontList();
+
+ /**
+ * Create the platform font-list object (gfxPlatformFontList concrete subclass).
+ * This function is responsible to create the appropriate subclass of
+ * gfxPlatformFontList *and* to call its InitFontList() method.
+ */
+ virtual gfxPlatformFontList *CreatePlatformFontList() {
+ NS_NOTREACHED("oops, this platform doesn't have a gfxPlatformFontList implementation");
+ return nullptr;
+ }
+
+ /**
+ * Resolving a font name to family name. The result MUST be in the result of GetFontList().
+ * If the name doesn't in the system, aFamilyName will be empty string, but not failed.
+ */
+ virtual nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
+ /**
+ * Create the appropriate platform font group
+ */
+ virtual gfxFontGroup*
+ CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize) = 0;
+
+ /**
+ * Look up a local platform font using the full font face name.
+ * (Needed to support @font-face src local().)
+ * Ownership of the returned gfxFontEntry is passed to the caller,
+ * who must either AddRef() or delete.
+ */
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle);
+
+ /**
+ * Activate a platform font. (Needed to support @font-face src url().)
+ * aFontData is a NS_Malloc'ed block that must be freed by this function
+ * (or responsibility passed on) when it is no longer needed; the caller
+ * will NOT free it.
+ * Ownership of the returned gfxFontEntry is passed to the caller,
+ * who must either AddRef() or delete.
+ */
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength);
+
+ /**
+ * Whether to allow downloadable fonts via @font-face rules
+ */
+ bool DownloadableFontsEnabled();
+
+ /**
+ * True when hinting should be enabled. This setting shouldn't
+ * change per gecko process, while the process is live. If so the
+ * results are not defined.
+ *
+ * NB: this bit is only honored by the FT2 backend, currently.
+ */
+ virtual bool FontHintingEnabled() { return true; }
+
+ /**
+ * True when zooming should not require reflow, so glyph metrics and
+ * positioning should not be adjusted for device pixels.
+ * If this is TRUE, then FontHintingEnabled() should be FALSE,
+ * but the converse is not necessarily required; in particular,
+ * B2G always has FontHintingEnabled FALSE, but RequiresLinearZoom
+ * is only true for the browser process, not Gaia or other apps.
+ *
+ * Like FontHintingEnabled (above), this setting shouldn't
+ * change per gecko process, while the process is live. If so the
+ * results are not defined.
+ *
+ * NB: this bit is only honored by the FT2 backend, currently.
+ */
+ virtual bool RequiresLinearZoom() { return false; }
+
+ /**
+ * Whether the frame->StyleFont().mFont.smoothing field is respected by
+ * text rendering on this platform.
+ */
+ virtual bool RespectsFontStyleSmoothing() const { return false; }
+
+ /**
+ * Whether to check all font cmaps during system font fallback
+ */
+ bool UseCmapsDuringSystemFallback();
+
+ /**
+ * Whether to render SVG glyphs within an OpenType font wrapper
+ */
+ bool OpenTypeSVGEnabled();
+
+ /**
+ * Max character length of words in the word cache
+ */
+ uint32_t WordCacheCharLimit();
+
+ /**
+ * Max number of entries in word cache
+ */
+ uint32_t WordCacheMaxEntries();
+
+ /**
+ * Whether to use the SIL Graphite rendering engine
+ * (for fonts that include Graphite tables)
+ */
+ bool UseGraphiteShaping();
+
+ // check whether format is supported on a platform or not (if unclear, returns true)
+ virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) { return false; }
+
+ virtual bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) { return false; }
+
+ // returns a list of commonly used fonts for a given character
+ // these are *possible* matches, no cmap-checking is done at this level
+ virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, uint32_t /*aNextCh*/,
+ Script /*aRunScript*/,
+ nsTArray<const char*>& /*aFontList*/)
+ {
+ // platform-specific override, by default do nothing
+ }
+
+ // Are we in safe mode?
+ static bool InSafeMode();
+
+ static bool OffMainThreadCompositingEnabled();
+
+ void UpdateCanUseHardwareVideoDecoding();
+
+ // Returns a prioritized list of all available compositor backends.
+ void GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends);
+
+ /**
+ * Is it possible to use buffer rotation. Note that these
+ * check the preference, but also allow for the override to
+ * disable it using DisableBufferRotation.
+ */
+ static bool BufferRotationEnabled();
+ static void DisableBufferRotation();
+
+ /**
+ * Are we going to try color management?
+ */
+ static eCMSMode GetCMSMode();
+
+ /**
+ * Determines the rendering intent for color management.
+ *
+ * If the value in the pref gfx.color_management.rendering_intent is a
+ * valid rendering intent as defined in gfx/qcms/qcms.h, that
+ * value is returned. Otherwise, -1 is returned and the embedded intent
+ * should be used.
+ *
+ * See bug 444014 for details.
+ */
+ static int GetRenderingIntent();
+
+ /**
+ * Convert a pixel using a cms transform in an endian-aware manner.
+ *
+ * Sets 'out' to 'in' if transform is nullptr.
+ */
+ static void TransformPixel(const Color& in, Color& out, qcms_transform *transform);
+
+ /**
+ * Return the output device ICC profile.
+ */
+ static qcms_profile* GetCMSOutputProfile();
+
+ /**
+ * Return the sRGB ICC profile.
+ */
+ static qcms_profile* GetCMSsRGBProfile();
+
+ /**
+ * Return sRGB -> output device transform.
+ */
+ static qcms_transform* GetCMSRGBTransform();
+
+ /**
+ * Return output -> sRGB device transform.
+ */
+ static qcms_transform* GetCMSInverseRGBTransform();
+
+ /**
+ * Return sRGBA -> output device transform.
+ */
+ static qcms_transform* GetCMSRGBATransform();
+
+ virtual void FontsPrefsChanged(const char *aPref);
+
+ int32_t GetBidiNumeralOption();
+
+ static void
+ FlushFontAndWordCaches();
+
+ /**
+ * Returns a 1x1 surface that can be used to create graphics contexts
+ * for measuring text etc as if they will be rendered to the screen
+ */
+ gfxASurface* ScreenReferenceSurface() { return mScreenReferenceSurface; }
+
+ /**
+ * Returns a 1x1 DrawTarget that can be used for measuring text etc. as
+ * it would measure if rendered on-screen. Guaranteed to return a
+ * non-null and valid DrawTarget.
+ */
+ mozilla::gfx::DrawTarget* ScreenReferenceDrawTarget() { return mScreenReferenceDrawTarget; }
+
+ virtual mozilla::gfx::SurfaceFormat Optimal2DFormatForContent(gfxContentType aContent);
+
+ virtual gfxImageFormat OptimalFormatForContent(gfxContentType aContent);
+
+ virtual gfxImageFormat GetOffscreenFormat()
+ { return mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32; }
+
+ /**
+ * Returns a logger if one is available and logging is enabled
+ */
+ static mozilla::LogModule* GetLog(eGfxLog aWhichLog);
+
+ int GetScreenDepth() const { return mScreenDepth; }
+ mozilla::gfx::IntSize GetScreenSize() const { return mScreenSize; }
+
+ /**
+ * Return the layer debugging options to use browser-wide.
+ */
+ mozilla::layers::DiagnosticTypes GetLayerDiagnosticTypes();
+
+ static mozilla::gfx::IntRect FrameCounterBounds() {
+ int bits = 16;
+ int sizeOfBit = 3;
+ return mozilla::gfx::IntRect(0, 0, bits * sizeOfBit, sizeOfBit);
+ }
+
+ mozilla::gl::SkiaGLGlue* GetSkiaGLGlue();
+ void PurgeSkiaGPUCache();
+ static void PurgeSkiaFontCache();
+
+ virtual bool IsInGonkEmulator() const { return false; }
+
+ static bool UsesOffMainThreadCompositing();
+
+ bool HasEnoughTotalSystemMemoryForSkiaGL();
+
+ /**
+ * Get the hardware vsync source for each platform.
+ * Should only exist and be valid on the parent process
+ */
+ virtual mozilla::gfx::VsyncSource* GetHardwareVsync() {
+ MOZ_ASSERT(mVsyncSource != nullptr);
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return mVsyncSource;
+ }
+
+ /**
+ * True if layout rendering should use ASAP mode, which means
+ * the refresh driver and compositor should render ASAP.
+ * Used for talos testing purposes
+ */
+ static bool IsInLayoutAsapMode();
+
+ /**
+ * Returns the software vsync rate to use.
+ */
+ static int GetSoftwareVsyncRate();
+
+ /**
+ * Returns whether or not a custom vsync rate is set.
+ */
+ static bool ForceSoftwareVsync();
+
+ /**
+ * Returns the default frame rate for the refresh driver / software vsync.
+ */
+ static int GetDefaultFrameRate();
+
+ /**
+ * Used to test which input types are handled via APZ.
+ */
+ virtual bool SupportsApzWheelInput() const {
+ return false;
+ }
+ virtual bool SupportsApzTouchInput() const {
+ return false;
+ }
+ bool SupportsApzDragInput() const;
+
+ virtual void FlushContentDrawing() {}
+
+ // If a device reset has occurred, schedule any necessary paints in the
+ // widget. This should only be used within nsRefreshDriver.
+ virtual void SchedulePaintIfDeviceReset() {}
+
+ /**
+ * Helper method, creates a draw target for a specific Azure backend.
+ * Used by CreateOffscreenDrawTarget.
+ */
+ already_AddRefed<DrawTarget>
+ CreateDrawTargetForBackend(mozilla::gfx::BackendType aBackend,
+ const mozilla::gfx::IntSize& aSize,
+ mozilla::gfx::SurfaceFormat aFormat);
+
+ /**
+ * Wrapper around gfxPrefs::PerfWarnings().
+ * Extracted into a function to avoid including gfxPrefs.h from this file.
+ */
+ static bool PerfWarnings();
+
+ void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend);
+ mozilla::layers::LayersBackend GetCompositorBackend() const {
+ return mCompositorBackend;
+ }
+
+ virtual void CompositorUpdated() {}
+
+ // Plugin async drawing support.
+ virtual bool SupportsPluginDirectBitmapDrawing() {
+ return false;
+ }
+
+ // Some platforms don't support CompositorOGL in an unaccelerated OpenGL
+ // context. These platforms should return true here.
+ virtual bool RequiresAcceleratedGLContextForCompositorOGL() const {
+ return false;
+ }
+
+ uint64_t GetDeviceCounter() const {
+ return mDeviceCounter;
+ }
+
+ /**
+ * Check the blocklist for a feature. Returns false if the feature is blocked
+ * with an appropriate message and failure ID.
+ * */
+ static bool IsGfxInfoStatusOkay(int32_t aFeature, nsCString* aOutMessage,
+ nsCString& aFailureId);
+
+ const gfxSkipChars& EmptySkipChars() const { return kEmptySkipChars; }
+
+ /**
+ * Return information on how child processes should initialize graphics
+ * devices.
+ */
+ virtual void BuildContentDeviceData(mozilla::gfx::ContentDeviceData* aOut);
+
+ /**
+ * Imports settings from the GPU process. This should only be called through
+ * GPUProcessManager, in the UI process.
+ */
+ virtual void ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData);
+
+protected:
+ gfxPlatform();
+ virtual ~gfxPlatform();
+
+ virtual void InitAcceleration();
+
+ /**
+ * Called immediately before deleting the gfxPlatform object.
+ */
+ virtual void WillShutdown();
+
+ /**
+ * Initialized hardware vsync based on each platform.
+ */
+ virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource();
+
+ // Returns whether or not layers should be accelerated by default on this platform.
+ bool AccelerateLayersByDefault();
+
+ // Returns a prioritized list of available compositor backends for acceleration.
+ virtual void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
+
+ /**
+ * Initialise the preferred and fallback canvas backends
+ * aBackendBitmask specifies the backends which are acceptable to the caller.
+ * The backend used is determined by aBackendBitmask and the order specified
+ * by the gfx.canvas.azure.backends pref.
+ */
+ void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault,
+ uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
+
+ /**
+ * Content-process only. Requests device preferences from the parent process
+ * and updates any cached settings.
+ */
+ void FetchAndImportContentDeviceData();
+ virtual void ImportContentDeviceData(const mozilla::gfx::ContentDeviceData& aData);
+
+ /**
+ * Increase the global device counter after a device has been removed/reset.
+ */
+ void BumpDeviceCounter();
+
+ /**
+ * returns the first backend named in the pref gfx.canvas.azure.backends
+ * which is a component of aBackendBitmask, a bitmask of backend types
+ */
+ static mozilla::gfx::BackendType GetCanvasBackendPref(uint32_t aBackendBitmask);
+
+ /**
+ * returns the first backend named in the pref gfx.content.azure.backend
+ * which is a component of aBackendBitmask, a bitmask of backend types
+ */
+ static mozilla::gfx::BackendType GetContentBackendPref(uint32_t &aBackendBitmask);
+
+ /**
+ * Will return the first backend named in aBackendPrefName
+ * allowed by aBackendBitmask, a bitmask of backend types.
+ * It also modifies aBackendBitmask to only include backends that are
+ * allowed given the prefs.
+ */
+ static mozilla::gfx::BackendType GetBackendPref(const char* aBackendPrefName,
+ uint32_t &aBackendBitmask);
+ /**
+ * Decode the backend enumberation from a string.
+ */
+ static mozilla::gfx::BackendType BackendTypeForName(const nsCString& aName);
+
+ static already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFontForFontWithCairoSkia(mozilla::gfx::DrawTarget* aTarget, gfxFont* aFont);
+
+ virtual bool CanUseHardwareVideoDecoding();
+
+ int8_t mAllowDownloadableFonts;
+ int8_t mGraphiteShapingEnabled;
+ int8_t mOpenTypeSVGEnabled;
+
+ int8_t mBidiNumeralOption;
+
+ // whether to always search font cmaps globally
+ // when doing system font fallback
+ int8_t mFallbackUsesCmaps;
+
+ // max character limit for words in word cache
+ int32_t mWordCacheCharLimit;
+
+ // max number of entries in word cache
+ int32_t mWordCacheMaxEntries;
+
+ uint64_t mTotalSystemMemory;
+
+ // Hardware vsync source. Only valid on parent process
+ RefPtr<mozilla::gfx::VsyncSource> mVsyncSource;
+
+ RefPtr<mozilla::gfx::DrawTarget> mScreenReferenceDrawTarget;
+
+private:
+ /**
+ * Start up Thebes.
+ */
+ static void Init();
+
+ static void InitOpenGLConfig();
+ static void CreateCMSOutputProfile();
+
+ static void GetCMSOutputProfileData(void *&mem, size_t &size);
+
+ friend void RecordingPrefChanged(const char *aPrefName, void *aClosure);
+
+ virtual void GetPlatformCMSOutputProfile(void *&mem, size_t &size);
+
+ /**
+ * Calling this function will compute and set the ideal tile size for the
+ * platform. This will only have an effect in the parent process; child processes
+ * should be updated via SetTileSize to match the value computed in the parent.
+ */
+ void ComputeTileSize();
+
+ /**
+ * This uses nsIScreenManager to determine the screen size and color depth
+ */
+ void PopulateScreenInfo();
+
+ void InitCompositorAccelerationPrefs();
+ void InitGPUProcessPrefs();
+
+ RefPtr<gfxASurface> mScreenReferenceSurface;
+ nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
+ nsCOMPtr<nsIObserver> mFontPrefsObserver;
+ nsCOMPtr<nsIObserver> mMemoryPressureObserver;
+
+ // The preferred draw target backend to use for canvas
+ mozilla::gfx::BackendType mPreferredCanvasBackend;
+ // The fallback draw target backend to use for canvas, if the preferred backend fails
+ mozilla::gfx::BackendType mFallbackCanvasBackend;
+ // The backend to use for content
+ mozilla::gfx::BackendType mContentBackend;
+ // Bitmask of backend types we can use to render content
+ uint32_t mContentBackendBitmask;
+
+ mozilla::widget::GfxInfoCollector<gfxPlatform> mAzureCanvasBackendCollector;
+ mozilla::widget::GfxInfoCollector<gfxPlatform> mApzSupportCollector;
+ mozilla::widget::GfxInfoCollector<gfxPlatform> mTilesInfoCollector;
+
+ RefPtr<mozilla::gfx::DrawEventRecorder> mRecorder;
+ RefPtr<mozilla::gl::SkiaGLGlue> mSkiaGlue;
+
+ // Backend that we are compositing with. NONE, if no compositor has been
+ // created yet.
+ mozilla::layers::LayersBackend mCompositorBackend;
+
+ int32_t mScreenDepth;
+ mozilla::gfx::IntSize mScreenSize;
+
+ // Generation number for devices that ClientLayerManagers might depend on.
+ uint64_t mDeviceCounter;
+
+ // An instance of gfxSkipChars which is empty. It is used as the
+ // basis for error-case iterators.
+ const gfxSkipChars kEmptySkipChars;
+};
+
+#endif /* GFX_PLATFORM_H */
diff --git a/system/graphics/thebes/gfxPlatformFontList.cpp b/system/graphics/thebes/gfxPlatformFontList.cpp
new file mode 100644
index 000000000..5f7bbb832
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatformFontList.cpp
@@ -0,0 +1,1699 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+
+#include "gfxPlatformFontList.h"
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+
+#include "nsCRT.h"
+#include "nsGkAtoms.h"
+#include "nsILocaleService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeRange.h"
+#include "nsUnicodeProperties.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/gfx/2D.h"
+
+#include <locale.h>
+
+using namespace mozilla;
+
+#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontlist), \
+ LogLevel::Debug)
+#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontinit), \
+ LogLevel::Debug)
+
+gfxPlatformFontList *gfxPlatformFontList::sPlatformFontList = nullptr;
+
+// Character ranges that require complex-script shaping support in the font,
+// and so should be masked out by ReadCMAP if the necessary layout tables
+// are not present.
+// Currently used by the Mac and FT2 implementations only, but probably should
+// be supported on Windows as well.
+const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
+ // Actually, now that harfbuzz supports presentation-forms shaping for
+ // Arabic, we can render it without layout tables. So maybe we don't
+ // want to mask the basic Arabic block here?
+ // This affects the arabic-fallback-*.html reftests, which rely on
+ // loading a font that *doesn't* have any GSUB table.
+ { 0x0600, 0x06FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x0700, 0x074F, { TRUETYPE_TAG('s','y','r','c'), 0, 0 } },
+ { 0x0750, 0x077F, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x08A0, 0x08FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x0900, 0x097F, { TRUETYPE_TAG('d','e','v','2'),
+ TRUETYPE_TAG('d','e','v','a'), 0 } },
+ { 0x0980, 0x09FF, { TRUETYPE_TAG('b','n','g','2'),
+ TRUETYPE_TAG('b','e','n','g'), 0 } },
+ { 0x0A00, 0x0A7F, { TRUETYPE_TAG('g','u','r','2'),
+ TRUETYPE_TAG('g','u','r','u'), 0 } },
+ { 0x0A80, 0x0AFF, { TRUETYPE_TAG('g','j','r','2'),
+ TRUETYPE_TAG('g','u','j','r'), 0 } },
+ { 0x0B00, 0x0B7F, { TRUETYPE_TAG('o','r','y','2'),
+ TRUETYPE_TAG('o','r','y','a'), 0 } },
+ { 0x0B80, 0x0BFF, { TRUETYPE_TAG('t','m','l','2'),
+ TRUETYPE_TAG('t','a','m','l'), 0 } },
+ { 0x0C00, 0x0C7F, { TRUETYPE_TAG('t','e','l','2'),
+ TRUETYPE_TAG('t','e','l','u'), 0 } },
+ { 0x0C80, 0x0CFF, { TRUETYPE_TAG('k','n','d','2'),
+ TRUETYPE_TAG('k','n','d','a'), 0 } },
+ { 0x0D00, 0x0D7F, { TRUETYPE_TAG('m','l','m','2'),
+ TRUETYPE_TAG('m','l','y','m'), 0 } },
+ { 0x0D80, 0x0DFF, { TRUETYPE_TAG('s','i','n','h'), 0, 0 } },
+ { 0x0E80, 0x0EFF, { TRUETYPE_TAG('l','a','o',' '), 0, 0 } },
+ { 0x0F00, 0x0FFF, { TRUETYPE_TAG('t','i','b','t'), 0, 0 } },
+ { 0x1000, 0x109f, { TRUETYPE_TAG('m','y','m','r'),
+ TRUETYPE_TAG('m','y','m','2'), 0 } },
+ { 0x1780, 0x17ff, { TRUETYPE_TAG('k','h','m','r'), 0, 0 } },
+ // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
+ { 0xaa60, 0xaa7f, { TRUETYPE_TAG('m','y','m','r'),
+ TRUETYPE_TAG('m','y','m','2'), 0 } },
+ // Thai seems to be "renderable" without AAT morphing tables
+ { 0, 0, { 0, 0, 0 } } // terminator
+};
+
+// prefs for the font info loader
+#define FONT_LOADER_FAMILIES_PER_SLICE_PREF "gfx.font_loader.families_per_slice"
+#define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay"
+#define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval"
+
+static const char* kObservedPrefs[] = {
+ "font.",
+ "font.name-list.",
+ "intl.accept_languages", // hmmmm...
+ nullptr
+};
+
+static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
+
+// xxx - this can probably be eliminated by reworking pref font handling code
+static const char *gPrefLangNames[] = {
+ #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
+ #include "gfxFontPrefLangList.h"
+ #undef FONT_PREF_LANG
+};
+
+static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
+ "size of pref lang name array doesn't match pref lang enum size");
+
+class gfxFontListPrefObserver final : public nsIObserver {
+ ~gfxFontListPrefObserver() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
+
+NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
+
+NS_IMETHODIMP
+gfxFontListPrefObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
+ // XXX this could be made to only clear out the cache for the prefs that were changed
+ // but it probably isn't that big a deal.
+ gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
+ gfxFontCache::GetCache()->AgeAllGenerations();
+ return NS_OK;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
+
+NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
+
+NS_IMETHODIMP
+gfxPlatformFontList::MemoryReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
+{
+ FontListSizes sizes;
+ sizes.mFontListSize = 0;
+ sizes.mFontTableCacheSize = 0;
+ sizes.mCharMapsSize = 0;
+
+ gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf,
+ &sizes);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES,
+ sizes.mFontListSize,
+ "Memory used to manage the list of font families and faces.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES,
+ sizes.mCharMapsSize,
+ "Memory used to record the character coverage of individual fonts.");
+
+ if (sizes.mFontTableCacheSize) {
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
+ sizes.mFontTableCacheSize,
+ "Memory used for cached font metrics and layout tables.");
+ }
+
+ return NS_OK;
+}
+
+gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
+ : mFontFamilies(64), mOtherFamilyNames(16),
+ mBadUnderlineFamilyNames(8), mSharedCmaps(8),
+ mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0),
+ mFontFamilyWhitelistActive(false)
+{
+ mOtherFamilyNamesInitialized = false;
+
+ if (aNeedFullnamePostscriptNames) {
+ mExtraNames = MakeUnique<ExtraNames>();
+ }
+ mFaceNameListsInitialized = false;
+
+ LoadBadUnderlineList();
+
+ // pref changes notification setup
+ NS_ASSERTION(!gFontListPrefObserver,
+ "There has been font list pref observer already");
+ gFontListPrefObserver = new gfxFontListPrefObserver();
+ NS_ADDREF(gFontListPrefObserver);
+ Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
+
+ Preferences::RegisterCallback(FontWhitelistPrefChanged,
+ kFontSystemWhitelistPref);
+
+ RegisterStrongMemoryReporter(new MemoryReporter());
+}
+
+gfxPlatformFontList::~gfxPlatformFontList()
+{
+ mSharedCmaps.Clear();
+ ClearLangGroupPrefFonts();
+ NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
+ Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
+ Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+ kFontSystemWhitelistPref);
+ NS_RELEASE(gFontListPrefObserver);
+}
+
+// number of CSS generic font families
+const uint32_t kNumGenerics = 5;
+
+void
+gfxPlatformFontList::ApplyWhitelist()
+{
+ nsTArray<nsString> list;
+ gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
+ uint32_t numFonts = list.Length();
+ mFontFamilyWhitelistActive = (numFonts > 0);
+ if (!mFontFamilyWhitelistActive) {
+ return;
+ }
+ nsTHashtable<nsStringHashKey> familyNamesWhitelist;
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsString key;
+ ToLowerCase(list[i], key);
+ familyNamesWhitelist.PutEntry(key);
+ }
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ // Don't continue if we only have one font left.
+ if (mFontFamilies.Count() == 1) {
+ break;
+ }
+ nsString fontFamilyName(iter.Key());
+ ToLowerCase(fontFamilyName);
+ if (!familyNamesWhitelist.Contains(fontFamilyName)) {
+ iter.Remove();
+ }
+ }
+}
+
+nsresult
+gfxPlatformFontList::InitFontList()
+{
+ mFontlistInitCount++;
+
+ if (LOG_FONTINIT_ENABLED()) {
+ LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
+ }
+
+ // rebuilding fontlist so clear out font/word caches
+ gfxFontCache *fontCache = gfxFontCache::GetCache();
+ if (fontCache) {
+ fontCache->AgeAllGenerations();
+ fontCache->FlushShapedWordCaches();
+ }
+
+ gfxPlatform::PurgeSkiaFontCache();
+
+ mFontFamilies.Clear();
+ mOtherFamilyNames.Clear();
+ mOtherFamilyNamesInitialized = false;
+ if (mExtraNames) {
+ mExtraNames->mFullnames.Clear();
+ mExtraNames->mPostscriptNames.Clear();
+ }
+ mFaceNameListsInitialized = false;
+ ClearLangGroupPrefFonts();
+ mReplacementCharFallbackFamily = nullptr;
+ CancelLoader();
+
+ // initialize ranges of characters for which system-wide font search should be skipped
+ mCodepointsWithNoFonts.reset();
+ mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
+ mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
+
+ sPlatformFontList = this;
+
+ nsresult rv = InitFontListForPlatform();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ ApplyWhitelist();
+ return NS_OK;
+}
+
+void
+gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
+{
+ aResult = aKeyName;
+ ToLowerCase(aResult);
+}
+
+#define OTHERNAMES_TIMEOUT 200
+
+void
+gfxPlatformFontList::InitOtherFamilyNames()
+{
+ if (mOtherFamilyNamesInitialized) {
+ return;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+ bool timedOut = false;
+
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ family->ReadOtherFamilyNames(this);
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
+ timedOut = true;
+ break;
+ }
+ }
+
+ if (!timedOut) {
+ mOtherFamilyNamesInitialized = true;
+ }
+ TimeStamp end = TimeStamp::Now();
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = end - start;
+ LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
+ elapsed.ToMilliseconds(),
+ (timedOut ? "timeout" : "")));
+ }
+}
+
+// time limit for loading facename lists (ms)
+#define NAMELIST_TIMEOUT 200
+
+gfxFontEntry*
+gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
+{
+ TimeStamp start = TimeStamp::Now();
+ bool timedOut = false;
+ // if mFirstChar is not 0, only load facenames for families
+ // that start with this character
+ char16_t firstChar = 0;
+ gfxFontEntry *lookup = nullptr;
+
+ // iterate over familes starting with the same letter
+ firstChar = ToLowerCase(aFaceName.CharAt(0));
+
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+
+ // when filtering, skip names that don't start with the filter character
+ if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
+ continue;
+ }
+
+ family->ReadFaceNames(this, NeedFullnamePostscriptNames());
+
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
+ timedOut = true;
+ break;
+ }
+ }
+
+ lookup = FindFaceName(aFaceName);
+
+ TimeStamp end = TimeStamp::Now();
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = end - start;
+ LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
+ elapsed.ToMilliseconds(),
+ (lookup ? "found name" : ""),
+ (timedOut ? "timeout" : "")));
+ }
+
+ return lookup;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::FindFaceName(const nsAString& aFaceName)
+{
+ gfxFontEntry *lookup;
+
+ // lookup in name lookup tables, return null if not found
+ if (mExtraNames &&
+ ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
+ (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
+ return lookup;
+ }
+
+ return nullptr;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName)
+{
+ gfxFontEntry *lookup = nullptr;
+
+ // initialize facename lookup tables if needed
+ // note: this can terminate early or time out, in which case
+ // mFaceNameListsInitialized remains false
+ if (!mFaceNameListsInitialized) {
+ lookup = SearchFamiliesForFaceName(aFaceName);
+ if (lookup) {
+ return lookup;
+ }
+ }
+
+ // lookup in name lookup tables, return null if not found
+ if (!(lookup = FindFaceName(aFaceName))) {
+ // names not completely initialized, so keep track of lookup misses
+ if (!mFaceNameListsInitialized) {
+ if (!mFaceNamesMissed) {
+ mFaceNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
+ }
+ mFaceNamesMissed->PutEntry(aFaceName);
+ }
+ }
+
+ return lookup;
+}
+
+void
+gfxPlatformFontList::PreloadNamesList()
+{
+ AutoTArray<nsString, 10> preloadFonts;
+ gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
+
+ uint32_t numFonts = preloadFonts.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsAutoString key;
+ GenerateFontListKey(preloadFonts[i], key);
+
+ // only search canonical names!
+ gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
+ if (familyEntry) {
+ familyEntry->ReadOtherFamilyNames(this);
+ }
+ }
+
+}
+
+void
+gfxPlatformFontList::LoadBadUnderlineList()
+{
+ AutoTArray<nsString, 10> blacklist;
+ gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
+ uint32_t numFonts = blacklist.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsAutoString key;
+ GenerateFontListKey(blacklist[i], key);
+ mBadUnderlineFamilyNames.PutEntry(key);
+ }
+}
+
+void
+gfxPlatformFontList::UpdateFontList()
+{
+ InitFontList();
+ RebuildLocalFonts();
+}
+
+void
+gfxPlatformFontList::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ // use the first variation for now. This data should be the same
+ // for all the variations and should probably be moved up to
+ // the Family
+ gfxFontStyle style;
+ style.language = aLangGroup;
+ bool needsBold;
+ RefPtr<gfxFontEntry> fontEntry = family->FindFontForStyle(style, needsBold);
+ NS_ASSERTION(fontEntry, "couldn't find any font entry in family");
+ if (!fontEntry) {
+ continue;
+ }
+
+ /* skip symbol fonts */
+ if (fontEntry->IsSymbolFont()) {
+ continue;
+ }
+
+ if (fontEntry->SupportsLangGroup(aLangGroup) &&
+ fontEntry->MatchesGenericFamily(aGenericFamily)) {
+ nsAutoString localizedFamilyName;
+ family->LocalizedName(localizedFamilyName);
+ aListOfFonts.AppendElement(localizedFamilyName);
+ }
+ }
+
+ aListOfFonts.Sort();
+ aListOfFonts.Compact();
+}
+
+void
+gfxPlatformFontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFamilyArray.AppendElement(family);
+ }
+}
+
+gfxFontEntry*
+gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aStyle)
+ {
+ gfxFontEntry* fontEntry = nullptr;
+
+ // is codepoint with no matching font? return null immediately
+ if (mCodepointsWithNoFonts.test(aCh)) {
+ return nullptr;
+ }
+
+ // Try to short-circuit font fallback for U+FFFD, used to represent
+ // encoding errors: just use cached family from last time U+FFFD was seen.
+ // This helps speed up pages with lots of encoding errors, binary-as-text,
+ // etc.
+ if (aCh == 0xFFFD && mReplacementCharFallbackFamily) {
+ bool needsBold; // ignored in the system fallback case
+
+ fontEntry =
+ mReplacementCharFallbackFamily->FindFontForStyle(*aStyle,
+ needsBold);
+
+ // this should never fail, as we must have found U+FFFD in order to set
+ // mReplacementCharFallbackFamily at all, but better play it safe
+ if (fontEntry && fontEntry->HasCharacter(aCh)) {
+ return fontEntry;
+ }
+ }
+
+ TimeStamp start = TimeStamp::Now();
+
+ // search commonly available fonts
+ bool common = true;
+ gfxFontFamily *fallbackFamily = nullptr;
+ fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle,
+ &fallbackFamily);
+
+ // if didn't find a font, do system-wide fallback (except for specials)
+ uint32_t cmapCount = 0;
+ if (!fontEntry) {
+ common = false;
+ fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount,
+ &fallbackFamily);
+ }
+ TimeDuration elapsed = TimeStamp::Now() - start;
+
+ LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
+ uint32_t unicodeRange = FindCharUnicodeRange(aCh);
+ Script script = mozilla::unicode::GetScriptCode(aCh);
+ MOZ_LOG(log, LogLevel::Warning,\
+ ("(textrun-systemfallback-%s) char: u+%6.6x "
+ "unicode-range: %d script: %d match: [%s]"
+ " time: %dus cmaps: %d\n",
+ (common ? "common" : "global"), aCh,
+ unicodeRange, script,
+ (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() :
+ "<none>"),
+ int32_t(elapsed.ToMicroseconds()),
+ cmapCount));
+ }
+
+ // no match? add to set of non-matching codepoints
+ if (!fontEntry) {
+ mCodepointsWithNoFonts.set(aCh);
+ } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) {
+ mReplacementCharFallbackFamily = fallbackFamily;
+ }
+
+ return fontEntry;
+}
+
+#define NUM_FALLBACK_FONTS 8
+
+gfxFontEntry*
+gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily)
+{
+ AutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks;
+ uint32_t i, numFallbacks;
+
+ gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh,
+ aRunScript,
+ defaultFallbacks);
+ numFallbacks = defaultFallbacks.Length();
+ for (i = 0; i < numFallbacks; i++) {
+ nsAutoString familyName;
+ const char *fallbackFamily = defaultFallbacks[i];
+
+ familyName.AppendASCII(fallbackFamily);
+ gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName);
+ if (!fallback)
+ continue;
+
+ gfxFontEntry *fontEntry;
+ bool needsBold; // ignored in the system fallback case
+
+ // use first font in list that supports a given character
+ fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold);
+ if (fontEntry && fontEntry->HasCharacter(aCh)) {
+ *aMatchedFamily = fallback;
+ return fontEntry;
+ }
+ }
+
+ return nullptr;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ uint32_t& aCmapCount,
+ gfxFontFamily** aMatchedFamily)
+{
+ bool useCmaps = IsFontFamilyWhitelistActive() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+ if (!useCmaps) {
+ // Allow platform-specific fallback code to try and find a usable font
+ gfxFontEntry* fe =
+ PlatformGlobalFontFallback(aCh, aRunScript, aMatchStyle,
+ aMatchedFamily);
+ if (fe) {
+ return fe;
+ }
+ }
+
+ // otherwise, try to find it among local fonts
+ GlobalFontMatch data(aCh, aRunScript, aMatchStyle);
+
+ // iterate over all font families to find a font that support the character
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ // evaluate all fonts in this family for a match
+ family->FindFontForChar(&data);
+ }
+
+ aCmapCount = data.mCmapsTested;
+ *aMatchedFamily = data.mMatchedFamily;
+
+ return data.mBestMatch;
+}
+
+gfxFontFamily*
+gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
+{
+ if (aFamily && !aFamily->HasStyles()) {
+ aFamily->FindStyleVariations();
+ aFamily->CheckForSimpleFamily();
+ }
+
+ if (aFamily && aFamily->GetFontList().Length() == 0) {
+ // failed to load any faces for this family, so discard it
+ nsAutoString key;
+ GenerateFontListKey(aFamily->Name(), key);
+ mFontFamilies.Remove(key);
+ return nullptr;
+ }
+
+ return aFamily;
+}
+
+bool
+gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ nsAutoString key;
+ GenerateFontListKey(aFamily, key);
+
+ NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
+
+ // lookup in canonical (i.e. English) family name list
+ gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
+
+ // if not found, lookup in other family names list (mostly localized names)
+ if (!familyEntry) {
+ familyEntry = mOtherFamilyNames.GetWeak(key);
+ }
+
+ // if still not found and other family names not yet fully initialized,
+ // initialize the rest of the list and try again. this is done lazily
+ // since reading name table entries is expensive.
+ // although ASCII localized family names are possible they don't occur
+ // in practice so avoid pulling in names at startup
+ if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
+ InitOtherFamilyNames();
+ familyEntry = mOtherFamilyNames.GetWeak(key);
+ if (!familyEntry && !mOtherFamilyNamesInitialized) {
+ // localized family names load timed out, add name to list of
+ // names to check after localized names are loaded
+ if (!mOtherNamesMissed) {
+ mOtherNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
+ }
+ mOtherNamesMissed->PutEntry(key);
+ }
+ }
+
+ familyEntry = CheckFamily(familyEntry);
+ if (familyEntry) {
+ aOutput->AppendElement(familyEntry);
+ return true;
+ }
+
+ return false;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold)
+{
+ gfxFontFamily *familyEntry = FindFamily(aFamily);
+
+ aNeedsBold = false;
+
+ if (familyEntry)
+ return familyEntry->FindFontForStyle(*aStyle, aNeedsBold);
+
+ return nullptr;
+}
+
+void
+gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName)
+{
+ nsAutoString key;
+ GenerateFontListKey(aOtherFamilyName, key);
+
+ if (!mOtherFamilyNames.GetWeak(key)) {
+ mOtherFamilyNames.Put(key, aFamilyEntry);
+ LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, "
+ "other family: %s\n",
+ NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
+ if (mBadUnderlineFamilyNames.Contains(key))
+ aFamilyEntry->SetBadUnderlineFamily();
+ }
+}
+
+void
+gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname)
+{
+ if (!mExtraNames->mFullnames.GetWeak(aFullname)) {
+ mExtraNames->mFullnames.Put(aFullname, aFontEntry);
+ LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
+ NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aFullname).get()));
+ }
+}
+
+void
+gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName)
+{
+ if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) {
+ mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry);
+ LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
+ NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aPostscriptName).get()));
+ }
+}
+
+bool
+gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+ aFamilyName.Truncate();
+ gfxFontFamily *ff = FindFamily(aFontName);
+ if (!ff) {
+ return false;
+ }
+ aFamilyName.Assign(ff->Name());
+ return true;
+}
+
+gfxCharacterMap*
+gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap)
+{
+ aCmap->CalcHash();
+ gfxCharacterMap *cmap = AddCmap(aCmap);
+ cmap->mShared = true;
+ return cmap;
+}
+
+// add a cmap to the shared cmap set
+gfxCharacterMap*
+gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap)
+{
+ CharMapHashKey *found =
+ mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ return found->GetKey();
+}
+
+// remove the cmap from the shared cmap set
+void
+gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
+{
+ // skip lookups during teardown
+ if (mSharedCmaps.Count() == 0) {
+ return;
+ }
+
+ // cmap needs to match the entry *and* be the same ptr before removing
+ CharMapHashKey *found =
+ mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ if (found && found->GetKey() == aCharMap) {
+ mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ }
+}
+
+void
+gfxPlatformFontList::ResolveGenericFontNames(
+ FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang,
+ nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+ const char* langGroupStr = GetPrefLangName(aPrefLang);
+ const char* generic = GetGenericName(aGenericType);
+
+ if (!generic) {
+ return;
+ }
+
+ AutoTArray<nsString,4> genericFamilies;
+
+ // load family for "font.name.generic.lang"
+ nsAutoCString prefFontName("font.name.");
+ prefFontName.Append(generic);
+ prefFontName.Append('.');
+ prefFontName.Append(langGroupStr);
+ gfxFontUtils::AppendPrefsFontList(prefFontName.get(), genericFamilies);
+
+ // load fonts for "font.name-list.generic.lang"
+ nsAutoCString prefFontListName("font.name-list.");
+ prefFontListName.Append(generic);
+ prefFontListName.Append('.');
+ prefFontListName.Append(langGroupStr);
+ gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
+
+ nsIAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
+ NS_ASSERTION(langGroup, "null lang group for pref lang");
+
+ gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
+ langGroup,
+ aGenericFamilies);
+
+#if 0 // dump out generic mappings
+ printf("%s ===> ", prefFontName.get());
+ for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
+ if (k > 0) printf(", ");
+ printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get());
+ }
+ printf("\n");
+#endif
+}
+
+void
+gfxPlatformFontList::ResolveEmojiFontNames(
+ nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+ // emoji preference has no lang name
+ AutoTArray<nsString,4> genericFamilies;
+
+ nsAutoCString prefFontListName("font.name-list.emoji");
+ gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
+
+ gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
+ nullptr,
+ aGenericFamilies);
+}
+
+void
+gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
+ nsTArray<nsString>& aGenericNameFamilies,
+ nsIAtom* aLangGroup,
+ nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+ // lookup and add platform fonts uniquely
+ for (const nsString& genericFamily : aGenericNameFamilies) {
+ gfxFontStyle style;
+ style.language = aLangGroup;
+ style.systemFont = false;
+ AutoTArray<gfxFontFamily*,10> families;
+ FindAndAddFamilies(genericFamily, &families, &style, 1.0);
+ for (gfxFontFamily* f : families) {
+ if (!aGenericFamilies->Contains(f)) {
+ aGenericFamilies->AppendElement(f);
+ }
+ }
+ }
+}
+
+nsTArray<RefPtr<gfxFontFamily>>*
+gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang)
+{
+ // treat -moz-fixed as monospace
+ if (aGenericType == eFamily_moz_fixed) {
+ aGenericType = eFamily_monospace;
+ }
+
+ if (aGenericType == eFamily_moz_emoji) {
+ // Emoji font has no lang
+ PrefFontList* prefFonts = mEmojiPrefFont.get();
+ if (MOZ_UNLIKELY(!prefFonts)) {
+ prefFonts = new PrefFontList;
+ ResolveEmojiFontNames(prefFonts);
+ mEmojiPrefFont.reset(prefFonts);
+ }
+ return prefFonts;
+ }
+
+ PrefFontList* prefFonts =
+ mLangGroupPrefFonts[aPrefLang][aGenericType].get();
+ if (MOZ_UNLIKELY(!prefFonts)) {
+ prefFonts = new PrefFontList;
+ ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
+ mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
+ }
+ return prefFonts;
+}
+
+void
+gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
+ nsIAtom* aLanguage,
+ nsTArray<gfxFontFamily*>& aFamilyList)
+{
+ // map lang ==> langGroup
+ nsIAtom* langGroup = GetLangGroup(aLanguage);
+
+ // langGroup ==> prefLang
+ eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
+
+ // lookup pref fonts
+ nsTArray<RefPtr<gfxFontFamily>>* prefFonts =
+ GetPrefFontsLangGroup(aGenericType, prefLang);
+
+ if (!prefFonts->IsEmpty()) {
+ aFamilyList.AppendElements(*prefFonts);
+ }
+}
+
+static nsIAtom* PrefLangToLangGroups(uint32_t aIndex)
+{
+ // static array here avoids static constructor
+ static nsIAtom* gPrefLangToLangGroups[] = {
+ #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
+ #include "gfxFontPrefLangList.h"
+ #undef FONT_PREF_LANG
+ };
+
+ return aIndex < ArrayLength(gPrefLangToLangGroups)
+ ? gPrefLangToLangGroups[aIndex]
+ : nsGkAtoms::Unicode;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(const char* aLang)
+{
+ if (!aLang || !aLang[0]) {
+ return eFontPrefLang_Others;
+ }
+ for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
+ if (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
+ return eFontPrefLang(i);
+ }
+ }
+ return eFontPrefLang_Others;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(nsIAtom *aLang)
+{
+ if (!aLang)
+ return eFontPrefLang_Others;
+ nsAutoCString lang;
+ aLang->ToUTF8String(lang);
+ return GetFontPrefLangFor(lang.get());
+}
+
+nsIAtom*
+gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang)
+{
+ // the special CJK set pref lang should be resolved into separate
+ // calls to individual CJK pref langs before getting here
+ NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
+
+ return PrefLangToLangGroups(uint32_t(aLang));
+}
+
+const char*
+gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang)
+{
+ if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+ return gPrefLangNames[uint32_t(aLang)];
+ }
+ return nullptr;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange)
+{
+ switch (aUnicodeRange) {
+ case kRangeSetLatin: return eFontPrefLang_Western;
+ case kRangeCyrillic: return eFontPrefLang_Cyrillic;
+ case kRangeGreek: return eFontPrefLang_Greek;
+ case kRangeHebrew: return eFontPrefLang_Hebrew;
+ case kRangeArabic: return eFontPrefLang_Arabic;
+ case kRangeThai: return eFontPrefLang_Thai;
+ case kRangeKorean: return eFontPrefLang_Korean;
+ case kRangeJapanese: return eFontPrefLang_Japanese;
+ case kRangeSChinese: return eFontPrefLang_ChineseCN;
+ case kRangeTChinese: return eFontPrefLang_ChineseTW;
+ case kRangeDevanagari: return eFontPrefLang_Devanagari;
+ case kRangeTamil: return eFontPrefLang_Tamil;
+ case kRangeArmenian: return eFontPrefLang_Armenian;
+ case kRangeBengali: return eFontPrefLang_Bengali;
+ case kRangeCanadian: return eFontPrefLang_Canadian;
+ case kRangeEthiopic: return eFontPrefLang_Ethiopic;
+ case kRangeGeorgian: return eFontPrefLang_Georgian;
+ case kRangeGujarati: return eFontPrefLang_Gujarati;
+ case kRangeGurmukhi: return eFontPrefLang_Gurmukhi;
+ case kRangeKhmer: return eFontPrefLang_Khmer;
+ case kRangeMalayalam: return eFontPrefLang_Malayalam;
+ case kRangeOriya: return eFontPrefLang_Oriya;
+ case kRangeTelugu: return eFontPrefLang_Telugu;
+ case kRangeKannada: return eFontPrefLang_Kannada;
+ case kRangeSinhala: return eFontPrefLang_Sinhala;
+ case kRangeTibetan: return eFontPrefLang_Tibetan;
+ case kRangeSetCJK: return eFontPrefLang_CJKSet;
+ default: return eFontPrefLang_Others;
+ }
+}
+
+bool
+gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang)
+{
+ switch (aLang) {
+ case eFontPrefLang_Japanese:
+ case eFontPrefLang_ChineseTW:
+ case eFontPrefLang_ChineseCN:
+ case eFontPrefLang_ChineseHK:
+ case eFontPrefLang_Korean:
+ case eFontPrefLang_CJKSet:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
+{
+ if (IsLangCJK(aCharLang)) {
+ AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
+ } else {
+ AppendPrefLang(aPrefLangs, aLen, aCharLang);
+ }
+
+ AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
+}
+
+void
+gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
+{
+ // prefer the lang specified by the page *if* CJK
+ if (IsLangCJK(aPageLang)) {
+ AppendPrefLang(aPrefLangs, aLen, aPageLang);
+ }
+
+ // if not set up, set up the default CJK order, based on accept lang settings and locale
+ if (mCJKPrefLangs.Length() == 0) {
+
+ // temp array
+ eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
+ uint32_t tempLen = 0;
+
+ // Add the CJK pref fonts from accept languages, the order should be same order
+ nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages");
+ if (!list.IsEmpty()) {
+ const char kComma = ',';
+ const char *p, *p_end;
+ list.BeginReading(p);
+ list.EndReading(p_end);
+ while (p < p_end) {
+ while (nsCRT::IsAsciiSpace(*p)) {
+ if (++p == p_end)
+ break;
+ }
+ if (p == p_end)
+ break;
+ const char *start = p;
+ while (++p != p_end && *p != kComma)
+ /* nothing */ ;
+ nsAutoCString lang(Substring(start, p));
+ lang.CompressWhitespace(false, true);
+ eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get());
+ switch (fpl) {
+ case eFontPrefLang_Japanese:
+ case eFontPrefLang_Korean:
+ case eFontPrefLang_ChineseCN:
+ case eFontPrefLang_ChineseHK:
+ case eFontPrefLang_ChineseTW:
+ AppendPrefLang(tempPrefLangs, tempLen, fpl);
+ break;
+ default:
+ break;
+ }
+ p++;
+ }
+ }
+
+ do { // to allow 'break' to abort this block if a call fails
+ nsresult rv;
+ nsCOMPtr<nsILocaleService> ls =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsILocale> appLocale;
+ rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
+ if (NS_FAILED(rv))
+ break;
+
+ nsString localeStr;
+ rv = appLocale->
+ GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
+ if (NS_FAILED(rv))
+ break;
+
+ const nsAString& lang = Substring(localeStr, 0, 2);
+ if (lang.EqualsLiteral("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (lang.EqualsLiteral("zh")) {
+ const nsAString& region = Substring(localeStr, 3, 2);
+ if (region.EqualsLiteral("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (region.EqualsLiteral("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (region.EqualsLiteral("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (lang.EqualsLiteral("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ } while (0);
+
+ // last resort... (the order is same as old gfx.)
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+
+ // copy into the cached array
+ uint32_t j;
+ for (j = 0; j < tempLen; j++) {
+ mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
+ }
+ }
+
+ // append in cached CJK langs
+ uint32_t i, numCJKlangs = mCJKPrefLangs.Length();
+
+ for (i = 0; i < numCJKlangs; i++) {
+ AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
+ }
+
+}
+
+void
+gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
+{
+ if (aLen >= kMaxLenPrefLangList) return;
+
+ // make sure
+ uint32_t i = 0;
+ while (i < aLen && aPrefLangs[i] != aAddLang) {
+ i++;
+ }
+
+ if (i == aLen) {
+ aPrefLangs[aLen] = aAddLang;
+ aLen++;
+ }
+}
+
+mozilla::FontFamilyType
+gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
+{
+ if (aLang == eFontPrefLang_Emoji) {
+ return eFamily_moz_emoji;
+ }
+
+ // initialize lang group pref font defaults (i.e. serif/sans-serif)
+ if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
+ mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
+ for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
+ nsAutoCString prefDefaultFontType("font.default.");
+ prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
+ nsAdoptingCString serifOrSans =
+ Preferences::GetCString(prefDefaultFontType.get());
+ if (serifOrSans.EqualsLiteral("sans-serif")) {
+ mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
+ } else {
+ mDefaultGenericsLangGroup[i] = eFamily_serif;
+ }
+ }
+ }
+
+ if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+ return mDefaultGenericsLangGroup[uint32_t(aLang)];
+ }
+ return eFamily_serif;
+}
+
+
+gfxFontFamily*
+gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+{
+ gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
+ if (family) {
+ return family;
+ }
+ // Something has gone wrong and we were unable to retrieve a default font
+ // from the platform. (Likely the whitelist has blocked all potential
+ // default fonts.) As a last resort, we return the first font listed in
+ // mFontFamilies.
+ return mFontFamilies.Iter().Data();
+}
+
+void
+gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFontFamilyNames.AppendElement(family->Name());
+ }
+}
+
+nsILanguageAtomService*
+gfxPlatformFontList::GetLangService()
+{
+ if (!mLangService) {
+ mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+ }
+ NS_ASSERTION(mLangService, "no language service!");
+ return mLangService;
+}
+
+nsIAtom*
+gfxPlatformFontList::GetLangGroup(nsIAtom* aLanguage)
+{
+ // map lang ==> langGroup
+ nsIAtom *langGroup = nullptr;
+ if (aLanguage) {
+ nsresult rv;
+ nsILanguageAtomService* langService = GetLangService();
+ langGroup = langService->GetLanguageGroup(aLanguage, &rv);
+ }
+ if (!langGroup) {
+ langGroup = nsGkAtoms::Unicode;
+ }
+ return langGroup;
+}
+
+/* static */ const char*
+gfxPlatformFontList::GetGenericName(FontFamilyType aGenericType)
+{
+ static const char kGeneric_serif[] = "serif";
+ static const char kGeneric_sans_serif[] = "sans-serif";
+ static const char kGeneric_monospace[] = "monospace";
+ static const char kGeneric_cursive[] = "cursive";
+ static const char kGeneric_fantasy[] = "fantasy";
+
+ // type should be standard generic type at this point
+ NS_ASSERTION(aGenericType >= eFamily_serif &&
+ aGenericType <= eFamily_fantasy,
+ "standard generic font family type required");
+
+ // map generic type to string
+ const char *generic = nullptr;
+ switch (aGenericType) {
+ case eFamily_serif:
+ generic = kGeneric_serif;
+ break;
+ case eFamily_sans_serif:
+ generic = kGeneric_sans_serif;
+ break;
+ case eFamily_monospace:
+ generic = kGeneric_monospace;
+ break;
+ case eFamily_cursive:
+ generic = kGeneric_cursive;
+ break;
+ case eFamily_fantasy:
+ generic = kGeneric_fantasy;
+ break;
+ default:
+ break;
+ }
+
+ return generic;
+}
+
+// mapping of moz lang groups ==> default lang
+struct MozLangGroupData {
+ nsIAtom* const& mozLangGroup;
+ const char *defaultLang;
+};
+
+const MozLangGroupData MozLangGroups[] = {
+ { nsGkAtoms::x_western, "en" },
+ { nsGkAtoms::x_cyrillic, "ru" },
+ { nsGkAtoms::x_devanagari, "hi" },
+ { nsGkAtoms::x_tamil, "ta" },
+ { nsGkAtoms::x_armn, "hy" },
+ { nsGkAtoms::x_beng, "bn" },
+ { nsGkAtoms::x_cans, "iu" },
+ { nsGkAtoms::x_ethi, "am" },
+ { nsGkAtoms::x_geor, "ka" },
+ { nsGkAtoms::x_gujr, "gu" },
+ { nsGkAtoms::x_guru, "pa" },
+ { nsGkAtoms::x_khmr, "km" },
+ { nsGkAtoms::x_knda, "kn" },
+ { nsGkAtoms::x_mlym, "ml" },
+ { nsGkAtoms::x_orya, "or" },
+ { nsGkAtoms::x_sinh, "si" },
+ { nsGkAtoms::x_tamil, "ta" },
+ { nsGkAtoms::x_telu, "te" },
+ { nsGkAtoms::x_tibt, "bo" },
+ { nsGkAtoms::Unicode, 0 }
+};
+
+bool
+gfxPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
+ nsIAtom* aLangGroup,
+ nsACString& aFcLang)
+{
+ // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
+ // aOSLang is in the form "language[_territory][.codeset][@modifier]".
+ // fontconfig takes languages in the form "language-territory".
+ // nsILanguageAtomService takes languages in the form language-subtag,
+ // where subtag may be a territory. fontconfig and nsILanguageAtomService
+ // handle case-conversion for us.
+ const char *pos, *end;
+ aOSLang.BeginReading(pos);
+ aOSLang.EndReading(end);
+ aFcLang.Truncate();
+ while (pos < end) {
+ switch (*pos) {
+ case '.':
+ case '@':
+ end = pos;
+ break;
+ case '_':
+ aFcLang.Append('-');
+ break;
+ default:
+ aFcLang.Append(*pos);
+ }
+ ++pos;
+ }
+
+ nsILanguageAtomService* langService = GetLangService();
+ nsIAtom *atom = langService->LookupLanguage(aFcLang);
+ return atom == aLangGroup;
+}
+
+void
+gfxPlatformFontList::GetSampleLangForGroup(nsIAtom* aLanguage,
+ nsACString& aLangStr,
+ bool aCheckEnvironment)
+{
+ aLangStr.Truncate();
+ if (!aLanguage) {
+ return;
+ }
+
+ // set up lang string
+ const MozLangGroupData *mozLangGroup = nullptr;
+
+ // -- look it up in the list of moz lang groups
+ for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
+ if (aLanguage == MozLangGroups[i].mozLangGroup) {
+ mozLangGroup = &MozLangGroups[i];
+ break;
+ }
+ }
+
+ // -- not a mozilla lang group? Just return the BCP47 string
+ // representation of the lang group
+ if (!mozLangGroup) {
+ // Not a special mozilla language group.
+ // Use aLanguage as a language code.
+ aLanguage->ToUTF8String(aLangStr);
+ return;
+ }
+
+ // -- check the environment for the user's preferred language that
+ // corresponds to this mozilla lang group.
+ if (aCheckEnvironment) {
+ const char *languages = getenv("LANGUAGE");
+ if (languages) {
+ const char separator = ':';
+
+ for (const char *pos = languages; true; ++pos) {
+ if (*pos == '\0' || *pos == separator) {
+ if (languages < pos &&
+ TryLangForGroup(Substring(languages, pos),
+ aLanguage, aLangStr))
+ return;
+
+ if (*pos == '\0')
+ break;
+
+ languages = pos + 1;
+ }
+ }
+ }
+ const char *ctype = setlocale(LC_CTYPE, nullptr);
+ if (ctype &&
+ TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr)) {
+ return;
+ }
+ }
+
+ if (mozLangGroup->defaultLang) {
+ aLangStr.Assign(mozLangGroup->defaultLang);
+ } else {
+ aLangStr.Truncate();
+ }
+}
+
+void
+gfxPlatformFontList::InitLoader()
+{
+ GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
+ mStartIndex = 0;
+ mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
+ memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
+}
+
+#define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms
+
+bool
+gfxPlatformFontList::LoadFontInfo()
+{
+ TimeStamp start = TimeStamp::Now();
+ uint32_t i, endIndex = mNumFamilies;
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ // for each font family, load in various font info
+ for (i = mStartIndex; i < endIndex; i++) {
+ nsAutoString key;
+ gfxFontFamily *familyEntry;
+ GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
+
+ // lookup in canonical (i.e. English) family name list
+ if (!(familyEntry = mFontFamilies.GetWeak(key))) {
+ continue;
+ }
+
+ // read in face names
+ familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo);
+
+ // load the cmaps if needed
+ if (loadCmaps) {
+ familyEntry->ReadAllCMAPs(mFontInfo);
+ }
+
+ // limit the time spent reading fonts in one pass
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
+ i + 1 != endIndex) {
+ endIndex = i + 1;
+ break;
+ }
+ }
+
+ mStartIndex = endIndex;
+ bool done = mStartIndex >= mNumFamilies;
+
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
+ elapsed.ToMilliseconds(), (done ? "true" : "false")));
+ }
+
+ if (done) {
+ mOtherFamilyNamesInitialized = true;
+ mFaceNameListsInitialized = true;
+ }
+
+ return done;
+}
+
+void
+gfxPlatformFontList::CleanupLoader()
+{
+ mFontFamiliesToLoad.Clear();
+ mNumFamilies = 0;
+ bool rebuilt = false, forceReflow = false;
+
+ // if had missed face names that are now available, force reflow all
+ if (mFaceNamesMissed) {
+ for (auto it = mFaceNamesMissed->Iter(); !it.Done(); it.Next()) {
+ if (FindFaceName(it.Get()->GetKey())) {
+ rebuilt = true;
+ RebuildLocalFonts();
+ break;
+ }
+ }
+ mFaceNamesMissed = nullptr;
+ }
+
+ if (mOtherNamesMissed) {
+ for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) {
+ if (FindFamily(it.Get()->GetKey())) {
+ forceReflow = true;
+ ForceGlobalReflow();
+ break;
+ }
+ }
+ mOtherNamesMissed = nullptr;
+ }
+
+ if (LOG_FONTINIT_ENABLED() && mFontInfo) {
+ LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
+ "%d families %d fonts %d cmaps "
+ "%d facenames %d othernames %s %s",
+ mLoadTime.ToMilliseconds(),
+ mFontInfo->mLoadStats.families,
+ mFontInfo->mLoadStats.fonts,
+ mFontInfo->mLoadStats.cmaps,
+ mFontInfo->mLoadStats.facenames,
+ mFontInfo->mLoadStats.othernames,
+ (rebuilt ? "(userfont sets rebuilt)" : ""),
+ (forceReflow ? "(global reflow)" : "")));
+ }
+
+ gfxFontInfoLoader::CleanupLoader();
+}
+
+void
+gfxPlatformFontList::GetPrefsAndStartLoader()
+{
+ mIncrement =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF));
+
+ uint32_t delay =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF));
+ uint32_t interval =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
+
+ StartLoader(delay, interval);
+}
+
+void
+gfxPlatformFontList::ForceGlobalReflow()
+{
+ // modify a preference that will trigger reflow everywhere
+ static const char kPrefName[] = "font.internaluseonly.changed";
+ bool fontInternalChange = Preferences::GetBool(kPrefName, false);
+ Preferences::SetBool(kPrefName, !fontInternalChange);
+}
+
+void
+gfxPlatformFontList::RebuildLocalFonts()
+{
+ for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) {
+ it.Get()->GetKey()->RebuildLocalRules();
+ }
+}
+
+void
+gfxPlatformFontList::ClearLangGroupPrefFonts()
+{
+ for (uint32_t i = eFontPrefLang_First;
+ i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+ auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+ for (uint32_t j = eFamily_generic_first;
+ j < eFamily_generic_first + eFamily_generic_count; j++) {
+ prefFontsLangGroup[j] = nullptr;
+ }
+ }
+}
+
+// Support for memory reporting
+
+// this is also used by subclasses that hold additional font tables
+/*static*/ size_t
+gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
+ const FontFamilyTable& aTable,
+ MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ // We don't count the size of the family here, because this is an
+ // *extra* reference to a family that will have already been counted in
+ // the main list.
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+ return n;
+}
+
+/*static*/ size_t
+gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
+ const FontEntryTable& aTable,
+ MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ // The font itself is counted by its owning family; here we only care
+ // about the names stored in the hashtable keys.
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+ return n;
+}
+
+void
+gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize +=
+ mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) {
+ aSizes->mFontListSize +=
+ iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ iter.Data()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
+ }
+
+ aSizes->mFontListSize +=
+ SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
+
+ if (mExtraNames) {
+ aSizes->mFontListSize +=
+ SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames,
+ aMallocSizeOf);
+ aSizes->mFontListSize +=
+ SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames,
+ aMallocSizeOf);
+ }
+
+ for (uint32_t i = eFontPrefLang_First;
+ i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+ auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+ for (uint32_t j = eFamily_generic_first;
+ j < eFamily_generic_first + eFamily_generic_count; j++) {
+ PrefFontList* pf = prefFontsLangGroup[j].get();
+ if (pf) {
+ aSizes->mFontListSize +=
+ pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ }
+ }
+
+ aSizes->mFontListSize +=
+ mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
+ aSizes->mFontListSize +=
+ mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
+ aSizes->mCharMapsSize +=
+ iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+}
+
+void
+gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+bool
+gfxPlatformFontList::IsFontFamilyWhitelistActive()
+{
+ return mFontFamilyWhitelistActive;
+}
+
+#undef LOG
+#undef LOG_ENABLED
diff --git a/system/graphics/thebes/gfxPlatformFontList.h b/system/graphics/thebes/gfxPlatformFontList.h
new file mode 100644
index 000000000..d77c12059
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatformFontList.h
@@ -0,0 +1,482 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFXPLATFORMFONTLIST_H_
+#define GFXPLATFORMFONTLIST_H_
+
+#include "nsDataHashtable.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTHashtable.h"
+
+#include "gfxFontUtils.h"
+#include "gfxFontInfoLoader.h"
+#include "gfxFont.h"
+#include "gfxFontConstants.h"
+#include "gfxPlatform.h"
+#include "gfxFontFamilyList.h"
+
+#include "nsIMemoryReporter.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/RangedArray.h"
+#include "nsILanguageAtomService.h"
+
+class CharMapHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef gfxCharacterMap* KeyType;
+ typedef const gfxCharacterMap* KeyTypePointer;
+
+ explicit CharMapHashKey(const gfxCharacterMap *aCharMap) :
+ mCharMap(const_cast<gfxCharacterMap*>(aCharMap))
+ {
+ MOZ_COUNT_CTOR(CharMapHashKey);
+ }
+ CharMapHashKey(const CharMapHashKey& toCopy) :
+ mCharMap(toCopy.mCharMap)
+ {
+ MOZ_COUNT_CTOR(CharMapHashKey);
+ }
+ ~CharMapHashKey()
+ {
+ MOZ_COUNT_DTOR(CharMapHashKey);
+ }
+
+ gfxCharacterMap* GetKey() const { return mCharMap; }
+
+ bool KeyEquals(const gfxCharacterMap *aCharMap) const {
+ NS_ASSERTION(!aCharMap->mBuildOnTheFly && !mCharMap->mBuildOnTheFly,
+ "custom cmap used in shared cmap hashtable");
+ // cmaps built on the fly never match
+ if (aCharMap->mHash != mCharMap->mHash)
+ {
+ return false;
+ }
+ return mCharMap->Equals(aCharMap);
+ }
+
+ static const gfxCharacterMap* KeyToPointer(gfxCharacterMap *aCharMap) {
+ return aCharMap;
+ }
+ static PLDHashNumber HashKey(const gfxCharacterMap *aCharMap) {
+ return aCharMap->mHash;
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+protected:
+ gfxCharacterMap *mCharMap;
+};
+
+// gfxPlatformFontList is an abstract class for the global font list on the system;
+// concrete subclasses for each platform implement the actual interface to the system fonts.
+// This class exists because we cannot rely on the platform font-finding APIs to behave
+// in sensible/similar ways, particularly with rich, complex OpenType families,
+// so we do our own font family/style management here instead.
+
+// Much of this is based on the old gfxQuartzFontCache, but adapted for use on all platforms.
+
+struct FontListSizes {
+ uint32_t mFontListSize; // size of the font list and dependent objects
+ // (font family and face names, etc), but NOT
+ // including the font table cache and the cmaps
+ uint32_t mFontTableCacheSize; // memory used for the gfxFontEntry table caches
+ uint32_t mCharMapsSize; // memory used for cmap coverage info
+};
+
+class gfxUserFontSet;
+
+class gfxPlatformFontList : public gfxFontInfoLoader
+{
+public:
+ typedef mozilla::unicode::Script Script;
+
+ static gfxPlatformFontList* PlatformFontList() {
+ return sPlatformFontList;
+ }
+
+ static nsresult Init() {
+ NS_ASSERTION(!sPlatformFontList, "What's this doing here?");
+ gfxPlatform::GetPlatform()->CreatePlatformFontList();
+ if (!sPlatformFontList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+ }
+
+ static void Shutdown() {
+ delete sPlatformFontList;
+ sPlatformFontList = nullptr;
+ }
+
+ virtual ~gfxPlatformFontList();
+
+ // initialize font lists
+ nsresult InitFontList();
+
+ virtual void GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts);
+
+ void UpdateFontList();
+
+ virtual void ClearLangGroupPrefFonts();
+
+ virtual void GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray);
+
+ gfxFontEntry*
+ SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aStyle);
+
+ // Find family(ies) matching aFamily and append to the aOutput array
+ // (there may be multiple results in the case of fontconfig aliases, etc).
+ // Return true if any match was found and appended, false if none.
+ virtual bool
+ FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0);
+
+ gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
+
+ // name lookup table methods
+
+ void AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName);
+
+ void AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname);
+
+ void AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName);
+
+ bool NeedFullnamePostscriptNames() { return mExtraNames != nullptr; }
+
+ // pure virtual functions, to be provided by concrete subclasses
+
+ // get the system default font family
+ gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+
+ // look up a font by name on the host platform
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle) = 0;
+
+ // create a new platform font from downloaded data (@font-face)
+ // this method is responsible to ensure aFontData is free()'d
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength) = 0;
+
+ // get the standard family name on the platform for a given font name
+ // (platforms may override, eg Mac)
+ virtual bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const;
+
+ // search for existing cmap that matches the input
+ // return the input if no match is found
+ gfxCharacterMap* FindCharMap(gfxCharacterMap *aCmap);
+
+ // add a cmap to the shared cmap set
+ gfxCharacterMap* AddCmap(const gfxCharacterMap *aCharMap);
+
+ // remove the cmap from the shared cmap set
+ void RemoveCmap(const gfxCharacterMap *aCharMap);
+
+ // keep track of userfont sets to notify when global fontlist changes occur
+ void AddUserFontSet(gfxUserFontSet *aUserFontSet) {
+ mUserFontSetList.PutEntry(aUserFontSet);
+ }
+
+ void RemoveUserFontSet(gfxUserFontSet *aUserFontSet) {
+ mUserFontSetList.RemoveEntry(aUserFontSet);
+ }
+
+ static const gfxFontEntry::ScriptRange sComplexScriptRanges[];
+
+ void GetFontlistInitInfo(uint32_t& aNumInits, uint32_t& aLoaderState) {
+ aNumInits = mFontlistInitCount;
+ aLoaderState = (uint32_t) mState;
+ }
+
+ virtual void
+ AddGenericFonts(mozilla::FontFamilyType aGenericType,
+ nsIAtom* aLanguage,
+ nsTArray<gfxFontFamily*>& aFamilyList);
+
+ nsTArray<RefPtr<gfxFontFamily>>*
+ GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang);
+
+ // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs
+ void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang);
+
+ // convert a lang group to enum constant (i.e. "zh-TW" ==> eFontPrefLang_ChineseTW)
+ static eFontPrefLang GetFontPrefLangFor(const char* aLang);
+
+ // convert a lang group atom to enum constant
+ static eFontPrefLang GetFontPrefLangFor(nsIAtom *aLang);
+
+ // convert an enum constant to a lang group atom
+ static nsIAtom* GetLangGroupForPrefLang(eFontPrefLang aLang);
+
+ // convert a enum constant to lang group string (i.e. eFontPrefLang_ChineseTW ==> "zh-TW")
+ static const char* GetPrefLangName(eFontPrefLang aLang);
+
+ // map a Unicode range (based on char code) to a font language for Preferences
+ static eFontPrefLang GetFontPrefLangFor(uint8_t aUnicodeRange);
+
+ // returns true if a pref lang is CJK
+ static bool IsLangCJK(eFontPrefLang aLang);
+
+ // helper method to add a pref lang to an array, if not already in array
+ static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang);
+
+ // default serif/sans-serif choice based on font.default.xxx prefs
+ mozilla::FontFamilyType
+ GetDefaultGeneric(eFontPrefLang aLang);
+
+ // map lang group ==> lang string
+ void GetSampleLangForGroup(nsIAtom* aLanguage, nsACString& aLangStr,
+ bool aCheckEnvironment = true);
+
+ // Returns true if the font family whitelist is not empty.
+ bool IsFontFamilyWhitelistActive();
+
+ static void FontWhitelistPrefChanged(const char *aPref, void *aClosure) {
+ gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+ }
+
+protected:
+ class MemoryReporter final : public nsIMemoryReporter
+ {
+ ~MemoryReporter() {}
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+ };
+
+ explicit gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true);
+
+ static gfxPlatformFontList *sPlatformFontList;
+
+ // Convenience method to return the first matching family (if any) as found
+ // by FindAndAddFamilies().
+ gfxFontFamily*
+ FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0)
+ {
+ AutoTArray<gfxFontFamily*,1> families;
+ return FindAndAddFamilies(aFamily, &families, aStyle, aDevToCssSize)
+ ? families[0] : nullptr;
+ }
+
+ // Lookup family name in global family list without substitutions or
+ // localized family name lookup. Used for common font fallback families.
+ gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) {
+ nsAutoString key;
+ gfxFontFamily *familyEntry;
+ GenerateFontListKey(aFamily, key);
+ if ((familyEntry = mFontFamilies.GetWeak(key))) {
+ return CheckFamily(familyEntry);
+ }
+ return nullptr;
+ }
+
+ // returns default font for a given character, null otherwise
+ gfxFontEntry* CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily);
+
+ // Search fonts system-wide for a given character, null if not found.
+ gfxFontEntry* GlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ uint32_t& aCmapCount,
+ gfxFontFamily** aMatchedFamily);
+
+ // Platform-specific implementation of global font fallback, if any;
+ // this may return nullptr in which case the default cmap-based fallback
+ // will be performed.
+ virtual gfxFontEntry*
+ PlatformGlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily)
+ {
+ return nullptr;
+ }
+
+ // whether system-based font fallback is used or not
+ // if system fallback is used, no need to load all cmaps
+ virtual bool UsesSystemFallback() { return false; }
+
+ void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
+ eFontPrefLang aCharLang, eFontPrefLang aPageLang);
+
+ // verifies that a family contains a non-zero font count
+ gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
+
+ // initialize localized family names
+ void InitOtherFamilyNames();
+
+ // search through font families, looking for a given name, initializing
+ // facename lists along the way. first checks all families with names
+ // close to face name, then searchs all families if not found.
+ gfxFontEntry* SearchFamiliesForFaceName(const nsAString& aFaceName);
+
+ // helper method for finding fullname/postscript names in facename lists
+ gfxFontEntry* FindFaceName(const nsAString& aFaceName);
+
+ // look up a font by name, for cases where platform font list
+ // maintains explicit mappings of fullname/psname ==> font
+ virtual gfxFontEntry* LookupInFaceNameLists(const nsAString& aFontName);
+
+ // commonly used fonts for which the name table should be loaded at startup
+ virtual void PreloadNamesList();
+
+ // load the bad underline blacklist from pref.
+ void LoadBadUnderlineList();
+
+ void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult);
+
+ virtual void GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames);
+
+ nsILanguageAtomService* GetLangService();
+
+ // helper function to map lang to lang group
+ nsIAtom* GetLangGroup(nsIAtom* aLanguage);
+
+ // helper method for finding an appropriate lang string
+ bool TryLangForGroup(const nsACString& aOSLang, nsIAtom* aLangGroup,
+ nsACString& aLang);
+
+ static const char* GetGenericName(mozilla::FontFamilyType aGenericType);
+
+ // gfxFontInfoLoader overrides, used to load in font cmaps
+ virtual void InitLoader();
+ virtual bool LoadFontInfo();
+ virtual void CleanupLoader();
+
+ // read the loader initialization prefs, and start it
+ void GetPrefsAndStartLoader();
+
+ // for font list changes that affect all documents
+ void ForceGlobalReflow();
+
+ void RebuildLocalFonts();
+
+ void
+ ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang,
+ nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
+
+ void
+ ResolveEmojiFontNames(nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
+
+ void
+ GetFontFamiliesFromGenericFamilies(
+ nsTArray<nsString>& aGenericFamilies,
+ nsIAtom* aLangGroup,
+ nsTArray<RefPtr<gfxFontFamily>>* aFontFamilies);
+
+ virtual nsresult InitFontListForPlatform() = 0;
+
+ void ApplyWhitelist();
+
+ typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontFamilyTable;
+ typedef nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> FontEntryTable;
+
+ // used by memory reporter to accumulate sizes of family names in the table
+ static size_t
+ SizeOfFontFamilyTableExcludingThis(const FontFamilyTable& aTable,
+ mozilla::MallocSizeOf aMallocSizeOf);
+ static size_t
+ SizeOfFontEntryTableExcludingThis(const FontEntryTable& aTable,
+ mozilla::MallocSizeOf aMallocSizeOf);
+
+ // Platform-specific helper for GetDefaultFont(...).
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) = 0;
+
+ // canonical family name ==> family entry (unique, one name per family entry)
+ FontFamilyTable mFontFamilies;
+
+ // other family name ==> family entry (not unique, can have multiple names per
+ // family entry, only names *other* than the canonical names are stored here)
+ FontFamilyTable mOtherFamilyNames;
+
+ // flag set after InitOtherFamilyNames is called upon first name lookup miss
+ bool mOtherFamilyNamesInitialized;
+
+ // flag set after fullname and Postcript name lists are populated
+ bool mFaceNameListsInitialized;
+
+ struct ExtraNames {
+ ExtraNames() : mFullnames(64), mPostscriptNames(64) {}
+
+ // fullname ==> font entry (unique, one name per font entry)
+ FontEntryTable mFullnames;
+ // Postscript name ==> font entry (unique, one name per font entry)
+ FontEntryTable mPostscriptNames;
+ };
+ mozilla::UniquePtr<ExtraNames> mExtraNames;
+
+ // face names missed when face name loading takes a long time
+ mozilla::UniquePtr<nsTHashtable<nsStringHashKey> > mFaceNamesMissed;
+
+ // localized family names missed when face name loading takes a long time
+ mozilla::UniquePtr<nsTHashtable<nsStringHashKey> > mOtherNamesMissed;
+
+ typedef nsTArray<RefPtr<gfxFontFamily>> PrefFontList;
+ typedef mozilla::RangedArray<mozilla::UniquePtr<PrefFontList>,
+ mozilla::eFamily_generic_first,
+ mozilla::eFamily_generic_count> PrefFontsForLangGroup;
+ mozilla::RangedArray<PrefFontsForLangGroup,
+ eFontPrefLang_First,
+ eFontPrefLang_Count> mLangGroupPrefFonts;
+
+ mozilla::UniquePtr<PrefFontList> mEmojiPrefFont;
+
+ // when system-wide font lookup fails for a character, cache it to skip future searches
+ gfxSparseBitSet mCodepointsWithNoFonts;
+
+ // the family to use for U+FFFD fallback, to avoid expensive search every time
+ // on pages with lots of problems
+ RefPtr<gfxFontFamily> mReplacementCharFallbackFamily;
+
+ nsTHashtable<nsStringHashKey> mBadUnderlineFamilyNames;
+
+ // character map data shared across families
+ // contains weak ptrs to cmaps shared by font entry objects
+ nsTHashtable<CharMapHashKey> mSharedCmaps;
+
+ // data used as part of the font cmap loading process
+ nsTArray<RefPtr<gfxFontFamily> > mFontFamiliesToLoad;
+ uint32_t mStartIndex;
+ uint32_t mIncrement;
+ uint32_t mNumFamilies;
+
+ // xxx - info for diagnosing no default font aborts
+ // see bugs 636957, 1070983, 1189129
+ uint32_t mFontlistInitCount; // num times InitFontList called
+
+ nsTHashtable<nsPtrHashKey<gfxUserFontSet> > mUserFontSetList;
+
+ nsCOMPtr<nsILanguageAtomService> mLangService;
+ nsTArray<uint32_t> mCJKPrefLangs;
+ nsTArray<mozilla::FontFamilyType> mDefaultGenericsLangGroup;
+
+ bool mFontFamilyWhitelistActive;
+};
+
+#endif /* GFXPLATFORMFONTLIST_H_ */
diff --git a/system/graphics/thebes/gfxPlatformGtk.cpp b/system/graphics/thebes/gfxPlatformGtk.cpp
new file mode 100644
index 000000000..6b5593524
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatformGtk.cpp
@@ -0,0 +1,899 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_ENGINE
+
+#include "gfxPlatformGtk.h"
+#include "prenv.h"
+
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "gfx2DGlue.h"
+#include "gfxFcPlatformFontList.h"
+#include "gfxFontconfigUtils.h"
+#include "gfxFontconfigFonts.h"
+#include "gfxConfig.h"
+#include "gfxContext.h"
+#include "gfxUserFontSet.h"
+#include "gfxUtils.h"
+#include "gfxFT2FontBase.h"
+#include "gfxPrefs.h"
+#include "VsyncSource.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Monitor.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/message_loop.h"
+#include "mozilla/gfx/Logging.h"
+
+#include "mozilla/gfx/2D.h"
+
+#include "cairo.h"
+#include <gtk/gtk.h>
+
+#include "gfxImageSurface.h"
+#ifdef MOZ_X11
+#include <gdk/gdkx.h>
+#include "gfxXlibSurface.h"
+#include "cairo-xlib.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/X11Util.h"
+
+#ifdef GL_PROVIDER_GLX
+#include "GLContextProvider.h"
+#include "GLContextGLX.h"
+#include "GLXLibrary.h"
+#endif
+
+/* Undefine the Status from Xlib since it will conflict with system headers on OSX */
+#if defined(__APPLE__) && defined(Status)
+#undef Status
+#endif
+
+#endif /* MOZ_X11 */
+
+#include <fontconfig/fontconfig.h>
+
+#include "nsMathUtils.h"
+
+#define GDK_PIXMAP_SIZE_MAX 32767
+
+#define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS "gfx.font_rendering.fontconfig.max_generic_substitutions"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+
+gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nullptr;
+
+#if (MOZ_WIDGET_GTK == 2)
+static cairo_user_data_key_t cairo_gdk_drawable_key;
+#endif
+
+bool gfxPlatformGtk::sUseFcFontList = false;
+
+gfxPlatformGtk::gfxPlatformGtk()
+{
+ gtk_init(nullptr, nullptr);
+
+ sUseFcFontList = mozilla::Preferences::GetBool("gfx.font_rendering.fontconfig.fontlist.enabled");
+ if (!sUseFcFontList && !sFontconfigUtils) {
+ sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils();
+ }
+
+ mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
+
+#ifdef MOZ_X11
+ if (XRE_IsParentProcess()) {
+ if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
+ mozilla::Preferences::GetBool("gfx.xrender.enabled"))
+ {
+ gfxVars::SetUseXRender(true);
+ }
+ }
+#endif
+
+ uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+ uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+#ifdef USE_SKIA
+ canvasMask |= BackendTypeBit(BackendType::SKIA);
+ contentMask |= BackendTypeBit(BackendType::SKIA);
+#endif
+ InitBackendPrefs(canvasMask, BackendType::CAIRO,
+ contentMask, BackendType::CAIRO);
+
+#ifdef MOZ_X11
+ if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ mCompositorDisplay = XOpenDisplay(nullptr);
+ MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
+ } else {
+ mCompositorDisplay = nullptr;
+ }
+#endif // MOZ_X11
+}
+
+gfxPlatformGtk::~gfxPlatformGtk()
+{
+ if (!sUseFcFontList) {
+ gfxFontconfigUtils::Shutdown();
+ sFontconfigUtils = nullptr;
+ gfxPangoFontGroup::Shutdown();
+ }
+
+#ifdef MOZ_X11
+ if (mCompositorDisplay) {
+ XCloseDisplay(mCompositorDisplay);
+ }
+#endif // MOZ_X11
+}
+
+void
+gfxPlatformGtk::FlushContentDrawing()
+{
+ if (gfxVars::UseXRender()) {
+ XFlush(DefaultXDisplay());
+ }
+}
+
+already_AddRefed<gfxASurface>
+gfxPlatformGtk::CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat)
+{
+ if (!Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> newSurface;
+ bool needsClear = true;
+#ifdef MOZ_X11
+ // XXX we really need a different interface here, something that passes
+ // in more context, including the display and/or target surface type that
+ // we should try to match
+ GdkScreen *gdkScreen = gdk_screen_get_default();
+ if (gdkScreen) {
+ // When forcing PaintedLayers to use image surfaces for content,
+ // force creation of gfxImageSurface surfaces.
+ if (gfxVars::UseXRender()) {
+ Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen);
+ XRenderPictFormat* xrenderFormat =
+ gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen),
+ aFormat);
+
+ if (xrenderFormat) {
+ newSurface = gfxXlibSurface::Create(screen, xrenderFormat,
+ aSize);
+ }
+ }
+ }
+#endif
+
+ if (!newSurface) {
+ // We couldn't create a native surface for whatever reason;
+ // e.g., no display, no RENDER, bad size, etc.
+ // Fall back to image surface for the data.
+ newSurface = new gfxImageSurface(aSize, aFormat);
+
+ // The gfxImageSurface ctor zeroes this for us, no need to
+ // waste time clearing again
+ needsClear = false;
+ }
+
+ if (newSurface->CairoStatus()) {
+ newSurface = nullptr; // surface isn't valid for some reason
+ }
+
+ if (newSurface && needsClear) {
+ gfxUtils::ClearThebesSurface(newSurface);
+ }
+
+ return newSurface.forget();
+}
+
+nsresult
+gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ if (sUseFcFontList) {
+ gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
+ aGenericFamily,
+ aListOfFonts);
+ return NS_OK;
+ }
+
+ return sFontconfigUtils->GetFontList(aLangGroup,
+ aGenericFamily,
+ aListOfFonts);
+}
+
+nsresult
+gfxPlatformGtk::UpdateFontList()
+{
+ if (sUseFcFontList) {
+ gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+ return NS_OK;
+ }
+
+ return sFontconfigUtils->UpdateFontList();
+}
+
+// xxx - this is ubuntu centric, need to go through other distros and flesh
+// out a more general list
+static const char kFontDejaVuSans[] = "DejaVu Sans";
+static const char kFontDejaVuSerif[] = "DejaVu Serif";
+static const char kFontFreeSans[] = "FreeSans";
+static const char kFontFreeSerif[] = "FreeSerif";
+static const char kFontTakaoPGothic[] = "TakaoPGothic";
+static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
+static const char kFontDroidSansFallback[] = "Droid Sans Fallback";
+static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
+static const char kFontNanumGothic[] = "NanumGothic";
+
+void
+gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList)
+{
+ EmojiPresentation emoji = GetEmojiPresentation(aCh);
+ if (emoji != EmojiPresentation::TextOnly) {
+ if (aNextCh == kVariationSelector16 ||
+ (aNextCh != kVariationSelector15 &&
+ emoji == EmojiPresentation::EmojiDefault)) {
+ // if char is followed by VS16, try for a color emoji glyph
+ aFontList.AppendElement(kFontTwemojiMozilla);
+ }
+ }
+
+ aFontList.AppendElement(kFontDejaVuSerif);
+ aFontList.AppendElement(kFontFreeSerif);
+ aFontList.AppendElement(kFontDejaVuSans);
+ aFontList.AppendElement(kFontFreeSans);
+
+ // add fonts for CJK ranges
+ // xxx - this isn't really correct, should use the same CJK font ordering
+ // as the pref font code
+ if (aCh >= 0x3000 &&
+ ((aCh < 0xe000) ||
+ (aCh >= 0xf900 && aCh < 0xfff0) ||
+ ((aCh >> 16) == 2))) {
+ aFontList.AppendElement(kFontTakaoPGothic);
+ aFontList.AppendElement(kFontDroidSansFallback);
+ aFontList.AppendElement(kFontWenQuanYiMicroHei);
+ aFontList.AppendElement(kFontNanumGothic);
+ }
+}
+
+gfxPlatformFontList*
+gfxPlatformGtk::CreatePlatformFontList()
+{
+ gfxPlatformFontList* list = new gfxFcPlatformFontList();
+ if (NS_SUCCEEDED(list->InitFontList())) {
+ return list;
+ }
+ gfxPlatformFontList::Shutdown();
+ return nullptr;
+}
+
+nsresult
+gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+ if (sUseFcFontList) {
+ gfxPlatformFontList::PlatformFontList()->
+ GetStandardFamilyName(aFontName, aFamilyName);
+ return NS_OK;
+ }
+
+ return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
+}
+
+gfxFontGroup *
+gfxPlatformGtk::CreateFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle* aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet* aUserFontSet,
+ gfxFloat aDevToCssSize)
+{
+ if (sUseFcFontList) {
+ return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
+ aUserFontSet, aDevToCssSize);
+ }
+
+ return new gfxPangoFontGroup(aFontFamilyList, aStyle,
+ aUserFontSet, aDevToCssSize);
+}
+
+gfxFontEntry*
+gfxPlatformGtk::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ if (sUseFcFontList) {
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+ return pfl->LookupLocalFont(aFontName, aWeight, aStretch,
+ aStyle);
+ }
+
+ return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight,
+ aStretch, aStyle);
+}
+
+gfxFontEntry*
+gfxPlatformGtk::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ if (sUseFcFontList) {
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+ return pfl->MakePlatformFont(aFontName, aWeight, aStretch,
+ aStyle, aFontData, aLength);
+ }
+
+ // passing ownership of the font data to the new font entry
+ return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight,
+ aStretch, aStyle,
+ aFontData, aLength);
+}
+
+bool
+gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
+{
+ // check for strange format flags
+ NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
+ "strange font format hint set");
+
+ // accept supported formats
+ // Pango doesn't apply features from AAT TrueType extensions.
+ // Assume that if this is the only SFNT format specified,
+ // then AAT extensions are required for complex script support.
+ if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) {
+ return true;
+ }
+
+ // reject all other formats, known and unknown
+ if (aFormatFlags != 0) {
+ return false;
+ }
+
+ // no format hint set, need to look at data
+ return true;
+}
+
+static int32_t sDPI = 0;
+
+int32_t
+gfxPlatformGtk::GetDPI()
+{
+ if (!sDPI) {
+ // Make sure init is run so we have a resolution
+ GdkScreen *screen = gdk_screen_get_default();
+ gtk_settings_get_for_screen(screen);
+ sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
+ if (sDPI <= 0) {
+ // Fall back to something sane
+ sDPI = 96;
+ }
+ }
+ return sDPI;
+}
+
+double
+gfxPlatformGtk::GetDPIScale()
+{
+ // Integer scale factors work well with GTK window scaling, image scaling,
+ // and pixel alignment, but there is a range where 1 is too small and 2 is
+ // too big. An additional step of 1.5 is added because this is common
+ // scale on WINNT and at this ratio the advantages of larger rendering
+ // outweigh the disadvantages from scaling and pixel mis-alignment.
+ int32_t dpi = GetDPI();
+ if (dpi < 144) {
+ return 1.0;
+ } else if (dpi < 168) {
+ return 1.5;
+ } else {
+ return round(dpi/96.0);
+ }
+}
+
+bool
+gfxPlatformGtk::UseImageOffscreenSurfaces()
+{
+ return GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO ||
+ gfxPrefs::UseImageOffscreenSurfaces();
+}
+
+gfxImageFormat
+gfxPlatformGtk::GetOffscreenFormat()
+{
+ // Make sure there is a screen
+ GdkScreen *screen = gdk_screen_get_default();
+ if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
+ return SurfaceFormat::R5G6B5_UINT16;
+ }
+
+ return SurfaceFormat::X8R8G8B8_UINT32;
+}
+
+void gfxPlatformGtk::FontsPrefsChanged(const char *aPref)
+{
+ // only checking for generic substitions, pass other changes up
+ if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, aPref)) {
+ gfxPlatform::FontsPrefsChanged(aPref);
+ return;
+ }
+
+ mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
+ if (sUseFcFontList) {
+ gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
+ pfl->ClearGenericMappings();
+ FlushFontAndWordCaches();
+ }
+}
+
+uint32_t gfxPlatformGtk::MaxGenericSubstitions()
+{
+ if (mMaxGenericSubstitutions == UNINITIALIZED_VALUE) {
+ mMaxGenericSubstitutions =
+ Preferences::GetInt(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, 3);
+ if (mMaxGenericSubstitutions < 0) {
+ mMaxGenericSubstitutions = 3;
+ }
+ }
+
+ return uint32_t(mMaxGenericSubstitutions);
+}
+
+void
+gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
+{
+ mem = nullptr;
+ size = 0;
+
+#ifdef MOZ_X11
+ GdkDisplay *display = gdk_display_get_default();
+ if (!GDK_IS_X11_DISPLAY(display))
+ return;
+
+ const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
+ const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
+
+ Atom edidAtom, iccAtom;
+ Display *dpy = GDK_DISPLAY_XDISPLAY(display);
+ // In xpcshell tests, we never initialize X and hence don't have a Display.
+ // In this case, there's no output colour management to be done, so we just
+ // return with nullptr.
+ if (!dpy)
+ return;
+
+ Window root = gdk_x11_get_default_root_xwindow();
+
+ Atom retAtom;
+ int retFormat;
+ unsigned long retLength, retAfter;
+ unsigned char *retProperty ;
+
+ iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
+ if (iccAtom) {
+ // read once to get size, once for the data
+ if (Success == XGetWindowProperty(dpy, root, iccAtom,
+ 0, INT_MAX /* length */,
+ False, AnyPropertyType,
+ &retAtom, &retFormat, &retLength,
+ &retAfter, &retProperty)) {
+
+ if (retLength > 0) {
+ void *buffer = malloc(retLength);
+ if (buffer) {
+ memcpy(buffer, retProperty, retLength);
+ mem = buffer;
+ size = retLength;
+ }
+ }
+
+ XFree(retProperty);
+ if (size > 0) {
+#ifdef DEBUG_tor
+ fprintf(stderr,
+ "ICM profile read from %s successfully\n",
+ ICC_PROFILE_ATOM_NAME);
+#endif
+ return;
+ }
+ }
+ }
+
+ edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
+ if (edidAtom) {
+ if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
+ False, AnyPropertyType,
+ &retAtom, &retFormat, &retLength,
+ &retAfter, &retProperty)) {
+ double gamma;
+ qcms_CIE_xyY whitePoint;
+ qcms_CIE_xyYTRIPLE primaries;
+
+ if (retLength != 128) {
+#ifdef DEBUG_tor
+ fprintf(stderr, "Short EDID data\n");
+#endif
+ return;
+ }
+
+ // Format documented in "VESA E-EDID Implementation Guide"
+
+ gamma = (100 + retProperty[0x17]) / 100.0;
+ whitePoint.x = ((retProperty[0x21] << 2) |
+ (retProperty[0x1a] >> 2 & 3)) / 1024.0;
+ whitePoint.y = ((retProperty[0x22] << 2) |
+ (retProperty[0x1a] >> 0 & 3)) / 1024.0;
+ whitePoint.Y = 1.0;
+
+ primaries.red.x = ((retProperty[0x1b] << 2) |
+ (retProperty[0x19] >> 6 & 3)) / 1024.0;
+ primaries.red.y = ((retProperty[0x1c] << 2) |
+ (retProperty[0x19] >> 4 & 3)) / 1024.0;
+ primaries.red.Y = 1.0;
+
+ primaries.green.x = ((retProperty[0x1d] << 2) |
+ (retProperty[0x19] >> 2 & 3)) / 1024.0;
+ primaries.green.y = ((retProperty[0x1e] << 2) |
+ (retProperty[0x19] >> 0 & 3)) / 1024.0;
+ primaries.green.Y = 1.0;
+
+ primaries.blue.x = ((retProperty[0x1f] << 2) |
+ (retProperty[0x1a] >> 6 & 3)) / 1024.0;
+ primaries.blue.y = ((retProperty[0x20] << 2) |
+ (retProperty[0x1a] >> 4 & 3)) / 1024.0;
+ primaries.blue.Y = 1.0;
+
+ XFree(retProperty);
+
+#ifdef DEBUG_tor
+ fprintf(stderr, "EDID gamma: %f\n", gamma);
+ fprintf(stderr, "EDID whitepoint: %f %f %f\n",
+ whitePoint.x, whitePoint.y, whitePoint.Y);
+ fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
+ primaries.Red.x, primaries.Red.y, primaries.Red.Y,
+ primaries.Green.x, primaries.Green.y, primaries.Green.Y,
+ primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
+#endif
+
+ qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
+
+#ifdef DEBUG_tor
+ if (size > 0) {
+ fprintf(stderr,
+ "ICM profile read from %s successfully\n",
+ EDID1_ATOM_NAME);
+ }
+#endif
+ }
+ }
+#endif
+}
+
+
+#if (MOZ_WIDGET_GTK == 2)
+void
+gfxPlatformGtk::SetGdkDrawable(cairo_surface_t *target,
+ GdkDrawable *drawable)
+{
+ if (cairo_surface_status(target))
+ return;
+
+ g_object_ref(drawable);
+
+ cairo_surface_set_user_data (target,
+ &cairo_gdk_drawable_key,
+ drawable,
+ g_object_unref);
+}
+
+GdkDrawable *
+gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target)
+{
+ if (cairo_surface_status(target))
+ return nullptr;
+
+ GdkDrawable *result;
+
+ result = (GdkDrawable*) cairo_surface_get_user_data (target,
+ &cairo_gdk_drawable_key);
+ if (result)
+ return result;
+
+#ifdef MOZ_X11
+ if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB)
+ return nullptr;
+
+ // try looking it up in gdk's table
+ result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target));
+ if (result) {
+ SetGdkDrawable(target, result);
+ return result;
+ }
+#endif
+
+ return nullptr;
+}
+#endif
+
+already_AddRefed<ScaledFont>
+gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
+{
+ switch (aTarget->GetBackendType()) {
+ case BackendType::CAIRO:
+ case BackendType::SKIA:
+ if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
+ gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
+ return Factory::CreateScaledFontForFontconfigFont(
+ fcFont->GetCairoScaledFont(),
+ fcFont->GetPattern(),
+ fcFont->GetAdjustedSize());
+ }
+ MOZ_FALLTHROUGH;
+ default:
+ return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
+ }
+}
+
+#ifdef GL_PROVIDER_GLX
+
+class GLXVsyncSource final : public VsyncSource
+{
+public:
+ GLXVsyncSource()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mGlobalDisplay = new GLXDisplay();
+ }
+
+ virtual ~GLXVsyncSource()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ virtual Display& GetGlobalDisplay() override
+ {
+ return *mGlobalDisplay;
+ }
+
+ class GLXDisplay final : public VsyncSource::Display
+ {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLXDisplay)
+
+ public:
+ GLXDisplay() : mGLContext(nullptr)
+ , mXDisplay(nullptr)
+ , mSetupLock("GLXVsyncSetupLock")
+ , mVsyncThread("GLXVsyncThread")
+ , mVsyncTask(nullptr)
+ , mVsyncEnabledLock("GLXVsyncEnabledLock")
+ , mVsyncEnabled(false)
+ {
+ }
+
+ // Sets up the display's GL context on a worker thread.
+ // Required as GLContexts may only be used by the creating thread.
+ // Returns true if setup was a success.
+ bool Setup()
+ {
+ MonitorAutoLock lock(mSetupLock);
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mVsyncThread.Start())
+ return false;
+
+ RefPtr<Runnable> vsyncSetup = NewRunnableMethod(this, &GLXDisplay::SetupGLContext);
+ mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
+ // Wait until the setup has completed.
+ lock.Wait();
+ return mGLContext != nullptr;
+ }
+
+ // Called on the Vsync thread to setup the GL context.
+ void SetupGLContext()
+ {
+ MonitorAutoLock lock(mSetupLock);
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(!mGLContext, "GLContext already setup!");
+
+ // Create video sync timer on a separate Display to prevent locking the
+ // main thread X display.
+ mXDisplay = XOpenDisplay(nullptr);
+ if (!mXDisplay) {
+ lock.NotifyAll();
+ return;
+ }
+
+ // Most compositors wait for vsync events on the root window.
+ Window root = DefaultRootWindow(mXDisplay);
+ int screen = DefaultScreen(mXDisplay);
+
+ ScopedXFree<GLXFBConfig> cfgs;
+ GLXFBConfig config;
+ int visid;
+ if (!gl::GLContextGLX::FindFBConfigForWindow(mXDisplay, screen, root,
+ &cfgs, &config, &visid)) {
+ lock.NotifyAll();
+ return;
+ }
+
+ mGLContext = gl::GLContextGLX::CreateGLContext(
+ gl::CreateContextFlags::NONE,
+ gl::SurfaceCaps::Any(),
+ nullptr,
+ false,
+ mXDisplay,
+ root,
+ config,
+ false);
+
+ if (!mGLContext) {
+ lock.NotifyAll();
+ return;
+ }
+
+ mGLContext->MakeCurrent();
+
+ // Test that SGI_video_sync lets us get the counter.
+ unsigned int syncCounter = 0;
+ if (gl::sGLXLibrary.xGetVideoSync(&syncCounter) != 0) {
+ mGLContext = nullptr;
+ }
+
+ lock.NotifyAll();
+ }
+
+ virtual void EnableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mGLContext, "GLContext not setup!");
+
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (mVsyncEnabled) {
+ return;
+ }
+ mVsyncEnabled = true;
+
+ // If the task has not nulled itself out, it hasn't yet realized
+ // that vsync was disabled earlier, so continue its execution.
+ if (!mVsyncTask) {
+ mVsyncTask = NewRunnableMethod(this, &GLXDisplay::RunVsync);
+ RefPtr<Runnable> addrefedTask = mVsyncTask;
+ mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
+ }
+ }
+
+ virtual void DisableVsync() override
+ {
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ mVsyncEnabled = false;
+ }
+
+ virtual bool IsVsyncEnabled() override
+ {
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ return mVsyncEnabled;
+ }
+
+ virtual void Shutdown() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ DisableVsync();
+
+ // Cleanup thread-specific resources before shutting down.
+ RefPtr<Runnable> shutdownTask = NewRunnableMethod(this, &GLXDisplay::Cleanup);
+ mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
+
+ // Stop, waiting for the cleanup task to finish execution.
+ mVsyncThread.Stop();
+ }
+
+ private:
+ virtual ~GLXDisplay()
+ {
+ }
+
+ void RunVsync()
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mGLContext->MakeCurrent();
+
+ unsigned int syncCounter = 0;
+ gl::sGLXLibrary.xGetVideoSync(&syncCounter);
+ for (;;) {
+ {
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (!mVsyncEnabled) {
+ mVsyncTask = nullptr;
+ return;
+ }
+ }
+
+ TimeStamp lastVsync = TimeStamp::Now();
+ bool useSoftware = false;
+
+ // Wait until the video sync counter reaches the next value by waiting
+ // until the parity of the counter value changes.
+ unsigned int nextSync = syncCounter + 1;
+ int status;
+ if ((status = gl::sGLXLibrary.xWaitVideoSync(2, nextSync % 2, &syncCounter)) != 0) {
+ gfxWarningOnce() << "glXWaitVideoSync returned " << status;
+ useSoftware = true;
+ }
+
+ if (syncCounter == (nextSync - 1)) {
+ gfxWarningOnce() << "glXWaitVideoSync failed to increment the sync counter.";
+ useSoftware = true;
+ }
+
+ if (useSoftware) {
+ double remaining = (1000.f / 60.f) -
+ (TimeStamp::Now() - lastVsync).ToMilliseconds();
+ if (remaining > 0) {
+ PlatformThread::Sleep(remaining);
+ }
+ }
+
+ lastVsync = TimeStamp::Now();
+ NotifyVsync(lastVsync);
+ }
+ }
+
+ void Cleanup() {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mGLContext = nullptr;
+ XCloseDisplay(mXDisplay);
+ }
+
+ // Owned by the vsync thread.
+ RefPtr<gl::GLContextGLX> mGLContext;
+ _XDisplay* mXDisplay;
+ Monitor mSetupLock;
+ base::Thread mVsyncThread;
+ RefPtr<Runnable> mVsyncTask;
+ Monitor mVsyncEnabledLock;
+ bool mVsyncEnabled;
+ };
+private:
+ // We need a refcounted VsyncSource::Display to use chromium IPC runnables.
+ RefPtr<GLXDisplay> mGlobalDisplay;
+};
+
+already_AddRefed<gfx::VsyncSource>
+gfxPlatformGtk::CreateHardwareVsyncSource()
+{
+ // Only use GLX vsync when the OpenGL compositor is being used.
+ // The extra cost of initializing a GLX context while blocking the main
+ // thread is not worth it when using basic composition.
+ if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+ if (gl::sGLXLibrary.SupportsVideoSync()) {
+ RefPtr<VsyncSource> vsyncSource = new GLXVsyncSource();
+ VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
+ if (!static_cast<GLXVsyncSource::GLXDisplay&>(display).Setup()) {
+ NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
+ return gfxPlatform::CreateHardwareVsyncSource();
+ }
+ return vsyncSource.forget();
+ }
+ NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
+ }
+ return gfxPlatform::CreateHardwareVsyncSource();
+}
+
+bool
+gfxPlatformGtk::SupportsApzTouchInput() const
+{
+ int value = gfxPrefs::TouchEventsEnabled();
+ return value == 1 || value == 2;
+}
+
+#endif
diff --git a/system/graphics/thebes/gfxPlatformGtk.h b/system/graphics/thebes/gfxPlatformGtk.h
new file mode 100644
index 000000000..22ed4b08f
--- /dev/null
+++ b/system/graphics/thebes/gfxPlatformGtk.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PLATFORM_GTK_H
+#define GFX_PLATFORM_GTK_H
+
+#include "gfxPlatform.h"
+#include "nsAutoRef.h"
+#include "nsTArray.h"
+#include "mozilla/gfx/gfxVars.h"
+
+#if (MOZ_WIDGET_GTK == 2)
+extern "C" {
+ typedef struct _GdkDrawable GdkDrawable;
+}
+#endif
+
+#ifdef MOZ_X11
+struct _XDisplay;
+typedef struct _XDisplay Display;
+#endif // MOZ_X11
+
+class gfxFontconfigUtils;
+
+class gfxPlatformGtk : public gfxPlatform {
+public:
+ gfxPlatformGtk();
+ virtual ~gfxPlatformGtk();
+
+ static gfxPlatformGtk *GetPlatform() {
+ return (gfxPlatformGtk*) gfxPlatform::GetPlatform();
+ }
+
+ virtual already_AddRefed<gfxASurface>
+ CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat) override;
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
+
+ virtual nsresult GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts) override;
+
+ virtual nsresult UpdateFontList() override;
+
+ virtual void
+ GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList) override;
+
+ virtual gfxPlatformFontList* CreatePlatformFontList() override;
+
+ virtual nsresult GetStandardFamilyName(const nsAString& aFontName,
+ nsAString& aFamilyName) override;
+
+ gfxFontGroup*
+ CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize) override;
+
+ /**
+ * Look up a local platform font using the full font face name (needed to
+ * support @font-face src local() )
+ */
+ virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle) override;
+
+ /**
+ * Activate a platform font (needed to support @font-face src url() )
+ *
+ */
+ virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength) override;
+
+ /**
+ * Check whether format is supported on a platform or not (if unclear,
+ * returns true).
+ */
+ virtual bool IsFontFormatSupported(nsIURI *aFontURI,
+ uint32_t aFormatFlags) override;
+
+ /**
+ * Calls XFlush if xrender is enabled.
+ */
+ virtual void FlushContentDrawing() override;
+
+#if (MOZ_WIDGET_GTK == 2)
+ static void SetGdkDrawable(cairo_surface_t *target,
+ GdkDrawable *drawable);
+ static GdkDrawable *GetGdkDrawable(cairo_surface_t *target);
+#endif
+
+ static int32_t GetDPI();
+ static double GetDPIScale();
+
+#ifdef MOZ_X11
+ virtual void GetAzureBackendInfo(mozilla::widget::InfoObject &aObj) override {
+ gfxPlatform::GetAzureBackendInfo(aObj);
+ aObj.DefineProperty("CairoUseXRender", mozilla::gfx::gfxVars::UseXRender());
+ }
+#endif
+
+ static bool UseFcFontList() { return sUseFcFontList; }
+
+ bool UseImageOffscreenSurfaces();
+
+ virtual gfxImageFormat GetOffscreenFormat() override;
+
+ bool SupportsApzWheelInput() const override {
+ return true;
+ }
+
+ bool SupportsApzTouchInput() const override;
+
+ void FontsPrefsChanged(const char *aPref) override;
+
+ // maximum number of fonts to substitute for a generic
+ uint32_t MaxGenericSubstitions();
+
+ bool SupportsPluginDirectBitmapDrawing() override {
+ return true;
+ }
+
+#ifdef GL_PROVIDER_GLX
+ already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+#endif
+
+#ifdef MOZ_X11
+ Display* GetCompositorDisplay() {
+ return mCompositorDisplay;
+ }
+#endif // MOZ_X11
+
+protected:
+ static gfxFontconfigUtils *sFontconfigUtils;
+
+ int8_t mMaxGenericSubstitutions;
+
+private:
+ virtual void GetPlatformCMSOutputProfile(void *&mem,
+ size_t &size) override;
+
+#ifdef MOZ_X11
+ Display* mCompositorDisplay;
+#endif
+
+ // xxx - this will be removed once the new fontconfig platform font list
+ // replaces gfxPangoFontGroup
+ static bool sUseFcFontList;
+};
+
+#endif /* GFX_PLATFORM_GTK_H */
diff --git a/system/graphics/thebes/gfxPoint.h b/system/graphics/thebes/gfxPoint.h
new file mode 100644
index 000000000..5883244e3
--- /dev/null
+++ b/system/graphics/thebes/gfxPoint.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_POINT_H
+#define GFX_POINT_H
+
+#include "nsMathUtils.h"
+#include "mozilla/gfx/BaseSize.h"
+#include "mozilla/gfx/BasePoint.h"
+#include "mozilla/gfx/Matrix.h"
+#include "nsSize.h"
+#include "nsPoint.h"
+
+#include "gfxTypes.h"
+
+struct gfxSize : public mozilla::gfx::BaseSize<gfxFloat, gfxSize> {
+ typedef mozilla::gfx::BaseSize<gfxFloat, gfxSize> Super;
+
+ gfxSize() : Super() {}
+ gfxSize(gfxFloat aWidth, gfxFloat aHeight) : Super(aWidth, aHeight) {}
+ MOZ_IMPLICIT gfxSize(const mozilla::gfx::IntSize& aSize) : Super(aSize.width, aSize.height) {}
+};
+
+struct gfxPoint : public mozilla::gfx::BasePoint<gfxFloat, gfxPoint> {
+ typedef mozilla::gfx::BasePoint<gfxFloat, gfxPoint> Super;
+
+ gfxPoint() : Super() {}
+ gfxPoint(gfxFloat aX, gfxFloat aY) : Super(aX, aY) {}
+ MOZ_IMPLICIT gfxPoint(const nsIntPoint& aPoint) : Super(aPoint.x, aPoint.y) {}
+
+ bool WithinEpsilonOf(const gfxPoint& aPoint, gfxFloat aEpsilon) {
+ return fabs(aPoint.x - x) < aEpsilon && fabs(aPoint.y - y) < aEpsilon;
+ }
+
+ void Transform(const mozilla::gfx::Matrix4x4 &aMatrix)
+ {
+ // Transform this point with aMatrix
+ double px = x;
+ double py = y;
+
+ x = px * aMatrix._11 + py * aMatrix._21 + aMatrix._41;
+ y = px * aMatrix._12 + py * aMatrix._22 + aMatrix._42;
+
+ double w = px * aMatrix._14 + py * aMatrix._24 + aMatrix._44;
+ x /= w;
+ y /= w;
+ }
+};
+
+inline gfxPoint
+operator*(const gfxPoint& aPoint, const gfxSize& aSize)
+{
+ return gfxPoint(aPoint.x * aSize.width, aPoint.y * aSize.height);
+}
+
+inline gfxPoint
+operator/(const gfxPoint& aPoint, const gfxSize& aSize)
+{
+ return gfxPoint(aPoint.x / aSize.width, aPoint.y / aSize.height);
+}
+
+inline gfxSize
+operator/(gfxFloat aValue, const gfxSize& aSize)
+{
+ return gfxSize(aValue / aSize.width, aValue / aSize.height);
+}
+
+#endif /* GFX_POINT_H */
diff --git a/system/graphics/thebes/gfxPrefs.cpp b/system/graphics/thebes/gfxPrefs.cpp
new file mode 100644
index 000000000..72bc58d78
--- /dev/null
+++ b/system/graphics/thebes/gfxPrefs.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPrefs.h"
+
+#include "MainThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+
+using namespace mozilla;
+
+nsTArray<gfxPrefs::Pref*>* gfxPrefs::sGfxPrefList = nullptr;
+gfxPrefs* gfxPrefs::sInstance = nullptr;
+bool gfxPrefs::sInstanceHasBeenDestroyed = false;
+
+void
+gfxPrefs::DestroySingleton()
+{
+ if (sInstance) {
+ delete sInstance;
+ sInstance = nullptr;
+ sInstanceHasBeenDestroyed = true;
+ }
+ MOZ_ASSERT(!SingletonExists());
+}
+
+bool
+gfxPrefs::SingletonExists()
+{
+ return sInstance != nullptr;
+}
+
+gfxPrefs::gfxPrefs()
+{
+ // UI, content, and plugin processes use XPCOM and should have prefs
+ // ready by the time we initialize gfxPrefs.
+ MOZ_ASSERT_IF(XRE_IsContentProcess() ||
+ XRE_IsParentProcess() ||
+ XRE_GetProcessType() == GeckoProcessType_Plugin,
+ Preferences::IsServiceAvailable());
+
+ gfxPrefs::AssertMainThread();
+}
+
+void
+gfxPrefs::Init()
+{
+ // Set up Moz2D prefs.
+ mPrefGfxLoggingLevel.SetChangeCallback([]() -> void {
+ mozilla::gfx::LoggingPrefs::sGfxLogLevel = GetSingleton().mPrefGfxLoggingLevel.GetLiveValue();
+ });
+}
+
+gfxPrefs::~gfxPrefs()
+{
+ gfxPrefs::AssertMainThread();
+ mPrefGfxLoggingLevel.SetChangeCallback(nullptr);
+ delete sGfxPrefList;
+ sGfxPrefList = nullptr;
+}
+
+void gfxPrefs::AssertMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "this code must be run on the main thread");
+}
+
+void
+gfxPrefs::Pref::OnChange()
+{
+ if (auto gpm = gfx::GPUProcessManager::Get()) {
+ if (gfx::GPUChild* gpu = gpm->GetGPUChild()) {
+ GfxPrefValue value;
+ GetLiveValue(&value);
+ Unused << gpu->SendUpdatePref(gfx::GfxPrefSetting(mIndex, value));
+ }
+ }
+ FireChangeCallback();
+}
+
+void
+gfxPrefs::Pref::FireChangeCallback()
+{
+ if (mChangeCallback) {
+ mChangeCallback();
+ }
+}
+
+void
+gfxPrefs::Pref::SetChangeCallback(ChangeCallback aCallback)
+{
+ mChangeCallback = aCallback;
+
+ if (!IsParentProcess() && IsPrefsServiceAvailable()) {
+ // If we're in the parent process, we watch prefs by default so we can
+ // send changes over to the GPU process. Otherwise, we need to add or
+ // remove a watch for the pref now.
+ if (aCallback) {
+ WatchChanges(Name(), this);
+ } else {
+ UnwatchChanges(Name(), this);
+ }
+ }
+
+ // Fire the callback once to make initialization easier for the caller.
+ FireChangeCallback();
+}
+
+// On lightweight processes such as for GMP and GPU, XPCOM is not initialized,
+// and therefore we don't have access to Preferences. When XPCOM is not
+// available we rely on manual synchronization of gfxPrefs values over IPC.
+/* static */ bool
+gfxPrefs::IsPrefsServiceAvailable()
+{
+ return Preferences::IsServiceAvailable();
+}
+
+/* static */ bool
+gfxPrefs::IsParentProcess()
+{
+ return XRE_IsParentProcess();
+}
+
+void gfxPrefs::PrefAddVarCache(bool* aVariable,
+ const char* aPref,
+ bool aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::AddBoolVarCache(aVariable, aPref, aDefault);
+}
+
+void gfxPrefs::PrefAddVarCache(int32_t* aVariable,
+ const char* aPref,
+ int32_t aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::AddIntVarCache(aVariable, aPref, aDefault);
+}
+
+void gfxPrefs::PrefAddVarCache(uint32_t* aVariable,
+ const char* aPref,
+ uint32_t aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::AddUintVarCache(aVariable, aPref, aDefault);
+}
+
+void gfxPrefs::PrefAddVarCache(float* aVariable,
+ const char* aPref,
+ float aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::AddFloatVarCache(aVariable, aPref, aDefault);
+}
+
+bool gfxPrefs::PrefGet(const char* aPref, bool aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ return Preferences::GetBool(aPref, aDefault);
+}
+
+int32_t gfxPrefs::PrefGet(const char* aPref, int32_t aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ return Preferences::GetInt(aPref, aDefault);
+}
+
+uint32_t gfxPrefs::PrefGet(const char* aPref, uint32_t aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ return Preferences::GetUint(aPref, aDefault);
+}
+
+float gfxPrefs::PrefGet(const char* aPref, float aDefault)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ return Preferences::GetFloat(aPref, aDefault);
+}
+
+void gfxPrefs::PrefSet(const char* aPref, bool aValue)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::SetBool(aPref, aValue);
+}
+
+void gfxPrefs::PrefSet(const char* aPref, int32_t aValue)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::SetInt(aPref, aValue);
+}
+
+void gfxPrefs::PrefSet(const char* aPref, uint32_t aValue)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::SetUint(aPref, aValue);
+}
+
+void gfxPrefs::PrefSet(const char* aPref, float aValue)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::SetFloat(aPref, aValue);
+}
+
+static void
+OnGfxPrefChanged(const char* aPrefname, void* aClosure)
+{
+ reinterpret_cast<gfxPrefs::Pref*>(aClosure)->OnChange();
+}
+
+void gfxPrefs::WatchChanges(const char* aPrefname, Pref* aPref)
+{
+ MOZ_ASSERT(IsPrefsServiceAvailable());
+ Preferences::RegisterCallback(OnGfxPrefChanged, aPrefname, aPref, Preferences::ExactMatch);
+}
+
+void gfxPrefs::UnwatchChanges(const char* aPrefname, Pref* aPref)
+{
+ // The Preferences service can go offline before gfxPrefs is destroyed.
+ if (IsPrefsServiceAvailable()) {
+ Preferences::UnregisterCallback(OnGfxPrefChanged, aPrefname, aPref, Preferences::ExactMatch);
+ }
+}
+
+void gfxPrefs::CopyPrefValue(const bool* aValue, GfxPrefValue* aOutValue)
+{
+ *aOutValue = *aValue;
+}
+
+void gfxPrefs::CopyPrefValue(const int32_t* aValue, GfxPrefValue* aOutValue)
+{
+ *aOutValue = *aValue;
+}
+
+void gfxPrefs::CopyPrefValue(const uint32_t* aValue, GfxPrefValue* aOutValue)
+{
+ *aOutValue = *aValue;
+}
+
+void gfxPrefs::CopyPrefValue(const float* aValue, GfxPrefValue* aOutValue)
+{
+ *aOutValue = *aValue;
+}
+
+void gfxPrefs::CopyPrefValue(const GfxPrefValue* aValue, bool* aOutValue)
+{
+ *aOutValue = aValue->get_bool();
+}
+
+void gfxPrefs::CopyPrefValue(const GfxPrefValue* aValue, int32_t* aOutValue)
+{
+ *aOutValue = aValue->get_int32_t();
+}
+
+void gfxPrefs::CopyPrefValue(const GfxPrefValue* aValue, uint32_t* aOutValue)
+{
+ *aOutValue = aValue->get_uint32_t();
+}
+
+void gfxPrefs::CopyPrefValue(const GfxPrefValue* aValue, float* aOutValue)
+{
+ *aOutValue = aValue->get_float();
+}
diff --git a/system/graphics/thebes/gfxPrefs.h b/system/graphics/thebes/gfxPrefs.h
new file mode 100644
index 000000000..143d9199c
--- /dev/null
+++ b/system/graphics/thebes/gfxPrefs.h
@@ -0,0 +1,671 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PREFS_H
+#define GFX_PREFS_H
+
+#include <cmath> // for M_PI
+#include <stdint.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Function.h"
+#include "mozilla/gfx/LoggingConstants.h"
+#include "nsTArray.h"
+
+// First time gfxPrefs::GetSingleton() needs to be called on the main thread,
+// before any of the methods accessing the values are used, but after
+// the Preferences system has been initialized.
+
+// The static methods to access the preference value are safe to call
+// from any thread after that first call.
+
+// To register a preference, you need to add a line in this file using
+// the DECL_GFX_PREF macro.
+//
+// Update argument controls whether we read the preference value and save it
+// or connect with a callback. See UpdatePolicy enum below.
+// Pref is the string with the preference name.
+// Name argument is the name of the static function to create.
+// Type is the type of the preference - bool, int32_t, uint32_t.
+// Default is the default value for the preference.
+//
+// For example this line in the .h:
+// DECL_GFX_PREF(Once,"layers.dump",LayersDump,bool,false);
+// means that you can call
+// bool var = gfxPrefs::LayersDump();
+// from any thread, but that you will only get the preference value of
+// "layers.dump" as it was set at the start of the session (subject to
+// note 2 below). If the value was not set, the default would be false.
+//
+// In another example, this line in the .h:
+// DECL_GFX_PREF(Live,"gl.msaa-level",MSAALevel,uint32_t,2);
+// means that every time you call
+// uint32_t var = gfxPrefs::MSAALevel();
+// from any thread, you will get the most up to date preference value of
+// "gl.msaa-level". If the value is not set, the default would be 2.
+
+// Note 1: Changing a preference from Live to Once is now as simple
+// as changing the Update argument. If your code worked before, it will
+// keep working, and behave as if the user never changes the preference.
+// Things are a bit more complicated and perhaps even dangerous when
+// going from Once to Live, or indeed setting a preference to be Live
+// in the first place, so be careful. You need to be ready for the
+// values changing mid execution, and if you're using those preferences
+// in any setup and initialization, you may need to do extra work.
+
+// Note 2: Prefs can be set by using the corresponding Set method. For
+// example, if the accessor is Foo() then calling SetFoo(...) will update
+// the preference and also change the return value of subsequent Foo() calls.
+// This is true even for 'Once' prefs which otherwise do not change if the
+// pref is updated after initialization. Changing gfxPrefs values in content
+// processes will not affect the result in other processes. Changing gfxPrefs
+// values in the GPU process is not supported at all.
+
+#define DECL_GFX_PREF(Update, Prefname, Name, Type, Default) \
+public: \
+static Type Name() { MOZ_ASSERT(SingletonExists()); return GetSingleton().mPref##Name.mValue; } \
+static void Set##Name(Type aVal) { MOZ_ASSERT(SingletonExists()); \
+ GetSingleton().mPref##Name.Set(UpdatePolicy::Update, Get##Name##PrefName(), aVal); } \
+static const char* Get##Name##PrefName() { return Prefname; } \
+static Type Get##Name##PrefDefault() { return Default; } \
+private: \
+PrefTemplate<UpdatePolicy::Update, Type, Get##Name##PrefDefault, Get##Name##PrefName> mPref##Name
+
+namespace mozilla {
+namespace gfx {
+class GfxPrefValue; // defined in PGPU.ipdl
+} // namespace gfx
+} // namespace mozilla
+
+class gfxPrefs;
+class gfxPrefs final
+{
+ typedef mozilla::gfx::GfxPrefValue GfxPrefValue;
+
+private:
+ // Enums for the update policy.
+ enum class UpdatePolicy {
+ Skip, // Set the value to default, skip any Preferences calls
+ Once, // Evaluate the preference once, unchanged during the session
+ Live // Evaluate the preference and set callback so it stays current/live
+ };
+
+public:
+ class Pref
+ {
+ public:
+ Pref() : mChangeCallback(nullptr)
+ {
+ mIndex = sGfxPrefList->Length();
+ sGfxPrefList->AppendElement(this);
+ }
+
+ size_t Index() const { return mIndex; }
+ void OnChange();
+
+ typedef void (*ChangeCallback)();
+ void SetChangeCallback(ChangeCallback aCallback);
+
+ virtual const char* Name() const = 0;
+
+ // Returns true if the value is default, false if changed.
+ virtual bool HasDefaultValue() const = 0;
+
+ // Returns the pref value as a discriminated union.
+ virtual void GetLiveValue(GfxPrefValue* aOutValue) const = 0;
+
+ // Returns the pref value as a discriminated union.
+ virtual void GetCachedValue(GfxPrefValue* aOutValue) const = 0;
+
+ // Change the cached value. GfxPrefValue must be a compatible type.
+ virtual void SetCachedValue(const GfxPrefValue& aOutValue) = 0;
+
+ protected:
+ void FireChangeCallback();
+
+ private:
+ size_t mIndex;
+ ChangeCallback mChangeCallback;
+ };
+
+ static const nsTArray<Pref*>& all() {
+ return *sGfxPrefList;
+ }
+
+private:
+ // We split out a base class to reduce the number of virtual function
+ // instantiations that we do, which saves code size.
+ template<class T>
+ class TypedPref : public Pref
+ {
+ public:
+ explicit TypedPref(T aValue)
+ : mValue(aValue)
+ {}
+
+ void GetCachedValue(GfxPrefValue* aOutValue) const override {
+ CopyPrefValue(&mValue, aOutValue);
+ }
+ void SetCachedValue(const GfxPrefValue& aOutValue) override {
+ // This is only used in non-XPCOM processes.
+ MOZ_ASSERT(!IsPrefsServiceAvailable());
+
+ T newValue;
+ CopyPrefValue(&aOutValue, &newValue);
+
+ if (mValue != newValue) {
+ mValue = newValue;
+ FireChangeCallback();
+ }
+ }
+
+ protected:
+ T GetLiveValueByName(const char* aPrefName) const {
+ if (IsPrefsServiceAvailable()) {
+ return PrefGet(aPrefName, mValue);
+ }
+ return mValue;
+ }
+
+ public:
+ T mValue;
+ };
+
+ // Since we cannot use const char*, use a function that returns it.
+ template <UpdatePolicy Update, class T, T Default(void), const char* Prefname(void)>
+ class PrefTemplate final : public TypedPref<T>
+ {
+ typedef TypedPref<T> BaseClass;
+ public:
+ PrefTemplate()
+ : BaseClass(Default())
+ {
+ // If not using the Preferences service, values are synced over IPC, so
+ // there's no need to register us as a Preferences observer.
+ if (IsPrefsServiceAvailable()) {
+ Register(Update, Prefname());
+ }
+ // By default we only watch changes in the parent process, to communicate
+ // changes to the GPU process.
+ if (IsParentProcess() && Update == UpdatePolicy::Live) {
+ WatchChanges(Prefname(), this);
+ }
+ }
+ ~PrefTemplate() {
+ if (IsParentProcess() && Update == UpdatePolicy::Live) {
+ UnwatchChanges(Prefname(), this);
+ }
+ }
+ void Register(UpdatePolicy aUpdate, const char* aPreference)
+ {
+ AssertMainThread();
+ switch (aUpdate) {
+ case UpdatePolicy::Skip:
+ break;
+ case UpdatePolicy::Once:
+ this->mValue = PrefGet(aPreference, this->mValue);
+ break;
+ case UpdatePolicy::Live:
+ PrefAddVarCache(&this->mValue, aPreference, this->mValue);
+ break;
+ default:
+ MOZ_CRASH("Incomplete switch");
+ }
+ }
+ void Set(UpdatePolicy aUpdate, const char* aPref, T aValue)
+ {
+ AssertMainThread();
+ PrefSet(aPref, aValue);
+ switch (aUpdate) {
+ case UpdatePolicy::Skip:
+ case UpdatePolicy::Live:
+ break;
+ case UpdatePolicy::Once:
+ this->mValue = PrefGet(aPref, this->mValue);
+ break;
+ default:
+ MOZ_CRASH("Incomplete switch");
+ }
+ }
+ const char *Name() const override {
+ return Prefname();
+ }
+ void GetLiveValue(GfxPrefValue* aOutValue) const override {
+ T value = GetLiveValue();
+ CopyPrefValue(&value, aOutValue);
+ }
+ // When using the Preferences service, the change callback can be triggered
+ // *before* our cached value is updated, so we expose a method to grab the
+ // true live value.
+ T GetLiveValue() const {
+ return BaseClass::GetLiveValueByName(Prefname());
+ }
+ bool HasDefaultValue() const override {
+ return this->mValue == Default();
+ }
+ };
+
+ // This is where DECL_GFX_PREF for each of the preferences should go.
+ // We will keep these in an alphabetical order to make it easier to see if
+ // a method accessing a pref already exists. Just add yours in the list.
+
+ // The apz prefs are explained in AsyncPanZoomController.cpp
+ DECL_GFX_PREF(Live, "apz.allow_checkerboarding", APZAllowCheckerboarding, bool, true);
+ DECL_GFX_PREF(Live, "apz.allow_immediate_handoff", APZAllowImmediateHandoff, bool, true);
+ DECL_GFX_PREF(Live, "apz.allow_zooming", APZAllowZooming, bool, false);
+ DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle", APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
+ DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold", APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
+ DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle", APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
+ DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle", APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
+ DECL_GFX_PREF(Live, "apz.axis_lock.mode", APZAxisLockMode, int32_t, 0);
+ DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 400);
+ DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50);
+ DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100);
+ DECL_GFX_PREF(Live, "apz.disable_for_scroll_linked_effects", APZDisableForScrollLinkedEffects, bool, false);
+ DECL_GFX_PREF(Live, "apz.displayport_expiry_ms", APZDisplayPortExpiryTime, uint32_t, 15000);
+ DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false);
+ DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false);
+ DECL_GFX_PREF(Live, "apz.fling_accel_base_mult", APZFlingAccelBaseMultiplier, float, 1.0f);
+ DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms", APZFlingAccelInterval, int32_t, 500);
+ DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult", APZFlingAccelSupplementalMultiplier, float, 1.0f);
+ DECL_GFX_PREF(Live, "apz.fling_accel_min_velocity", APZFlingAccelMinVelocity, float, 1.5f);
+ DECL_GFX_PREF(Once, "apz.fling_curve_function_x1", APZCurveFunctionX1, float, 0.0f);
+ DECL_GFX_PREF(Once, "apz.fling_curve_function_x2", APZCurveFunctionX2, float, 1.0f);
+ DECL_GFX_PREF(Once, "apz.fling_curve_function_y1", APZCurveFunctionY1, float, 0.0f);
+ DECL_GFX_PREF(Once, "apz.fling_curve_function_y2", APZCurveFunctionY2, float, 1.0f);
+ DECL_GFX_PREF(Live, "apz.fling_curve_threshold_inches_per_ms", APZCurveThreshold, float, -1.0f);
+ DECL_GFX_PREF(Live, "apz.fling_friction", APZFlingFriction, float, 0.002f);
+ DECL_GFX_PREF(Live, "apz.fling_min_velocity_threshold", APZFlingMinVelocityThreshold, float, 0.5f);
+ DECL_GFX_PREF(Live, "apz.fling_stop_on_tap_threshold", APZFlingStopOnTapThreshold, float, 0.05f);
+ DECL_GFX_PREF(Live, "apz.fling_stopped_threshold", APZFlingStoppedThreshold, float, 0.01f);
+ DECL_GFX_PREF(Live, "apz.highlight_checkerboarded_areas", APZHighlightCheckerboardedAreas, bool, false);
+ DECL_GFX_PREF(Live, "apz.max_velocity_inches_per_ms", APZMaxVelocity, float, -1.0f);
+ DECL_GFX_PREF(Once, "apz.max_velocity_queue_size", APZMaxVelocityQueueSize, uint32_t, 5);
+ DECL_GFX_PREF(Live, "apz.min_skate_speed", APZMinSkateSpeed, float, 1.0f);
+ DECL_GFX_PREF(Live, "apz.minimap.enabled", APZMinimap, bool, false);
+ DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled", APZMinimapVisibilityEnabled, bool, false);
+ DECL_GFX_PREF(Live, "apz.overscroll.enabled", APZOverscrollEnabled, bool, false);
+ DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
+ DECL_GFX_PREF(Live, "apz.overscroll.spring_friction", APZOverscrollSpringFriction, float, 0.015f);
+ DECL_GFX_PREF(Live, "apz.overscroll.spring_stiffness", APZOverscrollSpringStiffness, float, 0.001f);
+ DECL_GFX_PREF(Live, "apz.overscroll.stop_distance_threshold", APZOverscrollStopDistanceThreshold, float, 5.0f);
+ DECL_GFX_PREF(Live, "apz.overscroll.stop_velocity_threshold", APZOverscrollStopVelocityThreshold, float, 0.01f);
+ DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor", APZOverscrollStretchFactor, float, 0.5f);
+ DECL_GFX_PREF(Live, "apz.paint_skipping.enabled", APZPaintSkipping, bool, true);
+ DECL_GFX_PREF(Live, "apz.peek_messages.enabled", APZPeekMessages, bool, true);
+ DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
+ DECL_GFX_PREF(Live, "apz.record_checkerboarding", APZRecordCheckerboarding, bool, false);
+ DECL_GFX_PREF(Live, "apz.test.fails_with_native_injection", APZTestFailsWithNativeInjection, bool, false);
+ DECL_GFX_PREF(Live, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
+ DECL_GFX_PREF(Live, "apz.touch_move_tolerance", APZTouchMoveTolerance, float, 0.0);
+ DECL_GFX_PREF(Live, "apz.touch_start_tolerance", APZTouchStartTolerance, float, 1.0f/4.5f);
+ DECL_GFX_PREF(Live, "apz.velocity_bias", APZVelocityBias, float, 0.0f);
+ DECL_GFX_PREF(Live, "apz.velocity_relevance_time_ms", APZVelocityRelevanceTime, uint32_t, 150);
+ DECL_GFX_PREF(Live, "apz.x_skate_highmem_adjust", APZXSkateHighMemAdjust, float, 0.0f);
+ DECL_GFX_PREF(Live, "apz.x_skate_size_multiplier", APZXSkateSizeMultiplier, float, 1.5f);
+ DECL_GFX_PREF(Live, "apz.x_stationary_size_multiplier", APZXStationarySizeMultiplier, float, 3.0f);
+ DECL_GFX_PREF(Live, "apz.y_skate_highmem_adjust", APZYSkateHighMemAdjust, float, 0.0f);
+ DECL_GFX_PREF(Live, "apz.y_skate_size_multiplier", APZYSkateSizeMultiplier, float, 2.5f);
+ DECL_GFX_PREF(Live, "apz.y_stationary_size_multiplier", APZYStationarySizeMultiplier, float, 3.5f);
+ DECL_GFX_PREF(Live, "apz.zoom_animation_duration_ms", APZZoomAnimationDuration, int32_t, 250);
+ DECL_GFX_PREF(Live, "apz.scale_repaint_delay_ms", APZScaleRepaintDelay, int32_t, 500);
+ DECL_GFX_PREF(Once, "apz.desktop.enabled", APZDesktopEnabled, bool, false);
+
+ DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable", ForceUserScalable, bool, false);
+ DECL_GFX_PREF(Live, "browser.viewport.desktopWidth", DesktopViewportWidth, int32_t, 980);
+
+ DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled", PluginAsyncDrawingEnabled, bool, false);
+ DECL_GFX_PREF(Live, "dom.meta-viewport.enabled", MetaViewportEnabled, bool, false);
+ DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
+ DECL_GFX_PREF(Once, "dom.vr.oculus.enabled", VROculusEnabled, bool, true);
+ DECL_GFX_PREF(Once, "dom.vr.openvr.enabled", VROpenVREnabled, bool, false);
+ DECL_GFX_PREF(Once, "dom.vr.osvr.enabled", VROSVREnabled, bool, false);
+ DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled", VRPosePredictionEnabled, bool, false);
+ DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled", PointerEventsEnabled, bool, false);
+ DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled", TouchEventsEnabled, int32_t, 0);
+
+ DECL_GFX_PREF(Live, "general.smoothScroll", SmoothScrollEnabled, bool, true);
+ DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
+ SmoothScrollCurrentVelocityWeighting, float, 0.25);
+ DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
+ SmoothScrollDurationToIntervalRatio, int32_t, 200);
+ DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel", WheelSmoothScrollEnabled, bool, true);
+ DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMaxMS",
+ WheelSmoothScrollMaxDurationMs, int32_t, 400);
+ DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMinMS",
+ WheelSmoothScrollMinDurationMs, int32_t, 200);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pages", PageSmoothScrollEnabled, bool, true);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pages.durationMaxMS",
+ PageSmoothScrollMaxDurationMs, int32_t, 150);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pages.durationMinMS",
+ PageSmoothScrollMinDurationMs, int32_t, 150);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pixels", PixelSmoothScrollEnabled, bool, true);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMaxMS",
+ PixelSmoothScrollMaxDurationMs, int32_t, 150);
+ DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMinMS",
+ PixelSmoothScrollMinDurationMs, int32_t, 150);
+ DECL_GFX_PREF(Live, "general.smoothScroll.stopDecelerationWeighting",
+ SmoothScrollStopDecelerationWeighting, float, 0.4f);
+
+ DECL_GFX_PREF(Once, "gfx.android.rgb16.force", AndroidRGB16Force, bool, false);
+#if defined(ANDROID)
+ DECL_GFX_PREF(Once, "gfx.apitrace.enabled", UseApitrace, bool, false);
+#endif
+#if defined(RELEASE_OR_BETA)
+ // "Skip" means this is locked to the default value in beta and release.
+ DECL_GFX_PREF(Skip, "gfx.blocklist.all", BlocklistAll, int32_t, 0);
+#else
+ DECL_GFX_PREF(Once, "gfx.blocklist.all", BlocklistAll, int32_t, 0);
+#endif
+ DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls", CanvasAutoAccelerateMinCalls, int32_t, 4);
+ DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30);
+ DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_seconds", CanvasAutoAccelerateMinSeconds, float, 5.0f);
+ DECL_GFX_PREF(Live, "gfx.canvas.azure.accelerated", CanvasAzureAccelerated, bool, false);
+ // 0x7fff is the maximum supported xlib surface size and is more than enough for canvases.
+ DECL_GFX_PREF(Live, "gfx.canvas.max-size", MaxCanvasSize, int32_t, 0x7fff);
+ DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-items", CanvasSkiaGLCacheItems, int32_t, 256);
+ DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-size", CanvasSkiaGLCacheSize, int32_t, 96);
+ DECL_GFX_PREF(Once, "gfx.canvas.skiagl.dynamic-cache", CanvasSkiaGLDynamicCache, bool, false);
+
+ DECL_GFX_PREF(Live, "gfx.color_management.enablev4", CMSEnableV4, bool, false);
+ DECL_GFX_PREF(Live, "gfx.color_management.mode", CMSMode, int32_t,-1);
+ // The zero default here should match QCMS_INTENT_DEFAULT from qcms.h
+ DECL_GFX_PREF(Live, "gfx.color_management.rendering_intent", CMSRenderingIntent, int32_t, 0);
+
+ DECL_GFX_PREF(Once, "gfx.device-reset.limit", DeviceResetLimitCount, int32_t, 10);
+ DECL_GFX_PREF(Once, "gfx.device-reset.threshold-ms", DeviceResetThresholdMilliseconds, int32_t, -1);
+
+ DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false);
+ DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false);
+ DECL_GFX_PREF(Live, "gfx.direct3d11.reuse-decoder-device", Direct3D11ReuseDecoderDevice, int32_t, -1);
+ DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false);
+ DECL_GFX_PREF(Once, "gfx.e10s.hide-plugins-for-scroll", HidePluginsForScroll, bool, true);
+ DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false);
+ DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false);
+ DECL_GFX_PREF(Live, "gfx.layerscope.port", LayerScopePort, int32_t, 23456);
+ // Note that "gfx.logging.level" is defined in Logging.h.
+ DECL_GFX_PREF(Live, "gfx.logging.level", GfxLoggingLevel, int32_t, mozilla::gfx::LOG_DEFAULT);
+ DECL_GFX_PREF(Once, "gfx.logging.crash.length", GfxLoggingCrashLength, uint32_t, 16);
+ DECL_GFX_PREF(Live, "gfx.logging.painted-pixel-count.enabled",GfxLoggingPaintedPixelCountEnabled, bool, false);
+ // The maximums here are quite conservative, we can tighten them if problems show up.
+ DECL_GFX_PREF(Once, "gfx.logging.texture-usage.enabled", GfxLoggingTextureUsageEnabled, bool, false);
+ DECL_GFX_PREF(Once, "gfx.logging.peak-texture-usage.enabled",GfxLoggingPeakTextureUsageEnabled, bool, false);
+ DECL_GFX_PREF(Once, "gfx.max-alloc-size", MaxAllocSize, int32_t, (int32_t)500000000);
+ DECL_GFX_PREF(Once, "gfx.max-texture-size", MaxTextureSize, int32_t, (int32_t)32767);
+ DECL_GFX_PREF(Live, "gfx.partialpresent.force", PartialPresent, int32_t, 0);
+ DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false);
+ DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled", SurfaceTextureDetachEnabled, bool, true);
+ DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
+ DECL_GFX_PREF(Live, "gfx.testing.device-fail", DeviceFailForTesting, bool, false);
+ DECL_GFX_PREF(Once, "gfx.text.disable-aa", DisableAllTextAA, bool, false);
+ DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion", YCbCrAccurateConversion, bool, false);
+
+ DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer", UseNativePushLayer, bool, false);
+ DECL_GFX_PREF(Live, "gfx.content.always-paint", AlwaysPaint, bool, false);
+
+ // Disable surface sharing due to issues with compatible FBConfigs on
+ // NVIDIA drivers as described in bug 1193015.
+ DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap", UseGLXTextureFromPixmap, bool, false);
+
+ DECL_GFX_PREF(Once, "gfx.use-iosurface-textures", UseIOSurfaceTextures, bool, false);
+
+ // These times should be in milliseconds
+ DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold", TouchResampleVsyncDelayThreshold, int32_t, 20);
+ DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict", TouchResampleMaxPredict, int32_t, 8);
+ DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta", TouchResampleMinDelta, int32_t, 2);
+ DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
+ DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust", TouchVsyncSampleAdjust, int32_t, 5);
+
+ DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms", CollectScrollTransforms, bool, false);
+ // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread
+ // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10.
+ DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count", CompositorUnobserveCount, int32_t, 10);
+ // Use vsync events generated by hardware
+ DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true);
+ DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled", ScreenMirroringEnabled, bool, false);
+
+ DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist", IgnoreDXInterop2Blacklist, bool, false);
+ DECL_GFX_PREF(Live, "gl.msaa-level", MSAALevel, uint32_t, 2);
+ DECL_GFX_PREF(Live, "gl.require-hardware", RequireHardwareGL, bool, false);
+
+ DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
+ DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
+ DECL_GFX_PREF(Live, "image.decode-immediately.enabled", ImageDecodeImmediatelyEnabled, bool, false);
+ DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
+ DECL_GFX_PREF(Live, "image.infer-src-animation.threshold-ms", ImageInferSrcAnimationThresholdMS, uint32_t, 2000);
+ DECL_GFX_PREF(Once, "image.layerize.always", ImageLayerizeAlways, bool, false);
+ DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
+ DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
+ DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
+ DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
+ DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
+ DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
+ DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
+ DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
+ DECL_GFX_PREF(Live, "image.webp.enabled", ImageWebPEnabled, bool, true);
+
+ DECL_GFX_PREF(Once, "layers.acceleration.enabled", LayersAccelerationEnabledDoNotUseDirectly, bool, true);
+ DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
+ DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram", FPSPrintHistogram, bool, false);
+ DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
+ DECL_GFX_PREF(Once, "layers.acceleration.force", LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
+ DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled", LayersAMDSwitchableGfxEnabled, bool, false);
+ DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled", AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
+ DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
+ DECL_GFX_PREF(Live, "layers.bench.enabled", LayersBenchEnabled, bool, false);
+ DECL_GFX_PREF(Once, "layers.bufferrotation.enabled", BufferRotationEnabled, bool, true);
+ DECL_GFX_PREF(Live, "layers.child-process-shutdown", ChildProcessShutdown, bool, true);
+ DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true);
+ DECL_GFX_PREF(Live, "layers.composer2d.enabled", Composer2DCompositionEnabled, bool, false);
+ DECL_GFX_PREF(Once, "layers.d3d11.force-warp", LayersD3D11ForceWARP, bool, false);
+ DECL_GFX_PREF(Live, "layers.deaa.enabled", LayersDEAAEnabled, bool, false);
+ DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false);
+ DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false);
+ DECL_GFX_PREF(Live, "layers.draw-tile-borders", DrawTileBorders, bool, false);
+ DECL_GFX_PREF(Live, "layers.draw-layer-info", DrawLayerInfo, bool, false);
+ DECL_GFX_PREF(Live, "layers.dump", LayersDump, bool, false);
+ DECL_GFX_PREF(Live, "layers.dump-texture", LayersDumpTexture, bool, false);
+#ifdef MOZ_DUMP_PAINTING
+ DECL_GFX_PREF(Live, "layers.dump-client-layers", DumpClientLayers, bool, false);
+ DECL_GFX_PREF(Live, "layers.dump-decision", LayersDumpDecision, bool, false);
+ DECL_GFX_PREF(Live, "layers.dump-host-layers", DumpHostLayers, bool, false);
+#endif
+
+ // 0 is "no change" for contrast, positive values increase it, negative values
+ // decrease it until we hit mid gray at -1 contrast, after that it gets weird.
+ DECL_GFX_PREF(Live, "layers.effect.contrast", LayersEffectContrast, float, 0.0f);
+ DECL_GFX_PREF(Live, "layers.effect.grayscale", LayersEffectGrayscale, bool, false);
+ DECL_GFX_PREF(Live, "layers.effect.invert", LayersEffectInvert, bool, false);
+ DECL_GFX_PREF(Once, "layers.enable-tiles", LayersTilesEnabled, bool, false);
+ DECL_GFX_PREF(Live, "layers.flash-borders", FlashLayerBorders, bool, false);
+ DECL_GFX_PREF(Once, "layers.force-shmem-tiles", ForceShmemTiles, bool, false);
+ DECL_GFX_PREF(Live, "layers.frame-counter", DrawFrameCounter, bool, false);
+ DECL_GFX_PREF(Once, "layers.gpu-process.dev.enabled", GPUProcessDevEnabled, bool, false);
+ DECL_GFX_PREF(Once, "layers.gpu-process.dev.force-enabled", GPUProcessDevForceEnabled, bool, false);
+ DECL_GFX_PREF(Once, "layers.gpu-process.dev.timeout_ms", GPUProcessDevTimeoutMs, int32_t, 5000);
+ DECL_GFX_PREF(Live, "layers.gpu-process.dev.max_restarts", GPUProcessDevMaxRestarts, int32_t, 0);
+ DECL_GFX_PREF(Once, "layers.gralloc.disable", DisableGralloc, bool, false);
+ DECL_GFX_PREF(Live, "layers.low-precision-buffer", UseLowPrecisionBuffer, bool, false);
+ DECL_GFX_PREF(Live, "layers.low-precision-opacity", LowPrecisionOpacity, float, 1.0f);
+ DECL_GFX_PREF(Live, "layers.low-precision-resolution", LowPrecisionResolution, float, 0.25f);
+ DECL_GFX_PREF(Live, "layers.max-active", MaxActiveLayers, int32_t, -1);
+ DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
+ DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
+ DECL_GFX_PREF(Live, "layers.orientation.sync.timeout", OrientationSyncMillis, uint32_t, (uint32_t)0);
+ DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking", OverzealousGrallocUnlocking, bool, false);
+ DECL_GFX_PREF(Once, "layers.prefer-opengl", LayersPreferOpenGL, bool, false);
+ DECL_GFX_PREF(Live, "layers.progressive-paint", ProgressivePaint, bool, false);
+ DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
+ DECL_GFX_PREF(Live, "layers.single-tile.enabled", LayersSingleTileEnabled, bool, true);
+
+ // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
+ // screen size does not align nicely to the default tile size. Although layers can be any size,
+ // they are often the same size as the screen, especially for width.
+ DECL_GFX_PREF(Once, "layers.tile-width", LayersTileWidth, int32_t, 256);
+ DECL_GFX_PREF(Once, "layers.tile-height", LayersTileHeight, int32_t, 256);
+ DECL_GFX_PREF(Once, "layers.tile-initial-pool-size", LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
+ DECL_GFX_PREF(Once, "layers.tile-pool-unused-size", LayersTilePoolUnusedSize, uint32_t, (uint32_t)10);
+ DECL_GFX_PREF(Once, "layers.tile-pool-shrink-timeout", LayersTilePoolShrinkTimeout, uint32_t, (uint32_t)50);
+ DECL_GFX_PREF(Once, "layers.tile-pool-clear-timeout", LayersTilePoolClearTimeout, uint32_t, (uint32_t)5000);
+ DECL_GFX_PREF(Once, "layers.tiles.adjust", LayersTilesAdjust, bool, true);
+ DECL_GFX_PREF(Once, "layers.tiles.edge-padding", TileEdgePaddingEnabled, bool, true);
+ DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled", LayerTileFadeInEnabled, bool, false);
+ DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms", LayerTileFadeInDuration, uint32_t, 250);
+ DECL_GFX_PREF(Live, "layers.transaction.warning-ms", LayerTransactionWarning, uint32_t, 200);
+ DECL_GFX_PREF(Once, "layers.uniformity-info", UniformityInfo, bool, false);
+ DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces", UseImageOffscreenSurfaces, bool, true);
+ DECL_GFX_PREF(Live, "layers.draw-mask-debug", DrawMaskLayer, bool, false);
+
+ DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
+ DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled", ScrollBehaviorEnabled, bool, true);
+ DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
+ DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000);
+ DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-sensitivity", ScrollSnapPredictionSensitivity, float, 0.750f);
+ DECL_GFX_PREF(Live, "layout.css.scroll-snap.proximity-threshold", ScrollSnapProximityThreshold, int32_t, 200);
+ DECL_GFX_PREF(Live, "layout.css.touch_action.enabled", TouchActionEnabled, bool, false);
+ DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false);
+ DECL_GFX_PREF(Live, "layout.display-list.dump-content", LayoutDumpDisplayListContent, bool, false);
+ DECL_GFX_PREF(Live, "layout.event-regions.enabled", LayoutEventRegionsEnabledDoNotUseDirectly, bool, false);
+ DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1);
+ DECL_GFX_PREF(Once, "layout.paint_rects_separately", LayoutPaintRectsSeparately, bool, true);
+
+ // This and code dependent on it should be removed once containerless scrolling looks stable.
+ DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers", LayoutUseContainersForRootFrames, bool, true);
+
+ DECL_GFX_PREF(Once, "media.hardware-video-decoding.force-enabled",
+ HardwareVideoDecodingForceEnabled, bool, false);
+#ifdef XP_WIN
+ DECL_GFX_PREF(Live, "media.windows-media-foundation.allow-d3d11-dxva", PDMWMFAllowD3D11, bool, true);
+ DECL_GFX_PREF(Live, "media.windows-media-foundation.max-dxva-videos", PDMWMFMaxDXVAVideos, uint32_t, 8);
+ DECL_GFX_PREF(Live, "media.wmf.low-latency.enabled", PDMWMFLowLatencyEnabled, bool, false);
+ DECL_GFX_PREF(Live, "media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false);
+#endif
+
+ // These affect how line scrolls from wheel events will be accelerated.
+ DECL_GFX_PREF(Live, "mousewheel.acceleration.factor", MouseWheelAccelerationFactor, int32_t, -1);
+ DECL_GFX_PREF(Live, "mousewheel.acceleration.start", MouseWheelAccelerationStart, int32_t, -1);
+
+ // This affects whether events will be routed through APZ or not.
+ DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
+ MouseWheelHasRootScrollDeltaOverride, bool, false);
+ DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.horizontal.factor",
+ MouseWheelRootScrollHorizontalFactor, int32_t, 0);
+ DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.vertical.factor",
+ MouseWheelRootScrollVerticalFactor, int32_t, 0);
+ DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
+ DECL_GFX_PREF(Live, "mousewheel.transaction.timeout", MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
+
+ DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
+
+ DECL_GFX_PREF(Live, "test.events.async.enabled", TestEventsAsyncEnabled, bool, false);
+ DECL_GFX_PREF(Live, "test.mousescroll", MouseScrollTestingEnabled, bool, false);
+
+ DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay", UiClickHoldContextMenusDelay, int32_t, 500);
+
+ // WebGL (for pref access from Worker threads)
+ DECL_GFX_PREF(Live, "webgl.all-angle-options", WebGLAllANGLEOptions, bool, false);
+ DECL_GFX_PREF(Live, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
+ DECL_GFX_PREF(Live, "webgl.angle.try-d3d11", WebGLANGLETryD3D11, bool, false);
+ DECL_GFX_PREF(Live, "webgl.angle.force-warp", WebGLANGLEForceWARP, bool, false);
+ DECL_GFX_PREF(Live, "webgl.bypass-shader-validation", WebGLBypassShaderValidator, bool, true);
+ DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground", WebGLCanLoseContextInForeground, bool, true);
+ DECL_GFX_PREF(Live, "webgl.default-no-alpha", WebGLDefaultNoAlpha, bool, false);
+ DECL_GFX_PREF(Live, "webgl.disable-angle", WebGLDisableANGLE, bool, false);
+ DECL_GFX_PREF(Live, "webgl.disable-wgl", WebGLDisableWGL, bool, false);
+ DECL_GFX_PREF(Live, "webgl.disable-extensions", WebGLDisableExtensions, bool, false);
+ DECL_GFX_PREF(Live, "webgl.dxgl.enabled", WebGLDXGLEnabled, bool, false);
+ DECL_GFX_PREF(Live, "webgl.dxgl.needs-finish", WebGLDXGLNeedsFinish, bool, false);
+
+ DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
+ WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
+ DECL_GFX_PREF(Live, "webgl.disable-DOM-blit-uploads",
+ WebGLDisableDOMBlitUploads, bool, false);
+
+ DECL_GFX_PREF(Live, "webgl.disabled", WebGLDisabled, bool, false);
+
+ DECL_GFX_PREF(Live, "webgl.enable-draft-extensions", WebGLDraftExtensionsEnabled, bool, false);
+ DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions", WebGLPrivilegedExtensionsEnabled, bool, false);
+ DECL_GFX_PREF(Live, "webgl.enable-webgl2", WebGL2Enabled, bool, true);
+ DECL_GFX_PREF(Live, "webgl.force-enabled", WebGLForceEnabled, bool, false);
+ DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
+ DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
+ DECL_GFX_PREF(Live, "webgl.max-warnings-per-context", WebGLMaxWarningsPerContext, uint32_t, 32);
+ DECL_GFX_PREF(Live, "webgl.min_capability_mode", WebGLMinCapabilityMode, bool, false);
+ DECL_GFX_PREF(Live, "webgl.msaa-force", WebGLForceMSAA, bool, false);
+ DECL_GFX_PREF(Live, "webgl.prefer-16bpp", WebGLPrefer16bpp, bool, false);
+ DECL_GFX_PREF(Live, "webgl.restore-context-when-visible", WebGLRestoreWhenVisible, bool, true);
+ DECL_GFX_PREF(Live, "webgl.allow-immediate-queries", WebGLImmediateQueries, bool, false);
+ DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation", WebGLFBInvalidation, bool, false);
+
+ DECL_GFX_PREF(Live, "webgl.webgl2-compat-mode", WebGL2CompatMode, bool, false);
+
+ // WARNING:
+ // Please make sure that you've added your new preference to the list above in alphabetical order.
+ // Please do not just append it to the end of the list.
+
+public:
+ // Manage the singleton:
+ static gfxPrefs& GetSingleton()
+ {
+ MOZ_ASSERT(!sInstanceHasBeenDestroyed, "Should never recreate a gfxPrefs instance!");
+ if (!sInstance) {
+ sGfxPrefList = new nsTArray<Pref*>();
+ sInstance = new gfxPrefs;
+ sInstance->Init();
+ }
+ MOZ_ASSERT(SingletonExists());
+ return *sInstance;
+ }
+ static void DestroySingleton();
+ static bool SingletonExists();
+
+private:
+ static gfxPrefs* sInstance;
+ static bool sInstanceHasBeenDestroyed;
+ static nsTArray<Pref*>* sGfxPrefList;
+
+private:
+ // The constructor cannot access GetSingleton(), since sInstance (necessarily)
+ // has not been assigned yet. Follow-up initialization that needs GetSingleton()
+ // must be added to Init().
+ void Init();
+
+ static bool IsPrefsServiceAvailable();
+ static bool IsParentProcess();
+ // Creating these to avoid having to include Preferences.h in the .h
+ static void PrefAddVarCache(bool*, const char*, bool);
+ static void PrefAddVarCache(int32_t*, const char*, int32_t);
+ static void PrefAddVarCache(uint32_t*, const char*, uint32_t);
+ static void PrefAddVarCache(float*, const char*, float);
+ static bool PrefGet(const char*, bool);
+ static int32_t PrefGet(const char*, int32_t);
+ static uint32_t PrefGet(const char*, uint32_t);
+ static float PrefGet(const char*, float);
+ static void PrefSet(const char* aPref, bool aValue);
+ static void PrefSet(const char* aPref, int32_t aValue);
+ static void PrefSet(const char* aPref, uint32_t aValue);
+ static void PrefSet(const char* aPref, float aValue);
+ static void WatchChanges(const char* aPrefname, Pref* aPref);
+ static void UnwatchChanges(const char* aPrefname, Pref* aPref);
+ // Creating these to avoid having to include PGPU.h in the .h
+ static void CopyPrefValue(const bool* aValue, GfxPrefValue* aOutValue);
+ static void CopyPrefValue(const int32_t* aValue, GfxPrefValue* aOutValue);
+ static void CopyPrefValue(const uint32_t* aValue, GfxPrefValue* aOutValue);
+ static void CopyPrefValue(const float* aValue, GfxPrefValue* aOutValue);
+ static void CopyPrefValue(const GfxPrefValue* aValue, bool* aOutValue);
+ static void CopyPrefValue(const GfxPrefValue* aValue, int32_t* aOutValue);
+ static void CopyPrefValue(const GfxPrefValue* aValue, uint32_t* aOutValue);
+ static void CopyPrefValue(const GfxPrefValue* aValue, float* aOutValue);
+
+ static void AssertMainThread();
+
+ gfxPrefs();
+ ~gfxPrefs();
+ gfxPrefs(const gfxPrefs&) = delete;
+ gfxPrefs& operator=(const gfxPrefs&) = delete;
+};
+
+#undef DECL_GFX_PREF /* Don't need it outside of this file */
+
+#endif /* GFX_PREFS_H */
diff --git a/system/graphics/thebes/gfxQuad.h b/system/graphics/thebes/gfxQuad.h
new file mode 100644
index 000000000..8dd7a26e1
--- /dev/null
+++ b/system/graphics/thebes/gfxQuad.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_QUAD_H
+#define GFX_QUAD_H
+
+#include "gfxTypes.h"
+#include "gfxRect.h"
+#include "gfxLineSegment.h"
+#include <algorithm>
+
+struct gfxQuad {
+ gfxQuad(const gfxPoint& aOne, const gfxPoint& aTwo, const gfxPoint& aThree, const gfxPoint& aFour)
+ {
+ mPoints[0] = aOne;
+ mPoints[1] = aTwo;
+ mPoints[2] = aThree;
+ mPoints[3] = aFour;
+ }
+
+ bool Contains(const gfxPoint& aPoint)
+ {
+ return (gfxLineSegment(mPoints[0], mPoints[1]).PointsOnSameSide(aPoint, mPoints[2]) &&
+ gfxLineSegment(mPoints[1], mPoints[2]).PointsOnSameSide(aPoint, mPoints[3]) &&
+ gfxLineSegment(mPoints[2], mPoints[3]).PointsOnSameSide(aPoint, mPoints[0]) &&
+ gfxLineSegment(mPoints[3], mPoints[0]).PointsOnSameSide(aPoint, mPoints[1]));
+ }
+
+ gfxRect GetBounds()
+ {
+ gfxFloat min_x, max_x;
+ gfxFloat min_y, max_y;
+
+ min_x = max_x = mPoints[0].x;
+ min_y = max_y = mPoints[0].y;
+
+ for (int i=1; i<4; i++) {
+ min_x = std::min(mPoints[i].x, min_x);
+ max_x = std::max(mPoints[i].x, max_x);
+ min_y = std::min(mPoints[i].y, min_y);
+ max_y = std::max(mPoints[i].y, max_y);
+ }
+ return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ gfxPoint mPoints[4];
+};
+
+#endif /* GFX_QUAD_H */
diff --git a/system/graphics/thebes/gfxQuaternion.h b/system/graphics/thebes/gfxQuaternion.h
new file mode 100644
index 000000000..e25974974
--- /dev/null
+++ b/system/graphics/thebes/gfxQuaternion.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_QUATERNION_H
+#define GFX_QUATERNION_H
+
+#include "mozilla/gfx/BasePoint4D.h"
+#include "mozilla/gfx/Matrix.h"
+#include "nsAlgorithm.h"
+#include <algorithm>
+
+struct gfxQuaternion : public mozilla::gfx::BasePoint4D<gfxFloat, gfxQuaternion> {
+ typedef mozilla::gfx::BasePoint4D<gfxFloat, gfxQuaternion> Super;
+
+ gfxQuaternion() : Super() {}
+ gfxQuaternion(gfxFloat aX, gfxFloat aY, gfxFloat aZ, gfxFloat aW) : Super(aX, aY, aZ, aW) {}
+
+ explicit gfxQuaternion(const mozilla::gfx::Matrix4x4& aMatrix) {
+ w = 0.5 * sqrt(std::max(1 + aMatrix[0][0] + aMatrix[1][1] + aMatrix[2][2], 0.0f));
+ x = 0.5 * sqrt(std::max(1 + aMatrix[0][0] - aMatrix[1][1] - aMatrix[2][2], 0.0f));
+ y = 0.5 * sqrt(std::max(1 - aMatrix[0][0] + aMatrix[1][1] - aMatrix[2][2], 0.0f));
+ z = 0.5 * sqrt(std::max(1 - aMatrix[0][0] - aMatrix[1][1] + aMatrix[2][2], 0.0f));
+
+ if(aMatrix[2][1] > aMatrix[1][2])
+ x = -x;
+ if(aMatrix[0][2] > aMatrix[2][0])
+ y = -y;
+ if(aMatrix[1][0] > aMatrix[0][1])
+ z = -z;
+ }
+
+ // Convert from |direction axis, angle| pair to gfxQuaternion.
+ //
+ // Reference:
+ // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
+ //
+ // if the direction axis is (x, y, z) = xi + yj + zk,
+ // and the angle is |theta|, this formula can be done using
+ // an extension of Euler's formula:
+ // q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
+ // = cos(theta/2) +
+ // x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
+ // Note: aDirection should be an unit vector and
+ // the unit of aAngle should be Radian.
+ gfxQuaternion(const mozilla::gfx::Point3D &aDirection, gfxFloat aAngle) {
+ MOZ_ASSERT(mozilla::gfx::FuzzyEqual(aDirection.Length(), 1.0f),
+ "aDirection should be an unit vector");
+ x = aDirection.x * sin(aAngle/2.0);
+ y = aDirection.y * sin(aAngle/2.0);
+ z = aDirection.z * sin(aAngle/2.0);
+ w = cos(aAngle/2.0);
+ }
+
+ gfxQuaternion Slerp(const gfxQuaternion &aOther, gfxFloat aCoeff) {
+ gfxFloat dot = mozilla::clamped(DotProduct(aOther), -1.0, 1.0);
+ if (dot == 1.0) {
+ return *this;
+ }
+
+ gfxFloat theta = acos(dot);
+ gfxFloat rsintheta = 1/sqrt(1 - dot*dot);
+ gfxFloat rightWeight = sin(aCoeff*theta)*rsintheta;
+
+ gfxQuaternion left = *this;
+ gfxQuaternion right = aOther;
+
+ left *= cos(aCoeff*theta) - dot*rightWeight;
+ right *= rightWeight;
+
+ return left + right;
+ }
+
+ mozilla::gfx::Matrix4x4 ToMatrix() {
+ mozilla::gfx::Matrix4x4 temp;
+
+ temp[0][0] = 1 - 2 * (y * y + z * z);
+ temp[0][1] = 2 * (x * y + w * z);
+ temp[0][2] = 2 * (x * z - w * y);
+ temp[1][0] = 2 * (x * y - w * z);
+ temp[1][1] = 1 - 2 * (x * x + z * z);
+ temp[1][2] = 2 * (y * z + w * x);
+ temp[2][0] = 2 * (x * z + w * y);
+ temp[2][1] = 2 * (y * z - w * x);
+ temp[2][2] = 1 - 2 * (x * x + y * y);
+
+ return temp;
+ }
+
+};
+
+#endif /* GFX_QUATERNION_H */
diff --git a/system/graphics/thebes/gfxRect.cpp b/system/graphics/thebes/gfxRect.cpp
new file mode 100644
index 000000000..a418f394e
--- /dev/null
+++ b/system/graphics/thebes/gfxRect.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxRect.h"
+
+#include "nsMathUtils.h"
+
+#include "mozilla/gfx/Matrix.h"
+
+#include "gfxQuad.h"
+
+gfxQuad
+gfxRect::TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const
+{
+ gfxPoint points[4];
+
+ points[0] = TopLeft();
+ points[1] = TopRight();
+ points[2] = BottomRight();
+ points[3] = BottomLeft();
+
+ points[0].Transform(aMatrix);
+ points[1].Transform(aMatrix);
+ points[2].Transform(aMatrix);
+ points[3].Transform(aMatrix);
+
+ // Could this ever result in lines that intersect? I don't think so.
+ return gfxQuad(points[0], points[1], points[2], points[3]);
+}
+
+static bool
+WithinEpsilonOfInteger(gfxFloat aX, gfxFloat aEpsilon)
+{
+ return fabs(NS_round(aX) - aX) <= fabs(aEpsilon);
+}
+
+bool
+gfxRect::WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const
+{
+ NS_ASSERTION(-0.5 < aEpsilon && aEpsilon < 0.5, "Nonsense epsilon value");
+ return (WithinEpsilonOfInteger(x, aEpsilon) &&
+ WithinEpsilonOfInteger(y, aEpsilon) &&
+ WithinEpsilonOfInteger(width, aEpsilon) &&
+ WithinEpsilonOfInteger(height, aEpsilon));
+}
+
+/* Clamp r to CAIRO_COORD_MIN .. CAIRO_COORD_MAX
+ * these are to be device coordinates.
+ *
+ * Cairo is currently using 24.8 fixed point,
+ * so -2^24 .. 2^24-1 is our valid
+ */
+
+#define CAIRO_COORD_MAX (16777215.0)
+#define CAIRO_COORD_MIN (-16777216.0)
+
+void
+gfxRect::Condition()
+{
+ // if either x or y is way out of bounds;
+ // note that we don't handle negative w/h here
+ if (x > CAIRO_COORD_MAX) {
+ x = CAIRO_COORD_MAX;
+ width = 0.0;
+ }
+
+ if (y > CAIRO_COORD_MAX) {
+ y = CAIRO_COORD_MAX;
+ height = 0.0;
+ }
+
+ if (x < CAIRO_COORD_MIN) {
+ width += x - CAIRO_COORD_MIN;
+ if (width < 0.0)
+ width = 0.0;
+ x = CAIRO_COORD_MIN;
+ }
+
+ if (y < CAIRO_COORD_MIN) {
+ height += y - CAIRO_COORD_MIN;
+ if (height < 0.0)
+ height = 0.0;
+ y = CAIRO_COORD_MIN;
+ }
+
+ if (x + width > CAIRO_COORD_MAX) {
+ width = CAIRO_COORD_MAX - x;
+ }
+
+ if (y + height > CAIRO_COORD_MAX) {
+ height = CAIRO_COORD_MAX - y;
+ }
+}
+
diff --git a/system/graphics/thebes/gfxRect.h b/system/graphics/thebes/gfxRect.h
new file mode 100644
index 000000000..56d5c43e5
--- /dev/null
+++ b/system/graphics/thebes/gfxRect.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_RECT_H
+#define GFX_RECT_H
+
+#include "gfxTypes.h"
+#include "gfxPoint.h"
+#include "nsDebug.h"
+#include "nsRect.h"
+#include "mozilla/gfx/BaseMargin.h"
+#include "mozilla/gfx/BaseRect.h"
+#include "mozilla/gfx/MatrixFwd.h"
+#include "mozilla/Assertions.h"
+
+struct gfxQuad;
+
+struct gfxMargin : public mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> {
+ typedef mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> Super;
+
+ // Constructors
+ gfxMargin() : Super() {}
+ gfxMargin(const gfxMargin& aMargin) : Super(aMargin) {}
+ gfxMargin(gfxFloat aTop, gfxFloat aRight, gfxFloat aBottom, gfxFloat aLeft)
+ : Super(aTop, aRight, aBottom, aLeft) {}
+};
+
+namespace mozilla {
+ namespace css {
+ enum Corner {
+ // this order is important!
+ eCornerTopLeft = 0,
+ eCornerTopRight = 1,
+ eCornerBottomRight = 2,
+ eCornerBottomLeft = 3,
+ eNumCorners = 4
+ };
+ } // namespace css
+} // namespace mozilla
+#define NS_CORNER_TOP_LEFT mozilla::css::eCornerTopLeft
+#define NS_CORNER_TOP_RIGHT mozilla::css::eCornerTopRight
+#define NS_CORNER_BOTTOM_RIGHT mozilla::css::eCornerBottomRight
+#define NS_CORNER_BOTTOM_LEFT mozilla::css::eCornerBottomLeft
+#define NS_NUM_CORNERS mozilla::css::eNumCorners
+
+#define NS_FOR_CSS_CORNERS(var_) \
+ for (mozilla::css::Corner var_ = NS_CORNER_TOP_LEFT; \
+ var_ <= NS_CORNER_BOTTOM_LEFT; \
+ var_++)
+
+static inline mozilla::css::Corner operator++(mozilla::css::Corner& corner, int) {
+ NS_PRECONDITION(corner >= NS_CORNER_TOP_LEFT &&
+ corner < NS_NUM_CORNERS, "Out of range corner");
+ corner = mozilla::css::Corner(corner + 1);
+ return corner;
+}
+
+struct gfxRect :
+ public mozilla::gfx::BaseRect<gfxFloat, gfxRect, gfxPoint, gfxSize, gfxMargin> {
+ typedef mozilla::gfx::BaseRect<gfxFloat, gfxRect, gfxPoint, gfxSize, gfxMargin> Super;
+
+ gfxRect() : Super() {}
+ gfxRect(const gfxPoint& aPos, const gfxSize& aSize) :
+ Super(aPos, aSize) {}
+ gfxRect(gfxFloat aX, gfxFloat aY, gfxFloat aWidth, gfxFloat aHeight) :
+ Super(aX, aY, aWidth, aHeight) {}
+
+ /**
+ * Return true if all components of this rect are within
+ * aEpsilon of integer coordinates, defined as
+ * |round(coord) - coord| <= |aEpsilon|
+ * for x,y,width,height.
+ */
+ bool WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const;
+
+ gfxPoint AtCorner(mozilla::css::Corner corner) const {
+ switch (corner) {
+ case NS_CORNER_TOP_LEFT: return TopLeft();
+ case NS_CORNER_TOP_RIGHT: return TopRight();
+ case NS_CORNER_BOTTOM_RIGHT: return BottomRight();
+ case NS_CORNER_BOTTOM_LEFT: return BottomLeft();
+ default:
+ NS_ERROR("Invalid corner!");
+ break;
+ }
+ return gfxPoint(0.0, 0.0);
+ }
+
+ gfxPoint CCWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopLeft();
+ case NS_SIDE_RIGHT: return TopRight();
+ case NS_SIDE_BOTTOM: return BottomRight();
+ case NS_SIDE_LEFT: return BottomLeft();
+ }
+ MOZ_CRASH("Incomplete switch");
+ }
+
+ gfxPoint CWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopRight();
+ case NS_SIDE_RIGHT: return BottomRight();
+ case NS_SIDE_BOTTOM: return BottomLeft();
+ case NS_SIDE_LEFT: return TopLeft();
+ }
+ MOZ_CRASH("Incomplete switch");
+ }
+
+ /* Conditions this border to Cairo's max coordinate space.
+ * The caller can check IsEmpty() after Condition() -- if it's TRUE,
+ * the caller can possibly avoid doing any extra rendering.
+ */
+ void Condition();
+
+ void Scale(gfxFloat k) {
+ NS_ASSERTION(k >= 0.0, "Invalid (negative) scale factor");
+ x *= k;
+ y *= k;
+ width *= k;
+ height *= k;
+ }
+
+ void Scale(gfxFloat sx, gfxFloat sy) {
+ NS_ASSERTION(sx >= 0.0, "Invalid (negative) scale factor");
+ NS_ASSERTION(sy >= 0.0, "Invalid (negative) scale factor");
+ x *= sx;
+ y *= sy;
+ width *= sx;
+ height *= sy;
+ }
+
+ void ScaleInverse(gfxFloat k) {
+ NS_ASSERTION(k > 0.0, "Invalid (negative) scale factor");
+ x /= k;
+ y /= k;
+ width /= k;
+ height /= k;
+ }
+
+ /*
+ * Transform this rectangle with aMatrix, resulting in a gfxQuad.
+ */
+ gfxQuad TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const;
+};
+
+#endif /* GFX_RECT_H */
diff --git a/system/graphics/thebes/gfxSVGGlyphs.cpp b/system/graphics/thebes/gfxSVGGlyphs.cpp
new file mode 100644
index 000000000..13c0420cc
--- /dev/null
+++ b/system/graphics/thebes/gfxSVGGlyphs.cpp
@@ -0,0 +1,502 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxSVGGlyphs.h"
+
+#include "mozilla/SVGContextPaint.h"
+#include "nsError.h"
+#include "nsIDOMDocument.h"
+#include "nsString.h"
+#include "nsIDocument.h"
+#include "nsICategoryManager.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsIContentViewer.h"
+#include "nsIStreamListener.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIPresShell.h"
+#include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
+#include "nsIInputStream.h"
+#include "nsStringStream.h"
+#include "nsStreamUtils.h"
+#include "nsIPrincipal.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/LoadInfo.h"
+#include "nsSVGUtils.h"
+#include "nsHostObjectProtocolHandler.h"
+#include "nsContentUtils.h"
+#include "gfxFont.h"
+#include "nsSMILAnimationController.h"
+#include "gfxContext.h"
+#include "harfbuzz/hb.h"
+#include "zlib.h"
+#include "mozilla/dom/ImageTracker.h"
+
+#define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
+#define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+typedef mozilla::dom::Element Element;
+
+/* static */ const Color SimpleTextContextPaint::sZero = Color();
+
+gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
+ : mSVGData(aSVGTable)
+ , mFontEntry(aFontEntry)
+{
+ unsigned int length;
+ const char* svgData = hb_blob_get_data(mSVGData, &length);
+ mHeader = reinterpret_cast<const Header*>(svgData);
+ mDocIndex = nullptr;
+
+ if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
+ uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
+ const DocIndex* docIndex = reinterpret_cast<const DocIndex*>
+ (svgData + mHeader->mDocIndexOffset);
+ // Limit the number of documents to avoid overflow
+ if (uint64_t(mHeader->mDocIndexOffset) + 2 +
+ uint16_t(docIndex->mNumEntries) * sizeof(IndexEntry) <= length) {
+ mDocIndex = docIndex;
+ }
+ }
+}
+
+gfxSVGGlyphs::~gfxSVGGlyphs()
+{
+ hb_blob_destroy(mSVGData);
+}
+
+void
+gfxSVGGlyphs::DidRefresh()
+{
+ mFontEntry->NotifyGlyphsChanged();
+}
+
+/*
+ * Comparison operator for finding a range containing a given glyph ID. Simply
+ * checks whether |key| is less (greater) than every element of |range|, in
+ * which case return |key| < |range| (|key| > |range|). Otherwise |key| is in
+ * |range|, in which case return equality.
+ * The total ordering here is guaranteed by
+ * (1) the index ranges being disjoint; and
+ * (2) the (sole) key always being a singleton, so intersection => containment
+ * (note that this is wrong if we have more than one intersection or two
+ * sets intersecting of size > 1 -- so... don't do that)
+ */
+/* static */ int
+gfxSVGGlyphs::CompareIndexEntries(const void *aKey, const void *aEntry)
+{
+ const uint32_t key = *(uint32_t*)aKey;
+ const IndexEntry *entry = (const IndexEntry*)aEntry;
+
+ if (key < uint16_t(entry->mStartGlyph)) {
+ return -1;
+ }
+ if (key > uint16_t(entry->mEndGlyph)) {
+ return 1;
+ }
+ return 0;
+}
+
+gfxSVGGlyphsDocument *
+gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
+{
+ if (!mDocIndex) {
+ // Invalid table
+ return nullptr;
+ }
+
+ IndexEntry *entry = (IndexEntry*)bsearch(&aGlyphId, mDocIndex->mEntries,
+ uint16_t(mDocIndex->mNumEntries),
+ sizeof(IndexEntry),
+ CompareIndexEntries);
+ if (!entry) {
+ return nullptr;
+ }
+
+ gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset);
+
+ if (!result) {
+ unsigned int length;
+ const uint8_t *data = (const uint8_t*)hb_blob_get_data(mSVGData, &length);
+ if (entry->mDocOffset > 0 &&
+ uint64_t(mHeader->mDocIndexOffset) + entry->mDocOffset + entry->mDocLength <= length) {
+ result = new gfxSVGGlyphsDocument(data + mHeader->mDocIndexOffset + entry->mDocOffset,
+ entry->mDocLength, this);
+ mGlyphDocs.Put(entry->mDocOffset, result);
+ }
+ }
+
+ return result;
+}
+
+nsresult
+gfxSVGGlyphsDocument::SetupPresentation()
+{
+ nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ nsXPIDLCString contractId;
+ nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = do_GetService(contractId);
+ NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = viewer->Init(nullptr, gfx::IntRect(0, 0, 1000, 1000));
+ if (NS_SUCCEEDED(rv)) {
+ rv = viewer->Open(nullptr, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIPresShell> presShell;
+ rv = viewer->GetPresShell(getter_AddRefs(presShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsPresContext* presContext = presShell->GetPresContext();
+ presContext->SetIsGlyph(true);
+
+ if (!presShell->DidInitialize()) {
+ nsRect rect = presContext->GetVisibleArea();
+ rv = presShell->Initialize(rect.width, rect.height);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mDocument->FlushPendingNotifications(Flush_Layout);
+
+ nsSMILAnimationController* controller = mDocument->GetAnimationController();
+ if (controller) {
+ controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
+ }
+ mDocument->ImageTracker()->SetAnimatingState(true);
+
+ mViewer = viewer;
+ mPresShell = presShell;
+ mPresShell->AddPostRefreshObserver(this);
+
+ return NS_OK;
+}
+
+void
+gfxSVGGlyphsDocument::DidRefresh()
+{
+ mOwner->DidRefresh();
+}
+
+/**
+ * Walk the DOM tree to find all glyph elements and insert them into the lookup
+ * table
+ * @param aElem The element to search from
+ */
+void
+gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem)
+{
+ for (nsIContent *child = aElem->GetLastChild(); child;
+ child = child->GetPreviousSibling()) {
+ if (!child->IsElement()) {
+ continue;
+ }
+ FindGlyphElements(child->AsElement());
+ }
+
+ InsertGlyphId(aElem);
+}
+
+/**
+ * If there exists an SVG glyph with the specified glyph id, render it and return true
+ * If no such glyph exists, or in the case of an error return false
+ * @param aContext The thebes aContext to draw to
+ * @param aGlyphId The glyph id
+ * @return true iff rendering succeeded
+ */
+bool
+gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
+ SVGContextPaint* aContextPaint)
+{
+ gfxContextAutoSaveRestore aContextRestorer(aContext);
+
+ Element *glyph = mGlyphIdMap.Get(aGlyphId);
+ NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
+
+ AutoSetRestoreSVGContextPaint autoSetRestore(aContextPaint, glyph->OwnerDoc());
+
+ return nsSVGUtils::PaintSVGGlyph(glyph, aContext);
+}
+
+bool
+gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
+ gfxRect *aResult)
+{
+ Element *glyph = mGlyphIdMap.Get(aGlyphId);
+ NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
+
+ return nsSVGUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
+}
+
+Element *
+gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId)
+{
+ Element *elem;
+
+ if (!mGlyphIdMap.Get(aGlyphId, &elem)) {
+ elem = nullptr;
+ if (gfxSVGGlyphsDocument *set = FindOrCreateGlyphsDocument(aGlyphId)) {
+ elem = set->GetGlyphElement(aGlyphId);
+ }
+ mGlyphIdMap.Put(aGlyphId, elem);
+ }
+
+ return elem;
+}
+
+bool
+gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
+{
+ return !!GetGlyphElement(aGlyphId);
+}
+
+size_t
+gfxSVGGlyphs::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // We don't include the size of mSVGData here, because (depending on the
+ // font backend implementation) it will either wrap a block of data owned
+ // by the system (and potentially shared), or a table that's in our font
+ // table cache and therefore already counted.
+ size_t result = aMallocSizeOf(this)
+ + mGlyphDocs.ShallowSizeOfExcludingThis(aMallocSizeOf)
+ + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mGlyphDocs.ConstIter(); !iter.Done(); iter.Next()) {
+ result += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ return result;
+}
+
+Element *
+gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
+{
+ return mGlyphIdMap.Get(aGlyphId);
+}
+
+gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
+ uint32_t aBufLen,
+ gfxSVGGlyphs *aSVGGlyphs)
+ : mOwner(aSVGGlyphs)
+{
+ if (aBufLen >= 14 && aBuffer[0] == 31 && aBuffer[1] == 139) {
+ // It's a gzip-compressed document; decompress it before parsing.
+ // The original length (modulo 2^32) is found in the last 4 bytes
+ // of the data, stored in little-endian format. We read it as
+ // individual bytes to avoid possible alignment issues.
+ // (Note that if the original length was >2^32, then origLen here
+ // will be incorrect; but then the inflate() call will not return
+ // Z_STREAM_END and we'll bail out safely.)
+ size_t origLen = (size_t(aBuffer[aBufLen - 1]) << 24) +
+ (size_t(aBuffer[aBufLen - 2]) << 16) +
+ (size_t(aBuffer[aBufLen - 3]) << 8) +
+ size_t(aBuffer[aBufLen - 4]);
+ AutoTArray<uint8_t, 4096> outBuf;
+ if (outBuf.SetLength(origLen, mozilla::fallible)) {
+ z_stream s = {0};
+ s.next_in = const_cast<Byte*>(aBuffer);
+ s.avail_in = aBufLen;
+ s.next_out = outBuf.Elements();
+ s.avail_out = outBuf.Length();
+ // The magic number 16 here is the zlib flag to expect gzip format,
+ // see http://www.zlib.net/manual.html#Advanced
+ if (Z_OK == inflateInit2(&s, 16 + MAX_WBITS)) {
+ int result = inflate(&s, Z_FINISH);
+ if (Z_STREAM_END == result) {
+ MOZ_ASSERT(size_t(s.next_out - outBuf.Elements()) == origLen);
+ ParseDocument(outBuf.Elements(), outBuf.Length());
+ } else {
+ NS_WARNING("Failed to decompress SVG glyphs document");
+ }
+ inflateEnd(&s);
+ }
+ } else {
+ NS_WARNING("Failed to allocate memory for SVG glyphs document");
+ }
+ } else {
+ ParseDocument(aBuffer, aBufLen);
+ }
+
+ if (!mDocument) {
+ NS_WARNING("Could not parse SVG glyphs document");
+ return;
+ }
+
+ Element *root = mDocument->GetRootElement();
+ if (!root) {
+ NS_WARNING("Could not parse SVG glyphs document");
+ return;
+ }
+
+ nsresult rv = SetupPresentation();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Couldn't setup presentation for SVG glyphs document");
+ return;
+ }
+
+ FindGlyphElements(root);
+}
+
+gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
+{
+ if (mDocument) {
+ mDocument->OnPageHide(false, nullptr);
+ }
+ if (mPresShell) {
+ mPresShell->RemovePostRefreshObserver(this);
+ }
+ if (mViewer) {
+ mViewer->Close(nullptr);
+ mViewer->Destroy();
+ }
+}
+
+static nsresult
+CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
+ nsCOMPtr<nsIInputStream> &aResult)
+{
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
+ reinterpret_cast<const char *>(aBuffer),
+ aBufLen, NS_ASSIGNMENT_DEPEND);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> aBufferedStream;
+ if (!NS_InputStreamIsBuffered(stream)) {
+ rv = NS_NewBufferedInputStream(getter_AddRefs(aBufferedStream), stream, 4096);
+ NS_ENSURE_SUCCESS(rv, rv);
+ stream = aBufferedStream;
+ }
+
+ aResult = stream;
+
+ return NS_OK;
+}
+
+nsresult
+gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen)
+{
+ // Mostly pulled from nsDOMParser::ParseFromStream
+
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> uri;
+ nsHostObjectProtocolHandler::GenerateURIString(NS_LITERAL_CSTRING(FONTTABLEURI_SCHEME),
+ nullptr,
+ mSVGGlyphsDocumentURI);
+
+ rv = NS_NewURI(getter_AddRefs(uri), mSVGGlyphsDocumentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ rv = NS_NewDOMDocument(getter_AddRefs(domDoc),
+ EmptyString(), // aNamespaceURI
+ EmptyString(), // aQualifiedName
+ nullptr, // aDoctype
+ uri, uri, principal,
+ false, // aLoadedAsData
+ nullptr, // aEventObject
+ DocumentFlavorSVG);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
+ if (!document) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+ uri,
+ nullptr, //aStream
+ principal,
+ nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+ nsIContentPolicy::TYPE_OTHER,
+ SVG_CONTENT_TYPE,
+ UTF8_CHARSET);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Set this early because various decisions during page-load depend on it.
+ document->SetIsBeingUsedAsImage();
+ document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
+
+ nsCOMPtr<nsIStreamListener> listener;
+ rv = document->StartDocumentLoad("external-resource", channel,
+ nullptr, // aLoadGroup
+ nullptr, // aContainer
+ getter_AddRefs(listener),
+ true /* aReset */);
+ if (NS_FAILED(rv) || !listener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = listener->OnStartRequest(channel, nullptr /* aContext */);
+ if (NS_FAILED(rv)) {
+ channel->Cancel(rv);
+ }
+
+ nsresult status;
+ channel->GetStatus(&status);
+ if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
+ rv = listener->OnDataAvailable(channel, nullptr /* aContext */, stream, 0, aBufLen);
+ if (NS_FAILED(rv)) {
+ channel->Cancel(rv);
+ }
+ channel->GetStatus(&status);
+ }
+
+ rv = listener->OnStopRequest(channel, nullptr /* aContext */, status);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ document.swap(mDocument);
+
+ return NS_OK;
+}
+
+void
+gfxSVGGlyphsDocument::InsertGlyphId(Element *aGlyphElement)
+{
+ nsAutoString glyphIdStr;
+ static const uint32_t glyphPrefixLength = 5;
+ // The maximum glyph ID is 65535 so the maximum length of the numeric part
+ // is 5.
+ if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, glyphIdStr) ||
+ !StringBeginsWith(glyphIdStr, NS_LITERAL_STRING("glyph")) ||
+ glyphIdStr.Length() > glyphPrefixLength + 5) {
+ return;
+ }
+
+ uint32_t id = 0;
+ for (uint32_t i = glyphPrefixLength; i < glyphIdStr.Length(); ++i) {
+ char16_t ch = glyphIdStr.CharAt(i);
+ if (ch < '0' || ch > '9') {
+ return;
+ }
+ if (ch == '0' && i == glyphPrefixLength) {
+ return;
+ }
+ id = id * 10 + (ch - '0');
+ }
+
+ mGlyphIdMap.Put(id, aGlyphElement);
+}
+
+size_t
+gfxSVGGlyphsDocument::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this)
+ + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf)
+ + mSVGGlyphsDocumentURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+}
diff --git a/system/graphics/thebes/gfxSVGGlyphs.h b/system/graphics/thebes/gfxSVGGlyphs.h
new file mode 100644
index 000000000..8ebebb44b
--- /dev/null
+++ b/system/graphics/thebes/gfxSVGGlyphs.h
@@ -0,0 +1,242 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_SVG_GLYPHS_WRAPPER_H
+#define GFX_SVG_GLYPHS_WRAPPER_H
+
+#include "gfxFontUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "nsString.h"
+#include "nsClassHashtable.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "gfxPattern.h"
+#include "mozilla/gfx/UserData.h"
+#include "mozilla/SVGContextPaint.h"
+#include "nsRefreshDriver.h"
+
+class nsIDocument;
+class nsIContentViewer;
+class nsIPresShell;
+class gfxSVGGlyphs;
+
+namespace mozilla {
+class SVGContextPaint;
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
+/**
+ * Wraps an SVG document contained in the SVG table of an OpenType font.
+ * There may be multiple SVG documents in an SVG table which we lazily parse
+ * so we have an instance of this class for every document in the SVG table
+ * which contains a glyph ID which has been used
+ * Finds and looks up elements contained in the SVG document which have glyph
+ * mappings to be drawn by gfxSVGGlyphs
+ */
+class gfxSVGGlyphsDocument final : public nsAPostRefreshObserver
+{
+ typedef mozilla::dom::Element Element;
+
+public:
+ gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen,
+ gfxSVGGlyphs *aSVGGlyphs);
+
+ Element *GetGlyphElement(uint32_t aGlyphId);
+
+ ~gfxSVGGlyphsDocument();
+
+ virtual void DidRefresh() override;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+private:
+ nsresult ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen);
+
+ nsresult SetupPresentation();
+
+ void FindGlyphElements(Element *aElement);
+
+ void InsertGlyphId(Element *aGlyphElement);
+
+ // Weak so as not to create a cycle. mOwner owns us so this can't dangle.
+ gfxSVGGlyphs* mOwner;
+ nsCOMPtr<nsIDocument> mDocument;
+ nsCOMPtr<nsIContentViewer> mViewer;
+ nsCOMPtr<nsIPresShell> mPresShell;
+
+ nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
+
+ nsCString mSVGGlyphsDocumentURI;
+};
+
+/**
+ * Used by |gfxFontEntry| to represent the SVG table of an OpenType font.
+ * Handles lazy parsing of the SVG documents in the table, looking up SVG glyphs
+ * and rendering SVG glyphs.
+ * Each |gfxFontEntry| owns at most one |gfxSVGGlyphs| instance.
+ */
+class gfxSVGGlyphs
+{
+private:
+ typedef mozilla::dom::Element Element;
+
+public:
+ /**
+ * @param aSVGTable The SVG table from the OpenType font
+ *
+ * The gfxSVGGlyphs object takes over ownership of the blob references
+ * that are passed in, and will hb_blob_destroy() them when finished;
+ * the caller should -not- destroy these references.
+ */
+ gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry);
+
+ /**
+ * Releases our references to the SVG table and cleans up everything else.
+ */
+ ~gfxSVGGlyphs();
+
+ /**
+ * This is called when the refresh driver has ticked.
+ */
+ void DidRefresh();
+
+ /**
+ * Find the |gfxSVGGlyphsDocument| containing an SVG glyph for |aGlyphId|.
+ * If |aGlyphId| does not map to an SVG document, return null.
+ * If a |gfxSVGGlyphsDocument| has not been created for the document, create one.
+ */
+ gfxSVGGlyphsDocument *FindOrCreateGlyphsDocument(uint32_t aGlyphId);
+
+ /**
+ * Return true iff there is an SVG glyph for |aGlyphId|
+ */
+ bool HasSVGGlyph(uint32_t aGlyphId);
+
+ /**
+ * Render the SVG glyph for |aGlyphId|
+ * @param aContextPaint Information on text context paints.
+ * See |SVGContextPaint|.
+ */
+ bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
+ mozilla::SVGContextPaint* aContextPaint);
+
+ /**
+ * Get the extents for the SVG glyph associated with |aGlyphId|
+ * @param aSVGToAppSpace The matrix mapping the SVG glyph space to the
+ * target context space
+ */
+ bool GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
+ gfxRect *aResult);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+private:
+ Element *GetGlyphElement(uint32_t aGlyphId);
+
+ nsClassHashtable<nsUint32HashKey, gfxSVGGlyphsDocument> mGlyphDocs;
+ nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
+
+ hb_blob_t *mSVGData;
+ gfxFontEntry *mFontEntry;
+
+ const struct Header {
+ mozilla::AutoSwap_PRUint16 mVersion;
+ mozilla::AutoSwap_PRUint32 mDocIndexOffset;
+ mozilla::AutoSwap_PRUint32 mColorPalettesOffset;
+ } *mHeader;
+
+ struct IndexEntry {
+ mozilla::AutoSwap_PRUint16 mStartGlyph;
+ mozilla::AutoSwap_PRUint16 mEndGlyph;
+ mozilla::AutoSwap_PRUint32 mDocOffset;
+ mozilla::AutoSwap_PRUint32 mDocLength;
+ };
+
+ const struct DocIndex {
+ mozilla::AutoSwap_PRUint16 mNumEntries;
+ IndexEntry mEntries[1]; /* actual length = mNumEntries */
+ } *mDocIndex;
+
+ static int CompareIndexEntries(const void *_a, const void *_b);
+};
+
+/**
+ * XXX This is a complete hack and should die (see bug 1291494).
+ *
+ * This class is used when code fails to pass through an SVGContextPaint from
+ * the context in which we are painting. In that case we create one of these
+ * as a fallback and have it wrap the gfxContext's current gfxPattern and
+ * pretend that that is the paint context's fill pattern. In some contexts
+ * that will be the case, in others it will not. As we convert more code to
+ * Moz2D the less likely it is that this hack will work. It will also make
+ * converting to Moz2D harder.
+ */
+class SimpleTextContextPaint : public mozilla::SVGContextPaint
+{
+private:
+ static const mozilla::gfx::Color sZero;
+
+ static gfxMatrix SetupDeviceToPatternMatrix(gfxPattern *aPattern,
+ const gfxMatrix& aCTM)
+ {
+ if (!aPattern) {
+ return gfxMatrix();
+ }
+ gfxMatrix deviceToUser = aCTM;
+ if (!deviceToUser.Invert()) {
+ return gfxMatrix(0, 0, 0, 0, 0, 0); // singular
+ }
+ return deviceToUser * aPattern->GetMatrix();
+ }
+
+public:
+ SimpleTextContextPaint(gfxPattern *aFillPattern, gfxPattern *aStrokePattern,
+ const gfxMatrix& aCTM) :
+ mFillPattern(aFillPattern ? aFillPattern : new gfxPattern(sZero)),
+ mStrokePattern(aStrokePattern ? aStrokePattern : new gfxPattern(sZero))
+ {
+ mFillMatrix = SetupDeviceToPatternMatrix(aFillPattern, aCTM);
+ mStrokeMatrix = SetupDeviceToPatternMatrix(aStrokePattern, aCTM);
+ }
+
+ already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
+ float aOpacity,
+ const gfxMatrix& aCTM) {
+ if (mFillPattern) {
+ mFillPattern->SetMatrix(aCTM * mFillMatrix);
+ }
+ RefPtr<gfxPattern> fillPattern = mFillPattern;
+ return fillPattern.forget();
+ }
+
+ already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
+ float aOpacity,
+ const gfxMatrix& aCTM) {
+ if (mStrokePattern) {
+ mStrokePattern->SetMatrix(aCTM * mStrokeMatrix);
+ }
+ RefPtr<gfxPattern> strokePattern = mStrokePattern;
+ return strokePattern.forget();
+ }
+
+ float GetFillOpacity() const {
+ return mFillPattern ? 1.0f : 0.0f;
+ }
+
+ float GetStrokeOpacity() const {
+ return mStrokePattern ? 1.0f : 0.0f;
+ }
+
+private:
+ RefPtr<gfxPattern> mFillPattern;
+ RefPtr<gfxPattern> mStrokePattern;
+
+ // Device space to pattern space transforms
+ gfxMatrix mFillMatrix;
+ gfxMatrix mStrokeMatrix;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxScriptItemizer.cpp b/system/graphics/thebes/gfxScriptItemizer.cpp
new file mode 100644
index 000000000..90e0ca98c
--- /dev/null
+++ b/system/graphics/thebes/gfxScriptItemizer.cpp
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is based on usc_impl.c from ICU 4.2.0.1, slightly adapted
+ * for use within Mozilla Gecko, separate from a standard ICU build.
+ *
+ * The original ICU license of the code follows:
+ *
+ * ICU License - ICU 1.8.1 and later
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright (c) 1995-2009 International Business Machines Corporation and
+ * others
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, provided that the above copyright notice(s) and this
+ * permission notice appear in all copies of the Software and that both the
+ * above copyright notice(s) and this permission notice appear in supporting
+ * documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
+ * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ *
+ * All trademarks and registered trademarks mentioned herein are the property
+ * of their respective owners.
+ */
+
+#include "gfxScriptItemizer.h"
+#include "nsUnicodeProperties.h"
+#include "nsCharTraits.h"
+#include "harfbuzz/hb.h"
+
+#define MOD(sp) ((sp) % PAREN_STACK_DEPTH)
+#define LIMIT_INC(sp) (((sp) < PAREN_STACK_DEPTH)? (sp) + 1 : PAREN_STACK_DEPTH)
+#define INC(sp,count) (MOD((sp) + (count)))
+#define INC1(sp) (INC(sp, 1))
+#define DEC(sp,count) (MOD((sp) + PAREN_STACK_DEPTH - (count)))
+#define DEC1(sp) (DEC(sp, 1))
+#define STACK_IS_EMPTY() (pushCount <= 0)
+#define STACK_IS_NOT_EMPTY() (! STACK_IS_EMPTY())
+#define TOP() (parenStack[parenSP])
+#define SYNC_FIXUP() (fixupCount = 0)
+
+using namespace mozilla::unicode;
+
+void
+gfxScriptItemizer::push(uint32_t endPairChar, Script newScriptCode)
+{
+ pushCount = LIMIT_INC(pushCount);
+ fixupCount = LIMIT_INC(fixupCount);
+
+ parenSP = INC1(parenSP);
+ parenStack[parenSP].endPairChar = endPairChar;
+ parenStack[parenSP].scriptCode = newScriptCode;
+}
+
+void
+gfxScriptItemizer::pop()
+{
+ if (STACK_IS_EMPTY()) {
+ return;
+ }
+
+ if (fixupCount > 0) {
+ fixupCount -= 1;
+ }
+
+ pushCount -= 1;
+ parenSP = DEC1(parenSP);
+
+ /* If the stack is now empty, reset the stack
+ pointers to their initial values.
+ */
+ if (STACK_IS_EMPTY()) {
+ parenSP = -1;
+ }
+}
+
+void
+gfxScriptItemizer::fixup(Script newScriptCode)
+{
+ int32_t fixupSP = DEC(parenSP, fixupCount);
+
+ while (fixupCount-- > 0) {
+ fixupSP = INC1(fixupSP);
+ parenStack[fixupSP].scriptCode = newScriptCode;
+ }
+}
+
+static inline bool
+SameScript(Script runScript, Script currCharScript)
+{
+ return runScript <= Script::INHERITED ||
+ currCharScript <= Script::INHERITED ||
+ currCharScript == runScript;
+}
+
+gfxScriptItemizer::gfxScriptItemizer(const char16_t *src, uint32_t length)
+ : textPtr(src), textLength(length)
+{
+ reset();
+}
+
+void
+gfxScriptItemizer::SetText(const char16_t *src, uint32_t length)
+{
+ textPtr = src;
+ textLength = length;
+
+ reset();
+}
+
+bool
+gfxScriptItemizer::Next(uint32_t& aRunStart, uint32_t& aRunLimit,
+ Script& aRunScript)
+{
+ /* if we've fallen off the end of the text, we're done */
+ if (scriptLimit >= textLength) {
+ return false;
+ }
+
+ SYNC_FIXUP();
+ scriptCode = Script::COMMON;
+
+ for (scriptStart = scriptLimit; scriptLimit < textLength; scriptLimit += 1) {
+ uint32_t ch;
+ Script sc;
+ uint32_t startOfChar = scriptLimit;
+
+ ch = textPtr[scriptLimit];
+
+ /* decode UTF-16 (may be surrogate pair) */
+ if (NS_IS_HIGH_SURROGATE(ch) && scriptLimit < textLength - 1) {
+ uint32_t low = textPtr[scriptLimit + 1];
+ if (NS_IS_LOW_SURROGATE(low)) {
+ ch = SURROGATE_TO_UCS4(ch, low);
+ scriptLimit += 1;
+ }
+ }
+
+ // Initialize gc to UNASSIGNED; we'll only set it to the true GC
+ // if the character has script=COMMON, otherwise we don't care.
+ uint8_t gc = HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
+
+ sc = GetScriptCode(ch);
+ if (sc == Script::COMMON) {
+ /*
+ * Paired character handling:
+ *
+ * if it's an open character, push it onto the stack.
+ * if it's a close character, find the matching open on the
+ * stack, and use that script code. Any non-matching open
+ * characters above it on the stack will be popped.
+ *
+ * We only do this if the script is COMMON; for chars with
+ * specific script assignments, we just use them as-is.
+ */
+ gc = GetGeneralCategory(ch);
+ if (gc == HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION) {
+ uint32_t endPairChar = mozilla::unicode::GetMirroredChar(ch);
+ if (endPairChar != ch) {
+ push(endPairChar, scriptCode);
+ }
+ } else if (gc == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION &&
+ HasMirroredChar(ch))
+ {
+ while (STACK_IS_NOT_EMPTY() && TOP().endPairChar != ch) {
+ pop();
+ }
+
+ if (STACK_IS_NOT_EMPTY()) {
+ sc = TOP().scriptCode;
+ }
+ }
+ }
+
+ if (SameScript(scriptCode, sc)) {
+ if (scriptCode <= Script::INHERITED &&
+ sc > Script::INHERITED)
+ {
+ scriptCode = sc;
+ fixup(scriptCode);
+ }
+
+ /*
+ * if this character is a close paired character,
+ * pop the matching open character from the stack
+ */
+ if (gc == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION &&
+ HasMirroredChar(ch)) {
+ pop();
+ }
+ } else {
+ /*
+ * reset scriptLimit in case it was advanced during reading a
+ * multiple-code-unit character
+ */
+ scriptLimit = startOfChar;
+
+ break;
+ }
+ }
+
+ aRunStart = scriptStart;
+ aRunLimit = scriptLimit;
+ aRunScript = scriptCode;
+
+ return true;
+}
diff --git a/system/graphics/thebes/gfxScriptItemizer.h b/system/graphics/thebes/gfxScriptItemizer.h
new file mode 100644
index 000000000..8089738dc
--- /dev/null
+++ b/system/graphics/thebes/gfxScriptItemizer.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is based on usc_impl.c from ICU 4.2.0.1, slightly adapted
+ * for use within Mozilla Gecko, separate from a standard ICU build.
+ *
+ * The original ICU license of the code follows:
+ *
+ * ICU License - ICU 1.8.1 and later
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright (c) 1995-2009 International Business Machines Corporation and
+ * others
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, provided that the above copyright notice(s) and this
+ * permission notice appear in all copies of the Software and that both the
+ * above copyright notice(s) and this permission notice appear in supporting
+ * documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
+ * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ *
+ * All trademarks and registered trademarks mentioned herein are the property
+ * of their respective owners.
+ */
+
+#ifndef GFX_SCRIPTITEMIZER_H
+#define GFX_SCRIPTITEMIZER_H
+
+#include <stdint.h>
+#include "nsUnicodeScriptCodes.h"
+
+#define PAREN_STACK_DEPTH 32
+
+class gfxScriptItemizer
+{
+public:
+ typedef mozilla::unicode::Script Script;
+
+ gfxScriptItemizer(const char16_t *src, uint32_t length);
+
+ void SetText(const char16_t *src, uint32_t length);
+
+ bool Next(uint32_t& aRunStart, uint32_t& aRunLimit,
+ Script& aRunScript);
+
+protected:
+ void reset() {
+ scriptStart = 0;
+ scriptLimit = 0;
+ scriptCode = Script::INVALID;
+ parenSP = -1;
+ pushCount = 0;
+ fixupCount = 0;
+ }
+
+ void push(uint32_t endPairChar, Script newScriptCode);
+ void pop();
+ void fixup(Script newScriptCode);
+
+ struct ParenStackEntry {
+ uint32_t endPairChar;
+ Script scriptCode;
+ };
+
+ const char16_t *textPtr;
+ uint32_t textLength;
+
+ uint32_t scriptStart;
+ uint32_t scriptLimit;
+ Script scriptCode;
+
+ struct ParenStackEntry parenStack[PAREN_STACK_DEPTH];
+ uint32_t parenSP;
+ uint32_t pushCount;
+ uint32_t fixupCount;
+};
+
+#endif /* GFX_SCRIPTITEMIZER_H */
diff --git a/system/graphics/thebes/gfxSharedImageSurface.h b/system/graphics/thebes/gfxSharedImageSurface.h
new file mode 100644
index 000000000..9a954f981
--- /dev/null
+++ b/system/graphics/thebes/gfxSharedImageSurface.h
@@ -0,0 +1,24 @@
+// vim:set ts=4 sts=4 sw=4 et cin:
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_SHARED_IMAGESURFACE_H
+#define GFX_SHARED_IMAGESURFACE_H
+
+#include "gfxBaseSharedMemorySurface.h"
+
+class gfxSharedImageSurface : public gfxBaseSharedMemorySurface<gfxImageSurface, gfxSharedImageSurface>
+{
+ typedef gfxBaseSharedMemorySurface<gfxImageSurface, gfxSharedImageSurface> Super;
+ friend class gfxBaseSharedMemorySurface<gfxImageSurface, gfxSharedImageSurface>;
+private:
+ gfxSharedImageSurface(const mozilla::gfx::IntSize& aSize, long aStride,
+ gfxImageFormat aFormat,
+ const mozilla::ipc::Shmem& aShmem)
+ : Super(aSize, aStride, aFormat, aShmem)
+ {}
+};
+
+#endif /* GFX_SHARED_IMAGESURFACE_H */
diff --git a/system/graphics/thebes/gfxSkipChars.cpp b/system/graphics/thebes/gfxSkipChars.cpp
new file mode 100644
index 000000000..0cd53c87b
--- /dev/null
+++ b/system/graphics/thebes/gfxSkipChars.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxSkipChars.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/gfx/Logging.h"
+
+struct SkippedRangeStartComparator
+{
+ const uint32_t mOffset;
+ explicit SkippedRangeStartComparator(const uint32_t aOffset) : mOffset(aOffset) {}
+ int operator()(const gfxSkipChars::SkippedRange& aRange) const {
+ return (mOffset < aRange.Start()) ? -1 : 1;
+ }
+};
+
+void
+gfxSkipCharsIterator::SetOriginalOffset(int32_t aOffset)
+{
+ aOffset += mOriginalStringToSkipCharsOffset;
+ if (MOZ_UNLIKELY(uint32_t(aOffset) > mSkipChars->mCharCount)) {
+ gfxCriticalError() <<
+ "invalid offset " << aOffset <<
+ " for gfxSkipChars length " << mSkipChars->mCharCount;
+ aOffset = mSkipChars->mCharCount;
+ }
+
+ mOriginalStringOffset = aOffset;
+
+ const uint32_t rangeCount = mSkipChars->mRanges.Length();
+ if (rangeCount == 0) {
+ mSkippedStringOffset = aOffset;
+ return;
+ }
+
+ // at start of string?
+ if (aOffset == 0) {
+ mSkippedStringOffset = 0;
+ mCurrentRangeIndex =
+ rangeCount && mSkipChars->mRanges[0].Start() == 0 ? 0 : -1;
+ return;
+ }
+
+ // find the range that includes or precedes aOffset
+ const nsTArray<gfxSkipChars::SkippedRange>& ranges = mSkipChars->mRanges;
+ size_t idx;
+ mozilla::BinarySearchIf(ranges, 0, rangeCount,
+ SkippedRangeStartComparator(aOffset),
+ &idx);
+
+ if (idx == rangeCount) {
+ mCurrentRangeIndex = rangeCount - 1;
+ } else if (uint32_t(aOffset) < ranges[idx].Start()) {
+ mCurrentRangeIndex = idx - 1;
+ if (mCurrentRangeIndex == -1) {
+ mSkippedStringOffset = aOffset;
+ return;
+ }
+ } else {
+ mCurrentRangeIndex = idx;
+ }
+
+ const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
+ if (uint32_t(aOffset) < r.End()) {
+ mSkippedStringOffset = r.SkippedOffset();
+ return;
+ }
+
+ mSkippedStringOffset = aOffset - r.NextDelta();
+}
+
+struct SkippedRangeOffsetComparator
+{
+ const uint32_t mOffset;
+ explicit SkippedRangeOffsetComparator(const uint32_t aOffset) : mOffset(aOffset) {}
+ int operator()(const gfxSkipChars::SkippedRange& aRange) const {
+ return (mOffset < aRange.SkippedOffset()) ? -1 : 1;
+ }
+};
+
+void
+gfxSkipCharsIterator::SetSkippedOffset(uint32_t aOffset)
+{
+ NS_ASSERTION((mSkipChars->mRanges.IsEmpty() &&
+ aOffset <= mSkipChars->mCharCount) ||
+ (aOffset <= mSkipChars->LastRange().SkippedOffset() +
+ mSkipChars->mCharCount -
+ mSkipChars->LastRange().End()),
+ "Invalid skipped offset");
+ mSkippedStringOffset = aOffset;
+
+ uint32_t rangeCount = mSkipChars->mRanges.Length();
+ if (rangeCount == 0) {
+ mOriginalStringOffset = aOffset;
+ return;
+ }
+
+ const nsTArray<gfxSkipChars::SkippedRange>& ranges = mSkipChars->mRanges;
+ size_t idx;
+ mozilla::BinarySearchIf(ranges, 0, rangeCount,
+ SkippedRangeOffsetComparator(aOffset),
+ &idx);
+
+ if (idx == rangeCount) {
+ mCurrentRangeIndex = rangeCount - 1;
+ } else if (aOffset < ranges[idx].SkippedOffset()) {
+ mCurrentRangeIndex = idx - 1;
+ if (mCurrentRangeIndex == -1) {
+ mOriginalStringOffset = aOffset;
+ return;
+ }
+ } else {
+ mCurrentRangeIndex = idx;
+ }
+
+ const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
+ mOriginalStringOffset = r.End() + aOffset - r.SkippedOffset();
+}
+
+bool
+gfxSkipCharsIterator::IsOriginalCharSkipped(int32_t* aRunLength) const
+{
+ if (mCurrentRangeIndex == -1) {
+ // we're before the first skipped range (if any)
+ if (aRunLength) {
+ uint32_t end = mSkipChars->mRanges.IsEmpty() ?
+ mSkipChars->mCharCount : mSkipChars->mRanges[0].Start();
+ *aRunLength = end - mOriginalStringOffset;
+ }
+ return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
+ }
+
+ const gfxSkipChars::SkippedRange& range =
+ mSkipChars->mRanges[mCurrentRangeIndex];
+
+ if (uint32_t(mOriginalStringOffset) < range.End()) {
+ if (aRunLength) {
+ *aRunLength = range.End() - mOriginalStringOffset;
+ }
+ return true;
+ }
+
+ if (aRunLength) {
+ uint32_t end =
+ uint32_t(mCurrentRangeIndex) + 1 < mSkipChars->mRanges.Length() ?
+ mSkipChars->mRanges[mCurrentRangeIndex + 1].Start() :
+ mSkipChars->mCharCount;
+ *aRunLength = end - mOriginalStringOffset;
+ }
+
+ return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
+}
diff --git a/system/graphics/thebes/gfxSkipChars.h b/system/graphics/thebes/gfxSkipChars.h
new file mode 100644
index 000000000..352a16090
--- /dev/null
+++ b/system/graphics/thebes/gfxSkipChars.h
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_SKIP_CHARS_H
+#define GFX_SKIP_CHARS_H
+
+#include "nsTArray.h"
+
+/*
+ * gfxSkipChars is a data structure representing a list of characters that
+ * have been skipped. The initial string is called the "original string"
+ * and after skipping some characters, the result is called the "skipped string".
+ * gfxSkipChars provides efficient ways to translate between offsets in the
+ * original string and the skipped string. It is used by textrun code to keep
+ * track of offsets before and after text transformations such as whitespace
+ * compression and control code deletion.
+ */
+
+/**
+ * The gfxSkipChars is represented as a sorted array of skipped ranges.
+ *
+ * A freshly-created gfxSkipChars means "all chars kept".
+ */
+class gfxSkipChars
+{
+ friend struct SkippedRangeStartComparator;
+ friend struct SkippedRangeOffsetComparator;
+
+private:
+ class SkippedRange
+ {
+ public:
+ SkippedRange(uint32_t aOffset, uint32_t aLength, uint32_t aDelta)
+ : mOffset(aOffset), mLength(aLength), mDelta(aDelta)
+ { }
+
+ uint32_t Start() const
+ {
+ return mOffset;
+ }
+
+ uint32_t End() const
+ {
+ return mOffset + mLength;
+ }
+
+ uint32_t Length() const
+ {
+ return mLength;
+ }
+
+ uint32_t SkippedOffset() const
+ {
+ return mOffset - mDelta;
+ }
+
+ uint32_t Delta() const
+ {
+ return mDelta;
+ }
+
+ uint32_t NextDelta() const
+ {
+ return mDelta + mLength;
+ }
+
+ void Extend(uint32_t aChars)
+ {
+ mLength += aChars;
+ }
+
+ private:
+ uint32_t mOffset; // original-string offset at which we want to skip
+ uint32_t mLength; // number of skipped chars at this offset
+ uint32_t mDelta; // sum of lengths of preceding skipped-ranges
+ };
+
+public:
+ gfxSkipChars()
+ : mCharCount(0)
+ { }
+
+ void SkipChars(uint32_t aChars)
+ {
+ NS_ASSERTION(mCharCount + aChars > mCharCount,
+ "Character count overflow");
+ uint32_t rangeCount = mRanges.Length();
+ uint32_t delta = 0;
+ if (rangeCount > 0) {
+ SkippedRange& lastRange = mRanges[rangeCount - 1];
+ if (lastRange.End() == mCharCount) {
+ lastRange.Extend(aChars);
+ mCharCount += aChars;
+ return;
+ }
+ delta = lastRange.NextDelta();
+ }
+ mRanges.AppendElement(SkippedRange(mCharCount, aChars, delta));
+ mCharCount += aChars;
+ }
+
+ void KeepChars(uint32_t aChars)
+ {
+ NS_ASSERTION(mCharCount + aChars > mCharCount,
+ "Character count overflow");
+ mCharCount += aChars;
+ }
+
+ void SkipChar()
+ {
+ SkipChars(1);
+ }
+
+ void KeepChar()
+ {
+ KeepChars(1);
+ }
+
+ void TakeFrom(gfxSkipChars* aSkipChars)
+ {
+ mRanges.SwapElements(aSkipChars->mRanges);
+ mCharCount = aSkipChars->mCharCount;
+ aSkipChars->mCharCount = 0;
+ }
+
+ int32_t GetOriginalCharCount() const
+ {
+ return mCharCount;
+ }
+
+ const SkippedRange& LastRange() const
+ {
+ // this is only valid if mRanges is non-empty; no assertion here
+ // because nsTArray will already assert if we abuse it
+ return mRanges[mRanges.Length() - 1];
+ }
+
+ friend class gfxSkipCharsIterator;
+
+private:
+ nsTArray<SkippedRange> mRanges;
+ uint32_t mCharCount;
+};
+
+/**
+ * A gfxSkipCharsIterator represents a position in the original string. It lets you
+ * map efficiently to and from positions in the string after skipped characters
+ * have been removed. You can also specify an offset that is added to all
+ * incoming original string offsets and subtracted from all outgoing original
+ * string offsets --- useful when the gfxSkipChars corresponds to something
+ * offset from the original DOM coordinates, which it often does for gfxTextRuns.
+ *
+ * The current positions (in both the original and skipped strings) are
+ * always constrained to be >= 0 and <= the string length. When the position
+ * is equal to the string length, it is at the end of the string. The current
+ * positions do not include any aOriginalStringToSkipCharsOffset.
+ *
+ * When the position in the original string corresponds to a skipped character,
+ * the skipped-characters offset is the offset of the next unskipped character,
+ * or the skipped-characters string length if there is no next unskipped character.
+ */
+class gfxSkipCharsIterator
+{
+public:
+ /**
+ * @param aOriginalStringToSkipCharsOffset add this to all incoming and
+ * outgoing original string offsets
+ */
+ gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
+ int32_t aOriginalStringToSkipCharsOffset,
+ int32_t aOriginalStringOffset)
+ : mSkipChars(&aSkipChars),
+ mOriginalStringOffset(0),
+ mSkippedStringOffset(0),
+ mCurrentRangeIndex(-1),
+ mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
+ {
+ SetOriginalOffset(aOriginalStringOffset);
+ }
+
+ explicit gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
+ int32_t aOriginalStringToSkipCharsOffset = 0)
+ : mSkipChars(&aSkipChars),
+ mOriginalStringOffset(0),
+ mSkippedStringOffset(0),
+ mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
+ {
+ mCurrentRangeIndex =
+ mSkipChars->mRanges.IsEmpty() ||
+ mSkipChars->mRanges[0].Start() > 0 ? -1 : 0;
+ }
+
+ gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
+ : mSkipChars(aIterator.mSkipChars),
+ mOriginalStringOffset(aIterator.mOriginalStringOffset),
+ mSkippedStringOffset(aIterator.mSkippedStringOffset),
+ mCurrentRangeIndex(aIterator.mCurrentRangeIndex),
+ mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset)
+ { }
+
+ /**
+ * The empty constructor creates an object that is useless until it is assigned.
+ */
+ gfxSkipCharsIterator()
+ : mSkipChars(nullptr)
+ { }
+
+ /**
+ * Return true if this iterator is properly initialized and usable.
+ */
+ bool IsInitialized()
+ {
+ return mSkipChars != nullptr;
+ }
+
+ /**
+ * Set the iterator to aOriginalStringOffset in the original string.
+ * This can efficiently move forward or backward from the current position.
+ * aOriginalStringOffset is clamped to [0,originalStringLength].
+ */
+ void SetOriginalOffset(int32_t aOriginalStringOffset);
+
+ /**
+ * Set the iterator to aSkippedStringOffset in the skipped string.
+ * This can efficiently move forward or backward from the current position.
+ * aSkippedStringOffset is clamped to [0,skippedStringLength].
+ */
+ void SetSkippedOffset(uint32_t aSkippedStringOffset);
+
+ uint32_t ConvertOriginalToSkipped(int32_t aOriginalStringOffset)
+ {
+ SetOriginalOffset(aOriginalStringOffset);
+ return GetSkippedOffset();
+ }
+
+ int32_t ConvertSkippedToOriginal(uint32_t aSkippedStringOffset)
+ {
+ SetSkippedOffset(aSkippedStringOffset);
+ return GetOriginalOffset();
+ }
+
+ /**
+ * Test if the character at the current position in the original string
+ * is skipped or not. If aRunLength is non-null, then *aRunLength is set
+ * to a number of characters all of which are either skipped or not, starting
+ * at this character. When the current position is at the end of the original
+ * string, we return true and *aRunLength is set to zero.
+ */
+ bool IsOriginalCharSkipped(int32_t* aRunLength = nullptr) const;
+
+ void AdvanceOriginal(int32_t aDelta)
+ {
+ SetOriginalOffset(GetOriginalOffset() + aDelta);
+ }
+
+ void AdvanceSkipped(int32_t aDelta)
+ {
+ SetSkippedOffset(GetSkippedOffset() + aDelta);
+ }
+
+ /**
+ * @return the offset within the original string
+ */
+ int32_t GetOriginalOffset() const
+ {
+ return mOriginalStringOffset - mOriginalStringToSkipCharsOffset;
+ }
+
+ /**
+ * @return the offset within the skipped string corresponding to the
+ * current position in the original string. If the current position
+ * in the original string is a character that is skipped, then we return
+ * the position corresponding to the first non-skipped character in the
+ * original string after the current position, or the length of the skipped
+ * string if there is no such character.
+ */
+ uint32_t GetSkippedOffset() const
+ {
+ return mSkippedStringOffset;
+ }
+
+ int32_t GetOriginalEnd() const
+ {
+ return mSkipChars->GetOriginalCharCount() -
+ mOriginalStringToSkipCharsOffset;
+ }
+
+private:
+ const gfxSkipChars* mSkipChars;
+
+ // Current position
+ int32_t mOriginalStringOffset;
+ uint32_t mSkippedStringOffset;
+
+ // Index of the last skippedRange that precedes or contains the current
+ // position in the original string.
+ // If index == -1 then we are before the first skipped char.
+ int32_t mCurrentRangeIndex;
+
+ // This offset is added to map from "skipped+unskipped characters in
+ // the original DOM string" character space to "skipped+unskipped
+ // characters in the textrun's gfxSkipChars" character space
+ int32_t mOriginalStringToSkipCharsOffset;
+};
+
+#endif /*GFX_SKIP_CHARS_H*/
diff --git a/system/graphics/thebes/gfxTextRun.cpp b/system/graphics/thebes/gfxTextRun.cpp
new file mode 100644
index 000000000..664514fdd
--- /dev/null
+++ b/system/graphics/thebes/gfxTextRun.cpp
@@ -0,0 +1,3248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxTextRun.h"
+#include "gfxGlyphExtents.h"
+#include "gfxPlatformFontList.h"
+#include "gfxUserFontSet.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Sprintf.h"
+#include "nsGkAtoms.h"
+#include "nsILanguageAtomService.h"
+#include "nsServiceManagerUtils.h"
+
+#include "gfxContext.h"
+#include "gfxFontConstants.h"
+#include "gfxFontMissingGlyphs.h"
+#include "gfxScriptItemizer.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeRange.h"
+#include "nsStyleConsts.h"
+#include "mozilla/Likely.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/Logging.h" // for gfxCriticalError
+#include "mozilla/UniquePtr.h"
+
+#if defined(MOZ_WIDGET_GTK)
+#include "gfxPlatformGtk.h" // xxx - for UseFcFontList
+#endif
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+
+#include "cairo.h"
+
+#include <unicode/unorm2.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+using mozilla::services::GetObserverService;
+
+static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
+static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
+
+#ifdef DEBUG_roc
+#define DEBUG_TEXT_RUN_STORAGE_METRICS
+#endif
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+extern uint32_t gTextRunStorageHighWaterMark;
+extern uint32_t gTextRunStorage;
+extern uint32_t gFontCount;
+extern uint32_t gGlyphExtentsCount;
+extern uint32_t gGlyphExtentsWidthsTotalSize;
+extern uint32_t gGlyphExtentsSetupEagerSimple;
+extern uint32_t gGlyphExtentsSetupEagerTight;
+extern uint32_t gGlyphExtentsSetupLazyTight;
+extern uint32_t gGlyphExtentsSetupFallBackToTight;
+#endif
+
+bool
+gfxTextRun::GlyphRunIterator::NextRun() {
+ if (mNextIndex >= mTextRun->mGlyphRuns.Length())
+ return false;
+ mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
+ if (mGlyphRun->mCharacterOffset >= mEndOffset)
+ return false;
+
+ mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
+ uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
+ ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
+ mStringEnd = std::min(mEndOffset, last);
+
+ ++mNextIndex;
+ return true;
+}
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+static void
+AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
+{
+ // Ignores detailed glyphs... we don't know when those have been constructed
+ // Also ignores gfxSkipChars dynamic storage (which won't be anything
+ // for preformatted text)
+ // Also ignores GlyphRun array, again because it hasn't been constructed
+ // by the time this gets called. If there's only one glyphrun that's stored
+ // directly in the textrun anyway so no additional overhead.
+ uint32_t length = aTextRun->GetLength();
+ int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
+ bytes += sizeof(gfxTextRun);
+ gTextRunStorage += bytes*aSign;
+ gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
+}
+#endif
+
+static bool
+NeedsGlyphExtents(gfxTextRun *aTextRun)
+{
+ if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
+ return true;
+ uint32_t numRuns;
+ const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
+ for (uint32_t i = 0; i < numRuns; ++i) {
+ if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
+ return true;
+ }
+ return false;
+}
+
+// Helper for textRun creation to preallocate storage for glyph records;
+// this function returns a pointer to the newly-allocated glyph storage.
+// Returns nullptr if allocation fails.
+void *
+gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
+{
+ // Allocate the storage we need, returning nullptr on failure rather than
+ // throwing an exception (because web content can create huge runs).
+ void *storage = malloc(aSize + aLength * sizeof(CompressedGlyph));
+ if (!storage) {
+ NS_WARNING("failed to allocate storage for text run!");
+ return nullptr;
+ }
+
+ // Initialize the glyph storage (beyond aSize) to zero
+ memset(reinterpret_cast<char*>(storage) + aSize, 0,
+ aLength * sizeof(CompressedGlyph));
+
+ return storage;
+}
+
+already_AddRefed<gfxTextRun>
+gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
+ uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
+{
+ void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
+ if (!storage) {
+ return nullptr;
+ }
+
+ RefPtr<gfxTextRun> result = new (storage) gfxTextRun(aParams, aLength,
+ aFontGroup, aFlags);
+ return result.forget();
+}
+
+gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
+ uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
+ : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
+ , mUserData(aParams->mUserData)
+ , mFontGroup(aFontGroup)
+ , mReleasedFontGroup(false)
+ , mShapingState(eShapingState_Normal)
+{
+ NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
+ MOZ_COUNT_CTOR(gfxTextRun);
+ NS_ADDREF(mFontGroup);
+
+#ifndef RELEASE_OR_BETA
+ gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
+ if (tp) {
+ tp->current.textrunConst++;
+ }
+#endif
+
+ mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
+
+ if (aParams->mSkipChars) {
+ mSkipChars.TakeFrom(aParams->mSkipChars);
+ }
+
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ AccountStorageForTextRun(this, 1);
+#endif
+
+ mSkipDrawing = mFontGroup->ShouldSkipDrawing();
+}
+
+gfxTextRun::~gfxTextRun()
+{
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ AccountStorageForTextRun(this, -1);
+#endif
+#ifdef DEBUG
+ // Make it easy to detect a dead text run
+ mFlags = 0xFFFFFFFF;
+#endif
+
+ // The cached ellipsis textrun (if any) in a fontgroup will have already
+ // been told to release its reference to the group, so we mustn't do that
+ // again here.
+ if (!mReleasedFontGroup) {
+#ifndef RELEASE_OR_BETA
+ gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
+ if (tp) {
+ tp->current.textrunDestr++;
+ }
+#endif
+ NS_RELEASE(mFontGroup);
+ }
+
+ MOZ_COUNT_DTOR(gfxTextRun);
+}
+
+void
+gfxTextRun::ReleaseFontGroup()
+{
+ NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
+ NS_RELEASE(mFontGroup);
+ mReleasedFontGroup = true;
+}
+
+bool
+gfxTextRun::SetPotentialLineBreaks(Range aRange, const uint8_t* aBreakBefore)
+{
+ NS_ASSERTION(aRange.end <= GetLength(), "Overflow");
+
+ uint32_t changed = 0;
+ CompressedGlyph* cg = mCharacterGlyphs + aRange.start;
+ const CompressedGlyph* const end = cg + aRange.Length();
+ while (cg < end) {
+ uint8_t canBreak = *aBreakBefore++;
+ if (canBreak && !cg->IsClusterStart()) {
+ // XXX If we replace the line-breaker with one based more closely
+ // on UAX#14 (e.g. using ICU), this may not be needed any more.
+ // Avoid possible breaks inside a cluster, EXCEPT when the previous
+ // character was a space (compare UAX#14 rules LB9, LB10).
+ if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) {
+ canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
+ }
+ }
+ changed |= cg->SetCanBreakBefore(canBreak);
+ ++cg;
+ }
+ return changed != 0;
+}
+
+gfxTextRun::LigatureData
+gfxTextRun::ComputeLigatureData(Range aPartRange,
+ PropertyProvider *aProvider) const
+{
+ NS_ASSERTION(aPartRange.start < aPartRange.end,
+ "Computing ligature data for empty range");
+ NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow");
+
+ LigatureData result;
+ const CompressedGlyph *charGlyphs = mCharacterGlyphs;
+
+ uint32_t i;
+ for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
+ NS_ASSERTION(i > 0, "Ligature at the start of the run??");
+ }
+ result.mRange.start = i;
+ for (i = aPartRange.start + 1;
+ i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
+ }
+ result.mRange.end = i;
+
+ int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange);
+ // Count the number of started clusters we have seen
+ uint32_t totalClusterCount = 0;
+ uint32_t partClusterIndex = 0;
+ uint32_t partClusterCount = 0;
+ for (i = result.mRange.start; i < result.mRange.end; ++i) {
+ // Treat the first character of the ligature as the start of a
+ // cluster for our purposes of allocating ligature width to its
+ // characters.
+ if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) {
+ ++totalClusterCount;
+ if (i < aPartRange.start) {
+ ++partClusterIndex;
+ } else if (i < aPartRange.end) {
+ ++partClusterCount;
+ }
+ }
+ }
+ NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
+ result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
+ result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
+
+ // Any rounding errors are apportioned to the final part of the ligature,
+ // so that measuring all parts of a ligature and summing them is equal to
+ // the ligature width.
+ if (aPartRange.end == result.mRange.end) {
+ gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
+ result.mPartWidth += ligatureWidth - allParts;
+ }
+
+ if (partClusterCount == 0) {
+ // nothing to draw
+ result.mClipBeforePart = result.mClipAfterPart = true;
+ } else {
+ // Determine whether we should clip before or after this part when
+ // drawing its slice of the ligature.
+ // We need to clip before the part if any cluster is drawn before
+ // this part.
+ result.mClipBeforePart = partClusterIndex > 0;
+ // We need to clip after the part if any cluster is drawn after
+ // this part.
+ result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
+ }
+
+ if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
+ gfxFont::Spacing spacing;
+ if (aPartRange.start == result.mRange.start) {
+ aProvider->GetSpacing(
+ Range(aPartRange.start, aPartRange.start + 1), &spacing);
+ result.mPartWidth += spacing.mBefore;
+ }
+ if (aPartRange.end == result.mRange.end) {
+ aProvider->GetSpacing(
+ Range(aPartRange.end - 1, aPartRange.end), &spacing);
+ result.mPartWidth += spacing.mAfter;
+ }
+ }
+
+ return result;
+}
+
+gfxFloat
+gfxTextRun::ComputePartialLigatureWidth(Range aPartRange,
+ PropertyProvider *aProvider) const
+{
+ if (aPartRange.start >= aPartRange.end)
+ return 0;
+ LigatureData data = ComputeLigatureData(aPartRange, aProvider);
+ return data.mPartWidth;
+}
+
+int32_t
+gfxTextRun::GetAdvanceForGlyphs(Range aRange) const
+{
+ int32_t advance = 0;
+ for (auto i = aRange.start; i < aRange.end; ++i) {
+ advance += GetAdvanceForGlyph(i);
+ }
+ return advance;
+}
+
+static void
+GetAdjustedSpacing(const gfxTextRun *aTextRun, gfxTextRun::Range aRange,
+ gfxTextRun::PropertyProvider *aProvider,
+ gfxTextRun::PropertyProvider::Spacing *aSpacing)
+{
+ if (aRange.start >= aRange.end)
+ return;
+
+ aProvider->GetSpacing(aRange, aSpacing);
+
+#ifdef DEBUG
+ // Check to see if we have spacing inside ligatures
+
+ const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
+ uint32_t i;
+
+ for (i = aRange.start; i < aRange.end; ++i) {
+ if (!charGlyphs[i].IsLigatureGroupStart()) {
+ NS_ASSERTION(i == aRange.start ||
+ aSpacing[i - aRange.start].mBefore == 0,
+ "Before-spacing inside a ligature!");
+ NS_ASSERTION(i - 1 <= aRange.start ||
+ aSpacing[i - 1 - aRange.start].mAfter == 0,
+ "After-spacing inside a ligature!");
+ }
+ }
+#endif
+}
+
+bool
+gfxTextRun::GetAdjustedSpacingArray(Range aRange, PropertyProvider *aProvider,
+ Range aSpacingRange,
+ nsTArray<PropertyProvider::Spacing>*
+ aSpacing) const
+{
+ if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
+ return false;
+ if (!aSpacing->AppendElements(aRange.Length()))
+ return false;
+ auto spacingOffset = aSpacingRange.start - aRange.start;
+ memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
+ GetAdjustedSpacing(this, aSpacingRange, aProvider,
+ aSpacing->Elements() + spacingOffset);
+ memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
+ sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end));
+ return true;
+}
+
+void
+gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const
+{
+ if (aRange->start >= aRange->end)
+ return;
+
+ const CompressedGlyph *charGlyphs = mCharacterGlyphs;
+
+ while (aRange->start < aRange->end &&
+ !charGlyphs[aRange->start].IsLigatureGroupStart()) {
+ ++aRange->start;
+ }
+ if (aRange->end < GetLength()) {
+ while (aRange->end > aRange->start &&
+ !charGlyphs[aRange->end].IsLigatureGroupStart()) {
+ --aRange->end;
+ }
+ }
+}
+
+void
+gfxTextRun::DrawGlyphs(gfxFont *aFont, Range aRange, gfxPoint *aPt,
+ PropertyProvider *aProvider, Range aSpacingRange,
+ TextRunDrawParams& aParams, uint16_t aOrientation) const
+{
+ AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
+ bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
+ aSpacingRange, &spacingBuffer);
+ aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
+ aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
+}
+
+static void
+ClipPartialLigature(const gfxTextRun* aTextRun,
+ gfxFloat *aStart, gfxFloat *aEnd,
+ gfxFloat aOrigin,
+ gfxTextRun::LigatureData *aLigature)
+{
+ if (aLigature->mClipBeforePart) {
+ if (aTextRun->IsRightToLeft()) {
+ *aEnd = std::min(*aEnd, aOrigin);
+ } else {
+ *aStart = std::max(*aStart, aOrigin);
+ }
+ }
+ if (aLigature->mClipAfterPart) {
+ gfxFloat endEdge =
+ aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
+ if (aTextRun->IsRightToLeft()) {
+ *aStart = std::max(*aStart, endEdge);
+ } else {
+ *aEnd = std::min(*aEnd, endEdge);
+ }
+ }
+}
+
+void
+gfxTextRun::DrawPartialLigature(gfxFont *aFont, Range aRange,
+ gfxPoint *aPt, PropertyProvider *aProvider,
+ TextRunDrawParams& aParams,
+ uint16_t aOrientation) const
+{
+ if (aRange.start >= aRange.end) {
+ return;
+ }
+
+ // Draw partial ligature. We hack this by clipping the ligature.
+ LigatureData data = ComputeLigatureData(aRange, aProvider);
+ gfxRect clipExtents = aParams.context->GetClipExtents();
+ gfxFloat start, end;
+ if (aParams.isVerticalRun) {
+ start = clipExtents.Y() * mAppUnitsPerDevUnit;
+ end = clipExtents.YMost() * mAppUnitsPerDevUnit;
+ ClipPartialLigature(this, &start, &end, aPt->y, &data);
+ } else {
+ start = clipExtents.X() * mAppUnitsPerDevUnit;
+ end = clipExtents.XMost() * mAppUnitsPerDevUnit;
+ ClipPartialLigature(this, &start, &end, aPt->x, &data);
+ }
+
+ {
+ // use division here to ensure that when the rect is aligned on multiples
+ // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
+ // Also, make sure we snap the rectangle to device pixels.
+ Rect clipRect = aParams.isVerticalRun ?
+ Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
+ clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) :
+ Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
+ (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
+ MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
+
+ aParams.context->Save();
+ aParams.context->Clip(clipRect);
+ }
+
+ gfxPoint pt;
+ if (aParams.isVerticalRun) {
+ pt = gfxPoint(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
+ } else {
+ pt = gfxPoint(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
+ }
+
+ DrawGlyphs(aFont, data.mRange, &pt,
+ aProvider, aRange, aParams, aOrientation);
+ aParams.context->Restore();
+
+ if (aParams.isVerticalRun) {
+ aPt->y += aParams.direction * data.mPartWidth;
+ } else {
+ aPt->x += aParams.direction * data.mPartWidth;
+ }
+}
+
+// Returns true if a glyph run is using a font with synthetic bolding enabled,
+// or a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
+// check whether the text run needs to be explicitly composited in order to
+// support opacity.
+static bool
+HasSyntheticBoldOrColor(const gfxTextRun *aRun, gfxTextRun::Range aRange)
+{
+ gfxTextRun::GlyphRunIterator iter(aRun, aRange);
+ while (iter.NextRun()) {
+ gfxFont *font = iter.GetGlyphRun()->mFont;
+ if (font) {
+ if (font->IsSyntheticBold()) {
+ return true;
+ }
+ gfxFontEntry* fe = font->GetFontEntry();
+ if (fe->TryGetSVGData(font) || fe->TryGetColorGlyphs()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Returns true if color is neither opaque nor transparent (i.e. alpha is not 0
+// or 1), and false otherwise. If true, aCurrentColorOut is set on output.
+static bool
+HasNonOpaqueNonTransparentColor(gfxContext *aContext, Color& aCurrentColorOut)
+{
+ if (aContext->GetDeviceColor(aCurrentColorOut)) {
+ if (0.f < aCurrentColorOut.a && aCurrentColorOut.a < 1.f) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// helper class for double-buffering drawing with non-opaque color
+struct BufferAlphaColor {
+ explicit BufferAlphaColor(gfxContext *aContext)
+ : mContext(aContext)
+ {
+
+ }
+
+ ~BufferAlphaColor() {}
+
+ void PushSolidColor(const gfxRect& aBounds, const Color& aAlphaColor, uint32_t appsPerDevUnit)
+ {
+ mContext->Save();
+ mContext->NewPath();
+ mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
+ aBounds.Y() / appsPerDevUnit,
+ aBounds.Width() / appsPerDevUnit,
+ aBounds.Height() / appsPerDevUnit), true);
+ mContext->Clip();
+ mContext->SetColor(Color(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
+ mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
+ }
+
+ void PopAlpha()
+ {
+ // pop the text, using the color alpha as the opacity
+ mContext->PopGroupAndBlend();
+ mContext->Restore();
+ }
+
+ gfxContext *mContext;
+};
+
+void
+gfxTextRun::Draw(Range aRange, gfxPoint aPt, const DrawParams& aParams) const
+{
+ NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
+ NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
+ !(aParams.drawMode & DrawMode::GLYPH_PATH),
+ "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
+ NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
+ "callback must not be specified unless using GLYPH_PATH");
+
+ bool skipDrawing = mSkipDrawing;
+ if (aParams.drawMode & DrawMode::GLYPH_FILL) {
+ Color currentColor;
+ if (aParams.context->GetDeviceColor(currentColor) &&
+ currentColor.a == 0) {
+ skipDrawing = true;
+ }
+ }
+
+ gfxFloat direction = GetDirection();
+
+ if (skipDrawing) {
+ // We don't need to draw anything;
+ // but if the caller wants advance width, we need to compute it here
+ if (aParams.advanceWidth) {
+ gfxTextRun::Metrics metrics = MeasureText(
+ aRange, gfxFont::LOOSE_INK_EXTENTS,
+ aParams.context->GetDrawTarget(), aParams.provider);
+ *aParams.advanceWidth = metrics.mAdvanceWidth * direction;
+ }
+
+ // return without drawing
+ return;
+ }
+
+ // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
+ // correctly unless first drawn without alpha
+ BufferAlphaColor syntheticBoldBuffer(aParams.context);
+ Color currentColor;
+ bool needToRestore = false;
+
+ if (aParams.drawMode & DrawMode::GLYPH_FILL &&
+ HasNonOpaqueNonTransparentColor(aParams.context, currentColor) &&
+ HasSyntheticBoldOrColor(this, aRange)) {
+ needToRestore = true;
+ // measure text, use the bounding box
+ gfxTextRun::Metrics metrics = MeasureText(
+ aRange, gfxFont::LOOSE_INK_EXTENTS,
+ aParams.context->GetDrawTarget(), aParams.provider);
+ metrics.mBoundingBox.MoveBy(aPt);
+ syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
+ GetAppUnitsPerDevUnit());
+ }
+
+ // Set up parameters that will be constant across all glyph runs we need
+ // to draw, regardless of the font used.
+ TextRunDrawParams params;
+ params.context = aParams.context;
+ params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
+ params.isVerticalRun = IsVertical();
+ params.isRTL = IsRightToLeft();
+ params.direction = direction;
+ params.strokeOpts = aParams.strokeOpts;
+ params.textStrokeColor = aParams.textStrokeColor;
+ params.textStrokePattern = aParams.textStrokePattern;
+ params.drawOpts = aParams.drawOpts;
+ params.drawMode = aParams.drawMode;
+ params.callbacks = aParams.callbacks;
+ params.runContextPaint = aParams.contextPaint;
+ params.paintSVGGlyphs = !aParams.callbacks ||
+ aParams.callbacks->mShouldPaintSVGGlyphs;
+ params.dt = aParams.context->GetDrawTarget();
+ params.fontSmoothingBGColor =
+ aParams.context->GetFontSmoothingBackgroundColor();
+
+ GlyphRunIterator iter(this, aRange);
+ gfxFloat advance = 0.0;
+
+ while (iter.NextRun()) {
+ gfxFont *font = iter.GetGlyphRun()->mFont;
+ uint32_t start = iter.GetStringStart();
+ uint32_t end = iter.GetStringEnd();
+ Range ligatureRange(start, end);
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ bool drawPartial = (aParams.drawMode & DrawMode::GLYPH_FILL) ||
+ (aParams.drawMode == DrawMode::GLYPH_PATH &&
+ aParams.callbacks);
+ gfxPoint origPt = aPt;
+
+ if (drawPartial) {
+ DrawPartialLigature(font, Range(start, ligatureRange.start),
+ &aPt, aParams.provider, params,
+ iter.GetGlyphRun()->mOrientation);
+ }
+
+ DrawGlyphs(font, ligatureRange, &aPt,
+ aParams.provider, ligatureRange, params,
+ iter.GetGlyphRun()->mOrientation);
+
+ if (drawPartial) {
+ DrawPartialLigature(font, Range(ligatureRange.end, end),
+ &aPt, aParams.provider, params,
+ iter.GetGlyphRun()->mOrientation);
+ }
+
+ if (params.isVerticalRun) {
+ advance += (aPt.y - origPt.y) * params.direction;
+ } else {
+ advance += (aPt.x - origPt.x) * params.direction;
+ }
+ }
+
+ // composite result when synthetic bolding used
+ if (needToRestore) {
+ syntheticBoldBuffer.PopAlpha();
+ }
+
+ if (aParams.advanceWidth) {
+ *aParams.advanceWidth = advance;
+ }
+}
+
+// This method is mostly parallel to Draw().
+void
+gfxTextRun::DrawEmphasisMarks(gfxContext *aContext, gfxTextRun* aMark,
+ gfxFloat aMarkAdvance, gfxPoint aPt,
+ Range aRange, PropertyProvider* aProvider) const
+{
+ MOZ_ASSERT(aRange.end <= GetLength());
+
+ EmphasisMarkDrawParams params;
+ params.context = aContext;
+ params.mark = aMark;
+ params.advance = aMarkAdvance;
+ params.direction = GetDirection();
+ params.isVertical = IsVertical();
+
+ gfxFloat& inlineCoord = params.isVertical ? aPt.y : aPt.x;
+ gfxFloat direction = params.direction;
+
+ GlyphRunIterator iter(this, aRange);
+ while (iter.NextRun()) {
+ gfxFont* font = iter.GetGlyphRun()->mFont;
+ uint32_t start = iter.GetStringStart();
+ uint32_t end = iter.GetStringEnd();
+ Range ligatureRange(start, end);
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ inlineCoord += direction * ComputePartialLigatureWidth(
+ Range(start, ligatureRange.start), aProvider);
+
+ AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
+ bool haveSpacing = GetAdjustedSpacingArray(
+ ligatureRange, aProvider, ligatureRange, &spacingBuffer);
+ params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
+ font->DrawEmphasisMarks(this, &aPt, ligatureRange.start,
+ ligatureRange.Length(), params);
+
+ inlineCoord += direction * ComputePartialLigatureWidth(
+ Range(ligatureRange.end, end), aProvider);
+ }
+}
+
+void
+gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ PropertyProvider *aProvider,
+ Range aSpacingRange,
+ uint16_t aOrientation,
+ Metrics *aMetrics) const
+{
+ AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
+ bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
+ aSpacingRange, &spacingBuffer);
+ Metrics metrics = aFont->Measure(this, aRange.start, aRange.end,
+ aBoundingBoxType, aRefDrawTarget,
+ haveSpacing ? spacingBuffer.Elements() : nullptr,
+ aOrientation);
+ aMetrics->CombineWith(metrics, IsRightToLeft());
+}
+
+void
+gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
+ PropertyProvider *aProvider, uint16_t aOrientation,
+ Metrics *aMetrics) const
+{
+ if (aRange.start >= aRange.end)
+ return;
+
+ // Measure partial ligature. We hack this by clipping the metrics in the
+ // same way we clip the drawing.
+ LigatureData data = ComputeLigatureData(aRange, aProvider);
+
+ // First measure the complete ligature
+ Metrics metrics;
+ AccumulateMetricsForRun(aFont, data.mRange,
+ aBoundingBoxType, aRefDrawTarget,
+ aProvider, aRange, aOrientation, &metrics);
+
+ // Clip the bounding box to the ligature part
+ gfxFloat bboxLeft = metrics.mBoundingBox.X();
+ gfxFloat bboxRight = metrics.mBoundingBox.XMost();
+ // Where we are going to start "drawing" relative to our left baseline origin
+ gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
+ ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
+ metrics.mBoundingBox.x = bboxLeft;
+ metrics.mBoundingBox.width = bboxRight - bboxLeft;
+
+ // mBoundingBox is now relative to the left baseline origin for the entire
+ // ligature. Shift it left.
+ metrics.mBoundingBox.x -=
+ IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
+ : data.mPartAdvance;
+ metrics.mAdvanceWidth = data.mPartWidth;
+
+ aMetrics->CombineWith(metrics, IsRightToLeft());
+}
+
+gfxTextRun::Metrics
+gfxTextRun::MeasureText(Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ PropertyProvider *aProvider) const
+{
+ NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
+
+ Metrics accumulatedMetrics;
+ GlyphRunIterator iter(this, aRange);
+ while (iter.NextRun()) {
+ gfxFont *font = iter.GetGlyphRun()->mFont;
+ uint32_t start = iter.GetStringStart();
+ uint32_t end = iter.GetStringEnd();
+ Range ligatureRange(start, end);
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ AccumulatePartialLigatureMetrics(
+ font, Range(start, ligatureRange.start),
+ aBoundingBoxType, aRefDrawTarget, aProvider,
+ iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
+
+ // XXX This sucks. We have to get glyph extents just so we can detect
+ // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
+ // even though in almost all cases we could get correct results just
+ // by getting some ascent/descent from the font and using our stored
+ // advance widths.
+ AccumulateMetricsForRun(font,
+ ligatureRange, aBoundingBoxType,
+ aRefDrawTarget, aProvider, ligatureRange,
+ iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
+
+ AccumulatePartialLigatureMetrics(
+ font, Range(ligatureRange.end, end),
+ aBoundingBoxType, aRefDrawTarget, aProvider,
+ iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
+ }
+
+ return accumulatedMetrics;
+}
+
+#define MEASUREMENT_BUFFER_SIZE 100
+
+uint32_t
+gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
+ bool aLineBreakBefore, gfxFloat aWidth,
+ PropertyProvider *aProvider,
+ SuppressBreak aSuppressBreak,
+ gfxFloat *aTrimWhitespace,
+ bool aWhitespaceCanHang,
+ Metrics *aMetrics,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ bool *aUsedHyphenation,
+ uint32_t *aLastBreak,
+ bool aCanWordWrap,
+ gfxBreakPriority *aBreakPriority)
+{
+ aMaxLength = std::min(aMaxLength, GetLength() - aStart);
+
+ NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
+
+ Range bufferRange(aStart, aStart +
+ std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
+ PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
+ bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
+ if (haveSpacing) {
+ GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
+ }
+ bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
+ bool haveHyphenation = aProvider &&
+ (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
+ (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
+ (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
+ if (haveHyphenation) {
+ aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer);
+ }
+
+ gfxFloat width = 0;
+ gfxFloat advance = 0;
+ // The number of space characters that can be trimmed or hang at a soft-wrap
+ uint32_t trimmableChars = 0;
+ // The amount of space removed by ignoring trimmableChars
+ gfxFloat trimmableAdvance = 0;
+ int32_t lastBreak = -1;
+ int32_t lastBreakTrimmableChars = -1;
+ gfxFloat lastBreakTrimmableAdvance = -1;
+ bool aborted = false;
+ uint32_t end = aStart + aMaxLength;
+ bool lastBreakUsedHyphenation = false;
+
+ Range ligatureRange(aStart, end);
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ uint32_t i;
+ for (i = aStart; i < end; ++i) {
+ if (i >= bufferRange.end) {
+ // Fetch more spacing and hyphenation data
+ bufferRange.start = i;
+ bufferRange.end = std::min(aStart + aMaxLength,
+ i + MEASUREMENT_BUFFER_SIZE);
+ if (haveSpacing) {
+ GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
+ }
+ if (haveHyphenation) {
+ aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer);
+ }
+ }
+
+ // There can't be a word-wrap break opportunity at the beginning of the
+ // line: if the width is too small for even one character to fit, it
+ // could be the first and last break opportunity on the line, and that
+ // would trigger an infinite loop.
+ if (aSuppressBreak != eSuppressAllBreaks &&
+ (aSuppressBreak != eSuppressInitialBreak || i > aStart)) {
+ bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
+ bool atHyphenationBreak = !atNaturalBreak &&
+ haveHyphenation && hyphenBuffer[i - bufferRange.start];
+ bool atBreak = atNaturalBreak || atHyphenationBreak;
+ bool wordWrapping =
+ aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
+ *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
+
+ if (atBreak || wordWrapping) {
+ gfxFloat hyphenatedAdvance = advance;
+ if (atHyphenationBreak) {
+ hyphenatedAdvance += aProvider->GetHyphenWidth();
+ }
+
+ if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
+ // We can break here.
+ lastBreak = i;
+ lastBreakTrimmableChars = trimmableChars;
+ lastBreakTrimmableAdvance = trimmableAdvance;
+ lastBreakUsedHyphenation = atHyphenationBreak;
+ *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
+ : gfxBreakPriority::eWordWrapBreak;
+ }
+
+ width += advance;
+ advance = 0;
+ if (width - trimmableAdvance > aWidth) {
+ // No more text fits. Abort
+ aborted = true;
+ break;
+ }
+ }
+ }
+
+ gfxFloat charAdvance;
+ if (i >= ligatureRange.start && i < ligatureRange.end) {
+ charAdvance = GetAdvanceForGlyphs(Range(i, i + 1));
+ if (haveSpacing) {
+ PropertyProvider::Spacing *space =
+ &spacingBuffer[i - bufferRange.start];
+ charAdvance += space->mBefore + space->mAfter;
+ }
+ } else {
+ charAdvance =
+ ComputePartialLigatureWidth(Range(i, i + 1), aProvider);
+ }
+
+ advance += charAdvance;
+ if (aTrimWhitespace || aWhitespaceCanHang) {
+ if (mCharacterGlyphs[i].CharIsSpace()) {
+ ++trimmableChars;
+ trimmableAdvance += charAdvance;
+ } else {
+ trimmableAdvance = 0;
+ trimmableChars = 0;
+ }
+ }
+ }
+
+ if (!aborted) {
+ width += advance;
+ }
+
+ // There are three possibilities:
+ // 1) all the text fit (width <= aWidth)
+ // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
+ // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
+ uint32_t charsFit;
+ bool usedHyphenation = false;
+ if (width - trimmableAdvance <= aWidth) {
+ charsFit = aMaxLength;
+ } else if (lastBreak >= 0) {
+ charsFit = lastBreak - aStart;
+ trimmableChars = lastBreakTrimmableChars;
+ trimmableAdvance = lastBreakTrimmableAdvance;
+ usedHyphenation = lastBreakUsedHyphenation;
+ } else {
+ charsFit = aMaxLength;
+ }
+
+ if (aMetrics) {
+ auto fitEnd = aStart + charsFit;
+ // Initially, measure everything, so that our bounding box includes
+ // any trimmable or hanging whitespace.
+ *aMetrics = MeasureText(Range(aStart, fitEnd),
+ aBoundingBoxType, aRefDrawTarget,
+ aProvider);
+ if (aTrimWhitespace || aWhitespaceCanHang) {
+ // Measure trailing whitespace that is to be trimmed/hung.
+ Metrics trimOrHangMetrics =
+ MeasureText(Range(fitEnd - trimmableChars, fitEnd),
+ aBoundingBoxType, aRefDrawTarget,
+ aProvider);
+ if (aTrimWhitespace) {
+ aMetrics->mAdvanceWidth -= trimOrHangMetrics.mAdvanceWidth;
+ } else if (aMetrics->mAdvanceWidth > aWidth) {
+ // Restrict width of hanging whitespace so it doesn't overflow.
+ aMetrics->mAdvanceWidth =
+ std::max(aWidth, aMetrics->mAdvanceWidth -
+ trimOrHangMetrics.mAdvanceWidth);
+ }
+ }
+ }
+ if (aTrimWhitespace) {
+ *aTrimWhitespace = trimmableAdvance;
+ }
+ if (aUsedHyphenation) {
+ *aUsedHyphenation = usedHyphenation;
+ }
+ if (aLastBreak && charsFit == aMaxLength) {
+ if (lastBreak < 0) {
+ *aLastBreak = UINT32_MAX;
+ } else {
+ *aLastBreak = lastBreak - aStart;
+ }
+ }
+
+ return charsFit;
+}
+
+gfxFloat
+gfxTextRun::GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
+ PropertyProvider::Spacing* aSpacing) const
+{
+ NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
+
+ Range ligatureRange = aRange;
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ gfxFloat result =
+ ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
+ aProvider) +
+ ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
+ aProvider);
+
+ if (aSpacing) {
+ aSpacing->mBefore = aSpacing->mAfter = 0;
+ }
+
+ // Account for all remaining spacing here. This is more efficient than
+ // processing it along with the glyphs.
+ if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
+ uint32_t i;
+ AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
+ if (spacingBuffer.AppendElements(aRange.Length())) {
+ GetAdjustedSpacing(this, ligatureRange, aProvider,
+ spacingBuffer.Elements());
+ for (i = 0; i < ligatureRange.Length(); ++i) {
+ PropertyProvider::Spacing *space = &spacingBuffer[i];
+ result += space->mBefore + space->mAfter;
+ }
+ if (aSpacing) {
+ aSpacing->mBefore = spacingBuffer[0].mBefore;
+ aSpacing->mAfter = spacingBuffer.LastElement().mAfter;
+ }
+ }
+ }
+
+ return result + GetAdvanceForGlyphs(ligatureRange);
+}
+
+gfxFloat
+gfxTextRun::GetMinAdvanceWidth(Range aRange)
+{
+ MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range");
+
+ Range ligatureRange = aRange;
+ ShrinkToLigatureBoundaries(&ligatureRange);
+
+ gfxFloat result = std::max(
+ ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
+ nullptr),
+ ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
+ nullptr));
+
+ // XXX Do we need to take spacing into account? When each grapheme cluster
+ // takes its own line, we shouldn't be adding spacings around them.
+ gfxFloat clusterAdvance = 0;
+ for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) {
+ clusterAdvance += GetAdvanceForGlyph(i);
+ if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) {
+ result = std::max(result, clusterAdvance);
+ clusterAdvance = 0;
+ }
+ }
+
+ return result;
+}
+
+bool
+gfxTextRun::SetLineBreaks(Range aRange,
+ bool aLineBreakBefore, bool aLineBreakAfter,
+ gfxFloat *aAdvanceWidthDelta)
+{
+ // Do nothing because our shaping does not currently take linebreaks into
+ // account. There is no change in advance width.
+ if (aAdvanceWidthDelta) {
+ *aAdvanceWidthDelta = 0;
+ }
+ return false;
+}
+
+uint32_t
+gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const
+{
+ NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
+ NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
+ "non-empty text but no glyph runs present!");
+ if (aOffset == GetLength())
+ return mGlyphRuns.Length();
+ uint32_t start = 0;
+ uint32_t end = mGlyphRuns.Length();
+ while (end - start > 1) {
+ uint32_t mid = (start + end)/2;
+ if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
+ start = mid;
+ } else {
+ end = mid;
+ }
+ }
+ NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
+ "Hmm, something went wrong, aOffset should have been found");
+ return start;
+}
+
+nsresult
+gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
+ uint32_t aUTF16Offset, bool aForceNewRun,
+ uint16_t aOrientation)
+{
+ NS_ASSERTION(aFont, "adding glyph run for null font!");
+ NS_ASSERTION(aOrientation != gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED,
+ "mixed orientation should have been resolved");
+ if (!aFont) {
+ return NS_OK;
+ }
+ uint32_t numGlyphRuns = mGlyphRuns.Length();
+ if (!aForceNewRun && numGlyphRuns > 0) {
+ GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
+
+ NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
+ "Glyph runs out of order (and run not forced)");
+
+ // Don't append a run if the font is already the one we want
+ if (lastGlyphRun->mFont == aFont &&
+ lastGlyphRun->mMatchType == aMatchType &&
+ lastGlyphRun->mOrientation == aOrientation)
+ {
+ return NS_OK;
+ }
+
+ // If the offset has not changed, avoid leaving a zero-length run
+ // by overwriting the last entry instead of appending...
+ if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
+
+ // ...except that if the run before the last entry had the same
+ // font as the new one wants, merge with it instead of creating
+ // adjacent runs with the same font
+ if (numGlyphRuns > 1 &&
+ mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
+ mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType &&
+ mGlyphRuns[numGlyphRuns - 2].mOrientation == aOrientation)
+ {
+ mGlyphRuns.TruncateLength(numGlyphRuns - 1);
+ return NS_OK;
+ }
+
+ lastGlyphRun->mFont = aFont;
+ lastGlyphRun->mMatchType = aMatchType;
+ lastGlyphRun->mOrientation = aOrientation;
+ return NS_OK;
+ }
+ }
+
+ NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
+ "First run doesn't cover the first character (and run not forced)?");
+
+ GlyphRun *glyphRun = mGlyphRuns.AppendElement();
+ if (!glyphRun)
+ return NS_ERROR_OUT_OF_MEMORY;
+ glyphRun->mFont = aFont;
+ glyphRun->mCharacterOffset = aUTF16Offset;
+ glyphRun->mMatchType = aMatchType;
+ glyphRun->mOrientation = aOrientation;
+ return NS_OK;
+}
+
+void
+gfxTextRun::SortGlyphRuns()
+{
+ if (mGlyphRuns.Length() <= 1)
+ return;
+
+ nsTArray<GlyphRun> runs(mGlyphRuns);
+ GlyphRunOffsetComparator comp;
+ runs.Sort(comp);
+
+ // Now copy back, coalescing adjacent glyph runs that have the same font
+ mGlyphRuns.Clear();
+ uint32_t i, count = runs.Length();
+ for (i = 0; i < count; ++i) {
+ // a GlyphRun with the same font and orientation as the previous can
+ // just be skipped; the last GlyphRun will cover its character range.
+ if (i == 0 || runs[i].mFont != runs[i - 1].mFont ||
+ runs[i].mOrientation != runs[i - 1].mOrientation) {
+ mGlyphRuns.AppendElement(runs[i]);
+ // If two fonts have the same character offset, Sort() will have
+ // randomized the order.
+ NS_ASSERTION(i == 0 ||
+ runs[i].mCharacterOffset !=
+ runs[i - 1].mCharacterOffset,
+ "Two fonts for the same run, glyph indices may not match the font");
+ }
+ }
+}
+
+// Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
+// therefore we only call it once, at the end of textrun construction,
+// NOT incrementally as each glyph run is added (bug 680402).
+void
+gfxTextRun::SanitizeGlyphRuns()
+{
+ if (mGlyphRuns.Length() <= 1)
+ return;
+
+ // If any glyph run starts with ligature-continuation characters, we need to advance it
+ // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
+ // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
+ // it appear as if a ligature has been formed)
+ int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
+ const CompressedGlyph *charGlyphs = mCharacterGlyphs;
+ for (i = lastRunIndex; i >= 0; --i) {
+ GlyphRun& run = mGlyphRuns[i];
+ while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
+ run.mCharacterOffset < GetLength()) {
+ run.mCharacterOffset++;
+ }
+ // if the run has become empty, eliminate it
+ if ((i < lastRunIndex &&
+ run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
+ (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
+ mGlyphRuns.RemoveElementAt(i);
+ --lastRunIndex;
+ }
+ }
+}
+
+uint32_t
+gfxTextRun::CountMissingGlyphs() const
+{
+ uint32_t i;
+ uint32_t count = 0;
+ for (i = 0; i < GetLength(); ++i) {
+ if (mCharacterGlyphs[i].IsMissing()) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+void
+gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
+{
+ uint32_t wordLen = aShapedWord->GetLength();
+ NS_ASSERTION(aOffset + wordLen <= GetLength(),
+ "word overruns end of textrun!");
+
+ CompressedGlyph *charGlyphs = GetCharacterGlyphs();
+ const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
+ if (aShapedWord->HasDetailedGlyphs()) {
+ for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
+ const CompressedGlyph& g = wordGlyphs[i];
+ if (g.IsSimpleGlyph()) {
+ charGlyphs[aOffset] = g;
+ } else {
+ const DetailedGlyph *details =
+ g.GetGlyphCount() > 0 ?
+ aShapedWord->GetDetailedGlyphs(i) : nullptr;
+ SetGlyphs(aOffset, g, details);
+ }
+ }
+ } else {
+ memcpy(charGlyphs + aOffset, wordGlyphs,
+ wordLen * sizeof(CompressedGlyph));
+ }
+}
+
+void
+gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, Range aRange, uint32_t aDest)
+{
+ NS_ASSERTION(aRange.end <= aSource->GetLength(),
+ "Source substring out of range");
+ NS_ASSERTION(aDest + aRange.Length() <= GetLength(),
+ "Destination substring out of range");
+
+ if (aSource->mSkipDrawing) {
+ mSkipDrawing = true;
+ }
+
+ // Copy base glyph data, and DetailedGlyph data where present
+ const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aRange.start;
+ CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
+ for (uint32_t i = 0; i < aRange.Length(); ++i) {
+ CompressedGlyph g = srcGlyphs[i];
+ g.SetCanBreakBefore(!g.IsClusterStart() ?
+ CompressedGlyph::FLAG_BREAK_TYPE_NONE :
+ dstGlyphs[i].CanBreakBefore());
+ if (!g.IsSimpleGlyph()) {
+ uint32_t count = g.GetGlyphCount();
+ if (count > 0) {
+ DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
+ if (dst) {
+ DetailedGlyph *src =
+ aSource->GetDetailedGlyphs(i + aRange.start);
+ if (src) {
+ ::memcpy(dst, src, count * sizeof(DetailedGlyph));
+ } else {
+ g.SetMissing(0);
+ }
+ } else {
+ g.SetMissing(0);
+ }
+ }
+ }
+ dstGlyphs[i] = g;
+ }
+
+ // Copy glyph runs
+ GlyphRunIterator iter(aSource, aRange);
+#ifdef DEBUG
+ const GlyphRun *prevRun = nullptr;
+#endif
+ while (iter.NextRun()) {
+ gfxFont *font = iter.GetGlyphRun()->mFont;
+ NS_ASSERTION(!prevRun || prevRun->mFont != iter.GetGlyphRun()->mFont ||
+ prevRun->mMatchType != iter.GetGlyphRun()->mMatchType ||
+ prevRun->mOrientation != iter.GetGlyphRun()->mOrientation,
+ "Glyphruns not coalesced?");
+#ifdef DEBUG
+ prevRun = iter.GetGlyphRun();
+ uint32_t end = iter.GetStringEnd();
+#endif
+ uint32_t start = iter.GetStringStart();
+
+ // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
+ // Although it's unusual (and not desirable), it's possible for us to assign
+ // different fonts to a base character and a following diacritic.
+ // Example on OSX 10.5/10.6 with default fonts installed:
+ // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
+ // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
+ // This means the rendering of the cluster will probably not be very good,
+ // but it's the best we can do for now if the specified font only covered the
+ // initial base character and not its applied marks.
+ NS_WARNING_ASSERTION(
+ aSource->IsClusterStart(start),
+ "Started font run in the middle of a cluster");
+ NS_WARNING_ASSERTION(
+ end == aSource->GetLength() || aSource->IsClusterStart(end),
+ "Ended font run in the middle of a cluster");
+
+ nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
+ start - aRange.start + aDest, false,
+ iter.GetGlyphRun()->mOrientation);
+ if (NS_FAILED(rv))
+ return;
+ }
+}
+
+void
+gfxTextRun::ClearGlyphsAndCharacters()
+{
+ ResetGlyphRuns();
+ memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
+ mLength * sizeof(CompressedGlyph));
+ mDetailedGlyphs = nullptr;
+}
+
+void
+gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
+ uint32_t aCharIndex, uint16_t aOrientation)
+{
+ if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
+ return;
+ }
+
+ aFont->InitWordCache();
+ static const uint8_t space = ' ';
+ uint32_t flags = gfxTextRunFactory::TEXT_IS_8BIT |
+ gfxTextRunFactory::TEXT_IS_ASCII |
+ gfxTextRunFactory::TEXT_IS_PERSISTENT |
+ aOrientation;
+ bool vertical =
+ (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0;
+ gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget,
+ &space, 1,
+ gfxShapedWord::HashMix(0, ' '),
+ Script::LATIN,
+ vertical,
+ mAppUnitsPerDevUnit,
+ flags,
+ nullptr);
+ if (sw) {
+ AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
+ aOrientation);
+ CopyGlyphDataFrom(sw, aCharIndex);
+ }
+}
+
+bool
+gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
+ char16_t aSpaceChar, uint16_t aOrientation)
+{
+ uint32_t spaceGlyph = aFont->GetSpaceGlyph();
+ if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
+ return false;
+ }
+
+ gfxFont::Orientation fontOrientation =
+ (aOrientation & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) ?
+ gfxFont::eVertical : gfxFont::eHorizontal;
+ uint32_t spaceWidthAppUnits =
+ NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth *
+ mAppUnitsPerDevUnit);
+ if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
+ return false;
+ }
+
+ AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
+ aOrientation);
+ CompressedGlyph g;
+ g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
+ if (aSpaceChar == ' ') {
+ g.SetIsSpace();
+ }
+ GetCharacterGlyphs()[aCharIndex] = g;
+ return true;
+}
+
+void
+gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
+{
+ bool needsGlyphExtents = NeedsGlyphExtents(this);
+ if (!needsGlyphExtents && !mDetailedGlyphs)
+ return;
+
+ uint32_t i, runCount = mGlyphRuns.Length();
+ CompressedGlyph *charGlyphs = mCharacterGlyphs;
+ for (i = 0; i < runCount; ++i) {
+ const GlyphRun& run = mGlyphRuns[i];
+ gfxFont *font = run.mFont;
+ if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
+ MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
+ continue;
+ }
+
+ uint32_t start = run.mCharacterOffset;
+ uint32_t end = i + 1 < runCount ?
+ mGlyphRuns[i + 1].mCharacterOffset : GetLength();
+ bool fontIsSetup = false;
+ uint32_t j;
+ gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
+
+ for (j = start; j < end; ++j) {
+ const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
+ if (glyphData->IsSimpleGlyph()) {
+ // If we're in speed mode, don't set up glyph extents here; we'll
+ // just return "optimistic" glyph bounds later
+ if (needsGlyphExtents) {
+ uint32_t glyphIndex = glyphData->GetSimpleGlyph();
+ if (!extents->IsGlyphKnown(glyphIndex)) {
+ if (!fontIsSetup) {
+ if (!font->SetupCairoFont(aRefDrawTarget)) {
+ NS_WARNING("failed to set up font for glyph extents");
+ break;
+ }
+ fontIsSetup = true;
+ }
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ ++gGlyphExtentsSetupEagerSimple;
+#endif
+ font->SetupGlyphExtents(aRefDrawTarget,
+ glyphIndex, false, extents);
+ }
+ }
+ } else if (!glyphData->IsMissing()) {
+ uint32_t glyphCount = glyphData->GetGlyphCount();
+ if (glyphCount == 0) {
+ continue;
+ }
+ const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
+ if (!details) {
+ continue;
+ }
+ for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
+ uint32_t glyphIndex = details->mGlyphID;
+ if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
+ if (!fontIsSetup) {
+ if (!font->SetupCairoFont(aRefDrawTarget)) {
+ NS_WARNING("failed to set up font for glyph extents");
+ break;
+ }
+ fontIsSetup = true;
+ }
+#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
+ ++gGlyphExtentsSetupEagerTight;
+#endif
+ font->SetupGlyphExtents(aRefDrawTarget,
+ glyphIndex, true, extents);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+size_t
+gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
+{
+ // The second arg is how much gfxTextRun::AllocateStorage would have
+ // allocated.
+ size_t total = mGlyphRuns.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ if (mDetailedGlyphs) {
+ total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ return total;
+}
+
+size_t
+gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+
+#ifdef DEBUG
+void
+gfxTextRun::Dump(FILE* aOutput) {
+ if (!aOutput) {
+ aOutput = stdout;
+ }
+
+ uint32_t i;
+ fputc('[', aOutput);
+ for (i = 0; i < mGlyphRuns.Length(); ++i) {
+ if (i > 0) {
+ fputc(',', aOutput);
+ }
+ gfxFont* font = mGlyphRuns[i].mFont;
+ const gfxFontStyle* style = font->GetStyle();
+ NS_ConvertUTF16toUTF8 fontName(font->GetName());
+ nsAutoCString lang;
+ style->language->ToUTF8String(lang);
+ fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
+ fontName.get(), style->size,
+ style->weight, style->style, lang.get());
+ }
+ fputc(']', aOutput);
+}
+#endif
+
+gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize)
+ : mFamilyList(aFontFamilyList)
+ , mStyle(*aStyle)
+ , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
+ , mHyphenWidth(-1)
+ , mDevToCssSize(aDevToCssSize)
+ , mUserFontSet(aUserFontSet)
+ , mTextPerf(aTextPerf)
+ , mLastPrefLang(eFontPrefLang_Western)
+ , mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language))
+ , mLastPrefFirstFont(false)
+ , mSkipDrawing(false)
+ , mSkipUpdateUserFonts(false)
+{
+ // We don't use SetUserFontSet() here, as we want to unconditionally call
+ // BuildFontList() rather than only do UpdateUserFonts() if it changed.
+ mCurrGeneration = GetGeneration();
+ BuildFontList();
+}
+
+gfxFontGroup::~gfxFontGroup()
+{
+}
+
+void
+gfxFontGroup::BuildFontList()
+{
+ bool enumerateFonts = true;
+
+#if defined(MOZ_WIDGET_GTK)
+ // xxx - eliminate this once gfxPangoFontGroup is no longer needed
+ enumerateFonts = gfxPlatformGtk::UseFcFontList();
+#endif
+ if (!enumerateFonts) {
+ return;
+ }
+
+ // initialize fonts in the font family list
+ AutoTArray<gfxFontFamily*,10> fonts;
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+
+ // lookup fonts in the fontlist
+ for (const FontFamilyName& name : mFamilyList.GetFontlist()) {
+ if (name.IsNamed()) {
+ AddPlatformFont(name.mName, fonts);
+ } else {
+ pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
+ if (mTextPerf) {
+ mTextPerf->current.genericLookups++;
+ }
+ }
+ }
+
+ // if necessary, append default generic onto the end
+ if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+ !mFamilyList.HasDefaultGeneric()) {
+ pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(),
+ mStyle.language, fonts);
+ if (mTextPerf) {
+ mTextPerf->current.genericLookups++;
+ }
+ }
+
+ // build the fontlist from the specified families
+ for (gfxFontFamily* fontFamily : fonts) {
+ AddFamilyToFontList(fontFamily);
+ }
+}
+
+void
+gfxFontGroup::AddPlatformFont(const nsAString& aName,
+ nsTArray<gfxFontFamily*>& aFamilyList)
+{
+ // First, look up in the user font set...
+ // If the fontSet matches the family, we must not look for a platform
+ // font of the same name, even if we fail to actually get a fontEntry
+ // here; we'll fall back to the next name in the CSS font-family list.
+ if (mUserFontSet) {
+ // Add userfonts to the fontlist whether already loaded
+ // or not. Loading is initiated during font matching.
+ gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
+ if (family) {
+ aFamilyList.AppendElement(family);
+ return;
+ }
+ }
+
+ // Not known in the user font set ==> check system fonts
+ gfxPlatformFontList::PlatformFontList()
+ ->FindAndAddFamilies(aName, &aFamilyList, &mStyle, mDevToCssSize);
+}
+
+void
+gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
+{
+ NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
+ AutoTArray<gfxFontEntry*,4> fontEntryList;
+ bool needsBold;
+ aFamily->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
+ // add these to the fontlist
+ for (gfxFontEntry* fe : fontEntryList) {
+ if (!HasFont(fe)) {
+ FamilyFace ff(aFamily, fe, needsBold);
+ if (fe->mIsUserFontContainer) {
+ ff.CheckState(mSkipDrawing);
+ }
+ mFonts.AppendElement(ff);
+ }
+ }
+ // for a family marked as "check fallback faces", only mark the last
+ // entry so that fallbacks for a family are only checked once
+ if (aFamily->CheckForFallbackFaces() &&
+ !fontEntryList.IsEmpty() && !mFonts.IsEmpty()) {
+ mFonts.LastElement().SetCheckForFallbackFaces();
+ }
+}
+
+bool
+gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
+{
+ uint32_t count = mFonts.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ if (mFonts[i].FontEntry() == aFontEntry) {
+ return true;
+ }
+ }
+ return false;
+}
+
+gfxFont*
+gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh)
+{
+ if (uint32_t(i) >= mFonts.Length()) {
+ return nullptr;
+ }
+
+ FamilyFace& ff = mFonts[i];
+ if (ff.IsInvalid() || ff.IsLoading()) {
+ return nullptr;
+ }
+
+ RefPtr<gfxFont> font = ff.Font();
+ if (!font) {
+ gfxFontEntry* fe = mFonts[i].FontEntry();
+ gfxCharacterMap* unicodeRangeMap = nullptr;
+ if (fe->mIsUserFontContainer) {
+ gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
+ if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
+ ufe->CharacterInUnicodeRange(aCh) &&
+ !FontLoadingForFamily(ff.Family(), aCh)) {
+ ufe->Load();
+ ff.CheckState(mSkipDrawing);
+ }
+ fe = ufe->GetPlatformFontEntry();
+ if (!fe) {
+ return nullptr;
+ }
+ unicodeRangeMap = ufe->GetUnicodeRangeMap();
+ }
+ font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold(),
+ unicodeRangeMap);
+ if (!font || !font->Valid()) {
+ ff.SetInvalid();
+ return nullptr;
+ }
+ mFonts[i].SetFont(font);
+ }
+ return font.get();
+}
+
+void
+gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing)
+{
+ gfxFontEntry* fe = FontEntry();
+ if (fe->mIsUserFontContainer) {
+ gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
+ gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
+ switch (state) {
+ case gfxUserFontEntry::STATUS_LOADING:
+ SetLoading(true);
+ break;
+ case gfxUserFontEntry::STATUS_FAILED:
+ SetInvalid();
+ // fall-thru to the default case
+ MOZ_FALLTHROUGH;
+ default:
+ SetLoading(false);
+ }
+ if (ufe->WaitForUserFont()) {
+ aSkipDrawing = true;
+ }
+ }
+}
+
+bool
+gfxFontGroup::FamilyFace::EqualsUserFont(const gfxUserFontEntry* aUserFont) const
+{
+ gfxFontEntry* fe = FontEntry();
+ // if there's a font, the entry is the underlying platform font
+ if (mFontCreated) {
+ gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry();
+ if (pfe == fe) {
+ return true;
+ }
+ } else if (fe == aUserFont) {
+ return true;
+ }
+ return false;
+}
+
+bool
+gfxFontGroup::FontLoadingForFamily(gfxFontFamily* aFamily, uint32_t aCh) const
+{
+ uint32_t count = mFonts.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ const FamilyFace& ff = mFonts[i];
+ if (ff.IsLoading() && ff.Family() == aFamily) {
+ const gfxUserFontEntry* ufe =
+ static_cast<gfxUserFontEntry*>(ff.FontEntry());
+ if (ufe->CharacterInUnicodeRange(aCh)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+gfxFont*
+gfxFontGroup::GetDefaultFont()
+{
+ if (mDefaultFont) {
+ return mDefaultFont.get();
+ }
+
+ bool needsBold;
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
+ NS_ASSERTION(defaultFamily,
+ "invalid default font returned by GetDefaultFont");
+
+ if (defaultFamily) {
+ gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
+ needsBold);
+ if (fe) {
+ mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
+ }
+ }
+
+ uint32_t numInits, loaderState;
+ pfl->GetFontlistInitInfo(numInits, loaderState);
+ NS_ASSERTION(numInits != 0,
+ "must initialize system fontlist before getting default font!");
+
+ uint32_t numFonts = 0;
+ if (!mDefaultFont) {
+ // Try for a "font of last resort...."
+ // Because an empty font list would be Really Bad for later code
+ // that assumes it will be able to get valid metrics for layout,
+ // just look for the first usable font and put in the list.
+ // (see bug 554544)
+ AutoTArray<RefPtr<gfxFontFamily>,200> familyList;
+ pfl->GetFontFamilyList(familyList);
+ numFonts = familyList.Length();
+ for (uint32_t i = 0; i < numFonts; ++i) {
+ gfxFontEntry *fe = familyList[i]->FindFontForStyle(mStyle,
+ needsBold);
+ if (fe) {
+ mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
+ if (mDefaultFont) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!mDefaultFont) {
+ // an empty font list at this point is fatal; we're not going to
+ // be able to do even the most basic layout operations
+
+ // annotate crash report with fontlist info
+ nsAutoCString fontInitInfo;
+ fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
+ numInits, numFonts, loaderState);
+#ifdef XP_WIN
+ bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
+ double upTime = (double) GetTickCount();
+ fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
+ dwriteEnabled ? "directwrite" : "gdi", upTime/1000);
+#endif
+ gfxCriticalError() << fontInitInfo.get();
+
+ char msg[256]; // CHECK buffer length if revising message below
+ nsAutoString familiesString;
+ mFamilyList.ToString(familiesString);
+ SprintfLiteral(msg, "unable to find a usable font (%.220s)",
+ NS_ConvertUTF16toUTF8(familiesString).get());
+ NS_RUNTIMEABORT(msg);
+ }
+
+ return mDefaultFont.get();
+}
+
+gfxFont*
+gfxFontGroup::GetFirstValidFont(uint32_t aCh)
+{
+ uint32_t count = mFonts.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ FamilyFace& ff = mFonts[i];
+ if (ff.IsInvalid()) {
+ continue;
+ }
+
+ // already have a font?
+ gfxFont* font = ff.Font();
+ if (font) {
+ return font;
+ }
+
+ // Need to build a font, loading userfont if not loaded. In
+ // cases where unicode range might apply, use the character
+ // provided.
+ if (ff.IsUserFontContainer()) {
+ gfxUserFontEntry* ufe =
+ static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
+ bool inRange = ufe->CharacterInUnicodeRange(aCh);
+ if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
+ inRange && !FontLoadingForFamily(ff.Family(), aCh)) {
+ ufe->Load();
+ ff.CheckState(mSkipDrawing);
+ }
+ if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED ||
+ !inRange) {
+ continue;
+ }
+ }
+
+ font = GetFontAt(i, aCh);
+ if (font) {
+ return font;
+ }
+ }
+ return GetDefaultFont();
+}
+
+gfxFont *
+gfxFontGroup::GetFirstMathFont()
+{
+ uint32_t count = mFonts.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ gfxFont* font = GetFontAt(i);
+ if (font && font->TryGetMathTable()) {
+ return font;
+ }
+ }
+ return nullptr;
+}
+
+gfxFontGroup *
+gfxFontGroup::Copy(const gfxFontStyle *aStyle)
+{
+ gfxFontGroup *fg =
+ new gfxFontGroup(mFamilyList, aStyle, mTextPerf,
+ mUserFontSet, mDevToCssSize);
+ return fg;
+}
+
+bool
+gfxFontGroup::IsInvalidChar(uint8_t ch)
+{
+ return ((ch & 0x7f) < 0x20 || ch == 0x7f);
+}
+
+bool
+gfxFontGroup::IsInvalidChar(char16_t ch)
+{
+ // All printable 7-bit ASCII values are OK
+ if (ch >= ' ' && ch < 0x7f) {
+ return false;
+ }
+ // No point in sending non-printing control chars through font shaping
+ if (ch <= 0x9f) {
+ return true;
+ }
+ return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
+ (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
+ IsBidiControl(ch));
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
+{
+ aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
+ return gfxTextRun::Create(aParams, 0, this, aFlags);
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
+{
+ aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
+
+ RefPtr<gfxTextRun> textRun =
+ gfxTextRun::Create(aParams, 1, this, aFlags);
+ if (!textRun) {
+ return nullptr;
+ }
+
+ uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
+ if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
+ orientation = TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+ }
+
+ gfxFont *font = GetFirstValidFont();
+ if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
+ MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
+ // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
+ // them, and always create at least size 1 fonts, i.e. they still
+ // render something for size 0 fonts.
+ textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false,
+ orientation);
+ }
+ else {
+ if (font->GetSpaceGlyph()) {
+ // Normally, the font has a cached space glyph, so we can avoid
+ // the cost of calling FindFontForChar.
+ textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
+ } else {
+ // In case the primary font doesn't have <space> (bug 970891),
+ // find one that does.
+ uint8_t matchType;
+ RefPtr<gfxFont> spaceFont =
+ FindFontForChar(' ', 0, 0, Script::LATIN, nullptr,
+ &matchType);
+ if (spaceFont) {
+ textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0,
+ orientation);
+ }
+ }
+ }
+
+ // Note that the gfxGlyphExtents glyph bounds storage for the font will
+ // always contain an entry for the font's space glyph, so we don't have
+ // to call FetchGlyphExtents here.
+ return textRun.forget();
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
+ const Parameters *aParams, uint32_t aFlags)
+{
+ RefPtr<gfxTextRun> textRun =
+ gfxTextRun::Create(aParams, aLength, this, aFlags);
+ if (!textRun) {
+ return nullptr;
+ }
+
+ uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
+ if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
+ orientation = TEXT_ORIENT_VERTICAL_UPRIGHT;
+ }
+ textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
+ orientation);
+ return textRun.forget();
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
+ uint32_t aAppUnitsPerDevUnit)
+{
+ // only use U+2010 if it is supported by the first font in the group;
+ // it's better to use ASCII '-' from the primary font than to fall back to
+ // U+2010 from some other, possibly poorly-matching face
+ static const char16_t hyphen = 0x2010;
+ gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
+ if (font->HasCharacter(hyphen)) {
+ return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
+ gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
+ }
+
+ static const uint8_t dash = '-';
+ return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
+ gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
+}
+
+gfxFloat
+gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
+{
+ if (mHyphenWidth < 0) {
+ RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
+ if (dt) {
+ RefPtr<gfxTextRun>
+ hyphRun(MakeHyphenTextRun(dt,
+ aProvider->GetAppUnitsPerDevUnit()));
+ mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
+ }
+ }
+ return mHyphenWidth;
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
+ const Parameters *aParams, uint32_t aFlags,
+ gfxMissingFontRecorder *aMFR)
+{
+ if (aLength == 0) {
+ return MakeEmptyTextRun(aParams, aFlags);
+ }
+ if (aLength == 1 && aString[0] == ' ') {
+ return MakeSpaceTextRun(aParams, aFlags);
+ }
+
+ aFlags |= TEXT_IS_8BIT;
+
+ if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
+ MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
+ // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
+ // them, and always create at least size 1 fonts, i.e. they still
+ // render something for size 0 fonts.
+ return MakeBlankTextRun(aLength, aParams, aFlags);
+ }
+
+ RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
+ aFlags);
+ if (!textRun) {
+ return nullptr;
+ }
+
+ InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
+
+ textRun->FetchGlyphExtents(aParams->mDrawTarget);
+
+ return textRun.forget();
+}
+
+already_AddRefed<gfxTextRun>
+gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
+ const Parameters *aParams, uint32_t aFlags,
+ gfxMissingFontRecorder *aMFR)
+{
+ if (aLength == 0) {
+ return MakeEmptyTextRun(aParams, aFlags);
+ }
+ if (aLength == 1 && aString[0] == ' ') {
+ return MakeSpaceTextRun(aParams, aFlags);
+ }
+ if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
+ MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
+ return MakeBlankTextRun(aLength, aParams, aFlags);
+ }
+
+ RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
+ aFlags);
+ if (!textRun) {
+ return nullptr;
+ }
+
+ InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
+
+ textRun->FetchGlyphExtents(aParams->mDrawTarget);
+
+ return textRun.forget();
+}
+
+template<typename T>
+void
+gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString,
+ uint32_t aLength,
+ gfxMissingFontRecorder *aMFR)
+{
+ NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
+
+ // we need to do numeral processing even on 8-bit text,
+ // in case we're converting Western to Hindi/Arabic digits
+ int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
+ UniquePtr<char16_t[]> transformedString;
+ if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
+ // scan the string for numerals that may need to be transformed;
+ // if we find any, we'll make a local copy here and use that for
+ // font matching and glyph generation/shaping
+ bool prevIsArabic =
+ (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
+ for (uint32_t i = 0; i < aLength; ++i) {
+ char16_t origCh = aString[i];
+ char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
+ if (newCh != origCh) {
+ if (!transformedString) {
+ transformedString = MakeUnique<char16_t[]>(aLength);
+ if (sizeof(T) == sizeof(char16_t)) {
+ memcpy(transformedString.get(), aString, i * sizeof(char16_t));
+ } else {
+ for (uint32_t j = 0; j < i; ++j) {
+ transformedString[j] = aString[j];
+ }
+ }
+ }
+ }
+ if (transformedString) {
+ transformedString[i] = newCh;
+ }
+ prevIsArabic = IS_ARABIC_CHAR(newCh);
+ }
+ }
+
+ LogModule* log = mStyle.systemFont
+ ? gfxPlatform::GetLog(eGfxLog_textrunui)
+ : gfxPlatform::GetLog(eGfxLog_textrun);
+
+ // variant fallback handling may end up passing through this twice
+ bool redo;
+ do {
+ redo = false;
+
+ if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
+ nsAutoCString lang;
+ mStyle.language->ToUTF8String(lang);
+ nsAutoString families;
+ mFamilyList.ToString(families);
+ nsAutoCString str((const char*)aString, aLength);
+ MOZ_LOG(log, LogLevel::Warning,\
+ ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
+ "len %d weight: %d width: %d style: %s size: %6.2f %d-byte "
+ "TEXTRUN [%s] ENDTEXTRUN\n",
+ (mStyle.systemFont ? "textrunui" : "textrun"),
+ NS_ConvertUTF16toUTF8(families).get(),
+ (mFamilyList.GetDefaultFontType() == eFamily_serif ?
+ "serif" :
+ (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
+ "sans-serif" : "none")),
+ lang.get(), Script::LATIN, aLength,
+ uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
+ (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
+ (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
+ "normal")),
+ mStyle.size,
+ sizeof(T),
+ str.get()));
+ }
+
+ // the text is still purely 8-bit; bypass the script-run itemizer
+ // and treat it as a single Latin run
+ InitScriptRun(aDrawTarget, aTextRun, aString,
+ 0, aLength, Script::LATIN, aMFR);
+ } else {
+ const char16_t *textPtr;
+ if (transformedString) {
+ textPtr = transformedString.get();
+ } else {
+ // typecast to avoid compilation error for the 8-bit version,
+ // even though this is dead code in that case
+ textPtr = reinterpret_cast<const char16_t*>(aString);
+ }
+
+ // split into script runs so that script can potentially influence
+ // the font matching process below
+ gfxScriptItemizer scriptRuns(textPtr, aLength);
+
+ uint32_t runStart = 0, runLimit = aLength;
+ Script runScript = Script::LATIN;
+ while (scriptRuns.Next(runStart, runLimit, runScript)) {
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
+ nsAutoCString lang;
+ mStyle.language->ToUTF8String(lang);
+ nsAutoString families;
+ mFamilyList.ToString(families);
+ uint32_t runLen = runLimit - runStart;
+ MOZ_LOG(log, LogLevel::Warning,\
+ ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
+ "len %d weight: %d width: %d style: %s size: %6.2f "
+ "%d-byte TEXTRUN [%s] ENDTEXTRUN\n",
+ (mStyle.systemFont ? "textrunui" : "textrun"),
+ NS_ConvertUTF16toUTF8(families).get(),
+ (mFamilyList.GetDefaultFontType() == eFamily_serif ?
+ "serif" :
+ (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
+ "sans-serif" : "none")),
+ lang.get(), runScript, runLen,
+ uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
+ (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
+ (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
+ "normal")),
+ mStyle.size,
+ sizeof(T),
+ NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
+ }
+
+ InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart,
+ runStart, runLimit - runStart, runScript, aMFR);
+ }
+ }
+
+ // if shaping was aborted due to lack of feature support, clear out
+ // glyph runs and redo shaping with fallback forced on
+ if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
+ redo = true;
+ aTextRun->SetShapingState(
+ gfxTextRun::eShapingState_ForceFallbackFeature);
+ aTextRun->ClearGlyphsAndCharacters();
+ }
+
+ } while (redo);
+
+ if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
+ gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
+ if (!glyph->IsSimpleGlyph()) {
+ glyph->SetClusterStart(true);
+ }
+ }
+
+ // It's possible for CoreText to omit glyph runs if it decides they contain
+ // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
+ // need to eliminate them from the glyph run array to avoid drawing "partial
+ // ligatures" with the wrong font.
+ // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
+ // it will iterate back over all glyphruns in the textrun, which leads to
+ // pathologically-bad perf in the case where a textrun contains many script
+ // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
+ // every time a new script subrun is processed.
+ aTextRun->SanitizeGlyphRuns();
+
+ aTextRun->SortGlyphRuns();
+}
+
+static inline bool
+IsPUA(uint32_t aUSV)
+{
+ // We could look up the General Category of the codepoint here,
+ // but it's simpler to check PUA codepoint ranges.
+ return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
+}
+
+template<typename T>
+void
+gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString, // text for this script run,
+ // not the entire textrun
+ uint32_t aOffset, // position of the script run
+ // within the textrun
+ uint32_t aLength, // length of the script run
+ Script aRunScript,
+ gfxMissingFontRecorder *aMFR)
+{
+ NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
+ NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
+ "don't call InitScriptRun with aborted shaping state");
+
+ // confirm the load state of userfonts in the list
+ if (!mSkipUpdateUserFonts && mUserFontSet &&
+ mCurrGeneration != mUserFontSet->GetGeneration()) {
+ UpdateUserFonts();
+ }
+
+ gfxFont *mainFont = GetFirstValidFont();
+
+ uint32_t runStart = 0;
+ AutoTArray<gfxTextRange,3> fontRanges;
+ ComputeRanges(fontRanges, aString, aLength, aRunScript,
+ aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK);
+ uint32_t numRanges = fontRanges.Length();
+ bool missingChars = false;
+
+ for (uint32_t r = 0; r < numRanges; r++) {
+ const gfxTextRange& range = fontRanges[r];
+ uint32_t matchedLength = range.Length();
+ gfxFont *matchedFont = range.font;
+ bool vertical =
+ range.orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+ // create the glyph run for this range
+ if (matchedFont && mStyle.noFallbackVariantFeatures) {
+ // common case - just do glyph layout and record the
+ // resulting positioned glyphs
+ aTextRun->AddGlyphRun(matchedFont, range.matchType,
+ aOffset + runStart, (matchedLength > 0),
+ range.orientation);
+ if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
+ aString + runStart,
+ aOffset + runStart,
+ matchedLength,
+ aRunScript,
+ vertical)) {
+ // glyph layout failed! treat as missing glyphs
+ matchedFont = nullptr;
+ }
+ } else if (matchedFont) {
+ // shape with some variant feature that requires fallback handling
+ bool petiteToSmallCaps = false;
+ bool syntheticLower = false;
+ bool syntheticUpper = false;
+
+ if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
+ (aTextRun->GetShapingState() ==
+ gfxTextRun::eShapingState_ForceFallbackFeature ||
+ !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
+ aString, aLength,
+ aRunScript)))
+ {
+ // fallback for subscript/superscript variant glyphs
+
+ // if the feature was already used, abort and force
+ // fallback across the entire textrun
+ gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
+
+ if (ss == gfxTextRun::eShapingState_Normal) {
+ aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFallback);
+ } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
+ aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
+ return;
+ }
+
+ RefPtr<gfxFont> subSuperFont =
+ matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
+ aTextRun->AddGlyphRun(subSuperFont, range.matchType,
+ aOffset + runStart, (matchedLength > 0),
+ range.orientation);
+ if (!subSuperFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
+ aString + runStart,
+ aOffset + runStart,
+ matchedLength,
+ aRunScript,
+ vertical)) {
+ // glyph layout failed! treat as missing glyphs
+ matchedFont = nullptr;
+ }
+ } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
+ !matchedFont->SupportsVariantCaps(aRunScript,
+ mStyle.variantCaps,
+ petiteToSmallCaps,
+ syntheticLower,
+ syntheticUpper))
+ {
+ // fallback for small-caps variant glyphs
+ if (!matchedFont->InitFakeSmallCapsRun(aDrawTarget, aTextRun,
+ aString + runStart,
+ aOffset + runStart,
+ matchedLength,
+ range.matchType,
+ range.orientation,
+ aRunScript,
+ syntheticLower,
+ syntheticUpper)) {
+ matchedFont = nullptr;
+ }
+ } else {
+ // shape normally with variant feature enabled
+ gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
+
+ // adjust the shaping state if necessary
+ if (ss == gfxTextRun::eShapingState_Normal) {
+ aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFeature);
+ } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
+ // already have shaping results using fallback, need to redo
+ aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
+ return;
+ }
+
+ // do glyph layout and record the resulting positioned glyphs
+ aTextRun->AddGlyphRun(matchedFont, range.matchType,
+ aOffset + runStart, (matchedLength > 0),
+ range.orientation);
+ if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
+ aString + runStart,
+ aOffset + runStart,
+ matchedLength,
+ aRunScript,
+ vertical)) {
+ // glyph layout failed! treat as missing glyphs
+ matchedFont = nullptr;
+ }
+ }
+ } else {
+ aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
+ aOffset + runStart, (matchedLength > 0),
+ range.orientation);
+ }
+
+ if (!matchedFont) {
+ // We need to set cluster boundaries (and mark spaces) so that
+ // surrogate pairs, combining characters, etc behave properly,
+ // even if we don't have glyphs for them
+ aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
+ matchedLength);
+
+ // various "missing" characters may need special handling,
+ // so we check for them here
+ uint32_t runLimit = runStart + matchedLength;
+ for (uint32_t index = runStart; index < runLimit; index++) {
+ T ch = aString[index];
+
+ // tab and newline are not to be displayed as hexboxes,
+ // but do need to be recorded in the textrun
+ if (ch == '\n') {
+ aTextRun->SetIsNewline(aOffset + index);
+ continue;
+ }
+ if (ch == '\t') {
+ aTextRun->SetIsTab(aOffset + index);
+ continue;
+ }
+
+ // for 16-bit textruns only, check for surrogate pairs and
+ // special Unicode spaces; omit these checks in 8-bit runs
+ if (sizeof(T) == sizeof(char16_t)) {
+ if (NS_IS_HIGH_SURROGATE(ch) &&
+ index + 1 < aLength &&
+ NS_IS_LOW_SURROGATE(aString[index + 1]))
+ {
+ uint32_t usv =
+ SURROGATE_TO_UCS4(ch, aString[index + 1]);
+ aTextRun->SetMissingGlyph(aOffset + index,
+ usv,
+ mainFont);
+ index++;
+ if (!mSkipDrawing && !IsPUA(usv)) {
+ missingChars = true;
+ }
+ continue;
+ }
+
+ // check if this is a known Unicode whitespace character that
+ // we can render using the space glyph with a custom width
+ gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
+ if (wid >= 0.0) {
+ nscoord advance =
+ aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
+ if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
+ aTextRun->GetCharacterGlyphs()[aOffset + index].
+ SetSimpleGlyph(advance,
+ mainFont->GetSpaceGlyph());
+ } else {
+ gfxTextRun::DetailedGlyph detailedGlyph;
+ detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
+ detailedGlyph.mAdvance = advance;
+ detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
+ gfxShapedText::CompressedGlyph g;
+ g.SetComplex(true, true, 1);
+ aTextRun->SetGlyphs(aOffset + index,
+ g, &detailedGlyph);
+ }
+ continue;
+ }
+ }
+
+ if (IsInvalidChar(ch)) {
+ // invalid chars are left as zero-width/invisible
+ continue;
+ }
+
+ // record char code so we can draw a box with the Unicode value
+ aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
+ if (!mSkipDrawing && !IsPUA(ch)) {
+ missingChars = true;
+ }
+ }
+ }
+
+ runStart += matchedLength;
+ }
+
+ if (aMFR && missingChars) {
+ aMFR->RecordScript(aRunScript);
+ }
+}
+
+gfxTextRun *
+gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
+ LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
+{
+ MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK),
+ "flags here should only be used to specify orientation");
+ if (mCachedEllipsisTextRun &&
+ (mCachedEllipsisTextRun->GetFlags() & TEXT_ORIENT_MASK) == aFlags &&
+ mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
+ return mCachedEllipsisTextRun.get();
+ }
+
+ // Use a Unicode ellipsis if the font supports it,
+ // otherwise use three ASCII periods as fallback.
+ gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
+ nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
+ ? nsDependentString(kEllipsisChar,
+ ArrayLength(kEllipsisChar) - 1)
+ : nsDependentString(kASCIIPeriodsChar,
+ ArrayLength(kASCIIPeriodsChar) - 1);
+
+ RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
+ Parameters params = {
+ refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
+ };
+ mCachedEllipsisTextRun =
+ MakeTextRun(ellipsis.get(), ellipsis.Length(), &params,
+ aFlags | TEXT_IS_PERSISTENT, nullptr);
+ if (!mCachedEllipsisTextRun) {
+ return nullptr;
+ }
+ // don't let the presence of a cached ellipsis textrun prolong the
+ // fontgroup's life
+ mCachedEllipsisTextRun->ReleaseFontGroup();
+ return mCachedEllipsisTextRun.get();
+}
+
+already_AddRefed<gfxFont>
+gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
+ Script aRunScript)
+{
+ GlobalFontMatch data(aCh, aRunScript, &mStyle);
+ aFamily->SearchAllFontsForChar(&data);
+ gfxFontEntry* fe = data.mBestMatch;
+ if (!fe) {
+ return nullptr;
+ }
+
+ bool needsBold = mStyle.weight >= 600 && !fe->IsBold() &&
+ mStyle.allowSyntheticWeight;
+ RefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
+ return font.forget();
+}
+
+gfxFloat
+gfxFontGroup::GetUnderlineOffset()
+{
+ if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
+ // if the fontlist contains a bad underline font, make the underline
+ // offset the min of the first valid font and bad font underline offsets
+ uint32_t len = mFonts.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ FamilyFace& ff = mFonts[i];
+ if (!ff.IsUserFontContainer() &&
+ !ff.FontEntry()->IsUserFont() &&
+ ff.Family() &&
+ ff.Family()->IsBadUnderlineFamily()) {
+ RefPtr<gfxFont> font = GetFontAt(i);
+ if (!font) {
+ continue;
+ }
+ gfxFloat bad = font->GetMetrics(gfxFont::eHorizontal).
+ underlineOffset;
+ gfxFloat first =
+ GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal).
+ underlineOffset;
+ mUnderlineOffset = std::min(first, bad);
+ return mUnderlineOffset;
+ }
+ }
+
+ // no bad underline fonts, use the first valid font's metric
+ mUnderlineOffset = GetFirstValidFont()->
+ GetMetrics(gfxFont::eHorizontal).underlineOffset;
+ }
+
+ return mUnderlineOffset;
+}
+
+already_AddRefed<gfxFont>
+gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
+ Script aRunScript, gfxFont *aPrevMatchedFont,
+ uint8_t *aMatchType)
+{
+ // If the char is a cluster extender, we want to use the same font as the
+ // preceding character if possible. This is preferable to using the font
+ // group because it avoids breaks in shaping within a cluster.
+ if (aPrevMatchedFont && IsClusterExtender(aCh) &&
+ aPrevMatchedFont->HasCharacter(aCh)) {
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+
+ // Special cases for NNBSP (as used in Mongolian):
+ const uint32_t NARROW_NO_BREAK_SPACE = 0x202f;
+ if (aCh == NARROW_NO_BREAK_SPACE) {
+ // If there is no preceding character, try the font that we'd use
+ // for the next char (unless it's just another NNBSP; we don't try
+ // to look ahead through a whole run of them).
+ if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) {
+ RefPtr<gfxFont> nextFont =
+ FindFontForChar(aNextCh, 0, 0, aRunScript, aPrevMatchedFont,
+ aMatchType);
+ if (nextFont && nextFont->HasCharacter(aCh)) {
+ return nextFont.forget();
+ }
+ }
+ // Otherwise, treat NNBSP like a cluster extender (as above) and try
+ // to continue the preceding font run.
+ if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+ }
+
+ // To optimize common cases, try the first font in the font-group
+ // before going into the more detailed checks below
+ uint32_t nextIndex = 0;
+ bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
+ bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
+ bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
+
+ if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
+ RefPtr<gfxFont> firstFont = GetFontAt(0, aCh);
+ if (firstFont) {
+ if (firstFont->HasCharacter(aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return firstFont.forget();
+ }
+
+ RefPtr<gfxFont> font;
+ if (mFonts[0].CheckForFallbackFaces()) {
+ font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
+ aRunScript);
+ } else if (!firstFont->GetFontEntry()->IsUserFont()) {
+ // For platform fonts (but not userfonts), we may need to do
+ // fallback within the family to handle cases where some faces
+ // such as Italic or Black have reduced character sets compared
+ // to the family's Regular face.
+ gfxFontEntry* fe = firstFont->GetFontEntry();
+ if (!fe->IsUpright() ||
+ fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
+ fe->Stretch() != NS_FONT_STRETCH_NORMAL) {
+ // If style/weight/stretch was not Normal, see if we can
+ // fall back to a next-best face (e.g. Arial Black -> Bold,
+ // or Arial Narrow -> Regular).
+ font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
+ aRunScript);
+ }
+ }
+ if (font) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return font.forget();
+ }
+ }
+
+ // we don't need to check the first font again below
+ ++nextIndex;
+ }
+
+ if (aPrevMatchedFont) {
+ // Don't switch fonts for control characters, regardless of
+ // whether they are present in the current font, as they won't
+ // actually be rendered (see bug 716229)
+ if (isJoinControl ||
+ GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+
+ // if previous character was a join-causer (ZWJ),
+ // use the same font as the previous range if we can
+ if (wasJoinCauser) {
+ if (aPrevMatchedFont->HasCharacter(aCh)) {
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+ }
+ }
+
+ // if this character is a variation selector,
+ // use the previous font regardless of whether it supports VS or not.
+ // otherwise the text run will be divided.
+ if (isVarSelector) {
+ if (aPrevMatchedFont) {
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+ // VS alone. it's meaningless to search different fonts
+ return nullptr;
+ }
+
+ // 1. check remaining fonts in the font group
+ uint32_t fontListLength = mFonts.Length();
+ for (uint32_t i = nextIndex; i < fontListLength; i++) {
+ FamilyFace& ff = mFonts[i];
+ if (ff.IsInvalid() || ff.IsLoading()) {
+ continue;
+ }
+
+ // if available, use already made gfxFont and check for character
+ RefPtr<gfxFont> font = ff.Font();
+ if (font) {
+ if (font->HasCharacter(aCh)) {
+ return font.forget();
+ }
+ continue;
+ }
+
+ // don't have a gfxFont yet, test before building
+ gfxFontEntry *fe = ff.FontEntry();
+ if (fe->mIsUserFontContainer) {
+ // for userfonts, need to test both the unicode range map and
+ // the cmap of the platform font entry
+ gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
+
+ // never match a character outside the defined unicode range
+ if (!ufe->CharacterInUnicodeRange(aCh)) {
+ continue;
+ }
+
+ // load if not already loaded but only if no other font in similar
+ // range within family is loading
+ if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
+ !FontLoadingForFamily(ff.Family(), aCh)) {
+ ufe->Load();
+ ff.CheckState(mSkipDrawing);
+ }
+ gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
+ if (pfe && pfe->HasCharacter(aCh)) {
+ font = GetFontAt(i, aCh);
+ if (font) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return font.forget();
+ }
+ }
+ } else if (fe->HasCharacter(aCh)) {
+ // for normal platform fonts, after checking the cmap
+ // build the font via GetFontAt
+ font = GetFontAt(i, aCh);
+ if (font) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return font.forget();
+ }
+ }
+
+ // check other family faces if needed
+ if (ff.CheckForFallbackFaces()) {
+ NS_ASSERTION(i == 0 ? true :
+ !mFonts[i-1].CheckForFallbackFaces() ||
+ !mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
+ "should only do fallback once per font family");
+ font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
+ if (font) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return font.forget();
+ }
+ } else {
+ // For platform fonts, but not user fonts, consider intra-family
+ // fallback to handle styles with reduced character sets (see
+ // also above).
+ fe = ff.FontEntry();
+ if (!fe->mIsUserFontContainer && !fe->IsUserFont() &&
+ (!fe->IsUpright() ||
+ fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
+ fe->Stretch() != NS_FONT_STRETCH_NORMAL)) {
+ font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
+ if (font) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return font.forget();
+ }
+ }
+ }
+ }
+
+ if (fontListLength == 0) {
+ RefPtr<gfxFont> defaultFont = GetDefaultFont();
+ if (defaultFont->HasCharacter(aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return defaultFont.forget();
+ }
+ }
+
+ // if character is in Private Use Area, don't do matching against pref or system fonts
+ if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
+ return nullptr;
+
+ // 2. search pref fonts
+ RefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh, aNextCh);
+ if (font) {
+ *aMatchType = gfxTextRange::kPrefsFallback;
+ return font.forget();
+ }
+
+ // 3. use fallback fonts
+ // -- before searching for something else check the font used for the previous character
+ if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
+ *aMatchType = gfxTextRange::kSystemFallback;
+ RefPtr<gfxFont> ret = aPrevMatchedFont;
+ return ret.forget();
+ }
+
+ // for known "space" characters, don't do a full system-fallback search;
+ // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
+ if (GetGeneralCategory(aCh) ==
+ HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
+ GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0)
+ {
+ return nullptr;
+ }
+
+ // -- otherwise look for other stuff
+ *aMatchType = gfxTextRange::kSystemFallback;
+ font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
+ return font.forget();
+}
+
+template<typename T>
+void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
+ const T *aString, uint32_t aLength,
+ Script aRunScript, uint16_t aOrientation)
+{
+ NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
+ NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
+
+ uint32_t prevCh = 0;
+ uint32_t nextCh = aString[0];
+ if (sizeof(T) == sizeof(char16_t)) {
+ if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) &&
+ NS_IS_LOW_SURROGATE(aString[1])) {
+ nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
+ }
+ }
+ int32_t lastRangeIndex = -1;
+
+ // initialize prevFont to the group's primary font, so that this will be
+ // used for string-initial control chars, etc rather than risk hitting font
+ // fallback for these (bug 716229)
+ gfxFont *prevFont = GetFirstValidFont();
+
+ // if we use the initial value of prevFont, we treat this as a match from
+ // the font group; fixes bug 978313
+ uint8_t matchType = gfxTextRange::kFontGroup;
+
+ for (uint32_t i = 0; i < aLength; i++) {
+
+ const uint32_t origI = i; // save off in case we increase for surrogate
+
+ // set up current ch
+ uint32_t ch = nextCh;
+
+ // Get next char (if any) so that FindFontForChar can look ahead
+ // for a possible variation selector.
+
+ if (sizeof(T) == sizeof(char16_t)) {
+ // In 16-bit case only, check for surrogate pairs.
+ if (ch > 0xffffu) {
+ i++;
+ }
+ if (i < aLength - 1) {
+ nextCh = aString[i + 1];
+ if ((i + 2 < aLength) && NS_IS_HIGH_SURROGATE(nextCh) &&
+ NS_IS_LOW_SURROGATE(aString[i + 2])) {
+ nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]);
+ }
+ } else {
+ nextCh = 0;
+ }
+ } else {
+ // 8-bit case is trivial.
+ nextCh = i < aLength - 1 ? aString[i + 1] : 0;
+ }
+
+ if (ch == 0xa0) {
+ ch = ' ';
+ }
+
+ // find the font for this char
+ RefPtr<gfxFont> font =
+ FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont,
+ &matchType);
+
+#ifndef RELEASE_OR_BETA
+ if (MOZ_UNLIKELY(mTextPerf)) {
+ if (matchType == gfxTextRange::kPrefsFallback) {
+ mTextPerf->current.fallbackPrefs++;
+ } else if (matchType == gfxTextRange::kSystemFallback) {
+ mTextPerf->current.fallbackSystem++;
+ }
+ }
+#endif
+
+ prevCh = ch;
+
+ uint16_t orient = aOrientation;
+ if (aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+ // For CSS text-orientation:mixed, we need to resolve orientation
+ // on a per-character basis using the UTR50 orientation property.
+ switch (GetVerticalOrientation(ch)) {
+ case VERTICAL_ORIENTATION_U:
+ case VERTICAL_ORIENTATION_Tr:
+ case VERTICAL_ORIENTATION_Tu:
+ orient = TEXT_ORIENT_VERTICAL_UPRIGHT;
+ break;
+ case VERTICAL_ORIENTATION_R:
+ orient = TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+ break;
+ }
+ }
+
+ if (lastRangeIndex == -1) {
+ // first char ==> make a new range
+ aRanges.AppendElement(gfxTextRange(0, 1, font, matchType, orient));
+ lastRangeIndex++;
+ prevFont = font;
+ } else {
+ // if font or orientation has changed, make a new range...
+ // unless ch is a variation selector (bug 1248248)
+ gfxTextRange& prevRange = aRanges[lastRangeIndex];
+ if (prevRange.font != font || prevRange.matchType != matchType ||
+ (prevRange.orientation != orient && !IsClusterExtender(ch))) {
+ // close out the previous range
+ prevRange.end = origI;
+ aRanges.AppendElement(gfxTextRange(origI, i + 1,
+ font, matchType, orient));
+ lastRangeIndex++;
+
+ // update prevFont for the next match, *unless* we switched
+ // fonts on a ZWJ, in which case propagating the changed font
+ // is probably not a good idea (see bug 619511)
+ if (sizeof(T) == sizeof(uint8_t) ||
+ !gfxFontUtils::IsJoinCauser(ch))
+ {
+ prevFont = font;
+ }
+ }
+ }
+ }
+
+ aRanges[lastRangeIndex].end = aLength;
+
+#ifndef RELEASE_OR_BETA
+ LogModule* log = mStyle.systemFont
+ ? gfxPlatform::GetLog(eGfxLog_textrunui)
+ : gfxPlatform::GetLog(eGfxLog_textrun);
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
+ nsAutoCString lang;
+ mStyle.language->ToUTF8String(lang);
+ nsAutoString families;
+ mFamilyList.ToString(families);
+
+ // collect the font matched for each range
+ nsAutoCString fontMatches;
+ for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
+ const gfxTextRange& r = aRanges[i];
+ fontMatches.AppendPrintf(" [%u:%u] %.200s (%s)", r.start, r.end,
+ (r.font.get() ?
+ NS_ConvertUTF16toUTF8(r.font->GetName()).get() : "<null>"),
+ (r.matchType == gfxTextRange::kFontGroup ?
+ "list" :
+ (r.matchType == gfxTextRange::kPrefsFallback) ?
+ "prefs" : "sys"));
+ }
+ MOZ_LOG(log, LogLevel::Debug,\
+ ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
+ "%s\n",
+ (mStyle.systemFont ? "textrunui" : "textrun"),
+ NS_ConvertUTF16toUTF8(families).get(),
+ (mFamilyList.GetDefaultFontType() == eFamily_serif ?
+ "serif" :
+ (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
+ "sans-serif" : "none")),
+ lang.get(), aRunScript,
+ fontMatches.get()));
+ }
+#endif
+}
+
+gfxUserFontSet*
+gfxFontGroup::GetUserFontSet()
+{
+ return mUserFontSet;
+}
+
+void
+gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
+{
+ if (aUserFontSet == mUserFontSet) {
+ return;
+ }
+ mUserFontSet = aUserFontSet;
+ mCurrGeneration = GetGeneration() - 1;
+ UpdateUserFonts();
+}
+
+uint64_t
+gfxFontGroup::GetGeneration()
+{
+ if (!mUserFontSet)
+ return 0;
+ return mUserFontSet->GetGeneration();
+}
+
+uint64_t
+gfxFontGroup::GetRebuildGeneration()
+{
+ if (!mUserFontSet)
+ return 0;
+ return mUserFontSet->GetRebuildGeneration();
+}
+
+// note: gfxPangoFontGroup overrides UpdateUserFonts, such that
+// BuildFontList is never used
+void
+gfxFontGroup::UpdateUserFonts()
+{
+ if (mCurrGeneration < GetRebuildGeneration()) {
+ // fonts in userfont set changed, need to redo the fontlist
+ mFonts.Clear();
+ ClearCachedData();
+ BuildFontList();
+ mCurrGeneration = GetGeneration();
+ } else if (mCurrGeneration != GetGeneration()) {
+ // load state change occurred, verify load state and validity of fonts
+ ClearCachedData();
+
+ uint32_t len = mFonts.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ FamilyFace& ff = mFonts[i];
+ if (ff.Font() || !ff.IsUserFontContainer()) {
+ continue;
+ }
+ ff.CheckState(mSkipDrawing);
+ }
+
+ mCurrGeneration = GetGeneration();
+ }
+}
+
+bool
+gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont)
+{
+ UpdateUserFonts();
+ // search through the fonts list for a specific user font
+ uint32_t len = mFonts.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ FamilyFace& ff = mFonts[i];
+ if (ff.EqualsUserFont(aUserFont)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+already_AddRefed<gfxFont>
+gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh)
+{
+ RefPtr<gfxFont> font;
+
+ eFontPrefLang charLang;
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+
+ EmojiPresentation emoji = GetEmojiPresentation(aCh);
+ if ((emoji != EmojiPresentation::TextOnly &&
+ (aNextCh == kVariationSelector16 ||
+ (emoji == EmojiPresentation::EmojiDefault &&
+ aNextCh != kVariationSelector15)))) {
+ charLang = eFontPrefLang_Emoji;
+ } else {
+ // get the pref font list if it hasn't been set up already
+ uint32_t unicodeRange = FindCharUnicodeRange(aCh);
+ charLang = pfl->GetFontPrefLangFor(unicodeRange);
+ }
+
+ // if the last pref font was the first family in the pref list, no need to recheck through a list of families
+ if (mLastPrefFont && charLang == mLastPrefLang &&
+ mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
+ font = mLastPrefFont;
+ return font.forget();
+ }
+
+ // based on char lang and page lang, set up list of pref lang fonts to check
+ eFontPrefLang prefLangs[kMaxLenPrefLangList];
+ uint32_t i, numLangs = 0;
+
+ pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
+
+ for (i = 0; i < numLangs; i++) {
+ eFontPrefLang currentLang = prefLangs[i];
+ mozilla::FontFamilyType defaultGeneric =
+ pfl->GetDefaultGeneric(currentLang);
+ nsTArray<RefPtr<gfxFontFamily>>* families =
+ pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
+ NS_ASSERTION(families, "no pref font families found");
+
+ // find the first pref font that includes the character
+ uint32_t j, numPrefs;
+ numPrefs = families->Length();
+ for (j = 0; j < numPrefs; j++) {
+ // look up the appropriate face
+ gfxFontFamily *family = (*families)[j];
+ if (!family) continue;
+
+ // if a pref font is used, it's likely to be used again in the same text run.
+ // the style doesn't change so the face lookup can be cached rather than calling
+ // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent
+ // pref font lookups
+ if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
+ font = mLastPrefFont;
+ return font.forget();
+ }
+
+ bool needsBold;
+ gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
+ // if ch in cmap, create and return a gfxFont
+ if (fe && fe->HasCharacter(aCh)) {
+ RefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
+ if (!prefFont) continue;
+ mLastPrefFamily = family;
+ mLastPrefFont = prefFont;
+ mLastPrefLang = charLang;
+ mLastPrefFirstFont = (i == 0 && j == 0);
+ return prefFont.forget();
+ }
+
+ }
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<gfxFont>
+gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript)
+{
+ gfxFontEntry *fe =
+ gfxPlatformFontList::PlatformFontList()->
+ SystemFindFontForChar(aCh, aNextCh, aRunScript, &mStyle);
+ if (fe) {
+ bool wantBold = mStyle.ComputeWeight() >= 6;
+ RefPtr<gfxFont> font =
+ fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
+ return font.forget();
+ }
+
+ return nullptr;
+}
+
+/*static*/ void
+gfxFontGroup::Shutdown()
+{
+ NS_IF_RELEASE(gLangService);
+}
+
+nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
+
+void
+gfxMissingFontRecorder::Flush()
+{
+ static bool mNotifiedFontsInitialized = false;
+ static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
+ if (!mNotifiedFontsInitialized) {
+ memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
+ mNotifiedFontsInitialized = true;
+ }
+
+ nsAutoString fontNeeded;
+ for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
+ mMissingFonts[i] &= ~mNotifiedFonts[i];
+ if (!mMissingFonts[i]) {
+ continue;
+ }
+ for (uint32_t j = 0; j < 32; ++j) {
+ if (!(mMissingFonts[i] & (1 << j))) {
+ continue;
+ }
+ mNotifiedFonts[i] |= (1 << j);
+ if (!fontNeeded.IsEmpty()) {
+ fontNeeded.Append(char16_t(','));
+ }
+ uint32_t sc = i * 32 + j;
+ MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES),
+ "how did we set the bit for an invalid script code?");
+ uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc));
+ fontNeeded.Append(char16_t(tag >> 24));
+ fontNeeded.Append(char16_t((tag >> 16) & 0xff));
+ fontNeeded.Append(char16_t((tag >> 8) & 0xff));
+ fontNeeded.Append(char16_t(tag & 0xff));
+ }
+ mMissingFonts[i] = 0;
+ }
+ if (!fontNeeded.IsEmpty()) {
+ nsCOMPtr<nsIObserverService> service = GetObserverService();
+ service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
+ }
+}
diff --git a/system/graphics/thebes/gfxTextRun.h b/system/graphics/thebes/gfxTextRun.h
new file mode 100644
index 000000000..7217eca74
--- /dev/null
+++ b/system/graphics/thebes/gfxTextRun.h
@@ -0,0 +1,1236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEXTRUN_H
+#define GFX_TEXTRUN_H
+
+#include "gfxTypes.h"
+#include "nsString.h"
+#include "gfxPoint.h"
+#include "gfxFont.h"
+#include "gfxFont-Impl.h"
+#include "gfxFontConstants.h"
+#include "nsTArray.h"
+#include "gfxSkipChars.h"
+#include "gfxPlatform.h"
+#include "mozilla/MemoryReporting.h"
+#include "DrawMode.h"
+#include "harfbuzz/hb.h"
+#include "nsUnicodeScriptCodes.h"
+#include "nsColor.h"
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+class gfxContext;
+class gfxFontGroup;
+class gfxUserFontEntry;
+class gfxUserFontSet;
+class nsIAtom;
+class nsILanguageAtomService;
+class gfxMissingFontRecorder;
+
+namespace mozilla {
+class SVGContextPaint;
+};
+
+/**
+ * Callback for Draw() to use when drawing text with mode
+ * DrawMode::GLYPH_PATH.
+ */
+struct gfxTextRunDrawCallbacks {
+
+ /**
+ * Constructs a new DrawCallbacks object.
+ *
+ * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be painted. If
+ * false, SVG glyphs will not be painted; fallback plain glyphs are not
+ * emitted either.
+ */
+ explicit gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false)
+ : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs)
+ {
+ }
+
+ /**
+ * Called when a path has been emitted to the gfxContext when
+ * painting a text run. This can be called any number of times,
+ * due to partial ligatures and intervening SVG glyphs.
+ */
+ virtual void NotifyGlyphPathEmitted() = 0;
+
+ bool mShouldPaintSVGGlyphs;
+};
+
+/**
+ * gfxTextRun is an abstraction for drawing and measuring substrings of a run
+ * of text. It stores runs of positioned glyph data, each run having a single
+ * gfxFont. The glyphs are associated with a string of source text, and the
+ * gfxTextRun APIs take parameters that are offsets into that source text.
+ *
+ * gfxTextRuns are mostly immutable. The only things that can change are
+ * inter-cluster spacing and line break placement. Spacing is always obtained
+ * lazily by methods that need it, it is not cached. Line breaks are stored
+ * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
+ * not actually do anything to explicitly account for line breaks). Initially
+ * there are no line breaks. The textrun can record line breaks before or after
+ * any given cluster. (Line breaks specified inside clusters are ignored.)
+ *
+ * It is important that zero-length substrings are handled correctly. This will
+ * be on the test!
+ */
+class gfxTextRun : public gfxShapedText
+{
+ NS_INLINE_DECL_REFCOUNTING(gfxTextRun);
+
+protected:
+ // Override operator delete to properly free the object that was
+ // allocated via malloc.
+ void operator delete(void* p) {
+ free(p);
+ }
+
+ virtual ~gfxTextRun();
+
+public:
+ typedef gfxFont::RunMetrics Metrics;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ // Public textrun API for general use
+
+ bool IsClusterStart(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].IsClusterStart();
+ }
+ bool IsLigatureGroupStart(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].IsLigatureGroupStart();
+ }
+ bool CanBreakLineBefore(uint32_t aPos) const {
+ return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
+ }
+ bool CanHyphenateBefore(uint32_t aPos) const {
+ return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
+ }
+
+ // Returns a gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_* value
+ // as defined in gfxFont.h (may be NONE, NORMAL or HYPHEN).
+ uint8_t CanBreakBefore(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CanBreakBefore();
+ }
+
+ bool CharIsSpace(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsSpace();
+ }
+ bool CharIsTab(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsTab();
+ }
+ bool CharIsNewline(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsNewline();
+ }
+ bool CharMayHaveEmphasisMark(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharMayHaveEmphasisMark();
+ }
+
+ // All offsets are in terms of the string passed into MakeTextRun.
+
+ // Describe range [start, end) of a text run. The range is
+ // restricted to grapheme cluster boundaries.
+ struct Range
+ {
+ uint32_t start;
+ uint32_t end;
+ uint32_t Length() const { return end - start; }
+
+ Range() : start(0), end(0) {}
+ Range(uint32_t aStart, uint32_t aEnd)
+ : start(aStart), end(aEnd) {}
+ explicit Range(const gfxTextRun* aTextRun)
+ : start(0), end(aTextRun->GetLength()) {}
+ };
+
+ // All coordinates are in layout/app units
+
+ /**
+ * Set the potential linebreaks for a substring of the textrun. These are
+ * the "allow break before" points. Initially, there are no potential
+ * linebreaks.
+ *
+ * This can change glyphs and/or geometry! Some textruns' shapes
+ * depend on potential line breaks (e.g., title-case-converting textruns).
+ * This function is virtual so that those textruns can reshape themselves.
+ *
+ * @return true if this changed the linebreaks, false if the new line
+ * breaks are the same as the old
+ */
+ virtual bool SetPotentialLineBreaks(Range aRange,
+ const uint8_t* aBreakBefore);
+
+ /**
+ * Layout provides PropertyProvider objects. These allow detection of
+ * potential line break points and computation of spacing. We pass the data
+ * this way to allow lazy data acquisition; for example BreakAndMeasureText
+ * will want to only ask for properties of text it's actually looking at.
+ *
+ * NOTE that requested spacing may not actually be applied, if the textrun
+ * is unable to apply it in some context. Exception: spacing around a
+ * whitespace character MUST always be applied.
+ */
+ class PropertyProvider {
+ public:
+ // Detect hyphenation break opportunities in the given range; breaks
+ // not at cluster boundaries will be ignored.
+ virtual void GetHyphenationBreaks(Range aRange, bool *aBreakBefore) = 0;
+
+ // Returns the provider's hyphenation setting, so callers can decide
+ // whether it is necessary to call GetHyphenationBreaks.
+ // Result is an NS_STYLE_HYPHENS_* value.
+ virtual int8_t GetHyphensOption() = 0;
+
+ // Returns the extra width that will be consumed by a hyphen. This should
+ // be constant for a given textrun.
+ virtual gfxFloat GetHyphenWidth() = 0;
+
+ typedef gfxFont::Spacing Spacing;
+
+ /**
+ * Get the spacing around the indicated characters. Spacing must be zero
+ * inside clusters. In other words, if character i is not
+ * CLUSTER_START, then character i-1 must have zero after-spacing and
+ * character i must have zero before-spacing.
+ */
+ virtual void GetSpacing(Range aRange, Spacing *aSpacing) = 0;
+
+ // Returns a gfxContext that can be used to measure the hyphen glyph.
+ // Only called if the hyphen width is requested.
+ virtual already_AddRefed<DrawTarget> GetDrawTarget() = 0;
+
+ // Return the appUnitsPerDevUnit value to be used when measuring.
+ // Only called if the hyphen width is requested.
+ virtual uint32_t GetAppUnitsPerDevUnit() = 0;
+ };
+
+ struct DrawParams
+ {
+ gfxContext* context;
+ DrawMode drawMode = DrawMode::GLYPH_FILL;
+ nscolor textStrokeColor = 0;
+ gfxPattern* textStrokePattern = nullptr;
+ const mozilla::gfx::StrokeOptions *strokeOpts = nullptr;
+ const mozilla::gfx::DrawOptions *drawOpts = nullptr;
+ PropertyProvider* provider = nullptr;
+ // If non-null, the advance width of the substring is set.
+ gfxFloat* advanceWidth = nullptr;
+ mozilla::SVGContextPaint* contextPaint = nullptr;
+ gfxTextRunDrawCallbacks* callbacks = nullptr;
+ explicit DrawParams(gfxContext* aContext) : context(aContext) {}
+ };
+
+ /**
+ * Draws a substring. Uses only GetSpacing from aBreakProvider.
+ * The provided point is the baseline origin on the left of the string
+ * for LTR, on the right of the string for RTL.
+ *
+ * Drawing should respect advance widths in the sense that for LTR runs,
+ * Draw(Range(start, middle), pt, ...) followed by
+ * Draw(Range(middle, end), gfxPoint(pt.x + advance, pt.y), ...)
+ * should have the same effect as
+ * Draw(Range(start, end), pt, ...)
+ *
+ * For RTL runs the rule is:
+ * Draw(Range(middle, end), pt, ...) followed by
+ * Draw(Range(start, middle), gfxPoint(pt.x + advance, pt.y), ...)
+ * should have the same effect as
+ * Draw(Range(start, end), pt, ...)
+ *
+ * Glyphs should be drawn in logical content order, which can be significant
+ * if they overlap (perhaps due to negative spacing).
+ */
+ void Draw(Range aRange, gfxPoint aPt, const DrawParams& aParams) const;
+
+ /**
+ * Draws the emphasis marks for this text run. Uses only GetSpacing
+ * from aProvider. The provided point is the baseline origin of the
+ * line of emphasis marks.
+ */
+ void DrawEmphasisMarks(gfxContext* aContext, gfxTextRun* aMark,
+ gfxFloat aMarkAdvance, gfxPoint aPt,
+ Range aRange, PropertyProvider* aProvider) const;
+
+ /**
+ * Computes the ReflowMetrics for a substring.
+ * Uses GetSpacing from aBreakProvider.
+ * @param aBoundingBoxType which kind of bounding box (loose/tight)
+ */
+ Metrics MeasureText(Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ PropertyProvider* aProvider) const;
+
+ Metrics MeasureText(gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ PropertyProvider* aProvider = nullptr) const {
+ return MeasureText(Range(this), aBoundingBoxType,
+ aDrawTargetForTightBoundingBox, aProvider);
+ }
+
+ /**
+ * Computes just the advance width for a substring.
+ * Uses GetSpacing from aBreakProvider.
+ * If aSpacing is not null, the spacing attached before and after
+ * the substring would be returned in it. NOTE: the spacing is
+ * included in the advance width.
+ */
+ gfxFloat GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
+ PropertyProvider::Spacing*
+ aSpacing = nullptr) const;
+
+ gfxFloat GetAdvanceWidth() const {
+ return GetAdvanceWidth(Range(this), nullptr);
+ }
+
+ /**
+ * Computes the minimum advance width for a substring assuming line
+ * breaking is allowed everywhere.
+ */
+ gfxFloat GetMinAdvanceWidth(Range aRange);
+
+ /**
+ * Clear all stored line breaks for the given range (both before and after),
+ * and then set the line-break state before aRange.start to aBreakBefore and
+ * after the last cluster to aBreakAfter.
+ *
+ * We require that before and after line breaks be consistent. For clusters
+ * i and i+1, we require that if there is a break after cluster i, a break
+ * will be specified before cluster i+1. This may be temporarily violated
+ * (e.g. after reflowing line L and before reflowing line L+1); to handle
+ * these temporary violations, we say that there is a break betwen i and i+1
+ * if a break is specified after i OR a break is specified before i+1.
+ *
+ * This can change textrun geometry! The existence of a linebreak can affect
+ * the advance width of the cluster before the break (when kerning) or the
+ * geometry of one cluster before the break or any number of clusters
+ * after the break. (The one-cluster-before-the-break limit is somewhat
+ * arbitrary; if some scripts require breaking it, then we need to
+ * alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase
+ * it could affect the layout of frames before it...)
+ *
+ * We return true if glyphs or geometry changed, false otherwise. This
+ * function is virtual so that gfxTextRun subclasses can reshape
+ * properly.
+ *
+ * @param aAdvanceWidthDelta if non-null, returns the change in advance
+ * width of the given range.
+ */
+ virtual bool SetLineBreaks(Range aRange,
+ bool aLineBreakBefore, bool aLineBreakAfter,
+ gfxFloat* aAdvanceWidthDelta);
+
+ enum SuppressBreak {
+ eNoSuppressBreak,
+ // Measure the range of text as if there is no break before it.
+ eSuppressInitialBreak,
+ // Measure the range of text as if it contains no break
+ eSuppressAllBreaks
+ };
+
+ /**
+ * Finds the longest substring that will fit into the given width.
+ * Uses GetHyphenationBreaks and GetSpacing from aBreakProvider.
+ * Guarantees the following:
+ * -- 0 <= result <= aMaxLength
+ * -- result is the maximal value of N such that either
+ * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
+ * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
+ * OR N == aMaxLength && GetAdvanceWidth(aStart, N) <= aWidth
+ * where GetAdvanceWidth assumes the effect of
+ * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
+ * -- if no such N exists, then result is the smallest N such that
+ * N < aMaxLength && line break at N
+ * OR N < aMaxLength && hyphen break at N
+ * OR N == aMaxLength
+ *
+ * The call has the effect of
+ * SetLineBreaks(aStart, result, aLineBreakBefore, result < aMaxLength, aProvider)
+ * and the returned metrics and the invariants above reflect this.
+ *
+ * @param aMaxLength this can be UINT32_MAX, in which case the length used
+ * is up to the end of the string
+ * @param aLineBreakBefore set to true if and only if there is an actual
+ * line break at the start of this string.
+ * @param aSuppressBreak what break should be suppressed.
+ * @param aTrimWhitespace if non-null, then we allow a trailing run of
+ * spaces to be trimmed; the width of the space(s) will not be included in
+ * the measured string width for comparison with the limit aWidth, and
+ * trimmed spaces will not be included in returned metrics. The width
+ * of the trimmed spaces will be returned in aTrimWhitespace.
+ * Trimmed spaces are still counted in the "characters fit" result.
+ * @param aMetrics if non-null, we fill this in for the returned substring.
+ * If a hyphenation break was used, the hyphen is NOT included in the returned metrics.
+ * @param aBoundingBoxType whether to make the bounding box in aMetrics tight
+ * @param aDrawTargetForTightBoundingbox a reference DrawTarget to get the
+ * tight bounding box, if requested
+ * @param aUsedHyphenation if non-null, records if we selected a hyphenation break
+ * @param aLastBreak if non-null and result is aMaxLength, we set this to
+ * the maximal N such that
+ * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
+ * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
+ * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes
+ * the effect of
+ * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
+ *
+ * @param aCanWordWrap true if we can break between any two grapheme
+ * clusters. This is set by overflow-wrap|word-wrap: break-word
+ *
+ * @param aBreakPriority in/out the priority of the break opportunity
+ * saved in the line. If we are prioritizing break opportunities, we will
+ * not set a break with a lower priority. @see gfxBreakPriority.
+ *
+ * Note that negative advance widths are possible especially if negative
+ * spacing is provided.
+ */
+ uint32_t BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
+ bool aLineBreakBefore, gfxFloat aWidth,
+ PropertyProvider *aProvider,
+ SuppressBreak aSuppressBreak,
+ gfxFloat *aTrimWhitespace,
+ bool aHangWhitespace,
+ Metrics *aMetrics,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ bool *aUsedHyphenation,
+ uint32_t *aLastBreak,
+ bool aCanWordWrap,
+ gfxBreakPriority *aBreakPriority);
+
+ // Utility getters
+
+ void *GetUserData() const { return mUserData; }
+ void SetUserData(void *aUserData) { mUserData = aUserData; }
+
+ void SetFlagBits(uint32_t aFlags) {
+ NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
+ "Only user flags should be mutable");
+ mFlags |= aFlags;
+ }
+ void ClearFlagBits(uint32_t aFlags) {
+ NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
+ "Only user flags should be mutable");
+ mFlags &= ~aFlags;
+ }
+ const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
+ gfxFontGroup *GetFontGroup() const { return mFontGroup; }
+
+
+ // Call this, don't call "new gfxTextRun" directly. This does custom
+ // allocation and initialization
+ static already_AddRefed<gfxTextRun>
+ Create(const gfxTextRunFactory::Parameters *aParams,
+ uint32_t aLength, gfxFontGroup *aFontGroup,
+ uint32_t aFlags);
+
+ // The text is divided into GlyphRuns as necessary
+ struct GlyphRun {
+ RefPtr<gfxFont> mFont; // never null
+ uint32_t mCharacterOffset; // into original UTF16 string
+ uint8_t mMatchType;
+ uint16_t mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
+ };
+
+ class GlyphRunIterator {
+ public:
+ GlyphRunIterator(const gfxTextRun *aTextRun, Range aRange)
+ : mTextRun(aTextRun)
+ , mStartOffset(aRange.start)
+ , mEndOffset(aRange.end) {
+ mNextIndex = mTextRun->FindFirstGlyphRunContaining(aRange.start);
+ }
+ bool NextRun();
+ const GlyphRun *GetGlyphRun() const { return mGlyphRun; }
+ uint32_t GetStringStart() const { return mStringStart; }
+ uint32_t GetStringEnd() const { return mStringEnd; }
+ private:
+ const gfxTextRun *mTextRun;
+ MOZ_INIT_OUTSIDE_CTOR const GlyphRun *mGlyphRun;
+ MOZ_INIT_OUTSIDE_CTOR uint32_t mStringStart;
+ MOZ_INIT_OUTSIDE_CTOR uint32_t mStringEnd;
+ uint32_t mNextIndex;
+ uint32_t mStartOffset;
+ uint32_t mEndOffset;
+ };
+
+ class GlyphRunOffsetComparator {
+ public:
+ bool Equals(const GlyphRun& a,
+ const GlyphRun& b) const
+ {
+ return a.mCharacterOffset == b.mCharacterOffset;
+ }
+
+ bool LessThan(const GlyphRun& a,
+ const GlyphRun& b) const
+ {
+ return a.mCharacterOffset < b.mCharacterOffset;
+ }
+ };
+
+ friend class GlyphRunIterator;
+ friend class FontSelector;
+
+ // API for setting up the textrun glyphs. Should only be called by
+ // things that construct textruns.
+ /**
+ * We've found a run of text that should use a particular font. Call this
+ * only during initialization when font substitution has been computed.
+ * Call it before setting up the glyphs for the characters in this run;
+ * SetMissingGlyph requires that the correct glyphrun be installed.
+ *
+ * If aForceNewRun, a new glyph run will be added, even if the
+ * previously added run uses the same font. If glyph runs are
+ * added out of strictly increasing aStartCharIndex order (via
+ * force), then SortGlyphRuns must be called after all glyph runs
+ * are added before any further operations are performed with this
+ * TextRun.
+ */
+ nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
+ uint32_t aStartCharIndex, bool aForceNewRun,
+ uint16_t aOrientation);
+ void ResetGlyphRuns() { mGlyphRuns.Clear(); }
+ void SortGlyphRuns();
+ void SanitizeGlyphRuns();
+
+ const CompressedGlyph* GetCharacterGlyphs() const final {
+ MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+ return mCharacterGlyphs;
+ }
+ CompressedGlyph* GetCharacterGlyphs() final {
+ MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+ return mCharacterGlyphs;
+ }
+
+ // clean out results from shaping in progress, used for fallback scenarios
+ void ClearGlyphsAndCharacters();
+
+ void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
+ uint32_t aCharIndex, uint16_t aOrientation);
+
+ // Set the glyph data for the given character index to the font's
+ // space glyph, IF this can be done as a "simple" glyph record
+ // (not requiring a DetailedGlyph entry). This avoids the need to call
+ // the font shaper and go through the shaped-word cache for most spaces.
+ //
+ // The parameter aSpaceChar is the original character code for which
+ // this space glyph is being used; if this is U+0020, we need to record
+ // that it could be trimmed at a run edge, whereas other kinds of space
+ // (currently just U+00A0) would not be trimmable/breakable.
+ //
+ // Returns true if it was able to set simple glyph data for the space;
+ // if it returns false, the caller needs to fall back to some other
+ // means to create the necessary (detailed) glyph data.
+ bool SetSpaceGlyphIfSimple(gfxFont *aFont, uint32_t aCharIndex,
+ char16_t aSpaceChar, uint16_t aOrientation);
+
+ // Record the positions of specific characters that layout may need to
+ // detect in the textrun, even though it doesn't have an explicit copy
+ // of the original text. These are recorded using flag bits in the
+ // CompressedGlyph record; if necessary, we convert "simple" glyph records
+ // to "complex" ones as the Tab and Newline flags are not present in
+ // simple CompressedGlyph records.
+ void SetIsTab(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetIsTab();
+ }
+ void SetIsNewline(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetIsNewline();
+ }
+ void SetNoEmphasisMark(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetNoEmphasisMark();
+ }
+
+ /**
+ * Prefetch all the glyph extents needed to ensure that Measure calls
+ * on this textrun not requesting tight boundingBoxes will succeed. Note
+ * that some glyph extents might not be fetched due to OOM or other
+ * errors.
+ */
+ void FetchGlyphExtents(DrawTarget* aRefDrawTarget);
+
+ uint32_t CountMissingGlyphs() const;
+ const GlyphRun* GetGlyphRuns(uint32_t* aNumGlyphRuns) const {
+ *aNumGlyphRuns = mGlyphRuns.Length();
+ return mGlyphRuns.Elements();
+ }
+ // Returns the index of the GlyphRun containing the given offset.
+ // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
+ uint32_t FindFirstGlyphRunContaining(uint32_t aOffset) const;
+
+ // Copy glyph data from a ShapedWord into this textrun.
+ void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart);
+
+ // Copy glyph data for a range of characters from aSource to this
+ // textrun.
+ void CopyGlyphDataFrom(gfxTextRun *aSource, Range aRange, uint32_t aDest);
+
+ // Tell the textrun to release its reference to its creating gfxFontGroup
+ // immediately, rather than on destruction. This is used for textruns
+ // that are actually owned by a gfxFontGroup, so that they don't keep it
+ // permanently alive due to a circular reference. (The caller of this is
+ // taking responsibility for ensuring the textrun will not outlive its
+ // mFontGroup.)
+ void ReleaseFontGroup();
+
+ struct LigatureData {
+ // textrun range of the containing ligature
+ Range mRange;
+ // appunits advance to the start of the ligature part within the ligature;
+ // never includes any spacing
+ gfxFloat mPartAdvance;
+ // appunits width of the ligature part; includes before-spacing
+ // when the part is at the start of the ligature, and after-spacing
+ // when the part is as the end of the ligature
+ gfxFloat mPartWidth;
+
+ bool mClipBeforePart;
+ bool mClipAfterPart;
+ };
+
+ // return storage used by this run, for memory reporter;
+ // nsTransformedTextRun needs to override this as it holds additional data
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ MOZ_MUST_OVERRIDE;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ MOZ_MUST_OVERRIDE;
+
+ // Get the size, if it hasn't already been gotten, marking as it goes.
+ size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
+ if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
+ return 0;
+ }
+ mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+ return SizeOfIncludingThis(aMallocSizeOf);
+ }
+ void ResetSizeOfAccountingFlags() {
+ mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+ }
+
+ // shaping state - for some font features, fallback is required that
+ // affects the entire run. for example, fallback for one script/font
+ // portion of a textrun requires fallback to be applied to the entire run
+
+ enum ShapingState {
+ eShapingState_Normal, // default state
+ eShapingState_ShapingWithFeature, // have shaped with feature
+ eShapingState_ShapingWithFallback, // have shaped with fallback
+ eShapingState_Aborted, // abort initial iteration
+ eShapingState_ForceFallbackFeature // redo with fallback forced on
+ };
+
+ ShapingState GetShapingState() const { return mShapingState; }
+ void SetShapingState(ShapingState aShapingState) {
+ mShapingState = aShapingState;
+ }
+
+ int32_t GetAdvanceForGlyph(uint32_t aIndex) const
+ {
+ const CompressedGlyph& glyphData = mCharacterGlyphs[aIndex];
+ if (glyphData.IsSimpleGlyph()) {
+ return glyphData.GetSimpleAdvance();
+ }
+ uint32_t glyphCount = glyphData.GetGlyphCount();
+ if (!glyphCount) {
+ return 0;
+ }
+ const DetailedGlyph* details = GetDetailedGlyphs(aIndex);
+ int32_t advance = 0;
+ for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
+ advance += details->mAdvance;
+ }
+ return advance;
+ }
+
+#ifdef DEBUG
+ void Dump(FILE* aOutput);
+#endif
+
+protected:
+ /**
+ * Create a textrun, and set its mCharacterGlyphs to point immediately
+ * after the base object; this is ONLY used in conjunction with placement
+ * new, after allocating a block large enough for the glyph records to
+ * follow the base textrun object.
+ */
+ gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
+ uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags);
+
+ /**
+ * Helper for the Create() factory method to allocate the required
+ * glyph storage for a textrun object with the basic size aSize,
+ * plus room for aLength glyph records.
+ */
+ static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
+
+ // Pointer to the array of CompressedGlyph records; must be initialized
+ // when the object is constructed.
+ CompressedGlyph *mCharacterGlyphs;
+
+private:
+ // **** general helpers ****
+
+ // Get the total advance for a range of glyphs.
+ int32_t GetAdvanceForGlyphs(Range aRange) const;
+
+ // Spacing for characters outside the range aSpacingStart/aSpacingEnd
+ // is assumed to be zero; such characters are not passed to aProvider.
+ // This is useful to protect aProvider from being passed character indices
+ // it is not currently able to handle.
+ bool GetAdjustedSpacingArray(Range aRange, PropertyProvider *aProvider,
+ Range aSpacingRange,
+ nsTArray<PropertyProvider::Spacing>*
+ aSpacing) const;
+
+ CompressedGlyph& EnsureComplexGlyph(uint32_t aIndex)
+ {
+ gfxShapedText::EnsureComplexGlyph(aIndex, mCharacterGlyphs[aIndex]);
+ return mCharacterGlyphs[aIndex];
+ }
+
+ // **** ligature helpers ****
+ // (Platforms do the actual ligaturization, but we need to do a bunch of stuff
+ // to handle requests that begin or end inside a ligature)
+
+ // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
+ LigatureData ComputeLigatureData(Range aPartRange,
+ PropertyProvider *aProvider) const;
+ gfxFloat ComputePartialLigatureWidth(Range aPartRange,
+ PropertyProvider *aProvider) const;
+ void DrawPartialLigature(gfxFont *aFont, Range aRange,
+ gfxPoint *aPt, PropertyProvider *aProvider,
+ TextRunDrawParams& aParams,
+ uint16_t aOrientation) const;
+ // Advance aRange.start to the start of the nearest ligature, back
+ // up aRange.end to the nearest ligature end; may result in
+ // aRange->start == aRange->end.
+ void ShrinkToLigatureBoundaries(Range* aRange) const;
+ // result in appunits
+ gfxFloat GetPartialLigatureWidth(Range aRange,
+ PropertyProvider *aProvider) const;
+ void AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ PropertyProvider *aProvider,
+ uint16_t aOrientation,
+ Metrics *aMetrics) const;
+
+ // **** measurement helper ****
+ void AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ PropertyProvider *aProvider,
+ Range aSpacingRange,
+ uint16_t aOrientation,
+ Metrics *aMetrics) const;
+
+ // **** drawing helper ****
+ void DrawGlyphs(gfxFont *aFont, Range aRange, gfxPoint *aPt,
+ PropertyProvider *aProvider, Range aSpacingRange,
+ TextRunDrawParams& aParams, uint16_t aOrientation) const;
+
+ // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
+ // for smaller size especially in the super-common one-glyphrun case
+ AutoTArray<GlyphRun,1> mGlyphRuns;
+
+ void *mUserData;
+ gfxFontGroup *mFontGroup; // addrefed on creation, but our reference
+ // may be released by ReleaseFontGroup()
+ gfxSkipChars mSkipChars;
+
+ bool mSkipDrawing; // true if the font group we used had a user font
+ // download that's in progress, so we should hide text
+ // until the download completes (or timeout fires)
+ bool mReleasedFontGroup; // we already called NS_RELEASE on
+ // mFontGroup, so don't do it again
+
+ // shaping state for handling variant fallback features
+ // such as subscript/superscript variant glyphs
+ ShapingState mShapingState;
+};
+
+class gfxFontGroup : public gfxTextRunFactory {
+public:
+ typedef mozilla::unicode::Script Script;
+
+ static void Shutdown(); // platform must call this to release the languageAtomService
+
+ gfxFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle* aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet* aUserFontSet,
+ gfxFloat aDevToCssSize);
+
+ virtual ~gfxFontGroup();
+
+ // Returns first valid font in the fontlist or default font.
+ // Initiates userfont loads if userfont not loaded
+ virtual gfxFont* GetFirstValidFont(uint32_t aCh = 0x20);
+
+ // Returns the first font in the font-group that has an OpenType MATH table,
+ // or null if no such font is available. The GetMathConstant methods may be
+ // called on the returned font.
+ gfxFont *GetFirstMathFont();
+
+ const gfxFontStyle *GetStyle() const { return &mStyle; }
+
+ virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
+
+ /**
+ * The listed characters should be treated as invisible and zero-width
+ * when creating textruns.
+ */
+ static bool IsInvalidChar(uint8_t ch);
+ static bool IsInvalidChar(char16_t ch);
+
+ /**
+ * Make a textrun for a given string.
+ * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
+ * textrun will copy it.
+ * This calls FetchGlyphExtents on the textrun.
+ */
+ virtual already_AddRefed<gfxTextRun>
+ MakeTextRun(const char16_t *aString, uint32_t aLength,
+ const Parameters *aParams, uint32_t aFlags,
+ gfxMissingFontRecorder *aMFR);
+ /**
+ * Make a textrun for a given string.
+ * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
+ * textrun will copy it.
+ * This calls FetchGlyphExtents on the textrun.
+ */
+ virtual already_AddRefed<gfxTextRun>
+ MakeTextRun(const uint8_t *aString, uint32_t aLength,
+ const Parameters *aParams, uint32_t aFlags,
+ gfxMissingFontRecorder *aMFR);
+
+ /**
+ * Textrun creation helper for clients that don't want to pass
+ * a full Parameters record.
+ */
+ template<typename T>
+ already_AddRefed<gfxTextRun>
+ MakeTextRun(const T* aString, uint32_t aLength,
+ DrawTarget* aRefDrawTarget,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags,
+ gfxMissingFontRecorder *aMFR)
+ {
+ gfxTextRunFactory::Parameters params = {
+ aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
+ };
+ return MakeTextRun(aString, aLength, &params, aFlags, aMFR);
+ }
+
+ /**
+ * Get the (possibly-cached) width of the hyphen character.
+ * The aCtx and aAppUnitsPerDevUnit parameters will be used only if
+ * needed to initialize the cached hyphen width; otherwise they are
+ * ignored.
+ */
+ gfxFloat GetHyphenWidth(gfxTextRun::PropertyProvider* aProvider);
+
+ /**
+ * Make a text run representing a single hyphen character.
+ * This will use U+2010 HYPHEN if available in the first font,
+ * otherwise fall back to U+002D HYPHEN-MINUS.
+ * The caller is responsible for deleting the returned text run
+ * when no longer required.
+ */
+ already_AddRefed<gfxTextRun>
+ MakeHyphenTextRun(DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit);
+
+ /**
+ * Check whether a given font (specified by its gfxFontEntry)
+ * is already in the fontgroup's list of actual fonts
+ */
+ bool HasFont(const gfxFontEntry *aFontEntry);
+
+ // This returns the preferred underline for this font group.
+ // Some CJK fonts have wrong underline offset in its metrics.
+ // If this group has such "bad" font, each platform's gfxFontGroup
+ // initialized mUnderlineOffset. The value should be lower value of
+ // first font's metrics and the bad font's metrics. Otherwise, this
+ // returns from first font's metrics.
+ enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX };
+ virtual gfxFloat GetUnderlineOffset();
+
+ virtual already_AddRefed<gfxFont>
+ FindFontForChar(uint32_t ch, uint32_t prevCh, uint32_t aNextCh,
+ Script aRunScript, gfxFont *aPrevMatchedFont,
+ uint8_t *aMatchType);
+
+ gfxUserFontSet* GetUserFontSet();
+
+ // With downloadable fonts, the composition of the font group can change as fonts are downloaded
+ // for each change in state of the user font set, the generation value is bumped to avoid picking up
+ // previously created text runs in the text run word cache. For font groups based on stylesheets
+ // with no @font-face rule, this always returns 0.
+ uint64_t GetGeneration();
+
+ // generation of the latest fontset rebuild, 0 when no fontset present
+ uint64_t GetRebuildGeneration();
+
+ // used when logging text performance
+ gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; }
+
+ // This will call UpdateUserFonts() if the user font set is changed.
+ void SetUserFontSet(gfxUserFontSet *aUserFontSet);
+
+ void ClearCachedData()
+ {
+ mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
+ mSkipDrawing = false;
+ mHyphenWidth = -1;
+ mCachedEllipsisTextRun = nullptr;
+ }
+
+ // If there is a user font set, check to see whether the font list or any
+ // caches need updating.
+ virtual void UpdateUserFonts();
+
+ // search for a specific userfont in the list of fonts
+ bool ContainsUserFont(const gfxUserFontEntry* aUserFont);
+
+ bool ShouldSkipDrawing() const {
+ return mSkipDrawing;
+ }
+
+ class LazyReferenceDrawTargetGetter {
+ public:
+ virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
+ };
+ // The gfxFontGroup keeps ownership of this textrun.
+ // It is only guaranteed to exist until the next call to GetEllipsisTextRun
+ // (which might use a different appUnitsPerDev value or flags) for the font
+ // group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
+ // Get it/use it/forget it :) - don't keep a reference that might go stale.
+ gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
+ LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
+
+protected:
+ // search through pref fonts for a character, return nullptr if no matching pref font
+ already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh,
+ uint32_t aNextCh);
+
+ already_AddRefed<gfxFont>
+ WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript);
+
+ template<typename T>
+ void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
+ const T *aString, uint32_t aLength,
+ Script aRunScript, uint16_t aOrientation);
+
+ class FamilyFace {
+ public:
+ FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
+ mNeedsBold(false), mFontCreated(false),
+ mLoading(false), mInvalid(false),
+ mCheckForFallbackFaces(false)
+ { }
+
+ FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
+ : mFamily(aFamily), mNeedsBold(false), mFontCreated(true),
+ mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
+ {
+ NS_ASSERTION(aFont, "font pointer must not be null");
+ NS_ASSERTION(!aFamily ||
+ aFamily->ContainsFace(aFont->GetFontEntry()),
+ "font is not a member of the given family");
+ mFont = aFont;
+ NS_ADDREF(aFont);
+ }
+
+ FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
+ bool aNeedsBold)
+ : mFamily(aFamily), mNeedsBold(aNeedsBold), mFontCreated(false),
+ mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
+ {
+ NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
+ NS_ASSERTION(!aFamily ||
+ aFamily->ContainsFace(aFontEntry),
+ "font is not a member of the given family");
+ mFontEntry = aFontEntry;
+ NS_ADDREF(aFontEntry);
+ }
+
+ FamilyFace(const FamilyFace& aOtherFamilyFace)
+ : mFamily(aOtherFamilyFace.mFamily),
+ mNeedsBold(aOtherFamilyFace.mNeedsBold),
+ mFontCreated(aOtherFamilyFace.mFontCreated),
+ mLoading(aOtherFamilyFace.mLoading),
+ mInvalid(aOtherFamilyFace.mInvalid),
+ mCheckForFallbackFaces(aOtherFamilyFace.mCheckForFallbackFaces)
+ {
+ if (mFontCreated) {
+ mFont = aOtherFamilyFace.mFont;
+ NS_ADDREF(mFont);
+ } else {
+ mFontEntry = aOtherFamilyFace.mFontEntry;
+ NS_IF_ADDREF(mFontEntry);
+ }
+ }
+
+ ~FamilyFace()
+ {
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ } else {
+ NS_IF_RELEASE(mFontEntry);
+ }
+ }
+
+ FamilyFace& operator=(const FamilyFace& aOther)
+ {
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ } else {
+ NS_IF_RELEASE(mFontEntry);
+ }
+
+ mFamily = aOther.mFamily;
+ mNeedsBold = aOther.mNeedsBold;
+ mFontCreated = aOther.mFontCreated;
+ mLoading = aOther.mLoading;
+ mInvalid = aOther.mInvalid;
+
+ if (mFontCreated) {
+ mFont = aOther.mFont;
+ NS_ADDREF(mFont);
+ } else {
+ mFontEntry = aOther.mFontEntry;
+ NS_IF_ADDREF(mFontEntry);
+ }
+
+ return *this;
+ }
+
+ gfxFontFamily* Family() const { return mFamily.get(); }
+ gfxFont* Font() const {
+ return mFontCreated ? mFont : nullptr;
+ }
+
+ gfxFontEntry* FontEntry() const {
+ return mFontCreated ? mFont->GetFontEntry() : mFontEntry;
+ }
+
+ bool NeedsBold() const { return mNeedsBold; }
+ bool IsUserFontContainer() const {
+ return FontEntry()->mIsUserFontContainer;
+ }
+ bool IsLoading() const { return mLoading; }
+ bool IsInvalid() const { return mInvalid; }
+ void CheckState(bool& aSkipDrawing);
+ void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
+ void SetInvalid() { mInvalid = true; }
+ bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
+ void SetCheckForFallbackFaces() { mCheckForFallbackFaces = true; }
+
+ void SetFont(gfxFont* aFont)
+ {
+ NS_ASSERTION(aFont, "font pointer must not be null");
+ NS_ADDREF(aFont);
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ } else {
+ NS_IF_RELEASE(mFontEntry);
+ }
+ mFont = aFont;
+ mFontCreated = true;
+ mLoading = false;
+ }
+
+ bool EqualsUserFont(const gfxUserFontEntry* aUserFont) const;
+
+ private:
+ RefPtr<gfxFontFamily> mFamily;
+ // either a font or a font entry exists
+ union {
+ gfxFont* mFont;
+ gfxFontEntry* mFontEntry;
+ };
+ bool mNeedsBold : 1;
+ bool mFontCreated : 1;
+ bool mLoading : 1;
+ bool mInvalid : 1;
+ bool mCheckForFallbackFaces : 1;
+ };
+
+ // List of font families, either named or generic.
+ // Generic names map to system pref fonts based on language.
+ mozilla::FontFamilyList mFamilyList;
+
+ // Fontlist containing a font entry for each family found. gfxFont objects
+ // are created as needed and userfont loads are initiated when needed.
+ // Code should be careful about addressing this array directly.
+ nsTArray<FamilyFace> mFonts;
+
+ RefPtr<gfxFont> mDefaultFont;
+ gfxFontStyle mStyle;
+
+ gfxFloat mUnderlineOffset;
+ gfxFloat mHyphenWidth;
+ gfxFloat mDevToCssSize;
+
+ RefPtr<gfxUserFontSet> mUserFontSet;
+ uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed
+
+ gfxTextPerfMetrics *mTextPerf;
+
+ // Cache a textrun representing an ellipsis (useful for CSS text-overflow)
+ // at a specific appUnitsPerDevPixel size and orientation
+ RefPtr<gfxTextRun> mCachedEllipsisTextRun;
+
+ // cache the most recent pref font to avoid general pref font lookup
+ RefPtr<gfxFontFamily> mLastPrefFamily;
+ RefPtr<gfxFont> mLastPrefFont;
+ eFontPrefLang mLastPrefLang; // lang group for last pref font
+ eFontPrefLang mPageLang;
+ bool mLastPrefFirstFont; // is this the first font in the list of pref fonts for this lang group?
+
+ bool mSkipDrawing; // hide text while waiting for a font
+ // download to complete (or fallback
+ // timer to fire)
+
+ // xxx - gfxPangoFontGroup skips UpdateUserFonts
+ bool mSkipUpdateUserFonts;
+
+ /**
+ * Textrun creation short-cuts for special cases where we don't need to
+ * call a font shaper to generate glyphs.
+ */
+ already_AddRefed<gfxTextRun>
+ MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
+
+ already_AddRefed<gfxTextRun>
+ MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
+
+ already_AddRefed<gfxTextRun>
+ MakeBlankTextRun(uint32_t aLength, const Parameters *aParams,
+ uint32_t aFlags);
+
+ // Initialize the list of fonts
+ void BuildFontList();
+
+ // Get the font at index i within the fontlist.
+ // Will initiate userfont load if not already loaded.
+ // May return null if userfont not loaded or if font invalid
+ virtual gfxFont* GetFontAt(int32_t i, uint32_t aCh = 0x20);
+
+ // Whether there's a font loading for a given family in the fontlist
+ // for a given character
+ bool FontLoadingForFamily(gfxFontFamily* aFamily, uint32_t aCh) const;
+
+ // will always return a font or force a shutdown
+ gfxFont* GetDefaultFont();
+
+ // Init this font group's font metrics. If there no bad fonts, you don't need to call this.
+ // But if there are one or more bad fonts which have bad underline offset,
+ // you should call this with the *first* bad font.
+ void InitMetricsForBadFont(gfxFont* aBadFont);
+
+ // Set up the textrun glyphs for an entire text run:
+ // find script runs, and then call InitScriptRun for each
+ template<typename T>
+ void InitTextRun(DrawTarget* aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString,
+ uint32_t aLength,
+ gfxMissingFontRecorder *aMFR);
+
+ // InitTextRun helper to handle a single script run, by finding font ranges
+ // and calling each font's InitTextRun() as appropriate
+ template<typename T>
+ void InitScriptRun(DrawTarget* aDrawTarget,
+ gfxTextRun *aTextRun,
+ const T *aString,
+ uint32_t aScriptRunStart,
+ uint32_t aScriptRunEnd,
+ Script aRunScript,
+ gfxMissingFontRecorder *aMFR);
+
+ // Helper for font-matching:
+ // search all faces in a family for a fallback in cases where it's unclear
+ // whether the family might have a font for a given character
+ already_AddRefed<gfxFont>
+ FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
+ Script aRunScript);
+
+ // helper methods for looking up fonts
+
+ // lookup and add a font with a given name (i.e. *not* a generic!)
+ void AddPlatformFont(const nsAString& aName,
+ nsTArray<gfxFontFamily*>& aFamilyList);
+
+ // do style selection and add entries to list
+ void AddFamilyToFontList(gfxFontFamily* aFamily);
+
+ static nsILanguageAtomService* gLangService;
+};
+
+// A "missing font recorder" is to be used during text-run creation to keep
+// a record of any scripts encountered for which font coverage was lacking;
+// when Flush() is called, it sends a notification that front-end code can use
+// to download fonts on demand (or whatever else it wants to do).
+
+#define GFX_MISSING_FONTS_NOTIFY_PREF "gfx.missing_fonts.notify"
+
+class gfxMissingFontRecorder {
+public:
+ gfxMissingFontRecorder()
+ {
+ MOZ_COUNT_CTOR(gfxMissingFontRecorder);
+ memset(&mMissingFonts, 0, sizeof(mMissingFonts));
+ }
+
+ ~gfxMissingFontRecorder()
+ {
+#ifdef DEBUG
+ for (uint32_t i = 0; i < kNumScriptBitsWords; i++) {
+ NS_ASSERTION(mMissingFonts[i] == 0,
+ "failed to flush the missing-font recorder");
+ }
+#endif
+ MOZ_COUNT_DTOR(gfxMissingFontRecorder);
+ }
+
+ // record this script code in our mMissingFonts bitset
+ void RecordScript(mozilla::unicode::Script aScriptCode)
+ {
+ mMissingFonts[static_cast<uint32_t>(aScriptCode) >> 5] |=
+ (1 << (static_cast<uint32_t>(aScriptCode) & 0x1f));
+ }
+
+ // send a notification of any missing-scripts that have been
+ // recorded, and clear the mMissingFonts set for re-use
+ void Flush();
+
+ // forget any missing-scripts that have been recorded up to now;
+ // called before discarding a recorder we no longer care about
+ void Clear()
+ {
+ memset(&mMissingFonts, 0, sizeof(mMissingFonts));
+ }
+
+private:
+ // Number of 32-bit words needed for the missing-script flags
+ static const uint32_t kNumScriptBitsWords =
+ ((static_cast<int>(mozilla::unicode::Script::NUM_SCRIPT_CODES) + 31) / 32);
+ uint32_t mMissingFonts[kNumScriptBitsWords];
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxTypes.h b/system/graphics/thebes/gfxTypes.h
new file mode 100644
index 000000000..976da1fef
--- /dev/null
+++ b/system/graphics/thebes/gfxTypes.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TYPES_H
+#define GFX_TYPES_H
+
+#include <stdint.h>
+
+typedef struct _cairo_surface cairo_surface_t;
+typedef struct _cairo_user_data_key cairo_user_data_key_t;
+
+typedef void (*thebes_destroy_func_t) (void *data);
+
+/**
+ * Currently needs to be 'double' for Cairo compatibility. Could
+ * become 'float', perhaps, in some configurations.
+ */
+typedef double gfxFloat;
+
+/**
+ * Priority of a line break opportunity.
+ *
+ * eNoBreak The line has no break opportunities
+ * eWordWrapBreak The line has a break opportunity only within a word. With
+ * overflow-wrap|word-wrap: break-word we will break at this point only if
+ * there are no other break opportunities in the line.
+ * eNormalBreak The line has a break opportunity determined by the standard
+ * line-breaking algorithm.
+ *
+ * Future expansion: split eNormalBreak into multiple priorities, e.g.
+ * punctuation break and whitespace break (bug 389710).
+ * As and when we implement it, text-wrap: unrestricted will
+ * mean that priorities are ignored and all line-break
+ * opportunities are equal.
+ *
+ * @see gfxTextRun::BreakAndMeasureText
+ * @see nsLineLayout::NotifyOptionalBreakPosition
+ */
+enum class gfxBreakPriority {
+ eNoBreak = 0,
+ eWordWrapBreak,
+ eNormalBreak
+};
+
+enum class gfxSurfaceType {
+ Image,
+ PDF,
+ PS,
+ Xlib,
+ Xcb,
+ Glitz, // unused, but needed for cairo parity
+ Quartz,
+ Win32,
+ BeOS,
+ DirectFB, // unused, but needed for cairo parity
+ SVG,
+ OS2,
+ Win32Printing,
+ QuartzImage,
+ Script,
+ QPainter,
+ Recording,
+ VG,
+ GL,
+ DRM,
+ Tee,
+ XML,
+ Skia,
+ Subsurface,
+ Max
+};
+
+enum class gfxContentType {
+ COLOR = 0x1000,
+ ALPHA = 0x2000,
+ COLOR_ALPHA = 0x3000,
+ SENTINEL = 0xffff
+};
+
+#endif /* GFX_TYPES_H */
diff --git a/system/graphics/thebes/gfxUserFontSet.cpp b/system/graphics/thebes/gfxUserFontSet.cpp
new file mode 100644
index 000000000..585bd14d0
--- /dev/null
+++ b/system/graphics/thebes/gfxUserFontSet.cpp
@@ -0,0 +1,1415 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+
+#include "gfxUserFontSet.h"
+#include "gfxPlatform.h"
+#include "nsContentPolicyUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsNetUtil.h"
+#include "nsIJARChannel.h"
+#include "nsIProtocolHandler.h"
+#include "nsIPrincipal.h"
+#include "nsIZipReader.h"
+#include "gfxFontConstants.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/gfx/2D.h"
+#include "gfxPlatformFontList.h"
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+using namespace mozilla;
+
+mozilla::LogModule*
+gfxUserFontSet::GetUserFontsLog()
+{
+ static LazyLogModule sLog("userfonts");
+ return sLog;
+}
+
+#define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
+
+static uint64_t sFontSetGeneration = 0;
+
+// Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
+// adapted to use Mozilla allocators and to allow the final
+// memory buffer to be adopted by the client.
+class ExpandingMemoryStream : public ots::OTSStream {
+public:
+ ExpandingMemoryStream(size_t initial, size_t limit)
+ : mLength(initial), mLimit(limit), mOff(0) {
+ mPtr = moz_xmalloc(mLength);
+ }
+
+ ~ExpandingMemoryStream() {
+ free(mPtr);
+ }
+
+ // Return the buffer, resized to fit its contents (as it may have been
+ // over-allocated during growth), and give up ownership of it so the
+ // caller becomes responsible to call free() when finished with it.
+ void* forget() {
+ void* p = moz_xrealloc(mPtr, mOff);
+ mPtr = nullptr;
+ return p;
+ }
+
+ bool WriteRaw(const void* data, size_t length) {
+ if ((mOff + length > mLength) ||
+ (mLength > std::numeric_limits<size_t>::max() - mOff)) {
+ if (mLength == mLimit) {
+ return false;
+ }
+ size_t newLength = (mLength + 1) * 2;
+ if (newLength < mLength) {
+ return false;
+ }
+ if (newLength > mLimit) {
+ newLength = mLimit;
+ }
+ mPtr = moz_xrealloc(mPtr, newLength);
+ mLength = newLength;
+ return WriteRaw(data, length);
+ }
+ std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
+ mOff += length;
+ return true;
+ }
+
+ bool Seek(off_t position) {
+ if (position < 0) {
+ return false;
+ }
+ if (static_cast<size_t>(position) > mLength) {
+ return false;
+ }
+ mOff = position;
+ return true;
+ }
+
+ off_t Tell() const {
+ return mOff;
+ }
+
+private:
+ void* mPtr;
+ size_t mLength;
+ const size_t mLimit;
+ off_t mOff;
+};
+
+gfxUserFontEntry::gfxUserFontEntry(gfxUserFontSet* aFontSet,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay)
+ : gfxFontEntry(NS_LITERAL_STRING("userfont")),
+ mUserFontLoadState(STATUS_NOT_LOADED),
+ mFontDataLoadingState(NOT_LOADING),
+ mUnsupportedFormat(false),
+ mFontDisplay(aFontDisplay),
+ mLoader(nullptr),
+ mFontSet(aFontSet)
+{
+ MOZ_ASSERT(aWeight != 0,
+ "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
+ mIsUserFontContainer = true;
+ mSrcList = aFontFaceSrcList;
+ mSrcIndex = 0;
+ mWeight = aWeight;
+ mStretch = aStretch;
+ mStyle = aStyle;
+ mFeatureSettings.AppendElements(aFeatureSettings);
+ mLanguageOverride = aLanguageOverride;
+
+ if (aUnicodeRanges) {
+ mCharacterMap = new gfxCharacterMap(*aUnicodeRanges);
+ }
+}
+
+gfxUserFontEntry::~gfxUserFontEntry()
+{
+}
+
+bool
+gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay)
+{
+ return mWeight == aWeight &&
+ mStretch == aStretch &&
+ mStyle == aStyle &&
+ mFeatureSettings == aFeatureSettings &&
+ mLanguageOverride == aLanguageOverride &&
+ mSrcList == aFontFaceSrcList &&
+ mFontDisplay == aFontDisplay &&
+ ((!aUnicodeRanges && !mCharacterMap) ||
+ (aUnicodeRanges && mCharacterMap && mCharacterMap->Equals(aUnicodeRanges)));
+}
+
+gfxFont*
+gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold)
+{
+ NS_NOTREACHED("should only be creating a gfxFont"
+ " with an actual platform font entry");
+
+ // userfont entry is a container, can't create font from the container
+ return nullptr;
+}
+
+class gfxOTSContext : public ots::OTSContext {
+public:
+ explicit gfxOTSContext(gfxUserFontEntry* aUserFontEntry)
+ : mUserFontEntry(aUserFontEntry) {}
+
+ virtual ots::TableAction GetTableAction(uint32_t aTag) override {
+ // Preserve Graphite, color glyph and SVG tables
+ if (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
+ aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') ||
+ aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') ||
+ aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
+ aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
+ aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
+ return ots::TABLE_ACTION_PASSTHRU;
+ }
+ return ots::TABLE_ACTION_DEFAULT;
+ }
+
+ virtual void Message(int level, const char* format,
+ ...) MSGFUNC_FMT_ATTR override {
+ va_list va;
+ va_start(va, format);
+
+ nsCString msg;
+ msg.AppendPrintf(format, va);
+
+ va_end(va);
+
+ if (level > 0) {
+ // For warnings (rather than errors that cause the font to fail),
+ // we only report the first instance of any given message.
+ if (mWarningsIssued.Contains(msg)) {
+ return;
+ }
+ mWarningsIssued.PutEntry(msg);
+ }
+
+ mUserFontEntry->mFontSet->LogMessage(mUserFontEntry, msg.get());
+ }
+
+private:
+ gfxUserFontEntry* mUserFontEntry;
+ nsTHashtable<nsCStringHashKey> mWarningsIssued;
+};
+
+// Call the OTS library to sanitize an sfnt before attempting to use it.
+// Returns a newly-allocated block, or nullptr in case of fatal errors.
+const uint8_t*
+gfxUserFontEntry::SanitizeOpenTypeData(const uint8_t* aData,
+ uint32_t aLength,
+ uint32_t& aSaneLength,
+ gfxUserFontType aFontType)
+{
+ if (aFontType == GFX_USERFONT_UNKNOWN) {
+ aSaneLength = 0;
+ return nullptr;
+ }
+
+ uint32_t lengthHint = aLength;
+ if (aFontType == GFX_USERFONT_WOFF) {
+ lengthHint *= 2;
+ } else if (aFontType == GFX_USERFONT_WOFF2) {
+ lengthHint *= 3;
+ }
+
+ // limit output/expansion to 256MB
+ ExpandingMemoryStream output(lengthHint, 1024 * 1024 * 256);
+
+ gfxOTSContext otsContext(this);
+ if (!otsContext.Process(&output, aData, aLength)) {
+ // Failed to decode/sanitize the font, so discard it.
+ aSaneLength = 0;
+ return nullptr;
+ }
+
+ aSaneLength = output.Tell();
+ return static_cast<const uint8_t*>(output.forget());
+}
+
+void
+gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
+ bool aPrivate,
+ const nsAString& aOriginalName,
+ FallibleTArray<uint8_t>* aMetadata,
+ uint32_t aMetaOrigLen,
+ uint8_t aCompression)
+{
+ if (!aFontEntry->mUserFontData) {
+ aFontEntry->mUserFontData = MakeUnique<gfxUserFontData>();
+ }
+ gfxUserFontData* userFontData = aFontEntry->mUserFontData.get();
+ userFontData->mSrcIndex = mSrcIndex;
+ const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
+ switch (src.mSourceType) {
+ case gfxFontFaceSrc::eSourceType_Local:
+ userFontData->mLocalName = src.mLocalName;
+ break;
+ case gfxFontFaceSrc::eSourceType_URL:
+ userFontData->mURI = src.mURI;
+ userFontData->mPrincipal = mPrincipal;
+ break;
+ case gfxFontFaceSrc::eSourceType_Buffer:
+ userFontData->mIsBuffer = true;
+ break;
+ }
+ userFontData->mPrivate = aPrivate;
+ userFontData->mFormat = src.mFormatFlags;
+ userFontData->mRealName = aOriginalName;
+ if (aMetadata) {
+ userFontData->mMetadata.SwapElements(*aMetadata);
+ userFontData->mMetaOrigLen = aMetaOrigLen;
+ userFontData->mCompression = aCompression;
+ }
+}
+
+size_t
+gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this)
+ + mMetadata.ShallowSizeOfExcludingThis(aMallocSizeOf)
+ + mLocalName.SizeOfExcludingThisIfUnshared(aMallocSizeOf)
+ + mRealName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ // Not counting mURI and mPrincipal, as those will be shared.
+}
+
+void
+gfxUserFontEntry::GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
+ nsACString& aURI)
+{
+ aFamilyName.Assign(NS_ConvertUTF16toUTF8(mFamilyName));
+
+ aURI.Truncate();
+ if (mSrcIndex == mSrcList.Length()) {
+ aURI.AppendLiteral("(end of source list)");
+ } else {
+ if (mSrcList[mSrcIndex].mURI) {
+ mSrcList[mSrcIndex].mURI->GetSpec(aURI);
+ } else {
+ aURI.AppendLiteral("(invalid URI)");
+ }
+ }
+}
+
+struct WOFFHeader {
+ AutoSwap_PRUint32 signature;
+ AutoSwap_PRUint32 flavor;
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint16 numTables;
+ AutoSwap_PRUint16 reserved;
+ AutoSwap_PRUint32 totalSfntSize;
+ AutoSwap_PRUint16 majorVersion;
+ AutoSwap_PRUint16 minorVersion;
+ AutoSwap_PRUint32 metaOffset;
+ AutoSwap_PRUint32 metaCompLen;
+ AutoSwap_PRUint32 metaOrigLen;
+ AutoSwap_PRUint32 privOffset;
+ AutoSwap_PRUint32 privLen;
+};
+
+struct WOFF2Header {
+ AutoSwap_PRUint32 signature;
+ AutoSwap_PRUint32 flavor;
+ AutoSwap_PRUint32 length;
+ AutoSwap_PRUint16 numTables;
+ AutoSwap_PRUint16 reserved;
+ AutoSwap_PRUint32 totalSfntSize;
+ AutoSwap_PRUint32 totalCompressedSize;
+ AutoSwap_PRUint16 majorVersion;
+ AutoSwap_PRUint16 minorVersion;
+ AutoSwap_PRUint32 metaOffset;
+ AutoSwap_PRUint32 metaCompLen;
+ AutoSwap_PRUint32 metaOrigLen;
+ AutoSwap_PRUint32 privOffset;
+ AutoSwap_PRUint32 privLen;
+};
+
+template<typename HeaderT>
+void
+CopyWOFFMetadata(const uint8_t* aFontData,
+ uint32_t aLength,
+ FallibleTArray<uint8_t>* aMetadata,
+ uint32_t* aMetaOrigLen)
+{
+ // This function may be called with arbitrary, unvalidated "font" data
+ // from @font-face, so it needs to be careful to bounds-check, etc.,
+ // before trying to read anything.
+ // This just saves a copy of the compressed data block; it does NOT check
+ // that the block can be successfully decompressed, or that it contains
+ // well-formed/valid XML metadata.
+ if (aLength < sizeof(HeaderT)) {
+ return;
+ }
+ const HeaderT* woff =
+ reinterpret_cast<const HeaderT*>(aFontData);
+ uint32_t metaOffset = woff->metaOffset;
+ uint32_t metaCompLen = woff->metaCompLen;
+ if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
+ return;
+ }
+ if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
+ return;
+ }
+ if (!aMetadata->SetLength(woff->metaCompLen, fallible)) {
+ return;
+ }
+ memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
+ *aMetaOrigLen = woff->metaOrigLen;
+}
+
+void
+gfxUserFontEntry::LoadNextSrc()
+{
+ uint32_t numSrc = mSrcList.Length();
+
+ NS_ASSERTION(mSrcIndex < numSrc,
+ "already at the end of the src list for user font");
+ NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
+ mUserFontLoadState == STATUS_LOADING) &&
+ mFontDataLoadingState < LOADING_FAILED,
+ "attempting to load a font that has either completed or failed");
+
+ if (mUserFontLoadState == STATUS_NOT_LOADED) {
+ SetLoadState(STATUS_LOADING);
+ mFontDataLoadingState = LOADING_STARTED;
+ mUnsupportedFormat = false;
+ } else {
+ // we were already loading; move to the next source,
+ // but don't reset state - if we've already timed out,
+ // that counts against the new download
+ mSrcIndex++;
+ }
+
+ // load each src entry in turn, until a local face is found
+ // or a download begins successfully
+ while (mSrcIndex < numSrc) {
+ gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
+
+ // src local ==> lookup and load immediately
+
+ if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
+ // Don't look up local fonts if the font whitelist is being used.
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+ gfxFontEntry* fe = pfl && pfl->IsFontFamilyWhitelistActive() ?
+ nullptr :
+ gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
+ mWeight,
+ mStretch,
+ mStyle);
+ nsTArray<gfxUserFontSet*> fontSets;
+ GetUserFontSets(fontSets);
+ for (gfxUserFontSet* fontSet : fontSets) {
+ // We need to note on each gfxUserFontSet that contains the user
+ // font entry that we used a local() rule.
+ fontSet->SetLocalRulesUsed();
+ }
+ if (fe) {
+ LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
+ mFontSet, mSrcIndex,
+ NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get(),
+ uint32_t(mFontSet->mGeneration)));
+ fe->mFeatureSettings.AppendElements(mFeatureSettings);
+ fe->mLanguageOverride = mLanguageOverride;
+ fe->mFamilyName = mFamilyName;
+ // For src:local(), we don't care whether the request is from
+ // a private window as there's no issue of caching resources;
+ // local fonts are just available all the time.
+ StoreUserFontData(fe, false, nsString(), nullptr, 0,
+ gfxUserFontData::kUnknownCompression);
+ mPlatformFontEntry = fe;
+ SetLoadState(STATUS_LOADED);
+ return;
+ } else {
+ LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
+ mFontSet, mSrcIndex,
+ NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get()));
+ }
+ }
+
+ // src url ==> start the load process
+ else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
+ if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
+ currSrc.mFormatFlags)) {
+
+ nsIPrincipal* principal = nullptr;
+ bool bypassCache;
+ nsresult rv = mFontSet->CheckFontLoad(&currSrc, &principal,
+ &bypassCache);
+
+ if (NS_SUCCEEDED(rv) && principal != nullptr) {
+ if (!bypassCache) {
+ // see if we have an existing entry for this source
+ gfxFontEntry* fe = gfxUserFontSet::
+ UserFontCache::GetFont(currSrc.mURI,
+ principal,
+ this,
+ mFontSet->GetPrivateBrowsing());
+ if (fe) {
+ mPlatformFontEntry = fe;
+ SetLoadState(STATUS_LOADED);
+ if (LOG_ENABLED()) {
+ LOG(("userfonts (%p) [src %d] "
+ "loaded uri from cache: (%s) for (%s)\n",
+ mFontSet, mSrcIndex,
+ currSrc.mURI->GetSpecOrDefault().get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get()));
+ }
+ return;
+ }
+ }
+
+ // record the principal returned by CheckFontLoad,
+ // for use when creating a channel
+ // and when caching the loaded entry
+ mPrincipal = principal;
+
+ bool loadDoesntSpin = false;
+ rv = NS_URIChainHasFlags(currSrc.mURI,
+ nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
+ &loadDoesntSpin);
+
+ if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
+ uint8_t* buffer = nullptr;
+ uint32_t bufferLength = 0;
+
+ // sync load font immediately
+ rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
+ bufferLength);
+
+ if (NS_SUCCEEDED(rv) &&
+ LoadPlatformFont(buffer, bufferLength)) {
+ SetLoadState(STATUS_LOADED);
+ return;
+ } else {
+ mFontSet->LogMessage(this,
+ "font load failed",
+ nsIScriptError::errorFlag,
+ rv);
+ }
+
+ } else {
+ // otherwise load font async
+ rv = mFontSet->StartLoad(this, &currSrc);
+ bool loadOK = NS_SUCCEEDED(rv);
+
+ if (loadOK) {
+ if (LOG_ENABLED()) {
+ LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
+ mFontSet, mSrcIndex,
+ currSrc.mURI->GetSpecOrDefault().get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get()));
+ }
+ return;
+ } else {
+ mFontSet->LogMessage(this,
+ "download failed",
+ nsIScriptError::errorFlag,
+ rv);
+ }
+ }
+ } else {
+ mFontSet->LogMessage(this, "download not allowed",
+ nsIScriptError::errorFlag, rv);
+ }
+ } else {
+ // We don't log a warning to the web console yet,
+ // as another source may load successfully
+ mUnsupportedFormat = true;
+ }
+ }
+
+ // FontFace buffer ==> load immediately
+
+ else {
+ MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer);
+
+ uint8_t* buffer = nullptr;
+ uint32_t bufferLength = 0;
+
+ // sync load font immediately
+ currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
+ if (buffer && LoadPlatformFont(buffer, bufferLength)) {
+ // LoadPlatformFont takes ownership of the buffer, so no need
+ // to free it here.
+ SetLoadState(STATUS_LOADED);
+ return;
+ } else {
+ mFontSet->LogMessage(this,
+ "font load failed",
+ nsIScriptError::errorFlag);
+ }
+ }
+
+ mSrcIndex++;
+ }
+
+ if (mUnsupportedFormat) {
+ mFontSet->LogMessage(this, "no supported format found",
+ nsIScriptError::warningFlag);
+ }
+
+ // all src's failed; mark this entry as unusable (so fallback will occur)
+ LOG(("userfonts (%p) failed all src for (%s)\n",
+ mFontSet, NS_ConvertUTF16toUTF8(mFamilyName).get()));
+ mFontDataLoadingState = LOADING_FAILED;
+ SetLoadState(STATUS_FAILED);
+}
+
+void
+gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState)
+{
+ mUserFontLoadState = aLoadState;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)
+
+bool
+gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
+{
+ NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
+ mUserFontLoadState == STATUS_LOADING) &&
+ mFontDataLoadingState < LOADING_FAILED,
+ "attempting to load a font that has either completed or failed");
+
+ gfxFontEntry* fe = nullptr;
+
+ gfxUserFontType fontType =
+ gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+
+ // Unwrap/decompress/sanitize or otherwise munge the downloaded data
+ // to make a usable sfnt structure.
+
+ // Because platform font activation code may replace the name table
+ // in the font with a synthetic one, we save the original name so that
+ // it can be reported via the nsIDOMFontFace API.
+ nsAutoString originalFullName;
+
+ // Call the OTS sanitizer; this will also decode WOFF to sfnt
+ // if necessary. The original data in aFontData is left unchanged.
+ uint32_t saneLen;
+ uint32_t fontCompressionRatio = 0;
+ size_t computedSize = 0;
+ const uint8_t* saneData =
+ SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType);
+ if (!saneData) {
+ mFontSet->LogMessage(this, "rejected by sanitizer");
+ } else {
+ // Check whether saneData is a known OpenType format; it might be
+ // a TrueType Collection, which OTS would accept but we don't yet
+ // know how to handle. If so, discard.
+ if (gfxFontUtils::DetermineFontDataType(saneData, saneLen) !=
+ GFX_USERFONT_OPENTYPE) {
+ mFontSet->LogMessage(this, "not a supported OpenType format");
+ free((void*)saneData);
+ saneData = nullptr;
+ }
+ }
+ if (saneData) {
+ if (saneLen) {
+ fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5);
+ }
+
+ // The sanitizer ensures that we have a valid sfnt and a usable
+ // name table, so this should never fail unless we're out of
+ // memory, and GetFullNameFromSFNT is not directly exposed to
+ // arbitrary/malicious data from the web.
+ gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
+ originalFullName);
+
+ // Record size for memory reporting purposes. We measure this now
+ // because by the time we potentially want to collect reports, this
+ // data block may have been handed off to opaque OS font APIs that
+ // don't allow us to retrieve or measure it directly.
+ // The *OnAlloc function will also tell DMD about this block, as the
+ // OS font code may hold on to it for an extended period.
+ computedSize = UserFontMallocSizeOfOnAlloc(saneData);
+
+ // Here ownership of saneData is passed to the platform,
+ // which will delete it when no longer required
+ fe = gfxPlatform::GetPlatform()->MakePlatformFont(mName,
+ mWeight,
+ mStretch,
+ mStyle,
+ saneData,
+ saneLen);
+ if (!fe) {
+ mFontSet->LogMessage(this, "not usable by platform");
+ }
+ }
+
+ if (fe) {
+ fe->mComputedSizeOfUserFont = computedSize;
+
+ // Save a copy of the metadata block (if present) for nsIDOMFontFace
+ // to use if required. Ownership of the metadata block will be passed
+ // to the gfxUserFontData record below.
+ FallibleTArray<uint8_t> metadata;
+ uint32_t metaOrigLen = 0;
+ uint8_t compression = gfxUserFontData::kUnknownCompression;
+ if (fontType == GFX_USERFONT_WOFF) {
+ CopyWOFFMetadata<WOFFHeader>(aFontData, aLength,
+ &metadata, &metaOrigLen);
+ compression = gfxUserFontData::kZlibCompression;
+ } else if (fontType == GFX_USERFONT_WOFF2) {
+ CopyWOFFMetadata<WOFF2Header>(aFontData, aLength,
+ &metadata, &metaOrigLen);
+ compression = gfxUserFontData::kBrotliCompression;
+ }
+
+ // copy OpenType feature/language settings from the userfont entry to the
+ // newly-created font entry
+ fe->mFeatureSettings.AppendElements(mFeatureSettings);
+ fe->mLanguageOverride = mLanguageOverride;
+ fe->mFamilyName = mFamilyName;
+ StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
+ &metadata, metaOrigLen, compression);
+ if (LOG_ENABLED()) {
+ LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
+ "(%p) gen: %8.8x compress: %d%%\n",
+ mFontSet, mSrcIndex,
+ mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get(),
+ this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
+ }
+ mPlatformFontEntry = fe;
+ SetLoadState(STATUS_LOADED);
+ gfxUserFontSet::UserFontCache::CacheFont(fe);
+ } else {
+ if (LOG_ENABLED()) {
+ LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
+ " error making platform font\n",
+ mFontSet, mSrcIndex,
+ mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
+ NS_ConvertUTF16toUTF8(mFamilyName).get()));
+ }
+ }
+
+ // The downloaded data can now be discarded; the font entry is using the
+ // sanitized copy
+ free((void*)aFontData);
+
+ return fe != nullptr;
+}
+
+void
+gfxUserFontEntry::Load()
+{
+ if (mUserFontLoadState == STATUS_NOT_LOADED) {
+ LoadNextSrc();
+ }
+}
+
+void
+gfxUserFontEntry::IncrementGeneration()
+{
+ nsTArray<gfxUserFontSet*> fontSets;
+ GetUserFontSets(fontSets);
+ for (gfxUserFontSet* fontSet : fontSets) {
+ fontSet->IncrementGeneration();
+ }
+}
+
+// This is called when a font download finishes.
+// Ownership of aFontData passes in here, and the font set must
+// ensure that it is eventually deleted via free().
+bool
+gfxUserFontEntry::FontDataDownloadComplete(const uint8_t* aFontData,
+ uint32_t aLength,
+ nsresult aDownloadStatus)
+{
+ // forget about the loader, as we no longer potentially need to cancel it
+ // if the entry is obsoleted
+ mLoader = nullptr;
+
+ // download successful, make platform font using font data
+ if (NS_SUCCEEDED(aDownloadStatus) &&
+ mFontDataLoadingState != LOADING_TIMED_OUT) {
+ bool loaded = LoadPlatformFont(aFontData, aLength);
+ aFontData = nullptr;
+
+ if (loaded) {
+ IncrementGeneration();
+ return true;
+ }
+
+ } else {
+ // download failed
+ mFontSet->LogMessage(this,
+ (mFontDataLoadingState != LOADING_TIMED_OUT ?
+ "download failed" : "download timed out"),
+ nsIScriptError::errorFlag,
+ aDownloadStatus);
+ }
+
+ if (aFontData) {
+ free((void*)aFontData);
+ }
+
+ // error occurred, load next src if load not yet timed out
+ if (mFontDataLoadingState != LOADING_TIMED_OUT) {
+ LoadNextSrc();
+ }
+
+ // We ignore the status returned by LoadNext();
+ // even if loading failed, we need to bump the font-set generation
+ // and return true in order to trigger reflow, so that fallback
+ // will be used where the text was "masked" by the pending download
+ IncrementGeneration();
+ return true;
+}
+
+void
+gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult)
+{
+ aResult.Clear();
+ aResult.AppendElement(mFontSet);
+}
+
+gfxUserFontSet::gfxUserFontSet()
+ : mFontFamilies(4),
+ mLocalRulesUsed(false),
+ mRebuildLocalRules(false),
+ mDownloadCount(0),
+ mDownloadSize(0)
+{
+ IncrementGeneration(true);
+ gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
+ if (fp) {
+ fp->AddUserFontSet(this);
+ }
+}
+
+gfxUserFontSet::~gfxUserFontSet()
+{
+ gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
+ if (fp) {
+ fp->RemoveUserFontSet(this);
+ }
+}
+
+already_AddRefed<gfxUserFontEntry>
+gfxUserFontSet::FindOrCreateUserFontEntry(
+ const nsAString& aFamilyName,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay)
+{
+ RefPtr<gfxUserFontEntry> entry;
+
+ // If there's already a userfont entry in the family whose descriptors all match,
+ // we can just move it to the end of the list instead of adding a new
+ // face that will always "shadow" the old one.
+ // Note that we can't do this for platform font entries, even if the
+ // style descriptors match, as they might have had a different source list,
+ // but we no longer have the old source list available to check.
+ gfxUserFontFamily* family = LookupFamily(aFamilyName);
+ if (family) {
+ entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aWeight,
+ aStretch, aStyle,
+ aFeatureSettings, aLanguageOverride,
+ aUnicodeRanges, aFontDisplay);
+ }
+
+ if (!entry) {
+ entry = CreateUserFontEntry(aFontFaceSrcList, aWeight, aStretch,
+ aStyle, aFeatureSettings,
+ aLanguageOverride, aUnicodeRanges,
+ aFontDisplay);
+ entry->mFamilyName = aFamilyName;
+ }
+
+ return entry.forget();
+}
+
+already_AddRefed<gfxUserFontEntry>
+gfxUserFontSet::CreateUserFontEntry(
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay)
+{
+
+ RefPtr<gfxUserFontEntry> userFontEntry =
+ new gfxUserFontEntry(this, aFontFaceSrcList, aWeight,
+ aStretch, aStyle, aFeatureSettings,
+ aLanguageOverride, aUnicodeRanges, aFontDisplay);
+ return userFontEntry.forget();
+}
+
+gfxUserFontEntry*
+gfxUserFontSet::FindExistingUserFontEntry(
+ gfxUserFontFamily* aFamily,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay)
+{
+ MOZ_ASSERT(aWeight != 0,
+ "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
+
+ nsTArray<RefPtr<gfxFontEntry>>& fontList = aFamily->GetFontList();
+
+ for (size_t i = 0, count = fontList.Length(); i < count; i++) {
+ if (!fontList[i]->mIsUserFontContainer) {
+ continue;
+ }
+
+ gfxUserFontEntry* existingUserFontEntry =
+ static_cast<gfxUserFontEntry*>(fontList[i].get());
+ if (!existingUserFontEntry->Matches(aFontFaceSrcList,
+ aWeight, aStretch, aStyle,
+ aFeatureSettings, aLanguageOverride,
+ aUnicodeRanges, aFontDisplay)) {
+ continue;
+ }
+
+ return existingUserFontEntry;
+ }
+
+ return nullptr;
+}
+
+void
+gfxUserFontSet::AddUserFontEntry(const nsAString& aFamilyName,
+ gfxUserFontEntry* aUserFontEntry)
+{
+ gfxUserFontFamily* family = GetFamily(aFamilyName);
+ family->AddFontEntry(aUserFontEntry);
+
+ if (LOG_ENABLED()) {
+ LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %d "
+ "stretch: %d display: %d",
+ this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
+ (aUserFontEntry->IsItalic() ? "italic" :
+ (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
+ aUserFontEntry->Weight(), aUserFontEntry->Stretch(),
+ aUserFontEntry->GetFontDisplay()));
+ }
+}
+
+gfxUserFontEntry*
+gfxUserFontSet::FindUserFontEntryAndLoad(gfxFontFamily* aFamily,
+ const gfxFontStyle& aFontStyle,
+ bool& aNeedsBold,
+ bool& aWaitForUserFont)
+{
+ aWaitForUserFont = false;
+ gfxFontEntry* fe = aFamily->FindFontForStyle(aFontStyle, aNeedsBold);
+ NS_ASSERTION(!fe || fe->mIsUserFontContainer,
+ "should only have userfont entries in userfont families");
+ if (!fe) {
+ return nullptr;
+ }
+
+ gfxUserFontEntry* userFontEntry = static_cast<gfxUserFontEntry*>(fe);
+
+ // start the load if it hasn't been loaded
+ userFontEntry->Load();
+ if (userFontEntry->GetPlatformFontEntry()) {
+ return userFontEntry;
+ }
+
+ aWaitForUserFont = userFontEntry->WaitForUserFont();
+ return nullptr;
+}
+
+void
+gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
+{
+ // add one, increment again if zero
+ ++sFontSetGeneration;
+ if (sFontSetGeneration == 0)
+ ++sFontSetGeneration;
+ mGeneration = sFontSetGeneration;
+ if (aIsRebuild) {
+ mRebuildGeneration = mGeneration;
+ }
+}
+
+void
+gfxUserFontSet::RebuildLocalRules()
+{
+ if (mLocalRulesUsed) {
+ mRebuildLocalRules = true;
+ DoRebuildUserFontSet();
+ }
+}
+
+gfxUserFontFamily*
+gfxUserFontSet::LookupFamily(const nsAString& aFamilyName) const
+{
+ nsAutoString key(aFamilyName);
+ ToLowerCase(key);
+
+ return mFontFamilies.GetWeak(key);
+}
+
+bool
+gfxUserFontSet::ContainsUserFontSetFonts(const FontFamilyList& aFontList) const
+{
+ for (const FontFamilyName& name : aFontList.GetFontlist()) {
+ if (name.mType != eFamily_named &&
+ name.mType != eFamily_named_quoted) {
+ continue;
+ }
+ if (LookupFamily(name.mName)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+gfxUserFontFamily*
+gfxUserFontSet::GetFamily(const nsAString& aFamilyName)
+{
+ nsAutoString key(aFamilyName);
+ ToLowerCase(key);
+
+ gfxUserFontFamily* family = mFontFamilies.GetWeak(key);
+ if (!family) {
+ family = new gfxUserFontFamily(aFamilyName);
+ mFontFamilies.Put(key, family);
+ }
+ return family;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
+// across pages/fontsets rather than instantiating new platform fonts.
+//
+// Entries are added to this cache when a platform font is instantiated from
+// downloaded data, and removed when the platform font entry is destroyed.
+// We don't need to use a timed expiration scheme here because the gfxFontEntry
+// for a downloaded font will be kept alive by its corresponding gfxFont
+// instance(s) until they are deleted, and *that* happens using an expiration
+// tracker (gfxFontCache). The result is that the downloaded font instances
+// recorded here will persist between pages and can get reused (provided the
+// source URI and principal match, of course).
+///////////////////////////////////////////////////////////////////////////////
+
+nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
+ gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
+
+NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
+
+NS_IMETHODIMP
+gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!sUserFonts) {
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, "cacheservice:empty-cache")) {
+ for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
+ if (!i.Get()->IsPersistent()) {
+ i.Remove();
+ }
+ }
+ } else if (!strcmp(aTopic, "last-pb-context-exited")) {
+ for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
+ if (i.Get()->IsPrivate()) {
+ i.Remove();
+ }
+ }
+ } else if (!strcmp(aTopic, "xpcom-shutdown")) {
+ for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
+ i.Get()->GetFontEntry()->DisconnectSVG();
+ }
+ } else {
+ NS_NOTREACHED("unexpected topic");
+ }
+
+ return NS_OK;
+}
+
+static bool
+IgnorePrincipal(nsIURI* aURI)
+{
+ nsresult rv;
+ bool inherits = false;
+ rv = NS_URIChainHasFlags(aURI,
+ nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+ &inherits);
+ return NS_SUCCEEDED(rv) && inherits;
+}
+
+bool
+gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
+{
+ const gfxFontEntry* fe = aKey->mFontEntry;
+ // CRC32 checking mode
+ if (mLength || aKey->mLength) {
+ if (aKey->mLength != mLength ||
+ aKey->mCRC32 != mCRC32) {
+ return false;
+ }
+ } else {
+ bool result;
+ if (NS_FAILED(mURI->Equals(aKey->mURI, &result)) || !result) {
+ return false;
+ }
+
+ // For data: URIs, we don't care about the principal; otherwise, check it.
+ if (!IgnorePrincipal(mURI)) {
+ NS_ASSERTION(mPrincipal && aKey->mPrincipal,
+ "only data: URIs are allowed to omit the principal");
+ if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &result)) ||
+ !result) {
+ return false;
+ }
+ }
+
+ if (mPrivate != aKey->mPrivate) {
+ return false;
+ }
+ }
+
+ if (mFontEntry->mStyle != fe->mStyle ||
+ mFontEntry->mWeight != fe->mWeight ||
+ mFontEntry->mStretch != fe->mStretch ||
+ mFontEntry->mFeatureSettings != fe->mFeatureSettings ||
+ mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
+ mFontEntry->mFamilyName != fe->mFamilyName) {
+ return false;
+ }
+
+ return true;
+}
+
+void
+gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry,
+ EntryPersistence aPersistence)
+{
+ NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
+ "caching a font associated with no family yet");
+
+ // if caching is disabled, simply return
+ if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
+ return;
+ }
+
+ gfxUserFontData* data = aFontEntry->mUserFontData.get();
+ if (data->mIsBuffer) {
+#ifdef DEBUG_USERFONT_CACHE
+ printf("userfontcache skipped fontentry with buffer source: %p\n",
+ aFontEntry);
+#endif
+ return;
+ }
+
+ if (!sUserFonts) {
+ sUserFonts = new nsTHashtable<Entry>;
+
+ nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService();
+ if (obs) {
+ Flusher* flusher = new Flusher;
+ obs->AddObserver(flusher, "cacheservice:empty-cache",
+ false);
+ obs->AddObserver(flusher, "last-pb-context-exited", false);
+ obs->AddObserver(flusher, "xpcom-shutdown", false);
+ }
+
+ // Create and register a memory reporter for sUserFonts.
+ // This reporter is never unregistered, but that's OK because
+ // the reporter checks whether sUserFonts is null, so it would
+ // be safe to call even after UserFontCache::Shutdown has deleted
+ // the cache.
+ RegisterStrongMemoryReporter(new MemoryReporter());
+ }
+
+ if (data->mLength) {
+ MOZ_ASSERT(aPersistence == kPersistent);
+ MOZ_ASSERT(!data->mPrivate);
+ sUserFonts->PutEntry(Key(data->mCRC32, data->mLength, aFontEntry,
+ data->mPrivate, aPersistence));
+ } else {
+ MOZ_ASSERT(aPersistence == kDiscardable);
+ // For data: URIs, the principal is ignored; anyone who has the same
+ // data: URI is able to load it and get an equivalent font.
+ // Otherwise, the principal is used as part of the cache key.
+ nsIPrincipal* principal;
+ if (IgnorePrincipal(data->mURI)) {
+ principal = nullptr;
+ } else {
+ principal = data->mPrincipal;
+ }
+ sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry,
+ data->mPrivate, aPersistence));
+ }
+
+#ifdef DEBUG_USERFONT_CACHE
+ printf("userfontcache added fontentry: %p\n", aFontEntry);
+ Dump();
+#endif
+}
+
+void
+gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry)
+{
+ if (!sUserFonts) {
+ // if we've already deleted the cache (i.e. during shutdown),
+ // just ignore this
+ return;
+ }
+
+ // We can't simply use RemoveEntry here because it's possible the principal
+ // may have changed since the font was cached, in which case the lookup
+ // would no longer find the entry (bug 838105).
+ for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
+ if (i.Get()->GetFontEntry() == aFontEntry) {
+ i.Remove();
+ }
+ }
+
+#ifdef DEBUG_USERFONT_CACHE
+ printf("userfontcache removed fontentry: %p\n", aFontEntry);
+ Dump();
+#endif
+}
+
+gfxFontEntry*
+gfxUserFontSet::UserFontCache::GetFont(nsIURI* aSrcURI,
+ nsIPrincipal* aPrincipal,
+ gfxUserFontEntry* aUserFontEntry,
+ bool aPrivate)
+{
+ if (!sUserFonts ||
+ Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
+ return nullptr;
+ }
+
+ // We have to perform another content policy check here to prevent
+ // cache poisoning. E.g. a.com loads a font into the cache but
+ // b.com has a CSP not allowing any fonts to be loaded.
+ if (!aUserFontEntry->mFontSet->IsFontLoadAllowed(aSrcURI, aPrincipal)) {
+ return nullptr;
+ }
+
+ // Ignore principal when looking up a data: URI.
+ nsIPrincipal* principal;
+ if (IgnorePrincipal(aSrcURI)) {
+ principal = nullptr;
+ } else {
+ principal = aPrincipal;
+ }
+
+ Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, principal, aUserFontEntry,
+ aPrivate));
+ if (entry) {
+ return entry->GetFontEntry();
+ }
+
+ // The channel is never openend; to be conservative we use the most
+ // restrictive security flag: SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS.
+ nsCOMPtr<nsIChannel> chan;
+ if (NS_FAILED(NS_NewChannel(getter_AddRefs(chan),
+ aSrcURI,
+ aPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
+ nsIContentPolicy::TYPE_FONT))) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIJARChannel> jarchan = do_QueryInterface(chan);
+ if (!jarchan) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIZipEntry> zipentry;
+ if (NS_FAILED(jarchan->GetZipEntry(getter_AddRefs(zipentry)))) {
+ return nullptr;
+ }
+
+ uint32_t crc32, length;
+ zipentry->GetCRC32(&crc32);
+ zipentry->GetRealSize(&length);
+
+ entry = sUserFonts->GetEntry(Key(crc32, length, aUserFontEntry, aPrivate));
+ if (entry) {
+ return entry->GetFontEntry();
+ }
+
+ return nullptr;
+}
+
+void
+gfxUserFontSet::UserFontCache::Shutdown()
+{
+ if (sUserFonts) {
+ delete sUserFonts;
+ sUserFonts = nullptr;
+ }
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
+
+void
+gfxUserFontSet::UserFontCache::Entry::ReportMemory(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
+{
+ MOZ_ASSERT(mFontEntry);
+ nsAutoCString path("explicit/gfx/user-fonts/font(");
+
+ if (aAnonymize) {
+ path.AppendPrintf("<anonymized-%p>", this);
+ } else {
+ NS_ConvertUTF16toUTF8 familyName(mFontEntry->mFamilyName);
+ path.AppendPrintf("family=%s", familyName.get());
+ if (mURI) {
+ nsCString spec = mURI->GetSpecOrDefault();
+ spec.ReplaceChar('/', '\\');
+ // Some fonts are loaded using horrendously-long data: URIs;
+ // truncate those before reporting them.
+ bool isData;
+ if (NS_SUCCEEDED(mURI->SchemeIs("data", &isData)) && isData &&
+ spec.Length() > 255) {
+ spec.Truncate(252);
+ spec.Append("...");
+ }
+ path.AppendPrintf(", url=%s", spec.get());
+ }
+ if (mPrincipal) {
+ nsCOMPtr<nsIURI> uri;
+ mPrincipal->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ nsCString spec = uri->GetSpecOrDefault();
+ if (!spec.IsEmpty()) {
+ // Include a clue as to who loaded this resource. (Note
+ // that because of font entry sharing, other pages may now
+ // be using this resource, and the original page may not
+ // even be loaded any longer.)
+ spec.ReplaceChar('/', '\\');
+ path.AppendPrintf(", principal=%s", spec.get());
+ }
+ }
+ }
+ }
+ path.Append(')');
+
+ aHandleReport->Callback(
+ EmptyCString(), path,
+ nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
+ mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf),
+ NS_LITERAL_CSTRING("Memory used by @font-face resource."),
+ aData);
+}
+
+NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,
+ nsIMemoryReporter)
+
+NS_IMETHODIMP
+gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
+{
+ if (!sUserFonts) {
+ return NS_OK;
+ }
+
+ for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
+ it.Get()->ReportMemory(aHandleReport, aData, aAnonymize);
+ }
+
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP, UNITS_BYTES,
+ sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf),
+ "Memory used by the @font-face cache, not counting the actual font "
+ "resources.");
+
+ return NS_OK;
+}
+
+#ifdef DEBUG_USERFONT_CACHE
+
+void
+gfxUserFontSet::UserFontCache::Entry::Dump()
+{
+ nsresult rv;
+
+ nsAutoCString principalURISpec("(null)");
+ bool setDomain = false;
+
+ if (mPrincipal) {
+ nsCOMPtr<nsIURI> principalURI;
+ rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
+ if (NS_SUCCEEDED(rv)) {
+ principalURI->GetSpec(principalURISpec);
+ }
+
+ nsCOMPtr<nsIURI> domainURI;
+ mPrincipal->GetDomain(getter_AddRefs(domainURI));
+ if (domainURI) {
+ setDomain = true;
+ }
+ }
+
+ NS_ASSERTION(mURI, "null URI in userfont cache entry");
+
+ printf("userfontcache fontEntry: %p fonturihash: %8.8x "
+ "family: %s domainset: %s principal: [%s]\n",
+ mFontEntry,
+ nsURIHashKey::HashKey(mURI),
+ NS_ConvertUTF16toUTF8(mFontEntry->FamilyName()).get(),
+ setDomain ? "true" : "false",
+ principalURISpec.get());
+}
+
+void
+gfxUserFontSet::UserFontCache::Dump()
+{
+ if (!sUserFonts) {
+ return;
+ }
+
+ printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
+ for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
+ it.Get()->Dump();
+ }
+ printf("userfontcache dump ==================\n");
+}
+
+#endif
+
+#undef LOG
+#undef LOG_ENABLED
diff --git a/system/graphics/thebes/gfxUserFontSet.h b/system/graphics/thebes/gfxUserFontSet.h
new file mode 100644
index 000000000..896c6951e
--- /dev/null
+++ b/system/graphics/thebes/gfxUserFontSet.h
@@ -0,0 +1,716 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_USER_FONT_SET_H
+#define GFX_USER_FONT_SET_H
+
+#include "gfxFont.h"
+#include "gfxFontFamilyList.h"
+#include "nsRefPtrHashtable.h"
+#include "nsCOMPtr.h"
+#include "nsIURI.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptError.h"
+#include "nsURIHashKey.h"
+#include "mozilla/net/ReferrerPolicy.h"
+#include "gfxFontConstants.h"
+
+class nsFontFaceLoader;
+
+//#define DEBUG_USERFONT_CACHE
+
+class gfxFontFaceBufferSource
+{
+ NS_INLINE_DECL_REFCOUNTING(gfxFontFaceBufferSource)
+public:
+ virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) = 0;
+
+protected:
+ virtual ~gfxFontFaceBufferSource() {}
+};
+
+// parsed CSS @font-face rule information
+// lifetime: from when @font-face rule processed until font is loaded
+struct gfxFontFaceSrc {
+
+ enum SourceType {
+ eSourceType_Local,
+ eSourceType_URL,
+ eSourceType_Buffer
+ };
+
+ SourceType mSourceType;
+
+ // if url, whether to use the origin principal or not
+ bool mUseOriginPrincipal;
+
+ // format hint flags, union of all possible formats
+ // (e.g. TrueType, EOT, SVG, etc.)
+ // see FLAG_FORMAT_* enum values below
+ uint32_t mFormatFlags;
+
+ nsString mLocalName; // full font name if local
+ nsCOMPtr<nsIURI> mURI; // uri if url
+ nsCOMPtr<nsIURI> mReferrer; // referrer url if url
+ mozilla::net::ReferrerPolicy mReferrerPolicy;
+ nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url
+
+ RefPtr<gfxFontFaceBufferSource> mBuffer;
+};
+
+inline bool
+operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b)
+{
+ if (a.mSourceType != b.mSourceType) {
+ return false;
+ }
+ switch (a.mSourceType) {
+ case gfxFontFaceSrc::eSourceType_Local:
+ return a.mLocalName == b.mLocalName;
+ case gfxFontFaceSrc::eSourceType_URL: {
+ bool equals;
+ return a.mUseOriginPrincipal == b.mUseOriginPrincipal &&
+ a.mFormatFlags == b.mFormatFlags &&
+ NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals &&
+ NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) &&
+ equals &&
+ a.mReferrerPolicy == b.mReferrerPolicy &&
+ a.mOriginPrincipal->Equals(b.mOriginPrincipal);
+ }
+ case gfxFontFaceSrc::eSourceType_Buffer:
+ return a.mBuffer == b.mBuffer;
+ }
+ NS_WARNING("unexpected mSourceType");
+ return false;
+}
+
+// Subclassed to store platform-specific code cleaned out when font entry is
+// deleted.
+// Lifetime: from when platform font is created until it is deactivated.
+// If the platform does not need to add any platform-specific code/data here,
+// then the gfxUserFontSet will allocate a base gfxUserFontData and attach
+// to the entry to track the basic user font info fields here.
+class gfxUserFontData {
+public:
+ gfxUserFontData()
+ : mSrcIndex(0), mFormat(0), mMetaOrigLen(0),
+ mCRC32(0), mLength(0), mCompression(kUnknownCompression),
+ mPrivate(false), mIsBuffer(false)
+ { }
+ virtual ~gfxUserFontData() { }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ nsTArray<uint8_t> mMetadata; // woff metadata block (compressed), if any
+ nsCOMPtr<nsIURI> mURI; // URI of the source, if it was url()
+ nsCOMPtr<nsIPrincipal> mPrincipal; // principal for the download, if url()
+ nsString mLocalName; // font name used for the source, if local()
+ nsString mRealName; // original fullname from the font resource
+ uint32_t mSrcIndex; // index in the rule's source list
+ uint32_t mFormat; // format hint for the source used, if any
+ uint32_t mMetaOrigLen; // length needed to decompress metadata
+ uint32_t mCRC32; // Checksum
+ uint32_t mLength; // Font length
+ uint8_t mCompression; // compression type
+ bool mPrivate; // whether font belongs to a private window
+ bool mIsBuffer; // whether the font source was a buffer
+
+ enum {
+ kUnknownCompression = 0,
+ kZlibCompression = 1,
+ kBrotliCompression = 2
+ };
+};
+
+// initially contains a set of userfont font entry objects, replaced with
+// platform/user fonts as downloaded
+
+class gfxUserFontFamily : public gfxFontFamily {
+public:
+ friend class gfxUserFontSet;
+
+ explicit gfxUserFontFamily(const nsAString& aName)
+ : gfxFontFamily(aName) { }
+
+ virtual ~gfxUserFontFamily() { }
+
+ // add the given font entry to the end of the family's list
+ void AddFontEntry(gfxFontEntry* aFontEntry) {
+ // keep ref while removing existing entry
+ RefPtr<gfxFontEntry> fe = aFontEntry;
+ // remove existing entry, if already present
+ mAvailableFonts.RemoveElement(aFontEntry);
+ // insert at the beginning so that the last-defined font is the first
+ // one in the fontlist used for matching, as per CSS Fonts spec
+ mAvailableFonts.InsertElementAt(0, aFontEntry);
+
+ if (aFontEntry->mFamilyName.IsEmpty()) {
+ aFontEntry->mFamilyName = Name();
+ } else {
+#ifdef DEBUG
+ nsString thisName = Name();
+ nsString entryName = aFontEntry->mFamilyName;
+ ToLowerCase(thisName);
+ ToLowerCase(entryName);
+ MOZ_ASSERT(thisName.Equals(entryName));
+#endif
+ }
+ ResetCharacterMap();
+ }
+
+ // Remove all font entries from the family
+ void DetachFontEntries() {
+ mAvailableFonts.Clear();
+ }
+};
+
+class gfxUserFontEntry;
+class gfxOTSContext;
+
+class gfxUserFontSet {
+ friend class gfxUserFontEntry;
+ friend class gfxOTSContext;
+
+public:
+
+ NS_INLINE_DECL_REFCOUNTING(gfxUserFontSet)
+
+ gfxUserFontSet();
+
+ enum {
+ // no flags ==> no hint set
+ // unknown ==> unknown format hint set
+ FLAG_FORMAT_UNKNOWN = 1,
+ FLAG_FORMAT_OPENTYPE = 1 << 1,
+ FLAG_FORMAT_TRUETYPE = 1 << 2,
+ FLAG_FORMAT_TRUETYPE_AAT = 1 << 3,
+ FLAG_FORMAT_EOT = 1 << 4,
+ FLAG_FORMAT_SVG = 1 << 5,
+ FLAG_FORMAT_WOFF = 1 << 6,
+ FLAG_FORMAT_WOFF2 = 1 << 7,
+
+ // the common formats that we support everywhere
+ FLAG_FORMATS_COMMON = FLAG_FORMAT_OPENTYPE |
+ FLAG_FORMAT_TRUETYPE |
+ FLAG_FORMAT_WOFF |
+ FLAG_FORMAT_WOFF2,
+
+ // mask of all unused bits, update when adding new formats
+ FLAG_FORMAT_NOT_USED = ~((1 << 8)-1)
+ };
+
+
+ // creates a font face without adding it to a particular family
+ // weight - [100, 900] (multiples of 100)
+ // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED]
+ // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL
+ // language override = result of calling gfxFontStyle::ParseFontLanguageOverride
+ // TODO: support for unicode ranges not yet implemented
+ virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay) = 0;
+
+ // creates a font face for the specified family, or returns an existing
+ // matching entry on the family if there is one
+ already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntry(
+ const nsAString& aFamilyName,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay);
+
+ // add in a font face for which we have the gfxUserFontEntry already
+ void AddUserFontEntry(const nsAString& aFamilyName,
+ gfxUserFontEntry* aUserFontEntry);
+
+ // Whether there is a face with this family name
+ bool HasFamily(const nsAString& aFamilyName) const
+ {
+ return LookupFamily(aFamilyName) != nullptr;
+ }
+
+ // Look up and return the gfxUserFontFamily in mFontFamilies with
+ // the given name
+ gfxUserFontFamily* LookupFamily(const nsAString& aName) const;
+
+ // Look up names in a fontlist and return true if any are in the set
+ bool ContainsUserFontSetFonts(const mozilla::FontFamilyList& aFontList) const;
+
+ // Lookup a font entry for a given style, returns null if not loaded.
+ // aFamily must be a family returned by our LookupFamily method.
+ // (only used by gfxPangoFontGroup for now)
+ gfxUserFontEntry* FindUserFontEntryAndLoad(gfxFontFamily* aFamily,
+ const gfxFontStyle& aFontStyle,
+ bool& aNeedsBold,
+ bool& aWaitForUserFont);
+
+ // check whether the given source is allowed to be loaded;
+ // returns the Principal (for use in the key when caching the loaded font),
+ // and whether the load should bypass the cache (force-reload).
+ virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
+ nsIPrincipal** aPrincipal,
+ bool* aBypassCache) = 0;
+
+ // check whether content policies allow the given URI to load.
+ virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
+ nsIPrincipal* aPrincipal) = 0;
+
+ // initialize the process that loads external font data, which upon
+ // completion will call FontDataDownloadComplete method
+ virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
+ const gfxFontFaceSrc* aFontFaceSrc) = 0;
+
+ // generation - each time a face is loaded, generation is
+ // incremented so that the change can be recognized
+ uint64_t GetGeneration() { return mGeneration; }
+
+ // increment the generation on font load
+ void IncrementGeneration(bool aIsRebuild = false);
+
+ // Generation is bumped on font loads but that doesn't affect name-style
+ // mappings. Rebuilds do however affect name-style mappings so need to
+ // lookup fontlists again when that happens.
+ uint64_t GetRebuildGeneration() { return mRebuildGeneration; }
+
+ // rebuild if local rules have been used
+ void RebuildLocalRules();
+
+ class UserFontCache {
+ public:
+ // Flag passed when caching a font entry, to specify whether the entry
+ // should persist in the cache or be discardable.
+ typedef enum {
+ kDiscardable,
+ kPersistent
+ } EntryPersistence;
+
+ // Record a loaded user-font in the cache. This requires that the
+ // font-entry's userFontData has been set up already, as it relies
+ // on the URI and Principal recorded there.
+ // If aPersistence is Persistent, the entry will remain in the cache
+ // across cacheservice:empty-cache notifications. This is used for
+ // "preloaded hidden fonts" on FxOS.
+ static void CacheFont(gfxFontEntry* aFontEntry,
+ EntryPersistence aPersistence = kDiscardable);
+
+ // The given gfxFontEntry is being destroyed, so remove any record that
+ // refers to it.
+ static void ForgetFont(gfxFontEntry* aFontEntry);
+
+ // Return the gfxFontEntry corresponding to a given URI and principal,
+ // and the features of the given userfont entry, or nullptr if none is available.
+ // The aPrivate flag is set for requests coming from private windows,
+ // so we can avoid leaking fonts cached in private windows mode out to
+ // normal windows.
+ static gfxFontEntry* GetFont(nsIURI* aSrcURI,
+ nsIPrincipal* aPrincipal,
+ gfxUserFontEntry* aUserFontEntry,
+ bool aPrivate);
+
+ // Clear everything so that we don't leak URIs and Principals.
+ static void Shutdown();
+
+ // Memory-reporting support.
+ class MemoryReporter final : public nsIMemoryReporter
+ {
+ private:
+ ~MemoryReporter() { }
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+ };
+
+#ifdef DEBUG_USERFONT_CACHE
+ // dump contents
+ static void Dump();
+#endif
+
+ private:
+ // Helper that we use to observe the empty-cache notification
+ // from nsICacheService.
+ class Flusher : public nsIObserver
+ {
+ virtual ~Flusher() {}
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ Flusher() {}
+ };
+
+ // Key used to look up entries in the user-font cache.
+ // Note that key comparison does *not* use the mFontEntry field
+ // as a whole; it only compares specific fields within the entry
+ // (weight/width/style/features) that could affect font selection
+ // or rendering, and that must match between a font-set's userfont
+ // entry and the corresponding "real" font entry.
+ struct Key {
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mPrincipal; // use nullptr with data: URLs
+ // The font entry MUST notify the cache when it is destroyed
+ // (by calling ForgetFont()).
+ gfxFontEntry* MOZ_NON_OWNING_REF mFontEntry;
+ uint32_t mCRC32;
+ uint32_t mLength;
+ bool mPrivate;
+ EntryPersistence mPersistence;
+
+ Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
+ gfxFontEntry* aFontEntry, bool aPrivate,
+ EntryPersistence aPersistence = kDiscardable)
+ : mURI(aURI),
+ mPrincipal(aPrincipal),
+ mFontEntry(aFontEntry),
+ mCRC32(0),
+ mLength(0),
+ mPrivate(aPrivate),
+ mPersistence(aPersistence)
+ { }
+
+ Key(uint32_t aCRC32, uint32_t aLength,
+ gfxFontEntry* aFontEntry, bool aPrivate,
+ EntryPersistence aPersistence = kDiscardable)
+ : mURI(nullptr),
+ mPrincipal(nullptr),
+ mFontEntry(aFontEntry),
+ mCRC32(aCRC32),
+ mLength(aLength),
+ mPrivate(aPrivate),
+ mPersistence(aPersistence)
+ { }
+ };
+
+ class Entry : public PLDHashEntryHdr {
+ public:
+ typedef const Key& KeyType;
+ typedef const Key* KeyTypePointer;
+
+ explicit Entry(KeyTypePointer aKey)
+ : mURI(aKey->mURI),
+ mPrincipal(aKey->mPrincipal),
+ mCRC32(aKey->mCRC32),
+ mLength(aKey->mLength),
+ mFontEntry(aKey->mFontEntry),
+ mPrivate(aKey->mPrivate),
+ mPersistence(aKey->mPersistence)
+ { }
+
+ Entry(const Entry& aOther)
+ : mURI(aOther.mURI),
+ mPrincipal(aOther.mPrincipal),
+ mCRC32(aOther.mCRC32),
+ mLength(aOther.mLength),
+ mFontEntry(aOther.mFontEntry),
+ mPrivate(aOther.mPrivate),
+ mPersistence(aOther.mPersistence)
+ { }
+
+ ~Entry() { }
+
+ bool KeyEquals(const KeyTypePointer aKey) const;
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+ static PLDHashNumber HashKey(const KeyTypePointer aKey) {
+ if (aKey->mLength) {
+ return aKey->mCRC32;
+ }
+ uint32_t principalHash = 0;
+ if (aKey->mPrincipal) {
+ aKey->mPrincipal->GetHashValue(&principalHash);
+ }
+ return mozilla::HashGeneric(principalHash + int(aKey->mPrivate),
+ nsURIHashKey::HashKey(aKey->mURI),
+ HashFeatures(aKey->mFontEntry->mFeatureSettings),
+ mozilla::HashString(aKey->mFontEntry->mFamilyName),
+ (aKey->mFontEntry->mStyle |
+ (aKey->mFontEntry->mWeight << 2) |
+ (aKey->mFontEntry->mStretch << 11) ) ^
+ aKey->mFontEntry->mLanguageOverride);
+ }
+
+ enum { ALLOW_MEMMOVE = false };
+
+ gfxFontEntry* GetFontEntry() const { return mFontEntry; }
+
+ bool IsPersistent() const { return mPersistence == kPersistent; }
+ bool IsPrivate() const { return mPrivate; }
+
+ void ReportMemory(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize);
+
+#ifdef DEBUG_USERFONT_CACHE
+ void Dump();
+#endif
+
+ private:
+ static uint32_t
+ HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
+ return mozilla::HashBytes(aFeatures.Elements(),
+ aFeatures.Length() * sizeof(gfxFontFeature));
+ }
+
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mPrincipal; // or nullptr for data: URLs
+
+ uint32_t mCRC32;
+ uint32_t mLength;
+
+ // The "real" font entry corresponding to this downloaded font.
+ // The font entry MUST notify the cache when it is destroyed
+ // (by calling ForgetFont()).
+ gfxFontEntry* MOZ_NON_OWNING_REF mFontEntry;
+
+ // Whether this font was loaded from a private window.
+ bool mPrivate;
+
+ // Whether this entry should survive cache-flushing.
+ EntryPersistence mPersistence;
+ };
+
+ static nsTHashtable<Entry>* sUserFonts;
+ };
+
+ void SetLocalRulesUsed() {
+ mLocalRulesUsed = true;
+ }
+
+ static mozilla::LogModule* GetUserFontsLog();
+
+ // record statistics about font completion
+ virtual void RecordFontLoadDone(uint32_t aFontSize,
+ mozilla::TimeStamp aDoneTime) {}
+
+ void GetLoadStatistics(uint32_t& aLoadCount, uint64_t& aLoadSize) const {
+ aLoadCount = mDownloadCount;
+ aLoadSize = mDownloadSize;
+ }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~gfxUserFontSet();
+
+ // Return whether the font set is associated with a private-browsing tab.
+ virtual bool GetPrivateBrowsing() = 0;
+
+ // parse data for a data URL
+ virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
+ const gfxFontFaceSrc* aFontFaceSrc,
+ uint8_t* &aBuffer,
+ uint32_t &aBufferLength) = 0;
+
+ // report a problem of some kind (implemented in nsUserFontSet)
+ virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
+ const char* aMessage,
+ uint32_t aFlags = nsIScriptError::errorFlag,
+ nsresult aStatus = NS_OK) = 0;
+
+ // helper method for performing the actual userfont set rebuild
+ virtual void DoRebuildUserFontSet() = 0;
+
+ // helper method for FindOrCreateUserFontEntry
+ gfxUserFontEntry* FindExistingUserFontEntry(
+ gfxUserFontFamily* aFamily,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay);
+
+ // creates a new gfxUserFontFamily in mFontFamilies, or returns an existing
+ // family if there is one
+ gfxUserFontFamily* GetFamily(const nsAString& aFamilyName);
+
+ // font families defined by @font-face rules
+ nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies;
+
+ uint64_t mGeneration; // bumped on any font load change
+ uint64_t mRebuildGeneration; // only bumped on rebuilds
+
+ // true when local names have been looked up, false otherwise
+ bool mLocalRulesUsed;
+
+ // true when rules using local names need to be redone
+ bool mRebuildLocalRules;
+
+ // performance stats
+ uint32_t mDownloadCount;
+ uint64_t mDownloadSize;
+};
+
+// acts a placeholder until the real font is downloaded
+
+class gfxUserFontEntry : public gfxFontEntry {
+ friend class gfxUserFontSet;
+ friend class nsUserFontSet;
+ friend class nsFontFaceLoader;
+ friend class gfxOTSContext;
+
+public:
+ enum UserFontLoadState {
+ STATUS_NOT_LOADED = 0,
+ STATUS_LOADING,
+ STATUS_LOADED,
+ STATUS_FAILED
+ };
+
+ gfxUserFontEntry(gfxUserFontSet* aFontSet,
+ const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay);
+
+ virtual ~gfxUserFontEntry();
+
+ // Return whether the entry matches the given list of attributes
+ bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+ uint32_t aWeight,
+ int32_t aStretch,
+ uint8_t aStyle,
+ const nsTArray<gfxFontFeature>& aFeatureSettings,
+ uint32_t aLanguageOverride,
+ gfxSparseBitSet* aUnicodeRanges,
+ uint8_t aFontDisplay);
+
+ virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle,
+ bool aNeedsBold);
+
+ gfxFontEntry* GetPlatformFontEntry() const { return mPlatformFontEntry; }
+
+ // is the font loading or loaded, or did it fail?
+ UserFontLoadState LoadState() const { return mUserFontLoadState; }
+
+ // whether to wait before using fallback font or not
+ bool WaitForUserFont() const {
+ return mUserFontLoadState == STATUS_LOADING &&
+ mFontDataLoadingState < LOADING_SLOWLY;
+ }
+
+ // for userfonts, cmap is used to store the unicode range data
+ // no cmap ==> all codepoints permitted
+ bool CharacterInUnicodeRange(uint32_t ch) const {
+ if (mCharacterMap) {
+ return mCharacterMap->test(ch);
+ }
+ return true;
+ }
+
+ gfxCharacterMap* GetUnicodeRangeMap() const {
+ return mCharacterMap.get();
+ }
+
+ uint8_t GetFontDisplay() const { return mFontDisplay; }
+
+ // load the font - starts the loading of sources which continues until
+ // a valid font resource is found or all sources fail
+ void Load();
+
+ // methods to expose some information to FontFaceSet::UserFontSet
+ // since we can't make that class a friend
+ void SetLoader(nsFontFaceLoader* aLoader) { mLoader = aLoader; }
+ nsFontFaceLoader* GetLoader() { return mLoader; }
+ nsIPrincipal* GetPrincipal() { return mPrincipal; }
+ uint32_t GetSrcIndex() { return mSrcIndex; }
+ void GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
+ nsACString& aURI);
+
+#ifdef DEBUG
+ gfxUserFontSet* GetUserFontSet() const { return mFontSet; }
+#endif
+
+protected:
+ const uint8_t* SanitizeOpenTypeData(const uint8_t* aData,
+ uint32_t aLength,
+ uint32_t& aSaneLength,
+ gfxUserFontType aFontType);
+
+ // attempt to load the next resource in the src list.
+ void LoadNextSrc();
+
+ // change the load state
+ virtual void SetLoadState(UserFontLoadState aLoadState);
+
+ // when download has been completed, pass back data here
+ // aDownloadStatus == NS_OK ==> download succeeded, error otherwise
+ // returns true if platform font creation sucessful (or local()
+ // reference was next in line)
+ // Ownership of aFontData is passed in here; the font set must
+ // ensure that it is eventually deleted with free().
+ bool FontDataDownloadComplete(const uint8_t* aFontData, uint32_t aLength,
+ nsresult aDownloadStatus);
+
+ // helper method for creating a platform font
+ // returns true if platform font creation successful
+ // Ownership of aFontData is passed in here; the font must
+ // ensure that it is eventually deleted with free().
+ bool LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength);
+
+ // store metadata and src details for current src into aFontEntry
+ void StoreUserFontData(gfxFontEntry* aFontEntry,
+ bool aPrivate,
+ const nsAString& aOriginalName,
+ FallibleTArray<uint8_t>* aMetadata,
+ uint32_t aMetaOrigLen,
+ uint8_t aCompression);
+
+ // Clears and then adds to aResult all of the user font sets that this user
+ // font entry has been added to. This will at least include mFontSet, the
+ // owner of this user font entry.
+ virtual void GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult);
+
+ // Calls IncrementGeneration() on all user font sets that contain this
+ // user font entry.
+ void IncrementGeneration();
+
+ // general load state
+ UserFontLoadState mUserFontLoadState;
+
+ // detailed load state while font data is loading
+ // used to determine whether to use fallback font or not
+ // note that code depends on the ordering of these values!
+ enum FontDataLoadingState {
+ NOT_LOADING = 0, // not started to load any font resources yet
+ LOADING_STARTED, // loading has started; hide fallback font
+ LOADING_ALMOST_DONE, // timeout happened but we're nearly done,
+ // so keep hiding fallback font
+ LOADING_SLOWLY, // timeout happened and we're not nearly done,
+ // so use the fallback font
+ LOADING_TIMED_OUT, // font load took too long
+ LOADING_FAILED // failed to load any source: use fallback
+ };
+ FontDataLoadingState mFontDataLoadingState;
+
+ bool mUnsupportedFormat;
+ uint8_t mFontDisplay; // timing of userfont fallback
+
+ RefPtr<gfxFontEntry> mPlatformFontEntry;
+ nsTArray<gfxFontFaceSrc> mSrcList;
+ uint32_t mSrcIndex; // index of loading src item
+ // This field is managed by the nsFontFaceLoader. In the destructor and Cancel()
+ // methods of nsFontFaceLoader this reference is nulled out.
+ nsFontFaceLoader* MOZ_NON_OWNING_REF mLoader; // current loader for this entry, if any
+ gfxUserFontSet* mFontSet; // font-set which owns this userfont entry
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+
+#endif /* GFX_USER_FONT_SET_H */
diff --git a/system/graphics/thebes/gfxUtils.cpp b/system/graphics/thebes/gfxUtils.cpp
new file mode 100644
index 000000000..c09b93f26
--- /dev/null
+++ b/system/graphics/thebes/gfxUtils.cpp
@@ -0,0 +1,1485 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxUtils.h"
+
+#include "cairo.h"
+#include "gfxContext.h"
+#include "gfxEnv.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "gfxDrawable.h"
+#include "imgIEncoder.h"
+#include "libyuv.h"
+#include "mozilla/Base64.h"
+#include "mozilla/dom/ImageEncoder.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRunnable.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Vector.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIClipboardHelper.h"
+#include "nsIFile.h"
+#include "nsIGfxInfo.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "nsRegion.h"
+#include "nsServiceManagerUtils.h"
+#include "GeckoProfiler.h"
+#include "ImageContainer.h"
+#include "ImageRegion.h"
+#include "gfx2DGlue.h"
+#include "gfxPrefs.h"
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::image;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+#include "DeprecatedPremultiplyTables.h"
+
+#undef compress
+#include "mozilla/Compression.h"
+
+// Define if we should use a temp surface to prescale repeated patterns.
+// Performance <-> memory tradeoff
+#define PRESCALE_TEMP_SURFACE
+
+using namespace mozilla::Compression;
+extern "C" {
+
+/**
+ * Dump a raw image to the default log. This function is exported
+ * from libxul, so it can be called from any library in addition to
+ * (of course) from a debugger.
+ *
+ * Note: this helper currently assumes that all 2-bytepp images are
+ * r5g6b5, and that all 4-bytepp images are r8g8b8a8.
+ */
+NS_EXPORT
+void mozilla_dump_image(void* bytes, int width, int height, int bytepp,
+ int strideBytes)
+{
+ if (0 == strideBytes) {
+ strideBytes = width * bytepp;
+ }
+ SurfaceFormat format;
+ // TODO more flexible; parse string?
+ switch (bytepp) {
+ case 2:
+ format = SurfaceFormat::R5G6B5_UINT16;
+ break;
+ case 4:
+ default:
+ format = SurfaceFormat::R8G8B8A8;
+ break;
+ }
+
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)bytes, strideBytes,
+ IntSize(width, height),
+ format);
+ gfxUtils::DumpAsDataURI(surf);
+}
+
+}
+
+static uint8_t PremultiplyValue(uint8_t a, uint8_t v) {
+ return gfxUtils::sPremultiplyTable[a*256+v];
+}
+
+static uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) {
+ return gfxUtils::sUnpremultiplyTable[a*256+v];
+}
+
+static void
+PremultiplyData(const uint8_t* srcData,
+ size_t srcStride, // row-to-row stride in bytes
+ uint8_t* destData,
+ size_t destStride, // row-to-row stride in bytes
+ size_t pixelWidth,
+ size_t rowCount)
+{
+ MOZ_ASSERT(srcData && destData);
+
+ for (size_t y = 0; y < rowCount; ++y) {
+ const uint8_t* src = srcData + y * srcStride;
+ uint8_t* dest = destData + y * destStride;
+
+ for (size_t x = 0; x < pixelWidth; ++x) {
+#ifdef IS_LITTLE_ENDIAN
+ uint8_t b = *src++;
+ uint8_t g = *src++;
+ uint8_t r = *src++;
+ uint8_t a = *src++;
+
+ *dest++ = PremultiplyValue(a, b);
+ *dest++ = PremultiplyValue(a, g);
+ *dest++ = PremultiplyValue(a, r);
+ *dest++ = a;
+#else
+ uint8_t a = *src++;
+ uint8_t r = *src++;
+ uint8_t g = *src++;
+ uint8_t b = *src++;
+
+ *dest++ = a;
+ *dest++ = PremultiplyValue(a, r);
+ *dest++ = PremultiplyValue(a, g);
+ *dest++ = PremultiplyValue(a, b);
+#endif
+ }
+ }
+}
+static void
+UnpremultiplyData(const uint8_t* srcData,
+ size_t srcStride, // row-to-row stride in bytes
+ uint8_t* destData,
+ size_t destStride, // row-to-row stride in bytes
+ size_t pixelWidth,
+ size_t rowCount)
+{
+ MOZ_ASSERT(srcData && destData);
+
+ for (size_t y = 0; y < rowCount; ++y) {
+ const uint8_t* src = srcData + y * srcStride;
+ uint8_t* dest = destData + y * destStride;
+
+ for (size_t x = 0; x < pixelWidth; ++x) {
+#ifdef IS_LITTLE_ENDIAN
+ uint8_t b = *src++;
+ uint8_t g = *src++;
+ uint8_t r = *src++;
+ uint8_t a = *src++;
+
+ *dest++ = UnpremultiplyValue(a, b);
+ *dest++ = UnpremultiplyValue(a, g);
+ *dest++ = UnpremultiplyValue(a, r);
+ *dest++ = a;
+#else
+ uint8_t a = *src++;
+ uint8_t r = *src++;
+ uint8_t g = *src++;
+ uint8_t b = *src++;
+
+ *dest++ = a;
+ *dest++ = UnpremultiplyValue(a, r);
+ *dest++ = UnpremultiplyValue(a, g);
+ *dest++ = UnpremultiplyValue(a, b);
+#endif
+ }
+ }
+}
+
+static bool
+MapSrcDest(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf,
+ DataSourceSurface::MappedSurface* out_srcMap,
+ DataSourceSurface::MappedSurface* out_destMap)
+{
+ MOZ_ASSERT(srcSurf && destSurf);
+ MOZ_ASSERT(out_srcMap && out_destMap);
+
+ if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8 ||
+ destSurf->GetFormat() != SurfaceFormat::B8G8R8A8)
+ {
+ MOZ_ASSERT(false, "Only operate on BGRA8 surfs.");
+ return false;
+ }
+
+ if (srcSurf->GetSize().width != destSurf->GetSize().width ||
+ srcSurf->GetSize().height != destSurf->GetSize().height)
+ {
+ MOZ_ASSERT(false, "Width and height must match.");
+ return false;
+ }
+
+ if (srcSurf == destSurf) {
+ DataSourceSurface::MappedSurface map;
+ if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+ NS_WARNING("Couldn't Map srcSurf/destSurf.");
+ return false;
+ }
+
+ *out_srcMap = map;
+ *out_destMap = map;
+ return true;
+ }
+
+ // Map src for reading.
+ DataSourceSurface::MappedSurface srcMap;
+ if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
+ NS_WARNING("Couldn't Map srcSurf.");
+ return false;
+ }
+
+ // Map dest for writing.
+ DataSourceSurface::MappedSurface destMap;
+ if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+ NS_WARNING("Couldn't Map aDest.");
+ srcSurf->Unmap();
+ return false;
+ }
+
+ *out_srcMap = srcMap;
+ *out_destMap = destMap;
+ return true;
+}
+
+static void
+UnmapSrcDest(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf)
+{
+ if (srcSurf == destSurf) {
+ srcSurf->Unmap();
+ } else {
+ srcSurf->Unmap();
+ destSurf->Unmap();
+ }
+}
+
+bool
+gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf)
+{
+ MOZ_ASSERT(srcSurf && destSurf);
+
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
+ return false;
+
+ PremultiplyData(srcMap.mData, srcMap.mStride,
+ destMap.mData, destMap.mStride,
+ srcSurf->GetSize().width,
+ srcSurf->GetSize().height);
+
+ UnmapSrcDest(srcSurf, destSurf);
+ return true;
+}
+
+bool
+gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf)
+{
+ MOZ_ASSERT(srcSurf && destSurf);
+
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
+ return false;
+
+ UnpremultiplyData(srcMap.mData, srcMap.mStride,
+ destMap.mData, destMap.mStride,
+ srcSurf->GetSize().width,
+ srcSurf->GetSize().height);
+
+ UnmapSrcDest(srcSurf, destSurf);
+ return true;
+}
+
+static bool
+MapSrcAndCreateMappedDest(DataSourceSurface* srcSurf,
+ RefPtr<DataSourceSurface>* out_destSurf,
+ DataSourceSurface::MappedSurface* out_srcMap,
+ DataSourceSurface::MappedSurface* out_destMap)
+{
+ MOZ_ASSERT(srcSurf);
+ MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap);
+
+ if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8) {
+ MOZ_ASSERT(false, "Only operate on BGRA8.");
+ return false;
+ }
+
+ // Ok, map source for reading.
+ DataSourceSurface::MappedSurface srcMap;
+ if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
+ MOZ_ASSERT(false, "Couldn't Map srcSurf.");
+ return false;
+ }
+
+ // Make our dest surface based on the src.
+ RefPtr<DataSourceSurface> destSurf =
+ Factory::CreateDataSourceSurfaceWithStride(srcSurf->GetSize(),
+ srcSurf->GetFormat(),
+ srcMap.mStride);
+ if (NS_WARN_IF(!destSurf)) {
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface destMap;
+ if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+ MOZ_ASSERT(false, "Couldn't Map destSurf.");
+ srcSurf->Unmap();
+ return false;
+ }
+
+ *out_destSurf = destSurf;
+ *out_srcMap = srcMap;
+ *out_destMap = destMap;
+ return true;
+}
+
+already_AddRefed<DataSourceSurface>
+gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface* srcSurf)
+{
+ RefPtr<DataSourceSurface> destSurf;
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
+ MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
+ RefPtr<DataSourceSurface> surface(srcSurf);
+ return surface.forget();
+ }
+
+ PremultiplyData(srcMap.mData, srcMap.mStride,
+ destMap.mData, destMap.mStride,
+ srcSurf->GetSize().width,
+ srcSurf->GetSize().height);
+
+ UnmapSrcDest(srcSurf, destSurf);
+ return destSurf.forget();
+}
+
+already_AddRefed<DataSourceSurface>
+gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf)
+{
+ RefPtr<DataSourceSurface> destSurf;
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
+ MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
+ RefPtr<DataSourceSurface> surface(srcSurf);
+ return surface.forget();
+ }
+
+ UnpremultiplyData(srcMap.mData, srcMap.mStride,
+ destMap.mData, destMap.mStride,
+ srcSurf->GetSize().width,
+ srcSurf->GetSize().height);
+
+ UnmapSrcDest(srcSurf, destSurf);
+ return destSurf.forget();
+}
+
+void
+gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
+{
+ MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
+ libyuv::ABGRToARGB(aData, aLength, aData, aLength, aLength / 4, 1);
+}
+
+/**
+ * This returns the fastest operator to use for solid surfaces which have no
+ * alpha channel or their alpha channel is uniformly opaque.
+ * This differs per render mode.
+ */
+static CompositionOp
+OptimalFillOp()
+{
+#ifdef XP_WIN
+ if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend()) {
+ // D2D -really- hates operator source.
+ return CompositionOp::OP_OVER;
+ }
+#endif
+ return CompositionOp::OP_SOURCE;
+}
+
+// EXTEND_PAD won't help us here; we have to create a temporary surface to hold
+// the subimage of pixels we're allowed to sample.
+static already_AddRefed<gfxDrawable>
+CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
+ gfxContext* aContext,
+ const ImageRegion& aRegion,
+ const SurfaceFormat aFormat)
+{
+ PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ DrawTarget* destDrawTarget = aContext->GetDrawTarget();
+ if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) {
+ return nullptr;
+ }
+
+ gfxRect clipExtents = aContext->GetClipExtents();
+
+ // Inflate by one pixel because bilinear filtering will sample at most
+ // one pixel beyond the computed image pixel coordinate.
+ clipExtents.Inflate(1.0);
+
+ gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
+ needed.RoundOut();
+
+ // if 'needed' is empty, nothing will be drawn since aFill
+ // must be entirely outside the clip region, so it doesn't
+ // matter what we do here, but we should avoid trying to
+ // create a zero-size surface.
+ if (needed.IsEmpty())
+ return nullptr;
+
+ IntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
+
+ RefPtr<DrawTarget> target =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
+ if (!target || !target->IsValid()) {
+ return nullptr;
+ }
+
+ RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(target);
+ MOZ_ASSERT(tmpCtx); // already checked the target above
+
+ tmpCtx->SetOp(OptimalFillOp());
+ aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT,
+ SamplingFilter::LINEAR,
+ 1.0, gfxMatrix::Translation(needed.TopLeft()));
+ RefPtr<SourceSurface> surface = target->Snapshot();
+
+ RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft()));
+ return drawable.forget();
+}
+
+static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter,
+ int aImgWidth, int aImgHeight,
+ int aSourceWidth, int aSourceHeight)
+{
+ // Just pass the filter through unchanged
+ return aSamplingFilter;
+}
+
+#ifdef PRESCALE_TEMP_SURFACE
+// Only prescale a temporary surface if we're going to repeat it often.
+// Without prescaling, we'd scale every tile of the repeated rect.
+// However, using a temp surface also potentially uses more memory if the scaled
+// image is large.
+// So, only prescale on a temp surface if we know we're going to repeat the image
+// in either the X or Y axis multiple times (>= 5 times).
+static bool
+ShouldUseTempSurface(Rect aImageRect, Rect aNeededRect)
+{
+ int repeatX = aNeededRect.width / aImageRect.width;
+ int repeatY = aNeededRect.height / aImageRect.height;
+ return (repeatX >= 5) || (repeatY >= 5);
+}
+
+static bool
+PrescaleAndTileDrawable(gfxDrawable* aDrawable,
+ gfxContext* aContext,
+ const ImageRegion& aRegion,
+ Rect aImageRect,
+ const SamplingFilter aSamplingFilter,
+ const SurfaceFormat aFormat,
+ gfxFloat aOpacity,
+ ExtendMode aExtendMode)
+{
+ gfxSize scaleFactor = aContext->CurrentMatrix().ScaleFactors(true);
+ gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleFactor.width, scaleFactor.height);
+ const float fuzzFactor = 0.01f;
+
+ // If we aren't scaling or translating, don't go down this path
+ if ((FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor) &&
+ FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor)) ||
+ aContext->CurrentMatrix().HasNonAxisAlignedTransform()) {
+ return false;
+ }
+
+ gfxRect clipExtents = aContext->GetClipExtents();
+
+ // Inflate by one pixel because bilinear filtering will sample at most
+ // one pixel beyond the computed image pixel coordinate.
+ clipExtents.Inflate(1.0);
+
+ gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
+ Rect scaledNeededRect = ToMatrix(scaleMatrix).TransformBounds(ToRect(needed));
+ scaledNeededRect.RoundOut();
+ if (scaledNeededRect.IsEmpty()) {
+ return false;
+ }
+
+ Rect scaledImageRect = ToMatrix(scaleMatrix).TransformBounds(aImageRect);
+ if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) {
+ return false;
+ }
+
+ IntSize scaledImageSize((int32_t)scaledImageRect.width,
+ (int32_t)scaledImageRect.height);
+ if (scaledImageSize.width != scaledImageRect.width ||
+ scaledImageSize.height != scaledImageRect.height) {
+ // If the scaled image isn't pixel aligned, we'll get artifacts
+ // so we have to take the slow path.
+ return false;
+ }
+
+ RefPtr<DrawTarget> scaledDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
+ if (!scaledDT || !scaledDT->IsValid()) {
+ return false;
+ }
+
+ RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(scaledDT);
+ MOZ_ASSERT(tmpCtx); // already checked the target above
+
+ scaledDT->SetTransform(ToMatrix(scaleMatrix));
+ gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
+
+ // Since this is just the scaled image, we don't want to repeat anything yet.
+ aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::CLAMP, aSamplingFilter, 1.0, gfxMatrix());
+
+ RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot();
+
+ {
+ gfxContextMatrixAutoSaveRestore autoSR(aContext);
+ Matrix withoutScale = ToMatrix(aContext->CurrentMatrix());
+ DrawTarget* destDrawTarget = aContext->GetDrawTarget();
+
+ // The translation still is in scaled units
+ withoutScale.PreScale(1.0 / scaleFactor.width, 1.0 / scaleFactor.height);
+ aContext->SetMatrix(ThebesMatrix(withoutScale));
+
+ DrawOptions drawOptions(aOpacity, aContext->CurrentOp(),
+ aContext->CurrentAntialiasMode());
+
+ SurfacePattern scaledImagePattern(scaledImage, aExtendMode,
+ Matrix(), aSamplingFilter);
+ destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions);
+ }
+ return true;
+}
+#endif // PRESCALE_TEMP_SURFACE
+
+/* static */ void
+gfxUtils::DrawPixelSnapped(gfxContext* aContext,
+ gfxDrawable* aDrawable,
+ const gfxSize& aImageSize,
+ const ImageRegion& aRegion,
+ const SurfaceFormat aFormat,
+ SamplingFilter aSamplingFilter,
+ uint32_t aImageFlags,
+ gfxFloat aOpacity)
+{
+ PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ gfxRect imageRect(gfxPoint(0, 0), aImageSize);
+ gfxRect region(aRegion.Rect());
+ ExtendMode extendMode = aRegion.GetExtendMode();
+
+ RefPtr<gfxDrawable> drawable = aDrawable;
+
+ aSamplingFilter =
+ ReduceResamplingFilter(aSamplingFilter,
+ imageRect.Width(), imageRect.Height(),
+ region.Width(), region.Height());
+
+ // OK now, the hard part left is to account for the subimage sampling
+ // restriction. If all the transforms involved are just integer
+ // translations, then we assume no resampling will occur so there's
+ // nothing to do.
+ // XXX if only we had source-clipping in cairo!
+
+ if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
+ if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) {
+ if (drawable->DrawWithSamplingRect(aContext->GetDrawTarget(),
+ aContext->CurrentOp(),
+ aContext->CurrentAntialiasMode(),
+ aRegion.Rect(),
+ aRegion.Restriction(),
+ extendMode, aSamplingFilter,
+ aOpacity)) {
+ return;
+ }
+
+#ifdef PRESCALE_TEMP_SURFACE
+ if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion,
+ ToRect(imageRect), aSamplingFilter,
+ aFormat, aOpacity, extendMode)) {
+ return;
+ }
+#endif
+
+ RefPtr<gfxDrawable> restrictedDrawable =
+ CreateSamplingRestrictedDrawable(aDrawable, aContext,
+ aRegion, aFormat);
+ if (restrictedDrawable) {
+ drawable.swap(restrictedDrawable);
+
+ // We no longer need to tile: Either we never needed to, or we already
+ // filled a surface with the tiled pattern; this surface can now be
+ // drawn without tiling.
+ extendMode = ExtendMode::CLAMP;
+ }
+ }
+ }
+
+ drawable->Draw(aContext, aRegion.Rect(), extendMode, aSamplingFilter,
+ aOpacity, gfxMatrix());
+}
+
+/* static */ int
+gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return 32;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return 24;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return 16;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*static*/ void
+gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
+{
+ aContext->NewPath();
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
+ }
+ aContext->Clip();
+}
+
+/*static*/ void
+gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
+{
+ uint32_t numRects = aRegion.GetNumRects();
+ // If there is only one rect, then the region bounds are equivalent to the
+ // contents. So just use push a single clip rect with the bounds.
+ if (numRects == 1) {
+ aTarget->PushClipRect(Rect(aRegion.GetBounds()));
+ return;
+ }
+
+ // Check if the target's transform will preserve axis-alignment and
+ // pixel-alignment for each rect. For now, just handle the common case
+ // of integer translations.
+ Matrix transform = aTarget->GetTransform();
+ if (transform.IsIntegerTranslation()) {
+ IntPoint translation = RoundedToInt(transform.GetTranslation());
+ AutoTArray<IntRect, 16> rects;
+ rects.SetLength(numRects);
+ uint32_t i = 0;
+ // Build the list of transformed rects by adding in the translation.
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ IntRect rect = iter.Get();
+ rect.MoveBy(translation);
+ rects[i++] = rect;
+ }
+ aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+ } else {
+ // The transform does not produce axis-aligned rects or a rect was not
+ // pixel-aligned. So just build a path with all the rects and clip to it
+ // instead.
+ RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ AppendRectToPath(pathBuilder, Rect(iter.Get()));
+ }
+ RefPtr<Path> path = pathBuilder->Finish();
+ aTarget->PushClip(path);
+ }
+}
+
+/*static*/ gfxFloat
+gfxUtils::ClampToScaleFactor(gfxFloat aVal)
+{
+ // Arbitary scale factor limitation. We can increase this
+ // for better scaling performance at the cost of worse
+ // quality.
+ static const gfxFloat kScaleResolution = 2;
+
+ // Negative scaling is just a flip and irrelevant to
+ // our resolution calculation.
+ if (aVal < 0.0) {
+ aVal = -aVal;
+ }
+
+ bool inverse = false;
+ if (aVal < 1.0) {
+ inverse = true;
+ aVal = 1 / aVal;
+ }
+
+ gfxFloat power = log(aVal)/log(kScaleResolution);
+
+ // If power is within 1e-5 of an integer, round to nearest to
+ // prevent floating point errors, otherwise round up to the
+ // next integer value.
+ if (fabs(power - NS_round(power)) < 1e-5) {
+ power = NS_round(power);
+ } else if (inverse) {
+ power = floor(power);
+ } else {
+ power = ceil(power);
+ }
+
+ gfxFloat scale = pow(kScaleResolution, power);
+
+ if (inverse) {
+ scale = 1 / scale;
+ }
+
+ return scale;
+}
+
+gfxMatrix
+gfxUtils::TransformRectToRect(const gfxRect& aFrom, const gfxPoint& aToTopLeft,
+ const gfxPoint& aToTopRight, const gfxPoint& aToBottomRight)
+{
+ gfxMatrix m;
+ if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
+ // Not a rotation, so xy and yx are zero
+ m._21 = m._12 = 0.0;
+ m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
+ m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
+ m._31 = aToTopLeft.x - m._11*aFrom.x;
+ m._32 = aToTopLeft.y - m._22*aFrom.y;
+ } else {
+ NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
+ "Destination rectangle not axis-aligned");
+ m._11 = m._22 = 0.0;
+ m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
+ m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
+ m._31 = aToTopLeft.x - m._21*aFrom.y;
+ m._32 = aToTopLeft.y - m._12*aFrom.x;
+ }
+ return m;
+}
+
+Matrix
+gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft,
+ const IntPoint& aToTopRight, const IntPoint& aToBottomRight)
+{
+ Matrix m;
+ if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
+ // Not a rotation, so xy and yx are zero
+ m._12 = m._21 = 0.0;
+ m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
+ m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
+ m._31 = aToTopLeft.x - m._11*aFrom.x;
+ m._32 = aToTopLeft.y - m._22*aFrom.y;
+ } else {
+ NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
+ "Destination rectangle not axis-aligned");
+ m._11 = m._22 = 0.0;
+ m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
+ m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
+ m._31 = aToTopLeft.x - m._21*aFrom.y;
+ m._32 = aToTopLeft.y - m._12*aFrom.x;
+ }
+ return m;
+}
+
+/* This function is sort of shitty. We truncate doubles
+ * to ints then convert those ints back to doubles to make sure that
+ * they equal the doubles that we got in. */
+bool
+gfxUtils::GfxRectToIntRect(const gfxRect& aIn, IntRect* aOut)
+{
+ *aOut = IntRect(int32_t(aIn.X()), int32_t(aIn.Y()),
+ int32_t(aIn.Width()), int32_t(aIn.Height()));
+ return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn);
+}
+
+/* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface)
+{
+ if (aSurface->CairoStatus()) {
+ return;
+ }
+ cairo_surface_t* surf = aSurface->CairoSurface();
+ if (cairo_surface_status(surf)) {
+ return;
+ }
+ cairo_t* ctx = cairo_create(surf);
+ cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize());
+ cairo_rectangle(ctx, bounds.x, bounds.y, bounds.width, bounds.height);
+ cairo_fill(ctx);
+ cairo_destroy(ctx);
+}
+
+/* static */ already_AddRefed<DataSourceSurface>
+gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
+ SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aFormat != aSurface->GetFormat(),
+ "Unnecessary - and very expersive - surface format conversion");
+
+ Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
+
+ if (aSurface->GetType() != SurfaceType::DATA) {
+ // If the surface is NOT of type DATA then its data is not mapped into main
+ // memory. Format conversion is probably faster on the GPU, and by doing it
+ // there we can avoid any expensive uploads/readbacks except for (possibly)
+ // a single readback due to the unavoidable GetDataSurface() call. Using
+ // CreateOffscreenContentDrawTarget ensures the conversion happens on the
+ // GPU.
+ RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
+ CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat);
+ if (!dt) {
+ gfxWarning() << "gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat failed in CreateOffscreenContentDrawTarget";
+ return nullptr;
+ }
+
+ // Using DrawSurface() here rather than CopySurface() because CopySurface
+ // is optimized for memcpy and therefore isn't good for format conversion.
+ // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
+ // generally more optimized.
+ dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_OVER));
+ RefPtr<SourceSurface> surface = dt->Snapshot();
+ return surface->GetDataSurface();
+ }
+
+ // If the surface IS of type DATA then it may or may not be in main memory
+ // depending on whether or not it has been mapped yet. We have no way of
+ // knowing, so we can't be sure if it's best to create a data wrapping
+ // DrawTarget for the conversion or an offscreen content DrawTarget. We could
+ // guess it's not mapped and create an offscreen content DrawTarget, but if
+ // it is then we'll end up uploading the surface data, and most likely the
+ // caller is going to be accessing the resulting surface data, resulting in a
+ // readback (both very expensive operations). Alternatively we could guess
+ // the data is mapped and create a data wrapping DrawTarget and, if the
+ // surface is not in main memory, then we will incure a readback. The latter
+ // of these two "wrong choices" is the least costly (a readback, vs an
+ // upload and a readback), and more than likely the DATA surface that we've
+ // been passed actually IS in main memory anyway. For these reasons it's most
+ // likely best to create a data wrapping DrawTarget here to do the format
+ // conversion.
+ RefPtr<DataSourceSurface> dataSurface =
+ Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat);
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurface ||
+ !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+ return nullptr;
+ }
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ map.mData,
+ dataSurface->GetSize(),
+ map.mStride,
+ aFormat);
+ if (!dt) {
+ dataSurface->Unmap();
+ return nullptr;
+ }
+ // Using DrawSurface() here rather than CopySurface() because CopySurface
+ // is optimized for memcpy and therefore isn't good for format conversion.
+ // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
+ // generally more optimized.
+ dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_OVER));
+ dataSurface->Unmap();
+ return dataSurface.forget();
+}
+
+const uint32_t gfxUtils::sNumFrameColors = 8;
+
+/* static */ const gfx::Color&
+gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
+{
+ static bool initialized = false;
+ static gfx::Color colors[sNumFrameColors];
+
+ if (!initialized) {
+ uint32_t i = 0;
+ colors[i++] = gfx::Color::FromABGR(0xffff0000);
+ colors[i++] = gfx::Color::FromABGR(0xffcc00ff);
+ colors[i++] = gfx::Color::FromABGR(0xff0066cc);
+ colors[i++] = gfx::Color::FromABGR(0xff00ff00);
+ colors[i++] = gfx::Color::FromABGR(0xff33ffff);
+ colors[i++] = gfx::Color::FromABGR(0xffff0099);
+ colors[i++] = gfx::Color::FromABGR(0xff0000ff);
+ colors[i++] = gfx::Color::FromABGR(0xff999999);
+ MOZ_ASSERT(i == sNumFrameColors);
+ initialized = true;
+ }
+
+ return colors[aFrameNumber % sNumFrameColors];
+}
+
+static nsresult
+EncodeSourceSurfaceInternal(SourceSurface* aSurface,
+ const nsACString& aMimeType,
+ const nsAString& aOutputOptions,
+ gfxUtils::BinaryOrData aBinaryOrData,
+ FILE* aFile,
+ nsCString* aStrOut)
+{
+ MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
+ "Copying binary encoding to clipboard not currently supported");
+
+ const IntSize size = aSurface->GetSize();
+ if (size.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ const Size floatSize(size.width, size.height);
+
+ RefPtr<DataSourceSurface> dataSurface;
+ if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
+ // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
+ dataSurface =
+ gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
+ SurfaceFormat::B8G8R8A8);
+ } else {
+ dataSurface = aSurface->GetDataSurface();
+ }
+ if (!dataSurface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString encoderCID(
+ NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
+ nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
+ if (!encoder) {
+#ifdef DEBUG
+ int32_t w = std::min(size.width, 8);
+ int32_t h = std::min(size.height, 8);
+ printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
+ for (int32_t y = 0; y < h; ++y) {
+ for (int32_t x = 0; x < w; ++x) {
+ printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
+ }
+ }
+#endif
+ dataSurface->Unmap();
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = encoder->InitFromData(map.mData,
+ BufferSizeFromStrideAndHeight(map.mStride, size.height),
+ size.width,
+ size.height,
+ map.mStride,
+ imgIEncoder::INPUT_FORMAT_HOSTARGB,
+ aOutputOptions);
+ dataSurface->Unmap();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> imgStream;
+ CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
+ if (!imgStream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint64_t bufSize64;
+ rv = imgStream->Available(&bufSize64);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
+
+ uint32_t bufSize = (uint32_t)bufSize64;
+
+ // ...leave a little extra room so we can call read again and make sure we
+ // got everything. 16 bytes for better padding (maybe)
+ bufSize += 16;
+ uint32_t imgSize = 0;
+ Vector<char> imgData;
+ if (!imgData.initCapacity(bufSize)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint32_t numReadThisTime = 0;
+ while ((rv = imgStream->Read(imgData.begin() + imgSize,
+ bufSize - imgSize,
+ &numReadThisTime)) == NS_OK && numReadThisTime > 0)
+ {
+ // Update the length of the vector without overwriting the new data.
+ if (!imgData.growByUninitialized(numReadThisTime)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ imgSize += numReadThisTime;
+ if (imgSize == bufSize) {
+ // need a bigger buffer, just double
+ bufSize *= 2;
+ if (!imgData.resizeUninitialized(bufSize)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
+
+ if (aBinaryOrData == gfxUtils::eBinaryEncode) {
+ if (aFile) {
+ fwrite(imgData.begin(), 1, imgSize, aFile);
+ }
+ return NS_OK;
+ }
+
+ // base 64, result will be null-terminated
+ nsCString encodedImg;
+ rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString string("data:");
+ string.Append(aMimeType);
+ string.Append(";base64,");
+ string.Append(encodedImg);
+
+ if (aFile) {
+#ifdef ANDROID
+ if (aFile == stdout || aFile == stderr) {
+ // ADB logcat cuts off long strings so we will break it down
+ const char* cStr = string.BeginReading();
+ size_t len = strlen(cStr);
+ while (true) {
+ printf_stderr("IMG: %.140s\n", cStr);
+ if (len <= 140)
+ break;
+ len -= 140;
+ cStr += 140;
+ }
+ }
+#endif
+ fprintf(aFile, "%s", string.BeginReading());
+ } else if (aStrOut) {
+ *aStrOut = string;
+ } else {
+ nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
+ if (clipboard) {
+ clipboard->CopyString(NS_ConvertASCIItoUTF16(string));
+ }
+ }
+ return NS_OK;
+}
+
+static nsCString
+EncodeSourceSurfaceAsPNGURI(SourceSurface* aSurface)
+{
+ nsCString string;
+ EncodeSourceSurfaceInternal(aSurface, NS_LITERAL_CSTRING("image/png"),
+ EmptyString(), gfxUtils::eDataURIEncode,
+ nullptr, &string);
+ return string;
+}
+
+/* static */ nsresult
+gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
+ const nsACString& aMimeType,
+ const nsAString& aOutputOptions,
+ BinaryOrData aBinaryOrData,
+ FILE* aFile)
+{
+ return EncodeSourceSurfaceInternal(aSurface, aMimeType, aOutputOptions,
+ aBinaryOrData, aFile, nullptr);
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+
+/* static */ float*
+gfxUtils::Get4x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
+{
+ static const float yuv_to_rgb_rec601[12] = { 1.16438f, 0.0f, 1.59603f, 0.0f,
+ 1.16438f, -0.39176f, -0.81297f, 0.0f,
+ 1.16438f, 2.01723f, 0.0f, 0.0f,
+ };
+
+ static const float yuv_to_rgb_rec709[12] = { 1.16438f, 0.0f, 1.79274f, 0.0f,
+ 1.16438f, -0.21325f, -0.53291f, 0.0f,
+ 1.16438f, 2.11240f, 0.0f, 0.0f,
+ };
+
+ if (aYUVColorSpace == YUVColorSpace::BT709) {
+ return const_cast<float*>(yuv_to_rgb_rec709);
+ } else {
+ return const_cast<float*>(yuv_to_rgb_rec601);
+ }
+}
+
+/* static */ float*
+gfxUtils::Get3x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
+{
+ static const float yuv_to_rgb_rec601[9] = {
+ 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.39176f, 2.01723f, 1.59603f, -0.81297f, 0.0f,
+ };
+ static const float yuv_to_rgb_rec709[9] = {
+ 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.21325f, 2.11240f, 1.79274f, -0.53291f, 0.0f,
+ };
+
+ if (aYUVColorSpace == YUVColorSpace::BT709) {
+ return const_cast<float*>(yuv_to_rgb_rec709);
+ } else {
+ return const_cast<float*>(yuv_to_rgb_rec601);
+ }
+}
+
+/* static */ void
+gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
+{
+ WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
+}
+
+/* static */ void
+gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
+{
+ FILE* file = fopen(aFile, "wb");
+
+ if (!file) {
+ // Maybe the directory doesn't exist; try creating it, then fopen again.
+ nsresult rv = NS_ERROR_FAILURE;
+ nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
+ if (comFile) {
+ NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
+ rv = comFile->InitWithPath(utf16path);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> dirPath;
+ comFile->GetParent(getter_AddRefs(dirPath));
+ if (dirPath) {
+ rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
+ if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
+ file = fopen(aFile, "wb");
+ }
+ }
+ }
+ }
+ if (!file) {
+ NS_WARNING("Failed to open file to create PNG!");
+ return;
+ }
+ }
+
+ EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
+ EmptyString(), eBinaryEncode, file);
+ fclose(file);
+}
+
+/* static */ void
+gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
+{
+ WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
+}
+
+/* static */ void
+gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
+{
+ RefPtr<SourceSurface> surface = aDT->Snapshot();
+ if (surface) {
+ WriteAsPNG(surface, aFile);
+ } else {
+ NS_WARNING("Failed to get surface!");
+ }
+}
+
+/* static */ void
+gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
+{
+ int32_t width = 1000, height = 1000;
+ nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
+ aShell->GetPresContext()->DevPixelsToAppUnits(height));
+
+ RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
+ CreateOffscreenContentDrawTarget(IntSize(width, height),
+ SurfaceFormat::B8G8R8A8);
+ NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
+
+ RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(context); // already checked the draw target above
+ aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
+ WriteAsPNG(dt.get(), aFile);
+}
+
+/* static */ void
+gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
+{
+ EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
+ EmptyString(), eDataURIEncode, aFile);
+}
+
+/* static */ nsCString
+gfxUtils::GetAsDataURI(SourceSurface* aSurface)
+{
+ return EncodeSourceSurfaceAsPNGURI(aSurface);
+}
+
+/* static */ void
+gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
+{
+ RefPtr<SourceSurface> surface = aDT->Snapshot();
+ if (surface) {
+ DumpAsDataURI(surface, aFile);
+ } else {
+ NS_WARNING("Failed to get surface!");
+ }
+}
+
+/* static */ nsCString
+gfxUtils::GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface)
+{
+ int32_t dataSize = aSourceSurface->GetSize().height * aSourceSurface->Stride();
+ auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(dataSize));
+ if (compressedData) {
+ int nDataSize = LZ4::compress((char*)aSourceSurface->GetData(),
+ dataSize,
+ compressedData.get());
+ if (nDataSize > 0) {
+ nsCString encodedImg;
+ nsresult rv = Base64Encode(Substring(compressedData.get(), nDataSize), encodedImg);
+ if (rv == NS_OK) {
+ nsCString string("");
+ string.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,",
+ aSourceSurface->GetSize().width,
+ aSourceSurface->Stride(),
+ aSourceSurface->GetSize().height);
+ string.Append(encodedImg);
+ return string;
+ }
+ }
+ }
+ return nsCString("");
+}
+
+/* static */ nsCString
+gfxUtils::GetAsDataURI(DrawTarget* aDT)
+{
+ RefPtr<SourceSurface> surface = aDT->Snapshot();
+ if (surface) {
+ return EncodeSourceSurfaceAsPNGURI(surface);
+ } else {
+ NS_WARNING("Failed to get surface!");
+ return nsCString("");
+ }
+}
+
+/* static */ void
+gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
+{
+ EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
+ EmptyString(), eDataURIEncode, nullptr);
+}
+
+/* static */ void
+gfxUtils::CopyAsDataURI(DrawTarget* aDT)
+{
+ RefPtr<SourceSurface> surface = aDT->Snapshot();
+ if (surface) {
+ CopyAsDataURI(surface);
+ } else {
+ NS_WARNING("Failed to get surface!");
+ }
+}
+
+/* static */ UniquePtr<uint8_t[]>
+gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
+ bool aIsAlphaPremultiplied,
+ int32_t* outFormat)
+{
+ *outFormat = 0;
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
+ return nullptr;
+
+ uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
+ auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
+ if (!imageBuffer) {
+ aSurface->Unmap();
+ return nullptr;
+ }
+ memcpy(imageBuffer.get(), map.mData, bufferSize);
+
+ aSurface->Unmap();
+
+ int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+ if (!aIsAlphaPremultiplied) {
+ // We need to convert to INPUT_FORMAT_RGBA, otherwise
+ // we are automatically considered premult, and unpremult'd.
+ // Yes, it is THAT silly.
+ // Except for different lossy conversions by color,
+ // we could probably just change the label, and not change the data.
+ gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize);
+ format = imgIEncoder::INPUT_FORMAT_RGBA;
+ }
+
+ *outFormat = format;
+ return imageBuffer;
+}
+
+/* static */ nsresult
+gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
+ bool aIsAlphaPremultiplied,
+ const char* aMimeType,
+ const char16_t* aEncoderOptions,
+ nsIInputStream** outStream)
+{
+ nsCString enccid("@mozilla.org/image/encoder;2?type=");
+ enccid += aMimeType;
+ nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
+ if (!encoder)
+ return NS_ERROR_FAILURE;
+
+ int32_t format = 0;
+ UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format);
+ if (!imageBuffer)
+ return NS_ERROR_FAILURE;
+
+ return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
+ aSurface->GetSize().height,
+ imageBuffer.get(), format,
+ encoder, aEncoderOptions, outStream);
+}
+
+class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
+{
+public:
+ GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ int32_t feature,
+ nsACString& failureId,
+ int32_t* status)
+ : WorkerMainThreadRunnable(workerPrivate,
+ NS_LITERAL_CSTRING("GFX :: GetFeatureStatus"))
+ , mGfxInfo(gfxInfo)
+ , mFeature(feature)
+ , mStatus(status)
+ , mFailureId(failureId)
+ , mNSResult(NS_OK)
+ {
+ }
+
+ bool MainThreadRun() override
+ {
+ if (mGfxInfo) {
+ mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus);
+ }
+ return true;
+ }
+
+ nsresult GetNSResult() const
+ {
+ return mNSResult;
+ }
+
+protected:
+ ~GetFeatureStatusRunnable() {}
+
+private:
+ nsCOMPtr<nsIGfxInfo> mGfxInfo;
+ int32_t mFeature;
+ int32_t* mStatus;
+ nsACString& mFailureId;
+ nsresult mNSResult;
+};
+
+/* static */ nsresult
+gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ int32_t feature, nsACString& failureId,
+ int32_t* status)
+{
+ if (!NS_IsMainThread()) {
+ dom::workers::WorkerPrivate* workerPrivate =
+ dom::workers::GetCurrentThreadWorkerPrivate();
+
+ RefPtr<GetFeatureStatusRunnable> runnable =
+ new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, failureId,
+ status);
+
+ ErrorResult rv;
+ runnable->Dispatch(dom::workers::Terminating, rv);
+ if (rv.Failed()) {
+ // XXXbz This is totally broken, since we're supposed to just abort
+ // everything up the callstack but the callers basically eat the
+ // exception. Ah, well.
+ return rv.StealNSResult();
+ }
+
+ return runnable->GetNSResult();
+ }
+
+ return gfxInfo->GetFeatureStatus(feature, failureId, status);
+}
+
+/* static */ bool
+gfxUtils::IsFeatureBlacklisted(nsCOMPtr<nsIGfxInfo> gfxInfo, int32_t feature,
+ nsACString* const out_blacklistId)
+{
+ if (!gfxInfo) {
+ gfxInfo = services::GetGfxInfo();
+ }
+
+ int32_t status;
+ if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
+ *out_blacklistId, &status)))
+ {
+ out_blacklistId->AssignLiteral("");
+ return true;
+ }
+
+ return status != nsIGfxInfo::FEATURE_STATUS_OK;
+}
+
+/* static */ bool
+gfxUtils::DumpDisplayList() {
+ return gfxPrefs::LayoutDumpDisplayList() ||
+ (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
+}
+
+FILE *gfxUtils::sDumpPaintFile = stderr;
+
+namespace mozilla {
+namespace gfx {
+
+Color ToDeviceColor(Color aColor)
+{
+ // aColor is pass-by-value since to get return value optimization goodness we
+ // need to return the same object from all return points in this function. We
+ // could declare a local Color variable and use that, but we might as well
+ // just use aColor.
+ if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
+ qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
+ if (transform) {
+ gfxPlatform::TransformPixel(aColor, aColor, transform);
+ // Use the original alpha to avoid unnecessary float->byte->float
+ // conversion errors
+ }
+ }
+ return aColor;
+}
+
+Color ToDeviceColor(nscolor aColor)
+{
+ return ToDeviceColor(Color::FromABGR(aColor));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/system/graphics/thebes/gfxUtils.h b/system/graphics/thebes/gfxUtils.h
new file mode 100644
index 000000000..7a4679fb9
--- /dev/null
+++ b/system/graphics/thebes/gfxUtils.h
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_UTILS_H
+#define GFX_UTILS_H
+
+#include "gfxTypes.h"
+#include "ImageTypes.h"
+#include "imgIContainer.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsColor.h"
+#include "nsPrintfCString.h"
+#include "nsRegionFwd.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/CheckedInt.h"
+
+class gfxASurface;
+class gfxDrawable;
+class nsIInputStream;
+class nsIGfxInfo;
+class nsIPresShell;
+
+namespace mozilla {
+namespace layers {
+struct PlanarYCbCrData;
+} // namespace layers
+namespace image {
+class ImageRegion;
+} // namespace image
+} // namespace mozilla
+
+class gfxUtils {
+public:
+ typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntPoint IntPoint;
+ typedef mozilla::gfx::Matrix Matrix;
+ typedef mozilla::gfx::SourceSurface SourceSurface;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+ typedef mozilla::image::ImageRegion ImageRegion;
+ typedef mozilla::YUVColorSpace YUVColorSpace;
+
+ /*
+ * Premultiply or Unpremultiply aSourceSurface, writing the result
+ * to aDestSurface or back into aSourceSurface if aDestSurface is null.
+ *
+ * If aDestSurface is given, it must have identical format, dimensions, and
+ * stride as the source.
+ *
+ * If the source is not SurfaceFormat::A8R8G8B8_UINT32, no operation is performed. If
+ * aDestSurface is given, the data is copied over.
+ */
+ static bool PremultiplyDataSurface(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf);
+ static bool UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
+ DataSourceSurface* destSurf);
+
+ static already_AddRefed<DataSourceSurface>
+ CreatePremultipliedDataSurface(DataSourceSurface* srcSurf);
+ static already_AddRefed<DataSourceSurface>
+ CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf);
+
+ static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
+
+ /**
+ * Draw something drawable while working around limitations like bad support
+ * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
+ * extreme user-space-to-image-space transforms.
+ *
+ * The input parameters here usually come from the output of our image
+ * snapping algorithm in nsLayoutUtils.cpp.
+ * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
+ * adjusting the parameters. For example, certain images with transparent
+ * margins only have a drawable subimage. For those images, imgFrame::Draw
+ * will tweak the rects and transforms that it gets from the pixel snapping
+ * algorithm before passing them on to this method.
+ */
+ static void DrawPixelSnapped(gfxContext* aContext,
+ gfxDrawable* aDrawable,
+ const gfxSize& aImageSize,
+ const ImageRegion& aRegion,
+ const mozilla::gfx::SurfaceFormat aFormat,
+ mozilla::gfx::SamplingFilter aSamplingFilter,
+ uint32_t aImageFlags = imgIContainer::FLAG_NONE,
+ gfxFloat aOpacity = 1.0);
+
+ /**
+ * Clip aContext to the region aRegion.
+ */
+ static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
+
+ /**
+ * Clip aTarget to the region aRegion.
+ */
+ static void ClipToRegion(mozilla::gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
+
+ /*
+ * Convert image format to depth value
+ */
+ static int ImageFormatToDepth(gfxImageFormat aFormat);
+
+ /**
+ * Return the transform matrix that maps aFrom to the rectangle defined by
+ * aToTopLeft/aToTopRight/aToBottomRight. aFrom must be
+ * nonempty and the destination rectangle must be axis-aligned.
+ */
+ static gfxMatrix TransformRectToRect(const gfxRect& aFrom,
+ const gfxPoint& aToTopLeft,
+ const gfxPoint& aToTopRight,
+ const gfxPoint& aToBottomRight);
+
+ static Matrix TransformRectToRect(const gfxRect& aFrom,
+ const IntPoint& aToTopLeft,
+ const IntPoint& aToTopRight,
+ const IntPoint& aToBottomRight);
+
+ /**
+ * If aIn can be represented exactly using an gfx::IntRect (i.e.
+ * integer-aligned edges and coordinates in the int32_t range) then we
+ * set aOut to that rectangle, otherwise return failure.
+ */
+ static bool GfxRectToIntRect(const gfxRect& aIn, mozilla::gfx::IntRect* aOut);
+
+ /**
+ * Return the smallest power of kScaleResolution (2) greater than or equal to
+ * aVal.
+ */
+ static gfxFloat ClampToScaleFactor(gfxFloat aVal);
+
+ /**
+ * Clears surface to aColor (which defaults to transparent black).
+ */
+ static void ClearThebesSurface(gfxASurface* aSurface);
+
+ /**
+ * Get array of yuv to rgb conversion matrix.
+ */
+ static float* Get4x3YuvColorMatrix(YUVColorSpace aYUVColorSpace);
+
+ static float* Get3x3YuvColorMatrix(YUVColorSpace aYUVColorSpace);
+
+ /**
+ * Creates a copy of aSurface, but having the SurfaceFormat aFormat.
+ *
+ * This function always creates a new surface. Do not call it if aSurface's
+ * format is the same as aFormat. Such a non-conversion would just be an
+ * unnecessary and wasteful copy (this function asserts to prevent that).
+ *
+ * This function is intended to be called by code that needs to access the
+ * pixel data of the surface, but doesn't want to have lots of branches
+ * to handle different pixel data formats (code which would become out of
+ * date if and when new formats are added). Callers can use this function
+ * to copy the surface to a specified format so that they only have to
+ * handle pixel data in that one format.
+ *
+ * WARNING: There are format conversions that will not be supported by this
+ * function. It very much depends on what the Moz2D backends support. If
+ * the temporary B8G8R8A8 DrawTarget that this function creates has a
+ * backend that supports DrawSurface() calls passing a surface with
+ * aSurface's format it will work. Otherwise it will not.
+ *
+ * *** IMPORTANT PERF NOTE ***
+ *
+ * This function exists partly because format conversion is fraught with
+ * non-obvious performance hazards, so we don't want Moz2D consumers to be
+ * doing their own format conversion. Do not try to do so, or at least read
+ * the comments in this functions implemtation. That said, the copy that
+ * this function carries out has a cost and, although this function tries
+ * to avoid perf hazards such as expensive uploads to/readbacks from the
+ * GPU, it can't guarantee that it always successfully does so. Perf
+ * critical code that can directly handle the common formats that it
+ * encounters in a way that is cheaper than a copy-with-format-conversion
+ * should consider doing so, and only use this function as a fallback to
+ * handle other formats.
+ *
+ * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a
+ * SurfaceFormat argument (with a default argument meaning "use the
+ * existing surface's format") and returned a DataSourceSurface in that
+ * format. (There would then be an issue of callers maybe failing to
+ * realize format conversion may involve expensive copying/uploading/
+ * readback.)
+ */
+ static already_AddRefed<DataSourceSurface>
+ CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
+ SurfaceFormat aFormat);
+
+ static const uint8_t sUnpremultiplyTable[256*256];
+ static const uint8_t sPremultiplyTable[256*256];
+
+ /**
+ * Return a color that can be used to identify a frame with a given frame number.
+ * The colors will cycle after sNumFrameColors. You can query colors 0 .. sNumFrameColors-1
+ * to get all the colors back.
+ */
+ static const mozilla::gfx::Color& GetColorForFrameNumber(uint64_t aFrameNumber);
+ static const uint32_t sNumFrameColors;
+
+
+ enum BinaryOrData {
+ eBinaryEncode,
+ eDataURIEncode
+ };
+
+ /**
+ * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
+ *
+ * @param aMimeType The MIME-type of the image type that the surface is to
+ * be encoded to. Used to create an appropriate imgIEncoder instance to
+ * do the encoding.
+ *
+ * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
+ * the value of the |outputOptions| parameter. Callers are responsible
+ * for making sure that this is a sane value for the passed MIME-type
+ * (i.e. for the type of encoder that will be created).
+ *
+ * @aBinaryOrData Flag used to determine if the surface is simply encoded
+ * to the requested binary image format, or if the binary image is
+ * further converted to base-64 and written out as a 'data:' URI.
+ *
+ * @aFile If specified, the encoded data is written out to aFile, otherwise
+ * it is copied to the clipboard.
+ *
+ * TODO: Copying to the clipboard as a binary file is not currently
+ * supported.
+ */
+ static nsresult
+ EncodeSourceSurface(SourceSurface* aSurface,
+ const nsACString& aMimeType,
+ const nsAString& aOutputOptions,
+ BinaryOrData aBinaryOrData,
+ FILE* aFile);
+
+ /**
+ * Write as a PNG file to the path aFile.
+ */
+ static void WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile);
+ static void WriteAsPNG(SourceSurface* aSurface, const char* aFile);
+ static void WriteAsPNG(DrawTarget* aDT, const nsAString& aFile);
+ static void WriteAsPNG(DrawTarget* aDT, const char* aFile);
+ static void WriteAsPNG(nsIPresShell* aShell, const char* aFile);
+
+ /**
+ * Dump as a PNG encoded Data URL to a FILE stream (using stdout by
+ * default).
+ *
+ * Rather than giving aFile a default argument we have separate functions
+ * to make them easier to use from a debugger.
+ */
+ static void DumpAsDataURI(SourceSurface* aSourceSurface, FILE* aFile);
+ static inline void DumpAsDataURI(SourceSurface* aSourceSurface) {
+ DumpAsDataURI(aSourceSurface, stdout);
+ }
+ static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
+ static inline void DumpAsDataURI(DrawTarget* aDT) {
+ DumpAsDataURI(aDT, stdout);
+ }
+ static nsCString GetAsDataURI(SourceSurface* aSourceSurface);
+ static nsCString GetAsDataURI(DrawTarget* aDT);
+ static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
+
+ static mozilla::UniquePtr<uint8_t[]> GetImageBuffer(DataSourceSurface* aSurface,
+ bool aIsAlphaPremultiplied,
+ int32_t* outFormat);
+
+ static nsresult GetInputStream(DataSourceSurface* aSurface,
+ bool aIsAlphaPremultiplied,
+ const char* aMimeType,
+ const char16_t* aEncoderOptions,
+ nsIInputStream** outStream);
+
+ static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ int32_t feature,
+ nsACString& failureId,
+ int32_t* status);
+
+ // Can pass `nullptr` for gfxInfo.
+ // If FAILED(ThreadSafeGetFeatureStatus), out_blacklistId will be empty.
+ static bool IsFeatureBlacklisted(nsCOMPtr<nsIGfxInfo> gfxInfo, int32_t feature,
+ nsACString* const out_blacklistId);
+
+ /**
+ * Copy to the clipboard as a PNG encoded Data URL.
+ */
+ static void CopyAsDataURI(SourceSurface* aSourceSurface);
+ static void CopyAsDataURI(DrawTarget* aDT);
+
+ static bool DumpDisplayList();
+
+ static FILE* sDumpPaintFile;
+};
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * If the CMS mode is eCMSMode_All, these functions transform the passed
+ * color to a device color using the transform returened by gfxPlatform::
+ * GetCMSRGBTransform(). If the CMS mode is some other value, the color is
+ * returned unchanged (other than a type change to Moz2D Color, if
+ * applicable).
+ */
+Color ToDeviceColor(Color aColor);
+Color ToDeviceColor(nscolor aColor);
+
+/**
+ * Performs a checked multiply of the given width, height, and bytes-per-pixel
+ * values.
+ */
+static inline CheckedInt<uint32_t>
+SafeBytesForBitmap(uint32_t aWidth, uint32_t aHeight, unsigned aBytesPerPixel)
+{
+ MOZ_ASSERT(aBytesPerPixel > 0);
+ CheckedInt<uint32_t> width = uint32_t(aWidth);
+ CheckedInt<uint32_t> height = uint32_t(aHeight);
+ return width * height * aBytesPerPixel;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/thebes/gfxWindowsNativeDrawing.cpp b/system/graphics/thebes/gfxWindowsNativeDrawing.cpp
new file mode 100644
index 000000000..bd2f78d21
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsNativeDrawing.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+#include "nsMathUtils.h"
+
+#include "gfxWindowsNativeDrawing.h"
+#include "gfxWindowsSurface.h"
+#include "gfxAlphaRecovery.h"
+#include "gfxPattern.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
+#include "gfx2DGlue.h"
+
+#include "cairo.h"
+#include "cairo-win32.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+enum {
+ RENDER_STATE_INIT,
+
+ RENDER_STATE_NATIVE_DRAWING,
+ RENDER_STATE_NATIVE_DRAWING_DONE,
+
+ RENDER_STATE_ALPHA_RECOVERY_BLACK,
+ RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE,
+ RENDER_STATE_ALPHA_RECOVERY_WHITE,
+ RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE,
+
+ RENDER_STATE_DONE
+};
+
+gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext* ctx,
+ const gfxRect& nativeRect,
+ uint32_t nativeDrawFlags)
+ : mContext(ctx), mNativeRect(nativeRect), mNativeDrawFlags(nativeDrawFlags), mRenderState(RENDER_STATE_INIT)
+{
+}
+
+HDC
+gfxWindowsNativeDrawing::BeginNativeDrawing()
+{
+ if (mRenderState == RENDER_STATE_INIT) {
+ RefPtr<gfxASurface> surf;
+ DrawTarget* drawTarget = mContext->GetDrawTarget();
+ cairo_t* cairo = nullptr;
+ if (drawTarget->GetBackendType() == BackendType::CAIRO) {
+ cairo = static_cast<cairo_t*>
+ (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+ if (cairo) {
+ cairo_surface_t* s = cairo_get_group_target(cairo);
+ if (s) {
+ mDeviceOffset = mContext->GetDeviceOffset();
+ double sdx, sdy;
+ cairo_surface_get_device_offset(s, &sdx, &sdy);
+ mDeviceOffset.x -= sdx;
+ mDeviceOffset.y -= sdy;
+ surf = gfxASurface::Wrap(s);
+ }
+ }
+ }
+
+ if (surf && surf->CairoStatus() != 0)
+ return nullptr;
+
+ gfxMatrix m = mContext->CurrentMatrix();
+ if (!m.HasNonTranslation())
+ mTransformType = TRANSLATION_ONLY;
+ else if (m.HasNonAxisAlignedTransform())
+ mTransformType = COMPLEX;
+ else
+ mTransformType = AXIS_ALIGNED_SCALE;
+
+ // if this is a native win32 surface, we don't have to
+ // redirect rendering to our own HDC; in some cases,
+ // we may be able to use the HDC from the surface directly.
+ if (surf &&
+ ((surf->GetType() == gfxSurfaceType::Win32 ||
+ surf->GetType() == gfxSurfaceType::Win32Printing) &&
+ (surf->GetContentType() == gfxContentType::COLOR ||
+ (surf->GetContentType() == gfxContentType::COLOR_ALPHA &&
+ (mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA)))))
+ {
+ // grab the DC. This can fail if there is a complex clipping path,
+ // in which case we'll have to fall back.
+ mWinSurface = static_cast<gfxWindowsSurface*>(static_cast<gfxASurface*>(surf.get()));
+ mDC = cairo_win32_get_dc_with_clip(cairo);
+
+ if (mDC) {
+ if (mTransformType == TRANSLATION_ONLY) {
+ mRenderState = RENDER_STATE_NATIVE_DRAWING;
+
+ mTranslation = m.GetTranslation();
+ } else if (((mTransformType == AXIS_ALIGNED_SCALE)
+ && (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) ||
+ (mNativeDrawFlags & CAN_COMPLEX_TRANSFORM))
+ {
+ mWorldTransform.eM11 = (FLOAT) m._11;
+ mWorldTransform.eM12 = (FLOAT) m._12;
+ mWorldTransform.eM21 = (FLOAT) m._21;
+ mWorldTransform.eM22 = (FLOAT) m._22;
+ mWorldTransform.eDx = (FLOAT) m._31;
+ mWorldTransform.eDy = (FLOAT) m._32;
+
+ mRenderState = RENDER_STATE_NATIVE_DRAWING;
+ }
+ }
+ }
+
+ // If we couldn't do native drawing, then we have to do two-buffer drawing
+ // and do alpha recovery
+ if (mRenderState == RENDER_STATE_INIT) {
+ mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK;
+
+ // We round out our native rect here, that way the snapping will
+ // happen correctly.
+ mNativeRect.RoundOut();
+
+ // we only do the scale bit if we can do an axis aligned
+ // scale; otherwise we scale (if necessary) after
+ // rendering with cairo. Note that if we're doing alpha recovery,
+ // we cannot do a full complex transform with win32 (I mean, we could, but
+ // it would require more code that's not here.)
+ if (mTransformType == TRANSLATION_ONLY || !(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) {
+ mScale = gfxSize(1.0, 1.0);
+
+ // Add 1 to the surface size; it's guaranteed to not be incorrect,
+ // and it fixes bug 382458
+ // There's probably a better fix, but I haven't figured out
+ // the root cause of the problem.
+ mTempSurfaceSize =
+ IntSize((int32_t) ceil(mNativeRect.Width() + 1),
+ (int32_t) ceil(mNativeRect.Height() + 1));
+ } else {
+ // figure out the scale factors
+ mScale = m.ScaleFactors(true);
+
+ mWorldTransform.eM11 = (FLOAT) mScale.width;
+ mWorldTransform.eM12 = 0.0f;
+ mWorldTransform.eM21 = 0.0f;
+ mWorldTransform.eM22 = (FLOAT) mScale.height;
+ mWorldTransform.eDx = 0.0f;
+ mWorldTransform.eDy = 0.0f;
+
+ // See comment above about "+1"
+ mTempSurfaceSize =
+ IntSize((int32_t) ceil(mNativeRect.Width() * mScale.width + 1),
+ (int32_t) ceil(mNativeRect.Height() * mScale.height + 1));
+ }
+ }
+ }
+
+ if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
+ // we can just do native drawing directly to the context's surface
+
+ // do we need to use SetWorldTransform?
+ if (mTransformType != TRANSLATION_ONLY) {
+ SetGraphicsMode(mDC, GM_ADVANCED);
+ GetWorldTransform(mDC, &mOldWorldTransform);
+ SetWorldTransform(mDC, &mWorldTransform);
+ }
+ GetViewportOrgEx(mDC, &mOrigViewportOrigin);
+ SetViewportOrgEx(mDC,
+ mOrigViewportOrigin.x - (int)mDeviceOffset.x,
+ mOrigViewportOrigin.y - (int)mDeviceOffset.y,
+ nullptr);
+
+ return mDC;
+ } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK ||
+ mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE)
+ {
+ // we're going to use mWinSurface to create our temporary surface here
+
+ // get us a RGB24 DIB; DIB is important, because
+ // we can later call GetImageSurface on it.
+ mWinSurface = new gfxWindowsSurface(mTempSurfaceSize);
+ mDC = mWinSurface->GetDC();
+
+ RECT r = { 0, 0, mTempSurfaceSize.width, mTempSurfaceSize.height };
+ if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK)
+ FillRect(mDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ else
+ FillRect(mDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
+
+ if ((mTransformType != TRANSLATION_ONLY) &&
+ (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE))
+ {
+ SetGraphicsMode(mDC, GM_ADVANCED);
+ SetWorldTransform(mDC, &mWorldTransform);
+ }
+
+ return mDC;
+ } else {
+ NS_ERROR("Bogus render state!");
+ return nullptr;
+ }
+}
+
+bool
+gfxWindowsNativeDrawing::ShouldRenderAgain()
+{
+ switch (mRenderState) {
+ case RENDER_STATE_NATIVE_DRAWING_DONE:
+ return false;
+
+ case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
+ mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
+ return true;
+
+ case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE:
+ return false;
+
+ default:
+ NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
+ break;
+ }
+
+ return false;
+}
+
+void
+gfxWindowsNativeDrawing::EndNativeDrawing()
+{
+ if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
+ // we drew directly to the HDC in the context; undo our changes
+ SetViewportOrgEx(mDC, mOrigViewportOrigin.x, mOrigViewportOrigin.y, nullptr);
+
+ if (mTransformType != TRANSLATION_ONLY)
+ SetWorldTransform(mDC, &mOldWorldTransform);
+
+ mWinSurface->MarkDirty();
+
+ mRenderState = RENDER_STATE_NATIVE_DRAWING_DONE;
+ } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK) {
+ mBlackSurface = mWinSurface;
+ mWinSurface = nullptr;
+
+ mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE;
+ } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) {
+ mWhiteSurface = mWinSurface;
+ mWinSurface = nullptr;
+
+ mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE;
+ } else {
+ NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing");
+ }
+}
+
+void
+gfxWindowsNativeDrawing::PaintToContext()
+{
+ if (mRenderState == RENDER_STATE_NATIVE_DRAWING_DONE) {
+ // nothing to do, it already went to the context
+ mRenderState = RENDER_STATE_DONE;
+ } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE) {
+ RefPtr<gfxImageSurface> black = mBlackSurface->GetAsImageSurface();
+ RefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface();
+ if (!gfxAlphaRecovery::RecoverAlpha(black, white)) {
+ NS_ERROR("Alpha recovery failure");
+ return;
+ }
+ RefPtr<DataSourceSurface> source =
+ Factory::CreateWrappingDataSourceSurface(black->Data(),
+ black->Stride(),
+ black->GetSize(),
+ SurfaceFormat::B8G8R8A8);
+ {
+ DrawTarget* dt = mContext->GetDrawTarget();
+ AutoRestoreTransform autoRestoreTransform(dt);
+
+ Matrix newTransform = dt->GetTransform();
+ newTransform.PreTranslate(ToPoint(mNativeRect.TopLeft()));
+ dt->SetTransform(newTransform);
+
+ Rect rect(Point(0.0, 0.0), ToSize(mNativeRect.Size()));
+ Matrix m = Matrix::Scaling(1.0 / mScale.width, 1.0 / mScale.height);
+ SamplingFilter filter = (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING)
+ ? SamplingFilter::LINEAR
+ : SamplingFilter::GOOD;
+ SurfacePattern pat(source, ExtendMode::CLAMP, m, filter);
+ dt->FillRect(rect, pat);
+ }
+
+ mRenderState = RENDER_STATE_DONE;
+ } else {
+ NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
+ }
+}
+
+void
+gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect& r,
+ RECT& rout)
+{
+ /* If we're doing native drawing, then we're still in the coordinate space
+ * of the context; otherwise, we're in our own little world,
+ * relative to the passed-in nativeRect.
+ */
+
+ gfxRect roundedRect(r);
+
+ if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
+ if (mTransformType == TRANSLATION_ONLY) {
+ roundedRect.MoveBy(mTranslation);
+ }
+ } else {
+ roundedRect.MoveBy(-mNativeRect.TopLeft());
+ }
+
+ roundedRect.Round();
+
+ rout.left = LONG(roundedRect.X());
+ rout.right = LONG(roundedRect.XMost());
+ rout.top = LONG(roundedRect.Y());
+ rout.bottom = LONG(roundedRect.YMost());
+}
diff --git a/system/graphics/thebes/gfxWindowsNativeDrawing.h b/system/graphics/thebes/gfxWindowsNativeDrawing.h
new file mode 100644
index 000000000..25589fc5b
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsNativeDrawing.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _GFXWINDOWSNATIVEDRAWING_H_
+#define _GFXWINDOWSNATIVEDRAWING_H_
+
+#include <windows.h>
+
+#include "gfxContext.h"
+#include "gfxWindowsSurface.h"
+
+class gfxWindowsNativeDrawing {
+public:
+
+ /* Flags for notifying this class what kind of operations the native
+ * drawing supports
+ */
+
+ enum {
+ /* Whether the native drawing can draw to a surface of content COLOR_ALPHA */
+ CAN_DRAW_TO_COLOR_ALPHA = 1 << 0,
+ CANNOT_DRAW_TO_COLOR_ALPHA = 0 << 0,
+
+ /* Whether the native drawing can be scaled using SetWorldTransform */
+ CAN_AXIS_ALIGNED_SCALE = 1 << 1,
+ CANNOT_AXIS_ALIGNED_SCALE = 0 << 1,
+
+ /* Whether the native drawing can be both scaled and rotated arbitrarily using SetWorldTransform */
+ CAN_COMPLEX_TRANSFORM = 1 << 2,
+ CANNOT_COMPLEX_TRANSFORM = 0 << 2,
+
+ /* If we have to do transforms with cairo, should we use nearest-neighbour filtering? */
+ DO_NEAREST_NEIGHBOR_FILTERING = 1 << 3,
+ DO_BILINEAR_FILTERING = 0 << 3
+ };
+
+ /* Create native win32 drawing for a rectangle bounded by
+ * nativeRect.
+ *
+ * Typical usage looks like:
+ *
+ * gfxWindowsNativeDrawing nativeDraw(ctx, destGfxRect, capabilities);
+ * do {
+ * HDC dc = nativeDraw.BeginNativeDrawing();
+ * if (!dc)
+ * return NS_ERROR_FAILURE;
+ *
+ * RECT winRect;
+ * nativeDraw.TransformToNativeRect(rect, winRect);
+ *
+ * ... call win32 operations on HDC to draw to winRect ...
+ *
+ * nativeDraw.EndNativeDrawing();
+ * } while (nativeDraw.ShouldRenderAgain());
+ * nativeDraw.PaintToContext();
+ */
+ gfxWindowsNativeDrawing(gfxContext *ctx,
+ const gfxRect& nativeRect,
+ uint32_t nativeDrawFlags = CANNOT_DRAW_TO_COLOR_ALPHA |
+ CANNOT_AXIS_ALIGNED_SCALE |
+ CANNOT_COMPLEX_TRANSFORM |
+ DO_BILINEAR_FILTERING);
+
+ /* Returns a HDC which may be used for native drawing. This HDC is valid
+ * until EndNativeDrawing is called; if it is used for drawing after that time,
+ * the result is undefined. */
+ HDC BeginNativeDrawing();
+
+ /* Transform the native rect into something valid for rendering
+ * to the HDC. This may or may not change RECT, depending on
+ * whether SetWorldTransform is used or not. */
+ void TransformToNativeRect(const gfxRect& r, RECT& rout);
+
+ /* Marks the end of native drawing */
+ void EndNativeDrawing();
+
+ /* Returns true if the native drawing should be executed again */
+ bool ShouldRenderAgain();
+
+ /* Places the result to the context, if necessary */
+ void PaintToContext();
+
+private:
+
+ RefPtr<gfxContext> mContext;
+ gfxRect mNativeRect;
+ uint32_t mNativeDrawFlags;
+
+ // what state the rendering is in
+ uint8_t mRenderState;
+
+ mozilla::gfx::Point mDeviceOffset;
+ RefPtr<gfxPattern> mBlackPattern, mWhitePattern;
+
+ enum TransformType {
+ TRANSLATION_ONLY,
+ AXIS_ALIGNED_SCALE,
+ COMPLEX
+ };
+
+ TransformType mTransformType;
+ gfxPoint mTranslation;
+ gfxSize mScale;
+ XFORM mWorldTransform;
+
+ // saved state
+ RefPtr<gfxWindowsSurface> mWinSurface, mBlackSurface, mWhiteSurface;
+ HDC mDC;
+ XFORM mOldWorldTransform;
+ POINT mOrigViewportOrigin;
+ mozilla::gfx::IntSize mTempSurfaceSize;
+};
+
+#endif
diff --git a/system/graphics/thebes/gfxWindowsPlatform.cpp b/system/graphics/thebes/gfxWindowsPlatform.cpp
new file mode 100644
index 000000000..2a8634078
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsPlatform.cpp
@@ -0,0 +1,1983 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxWindowsPlatform.h"
+
+#include "cairo.h"
+#include "mozilla/ArrayUtils.h"
+
+#include "gfxImageSurface.h"
+#include "gfxWindowsSurface.h"
+
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsIGfxInfo.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+#include "GeckoProfiler.h"
+
+#include "nsIWindowsRegKey.h"
+#include "nsIFile.h"
+#include "plbase64.h"
+#include "nsIXULRuntime.h"
+#include "imgLoader.h"
+
+#include "nsIGfxInfo.h"
+
+#include "gfxGDIFontList.h"
+#include "gfxGDIFont.h"
+
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ReadbackManagerD3D11.h"
+
+#include "gfxDWriteFontList.h"
+#include "gfxDWriteFonts.h"
+#include "gfxDWriteCommon.h"
+#include <dwrite.h>
+
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+#include "nsWindowsHelpers.h"
+#include "gfx2DGlue.h"
+
+#include <string>
+
+#include <d3d10_1.h>
+
+#include "mozilla/gfx/2D.h"
+
+#include "nsMemory.h"
+
+#include <dwmapi.h>
+#include <d3d11.h>
+
+#include "nsIMemoryReporter.h"
+#include <winternl.h>
+#include "d3dkmtQueryStatistics.h"
+
+#include "base/thread.h"
+#include "gfxPrefs.h"
+#include "gfxConfig.h"
+#include "VsyncSource.h"
+#include "DriverCrashGuard.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "D3D11Checks.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+using namespace mozilla::image;
+using namespace mozilla::unicode;
+
+IDWriteRenderingParams* GetDwriteRenderingParams(bool aGDI)
+{
+ gfxWindowsPlatform::TextRenderingMode mode = aGDI ?
+ gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC :
+ gfxWindowsPlatform::TEXT_RENDERING_NORMAL;
+ return gfxWindowsPlatform::GetPlatform()->GetRenderingParams(mode);
+}
+
+DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
+{
+ mDC = nullptr;
+ if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
+ cairo_t* ctx = static_cast<cairo_t*>
+ (aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+ if (ctx) {
+ cairo_surface_t* surf = cairo_get_group_target(ctx);
+ if (surf) {
+ cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
+ if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
+ surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
+ mDC = cairo_win32_surface_get_dc(surf);
+ mNeedsRelease = false;
+ SaveDC(mDC);
+ cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
+ cairo_win32_scaled_font_select_font(scaled, mDC);
+ }
+ }
+ }
+ }
+
+ if (!mDC) {
+ // Get the whole screen DC:
+ mDC = GetDC(nullptr);
+ SetGraphicsMode(mDC, GM_ADVANCED);
+ mNeedsRelease = true;
+ }
+}
+
+class GfxD2DVramReporter final : public nsIMemoryReporter
+{
+ ~GfxD2DVramReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
+ Factory::GetD2DVRAMUsageDrawTarget(),
+ "Video memory used by D2D DrawTargets.");
+
+ MOZ_COLLECT_REPORT(
+ "gfx-d2d-vram-source-surface", KIND_OTHER, UNITS_BYTES,
+ Factory::GetD2DVRAMUsageSourceSurface(),
+ "Video memory used by D2D SourceSurfaces.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter)
+
+#define GFX_CLEARTYPE_PARAMS "gfx.font_rendering.cleartype_params."
+#define GFX_CLEARTYPE_PARAMS_GAMMA "gfx.font_rendering.cleartype_params.gamma"
+#define GFX_CLEARTYPE_PARAMS_CONTRAST "gfx.font_rendering.cleartype_params.enhanced_contrast"
+#define GFX_CLEARTYPE_PARAMS_LEVEL "gfx.font_rendering.cleartype_params.cleartype_level"
+#define GFX_CLEARTYPE_PARAMS_STRUCTURE "gfx.font_rendering.cleartype_params.pixel_structure"
+#define GFX_CLEARTYPE_PARAMS_MODE "gfx.font_rendering.cleartype_params.rendering_mode"
+
+class GPUAdapterReporter final : public nsIMemoryReporter
+{
+ // Callers must Release the DXGIAdapter after use or risk mem-leak
+ static bool GetDXGIAdapter(IDXGIAdapter **aDXGIAdapter)
+ {
+ ID3D11Device *d3d11Device;
+ IDXGIDevice *dxgiDevice;
+ bool result = false;
+
+ if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) {
+ if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice) == S_OK) {
+ result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK);
+ dxgiDevice->Release();
+ }
+ }
+
+ return result;
+ }
+
+ ~GPUAdapterReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ HANDLE ProcessHandle = GetCurrentProcess();
+
+ int64_t dedicatedBytesUsed = 0;
+ int64_t sharedBytesUsed = 0;
+ int64_t committedBytesUsed = 0;
+ IDXGIAdapter *DXGIAdapter;
+
+ HMODULE gdi32Handle;
+ PFND3DKMTQS queryD3DKMTStatistics = nullptr;
+
+ if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
+ queryD3DKMTStatistics = (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
+
+ if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
+ // Most of this block is understood thanks to wj32's work on Process Hacker
+
+ DXGI_ADAPTER_DESC adapterDesc;
+ D3DKMTQS queryStatistics;
+
+ DXGIAdapter->GetDesc(&adapterDesc);
+ DXGIAdapter->Release();
+
+ memset(&queryStatistics, 0, sizeof(D3DKMTQS));
+ queryStatistics.Type = D3DKMTQS_PROCESS;
+ queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
+ queryStatistics.hProcess = ProcessHandle;
+ if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
+ committedBytesUsed = queryStatistics.QueryResult.ProcessInfo.SystemMemory.BytesAllocated;
+ }
+
+ memset(&queryStatistics, 0, sizeof(D3DKMTQS));
+ queryStatistics.Type = D3DKMTQS_ADAPTER;
+ queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
+ if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
+ ULONG i;
+ ULONG segmentCount = queryStatistics.QueryResult.AdapterInfo.NbSegments;
+
+ for (i = 0; i < segmentCount; i++) {
+ memset(&queryStatistics, 0, sizeof(D3DKMTQS));
+ queryStatistics.Type = D3DKMTQS_SEGMENT;
+ queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
+ queryStatistics.QuerySegment.SegmentId = i;
+
+ if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
+ bool aperture;
+
+ // SegmentInformation has a different definition in Win7 than later versions
+ if (!IsWin8OrLater())
+ aperture = queryStatistics.QueryResult.SegmentInfoWin7.Aperture;
+ else
+ aperture = queryStatistics.QueryResult.SegmentInfoWin8.Aperture;
+
+ memset(&queryStatistics, 0, sizeof(D3DKMTQS));
+ queryStatistics.Type = D3DKMTQS_PROCESS_SEGMENT;
+ queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
+ queryStatistics.hProcess = ProcessHandle;
+ queryStatistics.QueryProcessSegment.SegmentId = i;
+ if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
+ ULONGLONG bytesCommitted;
+ if (!IsWin8OrLater())
+ bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInfo.Win7.BytesCommitted;
+ else
+ bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInfo.Win8.BytesCommitted;
+ if (aperture)
+ sharedBytesUsed += bytesCommitted;
+ else
+ dedicatedBytesUsed += bytesCommitted;
+ }
+ }
+ }
+ }
+ }
+
+ FreeLibrary(gdi32Handle);
+
+ MOZ_COLLECT_REPORT(
+ "gpu-committed", KIND_OTHER, UNITS_BYTES, committedBytesUsed,
+ "Memory committed by the Windows graphics system.");
+
+ MOZ_COLLECT_REPORT(
+ "gpu-dedicated", KIND_OTHER, UNITS_BYTES,
+ dedicatedBytesUsed,
+ "Out-of-process memory allocated for this process in a physical "
+ "GPU adapter's memory.");
+
+ MOZ_COLLECT_REPORT(
+ "gpu-shared", KIND_OTHER, UNITS_BYTES,
+ sharedBytesUsed,
+ "In-process memory that is shared with the GPU.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)
+
+Atomic<size_t> gfxWindowsPlatform::sD3D11SharedTextures;
+Atomic<size_t> gfxWindowsPlatform::sD3D9SharedTextures;
+
+class D3DSharedTexturesReporter final : public nsIMemoryReporter
+{
+ ~D3DSharedTexturesReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ if (gfxWindowsPlatform::sD3D11SharedTextures > 0) {
+ MOZ_COLLECT_REPORT(
+ "d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
+ gfxWindowsPlatform::sD3D11SharedTextures,
+ "D3D11 shared textures.");
+ }
+
+ if (gfxWindowsPlatform::sD3D9SharedTextures > 0) {
+ MOZ_COLLECT_REPORT(
+ "d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
+ gfxWindowsPlatform::sD3D9SharedTextures,
+ "D3D9 shared textures.");
+ }
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
+
+gfxWindowsPlatform::gfxWindowsPlatform()
+ : mRenderMode(RENDER_GDI)
+{
+ /*
+ * Initialize COM
+ */
+ CoInitialize(nullptr);
+
+ RegisterStrongMemoryReporter(new GfxD2DVramReporter());
+ RegisterStrongMemoryReporter(new GPUAdapterReporter());
+ RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
+}
+
+gfxWindowsPlatform::~gfxWindowsPlatform()
+{
+ mozilla::gfx::Factory::D2DCleanup();
+
+ DeviceManagerDx::Shutdown();
+
+ /*
+ * Uninitialize COM
+ */
+ CoUninitialize();
+}
+
+static void
+UpdateANGLEConfig()
+{
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled, "D3D11 compositing is disabled");
+ }
+}
+
+void
+gfxWindowsPlatform::InitAcceleration()
+{
+ gfxPlatform::InitAcceleration();
+
+ // Set up the D3D11 feature levels we can ask for.
+ if (IsWin8OrLater()) {
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
+ }
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
+
+ DeviceManagerDx::Init();
+
+ InitializeConfig();
+ InitializeDevices();
+ UpdateANGLEConfig();
+ UpdateRenderMode();
+
+ // If we have Skia and we didn't init dwrite already, do it now.
+ if (!mDWriteFactory && GetDefaultContentBackend() == BackendType::SKIA) {
+ InitDWriteSupport();
+ }
+
+ // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
+ // so update the cached value now.
+ UpdateCanUseHardwareVideoDecoding();
+}
+
+bool
+gfxWindowsPlatform::CanUseHardwareVideoDecoding()
+{
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ if (!dm) {
+ return false;
+ }
+ if (!dm->TextureSharingWorks()) {
+ return false;
+ }
+ return !dm->IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
+}
+
+bool
+gfxWindowsPlatform::InitDWriteSupport()
+{
+ // DWrite is only supported on Windows 7 with the platform update and higher.
+ // We check this by seeing if D2D1 support is available.
+ if (!Factory::SupportsD2D1()) {
+ return false;
+ }
+
+ decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
+ GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory");
+ if (!createDWriteFactory) {
+ return false;
+ }
+
+ // I need a direct pointer to be able to cast to IUnknown**, I also need to
+ // remember to release this because the nsRefPtr will AddRef it.
+ RefPtr<IDWriteFactory> factory;
+ HRESULT hr = createDWriteFactory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory),
+ (IUnknown **)((IDWriteFactory **)getter_AddRefs(factory)));
+ if (FAILED(hr) || !factory) {
+ return false;
+ }
+
+ mDWriteFactory = factory;
+ Factory::SetDWriteFactory(mDWriteFactory);
+
+ SetupClearTypeParams();
+ return true;
+}
+
+bool
+gfxWindowsPlatform::HandleDeviceReset()
+{
+ DeviceResetReason resetReason = DeviceResetReason::OK;
+ if (!DidRenderingDeviceReset(&resetReason)) {
+ return false;
+ }
+
+ // Remove devices and adapters.
+ DeviceManagerDx::Get()->ResetDevices();
+
+ imgLoader::NormalLoader()->ClearCache(true);
+ imgLoader::NormalLoader()->ClearCache(false);
+ imgLoader::PrivateBrowsingLoader()->ClearCache(true);
+ imgLoader::PrivateBrowsingLoader()->ClearCache(false);
+ gfxAlphaBoxBlur::ShutdownBlurCache();
+
+ if (XRE_IsContentProcess()) {
+ // Fetch updated device parameters.
+ FetchAndImportContentDeviceData();
+ UpdateANGLEConfig();
+ }
+
+ InitializeDevices();
+ UpdateANGLEConfig();
+ BumpDeviceCounter();
+ return true;
+}
+
+void
+gfxWindowsPlatform::UpdateBackendPrefs()
+{
+ uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+ uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+ BackendType defaultBackend = BackendType::CAIRO;
+ if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::GetD2D1Device()) {
+ contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+ canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+ defaultBackend = BackendType::DIRECT2D1_1;
+ } else {
+ canvasMask |= BackendTypeBit(BackendType::SKIA);
+ }
+ contentMask |= BackendTypeBit(BackendType::SKIA);
+ InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend);
+}
+
+bool
+gfxWindowsPlatform::IsDirect2DBackend()
+{
+ return GetDefaultContentBackend() == BackendType::DIRECT2D1_1;
+}
+
+void
+gfxWindowsPlatform::UpdateRenderMode()
+{
+ bool didReset = HandleDeviceReset();
+
+ UpdateBackendPrefs();
+
+ if (didReset) {
+ mScreenReferenceDrawTarget =
+ CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+ if (!mScreenReferenceDrawTarget) {
+ gfxCriticalNote << "Failed to update reference draw target after device reset"
+ << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device())
+ << ", D3D11 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::D3D11_COMPOSITING))
+ << ", D2D1 device:" << hexa(Factory::GetD2D1Device())
+ << ", D2D1 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D))
+ << ", content:" << int(GetDefaultContentBackend())
+ << ", compositor:" << int(GetCompositorBackend());
+ MOZ_CRASH("GFX: Failed to update reference draw target after device reset");
+ }
+ }
+}
+
+mozilla::gfx::BackendType
+gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
+{
+ mozilla::gfx::BackendType defaultBackend = gfxPlatform::GetDefaultContentBackend();
+ if (aLayers == LayersBackend::LAYERS_D3D11) {
+ return defaultBackend;
+ }
+
+ if (defaultBackend == BackendType::DIRECT2D1_1) {
+ // We can't have D2D without D3D11 layers, so fallback to Cairo.
+ return BackendType::CAIRO;
+ }
+
+ // Otherwise we have some non-accelerated backend and that's ok.
+ return defaultBackend;
+}
+
+gfxPlatformFontList*
+gfxWindowsPlatform::CreatePlatformFontList()
+{
+ gfxPlatformFontList *pfl;
+
+ // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
+ // crashers so blacklist them altogether
+ if (IsNotWin7PreRTM() && GetDWriteFactory()) {
+ pfl = new gfxDWriteFontList();
+ if (NS_SUCCEEDED(pfl->InitFontList())) {
+ return pfl;
+ }
+ // DWrite font initialization failed! Don't know why this would happen,
+ // but apparently it can - see bug 594865.
+ // So we're going to fall back to GDI fonts & rendering.
+ gfxPlatformFontList::Shutdown();
+ DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_FONT_FAIL"));
+ }
+
+ pfl = new gfxGDIFontList();
+
+ if (NS_SUCCEEDED(pfl->InitFontList())) {
+ return pfl;
+ }
+
+ gfxPlatformFontList::Shutdown();
+ return nullptr;
+}
+
+// This function will permanently disable D2D for the session. It's intended to
+// be used when, after initially chosing to use Direct2D, we encounter a
+// scenario we can't support.
+//
+// This is called during gfxPlatform::Init() so at this point there should be no
+// DrawTargetD2D/1 instances.
+void
+gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId)
+{
+ gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage, aFailureId);
+ Factory::SetDirect3D11Device(nullptr);
+ UpdateBackendPrefs();
+}
+
+already_AddRefed<gfxASurface>
+gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat)
+{
+ if (!Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> surf = nullptr;
+
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D)
+ surf = new gfxWindowsSurface(aSize, aFormat);
+#endif
+
+ if (!surf || surf->CairoStatus()) {
+ surf = new gfxImageSurface(aSize, aFormat);
+ }
+
+ return surf.forget();
+}
+
+already_AddRefed<ScaledFont>
+gfxWindowsPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
+{
+ if (aFont->GetType() == gfxFont::FONT_TYPE_DWRITE) {
+ gfxDWriteFont *font = static_cast<gfxDWriteFont*>(aFont);
+
+ NativeFont nativeFont;
+ nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
+ nativeFont.mFont = font->GetFontFace();
+
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
+ return Factory::CreateScaledFontWithCairo(nativeFont,
+ font->GetAdjustedSize(),
+ font->GetCairoScaledFont());
+ }
+
+ return Factory::CreateScaledFontForNativeFont(nativeFont,
+ font->GetAdjustedSize());
+ }
+
+ NS_ASSERTION(aFont->GetType() == gfxFont::FONT_TYPE_GDI,
+ "Fonts on windows should be GDI or DWrite!");
+
+ NativeFont nativeFont;
+ nativeFont.mType = NativeFontType::GDI_FONT_FACE;
+ LOGFONT lf;
+ GetObject(static_cast<gfxGDIFont*>(aFont)->GetHFONT(), sizeof(LOGFONT), &lf);
+ nativeFont.mFont = &lf;
+
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
+ return Factory::CreateScaledFontWithCairo(nativeFont,
+ aFont->GetAdjustedSize(),
+ aFont->GetCairoScaledFont());
+ }
+
+ return Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
+}
+
+static const char kFontAparajita[] = "Aparajita";
+static const char kFontArabicTypesetting[] = "Arabic Typesetting";
+static const char kFontArial[] = "Arial";
+static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
+static const char kFontCambria[] = "Cambria";
+static const char kFontCambriaMath[] = "Cambria Math";
+static const char kFontEbrima[] = "Ebrima";
+static const char kFontEstrangeloEdessa[] = "Estrangelo Edessa";
+static const char kFontEuphemia[] = "Euphemia";
+static const char kFontGabriola[] = "Gabriola";
+static const char kFontJavaneseText[] = "Javanese Text";
+static const char kFontKhmerUI[] = "Khmer UI";
+static const char kFontLaoUI[] = "Lao UI";
+static const char kFontLeelawadeeUI[] = "Leelawadee UI";
+static const char kFontLucidaSansUnicode[] = "Lucida Sans Unicode";
+static const char kFontMVBoli[] = "MV Boli";
+static const char kFontMalgunGothic[] = "Malgun Gothic";
+static const char kFontMicrosoftJhengHei[] = "Microsoft JhengHei";
+static const char kFontMicrosoftNewTaiLue[] = "Microsoft New Tai Lue";
+static const char kFontMicrosoftPhagsPa[] = "Microsoft PhagsPa";
+static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
+static const char kFontMicrosoftUighur[] = "Microsoft Uighur";
+static const char kFontMicrosoftYaHei[] = "Microsoft YaHei";
+static const char kFontMicrosoftYiBaiti[] = "Microsoft Yi Baiti";
+static const char kFontMeiryo[] = "Meiryo";
+static const char kFontMongolianBaiti[] = "Mongolian Baiti";
+static const char kFontMyanmarText[] = "Myanmar Text";
+static const char kFontNirmalaUI[] = "Nirmala UI";
+static const char kFontNyala[] = "Nyala";
+static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
+static const char kFontSegoeUI[] = "Segoe UI";
+static const char kFontSegoeUIEmoji[] = "Segoe UI Emoji";
+static const char kFontSegoeUISymbol[] = "Segoe UI Symbol";
+static const char kFontSylfaen[] = "Sylfaen";
+static const char kFontTraditionalArabic[] = "Traditional Arabic";
+static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
+static const char kFontUtsaah[] = "Utsaah";
+static const char kFontYuGothic[] = "Yu Gothic";
+
+void
+gfxWindowsPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList)
+{
+ EmojiPresentation emoji = GetEmojiPresentation(aCh);
+ if (emoji != EmojiPresentation::TextOnly) {
+ if (aNextCh == kVariationSelector16 ||
+ (aNextCh != kVariationSelector15 &&
+ emoji == EmojiPresentation::EmojiDefault)) {
+ // if char is followed by VS16, try for a color emoji glyph
+ // XXX: For Win8+ native, aFontList.AppendElement(kFontSegoeUIEmoji);
+ aFontList.AppendElement(kFontTwemojiMozilla);
+ }
+ }
+
+ // Arial is used as the default fallback for system fallback
+ aFontList.AppendElement(kFontArial);
+
+ if (!IS_IN_BMP(aCh)) {
+ uint32_t p = aCh >> 16;
+ if (p == 1) { // SMP plane
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ aFontList.AppendElement(kFontEbrima);
+ aFontList.AppendElement(kFontNirmalaUI);
+ aFontList.AppendElement(kFontCambriaMath);
+ }
+ } else {
+ uint32_t b = (aCh >> 8) & 0xff;
+
+ switch (b) {
+ case 0x05:
+ aFontList.AppendElement(kFontEstrangeloEdessa);
+ aFontList.AppendElement(kFontCambria);
+ break;
+ case 0x06:
+ aFontList.AppendElement(kFontMicrosoftUighur);
+ break;
+ case 0x07:
+ aFontList.AppendElement(kFontEstrangeloEdessa);
+ aFontList.AppendElement(kFontMVBoli);
+ aFontList.AppendElement(kFontEbrima);
+ break;
+ case 0x09:
+ aFontList.AppendElement(kFontNirmalaUI);
+ aFontList.AppendElement(kFontUtsaah);
+ aFontList.AppendElement(kFontAparajita);
+ break;
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ aFontList.AppendElement(kFontNirmalaUI);
+ break;
+ case 0x0e:
+ aFontList.AppendElement(kFontLaoUI);
+ aFontList.AppendElement(kFontLeelawadeeUI);
+ break;
+ case 0x10:
+ aFontList.AppendElement(kFontMyanmarText);
+ break;
+ case 0x11:
+ aFontList.AppendElement(kFontMalgunGothic);
+ break;
+ case 0x12:
+ case 0x13:
+ aFontList.AppendElement(kFontNyala);
+ aFontList.AppendElement(kFontPlantagenetCherokee);
+ break;
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ aFontList.AppendElement(kFontEuphemia);
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ break;
+ case 0x17:
+ aFontList.AppendElement(kFontKhmerUI);
+ aFontList.AppendElement(kFontLeelawadeeUI);
+ break;
+ case 0x18: // Mongolian
+ aFontList.AppendElement(kFontMongolianBaiti);
+ aFontList.AppendElement(kFontEuphemia);
+ break;
+ case 0x19:
+ aFontList.AppendElement(kFontMicrosoftTaiLe);
+ aFontList.AppendElement(kFontMicrosoftNewTaiLue);
+ aFontList.AppendElement(kFontKhmerUI);
+ aFontList.AppendElement(kFontLeelawadeeUI);
+ break;
+ case 0x1a:
+ aFontList.AppendElement(kFontLeelawadeeUI);
+ break;
+ case 0x1c:
+ aFontList.AppendElement(kFontNirmalaUI);
+ break;
+ case 0x20: // Symbol ranges
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ aFontList.AppendElement(kFontSegoeUI);
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ aFontList.AppendElement(kFontCambria);
+ aFontList.AppendElement(kFontMeiryo);
+ aFontList.AppendElement(kFontArial);
+ aFontList.AppendElement(kFontLucidaSansUnicode);
+ aFontList.AppendElement(kFontEbrima);
+ break;
+ case 0x2d:
+ case 0x2e:
+ case 0x2f:
+ aFontList.AppendElement(kFontEbrima);
+ aFontList.AppendElement(kFontNyala);
+ aFontList.AppendElement(kFontSegoeUI);
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ aFontList.AppendElement(kFontMeiryo);
+ break;
+ case 0x28: // Braille
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ break;
+ case 0x30:
+ case 0x31:
+ aFontList.AppendElement(kFontMicrosoftYaHei);
+ break;
+ case 0x32:
+ aFontList.AppendElement(kFontMalgunGothic);
+ break;
+ case 0x4d:
+ aFontList.AppendElement(kFontSegoeUISymbol);
+ break;
+ case 0x9f:
+ aFontList.AppendElement(kFontMicrosoftYaHei);
+ aFontList.AppendElement(kFontYuGothic);
+ break;
+ case 0xa0: // Yi
+ case 0xa1:
+ case 0xa2:
+ case 0xa3:
+ case 0xa4:
+ aFontList.AppendElement(kFontMicrosoftYiBaiti);
+ aFontList.AppendElement(kFontSegoeUI);
+ break;
+ case 0xa5:
+ case 0xa6:
+ case 0xa7:
+ aFontList.AppendElement(kFontEbrima);
+ aFontList.AppendElement(kFontSegoeUI);
+ aFontList.AppendElement(kFontCambriaMath);
+ break;
+ case 0xa8:
+ aFontList.AppendElement(kFontMicrosoftPhagsPa);
+ aFontList.AppendElement(kFontNirmalaUI);
+ break;
+ case 0xa9:
+ aFontList.AppendElement(kFontMalgunGothic);
+ aFontList.AppendElement(kFontJavaneseText);
+ aFontList.AppendElement(kFontLeelawadeeUI);
+ break;
+ case 0xaa:
+ aFontList.AppendElement(kFontMyanmarText);
+ break;
+ case 0xab:
+ aFontList.AppendElement(kFontEbrima);
+ aFontList.AppendElement(kFontNyala);
+ break;
+ case 0xd7:
+ aFontList.AppendElement(kFontMalgunGothic);
+ break;
+ case 0xfb:
+ aFontList.AppendElement(kFontMicrosoftUighur);
+ aFontList.AppendElement(kFontGabriola);
+ aFontList.AppendElement(kFontSylfaen);
+ break;
+ case 0xfc:
+ case 0xfd:
+ aFontList.AppendElement(kFontTraditionalArabic);
+ aFontList.AppendElement(kFontArabicTypesetting);
+ break;
+ case 0xfe:
+ aFontList.AppendElement(kFontTraditionalArabic);
+ aFontList.AppendElement(kFontMicrosoftJhengHei);
+ break;
+ case 0xff:
+ aFontList.AppendElement(kFontMicrosoftJhengHei);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Arial Unicode MS has lots of glyphs for obscure characters,
+ // use it as a last resort
+ aFontList.AppendElement(kFontArialUnicodeMS);
+}
+
+gfxFontGroup *
+gfxWindowsPlatform::CreateFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize)
+{
+ return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
+ aUserFontSet, aDevToCssSize);
+}
+
+bool
+gfxWindowsPlatform::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
+{
+ // check for strange format flags
+ NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
+ "strange font format hint set");
+
+ // accept supported formats
+ if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) {
+ return true;
+ }
+
+ // reject all other formats, known and unknown
+ if (aFormatFlags != 0) {
+ return false;
+ }
+
+ // no format hint set, need to look at data
+ return true;
+}
+
+bool
+gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason)
+{
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ if (!dm) {
+ return false;
+ }
+ return dm->HasDeviceReset(aResetReason);
+}
+
+void
+gfxWindowsPlatform::CompositorUpdated()
+{
+ DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::COMPOSITOR_UPDATED);
+ UpdateRenderMode();
+}
+
+BOOL CALLBACK
+InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg)
+{
+ RedrawWindow(aWnd, nullptr, nullptr,
+ RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_FRAME);
+ return TRUE;
+}
+
+void
+gfxWindowsPlatform::SchedulePaintIfDeviceReset()
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+
+ DeviceResetReason resetReason = DeviceResetReason::OK;
+ if (!DidRenderingDeviceReset(&resetReason)) {
+ return;
+ }
+
+ gfxCriticalNote << "(gfxWindowsPlatform) Detected device reset: " << (int)resetReason;
+
+ // Trigger an ::OnPaint for each window.
+ ::EnumThreadWindows(GetCurrentThreadId(),
+ InvalidateWindowForDeviceReset,
+ 0);
+
+ gfxCriticalNote << "(gfxWindowsPlatform) Finished device reset.";
+}
+
+void
+gfxWindowsPlatform::GetPlatformCMSOutputProfile(void* &mem, size_t &mem_size)
+{
+ WCHAR str[MAX_PATH];
+ DWORD size = MAX_PATH;
+ BOOL res;
+
+ mem = nullptr;
+ mem_size = 0;
+
+ HDC dc = GetDC(nullptr);
+ if (!dc)
+ return;
+
+ MOZ_SEH_TRY {
+ res = GetICMProfileW(dc, &size, (LPWSTR)&str);
+ } MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) {
+ res = FALSE;
+ }
+
+ ReleaseDC(nullptr, dc);
+ if (!res)
+ return;
+
+#ifdef _WIN32
+ qcms_data_from_unicode_path(str, &mem, &mem_size);
+
+#ifdef DEBUG_tor
+ if (mem_size > 0)
+ fprintf(stderr,
+ "ICM profile read from %s successfully\n",
+ NS_ConvertUTF16toUTF8(str).get());
+#endif // DEBUG_tor
+#endif // _WIN32
+}
+
+void
+gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion)
+{
+ DWORD versInfoSize, vers[4] = {0};
+ // version info not available case
+ aVersion.AssignLiteral(u"0.0.0.0");
+ versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
+ AutoTArray<BYTE,512> versionInfo;
+
+ if (versInfoSize == 0 ||
+ !versionInfo.AppendElements(uint32_t(versInfoSize)))
+ {
+ return;
+ }
+
+ if (!GetFileVersionInfoW(aDLLPath, 0, versInfoSize,
+ LPBYTE(versionInfo.Elements())))
+ {
+ return;
+ }
+
+ UINT len = 0;
+ VS_FIXEDFILEINFO *fileInfo = nullptr;
+ if (!VerQueryValue(LPBYTE(versionInfo.Elements()), TEXT("\\"),
+ (LPVOID *)&fileInfo, &len) ||
+ len == 0 ||
+ fileInfo == nullptr)
+ {
+ return;
+ }
+
+ DWORD fileVersMS = fileInfo->dwFileVersionMS;
+ DWORD fileVersLS = fileInfo->dwFileVersionLS;
+
+ vers[0] = HIWORD(fileVersMS);
+ vers[1] = LOWORD(fileVersMS);
+ vers[2] = HIWORD(fileVersLS);
+ vers[3] = LOWORD(fileVersLS);
+
+ char buf[256];
+ SprintfLiteral(buf, "%u.%u.%u.%u", vers[0], vers[1], vers[2], vers[3]);
+ aVersion.Assign(NS_ConvertUTF8toUTF16(buf));
+}
+
+void
+gfxWindowsPlatform::GetCleartypeParams(nsTArray<ClearTypeParameterInfo>& aParams)
+{
+ HKEY hKey, subKey;
+ DWORD i, rv, size, type;
+ WCHAR displayName[256], subkeyName[256];
+
+ aParams.Clear();
+
+ // construct subkeys based on HKLM subkeys, assume they are same for HKCU
+ rv = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"Software\\Microsoft\\Avalon.Graphics",
+ 0, KEY_READ, &hKey);
+
+ if (rv != ERROR_SUCCESS) {
+ return;
+ }
+
+ // enumerate over subkeys
+ for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
+ size = ArrayLength(displayName);
+ rv = RegEnumKeyExW(hKey, i, displayName, &size,
+ nullptr, nullptr, nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ continue;
+ }
+
+ ClearTypeParameterInfo ctinfo;
+ ctinfo.displayName.Assign(displayName);
+
+ DWORD subrv, value;
+ bool foundData = false;
+
+ swprintf_s(subkeyName, ArrayLength(subkeyName),
+ L"Software\\Microsoft\\Avalon.Graphics\\%s", displayName);
+
+ // subkey for gamma, pixel structure
+ subrv = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ subkeyName, 0, KEY_QUERY_VALUE, &subKey);
+
+ if (subrv == ERROR_SUCCESS) {
+ size = sizeof(value);
+ subrv = RegQueryValueExW(subKey, L"GammaLevel", nullptr, &type,
+ (LPBYTE)&value, &size);
+ if (subrv == ERROR_SUCCESS && type == REG_DWORD) {
+ foundData = true;
+ ctinfo.gamma = value;
+ }
+
+ size = sizeof(value);
+ subrv = RegQueryValueExW(subKey, L"PixelStructure", nullptr, &type,
+ (LPBYTE)&value, &size);
+ if (subrv == ERROR_SUCCESS && type == REG_DWORD) {
+ foundData = true;
+ ctinfo.pixelStructure = value;
+ }
+
+ RegCloseKey(subKey);
+ }
+
+ // subkey for cleartype level, enhanced contrast
+ subrv = RegOpenKeyExW(HKEY_CURRENT_USER,
+ subkeyName, 0, KEY_QUERY_VALUE, &subKey);
+
+ if (subrv == ERROR_SUCCESS) {
+ size = sizeof(value);
+ subrv = RegQueryValueExW(subKey, L"ClearTypeLevel", nullptr, &type,
+ (LPBYTE)&value, &size);
+ if (subrv == ERROR_SUCCESS && type == REG_DWORD) {
+ foundData = true;
+ ctinfo.clearTypeLevel = value;
+ }
+
+ size = sizeof(value);
+ subrv = RegQueryValueExW(subKey, L"EnhancedContrastLevel",
+ nullptr, &type, (LPBYTE)&value, &size);
+ if (subrv == ERROR_SUCCESS && type == REG_DWORD) {
+ foundData = true;
+ ctinfo.enhancedContrast = value;
+ }
+
+ RegCloseKey(subKey);
+ }
+
+ if (foundData) {
+ aParams.AppendElement(ctinfo);
+ }
+ }
+
+ RegCloseKey(hKey);
+}
+
+void
+gfxWindowsPlatform::FontsPrefsChanged(const char *aPref)
+{
+ bool clearTextFontCaches = true;
+
+ gfxPlatform::FontsPrefsChanged(aPref);
+
+ if (aPref && !strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
+ SetupClearTypeParams();
+ } else {
+ clearTextFontCaches = false;
+ }
+
+ if (clearTextFontCaches) {
+ gfxFontCache *fc = gfxFontCache::GetCache();
+ if (fc) {
+ fc->Flush();
+ }
+ }
+}
+
+#define DISPLAY1_REGISTRY_KEY \
+ HKEY_CURRENT_USER, L"Software\\Microsoft\\Avalon.Graphics\\DISPLAY1"
+
+#define ENHANCED_CONTRAST_VALUE_NAME L"EnhancedContrastLevel"
+
+void
+gfxWindowsPlatform::SetupClearTypeParams()
+{
+ if (GetDWriteFactory()) {
+ // any missing prefs will default to invalid (-1) and be ignored;
+ // out-of-range values will also be ignored
+ FLOAT gamma = -1.0;
+ FLOAT contrast = -1.0;
+ FLOAT level = -1.0;
+ int geometry = -1;
+ int mode = -1;
+ int32_t value;
+ if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, &value))) {
+ if (value >= 1000 && value <= 2200) {
+ gamma = FLOAT(value / 1000.0);
+ }
+ }
+
+ if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, &value))) {
+ if (value >= 0 && value <= 1000) {
+ contrast = FLOAT(value / 100.0);
+ }
+ }
+
+ if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, &value))) {
+ if (value >= 0 && value <= 100) {
+ level = FLOAT(value / 100.0);
+ }
+ }
+
+ if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, &value))) {
+ if (value >= 0 && value <= 2) {
+ geometry = value;
+ }
+ }
+
+ if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, &value))) {
+ if (value >= 0 && value <= 5) {
+ mode = value;
+ }
+ }
+
+ cairo_dwrite_set_cleartype_params(gamma, contrast, level, geometry, mode);
+
+ switch (mode) {
+ case DWRITE_RENDERING_MODE_ALIASED:
+ case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
+ mMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+ break;
+ case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
+ mMeasuringMode = DWRITE_MEASURING_MODE_GDI_NATURAL;
+ break;
+ default:
+ mMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
+ break;
+ }
+
+ RefPtr<IDWriteRenderingParams> defaultRenderingParams;
+ GetDWriteFactory()->CreateRenderingParams(getter_AddRefs(defaultRenderingParams));
+ // For EnhancedContrast, we override the default if the user has not set it
+ // in the registry (by using the ClearType Tuner).
+ if (contrast < 0.0 || contrast > 10.0) {
+ HKEY hKey;
+ LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY,
+ 0, KEY_READ, &hKey);
+ if (res == ERROR_SUCCESS) {
+ res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME,
+ nullptr, nullptr, nullptr, nullptr);
+ if (res == ERROR_SUCCESS) {
+ contrast = defaultRenderingParams->GetEnhancedContrast();
+ }
+ RegCloseKey(hKey);
+ }
+
+ if (contrast < 0.0 || contrast > 10.0) {
+ contrast = 1.0;
+ }
+ }
+
+ if (GetDefaultContentBackend() == BackendType::SKIA) {
+ // Skia doesn't support a contrast value outside of 0-1, so default to 1.0
+ if (contrast < 0.0 || contrast > 1.0) {
+ NS_WARNING("Custom dwrite contrast not supported in Skia. Defaulting to 1.0.");
+ contrast = 1.0;
+ }
+ }
+
+ // For parameters that have not been explicitly set,
+ // we copy values from default params (or our overridden value for contrast)
+ if (gamma < 1.0 || gamma > 2.2) {
+ gamma = defaultRenderingParams->GetGamma();
+ }
+
+ if (level < 0.0 || level > 1.0) {
+ level = defaultRenderingParams->GetClearTypeLevel();
+ }
+
+ DWRITE_PIXEL_GEOMETRY dwriteGeometry =
+ static_cast<DWRITE_PIXEL_GEOMETRY>(geometry);
+ DWRITE_RENDERING_MODE renderMode =
+ static_cast<DWRITE_RENDERING_MODE>(mode);
+
+ if (dwriteGeometry < DWRITE_PIXEL_GEOMETRY_FLAT ||
+ dwriteGeometry > DWRITE_PIXEL_GEOMETRY_BGR) {
+ dwriteGeometry = defaultRenderingParams->GetPixelGeometry();
+ }
+
+ if (renderMode < DWRITE_RENDERING_MODE_DEFAULT ||
+ renderMode > DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC) {
+ renderMode = defaultRenderingParams->GetRenderingMode();
+ }
+
+ mRenderingParams[TEXT_RENDERING_NO_CLEARTYPE] = defaultRenderingParams;
+
+ HRESULT hr = GetDWriteFactory()->CreateCustomRenderingParams(
+ gamma, contrast, level, dwriteGeometry, renderMode,
+ getter_AddRefs(mRenderingParams[TEXT_RENDERING_NORMAL]));
+ if (FAILED(hr) || !mRenderingParams[TEXT_RENDERING_NORMAL]) {
+ mRenderingParams[TEXT_RENDERING_NORMAL] = defaultRenderingParams;
+ }
+
+ hr = GetDWriteFactory()->CreateCustomRenderingParams(
+ gamma, contrast, level,
+ dwriteGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC,
+ getter_AddRefs(mRenderingParams[TEXT_RENDERING_GDI_CLASSIC]));
+ if (FAILED(hr) || !mRenderingParams[TEXT_RENDERING_GDI_CLASSIC]) {
+ mRenderingParams[TEXT_RENDERING_GDI_CLASSIC] =
+ defaultRenderingParams;
+ }
+ }
+}
+
+ReadbackManagerD3D11*
+gfxWindowsPlatform::GetReadbackManager()
+{
+ if (!mD3D11ReadbackManager) {
+ mD3D11ReadbackManager = new ReadbackManagerD3D11();
+ }
+
+ return mD3D11ReadbackManager;
+}
+
+bool
+gfxWindowsPlatform::IsOptimus()
+{
+ static int knowIsOptimus = -1;
+ if (knowIsOptimus == -1) {
+ // other potential optimus -- nvd3d9wrapx.dll & nvdxgiwrap.dll
+ if (GetModuleHandleA("nvumdshim.dll") ||
+ GetModuleHandleA("nvumdshimx.dll"))
+ {
+ knowIsOptimus = 1;
+ } else {
+ knowIsOptimus = 0;
+ }
+ }
+ return knowIsOptimus;
+}
+
+static inline bool
+IsWARPStable()
+{
+ // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
+ if (!IsWin8OrLater() || GetModuleHandleA("nvdxgiwrap.dll")) {
+ return false;
+ }
+ return true;
+}
+
+static void
+InitializeANGLEConfig()
+{
+ FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
+
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable, "D3D11 compositing is disabled",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DISABLED"));
+ return;
+ }
+
+ d3d11ANGLE.EnableByDefault();
+
+ nsCString message;
+ nsCString failureId;
+ if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, &message,
+ failureId)) {
+ d3d11ANGLE.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
+ }
+
+}
+
+void
+gfxWindowsPlatform::InitializeDirectDrawConfig()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
+ ddraw.EnableByDefault();
+}
+
+void
+gfxWindowsPlatform::InitializeConfig()
+{
+ if (XRE_IsParentProcess()) {
+ // The parent process first determines which features can be attempted.
+ // This information is relayed to content processes and the GPU process.
+ InitializeD3D11Config();
+ InitializeANGLEConfig();
+ InitializeD2DConfig();
+ } else {
+ FetchAndImportContentDeviceData();
+ InitializeANGLEConfig();
+ }
+}
+
+void
+gfxWindowsPlatform::InitializeD3D11Config()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+
+ if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+ d3d11.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_NEED_HWCOMP"));
+ return;
+ }
+
+ d3d11.EnableByDefault();
+
+ if (!IsWin8OrLater() &&
+ !DeviceManagerDx::Get()->CheckRemotePresentSupport()) {
+ nsCOMPtr<nsIGfxInfo> gfxInfo;
+ gfxInfo = services::GetGfxInfo();
+ nsAutoString adaptorId;
+ gfxInfo->GetAdapterDeviceID(adaptorId);
+ // Blacklist Intel HD Graphics 510/520/530 on Windows 7 without platform
+ // update due to the crashes in Bug 1351349.
+ if (adaptorId.EqualsLiteral("0x1912") || adaptorId.EqualsLiteral("0x1916") ||
+ adaptorId.EqualsLiteral("0x1902")) {
+ d3d11.Disable(FeatureStatus::Blacklisted, "Blacklisted, see bug 1351349",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_BUG_1351349"));
+ }
+ }
+
+ nsCString message;
+ nsCString failureId;
+ if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &message, failureId)) {
+ d3d11.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
+ }
+
+ // Check if the user really, really wants WARP.
+ if (gfxPrefs::LayersD3D11ForceWARP()) {
+ // Force D3D11 on even if we disabled it.
+ d3d11.UserForceEnable("User force-enabled WARP");
+ }
+}
+
+void
+gfxWindowsPlatform::InitializeDevices()
+{
+ MOZ_ASSERT(!InSafeMode());
+
+ if (XRE_IsParentProcess()) {
+ // If we're the UI process, and the GPU process is enabled, then we don't
+ // initialize any DirectX devices. We do leave them enabled in gfxConfig
+ // though. If the GPU process fails to create these devices it will send
+ // a message back and we'll update their status.
+ if (InitGPUProcessSupport()) {
+ return;
+ }
+
+ // No GPU process, continue initializing devices as normal.
+ }
+
+ // If acceleration is disabled, we refuse to initialize anything.
+ if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+ return;
+ }
+
+ // If we previously crashed initializing devices, bail out now.
+ D3D11LayersCrashGuard detectCrashes;
+ if (detectCrashes.Crashed()) {
+ gfxConfig::SetFailed(Feature::HW_COMPOSITING,
+ FeatureStatus::CrashedOnStartup,
+ "Crashed during startup in a previous session");
+ gfxConfig::SetFailed(Feature::D3D11_COMPOSITING,
+ FeatureStatus::CrashedOnStartup,
+ "Harware acceleration crashed during startup in a previous session");
+ gfxConfig::SetFailed(Feature::DIRECT2D,
+ FeatureStatus::CrashedOnStartup,
+ "Harware acceleration crashed during startup in a previous session");
+ return;
+ }
+
+ // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
+ InitializeD3D11();
+ InitializeD2D();
+}
+
+void
+gfxWindowsPlatform::InitializeD3D11()
+{
+ // This function attempts to initialize our D3D11 devices, if the hardware
+ // is not blacklisted for D3D11 layers. This first attempt will try to create
+ // a hardware accelerated device. If this creation fails or the hardware is
+ // blacklisted, then this function will abort if WARP is disabled, causing us
+ // to fallback to Basic layers. If WARP is not disabled it will use a WARP
+ // device which should always be available on Windows 7 and higher.
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ return;
+ }
+
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ if (XRE_IsParentProcess()) {
+ if (!dm->CreateCompositorDevices()) {
+ return;
+ }
+ }
+
+ dm->CreateContentDevices();
+
+ // Content process failed to create the d3d11 device while parent process
+ // succeed.
+ if (XRE_IsContentProcess() &&
+ !gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ gfxCriticalError() << "[D3D11] Failed to create the D3D11 device in content \
+ process.";
+ }
+}
+
+void
+gfxWindowsPlatform::InitializeD2DConfig()
+{
+ FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
+
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D requires Direct3D 11 compositing",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
+ return;
+ }
+
+ d2d1.SetDefaultFromPref(
+ gfxPrefs::GetDirect2DDisabledPrefName(),
+ false,
+ gfxPrefs::GetDirect2DDisabledPrefDefault());
+
+ nsCString message;
+ nsCString failureId;
+ if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message, failureId)) {
+ d2d1.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
+ }
+
+ if (!d2d1.IsEnabled() && gfxPrefs::Direct2DForceEnabled()) {
+ d2d1.UserForceEnable("Force-enabled via user-preference");
+ }
+}
+
+void
+gfxWindowsPlatform::InitializeD2D()
+{
+ FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
+
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+
+ // We don't know this value ahead of time, but the user can force-override
+ // it, so we use Disable instead of SetFailed.
+ if (dm->IsWARP()) {
+ d2d1.Disable(FeatureStatus::Blocked, "Direct2D is not compatible with Direct3D11 WARP",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_WARP_BLOCK"));
+ }
+
+ // If we pass all the initial checks, we can proceed to runtime decisions.
+ if (!d2d1.IsEnabled()) {
+ return;
+ }
+
+ if (!Factory::SupportsD2D1()) {
+ d2d1.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a Direct2D 1.1 factory",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_FACTORY"));
+ return;
+ }
+
+ if (!dm->GetContentDevice()) {
+ d2d1.SetFailed(FeatureStatus::Failed, "Failed to acquire a Direct3D 11 content device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DEVICE"));
+ return;
+ }
+
+ if (!dm->TextureSharingWorks()) {
+ d2d1.SetFailed(FeatureStatus::Failed, "Direct3D11 device does not support texture sharing",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_TXT_SHARING"));
+ return;
+ }
+
+ // Using Direct2D depends on DWrite support.
+ if (!mDWriteFactory && !InitDWriteSupport()) {
+ d2d1.SetFailed(FeatureStatus::Failed, "Failed to initialize DirectWrite support",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DWRITE"));
+ return;
+ }
+
+ // Verify that Direct2D device creation succeeded.
+ RefPtr<ID3D11Device> contentDevice = dm->GetContentDevice();
+ if (!Factory::SetDirect3D11Device(contentDevice)) {
+ d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_CREATE_FAILED"));
+ return;
+ }
+
+ MOZ_ASSERT(d2d1.IsEnabled());
+}
+
+bool
+gfxWindowsPlatform::InitGPUProcessSupport()
+{
+ FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
+
+ if (!gpuProc.IsEnabled()) {
+ return false;
+ }
+
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ // Don't use the GPU process if not using D3D11.
+ gpuProc.Disable(
+ FeatureStatus::Unavailable,
+ "Not using GPU Process since D3D11 is unavailable",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_D3D11"));
+ } else if (!IsWin7SP1OrLater()) {
+ // On Windows 7 Pre-SP1, DXGI 1.2 is not available and remote presentation
+ // for D3D11 will not work. Rather than take a regression we revert back to
+ // in-process rendering.
+ gpuProc.Disable(
+ FeatureStatus::Unavailable,
+ "Windows 7 Pre-SP1 cannot use the GPU process",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_OLD_WINDOWS"));
+ } else if (!IsWin8OrLater()) {
+ // Windows 7 SP1 can have DXGI 1.2 only via the Platform Update, so we
+ // explicitly check for that here.
+ if (!DeviceManagerDx::Get()->CheckRemotePresentSupport()) {
+ gpuProc.Disable(
+ FeatureStatus::Unavailable,
+ "GPU Process requires the Windows 7 Platform Update",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_PLATFORM_UPDATE"));
+ } else {
+ // Clear anything cached by the above call since we don't need it.
+ DeviceManagerDx::Get()->ResetDevices();
+ }
+ }
+
+ // If we're still enabled at this point, the user set the force-enabled pref.
+ return gpuProc.IsEnabled();
+}
+
+bool
+gfxWindowsPlatform::DwmCompositionEnabled()
+{
+ BOOL dwmEnabled = false;
+
+ if (FAILED(DwmIsCompositionEnabled(&dwmEnabled))) {
+ return false;
+ }
+
+ return dwmEnabled;
+}
+
+class D3DVsyncSource final : public VsyncSource
+{
+public:
+
+ class D3DVsyncDisplay final : public VsyncSource::Display
+ {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(D3DVsyncDisplay)
+ public:
+ D3DVsyncDisplay()
+ : mPrevVsync(TimeStamp::Now())
+ , mVsyncEnabledLock("D3DVsyncEnabledLock")
+ , mVsyncEnabled(false)
+ {
+ mVsyncThread = new base::Thread("WindowsVsyncThread");
+ MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "GFX: Could not start Windows vsync thread");
+ SetVsyncRate();
+ }
+
+ void SetVsyncRate()
+ {
+ if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
+ mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+ return;
+ }
+
+ DWM_TIMING_INFO vblankTime;
+ // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
+ vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
+ HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
+ if (SUCCEEDED(hr)) {
+ UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
+ // We get the rate in hertz / time, but we want the rate in ms.
+ float rate = ((float) refreshRate.uiDenominator
+ / (float) refreshRate.uiNumerator) * 1000;
+ mVsyncRate = TimeDuration::FromMilliseconds(rate);
+ } else {
+ mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+ }
+ }
+
+ virtual void Shutdown() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ DisableVsync();
+ mVsyncThread->Stop();
+ delete mVsyncThread;
+ }
+
+ virtual void EnableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mVsyncThread->IsRunning());
+ { // scope lock
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (mVsyncEnabled) {
+ return;
+ }
+ mVsyncEnabled = true;
+ }
+
+ mVsyncThread->message_loop()->PostTask(
+ NewRunnableMethod(this, &D3DVsyncDisplay::VBlankLoop));
+ }
+
+ virtual void DisableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mVsyncThread->IsRunning());
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (!mVsyncEnabled) {
+ return;
+ }
+ mVsyncEnabled = false;
+ }
+
+ virtual bool IsVsyncEnabled() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ return mVsyncEnabled;
+ }
+
+ virtual TimeDuration GetVsyncRate() override
+ {
+ return mVsyncRate;
+ }
+
+ void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp)
+ {
+ MOZ_ASSERT(IsInVsyncThread());
+ NS_WARNING("DwmComposition dynamically disabled, falling back to software timers");
+
+ TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
+ TimeDuration delay = nextVsync - TimeStamp::Now();
+ if (delay.ToMilliseconds() < 0) {
+ delay = mozilla::TimeDuration::FromMilliseconds(0);
+ }
+
+ mVsyncThread->message_loop()->PostDelayedTask(
+ NewRunnableMethod(this, &D3DVsyncDisplay::VBlankLoop),
+ delay.ToMilliseconds());
+ }
+
+ // Returns the timestamp for the just happened vsync
+ TimeStamp GetVBlankTime()
+ {
+ TimeStamp vsync = TimeStamp::Now();
+ TimeStamp now = vsync;
+
+ DWM_TIMING_INFO vblankTime;
+ // Make sure to init the cbSize, otherwise
+ // GetCompositionTiming will fail
+ vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
+ HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
+ if (!SUCCEEDED(hr)) {
+ return vsync;
+ }
+
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+
+ LARGE_INTEGER qpcNow;
+ QueryPerformanceCounter(&qpcNow);
+
+ const int microseconds = 1000000;
+ int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
+ int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
+ vsync -= TimeDuration::FromMicroseconds((double) usAdjust);
+
+ if (IsWin10OrLater()) {
+ // On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
+ // reports the upcoming vsync time, which is in the future.
+ // It can also sometimes report a vblank time in the past.
+ // Since large parts of Gecko assume TimeStamps can't be in future,
+ // use the previous vsync.
+
+ // Windows 10 and Intel HD vsync timestamps are messy and
+ // all over the place once in a while. Most of the time,
+ // it reports the upcoming vsync. Sometimes, that upcoming
+ // vsync is in the past. Sometimes that upcoming vsync is before
+ // the previously seen vsync.
+ // In these error cases, normalize to Now();
+ if (vsync >= now) {
+ vsync = vsync - mVsyncRate;
+ }
+ }
+
+ // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
+ // from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
+ if (vsync >= now) {
+ vsync = now;
+ }
+
+ // Our vsync time is some time very far in the past, adjust to Now.
+ // 4 ms is arbitrary, so feel free to pick something else if this isn't
+ // working. See the comment above within IsWin10OrLater().
+ if ((now - vsync).ToMilliseconds() > 4.0) {
+ vsync = now;
+ }
+
+ return vsync;
+ }
+
+ void VBlankLoop()
+ {
+ MOZ_ASSERT(IsInVsyncThread());
+ MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
+
+ TimeStamp vsync = TimeStamp::Now();
+ mPrevVsync = TimeStamp();
+ TimeStamp flushTime = TimeStamp::Now();
+ TimeDuration longVBlank = mVsyncRate * 2;
+
+ for (;;) {
+ { // scope lock
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (!mVsyncEnabled) return;
+ }
+
+ // Large parts of gecko assume that the refresh driver timestamp
+ // must be <= Now() and cannot be in the future.
+ MOZ_ASSERT(vsync <= TimeStamp::Now());
+ Display::NotifyVsync(vsync);
+
+ // DwmComposition can be dynamically enabled/disabled
+ // so we have to check every time that it's available.
+ // When it is unavailable, we fallback to software but will try
+ // to get back to dwm rendering once it's re-enabled
+ if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
+ ScheduleSoftwareVsync(vsync);
+ return;
+ }
+
+ // Using WaitForVBlank, the whole system dies because WaitForVBlank
+ // only works if it's run on the same thread as the Present();
+ HRESULT hr = DwmFlush();
+ if (!SUCCEEDED(hr)) {
+ // DWMFlush isn't working, fallback to software vsync.
+ ScheduleSoftwareVsync(TimeStamp::Now());
+ return;
+ }
+
+ TimeStamp now = TimeStamp::Now();
+ TimeDuration flushDiff = now - flushTime;
+ flushTime = now;
+ if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
+ // Our vblank took longer than 2 intervals, readjust our timestamps
+ vsync = GetVBlankTime();
+ mPrevVsync = vsync;
+ } else {
+ // Instead of giving the actual vsync time, a constant interval
+ // between vblanks instead of the noise generated via hardware
+ // is actually what we want. Most apps just care about the diff
+ // between vblanks to animate, so a clean constant interval is
+ // smoother.
+ vsync = mPrevVsync + mVsyncRate;
+ if (vsync > now) {
+ // DWMFlush woke up very early, so readjust our times again
+ vsync = GetVBlankTime();
+ }
+
+ if (vsync <= mPrevVsync) {
+ vsync = TimeStamp::Now();
+ }
+
+ if ((now - vsync).ToMilliseconds() > 2.0) {
+ // Account for time drift here where vsync never quite catches up to
+ // Now and we'd fall ever so slightly further behind Now().
+ vsync = GetVBlankTime();
+ }
+
+ mPrevVsync = vsync;
+ }
+ } // end for
+ }
+
+ private:
+ virtual ~D3DVsyncDisplay()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ bool IsInVsyncThread()
+ {
+ return mVsyncThread->thread_id() == PlatformThread::CurrentId();
+ }
+
+ TimeStamp mPrevVsync;
+ Monitor mVsyncEnabledLock;
+ base::Thread* mVsyncThread;
+ TimeDuration mVsyncRate;
+ bool mVsyncEnabled;
+ }; // end d3dvsyncdisplay
+
+ D3DVsyncSource()
+ {
+ mPrimaryDisplay = new D3DVsyncDisplay();
+ }
+
+ virtual Display& GetGlobalDisplay() override
+ {
+ return *mPrimaryDisplay;
+ }
+
+private:
+ virtual ~D3DVsyncSource()
+ {
+ }
+ RefPtr<D3DVsyncDisplay> mPrimaryDisplay;
+}; // end D3DVsyncSource
+
+already_AddRefed<mozilla::gfx::VsyncSource>
+gfxWindowsPlatform::CreateHardwareVsyncSource()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");
+
+ BOOL dwmEnabled = false;
+ DwmIsCompositionEnabled(&dwmEnabled);
+ if (!dwmEnabled) {
+ NS_WARNING("DWM not enabled, falling back to software vsync");
+ return gfxPlatform::CreateHardwareVsyncSource();
+ }
+
+ RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
+ return d3dVsyncSource.forget();
+}
+
+bool
+gfxWindowsPlatform::SupportsApzTouchInput() const
+{
+ int value = gfxPrefs::TouchEventsEnabled();
+ return value == 1 || value == 2;
+}
+
+void
+gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
+{
+ if (gfxConfig::IsEnabled(Feature::OPENGL_COMPOSITING) && gfxPrefs::LayersPreferOpenGL()) {
+ aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
+ }
+
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ aBackends.AppendElement(LayersBackend::LAYERS_D3D11);
+ }
+}
+
+void
+gfxWindowsPlatform::ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ gfxPlatform::ImportGPUDeviceData(aData);
+
+ gfxConfig::ImportChange(Feature::D3D11_COMPOSITING, aData.d3d11Compositing());
+
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ dm->ImportDeviceInfo(aData.gpuDevice().get_D3D11DeviceStatus());
+ } else {
+ // There should be no devices, so this just takes away the device status.
+ dm->ResetDevices();
+
+ // Make sure we disable D2D if content processes might use it.
+ FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
+ if (d2d1.IsEnabled()) {
+ d2d1.SetFailed(
+ FeatureStatus::Unavailable,
+ "Direct2D requires Direct3D 11 compositing",
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
+ }
+ }
+
+ // CanUseHardwareVideoDecoding depends on d3d11 state, so update
+ // the cached value now.
+ UpdateCanUseHardwareVideoDecoding();
+
+ // For completeness (and messaging in about:support). Content recomputes this
+ // on its own, and we won't use ANGLE in the UI process if we're using a GPU
+ // process.
+ UpdateANGLEConfig();
+}
+
+void
+gfxWindowsPlatform::ImportContentDeviceData(const mozilla::gfx::ContentDeviceData& aData)
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ gfxPlatform::ImportContentDeviceData(aData);
+
+ const DevicePrefs& prefs = aData.prefs();
+ gfxConfig::Inherit(Feature::D3D11_COMPOSITING, prefs.d3d11Compositing());
+ gfxConfig::Inherit(Feature::DIRECT2D, prefs.useD2D1());
+
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ dm->ImportDeviceInfo(aData.d3d11());
+ }
+}
+
+void
+gfxWindowsPlatform::BuildContentDeviceData(ContentDeviceData* aOut)
+{
+ // Check for device resets before giving back new graphics information.
+ UpdateRenderMode();
+
+ gfxPlatform::BuildContentDeviceData(aOut);
+
+ const FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+ aOut->prefs().d3d11Compositing() = d3d11.GetValue();
+ aOut->prefs().useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);
+
+ if (d3d11.IsEnabled()) {
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ dm->ExportDeviceInfo(&aOut->d3d11());
+ }
+}
+
+bool
+gfxWindowsPlatform::SupportsPluginDirectDXGIDrawing()
+{
+ DeviceManagerDx* dm = DeviceManagerDx::Get();
+ if (!dm->GetContentDevice() || !dm->TextureSharingWorks()) {
+ return false;
+ }
+ return true;
+}
diff --git a/system/graphics/thebes/gfxWindowsPlatform.h b/system/graphics/thebes/gfxWindowsPlatform.h
new file mode 100644
index 000000000..111f29459
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsPlatform.h
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WINDOWS_PLATFORM_H
+#define GFX_WINDOWS_PLATFORM_H
+
+
+/**
+ * XXX to get CAIRO_HAS_D2D_SURFACE, CAIRO_HAS_DWRITE_FONT
+ * and cairo_win32_scaled_font_select_font
+ */
+#include "cairo-win32.h"
+
+#include "gfxFontUtils.h"
+#include "gfxWindowsSurface.h"
+#include "gfxFont.h"
+#include "gfxDWriteFonts.h"
+#include "gfxPlatform.h"
+#include "gfxTelemetry.h"
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Atomics.h"
+#include "nsTArray.h"
+#include "nsDataHashtable.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
+
+#include <windows.h>
+#include <objbase.h>
+
+#include <dxgi.h>
+
+// This header is available in the June 2010 SDK and in the Win8 SDK
+#include <d3dcommon.h>
+// Win 8.0 SDK types we'll need when building using older sdks.
+#if !defined(D3D_FEATURE_LEVEL_11_1) // defined in the 8.0 SDK only
+#define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
+#define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
+#define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096
+#endif
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+class FeatureState;
+class DeviceManagerDx;
+}
+namespace layers {
+class ReadbackManagerD3D11;
+}
+}
+struct IDirect3DDevice9;
+struct ID3D11Device;
+struct IDXGIAdapter1;
+
+/**
+ * Utility to get a Windows HDC from a Moz2D DrawTarget. If the DrawTarget is
+ * not backed by a HDC this will get the HDC for the screen device context
+ * instead.
+ */
+class MOZ_STACK_CLASS DCFromDrawTarget final
+{
+public:
+ DCFromDrawTarget(mozilla::gfx::DrawTarget& aDrawTarget);
+
+ ~DCFromDrawTarget() {
+ if (mNeedsRelease) {
+ ReleaseDC(nullptr, mDC);
+ } else {
+ RestoreDC(mDC, -1);
+ }
+ }
+
+ operator HDC () {
+ return mDC;
+ }
+
+private:
+ HDC mDC;
+ bool mNeedsRelease;
+};
+
+// ClearType parameters set by running ClearType tuner
+struct ClearTypeParameterInfo {
+ ClearTypeParameterInfo() :
+ gamma(-1), pixelStructure(-1), clearTypeLevel(-1), enhancedContrast(-1)
+ { }
+
+ nsString displayName; // typically just 'DISPLAY1'
+ int32_t gamma;
+ int32_t pixelStructure;
+ int32_t clearTypeLevel;
+ int32_t enhancedContrast;
+};
+
+class gfxWindowsPlatform : public gfxPlatform
+{
+ friend class mozilla::gfx::DeviceManagerDx;
+
+public:
+ enum TextRenderingMode {
+ TEXT_RENDERING_NO_CLEARTYPE,
+ TEXT_RENDERING_NORMAL,
+ TEXT_RENDERING_GDI_CLASSIC,
+ TEXT_RENDERING_COUNT
+ };
+
+ gfxWindowsPlatform();
+ virtual ~gfxWindowsPlatform();
+ static gfxWindowsPlatform *GetPlatform() {
+ return (gfxWindowsPlatform*) gfxPlatform::GetPlatform();
+ }
+
+ virtual gfxPlatformFontList* CreatePlatformFontList() override;
+
+ virtual already_AddRefed<gfxASurface>
+ CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat) override;
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
+
+ enum RenderMode {
+ /* Use GDI and windows surfaces */
+ RENDER_GDI = 0,
+
+ /* Use 32bpp image surfaces and call StretchDIBits */
+ RENDER_IMAGE_STRETCH32,
+
+ /* Use 32bpp image surfaces, and do 32->24 conversion before calling StretchDIBits */
+ RENDER_IMAGE_STRETCH24,
+
+ /* Use Direct2D rendering */
+ RENDER_DIRECT2D,
+
+ /* max */
+ RENDER_MODE_MAX
+ };
+
+ bool IsDirect2DBackend();
+
+ /**
+ * Updates render mode with relation to the current preferences and
+ * available devices.
+ */
+ void UpdateRenderMode();
+
+ /**
+ * Verifies a D2D device is present and working, will attempt to create one
+ * it is non-functional or non-existant.
+ *
+ * \param aAttemptForce Attempt to force D2D cairo device creation by using
+ * cairo device creation routines.
+ */
+ void VerifyD2DDevice(bool aAttemptForce);
+
+ virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList) override;
+
+ gfxFontGroup*
+ CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize) override;
+
+ virtual bool CanUseHardwareVideoDecoding() override;
+
+ /**
+ * Check whether format is supported on a platform or not (if unclear, returns true)
+ */
+ virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
+
+ virtual void CompositorUpdated() override;
+
+ bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) override;
+ void SchedulePaintIfDeviceReset() override;
+
+ mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) override;
+
+ static void GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion);
+
+ // returns ClearType tuning information for each display
+ static void GetCleartypeParams(nsTArray<ClearTypeParameterInfo>& aParams);
+
+ virtual void FontsPrefsChanged(const char *aPref) override;
+
+ void SetupClearTypeParams();
+
+ IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
+ inline bool DWriteEnabled() { return !!mDWriteFactory; }
+ inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
+
+ IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
+ { return mRenderingParams[aRenderMode]; }
+
+public:
+ bool DwmCompositionEnabled();
+
+ mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
+
+ static bool IsOptimus();
+
+ bool SupportsApzWheelInput() const override {
+ return true;
+ }
+ bool SupportsApzTouchInput() const override;
+
+ // Recreate devices as needed for a device reset. Returns true if a device
+ // reset occurred.
+ bool HandleDeviceReset();
+ void UpdateBackendPrefs();
+
+ virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+ static mozilla::Atomic<size_t> sD3D11SharedTextures;
+ static mozilla::Atomic<size_t> sD3D9SharedTextures;
+
+ bool SupportsPluginDirectBitmapDrawing() override {
+ return true;
+ }
+ bool SupportsPluginDirectDXGIDrawing();
+
+protected:
+ void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends) override;
+ virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size) override;
+
+ void ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData) override;
+ void ImportContentDeviceData(const mozilla::gfx::ContentDeviceData& aData) override;
+ void BuildContentDeviceData(mozilla::gfx::ContentDeviceData* aOut) override;
+
+protected:
+ RenderMode mRenderMode;
+
+private:
+ void Init();
+ void InitAcceleration() override;
+
+ void InitializeDevices();
+ void InitializeD3D11();
+ void InitializeD2D();
+ bool InitDWriteSupport();
+ bool InitGPUProcessSupport();
+
+ void DisableD2D(mozilla::gfx::FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+
+ void InitializeConfig();
+ void InitializeD3D9Config();
+ void InitializeD3D11Config();
+ void InitializeD2DConfig();
+ void InitializeDirectDrawConfig();
+
+ RefPtr<IDWriteFactory> mDWriteFactory;
+ RefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
+ DWRITE_MEASURING_MODE mMeasuringMode;
+
+ RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
+
+ nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
+};
+
+#endif /* GFX_WINDOWS_PLATFORM_H */
diff --git a/system/graphics/thebes/gfxWindowsSurface.cpp b/system/graphics/thebes/gfxWindowsSurface.cpp
new file mode 100644
index 000000000..5a993aba0
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsSurface.cpp
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxWindowsSurface.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "mozilla/gfx/Logging.h"
+
+#include "cairo.h"
+#include "cairo-win32.h"
+
+#include "nsString.h"
+
+gfxWindowsSurface::gfxWindowsSurface(HDC dc, uint32_t flags) :
+ mOwnsDC(false), mDC(dc), mWnd(nullptr)
+{
+ InitWithDC(flags);
+}
+
+void
+gfxWindowsSurface::MakeInvalid(mozilla::gfx::IntSize& size)
+{
+ size = mozilla::gfx::IntSize(-1, -1);
+}
+
+gfxWindowsSurface::gfxWindowsSurface(const mozilla::gfx::IntSize& realSize, gfxImageFormat imageFormat) :
+ mOwnsDC(false), mWnd(nullptr)
+{
+ mozilla::gfx::IntSize size(realSize);
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(size))
+ MakeInvalid(size);
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(imageFormat);
+ cairo_surface_t *surf =
+ cairo_win32_surface_create_with_dib(cformat, size.width, size.height);
+
+ Init(surf);
+
+ if (CairoStatus() == CAIRO_STATUS_SUCCESS) {
+ mDC = cairo_win32_surface_get_dc(CairoSurface());
+ RecordMemoryUsed(size.width * size.height * 4 + sizeof(gfxWindowsSurface));
+ } else {
+ mDC = nullptr;
+ }
+}
+
+gfxWindowsSurface::gfxWindowsSurface(cairo_surface_t *csurf) :
+ mOwnsDC(false), mWnd(nullptr)
+{
+ if (cairo_surface_status(csurf) == 0)
+ mDC = cairo_win32_surface_get_dc(csurf);
+ else
+ mDC = nullptr;
+
+ MOZ_ASSERT(cairo_surface_get_type(csurf) != CAIRO_SURFACE_TYPE_WIN32_PRINTING);
+
+ Init(csurf, true);
+}
+
+void
+gfxWindowsSurface::InitWithDC(uint32_t flags)
+{
+ if (flags & FLAG_IS_TRANSPARENT) {
+ Init(cairo_win32_surface_create_with_alpha(mDC));
+ } else {
+ Init(cairo_win32_surface_create(mDC));
+ }
+}
+
+already_AddRefed<gfxASurface>
+gfxWindowsSurface::CreateSimilarSurface(gfxContentType aContent,
+ const mozilla::gfx::IntSize& aSize)
+{
+ if (!mSurface || !mSurfaceValid) {
+ return nullptr;
+ }
+
+ cairo_surface_t *surface;
+ if (GetContentType() == gfxContentType::COLOR_ALPHA) {
+ // When creating a similar surface to a transparent surface, ensure
+ // the new surface uses a DIB. cairo_surface_create_similar won't
+ // use a DIB for a gfxContentType::COLOR surface if this surface doesn't
+ // have a DIB (e.g. if we're a transparent window surface). But
+ // we need a DIB to perform well if the new surface is composited into
+ // a surface that's the result of create_similar(gfxContentType::COLOR_ALPHA)
+ // (e.g. a backbuffer for the window) --- that new surface *would*
+ // have a DIB.
+ gfxImageFormat gformat =
+ gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
+ cairo_format_t cformat = GfxFormatToCairoFormat(gformat);
+ surface = cairo_win32_surface_create_with_dib(cformat, aSize.width,
+ aSize.height);
+ } else {
+ surface =
+ cairo_surface_create_similar(mSurface, (cairo_content_t)(int)aContent,
+ aSize.width, aSize.height);
+ }
+
+ if (cairo_surface_status(surface)) {
+ cairo_surface_destroy(surface);
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> result = Wrap(surface, aSize);
+ cairo_surface_destroy(surface);
+ return result.forget();
+}
+
+gfxWindowsSurface::~gfxWindowsSurface()
+{
+ if (mOwnsDC) {
+ if (mWnd)
+ ::ReleaseDC(mWnd, mDC);
+ else
+ ::DeleteDC(mDC);
+ }
+}
+
+HDC
+gfxWindowsSurface::GetDC()
+{
+ return cairo_win32_surface_get_dc (CairoSurface());
+}
+
+already_AddRefed<gfxImageSurface>
+gfxWindowsSurface::GetAsImageSurface()
+{
+ if (!mSurfaceValid) {
+ NS_WARNING ("GetImageSurface on an invalid (null) surface; who's calling this without checking for surface errors?");
+ return nullptr;
+ }
+
+ NS_ASSERTION(CairoSurface() != nullptr, "CairoSurface() shouldn't be nullptr when mSurfaceValid is TRUE!");
+
+ cairo_surface_t *isurf = cairo_win32_surface_get_image(CairoSurface());
+ if (!isurf)
+ return nullptr;
+
+ RefPtr<gfxImageSurface> result = gfxASurface::Wrap(isurf).downcast<gfxImageSurface>();
+ result->SetOpaqueRect(GetOpaqueRect());
+
+ return result.forget();
+}
+
+const mozilla::gfx::IntSize
+gfxWindowsSurface::GetSize() const
+{
+ if (!mSurfaceValid) {
+ NS_WARNING ("GetImageSurface on an invalid (null) surface; who's calling this without checking for surface errors?");
+ return mozilla::gfx::IntSize(-1, -1);
+ }
+
+ NS_ASSERTION(mSurface != nullptr, "CairoSurface() shouldn't be nullptr when mSurfaceValid is TRUE!");
+
+ return mozilla::gfx::IntSize(cairo_win32_surface_get_width(mSurface),
+ cairo_win32_surface_get_height(mSurface));
+}
diff --git a/system/graphics/thebes/gfxWindowsSurface.h b/system/graphics/thebes/gfxWindowsSurface.h
new file mode 100644
index 000000000..c425f1818
--- /dev/null
+++ b/system/graphics/thebes/gfxWindowsSurface.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WINDOWSSURFACE_H
+#define GFX_WINDOWSSURFACE_H
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+
+/* include windows.h for the HWND and HDC definitions that we need. */
+#include <windows.h>
+
+struct IDirect3DSurface9;
+
+/* undefine LoadImage because our code uses that name */
+#undef LoadImage
+
+class gfxContext;
+
+class gfxWindowsSurface : public gfxASurface {
+public:
+ enum {
+ FLAG_IS_TRANSPARENT = (1 << 2)
+ };
+
+ gfxWindowsSurface(HDC dc, uint32_t flags = 0);
+
+ // Create a DIB surface
+ gfxWindowsSurface(const mozilla::gfx::IntSize& size,
+ gfxImageFormat imageFormat = mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32);
+
+ gfxWindowsSurface(cairo_surface_t *csurf);
+
+ virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
+ const mozilla::gfx::IntSize& aSize);
+
+ void InitWithDC(uint32_t flags);
+
+ virtual ~gfxWindowsSurface();
+
+ HDC GetDC();
+
+ already_AddRefed<gfxImageSurface> GetAsImageSurface();
+
+ const mozilla::gfx::IntSize GetSize() const;
+
+private:
+ void MakeInvalid(mozilla::gfx::IntSize& size);
+
+ bool mOwnsDC;
+
+ HDC mDC;
+ HWND mWnd;
+};
+
+#endif /* GFX_WINDOWSSURFACE_H */
diff --git a/system/graphics/thebes/gfxXlibNativeRenderer.cpp b/system/graphics/thebes/gfxXlibNativeRenderer.cpp
new file mode 100644
index 000000000..19213b4e7
--- /dev/null
+++ b/system/graphics/thebes/gfxXlibNativeRenderer.cpp
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxXlibNativeRenderer.h"
+
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "gfxAlphaRecovery.h"
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+#include "mozilla/gfx/BorrowedContext.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "gfx2DGlue.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#if 0
+#include <stdio.h>
+#define NATIVE_DRAWING_NOTE(m) fprintf(stderr, m)
+#else
+#define NATIVE_DRAWING_NOTE(m) do {} while (0)
+#endif
+
+/* We have four basic strategies available:
+
+ 1) 'direct': If the target is an xlib surface, and other conditions are met,
+ we can pass the underlying drawable directly to the callback.
+
+ 2) 'simple': If the drawing is opaque, or we can draw to a surface with an
+ alpha channel, then we can create a temporary xlib surface, pass its
+ underlying drawable to the callback, and composite the result using
+ cairo.
+
+ 3) 'copy-background': If the drawing is not opaque but the target is
+ opaque, and we can draw to a surface with format such that pixel
+ conversion to and from the target format is exact, we can create a
+ temporary xlib surface, copy the background from the target, pass the
+ underlying drawable to the callback, and copy back to the target.
+
+ This strategy is not used if the pixel format conversion is not exact,
+ because that would mean that drawing intended to be very transparent
+ messes with other content.
+
+ The strategy is prefered over simple for non-opaque drawing and opaque
+ targets on the same screen as compositing without alpha is a simpler
+ operation.
+
+ 4) 'alpha-extraction': create a temporary xlib surface, fill with black,
+ pass its underlying drawable to the callback, copy the results to a
+ cairo image surface, repeat with a white background, update the on-black
+ image alpha values by comparing the two images, then paint the on-black
+ image using cairo.
+
+ Sure would be nice to have an X extension or GL to do this for us on the
+ server...
+*/
+
+static cairo_bool_t
+_convert_coord_to_int (double coord, int32_t *v)
+{
+ *v = (int32_t)coord;
+ /* XXX allow some tolerance here? */
+ return *v == coord;
+}
+
+static bool
+_get_rectangular_clip (cairo_t *cr,
+ const IntRect& bounds,
+ bool *need_clip,
+ IntRect *rectangles, int max_rectangles,
+ int *num_rectangles)
+{
+ cairo_rectangle_list_t *cliplist;
+ cairo_rectangle_t *clips;
+ int i;
+ bool retval = true;
+
+ cliplist = cairo_copy_clip_rectangle_list (cr);
+ if (cliplist->status != CAIRO_STATUS_SUCCESS) {
+ retval = false;
+ NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip");
+ goto FINISH;
+ }
+
+ /* the clip is always in surface backend coordinates (i.e. native backend coords) */
+ clips = cliplist->rectangles;
+
+ for (i = 0; i < cliplist->num_rectangles; ++i) {
+
+ IntRect rect;
+ if (!_convert_coord_to_int (clips[i].x, &rect.x) ||
+ !_convert_coord_to_int (clips[i].y, &rect.y) ||
+ !_convert_coord_to_int (clips[i].width, &rect.width) ||
+ !_convert_coord_to_int (clips[i].height, &rect.height))
+ {
+ retval = false;
+ NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip");
+ goto FINISH;
+ }
+
+ if (rect.IsEqualInterior(bounds)) {
+ /* the bounds are entirely inside the clip region so we don't need to clip. */
+ *need_clip = false;
+ goto FINISH;
+ }
+
+ NS_ASSERTION(bounds.Contains(rect),
+ "Was expecting to be clipped to bounds");
+
+ if (i >= max_rectangles) {
+ retval = false;
+ NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count");
+ goto FINISH;
+ }
+
+ rectangles[i] = rect;
+ }
+
+ *need_clip = true;
+ *num_rectangles = cliplist->num_rectangles;
+
+FINISH:
+ cairo_rectangle_list_destroy (cliplist);
+
+ return retval;
+}
+
+#define MAX_STATIC_CLIP_RECTANGLES 50
+
+/**
+ * Try the direct path.
+ * @return True if we took the direct path
+ */
+bool
+gfxXlibNativeRenderer::DrawDirect(DrawTarget* aDT, IntSize size,
+ uint32_t flags,
+ Screen *screen, Visual *visual)
+{
+ // We need to actually borrow the context because we want to read out the
+ // clip rectangles.
+ BorrowedCairoContext borrowed(aDT);
+ if (!borrowed.mCairo) {
+ return false;
+ }
+
+ bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual);
+ borrowed.Finish();
+
+ return direct;
+}
+
+bool
+gfxXlibNativeRenderer::DrawCairo(cairo_t* cr, IntSize size,
+ uint32_t flags,
+ Screen *screen, Visual *visual)
+{
+ /* Check that the target surface is an xlib surface. */
+ cairo_surface_t *target = cairo_get_group_target (cr);
+ if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) {
+ NATIVE_DRAWING_NOTE("FALLBACK: non-X surface");
+ return false;
+ }
+
+ cairo_matrix_t matrix;
+ cairo_get_matrix (cr, &matrix);
+ double device_offset_x, device_offset_y;
+ cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
+
+ /* Draw() checked that the matrix contained only a very-close-to-integer
+ translation. Here (and in several other places and thebes) device
+ offsets are assumed to be integer. */
+ NS_ASSERTION(int32_t(device_offset_x) == device_offset_x &&
+ int32_t(device_offset_y) == device_offset_y,
+ "Expected integer device offsets");
+ IntPoint offset(NS_lroundf(matrix.x0 + device_offset_x),
+ NS_lroundf(matrix.y0 + device_offset_y));
+
+ int max_rectangles = 0;
+ if (flags & DRAW_SUPPORTS_CLIP_RECT) {
+ max_rectangles = 1;
+ }
+ if (flags & DRAW_SUPPORTS_CLIP_LIST) {
+ max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
+ }
+
+ /* The client won't draw outside the surface so consider this when
+ analysing clip rectangles. */
+ IntRect bounds(offset, size);
+ bounds.IntersectRect(bounds,
+ IntRect(0, 0,
+ cairo_xlib_surface_get_width(target),
+ cairo_xlib_surface_get_height(target)));
+
+ bool needs_clip = true;
+ IntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
+ int rect_count = 0;
+
+ /* Check that the clip is rectangular and aligned on unit boundaries. */
+ /* Temporarily set the matrix for _get_rectangular_clip. It's basically
+ the identity matrix, but we must adjust for the fact that our
+ offset-rect is in device coordinates. */
+ cairo_identity_matrix (cr);
+ cairo_translate (cr, -device_offset_x, -device_offset_y);
+ bool have_rectangular_clip =
+ _get_rectangular_clip (cr, bounds, &needs_clip,
+ rectangles, max_rectangles, &rect_count);
+ cairo_set_matrix (cr, &matrix);
+ if (!have_rectangular_clip)
+ return false;
+
+ /* Stop now if everything is clipped out */
+ if (needs_clip && rect_count == 0)
+ return true;
+
+ /* Check that the screen is supported.
+ Visuals belong to screens, so, if alternate visuals are not supported,
+ then alternate screens cannot be supported. */
+ bool supports_alternate_visual =
+ (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0;
+ bool supports_alternate_screen = supports_alternate_visual &&
+ (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN);
+ if (!supports_alternate_screen &&
+ cairo_xlib_surface_get_screen (target) != screen) {
+ NATIVE_DRAWING_NOTE("FALLBACK: non-default screen");
+ return false;
+ }
+
+ /* Check that there is a visual */
+ Visual *target_visual = cairo_xlib_surface_get_visual (target);
+ if (!target_visual) {
+ NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface");
+ return false;
+ }
+ /* Check that the visual is supported */
+ if (!supports_alternate_visual && target_visual != visual) {
+ // Only the format of the visual is important (not the GLX properties)
+ // for Xlib or XRender drawing.
+ XRenderPictFormat *target_format =
+ cairo_xlib_surface_get_xrender_format (target);
+ if (!target_format ||
+ (target_format !=
+ XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) {
+ NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual");
+ return false;
+ }
+ }
+
+ /* we're good to go! */
+ NATIVE_DRAWING_NOTE("TAKING FAST PATH\n");
+ cairo_surface_flush (target);
+ nsresult rv = DrawWithXlib(target,
+ offset, rectangles,
+ needs_clip ? rect_count : 0);
+ if (NS_SUCCEEDED(rv)) {
+ cairo_surface_mark_dirty (target);
+ return true;
+ }
+ return false;
+}
+
+static bool
+VisualHasAlpha(Screen *screen, Visual *visual) {
+ // There may be some other visuals format with alpha but usually this is
+ // the only one we care about.
+ return visual->c_class == TrueColor &&
+ visual->bits_per_rgb == 8 &&
+ visual->red_mask == 0xff0000 &&
+ visual->green_mask == 0xff00 &&
+ visual->blue_mask == 0xff &&
+ gfxXlibSurface::DepthOfVisual(screen, visual) == 32;
+}
+
+// Returns whether pixel conversion between visual and format is exact (in
+// both directions).
+static bool
+FormatConversionIsExact(Screen *screen, Visual *visual, XRenderPictFormat *format) {
+ if (!format ||
+ visual->c_class != TrueColor ||
+ format->type != PictTypeDirect ||
+ gfxXlibSurface::DepthOfVisual(screen, visual) != format->depth)
+ return false;
+
+ XRenderPictFormat *visualFormat =
+ XRenderFindVisualFormat(DisplayOfScreen(screen), visual);
+
+ if (visualFormat->type != PictTypeDirect )
+ return false;
+
+ const XRenderDirectFormat& a = visualFormat->direct;
+ const XRenderDirectFormat& b = format->direct;
+ return a.redMask == b.redMask &&
+ a.greenMask == b.greenMask &&
+ a.blueMask == b.blueMask;
+}
+
+// The 3 non-direct strategies described above.
+// The surface format and strategy are inter-dependent.
+enum DrawingMethod {
+ eSimple,
+ eCopyBackground,
+ eAlphaExtraction
+};
+
+static cairo_surface_t*
+CreateTempXlibSurface (cairo_surface_t* cairoTarget,
+ DrawTarget* drawTarget,
+ IntSize size,
+ bool canDrawOverBackground,
+ uint32_t flags, Screen *screen, Visual *visual,
+ DrawingMethod *method)
+{
+ NS_ASSERTION(cairoTarget || drawTarget, "Must have some type");
+
+ bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0;
+ bool supportsAlternateVisual =
+ (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0;
+ bool supportsAlternateScreen = supportsAlternateVisual &&
+ (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_SCREEN);
+
+ cairo_surface_type_t cairoTargetType =
+ cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF;
+
+ Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ?
+ cairo_xlib_surface_get_screen (cairoTarget) : screen;
+
+ // When the background has an alpha channel, we need to draw with an alpha
+ // channel anyway, so there is no need to copy the background. If
+ // doCopyBackground is set here, we'll also need to check below that the
+ // background can copied without any loss in format conversions.
+ bool doCopyBackground = !drawIsOpaque && canDrawOverBackground &&
+ cairoTarget && cairo_surface_get_content (cairoTarget) == CAIRO_CONTENT_COLOR;
+
+ if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) {
+ // Prefer a visual on the target screen.
+ // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.)
+ visual = DefaultVisualOfScreen(target_screen);
+ screen = target_screen;
+
+ } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) {
+ // Analyse the pixel formats either to check whether we can
+ // doCopyBackground or to see if we can find a better visual for
+ // opaque drawing.
+ Visual *target_visual = nullptr;
+ XRenderPictFormat *target_format = nullptr;
+ if (cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) {
+ target_visual = cairo_xlib_surface_get_visual (cairoTarget);
+ target_format = cairo_xlib_surface_get_xrender_format (cairoTarget);
+ } else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) {
+ gfxImageFormat imageFormat =
+ drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) :
+ CairoFormatToGfxFormat(cairo_image_surface_get_format(cairoTarget));
+ target_visual = gfxXlibSurface::FindVisual(screen, imageFormat);
+ Display *dpy = DisplayOfScreen(screen);
+ if (target_visual) {
+ target_format = XRenderFindVisualFormat(dpy, target_visual);
+ } else {
+ target_format =
+ gfxXlibSurface::FindRenderFormat(dpy, imageFormat);
+ }
+ }
+
+ if (supportsAlternateVisual &&
+ (supportsAlternateScreen || screen == target_screen)) {
+ if (target_visual) {
+ visual = target_visual;
+ screen = target_screen;
+ }
+ }
+ // Could try harder to match formats across screens for background
+ // copying when !supportsAlternateScreen, if we cared. Preferably
+ // we'll find a visual below with an alpha channel anyway; if so, the
+ // background won't need to be copied.
+
+ if (doCopyBackground && visual != target_visual &&
+ !FormatConversionIsExact(screen, visual, target_format)) {
+ doCopyBackground = false;
+ }
+ }
+
+ if (supportsAlternateVisual && !drawIsOpaque &&
+ (screen != target_screen ||
+ !(doCopyBackground || VisualHasAlpha(screen, visual)))) {
+ // Try to find a visual with an alpha channel.
+ Screen *visualScreen =
+ supportsAlternateScreen ? target_screen : screen;
+ Visual *argbVisual =
+ gfxXlibSurface::FindVisual(visualScreen,
+ SurfaceFormat::A8R8G8B8_UINT32);
+ if (argbVisual) {
+ visual = argbVisual;
+ screen = visualScreen;
+ } else if (!doCopyBackground &&
+ gfxXlibSurface::DepthOfVisual(screen, visual) != 24) {
+ // Will need to do alpha extraction; prefer a 24-bit visual.
+ // No advantage in using the target screen.
+ Visual *rgb24Visual =
+ gfxXlibSurface::FindVisual(screen,
+ SurfaceFormat::X8R8G8B8_UINT32);
+ if (rgb24Visual) {
+ visual = rgb24Visual;
+ }
+ }
+ }
+
+ Drawable drawable =
+ (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ?
+ cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen);
+
+ cairo_surface_t *surface =
+ gfxXlibSurface::CreateCairoSurface(screen, visual,
+ IntSize(size.width, size.height),
+ drawable);
+ if (!surface) {
+ return nullptr;
+ }
+
+ if (drawIsOpaque ||
+ cairo_surface_get_content(surface) == CAIRO_CONTENT_COLOR_ALPHA) {
+ NATIVE_DRAWING_NOTE(drawIsOpaque ?
+ ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA");
+ *method = eSimple;
+ } else if (doCopyBackground) {
+ NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n");
+ *method = eCopyBackground;
+ } else {
+ NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n");
+ *method = eAlphaExtraction;
+ }
+
+ return surface;
+}
+
+bool
+gfxXlibNativeRenderer::DrawOntoTempSurface(cairo_surface_t *tempXlibSurface,
+ IntPoint offset)
+{
+ cairo_surface_flush(tempXlibSurface);
+ /* no clipping is needed because the callback can't draw outside the native
+ surface anyway */
+ nsresult rv = DrawWithXlib(tempXlibSurface, offset, nullptr, 0);
+ cairo_surface_mark_dirty(tempXlibSurface);
+ return NS_SUCCEEDED(rv);
+}
+
+static already_AddRefed<gfxImageSurface>
+CopyXlibSurfaceToImage(cairo_surface_t *tempXlibSurface,
+ IntSize size,
+ gfxImageFormat format)
+{
+ RefPtr<gfxImageSurface> result = new gfxImageSurface(size, format);
+
+ cairo_t* copyCtx = cairo_create(result->CairoSurface());
+ cairo_set_source_surface(copyCtx, tempXlibSurface, 0, 0);
+ cairo_set_operator(copyCtx, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(copyCtx);
+ cairo_destroy(copyCtx);
+
+ return result.forget();
+}
+
+void
+gfxXlibNativeRenderer::Draw(gfxContext* ctx, IntSize size,
+ uint32_t flags, Screen *screen, Visual *visual)
+{
+ gfxMatrix matrix = ctx->CurrentMatrix();
+
+ // We can only draw direct or onto a copied background if pixels align and
+ // native drawing is compatible with the current operator. (The matrix is
+ // actually also pixel-exact for flips and right-angle rotations, which
+ // would permit copying the background but not drawing direct.)
+ bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation();
+ bool canDrawOverBackground = matrixIsIntegerTranslation &&
+ ctx->CurrentOp() == CompositionOp::OP_OVER;
+
+ // The padding of 0.5 for non-pixel-exact transformations used here is
+ // the same as what _cairo_pattern_analyze_filter uses.
+ const gfxFloat filterRadius = 0.5;
+ gfxRect affectedRect(0.0, 0.0, size.width, size.height);
+ if (!matrixIsIntegerTranslation) {
+ // The filter footprint means that the affected rectangle is a
+ // little larger than the drawingRect;
+ affectedRect.Inflate(filterRadius);
+
+ NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation");
+ } else if (!canDrawOverBackground) {
+ NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator");
+ }
+
+ DrawTarget* drawTarget = ctx->GetDrawTarget();
+ if (!drawTarget) {
+ gfxCriticalError() << "gfxContext without a DrawTarget";
+ return;
+ }
+
+ // Clipping to the region affected by drawing allows us to consider only
+ // the portions of the clip region that will be affected by drawing.
+ gfxRect clipExtents;
+ {
+ gfxContextAutoSaveRestore autoSR(ctx);
+ ctx->Clip(affectedRect);
+
+ clipExtents = ctx->GetClipExtents();
+ if (clipExtents.IsEmpty()) {
+ return; // nothing to do
+ }
+ if (canDrawOverBackground &&
+ DrawDirect(drawTarget, size, flags, screen, visual)) {
+ return;
+ }
+ }
+
+ IntRect drawingRect(IntPoint(0, 0), size);
+ // Drawing need only be performed within the clip extents
+ // (and padding for the filter).
+ if (!matrixIsIntegerTranslation) {
+ // The source surface may need to be a little larger than the clip
+ // extents due to the filter footprint.
+ clipExtents.Inflate(filterRadius);
+ }
+ clipExtents.RoundOut();
+
+ IntRect intExtents(int32_t(clipExtents.X()),
+ int32_t(clipExtents.Y()),
+ int32_t(clipExtents.Width()),
+ int32_t(clipExtents.Height()));
+ drawingRect.IntersectRect(drawingRect, intExtents);
+
+ gfxPoint offset(drawingRect.x, drawingRect.y);
+
+ DrawingMethod method;
+ Matrix dtTransform = drawTarget->GetTransform();
+ gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32);
+ cairo_t* cairo = static_cast<cairo_t*>
+ (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+ cairo_surface_t* cairoTarget = cairo ? cairo_get_group_target(cairo) : nullptr;
+ cairo_surface_t* tempXlibSurface =
+ CreateTempXlibSurface(cairoTarget, drawTarget, size,
+ canDrawOverBackground, flags, screen, visual,
+ &method);
+ if (!tempXlibSurface) {
+ return;
+ }
+
+ bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0;
+ if (!drawIsOpaque) {
+ cairo_t* tmpCtx = cairo_create(tempXlibSurface);
+ if (method == eCopyBackground) {
+ NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget");
+ cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE);
+ gfxPoint pt = -(offset + deviceTranslation);
+ cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y);
+ // The copy from the tempXlibSurface to the target context should
+ // use operator SOURCE, but that would need a mask to bound the
+ // operation. Here we only copy opaque backgrounds so operator
+ // OVER will behave like SOURCE masked by the surface.
+ NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR,
+ "Don't copy background with a transparent surface");
+ } else {
+ cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR);
+ }
+ cairo_paint(tmpCtx);
+ cairo_destroy(tmpCtx);
+ }
+
+ if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
+ cairo_surface_destroy(tempXlibSurface);
+ return;
+ }
+
+ SurfaceFormat moz2DFormat =
+ cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ?
+ SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
+ if (method != eAlphaExtraction) {
+ RefPtr<SourceSurface> sourceSurface =
+ Factory::CreateSourceSurfaceForCairoSurface(tempXlibSurface, size, moz2DFormat);
+ if (sourceSurface) {
+ drawTarget->DrawSurface(sourceSurface,
+ Rect(offset.x, offset.y, size.width, size.height),
+ Rect(0, 0, size.width, size.height));
+ }
+ cairo_surface_destroy(tempXlibSurface);
+ return;
+ }
+
+ RefPtr<gfxImageSurface> blackImage =
+ CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::A8R8G8B8_UINT32);
+
+ cairo_t* tmpCtx = cairo_create(tempXlibSurface);
+ cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0);
+ cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(tmpCtx);
+ cairo_destroy(tmpCtx);
+ DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft());
+ RefPtr<gfxImageSurface> whiteImage =
+ CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::X8R8G8B8_UINT32);
+
+ if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS &&
+ whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) {
+ if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) {
+ cairo_surface_destroy(tempXlibSurface);
+ return;
+ }
+
+ gfxASurface* paintSurface = blackImage;
+ RefPtr<SourceSurface> sourceSurface =
+ Factory::CreateSourceSurfaceForCairoSurface(paintSurface->CairoSurface(),
+ size, moz2DFormat);
+ if (sourceSurface) {
+ drawTarget->DrawSurface(sourceSurface,
+ Rect(offset.x, offset.y, size.width, size.height),
+ Rect(0, 0, size.width, size.height));
+ }
+ }
+ cairo_surface_destroy(tempXlibSurface);
+}
diff --git a/system/graphics/thebes/gfxXlibNativeRenderer.h b/system/graphics/thebes/gfxXlibNativeRenderer.h
new file mode 100644
index 000000000..85af130ca
--- /dev/null
+++ b/system/graphics/thebes/gfxXlibNativeRenderer.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFXXLIBNATIVERENDER_H_
+#define GFXXLIBNATIVERENDER_H_
+
+#include "nsPoint.h"
+#include "nsRect.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Point.h"
+#include <X11/Xlib.h>
+
+namespace mozilla {
+namespace gfx {
+ class DrawTarget;
+}
+}
+
+class gfxASurface;
+class gfxContext;
+typedef struct _cairo cairo_t;
+typedef struct _cairo_surface cairo_surface_t;
+
+/**
+ * This class lets us take code that draws into an X drawable and lets us
+ * use it to draw into any Thebes context. The user should subclass this class,
+ * override DrawWithXib, and then call Draw(). The drawing will be subjected
+ * to all Thebes transformations, clipping etc.
+ */
+class gfxXlibNativeRenderer {
+public:
+ /**
+ * Perform the native drawing.
+ * @param surface the cairo_surface_t for drawing. Must be a cairo_xlib_surface_t.
+ * The extents of this surface do not necessarily cover the
+ * entire rectangle with size provided to Draw().
+ * @param offset draw at this offset into the given drawable
+ * @param clipRects an array of rectangles; clip to the union.
+ * Any rectangles provided will be contained by the
+ * rectangle with size provided to Draw and by the
+ * surface extents.
+ * @param numClipRects the number of rects in the array, or zero if
+ * no clipping is required.
+ */
+ virtual nsresult DrawWithXlib(cairo_surface_t* surface,
+ mozilla::gfx::IntPoint offset,
+ mozilla::gfx::IntRect* clipRects,
+ uint32_t numClipRects) = 0;
+
+ enum {
+ // If set, then Draw() is opaque, i.e., every pixel in the intersection
+ // of the clipRect and (offset.x,offset.y,bounds.width,bounds.height)
+ // will be set and there is no dependence on what the existing pixels
+ // in the drawable are set to.
+ DRAW_IS_OPAQUE = 0x01,
+ // If set, then numClipRects can be zero or one
+ DRAW_SUPPORTS_CLIP_RECT = 0x04,
+ // If set, then numClipRects can be any value. If neither this
+ // nor CLIP_RECT are set, then numClipRects will be zero
+ DRAW_SUPPORTS_CLIP_LIST = 0x08,
+ // If set, then the surface in the callback may have any visual;
+ // otherwise the pixels will have the same format as the visual
+ // passed to 'Draw'.
+ DRAW_SUPPORTS_ALTERNATE_VISUAL = 0x10,
+ // If set, then the Screen 'screen' in the callback can be different
+ // from the default Screen of the display passed to 'Draw' and can be
+ // on a different display.
+ DRAW_SUPPORTS_ALTERNATE_SCREEN = 0x20
+ };
+
+ /**
+ * @param flags see above
+ * @param size the size of the rectangle being drawn;
+ * the caller guarantees that drawing will not extend beyond the rectangle
+ * (0,0,size.width,size.height).
+ * @param screen a Screen to use for the drawing if ctx doesn't have one.
+ * @param visual a Visual to use for the drawing if ctx doesn't have one.
+ * @param result if non-null, we will try to capture a copy of the
+ * rendered image into a surface similar to the surface of ctx; if
+ * successful, a pointer to the new gfxASurface is stored in *resultSurface,
+ * otherwise *resultSurface is set to nullptr.
+ */
+ void Draw(gfxContext* ctx, mozilla::gfx::IntSize size,
+ uint32_t flags, Screen *screen, Visual *visual);
+
+private:
+ bool DrawDirect(mozilla::gfx::DrawTarget* aDT, mozilla::gfx::IntSize bounds,
+ uint32_t flags, Screen *screen, Visual *visual);
+
+ bool DrawCairo(cairo_t* cr, mozilla::gfx::IntSize size,
+ uint32_t flags, Screen *screen, Visual *visual);
+
+ void DrawFallback(mozilla::gfx::DrawTarget* dt, gfxContext* ctx,
+ gfxASurface* aSurface, mozilla::gfx::IntSize& size,
+ mozilla::gfx::IntRect& drawingRect, bool canDrawOverBackground,
+ uint32_t flags, Screen* screen, Visual* visual);
+
+ bool DrawOntoTempSurface(cairo_surface_t *tempXlibSurface,
+ mozilla::gfx::IntPoint offset);
+
+};
+
+#endif /*GFXXLIBNATIVERENDER_H_*/
diff --git a/system/graphics/thebes/gfxXlibSurface.cpp b/system/graphics/thebes/gfxXlibSurface.cpp
new file mode 100644
index 000000000..12891c454
--- /dev/null
+++ b/system/graphics/thebes/gfxXlibSurface.cpp
@@ -0,0 +1,614 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxXlibSurface.h"
+
+#include "cairo.h"
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+#include <X11/Xlibint.h> /* For XESetCloseDisplay */
+#undef max // Xlibint.h defines this and it breaks std::max
+#undef min // Xlibint.h defines this and it breaks std::min
+
+#include "nsTArray.h"
+#include "nsAlgorithm.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Preferences.h"
+#include <algorithm>
+#include "mozilla/CheckedInt.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
+ : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
+#if defined(GL_PROVIDER_GLX)
+ , mGLXPixmap(X11None)
+#endif
+{
+ const gfx::IntSize size = DoSizeQuery();
+ cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
+ Init(surf);
+}
+
+gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfx::IntSize& size)
+ : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
+#if defined(GL_PROVIDER_GLX)
+ , mGLXPixmap(X11None)
+#endif
+{
+ NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
+ "Bad size");
+
+ cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
+ Init(surf);
+}
+
+gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
+ const gfx::IntSize& size)
+ : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)),
+ mDrawable(drawable)
+#if defined(GL_PROVIDER_GLX)
+ , mGLXPixmap(X11None)
+#endif
+{
+ NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
+ "Bad Size");
+
+ cairo_surface_t *surf =
+ cairo_xlib_surface_create_with_xrender_format(mDisplay, drawable,
+ screen, format,
+ size.width, size.height);
+ Init(surf);
+}
+
+gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf)
+ : mPixmapTaken(false)
+#if defined(GL_PROVIDER_GLX)
+ , mGLXPixmap(X11None)
+#endif
+{
+ NS_PRECONDITION(cairo_surface_status(csurf) == 0,
+ "Not expecting an error surface");
+
+ mDrawable = cairo_xlib_surface_get_drawable(csurf);
+ mDisplay = cairo_xlib_surface_get_display(csurf);
+
+ Init(csurf, true);
+}
+
+gfxXlibSurface::~gfxXlibSurface()
+{
+ // gfxASurface's destructor calls RecordMemoryFreed().
+ if (mPixmapTaken) {
+#if defined(GL_PROVIDER_GLX)
+ if (mGLXPixmap) {
+ gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
+ }
+#endif
+ XFreePixmap (mDisplay, mDrawable);
+ }
+}
+
+static Drawable
+CreatePixmap(Screen *screen, const gfx::IntSize& size, unsigned int depth,
+ Drawable relatedDrawable)
+{
+ if (!Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
+ return X11None;
+
+ if (relatedDrawable == X11None) {
+ relatedDrawable = RootWindowOfScreen(screen);
+ }
+ Display *dpy = DisplayOfScreen(screen);
+ // X gives us a fatal error if we try to create a pixmap of width
+ // or height 0
+ return XCreatePixmap(dpy, relatedDrawable,
+ std::max(1, size.width), std::max(1, size.height),
+ depth);
+}
+
+void
+gfxXlibSurface::TakePixmap()
+{
+ NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
+ mPixmapTaken = true;
+
+ // The bit depth returned from Cairo is technically int, but this is
+ // the last place we'd be worried about that scenario.
+ unsigned int bitDepth = cairo_xlib_surface_get_depth(CairoSurface());
+ MOZ_ASSERT((bitDepth % 8) == 0, "Memory used not recorded correctly");
+
+ // Divide by 8 because surface_get_depth gives us the number of *bits* per
+ // pixel.
+ gfx::IntSize size = GetSize();
+ CheckedInt32 totalBytes = CheckedInt32(size.width) * CheckedInt32(size.height) * (bitDepth/8);
+
+ // Don't do anything in the "else" case. We could add INT32_MAX, but that
+ // would overflow the memory used counter. It would also mean we tried for
+ // a 2G image. For now, we'll just assert,
+ MOZ_ASSERT(totalBytes.isValid(),"Did not expect to exceed 2Gb image");
+ if (totalBytes.isValid()) {
+ RecordMemoryUsed(totalBytes.value());
+ }
+}
+
+Drawable
+gfxXlibSurface::ReleasePixmap() {
+ NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
+ mPixmapTaken = false;
+ RecordMemoryFreed();
+ return mDrawable;
+}
+
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+ DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+ Drawable mPixmap;
+ Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+ DestroyPixmapClosure *closure = static_cast<DestroyPixmapClosure*>(data);
+ XFreePixmap(DisplayOfScreen(closure->mScreen), closure->mPixmap);
+ delete closure;
+}
+
+/* static */
+cairo_surface_t *
+gfxXlibSurface::CreateCairoSurface(Screen *screen, Visual *visual,
+ const gfx::IntSize& size, Drawable relatedDrawable)
+{
+ Drawable drawable =
+ CreatePixmap(screen, size, DepthOfVisual(screen, visual),
+ relatedDrawable);
+ if (!drawable)
+ return nullptr;
+
+ cairo_surface_t* surface =
+ cairo_xlib_surface_create(DisplayOfScreen(screen), drawable, visual,
+ size.width, size.height);
+ if (cairo_surface_status(surface)) {
+ cairo_surface_destroy(surface);
+ XFreePixmap(DisplayOfScreen(screen), drawable);
+ return nullptr;
+ }
+
+ DestroyPixmapClosure *closure = new DestroyPixmapClosure(drawable, screen);
+ cairo_surface_set_user_data(surface, &gDestroyPixmapKey,
+ closure, DestroyPixmap);
+ return surface;
+}
+
+/* static */
+already_AddRefed<gfxXlibSurface>
+gfxXlibSurface::Create(Screen *screen, Visual *visual,
+ const gfx::IntSize& size, Drawable relatedDrawable)
+{
+ Drawable drawable =
+ CreatePixmap(screen, size, DepthOfVisual(screen, visual),
+ relatedDrawable);
+ if (!drawable)
+ return nullptr;
+
+ RefPtr<gfxXlibSurface> result =
+ new gfxXlibSurface(DisplayOfScreen(screen), drawable, visual, size);
+ result->TakePixmap();
+
+ if (result->CairoStatus() != 0)
+ return nullptr;
+
+ return result.forget();
+}
+
+/* static */
+already_AddRefed<gfxXlibSurface>
+gfxXlibSurface::Create(Screen *screen, XRenderPictFormat *format,
+ const gfx::IntSize& size, Drawable relatedDrawable)
+{
+ Drawable drawable =
+ CreatePixmap(screen, size, format->depth, relatedDrawable);
+ if (!drawable)
+ return nullptr;
+
+ RefPtr<gfxXlibSurface> result =
+ new gfxXlibSurface(screen, drawable, format, size);
+ result->TakePixmap();
+
+ if (result->CairoStatus() != 0)
+ return nullptr;
+
+ return result.forget();
+}
+
+static bool GetForce24bppPref()
+{
+ return Preferences::GetBool("mozilla.widget.force-24bpp", false);
+}
+
+already_AddRefed<gfxASurface>
+gfxXlibSurface::CreateSimilarSurface(gfxContentType aContent,
+ const gfx::IntSize& aSize)
+{
+ if (!mSurface || !mSurfaceValid) {
+ return nullptr;
+ }
+
+ if (aContent == gfxContentType::COLOR) {
+ // cairo_surface_create_similar will use a matching visual if it can.
+ // However, systems with 16-bit or indexed default visuals may benefit
+ // from rendering with 24-bit formats.
+ static bool force24bpp = GetForce24bppPref();
+ if (force24bpp
+ && cairo_xlib_surface_get_depth(CairoSurface()) != 24) {
+ XRenderPictFormat* format =
+ XRenderFindStandardFormat(mDisplay, PictStandardRGB24);
+ if (format) {
+ // Cairo only performs simple self-copies as desired if it
+ // knows that this is a Pixmap surface. It only knows that
+ // surfaces are pixmap surfaces if it creates the Pixmap
+ // itself, so we use cairo_surface_create_similar with a
+ // temporary reference surface to indicate the format.
+ Screen* screen = cairo_xlib_surface_get_screen(CairoSurface());
+ RefPtr<gfxXlibSurface> depth24reference =
+ gfxXlibSurface::Create(screen, format,
+ gfx::IntSize(1, 1), mDrawable);
+ if (depth24reference)
+ return depth24reference->
+ gfxASurface::CreateSimilarSurface(aContent, aSize);
+ }
+ }
+ }
+
+ return gfxASurface::CreateSimilarSurface(aContent, aSize);
+}
+
+void
+gfxXlibSurface::Finish()
+{
+#if defined(GL_PROVIDER_GLX)
+ if (mPixmapTaken && mGLXPixmap) {
+ gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
+ mGLXPixmap = X11None;
+ }
+#endif
+ gfxASurface::Finish();
+}
+
+const gfx::IntSize
+gfxXlibSurface::GetSize() const
+{
+ if (!mSurfaceValid)
+ return gfx::IntSize(0,0);
+
+ return gfx::IntSize(cairo_xlib_surface_get_width(mSurface),
+ cairo_xlib_surface_get_height(mSurface));
+}
+
+const gfx::IntSize
+gfxXlibSurface::DoSizeQuery()
+{
+ // figure out width/height/depth
+ Window root_ignore;
+ int x_ignore, y_ignore;
+ unsigned int bwidth_ignore, width, height, depth;
+
+ XGetGeometry(mDisplay,
+ mDrawable,
+ &root_ignore, &x_ignore, &y_ignore,
+ &width, &height,
+ &bwidth_ignore, &depth);
+
+ return gfx::IntSize(width, height);
+}
+
+class DisplayTable {
+public:
+ static bool GetColormapAndVisual(Screen* screen,
+ XRenderPictFormat* format,
+ Visual* visual, Colormap* colormap,
+ Visual** visualForColormap);
+
+private:
+ struct ColormapEntry {
+ XRenderPictFormat* mFormat;
+ // The Screen is needed here because colormaps (and their visuals) may
+ // only be used on one Screen, but XRenderPictFormats are not unique
+ // to any one Screen.
+ Screen* mScreen;
+ Visual* mVisual;
+ Colormap mColormap;
+ };
+
+ class DisplayInfo {
+ public:
+ explicit DisplayInfo(Display* display) : mDisplay(display) { }
+ Display* mDisplay;
+ nsTArray<ColormapEntry> mColormapEntries;
+ };
+
+ // Comparator for finding the DisplayInfo
+ class FindDisplay {
+ public:
+ bool Equals(const DisplayInfo& info, const Display *display) const
+ {
+ return info.mDisplay == display;
+ }
+ };
+
+ static int DisplayClosing(Display *display, XExtCodes* codes);
+
+ nsTArray<DisplayInfo> mDisplays;
+ static DisplayTable* sDisplayTable;
+};
+
+DisplayTable* DisplayTable::sDisplayTable;
+
+// Pixmaps don't have a particular associated visual but the pixel values are
+// interpreted according to a visual/colormap pairs.
+//
+// cairo is designed for surfaces with either TrueColor visuals or the
+// default visual (which may not be true color). TrueColor visuals don't
+// really need a colormap because the visual indicates the pixel format,
+// and cairo uses the default visual with the default colormap, so cairo
+// surfaces don't need an explicit colormap.
+//
+// However, some toolkits (e.g. GDK) need a colormap even with TrueColor
+// visuals. We can create a colormap for these visuals, but it will use about
+// 20kB of memory in the server, so we use the default colormap when
+// suitable and share colormaps between surfaces. Another reason for
+// minimizing colormap turnover is that the plugin process must leak resources
+// for each new colormap id when using older GDK libraries (bug 569775).
+//
+// Only the format of the pixels is important for rendering to Pixmaps, so if
+// the format of a visual matches that of the surface, then that visual can be
+// used for rendering to the surface. Multiple visuals can match the same
+// format (but have different GLX properties), so the visual returned may
+// differ from the visual passed in. Colormaps are tied to a visual, so
+// should only be used with their visual.
+
+/* static */ bool
+DisplayTable::GetColormapAndVisual(Screen* aScreen, XRenderPictFormat* aFormat,
+ Visual* aVisual, Colormap* aColormap,
+ Visual** aVisualForColormap)
+
+{
+ Display* display = DisplayOfScreen(aScreen);
+
+ // Use the default colormap if the default visual matches.
+ Visual *defaultVisual = DefaultVisualOfScreen(aScreen);
+ if (aVisual == defaultVisual
+ || (aFormat
+ && aFormat == XRenderFindVisualFormat(display, defaultVisual)))
+ {
+ *aColormap = DefaultColormapOfScreen(aScreen);
+ *aVisualForColormap = defaultVisual;
+ return true;
+ }
+
+ // Only supporting TrueColor non-default visuals
+ if (!aVisual || aVisual->c_class != TrueColor)
+ return false;
+
+ if (!sDisplayTable) {
+ sDisplayTable = new DisplayTable();
+ }
+
+ nsTArray<DisplayInfo>* displays = &sDisplayTable->mDisplays;
+ size_t d = displays->IndexOf(display, 0, FindDisplay());
+
+ if (d == displays->NoIndex) {
+ d = displays->Length();
+ // Register for notification of display closing, when this info
+ // becomes invalid.
+ XExtCodes *codes = XAddExtension(display);
+ if (!codes)
+ return false;
+
+ XESetCloseDisplay(display, codes->extension, DisplayClosing);
+ // Add a new DisplayInfo.
+ displays->AppendElement(display);
+ }
+
+ nsTArray<ColormapEntry>* entries =
+ &displays->ElementAt(d).mColormapEntries;
+
+ // Only a small number of formats are expected to be used, so just do a
+ // simple linear search.
+ for (uint32_t i = 0; i < entries->Length(); ++i) {
+ const ColormapEntry& entry = entries->ElementAt(i);
+ // Only the format and screen need to match. (The visual may differ.)
+ // If there is no format (e.g. no RENDER extension) then just compare
+ // the visual.
+ if ((aFormat && entry.mFormat == aFormat && entry.mScreen == aScreen)
+ || aVisual == entry.mVisual) {
+ *aColormap = entry.mColormap;
+ *aVisualForColormap = entry.mVisual;
+ return true;
+ }
+ }
+
+ // No existing entry. Create a colormap and add an entry.
+ Colormap colormap = XCreateColormap(display, RootWindowOfScreen(aScreen),
+ aVisual, AllocNone);
+ ColormapEntry* newEntry = entries->AppendElement();
+ newEntry->mFormat = aFormat;
+ newEntry->mScreen = aScreen;
+ newEntry->mVisual = aVisual;
+ newEntry->mColormap = colormap;
+
+ *aColormap = colormap;
+ *aVisualForColormap = aVisual;
+ return true;
+}
+
+/* static */ int
+DisplayTable::DisplayClosing(Display *display, XExtCodes* codes)
+{
+ // No need to free the colormaps explicitly as they will be released when
+ // the connection is closed.
+ sDisplayTable->mDisplays.RemoveElement(display, FindDisplay());
+ if (sDisplayTable->mDisplays.Length() == 0) {
+ delete sDisplayTable;
+ sDisplayTable = nullptr;
+ }
+ return 0;
+}
+
+/* static */
+bool
+gfxXlibSurface::GetColormapAndVisual(cairo_surface_t* aXlibSurface,
+ Colormap* aColormap, Visual** aVisual)
+{
+ XRenderPictFormat* format =
+ cairo_xlib_surface_get_xrender_format(aXlibSurface);
+ Screen* screen = cairo_xlib_surface_get_screen(aXlibSurface);
+ Visual* visual = cairo_xlib_surface_get_visual(aXlibSurface);
+
+ return DisplayTable::GetColormapAndVisual(screen, format, visual,
+ aColormap, aVisual);
+}
+
+bool
+gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap, Visual** aVisual)
+{
+ if (!mSurfaceValid)
+ return false;
+
+ return GetColormapAndVisual(CairoSurface(), aColormap, aVisual);
+}
+
+/* static */
+int
+gfxXlibSurface::DepthOfVisual(const Screen* screen, const Visual* visual)
+{
+ for (int d = 0; d < screen->ndepths; d++) {
+ const Depth& d_info = screen->depths[d];
+ if (visual >= &d_info.visuals[0]
+ && visual < &d_info.visuals[d_info.nvisuals])
+ return d_info.depth;
+ }
+
+ NS_ERROR("Visual not on Screen.");
+ return 0;
+}
+
+/* static */
+Visual*
+gfxXlibSurface::FindVisual(Screen *screen, gfxImageFormat format)
+{
+ int depth;
+ unsigned long red_mask, green_mask, blue_mask;
+ switch (format) {
+ case gfx::SurfaceFormat::A8R8G8B8_UINT32:
+ depth = 32;
+ red_mask = 0xff0000;
+ green_mask = 0xff00;
+ blue_mask = 0xff;
+ break;
+ case gfx::SurfaceFormat::X8R8G8B8_UINT32:
+ depth = 24;
+ red_mask = 0xff0000;
+ green_mask = 0xff00;
+ blue_mask = 0xff;
+ break;
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ depth = 16;
+ red_mask = 0xf800;
+ green_mask = 0x7e0;
+ blue_mask = 0x1f;
+ break;
+ case gfx::SurfaceFormat::A8:
+ default:
+ return nullptr;
+ }
+
+ for (int d = 0; d < screen->ndepths; d++) {
+ const Depth& d_info = screen->depths[d];
+ if (d_info.depth != depth)
+ continue;
+
+ for (int v = 0; v < d_info.nvisuals; v++) {
+ Visual* visual = &d_info.visuals[v];
+
+ if (visual->c_class == TrueColor &&
+ visual->red_mask == red_mask &&
+ visual->green_mask == green_mask &&
+ visual->blue_mask == blue_mask)
+ return visual;
+ }
+ }
+
+ return nullptr;
+}
+
+/* static */
+XRenderPictFormat*
+gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format)
+{
+ switch (format) {
+ case gfx::SurfaceFormat::A8R8G8B8_UINT32:
+ return XRenderFindStandardFormat (dpy, PictStandardARGB32);
+ case gfx::SurfaceFormat::X8R8G8B8_UINT32:
+ return XRenderFindStandardFormat (dpy, PictStandardRGB24);
+ case gfx::SurfaceFormat::R5G6B5_UINT16: {
+ // PictStandardRGB16_565 is not standard Xrender format
+ // we should try to find related visual
+ // and find xrender format by visual
+ Visual *visual = FindVisual(DefaultScreenOfDisplay(dpy), format);
+ if (!visual)
+ return nullptr;
+ return XRenderFindVisualFormat(dpy, visual);
+ }
+ case gfx::SurfaceFormat::A8:
+ return XRenderFindStandardFormat (dpy, PictStandardA8);
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+Screen*
+gfxXlibSurface::XScreen()
+{
+ return cairo_xlib_surface_get_screen(CairoSurface());
+}
+
+XRenderPictFormat*
+gfxXlibSurface::XRenderFormat()
+{
+ return cairo_xlib_surface_get_xrender_format(CairoSurface());
+}
+
+#if defined(GL_PROVIDER_GLX)
+GLXPixmap
+gfxXlibSurface::GetGLXPixmap()
+{
+ if (!mGLXPixmap) {
+#ifdef DEBUG
+ // cairo_surface_has_show_text_glyphs is used solely for the
+ // side-effect of setting the error on surface if
+ // cairo_surface_finish() has been called.
+ cairo_surface_has_show_text_glyphs(CairoSurface());
+ NS_ASSERTION(CairoStatus() != CAIRO_STATUS_SURFACE_FINISHED,
+ "GetGLXPixmap called after surface finished");
+#endif
+ mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
+ }
+ return mGLXPixmap;
+}
+
+void
+gfxXlibSurface::BindGLXPixmap(GLXPixmap aPixmap)
+{
+ MOZ_ASSERT(!mGLXPixmap, "A GLXPixmap is already bound!");
+ mGLXPixmap = aPixmap;
+}
+
+#endif
diff --git a/system/graphics/thebes/gfxXlibSurface.h b/system/graphics/thebes/gfxXlibSurface.h
new file mode 100644
index 000000000..499bc5e96
--- /dev/null
+++ b/system/graphics/thebes/gfxXlibSurface.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_XLIBSURFACE_H
+#define GFX_XLIBSURFACE_H
+
+#include "gfxASurface.h"
+
+#include <X11/extensions/Xrender.h>
+#include <X11/Xlib.h>
+#include "X11UndefineNone.h"
+
+#if defined(GL_PROVIDER_GLX)
+#include "GLXLibrary.h"
+#endif
+
+#include "nsSize.h"
+
+// Although the dimension parameters in the xCreatePixmapReq wire protocol are
+// 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+// either dimension cannot be represented by a 16-bit *signed* integer.
+#define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+
+class gfxXlibSurface final : public gfxASurface {
+public:
+ // construct a wrapper around the specified drawable with dpy/visual.
+ // Will use XGetGeometry to query the window/pixmap size.
+ gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual);
+
+ // construct a wrapper around the specified drawable with dpy/visual,
+ // and known width/height.
+ gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const mozilla::gfx::IntSize& size);
+
+ // construct a wrapper around the specified drawable with dpy/format,
+ // and known width/height.
+ gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
+ const mozilla::gfx::IntSize& size);
+
+ explicit gfxXlibSurface(cairo_surface_t *csurf);
+
+ // create a new Pixmap and wrapper surface.
+ // |relatedDrawable| provides a hint to the server for determining whether
+ // the pixmap should be in video or system memory. It must be on
+ // |screen| (if specified).
+ static already_AddRefed<gfxXlibSurface>
+ Create(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
+ Drawable relatedDrawable = X11None);
+ static cairo_surface_t *
+ CreateCairoSurface(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
+ Drawable relatedDrawable = X11None);
+ static already_AddRefed<gfxXlibSurface>
+ Create(Screen* screen, XRenderPictFormat *format, const mozilla::gfx::IntSize& size,
+ Drawable relatedDrawable = X11None);
+
+ virtual ~gfxXlibSurface();
+
+ virtual already_AddRefed<gfxASurface>
+ CreateSimilarSurface(gfxContentType aType,
+ const mozilla::gfx::IntSize& aSize) override;
+ virtual void Finish() override;
+
+ virtual const mozilla::gfx::IntSize GetSize() const override;
+
+ Display* XDisplay() { return mDisplay; }
+ Screen* XScreen();
+ Drawable XDrawable() { return mDrawable; }
+ XRenderPictFormat* XRenderFormat();
+
+ static int DepthOfVisual(const Screen* screen, const Visual* visual);
+ static Visual* FindVisual(Screen* screen, gfxImageFormat format);
+ static XRenderPictFormat *FindRenderFormat(Display *dpy, gfxImageFormat format);
+ static bool GetColormapAndVisual(cairo_surface_t* aXlibSurface, Colormap* colormap, Visual **visual);
+
+ // take ownership of a passed-in Pixmap, calling XFreePixmap on it
+ // when the gfxXlibSurface is destroyed.
+ void TakePixmap();
+
+ // Release ownership of this surface's Pixmap. This is only valid
+ // on gfxXlibSurfaces for which the user called TakePixmap(), or
+ // on those created by a Create() factory method.
+ Drawable ReleasePixmap();
+
+ // Find a visual and colormap pair suitable for rendering to this surface.
+ bool GetColormapAndVisual(Colormap* colormap, Visual **visual);
+
+#if defined(GL_PROVIDER_GLX)
+ GLXPixmap GetGLXPixmap();
+ // Binds a GLXPixmap backed by this context's surface.
+ // Primarily for use in sharing surfaces.
+ void BindGLXPixmap(GLXPixmap aPixmap);
+#endif
+
+ // Return true if cairo will take its slow path when this surface is used
+ // in a pattern with EXTEND_PAD. As a workaround for XRender's RepeatPad
+ // not being implemented correctly on old X servers, cairo avoids XRender
+ // and instead reads back to perform EXTEND_PAD with pixman. Cairo does
+ // this for servers older than xorg-server 1.7.
+ bool IsPadSlow() {
+ // The test here matches that for buggy_pad_reflect in
+ // _cairo_xlib_device_create.
+ return VendorRelease(mDisplay) >= 60700000 ||
+ VendorRelease(mDisplay) < 10699000;
+ }
+
+protected:
+ // if TakePixmap() has been called on this
+ bool mPixmapTaken;
+
+ Display *mDisplay;
+ Drawable mDrawable;
+
+ const mozilla::gfx::IntSize DoSizeQuery();
+
+#if defined(GL_PROVIDER_GLX)
+ GLXPixmap mGLXPixmap;
+#endif
+};
+
+#endif /* GFX_XLIBSURFACE_H */
diff --git a/system/graphics/thebes/moz.build b/system/graphics/thebes/moz.build
new file mode 100644
index 000000000..0b55a1a58
--- /dev/null
+++ b/system/graphics/thebes/moz.build
@@ -0,0 +1,219 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'ContextStateTracker.h',
+ 'DrawMode.h',
+ 'gfx2DGlue.h',
+ 'gfxAlphaRecovery.h',
+ 'gfxASurface.h',
+ 'gfxBaseSharedMemorySurface.h',
+ 'gfxBlur.h',
+ 'gfxColor.h',
+ 'gfxContext.h',
+ 'gfxDrawable.h',
+ 'gfxEnv.h',
+ 'gfxFailure.h',
+ 'gfxFont-Impl.h',
+ 'gfxFont.h',
+ 'gfxFontConstants.h',
+ 'gfxFontEntry.h',
+ 'gfxFontFamilyList.h',
+ 'gfxFontFeatures.h',
+ 'gfxFontInfoLoader.h',
+ 'gfxFontPrefLangList.h',
+ 'gfxFontTest.h',
+ 'gfxFontUtils.h',
+ 'gfxGradientCache.h',
+ 'gfxImageSurface.h',
+ 'gfxLineSegment.h',
+ 'gfxMathTable.h',
+ 'gfxMatrix.h',
+ 'gfxPattern.h',
+ 'gfxPlatform.h',
+ 'gfxPoint.h',
+ 'gfxPrefs.h',
+ 'gfxQuad.h',
+ 'gfxQuaternion.h',
+ 'gfxRect.h',
+ 'gfxSharedImageSurface.h',
+ 'gfxSkipChars.h',
+ 'gfxSVGGlyphs.h',
+ 'gfxTextRun.h',
+ 'gfxTypes.h',
+ 'gfxUserFontSet.h',
+ 'gfxUtils.h',
+ 'RoundedRect.h',
+ 'SoftwareVsyncSource.h',
+ 'VsyncSource.h',
+]
+
+EXPORTS.mozilla.gfx += [
+ 'D3D11Checks.h',
+ 'DeviceManagerDx.h',
+ 'PrintTarget.h',
+ 'PrintTargetThebes.h',
+]
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ EXPORTS += [
+ 'gfxFontconfigFonts.h',
+ 'gfxFT2FontBase.h',
+ 'gfxGdkNativeRenderer.h',
+ 'gfxPlatformGtk.h',
+ ]
+ EXPORTS.mozilla.gfx += [
+ 'PrintTargetPDF.h',
+ 'PrintTargetPS.h',
+ ]
+ SOURCES += [
+ 'gfxFcPlatformFontList.cpp',
+ 'gfxFontconfigFonts.cpp',
+ 'gfxFontconfigUtils.cpp',
+ 'gfxFT2FontBase.cpp',
+ 'gfxFT2Utils.cpp',
+ 'gfxGdkNativeRenderer.cpp',
+ 'gfxPlatformGtk.cpp',
+ 'PrintTargetPDF.cpp',
+ 'PrintTargetPS.cpp',
+ ]
+
+ if CONFIG['MOZ_X11']:
+ EXPORTS += [
+ 'gfxXlibNativeRenderer.h',
+ 'gfxXlibSurface.h',
+ ]
+ SOURCES += [
+ 'gfxXlibNativeRenderer.cpp',
+ 'gfxXlibSurface.cpp',
+ ]
+
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS += [
+ 'gfxDWriteFonts.h',
+ 'gfxGDIFont.h',
+ 'gfxGDIFontList.h',
+ 'gfxPlatformFontList.h',
+ 'gfxWindowsNativeDrawing.h',
+ 'gfxWindowsPlatform.h',
+ 'gfxWindowsSurface.h',
+ ]
+ EXPORTS.mozilla.gfx += [
+ 'PrintTargetPDF.h',
+ 'PrintTargetWindows.h',
+ ]
+ SOURCES += [
+ 'gfxDWriteCommon.cpp',
+ 'gfxDWriteFontList.cpp',
+ 'gfxDWriteFonts.cpp',
+ 'gfxGDIFont.cpp',
+ 'gfxGDIFontList.cpp',
+ 'gfxWindowsNativeDrawing.cpp',
+ 'gfxWindowsPlatform.cpp',
+ 'gfxWindowsSurface.cpp',
+ 'PrintTargetPDF.cpp',
+ 'PrintTargetWindows.cpp',
+ ]
+
+# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.
+if CONFIG['INTEL_ARCHITECTURE']:
+ SOURCES += ['gfxAlphaRecoverySSE2.cpp']
+ # The file uses SSE2 intrinsics, so it needs special compile flags on some
+ # compilers.
+ SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+
+SOURCES += [
+ 'CJKCompatSVS.cpp',
+ 'ContextStateTracker.cpp',
+ 'gfxAlphaRecovery.cpp',
+ 'gfxASurface.cpp',
+ 'gfxBaseSharedMemorySurface.cpp',
+ 'gfxBlur.cpp',
+ 'gfxContext.cpp',
+ 'gfxDrawable.cpp',
+ 'gfxFont.cpp',
+ 'gfxFontEntry.cpp',
+ 'gfxFontFeatures.cpp',
+ 'gfxFontInfoLoader.cpp',
+ 'gfxFontMissingGlyphs.cpp',
+ 'gfxFontTest.cpp',
+ 'gfxFontUtils.cpp',
+ 'gfxGlyphExtents.cpp',
+ 'gfxGradientCache.cpp',
+ 'gfxGraphiteShaper.cpp',
+ 'gfxHarfBuzzShaper.cpp',
+ 'gfxImageSurface.cpp',
+ 'gfxMathTable.cpp',
+ 'gfxMatrix.cpp',
+ 'gfxPattern.cpp',
+ 'gfxPlatform.cpp',
+ 'gfxPlatformFontList.cpp',
+ 'gfxPrefs.cpp',
+ 'gfxRect.cpp',
+ 'gfxScriptItemizer.cpp',
+ 'gfxSkipChars.cpp',
+ 'gfxSVGGlyphs.cpp',
+ 'gfxTextRun.cpp',
+ 'gfxUserFontSet.cpp',
+ 'gfxUtils.cpp',
+ 'nsUnicodeRange.cpp',
+ 'PrintTarget.cpp',
+ 'PrintTargetThebes.cpp',
+ 'SoftwareVsyncSource.cpp',
+ 'VsyncSource.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'D3D11Checks.cpp',
+ 'DeviceManagerDx.cpp',
+ ]
+
+# We use ICU for normalization functions
+USE_LIBS += [
+ 'icu',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+GENERATED_FILES = [
+ 'DeprecatedPremultiplyTables.h',
+]
+GENERATED_FILES['DeprecatedPremultiplyTables.h'].script = 'genTables.py:generate'
+
+LOCAL_INCLUDES += [
+ '/dom/workers',
+ '/dom/xml',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ DEFINES['MOZ_ENABLE_FREETYPE'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ for var in ('MOZ_ENABLE_D3D10_LAYER'):
+ if CONFIG[var]:
+ DEFINES[var] = True
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+CFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CFLAGS += CONFIG['TK_CFLAGS']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+LOCAL_INCLUDES += ['/libs/libyuv/include']
+
+DEFINES['GRAPHITE2_STATIC'] = True
+
+if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
+ DEFINES['OTS_DLL'] = True
+
+if CONFIG['CLANG_CXX']:
+ # Suppress warnings from Skia header files.
+ SOURCES['gfxPlatform.cpp'].flags += ['-Wno-implicit-fallthrough']
diff --git a/system/graphics/thebes/nsUnicodeRange.cpp b/system/graphics/thebes/nsUnicodeRange.cpp
new file mode 100644
index 000000000..af53f21fa
--- /dev/null
+++ b/system/graphics/thebes/nsUnicodeRange.cpp
@@ -0,0 +1,419 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUnicodeRange.h"
+
+/**********************************************************************
+ * Unicode subranges as defined in unicode 3.0
+ * x-western -> latin
+ * 0000 - 036f
+ * 1e00 - 1eff
+ * 2000 - 206f (general punctuation)
+ * 20a0 - 20cf (currency symbols)
+ * 2100 - 214f (letterlike symbols)
+ * 2150 - 218f (Number Forms)
+ * el -> greek
+ * 0370 - 03ff
+ * 1f00 - 1fff
+ * x-cyrillic -> cyrillic
+ * 0400 - 04ff
+ * he -> hebrew
+ * 0590 - 05ff
+ * ar -> arabic
+ * 0600 - 06ff
+ * fb50 - fdff (arabic presentation forms)
+ * fe70 - feff (arabic presentation forms b)
+ * th - thai
+ * 0e00 - 0e7f
+ * ko -> korean
+ * ac00 - d7af (hangul Syllables)
+ * 1100 - 11ff (jamo)
+ * 3130 - 318f (hangul compatibility jamo)
+ * ja
+ * 3040 - 309f (hiragana)
+ * 30a0 - 30ff (katakana)
+ * zh-CN
+ * zh-TW
+ *
+ * CJK
+ * 3100 - 312f (bopomofo)
+ * 31a0 - 31bf (bopomofo extended)
+ * 3000 - 303f (CJK Symbols and Punctuation)
+ * 2e80 - 2eff (CJK radicals supplement)
+ * 2f00 - 2fdf (Kangxi Radicals)
+ * 2ff0 - 2fff (Ideographic Description Characters)
+ * 3190 - 319f (kanbun)
+ * 3200 - 32ff (Enclosed CJK letters and Months)
+ * 3300 - 33ff (CJK compatibility)
+ * 3400 - 4dbf (CJK Unified Ideographs Extension A)
+ * 4e00 - 9faf (CJK Unified Ideographs)
+ * f900 - fa5f (CJK Compatibility Ideographs)
+ * fe30 - fe4f (CJK compatibility Forms)
+ * ff00 - ffef (halfwidth and fullwidth forms)
+ *
+ * Armenian
+ * 0530 - 058f
+ * Sriac
+ * 0700 - 074f
+ * Thaana
+ * 0780 - 07bf
+ * Devanagari
+ * 0900 - 097f
+ * Bengali
+ * 0980 - 09ff
+ * Gurmukhi
+ * 0a00 - 0a7f
+ * Gujarati
+ * 0a80 - 0aff
+ * Oriya
+ * 0b00 - 0b7f
+ * Tamil
+ * 0b80 - 0bff
+ * Telugu
+ * 0c00 - 0c7f
+ * Kannada
+ * 0c80 - 0cff
+ * Malayalam
+ * 0d00 - 0d7f
+ * Sinhala
+ * 0d80 - 0def
+ * Lao
+ * 0e80 - 0eff
+ * Tibetan
+ * 0f00 - 0fbf
+ * Myanmar
+ * 1000 - 109f
+ * Georgian
+ * 10a0 - 10ff
+ * Ethiopic
+ * 1200 - 137f
+ * Cherokee
+ * 13a0 - 13ff
+ * Canadian Aboriginal Syllabics
+ * 1400 - 167f
+ * Ogham
+ * 1680 - 169f
+ * Runic
+ * 16a0 - 16ff
+ * Khmer
+ * 1780 - 17ff
+ * Mongolian
+ * 1800 - 18af
+ * Misc - superscripts and subscripts
+ * 2070 - 209f
+ * Misc - Combining Diacritical Marks for Symbols
+ * 20d0 - 20ff
+ * Misc - Arrows
+ * 2190 - 21ff
+ * Misc - Mathematical Operators
+ * 2200 - 22ff
+ * Misc - Miscellaneous Technical
+ * 2300 - 23ff
+ * Misc - Control picture
+ * 2400 - 243f
+ * Misc - Optical character recognition
+ * 2440 - 2450
+ * Misc - Enclose Alphanumerics
+ * 2460 - 24ff
+ * Misc - Box Drawing
+ * 2500 - 257f
+ * Misc - Block Elements
+ * 2580 - 259f
+ * Misc - Geometric Shapes
+ * 25a0 - 25ff
+ * Misc - Miscellaneous Symbols
+ * 2600 - 267f
+ * Misc - Dingbats
+ * 2700 - 27bf
+ * Misc - Braille Patterns
+ * 2800 - 28ff
+ * Yi Syllables
+ * a000 - a48f
+ * Yi radicals
+ * a490 - a4cf
+ * Alphabetic Presentation Forms
+ * fb00 - fb4f
+ * Misc - Combining half Marks
+ * fe20 - fe2f
+ * Misc - small form variants
+ * fe50 - fe6f
+ * Misc - Specials
+ * fff0 - ffff
+ *********************************************************************/
+
+
+
+#define NUM_OF_SUBTABLES 10
+#define SUBTABLE_SIZE 16
+
+static const uint8_t gUnicodeSubrangeTable[NUM_OF_SUBTABLES][SUBTABLE_SIZE] =
+{
+ { // table for X---
+ kRangeTableBase+1, //u0xxx
+ kRangeTableBase+2, //u1xxx
+ kRangeTableBase+3, //u2xxx
+ kRangeSetCJK, //u3xxx
+ kRangeSetCJK, //u4xxx
+ kRangeSetCJK, //u5xxx
+ kRangeSetCJK, //u6xxx
+ kRangeSetCJK, //u7xxx
+ kRangeSetCJK, //u8xxx
+ kRangeSetCJK, //u9xxx
+ kRangeTableBase+4, //uaxxx
+ kRangeKorean, //ubxxx
+ kRangeKorean, //ucxxx
+ kRangeTableBase+5, //udxxx
+ kRangePrivate, //uexxx
+ kRangeTableBase+6 //ufxxx
+ },
+ { //table for 0X--
+ kRangeSetLatin, //u00xx
+ kRangeSetLatin, //u01xx
+ kRangeSetLatin, //u02xx
+ kRangeGreek, //u03xx XXX 0300-036f is in fact kRangeCombiningDiacriticalMarks
+ kRangeCyrillic, //u04xx
+ kRangeTableBase+7, //u05xx, includes Cyrillic supplement, Hebrew, and Armenian
+ kRangeArabic, //u06xx
+ kRangeTertiaryTable, //u07xx
+ kRangeUnassigned, //u08xx
+ kRangeTertiaryTable, //u09xx
+ kRangeTertiaryTable, //u0axx
+ kRangeTertiaryTable, //u0bxx
+ kRangeTertiaryTable, //u0cxx
+ kRangeTertiaryTable, //u0dxx
+ kRangeTertiaryTable, //u0exx
+ kRangeTibetan //u0fxx
+ },
+ { //table for 1x--
+ kRangeTertiaryTable, //u10xx
+ kRangeKorean, //u11xx
+ kRangeEthiopic, //u12xx
+ kRangeTertiaryTable, //u13xx
+ kRangeCanadian, //u14xx
+ kRangeCanadian, //u15xx
+ kRangeTertiaryTable, //u16xx
+ kRangeKhmer, //u17xx
+ kRangeMongolian, //u18xx
+ kRangeUnassigned, //u19xx
+ kRangeUnassigned, //u1axx
+ kRangeUnassigned, //u1bxx
+ kRangeUnassigned, //u1cxx
+ kRangeUnassigned, //u1dxx
+ kRangeSetLatin, //u1exx
+ kRangeGreek //u1fxx
+ },
+ { //table for 2x--
+ kRangeSetLatin, //u20xx
+ kRangeSetLatin, //u21xx
+ kRangeMathOperators, //u22xx
+ kRangeMiscTechnical, //u23xx
+ kRangeControlOpticalEnclose, //u24xx
+ kRangeBoxBlockGeometrics, //u25xx
+ kRangeMiscSymbols, //u26xx
+ kRangeDingbats, //u27xx
+ kRangeBraillePattern, //u28xx
+ kRangeUnassigned, //u29xx
+ kRangeUnassigned, //u2axx
+ kRangeUnassigned, //u2bxx
+ kRangeUnassigned, //u2cxx
+ kRangeUnassigned, //u2dxx
+ kRangeSetCJK, //u2exx
+ kRangeSetCJK //u2fxx
+ },
+ { //table for ax--
+ kRangeYi, //ua0xx
+ kRangeYi, //ua1xx
+ kRangeYi, //ua2xx
+ kRangeYi, //ua3xx
+ kRangeYi, //ua4xx
+ kRangeUnassigned, //ua5xx
+ kRangeUnassigned, //ua6xx
+ kRangeUnassigned, //ua7xx
+ kRangeUnassigned, //ua8xx
+ kRangeUnassigned, //ua9xx
+ kRangeUnassigned, //uaaxx
+ kRangeUnassigned, //uabxx
+ kRangeKorean, //uacxx
+ kRangeKorean, //uadxx
+ kRangeKorean, //uaexx
+ kRangeKorean //uafxx
+ },
+ { //table for dx--
+ kRangeKorean, //ud0xx
+ kRangeKorean, //ud1xx
+ kRangeKorean, //ud2xx
+ kRangeKorean, //ud3xx
+ kRangeKorean, //ud4xx
+ kRangeKorean, //ud5xx
+ kRangeKorean, //ud6xx
+ kRangeKorean, //ud7xx
+ kRangeSurrogate, //ud8xx
+ kRangeSurrogate, //ud9xx
+ kRangeSurrogate, //udaxx
+ kRangeSurrogate, //udbxx
+ kRangeSurrogate, //udcxx
+ kRangeSurrogate, //uddxx
+ kRangeSurrogate, //udexx
+ kRangeSurrogate //udfxx
+ },
+ { // table for fx--
+ kRangePrivate, //uf0xx
+ kRangePrivate, //uf1xx
+ kRangePrivate, //uf2xx
+ kRangePrivate, //uf3xx
+ kRangePrivate, //uf4xx
+ kRangePrivate, //uf5xx
+ kRangePrivate, //uf6xx
+ kRangePrivate, //uf7xx
+ kRangePrivate, //uf8xx
+ kRangeSetCJK, //uf9xx
+ kRangeSetCJK, //ufaxx
+ kRangeArabic, //ufbxx, includes alphabic presentation form
+ kRangeArabic, //ufcxx
+ kRangeArabic, //ufdxx
+ kRangeTableBase+8, //ufexx
+ kRangeTableBase+9 //uffxx, halfwidth and fullwidth forms, includes Specials
+ },
+ { //table for 0x0500 - 0x05ff
+ kRangeCyrillic, //u050x
+ kRangeCyrillic, //u051x
+ kRangeCyrillic, //u052x
+ kRangeArmenian, //u053x
+ kRangeArmenian, //u054x
+ kRangeArmenian, //u055x
+ kRangeArmenian, //u056x
+ kRangeArmenian, //u057x
+ kRangeArmenian, //u058x
+ kRangeHebrew, //u059x
+ kRangeHebrew, //u05ax
+ kRangeHebrew, //u05bx
+ kRangeHebrew, //u05cx
+ kRangeHebrew, //u05dx
+ kRangeHebrew, //u05ex
+ kRangeHebrew //u05fx
+ },
+ { //table for 0xfe00 - 0xfeff
+ kRangeSetCJK, //ufe0x
+ kRangeSetCJK, //ufe1x
+ kRangeSetCJK, //ufe2x
+ kRangeSetCJK, //ufe3x
+ kRangeSetCJK, //ufe4x
+ kRangeSetCJK, //ufe5x
+ kRangeSetCJK, //ufe6x
+ kRangeArabic, //ufe7x
+ kRangeArabic, //ufe8x
+ kRangeArabic, //ufe9x
+ kRangeArabic, //ufeax
+ kRangeArabic, //ufebx
+ kRangeArabic, //ufecx
+ kRangeArabic, //ufedx
+ kRangeArabic, //ufeex
+ kRangeArabic //ufefx
+ },
+ { //table for 0xff00 - 0xffff
+ kRangeSetCJK, //uff0x, fullwidth latin
+ kRangeSetCJK, //uff1x, fullwidth latin
+ kRangeSetCJK, //uff2x, fullwidth latin
+ kRangeSetCJK, //uff3x, fullwidth latin
+ kRangeSetCJK, //uff4x, fullwidth latin
+ kRangeSetCJK, //uff5x, fullwidth latin
+ kRangeSetCJK, //uff6x, halfwidth katakana
+ kRangeSetCJK, //uff7x, halfwidth katakana
+ kRangeSetCJK, //uff8x, halfwidth katakana
+ kRangeSetCJK, //uff9x, halfwidth katakana
+ kRangeSetCJK, //uffax, halfwidth hangul jamo
+ kRangeSetCJK, //uffbx, halfwidth hangul jamo
+ kRangeSetCJK, //uffcx, halfwidth hangul jamo
+ kRangeSetCJK, //uffdx, halfwidth hangul jamo
+ kRangeSetCJK, //uffex, fullwidth symbols
+ kRangeSpecials, //ufffx, Specials
+ },
+};
+
+// Most scripts between U+0700 and U+16FF are assigned a chunk of 128 (0x80)
+// code points so that the number of entries in the tertiary range
+// table for that range is obtained by dividing (0x1700 - 0x0700) by 128.
+// Exceptions: Ethiopic, Tibetan, Hangul Jamo and Canadian aboriginal
+// syllabaries take multiple chunks and Ogham and Runic share a single chunk.
+#define TERTIARY_TABLE_SIZE ((0x1700 - 0x0700) / 0x80)
+
+static const uint8_t gUnicodeTertiaryRangeTable[TERTIARY_TABLE_SIZE] =
+{ //table for 0x0700 - 0x1600
+ kRangeSyriac, //u070x
+ kRangeThaana, //u078x
+ kRangeUnassigned, //u080x place holder(resolved in the 2ndary tab.)
+ kRangeUnassigned, //u088x place holder(resolved in the 2ndary tab.)
+ kRangeDevanagari, //u090x
+ kRangeBengali, //u098x
+ kRangeGurmukhi, //u0a0x
+ kRangeGujarati, //u0a8x
+ kRangeOriya, //u0b0x
+ kRangeTamil, //u0b8x
+ kRangeTelugu, //u0c0x
+ kRangeKannada, //u0c8x
+ kRangeMalayalam, //u0d0x
+ kRangeSinhala, //u0d8x
+ kRangeThai, //u0e0x
+ kRangeLao, //u0e8x
+ kRangeTibetan, //u0f0x place holder(resolved in the 2ndary tab.)
+ kRangeTibetan, //u0f8x place holder(resolved in the 2ndary tab.)
+ kRangeMyanmar, //u100x
+ kRangeGeorgian, //u108x
+ kRangeKorean, //u110x place holder(resolved in the 2ndary tab.)
+ kRangeKorean, //u118x place holder(resolved in the 2ndary tab.)
+ kRangeEthiopic, //u120x place holder(resolved in the 2ndary tab.)
+ kRangeEthiopic, //u128x place holder(resolved in the 2ndary tab.)
+ kRangeEthiopic, //u130x
+ kRangeCherokee, //u138x
+ kRangeCanadian, //u140x place holder(resolved in the 2ndary tab.)
+ kRangeCanadian, //u148x place holder(resolved in the 2ndary tab.)
+ kRangeCanadian, //u150x place holder(resolved in the 2ndary tab.)
+ kRangeCanadian, //u158x place holder(resolved in the 2ndary tab.)
+ kRangeCanadian, //u160x
+ kRangeOghamRunic //u168x this contains two scripts, Ogham & Runic
+};
+
+// A two level index is almost enough for locating a range, with the
+// exception of u03xx and u05xx. Since we don't really care about range for
+// combining diacritical marks in our font application, they are
+// not discriminated further. But future adoption of this module for other use
+// should be aware of this limitation. The implementation can be extended if
+// there is such a need.
+// For Indic, Southeast Asian scripts and some other scripts between
+// U+0700 and U+16FF, it's extended to the third level.
+uint32_t FindCharUnicodeRange(uint32_t ch)
+{
+ uint32_t range;
+
+ // aggregate ranges for non-BMP codepoints
+ if (ch > 0xFFFF) {
+ uint32_t p = (ch >> 16);
+ if (p == 1) {
+ return kRangeSMP;
+ } else if (p == 2) {
+ return kRangeSetCJK;
+ }
+ return kRangeHigherPlanes;
+ }
+
+ // lookup explicit range for BMP codepoints
+ // first general range
+ range = gUnicodeSubrangeTable[0][ch >> 12];
+
+ // if general range is good enough, return that
+ if (range < kRangeTableBase)
+ // we try to get a specific range
+ return range;
+
+ // otherwise, use subrange tables
+ range = gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x0f00) >> 8];
+ if (range < kRangeTableBase)
+ return range;
+ if (range < kRangeTertiaryTable)
+ return gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x00f0) >> 4];
+
+ // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks
+ return gUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7];
+}
diff --git a/system/graphics/thebes/nsUnicodeRange.h b/system/graphics/thebes/nsUnicodeRange.h
new file mode 100644
index 000000000..292a6a9ab
--- /dev/null
+++ b/system/graphics/thebes/nsUnicodeRange.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NS_UNICODERANGE_H
+#define NS_UNICODERANGE_H
+
+#include <stdint.h>
+
+// The following constants define unicode subranges
+// values below kRangeNum must be continuous so that we can map to
+// lang group directly.
+// all ranges we care about should be defined under 32, that allows
+// us to store range using bits of a uint32_t
+
+// frequently used range definitions
+const uint8_t kRangeCyrillic = 0;
+const uint8_t kRangeGreek = 1;
+const uint8_t kRangeHebrew = 2;
+const uint8_t kRangeArabic = 3;
+const uint8_t kRangeThai = 4;
+const uint8_t kRangeKorean = 5;
+const uint8_t kRangeJapanese = 6;
+const uint8_t kRangeSChinese = 7;
+const uint8_t kRangeTChinese = 8;
+const uint8_t kRangeDevanagari = 9;
+const uint8_t kRangeTamil = 10;
+const uint8_t kRangeArmenian = 11;
+const uint8_t kRangeBengali = 12;
+const uint8_t kRangeCanadian = 13;
+const uint8_t kRangeEthiopic = 14;
+const uint8_t kRangeGeorgian = 15;
+const uint8_t kRangeGujarati = 16;
+const uint8_t kRangeGurmukhi = 17;
+const uint8_t kRangeKhmer = 18;
+const uint8_t kRangeMalayalam = 19;
+const uint8_t kRangeOriya = 20;
+const uint8_t kRangeTelugu = 21;
+const uint8_t kRangeKannada = 22;
+const uint8_t kRangeSinhala = 23;
+const uint8_t kRangeTibetan = 24;
+
+const uint8_t kRangeSpecificItemNum = 25;
+
+//range/rangeSet grow to this place 25-29
+
+const uint8_t kRangeSetStart = 30; // range set definition starts from here
+const uint8_t kRangeSetLatin = 30;
+const uint8_t kRangeSetCJK = 31;
+const uint8_t kRangeSetEnd = 31; // range set definition ends here, this
+ // and smaller ranges are used as bit
+ // mask, don't increase this value.
+
+// less frequently used range definition
+const uint8_t kRangeSurrogate = 32;
+const uint8_t kRangePrivate = 33;
+const uint8_t kRangeMisc = 34;
+const uint8_t kRangeUnassigned = 35;
+const uint8_t kRangeSyriac = 36;
+const uint8_t kRangeThaana = 37;
+const uint8_t kRangeLao = 38;
+const uint8_t kRangeMyanmar = 39;
+const uint8_t kRangeCherokee = 40;
+const uint8_t kRangeOghamRunic = 41;
+const uint8_t kRangeMongolian = 42;
+const uint8_t kRangeMathOperators = 43;
+const uint8_t kRangeMiscTechnical = 44;
+const uint8_t kRangeControlOpticalEnclose = 45;
+const uint8_t kRangeBoxBlockGeometrics = 46;
+const uint8_t kRangeMiscSymbols = 47;
+const uint8_t kRangeDingbats = 48;
+const uint8_t kRangeBraillePattern = 49;
+const uint8_t kRangeYi = 50;
+const uint8_t kRangeCombiningDiacriticalMarks = 51;
+const uint8_t kRangeSpecials = 52;
+
+// aggregate ranges for non-BMP codepoints (u+2xxxx are all CJK)
+const uint8_t kRangeSMP = 53; // u+1xxxx
+const uint8_t kRangeHigherPlanes = 54; // u+3xxxx and above
+
+const uint8_t kRangeTableBase = 128; //values over 127 are reserved for internal use only
+const uint8_t kRangeTertiaryTable = 145; // leave room for 16 subtable
+ // indices (kRangeTableBase + 1 ..
+ // kRangeTableBase + 16)
+
+
+
+uint32_t FindCharUnicodeRange(uint32_t ch);
+
+#endif
diff --git a/system/moz.build b/system/moz.build
index a27ff9438..be050809f 100644
--- a/system/moz.build
+++ b/system/moz.build
@@ -4,7 +4,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
- 'docshell'
+ 'docshell',
+ 'graphics',
]
DIRS += ['runtime']
diff --git a/system/toolkit.mozbuild b/system/toolkit.mozbuild
index 04875ab9d..19671af27 100644
--- a/system/toolkit.mozbuild
+++ b/system/toolkit.mozbuild
@@ -58,7 +58,6 @@ if CONFIG['ENABLE_TESTS']:
DIRS += [
'/testing/gtest',
'/caps',
- '/gfx',
'/image',
'/dom',
'/widget',